From fc37c33dec3c9e06ac1696b4cd85eb2c123fa296 Mon Sep 17 00:00:00 2001 From: Bastian Germann Date: Mon, 16 Jan 2023 17:32:36 +0000 Subject: [PATCH] Import gambas3_3.18.0.orig.tar.gz [dgit import orig gambas3_3.18.0.orig.tar.gz] --- .gitignore | 49 + .gitlab-ci.yml | 243 + AUTHORS | 311 + CONTRIBUTING.md | 149 + COPYING | 339 + ChangeLog | 0 INSTALL | 1 + Makefile.am | 62 + NEWS | 0 README | 50 + README.commit | 60 + README.md | 56 + TEMPLATE/README | 34 + TEMPLATE/TEMPLATE.c | 28 + TEMPLATE/TEMPLATE.conf | 79 + TEMPLATE/TEMPLATE.cpp | 28 + TEMPLATE/TEMPLATE.h | 35 + TEMPLATE/conf/gb.cairo.conf | 76 + TEMPLATE/conf/gb.dbus.conf | 76 + TEMPLATE/conf/gb.desktop.conf | 76 + TEMPLATE/conf/gb.form.htmlview.conf | 80 + TEMPLATE/conf/gb.gmp.conf | 76 + TEMPLATE/conf/gb.gsl.conf | 76 + TEMPLATE/conf/gb.image.io.conf | 76 + TEMPLATE/conf/gb.media.conf | 76 + TEMPLATE/conf/gb.mime.conf | 76 + TEMPLATE/conf/gb.net.pop3.conf | 76 + TEMPLATE/conf/gb.net.smtp.conf | 76 + TEMPLATE/conf/gb.openal.conf | 76 + TEMPLATE/conf/gb.poppler.conf | 79 + TEMPLATE/make-component | 64 + TEMPLATE/template/AUTHORS | 0 TEMPLATE/template/ChangeLog | 0 TEMPLATE/template/Makefile.am | 3 + TEMPLATE/template/NEWS | 0 TEMPLATE/template/README | 0 TEMPLATE/template/SOURCES | 2 + TEMPLATE/template/configure.ac | 72 + TEMPLATE/template/make-component | 3 + TEMPLATE/template/src/.component | 3 + TEMPLATE/template/src/Makefile.am | 12 + TODO | 74 + VERSION | 1 + acinclude.m4 | 1727 + app/AUTHORS | 0 app/COPYING | 1 + app/ChangeLog | 0 app/INSTALL | 231 + app/Makefile.am | 86 + app/NEWS | 0 app/README | 0 app/TODO | 0 app/acinclude.m4 | 1 + app/configure.ac | 21 + app/desktop/gambas3-48.png | Bin 0 -> 1864 bytes app/desktop/gambas3.appdata.xml | 31 + app/desktop/gambas3.desktop | 30 + app/desktop/gambas3.png | Bin 0 -> 6834 bytes app/desktop/gambas3.svg | 166 + app/examples/Basic/Blights/.directory | 2 + app/examples/Basic/Blights/.icon.png | Bin 0 -> 4209 bytes app/examples/Basic/Blights/.lang/ca.po | 28 + app/examples/Basic/Blights/.lang/cs.po | 20 + app/examples/Basic/Blights/.lang/de.po | 21 + app/examples/Basic/Blights/.lang/es.po | 20 + app/examples/Basic/Blights/.lang/fr.po | 3 + app/examples/Basic/Blights/.lang/nl.po | 20 + app/examples/Basic/Blights/.lang/ru.po | 34 + app/examples/Basic/Blights/.lang/sv.po | 15 + app/examples/Basic/Blights/.project | 16 + app/examples/Basic/Blights/.src/win1.class | 36 + app/examples/Basic/Blights/.src/win1.form | 53 + app/examples/Basic/Blights/ampoule.png | Bin 0 -> 1543 bytes app/examples/Basic/Blights/bloff.xpm | 1234 + app/examples/Basic/Blights/blon.xpm | 1503 + app/examples/Basic/Collection/.directory | 2 + app/examples/Basic/Collection/.icon.png | Bin 0 -> 4310 bytes app/examples/Basic/Collection/.lang/ca.po | 48 + app/examples/Basic/Collection/.lang/cs.po | 40 + app/examples/Basic/Collection/.lang/de.po | 41 + app/examples/Basic/Collection/.lang/es.po | 41 + app/examples/Basic/Collection/.lang/nl.po | 41 + app/examples/Basic/Collection/.lang/ru.po | 54 + app/examples/Basic/Collection/.project | 17 + .../Basic/Collection/.src/CThing.class | 6 + .../Basic/Collection/.src/FStart.class | 40 + .../Basic/Collection/.src/FStart.form | 20 + app/examples/Basic/Collection/collection.png | Bin 0 -> 2062 bytes app/examples/Basic/DragNDrop/.directory | 2 + app/examples/Basic/DragNDrop/.icon.png | Bin 0 -> 4159 bytes app/examples/Basic/DragNDrop/.lang/ru.po | 34 + app/examples/Basic/DragNDrop/.project | 17 + .../Basic/DragNDrop/.src/FDragNDrop.class | 138 + .../Basic/DragNDrop/.src/FDragNDrop.form | 108 + app/examples/Basic/DragNDrop/drop.png | Bin 0 -> 1503 bytes app/examples/Basic/Object/.directory | 2 + app/examples/Basic/Object/.icon.png | Bin 0 -> 4189 bytes app/examples/Basic/Object/.lang/ca.po | 43 + app/examples/Basic/Object/.lang/cs.po | 40 + app/examples/Basic/Object/.lang/de.po | 41 + app/examples/Basic/Object/.lang/es.po | 37 + app/examples/Basic/Object/.lang/nl.po | 41 + app/examples/Basic/Object/.lang/ru.po | 58 + app/examples/Basic/Object/.project | 17 + app/examples/Basic/Object/.src/CThing.class | 6 + app/examples/Basic/Object/.src/FStart.class | 44 + app/examples/Basic/Object/.src/FStart.form | 28 + app/examples/Basic/Object/object.png | Bin 0 -> 1499 bytes app/examples/Basic/Timer/.directory | 2 + app/examples/Basic/Timer/.icon.png | Bin 0 -> 4418 bytes app/examples/Basic/Timer/.lang/ca.po | 113 + app/examples/Basic/Timer/.lang/cs.po | 92 + app/examples/Basic/Timer/.lang/de.po | 93 + app/examples/Basic/Timer/.lang/es.po | 96 + app/examples/Basic/Timer/.lang/nl.po | 92 + app/examples/Basic/Timer/.lang/ru.po | 114 + app/examples/Basic/Timer/.project | 19 + .../Basic/Timer/.src/FOtherTimer.class | 85 + .../Basic/Timer/.src/FOtherTimer.form | 40 + app/examples/Basic/Timer/.src/FTimer.class | 56 + app/examples/Basic/Timer/.src/FTimer.form | 100 + app/examples/Basic/Timer/timer.png | Bin 0 -> 2778 bytes .../Control/ArrayOfControls/.directory | 2 + .../Control/ArrayOfControls/.icon.png | Bin 0 -> 4209 bytes .../Control/ArrayOfControls/.lang/ca.po | 45 + .../Control/ArrayOfControls/.lang/cs.po | 44 + .../Control/ArrayOfControls/.lang/de.po | 44 + .../Control/ArrayOfControls/.lang/nl.po | 43 + .../Control/ArrayOfControls/.lang/ru.po | 58 + app/examples/Control/ArrayOfControls/.project | 18 + .../Control/ArrayOfControls/.src/FMain.class | 83 + .../Control/ArrayOfControls/.src/FMain.form | 25 + .../Control/ArrayOfControls/green.png | Bin 0 -> 873 bytes .../Control/ArrayOfControls/green1.png | Bin 0 -> 1548 bytes .../Control/ArrayOfControls/phone.png | Bin 0 -> 18663 bytes app/examples/Control/ArrayOfControls/red.png | Bin 0 -> 1452 bytes app/examples/Control/ArrayOfControls/red1.png | Bin 0 -> 1571 bytes app/examples/Control/Embedder/.directory | 2 + app/examples/Control/Embedder/.icon.png | Bin 0 -> 4242 bytes app/examples/Control/Embedder/.lang/ca.po | 70 + app/examples/Control/Embedder/.lang/cs.po | 62 + app/examples/Control/Embedder/.lang/de.po | 59 + app/examples/Control/Embedder/.lang/es.po | 57 + app/examples/Control/Embedder/.lang/nl.po | 55 + app/examples/Control/Embedder/.lang/ru.po | 77 + app/examples/Control/Embedder/.project | 20 + .../Control/Embedder/.src/FMain.class | 89 + app/examples/Control/Embedder/.src/FMain.form | 54 + app/examples/Control/Embedder/embedder.png | Bin 0 -> 2623 bytes .../Control/HighlightEditor/.directory | 2 + .../.hidden/screenshots/2014-12-17.png | Bin 0 -> 58870 bytes .../Control/HighlightEditor/.icon.png | Bin 0 -> 18015 bytes .../Control/HighlightEditor/.lang/ca.po | 43 + .../Control/HighlightEditor/.lang/cs.po | 40 + .../Control/HighlightEditor/.lang/de.po | 41 + .../Control/HighlightEditor/.lang/es.po | 34 + .../Control/HighlightEditor/.lang/nl.po | 39 + .../Control/HighlightEditor/.lang/ru.po | 64 + app/examples/Control/HighlightEditor/.project | 21 + .../HighlightEditor/.src/FEditor.class | 184 + .../Control/HighlightEditor/.src/FEditor.form | 36 + .../Control/HighlightEditor/download.html | 155 + .../Control/HighlightEditor/editor.png | Bin 0 -> 1657 bytes app/examples/Control/LCDLabel/.directory | 2 + app/examples/Control/LCDLabel/.icon.png | Bin 0 -> 3429 bytes app/examples/Control/LCDLabel/.lang/ru.po | 48 + app/examples/Control/LCDLabel/.project | 17 + .../Control/LCDLabel/.src/FMain.class | 54 + app/examples/Control/LCDLabel/.src/FMain.form | 33 + .../Control/LCDLabel/.src/TimeBoxDemo.class | 152 + app/examples/Control/LCDLabel/alarm.ogg | Bin 0 -> 61621 bytes app/examples/Control/LCDLabel/lcdlabel.png | Bin 0 -> 492 bytes app/examples/Control/MapView/.directory | 2 + .../Control/MapView/.hidden/mapview.png | Bin 0 -> 1749 bytes app/examples/Control/MapView/.icon.png | Bin 0 -> 3486 bytes app/examples/Control/MapView/.lang/ru.po | 78 + app/examples/Control/MapView/.project | 18 + app/examples/Control/MapView/.src/FMain.class | 140 + app/examples/Control/MapView/.src/FMain.form | 80 + app/examples/Control/TextEdit/.directory | 2 + app/examples/Control/TextEdit/.icon.png | Bin 0 -> 4151 bytes app/examples/Control/TextEdit/.lang/ca.po | 118 + app/examples/Control/TextEdit/.lang/cs.po | 109 + app/examples/Control/TextEdit/.lang/de.po | 116 + app/examples/Control/TextEdit/.lang/es.po | 101 + app/examples/Control/TextEdit/.lang/fr.po | 83 + app/examples/Control/TextEdit/.lang/nl.po | 99 + app/examples/Control/TextEdit/.lang/ru.po | 120 + app/examples/Control/TextEdit/.lang/sv.po | 84 + app/examples/Control/TextEdit/.project | 22 + .../Control/TextEdit/.src/FMain.class | 171 + app/examples/Control/TextEdit/.src/FMain.form | 136 + .../Control/TextEdit/.src/frmShowHtml.class | 29 + .../Control/TextEdit/.src/frmShowHtml.form | 29 + app/examples/Control/TextEdit/edit.png | Bin 0 -> 1233 bytes app/examples/Control/TextEdit/text.html | 17 + app/examples/Control/TreeView/.directory | 2 + app/examples/Control/TreeView/.icon.png | Bin 0 -> 4093 bytes app/examples/Control/TreeView/.lang/ca.po | 88 + app/examples/Control/TreeView/.lang/cs.po | 76 + app/examples/Control/TreeView/.lang/de.po | 77 + app/examples/Control/TreeView/.lang/es.po | 73 + app/examples/Control/TreeView/.lang/nl.po | 75 + app/examples/Control/TreeView/.lang/ru.po | 154 + app/examples/Control/TreeView/.project | 17 + .../TreeView/.src/TreeViewExample.class | 184 + .../TreeView/.src/TreeViewExample.form | 59 + app/examples/Control/TreeView/Female.png | Bin 0 -> 293 bytes app/examples/Control/TreeView/Male.png | Bin 0 -> 323 bytes app/examples/Control/TreeView/treeview.png | Bin 0 -> 471 bytes app/examples/Control/Wizard/.directory | 2 + app/examples/Control/Wizard/.icon.png | Bin 0 -> 4168 bytes app/examples/Control/Wizard/.lang/ca.po | 139 + app/examples/Control/Wizard/.lang/cs.po | 138 + app/examples/Control/Wizard/.lang/de.po | 138 + app/examples/Control/Wizard/.lang/nl.po | 107 + app/examples/Control/Wizard/.lang/ru.po | 152 + app/examples/Control/Wizard/.project | 16 + app/examples/Control/Wizard/.src/FMain.class | 65 + app/examples/Control/Wizard/.src/FMain.form | 118 + app/examples/Control/Wizard/wizard.png | Bin 0 -> 1510 bytes app/examples/Database/Database/.component | 4 + app/examples/Database/Database/.directory | 2 + app/examples/Database/Database/.icon.png | Bin 0 -> 4336 bytes app/examples/Database/Database/.lang/ca.po | 160 + app/examples/Database/Database/.lang/cs.po | 157 + app/examples/Database/Database/.lang/de.po | 153 + app/examples/Database/Database/.lang/es.po | 151 + app/examples/Database/Database/.lang/nl.po | 147 + app/examples/Database/Database/.lang/ru.po | 198 + app/examples/Database/Database/.project | 19 + .../Database/Database/.src/FMain.class | 178 + .../Database/Database/.src/FMain.form | 118 + .../Database/Database/.src/FRequest.class | 131 + .../Database/Database/.src/FRequest.form | 12 + .../Database/Database/.src/FTest.class | 25 + .../Database/Database/.src/FTest.form | 121 + .../Database/Database/.src/Form1.class | 83 + .../Database/Database/.src/Form1.form | 8 + app/examples/Database/Database/database.png | Bin 0 -> 1142 bytes app/examples/Database/MySQLExample/.directory | 2 + app/examples/Database/MySQLExample/.icon.png | Bin 0 -> 4409 bytes .../Database/MySQLExample/.lang/ca.po | 539 + .../Database/MySQLExample/.lang/cs.po | 536 + .../Database/MySQLExample/.lang/de.po | 536 + .../Database/MySQLExample/.lang/es.po | 533 + .../Database/MySQLExample/.lang/fr.po | 1546 + .../Database/MySQLExample/.lang/nl.po | 531 + .../Database/MySQLExample/.lang/ru.po | 554 + app/examples/Database/MySQLExample/.project | 26 + .../.src/CreateObjects/FNewDatabase.class | 33 + .../.src/CreateObjects/FNewDatabase.form | 54 + .../.src/CreateObjects/FNewEvent.class | 51 + .../.src/CreateObjects/FNewEvent.form | 53 + .../.src/CreateObjects/FNewField.class | 53 + .../.src/CreateObjects/FNewField.form | 77 + .../.src/CreateObjects/FNewIndex.class | 79 + .../.src/CreateObjects/FNewIndex.form | 70 + .../.src/CreateObjects/FNewRoutine.class | 65 + .../.src/CreateObjects/FNewRoutine.form | 71 + .../.src/CreateObjects/FNewTable.class | 55 + .../.src/CreateObjects/FNewTable.form | 54 + .../.src/CreateObjects/FNewTrigger.class | 62 + .../.src/CreateObjects/FNewTrigger.form | 64 + .../.src/CreateObjects/FNewView.class | 45 + .../.src/CreateObjects/FNewView.form | 44 + .../Database/MySQLExample/.src/FConnect.class | 46 + .../Database/MySQLExample/.src/FConnect.form | 73 + .../Database/MySQLExample/.src/FMessage.class | 21 + .../Database/MySQLExample/.src/FMessage.form | 12 + .../Database/MySQLExample/.src/FTables.class | 1186 + .../Database/MySQLExample/.src/FTables.form | 206 + .../Database/MySQLExample/.src/modMain.module | 69 + .../Database/MySQLExample/icons/16/Admin.png | Bin 0 -> 1099 bytes .../Database/MySQLExample/icons/16/Blob.png | Bin 0 -> 646 bytes .../MySQLExample/icons/16/Column_FK.png | Bin 0 -> 529 bytes .../MySQLExample/icons/16/Database.png | Bin 0 -> 805 bytes .../MySQLExample/icons/16/Datetime.png | Bin 0 -> 1140 bytes .../Database/MySQLExample/icons/16/Field.png | Bin 0 -> 653 bytes .../MySQLExample/icons/16/Function.png | Bin 0 -> 245 bytes .../Database/MySQLExample/icons/16/Index.png | Bin 0 -> 1122 bytes .../Database/MySQLExample/icons/16/Lock.png | Bin 0 -> 1195 bytes .../Database/MySQLExample/icons/16/New.png | Bin 0 -> 817 bytes .../MySQLExample/icons/16/Numeric.png | Bin 0 -> 1021 bytes .../MySQLExample/icons/16/Primarykey.png | Bin 0 -> 585 bytes .../MySQLExample/icons/16/Refresh.png | Bin 0 -> 349 bytes .../MySQLExample/icons/16/Routine.png | Bin 0 -> 827 bytes .../Database/MySQLExample/icons/16/String.png | Bin 0 -> 1024 bytes .../Database/MySQLExample/icons/16/Table.png | Bin 0 -> 823 bytes .../MySQLExample/icons/16/Trigger.png | Bin 0 -> 930 bytes .../Database/MySQLExample/icons/16/View.png | Bin 0 -> 697 bytes .../MySQLExample/icons/16/Warning.png | Bin 0 -> 543 bytes .../Database/MySQLExample/icons/24/Null.png | Bin 0 -> 121 bytes .../Database/PictureDatabase/.directory | 2 + .../Database/PictureDatabase/.icon.png | Bin 0 -> 4228 bytes .../Database/PictureDatabase/.lang/ca.po | 84 + .../Database/PictureDatabase/.lang/cs.po | 75 + .../Database/PictureDatabase/.lang/de.po | 76 + .../Database/PictureDatabase/.lang/es.po | 36 + .../Database/PictureDatabase/.lang/nl.po | 67 + .../Database/PictureDatabase/.lang/ru.po | 194 + .../Database/PictureDatabase/.project | 25 + .../.src/FormPictureDatabase.class | 227 + .../.src/FormPictureDatabase.form | 67 + .../.src/ModuleDatabase.module | 215 + .../Images/document-save-as.png | Bin 0 -> 845 bytes .../PictureDatabase/Images/document-save.png | Bin 0 -> 903 bytes .../Images/image-x-generic.png | Bin 0 -> 1150 bytes .../PictureDatabase/Images/list-add.png | Bin 0 -> 366 bytes .../PictureDatabase/Images/list-remove.png | Bin 0 -> 282 bytes app/examples/Drawing/AnalogWatch/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 24631 bytes app/examples/Drawing/AnalogWatch/.icon.png | Bin 0 -> 3642 bytes app/examples/Drawing/AnalogWatch/.lang/ru.po | 72 + app/examples/Drawing/AnalogWatch/.project | 18 + .../Drawing/AnalogWatch/.src/FrmClock.class | 152 + .../Drawing/AnalogWatch/.src/FrmClock.form | 30 + app/examples/Drawing/AnalogWatch/timer.png | Bin 0 -> 2778 bytes app/examples/Drawing/Barcode/.directory | 2 + app/examples/Drawing/Barcode/.icon.png | Bin 0 -> 3990 bytes app/examples/Drawing/Barcode/.lang/ca.po | 77 + app/examples/Drawing/Barcode/.lang/cs.po | 76 + app/examples/Drawing/Barcode/.lang/de.po | 76 + app/examples/Drawing/Barcode/.lang/ru.po | 90 + app/examples/Drawing/Barcode/.project | 21 + app/examples/Drawing/Barcode/.src/FMain.class | 161 + app/examples/Drawing/Barcode/.src/FMain.form | 88 + .../Drawing/Barcode/.src/modCrBcode.module | 98 + app/examples/Drawing/Barcode/barcode.png | Bin 0 -> 765 bytes app/examples/Drawing/Chart/.directory | 2 + app/examples/Drawing/Chart/.icon.png | Bin 0 -> 4083 bytes app/examples/Drawing/Chart/.lang/ca.po | 85 + app/examples/Drawing/Chart/.lang/cs.po | 84 + app/examples/Drawing/Chart/.lang/de.po | 81 + app/examples/Drawing/Chart/.lang/es.po | 78 + app/examples/Drawing/Chart/.lang/ru.po | 114 + app/examples/Drawing/Chart/.project | 17 + .../Drawing/Chart/.src/FormChart.class | 112 + .../Drawing/Chart/.src/FormChart.form | 32 + .../Drawing/Chart/.src/FormData.class | 43 + app/examples/Drawing/Chart/.src/FormData.form | 89 + app/examples/Drawing/Chart/graph.png | Bin 0 -> 225 bytes app/examples/Drawing/Clock/.directory | 2 + app/examples/Drawing/Clock/.icon.png | Bin 0 -> 4367 bytes app/examples/Drawing/Clock/.icon/16.png | Bin 0 -> 949 bytes app/examples/Drawing/Clock/.icon/32.png | Bin 0 -> 2599 bytes app/examples/Drawing/Clock/.icon/48.png | Bin 0 -> 4946 bytes app/examples/Drawing/Clock/.lang/ca.po | 63 + app/examples/Drawing/Clock/.lang/cs.po | 56 + app/examples/Drawing/Clock/.lang/de.po | 57 + app/examples/Drawing/Clock/.lang/es.po | 53 + app/examples/Drawing/Clock/.lang/ru.po | 84 + app/examples/Drawing/Clock/.project | 21 + app/examples/Drawing/Clock/.src/FClock.class | 170 + app/examples/Drawing/Clock/.src/FClock.form | 55 + app/examples/Drawing/Clock/img/arrow_hour.png | Bin 0 -> 396 bytes app/examples/Drawing/Clock/img/arrow_min.png | Bin 0 -> 420 bytes app/examples/Drawing/Clock/img/arrow_sec.png | Bin 0 -> 1253 bytes .../Drawing/Clock/img/clock_bg_big1.png | Bin 0 -> 19642 bytes .../Drawing/Clock/img/clock_bg_big2.png | Bin 0 -> 24928 bytes .../Drawing/Clock/img/clock_bg_big3.png | Bin 0 -> 50841 bytes .../Drawing/Clock/img/clock_bg_big4.png | Bin 0 -> 21425 bytes app/examples/Drawing/Fractal/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 152730 bytes app/examples/Drawing/Fractal/.icon.png | Bin 0 -> 3483 bytes app/examples/Drawing/Fractal/.lang/cs.po | 60 + app/examples/Drawing/Fractal/.lang/fr.po | 61 + app/examples/Drawing/Fractal/.lang/ru.po | 80 + app/examples/Drawing/Fractal/.project | 18 + .../Drawing/Fractal/.src/FFractal.class | 245 + .../Drawing/Fractal/.src/FFractal.form | 18 + .../Drawing/Fractal/.src/FractalTask.class | 324 + app/examples/Drawing/Fractal/icon.png | Bin 0 -> 4737 bytes app/examples/Drawing/Fractal/rose.jpg | Bin 0 -> 26938 bytes app/examples/Drawing/GSLSpline/.directory | 2 + app/examples/Drawing/GSLSpline/.icon.png | Bin 0 -> 4045 bytes app/examples/Drawing/GSLSpline/.lang/ru.po | 54 + app/examples/Drawing/GSLSpline/.project | 15 + .../Drawing/GSLSpline/.src/FMain.class | 130 + .../Drawing/GSLSpline/.src/FMain.form | 45 + app/examples/Drawing/GSLSpline/spline.png | Bin 0 -> 1632 bytes app/examples/Drawing/Gravity/.directory | 2 + app/examples/Drawing/Gravity/.icon.png | Bin 0 -> 4437 bytes app/examples/Drawing/Gravity/.lang/ca.po | 135 + app/examples/Drawing/Gravity/.lang/cs.po | 128 + app/examples/Drawing/Gravity/.lang/de.po | 129 + app/examples/Drawing/Gravity/.lang/es.po | 126 + app/examples/Drawing/Gravity/.lang/ru.po | 162 + app/examples/Drawing/Gravity/.project | 18 + .../Drawing/Gravity/.src/FAbout.class | 8 + app/examples/Drawing/Gravity/.src/FAbout.form | 42 + app/examples/Drawing/Gravity/.src/FMain.class | 232 + app/examples/Drawing/Gravity/.src/FMain.form | 143 + app/examples/Drawing/Gravity/.src/cBall.class | 9 + app/examples/Drawing/Gravity/gravity.png | Bin 0 -> 7084 bytes .../Drawing/OnScreenDisplay/.directory | 2 + .../Drawing/OnScreenDisplay/.icon.png | Bin 0 -> 4186 bytes .../Drawing/OnScreenDisplay/.lang/ca.po | 27 + .../Drawing/OnScreenDisplay/.lang/cs.po | 20 + .../Drawing/OnScreenDisplay/.lang/de.po | 21 + .../Drawing/OnScreenDisplay/.lang/es.po | 15 + .../Drawing/OnScreenDisplay/.lang/ru.po | 34 + app/examples/Drawing/OnScreenDisplay/.project | 17 + .../.src/FOnScreenDisplay.class | 67 + .../.src/FOnScreenDisplay.form | 12 + app/examples/Drawing/OnScreenDisplay/icon.png | Bin 0 -> 1300 bytes app/examples/Drawing/Painting/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 90069 bytes app/examples/Drawing/Painting/.icon.png | Bin 0 -> 3331 bytes app/examples/Drawing/Painting/.lang/ca.po | 126 + app/examples/Drawing/Painting/.lang/cs.po | 128 + app/examples/Drawing/Painting/.lang/de.po | 129 + app/examples/Drawing/Painting/.lang/ru.po | 148 + app/examples/Drawing/Painting/.project | 16 + .../Drawing/Painting/.src/FMain.class | 548 + app/examples/Drawing/Painting/.src/FMain.form | 195 + .../Painting/.src/MMakeSourceFile.module | 32 + app/examples/Drawing/Painting/Example1 | 29 + app/examples/Drawing/Painting/Example10 | 11 + app/examples/Drawing/Painting/Example11 | 26 + app/examples/Drawing/Painting/Example12 | 18 + app/examples/Drawing/Painting/Example13 | 16 + app/examples/Drawing/Painting/Example14 | 28 + app/examples/Drawing/Painting/Example15 | 22 + app/examples/Drawing/Painting/Example16 | 25 + app/examples/Drawing/Painting/Example17 | 29 + app/examples/Drawing/Painting/Example18 | 16 + app/examples/Drawing/Painting/Example2 | 29 + app/examples/Drawing/Painting/Example20 | 17 + app/examples/Drawing/Painting/Example21 | 14 + app/examples/Drawing/Painting/Example22 | 10 + app/examples/Drawing/Painting/Example3 | 15 + app/examples/Drawing/Painting/Example4 | 13 + app/examples/Drawing/Painting/Example5 | 61 + app/examples/Drawing/Painting/Example6 | 27 + app/examples/Drawing/Painting/Example7 | 17 + app/examples/Drawing/Painting/Example8 | 22 + app/examples/Drawing/Painting/Example9 | 23 + app/examples/Drawing/Painting/clovis.jpg | Bin 0 -> 15838 bytes app/examples/Drawing/Painting/gambas.svg | 540 + app/examples/Drawing/Painting/icon.png | Bin 0 -> 2078 bytes app/examples/Drawing/Painting/image.jpg | Bin 0 -> 19496 bytes app/examples/Drawing/QuasiRegular/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 44667 bytes app/examples/Drawing/QuasiRegular/.icon.png | Bin 0 -> 3730 bytes app/examples/Drawing/QuasiRegular/.project | 14 + .../Drawing/QuasiRegular/.src/FMain.class | 87 + .../Drawing/QuasiRegular/.src/FMain.form | 10 + app/examples/Drawing/QuasiRegular/icon.png | Bin 0 -> 705 bytes .../Drawing/RandomColorSort/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 247987 bytes .../Drawing/RandomColorSort/.icon.png | Bin 0 -> 3663 bytes .../Drawing/RandomColorSort/.lang/ru.po | 96 + app/examples/Drawing/RandomColorSort/.project | 16 + .../Drawing/RandomColorSort/.src/FMain.class | 265 + .../Drawing/RandomColorSort/.src/FMain.form | 59 + .../RandomColorSort/RandomColorSort.png | Bin 0 -> 10533 bytes app/examples/Drawing/Tablet/.directory | 2 + .../Tablet/.hidden/screenshots/2014-12-14.png | Bin 0 -> 59466 bytes app/examples/Drawing/Tablet/.icon.png | Bin 0 -> 3536 bytes app/examples/Drawing/Tablet/.lang/ru.po | 112 + app/examples/Drawing/Tablet/.project | 16 + app/examples/Drawing/Tablet/.src/FMain.class | 178 + app/examples/Drawing/Tablet/.src/FMain.form | 174 + app/examples/Drawing/Tablet/Icon.png | Bin 0 -> 3753 bytes app/examples/Games/BeastScroll/.dir_icon.png | Bin 0 -> 1571 bytes app/examples/Games/BeastScroll/.directory | 2 + app/examples/Games/BeastScroll/.icon.png | Bin 0 -> 3371 bytes app/examples/Games/BeastScroll/.lang/ru.po | 56 + app/examples/Games/BeastScroll/.project | 18 + .../Games/BeastScroll/.src/MMain.module | 215 + app/examples/Games/BeastScroll/b-title.mod | Bin 0 -> 56838 bytes app/examples/Games/BeastScroll/bgd1_ciel.png | Bin 0 -> 6797 bytes .../Games/BeastScroll/bgd2_montagnes.png | Bin 0 -> 177395 bytes app/examples/Games/BeastScroll/bgd3_sol1.png | Bin 0 -> 9994 bytes app/examples/Games/BeastScroll/bgd4_sol2.png | Bin 0 -> 9668 bytes app/examples/Games/BeastScroll/bgd5_sol3.png | Bin 0 -> 4772 bytes app/examples/Games/BeastScroll/fireworks.png | Bin 0 -> 46710 bytes app/examples/Games/BeastScroll/logo.png | Bin 0 -> 4768 bytes app/examples/Games/BeastScroll/scrolltext.png | Bin 0 -> 18100 bytes .../Games/BeastScroll/sprite_arbre.png | Bin 0 -> 73213 bytes .../Games/BeastScroll/sprite_barriere.png | Bin 0 -> 6455 bytes .../Games/BeastScroll/sprite_nuages1.png | Bin 0 -> 14285 bytes .../Games/BeastScroll/sprite_nuages2.png | Bin 0 -> 5079 bytes .../Games/BeastScroll/sprite_nuages3.png | Bin 0 -> 2389 bytes .../Games/BeastScroll/sprite_nuages4.png | Bin 0 -> 1036 bytes app/examples/Games/Concent/.directory | 2 + app/examples/Games/Concent/.icon.png | Bin 0 -> 4417 bytes app/examples/Games/Concent/.icon/16.png | Bin 0 -> 949 bytes app/examples/Games/Concent/.icon/32.png | Bin 0 -> 2599 bytes app/examples/Games/Concent/.icon/48.png | Bin 0 -> 4946 bytes app/examples/Games/Concent/.lang/ca.po | 217 + app/examples/Games/Concent/.lang/cs.po | 224 + app/examples/Games/Concent/.lang/de.po | 168 + app/examples/Games/Concent/.lang/en.po | 226 + app/examples/Games/Concent/.lang/es.po | 120 + app/examples/Games/Concent/.lang/fr.po | 164 + app/examples/Games/Concent/.lang/ru.po | 222 + app/examples/Games/Concent/.project | 22 + app/examples/Games/Concent/.src/fotos.class | 7 + app/examples/Games/Concent/.src/fotos.form | 210 + .../Games/Concent/.src/frmAcerca.class | 12 + .../Games/Concent/.src/frmAcerca.form | 20 + .../Games/Concent/.src/frmInstrucciones.class | 11 + .../Games/Concent/.src/frmInstrucciones.form | 22 + .../Games/Concent/.src/funciones.module | 162 + .../Games/Concent/.src/principal.class | 390 + .../Games/Concent/.src/principal.form | 220 + app/examples/Games/Concent/Blockhit.wav | Bin 0 -> 1148 bytes app/examples/Games/Concent/CHANGELOG | 6 + app/examples/Games/Concent/Missed.wav | Bin 0 -> 15612 bytes app/examples/Games/Concent/Newlevel.wav | Bin 0 -> 19842 bytes app/examples/Games/Concent/Paddle.wav | Bin 0 -> 1754 bytes app/examples/Games/Concent/Setup.wav | Bin 0 -> 768 bytes app/examples/Games/Concent/Wallhit.wav | Bin 0 -> 768 bytes app/examples/Games/Concent/applause.wav | Bin 0 -> 33654 bytes app/examples/Games/Concent/imagenes/an1.gif | Bin 0 -> 1601 bytes app/examples/Games/Concent/imagenes/an10.gif | Bin 0 -> 2209 bytes app/examples/Games/Concent/imagenes/an11.gif | Bin 0 -> 1365 bytes app/examples/Games/Concent/imagenes/an12.gif | Bin 0 -> 1332 bytes app/examples/Games/Concent/imagenes/an13.gif | Bin 0 -> 2223 bytes app/examples/Games/Concent/imagenes/an14.gif | Bin 0 -> 1924 bytes app/examples/Games/Concent/imagenes/an15.gif | Bin 0 -> 1132 bytes app/examples/Games/Concent/imagenes/an16.gif | Bin 0 -> 1318 bytes app/examples/Games/Concent/imagenes/an17.gif | Bin 0 -> 1845 bytes app/examples/Games/Concent/imagenes/an18.gif | Bin 0 -> 1571 bytes app/examples/Games/Concent/imagenes/an19.gif | Bin 0 -> 1849 bytes app/examples/Games/Concent/imagenes/an2.gif | Bin 0 -> 1373 bytes app/examples/Games/Concent/imagenes/an20.gif | Bin 0 -> 1540 bytes app/examples/Games/Concent/imagenes/an21.gif | Bin 0 -> 1720 bytes app/examples/Games/Concent/imagenes/an22.gif | Bin 0 -> 1332 bytes app/examples/Games/Concent/imagenes/an23.gif | Bin 0 -> 961 bytes app/examples/Games/Concent/imagenes/an24.gif | Bin 0 -> 1627 bytes app/examples/Games/Concent/imagenes/an25.gif | Bin 0 -> 1694 bytes app/examples/Games/Concent/imagenes/an26.gif | Bin 0 -> 1931 bytes app/examples/Games/Concent/imagenes/an27.gif | Bin 0 -> 2124 bytes app/examples/Games/Concent/imagenes/an28.gif | Bin 0 -> 1944 bytes app/examples/Games/Concent/imagenes/an29.gif | Bin 0 -> 1414 bytes app/examples/Games/Concent/imagenes/an3.gif | Bin 0 -> 1279 bytes app/examples/Games/Concent/imagenes/an30.gif | Bin 0 -> 1891 bytes app/examples/Games/Concent/imagenes/an31.gif | Bin 0 -> 1910 bytes app/examples/Games/Concent/imagenes/an32.gif | Bin 0 -> 1455 bytes app/examples/Games/Concent/imagenes/an33.gif | Bin 0 -> 1444 bytes app/examples/Games/Concent/imagenes/an34.gif | Bin 0 -> 1772 bytes app/examples/Games/Concent/imagenes/an35.gif | Bin 0 -> 1737 bytes app/examples/Games/Concent/imagenes/an36.gif | Bin 0 -> 1686 bytes app/examples/Games/Concent/imagenes/an37.gif | Bin 0 -> 2003 bytes app/examples/Games/Concent/imagenes/an38.gif | Bin 0 -> 1782 bytes app/examples/Games/Concent/imagenes/an39.gif | Bin 0 -> 2443 bytes app/examples/Games/Concent/imagenes/an4.gif | Bin 0 -> 1227 bytes app/examples/Games/Concent/imagenes/an40.gif | Bin 0 -> 2582 bytes app/examples/Games/Concent/imagenes/an5.gif | Bin 0 -> 1628 bytes app/examples/Games/Concent/imagenes/an6.gif | Bin 0 -> 2511 bytes app/examples/Games/Concent/imagenes/an7.gif | Bin 0 -> 1811 bytes app/examples/Games/Concent/imagenes/an8.gif | Bin 0 -> 1756 bytes app/examples/Games/Concent/imagenes/an9.gif | Bin 0 -> 2486 bytes .../Games/Concent/imagenes/colombia.gif | Bin 0 -> 145 bytes app/examples/Games/Concent/imagenes/inter.gif | Bin 0 -> 131 bytes app/examples/Games/Concent/imagenes/inter.jpg | Bin 0 -> 1148 bytes app/examples/Games/Concent/imagenes/logo.gif | Bin 0 -> 6138 bytes app/examples/Games/Concent/imagenes/logo.png | Bin 0 -> 1591 bytes app/examples/Games/Concent/imagenes/ok.gif | Bin 0 -> 1355 bytes .../Games/Concent/imagenes/tierra.gif | Bin 0 -> 32485 bytes .../Games/Concent/imagenes/tierra3.jpg | Bin 0 -> 23070 bytes app/examples/Games/Concent/move.wav | Bin 0 -> 13318 bytes app/examples/Games/Concent/shuffle.wav | Bin 0 -> 6999 bytes app/examples/Games/DeepSpace/.directory | 2 + app/examples/Games/DeepSpace/.icon.png | Bin 0 -> 4334 bytes app/examples/Games/DeepSpace/.lang/ca.po | 51 + app/examples/Games/DeepSpace/.lang/cs.po | 44 + app/examples/Games/DeepSpace/.lang/de.po | 44 + app/examples/Games/DeepSpace/.lang/es.po | 44 + app/examples/Games/DeepSpace/.lang/ru.po | 78 + app/examples/Games/DeepSpace/.project | 17 + .../Games/DeepSpace/.src/CBullet.class | 11 + .../Games/DeepSpace/.src/CObject.class | 87 + .../Games/DeepSpace/.src/FAbout.class | 19 + app/examples/Games/DeepSpace/.src/FAbout.form | 19 + app/examples/Games/DeepSpace/.src/FMain.class | 81 + app/examples/Games/DeepSpace/.src/FMain.form | 35 + .../Games/DeepSpace/.src/MMain.module | 348 + .../Games/DeepSpace/.src/MMath.module | 64 + .../Games/DeepSpace/doc/.html_files/eg3.gif | Bin 0 -> 2782 bytes .../Games/DeepSpace/doc/.html_files/eg3b.gif | Bin 0 -> 4590 bytes .../Games/DeepSpace/doc/coordinates.html | 150 + app/examples/Games/DeepSpace/doc/howto.txt | 16 + app/examples/Games/DeepSpace/doc/todo.txt | 17 + .../Games/DeepSpace/images/deepspace.png | Bin 0 -> 14195 bytes .../Games/DeepSpace/object.data/kite.2do | 8 + .../Games/DeepSpace/object.data/main.lst | 12 + .../Games/DeepSpace/object.data/ship.2do | 13 + .../Games/DeepSpace/object.data/triangle.2do | 8 + .../Games/DeepSpace/object.data/x-wing.2do | 38 + app/examples/Games/GNUBoxWorld/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 185109 bytes app/examples/Games/GNUBoxWorld/.icon.png | Bin 0 -> 3722 bytes app/examples/Games/GNUBoxWorld/.lang/ca.po | 112 + app/examples/Games/GNUBoxWorld/.lang/cs.po | 116 + app/examples/Games/GNUBoxWorld/.lang/de.po | 105 + app/examples/Games/GNUBoxWorld/.lang/es_AR.po | 101 + app/examples/Games/GNUBoxWorld/.lang/ru.po | 160 + app/examples/Games/GNUBoxWorld/.project | 19 + .../Games/GNUBoxWorld/.src/Cell.class | 78 + .../Games/GNUBoxWorld/.src/FMain.class | 274 + .../Games/GNUBoxWorld/.src/FMain.form | 37 + .../Games/GNUBoxWorld/.src/FrmAbout.class | 19 + .../Games/GNUBoxWorld/.src/FrmAbout.form | 45 + .../Games/GNUBoxWorld/.src/GameBoard.class | 595 + app/examples/Games/GNUBoxWorld/License | 189 + app/examples/Games/GNUBoxWorld/abajo.png | Bin 0 -> 5205 bytes app/examples/Games/GNUBoxWorld/arriba.png | Bin 0 -> 5291 bytes app/examples/Games/GNUBoxWorld/derecha.png | Bin 0 -> 4347 bytes app/examples/Games/GNUBoxWorld/destino.png | Bin 0 -> 9527 bytes app/examples/Games/GNUBoxWorld/ganador.png | Bin 0 -> 255686 bytes app/examples/Games/GNUBoxWorld/icon.png | Bin 0 -> 5889 bytes app/examples/Games/GNUBoxWorld/izquierda.png | Bin 0 -> 4350 bytes app/examples/Games/GNUBoxWorld/logo.png | Bin 0 -> 108170 bytes app/examples/Games/GNUBoxWorld/movible.png | Bin 0 -> 3831 bytes .../Games/GNUBoxWorld/movibleendestino.png | Bin 0 -> 3656 bytes .../Games/GNUBoxWorld/obstaculo-l.png | Bin 0 -> 1633 bytes .../Games/GNUBoxWorld/obstaculo-lr.png | Bin 0 -> 1643 bytes .../Games/GNUBoxWorld/obstaculo-r.png | Bin 0 -> 1627 bytes app/examples/Games/GNUBoxWorld/obstaculo.png | Bin 0 -> 1575 bytes app/examples/Games/GNUBoxWorld/piso.png | Bin 0 -> 10046 bytes app/examples/Games/GameOfLife/.debug | 9 + app/examples/Games/GameOfLife/.directory | 2 + app/examples/Games/GameOfLife/.icon.png | Bin 0 -> 3502 bytes app/examples/Games/GameOfLife/.lang/ca.po | 131 + app/examples/Games/GameOfLife/.lang/cs.po | 130 + app/examples/Games/GameOfLife/.lang/de.po | 121 + app/examples/Games/GameOfLife/.lang/ru.po | 158 + app/examples/Games/GameOfLife/.project | 17 + .../Games/GameOfLife/.src/CGameField.class | 236 + .../Games/GameOfLife/.src/FMain.class | 95 + app/examples/Games/GameOfLife/.src/FMain.form | 162 + .../Games/GameOfLife/glob2-icon-48x48.png | Bin 0 -> 3390 bytes app/examples/Games/Invaders/.directory | 2 + app/examples/Games/Invaders/.icon.png | Bin 0 -> 3092 bytes app/examples/Games/Invaders/.project | 13 + .../Games/Invaders/.src/Enemies.class | 102 + app/examples/Games/Invaders/.src/Enemy.class | 161 + app/examples/Games/Invaders/.src/MMain.module | 139 + .../Games/Invaders/.src/Missile.class | 84 + .../Games/Invaders/.src/Missiles.class | 65 + app/examples/Games/Invaders/invaders.png | Bin 0 -> 125 bytes app/examples/Games/MineSweeper/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 48121 bytes app/examples/Games/MineSweeper/.icon.png | Bin 0 -> 3434 bytes app/examples/Games/MineSweeper/.lang/cs.po | 52 + app/examples/Games/MineSweeper/.lang/ja.po | 53 + app/examples/Games/MineSweeper/.lang/ru.po | 80 + app/examples/Games/MineSweeper/.lang/zh.po | 52 + app/examples/Games/MineSweeper/.lang/zh_TW.po | 52 + app/examples/Games/MineSweeper/.project | 19 + .../Games/MineSweeper/.src/FMain.class | 249 + .../Games/MineSweeper/.src/FMain.form | 77 + .../Games/MineSweeper/.src/FSettings.class | 66 + .../Games/MineSweeper/.src/FSettings.form | 71 + .../MineSweeper/.src/MineSweeperGame.class | 194 + .../Games/MineSweeper/image/bigflag.png | Bin 0 -> 6066 bytes .../Games/MineSweeper/image/cover.png | Bin 0 -> 331 bytes .../Games/MineSweeper/image/coveron.png | Bin 0 -> 238 bytes .../Games/MineSweeper/image/empty.png | Bin 0 -> 97 bytes .../Games/MineSweeper/image/expr_lose.png | Bin 0 -> 1941 bytes .../Games/MineSweeper/image/expr_normal.png | Bin 0 -> 1869 bytes .../Games/MineSweeper/image/expr_o.png | Bin 0 -> 1462 bytes .../Games/MineSweeper/image/expr_win.png | Bin 0 -> 2259 bytes .../Games/MineSweeper/image/false.png | Bin 0 -> 1021 bytes app/examples/Games/MineSweeper/image/flag.png | Bin 0 -> 765 bytes app/examples/Games/MineSweeper/image/mine.png | Bin 0 -> 755 bytes .../Games/MineSweeper/image/number_1.png | Bin 0 -> 388 bytes .../Games/MineSweeper/image/number_2.png | Bin 0 -> 407 bytes .../Games/MineSweeper/image/number_3.png | Bin 0 -> 428 bytes .../Games/MineSweeper/image/number_4.png | Bin 0 -> 387 bytes .../Games/MineSweeper/image/number_5.png | Bin 0 -> 381 bytes .../Games/MineSweeper/image/number_6.png | Bin 0 -> 462 bytes .../Games/MineSweeper/image/number_7.png | Bin 0 -> 367 bytes .../Games/MineSweeper/image/number_8.png | Bin 0 -> 454 bytes app/examples/Games/Pong/.directory | 2 + app/examples/Games/Pong/.icon.png | Bin 0 -> 4849 bytes app/examples/Games/Pong/.project | 11 + app/examples/Games/Pong/.src/Ball.class | 145 + app/examples/Games/Pong/.src/MMain.module | 191 + app/examples/Games/Pong/.src/NPC.class | 116 + app/examples/Games/Pong/.src/Paddle.class | 96 + app/examples/Games/Pong/SPEED | 47 + app/examples/Games/Pong/pong.png | Bin 0 -> 89 bytes app/examples/Games/Puzzle1To8/.directory | 2 + app/examples/Games/Puzzle1To8/.icon.png | Bin 0 -> 4303 bytes app/examples/Games/Puzzle1To8/.lang/ca.po | 98 + app/examples/Games/Puzzle1To8/.lang/cs.po | 92 + app/examples/Games/Puzzle1To8/.lang/de.po | 112 + app/examples/Games/Puzzle1To8/.lang/es_AR.po | 94 + app/examples/Games/Puzzle1To8/.lang/fr.po | 111 + app/examples/Games/Puzzle1To8/.lang/ru.po | 126 + app/examples/Games/Puzzle1To8/.project | 17 + .../Games/Puzzle1To8/.src/Casillero.class | 66 + .../Games/Puzzle1To8/.src/Esquema.class | 110 + .../Games/Puzzle1To8/.src/FMain.class | 111 + app/examples/Games/Puzzle1To8/.src/FMain.form | 95 + .../Games/Puzzle1To8/.src/FrmAbout.class | 18 + .../Games/Puzzle1To8/.src/FrmAbout.form | 39 + .../Games/Puzzle1To8/.src/FrmAyuda.class | 18 + .../Games/Puzzle1To8/.src/FrmAyuda.form | 37 + app/examples/Games/Puzzle1To8/Licence | 189 + app/examples/Games/Puzzle1To8/ejemplo1.png | Bin 0 -> 5594 bytes app/examples/Games/Puzzle1To8/ejemplo2.png | Bin 0 -> 5481 bytes app/examples/Games/Puzzle1To8/logo.png | Bin 0 -> 41279 bytes .../Games/RobotFindsKitten/.directory | 2 + app/examples/Games/RobotFindsKitten/.icon.png | Bin 0 -> 4141 bytes .../Games/RobotFindsKitten/.lang/ca.po | 40 + .../Games/RobotFindsKitten/.lang/cs.po | 32 + .../Games/RobotFindsKitten/.lang/de.po | 32 + .../Games/RobotFindsKitten/.lang/es.po | 28 + .../Games/RobotFindsKitten/.lang/ru.po | 50 + app/examples/Games/RobotFindsKitten/.project | 18 + .../Games/RobotFindsKitten/.src/Frfk.class | 193 + .../Games/RobotFindsKitten/.src/Frfk.form | 32 + app/examples/Games/RobotFindsKitten/COPYING | 340 + app/examples/Games/RobotFindsKitten/heart.png | Bin 0 -> 9149 bytes app/examples/Games/RobotFindsKitten/nkis.txt | 622 + .../Games/RobotFindsKitten/nkis_ru.txt | 622 + .../Games/RobotFindsKitten/readme.txt | 24 + app/examples/Games/Snake/.directory | 2 + app/examples/Games/Snake/.icon.png | Bin 0 -> 3584 bytes app/examples/Games/Snake/.lang/ca.po | 102 + app/examples/Games/Snake/.lang/cs.po | 114 + app/examples/Games/Snake/.lang/de.po | 101 + app/examples/Games/Snake/.lang/ru.po | 162 + app/examples/Games/Snake/.project | 18 + app/examples/Games/Snake/.src/FrmMain.class | 430 + app/examples/Games/Snake/.src/FrmMain.form | 89 + app/examples/Games/Snake/apple.png | Bin 0 -> 1141 bytes app/examples/Games/Snake/body.png | Bin 0 -> 1138 bytes app/examples/Games/Snake/dead.wav | Bin 0 -> 10026 bytes app/examples/Games/Snake/eat.wav | Bin 0 -> 1192 bytes app/examples/Games/Snake/head.png | Bin 0 -> 1197 bytes app/examples/Games/Snake/start.wav | Bin 0 -> 31810 bytes app/examples/Games/Solitaire/.directory | 2 + app/examples/Games/Solitaire/.icon.png | Bin 0 -> 4209 bytes app/examples/Games/Solitaire/.lang/ca.po | 96 + app/examples/Games/Solitaire/.lang/cs.po | 94 + app/examples/Games/Solitaire/.lang/de.po | 89 + app/examples/Games/Solitaire/.lang/es.po | 79 + app/examples/Games/Solitaire/.lang/ru.po | 116 + app/examples/Games/Solitaire/.project | 18 + .../Games/Solitaire/.src/CBoardDesign.class | 9 + app/examples/Games/Solitaire/.src/CMove.class | 5 + .../Games/Solitaire/.src/FBoardSelect.class | 35 + .../Games/Solitaire/.src/FBoardSelect.form | 23 + .../Games/Solitaire/.src/FGameArea.class | 368 + .../Games/Solitaire/.src/FGameArea.form | 64 + .../Games/Solitaire/.src/Global.class | 11 + .../Games/Solitaire/.src/MBoards.module | 69 + app/examples/Games/Solitaire/ball.png | Bin 0 -> 3327 bytes app/examples/Games/Solitaire/new.png | Bin 0 -> 155 bytes app/examples/Games/Solitaire/quit.png | Bin 0 -> 179 bytes app/examples/Games/Solitaire/redo.png | Bin 0 -> 131 bytes app/examples/Games/Solitaire/undo.png | Bin 0 -> 131 bytes app/examples/Games/StarField/.directory | 2 + app/examples/Games/StarField/.icon.png | Bin 0 -> 3140 bytes app/examples/Games/StarField/.project | 13 + .../Games/StarField/.src/MMain.module | 82 + app/examples/Games/StarField/enterprise.png | Bin 0 -> 4623 bytes app/examples/Games/StarField/logo.png | Bin 0 -> 12795 bytes app/examples/Image/ImageViewer/.directory | 2 + app/examples/Image/ImageViewer/.icon.png | Bin 0 -> 4419 bytes app/examples/Image/ImageViewer/.lang/ca.po | 64 + app/examples/Image/ImageViewer/.lang/cs.po | 60 + app/examples/Image/ImageViewer/.lang/de.po | 57 + app/examples/Image/ImageViewer/.lang/es.po | 43 + app/examples/Image/ImageViewer/.lang/nl.po | 55 + app/examples/Image/ImageViewer/.lang/ru.po | 82 + app/examples/Image/ImageViewer/.project | 21 + .../Image/ImageViewer/.src/FViewer.class | 106 + .../Image/ImageViewer/.src/FViewer.form | 52 + app/examples/Image/ImageViewer/image.png | Bin 0 -> 8428 bytes app/examples/Image/ImageViewer/test.png | Bin 0 -> 110 bytes app/examples/Image/Lighttable/.directory | 2 + app/examples/Image/Lighttable/.icon.png | Bin 0 -> 4434 bytes app/examples/Image/Lighttable/.lang/ca.po | 337 + app/examples/Image/Lighttable/.lang/cs.po | 330 + app/examples/Image/Lighttable/.lang/de.po | 330 + app/examples/Image/Lighttable/.lang/en.po | 264 + app/examples/Image/Lighttable/.lang/nl.po | 327 + app/examples/Image/Lighttable/.lang/ru.po | 350 + app/examples/Image/Lighttable/.project | 25 + .../Image/Lighttable/.src/FHelp.class | 28 + app/examples/Image/Lighttable/.src/FHelp.form | 26 + .../Image/Lighttable/.src/FInfo.class | 37 + app/examples/Image/Lighttable/.src/FInfo.form | 25 + .../Image/Lighttable/.src/FMain.class | 903 + app/examples/Image/Lighttable/.src/FMain.form | 162 + .../Image/Lighttable/.src/FRename.class | 58 + .../Image/Lighttable/.src/FRename.form | 25 + .../Image/Lighttable/.src/FRenameAll.class | 101 + .../Image/Lighttable/.src/FRenameAll.form | 77 + .../Lighttable/.src/FRenameAllWarning.class | 19 + .../Lighttable/.src/FRenameAllWarning.form | 29 + .../Image/Lighttable/.src/FSlideshow.class | 56 + .../Image/Lighttable/.src/FSlideshow.form | 46 + .../Image/Lighttable/.src/FStart.class | 135 + .../Image/Lighttable/.src/FStart.form | 42 + .../Image/Lighttable/.src/FTime.class | 27 + app/examples/Image/Lighttable/.src/FTime.form | 58 + .../Image/Lighttable/.src/MMain.module | 53 + app/examples/Image/Lighttable/CHANGELOG | 3 + app/examples/Image/Lighttable/FStart.class | 92 + app/examples/Image/Lighttable/FStart.form | 35 + app/examples/Image/Lighttable/Help_ca.html | 60 + app/examples/Image/Lighttable/Help_de.html | 74 + app/examples/Image/Lighttable/Help_en.html | 71 + app/examples/Image/Lighttable/Help_ru.html | 29 + app/examples/Image/Lighttable/LTicon.png | Bin 0 -> 296 bytes app/examples/Image/Lighttable/Liesmich.txt | 33 + app/examples/Image/Lighttable/Readme.txt | 33 + app/examples/Image/Lighttable/close.png | Bin 0 -> 596 bytes app/examples/Image/Lighttable/hand1.png | Bin 0 -> 176 bytes .../Image/Lighttable/help-contents.png | Bin 0 -> 742 bytes app/examples/Image/Lighttable/lighttable.png | Bin 0 -> 6536 bytes app/examples/Image/Lighttable/move.png | Bin 0 -> 169 bytes app/examples/Image/Lighttable/zoom-in.png | Bin 0 -> 710 bytes app/examples/Image/PhotoTouch/.directory | 2 + .../.hidden/screenshots/phototouch.jpg | Bin 0 -> 150309 bytes app/examples/Image/PhotoTouch/.icon.png | Bin 0 -> 3488 bytes app/examples/Image/PhotoTouch/.lang/fr.po | 184 + app/examples/Image/PhotoTouch/.lang/nl.po | 183 + app/examples/Image/PhotoTouch/.lang/ru.po | 212 + app/examples/Image/PhotoTouch/.project | 24 + .../Image/PhotoTouch/.src/CAnimation.class | 97 + .../Image/PhotoTouch/.src/CButton.class | 189 + .../Image/PhotoTouch/.src/FBrightness.class | 146 + .../Image/PhotoTouch/.src/FBrightness.form | 37 + .../Image/PhotoTouch/.src/FMain.class | 1117 + app/examples/Image/PhotoTouch/.src/FMain.form | 65 + .../Image/PhotoTouch/.src/FResize.class | 57 + .../Image/PhotoTouch/.src/FResize.form | 40 + .../Image/PhotoTouch/.src/FScissors.class | 260 + .../Image/PhotoTouch/.src/FScissors.form | 38 + app/examples/Image/PhotoTouch/balance.png | Bin 0 -> 1125 bytes app/examples/Image/PhotoTouch/blur.png | Bin 0 -> 1309 bytes app/examples/Image/PhotoTouch/brightness.png | Bin 0 -> 1534 bytes app/examples/Image/PhotoTouch/contrast.png | Bin 0 -> 1340 bytes app/examples/Image/PhotoTouch/delete.png | Bin 0 -> 853 bytes app/examples/Image/PhotoTouch/despeckle.png | Bin 0 -> 1530 bytes app/examples/Image/PhotoTouch/film.png | Bin 0 -> 756 bytes app/examples/Image/PhotoTouch/gamma.png | Bin 0 -> 693 bytes app/examples/Image/PhotoTouch/hflip.png | Bin 0 -> 953 bytes app/examples/Image/PhotoTouch/icon.png | Bin 0 -> 4904 bytes app/examples/Image/PhotoTouch/invert.png | Bin 0 -> 1942 bytes app/examples/Image/PhotoTouch/magic.png | Bin 0 -> 1093 bytes app/examples/Image/PhotoTouch/next.png | Bin 0 -> 749 bytes app/examples/Image/PhotoTouch/normalize.png | Bin 0 -> 910 bytes app/examples/Image/PhotoTouch/oil.png | Bin 0 -> 1189 bytes app/examples/Image/PhotoTouch/ok.png | Bin 0 -> 655 bytes app/examples/Image/PhotoTouch/photo.png | Bin 0 -> 1068 bytes app/examples/Image/PhotoTouch/previous.png | Bin 0 -> 724 bytes app/examples/Image/PhotoTouch/quit.png | Bin 0 -> 1034 bytes app/examples/Image/PhotoTouch/redo.png | Bin 0 -> 1042 bytes app/examples/Image/PhotoTouch/resize.png | Bin 0 -> 852 bytes app/examples/Image/PhotoTouch/rotate-left.png | Bin 0 -> 1204 bytes .../Image/PhotoTouch/rotate-right.png | Bin 0 -> 1192 bytes app/examples/Image/PhotoTouch/save-all.png | Bin 0 -> 998 bytes app/examples/Image/PhotoTouch/save.png | Bin 0 -> 674 bytes app/examples/Image/PhotoTouch/scissors.png | Bin 0 -> 1549 bytes app/examples/Image/PhotoTouch/sharpen.png | Bin 0 -> 1301 bytes app/examples/Image/PhotoTouch/undo.png | Bin 0 -> 1071 bytes app/examples/Image/PhotoTouch/usb.png | Bin 0 -> 739 bytes app/examples/Image/PhotoTouch/vflip.png | Bin 0 -> 1018 bytes app/examples/Image/PhotoTouch/zoom-fit.png | Bin 0 -> 1571 bytes app/examples/Image/PhotoTouch/zoom-in.png | Bin 0 -> 1634 bytes .../Image/PhotoTouch/zoom-original.png | Bin 0 -> 1615 bytes app/examples/Image/PhotoTouch/zoom-out.png | Bin 0 -> 1568 bytes app/examples/Misc/Console/.directory | 2 + app/examples/Misc/Console/.icon.png | Bin 0 -> 4129 bytes app/examples/Misc/Console/.lang/fr.po | 19 + app/examples/Misc/Console/.lang/ru.po | 46 + app/examples/Misc/Console/.project | 16 + app/examples/Misc/Console/.src/FConsole.class | 162 + app/examples/Misc/Console/.src/FConsole.form | 37 + app/examples/Misc/Console/terminal.png | Bin 0 -> 2312 bytes app/examples/Misc/DBusExplorer/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 131380 bytes app/examples/Misc/DBusExplorer/.icon.png | Bin 0 -> 17373 bytes app/examples/Misc/DBusExplorer/.lang/ru.po | 72 + app/examples/Misc/DBusExplorer/.project | 20 + .../DBusExplorer/.src/FVersiongbXML.class | 313 + .../Misc/DBusExplorer/.src/FVersiongbXML.form | 121 + app/examples/Misc/DBusExplorer/dbus22.png | Bin 0 -> 894 bytes app/examples/Misc/DBusExplorer/dbus64.png | Bin 0 -> 4222 bytes app/examples/Misc/DBusExplorer/method.png | Bin 0 -> 724 bytes app/examples/Misc/DBusExplorer/property.png | Bin 0 -> 728 bytes app/examples/Misc/DBusExplorer/signal.png | Bin 0 -> 780 bytes app/examples/Misc/Evaluator/.directory | 2 + app/examples/Misc/Evaluator/.icon.png | Bin 0 -> 4208 bytes app/examples/Misc/Evaluator/.lang/ca.po | 76 + app/examples/Misc/Evaluator/.lang/cs.po | 68 + app/examples/Misc/Evaluator/.lang/de.po | 69 + app/examples/Misc/Evaluator/.lang/es.po | 71 + app/examples/Misc/Evaluator/.lang/ru.po | 82 + app/examples/Misc/Evaluator/.project | 18 + app/examples/Misc/Evaluator/.src/FEval.class | 53 + app/examples/Misc/Evaluator/.src/FEval.form | 83 + app/examples/Misc/Evaluator/calculator.png | Bin 0 -> 3612 bytes app/examples/Misc/Explorer/.directory | 2 + app/examples/Misc/Explorer/.icon.png | Bin 0 -> 3277 bytes app/examples/Misc/Explorer/.lang/ca.po | 56 + app/examples/Misc/Explorer/.lang/cs.po | 52 + app/examples/Misc/Explorer/.lang/de.po | 49 + app/examples/Misc/Explorer/.lang/es.po | 35 + app/examples/Misc/Explorer/.lang/ru.po | 70 + app/examples/Misc/Explorer/.project | 20 + .../Misc/Explorer/.src/FExplorer.class | 175 + .../Misc/Explorer/.src/FExplorer.form | 38 + app/examples/Misc/Explorer/folder.png | Bin 0 -> 2450 bytes app/examples/Misc/Notepad/.directory | 2 + app/examples/Misc/Notepad/.icon.png | Bin 0 -> 4288 bytes app/examples/Misc/Notepad/.lang/ca.po | 148 + app/examples/Misc/Notepad/.lang/cs.po | 150 + app/examples/Misc/Notepad/.lang/de.po | 141 + app/examples/Misc/Notepad/.lang/es.po | 95 + app/examples/Misc/Notepad/.lang/ru.po | 164 + app/examples/Misc/Notepad/.project | 20 + app/examples/Misc/Notepad/.src/FAbout.class | 25 + app/examples/Misc/Notepad/.src/FAbout.form | 22 + app/examples/Misc/Notepad/.src/FNotepad.class | 222 + app/examples/Misc/Notepad/.src/FNotepad.form | 93 + app/examples/Misc/Notepad/notepad.png | Bin 0 -> 4015 bytes app/examples/Misc/PDFViewer/.directory | 2 + app/examples/Misc/PDFViewer/.icon.png | Bin 0 -> 4253 bytes app/examples/Misc/PDFViewer/.lang/ca.po | 80 + app/examples/Misc/PDFViewer/.lang/cs.po | 80 + app/examples/Misc/PDFViewer/.lang/de.po | 80 + app/examples/Misc/PDFViewer/.lang/es.po | 64 + app/examples/Misc/PDFViewer/.lang/ru.po | 112 + app/examples/Misc/PDFViewer/.project | 21 + app/examples/Misc/PDFViewer/.src/FMain.class | 472 + app/examples/Misc/PDFViewer/.src/FMain.form | 126 + app/examples/Misc/PDFViewer/.src/Fabout.class | 43 + app/examples/Misc/PDFViewer/.src/Fabout.form | 32 + app/examples/Misc/PDFViewer/pdf.png | Bin 0 -> 4588 bytes app/examples/Misc/SystemTray/.directory | 2 + app/examples/Misc/SystemTray/.icon.png | Bin 0 -> 17595 bytes app/examples/Misc/SystemTray/.lang/ru.po | 44 + app/examples/Misc/SystemTray/.project | 16 + app/examples/Misc/SystemTray/.src/FMain.class | 45 + app/examples/Misc/SystemTray/.src/FMain.form | 14 + app/examples/Misc/SystemTray/bg.png | Bin 0 -> 163 bytes app/examples/Misc/SystemTray/icon.png | Bin 0 -> 4363 bytes .../Misc/WatchGambasDirectory/.directory | 2 + .../Misc/WatchGambasDirectory/.icon.png | Bin 0 -> 3409 bytes .../Misc/WatchGambasDirectory/.lang/ru.po | 76 + .../Misc/WatchGambasDirectory/.project | 14 + .../WatchGambasDirectory/.src/MMain.module | 54 + .../Misc/WatchGambasDirectory/watch.svg | 1257 + app/examples/Multimedia/CDPlayer/.directory | 2 + app/examples/Multimedia/CDPlayer/.icon.png | Bin 0 -> 4311 bytes app/examples/Multimedia/CDPlayer/.lang/ca.po | 60 + app/examples/Multimedia/CDPlayer/.lang/cs.po | 52 + app/examples/Multimedia/CDPlayer/.lang/es.po | 44 + app/examples/Multimedia/CDPlayer/.lang/ru.po | 90 + app/examples/Multimedia/CDPlayer/.project | 19 + .../Multimedia/CDPlayer/.src/Fcdplayer.class | 139 + .../Multimedia/CDPlayer/.src/Fcdplayer.form | 47 + app/examples/Multimedia/CDPlayer/cdrom.png | Bin 0 -> 3987 bytes .../Multimedia/MediaPlayer/.directory | 2 + .../.hidden/screenshots/MediaPlayer.jpg | Bin 0 -> 144138 bytes app/examples/Multimedia/MediaPlayer/.icon.png | Bin 0 -> 3571 bytes .../Multimedia/MediaPlayer/.lang/fr.po | 64 + .../Multimedia/MediaPlayer/.lang/ru.po | 92 + app/examples/Multimedia/MediaPlayer/.project | 55 + .../MediaPlayer/.src/CAnimation.class | 101 + .../Multimedia/MediaPlayer/.src/CButton.class | 215 + .../MediaPlayer/.src/FControl.class | 133 + .../Multimedia/MediaPlayer/.src/FControl.form | 37 + .../Multimedia/MediaPlayer/.src/FMain.class | 616 + .../Multimedia/MediaPlayer/.src/FMain.form | 18 + .../Multimedia/MediaPlayer/.src/FTags.class | 147 + .../Multimedia/MediaPlayer/.src/FTags.form | 91 + .../Multimedia/MediaPlayer/.src/MTest.module | 67 + .../Multimedia/MediaPlayer/brightness.png | Bin 0 -> 1534 bytes .../Multimedia/MediaPlayer/config.png | Bin 0 -> 831 bytes .../Multimedia/MediaPlayer/contrast.png | Bin 0 -> 1340 bytes app/examples/Multimedia/MediaPlayer/eject.png | Bin 0 -> 1004 bytes app/examples/Multimedia/MediaPlayer/film.png | Bin 0 -> 756 bytes .../Multimedia/MediaPlayer/fullscreen.png | Bin 0 -> 598 bytes app/examples/Multimedia/MediaPlayer/gamma.png | Bin 0 -> 693 bytes app/examples/Multimedia/MediaPlayer/icon.png | Bin 0 -> 2509 bytes app/examples/Multimedia/MediaPlayer/info.png | Bin 0 -> 2413 bytes app/examples/Multimedia/MediaPlayer/mute.png | Bin 0 -> 958 bytes app/examples/Multimedia/MediaPlayer/pause.png | Bin 0 -> 393 bytes app/examples/Multimedia/MediaPlayer/play.png | Bin 0 -> 757 bytes app/examples/Multimedia/MediaPlayer/quit.png | Bin 0 -> 1034 bytes .../Multimedia/MediaPlayer/screenshot.png | Bin 0 -> 1090 bytes .../Multimedia/MediaPlayer/seek-backward.png | Bin 0 -> 1316 bytes .../Multimedia/MediaPlayer/seek-forward.png | Bin 0 -> 1283 bytes .../Multimedia/MediaPlayer/skip-backward.png | Bin 0 -> 1093 bytes .../Multimedia/MediaPlayer/skip-forward.png | Bin 0 -> 1085 bytes app/examples/Multimedia/MediaPlayer/stop.png | Bin 0 -> 495 bytes .../Multimedia/MediaPlayer/subtitle.png | Bin 0 -> 1174 bytes app/examples/Multimedia/MediaPlayer/undo.png | Bin 0 -> 1091 bytes app/examples/Multimedia/MediaPlayer/video.png | Bin 0 -> 762 bytes .../Multimedia/MediaPlayer/visualisation.png | Bin 0 -> 1093 bytes .../Multimedia/MediaPlayer/volume-0.png | Bin 0 -> 908 bytes .../Multimedia/MediaPlayer/volume-1.png | Bin 0 -> 785 bytes .../Multimedia/MediaPlayer/volume-2.png | Bin 0 -> 1052 bytes .../Multimedia/MediaPlayer/volume-3.png | Bin 0 -> 1384 bytes .../Multimedia/MediaPlayer/volume.png | Bin 0 -> 1384 bytes .../Multimedia/MediaPlayer/zoom-in.png | Bin 0 -> 1634 bytes .../Multimedia/MoviePlayer/.directory | 2 + app/examples/Multimedia/MoviePlayer/.icon.png | Bin 0 -> 4342 bytes .../Multimedia/MoviePlayer/.lang/ca.po | 32 + .../Multimedia/MoviePlayer/.lang/cs.po | 30 + .../Multimedia/MoviePlayer/.lang/es.po | 27 + .../Multimedia/MoviePlayer/.lang/ru.po | 52 + app/examples/Multimedia/MoviePlayer/.project | 19 + .../MoviePlayer/.src/FMoviePlayer.class | 136 + .../MoviePlayer/.src/FMoviePlayer.form | 42 + app/examples/Multimedia/MoviePlayer/video.png | Bin 0 -> 5244 bytes .../Multimedia/MusicPlayer/.directory | 2 + app/examples/Multimedia/MusicPlayer/.icon.png | Bin 0 -> 4226 bytes .../Multimedia/MusicPlayer/.icon/16.png | Bin 0 -> 288 bytes .../Multimedia/MusicPlayer/.icon/32.png | Bin 0 -> 272 bytes .../Multimedia/MusicPlayer/.icon/48.png | Bin 0 -> 555 bytes .../Multimedia/MusicPlayer/.lang/ca.po | 32 + .../Multimedia/MusicPlayer/.lang/cs.po | 24 + .../Multimedia/MusicPlayer/.lang/es.po | 24 + .../Multimedia/MusicPlayer/.lang/fr.po | 35 + .../Multimedia/MusicPlayer/.lang/ru.po | 38 + app/examples/Multimedia/MusicPlayer/.project | 25 + .../MusicPlayer/.src/FSoundPlayer.class | 145 + .../MusicPlayer/.src/FSoundPlayer.form | 62 + app/examples/Multimedia/MusicPlayer/sound.png | Bin 0 -> 3551 bytes app/examples/Multimedia/MyWebCam/.directory | 2 + app/examples/Multimedia/MyWebCam/.icon.png | Bin 0 -> 4360 bytes app/examples/Multimedia/MyWebCam/.lang/ca.po | 107 + app/examples/Multimedia/MyWebCam/.lang/cs.po | 112 + app/examples/Multimedia/MyWebCam/.lang/es.po | 95 + app/examples/Multimedia/MyWebCam/.lang/ru.po | 158 + app/examples/Multimedia/MyWebCam/.project | 19 + .../Multimedia/MyWebCam/.src/Form1.class | 203 + .../Multimedia/MyWebCam/.src/Form1.form | 122 + app/examples/Multimedia/MyWebCam/camera.png | Bin 0 -> 5363 bytes .../Multimedia/WaveGenerator/.directory | 2 + .../Multimedia/WaveGenerator/.icon.png | Bin 0 -> 4098 bytes .../Multimedia/WaveGenerator/.lang/ru.po | 106 + .../Multimedia/WaveGenerator/.project | 14 + .../Multimedia/WaveGenerator/.src/FMain.class | 102 + .../Multimedia/WaveGenerator/.src/FMain.form | 61 + .../WaveGenerator/audio-headphones.png | Bin 0 -> 2578 bytes app/examples/Multimedia/WebCam/.directory | 2 + app/examples/Multimedia/WebCam/.icon.png | Bin 0 -> 4272 bytes app/examples/Multimedia/WebCam/.lang/ru.po | 174 + app/examples/Multimedia/WebCam/.project | 17 + .../Multimedia/WebCam/.src/FDevice.class | 304 + .../Multimedia/WebCam/.src/FDevice.form | 149 + .../Multimedia/WebCam/.src/FMain.class | 126 + .../Multimedia/WebCam/.src/FMain.form | 62 + app/examples/Multimedia/WebCam/camera.png | Bin 0 -> 1027 bytes app/examples/Multimedia/WebCam/settings.png | Bin 0 -> 248 bytes .../Networking/ClientSocket/.directory | 2 + .../Networking/ClientSocket/.icon.png | Bin 0 -> 3551 bytes .../Networking/ClientSocket/.lang/ca.po | 127 + .../Networking/ClientSocket/.lang/cs.po | 120 + .../Networking/ClientSocket/.lang/de.po | 120 + .../Networking/ClientSocket/.lang/es.po | 63 + .../Networking/ClientSocket/.lang/nl.po | 120 + .../Networking/ClientSocket/.lang/ru.po | 148 + app/examples/Networking/ClientSocket/.project | 20 + .../ClientSocket/.src/FrmMain.class | 231 + .../Networking/ClientSocket/.src/FrmMain.form | 75 + .../Networking/ClientSocket/socket.png | Bin 0 -> 2200 bytes app/examples/Networking/DnsClient/.directory | 2 + app/examples/Networking/DnsClient/.icon.png | Bin 0 -> 18981 bytes app/examples/Networking/DnsClient/.lang/ca.po | 100 + app/examples/Networking/DnsClient/.lang/cs.po | 92 + app/examples/Networking/DnsClient/.lang/de.po | 93 + app/examples/Networking/DnsClient/.lang/es.po | 67 + app/examples/Networking/DnsClient/.lang/nl.po | 92 + app/examples/Networking/DnsClient/.lang/ru.po | 98 + app/examples/Networking/DnsClient/.project | 18 + .../Networking/DnsClient/.src/FMain.class | 308 + .../Networking/DnsClient/.src/FMain.form | 119 + .../Networking/DnsClient/dnsclient.png | Bin 0 -> 1808 bytes app/examples/Networking/HTTPGet/.directory | 2 + app/examples/Networking/HTTPGet/.icon.png | Bin 0 -> 3515 bytes app/examples/Networking/HTTPGet/.lang/ca.po | 124 + app/examples/Networking/HTTPGet/.lang/cs.po | 121 + app/examples/Networking/HTTPGet/.lang/de.po | 117 + .../Networking/HTTPGet/.lang/en_GB.po | 120 + app/examples/Networking/HTTPGet/.lang/es.po | 91 + app/examples/Networking/HTTPGet/.lang/nl.po | 120 + app/examples/Networking/HTTPGet/.lang/ru.po | 144 + app/examples/Networking/HTTPGet/.project | 22 + .../Networking/HTTPGet/.src/ClsParams.class | 8 + .../Networking/HTTPGet/.src/FConfig.class | 75 + .../Networking/HTTPGet/.src/FConfig.form | 64 + .../Networking/HTTPGet/.src/FHttpGet.class | 285 + .../Networking/HTTPGet/.src/FHttpGet.form | 65 + .../Networking/HTTPGet/httpclient.png | Bin 0 -> 1768 bytes app/examples/Networking/HTTPPost/.directory | 2 + app/examples/Networking/HTTPPost/.icon.png | Bin 0 -> 3515 bytes app/examples/Networking/HTTPPost/.lang/ca.po | 80 + app/examples/Networking/HTTPPost/.lang/cs.po | 72 + app/examples/Networking/HTTPPost/.lang/de.po | 73 + app/examples/Networking/HTTPPost/.lang/es.po | 56 + app/examples/Networking/HTTPPost/.lang/nl.po | 72 + app/examples/Networking/HTTPPost/.lang/ru.po | 104 + app/examples/Networking/HTTPPost/.project | 20 + .../Networking/HTTPPost/.src/FHttpPost.class | 105 + .../Networking/HTTPPost/.src/FHttpPost.form | 85 + .../Networking/HTTPPost/httpclient.png | Bin 0 -> 1768 bytes app/examples/Networking/POPMailbox/.directory | 2 + app/examples/Networking/POPMailbox/.icon.png | Bin 0 -> 4202 bytes .../Networking/POPMailbox/.lang/ru.po | 98 + app/examples/Networking/POPMailbox/.project | 19 + .../Networking/POPMailbox/.src/FMain.class | 106 + .../Networking/POPMailbox/.src/FMain.form | 50 + .../POPMailbox/.src/FSettings.class | 68 + .../Networking/POPMailbox/.src/FSettings.form | 66 + .../Networking/POPMailbox/pop3client.png | Bin 0 -> 957 bytes app/examples/Networking/SerialPort/.directory | 2 + app/examples/Networking/SerialPort/.icon.png | Bin 0 -> 4320 bytes .../Networking/SerialPort/.lang/ru.po | 266 + app/examples/Networking/SerialPort/.project | 17 + .../Networking/SerialPort/.src/FAbout.class | 16 + .../Networking/SerialPort/.src/FAbout.form | 32 + .../Networking/SerialPort/.src/FMain.class | 246 + .../Networking/SerialPort/.src/FMain.form | 189 + .../SerialPort/.src/Module_Config.module | 92 + .../SerialPort/.src/Module_RS232.module | 93 + .../Networking/SerialPort/serialport.png | Bin 0 -> 2498 bytes .../Networking/ServerSocket/.directory | 2 + .../Networking/ServerSocket/.icon.png | Bin 0 -> 3502 bytes .../Networking/ServerSocket/.lang/ca.po | 112 + .../Networking/ServerSocket/.lang/cs.po | 104 + .../Networking/ServerSocket/.lang/es.po | 99 + .../Networking/ServerSocket/.lang/nl.po | 104 + .../Networking/ServerSocket/.lang/ru.po | 172 + app/examples/Networking/ServerSocket/.project | 21 + .../ServerSocket/.src/FrmMain.class | 176 + .../Networking/ServerSocket/.src/FrmMain.form | 61 + .../Networking/ServerSocket/serversocket.png | Bin 0 -> 2122 bytes .../Networking/UDPServerClient/.directory | 2 + .../Networking/UDPServerClient/.icon.png | Bin 0 -> 4294 bytes .../Networking/UDPServerClient/.lang/ca.po | 104 + .../Networking/UDPServerClient/.lang/cs.po | 96 + .../Networking/UDPServerClient/.lang/es.po | 101 + .../Networking/UDPServerClient/.lang/nl.po | 96 + .../Networking/UDPServerClient/.lang/ru.po | 110 + .../Networking/UDPServerClient/.project | 17 + .../UDPServerClient/.src/FrmClient.class | 73 + .../UDPServerClient/.src/FrmClient.form | 68 + .../UDPServerClient/.src/FrmServer.class | 86 + .../UDPServerClient/.src/FrmServer.form | 38 + .../Networking/UDPServerClient/udpsocket.png | Bin 0 -> 2161 bytes app/examples/Networking/WebBrowser/.directory | 2 + app/examples/Networking/WebBrowser/.icon.png | Bin 0 -> 3585 bytes .../Networking/WebBrowser/.lang/ca.po | 212 + .../Networking/WebBrowser/.lang/cs.po | 204 + .../Networking/WebBrowser/.lang/de.po | 205 + .../Networking/WebBrowser/.lang/es.po | 62 + .../Networking/WebBrowser/.lang/nl.po | 204 + .../Networking/WebBrowser/.lang/ru.po | 258 + app/examples/Networking/WebBrowser/.project | 26 + .../Networking/WebBrowser/.src/FAuth.class | 38 + .../Networking/WebBrowser/.src/FAuth.form | 57 + .../Networking/WebBrowser/.src/FBrowser.class | 584 + .../Networking/WebBrowser/.src/FBrowser.form | 352 + .../WebBrowser/.src/FDownload.class | 50 + .../Networking/WebBrowser/.src/FDownload.form | 34 + .../WebBrowser/.src/FDownloadList.class | 20 + .../WebBrowser/.src/FDownloadList.form | 20 + .../WebBrowser/.src/FEditable.class | 15 + .../Networking/WebBrowser/.src/FEditable.form | 19 + .../Networking/WebBrowser/.src/FOption.class | 67 + .../Networking/WebBrowser/.src/FOption.form | 69 + app/examples/Networking/WebBrowser/icon.png | Bin 0 -> 2932 bytes .../Networking/WebBrowser/list-ordered.png | Bin 0 -> 518 bytes .../Networking/WebBrowser/list-unordered.png | Bin 0 -> 237 bytes app/examples/OpenGL/3DWebCam/.directory | 2 + app/examples/OpenGL/3DWebCam/.icon.png | Bin 0 -> 4343 bytes app/examples/OpenGL/3DWebCam/.lang/ru.po | 46 + app/examples/OpenGL/3DWebCam/.project | 19 + .../OpenGL/3DWebCam/.src/Mmain.module | 256 + app/examples/OpenGL/3DWebCam/webcam.png | Bin 0 -> 6442 bytes app/examples/OpenGL/GambasGears/.directory | 2 + app/examples/OpenGL/GambasGears/.icon.png | Bin 0 -> 3450 bytes app/examples/OpenGL/GambasGears/.lang/ru.po | 38 + app/examples/OpenGL/GambasGears/.project | 19 + .../OpenGL/GambasGears/.src/Module1.module | 256 + app/examples/OpenGL/GambasGears/gears.png | Bin 0 -> 3488 bytes app/examples/OpenGL/Md2Model/.directory | 2 + app/examples/OpenGL/Md2Model/.icon.png | Bin 0 -> 3521 bytes app/examples/OpenGL/Md2Model/.lang/ru.po | 50 + app/examples/OpenGL/Md2Model/.project | 22 + app/examples/OpenGL/Md2Model/.src/FMain.class | 243 + app/examples/OpenGL/Md2Model/.src/FMain.form | 31 + app/examples/OpenGL/Md2Model/Weapon.md2 | Bin 0 -> 71708 bytes app/examples/OpenGL/Md2Model/Weapon.png | Bin 0 -> 17376 bytes app/examples/OpenGL/Md2Model/bauul.jpg | Bin 0 -> 25801 bytes app/examples/OpenGL/Md2Model/bauul.md2 | Bin 0 -> 261308 bytes app/examples/OpenGL/Md2Model/goblin.jpg | Bin 0 -> 23224 bytes app/examples/OpenGL/Md2Model/goblin.md2 | Bin 0 -> 259348 bytes app/examples/OpenGL/Md2Model/icon.png | Bin 0 -> 5529 bytes app/examples/OpenGL/Md2Model/igdosh.png | Bin 0 -> 22466 bytes app/examples/OpenGL/Md2Model/knight.jpg | Bin 0 -> 16927 bytes app/examples/OpenGL/Md2Model/knight.md2 | Bin 0 -> 320236 bytes app/examples/OpenGL/Md2Model/ogro.jpg | Bin 0 -> 23954 bytes app/examples/OpenGL/Md2Model/ogro.md2 | Bin 0 -> 317276 bytes app/examples/OpenGL/Md2Model/rat.jpg | Bin 0 -> 22384 bytes app/examples/OpenGL/Md2Model/rat.md2 | Bin 0 -> 303388 bytes app/examples/OpenGL/Md2Model/rhino.jpg | Bin 0 -> 18925 bytes app/examples/OpenGL/Md2Model/rhino.md2 | Bin 0 -> 418932 bytes app/examples/OpenGL/NeHeTutorial/.directory | 2 + app/examples/OpenGL/NeHeTutorial/.icon.png | Bin 0 -> 4212 bytes app/examples/OpenGL/NeHeTutorial/.lang/ru.po | 34 + app/examples/OpenGL/NeHeTutorial/.project | 16 + .../OpenGL/NeHeTutorial/.src/Example1.module | 48 + .../OpenGL/NeHeTutorial/.src/Example10.module | 389 + .../OpenGL/NeHeTutorial/.src/Example11.module | 157 + .../OpenGL/NeHeTutorial/.src/Example16.module | 238 + .../OpenGL/NeHeTutorial/.src/Example19.module | 295 + .../OpenGL/NeHeTutorial/.src/Example2.module | 73 + .../OpenGL/NeHeTutorial/.src/Example25.module | 243 + .../OpenGL/NeHeTutorial/.src/Example3.module | 76 + .../OpenGL/NeHeTutorial/.src/Example4.module | 90 + .../OpenGL/NeHeTutorial/.src/Example42.module | 301 + .../OpenGL/NeHeTutorial/.src/Example5.module | 149 + .../OpenGL/NeHeTutorial/.src/Example6.module | 184 + .../OpenGL/NeHeTutorial/.src/Example7.module | 219 + .../OpenGL/NeHeTutorial/.src/Example8.module | 232 + .../OpenGL/NeHeTutorial/.src/Example9.module | 190 + .../OpenGL/NeHeTutorial/.src/MMain.module | 17 + app/examples/OpenGL/NeHeTutorial/NeHe.png | Bin 0 -> 14493 bytes app/examples/OpenGL/NeHeTutorial/Sphere.txt | 486 + app/examples/OpenGL/NeHeTutorial/Star.png | Bin 0 -> 4910 bytes app/examples/OpenGL/NeHeTutorial/Torus.txt | 486 + app/examples/OpenGL/NeHeTutorial/Tube.txt | 486 + app/examples/OpenGL/NeHeTutorial/barrel.png | Bin 0 -> 294 bytes app/examples/OpenGL/NeHeTutorial/ceiling.png | Bin 0 -> 140734 bytes app/examples/OpenGL/NeHeTutorial/crate.jpeg | Bin 0 -> 16088 bytes app/examples/OpenGL/NeHeTutorial/floor.png | Bin 0 -> 29009 bytes app/examples/OpenGL/NeHeTutorial/glass.png | Bin 0 -> 41118 bytes app/examples/OpenGL/NeHeTutorial/icon.png | Bin 0 -> 3377 bytes app/examples/OpenGL/NeHeTutorial/rainbow.txt | 12 + app/examples/OpenGL/NeHeTutorial/wall.jpeg | Bin 0 -> 121454 bytes app/examples/OpenGL/NeHeTutorial/world.txt | 16 + .../OpenGL/NeHeTutorialShell/.directory | 2 + .../OpenGL/NeHeTutorialShell/.icon.png | Bin 0 -> 4202 bytes .../OpenGL/NeHeTutorialShell/.lang/ru.po | 110 + .../OpenGL/NeHeTutorialShell/.project | 13 + .../OpenGL/NeHeTutorialShell/.src/FMain.class | 64 + .../OpenGL/NeHeTutorialShell/.src/FMain.form | 64 + .../OpenGL/NeHeTutorialShell/icon.png | Bin 0 -> 4015 bytes .../OpenGL/NeHeTutorialShell/nehe.png | Bin 0 -> 5785 bytes .../OpenGL/PDFPresentation/.directory | 2 + app/examples/OpenGL/PDFPresentation/.icon.png | Bin 0 -> 3540 bytes .../OpenGL/PDFPresentation/.lang/ru.po | 70 + app/examples/OpenGL/PDFPresentation/.project | 27 + .../OpenGL/PDFPresentation/.src/CLogo.class | 224 + .../.src/CPdfPresentation.class | 464 + .../OpenGL/PDFPresentation/.src/FMain.class | 111 + .../OpenGL/PDFPresentation/.src/FMain.form | 37 + .../OpenGL/PDFPresentation/.src/MMain.module | 12 + app/examples/OpenGL/PDFPresentation/icon.png | Bin 0 -> 14695 bytes app/examples/OpenGL/PDFPresentation/logo.png | Bin 0 -> 27199 bytes app/examples/OpenGL/PDFPresentation/music.xm | Bin 0 -> 482806 bytes app/examples/OpenGL/TunnelSDL/.dir_icon.png | Bin 0 -> 1571 bytes app/examples/OpenGL/TunnelSDL/.directory | 2 + app/examples/OpenGL/TunnelSDL/.icon.png | Bin 0 -> 3650 bytes app/examples/OpenGL/TunnelSDL/.lang/ru.po | 38 + app/examples/OpenGL/TunnelSDL/.project | 23 + .../OpenGL/TunnelSDL/.src/CTunnel.class | 132 + .../OpenGL/TunnelSDL/.src/MMain.module | 67 + app/examples/OpenGL/TunnelSDL/texture.png | Bin 0 -> 118636 bytes app/examples/OpenGL/TunnelSDL/tunnelsdl.png | Bin 0 -> 7012 bytes app/examples/Printing/Printing/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 76629 bytes app/examples/Printing/Printing/.icon.png | Bin 0 -> 3334 bytes app/examples/Printing/Printing/.lang/ru.po | 72 + app/examples/Printing/Printing/.project | 15 + .../Printing/Printing/.src/FMain.class | 174 + .../Printing/Printing/.src/FMain.form | 90 + .../Printing/Printing/molly-malone.txt | 35 + .../Printing/Printing/printer-laser.png | Bin 0 -> 1358 bytes .../.connection/Connection1.connection | 11 + .../.connection/Connection1.template | 65 + .../Printing/ReportExample/.directory | 2 + .../Printing/ReportExample/.hidden/report.png | Bin 0 -> 3154 bytes app/examples/Printing/ReportExample/.icon.png | Bin 0 -> 3544 bytes .../Printing/ReportExample/.lang/ru.po | 114 + app/examples/Printing/ReportExample/.project | 17 + .../Printing/ReportExample/.src/FMain.class | 41 + .../Printing/ReportExample/.src/FMain.form | 72 + .../Printing/ReportExample/.src/Report1.class | 32 + .../ReportExample/.src/Report1.report | 102 + .../Printing/ReportExample/.src/Report2.class | 19 + .../ReportExample/.src/Report2.report | 19 + .../Printing/ReportExample/.src/Report3.class | 45 + .../ReportExample/.src/Report3.report | 74 + .../Printing/ReportExample/gambas.svg | 540 + app/examples/Web/SmallWiki/.directory | 2 + app/examples/Web/SmallWiki/.icon.png | Bin 0 -> 15892 bytes app/examples/Web/SmallWiki/.lang/ru.po | 52 + app/examples/Web/SmallWiki/.project | 15 + .../Web/SmallWiki/.public/critical.png | Bin 0 -> 589 bytes app/examples/Web/SmallWiki/.public/edit.png | Bin 0 -> 116 bytes app/examples/Web/SmallWiki/.public/info.png | Bin 0 -> 777 bytes app/examples/Web/SmallWiki/.public/logo.png | Bin 0 -> 4125 bytes app/examples/Web/SmallWiki/.public/style.css | 233 + app/examples/Web/SmallWiki/.public/tip.png | Bin 0 -> 534 bytes app/examples/Web/SmallWiki/.public/up.png | Bin 0 -> 185 bytes .../Web/SmallWiki/.public/warning.png | Bin 0 -> 704 bytes app/examples/Web/SmallWiki/.src/Main.module | 282 + .../Web/SmallWiki/.src/Markdown.class | 40 + .../Web/SmallWiki/.src/MarkdownLink.class | 9 + app/examples/Web/SmallWiki/.src/Markup.module | 909 + app/examples/Web/SmallWiki/.src/Wiki.class | 47 + app/examples/Web/SmallWiki/.src/Wiki.webpage | 90 + .../Web/SmallWiki/.src/WikiMarkdown.class | 31 + app/examples/Web/SmallWiki/page | 8 + app/examples/Web/SmallWiki/passwd | 1 + app/man/Makefile.am | 1 + app/man/gambas3.1 | 53 + app/man/gbs3.1 | 151 + app/man/gbw3.1 | 111 + app/mime/application-x-gambasscript-48.png | Bin 0 -> 1416 bytes app/mime/application-x-gambasscript.png | Bin 0 -> 7529 bytes app/mime/application-x-gambasscript.xml | 14 + .../application-x-gambasserverpage-48.png | Bin 0 -> 1557 bytes app/mime/application-x-gambasserverpage.png | Bin 0 -> 8809 bytes app/mime/application-x-gambasserverpage.xml | 13 + app/reconf | 1 + app/src/INSTALL | 1 + .../.connection/Connection1.connection | 11 + .../.connection/Connection1.template | 160 + .../.connection/Connection2.connection | 10 + .../.connection/gambas3_farm.connection | 11 + .../.connection/gambas3_farm.template | 213 + app/src/gambas-farm-server/.directory | 2 + app/src/gambas-farm-server/.icon.png | Bin 0 -> 8866 bytes app/src/gambas-farm-server/.project | 16 + app/src/gambas-farm-server/.src/MMain.module | 1031 + app/src/gambas-farm-server/logo.png | Bin 0 -> 743 bytes app/src/gambas-farm-server/usage.txt | 8 + .../.connection/Connection1.connection | 9 + app/src/gambas-wiki/.directory | 2 + app/src/gambas-wiki/.hidden/CHANGELOG | 7 + .../.hidden/Uncompressed/.public/hilitor.js | 236 + .../Uncompressed/.public/style-rtl.css | 16 + .../.hidden/Uncompressed/.public/style-w.css | 20 + .../.hidden/Uncompressed/.public/style.css | 677 + app/src/gambas-wiki/.icon.png | Bin 0 -> 15462 bytes app/src/gambas-wiki/.lang/ar.po | 224 + app/src/gambas-wiki/.lang/ca.po | 224 + app/src/gambas-wiki/.lang/cs.po | 224 + app/src/gambas-wiki/.lang/de.po | 350 + app/src/gambas-wiki/.lang/es.po | 319 + app/src/gambas-wiki/.lang/es_ES.po | 319 + app/src/gambas-wiki/.lang/fa.po | 224 + app/src/gambas-wiki/.lang/fr.po | 350 + app/src/gambas-wiki/.lang/it.po | 354 + app/src/gambas-wiki/.lang/nl.po | 336 + app/src/gambas-wiki/.lang/pt_BR.po | 342 + app/src/gambas-wiki/.lang/ru.po | 354 + app/src/gambas-wiki/.lang/sv.po | 224 + app/src/gambas-wiki/.lang/zh.po | 341 + app/src/gambas-wiki/.project | 28 + app/src/gambas-wiki/.public/critical.png | 1 + app/src/gambas-wiki/.public/edit.png | Bin 0 -> 237 bytes app/src/gambas-wiki/.public/error-bg.png | Bin 0 -> 153 bytes app/src/gambas-wiki/.public/gambas.png | Bin 0 -> 16241 bytes app/src/gambas-wiki/.public/highlight.js | 1 + app/src/gambas-wiki/.public/hilitor.js | 305 + app/src/gambas-wiki/.public/home.png | Bin 0 -> 217 bytes app/src/gambas-wiki/.public/info.png | 1 + app/src/gambas-wiki/.public/logo.png | Bin 0 -> 1973 bytes app/src/gambas-wiki/.public/playground.js | 95 + app/src/gambas-wiki/.public/style-nh.css | 27 + app/src/gambas-wiki/.public/style-rtl.css | 4 + app/src/gambas-wiki/.public/style-w.css | 20 + app/src/gambas-wiki/.public/style.css | 822 + app/src/gambas-wiki/.public/tip.png | Bin 0 -> 241 bytes app/src/gambas-wiki/.public/up-gray.png | Bin 0 -> 197 bytes app/src/gambas-wiki/.public/up.png | Bin 0 -> 201 bytes app/src/gambas-wiki/.public/vb.png | Bin 0 -> 544 bytes app/src/gambas-wiki/.public/waiting.gif | Bin 0 -> 723 bytes app/src/gambas-wiki/.public/warning.png | 1 + app/src/gambas-wiki/.src/CAuthor.class | 108 + app/src/gambas-wiki/.src/CClassInfo.class | 602 + app/src/gambas-wiki/.src/CComponent.class | 1010 + app/src/gambas-wiki/.src/CPropertyInfo.class | 246 + app/src/gambas-wiki/.src/CSymbolInfo.class | 743 + app/src/gambas-wiki/.src/CUser.class | 99 + app/src/gambas-wiki/.src/Confirm.class | 24 + app/src/gambas-wiki/.src/Confirm.webpage | 43 + app/src/gambas-wiki/.src/FixNews.module | 63 + app/src/gambas-wiki/.src/HttpStat.module | 118 + app/src/gambas-wiki/.src/Main.module | 1746 + app/src/gambas-wiki/.src/OldWiki.module | 603 + app/src/gambas-wiki/.src/Register.class | 17 + app/src/gambas-wiki/.src/Register.webpage | 131 + app/src/gambas-wiki/.src/Wiki.class | 414 + app/src/gambas-wiki/.src/Wiki.webpage | 219 + app/src/gambas-wiki/.src/WikiMarkdown.class | 331 + app/src/gambas-wiki/AUTHORS | 1 + app/src/gambas-wiki/gambas3-ide.project | 1 + app/src/gambas-wiki/gambas3-scripter.project | 1 + app/src/gambas-wiki/icon.png | Bin 0 -> 1126 bytes app/src/gambas-wiki/page | 8 + app/src/gambas-wiki/passwd | 1 + app/src/gambas3-selftest/.directory | 2 + app/src/gambas3-selftest/.hidden/logo.png | Bin 0 -> 2848 bytes app/src/gambas3-selftest/.icon.png | Bin 0 -> 10556 bytes app/src/gambas3-selftest/.lang/en_GB.po | 43 + app/src/gambas3-selftest/.lang/it.po | 40 + app/src/gambas3-selftest/.project | 18 + app/src/gambas3-selftest/.settings_old | 22 + .../.src/TestSources/BaseClass.class | 37 + .../.src/TestSources/ChildClass.class | 19 + .../.src/TestSources/Class1.class | 4 + .../.src/TestSources/Class4.class | 11 + .../.src/TestSources/Class5.class | 9 + .../.src/TestSources/Crash.class | 1 + .../.src/TestSources/Crash2.class | 14 + .../.src/TestSources/Crash3.class | 22 + .../mFileReadWrite.module | 173 + .../.src/TestSources/Module1.module | 42 + .../.src/TestSources/POINT3D.class | 34 + .../.src/TestSources/POINTFloat.class | 25 + .../.src/TestSources/SubCrash.class | 23 + .../.src/TestSources/TestClass1.class | 5 + .../.src/TestSources/TestClass2.class | 132 + .../.src/TestSources/TestClass3.class | 41 + .../.src/TestSources/YetAnotherClass.class | 21 + .../.src/TestSources/mTest.module | 697 + .../.src/Tests/CrashMyTests.test | 10 + .../.src/Tests/Errorhandling.test | 27 + .../.src/Tests/FileReadWrite.test | 19 + .../.src/Tests/GambasSelftests.test | 2316 + .../.src/Tests/LocalStaticFun.test | 69 + .../.src/Tests/SlowThousandsProcs.test | 75 + .../gambas3-selftest/.src/Tests/Timers.test | 63 + app/src/gambas3-selftest/.test | 21 + app/src/gambas3-selftest/COPYING | 339 + .../Excluded or Unimplemented | 55 + app/src/gambas3-selftest/SearchList | 287 + app/src/gambas3-selftest/run.sh | 30 + app/src/gambas3/.directory | 2 + app/src/gambas3/.hidden/font/GambasBold.sfd | 38101 +++++++++++++++ .../gambas3/.hidden/font/GambasRegular.sfd | 38246 +++++++++++++++ .../.hidden/font/GambasRoundRegular.sfd | 38254 ++++++++++++++++ app/src/gambas3/.hidden/font/LICENSE | 97 + app/src/gambas3/.hidden/font/gambasb12.otb | Bin 0 -> 32992 bytes app/src/gambas3/.hidden/font/gambasb13.otb | Bin 0 -> 45620 bytes app/src/gambas3/.hidden/font/gambasb16.otb | Bin 0 -> 65380 bytes app/src/gambas3/.hidden/font/gambasr12.otb | Bin 0 -> 32996 bytes app/src/gambas3/.hidden/font/gambasr13.otb | Bin 0 -> 43232 bytes app/src/gambas3/.hidden/font/gambasr16.otb | Bin 0 -> 62380 bytes app/src/gambas3/.hidden/font/groundr16.otb | Bin 0 -> 75760 bytes app/src/gambas3/.hidden/make-help-archive | 4 + app/src/gambas3/.hidden/report-ng.sh | 132 + app/src/gambas3/.icon.png | Bin 0 -> 12239 bytes app/src/gambas3/.lang/ar.po | 8685 ++++ app/src/gambas3/.lang/ca.po | 8713 ++++ app/src/gambas3/.lang/cs.po | 8386 ++++ app/src/gambas3/.lang/cy.po | 8534 ++++ app/src/gambas3/.lang/de.po | 8994 ++++ app/src/gambas3/.lang/el.po | 9011 ++++ app/src/gambas3/.lang/es.po | 7958 ++++ app/src/gambas3/.lang/es_ES.po | 7958 ++++ app/src/gambas3/.lang/fa.po | 8645 ++++ app/src/gambas3/.lang/fr.po | 8956 ++++ app/src/gambas3/.lang/gl_ES.po | 8660 ++++ app/src/gambas3/.lang/hr.po | 8578 ++++ app/src/gambas3/.lang/hu.po | 8132 ++++ app/src/gambas3/.lang/id.po | 8034 ++++ app/src/gambas3/.lang/it.po | 8375 ++++ app/src/gambas3/.lang/ja.po | 8611 ++++ app/src/gambas3/.lang/ko.po | 8229 ++++ app/src/gambas3/.lang/lt.po | 8026 ++++ app/src/gambas3/.lang/nl.po | 8018 ++++ app/src/gambas3/.lang/no.po | 8040 ++++ app/src/gambas3/.lang/pl.po | 7798 ++++ app/src/gambas3/.lang/pt.po | 8304 ++++ app/src/gambas3/.lang/pt_BR.po | 8159 ++++ app/src/gambas3/.lang/ro.po | 8032 ++++ app/src/gambas3/.lang/ru.po | 7934 ++++ app/src/gambas3/.lang/sl.po | 8139 ++++ app/src/gambas3/.lang/sv.po | 8670 ++++ app/src/gambas3/.lang/tr.po | 8472 ++++ app/src/gambas3/.lang/zh.po | 8466 ++++ app/src/gambas3/.lang/zh_TW.po | 8964 ++++ app/src/gambas3/.project | 60 + app/src/gambas3/.src/CRecentProject.class | 259 + app/src/gambas3/.src/CStyle.class | 76 + app/src/gambas3/.src/CWaitingAnimation.class | 113 + .../gambas3/.src/Component/CClassInfo.class | 1035 + .../gambas3/.src/Component/CComponent.class | 706 + .../.src/Component/CDocumentation.class | 1247 + app/src/gambas3/.src/Component/CModule.class | 107 + .../.src/Component/CPropertyInfo.class | 397 + .../gambas3/.src/Component/CSymbolInfo.class | 1549 + .../gambas3/.src/Connection/FExportData.class | 146 + .../gambas3/.src/Connection/FExportData.form | 47 + .../.src/Connection/FImportTable.class | 512 + .../gambas3/.src/Connection/FImportTable.form | 154 + .../.src/Connection/FNewConnection.class | 434 + .../.src/Connection/FNewConnection.form | 220 + .../gambas3/.src/Connection/FPasteTable.class | 221 + .../gambas3/.src/Connection/FPasteTable.form | 77 + .../.src/Connection/MConnection.module | 553 + app/src/gambas3/.src/Debug/Breakpoints.module | 190 + app/src/gambas3/.src/Debug/CProfile.class | 165 + app/src/gambas3/.src/Debug/CProfileTask.class | 427 + app/src/gambas3/.src/Debug/Design.module | 1985 + app/src/gambas3/.src/Debug/FBrowser.class | 30 + app/src/gambas3/.src/Debug/FBrowser.form | 23 + app/src/gambas3/.src/Debug/FCrash.class | 150 + app/src/gambas3/.src/Debug/FCrash.form | 78 + app/src/gambas3/.src/Debug/FDebugButton.class | 227 + app/src/gambas3/.src/Debug/FDebugButton.form | 81 + app/src/gambas3/.src/Debug/FDebugConfig.class | 233 + app/src/gambas3/.src/Debug/FDebugConfig.form | 430 + app/src/gambas3/.src/Debug/FDebugExpr.class | 901 + app/src/gambas3/.src/Debug/FDebugExpr.form | 72 + app/src/gambas3/.src/Debug/FDebugInfo.class | 1042 + app/src/gambas3/.src/Debug/FDebugInfo.form | 382 + app/src/gambas3/.src/Debug/FDebugger.class | 1164 + app/src/gambas3/.src/Debug/FDebugger.form | 271 + app/src/gambas3/.src/Debug/FOutput.class | 574 + app/src/gambas3/.src/Debug/FOutput.form | 132 + app/src/gambas3/.src/Debug/FProfile.class | 794 + app/src/gambas3/.src/Debug/FProfile.form | 151 + app/src/gambas3/.src/Debug/FTestSuite.class | 330 + app/src/gambas3/.src/Debug/FTestSuite.form | 114 + .../.src/Dialog/Database/FFieldChooser.class | 159 + .../.src/Dialog/Database/FFieldChooser.form | 104 + .../.src/Dialog/Database/FTableChooser.class | 75 + .../.src/Dialog/Database/FTableChooser.form | 52 + .../gambas3/.src/Dialog/FColorChooser.class | 371 + .../gambas3/.src/Dialog/FColorChooser.form | 59 + .../gambas3/.src/Dialog/FFileProperty.class | 96 + .../gambas3/.src/Dialog/FFileProperty.form | 35 + .../gambas3/.src/Dialog/FFontChooser.class | 47 + app/src/gambas3/.src/Dialog/FFontChooser.form | 32 + app/src/gambas3/.src/Dialog/FList.class | 227 + app/src/gambas3/.src/Dialog/FList.form | 94 + app/src/gambas3/.src/Dialog/FSelectIcon.class | 243 + app/src/gambas3/.src/Dialog/FSelectIcon.form | 86 + .../.src/Editor/Browse/FFileOverwrite.class | 50 + .../.src/Editor/Browse/FFileOverwrite.form | 72 + .../.src/Editor/Browse/FProjectBrowser.class | 799 + .../.src/Editor/Browse/FProjectBrowser.form | 433 + app/src/gambas3/.src/Editor/CBookmark.class | 181 + .../gambas3/.src/Editor/CInsertColor.class | 309 + app/src/gambas3/.src/Editor/CInsertDate.class | 174 + app/src/gambas3/.src/Editor/CPosition.class | 238 + app/src/gambas3/.src/Editor/CTask.class | 89 + app/src/gambas3/.src/Editor/CUndo.class | 188 + .../.src/Editor/Code/CCompletion.class | 282 + .../gambas3/.src/Editor/Code/CDatatype.class | 98 + .../.src/Editor/Code/CSampleCode.class | 508 + .../.src/Editor/Code/FCompletion.class | 1232 + .../gambas3/.src/Editor/Code/FCompletion.form | 51 + .../.src/Editor/Code/FConflictEditor.class | 1564 + .../.src/Editor/Code/FConflictEditor.form | 637 + .../gambas3/.src/Editor/Code/FEditor.class | 4059 ++ app/src/gambas3/.src/Editor/Code/FEditor.form | 757 + .../.src/Editor/Code/FPasteSpecial.class | 226 + .../.src/Editor/Code/FPasteSpecial.form | 68 + .../.src/Editor/Code/FProcedureList.class | 390 + .../.src/Editor/Code/FProcedureList.form | 87 + .../gambas3/.src/Editor/Code/FSignature.class | 283 + .../gambas3/.src/Editor/Code/FSignature.form | 56 + .../.src/Editor/Code/FTextEditor.class | 2030 + .../gambas3/.src/Editor/Code/FTextEditor.form | 877 + .../.src/Editor/Code/MPrettyCode.module | 574 + .../.src/Editor/Connection/CField.class | 31 + .../.src/Editor/Connection/CIndexField.class | 18 + .../Editor/Connection/FConnectionEditor.class | 2018 + .../Editor/Connection/FConnectionEditor.form | 517 + .../.src/Editor/Connection/FNewTable.class | 75 + .../.src/Editor/Connection/FNewTable.form | 64 + .../gambas3/.src/Editor/Csv/FCsvEditor.class | 2 + .../gambas3/.src/Editor/Csv/FCsvEditor.form | 54 + .../gambas3/.src/Editor/Csv/FCsvOption.class | 20 + .../gambas3/.src/Editor/Csv/FCsvOption.form | 124 + app/src/gambas3/.src/Editor/FInsertChar.class | 113 + app/src/gambas3/.src/Editor/FInsertChar.form | 16 + .../gambas3/.src/Editor/Form/CControl.class | 2620 ++ app/src/gambas3/.src/Editor/Form/CMenu.class | 79 + .../.src/Editor/Form/FDeleteUnknown.class | 39 + .../.src/Editor/Form/FDeleteUnknown.form | 47 + app/src/gambas3/.src/Editor/Form/FForm.class | 4733 ++ app/src/gambas3/.src/Editor/Form/FForm.form | 1046 + .../gambas3/.src/Editor/Form/FFormStack.class | 321 + .../gambas3/.src/Editor/Form/FFormStack.form | 43 + app/src/gambas3/.src/Editor/Form/FMenu.class | 1353 + app/src/gambas3/.src/Editor/Form/FMenu.form | 412 + .../gambas3/.src/Editor/Form/FProperty.class | 1377 + .../gambas3/.src/Editor/Form/FProperty.form | 119 + app/src/gambas3/.src/Editor/Form/FText.class | 60 + app/src/gambas3/.src/Editor/Form/FText.form | 32 + .../gambas3/.src/Editor/Form/FToolBox.class | 435 + .../gambas3/.src/Editor/Form/FToolBox.form | 96 + .../gambas3/.src/Editor/Form/FToolPanel.class | 221 + .../gambas3/.src/Editor/Form/FToolPanel.form | 14 + .../.src/Editor/Image/CImageClipboard.class | 13 + .../.src/Editor/Image/CImageSelection.class | 1227 + .../.src/Editor/Image/CImageShape.class | 331 + .../.src/Editor/Image/FImageEditor.class | 3449 ++ .../.src/Editor/Image/FImageEditor.form | 878 + .../Editor/Image/FImageOffsetSelection.class | 44 + .../Editor/Image/FImageOffsetSelection.form | 52 + .../.src/Editor/Image/FImageProperty.class | 1587 + .../.src/Editor/Image/FImageProperty.form | 667 + .../.src/Editor/Image/FImageQuality.class | 20 + .../.src/Editor/Image/FImageQuality.form | 25 + .../.src/Editor/Image/FImageResize.class | 147 + .../.src/Editor/Image/FImageResize.form | 108 + .../.src/Editor/Image/FImageRotate.class | 39 + .../.src/Editor/Image/FImageRotate.form | 25 + .../.src/Editor/Image/ImageSelectionBox.class | 102 + .../gambas3/.src/Editor/MCompressFile.module | 603 + app/src/gambas3/.src/Editor/MPrint.module | 141 + .../.src/Exported/EnvChooser/EnvChooser.class | 398 + .../ProjectChooser/FProjectChooser.class | 219 + .../ProjectChooser/FProjectChooser.form | 65 + .../ProjectChooser/ProjectChooser.class | 132 + app/src/gambas3/.src/Exported/TabPanel.class | 98 + .../gambas3/.src/Exported/TextEditor.class | 626 + app/src/gambas3/.src/FCommandError.class | 53 + app/src/gambas3/.src/FCommandError.form | 43 + app/src/gambas3/.src/FMain.class | 3545 ++ app/src/gambas3/.src/FMain.form | 2091 + app/src/gambas3/.src/FSave.class | 83 + app/src/gambas3/.src/FSave.form | 53 + app/src/gambas3/.src/FScreenshot.class | 50 + app/src/gambas3/.src/FScreenshot.form | 41 + app/src/gambas3/.src/Family/CFamily.class | 719 + .../.src/Family/Form/CFamilyForm.class | 194 + .../gambas3/.src/Family/Report/AngleBox.class | 90 + .../.src/Family/Report/CFamilyReport.class | 574 + .../.src/Family/Report/CReportBrush.class | 264 + .../gambas3/.src/Family/Report/CoordBox.class | 422 + .../Family/Report/FReportBorderChooser.class | 503 + .../Family/Report/FReportBorderChooser.form | 169 + .../Report/FReportBoxShadowChooser.class | 136 + .../Report/FReportBoxShadowChooser.form | 79 + .../Family/Report/FReportBrushChooser.class | 561 + .../Family/Report/FReportBrushChooser.form | 118 + .../Family/Report/FReportCoordChooser.class | 95 + .../Family/Report/FReportCoordChooser.form | 33 + .../Family/Report/FReportPaddingChooser.class | 97 + .../Family/Report/FReportPaddingChooser.form | 58 + .../Family/TermForm/CFamilyTermForm.class | 99 + .../.src/Family/WebForm/CFamilyWebForm.class | 278 + .../.src/Family/WebForm/FWebFontChooser.class | 126 + .../.src/Family/WebForm/FWebFontChooser.form | 103 + .../.src/Family/WebForm/WebformWebMenu.class | 51 + .../gambas3/.src/Help/CHelpSearchTask.class | 115 + app/src/gambas3/.src/Help/FHelpBrowser.class | 459 + app/src/gambas3/.src/Help/FHelpBrowser.form | 161 + app/src/gambas3/.src/Help/FHelpShortcut.class | 50 + app/src/gambas3/.src/Help/FHelpShortcut.form | 38 + app/src/gambas3/.src/Help/FTips.class | 176 + app/src/gambas3/.src/Help/FTips.form | 79 + app/src/gambas3/.src/Help/HelpBrowser.class | 295 + app/src/gambas3/.src/Help/HelpView.class | 209 + app/src/gambas3/.src/Help/MHelp.module | 1075 + app/src/gambas3/.src/Help/Wiki/URL.class | 105 + app/src/gambas3/.src/Help/Wiki/Wiki.module | 1043 + .../gambas3/.src/Help/Wiki/WikiMarkdown.class | 319 + app/src/gambas3/.src/MMime.module | 123 + app/src/gambas3/.src/MTest.module | 225 + app/src/gambas3/.src/MTheme.module | 233 + .../gambas3/.src/Options/CBackground.class | 68 + app/src/gambas3/.src/Options/FLayout.class | 58 + app/src/gambas3/.src/Options/FLayout.form | 38 + app/src/gambas3/.src/Options/FOption.class | 1998 + app/src/gambas3/.src/Options/FOption.form | 1279 + app/src/gambas3/.src/Options/FProxy.class | 55 + app/src/gambas3/.src/Options/FProxy.form | 67 + app/src/gambas3/.src/Options/FSnippet.class | 79 + app/src/gambas3/.src/Options/FSnippet.form | 43 + .../gambas3/.src/Packager/FMakeInstall.class | 1395 + .../gambas3/.src/Packager/FMakeInstall.form | 721 + .../.src/Packager/FSelectExtraFile.class | 39 + .../.src/Packager/FSelectExtraFile.form | 35 + app/src/gambas3/.src/Packager/Package.module | 2815 ++ app/src/gambas3/.src/Project.module | 7035 +++ .../gambas3/.src/Project/CProjectInfo.class | 281 + .../gambas3/.src/Project/CProjectList.class | 683 + .../gambas3/.src/Project/CProjectTree.class | 1254 + .../Project/Component/ComponentChooser.class | 167 + .../Project/Component/FComponentChooser.class | 823 + .../Project/Component/FComponentChooser.form | 107 + .../Project/Component/FSelectComponent.class | 24 + .../Project/Component/FSelectComponent.form | 30 + .../.src/Project/Conversion/FConvert.class | 56 + .../.src/Project/Conversion/FConvert.form | 22 + .../.src/Project/Conversion/MConvert.module | 819 + .../gambas3/.src/Project/FCreateFile.class | 379 + app/src/gambas3/.src/Project/FCreateFile.form | 157 + .../gambas3/.src/Project/FCreateProject.class | 260 + .../gambas3/.src/Project/FCreateProject.form | 144 + .../gambas3/.src/Project/FImportFile.class | 204 + app/src/gambas3/.src/Project/FImportFile.form | 20 + .../.src/Project/FMakeExecutable.class | 123 + .../gambas3/.src/Project/FMakeExecutable.form | 130 + .../.src/Project/FMakeSourceArchive.class | 44 + .../.src/Project/FMakeSourceArchive.form | 19 + .../gambas3/.src/Project/FOpenProject.class | 234 + .../gambas3/.src/Project/FOpenProject.form | 150 + .../.src/Project/FProjectProperty.class | 1232 + .../.src/Project/FProjectProperty.form | 771 + .../gambas3/.src/Project/FSaveProjectAs.class | 103 + .../gambas3/.src/Project/FSaveProjectAs.form | 65 + .../gambas3/.src/Project/Farm/CSoftware.class | 704 + .../.src/Project/Farm/CSoftwareGroup.class | 27 + .../.src/Project/Farm/FFarmConfig.class | 32 + .../.src/Project/Farm/FFarmConfig.form | 30 + .../.src/Project/Farm/FFarmLogin.class | 114 + .../gambas3/.src/Project/Farm/FFarmLogin.form | 90 + .../.src/Project/Farm/FFarmRegister.class | 64 + .../.src/Project/Farm/FFarmRegister.form | 100 + .../.src/Project/Farm/FFarmRequest.class | 64 + .../.src/Project/Farm/FFarmRequest.form | 32 + .../gambas3/.src/Project/Farm/FPublish.class | 355 + .../gambas3/.src/Project/Farm/FPublish.form | 235 + .../.src/Project/Farm/FSoftwareFarm.class | 777 + .../.src/Project/Farm/FSoftwareFarm.form | 320 + .../.src/Project/Farm/FarmIdentity.class | 144 + .../.src/Project/Farm/FarmRequest.class | 396 + .../Project/Farm/FarmRequestManager.module | 97 + .../.src/Project/Farm/Publish/CTag.class | 78 + .../Project/Farm/Publish/CTagCompletion.class | 82 + .../Project/Farm/Publish/CTagEditor.class | 223 + .../.src/Project/Farm/SoftwareBox.class | 326 + .../.src/Project/Library/CLibraryInfo.class | 228 + .../.src/Project/Library/FSelectLibrary.class | 146 + .../.src/Project/Library/FSelectLibrary.form | 52 + .../.src/Project/Library/LibraryItem.class | 120 + .../.src/Project/Patch/FMakePatch.class | 155 + .../.src/Project/Patch/FMakePatch.form | 97 + .../gambas3/.src/Project/Patch/FPatch.class | 221 + .../gambas3/.src/Project/Patch/FPatch.form | 68 + .../gambas3/.src/Project/Patch/Patch.class | 212 + app/src/gambas3/.src/Project/ProjectBox.class | 548 + app/src/gambas3/.src/Save.module | 62 + app/src/gambas3/.src/Search/CGrepResult.class | 6 + .../gambas3/.src/Search/CSearchResult.class | 34 + app/src/gambas3/.src/Search/CSearchTask.class | 205 + app/src/gambas3/.src/Search/FSearch.class | 1537 + app/src/gambas3/.src/Search/FSearch.form | 198 + .../.src/Translation/CTranslation.class | 302 + .../.src/Translation/FNewTranslation.class | 55 + .../.src/Translation/FNewTranslation.form | 32 + .../gambas3/.src/Translation/FTranslate.class | 1871 + .../gambas3/.src/Translation/FTranslate.form | 366 + .../.src/Translation/MyLanguage.module | 272 + app/src/gambas3/.src/Util.module | 87 + .../gambas3/.src/Util/MErrorMessage.module | 393 + .../gambas3/.src/Util/MRemoveAccents.module | 69 + .../gambas3/.src/Util/MSdlDefaultFont.module | 85 + .../.src/VersionControl/CVersionControl.class | 137 + .../VersionControl/CVersionControlGit.class | 576 + .../CVersionControlHistoryTask.class | 22 + .../CVersionControlSubversion.class | 422 + .../.src/VersionControl/FConflict.class | 288 + .../.src/VersionControl/FConflict.form | 121 + .../.src/VersionControl/FCreateBranch.class | 35 + .../.src/VersionControl/FCreateBranch.form | 41 + .../.src/VersionControl/FFileInfoVC.class | 196 + .../.src/VersionControl/FFileInfoVC.form | 82 + .../.src/VersionControl/FProjectVersion.class | 307 + .../.src/VersionControl/FProjectVersion.form | 231 + .../.src/VersionControl/FVersionControl.class | 300 + .../.src/VersionControl/FVersionControl.form | 160 + .../.src/VersionControl/VersionControl.module | 922 + .../gambas3/.src/Welcome/CCoolButton.class | 232 + app/src/gambas3/.src/Welcome/CSnowFlake.class | 9 + app/src/gambas3/.src/Welcome/CTear.class | 4 + app/src/gambas3/.src/Welcome/CUser.class | 11 + app/src/gambas3/.src/Welcome/CWelcome.class | 433 + app/src/gambas3/.src/Welcome/FAbout.class | 431 + app/src/gambas3/.src/Welcome/FAbout.form | 37 + .../gambas3/.src/Welcome/FSystemInfo.class | 292 + app/src/gambas3/.src/Welcome/FSystemInfo.form | 38 + app/src/gambas3/.src/Welcome/FWelcome.class | 473 + app/src/gambas3/.src/Welcome/FWelcome.form | 111 + app/src/gambas3/AUTHORS | 1 + app/src/gambas3/_fake_project | 4 + app/src/gambas3/debugger/Main.module | 84 + app/src/gambas3/debugger/_project | 7 + app/src/gambas3/font/font.allow | 8 + app/src/gambas3/font/font.conf | 4 + app/src/gambas3/font/gambasb12.otb | 1 + app/src/gambas3/font/gambasb13.otb | 1 + app/src/gambas3/font/gambasb16.otb | 1 + app/src/gambas3/font/gambasr12.otb | 1 + app/src/gambas3/font/gambasr13.otb | 1 + app/src/gambas3/font/gambasr16.otb | 1 + app/src/gambas3/font/groundr16.otb | 1 + app/src/gambas3/gitignore | 18 + app/src/gambas3/help/class-help.html | 35 + app/src/gambas3/help/component-help.html | 23 + app/src/gambas3/help/property-help.html | 21 + app/src/gambas3/help/symbol-help.html | 17 + app/src/gambas3/help/wiki/critical.png | 1 + app/src/gambas3/help/wiki/error-bg.png | Bin 0 -> 153 bytes app/src/gambas3/help/wiki/highlight.js | 60 + app/src/gambas3/help/wiki/info.png | 1 + app/src/gambas3/help/wiki/page.html | 39 + app/src/gambas3/help/wiki/style-custom.css | 12 + .../help/wiki/style-offline-htmlview.css | 36 + app/src/gambas3/help/wiki/style-offline.css | 22 + app/src/gambas3/help/wiki/style.css | 1 + app/src/gambas3/help/wiki/tip.png | 1 + app/src/gambas3/help/wiki/up.png | Bin 0 -> 185 bytes app/src/gambas3/help/wiki/vb.png | 1 + app/src/gambas3/help/wiki/warning.png | 1 + app/src/gambas3/img/16/arrange-h.png | Bin 0 -> 161 bytes app/src/gambas3/img/16/arrange-hcenter.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/arrange-lr.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/arrange-tb.png | Bin 0 -> 162 bytes app/src/gambas3/img/16/arrange-v.png | Bin 0 -> 158 bytes app/src/gambas3/img/16/arrange-vcenter.png | Bin 0 -> 166 bytes app/src/gambas3/img/16/checked-gray.png | Bin 0 -> 210 bytes app/src/gambas3/img/16/checked-lock.png | Bin 0 -> 157 bytes app/src/gambas3/img/16/checked.png | Bin 0 -> 190 bytes app/src/gambas3/img/16/close-window.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/collapse-container.png | Bin 0 -> 165 bytes app/src/gambas3/img/16/configure.png | 1 + app/src/gambas3/img/16/control.png | Bin 0 -> 411 bytes app/src/gambas3/img/16/cursive.png | Bin 0 -> 551 bytes app/src/gambas3/img/16/expand-container.png | Bin 0 -> 165 bytes app/src/gambas3/img/16/fantasy.png | Bin 0 -> 478 bytes app/src/gambas3/img/16/max-window.png | Bin 0 -> 169 bytes app/src/gambas3/img/16/monospace.png | Bin 0 -> 372 bytes app/src/gambas3/img/16/red-arrow-c.png | Bin 0 -> 155 bytes app/src/gambas3/img/16/red-arrow-h.png | Bin 0 -> 148 bytes app/src/gambas3/img/16/red-arrow-r.png | Bin 0 -> 151 bytes app/src/gambas3/img/16/red-arrow-v.png | Bin 0 -> 152 bytes .../gambas3/img/16/remove-from-container.png | Bin 0 -> 159 bytes app/src/gambas3/img/16/rename.png | Bin 0 -> 219 bytes app/src/gambas3/img/16/round-ne.png | Bin 0 -> 219 bytes app/src/gambas3/img/16/round-nw.png | Bin 0 -> 221 bytes app/src/gambas3/img/16/round-se.png | Bin 0 -> 219 bytes app/src/gambas3/img/16/round-sw.png | Bin 0 -> 216 bytes app/src/gambas3/img/16/sans-serif.png | Bin 0 -> 487 bytes app/src/gambas3/img/16/select.png | Bin 0 -> 171 bytes app/src/gambas3/img/16/separator.png | Bin 0 -> 155 bytes app/src/gambas3/img/16/serif.png | Bin 0 -> 493 bytes app/src/gambas3/img/16/stack.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/tabmove.png | Bin 0 -> 275 bytes app/src/gambas3/img/16/tabstrip.png | Bin 0 -> 248 bytes app/src/gambas3/img/16/tile.png | Bin 0 -> 120 bytes app/src/gambas3/img/16/toggle-container.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/unchecked.png | Bin 0 -> 156 bytes app/src/gambas3/img/16/white-close.png | Bin 0 -> 157 bytes app/src/gambas3/img/32/breakpoint.png | Bin 0 -> 313 bytes app/src/gambas3/img/32/comment.png | Bin 0 -> 248 bytes app/src/gambas3/img/32/cross.png | Bin 0 -> 171 bytes app/src/gambas3/img/32/dbus.png | Bin 0 -> 1505 bytes app/src/gambas3/img/32/delete-container.png | Bin 0 -> 151 bytes app/src/gambas3/img/32/do-not-translate.png | Bin 0 -> 256 bytes app/src/gambas3/img/32/embed-container.png | Bin 0 -> 157 bytes app/src/gambas3/img/32/eol.png | Bin 0 -> 194 bytes app/src/gambas3/img/32/error.png | Bin 0 -> 356 bytes app/src/gambas3/img/32/exported-namespace.png | Bin 0 -> 602 bytes app/src/gambas3/img/32/exported.png | Bin 0 -> 603 bytes app/src/gambas3/img/32/filter-menu.png | Bin 0 -> 530 bytes app/src/gambas3/img/32/filter.png | Bin 0 -> 521 bytes app/src/gambas3/img/32/linked.png | Bin 0 -> 214 bytes app/src/gambas3/img/32/magic.png | Bin 0 -> 241 bytes app/src/gambas3/img/32/multicontainer.png | Bin 0 -> 156 bytes app/src/gambas3/img/32/opengl.png | Bin 0 -> 1329 bytes app/src/gambas3/img/32/tab.png | Bin 0 -> 206 bytes app/src/gambas3/img/32/tile.png | Bin 0 -> 116 bytes app/src/gambas3/img/32/uncomment.png | Bin 0 -> 388 bytes app/src/gambas3/img/32/wrap.png | Bin 0 -> 204 bytes app/src/gambas3/img/48/all.png | Bin 0 -> 1521 bytes app/src/gambas3/img/background/baraka.png | Bin 0 -> 10448 bytes app/src/gambas3/img/background/christ.png | Bin 0 -> 186 bytes app/src/gambas3/img/background/circle.png | Bin 0 -> 193 bytes app/src/gambas3/img/background/cross.png | Bin 0 -> 146 bytes app/src/gambas3/img/background/cubism.png | Bin 0 -> 63853 bytes app/src/gambas3/img/background/dark.png | Bin 0 -> 113 bytes app/src/gambas3/img/background/gambas.png | Bin 0 -> 974 bytes app/src/gambas3/img/background/hline.png | Bin 0 -> 138 bytes app/src/gambas3/img/background/hose.png | Bin 0 -> 44520 bytes app/src/gambas3/img/background/illusion.png | Bin 0 -> 2946 bytes app/src/gambas3/img/background/labyrinth.png | Bin 0 -> 973 bytes app/src/gambas3/img/background/light.png | Bin 0 -> 113 bytes app/src/gambas3/img/background/list | 23 + app/src/gambas3/img/background/medracim.png | Bin 0 -> 172457 bytes app/src/gambas3/img/background/mosaic.png | Bin 0 -> 76015 bytes app/src/gambas3/img/background/nature.png | Bin 0 -> 46970 bytes app/src/gambas3/img/background/oil.png | Bin 0 -> 74848 bytes app/src/gambas3/img/background/point.png | Bin 0 -> 135 bytes app/src/gambas3/img/background/quasi.png | Bin 0 -> 34460 bytes app/src/gambas3/img/background/smoke.png | Bin 0 -> 25578 bytes app/src/gambas3/img/background/square.png | Bin 0 -> 480 bytes app/src/gambas3/img/background/star.png | Bin 0 -> 34262 bytes app/src/gambas3/img/background/tawhid.png | Bin 0 -> 22454 bytes app/src/gambas3/img/background/vline.png | Bin 0 -> 133 bytes app/src/gambas3/img/background/warda.png | Bin 0 -> 24560 bytes app/src/gambas3/img/background/weave.png | Bin 0 -> 60153 bytes app/src/gambas3/img/broken.svg | 122 + app/src/gambas3/img/component/deprecated.png | Bin 0 -> 285 bytes .../gambas3/img/component/experimental.png | Bin 0 -> 245 bytes app/src/gambas3/img/component/finished.png | Bin 0 -> 239 bytes app/src/gambas3/img/component/unfinished.png | Bin 0 -> 290 bytes app/src/gambas3/img/component/unknown.png | Bin 0 -> 350 bytes app/src/gambas3/img/contrib/Radoslav.gif | Bin 0 -> 2405 bytes app/src/gambas3/img/control/button.png | Bin 0 -> 804 bytes app/src/gambas3/img/control/checkbox.png | Bin 0 -> 229 bytes app/src/gambas3/img/control/columnview.png | Bin 0 -> 170 bytes app/src/gambas3/img/control/combobox.png | Bin 0 -> 306 bytes app/src/gambas3/img/control/deprecated.png | Bin 0 -> 803 bytes app/src/gambas3/img/control/dial.png | Bin 0 -> 1008 bytes app/src/gambas3/img/control/dnsclient.png | Bin 0 -> 2333 bytes app/src/gambas3/img/control/drawingarea.png | Bin 0 -> 460 bytes app/src/gambas3/img/control/embedder.png | Bin 0 -> 1467 bytes app/src/gambas3/img/control/frame.png | Bin 0 -> 1016 bytes app/src/gambas3/img/control/ftpclient.png | Bin 0 -> 2193 bytes app/src/gambas3/img/control/glarea.png | Bin 0 -> 1781 bytes app/src/gambas3/img/control/gridview.png | Bin 0 -> 137 bytes app/src/gambas3/img/control/hbox.png | Bin 0 -> 253 bytes app/src/gambas3/img/control/hpanel.png | Bin 0 -> 256 bytes app/src/gambas3/img/control/hsplit.png | Bin 0 -> 279 bytes app/src/gambas3/img/control/httpclient.png | Bin 0 -> 2229 bytes app/src/gambas3/img/control/iconview.png | Bin 0 -> 151 bytes app/src/gambas3/img/control/label.png | Bin 0 -> 1285 bytes app/src/gambas3/img/control/lcdnumber.png | Bin 0 -> 212 bytes app/src/gambas3/img/control/listbox.png | Bin 0 -> 127 bytes app/src/gambas3/img/control/listview.png | Bin 0 -> 145 bytes app/src/gambas3/img/control/moviebox.png | Bin 0 -> 432 bytes app/src/gambas3/img/control/panel.png | Bin 0 -> 159 bytes app/src/gambas3/img/control/picturebox.png | Bin 0 -> 384 bytes app/src/gambas3/img/control/printer.png | Bin 0 -> 281 bytes app/src/gambas3/img/control/progressbar.png | Bin 0 -> 219 bytes app/src/gambas3/img/control/radiobutton.png | Bin 0 -> 774 bytes app/src/gambas3/img/control/scrollarea.png | Bin 0 -> 490 bytes app/src/gambas3/img/control/scrollbar.png | Bin 0 -> 315 bytes app/src/gambas3/img/control/scrollview.png | Bin 0 -> 202 bytes app/src/gambas3/img/control/select.png | Bin 0 -> 1203 bytes app/src/gambas3/img/control/separator.png | Bin 0 -> 129 bytes app/src/gambas3/img/control/serialport.png | Bin 0 -> 847 bytes app/src/gambas3/img/control/serversocket.png | Bin 0 -> 998 bytes app/src/gambas3/img/control/slider.png | Bin 0 -> 471 bytes app/src/gambas3/img/control/socket.png | Bin 0 -> 640 bytes app/src/gambas3/img/control/spinbox.png | Bin 0 -> 311 bytes app/src/gambas3/img/control/splitter.png | Bin 0 -> 144 bytes app/src/gambas3/img/control/spring.png | Bin 0 -> 132 bytes app/src/gambas3/img/control/tabstrip.png | Bin 0 -> 540 bytes app/src/gambas3/img/control/textarea.png | Bin 0 -> 1002 bytes app/src/gambas3/img/control/textbox.png | Bin 0 -> 946 bytes app/src/gambas3/img/control/textedit.png | Bin 0 -> 992 bytes app/src/gambas3/img/control/textlabel.png | Bin 0 -> 1235 bytes app/src/gambas3/img/control/timer.png | Bin 0 -> 944 bytes app/src/gambas3/img/control/togglebutton.png | Bin 0 -> 235 bytes app/src/gambas3/img/control/toolbutton.png | Bin 0 -> 970 bytes app/src/gambas3/img/control/trayicon.png | Bin 0 -> 1238 bytes app/src/gambas3/img/control/treeview.png | Bin 0 -> 243 bytes app/src/gambas3/img/control/udpsocket.png | Bin 0 -> 1115 bytes app/src/gambas3/img/control/unknown.png | Bin 0 -> 125 bytes app/src/gambas3/img/control/vbox.png | Bin 0 -> 252 bytes app/src/gambas3/img/control/void.png | Bin 0 -> 212 bytes app/src/gambas3/img/control/void2.png | Bin 0 -> 226 bytes app/src/gambas3/img/control/voidbutton.png | Bin 0 -> 445 bytes app/src/gambas3/img/control/vpanel.png | Bin 0 -> 274 bytes app/src/gambas3/img/control/vsplit.png | Bin 0 -> 285 bytes app/src/gambas3/img/control/webview.png | Bin 0 -> 1432 bytes app/src/gambas3/img/crash/atari-dark.png | Bin 0 -> 215 bytes app/src/gambas3/img/crash/atari.png | Bin 0 -> 215 bytes app/src/gambas3/img/draw/duplicate.png | Bin 0 -> 167 bytes app/src/gambas3/img/draw/expand.png | Bin 0 -> 334 bytes app/src/gambas3/img/draw/invert.png | Bin 0 -> 257 bytes app/src/gambas3/img/draw/move.png | Bin 0 -> 214 bytes app/src/gambas3/img/draw/offset.png | Bin 0 -> 157 bytes app/src/gambas3/img/draw/save-selection.png | Bin 0 -> 210 bytes app/src/gambas3/img/family/column-i.png | Bin 0 -> 165 bytes app/src/gambas3/img/family/column.png | Bin 0 -> 163 bytes app/src/gambas3/img/family/expand.png | Bin 0 -> 127 bytes app/src/gambas3/img/family/fill-c.png | Bin 0 -> 131 bytes app/src/gambas3/img/family/fill.png | Bin 0 -> 132 bytes app/src/gambas3/img/family/horizontal-c.png | Bin 0 -> 158 bytes app/src/gambas3/img/family/horizontal-i-c.png | Bin 0 -> 160 bytes app/src/gambas3/img/family/horizontal-i.png | Bin 0 -> 159 bytes app/src/gambas3/img/family/horizontal.png | Bin 0 -> 157 bytes app/src/gambas3/img/family/hsplit.png | Bin 0 -> 161 bytes app/src/gambas3/img/family/row-i.png | Bin 0 -> 163 bytes app/src/gambas3/img/family/row.png | Bin 0 -> 165 bytes app/src/gambas3/img/family/vertical-c.png | Bin 0 -> 160 bytes app/src/gambas3/img/family/vertical-i-c.png | Bin 0 -> 162 bytes app/src/gambas3/img/family/vertical-i.png | Bin 0 -> 160 bytes app/src/gambas3/img/family/vertical.png | Bin 0 -> 158 bytes app/src/gambas3/img/family/vsplit.png | Bin 0 -> 188 bytes app/src/gambas3/img/logo/archlinux.png | Bin 0 -> 2137 bytes app/src/gambas3/img/logo/autotools.png | Bin 0 -> 2115 bytes app/src/gambas3/img/logo/debian.png | Bin 0 -> 2277 bytes app/src/gambas3/img/logo/fedora.png | Bin 0 -> 2846 bytes app/src/gambas3/img/logo/folder.png | Bin 0 -> 2647 bytes app/src/gambas3/img/logo/gambas.svg | 540 + app/src/gambas3/img/logo/head-16.png | Bin 0 -> 514 bytes app/src/gambas3/img/logo/head-256.png | Bin 0 -> 14310 bytes app/src/gambas3/img/logo/head-32.png | Bin 0 -> 946 bytes app/src/gambas3/img/logo/logo-ide.png | Bin 0 -> 6834 bytes app/src/gambas3/img/logo/logo.png | Bin 0 -> 5164 bytes app/src/gambas3/img/logo/mageia.png | Bin 0 -> 1967 bytes app/src/gambas3/img/logo/mandriva.png | Bin 0 -> 2048 bytes app/src/gambas3/img/logo/package-gnu.png | Bin 0 -> 3166 bytes app/src/gambas3/img/logo/project.png | Bin 0 -> 1310 bytes app/src/gambas3/img/logo/redhat.png | Bin 0 -> 1373 bytes app/src/gambas3/img/logo/self-extractible.png | Bin 0 -> 2595 bytes app/src/gambas3/img/logo/slackware.png | Bin 0 -> 3840 bytes app/src/gambas3/img/logo/suse.png | Bin 0 -> 2386 bytes app/src/gambas3/img/logo/ubuntu.png | Bin 0 -> 2113 bytes app/src/gambas3/img/module/class.png | Bin 0 -> 997 bytes app/src/gambas3/img/module/form.png | Bin 0 -> 149 bytes app/src/gambas3/img/module/module.png | Bin 0 -> 601 bytes app/src/gambas3/img/module/report.png | Bin 0 -> 596 bytes app/src/gambas3/img/module/termform.png | Bin 0 -> 179 bytes app/src/gambas3/img/module/test.png | Bin 0 -> 444 bytes app/src/gambas3/img/module/webform.png | Bin 0 -> 2071 bytes app/src/gambas3/img/module/webpage.png | Bin 0 -> 1898 bytes app/src/gambas3/img/symbol/constant.png | Bin 0 -> 138 bytes app/src/gambas3/img/symbol/control.png | Bin 0 -> 141 bytes app/src/gambas3/img/symbol/event.png | Bin 0 -> 322 bytes app/src/gambas3/img/symbol/method.png | Bin 0 -> 383 bytes app/src/gambas3/img/symbol/property-ro.png | Bin 0 -> 140 bytes app/src/gambas3/img/symbol/property-rw.png | Bin 0 -> 225 bytes app/src/gambas3/img/symbol/property.png | Bin 0 -> 141 bytes app/src/gambas3/img/symbol/s-method.png | Bin 0 -> 384 bytes app/src/gambas3/img/symbol/s-property-ro.png | Bin 0 -> 138 bytes app/src/gambas3/img/symbol/s-property-rw.png | Bin 0 -> 225 bytes app/src/gambas3/img/symbol/s-property.png | Bin 0 -> 141 bytes app/src/gambas3/img/symbol/s-special.png | Bin 0 -> 375 bytes app/src/gambas3/img/symbol/s-variable.png | Bin 0 -> 141 bytes app/src/gambas3/img/symbol/special.png | Bin 0 -> 384 bytes app/src/gambas3/img/symbol/symbol.png | Bin 0 -> 127 bytes app/src/gambas3/img/symbol/unknown.png | Bin 0 -> 140 bytes app/src/gambas3/img/symbol/variable.png | Bin 0 -> 141 bytes app/src/gambas3/img/waiting.gif | Bin 0 -> 9746 bytes app/src/gambas3/install/Makefile.am | 64 + app/src/gambas3/install/acinclude.m4 | 161 + app/src/gambas3/install/categories | 136 + app/src/gambas3/install/group/archlinux | 51 + app/src/gambas3/install/group/autotools | 1 + app/src/gambas3/install/group/debian | 57 + app/src/gambas3/install/group/fedora | 34 + app/src/gambas3/install/group/mageia | 132 + app/src/gambas3/install/group/mandriva | 99 + app/src/gambas3/install/group/self | 1 + app/src/gambas3/install/group/slackware | 34 + app/src/gambas3/install/group/suse | 225 + app/src/gambas3/install/group/ubuntu | 57 + app/src/gambas3/install/menu/archlinux | 64 + app/src/gambas3/install/menu/autotools | 1 + app/src/gambas3/install/menu/debian | 64 + app/src/gambas3/install/menu/fedora | 109 + app/src/gambas3/install/menu/mageia | 111 + app/src/gambas3/install/menu/mandriva | 111 + app/src/gambas3/install/menu/self | 1 + app/src/gambas3/install/menu/slackware | 109 + app/src/gambas3/install/menu/suse | 109 + app/src/gambas3/install/menu/ubuntu | 64 + app/src/gambas3/install/slack-desc-header | 7 + app/src/gambas3/layout/$compact.layout | 23 + app/src/gambas3/layout/$default.layout | 23 + app/src/gambas3/license | 15 + app/src/gambas3/markdown/markdown.css | 392 + app/src/gambas3/markdown/markdown.js | 96 + app/src/gambas3/po-header.txt | 10 + app/src/gambas3/pot-header.txt | 10 + app/src/gambas3/shortcut.desktop | 9 + app/src/gambas3/snippets | 97 + app/src/gambas3/support.txt | 168 + app/src/gambas3/tags.txt | 19 + app/src/gambas3/template/CClass.class | 9 + app/src/gambas3/template/CContainer.class | 9 + app/src/gambas3/template/CControl.class | 29 + app/src/gambas3/template/FMain.class | 9 + app/src/gambas3/template/FMain.form | 6 + app/src/gambas3/template/FTest.class | 9 + app/src/gambas3/template/FTest.form | 5 + app/src/gambas3/template/MMain.module | 5 + app/src/gambas3/template/MTest.module | 9 + app/src/gambas3/template/_project | 108 + app/src/gambas3/template/ccontainer.png | Bin 0 -> 1606 bytes app/src/gambas3/template/ccontrol.png | Bin 0 -> 1606 bytes app/src/gambas3/template/list | 22 + app/src/gambas3/theme/amber | 23 + app/src/gambas3/theme/amethyst | 23 + app/src/gambas3/theme/blues | 24 + app/src/gambas3/theme/emerald | 23 + app/src/gambas3/theme/gambas | 42 + app/src/gambas3/theme/obsidian | 24 + app/src/gambas3/theme/pastel | 24 + app/src/gambas3/theme/quest | 23 + app/src/gambas3/theme/quick | 24 + app/src/gambas3/theme/ruby | 23 + app/src/gambas3/theme/sapphire | 24 + app/src/gambas3/theme/visual | 23 + app/src/gambas3/theme/zen | 24 + app/src/gambas3/tips/tips.ca.txt | 463 + app/src/gambas3/tips/tips.cs.txt | 547 + app/src/gambas3/tips/tips.de.txt | 417 + app/src/gambas3/tips/tips.en.txt | 592 + app/src/gambas3/tips/tips.es.txt | 302 + app/src/gambas3/tips/tips.fr.txt | 604 + app/src/gambas3/tips/tips.it.txt | 558 + app/src/gambas3/tips/tips.nl.txt | 274 + app/src/gambas3/tips/tips.pl.txt | 280 + app/src/gambas3/tips/tips.ru.txt | 578 + app/src/gambas3/tips/tips.sl.txt | 236 + app/src/gambas3/tips/tips.sv.txt | 401 + app/src/gambas3/tips/tips.tr.txt | 278 + app/src/gambas3/tips/tips.zh.txt | 341 + app/src/gambas3/tips/tips.zh_TW.txt | 442 + app/src/gambas3/usage | 33 + app/src/gambas3/welcome/bells.mp3 | Bin 0 -> 139443 bytes app/src/gambas3/welcome/border.png | Bin 0 -> 437 bytes app/src/gambas3/welcome/corner.png | Bin 0 -> 2836 bytes app/src/gambas3/welcome/fir.png | Bin 0 -> 6955 bytes app/src/gambas3/welcome/spiritus.mp3 | Bin 0 -> 131582 bytes app/src/gb.wiki/.directory | 2 + app/src/gb.wiki/.icon.png | Bin 0 -> 4229 bytes app/src/gb.wiki/.lang/it.po | 193 + app/src/gb.wiki/.lang/nl.po | 188 + app/src/gb.wiki/.lang/pt_BR.po | 190 + app/src/gb.wiki/.lang/ru.po | 210 + app/src/gb.wiki/.lang/zh.po | 188 + app/src/gb.wiki/.project | 15 + app/src/gb.wiki/.src/Actions.module | 189 + app/src/gb.wiki/.src/Controllers/Admin.module | 115 + app/src/gb.wiki/.src/Controllers/Image.module | 42 + .../.src/Controllers/UserManage.module | 109 + app/src/gb.wiki/.src/Controllers/Wiki.module | 64 + app/src/gb.wiki/.src/DBScheme.class | 21 + app/src/gb.wiki/.src/Main.module | 253 + app/src/gb.wiki/.src/Models/Comments.module | 2 + app/src/gb.wiki/.src/Models/Pages.module | 58 + app/src/gb.wiki/.src/Models/Users.module | 163 + app/src/gb.wiki/.src/Models/_Page.class | 171 + app/src/gb.wiki/.src/Models/_User.class | 162 + app/src/gb.wiki/.src/Parser.module | 1223 + app/src/gb.wiki/.src/Path.module | 120 + .../gb.wiki/.src/Views/Admin/WAdminMain.class | 2 + .../.src/Views/Admin/WAdminMain.webpage | 78 + app/src/gb.wiki/.src/Views/WAccount.class | 2 + app/src/gb.wiki/.src/Views/WAccount.webpage | 52 + app/src/gb.wiki/.src/Views/WEdit.class | 2 + app/src/gb.wiki/.src/Views/WEdit.webpage | 27 + app/src/gb.wiki/.src/Views/WHeader.class | 190 + app/src/gb.wiki/.src/Views/WHeader.webpage | 242 + app/src/gb.wiki/.src/Views/WLogin.class | 2 + app/src/gb.wiki/.src/Views/WLogin.webpage | 23 + app/src/gb.wiki/.src/Views/WWiki.class | 1 + app/src/gb.wiki/.src/Views/WWiki.webpage | 17 + app/src/gb.wiki/help.png | Bin 0 -> 1563 bytes app/src/gbs3/.directory | 2 + app/src/gbs3/.hidden/CHANGELOG | 40 + app/src/gbs3/.icon.png | Bin 0 -> 17086 bytes app/src/gbs3/.lang/OldTranslation/en_US.po | 159 + app/src/gbs3/.lang/OldTranslation/fr.po | 181 + app/src/gbs3/.lang/de.po | 155 + app/src/gbs3/.lang/en_US.po | 155 + app/src/gbs3/.lang/fr.po | 155 + app/src/gbs3/.lang/it.po | 91 + app/src/gbs3/.project | 26 + app/src/gbs3/.src/CComponent.class | 412 + app/src/gbs3/.src/ClassDef.class | 40 + app/src/gbs3/.src/Compile.module | 44 + app/src/gbs3/.src/CompileError.module | 10 + app/src/gbs3/.src/Context.class | 90 + app/src/gbs3/.src/ConvertProject.module | 130 + app/src/gbs3/.src/GenerateFiles.module | 67 + .../gbs3/.src/GenerateFilesFromArgs.module | 92 + .../gbs3/.src/GenerateFromLazyScript.module | 139 + app/src/gbs3/.src/Libraries.module | 167 + app/src/gbs3/.src/MServerPage.module | 124 + app/src/gbs3/.src/MakeListing.module | 47 + app/src/gbs3/.src/MakeVirtualProject.module | 116 + app/src/gbs3/.src/ParseArgs.module | 234 + app/src/gbs3/.src/PluginInfo.module | 41 + app/src/gbs3/.src/ProcessAssignment.module | 130 + app/src/gbs3/.src/Reader.class | 647 + app/src/gbs3/.src/ScriptApplyAlias.module | 139 + .../gbs3/.src/ScriptApplyLineCallBacks.module | 19 + .../.src/ScriptApplyPostProgCallBacks.module | 20 + .../.src/ScriptApplyProgramCallBacks.module | 20 + app/src/gbs3/.src/ScriptKeywordFilter.module | 29 + app/src/gbs3/.src/ScriptMain.module | 376 + app/src/gbs3/.src/ScriptPreProcess.module | 238 + app/src/gbs3/.src/ScriptSetAliasTable.module | 40 + .../gbs3/.src/ScriptSetKeywordFilter.module | 42 + .../.src/ScriptSetPostProcessCallBack.module | 40 + .../.src/ScriptSetPreProcessCallBack.module | 59 + app/src/gbs3/.src/ScripterExecute.class | 40 + app/src/gbs3/.src/ScripterPlugin.class | 68 + app/src/gbs3/.src/TestSuite/TSR.module | 221 + .../gbs3/.src/TestSuite/TaMServerPages.test | 44 + .../.src/TestSuite/TbCommandLinePrograms.test | 62 + .../gbs3/.src/TestSuite/TcMMainFunctions.test | 130 + .../gbs3/.src/TestSuite/TdGeneralCompile.test | 11 + app/src/gbs3/.src/TestSuite/TeAliases.test | 42 + .../gbs3/.src/TestSuite/TfKeyWordFilter.test | 41 + app/src/gbs3/.src/TestSuite/TgPluginMode.test | 41 + .../TestSuite/ThPrePostProcessCallBacks.test | 94 + app/src/gbs3/.src/UseLibComp.module | 44 + app/src/gbs3/.src/Verbose.module | 15 + app/src/gbs3/.src/Warning.module | 13 + app/src/gbs3/.test | 5 + app/src/gbs3/PrePostProcessPlugin | 20 + app/src/gbs3/RunComponent | 34 + app/src/gbs3/TestApp | 19 + app/src/gbs3/TestAppCompiled | 38 + app/src/gbs3/TestAppInclude | 8 + app/src/gbs3/TestAppInclude2 | 9 + app/src/gbs3/TestAppWithMain | 20 + app/src/gbs3/TestAppWithMainCompiled | 38 + app/src/gbs3/TestExternStructNoMain.gbs | 63 + app/src/gbs3/TestExternStructWithMain.gbs | 63 + app/src/gbs3/TestFile.info | 2 + app/src/gbs3/TestFileFail.info | 2 + app/src/gbs3/TestLazyApp | 43 + app/src/gbs3/TestPlugin | 43 + app/src/gbs3/TestPlugin2 | 28 + app/src/gbs3/TestPlugin3.gbs | 12 + app/src/gbs3/TestPrePostProcess.gbs | 12 + app/src/gbs3/TestWebPageSource | 13 + app/src/gbs3/WebPageCompiled | 13 + app/src/gbs3/WebPageExpected | 13 + app/src/gbs3/WebPageTest | 11 + app/src/gbs3/icon.png | Bin 0 -> 1528 bytes app/src/gbs3/license | 6 + app/src/gbs3/usage-gbs | 30 + app/src/gbs3/usage-gbw | 21 + app/template/console/.directory | 2 + app/template/console/.icon.png | Bin 0 -> 7173 bytes app/template/console/.lang/es.po | 19 + app/template/console/.lang/es_ES.po | 19 + app/template/console/.lang/fr.po | 19 + app/template/console/.lang/it.po | 19 + app/template/console/.lang/ru.po | 34 + app/template/console/.lang/zh.po | 19 + app/template/console/.project | 11 + app/template/console/.src/Main.module | 7 + app/template/console/icon.png | Bin 0 -> 264 bytes app/template/database/.directory | 2 + app/template/database/.icon.png | Bin 0 -> 6607 bytes app/template/database/.lang/fr.po | 19 + app/template/database/.lang/it.po | 19 + app/template/database/.lang/ru.po | 34 + app/template/database/.lang/zh.po | 19 + app/template/database/.project | 16 + app/template/database/.src/FMain.class | 2 + app/template/database/.src/FMain.form | 5 + app/template/database/icon.png | Bin 0 -> 147 bytes app/template/graphical/.directory | 2 + app/template/graphical/.icon.png | Bin 0 -> 9715 bytes app/template/graphical/.lang/es.po | 19 + app/template/graphical/.lang/es_ES.po | 19 + app/template/graphical/.lang/fr.po | 19 + app/template/graphical/.lang/it.po | 19 + app/template/graphical/.lang/ru.po | 33 + app/template/graphical/.lang/zh.po | 19 + app/template/graphical/.project | 14 + app/template/graphical/.src/FMain.class | 2 + app/template/graphical/.src/FMain.form | 5 + app/template/graphical/icon.png | Bin 0 -> 429 bytes app/template/gtk2/.directory | 2 + app/template/gtk2/.icon.png | Bin 0 -> 13010 bytes app/template/gtk2/.lang/es.po | 19 + app/template/gtk2/.lang/es_ES.po | 19 + app/template/gtk2/.lang/fr.po | 19 + app/template/gtk2/.lang/it.po | 19 + app/template/gtk2/.lang/ru.po | 34 + app/template/gtk2/.lang/zh.po | 19 + app/template/gtk2/.project | 14 + app/template/gtk2/.src/FMain.class | 2 + app/template/gtk2/.src/FMain.form | 5 + app/template/gtk2/icon.png | Bin 0 -> 644 bytes app/template/gtk3/.directory | 2 + app/template/gtk3/.icon.png | Bin 0 -> 13132 bytes app/template/gtk3/.lang/es.po | 19 + app/template/gtk3/.lang/fr.po | 19 + app/template/gtk3/.lang/it.po | 19 + app/template/gtk3/.lang/ru.po | 34 + app/template/gtk3/.lang/zh.po | 19 + app/template/gtk3/.project | 14 + app/template/gtk3/.src/FMain.class | 2 + app/template/gtk3/.src/FMain.form | 5 + app/template/gtk3/icon.png | Bin 0 -> 647 bytes app/template/qt/.directory | 2 + app/template/qt/.icon.png | Bin 0 -> 13190 bytes app/template/qt/.lang/es.po | 19 + app/template/qt/.lang/es_ES.po | 19 + app/template/qt/.lang/fr.po | 19 + app/template/qt/.lang/it.po | 19 + app/template/qt/.lang/ru.po | 34 + app/template/qt/.lang/zh.po | 19 + app/template/qt/.project | 14 + app/template/qt/.src/FMain.class | 2 + app/template/qt/.src/FMain.form | 5 + app/template/qt/icon.png | Bin 0 -> 769 bytes app/template/sdl/.directory | 2 + app/template/sdl/.icon.png | Bin 0 -> 10332 bytes app/template/sdl/.lang/es.po | 19 + app/template/sdl/.lang/es_ES.po | 19 + app/template/sdl/.lang/fr.po | 19 + app/template/sdl/.lang/it.po | 19 + app/template/sdl/.lang/ru.po | 34 + app/template/sdl/.lang/zh.po | 19 + app/template/sdl/.project | 14 + app/template/sdl/.src/MMain.module | 40 + app/template/sdl/icon.png | Bin 0 -> 538 bytes app/template/web/.directory | 2 + app/template/web/.icon.png | Bin 0 -> 14817 bytes app/template/web/.lang/es.po | 19 + app/template/web/.lang/es_ES.po | 19 + app/template/web/.lang/fr.po | 19 + app/template/web/.lang/it.po | 19 + app/template/web/.lang/ru.po | 34 + app/template/web/.lang/zh.po | 19 + app/template/web/.project | 14 + app/template/web/.src/Main.class | 2 + app/template/web/.src/Main.webpage | 27 + app/template/web/icon.png | Bin 0 -> 775 bytes app/template/webform/.directory | 2 + app/template/webform/.icon.png | Bin 0 -> 12584 bytes app/template/webform/.lang/fr.po | 27 + app/template/webform/.lang/it.po | 27 + app/template/webform/.lang/ru.po | 42 + app/template/webform/.lang/zh.po | 27 + app/template/webform/.project | 16 + app/template/webform/.src/Webform1.class | 22 + app/template/webform/.src/Webform1.webform | 27 + app/template/webform/icon.png | Bin 0 -> 668 bytes app/template/~subversion/.directory | 2 + app/template/~subversion/.icon.png | Bin 0 -> 9478 bytes app/template/~subversion/.lang/es.po | 19 + app/template/~subversion/.lang/es_ES.po | 19 + app/template/~subversion/.lang/fr.po | 19 + app/template/~subversion/.lang/it.po | 19 + app/template/~subversion/.lang/ru.po | 34 + app/template/~subversion/.lang/zh.po | 19 + app/template/~subversion/.project | 11 + app/template/~subversion/.src/Main.module | 7 + app/template/~subversion/icon.png | Bin 0 -> 465 bytes benchmark/benchmark.gbs | 168 + benchmark/mandelbrot.gbs | 80 + benchmark/mandelbrot.jvs | 166 + benchmark/mandelbrot.pl | 75 + benchmark/mandelbrot.py | 54 + benchmark/nbody.gbs | 212 + benchmark/nbody.jvs | 174 + benchmark/nbody.pl | 112 + benchmark/nbody.py | 120 + benchmark/polynom.gbs | 34 + benchmark/polynom.jvs | 38 + benchmark/polynom.pl | 35 + benchmark/polynom.py | 23 + benchmark/primes.gbs | 61 + benchmark/primes.jvs | 69 + benchmark/primes.pl | 44 + benchmark/primes.py | 30 + benchmark/sort.gbs | 69 + benchmark/sort.jvs | 84 + benchmark/sort.pl | 129 + benchmark/sort.py | 115 + benchmark/string.gbs | 23 + benchmark/string.jvs | 29 + benchmark/string.pl | 18 + benchmark/string.py | 23 + benchmark/string2.gbs | 17 + benchmark/string2.jvs | 31 + benchmark/string2.pl | 16 + benchmark/string2.py | 11 + benchmark/test.jvs | 8 + build-dist | 34 + comp/AUTHORS | 0 comp/COPYING | 1 + comp/ChangeLog | 0 comp/INSTALL | 231 + comp/Makefile.am | 54 + comp/NEWS | 0 comp/README | 0 comp/TODO | 0 comp/acinclude.m4 | 1 + comp/configure.ac | 17 + comp/reconf | 1 + comp/src/gb.args/.component | 4 + comp/src/gb.args/.directory | 2 + comp/src/gb.args/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.args/.lang/cs.po | 36 + comp/src/gb.args/.lang/es.po | 35 + comp/src/gb.args/.lang/es_ES.po | 35 + comp/src/gb.args/.lang/fr.po | 35 + comp/src/gb.args/.lang/it.po | 40 + comp/src/gb.args/.lang/nl.po | 36 + comp/src/gb.args/.lang/pt_BR.po | 36 + comp/src/gb.args/.lang/ru.po | 50 + comp/src/gb.args/.lang/zh.po | 36 + comp/src/gb.args/.project | 12 + comp/src/gb.args/.src/Args.module | 255 + comp/src/gb.args/.src/MMain.module | 24 + comp/src/gb.chart/.component | 7 + comp/src/gb.chart/.directory | 2 + comp/src/gb.chart/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.chart/.project | 14 + comp/src/gb.chart/.src/CPoint.class | 13 + comp/src/gb.chart/.src/CRect.class | 16 + comp/src/gb.chart/.src/Chart.class | 376 + comp/src/gb.chart/.src/ChartStyle.class | 9 + comp/src/gb.chart/.src/ChartType.class | 33 + comp/src/gb.chart/.src/FTest.class | 510 + comp/src/gb.chart/.src/FTest.form | 234 + comp/src/gb.chart/.src/FTest2.class | 45 + comp/src/gb.chart/.src/FTest2.form | 19 + comp/src/gb.chart/.src/MTools.module | 376 + comp/src/gb.chart/.src/Styles/_CSerie.class | 151 + .../gb.chart/.src/Styles/_CStyleAreas.class | 135 + .../src/gb.chart/.src/Styles/_CStyleBar.class | 109 + .../gb.chart/.src/Styles/_CStyleColumns.class | 113 + .../gb.chart/.src/Styles/_CStyleLine.class | 122 + .../src/gb.chart/.src/Styles/_CStylePie.class | 106 + .../gb.chart/.src/Styles/_CStylePlot.class | 94 + comp/src/gb.chart/.src/_CAxes.class | 89 + comp/src/gb.chart/.src/_CHeaders.class | 103 + comp/src/gb.chart/.src/_CLabel.class | 41 + comp/src/gb.chart/.src/_CLabels.class | 3 + comp/src/gb.chart/.src/_CLegend.class | 206 + comp/src/gb.chart/.src/_CTitle.class | 53 + comp/src/gb.chart/.src/_CXAxe.class | 25 + comp/src/gb.chart/.src/_CYAxe.class | 22 + comp/src/gb.chart/.src/_Colors.class | 65 + comp/src/gb.chart/img/areas.png | Bin 0 -> 308 bytes comp/src/gb.chart/img/areasnormal.png | Bin 0 -> 278 bytes comp/src/gb.chart/img/areaspercent.png | Bin 0 -> 226 bytes comp/src/gb.chart/img/areasstacked.png | Bin 0 -> 308 bytes comp/src/gb.chart/img/bars.png | Bin 0 -> 235 bytes comp/src/gb.chart/img/barsnormal.png | Bin 0 -> 204 bytes comp/src/gb.chart/img/barspercent.png | Bin 0 -> 172 bytes comp/src/gb.chart/img/barsstacked.png | Bin 0 -> 177 bytes comp/src/gb.chart/img/columns.png | Bin 0 -> 201 bytes .../gb.chart/img/columnslinecombination.png | Bin 0 -> 249 bytes comp/src/gb.chart/img/columnsnormal.png | Bin 0 -> 195 bytes comp/src/gb.chart/img/columnspercent.png | Bin 0 -> 194 bytes comp/src/gb.chart/img/columnsstacked.png | Bin 0 -> 191 bytes comp/src/gb.chart/img/lines.png | Bin 0 -> 307 bytes comp/src/gb.chart/img/linesnormal.png | Bin 0 -> 307 bytes comp/src/gb.chart/img/linespercent.png | Bin 0 -> 217 bytes comp/src/gb.chart/img/linesstacked.png | Bin 0 -> 309 bytes comp/src/gb.chart/img/linessymbols.png | Bin 0 -> 322 bytes comp/src/gb.chart/img/net.png | Bin 0 -> 334 bytes comp/src/gb.chart/img/pie.png | Bin 0 -> 290 bytes comp/src/gb.chart/img/pienormal.png | Bin 0 -> 290 bytes comp/src/gb.chart/img/pieoffset1.png | Bin 0 -> 315 bytes comp/src/gb.chart/img/pieoffset2.png | Bin 0 -> 333 bytes comp/src/gb.chart/img/pierings.png | Bin 0 -> 367 bytes comp/src/gb.chart/img/plots.png | Bin 0 -> 229 bytes comp/src/gb.chart/img/plotsnormal.png | Bin 0 -> 229 bytes comp/src/gb.chart/img/stockchart.png | Bin 0 -> 246 bytes comp/src/gb.db.form/.component | 6 + .../.connection/Connection1.connection | 8 + .../.connection/Connection2.connection | 11 + .../.connection/Connection3.connection | 12 + .../.connection/Connection4.connection | 12 + comp/src/gb.db.form/.directory | 2 + .../.hidden/control/databrowser.png | Bin 0 -> 922 bytes .../.hidden/control/datacheckbox.png | Bin 0 -> 984 bytes .../gb.db.form/.hidden/control/datacombo.png | Bin 0 -> 989 bytes .../.hidden/control/datacomboview.png | Bin 0 -> 918 bytes .../.hidden/control/datacontrol.png | Bin 0 -> 1519 bytes .../gb.db.form/.hidden/control/datasource.png | Bin 0 -> 1177 bytes .../gb.db.form/.hidden/control/dataview.png | Bin 0 -> 773 bytes comp/src/gb.db.form/.hidden/icon.png | Bin 0 -> 1594 bytes comp/src/gb.db.form/.icon.png | Bin 0 -> 9749 bytes comp/src/gb.db.form/.lang/ca.po | 156 + comp/src/gb.db.form/.lang/cs.po | 152 + comp/src/gb.db.form/.lang/de.po | 152 + comp/src/gb.db.form/.lang/es.po | 203 + comp/src/gb.db.form/.lang/es_ES.po | 203 + comp/src/gb.db.form/.lang/fr.po | 198 + comp/src/gb.db.form/.lang/it.po | 119 + comp/src/gb.db.form/.lang/nl.po | 120 + comp/src/gb.db.form/.lang/pt_BR.po | 119 + comp/src/gb.db.form/.lang/ru.po | 130 + comp/src/gb.db.form/.lang/sv.po | 208 + comp/src/gb.db.form/.lang/zh.po | 207 + comp/src/gb.db.form/.project | 21 + comp/src/gb.db.form/.src/Common.module | 110 + comp/src/gb.db.form/.src/DataBrowser.class | 216 + comp/src/gb.db.form/.src/DataCheckBox.class | 163 + comp/src/gb.db.form/.src/DataCombo.class | 344 + comp/src/gb.db.form/.src/DataComboView.class | 482 + comp/src/gb.db.form/.src/DataConnection.class | 147 + comp/src/gb.db.form/.src/DataControl.class | 385 + comp/src/gb.db.form/.src/DataField.class | 82 + comp/src/gb.db.form/.src/DataSource.class | 803 + comp/src/gb.db.form/.src/DataTable.class | 484 + comp/src/gb.db.form/.src/DataTree.class | 145 + comp/src/gb.db.form/.src/DataView.class | 1312 + comp/src/gb.db.form/.src/FBlobEditor.class | 229 + comp/src/gb.db.form/.src/FBlobEditor.form | 50 + comp/src/gb.db.form/.src/FBrowser.class | 272 + comp/src/gb.db.form/.src/FBrowser.form | 65 + comp/src/gb.db.form/.src/Test/FMain.class | 9 + comp/src/gb.db.form/.src/Test/FMain.form | 16 + comp/src/gb.db.form/.src/Test/FMain2.class | 76 + comp/src/gb.db.form/.src/Test/FMain2.form | 139 + comp/src/gb.db.form/.src/Test/FMain3.class | 8 + comp/src/gb.db.form/.src/Test/FMain3.form | 33 + comp/src/gb.db.form/.src/Test/FTest.class | 44 + comp/src/gb.db.form/.src/Test/FTest.form | 66 + comp/src/gb.db.form/.src/Test/Main.module | 24 + comp/src/gb.dbus.trayicon/.component | 7 + comp/src/gb.dbus.trayicon/.directory | 2 + .../.hidden/com.canonical.dbusmenu.xml | 437 + comp/src/gb.dbus.trayicon/.hidden/doc.txt | 167 + comp/src/gb.dbus.trayicon/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.dbus.trayicon/.project | 13 + .../.src/DBusStatusIcon.class | 348 + .../.src/DBusStatusIconMenu.class | 275 + comp/src/gb.dbus.trayicon/.src/FMain.class | 60 + comp/src/gb.dbus.trayicon/.src/FMain.form | 18 + comp/src/gb.dbus.trayicon/.src/FMain1.class | 53 + comp/src/gb.dbus.trayicon/.src/FMain1.form | 36 + comp/src/gb.dbus.trayicon/.src/FTest.class | 95 + comp/src/gb.dbus.trayicon/.src/FTest.form | 76 + comp/src/gb.dbus.trayicon/.src/Main.module | 56 + comp/src/gb.dbus.trayicon/.src/TrayIcon.class | 347 + .../src/gb.dbus.trayicon/.src/TrayIcons.class | 44 + .../gb.dbus.trayicon/.src/_DBusMenuItem.class | 6 + .../.src/_DBusMenuLayout.class | 6 + .../.src/_DBusMenuProperties.class | 6 + .../.src/_DBusStatusIconPixmap.class | 29 + .../.src/_DBusStatusIconTooltip.class | 16 + .../src/gb.dbus.trayicon/.src/_DBusUInt.class | 16 + comp/src/gb.dbus.trayicon/default.png | Bin 0 -> 9744 bytes comp/src/gb.desktop/.component | 6 + comp/src/gb.desktop/.directory | 2 + .../.hidden/control/desktopwatcher.png | Bin 0 -> 2758 bytes comp/src/gb.desktop/.hidden/icon.png | Bin 0 -> 182 bytes .../.hidden/xdg-utils-1.0.2/LICENSE | 18 + .../.hidden/xdg-utils-1.0.2/scripts/README | 17 + .../xdg-utils-1.0.2/scripts/xdg-desktop-icon | 549 + .../xdg-utils-1.0.2/scripts/xdg-desktop-menu | 1264 + .../.hidden/xdg-utils-1.0.2/scripts/xdg-email | 640 + .../xdg-utils-1.0.2/scripts/xdg-icon-resource | 837 + .../.hidden/xdg-utils-1.0.2/scripts/xdg-mime | 1090 + .../.hidden/xdg-utils-1.0.2/scripts/xdg-open | 436 + .../xdg-utils-1.0.2/scripts/xdg-screensaver | 786 + .../xdg-utils-1.0.3.pre.patch/xdg-email | 730 + .../.hidden/xdg-utils-1.0.3.pre/xdg-copy | 303 + .../xdg-utils-1.0.3.pre/xdg-desktop-icon | 559 + .../xdg-utils-1.0.3.pre/xdg-desktop-menu | 1224 + .../.hidden/xdg-utils-1.0.3.pre/xdg-email | 723 + .../xdg-utils-1.0.3.pre/xdg-file-dialog | 603 + .../xdg-utils-1.0.3.pre/xdg-icon-resource | 841 + .../.hidden/xdg-utils-1.0.3.pre/xdg-mime | 1212 + .../.hidden/xdg-utils-1.0.3.pre/xdg-open | 523 + .../xdg-utils-1.0.3.pre/xdg-screensaver | 888 + .../.hidden/xdg-utils-1.0.3.pre/xdg-settings | 872 + .../.hidden/xdg-utils-1.0.3.pre/xdg-su | 438 + .../.hidden/xdg-utils-1.0.3.pre/xdg-terminal | 462 + comp/src/gb.desktop/.icon.png | Bin 0 -> 5379 bytes comp/src/gb.desktop/.project | 17 + comp/src/gb.desktop/.src/Desktop.class | 604 + comp/src/gb.desktop/.src/DesktopFile.class | 749 + comp/src/gb.desktop/.src/DesktopMime.class | 509 + comp/src/gb.desktop/.src/Main.module | 279 + comp/src/gb.desktop/.src/Tests/Form1.class | 82 + comp/src/gb.desktop/.src/Tests/Form1.form | 25 + comp/src/gb.desktop/.src/Tests/Form11.class | 2 + comp/src/gb.desktop/.src/Tests/Form11.form | 11 + comp/src/gb.desktop/.src/Tests/Form2.class | 38 + comp/src/gb.desktop/.src/Tests/Form2.form | 10 + comp/src/gb.desktop/.src/Tests/Module1.module | 7 + comp/src/gb.desktop/.src/_DesktopIcons.class | 16 + comp/src/gb.desktop/.src/_DesktopMenus.class | 15 + .../gb.desktop/.src/_Desktop_Passwords.class | 185 + .../.src/_Desktop_ScreenSaver.class | 45 + comp/src/gb.desktop/file.png | 1 + .../src/gb.desktop/xdg-utils/xdg-desktop-icon | 757 + .../src/gb.desktop/xdg-utils/xdg-desktop-menu | 1447 + comp/src/gb.desktop/xdg-utils/xdg-email | 1036 + .../gb.desktop/xdg-utils/xdg-icon-resource | 1053 + comp/src/gb.desktop/xdg-utils/xdg-mime | 1586 + comp/src/gb.desktop/xdg-utils/xdg-open | 1066 + comp/src/gb.desktop/xdg-utils/xdg-screensaver | 1434 + comp/src/gb.desktop/xdg-utils/xdg-settings | 1401 + comp/src/gb.eval.highlight/.component | 4 + comp/src/gb.eval.highlight/.directory | 2 + comp/src/gb.eval.highlight/.icon.png | Bin 0 -> 11424 bytes comp/src/gb.eval.highlight/.lang/ar.po | 175 + comp/src/gb.eval.highlight/.lang/ca.po | 181 + comp/src/gb.eval.highlight/.lang/cs.po | 179 + comp/src/gb.eval.highlight/.lang/cy.po | 179 + comp/src/gb.eval.highlight/.lang/de.po | 179 + comp/src/gb.eval.highlight/.lang/el.po | 179 + comp/src/gb.eval.highlight/.lang/es.po | 179 + comp/src/gb.eval.highlight/.lang/es_ES.po | 179 + comp/src/gb.eval.highlight/.lang/fa.po | 180 + comp/src/gb.eval.highlight/.lang/fr.po | 175 + comp/src/gb.eval.highlight/.lang/gl_ES.po | 179 + comp/src/gb.eval.highlight/.lang/hr.po | 175 + comp/src/gb.eval.highlight/.lang/hu.po | 179 + comp/src/gb.eval.highlight/.lang/id.po | 179 + comp/src/gb.eval.highlight/.lang/it.po | 175 + comp/src/gb.eval.highlight/.lang/ja.po | 179 + comp/src/gb.eval.highlight/.lang/ko.po | 179 + comp/src/gb.eval.highlight/.lang/lt.po | 179 + comp/src/gb.eval.highlight/.lang/nl.po | 179 + comp/src/gb.eval.highlight/.lang/no.po | 179 + comp/src/gb.eval.highlight/.lang/pl.po | 179 + comp/src/gb.eval.highlight/.lang/pt.po | 179 + comp/src/gb.eval.highlight/.lang/pt_BR.po | 179 + comp/src/gb.eval.highlight/.lang/ro.po | 179 + comp/src/gb.eval.highlight/.lang/ru.po | 179 + comp/src/gb.eval.highlight/.lang/sl.po | 179 + comp/src/gb.eval.highlight/.lang/sv.po | 179 + comp/src/gb.eval.highlight/.lang/tr.po | 179 + comp/src/gb.eval.highlight/.lang/zh.po | 175 + comp/src/gb.eval.highlight/.lang/zh_TW.po | 175 + comp/src/gb.eval.highlight/.project | 11 + .../gb.eval.highlight/.src/Highlight.class | 110 + comp/src/gb.eval.highlight/.src/Init.module | 171 + comp/src/gb.eval.highlight/.src/Main.module | 57 + .../.src/OldHighlighter/Helper.module | 71 + .../.src/OldHighlighter/HighlightC.module | 274 + .../OldHighlighter/HighlightCPlusPlus.module | 53 + .../.src/OldHighlighter/HighlightCSS.module | 243 + .../.src/OldHighlighter/HighlightDiff.module | 34 + .../.src/OldHighlighter/HighlightHTML.module | 538 + .../OldHighlighter/HighlightJavascript.module | 287 + .../.src/OldHighlighter/HighlightSQL.module | 159 + comp/src/gb.eval.highlight/.src/State.class | 5 + .../.src/TestSuite/AnsiTextColor.test | 13 + .../.src/TestSuite/HtmlTextFormatColor.test | 14 + .../.src/TestSuite/RichTextFormatColor.test | 13 + .../.src/TextHighlighter.class | 969 + .../.src/TextHighlighterStyle.class | 174 + .../.src/TextHighlighterTheme.class | 228 + .../.src/TextHighlighter_C.class | 284 + .../.src/TextHighlighter_CPlusPlus.class | 59 + .../.src/TextHighlighter_CSS.class | 304 + .../.src/TextHighlighter_Diff.class | 47 + .../.src/TextHighlighter_Gambas.class | 88 + .../.src/TextHighlighter_Html.class | 640 + .../.src/TextHighlighter_Javascript.class | 291 + .../.src/TextHighlighter_SQL.class | 171 + .../.src/TextHighlighter_Sh.class | 367 + .../.src/TextHighlighter_WebPage.class | 13 + comp/src/gb.eval.highlight/.test | 5 + .../UnitTest/ANSITestResults | 18 + .../UnitTest/HTMLTestResults | 18 + .../gb.eval.highlight/UnitTest/RTFTestResults | 18 + .../gb.eval.highlight/UnitTest/TestProgram | 17 + comp/src/gb.eval.highlight/css/properties | 237 + comp/src/gb.eval.highlight/css/values | 290 + comp/src/gb.eval.highlight/gambas | 1 + comp/src/gb.eval.highlight/sql/datatypes | 67 + comp/src/gb.eval.highlight/sql/functions | 206 + comp/src/gb.eval.highlight/sql/keywords | 543 + comp/src/gb.eval.highlight/sql/operators | 58 + comp/src/gb.form.dialog/.component | 6 + comp/src/gb.form.dialog/.directory | 2 + comp/src/gb.form.dialog/.hidden/icon.png | Bin 0 -> 231 bytes comp/src/gb.form.dialog/.icon.png | Bin 0 -> 5643 bytes comp/src/gb.form.dialog/.lang/ca.po | 44 + comp/src/gb.form.dialog/.lang/cs.po | 42 + comp/src/gb.form.dialog/.lang/de.po | 42 + comp/src/gb.form.dialog/.lang/es.po | 46 + comp/src/gb.form.dialog/.lang/es_ES.po | 46 + comp/src/gb.form.dialog/.lang/fr.po | 63 + comp/src/gb.form.dialog/.lang/it.po | 55 + comp/src/gb.form.dialog/.lang/ja.po | 33 + comp/src/gb.form.dialog/.lang/nl.po | 56 + comp/src/gb.form.dialog/.lang/pt_BR.po | 55 + comp/src/gb.form.dialog/.lang/ru.po | 76 + comp/src/gb.form.dialog/.lang/sv.po | 32 + comp/src/gb.form.dialog/.lang/zh.po | 55 + comp/src/gb.form.dialog/.project | 20 + comp/src/gb.form.dialog/.src/Dialog.class | 156 + .../gb.form.dialog/.src/FAskPassword.class | 110 + .../src/gb.form.dialog/.src/FAskPassword.form | 76 + comp/src/gb.form.dialog/.src/FDirDialog.class | 61 + comp/src/gb.form.dialog/.src/FDirDialog.form | 30 + .../src/gb.form.dialog/.src/FFileDialog.class | 164 + comp/src/gb.form.dialog/.src/FFileDialog.form | 18 + .../src/gb.form.dialog/.src/FFontDialog.class | 62 + comp/src/gb.form.dialog/.src/FFontDialog.form | 33 + comp/src/gb.form.dialog/.src/FInputDate.class | 43 + comp/src/gb.form.dialog/.src/FInputDate.form | 37 + comp/src/gb.form.dialog/.src/Main.module | 11 + comp/src/gb.form.editor/.component | 5 + comp/src/gb.form.editor/.directory | 2 + comp/src/gb.form.editor/.hidden/CHANGELOG | 3 + .../.hidden/control/texteditor.png | Bin 0 -> 244 bytes comp/src/gb.form.editor/.icon.png | Bin 0 -> 6026 bytes comp/src/gb.form.editor/.lang/fr.po | 51 + comp/src/gb.form.editor/.project | 31 + comp/src/gb.form.editor/.src/CCommand.class | 261 + .../gb.form.editor/.src/CCommandBefore.class | 7 + comp/src/gb.form.editor/.src/CDocument.class | 1744 + comp/src/gb.form.editor/.src/CLineInfo.class | 101 + comp/src/gb.form.editor/.src/FFind.class | 108 + comp/src/gb.form.editor/.src/FFind.form | 41 + comp/src/gb.form.editor/.src/FGoto.class | 52 + comp/src/gb.form.editor/.src/FGoto.form | 42 + comp/src/gb.form.editor/.src/Helper.module | 71 + comp/src/gb.form.editor/.src/Main.module | 16 + comp/src/gb.form.editor/.src/TextEditor.class | 6966 +++ .../gb.form.editor/.src/TextEditorMode.class | 298 + .../.src/TextEditorMode_CSS.class | 12 + .../.src/TextEditorMode_Gambas.class | 71 + .../.src/TextEditorMode_HTML.class | 88 + .../.src/TextEditorMode_Javascript.class | 14 + .../.src/TextEditorMode_SQL.class | 5 + .../.src/TextEditorMode_Sh.class | 14 + .../.src/TextEditorMode_WebPage.class | 3 + .../gb.form.editor/.src/TextEditorStyle.class | 42 + .../.src/_TextEditor_Line.class | 259 + .../.src/_TextEditor_Rows.class | 438 + .../.src/_TextEditor_State.class | 37 + comp/src/gb.form.editor/.src/test/FTest.class | 10 + comp/src/gb.form.editor/.src/test/FTest.form | 13 + .../.src/test/FTestEditor.class | 287 + .../gb.form.editor/.src/test/FTestEditor.form | 76 + comp/src/gb.form.editor/Text1 | 19 + comp/src/gb.form.editor/test.html | 89 + comp/src/gb.form.mdi/.component | 6 + comp/src/gb.form.mdi/.directory | 2 + .../gb.form.mdi/.hidden/control/toolbar.png | Bin 0 -> 189 bytes .../gb.form.mdi/.hidden/control/workspace.png | Bin 0 -> 182 bytes comp/src/gb.form.mdi/.icon.png | Bin 0 -> 5310 bytes comp/src/gb.form.mdi/.lang/ca.po | 160 + comp/src/gb.form.mdi/.lang/cs.po | 160 + comp/src/gb.form.mdi/.lang/de.po | 270 + comp/src/gb.form.mdi/.lang/es.po | 215 + comp/src/gb.form.mdi/.lang/es_ES.po | 215 + comp/src/gb.form.mdi/.lang/fr.po | 387 + comp/src/gb.form.mdi/.lang/it.po | 199 + comp/src/gb.form.mdi/.lang/ja.po | 39 + comp/src/gb.form.mdi/.lang/nl.po | 200 + comp/src/gb.form.mdi/.lang/pt_BR.po | 199 + comp/src/gb.form.mdi/.lang/ru.po | 278 + comp/src/gb.form.mdi/.lang/sv.po | 148 + comp/src/gb.form.mdi/.lang/zh.po | 199 + comp/src/gb.form.mdi/.project | 21 + comp/src/gb.form.mdi/.src/Action/Action.class | 112 + .../src/gb.form.mdi/.src/Action/CAction.class | 128 + .../gb.form.mdi/.src/Action/MAction.module | 170 + comp/src/gb.form.mdi/.src/MMain.module | 7 + .../gb.form.mdi/.src/Shortcut/FShortcut.class | 336 + .../gb.form.mdi/.src/Shortcut/FShortcut.form | 61 + .../.src/Shortcut/FShortcutEditor.class | 100 + .../.src/Shortcut/FShortcutEditor.form | 22 + comp/src/gb.form.mdi/.src/Tests/FMain.class | 82 + comp/src/gb.form.mdi/.src/Tests/FMain.form | 120 + comp/src/gb.form.mdi/.src/Tests/FMain1.class | 48 + comp/src/gb.form.mdi/.src/Tests/FMain1.form | 105 + comp/src/gb.form.mdi/.src/Tests/FMain2.class | 1 + comp/src/gb.form.mdi/.src/Tests/FMain2.form | 121 + .../.src/Tests/FTestBackground.class | 8 + .../.src/Tests/FTestBackground.form | 10 + .../.src/Tests/FTestSidePanel.class | 12 + .../.src/Tests/FTestSidePanel.form | 19 + comp/src/gb.form.mdi/.src/Tests/Form1.class | 18 + comp/src/gb.form.mdi/.src/Tests/Form1.form | 17 + comp/src/gb.form.mdi/.src/Tests/Form2.class | 17 + comp/src/gb.form.mdi/.src/Tests/Form2.form | 22 + .../gb.form.mdi/.src/ToolBar/CToolbar.class | 18 + .../gb.form.mdi/.src/ToolBar/FToolBar.class | 1804 + .../gb.form.mdi/.src/ToolBar/FToolBar.form | 27 + .../.src/ToolBar/FToolBarConfig.class | 506 + .../.src/ToolBar/FToolBarConfig.form | 98 + .../gb.form.mdi/.src/ToolBar/ToolBar.class | 213 + .../.src/ToolBar/ToolBarExpander.class | 9 + .../gb.form.mdi/.src/Workspace/CWindow.class | 9 + .../.src/Workspace/FWorkspace.class | 1047 + .../.src/Workspace/FWorkspace.form | 145 + .../.src/Workspace/Workspace.class | 284 + comp/src/gb.form.mdi/70a017.png | Bin 0 -> 1542 bytes comp/src/gb.form.mdi/control/buttonbox.png | Bin 0 -> 290 bytes comp/src/gb.form.mdi/control/combobox.png | Bin 0 -> 287 bytes comp/src/gb.form.mdi/control/datebox.png | Bin 0 -> 568 bytes comp/src/gb.form.mdi/control/valuebox.png | Bin 0 -> 374 bytes comp/src/gb.form.mdi/img/close.png | Bin 0 -> 112 bytes comp/src/gb.form.mdi/img/configure.png | Bin 0 -> 141 bytes comp/src/gb.form.mdi/img/configure_dark.png | Bin 0 -> 139 bytes comp/src/gb.form.mdi/img/expander.png | Bin 0 -> 188 bytes comp/src/gb.form.mdi/img/handle-v.png | Bin 0 -> 100 bytes comp/src/gb.form.mdi/img/handle.png | Bin 0 -> 97 bytes comp/src/gb.form.mdi/img/hash.png | Bin 0 -> 86 bytes comp/src/gb.form.mdi/img/roll.png | Bin 0 -> 90 bytes comp/src/gb.form.mdi/img/separator.png | Bin 0 -> 92 bytes comp/src/gb.form.mdi/img/space.png | Bin 0 -> 92 bytes comp/src/gb.form.mdi/img/unroll.png | Bin 0 -> 93 bytes comp/src/gb.form.print/.component | 6 + comp/src/gb.form.print/.directory | 2 + comp/src/gb.form.print/.hidden/printer.png | Bin 0 -> 566 bytes comp/src/gb.form.print/.icon.png | Bin 0 -> 6301 bytes comp/src/gb.form.print/.lang/es.po | 215 + comp/src/gb.form.print/.lang/es_ES.po | 215 + comp/src/gb.form.print/.lang/fr.po | 174 + comp/src/gb.form.print/.lang/it.po | 174 + comp/src/gb.form.print/.lang/nl.po | 219 + comp/src/gb.form.print/.lang/pt_BR.po | 174 + comp/src/gb.form.print/.lang/ru.po | 190 + comp/src/gb.form.print/.lang/zh.po | 175 + comp/src/gb.form.print/.project | 16 + comp/src/gb.form.print/.src/FPreview.class | 694 + comp/src/gb.form.print/.src/FPreview.form | 337 + comp/src/gb.form.print/.src/FPrinting.class | 13 + comp/src/gb.form.print/.src/FPrinting.form | 24 + comp/src/gb.form.print/.src/MTest.module | 51 + comp/src/gb.form.print/.src/Printer.class | 31 + comp/src/gb.form.print/molly-malone.txt | 35 + comp/src/gb.form.stock/.component | 4 + comp/src/gb.form.stock/.directory | 2 + comp/src/gb.form.stock/.hidden/CHANGELOG | 3 + comp/src/gb.form.stock/.hidden/earth.png | Bin 0 -> 3866 bytes comp/src/gb.form.stock/.hidden/goutte.png | Bin 0 -> 575 bytes comp/src/gb.form.stock/.hidden/icon.png | Bin 0 -> 173 bytes comp/src/gb.form.stock/.icon.png | Bin 0 -> 6121 bytes comp/src/gb.form.stock/.project | 21 + comp/src/gb.form.stock/.src/Main.module | 270 + .../gb.form.stock/.src/MakeIconTheme.module | 226 + .../gb.form.stock/.src/_DefaultStock.class | 120 + .../gb.form.stock/gambas-mono/128/alarm.png | Bin 0 -> 2275 bytes .../gb.form.stock/gambas-mono/128/android.png | Bin 0 -> 1617 bytes .../gambas-mono/128/application.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas-mono/128/audio.png | Bin 0 -> 2068 bytes .../gb.form.stock/gambas-mono/128/book.png | Bin 0 -> 282 bytes .../src/gb.form.stock/gambas-mono/128/bus.png | Bin 0 -> 1154 bytes comp/src/gb.form.stock/gambas-mono/128/c.png | Bin 0 -> 1231 bytes .../gambas-mono/128/chart-line.png | Bin 0 -> 736 bytes .../gambas-mono/128/chart-pie.png | Bin 0 -> 2350 bytes .../gb.form.stock/gambas-mono/128/chart.png | Bin 0 -> 159 bytes .../gb.form.stock/gambas-mono/128/chat.png | Bin 0 -> 1777 bytes .../gb.form.stock/gambas-mono/128/clock.png | Bin 0 -> 1991 bytes .../gb.form.stock/gambas-mono/128/cloud.png | Bin 0 -> 1555 bytes .../gb.form.stock/gambas-mono/128/compass.png | Bin 0 -> 2284 bytes .../gb.form.stock/gambas-mono/128/cookie.png | Bin 0 -> 1732 bytes .../src/gb.form.stock/gambas-mono/128/cpp.png | Bin 0 -> 1199 bytes .../src/gb.form.stock/gambas-mono/128/css.png | Bin 0 -> 1873 bytes .../gambas-mono/128/currency.png | Bin 0 -> 2896 bytes .../gb.form.stock/gambas-mono/128/delete.png | Bin 0 -> 2037 bytes .../gambas-mono/128/directory.png | Bin 0 -> 162 bytes .../gambas-mono/128/download.png | Bin 0 -> 4843 bytes .../gb.form.stock/gambas-mono/128/error.png | Bin 0 -> 1208 bytes .../gb.form.stock/gambas-mono/128/exec.png | Bin 0 -> 1603 bytes .../gb.form.stock/gambas-mono/128/file.png | Bin 0 -> 218 bytes .../src/gb.form.stock/gambas-mono/128/fog.png | Bin 0 -> 1053 bytes .../gambas-mono/128/folder-document.png | Bin 0 -> 212 bytes .../gambas-mono/128/folder-download.png | Bin 0 -> 376 bytes .../gambas-mono/128/folder-home.png | Bin 0 -> 300 bytes .../gambas-mono/128/folder-image.png | Bin 0 -> 380 bytes .../gambas-mono/128/folder-music.png | Bin 0 -> 551 bytes .../gambas-mono/128/folder-network.png | Bin 0 -> 387 bytes .../gambas-mono/128/folder-recent.png | Bin 0 -> 1015 bytes .../gambas-mono/128/folder-remote.png | Bin 0 -> 418 bytes .../gambas-mono/128/folder-root.png | Bin 0 -> 379 bytes .../gambas-mono/128/folder-share.png | Bin 0 -> 639 bytes .../gambas-mono/128/folder-template.png | Bin 0 -> 730 bytes .../gambas-mono/128/folder-video.png | Bin 0 -> 246 bytes .../gb.form.stock/gambas-mono/128/gambas.png | Bin 0 -> 2729 bytes comp/src/gb.form.stock/gambas-mono/128/h.png | Bin 0 -> 645 bytes .../gb.form.stock/gambas-mono/128/help.png | Bin 0 -> 2635 bytes .../gb.form.stock/gambas-mono/128/html.png | Bin 0 -> 1381 bytes .../gb.form.stock/gambas-mono/128/image.png | Bin 0 -> 653 bytes .../gb.form.stock/gambas-mono/128/info.png | Bin 0 -> 285 bytes .../gambas-mono/128/internet.png | Bin 0 -> 2745 bytes .../gb.form.stock/gambas-mono/128/java.png | Bin 0 -> 597 bytes comp/src/gb.form.stock/gambas-mono/128/js.png | Bin 0 -> 1076 bytes .../gb.form.stock/gambas-mono/128/json.png | Bin 0 -> 1180 bytes .../gb.form.stock/gambas-mono/128/layout.png | Bin 0 -> 906 bytes .../gb.form.stock/gambas-mono/128/linux.png | Bin 0 -> 3004 bytes .../gb.form.stock/gambas-mono/128/magnet.png | Bin 0 -> 1060 bytes .../src/gb.form.stock/gambas-mono/128/map.png | Bin 0 -> 2741 bytes .../gb.form.stock/gambas-mono/128/marker.png | Bin 0 -> 1529 bytes comp/src/gb.form.stock/gambas-mono/128/md.png | Bin 0 -> 816 bytes .../gb.form.stock/gambas-mono/128/memory.png | Bin 0 -> 182 bytes .../gb.form.stock/gambas-mono/128/message.png | Bin 0 -> 2405 bytes .../gb.form.stock/gambas-mono/128/modem.png | Bin 0 -> 490 bytes .../gb.form.stock/gambas-mono/128/money.png | Bin 0 -> 1379 bytes .../gambas-mono/128/moon-cloud.png | Bin 0 -> 1871 bytes .../gb.form.stock/gambas-mono/128/moon.png | Bin 0 -> 1338 bytes .../gambas-mono/128/multimedia.png | Bin 0 -> 824 bytes .../gb.form.stock/gambas-mono/128/network.png | Bin 0 -> 594 bytes .../gb.form.stock/gambas-mono/128/news.png | Bin 0 -> 495 bytes .../src/gb.form.stock/gambas-mono/128/pda.png | Bin 0 -> 446 bytes .../gb.form.stock/gambas-mono/128/people.png | Bin 0 -> 2047 bytes .../gb.form.stock/gambas-mono/128/phone.png | Bin 0 -> 318 bytes .../gb.form.stock/gambas-mono/128/printer.png | Bin 0 -> 424 bytes .../gb.form.stock/gambas-mono/128/program.png | Bin 0 -> 1169 bytes .../gambas-mono/128/question.png | Bin 0 -> 2633 bytes .../gb.form.stock/gambas-mono/128/rain.png | Bin 0 -> 1177 bytes .../gb.form.stock/gambas-mono/128/script.png | Bin 0 -> 1038 bytes .../gb.form.stock/gambas-mono/128/sdcard.png | Bin 0 -> 220 bytes .../gb.form.stock/gambas-mono/128/share.png | Bin 0 -> 832 bytes .../gambas-mono/128/smartphone.png | Bin 0 -> 377 bytes .../gb.form.stock/gambas-mono/128/snow.png | Bin 0 -> 1263 bytes .../gb.form.stock/gambas-mono/128/sport.png | Bin 0 -> 3747 bytes .../gambas-mono/128/statistics.png | Bin 0 -> 754 bytes .../gb.form.stock/gambas-mono/128/storm.png | Bin 0 -> 1447 bytes .../gambas-mono/128/sun-cloud.png | Bin 0 -> 1789 bytes .../src/gb.form.stock/gambas-mono/128/sun.png | Bin 0 -> 1336 bytes .../gb.form.stock/gambas-mono/128/taxi.png | Bin 0 -> 849 bytes .../gambas-mono/128/template.png | Bin 0 -> 551 bytes .../gb.form.stock/gambas-mono/128/text.png | Bin 0 -> 231 bytes .../gb.form.stock/gambas-mono/128/traffic.png | Bin 0 -> 817 bytes .../gb.form.stock/gambas-mono/128/truck.png | Bin 0 -> 866 bytes .../gb.form.stock/gambas-mono/128/upload.png | Bin 0 -> 4793 bytes .../gambas-mono/128/user-group.png | Bin 0 -> 2442 bytes .../gb.form.stock/gambas-mono/128/user.png | Bin 0 -> 1667 bytes .../gb.form.stock/gambas-mono/128/vector.png | Bin 0 -> 806 bytes .../gb.form.stock/gambas-mono/128/video.png | Bin 0 -> 302 bytes .../gb.form.stock/gambas-mono/128/warning.png | Bin 0 -> 828 bytes .../src/gb.form.stock/gambas-mono/128/xml.png | Bin 0 -> 1547 bytes .../gb.form.stock/gambas-mono/32/access.png | Bin 0 -> 201 bytes comp/src/gb.form.stock/gambas-mono/32/add.png | Bin 0 -> 135 bytes .../gb.form.stock/gambas-mono/32/added.png | Bin 0 -> 469 bytes .../gambas-mono/32/administrator.png | Bin 0 -> 545 bytes .../gb.form.stock/gambas-mono/32/agenda.png | Bin 0 -> 188 bytes .../gb.form.stock/gambas-mono/32/alarm.png | Bin 0 -> 608 bytes .../gambas-mono/32/align-bottom.png | Bin 0 -> 142 bytes .../gambas-mono/32/align-center.png | Bin 0 -> 149 bytes .../gambas-mono/32/align-height.png | Bin 0 -> 143 bytes .../gambas-mono/32/align-left.png | Bin 0 -> 144 bytes .../gambas-mono/32/align-middle.png | Bin 0 -> 144 bytes .../gambas-mono/32/align-right.png | Bin 0 -> 147 bytes .../gambas-mono/32/align-top.png | Bin 0 -> 144 bytes .../gambas-mono/32/align-width.png | Bin 0 -> 144 bytes .../gb.form.stock/gambas-mono/32/anchor.png | Bin 0 -> 632 bytes .../gb.form.stock/gambas-mono/32/android.png | Bin 0 -> 544 bytes .../gambas-mono/32/application.png | Bin 0 -> 140 bytes .../gb.form.stock/gambas-mono/32/apply.png | Bin 0 -> 207 bytes .../gb.form.stock/gambas-mono/32/archive.png | Bin 0 -> 178 bytes .../gb.form.stock/gambas-mono/32/attach.png | Bin 0 -> 751 bytes .../gb.form.stock/gambas-mono/32/audio.png | Bin 0 -> 606 bytes .../gb.form.stock/gambas-mono/32/average.png | Bin 0 -> 608 bytes .../gambas-mono/32/battery-high.png | Bin 0 -> 155 bytes .../gambas-mono/32/battery-low.png | Bin 0 -> 155 bytes .../gambas-mono/32/battery-medium.png | Bin 0 -> 155 bytes .../gb.form.stock/gambas-mono/32/battery.png | Bin 0 -> 249 bytes .../gb.form.stock/gambas-mono/32/bicycle.png | Bin 0 -> 504 bytes .../src/gb.form.stock/gambas-mono/32/blue.png | Bin 0 -> 534 bytes .../gambas-mono/32/bluetooth-off.png | Bin 0 -> 321 bytes .../gambas-mono/32/bluetooth.png | Bin 0 -> 243 bytes .../src/gb.form.stock/gambas-mono/32/blur.png | Bin 0 -> 1699 bytes .../src/gb.form.stock/gambas-mono/32/book.png | Bin 0 -> 170 bytes .../gb.form.stock/gambas-mono/32/bookmark.png | Bin 0 -> 265 bytes .../gambas-mono/32/border-bottom.png | Bin 0 -> 153 bytes .../gambas-mono/32/border-diag.png | Bin 0 -> 315 bytes .../gambas-mono/32/border-horizontal.png | Bin 0 -> 153 bytes .../gambas-mono/32/border-inside.png | Bin 0 -> 153 bytes .../gambas-mono/32/border-left.png | Bin 0 -> 146 bytes .../gambas-mono/32/border-none.png | Bin 0 -> 143 bytes .../gambas-mono/32/border-outside.png | Bin 0 -> 153 bytes .../gambas-mono/32/border-rev-diag.png | Bin 0 -> 311 bytes .../gambas-mono/32/border-right.png | Bin 0 -> 146 bytes .../gambas-mono/32/border-top.png | Bin 0 -> 153 bytes .../gambas-mono/32/border-vertical.png | Bin 0 -> 156 bytes .../gb.form.stock/gambas-mono/32/border.png | Bin 0 -> 143 bytes .../gb.form.stock/gambas-mono/32/bottom.png | Bin 0 -> 233 bytes .../gambas-mono/32/brightness.png | Bin 0 -> 353 bytes .../gb.form.stock/gambas-mono/32/brush.png | Bin 0 -> 334 bytes comp/src/gb.form.stock/gambas-mono/32/bus.png | Bin 0 -> 365 bytes comp/src/gb.form.stock/gambas-mono/32/c.png | Bin 0 -> 443 bytes .../src/gb.form.stock/gambas-mono/32/cake.png | Bin 0 -> 347 bytes .../gambas-mono/32/calculator.png | Bin 0 -> 170 bytes .../gb.form.stock/gambas-mono/32/calendar.png | Bin 0 -> 189 bytes .../gambas-mono/32/call-stop.png | Bin 0 -> 337 bytes .../src/gb.form.stock/gambas-mono/32/call.png | Bin 0 -> 602 bytes .../gb.form.stock/gambas-mono/32/camera.png | Bin 0 -> 249 bytes .../gb.form.stock/gambas-mono/32/cancel.png | Bin 0 -> 305 bytes .../gb.form.stock/gambas-mono/32/cap-butt.png | Bin 0 -> 136 bytes .../gambas-mono/32/cap-round.png | Bin 0 -> 328 bytes .../gambas-mono/32/cap-square.png | Bin 0 -> 134 bytes comp/src/gb.form.stock/gambas-mono/32/car.png | Bin 0 -> 327 bytes .../gb.form.stock/gambas-mono/32/cdrom.png | Bin 0 -> 428 bytes .../gambas-mono/32/chart-line.png | Bin 0 -> 334 bytes .../gambas-mono/32/chart-pie.png | Bin 0 -> 724 bytes .../gb.form.stock/gambas-mono/32/chart.png | Bin 0 -> 147 bytes .../src/gb.form.stock/gambas-mono/32/chat.png | Bin 0 -> 533 bytes .../gb.form.stock/gambas-mono/32/cherry.png | Bin 0 -> 655 bytes .../src/gb.form.stock/gambas-mono/32/city.png | Bin 0 -> 223 bytes .../gb.form.stock/gambas-mono/32/clear.png | Bin 0 -> 317 bytes .../gb.form.stock/gambas-mono/32/clock.png | Bin 0 -> 487 bytes .../gb.form.stock/gambas-mono/32/clone.png | Bin 0 -> 231 bytes .../gb.form.stock/gambas-mono/32/close.png | Bin 0 -> 281 bytes .../gb.form.stock/gambas-mono/32/cloud.png | Bin 0 -> 475 bytes .../gambas-mono/32/color-picker.png | Bin 0 -> 253 bytes .../gb.form.stock/gambas-mono/32/color.png | Bin 0 -> 292 bytes .../gambas-mono/32/column-after.png | Bin 0 -> 325 bytes .../gambas-mono/32/column-before.png | Bin 0 -> 306 bytes .../gambas-mono/32/column-remove.png | Bin 0 -> 353 bytes .../gb.form.stock/gambas-mono/32/compass.png | Bin 0 -> 557 bytes .../gambas-mono/32/component.png | Bin 0 -> 144 bytes .../gb.form.stock/gambas-mono/32/compress.png | Bin 0 -> 328 bytes .../gb.form.stock/gambas-mono/32/computer.png | Bin 0 -> 305 bytes .../gb.form.stock/gambas-mono/32/conflict.png | Bin 0 -> 544 bytes .../gb.form.stock/gambas-mono/32/connect.png | Bin 0 -> 276 bytes .../gb.form.stock/gambas-mono/32/contrast.png | Bin 0 -> 430 bytes .../gb.form.stock/gambas-mono/32/cookie.png | Bin 0 -> 475 bytes .../src/gb.form.stock/gambas-mono/32/copy.png | Bin 0 -> 160 bytes comp/src/gb.form.stock/gambas-mono/32/cpp.png | Bin 0 -> 436 bytes comp/src/gb.form.stock/gambas-mono/32/cpu.png | Bin 0 -> 174 bytes .../src/gb.form.stock/gambas-mono/32/crop.png | Bin 0 -> 154 bytes comp/src/gb.form.stock/gambas-mono/32/css.png | Bin 0 -> 617 bytes comp/src/gb.form.stock/gambas-mono/32/csv.png | Bin 0 -> 184 bytes .../gb.form.stock/gambas-mono/32/currency.png | Bin 0 -> 712 bytes comp/src/gb.form.stock/gambas-mono/32/cut.png | Bin 0 -> 315 bytes .../gb.form.stock/gambas-mono/32/database.png | Bin 0 -> 440 bytes .../gambas-mono/32/debug-cursor.png | Bin 0 -> 240 bytes .../gambas-mono/32/debug-into.png | Bin 0 -> 181 bytes .../gambas-mono/32/debug-out.png | Bin 0 -> 182 bytes .../gambas-mono/32/debug-over.png | Bin 0 -> 197 bytes .../gb.form.stock/gambas-mono/32/delete.png | Bin 0 -> 482 bytes .../gb.form.stock/gambas-mono/32/desktop.png | Bin 0 -> 162 bytes .../gambas-mono/32/development.png | Bin 0 -> 242 bytes .../gambas-mono/32/difference.png | Bin 0 -> 173 bytes .../gambas-mono/32/directory.png | Bin 0 -> 151 bytes .../gambas-mono/32/disconnect.png | Bin 0 -> 281 bytes .../src/gb.form.stock/gambas-mono/32/down.png | Bin 0 -> 219 bytes .../gb.form.stock/gambas-mono/32/download.png | Bin 0 -> 990 bytes .../gambas-mono/32/draw-arrow.png | Bin 0 -> 343 bytes .../gambas-mono/32/draw-circle.png | Bin 0 -> 458 bytes .../gambas-mono/32/draw-line.png | Bin 0 -> 173 bytes .../gambas-mono/32/draw-path.png | Bin 0 -> 391 bytes .../gambas-mono/32/draw-polygon.png | Bin 0 -> 748 bytes .../gambas-mono/32/draw-rectangle.png | Bin 0 -> 136 bytes .../gambas-mono/32/draw-round.png | Bin 0 -> 321 bytes .../gambas-mono/32/draw-star.png | Bin 0 -> 816 bytes .../gambas-mono/32/draw-text.png | Bin 0 -> 266 bytes .../gb.form.stock/gambas-mono/32/drink.png | Bin 0 -> 256 bytes .../gb.form.stock/gambas-mono/32/earth.png | Bin 0 -> 635 bytes .../src/gb.form.stock/gambas-mono/32/edit.png | Bin 0 -> 221 bytes .../gb.form.stock/gambas-mono/32/eject.png | Bin 0 -> 190 bytes .../gambas-mono/32/emote-cool.png | Bin 0 -> 498 bytes .../gambas-mono/32/emote-crying.png | Bin 0 -> 507 bytes .../gambas-mono/32/emote-happy.png | Bin 0 -> 461 bytes .../gambas-mono/32/emote-kiss.png | Bin 0 -> 423 bytes .../gambas-mono/32/emote-laugh.png | Bin 0 -> 429 bytes .../gambas-mono/32/emote-plain.png | Bin 0 -> 379 bytes .../gambas-mono/32/emote-raspberry.png | Bin 0 -> 406 bytes .../gambas-mono/32/emote-sad.png | Bin 0 -> 461 bytes .../gambas-mono/32/emote-surprise.png | Bin 0 -> 481 bytes .../gambas-mono/32/emote-uncertain.png | Bin 0 -> 457 bytes .../gambas-mono/32/emote-wink.png | Bin 0 -> 462 bytes .../gambas-mono/32/end-of-line.png | Bin 0 -> 186 bytes comp/src/gb.form.stock/gambas-mono/32/end.png | Bin 0 -> 181 bytes .../gb.form.stock/gambas-mono/32/energy.png | Bin 0 -> 304 bytes .../gb.form.stock/gambas-mono/32/erase.png | Bin 0 -> 225 bytes .../gb.form.stock/gambas-mono/32/error.png | Bin 0 -> 357 bytes .../gambas-mono/32/exclusive.png | Bin 0 -> 156 bytes .../src/gb.form.stock/gambas-mono/32/exec.png | Bin 0 -> 442 bytes .../gb.form.stock/gambas-mono/32/export.png | Bin 0 -> 197 bytes .../gb.form.stock/gambas-mono/32/factory.png | Bin 0 -> 246 bytes .../gambas-mono/32/file-manager.png | Bin 0 -> 161 bytes .../src/gb.form.stock/gambas-mono/32/file.png | Bin 0 -> 163 bytes .../src/gb.form.stock/gambas-mono/32/fill.png | Bin 0 -> 407 bytes .../gb.form.stock/gambas-mono/32/filter.png | Bin 0 -> 189 bytes .../src/gb.form.stock/gambas-mono/32/find.png | Bin 0 -> 402 bytes .../gb.form.stock/gambas-mono/32/firewall.png | Bin 0 -> 380 bytes .../gb.form.stock/gambas-mono/32/first.png | Bin 0 -> 214 bytes .../gambas-mono/32/flag-blue.png | Bin 0 -> 364 bytes .../gambas-mono/32/flag-green.png | Bin 0 -> 397 bytes .../gb.form.stock/gambas-mono/32/flag-red.png | Bin 0 -> 348 bytes .../gambas-mono/32/flag-yellow.png | Bin 0 -> 364 bytes .../src/gb.form.stock/gambas-mono/32/flag.png | Bin 0 -> 197 bytes .../gb.form.stock/gambas-mono/32/flip-h.png | Bin 0 -> 198 bytes .../gb.form.stock/gambas-mono/32/flip-v.png | Bin 0 -> 226 bytes .../gb.form.stock/gambas-mono/32/floppy.png | Bin 0 -> 184 bytes comp/src/gb.form.stock/gambas-mono/32/fog.png | Bin 0 -> 334 bytes .../gambas-mono/32/folder-blue.png | Bin 0 -> 314 bytes .../gambas-mono/32/folder-document.png | Bin 0 -> 173 bytes .../gambas-mono/32/folder-download.png | Bin 0 -> 207 bytes .../gambas-mono/32/folder-green.png | Bin 0 -> 363 bytes .../gambas-mono/32/folder-home.png | Bin 0 -> 192 bytes .../gambas-mono/32/folder-image.png | Bin 0 -> 205 bytes .../gambas-mono/32/folder-music.png | Bin 0 -> 244 bytes .../gambas-mono/32/folder-network.png | Bin 0 -> 214 bytes .../gambas-mono/32/folder-recent.png | Bin 0 -> 291 bytes .../gambas-mono/32/folder-red.png | Bin 0 -> 300 bytes .../gambas-mono/32/folder-remote.png | Bin 0 -> 187 bytes .../gambas-mono/32/folder-root.png | Bin 0 -> 208 bytes .../gambas-mono/32/folder-share.png | Bin 0 -> 260 bytes .../gambas-mono/32/folder-template.png | Bin 0 -> 730 bytes .../gambas-mono/32/folder-video.png | Bin 0 -> 216 bytes .../gambas-mono/32/folder-yellow.png | Bin 0 -> 279 bytes .../src/gb.form.stock/gambas-mono/32/font.png | Bin 0 -> 834 bytes .../gb.form.stock/gambas-mono/32/formula.png | Bin 0 -> 848 bytes .../gb.form.stock/gambas-mono/32/forward.png | Bin 0 -> 205 bytes .../gambas-mono/32/fullscreen.png | Bin 0 -> 159 bytes .../gb.form.stock/gambas-mono/32/gambas.png | Bin 0 -> 935 bytes .../src/gb.form.stock/gambas-mono/32/game.png | Bin 0 -> 223 bytes .../gb.form.stock/gambas-mono/32/gamma.png | Bin 0 -> 373 bytes comp/src/gb.form.stock/gambas-mono/32/gnu.png | Bin 0 -> 889 bytes .../gb.form.stock/gambas-mono/32/gradient.png | Bin 0 -> 278 bytes .../gb.form.stock/gambas-mono/32/green.png | Bin 0 -> 582 bytes .../src/gb.form.stock/gambas-mono/32/grid.png | Bin 0 -> 154 bytes .../gb.form.stock/gambas-mono/32/group.png | Bin 0 -> 311 bytes comp/src/gb.form.stock/gambas-mono/32/h.png | Bin 0 -> 302 bytes .../src/gb.form.stock/gambas-mono/32/halt.png | Bin 0 -> 404 bytes .../gambas-mono/32/harddisk-root.png | Bin 0 -> 191 bytes .../gb.form.stock/gambas-mono/32/harddisk.png | Bin 0 -> 147 bytes .../gb.form.stock/gambas-mono/32/hardware.png | Bin 0 -> 167 bytes .../gb.form.stock/gambas-mono/32/hatch.png | Bin 0 -> 206 bytes .../gb.form.stock/gambas-mono/32/headset.png | Bin 0 -> 529 bytes .../src/gb.form.stock/gambas-mono/32/help.png | Bin 0 -> 649 bytes .../gambas-mono/32/hibernate.png | Bin 0 -> 520 bytes .../gambas-mono/32/highlight.png | Bin 0 -> 223 bytes .../src/gb.form.stock/gambas-mono/32/home.png | Bin 0 -> 194 bytes .../gb.form.stock/gambas-mono/32/house.png | Bin 0 -> 189 bytes .../src/gb.form.stock/gambas-mono/32/html.png | Bin 0 -> 416 bytes comp/src/gb.form.stock/gambas-mono/32/hue.png | Bin 0 -> 456 bytes .../gb.form.stock/gambas-mono/32/identity.png | Bin 0 -> 626 bytes .../gb.form.stock/gambas-mono/32/image.png | Bin 0 -> 243 bytes .../gb.form.stock/gambas-mono/32/import.png | Bin 0 -> 200 bytes .../gambas-mono/32/important.png | Bin 0 -> 396 bytes .../gb.form.stock/gambas-mono/32/indent.png | Bin 0 -> 191 bytes .../src/gb.form.stock/gambas-mono/32/info.png | Bin 0 -> 165 bytes .../gambas-mono/32/inheritance.png | Bin 0 -> 220 bytes .../gambas-mono/32/insert-image.png | Bin 0 -> 414 bytes .../gambas-mono/32/insert-link.png | Bin 0 -> 633 bytes .../gambas-mono/32/insert-table.png | Bin 0 -> 360 bytes .../gambas-mono/32/insert-text.png | Bin 0 -> 354 bytes .../gb.form.stock/gambas-mono/32/internet.png | Bin 0 -> 1132 bytes .../gambas-mono/32/intersection.png | Bin 0 -> 164 bytes .../gb.form.stock/gambas-mono/32/invert.png | Bin 0 -> 243 bytes .../src/gb.form.stock/gambas-mono/32/java.png | Bin 0 -> 234 bytes .../gambas-mono/32/join-bevel.png | Bin 0 -> 176 bytes .../gambas-mono/32/join-miter.png | Bin 0 -> 135 bytes .../gambas-mono/32/join-round.png | Bin 0 -> 417 bytes comp/src/gb.form.stock/gambas-mono/32/js.png | Bin 0 -> 421 bytes .../src/gb.form.stock/gambas-mono/32/json.png | Bin 0 -> 450 bytes .../src/gb.form.stock/gambas-mono/32/jump.png | Bin 0 -> 296 bytes comp/src/gb.form.stock/gambas-mono/32/key.png | Bin 0 -> 290 bytes .../gb.form.stock/gambas-mono/32/keyboard.png | Bin 0 -> 283 bytes .../src/gb.form.stock/gambas-mono/32/lamp.png | Bin 0 -> 372 bytes .../gb.form.stock/gambas-mono/32/language.png | Bin 0 -> 354 bytes .../src/gb.form.stock/gambas-mono/32/last.png | Bin 0 -> 212 bytes .../gb.form.stock/gambas-mono/32/layer.png | Bin 0 -> 506 bytes .../gb.form.stock/gambas-mono/32/layout.png | Bin 0 -> 390 bytes .../src/gb.form.stock/gambas-mono/32/left.png | Bin 0 -> 183 bytes .../gb.form.stock/gambas-mono/32/library.png | Bin 0 -> 192 bytes .../gambas-mono/32/lightness.png | Bin 0 -> 391 bytes .../gambas-mono/32/line-style.png | Bin 0 -> 142 bytes .../gambas-mono/32/line-width.png | Bin 0 -> 135 bytes .../src/gb.form.stock/gambas-mono/32/link.png | Bin 0 -> 489 bytes .../gb.form.stock/gambas-mono/32/linux.png | Bin 0 -> 1311 bytes .../gambas-mono/32/lock-screen.png | Bin 0 -> 460 bytes .../src/gb.form.stock/gambas-mono/32/lock.png | Bin 0 -> 270 bytes .../gb.form.stock/gambas-mono/32/logout.png | Bin 0 -> 425 bytes .../src/gb.form.stock/gambas-mono/32/love.png | Bin 0 -> 614 bytes .../gb.form.stock/gambas-mono/32/lower.png | Bin 0 -> 145 bytes .../gb.form.stock/gambas-mono/32/magnet.png | Bin 0 -> 1060 bytes .../src/gb.form.stock/gambas-mono/32/mail.png | Bin 0 -> 250 bytes .../gb.form.stock/gambas-mono/32/make-all.png | Bin 0 -> 248 bytes .../src/gb.form.stock/gambas-mono/32/make.png | Bin 0 -> 252 bytes comp/src/gb.form.stock/gambas-mono/32/map.png | Bin 0 -> 632 bytes .../gb.form.stock/gambas-mono/32/marker.png | Bin 0 -> 494 bytes .../src/gb.form.stock/gambas-mono/32/math.png | Bin 0 -> 861 bytes comp/src/gb.form.stock/gambas-mono/32/md.png | Bin 0 -> 356 bytes .../gambas-mono/32/media-player.png | Bin 0 -> 179 bytes .../gb.form.stock/gambas-mono/32/memory.png | Bin 0 -> 166 bytes .../src/gb.form.stock/gambas-mono/32/menu.png | Bin 0 -> 131 bytes .../gb.form.stock/gambas-mono/32/message.png | Bin 0 -> 636 bytes .../gambas-mono/32/microphone-off.png | Bin 0 -> 442 bytes .../gambas-mono/32/microphone.png | Bin 0 -> 371 bytes .../gb.form.stock/gambas-mono/32/modem.png | Bin 0 -> 188 bytes .../gb.form.stock/gambas-mono/32/modified.png | Bin 0 -> 523 bytes .../gb.form.stock/gambas-mono/32/money.png | Bin 0 -> 367 bytes .../gb.form.stock/gambas-mono/32/monitor.png | Bin 0 -> 536 bytes .../gambas-mono/32/moon-cloud.png | Bin 0 -> 547 bytes .../src/gb.form.stock/gambas-mono/32/moon.png | Bin 0 -> 386 bytes .../gb.form.stock/gambas-mono/32/more-h.png | Bin 0 -> 148 bytes .../src/gb.form.stock/gambas-mono/32/more.png | Bin 0 -> 143 bytes .../gb.form.stock/gambas-mono/32/mount.png | Bin 0 -> 185 bytes .../gb.form.stock/gambas-mono/32/mouse.png | Bin 0 -> 273 bytes .../src/gb.form.stock/gambas-mono/32/move.png | Bin 0 -> 323 bytes .../gambas-mono/32/multimedia.png | Bin 0 -> 268 bytes .../gb.form.stock/gambas-mono/32/muted.png | Bin 0 -> 340 bytes .../gb.form.stock/gambas-mono/32/network.png | Bin 0 -> 206 bytes .../gambas-mono/32/new-appointment.png | Bin 0 -> 653 bytes .../gb.form.stock/gambas-mono/32/new-dir.png | Bin 0 -> 346 bytes .../gb.form.stock/gambas-mono/32/new-mail.png | Bin 0 -> 436 bytes .../gb.form.stock/gambas-mono/32/new-tab.png | Bin 0 -> 355 bytes .../gb.form.stock/gambas-mono/32/new-tag.png | Bin 0 -> 422 bytes .../gb.form.stock/gambas-mono/32/new-user.png | Bin 0 -> 597 bytes .../gambas-mono/32/new-window.png | Bin 0 -> 355 bytes comp/src/gb.form.stock/gambas-mono/32/new.png | Bin 0 -> 350 bytes .../src/gb.form.stock/gambas-mono/32/news.png | Bin 0 -> 198 bytes .../src/gb.form.stock/gambas-mono/32/next.png | Bin 0 -> 194 bytes .../gambas-mono/32/notification.png | Bin 0 -> 493 bytes .../gambas-mono/32/office-calc.png | Bin 0 -> 184 bytes .../gambas-mono/32/office-draw.png | Bin 0 -> 232 bytes .../gambas-mono/32/office-math.png | Bin 0 -> 444 bytes .../gambas-mono/32/office-presentation.png | Bin 0 -> 261 bytes .../gb.form.stock/gambas-mono/32/office.png | Bin 0 -> 186 bytes comp/src/gb.form.stock/gambas-mono/32/ok.png | Bin 0 -> 207 bytes .../gb.form.stock/gambas-mono/32/opacity.png | Bin 0 -> 450 bytes .../gambas-mono/32/open-link.png | Bin 0 -> 298 bytes .../gambas-mono/32/open-recent.png | Bin 0 -> 167 bytes .../src/gb.form.stock/gambas-mono/32/open.png | Bin 0 -> 209 bytes .../gb.form.stock/gambas-mono/32/options.png | Bin 0 -> 351 bytes .../gb.form.stock/gambas-mono/32/package.png | Bin 0 -> 200 bytes .../gambas-mono/32/page-break.png | Bin 0 -> 175 bytes .../gb.form.stock/gambas-mono/32/page-two.png | Bin 0 -> 182 bytes .../src/gb.form.stock/gambas-mono/32/page.png | Bin 0 -> 164 bytes .../gambas-mono/32/paragraph.png | Bin 0 -> 204 bytes .../gambas-mono/32/paste-special.png | Bin 0 -> 289 bytes .../gb.form.stock/gambas-mono/32/paste.png | Bin 0 -> 149 bytes .../gb.form.stock/gambas-mono/32/pause.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas-mono/32/pda.png | Bin 0 -> 212 bytes comp/src/gb.form.stock/gambas-mono/32/pdf.png | Bin 0 -> 492 bytes comp/src/gb.form.stock/gambas-mono/32/pen.png | Bin 0 -> 201 bytes .../gb.form.stock/gambas-mono/32/people.png | Bin 0 -> 961 bytes .../gb.form.stock/gambas-mono/32/percent.png | Bin 0 -> 947 bytes .../gb.form.stock/gambas-mono/32/phone.png | Bin 0 -> 188 bytes comp/src/gb.form.stock/gambas-mono/32/pin.png | Bin 0 -> 537 bytes .../gb.form.stock/gambas-mono/32/plane.png | Bin 0 -> 625 bytes .../src/gb.form.stock/gambas-mono/32/play.png | Bin 0 -> 184 bytes .../gb.form.stock/gambas-mono/32/plugin.png | Bin 0 -> 253 bytes .../gb.form.stock/gambas-mono/32/pointer.png | Bin 0 -> 649 bytes .../gb.form.stock/gambas-mono/32/preview.png | Bin 0 -> 627 bytes .../gb.form.stock/gambas-mono/32/previous.png | Bin 0 -> 201 bytes .../gb.form.stock/gambas-mono/32/print.png | Bin 0 -> 173 bytes .../gb.form.stock/gambas-mono/32/printer.png | Bin 0 -> 260 bytes .../gb.form.stock/gambas-mono/32/program.png | Bin 0 -> 372 bytes .../gambas-mono/32/properties.png | Bin 0 -> 143 bytes .../gb.form.stock/gambas-mono/32/question.png | Bin 0 -> 677 bytes .../src/gb.form.stock/gambas-mono/32/quit.png | Bin 0 -> 373 bytes .../gb.form.stock/gambas-mono/32/quote.png | Bin 0 -> 210 bytes .../src/gb.form.stock/gambas-mono/32/rain.png | Bin 0 -> 384 bytes .../gb.form.stock/gambas-mono/32/raise.png | Bin 0 -> 158 bytes .../gb.form.stock/gambas-mono/32/record.png | Bin 0 -> 321 bytes comp/src/gb.form.stock/gambas-mono/32/red.png | Bin 0 -> 502 bytes .../src/gb.form.stock/gambas-mono/32/redo.png | Bin 0 -> 311 bytes .../gb.form.stock/gambas-mono/32/refresh.png | Bin 0 -> 440 bytes .../gb.form.stock/gambas-mono/32/remove.png | Bin 0 -> 129 bytes .../gb.form.stock/gambas-mono/32/rename.png | Bin 0 -> 208 bytes .../gb.form.stock/gambas-mono/32/repeat.png | Bin 0 -> 234 bytes .../gb.form.stock/gambas-mono/32/replace.png | Bin 0 -> 408 bytes .../gambas-mono/32/reply-all.png | Bin 0 -> 336 bytes .../gb.form.stock/gambas-mono/32/reply.png | Bin 0 -> 321 bytes .../gb.form.stock/gambas-mono/32/resize.png | Bin 0 -> 151 bytes .../gb.form.stock/gambas-mono/32/restart.png | Bin 0 -> 442 bytes .../gambas-mono/32/restaurant.png | Bin 0 -> 724 bytes .../gb.form.stock/gambas-mono/32/revert.png | Bin 0 -> 238 bytes .../gb.form.stock/gambas-mono/32/rewind.png | Bin 0 -> 203 bytes .../gb.form.stock/gambas-mono/32/right.png | Bin 0 -> 184 bytes .../gambas-mono/32/rotate-left.png | Bin 0 -> 200 bytes .../gambas-mono/32/rotate-right.png | Bin 0 -> 201 bytes .../gb.form.stock/gambas-mono/32/rotate.png | Bin 0 -> 380 bytes .../gambas-mono/32/row-after.png | Bin 0 -> 326 bytes .../gambas-mono/32/row-before.png | Bin 0 -> 305 bytes .../gambas-mono/32/row-remove.png | Bin 0 -> 352 bytes comp/src/gb.form.stock/gambas-mono/32/rss.png | Bin 0 -> 529 bytes .../gambas-mono/32/saturation.png | Bin 0 -> 446 bytes .../gb.form.stock/gambas-mono/32/save-as.png | Bin 0 -> 216 bytes .../src/gb.form.stock/gambas-mono/32/save.png | Bin 0 -> 207 bytes .../gb.form.stock/gambas-mono/32/scanner.png | Bin 0 -> 483 bytes .../gb.form.stock/gambas-mono/32/science.png | Bin 0 -> 370 bytes .../gb.form.stock/gambas-mono/32/screen.png | Bin 0 -> 177 bytes .../gb.form.stock/gambas-mono/32/script.png | Bin 0 -> 446 bytes .../gb.form.stock/gambas-mono/32/sdcard.png | Bin 0 -> 173 bytes .../gambas-mono/32/security-high.png | Bin 0 -> 815 bytes .../gambas-mono/32/security-low.png | Bin 0 -> 681 bytes .../gambas-mono/32/security-medium.png | Bin 0 -> 788 bytes .../gb.form.stock/gambas-mono/32/security.png | Bin 0 -> 719 bytes .../gambas-mono/32/select-all.png | Bin 0 -> 217 bytes .../gb.form.stock/gambas-mono/32/select.png | Bin 0 -> 198 bytes .../gb.form.stock/gambas-mono/32/server.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas-mono/32/share.png | Bin 0 -> 267 bytes .../gb.form.stock/gambas-mono/32/shopping.png | Bin 0 -> 352 bytes .../gb.form.stock/gambas-mono/32/shortcut.png | Bin 0 -> 576 bytes .../gb.form.stock/gambas-mono/32/shuffle.png | Bin 0 -> 353 bytes .../gb.form.stock/gambas-mono/32/shutdown.png | Bin 0 -> 443 bytes .../gambas-mono/32/smartphone.png | Bin 0 -> 177 bytes .../src/gb.form.stock/gambas-mono/32/snow.png | Bin 0 -> 373 bytes .../gambas-mono/32/sort-ascent.png | Bin 0 -> 249 bytes .../gambas-mono/32/sort-descent.png | Bin 0 -> 256 bytes .../gb.form.stock/gambas-mono/32/speaker.png | Bin 0 -> 418 bytes .../gambas-mono/32/spell-check.png | Bin 0 -> 707 bytes .../gb.form.stock/gambas-mono/32/sport.png | Bin 0 -> 1005 bytes .../src/gb.form.stock/gambas-mono/32/star.png | Bin 0 -> 897 bytes .../gb.form.stock/gambas-mono/32/start.png | Bin 0 -> 185 bytes .../gambas-mono/32/statistics.png | Bin 0 -> 314 bytes .../src/gb.form.stock/gambas-mono/32/stop.png | Bin 0 -> 137 bytes .../gb.form.stock/gambas-mono/32/storm.png | Bin 0 -> 461 bytes .../gb.form.stock/gambas-mono/32/subtitle.png | Bin 0 -> 166 bytes comp/src/gb.form.stock/gambas-mono/32/sum.png | Bin 0 -> 549 bytes .../gambas-mono/32/sun-cloud.png | Bin 0 -> 537 bytes comp/src/gb.form.stock/gambas-mono/32/sun.png | Bin 0 -> 420 bytes .../gb.form.stock/gambas-mono/32/suspend.png | Bin 0 -> 366 bytes .../gambas-mono/32/switch-user.png | Bin 0 -> 444 bytes .../gb.form.stock/gambas-mono/32/system.png | Bin 0 -> 283 bytes comp/src/gb.form.stock/gambas-mono/32/tab.png | Bin 0 -> 206 bytes .../gb.form.stock/gambas-mono/32/table.png | Bin 0 -> 151 bytes .../gb.form.stock/gambas-mono/32/tablet.png | Bin 0 -> 268 bytes comp/src/gb.form.stock/gambas-mono/32/tag.png | Bin 0 -> 265 bytes .../src/gb.form.stock/gambas-mono/32/taxi.png | Bin 0 -> 338 bytes .../gb.form.stock/gambas-mono/32/template.png | Bin 0 -> 255 bytes .../gb.form.stock/gambas-mono/32/terminal.png | Bin 0 -> 198 bytes .../gambas-mono/32/text-baseline.png | Bin 0 -> 173 bytes .../gambas-mono/32/text-bigger.png | Bin 0 -> 818 bytes .../gambas-mono/32/text-bold.png | Bin 0 -> 727 bytes .../gambas-mono/32/text-bottom.png | Bin 0 -> 173 bytes .../gambas-mono/32/text-center.png | Bin 0 -> 136 bytes .../gambas-mono/32/text-fill.png | Bin 0 -> 130 bytes .../gambas-mono/32/text-italic.png | Bin 0 -> 734 bytes .../gambas-mono/32/text-left.png | Bin 0 -> 134 bytes .../gambas-mono/32/text-line-spacing.png | Bin 0 -> 229 bytes .../gambas-mono/32/text-list-order-rtl.png | Bin 0 -> 365 bytes .../gambas-mono/32/text-list-order.png | Bin 0 -> 367 bytes .../gambas-mono/32/text-list.png | Bin 0 -> 172 bytes .../gambas-mono/32/text-lower.png | Bin 0 -> 908 bytes .../gambas-mono/32/text-middle.png | Bin 0 -> 186 bytes .../gambas-mono/32/text-outline.png | Bin 0 -> 1035 bytes .../gambas-mono/32/text-right.png | Bin 0 -> 136 bytes .../gambas-mono/32/text-shadow.png | Bin 0 -> 1185 bytes .../gambas-mono/32/text-smaller.png | Bin 0 -> 817 bytes .../gambas-mono/32/text-strike.png | Bin 0 -> 693 bytes .../gb.form.stock/gambas-mono/32/text-sub.png | Bin 0 -> 823 bytes .../gambas-mono/32/text-super.png | Bin 0 -> 817 bytes .../gb.form.stock/gambas-mono/32/text-top.png | Bin 0 -> 174 bytes .../gambas-mono/32/text-underline.png | Bin 0 -> 741 bytes .../gambas-mono/32/text-upper.png | Bin 0 -> 777 bytes .../src/gb.form.stock/gambas-mono/32/text.png | Bin 0 -> 177 bytes .../gb.form.stock/gambas-mono/32/theme.png | Bin 0 -> 168 bytes .../gb.form.stock/gambas-mono/32/today.png | Bin 0 -> 223 bytes .../gb.form.stock/gambas-mono/32/toilet.png | Bin 0 -> 697 bytes .../gb.form.stock/gambas-mono/32/tools.png | Bin 0 -> 311 bytes comp/src/gb.form.stock/gambas-mono/32/top.png | Bin 0 -> 232 bytes .../src/gb.form.stock/gambas-mono/32/town.png | Bin 0 -> 234 bytes .../gb.form.stock/gambas-mono/32/traffic.png | Bin 0 -> 251 bytes .../gb.form.stock/gambas-mono/32/train.png | Bin 0 -> 404 bytes .../gambas-mono/32/transparency.png | Bin 0 -> 447 bytes .../gambas-mono/32/trash-full.png | Bin 0 -> 268 bytes .../gb.form.stock/gambas-mono/32/trash.png | Bin 0 -> 191 bytes .../gb.form.stock/gambas-mono/32/truck.png | Bin 0 -> 381 bytes .../gambas-mono/32/uncompress.png | Bin 0 -> 319 bytes .../src/gb.form.stock/gambas-mono/32/undo.png | Bin 0 -> 306 bytes .../gb.form.stock/gambas-mono/32/ungroup.png | Bin 0 -> 321 bytes .../gb.form.stock/gambas-mono/32/unindent.png | Bin 0 -> 192 bytes .../gb.form.stock/gambas-mono/32/union.png | Bin 0 -> 164 bytes .../gb.form.stock/gambas-mono/32/unlock.png | Bin 0 -> 279 bytes .../gb.form.stock/gambas-mono/32/unselect.png | Bin 0 -> 357 bytes comp/src/gb.form.stock/gambas-mono/32/up.png | Bin 0 -> 219 bytes .../gb.form.stock/gambas-mono/32/update.png | Bin 0 -> 385 bytes .../gb.form.stock/gambas-mono/32/upload.png | Bin 0 -> 971 bytes comp/src/gb.form.stock/gambas-mono/32/usb.png | Bin 0 -> 226 bytes .../gambas-mono/32/user-group.png | Bin 0 -> 1166 bytes .../src/gb.form.stock/gambas-mono/32/user.png | Bin 0 -> 499 bytes .../gb.form.stock/gambas-mono/32/vector.png | Bin 0 -> 271 bytes .../gambas-mono/32/version-control.png | Bin 0 -> 338 bytes .../gb.form.stock/gambas-mono/32/video.png | Bin 0 -> 199 bytes .../gambas-mono/32/view-compact.png | Bin 0 -> 140 bytes .../gambas-mono/32/view-detail.png | Bin 0 -> 130 bytes .../gambas-mono/32/view-icon.png | Bin 0 -> 131 bytes .../gambas-mono/32/view-normal.png | Bin 0 -> 299 bytes .../gambas-mono/32/view-preview.png | Bin 0 -> 242 bytes .../gambas-mono/32/view-split-h.png | Bin 0 -> 151 bytes .../gambas-mono/32/view-split-v.png | Bin 0 -> 147 bytes .../gambas-mono/32/view-tree.png | Bin 0 -> 142 bytes .../gambas-mono/32/volume-high.png | Bin 0 -> 595 bytes .../gambas-mono/32/volume-low.png | Bin 0 -> 290 bytes .../gambas-mono/32/volume-medium.png | Bin 0 -> 434 bytes .../gb.form.stock/gambas-mono/32/volume.png | Bin 0 -> 240 bytes comp/src/gb.form.stock/gambas-mono/32/vpn.png | Bin 0 -> 318 bytes .../src/gb.form.stock/gambas-mono/32/walk.png | Bin 0 -> 878 bytes .../gb.form.stock/gambas-mono/32/warning.png | Bin 0 -> 303 bytes .../gb.form.stock/gambas-mono/32/watch.png | Bin 0 -> 543 bytes .../gb.form.stock/gambas-mono/32/webcam.png | Bin 0 -> 440 bytes .../gambas-mono/32/wifi-high.png | Bin 0 -> 605 bytes .../gb.form.stock/gambas-mono/32/wifi-low.png | Bin 0 -> 632 bytes .../gambas-mono/32/wifi-medium.png | Bin 0 -> 640 bytes .../src/gb.form.stock/gambas-mono/32/wifi.png | Bin 0 -> 593 bytes .../gb.form.stock/gambas-mono/32/window.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas-mono/32/windows.png | Bin 0 -> 694 bytes .../gb.form.stock/gambas-mono/32/wizard.png | Bin 0 -> 265 bytes .../gb.form.stock/gambas-mono/32/worship.png | Bin 0 -> 242 bytes .../src/gb.form.stock/gambas-mono/32/wrap.png | Bin 0 -> 273 bytes comp/src/gb.form.stock/gambas-mono/32/xml.png | Bin 0 -> 438 bytes .../gb.form.stock/gambas-mono/32/zoom-fit.png | Bin 0 -> 554 bytes .../gb.form.stock/gambas-mono/32/zoom-in.png | Bin 0 -> 453 bytes .../gambas-mono/32/zoom-normal-rtl.png | Bin 0 -> 494 bytes .../gambas-mono/32/zoom-normal.png | Bin 0 -> 503 bytes .../gb.form.stock/gambas-mono/32/zoom-out.png | Bin 0 -> 450 bytes .../gambas-mono/32/zoom-width.png | Bin 0 -> 603 bytes .../gb.form.stock/gambas-mono/app/gambas3.png | Bin 0 -> 6361 bytes comp/src/gb.form.stock/gambas/128/access.png | Bin 0 -> 354 bytes .../gambas/128/administrator.png | Bin 0 -> 2098 bytes comp/src/gb.form.stock/gambas/128/agenda.png | Bin 0 -> 340 bytes comp/src/gb.form.stock/gambas/128/alarm.png | Bin 0 -> 2280 bytes comp/src/gb.form.stock/gambas/128/android.png | Bin 0 -> 1900 bytes .../gb.form.stock/gambas/128/application.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas/128/archive.png | Bin 0 -> 264 bytes comp/src/gb.form.stock/gambas/128/audio.png | Bin 0 -> 2090 bytes comp/src/gb.form.stock/gambas/128/battery.png | Bin 0 -> 436 bytes comp/src/gb.form.stock/gambas/128/bicycle.png | Bin 0 -> 1542 bytes .../gambas/128/bluetooth-off.png | Bin 0 -> 717 bytes .../gb.form.stock/gambas/128/bluetooth.png | Bin 0 -> 516 bytes comp/src/gb.form.stock/gambas/128/book.png | Bin 0 -> 301 bytes .../src/gb.form.stock/gambas/128/bookmark.png | Bin 0 -> 706 bytes comp/src/gb.form.stock/gambas/128/bus.png | Bin 0 -> 1154 bytes comp/src/gb.form.stock/gambas/128/c.png | Bin 0 -> 1336 bytes comp/src/gb.form.stock/gambas/128/cake.png | Bin 0 -> 1200 bytes .../gb.form.stock/gambas/128/calculator.png | Bin 0 -> 322 bytes .../src/gb.form.stock/gambas/128/calendar.png | Bin 0 -> 429 bytes comp/src/gb.form.stock/gambas/128/camera.png | Bin 0 -> 695 bytes comp/src/gb.form.stock/gambas/128/car.png | Bin 0 -> 803 bytes comp/src/gb.form.stock/gambas/128/cdrom.png | Bin 0 -> 1516 bytes .../gb.form.stock/gambas/128/chart-line.png | Bin 0 -> 862 bytes .../gb.form.stock/gambas/128/chart-pie.png | Bin 0 -> 2864 bytes comp/src/gb.form.stock/gambas/128/chart.png | Bin 0 -> 187 bytes comp/src/gb.form.stock/gambas/128/chat.png | Bin 0 -> 1902 bytes comp/src/gb.form.stock/gambas/128/cherry.png | Bin 0 -> 2467 bytes comp/src/gb.form.stock/gambas/128/city.png | Bin 0 -> 396 bytes comp/src/gb.form.stock/gambas/128/clock.png | Bin 0 -> 2007 bytes comp/src/gb.form.stock/gambas/128/cloud.png | Bin 0 -> 1560 bytes .../gb.form.stock/gambas/128/color-picker.png | Bin 0 -> 511 bytes comp/src/gb.form.stock/gambas/128/color.png | Bin 0 -> 1145 bytes comp/src/gb.form.stock/gambas/128/compass.png | Bin 0 -> 2461 bytes .../gb.form.stock/gambas/128/component.png | Bin 0 -> 167 bytes .../src/gb.form.stock/gambas/128/computer.png | Bin 0 -> 972 bytes comp/src/gb.form.stock/gambas/128/cookie.png | Bin 0 -> 1747 bytes comp/src/gb.form.stock/gambas/128/copy.png | Bin 0 -> 169 bytes comp/src/gb.form.stock/gambas/128/cpp.png | Bin 0 -> 1292 bytes comp/src/gb.form.stock/gambas/128/cpu.png | Bin 0 -> 192 bytes comp/src/gb.form.stock/gambas/128/css.png | Bin 0 -> 2144 bytes comp/src/gb.form.stock/gambas/128/csv.png | Bin 0 -> 275 bytes .../src/gb.form.stock/gambas/128/currency.png | Bin 0 -> 2635 bytes comp/src/gb.form.stock/gambas/128/cut.png | Bin 0 -> 1130 bytes .../src/gb.form.stock/gambas/128/database.png | Bin 0 -> 1940 bytes comp/src/gb.form.stock/gambas/128/delete.png | Bin 0 -> 2037 bytes comp/src/gb.form.stock/gambas/128/desktop.png | Bin 0 -> 213 bytes .../gb.form.stock/gambas/128/development.png | Bin 0 -> 460 bytes .../gb.form.stock/gambas/128/directory.png | Bin 0 -> 162 bytes .../src/gb.form.stock/gambas/128/download.png | Bin 0 -> 2277 bytes comp/src/gb.form.stock/gambas/128/drink.png | Bin 0 -> 559 bytes comp/src/gb.form.stock/gambas/128/earth.png | Bin 0 -> 3010 bytes comp/src/gb.form.stock/gambas/128/edit.png | Bin 0 -> 458 bytes .../gb.form.stock/gambas/128/emote-cool.png | Bin 0 -> 1967 bytes .../gb.form.stock/gambas/128/emote-crying.png | Bin 0 -> 1995 bytes .../gb.form.stock/gambas/128/emote-happy.png | Bin 0 -> 1837 bytes .../gb.form.stock/gambas/128/emote-kiss.png | Bin 0 -> 1673 bytes .../gb.form.stock/gambas/128/emote-laugh.png | Bin 0 -> 1639 bytes .../gb.form.stock/gambas/128/emote-plain.png | Bin 0 -> 1368 bytes .../gambas/128/emote-raspberry.png | Bin 0 -> 1536 bytes .../gb.form.stock/gambas/128/emote-sad.png | Bin 0 -> 1819 bytes .../gambas/128/emote-surprise.png | Bin 0 -> 1626 bytes .../gambas/128/emote-uncertain.png | Bin 0 -> 1443 bytes .../gb.form.stock/gambas/128/emote-wink.png | Bin 0 -> 1825 bytes comp/src/gb.form.stock/gambas/128/energy.png | Bin 0 -> 596 bytes comp/src/gb.form.stock/gambas/128/error.png | Bin 0 -> 1206 bytes comp/src/gb.form.stock/gambas/128/exec.png | Bin 0 -> 1603 bytes comp/src/gb.form.stock/gambas/128/factory.png | Bin 0 -> 430 bytes .../gb.form.stock/gambas/128/file-manager.png | Bin 0 -> 201 bytes comp/src/gb.form.stock/gambas/128/file.png | Bin 0 -> 218 bytes comp/src/gb.form.stock/gambas/128/find.png | Bin 0 -> 1329 bytes .../src/gb.form.stock/gambas/128/firewall.png | Bin 0 -> 1275 bytes .../gb.form.stock/gambas/128/flag-blue.png | Bin 0 -> 353 bytes .../gb.form.stock/gambas/128/flag-green.png | Bin 0 -> 348 bytes .../src/gb.form.stock/gambas/128/flag-red.png | Bin 0 -> 353 bytes .../gb.form.stock/gambas/128/flag-yellow.png | Bin 0 -> 348 bytes comp/src/gb.form.stock/gambas/128/flag.png | Bin 0 -> 342 bytes comp/src/gb.form.stock/gambas/128/floppy.png | Bin 0 -> 238 bytes comp/src/gb.form.stock/gambas/128/fog.png | Bin 0 -> 1051 bytes .../gb.form.stock/gambas/128/folder-blue.png | Bin 0 -> 162 bytes .../gambas/128/folder-document.png | Bin 0 -> 234 bytes .../gambas/128/folder-download.png | Bin 0 -> 378 bytes .../gb.form.stock/gambas/128/folder-green.png | Bin 0 -> 162 bytes .../gb.form.stock/gambas/128/folder-home.png | Bin 0 -> 336 bytes .../gb.form.stock/gambas/128/folder-image.png | Bin 0 -> 389 bytes .../gb.form.stock/gambas/128/folder-music.png | Bin 0 -> 563 bytes .../gambas/128/folder-network.png | Bin 0 -> 390 bytes .../gambas/128/folder-recent.png | Bin 0 -> 1034 bytes .../gb.form.stock/gambas/128/folder-red.png | Bin 0 -> 162 bytes .../gambas/128/folder-remote.png | Bin 0 -> 425 bytes .../gb.form.stock/gambas/128/folder-root.png | Bin 0 -> 382 bytes .../gb.form.stock/gambas/128/folder-share.png | Bin 0 -> 673 bytes .../gambas/128/folder-template.png | Bin 0 -> 740 bytes .../gb.form.stock/gambas/128/folder-video.png | Bin 0 -> 288 bytes .../gambas/128/folder-yellow.png | Bin 0 -> 162 bytes comp/src/gb.form.stock/gambas/128/font.png | Bin 0 -> 2350 bytes comp/src/gb.form.stock/gambas/128/gambas.png | Bin 0 -> 2872 bytes comp/src/gb.form.stock/gambas/128/game.png | Bin 0 -> 638 bytes comp/src/gb.form.stock/gambas/128/gnu.png | Bin 0 -> 3479 bytes comp/src/gb.form.stock/gambas/128/h.png | Bin 0 -> 657 bytes .../gambas/128/harddisk-root.png | Bin 0 -> 326 bytes .../src/gb.form.stock/gambas/128/harddisk.png | Bin 0 -> 192 bytes .../src/gb.form.stock/gambas/128/hardware.png | Bin 0 -> 212 bytes comp/src/gb.form.stock/gambas/128/headset.png | Bin 0 -> 2376 bytes comp/src/gb.form.stock/gambas/128/help.png | Bin 0 -> 2787 bytes .../gb.form.stock/gambas/128/hibernate.png | Bin 0 -> 1978 bytes comp/src/gb.form.stock/gambas/128/house.png | Bin 0 -> 331 bytes comp/src/gb.form.stock/gambas/128/html.png | Bin 0 -> 1538 bytes .../src/gb.form.stock/gambas/128/identity.png | Bin 0 -> 2830 bytes comp/src/gb.form.stock/gambas/128/image.png | Bin 0 -> 677 bytes .../gb.form.stock/gambas/128/important.png | Bin 0 -> 1339 bytes comp/src/gb.form.stock/gambas/128/info.png | Bin 0 -> 291 bytes .../src/gb.form.stock/gambas/128/internet.png | Bin 0 -> 2764 bytes comp/src/gb.form.stock/gambas/128/java.png | Bin 0 -> 602 bytes comp/src/gb.form.stock/gambas/128/js.png | Bin 0 -> 1157 bytes comp/src/gb.form.stock/gambas/128/json.png | Bin 0 -> 1165 bytes .../src/gb.form.stock/gambas/128/keyboard.png | Bin 0 -> 836 bytes comp/src/gb.form.stock/gambas/128/library.png | Bin 0 -> 276 bytes comp/src/gb.form.stock/gambas/128/linux.png | Bin 0 -> 3240 bytes .../gb.form.stock/gambas/128/lock-screen.png | Bin 0 -> 1950 bytes comp/src/gb.form.stock/gambas/128/lock.png | Bin 0 -> 873 bytes comp/src/gb.form.stock/gambas/128/logout.png | Bin 0 -> 1814 bytes comp/src/gb.form.stock/gambas/128/love.png | Bin 0 -> 1815 bytes comp/src/gb.form.stock/gambas/128/magnet.png | Bin 0 -> 1378 bytes comp/src/gb.form.stock/gambas/128/mail.png | Bin 0 -> 591 bytes comp/src/gb.form.stock/gambas/128/map.png | Bin 0 -> 2741 bytes comp/src/gb.form.stock/gambas/128/marker.png | Bin 0 -> 1529 bytes comp/src/gb.form.stock/gambas/128/md.png | Bin 0 -> 858 bytes .../gb.form.stock/gambas/128/media-player.png | Bin 0 -> 280 bytes comp/src/gb.form.stock/gambas/128/memory.png | Bin 0 -> 180 bytes comp/src/gb.form.stock/gambas/128/message.png | Bin 0 -> 2393 bytes .../gambas/128/microphone-off.png | Bin 0 -> 1501 bytes .../gb.form.stock/gambas/128/microphone.png | Bin 0 -> 1655 bytes comp/src/gb.form.stock/gambas/128/modem.png | Bin 0 -> 591 bytes comp/src/gb.form.stock/gambas/128/money.png | Bin 0 -> 1427 bytes comp/src/gb.form.stock/gambas/128/monitor.png | Bin 0 -> 1481 bytes .../gb.form.stock/gambas/128/moon-cloud.png | Bin 0 -> 1944 bytes comp/src/gb.form.stock/gambas/128/moon.png | Bin 0 -> 1341 bytes comp/src/gb.form.stock/gambas/128/mouse.png | Bin 0 -> 744 bytes .../gb.form.stock/gambas/128/multimedia.png | Bin 0 -> 824 bytes comp/src/gb.form.stock/gambas/128/network.png | Bin 0 -> 616 bytes comp/src/gb.form.stock/gambas/128/new.png | Bin 0 -> 1118 bytes comp/src/gb.form.stock/gambas/128/news.png | Bin 0 -> 482 bytes .../gb.form.stock/gambas/128/notification.png | Bin 0 -> 1915 bytes .../gb.form.stock/gambas/128/office-calc.png | Bin 0 -> 275 bytes .../gb.form.stock/gambas/128/office-draw.png | Bin 0 -> 489 bytes .../gb.form.stock/gambas/128/office-math.png | Bin 0 -> 1053 bytes .../gambas/128/office-presentation.png | Bin 0 -> 757 bytes comp/src/gb.form.stock/gambas/128/office.png | Bin 0 -> 275 bytes .../gb.form.stock/gambas/128/open-recent.png | Bin 0 -> 306 bytes comp/src/gb.form.stock/gambas/128/open.png | Bin 0 -> 402 bytes comp/src/gb.form.stock/gambas/128/options.png | Bin 0 -> 933 bytes comp/src/gb.form.stock/gambas/128/package.png | Bin 0 -> 322 bytes comp/src/gb.form.stock/gambas/128/pda.png | Bin 0 -> 393 bytes comp/src/gb.form.stock/gambas/128/pdf.png | Bin 0 -> 1709 bytes comp/src/gb.form.stock/gambas/128/pen.png | Bin 0 -> 370 bytes comp/src/gb.form.stock/gambas/128/people.png | Bin 0 -> 2839 bytes comp/src/gb.form.stock/gambas/128/phone.png | Bin 0 -> 324 bytes comp/src/gb.form.stock/gambas/128/plane.png | Bin 0 -> 1592 bytes comp/src/gb.form.stock/gambas/128/plugin.png | Bin 0 -> 647 bytes comp/src/gb.form.stock/gambas/128/preview.png | Bin 0 -> 2591 bytes comp/src/gb.form.stock/gambas/128/printer.png | Bin 0 -> 435 bytes comp/src/gb.form.stock/gambas/128/program.png | Bin 0 -> 1169 bytes .../src/gb.form.stock/gambas/128/question.png | Bin 0 -> 2195 bytes comp/src/gb.form.stock/gambas/128/quit.png | Bin 0 -> 1243 bytes comp/src/gb.form.stock/gambas/128/rain.png | Bin 0 -> 1177 bytes comp/src/gb.form.stock/gambas/128/rename.png | Bin 0 -> 380 bytes comp/src/gb.form.stock/gambas/128/restart.png | Bin 0 -> 1791 bytes .../gb.form.stock/gambas/128/restaurant.png | Bin 0 -> 1947 bytes comp/src/gb.form.stock/gambas/128/rss.png | Bin 0 -> 1826 bytes comp/src/gb.form.stock/gambas/128/scanner.png | Bin 0 -> 996 bytes comp/src/gb.form.stock/gambas/128/science.png | Bin 0 -> 1663 bytes comp/src/gb.form.stock/gambas/128/screen.png | Bin 0 -> 392 bytes comp/src/gb.form.stock/gambas/128/script.png | Bin 0 -> 1032 bytes comp/src/gb.form.stock/gambas/128/sdcard.png | Bin 0 -> 220 bytes .../src/gb.form.stock/gambas/128/security.png | Bin 0 -> 2383 bytes comp/src/gb.form.stock/gambas/128/server.png | Bin 0 -> 198 bytes comp/src/gb.form.stock/gambas/128/share.png | Bin 0 -> 991 bytes .../src/gb.form.stock/gambas/128/shopping.png | Bin 0 -> 868 bytes .../src/gb.form.stock/gambas/128/shutdown.png | Bin 0 -> 1995 bytes .../gb.form.stock/gambas/128/smartphone.png | Bin 0 -> 694 bytes comp/src/gb.form.stock/gambas/128/snow.png | Bin 0 -> 1297 bytes comp/src/gb.form.stock/gambas/128/speaker.png | Bin 0 -> 1573 bytes comp/src/gb.form.stock/gambas/128/sport.png | Bin 0 -> 3718 bytes comp/src/gb.form.stock/gambas/128/star.png | Bin 0 -> 2035 bytes .../gb.form.stock/gambas/128/statistics.png | Bin 0 -> 767 bytes comp/src/gb.form.stock/gambas/128/storm.png | Bin 0 -> 1688 bytes .../gb.form.stock/gambas/128/sun-cloud.png | Bin 0 -> 1718 bytes comp/src/gb.form.stock/gambas/128/sun.png | Bin 0 -> 1000 bytes comp/src/gb.form.stock/gambas/128/suspend.png | Bin 0 -> 1424 bytes .../gb.form.stock/gambas/128/switch-user.png | Bin 0 -> 1873 bytes comp/src/gb.form.stock/gambas/128/system.png | Bin 0 -> 917 bytes comp/src/gb.form.stock/gambas/128/table.png | Bin 0 -> 205 bytes comp/src/gb.form.stock/gambas/128/tablet.png | Bin 0 -> 843 bytes comp/src/gb.form.stock/gambas/128/tag.png | Bin 0 -> 690 bytes comp/src/gb.form.stock/gambas/128/taxi.png | Bin 0 -> 849 bytes .../src/gb.form.stock/gambas/128/template.png | Bin 0 -> 551 bytes .../src/gb.form.stock/gambas/128/terminal.png | Bin 0 -> 398 bytes comp/src/gb.form.stock/gambas/128/text.png | Bin 0 -> 259 bytes comp/src/gb.form.stock/gambas/128/theme.png | Bin 0 -> 219 bytes comp/src/gb.form.stock/gambas/128/toilet.png | Bin 0 -> 1771 bytes comp/src/gb.form.stock/gambas/128/tools.png | Bin 0 -> 1026 bytes comp/src/gb.form.stock/gambas/128/town.png | Bin 0 -> 468 bytes comp/src/gb.form.stock/gambas/128/traffic.png | Bin 0 -> 1242 bytes comp/src/gb.form.stock/gambas/128/train.png | Bin 0 -> 1289 bytes .../gb.form.stock/gambas/128/trash-full.png | Bin 0 -> 657 bytes comp/src/gb.form.stock/gambas/128/trash.png | Bin 0 -> 434 bytes comp/src/gb.form.stock/gambas/128/truck.png | Bin 0 -> 866 bytes comp/src/gb.form.stock/gambas/128/unlock.png | Bin 0 -> 858 bytes comp/src/gb.form.stock/gambas/128/update.png | Bin 0 -> 1343 bytes comp/src/gb.form.stock/gambas/128/upload.png | Bin 0 -> 2212 bytes comp/src/gb.form.stock/gambas/128/usb.png | Bin 0 -> 754 bytes .../gb.form.stock/gambas/128/user-group.png | Bin 0 -> 2649 bytes comp/src/gb.form.stock/gambas/128/user.png | Bin 0 -> 1785 bytes comp/src/gb.form.stock/gambas/128/vector.png | Bin 0 -> 847 bytes comp/src/gb.form.stock/gambas/128/video.png | Bin 0 -> 315 bytes comp/src/gb.form.stock/gambas/128/vpn.png | Bin 0 -> 874 bytes comp/src/gb.form.stock/gambas/128/walk.png | Bin 0 -> 2967 bytes comp/src/gb.form.stock/gambas/128/warning.png | Bin 0 -> 804 bytes comp/src/gb.form.stock/gambas/128/webcam.png | Bin 0 -> 2032 bytes comp/src/gb.form.stock/gambas/128/window.png | Bin 0 -> 205 bytes comp/src/gb.form.stock/gambas/128/windows.png | Bin 0 -> 4598 bytes comp/src/gb.form.stock/gambas/128/wizard.png | Bin 0 -> 571 bytes comp/src/gb.form.stock/gambas/128/worship.png | Bin 0 -> 608 bytes comp/src/gb.form.stock/gambas/128/xml.png | Bin 0 -> 1537 bytes comp/src/gb.form.stock/gambas/32/access.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas/32/add.png | Bin 0 -> 135 bytes comp/src/gb.form.stock/gambas/32/added.png | Bin 0 -> 459 bytes .../gb.form.stock/gambas/32/administrator.png | Bin 0 -> 587 bytes comp/src/gb.form.stock/gambas/32/agenda.png | Bin 0 -> 185 bytes comp/src/gb.form.stock/gambas/32/alarm.png | Bin 0 -> 603 bytes .../gb.form.stock/gambas/32/align-bottom.png | Bin 0 -> 136 bytes .../gb.form.stock/gambas/32/align-center.png | Bin 0 -> 140 bytes .../gb.form.stock/gambas/32/align-height.png | Bin 0 -> 134 bytes .../gb.form.stock/gambas/32/align-left.png | Bin 0 -> 141 bytes .../gb.form.stock/gambas/32/align-middle.png | Bin 0 -> 138 bytes .../gb.form.stock/gambas/32/align-right.png | Bin 0 -> 140 bytes .../src/gb.form.stock/gambas/32/align-top.png | Bin 0 -> 138 bytes .../gb.form.stock/gambas/32/align-width.png | Bin 0 -> 141 bytes comp/src/gb.form.stock/gambas/32/anchor.png | Bin 0 -> 632 bytes comp/src/gb.form.stock/gambas/32/android.png | Bin 0 -> 588 bytes .../gb.form.stock/gambas/32/application.png | Bin 0 -> 152 bytes comp/src/gb.form.stock/gambas/32/apply.png | Bin 0 -> 207 bytes comp/src/gb.form.stock/gambas/32/archive.png | Bin 0 -> 181 bytes comp/src/gb.form.stock/gambas/32/attach.png | Bin 0 -> 751 bytes comp/src/gb.form.stock/gambas/32/audio.png | Bin 0 -> 670 bytes comp/src/gb.form.stock/gambas/32/average.png | Bin 0 -> 608 bytes .../gb.form.stock/gambas/32/battery-high.png | Bin 0 -> 156 bytes .../gb.form.stock/gambas/32/battery-low.png | Bin 0 -> 155 bytes .../gambas/32/battery-medium.png | Bin 0 -> 156 bytes comp/src/gb.form.stock/gambas/32/battery.png | Bin 0 -> 252 bytes comp/src/gb.form.stock/gambas/32/bicycle.png | Bin 0 -> 504 bytes comp/src/gb.form.stock/gambas/32/blue.png | Bin 0 -> 345 bytes .../gb.form.stock/gambas/32/bluetooth-off.png | Bin 0 -> 320 bytes .../src/gb.form.stock/gambas/32/bluetooth.png | Bin 0 -> 243 bytes comp/src/gb.form.stock/gambas/32/blur.png | Bin 0 -> 1699 bytes comp/src/gb.form.stock/gambas/32/book.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas/32/bookmark.png | Bin 0 -> 273 bytes .../gb.form.stock/gambas/32/border-bottom.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas/32/border-diag.png | Bin 0 -> 315 bytes .../gambas/32/border-horizontal.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas/32/border-inside.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas/32/border-left.png | Bin 0 -> 146 bytes .../gb.form.stock/gambas/32/border-none.png | Bin 0 -> 143 bytes .../gambas/32/border-outside.png | Bin 0 -> 153 bytes .../gambas/32/border-rev-diag.png | Bin 0 -> 311 bytes .../gb.form.stock/gambas/32/border-right.png | Bin 0 -> 146 bytes .../gb.form.stock/gambas/32/border-top.png | Bin 0 -> 153 bytes .../gambas/32/border-vertical.png | Bin 0 -> 156 bytes comp/src/gb.form.stock/gambas/32/border.png | Bin 0 -> 143 bytes comp/src/gb.form.stock/gambas/32/bottom.png | Bin 0 -> 233 bytes .../gb.form.stock/gambas/32/brightness.png | Bin 0 -> 353 bytes comp/src/gb.form.stock/gambas/32/brush.png | Bin 0 -> 334 bytes comp/src/gb.form.stock/gambas/32/bus.png | Bin 0 -> 365 bytes comp/src/gb.form.stock/gambas/32/c.png | Bin 0 -> 475 bytes comp/src/gb.form.stock/gambas/32/cake.png | Bin 0 -> 380 bytes .../gb.form.stock/gambas/32/calculator.png | Bin 0 -> 173 bytes comp/src/gb.form.stock/gambas/32/calendar.png | Bin 0 -> 214 bytes .../src/gb.form.stock/gambas/32/call-stop.png | Bin 0 -> 337 bytes comp/src/gb.form.stock/gambas/32/call.png | Bin 0 -> 602 bytes comp/src/gb.form.stock/gambas/32/camera.png | Bin 0 -> 249 bytes comp/src/gb.form.stock/gambas/32/cancel.png | Bin 0 -> 305 bytes comp/src/gb.form.stock/gambas/32/cap-butt.png | Bin 0 -> 136 bytes .../src/gb.form.stock/gambas/32/cap-round.png | Bin 0 -> 328 bytes .../gb.form.stock/gambas/32/cap-square.png | Bin 0 -> 134 bytes comp/src/gb.form.stock/gambas/32/car.png | Bin 0 -> 327 bytes comp/src/gb.form.stock/gambas/32/cdrom.png | Bin 0 -> 408 bytes .../gb.form.stock/gambas/32/chart-line.png | Bin 0 -> 409 bytes .../src/gb.form.stock/gambas/32/chart-pie.png | Bin 0 -> 1036 bytes comp/src/gb.form.stock/gambas/32/chart.png | Bin 0 -> 171 bytes comp/src/gb.form.stock/gambas/32/chat.png | Bin 0 -> 565 bytes comp/src/gb.form.stock/gambas/32/cherry.png | Bin 0 -> 791 bytes comp/src/gb.form.stock/gambas/32/city.png | Bin 0 -> 223 bytes comp/src/gb.form.stock/gambas/32/clear.png | Bin 0 -> 317 bytes comp/src/gb.form.stock/gambas/32/clock.png | Bin 0 -> 453 bytes comp/src/gb.form.stock/gambas/32/clone.png | Bin 0 -> 231 bytes comp/src/gb.form.stock/gambas/32/close.png | Bin 0 -> 281 bytes comp/src/gb.form.stock/gambas/32/cloud.png | Bin 0 -> 475 bytes .../gb.form.stock/gambas/32/color-picker.png | Bin 0 -> 266 bytes comp/src/gb.form.stock/gambas/32/color.png | Bin 0 -> 284 bytes .../gb.form.stock/gambas/32/column-after.png | Bin 0 -> 325 bytes .../gb.form.stock/gambas/32/column-before.png | Bin 0 -> 305 bytes .../gb.form.stock/gambas/32/column-remove.png | Bin 0 -> 351 bytes comp/src/gb.form.stock/gambas/32/compass.png | Bin 0 -> 649 bytes .../src/gb.form.stock/gambas/32/component.png | Bin 0 -> 144 bytes comp/src/gb.form.stock/gambas/32/compress.png | Bin 0 -> 337 bytes comp/src/gb.form.stock/gambas/32/computer.png | Bin 0 -> 318 bytes comp/src/gb.form.stock/gambas/32/conflict.png | Bin 0 -> 533 bytes comp/src/gb.form.stock/gambas/32/connect.png | Bin 0 -> 262 bytes comp/src/gb.form.stock/gambas/32/contrast.png | Bin 0 -> 430 bytes comp/src/gb.form.stock/gambas/32/cookie.png | Bin 0 -> 489 bytes comp/src/gb.form.stock/gambas/32/copy.png | Bin 0 -> 160 bytes comp/src/gb.form.stock/gambas/32/cpp.png | Bin 0 -> 459 bytes comp/src/gb.form.stock/gambas/32/cpu.png | Bin 0 -> 165 bytes comp/src/gb.form.stock/gambas/32/crop.png | Bin 0 -> 154 bytes comp/src/gb.form.stock/gambas/32/css.png | Bin 0 -> 724 bytes comp/src/gb.form.stock/gambas/32/csv.png | Bin 0 -> 190 bytes comp/src/gb.form.stock/gambas/32/currency.png | Bin 0 -> 721 bytes comp/src/gb.form.stock/gambas/32/cut.png | Bin 0 -> 350 bytes comp/src/gb.form.stock/gambas/32/database.png | Bin 0 -> 659 bytes .../gb.form.stock/gambas/32/debug-cursor.png | Bin 0 -> 240 bytes .../gb.form.stock/gambas/32/debug-into.png | Bin 0 -> 181 bytes .../src/gb.form.stock/gambas/32/debug-out.png | Bin 0 -> 182 bytes .../gb.form.stock/gambas/32/debug-over.png | Bin 0 -> 197 bytes comp/src/gb.form.stock/gambas/32/delete.png | Bin 0 -> 531 bytes comp/src/gb.form.stock/gambas/32/desktop.png | Bin 0 -> 174 bytes .../gb.form.stock/gambas/32/development.png | Bin 0 -> 241 bytes .../gb.form.stock/gambas/32/difference.png | Bin 0 -> 173 bytes .../src/gb.form.stock/gambas/32/directory.png | Bin 0 -> 151 bytes .../gb.form.stock/gambas/32/disconnect.png | Bin 0 -> 298 bytes comp/src/gb.form.stock/gambas/32/down.png | Bin 0 -> 219 bytes comp/src/gb.form.stock/gambas/32/download.png | Bin 0 -> 623 bytes .../gb.form.stock/gambas/32/draw-arrow.png | Bin 0 -> 343 bytes .../gb.form.stock/gambas/32/draw-circle.png | Bin 0 -> 458 bytes .../src/gb.form.stock/gambas/32/draw-line.png | Bin 0 -> 173 bytes .../src/gb.form.stock/gambas/32/draw-path.png | Bin 0 -> 391 bytes .../gb.form.stock/gambas/32/draw-polygon.png | Bin 0 -> 748 bytes .../gambas/32/draw-rectangle.png | Bin 0 -> 136 bytes .../gb.form.stock/gambas/32/draw-round.png | Bin 0 -> 321 bytes .../src/gb.form.stock/gambas/32/draw-star.png | Bin 0 -> 816 bytes .../src/gb.form.stock/gambas/32/draw-text.png | Bin 0 -> 266 bytes comp/src/gb.form.stock/gambas/32/drink.png | Bin 0 -> 256 bytes comp/src/gb.form.stock/gambas/32/earth.png | Bin 0 -> 919 bytes comp/src/gb.form.stock/gambas/32/edit.png | Bin 0 -> 247 bytes comp/src/gb.form.stock/gambas/32/eject.png | Bin 0 -> 190 bytes .../gb.form.stock/gambas/32/emote-cool.png | Bin 0 -> 469 bytes .../gb.form.stock/gambas/32/emote-crying.png | Bin 0 -> 471 bytes .../gb.form.stock/gambas/32/emote-happy.png | Bin 0 -> 438 bytes .../gb.form.stock/gambas/32/emote-kiss.png | Bin 0 -> 411 bytes .../gb.form.stock/gambas/32/emote-laugh.png | Bin 0 -> 405 bytes .../gb.form.stock/gambas/32/emote-plain.png | Bin 0 -> 358 bytes .../gambas/32/emote-raspberry.png | Bin 0 -> 388 bytes .../src/gb.form.stock/gambas/32/emote-sad.png | Bin 0 -> 439 bytes .../gambas/32/emote-surprise.png | Bin 0 -> 398 bytes .../gambas/32/emote-uncertain.png | Bin 0 -> 388 bytes .../gb.form.stock/gambas/32/emote-wink.png | Bin 0 -> 438 bytes .../gb.form.stock/gambas/32/end-of-line.png | Bin 0 -> 186 bytes comp/src/gb.form.stock/gambas/32/end.png | Bin 0 -> 181 bytes comp/src/gb.form.stock/gambas/32/energy.png | Bin 0 -> 313 bytes comp/src/gb.form.stock/gambas/32/erase.png | Bin 0 -> 229 bytes comp/src/gb.form.stock/gambas/32/error.png | Bin 0 -> 349 bytes .../src/gb.form.stock/gambas/32/exclusive.png | Bin 0 -> 156 bytes comp/src/gb.form.stock/gambas/32/exec.png | Bin 0 -> 442 bytes comp/src/gb.form.stock/gambas/32/export.png | Bin 0 -> 203 bytes comp/src/gb.form.stock/gambas/32/factory.png | Bin 0 -> 246 bytes .../gb.form.stock/gambas/32/file-manager.png | Bin 0 -> 168 bytes comp/src/gb.form.stock/gambas/32/file.png | Bin 0 -> 163 bytes comp/src/gb.form.stock/gambas/32/fill.png | Bin 0 -> 434 bytes comp/src/gb.form.stock/gambas/32/filter.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas/32/find.png | Bin 0 -> 402 bytes comp/src/gb.form.stock/gambas/32/firewall.png | Bin 0 -> 396 bytes comp/src/gb.form.stock/gambas/32/first.png | Bin 0 -> 214 bytes .../src/gb.form.stock/gambas/32/flag-blue.png | Bin 0 -> 197 bytes .../gb.form.stock/gambas/32/flag-green.png | Bin 0 -> 197 bytes comp/src/gb.form.stock/gambas/32/flag-red.png | Bin 0 -> 197 bytes .../gb.form.stock/gambas/32/flag-yellow.png | Bin 0 -> 202 bytes comp/src/gb.form.stock/gambas/32/flag.png | Bin 0 -> 197 bytes comp/src/gb.form.stock/gambas/32/flip-h.png | Bin 0 -> 198 bytes comp/src/gb.form.stock/gambas/32/flip-v.png | Bin 0 -> 226 bytes comp/src/gb.form.stock/gambas/32/floppy.png | Bin 0 -> 184 bytes comp/src/gb.form.stock/gambas/32/fog.png | Bin 0 -> 339 bytes .../gb.form.stock/gambas/32/folder-blue.png | Bin 0 -> 143 bytes .../gambas/32/folder-document.png | Bin 0 -> 174 bytes .../gambas/32/folder-download.png | Bin 0 -> 210 bytes .../gb.form.stock/gambas/32/folder-green.png | Bin 0 -> 143 bytes .../gb.form.stock/gambas/32/folder-home.png | Bin 0 -> 200 bytes .../gb.form.stock/gambas/32/folder-image.png | Bin 0 -> 208 bytes .../gb.form.stock/gambas/32/folder-music.png | Bin 0 -> 252 bytes .../gambas/32/folder-network.png | Bin 0 -> 223 bytes .../gb.form.stock/gambas/32/folder-recent.png | Bin 0 -> 305 bytes .../gb.form.stock/gambas/32/folder-red.png | Bin 0 -> 143 bytes .../gb.form.stock/gambas/32/folder-remote.png | Bin 0 -> 200 bytes .../gb.form.stock/gambas/32/folder-root.png | Bin 0 -> 211 bytes .../gb.form.stock/gambas/32/folder-share.png | Bin 0 -> 311 bytes .../gambas/32/folder-template.png | Bin 0 -> 327 bytes .../gb.form.stock/gambas/32/folder-video.png | Bin 0 -> 229 bytes .../gb.form.stock/gambas/32/folder-yellow.png | Bin 0 -> 143 bytes comp/src/gb.form.stock/gambas/32/font.png | Bin 0 -> 834 bytes comp/src/gb.form.stock/gambas/32/formula.png | Bin 0 -> 848 bytes comp/src/gb.form.stock/gambas/32/forward.png | Bin 0 -> 205 bytes .../gb.form.stock/gambas/32/fullscreen.png | Bin 0 -> 159 bytes comp/src/gb.form.stock/gambas/32/gambas.png | Bin 0 -> 946 bytes comp/src/gb.form.stock/gambas/32/game.png | Bin 0 -> 226 bytes comp/src/gb.form.stock/gambas/32/gamma.png | Bin 0 -> 373 bytes comp/src/gb.form.stock/gambas/32/gnu.png | Bin 0 -> 1309 bytes comp/src/gb.form.stock/gambas/32/gradient.png | Bin 0 -> 278 bytes comp/src/gb.form.stock/gambas/32/green.png | Bin 0 -> 345 bytes comp/src/gb.form.stock/gambas/32/grid.png | Bin 0 -> 154 bytes comp/src/gb.form.stock/gambas/32/group.png | Bin 0 -> 311 bytes comp/src/gb.form.stock/gambas/32/h.png | Bin 0 -> 311 bytes comp/src/gb.form.stock/gambas/32/halt.png | Bin 0 -> 409 bytes .../gb.form.stock/gambas/32/harddisk-root.png | Bin 0 -> 199 bytes comp/src/gb.form.stock/gambas/32/harddisk.png | Bin 0 -> 160 bytes comp/src/gb.form.stock/gambas/32/hardware.png | Bin 0 -> 172 bytes comp/src/gb.form.stock/gambas/32/hatch.png | Bin 0 -> 206 bytes comp/src/gb.form.stock/gambas/32/headset.png | Bin 0 -> 666 bytes comp/src/gb.form.stock/gambas/32/help.png | Bin 0 -> 702 bytes .../src/gb.form.stock/gambas/32/hibernate.png | Bin 0 -> 571 bytes .../src/gb.form.stock/gambas/32/highlight.png | Bin 0 -> 230 bytes comp/src/gb.form.stock/gambas/32/home.png | Bin 0 -> 217 bytes comp/src/gb.form.stock/gambas/32/house.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas/32/html.png | Bin 0 -> 443 bytes comp/src/gb.form.stock/gambas/32/hue.png | Bin 0 -> 459 bytes comp/src/gb.form.stock/gambas/32/identity.png | Bin 0 -> 673 bytes comp/src/gb.form.stock/gambas/32/image.png | Bin 0 -> 249 bytes comp/src/gb.form.stock/gambas/32/import.png | Bin 0 -> 206 bytes .../src/gb.form.stock/gambas/32/important.png | Bin 0 -> 368 bytes comp/src/gb.form.stock/gambas/32/indent.png | Bin 0 -> 191 bytes comp/src/gb.form.stock/gambas/32/info.png | Bin 0 -> 165 bytes .../gb.form.stock/gambas/32/inheritance.png | Bin 0 -> 220 bytes .../gb.form.stock/gambas/32/insert-image.png | Bin 0 -> 433 bytes .../gb.form.stock/gambas/32/insert-link.png | Bin 0 -> 633 bytes .../gb.form.stock/gambas/32/insert-table.png | Bin 0 -> 382 bytes .../gb.form.stock/gambas/32/insert-text.png | Bin 0 -> 354 bytes comp/src/gb.form.stock/gambas/32/internet.png | Bin 0 -> 1159 bytes .../gb.form.stock/gambas/32/intersection.png | Bin 0 -> 164 bytes comp/src/gb.form.stock/gambas/32/invert.png | Bin 0 -> 243 bytes comp/src/gb.form.stock/gambas/32/java.png | Bin 0 -> 238 bytes .../gb.form.stock/gambas/32/join-bevel.png | Bin 0 -> 176 bytes .../gb.form.stock/gambas/32/join-miter.png | Bin 0 -> 135 bytes .../gb.form.stock/gambas/32/join-round.png | Bin 0 -> 417 bytes comp/src/gb.form.stock/gambas/32/js.png | Bin 0 -> 432 bytes comp/src/gb.form.stock/gambas/32/json.png | Bin 0 -> 441 bytes comp/src/gb.form.stock/gambas/32/jump.png | Bin 0 -> 212 bytes comp/src/gb.form.stock/gambas/32/key.png | Bin 0 -> 342 bytes comp/src/gb.form.stock/gambas/32/keyboard.png | Bin 0 -> 289 bytes comp/src/gb.form.stock/gambas/32/lamp.png | Bin 0 -> 341 bytes comp/src/gb.form.stock/gambas/32/language.png | Bin 0 -> 387 bytes comp/src/gb.form.stock/gambas/32/last.png | Bin 0 -> 212 bytes comp/src/gb.form.stock/gambas/32/layer.png | Bin 0 -> 506 bytes comp/src/gb.form.stock/gambas/32/layout.png | Bin 0 -> 384 bytes comp/src/gb.form.stock/gambas/32/left.png | Bin 0 -> 183 bytes comp/src/gb.form.stock/gambas/32/library.png | Bin 0 -> 195 bytes .../src/gb.form.stock/gambas/32/lightness.png | Bin 0 -> 391 bytes .../gb.form.stock/gambas/32/line-style.png | Bin 0 -> 142 bytes .../gb.form.stock/gambas/32/line-width.png | Bin 0 -> 135 bytes comp/src/gb.form.stock/gambas/32/link.png | Bin 0 -> 489 bytes comp/src/gb.form.stock/gambas/32/linux.png | Bin 0 -> 1446 bytes .../gb.form.stock/gambas/32/lock-screen.png | Bin 0 -> 491 bytes comp/src/gb.form.stock/gambas/32/lock.png | Bin 0 -> 266 bytes comp/src/gb.form.stock/gambas/32/logout.png | Bin 0 -> 448 bytes comp/src/gb.form.stock/gambas/32/love.png | Bin 0 -> 623 bytes comp/src/gb.form.stock/gambas/32/lower.png | Bin 0 -> 145 bytes comp/src/gb.form.stock/gambas/32/magnet.png | Bin 0 -> 431 bytes comp/src/gb.form.stock/gambas/32/mail.png | Bin 0 -> 282 bytes comp/src/gb.form.stock/gambas/32/make-all.png | Bin 0 -> 248 bytes comp/src/gb.form.stock/gambas/32/make.png | Bin 0 -> 252 bytes comp/src/gb.form.stock/gambas/32/map.png | Bin 0 -> 632 bytes comp/src/gb.form.stock/gambas/32/marker.png | Bin 0 -> 494 bytes comp/src/gb.form.stock/gambas/32/math.png | Bin 0 -> 861 bytes comp/src/gb.form.stock/gambas/32/md.png | Bin 0 -> 376 bytes .../gb.form.stock/gambas/32/media-player.png | Bin 0 -> 179 bytes comp/src/gb.form.stock/gambas/32/memory.png | Bin 0 -> 161 bytes comp/src/gb.form.stock/gambas/32/menu.png | Bin 0 -> 131 bytes comp/src/gb.form.stock/gambas/32/message.png | Bin 0 -> 673 bytes .../gambas/32/microphone-off.png | Bin 0 -> 446 bytes .../gb.form.stock/gambas/32/microphone.png | Bin 0 -> 448 bytes comp/src/gb.form.stock/gambas/32/modem.png | Bin 0 -> 214 bytes comp/src/gb.form.stock/gambas/32/modified.png | Bin 0 -> 524 bytes comp/src/gb.form.stock/gambas/32/money.png | Bin 0 -> 410 bytes comp/src/gb.form.stock/gambas/32/monitor.png | Bin 0 -> 517 bytes .../gb.form.stock/gambas/32/moon-cloud.png | Bin 0 -> 551 bytes comp/src/gb.form.stock/gambas/32/moon.png | Bin 0 -> 376 bytes comp/src/gb.form.stock/gambas/32/more-h.png | Bin 0 -> 148 bytes comp/src/gb.form.stock/gambas/32/more.png | Bin 0 -> 143 bytes comp/src/gb.form.stock/gambas/32/mount.png | Bin 0 -> 185 bytes comp/src/gb.form.stock/gambas/32/mouse.png | Bin 0 -> 273 bytes comp/src/gb.form.stock/gambas/32/move.png | Bin 0 -> 323 bytes .../gb.form.stock/gambas/32/multimedia.png | Bin 0 -> 268 bytes comp/src/gb.form.stock/gambas/32/muted.png | Bin 0 -> 306 bytes comp/src/gb.form.stock/gambas/32/network.png | Bin 0 -> 230 bytes .../gambas/32/new-appointment.png | Bin 0 -> 636 bytes comp/src/gb.form.stock/gambas/32/new-dir.png | Bin 0 -> 348 bytes comp/src/gb.form.stock/gambas/32/new-mail.png | Bin 0 -> 464 bytes comp/src/gb.form.stock/gambas/32/new-tab.png | Bin 0 -> 358 bytes comp/src/gb.form.stock/gambas/32/new-tag.png | Bin 0 -> 422 bytes comp/src/gb.form.stock/gambas/32/new-user.png | Bin 0 -> 628 bytes .../gb.form.stock/gambas/32/new-window.png | Bin 0 -> 364 bytes comp/src/gb.form.stock/gambas/32/new.png | Bin 0 -> 350 bytes comp/src/gb.form.stock/gambas/32/news.png | Bin 0 -> 205 bytes comp/src/gb.form.stock/gambas/32/next.png | Bin 0 -> 194 bytes .../gb.form.stock/gambas/32/notification.png | Bin 0 -> 511 bytes .../gb.form.stock/gambas/32/office-calc.png | Bin 0 -> 190 bytes .../gb.form.stock/gambas/32/office-draw.png | Bin 0 -> 237 bytes .../gb.form.stock/gambas/32/office-math.png | Bin 0 -> 460 bytes .../gambas/32/office-presentation.png | Bin 0 -> 268 bytes comp/src/gb.form.stock/gambas/32/office.png | Bin 0 -> 191 bytes comp/src/gb.form.stock/gambas/32/ok.png | Bin 0 -> 207 bytes comp/src/gb.form.stock/gambas/32/opacity.png | Bin 0 -> 450 bytes .../src/gb.form.stock/gambas/32/open-link.png | Bin 0 -> 298 bytes .../gb.form.stock/gambas/32/open-recent.png | Bin 0 -> 164 bytes comp/src/gb.form.stock/gambas/32/open.png | Bin 0 -> 215 bytes comp/src/gb.form.stock/gambas/32/options.png | Bin 0 -> 351 bytes comp/src/gb.form.stock/gambas/32/package.png | Bin 0 -> 200 bytes .../gb.form.stock/gambas/32/page-break.png | Bin 0 -> 178 bytes comp/src/gb.form.stock/gambas/32/page-two.png | Bin 0 -> 182 bytes comp/src/gb.form.stock/gambas/32/page.png | Bin 0 -> 164 bytes .../src/gb.form.stock/gambas/32/paragraph.png | Bin 0 -> 204 bytes .../gb.form.stock/gambas/32/paste-special.png | Bin 0 -> 309 bytes comp/src/gb.form.stock/gambas/32/paste.png | Bin 0 -> 149 bytes comp/src/gb.form.stock/gambas/32/pause.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas/32/pda.png | Bin 0 -> 211 bytes comp/src/gb.form.stock/gambas/32/pdf.png | Bin 0 -> 635 bytes comp/src/gb.form.stock/gambas/32/pen.png | Bin 0 -> 201 bytes comp/src/gb.form.stock/gambas/32/people.png | Bin 0 -> 1207 bytes comp/src/gb.form.stock/gambas/32/percent.png | Bin 0 -> 947 bytes comp/src/gb.form.stock/gambas/32/phone.png | Bin 0 -> 193 bytes comp/src/gb.form.stock/gambas/32/pin.png | Bin 0 -> 709 bytes comp/src/gb.form.stock/gambas/32/plane.png | Bin 0 -> 625 bytes comp/src/gb.form.stock/gambas/32/play.png | Bin 0 -> 183 bytes comp/src/gb.form.stock/gambas/32/plugin.png | Bin 0 -> 253 bytes comp/src/gb.form.stock/gambas/32/pointer.png | Bin 0 -> 649 bytes comp/src/gb.form.stock/gambas/32/preview.png | Bin 0 -> 627 bytes comp/src/gb.form.stock/gambas/32/previous.png | Bin 0 -> 201 bytes comp/src/gb.form.stock/gambas/32/print.png | Bin 0 -> 173 bytes comp/src/gb.form.stock/gambas/32/printer.png | Bin 0 -> 213 bytes comp/src/gb.form.stock/gambas/32/program.png | Bin 0 -> 375 bytes .../gb.form.stock/gambas/32/properties.png | Bin 0 -> 143 bytes comp/src/gb.form.stock/gambas/32/question.png | Bin 0 -> 610 bytes comp/src/gb.form.stock/gambas/32/quit.png | Bin 0 -> 374 bytes comp/src/gb.form.stock/gambas/32/quote.png | Bin 0 -> 210 bytes comp/src/gb.form.stock/gambas/32/rain.png | Bin 0 -> 384 bytes comp/src/gb.form.stock/gambas/32/raise.png | Bin 0 -> 158 bytes comp/src/gb.form.stock/gambas/32/record.png | Bin 0 -> 321 bytes comp/src/gb.form.stock/gambas/32/red.png | Bin 0 -> 345 bytes comp/src/gb.form.stock/gambas/32/redo.png | Bin 0 -> 311 bytes comp/src/gb.form.stock/gambas/32/refresh.png | Bin 0 -> 440 bytes comp/src/gb.form.stock/gambas/32/remove.png | Bin 0 -> 129 bytes comp/src/gb.form.stock/gambas/32/rename.png | Bin 0 -> 211 bytes comp/src/gb.form.stock/gambas/32/repeat.png | Bin 0 -> 264 bytes comp/src/gb.form.stock/gambas/32/replace.png | Bin 0 -> 403 bytes .../src/gb.form.stock/gambas/32/reply-all.png | Bin 0 -> 336 bytes comp/src/gb.form.stock/gambas/32/reply.png | Bin 0 -> 321 bytes comp/src/gb.form.stock/gambas/32/resize.png | Bin 0 -> 151 bytes comp/src/gb.form.stock/gambas/32/restart.png | Bin 0 -> 468 bytes .../gb.form.stock/gambas/32/restaurant.png | Bin 0 -> 724 bytes comp/src/gb.form.stock/gambas/32/revert.png | Bin 0 -> 243 bytes comp/src/gb.form.stock/gambas/32/rewind.png | Bin 0 -> 203 bytes comp/src/gb.form.stock/gambas/32/right.png | Bin 0 -> 184 bytes .../gb.form.stock/gambas/32/rotate-left.png | Bin 0 -> 200 bytes .../gb.form.stock/gambas/32/rotate-right.png | Bin 0 -> 201 bytes comp/src/gb.form.stock/gambas/32/rotate.png | Bin 0 -> 380 bytes .../src/gb.form.stock/gambas/32/row-after.png | Bin 0 -> 327 bytes .../gb.form.stock/gambas/32/row-before.png | Bin 0 -> 304 bytes .../gb.form.stock/gambas/32/row-remove.png | Bin 0 -> 351 bytes comp/src/gb.form.stock/gambas/32/rss.png | Bin 0 -> 576 bytes .../gb.form.stock/gambas/32/saturation.png | Bin 0 -> 446 bytes comp/src/gb.form.stock/gambas/32/save-as.png | Bin 0 -> 222 bytes comp/src/gb.form.stock/gambas/32/save.png | Bin 0 -> 213 bytes comp/src/gb.form.stock/gambas/32/scanner.png | Bin 0 -> 520 bytes comp/src/gb.form.stock/gambas/32/science.png | Bin 0 -> 439 bytes comp/src/gb.form.stock/gambas/32/screen.png | Bin 0 -> 233 bytes comp/src/gb.form.stock/gambas/32/script.png | Bin 0 -> 449 bytes comp/src/gb.form.stock/gambas/32/sdcard.png | Bin 0 -> 173 bytes .../gb.form.stock/gambas/32/security-high.png | Bin 0 -> 808 bytes .../gb.form.stock/gambas/32/security-low.png | Bin 0 -> 682 bytes .../gambas/32/security-medium.png | Bin 0 -> 780 bytes comp/src/gb.form.stock/gambas/32/security.png | Bin 0 -> 665 bytes .../gb.form.stock/gambas/32/select-all.png | Bin 0 -> 217 bytes comp/src/gb.form.stock/gambas/32/select.png | Bin 0 -> 198 bytes comp/src/gb.form.stock/gambas/32/server.png | Bin 0 -> 165 bytes comp/src/gb.form.stock/gambas/32/share.png | Bin 0 -> 288 bytes comp/src/gb.form.stock/gambas/32/shopping.png | Bin 0 -> 352 bytes comp/src/gb.form.stock/gambas/32/shortcut.png | Bin 0 -> 576 bytes comp/src/gb.form.stock/gambas/32/shuffle.png | Bin 0 -> 485 bytes comp/src/gb.form.stock/gambas/32/shutdown.png | Bin 0 -> 493 bytes .../gb.form.stock/gambas/32/smartphone.png | Bin 0 -> 299 bytes comp/src/gb.form.stock/gambas/32/snow.png | Bin 0 -> 377 bytes .../gb.form.stock/gambas/32/sort-ascent.png | Bin 0 -> 249 bytes .../gb.form.stock/gambas/32/sort-descent.png | Bin 0 -> 256 bytes comp/src/gb.form.stock/gambas/32/speaker.png | Bin 0 -> 429 bytes .../gb.form.stock/gambas/32/spell-check.png | Bin 0 -> 794 bytes comp/src/gb.form.stock/gambas/32/sport.png | Bin 0 -> 984 bytes comp/src/gb.form.stock/gambas/32/star.png | Bin 0 -> 592 bytes comp/src/gb.form.stock/gambas/32/start.png | Bin 0 -> 185 bytes .../gb.form.stock/gambas/32/statistics.png | Bin 0 -> 324 bytes comp/src/gb.form.stock/gambas/32/stop.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas/32/storm.png | Bin 0 -> 479 bytes comp/src/gb.form.stock/gambas/32/subtitle.png | Bin 0 -> 166 bytes comp/src/gb.form.stock/gambas/32/sum.png | Bin 0 -> 549 bytes .../src/gb.form.stock/gambas/32/sun-cloud.png | Bin 0 -> 523 bytes comp/src/gb.form.stock/gambas/32/sun.png | Bin 0 -> 331 bytes comp/src/gb.form.stock/gambas/32/suspend.png | Bin 0 -> 392 bytes .../gb.form.stock/gambas/32/switch-user.png | Bin 0 -> 468 bytes comp/src/gb.form.stock/gambas/32/system.png | Bin 0 -> 283 bytes comp/src/gb.form.stock/gambas/32/tab.png | Bin 0 -> 206 bytes comp/src/gb.form.stock/gambas/32/table.png | Bin 0 -> 162 bytes comp/src/gb.form.stock/gambas/32/tablet.png | Bin 0 -> 295 bytes comp/src/gb.form.stock/gambas/32/tag.png | Bin 0 -> 265 bytes comp/src/gb.form.stock/gambas/32/taxi.png | Bin 0 -> 338 bytes comp/src/gb.form.stock/gambas/32/template.png | Bin 0 -> 255 bytes comp/src/gb.form.stock/gambas/32/terminal.png | Bin 0 -> 208 bytes .../gb.form.stock/gambas/32/text-baseline.png | Bin 0 -> 173 bytes .../gb.form.stock/gambas/32/text-bigger.png | Bin 0 -> 818 bytes .../src/gb.form.stock/gambas/32/text-bold.png | Bin 0 -> 727 bytes .../gb.form.stock/gambas/32/text-bottom.png | Bin 0 -> 173 bytes .../gb.form.stock/gambas/32/text-center.png | Bin 0 -> 136 bytes .../src/gb.form.stock/gambas/32/text-fill.png | Bin 0 -> 130 bytes .../gb.form.stock/gambas/32/text-italic.png | Bin 0 -> 734 bytes .../src/gb.form.stock/gambas/32/text-left.png | Bin 0 -> 134 bytes .../gambas/32/text-line-spacing.png | Bin 0 -> 229 bytes .../gambas/32/text-list-order-rtl.png | Bin 0 -> 365 bytes .../gambas/32/text-list-order.png | Bin 0 -> 367 bytes .../src/gb.form.stock/gambas/32/text-list.png | Bin 0 -> 172 bytes .../gb.form.stock/gambas/32/text-lower.png | Bin 0 -> 908 bytes .../gb.form.stock/gambas/32/text-middle.png | Bin 0 -> 186 bytes .../gb.form.stock/gambas/32/text-outline.png | Bin 0 -> 1035 bytes .../gb.form.stock/gambas/32/text-right.png | Bin 0 -> 136 bytes .../gb.form.stock/gambas/32/text-shadow.png | Bin 0 -> 1185 bytes .../gb.form.stock/gambas/32/text-smaller.png | Bin 0 -> 817 bytes .../gb.form.stock/gambas/32/text-strike.png | Bin 0 -> 693 bytes comp/src/gb.form.stock/gambas/32/text-sub.png | Bin 0 -> 823 bytes .../gb.form.stock/gambas/32/text-super.png | Bin 0 -> 817 bytes comp/src/gb.form.stock/gambas/32/text-top.png | Bin 0 -> 174 bytes .../gambas/32/text-underline.png | Bin 0 -> 741 bytes .../gb.form.stock/gambas/32/text-upper.png | Bin 0 -> 777 bytes comp/src/gb.form.stock/gambas/32/text.png | Bin 0 -> 177 bytes comp/src/gb.form.stock/gambas/32/theme.png | Bin 0 -> 180 bytes comp/src/gb.form.stock/gambas/32/today.png | Bin 0 -> 284 bytes comp/src/gb.form.stock/gambas/32/toilet.png | Bin 0 -> 697 bytes comp/src/gb.form.stock/gambas/32/tools.png | Bin 0 -> 341 bytes comp/src/gb.form.stock/gambas/32/top.png | Bin 0 -> 232 bytes comp/src/gb.form.stock/gambas/32/town.png | Bin 0 -> 234 bytes comp/src/gb.form.stock/gambas/32/traffic.png | Bin 0 -> 350 bytes comp/src/gb.form.stock/gambas/32/train.png | Bin 0 -> 404 bytes .../gb.form.stock/gambas/32/transparency.png | Bin 0 -> 447 bytes .../gb.form.stock/gambas/32/trash-full.png | Bin 0 -> 285 bytes comp/src/gb.form.stock/gambas/32/trash.png | Bin 0 -> 197 bytes comp/src/gb.form.stock/gambas/32/truck.png | Bin 0 -> 381 bytes .../gb.form.stock/gambas/32/uncompress.png | Bin 0 -> 319 bytes comp/src/gb.form.stock/gambas/32/undo.png | Bin 0 -> 306 bytes comp/src/gb.form.stock/gambas/32/ungroup.png | Bin 0 -> 321 bytes comp/src/gb.form.stock/gambas/32/unindent.png | Bin 0 -> 192 bytes comp/src/gb.form.stock/gambas/32/union.png | Bin 0 -> 164 bytes comp/src/gb.form.stock/gambas/32/unlock.png | Bin 0 -> 280 bytes comp/src/gb.form.stock/gambas/32/unselect.png | Bin 0 -> 357 bytes comp/src/gb.form.stock/gambas/32/up.png | Bin 0 -> 219 bytes comp/src/gb.form.stock/gambas/32/update.png | Bin 0 -> 359 bytes comp/src/gb.form.stock/gambas/32/upload.png | Bin 0 -> 612 bytes comp/src/gb.form.stock/gambas/32/usb.png | Bin 0 -> 244 bytes .../gb.form.stock/gambas/32/user-group.png | Bin 0 -> 1264 bytes comp/src/gb.form.stock/gambas/32/user.png | Bin 0 -> 530 bytes comp/src/gb.form.stock/gambas/32/vector.png | Bin 0 -> 309 bytes .../gambas/32/version-control.png | Bin 0 -> 377 bytes comp/src/gb.form.stock/gambas/32/video.png | Bin 0 -> 199 bytes .../gb.form.stock/gambas/32/view-compact.png | Bin 0 -> 140 bytes .../gb.form.stock/gambas/32/view-detail.png | Bin 0 -> 130 bytes .../src/gb.form.stock/gambas/32/view-icon.png | Bin 0 -> 131 bytes .../gb.form.stock/gambas/32/view-normal.png | Bin 0 -> 302 bytes .../gb.form.stock/gambas/32/view-preview.png | Bin 0 -> 242 bytes .../gb.form.stock/gambas/32/view-split-h.png | Bin 0 -> 136 bytes .../gb.form.stock/gambas/32/view-split-v.png | Bin 0 -> 136 bytes .../src/gb.form.stock/gambas/32/view-tree.png | Bin 0 -> 150 bytes .../gb.form.stock/gambas/32/volume-high.png | Bin 0 -> 595 bytes .../gb.form.stock/gambas/32/volume-low.png | Bin 0 -> 617 bytes .../gb.form.stock/gambas/32/volume-medium.png | Bin 0 -> 627 bytes comp/src/gb.form.stock/gambas/32/volume.png | Bin 0 -> 258 bytes comp/src/gb.form.stock/gambas/32/vpn.png | Bin 0 -> 324 bytes comp/src/gb.form.stock/gambas/32/walk.png | Bin 0 -> 878 bytes comp/src/gb.form.stock/gambas/32/warning.png | Bin 0 -> 299 bytes comp/src/gb.form.stock/gambas/32/watch.png | Bin 0 -> 543 bytes comp/src/gb.form.stock/gambas/32/webcam.png | Bin 0 -> 539 bytes .../src/gb.form.stock/gambas/32/wifi-high.png | Bin 0 -> 605 bytes comp/src/gb.form.stock/gambas/32/wifi-low.png | Bin 0 -> 632 bytes .../gb.form.stock/gambas/32/wifi-medium.png | Bin 0 -> 640 bytes comp/src/gb.form.stock/gambas/32/wifi.png | Bin 0 -> 605 bytes comp/src/gb.form.stock/gambas/32/window.png | Bin 0 -> 161 bytes comp/src/gb.form.stock/gambas/32/windows.png | Bin 0 -> 1641 bytes comp/src/gb.form.stock/gambas/32/wizard.png | Bin 0 -> 278 bytes comp/src/gb.form.stock/gambas/32/worship.png | Bin 0 -> 242 bytes comp/src/gb.form.stock/gambas/32/wrap.png | Bin 0 -> 273 bytes comp/src/gb.form.stock/gambas/32/xml.png | Bin 0 -> 451 bytes comp/src/gb.form.stock/gambas/32/zoom-fit.png | Bin 0 -> 554 bytes comp/src/gb.form.stock/gambas/32/zoom-in.png | Bin 0 -> 453 bytes .../gambas/32/zoom-normal-rtl.png | Bin 0 -> 494 bytes .../gb.form.stock/gambas/32/zoom-normal.png | Bin 0 -> 503 bytes comp/src/gb.form.stock/gambas/32/zoom-out.png | Bin 0 -> 450 bytes .../gb.form.stock/gambas/32/zoom-width.png | Bin 0 -> 603 bytes comp/src/gb.form.stock/gambas/app/gambas3.png | Bin 0 -> 6834 bytes comp/src/gb.form.stock/icon.map | 1 + comp/src/gb.form.stock/links | 171 + comp/src/gb.form.stock/reverse | 116 + comp/src/gb.form.terminal/.component | 6 + comp/src/gb.form.terminal/.directory | 2 + .../.hidden/Konsole keys README.txt | 73 + .../gb.form.terminal/.hidden/Konsole keys.txt | 175 + .../.hidden/XTerm control sequences.txt | 1849 + .../.hidden/XtermTests/16colors.sh | 119 + .../.hidden/XtermTests/256colors.pl | 51 + .../.hidden/XtermTests/256colors2.pl | 180 + .../.hidden/XtermTests/88colors.pl | 49 + .../.hidden/XtermTests/88colors2.pl | 182 + .../.hidden/XtermTests/8colors.sh | 107 + .../.hidden/XtermTests/acolors.sh | 93 + .../.hidden/XtermTests/doublechars.sh | 116 + .../.hidden/XtermTests/dynamic.pl | 132 + .../.hidden/XtermTests/dynamic.sh | 89 + .../.hidden/XtermTests/dynamic2.sh | 114 + .../.hidden/XtermTests/fonts.sh | 88 + .../.hidden/XtermTests/paste64.pl | 190 + .../.hidden/XtermTests/query-color.pl | 169 + .../.hidden/XtermTests/query-fonts.pl | 167 + .../.hidden/XtermTests/resize.pl | 141 + .../.hidden/XtermTests/resize.sh | 111 + .../.hidden/XtermTests/tcapquery.pl | 325 + .../.hidden/XtermTests/title.sh | 84 + comp/src/gb.form.terminal/.hidden/XtermVT100 | 457 + .../.hidden/control/terminalview.png | Bin 0 -> 398 bytes comp/src/gb.form.terminal/.icon.png | Bin 0 -> 5233 bytes comp/src/gb.form.terminal/.lang/fr.po | 39 + comp/src/gb.form.terminal/.lang/it.po | 40 + comp/src/gb.form.terminal/.lang/pt_BR.po | 112 + comp/src/gb.form.terminal/.lang/ru.po | 42 + comp/src/gb.form.terminal/.lang/zh.po | 111 + comp/src/gb.form.terminal/.project | 18 + .../.src/TerminalView/CTerminalLine.class | 200 + .../.src/TerminalView/TerminalAttr.class | 113 + .../.src/TerminalView/TerminalFilter.class | 25 + .../TerminalView/TerminalFilter_VT100.class | 1010 + .../.src/TerminalView/TerminalLink.class | 59 + .../.src/TerminalView/TerminalScreen.class | 2475 + .../.src/TerminalView/TerminalView.class | 1873 + .../.src/TerminalView/test/FOtherTest.class | 11 + .../.src/TerminalView/test/FOtherTest.form | 8 + .../TerminalView/test/FTestTerminalView.class | 144 + .../TerminalView/test/FTestTerminalView.form | 57 + .../.src/TerminalView/test/Form1.class | 44 + .../.src/TerminalView/test/Form1.form | 9 + .../.src/TerminalView/test/MTest.module | 8 + .../.src/TerminalView/test/PipeTest.class | 224 + .../.src/TerminalView/test/PipeTest.form | 75 + .../.src/TerminalView/test/PipedTask.class | 73 + .../TerminalView/test/TelNetProtocol.class | 139 + .../.src/TerminalView/test/VT100/Attr.class | 220 + .../TerminalView/test/VT100/Console.class | 63 + .../.src/TerminalView/test/VT100/Main.module | 38 + comp/src/gb.form.terminal/brush_dark.png | Bin 0 -> 85 bytes comp/src/gb.form.terminal/brush_light.png | Bin 0 -> 85 bytes comp/src/gb.form.terminal/brush_medium.png | Bin 0 -> 85 bytes comp/src/gb.form/.component | 5 + comp/src/gb.form/.directory | 2 + comp/src/gb.form/.hidden/CHANGELOG | 9 + .../gb.form/.hidden/FailedNewMaskBox.class | 829 + .../src/gb.form/.hidden/control/buttonbox.png | Bin 0 -> 258 bytes .../gb.form/.hidden/control/colorbutton.png | Bin 0 -> 404 bytes .../gb.form/.hidden/control/colorchooser.png | Bin 0 -> 845 bytes .../gb.form/.hidden/control/colorpalette.png | Bin 0 -> 592 bytes comp/src/gb.form/.hidden/control/datebox.png | Bin 0 -> 331 bytes .../gb.form/.hidden/control/datechooser.png | Bin 0 -> 309 bytes comp/src/gb.form/.hidden/control/dirbox.png | Bin 0 -> 248 bytes .../gb.form/.hidden/control/dirbrowser.png | Bin 0 -> 244 bytes .../gb.form/.hidden/control/dirchooser.png | Bin 0 -> 252 bytes comp/src/gb.form/.hidden/control/dirview.png | Bin 0 -> 151 bytes .../gb.form/.hidden/control/documentview.png | Bin 0 -> 721 bytes comp/src/gb.form/.hidden/control/expander.png | Bin 0 -> 233 bytes comp/src/gb.form/.hidden/control/filebox.png | Bin 0 -> 276 bytes .../gb.form/.hidden/control/filechooser.png | Bin 0 -> 318 bytes .../.hidden/control/fileproperties.png | Bin 0 -> 265 bytes comp/src/gb.form/.hidden/control/fileview.png | Bin 0 -> 225 bytes comp/src/gb.form/.hidden/control/fontbox.png | Bin 0 -> 738 bytes .../gb.form/.hidden/control/fontchooser.png | Bin 0 -> 879 bytes .../src/gb.form/.hidden/control/iconpanel.png | Bin 0 -> 182 bytes .../src/gb.form/.hidden/control/imageview.png | Bin 0 -> 624 bytes comp/src/gb.form/.hidden/control/lcdlabel.png | Bin 0 -> 212 bytes .../gb.form/.hidden/control/listcontainer.png | Bin 0 -> 148 bytes .../gb.form/.hidden/control/listeditor.png | Bin 0 -> 884 bytes comp/src/gb.form/.hidden/control/maskbox.png | Bin 0 -> 615 bytes .../gb.form/.hidden/control/menubutton.png | Bin 0 -> 1070 bytes .../gb.form/.hidden/control/messageview.png | Bin 0 -> 219 bytes .../src/gb.form/.hidden/control/sidepanel.png | Bin 0 -> 228 bytes .../src/gb.form/.hidden/control/sliderbox.png | Bin 0 -> 651 bytes comp/src/gb.form/.hidden/control/spinbar.png | Bin 0 -> 328 bytes comp/src/gb.form/.hidden/control/spinner.png | Bin 0 -> 1130 bytes .../gb.form/.hidden/control/switchbutton.png | Bin 0 -> 644 bytes .../src/gb.form/.hidden/control/tableview.png | Bin 0 -> 902 bytes comp/src/gb.form/.hidden/control/tabpanel.png | Bin 0 -> 175 bytes comp/src/gb.form/.hidden/control/timebox.png | Bin 0 -> 588 bytes .../src/gb.form/.hidden/control/toolpanel.png | Bin 0 -> 184 bytes comp/src/gb.form/.hidden/control/urllabel.png | Bin 0 -> 713 bytes comp/src/gb.form/.hidden/control/valuebox.png | Bin 0 -> 894 bytes comp/src/gb.form/.hidden/control/wizard.png | Bin 0 -> 290 bytes comp/src/gb.form/.hidden/icon.png | Bin 0 -> 180 bytes comp/src/gb.form/.icon.png | Bin 0 -> 5355 bytes comp/src/gb.form/.lang/ar.po | 738 + comp/src/gb.form/.lang/ca.po | 748 + comp/src/gb.form/.lang/cs.po | 748 + comp/src/gb.form/.lang/de.po | 765 + comp/src/gb.form/.lang/es.po | 749 + comp/src/gb.form/.lang/es_ES.po | 749 + comp/src/gb.form/.lang/fa.po | 738 + comp/src/gb.form/.lang/fr.po | 508 + comp/src/gb.form/.lang/it.po | 464 + comp/src/gb.form/.lang/ja.po | 743 + comp/src/gb.form/.lang/nl.po | 763 + comp/src/gb.form/.lang/pt_BR.po | 747 + comp/src/gb.form/.lang/ru.po | 753 + comp/src/gb.form/.lang/sv.po | 742 + comp/src/gb.form/.lang/zh.po | 747 + comp/src/gb.form/.lang/zh_TW.po | 710 + comp/src/gb.form/.project | 31 + comp/src/gb.form/.src/Balloon/Balloon.class | 89 + comp/src/gb.form/.src/Balloon/FBalloon.class | 421 + comp/src/gb.form/.src/Balloon/FBalloon.form | 26 + comp/src/gb.form/.src/Button/ButtonBox.class | 430 + comp/src/gb.form/.src/Button/MenuButton.class | 586 + .../gb.form/.src/Button/SwitchButton.class | 253 + comp/src/gb.form/.src/Color/ColorButton.class | 151 + .../src/gb.form/.src/Color/ColorChooser.class | 113 + .../src/gb.form/.src/Color/ColorPalette.class | 394 + .../gb.form/.src/Color/FColorChooser.class | 622 + .../src/gb.form/.src/Color/FColorChooser.form | 199 + comp/src/gb.form/.src/Completion.class | 342 + comp/src/gb.form/.src/Date/DateBox.class | 304 + comp/src/gb.form/.src/Date/DateChooser.class | 278 + comp/src/gb.form/.src/Date/FCalendar.class | 1025 + comp/src/gb.form/.src/Date/FCalendar.form | 118 + comp/src/gb.form/.src/Date/TimeBox.class | 218 + .../.src/Date/_DateChooser_Colors.class | 26 + .../gb.form/.src/Date/_DateChooser_Data.class | 27 + .../gb.form/.src/Date/_DateChooser_Date.class | 68 + .../.src/DocumentView/DocumentView.class | 1082 + .../.src/DocumentView/_DocumentItem.class | 50 + .../.src/DocumentView/_DocumentLayout.class | 14 + comp/src/gb.form/.src/Expander/Expander.class | 100 + .../src/gb.form/.src/Expander/FExpander.class | 218 + comp/src/gb.form/.src/Expander/FExpander.form | 22 + comp/src/gb.form/.src/FInputBox.class | 56 + comp/src/gb.form/.src/FInputBox.form | 39 + comp/src/gb.form/.src/File/Bookmark.class | 11 + .../.src/File/Bookmark/CBookmark.class | 54 + .../.src/File/Bookmark/CBookmarkList.class | 201 + .../.src/File/Bookmark/FEditBookmark.class | 140 + .../.src/File/Bookmark/FEditBookmark.form | 50 + comp/src/gb.form/.src/File/Bookmarks.class | 108 + comp/src/gb.form/.src/File/CTaskPreview.class | 532 + .../gb.form/.src/File/Chooser/DirButton.class | 167 + .../.src/File/Chooser/DirChooser.class | 313 + .../.src/File/Chooser/FDirChooser.class | 1839 + .../.src/File/Chooser/FDirChooser.form | 215 + .../.src/File/Chooser/FileChooser.class | 471 + comp/src/gb.form/.src/File/DirBox.class | 94 + comp/src/gb.form/.src/File/DirBrowser.class | 143 + comp/src/gb.form/.src/File/DirCache.class | 163 + comp/src/gb.form/.src/File/DirView.class | 785 + comp/src/gb.form/.src/File/FileBox.class | 167 + comp/src/gb.form/.src/File/FileView.class | 1674 + .../.src/File/Properties/CTaskDirSize.class | 71 + .../File/Properties/FFileProperties.class | 471 + .../.src/File/Properties/FFileProperties.form | 213 + .../.src/File/Properties/FileProperties.class | 98 + .../File/Properties/_FilePropertiesData.class | 6 + comp/src/gb.form/.src/FileCompletion.class | 42 + comp/src/gb.form/.src/Font/FFontChooser.class | 919 + comp/src/gb.form/.src/Font/FFontChooser.form | 136 + comp/src/gb.form/.src/Font/FontBox.class | 114 + .../src/gb.form/.src/Font/FontCacheTask.class | 111 + comp/src/gb.form/.src/Font/FontChooser.class | 170 + comp/src/gb.form/.src/Help.module | 74 + .../gb.form/.src/IconPanel/IconPanel.class | 408 + .../.src/IconPanel/_IconPanelContainer.class | 105 + .../gb.form/.src/ImageView/ImageView.class | 414 + comp/src/gb.form/.src/InputBox.class | 9 + comp/src/gb.form/.src/LCDLabel.class | 455 + comp/src/gb.form/.src/ListContainer.class | 413 + .../gb.form/.src/ListEditor/FListEditor.class | 391 + .../gb.form/.src/ListEditor/FListEditor.form | 55 + .../gb.form/.src/ListEditor/ListEditor.class | 220 + comp/src/gb.form/.src/Main.module | 523 + comp/src/gb.form/.src/MaskBox.class | 933 + comp/src/gb.form/.src/MaskBox2.class | 62 + comp/src/gb.form/.src/Message/FMessage.class | 87 + comp/src/gb.form/.src/Message/FMessage.form | 43 + comp/src/gb.form/.src/Message/Message.module | 9 + .../gb.form/.src/Message/MessageView.class | 242 + comp/src/gb.form/.src/MessageLabel.class | 151 + .../.src/MultiContainer/_MultiContainer.class | 236 + .../MultiContainer/_MultiContainerTab.class | 66 + .../gb.form/.src/SidePanel/FSidePanel.class | 1260 + .../gb.form/.src/SidePanel/FSidePanel.form | 25 + .../gb.form/.src/SidePanel/SidePanel.class | 249 + comp/src/gb.form/.src/SliderBox.class | 217 + comp/src/gb.form/.src/SpinBar/SpinBar.class | 433 + comp/src/gb.form/.src/Spinner.class | 301 + comp/src/gb.form/.src/Stock.class | 878 + comp/src/gb.form/.src/TabPanel/TabPanel.class | 1129 + .../.src/TabPanel/_TabPanelButton.class | 581 + .../.src/TabPanel/_TabPanelContainer.class | 108 + comp/src/gb.form/.src/TableView.class | 562 + comp/src/gb.form/.src/Test/FBugFileView.class | 22 + comp/src/gb.form/.src/Test/FBugFileView.form | 27 + .../src/gb.form/.src/Test/FDocumentView.class | 137 + comp/src/gb.form/.src/Test/FDocumentView.form | 91 + comp/src/gb.form/.src/Test/FIconPanel.class | 9 + comp/src/gb.form/.src/Test/FIconPanel.form | 32 + comp/src/gb.form/.src/Test/FIconView.class | 30 + comp/src/gb.form/.src/Test/FIconView.form | 20 + comp/src/gb.form/.src/Test/FLCDLabel.class | 17 + comp/src/gb.form/.src/Test/FLCDLabel.form | 19 + comp/src/gb.form/.src/Test/FMain.class | 30 + comp/src/gb.form/.src/Test/FMain.form | 79 + comp/src/gb.form/.src/Test/FSpinBar.class | 1 + comp/src/gb.form/.src/Test/FSpinBar.form | 27 + .../gb.form/.src/Test/FTestArrangement.class | 14 + .../gb.form/.src/Test/FTestArrangement.form | 18 + comp/src/gb.form/.src/Test/FTestBalloon.class | 19 + comp/src/gb.form/.src/Test/FTestBalloon.form | 14 + .../gb.form/.src/Test/FTestColorChooser.class | 26 + .../gb.form/.src/Test/FTestColorChooser.form | 16 + .../gb.form/.src/Test/FTestCompletion.class | 24 + .../gb.form/.src/Test/FTestCompletion.form | 21 + .../gb.form/.src/Test/FTestDateChooser.class | 28 + .../gb.form/.src/Test/FTestDateChooser.form | 39 + .../gb.form/.src/Test/FTestDirBrowser.class | 8 + .../gb.form/.src/Test/FTestDirBrowser.form | 11 + .../src/gb.form/.src/Test/FTestExpander.class | 2 + comp/src/gb.form/.src/Test/FTestExpander.form | 22 + comp/src/gb.form/.src/Test/FTestFileBox.class | 2 + comp/src/gb.form/.src/Test/FTestFileBox.form | 27 + .../gb.form/.src/Test/FTestFileChooser.class | 53 + .../gb.form/.src/Test/FTestFileChooser.form | 77 + .../src/gb.form/.src/Test/FTestFileView.class | 21 + comp/src/gb.form/.src/Test/FTestFileView.form | 18 + comp/src/gb.form/.src/Test/FTestFontBox.class | 2 + comp/src/gb.form/.src/Test/FTestFontBox.form | 11 + .../gb.form/.src/Test/FTestFontChooser.class | 8 + .../gb.form/.src/Test/FTestFontChooser.form | 10 + .../gb.form/.src/Test/FTestImageView.class | 52 + .../src/gb.form/.src/Test/FTestImageView.form | 5 + .../gb.form/.src/Test/FTestListEditor.class | 14 + .../gb.form/.src/Test/FTestListEditor.form | 10 + comp/src/gb.form/.src/Test/FTestMaskBox.class | 56 + comp/src/gb.form/.src/Test/FTestMaskBox.form | 9 + .../gb.form/.src/Test/FTestMenuButton.class | 14 + .../gb.form/.src/Test/FTestMenuButton.form | 104 + .../gb.form/.src/Test/FTestMessageView.class | 14 + .../gb.form/.src/Test/FTestMessageView.form | 29 + .../gb.form/.src/Test/FTestSidePanel.class | 27 + .../src/gb.form/.src/Test/FTestSidePanel.form | 49 + comp/src/gb.form/.src/Test/FTestSpinner.class | 15 + comp/src/gb.form/.src/Test/FTestSpinner.form | 20 + .../gb.form/.src/Test/FTestSwitchButton.class | 13 + .../gb.form/.src/Test/FTestSwitchButton.form | 34 + .../src/gb.form/.src/Test/FTestTabPanel.class | 92 + comp/src/gb.form/.src/Test/FTestTabPanel.form | 60 + .../gb.form/.src/Test/FTestTableView.class | 51 + .../src/gb.form/.src/Test/FTestTableView.form | 23 + comp/src/gb.form/.src/Test/FTestTimeBox.class | 2 + comp/src/gb.form/.src/Test/FTestTimeBox.form | 12 + .../gb.form/.src/Test/FTestToolPanel.class | 8 + .../src/gb.form/.src/Test/FTestToolPanel.form | 22 + .../src/gb.form/.src/Test/FTestValueBox.class | 14 + comp/src/gb.form/.src/Test/FTestValueBox.form | 25 + comp/src/gb.form/.src/Test/FTestWizard.class | 8 + comp/src/gb.form/.src/Test/FTestWizard.form | 30 + comp/src/gb.form/.src/Test/FWiki.class | 10 + comp/src/gb.form/.src/Test/FWiki.form | 27 + comp/src/gb.form/.src/Test/Form1.class | 33 + comp/src/gb.form/.src/Test/Form1.form | 32 + comp/src/gb.form/.src/Test/Form2.class | 87 + comp/src/gb.form/.src/Test/Form2.form | 100 + comp/src/gb.form/.src/Test/Form3.class | 24 + comp/src/gb.form/.src/Test/Form3.form | 19 + comp/src/gb.form/.src/Test/Form4.class | 10 + comp/src/gb.form/.src/Test/Form4.form | 15 + comp/src/gb.form/.src/TestControl.class | 11 + .../src/gb.form/.src/ToolPanel/FToolBar.class | 73 + comp/src/gb.form/.src/ToolPanel/FToolBar.form | 15 + .../gb.form/.src/ToolPanel/ToolPanel.class | 327 + .../.src/ToolPanel/_ToolPanelContainer.class | 110 + comp/src/gb.form/.src/URLLabel.class | 131 + comp/src/gb.form/.src/ValueBox.class | 404 + comp/src/gb.form/.src/Wizard/FWizard.class | 150 + comp/src/gb.form/.src/Wizard/FWizard.form | 50 + comp/src/gb.form/.src/Wizard/Wizard.class | 465 + .../.src/Wizard/_WizardContainer.class | 56 + comp/src/gb.form/img/16/cross.png | Bin 0 -> 115 bytes comp/src/gb.form/img/32/filter-menu.png | Bin 0 -> 533 bytes comp/src/gb.form/img/32/filter.png | Bin 0 -> 518 bytes comp/src/gb.form/img/32/warning.png | Bin 0 -> 1932 bytes .../gb.form/img/8/new/side-bottom-void.png | Bin 0 -> 117 bytes comp/src/gb.form/img/8/new/side-bottom.png | Bin 0 -> 111 bytes comp/src/gb.form/img/8/new/side-left.png | Bin 0 -> 112 bytes .../src/gb.form/img/8/new/side-right-void.png | Bin 0 -> 115 bytes comp/src/gb.form/img/8/new/side-right.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/new/side-top.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-bottom-void.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-bottom.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-left.png | Bin 0 -> 113 bytes comp/src/gb.form/img/8/side-right-void.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-right.png | Bin 0 -> 109 bytes comp/src/gb.form/img/8/side-top.png | Bin 0 -> 115 bytes comp/src/gb.form/img/colormap-snap.png | Bin 0 -> 1292 bytes comp/src/gb.form/img/colormap.png | Bin 0 -> 8985 bytes comp/src/gb.form/img/cross.png | Bin 0 -> 299 bytes comp/src/gb.form/img/emblem/lock.png | 1 + comp/src/gb.form/img/handle-h.png | Bin 0 -> 102 bytes comp/src/gb.form/img/handle-v.png | Bin 0 -> 97 bytes comp/src/gb.form/img/line.png | Bin 0 -> 110 bytes comp/src/gb.form/img/lock.png | Bin 0 -> 724 bytes comp/src/gb.form/img/page.png | Bin 0 -> 137 bytes comp/src/gb.form/img/round.png | Bin 0 -> 124 bytes comp/src/gb.form/img/select-dark.png | Bin 0 -> 112 bytes comp/src/gb.form/img/select.png | Bin 0 -> 114 bytes comp/src/gb.form/img/unknown.svg | 72 + comp/src/gb.form/img/valuemap-snap.png | Bin 0 -> 149 bytes comp/src/gb.form/img/valuemap.png | Bin 0 -> 897 bytes comp/src/gb.form/map/icon.map | 471 + comp/src/gb.form/stock/16/gambas.png | Bin 0 -> 489 bytes comp/src/gb.form/stock/16/gnu.png | Bin 0 -> 360 bytes comp/src/gb.form/stock/16/linux.png | Bin 0 -> 638 bytes comp/src/gb.form/stock/32/gambas.png | Bin 0 -> 925 bytes comp/src/gb.form/stock/32/gnu.png | Bin 0 -> 1022 bytes comp/src/gb.form/stock/32/linux.png | Bin 0 -> 1446 bytes comp/src/gb.form/stock/scalable/gambas.svg | 91 + comp/src/gb.form/stock/scalable/gnu.svg | 94 + comp/src/gb.form/stock/scalable/linux.svg | 1 + comp/src/gb.gui.base/.component | 5 + comp/src/gb.gui.base/.directory | 2 + comp/src/gb.gui.base/.hidden/window.png | Bin 0 -> 205 bytes comp/src/gb.gui.base/.icon.png | Bin 0 -> 5013 bytes comp/src/gb.gui.base/.lang/de.po | 56 + comp/src/gb.gui.base/.lang/es.po | 57 + comp/src/gb.gui.base/.lang/fr.po | 391 + comp/src/gb.gui.base/.lang/it.po | 56 + comp/src/gb.gui.base/.lang/zh.po | 244 + comp/src/gb.gui.base/.project | 16 + comp/src/gb.gui.base/.src/Action.class | 576 + comp/src/gb.gui.base/.src/Border.class | 861 + comp/src/gb.gui.base/.src/Clipboard.class | 32 + .../gb.gui.base/.src/ComboBox/ComboBox.class | 696 + .../.src/ComboBox/FComboBoxPopup.class | 134 + .../.src/ComboBox/FComboBoxPopup.form | 12 + .../.src/ComboBox/_ComboBox_Item.class | 33 + .../.src/ComboBox/_ComboBox_Selection.class | 53 + comp/src/gb.gui.base/.src/Desktop.class | 82 + comp/src/gb.gui.base/.src/Drag.class | 35 + comp/src/gb.gui.base/.src/Draw.module | 674 + comp/src/gb.gui.base/.src/Fill.class | 5 + comp/src/gb.gui.base/.src/Frame.class | 274 + .../gb.gui.base/.src/GridView/GridView.class | 2556 ++ .../.src/GridView/GridViewSelection.class | 197 + .../.src/GridView/_GridView_Cell.class | 272 + .../.src/GridView/_GridView_Column.class | 175 + .../.src/GridView/_GridView_Columns.class | 268 + .../.src/GridView/_GridView_Data.class | 45 + .../.src/GridView/_GridView_Row.class | 115 + .../.src/GridView/_GridView_Rows.class | 583 + .../gb.gui.base/.src/IconView/IconView.class | 1856 + .../.src/IconView/_IconView_Item.class | 797 + comp/src/gb.gui.base/.src/Label.class | 422 + comp/src/gb.gui.base/.src/Line.class | 5 + .../gb.gui.base/.src/ListBox/ListBox.class | 526 + .../.src/ListBox/_ListBox_Item.class | 47 + comp/src/gb.gui.base/.src/Main.module | 283 + .../gb.gui.base/.src/Message/FMessage.class | 241 + .../gb.gui.base/.src/Message/FMessage.form | 71 + .../gb.gui.base/.src/Message/Message.class | 65 + comp/src/gb.gui.base/.src/MovieBox.class | 381 + comp/src/gb.gui.base/.src/Paint.class | 547 + comp/src/gb.gui.base/.src/Picture.class | 299 + comp/src/gb.gui.base/.src/PictureBox.class | 333 + comp/src/gb.gui.base/.src/ProgressBar.class | 209 + comp/src/gb.gui.base/.src/ScrollArea.class | 1108 + comp/src/gb.gui.base/.src/ScrollView.class | 154 + comp/src/gb.gui.base/.src/Separator.class | 38 + comp/src/gb.gui.base/.src/Shortcut.class | 60 + comp/src/gb.gui.base/.src/SpinBox.class | 737 + comp/src/gb.gui.base/.src/Split/HSplit.class | 11 + .../src/gb.gui.base/.src/Split/Splitter.class | 556 + comp/src/gb.gui.base/.src/Split/VSplit.class | 11 + comp/src/gb.gui.base/.src/Spring.class | 15 + comp/src/gb.gui.base/.src/Test/FBorder.class | 28 + comp/src/gb.gui.base/.src/Test/FBorder.form | 16 + comp/src/gb.gui.base/.src/Test/FListBox.class | 46 + comp/src/gb.gui.base/.src/Test/FListBox.form | 39 + comp/src/gb.gui.base/.src/Test/FMain.class | 35 + comp/src/gb.gui.base/.src/Test/FMain.form | 24 + comp/src/gb.gui.base/.src/Test/FPaint.class | 30 + comp/src/gb.gui.base/.src/Test/FPaint.form | 10 + .../gb.gui.base/.src/Test/FPictureBox.class | 2 + .../gb.gui.base/.src/Test/FPictureBox.form | 9 + .../gb.gui.base/.src/Test/FRightToLeft.class | 34 + .../gb.gui.base/.src/Test/FRightToLeft.form | 61 + .../gb.gui.base/.src/Test/FScrollArea.class | 45 + .../gb.gui.base/.src/Test/FScrollArea.form | 220 + .../gb.gui.base/.src/Test/FTestClipping.class | 36 + .../gb.gui.base/.src/Test/FTestClipping.form | 10 + .../gb.gui.base/.src/Test/FTestCombo.class | 54 + .../src/gb.gui.base/.src/Test/FTestCombo.form | 98 + .../gb.gui.base/.src/Test/FTestFrame.class | 20 + .../src/gb.gui.base/.src/Test/FTestFrame.form | 22 + .../gb.gui.base/.src/Test/FTestGridView.class | 211 + .../gb.gui.base/.src/Test/FTestGridView.form | 57 + .../gb.gui.base/.src/Test/FTestIconView.class | 55 + .../gb.gui.base/.src/Test/FTestIconView.form | 31 + .../gb.gui.base/.src/Test/FTestLabel.class | 14 + .../src/gb.gui.base/.src/Test/FTestLabel.form | 74 + .../gb.gui.base/.src/Test/FTestListView.class | 61 + .../gb.gui.base/.src/Test/FTestListView.form | 21 + .../.src/Test/FTestMouseWheel.class | 20 + .../.src/Test/FTestMouseWheel.form | 33 + .../gb.gui.base/.src/Test/FTestMyCombo.class | 38 + .../gb.gui.base/.src/Test/FTestMyCombo.form | 27 + .../.src/Test/FTestProgressBar.class | 21 + .../.src/Test/FTestProgressBar.form | 23 + .../gb.gui.base/.src/Test/FTestSpinBox.class | 2 + .../gb.gui.base/.src/Test/FTestSpinBox.form | 26 + .../gb.gui.base/.src/Test/FTestSplit.class | 22 + .../src/gb.gui.base/.src/Test/FTestSplit.form | 43 + .../gb.gui.base/.src/Test/FTestTreeView.class | 185 + .../gb.gui.base/.src/Test/FTestTreeView.form | 79 + .../.src/Test/FileView/CTaskPreview.class | 98 + .../.src/Test/FileView/DirCache.class | 159 + .../.src/Test/FileView/FTestFileView.class | 14 + .../.src/Test/FileView/FTestFileView.form | 17 + .../.src/Test/FileView/FileView.class | 1126 + .../.src/Test/FileView/Help.module | 74 + .../gb.gui.base/.src/Test/TestMessage.module | 21 + comp/src/gb.gui.base/.src/TestCursor.class | 82 + comp/src/gb.gui.base/.src/TextLabel.class | 39 + .../.src/TreeView/ColumnView.class | 232 + .../gb.gui.base/.src/TreeView/ListView.class | 17 + .../gb.gui.base/.src/TreeView/RenameBox.class | 84 + .../gb.gui.base/.src/TreeView/TreeView.class | 50 + .../.src/TreeView/TreeViewSelection.class | 133 + .../.src/TreeView/_ColumnView_Columns.class | 79 + .../gb.gui.base/.src/TreeView/_TreeView.class | 2025 + .../.src/TreeView/_TreeView_Item.class | 692 + comp/src/gb.gui.base/.src/_Draw_Clip.class | 76 + comp/src/gb.gui.base/.src/_Draw_Style.class | 55 + comp/src/gb.gui.base/.src/_Gui.class | 413 + comp/src/gb.gui.base/broken.svg | 122 + comp/src/gb.gui.base/message/delete.png | 1 + comp/src/gb.gui.base/message/error.png | 1 + comp/src/gb.gui.base/message/info.png | 1 + comp/src/gb.gui.base/message/question.png | 1 + comp/src/gb.gui.base/message/warning.png | 1 + comp/src/gb.gui.base/moviebox.png | 1 + comp/src/gb.gui.base/pattern/10.png | Bin 0 -> 69 bytes comp/src/gb.gui.base/pattern/11.png | Bin 0 -> 70 bytes comp/src/gb.gui.base/pattern/12.png | Bin 0 -> 82 bytes comp/src/gb.gui.base/pattern/13.png | Bin 0 -> 82 bytes comp/src/gb.gui.base/pattern/14.png | Bin 0 -> 78 bytes comp/src/gb.gui.base/pattern/2.png | Bin 0 -> 71 bytes comp/src/gb.gui.base/pattern/3.png | Bin 0 -> 74 bytes comp/src/gb.gui.base/pattern/4.png | Bin 0 -> 74 bytes comp/src/gb.gui.base/pattern/5.png | Bin 0 -> 71 bytes comp/src/gb.gui.base/pattern/6.png | Bin 0 -> 76 bytes comp/src/gb.gui.base/pattern/7.png | Bin 0 -> 76 bytes comp/src/gb.gui.base/pattern/8.png | Bin 0 -> 73 bytes comp/src/gb.gui.base/pattern/9.png | Bin 0 -> 70 bytes comp/src/gb.gui.base/picturebox.png | 1 + comp/src/gb.gui.base/spinbox/minus.png | Bin 0 -> 129 bytes comp/src/gb.gui.base/spinbox/plus.png | Bin 0 -> 135 bytes comp/src/gb.logging/.component | 4 + comp/src/gb.logging/.directory | 2 + comp/src/gb.logging/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.logging/.project | 11 + comp/src/gb.logging/.src/ComplexLogger.class | 118 + comp/src/gb.logging/.src/ConsoleHandler.class | 50 + comp/src/gb.logging/.src/FileHandler.class | 91 + comp/src/gb.logging/.src/Formatter.module | 167 + comp/src/gb.logging/.src/LogHandler.class | 64 + comp/src/gb.logging/.src/LogLevel.module | 11 + comp/src/gb.logging/.src/LogRotator.module | 65 + comp/src/gb.logging/.src/Logger.class | 108 + comp/src/gb.logging/.src/MTest.module | 36 + comp/src/gb.map/.component | 7 + .../gb.map/.connection/Connection1.connection | 11 + comp/src/gb.map/.directory | 2 + comp/src/gb.map/.hidden/control/mapview.png | Bin 0 -> 2045 bytes comp/src/gb.map/.icon.png | Bin 0 -> 18789 bytes comp/src/gb.map/.project | 20 + comp/src/gb.map/.src/FCarto.class | 133 + comp/src/gb.map/.src/FCarto.form | 13 + comp/src/gb.map/.src/Map.class | 477 + comp/src/gb.map/.src/MapView.class | 426 + .../gb.map/.src/Shapes/_MapShapeItem.class | 109 + comp/src/gb.map/.src/Shapes/_ShapeItem.class | 3 + comp/src/gb.map/.src/Sprite.class | 119 + comp/src/gb.map/.src/Tests/FMain.class | 193 + comp/src/gb.map/.src/Tests/FMain.form | 35 + comp/src/gb.map/.src/Tests/FTestWmts.class | 24 + comp/src/gb.map/.src/Tests/FTestWmts.form | 8 + comp/src/gb.map/.src/Tests/Form1.class | 23 + comp/src/gb.map/.src/Tests/Form1.form | 21 + comp/src/gb.map/.src/Tests/Form2.class | 96 + comp/src/gb.map/.src/Tests/Form2.form | 17 + comp/src/gb.map/.src/Tests/Form3.class | 42 + comp/src/gb.map/.src/Tests/Form3.form | 8 + comp/src/gb.map/.src/Tests/Form4.class | 75 + comp/src/gb.map/.src/Tests/Form4.form | 10 + comp/src/gb.map/.src/Tests/Form5.class | 28 + comp/src/gb.map/.src/Tests/Form5.form | 8 + comp/src/gb.map/.src/Tests/Form6.class | 40 + comp/src/gb.map/.src/Tests/Form6.form | 10 + comp/src/gb.map/.src/Tests/Form7.class | 9 + comp/src/gb.map/.src/Tests/Form7.form | 8 + comp/src/gb.map/.src/Tests/MMain.module | 11 + .../src/gb.map/.src/Tests/_BalloonLayer.class | 12 + comp/src/gb.map/.src/Tools/Geo.module | 131 + comp/src/gb.map/.src/Tools/MyPaint.class | 18 + comp/src/gb.map/.src/Tools/Proj.class | 84 + comp/src/gb.map/.src/Tools/_MapProxy.class | 20 + comp/src/gb.map/.src/Types/MapBounds.class | 143 + comp/src/gb.map/.src/Types/MapPoint.class | 211 + comp/src/gb.map/.src/Types/TileSource.module | 13 + comp/src/gb.map/.src/Types/_Tile.class | 16 + comp/src/gb.map/.src/_MapLayer.class | 97 + comp/src/gb.map/.src/_MapShape.class | 610 + comp/src/gb.map/.src/_MapTile.class | 770 + comp/src/gb.map/.src/_ViewLayer.class | 125 + comp/src/gb.map/Text1 | 10 + comp/src/gb.map/bar.png | Bin 0 -> 96 bytes comp/src/gb.map/cursor.png | Bin 0 -> 349 bytes comp/src/gb.map/minus.png | Bin 0 -> 283 bytes comp/src/gb.map/plus.png | Bin 0 -> 304 bytes comp/src/gb.map/point.png | Bin 0 -> 1010 bytes comp/src/gb.map/pointsparcelle | 0 comp/src/gb.markdown/.component | 4 + comp/src/gb.markdown/.directory | 2 + comp/src/gb.markdown/.icon.png | Bin 0 -> 10933 bytes comp/src/gb.markdown/.project | 10 + comp/src/gb.markdown/.src/MTest.module | 7 + comp/src/gb.markdown/.src/Markdown.class | 67 + comp/src/gb.markdown/.src/MarkdownLink.class | 10 + comp/src/gb.markdown/.src/Markup.module | 1096 + comp/src/gb.markdown/.src/TMarkdown.test | 424 + comp/src/gb.markdown/.test | 9 + comp/src/gb.markdown/test.txt | 43 + comp/src/gb.markdown/test2.txt | 44 + comp/src/gb.markdown/test3.txt | 45 + comp/src/gb.media.form/.component | 5 + comp/src/gb.media.form/.directory | 2 + .../.hidden/control/mediaview.png | Bin 0 -> 307 bytes comp/src/gb.media.form/.icon.png | Bin 0 -> 6679 bytes comp/src/gb.media.form/.project | 14 + .../src/gb.media.form/.src/FMediaPlayer.class | 485 + comp/src/gb.media.form/.src/FMediaPlayer.form | 72 + comp/src/gb.media.form/.src/FTest.class | 20 + comp/src/gb.media.form/.src/FTest.form | 25 + comp/src/gb.media.form/.src/MediaView.class | 182 + comp/src/gb.memcached/.component | 6 + comp/src/gb.memcached/.directory | 2 + comp/src/gb.memcached/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.memcached/.project | 14 + comp/src/gb.memcached/.src/FMain.class | 51 + comp/src/gb.memcached/.src/FMain.form | 76 + comp/src/gb.memcached/.src/Main.module | 57 + comp/src/gb.memcached/.src/Memcached.class | 414 + .../gb.memcached/.src/_Memcached_Key.class | 130 + comp/src/gb.mysql/.component | 6 + comp/src/gb.mysql/.directory | 2 + comp/src/gb.mysql/.icon.png | Bin 0 -> 10724 bytes comp/src/gb.mysql/.lang/es.po | 34 + comp/src/gb.mysql/.project | 18 + comp/src/gb.mysql/.src/Connection.class | 12 + comp/src/gb.mysql/.src/DB.class | 12 + comp/src/gb.mysql/.src/_DataBase.class | 153 + comp/src/gb.mysql/.src/_DataTypes.class | 241 + comp/src/gb.mysql/.src/_Event.class | 51 + comp/src/gb.mysql/.src/_Field.class | 94 + .../gb.mysql/.src/_FieldEspecifications.class | 62 + comp/src/gb.mysql/.src/_Index.class | 50 + comp/src/gb.mysql/.src/_MySQL.class | 324 + comp/src/gb.mysql/.src/_Result.class | 124 + comp/src/gb.mysql/.src/_Routines.class | 83 + comp/src/gb.mysql/.src/_Table.class | 161 + .../src/gb.mysql/.src/_TableMaintenance.class | 71 + comp/src/gb.mysql/.src/_Trigger.class | 53 + comp/src/gb.mysql/.src/_User.class | 79 + comp/src/gb.mysql/.src/_Version.class | 34 + comp/src/gb.mysql/.src/_View.class | 43 + comp/src/gb.mysql/.src/modMain.module | 67 + comp/src/gb.mysql/logo.png | Bin 0 -> 6577 bytes comp/src/gb.net.pop3/.component | 5 + comp/src/gb.net.pop3/.directory | 2 + .../.hidden/control/pop3client.png | Bin 0 -> 937 bytes comp/src/gb.net.pop3/.icon.png | Bin 0 -> 8203 bytes comp/src/gb.net.pop3/.lang/cs.po | 44 + comp/src/gb.net.pop3/.lang/es.po | 19 + comp/src/gb.net.pop3/.lang/es_ES.po | 19 + comp/src/gb.net.pop3/.lang/it.po | 19 + comp/src/gb.net.pop3/.lang/nl.po | 20 + comp/src/gb.net.pop3/.lang/ru.po | 34 + comp/src/gb.net.pop3/.lang/zh.po | 20 + comp/src/gb.net.pop3/.project | 15 + comp/src/gb.net.pop3/.src/MTest.module | 54 + comp/src/gb.net.pop3/.src/Net.class | 18 + comp/src/gb.net.pop3/.src/POPClient.class | 72 + comp/src/gb.net.pop3/.src/Pop3Client.class | 530 + comp/src/gb.net.pop3/.src/SSLClient.class | 76 + comp/src/gb.net.pop3/.src/TCPClient.class | 102 + .../.src/_Pop3Client_Message.class | 144 + comp/src/gb.net.smtp/.component | 4 + comp/src/gb.net.smtp/.directory | 2 + .../.hidden/control/smtpclient.png | Bin 0 -> 895 bytes comp/src/gb.net.smtp/.icon.png | Bin 0 -> 8090 bytes comp/src/gb.net.smtp/.project | 12 + comp/src/gb.net.smtp/.src/Encode.module | 210 + comp/src/gb.net.smtp/.src/Main.module | 68 + comp/src/gb.net.smtp/.src/Net.class | 7 + comp/src/gb.net.smtp/.src/SmtpClient.class | 601 + comp/src/gb.net.smtp/.src/SmtpPart.class | 126 + comp/src/gb.net.smtp/.src/SmtpSession.class | 147 + comp/src/gb.net.smtp/.src/SslSession.class | 76 + comp/src/gb.net.smtp/.src/TcpSession.class | 89 + comp/src/gb.net.smtp/.src/TlsSession.class | 77 + comp/src/gb.report/.component | 7 + .../.connection/Connection1.connection | 10 + .../.connection/Connection2.connection | 10 + .../gb.report/.connection/MainConn.connection | 9 + comp/src/gb.report/.dir_icon.png | Bin 0 -> 1571 bytes comp/src/gb.report/.directory | 2 + .../.hidden/control/reportdrawingarea.png | Bin 0 -> 621 bytes .../.hidden/control/reportgridview.png | Bin 0 -> 139 bytes .../gb.report/.hidden/control/reporthbox.png | Bin 0 -> 153 bytes .../gb.report/.hidden/control/reportimage.png | Bin 0 -> 1531 bytes .../gb.report/.hidden/control/reportlabel.png | Bin 0 -> 449 bytes .../gb.report/.hidden/control/reportline.png | Bin 0 -> 113 bytes .../.hidden/control/reportpagebreak.png | Bin 0 -> 158 bytes .../gb.report/.hidden/control/reportpanel.png | Bin 0 -> 154 bytes .../.hidden/control/reportsvgimage.png | Bin 0 -> 2284 bytes .../.hidden/control/reporttextlabel.png | Bin 0 -> 686 bytes .../gb.report/.hidden/control/reportvbox.png | Bin 0 -> 241 bytes .../gb.report/.hidden/control/reportview.png | Bin 0 -> 732 bytes .../.hidden/control/reportvpanel.png | Bin 0 -> 200 bytes comp/src/gb.report/.icon.png | Bin 0 -> 16142 bytes comp/src/gb.report/.lang/ca.po | 224 + comp/src/gb.report/.lang/cs.po | 233 + comp/src/gb.report/.lang/es.po | 423 + comp/src/gb.report/.lang/es_ES.po | 436 + comp/src/gb.report/.lang/fr.po | 454 + comp/src/gb.report/.lang/nl.po | 436 + comp/src/gb.report/.lang/ru.po | 450 + comp/src/gb.report/.lang/zh.po | 423 + comp/src/gb.report/.project | 25 + .../gb.report/.src/Borders/ReportBorder.class | 209 + .../.src/Borders/_ReportBorderSide.class | 16 + .../.src/Borders/_ReportRoundCorner.class | 136 + .../.src/BoxShadow/FReportBoxEditor.class | 26 + .../.src/BoxShadow/FReportBoxEditor.form | 11 + .../.src/BoxShadow/ReportBoxShadow.class | 222 + .../.src/BoxShadow/_ReportBoxShadow.class | 13 + .../gb.report/.src/Brush/ReportBrush.class | 222 + .../.src/Controls/ReportControl.class | 596 + .../gb.report/.src/MainTools/MReport.module | 26 + .../.src/MainTools/ReportUnits.module | 92 + .../.src/MainTools/Types/TControl.class | 76 + .../.src/MainTools/Types/TPageColumn.class | 8 + .../.src/MainTools/Types/TSizeHint.class | 5 + .../.src/MainTools/Types/TSizeParse.class | 70 + comp/src/gb.report/.src/Optional/Align.class | 10 + .../src/gb.report/.src/Optional/Arrange.class | 9 + comp/src/gb.report/.src/Optional/Line.class | 13 + .../.src/Padding/ReportPadding.class | 63 + comp/src/gb.report/.src/Preview/CPrint.class | 54 + .../src/gb.report/.src/Preview/FOptions.class | 9 + comp/src/gb.report/.src/Preview/FOptions.form | 52 + .../src/gb.report/.src/Preview/FPreview.class | 400 + comp/src/gb.report/.src/Preview/FPreview.form | 275 + comp/src/gb.report/.src/Preview/FPrint.class | 37 + comp/src/gb.report/.src/Preview/FPrint.form | 132 + comp/src/gb.report/.src/Preview/Form1.class | 30 + comp/src/gb.report/.src/Preview/Form1.form | 59 + .../gb.report/.src/Preview/ReportView.class | 709 + .../.src/Preview/ReportViewTask.class | 33 + comp/src/gb.report/.src/Report.class | 794 + comp/src/gb.report/.src/ReportContainer.class | 945 + .../gb.report/.src/ReportDrawingArea.class | 63 + comp/src/gb.report/.src/ReportFrame.class | 354 + comp/src/gb.report/.src/ReportGridView.class | 188 + .../ReportGridView/_ReportGridViewCell.class | 1 + .../_ReportGridViewColumn.class | 18 + .../_ReportGridViewColumns.class | 117 + .../ReportGridView/_ReportGridViewRow.class | 51 + .../ReportGridView/_ReportGridViewRows.class | 160 + comp/src/gb.report/.src/ReportHBox.class | 21 + comp/src/gb.report/.src/ReportImage.class | 186 + comp/src/gb.report/.src/ReportLabel.class | 226 + comp/src/gb.report/.src/ReportLine.class | 127 + comp/src/gb.report/.src/ReportPageBreak.class | 6 + comp/src/gb.report/.src/ReportPanel.class | 25 + comp/src/gb.report/.src/ReportSection.class | 23 + comp/src/gb.report/.src/ReportSvgImage.class | 185 + comp/src/gb.report/.src/ReportTextLabel.class | 97 + comp/src/gb.report/.src/ReportVBox.class | 33 + comp/src/gb.report/.src/ReportVPanel.class | 336 + .../gb.report/.src/Tests/Old/Report1.class | 8 + .../gb.report/.src/Tests/Old/Report1.report | 55 + .../gb.report/.src/Tests/Old/Report10.class | 8 + .../gb.report/.src/Tests/Old/Report10.report | 64 + .../gb.report/.src/Tests/Old/Report12.class | 16 + .../gb.report/.src/Tests/Old/Report12.report | 17 + .../gb.report/.src/Tests/Old/Report13.class | 38 + .../gb.report/.src/Tests/Old/Report13.report | 21 + .../gb.report/.src/Tests/Old/Report14.class | 32 + .../gb.report/.src/Tests/Old/Report14.report | 90 + .../gb.report/.src/Tests/Old/Report2.class | 20 + .../gb.report/.src/Tests/Old/Report2.report | 19 + .../gb.report/.src/Tests/Old/Report3.class | 20 + .../gb.report/.src/Tests/Old/Report3.report | 42 + .../gb.report/.src/Tests/Old/Report4.class | 46 + .../gb.report/.src/Tests/Old/Report4.report | 96 + .../gb.report/.src/Tests/Old/Report5.class | 1 + .../gb.report/.src/Tests/Old/Report5.report | 85 + .../gb.report/.src/Tests/Old/Report6.class | 2 + .../gb.report/.src/Tests/Old/Report6.report | 17 + .../gb.report/.src/Tests/Old/Report7.class | 29 + .../gb.report/.src/Tests/Old/Report7.report | 59 + .../gb.report/.src/Tests/Old/Report8.class | 56 + .../gb.report/.src/Tests/Old/Report8.report | 79 + .../gb.report/.src/Tests/Old/myReport1.class | 43 + .../gb.report/.src/Tests/Old/myReport1.report | 142 + .../gb.report/.src/Tests/Old/myReport2.class | 85 + .../gb.report/.src/Tests/Old/myReport2.report | 228 + .../gb.report/.src/Tests/Old/myReport5.class | 22 + .../gb.report/.src/Tests/Old/myReport5.report | 91 + .../gb.report/.src/Tests/OutputReport.class | 146 + .../gb.report/.src/Tests/OutputReport.report | 306 + .../gb.report/.src/Tests/OutputReport2.class | 166 + .../gb.report/.src/Tests/OutputReport2.report | 308 + comp/src/gb.report/.src/Tests/Paints.class | 33 + comp/src/gb.report/.src/Tests/Report11.class | 29 + comp/src/gb.report/.src/Tests/Report11.report | 29 + comp/src/gb.report/.src/Tests/Report41.class | 36 + comp/src/gb.report/.src/Tests/Report41.report | 34 + comp/src/gb.report/.src/Tests/Report51.class | 69 + comp/src/gb.report/.src/Tests/Report51.report | 15 + comp/src/gb.report/.src/Tests/Report52.class | 69 + comp/src/gb.report/.src/Tests/Report52.report | 15 + comp/src/gb.report/.src/Tests/Report9.class | 17 + comp/src/gb.report/.src/Tests/Report9.report | 22 + comp/src/gb.report/.src/Tests/Test.module | 9 + .../.src/Tests/rpTestShadowGrid.class | 23 + .../.src/Tests/rpTestShadowGrid.report | 25 + comp/src/gb.report/ChangeLog | 6 + comp/src/gb.report/gambas.svg | 540 + comp/src/gb.report/img/16/red-arrow-h.png | Bin 0 -> 117 bytes comp/src/gb.report/img/16/red-arrow-v.png | Bin 0 -> 120 bytes comp/src/gb.report/img/22/FullWidth.png | Bin 0 -> 139 bytes comp/src/gb.report/img/22/OnePage.png | Bin 0 -> 117 bytes comp/src/gb.report/img/22/RealSize.png | Bin 0 -> 204 bytes comp/src/gb.report/img/22/TwoPage.png | Bin 0 -> 121 bytes comp/src/gb.report/img/32/Collatecopie.png | Bin 0 -> 449 bytes comp/src/gb.report/img/32/Empty.png | Bin 0 -> 719 bytes comp/src/gb.report/img/32/grayscale.png | Bin 0 -> 1564 bytes comp/src/gb.report/img/32/reverse.png | Bin 0 -> 820 bytes comp/src/gb.report/img/control/hbox.png | Bin 0 -> 158 bytes comp/src/gb.report/img/control/label.png | Bin 0 -> 466 bytes comp/src/gb.report/img/control/picturebox.png | Bin 0 -> 2381 bytes comp/src/gb.report/img/control/vbox.png | Bin 0 -> 250 bytes comp/src/gb.report/img/control/vpanel.png | Bin 0 -> 200 bytes comp/src/gb.report/img/logo.svg | 540 + comp/src/gb.report/printer1.png | Bin 0 -> 1630 bytes comp/src/gb.report2/.component | 5 + .../.connection/Connection1.connection | 11 + .../.connection/Connection2.connection | 10 + .../.connection/Connection2.template | 398 + comp/src/gb.report2/.directory | 2 + .../.hidden/control/reportdrawingarea.png | 1 + .../.hidden/control/reportgridview.png | 1 + .../gb.report2/.hidden/control/reporthbox.png | 1 + .../.hidden/control/reportimage.png | 1 + .../.hidden/control/reportlabel.png | 1 + .../gb.report2/.hidden/control/reportline.png | Bin 0 -> 186 bytes .../.hidden/control/reportpagebreak.png | Bin 0 -> 391 bytes .../.hidden/control/reportpanel.png | 1 + .../.hidden/control/reportsvgimage.png | Bin 0 -> 383 bytes .../.hidden/control/reporttextlabel.png | 1 + .../gb.report2/.hidden/control/reportvbox.png | 1 + .../gb.report2/.hidden/control/reportview.png | 1 + .../.hidden/control/reportvpanel.png | 1 + comp/src/gb.report2/.icon.png | Bin 0 -> 6238 bytes comp/src/gb.report2/.lang/de.po | 222 + comp/src/gb.report2/.lang/es.po | 343 + comp/src/gb.report2/.lang/es_ES.po | 343 + comp/src/gb.report2/.lang/fr.po | 389 + comp/src/gb.report2/.lang/it.po | 211 + comp/src/gb.report2/.lang/nl.po | 212 + comp/src/gb.report2/.lang/pt_BR.po | 221 + comp/src/gb.report2/.lang/ru.po | 396 + comp/src/gb.report2/.lang/zh.po | 381 + comp/src/gb.report2/.project | 18 + .../gb.report2/.src/Evaluator/CResult.class | 55 + .../gb.report2/.src/Evaluator/_RepExp.class | 50 + comp/src/gb.report2/.src/Optional/Align.class | 10 + .../gb.report2/.src/Optional/Arrange.class | 11 + comp/src/gb.report2/.src/Optional/Line.class | 13 + comp/src/gb.report2/.src/Report.class | 802 + .../src/gb.report2/.src/ReportContainer.class | 1088 + comp/src/gb.report2/.src/ReportControl.class | 648 + .../gb.report2/.src/ReportDrawingArea.class | 96 + comp/src/gb.report2/.src/ReportFrame.class | 366 + .../.src/ReportGridView/ReportGridView.class | 52 + .../_ReportGridViewColumn.class | 96 + .../_ReportGridViewColumns.class | 141 + .../ReportGridView/_ReportGridViewData.class | 2 + .../ReportGridView/_ReportGridViewRow.class | 52 + .../ReportGridView/_ReportGridViewRows.class | 120 + comp/src/gb.report2/.src/ReportHBox.class | 21 + comp/src/gb.report2/.src/ReportImage.class | 200 + comp/src/gb.report2/.src/ReportLabel.class | 239 + comp/src/gb.report2/.src/ReportLine.class | 127 + .../src/gb.report2/.src/ReportPageBreak.class | 6 + comp/src/gb.report2/.src/ReportPanel.class | 25 + comp/src/gb.report2/.src/ReportSvgImage.class | 200 + .../src/gb.report2/.src/ReportTextLabel.class | 143 + comp/src/gb.report2/.src/ReportVBox.class | 17 + comp/src/gb.report2/.src/ReportVPanel.class | 26 + comp/src/gb.report2/.src/Tests/Module2.module | 13 + comp/src/gb.report2/.src/Tests/Report10.class | 15 + .../src/gb.report2/.src/Tests/Report10.report | 47 + comp/src/gb.report2/.src/Tests/Report13.class | 8 + .../src/gb.report2/.src/Tests/Report13.report | 41 + comp/src/gb.report2/.src/Tests/Report14.class | 8 + .../src/gb.report2/.src/Tests/Report14.report | 63 + comp/src/gb.report2/.src/Tests/Report15.class | 2 + .../src/gb.report2/.src/Tests/Report15.report | 13 + comp/src/gb.report2/.src/Tests/Report16.class | 14 + .../src/gb.report2/.src/Tests/Report16.report | 24 + comp/src/gb.report2/.src/Tests/Report17.class | 8 + .../src/gb.report2/.src/Tests/Report17.report | 27 + .../src/gb.report2/.src/Tests/old/FMain.class | 16 + comp/src/gb.report2/.src/Tests/old/FMain.form | 10 + .../gb.report2/.src/Tests/old/Module1.module | 19 + .../.src/Tests/old/OutputReport2.class | 166 + .../.src/Tests/old/OutputReport2.report | 305 + .../gb.report2/.src/Tests/old/Report1.class | 43 + .../gb.report2/.src/Tests/old/Report1.report | 47 + .../gb.report2/.src/Tests/old/Report11.class | 32 + .../gb.report2/.src/Tests/old/Report11.report | 98 + .../gb.report2/.src/Tests/old/Report12.class | 20 + .../gb.report2/.src/Tests/old/Report12.report | 44 + .../gb.report2/.src/Tests/old/Report2.class | 22 + .../gb.report2/.src/Tests/old/Report2.report | 26 + .../gb.report2/.src/Tests/old/Report3.class | 26 + .../gb.report2/.src/Tests/old/Report3.report | 24 + .../gb.report2/.src/Tests/old/Report4.class | 18 + .../gb.report2/.src/Tests/old/Report4.report | 20 + .../gb.report2/.src/Tests/old/Report5.class | 8 + .../gb.report2/.src/Tests/old/Report5.report | 31 + .../gb.report2/.src/Tests/old/Report6.class | 2 + .../gb.report2/.src/Tests/old/Report6.report | 11 + .../gb.report2/.src/Tests/old/Report7.class | 30 + .../gb.report2/.src/Tests/old/Report7.report | 10 + .../gb.report2/.src/Tests/old/Report8.class | 2 + .../gb.report2/.src/Tests/old/Report8.report | 19 + .../gb.report2/.src/Tests/old/Report9.class | 9 + .../gb.report2/.src/Tests/old/Report9.report | 9 + comp/src/gb.report2/.src/Tools/CPrint.class | 72 + comp/src/gb.report2/.src/Tools/MUtil.module | 24 + .../.src/Types/Base/ReportBrush.class | 222 + .../.src/Types/Base/ReportMargin.class | 40 + .../.src/Types/Base/ReportPadding.class | 94 + .../.src/Types/Border/ReportBorder.class | 205 + .../.src/Types/Border/_ReportBorderSide.class | 16 + .../Types/Border/_ReportRoundCorner.class | 137 + .../Types/BoxShadow/ReportBoxShadow.class | 225 + .../Types/BoxShadow/_ReportBoxShadow.class | 14 + .../.src/Types/ReportSizeHints.class | 26 + .../.src/Types/ReportSizeParser.class | 83 + comp/src/gb.report2/.src/Types/TControl.class | 3 + .../gb.report2/.src/Types/TSizeParse.class | 5 + .../.src/Types/_ReportVirtualControl.class | 78 + .../src/gb.report2/.src/Viewer/FPreview.class | 550 + comp/src/gb.report2/.src/Viewer/FPreview.form | 363 + .../gb.report2/.src/Viewer/ReportView.class | 410 + comp/src/gb.report2/.src/_ReportSection.class | 23 + comp/src/gb.report2/16/full-width.png | Bin 0 -> 134 bytes comp/src/gb.report2/16/one-page.png | Bin 0 -> 126 bytes comp/src/gb.report2/16/real-size.png | Bin 0 -> 192 bytes comp/src/gb.report2/16/red-arrow-h.png | Bin 0 -> 117 bytes comp/src/gb.report2/16/red-arrow-v.png | Bin 0 -> 120 bytes comp/src/gb.report2/16/two-pages.png | Bin 0 -> 130 bytes comp/src/gb.report2/22/FullWidth.png | Bin 0 -> 139 bytes comp/src/gb.report2/22/OnePage.png | Bin 0 -> 117 bytes comp/src/gb.report2/22/RealSize.png | Bin 0 -> 204 bytes comp/src/gb.report2/22/TwoPage.png | Bin 0 -> 121 bytes comp/src/gb.report2/32/Collatecopie.png | Bin 0 -> 449 bytes comp/src/gb.report2/32/Empty.png | Bin 0 -> 719 bytes comp/src/gb.report2/32/grayscale.png | Bin 0 -> 1564 bytes comp/src/gb.report2/32/reverse.png | Bin 0 -> 820 bytes comp/src/gb.report2/FunctionsList | 52 + comp/src/gb.report2/Structure | 9 + comp/src/gb.report2/gambas.svg | 540 + comp/src/gb.report2/icon.png | Bin 0 -> 2357 bytes comp/src/gb.report2/tmpJournal | 12 + comp/src/gb.report2/tortueface.gif | Bin 0 -> 1019 bytes comp/src/gb.scanner/.component | 4 + comp/src/gb.scanner/.directory | 2 + comp/src/gb.scanner/.hidden/scanner.png | Bin 0 -> 380 bytes comp/src/gb.scanner/.icon.png | Bin 0 -> 8599 bytes comp/src/gb.scanner/.project | 20 + comp/src/gb.scanner/.src/Demo/FScan.class | 397 + comp/src/gb.scanner/.src/Demo/FScan.form | 211 + comp/src/gb.scanner/.src/Demo/Form1.class | 25 + comp/src/gb.scanner/.src/Demo/Form1.form | 11 + comp/src/gb.scanner/.src/Demo/Main.module | 8 + comp/src/gb.scanner/.src/Demo/Main2.module | 31 + comp/src/gb.scanner/.src/MTest.module | 17 + comp/src/gb.scanner/.src/MTest2.module | 21 + comp/src/gb.scanner/.src/Module1.module | 28 + comp/src/gb.scanner/.src/Module2.module | 10 + comp/src/gb.scanner/.src/Scanner.class | 456 + comp/src/gb.scanner/.src/ScannerOption.class | 126 + comp/src/gb.scanner/.src/Scanners.class | 168 + comp/src/gb.scanner/next.png | Bin 0 -> 1304 bytes comp/src/gb.settings/.component | 4 + comp/src/gb.settings/.directory | 2 + comp/src/gb.settings/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.settings/.project | 14 + comp/src/gb.settings/.src/Main.module | 28 + comp/src/gb.settings/.src/Settings.class | 845 + .../src/gb.settings/.src/_Settings_Keys.class | 104 + comp/src/gb.term.form/.component | 8 + comp/src/gb.term.form/.directory | 2 + comp/src/gb.term.form/.hidden/CHANGELOG | 4 + .../.hidden/control/termbutton.png | 1 + .../.hidden/control/termcheckbox.png | 1 + .../.hidden/control/termframe.png | 1 + .../gb.term.form/.hidden/control/termhbox.png | 1 + .../.hidden/control/termlabel.png | 1 + .../.hidden/control/termlistbox.png | 1 + .../.hidden/control/termpanel.png | 1 + .../.hidden/control/termpicturebox.png | 1 + .../.hidden/control/termradiobutton.png | 1 + .../.hidden/control/termscrollbar.png | 1 + .../.hidden/control/termtextbox.png | 1 + .../gb.term.form/.hidden/control/termvbox.png | 1 + comp/src/gb.term.form/.icon.png | Bin 0 -> 11170 bytes comp/src/gb.term.form/.lang/it.po | 15 + comp/src/gb.term.form/.lang/nl.po | 15 + comp/src/gb.term.form/.lang/ru.po | 40 + comp/src/gb.term.form/.lang/zh.po | 29 + comp/src/gb.term.form/.project | 17 + comp/src/gb.term.form/.src/Align.class | 8 + comp/src/gb.term.form/.src/Arrange.class | 4 + comp/src/gb.term.form/.src/Attr.class | 341 + comp/src/gb.term.form/.src/Border.class | 4 + comp/src/gb.term.form/.src/Char.class | 14 + comp/src/gb.term.form/.src/Desktop.class | 45 + .../gb.term.form/.src/Dialog/Message.class | 42 + comp/src/gb.term.form/.src/Key.class | 65 + comp/src/gb.term.form/.src/Mouse.class | 102 + comp/src/gb.term.form/.src/TermButton.class | 150 + comp/src/gb.term.form/.src/TermCheckBox.class | 114 + comp/src/gb.term.form/.src/TermColor.class | 21 + .../src/gb.term.form/.src/TermContainer.class | 243 + comp/src/gb.term.form/.src/TermControl.class | 783 + comp/src/gb.term.form/.src/TermForm.class | 23 + comp/src/gb.term.form/.src/TermFrame.class | 57 + comp/src/gb.term.form/.src/TermHBox.class | 9 + comp/src/gb.term.form/.src/TermLabel.class | 73 + comp/src/gb.term.form/.src/TermListBox.class | 201 + comp/src/gb.term.form/.src/TermPanel.class | 23 + .../gb.term.form/.src/TermPictureBox.class | 222 + .../gb.term.form/.src/TermRadioButton.class | 112 + .../src/gb.term.form/.src/TermScrollBar.class | 173 + comp/src/gb.term.form/.src/TermTextBox.class | 187 + comp/src/gb.term.form/.src/TermVBox.class | 11 + comp/src/gb.term.form/.src/TermWindow.class | 577 + comp/src/gb.term.form/.src/TermWindows.class | 715 + comp/src/gb.term.form/.src/Test/FTest2.class | 61 + comp/src/gb.term.form/.src/Test/Main.module | 136 + comp/src/gb.term.form/.src/Test/Main2.module | 61 + comp/src/gb.term.form/.src/Test/Main3.module | 7 + .../src/gb.term.form/.src/Test/Module1.module | 7 + .../gb.term.form/.src/Test/Termform1.class | 8 + .../gb.term.form/.src/Test/Termform1.termform | 16 + .../gb.term.form/.src/Test/Termform2.class | 9 + .../gb.term.form/.src/Test/Termform2.termform | 5 + comp/src/gb.term.form/.src/Test/trfTest.class | 85 + comp/src/gb.term.form/all.png | Bin 0 -> 1507 bytes comp/src/gb.util.web/.component | 4 + comp/src/gb.util.web/.directory | 2 + .../.hidden/control/ccontainer.png | Bin 0 -> 1606 bytes .../gb.util.web/.hidden/control/ccontrol.png | Bin 0 -> 1606 bytes comp/src/gb.util.web/.icon.png | Bin 0 -> 10933 bytes comp/src/gb.util.web/.project | 9 + comp/src/gb.util.web/.src/JS.class | 68 + comp/src/gb.util.web/.src/JSON.module | 381 + .../src/gb.util.web/.src/JSONCollection.class | 42 + comp/src/gb.util.web/.src/MMain.module | 32 + comp/src/gb.util.web/.src/URL.class | 185 + comp/src/gb.util.web/.src/URLQuery.class | 119 + comp/src/gb.util.web/.src/XML.class | 3 + comp/src/gb.util.web/.src/XMLMarkup.class | 1 + comp/src/gb.util/.component | 4 + comp/src/gb.util/.directory | 2 + comp/src/gb.util/.icon.png | Bin 0 -> 11424 bytes comp/src/gb.util/.lang/ar.po | 307 + comp/src/gb.util/.lang/ca.po | 308 + comp/src/gb.util/.lang/cs.po | 311 + comp/src/gb.util/.lang/cy.po | 307 + comp/src/gb.util/.lang/de.po | 307 + comp/src/gb.util/.lang/el.po | 307 + comp/src/gb.util/.lang/es.po | 307 + comp/src/gb.util/.lang/es_ES.po | 307 + comp/src/gb.util/.lang/fa.po | 307 + comp/src/gb.util/.lang/fr.po | 335 + comp/src/gb.util/.lang/gl_ES.po | 307 + comp/src/gb.util/.lang/hr.po | 307 + comp/src/gb.util/.lang/hu.po | 307 + comp/src/gb.util/.lang/id.po | 308 + comp/src/gb.util/.lang/it.po | 314 + comp/src/gb.util/.lang/ja.po | 307 + comp/src/gb.util/.lang/ko.po | 307 + comp/src/gb.util/.lang/lt.po | 365 + comp/src/gb.util/.lang/nl.po | 307 + comp/src/gb.util/.lang/no.po | 307 + comp/src/gb.util/.lang/pl.po | 307 + comp/src/gb.util/.lang/pt.po | 307 + comp/src/gb.util/.lang/pt_BR.po | 307 + comp/src/gb.util/.lang/ro.po | 307 + comp/src/gb.util/.lang/ru.po | 318 + comp/src/gb.util/.lang/sl.po | 307 + comp/src/gb.util/.lang/sv.po | 307 + comp/src/gb.util/.lang/tr.po | 307 + comp/src/gb.util/.lang/zh.po | 307 + comp/src/gb.util/.lang/zh_TW.po | 307 + comp/src/gb.util/.project | 11 + comp/src/gb.util/.src/Class.class | 127 + comp/src/gb.util/.src/ClassStat.class | 12 + comp/src/gb.util/.src/CsvFile.class | 332 + comp/src/gb.util/.src/Date.module | 215 + comp/src/gb.util/.src/File.class | 42 + comp/src/gb.util/.src/Language.class | 395 + comp/src/gb.util/.src/MMain.module | 69 + .../src/gb.util/.src/MPhonetic_English.module | 876 + comp/src/gb.util/.src/MPhonetic_French.module | 283 + comp/src/gb.util/.src/Process.class | 39 + comp/src/gb.util/.src/ProcessExpect.class | 78 + comp/src/gb.util/.src/ProcessPrompt.class | 5 + comp/src/gb.util/.src/Shell.module | 59 + comp/src/gb.util/.src/String.class | 416 + comp/src/gb.web.feed/.component | 7 + comp/src/gb.web.feed/.directory | 2 + comp/src/gb.web.feed/.hidden/TODO | 10 + comp/src/gb.web.feed/.icon.png | Bin 0 -> 12094 bytes comp/src/gb.web.feed/.lang/it.po | 119 + comp/src/gb.web.feed/.lang/nl.po | 141 + comp/src/gb.web.feed/.lang/ru.po | 106 + comp/src/gb.web.feed/.lang/zh.po | 119 + comp/src/gb.web.feed/.project | 14 + comp/src/gb.web.feed/.src/Main.module | 28 + comp/src/gb.web.feed/.src/Rss.class | 427 + comp/src/gb.web.feed/.src/RssCategory.class | 24 + comp/src/gb.web.feed/.src/RssCloud.class | 64 + comp/src/gb.web.feed/.src/RssDate.class | 38 + comp/src/gb.web.feed/.src/RssEnclosure.class | 29 + comp/src/gb.web.feed/.src/RssGuid.class | 37 + comp/src/gb.web.feed/.src/RssImage.class | 79 + comp/src/gb.web.feed/.src/RssItem.class | 113 + comp/src/gb.web.feed/.src/RssSource.class | 27 + comp/src/gb.web.feed/.src/RssTextInput.class | 51 + comp/src/gb.web.feed/Feed-icon.svg | 18 + comp/src/gb.web.feed/test.xml | 41 + comp/src/gb.web.form/.component | 7 + comp/src/gb.web.form/.directory | 2 + .../.hidden/Uncompressed/gw-style.css | 504 + .../gb.web.form/.hidden/Uncompressed/lib.js | 1321 + .../.hidden/Uncompressed/style.css | 732 + comp/src/gb.web.form/.hidden/calendar.js | 1577 + .../gb.web.form/.hidden/control/webbutton.png | 1 + .../.hidden/control/webcheckbox.png | 1 + .../.hidden/control/webcombobox.png | 1 + .../.hidden/control/webcontainer.png | Bin 0 -> 109 bytes .../.hidden/control/webdatebox.png | 1 + .../.hidden/control/webdatechooser.png | 1 + .../.hidden/control/webexpander.png | 1 + .../gb.web.form/.hidden/control/webhbox.png | 1 + .../gb.web.form/.hidden/control/webhtml.png | Bin 0 -> 403 bytes .../gb.web.form/.hidden/control/webimage.png | 1 + .../gb.web.form/.hidden/control/weblabel.png | 1 + .../.hidden/control/weblistbox.png | 1 + .../gb.web.form/.hidden/control/webmenu.png | Bin 0 -> 637 bytes .../.hidden/control/webmenubar.png | Bin 0 -> 472 bytes .../.hidden/control/webmenuitem.png | Bin 0 -> 1059 bytes .../.hidden/control/webprogressbar.png | 1 + .../.hidden/control/webradiobutton.png | 1 + .../.hidden/control/webscrollview.png | 1 + .../.hidden/control/webseparator.png | 1 + .../gb.web.form/.hidden/control/webslider.png | 1 + .../.hidden/control/webspinbox.png | 1 + .../gb.web.form/.hidden/control/webtable.png | Bin 0 -> 195 bytes .../.hidden/control/webtabpanel.png | 1 + .../.hidden/control/webtextarea.png | 1 + .../.hidden/control/webtextbox.png | 1 + .../gb.web.form/.hidden/control/webtimer.png | 1 + .../.hidden/control/webuploadarea.png | Bin 0 -> 245 bytes .../.hidden/control/webuploadbutton.png | Bin 0 -> 153 bytes .../.hidden/control/webuploader.png | Bin 0 -> 387 bytes .../gb.web.form/.hidden/control/webvbox.png | 1 + comp/src/gb.web.form/.icon.png | Bin 0 -> 11601 bytes comp/src/gb.web.form/.lang/fr.po | 306 + comp/src/gb.web.form/.lang/it.po | 44 + comp/src/gb.web.form/.lang/nl.po | 283 + comp/src/gb.web.form/.lang/pt_BR.po | 44 + comp/src/gb.web.form/.lang/ru.po | 300 + comp/src/gb.web.form/.lang/zh.po | 306 + comp/src/gb.web.form/.project | 18 + comp/src/gb.web.form/.public/favicon.png | Bin 0 -> 16241 bytes .../src/gb.web.form/.public/gw-arrow-down.png | Bin 0 -> 132 bytes .../src/gb.web.form/.public/gw-arrow-left.png | Bin 0 -> 117 bytes .../gb.web.form/.public/gw-arrow-right.png | Bin 0 -> 114 bytes comp/src/gb.web.form/.public/gw-arrow-up.png | Bin 0 -> 130 bytes comp/src/gb.web.form/.public/gw-close.png | Bin 0 -> 478 bytes comp/src/gb.web.form/.public/gw-max.png | Bin 0 -> 233 bytes .../src/gb.web.form/.public/gw-table-more.gif | Bin 0 -> 723 bytes comp/src/gb.web.form/.public/gw-waiting.gif | Bin 0 -> 6968 bytes .../src/gb.web.form/.public/message/close.svg | 148 + .../src/gb.web.form/.public/message/error.png | Bin 0 -> 1173 bytes comp/src/gb.web.form/.public/message/info.png | Bin 0 -> 828 bytes .../gb.web.form/.public/message/question.png | Bin 0 -> 1189 bytes .../gb.web.form/.public/message/warning.png | Bin 0 -> 588 bytes comp/src/gb.web.form/.public/new.png | Bin 0 -> 608 bytes comp/src/gb.web.form/.public/open.png | Bin 0 -> 817 bytes comp/src/gb.web.form/.src/Align.class | 11 + comp/src/gb.web.form/.src/Arrange.class | 16 + .../gb.web.form/.src/Calendar/FCalendar.class | 25 + .../.src/Calendar/FCalendar.webform | 9 + .../.src/Calendar/WebDateBox.class | 121 + .../.src/Calendar/WebDateChooser.class | 244 + comp/src/gb.web.form/.src/Color.class | 44 + comp/src/gb.web.form/.src/Header.class | 36 + comp/src/gb.web.form/.src/Header.webpage | 12 + comp/src/gb.web.form/.src/Main.module | 30 + .../gb.web.form/.src/Message/FMessage.class | 94 + .../gb.web.form/.src/Message/FMessage.webform | 43 + .../gb.web.form/.src/Message/Message.class | 61 + comp/src/gb.web.form/.src/Scroll.class | 14 + comp/src/gb.web.form/.src/Select.class | 12 + comp/src/gb.web.form/.src/Test/FHello.class | 8 + comp/src/gb.web.form/.src/Test/FHello.webform | 31 + .../.src/Test/FTestWebUploader.class | 16 + .../.src/Test/FTestWebUploader.webform | 18 + comp/src/gb.web.form/.src/Test/Webform1.class | 83 + .../gb.web.form/.src/Test/Webform1.webform | 147 + comp/src/gb.web.form/.src/Test/Webform2.class | 146 + .../gb.web.form/.src/Test/Webform2.webform | 275 + comp/src/gb.web.form/.src/Test/Webform3.class | 75 + .../gb.web.form/.src/Test/Webform3.webform | 41 + comp/src/gb.web.form/.src/Test/Webform4.class | 41 + .../gb.web.form/.src/Test/Webform4.webform | 45 + comp/src/gb.web.form/.src/Test/Webform5.class | 16 + .../gb.web.form/.src/Test/Webform5.webform | 25 + comp/src/gb.web.form/.src/Test/Webform6.class | 20 + .../gb.web.form/.src/Test/Webform6.webform | 30 + comp/src/gb.web.form/.src/Test/Webform7.class | 4 + .../gb.web.form/.src/Test/Webform7.webform | 15 + comp/src/gb.web.form/.src/Test/Webform8.class | 13 + .../gb.web.form/.src/Test/Webform8.webform | 12 + comp/src/gb.web.form/.src/WebButton.class | 134 + comp/src/gb.web.form/.src/WebCheckBox.class | 71 + comp/src/gb.web.form/.src/WebComboBox.class | 269 + comp/src/gb.web.form/.src/WebContainer.class | 438 + comp/src/gb.web.form/.src/WebControl.class | 1102 + .../gb.web.form/.src/WebControlStyle.class | 46 + comp/src/gb.web.form/.src/WebExpander.class | 109 + comp/src/gb.web.form/.src/WebForm.class | 1040 + comp/src/gb.web.form/.src/WebHBox.class | 17 + comp/src/gb.web.form/.src/WebHtml.class | 32 + comp/src/gb.web.form/.src/WebImage.class | 61 + comp/src/gb.web.form/.src/WebLabel.class | 83 + comp/src/gb.web.form/.src/WebMenu.class | 133 + comp/src/gb.web.form/.src/WebMenuBar.class | 14 + comp/src/gb.web.form/.src/WebMenuItem.class | 70 + .../src/gb.web.form/.src/WebProgressBar.class | 58 + .../src/gb.web.form/.src/WebRadioButton.class | 81 + comp/src/gb.web.form/.src/WebScrollView.class | 188 + comp/src/gb.web.form/.src/WebSeparator.class | 18 + comp/src/gb.web.form/.src/WebSlider.class | 21 + comp/src/gb.web.form/.src/WebSpinBox.class | 123 + comp/src/gb.web.form/.src/WebTabPanel.class | 193 + .../gb.web.form/.src/WebTable/WebTable.class | 644 + .../.src/WebTable/WebTableData.class | 8 + .../.src/WebTable/WebTableSelection.class | 296 + .../.src/WebTable/_WebTableColumn.class | 136 + .../.src/WebTable/_WebTableColumns.class | 115 + comp/src/gb.web.form/.src/WebTextArea.class | 148 + comp/src/gb.web.form/.src/WebTextBox.class | 187 + comp/src/gb.web.form/.src/WebTimer.class | 93 + comp/src/gb.web.form/.src/WebUploadArea.class | 195 + .../gb.web.form/.src/WebUploadButton.class | 137 + comp/src/gb.web.form/.src/WebUploader.class | 135 + comp/src/gb.web.form/.src/WebVBox.class | 17 + comp/src/gb.web.form/.src/WebWindow.class | 368 + comp/src/gb.web.form/ac.js | 225 + comp/src/gb.web.form/arrow-down.png | Bin 0 -> 91 bytes comp/src/gb.web.form/arrow-right.png | Bin 0 -> 94 bytes comp/src/gb.web.form/clear.png | Bin 0 -> 116 bytes comp/src/gb.web.form/lib.js | 1440 + comp/src/gb.web.form/shadow.png | Bin 0 -> 76 bytes comp/src/gb.web.form/style.css | 878 + comp/src/gb.web.gui/.component | 6 + comp/src/gb.web.gui/.directory | 2 + .../gb.web.gui/.hidden/control/webaudio.png | Bin 0 -> 896 bytes .../gb.web.gui/.hidden/control/webbutton.png | 1 + .../.hidden/control/webcheckbox.png | 1 + .../.hidden/control/webcombobox.png | 1 + .../.hidden/control/webcontainer.png | Bin 0 -> 136 bytes .../gb.web.gui/.hidden/control/webdatebox.png | 1 + .../.hidden/control/webdatechooser.png | 1 + .../.hidden/control/webdrawingarea.png | 1 + .../.hidden/control/webexpander.png | 1 + .../.hidden/control/webfilebutton.png | Bin 0 -> 296 bytes .../gb.web.gui/.hidden/control/webhbox.png | 1 + .../gb.web.gui/.hidden/control/webhtml.png | Bin 0 -> 1249 bytes .../gb.web.gui/.hidden/control/webimage.png | 1 + .../gb.web.gui/.hidden/control/weblabel.png | 1 + .../gb.web.gui/.hidden/control/weblistbox.png | 1 + .../.hidden/control/webprogressbar.png | 1 + .../.hidden/control/webradiobutton.png | 1 + .../.hidden/control/webscrollview.png | 1 + .../.hidden/control/webseparator.png | 1 + .../gb.web.gui/.hidden/control/webslider.png | 1 + .../gb.web.gui/.hidden/control/webspinbox.png | 1 + .../gb.web.gui/.hidden/control/webtable.png | 1 + .../.hidden/control/webtabpanel.png | 1 + .../.hidden/control/webtextarea.png | 1 + .../gb.web.gui/.hidden/control/webtextbox.png | 1 + .../gb.web.gui/.hidden/control/webtimer.png | 1 + .../gb.web.gui/.hidden/control/webtree.png | 1 + .../gb.web.gui/.hidden/control/webvbox.png | 1 + comp/src/gb.web.gui/.hidden/lib.js.copy | 1477 + comp/src/gb.web.gui/.hidden/webform.png | 1 + comp/src/gb.web.gui/.icon.png | Bin 0 -> 12187 bytes comp/src/gb.web.gui/.lang/it.po | 36 + comp/src/gb.web.gui/.lang/pt_BR.po | 37 + comp/src/gb.web.gui/.lang/zh.po | 219 + comp/src/gb.web.gui/.project | 20 + comp/src/gb.web.gui/.public/blip.ogg | Bin 0 -> 75212 bytes comp/src/gb.web.gui/.public/favicon.png | Bin 0 -> 16241 bytes comp/src/gb.web.gui/.public/gw-arrow-down.png | Bin 0 -> 899 bytes comp/src/gb.web.gui/.public/gw-arrow-left.png | Bin 0 -> 863 bytes .../src/gb.web.gui/.public/gw-arrow-right.png | Bin 0 -> 963 bytes comp/src/gb.web.gui/.public/gw-arrow-up.png | Bin 0 -> 885 bytes comp/src/gb.web.gui/.public/gw-close.png | Bin 0 -> 568 bytes comp/src/gb.web.gui/.public/gw-table-more.gif | Bin 0 -> 723 bytes comp/src/gb.web.gui/.public/gw-waiting.gif | Bin 0 -> 6968 bytes comp/src/gb.web.gui/.public/message/error.png | Bin 0 -> 1212 bytes comp/src/gb.web.gui/.public/message/info.png | Bin 0 -> 283 bytes .../gb.web.gui/.public/message/question.png | Bin 0 -> 2316 bytes .../gb.web.gui/.public/message/warning.png | Bin 0 -> 919 bytes comp/src/gb.web.gui/.src/Align.class | 55 + comp/src/gb.web.gui/.src/Application.class | 11 + comp/src/gb.web.gui/.src/Arrange.class | 16 + comp/src/gb.web.gui/.src/CSelection.class | 256 + .../gb.web.gui/.src/Calendar/FCalendar.class | 32 + .../.src/Calendar/FCalendar.webform | 10 + .../gb.web.gui/.src/Calendar/WebDateBox.class | 132 + .../.src/Calendar/WebDateChooser.class | 257 + comp/src/gb.web.gui/.src/Color.class | 92 + comp/src/gb.web.gui/.src/Header.class | 50 + comp/src/gb.web.gui/.src/Header.webpage | 13 + comp/src/gb.web.gui/.src/Key.class | 143 + .../src/gb.web.gui/.src/Menu/FPopupMenu.class | 94 + .../gb.web.gui/.src/Menu/FPopupMenu.webform | 10 + comp/src/gb.web.gui/.src/Menu/WebMenu.class | 101 + .../src/gb.web.gui/.src/Menu/WebMenuBar.class | 4 + .../gb.web.gui/.src/Message/FMessage.class | 90 + .../gb.web.gui/.src/Message/FMessage.webform | 46 + .../src/gb.web.gui/.src/Message/Message.class | 62 + comp/src/gb.web.gui/.src/Mouse.class | 98 + comp/src/gb.web.gui/.src/Paint/Gradient.class | 45 + comp/src/gb.web.gui/.src/Paint/Paint.class | 248 + .../gb.web.gui/.src/Paint/PaintBrush.class | 47 + .../gb.web.gui/.src/Paint/PaintDriver.class | 114 + .../Paint/PaintDriver_WebDrawingArea.class | 320 + comp/src/gb.web.gui/.src/Scroll.class | 14 + comp/src/gb.web.gui/.src/Select.class | 12 + comp/src/gb.web.gui/.src/Session.module | 227 + comp/src/gb.web.gui/.src/StartupError.class | 9 + comp/src/gb.web.gui/.src/StartupError.webpage | 15 + comp/src/gb.web.gui/.src/Table/WebTable.class | 673 + .../gb.web.gui/.src/Table/WebTableData.class | 19 + .../.src/Table/_WebTableColumn.class | 140 + .../.src/Table/_WebTableColumns.class | 124 + comp/src/gb.web.gui/.src/Test/FInputBox.class | 31 + .../gb.web.gui/.src/Test/FInputBox.webform | 47 + .../gb.web.gui/.src/Test/FTestDialog.class | 20 + .../gb.web.gui/.src/Test/FTestDialog.webform | 33 + .../.src/Test/FTestDrawingArea.class | 92 + .../.src/Test/FTestDrawingArea.webform | 22 + .../gb.web.gui/.src/Test/FTestSlider.class | 9 + .../gb.web.gui/.src/Test/FTestSlider.webform | 12 + .../gb.web.gui/.src/Test/FTestWebAudio.class | 92 + .../.src/Test/FTestWebAudio.webform | 48 + comp/src/gb.web.gui/.src/Test/Webform1.class | 388 + .../src/gb.web.gui/.src/Test/Webform1.webform | 306 + comp/src/gb.web.gui/.src/Test/Webform2.class | 24 + .../src/gb.web.gui/.src/Test/Webform2.webform | 42 + comp/src/gb.web.gui/.src/Test/Webform3.class | 52 + .../src/gb.web.gui/.src/Test/Webform3.webform | 32 + comp/src/gb.web.gui/.src/Test/Webform4.class | 14 + .../src/gb.web.gui/.src/Test/Webform4.webform | 67 + comp/src/gb.web.gui/.src/Tree/WebTree.class | 1615 + .../gb.web.gui/.src/Tree/WebTreeData.class | 19 + .../.src/Tree/WebTreeSelection.class | 87 + .../gb.web.gui/.src/Tree/_WebTreeColumn.class | 4 + .../.src/Tree/_WebTreeColumns.class | 108 + .../gb.web.gui/.src/Tree/_WebTreeItem.class | 519 + comp/src/gb.web.gui/.src/Upload.module | 72 + comp/src/gb.web.gui/.src/WebAudio.class | 216 + comp/src/gb.web.gui/.src/WebButton.class | 219 + comp/src/gb.web.gui/.src/WebCheckBox.class | 74 + comp/src/gb.web.gui/.src/WebComboBox.class | 282 + comp/src/gb.web.gui/.src/WebContainer.class | 332 + comp/src/gb.web.gui/.src/WebControl.class | 1303 + .../src/gb.web.gui/.src/WebControlStyle.class | 46 + comp/src/gb.web.gui/.src/WebDrawingArea.class | 77 + comp/src/gb.web.gui/.src/WebFileButton.class | 201 + comp/src/gb.web.gui/.src/WebForm.class | 1727 + comp/src/gb.web.gui/.src/WebHBox.class | 17 + comp/src/gb.web.gui/.src/WebHtml.class | 38 + comp/src/gb.web.gui/.src/WebImage.class | 135 + comp/src/gb.web.gui/.src/WebLabel.class | 95 + comp/src/gb.web.gui/.src/WebListBox.class | 346 + comp/src/gb.web.gui/.src/WebProgressBar.class | 56 + comp/src/gb.web.gui/.src/WebRadioButton.class | 84 + comp/src/gb.web.gui/.src/WebScrollView.class | 179 + comp/src/gb.web.gui/.src/WebSeparator.class | 18 + comp/src/gb.web.gui/.src/WebSlider.class | 21 + comp/src/gb.web.gui/.src/WebSpinBox.class | 125 + comp/src/gb.web.gui/.src/WebTabPanel.class | 237 + comp/src/gb.web.gui/.src/WebTextArea.class | 145 + comp/src/gb.web.gui/.src/WebTextBox.class | 184 + comp/src/gb.web.gui/.src/WebTimer.class | 109 + comp/src/gb.web.gui/.src/WebVBox.class | 17 + comp/src/gb.web.gui/.src/WebWindow.class | 367 + comp/src/gb.web.gui/ac.js | 225 + comp/src/gb.web.gui/lib.js | 2060 + comp/src/gb.web.gui/style.css | 1134 + comp/src/gb.web/.component | 5 + comp/src/gb.web/.directory | 2 + comp/src/gb.web/.hidden/Session_opt | 527 + comp/src/gb.web/.icon.png | Bin 0 -> 11601 bytes comp/src/gb.web/.project | 16 + comp/src/gb.web/.src/Application.module | 85 + comp/src/gb.web/.src/CGI.module | 154 + comp/src/gb.web/.src/FileSessionManager.class | 316 + comp/src/gb.web/.src/Main.module | 348 + comp/src/gb.web/.src/Request.module | 347 + comp/src/gb.web/.src/Response.module | 388 + comp/src/gb.web/.src/Session.module | 308 + comp/src/gb.web/.src/SessionManager.class | 38 + .../gb.web/.src/SqliteSessionManager.class | 384 + comp/src/gb.web/.src/URL.class | 62 + comp/src/gb.web/.src/WebPage.class | 111 + comp/src/gb.web/.src/Webpage1.class | 7 + comp/src/gb.web/.src/Webpage1.webpage | 22 + comp/src/gb.web/.src/Webpage2.class | 2 + comp/src/gb.web/.src/Webpage2.webpage | 1 + comp/src/gb.web/.src/_Request_Get.class | 46 + comp/src/gb.web/.src/_Request_Post.class | 257 + comp/src/gb.web/.src/_ResponseCache.module | 13 + comp/src/gb.web/lang.cache | 1 + comp/src/order | 1 + component.am | 71 + configure.ac | 99 + gb.cairo/AUTHORS | 0 gb.cairo/COPYING | 1 + gb.cairo/ChangeLog | 0 gb.cairo/INSTALL | 1 + gb.cairo/Makefile.am | 3 + gb.cairo/NEWS | 0 gb.cairo/README | 0 gb.cairo/acinclude.m4 | 1 + gb.cairo/component.am | 1 + gb.cairo/configure.ac | 18 + gb.cairo/gambas.h | 1 + gb.cairo/gb.image.h | 1 + gb.cairo/gb_common.h | 1 + gb.cairo/m4 | 1 + gb.cairo/reconf | 1 + gb.cairo/src/Makefile.am | 11 + gb.cairo/src/c_cairo.c | 1199 + gb.cairo/src/c_cairo.h | 85 + gb.cairo/src/c_surface.c | 210 + gb.cairo/src/c_surface.h | 50 + gb.cairo/src/gb.cairo.component | 4 + gb.cairo/src/main.c | 57 + gb.cairo/src/main.h | 37 + gb.compress.bzlib2/AUTHORS | 0 gb.compress.bzlib2/COPYING | 1 + gb.compress.bzlib2/ChangeLog | 0 gb.compress.bzlib2/INSTALL | 1 + gb.compress.bzlib2/Makefile.am | 3 + gb.compress.bzlib2/NEWS | 0 gb.compress.bzlib2/README | 0 gb.compress.bzlib2/acinclude.m4 | 1 + gb.compress.bzlib2/component.am | 1 + gb.compress.bzlib2/configure.ac | 26 + gb.compress.bzlib2/gambas.h | 1 + gb.compress.bzlib2/gb.compress.h | 1 + gb.compress.bzlib2/gb_common.h | 1 + gb.compress.bzlib2/m4 | 1 + gb.compress.bzlib2/reconf | 1 + gb.compress.bzlib2/src/Makefile.am | 10 + .../src/gb.compress.bzlib2.component | 5 + gb.compress.bzlib2/src/main.c | 579 + gb.compress.bzlib2/src/main.h | 49 + gb.compress.zlib/AUTHORS | 0 gb.compress.zlib/COPYING | 1 + gb.compress.zlib/ChangeLog | 0 gb.compress.zlib/INSTALL | 1 + gb.compress.zlib/Makefile.am | 3 + gb.compress.zlib/NEWS | 0 gb.compress.zlib/README | 0 gb.compress.zlib/acinclude.m4 | 1 + gb.compress.zlib/component.am | 1 + gb.compress.zlib/configure.ac | 24 + gb.compress.zlib/gambas.h | 1 + gb.compress.zlib/gb.compress.h | 1 + gb.compress.zlib/gb_common.h | 1 + gb.compress.zlib/m4 | 1 + gb.compress.zlib/reconf | 1 + gb.compress.zlib/src/Makefile.am | 10 + .../src/gb.compress.zlib.component | 5 + gb.compress.zlib/src/main.c | 530 + gb.compress.zlib/src/main.h | 47 + gb.compress.zstd/AUTHORS | 0 gb.compress.zstd/COPYING | 1 + gb.compress.zstd/ChangeLog | 0 gb.compress.zstd/INSTALL | 1 + gb.compress.zstd/Makefile.am | 3 + gb.compress.zstd/NEWS | 0 gb.compress.zstd/README | 0 gb.compress.zstd/acinclude.m4 | 1 + gb.compress.zstd/component.am | 1 + gb.compress.zstd/configure.ac | 24 + gb.compress.zstd/gambas.h | 1 + gb.compress.zstd/gb.compress.h | 1 + gb.compress.zstd/gb_common.h | 1 + gb.compress.zstd/m4 | 1 + gb.compress.zstd/reconf | 1 + gb.compress.zstd/src/Makefile.am | 10 + .../src/gb.compress.zstd.component | 4 + gb.compress.zstd/src/main.c | 470 + gb.compress.zstd/src/main.h | 48 + gb.crypt/AUTHORS | 0 gb.crypt/COPYING | 1 + gb.crypt/ChangeLog | 0 gb.crypt/INSTALL | 1 + gb.crypt/Makefile.am | 3 + gb.crypt/NEWS | 0 gb.crypt/README | 0 gb.crypt/acinclude.m4 | 1 + gb.crypt/component.am | 1 + gb.crypt/configure.ac | 28 + gb.crypt/gambas.h | 1 + gb.crypt/gb_common.h | 1 + gb.crypt/m4 | 1 + gb.crypt/reconf | 1 + gb.crypt/src/Makefile.am | 14 + gb.crypt/src/c_crypt.c | 203 + gb.crypt/src/c_crypt.h | 33 + gb.crypt/src/gb.crypt.component | 7 + gb.crypt/src/main.c | 47 + gb.crypt/src/main.h | 34 + gb.db.mysql/AUTHORS | 0 gb.db.mysql/COPYING | 1 + gb.db.mysql/ChangeLog | 0 gb.db.mysql/INSTALL | 1 + gb.db.mysql/Makefile.am | 3 + gb.db.mysql/NEWS | 0 gb.db.mysql/README | 0 gb.db.mysql/acinclude.m4 | 1 + gb.db.mysql/component.am | 1 + gb.db.mysql/configure.ac | 55 + gb.db.mysql/gambas.h | 1 + gb.db.mysql/gb.db.h | 1 + gb.db.mysql/gb.db.proto.h | 1 + gb.db.mysql/gb_common.h | 1 + gb.db.mysql/m4 | 1 + gb.db.mysql/reconf | 1 + gb.db.mysql/src/Makefile.am | 11 + gb.db.mysql/src/gb.db.mysql.component | 5 + gb.db.mysql/src/main.c | 2903 ++ gb.db.mysql/src/main.h | 46 + gb.db.odbc/AUTHORS | 0 gb.db.odbc/COPYING | 1 + gb.db.odbc/ChangeLog | 37 + gb.db.odbc/INSTALL | 1 + gb.db.odbc/Makefile.am | 3 + gb.db.odbc/NEWS | 0 gb.db.odbc/README | 0 gb.db.odbc/acinclude.m4 | 1 + gb.db.odbc/component.am | 1 + gb.db.odbc/configure.ac | 28 + gb.db.odbc/gambas.h | 1 + gb.db.odbc/gb.db.h | 1 + gb.db.odbc/gb.db.proto.h | 1 + gb.db.odbc/gb_common.h | 1 + gb.db.odbc/m4 | 1 + gb.db.odbc/reconf | 1 + gb.db.odbc/src/Makefile.am | 11 + gb.db.odbc/src/gb.db.odbc.component | 6 + gb.db.odbc/src/main.c | 3311 ++ gb.db.odbc/src/main.h | 38 + gb.db.postgresql/AUTHORS | 0 gb.db.postgresql/COPYING | 26 + gb.db.postgresql/COPYING.GPL | 339 + gb.db.postgresql/ChangeLog | 0 gb.db.postgresql/INSTALL | 1 + gb.db.postgresql/Makefile.am | 3 + gb.db.postgresql/NEWS | 0 gb.db.postgresql/README | 0 gb.db.postgresql/acinclude.m4 | 1 + gb.db.postgresql/component.am | 1 + gb.db.postgresql/configure.ac | 28 + gb.db.postgresql/gambas.h | 1 + gb.db.postgresql/gb.db.h | 1 + gb.db.postgresql/gb.db.proto.h | 1 + gb.db.postgresql/gb_common.h | 1 + gb.db.postgresql/m4 | 1 + gb.db.postgresql/reconf | 1 + gb.db.postgresql/src/Makefile.am | 11 + .../src/gb.db.postgresql.component | 6 + gb.db.postgresql/src/main.c | 3027 ++ gb.db.postgresql/src/main.h | 56 + gb.db.sqlite2/AUTHORS | 0 gb.db.sqlite2/COPYING | 1 + gb.db.sqlite2/ChangeLog | 38 + gb.db.sqlite2/INSTALL | 1 + gb.db.sqlite2/Makefile.am | 3 + gb.db.sqlite2/NEWS | 0 gb.db.sqlite2/README | 56 + gb.db.sqlite2/TODO | 15 + gb.db.sqlite2/acinclude.m4 | 1 + gb.db.sqlite2/component.am | 1 + gb.db.sqlite2/configure.ac | 26 + gb.db.sqlite2/gambas.h | 1 + gb.db.sqlite2/gb.db.h | 1 + gb.db.sqlite2/gb.db.proto.h | 1 + gb.db.sqlite2/gb_common.h | 1 + gb.db.sqlite2/m4 | 1 + gb.db.sqlite2/reconf | 1 + gb.db.sqlite2/src/Makefile.am | 13 + gb.db.sqlite2/src/dataset.cpp | 585 + gb.db.sqlite2/src/dataset.h | 420 + gb.db.sqlite2/src/gb.db.sqlite2.component | 6 + gb.db.sqlite2/src/main.cpp | 2521 + gb.db.sqlite2/src/main.h | 44 + gb.db.sqlite2/src/qry_dat.cpp | 469 + gb.db.sqlite2/src/qry_dat.h | 302 + gb.db.sqlite2/src/sqlitedataset.cpp | 783 + gb.db.sqlite2/src/sqlitedataset.h | 208 + gb.db.sqlite2/src/stringhelper.cpp | 95 + gb.db.sqlite2/src/stringhelper.h | 72 + gb.db.sqlite3/AUTHORS | 0 gb.db.sqlite3/COPYING | 1 + gb.db.sqlite3/ChangeLog | 55 + gb.db.sqlite3/INSTALL | 1 + gb.db.sqlite3/Makefile.am | 3 + gb.db.sqlite3/NEWS | 0 gb.db.sqlite3/README | 60 + gb.db.sqlite3/acinclude.m4 | 1 + gb.db.sqlite3/component.am | 1 + gb.db.sqlite3/configure.ac | 20 + gb.db.sqlite3/gambas.h | 1 + gb.db.sqlite3/gb.db.h | 1 + gb.db.sqlite3/gb.db.proto.h | 1 + gb.db.sqlite3/gb_common.h | 1 + gb.db.sqlite3/m4 | 1 + gb.db.sqlite3/reconf | 1 + gb.db.sqlite3/src/Makefile.am | 14 + gb.db.sqlite3/src/README | 56 + gb.db.sqlite3/src/gb.db.sqlite3.component | 6 + gb.db.sqlite3/src/gb_buffer.c | 104 + gb.db.sqlite3/src/gb_buffer.h | 47 + gb.db.sqlite3/src/helper.c | 533 + gb.db.sqlite3/src/helper.h | 68 + gb.db.sqlite3/src/main.c | 2539 + gb.db.sqlite3/src/main.h | 39 + gb.dbus/AUTHORS | 0 gb.dbus/COPYING | 1 + gb.dbus/ChangeLog | 0 gb.dbus/INSTALL | 1 + gb.dbus/Makefile.am | 3 + gb.dbus/NEWS | 0 gb.dbus/README | 0 gb.dbus/acinclude.m4 | 1 + gb.dbus/component.am | 1 + gb.dbus/configure.ac | 16 + gb.dbus/gambas.h | 1 + gb.dbus/gb_common.h | 1 + gb.dbus/m4 | 1 + gb.dbus/reconf | 1 + gb.dbus/src/Makefile.am | 17 + gb.dbus/src/c_dbus.c | 60 + gb.dbus/src/c_dbus.h | 35 + gb.dbus/src/c_dbusconnection.c | 268 + gb.dbus/src/c_dbusconnection.h | 47 + gb.dbus/src/c_dbusobserver.c | 322 + gb.dbus/src/c_dbusobserver.h | 63 + gb.dbus/src/c_dbusvariant.c | 72 + gb.dbus/src/c_dbusvariant.h | 48 + gb.dbus/src/dbus_print_message.c | 435 + gb.dbus/src/dbus_print_message.h | 55 + gb.dbus/src/gb.dbus.component | 4 + gb.dbus/src/gb.dbus/.directory | 2 + gb.dbus/src/gb.dbus/.icon.png | Bin 0 -> 10569 bytes gb.dbus/src/gb.dbus/.project | 9 + gb.dbus/src/gb.dbus/.src/CIntrospection.class | 195 + gb.dbus/src/gb.dbus/.src/DBus.class | 178 + .../src/gb.dbus/.src/DBusApplication.class | 135 + gb.dbus/src/gb.dbus/.src/DBusConnection.class | 58 + gb.dbus/src/gb.dbus/.src/DBusObject.class | 704 + gb.dbus/src/gb.dbus/.src/DBusProxy.class | 412 + gb.dbus/src/gb.dbus/.src/DBusSignal.class | 33 + gb.dbus/src/gb.dbus/.src/DBusValues.class | 4 + gb.dbus/src/gb.dbus/.src/MMain.module | 172 + gb.dbus/src/gb.dbus/.src/Test/CTest.class | 65 + gb.dbus/src/gb.dbus/.src/Test/CTest2.class | 9 + gb.dbus/src/gb.dbus/.src/Test/CTest3.class | 15 + gb.dbus/src/gb.dbus/.src/Test/MyObject.class | 32 + gb.dbus/src/gb.dbus/.src/Test/MyValue.class | 5 + gb.dbus/src/gb.dbus/.src/Test/mpris.class | 205 + gb.dbus/src/gb.dbus/.src/_DBusNull.class | 5 + gb.dbus/src/helper.c | 1562 + gb.dbus/src/helper.h | 57 + gb.dbus/src/main.c | 60 + gb.dbus/src/main.h | 39 + gb.desktop.gnome.keyring/AUTHORS | 0 gb.desktop.gnome.keyring/COPYING | 1 + gb.desktop.gnome.keyring/ChangeLog | 0 gb.desktop.gnome.keyring/INSTALL | 1 + gb.desktop.gnome.keyring/Makefile.am | 3 + gb.desktop.gnome.keyring/NEWS | 0 gb.desktop.gnome.keyring/README | 0 gb.desktop.gnome.keyring/acinclude.m4 | 1 + gb.desktop.gnome.keyring/component.am | 1 + gb.desktop.gnome.keyring/configure.ac | 19 + gb.desktop.gnome.keyring/gambas.h | 1 + gb.desktop.gnome.keyring/gb_common.h | 1 + gb.desktop.gnome.keyring/m4 | 1 + gb.desktop.gnome.keyring/reconf | 1 + gb.desktop.gnome.keyring/src/Makefile.am | 11 + .../src/gb.desktop.gnome.keyring.component | 3 + gb.desktop.gnome.keyring/src/keyring.c | 161 + gb.desktop.gnome.keyring/src/keyring.h | 34 + gb.desktop.gnome.keyring/src/main.c | 49 + gb.desktop.gnome.keyring/src/main.h | 33 + gb.desktop.x11/AUTHORS | 0 gb.desktop.x11/COPYING | 1 + gb.desktop.x11/ChangeLog | 0 gb.desktop.x11/INSTALL | 1 + gb.desktop.x11/Makefile.am | 3 + gb.desktop.x11/NEWS | 0 gb.desktop.x11/README | 0 gb.desktop.x11/acinclude.m4 | 1 + gb.desktop.x11/component.am | 1 + gb.desktop.x11/configure.ac | 24 + gb.desktop.x11/gambas.h | 1 + gb.desktop.x11/gb.image.h | 1 + gb.desktop.x11/gb_common.h | 1 + gb.desktop.x11/gb_list.h | 1 + gb.desktop.x11/gb_list_temp.h | 1 + gb.desktop.x11/m4 | 1 + gb.desktop.x11/reconf | 1 + gb.desktop.x11/src/Makefile.am | 38 + gb.desktop.x11/src/c_x11.c | 759 + gb.desktop.x11/src/c_x11.h | 48 + gb.desktop.x11/src/c_x11systray.c | 214 + gb.desktop.x11/src/c_x11systray.h | 38 + gb.desktop.x11/src/gb.desktop.x11.component | 4 + gb.desktop.x11/src/gb.desktop.x11/.directory | 2 + .../src/gb.desktop.x11/.hidden/icon.png | Bin 0 -> 979 bytes gb.desktop.x11/src/gb.desktop.x11/.icon.png | Bin 0 -> 11079 bytes gb.desktop.x11/src/gb.desktop.x11/.project | 10 + .../src/gb.desktop.x11/.src/Atom.class | 19 + .../gb.desktop.x11/.src/DesktopWatcher.class | 97 + .../gb.desktop.x11/.src/DesktopWindow.class | 329 + .../src/gb.desktop.x11/.src/Main.module | 5 + .../src/gb.desktop.x11/.src/X11.class | 131 + .../gb.desktop.x11/.src/_DesktopVirtual.class | 100 + .../.src/_Desktop_Windows.class | 83 + gb.desktop.x11/src/gb_list.c | 24 + gb.desktop.x11/src/main.c | 53 + gb.desktop.x11/src/main.h | 35 + gb.desktop.x11/src/systray/common.h | 82 + gb.desktop.x11/src/systray/debug.c | 150 + gb.desktop.x11/src/systray/debug.h | 88 + gb.desktop.x11/src/systray/embed.c | 358 + gb.desktop.x11/src/systray/embed.h | 43 + gb.desktop.x11/src/systray/icons.c | 242 + gb.desktop.x11/src/systray/icons.h | 117 + gb.desktop.x11/src/systray/kde_tray.c | 161 + gb.desktop.x11/src/systray/kde_tray.h | 34 + gb.desktop.x11/src/systray/list.h | 76 + gb.desktop.x11/src/systray/settings.c | 949 + gb.desktop.x11/src/systray/settings.h | 94 + gb.desktop.x11/src/systray/systray.c | 992 + gb.desktop.x11/src/systray/systray.h | 47 + gb.desktop.x11/src/systray/tray.c | 747 + gb.desktop.x11/src/systray/tray.h | 146 + gb.desktop.x11/src/systray/wmh.c | 232 + gb.desktop.x11/src/systray/wmh.h | 102 + gb.desktop.x11/src/systray/xembed.c | 537 + gb.desktop.x11/src/systray/xembed.h | 49 + gb.desktop.x11/src/systray/xutils.c | 442 + gb.desktop.x11/src/systray/xutils.h | 116 + gb.desktop.x11/src/x11.c | 835 + gb.desktop.x11/src/x11.h | 116 + gb.form.htmlview/AUTHORS | 0 gb.form.htmlview/COPYING | 1 + gb.form.htmlview/ChangeLog | 0 gb.form.htmlview/INSTALL | 1 + gb.form.htmlview/Makefile.am | 3 + gb.form.htmlview/NEWS | 0 gb.form.htmlview/README | 0 gb.form.htmlview/acinclude.m4 | 1 + gb.form.htmlview/component.am | 1 + gb.form.htmlview/configure.ac | 18 + gb.form.htmlview/gambas.h | 1 + gb.form.htmlview/gb.draw.h | 1 + gb.form.htmlview/gb.geom.h | 1 + gb.form.htmlview/gb.image.h | 1 + gb.form.htmlview/gb.paint.h | 1 + gb.form.htmlview/gb_common.h | 1 + gb.form.htmlview/m4 | 1 + gb.form.htmlview/reconf | 1 + gb.form.htmlview/src/Makefile.am | 146 + gb.form.htmlview/src/c_htmldocument.cpp | 1222 + gb.form.htmlview/src/c_htmldocument.h | 67 + .../src/gb.form.htmlview.component | 1 + .../src/gb.form.htmlview/.component | 5 + .../src/gb.form.htmlview/.directory | 2 + .../.hidden/control/htmlview.png | Bin 0 -> 897 bytes .../gb.form.htmlview/.hidden/test/desert.jpg | Bin 0 -> 16590 bytes .../gb.form.htmlview/.hidden/test/test.html | 77 + .../gb.form.htmlview/.hidden/test/test2.html | 1095 + .../gb.form.htmlview/.hidden/test/test3.html | 19 + .../gb.form.htmlview/.hidden/test/test4.html | 86 + .../gb.form.htmlview/.hidden/test/test5.html | 154 + .../gb.form.htmlview/.hidden/test/test6.html | 1640 + .../src/gb.form.htmlview/.icon.png | Bin 0 -> 8507 bytes .../src/gb.form.htmlview/.project | 14 + .../src/gb.form.htmlview/.src/FTest.class | 73 + .../src/gb.form.htmlview/.src/FTest.form | 41 + .../src/gb.form.htmlview/.src/HtmlView.class | 480 + .../.src/HtmlViewDocument.class | 36 + .../src/gb.form.htmlview/.src/Main.module | 7 + .../src/gb.form.htmlview/fail.png | Bin 0 -> 720 bytes .../src/gb.form.htmlview/master.css | 331 + gb.form.htmlview/src/gumbo/LICENSE | 202 + gb.form.htmlview/src/gumbo/attribute.c | 44 + gb.form.htmlview/src/gumbo/attribute.h | 37 + gb.form.htmlview/src/gumbo/char_ref.c | 23069 ++++++++++ gb.form.htmlview/src/gumbo/char_ref.h | 60 + gb.form.htmlview/src/gumbo/char_ref.rl | 2554 ++ gb.form.htmlview/src/gumbo/error.c | 279 + gb.form.htmlview/src/gumbo/error.h | 225 + gb.form.htmlview/src/gumbo/gumbo.h | 671 + gb.form.htmlview/src/gumbo/insertion_mode.h | 57 + gb.form.htmlview/src/gumbo/parser.c | 4188 ++ gb.form.htmlview/src/gumbo/parser.h | 57 + gb.form.htmlview/src/gumbo/string_buffer.c | 110 + gb.form.htmlview/src/gumbo/string_buffer.h | 84 + gb.form.htmlview/src/gumbo/string_piece.c | 48 + gb.form.htmlview/src/gumbo/string_piece.h | 38 + gb.form.htmlview/src/gumbo/tag.c | 95 + gb.form.htmlview/src/gumbo/tag_enum.h | 153 + gb.form.htmlview/src/gumbo/tag_gperf.h | 105 + gb.form.htmlview/src/gumbo/tag_sizes.h | 4 + gb.form.htmlview/src/gumbo/tag_strings.h | 153 + gb.form.htmlview/src/gumbo/token_type.h | 41 + gb.form.htmlview/src/gumbo/tokenizer.c | 2897 ++ gb.form.htmlview/src/gumbo/tokenizer.h | 123 + gb.form.htmlview/src/gumbo/tokenizer_states.h | 103 + gb.form.htmlview/src/gumbo/utf8.c | 270 + gb.form.htmlview/src/gumbo/utf8.h | 132 + gb.form.htmlview/src/gumbo/util.c | 58 + gb.form.htmlview/src/gumbo/util.h | 60 + gb.form.htmlview/src/gumbo/vector.c | 123 + gb.form.htmlview/src/gumbo/vector.h | 67 + gb.form.htmlview/src/litehtml/LICENSE | 24 + gb.form.htmlview/src/litehtml/README.md | 38 + gb.form.htmlview/src/litehtml/attributes.h | 35 + gb.form.htmlview/src/litehtml/background.cpp | 76 + gb.form.htmlview/src/litehtml/background.h | 58 + gb.form.htmlview/src/litehtml/borders.h | 305 + gb.form.htmlview/src/litehtml/box.cpp | 432 + gb.form.htmlview/src/litehtml/box.h | 120 + gb.form.htmlview/src/litehtml/codepoint.cpp | 82 + gb.form.htmlview/src/litehtml/codepoint.h | 51 + gb.form.htmlview/src/litehtml/context.cpp | 12 + gb.form.htmlview/src/litehtml/context.h | 20 + gb.form.htmlview/src/litehtml/css_length.cpp | 54 + gb.form.htmlview/src/litehtml/css_length.h | 135 + gb.form.htmlview/src/litehtml/css_margins.h | 36 + gb.form.htmlview/src/litehtml/css_offsets.h | 36 + gb.form.htmlview/src/litehtml/css_position.h | 36 + .../src/litehtml/css_selector.cpp | 263 + gb.form.htmlview/src/litehtml/css_selector.h | 278 + gb.form.htmlview/src/litehtml/document.cpp | 974 + gb.form.htmlview/src/litehtml/document.h | 117 + gb.form.htmlview/src/litehtml/el_anchor.cpp | 26 + gb.form.htmlview/src/litehtml/el_anchor.h | 18 + gb.form.htmlview/src/litehtml/el_base.cpp | 13 + gb.form.htmlview/src/litehtml/el_base.h | 17 + .../src/litehtml/el_before_after.cpp | 207 + .../src/litehtml/el_before_after.h | 40 + gb.form.htmlview/src/litehtml/el_body.cpp | 12 + gb.form.htmlview/src/litehtml/el_body.h | 17 + gb.form.htmlview/src/litehtml/el_break.cpp | 13 + gb.form.htmlview/src/litehtml/el_break.h | 17 + gb.form.htmlview/src/litehtml/el_cdata.cpp | 20 + gb.form.htmlview/src/litehtml/el_cdata.h | 19 + gb.form.htmlview/src/litehtml/el_comment.cpp | 25 + gb.form.htmlview/src/litehtml/el_comment.h | 20 + gb.form.htmlview/src/litehtml/el_div.cpp | 18 + gb.form.htmlview/src/litehtml/el_div.h | 17 + gb.form.htmlview/src/litehtml/el_font.cpp | 55 + gb.form.htmlview/src/litehtml/el_font.h | 17 + gb.form.htmlview/src/litehtml/el_image.cpp | 273 + gb.form.htmlview/src/litehtml/el_image.h | 28 + gb.form.htmlview/src/litehtml/el_li.cpp | 35 + gb.form.htmlview/src/litehtml/el_li.h | 20 + gb.form.htmlview/src/litehtml/el_link.cpp | 39 + gb.form.htmlview/src/litehtml/el_link.h | 18 + gb.form.htmlview/src/litehtml/el_para.cpp | 18 + gb.form.htmlview/src/litehtml/el_para.h | 18 + gb.form.htmlview/src/litehtml/el_script.cpp | 25 + gb.form.htmlview/src/litehtml/el_script.h | 20 + gb.form.htmlview/src/litehtml/el_space.cpp | 40 + gb.form.htmlview/src/litehtml/el_space.h | 20 + gb.form.htmlview/src/litehtml/el_style.cpp | 31 + gb.form.htmlview/src/litehtml/el_style.h | 20 + gb.form.htmlview/src/litehtml/el_table.cpp | 105 + gb.form.htmlview/src/litehtml/el_table.h | 26 + gb.form.htmlview/src/litehtml/el_td.cpp | 44 + gb.form.htmlview/src/litehtml/el_td.h | 17 + gb.form.htmlview/src/litehtml/el_text.cpp | 183 + gb.form.htmlview/src/litehtml/el_text.h | 37 + gb.form.htmlview/src/litehtml/el_title.cpp | 15 + gb.form.htmlview/src/litehtml/el_title.h | 18 + gb.form.htmlview/src/litehtml/el_tr.cpp | 46 + gb.form.htmlview/src/litehtml/el_tr.h | 18 + gb.form.htmlview/src/litehtml/element.cpp | 411 + gb.form.htmlview/src/litehtml/element.h | 408 + gb.form.htmlview/src/litehtml/html.cpp | 245 + gb.form.htmlview/src/litehtml/html.h | 117 + gb.form.htmlview/src/litehtml/html_tag.cpp | 4808 ++ gb.form.htmlview/src/litehtml/html_tag.h | 248 + gb.form.htmlview/src/litehtml/iterators.cpp | 93 + gb.form.htmlview/src/litehtml/iterators.h | 89 + gb.form.htmlview/src/litehtml/media_query.cpp | 431 + gb.form.htmlview/src/litehtml/media_query.h | 77 + gb.form.htmlview/src/litehtml/num_cvt.cpp | 108 + gb.form.htmlview/src/litehtml/num_cvt.h | 19 + gb.form.htmlview/src/litehtml/os_types.h | 85 + gb.form.htmlview/src/litehtml/strtod.cpp | 276 + gb.form.htmlview/src/litehtml/style.cpp | 658 + gb.form.htmlview/src/litehtml/style.h | 96 + gb.form.htmlview/src/litehtml/stylesheet.cpp | 218 + gb.form.htmlview/src/litehtml/stylesheet.h | 47 + gb.form.htmlview/src/litehtml/table.cpp | 594 + gb.form.htmlview/src/litehtml/table.h | 251 + .../src/litehtml/tstring_view.cpp | 46 + gb.form.htmlview/src/litehtml/tstring_view.h | 136 + gb.form.htmlview/src/litehtml/types.h | 750 + gb.form.htmlview/src/litehtml/url.cpp | 163 + gb.form.htmlview/src/litehtml/url.h | 139 + gb.form.htmlview/src/litehtml/url_path.cpp | 86 + gb.form.htmlview/src/litehtml/url_path.h | 51 + .../src/litehtml/utf8_strings.cpp | 97 + gb.form.htmlview/src/litehtml/utf8_strings.h | 59 + gb.form.htmlview/src/litehtml/web_color.cpp | 256 + gb.form.htmlview/src/litehtml/web_color.h | 61 + gb.form.htmlview/src/main.cpp | 59 + gb.form.htmlview/src/main.h | 46 + gb.gmp/AUTHORS | 0 gb.gmp/COPYING | 1 + gb.gmp/ChangeLog | 0 gb.gmp/INSTALL | 1 + gb.gmp/Makefile.am | 3 + gb.gmp/NEWS | 0 gb.gmp/README | 0 gb.gmp/acinclude.m4 | 1 + gb.gmp/component.am | 1 + gb.gmp/configure.ac | 21 + gb.gmp/gambas.h | 1 + gb.gmp/gb_common.h | 1 + gb.gmp/m4 | 1 + gb.gmp/reconf | 1 + gb.gmp/src/Makefile.am | 13 + gb.gmp/src/c_bigint.c | 682 + gb.gmp/src/c_bigint.h | 44 + gb.gmp/src/c_rational.c | 730 + gb.gmp/src/c_rational.h | 42 + gb.gmp/src/gb.gmp.component | 3 + gb.gmp/src/main.c | 84 + gb.gmp/src/main.h | 39 + gb.gsl/AUTHORS | 0 gb.gsl/COPYING | 1 + gb.gsl/ChangeLog | 0 gb.gsl/INSTALL | 1 + gb.gsl/Makefile.am | 3 + gb.gsl/NEWS | 0 gb.gsl/README | 0 gb.gsl/Test/test/.directory | 2 + gb.gsl/Test/test/.icon.png | Bin 0 -> 3682 bytes gb.gsl/Test/test/.project | 9 + gb.gsl/Test/test/.src/MMain.module | 247 + gb.gsl/Test/test/.src/Test.class | 199 + gb.gsl/Test/test/.src/TestComplex.class | 94 + gb.gsl/Test/test/.src/TestSuite.class | 141 + gb.gsl/acinclude.m4 | 1 + gb.gsl/component.am | 1 + gb.gsl/configure.ac | 19 + gb.gsl/gambas.h | 1 + gb.gsl/gb_common.h | 1 + gb.gsl/m4 | 1 + gb.gsl/reconf | 1 + gb.gsl/src/Makefile.am | 17 + gb.gsl/src/c_complex.c | 668 + gb.gsl/src/c_complex.h | 70 + gb.gsl/src/c_float_array.c | 421 + gb.gsl/src/c_float_array.h | 43 + gb.gsl/src/c_gsl.c | 327 + gb.gsl/src/c_gsl.h | 37 + gb.gsl/src/c_matrix.c | 1300 + gb.gsl/src/c_matrix.h | 44 + gb.gsl/src/c_newtonpolynomial.c | 53 + gb.gsl/src/c_newtonpolynomial.h | 48 + gb.gsl/src/c_polynomial.c | 914 + gb.gsl/src/c_polynomial.h | 48 + gb.gsl/src/c_vector.c | 817 + gb.gsl/src/c_vector.h | 52 + gb.gsl/src/gb.gsl.component | 5 + gb.gsl/src/main.c | 90 + gb.gsl/src/main.h | 50 + gb.gtk/AUTHORS | 0 gb.gtk/COPYING | 1 + gb.gtk/ChangeLog | 118 + gb.gtk/INSTALL | 1 + gb.gtk/Makefile.am | 3 + gb.gtk/NEWS | 0 gb.gtk/README | 0 gb.gtk/TODO | 16 + gb.gtk/acinclude.m4 | 1 + gb.gtk/component.am | 1 + gb.gtk/configure.ac | 29 + gb.gtk/gambas.h | 1 + gb.gtk/gb.draw.h | 1 + gb.gtk/gb.geom.h | 1 + gb.gtk/gb.gl.h | 1 + gb.gtk/gb.image.h | 1 + gb.gtk/gb.paint.h | 1 + gb.gtk/gb_common.h | 1 + gb.gtk/m4 | 1 + gb.gtk/reconf | 1 + gb.gtk/share | 1 + gb.gtk/src/CButton.cpp | 312 + gb.gtk/src/CButton.h | 53 + gb.gtk/src/CClipboard.cpp | 609 + gb.gtk/src/CClipboard.h | 36 + gb.gtk/src/CColor.cpp | 154 + gb.gtk/src/CColor.h | 33 + gb.gtk/src/CConst.cpp | 154 + gb.gtk/src/CConst.h | 41 + gb.gtk/src/CContainer.cpp | 753 + gb.gtk/src/CContainer.h | 121 + gb.gtk/src/CDialog.cpp | 228 + gb.gtk/src/CDialog.h | 34 + gb.gtk/src/CDraw.cpp | 39 + gb.gtk/src/CDraw.h | 39 + gb.gtk/src/CDrawingArea.cpp | 280 + gb.gtk/src/CDrawingArea.h | 53 + gb.gtk/src/CFont.cpp | 447 + gb.gtk/src/CFont.h | 57 + gb.gtk/src/CImage.cpp | 291 + gb.gtk/src/CImage.h | 55 + gb.gtk/src/CKey.cpp | 229 + gb.gtk/src/CKey.h | 35 + gb.gtk/src/CMenu.cpp | 566 + gb.gtk/src/CMenu.h | 58 + gb.gtk/src/CMouse.cpp | 472 + gb.gtk/src/CMouse.h | 54 + gb.gtk/src/CPanel.cpp | 153 + gb.gtk/src/CPanel.h | 54 + gb.gtk/src/CPicture.cpp | 276 + gb.gtk/src/CPicture.h | 57 + gb.gtk/src/CScreen.cpp | 558 + gb.gtk/src/CScreen.h | 55 + gb.gtk/src/CSlider.cpp | 173 + gb.gtk/src/CSlider.h | 59 + gb.gtk/src/CStyle.cpp | 1031 + gb.gtk/src/CStyle.h | 43 + gb.gtk/src/CTabStrip.cpp | 388 + gb.gtk/src/CTabStrip.h | 53 + gb.gtk/src/CTextArea.cpp | 499 + gb.gtk/src/CTextArea.h | 52 + gb.gtk/src/CTextBox.cpp | 287 + gb.gtk/src/CTextBox.h | 49 + gb.gtk/src/CTrayIcon.cpp | 331 + gb.gtk/src/CTrayIcon.h | 52 + gb.gtk/src/CWatcher.cpp | 179 + gb.gtk/src/CWatcher.h | 45 + gb.gtk/src/CWidget.cpp | 1108 + gb.gtk/src/CWidget.h | 94 + gb.gtk/src/CWindow.cpp | 983 + gb.gtk/src/CWindow.h | 68 + gb.gtk/src/Makefile.am | 92 + gb.gtk/src/canimation.cpp | 226 + gb.gtk/src/canimation.h | 49 + gb.gtk/src/cpaint_impl.cpp | 1753 + gb.gtk/src/cpaint_impl.h | 50 + gb.gtk/src/cprinter.cpp | 390 + gb.gtk/src/cprinter.h | 52 + gb.gtk/src/csvgimage.cpp | 387 + gb.gtk/src/csvgimage.h | 61 + gb.gtk/src/font-parser.cpp | 175 + gb.gtk/src/font-parser.h | 32 + gb.gtk/src/gapplication.cpp | 1823 + gb.gtk/src/gapplication.h | 153 + gb.gtk/src/gb.gtk.component | 6 + gb.gtk/src/gb.gtk.h | 74 + gb.gtk/src/gbutton.cpp | 754 + gb.gtk/src/gbutton.h | 103 + gb.gtk/src/gclipboard.h | 55 + gb.gtk/src/gcolor.h | 32 + gb.gtk/src/gcontainer.cpp | 1018 + gb.gtk/src/gcontainer.h | 187 + gb.gtk/src/gcontrol.cpp | 3045 ++ gb.gtk/src/gcontrol.h | 396 + gb.gtk/src/gcursor.cpp | 80 + gb.gtk/src/gcursor.h | 49 + gb.gtk/src/gdesktop.cpp | 267 + gb.gtk/src/gdesktop.h | 88 + gb.gtk/src/gdialog.cpp | 598 + gb.gtk/src/gdialog.h | 58 + gb.gtk/src/gdrag.cpp | 815 + gb.gtk/src/gdrag.h | 122 + gb.gtk/src/gdrawingarea.cpp | 411 + gb.gtk/src/gdrawingarea.h | 88 + gb.gtk/src/gfont.cpp | 818 + gb.gtk/src/gfont.h | 126 + gb.gtk/src/ggambastag.h | 38 + gb.gtk/src/gglarea.cpp | 36 + gb.gtk/src/gglarea.h | 33 + gb.gtk/src/gkey.cpp | 621 + gb.gtk/src/gkey.h | 72 + gb.gtk/src/gmainwindow.cpp | 2136 + gb.gtk/src/gmainwindow.h | 230 + gb.gtk/src/gmenu.cpp | 1375 + gb.gtk/src/gmenu.h | 185 + gb.gtk/src/gmouse.cpp | 387 + gb.gtk/src/gmouse.h | 109 + gb.gtk/src/gpanel.cpp | 143 + gb.gtk/src/gpanel.h | 46 + gb.gtk/src/gpicture.cpp | 1223 + gb.gtk/src/gpicture.h | 133 + gb.gtk/src/gplugin.h | 45 + gb.gtk/src/gprinter.cpp | 728 + gb.gtk/src/gprinter.h | 114 + gb.gtk/src/gscrollbar.h | 35 + gb.gtk/src/gshare.h | 92 + gb.gtk/src/gsignals.cpp | 399 + gb.gtk/src/gslider.cpp | 309 + gb.gtk/src/gslider.h | 83 + gb.gtk/src/gtabstrip.cpp | 901 + gb.gtk/src/gtabstrip.h | 98 + gb.gtk/src/gtag.h | 42 + gb.gtk/src/gtextarea.cpp | 1166 + gb.gtk/src/gtextarea.h | 131 + gb.gtk/src/gtextbox.cpp | 565 + gb.gtk/src/gtextbox.h | 106 + gb.gtk/src/gtools.cpp | 2583 ++ gb.gtk/src/gtools.h | 249 + gb.gtk/src/gtrayicon.cpp | 283 + gb.gtk/src/gtrayicon.h | 84 + gb.gtk/src/gtree.cpp | 1287 + gb.gtk/src/gtree.h | 190 + gb.gtk/src/kentities.h | 860 + gb.gtk/src/main.cpp | 698 + gb.gtk/src/main.h | 63 + gb.gtk/src/opengl/Makefile.am | 13 + gb.gtk/src/opengl/c_glarea.c | 185 + gb.gtk/src/opengl/c_glarea.h | 48 + gb.gtk/src/opengl/gb.gtk.opengl.component | 6 + gb.gtk/src/opengl/main.c | 61 + gb.gtk/src/opengl/main.h | 39 + gb.gtk/src/sm/bonobo-macros.h | 100 + gb.gtk/src/sm/gnome-client.c | 3094 ++ gb.gtk/src/sm/gnome-client.h | 489 + gb.gtk/src/sm/gnome-ice.c | 153 + gb.gtk/src/sm/gnome-ice.h | 44 + gb.gtk/src/sm/gnome-macros.h | 64 + gb.gtk/src/sm/gnome-marshal.c | 369 + gb.gtk/src/sm/gnome-marshal.h | 79 + gb.gtk/src/sm/gnome-uidefs.h | 122 + gb.gtk/src/sm/gnometypebuiltins.c | 459 + gb.gtk/src/sm/gnometypebuiltins.h | 76 + gb.gtk/src/sm/libgnomeui.h | 88 + gb.gtk/src/sm/libgnomeuiP.h | 56 + gb.gtk/src/sm/sm.h | 38 + gb.gtk/src/watcher.cpp | 164 + gb.gtk/src/watcher.h | 58 + gb.gtk/src/widgets.h | 149 + gb.gtk/src/x11.c | 1 + gb.gtk/src/x11.h | 1 + gb.gtk3/AUTHORS | 0 gb.gtk3/COPYING | 1 + gb.gtk3/ChangeLog | 0 gb.gtk3/INSTALL | 1 + gb.gtk3/Makefile.am | 3 + gb.gtk3/NEWS | 0 gb.gtk3/README | 0 gb.gtk3/TODO | 0 gb.gtk3/acinclude.m4 | 1 + gb.gtk3/component.am | 1 + gb.gtk3/configure.ac | 52 + gb.gtk3/gambas.h | 1 + gb.gtk3/gb.draw.h | 1 + gb.gtk3/gb.geom.h | 1 + gb.gtk3/gb.gl.h | 1 + gb.gtk3/gb.image.h | 1 + gb.gtk3/gb.paint.h | 1 + gb.gtk3/gb_common.h | 1 + gb.gtk3/m4 | 1 + gb.gtk3/reconf | 1 + gb.gtk3/share | 1 + gb.gtk3/src/CButton.cpp | 1 + gb.gtk3/src/CButton.h | 1 + gb.gtk3/src/CClipboard.cpp | 1 + gb.gtk3/src/CClipboard.h | 1 + gb.gtk3/src/CColor.cpp | 1 + gb.gtk3/src/CColor.h | 1 + gb.gtk3/src/CConst.cpp | 1 + gb.gtk3/src/CConst.h | 1 + gb.gtk3/src/CContainer.cpp | 1 + gb.gtk3/src/CContainer.h | 1 + gb.gtk3/src/CDialog.cpp | 1 + gb.gtk3/src/CDialog.h | 1 + gb.gtk3/src/CDraw.cpp | 1 + gb.gtk3/src/CDraw.h | 1 + gb.gtk3/src/CDrawingArea.cpp | 1 + gb.gtk3/src/CDrawingArea.h | 1 + gb.gtk3/src/CFont.cpp | 1 + gb.gtk3/src/CFont.h | 1 + gb.gtk3/src/CImage.cpp | 1 + gb.gtk3/src/CImage.h | 1 + gb.gtk3/src/CKey.cpp | 1 + gb.gtk3/src/CKey.h | 1 + gb.gtk3/src/CMenu.cpp | 1 + gb.gtk3/src/CMenu.h | 1 + gb.gtk3/src/CMouse.cpp | 1 + gb.gtk3/src/CMouse.h | 1 + gb.gtk3/src/CPanel.cpp | 1 + gb.gtk3/src/CPanel.h | 1 + gb.gtk3/src/CPicture.cpp | 1 + gb.gtk3/src/CPicture.h | 1 + gb.gtk3/src/CScreen.cpp | 1 + gb.gtk3/src/CScreen.h | 1 + gb.gtk3/src/CSlider.cpp | 1 + gb.gtk3/src/CSlider.h | 1 + gb.gtk3/src/CStyle.cpp | 1 + gb.gtk3/src/CStyle.h | 1 + gb.gtk3/src/CTabStrip.cpp | 1 + gb.gtk3/src/CTabStrip.h | 1 + gb.gtk3/src/CTextArea.cpp | 1 + gb.gtk3/src/CTextArea.h | 1 + gb.gtk3/src/CTextBox.cpp | 1 + gb.gtk3/src/CTextBox.h | 1 + gb.gtk3/src/CTrayIcon.cpp | 1 + gb.gtk3/src/CTrayIcon.h | 1 + gb.gtk3/src/CWatcher.cpp | 1 + gb.gtk3/src/CWatcher.h | 1 + gb.gtk3/src/CWidget.cpp | 1 + gb.gtk3/src/CWidget.h | 1 + gb.gtk3/src/CWindow.cpp | 1 + gb.gtk3/src/CWindow.h | 1 + gb.gtk3/src/Makefile.am | 75 + gb.gtk3/src/canimation.cpp | 1 + gb.gtk3/src/canimation.h | 1 + gb.gtk3/src/cpaint_impl.cpp | 1 + gb.gtk3/src/cpaint_impl.h | 1 + gb.gtk3/src/cprinter.cpp | 1 + gb.gtk3/src/cprinter.h | 1 + gb.gtk3/src/csvgimage.cpp | 1 + gb.gtk3/src/csvgimage.h | 1 + gb.gtk3/src/font-parser.cpp | 1 + gb.gtk3/src/font-parser.h | 1 + gb.gtk3/src/gapplication.cpp | 1 + gb.gtk3/src/gapplication.h | 1 + gb.gtk3/src/gb.gtk.h | 1 + gb.gtk3/src/gb.gtk.patch.h | 211 + gb.gtk3/src/gb.gtk.platform.h | 74 + gb.gtk3/src/gb.gtk3.component | 7 + gb.gtk3/src/gbutton.cpp | 1 + gb.gtk3/src/gbutton.h | 1 + gb.gtk3/src/gclipboard.h | 1 + gb.gtk3/src/gcolor.h | 1 + gb.gtk3/src/gcontainer.cpp | 1 + gb.gtk3/src/gcontainer.h | 1 + gb.gtk3/src/gcontrol.cpp | 1 + gb.gtk3/src/gcontrol.h | 1 + gb.gtk3/src/gcursor.cpp | 1 + gb.gtk3/src/gcursor.h | 1 + gb.gtk3/src/gdesktop.cpp | 1 + gb.gtk3/src/gdesktop.h | 1 + gb.gtk3/src/gdialog.cpp | 1 + gb.gtk3/src/gdialog.h | 1 + gb.gtk3/src/gdrag.cpp | 1 + gb.gtk3/src/gdrag.h | 1 + gb.gtk3/src/gdrawingarea.cpp | 1 + gb.gtk3/src/gdrawingarea.h | 1 + gb.gtk3/src/gfont.cpp | 1 + gb.gtk3/src/gfont.h | 1 + gb.gtk3/src/ggambastag.h | 1 + gb.gtk3/src/gkey.cpp | 1 + gb.gtk3/src/gkey.h | 1 + gb.gtk3/src/gmainwindow.cpp | 1 + gb.gtk3/src/gmainwindow.h | 1 + gb.gtk3/src/gmenu.cpp | 1 + gb.gtk3/src/gmenu.h | 1 + gb.gtk3/src/gmouse.cpp | 1 + gb.gtk3/src/gmouse.h | 1 + gb.gtk3/src/gpanel.cpp | 1 + gb.gtk3/src/gpanel.h | 1 + gb.gtk3/src/gpicture.cpp | 1 + gb.gtk3/src/gpicture.h | 1 + gb.gtk3/src/gprinter.cpp | 1 + gb.gtk3/src/gprinter.h | 1 + gb.gtk3/src/gscrollbar.h | 1 + gb.gtk3/src/gshare.h | 1 + gb.gtk3/src/gsignals.cpp | 1 + gb.gtk3/src/gslider.cpp | 1 + gb.gtk3/src/gslider.h | 1 + gb.gtk3/src/gtabstrip.cpp | 1 + gb.gtk3/src/gtabstrip.h | 1 + gb.gtk3/src/gtag.h | 1 + gb.gtk3/src/gtextarea.cpp | 1 + gb.gtk3/src/gtextarea.h | 1 + gb.gtk3/src/gtextbox.cpp | 1 + gb.gtk3/src/gtextbox.h | 1 + gb.gtk3/src/gtools.cpp | 1 + gb.gtk3/src/gtools.h | 1 + gb.gtk3/src/gtrayicon.cpp | 1 + gb.gtk3/src/gtrayicon.h | 1 + gb.gtk3/src/gtree.cpp | 1 + gb.gtk3/src/gtree.h | 1 + gb.gtk3/src/kentities.h | 1 + gb.gtk3/src/main.cpp | 712 + gb.gtk3/src/main.h | 67 + gb.gtk3/src/opengl/Makefile.am | 13 + gb.gtk3/src/opengl/c_glarea.c | 101 + gb.gtk3/src/opengl/c_glarea.h | 48 + gb.gtk3/src/opengl/gb.gtk3.opengl.component | 5 + gb.gtk3/src/opengl/main.c | 54 + gb.gtk3/src/opengl/main.h | 38 + gb.gtk3/src/watcher.cpp | 1 + gb.gtk3/src/watcher.h | 1 + gb.gtk3/src/wayland/Makefile.am | 11 + gb.gtk3/src/wayland/gb.gtk3.wayland.component | 6 + gb.gtk3/src/wayland/main.c | 126 + gb.gtk3/src/wayland/main.h | 36 + gb.gtk3/src/webview/Makefile.am | 14 + gb.gtk3/src/webview/c_websettings.c | 403 + gb.gtk3/src/webview/c_websettings.h | 50 + gb.gtk3/src/webview/c_webview.c | 679 + gb.gtk3/src/webview/c_webview.h | 63 + gb.gtk3/src/webview/gb.gtk3.webview.component | 5 + gb.gtk3/src/webview/main.c | 54 + gb.gtk3/src/webview/main.h | 36 + gb.gtk3/src/widgets.h | 1 + gb.gtk3/src/x11/Makefile.am | 12 + gb.gtk3/src/x11/gb.gtk3.x11.component | 6 + gb.gtk3/src/x11/main.c | 168 + gb.gtk3/src/x11/main.h | 41 + gb.gtk3/src/x11/x11.c | 1 + gb.gtk3/src/x11/x11.h | 1 + gb.httpd/AUTHORS | 0 gb.httpd/COPYING | 1 + gb.httpd/ChangeLog | 0 gb.httpd/INSTALL | 1 + gb.httpd/Makefile.am | 3 + gb.httpd/NEWS | 0 gb.httpd/README | 0 gb.httpd/acinclude.m4 | 1 + gb.httpd/component.am | 1 + gb.httpd/configure.ac | 150 + gb.httpd/gambas.h | 1 + gb.httpd/gb_common.h | 1 + gb.httpd/m4 | 1 + gb.httpd/reconf | 1 + gb.httpd/src/Makefile.am | 20 + gb.httpd/src/fdwatch.c | 840 + gb.httpd/src/fdwatch.h | 85 + gb.httpd/src/gb.httpd.component | 3 + gb.httpd/src/libhttpd.c | 4376 ++ gb.httpd/src/libhttpd.h | 298 + gb.httpd/src/main.c | 90 + gb.httpd/src/main.h | 52 + gb.httpd/src/match.c | 86 + gb.httpd/src/match.h | 36 + gb.httpd/src/mime_encodings.h | 8 + gb.httpd/src/mime_types.h | 569 + gb.httpd/src/strerror.c | 37 + gb.httpd/src/tdate_parse.c | 312 + gb.httpd/src/tdate_parse.h | 33 + gb.httpd/src/thttpd.c | 2281 + gb.httpd/src/thttpd.h | 405 + gb.httpd/src/timers.c | 334 + gb.httpd/src/timers.h | 110 + gb.httpd/src/version.h | 9 + gb.image.imlib/AUTHORS | 0 gb.image.imlib/COPYING | 1 + gb.image.imlib/ChangeLog | 0 gb.image.imlib/INSTALL | 1 + gb.image.imlib/Makefile.am | 3 + gb.image.imlib/NEWS | 0 gb.image.imlib/README | 0 gb.image.imlib/acinclude.m4 | 1 + gb.image.imlib/component.am | 1 + gb.image.imlib/configure.ac | 18 + gb.image.imlib/gambas.h | 1 + gb.image.imlib/gb.draw.h | 1 + gb.image.imlib/gb.image.h | 1 + gb.image.imlib/gb_common.h | 1 + gb.image.imlib/m4 | 1 + gb.image.imlib/reconf | 1 + gb.image.imlib/src/Makefile.am | 10 + gb.image.imlib/src/c_image.c | 281 + gb.image.imlib/src/c_image.h | 47 + gb.image.imlib/src/c_imlib.c | 129 + gb.image.imlib/src/c_imlib.h | 33 + gb.image.imlib/src/gb.image.imlib.component | 5 + gb.image.imlib/src/main.c | 49 + gb.image.imlib/src/main.h | 38 + gb.image.io/AUTHORS | 0 gb.image.io/COPYING | 1 + gb.image.io/ChangeLog | 0 gb.image.io/INSTALL | 1 + gb.image.io/Makefile.am | 3 + gb.image.io/NEWS | 0 gb.image.io/README | 0 gb.image.io/acinclude.m4 | 1 + gb.image.io/component.am | 1 + gb.image.io/configure.ac | 18 + gb.image.io/gambas.h | 1 + gb.image.io/gb.image.h | 1 + gb.image.io/gb_common.h | 1 + gb.image.io/m4 | 1 + gb.image.io/reconf | 1 + gb.image.io/src/Makefile.am | 10 + gb.image.io/src/c_image.c | 311 + gb.image.io/src/c_image.h | 44 + gb.image.io/src/gb.image.io.component | 6 + gb.image.io/src/main.c | 51 + gb.image.io/src/main.h | 38 + gb.jit.llvm/AUTHORS | 1 + gb.jit.llvm/COPYING | 1 + gb.jit.llvm/ChangeLog | 0 gb.jit.llvm/INSTALL | 1 + gb.jit.llvm/Makefile.am | 3 + gb.jit.llvm/NEWS | 0 gb.jit.llvm/README | 17 + gb.jit.llvm/acinclude.m4 | 1 + gb.jit.llvm/component.am | 1 + gb.jit.llvm/configure.ac | 54 + gb.jit.llvm/gambas.h | 1 + gb.jit.llvm/gb_common.h | 1 + gb.jit.llvm/m4 | 1 + gb.jit.llvm/reconf | 1 + gb.jit.llvm/src/Makefile.am | 34 + gb.jit.llvm/src/gb.jit.h | 218 + gb.jit.llvm/src/gb.jit.llvm.component | 5 + gb.jit.llvm/src/jit.h | 1309 + gb.jit.llvm/src/jit_api.cpp | 79 + gb.jit.llvm/src/jit_codegen.cpp | 7001 +++ gb.jit.llvm/src/jit_codegen_conv.h | 786 + gb.jit.llvm/src/jit_compile.cpp | 139 + gb.jit.llvm/src/jit_conv.cpp | 280 + gb.jit.llvm/src/jit_expressions.cpp | 1872 + gb.jit.llvm/src/jit_gambas_pass.cpp | 139 + gb.jit.llvm/src/jit_gambas_pass.h | 31 + gb.jit.llvm/src/jit_read.cpp | 1100 + gb.jit.llvm/src/jit_runtime.c | 1090 + gb.jit.llvm/src/jit_runtime.h | 64 + gb.jit.llvm/src/main.cpp | 78 + gb.jit.llvm/src/main.h | 257 + gb.libxml/AUTHORS | 0 gb.libxml/COPYING | 1 + gb.libxml/ChangeLog | 0 gb.libxml/INSTALL | 1 + gb.libxml/Makefile.am | 3 + gb.libxml/NEWS | 0 gb.libxml/README | 0 gb.libxml/acinclude.m4 | 1 + gb.libxml/component.am | 1 + gb.libxml/configure.ac | 20 + gb.libxml/gambas.h | 1 + gb.libxml/m4 | 1 + gb.libxml/reconf | 1 + gb.libxml/src/CXMLDocument.c | 174 + gb.libxml/src/CXMLDocument.h | 54 + gb.libxml/src/CXMLNode.c | 300 + gb.libxml/src/CXMLNode.h | 52 + gb.libxml/src/CXMLReader.c | 517 + gb.libxml/src/CXMLReader.h | 54 + gb.libxml/src/CXMLWriter.c | 429 + gb.libxml/src/CXMLWriter.h | 52 + gb.libxml/src/Makefile.am | 15 + gb.libxml/src/gb.libxml.component | 7 + gb.libxml/src/libxml.kateproject | 7 + gb.libxml/src/main.c | 73 + gb.libxml/src/main.h | 35 + gb.media/AUTHORS | 0 gb.media/COPYING | 1 + gb.media/ChangeLog | 0 gb.media/INSTALL | 1 + gb.media/Makefile.am | 3 + gb.media/NEWS | 0 gb.media/README | 0 gb.media/acinclude.m4 | 1 + gb.media/component.am | 1 + gb.media/configure.ac | 17 + gb.media/gambas.h | 1 + gb.media/gb.image.h | 1 + gb.media/gb_common.h | 1 + gb.media/m4 | 1 + gb.media/reconf | 1 + gb.media/src/Makefile.am | 13 + gb.media/src/c_media.c | 2260 + gb.media/src/c_media.h | 138 + gb.media/src/c_mediaplayer.c | 658 + gb.media/src/c_mediaplayer.h | 62 + gb.media/src/c_mediavideo.c | 73 + gb.media/src/c_mediavideo.h | 50 + gb.media/src/gb.media.component | 4 + gb.media/src/main.c | 92 + gb.media/src/main.h | 46 + gb.mime/AUTHORS | 0 gb.mime/COPYING | 1 + gb.mime/ChangeLog | 0 gb.mime/INSTALL | 1 + gb.mime/Makefile.am | 3 + gb.mime/NEWS | 0 gb.mime/README | 0 gb.mime/acinclude.m4 | 1 + gb.mime/component.am | 1 + gb.mime/configure.ac | 58 + gb.mime/gambas.h | 1 + gb.mime/gb_common.h | 1 + gb.mime/m4 | 1 + gb.mime/reconf | 1 + gb.mime/src/Makefile.am | 13 + gb.mime/src/c_mime.c | 108 + gb.mime/src/c_mime.h | 35 + gb.mime/src/c_mimemessage.c | 304 + gb.mime/src/c_mimemessage.h | 48 + gb.mime/src/c_mimepart.c | 459 + gb.mime/src/c_mimepart.h | 45 + gb.mime/src/gb.mime.component | 3 + gb.mime/src/main.c | 61 + gb.mime/src/main.h | 46 + gb.ncurses/AUTHORS | 0 gb.ncurses/COPYING | 1 + gb.ncurses/ChangeLog | 0 gb.ncurses/INSTALL | 1 + gb.ncurses/Makefile.am | 3 + gb.ncurses/NEWS | 0 gb.ncurses/README | 0 gb.ncurses/acinclude.m4 | 1 + gb.ncurses/component.am | 1 + gb.ncurses/configure.ac | 33 + gb.ncurses/gambas.h | 1 + gb.ncurses/gb_common.h | 1 + gb.ncurses/m4 | 1 + gb.ncurses/reconf | 1 + gb.ncurses/src/Makefile.am | 17 + gb.ncurses/src/c_color.c | 279 + gb.ncurses/src/c_color.h | 13 + gb.ncurses/src/c_input.c | 632 + gb.ncurses/src/c_input.h | 63 + gb.ncurses/src/c_key.c | 76 + gb.ncurses/src/c_key.h | 29 + gb.ncurses/src/c_screen.c | 293 + gb.ncurses/src/c_screen.h | 50 + gb.ncurses/src/c_window.c | 944 + gb.ncurses/src/c_window.h | 63 + gb.ncurses/src/gb.ncurses.component | 3 + gb.ncurses/src/main.c | 106 + gb.ncurses/src/main.h | 37 + gb.net.curl/AUTHORS | 0 gb.net.curl/COPYING | 1 + gb.net.curl/ChangeLog | 0 gb.net.curl/INSTALL | 1 + gb.net.curl/Makefile.am | 3 + gb.net.curl/NEWS | 0 gb.net.curl/README | 0 gb.net.curl/acinclude.m4 | 1 + gb.net.curl/component.am | 1 + gb.net.curl/configure.ac | 21 + gb.net.curl/gambas.h | 1 + gb.net.curl/gb_common.h | 1 + gb.net.curl/m4 | 1 + gb.net.curl/reconf | 1 + gb.net.curl/src/CCurl.c | 777 + gb.net.curl/src/CCurl.h | 121 + gb.net.curl/src/CFtpClient.c | 333 + gb.net.curl/src/CFtpClient.h | 56 + gb.net.curl/src/CHttpClient.c | 701 + gb.net.curl/src/CHttpClient.h | 77 + gb.net.curl/src/CNet.c | 201 + gb.net.curl/src/CNet.h | 47 + gb.net.curl/src/CProxy.c | 147 + gb.net.curl/src/CProxy.h | 38 + gb.net.curl/src/Makefile.am | 21 + gb.net.curl/src/gb.net.curl.component | 12 + gb.net.curl/src/gb.net.curl/.directory | 2 + gb.net.curl/src/gb.net.curl/.icon.png | Bin 0 -> 10569 bytes gb.net.curl/src/gb.net.curl/.project | 10 + .../src/gb.net.curl/.src/Download.class | 96 + .../gb.net.curl/.src/DownloadManager.class | 361 + .../src/gb.net.curl/.src/HttpForm.class | 124 + gb.net.curl/src/gb.net.curl/.src/MMain.module | 42 + gb.net.curl/src/gbcurl.c | 379 + gb.net.curl/src/gbcurl.h | 79 + gb.net.curl/src/main.c | 60 + gb.net.curl/src/main.h | 34 + gb.net/AUTHORS | 0 gb.net/COPYING | 1 + gb.net/ChangeLog | 241 + gb.net/INSTALL | 1 + gb.net/Makefile.am | 3 + gb.net/NEWS | 0 gb.net/README | 0 gb.net/acinclude.m4 | 1 + gb.net/component.am | 1 + gb.net/configure.ac | 20 + gb.net/gambas.h | 1 + gb.net/gb_common.h | 1 + gb.net/m4 | 1 + gb.net/reconf | 1 + gb.net/src/CDnsClient.c | 720 + gb.net/src/CDnsClient.h | 68 + gb.net/src/CNet.c | 161 + gb.net/src/CNet.h | 86 + gb.net/src/CSerialPort.c | 762 + gb.net/src/CSerialPort.h | 82 + gb.net/src/CServerSocket.c | 644 + gb.net/src/CServerSocket.h | 70 + gb.net/src/CSocket.c | 949 + gb.net/src/CSocket.h | 112 + gb.net/src/CUdpSocket.c | 792 + gb.net/src/CUdpSocket.h | 76 + gb.net/src/Makefile.am | 25 + gb.net/src/doc/README | 146 + gb.net/src/doc/changes.txt | 87 + gb.net/src/doc/threading.sxw | Bin 0 -> 8432 bytes gb.net/src/gb.net.component | 12 + gb.net/src/gb_network.h | 37 + gb.net/src/main.c | 68 + gb.net/src/main.h | 34 + gb.net/src/speed.c | 66 + gb.net/src/speed.h | 29 + gb.net/src/tools.c | 464 + gb.net/src/tools.h | 70 + gb.openal/AUTHORS | 0 gb.openal/COPYING | 1 + gb.openal/ChangeLog | 0 gb.openal/INSTALL | 1 + gb.openal/Makefile.am | 3 + gb.openal/NEWS | 0 gb.openal/README | 0 gb.openal/acinclude.m4 | 1 + gb.openal/component.am | 1 + gb.openal/configure.ac | 18 + gb.openal/gambas.h | 1 + gb.openal/gb_common.h | 1 + gb.openal/m4 | 1 + gb.openal/reconf | 1 + gb.openal/src/Makefile.am | 10 + gb.openal/src/c_al.c | 673 + gb.openal/src/c_al.h | 33 + gb.openal/src/c_alc.c | 509 + gb.openal/src/c_alc.h | 52 + gb.openal/src/c_alure.c | 411 + gb.openal/src/c_alure.h | 45 + gb.openal/src/gb.openal.component | 3 + gb.openal/src/main.c | 55 + gb.openal/src/main.h | 45 + gb.opengl/AUTHORS | 0 gb.opengl/COPYING | 1 + gb.opengl/ChangeLog | 0 gb.opengl/INSTALL | 1 + gb.opengl/Makefile.am | 3 + gb.opengl/NEWS | 0 gb.opengl/README | 7 + gb.opengl/acinclude.m4 | 1 + gb.opengl/component.am | 1 + gb.opengl/configure.ac | 35 + gb.opengl/gambas.h | 1 + gb.opengl/gb.image.h | 1 + gb.opengl/gb_common.h | 1 + gb.opengl/m4 | 1 + gb.opengl/reconf | 1 + gb.opengl/src/GL.c | 1069 + gb.opengl/src/GL.h | 35 + gb.opengl/src/GLclipping.c | 58 + gb.opengl/src/GLclipping.h | 32 + gb.opengl/src/GLcolorLighting.c | 421 + gb.opengl/src/GLcolorLighting.h | 61 + gb.opengl/src/GLcoordTransf.c | 122 + gb.opengl/src/GLcoordTransf.h | 43 + gb.opengl/src/GLdisplayList.c | 81 + gb.opengl/src/GLdisplayList.h | 38 + gb.opengl/src/GLeval.c | 115 + gb.opengl/src/GLeval.h | 45 + gb.opengl/src/GLfog.c | 68 + gb.opengl/src/GLfog.h | 34 + gb.opengl/src/GLframeBufferOps.c | 142 + gb.opengl/src/GLframeBufferOps.h | 49 + gb.opengl/src/GLinfo.c | 501 + gb.opengl/src/GLinfo.h | 34 + gb.opengl/src/GLmodesExec.c | 64 + gb.opengl/src/GLmodesExec.h | 36 + gb.opengl/src/GLpixelOperations.c | 75 + gb.opengl/src/GLpixelOperations.h | 37 + gb.opengl/src/GLprimitives.c | 178 + gb.opengl/src/GLprimitives.h | 43 + gb.opengl/src/GLrasterization.c | 214 + gb.opengl/src/GLrasterization.h | 49 + gb.opengl/src/GLselectFeedback.c | 150 + gb.opengl/src/GLselectFeedback.h | 38 + gb.opengl/src/GLtextureMapping.c | 334 + gb.opengl/src/GLtextureMapping.h | 59 + gb.opengl/src/Makefile.am | 29 + gb.opengl/src/framebufferobject.c | 194 + gb.opengl/src/framebufferobject.h | 46 + gb.opengl/src/gb.gl.h | 42 + gb.opengl/src/gb.opengl.component | 6 + gb.opengl/src/glsl/GL.c | 114 + gb.opengl/src/glsl/GL.h | 35 + gb.opengl/src/glsl/GLattributes.c | 175 + gb.opengl/src/glsl/GLattributes.h | 38 + gb.opengl/src/glsl/GLinfo.h | 31 + gb.opengl/src/glsl/GLprogram.c | 98 + gb.opengl/src/glsl/GLprogram.h | 38 + gb.opengl/src/glsl/GLshader.c | 154 + gb.opengl/src/glsl/GLshader.h | 41 + gb.opengl/src/glsl/GLuniform.c | 519 + gb.opengl/src/glsl/GLuniform.h | 56 + gb.opengl/src/glsl/Makefile.am | 17 + gb.opengl/src/glsl/gb.opengl.glsl.component | 6 + gb.opengl/src/glsl/main.c | 48 + gb.opengl/src/glsl/main.h | 39 + gb.opengl/src/glu/GLU.c | 151 + gb.opengl/src/glu/GLU.h | 33 + gb.opengl/src/glu/GLUcoordTransf.c | 61 + gb.opengl/src/glu/GLUcoordTransf.h | 34 + gb.opengl/src/glu/GLUnurb.c | 153 + gb.opengl/src/glu/GLUnurb.h | 47 + gb.opengl/src/glu/GLUproject.c | 138 + gb.opengl/src/glu/GLUproject.h | 34 + gb.opengl/src/glu/GLUquadratic.c | 112 + gb.opengl/src/glu/GLUquadratic.h | 40 + gb.opengl/src/glu/GLUtextureImage.c | 53 + gb.opengl/src/glu/GLUtextureImage.h | 33 + gb.opengl/src/glu/Makefile.am | 20 + gb.opengl/src/glu/cglunurb.c | 74 + gb.opengl/src/glu/cglunurb.h | 50 + gb.opengl/src/glu/cgluquadric.c | 64 + gb.opengl/src/glu/cgluquadric.h | 63 + gb.opengl/src/glu/gb.opengl.glu.component | 6 + gb.opengl/src/glu/main.c | 84 + gb.opengl/src/glu/main.h | 45 + gb.opengl/src/main.c | 111 + gb.opengl/src/main.h | 47 + gb.opengl/src/sge/Makefile.am | 13 + gb.opengl/src/sge/cmd2model.c | 589 + gb.opengl/src/sge/cmd2model.h | 167 + gb.opengl/src/sge/cmd2object.c | 175 + gb.opengl/src/sge/cmd2object.h | 48 + gb.opengl/src/sge/gb.opengl.sge.component | 4 + gb.opengl/src/sge/main.c | 49 + gb.opengl/src/sge/main.h | 36 + gb.openssl/AUTHORS | 0 gb.openssl/COPYING | 28 + gb.openssl/COPYING.GPL | 339 + gb.openssl/ChangeLog | 0 gb.openssl/INSTALL | 1 + gb.openssl/Makefile.am | 3 + gb.openssl/NEWS | 0 gb.openssl/README | 0 gb.openssl/acinclude.m4 | 1 + gb.openssl/component.am | 1 + gb.openssl/configure.ac | 21 + gb.openssl/gambas.h | 1 + gb.openssl/gb_common.h | 1 + gb.openssl/m4 | 1 + gb.openssl/openssl-test/.directory | 2 + gb.openssl/openssl-test/.icon.png | Bin 0 -> 11170 bytes gb.openssl/openssl-test/.project | 8 + gb.openssl/openssl-test/.src/Main.module | 36 + gb.openssl/reconf | 1 + gb.openssl/src/Makefile.am | 17 + gb.openssl/src/c_cipher.c | 500 + gb.openssl/src/c_cipher.h | 42 + gb.openssl/src/c_digest.c | 289 + gb.openssl/src/c_digest.h | 43 + gb.openssl/src/c_hmac.c | 115 + gb.openssl/src/c_hmac.h | 43 + gb.openssl/src/c_openssl.c | 201 + gb.openssl/src/c_openssl.h | 43 + gb.openssl/src/gb.openssl.component | 3 + gb.openssl/src/main.c | 114 + gb.openssl/src/main.h | 47 + gb.pcre/AUTHORS | 0 gb.pcre/COPYING | 1 + gb.pcre/ChangeLog | 0 gb.pcre/INSTALL | 1 + gb.pcre/Makefile.am | 3 + gb.pcre/NEWS | 0 gb.pcre/README | 0 gb.pcre/acinclude.m4 | 1 + gb.pcre/component.am | 1 + gb.pcre/configure.ac | 38 + gb.pcre/gambas.h | 1 + gb.pcre/gb_common.h | 1 + gb.pcre/m4 | 1 + gb.pcre/reconf | 1 + gb.pcre/src/Makefile.am | 24 + gb.pcre/src/README | 62 + gb.pcre/src/gb.pcre.component | 7 + gb.pcre/src/gb.pcre.h | 40 + gb.pcre/src/main.c | 68 + gb.pcre/src/main.h | 37 + gb.pcre/src/regexp.c | 532 + gb.pcre/src/regexp.h | 63 + gb.pcre/src/regexp2.c | 497 + gb.pcre/src/regexp2.h | 64 + gb.pdf/AUTHORS | 0 gb.pdf/COPYING | 1 + gb.pdf/ChangeLog | 15 + gb.pdf/INSTALL | 1 + gb.pdf/Makefile.am | 3 + gb.pdf/NEWS | 0 gb.pdf/README | 0 gb.pdf/acinclude.m4 | 1 + gb.pdf/component.am | 1 + gb.pdf/configure.ac | 45 + gb.pdf/gambas.h | 1 + gb.pdf/gb.gtk.h | 1 + gb.pdf/gb.image.h | 1 + gb.pdf/gb_common.h | 1 + gb.pdf/m4 | 1 + gb.pdf/reconf | 1 + gb.pdf/src/CPdfDocument.cpp | 1542 + gb.pdf/src/CPdfDocument.h | 142 + gb.pdf/src/Makefile.am | 17 + gb.pdf/src/gb.pdf.component | 5 + gb.pdf/src/main.cpp | 78 + gb.pdf/src/main.h | 36 + gb.poppler/AUTHORS | 0 gb.poppler/COPYING | 1 + gb.poppler/ChangeLog | 0 gb.poppler/INSTALL | 1 + gb.poppler/Makefile.am | 3 + gb.poppler/NEWS | 0 gb.poppler/README | 0 gb.poppler/acinclude.m4 | 1 + gb.poppler/component.am | 1 + gb.poppler/configure.ac | 15 + gb.poppler/gambas.h | 1 + gb.poppler/gb.geom.h | 1 + gb.poppler/gb.image.h | 1 + gb.poppler/gb_common.h | 1 + gb.poppler/m4 | 1 + gb.poppler/reconf | 1 + gb.poppler/src/Makefile.am | 12 + gb.poppler/src/c_pdf_document.cpp | 874 + gb.poppler/src/c_pdf_document.h | 89 + gb.poppler/src/gb.poppler.component | 4 + gb.poppler/src/main.cpp | 67 + gb.poppler/src/main.h | 40 + gb.qt4/AUTHORS | 0 gb.qt4/COPYING | 1 + gb.qt4/ChangeLog | 0 gb.qt4/INSTALL | 1 + gb.qt4/Makefile.am | 3 + gb.qt4/NEWS | 0 gb.qt4/README | 0 gb.qt4/acinclude.m4 | 1 + gb.qt4/component.am | 1 + gb.qt4/configure.ac | 63 + gb.qt4/gambas.h | 1 + gb.qt4/gb.draw.h | 1 + gb.qt4/gb.eval.h | 1 + gb.qt4/gb.geom.h | 1 + gb.qt4/gb.gl.h | 1 + gb.qt4/gb.image.h | 1 + gb.qt4/gb.paint.h | 1 + gb.qt4/gb.qt.am | 6 + gb.qt4/gb_common.h | 1 + gb.qt4/gbc_read_common.h | 1 + gb.qt4/m4 | 1 + gb.qt4/reconf | 1 + gb.qt4/share/gb.form.action.h | 93 + gb.qt4/share/gb.form.arrangement.h | 736 + gb.qt4/share/gb.form.const.h | 171 + gb.qt4/share/gb.form.font.h | 37 + gb.qt4/share/gb.form.print.h | 55 + gb.qt4/share/gb.form.properties.h | 188 + gb.qt4/share/gb.form.trayicon.h | 615 + gb.qt4/share/gb.form.trayicon.large.h | 263 + gb.qt4/src/CButton.cpp | 685 + gb.qt4/src/CButton.h | 114 + gb.qt4/src/CCheckBox.cpp | 197 + gb.qt4/src/CCheckBox.h | 83 + gb.qt4/src/CClipboard.cpp | 922 + gb.qt4/src/CClipboard.h | 60 + gb.qt4/src/CColor.cpp | 204 + gb.qt4/src/CColor.h | 37 + gb.qt4/src/CConst.cpp | 229 + gb.qt4/src/CConst.h | 45 + gb.qt4/src/CContainer.cpp | 1662 + gb.qt4/src/CContainer.h | 195 + gb.qt4/src/CDialog.cpp | 477 + gb.qt4/src/CDialog.h | 34 + gb.qt4/src/CDraw.cpp | 300 + gb.qt4/src/CDraw.h | 50 + gb.qt4/src/CDrawingArea.cpp | 740 + gb.qt4/src/CDrawingArea.h | 144 + gb.qt4/src/CEmbedder.cpp | 143 + gb.qt4/src/CEmbedder.h | 67 + gb.qt4/src/CFont.cpp | 729 + gb.qt4/src/CFont.h | 85 + gb.qt4/src/CImage.cpp | 348 + gb.qt4/src/CImage.h | 56 + gb.qt4/src/CKey.cpp | 241 + gb.qt4/src/CKey.h | 51 + gb.qt4/src/CMenu.cpp | 1276 + gb.qt4/src/CMenu.h | 137 + gb.qt4/src/CMouse.cpp | 678 + gb.qt4/src/CMouse.h | 101 + gb.qt4/src/CPanel.cpp | 169 + gb.qt4/src/CPanel.h | 67 + gb.qt4/src/CPicture.cpp | 389 + gb.qt4/src/CPicture.h | 78 + gb.qt4/src/CRadioButton.cpp | 199 + gb.qt4/src/CRadioButton.h | 83 + gb.qt4/src/CScreen.cpp | 597 + gb.qt4/src/CScreen.h | 53 + gb.qt4/src/CScrollBar.cpp | 246 + gb.qt4/src/CScrollBar.h | 81 + gb.qt4/src/CSlider.cpp | 290 + gb.qt4/src/CSlider.h | 84 + gb.qt4/src/CStyle.cpp | 566 + gb.qt4/src/CStyle.h | 35 + gb.qt4/src/CTabStrip.cpp | 911 + gb.qt4/src/CTabStrip.h | 106 + gb.qt4/src/CTextArea.cpp | 692 + gb.qt4/src/CTextArea.h | 70 + gb.qt4/src/CTextBox.cpp | 405 + gb.qt4/src/CTextBox.h | 76 + gb.qt4/src/CWatch.cpp | 153 + gb.qt4/src/CWatch.h | 62 + gb.qt4/src/CWatcher.cpp | 179 + gb.qt4/src/CWatcher.h | 74 + gb.qt4/src/CWidget.cpp | 3489 ++ gb.qt4/src/CWidget.h | 337 + gb.qt4/src/CWindow.cpp | 3192 ++ gb.qt4/src/CWindow.h | 291 + gb.qt4/src/Makefile.am | 57 + gb.qt4/src/canimation.cpp | 189 + gb.qt4/src/canimation.h | 65 + gb.qt4/src/cpaint_impl.cpp | 1671 + gb.qt4/src/cpaint_impl.h | 92 + gb.qt4/src/cprinter.cpp | 576 + gb.qt4/src/cprinter.h | 57 + gb.qt4/src/csvgimage.cpp | 274 + gb.qt4/src/csvgimage.h | 57 + gb.qt4/src/ctrayicon.cpp | 462 + gb.qt4/src/ctrayicon.h | 71 + gb.qt4/src/desktop.c | 119 + gb.qt4/src/desktop.h | 40 + gb.qt4/src/ext/CDial.cpp | 198 + gb.qt4/src/ext/CDial.h | 63 + gb.qt4/src/ext/CEditor.cpp | 1865 + gb.qt4/src/ext/CEditor.h | 80 + gb.qt4/src/ext/CLCDNumber.cpp | 178 + gb.qt4/src/ext/CLCDNumber.h | 43 + gb.qt4/src/ext/CTextEdit.cpp | 735 + gb.qt4/src/ext/CTextEdit.h | 86 + gb.qt4/src/ext/Makefile.am | 20 + gb.qt4/src/ext/garray.cpp | 71 + gb.qt4/src/ext/garray.h | 112 + gb.qt4/src/ext/gb.qt4.ext.component | 4 + gb.qt4/src/ext/gdocument.cpp | 1786 + gb.qt4/src/ext/gdocument.h | 238 + gb.qt4/src/ext/gstring.cpp | 70 + gb.qt4/src/ext/gstring.h | 275 + gb.qt4/src/ext/gview.cpp | 3447 ++ gb.qt4/src/ext/gview.h | 317 + gb.qt4/src/ext/main.cpp | 93 + gb.qt4/src/ext/main.h | 41 + gb.qt4/src/fix_style.cpp | 237 + gb.qt4/src/fix_style.h | 62 + gb.qt4/src/gb.qt.h | 177 + gb.qt4/src/gb.qt4.component | 6 + gb.qt4/src/main.cpp | 1599 + gb.qt4/src/main.h | 170 + gb.qt4/src/opengl/CGLarea.cpp | 202 + gb.qt4/src/opengl/CGLarea.h | 60 + gb.qt4/src/opengl/Makefile.am | 14 + gb.qt4/src/opengl/gb.qt4.opengl.component | 6 + gb.qt4/src/opengl/main.cpp | 53 + gb.qt4/src/opengl/main.h | 37 + gb.qt4/src/trayicon.xpm | 163 + gb.qt4/src/webkit/Makefile.am | 19 + gb.qt4/src/webkit/ccookiejar.cpp | 188 + gb.qt4/src/webkit/ccookiejar.h | 77 + gb.qt4/src/webkit/control/webview.png | Bin 0 -> 1160 bytes gb.qt4/src/webkit/cwebdownload.cpp | 369 + gb.qt4/src/webkit/cwebdownload.h | 89 + gb.qt4/src/webkit/cwebelement.cpp | 399 + gb.qt4/src/webkit/cwebelement.h | 52 + gb.qt4/src/webkit/cwebframe.cpp | 177 + gb.qt4/src/webkit/cwebframe.h | 56 + gb.qt4/src/webkit/cwebhittest.cpp | 121 + gb.qt4/src/webkit/cwebhittest.h | 54 + gb.qt4/src/webkit/cwebsettings.cpp | 489 + gb.qt4/src/webkit/cwebsettings.h | 50 + gb.qt4/src/webkit/cwebview.cpp | 943 + gb.qt4/src/webkit/cwebview.h | 122 + gb.qt4/src/webkit/gb.qt4.webkit.component | 10 + gb.qt4/src/webkit/main.cpp | 143 + gb.qt4/src/webkit/main.h | 41 + gb.qt4/src/webview/Makefile.am | 16 + gb.qt4/src/webview/c_websettings.cpp | 500 + gb.qt4/src/webview/c_websettings.h | 56 + gb.qt4/src/webview/c_webview.cpp | 751 + gb.qt4/src/webview/c_webview.h | 121 + gb.qt4/src/webview/gb.qt4.webview.component | 10 + gb.qt4/src/webview/jsonwriter.cpp | 162 + gb.qt4/src/webview/jsonwriter.h | 29 + gb.qt4/src/webview/main.cpp | 142 + gb.qt4/src/webview/main.h | 41 + gb.qt4/src/x11.c | 801 + gb.qt4/src/x11.h | 135 + gb.qt5/AUTHORS | 0 gb.qt5/COPYING | 1 + gb.qt5/ChangeLog | 0 gb.qt5/INSTALL | 1 + gb.qt5/Makefile.am | 3 + gb.qt5/NEWS | 0 gb.qt5/README | 0 gb.qt5/acinclude.m4 | 1 + gb.qt5/component.am | 1 + gb.qt5/configure.ac | 73 + gb.qt5/gambas.h | 1 + gb.qt5/gb.draw.h | 1 + gb.qt5/gb.eval.h | 1 + gb.qt5/gb.geom.h | 1 + gb.qt5/gb.gl.h | 1 + gb.qt5/gb.image.h | 1 + gb.qt5/gb.paint.h | 1 + gb.qt5/gb.qt.am | 6 + gb.qt5/gb_common.h | 1 + gb.qt5/m4 | 1 + gb.qt5/reconf | 1 + gb.qt5/share | 1 + gb.qt5/src/CButton.cpp | 1 + gb.qt5/src/CButton.h | 1 + gb.qt5/src/CCheckBox.cpp | 1 + gb.qt5/src/CCheckBox.h | 1 + gb.qt5/src/CClipboard.cpp | 1 + gb.qt5/src/CClipboard.h | 1 + gb.qt5/src/CColor.cpp | 1 + gb.qt5/src/CColor.h | 1 + gb.qt5/src/CConst.cpp | 1 + gb.qt5/src/CConst.h | 1 + gb.qt5/src/CContainer.cpp | 1 + gb.qt5/src/CContainer.h | 1 + gb.qt5/src/CDialog.cpp | 1 + gb.qt5/src/CDialog.h | 1 + gb.qt5/src/CDraw.cpp | 1 + gb.qt5/src/CDraw.h | 1 + gb.qt5/src/CDrawingArea.cpp | 1 + gb.qt5/src/CDrawingArea.h | 1 + gb.qt5/src/CFont.cpp | 1 + gb.qt5/src/CFont.h | 1 + gb.qt5/src/CImage.cpp | 1 + gb.qt5/src/CImage.h | 1 + gb.qt5/src/CKey.cpp | 1 + gb.qt5/src/CKey.h | 1 + gb.qt5/src/CMenu.cpp | 1 + gb.qt5/src/CMenu.h | 1 + gb.qt5/src/CMouse.cpp | 1 + gb.qt5/src/CMouse.h | 1 + gb.qt5/src/CPanel.cpp | 1 + gb.qt5/src/CPanel.h | 1 + gb.qt5/src/CPicture.cpp | 1 + gb.qt5/src/CPicture.h | 1 + gb.qt5/src/CRadioButton.cpp | 1 + gb.qt5/src/CRadioButton.h | 1 + gb.qt5/src/CScreen.cpp | 1 + gb.qt5/src/CScreen.h | 1 + gb.qt5/src/CScrollBar.cpp | 1 + gb.qt5/src/CScrollBar.h | 1 + gb.qt5/src/CSlider.cpp | 1 + gb.qt5/src/CSlider.h | 1 + gb.qt5/src/CStyle.cpp | 1 + gb.qt5/src/CStyle.h | 1 + gb.qt5/src/CTabStrip.cpp | 1 + gb.qt5/src/CTabStrip.h | 1 + gb.qt5/src/CTextArea.cpp | 1 + gb.qt5/src/CTextArea.h | 1 + gb.qt5/src/CTextBox.cpp | 1 + gb.qt5/src/CTextBox.h | 1 + gb.qt5/src/CWatch.cpp | 1 + gb.qt5/src/CWatch.h | 1 + gb.qt5/src/CWatcher.cpp | 1 + gb.qt5/src/CWatcher.h | 1 + gb.qt5/src/CWidget.cpp | 1 + gb.qt5/src/CWidget.h | 1 + gb.qt5/src/CWindow.cpp | 1 + gb.qt5/src/CWindow.h | 1 + gb.qt5/src/Makefile.am | 50 + gb.qt5/src/canimation.cpp | 1 + gb.qt5/src/canimation.h | 1 + gb.qt5/src/cpaint_impl.cpp | 1 + gb.qt5/src/cpaint_impl.h | 1 + gb.qt5/src/cprinter.cpp | 1 + gb.qt5/src/cprinter.h | 1 + gb.qt5/src/csvgimage.cpp | 1 + gb.qt5/src/csvgimage.h | 1 + gb.qt5/src/ctrayicon.cpp | 1 + gb.qt5/src/ctrayicon.h | 1 + gb.qt5/src/ext/CDial.cpp | 1 + gb.qt5/src/ext/CDial.h | 1 + gb.qt5/src/ext/CLCDNumber.cpp | 1 + gb.qt5/src/ext/CLCDNumber.h | 1 + gb.qt5/src/ext/CTextEdit.cpp | 1 + gb.qt5/src/ext/CTextEdit.h | 1 + gb.qt5/src/ext/Makefile.am | 16 + gb.qt5/src/ext/gb.qt5.ext.component | 4 + gb.qt5/src/ext/main.cpp | 67 + gb.qt5/src/ext/main.h | 36 + gb.qt5/src/fix_style.cpp | 1 + gb.qt5/src/fix_style.h | 1 + gb.qt5/src/gb.qt.h | 1 + gb.qt5/src/gb.qt.platform.h | 80 + gb.qt5/src/gb.qt5.component | 6 + gb.qt5/src/main.cpp | 1332 + gb.qt5/src/main.h | 165 + gb.qt5/src/opengl/CGLarea.cpp | 158 + gb.qt5/src/opengl/CGLarea.h | 60 + gb.qt5/src/opengl/COldGLarea.cpp | 204 + gb.qt5/src/opengl/COldGLarea.h | 60 + gb.qt5/src/opengl/Makefile.am | 23 + gb.qt5/src/opengl/gb.qt5.opengl.component | 6 + gb.qt5/src/opengl/main.cpp | 58 + gb.qt5/src/opengl/main.h | 37 + gb.qt5/src/trayicon.xpm | 1 + gb.qt5/src/wayland/Makefile.am | 13 + gb.qt5/src/wayland/gb.qt5.wayland.component | 6 + gb.qt5/src/wayland/main.cpp | 243 + gb.qt5/src/wayland/main.h | 41 + gb.qt5/src/webkit/Makefile.am | 20 + gb.qt5/src/webkit/ccookiejar.cpp | 1 + gb.qt5/src/webkit/ccookiejar.h | 1 + gb.qt5/src/webkit/control/webview.png | Bin 0 -> 1572 bytes gb.qt5/src/webkit/cwebdownload.cpp | 1 + gb.qt5/src/webkit/cwebdownload.h | 1 + gb.qt5/src/webkit/cwebelement.cpp | 1 + gb.qt5/src/webkit/cwebelement.h | 1 + gb.qt5/src/webkit/cwebframe.cpp | 1 + gb.qt5/src/webkit/cwebframe.h | 1 + gb.qt5/src/webkit/cwebhittest.cpp | 1 + gb.qt5/src/webkit/cwebhittest.h | 1 + gb.qt5/src/webkit/cwebsettings.cpp | 1 + gb.qt5/src/webkit/cwebsettings.h | 1 + gb.qt5/src/webkit/cwebview.cpp | 1 + gb.qt5/src/webkit/cwebview.h | 1 + gb.qt5/src/webkit/gb.qt5.webkit.component | 10 + gb.qt5/src/webkit/main.cpp | 1 + gb.qt5/src/webkit/main.h | 1 + gb.qt5/src/webview/Makefile.am | 14 + gb.qt5/src/webview/c_websettings.cpp | 557 + gb.qt5/src/webview/c_websettings.h | 56 + gb.qt5/src/webview/c_webview.cpp | 1268 + gb.qt5/src/webview/c_webview.h | 129 + gb.qt5/src/webview/gb.qt5.webview.component | 5 + gb.qt5/src/webview/main.cpp | 142 + gb.qt5/src/webview/main.h | 41 + gb.qt5/src/x11/Makefile.am | 14 + gb.qt5/src/x11/gb.qt5.x11.component | 6 + gb.qt5/src/x11/main.cpp | 441 + gb.qt5/src/x11/main.h | 42 + gb.qt5/src/x11/x11.c | 1 + gb.qt5/src/x11/x11.h | 1 + gb.sdl.sound/AUTHORS | 5 + gb.sdl.sound/COPYING | 1 + gb.sdl.sound/ChangeLog | 0 gb.sdl.sound/INSTALL | 1 + gb.sdl.sound/Makefile.am | 3 + gb.sdl.sound/NEWS | 0 gb.sdl.sound/README | 10 + gb.sdl.sound/acinclude.m4 | 1 + gb.sdl.sound/component.am | 1 + gb.sdl.sound/configure.ac | 24 + gb.sdl.sound/gambas.h | 1 + gb.sdl.sound/gb_common.h | 1 + gb.sdl.sound/m4 | 1 + gb.sdl.sound/reconf | 1 + gb.sdl.sound/src/Makefile.am | 13 + gb.sdl.sound/src/cdrom.c | 491 + gb.sdl.sound/src/cdrom.h | 52 + gb.sdl.sound/src/gb.sdl.sound.component | 9 + gb.sdl.sound/src/main.c | 97 + gb.sdl.sound/src/main.h | 33 + gb.sdl.sound/src/sound.c | 604 + gb.sdl.sound/src/sound.h | 72 + gb.sdl/AUTHORS | 6 + gb.sdl/COPYING | 1 + gb.sdl/ChangeLog | 0 gb.sdl/INSTALL | 1 + gb.sdl/Makefile.am | 3 + gb.sdl/NEWS | 0 gb.sdl/README | 12 + gb.sdl/acinclude.m4 | 1 + gb.sdl/component.am | 1 + gb.sdl/configure.ac | 49 + gb.sdl/gambas.h | 1 + gb.sdl/gb.image.h | 1 + gb.sdl/gb_common.h | 1 + gb.sdl/m4 | 1 + gb.sdl/reconf | 1 + gb.sdl/src/Cconst.cpp | 69 + gb.sdl/src/Cconst.h | 34 + gb.sdl/src/Cdesktop.cpp | 51 + gb.sdl/src/Cdesktop.h | 34 + gb.sdl/src/Cdraw.cpp | 367 + gb.sdl/src/Cdraw.h | 49 + gb.sdl/src/Cfont.cpp | 243 + gb.sdl/src/Cfont.h | 47 + gb.sdl/src/Cimage.cpp | 160 + gb.sdl/src/Cimage.h | 58 + gb.sdl/src/Cjoystick.cpp | 296 + gb.sdl/src/Cjoystick.h | 52 + gb.sdl/src/Ckey.cpp | 235 + gb.sdl/src/Ckey.h | 50 + gb.sdl/src/Cmouse.cpp | 319 + gb.sdl/src/Cmouse.h | 65 + gb.sdl/src/Cwindow.cpp | 548 + gb.sdl/src/Cwindow.h | 78 + gb.sdl/src/Makefile.am | 34 + gb.sdl/src/SDL_h.h | 84 + gb.sdl/src/SDLapp.cpp | 266 + gb.sdl/src/SDLapp.h | 61 + gb.sdl/src/SDLcore.cpp | 52 + gb.sdl/src/SDLcore.h | 50 + gb.sdl/src/SDLcursor.cpp | 126 + gb.sdl/src/SDLcursor.h | 52 + gb.sdl/src/SDLdebug.cpp | 98 + gb.sdl/src/SDLdebug.h | 34 + gb.sdl/src/SDLerror.cpp | 30 + gb.sdl/src/SDLerror.h | 41 + gb.sdl/src/SDLfont.cpp | 586 + gb.sdl/src/SDLfont.h | 95 + gb.sdl/src/SDLgfx.cpp | 533 + gb.sdl/src/SDLgfx.h | 76 + gb.sdl/src/SDLosrender.cpp | 79 + gb.sdl/src/SDLosrender.h | 45 + gb.sdl/src/SDLsurface.cpp | 284 + gb.sdl/src/SDLsurface.h | 74 + gb.sdl/src/SDLtexture.cpp | 177 + gb.sdl/src/SDLtexture.h | 73 + gb.sdl/src/SDLwindow.cpp | 276 + gb.sdl/src/SDLwindow.h | 105 + gb.sdl/src/default_font.h | 632 + gb.sdl/src/gb.sdl.component | 6 + gb.sdl/src/main.cpp | 155 + gb.sdl/src/main.h | 42 + gb.sdl2/AUTHORS | 0 gb.sdl2/COPYING | 1 + gb.sdl2/ChangeLog | 0 gb.sdl2/INSTALL | 1 + gb.sdl2/Makefile.am | 3 + gb.sdl2/NEWS | 0 gb.sdl2/README | 0 gb.sdl2/acinclude.m4 | 1 + gb.sdl2/component.am | 1 + gb.sdl2/configure.ac | 27 + gb.sdl2/gambas.h | 1 + gb.sdl2/gb.geom.h | 1 + gb.sdl2/gb.image.h | 1 + gb.sdl2/gb_common.h | 1 + gb.sdl2/gb_list.h | 1 + gb.sdl2/gb_list_temp.h | 1 + gb.sdl2/m4 | 1 + gb.sdl2/reconf | 1 + gb.sdl2/src/Makefile.am | 29 + gb.sdl2/src/audio/Makefile.am | 15 + gb.sdl2/src/audio/c_channel.c | 411 + gb.sdl2/src/audio/c_channel.h | 55 + gb.sdl2/src/audio/c_music.c | 241 + gb.sdl2/src/audio/c_music.h | 36 + gb.sdl2/src/audio/c_sound.c | 141 + gb.sdl2/src/audio/c_sound.h | 41 + gb.sdl2/src/audio/gb.sdl2.audio.component | 2 + gb.sdl2/src/audio/main.c | 159 + gb.sdl2/src/audio/main.h | 52 + gb.sdl2/src/c_draw.c | 423 + gb.sdl2/src/c_draw.h | 49 + gb.sdl2/src/c_font.c | 527 + gb.sdl2/src/c_font.h | 52 + gb.sdl2/src/c_image.c | 214 + gb.sdl2/src/c_image.h | 57 + gb.sdl2/src/c_key.c | 344 + gb.sdl2/src/c_key.h | 37 + gb.sdl2/src/c_mouse.c | 276 + gb.sdl2/src/c_mouse.h | 50 + gb.sdl2/src/c_window.c | 670 + gb.sdl2/src/c_window.h | 72 + gb.sdl2/src/default_font.c | 202 + gb.sdl2/src/default_font.h | 38 + gb.sdl2/src/default_font_data.h | 1075 + gb.sdl2/src/gb.sdl2.component | 5 + gb.sdl2/src/main.c | 251 + gb.sdl2/src/main.h | 78 + gb.v4l/AUTHORS | 0 gb.v4l/COPYING | 1 + gb.v4l/ChangeLog | 241 + gb.v4l/INSTALL | 1 + gb.v4l/Makefile.am | 3 + gb.v4l/NEWS | 0 gb.v4l/README | 4 + gb.v4l/acinclude.m4 | 1 + gb.v4l/component.am | 1 + gb.v4l/configure.ac | 26 + gb.v4l/gambas.h | 1 + gb.v4l/gb.image.h | 1 + gb.v4l/gb_common.h | 1 + gb.v4l/m4 | 1 + gb.v4l/orig/video-capture-0.2.tar.gz | Bin 0 -> 10049 bytes gb.v4l/reconf | 1 + gb.v4l/src/CConverters.c | 255 + gb.v4l/src/CWebcam.c | 1906 + gb.v4l/src/CWebcam.h | 197 + gb.v4l/src/Makefile.am | 14 + gb.v4l/src/gb.v4l.component | 10 + gb.v4l/src/gv4l2.c | 803 + gb.v4l/src/main.c | 50 + gb.v4l/src/main.h | 36 + gb.v4l/src/videodev.h | 343 + gb.xml/AUTHORS | 0 gb.xml/COPYING | 1 + gb.xml/ChangeLog | 0 gb.xml/INSTALL | 1 + gb.xml/Makefile.am | 4 + gb.xml/NEWS | 0 gb.xml/README | 7 + gb.xml/TODO | 8 + gb.xml/acinclude.m4 | 1 + gb.xml/component.am | 1 + gb.xml/configure.ac | 38 + gb.xml/gambas.h | 1 + gb.xml/gb_common.h | 1 + gb.xml/m4 | 1 + gb.xml/reconf | 1 + gb.xml/src/.directory | 6 + gb.xml/src/CDocument.cpp | 238 + gb.xml/src/CDocument.h | 29 + gb.xml/src/CElement.cpp | 411 + gb.xml/src/CElement.h | 29 + gb.xml/src/CExplorer.cpp | 154 + gb.xml/src/CExplorer.h | 37 + gb.xml/src/CNode.cpp | 438 + gb.xml/src/CNode.h | 31 + gb.xml/src/CReader.cpp | 435 + gb.xml/src/CReader.h | 42 + gb.xml/src/CTextNode.cpp | 94 + gb.xml/src/CTextNode.h | 31 + gb.xml/src/Makefile.am | 18 + gb.xml/src/document.cpp | 214 + gb.xml/src/document.h | 38 + gb.xml/src/element.cpp | 335 + gb.xml/src/element.h | 63 + gb.xml/src/explorer.cpp | 138 + gb.xml/src/explorer.h | 65 + gb.xml/src/gb.xml.component | 5 + gb.xml/src/gb.xml.h | 204 + gb.xml/src/gb.xml/.component | 4 + gb.xml/src/gb.xml/.directory | 2 + gb.xml/src/gb.xml/.icon.png | Bin 0 -> 10933 bytes gb.xml/src/gb.xml/.project | 10 + gb.xml/src/gb.xml/.src/MTest.module | 34 + gb.xml/src/gb.xml/.src/MTest2.module | 32 + gb.xml/src/gb.xml/.src/XmlReader.class | 104 + gb.xml/src/gb.xml/.src/XmlWriter.class | 241 + gb.xml/src/gb.xml/.src/_XmlWriterDTD.class | 8 + gb.xml/src/gb.xml/text.xml | 2 + gb.xml/src/gbinterface.h | 27 + gb.xml/src/html/CHTMLDocument.cpp | 236 + gb.xml/src/html/CHTMLDocument.h | 34 + gb.xml/src/html/CHTMLElement.cpp | 121 + gb.xml/src/html/CHTMLElement.h | 32 + gb.xml/src/html/Makefile.am | 19 + gb.xml/src/html/cssfilter.cpp | 195 + gb.xml/src/html/cssfilter.h | 8 + gb.xml/src/html/gb.xml.html.component | 5 + gb.xml/src/html/gb.xml.html.h | 26 + gb.xml/src/html/htmldocument.cpp | 317 + gb.xml/src/html/htmldocument.h | 61 + gb.xml/src/html/htmlelement.cpp | 104 + gb.xml/src/html/htmlelement.h | 42 + gb.xml/src/html/htmlmain.cpp | 63 + gb.xml/src/html/htmlmain.h | 35 + gb.xml/src/html/htmlparser.cpp | 342 + gb.xml/src/html/htmlparser.h | 9 + gb.xml/src/html/htmlserializer.cpp | 239 + gb.xml/src/html/htmlserializer.h | 10 + gb.xml/src/main.cpp | 139 + gb.xml/src/main.h | 32 + gb.xml/src/node.cpp | 646 + gb.xml/src/node.h | 98 + gb.xml/src/parser.cpp | 341 + gb.xml/src/parser.h | 14 + gb.xml/src/reader.cpp | 499 + gb.xml/src/reader.h | 114 + gb.xml/src/rpc/Makefile.am | 2 + gb.xml/src/rpc/gb.xml.rpc.component | 1 + gb.xml/src/rpc/gb.xml.rpc/.component | 4 + gb.xml/src/rpc/gb.xml.rpc/.directory | 2 + gb.xml/src/rpc/gb.xml.rpc/.icon.png | Bin 0 -> 10569 bytes gb.xml/src/rpc/gb.xml.rpc/.project | 14 + gb.xml/src/rpc/gb.xml.rpc/.src/RpcArray.class | 250 + gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class | 29 + .../src/rpc/gb.xml.rpc/.src/RpcClient.class | 372 + .../src/rpc/gb.xml.rpc/.src/RpcFunction.class | 125 + .../src/rpc/gb.xml.rpc/.src/RpcServer.class | 357 + .../src/rpc/gb.xml.rpc/.src/RpcStruct.class | 151 + gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class | 101 + .../rpc/gb.xml.rpc/.src/Test/CXMLRPC.class | 94 + .../src/rpc/gb.xml.rpc/.src/Test/MMain.module | 63 + .../src/rpc/gb.xml.rpc/.src/Test/MTest.module | 61 + gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module | 338 + gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class | 101 + gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class | 94 + .../src/rpc/gb.xml.rpc/.src/miniServer.class | 465 + gb.xml/src/serializer.cpp | 351 + gb.xml/src/serializer.h | 18 + gb.xml/src/textnode.cpp | 336 + gb.xml/src/textnode.h | 51 + gb.xml/src/utils.cpp | 372 + gb.xml/src/utils.h | 62 + gb.xml/src/xslt/CXSLT.cpp | 181 + gb.xml/src/xslt/CXSLT.h | 44 + gb.xml/src/xslt/Makefile.am | 11 + gb.xml/src/xslt/gb.xml.xslt.component | 6 + gb.xml/src/xslt/main.cpp | 48 + gb.xml/src/xslt/main.h | 38 + gb.xml/src/xslt/xslt.pro | 14 + logo/gambas-ide.svg | 166 + logo/gambas.svg | 91 + m4/ax_compare_version.m4 | 176 + m4/gb_cflags_gcc_option.m4 | 222 + m4/gb_httpd.m4 | 176 + m4/gb_sdl.m4 | 183 + main/AUTHORS | 0 main/COPYING | 1 + main/ChangeLog | 1 + main/INSTALL | 231 + main/Makefile.am | 76 + main/NEWS | 0 main/README | 0 main/TODO | 0 main/acinclude.m4 | 1 + main/component.am | 1 + main/configure.ac | 155 + main/gb.pcre.h | 1 + main/gbc/Makefile.am | 72 + main/gbc/gb_alloc.c | 24 + main/gbc/gb_array.c | 25 + main/gbc/gb_buffer.c | 25 + main/gbc/gb_common.c | 32 + main/gbc/gb_error.c | 289 + main/gbc/gb_error.h | 129 + main/gbc/gb_file.c | 29 + main/gbc/gb_file.h | 24 + main/gbc/gb_str.c | 199 + main/gbc/gb_str.h | 42 + main/gbc/gb_table.c | 26 + main/gbc/gba.c | 487 + main/gbc/gbc.c | 864 + main/gbc/gbc_arch.c | 43 + main/gbc/gbc_archive.c | 444 + main/gbc/gbc_archive.h | 49 + main/gbc/gbc_chown.c | 52 + main/gbc/gbc_chown.h | 29 + main/gbc/gbc_class.c | 1063 + main/gbc/gbc_class.h | 283 + main/gbc/gbc_code.c | 37 + main/gbc/gbc_compile.c | 823 + main/gbc/gbc_compile.h | 150 + main/gbc/gbc_dump.c | 900 + main/gbc/gbc_form.c | 597 + main/gbc/gbc_form.h | 59 + main/gbc/gbc_form_webpage.c | 526 + main/gbc/gbc_header.c | 1219 + main/gbc/gbc_header.h | 69 + main/gbc/gbc_help.c | 182 + main/gbc/gbc_help.h | 40 + main/gbc/gbc_output.c | 1424 + main/gbc/gbc_output.h | 52 + main/gbc/gbc_pcode.c | 28 + main/gbc/gbc_preprocess.c | 316 + main/gbc/gbc_preprocess.h | 37 + main/gbc/gbc_read.c | 901 + main/gbc/gbc_read.h | 43 + main/gbc/gbc_reserved.c | 27 + main/gbc/gbc_reserved_make.c | 469 + main/gbc/gbc_trans.c | 861 + main/gbc/gbc_trans.h | 285 + main/gbc/gbc_trans_code.c | 781 + main/gbc/gbc_trans_const.c | 494 + main/gbc/gbc_trans_ctrl.c | 1379 + main/gbc/gbc_trans_expr.c | 1196 + main/gbc/gbc_trans_subr.c | 1061 + main/gbc/gbc_trans_tree.c | 882 + main/gbc/gbc_type.c | 219 + main/gbc/gbc_type.h | 115 + main/gbc/gbi.c | 938 + main/gbx/Makefile.am | 98 + main/gbx/gb.jit.h | 119 + main/gbx/gb_alloc.c | 27 + main/gbx/gb_array.c | 25 + main/gbx/gb_buffer.c | 25 + main/gbx/gb_common.c | 111 + main/gbx/gb_common_check.h | 40 + main/gbx/gb_error.c | 819 + main/gbx/gb_error.h | 196 + main/gbx/gb_file.c | 38 + main/gbx/gb_file.h | 26 + main/gbx/gb_hash.c | 24 + main/gbx/gb_list.c | 24 + main/gbx/gb_table.c | 28 + main/gbx/gbx.c | 541 + main/gbx/gbx.h | 31 + main/gbx/gbx_api.c | 2613 ++ main/gbx/gbx_api.h | 242 + main/gbx/gbx_archive.c | 798 + main/gbx/gbx_archive.h | 103 + main/gbx/gbx_c_application.c | 404 + main/gbx/gbx_c_application.h | 35 + main/gbx/gbx_c_array.c | 2604 ++ main/gbx/gbx_c_array.h | 134 + main/gbx/gbx_c_class.c | 1029 + main/gbx/gbx_c_class.h | 38 + main/gbx/gbx_c_collection.c | 459 + main/gbx/gbx_c_collection.h | 70 + main/gbx/gbx_c_enum.c | 210 + main/gbx/gbx_c_enum.h | 54 + main/gbx/gbx_c_error.c | 169 + main/gbx/gbx_c_error.h | 33 + main/gbx/gbx_c_file.c | 1212 + main/gbx/gbx_c_file.h | 86 + main/gbx/gbx_c_gambas.c | 272 + main/gbx/gbx_c_gambas.h | 35 + main/gbx/gbx_c_observer.c | 164 + main/gbx/gbx_c_observer.h | 57 + main/gbx/gbx_c_process.c | 1276 + main/gbx/gbx_c_process.h | 83 + main/gbx/gbx_c_string.c | 1042 + main/gbx/gbx_c_string.h | 50 + main/gbx/gbx_c_system.c | 460 + main/gbx/gbx_c_system.h | 35 + main/gbx/gbx_c_task.c | 768 + main/gbx/gbx_c_task.h | 62 + main/gbx/gbx_c_timer.c | 262 + main/gbx/gbx_c_timer.h | 56 + main/gbx/gbx_class.c | 1648 + main/gbx/gbx_class.h | 570 + main/gbx/gbx_class_desc.h | 209 + main/gbx/gbx_class_info.c | 448 + main/gbx/gbx_class_init.c | 187 + main/gbx/gbx_class_load.c | 1368 + main/gbx/gbx_class_load.h | 208 + main/gbx/gbx_class_native.c | 358 + main/gbx/gbx_compare.c | 690 + main/gbx/gbx_compare.h | 63 + main/gbx/gbx_component.c | 449 + main/gbx/gbx_component.h | 91 + main/gbx/gbx_date.c | 1028 + main/gbx/gbx_date.h | 109 + main/gbx/gbx_debug.c | 762 + main/gbx/gbx_debug.h | 90 + main/gbx/gbx_eval.c | 162 + main/gbx/gbx_eval.h | 41 + main/gbx/gbx_event.c | 274 + main/gbx/gbx_event.h | 59 + main/gbx/gbx_exec.c | 2060 + main/gbx/gbx_exec.h | 442 + main/gbx/gbx_exec_enum.c | 97 + main/gbx/gbx_exec_loop.c | 4781 ++ main/gbx/gbx_exec_operator.c | 368 + main/gbx/gbx_exec_pop.c | 304 + main/gbx/gbx_exec_push.c | 590 + main/gbx/gbx_expression.h | 75 + main/gbx/gbx_extern.c | 761 + main/gbx/gbx_extern.h | 49 + main/gbx/gbx_info.h | 70 + main/gbx/gbx_jit.c | 500 + main/gbx/gbx_jit.h | 63 + main/gbx/gbx_library.c | 375 + main/gbx/gbx_library.h | 79 + main/gbx/gbx_local.c | 2226 + main/gbx/gbx_local.h | 129 + main/gbx/gbx_math.c | 242 + main/gbx/gbx_math.h | 72 + main/gbx/gbx_number.c | 601 + main/gbx/gbx_number.h | 45 + main/gbx/gbx_object.c | 549 + main/gbx/gbx_object.h | 253 + main/gbx/gbx_project.c | 389 + main/gbx/gbx_project.h | 63 + main/gbx/gbx_regexp.c | 304 + main/gbx/gbx_regexp.h | 37 + main/gbx/gbx_replace.c | 25 + main/gbx/gbx_signal.c | 494 + main/gbx/gbx_signal.h | 69 + main/gbx/gbx_split.c | 243 + main/gbx/gbx_split.h | 31 + main/gbx/gbx_stack.c | 187 + main/gbx/gbx_stack.h | 145 + main/gbx/gbx_stream.c | 2227 + main/gbx/gbx_stream.h | 288 + main/gbx/gbx_stream_arch.c | 165 + main/gbx/gbx_stream_buffer.c | 217 + main/gbx/gbx_stream_direct.c | 230 + main/gbx/gbx_stream_lock.c | 109 + main/gbx/gbx_stream_memory.c | 187 + main/gbx/gbx_stream_null.c | 117 + main/gbx/gbx_stream_pipe.c | 137 + main/gbx/gbx_stream_process.c | 119 + main/gbx/gbx_stream_string.c | 140 + main/gbx/gbx_string.c | 1416 + main/gbx/gbx_string.h | 236 + main/gbx/gbx_struct.c | 59 + main/gbx/gbx_struct.h | 51 + main/gbx/gbx_subr.c | 250 + main/gbx/gbx_subr.h | 263 + main/gbx/gbx_subr_conv.c | 291 + main/gbx/gbx_subr_extern.c | 365 + main/gbx/gbx_subr_file.c | 1153 + main/gbx/gbx_subr_math.c | 963 + main/gbx/gbx_subr_math_temp.h | 463 + main/gbx/gbx_subr_misc.c | 448 + main/gbx/gbx_subr_string.c | 1315 + main/gbx/gbx_subr_test.c | 588 + main/gbx/gbx_subr_test_temp.h | 333 + main/gbx/gbx_subr_time.c | 287 + main/gbx/gbx_test.c | 93 + main/gbx/gbx_test.h | 32 + main/gbx/gbx_type.c | 351 + main/gbx/gbx_type.h | 99 + main/gbx/gbx_value.c | 2194 + main/gbx/gbx_value.h | 661 + main/gbx/gbx_variant.h | 99 + main/gbx/gbx_watch.c | 766 + main/gbx/gbx_watch.h | 75 + main/lib/Makefile.am | 2 + main/lib/clipper/LICENSE | 26 + main/lib/clipper/Makefile.am | 19 + main/lib/clipper/c_clipper.cpp | 485 + main/lib/clipper/c_clipper.h | 48 + main/lib/clipper/clipper.cpp | 4630 ++ main/lib/clipper/clipper.hpp | 406 + main/lib/clipper/gb.clipper.component | 4 + main/lib/clipper/gb.geom.h | 1 + main/lib/clipper/main.cpp | 62 + main/lib/clipper/main.h | 36 + main/lib/complex/Makefile.am | 14 + main/lib/complex/ccomplex.c | 543 + main/lib/complex/ccomplex.h | 44 + main/lib/complex/gb.complex.component | 4 + main/lib/complex/main.c | 58 + main/lib/complex/main.h | 34 + main/lib/compress/CCompress.c | 187 + main/lib/compress/CCompress.h | 51 + main/lib/compress/CUncompress.c | 149 + main/lib/compress/CUncompress.h | 49 + main/lib/compress/Makefile.am | 12 + main/lib/compress/gb.compress.component | 3 + main/lib/compress/gb.compress.h | 68 + main/lib/compress/main.c | 111 + main/lib/compress/main.h | 39 + main/lib/data/Makefile.am | 21 + main/lib/data/TODO | 55 + main/lib/data/c_avltree.c | 764 + main/lib/data/c_avltree.h | 33 + main/lib/data/c_circular.c | 307 + main/lib/data/c_circular.h | 33 + main/lib/data/c_deque.c | 391 + main/lib/data/c_deque.h | 36 + main/lib/data/c_graph.c | 485 + main/lib/data/c_graph.h | 66 + main/lib/data/c_graphmatrix.c | 699 + main/lib/data/c_graphmatrix.h | 34 + main/lib/data/c_heap.c | 406 + main/lib/data/c_heap.h | 33 + main/lib/data/c_list.c | 1363 + main/lib/data/c_list.h | 36 + main/lib/data/c_trie.c | 590 + main/lib/data/c_trie.h | 33 + main/lib/data/gb.data.component | 3 + main/lib/data/gb.data/.component | 3 + main/lib/data/gb.data/.directory | 2 + main/lib/data/gb.data/.icon.png | Bin 0 -> 10569 bytes main/lib/data/gb.data/.project | 10 + main/lib/data/gb.data/.src/MMain.module | 4 + main/lib/data/gb.data/.src/PrioSet.class | 63 + .../data/gb.data/.src/_PrioSet_Entry.class | 17 + main/lib/data/list.h | 99 + main/lib/data/lookup3.h | 142 + main/lib/data/main.c | 81 + main/lib/data/main.h | 32 + main/lib/data/string_compare.h | 43 + main/lib/data/trie.c | 756 + main/lib/data/trie.h | 82 + main/lib/db/CConnection.c | 854 + main/lib/db/CConnection.h | 56 + main/lib/db/CDatabase.c | 206 + main/lib/db/CDatabase.h | 55 + main/lib/db/CField.c | 329 + main/lib/db/CField.h | 58 + main/lib/db/CIndex.c | 274 + main/lib/db/CIndex.h | 56 + main/lib/db/CResult.c | 1011 + main/lib/db/CResult.h | 78 + main/lib/db/CResultField.c | 258 + main/lib/db/CResultField.h | 57 + main/lib/db/CTable.c | 450 + main/lib/db/CTable.h | 63 + main/lib/db/CUser.c | 229 + main/lib/db/CUser.h | 56 + main/lib/db/Makefile.am | 25 + main/lib/db/c_subcollection.c | 280 + main/lib/db/c_subcollection.h | 68 + main/lib/db/deletemap.c | 183 + main/lib/db/deletemap.h | 35 + main/lib/db/gb.db.component | 4 + main/lib/db/gb.db.h | 273 + main/lib/db/gb.db.proto.h | 148 + main/lib/db/gb.db/.component | 3 + main/lib/db/gb.db/.directory | 2 + main/lib/db/gb.db/.icon.png | Bin 0 -> 10933 bytes main/lib/db/gb.db/.project | 11 + main/lib/db/gb.db/.src/Connection.class | 592 + main/lib/db/gb.db/.src/Connections.class | 215 + main/lib/db/gb.db/.src/Main.module | 15 + main/lib/db/gb.db/.src/SQLRequest.class | 176 + main/lib/db/gb_barray.h | 57 + main/lib/db/main.c | 767 + main/lib/db/main.h | 65 + main/lib/db/sqlite.c | 213 + main/lib/db/sqlite.h | 35 + main/lib/debug/CDebug.c | 323 + main/lib/debug/CDebug.h | 39 + main/lib/debug/Makefile.am | 18 + main/lib/debug/debug.c | 1504 + main/lib/debug/debug.h | 74 + main/lib/debug/gb.debug.component | 2 + main/lib/debug/gb.debug.h | 128 + main/lib/debug/main.c | 73 + main/lib/debug/main.h | 36 + main/lib/debug/print.c | 495 + main/lib/debug/print.h | 39 + main/lib/debug/profile.c | 246 + main/lib/debug/profile.h | 34 + main/lib/draw/Makefile.am | 15 + main/lib/draw/cdraw.c | 1309 + main/lib/draw/cdraw.h | 45 + main/lib/draw/cpaint.c | 1855 + main/lib/draw/cpaint.h | 52 + main/lib/draw/gb.draw.h | 101 + main/lib/draw/gb.geom.h | 1 + main/lib/draw/gb.image.h | 1 + main/lib/draw/gb.paint.h | 244 + main/lib/draw/gb_list.c | 24 + main/lib/draw/main.c | 78 + main/lib/draw/main.h | 39 + main/lib/draw/matrix.c | 221 + main/lib/draw/matrix.h | 49 + main/lib/eval/Makefile.am | 29 + main/lib/eval/c_expression.c | 196 + main/lib/eval/c_expression.h | 49 + main/lib/eval/c_highlight.c | 317 + main/lib/eval/c_highlight.h | 36 + main/lib/eval/c_system.c | 131 + main/lib/eval/c_system.h | 35 + main/lib/eval/eval.c | 291 + main/lib/eval/eval.h | 62 + main/lib/eval/eval_analyze.c | 766 + main/lib/eval/eval_analyze.h | 32 + main/lib/eval/eval_code.c | 40 + main/lib/eval/eval_code.h | 109 + main/lib/eval/eval_read.c | 759 + main/lib/eval/eval_read.h | 36 + main/lib/eval/eval_reserved.c | 26 + main/lib/eval/eval_trans.c | 458 + main/lib/eval/eval_trans.h | 73 + main/lib/eval/eval_trans_expr.c | 626 + main/lib/eval/eval_trans_tree.c | 713 + main/lib/eval/gb.eval.component | 20 + main/lib/eval/gb.eval.h | 111 + main/lib/eval/gb_alloc_override.h | 34 + main/lib/eval/gb_array.c | 26 + main/lib/eval/gb_error.c | 116 + main/lib/eval/gb_error.h | 59 + main/lib/eval/gb_table.c | 28 + main/lib/eval/main.c | 78 + main/lib/eval/main.h | 35 + main/lib/gb.component | 4 + main/lib/geom/Makefile.am | 13 + main/lib/geom/cpoint.c | 33 + main/lib/geom/cpoint.h | 54 + main/lib/geom/cpoint_temp.h | 262 + main/lib/geom/crect.c | 32 + main/lib/geom/crect.h | 60 + main/lib/geom/crect_temp.h | 427 + main/lib/geom/gb.geom.component | 3 + main/lib/geom/gb.geom.h | 105 + main/lib/geom/main.c | 67 + main/lib/geom/main.h | 37 + main/lib/gui.opengl/Makefile.am | 11 + main/lib/gui.opengl/gb.gui.opengl.component | 6 + main/lib/gui.opengl/main.c | 75 + main/lib/gui.opengl/main.h | 34 + main/lib/gui.qt.opengl/Makefile.am | 12 + .../gui.qt.opengl/gb.gui.qt.opengl.component | 4 + main/lib/gui.qt.opengl/main.c | 71 + main/lib/gui.qt.opengl/main.h | 34 + main/lib/gui.qt.webkit/Makefile.am | 12 + .../gui.qt.webkit/gb.gui.qt.webkit.component | 4 + main/lib/gui.qt.webkit/main.c | 71 + main/lib/gui.qt.webkit/main.h | 34 + main/lib/gui.qt/Makefile.am | 12 + main/lib/gui.qt/gb.gui.qt.component | 6 + main/lib/gui.qt/gb_gui_test_temp.h | 1 + main/lib/gui.qt/main.c | 132 + main/lib/gui.qt/main.h | 36 + main/lib/gui.trayicon/Makefile.am | 13 + main/lib/gui.trayicon/cfaketrayicon.c | 83 + main/lib/gui.trayicon/cfaketrayicon.h | 35 + .../gui.trayicon/gb.gui.trayicon.component | 5 + main/lib/gui.trayicon/main.c | 81 + main/lib/gui.trayicon/main.h | 34 + main/lib/gui.webview/Makefile.am | 11 + main/lib/gui.webview/gb.gui.webview.component | 5 + main/lib/gui.webview/main.c | 73 + main/lib/gui.webview/main.h | 34 + main/lib/gui/Makefile.am | 12 + main/lib/gui/gb.gui.component | 6 + main/lib/gui/gb_gui_test_temp.h | 85 + main/lib/gui/main.c | 152 + main/lib/gui/main.h | 34 + main/lib/hash/Makefile.am | 14 + main/lib/hash/c_hash.c | 75 + main/lib/hash/c_hash.h | 33 + main/lib/hash/gb.hash.component | 4 + main/lib/hash/gb.hash.h | 51 + main/lib/hash/hash.c | 1506 + main/lib/hash/hash.h | 76 + main/lib/hash/main.c | 150 + main/lib/hash/main.h | 41 + main/lib/hash/platform.h | 639 + main/lib/image.effect/CImage.cpp | 473 + main/lib/image.effect/CImage.h | 48 + main/lib/image.effect/Makefile.am | 29 + main/lib/image.effect/effect.cpp | 153 + main/lib/image.effect/effect.h | 44 + .../image.effect/gb.image.effect.component | 5 + main/lib/image.effect/kcpuinfo.cpp | 212 + main/lib/image.effect/kcpuinfo.h | 92 + main/lib/image.effect/kimageeffect.cpp | 5043 ++ main/lib/image.effect/kimageeffect.h | 811 + main/lib/image.effect/main.cpp | 51 + main/lib/image.effect/main.h | 36 + main/lib/image.effect/qcolor.cpp | 1021 + main/lib/image.effect/qcolor.h | 255 + main/lib/image.effect/qimage.cpp | 147 + main/lib/image.effect/qimage.h | 493 + main/lib/image.effect/qpoint.cpp | 463 + main/lib/image.effect/qpoint.h | 235 + main/lib/image.effect/qrect.cpp | 981 + main/lib/image.effect/qrect.h | 294 + main/lib/image.effect/qsize.cpp | 452 + main/lib/image.effect/qsize.h | 256 + main/lib/image.effect/qt.h | 57 + main/lib/image/CImage.c | 598 + main/lib/image/CImage.h | 48 + main/lib/image/CImageStat.c | 140 + main/lib/image/CImageStat.h | 51 + main/lib/image/Makefile.am | 24 + main/lib/image/c_color.c | 729 + main/lib/image/c_color.h | 53 + main/lib/image/gb.image.component | 5 + main/lib/image/gb.image.h | 173 + main/lib/image/image.c | 2452 + main/lib/image/image.h | 89 + main/lib/image/image_stat.c | 624 + main/lib/image/image_stat.h | 66 + main/lib/image/main.c | 83 + main/lib/image/main.h | 35 + main/lib/inotify/Makefile.am | 13 + main/lib/inotify/TODO | 5 + main/lib/inotify/c_watch.c | 709 + main/lib/inotify/c_watch.h | 46 + main/lib/inotify/gb.inotify.component | 3 + main/lib/inotify/gb_list.c | 24 + main/lib/inotify/main.c | 43 + main/lib/inotify/main.h | 32 + main/lib/jit/Makefile.am | 16 + main/lib/jit/gb.jit.component | 6 + main/lib/jit/gb.jit/.component | 4 + main/lib/jit/gb.jit/.directory | 2 + main/lib/jit/gb.jit/.hidden/icon.png | Bin 0 -> 693 bytes main/lib/jit/gb.jit/.icon.png | Bin 0 -> 10448 bytes main/lib/jit/gb.jit/.project | 14 + main/lib/jit/gb.jit/.src/CCompilation.class | 165 + main/lib/jit/gb.jit/.src/Jit.module | 202 + main/lib/jit/gb.jit/.src/Main.module | 7 + main/lib/jit/gb.jit/.src/_ClassStat.class | 100 + main/lib/jit/gb.jit/gambas.h | 1 + main/lib/jit/gb.jit/gb.jit.h | 1 + main/lib/jit/gb.jit/gb_error_common.h | 1 + main/lib/jit/gb.jit/gb_overflow.h | 1 + main/lib/jit/gb.jit/jit.h | 684 + main/lib/jit/gb.jit/jit_after.h | 21 + main/lib/jit/gb_str.c | 227 + main/lib/jit/gb_str.h | 42 + main/lib/jit/gbc_reserved.c | 30 + main/lib/jit/jit.c | 567 + main/lib/jit/jit.h | 94 + main/lib/jit/jit_body.c | 3783 ++ main/lib/jit/main.c | 66 + main/lib/jit/main.h | 39 + main/lib/option/Makefile.am | 14 + main/lib/option/gb.option.component | 4 + main/lib/option/getoptions.c | 396 + main/lib/option/getoptions.h | 56 + main/lib/option/main.c | 90 + main/lib/option/main.h | 35 + main/lib/signal/Makefile.am | 14 + main/lib/signal/csignal.c | 269 + main/lib/signal/csignal.h | 34 + main/lib/signal/gb.signal.component | 3 + main/lib/signal/main.c | 48 + main/lib/signal/main.h | 34 + main/lib/term/Makefile.am | 14 + main/lib/term/cterm.c | 478 + main/lib/term/cterm.h | 44 + main/lib/term/gb.term.component | 3 + main/lib/term/main.c | 49 + main/lib/term/main.h | 36 + main/lib/test/Makefile.am | 14 + main/lib/test/gb.test.component | 5 + main/lib/test/gb.test/.component | 5 + main/lib/test/gb.test/.directory | 2 + main/lib/test/gb.test/.hidden/CHANGELOG | 177 + main/lib/test/gb.test/.hidden/flowchart.fodg | 617 + main/lib/test/gb.test/.hidden/flowchart.svg | 352 + main/lib/test/gb.test/.hidden/gb.test.png | Bin 0 -> 240 bytes .../test/gb.test/.hidden/summary-example.txt | 14 + main/lib/test/gb.test/.icon.png | Bin 0 -> 8205 bytes main/lib/test/gb.test/.lang/de.po | 135 + main/lib/test/gb.test/.lang/it.po | 20 + main/lib/test/gb.test/.lang/pt_BR.po | 157 + main/lib/test/gb.test/.project | 25 + main/lib/test/gb.test/.src/Helper.module | 141 + .../test/gb.test/.src/Tap/TapContext.class | 16 + .../lib/test/gb.test/.src/Tap/TapParser.class | 189 + .../test/gb.test/.src/Tap/TapPrinter.class | 204 + .../lib/test/gb.test/.src/TestAssertion.class | 54 + .../gb.test/.src/TestMyself/MustFail.test | 30 + .../gb.test/.src/TestMyself/TAllAsserts.test | 368 + .../gb.test/.src/TestMyself/TBailout.test | 8 + .../gb.test/.src/TestMyself/TCrashes.test | 22 + .../test/gb.test/.src/TestMyself/TElse.test | 31 + .../test/gb.test/.src/TestMyself/TEmpty.test | 3 + .../.src/TestMyself/TIntendedFailures.test | 66 + .../gb.test/.src/TestMyself/TInternals.test | 141 + .../test/gb.test/.src/TestMyself/TParser.test | 123 + .../test/gb.test/.src/TestMyself/TSetup.test | 52 + .../gb.test/.src/TestMyself/TSkipAll.test | 7 + .../gb.test/.src/TestMyself/TSummary.test | 62 + .../gb.test/.src/TestMyself/TWrongPlan.test | 10 + main/lib/test/gb.test/.src/TestRunner.module | 179 + main/lib/test/gb.test/.src/TestStats.class | 47 + .../test/gb.test/.src/TestSuite/Assert.module | 335 + .../test/gb.test/.src/TestSuite/Test.module | 494 + .../gb.test/.src/TestSuite/TestCase.class | 79 + .../gb.test/.src/TestSuite/TestCommand.class | 127 + .../gb.test/.src/TestSuite/TestSuite.class | 137 + main/lib/test/gb.test/.src/ZzzDoSth.module | 23 + main/lib/test/gb.test/.test | 37 + main/lib/test/gb.test/LICENSE | 340 + main/lib/test/gb.test/README.md | 151 + main/lib/test/gb.test/taskell.md | 54 + main/lib/test/gb.test/test.sh | 5 + .../gb.test/unittesthelloworld-1.0.0.tar.gz | Bin 0 -> 12298 bytes main/lib/test/main.c | 64 + main/lib/test/main.h | 36 + main/lib/vb/Makefile.am | 14 + main/lib/vb/gb.vb.component | 6 + main/lib/vb/main.c | 50 + main/lib/vb/main.h | 35 + main/lib/vb/vb.c | 312 + main/lib/vb/vb.h | 33 + main/lib/vb/vbdate.c | 215 + main/lib/vb/vbdate.h | 32 + main/m4 | 1 + main/man/Makefile.am | 1 + main/man/gba3.1 | 54 + main/man/gbc3.1 | 72 + main/man/gbh3.1 | 49 + main/man/gbi3.1 | 45 + main/man/gbr3.1 | 66 + main/man/gbx3.1 | 72 + main/mime/application-x-gambas3-48.png | Bin 0 -> 1437 bytes main/mime/application-x-gambas3.png | Bin 0 -> 7788 bytes main/mime/application-x-gambas3.xml | 12 + main/reconf | 1 + main/share/Makefile.am | 1 + main/share/gambas.h | 1320 + main/share/gb_alloc.h | 117 + main/share/gb_alloc_temp.h | 495 + main/share/gb_arch.h | 90 + main/share/gb_arch_temp.h | 362 + main/share/gb_array.h | 121 + main/share/gb_array_temp.h | 217 + main/share/gb_buffer.h | 49 + main/share/gb_buffer_temp.h | 163 + main/share/gb_class_desc_common.h | 40 + main/share/gb_code.h | 171 + main/share/gb_code_temp.h | 1511 + main/share/gb_common.h | 209 + main/share/gb_common_buffer.h | 64 + main/share/gb_common_buffer_temp.h | 65 + main/share/gb_common_case.h | 60 + main/share/gb_common_case_temp.h | 104 + main/share/gb_common_string.h | 37 + main/share/gb_common_string_temp.h | 197 + main/share/gb_common_swap.h | 42 + main/share/gb_common_swap_temp.h | 71 + main/share/gb_component.h | 72 + main/share/gb_error_common.h | 213 + main/share/gb_file_share.h | 143 + main/share/gb_file_temp.h | 1308 + main/share/gb_hash.h | 106 + main/share/gb_hash_temp.h | 534 + main/share/gb_limit.h | 106 + main/share/gb_list.h | 43 + main/share/gb_list_temp.h | 90 + main/share/gb_magic.h | 35 + main/share/gb_overflow.h | 46 + main/share/gb_pcode.h | 208 + main/share/gb_pcode_temp.h | 602 + main/share/gb_replace.h | 51 + main/share/gb_replace_temp.h | 135 + main/share/gb_reserved.h | 411 + main/share/gb_reserved_keyword.h | 600 + main/share/gb_reserved_temp.h | 982 + main/share/gb_system.h | 29 + main/share/gb_system_temp.h | 64 + main/share/gb_table.h | 106 + main/share/gb_table_temp.h | 605 + main/share/gb_type_common.h | 92 + main/share/gbc_read_common.h | 108 + main/share/gbc_read_temp.h | 504 + main/share/gbc_trans_common.h | 173 + main/share/gbx_subr_common.h | 680 + main/tools/gbh3/.directory | 2 + main/tools/gbh3/.icon.png | Bin 0 -> 3398 bytes main/tools/gbh3/.project | 11 + main/tools/gbh3/.src/MMain.module | 447 + main/tools/gbh3/.src/MOldMain.module | 256 + main/tools/gbh3/README | 112 + main/tools/gbh3/icon.png | Bin 0 -> 3211 bytes main/tools/gbh3/license | 6 + main/tools/gbh3/usage | 18 + reconf | 7 + reconf-all | 9 + test-fast | 9 + version.m4 | 13 + 8116 files changed, 1343041 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 AUTHORS create mode 100644 CONTRIBUTING.md create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 README.commit create mode 100644 README.md create mode 100644 TEMPLATE/README create mode 100644 TEMPLATE/TEMPLATE.c create mode 100644 TEMPLATE/TEMPLATE.conf create mode 100644 TEMPLATE/TEMPLATE.cpp create mode 100644 TEMPLATE/TEMPLATE.h create mode 100644 TEMPLATE/conf/gb.cairo.conf create mode 100644 TEMPLATE/conf/gb.dbus.conf create mode 100644 TEMPLATE/conf/gb.desktop.conf create mode 100644 TEMPLATE/conf/gb.form.htmlview.conf create mode 100644 TEMPLATE/conf/gb.gmp.conf create mode 100644 TEMPLATE/conf/gb.gsl.conf create mode 100644 TEMPLATE/conf/gb.image.io.conf create mode 100644 TEMPLATE/conf/gb.media.conf create mode 100644 TEMPLATE/conf/gb.mime.conf create mode 100644 TEMPLATE/conf/gb.net.pop3.conf create mode 100644 TEMPLATE/conf/gb.net.smtp.conf create mode 100644 TEMPLATE/conf/gb.openal.conf create mode 100644 TEMPLATE/conf/gb.poppler.conf create mode 100755 TEMPLATE/make-component create mode 100644 TEMPLATE/template/AUTHORS create mode 100644 TEMPLATE/template/ChangeLog create mode 100644 TEMPLATE/template/Makefile.am create mode 100644 TEMPLATE/template/NEWS create mode 100644 TEMPLATE/template/README create mode 100644 TEMPLATE/template/SOURCES create mode 100644 TEMPLATE/template/configure.ac create mode 100755 TEMPLATE/template/make-component create mode 100644 TEMPLATE/template/src/.component create mode 100644 TEMPLATE/template/src/Makefile.am create mode 100644 TODO create mode 100644 VERSION create mode 100644 acinclude.m4 create mode 100644 app/AUTHORS create mode 120000 app/COPYING create mode 100644 app/ChangeLog create mode 100644 app/INSTALL create mode 100644 app/Makefile.am create mode 100644 app/NEWS create mode 100644 app/README create mode 100644 app/TODO create mode 120000 app/acinclude.m4 create mode 100644 app/configure.ac create mode 100644 app/desktop/gambas3-48.png create mode 100644 app/desktop/gambas3.appdata.xml create mode 100644 app/desktop/gambas3.desktop create mode 100644 app/desktop/gambas3.png create mode 100644 app/desktop/gambas3.svg create mode 100644 app/examples/Basic/Blights/.directory create mode 100644 app/examples/Basic/Blights/.icon.png create mode 100644 app/examples/Basic/Blights/.lang/ca.po create mode 100644 app/examples/Basic/Blights/.lang/cs.po create mode 100644 app/examples/Basic/Blights/.lang/de.po create mode 100644 app/examples/Basic/Blights/.lang/es.po create mode 100644 app/examples/Basic/Blights/.lang/fr.po create mode 100644 app/examples/Basic/Blights/.lang/nl.po create mode 100644 app/examples/Basic/Blights/.lang/ru.po create mode 100644 app/examples/Basic/Blights/.lang/sv.po create mode 100644 app/examples/Basic/Blights/.project create mode 100644 app/examples/Basic/Blights/.src/win1.class create mode 100644 app/examples/Basic/Blights/.src/win1.form create mode 100644 app/examples/Basic/Blights/ampoule.png create mode 100644 app/examples/Basic/Blights/bloff.xpm create mode 100644 app/examples/Basic/Blights/blon.xpm create mode 100644 app/examples/Basic/Collection/.directory create mode 100644 app/examples/Basic/Collection/.icon.png create mode 100644 app/examples/Basic/Collection/.lang/ca.po create mode 100644 app/examples/Basic/Collection/.lang/cs.po create mode 100644 app/examples/Basic/Collection/.lang/de.po create mode 100644 app/examples/Basic/Collection/.lang/es.po create mode 100644 app/examples/Basic/Collection/.lang/nl.po create mode 100644 app/examples/Basic/Collection/.lang/ru.po create mode 100644 app/examples/Basic/Collection/.project create mode 100644 app/examples/Basic/Collection/.src/CThing.class create mode 100644 app/examples/Basic/Collection/.src/FStart.class create mode 100644 app/examples/Basic/Collection/.src/FStart.form create mode 100644 app/examples/Basic/Collection/collection.png create mode 100644 app/examples/Basic/DragNDrop/.directory create mode 100644 app/examples/Basic/DragNDrop/.icon.png create mode 100644 app/examples/Basic/DragNDrop/.lang/ru.po create mode 100644 app/examples/Basic/DragNDrop/.project create mode 100644 app/examples/Basic/DragNDrop/.src/FDragNDrop.class create mode 100644 app/examples/Basic/DragNDrop/.src/FDragNDrop.form create mode 100644 app/examples/Basic/DragNDrop/drop.png create mode 100644 app/examples/Basic/Object/.directory create mode 100644 app/examples/Basic/Object/.icon.png create mode 100644 app/examples/Basic/Object/.lang/ca.po create mode 100644 app/examples/Basic/Object/.lang/cs.po create mode 100644 app/examples/Basic/Object/.lang/de.po create mode 100644 app/examples/Basic/Object/.lang/es.po create mode 100644 app/examples/Basic/Object/.lang/nl.po create mode 100644 app/examples/Basic/Object/.lang/ru.po create mode 100644 app/examples/Basic/Object/.project create mode 100644 app/examples/Basic/Object/.src/CThing.class create mode 100644 app/examples/Basic/Object/.src/FStart.class create mode 100644 app/examples/Basic/Object/.src/FStart.form create mode 100644 app/examples/Basic/Object/object.png create mode 100644 app/examples/Basic/Timer/.directory create mode 100644 app/examples/Basic/Timer/.icon.png create mode 100644 app/examples/Basic/Timer/.lang/ca.po create mode 100644 app/examples/Basic/Timer/.lang/cs.po create mode 100644 app/examples/Basic/Timer/.lang/de.po create mode 100644 app/examples/Basic/Timer/.lang/es.po create mode 100644 app/examples/Basic/Timer/.lang/nl.po create mode 100644 app/examples/Basic/Timer/.lang/ru.po create mode 100644 app/examples/Basic/Timer/.project create mode 100644 app/examples/Basic/Timer/.src/FOtherTimer.class create mode 100644 app/examples/Basic/Timer/.src/FOtherTimer.form create mode 100644 app/examples/Basic/Timer/.src/FTimer.class create mode 100644 app/examples/Basic/Timer/.src/FTimer.form create mode 100644 app/examples/Basic/Timer/timer.png create mode 100644 app/examples/Control/ArrayOfControls/.directory create mode 100644 app/examples/Control/ArrayOfControls/.icon.png create mode 100644 app/examples/Control/ArrayOfControls/.lang/ca.po create mode 100644 app/examples/Control/ArrayOfControls/.lang/cs.po create mode 100644 app/examples/Control/ArrayOfControls/.lang/de.po create mode 100644 app/examples/Control/ArrayOfControls/.lang/nl.po create mode 100644 app/examples/Control/ArrayOfControls/.lang/ru.po create mode 100644 app/examples/Control/ArrayOfControls/.project create mode 100644 app/examples/Control/ArrayOfControls/.src/FMain.class create mode 100644 app/examples/Control/ArrayOfControls/.src/FMain.form create mode 100644 app/examples/Control/ArrayOfControls/green.png create mode 100644 app/examples/Control/ArrayOfControls/green1.png create mode 100644 app/examples/Control/ArrayOfControls/phone.png create mode 100644 app/examples/Control/ArrayOfControls/red.png create mode 100644 app/examples/Control/ArrayOfControls/red1.png create mode 100644 app/examples/Control/Embedder/.directory create mode 100644 app/examples/Control/Embedder/.icon.png create mode 100644 app/examples/Control/Embedder/.lang/ca.po create mode 100644 app/examples/Control/Embedder/.lang/cs.po create mode 100644 app/examples/Control/Embedder/.lang/de.po create mode 100644 app/examples/Control/Embedder/.lang/es.po create mode 100644 app/examples/Control/Embedder/.lang/nl.po create mode 100644 app/examples/Control/Embedder/.lang/ru.po create mode 100644 app/examples/Control/Embedder/.project create mode 100644 app/examples/Control/Embedder/.src/FMain.class create mode 100644 app/examples/Control/Embedder/.src/FMain.form create mode 100644 app/examples/Control/Embedder/embedder.png create mode 100644 app/examples/Control/HighlightEditor/.directory create mode 100644 app/examples/Control/HighlightEditor/.hidden/screenshots/2014-12-17.png create mode 100644 app/examples/Control/HighlightEditor/.icon.png create mode 100644 app/examples/Control/HighlightEditor/.lang/ca.po create mode 100644 app/examples/Control/HighlightEditor/.lang/cs.po create mode 100644 app/examples/Control/HighlightEditor/.lang/de.po create mode 100644 app/examples/Control/HighlightEditor/.lang/es.po create mode 100644 app/examples/Control/HighlightEditor/.lang/nl.po create mode 100644 app/examples/Control/HighlightEditor/.lang/ru.po create mode 100644 app/examples/Control/HighlightEditor/.project create mode 100644 app/examples/Control/HighlightEditor/.src/FEditor.class create mode 100644 app/examples/Control/HighlightEditor/.src/FEditor.form create mode 100644 app/examples/Control/HighlightEditor/download.html create mode 100644 app/examples/Control/HighlightEditor/editor.png create mode 100644 app/examples/Control/LCDLabel/.directory create mode 100644 app/examples/Control/LCDLabel/.icon.png create mode 100644 app/examples/Control/LCDLabel/.lang/ru.po create mode 100644 app/examples/Control/LCDLabel/.project create mode 100644 app/examples/Control/LCDLabel/.src/FMain.class create mode 100644 app/examples/Control/LCDLabel/.src/FMain.form create mode 100644 app/examples/Control/LCDLabel/.src/TimeBoxDemo.class create mode 100644 app/examples/Control/LCDLabel/alarm.ogg create mode 100644 app/examples/Control/LCDLabel/lcdlabel.png create mode 100644 app/examples/Control/MapView/.directory create mode 100644 app/examples/Control/MapView/.hidden/mapview.png create mode 100644 app/examples/Control/MapView/.icon.png create mode 100644 app/examples/Control/MapView/.lang/ru.po create mode 100644 app/examples/Control/MapView/.project create mode 100644 app/examples/Control/MapView/.src/FMain.class create mode 100644 app/examples/Control/MapView/.src/FMain.form create mode 100644 app/examples/Control/TextEdit/.directory create mode 100644 app/examples/Control/TextEdit/.icon.png create mode 100644 app/examples/Control/TextEdit/.lang/ca.po create mode 100644 app/examples/Control/TextEdit/.lang/cs.po create mode 100644 app/examples/Control/TextEdit/.lang/de.po create mode 100644 app/examples/Control/TextEdit/.lang/es.po create mode 100644 app/examples/Control/TextEdit/.lang/fr.po create mode 100644 app/examples/Control/TextEdit/.lang/nl.po create mode 100644 app/examples/Control/TextEdit/.lang/ru.po create mode 100644 app/examples/Control/TextEdit/.lang/sv.po create mode 100644 app/examples/Control/TextEdit/.project create mode 100644 app/examples/Control/TextEdit/.src/FMain.class create mode 100644 app/examples/Control/TextEdit/.src/FMain.form create mode 100644 app/examples/Control/TextEdit/.src/frmShowHtml.class create mode 100644 app/examples/Control/TextEdit/.src/frmShowHtml.form create mode 100644 app/examples/Control/TextEdit/edit.png create mode 100644 app/examples/Control/TextEdit/text.html create mode 100644 app/examples/Control/TreeView/.directory create mode 100644 app/examples/Control/TreeView/.icon.png create mode 100644 app/examples/Control/TreeView/.lang/ca.po create mode 100644 app/examples/Control/TreeView/.lang/cs.po create mode 100644 app/examples/Control/TreeView/.lang/de.po create mode 100644 app/examples/Control/TreeView/.lang/es.po create mode 100644 app/examples/Control/TreeView/.lang/nl.po create mode 100644 app/examples/Control/TreeView/.lang/ru.po create mode 100644 app/examples/Control/TreeView/.project create mode 100644 app/examples/Control/TreeView/.src/TreeViewExample.class create mode 100644 app/examples/Control/TreeView/.src/TreeViewExample.form create mode 100644 app/examples/Control/TreeView/Female.png create mode 100644 app/examples/Control/TreeView/Male.png create mode 100644 app/examples/Control/TreeView/treeview.png create mode 100644 app/examples/Control/Wizard/.directory create mode 100644 app/examples/Control/Wizard/.icon.png create mode 100644 app/examples/Control/Wizard/.lang/ca.po create mode 100644 app/examples/Control/Wizard/.lang/cs.po create mode 100644 app/examples/Control/Wizard/.lang/de.po create mode 100644 app/examples/Control/Wizard/.lang/nl.po create mode 100644 app/examples/Control/Wizard/.lang/ru.po create mode 100644 app/examples/Control/Wizard/.project create mode 100644 app/examples/Control/Wizard/.src/FMain.class create mode 100644 app/examples/Control/Wizard/.src/FMain.form create mode 100644 app/examples/Control/Wizard/wizard.png create mode 100644 app/examples/Database/Database/.component create mode 100644 app/examples/Database/Database/.directory create mode 100644 app/examples/Database/Database/.icon.png create mode 100644 app/examples/Database/Database/.lang/ca.po create mode 100644 app/examples/Database/Database/.lang/cs.po create mode 100644 app/examples/Database/Database/.lang/de.po create mode 100644 app/examples/Database/Database/.lang/es.po create mode 100644 app/examples/Database/Database/.lang/nl.po create mode 100644 app/examples/Database/Database/.lang/ru.po create mode 100644 app/examples/Database/Database/.project create mode 100644 app/examples/Database/Database/.src/FMain.class create mode 100644 app/examples/Database/Database/.src/FMain.form create mode 100644 app/examples/Database/Database/.src/FRequest.class create mode 100644 app/examples/Database/Database/.src/FRequest.form create mode 100644 app/examples/Database/Database/.src/FTest.class create mode 100644 app/examples/Database/Database/.src/FTest.form create mode 100644 app/examples/Database/Database/.src/Form1.class create mode 100644 app/examples/Database/Database/.src/Form1.form create mode 100644 app/examples/Database/Database/database.png create mode 100644 app/examples/Database/MySQLExample/.directory create mode 100644 app/examples/Database/MySQLExample/.icon.png create mode 100644 app/examples/Database/MySQLExample/.lang/ca.po create mode 100644 app/examples/Database/MySQLExample/.lang/cs.po create mode 100644 app/examples/Database/MySQLExample/.lang/de.po create mode 100644 app/examples/Database/MySQLExample/.lang/es.po create mode 100644 app/examples/Database/MySQLExample/.lang/fr.po create mode 100644 app/examples/Database/MySQLExample/.lang/nl.po create mode 100644 app/examples/Database/MySQLExample/.lang/ru.po create mode 100644 app/examples/Database/MySQLExample/.project create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form create mode 100644 app/examples/Database/MySQLExample/.src/FConnect.class create mode 100644 app/examples/Database/MySQLExample/.src/FConnect.form create mode 100644 app/examples/Database/MySQLExample/.src/FMessage.class create mode 100644 app/examples/Database/MySQLExample/.src/FMessage.form create mode 100644 app/examples/Database/MySQLExample/.src/FTables.class create mode 100644 app/examples/Database/MySQLExample/.src/FTables.form create mode 100644 app/examples/Database/MySQLExample/.src/modMain.module create mode 100644 app/examples/Database/MySQLExample/icons/16/Admin.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Blob.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Column_FK.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Database.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Datetime.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Field.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Function.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Index.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Lock.png create mode 100644 app/examples/Database/MySQLExample/icons/16/New.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Numeric.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Primarykey.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Refresh.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Routine.png create mode 100644 app/examples/Database/MySQLExample/icons/16/String.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Table.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Trigger.png create mode 100644 app/examples/Database/MySQLExample/icons/16/View.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Warning.png create mode 100644 app/examples/Database/MySQLExample/icons/24/Null.png create mode 100644 app/examples/Database/PictureDatabase/.directory create mode 100644 app/examples/Database/PictureDatabase/.icon.png create mode 100644 app/examples/Database/PictureDatabase/.lang/ca.po create mode 100644 app/examples/Database/PictureDatabase/.lang/cs.po create mode 100644 app/examples/Database/PictureDatabase/.lang/de.po create mode 100644 app/examples/Database/PictureDatabase/.lang/es.po create mode 100644 app/examples/Database/PictureDatabase/.lang/nl.po create mode 100644 app/examples/Database/PictureDatabase/.lang/ru.po create mode 100644 app/examples/Database/PictureDatabase/.project create mode 100644 app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class create mode 100644 app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form create mode 100644 app/examples/Database/PictureDatabase/.src/ModuleDatabase.module create mode 100644 app/examples/Database/PictureDatabase/Images/document-save-as.png create mode 100644 app/examples/Database/PictureDatabase/Images/document-save.png create mode 100644 app/examples/Database/PictureDatabase/Images/image-x-generic.png create mode 100644 app/examples/Database/PictureDatabase/Images/list-add.png create mode 100644 app/examples/Database/PictureDatabase/Images/list-remove.png create mode 100644 app/examples/Drawing/AnalogWatch/.directory create mode 100644 app/examples/Drawing/AnalogWatch/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/AnalogWatch/.icon.png create mode 100644 app/examples/Drawing/AnalogWatch/.lang/ru.po create mode 100644 app/examples/Drawing/AnalogWatch/.project create mode 100644 app/examples/Drawing/AnalogWatch/.src/FrmClock.class create mode 100644 app/examples/Drawing/AnalogWatch/.src/FrmClock.form create mode 100644 app/examples/Drawing/AnalogWatch/timer.png create mode 100644 app/examples/Drawing/Barcode/.directory create mode 100644 app/examples/Drawing/Barcode/.icon.png create mode 100644 app/examples/Drawing/Barcode/.lang/ca.po create mode 100644 app/examples/Drawing/Barcode/.lang/cs.po create mode 100644 app/examples/Drawing/Barcode/.lang/de.po create mode 100644 app/examples/Drawing/Barcode/.lang/ru.po create mode 100644 app/examples/Drawing/Barcode/.project create mode 100644 app/examples/Drawing/Barcode/.src/FMain.class create mode 100644 app/examples/Drawing/Barcode/.src/FMain.form create mode 100644 app/examples/Drawing/Barcode/.src/modCrBcode.module create mode 100644 app/examples/Drawing/Barcode/barcode.png create mode 100644 app/examples/Drawing/Chart/.directory create mode 100644 app/examples/Drawing/Chart/.icon.png create mode 100644 app/examples/Drawing/Chart/.lang/ca.po create mode 100644 app/examples/Drawing/Chart/.lang/cs.po create mode 100644 app/examples/Drawing/Chart/.lang/de.po create mode 100644 app/examples/Drawing/Chart/.lang/es.po create mode 100644 app/examples/Drawing/Chart/.lang/ru.po create mode 100644 app/examples/Drawing/Chart/.project create mode 100644 app/examples/Drawing/Chart/.src/FormChart.class create mode 100644 app/examples/Drawing/Chart/.src/FormChart.form create mode 100644 app/examples/Drawing/Chart/.src/FormData.class create mode 100644 app/examples/Drawing/Chart/.src/FormData.form create mode 100644 app/examples/Drawing/Chart/graph.png create mode 100644 app/examples/Drawing/Clock/.directory create mode 100644 app/examples/Drawing/Clock/.icon.png create mode 100644 app/examples/Drawing/Clock/.icon/16.png create mode 100644 app/examples/Drawing/Clock/.icon/32.png create mode 100644 app/examples/Drawing/Clock/.icon/48.png create mode 100644 app/examples/Drawing/Clock/.lang/ca.po create mode 100644 app/examples/Drawing/Clock/.lang/cs.po create mode 100644 app/examples/Drawing/Clock/.lang/de.po create mode 100644 app/examples/Drawing/Clock/.lang/es.po create mode 100644 app/examples/Drawing/Clock/.lang/ru.po create mode 100644 app/examples/Drawing/Clock/.project create mode 100644 app/examples/Drawing/Clock/.src/FClock.class create mode 100644 app/examples/Drawing/Clock/.src/FClock.form create mode 100644 app/examples/Drawing/Clock/img/arrow_hour.png create mode 100644 app/examples/Drawing/Clock/img/arrow_min.png create mode 100644 app/examples/Drawing/Clock/img/arrow_sec.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big1.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big2.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big3.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big4.png create mode 100644 app/examples/Drawing/Fractal/.directory create mode 100644 app/examples/Drawing/Fractal/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/Fractal/.icon.png create mode 100644 app/examples/Drawing/Fractal/.lang/cs.po create mode 100644 app/examples/Drawing/Fractal/.lang/fr.po create mode 100644 app/examples/Drawing/Fractal/.lang/ru.po create mode 100644 app/examples/Drawing/Fractal/.project create mode 100644 app/examples/Drawing/Fractal/.src/FFractal.class create mode 100644 app/examples/Drawing/Fractal/.src/FFractal.form create mode 100644 app/examples/Drawing/Fractal/.src/FractalTask.class create mode 100644 app/examples/Drawing/Fractal/icon.png create mode 100644 app/examples/Drawing/Fractal/rose.jpg create mode 100644 app/examples/Drawing/GSLSpline/.directory create mode 100644 app/examples/Drawing/GSLSpline/.icon.png create mode 100644 app/examples/Drawing/GSLSpline/.lang/ru.po create mode 100644 app/examples/Drawing/GSLSpline/.project create mode 100644 app/examples/Drawing/GSLSpline/.src/FMain.class create mode 100644 app/examples/Drawing/GSLSpline/.src/FMain.form create mode 100644 app/examples/Drawing/GSLSpline/spline.png create mode 100644 app/examples/Drawing/Gravity/.directory create mode 100644 app/examples/Drawing/Gravity/.icon.png create mode 100644 app/examples/Drawing/Gravity/.lang/ca.po create mode 100644 app/examples/Drawing/Gravity/.lang/cs.po create mode 100644 app/examples/Drawing/Gravity/.lang/de.po create mode 100644 app/examples/Drawing/Gravity/.lang/es.po create mode 100644 app/examples/Drawing/Gravity/.lang/ru.po create mode 100644 app/examples/Drawing/Gravity/.project create mode 100644 app/examples/Drawing/Gravity/.src/FAbout.class create mode 100644 app/examples/Drawing/Gravity/.src/FAbout.form create mode 100644 app/examples/Drawing/Gravity/.src/FMain.class create mode 100644 app/examples/Drawing/Gravity/.src/FMain.form create mode 100644 app/examples/Drawing/Gravity/.src/cBall.class create mode 100644 app/examples/Drawing/Gravity/gravity.png create mode 100644 app/examples/Drawing/OnScreenDisplay/.directory create mode 100644 app/examples/Drawing/OnScreenDisplay/.icon.png create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/ca.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/cs.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/de.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/es.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/ru.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.project create mode 100644 app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class create mode 100644 app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form create mode 100644 app/examples/Drawing/OnScreenDisplay/icon.png create mode 100644 app/examples/Drawing/Painting/.directory create mode 100644 app/examples/Drawing/Painting/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/Painting/.icon.png create mode 100644 app/examples/Drawing/Painting/.lang/ca.po create mode 100644 app/examples/Drawing/Painting/.lang/cs.po create mode 100644 app/examples/Drawing/Painting/.lang/de.po create mode 100644 app/examples/Drawing/Painting/.lang/ru.po create mode 100644 app/examples/Drawing/Painting/.project create mode 100644 app/examples/Drawing/Painting/.src/FMain.class create mode 100644 app/examples/Drawing/Painting/.src/FMain.form create mode 100644 app/examples/Drawing/Painting/.src/MMakeSourceFile.module create mode 100644 app/examples/Drawing/Painting/Example1 create mode 100644 app/examples/Drawing/Painting/Example10 create mode 100644 app/examples/Drawing/Painting/Example11 create mode 100644 app/examples/Drawing/Painting/Example12 create mode 100644 app/examples/Drawing/Painting/Example13 create mode 100644 app/examples/Drawing/Painting/Example14 create mode 100644 app/examples/Drawing/Painting/Example15 create mode 100644 app/examples/Drawing/Painting/Example16 create mode 100644 app/examples/Drawing/Painting/Example17 create mode 100644 app/examples/Drawing/Painting/Example18 create mode 100644 app/examples/Drawing/Painting/Example2 create mode 100644 app/examples/Drawing/Painting/Example20 create mode 100644 app/examples/Drawing/Painting/Example21 create mode 100644 app/examples/Drawing/Painting/Example22 create mode 100644 app/examples/Drawing/Painting/Example3 create mode 100644 app/examples/Drawing/Painting/Example4 create mode 100644 app/examples/Drawing/Painting/Example5 create mode 100644 app/examples/Drawing/Painting/Example6 create mode 100644 app/examples/Drawing/Painting/Example7 create mode 100644 app/examples/Drawing/Painting/Example8 create mode 100644 app/examples/Drawing/Painting/Example9 create mode 100644 app/examples/Drawing/Painting/clovis.jpg create mode 100644 app/examples/Drawing/Painting/gambas.svg create mode 100644 app/examples/Drawing/Painting/icon.png create mode 100644 app/examples/Drawing/Painting/image.jpg create mode 100644 app/examples/Drawing/QuasiRegular/.directory create mode 100644 app/examples/Drawing/QuasiRegular/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/QuasiRegular/.icon.png create mode 100644 app/examples/Drawing/QuasiRegular/.project create mode 100644 app/examples/Drawing/QuasiRegular/.src/FMain.class create mode 100644 app/examples/Drawing/QuasiRegular/.src/FMain.form create mode 100644 app/examples/Drawing/QuasiRegular/icon.png create mode 100644 app/examples/Drawing/RandomColorSort/.directory create mode 100644 app/examples/Drawing/RandomColorSort/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/RandomColorSort/.icon.png create mode 100644 app/examples/Drawing/RandomColorSort/.lang/ru.po create mode 100644 app/examples/Drawing/RandomColorSort/.project create mode 100644 app/examples/Drawing/RandomColorSort/.src/FMain.class create mode 100644 app/examples/Drawing/RandomColorSort/.src/FMain.form create mode 100644 app/examples/Drawing/RandomColorSort/RandomColorSort.png create mode 100644 app/examples/Drawing/Tablet/.directory create mode 100644 app/examples/Drawing/Tablet/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/Tablet/.icon.png create mode 100644 app/examples/Drawing/Tablet/.lang/ru.po create mode 100644 app/examples/Drawing/Tablet/.project create mode 100644 app/examples/Drawing/Tablet/.src/FMain.class create mode 100644 app/examples/Drawing/Tablet/.src/FMain.form create mode 100644 app/examples/Drawing/Tablet/Icon.png create mode 100644 app/examples/Games/BeastScroll/.dir_icon.png create mode 100644 app/examples/Games/BeastScroll/.directory create mode 100644 app/examples/Games/BeastScroll/.icon.png create mode 100644 app/examples/Games/BeastScroll/.lang/ru.po create mode 100644 app/examples/Games/BeastScroll/.project create mode 100644 app/examples/Games/BeastScroll/.src/MMain.module create mode 100644 app/examples/Games/BeastScroll/b-title.mod create mode 100644 app/examples/Games/BeastScroll/bgd1_ciel.png create mode 100644 app/examples/Games/BeastScroll/bgd2_montagnes.png create mode 100644 app/examples/Games/BeastScroll/bgd3_sol1.png create mode 100644 app/examples/Games/BeastScroll/bgd4_sol2.png create mode 100644 app/examples/Games/BeastScroll/bgd5_sol3.png create mode 100644 app/examples/Games/BeastScroll/fireworks.png create mode 100644 app/examples/Games/BeastScroll/logo.png create mode 100644 app/examples/Games/BeastScroll/scrolltext.png create mode 100644 app/examples/Games/BeastScroll/sprite_arbre.png create mode 100644 app/examples/Games/BeastScroll/sprite_barriere.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages1.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages2.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages3.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages4.png create mode 100644 app/examples/Games/Concent/.directory create mode 100644 app/examples/Games/Concent/.icon.png create mode 100644 app/examples/Games/Concent/.icon/16.png create mode 100644 app/examples/Games/Concent/.icon/32.png create mode 100644 app/examples/Games/Concent/.icon/48.png create mode 100644 app/examples/Games/Concent/.lang/ca.po create mode 100644 app/examples/Games/Concent/.lang/cs.po create mode 100644 app/examples/Games/Concent/.lang/de.po create mode 100644 app/examples/Games/Concent/.lang/en.po create mode 100644 app/examples/Games/Concent/.lang/es.po create mode 100644 app/examples/Games/Concent/.lang/fr.po create mode 100644 app/examples/Games/Concent/.lang/ru.po create mode 100644 app/examples/Games/Concent/.project create mode 100644 app/examples/Games/Concent/.src/fotos.class create mode 100644 app/examples/Games/Concent/.src/fotos.form create mode 100644 app/examples/Games/Concent/.src/frmAcerca.class create mode 100644 app/examples/Games/Concent/.src/frmAcerca.form create mode 100644 app/examples/Games/Concent/.src/frmInstrucciones.class create mode 100644 app/examples/Games/Concent/.src/frmInstrucciones.form create mode 100644 app/examples/Games/Concent/.src/funciones.module create mode 100644 app/examples/Games/Concent/.src/principal.class create mode 100644 app/examples/Games/Concent/.src/principal.form create mode 100644 app/examples/Games/Concent/Blockhit.wav create mode 100644 app/examples/Games/Concent/CHANGELOG create mode 100644 app/examples/Games/Concent/Missed.wav create mode 100644 app/examples/Games/Concent/Newlevel.wav create mode 100644 app/examples/Games/Concent/Paddle.wav create mode 100644 app/examples/Games/Concent/Setup.wav create mode 100644 app/examples/Games/Concent/Wallhit.wav create mode 100644 app/examples/Games/Concent/applause.wav create mode 100644 app/examples/Games/Concent/imagenes/an1.gif create mode 100644 app/examples/Games/Concent/imagenes/an10.gif create mode 100644 app/examples/Games/Concent/imagenes/an11.gif create mode 100644 app/examples/Games/Concent/imagenes/an12.gif create mode 100644 app/examples/Games/Concent/imagenes/an13.gif create mode 100644 app/examples/Games/Concent/imagenes/an14.gif create mode 100644 app/examples/Games/Concent/imagenes/an15.gif create mode 100644 app/examples/Games/Concent/imagenes/an16.gif create mode 100644 app/examples/Games/Concent/imagenes/an17.gif create mode 100644 app/examples/Games/Concent/imagenes/an18.gif create mode 100644 app/examples/Games/Concent/imagenes/an19.gif create mode 100644 app/examples/Games/Concent/imagenes/an2.gif create mode 100644 app/examples/Games/Concent/imagenes/an20.gif create mode 100644 app/examples/Games/Concent/imagenes/an21.gif create mode 100644 app/examples/Games/Concent/imagenes/an22.gif create mode 100644 app/examples/Games/Concent/imagenes/an23.gif create mode 100644 app/examples/Games/Concent/imagenes/an24.gif create mode 100644 app/examples/Games/Concent/imagenes/an25.gif create mode 100644 app/examples/Games/Concent/imagenes/an26.gif create mode 100644 app/examples/Games/Concent/imagenes/an27.gif create mode 100644 app/examples/Games/Concent/imagenes/an28.gif create mode 100644 app/examples/Games/Concent/imagenes/an29.gif create mode 100644 app/examples/Games/Concent/imagenes/an3.gif create mode 100644 app/examples/Games/Concent/imagenes/an30.gif create mode 100644 app/examples/Games/Concent/imagenes/an31.gif create mode 100644 app/examples/Games/Concent/imagenes/an32.gif create mode 100644 app/examples/Games/Concent/imagenes/an33.gif create mode 100644 app/examples/Games/Concent/imagenes/an34.gif create mode 100644 app/examples/Games/Concent/imagenes/an35.gif create mode 100644 app/examples/Games/Concent/imagenes/an36.gif create mode 100644 app/examples/Games/Concent/imagenes/an37.gif create mode 100644 app/examples/Games/Concent/imagenes/an38.gif create mode 100644 app/examples/Games/Concent/imagenes/an39.gif create mode 100644 app/examples/Games/Concent/imagenes/an4.gif create mode 100644 app/examples/Games/Concent/imagenes/an40.gif create mode 100644 app/examples/Games/Concent/imagenes/an5.gif create mode 100644 app/examples/Games/Concent/imagenes/an6.gif create mode 100644 app/examples/Games/Concent/imagenes/an7.gif create mode 100644 app/examples/Games/Concent/imagenes/an8.gif create mode 100644 app/examples/Games/Concent/imagenes/an9.gif create mode 100644 app/examples/Games/Concent/imagenes/colombia.gif create mode 100644 app/examples/Games/Concent/imagenes/inter.gif create mode 100644 app/examples/Games/Concent/imagenes/inter.jpg create mode 100644 app/examples/Games/Concent/imagenes/logo.gif create mode 100644 app/examples/Games/Concent/imagenes/logo.png create mode 100644 app/examples/Games/Concent/imagenes/ok.gif create mode 100644 app/examples/Games/Concent/imagenes/tierra.gif create mode 100644 app/examples/Games/Concent/imagenes/tierra3.jpg create mode 100644 app/examples/Games/Concent/move.wav create mode 100644 app/examples/Games/Concent/shuffle.wav create mode 100644 app/examples/Games/DeepSpace/.directory create mode 100644 app/examples/Games/DeepSpace/.icon.png create mode 100644 app/examples/Games/DeepSpace/.lang/ca.po create mode 100644 app/examples/Games/DeepSpace/.lang/cs.po create mode 100644 app/examples/Games/DeepSpace/.lang/de.po create mode 100644 app/examples/Games/DeepSpace/.lang/es.po create mode 100644 app/examples/Games/DeepSpace/.lang/ru.po create mode 100644 app/examples/Games/DeepSpace/.project create mode 100644 app/examples/Games/DeepSpace/.src/CBullet.class create mode 100644 app/examples/Games/DeepSpace/.src/CObject.class create mode 100644 app/examples/Games/DeepSpace/.src/FAbout.class create mode 100644 app/examples/Games/DeepSpace/.src/FAbout.form create mode 100644 app/examples/Games/DeepSpace/.src/FMain.class create mode 100644 app/examples/Games/DeepSpace/.src/FMain.form create mode 100644 app/examples/Games/DeepSpace/.src/MMain.module create mode 100644 app/examples/Games/DeepSpace/.src/MMath.module create mode 100644 app/examples/Games/DeepSpace/doc/.html_files/eg3.gif create mode 100644 app/examples/Games/DeepSpace/doc/.html_files/eg3b.gif create mode 100644 app/examples/Games/DeepSpace/doc/coordinates.html create mode 100644 app/examples/Games/DeepSpace/doc/howto.txt create mode 100644 app/examples/Games/DeepSpace/doc/todo.txt create mode 100644 app/examples/Games/DeepSpace/images/deepspace.png create mode 100644 app/examples/Games/DeepSpace/object.data/kite.2do create mode 100644 app/examples/Games/DeepSpace/object.data/main.lst create mode 100644 app/examples/Games/DeepSpace/object.data/ship.2do create mode 100644 app/examples/Games/DeepSpace/object.data/triangle.2do create mode 100644 app/examples/Games/DeepSpace/object.data/x-wing.2do create mode 100644 app/examples/Games/GNUBoxWorld/.directory create mode 100644 app/examples/Games/GNUBoxWorld/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Games/GNUBoxWorld/.icon.png create mode 100644 app/examples/Games/GNUBoxWorld/.lang/ca.po create mode 100644 app/examples/Games/GNUBoxWorld/.lang/cs.po create mode 100644 app/examples/Games/GNUBoxWorld/.lang/de.po create mode 100644 app/examples/Games/GNUBoxWorld/.lang/es_AR.po create mode 100644 app/examples/Games/GNUBoxWorld/.lang/ru.po create mode 100644 app/examples/Games/GNUBoxWorld/.project create mode 100644 app/examples/Games/GNUBoxWorld/.src/Cell.class create mode 100644 app/examples/Games/GNUBoxWorld/.src/FMain.class create mode 100644 app/examples/Games/GNUBoxWorld/.src/FMain.form create mode 100644 app/examples/Games/GNUBoxWorld/.src/FrmAbout.class create mode 100644 app/examples/Games/GNUBoxWorld/.src/FrmAbout.form create mode 100644 app/examples/Games/GNUBoxWorld/.src/GameBoard.class create mode 100644 app/examples/Games/GNUBoxWorld/License create mode 100644 app/examples/Games/GNUBoxWorld/abajo.png create mode 100644 app/examples/Games/GNUBoxWorld/arriba.png create mode 100644 app/examples/Games/GNUBoxWorld/derecha.png create mode 100644 app/examples/Games/GNUBoxWorld/destino.png create mode 100644 app/examples/Games/GNUBoxWorld/ganador.png create mode 100644 app/examples/Games/GNUBoxWorld/icon.png create mode 100644 app/examples/Games/GNUBoxWorld/izquierda.png create mode 100644 app/examples/Games/GNUBoxWorld/logo.png create mode 100644 app/examples/Games/GNUBoxWorld/movible.png create mode 100644 app/examples/Games/GNUBoxWorld/movibleendestino.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo-l.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo-lr.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo-r.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo.png create mode 100644 app/examples/Games/GNUBoxWorld/piso.png create mode 100644 app/examples/Games/GameOfLife/.debug create mode 100644 app/examples/Games/GameOfLife/.directory create mode 100644 app/examples/Games/GameOfLife/.icon.png create mode 100644 app/examples/Games/GameOfLife/.lang/ca.po create mode 100644 app/examples/Games/GameOfLife/.lang/cs.po create mode 100644 app/examples/Games/GameOfLife/.lang/de.po create mode 100644 app/examples/Games/GameOfLife/.lang/ru.po create mode 100644 app/examples/Games/GameOfLife/.project create mode 100644 app/examples/Games/GameOfLife/.src/CGameField.class create mode 100644 app/examples/Games/GameOfLife/.src/FMain.class create mode 100644 app/examples/Games/GameOfLife/.src/FMain.form create mode 100644 app/examples/Games/GameOfLife/glob2-icon-48x48.png create mode 100644 app/examples/Games/Invaders/.directory create mode 100644 app/examples/Games/Invaders/.icon.png create mode 100644 app/examples/Games/Invaders/.project create mode 100644 app/examples/Games/Invaders/.src/Enemies.class create mode 100644 app/examples/Games/Invaders/.src/Enemy.class create mode 100644 app/examples/Games/Invaders/.src/MMain.module create mode 100644 app/examples/Games/Invaders/.src/Missile.class create mode 100644 app/examples/Games/Invaders/.src/Missiles.class create mode 100644 app/examples/Games/Invaders/invaders.png create mode 100644 app/examples/Games/MineSweeper/.directory create mode 100644 app/examples/Games/MineSweeper/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Games/MineSweeper/.icon.png create mode 100644 app/examples/Games/MineSweeper/.lang/cs.po create mode 100644 app/examples/Games/MineSweeper/.lang/ja.po create mode 100644 app/examples/Games/MineSweeper/.lang/ru.po create mode 100644 app/examples/Games/MineSweeper/.lang/zh.po create mode 100644 app/examples/Games/MineSweeper/.lang/zh_TW.po create mode 100644 app/examples/Games/MineSweeper/.project create mode 100644 app/examples/Games/MineSweeper/.src/FMain.class create mode 100644 app/examples/Games/MineSweeper/.src/FMain.form create mode 100644 app/examples/Games/MineSweeper/.src/FSettings.class create mode 100644 app/examples/Games/MineSweeper/.src/FSettings.form create mode 100644 app/examples/Games/MineSweeper/.src/MineSweeperGame.class create mode 100644 app/examples/Games/MineSweeper/image/bigflag.png create mode 100644 app/examples/Games/MineSweeper/image/cover.png create mode 100644 app/examples/Games/MineSweeper/image/coveron.png create mode 100644 app/examples/Games/MineSweeper/image/empty.png create mode 100644 app/examples/Games/MineSweeper/image/expr_lose.png create mode 100644 app/examples/Games/MineSweeper/image/expr_normal.png create mode 100644 app/examples/Games/MineSweeper/image/expr_o.png create mode 100644 app/examples/Games/MineSweeper/image/expr_win.png create mode 100644 app/examples/Games/MineSweeper/image/false.png create mode 100644 app/examples/Games/MineSweeper/image/flag.png create mode 100644 app/examples/Games/MineSweeper/image/mine.png create mode 100644 app/examples/Games/MineSweeper/image/number_1.png create mode 100644 app/examples/Games/MineSweeper/image/number_2.png create mode 100644 app/examples/Games/MineSweeper/image/number_3.png create mode 100644 app/examples/Games/MineSweeper/image/number_4.png create mode 100644 app/examples/Games/MineSweeper/image/number_5.png create mode 100644 app/examples/Games/MineSweeper/image/number_6.png create mode 100644 app/examples/Games/MineSweeper/image/number_7.png create mode 100644 app/examples/Games/MineSweeper/image/number_8.png create mode 100644 app/examples/Games/Pong/.directory create mode 100644 app/examples/Games/Pong/.icon.png create mode 100644 app/examples/Games/Pong/.project create mode 100644 app/examples/Games/Pong/.src/Ball.class create mode 100644 app/examples/Games/Pong/.src/MMain.module create mode 100644 app/examples/Games/Pong/.src/NPC.class create mode 100644 app/examples/Games/Pong/.src/Paddle.class create mode 100644 app/examples/Games/Pong/SPEED create mode 100644 app/examples/Games/Pong/pong.png create mode 100644 app/examples/Games/Puzzle1To8/.directory create mode 100644 app/examples/Games/Puzzle1To8/.icon.png create mode 100644 app/examples/Games/Puzzle1To8/.lang/ca.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/cs.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/de.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/es_AR.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/fr.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/ru.po create mode 100644 app/examples/Games/Puzzle1To8/.project create mode 100644 app/examples/Games/Puzzle1To8/.src/Casillero.class create mode 100644 app/examples/Games/Puzzle1To8/.src/Esquema.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FMain.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FMain.form create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAbout.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAbout.form create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAyuda.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAyuda.form create mode 100644 app/examples/Games/Puzzle1To8/Licence create mode 100644 app/examples/Games/Puzzle1To8/ejemplo1.png create mode 100644 app/examples/Games/Puzzle1To8/ejemplo2.png create mode 100644 app/examples/Games/Puzzle1To8/logo.png create mode 100644 app/examples/Games/RobotFindsKitten/.directory create mode 100644 app/examples/Games/RobotFindsKitten/.icon.png create mode 100644 app/examples/Games/RobotFindsKitten/.lang/ca.po create mode 100644 app/examples/Games/RobotFindsKitten/.lang/cs.po create mode 100644 app/examples/Games/RobotFindsKitten/.lang/de.po create mode 100644 app/examples/Games/RobotFindsKitten/.lang/es.po create mode 100644 app/examples/Games/RobotFindsKitten/.lang/ru.po create mode 100644 app/examples/Games/RobotFindsKitten/.project create mode 100644 app/examples/Games/RobotFindsKitten/.src/Frfk.class create mode 100644 app/examples/Games/RobotFindsKitten/.src/Frfk.form create mode 100644 app/examples/Games/RobotFindsKitten/COPYING create mode 100644 app/examples/Games/RobotFindsKitten/heart.png create mode 100644 app/examples/Games/RobotFindsKitten/nkis.txt create mode 100644 app/examples/Games/RobotFindsKitten/nkis_ru.txt create mode 100644 app/examples/Games/RobotFindsKitten/readme.txt create mode 100644 app/examples/Games/Snake/.directory create mode 100644 app/examples/Games/Snake/.icon.png create mode 100644 app/examples/Games/Snake/.lang/ca.po create mode 100644 app/examples/Games/Snake/.lang/cs.po create mode 100644 app/examples/Games/Snake/.lang/de.po create mode 100644 app/examples/Games/Snake/.lang/ru.po create mode 100644 app/examples/Games/Snake/.project create mode 100644 app/examples/Games/Snake/.src/FrmMain.class create mode 100644 app/examples/Games/Snake/.src/FrmMain.form create mode 100644 app/examples/Games/Snake/apple.png create mode 100644 app/examples/Games/Snake/body.png create mode 100644 app/examples/Games/Snake/dead.wav create mode 100644 app/examples/Games/Snake/eat.wav create mode 100644 app/examples/Games/Snake/head.png create mode 100644 app/examples/Games/Snake/start.wav create mode 100644 app/examples/Games/Solitaire/.directory create mode 100644 app/examples/Games/Solitaire/.icon.png create mode 100644 app/examples/Games/Solitaire/.lang/ca.po create mode 100644 app/examples/Games/Solitaire/.lang/cs.po create mode 100644 app/examples/Games/Solitaire/.lang/de.po create mode 100644 app/examples/Games/Solitaire/.lang/es.po create mode 100644 app/examples/Games/Solitaire/.lang/ru.po create mode 100644 app/examples/Games/Solitaire/.project create mode 100644 app/examples/Games/Solitaire/.src/CBoardDesign.class create mode 100644 app/examples/Games/Solitaire/.src/CMove.class create mode 100644 app/examples/Games/Solitaire/.src/FBoardSelect.class create mode 100644 app/examples/Games/Solitaire/.src/FBoardSelect.form create mode 100644 app/examples/Games/Solitaire/.src/FGameArea.class create mode 100644 app/examples/Games/Solitaire/.src/FGameArea.form create mode 100644 app/examples/Games/Solitaire/.src/Global.class create mode 100644 app/examples/Games/Solitaire/.src/MBoards.module create mode 100644 app/examples/Games/Solitaire/ball.png create mode 100644 app/examples/Games/Solitaire/new.png create mode 100644 app/examples/Games/Solitaire/quit.png create mode 100644 app/examples/Games/Solitaire/redo.png create mode 100644 app/examples/Games/Solitaire/undo.png create mode 100644 app/examples/Games/StarField/.directory create mode 100644 app/examples/Games/StarField/.icon.png create mode 100644 app/examples/Games/StarField/.project create mode 100644 app/examples/Games/StarField/.src/MMain.module create mode 100644 app/examples/Games/StarField/enterprise.png create mode 100644 app/examples/Games/StarField/logo.png create mode 100644 app/examples/Image/ImageViewer/.directory create mode 100644 app/examples/Image/ImageViewer/.icon.png create mode 100644 app/examples/Image/ImageViewer/.lang/ca.po create mode 100644 app/examples/Image/ImageViewer/.lang/cs.po create mode 100644 app/examples/Image/ImageViewer/.lang/de.po create mode 100644 app/examples/Image/ImageViewer/.lang/es.po create mode 100644 app/examples/Image/ImageViewer/.lang/nl.po create mode 100644 app/examples/Image/ImageViewer/.lang/ru.po create mode 100644 app/examples/Image/ImageViewer/.project create mode 100644 app/examples/Image/ImageViewer/.src/FViewer.class create mode 100644 app/examples/Image/ImageViewer/.src/FViewer.form create mode 100644 app/examples/Image/ImageViewer/image.png create mode 100644 app/examples/Image/ImageViewer/test.png create mode 100644 app/examples/Image/Lighttable/.directory create mode 100644 app/examples/Image/Lighttable/.icon.png create mode 100644 app/examples/Image/Lighttable/.lang/ca.po create mode 100644 app/examples/Image/Lighttable/.lang/cs.po create mode 100644 app/examples/Image/Lighttable/.lang/de.po create mode 100644 app/examples/Image/Lighttable/.lang/en.po create mode 100644 app/examples/Image/Lighttable/.lang/nl.po create mode 100644 app/examples/Image/Lighttable/.lang/ru.po create mode 100644 app/examples/Image/Lighttable/.project create mode 100644 app/examples/Image/Lighttable/.src/FHelp.class create mode 100644 app/examples/Image/Lighttable/.src/FHelp.form create mode 100644 app/examples/Image/Lighttable/.src/FInfo.class create mode 100644 app/examples/Image/Lighttable/.src/FInfo.form create mode 100644 app/examples/Image/Lighttable/.src/FMain.class create mode 100644 app/examples/Image/Lighttable/.src/FMain.form create mode 100644 app/examples/Image/Lighttable/.src/FRename.class create mode 100644 app/examples/Image/Lighttable/.src/FRename.form create mode 100644 app/examples/Image/Lighttable/.src/FRenameAll.class create mode 100644 app/examples/Image/Lighttable/.src/FRenameAll.form create mode 100644 app/examples/Image/Lighttable/.src/FRenameAllWarning.class create mode 100644 app/examples/Image/Lighttable/.src/FRenameAllWarning.form create mode 100644 app/examples/Image/Lighttable/.src/FSlideshow.class create mode 100644 app/examples/Image/Lighttable/.src/FSlideshow.form create mode 100644 app/examples/Image/Lighttable/.src/FStart.class create mode 100644 app/examples/Image/Lighttable/.src/FStart.form create mode 100644 app/examples/Image/Lighttable/.src/FTime.class create mode 100644 app/examples/Image/Lighttable/.src/FTime.form create mode 100644 app/examples/Image/Lighttable/.src/MMain.module create mode 100644 app/examples/Image/Lighttable/CHANGELOG create mode 100644 app/examples/Image/Lighttable/FStart.class create mode 100644 app/examples/Image/Lighttable/FStart.form create mode 100644 app/examples/Image/Lighttable/Help_ca.html create mode 100644 app/examples/Image/Lighttable/Help_de.html create mode 100644 app/examples/Image/Lighttable/Help_en.html create mode 100644 app/examples/Image/Lighttable/Help_ru.html create mode 100644 app/examples/Image/Lighttable/LTicon.png create mode 100644 app/examples/Image/Lighttable/Liesmich.txt create mode 100644 app/examples/Image/Lighttable/Readme.txt create mode 100644 app/examples/Image/Lighttable/close.png create mode 100644 app/examples/Image/Lighttable/hand1.png create mode 100644 app/examples/Image/Lighttable/help-contents.png create mode 100644 app/examples/Image/Lighttable/lighttable.png create mode 100644 app/examples/Image/Lighttable/move.png create mode 100644 app/examples/Image/Lighttable/zoom-in.png create mode 100644 app/examples/Image/PhotoTouch/.directory create mode 100644 app/examples/Image/PhotoTouch/.hidden/screenshots/phototouch.jpg create mode 100644 app/examples/Image/PhotoTouch/.icon.png create mode 100644 app/examples/Image/PhotoTouch/.lang/fr.po create mode 100644 app/examples/Image/PhotoTouch/.lang/nl.po create mode 100644 app/examples/Image/PhotoTouch/.lang/ru.po create mode 100644 app/examples/Image/PhotoTouch/.project create mode 100644 app/examples/Image/PhotoTouch/.src/CAnimation.class create mode 100644 app/examples/Image/PhotoTouch/.src/CButton.class create mode 100644 app/examples/Image/PhotoTouch/.src/FBrightness.class create mode 100644 app/examples/Image/PhotoTouch/.src/FBrightness.form create mode 100644 app/examples/Image/PhotoTouch/.src/FMain.class create mode 100644 app/examples/Image/PhotoTouch/.src/FMain.form create mode 100644 app/examples/Image/PhotoTouch/.src/FResize.class create mode 100644 app/examples/Image/PhotoTouch/.src/FResize.form create mode 100644 app/examples/Image/PhotoTouch/.src/FScissors.class create mode 100644 app/examples/Image/PhotoTouch/.src/FScissors.form create mode 100644 app/examples/Image/PhotoTouch/balance.png create mode 100644 app/examples/Image/PhotoTouch/blur.png create mode 100644 app/examples/Image/PhotoTouch/brightness.png create mode 100644 app/examples/Image/PhotoTouch/contrast.png create mode 100644 app/examples/Image/PhotoTouch/delete.png create mode 100644 app/examples/Image/PhotoTouch/despeckle.png create mode 100644 app/examples/Image/PhotoTouch/film.png create mode 100644 app/examples/Image/PhotoTouch/gamma.png create mode 100644 app/examples/Image/PhotoTouch/hflip.png create mode 100644 app/examples/Image/PhotoTouch/icon.png create mode 100644 app/examples/Image/PhotoTouch/invert.png create mode 100644 app/examples/Image/PhotoTouch/magic.png create mode 100644 app/examples/Image/PhotoTouch/next.png create mode 100644 app/examples/Image/PhotoTouch/normalize.png create mode 100644 app/examples/Image/PhotoTouch/oil.png create mode 100644 app/examples/Image/PhotoTouch/ok.png create mode 100644 app/examples/Image/PhotoTouch/photo.png create mode 100644 app/examples/Image/PhotoTouch/previous.png create mode 100644 app/examples/Image/PhotoTouch/quit.png create mode 100644 app/examples/Image/PhotoTouch/redo.png create mode 100644 app/examples/Image/PhotoTouch/resize.png create mode 100644 app/examples/Image/PhotoTouch/rotate-left.png create mode 100644 app/examples/Image/PhotoTouch/rotate-right.png create mode 100644 app/examples/Image/PhotoTouch/save-all.png create mode 100644 app/examples/Image/PhotoTouch/save.png create mode 100644 app/examples/Image/PhotoTouch/scissors.png create mode 100644 app/examples/Image/PhotoTouch/sharpen.png create mode 100644 app/examples/Image/PhotoTouch/undo.png create mode 100644 app/examples/Image/PhotoTouch/usb.png create mode 100644 app/examples/Image/PhotoTouch/vflip.png create mode 100644 app/examples/Image/PhotoTouch/zoom-fit.png create mode 100644 app/examples/Image/PhotoTouch/zoom-in.png create mode 100644 app/examples/Image/PhotoTouch/zoom-original.png create mode 100644 app/examples/Image/PhotoTouch/zoom-out.png create mode 100644 app/examples/Misc/Console/.directory create mode 100644 app/examples/Misc/Console/.icon.png create mode 100644 app/examples/Misc/Console/.lang/fr.po create mode 100644 app/examples/Misc/Console/.lang/ru.po create mode 100644 app/examples/Misc/Console/.project create mode 100644 app/examples/Misc/Console/.src/FConsole.class create mode 100644 app/examples/Misc/Console/.src/FConsole.form create mode 100644 app/examples/Misc/Console/terminal.png create mode 100644 app/examples/Misc/DBusExplorer/.directory create mode 100644 app/examples/Misc/DBusExplorer/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Misc/DBusExplorer/.icon.png create mode 100644 app/examples/Misc/DBusExplorer/.lang/ru.po create mode 100644 app/examples/Misc/DBusExplorer/.project create mode 100644 app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class create mode 100644 app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form create mode 100644 app/examples/Misc/DBusExplorer/dbus22.png create mode 100644 app/examples/Misc/DBusExplorer/dbus64.png create mode 100644 app/examples/Misc/DBusExplorer/method.png create mode 100644 app/examples/Misc/DBusExplorer/property.png create mode 100644 app/examples/Misc/DBusExplorer/signal.png create mode 100644 app/examples/Misc/Evaluator/.directory create mode 100644 app/examples/Misc/Evaluator/.icon.png create mode 100644 app/examples/Misc/Evaluator/.lang/ca.po create mode 100644 app/examples/Misc/Evaluator/.lang/cs.po create mode 100644 app/examples/Misc/Evaluator/.lang/de.po create mode 100644 app/examples/Misc/Evaluator/.lang/es.po create mode 100644 app/examples/Misc/Evaluator/.lang/ru.po create mode 100644 app/examples/Misc/Evaluator/.project create mode 100644 app/examples/Misc/Evaluator/.src/FEval.class create mode 100644 app/examples/Misc/Evaluator/.src/FEval.form create mode 100644 app/examples/Misc/Evaluator/calculator.png create mode 100644 app/examples/Misc/Explorer/.directory create mode 100644 app/examples/Misc/Explorer/.icon.png create mode 100644 app/examples/Misc/Explorer/.lang/ca.po create mode 100644 app/examples/Misc/Explorer/.lang/cs.po create mode 100644 app/examples/Misc/Explorer/.lang/de.po create mode 100644 app/examples/Misc/Explorer/.lang/es.po create mode 100644 app/examples/Misc/Explorer/.lang/ru.po create mode 100644 app/examples/Misc/Explorer/.project create mode 100644 app/examples/Misc/Explorer/.src/FExplorer.class create mode 100644 app/examples/Misc/Explorer/.src/FExplorer.form create mode 100644 app/examples/Misc/Explorer/folder.png create mode 100644 app/examples/Misc/Notepad/.directory create mode 100644 app/examples/Misc/Notepad/.icon.png create mode 100644 app/examples/Misc/Notepad/.lang/ca.po create mode 100644 app/examples/Misc/Notepad/.lang/cs.po create mode 100644 app/examples/Misc/Notepad/.lang/de.po create mode 100644 app/examples/Misc/Notepad/.lang/es.po create mode 100644 app/examples/Misc/Notepad/.lang/ru.po create mode 100644 app/examples/Misc/Notepad/.project create mode 100644 app/examples/Misc/Notepad/.src/FAbout.class create mode 100644 app/examples/Misc/Notepad/.src/FAbout.form create mode 100644 app/examples/Misc/Notepad/.src/FNotepad.class create mode 100644 app/examples/Misc/Notepad/.src/FNotepad.form create mode 100644 app/examples/Misc/Notepad/notepad.png create mode 100644 app/examples/Misc/PDFViewer/.directory create mode 100644 app/examples/Misc/PDFViewer/.icon.png create mode 100644 app/examples/Misc/PDFViewer/.lang/ca.po create mode 100644 app/examples/Misc/PDFViewer/.lang/cs.po create mode 100644 app/examples/Misc/PDFViewer/.lang/de.po create mode 100644 app/examples/Misc/PDFViewer/.lang/es.po create mode 100644 app/examples/Misc/PDFViewer/.lang/ru.po create mode 100644 app/examples/Misc/PDFViewer/.project create mode 100644 app/examples/Misc/PDFViewer/.src/FMain.class create mode 100644 app/examples/Misc/PDFViewer/.src/FMain.form create mode 100644 app/examples/Misc/PDFViewer/.src/Fabout.class create mode 100644 app/examples/Misc/PDFViewer/.src/Fabout.form create mode 100644 app/examples/Misc/PDFViewer/pdf.png create mode 100644 app/examples/Misc/SystemTray/.directory create mode 100644 app/examples/Misc/SystemTray/.icon.png create mode 100644 app/examples/Misc/SystemTray/.lang/ru.po create mode 100644 app/examples/Misc/SystemTray/.project create mode 100644 app/examples/Misc/SystemTray/.src/FMain.class create mode 100644 app/examples/Misc/SystemTray/.src/FMain.form create mode 100644 app/examples/Misc/SystemTray/bg.png create mode 100644 app/examples/Misc/SystemTray/icon.png create mode 100644 app/examples/Misc/WatchGambasDirectory/.directory create mode 100644 app/examples/Misc/WatchGambasDirectory/.icon.png create mode 100644 app/examples/Misc/WatchGambasDirectory/.lang/ru.po create mode 100644 app/examples/Misc/WatchGambasDirectory/.project create mode 100644 app/examples/Misc/WatchGambasDirectory/.src/MMain.module create mode 100644 app/examples/Misc/WatchGambasDirectory/watch.svg create mode 100644 app/examples/Multimedia/CDPlayer/.directory create mode 100644 app/examples/Multimedia/CDPlayer/.icon.png create mode 100644 app/examples/Multimedia/CDPlayer/.lang/ca.po create mode 100644 app/examples/Multimedia/CDPlayer/.lang/cs.po create mode 100644 app/examples/Multimedia/CDPlayer/.lang/es.po create mode 100644 app/examples/Multimedia/CDPlayer/.lang/ru.po create mode 100644 app/examples/Multimedia/CDPlayer/.project create mode 100644 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class create mode 100644 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form create mode 100644 app/examples/Multimedia/CDPlayer/cdrom.png create mode 100644 app/examples/Multimedia/MediaPlayer/.directory create mode 100644 app/examples/Multimedia/MediaPlayer/.hidden/screenshots/MediaPlayer.jpg create mode 100644 app/examples/Multimedia/MediaPlayer/.icon.png create mode 100644 app/examples/Multimedia/MediaPlayer/.lang/fr.po create mode 100644 app/examples/Multimedia/MediaPlayer/.lang/ru.po create mode 100644 app/examples/Multimedia/MediaPlayer/.project create mode 100644 app/examples/Multimedia/MediaPlayer/.src/CAnimation.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/CButton.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FControl.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FControl.form create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FMain.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FMain.form create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FTags.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FTags.form create mode 100644 app/examples/Multimedia/MediaPlayer/.src/MTest.module create mode 100644 app/examples/Multimedia/MediaPlayer/brightness.png create mode 100644 app/examples/Multimedia/MediaPlayer/config.png create mode 100644 app/examples/Multimedia/MediaPlayer/contrast.png create mode 100644 app/examples/Multimedia/MediaPlayer/eject.png create mode 100644 app/examples/Multimedia/MediaPlayer/film.png create mode 100644 app/examples/Multimedia/MediaPlayer/fullscreen.png create mode 100644 app/examples/Multimedia/MediaPlayer/gamma.png create mode 100644 app/examples/Multimedia/MediaPlayer/icon.png create mode 100644 app/examples/Multimedia/MediaPlayer/info.png create mode 100644 app/examples/Multimedia/MediaPlayer/mute.png create mode 100644 app/examples/Multimedia/MediaPlayer/pause.png create mode 100644 app/examples/Multimedia/MediaPlayer/play.png create mode 100644 app/examples/Multimedia/MediaPlayer/quit.png create mode 100644 app/examples/Multimedia/MediaPlayer/screenshot.png create mode 100644 app/examples/Multimedia/MediaPlayer/seek-backward.png create mode 100644 app/examples/Multimedia/MediaPlayer/seek-forward.png create mode 100644 app/examples/Multimedia/MediaPlayer/skip-backward.png create mode 100644 app/examples/Multimedia/MediaPlayer/skip-forward.png create mode 100644 app/examples/Multimedia/MediaPlayer/stop.png create mode 100644 app/examples/Multimedia/MediaPlayer/subtitle.png create mode 100644 app/examples/Multimedia/MediaPlayer/undo.png create mode 100644 app/examples/Multimedia/MediaPlayer/video.png create mode 100644 app/examples/Multimedia/MediaPlayer/visualisation.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-0.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-1.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-2.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-3.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume.png create mode 100644 app/examples/Multimedia/MediaPlayer/zoom-in.png create mode 100644 app/examples/Multimedia/MoviePlayer/.directory create mode 100644 app/examples/Multimedia/MoviePlayer/.icon.png create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/ca.po create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/cs.po create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/es.po create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/ru.po create mode 100644 app/examples/Multimedia/MoviePlayer/.project create mode 100644 app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class create mode 100644 app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form create mode 100644 app/examples/Multimedia/MoviePlayer/video.png create mode 100644 app/examples/Multimedia/MusicPlayer/.directory create mode 100644 app/examples/Multimedia/MusicPlayer/.icon.png create mode 100644 app/examples/Multimedia/MusicPlayer/.icon/16.png create mode 100644 app/examples/Multimedia/MusicPlayer/.icon/32.png create mode 100644 app/examples/Multimedia/MusicPlayer/.icon/48.png create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/ca.po create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/cs.po create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/es.po create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/fr.po create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/ru.po create mode 100644 app/examples/Multimedia/MusicPlayer/.project create mode 100644 app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class create mode 100644 app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form create mode 100644 app/examples/Multimedia/MusicPlayer/sound.png create mode 100644 app/examples/Multimedia/MyWebCam/.directory create mode 100644 app/examples/Multimedia/MyWebCam/.icon.png create mode 100644 app/examples/Multimedia/MyWebCam/.lang/ca.po create mode 100644 app/examples/Multimedia/MyWebCam/.lang/cs.po create mode 100644 app/examples/Multimedia/MyWebCam/.lang/es.po create mode 100644 app/examples/Multimedia/MyWebCam/.lang/ru.po create mode 100644 app/examples/Multimedia/MyWebCam/.project create mode 100644 app/examples/Multimedia/MyWebCam/.src/Form1.class create mode 100644 app/examples/Multimedia/MyWebCam/.src/Form1.form create mode 100644 app/examples/Multimedia/MyWebCam/camera.png create mode 100644 app/examples/Multimedia/WaveGenerator/.directory create mode 100644 app/examples/Multimedia/WaveGenerator/.icon.png create mode 100644 app/examples/Multimedia/WaveGenerator/.lang/ru.po create mode 100644 app/examples/Multimedia/WaveGenerator/.project create mode 100644 app/examples/Multimedia/WaveGenerator/.src/FMain.class create mode 100644 app/examples/Multimedia/WaveGenerator/.src/FMain.form create mode 100644 app/examples/Multimedia/WaveGenerator/audio-headphones.png create mode 100644 app/examples/Multimedia/WebCam/.directory create mode 100644 app/examples/Multimedia/WebCam/.icon.png create mode 100644 app/examples/Multimedia/WebCam/.lang/ru.po create mode 100644 app/examples/Multimedia/WebCam/.project create mode 100644 app/examples/Multimedia/WebCam/.src/FDevice.class create mode 100644 app/examples/Multimedia/WebCam/.src/FDevice.form create mode 100644 app/examples/Multimedia/WebCam/.src/FMain.class create mode 100644 app/examples/Multimedia/WebCam/.src/FMain.form create mode 100644 app/examples/Multimedia/WebCam/camera.png create mode 100644 app/examples/Multimedia/WebCam/settings.png create mode 100644 app/examples/Networking/ClientSocket/.directory create mode 100644 app/examples/Networking/ClientSocket/.icon.png create mode 100644 app/examples/Networking/ClientSocket/.lang/ca.po create mode 100644 app/examples/Networking/ClientSocket/.lang/cs.po create mode 100644 app/examples/Networking/ClientSocket/.lang/de.po create mode 100644 app/examples/Networking/ClientSocket/.lang/es.po create mode 100644 app/examples/Networking/ClientSocket/.lang/nl.po create mode 100644 app/examples/Networking/ClientSocket/.lang/ru.po create mode 100644 app/examples/Networking/ClientSocket/.project create mode 100644 app/examples/Networking/ClientSocket/.src/FrmMain.class create mode 100644 app/examples/Networking/ClientSocket/.src/FrmMain.form create mode 100644 app/examples/Networking/ClientSocket/socket.png create mode 100644 app/examples/Networking/DnsClient/.directory create mode 100644 app/examples/Networking/DnsClient/.icon.png create mode 100644 app/examples/Networking/DnsClient/.lang/ca.po create mode 100644 app/examples/Networking/DnsClient/.lang/cs.po create mode 100644 app/examples/Networking/DnsClient/.lang/de.po create mode 100644 app/examples/Networking/DnsClient/.lang/es.po create mode 100644 app/examples/Networking/DnsClient/.lang/nl.po create mode 100644 app/examples/Networking/DnsClient/.lang/ru.po create mode 100644 app/examples/Networking/DnsClient/.project create mode 100644 app/examples/Networking/DnsClient/.src/FMain.class create mode 100644 app/examples/Networking/DnsClient/.src/FMain.form create mode 100644 app/examples/Networking/DnsClient/dnsclient.png create mode 100644 app/examples/Networking/HTTPGet/.directory create mode 100644 app/examples/Networking/HTTPGet/.icon.png create mode 100644 app/examples/Networking/HTTPGet/.lang/ca.po create mode 100644 app/examples/Networking/HTTPGet/.lang/cs.po create mode 100644 app/examples/Networking/HTTPGet/.lang/de.po create mode 100644 app/examples/Networking/HTTPGet/.lang/en_GB.po create mode 100644 app/examples/Networking/HTTPGet/.lang/es.po create mode 100644 app/examples/Networking/HTTPGet/.lang/nl.po create mode 100644 app/examples/Networking/HTTPGet/.lang/ru.po create mode 100644 app/examples/Networking/HTTPGet/.project create mode 100644 app/examples/Networking/HTTPGet/.src/ClsParams.class create mode 100644 app/examples/Networking/HTTPGet/.src/FConfig.class create mode 100644 app/examples/Networking/HTTPGet/.src/FConfig.form create mode 100644 app/examples/Networking/HTTPGet/.src/FHttpGet.class create mode 100644 app/examples/Networking/HTTPGet/.src/FHttpGet.form create mode 100644 app/examples/Networking/HTTPGet/httpclient.png create mode 100644 app/examples/Networking/HTTPPost/.directory create mode 100644 app/examples/Networking/HTTPPost/.icon.png create mode 100644 app/examples/Networking/HTTPPost/.lang/ca.po create mode 100644 app/examples/Networking/HTTPPost/.lang/cs.po create mode 100644 app/examples/Networking/HTTPPost/.lang/de.po create mode 100644 app/examples/Networking/HTTPPost/.lang/es.po create mode 100644 app/examples/Networking/HTTPPost/.lang/nl.po create mode 100644 app/examples/Networking/HTTPPost/.lang/ru.po create mode 100644 app/examples/Networking/HTTPPost/.project create mode 100644 app/examples/Networking/HTTPPost/.src/FHttpPost.class create mode 100644 app/examples/Networking/HTTPPost/.src/FHttpPost.form create mode 100644 app/examples/Networking/HTTPPost/httpclient.png create mode 100644 app/examples/Networking/POPMailbox/.directory create mode 100644 app/examples/Networking/POPMailbox/.icon.png create mode 100644 app/examples/Networking/POPMailbox/.lang/ru.po create mode 100644 app/examples/Networking/POPMailbox/.project create mode 100644 app/examples/Networking/POPMailbox/.src/FMain.class create mode 100644 app/examples/Networking/POPMailbox/.src/FMain.form create mode 100644 app/examples/Networking/POPMailbox/.src/FSettings.class create mode 100644 app/examples/Networking/POPMailbox/.src/FSettings.form create mode 100644 app/examples/Networking/POPMailbox/pop3client.png create mode 100644 app/examples/Networking/SerialPort/.directory create mode 100644 app/examples/Networking/SerialPort/.icon.png create mode 100644 app/examples/Networking/SerialPort/.lang/ru.po create mode 100644 app/examples/Networking/SerialPort/.project create mode 100644 app/examples/Networking/SerialPort/.src/FAbout.class create mode 100644 app/examples/Networking/SerialPort/.src/FAbout.form create mode 100644 app/examples/Networking/SerialPort/.src/FMain.class create mode 100644 app/examples/Networking/SerialPort/.src/FMain.form create mode 100644 app/examples/Networking/SerialPort/.src/Module_Config.module create mode 100644 app/examples/Networking/SerialPort/.src/Module_RS232.module create mode 100644 app/examples/Networking/SerialPort/serialport.png create mode 100644 app/examples/Networking/ServerSocket/.directory create mode 100644 app/examples/Networking/ServerSocket/.icon.png create mode 100644 app/examples/Networking/ServerSocket/.lang/ca.po create mode 100644 app/examples/Networking/ServerSocket/.lang/cs.po create mode 100644 app/examples/Networking/ServerSocket/.lang/es.po create mode 100644 app/examples/Networking/ServerSocket/.lang/nl.po create mode 100644 app/examples/Networking/ServerSocket/.lang/ru.po create mode 100644 app/examples/Networking/ServerSocket/.project create mode 100644 app/examples/Networking/ServerSocket/.src/FrmMain.class create mode 100644 app/examples/Networking/ServerSocket/.src/FrmMain.form create mode 100644 app/examples/Networking/ServerSocket/serversocket.png create mode 100644 app/examples/Networking/UDPServerClient/.directory create mode 100644 app/examples/Networking/UDPServerClient/.icon.png create mode 100644 app/examples/Networking/UDPServerClient/.lang/ca.po create mode 100644 app/examples/Networking/UDPServerClient/.lang/cs.po create mode 100644 app/examples/Networking/UDPServerClient/.lang/es.po create mode 100644 app/examples/Networking/UDPServerClient/.lang/nl.po create mode 100644 app/examples/Networking/UDPServerClient/.lang/ru.po create mode 100644 app/examples/Networking/UDPServerClient/.project create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmClient.class create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmClient.form create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmServer.class create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmServer.form create mode 100644 app/examples/Networking/UDPServerClient/udpsocket.png create mode 100644 app/examples/Networking/WebBrowser/.directory create mode 100644 app/examples/Networking/WebBrowser/.icon.png create mode 100644 app/examples/Networking/WebBrowser/.lang/ca.po create mode 100644 app/examples/Networking/WebBrowser/.lang/cs.po create mode 100644 app/examples/Networking/WebBrowser/.lang/de.po create mode 100644 app/examples/Networking/WebBrowser/.lang/es.po create mode 100644 app/examples/Networking/WebBrowser/.lang/nl.po create mode 100644 app/examples/Networking/WebBrowser/.lang/ru.po create mode 100644 app/examples/Networking/WebBrowser/.project create mode 100644 app/examples/Networking/WebBrowser/.src/FAuth.class create mode 100644 app/examples/Networking/WebBrowser/.src/FAuth.form create mode 100644 app/examples/Networking/WebBrowser/.src/FBrowser.class create mode 100644 app/examples/Networking/WebBrowser/.src/FBrowser.form create mode 100644 app/examples/Networking/WebBrowser/.src/FDownload.class create mode 100644 app/examples/Networking/WebBrowser/.src/FDownload.form create mode 100644 app/examples/Networking/WebBrowser/.src/FDownloadList.class create mode 100644 app/examples/Networking/WebBrowser/.src/FDownloadList.form create mode 100644 app/examples/Networking/WebBrowser/.src/FEditable.class create mode 100644 app/examples/Networking/WebBrowser/.src/FEditable.form create mode 100644 app/examples/Networking/WebBrowser/.src/FOption.class create mode 100644 app/examples/Networking/WebBrowser/.src/FOption.form create mode 100644 app/examples/Networking/WebBrowser/icon.png create mode 100644 app/examples/Networking/WebBrowser/list-ordered.png create mode 100644 app/examples/Networking/WebBrowser/list-unordered.png create mode 100644 app/examples/OpenGL/3DWebCam/.directory create mode 100644 app/examples/OpenGL/3DWebCam/.icon.png create mode 100644 app/examples/OpenGL/3DWebCam/.lang/ru.po create mode 100644 app/examples/OpenGL/3DWebCam/.project create mode 100644 app/examples/OpenGL/3DWebCam/.src/Mmain.module create mode 100644 app/examples/OpenGL/3DWebCam/webcam.png create mode 100644 app/examples/OpenGL/GambasGears/.directory create mode 100644 app/examples/OpenGL/GambasGears/.icon.png create mode 100644 app/examples/OpenGL/GambasGears/.lang/ru.po create mode 100644 app/examples/OpenGL/GambasGears/.project create mode 100644 app/examples/OpenGL/GambasGears/.src/Module1.module create mode 100644 app/examples/OpenGL/GambasGears/gears.png create mode 100644 app/examples/OpenGL/Md2Model/.directory create mode 100644 app/examples/OpenGL/Md2Model/.icon.png create mode 100644 app/examples/OpenGL/Md2Model/.lang/ru.po create mode 100644 app/examples/OpenGL/Md2Model/.project create mode 100644 app/examples/OpenGL/Md2Model/.src/FMain.class create mode 100644 app/examples/OpenGL/Md2Model/.src/FMain.form create mode 100644 app/examples/OpenGL/Md2Model/Weapon.md2 create mode 100644 app/examples/OpenGL/Md2Model/Weapon.png create mode 100644 app/examples/OpenGL/Md2Model/bauul.jpg create mode 100644 app/examples/OpenGL/Md2Model/bauul.md2 create mode 100644 app/examples/OpenGL/Md2Model/goblin.jpg create mode 100644 app/examples/OpenGL/Md2Model/goblin.md2 create mode 100644 app/examples/OpenGL/Md2Model/icon.png create mode 100644 app/examples/OpenGL/Md2Model/igdosh.png create mode 100644 app/examples/OpenGL/Md2Model/knight.jpg create mode 100644 app/examples/OpenGL/Md2Model/knight.md2 create mode 100644 app/examples/OpenGL/Md2Model/ogro.jpg create mode 100644 app/examples/OpenGL/Md2Model/ogro.md2 create mode 100644 app/examples/OpenGL/Md2Model/rat.jpg create mode 100644 app/examples/OpenGL/Md2Model/rat.md2 create mode 100644 app/examples/OpenGL/Md2Model/rhino.jpg create mode 100644 app/examples/OpenGL/Md2Model/rhino.md2 create mode 100644 app/examples/OpenGL/NeHeTutorial/.directory create mode 100644 app/examples/OpenGL/NeHeTutorial/.icon.png create mode 100644 app/examples/OpenGL/NeHeTutorial/.lang/ru.po create mode 100644 app/examples/OpenGL/NeHeTutorial/.project create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example1.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example10.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example11.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example16.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example19.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example2.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example25.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example3.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example4.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example42.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example5.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example6.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example7.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example8.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example9.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/MMain.module create mode 100644 app/examples/OpenGL/NeHeTutorial/NeHe.png create mode 100644 app/examples/OpenGL/NeHeTutorial/Sphere.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/Star.png create mode 100644 app/examples/OpenGL/NeHeTutorial/Torus.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/Tube.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/barrel.png create mode 100644 app/examples/OpenGL/NeHeTutorial/ceiling.png create mode 100644 app/examples/OpenGL/NeHeTutorial/crate.jpeg create mode 100644 app/examples/OpenGL/NeHeTutorial/floor.png create mode 100644 app/examples/OpenGL/NeHeTutorial/glass.png create mode 100644 app/examples/OpenGL/NeHeTutorial/icon.png create mode 100644 app/examples/OpenGL/NeHeTutorial/rainbow.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/wall.jpeg create mode 100644 app/examples/OpenGL/NeHeTutorial/world.txt create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.directory create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.icon.png create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.lang/ru.po create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.project create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.src/FMain.class create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.src/FMain.form create mode 100644 app/examples/OpenGL/NeHeTutorialShell/icon.png create mode 100644 app/examples/OpenGL/NeHeTutorialShell/nehe.png create mode 100644 app/examples/OpenGL/PDFPresentation/.directory create mode 100644 app/examples/OpenGL/PDFPresentation/.icon.png create mode 100644 app/examples/OpenGL/PDFPresentation/.lang/ru.po create mode 100644 app/examples/OpenGL/PDFPresentation/.project create mode 100644 app/examples/OpenGL/PDFPresentation/.src/CLogo.class create mode 100644 app/examples/OpenGL/PDFPresentation/.src/CPdfPresentation.class create mode 100644 app/examples/OpenGL/PDFPresentation/.src/FMain.class create mode 100644 app/examples/OpenGL/PDFPresentation/.src/FMain.form create mode 100644 app/examples/OpenGL/PDFPresentation/.src/MMain.module create mode 100644 app/examples/OpenGL/PDFPresentation/icon.png create mode 100644 app/examples/OpenGL/PDFPresentation/logo.png create mode 100644 app/examples/OpenGL/PDFPresentation/music.xm create mode 100644 app/examples/OpenGL/TunnelSDL/.dir_icon.png create mode 100644 app/examples/OpenGL/TunnelSDL/.directory create mode 100644 app/examples/OpenGL/TunnelSDL/.icon.png create mode 100644 app/examples/OpenGL/TunnelSDL/.lang/ru.po create mode 100644 app/examples/OpenGL/TunnelSDL/.project create mode 100644 app/examples/OpenGL/TunnelSDL/.src/CTunnel.class create mode 100644 app/examples/OpenGL/TunnelSDL/.src/MMain.module create mode 100644 app/examples/OpenGL/TunnelSDL/texture.png create mode 100644 app/examples/OpenGL/TunnelSDL/tunnelsdl.png create mode 100644 app/examples/Printing/Printing/.directory create mode 100644 app/examples/Printing/Printing/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Printing/Printing/.icon.png create mode 100644 app/examples/Printing/Printing/.lang/ru.po create mode 100644 app/examples/Printing/Printing/.project create mode 100644 app/examples/Printing/Printing/.src/FMain.class create mode 100644 app/examples/Printing/Printing/.src/FMain.form create mode 100644 app/examples/Printing/Printing/molly-malone.txt create mode 100644 app/examples/Printing/Printing/printer-laser.png create mode 100644 app/examples/Printing/ReportExample/.connection/Connection1.connection create mode 100644 app/examples/Printing/ReportExample/.connection/Connection1.template create mode 100644 app/examples/Printing/ReportExample/.directory create mode 100644 app/examples/Printing/ReportExample/.hidden/report.png create mode 100644 app/examples/Printing/ReportExample/.icon.png create mode 100644 app/examples/Printing/ReportExample/.lang/ru.po create mode 100644 app/examples/Printing/ReportExample/.project create mode 100644 app/examples/Printing/ReportExample/.src/FMain.class create mode 100644 app/examples/Printing/ReportExample/.src/FMain.form create mode 100644 app/examples/Printing/ReportExample/.src/Report1.class create mode 100644 app/examples/Printing/ReportExample/.src/Report1.report create mode 100644 app/examples/Printing/ReportExample/.src/Report2.class create mode 100644 app/examples/Printing/ReportExample/.src/Report2.report create mode 100644 app/examples/Printing/ReportExample/.src/Report3.class create mode 100644 app/examples/Printing/ReportExample/.src/Report3.report create mode 100644 app/examples/Printing/ReportExample/gambas.svg create mode 100644 app/examples/Web/SmallWiki/.directory create mode 100644 app/examples/Web/SmallWiki/.icon.png create mode 100644 app/examples/Web/SmallWiki/.lang/ru.po create mode 100644 app/examples/Web/SmallWiki/.project create mode 100644 app/examples/Web/SmallWiki/.public/critical.png create mode 100644 app/examples/Web/SmallWiki/.public/edit.png create mode 100644 app/examples/Web/SmallWiki/.public/info.png create mode 100644 app/examples/Web/SmallWiki/.public/logo.png create mode 100644 app/examples/Web/SmallWiki/.public/style.css create mode 100644 app/examples/Web/SmallWiki/.public/tip.png create mode 100644 app/examples/Web/SmallWiki/.public/up.png create mode 100644 app/examples/Web/SmallWiki/.public/warning.png create mode 100644 app/examples/Web/SmallWiki/.src/Main.module create mode 100644 app/examples/Web/SmallWiki/.src/Markdown.class create mode 100644 app/examples/Web/SmallWiki/.src/MarkdownLink.class create mode 100644 app/examples/Web/SmallWiki/.src/Markup.module create mode 100644 app/examples/Web/SmallWiki/.src/Wiki.class create mode 100644 app/examples/Web/SmallWiki/.src/Wiki.webpage create mode 100644 app/examples/Web/SmallWiki/.src/WikiMarkdown.class create mode 100644 app/examples/Web/SmallWiki/page create mode 100644 app/examples/Web/SmallWiki/passwd create mode 100644 app/man/Makefile.am create mode 100644 app/man/gambas3.1 create mode 100644 app/man/gbs3.1 create mode 100644 app/man/gbw3.1 create mode 100644 app/mime/application-x-gambasscript-48.png create mode 100644 app/mime/application-x-gambasscript.png create mode 100644 app/mime/application-x-gambasscript.xml create mode 100644 app/mime/application-x-gambasserverpage-48.png create mode 100644 app/mime/application-x-gambasserverpage.png create mode 100644 app/mime/application-x-gambasserverpage.xml create mode 120000 app/reconf create mode 100644 app/src/INSTALL create mode 100644 app/src/gambas-farm-server/.connection/Connection1.connection create mode 100644 app/src/gambas-farm-server/.connection/Connection1.template create mode 100644 app/src/gambas-farm-server/.connection/Connection2.connection create mode 100644 app/src/gambas-farm-server/.connection/gambas3_farm.connection create mode 100644 app/src/gambas-farm-server/.connection/gambas3_farm.template create mode 100644 app/src/gambas-farm-server/.directory create mode 100644 app/src/gambas-farm-server/.icon.png create mode 100644 app/src/gambas-farm-server/.project create mode 100644 app/src/gambas-farm-server/.src/MMain.module create mode 100644 app/src/gambas-farm-server/logo.png create mode 100644 app/src/gambas-farm-server/usage.txt create mode 100644 app/src/gambas-wiki/.connection/Connection1.connection create mode 100644 app/src/gambas-wiki/.directory create mode 100644 app/src/gambas-wiki/.hidden/CHANGELOG create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/hilitor.js create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/style-rtl.css create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/style-w.css create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/style.css create mode 100644 app/src/gambas-wiki/.icon.png create mode 100644 app/src/gambas-wiki/.lang/ar.po create mode 100644 app/src/gambas-wiki/.lang/ca.po create mode 100644 app/src/gambas-wiki/.lang/cs.po create mode 100644 app/src/gambas-wiki/.lang/de.po create mode 100644 app/src/gambas-wiki/.lang/es.po create mode 100644 app/src/gambas-wiki/.lang/es_ES.po create mode 100644 app/src/gambas-wiki/.lang/fa.po create mode 100644 app/src/gambas-wiki/.lang/fr.po create mode 100644 app/src/gambas-wiki/.lang/it.po create mode 100644 app/src/gambas-wiki/.lang/nl.po create mode 100644 app/src/gambas-wiki/.lang/pt_BR.po create mode 100644 app/src/gambas-wiki/.lang/ru.po create mode 100644 app/src/gambas-wiki/.lang/sv.po create mode 100644 app/src/gambas-wiki/.lang/zh.po create mode 100644 app/src/gambas-wiki/.project create mode 120000 app/src/gambas-wiki/.public/critical.png create mode 100644 app/src/gambas-wiki/.public/edit.png create mode 100644 app/src/gambas-wiki/.public/error-bg.png create mode 100644 app/src/gambas-wiki/.public/gambas.png create mode 120000 app/src/gambas-wiki/.public/highlight.js create mode 100644 app/src/gambas-wiki/.public/hilitor.js create mode 100644 app/src/gambas-wiki/.public/home.png create mode 120000 app/src/gambas-wiki/.public/info.png create mode 100644 app/src/gambas-wiki/.public/logo.png create mode 100644 app/src/gambas-wiki/.public/playground.js create mode 100644 app/src/gambas-wiki/.public/style-nh.css create mode 100644 app/src/gambas-wiki/.public/style-rtl.css create mode 100644 app/src/gambas-wiki/.public/style-w.css create mode 100644 app/src/gambas-wiki/.public/style.css create mode 100644 app/src/gambas-wiki/.public/tip.png create mode 100644 app/src/gambas-wiki/.public/up-gray.png create mode 100644 app/src/gambas-wiki/.public/up.png create mode 100644 app/src/gambas-wiki/.public/vb.png create mode 100644 app/src/gambas-wiki/.public/waiting.gif create mode 120000 app/src/gambas-wiki/.public/warning.png create mode 100644 app/src/gambas-wiki/.src/CAuthor.class create mode 100644 app/src/gambas-wiki/.src/CClassInfo.class create mode 100644 app/src/gambas-wiki/.src/CComponent.class create mode 100644 app/src/gambas-wiki/.src/CPropertyInfo.class create mode 100644 app/src/gambas-wiki/.src/CSymbolInfo.class create mode 100644 app/src/gambas-wiki/.src/CUser.class create mode 100644 app/src/gambas-wiki/.src/Confirm.class create mode 100644 app/src/gambas-wiki/.src/Confirm.webpage create mode 100644 app/src/gambas-wiki/.src/FixNews.module create mode 100644 app/src/gambas-wiki/.src/HttpStat.module create mode 100644 app/src/gambas-wiki/.src/Main.module create mode 100644 app/src/gambas-wiki/.src/OldWiki.module create mode 100644 app/src/gambas-wiki/.src/Register.class create mode 100644 app/src/gambas-wiki/.src/Register.webpage create mode 100644 app/src/gambas-wiki/.src/Wiki.class create mode 100644 app/src/gambas-wiki/.src/Wiki.webpage create mode 100644 app/src/gambas-wiki/.src/WikiMarkdown.class create mode 120000 app/src/gambas-wiki/AUTHORS create mode 120000 app/src/gambas-wiki/gambas3-ide.project create mode 120000 app/src/gambas-wiki/gambas3-scripter.project create mode 100644 app/src/gambas-wiki/icon.png create mode 100644 app/src/gambas-wiki/page create mode 100644 app/src/gambas-wiki/passwd create mode 100644 app/src/gambas3-selftest/.directory create mode 100644 app/src/gambas3-selftest/.hidden/logo.png create mode 100644 app/src/gambas3-selftest/.icon.png create mode 100755 app/src/gambas3-selftest/.lang/en_GB.po create mode 100644 app/src/gambas3-selftest/.lang/it.po create mode 100644 app/src/gambas3-selftest/.project create mode 100755 app/src/gambas3-selftest/.settings_old create mode 100644 app/src/gambas3-selftest/.src/TestSources/BaseClass.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/ChildClass.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/Class1.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/Class4.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/Class5.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/Crash.class create mode 100644 app/src/gambas3-selftest/.src/TestSources/Crash2.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/Crash3.class create mode 100644 app/src/gambas3-selftest/.src/TestSources/FileReadWriteSources/mFileReadWrite.module create mode 100755 app/src/gambas3-selftest/.src/TestSources/Module1.module create mode 100755 app/src/gambas3-selftest/.src/TestSources/POINT3D.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/POINTFloat.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/SubCrash.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/TestClass1.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/TestClass2.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/TestClass3.class create mode 100755 app/src/gambas3-selftest/.src/TestSources/YetAnotherClass.class create mode 100644 app/src/gambas3-selftest/.src/TestSources/mTest.module create mode 100644 app/src/gambas3-selftest/.src/Tests/CrashMyTests.test create mode 100644 app/src/gambas3-selftest/.src/Tests/Errorhandling.test create mode 100644 app/src/gambas3-selftest/.src/Tests/FileReadWrite.test create mode 100644 app/src/gambas3-selftest/.src/Tests/GambasSelftests.test create mode 100644 app/src/gambas3-selftest/.src/Tests/LocalStaticFun.test create mode 100644 app/src/gambas3-selftest/.src/Tests/SlowThousandsProcs.test create mode 100644 app/src/gambas3-selftest/.src/Tests/Timers.test create mode 100644 app/src/gambas3-selftest/.test create mode 100644 app/src/gambas3-selftest/COPYING create mode 100755 app/src/gambas3-selftest/Excluded or Unimplemented create mode 100755 app/src/gambas3-selftest/SearchList create mode 100755 app/src/gambas3-selftest/run.sh create mode 100644 app/src/gambas3/.directory create mode 100644 app/src/gambas3/.hidden/font/GambasBold.sfd create mode 100644 app/src/gambas3/.hidden/font/GambasRegular.sfd create mode 100644 app/src/gambas3/.hidden/font/GambasRoundRegular.sfd create mode 100644 app/src/gambas3/.hidden/font/LICENSE create mode 100644 app/src/gambas3/.hidden/font/gambasb12.otb create mode 100644 app/src/gambas3/.hidden/font/gambasb13.otb create mode 100644 app/src/gambas3/.hidden/font/gambasb16.otb create mode 100644 app/src/gambas3/.hidden/font/gambasr12.otb create mode 100644 app/src/gambas3/.hidden/font/gambasr13.otb create mode 100644 app/src/gambas3/.hidden/font/gambasr16.otb create mode 100644 app/src/gambas3/.hidden/font/groundr16.otb create mode 100644 app/src/gambas3/.hidden/make-help-archive create mode 100644 app/src/gambas3/.hidden/report-ng.sh create mode 100644 app/src/gambas3/.icon.png create mode 100644 app/src/gambas3/.lang/ar.po create mode 100644 app/src/gambas3/.lang/ca.po create mode 100644 app/src/gambas3/.lang/cs.po create mode 100644 app/src/gambas3/.lang/cy.po create mode 100644 app/src/gambas3/.lang/de.po create mode 100644 app/src/gambas3/.lang/el.po create mode 100644 app/src/gambas3/.lang/es.po create mode 100644 app/src/gambas3/.lang/es_ES.po create mode 100644 app/src/gambas3/.lang/fa.po create mode 100644 app/src/gambas3/.lang/fr.po create mode 100644 app/src/gambas3/.lang/gl_ES.po create mode 100644 app/src/gambas3/.lang/hr.po create mode 100644 app/src/gambas3/.lang/hu.po create mode 100644 app/src/gambas3/.lang/id.po create mode 100644 app/src/gambas3/.lang/it.po create mode 100644 app/src/gambas3/.lang/ja.po create mode 100644 app/src/gambas3/.lang/ko.po create mode 100644 app/src/gambas3/.lang/lt.po create mode 100644 app/src/gambas3/.lang/nl.po create mode 100644 app/src/gambas3/.lang/no.po create mode 100644 app/src/gambas3/.lang/pl.po create mode 100644 app/src/gambas3/.lang/pt.po create mode 100644 app/src/gambas3/.lang/pt_BR.po create mode 100644 app/src/gambas3/.lang/ro.po create mode 100644 app/src/gambas3/.lang/ru.po create mode 100644 app/src/gambas3/.lang/sl.po create mode 100644 app/src/gambas3/.lang/sv.po create mode 100644 app/src/gambas3/.lang/tr.po create mode 100644 app/src/gambas3/.lang/zh.po create mode 100644 app/src/gambas3/.lang/zh_TW.po create mode 100644 app/src/gambas3/.project create mode 100644 app/src/gambas3/.src/CRecentProject.class create mode 100644 app/src/gambas3/.src/CStyle.class create mode 100644 app/src/gambas3/.src/CWaitingAnimation.class create mode 100644 app/src/gambas3/.src/Component/CClassInfo.class create mode 100644 app/src/gambas3/.src/Component/CComponent.class create mode 100644 app/src/gambas3/.src/Component/CDocumentation.class create mode 100644 app/src/gambas3/.src/Component/CModule.class create mode 100644 app/src/gambas3/.src/Component/CPropertyInfo.class create mode 100644 app/src/gambas3/.src/Component/CSymbolInfo.class create mode 100644 app/src/gambas3/.src/Connection/FExportData.class create mode 100644 app/src/gambas3/.src/Connection/FExportData.form create mode 100644 app/src/gambas3/.src/Connection/FImportTable.class create mode 100644 app/src/gambas3/.src/Connection/FImportTable.form create mode 100644 app/src/gambas3/.src/Connection/FNewConnection.class create mode 100644 app/src/gambas3/.src/Connection/FNewConnection.form create mode 100644 app/src/gambas3/.src/Connection/FPasteTable.class create mode 100644 app/src/gambas3/.src/Connection/FPasteTable.form create mode 100644 app/src/gambas3/.src/Connection/MConnection.module create mode 100644 app/src/gambas3/.src/Debug/Breakpoints.module create mode 100644 app/src/gambas3/.src/Debug/CProfile.class create mode 100644 app/src/gambas3/.src/Debug/CProfileTask.class create mode 100644 app/src/gambas3/.src/Debug/Design.module create mode 100644 app/src/gambas3/.src/Debug/FBrowser.class create mode 100644 app/src/gambas3/.src/Debug/FBrowser.form create mode 100644 app/src/gambas3/.src/Debug/FCrash.class create mode 100644 app/src/gambas3/.src/Debug/FCrash.form create mode 100644 app/src/gambas3/.src/Debug/FDebugButton.class create mode 100644 app/src/gambas3/.src/Debug/FDebugButton.form create mode 100644 app/src/gambas3/.src/Debug/FDebugConfig.class create mode 100644 app/src/gambas3/.src/Debug/FDebugConfig.form create mode 100644 app/src/gambas3/.src/Debug/FDebugExpr.class create mode 100644 app/src/gambas3/.src/Debug/FDebugExpr.form create mode 100644 app/src/gambas3/.src/Debug/FDebugInfo.class create mode 100644 app/src/gambas3/.src/Debug/FDebugInfo.form create mode 100644 app/src/gambas3/.src/Debug/FDebugger.class create mode 100644 app/src/gambas3/.src/Debug/FDebugger.form create mode 100644 app/src/gambas3/.src/Debug/FOutput.class create mode 100644 app/src/gambas3/.src/Debug/FOutput.form create mode 100644 app/src/gambas3/.src/Debug/FProfile.class create mode 100644 app/src/gambas3/.src/Debug/FProfile.form create mode 100644 app/src/gambas3/.src/Debug/FTestSuite.class create mode 100644 app/src/gambas3/.src/Debug/FTestSuite.form create mode 100644 app/src/gambas3/.src/Dialog/Database/FFieldChooser.class create mode 100644 app/src/gambas3/.src/Dialog/Database/FFieldChooser.form create mode 100644 app/src/gambas3/.src/Dialog/Database/FTableChooser.class create mode 100644 app/src/gambas3/.src/Dialog/Database/FTableChooser.form create mode 100644 app/src/gambas3/.src/Dialog/FColorChooser.class create mode 100644 app/src/gambas3/.src/Dialog/FColorChooser.form create mode 100644 app/src/gambas3/.src/Dialog/FFileProperty.class create mode 100644 app/src/gambas3/.src/Dialog/FFileProperty.form create mode 100644 app/src/gambas3/.src/Dialog/FFontChooser.class create mode 100644 app/src/gambas3/.src/Dialog/FFontChooser.form create mode 100644 app/src/gambas3/.src/Dialog/FList.class create mode 100644 app/src/gambas3/.src/Dialog/FList.form create mode 100644 app/src/gambas3/.src/Dialog/FSelectIcon.class create mode 100644 app/src/gambas3/.src/Dialog/FSelectIcon.form create mode 100644 app/src/gambas3/.src/Editor/Browse/FFileOverwrite.class create mode 100644 app/src/gambas3/.src/Editor/Browse/FFileOverwrite.form create mode 100644 app/src/gambas3/.src/Editor/Browse/FProjectBrowser.class create mode 100644 app/src/gambas3/.src/Editor/Browse/FProjectBrowser.form create mode 100644 app/src/gambas3/.src/Editor/CBookmark.class create mode 100644 app/src/gambas3/.src/Editor/CInsertColor.class create mode 100644 app/src/gambas3/.src/Editor/CInsertDate.class create mode 100644 app/src/gambas3/.src/Editor/CPosition.class create mode 100644 app/src/gambas3/.src/Editor/CTask.class create mode 100644 app/src/gambas3/.src/Editor/CUndo.class create mode 100644 app/src/gambas3/.src/Editor/Code/CCompletion.class create mode 100644 app/src/gambas3/.src/Editor/Code/CDatatype.class create mode 100644 app/src/gambas3/.src/Editor/Code/CSampleCode.class create mode 100644 app/src/gambas3/.src/Editor/Code/FCompletion.class create mode 100644 app/src/gambas3/.src/Editor/Code/FCompletion.form create mode 100644 app/src/gambas3/.src/Editor/Code/FConflictEditor.class create mode 100644 app/src/gambas3/.src/Editor/Code/FConflictEditor.form create mode 100644 app/src/gambas3/.src/Editor/Code/FEditor.class create mode 100644 app/src/gambas3/.src/Editor/Code/FEditor.form create mode 100644 app/src/gambas3/.src/Editor/Code/FPasteSpecial.class create mode 100644 app/src/gambas3/.src/Editor/Code/FPasteSpecial.form create mode 100644 app/src/gambas3/.src/Editor/Code/FProcedureList.class create mode 100644 app/src/gambas3/.src/Editor/Code/FProcedureList.form create mode 100644 app/src/gambas3/.src/Editor/Code/FSignature.class create mode 100644 app/src/gambas3/.src/Editor/Code/FSignature.form create mode 100644 app/src/gambas3/.src/Editor/Code/FTextEditor.class create mode 100644 app/src/gambas3/.src/Editor/Code/FTextEditor.form create mode 100644 app/src/gambas3/.src/Editor/Code/MPrettyCode.module create mode 100644 app/src/gambas3/.src/Editor/Connection/CField.class create mode 100644 app/src/gambas3/.src/Editor/Connection/CIndexField.class create mode 100644 app/src/gambas3/.src/Editor/Connection/FConnectionEditor.class create mode 100644 app/src/gambas3/.src/Editor/Connection/FConnectionEditor.form create mode 100644 app/src/gambas3/.src/Editor/Connection/FNewTable.class create mode 100644 app/src/gambas3/.src/Editor/Connection/FNewTable.form create mode 100644 app/src/gambas3/.src/Editor/Csv/FCsvEditor.class create mode 100644 app/src/gambas3/.src/Editor/Csv/FCsvEditor.form create mode 100644 app/src/gambas3/.src/Editor/Csv/FCsvOption.class create mode 100644 app/src/gambas3/.src/Editor/Csv/FCsvOption.form create mode 100644 app/src/gambas3/.src/Editor/FInsertChar.class create mode 100644 app/src/gambas3/.src/Editor/FInsertChar.form create mode 100644 app/src/gambas3/.src/Editor/Form/CControl.class create mode 100644 app/src/gambas3/.src/Editor/Form/CMenu.class create mode 100644 app/src/gambas3/.src/Editor/Form/FDeleteUnknown.class create mode 100644 app/src/gambas3/.src/Editor/Form/FDeleteUnknown.form create mode 100644 app/src/gambas3/.src/Editor/Form/FForm.class create mode 100644 app/src/gambas3/.src/Editor/Form/FForm.form create mode 100644 app/src/gambas3/.src/Editor/Form/FFormStack.class create mode 100644 app/src/gambas3/.src/Editor/Form/FFormStack.form create mode 100644 app/src/gambas3/.src/Editor/Form/FMenu.class create mode 100644 app/src/gambas3/.src/Editor/Form/FMenu.form create mode 100644 app/src/gambas3/.src/Editor/Form/FProperty.class create mode 100644 app/src/gambas3/.src/Editor/Form/FProperty.form create mode 100644 app/src/gambas3/.src/Editor/Form/FText.class create mode 100644 app/src/gambas3/.src/Editor/Form/FText.form create mode 100644 app/src/gambas3/.src/Editor/Form/FToolBox.class create mode 100644 app/src/gambas3/.src/Editor/Form/FToolBox.form create mode 100644 app/src/gambas3/.src/Editor/Form/FToolPanel.class create mode 100644 app/src/gambas3/.src/Editor/Form/FToolPanel.form create mode 100644 app/src/gambas3/.src/Editor/Image/CImageClipboard.class create mode 100644 app/src/gambas3/.src/Editor/Image/CImageSelection.class create mode 100644 app/src/gambas3/.src/Editor/Image/CImageShape.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageEditor.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageEditor.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageOffsetSelection.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageOffsetSelection.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageProperty.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageProperty.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageQuality.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageQuality.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageResize.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageResize.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageRotate.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageRotate.form create mode 100644 app/src/gambas3/.src/Editor/Image/ImageSelectionBox.class create mode 100644 app/src/gambas3/.src/Editor/MCompressFile.module create mode 100644 app/src/gambas3/.src/Editor/MPrint.module create mode 100644 app/src/gambas3/.src/Exported/EnvChooser/EnvChooser.class create mode 100644 app/src/gambas3/.src/Exported/ProjectChooser/FProjectChooser.class create mode 100644 app/src/gambas3/.src/Exported/ProjectChooser/FProjectChooser.form create mode 100644 app/src/gambas3/.src/Exported/ProjectChooser/ProjectChooser.class create mode 100644 app/src/gambas3/.src/Exported/TabPanel.class create mode 100644 app/src/gambas3/.src/Exported/TextEditor.class create mode 100644 app/src/gambas3/.src/FCommandError.class create mode 100644 app/src/gambas3/.src/FCommandError.form create mode 100644 app/src/gambas3/.src/FMain.class create mode 100644 app/src/gambas3/.src/FMain.form create mode 100644 app/src/gambas3/.src/FSave.class create mode 100644 app/src/gambas3/.src/FSave.form create mode 100644 app/src/gambas3/.src/FScreenshot.class create mode 100644 app/src/gambas3/.src/FScreenshot.form create mode 100644 app/src/gambas3/.src/Family/CFamily.class create mode 100644 app/src/gambas3/.src/Family/Form/CFamilyForm.class create mode 100644 app/src/gambas3/.src/Family/Report/AngleBox.class create mode 100644 app/src/gambas3/.src/Family/Report/CFamilyReport.class create mode 100644 app/src/gambas3/.src/Family/Report/CReportBrush.class create mode 100644 app/src/gambas3/.src/Family/Report/CoordBox.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBorderChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBorderChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportBoxShadowChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBoxShadowChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportBrushChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBrushChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportCoordChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportCoordChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportPaddingChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportPaddingChooser.form create mode 100644 app/src/gambas3/.src/Family/TermForm/CFamilyTermForm.class create mode 100644 app/src/gambas3/.src/Family/WebForm/CFamilyWebForm.class create mode 100644 app/src/gambas3/.src/Family/WebForm/FWebFontChooser.class create mode 100644 app/src/gambas3/.src/Family/WebForm/FWebFontChooser.form create mode 100644 app/src/gambas3/.src/Family/WebForm/WebformWebMenu.class create mode 100644 app/src/gambas3/.src/Help/CHelpSearchTask.class create mode 100644 app/src/gambas3/.src/Help/FHelpBrowser.class create mode 100644 app/src/gambas3/.src/Help/FHelpBrowser.form create mode 100644 app/src/gambas3/.src/Help/FHelpShortcut.class create mode 100644 app/src/gambas3/.src/Help/FHelpShortcut.form create mode 100644 app/src/gambas3/.src/Help/FTips.class create mode 100644 app/src/gambas3/.src/Help/FTips.form create mode 100644 app/src/gambas3/.src/Help/HelpBrowser.class create mode 100644 app/src/gambas3/.src/Help/HelpView.class create mode 100644 app/src/gambas3/.src/Help/MHelp.module create mode 100644 app/src/gambas3/.src/Help/Wiki/URL.class create mode 100644 app/src/gambas3/.src/Help/Wiki/Wiki.module create mode 100644 app/src/gambas3/.src/Help/Wiki/WikiMarkdown.class create mode 100644 app/src/gambas3/.src/MMime.module create mode 100644 app/src/gambas3/.src/MTest.module create mode 100644 app/src/gambas3/.src/MTheme.module create mode 100644 app/src/gambas3/.src/Options/CBackground.class create mode 100644 app/src/gambas3/.src/Options/FLayout.class create mode 100644 app/src/gambas3/.src/Options/FLayout.form create mode 100644 app/src/gambas3/.src/Options/FOption.class create mode 100644 app/src/gambas3/.src/Options/FOption.form create mode 100644 app/src/gambas3/.src/Options/FProxy.class create mode 100644 app/src/gambas3/.src/Options/FProxy.form create mode 100644 app/src/gambas3/.src/Options/FSnippet.class create mode 100644 app/src/gambas3/.src/Options/FSnippet.form create mode 100644 app/src/gambas3/.src/Packager/FMakeInstall.class create mode 100644 app/src/gambas3/.src/Packager/FMakeInstall.form create mode 100644 app/src/gambas3/.src/Packager/FSelectExtraFile.class create mode 100644 app/src/gambas3/.src/Packager/FSelectExtraFile.form create mode 100644 app/src/gambas3/.src/Packager/Package.module create mode 100644 app/src/gambas3/.src/Project.module create mode 100644 app/src/gambas3/.src/Project/CProjectInfo.class create mode 100644 app/src/gambas3/.src/Project/CProjectList.class create mode 100644 app/src/gambas3/.src/Project/CProjectTree.class create mode 100644 app/src/gambas3/.src/Project/Component/ComponentChooser.class create mode 100644 app/src/gambas3/.src/Project/Component/FComponentChooser.class create mode 100644 app/src/gambas3/.src/Project/Component/FComponentChooser.form create mode 100644 app/src/gambas3/.src/Project/Component/FSelectComponent.class create mode 100644 app/src/gambas3/.src/Project/Component/FSelectComponent.form create mode 100644 app/src/gambas3/.src/Project/Conversion/FConvert.class create mode 100644 app/src/gambas3/.src/Project/Conversion/FConvert.form create mode 100644 app/src/gambas3/.src/Project/Conversion/MConvert.module create mode 100644 app/src/gambas3/.src/Project/FCreateFile.class create mode 100644 app/src/gambas3/.src/Project/FCreateFile.form create mode 100644 app/src/gambas3/.src/Project/FCreateProject.class create mode 100644 app/src/gambas3/.src/Project/FCreateProject.form create mode 100644 app/src/gambas3/.src/Project/FImportFile.class create mode 100644 app/src/gambas3/.src/Project/FImportFile.form create mode 100644 app/src/gambas3/.src/Project/FMakeExecutable.class create mode 100644 app/src/gambas3/.src/Project/FMakeExecutable.form create mode 100644 app/src/gambas3/.src/Project/FMakeSourceArchive.class create mode 100644 app/src/gambas3/.src/Project/FMakeSourceArchive.form create mode 100644 app/src/gambas3/.src/Project/FOpenProject.class create mode 100644 app/src/gambas3/.src/Project/FOpenProject.form create mode 100644 app/src/gambas3/.src/Project/FProjectProperty.class create mode 100644 app/src/gambas3/.src/Project/FProjectProperty.form create mode 100644 app/src/gambas3/.src/Project/FSaveProjectAs.class create mode 100644 app/src/gambas3/.src/Project/FSaveProjectAs.form create mode 100644 app/src/gambas3/.src/Project/Farm/CSoftware.class create mode 100644 app/src/gambas3/.src/Project/Farm/CSoftwareGroup.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmConfig.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmConfig.form create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmLogin.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmLogin.form create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRegister.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRegister.form create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRequest.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRequest.form create mode 100644 app/src/gambas3/.src/Project/Farm/FPublish.class create mode 100644 app/src/gambas3/.src/Project/Farm/FPublish.form create mode 100644 app/src/gambas3/.src/Project/Farm/FSoftwareFarm.class create mode 100644 app/src/gambas3/.src/Project/Farm/FSoftwareFarm.form create mode 100644 app/src/gambas3/.src/Project/Farm/FarmIdentity.class create mode 100644 app/src/gambas3/.src/Project/Farm/FarmRequest.class create mode 100644 app/src/gambas3/.src/Project/Farm/FarmRequestManager.module create mode 100644 app/src/gambas3/.src/Project/Farm/Publish/CTag.class create mode 100644 app/src/gambas3/.src/Project/Farm/Publish/CTagCompletion.class create mode 100644 app/src/gambas3/.src/Project/Farm/Publish/CTagEditor.class create mode 100644 app/src/gambas3/.src/Project/Farm/SoftwareBox.class create mode 100644 app/src/gambas3/.src/Project/Library/CLibraryInfo.class create mode 100644 app/src/gambas3/.src/Project/Library/FSelectLibrary.class create mode 100644 app/src/gambas3/.src/Project/Library/FSelectLibrary.form create mode 100644 app/src/gambas3/.src/Project/Library/LibraryItem.class create mode 100644 app/src/gambas3/.src/Project/Patch/FMakePatch.class create mode 100644 app/src/gambas3/.src/Project/Patch/FMakePatch.form create mode 100644 app/src/gambas3/.src/Project/Patch/FPatch.class create mode 100644 app/src/gambas3/.src/Project/Patch/FPatch.form create mode 100644 app/src/gambas3/.src/Project/Patch/Patch.class create mode 100644 app/src/gambas3/.src/Project/ProjectBox.class create mode 100644 app/src/gambas3/.src/Save.module create mode 100644 app/src/gambas3/.src/Search/CGrepResult.class create mode 100644 app/src/gambas3/.src/Search/CSearchResult.class create mode 100644 app/src/gambas3/.src/Search/CSearchTask.class create mode 100644 app/src/gambas3/.src/Search/FSearch.class create mode 100644 app/src/gambas3/.src/Search/FSearch.form create mode 100644 app/src/gambas3/.src/Translation/CTranslation.class create mode 100644 app/src/gambas3/.src/Translation/FNewTranslation.class create mode 100644 app/src/gambas3/.src/Translation/FNewTranslation.form create mode 100644 app/src/gambas3/.src/Translation/FTranslate.class create mode 100644 app/src/gambas3/.src/Translation/FTranslate.form create mode 100644 app/src/gambas3/.src/Translation/MyLanguage.module create mode 100644 app/src/gambas3/.src/Util.module create mode 100644 app/src/gambas3/.src/Util/MErrorMessage.module create mode 100644 app/src/gambas3/.src/Util/MRemoveAccents.module create mode 100644 app/src/gambas3/.src/Util/MSdlDefaultFont.module create mode 100644 app/src/gambas3/.src/VersionControl/CVersionControl.class create mode 100644 app/src/gambas3/.src/VersionControl/CVersionControlGit.class create mode 100644 app/src/gambas3/.src/VersionControl/CVersionControlHistoryTask.class create mode 100644 app/src/gambas3/.src/VersionControl/CVersionControlSubversion.class create mode 100644 app/src/gambas3/.src/VersionControl/FConflict.class create mode 100644 app/src/gambas3/.src/VersionControl/FConflict.form create mode 100644 app/src/gambas3/.src/VersionControl/FCreateBranch.class create mode 100644 app/src/gambas3/.src/VersionControl/FCreateBranch.form create mode 100644 app/src/gambas3/.src/VersionControl/FFileInfoVC.class create mode 100644 app/src/gambas3/.src/VersionControl/FFileInfoVC.form create mode 100644 app/src/gambas3/.src/VersionControl/FProjectVersion.class create mode 100644 app/src/gambas3/.src/VersionControl/FProjectVersion.form create mode 100644 app/src/gambas3/.src/VersionControl/FVersionControl.class create mode 100644 app/src/gambas3/.src/VersionControl/FVersionControl.form create mode 100644 app/src/gambas3/.src/VersionControl/VersionControl.module create mode 100644 app/src/gambas3/.src/Welcome/CCoolButton.class create mode 100644 app/src/gambas3/.src/Welcome/CSnowFlake.class create mode 100644 app/src/gambas3/.src/Welcome/CTear.class create mode 100644 app/src/gambas3/.src/Welcome/CUser.class create mode 100644 app/src/gambas3/.src/Welcome/CWelcome.class create mode 100644 app/src/gambas3/.src/Welcome/FAbout.class create mode 100644 app/src/gambas3/.src/Welcome/FAbout.form create mode 100644 app/src/gambas3/.src/Welcome/FSystemInfo.class create mode 100644 app/src/gambas3/.src/Welcome/FSystemInfo.form create mode 100644 app/src/gambas3/.src/Welcome/FWelcome.class create mode 100644 app/src/gambas3/.src/Welcome/FWelcome.form create mode 120000 app/src/gambas3/AUTHORS create mode 100644 app/src/gambas3/_fake_project create mode 100644 app/src/gambas3/debugger/Main.module create mode 100644 app/src/gambas3/debugger/_project create mode 100644 app/src/gambas3/font/font.allow create mode 100644 app/src/gambas3/font/font.conf create mode 120000 app/src/gambas3/font/gambasb12.otb create mode 120000 app/src/gambas3/font/gambasb13.otb create mode 120000 app/src/gambas3/font/gambasb16.otb create mode 120000 app/src/gambas3/font/gambasr12.otb create mode 120000 app/src/gambas3/font/gambasr13.otb create mode 120000 app/src/gambas3/font/gambasr16.otb create mode 120000 app/src/gambas3/font/groundr16.otb create mode 100644 app/src/gambas3/gitignore create mode 100644 app/src/gambas3/help/class-help.html create mode 100644 app/src/gambas3/help/component-help.html create mode 100644 app/src/gambas3/help/property-help.html create mode 100644 app/src/gambas3/help/symbol-help.html create mode 120000 app/src/gambas3/help/wiki/critical.png create mode 100644 app/src/gambas3/help/wiki/error-bg.png create mode 100644 app/src/gambas3/help/wiki/highlight.js create mode 120000 app/src/gambas3/help/wiki/info.png create mode 100644 app/src/gambas3/help/wiki/page.html create mode 100644 app/src/gambas3/help/wiki/style-custom.css create mode 100644 app/src/gambas3/help/wiki/style-offline-htmlview.css create mode 100644 app/src/gambas3/help/wiki/style-offline.css create mode 120000 app/src/gambas3/help/wiki/style.css create mode 120000 app/src/gambas3/help/wiki/tip.png create mode 100644 app/src/gambas3/help/wiki/up.png create mode 120000 app/src/gambas3/help/wiki/vb.png create mode 120000 app/src/gambas3/help/wiki/warning.png create mode 100644 app/src/gambas3/img/16/arrange-h.png create mode 100644 app/src/gambas3/img/16/arrange-hcenter.png create mode 100644 app/src/gambas3/img/16/arrange-lr.png create mode 100644 app/src/gambas3/img/16/arrange-tb.png create mode 100644 app/src/gambas3/img/16/arrange-v.png create mode 100644 app/src/gambas3/img/16/arrange-vcenter.png create mode 100644 app/src/gambas3/img/16/checked-gray.png create mode 100644 app/src/gambas3/img/16/checked-lock.png create mode 100644 app/src/gambas3/img/16/checked.png create mode 100644 app/src/gambas3/img/16/close-window.png create mode 100644 app/src/gambas3/img/16/collapse-container.png create mode 120000 app/src/gambas3/img/16/configure.png create mode 100644 app/src/gambas3/img/16/control.png create mode 100644 app/src/gambas3/img/16/cursive.png create mode 100644 app/src/gambas3/img/16/expand-container.png create mode 100644 app/src/gambas3/img/16/fantasy.png create mode 100644 app/src/gambas3/img/16/max-window.png create mode 100644 app/src/gambas3/img/16/monospace.png create mode 100644 app/src/gambas3/img/16/red-arrow-c.png create mode 100644 app/src/gambas3/img/16/red-arrow-h.png create mode 100644 app/src/gambas3/img/16/red-arrow-r.png create mode 100644 app/src/gambas3/img/16/red-arrow-v.png create mode 100644 app/src/gambas3/img/16/remove-from-container.png create mode 100644 app/src/gambas3/img/16/rename.png create mode 100644 app/src/gambas3/img/16/round-ne.png create mode 100644 app/src/gambas3/img/16/round-nw.png create mode 100644 app/src/gambas3/img/16/round-se.png create mode 100644 app/src/gambas3/img/16/round-sw.png create mode 100644 app/src/gambas3/img/16/sans-serif.png create mode 100644 app/src/gambas3/img/16/select.png create mode 100644 app/src/gambas3/img/16/separator.png create mode 100644 app/src/gambas3/img/16/serif.png create mode 100644 app/src/gambas3/img/16/stack.png create mode 100644 app/src/gambas3/img/16/tabmove.png create mode 100644 app/src/gambas3/img/16/tabstrip.png create mode 100644 app/src/gambas3/img/16/tile.png create mode 100644 app/src/gambas3/img/16/toggle-container.png create mode 100644 app/src/gambas3/img/16/unchecked.png create mode 100644 app/src/gambas3/img/16/white-close.png create mode 100644 app/src/gambas3/img/32/breakpoint.png create mode 100644 app/src/gambas3/img/32/comment.png create mode 100644 app/src/gambas3/img/32/cross.png create mode 100644 app/src/gambas3/img/32/dbus.png create mode 100644 app/src/gambas3/img/32/delete-container.png create mode 100644 app/src/gambas3/img/32/do-not-translate.png create mode 100644 app/src/gambas3/img/32/embed-container.png create mode 100644 app/src/gambas3/img/32/eol.png create mode 100644 app/src/gambas3/img/32/error.png create mode 100644 app/src/gambas3/img/32/exported-namespace.png create mode 100644 app/src/gambas3/img/32/exported.png create mode 100644 app/src/gambas3/img/32/filter-menu.png create mode 100644 app/src/gambas3/img/32/filter.png create mode 100644 app/src/gambas3/img/32/linked.png create mode 100644 app/src/gambas3/img/32/magic.png create mode 100644 app/src/gambas3/img/32/multicontainer.png create mode 100644 app/src/gambas3/img/32/opengl.png create mode 100644 app/src/gambas3/img/32/tab.png create mode 100644 app/src/gambas3/img/32/tile.png create mode 100644 app/src/gambas3/img/32/uncomment.png create mode 100644 app/src/gambas3/img/32/wrap.png create mode 100644 app/src/gambas3/img/48/all.png create mode 100644 app/src/gambas3/img/background/baraka.png create mode 100644 app/src/gambas3/img/background/christ.png create mode 100644 app/src/gambas3/img/background/circle.png create mode 100644 app/src/gambas3/img/background/cross.png create mode 100644 app/src/gambas3/img/background/cubism.png create mode 100644 app/src/gambas3/img/background/dark.png create mode 100644 app/src/gambas3/img/background/gambas.png create mode 100644 app/src/gambas3/img/background/hline.png create mode 100644 app/src/gambas3/img/background/hose.png create mode 100644 app/src/gambas3/img/background/illusion.png create mode 100644 app/src/gambas3/img/background/labyrinth.png create mode 100644 app/src/gambas3/img/background/light.png create mode 100644 app/src/gambas3/img/background/list create mode 100644 app/src/gambas3/img/background/medracim.png create mode 100644 app/src/gambas3/img/background/mosaic.png create mode 100644 app/src/gambas3/img/background/nature.png create mode 100644 app/src/gambas3/img/background/oil.png create mode 100644 app/src/gambas3/img/background/point.png create mode 100644 app/src/gambas3/img/background/quasi.png create mode 100644 app/src/gambas3/img/background/smoke.png create mode 100644 app/src/gambas3/img/background/square.png create mode 100644 app/src/gambas3/img/background/star.png create mode 100644 app/src/gambas3/img/background/tawhid.png create mode 100644 app/src/gambas3/img/background/vline.png create mode 100644 app/src/gambas3/img/background/warda.png create mode 100644 app/src/gambas3/img/background/weave.png create mode 100644 app/src/gambas3/img/broken.svg create mode 100644 app/src/gambas3/img/component/deprecated.png create mode 100644 app/src/gambas3/img/component/experimental.png create mode 100644 app/src/gambas3/img/component/finished.png create mode 100644 app/src/gambas3/img/component/unfinished.png create mode 100644 app/src/gambas3/img/component/unknown.png create mode 100644 app/src/gambas3/img/contrib/Radoslav.gif create mode 100644 app/src/gambas3/img/control/button.png create mode 100644 app/src/gambas3/img/control/checkbox.png create mode 100644 app/src/gambas3/img/control/columnview.png create mode 100644 app/src/gambas3/img/control/combobox.png create mode 100644 app/src/gambas3/img/control/deprecated.png create mode 100644 app/src/gambas3/img/control/dial.png create mode 100644 app/src/gambas3/img/control/dnsclient.png create mode 100644 app/src/gambas3/img/control/drawingarea.png create mode 100644 app/src/gambas3/img/control/embedder.png create mode 100644 app/src/gambas3/img/control/frame.png create mode 100644 app/src/gambas3/img/control/ftpclient.png create mode 100644 app/src/gambas3/img/control/glarea.png create mode 100644 app/src/gambas3/img/control/gridview.png create mode 100644 app/src/gambas3/img/control/hbox.png create mode 100644 app/src/gambas3/img/control/hpanel.png create mode 100644 app/src/gambas3/img/control/hsplit.png create mode 100644 app/src/gambas3/img/control/httpclient.png create mode 100644 app/src/gambas3/img/control/iconview.png create mode 100644 app/src/gambas3/img/control/label.png create mode 100644 app/src/gambas3/img/control/lcdnumber.png create mode 100644 app/src/gambas3/img/control/listbox.png create mode 100644 app/src/gambas3/img/control/listview.png create mode 100644 app/src/gambas3/img/control/moviebox.png create mode 100644 app/src/gambas3/img/control/panel.png create mode 100644 app/src/gambas3/img/control/picturebox.png create mode 100644 app/src/gambas3/img/control/printer.png create mode 100644 app/src/gambas3/img/control/progressbar.png create mode 100644 app/src/gambas3/img/control/radiobutton.png create mode 100644 app/src/gambas3/img/control/scrollarea.png create mode 100644 app/src/gambas3/img/control/scrollbar.png create mode 100644 app/src/gambas3/img/control/scrollview.png create mode 100644 app/src/gambas3/img/control/select.png create mode 100644 app/src/gambas3/img/control/separator.png create mode 100644 app/src/gambas3/img/control/serialport.png create mode 100644 app/src/gambas3/img/control/serversocket.png create mode 100644 app/src/gambas3/img/control/slider.png create mode 100644 app/src/gambas3/img/control/socket.png create mode 100644 app/src/gambas3/img/control/spinbox.png create mode 100644 app/src/gambas3/img/control/splitter.png create mode 100644 app/src/gambas3/img/control/spring.png create mode 100644 app/src/gambas3/img/control/tabstrip.png create mode 100644 app/src/gambas3/img/control/textarea.png create mode 100644 app/src/gambas3/img/control/textbox.png create mode 100644 app/src/gambas3/img/control/textedit.png create mode 100644 app/src/gambas3/img/control/textlabel.png create mode 100644 app/src/gambas3/img/control/timer.png create mode 100644 app/src/gambas3/img/control/togglebutton.png create mode 100644 app/src/gambas3/img/control/toolbutton.png create mode 100644 app/src/gambas3/img/control/trayicon.png create mode 100644 app/src/gambas3/img/control/treeview.png create mode 100644 app/src/gambas3/img/control/udpsocket.png create mode 100644 app/src/gambas3/img/control/unknown.png create mode 100644 app/src/gambas3/img/control/vbox.png create mode 100644 app/src/gambas3/img/control/void.png create mode 100644 app/src/gambas3/img/control/void2.png create mode 100644 app/src/gambas3/img/control/voidbutton.png create mode 100644 app/src/gambas3/img/control/vpanel.png create mode 100644 app/src/gambas3/img/control/vsplit.png create mode 100644 app/src/gambas3/img/control/webview.png create mode 100644 app/src/gambas3/img/crash/atari-dark.png create mode 100644 app/src/gambas3/img/crash/atari.png create mode 100644 app/src/gambas3/img/draw/duplicate.png create mode 100644 app/src/gambas3/img/draw/expand.png create mode 100644 app/src/gambas3/img/draw/invert.png create mode 100644 app/src/gambas3/img/draw/move.png create mode 100644 app/src/gambas3/img/draw/offset.png create mode 100644 app/src/gambas3/img/draw/save-selection.png create mode 100644 app/src/gambas3/img/family/column-i.png create mode 100644 app/src/gambas3/img/family/column.png create mode 100644 app/src/gambas3/img/family/expand.png create mode 100644 app/src/gambas3/img/family/fill-c.png create mode 100644 app/src/gambas3/img/family/fill.png create mode 100644 app/src/gambas3/img/family/horizontal-c.png create mode 100644 app/src/gambas3/img/family/horizontal-i-c.png create mode 100644 app/src/gambas3/img/family/horizontal-i.png create mode 100644 app/src/gambas3/img/family/horizontal.png create mode 100644 app/src/gambas3/img/family/hsplit.png create mode 100644 app/src/gambas3/img/family/row-i.png create mode 100644 app/src/gambas3/img/family/row.png create mode 100644 app/src/gambas3/img/family/vertical-c.png create mode 100644 app/src/gambas3/img/family/vertical-i-c.png create mode 100644 app/src/gambas3/img/family/vertical-i.png create mode 100644 app/src/gambas3/img/family/vertical.png create mode 100644 app/src/gambas3/img/family/vsplit.png create mode 100644 app/src/gambas3/img/logo/archlinux.png create mode 100644 app/src/gambas3/img/logo/autotools.png create mode 100644 app/src/gambas3/img/logo/debian.png create mode 100644 app/src/gambas3/img/logo/fedora.png create mode 100644 app/src/gambas3/img/logo/folder.png create mode 100644 app/src/gambas3/img/logo/gambas.svg create mode 100644 app/src/gambas3/img/logo/head-16.png create mode 100644 app/src/gambas3/img/logo/head-256.png create mode 100644 app/src/gambas3/img/logo/head-32.png create mode 100644 app/src/gambas3/img/logo/logo-ide.png create mode 100644 app/src/gambas3/img/logo/logo.png create mode 100644 app/src/gambas3/img/logo/mageia.png create mode 100644 app/src/gambas3/img/logo/mandriva.png create mode 100644 app/src/gambas3/img/logo/package-gnu.png create mode 100644 app/src/gambas3/img/logo/project.png create mode 100644 app/src/gambas3/img/logo/redhat.png create mode 100644 app/src/gambas3/img/logo/self-extractible.png create mode 100644 app/src/gambas3/img/logo/slackware.png create mode 100644 app/src/gambas3/img/logo/suse.png create mode 100644 app/src/gambas3/img/logo/ubuntu.png create mode 100644 app/src/gambas3/img/module/class.png create mode 100644 app/src/gambas3/img/module/form.png create mode 100644 app/src/gambas3/img/module/module.png create mode 100644 app/src/gambas3/img/module/report.png create mode 100644 app/src/gambas3/img/module/termform.png create mode 100644 app/src/gambas3/img/module/test.png create mode 100644 app/src/gambas3/img/module/webform.png create mode 100644 app/src/gambas3/img/module/webpage.png create mode 100644 app/src/gambas3/img/symbol/constant.png create mode 100644 app/src/gambas3/img/symbol/control.png create mode 100644 app/src/gambas3/img/symbol/event.png create mode 100644 app/src/gambas3/img/symbol/method.png create mode 100644 app/src/gambas3/img/symbol/property-ro.png create mode 100644 app/src/gambas3/img/symbol/property-rw.png create mode 100644 app/src/gambas3/img/symbol/property.png create mode 100644 app/src/gambas3/img/symbol/s-method.png create mode 100644 app/src/gambas3/img/symbol/s-property-ro.png create mode 100644 app/src/gambas3/img/symbol/s-property-rw.png create mode 100644 app/src/gambas3/img/symbol/s-property.png create mode 100644 app/src/gambas3/img/symbol/s-special.png create mode 100644 app/src/gambas3/img/symbol/s-variable.png create mode 100644 app/src/gambas3/img/symbol/special.png create mode 100644 app/src/gambas3/img/symbol/symbol.png create mode 100644 app/src/gambas3/img/symbol/unknown.png create mode 100644 app/src/gambas3/img/symbol/variable.png create mode 100644 app/src/gambas3/img/waiting.gif create mode 100644 app/src/gambas3/install/Makefile.am create mode 100644 app/src/gambas3/install/acinclude.m4 create mode 100644 app/src/gambas3/install/categories create mode 100644 app/src/gambas3/install/group/archlinux create mode 100644 app/src/gambas3/install/group/autotools create mode 100644 app/src/gambas3/install/group/debian create mode 100644 app/src/gambas3/install/group/fedora create mode 100644 app/src/gambas3/install/group/mageia create mode 100644 app/src/gambas3/install/group/mandriva create mode 100644 app/src/gambas3/install/group/self create mode 100644 app/src/gambas3/install/group/slackware create mode 100644 app/src/gambas3/install/group/suse create mode 100644 app/src/gambas3/install/group/ubuntu create mode 100644 app/src/gambas3/install/menu/archlinux create mode 100644 app/src/gambas3/install/menu/autotools create mode 100644 app/src/gambas3/install/menu/debian create mode 100644 app/src/gambas3/install/menu/fedora create mode 100644 app/src/gambas3/install/menu/mageia create mode 100644 app/src/gambas3/install/menu/mandriva create mode 100644 app/src/gambas3/install/menu/self create mode 100644 app/src/gambas3/install/menu/slackware create mode 100644 app/src/gambas3/install/menu/suse create mode 100644 app/src/gambas3/install/menu/ubuntu create mode 100644 app/src/gambas3/install/slack-desc-header create mode 100644 app/src/gambas3/layout/$compact.layout create mode 100644 app/src/gambas3/layout/$default.layout create mode 100644 app/src/gambas3/license create mode 100644 app/src/gambas3/markdown/markdown.css create mode 100644 app/src/gambas3/markdown/markdown.js create mode 100644 app/src/gambas3/po-header.txt create mode 100644 app/src/gambas3/pot-header.txt create mode 100644 app/src/gambas3/shortcut.desktop create mode 100644 app/src/gambas3/snippets create mode 100644 app/src/gambas3/support.txt create mode 100644 app/src/gambas3/tags.txt create mode 100644 app/src/gambas3/template/CClass.class create mode 100644 app/src/gambas3/template/CContainer.class create mode 100644 app/src/gambas3/template/CControl.class create mode 100644 app/src/gambas3/template/FMain.class create mode 100644 app/src/gambas3/template/FMain.form create mode 100644 app/src/gambas3/template/FTest.class create mode 100644 app/src/gambas3/template/FTest.form create mode 100644 app/src/gambas3/template/MMain.module create mode 100644 app/src/gambas3/template/MTest.module create mode 100644 app/src/gambas3/template/_project create mode 100644 app/src/gambas3/template/ccontainer.png create mode 100644 app/src/gambas3/template/ccontrol.png create mode 100644 app/src/gambas3/template/list create mode 100644 app/src/gambas3/theme/amber create mode 100644 app/src/gambas3/theme/amethyst create mode 100644 app/src/gambas3/theme/blues create mode 100644 app/src/gambas3/theme/emerald create mode 100644 app/src/gambas3/theme/gambas create mode 100644 app/src/gambas3/theme/obsidian create mode 100644 app/src/gambas3/theme/pastel create mode 100644 app/src/gambas3/theme/quest create mode 100644 app/src/gambas3/theme/quick create mode 100644 app/src/gambas3/theme/ruby create mode 100644 app/src/gambas3/theme/sapphire create mode 100644 app/src/gambas3/theme/visual create mode 100644 app/src/gambas3/theme/zen create mode 100644 app/src/gambas3/tips/tips.ca.txt create mode 100644 app/src/gambas3/tips/tips.cs.txt create mode 100644 app/src/gambas3/tips/tips.de.txt create mode 100644 app/src/gambas3/tips/tips.en.txt create mode 100644 app/src/gambas3/tips/tips.es.txt create mode 100644 app/src/gambas3/tips/tips.fr.txt create mode 100644 app/src/gambas3/tips/tips.it.txt create mode 100644 app/src/gambas3/tips/tips.nl.txt create mode 100644 app/src/gambas3/tips/tips.pl.txt create mode 100644 app/src/gambas3/tips/tips.ru.txt create mode 100644 app/src/gambas3/tips/tips.sl.txt create mode 100644 app/src/gambas3/tips/tips.sv.txt create mode 100644 app/src/gambas3/tips/tips.tr.txt create mode 100644 app/src/gambas3/tips/tips.zh.txt create mode 100644 app/src/gambas3/tips/tips.zh_TW.txt create mode 100644 app/src/gambas3/usage create mode 100644 app/src/gambas3/welcome/bells.mp3 create mode 100644 app/src/gambas3/welcome/border.png create mode 100644 app/src/gambas3/welcome/corner.png create mode 100644 app/src/gambas3/welcome/fir.png create mode 100644 app/src/gambas3/welcome/spiritus.mp3 create mode 100644 app/src/gb.wiki/.directory create mode 100644 app/src/gb.wiki/.icon.png create mode 100644 app/src/gb.wiki/.lang/it.po create mode 100644 app/src/gb.wiki/.lang/nl.po create mode 100644 app/src/gb.wiki/.lang/pt_BR.po create mode 100644 app/src/gb.wiki/.lang/ru.po create mode 100644 app/src/gb.wiki/.lang/zh.po create mode 100644 app/src/gb.wiki/.project create mode 100644 app/src/gb.wiki/.src/Actions.module create mode 100644 app/src/gb.wiki/.src/Controllers/Admin.module create mode 100644 app/src/gb.wiki/.src/Controllers/Image.module create mode 100644 app/src/gb.wiki/.src/Controllers/UserManage.module create mode 100644 app/src/gb.wiki/.src/Controllers/Wiki.module create mode 100644 app/src/gb.wiki/.src/DBScheme.class create mode 100644 app/src/gb.wiki/.src/Main.module create mode 100644 app/src/gb.wiki/.src/Models/Comments.module create mode 100644 app/src/gb.wiki/.src/Models/Pages.module create mode 100644 app/src/gb.wiki/.src/Models/Users.module create mode 100644 app/src/gb.wiki/.src/Models/_Page.class create mode 100644 app/src/gb.wiki/.src/Models/_User.class create mode 100644 app/src/gb.wiki/.src/Parser.module create mode 100644 app/src/gb.wiki/.src/Path.module create mode 100644 app/src/gb.wiki/.src/Views/Admin/WAdminMain.class create mode 100644 app/src/gb.wiki/.src/Views/Admin/WAdminMain.webpage create mode 100644 app/src/gb.wiki/.src/Views/WAccount.class create mode 100644 app/src/gb.wiki/.src/Views/WAccount.webpage create mode 100644 app/src/gb.wiki/.src/Views/WEdit.class create mode 100644 app/src/gb.wiki/.src/Views/WEdit.webpage create mode 100644 app/src/gb.wiki/.src/Views/WHeader.class create mode 100644 app/src/gb.wiki/.src/Views/WHeader.webpage create mode 100644 app/src/gb.wiki/.src/Views/WLogin.class create mode 100644 app/src/gb.wiki/.src/Views/WLogin.webpage create mode 100644 app/src/gb.wiki/.src/Views/WWiki.class create mode 100644 app/src/gb.wiki/.src/Views/WWiki.webpage create mode 100644 app/src/gb.wiki/help.png create mode 100644 app/src/gbs3/.directory create mode 100644 app/src/gbs3/.hidden/CHANGELOG create mode 100644 app/src/gbs3/.icon.png create mode 100644 app/src/gbs3/.lang/OldTranslation/en_US.po create mode 100644 app/src/gbs3/.lang/OldTranslation/fr.po create mode 100644 app/src/gbs3/.lang/de.po create mode 100644 app/src/gbs3/.lang/en_US.po create mode 100644 app/src/gbs3/.lang/fr.po create mode 100644 app/src/gbs3/.lang/it.po create mode 100644 app/src/gbs3/.project create mode 100644 app/src/gbs3/.src/CComponent.class create mode 100644 app/src/gbs3/.src/ClassDef.class create mode 100644 app/src/gbs3/.src/Compile.module create mode 100644 app/src/gbs3/.src/CompileError.module create mode 100644 app/src/gbs3/.src/Context.class create mode 100644 app/src/gbs3/.src/ConvertProject.module create mode 100644 app/src/gbs3/.src/GenerateFiles.module create mode 100644 app/src/gbs3/.src/GenerateFilesFromArgs.module create mode 100644 app/src/gbs3/.src/GenerateFromLazyScript.module create mode 100644 app/src/gbs3/.src/Libraries.module create mode 100644 app/src/gbs3/.src/MServerPage.module create mode 100644 app/src/gbs3/.src/MakeListing.module create mode 100644 app/src/gbs3/.src/MakeVirtualProject.module create mode 100644 app/src/gbs3/.src/ParseArgs.module create mode 100644 app/src/gbs3/.src/PluginInfo.module create mode 100644 app/src/gbs3/.src/ProcessAssignment.module create mode 100644 app/src/gbs3/.src/Reader.class create mode 100644 app/src/gbs3/.src/ScriptApplyAlias.module create mode 100644 app/src/gbs3/.src/ScriptApplyLineCallBacks.module create mode 100644 app/src/gbs3/.src/ScriptApplyPostProgCallBacks.module create mode 100644 app/src/gbs3/.src/ScriptApplyProgramCallBacks.module create mode 100644 app/src/gbs3/.src/ScriptKeywordFilter.module create mode 100644 app/src/gbs3/.src/ScriptMain.module create mode 100644 app/src/gbs3/.src/ScriptPreProcess.module create mode 100644 app/src/gbs3/.src/ScriptSetAliasTable.module create mode 100644 app/src/gbs3/.src/ScriptSetKeywordFilter.module create mode 100644 app/src/gbs3/.src/ScriptSetPostProcessCallBack.module create mode 100644 app/src/gbs3/.src/ScriptSetPreProcessCallBack.module create mode 100644 app/src/gbs3/.src/ScripterExecute.class create mode 100644 app/src/gbs3/.src/ScripterPlugin.class create mode 100644 app/src/gbs3/.src/TestSuite/TSR.module create mode 100644 app/src/gbs3/.src/TestSuite/TaMServerPages.test create mode 100644 app/src/gbs3/.src/TestSuite/TbCommandLinePrograms.test create mode 100644 app/src/gbs3/.src/TestSuite/TcMMainFunctions.test create mode 100644 app/src/gbs3/.src/TestSuite/TdGeneralCompile.test create mode 100644 app/src/gbs3/.src/TestSuite/TeAliases.test create mode 100644 app/src/gbs3/.src/TestSuite/TfKeyWordFilter.test create mode 100644 app/src/gbs3/.src/TestSuite/TgPluginMode.test create mode 100644 app/src/gbs3/.src/TestSuite/ThPrePostProcessCallBacks.test create mode 100644 app/src/gbs3/.src/UseLibComp.module create mode 100644 app/src/gbs3/.src/Verbose.module create mode 100644 app/src/gbs3/.src/Warning.module create mode 100644 app/src/gbs3/.test create mode 100644 app/src/gbs3/PrePostProcessPlugin create mode 100644 app/src/gbs3/RunComponent create mode 100644 app/src/gbs3/TestApp create mode 100644 app/src/gbs3/TestAppCompiled create mode 100644 app/src/gbs3/TestAppInclude create mode 100644 app/src/gbs3/TestAppInclude2 create mode 100644 app/src/gbs3/TestAppWithMain create mode 100644 app/src/gbs3/TestAppWithMainCompiled create mode 100644 app/src/gbs3/TestExternStructNoMain.gbs create mode 100644 app/src/gbs3/TestExternStructWithMain.gbs create mode 100644 app/src/gbs3/TestFile.info create mode 100644 app/src/gbs3/TestFileFail.info create mode 100644 app/src/gbs3/TestLazyApp create mode 100644 app/src/gbs3/TestPlugin create mode 100644 app/src/gbs3/TestPlugin2 create mode 100644 app/src/gbs3/TestPlugin3.gbs create mode 100644 app/src/gbs3/TestPrePostProcess.gbs create mode 100644 app/src/gbs3/TestWebPageSource create mode 100644 app/src/gbs3/WebPageCompiled create mode 100644 app/src/gbs3/WebPageExpected create mode 100644 app/src/gbs3/WebPageTest create mode 100644 app/src/gbs3/icon.png create mode 100644 app/src/gbs3/license create mode 100644 app/src/gbs3/usage-gbs create mode 100644 app/src/gbs3/usage-gbw create mode 100644 app/template/console/.directory create mode 100644 app/template/console/.icon.png create mode 100644 app/template/console/.lang/es.po create mode 100644 app/template/console/.lang/es_ES.po create mode 100644 app/template/console/.lang/fr.po create mode 100644 app/template/console/.lang/it.po create mode 100644 app/template/console/.lang/ru.po create mode 100644 app/template/console/.lang/zh.po create mode 100644 app/template/console/.project create mode 100644 app/template/console/.src/Main.module create mode 100644 app/template/console/icon.png create mode 100644 app/template/database/.directory create mode 100644 app/template/database/.icon.png create mode 100644 app/template/database/.lang/fr.po create mode 100644 app/template/database/.lang/it.po create mode 100644 app/template/database/.lang/ru.po create mode 100644 app/template/database/.lang/zh.po create mode 100644 app/template/database/.project create mode 100644 app/template/database/.src/FMain.class create mode 100644 app/template/database/.src/FMain.form create mode 100644 app/template/database/icon.png create mode 100644 app/template/graphical/.directory create mode 100644 app/template/graphical/.icon.png create mode 100644 app/template/graphical/.lang/es.po create mode 100644 app/template/graphical/.lang/es_ES.po create mode 100644 app/template/graphical/.lang/fr.po create mode 100644 app/template/graphical/.lang/it.po create mode 100644 app/template/graphical/.lang/ru.po create mode 100644 app/template/graphical/.lang/zh.po create mode 100644 app/template/graphical/.project create mode 100644 app/template/graphical/.src/FMain.class create mode 100644 app/template/graphical/.src/FMain.form create mode 100644 app/template/graphical/icon.png create mode 100644 app/template/gtk2/.directory create mode 100644 app/template/gtk2/.icon.png create mode 100644 app/template/gtk2/.lang/es.po create mode 100644 app/template/gtk2/.lang/es_ES.po create mode 100644 app/template/gtk2/.lang/fr.po create mode 100644 app/template/gtk2/.lang/it.po create mode 100644 app/template/gtk2/.lang/ru.po create mode 100644 app/template/gtk2/.lang/zh.po create mode 100644 app/template/gtk2/.project create mode 100644 app/template/gtk2/.src/FMain.class create mode 100644 app/template/gtk2/.src/FMain.form create mode 100644 app/template/gtk2/icon.png create mode 100644 app/template/gtk3/.directory create mode 100644 app/template/gtk3/.icon.png create mode 100644 app/template/gtk3/.lang/es.po create mode 100644 app/template/gtk3/.lang/fr.po create mode 100644 app/template/gtk3/.lang/it.po create mode 100644 app/template/gtk3/.lang/ru.po create mode 100644 app/template/gtk3/.lang/zh.po create mode 100644 app/template/gtk3/.project create mode 100644 app/template/gtk3/.src/FMain.class create mode 100644 app/template/gtk3/.src/FMain.form create mode 100644 app/template/gtk3/icon.png create mode 100644 app/template/qt/.directory create mode 100644 app/template/qt/.icon.png create mode 100644 app/template/qt/.lang/es.po create mode 100644 app/template/qt/.lang/es_ES.po create mode 100644 app/template/qt/.lang/fr.po create mode 100644 app/template/qt/.lang/it.po create mode 100644 app/template/qt/.lang/ru.po create mode 100644 app/template/qt/.lang/zh.po create mode 100644 app/template/qt/.project create mode 100644 app/template/qt/.src/FMain.class create mode 100644 app/template/qt/.src/FMain.form create mode 100644 app/template/qt/icon.png create mode 100644 app/template/sdl/.directory create mode 100644 app/template/sdl/.icon.png create mode 100644 app/template/sdl/.lang/es.po create mode 100644 app/template/sdl/.lang/es_ES.po create mode 100644 app/template/sdl/.lang/fr.po create mode 100644 app/template/sdl/.lang/it.po create mode 100644 app/template/sdl/.lang/ru.po create mode 100644 app/template/sdl/.lang/zh.po create mode 100644 app/template/sdl/.project create mode 100644 app/template/sdl/.src/MMain.module create mode 100644 app/template/sdl/icon.png create mode 100644 app/template/web/.directory create mode 100644 app/template/web/.icon.png create mode 100644 app/template/web/.lang/es.po create mode 100644 app/template/web/.lang/es_ES.po create mode 100644 app/template/web/.lang/fr.po create mode 100644 app/template/web/.lang/it.po create mode 100644 app/template/web/.lang/ru.po create mode 100644 app/template/web/.lang/zh.po create mode 100644 app/template/web/.project create mode 100644 app/template/web/.src/Main.class create mode 100644 app/template/web/.src/Main.webpage create mode 100644 app/template/web/icon.png create mode 100644 app/template/webform/.directory create mode 100644 app/template/webform/.icon.png create mode 100644 app/template/webform/.lang/fr.po create mode 100644 app/template/webform/.lang/it.po create mode 100644 app/template/webform/.lang/ru.po create mode 100644 app/template/webform/.lang/zh.po create mode 100644 app/template/webform/.project create mode 100644 app/template/webform/.src/Webform1.class create mode 100644 app/template/webform/.src/Webform1.webform create mode 100644 app/template/webform/icon.png create mode 100644 app/template/~subversion/.directory create mode 100644 app/template/~subversion/.icon.png create mode 100644 app/template/~subversion/.lang/es.po create mode 100644 app/template/~subversion/.lang/es_ES.po create mode 100644 app/template/~subversion/.lang/fr.po create mode 100644 app/template/~subversion/.lang/it.po create mode 100644 app/template/~subversion/.lang/ru.po create mode 100644 app/template/~subversion/.lang/zh.po create mode 100644 app/template/~subversion/.project create mode 100644 app/template/~subversion/.src/Main.module create mode 100644 app/template/~subversion/icon.png create mode 100755 benchmark/benchmark.gbs create mode 100755 benchmark/mandelbrot.gbs create mode 100755 benchmark/mandelbrot.jvs create mode 100755 benchmark/mandelbrot.pl create mode 100755 benchmark/mandelbrot.py create mode 100755 benchmark/nbody.gbs create mode 100755 benchmark/nbody.jvs create mode 100755 benchmark/nbody.pl create mode 100755 benchmark/nbody.py create mode 100755 benchmark/polynom.gbs create mode 100755 benchmark/polynom.jvs create mode 100755 benchmark/polynom.pl create mode 100755 benchmark/polynom.py create mode 100755 benchmark/primes.gbs create mode 100755 benchmark/primes.jvs create mode 100755 benchmark/primes.pl create mode 100755 benchmark/primes.py create mode 100755 benchmark/sort.gbs create mode 100755 benchmark/sort.jvs create mode 100755 benchmark/sort.pl create mode 100755 benchmark/sort.py create mode 100755 benchmark/string.gbs create mode 100755 benchmark/string.jvs create mode 100755 benchmark/string.pl create mode 100755 benchmark/string.py create mode 100755 benchmark/string2.gbs create mode 100755 benchmark/string2.jvs create mode 100755 benchmark/string2.pl create mode 100755 benchmark/string2.py create mode 100755 benchmark/test.jvs create mode 100755 build-dist create mode 100644 comp/AUTHORS create mode 120000 comp/COPYING create mode 100644 comp/ChangeLog create mode 100644 comp/INSTALL create mode 100644 comp/Makefile.am create mode 100644 comp/NEWS create mode 100644 comp/README create mode 100644 comp/TODO create mode 120000 comp/acinclude.m4 create mode 100644 comp/configure.ac create mode 120000 comp/reconf create mode 100644 comp/src/gb.args/.component create mode 100644 comp/src/gb.args/.directory create mode 100644 comp/src/gb.args/.icon.png create mode 100644 comp/src/gb.args/.lang/cs.po create mode 100644 comp/src/gb.args/.lang/es.po create mode 100644 comp/src/gb.args/.lang/es_ES.po create mode 100644 comp/src/gb.args/.lang/fr.po create mode 100644 comp/src/gb.args/.lang/it.po create mode 100644 comp/src/gb.args/.lang/nl.po create mode 100644 comp/src/gb.args/.lang/pt_BR.po create mode 100644 comp/src/gb.args/.lang/ru.po create mode 100644 comp/src/gb.args/.lang/zh.po create mode 100644 comp/src/gb.args/.project create mode 100644 comp/src/gb.args/.src/Args.module create mode 100644 comp/src/gb.args/.src/MMain.module create mode 100644 comp/src/gb.chart/.component create mode 100644 comp/src/gb.chart/.directory create mode 100644 comp/src/gb.chart/.icon.png create mode 100644 comp/src/gb.chart/.project create mode 100644 comp/src/gb.chart/.src/CPoint.class create mode 100644 comp/src/gb.chart/.src/CRect.class create mode 100644 comp/src/gb.chart/.src/Chart.class create mode 100644 comp/src/gb.chart/.src/ChartStyle.class create mode 100644 comp/src/gb.chart/.src/ChartType.class create mode 100644 comp/src/gb.chart/.src/FTest.class create mode 100644 comp/src/gb.chart/.src/FTest.form create mode 100644 comp/src/gb.chart/.src/FTest2.class create mode 100644 comp/src/gb.chart/.src/FTest2.form create mode 100644 comp/src/gb.chart/.src/MTools.module create mode 100644 comp/src/gb.chart/.src/Styles/_CSerie.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleAreas.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleBar.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleColumns.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleLine.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStylePie.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStylePlot.class create mode 100644 comp/src/gb.chart/.src/_CAxes.class create mode 100644 comp/src/gb.chart/.src/_CHeaders.class create mode 100644 comp/src/gb.chart/.src/_CLabel.class create mode 100644 comp/src/gb.chart/.src/_CLabels.class create mode 100644 comp/src/gb.chart/.src/_CLegend.class create mode 100644 comp/src/gb.chart/.src/_CTitle.class create mode 100644 comp/src/gb.chart/.src/_CXAxe.class create mode 100644 comp/src/gb.chart/.src/_CYAxe.class create mode 100644 comp/src/gb.chart/.src/_Colors.class create mode 100644 comp/src/gb.chart/img/areas.png create mode 100644 comp/src/gb.chart/img/areasnormal.png create mode 100644 comp/src/gb.chart/img/areaspercent.png create mode 100644 comp/src/gb.chart/img/areasstacked.png create mode 100644 comp/src/gb.chart/img/bars.png create mode 100644 comp/src/gb.chart/img/barsnormal.png create mode 100644 comp/src/gb.chart/img/barspercent.png create mode 100644 comp/src/gb.chart/img/barsstacked.png create mode 100644 comp/src/gb.chart/img/columns.png create mode 100644 comp/src/gb.chart/img/columnslinecombination.png create mode 100644 comp/src/gb.chart/img/columnsnormal.png create mode 100644 comp/src/gb.chart/img/columnspercent.png create mode 100644 comp/src/gb.chart/img/columnsstacked.png create mode 100644 comp/src/gb.chart/img/lines.png create mode 100644 comp/src/gb.chart/img/linesnormal.png create mode 100644 comp/src/gb.chart/img/linespercent.png create mode 100644 comp/src/gb.chart/img/linesstacked.png create mode 100644 comp/src/gb.chart/img/linessymbols.png create mode 100644 comp/src/gb.chart/img/net.png create mode 100644 comp/src/gb.chart/img/pie.png create mode 100644 comp/src/gb.chart/img/pienormal.png create mode 100644 comp/src/gb.chart/img/pieoffset1.png create mode 100644 comp/src/gb.chart/img/pieoffset2.png create mode 100644 comp/src/gb.chart/img/pierings.png create mode 100644 comp/src/gb.chart/img/plots.png create mode 100644 comp/src/gb.chart/img/plotsnormal.png create mode 100644 comp/src/gb.chart/img/stockchart.png create mode 100644 comp/src/gb.db.form/.component create mode 100644 comp/src/gb.db.form/.connection/Connection1.connection create mode 100644 comp/src/gb.db.form/.connection/Connection2.connection create mode 100644 comp/src/gb.db.form/.connection/Connection3.connection create mode 100644 comp/src/gb.db.form/.connection/Connection4.connection create mode 100644 comp/src/gb.db.form/.directory create mode 100644 comp/src/gb.db.form/.hidden/control/databrowser.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacheckbox.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacombo.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacomboview.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacontrol.png create mode 100644 comp/src/gb.db.form/.hidden/control/datasource.png create mode 100644 comp/src/gb.db.form/.hidden/control/dataview.png create mode 100644 comp/src/gb.db.form/.hidden/icon.png create mode 100644 comp/src/gb.db.form/.icon.png create mode 100644 comp/src/gb.db.form/.lang/ca.po create mode 100644 comp/src/gb.db.form/.lang/cs.po create mode 100644 comp/src/gb.db.form/.lang/de.po create mode 100644 comp/src/gb.db.form/.lang/es.po create mode 100644 comp/src/gb.db.form/.lang/es_ES.po create mode 100644 comp/src/gb.db.form/.lang/fr.po create mode 100644 comp/src/gb.db.form/.lang/it.po create mode 100644 comp/src/gb.db.form/.lang/nl.po create mode 100644 comp/src/gb.db.form/.lang/pt_BR.po create mode 100644 comp/src/gb.db.form/.lang/ru.po create mode 100644 comp/src/gb.db.form/.lang/sv.po create mode 100644 comp/src/gb.db.form/.lang/zh.po create mode 100644 comp/src/gb.db.form/.project create mode 100644 comp/src/gb.db.form/.src/Common.module create mode 100644 comp/src/gb.db.form/.src/DataBrowser.class create mode 100644 comp/src/gb.db.form/.src/DataCheckBox.class create mode 100644 comp/src/gb.db.form/.src/DataCombo.class create mode 100644 comp/src/gb.db.form/.src/DataComboView.class create mode 100644 comp/src/gb.db.form/.src/DataConnection.class create mode 100644 comp/src/gb.db.form/.src/DataControl.class create mode 100644 comp/src/gb.db.form/.src/DataField.class create mode 100644 comp/src/gb.db.form/.src/DataSource.class create mode 100644 comp/src/gb.db.form/.src/DataTable.class create mode 100644 comp/src/gb.db.form/.src/DataTree.class create mode 100644 comp/src/gb.db.form/.src/DataView.class create mode 100644 comp/src/gb.db.form/.src/FBlobEditor.class create mode 100644 comp/src/gb.db.form/.src/FBlobEditor.form create mode 100644 comp/src/gb.db.form/.src/FBrowser.class create mode 100644 comp/src/gb.db.form/.src/FBrowser.form create mode 100644 comp/src/gb.db.form/.src/Test/FMain.class create mode 100644 comp/src/gb.db.form/.src/Test/FMain.form create mode 100644 comp/src/gb.db.form/.src/Test/FMain2.class create mode 100644 comp/src/gb.db.form/.src/Test/FMain2.form create mode 100644 comp/src/gb.db.form/.src/Test/FMain3.class create mode 100644 comp/src/gb.db.form/.src/Test/FMain3.form create mode 100644 comp/src/gb.db.form/.src/Test/FTest.class create mode 100644 comp/src/gb.db.form/.src/Test/FTest.form create mode 100644 comp/src/gb.db.form/.src/Test/Main.module create mode 100644 comp/src/gb.dbus.trayicon/.component create mode 100644 comp/src/gb.dbus.trayicon/.directory create mode 100644 comp/src/gb.dbus.trayicon/.hidden/com.canonical.dbusmenu.xml create mode 100644 comp/src/gb.dbus.trayicon/.hidden/doc.txt create mode 100644 comp/src/gb.dbus.trayicon/.icon.png create mode 100644 comp/src/gb.dbus.trayicon/.project create mode 100644 comp/src/gb.dbus.trayicon/.src/DBusStatusIcon.class create mode 100644 comp/src/gb.dbus.trayicon/.src/DBusStatusIconMenu.class create mode 100644 comp/src/gb.dbus.trayicon/.src/FMain.class create mode 100644 comp/src/gb.dbus.trayicon/.src/FMain.form create mode 100644 comp/src/gb.dbus.trayicon/.src/FMain1.class create mode 100644 comp/src/gb.dbus.trayicon/.src/FMain1.form create mode 100644 comp/src/gb.dbus.trayicon/.src/FTest.class create mode 100644 comp/src/gb.dbus.trayicon/.src/FTest.form create mode 100644 comp/src/gb.dbus.trayicon/.src/Main.module create mode 100644 comp/src/gb.dbus.trayicon/.src/TrayIcon.class create mode 100644 comp/src/gb.dbus.trayicon/.src/TrayIcons.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusMenuItem.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusMenuLayout.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusMenuProperties.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusStatusIconPixmap.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusStatusIconTooltip.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusUInt.class create mode 100644 comp/src/gb.dbus.trayicon/default.png create mode 100644 comp/src/gb.desktop/.component create mode 100644 comp/src/gb.desktop/.directory create mode 100644 comp/src/gb.desktop/.hidden/control/desktopwatcher.png create mode 100644 comp/src/gb.desktop/.hidden/icon.png create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/LICENSE create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/README create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-desktop-icon create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-desktop-menu create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-email create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-icon-resource create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-mime create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-open create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-screensaver create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre.patch/xdg-email create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-copy create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-desktop-icon create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-desktop-menu create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-email create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-file-dialog create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-icon-resource create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-mime create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-open create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-screensaver create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-settings create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-su create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-terminal create mode 100644 comp/src/gb.desktop/.icon.png create mode 100644 comp/src/gb.desktop/.project create mode 100644 comp/src/gb.desktop/.src/Desktop.class create mode 100644 comp/src/gb.desktop/.src/DesktopFile.class create mode 100644 comp/src/gb.desktop/.src/DesktopMime.class create mode 100644 comp/src/gb.desktop/.src/Main.module create mode 100644 comp/src/gb.desktop/.src/Tests/Form1.class create mode 100644 comp/src/gb.desktop/.src/Tests/Form1.form create mode 100644 comp/src/gb.desktop/.src/Tests/Form11.class create mode 100644 comp/src/gb.desktop/.src/Tests/Form11.form create mode 100644 comp/src/gb.desktop/.src/Tests/Form2.class create mode 100644 comp/src/gb.desktop/.src/Tests/Form2.form create mode 100644 comp/src/gb.desktop/.src/Tests/Module1.module create mode 100644 comp/src/gb.desktop/.src/_DesktopIcons.class create mode 100644 comp/src/gb.desktop/.src/_DesktopMenus.class create mode 100644 comp/src/gb.desktop/.src/_Desktop_Passwords.class create mode 100644 comp/src/gb.desktop/.src/_Desktop_ScreenSaver.class create mode 120000 comp/src/gb.desktop/file.png create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-desktop-icon create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-desktop-menu create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-email create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-icon-resource create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-mime create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-open create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-screensaver create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-settings create mode 100644 comp/src/gb.eval.highlight/.component create mode 100644 comp/src/gb.eval.highlight/.directory create mode 100644 comp/src/gb.eval.highlight/.icon.png create mode 100644 comp/src/gb.eval.highlight/.lang/ar.po create mode 100644 comp/src/gb.eval.highlight/.lang/ca.po create mode 100644 comp/src/gb.eval.highlight/.lang/cs.po create mode 100644 comp/src/gb.eval.highlight/.lang/cy.po create mode 100644 comp/src/gb.eval.highlight/.lang/de.po create mode 100644 comp/src/gb.eval.highlight/.lang/el.po create mode 100644 comp/src/gb.eval.highlight/.lang/es.po create mode 100644 comp/src/gb.eval.highlight/.lang/es_ES.po create mode 100644 comp/src/gb.eval.highlight/.lang/fa.po create mode 100644 comp/src/gb.eval.highlight/.lang/fr.po create mode 100644 comp/src/gb.eval.highlight/.lang/gl_ES.po create mode 100644 comp/src/gb.eval.highlight/.lang/hr.po create mode 100644 comp/src/gb.eval.highlight/.lang/hu.po create mode 100644 comp/src/gb.eval.highlight/.lang/id.po create mode 100644 comp/src/gb.eval.highlight/.lang/it.po create mode 100644 comp/src/gb.eval.highlight/.lang/ja.po create mode 100644 comp/src/gb.eval.highlight/.lang/ko.po create mode 100644 comp/src/gb.eval.highlight/.lang/lt.po create mode 100644 comp/src/gb.eval.highlight/.lang/nl.po create mode 100644 comp/src/gb.eval.highlight/.lang/no.po create mode 100644 comp/src/gb.eval.highlight/.lang/pl.po create mode 100644 comp/src/gb.eval.highlight/.lang/pt.po create mode 100644 comp/src/gb.eval.highlight/.lang/pt_BR.po create mode 100644 comp/src/gb.eval.highlight/.lang/ro.po create mode 100644 comp/src/gb.eval.highlight/.lang/ru.po create mode 100644 comp/src/gb.eval.highlight/.lang/sl.po create mode 100644 comp/src/gb.eval.highlight/.lang/sv.po create mode 100644 comp/src/gb.eval.highlight/.lang/tr.po create mode 100644 comp/src/gb.eval.highlight/.lang/zh.po create mode 100644 comp/src/gb.eval.highlight/.lang/zh_TW.po create mode 100644 comp/src/gb.eval.highlight/.project create mode 100644 comp/src/gb.eval.highlight/.src/Highlight.class create mode 100644 comp/src/gb.eval.highlight/.src/Init.module create mode 100644 comp/src/gb.eval.highlight/.src/Main.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/Helper.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightC.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightCPlusPlus.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightCSS.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightDiff.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightHTML.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightJavascript.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightSQL.module create mode 100644 comp/src/gb.eval.highlight/.src/State.class create mode 100644 comp/src/gb.eval.highlight/.src/TestSuite/AnsiTextColor.test create mode 100644 comp/src/gb.eval.highlight/.src/TestSuite/HtmlTextFormatColor.test create mode 100644 comp/src/gb.eval.highlight/.src/TestSuite/RichTextFormatColor.test create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighterStyle.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighterTheme.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_C.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_CPlusPlus.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_CSS.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Diff.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Gambas.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Html.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Javascript.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_SQL.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Sh.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_WebPage.class create mode 100644 comp/src/gb.eval.highlight/.test create mode 100644 comp/src/gb.eval.highlight/UnitTest/ANSITestResults create mode 100644 comp/src/gb.eval.highlight/UnitTest/HTMLTestResults create mode 100644 comp/src/gb.eval.highlight/UnitTest/RTFTestResults create mode 100644 comp/src/gb.eval.highlight/UnitTest/TestProgram create mode 100644 comp/src/gb.eval.highlight/css/properties create mode 100644 comp/src/gb.eval.highlight/css/values create mode 120000 comp/src/gb.eval.highlight/gambas create mode 100644 comp/src/gb.eval.highlight/sql/datatypes create mode 100644 comp/src/gb.eval.highlight/sql/functions create mode 100644 comp/src/gb.eval.highlight/sql/keywords create mode 100644 comp/src/gb.eval.highlight/sql/operators create mode 100644 comp/src/gb.form.dialog/.component create mode 100644 comp/src/gb.form.dialog/.directory create mode 100644 comp/src/gb.form.dialog/.hidden/icon.png create mode 100644 comp/src/gb.form.dialog/.icon.png create mode 100644 comp/src/gb.form.dialog/.lang/ca.po create mode 100644 comp/src/gb.form.dialog/.lang/cs.po create mode 100644 comp/src/gb.form.dialog/.lang/de.po create mode 100644 comp/src/gb.form.dialog/.lang/es.po create mode 100644 comp/src/gb.form.dialog/.lang/es_ES.po create mode 100644 comp/src/gb.form.dialog/.lang/fr.po create mode 100644 comp/src/gb.form.dialog/.lang/it.po create mode 100644 comp/src/gb.form.dialog/.lang/ja.po create mode 100644 comp/src/gb.form.dialog/.lang/nl.po create mode 100644 comp/src/gb.form.dialog/.lang/pt_BR.po create mode 100644 comp/src/gb.form.dialog/.lang/ru.po create mode 100644 comp/src/gb.form.dialog/.lang/sv.po create mode 100644 comp/src/gb.form.dialog/.lang/zh.po create mode 100644 comp/src/gb.form.dialog/.project create mode 100644 comp/src/gb.form.dialog/.src/Dialog.class create mode 100644 comp/src/gb.form.dialog/.src/FAskPassword.class create mode 100644 comp/src/gb.form.dialog/.src/FAskPassword.form create mode 100644 comp/src/gb.form.dialog/.src/FDirDialog.class create mode 100644 comp/src/gb.form.dialog/.src/FDirDialog.form create mode 100644 comp/src/gb.form.dialog/.src/FFileDialog.class create mode 100644 comp/src/gb.form.dialog/.src/FFileDialog.form create mode 100644 comp/src/gb.form.dialog/.src/FFontDialog.class create mode 100644 comp/src/gb.form.dialog/.src/FFontDialog.form create mode 100644 comp/src/gb.form.dialog/.src/FInputDate.class create mode 100644 comp/src/gb.form.dialog/.src/FInputDate.form create mode 100644 comp/src/gb.form.dialog/.src/Main.module create mode 100644 comp/src/gb.form.editor/.component create mode 100644 comp/src/gb.form.editor/.directory create mode 100644 comp/src/gb.form.editor/.hidden/CHANGELOG create mode 100644 comp/src/gb.form.editor/.hidden/control/texteditor.png create mode 100644 comp/src/gb.form.editor/.icon.png create mode 100644 comp/src/gb.form.editor/.lang/fr.po create mode 100644 comp/src/gb.form.editor/.project create mode 100644 comp/src/gb.form.editor/.src/CCommand.class create mode 100644 comp/src/gb.form.editor/.src/CCommandBefore.class create mode 100644 comp/src/gb.form.editor/.src/CDocument.class create mode 100644 comp/src/gb.form.editor/.src/CLineInfo.class create mode 100644 comp/src/gb.form.editor/.src/FFind.class create mode 100644 comp/src/gb.form.editor/.src/FFind.form create mode 100644 comp/src/gb.form.editor/.src/FGoto.class create mode 100644 comp/src/gb.form.editor/.src/FGoto.form create mode 100644 comp/src/gb.form.editor/.src/Helper.module create mode 100644 comp/src/gb.form.editor/.src/Main.module create mode 100644 comp/src/gb.form.editor/.src/TextEditor.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_CSS.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_Gambas.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_HTML.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_Javascript.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_SQL.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_Sh.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_WebPage.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorStyle.class create mode 100644 comp/src/gb.form.editor/.src/_TextEditor_Line.class create mode 100644 comp/src/gb.form.editor/.src/_TextEditor_Rows.class create mode 100644 comp/src/gb.form.editor/.src/_TextEditor_State.class create mode 100644 comp/src/gb.form.editor/.src/test/FTest.class create mode 100644 comp/src/gb.form.editor/.src/test/FTest.form create mode 100644 comp/src/gb.form.editor/.src/test/FTestEditor.class create mode 100644 comp/src/gb.form.editor/.src/test/FTestEditor.form create mode 100644 comp/src/gb.form.editor/Text1 create mode 100644 comp/src/gb.form.editor/test.html create mode 100644 comp/src/gb.form.mdi/.component create mode 100644 comp/src/gb.form.mdi/.directory create mode 100644 comp/src/gb.form.mdi/.hidden/control/toolbar.png create mode 100644 comp/src/gb.form.mdi/.hidden/control/workspace.png create mode 100644 comp/src/gb.form.mdi/.icon.png create mode 100644 comp/src/gb.form.mdi/.lang/ca.po create mode 100644 comp/src/gb.form.mdi/.lang/cs.po create mode 100644 comp/src/gb.form.mdi/.lang/de.po create mode 100644 comp/src/gb.form.mdi/.lang/es.po create mode 100644 comp/src/gb.form.mdi/.lang/es_ES.po create mode 100644 comp/src/gb.form.mdi/.lang/fr.po create mode 100644 comp/src/gb.form.mdi/.lang/it.po create mode 100644 comp/src/gb.form.mdi/.lang/ja.po create mode 100644 comp/src/gb.form.mdi/.lang/nl.po create mode 100644 comp/src/gb.form.mdi/.lang/pt_BR.po create mode 100644 comp/src/gb.form.mdi/.lang/ru.po create mode 100644 comp/src/gb.form.mdi/.lang/sv.po create mode 100644 comp/src/gb.form.mdi/.lang/zh.po create mode 100644 comp/src/gb.form.mdi/.project create mode 100644 comp/src/gb.form.mdi/.src/Action/Action.class create mode 100644 comp/src/gb.form.mdi/.src/Action/CAction.class create mode 100644 comp/src/gb.form.mdi/.src/Action/MAction.module create mode 100644 comp/src/gb.form.mdi/.src/MMain.module create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcut.class create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcut.form create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcutEditor.class create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcutEditor.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain1.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain1.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain2.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain2.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FTestBackground.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FTestBackground.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FTestSidePanel.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FTestSidePanel.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form1.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form1.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form2.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form2.form create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/CToolbar.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBar.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBar.form create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBarConfig.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBarConfig.form create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/ToolBar.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/ToolBarExpander.class create mode 100644 comp/src/gb.form.mdi/.src/Workspace/CWindow.class create mode 100644 comp/src/gb.form.mdi/.src/Workspace/FWorkspace.class create mode 100644 comp/src/gb.form.mdi/.src/Workspace/FWorkspace.form create mode 100644 comp/src/gb.form.mdi/.src/Workspace/Workspace.class create mode 100644 comp/src/gb.form.mdi/70a017.png create mode 100644 comp/src/gb.form.mdi/control/buttonbox.png create mode 100644 comp/src/gb.form.mdi/control/combobox.png create mode 100644 comp/src/gb.form.mdi/control/datebox.png create mode 100644 comp/src/gb.form.mdi/control/valuebox.png create mode 100644 comp/src/gb.form.mdi/img/close.png create mode 100644 comp/src/gb.form.mdi/img/configure.png create mode 100644 comp/src/gb.form.mdi/img/configure_dark.png create mode 100644 comp/src/gb.form.mdi/img/expander.png create mode 100644 comp/src/gb.form.mdi/img/handle-v.png create mode 100644 comp/src/gb.form.mdi/img/handle.png create mode 100644 comp/src/gb.form.mdi/img/hash.png create mode 100644 comp/src/gb.form.mdi/img/roll.png create mode 100644 comp/src/gb.form.mdi/img/separator.png create mode 100644 comp/src/gb.form.mdi/img/space.png create mode 100644 comp/src/gb.form.mdi/img/unroll.png create mode 100644 comp/src/gb.form.print/.component create mode 100644 comp/src/gb.form.print/.directory create mode 100644 comp/src/gb.form.print/.hidden/printer.png create mode 100644 comp/src/gb.form.print/.icon.png create mode 100644 comp/src/gb.form.print/.lang/es.po create mode 100644 comp/src/gb.form.print/.lang/es_ES.po create mode 100644 comp/src/gb.form.print/.lang/fr.po create mode 100644 comp/src/gb.form.print/.lang/it.po create mode 100644 comp/src/gb.form.print/.lang/nl.po create mode 100644 comp/src/gb.form.print/.lang/pt_BR.po create mode 100644 comp/src/gb.form.print/.lang/ru.po create mode 100644 comp/src/gb.form.print/.lang/zh.po create mode 100644 comp/src/gb.form.print/.project create mode 100644 comp/src/gb.form.print/.src/FPreview.class create mode 100644 comp/src/gb.form.print/.src/FPreview.form create mode 100644 comp/src/gb.form.print/.src/FPrinting.class create mode 100644 comp/src/gb.form.print/.src/FPrinting.form create mode 100644 comp/src/gb.form.print/.src/MTest.module create mode 100644 comp/src/gb.form.print/.src/Printer.class create mode 100644 comp/src/gb.form.print/molly-malone.txt create mode 100644 comp/src/gb.form.stock/.component create mode 100644 comp/src/gb.form.stock/.directory create mode 100644 comp/src/gb.form.stock/.hidden/CHANGELOG create mode 100644 comp/src/gb.form.stock/.hidden/earth.png create mode 100644 comp/src/gb.form.stock/.hidden/goutte.png create mode 100644 comp/src/gb.form.stock/.hidden/icon.png create mode 100644 comp/src/gb.form.stock/.icon.png create mode 100644 comp/src/gb.form.stock/.project create mode 100644 comp/src/gb.form.stock/.src/Main.module create mode 100644 comp/src/gb.form.stock/.src/MakeIconTheme.module create mode 100644 comp/src/gb.form.stock/.src/_DefaultStock.class create mode 100644 comp/src/gb.form.stock/gambas-mono/128/alarm.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/android.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/application.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/audio.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/book.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/bus.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/c.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/chart-line.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/chart-pie.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/chart.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/chat.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/clock.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/cloud.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/compass.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/cookie.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/cpp.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/css.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/currency.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/delete.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/directory.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/download.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/error.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/exec.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/file.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/fog.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-document.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-download.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-home.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-image.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-music.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-network.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-recent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-remote.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-root.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-share.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-template.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/folder-video.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/gambas.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/help.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/html.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/image.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/info.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/internet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/java.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/js.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/json.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/layout.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/linux.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/magnet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/map.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/marker.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/md.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/memory.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/message.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/modem.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/money.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/moon-cloud.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/moon.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/multimedia.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/network.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/news.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/pda.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/people.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/phone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/printer.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/program.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/question.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/rain.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/script.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/sdcard.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/share.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/smartphone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/snow.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/sport.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/statistics.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/storm.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/sun-cloud.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/sun.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/taxi.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/template.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/text.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/traffic.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/truck.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/upload.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/user-group.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/user.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/vector.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/video.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/warning.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/xml.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/access.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/add.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/added.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/administrator.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/agenda.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/alarm.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-bottom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-center.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-height.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-middle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-top.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-width.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/anchor.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/android.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/application.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/apply.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/archive.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/attach.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/audio.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/average.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/battery-high.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/battery-low.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/battery-medium.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/battery.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bicycle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/blue.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bluetooth-off.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bluetooth.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/blur.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/book.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bookmark.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-bottom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-diag.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-horizontal.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-inside.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-none.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-outside.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-rev-diag.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-top.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border-vertical.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/border.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bottom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/brightness.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/brush.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bus.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/c.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cake.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/calculator.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/calendar.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/call-stop.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/call.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/camera.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cancel.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cap-butt.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cap-round.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cap-square.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/car.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cdrom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/chart-line.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/chart-pie.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/chart.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/chat.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cherry.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/city.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/clear.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/clock.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/clone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/close.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cloud.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/color-picker.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/color.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/column-after.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/column-before.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/column-remove.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/compass.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/component.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/compress.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/computer.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/conflict.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/connect.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/contrast.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cookie.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/copy.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cpp.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cpu.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/crop.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/css.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/csv.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/currency.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cut.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/database.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/debug-cursor.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/debug-into.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/debug-out.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/debug-over.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/delete.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/desktop.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/development.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/difference.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/directory.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/disconnect.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/down.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/download.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-arrow.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-circle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-line.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-path.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-polygon.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-rectangle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-round.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-star.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/draw-text.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/drink.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/earth.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/edit.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/eject.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-cool.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-crying.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-happy.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-kiss.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-laugh.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-plain.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-raspberry.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-sad.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-surprise.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-uncertain.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/emote-wink.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/end-of-line.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/end.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/energy.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/erase.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/error.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/exclusive.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/exec.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/export.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/factory.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/file-manager.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/file.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/fill.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/filter.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/find.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/firewall.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/first.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flag-blue.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flag-green.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flag-red.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flag-yellow.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flag.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flip-h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flip-v.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/floppy.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/fog.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-blue.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-document.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-download.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-green.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-home.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-image.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-music.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-network.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-recent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-red.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-remote.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-root.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-share.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-template.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-video.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/folder-yellow.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/font.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/formula.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/forward.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/fullscreen.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/gambas.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/game.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/gamma.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/gnu.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/gradient.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/green.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/grid.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/group.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/halt.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/harddisk-root.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/harddisk.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/hardware.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/hatch.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/headset.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/help.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/hibernate.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/highlight.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/home.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/house.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/html.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/hue.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/identity.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/image.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/import.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/important.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/indent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/info.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/inheritance.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/insert-image.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/insert-link.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/insert-table.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/insert-text.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/internet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/intersection.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/invert.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/java.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/join-bevel.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/join-miter.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/join-round.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/js.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/json.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/jump.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/key.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/keyboard.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lamp.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/language.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/last.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/layer.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/layout.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/library.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lightness.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/line-style.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/line-width.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/link.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/linux.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lock-screen.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lock.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/logout.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/love.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lower.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/magnet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/mail.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/make-all.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/make.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/map.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/marker.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/math.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/md.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/media-player.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/memory.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/menu.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/message.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/microphone-off.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/microphone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/modem.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/modified.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/money.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/monitor.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/moon-cloud.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/moon.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/more-h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/more.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/mount.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/mouse.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/move.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/multimedia.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/muted.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/network.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-appointment.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-dir.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-mail.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-tab.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-tag.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-user.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-window.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/news.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/next.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/notification.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office-calc.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office-draw.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office-math.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office-presentation.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/ok.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/opacity.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/open-link.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/open-recent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/open.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/options.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/package.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/page-break.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/page-two.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/page.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/paragraph.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/paste-special.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/paste.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pause.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pda.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pdf.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pen.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/people.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/percent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/phone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pin.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/plane.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/play.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/plugin.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pointer.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/preview.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/previous.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/print.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/printer.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/program.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/properties.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/question.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/quit.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/quote.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rain.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/raise.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/record.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/red.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/redo.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/refresh.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/remove.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rename.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/repeat.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/replace.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/reply-all.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/reply.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/resize.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/restart.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/restaurant.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/revert.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rewind.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rotate-left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rotate-right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rotate.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/row-after.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/row-before.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/row-remove.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rss.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/saturation.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/save-as.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/save.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/scanner.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/science.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/screen.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/script.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sdcard.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/security-high.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/security-low.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/security-medium.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/security.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/select-all.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/select.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/server.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/share.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/shopping.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/shortcut.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/shuffle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/shutdown.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/smartphone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/snow.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sort-ascent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sort-descent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/speaker.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/spell-check.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sport.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/star.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/start.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/statistics.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/stop.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/storm.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/subtitle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sum.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sun-cloud.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sun.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/suspend.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/switch-user.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/system.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/tab.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/table.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/tablet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/tag.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/taxi.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/template.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/terminal.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-baseline.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-bigger.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-bold.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-bottom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-center.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-fill.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-italic.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-line-spacing.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-list-order-rtl.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-list-order.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-list.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-lower.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-middle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-outline.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-shadow.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-smaller.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-strike.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-sub.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-super.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-top.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-underline.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-upper.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/theme.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/today.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/toilet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/tools.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/top.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/town.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/traffic.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/train.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/transparency.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/trash-full.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/trash.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/truck.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/uncompress.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/undo.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/ungroup.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/unindent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/union.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/unlock.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/unselect.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/up.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/update.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/upload.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/usb.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/user-group.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/user.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/vector.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/version-control.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/video.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-compact.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-detail.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-icon.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-normal.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-preview.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-split-h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-split-v.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-tree.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/volume-high.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/volume-low.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/volume-medium.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/volume.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/vpn.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/walk.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/warning.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/watch.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/webcam.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wifi-high.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wifi-low.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wifi-medium.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wifi.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/window.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/windows.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wizard.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/worship.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wrap.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/xml.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-fit.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-in.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-normal-rtl.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-normal.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-out.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-width.png create mode 100644 comp/src/gb.form.stock/gambas-mono/app/gambas3.png create mode 100644 comp/src/gb.form.stock/gambas/128/access.png create mode 100644 comp/src/gb.form.stock/gambas/128/administrator.png create mode 100644 comp/src/gb.form.stock/gambas/128/agenda.png create mode 100644 comp/src/gb.form.stock/gambas/128/alarm.png create mode 100644 comp/src/gb.form.stock/gambas/128/android.png create mode 100644 comp/src/gb.form.stock/gambas/128/application.png create mode 100644 comp/src/gb.form.stock/gambas/128/archive.png create mode 100644 comp/src/gb.form.stock/gambas/128/audio.png create mode 100644 comp/src/gb.form.stock/gambas/128/battery.png create mode 100644 comp/src/gb.form.stock/gambas/128/bicycle.png create mode 100644 comp/src/gb.form.stock/gambas/128/bluetooth-off.png create mode 100644 comp/src/gb.form.stock/gambas/128/bluetooth.png create mode 100644 comp/src/gb.form.stock/gambas/128/book.png create mode 100644 comp/src/gb.form.stock/gambas/128/bookmark.png create mode 100644 comp/src/gb.form.stock/gambas/128/bus.png create mode 100644 comp/src/gb.form.stock/gambas/128/c.png create mode 100644 comp/src/gb.form.stock/gambas/128/cake.png create mode 100644 comp/src/gb.form.stock/gambas/128/calculator.png create mode 100644 comp/src/gb.form.stock/gambas/128/calendar.png create mode 100644 comp/src/gb.form.stock/gambas/128/camera.png create mode 100644 comp/src/gb.form.stock/gambas/128/car.png create mode 100644 comp/src/gb.form.stock/gambas/128/cdrom.png create mode 100644 comp/src/gb.form.stock/gambas/128/chart-line.png create mode 100644 comp/src/gb.form.stock/gambas/128/chart-pie.png create mode 100644 comp/src/gb.form.stock/gambas/128/chart.png create mode 100644 comp/src/gb.form.stock/gambas/128/chat.png create mode 100644 comp/src/gb.form.stock/gambas/128/cherry.png create mode 100644 comp/src/gb.form.stock/gambas/128/city.png create mode 100644 comp/src/gb.form.stock/gambas/128/clock.png create mode 100644 comp/src/gb.form.stock/gambas/128/cloud.png create mode 100644 comp/src/gb.form.stock/gambas/128/color-picker.png create mode 100644 comp/src/gb.form.stock/gambas/128/color.png create mode 100644 comp/src/gb.form.stock/gambas/128/compass.png create mode 100644 comp/src/gb.form.stock/gambas/128/component.png create mode 100644 comp/src/gb.form.stock/gambas/128/computer.png create mode 100644 comp/src/gb.form.stock/gambas/128/cookie.png create mode 100644 comp/src/gb.form.stock/gambas/128/copy.png create mode 100644 comp/src/gb.form.stock/gambas/128/cpp.png create mode 100644 comp/src/gb.form.stock/gambas/128/cpu.png create mode 100644 comp/src/gb.form.stock/gambas/128/css.png create mode 100644 comp/src/gb.form.stock/gambas/128/csv.png create mode 100644 comp/src/gb.form.stock/gambas/128/currency.png create mode 100644 comp/src/gb.form.stock/gambas/128/cut.png create mode 100644 comp/src/gb.form.stock/gambas/128/database.png create mode 100644 comp/src/gb.form.stock/gambas/128/delete.png create mode 100644 comp/src/gb.form.stock/gambas/128/desktop.png create mode 100644 comp/src/gb.form.stock/gambas/128/development.png create mode 100644 comp/src/gb.form.stock/gambas/128/directory.png create mode 100644 comp/src/gb.form.stock/gambas/128/download.png create mode 100644 comp/src/gb.form.stock/gambas/128/drink.png create mode 100644 comp/src/gb.form.stock/gambas/128/earth.png create mode 100644 comp/src/gb.form.stock/gambas/128/edit.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-cool.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-crying.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-happy.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-kiss.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-laugh.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-plain.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-raspberry.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-sad.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-surprise.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-uncertain.png create mode 100644 comp/src/gb.form.stock/gambas/128/emote-wink.png create mode 100644 comp/src/gb.form.stock/gambas/128/energy.png create mode 100644 comp/src/gb.form.stock/gambas/128/error.png create mode 100644 comp/src/gb.form.stock/gambas/128/exec.png create mode 100644 comp/src/gb.form.stock/gambas/128/factory.png create mode 100644 comp/src/gb.form.stock/gambas/128/file-manager.png create mode 100644 comp/src/gb.form.stock/gambas/128/file.png create mode 100644 comp/src/gb.form.stock/gambas/128/find.png create mode 100644 comp/src/gb.form.stock/gambas/128/firewall.png create mode 100644 comp/src/gb.form.stock/gambas/128/flag-blue.png create mode 100644 comp/src/gb.form.stock/gambas/128/flag-green.png create mode 100644 comp/src/gb.form.stock/gambas/128/flag-red.png create mode 100644 comp/src/gb.form.stock/gambas/128/flag-yellow.png create mode 100644 comp/src/gb.form.stock/gambas/128/flag.png create mode 100644 comp/src/gb.form.stock/gambas/128/floppy.png create mode 100644 comp/src/gb.form.stock/gambas/128/fog.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-blue.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-document.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-download.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-green.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-home.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-image.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-music.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-network.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-recent.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-red.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-remote.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-root.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-share.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-template.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-video.png create mode 100644 comp/src/gb.form.stock/gambas/128/folder-yellow.png create mode 100644 comp/src/gb.form.stock/gambas/128/font.png create mode 100644 comp/src/gb.form.stock/gambas/128/gambas.png create mode 100644 comp/src/gb.form.stock/gambas/128/game.png create mode 100644 comp/src/gb.form.stock/gambas/128/gnu.png create mode 100644 comp/src/gb.form.stock/gambas/128/h.png create mode 100644 comp/src/gb.form.stock/gambas/128/harddisk-root.png create mode 100644 comp/src/gb.form.stock/gambas/128/harddisk.png create mode 100644 comp/src/gb.form.stock/gambas/128/hardware.png create mode 100644 comp/src/gb.form.stock/gambas/128/headset.png create mode 100644 comp/src/gb.form.stock/gambas/128/help.png create mode 100644 comp/src/gb.form.stock/gambas/128/hibernate.png create mode 100644 comp/src/gb.form.stock/gambas/128/house.png create mode 100644 comp/src/gb.form.stock/gambas/128/html.png create mode 100644 comp/src/gb.form.stock/gambas/128/identity.png create mode 100644 comp/src/gb.form.stock/gambas/128/image.png create mode 100644 comp/src/gb.form.stock/gambas/128/important.png create mode 100644 comp/src/gb.form.stock/gambas/128/info.png create mode 100644 comp/src/gb.form.stock/gambas/128/internet.png create mode 100644 comp/src/gb.form.stock/gambas/128/java.png create mode 100644 comp/src/gb.form.stock/gambas/128/js.png create mode 100644 comp/src/gb.form.stock/gambas/128/json.png create mode 100644 comp/src/gb.form.stock/gambas/128/keyboard.png create mode 100644 comp/src/gb.form.stock/gambas/128/library.png create mode 100644 comp/src/gb.form.stock/gambas/128/linux.png create mode 100644 comp/src/gb.form.stock/gambas/128/lock-screen.png create mode 100644 comp/src/gb.form.stock/gambas/128/lock.png create mode 100644 comp/src/gb.form.stock/gambas/128/logout.png create mode 100644 comp/src/gb.form.stock/gambas/128/love.png create mode 100644 comp/src/gb.form.stock/gambas/128/magnet.png create mode 100644 comp/src/gb.form.stock/gambas/128/mail.png create mode 100644 comp/src/gb.form.stock/gambas/128/map.png create mode 100644 comp/src/gb.form.stock/gambas/128/marker.png create mode 100644 comp/src/gb.form.stock/gambas/128/md.png create mode 100644 comp/src/gb.form.stock/gambas/128/media-player.png create mode 100644 comp/src/gb.form.stock/gambas/128/memory.png create mode 100644 comp/src/gb.form.stock/gambas/128/message.png create mode 100644 comp/src/gb.form.stock/gambas/128/microphone-off.png create mode 100644 comp/src/gb.form.stock/gambas/128/microphone.png create mode 100644 comp/src/gb.form.stock/gambas/128/modem.png create mode 100644 comp/src/gb.form.stock/gambas/128/money.png create mode 100644 comp/src/gb.form.stock/gambas/128/monitor.png create mode 100644 comp/src/gb.form.stock/gambas/128/moon-cloud.png create mode 100644 comp/src/gb.form.stock/gambas/128/moon.png create mode 100644 comp/src/gb.form.stock/gambas/128/mouse.png create mode 100644 comp/src/gb.form.stock/gambas/128/multimedia.png create mode 100644 comp/src/gb.form.stock/gambas/128/network.png create mode 100644 comp/src/gb.form.stock/gambas/128/new.png create mode 100644 comp/src/gb.form.stock/gambas/128/news.png create mode 100644 comp/src/gb.form.stock/gambas/128/notification.png create mode 100644 comp/src/gb.form.stock/gambas/128/office-calc.png create mode 100644 comp/src/gb.form.stock/gambas/128/office-draw.png create mode 100644 comp/src/gb.form.stock/gambas/128/office-math.png create mode 100644 comp/src/gb.form.stock/gambas/128/office-presentation.png create mode 100644 comp/src/gb.form.stock/gambas/128/office.png create mode 100644 comp/src/gb.form.stock/gambas/128/open-recent.png create mode 100644 comp/src/gb.form.stock/gambas/128/open.png create mode 100644 comp/src/gb.form.stock/gambas/128/options.png create mode 100644 comp/src/gb.form.stock/gambas/128/package.png create mode 100644 comp/src/gb.form.stock/gambas/128/pda.png create mode 100644 comp/src/gb.form.stock/gambas/128/pdf.png create mode 100644 comp/src/gb.form.stock/gambas/128/pen.png create mode 100644 comp/src/gb.form.stock/gambas/128/people.png create mode 100644 comp/src/gb.form.stock/gambas/128/phone.png create mode 100644 comp/src/gb.form.stock/gambas/128/plane.png create mode 100644 comp/src/gb.form.stock/gambas/128/plugin.png create mode 100644 comp/src/gb.form.stock/gambas/128/preview.png create mode 100644 comp/src/gb.form.stock/gambas/128/printer.png create mode 100644 comp/src/gb.form.stock/gambas/128/program.png create mode 100644 comp/src/gb.form.stock/gambas/128/question.png create mode 100644 comp/src/gb.form.stock/gambas/128/quit.png create mode 100644 comp/src/gb.form.stock/gambas/128/rain.png create mode 100644 comp/src/gb.form.stock/gambas/128/rename.png create mode 100644 comp/src/gb.form.stock/gambas/128/restart.png create mode 100644 comp/src/gb.form.stock/gambas/128/restaurant.png create mode 100644 comp/src/gb.form.stock/gambas/128/rss.png create mode 100644 comp/src/gb.form.stock/gambas/128/scanner.png create mode 100644 comp/src/gb.form.stock/gambas/128/science.png create mode 100644 comp/src/gb.form.stock/gambas/128/screen.png create mode 100644 comp/src/gb.form.stock/gambas/128/script.png create mode 100644 comp/src/gb.form.stock/gambas/128/sdcard.png create mode 100644 comp/src/gb.form.stock/gambas/128/security.png create mode 100644 comp/src/gb.form.stock/gambas/128/server.png create mode 100644 comp/src/gb.form.stock/gambas/128/share.png create mode 100644 comp/src/gb.form.stock/gambas/128/shopping.png create mode 100644 comp/src/gb.form.stock/gambas/128/shutdown.png create mode 100644 comp/src/gb.form.stock/gambas/128/smartphone.png create mode 100644 comp/src/gb.form.stock/gambas/128/snow.png create mode 100644 comp/src/gb.form.stock/gambas/128/speaker.png create mode 100644 comp/src/gb.form.stock/gambas/128/sport.png create mode 100644 comp/src/gb.form.stock/gambas/128/star.png create mode 100644 comp/src/gb.form.stock/gambas/128/statistics.png create mode 100644 comp/src/gb.form.stock/gambas/128/storm.png create mode 100644 comp/src/gb.form.stock/gambas/128/sun-cloud.png create mode 100644 comp/src/gb.form.stock/gambas/128/sun.png create mode 100644 comp/src/gb.form.stock/gambas/128/suspend.png create mode 100644 comp/src/gb.form.stock/gambas/128/switch-user.png create mode 100644 comp/src/gb.form.stock/gambas/128/system.png create mode 100644 comp/src/gb.form.stock/gambas/128/table.png create mode 100644 comp/src/gb.form.stock/gambas/128/tablet.png create mode 100644 comp/src/gb.form.stock/gambas/128/tag.png create mode 100644 comp/src/gb.form.stock/gambas/128/taxi.png create mode 100644 comp/src/gb.form.stock/gambas/128/template.png create mode 100644 comp/src/gb.form.stock/gambas/128/terminal.png create mode 100644 comp/src/gb.form.stock/gambas/128/text.png create mode 100644 comp/src/gb.form.stock/gambas/128/theme.png create mode 100644 comp/src/gb.form.stock/gambas/128/toilet.png create mode 100644 comp/src/gb.form.stock/gambas/128/tools.png create mode 100644 comp/src/gb.form.stock/gambas/128/town.png create mode 100644 comp/src/gb.form.stock/gambas/128/traffic.png create mode 100644 comp/src/gb.form.stock/gambas/128/train.png create mode 100644 comp/src/gb.form.stock/gambas/128/trash-full.png create mode 100644 comp/src/gb.form.stock/gambas/128/trash.png create mode 100644 comp/src/gb.form.stock/gambas/128/truck.png create mode 100644 comp/src/gb.form.stock/gambas/128/unlock.png create mode 100644 comp/src/gb.form.stock/gambas/128/update.png create mode 100644 comp/src/gb.form.stock/gambas/128/upload.png create mode 100644 comp/src/gb.form.stock/gambas/128/usb.png create mode 100644 comp/src/gb.form.stock/gambas/128/user-group.png create mode 100644 comp/src/gb.form.stock/gambas/128/user.png create mode 100644 comp/src/gb.form.stock/gambas/128/vector.png create mode 100644 comp/src/gb.form.stock/gambas/128/video.png create mode 100644 comp/src/gb.form.stock/gambas/128/vpn.png create mode 100644 comp/src/gb.form.stock/gambas/128/walk.png create mode 100644 comp/src/gb.form.stock/gambas/128/warning.png create mode 100644 comp/src/gb.form.stock/gambas/128/webcam.png create mode 100644 comp/src/gb.form.stock/gambas/128/window.png create mode 100644 comp/src/gb.form.stock/gambas/128/windows.png create mode 100644 comp/src/gb.form.stock/gambas/128/wizard.png create mode 100644 comp/src/gb.form.stock/gambas/128/worship.png create mode 100644 comp/src/gb.form.stock/gambas/128/xml.png create mode 100644 comp/src/gb.form.stock/gambas/32/access.png create mode 100644 comp/src/gb.form.stock/gambas/32/add.png create mode 100644 comp/src/gb.form.stock/gambas/32/added.png create mode 100644 comp/src/gb.form.stock/gambas/32/administrator.png create mode 100644 comp/src/gb.form.stock/gambas/32/agenda.png create mode 100644 comp/src/gb.form.stock/gambas/32/alarm.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-bottom.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-center.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-height.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-left.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-middle.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-right.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-top.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-width.png create mode 100644 comp/src/gb.form.stock/gambas/32/anchor.png create mode 100644 comp/src/gb.form.stock/gambas/32/android.png create mode 100644 comp/src/gb.form.stock/gambas/32/application.png create mode 100644 comp/src/gb.form.stock/gambas/32/apply.png create mode 100644 comp/src/gb.form.stock/gambas/32/archive.png create mode 100644 comp/src/gb.form.stock/gambas/32/attach.png create mode 100644 comp/src/gb.form.stock/gambas/32/audio.png create mode 100644 comp/src/gb.form.stock/gambas/32/average.png create mode 100644 comp/src/gb.form.stock/gambas/32/battery-high.png create mode 100644 comp/src/gb.form.stock/gambas/32/battery-low.png create mode 100644 comp/src/gb.form.stock/gambas/32/battery-medium.png create mode 100644 comp/src/gb.form.stock/gambas/32/battery.png create mode 100644 comp/src/gb.form.stock/gambas/32/bicycle.png create mode 100644 comp/src/gb.form.stock/gambas/32/blue.png create mode 100644 comp/src/gb.form.stock/gambas/32/bluetooth-off.png create mode 100644 comp/src/gb.form.stock/gambas/32/bluetooth.png create mode 100644 comp/src/gb.form.stock/gambas/32/blur.png create mode 100644 comp/src/gb.form.stock/gambas/32/book.png create mode 100644 comp/src/gb.form.stock/gambas/32/bookmark.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-bottom.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-diag.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-horizontal.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-inside.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-left.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-none.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-outside.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-rev-diag.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-right.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-top.png create mode 100644 comp/src/gb.form.stock/gambas/32/border-vertical.png create mode 100644 comp/src/gb.form.stock/gambas/32/border.png create mode 100644 comp/src/gb.form.stock/gambas/32/bottom.png create mode 100644 comp/src/gb.form.stock/gambas/32/brightness.png create mode 100644 comp/src/gb.form.stock/gambas/32/brush.png create mode 100644 comp/src/gb.form.stock/gambas/32/bus.png create mode 100644 comp/src/gb.form.stock/gambas/32/c.png create mode 100644 comp/src/gb.form.stock/gambas/32/cake.png create mode 100644 comp/src/gb.form.stock/gambas/32/calculator.png create mode 100644 comp/src/gb.form.stock/gambas/32/calendar.png create mode 100644 comp/src/gb.form.stock/gambas/32/call-stop.png create mode 100644 comp/src/gb.form.stock/gambas/32/call.png create mode 100644 comp/src/gb.form.stock/gambas/32/camera.png create mode 100644 comp/src/gb.form.stock/gambas/32/cancel.png create mode 100644 comp/src/gb.form.stock/gambas/32/cap-butt.png create mode 100644 comp/src/gb.form.stock/gambas/32/cap-round.png create mode 100644 comp/src/gb.form.stock/gambas/32/cap-square.png create mode 100644 comp/src/gb.form.stock/gambas/32/car.png create mode 100644 comp/src/gb.form.stock/gambas/32/cdrom.png create mode 100644 comp/src/gb.form.stock/gambas/32/chart-line.png create mode 100644 comp/src/gb.form.stock/gambas/32/chart-pie.png create mode 100644 comp/src/gb.form.stock/gambas/32/chart.png create mode 100644 comp/src/gb.form.stock/gambas/32/chat.png create mode 100644 comp/src/gb.form.stock/gambas/32/cherry.png create mode 100644 comp/src/gb.form.stock/gambas/32/city.png create mode 100644 comp/src/gb.form.stock/gambas/32/clear.png create mode 100644 comp/src/gb.form.stock/gambas/32/clock.png create mode 100644 comp/src/gb.form.stock/gambas/32/clone.png create mode 100644 comp/src/gb.form.stock/gambas/32/close.png create mode 100644 comp/src/gb.form.stock/gambas/32/cloud.png create mode 100644 comp/src/gb.form.stock/gambas/32/color-picker.png create mode 100644 comp/src/gb.form.stock/gambas/32/color.png create mode 100644 comp/src/gb.form.stock/gambas/32/column-after.png create mode 100644 comp/src/gb.form.stock/gambas/32/column-before.png create mode 100644 comp/src/gb.form.stock/gambas/32/column-remove.png create mode 100644 comp/src/gb.form.stock/gambas/32/compass.png create mode 100644 comp/src/gb.form.stock/gambas/32/component.png create mode 100644 comp/src/gb.form.stock/gambas/32/compress.png create mode 100644 comp/src/gb.form.stock/gambas/32/computer.png create mode 100644 comp/src/gb.form.stock/gambas/32/conflict.png create mode 100644 comp/src/gb.form.stock/gambas/32/connect.png create mode 100644 comp/src/gb.form.stock/gambas/32/contrast.png create mode 100644 comp/src/gb.form.stock/gambas/32/cookie.png create mode 100644 comp/src/gb.form.stock/gambas/32/copy.png create mode 100644 comp/src/gb.form.stock/gambas/32/cpp.png create mode 100644 comp/src/gb.form.stock/gambas/32/cpu.png create mode 100644 comp/src/gb.form.stock/gambas/32/crop.png create mode 100644 comp/src/gb.form.stock/gambas/32/css.png create mode 100644 comp/src/gb.form.stock/gambas/32/csv.png create mode 100644 comp/src/gb.form.stock/gambas/32/currency.png create mode 100644 comp/src/gb.form.stock/gambas/32/cut.png create mode 100644 comp/src/gb.form.stock/gambas/32/database.png create mode 100644 comp/src/gb.form.stock/gambas/32/debug-cursor.png create mode 100644 comp/src/gb.form.stock/gambas/32/debug-into.png create mode 100644 comp/src/gb.form.stock/gambas/32/debug-out.png create mode 100644 comp/src/gb.form.stock/gambas/32/debug-over.png create mode 100644 comp/src/gb.form.stock/gambas/32/delete.png create mode 100644 comp/src/gb.form.stock/gambas/32/desktop.png create mode 100644 comp/src/gb.form.stock/gambas/32/development.png create mode 100644 comp/src/gb.form.stock/gambas/32/difference.png create mode 100644 comp/src/gb.form.stock/gambas/32/directory.png create mode 100644 comp/src/gb.form.stock/gambas/32/disconnect.png create mode 100644 comp/src/gb.form.stock/gambas/32/down.png create mode 100644 comp/src/gb.form.stock/gambas/32/download.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-arrow.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-circle.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-line.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-path.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-polygon.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-rectangle.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-round.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-star.png create mode 100644 comp/src/gb.form.stock/gambas/32/draw-text.png create mode 100644 comp/src/gb.form.stock/gambas/32/drink.png create mode 100644 comp/src/gb.form.stock/gambas/32/earth.png create mode 100644 comp/src/gb.form.stock/gambas/32/edit.png create mode 100644 comp/src/gb.form.stock/gambas/32/eject.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-cool.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-crying.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-happy.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-kiss.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-laugh.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-plain.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-raspberry.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-sad.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-surprise.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-uncertain.png create mode 100644 comp/src/gb.form.stock/gambas/32/emote-wink.png create mode 100644 comp/src/gb.form.stock/gambas/32/end-of-line.png create mode 100644 comp/src/gb.form.stock/gambas/32/end.png create mode 100644 comp/src/gb.form.stock/gambas/32/energy.png create mode 100644 comp/src/gb.form.stock/gambas/32/erase.png create mode 100644 comp/src/gb.form.stock/gambas/32/error.png create mode 100644 comp/src/gb.form.stock/gambas/32/exclusive.png create mode 100644 comp/src/gb.form.stock/gambas/32/exec.png create mode 100644 comp/src/gb.form.stock/gambas/32/export.png create mode 100644 comp/src/gb.form.stock/gambas/32/factory.png create mode 100644 comp/src/gb.form.stock/gambas/32/file-manager.png create mode 100644 comp/src/gb.form.stock/gambas/32/file.png create mode 100644 comp/src/gb.form.stock/gambas/32/fill.png create mode 100644 comp/src/gb.form.stock/gambas/32/filter.png create mode 100644 comp/src/gb.form.stock/gambas/32/find.png create mode 100644 comp/src/gb.form.stock/gambas/32/firewall.png create mode 100644 comp/src/gb.form.stock/gambas/32/first.png create mode 100644 comp/src/gb.form.stock/gambas/32/flag-blue.png create mode 100644 comp/src/gb.form.stock/gambas/32/flag-green.png create mode 100644 comp/src/gb.form.stock/gambas/32/flag-red.png create mode 100644 comp/src/gb.form.stock/gambas/32/flag-yellow.png create mode 100644 comp/src/gb.form.stock/gambas/32/flag.png create mode 100644 comp/src/gb.form.stock/gambas/32/flip-h.png create mode 100644 comp/src/gb.form.stock/gambas/32/flip-v.png create mode 100644 comp/src/gb.form.stock/gambas/32/floppy.png create mode 100644 comp/src/gb.form.stock/gambas/32/fog.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-blue.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-document.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-download.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-green.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-home.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-image.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-music.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-network.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-recent.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-red.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-remote.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-root.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-share.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-template.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-video.png create mode 100644 comp/src/gb.form.stock/gambas/32/folder-yellow.png create mode 100644 comp/src/gb.form.stock/gambas/32/font.png create mode 100644 comp/src/gb.form.stock/gambas/32/formula.png create mode 100644 comp/src/gb.form.stock/gambas/32/forward.png create mode 100644 comp/src/gb.form.stock/gambas/32/fullscreen.png create mode 100644 comp/src/gb.form.stock/gambas/32/gambas.png create mode 100644 comp/src/gb.form.stock/gambas/32/game.png create mode 100644 comp/src/gb.form.stock/gambas/32/gamma.png create mode 100644 comp/src/gb.form.stock/gambas/32/gnu.png create mode 100644 comp/src/gb.form.stock/gambas/32/gradient.png create mode 100644 comp/src/gb.form.stock/gambas/32/green.png create mode 100644 comp/src/gb.form.stock/gambas/32/grid.png create mode 100644 comp/src/gb.form.stock/gambas/32/group.png create mode 100644 comp/src/gb.form.stock/gambas/32/h.png create mode 100644 comp/src/gb.form.stock/gambas/32/halt.png create mode 100644 comp/src/gb.form.stock/gambas/32/harddisk-root.png create mode 100644 comp/src/gb.form.stock/gambas/32/harddisk.png create mode 100644 comp/src/gb.form.stock/gambas/32/hardware.png create mode 100644 comp/src/gb.form.stock/gambas/32/hatch.png create mode 100644 comp/src/gb.form.stock/gambas/32/headset.png create mode 100644 comp/src/gb.form.stock/gambas/32/help.png create mode 100644 comp/src/gb.form.stock/gambas/32/hibernate.png create mode 100644 comp/src/gb.form.stock/gambas/32/highlight.png create mode 100644 comp/src/gb.form.stock/gambas/32/home.png create mode 100644 comp/src/gb.form.stock/gambas/32/house.png create mode 100644 comp/src/gb.form.stock/gambas/32/html.png create mode 100644 comp/src/gb.form.stock/gambas/32/hue.png create mode 100644 comp/src/gb.form.stock/gambas/32/identity.png create mode 100644 comp/src/gb.form.stock/gambas/32/image.png create mode 100644 comp/src/gb.form.stock/gambas/32/import.png create mode 100644 comp/src/gb.form.stock/gambas/32/important.png create mode 100644 comp/src/gb.form.stock/gambas/32/indent.png create mode 100644 comp/src/gb.form.stock/gambas/32/info.png create mode 100644 comp/src/gb.form.stock/gambas/32/inheritance.png create mode 100644 comp/src/gb.form.stock/gambas/32/insert-image.png create mode 100644 comp/src/gb.form.stock/gambas/32/insert-link.png create mode 100644 comp/src/gb.form.stock/gambas/32/insert-table.png create mode 100644 comp/src/gb.form.stock/gambas/32/insert-text.png create mode 100644 comp/src/gb.form.stock/gambas/32/internet.png create mode 100644 comp/src/gb.form.stock/gambas/32/intersection.png create mode 100644 comp/src/gb.form.stock/gambas/32/invert.png create mode 100644 comp/src/gb.form.stock/gambas/32/java.png create mode 100644 comp/src/gb.form.stock/gambas/32/join-bevel.png create mode 100644 comp/src/gb.form.stock/gambas/32/join-miter.png create mode 100644 comp/src/gb.form.stock/gambas/32/join-round.png create mode 100644 comp/src/gb.form.stock/gambas/32/js.png create mode 100644 comp/src/gb.form.stock/gambas/32/json.png create mode 100644 comp/src/gb.form.stock/gambas/32/jump.png create mode 100644 comp/src/gb.form.stock/gambas/32/key.png create mode 100644 comp/src/gb.form.stock/gambas/32/keyboard.png create mode 100644 comp/src/gb.form.stock/gambas/32/lamp.png create mode 100644 comp/src/gb.form.stock/gambas/32/language.png create mode 100644 comp/src/gb.form.stock/gambas/32/last.png create mode 100644 comp/src/gb.form.stock/gambas/32/layer.png create mode 100644 comp/src/gb.form.stock/gambas/32/layout.png create mode 100644 comp/src/gb.form.stock/gambas/32/left.png create mode 100644 comp/src/gb.form.stock/gambas/32/library.png create mode 100644 comp/src/gb.form.stock/gambas/32/lightness.png create mode 100644 comp/src/gb.form.stock/gambas/32/line-style.png create mode 100644 comp/src/gb.form.stock/gambas/32/line-width.png create mode 100644 comp/src/gb.form.stock/gambas/32/link.png create mode 100644 comp/src/gb.form.stock/gambas/32/linux.png create mode 100644 comp/src/gb.form.stock/gambas/32/lock-screen.png create mode 100644 comp/src/gb.form.stock/gambas/32/lock.png create mode 100644 comp/src/gb.form.stock/gambas/32/logout.png create mode 100644 comp/src/gb.form.stock/gambas/32/love.png create mode 100644 comp/src/gb.form.stock/gambas/32/lower.png create mode 100644 comp/src/gb.form.stock/gambas/32/magnet.png create mode 100644 comp/src/gb.form.stock/gambas/32/mail.png create mode 100644 comp/src/gb.form.stock/gambas/32/make-all.png create mode 100644 comp/src/gb.form.stock/gambas/32/make.png create mode 100644 comp/src/gb.form.stock/gambas/32/map.png create mode 100644 comp/src/gb.form.stock/gambas/32/marker.png create mode 100644 comp/src/gb.form.stock/gambas/32/math.png create mode 100644 comp/src/gb.form.stock/gambas/32/md.png create mode 100644 comp/src/gb.form.stock/gambas/32/media-player.png create mode 100644 comp/src/gb.form.stock/gambas/32/memory.png create mode 100644 comp/src/gb.form.stock/gambas/32/menu.png create mode 100644 comp/src/gb.form.stock/gambas/32/message.png create mode 100644 comp/src/gb.form.stock/gambas/32/microphone-off.png create mode 100644 comp/src/gb.form.stock/gambas/32/microphone.png create mode 100644 comp/src/gb.form.stock/gambas/32/modem.png create mode 100644 comp/src/gb.form.stock/gambas/32/modified.png create mode 100644 comp/src/gb.form.stock/gambas/32/money.png create mode 100644 comp/src/gb.form.stock/gambas/32/monitor.png create mode 100644 comp/src/gb.form.stock/gambas/32/moon-cloud.png create mode 100644 comp/src/gb.form.stock/gambas/32/moon.png create mode 100644 comp/src/gb.form.stock/gambas/32/more-h.png create mode 100644 comp/src/gb.form.stock/gambas/32/more.png create mode 100644 comp/src/gb.form.stock/gambas/32/mount.png create mode 100644 comp/src/gb.form.stock/gambas/32/mouse.png create mode 100644 comp/src/gb.form.stock/gambas/32/move.png create mode 100644 comp/src/gb.form.stock/gambas/32/multimedia.png create mode 100644 comp/src/gb.form.stock/gambas/32/muted.png create mode 100644 comp/src/gb.form.stock/gambas/32/network.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-appointment.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-dir.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-mail.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-tab.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-tag.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-user.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-window.png create mode 100644 comp/src/gb.form.stock/gambas/32/new.png create mode 100644 comp/src/gb.form.stock/gambas/32/news.png create mode 100644 comp/src/gb.form.stock/gambas/32/next.png create mode 100644 comp/src/gb.form.stock/gambas/32/notification.png create mode 100644 comp/src/gb.form.stock/gambas/32/office-calc.png create mode 100644 comp/src/gb.form.stock/gambas/32/office-draw.png create mode 100644 comp/src/gb.form.stock/gambas/32/office-math.png create mode 100644 comp/src/gb.form.stock/gambas/32/office-presentation.png create mode 100644 comp/src/gb.form.stock/gambas/32/office.png create mode 100644 comp/src/gb.form.stock/gambas/32/ok.png create mode 100644 comp/src/gb.form.stock/gambas/32/opacity.png create mode 100644 comp/src/gb.form.stock/gambas/32/open-link.png create mode 100644 comp/src/gb.form.stock/gambas/32/open-recent.png create mode 100644 comp/src/gb.form.stock/gambas/32/open.png create mode 100644 comp/src/gb.form.stock/gambas/32/options.png create mode 100644 comp/src/gb.form.stock/gambas/32/package.png create mode 100644 comp/src/gb.form.stock/gambas/32/page-break.png create mode 100644 comp/src/gb.form.stock/gambas/32/page-two.png create mode 100644 comp/src/gb.form.stock/gambas/32/page.png create mode 100644 comp/src/gb.form.stock/gambas/32/paragraph.png create mode 100644 comp/src/gb.form.stock/gambas/32/paste-special.png create mode 100644 comp/src/gb.form.stock/gambas/32/paste.png create mode 100644 comp/src/gb.form.stock/gambas/32/pause.png create mode 100644 comp/src/gb.form.stock/gambas/32/pda.png create mode 100644 comp/src/gb.form.stock/gambas/32/pdf.png create mode 100644 comp/src/gb.form.stock/gambas/32/pen.png create mode 100644 comp/src/gb.form.stock/gambas/32/people.png create mode 100644 comp/src/gb.form.stock/gambas/32/percent.png create mode 100644 comp/src/gb.form.stock/gambas/32/phone.png create mode 100644 comp/src/gb.form.stock/gambas/32/pin.png create mode 100644 comp/src/gb.form.stock/gambas/32/plane.png create mode 100644 comp/src/gb.form.stock/gambas/32/play.png create mode 100644 comp/src/gb.form.stock/gambas/32/plugin.png create mode 100644 comp/src/gb.form.stock/gambas/32/pointer.png create mode 100644 comp/src/gb.form.stock/gambas/32/preview.png create mode 100644 comp/src/gb.form.stock/gambas/32/previous.png create mode 100644 comp/src/gb.form.stock/gambas/32/print.png create mode 100644 comp/src/gb.form.stock/gambas/32/printer.png create mode 100644 comp/src/gb.form.stock/gambas/32/program.png create mode 100644 comp/src/gb.form.stock/gambas/32/properties.png create mode 100644 comp/src/gb.form.stock/gambas/32/question.png create mode 100644 comp/src/gb.form.stock/gambas/32/quit.png create mode 100644 comp/src/gb.form.stock/gambas/32/quote.png create mode 100644 comp/src/gb.form.stock/gambas/32/rain.png create mode 100644 comp/src/gb.form.stock/gambas/32/raise.png create mode 100644 comp/src/gb.form.stock/gambas/32/record.png create mode 100644 comp/src/gb.form.stock/gambas/32/red.png create mode 100644 comp/src/gb.form.stock/gambas/32/redo.png create mode 100644 comp/src/gb.form.stock/gambas/32/refresh.png create mode 100644 comp/src/gb.form.stock/gambas/32/remove.png create mode 100644 comp/src/gb.form.stock/gambas/32/rename.png create mode 100644 comp/src/gb.form.stock/gambas/32/repeat.png create mode 100644 comp/src/gb.form.stock/gambas/32/replace.png create mode 100644 comp/src/gb.form.stock/gambas/32/reply-all.png create mode 100644 comp/src/gb.form.stock/gambas/32/reply.png create mode 100644 comp/src/gb.form.stock/gambas/32/resize.png create mode 100644 comp/src/gb.form.stock/gambas/32/restart.png create mode 100644 comp/src/gb.form.stock/gambas/32/restaurant.png create mode 100644 comp/src/gb.form.stock/gambas/32/revert.png create mode 100644 comp/src/gb.form.stock/gambas/32/rewind.png create mode 100644 comp/src/gb.form.stock/gambas/32/right.png create mode 100644 comp/src/gb.form.stock/gambas/32/rotate-left.png create mode 100644 comp/src/gb.form.stock/gambas/32/rotate-right.png create mode 100644 comp/src/gb.form.stock/gambas/32/rotate.png create mode 100644 comp/src/gb.form.stock/gambas/32/row-after.png create mode 100644 comp/src/gb.form.stock/gambas/32/row-before.png create mode 100644 comp/src/gb.form.stock/gambas/32/row-remove.png create mode 100644 comp/src/gb.form.stock/gambas/32/rss.png create mode 100644 comp/src/gb.form.stock/gambas/32/saturation.png create mode 100644 comp/src/gb.form.stock/gambas/32/save-as.png create mode 100644 comp/src/gb.form.stock/gambas/32/save.png create mode 100644 comp/src/gb.form.stock/gambas/32/scanner.png create mode 100644 comp/src/gb.form.stock/gambas/32/science.png create mode 100644 comp/src/gb.form.stock/gambas/32/screen.png create mode 100644 comp/src/gb.form.stock/gambas/32/script.png create mode 100644 comp/src/gb.form.stock/gambas/32/sdcard.png create mode 100644 comp/src/gb.form.stock/gambas/32/security-high.png create mode 100644 comp/src/gb.form.stock/gambas/32/security-low.png create mode 100644 comp/src/gb.form.stock/gambas/32/security-medium.png create mode 100644 comp/src/gb.form.stock/gambas/32/security.png create mode 100644 comp/src/gb.form.stock/gambas/32/select-all.png create mode 100644 comp/src/gb.form.stock/gambas/32/select.png create mode 100644 comp/src/gb.form.stock/gambas/32/server.png create mode 100644 comp/src/gb.form.stock/gambas/32/share.png create mode 100644 comp/src/gb.form.stock/gambas/32/shopping.png create mode 100644 comp/src/gb.form.stock/gambas/32/shortcut.png create mode 100644 comp/src/gb.form.stock/gambas/32/shuffle.png create mode 100644 comp/src/gb.form.stock/gambas/32/shutdown.png create mode 100644 comp/src/gb.form.stock/gambas/32/smartphone.png create mode 100644 comp/src/gb.form.stock/gambas/32/snow.png create mode 100644 comp/src/gb.form.stock/gambas/32/sort-ascent.png create mode 100644 comp/src/gb.form.stock/gambas/32/sort-descent.png create mode 100644 comp/src/gb.form.stock/gambas/32/speaker.png create mode 100644 comp/src/gb.form.stock/gambas/32/spell-check.png create mode 100644 comp/src/gb.form.stock/gambas/32/sport.png create mode 100644 comp/src/gb.form.stock/gambas/32/star.png create mode 100644 comp/src/gb.form.stock/gambas/32/start.png create mode 100644 comp/src/gb.form.stock/gambas/32/statistics.png create mode 100644 comp/src/gb.form.stock/gambas/32/stop.png create mode 100644 comp/src/gb.form.stock/gambas/32/storm.png create mode 100644 comp/src/gb.form.stock/gambas/32/subtitle.png create mode 100644 comp/src/gb.form.stock/gambas/32/sum.png create mode 100644 comp/src/gb.form.stock/gambas/32/sun-cloud.png create mode 100644 comp/src/gb.form.stock/gambas/32/sun.png create mode 100644 comp/src/gb.form.stock/gambas/32/suspend.png create mode 100644 comp/src/gb.form.stock/gambas/32/switch-user.png create mode 100644 comp/src/gb.form.stock/gambas/32/system.png create mode 100644 comp/src/gb.form.stock/gambas/32/tab.png create mode 100644 comp/src/gb.form.stock/gambas/32/table.png create mode 100644 comp/src/gb.form.stock/gambas/32/tablet.png create mode 100644 comp/src/gb.form.stock/gambas/32/tag.png create mode 100644 comp/src/gb.form.stock/gambas/32/taxi.png create mode 100644 comp/src/gb.form.stock/gambas/32/template.png create mode 100644 comp/src/gb.form.stock/gambas/32/terminal.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-baseline.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-bigger.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-bold.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-bottom.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-center.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-fill.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-italic.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-left.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-line-spacing.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-list-order-rtl.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-list-order.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-list.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-lower.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-middle.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-outline.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-right.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-shadow.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-smaller.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-strike.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-sub.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-super.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-top.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-underline.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-upper.png create mode 100644 comp/src/gb.form.stock/gambas/32/text.png create mode 100644 comp/src/gb.form.stock/gambas/32/theme.png create mode 100644 comp/src/gb.form.stock/gambas/32/today.png create mode 100644 comp/src/gb.form.stock/gambas/32/toilet.png create mode 100644 comp/src/gb.form.stock/gambas/32/tools.png create mode 100644 comp/src/gb.form.stock/gambas/32/top.png create mode 100644 comp/src/gb.form.stock/gambas/32/town.png create mode 100644 comp/src/gb.form.stock/gambas/32/traffic.png create mode 100644 comp/src/gb.form.stock/gambas/32/train.png create mode 100644 comp/src/gb.form.stock/gambas/32/transparency.png create mode 100644 comp/src/gb.form.stock/gambas/32/trash-full.png create mode 100644 comp/src/gb.form.stock/gambas/32/trash.png create mode 100644 comp/src/gb.form.stock/gambas/32/truck.png create mode 100644 comp/src/gb.form.stock/gambas/32/uncompress.png create mode 100644 comp/src/gb.form.stock/gambas/32/undo.png create mode 100644 comp/src/gb.form.stock/gambas/32/ungroup.png create mode 100644 comp/src/gb.form.stock/gambas/32/unindent.png create mode 100644 comp/src/gb.form.stock/gambas/32/union.png create mode 100644 comp/src/gb.form.stock/gambas/32/unlock.png create mode 100644 comp/src/gb.form.stock/gambas/32/unselect.png create mode 100644 comp/src/gb.form.stock/gambas/32/up.png create mode 100644 comp/src/gb.form.stock/gambas/32/update.png create mode 100644 comp/src/gb.form.stock/gambas/32/upload.png create mode 100644 comp/src/gb.form.stock/gambas/32/usb.png create mode 100644 comp/src/gb.form.stock/gambas/32/user-group.png create mode 100644 comp/src/gb.form.stock/gambas/32/user.png create mode 100644 comp/src/gb.form.stock/gambas/32/vector.png create mode 100644 comp/src/gb.form.stock/gambas/32/version-control.png create mode 100644 comp/src/gb.form.stock/gambas/32/video.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-compact.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-detail.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-icon.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-normal.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-preview.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-split-h.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-split-v.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-tree.png create mode 100644 comp/src/gb.form.stock/gambas/32/volume-high.png create mode 100644 comp/src/gb.form.stock/gambas/32/volume-low.png create mode 100644 comp/src/gb.form.stock/gambas/32/volume-medium.png create mode 100644 comp/src/gb.form.stock/gambas/32/volume.png create mode 100644 comp/src/gb.form.stock/gambas/32/vpn.png create mode 100644 comp/src/gb.form.stock/gambas/32/walk.png create mode 100644 comp/src/gb.form.stock/gambas/32/warning.png create mode 100644 comp/src/gb.form.stock/gambas/32/watch.png create mode 100644 comp/src/gb.form.stock/gambas/32/webcam.png create mode 100644 comp/src/gb.form.stock/gambas/32/wifi-high.png create mode 100644 comp/src/gb.form.stock/gambas/32/wifi-low.png create mode 100644 comp/src/gb.form.stock/gambas/32/wifi-medium.png create mode 100644 comp/src/gb.form.stock/gambas/32/wifi.png create mode 100644 comp/src/gb.form.stock/gambas/32/window.png create mode 100644 comp/src/gb.form.stock/gambas/32/windows.png create mode 100644 comp/src/gb.form.stock/gambas/32/wizard.png create mode 100644 comp/src/gb.form.stock/gambas/32/worship.png create mode 100644 comp/src/gb.form.stock/gambas/32/wrap.png create mode 100644 comp/src/gb.form.stock/gambas/32/xml.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-fit.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-in.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-normal-rtl.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-normal.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-out.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-width.png create mode 100644 comp/src/gb.form.stock/gambas/app/gambas3.png create mode 120000 comp/src/gb.form.stock/icon.map create mode 100644 comp/src/gb.form.stock/links create mode 100644 comp/src/gb.form.stock/reverse create mode 100644 comp/src/gb.form.terminal/.component create mode 100644 comp/src/gb.form.terminal/.directory create mode 100644 comp/src/gb.form.terminal/.hidden/Konsole keys README.txt create mode 100644 comp/src/gb.form.terminal/.hidden/Konsole keys.txt create mode 100644 comp/src/gb.form.terminal/.hidden/XTerm control sequences.txt create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/16colors.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/256colors.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/256colors2.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/88colors.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/88colors2.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/8colors.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/acolors.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/doublechars.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/dynamic.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/dynamic.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/dynamic2.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/fonts.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/paste64.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/query-color.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/query-fonts.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/resize.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/resize.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/tcapquery.pl create mode 100644 comp/src/gb.form.terminal/.hidden/XtermTests/title.sh create mode 100644 comp/src/gb.form.terminal/.hidden/XtermVT100 create mode 100644 comp/src/gb.form.terminal/.hidden/control/terminalview.png create mode 100644 comp/src/gb.form.terminal/.icon.png create mode 100644 comp/src/gb.form.terminal/.lang/fr.po create mode 100644 comp/src/gb.form.terminal/.lang/it.po create mode 100644 comp/src/gb.form.terminal/.lang/pt_BR.po create mode 100644 comp/src/gb.form.terminal/.lang/ru.po create mode 100644 comp/src/gb.form.terminal/.lang/zh.po create mode 100644 comp/src/gb.form.terminal/.project create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/CTerminalLine.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalAttr.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalFilter.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalFilter_VT100.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalLink.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalScreen.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalView.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/FOtherTest.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/FOtherTest.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/FTestTerminalView.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/FTestTerminalView.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/Form1.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/Form1.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/MTest.module create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/PipeTest.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/PipeTest.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/PipedTask.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/TelNetProtocol.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/VT100/Attr.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/VT100/Console.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/test/VT100/Main.module create mode 100644 comp/src/gb.form.terminal/brush_dark.png create mode 100644 comp/src/gb.form.terminal/brush_light.png create mode 100644 comp/src/gb.form.terminal/brush_medium.png create mode 100644 comp/src/gb.form/.component create mode 100644 comp/src/gb.form/.directory create mode 100644 comp/src/gb.form/.hidden/CHANGELOG create mode 100644 comp/src/gb.form/.hidden/FailedNewMaskBox.class create mode 100644 comp/src/gb.form/.hidden/control/buttonbox.png create mode 100644 comp/src/gb.form/.hidden/control/colorbutton.png create mode 100644 comp/src/gb.form/.hidden/control/colorchooser.png create mode 100644 comp/src/gb.form/.hidden/control/colorpalette.png create mode 100644 comp/src/gb.form/.hidden/control/datebox.png create mode 100644 comp/src/gb.form/.hidden/control/datechooser.png create mode 100644 comp/src/gb.form/.hidden/control/dirbox.png create mode 100644 comp/src/gb.form/.hidden/control/dirbrowser.png create mode 100644 comp/src/gb.form/.hidden/control/dirchooser.png create mode 100644 comp/src/gb.form/.hidden/control/dirview.png create mode 100644 comp/src/gb.form/.hidden/control/documentview.png create mode 100644 comp/src/gb.form/.hidden/control/expander.png create mode 100644 comp/src/gb.form/.hidden/control/filebox.png create mode 100644 comp/src/gb.form/.hidden/control/filechooser.png create mode 100644 comp/src/gb.form/.hidden/control/fileproperties.png create mode 100644 comp/src/gb.form/.hidden/control/fileview.png create mode 100644 comp/src/gb.form/.hidden/control/fontbox.png create mode 100644 comp/src/gb.form/.hidden/control/fontchooser.png create mode 100644 comp/src/gb.form/.hidden/control/iconpanel.png create mode 100644 comp/src/gb.form/.hidden/control/imageview.png create mode 100644 comp/src/gb.form/.hidden/control/lcdlabel.png create mode 100644 comp/src/gb.form/.hidden/control/listcontainer.png create mode 100644 comp/src/gb.form/.hidden/control/listeditor.png create mode 100644 comp/src/gb.form/.hidden/control/maskbox.png create mode 100644 comp/src/gb.form/.hidden/control/menubutton.png create mode 100644 comp/src/gb.form/.hidden/control/messageview.png create mode 100644 comp/src/gb.form/.hidden/control/sidepanel.png create mode 100644 comp/src/gb.form/.hidden/control/sliderbox.png create mode 100644 comp/src/gb.form/.hidden/control/spinbar.png create mode 100644 comp/src/gb.form/.hidden/control/spinner.png create mode 100644 comp/src/gb.form/.hidden/control/switchbutton.png create mode 100644 comp/src/gb.form/.hidden/control/tableview.png create mode 100644 comp/src/gb.form/.hidden/control/tabpanel.png create mode 100644 comp/src/gb.form/.hidden/control/timebox.png create mode 100644 comp/src/gb.form/.hidden/control/toolpanel.png create mode 100644 comp/src/gb.form/.hidden/control/urllabel.png create mode 100644 comp/src/gb.form/.hidden/control/valuebox.png create mode 100644 comp/src/gb.form/.hidden/control/wizard.png create mode 100644 comp/src/gb.form/.hidden/icon.png create mode 100644 comp/src/gb.form/.icon.png create mode 100644 comp/src/gb.form/.lang/ar.po create mode 100644 comp/src/gb.form/.lang/ca.po create mode 100644 comp/src/gb.form/.lang/cs.po create mode 100644 comp/src/gb.form/.lang/de.po create mode 100644 comp/src/gb.form/.lang/es.po create mode 100644 comp/src/gb.form/.lang/es_ES.po create mode 100644 comp/src/gb.form/.lang/fa.po create mode 100644 comp/src/gb.form/.lang/fr.po create mode 100644 comp/src/gb.form/.lang/it.po create mode 100644 comp/src/gb.form/.lang/ja.po create mode 100644 comp/src/gb.form/.lang/nl.po create mode 100644 comp/src/gb.form/.lang/pt_BR.po create mode 100644 comp/src/gb.form/.lang/ru.po create mode 100644 comp/src/gb.form/.lang/sv.po create mode 100644 comp/src/gb.form/.lang/zh.po create mode 100644 comp/src/gb.form/.lang/zh_TW.po create mode 100644 comp/src/gb.form/.project create mode 100644 comp/src/gb.form/.src/Balloon/Balloon.class create mode 100644 comp/src/gb.form/.src/Balloon/FBalloon.class create mode 100644 comp/src/gb.form/.src/Balloon/FBalloon.form create mode 100644 comp/src/gb.form/.src/Button/ButtonBox.class create mode 100644 comp/src/gb.form/.src/Button/MenuButton.class create mode 100644 comp/src/gb.form/.src/Button/SwitchButton.class create mode 100644 comp/src/gb.form/.src/Color/ColorButton.class create mode 100644 comp/src/gb.form/.src/Color/ColorChooser.class create mode 100644 comp/src/gb.form/.src/Color/ColorPalette.class create mode 100644 comp/src/gb.form/.src/Color/FColorChooser.class create mode 100644 comp/src/gb.form/.src/Color/FColorChooser.form create mode 100644 comp/src/gb.form/.src/Completion.class create mode 100644 comp/src/gb.form/.src/Date/DateBox.class create mode 100644 comp/src/gb.form/.src/Date/DateChooser.class create mode 100644 comp/src/gb.form/.src/Date/FCalendar.class create mode 100644 comp/src/gb.form/.src/Date/FCalendar.form create mode 100644 comp/src/gb.form/.src/Date/TimeBox.class create mode 100644 comp/src/gb.form/.src/Date/_DateChooser_Colors.class create mode 100644 comp/src/gb.form/.src/Date/_DateChooser_Data.class create mode 100644 comp/src/gb.form/.src/Date/_DateChooser_Date.class create mode 100644 comp/src/gb.form/.src/DocumentView/DocumentView.class create mode 100644 comp/src/gb.form/.src/DocumentView/_DocumentItem.class create mode 100644 comp/src/gb.form/.src/DocumentView/_DocumentLayout.class create mode 100644 comp/src/gb.form/.src/Expander/Expander.class create mode 100644 comp/src/gb.form/.src/Expander/FExpander.class create mode 100644 comp/src/gb.form/.src/Expander/FExpander.form create mode 100644 comp/src/gb.form/.src/FInputBox.class create mode 100644 comp/src/gb.form/.src/FInputBox.form create mode 100644 comp/src/gb.form/.src/File/Bookmark.class create mode 100644 comp/src/gb.form/.src/File/Bookmark/CBookmark.class create mode 100644 comp/src/gb.form/.src/File/Bookmark/CBookmarkList.class create mode 100644 comp/src/gb.form/.src/File/Bookmark/FEditBookmark.class create mode 100644 comp/src/gb.form/.src/File/Bookmark/FEditBookmark.form create mode 100644 comp/src/gb.form/.src/File/Bookmarks.class create mode 100644 comp/src/gb.form/.src/File/CTaskPreview.class create mode 100644 comp/src/gb.form/.src/File/Chooser/DirButton.class create mode 100644 comp/src/gb.form/.src/File/Chooser/DirChooser.class create mode 100644 comp/src/gb.form/.src/File/Chooser/FDirChooser.class create mode 100644 comp/src/gb.form/.src/File/Chooser/FDirChooser.form create mode 100644 comp/src/gb.form/.src/File/Chooser/FileChooser.class create mode 100644 comp/src/gb.form/.src/File/DirBox.class create mode 100644 comp/src/gb.form/.src/File/DirBrowser.class create mode 100644 comp/src/gb.form/.src/File/DirCache.class create mode 100644 comp/src/gb.form/.src/File/DirView.class create mode 100644 comp/src/gb.form/.src/File/FileBox.class create mode 100644 comp/src/gb.form/.src/File/FileView.class create mode 100644 comp/src/gb.form/.src/File/Properties/CTaskDirSize.class create mode 100644 comp/src/gb.form/.src/File/Properties/FFileProperties.class create mode 100644 comp/src/gb.form/.src/File/Properties/FFileProperties.form create mode 100644 comp/src/gb.form/.src/File/Properties/FileProperties.class create mode 100644 comp/src/gb.form/.src/File/Properties/_FilePropertiesData.class create mode 100644 comp/src/gb.form/.src/FileCompletion.class create mode 100644 comp/src/gb.form/.src/Font/FFontChooser.class create mode 100644 comp/src/gb.form/.src/Font/FFontChooser.form create mode 100644 comp/src/gb.form/.src/Font/FontBox.class create mode 100644 comp/src/gb.form/.src/Font/FontCacheTask.class create mode 100644 comp/src/gb.form/.src/Font/FontChooser.class create mode 100644 comp/src/gb.form/.src/Help.module create mode 100644 comp/src/gb.form/.src/IconPanel/IconPanel.class create mode 100644 comp/src/gb.form/.src/IconPanel/_IconPanelContainer.class create mode 100644 comp/src/gb.form/.src/ImageView/ImageView.class create mode 100644 comp/src/gb.form/.src/InputBox.class create mode 100644 comp/src/gb.form/.src/LCDLabel.class create mode 100644 comp/src/gb.form/.src/ListContainer.class create mode 100644 comp/src/gb.form/.src/ListEditor/FListEditor.class create mode 100644 comp/src/gb.form/.src/ListEditor/FListEditor.form create mode 100644 comp/src/gb.form/.src/ListEditor/ListEditor.class create mode 100644 comp/src/gb.form/.src/Main.module create mode 100644 comp/src/gb.form/.src/MaskBox.class create mode 100644 comp/src/gb.form/.src/MaskBox2.class create mode 100644 comp/src/gb.form/.src/Message/FMessage.class create mode 100644 comp/src/gb.form/.src/Message/FMessage.form create mode 100644 comp/src/gb.form/.src/Message/Message.module create mode 100644 comp/src/gb.form/.src/Message/MessageView.class create mode 100644 comp/src/gb.form/.src/MessageLabel.class create mode 100644 comp/src/gb.form/.src/MultiContainer/_MultiContainer.class create mode 100644 comp/src/gb.form/.src/MultiContainer/_MultiContainerTab.class create mode 100644 comp/src/gb.form/.src/SidePanel/FSidePanel.class create mode 100644 comp/src/gb.form/.src/SidePanel/FSidePanel.form create mode 100644 comp/src/gb.form/.src/SidePanel/SidePanel.class create mode 100644 comp/src/gb.form/.src/SliderBox.class create mode 100644 comp/src/gb.form/.src/SpinBar/SpinBar.class create mode 100644 comp/src/gb.form/.src/Spinner.class create mode 100644 comp/src/gb.form/.src/Stock.class create mode 100644 comp/src/gb.form/.src/TabPanel/TabPanel.class create mode 100644 comp/src/gb.form/.src/TabPanel/_TabPanelButton.class create mode 100644 comp/src/gb.form/.src/TabPanel/_TabPanelContainer.class create mode 100644 comp/src/gb.form/.src/TableView.class create mode 100644 comp/src/gb.form/.src/Test/FBugFileView.class create mode 100644 comp/src/gb.form/.src/Test/FBugFileView.form create mode 100644 comp/src/gb.form/.src/Test/FDocumentView.class create mode 100644 comp/src/gb.form/.src/Test/FDocumentView.form create mode 100644 comp/src/gb.form/.src/Test/FIconPanel.class create mode 100644 comp/src/gb.form/.src/Test/FIconPanel.form create mode 100644 comp/src/gb.form/.src/Test/FIconView.class create mode 100644 comp/src/gb.form/.src/Test/FIconView.form create mode 100644 comp/src/gb.form/.src/Test/FLCDLabel.class create mode 100644 comp/src/gb.form/.src/Test/FLCDLabel.form create mode 100644 comp/src/gb.form/.src/Test/FMain.class create mode 100644 comp/src/gb.form/.src/Test/FMain.form create mode 100644 comp/src/gb.form/.src/Test/FSpinBar.class create mode 100644 comp/src/gb.form/.src/Test/FSpinBar.form create mode 100644 comp/src/gb.form/.src/Test/FTestArrangement.class create mode 100644 comp/src/gb.form/.src/Test/FTestArrangement.form create mode 100644 comp/src/gb.form/.src/Test/FTestBalloon.class create mode 100644 comp/src/gb.form/.src/Test/FTestBalloon.form create mode 100644 comp/src/gb.form/.src/Test/FTestColorChooser.class create mode 100644 comp/src/gb.form/.src/Test/FTestColorChooser.form create mode 100644 comp/src/gb.form/.src/Test/FTestCompletion.class create mode 100644 comp/src/gb.form/.src/Test/FTestCompletion.form create mode 100644 comp/src/gb.form/.src/Test/FTestDateChooser.class create mode 100644 comp/src/gb.form/.src/Test/FTestDateChooser.form create mode 100644 comp/src/gb.form/.src/Test/FTestDirBrowser.class create mode 100644 comp/src/gb.form/.src/Test/FTestDirBrowser.form create mode 100644 comp/src/gb.form/.src/Test/FTestExpander.class create mode 100644 comp/src/gb.form/.src/Test/FTestExpander.form create mode 100644 comp/src/gb.form/.src/Test/FTestFileBox.class create mode 100644 comp/src/gb.form/.src/Test/FTestFileBox.form create mode 100644 comp/src/gb.form/.src/Test/FTestFileChooser.class create mode 100644 comp/src/gb.form/.src/Test/FTestFileChooser.form create mode 100644 comp/src/gb.form/.src/Test/FTestFileView.class create mode 100644 comp/src/gb.form/.src/Test/FTestFileView.form create mode 100644 comp/src/gb.form/.src/Test/FTestFontBox.class create mode 100644 comp/src/gb.form/.src/Test/FTestFontBox.form create mode 100644 comp/src/gb.form/.src/Test/FTestFontChooser.class create mode 100644 comp/src/gb.form/.src/Test/FTestFontChooser.form create mode 100644 comp/src/gb.form/.src/Test/FTestImageView.class create mode 100644 comp/src/gb.form/.src/Test/FTestImageView.form create mode 100644 comp/src/gb.form/.src/Test/FTestListEditor.class create mode 100644 comp/src/gb.form/.src/Test/FTestListEditor.form create mode 100644 comp/src/gb.form/.src/Test/FTestMaskBox.class create mode 100644 comp/src/gb.form/.src/Test/FTestMaskBox.form create mode 100644 comp/src/gb.form/.src/Test/FTestMenuButton.class create mode 100644 comp/src/gb.form/.src/Test/FTestMenuButton.form create mode 100644 comp/src/gb.form/.src/Test/FTestMessageView.class create mode 100644 comp/src/gb.form/.src/Test/FTestMessageView.form create mode 100644 comp/src/gb.form/.src/Test/FTestSidePanel.class create mode 100644 comp/src/gb.form/.src/Test/FTestSidePanel.form create mode 100644 comp/src/gb.form/.src/Test/FTestSpinner.class create mode 100644 comp/src/gb.form/.src/Test/FTestSpinner.form create mode 100644 comp/src/gb.form/.src/Test/FTestSwitchButton.class create mode 100644 comp/src/gb.form/.src/Test/FTestSwitchButton.form create mode 100644 comp/src/gb.form/.src/Test/FTestTabPanel.class create mode 100644 comp/src/gb.form/.src/Test/FTestTabPanel.form create mode 100644 comp/src/gb.form/.src/Test/FTestTableView.class create mode 100644 comp/src/gb.form/.src/Test/FTestTableView.form create mode 100644 comp/src/gb.form/.src/Test/FTestTimeBox.class create mode 100644 comp/src/gb.form/.src/Test/FTestTimeBox.form create mode 100644 comp/src/gb.form/.src/Test/FTestToolPanel.class create mode 100644 comp/src/gb.form/.src/Test/FTestToolPanel.form create mode 100644 comp/src/gb.form/.src/Test/FTestValueBox.class create mode 100644 comp/src/gb.form/.src/Test/FTestValueBox.form create mode 100644 comp/src/gb.form/.src/Test/FTestWizard.class create mode 100644 comp/src/gb.form/.src/Test/FTestWizard.form create mode 100644 comp/src/gb.form/.src/Test/FWiki.class create mode 100644 comp/src/gb.form/.src/Test/FWiki.form create mode 100644 comp/src/gb.form/.src/Test/Form1.class create mode 100644 comp/src/gb.form/.src/Test/Form1.form create mode 100644 comp/src/gb.form/.src/Test/Form2.class create mode 100644 comp/src/gb.form/.src/Test/Form2.form create mode 100644 comp/src/gb.form/.src/Test/Form3.class create mode 100644 comp/src/gb.form/.src/Test/Form3.form create mode 100644 comp/src/gb.form/.src/Test/Form4.class create mode 100644 comp/src/gb.form/.src/Test/Form4.form create mode 100644 comp/src/gb.form/.src/TestControl.class create mode 100644 comp/src/gb.form/.src/ToolPanel/FToolBar.class create mode 100644 comp/src/gb.form/.src/ToolPanel/FToolBar.form create mode 100644 comp/src/gb.form/.src/ToolPanel/ToolPanel.class create mode 100644 comp/src/gb.form/.src/ToolPanel/_ToolPanelContainer.class create mode 100644 comp/src/gb.form/.src/URLLabel.class create mode 100644 comp/src/gb.form/.src/ValueBox.class create mode 100644 comp/src/gb.form/.src/Wizard/FWizard.class create mode 100644 comp/src/gb.form/.src/Wizard/FWizard.form create mode 100644 comp/src/gb.form/.src/Wizard/Wizard.class create mode 100644 comp/src/gb.form/.src/Wizard/_WizardContainer.class create mode 100644 comp/src/gb.form/img/16/cross.png create mode 100644 comp/src/gb.form/img/32/filter-menu.png create mode 100644 comp/src/gb.form/img/32/filter.png create mode 100644 comp/src/gb.form/img/32/warning.png create mode 100644 comp/src/gb.form/img/8/new/side-bottom-void.png create mode 100644 comp/src/gb.form/img/8/new/side-bottom.png create mode 100644 comp/src/gb.form/img/8/new/side-left.png create mode 100644 comp/src/gb.form/img/8/new/side-right-void.png create mode 100644 comp/src/gb.form/img/8/new/side-right.png create mode 100644 comp/src/gb.form/img/8/new/side-top.png create mode 100644 comp/src/gb.form/img/8/side-bottom-void.png create mode 100644 comp/src/gb.form/img/8/side-bottom.png create mode 100644 comp/src/gb.form/img/8/side-left.png create mode 100644 comp/src/gb.form/img/8/side-right-void.png create mode 100644 comp/src/gb.form/img/8/side-right.png create mode 100644 comp/src/gb.form/img/8/side-top.png create mode 100644 comp/src/gb.form/img/colormap-snap.png create mode 100644 comp/src/gb.form/img/colormap.png create mode 100644 comp/src/gb.form/img/cross.png create mode 120000 comp/src/gb.form/img/emblem/lock.png create mode 100644 comp/src/gb.form/img/handle-h.png create mode 100644 comp/src/gb.form/img/handle-v.png create mode 100644 comp/src/gb.form/img/line.png create mode 100644 comp/src/gb.form/img/lock.png create mode 100644 comp/src/gb.form/img/page.png create mode 100644 comp/src/gb.form/img/round.png create mode 100644 comp/src/gb.form/img/select-dark.png create mode 100644 comp/src/gb.form/img/select.png create mode 100644 comp/src/gb.form/img/unknown.svg create mode 100644 comp/src/gb.form/img/valuemap-snap.png create mode 100644 comp/src/gb.form/img/valuemap.png create mode 100644 comp/src/gb.form/map/icon.map create mode 100644 comp/src/gb.form/stock/16/gambas.png create mode 100644 comp/src/gb.form/stock/16/gnu.png create mode 100644 comp/src/gb.form/stock/16/linux.png create mode 100644 comp/src/gb.form/stock/32/gambas.png create mode 100644 comp/src/gb.form/stock/32/gnu.png create mode 100644 comp/src/gb.form/stock/32/linux.png create mode 100644 comp/src/gb.form/stock/scalable/gambas.svg create mode 100644 comp/src/gb.form/stock/scalable/gnu.svg create mode 100644 comp/src/gb.form/stock/scalable/linux.svg create mode 100644 comp/src/gb.gui.base/.component create mode 100644 comp/src/gb.gui.base/.directory create mode 100644 comp/src/gb.gui.base/.hidden/window.png create mode 100644 comp/src/gb.gui.base/.icon.png create mode 100644 comp/src/gb.gui.base/.lang/de.po create mode 100644 comp/src/gb.gui.base/.lang/es.po create mode 100644 comp/src/gb.gui.base/.lang/fr.po create mode 100644 comp/src/gb.gui.base/.lang/it.po create mode 100644 comp/src/gb.gui.base/.lang/zh.po create mode 100644 comp/src/gb.gui.base/.project create mode 100644 comp/src/gb.gui.base/.src/Action.class create mode 100644 comp/src/gb.gui.base/.src/Border.class create mode 100644 comp/src/gb.gui.base/.src/Clipboard.class create mode 100644 comp/src/gb.gui.base/.src/ComboBox/ComboBox.class create mode 100644 comp/src/gb.gui.base/.src/ComboBox/FComboBoxPopup.class create mode 100644 comp/src/gb.gui.base/.src/ComboBox/FComboBoxPopup.form create mode 100644 comp/src/gb.gui.base/.src/ComboBox/_ComboBox_Item.class create mode 100644 comp/src/gb.gui.base/.src/ComboBox/_ComboBox_Selection.class create mode 100644 comp/src/gb.gui.base/.src/Desktop.class create mode 100644 comp/src/gb.gui.base/.src/Drag.class create mode 100644 comp/src/gb.gui.base/.src/Draw.module create mode 100644 comp/src/gb.gui.base/.src/Fill.class create mode 100644 comp/src/gb.gui.base/.src/Frame.class create mode 100644 comp/src/gb.gui.base/.src/GridView/GridView.class create mode 100644 comp/src/gb.gui.base/.src/GridView/GridViewSelection.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Cell.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Column.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Columns.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Data.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Row.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Rows.class create mode 100644 comp/src/gb.gui.base/.src/IconView/IconView.class create mode 100644 comp/src/gb.gui.base/.src/IconView/_IconView_Item.class create mode 100644 comp/src/gb.gui.base/.src/Label.class create mode 100644 comp/src/gb.gui.base/.src/Line.class create mode 100644 comp/src/gb.gui.base/.src/ListBox/ListBox.class create mode 100644 comp/src/gb.gui.base/.src/ListBox/_ListBox_Item.class create mode 100644 comp/src/gb.gui.base/.src/Main.module create mode 100644 comp/src/gb.gui.base/.src/Message/FMessage.class create mode 100644 comp/src/gb.gui.base/.src/Message/FMessage.form create mode 100644 comp/src/gb.gui.base/.src/Message/Message.class create mode 100644 comp/src/gb.gui.base/.src/MovieBox.class create mode 100644 comp/src/gb.gui.base/.src/Paint.class create mode 100644 comp/src/gb.gui.base/.src/Picture.class create mode 100644 comp/src/gb.gui.base/.src/PictureBox.class create mode 100644 comp/src/gb.gui.base/.src/ProgressBar.class create mode 100644 comp/src/gb.gui.base/.src/ScrollArea.class create mode 100644 comp/src/gb.gui.base/.src/ScrollView.class create mode 100644 comp/src/gb.gui.base/.src/Separator.class create mode 100644 comp/src/gb.gui.base/.src/Shortcut.class create mode 100644 comp/src/gb.gui.base/.src/SpinBox.class create mode 100644 comp/src/gb.gui.base/.src/Split/HSplit.class create mode 100644 comp/src/gb.gui.base/.src/Split/Splitter.class create mode 100644 comp/src/gb.gui.base/.src/Split/VSplit.class create mode 100644 comp/src/gb.gui.base/.src/Spring.class create mode 100644 comp/src/gb.gui.base/.src/Test/FBorder.class create mode 100644 comp/src/gb.gui.base/.src/Test/FBorder.form create mode 100644 comp/src/gb.gui.base/.src/Test/FListBox.class create mode 100644 comp/src/gb.gui.base/.src/Test/FListBox.form create mode 100644 comp/src/gb.gui.base/.src/Test/FMain.class create mode 100644 comp/src/gb.gui.base/.src/Test/FMain.form create mode 100644 comp/src/gb.gui.base/.src/Test/FPaint.class create mode 100644 comp/src/gb.gui.base/.src/Test/FPaint.form create mode 100644 comp/src/gb.gui.base/.src/Test/FPictureBox.class create mode 100644 comp/src/gb.gui.base/.src/Test/FPictureBox.form create mode 100644 comp/src/gb.gui.base/.src/Test/FRightToLeft.class create mode 100644 comp/src/gb.gui.base/.src/Test/FRightToLeft.form create mode 100644 comp/src/gb.gui.base/.src/Test/FScrollArea.class create mode 100644 comp/src/gb.gui.base/.src/Test/FScrollArea.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestClipping.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestClipping.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestCombo.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestCombo.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestFrame.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestFrame.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestGridView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestGridView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestIconView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestIconView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestLabel.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestLabel.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestListView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestListView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestMouseWheel.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestMouseWheel.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestMyCombo.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestMyCombo.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestProgressBar.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestProgressBar.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestSpinBox.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestSpinBox.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestSplit.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestSplit.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestTreeView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestTreeView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/CTaskPreview.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/DirCache.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/FTestFileView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/FTestFileView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/FileView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/Help.module create mode 100644 comp/src/gb.gui.base/.src/Test/TestMessage.module create mode 100644 comp/src/gb.gui.base/.src/TestCursor.class create mode 100644 comp/src/gb.gui.base/.src/TextLabel.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/ColumnView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/ListView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/RenameBox.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/TreeView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/TreeViewSelection.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/_ColumnView_Columns.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/_TreeView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/_TreeView_Item.class create mode 100644 comp/src/gb.gui.base/.src/_Draw_Clip.class create mode 100644 comp/src/gb.gui.base/.src/_Draw_Style.class create mode 100644 comp/src/gb.gui.base/.src/_Gui.class create mode 100644 comp/src/gb.gui.base/broken.svg create mode 120000 comp/src/gb.gui.base/message/delete.png create mode 120000 comp/src/gb.gui.base/message/error.png create mode 120000 comp/src/gb.gui.base/message/info.png create mode 120000 comp/src/gb.gui.base/message/question.png create mode 120000 comp/src/gb.gui.base/message/warning.png create mode 120000 comp/src/gb.gui.base/moviebox.png create mode 100644 comp/src/gb.gui.base/pattern/10.png create mode 100644 comp/src/gb.gui.base/pattern/11.png create mode 100644 comp/src/gb.gui.base/pattern/12.png create mode 100644 comp/src/gb.gui.base/pattern/13.png create mode 100644 comp/src/gb.gui.base/pattern/14.png create mode 100644 comp/src/gb.gui.base/pattern/2.png create mode 100644 comp/src/gb.gui.base/pattern/3.png create mode 100644 comp/src/gb.gui.base/pattern/4.png create mode 100644 comp/src/gb.gui.base/pattern/5.png create mode 100644 comp/src/gb.gui.base/pattern/6.png create mode 100644 comp/src/gb.gui.base/pattern/7.png create mode 100644 comp/src/gb.gui.base/pattern/8.png create mode 100644 comp/src/gb.gui.base/pattern/9.png create mode 120000 comp/src/gb.gui.base/picturebox.png create mode 100644 comp/src/gb.gui.base/spinbox/minus.png create mode 100644 comp/src/gb.gui.base/spinbox/plus.png create mode 100644 comp/src/gb.logging/.component create mode 100644 comp/src/gb.logging/.directory create mode 100644 comp/src/gb.logging/.icon.png create mode 100644 comp/src/gb.logging/.project create mode 100644 comp/src/gb.logging/.src/ComplexLogger.class create mode 100644 comp/src/gb.logging/.src/ConsoleHandler.class create mode 100644 comp/src/gb.logging/.src/FileHandler.class create mode 100644 comp/src/gb.logging/.src/Formatter.module create mode 100644 comp/src/gb.logging/.src/LogHandler.class create mode 100644 comp/src/gb.logging/.src/LogLevel.module create mode 100644 comp/src/gb.logging/.src/LogRotator.module create mode 100644 comp/src/gb.logging/.src/Logger.class create mode 100644 comp/src/gb.logging/.src/MTest.module create mode 100644 comp/src/gb.map/.component create mode 100644 comp/src/gb.map/.connection/Connection1.connection create mode 100644 comp/src/gb.map/.directory create mode 100644 comp/src/gb.map/.hidden/control/mapview.png create mode 100644 comp/src/gb.map/.icon.png create mode 100644 comp/src/gb.map/.project create mode 100644 comp/src/gb.map/.src/FCarto.class create mode 100644 comp/src/gb.map/.src/FCarto.form create mode 100644 comp/src/gb.map/.src/Map.class create mode 100644 comp/src/gb.map/.src/MapView.class create mode 100644 comp/src/gb.map/.src/Shapes/_MapShapeItem.class create mode 100644 comp/src/gb.map/.src/Shapes/_ShapeItem.class create mode 100644 comp/src/gb.map/.src/Sprite.class create mode 100644 comp/src/gb.map/.src/Tests/FMain.class create mode 100644 comp/src/gb.map/.src/Tests/FMain.form create mode 100644 comp/src/gb.map/.src/Tests/FTestWmts.class create mode 100644 comp/src/gb.map/.src/Tests/FTestWmts.form create mode 100644 comp/src/gb.map/.src/Tests/Form1.class create mode 100644 comp/src/gb.map/.src/Tests/Form1.form create mode 100644 comp/src/gb.map/.src/Tests/Form2.class create mode 100644 comp/src/gb.map/.src/Tests/Form2.form create mode 100644 comp/src/gb.map/.src/Tests/Form3.class create mode 100644 comp/src/gb.map/.src/Tests/Form3.form create mode 100644 comp/src/gb.map/.src/Tests/Form4.class create mode 100644 comp/src/gb.map/.src/Tests/Form4.form create mode 100644 comp/src/gb.map/.src/Tests/Form5.class create mode 100644 comp/src/gb.map/.src/Tests/Form5.form create mode 100644 comp/src/gb.map/.src/Tests/Form6.class create mode 100644 comp/src/gb.map/.src/Tests/Form6.form create mode 100644 comp/src/gb.map/.src/Tests/Form7.class create mode 100644 comp/src/gb.map/.src/Tests/Form7.form create mode 100644 comp/src/gb.map/.src/Tests/MMain.module create mode 100644 comp/src/gb.map/.src/Tests/_BalloonLayer.class create mode 100644 comp/src/gb.map/.src/Tools/Geo.module create mode 100644 comp/src/gb.map/.src/Tools/MyPaint.class create mode 100644 comp/src/gb.map/.src/Tools/Proj.class create mode 100644 comp/src/gb.map/.src/Tools/_MapProxy.class create mode 100644 comp/src/gb.map/.src/Types/MapBounds.class create mode 100644 comp/src/gb.map/.src/Types/MapPoint.class create mode 100644 comp/src/gb.map/.src/Types/TileSource.module create mode 100644 comp/src/gb.map/.src/Types/_Tile.class create mode 100644 comp/src/gb.map/.src/_MapLayer.class create mode 100644 comp/src/gb.map/.src/_MapShape.class create mode 100644 comp/src/gb.map/.src/_MapTile.class create mode 100644 comp/src/gb.map/.src/_ViewLayer.class create mode 100644 comp/src/gb.map/Text1 create mode 100644 comp/src/gb.map/bar.png create mode 100644 comp/src/gb.map/cursor.png create mode 100644 comp/src/gb.map/minus.png create mode 100644 comp/src/gb.map/plus.png create mode 100644 comp/src/gb.map/point.png create mode 100644 comp/src/gb.map/pointsparcelle create mode 100644 comp/src/gb.markdown/.component create mode 100644 comp/src/gb.markdown/.directory create mode 100644 comp/src/gb.markdown/.icon.png create mode 100644 comp/src/gb.markdown/.project create mode 100644 comp/src/gb.markdown/.src/MTest.module create mode 100644 comp/src/gb.markdown/.src/Markdown.class create mode 100644 comp/src/gb.markdown/.src/MarkdownLink.class create mode 100644 comp/src/gb.markdown/.src/Markup.module create mode 100644 comp/src/gb.markdown/.src/TMarkdown.test create mode 100644 comp/src/gb.markdown/.test create mode 100644 comp/src/gb.markdown/test.txt create mode 100644 comp/src/gb.markdown/test2.txt create mode 100644 comp/src/gb.markdown/test3.txt create mode 100644 comp/src/gb.media.form/.component create mode 100644 comp/src/gb.media.form/.directory create mode 100644 comp/src/gb.media.form/.hidden/control/mediaview.png create mode 100644 comp/src/gb.media.form/.icon.png create mode 100644 comp/src/gb.media.form/.project create mode 100644 comp/src/gb.media.form/.src/FMediaPlayer.class create mode 100644 comp/src/gb.media.form/.src/FMediaPlayer.form create mode 100644 comp/src/gb.media.form/.src/FTest.class create mode 100644 comp/src/gb.media.form/.src/FTest.form create mode 100644 comp/src/gb.media.form/.src/MediaView.class create mode 100644 comp/src/gb.memcached/.component create mode 100644 comp/src/gb.memcached/.directory create mode 100644 comp/src/gb.memcached/.icon.png create mode 100644 comp/src/gb.memcached/.project create mode 100644 comp/src/gb.memcached/.src/FMain.class create mode 100644 comp/src/gb.memcached/.src/FMain.form create mode 100644 comp/src/gb.memcached/.src/Main.module create mode 100644 comp/src/gb.memcached/.src/Memcached.class create mode 100644 comp/src/gb.memcached/.src/_Memcached_Key.class create mode 100644 comp/src/gb.mysql/.component create mode 100644 comp/src/gb.mysql/.directory create mode 100644 comp/src/gb.mysql/.icon.png create mode 100644 comp/src/gb.mysql/.lang/es.po create mode 100644 comp/src/gb.mysql/.project create mode 100644 comp/src/gb.mysql/.src/Connection.class create mode 100644 comp/src/gb.mysql/.src/DB.class create mode 100644 comp/src/gb.mysql/.src/_DataBase.class create mode 100644 comp/src/gb.mysql/.src/_DataTypes.class create mode 100644 comp/src/gb.mysql/.src/_Event.class create mode 100644 comp/src/gb.mysql/.src/_Field.class create mode 100644 comp/src/gb.mysql/.src/_FieldEspecifications.class create mode 100644 comp/src/gb.mysql/.src/_Index.class create mode 100644 comp/src/gb.mysql/.src/_MySQL.class create mode 100644 comp/src/gb.mysql/.src/_Result.class create mode 100644 comp/src/gb.mysql/.src/_Routines.class create mode 100644 comp/src/gb.mysql/.src/_Table.class create mode 100644 comp/src/gb.mysql/.src/_TableMaintenance.class create mode 100644 comp/src/gb.mysql/.src/_Trigger.class create mode 100644 comp/src/gb.mysql/.src/_User.class create mode 100644 comp/src/gb.mysql/.src/_Version.class create mode 100644 comp/src/gb.mysql/.src/_View.class create mode 100644 comp/src/gb.mysql/.src/modMain.module create mode 100644 comp/src/gb.mysql/logo.png create mode 100644 comp/src/gb.net.pop3/.component create mode 100644 comp/src/gb.net.pop3/.directory create mode 100644 comp/src/gb.net.pop3/.hidden/control/pop3client.png create mode 100644 comp/src/gb.net.pop3/.icon.png create mode 100644 comp/src/gb.net.pop3/.lang/cs.po create mode 100644 comp/src/gb.net.pop3/.lang/es.po create mode 100644 comp/src/gb.net.pop3/.lang/es_ES.po create mode 100644 comp/src/gb.net.pop3/.lang/it.po create mode 100644 comp/src/gb.net.pop3/.lang/nl.po create mode 100644 comp/src/gb.net.pop3/.lang/ru.po create mode 100644 comp/src/gb.net.pop3/.lang/zh.po create mode 100644 comp/src/gb.net.pop3/.project create mode 100644 comp/src/gb.net.pop3/.src/MTest.module create mode 100644 comp/src/gb.net.pop3/.src/Net.class create mode 100644 comp/src/gb.net.pop3/.src/POPClient.class create mode 100644 comp/src/gb.net.pop3/.src/Pop3Client.class create mode 100644 comp/src/gb.net.pop3/.src/SSLClient.class create mode 100644 comp/src/gb.net.pop3/.src/TCPClient.class create mode 100644 comp/src/gb.net.pop3/.src/_Pop3Client_Message.class create mode 100644 comp/src/gb.net.smtp/.component create mode 100644 comp/src/gb.net.smtp/.directory create mode 100644 comp/src/gb.net.smtp/.hidden/control/smtpclient.png create mode 100644 comp/src/gb.net.smtp/.icon.png create mode 100644 comp/src/gb.net.smtp/.project create mode 100644 comp/src/gb.net.smtp/.src/Encode.module create mode 100644 comp/src/gb.net.smtp/.src/Main.module create mode 100644 comp/src/gb.net.smtp/.src/Net.class create mode 100644 comp/src/gb.net.smtp/.src/SmtpClient.class create mode 100644 comp/src/gb.net.smtp/.src/SmtpPart.class create mode 100644 comp/src/gb.net.smtp/.src/SmtpSession.class create mode 100644 comp/src/gb.net.smtp/.src/SslSession.class create mode 100644 comp/src/gb.net.smtp/.src/TcpSession.class create mode 100644 comp/src/gb.net.smtp/.src/TlsSession.class create mode 100644 comp/src/gb.report/.component create mode 100644 comp/src/gb.report/.connection/Connection1.connection create mode 100644 comp/src/gb.report/.connection/Connection2.connection create mode 100644 comp/src/gb.report/.connection/MainConn.connection create mode 100644 comp/src/gb.report/.dir_icon.png create mode 100644 comp/src/gb.report/.directory create mode 100644 comp/src/gb.report/.hidden/control/reportdrawingarea.png create mode 100644 comp/src/gb.report/.hidden/control/reportgridview.png create mode 100644 comp/src/gb.report/.hidden/control/reporthbox.png create mode 100644 comp/src/gb.report/.hidden/control/reportimage.png create mode 100644 comp/src/gb.report/.hidden/control/reportlabel.png create mode 100644 comp/src/gb.report/.hidden/control/reportline.png create mode 100644 comp/src/gb.report/.hidden/control/reportpagebreak.png create mode 100644 comp/src/gb.report/.hidden/control/reportpanel.png create mode 100644 comp/src/gb.report/.hidden/control/reportsvgimage.png create mode 100644 comp/src/gb.report/.hidden/control/reporttextlabel.png create mode 100644 comp/src/gb.report/.hidden/control/reportvbox.png create mode 100644 comp/src/gb.report/.hidden/control/reportview.png create mode 100644 comp/src/gb.report/.hidden/control/reportvpanel.png create mode 100644 comp/src/gb.report/.icon.png create mode 100644 comp/src/gb.report/.lang/ca.po create mode 100644 comp/src/gb.report/.lang/cs.po create mode 100644 comp/src/gb.report/.lang/es.po create mode 100644 comp/src/gb.report/.lang/es_ES.po create mode 100644 comp/src/gb.report/.lang/fr.po create mode 100644 comp/src/gb.report/.lang/nl.po create mode 100644 comp/src/gb.report/.lang/ru.po create mode 100644 comp/src/gb.report/.lang/zh.po create mode 100644 comp/src/gb.report/.project create mode 100644 comp/src/gb.report/.src/Borders/ReportBorder.class create mode 100644 comp/src/gb.report/.src/Borders/_ReportBorderSide.class create mode 100644 comp/src/gb.report/.src/Borders/_ReportRoundCorner.class create mode 100644 comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class create mode 100644 comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form create mode 100644 comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class create mode 100644 comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class create mode 100644 comp/src/gb.report/.src/Brush/ReportBrush.class create mode 100644 comp/src/gb.report/.src/Controls/ReportControl.class create mode 100644 comp/src/gb.report/.src/MainTools/MReport.module create mode 100644 comp/src/gb.report/.src/MainTools/ReportUnits.module create mode 100644 comp/src/gb.report/.src/MainTools/Types/TControl.class create mode 100644 comp/src/gb.report/.src/MainTools/Types/TPageColumn.class create mode 100644 comp/src/gb.report/.src/MainTools/Types/TSizeHint.class create mode 100644 comp/src/gb.report/.src/MainTools/Types/TSizeParse.class create mode 100644 comp/src/gb.report/.src/Optional/Align.class create mode 100644 comp/src/gb.report/.src/Optional/Arrange.class create mode 100644 comp/src/gb.report/.src/Optional/Line.class create mode 100644 comp/src/gb.report/.src/Padding/ReportPadding.class create mode 100644 comp/src/gb.report/.src/Preview/CPrint.class create mode 100644 comp/src/gb.report/.src/Preview/FOptions.class create mode 100644 comp/src/gb.report/.src/Preview/FOptions.form create mode 100644 comp/src/gb.report/.src/Preview/FPreview.class create mode 100644 comp/src/gb.report/.src/Preview/FPreview.form create mode 100644 comp/src/gb.report/.src/Preview/FPrint.class create mode 100644 comp/src/gb.report/.src/Preview/FPrint.form create mode 100644 comp/src/gb.report/.src/Preview/Form1.class create mode 100644 comp/src/gb.report/.src/Preview/Form1.form create mode 100644 comp/src/gb.report/.src/Preview/ReportView.class create mode 100644 comp/src/gb.report/.src/Preview/ReportViewTask.class create mode 100644 comp/src/gb.report/.src/Report.class create mode 100644 comp/src/gb.report/.src/ReportContainer.class create mode 100644 comp/src/gb.report/.src/ReportDrawingArea.class create mode 100644 comp/src/gb.report/.src/ReportFrame.class create mode 100644 comp/src/gb.report/.src/ReportGridView.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class create mode 100644 comp/src/gb.report/.src/ReportHBox.class create mode 100644 comp/src/gb.report/.src/ReportImage.class create mode 100644 comp/src/gb.report/.src/ReportLabel.class create mode 100644 comp/src/gb.report/.src/ReportLine.class create mode 100644 comp/src/gb.report/.src/ReportPageBreak.class create mode 100644 comp/src/gb.report/.src/ReportPanel.class create mode 100644 comp/src/gb.report/.src/ReportSection.class create mode 100644 comp/src/gb.report/.src/ReportSvgImage.class create mode 100644 comp/src/gb.report/.src/ReportTextLabel.class create mode 100644 comp/src/gb.report/.src/ReportVBox.class create mode 100644 comp/src/gb.report/.src/ReportVPanel.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report1.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report1.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report10.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report10.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report12.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report12.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report13.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report13.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report14.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report14.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report2.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report2.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report3.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report3.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report4.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report4.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report5.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report5.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report6.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report6.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report7.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report7.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report8.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report8.report create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport1.class create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport1.report create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport2.class create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport2.report create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport5.class create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport5.report create mode 100644 comp/src/gb.report/.src/Tests/OutputReport.class create mode 100644 comp/src/gb.report/.src/Tests/OutputReport.report create mode 100644 comp/src/gb.report/.src/Tests/OutputReport2.class create mode 100644 comp/src/gb.report/.src/Tests/OutputReport2.report create mode 100644 comp/src/gb.report/.src/Tests/Paints.class create mode 100644 comp/src/gb.report/.src/Tests/Report11.class create mode 100644 comp/src/gb.report/.src/Tests/Report11.report create mode 100644 comp/src/gb.report/.src/Tests/Report41.class create mode 100644 comp/src/gb.report/.src/Tests/Report41.report create mode 100644 comp/src/gb.report/.src/Tests/Report51.class create mode 100644 comp/src/gb.report/.src/Tests/Report51.report create mode 100644 comp/src/gb.report/.src/Tests/Report52.class create mode 100644 comp/src/gb.report/.src/Tests/Report52.report create mode 100644 comp/src/gb.report/.src/Tests/Report9.class create mode 100644 comp/src/gb.report/.src/Tests/Report9.report create mode 100644 comp/src/gb.report/.src/Tests/Test.module create mode 100644 comp/src/gb.report/.src/Tests/rpTestShadowGrid.class create mode 100644 comp/src/gb.report/.src/Tests/rpTestShadowGrid.report create mode 100644 comp/src/gb.report/ChangeLog create mode 100644 comp/src/gb.report/gambas.svg create mode 100644 comp/src/gb.report/img/16/red-arrow-h.png create mode 100644 comp/src/gb.report/img/16/red-arrow-v.png create mode 100644 comp/src/gb.report/img/22/FullWidth.png create mode 100644 comp/src/gb.report/img/22/OnePage.png create mode 100644 comp/src/gb.report/img/22/RealSize.png create mode 100644 comp/src/gb.report/img/22/TwoPage.png create mode 100644 comp/src/gb.report/img/32/Collatecopie.png create mode 100644 comp/src/gb.report/img/32/Empty.png create mode 100644 comp/src/gb.report/img/32/grayscale.png create mode 100644 comp/src/gb.report/img/32/reverse.png create mode 100644 comp/src/gb.report/img/control/hbox.png create mode 100644 comp/src/gb.report/img/control/label.png create mode 100644 comp/src/gb.report/img/control/picturebox.png create mode 100644 comp/src/gb.report/img/control/vbox.png create mode 100644 comp/src/gb.report/img/control/vpanel.png create mode 100644 comp/src/gb.report/img/logo.svg create mode 100644 comp/src/gb.report/printer1.png create mode 100644 comp/src/gb.report2/.component create mode 100644 comp/src/gb.report2/.connection/Connection1.connection create mode 100644 comp/src/gb.report2/.connection/Connection2.connection create mode 100644 comp/src/gb.report2/.connection/Connection2.template create mode 100644 comp/src/gb.report2/.directory create mode 120000 comp/src/gb.report2/.hidden/control/reportdrawingarea.png create mode 120000 comp/src/gb.report2/.hidden/control/reportgridview.png create mode 120000 comp/src/gb.report2/.hidden/control/reporthbox.png create mode 120000 comp/src/gb.report2/.hidden/control/reportimage.png create mode 120000 comp/src/gb.report2/.hidden/control/reportlabel.png create mode 100644 comp/src/gb.report2/.hidden/control/reportline.png create mode 100644 comp/src/gb.report2/.hidden/control/reportpagebreak.png create mode 120000 comp/src/gb.report2/.hidden/control/reportpanel.png create mode 100644 comp/src/gb.report2/.hidden/control/reportsvgimage.png create mode 120000 comp/src/gb.report2/.hidden/control/reporttextlabel.png create mode 120000 comp/src/gb.report2/.hidden/control/reportvbox.png create mode 120000 comp/src/gb.report2/.hidden/control/reportview.png create mode 120000 comp/src/gb.report2/.hidden/control/reportvpanel.png create mode 100644 comp/src/gb.report2/.icon.png create mode 100644 comp/src/gb.report2/.lang/de.po create mode 100644 comp/src/gb.report2/.lang/es.po create mode 100644 comp/src/gb.report2/.lang/es_ES.po create mode 100644 comp/src/gb.report2/.lang/fr.po create mode 100644 comp/src/gb.report2/.lang/it.po create mode 100644 comp/src/gb.report2/.lang/nl.po create mode 100644 comp/src/gb.report2/.lang/pt_BR.po create mode 100644 comp/src/gb.report2/.lang/ru.po create mode 100644 comp/src/gb.report2/.lang/zh.po create mode 100644 comp/src/gb.report2/.project create mode 100644 comp/src/gb.report2/.src/Evaluator/CResult.class create mode 100644 comp/src/gb.report2/.src/Evaluator/_RepExp.class create mode 100644 comp/src/gb.report2/.src/Optional/Align.class create mode 100644 comp/src/gb.report2/.src/Optional/Arrange.class create mode 100644 comp/src/gb.report2/.src/Optional/Line.class create mode 100644 comp/src/gb.report2/.src/Report.class create mode 100644 comp/src/gb.report2/.src/ReportContainer.class create mode 100644 comp/src/gb.report2/.src/ReportControl.class create mode 100644 comp/src/gb.report2/.src/ReportDrawingArea.class create mode 100644 comp/src/gb.report2/.src/ReportFrame.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/ReportGridView.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class create mode 100644 comp/src/gb.report2/.src/ReportHBox.class create mode 100644 comp/src/gb.report2/.src/ReportImage.class create mode 100644 comp/src/gb.report2/.src/ReportLabel.class create mode 100644 comp/src/gb.report2/.src/ReportLine.class create mode 100644 comp/src/gb.report2/.src/ReportPageBreak.class create mode 100644 comp/src/gb.report2/.src/ReportPanel.class create mode 100644 comp/src/gb.report2/.src/ReportSvgImage.class create mode 100644 comp/src/gb.report2/.src/ReportTextLabel.class create mode 100644 comp/src/gb.report2/.src/ReportVBox.class create mode 100644 comp/src/gb.report2/.src/ReportVPanel.class create mode 100644 comp/src/gb.report2/.src/Tests/Module2.module create mode 100644 comp/src/gb.report2/.src/Tests/Report10.class create mode 100644 comp/src/gb.report2/.src/Tests/Report10.report create mode 100644 comp/src/gb.report2/.src/Tests/Report13.class create mode 100644 comp/src/gb.report2/.src/Tests/Report13.report create mode 100644 comp/src/gb.report2/.src/Tests/Report14.class create mode 100644 comp/src/gb.report2/.src/Tests/Report14.report create mode 100644 comp/src/gb.report2/.src/Tests/Report15.class create mode 100644 comp/src/gb.report2/.src/Tests/Report15.report create mode 100644 comp/src/gb.report2/.src/Tests/Report16.class create mode 100644 comp/src/gb.report2/.src/Tests/Report16.report create mode 100644 comp/src/gb.report2/.src/Tests/Report17.class create mode 100644 comp/src/gb.report2/.src/Tests/Report17.report create mode 100644 comp/src/gb.report2/.src/Tests/old/FMain.class create mode 100644 comp/src/gb.report2/.src/Tests/old/FMain.form create mode 100644 comp/src/gb.report2/.src/Tests/old/Module1.module create mode 100644 comp/src/gb.report2/.src/Tests/old/OutputReport2.class create mode 100644 comp/src/gb.report2/.src/Tests/old/OutputReport2.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report1.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report1.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report11.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report11.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report12.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report12.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report2.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report2.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report3.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report3.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report4.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report4.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report5.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report5.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report6.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report6.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report7.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report7.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report8.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report8.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report9.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report9.report create mode 100644 comp/src/gb.report2/.src/Tools/CPrint.class create mode 100644 comp/src/gb.report2/.src/Tools/MUtil.module create mode 100644 comp/src/gb.report2/.src/Types/Base/ReportBrush.class create mode 100644 comp/src/gb.report2/.src/Types/Base/ReportMargin.class create mode 100644 comp/src/gb.report2/.src/Types/Base/ReportPadding.class create mode 100644 comp/src/gb.report2/.src/Types/Border/ReportBorder.class create mode 100644 comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class create mode 100644 comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class create mode 100644 comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class create mode 100644 comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class create mode 100644 comp/src/gb.report2/.src/Types/ReportSizeHints.class create mode 100644 comp/src/gb.report2/.src/Types/ReportSizeParser.class create mode 100644 comp/src/gb.report2/.src/Types/TControl.class create mode 100644 comp/src/gb.report2/.src/Types/TSizeParse.class create mode 100644 comp/src/gb.report2/.src/Types/_ReportVirtualControl.class create mode 100644 comp/src/gb.report2/.src/Viewer/FPreview.class create mode 100644 comp/src/gb.report2/.src/Viewer/FPreview.form create mode 100644 comp/src/gb.report2/.src/Viewer/ReportView.class create mode 100644 comp/src/gb.report2/.src/_ReportSection.class create mode 100644 comp/src/gb.report2/16/full-width.png create mode 100644 comp/src/gb.report2/16/one-page.png create mode 100644 comp/src/gb.report2/16/real-size.png create mode 100644 comp/src/gb.report2/16/red-arrow-h.png create mode 100644 comp/src/gb.report2/16/red-arrow-v.png create mode 100644 comp/src/gb.report2/16/two-pages.png create mode 100644 comp/src/gb.report2/22/FullWidth.png create mode 100644 comp/src/gb.report2/22/OnePage.png create mode 100644 comp/src/gb.report2/22/RealSize.png create mode 100644 comp/src/gb.report2/22/TwoPage.png create mode 100644 comp/src/gb.report2/32/Collatecopie.png create mode 100644 comp/src/gb.report2/32/Empty.png create mode 100644 comp/src/gb.report2/32/grayscale.png create mode 100644 comp/src/gb.report2/32/reverse.png create mode 100644 comp/src/gb.report2/FunctionsList create mode 100644 comp/src/gb.report2/Structure create mode 100644 comp/src/gb.report2/gambas.svg create mode 100644 comp/src/gb.report2/icon.png create mode 100644 comp/src/gb.report2/tmpJournal create mode 100644 comp/src/gb.report2/tortueface.gif create mode 100644 comp/src/gb.scanner/.component create mode 100644 comp/src/gb.scanner/.directory create mode 100644 comp/src/gb.scanner/.hidden/scanner.png create mode 100644 comp/src/gb.scanner/.icon.png create mode 100644 comp/src/gb.scanner/.project create mode 100644 comp/src/gb.scanner/.src/Demo/FScan.class create mode 100644 comp/src/gb.scanner/.src/Demo/FScan.form create mode 100644 comp/src/gb.scanner/.src/Demo/Form1.class create mode 100644 comp/src/gb.scanner/.src/Demo/Form1.form create mode 100644 comp/src/gb.scanner/.src/Demo/Main.module create mode 100644 comp/src/gb.scanner/.src/Demo/Main2.module create mode 100644 comp/src/gb.scanner/.src/MTest.module create mode 100644 comp/src/gb.scanner/.src/MTest2.module create mode 100644 comp/src/gb.scanner/.src/Module1.module create mode 100644 comp/src/gb.scanner/.src/Module2.module create mode 100644 comp/src/gb.scanner/.src/Scanner.class create mode 100644 comp/src/gb.scanner/.src/ScannerOption.class create mode 100644 comp/src/gb.scanner/.src/Scanners.class create mode 100644 comp/src/gb.scanner/next.png create mode 100644 comp/src/gb.settings/.component create mode 100644 comp/src/gb.settings/.directory create mode 100644 comp/src/gb.settings/.icon.png create mode 100644 comp/src/gb.settings/.project create mode 100644 comp/src/gb.settings/.src/Main.module create mode 100644 comp/src/gb.settings/.src/Settings.class create mode 100644 comp/src/gb.settings/.src/_Settings_Keys.class create mode 100644 comp/src/gb.term.form/.component create mode 100644 comp/src/gb.term.form/.directory create mode 100644 comp/src/gb.term.form/.hidden/CHANGELOG create mode 120000 comp/src/gb.term.form/.hidden/control/termbutton.png create mode 120000 comp/src/gb.term.form/.hidden/control/termcheckbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termframe.png create mode 120000 comp/src/gb.term.form/.hidden/control/termhbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termlabel.png create mode 120000 comp/src/gb.term.form/.hidden/control/termlistbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termpanel.png create mode 120000 comp/src/gb.term.form/.hidden/control/termpicturebox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termradiobutton.png create mode 120000 comp/src/gb.term.form/.hidden/control/termscrollbar.png create mode 120000 comp/src/gb.term.form/.hidden/control/termtextbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termvbox.png create mode 100644 comp/src/gb.term.form/.icon.png create mode 100644 comp/src/gb.term.form/.lang/it.po create mode 100644 comp/src/gb.term.form/.lang/nl.po create mode 100644 comp/src/gb.term.form/.lang/ru.po create mode 100644 comp/src/gb.term.form/.lang/zh.po create mode 100644 comp/src/gb.term.form/.project create mode 100644 comp/src/gb.term.form/.src/Align.class create mode 100644 comp/src/gb.term.form/.src/Arrange.class create mode 100644 comp/src/gb.term.form/.src/Attr.class create mode 100644 comp/src/gb.term.form/.src/Border.class create mode 100644 comp/src/gb.term.form/.src/Char.class create mode 100644 comp/src/gb.term.form/.src/Desktop.class create mode 100644 comp/src/gb.term.form/.src/Dialog/Message.class create mode 100644 comp/src/gb.term.form/.src/Key.class create mode 100644 comp/src/gb.term.form/.src/Mouse.class create mode 100644 comp/src/gb.term.form/.src/TermButton.class create mode 100644 comp/src/gb.term.form/.src/TermCheckBox.class create mode 100644 comp/src/gb.term.form/.src/TermColor.class create mode 100644 comp/src/gb.term.form/.src/TermContainer.class create mode 100644 comp/src/gb.term.form/.src/TermControl.class create mode 100644 comp/src/gb.term.form/.src/TermForm.class create mode 100644 comp/src/gb.term.form/.src/TermFrame.class create mode 100644 comp/src/gb.term.form/.src/TermHBox.class create mode 100644 comp/src/gb.term.form/.src/TermLabel.class create mode 100644 comp/src/gb.term.form/.src/TermListBox.class create mode 100644 comp/src/gb.term.form/.src/TermPanel.class create mode 100644 comp/src/gb.term.form/.src/TermPictureBox.class create mode 100644 comp/src/gb.term.form/.src/TermRadioButton.class create mode 100644 comp/src/gb.term.form/.src/TermScrollBar.class create mode 100644 comp/src/gb.term.form/.src/TermTextBox.class create mode 100644 comp/src/gb.term.form/.src/TermVBox.class create mode 100644 comp/src/gb.term.form/.src/TermWindow.class create mode 100644 comp/src/gb.term.form/.src/TermWindows.class create mode 100644 comp/src/gb.term.form/.src/Test/FTest2.class create mode 100644 comp/src/gb.term.form/.src/Test/Main.module create mode 100644 comp/src/gb.term.form/.src/Test/Main2.module create mode 100644 comp/src/gb.term.form/.src/Test/Main3.module create mode 100644 comp/src/gb.term.form/.src/Test/Module1.module create mode 100644 comp/src/gb.term.form/.src/Test/Termform1.class create mode 100644 comp/src/gb.term.form/.src/Test/Termform1.termform create mode 100644 comp/src/gb.term.form/.src/Test/Termform2.class create mode 100644 comp/src/gb.term.form/.src/Test/Termform2.termform create mode 100644 comp/src/gb.term.form/.src/Test/trfTest.class create mode 100644 comp/src/gb.term.form/all.png create mode 100644 comp/src/gb.util.web/.component create mode 100644 comp/src/gb.util.web/.directory create mode 100644 comp/src/gb.util.web/.hidden/control/ccontainer.png create mode 100644 comp/src/gb.util.web/.hidden/control/ccontrol.png create mode 100644 comp/src/gb.util.web/.icon.png create mode 100644 comp/src/gb.util.web/.project create mode 100644 comp/src/gb.util.web/.src/JS.class create mode 100644 comp/src/gb.util.web/.src/JSON.module create mode 100644 comp/src/gb.util.web/.src/JSONCollection.class create mode 100644 comp/src/gb.util.web/.src/MMain.module create mode 100644 comp/src/gb.util.web/.src/URL.class create mode 100644 comp/src/gb.util.web/.src/URLQuery.class create mode 100644 comp/src/gb.util.web/.src/XML.class create mode 100644 comp/src/gb.util.web/.src/XMLMarkup.class create mode 100644 comp/src/gb.util/.component create mode 100644 comp/src/gb.util/.directory create mode 100644 comp/src/gb.util/.icon.png create mode 100644 comp/src/gb.util/.lang/ar.po create mode 100644 comp/src/gb.util/.lang/ca.po create mode 100644 comp/src/gb.util/.lang/cs.po create mode 100644 comp/src/gb.util/.lang/cy.po create mode 100644 comp/src/gb.util/.lang/de.po create mode 100644 comp/src/gb.util/.lang/el.po create mode 100644 comp/src/gb.util/.lang/es.po create mode 100644 comp/src/gb.util/.lang/es_ES.po create mode 100644 comp/src/gb.util/.lang/fa.po create mode 100644 comp/src/gb.util/.lang/fr.po create mode 100644 comp/src/gb.util/.lang/gl_ES.po create mode 100644 comp/src/gb.util/.lang/hr.po create mode 100644 comp/src/gb.util/.lang/hu.po create mode 100644 comp/src/gb.util/.lang/id.po create mode 100644 comp/src/gb.util/.lang/it.po create mode 100644 comp/src/gb.util/.lang/ja.po create mode 100644 comp/src/gb.util/.lang/ko.po create mode 100644 comp/src/gb.util/.lang/lt.po create mode 100644 comp/src/gb.util/.lang/nl.po create mode 100644 comp/src/gb.util/.lang/no.po create mode 100644 comp/src/gb.util/.lang/pl.po create mode 100644 comp/src/gb.util/.lang/pt.po create mode 100644 comp/src/gb.util/.lang/pt_BR.po create mode 100644 comp/src/gb.util/.lang/ro.po create mode 100644 comp/src/gb.util/.lang/ru.po create mode 100644 comp/src/gb.util/.lang/sl.po create mode 100644 comp/src/gb.util/.lang/sv.po create mode 100644 comp/src/gb.util/.lang/tr.po create mode 100644 comp/src/gb.util/.lang/zh.po create mode 100644 comp/src/gb.util/.lang/zh_TW.po create mode 100644 comp/src/gb.util/.project create mode 100644 comp/src/gb.util/.src/Class.class create mode 100644 comp/src/gb.util/.src/ClassStat.class create mode 100644 comp/src/gb.util/.src/CsvFile.class create mode 100644 comp/src/gb.util/.src/Date.module create mode 100644 comp/src/gb.util/.src/File.class create mode 100644 comp/src/gb.util/.src/Language.class create mode 100644 comp/src/gb.util/.src/MMain.module create mode 100644 comp/src/gb.util/.src/MPhonetic_English.module create mode 100644 comp/src/gb.util/.src/MPhonetic_French.module create mode 100644 comp/src/gb.util/.src/Process.class create mode 100644 comp/src/gb.util/.src/ProcessExpect.class create mode 100644 comp/src/gb.util/.src/ProcessPrompt.class create mode 100644 comp/src/gb.util/.src/Shell.module create mode 100644 comp/src/gb.util/.src/String.class create mode 100644 comp/src/gb.web.feed/.component create mode 100644 comp/src/gb.web.feed/.directory create mode 100644 comp/src/gb.web.feed/.hidden/TODO create mode 100644 comp/src/gb.web.feed/.icon.png create mode 100644 comp/src/gb.web.feed/.lang/it.po create mode 100644 comp/src/gb.web.feed/.lang/nl.po create mode 100644 comp/src/gb.web.feed/.lang/ru.po create mode 100644 comp/src/gb.web.feed/.lang/zh.po create mode 100644 comp/src/gb.web.feed/.project create mode 100644 comp/src/gb.web.feed/.src/Main.module create mode 100644 comp/src/gb.web.feed/.src/Rss.class create mode 100644 comp/src/gb.web.feed/.src/RssCategory.class create mode 100644 comp/src/gb.web.feed/.src/RssCloud.class create mode 100644 comp/src/gb.web.feed/.src/RssDate.class create mode 100644 comp/src/gb.web.feed/.src/RssEnclosure.class create mode 100644 comp/src/gb.web.feed/.src/RssGuid.class create mode 100644 comp/src/gb.web.feed/.src/RssImage.class create mode 100644 comp/src/gb.web.feed/.src/RssItem.class create mode 100644 comp/src/gb.web.feed/.src/RssSource.class create mode 100644 comp/src/gb.web.feed/.src/RssTextInput.class create mode 100644 comp/src/gb.web.feed/Feed-icon.svg create mode 100644 comp/src/gb.web.feed/test.xml create mode 100644 comp/src/gb.web.form/.component create mode 100644 comp/src/gb.web.form/.directory create mode 100644 comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css create mode 100644 comp/src/gb.web.form/.hidden/Uncompressed/lib.js create mode 100644 comp/src/gb.web.form/.hidden/Uncompressed/style.css create mode 100644 comp/src/gb.web.form/.hidden/calendar.js create mode 120000 comp/src/gb.web.form/.hidden/control/webbutton.png create mode 120000 comp/src/gb.web.form/.hidden/control/webcheckbox.png create mode 120000 comp/src/gb.web.form/.hidden/control/webcombobox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webcontainer.png create mode 120000 comp/src/gb.web.form/.hidden/control/webdatebox.png create mode 120000 comp/src/gb.web.form/.hidden/control/webdatechooser.png create mode 120000 comp/src/gb.web.form/.hidden/control/webexpander.png create mode 120000 comp/src/gb.web.form/.hidden/control/webhbox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webhtml.png create mode 120000 comp/src/gb.web.form/.hidden/control/webimage.png create mode 120000 comp/src/gb.web.form/.hidden/control/weblabel.png create mode 120000 comp/src/gb.web.form/.hidden/control/weblistbox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webmenu.png create mode 100644 comp/src/gb.web.form/.hidden/control/webmenubar.png create mode 100644 comp/src/gb.web.form/.hidden/control/webmenuitem.png create mode 120000 comp/src/gb.web.form/.hidden/control/webprogressbar.png create mode 120000 comp/src/gb.web.form/.hidden/control/webradiobutton.png create mode 120000 comp/src/gb.web.form/.hidden/control/webscrollview.png create mode 120000 comp/src/gb.web.form/.hidden/control/webseparator.png create mode 120000 comp/src/gb.web.form/.hidden/control/webslider.png create mode 120000 comp/src/gb.web.form/.hidden/control/webspinbox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webtable.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtabpanel.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtextarea.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtextbox.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtimer.png create mode 100644 comp/src/gb.web.form/.hidden/control/webuploadarea.png create mode 100644 comp/src/gb.web.form/.hidden/control/webuploadbutton.png create mode 100644 comp/src/gb.web.form/.hidden/control/webuploader.png create mode 120000 comp/src/gb.web.form/.hidden/control/webvbox.png create mode 100644 comp/src/gb.web.form/.icon.png create mode 100644 comp/src/gb.web.form/.lang/fr.po create mode 100644 comp/src/gb.web.form/.lang/it.po create mode 100644 comp/src/gb.web.form/.lang/nl.po create mode 100644 comp/src/gb.web.form/.lang/pt_BR.po create mode 100644 comp/src/gb.web.form/.lang/ru.po create mode 100644 comp/src/gb.web.form/.lang/zh.po create mode 100644 comp/src/gb.web.form/.project create mode 100644 comp/src/gb.web.form/.public/favicon.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-down.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-left.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-right.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-up.png create mode 100644 comp/src/gb.web.form/.public/gw-close.png create mode 100644 comp/src/gb.web.form/.public/gw-max.png create mode 100644 comp/src/gb.web.form/.public/gw-table-more.gif create mode 100644 comp/src/gb.web.form/.public/gw-waiting.gif create mode 100644 comp/src/gb.web.form/.public/message/close.svg create mode 100644 comp/src/gb.web.form/.public/message/error.png create mode 100644 comp/src/gb.web.form/.public/message/info.png create mode 100644 comp/src/gb.web.form/.public/message/question.png create mode 100644 comp/src/gb.web.form/.public/message/warning.png create mode 100644 comp/src/gb.web.form/.public/new.png create mode 100644 comp/src/gb.web.form/.public/open.png create mode 100644 comp/src/gb.web.form/.src/Align.class create mode 100644 comp/src/gb.web.form/.src/Arrange.class create mode 100644 comp/src/gb.web.form/.src/Calendar/FCalendar.class create mode 100644 comp/src/gb.web.form/.src/Calendar/FCalendar.webform create mode 100644 comp/src/gb.web.form/.src/Calendar/WebDateBox.class create mode 100644 comp/src/gb.web.form/.src/Calendar/WebDateChooser.class create mode 100644 comp/src/gb.web.form/.src/Color.class create mode 100644 comp/src/gb.web.form/.src/Header.class create mode 100644 comp/src/gb.web.form/.src/Header.webpage create mode 100644 comp/src/gb.web.form/.src/Main.module create mode 100644 comp/src/gb.web.form/.src/Message/FMessage.class create mode 100644 comp/src/gb.web.form/.src/Message/FMessage.webform create mode 100644 comp/src/gb.web.form/.src/Message/Message.class create mode 100644 comp/src/gb.web.form/.src/Scroll.class create mode 100644 comp/src/gb.web.form/.src/Select.class create mode 100644 comp/src/gb.web.form/.src/Test/FHello.class create mode 100644 comp/src/gb.web.form/.src/Test/FHello.webform create mode 100644 comp/src/gb.web.form/.src/Test/FTestWebUploader.class create mode 100644 comp/src/gb.web.form/.src/Test/FTestWebUploader.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform1.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform1.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform2.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform2.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform3.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform3.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform4.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform4.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform5.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform5.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform6.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform6.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform7.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform7.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform8.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform8.webform create mode 100644 comp/src/gb.web.form/.src/WebButton.class create mode 100644 comp/src/gb.web.form/.src/WebCheckBox.class create mode 100644 comp/src/gb.web.form/.src/WebComboBox.class create mode 100644 comp/src/gb.web.form/.src/WebContainer.class create mode 100644 comp/src/gb.web.form/.src/WebControl.class create mode 100644 comp/src/gb.web.form/.src/WebControlStyle.class create mode 100644 comp/src/gb.web.form/.src/WebExpander.class create mode 100644 comp/src/gb.web.form/.src/WebForm.class create mode 100644 comp/src/gb.web.form/.src/WebHBox.class create mode 100644 comp/src/gb.web.form/.src/WebHtml.class create mode 100644 comp/src/gb.web.form/.src/WebImage.class create mode 100644 comp/src/gb.web.form/.src/WebLabel.class create mode 100644 comp/src/gb.web.form/.src/WebMenu.class create mode 100644 comp/src/gb.web.form/.src/WebMenuBar.class create mode 100644 comp/src/gb.web.form/.src/WebMenuItem.class create mode 100644 comp/src/gb.web.form/.src/WebProgressBar.class create mode 100644 comp/src/gb.web.form/.src/WebRadioButton.class create mode 100644 comp/src/gb.web.form/.src/WebScrollView.class create mode 100644 comp/src/gb.web.form/.src/WebSeparator.class create mode 100644 comp/src/gb.web.form/.src/WebSlider.class create mode 100644 comp/src/gb.web.form/.src/WebSpinBox.class create mode 100644 comp/src/gb.web.form/.src/WebTabPanel.class create mode 100644 comp/src/gb.web.form/.src/WebTable/WebTable.class create mode 100644 comp/src/gb.web.form/.src/WebTable/WebTableData.class create mode 100644 comp/src/gb.web.form/.src/WebTable/WebTableSelection.class create mode 100644 comp/src/gb.web.form/.src/WebTable/_WebTableColumn.class create mode 100644 comp/src/gb.web.form/.src/WebTable/_WebTableColumns.class create mode 100644 comp/src/gb.web.form/.src/WebTextArea.class create mode 100644 comp/src/gb.web.form/.src/WebTextBox.class create mode 100644 comp/src/gb.web.form/.src/WebTimer.class create mode 100644 comp/src/gb.web.form/.src/WebUploadArea.class create mode 100644 comp/src/gb.web.form/.src/WebUploadButton.class create mode 100644 comp/src/gb.web.form/.src/WebUploader.class create mode 100644 comp/src/gb.web.form/.src/WebVBox.class create mode 100644 comp/src/gb.web.form/.src/WebWindow.class create mode 100644 comp/src/gb.web.form/ac.js create mode 100644 comp/src/gb.web.form/arrow-down.png create mode 100644 comp/src/gb.web.form/arrow-right.png create mode 100644 comp/src/gb.web.form/clear.png create mode 100644 comp/src/gb.web.form/lib.js create mode 100644 comp/src/gb.web.form/shadow.png create mode 100644 comp/src/gb.web.form/style.css create mode 100644 comp/src/gb.web.gui/.component create mode 100644 comp/src/gb.web.gui/.directory create mode 100644 comp/src/gb.web.gui/.hidden/control/webaudio.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webbutton.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webcheckbox.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webcombobox.png create mode 100644 comp/src/gb.web.gui/.hidden/control/webcontainer.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webdatebox.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webdatechooser.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webdrawingarea.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webexpander.png create mode 100644 comp/src/gb.web.gui/.hidden/control/webfilebutton.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webhbox.png create mode 100644 comp/src/gb.web.gui/.hidden/control/webhtml.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webimage.png create mode 120000 comp/src/gb.web.gui/.hidden/control/weblabel.png create mode 120000 comp/src/gb.web.gui/.hidden/control/weblistbox.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webprogressbar.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webradiobutton.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webscrollview.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webseparator.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webslider.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webspinbox.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webtable.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webtabpanel.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webtextarea.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webtextbox.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webtimer.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webtree.png create mode 120000 comp/src/gb.web.gui/.hidden/control/webvbox.png create mode 100644 comp/src/gb.web.gui/.hidden/lib.js.copy create mode 120000 comp/src/gb.web.gui/.hidden/webform.png create mode 100644 comp/src/gb.web.gui/.icon.png create mode 100644 comp/src/gb.web.gui/.lang/it.po create mode 100644 comp/src/gb.web.gui/.lang/pt_BR.po create mode 100644 comp/src/gb.web.gui/.lang/zh.po create mode 100644 comp/src/gb.web.gui/.project create mode 100644 comp/src/gb.web.gui/.public/blip.ogg create mode 100644 comp/src/gb.web.gui/.public/favicon.png create mode 100644 comp/src/gb.web.gui/.public/gw-arrow-down.png create mode 100644 comp/src/gb.web.gui/.public/gw-arrow-left.png create mode 100644 comp/src/gb.web.gui/.public/gw-arrow-right.png create mode 100644 comp/src/gb.web.gui/.public/gw-arrow-up.png create mode 100644 comp/src/gb.web.gui/.public/gw-close.png create mode 100644 comp/src/gb.web.gui/.public/gw-table-more.gif create mode 100644 comp/src/gb.web.gui/.public/gw-waiting.gif create mode 100644 comp/src/gb.web.gui/.public/message/error.png create mode 100644 comp/src/gb.web.gui/.public/message/info.png create mode 100644 comp/src/gb.web.gui/.public/message/question.png create mode 100644 comp/src/gb.web.gui/.public/message/warning.png create mode 100644 comp/src/gb.web.gui/.src/Align.class create mode 100644 comp/src/gb.web.gui/.src/Application.class create mode 100644 comp/src/gb.web.gui/.src/Arrange.class create mode 100644 comp/src/gb.web.gui/.src/CSelection.class create mode 100644 comp/src/gb.web.gui/.src/Calendar/FCalendar.class create mode 100644 comp/src/gb.web.gui/.src/Calendar/FCalendar.webform create mode 100644 comp/src/gb.web.gui/.src/Calendar/WebDateBox.class create mode 100644 comp/src/gb.web.gui/.src/Calendar/WebDateChooser.class create mode 100644 comp/src/gb.web.gui/.src/Color.class create mode 100644 comp/src/gb.web.gui/.src/Header.class create mode 100644 comp/src/gb.web.gui/.src/Header.webpage create mode 100644 comp/src/gb.web.gui/.src/Key.class create mode 100644 comp/src/gb.web.gui/.src/Menu/FPopupMenu.class create mode 100644 comp/src/gb.web.gui/.src/Menu/FPopupMenu.webform create mode 100644 comp/src/gb.web.gui/.src/Menu/WebMenu.class create mode 100644 comp/src/gb.web.gui/.src/Menu/WebMenuBar.class create mode 100644 comp/src/gb.web.gui/.src/Message/FMessage.class create mode 100644 comp/src/gb.web.gui/.src/Message/FMessage.webform create mode 100644 comp/src/gb.web.gui/.src/Message/Message.class create mode 100644 comp/src/gb.web.gui/.src/Mouse.class create mode 100644 comp/src/gb.web.gui/.src/Paint/Gradient.class create mode 100644 comp/src/gb.web.gui/.src/Paint/Paint.class create mode 100644 comp/src/gb.web.gui/.src/Paint/PaintBrush.class create mode 100644 comp/src/gb.web.gui/.src/Paint/PaintDriver.class create mode 100644 comp/src/gb.web.gui/.src/Paint/PaintDriver_WebDrawingArea.class create mode 100644 comp/src/gb.web.gui/.src/Scroll.class create mode 100644 comp/src/gb.web.gui/.src/Select.class create mode 100644 comp/src/gb.web.gui/.src/Session.module create mode 100644 comp/src/gb.web.gui/.src/StartupError.class create mode 100644 comp/src/gb.web.gui/.src/StartupError.webpage create mode 100644 comp/src/gb.web.gui/.src/Table/WebTable.class create mode 100644 comp/src/gb.web.gui/.src/Table/WebTableData.class create mode 100644 comp/src/gb.web.gui/.src/Table/_WebTableColumn.class create mode 100644 comp/src/gb.web.gui/.src/Table/_WebTableColumns.class create mode 100644 comp/src/gb.web.gui/.src/Test/FInputBox.class create mode 100644 comp/src/gb.web.gui/.src/Test/FInputBox.webform create mode 100644 comp/src/gb.web.gui/.src/Test/FTestDialog.class create mode 100644 comp/src/gb.web.gui/.src/Test/FTestDialog.webform create mode 100644 comp/src/gb.web.gui/.src/Test/FTestDrawingArea.class create mode 100644 comp/src/gb.web.gui/.src/Test/FTestDrawingArea.webform create mode 100644 comp/src/gb.web.gui/.src/Test/FTestSlider.class create mode 100644 comp/src/gb.web.gui/.src/Test/FTestSlider.webform create mode 100644 comp/src/gb.web.gui/.src/Test/FTestWebAudio.class create mode 100644 comp/src/gb.web.gui/.src/Test/FTestWebAudio.webform create mode 100644 comp/src/gb.web.gui/.src/Test/Webform1.class create mode 100644 comp/src/gb.web.gui/.src/Test/Webform1.webform create mode 100644 comp/src/gb.web.gui/.src/Test/Webform2.class create mode 100644 comp/src/gb.web.gui/.src/Test/Webform2.webform create mode 100644 comp/src/gb.web.gui/.src/Test/Webform3.class create mode 100644 comp/src/gb.web.gui/.src/Test/Webform3.webform create mode 100644 comp/src/gb.web.gui/.src/Test/Webform4.class create mode 100644 comp/src/gb.web.gui/.src/Test/Webform4.webform create mode 100644 comp/src/gb.web.gui/.src/Tree/WebTree.class create mode 100644 comp/src/gb.web.gui/.src/Tree/WebTreeData.class create mode 100644 comp/src/gb.web.gui/.src/Tree/WebTreeSelection.class create mode 100644 comp/src/gb.web.gui/.src/Tree/_WebTreeColumn.class create mode 100644 comp/src/gb.web.gui/.src/Tree/_WebTreeColumns.class create mode 100644 comp/src/gb.web.gui/.src/Tree/_WebTreeItem.class create mode 100644 comp/src/gb.web.gui/.src/Upload.module create mode 100644 comp/src/gb.web.gui/.src/WebAudio.class create mode 100644 comp/src/gb.web.gui/.src/WebButton.class create mode 100644 comp/src/gb.web.gui/.src/WebCheckBox.class create mode 100644 comp/src/gb.web.gui/.src/WebComboBox.class create mode 100644 comp/src/gb.web.gui/.src/WebContainer.class create mode 100644 comp/src/gb.web.gui/.src/WebControl.class create mode 100644 comp/src/gb.web.gui/.src/WebControlStyle.class create mode 100644 comp/src/gb.web.gui/.src/WebDrawingArea.class create mode 100644 comp/src/gb.web.gui/.src/WebFileButton.class create mode 100644 comp/src/gb.web.gui/.src/WebForm.class create mode 100644 comp/src/gb.web.gui/.src/WebHBox.class create mode 100644 comp/src/gb.web.gui/.src/WebHtml.class create mode 100644 comp/src/gb.web.gui/.src/WebImage.class create mode 100644 comp/src/gb.web.gui/.src/WebLabel.class create mode 100644 comp/src/gb.web.gui/.src/WebListBox.class create mode 100644 comp/src/gb.web.gui/.src/WebProgressBar.class create mode 100644 comp/src/gb.web.gui/.src/WebRadioButton.class create mode 100644 comp/src/gb.web.gui/.src/WebScrollView.class create mode 100644 comp/src/gb.web.gui/.src/WebSeparator.class create mode 100644 comp/src/gb.web.gui/.src/WebSlider.class create mode 100644 comp/src/gb.web.gui/.src/WebSpinBox.class create mode 100644 comp/src/gb.web.gui/.src/WebTabPanel.class create mode 100644 comp/src/gb.web.gui/.src/WebTextArea.class create mode 100644 comp/src/gb.web.gui/.src/WebTextBox.class create mode 100644 comp/src/gb.web.gui/.src/WebTimer.class create mode 100644 comp/src/gb.web.gui/.src/WebVBox.class create mode 100644 comp/src/gb.web.gui/.src/WebWindow.class create mode 100644 comp/src/gb.web.gui/ac.js create mode 100644 comp/src/gb.web.gui/lib.js create mode 100644 comp/src/gb.web.gui/style.css create mode 100644 comp/src/gb.web/.component create mode 100644 comp/src/gb.web/.directory create mode 100644 comp/src/gb.web/.hidden/Session_opt create mode 100644 comp/src/gb.web/.icon.png create mode 100644 comp/src/gb.web/.project create mode 100644 comp/src/gb.web/.src/Application.module create mode 100644 comp/src/gb.web/.src/CGI.module create mode 100644 comp/src/gb.web/.src/FileSessionManager.class create mode 100644 comp/src/gb.web/.src/Main.module create mode 100644 comp/src/gb.web/.src/Request.module create mode 100644 comp/src/gb.web/.src/Response.module create mode 100644 comp/src/gb.web/.src/Session.module create mode 100644 comp/src/gb.web/.src/SessionManager.class create mode 100644 comp/src/gb.web/.src/SqliteSessionManager.class create mode 100644 comp/src/gb.web/.src/URL.class create mode 100644 comp/src/gb.web/.src/WebPage.class create mode 100644 comp/src/gb.web/.src/Webpage1.class create mode 100644 comp/src/gb.web/.src/Webpage1.webpage create mode 100644 comp/src/gb.web/.src/Webpage2.class create mode 100644 comp/src/gb.web/.src/Webpage2.webpage create mode 100644 comp/src/gb.web/.src/_Request_Get.class create mode 100644 comp/src/gb.web/.src/_Request_Post.class create mode 100644 comp/src/gb.web/.src/_ResponseCache.module create mode 100644 comp/src/gb.web/lang.cache create mode 100644 comp/src/order create mode 100644 component.am create mode 100644 configure.ac create mode 100644 gb.cairo/AUTHORS create mode 120000 gb.cairo/COPYING create mode 100644 gb.cairo/ChangeLog create mode 120000 gb.cairo/INSTALL create mode 100644 gb.cairo/Makefile.am create mode 100644 gb.cairo/NEWS create mode 100644 gb.cairo/README create mode 120000 gb.cairo/acinclude.m4 create mode 120000 gb.cairo/component.am create mode 100644 gb.cairo/configure.ac create mode 120000 gb.cairo/gambas.h create mode 120000 gb.cairo/gb.image.h create mode 120000 gb.cairo/gb_common.h create mode 120000 gb.cairo/m4 create mode 120000 gb.cairo/reconf create mode 100644 gb.cairo/src/Makefile.am create mode 100644 gb.cairo/src/c_cairo.c create mode 100644 gb.cairo/src/c_cairo.h create mode 100644 gb.cairo/src/c_surface.c create mode 100644 gb.cairo/src/c_surface.h create mode 100644 gb.cairo/src/gb.cairo.component create mode 100644 gb.cairo/src/main.c create mode 100644 gb.cairo/src/main.h create mode 100644 gb.compress.bzlib2/AUTHORS create mode 120000 gb.compress.bzlib2/COPYING create mode 100644 gb.compress.bzlib2/ChangeLog create mode 120000 gb.compress.bzlib2/INSTALL create mode 100644 gb.compress.bzlib2/Makefile.am create mode 100644 gb.compress.bzlib2/NEWS create mode 100644 gb.compress.bzlib2/README create mode 120000 gb.compress.bzlib2/acinclude.m4 create mode 120000 gb.compress.bzlib2/component.am create mode 100644 gb.compress.bzlib2/configure.ac create mode 120000 gb.compress.bzlib2/gambas.h create mode 120000 gb.compress.bzlib2/gb.compress.h create mode 120000 gb.compress.bzlib2/gb_common.h create mode 120000 gb.compress.bzlib2/m4 create mode 120000 gb.compress.bzlib2/reconf create mode 100644 gb.compress.bzlib2/src/Makefile.am create mode 100644 gb.compress.bzlib2/src/gb.compress.bzlib2.component create mode 100644 gb.compress.bzlib2/src/main.c create mode 100644 gb.compress.bzlib2/src/main.h create mode 100644 gb.compress.zlib/AUTHORS create mode 120000 gb.compress.zlib/COPYING create mode 100644 gb.compress.zlib/ChangeLog create mode 120000 gb.compress.zlib/INSTALL create mode 100644 gb.compress.zlib/Makefile.am create mode 100644 gb.compress.zlib/NEWS create mode 100644 gb.compress.zlib/README create mode 120000 gb.compress.zlib/acinclude.m4 create mode 120000 gb.compress.zlib/component.am create mode 100644 gb.compress.zlib/configure.ac create mode 120000 gb.compress.zlib/gambas.h create mode 120000 gb.compress.zlib/gb.compress.h create mode 120000 gb.compress.zlib/gb_common.h create mode 120000 gb.compress.zlib/m4 create mode 120000 gb.compress.zlib/reconf create mode 100644 gb.compress.zlib/src/Makefile.am create mode 100644 gb.compress.zlib/src/gb.compress.zlib.component create mode 100644 gb.compress.zlib/src/main.c create mode 100644 gb.compress.zlib/src/main.h create mode 100644 gb.compress.zstd/AUTHORS create mode 120000 gb.compress.zstd/COPYING create mode 100644 gb.compress.zstd/ChangeLog create mode 120000 gb.compress.zstd/INSTALL create mode 100644 gb.compress.zstd/Makefile.am create mode 100644 gb.compress.zstd/NEWS create mode 100644 gb.compress.zstd/README create mode 120000 gb.compress.zstd/acinclude.m4 create mode 120000 gb.compress.zstd/component.am create mode 100644 gb.compress.zstd/configure.ac create mode 120000 gb.compress.zstd/gambas.h create mode 120000 gb.compress.zstd/gb.compress.h create mode 120000 gb.compress.zstd/gb_common.h create mode 120000 gb.compress.zstd/m4 create mode 120000 gb.compress.zstd/reconf create mode 100644 gb.compress.zstd/src/Makefile.am create mode 100644 gb.compress.zstd/src/gb.compress.zstd.component create mode 100644 gb.compress.zstd/src/main.c create mode 100644 gb.compress.zstd/src/main.h create mode 100644 gb.crypt/AUTHORS create mode 120000 gb.crypt/COPYING create mode 100644 gb.crypt/ChangeLog create mode 120000 gb.crypt/INSTALL create mode 100644 gb.crypt/Makefile.am create mode 100644 gb.crypt/NEWS create mode 100644 gb.crypt/README create mode 120000 gb.crypt/acinclude.m4 create mode 120000 gb.crypt/component.am create mode 100644 gb.crypt/configure.ac create mode 120000 gb.crypt/gambas.h create mode 120000 gb.crypt/gb_common.h create mode 120000 gb.crypt/m4 create mode 120000 gb.crypt/reconf create mode 100644 gb.crypt/src/Makefile.am create mode 100644 gb.crypt/src/c_crypt.c create mode 100644 gb.crypt/src/c_crypt.h create mode 100644 gb.crypt/src/gb.crypt.component create mode 100644 gb.crypt/src/main.c create mode 100644 gb.crypt/src/main.h create mode 100644 gb.db.mysql/AUTHORS create mode 120000 gb.db.mysql/COPYING create mode 100644 gb.db.mysql/ChangeLog create mode 120000 gb.db.mysql/INSTALL create mode 100644 gb.db.mysql/Makefile.am create mode 100644 gb.db.mysql/NEWS create mode 100644 gb.db.mysql/README create mode 120000 gb.db.mysql/acinclude.m4 create mode 120000 gb.db.mysql/component.am create mode 100644 gb.db.mysql/configure.ac create mode 120000 gb.db.mysql/gambas.h create mode 120000 gb.db.mysql/gb.db.h create mode 120000 gb.db.mysql/gb.db.proto.h create mode 120000 gb.db.mysql/gb_common.h create mode 120000 gb.db.mysql/m4 create mode 120000 gb.db.mysql/reconf create mode 100755 gb.db.mysql/src/Makefile.am create mode 100644 gb.db.mysql/src/gb.db.mysql.component create mode 100644 gb.db.mysql/src/main.c create mode 100644 gb.db.mysql/src/main.h create mode 100644 gb.db.odbc/AUTHORS create mode 120000 gb.db.odbc/COPYING create mode 100644 gb.db.odbc/ChangeLog create mode 120000 gb.db.odbc/INSTALL create mode 100644 gb.db.odbc/Makefile.am create mode 100644 gb.db.odbc/NEWS create mode 100644 gb.db.odbc/README create mode 120000 gb.db.odbc/acinclude.m4 create mode 120000 gb.db.odbc/component.am create mode 100644 gb.db.odbc/configure.ac create mode 120000 gb.db.odbc/gambas.h create mode 120000 gb.db.odbc/gb.db.h create mode 120000 gb.db.odbc/gb.db.proto.h create mode 120000 gb.db.odbc/gb_common.h create mode 120000 gb.db.odbc/m4 create mode 120000 gb.db.odbc/reconf create mode 100644 gb.db.odbc/src/Makefile.am create mode 100644 gb.db.odbc/src/gb.db.odbc.component create mode 100644 gb.db.odbc/src/main.c create mode 100644 gb.db.odbc/src/main.h create mode 100644 gb.db.postgresql/AUTHORS create mode 100644 gb.db.postgresql/COPYING create mode 100644 gb.db.postgresql/COPYING.GPL create mode 100644 gb.db.postgresql/ChangeLog create mode 120000 gb.db.postgresql/INSTALL create mode 100644 gb.db.postgresql/Makefile.am create mode 100644 gb.db.postgresql/NEWS create mode 100644 gb.db.postgresql/README create mode 120000 gb.db.postgresql/acinclude.m4 create mode 120000 gb.db.postgresql/component.am create mode 100644 gb.db.postgresql/configure.ac create mode 120000 gb.db.postgresql/gambas.h create mode 120000 gb.db.postgresql/gb.db.h create mode 120000 gb.db.postgresql/gb.db.proto.h create mode 120000 gb.db.postgresql/gb_common.h create mode 120000 gb.db.postgresql/m4 create mode 120000 gb.db.postgresql/reconf create mode 100644 gb.db.postgresql/src/Makefile.am create mode 100644 gb.db.postgresql/src/gb.db.postgresql.component create mode 100644 gb.db.postgresql/src/main.c create mode 100644 gb.db.postgresql/src/main.h create mode 100644 gb.db.sqlite2/AUTHORS create mode 120000 gb.db.sqlite2/COPYING create mode 100644 gb.db.sqlite2/ChangeLog create mode 120000 gb.db.sqlite2/INSTALL create mode 100644 gb.db.sqlite2/Makefile.am create mode 100644 gb.db.sqlite2/NEWS create mode 100644 gb.db.sqlite2/README create mode 100644 gb.db.sqlite2/TODO create mode 120000 gb.db.sqlite2/acinclude.m4 create mode 120000 gb.db.sqlite2/component.am create mode 100644 gb.db.sqlite2/configure.ac create mode 120000 gb.db.sqlite2/gambas.h create mode 120000 gb.db.sqlite2/gb.db.h create mode 120000 gb.db.sqlite2/gb.db.proto.h create mode 120000 gb.db.sqlite2/gb_common.h create mode 120000 gb.db.sqlite2/m4 create mode 120000 gb.db.sqlite2/reconf create mode 100755 gb.db.sqlite2/src/Makefile.am create mode 100644 gb.db.sqlite2/src/dataset.cpp create mode 100644 gb.db.sqlite2/src/dataset.h create mode 100644 gb.db.sqlite2/src/gb.db.sqlite2.component create mode 100644 gb.db.sqlite2/src/main.cpp create mode 100644 gb.db.sqlite2/src/main.h create mode 100644 gb.db.sqlite2/src/qry_dat.cpp create mode 100644 gb.db.sqlite2/src/qry_dat.h create mode 100644 gb.db.sqlite2/src/sqlitedataset.cpp create mode 100644 gb.db.sqlite2/src/sqlitedataset.h create mode 100644 gb.db.sqlite2/src/stringhelper.cpp create mode 100644 gb.db.sqlite2/src/stringhelper.h create mode 100644 gb.db.sqlite3/AUTHORS create mode 120000 gb.db.sqlite3/COPYING create mode 100644 gb.db.sqlite3/ChangeLog create mode 120000 gb.db.sqlite3/INSTALL create mode 100644 gb.db.sqlite3/Makefile.am create mode 100644 gb.db.sqlite3/NEWS create mode 100644 gb.db.sqlite3/README create mode 120000 gb.db.sqlite3/acinclude.m4 create mode 120000 gb.db.sqlite3/component.am create mode 100644 gb.db.sqlite3/configure.ac create mode 120000 gb.db.sqlite3/gambas.h create mode 120000 gb.db.sqlite3/gb.db.h create mode 120000 gb.db.sqlite3/gb.db.proto.h create mode 120000 gb.db.sqlite3/gb_common.h create mode 120000 gb.db.sqlite3/m4 create mode 120000 gb.db.sqlite3/reconf create mode 100755 gb.db.sqlite3/src/Makefile.am create mode 100644 gb.db.sqlite3/src/README create mode 100644 gb.db.sqlite3/src/gb.db.sqlite3.component create mode 100644 gb.db.sqlite3/src/gb_buffer.c create mode 100644 gb.db.sqlite3/src/gb_buffer.h create mode 100644 gb.db.sqlite3/src/helper.c create mode 100644 gb.db.sqlite3/src/helper.h create mode 100644 gb.db.sqlite3/src/main.c create mode 100644 gb.db.sqlite3/src/main.h create mode 100644 gb.dbus/AUTHORS create mode 120000 gb.dbus/COPYING create mode 100644 gb.dbus/ChangeLog create mode 120000 gb.dbus/INSTALL create mode 100644 gb.dbus/Makefile.am create mode 100644 gb.dbus/NEWS create mode 100644 gb.dbus/README create mode 120000 gb.dbus/acinclude.m4 create mode 120000 gb.dbus/component.am create mode 100644 gb.dbus/configure.ac create mode 120000 gb.dbus/gambas.h create mode 120000 gb.dbus/gb_common.h create mode 120000 gb.dbus/m4 create mode 120000 gb.dbus/reconf create mode 100644 gb.dbus/src/Makefile.am create mode 100644 gb.dbus/src/c_dbus.c create mode 100644 gb.dbus/src/c_dbus.h create mode 100644 gb.dbus/src/c_dbusconnection.c create mode 100644 gb.dbus/src/c_dbusconnection.h create mode 100644 gb.dbus/src/c_dbusobserver.c create mode 100644 gb.dbus/src/c_dbusobserver.h create mode 100644 gb.dbus/src/c_dbusvariant.c create mode 100644 gb.dbus/src/c_dbusvariant.h create mode 100644 gb.dbus/src/dbus_print_message.c create mode 100644 gb.dbus/src/dbus_print_message.h create mode 100644 gb.dbus/src/gb.dbus.component create mode 100644 gb.dbus/src/gb.dbus/.directory create mode 100644 gb.dbus/src/gb.dbus/.icon.png create mode 100644 gb.dbus/src/gb.dbus/.project create mode 100644 gb.dbus/src/gb.dbus/.src/CIntrospection.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBus.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusApplication.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusConnection.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusObject.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusProxy.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusSignal.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusValues.class create mode 100644 gb.dbus/src/gb.dbus/.src/MMain.module create mode 100644 gb.dbus/src/gb.dbus/.src/Test/CTest.class create mode 100644 gb.dbus/src/gb.dbus/.src/Test/CTest2.class create mode 100644 gb.dbus/src/gb.dbus/.src/Test/CTest3.class create mode 100644 gb.dbus/src/gb.dbus/.src/Test/MyObject.class create mode 100644 gb.dbus/src/gb.dbus/.src/Test/MyValue.class create mode 100644 gb.dbus/src/gb.dbus/.src/Test/mpris.class create mode 100644 gb.dbus/src/gb.dbus/.src/_DBusNull.class create mode 100644 gb.dbus/src/helper.c create mode 100644 gb.dbus/src/helper.h create mode 100644 gb.dbus/src/main.c create mode 100644 gb.dbus/src/main.h create mode 100644 gb.desktop.gnome.keyring/AUTHORS create mode 120000 gb.desktop.gnome.keyring/COPYING create mode 100644 gb.desktop.gnome.keyring/ChangeLog create mode 120000 gb.desktop.gnome.keyring/INSTALL create mode 100644 gb.desktop.gnome.keyring/Makefile.am create mode 100644 gb.desktop.gnome.keyring/NEWS create mode 100644 gb.desktop.gnome.keyring/README create mode 120000 gb.desktop.gnome.keyring/acinclude.m4 create mode 120000 gb.desktop.gnome.keyring/component.am create mode 100644 gb.desktop.gnome.keyring/configure.ac create mode 120000 gb.desktop.gnome.keyring/gambas.h create mode 120000 gb.desktop.gnome.keyring/gb_common.h create mode 120000 gb.desktop.gnome.keyring/m4 create mode 120000 gb.desktop.gnome.keyring/reconf create mode 100644 gb.desktop.gnome.keyring/src/Makefile.am create mode 100644 gb.desktop.gnome.keyring/src/gb.desktop.gnome.keyring.component create mode 100644 gb.desktop.gnome.keyring/src/keyring.c create mode 100644 gb.desktop.gnome.keyring/src/keyring.h create mode 100644 gb.desktop.gnome.keyring/src/main.c create mode 100644 gb.desktop.gnome.keyring/src/main.h create mode 100644 gb.desktop.x11/AUTHORS create mode 120000 gb.desktop.x11/COPYING create mode 100644 gb.desktop.x11/ChangeLog create mode 120000 gb.desktop.x11/INSTALL create mode 100644 gb.desktop.x11/Makefile.am create mode 100644 gb.desktop.x11/NEWS create mode 100644 gb.desktop.x11/README create mode 120000 gb.desktop.x11/acinclude.m4 create mode 120000 gb.desktop.x11/component.am create mode 100644 gb.desktop.x11/configure.ac create mode 120000 gb.desktop.x11/gambas.h create mode 120000 gb.desktop.x11/gb.image.h create mode 120000 gb.desktop.x11/gb_common.h create mode 120000 gb.desktop.x11/gb_list.h create mode 120000 gb.desktop.x11/gb_list_temp.h create mode 120000 gb.desktop.x11/m4 create mode 120000 gb.desktop.x11/reconf create mode 100644 gb.desktop.x11/src/Makefile.am create mode 100644 gb.desktop.x11/src/c_x11.c create mode 100644 gb.desktop.x11/src/c_x11.h create mode 100644 gb.desktop.x11/src/c_x11systray.c create mode 100644 gb.desktop.x11/src/c_x11systray.h create mode 100644 gb.desktop.x11/src/gb.desktop.x11.component create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.directory create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.hidden/icon.png create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.icon.png create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.project create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.src/Atom.class create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.src/DesktopWatcher.class create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.src/DesktopWindow.class create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.src/Main.module create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.src/X11.class create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.src/_DesktopVirtual.class create mode 100644 gb.desktop.x11/src/gb.desktop.x11/.src/_Desktop_Windows.class create mode 100644 gb.desktop.x11/src/gb_list.c create mode 100644 gb.desktop.x11/src/main.c create mode 100644 gb.desktop.x11/src/main.h create mode 100644 gb.desktop.x11/src/systray/common.h create mode 100644 gb.desktop.x11/src/systray/debug.c create mode 100644 gb.desktop.x11/src/systray/debug.h create mode 100644 gb.desktop.x11/src/systray/embed.c create mode 100644 gb.desktop.x11/src/systray/embed.h create mode 100644 gb.desktop.x11/src/systray/icons.c create mode 100644 gb.desktop.x11/src/systray/icons.h create mode 100644 gb.desktop.x11/src/systray/kde_tray.c create mode 100644 gb.desktop.x11/src/systray/kde_tray.h create mode 100644 gb.desktop.x11/src/systray/list.h create mode 100644 gb.desktop.x11/src/systray/settings.c create mode 100644 gb.desktop.x11/src/systray/settings.h create mode 100644 gb.desktop.x11/src/systray/systray.c create mode 100644 gb.desktop.x11/src/systray/systray.h create mode 100644 gb.desktop.x11/src/systray/tray.c create mode 100644 gb.desktop.x11/src/systray/tray.h create mode 100644 gb.desktop.x11/src/systray/wmh.c create mode 100644 gb.desktop.x11/src/systray/wmh.h create mode 100644 gb.desktop.x11/src/systray/xembed.c create mode 100644 gb.desktop.x11/src/systray/xembed.h create mode 100644 gb.desktop.x11/src/systray/xutils.c create mode 100644 gb.desktop.x11/src/systray/xutils.h create mode 100644 gb.desktop.x11/src/x11.c create mode 100644 gb.desktop.x11/src/x11.h create mode 100644 gb.form.htmlview/AUTHORS create mode 120000 gb.form.htmlview/COPYING create mode 100644 gb.form.htmlview/ChangeLog create mode 120000 gb.form.htmlview/INSTALL create mode 100644 gb.form.htmlview/Makefile.am create mode 100644 gb.form.htmlview/NEWS create mode 100644 gb.form.htmlview/README create mode 120000 gb.form.htmlview/acinclude.m4 create mode 120000 gb.form.htmlview/component.am create mode 100644 gb.form.htmlview/configure.ac create mode 120000 gb.form.htmlview/gambas.h create mode 120000 gb.form.htmlview/gb.draw.h create mode 120000 gb.form.htmlview/gb.geom.h create mode 120000 gb.form.htmlview/gb.image.h create mode 120000 gb.form.htmlview/gb.paint.h create mode 120000 gb.form.htmlview/gb_common.h create mode 120000 gb.form.htmlview/m4 create mode 120000 gb.form.htmlview/reconf create mode 100644 gb.form.htmlview/src/Makefile.am create mode 100644 gb.form.htmlview/src/c_htmldocument.cpp create mode 100644 gb.form.htmlview/src/c_htmldocument.h create mode 120000 gb.form.htmlview/src/gb.form.htmlview.component create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.component create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.directory create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/control/htmlview.png create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/test/desert.jpg create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test.html create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test2.html create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test3.html create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test4.html create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test5.html create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test6.html create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.icon.png create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.project create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.src/FTest.class create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.src/FTest.form create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.src/HtmlView.class create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.src/HtmlViewDocument.class create mode 100644 gb.form.htmlview/src/gb.form.htmlview/.src/Main.module create mode 100644 gb.form.htmlview/src/gb.form.htmlview/fail.png create mode 100644 gb.form.htmlview/src/gb.form.htmlview/master.css create mode 100644 gb.form.htmlview/src/gumbo/LICENSE create mode 100644 gb.form.htmlview/src/gumbo/attribute.c create mode 100644 gb.form.htmlview/src/gumbo/attribute.h create mode 100644 gb.form.htmlview/src/gumbo/char_ref.c create mode 100644 gb.form.htmlview/src/gumbo/char_ref.h create mode 100644 gb.form.htmlview/src/gumbo/char_ref.rl create mode 100644 gb.form.htmlview/src/gumbo/error.c create mode 100644 gb.form.htmlview/src/gumbo/error.h create mode 100644 gb.form.htmlview/src/gumbo/gumbo.h create mode 100644 gb.form.htmlview/src/gumbo/insertion_mode.h create mode 100644 gb.form.htmlview/src/gumbo/parser.c create mode 100644 gb.form.htmlview/src/gumbo/parser.h create mode 100644 gb.form.htmlview/src/gumbo/string_buffer.c create mode 100644 gb.form.htmlview/src/gumbo/string_buffer.h create mode 100644 gb.form.htmlview/src/gumbo/string_piece.c create mode 100644 gb.form.htmlview/src/gumbo/string_piece.h create mode 100644 gb.form.htmlview/src/gumbo/tag.c create mode 100644 gb.form.htmlview/src/gumbo/tag_enum.h create mode 100644 gb.form.htmlview/src/gumbo/tag_gperf.h create mode 100644 gb.form.htmlview/src/gumbo/tag_sizes.h create mode 100644 gb.form.htmlview/src/gumbo/tag_strings.h create mode 100644 gb.form.htmlview/src/gumbo/token_type.h create mode 100644 gb.form.htmlview/src/gumbo/tokenizer.c create mode 100644 gb.form.htmlview/src/gumbo/tokenizer.h create mode 100644 gb.form.htmlview/src/gumbo/tokenizer_states.h create mode 100644 gb.form.htmlview/src/gumbo/utf8.c create mode 100644 gb.form.htmlview/src/gumbo/utf8.h create mode 100644 gb.form.htmlview/src/gumbo/util.c create mode 100644 gb.form.htmlview/src/gumbo/util.h create mode 100644 gb.form.htmlview/src/gumbo/vector.c create mode 100644 gb.form.htmlview/src/gumbo/vector.h create mode 100644 gb.form.htmlview/src/litehtml/LICENSE create mode 100644 gb.form.htmlview/src/litehtml/README.md create mode 100644 gb.form.htmlview/src/litehtml/attributes.h create mode 100644 gb.form.htmlview/src/litehtml/background.cpp create mode 100644 gb.form.htmlview/src/litehtml/background.h create mode 100644 gb.form.htmlview/src/litehtml/borders.h create mode 100644 gb.form.htmlview/src/litehtml/box.cpp create mode 100644 gb.form.htmlview/src/litehtml/box.h create mode 100644 gb.form.htmlview/src/litehtml/codepoint.cpp create mode 100644 gb.form.htmlview/src/litehtml/codepoint.h create mode 100644 gb.form.htmlview/src/litehtml/context.cpp create mode 100644 gb.form.htmlview/src/litehtml/context.h create mode 100644 gb.form.htmlview/src/litehtml/css_length.cpp create mode 100644 gb.form.htmlview/src/litehtml/css_length.h create mode 100644 gb.form.htmlview/src/litehtml/css_margins.h create mode 100644 gb.form.htmlview/src/litehtml/css_offsets.h create mode 100644 gb.form.htmlview/src/litehtml/css_position.h create mode 100644 gb.form.htmlview/src/litehtml/css_selector.cpp create mode 100644 gb.form.htmlview/src/litehtml/css_selector.h create mode 100644 gb.form.htmlview/src/litehtml/document.cpp create mode 100644 gb.form.htmlview/src/litehtml/document.h create mode 100644 gb.form.htmlview/src/litehtml/el_anchor.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_anchor.h create mode 100644 gb.form.htmlview/src/litehtml/el_base.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_base.h create mode 100644 gb.form.htmlview/src/litehtml/el_before_after.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_before_after.h create mode 100644 gb.form.htmlview/src/litehtml/el_body.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_body.h create mode 100644 gb.form.htmlview/src/litehtml/el_break.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_break.h create mode 100644 gb.form.htmlview/src/litehtml/el_cdata.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_cdata.h create mode 100644 gb.form.htmlview/src/litehtml/el_comment.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_comment.h create mode 100644 gb.form.htmlview/src/litehtml/el_div.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_div.h create mode 100644 gb.form.htmlview/src/litehtml/el_font.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_font.h create mode 100644 gb.form.htmlview/src/litehtml/el_image.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_image.h create mode 100644 gb.form.htmlview/src/litehtml/el_li.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_li.h create mode 100644 gb.form.htmlview/src/litehtml/el_link.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_link.h create mode 100644 gb.form.htmlview/src/litehtml/el_para.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_para.h create mode 100644 gb.form.htmlview/src/litehtml/el_script.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_script.h create mode 100644 gb.form.htmlview/src/litehtml/el_space.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_space.h create mode 100644 gb.form.htmlview/src/litehtml/el_style.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_style.h create mode 100644 gb.form.htmlview/src/litehtml/el_table.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_table.h create mode 100644 gb.form.htmlview/src/litehtml/el_td.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_td.h create mode 100644 gb.form.htmlview/src/litehtml/el_text.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_text.h create mode 100644 gb.form.htmlview/src/litehtml/el_title.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_title.h create mode 100644 gb.form.htmlview/src/litehtml/el_tr.cpp create mode 100644 gb.form.htmlview/src/litehtml/el_tr.h create mode 100644 gb.form.htmlview/src/litehtml/element.cpp create mode 100644 gb.form.htmlview/src/litehtml/element.h create mode 100644 gb.form.htmlview/src/litehtml/html.cpp create mode 100644 gb.form.htmlview/src/litehtml/html.h create mode 100644 gb.form.htmlview/src/litehtml/html_tag.cpp create mode 100644 gb.form.htmlview/src/litehtml/html_tag.h create mode 100644 gb.form.htmlview/src/litehtml/iterators.cpp create mode 100644 gb.form.htmlview/src/litehtml/iterators.h create mode 100644 gb.form.htmlview/src/litehtml/media_query.cpp create mode 100644 gb.form.htmlview/src/litehtml/media_query.h create mode 100644 gb.form.htmlview/src/litehtml/num_cvt.cpp create mode 100644 gb.form.htmlview/src/litehtml/num_cvt.h create mode 100644 gb.form.htmlview/src/litehtml/os_types.h create mode 100644 gb.form.htmlview/src/litehtml/strtod.cpp create mode 100644 gb.form.htmlview/src/litehtml/style.cpp create mode 100644 gb.form.htmlview/src/litehtml/style.h create mode 100644 gb.form.htmlview/src/litehtml/stylesheet.cpp create mode 100644 gb.form.htmlview/src/litehtml/stylesheet.h create mode 100644 gb.form.htmlview/src/litehtml/table.cpp create mode 100644 gb.form.htmlview/src/litehtml/table.h create mode 100644 gb.form.htmlview/src/litehtml/tstring_view.cpp create mode 100644 gb.form.htmlview/src/litehtml/tstring_view.h create mode 100644 gb.form.htmlview/src/litehtml/types.h create mode 100644 gb.form.htmlview/src/litehtml/url.cpp create mode 100644 gb.form.htmlview/src/litehtml/url.h create mode 100644 gb.form.htmlview/src/litehtml/url_path.cpp create mode 100644 gb.form.htmlview/src/litehtml/url_path.h create mode 100644 gb.form.htmlview/src/litehtml/utf8_strings.cpp create mode 100644 gb.form.htmlview/src/litehtml/utf8_strings.h create mode 100644 gb.form.htmlview/src/litehtml/web_color.cpp create mode 100644 gb.form.htmlview/src/litehtml/web_color.h create mode 100644 gb.form.htmlview/src/main.cpp create mode 100644 gb.form.htmlview/src/main.h create mode 100644 gb.gmp/AUTHORS create mode 120000 gb.gmp/COPYING create mode 100644 gb.gmp/ChangeLog create mode 120000 gb.gmp/INSTALL create mode 100644 gb.gmp/Makefile.am create mode 100644 gb.gmp/NEWS create mode 100644 gb.gmp/README create mode 120000 gb.gmp/acinclude.m4 create mode 120000 gb.gmp/component.am create mode 100644 gb.gmp/configure.ac create mode 120000 gb.gmp/gambas.h create mode 120000 gb.gmp/gb_common.h create mode 120000 gb.gmp/m4 create mode 120000 gb.gmp/reconf create mode 100644 gb.gmp/src/Makefile.am create mode 100644 gb.gmp/src/c_bigint.c create mode 100644 gb.gmp/src/c_bigint.h create mode 100644 gb.gmp/src/c_rational.c create mode 100644 gb.gmp/src/c_rational.h create mode 100644 gb.gmp/src/gb.gmp.component create mode 100644 gb.gmp/src/main.c create mode 100644 gb.gmp/src/main.h create mode 100644 gb.gsl/AUTHORS create mode 120000 gb.gsl/COPYING create mode 100644 gb.gsl/ChangeLog create mode 120000 gb.gsl/INSTALL create mode 100644 gb.gsl/Makefile.am create mode 100644 gb.gsl/NEWS create mode 100644 gb.gsl/README create mode 100644 gb.gsl/Test/test/.directory create mode 100644 gb.gsl/Test/test/.icon.png create mode 100644 gb.gsl/Test/test/.project create mode 100644 gb.gsl/Test/test/.src/MMain.module create mode 100644 gb.gsl/Test/test/.src/Test.class create mode 100644 gb.gsl/Test/test/.src/TestComplex.class create mode 100644 gb.gsl/Test/test/.src/TestSuite.class create mode 120000 gb.gsl/acinclude.m4 create mode 120000 gb.gsl/component.am create mode 100644 gb.gsl/configure.ac create mode 120000 gb.gsl/gambas.h create mode 120000 gb.gsl/gb_common.h create mode 120000 gb.gsl/m4 create mode 120000 gb.gsl/reconf create mode 100644 gb.gsl/src/Makefile.am create mode 100644 gb.gsl/src/c_complex.c create mode 100644 gb.gsl/src/c_complex.h create mode 100644 gb.gsl/src/c_float_array.c create mode 100644 gb.gsl/src/c_float_array.h create mode 100644 gb.gsl/src/c_gsl.c create mode 100644 gb.gsl/src/c_gsl.h create mode 100644 gb.gsl/src/c_matrix.c create mode 100644 gb.gsl/src/c_matrix.h create mode 100644 gb.gsl/src/c_newtonpolynomial.c create mode 100644 gb.gsl/src/c_newtonpolynomial.h create mode 100644 gb.gsl/src/c_polynomial.c create mode 100644 gb.gsl/src/c_polynomial.h create mode 100644 gb.gsl/src/c_vector.c create mode 100644 gb.gsl/src/c_vector.h create mode 100644 gb.gsl/src/gb.gsl.component create mode 100644 gb.gsl/src/main.c create mode 100644 gb.gsl/src/main.h create mode 100644 gb.gtk/AUTHORS create mode 120000 gb.gtk/COPYING create mode 100644 gb.gtk/ChangeLog create mode 120000 gb.gtk/INSTALL create mode 100644 gb.gtk/Makefile.am create mode 100644 gb.gtk/NEWS create mode 100644 gb.gtk/README create mode 100644 gb.gtk/TODO create mode 120000 gb.gtk/acinclude.m4 create mode 120000 gb.gtk/component.am create mode 100644 gb.gtk/configure.ac create mode 120000 gb.gtk/gambas.h create mode 120000 gb.gtk/gb.draw.h create mode 120000 gb.gtk/gb.geom.h create mode 120000 gb.gtk/gb.gl.h create mode 120000 gb.gtk/gb.image.h create mode 120000 gb.gtk/gb.paint.h create mode 120000 gb.gtk/gb_common.h create mode 120000 gb.gtk/m4 create mode 120000 gb.gtk/reconf create mode 120000 gb.gtk/share create mode 100644 gb.gtk/src/CButton.cpp create mode 100644 gb.gtk/src/CButton.h create mode 100644 gb.gtk/src/CClipboard.cpp create mode 100644 gb.gtk/src/CClipboard.h create mode 100644 gb.gtk/src/CColor.cpp create mode 100644 gb.gtk/src/CColor.h create mode 100644 gb.gtk/src/CConst.cpp create mode 100644 gb.gtk/src/CConst.h create mode 100644 gb.gtk/src/CContainer.cpp create mode 100644 gb.gtk/src/CContainer.h create mode 100644 gb.gtk/src/CDialog.cpp create mode 100644 gb.gtk/src/CDialog.h create mode 100644 gb.gtk/src/CDraw.cpp create mode 100644 gb.gtk/src/CDraw.h create mode 100644 gb.gtk/src/CDrawingArea.cpp create mode 100644 gb.gtk/src/CDrawingArea.h create mode 100644 gb.gtk/src/CFont.cpp create mode 100644 gb.gtk/src/CFont.h create mode 100644 gb.gtk/src/CImage.cpp create mode 100644 gb.gtk/src/CImage.h create mode 100644 gb.gtk/src/CKey.cpp create mode 100644 gb.gtk/src/CKey.h create mode 100644 gb.gtk/src/CMenu.cpp create mode 100644 gb.gtk/src/CMenu.h create mode 100644 gb.gtk/src/CMouse.cpp create mode 100644 gb.gtk/src/CMouse.h create mode 100644 gb.gtk/src/CPanel.cpp create mode 100644 gb.gtk/src/CPanel.h create mode 100644 gb.gtk/src/CPicture.cpp create mode 100644 gb.gtk/src/CPicture.h create mode 100644 gb.gtk/src/CScreen.cpp create mode 100644 gb.gtk/src/CScreen.h create mode 100644 gb.gtk/src/CSlider.cpp create mode 100644 gb.gtk/src/CSlider.h create mode 100644 gb.gtk/src/CStyle.cpp create mode 100644 gb.gtk/src/CStyle.h create mode 100644 gb.gtk/src/CTabStrip.cpp create mode 100644 gb.gtk/src/CTabStrip.h create mode 100644 gb.gtk/src/CTextArea.cpp create mode 100644 gb.gtk/src/CTextArea.h create mode 100644 gb.gtk/src/CTextBox.cpp create mode 100644 gb.gtk/src/CTextBox.h create mode 100644 gb.gtk/src/CTrayIcon.cpp create mode 100644 gb.gtk/src/CTrayIcon.h create mode 100644 gb.gtk/src/CWatcher.cpp create mode 100644 gb.gtk/src/CWatcher.h create mode 100644 gb.gtk/src/CWidget.cpp create mode 100644 gb.gtk/src/CWidget.h create mode 100644 gb.gtk/src/CWindow.cpp create mode 100644 gb.gtk/src/CWindow.h create mode 100644 gb.gtk/src/Makefile.am create mode 100644 gb.gtk/src/canimation.cpp create mode 100644 gb.gtk/src/canimation.h create mode 100644 gb.gtk/src/cpaint_impl.cpp create mode 100644 gb.gtk/src/cpaint_impl.h create mode 100644 gb.gtk/src/cprinter.cpp create mode 100644 gb.gtk/src/cprinter.h create mode 100644 gb.gtk/src/csvgimage.cpp create mode 100644 gb.gtk/src/csvgimage.h create mode 100644 gb.gtk/src/font-parser.cpp create mode 100644 gb.gtk/src/font-parser.h create mode 100644 gb.gtk/src/gapplication.cpp create mode 100644 gb.gtk/src/gapplication.h create mode 100644 gb.gtk/src/gb.gtk.component create mode 100644 gb.gtk/src/gb.gtk.h create mode 100644 gb.gtk/src/gbutton.cpp create mode 100644 gb.gtk/src/gbutton.h create mode 100644 gb.gtk/src/gclipboard.h create mode 100644 gb.gtk/src/gcolor.h create mode 100644 gb.gtk/src/gcontainer.cpp create mode 100644 gb.gtk/src/gcontainer.h create mode 100644 gb.gtk/src/gcontrol.cpp create mode 100644 gb.gtk/src/gcontrol.h create mode 100644 gb.gtk/src/gcursor.cpp create mode 100644 gb.gtk/src/gcursor.h create mode 100644 gb.gtk/src/gdesktop.cpp create mode 100644 gb.gtk/src/gdesktop.h create mode 100644 gb.gtk/src/gdialog.cpp create mode 100644 gb.gtk/src/gdialog.h create mode 100644 gb.gtk/src/gdrag.cpp create mode 100644 gb.gtk/src/gdrag.h create mode 100644 gb.gtk/src/gdrawingarea.cpp create mode 100644 gb.gtk/src/gdrawingarea.h create mode 100644 gb.gtk/src/gfont.cpp create mode 100644 gb.gtk/src/gfont.h create mode 100644 gb.gtk/src/ggambastag.h create mode 100644 gb.gtk/src/gglarea.cpp create mode 100644 gb.gtk/src/gglarea.h create mode 100644 gb.gtk/src/gkey.cpp create mode 100644 gb.gtk/src/gkey.h create mode 100644 gb.gtk/src/gmainwindow.cpp create mode 100644 gb.gtk/src/gmainwindow.h create mode 100644 gb.gtk/src/gmenu.cpp create mode 100644 gb.gtk/src/gmenu.h create mode 100644 gb.gtk/src/gmouse.cpp create mode 100644 gb.gtk/src/gmouse.h create mode 100644 gb.gtk/src/gpanel.cpp create mode 100644 gb.gtk/src/gpanel.h create mode 100644 gb.gtk/src/gpicture.cpp create mode 100644 gb.gtk/src/gpicture.h create mode 100644 gb.gtk/src/gplugin.h create mode 100644 gb.gtk/src/gprinter.cpp create mode 100644 gb.gtk/src/gprinter.h create mode 100644 gb.gtk/src/gscrollbar.h create mode 100644 gb.gtk/src/gshare.h create mode 100644 gb.gtk/src/gsignals.cpp create mode 100644 gb.gtk/src/gslider.cpp create mode 100644 gb.gtk/src/gslider.h create mode 100644 gb.gtk/src/gtabstrip.cpp create mode 100644 gb.gtk/src/gtabstrip.h create mode 100644 gb.gtk/src/gtag.h create mode 100644 gb.gtk/src/gtextarea.cpp create mode 100644 gb.gtk/src/gtextarea.h create mode 100644 gb.gtk/src/gtextbox.cpp create mode 100644 gb.gtk/src/gtextbox.h create mode 100644 gb.gtk/src/gtools.cpp create mode 100644 gb.gtk/src/gtools.h create mode 100644 gb.gtk/src/gtrayicon.cpp create mode 100644 gb.gtk/src/gtrayicon.h create mode 100644 gb.gtk/src/gtree.cpp create mode 100644 gb.gtk/src/gtree.h create mode 100644 gb.gtk/src/kentities.h create mode 100644 gb.gtk/src/main.cpp create mode 100644 gb.gtk/src/main.h create mode 100644 gb.gtk/src/opengl/Makefile.am create mode 100644 gb.gtk/src/opengl/c_glarea.c create mode 100644 gb.gtk/src/opengl/c_glarea.h create mode 100644 gb.gtk/src/opengl/gb.gtk.opengl.component create mode 100644 gb.gtk/src/opengl/main.c create mode 100644 gb.gtk/src/opengl/main.h create mode 100644 gb.gtk/src/sm/bonobo-macros.h create mode 100644 gb.gtk/src/sm/gnome-client.c create mode 100644 gb.gtk/src/sm/gnome-client.h create mode 100644 gb.gtk/src/sm/gnome-ice.c create mode 100644 gb.gtk/src/sm/gnome-ice.h create mode 100644 gb.gtk/src/sm/gnome-macros.h create mode 100644 gb.gtk/src/sm/gnome-marshal.c create mode 100644 gb.gtk/src/sm/gnome-marshal.h create mode 100644 gb.gtk/src/sm/gnome-uidefs.h create mode 100644 gb.gtk/src/sm/gnometypebuiltins.c create mode 100644 gb.gtk/src/sm/gnometypebuiltins.h create mode 100644 gb.gtk/src/sm/libgnomeui.h create mode 100644 gb.gtk/src/sm/libgnomeuiP.h create mode 100644 gb.gtk/src/sm/sm.h create mode 100644 gb.gtk/src/watcher.cpp create mode 100644 gb.gtk/src/watcher.h create mode 100644 gb.gtk/src/widgets.h create mode 120000 gb.gtk/src/x11.c create mode 120000 gb.gtk/src/x11.h create mode 100644 gb.gtk3/AUTHORS create mode 120000 gb.gtk3/COPYING create mode 100644 gb.gtk3/ChangeLog create mode 120000 gb.gtk3/INSTALL create mode 100644 gb.gtk3/Makefile.am create mode 100644 gb.gtk3/NEWS create mode 100644 gb.gtk3/README create mode 100644 gb.gtk3/TODO create mode 120000 gb.gtk3/acinclude.m4 create mode 120000 gb.gtk3/component.am create mode 100644 gb.gtk3/configure.ac create mode 120000 gb.gtk3/gambas.h create mode 120000 gb.gtk3/gb.draw.h create mode 120000 gb.gtk3/gb.geom.h create mode 120000 gb.gtk3/gb.gl.h create mode 120000 gb.gtk3/gb.image.h create mode 120000 gb.gtk3/gb.paint.h create mode 120000 gb.gtk3/gb_common.h create mode 120000 gb.gtk3/m4 create mode 120000 gb.gtk3/reconf create mode 120000 gb.gtk3/share create mode 120000 gb.gtk3/src/CButton.cpp create mode 120000 gb.gtk3/src/CButton.h create mode 120000 gb.gtk3/src/CClipboard.cpp create mode 120000 gb.gtk3/src/CClipboard.h create mode 120000 gb.gtk3/src/CColor.cpp create mode 120000 gb.gtk3/src/CColor.h create mode 120000 gb.gtk3/src/CConst.cpp create mode 120000 gb.gtk3/src/CConst.h create mode 120000 gb.gtk3/src/CContainer.cpp create mode 120000 gb.gtk3/src/CContainer.h create mode 120000 gb.gtk3/src/CDialog.cpp create mode 120000 gb.gtk3/src/CDialog.h create mode 120000 gb.gtk3/src/CDraw.cpp create mode 120000 gb.gtk3/src/CDraw.h create mode 120000 gb.gtk3/src/CDrawingArea.cpp create mode 120000 gb.gtk3/src/CDrawingArea.h create mode 120000 gb.gtk3/src/CFont.cpp create mode 120000 gb.gtk3/src/CFont.h create mode 120000 gb.gtk3/src/CImage.cpp create mode 120000 gb.gtk3/src/CImage.h create mode 120000 gb.gtk3/src/CKey.cpp create mode 120000 gb.gtk3/src/CKey.h create mode 120000 gb.gtk3/src/CMenu.cpp create mode 120000 gb.gtk3/src/CMenu.h create mode 120000 gb.gtk3/src/CMouse.cpp create mode 120000 gb.gtk3/src/CMouse.h create mode 120000 gb.gtk3/src/CPanel.cpp create mode 120000 gb.gtk3/src/CPanel.h create mode 120000 gb.gtk3/src/CPicture.cpp create mode 120000 gb.gtk3/src/CPicture.h create mode 120000 gb.gtk3/src/CScreen.cpp create mode 120000 gb.gtk3/src/CScreen.h create mode 120000 gb.gtk3/src/CSlider.cpp create mode 120000 gb.gtk3/src/CSlider.h create mode 120000 gb.gtk3/src/CStyle.cpp create mode 120000 gb.gtk3/src/CStyle.h create mode 120000 gb.gtk3/src/CTabStrip.cpp create mode 120000 gb.gtk3/src/CTabStrip.h create mode 120000 gb.gtk3/src/CTextArea.cpp create mode 120000 gb.gtk3/src/CTextArea.h create mode 120000 gb.gtk3/src/CTextBox.cpp create mode 120000 gb.gtk3/src/CTextBox.h create mode 120000 gb.gtk3/src/CTrayIcon.cpp create mode 120000 gb.gtk3/src/CTrayIcon.h create mode 120000 gb.gtk3/src/CWatcher.cpp create mode 120000 gb.gtk3/src/CWatcher.h create mode 120000 gb.gtk3/src/CWidget.cpp create mode 120000 gb.gtk3/src/CWidget.h create mode 120000 gb.gtk3/src/CWindow.cpp create mode 120000 gb.gtk3/src/CWindow.h create mode 100644 gb.gtk3/src/Makefile.am create mode 120000 gb.gtk3/src/canimation.cpp create mode 120000 gb.gtk3/src/canimation.h create mode 120000 gb.gtk3/src/cpaint_impl.cpp create mode 120000 gb.gtk3/src/cpaint_impl.h create mode 120000 gb.gtk3/src/cprinter.cpp create mode 120000 gb.gtk3/src/cprinter.h create mode 120000 gb.gtk3/src/csvgimage.cpp create mode 120000 gb.gtk3/src/csvgimage.h create mode 120000 gb.gtk3/src/font-parser.cpp create mode 120000 gb.gtk3/src/font-parser.h create mode 120000 gb.gtk3/src/gapplication.cpp create mode 120000 gb.gtk3/src/gapplication.h create mode 120000 gb.gtk3/src/gb.gtk.h create mode 100644 gb.gtk3/src/gb.gtk.patch.h create mode 100644 gb.gtk3/src/gb.gtk.platform.h create mode 100644 gb.gtk3/src/gb.gtk3.component create mode 120000 gb.gtk3/src/gbutton.cpp create mode 120000 gb.gtk3/src/gbutton.h create mode 120000 gb.gtk3/src/gclipboard.h create mode 120000 gb.gtk3/src/gcolor.h create mode 120000 gb.gtk3/src/gcontainer.cpp create mode 120000 gb.gtk3/src/gcontainer.h create mode 120000 gb.gtk3/src/gcontrol.cpp create mode 120000 gb.gtk3/src/gcontrol.h create mode 120000 gb.gtk3/src/gcursor.cpp create mode 120000 gb.gtk3/src/gcursor.h create mode 120000 gb.gtk3/src/gdesktop.cpp create mode 120000 gb.gtk3/src/gdesktop.h create mode 120000 gb.gtk3/src/gdialog.cpp create mode 120000 gb.gtk3/src/gdialog.h create mode 120000 gb.gtk3/src/gdrag.cpp create mode 120000 gb.gtk3/src/gdrag.h create mode 120000 gb.gtk3/src/gdrawingarea.cpp create mode 120000 gb.gtk3/src/gdrawingarea.h create mode 120000 gb.gtk3/src/gfont.cpp create mode 120000 gb.gtk3/src/gfont.h create mode 120000 gb.gtk3/src/ggambastag.h create mode 120000 gb.gtk3/src/gkey.cpp create mode 120000 gb.gtk3/src/gkey.h create mode 120000 gb.gtk3/src/gmainwindow.cpp create mode 120000 gb.gtk3/src/gmainwindow.h create mode 120000 gb.gtk3/src/gmenu.cpp create mode 120000 gb.gtk3/src/gmenu.h create mode 120000 gb.gtk3/src/gmouse.cpp create mode 120000 gb.gtk3/src/gmouse.h create mode 120000 gb.gtk3/src/gpanel.cpp create mode 120000 gb.gtk3/src/gpanel.h create mode 120000 gb.gtk3/src/gpicture.cpp create mode 120000 gb.gtk3/src/gpicture.h create mode 120000 gb.gtk3/src/gprinter.cpp create mode 120000 gb.gtk3/src/gprinter.h create mode 120000 gb.gtk3/src/gscrollbar.h create mode 120000 gb.gtk3/src/gshare.h create mode 120000 gb.gtk3/src/gsignals.cpp create mode 120000 gb.gtk3/src/gslider.cpp create mode 120000 gb.gtk3/src/gslider.h create mode 120000 gb.gtk3/src/gtabstrip.cpp create mode 120000 gb.gtk3/src/gtabstrip.h create mode 120000 gb.gtk3/src/gtag.h create mode 120000 gb.gtk3/src/gtextarea.cpp create mode 120000 gb.gtk3/src/gtextarea.h create mode 120000 gb.gtk3/src/gtextbox.cpp create mode 120000 gb.gtk3/src/gtextbox.h create mode 120000 gb.gtk3/src/gtools.cpp create mode 120000 gb.gtk3/src/gtools.h create mode 120000 gb.gtk3/src/gtrayicon.cpp create mode 120000 gb.gtk3/src/gtrayicon.h create mode 120000 gb.gtk3/src/gtree.cpp create mode 120000 gb.gtk3/src/gtree.h create mode 120000 gb.gtk3/src/kentities.h create mode 100644 gb.gtk3/src/main.cpp create mode 100644 gb.gtk3/src/main.h create mode 100644 gb.gtk3/src/opengl/Makefile.am create mode 100644 gb.gtk3/src/opengl/c_glarea.c create mode 100644 gb.gtk3/src/opengl/c_glarea.h create mode 100644 gb.gtk3/src/opengl/gb.gtk3.opengl.component create mode 100644 gb.gtk3/src/opengl/main.c create mode 100644 gb.gtk3/src/opengl/main.h create mode 120000 gb.gtk3/src/watcher.cpp create mode 120000 gb.gtk3/src/watcher.h create mode 100644 gb.gtk3/src/wayland/Makefile.am create mode 100644 gb.gtk3/src/wayland/gb.gtk3.wayland.component create mode 100644 gb.gtk3/src/wayland/main.c create mode 100644 gb.gtk3/src/wayland/main.h create mode 100644 gb.gtk3/src/webview/Makefile.am create mode 100644 gb.gtk3/src/webview/c_websettings.c create mode 100644 gb.gtk3/src/webview/c_websettings.h create mode 100644 gb.gtk3/src/webview/c_webview.c create mode 100644 gb.gtk3/src/webview/c_webview.h create mode 100644 gb.gtk3/src/webview/gb.gtk3.webview.component create mode 100644 gb.gtk3/src/webview/main.c create mode 100644 gb.gtk3/src/webview/main.h create mode 120000 gb.gtk3/src/widgets.h create mode 100644 gb.gtk3/src/x11/Makefile.am create mode 100644 gb.gtk3/src/x11/gb.gtk3.x11.component create mode 100644 gb.gtk3/src/x11/main.c create mode 100644 gb.gtk3/src/x11/main.h create mode 120000 gb.gtk3/src/x11/x11.c create mode 120000 gb.gtk3/src/x11/x11.h create mode 100644 gb.httpd/AUTHORS create mode 120000 gb.httpd/COPYING create mode 100644 gb.httpd/ChangeLog create mode 120000 gb.httpd/INSTALL create mode 100644 gb.httpd/Makefile.am create mode 100644 gb.httpd/NEWS create mode 100644 gb.httpd/README create mode 120000 gb.httpd/acinclude.m4 create mode 120000 gb.httpd/component.am create mode 100644 gb.httpd/configure.ac create mode 120000 gb.httpd/gambas.h create mode 120000 gb.httpd/gb_common.h create mode 120000 gb.httpd/m4 create mode 120000 gb.httpd/reconf create mode 100644 gb.httpd/src/Makefile.am create mode 100644 gb.httpd/src/fdwatch.c create mode 100644 gb.httpd/src/fdwatch.h create mode 100644 gb.httpd/src/gb.httpd.component create mode 100644 gb.httpd/src/libhttpd.c create mode 100644 gb.httpd/src/libhttpd.h create mode 100644 gb.httpd/src/main.c create mode 100644 gb.httpd/src/main.h create mode 100644 gb.httpd/src/match.c create mode 100644 gb.httpd/src/match.h create mode 100644 gb.httpd/src/mime_encodings.h create mode 100644 gb.httpd/src/mime_types.h create mode 100644 gb.httpd/src/strerror.c create mode 100644 gb.httpd/src/tdate_parse.c create mode 100644 gb.httpd/src/tdate_parse.h create mode 100644 gb.httpd/src/thttpd.c create mode 100644 gb.httpd/src/thttpd.h create mode 100644 gb.httpd/src/timers.c create mode 100644 gb.httpd/src/timers.h create mode 100644 gb.httpd/src/version.h create mode 100644 gb.image.imlib/AUTHORS create mode 120000 gb.image.imlib/COPYING create mode 100644 gb.image.imlib/ChangeLog create mode 120000 gb.image.imlib/INSTALL create mode 100644 gb.image.imlib/Makefile.am create mode 100644 gb.image.imlib/NEWS create mode 100644 gb.image.imlib/README create mode 120000 gb.image.imlib/acinclude.m4 create mode 120000 gb.image.imlib/component.am create mode 100644 gb.image.imlib/configure.ac create mode 120000 gb.image.imlib/gambas.h create mode 120000 gb.image.imlib/gb.draw.h create mode 120000 gb.image.imlib/gb.image.h create mode 120000 gb.image.imlib/gb_common.h create mode 120000 gb.image.imlib/m4 create mode 120000 gb.image.imlib/reconf create mode 100644 gb.image.imlib/src/Makefile.am create mode 100644 gb.image.imlib/src/c_image.c create mode 100644 gb.image.imlib/src/c_image.h create mode 100644 gb.image.imlib/src/c_imlib.c create mode 100644 gb.image.imlib/src/c_imlib.h create mode 100644 gb.image.imlib/src/gb.image.imlib.component create mode 100644 gb.image.imlib/src/main.c create mode 100644 gb.image.imlib/src/main.h create mode 100644 gb.image.io/AUTHORS create mode 120000 gb.image.io/COPYING create mode 100644 gb.image.io/ChangeLog create mode 120000 gb.image.io/INSTALL create mode 100644 gb.image.io/Makefile.am create mode 100644 gb.image.io/NEWS create mode 100644 gb.image.io/README create mode 120000 gb.image.io/acinclude.m4 create mode 120000 gb.image.io/component.am create mode 100644 gb.image.io/configure.ac create mode 120000 gb.image.io/gambas.h create mode 120000 gb.image.io/gb.image.h create mode 120000 gb.image.io/gb_common.h create mode 120000 gb.image.io/m4 create mode 120000 gb.image.io/reconf create mode 100644 gb.image.io/src/Makefile.am create mode 100644 gb.image.io/src/c_image.c create mode 100644 gb.image.io/src/c_image.h create mode 100644 gb.image.io/src/gb.image.io.component create mode 100644 gb.image.io/src/main.c create mode 100644 gb.image.io/src/main.h create mode 100644 gb.jit.llvm/AUTHORS create mode 120000 gb.jit.llvm/COPYING create mode 100644 gb.jit.llvm/ChangeLog create mode 120000 gb.jit.llvm/INSTALL create mode 100644 gb.jit.llvm/Makefile.am create mode 100644 gb.jit.llvm/NEWS create mode 100644 gb.jit.llvm/README create mode 120000 gb.jit.llvm/acinclude.m4 create mode 120000 gb.jit.llvm/component.am create mode 100644 gb.jit.llvm/configure.ac create mode 120000 gb.jit.llvm/gambas.h create mode 120000 gb.jit.llvm/gb_common.h create mode 120000 gb.jit.llvm/m4 create mode 120000 gb.jit.llvm/reconf create mode 100644 gb.jit.llvm/src/Makefile.am create mode 100644 gb.jit.llvm/src/gb.jit.h create mode 100644 gb.jit.llvm/src/gb.jit.llvm.component create mode 100644 gb.jit.llvm/src/jit.h create mode 100644 gb.jit.llvm/src/jit_api.cpp create mode 100644 gb.jit.llvm/src/jit_codegen.cpp create mode 100644 gb.jit.llvm/src/jit_codegen_conv.h create mode 100644 gb.jit.llvm/src/jit_compile.cpp create mode 100644 gb.jit.llvm/src/jit_conv.cpp create mode 100644 gb.jit.llvm/src/jit_expressions.cpp create mode 100644 gb.jit.llvm/src/jit_gambas_pass.cpp create mode 100644 gb.jit.llvm/src/jit_gambas_pass.h create mode 100644 gb.jit.llvm/src/jit_read.cpp create mode 100644 gb.jit.llvm/src/jit_runtime.c create mode 100644 gb.jit.llvm/src/jit_runtime.h create mode 100644 gb.jit.llvm/src/main.cpp create mode 100644 gb.jit.llvm/src/main.h create mode 100644 gb.libxml/AUTHORS create mode 120000 gb.libxml/COPYING create mode 100644 gb.libxml/ChangeLog create mode 120000 gb.libxml/INSTALL create mode 100644 gb.libxml/Makefile.am create mode 100644 gb.libxml/NEWS create mode 100644 gb.libxml/README create mode 120000 gb.libxml/acinclude.m4 create mode 120000 gb.libxml/component.am create mode 100644 gb.libxml/configure.ac create mode 120000 gb.libxml/gambas.h create mode 120000 gb.libxml/m4 create mode 120000 gb.libxml/reconf create mode 100644 gb.libxml/src/CXMLDocument.c create mode 100644 gb.libxml/src/CXMLDocument.h create mode 100644 gb.libxml/src/CXMLNode.c create mode 100644 gb.libxml/src/CXMLNode.h create mode 100644 gb.libxml/src/CXMLReader.c create mode 100644 gb.libxml/src/CXMLReader.h create mode 100644 gb.libxml/src/CXMLWriter.c create mode 100644 gb.libxml/src/CXMLWriter.h create mode 100644 gb.libxml/src/Makefile.am create mode 100644 gb.libxml/src/gb.libxml.component create mode 100644 gb.libxml/src/libxml.kateproject create mode 100644 gb.libxml/src/main.c create mode 100644 gb.libxml/src/main.h create mode 100644 gb.media/AUTHORS create mode 120000 gb.media/COPYING create mode 100644 gb.media/ChangeLog create mode 120000 gb.media/INSTALL create mode 100644 gb.media/Makefile.am create mode 100644 gb.media/NEWS create mode 100644 gb.media/README create mode 120000 gb.media/acinclude.m4 create mode 120000 gb.media/component.am create mode 100644 gb.media/configure.ac create mode 120000 gb.media/gambas.h create mode 120000 gb.media/gb.image.h create mode 120000 gb.media/gb_common.h create mode 120000 gb.media/m4 create mode 120000 gb.media/reconf create mode 100644 gb.media/src/Makefile.am create mode 100644 gb.media/src/c_media.c create mode 100644 gb.media/src/c_media.h create mode 100644 gb.media/src/c_mediaplayer.c create mode 100644 gb.media/src/c_mediaplayer.h create mode 100644 gb.media/src/c_mediavideo.c create mode 100644 gb.media/src/c_mediavideo.h create mode 100644 gb.media/src/gb.media.component create mode 100644 gb.media/src/main.c create mode 100644 gb.media/src/main.h create mode 100644 gb.mime/AUTHORS create mode 120000 gb.mime/COPYING create mode 100644 gb.mime/ChangeLog create mode 120000 gb.mime/INSTALL create mode 100644 gb.mime/Makefile.am create mode 100644 gb.mime/NEWS create mode 100644 gb.mime/README create mode 120000 gb.mime/acinclude.m4 create mode 120000 gb.mime/component.am create mode 100644 gb.mime/configure.ac create mode 120000 gb.mime/gambas.h create mode 120000 gb.mime/gb_common.h create mode 120000 gb.mime/m4 create mode 120000 gb.mime/reconf create mode 100644 gb.mime/src/Makefile.am create mode 100644 gb.mime/src/c_mime.c create mode 100644 gb.mime/src/c_mime.h create mode 100644 gb.mime/src/c_mimemessage.c create mode 100644 gb.mime/src/c_mimemessage.h create mode 100644 gb.mime/src/c_mimepart.c create mode 100644 gb.mime/src/c_mimepart.h create mode 100644 gb.mime/src/gb.mime.component create mode 100644 gb.mime/src/main.c create mode 100644 gb.mime/src/main.h create mode 100644 gb.ncurses/AUTHORS create mode 120000 gb.ncurses/COPYING create mode 100644 gb.ncurses/ChangeLog create mode 120000 gb.ncurses/INSTALL create mode 100644 gb.ncurses/Makefile.am create mode 100644 gb.ncurses/NEWS create mode 100644 gb.ncurses/README create mode 120000 gb.ncurses/acinclude.m4 create mode 120000 gb.ncurses/component.am create mode 100644 gb.ncurses/configure.ac create mode 120000 gb.ncurses/gambas.h create mode 120000 gb.ncurses/gb_common.h create mode 120000 gb.ncurses/m4 create mode 120000 gb.ncurses/reconf create mode 100644 gb.ncurses/src/Makefile.am create mode 100644 gb.ncurses/src/c_color.c create mode 100644 gb.ncurses/src/c_color.h create mode 100644 gb.ncurses/src/c_input.c create mode 100644 gb.ncurses/src/c_input.h create mode 100644 gb.ncurses/src/c_key.c create mode 100644 gb.ncurses/src/c_key.h create mode 100644 gb.ncurses/src/c_screen.c create mode 100644 gb.ncurses/src/c_screen.h create mode 100644 gb.ncurses/src/c_window.c create mode 100644 gb.ncurses/src/c_window.h create mode 100644 gb.ncurses/src/gb.ncurses.component create mode 100644 gb.ncurses/src/main.c create mode 100644 gb.ncurses/src/main.h create mode 100644 gb.net.curl/AUTHORS create mode 120000 gb.net.curl/COPYING create mode 100644 gb.net.curl/ChangeLog create mode 120000 gb.net.curl/INSTALL create mode 100644 gb.net.curl/Makefile.am create mode 100644 gb.net.curl/NEWS create mode 100644 gb.net.curl/README create mode 120000 gb.net.curl/acinclude.m4 create mode 120000 gb.net.curl/component.am create mode 100644 gb.net.curl/configure.ac create mode 120000 gb.net.curl/gambas.h create mode 120000 gb.net.curl/gb_common.h create mode 120000 gb.net.curl/m4 create mode 120000 gb.net.curl/reconf create mode 100644 gb.net.curl/src/CCurl.c create mode 100644 gb.net.curl/src/CCurl.h create mode 100644 gb.net.curl/src/CFtpClient.c create mode 100644 gb.net.curl/src/CFtpClient.h create mode 100644 gb.net.curl/src/CHttpClient.c create mode 100644 gb.net.curl/src/CHttpClient.h create mode 100644 gb.net.curl/src/CNet.c create mode 100644 gb.net.curl/src/CNet.h create mode 100644 gb.net.curl/src/CProxy.c create mode 100644 gb.net.curl/src/CProxy.h create mode 100644 gb.net.curl/src/Makefile.am create mode 100644 gb.net.curl/src/gb.net.curl.component create mode 100644 gb.net.curl/src/gb.net.curl/.directory create mode 100644 gb.net.curl/src/gb.net.curl/.icon.png create mode 100644 gb.net.curl/src/gb.net.curl/.project create mode 100644 gb.net.curl/src/gb.net.curl/.src/Download.class create mode 100644 gb.net.curl/src/gb.net.curl/.src/DownloadManager.class create mode 100644 gb.net.curl/src/gb.net.curl/.src/HttpForm.class create mode 100644 gb.net.curl/src/gb.net.curl/.src/MMain.module create mode 100644 gb.net.curl/src/gbcurl.c create mode 100644 gb.net.curl/src/gbcurl.h create mode 100644 gb.net.curl/src/main.c create mode 100644 gb.net.curl/src/main.h create mode 100644 gb.net/AUTHORS create mode 120000 gb.net/COPYING create mode 100644 gb.net/ChangeLog create mode 120000 gb.net/INSTALL create mode 100644 gb.net/Makefile.am create mode 100644 gb.net/NEWS create mode 100644 gb.net/README create mode 120000 gb.net/acinclude.m4 create mode 120000 gb.net/component.am create mode 100644 gb.net/configure.ac create mode 120000 gb.net/gambas.h create mode 120000 gb.net/gb_common.h create mode 120000 gb.net/m4 create mode 120000 gb.net/reconf create mode 100644 gb.net/src/CDnsClient.c create mode 100644 gb.net/src/CDnsClient.h create mode 100644 gb.net/src/CNet.c create mode 100644 gb.net/src/CNet.h create mode 100644 gb.net/src/CSerialPort.c create mode 100644 gb.net/src/CSerialPort.h create mode 100644 gb.net/src/CServerSocket.c create mode 100644 gb.net/src/CServerSocket.h create mode 100644 gb.net/src/CSocket.c create mode 100644 gb.net/src/CSocket.h create mode 100644 gb.net/src/CUdpSocket.c create mode 100644 gb.net/src/CUdpSocket.h create mode 100644 gb.net/src/Makefile.am create mode 100644 gb.net/src/doc/README create mode 100644 gb.net/src/doc/changes.txt create mode 100644 gb.net/src/doc/threading.sxw create mode 100644 gb.net/src/gb.net.component create mode 100644 gb.net/src/gb_network.h create mode 100644 gb.net/src/main.c create mode 100644 gb.net/src/main.h create mode 100644 gb.net/src/speed.c create mode 100644 gb.net/src/speed.h create mode 100644 gb.net/src/tools.c create mode 100644 gb.net/src/tools.h create mode 100644 gb.openal/AUTHORS create mode 120000 gb.openal/COPYING create mode 100644 gb.openal/ChangeLog create mode 120000 gb.openal/INSTALL create mode 100644 gb.openal/Makefile.am create mode 100644 gb.openal/NEWS create mode 100644 gb.openal/README create mode 120000 gb.openal/acinclude.m4 create mode 120000 gb.openal/component.am create mode 100644 gb.openal/configure.ac create mode 120000 gb.openal/gambas.h create mode 120000 gb.openal/gb_common.h create mode 120000 gb.openal/m4 create mode 120000 gb.openal/reconf create mode 100644 gb.openal/src/Makefile.am create mode 100644 gb.openal/src/c_al.c create mode 100644 gb.openal/src/c_al.h create mode 100644 gb.openal/src/c_alc.c create mode 100644 gb.openal/src/c_alc.h create mode 100644 gb.openal/src/c_alure.c create mode 100644 gb.openal/src/c_alure.h create mode 100644 gb.openal/src/gb.openal.component create mode 100644 gb.openal/src/main.c create mode 100644 gb.openal/src/main.h create mode 100644 gb.opengl/AUTHORS create mode 120000 gb.opengl/COPYING create mode 100644 gb.opengl/ChangeLog create mode 120000 gb.opengl/INSTALL create mode 100644 gb.opengl/Makefile.am create mode 100644 gb.opengl/NEWS create mode 100644 gb.opengl/README create mode 120000 gb.opengl/acinclude.m4 create mode 120000 gb.opengl/component.am create mode 100644 gb.opengl/configure.ac create mode 120000 gb.opengl/gambas.h create mode 120000 gb.opengl/gb.image.h create mode 120000 gb.opengl/gb_common.h create mode 120000 gb.opengl/m4 create mode 120000 gb.opengl/reconf create mode 100644 gb.opengl/src/GL.c create mode 100644 gb.opengl/src/GL.h create mode 100644 gb.opengl/src/GLclipping.c create mode 100644 gb.opengl/src/GLclipping.h create mode 100644 gb.opengl/src/GLcolorLighting.c create mode 100644 gb.opengl/src/GLcolorLighting.h create mode 100644 gb.opengl/src/GLcoordTransf.c create mode 100644 gb.opengl/src/GLcoordTransf.h create mode 100644 gb.opengl/src/GLdisplayList.c create mode 100644 gb.opengl/src/GLdisplayList.h create mode 100644 gb.opengl/src/GLeval.c create mode 100644 gb.opengl/src/GLeval.h create mode 100644 gb.opengl/src/GLfog.c create mode 100644 gb.opengl/src/GLfog.h create mode 100644 gb.opengl/src/GLframeBufferOps.c create mode 100644 gb.opengl/src/GLframeBufferOps.h create mode 100644 gb.opengl/src/GLinfo.c create mode 100644 gb.opengl/src/GLinfo.h create mode 100644 gb.opengl/src/GLmodesExec.c create mode 100644 gb.opengl/src/GLmodesExec.h create mode 100644 gb.opengl/src/GLpixelOperations.c create mode 100644 gb.opengl/src/GLpixelOperations.h create mode 100644 gb.opengl/src/GLprimitives.c create mode 100644 gb.opengl/src/GLprimitives.h create mode 100644 gb.opengl/src/GLrasterization.c create mode 100644 gb.opengl/src/GLrasterization.h create mode 100644 gb.opengl/src/GLselectFeedback.c create mode 100644 gb.opengl/src/GLselectFeedback.h create mode 100644 gb.opengl/src/GLtextureMapping.c create mode 100644 gb.opengl/src/GLtextureMapping.h create mode 100644 gb.opengl/src/Makefile.am create mode 100644 gb.opengl/src/framebufferobject.c create mode 100644 gb.opengl/src/framebufferobject.h create mode 100644 gb.opengl/src/gb.gl.h create mode 100644 gb.opengl/src/gb.opengl.component create mode 100644 gb.opengl/src/glsl/GL.c create mode 100644 gb.opengl/src/glsl/GL.h create mode 100644 gb.opengl/src/glsl/GLattributes.c create mode 100644 gb.opengl/src/glsl/GLattributes.h create mode 100644 gb.opengl/src/glsl/GLinfo.h create mode 100644 gb.opengl/src/glsl/GLprogram.c create mode 100644 gb.opengl/src/glsl/GLprogram.h create mode 100644 gb.opengl/src/glsl/GLshader.c create mode 100644 gb.opengl/src/glsl/GLshader.h create mode 100644 gb.opengl/src/glsl/GLuniform.c create mode 100644 gb.opengl/src/glsl/GLuniform.h create mode 100644 gb.opengl/src/glsl/Makefile.am create mode 100644 gb.opengl/src/glsl/gb.opengl.glsl.component create mode 100644 gb.opengl/src/glsl/main.c create mode 100644 gb.opengl/src/glsl/main.h create mode 100644 gb.opengl/src/glu/GLU.c create mode 100644 gb.opengl/src/glu/GLU.h create mode 100644 gb.opengl/src/glu/GLUcoordTransf.c create mode 100644 gb.opengl/src/glu/GLUcoordTransf.h create mode 100644 gb.opengl/src/glu/GLUnurb.c create mode 100644 gb.opengl/src/glu/GLUnurb.h create mode 100644 gb.opengl/src/glu/GLUproject.c create mode 100644 gb.opengl/src/glu/GLUproject.h create mode 100644 gb.opengl/src/glu/GLUquadratic.c create mode 100644 gb.opengl/src/glu/GLUquadratic.h create mode 100644 gb.opengl/src/glu/GLUtextureImage.c create mode 100644 gb.opengl/src/glu/GLUtextureImage.h create mode 100644 gb.opengl/src/glu/Makefile.am create mode 100644 gb.opengl/src/glu/cglunurb.c create mode 100644 gb.opengl/src/glu/cglunurb.h create mode 100644 gb.opengl/src/glu/cgluquadric.c create mode 100644 gb.opengl/src/glu/cgluquadric.h create mode 100644 gb.opengl/src/glu/gb.opengl.glu.component create mode 100644 gb.opengl/src/glu/main.c create mode 100644 gb.opengl/src/glu/main.h create mode 100644 gb.opengl/src/main.c create mode 100644 gb.opengl/src/main.h create mode 100644 gb.opengl/src/sge/Makefile.am create mode 100644 gb.opengl/src/sge/cmd2model.c create mode 100644 gb.opengl/src/sge/cmd2model.h create mode 100644 gb.opengl/src/sge/cmd2object.c create mode 100644 gb.opengl/src/sge/cmd2object.h create mode 100644 gb.opengl/src/sge/gb.opengl.sge.component create mode 100644 gb.opengl/src/sge/main.c create mode 100644 gb.opengl/src/sge/main.h create mode 100644 gb.openssl/AUTHORS create mode 100644 gb.openssl/COPYING create mode 100644 gb.openssl/COPYING.GPL create mode 100644 gb.openssl/ChangeLog create mode 120000 gb.openssl/INSTALL create mode 100644 gb.openssl/Makefile.am create mode 100644 gb.openssl/NEWS create mode 100644 gb.openssl/README create mode 120000 gb.openssl/acinclude.m4 create mode 120000 gb.openssl/component.am create mode 100644 gb.openssl/configure.ac create mode 120000 gb.openssl/gambas.h create mode 120000 gb.openssl/gb_common.h create mode 120000 gb.openssl/m4 create mode 100644 gb.openssl/openssl-test/.directory create mode 100644 gb.openssl/openssl-test/.icon.png create mode 100644 gb.openssl/openssl-test/.project create mode 100644 gb.openssl/openssl-test/.src/Main.module create mode 120000 gb.openssl/reconf create mode 100644 gb.openssl/src/Makefile.am create mode 100644 gb.openssl/src/c_cipher.c create mode 100644 gb.openssl/src/c_cipher.h create mode 100644 gb.openssl/src/c_digest.c create mode 100644 gb.openssl/src/c_digest.h create mode 100644 gb.openssl/src/c_hmac.c create mode 100644 gb.openssl/src/c_hmac.h create mode 100644 gb.openssl/src/c_openssl.c create mode 100644 gb.openssl/src/c_openssl.h create mode 100644 gb.openssl/src/gb.openssl.component create mode 100644 gb.openssl/src/main.c create mode 100644 gb.openssl/src/main.h create mode 100644 gb.pcre/AUTHORS create mode 120000 gb.pcre/COPYING create mode 100644 gb.pcre/ChangeLog create mode 120000 gb.pcre/INSTALL create mode 100644 gb.pcre/Makefile.am create mode 100644 gb.pcre/NEWS create mode 100644 gb.pcre/README create mode 120000 gb.pcre/acinclude.m4 create mode 120000 gb.pcre/component.am create mode 100644 gb.pcre/configure.ac create mode 120000 gb.pcre/gambas.h create mode 120000 gb.pcre/gb_common.h create mode 120000 gb.pcre/m4 create mode 120000 gb.pcre/reconf create mode 100644 gb.pcre/src/Makefile.am create mode 100644 gb.pcre/src/README create mode 100644 gb.pcre/src/gb.pcre.component create mode 100644 gb.pcre/src/gb.pcre.h create mode 100644 gb.pcre/src/main.c create mode 100644 gb.pcre/src/main.h create mode 100644 gb.pcre/src/regexp.c create mode 100644 gb.pcre/src/regexp.h create mode 100644 gb.pcre/src/regexp2.c create mode 100644 gb.pcre/src/regexp2.h create mode 100644 gb.pdf/AUTHORS create mode 120000 gb.pdf/COPYING create mode 100644 gb.pdf/ChangeLog create mode 120000 gb.pdf/INSTALL create mode 100644 gb.pdf/Makefile.am create mode 100644 gb.pdf/NEWS create mode 100644 gb.pdf/README create mode 120000 gb.pdf/acinclude.m4 create mode 120000 gb.pdf/component.am create mode 100644 gb.pdf/configure.ac create mode 120000 gb.pdf/gambas.h create mode 120000 gb.pdf/gb.gtk.h create mode 120000 gb.pdf/gb.image.h create mode 120000 gb.pdf/gb_common.h create mode 120000 gb.pdf/m4 create mode 120000 gb.pdf/reconf create mode 100644 gb.pdf/src/CPdfDocument.cpp create mode 100644 gb.pdf/src/CPdfDocument.h create mode 100644 gb.pdf/src/Makefile.am create mode 100644 gb.pdf/src/gb.pdf.component create mode 100644 gb.pdf/src/main.cpp create mode 100644 gb.pdf/src/main.h create mode 100644 gb.poppler/AUTHORS create mode 120000 gb.poppler/COPYING create mode 100644 gb.poppler/ChangeLog create mode 120000 gb.poppler/INSTALL create mode 100644 gb.poppler/Makefile.am create mode 100644 gb.poppler/NEWS create mode 100644 gb.poppler/README create mode 120000 gb.poppler/acinclude.m4 create mode 120000 gb.poppler/component.am create mode 100644 gb.poppler/configure.ac create mode 120000 gb.poppler/gambas.h create mode 120000 gb.poppler/gb.geom.h create mode 120000 gb.poppler/gb.image.h create mode 120000 gb.poppler/gb_common.h create mode 120000 gb.poppler/m4 create mode 120000 gb.poppler/reconf create mode 100644 gb.poppler/src/Makefile.am create mode 100644 gb.poppler/src/c_pdf_document.cpp create mode 100644 gb.poppler/src/c_pdf_document.h create mode 100644 gb.poppler/src/gb.poppler.component create mode 100644 gb.poppler/src/main.cpp create mode 100644 gb.poppler/src/main.h create mode 100644 gb.qt4/AUTHORS create mode 120000 gb.qt4/COPYING create mode 100644 gb.qt4/ChangeLog create mode 120000 gb.qt4/INSTALL create mode 100644 gb.qt4/Makefile.am create mode 100644 gb.qt4/NEWS create mode 100644 gb.qt4/README create mode 120000 gb.qt4/acinclude.m4 create mode 120000 gb.qt4/component.am create mode 100644 gb.qt4/configure.ac create mode 120000 gb.qt4/gambas.h create mode 120000 gb.qt4/gb.draw.h create mode 120000 gb.qt4/gb.eval.h create mode 120000 gb.qt4/gb.geom.h create mode 120000 gb.qt4/gb.gl.h create mode 120000 gb.qt4/gb.image.h create mode 120000 gb.qt4/gb.paint.h create mode 100644 gb.qt4/gb.qt.am create mode 120000 gb.qt4/gb_common.h create mode 120000 gb.qt4/gbc_read_common.h create mode 120000 gb.qt4/m4 create mode 120000 gb.qt4/reconf create mode 100644 gb.qt4/share/gb.form.action.h create mode 100644 gb.qt4/share/gb.form.arrangement.h create mode 100644 gb.qt4/share/gb.form.const.h create mode 100644 gb.qt4/share/gb.form.font.h create mode 100644 gb.qt4/share/gb.form.print.h create mode 100644 gb.qt4/share/gb.form.properties.h create mode 100644 gb.qt4/share/gb.form.trayicon.h create mode 100644 gb.qt4/share/gb.form.trayicon.large.h create mode 100644 gb.qt4/src/CButton.cpp create mode 100644 gb.qt4/src/CButton.h create mode 100644 gb.qt4/src/CCheckBox.cpp create mode 100644 gb.qt4/src/CCheckBox.h create mode 100644 gb.qt4/src/CClipboard.cpp create mode 100644 gb.qt4/src/CClipboard.h create mode 100644 gb.qt4/src/CColor.cpp create mode 100644 gb.qt4/src/CColor.h create mode 100644 gb.qt4/src/CConst.cpp create mode 100644 gb.qt4/src/CConst.h create mode 100644 gb.qt4/src/CContainer.cpp create mode 100644 gb.qt4/src/CContainer.h create mode 100644 gb.qt4/src/CDialog.cpp create mode 100644 gb.qt4/src/CDialog.h create mode 100644 gb.qt4/src/CDraw.cpp create mode 100644 gb.qt4/src/CDraw.h create mode 100644 gb.qt4/src/CDrawingArea.cpp create mode 100644 gb.qt4/src/CDrawingArea.h create mode 100644 gb.qt4/src/CEmbedder.cpp create mode 100644 gb.qt4/src/CEmbedder.h create mode 100644 gb.qt4/src/CFont.cpp create mode 100644 gb.qt4/src/CFont.h create mode 100644 gb.qt4/src/CImage.cpp create mode 100644 gb.qt4/src/CImage.h create mode 100644 gb.qt4/src/CKey.cpp create mode 100644 gb.qt4/src/CKey.h create mode 100644 gb.qt4/src/CMenu.cpp create mode 100644 gb.qt4/src/CMenu.h create mode 100644 gb.qt4/src/CMouse.cpp create mode 100644 gb.qt4/src/CMouse.h create mode 100644 gb.qt4/src/CPanel.cpp create mode 100644 gb.qt4/src/CPanel.h create mode 100644 gb.qt4/src/CPicture.cpp create mode 100644 gb.qt4/src/CPicture.h create mode 100644 gb.qt4/src/CRadioButton.cpp create mode 100644 gb.qt4/src/CRadioButton.h create mode 100644 gb.qt4/src/CScreen.cpp create mode 100644 gb.qt4/src/CScreen.h create mode 100644 gb.qt4/src/CScrollBar.cpp create mode 100644 gb.qt4/src/CScrollBar.h create mode 100644 gb.qt4/src/CSlider.cpp create mode 100644 gb.qt4/src/CSlider.h create mode 100644 gb.qt4/src/CStyle.cpp create mode 100644 gb.qt4/src/CStyle.h create mode 100644 gb.qt4/src/CTabStrip.cpp create mode 100644 gb.qt4/src/CTabStrip.h create mode 100644 gb.qt4/src/CTextArea.cpp create mode 100644 gb.qt4/src/CTextArea.h create mode 100644 gb.qt4/src/CTextBox.cpp create mode 100644 gb.qt4/src/CTextBox.h create mode 100644 gb.qt4/src/CWatch.cpp create mode 100644 gb.qt4/src/CWatch.h create mode 100644 gb.qt4/src/CWatcher.cpp create mode 100644 gb.qt4/src/CWatcher.h create mode 100644 gb.qt4/src/CWidget.cpp create mode 100644 gb.qt4/src/CWidget.h create mode 100644 gb.qt4/src/CWindow.cpp create mode 100644 gb.qt4/src/CWindow.h create mode 100644 gb.qt4/src/Makefile.am create mode 100644 gb.qt4/src/canimation.cpp create mode 100644 gb.qt4/src/canimation.h create mode 100644 gb.qt4/src/cpaint_impl.cpp create mode 100644 gb.qt4/src/cpaint_impl.h create mode 100644 gb.qt4/src/cprinter.cpp create mode 100644 gb.qt4/src/cprinter.h create mode 100644 gb.qt4/src/csvgimage.cpp create mode 100644 gb.qt4/src/csvgimage.h create mode 100644 gb.qt4/src/ctrayicon.cpp create mode 100644 gb.qt4/src/ctrayicon.h create mode 100644 gb.qt4/src/desktop.c create mode 100644 gb.qt4/src/desktop.h create mode 100644 gb.qt4/src/ext/CDial.cpp create mode 100644 gb.qt4/src/ext/CDial.h create mode 100644 gb.qt4/src/ext/CEditor.cpp create mode 100644 gb.qt4/src/ext/CEditor.h create mode 100644 gb.qt4/src/ext/CLCDNumber.cpp create mode 100644 gb.qt4/src/ext/CLCDNumber.h create mode 100644 gb.qt4/src/ext/CTextEdit.cpp create mode 100644 gb.qt4/src/ext/CTextEdit.h create mode 100644 gb.qt4/src/ext/Makefile.am create mode 100644 gb.qt4/src/ext/garray.cpp create mode 100644 gb.qt4/src/ext/garray.h create mode 100644 gb.qt4/src/ext/gb.qt4.ext.component create mode 100644 gb.qt4/src/ext/gdocument.cpp create mode 100644 gb.qt4/src/ext/gdocument.h create mode 100644 gb.qt4/src/ext/gstring.cpp create mode 100644 gb.qt4/src/ext/gstring.h create mode 100644 gb.qt4/src/ext/gview.cpp create mode 100644 gb.qt4/src/ext/gview.h create mode 100644 gb.qt4/src/ext/main.cpp create mode 100644 gb.qt4/src/ext/main.h create mode 100644 gb.qt4/src/fix_style.cpp create mode 100644 gb.qt4/src/fix_style.h create mode 100644 gb.qt4/src/gb.qt.h create mode 100644 gb.qt4/src/gb.qt4.component create mode 100644 gb.qt4/src/main.cpp create mode 100644 gb.qt4/src/main.h create mode 100644 gb.qt4/src/opengl/CGLarea.cpp create mode 100644 gb.qt4/src/opengl/CGLarea.h create mode 100644 gb.qt4/src/opengl/Makefile.am create mode 100644 gb.qt4/src/opengl/gb.qt4.opengl.component create mode 100644 gb.qt4/src/opengl/main.cpp create mode 100644 gb.qt4/src/opengl/main.h create mode 100644 gb.qt4/src/trayicon.xpm create mode 100644 gb.qt4/src/webkit/Makefile.am create mode 100644 gb.qt4/src/webkit/ccookiejar.cpp create mode 100644 gb.qt4/src/webkit/ccookiejar.h create mode 100644 gb.qt4/src/webkit/control/webview.png create mode 100644 gb.qt4/src/webkit/cwebdownload.cpp create mode 100644 gb.qt4/src/webkit/cwebdownload.h create mode 100644 gb.qt4/src/webkit/cwebelement.cpp create mode 100644 gb.qt4/src/webkit/cwebelement.h create mode 100644 gb.qt4/src/webkit/cwebframe.cpp create mode 100644 gb.qt4/src/webkit/cwebframe.h create mode 100644 gb.qt4/src/webkit/cwebhittest.cpp create mode 100644 gb.qt4/src/webkit/cwebhittest.h create mode 100644 gb.qt4/src/webkit/cwebsettings.cpp create mode 100644 gb.qt4/src/webkit/cwebsettings.h create mode 100644 gb.qt4/src/webkit/cwebview.cpp create mode 100644 gb.qt4/src/webkit/cwebview.h create mode 100644 gb.qt4/src/webkit/gb.qt4.webkit.component create mode 100644 gb.qt4/src/webkit/main.cpp create mode 100644 gb.qt4/src/webkit/main.h create mode 100644 gb.qt4/src/webview/Makefile.am create mode 100644 gb.qt4/src/webview/c_websettings.cpp create mode 100644 gb.qt4/src/webview/c_websettings.h create mode 100644 gb.qt4/src/webview/c_webview.cpp create mode 100644 gb.qt4/src/webview/c_webview.h create mode 100644 gb.qt4/src/webview/gb.qt4.webview.component create mode 100644 gb.qt4/src/webview/jsonwriter.cpp create mode 100644 gb.qt4/src/webview/jsonwriter.h create mode 100644 gb.qt4/src/webview/main.cpp create mode 100644 gb.qt4/src/webview/main.h create mode 100644 gb.qt4/src/x11.c create mode 100644 gb.qt4/src/x11.h create mode 100644 gb.qt5/AUTHORS create mode 120000 gb.qt5/COPYING create mode 100644 gb.qt5/ChangeLog create mode 120000 gb.qt5/INSTALL create mode 100644 gb.qt5/Makefile.am create mode 100644 gb.qt5/NEWS create mode 100644 gb.qt5/README create mode 120000 gb.qt5/acinclude.m4 create mode 120000 gb.qt5/component.am create mode 100644 gb.qt5/configure.ac create mode 120000 gb.qt5/gambas.h create mode 120000 gb.qt5/gb.draw.h create mode 120000 gb.qt5/gb.eval.h create mode 120000 gb.qt5/gb.geom.h create mode 120000 gb.qt5/gb.gl.h create mode 120000 gb.qt5/gb.image.h create mode 120000 gb.qt5/gb.paint.h create mode 100644 gb.qt5/gb.qt.am create mode 120000 gb.qt5/gb_common.h create mode 120000 gb.qt5/m4 create mode 120000 gb.qt5/reconf create mode 120000 gb.qt5/share create mode 120000 gb.qt5/src/CButton.cpp create mode 120000 gb.qt5/src/CButton.h create mode 120000 gb.qt5/src/CCheckBox.cpp create mode 120000 gb.qt5/src/CCheckBox.h create mode 120000 gb.qt5/src/CClipboard.cpp create mode 120000 gb.qt5/src/CClipboard.h create mode 120000 gb.qt5/src/CColor.cpp create mode 120000 gb.qt5/src/CColor.h create mode 120000 gb.qt5/src/CConst.cpp create mode 120000 gb.qt5/src/CConst.h create mode 120000 gb.qt5/src/CContainer.cpp create mode 120000 gb.qt5/src/CContainer.h create mode 120000 gb.qt5/src/CDialog.cpp create mode 120000 gb.qt5/src/CDialog.h create mode 120000 gb.qt5/src/CDraw.cpp create mode 120000 gb.qt5/src/CDraw.h create mode 120000 gb.qt5/src/CDrawingArea.cpp create mode 120000 gb.qt5/src/CDrawingArea.h create mode 120000 gb.qt5/src/CFont.cpp create mode 120000 gb.qt5/src/CFont.h create mode 120000 gb.qt5/src/CImage.cpp create mode 120000 gb.qt5/src/CImage.h create mode 120000 gb.qt5/src/CKey.cpp create mode 120000 gb.qt5/src/CKey.h create mode 120000 gb.qt5/src/CMenu.cpp create mode 120000 gb.qt5/src/CMenu.h create mode 120000 gb.qt5/src/CMouse.cpp create mode 120000 gb.qt5/src/CMouse.h create mode 120000 gb.qt5/src/CPanel.cpp create mode 120000 gb.qt5/src/CPanel.h create mode 120000 gb.qt5/src/CPicture.cpp create mode 120000 gb.qt5/src/CPicture.h create mode 120000 gb.qt5/src/CRadioButton.cpp create mode 120000 gb.qt5/src/CRadioButton.h create mode 120000 gb.qt5/src/CScreen.cpp create mode 120000 gb.qt5/src/CScreen.h create mode 120000 gb.qt5/src/CScrollBar.cpp create mode 120000 gb.qt5/src/CScrollBar.h create mode 120000 gb.qt5/src/CSlider.cpp create mode 120000 gb.qt5/src/CSlider.h create mode 120000 gb.qt5/src/CStyle.cpp create mode 120000 gb.qt5/src/CStyle.h create mode 120000 gb.qt5/src/CTabStrip.cpp create mode 120000 gb.qt5/src/CTabStrip.h create mode 120000 gb.qt5/src/CTextArea.cpp create mode 120000 gb.qt5/src/CTextArea.h create mode 120000 gb.qt5/src/CTextBox.cpp create mode 120000 gb.qt5/src/CTextBox.h create mode 120000 gb.qt5/src/CWatch.cpp create mode 120000 gb.qt5/src/CWatch.h create mode 120000 gb.qt5/src/CWatcher.cpp create mode 120000 gb.qt5/src/CWatcher.h create mode 120000 gb.qt5/src/CWidget.cpp create mode 120000 gb.qt5/src/CWidget.h create mode 120000 gb.qt5/src/CWindow.cpp create mode 120000 gb.qt5/src/CWindow.h create mode 100644 gb.qt5/src/Makefile.am create mode 120000 gb.qt5/src/canimation.cpp create mode 120000 gb.qt5/src/canimation.h create mode 120000 gb.qt5/src/cpaint_impl.cpp create mode 120000 gb.qt5/src/cpaint_impl.h create mode 120000 gb.qt5/src/cprinter.cpp create mode 120000 gb.qt5/src/cprinter.h create mode 120000 gb.qt5/src/csvgimage.cpp create mode 120000 gb.qt5/src/csvgimage.h create mode 120000 gb.qt5/src/ctrayicon.cpp create mode 120000 gb.qt5/src/ctrayicon.h create mode 120000 gb.qt5/src/ext/CDial.cpp create mode 120000 gb.qt5/src/ext/CDial.h create mode 120000 gb.qt5/src/ext/CLCDNumber.cpp create mode 120000 gb.qt5/src/ext/CLCDNumber.h create mode 120000 gb.qt5/src/ext/CTextEdit.cpp create mode 120000 gb.qt5/src/ext/CTextEdit.h create mode 100644 gb.qt5/src/ext/Makefile.am create mode 100644 gb.qt5/src/ext/gb.qt5.ext.component create mode 100644 gb.qt5/src/ext/main.cpp create mode 100644 gb.qt5/src/ext/main.h create mode 120000 gb.qt5/src/fix_style.cpp create mode 120000 gb.qt5/src/fix_style.h create mode 120000 gb.qt5/src/gb.qt.h create mode 100644 gb.qt5/src/gb.qt.platform.h create mode 100644 gb.qt5/src/gb.qt5.component create mode 100644 gb.qt5/src/main.cpp create mode 100644 gb.qt5/src/main.h create mode 100644 gb.qt5/src/opengl/CGLarea.cpp create mode 100644 gb.qt5/src/opengl/CGLarea.h create mode 100644 gb.qt5/src/opengl/COldGLarea.cpp create mode 100644 gb.qt5/src/opengl/COldGLarea.h create mode 100644 gb.qt5/src/opengl/Makefile.am create mode 100644 gb.qt5/src/opengl/gb.qt5.opengl.component create mode 100644 gb.qt5/src/opengl/main.cpp create mode 100644 gb.qt5/src/opengl/main.h create mode 120000 gb.qt5/src/trayicon.xpm create mode 100644 gb.qt5/src/wayland/Makefile.am create mode 100644 gb.qt5/src/wayland/gb.qt5.wayland.component create mode 100644 gb.qt5/src/wayland/main.cpp create mode 100644 gb.qt5/src/wayland/main.h create mode 100644 gb.qt5/src/webkit/Makefile.am create mode 120000 gb.qt5/src/webkit/ccookiejar.cpp create mode 120000 gb.qt5/src/webkit/ccookiejar.h create mode 100644 gb.qt5/src/webkit/control/webview.png create mode 120000 gb.qt5/src/webkit/cwebdownload.cpp create mode 120000 gb.qt5/src/webkit/cwebdownload.h create mode 120000 gb.qt5/src/webkit/cwebelement.cpp create mode 120000 gb.qt5/src/webkit/cwebelement.h create mode 120000 gb.qt5/src/webkit/cwebframe.cpp create mode 120000 gb.qt5/src/webkit/cwebframe.h create mode 120000 gb.qt5/src/webkit/cwebhittest.cpp create mode 120000 gb.qt5/src/webkit/cwebhittest.h create mode 120000 gb.qt5/src/webkit/cwebsettings.cpp create mode 120000 gb.qt5/src/webkit/cwebsettings.h create mode 120000 gb.qt5/src/webkit/cwebview.cpp create mode 120000 gb.qt5/src/webkit/cwebview.h create mode 100644 gb.qt5/src/webkit/gb.qt5.webkit.component create mode 120000 gb.qt5/src/webkit/main.cpp create mode 120000 gb.qt5/src/webkit/main.h create mode 100644 gb.qt5/src/webview/Makefile.am create mode 100644 gb.qt5/src/webview/c_websettings.cpp create mode 100644 gb.qt5/src/webview/c_websettings.h create mode 100644 gb.qt5/src/webview/c_webview.cpp create mode 100644 gb.qt5/src/webview/c_webview.h create mode 100644 gb.qt5/src/webview/gb.qt5.webview.component create mode 100644 gb.qt5/src/webview/main.cpp create mode 100644 gb.qt5/src/webview/main.h create mode 100644 gb.qt5/src/x11/Makefile.am create mode 100644 gb.qt5/src/x11/gb.qt5.x11.component create mode 100644 gb.qt5/src/x11/main.cpp create mode 100644 gb.qt5/src/x11/main.h create mode 120000 gb.qt5/src/x11/x11.c create mode 120000 gb.qt5/src/x11/x11.h create mode 100644 gb.sdl.sound/AUTHORS create mode 120000 gb.sdl.sound/COPYING create mode 100644 gb.sdl.sound/ChangeLog create mode 120000 gb.sdl.sound/INSTALL create mode 100644 gb.sdl.sound/Makefile.am create mode 100644 gb.sdl.sound/NEWS create mode 100644 gb.sdl.sound/README create mode 120000 gb.sdl.sound/acinclude.m4 create mode 120000 gb.sdl.sound/component.am create mode 100644 gb.sdl.sound/configure.ac create mode 120000 gb.sdl.sound/gambas.h create mode 120000 gb.sdl.sound/gb_common.h create mode 120000 gb.sdl.sound/m4 create mode 120000 gb.sdl.sound/reconf create mode 100644 gb.sdl.sound/src/Makefile.am create mode 100644 gb.sdl.sound/src/cdrom.c create mode 100644 gb.sdl.sound/src/cdrom.h create mode 100644 gb.sdl.sound/src/gb.sdl.sound.component create mode 100644 gb.sdl.sound/src/main.c create mode 100644 gb.sdl.sound/src/main.h create mode 100644 gb.sdl.sound/src/sound.c create mode 100644 gb.sdl.sound/src/sound.h create mode 100644 gb.sdl/AUTHORS create mode 120000 gb.sdl/COPYING create mode 100644 gb.sdl/ChangeLog create mode 120000 gb.sdl/INSTALL create mode 100644 gb.sdl/Makefile.am create mode 100644 gb.sdl/NEWS create mode 100644 gb.sdl/README create mode 120000 gb.sdl/acinclude.m4 create mode 120000 gb.sdl/component.am create mode 100644 gb.sdl/configure.ac create mode 120000 gb.sdl/gambas.h create mode 120000 gb.sdl/gb.image.h create mode 120000 gb.sdl/gb_common.h create mode 120000 gb.sdl/m4 create mode 120000 gb.sdl/reconf create mode 100644 gb.sdl/src/Cconst.cpp create mode 100644 gb.sdl/src/Cconst.h create mode 100644 gb.sdl/src/Cdesktop.cpp create mode 100644 gb.sdl/src/Cdesktop.h create mode 100644 gb.sdl/src/Cdraw.cpp create mode 100644 gb.sdl/src/Cdraw.h create mode 100644 gb.sdl/src/Cfont.cpp create mode 100644 gb.sdl/src/Cfont.h create mode 100644 gb.sdl/src/Cimage.cpp create mode 100644 gb.sdl/src/Cimage.h create mode 100644 gb.sdl/src/Cjoystick.cpp create mode 100644 gb.sdl/src/Cjoystick.h create mode 100644 gb.sdl/src/Ckey.cpp create mode 100644 gb.sdl/src/Ckey.h create mode 100644 gb.sdl/src/Cmouse.cpp create mode 100644 gb.sdl/src/Cmouse.h create mode 100644 gb.sdl/src/Cwindow.cpp create mode 100644 gb.sdl/src/Cwindow.h create mode 100644 gb.sdl/src/Makefile.am create mode 100644 gb.sdl/src/SDL_h.h create mode 100644 gb.sdl/src/SDLapp.cpp create mode 100644 gb.sdl/src/SDLapp.h create mode 100644 gb.sdl/src/SDLcore.cpp create mode 100644 gb.sdl/src/SDLcore.h create mode 100644 gb.sdl/src/SDLcursor.cpp create mode 100644 gb.sdl/src/SDLcursor.h create mode 100644 gb.sdl/src/SDLdebug.cpp create mode 100644 gb.sdl/src/SDLdebug.h create mode 100644 gb.sdl/src/SDLerror.cpp create mode 100644 gb.sdl/src/SDLerror.h create mode 100644 gb.sdl/src/SDLfont.cpp create mode 100644 gb.sdl/src/SDLfont.h create mode 100644 gb.sdl/src/SDLgfx.cpp create mode 100644 gb.sdl/src/SDLgfx.h create mode 100644 gb.sdl/src/SDLosrender.cpp create mode 100644 gb.sdl/src/SDLosrender.h create mode 100644 gb.sdl/src/SDLsurface.cpp create mode 100644 gb.sdl/src/SDLsurface.h create mode 100644 gb.sdl/src/SDLtexture.cpp create mode 100644 gb.sdl/src/SDLtexture.h create mode 100644 gb.sdl/src/SDLwindow.cpp create mode 100644 gb.sdl/src/SDLwindow.h create mode 100644 gb.sdl/src/default_font.h create mode 100644 gb.sdl/src/gb.sdl.component create mode 100644 gb.sdl/src/main.cpp create mode 100644 gb.sdl/src/main.h create mode 100644 gb.sdl2/AUTHORS create mode 120000 gb.sdl2/COPYING create mode 100644 gb.sdl2/ChangeLog create mode 120000 gb.sdl2/INSTALL create mode 100644 gb.sdl2/Makefile.am create mode 100644 gb.sdl2/NEWS create mode 100644 gb.sdl2/README create mode 120000 gb.sdl2/acinclude.m4 create mode 120000 gb.sdl2/component.am create mode 100644 gb.sdl2/configure.ac create mode 120000 gb.sdl2/gambas.h create mode 120000 gb.sdl2/gb.geom.h create mode 120000 gb.sdl2/gb.image.h create mode 120000 gb.sdl2/gb_common.h create mode 120000 gb.sdl2/gb_list.h create mode 120000 gb.sdl2/gb_list_temp.h create mode 120000 gb.sdl2/m4 create mode 120000 gb.sdl2/reconf create mode 100644 gb.sdl2/src/Makefile.am create mode 100644 gb.sdl2/src/audio/Makefile.am create mode 100644 gb.sdl2/src/audio/c_channel.c create mode 100644 gb.sdl2/src/audio/c_channel.h create mode 100644 gb.sdl2/src/audio/c_music.c create mode 100644 gb.sdl2/src/audio/c_music.h create mode 100644 gb.sdl2/src/audio/c_sound.c create mode 100644 gb.sdl2/src/audio/c_sound.h create mode 100644 gb.sdl2/src/audio/gb.sdl2.audio.component create mode 100644 gb.sdl2/src/audio/main.c create mode 100644 gb.sdl2/src/audio/main.h create mode 100644 gb.sdl2/src/c_draw.c create mode 100644 gb.sdl2/src/c_draw.h create mode 100644 gb.sdl2/src/c_font.c create mode 100644 gb.sdl2/src/c_font.h create mode 100644 gb.sdl2/src/c_image.c create mode 100644 gb.sdl2/src/c_image.h create mode 100644 gb.sdl2/src/c_key.c create mode 100644 gb.sdl2/src/c_key.h create mode 100644 gb.sdl2/src/c_mouse.c create mode 100644 gb.sdl2/src/c_mouse.h create mode 100644 gb.sdl2/src/c_window.c create mode 100644 gb.sdl2/src/c_window.h create mode 100644 gb.sdl2/src/default_font.c create mode 100644 gb.sdl2/src/default_font.h create mode 100644 gb.sdl2/src/default_font_data.h create mode 100644 gb.sdl2/src/gb.sdl2.component create mode 100644 gb.sdl2/src/main.c create mode 100644 gb.sdl2/src/main.h create mode 100644 gb.v4l/AUTHORS create mode 120000 gb.v4l/COPYING create mode 100644 gb.v4l/ChangeLog create mode 120000 gb.v4l/INSTALL create mode 100644 gb.v4l/Makefile.am create mode 100644 gb.v4l/NEWS create mode 100644 gb.v4l/README create mode 120000 gb.v4l/acinclude.m4 create mode 120000 gb.v4l/component.am create mode 100644 gb.v4l/configure.ac create mode 120000 gb.v4l/gambas.h create mode 120000 gb.v4l/gb.image.h create mode 120000 gb.v4l/gb_common.h create mode 120000 gb.v4l/m4 create mode 100644 gb.v4l/orig/video-capture-0.2.tar.gz create mode 120000 gb.v4l/reconf create mode 100644 gb.v4l/src/CConverters.c create mode 100644 gb.v4l/src/CWebcam.c create mode 100644 gb.v4l/src/CWebcam.h create mode 100644 gb.v4l/src/Makefile.am create mode 100644 gb.v4l/src/gb.v4l.component create mode 100644 gb.v4l/src/gv4l2.c create mode 100644 gb.v4l/src/main.c create mode 100644 gb.v4l/src/main.h create mode 100644 gb.v4l/src/videodev.h create mode 100755 gb.xml/AUTHORS create mode 120000 gb.xml/COPYING create mode 100755 gb.xml/ChangeLog create mode 120000 gb.xml/INSTALL create mode 100755 gb.xml/Makefile.am create mode 100755 gb.xml/NEWS create mode 100755 gb.xml/README create mode 100644 gb.xml/TODO create mode 120000 gb.xml/acinclude.m4 create mode 120000 gb.xml/component.am create mode 100644 gb.xml/configure.ac create mode 120000 gb.xml/gambas.h create mode 120000 gb.xml/gb_common.h create mode 120000 gb.xml/m4 create mode 120000 gb.xml/reconf create mode 100755 gb.xml/src/.directory create mode 100644 gb.xml/src/CDocument.cpp create mode 100644 gb.xml/src/CDocument.h create mode 100644 gb.xml/src/CElement.cpp create mode 100644 gb.xml/src/CElement.h create mode 100644 gb.xml/src/CExplorer.cpp create mode 100644 gb.xml/src/CExplorer.h create mode 100644 gb.xml/src/CNode.cpp create mode 100644 gb.xml/src/CNode.h create mode 100644 gb.xml/src/CReader.cpp create mode 100644 gb.xml/src/CReader.h create mode 100644 gb.xml/src/CTextNode.cpp create mode 100644 gb.xml/src/CTextNode.h create mode 100644 gb.xml/src/Makefile.am create mode 100644 gb.xml/src/document.cpp create mode 100644 gb.xml/src/document.h create mode 100644 gb.xml/src/element.cpp create mode 100644 gb.xml/src/element.h create mode 100644 gb.xml/src/explorer.cpp create mode 100644 gb.xml/src/explorer.h create mode 100755 gb.xml/src/gb.xml.component create mode 100644 gb.xml/src/gb.xml.h create mode 100644 gb.xml/src/gb.xml/.component create mode 100644 gb.xml/src/gb.xml/.directory create mode 100644 gb.xml/src/gb.xml/.icon.png create mode 100644 gb.xml/src/gb.xml/.project create mode 100644 gb.xml/src/gb.xml/.src/MTest.module create mode 100644 gb.xml/src/gb.xml/.src/MTest2.module create mode 100644 gb.xml/src/gb.xml/.src/XmlReader.class create mode 100644 gb.xml/src/gb.xml/.src/XmlWriter.class create mode 100644 gb.xml/src/gb.xml/.src/_XmlWriterDTD.class create mode 100644 gb.xml/src/gb.xml/text.xml create mode 100644 gb.xml/src/gbinterface.h create mode 100644 gb.xml/src/html/CHTMLDocument.cpp create mode 100644 gb.xml/src/html/CHTMLDocument.h create mode 100644 gb.xml/src/html/CHTMLElement.cpp create mode 100644 gb.xml/src/html/CHTMLElement.h create mode 100644 gb.xml/src/html/Makefile.am create mode 100644 gb.xml/src/html/cssfilter.cpp create mode 100644 gb.xml/src/html/cssfilter.h create mode 100755 gb.xml/src/html/gb.xml.html.component create mode 100644 gb.xml/src/html/gb.xml.html.h create mode 100644 gb.xml/src/html/htmldocument.cpp create mode 100644 gb.xml/src/html/htmldocument.h create mode 100644 gb.xml/src/html/htmlelement.cpp create mode 100644 gb.xml/src/html/htmlelement.h create mode 100644 gb.xml/src/html/htmlmain.cpp create mode 100644 gb.xml/src/html/htmlmain.h create mode 100644 gb.xml/src/html/htmlparser.cpp create mode 100644 gb.xml/src/html/htmlparser.h create mode 100644 gb.xml/src/html/htmlserializer.cpp create mode 100644 gb.xml/src/html/htmlserializer.h create mode 100644 gb.xml/src/main.cpp create mode 100644 gb.xml/src/main.h create mode 100644 gb.xml/src/node.cpp create mode 100644 gb.xml/src/node.h create mode 100644 gb.xml/src/parser.cpp create mode 100644 gb.xml/src/parser.h create mode 100644 gb.xml/src/reader.cpp create mode 100644 gb.xml/src/reader.h create mode 100644 gb.xml/src/rpc/Makefile.am create mode 120000 gb.xml/src/rpc/gb.xml.rpc.component create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.component create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.directory create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.icon.png create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.project create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcArray.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class create mode 100644 gb.xml/src/serializer.cpp create mode 100644 gb.xml/src/serializer.h create mode 100644 gb.xml/src/textnode.cpp create mode 100644 gb.xml/src/textnode.h create mode 100644 gb.xml/src/utils.cpp create mode 100644 gb.xml/src/utils.h create mode 100644 gb.xml/src/xslt/CXSLT.cpp create mode 100644 gb.xml/src/xslt/CXSLT.h create mode 100755 gb.xml/src/xslt/Makefile.am create mode 100755 gb.xml/src/xslt/gb.xml.xslt.component create mode 100644 gb.xml/src/xslt/main.cpp create mode 100644 gb.xml/src/xslt/main.h create mode 100644 gb.xml/src/xslt/xslt.pro create mode 100644 logo/gambas-ide.svg create mode 100644 logo/gambas.svg create mode 100644 m4/ax_compare_version.m4 create mode 100644 m4/gb_cflags_gcc_option.m4 create mode 100644 m4/gb_httpd.m4 create mode 100644 m4/gb_sdl.m4 create mode 100644 main/AUTHORS create mode 120000 main/COPYING create mode 120000 main/ChangeLog create mode 100644 main/INSTALL create mode 100644 main/Makefile.am create mode 100644 main/NEWS create mode 100644 main/README create mode 100644 main/TODO create mode 120000 main/acinclude.m4 create mode 120000 main/component.am create mode 100644 main/configure.ac create mode 120000 main/gb.pcre.h create mode 100644 main/gbc/Makefile.am create mode 100644 main/gbc/gb_alloc.c create mode 100644 main/gbc/gb_array.c create mode 100644 main/gbc/gb_buffer.c create mode 100644 main/gbc/gb_common.c create mode 100644 main/gbc/gb_error.c create mode 100644 main/gbc/gb_error.h create mode 100644 main/gbc/gb_file.c create mode 100644 main/gbc/gb_file.h create mode 100644 main/gbc/gb_str.c create mode 100644 main/gbc/gb_str.h create mode 100644 main/gbc/gb_table.c create mode 100644 main/gbc/gba.c create mode 100644 main/gbc/gbc.c create mode 100644 main/gbc/gbc_arch.c create mode 100644 main/gbc/gbc_archive.c create mode 100644 main/gbc/gbc_archive.h create mode 100644 main/gbc/gbc_chown.c create mode 100644 main/gbc/gbc_chown.h create mode 100644 main/gbc/gbc_class.c create mode 100644 main/gbc/gbc_class.h create mode 100644 main/gbc/gbc_code.c create mode 100644 main/gbc/gbc_compile.c create mode 100644 main/gbc/gbc_compile.h create mode 100644 main/gbc/gbc_dump.c create mode 100644 main/gbc/gbc_form.c create mode 100644 main/gbc/gbc_form.h create mode 100644 main/gbc/gbc_form_webpage.c create mode 100644 main/gbc/gbc_header.c create mode 100644 main/gbc/gbc_header.h create mode 100644 main/gbc/gbc_help.c create mode 100644 main/gbc/gbc_help.h create mode 100644 main/gbc/gbc_output.c create mode 100644 main/gbc/gbc_output.h create mode 100644 main/gbc/gbc_pcode.c create mode 100644 main/gbc/gbc_preprocess.c create mode 100644 main/gbc/gbc_preprocess.h create mode 100644 main/gbc/gbc_read.c create mode 100644 main/gbc/gbc_read.h create mode 100644 main/gbc/gbc_reserved.c create mode 100644 main/gbc/gbc_reserved_make.c create mode 100644 main/gbc/gbc_trans.c create mode 100644 main/gbc/gbc_trans.h create mode 100644 main/gbc/gbc_trans_code.c create mode 100644 main/gbc/gbc_trans_const.c create mode 100644 main/gbc/gbc_trans_ctrl.c create mode 100644 main/gbc/gbc_trans_expr.c create mode 100644 main/gbc/gbc_trans_subr.c create mode 100644 main/gbc/gbc_trans_tree.c create mode 100644 main/gbc/gbc_type.c create mode 100644 main/gbc/gbc_type.h create mode 100644 main/gbc/gbi.c create mode 100644 main/gbx/Makefile.am create mode 100644 main/gbx/gb.jit.h create mode 100644 main/gbx/gb_alloc.c create mode 100644 main/gbx/gb_array.c create mode 100644 main/gbx/gb_buffer.c create mode 100644 main/gbx/gb_common.c create mode 100644 main/gbx/gb_common_check.h create mode 100644 main/gbx/gb_error.c create mode 100644 main/gbx/gb_error.h create mode 100644 main/gbx/gb_file.c create mode 100644 main/gbx/gb_file.h create mode 100644 main/gbx/gb_hash.c create mode 100644 main/gbx/gb_list.c create mode 100644 main/gbx/gb_table.c create mode 100644 main/gbx/gbx.c create mode 100644 main/gbx/gbx.h create mode 100644 main/gbx/gbx_api.c create mode 100644 main/gbx/gbx_api.h create mode 100644 main/gbx/gbx_archive.c create mode 100644 main/gbx/gbx_archive.h create mode 100644 main/gbx/gbx_c_application.c create mode 100644 main/gbx/gbx_c_application.h create mode 100644 main/gbx/gbx_c_array.c create mode 100644 main/gbx/gbx_c_array.h create mode 100644 main/gbx/gbx_c_class.c create mode 100644 main/gbx/gbx_c_class.h create mode 100644 main/gbx/gbx_c_collection.c create mode 100644 main/gbx/gbx_c_collection.h create mode 100644 main/gbx/gbx_c_enum.c create mode 100644 main/gbx/gbx_c_enum.h create mode 100644 main/gbx/gbx_c_error.c create mode 100644 main/gbx/gbx_c_error.h create mode 100644 main/gbx/gbx_c_file.c create mode 100644 main/gbx/gbx_c_file.h create mode 100644 main/gbx/gbx_c_gambas.c create mode 100644 main/gbx/gbx_c_gambas.h create mode 100644 main/gbx/gbx_c_observer.c create mode 100644 main/gbx/gbx_c_observer.h create mode 100644 main/gbx/gbx_c_process.c create mode 100644 main/gbx/gbx_c_process.h create mode 100644 main/gbx/gbx_c_string.c create mode 100644 main/gbx/gbx_c_string.h create mode 100644 main/gbx/gbx_c_system.c create mode 100644 main/gbx/gbx_c_system.h create mode 100644 main/gbx/gbx_c_task.c create mode 100644 main/gbx/gbx_c_task.h create mode 100644 main/gbx/gbx_c_timer.c create mode 100644 main/gbx/gbx_c_timer.h create mode 100644 main/gbx/gbx_class.c create mode 100644 main/gbx/gbx_class.h create mode 100644 main/gbx/gbx_class_desc.h create mode 100644 main/gbx/gbx_class_info.c create mode 100644 main/gbx/gbx_class_init.c create mode 100644 main/gbx/gbx_class_load.c create mode 100644 main/gbx/gbx_class_load.h create mode 100644 main/gbx/gbx_class_native.c create mode 100644 main/gbx/gbx_compare.c create mode 100644 main/gbx/gbx_compare.h create mode 100644 main/gbx/gbx_component.c create mode 100644 main/gbx/gbx_component.h create mode 100644 main/gbx/gbx_date.c create mode 100644 main/gbx/gbx_date.h create mode 100644 main/gbx/gbx_debug.c create mode 100644 main/gbx/gbx_debug.h create mode 100644 main/gbx/gbx_eval.c create mode 100644 main/gbx/gbx_eval.h create mode 100644 main/gbx/gbx_event.c create mode 100644 main/gbx/gbx_event.h create mode 100644 main/gbx/gbx_exec.c create mode 100644 main/gbx/gbx_exec.h create mode 100644 main/gbx/gbx_exec_enum.c create mode 100644 main/gbx/gbx_exec_loop.c create mode 100644 main/gbx/gbx_exec_operator.c create mode 100644 main/gbx/gbx_exec_pop.c create mode 100644 main/gbx/gbx_exec_push.c create mode 100644 main/gbx/gbx_expression.h create mode 100644 main/gbx/gbx_extern.c create mode 100644 main/gbx/gbx_extern.h create mode 100644 main/gbx/gbx_info.h create mode 100644 main/gbx/gbx_jit.c create mode 100644 main/gbx/gbx_jit.h create mode 100644 main/gbx/gbx_library.c create mode 100644 main/gbx/gbx_library.h create mode 100644 main/gbx/gbx_local.c create mode 100644 main/gbx/gbx_local.h create mode 100644 main/gbx/gbx_math.c create mode 100644 main/gbx/gbx_math.h create mode 100644 main/gbx/gbx_number.c create mode 100644 main/gbx/gbx_number.h create mode 100644 main/gbx/gbx_object.c create mode 100644 main/gbx/gbx_object.h create mode 100644 main/gbx/gbx_project.c create mode 100644 main/gbx/gbx_project.h create mode 100644 main/gbx/gbx_regexp.c create mode 100644 main/gbx/gbx_regexp.h create mode 100644 main/gbx/gbx_replace.c create mode 100644 main/gbx/gbx_signal.c create mode 100644 main/gbx/gbx_signal.h create mode 100644 main/gbx/gbx_split.c create mode 100644 main/gbx/gbx_split.h create mode 100644 main/gbx/gbx_stack.c create mode 100644 main/gbx/gbx_stack.h create mode 100644 main/gbx/gbx_stream.c create mode 100644 main/gbx/gbx_stream.h create mode 100644 main/gbx/gbx_stream_arch.c create mode 100644 main/gbx/gbx_stream_buffer.c create mode 100644 main/gbx/gbx_stream_direct.c create mode 100644 main/gbx/gbx_stream_lock.c create mode 100644 main/gbx/gbx_stream_memory.c create mode 100644 main/gbx/gbx_stream_null.c create mode 100644 main/gbx/gbx_stream_pipe.c create mode 100644 main/gbx/gbx_stream_process.c create mode 100644 main/gbx/gbx_stream_string.c create mode 100644 main/gbx/gbx_string.c create mode 100644 main/gbx/gbx_string.h create mode 100644 main/gbx/gbx_struct.c create mode 100644 main/gbx/gbx_struct.h create mode 100644 main/gbx/gbx_subr.c create mode 100644 main/gbx/gbx_subr.h create mode 100644 main/gbx/gbx_subr_conv.c create mode 100644 main/gbx/gbx_subr_extern.c create mode 100644 main/gbx/gbx_subr_file.c create mode 100644 main/gbx/gbx_subr_math.c create mode 100644 main/gbx/gbx_subr_math_temp.h create mode 100644 main/gbx/gbx_subr_misc.c create mode 100644 main/gbx/gbx_subr_string.c create mode 100644 main/gbx/gbx_subr_test.c create mode 100644 main/gbx/gbx_subr_test_temp.h create mode 100644 main/gbx/gbx_subr_time.c create mode 100644 main/gbx/gbx_test.c create mode 100644 main/gbx/gbx_test.h create mode 100644 main/gbx/gbx_type.c create mode 100644 main/gbx/gbx_type.h create mode 100644 main/gbx/gbx_value.c create mode 100644 main/gbx/gbx_value.h create mode 100644 main/gbx/gbx_variant.h create mode 100644 main/gbx/gbx_watch.c create mode 100644 main/gbx/gbx_watch.h create mode 100644 main/lib/Makefile.am create mode 100644 main/lib/clipper/LICENSE create mode 100644 main/lib/clipper/Makefile.am create mode 100644 main/lib/clipper/c_clipper.cpp create mode 100644 main/lib/clipper/c_clipper.h create mode 100644 main/lib/clipper/clipper.cpp create mode 100644 main/lib/clipper/clipper.hpp create mode 100644 main/lib/clipper/gb.clipper.component create mode 120000 main/lib/clipper/gb.geom.h create mode 100644 main/lib/clipper/main.cpp create mode 100644 main/lib/clipper/main.h create mode 100644 main/lib/complex/Makefile.am create mode 100644 main/lib/complex/ccomplex.c create mode 100644 main/lib/complex/ccomplex.h create mode 100644 main/lib/complex/gb.complex.component create mode 100644 main/lib/complex/main.c create mode 100644 main/lib/complex/main.h create mode 100644 main/lib/compress/CCompress.c create mode 100644 main/lib/compress/CCompress.h create mode 100644 main/lib/compress/CUncompress.c create mode 100644 main/lib/compress/CUncompress.h create mode 100644 main/lib/compress/Makefile.am create mode 100644 main/lib/compress/gb.compress.component create mode 100644 main/lib/compress/gb.compress.h create mode 100644 main/lib/compress/main.c create mode 100644 main/lib/compress/main.h create mode 100644 main/lib/data/Makefile.am create mode 100644 main/lib/data/TODO create mode 100644 main/lib/data/c_avltree.c create mode 100644 main/lib/data/c_avltree.h create mode 100644 main/lib/data/c_circular.c create mode 100644 main/lib/data/c_circular.h create mode 100644 main/lib/data/c_deque.c create mode 100644 main/lib/data/c_deque.h create mode 100644 main/lib/data/c_graph.c create mode 100644 main/lib/data/c_graph.h create mode 100644 main/lib/data/c_graphmatrix.c create mode 100644 main/lib/data/c_graphmatrix.h create mode 100644 main/lib/data/c_heap.c create mode 100644 main/lib/data/c_heap.h create mode 100644 main/lib/data/c_list.c create mode 100644 main/lib/data/c_list.h create mode 100644 main/lib/data/c_trie.c create mode 100644 main/lib/data/c_trie.h create mode 100644 main/lib/data/gb.data.component create mode 100644 main/lib/data/gb.data/.component create mode 100644 main/lib/data/gb.data/.directory create mode 100644 main/lib/data/gb.data/.icon.png create mode 100644 main/lib/data/gb.data/.project create mode 100644 main/lib/data/gb.data/.src/MMain.module create mode 100644 main/lib/data/gb.data/.src/PrioSet.class create mode 100644 main/lib/data/gb.data/.src/_PrioSet_Entry.class create mode 100644 main/lib/data/list.h create mode 100644 main/lib/data/lookup3.h create mode 100644 main/lib/data/main.c create mode 100644 main/lib/data/main.h create mode 100644 main/lib/data/string_compare.h create mode 100644 main/lib/data/trie.c create mode 100644 main/lib/data/trie.h create mode 100644 main/lib/db/CConnection.c create mode 100644 main/lib/db/CConnection.h create mode 100644 main/lib/db/CDatabase.c create mode 100644 main/lib/db/CDatabase.h create mode 100644 main/lib/db/CField.c create mode 100644 main/lib/db/CField.h create mode 100644 main/lib/db/CIndex.c create mode 100644 main/lib/db/CIndex.h create mode 100644 main/lib/db/CResult.c create mode 100644 main/lib/db/CResult.h create mode 100644 main/lib/db/CResultField.c create mode 100644 main/lib/db/CResultField.h create mode 100644 main/lib/db/CTable.c create mode 100644 main/lib/db/CTable.h create mode 100644 main/lib/db/CUser.c create mode 100644 main/lib/db/CUser.h create mode 100644 main/lib/db/Makefile.am create mode 100644 main/lib/db/c_subcollection.c create mode 100644 main/lib/db/c_subcollection.h create mode 100644 main/lib/db/deletemap.c create mode 100644 main/lib/db/deletemap.h create mode 100644 main/lib/db/gb.db.component create mode 100644 main/lib/db/gb.db.h create mode 100644 main/lib/db/gb.db.proto.h create mode 100644 main/lib/db/gb.db/.component create mode 100644 main/lib/db/gb.db/.directory create mode 100644 main/lib/db/gb.db/.icon.png create mode 100644 main/lib/db/gb.db/.project create mode 100644 main/lib/db/gb.db/.src/Connection.class create mode 100644 main/lib/db/gb.db/.src/Connections.class create mode 100644 main/lib/db/gb.db/.src/Main.module create mode 100644 main/lib/db/gb.db/.src/SQLRequest.class create mode 100644 main/lib/db/gb_barray.h create mode 100644 main/lib/db/main.c create mode 100644 main/lib/db/main.h create mode 100644 main/lib/db/sqlite.c create mode 100644 main/lib/db/sqlite.h create mode 100644 main/lib/debug/CDebug.c create mode 100644 main/lib/debug/CDebug.h create mode 100644 main/lib/debug/Makefile.am create mode 100644 main/lib/debug/debug.c create mode 100644 main/lib/debug/debug.h create mode 100644 main/lib/debug/gb.debug.component create mode 100644 main/lib/debug/gb.debug.h create mode 100644 main/lib/debug/main.c create mode 100644 main/lib/debug/main.h create mode 100644 main/lib/debug/print.c create mode 100644 main/lib/debug/print.h create mode 100644 main/lib/debug/profile.c create mode 100644 main/lib/debug/profile.h create mode 100644 main/lib/draw/Makefile.am create mode 100644 main/lib/draw/cdraw.c create mode 100644 main/lib/draw/cdraw.h create mode 100644 main/lib/draw/cpaint.c create mode 100644 main/lib/draw/cpaint.h create mode 100644 main/lib/draw/gb.draw.h create mode 120000 main/lib/draw/gb.geom.h create mode 120000 main/lib/draw/gb.image.h create mode 100644 main/lib/draw/gb.paint.h create mode 100644 main/lib/draw/gb_list.c create mode 100644 main/lib/draw/main.c create mode 100644 main/lib/draw/main.h create mode 100644 main/lib/draw/matrix.c create mode 100644 main/lib/draw/matrix.h create mode 100644 main/lib/eval/Makefile.am create mode 100644 main/lib/eval/c_expression.c create mode 100644 main/lib/eval/c_expression.h create mode 100644 main/lib/eval/c_highlight.c create mode 100644 main/lib/eval/c_highlight.h create mode 100644 main/lib/eval/c_system.c create mode 100644 main/lib/eval/c_system.h create mode 100644 main/lib/eval/eval.c create mode 100644 main/lib/eval/eval.h create mode 100644 main/lib/eval/eval_analyze.c create mode 100644 main/lib/eval/eval_analyze.h create mode 100644 main/lib/eval/eval_code.c create mode 100644 main/lib/eval/eval_code.h create mode 100644 main/lib/eval/eval_read.c create mode 100644 main/lib/eval/eval_read.h create mode 100644 main/lib/eval/eval_reserved.c create mode 100644 main/lib/eval/eval_trans.c create mode 100644 main/lib/eval/eval_trans.h create mode 100644 main/lib/eval/eval_trans_expr.c create mode 100644 main/lib/eval/eval_trans_tree.c create mode 100644 main/lib/eval/gb.eval.component create mode 100644 main/lib/eval/gb.eval.h create mode 100644 main/lib/eval/gb_alloc_override.h create mode 100644 main/lib/eval/gb_array.c create mode 100644 main/lib/eval/gb_error.c create mode 100644 main/lib/eval/gb_error.h create mode 100644 main/lib/eval/gb_table.c create mode 100644 main/lib/eval/main.c create mode 100644 main/lib/eval/main.h create mode 100644 main/lib/gb.component create mode 100644 main/lib/geom/Makefile.am create mode 100644 main/lib/geom/cpoint.c create mode 100644 main/lib/geom/cpoint.h create mode 100644 main/lib/geom/cpoint_temp.h create mode 100644 main/lib/geom/crect.c create mode 100644 main/lib/geom/crect.h create mode 100644 main/lib/geom/crect_temp.h create mode 100644 main/lib/geom/gb.geom.component create mode 100644 main/lib/geom/gb.geom.h create mode 100644 main/lib/geom/main.c create mode 100644 main/lib/geom/main.h create mode 100644 main/lib/gui.opengl/Makefile.am create mode 100644 main/lib/gui.opengl/gb.gui.opengl.component create mode 100644 main/lib/gui.opengl/main.c create mode 100644 main/lib/gui.opengl/main.h create mode 100644 main/lib/gui.qt.opengl/Makefile.am create mode 100644 main/lib/gui.qt.opengl/gb.gui.qt.opengl.component create mode 100644 main/lib/gui.qt.opengl/main.c create mode 100644 main/lib/gui.qt.opengl/main.h create mode 100644 main/lib/gui.qt.webkit/Makefile.am create mode 100644 main/lib/gui.qt.webkit/gb.gui.qt.webkit.component create mode 100644 main/lib/gui.qt.webkit/main.c create mode 100644 main/lib/gui.qt.webkit/main.h create mode 100644 main/lib/gui.qt/Makefile.am create mode 100644 main/lib/gui.qt/gb.gui.qt.component create mode 120000 main/lib/gui.qt/gb_gui_test_temp.h create mode 100644 main/lib/gui.qt/main.c create mode 100644 main/lib/gui.qt/main.h create mode 100644 main/lib/gui.trayicon/Makefile.am create mode 100644 main/lib/gui.trayicon/cfaketrayicon.c create mode 100644 main/lib/gui.trayicon/cfaketrayicon.h create mode 100644 main/lib/gui.trayicon/gb.gui.trayicon.component create mode 100644 main/lib/gui.trayicon/main.c create mode 100644 main/lib/gui.trayicon/main.h create mode 100644 main/lib/gui.webview/Makefile.am create mode 100644 main/lib/gui.webview/gb.gui.webview.component create mode 100644 main/lib/gui.webview/main.c create mode 100644 main/lib/gui.webview/main.h create mode 100644 main/lib/gui/Makefile.am create mode 100644 main/lib/gui/gb.gui.component create mode 100644 main/lib/gui/gb_gui_test_temp.h create mode 100644 main/lib/gui/main.c create mode 100644 main/lib/gui/main.h create mode 100644 main/lib/hash/Makefile.am create mode 100644 main/lib/hash/c_hash.c create mode 100644 main/lib/hash/c_hash.h create mode 100644 main/lib/hash/gb.hash.component create mode 100644 main/lib/hash/gb.hash.h create mode 100644 main/lib/hash/hash.c create mode 100644 main/lib/hash/hash.h create mode 100644 main/lib/hash/main.c create mode 100644 main/lib/hash/main.h create mode 100644 main/lib/hash/platform.h create mode 100644 main/lib/image.effect/CImage.cpp create mode 100644 main/lib/image.effect/CImage.h create mode 100644 main/lib/image.effect/Makefile.am create mode 100644 main/lib/image.effect/effect.cpp create mode 100644 main/lib/image.effect/effect.h create mode 100644 main/lib/image.effect/gb.image.effect.component create mode 100644 main/lib/image.effect/kcpuinfo.cpp create mode 100644 main/lib/image.effect/kcpuinfo.h create mode 100644 main/lib/image.effect/kimageeffect.cpp create mode 100644 main/lib/image.effect/kimageeffect.h create mode 100644 main/lib/image.effect/main.cpp create mode 100644 main/lib/image.effect/main.h create mode 100644 main/lib/image.effect/qcolor.cpp create mode 100644 main/lib/image.effect/qcolor.h create mode 100644 main/lib/image.effect/qimage.cpp create mode 100644 main/lib/image.effect/qimage.h create mode 100644 main/lib/image.effect/qpoint.cpp create mode 100644 main/lib/image.effect/qpoint.h create mode 100644 main/lib/image.effect/qrect.cpp create mode 100644 main/lib/image.effect/qrect.h create mode 100644 main/lib/image.effect/qsize.cpp create mode 100644 main/lib/image.effect/qsize.h create mode 100644 main/lib/image.effect/qt.h create mode 100644 main/lib/image/CImage.c create mode 100644 main/lib/image/CImage.h create mode 100644 main/lib/image/CImageStat.c create mode 100644 main/lib/image/CImageStat.h create mode 100644 main/lib/image/Makefile.am create mode 100644 main/lib/image/c_color.c create mode 100644 main/lib/image/c_color.h create mode 100644 main/lib/image/gb.image.component create mode 100644 main/lib/image/gb.image.h create mode 100644 main/lib/image/image.c create mode 100644 main/lib/image/image.h create mode 100644 main/lib/image/image_stat.c create mode 100644 main/lib/image/image_stat.h create mode 100644 main/lib/image/main.c create mode 100644 main/lib/image/main.h create mode 100644 main/lib/inotify/Makefile.am create mode 100644 main/lib/inotify/TODO create mode 100644 main/lib/inotify/c_watch.c create mode 100644 main/lib/inotify/c_watch.h create mode 100644 main/lib/inotify/gb.inotify.component create mode 100644 main/lib/inotify/gb_list.c create mode 100644 main/lib/inotify/main.c create mode 100644 main/lib/inotify/main.h create mode 100644 main/lib/jit/Makefile.am create mode 100644 main/lib/jit/gb.jit.component create mode 100644 main/lib/jit/gb.jit/.component create mode 100644 main/lib/jit/gb.jit/.directory create mode 100644 main/lib/jit/gb.jit/.hidden/icon.png create mode 100644 main/lib/jit/gb.jit/.icon.png create mode 100644 main/lib/jit/gb.jit/.project create mode 100644 main/lib/jit/gb.jit/.src/CCompilation.class create mode 100644 main/lib/jit/gb.jit/.src/Jit.module create mode 100644 main/lib/jit/gb.jit/.src/Main.module create mode 100644 main/lib/jit/gb.jit/.src/_ClassStat.class create mode 120000 main/lib/jit/gb.jit/gambas.h create mode 120000 main/lib/jit/gb.jit/gb.jit.h create mode 120000 main/lib/jit/gb.jit/gb_error_common.h create mode 120000 main/lib/jit/gb.jit/gb_overflow.h create mode 100644 main/lib/jit/gb.jit/jit.h create mode 100644 main/lib/jit/gb.jit/jit_after.h create mode 100644 main/lib/jit/gb_str.c create mode 100644 main/lib/jit/gb_str.h create mode 100644 main/lib/jit/gbc_reserved.c create mode 100644 main/lib/jit/jit.c create mode 100644 main/lib/jit/jit.h create mode 100644 main/lib/jit/jit_body.c create mode 100644 main/lib/jit/main.c create mode 100644 main/lib/jit/main.h create mode 100755 main/lib/option/Makefile.am create mode 100755 main/lib/option/gb.option.component create mode 100644 main/lib/option/getoptions.c create mode 100644 main/lib/option/getoptions.h create mode 100644 main/lib/option/main.c create mode 100644 main/lib/option/main.h create mode 100644 main/lib/signal/Makefile.am create mode 100644 main/lib/signal/csignal.c create mode 100644 main/lib/signal/csignal.h create mode 100644 main/lib/signal/gb.signal.component create mode 100644 main/lib/signal/main.c create mode 100644 main/lib/signal/main.h create mode 100644 main/lib/term/Makefile.am create mode 100644 main/lib/term/cterm.c create mode 100644 main/lib/term/cterm.h create mode 100644 main/lib/term/gb.term.component create mode 100644 main/lib/term/main.c create mode 100644 main/lib/term/main.h create mode 100644 main/lib/test/Makefile.am create mode 100644 main/lib/test/gb.test.component create mode 100644 main/lib/test/gb.test/.component create mode 100644 main/lib/test/gb.test/.directory create mode 100644 main/lib/test/gb.test/.hidden/CHANGELOG create mode 100644 main/lib/test/gb.test/.hidden/flowchart.fodg create mode 100644 main/lib/test/gb.test/.hidden/flowchart.svg create mode 100644 main/lib/test/gb.test/.hidden/gb.test.png create mode 100644 main/lib/test/gb.test/.hidden/summary-example.txt create mode 100644 main/lib/test/gb.test/.icon.png create mode 100644 main/lib/test/gb.test/.lang/de.po create mode 100644 main/lib/test/gb.test/.lang/it.po create mode 100644 main/lib/test/gb.test/.lang/pt_BR.po create mode 100644 main/lib/test/gb.test/.project create mode 100644 main/lib/test/gb.test/.src/Helper.module create mode 100644 main/lib/test/gb.test/.src/Tap/TapContext.class create mode 100644 main/lib/test/gb.test/.src/Tap/TapParser.class create mode 100644 main/lib/test/gb.test/.src/Tap/TapPrinter.class create mode 100644 main/lib/test/gb.test/.src/TestAssertion.class create mode 100644 main/lib/test/gb.test/.src/TestMyself/MustFail.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TAllAsserts.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TBailout.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TCrashes.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TElse.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TEmpty.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TIntendedFailures.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TInternals.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TParser.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TSetup.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TSkipAll.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TSummary.test create mode 100644 main/lib/test/gb.test/.src/TestMyself/TWrongPlan.test create mode 100644 main/lib/test/gb.test/.src/TestRunner.module create mode 100644 main/lib/test/gb.test/.src/TestStats.class create mode 100644 main/lib/test/gb.test/.src/TestSuite/Assert.module create mode 100644 main/lib/test/gb.test/.src/TestSuite/Test.module create mode 100644 main/lib/test/gb.test/.src/TestSuite/TestCase.class create mode 100644 main/lib/test/gb.test/.src/TestSuite/TestCommand.class create mode 100644 main/lib/test/gb.test/.src/TestSuite/TestSuite.class create mode 100644 main/lib/test/gb.test/.src/ZzzDoSth.module create mode 100644 main/lib/test/gb.test/.test create mode 100644 main/lib/test/gb.test/LICENSE create mode 100644 main/lib/test/gb.test/README.md create mode 100644 main/lib/test/gb.test/taskell.md create mode 100755 main/lib/test/gb.test/test.sh create mode 100644 main/lib/test/gb.test/unittesthelloworld-1.0.0.tar.gz create mode 100644 main/lib/test/main.c create mode 100644 main/lib/test/main.h create mode 100644 main/lib/vb/Makefile.am create mode 100644 main/lib/vb/gb.vb.component create mode 100644 main/lib/vb/main.c create mode 100644 main/lib/vb/main.h create mode 100644 main/lib/vb/vb.c create mode 100644 main/lib/vb/vb.h create mode 100644 main/lib/vb/vbdate.c create mode 100644 main/lib/vb/vbdate.h create mode 120000 main/m4 create mode 100644 main/man/Makefile.am create mode 100644 main/man/gba3.1 create mode 100644 main/man/gbc3.1 create mode 100644 main/man/gbh3.1 create mode 100644 main/man/gbi3.1 create mode 100644 main/man/gbr3.1 create mode 100644 main/man/gbx3.1 create mode 100644 main/mime/application-x-gambas3-48.png create mode 100644 main/mime/application-x-gambas3.png create mode 100644 main/mime/application-x-gambas3.xml create mode 120000 main/reconf create mode 100644 main/share/Makefile.am create mode 100644 main/share/gambas.h create mode 100644 main/share/gb_alloc.h create mode 100644 main/share/gb_alloc_temp.h create mode 100644 main/share/gb_arch.h create mode 100644 main/share/gb_arch_temp.h create mode 100644 main/share/gb_array.h create mode 100644 main/share/gb_array_temp.h create mode 100644 main/share/gb_buffer.h create mode 100644 main/share/gb_buffer_temp.h create mode 100644 main/share/gb_class_desc_common.h create mode 100644 main/share/gb_code.h create mode 100644 main/share/gb_code_temp.h create mode 100644 main/share/gb_common.h create mode 100644 main/share/gb_common_buffer.h create mode 100644 main/share/gb_common_buffer_temp.h create mode 100644 main/share/gb_common_case.h create mode 100644 main/share/gb_common_case_temp.h create mode 100644 main/share/gb_common_string.h create mode 100644 main/share/gb_common_string_temp.h create mode 100644 main/share/gb_common_swap.h create mode 100644 main/share/gb_common_swap_temp.h create mode 100644 main/share/gb_component.h create mode 100644 main/share/gb_error_common.h create mode 100644 main/share/gb_file_share.h create mode 100644 main/share/gb_file_temp.h create mode 100644 main/share/gb_hash.h create mode 100644 main/share/gb_hash_temp.h create mode 100644 main/share/gb_limit.h create mode 100644 main/share/gb_list.h create mode 100644 main/share/gb_list_temp.h create mode 100644 main/share/gb_magic.h create mode 100644 main/share/gb_overflow.h create mode 100644 main/share/gb_pcode.h create mode 100644 main/share/gb_pcode_temp.h create mode 100644 main/share/gb_replace.h create mode 100644 main/share/gb_replace_temp.h create mode 100644 main/share/gb_reserved.h create mode 100644 main/share/gb_reserved_keyword.h create mode 100644 main/share/gb_reserved_temp.h create mode 100644 main/share/gb_system.h create mode 100644 main/share/gb_system_temp.h create mode 100644 main/share/gb_table.h create mode 100644 main/share/gb_table_temp.h create mode 100644 main/share/gb_type_common.h create mode 100644 main/share/gbc_read_common.h create mode 100644 main/share/gbc_read_temp.h create mode 100644 main/share/gbc_trans_common.h create mode 100644 main/share/gbx_subr_common.h create mode 100644 main/tools/gbh3/.directory create mode 100644 main/tools/gbh3/.icon.png create mode 100644 main/tools/gbh3/.project create mode 100644 main/tools/gbh3/.src/MMain.module create mode 100644 main/tools/gbh3/.src/MOldMain.module create mode 100644 main/tools/gbh3/README create mode 100644 main/tools/gbh3/icon.png create mode 100644 main/tools/gbh3/license create mode 100644 main/tools/gbh3/usage create mode 100755 reconf create mode 100755 reconf-all create mode 100755 test-fast create mode 100644 version.m4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cf5051fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +Makefile +Makefile.in +libtool +ltmain.sh +stamp-h1 +warnings.log +.deps/ +aclocal.m4 +config.guess +config.log +config.sub +configure +install-sh +autom4te.cache +config.h +config.h.in +config.status +config.cache +compile +depcomp +missing +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/m4/pkg.m4 +.libs/ +*.la +*.lo +*.o +*.a +.dirstamp +*_moc.cpp +*.pot +*.mo +*.gambas +main/gb*/gb*3 +DISABLED* +.startup +.settings +.info +.list +.action/ +.jit/ +**/.gitignore +app/other/MakeWebSite/gambas.sourceforge.net/*/ +*~ +.fuse_hidden* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..7aca7f33 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,243 @@ +build:archlinux: + image: archlinux:base + before_script: + - > + pacman -Syu --needed --noconfirm + alure autoconf automake bzip2 zstd coreutils curl dbus gcc gdk-pixbuf2 git + glew glib2 gmime gsl gst-plugins-base gstreamer gtk2 gtk3 gtkglext imlib2 + intltool libffi libgl libgnome-keyring libmariadbclient librsvg libsm + libxcursor libxml2 libxslt libxtst mariadb make mesa ncurses pcre + - > + pacman -Syu --needed --noconfirm + pkg-config poppler poppler-glib postgresql postgresql-libs qt5-svg + qt5-x11extras sdl2 sdl2_gfx sdl2_image sdl2_mixer sdl2_net + sdl2_ttf sdl_mixer sdl_ttf smpeg sqlite unixodbc v4l-utils xdg-utils + zlib gettext qt5-webengine webkit2gtk + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-sqlite2 --disable-qt4 --disable-pdf --disable-qt5webkit + - make -j$(nproc) + - make install + + +build:archlinux-clang: + image: archlinux:base + before_script: + - > + pacman -Syu --needed --noconfirm + alure autoconf automake bzip2 zstd coreutils curl dbus clang gdk-pixbuf2 git + glew glib2 gmime gsl gst-plugins-base gstreamer gtk2 gtk3 gtkglext imlib2 + intltool libffi libgl libgnome-keyring libmariadbclient librsvg libsm + libxcursor libxml2 libxslt libxtst mariadb make mesa ncurses pcre + - > + pacman -Syu --needed --noconfirm + pkg-config poppler poppler-glib postgresql postgresql-libs qt5-svg + qt5-x11extras sdl2 sdl2_gfx sdl2_image sdl2_mixer sdl2_net + sdl2_ttf sdl_mixer sdl_ttf smpeg sqlite unixodbc v4l-utils xdg-utils + zlib gettext qt5-webengine webkit2gtk + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure CC=clang CXX=clang++ -C --disable-sqlite2 --disable-qt4 --disable-pdf --disable-poppler --disable-qt5webkit + - make -j$(nproc) + - make install + + +build:debian-oldstable: + image: debian:oldstable + before_script: + - > + apt-get update && apt-get install -y build-essential g++ automake + autoconf libbz2-dev libzstd-dev default-libmysqlclient-dev unixodbc-dev libpq-dev + libsqlite0-dev libsqlite3-dev libglib2.0-dev + libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev + libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev + libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-glib-dev + libpoppler-private-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev + libffi-dev libqt4-dev libqtwebkit-dev libqt4-opengl-dev libglew-dev + libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev + linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev + libcairo2-dev libgsl-dev libncurses5-dev libgmime-2.6-dev libalure-dev + libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev + libsdl2-image-dev sane-utils libdumb1-dev libqt5opengl5-dev + libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev + qtwebengine5-dev libwebkit2gtk-4.0-dev git + libssl-dev + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring + - make -j$(nproc) + - make install + +build:debian-stable: + image: debian:stable + before_script: + - > + apt-get update && apt-get install -y build-essential g++ automake + autoconf libbz2-dev libzstd-dev default-libmysqlclient-dev unixodbc-dev libpq-dev + libsqlite3-dev libglib2.0-dev + libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev + libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev + libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-glib-dev + libpoppler-private-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev + libffi-dev libglew-dev + libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev + linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev + libcairo2-dev libgsl-dev libncurses5-dev libgmime-3.0-dev libalure-dev + libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev + libsdl2-image-dev sane-utils libdumb1-dev libqt5opengl5-dev + libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev + qtwebengine5-dev libwebkit2gtk-4.0-dev git libssl-dev + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring --disable-sqlite2 --disable-qt4 --disable-qtwebkit + - make -j$(nproc) + - make install + +build:debian-testing: + image: debian:testing + before_script: + - > + apt-get update && apt-get install -y build-essential g++ automake + autoconf libbz2-dev libzstd-dev default-libmysqlclient-dev unixodbc-dev libpq-dev + libsqlite3-dev libglib2.0-dev + libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev + libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev + libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-glib-dev + libpoppler-private-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev + libffi-dev libglew-dev + libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev + linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev + libcairo2-dev libgsl-dev libncurses5-dev libgmime-3.0-dev libalure-dev + libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev + libsdl2-image-dev sane-utils libdumb1-dev libqt5opengl5-dev + libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev + qtwebengine5-dev libwebkit2gtk-4.0-dev git libssl-dev + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring --disable-sqlite2 --disable-qt4 --disable-qtwebkit + - make -j$(nproc) + - make install + + +build:debian-unstable: + image: debian:unstable + before_script: + - > + apt-get update && apt-get install -y build-essential g++ automake autoconf libbz2-dev libzstd-dev default-libmysqlclient-dev unixodbc-dev libpq-dev libsqlite0-dev libsqlite3-dev libglib2.0-dev libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-glib-dev libpoppler-private-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev libffi-dev libglew-dev libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcairo2-dev libgsl-dev libncurses5-dev libgmime-3.0-dev libalure-dev libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev sane-utils libdumb1-dev libqt5opengl5-dev libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev qtwebengine5-dev libwebkit2gtk-4.0-dev git libssl-dev + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring --disable-qt4 + - make -j$(nproc) + - make install + +build:ubuntu-kinetic: + image: ubuntu:kinetic + before_script: + - > + apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential g++ automake autoconf libtool libbz2-dev libzstd-dev libmysqlclient-dev unixodbc-dev libpq-dev libsqlite0-dev libsqlite3-dev libglib2.0-dev libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-private-dev libpoppler-glib-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev libffi-dev libglew-dev libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcairo2-dev libgsl-dev libncurses5-dev libgmime-3.0-dev llvm-dev llvm libalure-dev libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev sane-utils libdumb1-dev libssl-dev libqt5opengl5-dev libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev qtwebengine5-dev libwebkit2gtk-4.0-dev git + + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring --disable-qt4 + - make -j$(nproc) + - make install + +build:ubuntu-jammy: + image: ubuntu:jammy + before_script: + - > + apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential g++ automake autoconf libtool libbz2-dev libzstd-dev libmysqlclient-dev unixodbc-dev libpq-dev libsqlite0-dev libsqlite3-dev libglib2.0-dev libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-private-dev libpoppler-glib-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev libffi-dev libglew-dev libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcairo2-dev libgsl-dev libncurses5-dev libgmime-3.0-dev llvm-dev llvm libalure-dev libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev sane-utils libdumb1-dev libssl-dev libqt5opengl5-dev libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev qtwebengine5-dev libwebkit2gtk-4.0-dev git + + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring --disable-qt4 + - make -j$(nproc) + - make install + +build:ubuntu-focal: + image: ubuntu:focal + before_script: + - > + apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential g++ automake autoconf libtool libbz2-dev libzstd-dev libmysqlclient-dev unixodbc-dev libpq-dev libsqlite0-dev libsqlite3-dev libglib2.0-dev libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-private-dev libpoppler-glib-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev libffi-dev libglew-dev libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcairo2-dev libgsl-dev libncurses5-dev libgmime-2.6-dev llvm-dev llvm libalure-dev libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev sane-utils libdumb1-dev libssl-dev libqt5opengl5-dev libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev qtwebengine5-dev libwebkit2gtk-4.0-dev git + + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring --disable-qt4 + - make -j$(nproc) + - make install + +build:ubuntu-bionic: + image: ubuntu:bionic + before_script: + - > + apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential g++ automake autoconf libtool libbz2-dev libzstd-dev libmysqlclient-dev unixodbc-dev libpq-dev libsqlite0-dev libsqlite3-dev libglib2.0-dev libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-private-dev libpoppler-glib-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev libffi-dev libqt4-dev libqtwebkit-dev libqt4-opengl-dev libglew-dev libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgdk-pixbuf2.0-dev linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcairo2-dev libgsl-dev libncurses5-dev libgmime-2.6-dev llvm-dev llvm libalure-dev libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev sane-utils libdumb1-dev libssl-dev libqt5opengl5-dev libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev qtwebengine5-dev libwebkit2gtk-4.0-dev + + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-keyring + - make -j$(nproc) + - make install + +build:ubuntu-xenial: + image: ubuntu:xenial + before_script: + - > + apt-get update && apt-get install -y build-essential g++ automake autoconf libtool libbz2-dev libmysqlclient-dev unixodbc-dev libpq-dev libsqlite0-dev libsqlite3-dev libglib2.0-dev libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-glib-dev libpoppler-private-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev libffi-dev libqt4-dev libqtwebkit-dev libqt4-opengl-dev libglew-dev libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgnome-keyring-dev libgdk-pixbuf2.0-dev linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcairo2-dev libgsl-dev libncurses5-dev libgmime-2.6-dev llvm-dev llvm libalure-dev libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev sane-utils libdumb1-dev libssl-dev libqt5opengl5-dev libqt5svg5-dev libqt5webkit5-dev libqt5x11extras5-dev qtbase5-dev libwebkit2gtk-3.0-dev + + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-gtk3webview --disable-qt5webview --disable-zstd + - make -j$(nproc) + - make install + +build:ubuntu-trusty: + image: ubuntu:trusty + before_script: + - > + apt-get update && apt-get install -y build-essential g++ automake autoconf libtool libbz2-dev libmysqlclient-dev unixodbc-dev libpq-dev libsqlite0-dev libsqlite3-dev libglib2.0-dev libgtk2.0-dev libcurl4-gnutls-dev libgtkglext1-dev libpcre3-dev libsdl-sound1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libxml2-dev libxslt1-dev librsvg2-dev libpoppler-dev libpoppler-glib-dev libpoppler-private-dev libpoppler-cpp-dev libasound2-dev libdirectfb-dev libxtst-dev libffi-dev libqt4-dev libqtwebkit-dev libqt4-opengl-dev libglew-dev libimlib2-dev libv4l-dev libsdl-ttf2.0-dev libgnome-keyring-dev libgdk-pixbuf2.0-dev linux-libc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcairo2-dev libgsl0-dev libncurses5-dev libgmime-2.6-dev llvm-dev llvm libalure-dev libgmp-dev libgtk-3-dev libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev sane-utils libdumb1-dev libssl-dev git libglu1-mesa-dev libglu1-mesa-dev libglew1.10 libglu1-mesa libgles2-mesa-dev libwebkit2gtk-3.0-dev + + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-qt5 --disable-gtk3webview --disable-gtk3opengl --disable-zstd + - make -j$(nproc) + - make install + +build:alpine: + image: alpine + before_script: + - > + apk add --no-progress -q + autoconf automake libtool curl-dev dbus-dev g++ gdk-pixbuf-dev git gmp-dev + glew-dev glib-dev gmime-dev gsl-dev gst-plugins-base-dev gstreamer-dev gtk+2.0-dev gtk+3.0-dev + imlib2-dev gettext-dev libffi-dev mesa-dev mariadb-dev librsvg-dev + libsm-dev libxcursor-dev libxml2-dev libxslt-dev libxtst-dev make ncurses-dev pcre-dev pkgconf + poppler-dev postgresql-dev qt5-qtsvg-dev qt5-qtx11extras-dev qt5-qtwebengine-dev + sdl2-dev sdl2_image-dev sdl2_mixer-dev sdl2_ttf-dev sqlite-dev unixodbc-dev xdg-utils + zlib-dev webkit2gtk-dev + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-openal --disable-qt4 --disable-sdl --disable-sdlsound --disable-sqlite2 --disable-v4l --disable-gtkopengl --disable-zstd --disable-qt5webkit --disable-pdf --disable-keyring + - make -j$(nproc) + - make install + +build:fedora-latest: + image: fedora:latest + before_script: + - > + dnf install -y libtool libtool-ltdl-devel gcc make autoconf dumb-devel gmime-devel gmime30-devel libffi-devel mariadb-devel postgresql-devel unixODBC-devel sqlite2-devel libsqlite3x-devel libxslt-devel libv4l-devel glew-devel poppler-devel poppler-glib-devel poppler-cpp-devel qt-devel qt5-qtbase-devel qt5-qtbase-private-devel qt5-qtwebengine-devel qt5-qtx11extras-devel qt5-qtsvg-devel SDL_ttf-devel SDL_mixer-devel SDL-devel SDL2_ttf-devel SDL2_mixer-devel SDL2_image-devel SDL2-devel cairo-devel gtk2-devel gtk3-devel imlib2-devel librsvg2-devel qt-webkit-devel qt5-qtwebkit qt5-qtwebkit-devel openal-soft-devel libjpeg-turbo-devel gtkglext-devel gmp-devel libxml2-devel libXtst-devel gsl-devel pcre-devel dbus-devel libcurl-devel alure-devel gstreamermm-devel libgnome-keyring-devel bzip2-devel libzstd-devel webkit2gtk3-devel ncurses-devel + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C + - make -j$(nproc) + - make install + +build:opensuse-tumbleweed: + image: opensuse/tumbleweed + before_script: + - > + zypper update -y && zypper install -y libtool gcc glew-devel gmime-devel gtk2-devel gtk3-devel libqt5-qtx11extras-devel libqt5-qtwebengine-devel libQt5Xml-devel libQt5Sql-devel libqt5-qtsvg-devel libQt5Gui-devel libQt5Core-devel libQt5Sql5-postgresql libQt5Sql5-unixODBC libQt5Sql5-mysql libQt5Sql5-sqlite webkit2gtk3-devel libICE-devel libSM-devel unixODBC-devel libzip-devel libzstd-devel libmariadb-devel gsl-devel libopenssl-devel alure-devel postgresql-devel libcurl-devel libSDL_ttf-devel libSDL_mixer-devel libSDL-devel libSDL2_ttf-devel libSDL2_mixer-devel libSDL2_image-devel libSDL2-devel libxslt-devel libv4l-devel librsvg-devel gtkglext-devel libgnome-keyring-devel libjpeg62-devel libpoppler-devel libpoppler-glib-devel imlib2-devel gmp-devel gstreamermm-devel + script: + - ./reconf-all + - GAMBAS_CONFIG_FAILURE=1 ./configure -C --disable-qt4 --disable-sqlite2 --disable-qt5webkit --disable-pdf + - make -j$(nproc) + - make install diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..8dea5f8f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,311 @@ +# +# GAMBAS AUTHORS FILE +# +# This file should list all people that have written code or done translations for Gambas. +# +# If you want to add or fix anything, please write on the mailing-list, or submit a merge +# request with GitLab. +# +# I apologize by advance for any mistake or omission. +# +# The format of that file is the following: +# +# [Name of the author] ([Country]) <[mail address]> +# [Description in Gambas markup syntax] +# ... +# +# [Name of another author] ({Country]) <[mail address]> +# [Description in Gambas markup syntax] +# ... +# +# Void lines and lines beginning with '#' are ignored. +# + +Fabien Bodard (France) + IDE file/project/picture selector and mascot redesign. + The reporting components. + The 'gb.chart' component. + The 'gb.map' component. + The 'gb.scanner' component. + +Brandon Bergren (USA) + Porting Gambas on Cygwin. + +Tobias Boege (Germany) + The 'gb.data' component. + The 'gb.inotify' component. + The 'gb.ncurses' component. + The 'gb.openssl' component. + The 'gb.test' component (co-author). + Sometimes German translation. + +Andrea Bortolan (Italy) + The ODBC database driver. + +Bruce Bruen (Australia) + IDE Packager wizard enhancements. + +David Villalobos Cambronero (Costa Rica) + The 'gb.mysql' MySQL specific component. + Spanish translation. + +Daniel Campos (Spain) + The networking component + The compression component + The old XML component + The GTK+ component. + The now deprecated VideoForLinux component + The PDF component. + Spanish translation. + +Laurent Carlier (France) + The SDL component. + The OpenGL component. + ArchLinux support. + +Paul Gardner-Stephen (Australia) + Porting Gambas on Solaris. + +Bastian Germann (Germany) + Cygwin package. + Debian package. + pkgsrc package. + Porting on NetBSD and OpenBSD. + Portability fixes for Cygwin. + +Nigel Gerrard (United Kingdom) + The [MySQL database driver](http://mysql.com) + The now deprecated QT extended component. + The old SQLite database driver. + +Rob Kudla (USA) + The old Gambas Wiki hosting. + Mandriva RPM packages maintainer. + The 'gb.pcre' component. + +Sebastian Kulesz + Spanish translation. + Gambas Debian/Ubuntu packaging. + The 'gb.logging' component. + The 'gb.memcached' component. + The 'gb.net.pop3' component. + +Jussi Lahtinen + Gambas self-test program. + +Emil Lenngren (Sweden) + The deprecated old Just-In-Time compiler. + +Alexey Loginov (Russia) + Russian translation. + Mageia RPM packages maintainer. + +Marcelo López (Argentina) + ODBC driver enhancements. + +Benoît Minisini (France) + French translation. + Main developer. + Benevolent dictator. + +Randall Morgan + The 'gb.gsl' GNU Scientific Library component. + +Ronald Onstenk (Netherlands) + The find list in the 1.0 Development Environnement + +Carlos F. A. Paniago (Brazil) + Porting Gambas on FreeBSD. + +Adrien Prokopowicz (France) + The rewritten 'gb.xml' component. + The 'gb.xml.html' component. + +José L. Redrejo Rodríguez (Spain) + Let Gambas compile on all Debian architectures. + Allowed the Gambas IDE to make Debian packages. + Debian and Linex package maintainer. + Spanish translation. + +Robert Rowe (USA) + Development environment enhancements. + +Philippe Séraphin (France) + IDE Packager wizard enhancements. + +Bruce Steers (UK) + Shell highlighting. + TerminalView window commands support. + +Christof Thalhofer (Germany) + Unit test framework. + Gambas self-test program. + Mailing-list manager. + +Brian Grindley (USA) + Scripter enhancements. + +# +# List of people who are just doing example programs +# + +Jairo Alonso Badillo Bedoya (Columnia) + Concent game example. + +Gareth Bult (United Kingdom) + WebCam video example. + +Iman Karim (Germany) + Gravity example. + GameOfLife example. + +Timothy Marshal-Nichols (United Kingdom) + PictureDatabase example. + Printing example. + +Pablo Mileti (Argentina) + GNUBoxWorld and Puzzle1To8 examples. + +# +# List of people who are just doing translations +# + +Ahmad Kamal + Arabic translation. + +Daif Al-Otaibi + Arabic translation. + +Dimitri Bellini (Italy) + Italian translation. + +Vincenzo Virgilio (Italy) + Italian translation. + +Maurizio Pozzobon (Italy) + Italian translation + +Jordi Sayol (Andorra) + Catalan translation + +Kazutaka Harada (Japan) + Japanese translation + +Yizhou He + Simplified Chinese translation. + +Knut Berg + Norwegian translation. + +Chunchi Lin + Traditional Chinese translation. + +Wojciech Saltarski (Poland) + Polish translation. + +Radoslav Dejanovic (Croatia) + Croatian translation. + +Luis Minero (Portugal) + Portuguese translation. + +Iuri Matias (Portugal) + Portuguese translation. + +Peter Cernoch (Czech Republic) + Czech translation. + +Nelson Ferraz + Brazilian Portuguese translation. + +Fermyno Gutierrez + Brazilian Portuguese translation. + +Ronald Onstenk (Netherlands) + Dutch translation. + +Fabrice Mous (Netherlands) + Dutch translation. + +Sergey Irupin (Russia) + Russian translation. + +Nima Mohammadi (Iran) + Farsi translation. + +Miha Ambroz + Slovenian translation. + +David Cendal Lago + Galician translation. + +Peter Landgren + Swedish translation. + +Marco Bauer (Germany) + German translation. + +Klaus-Peter Richter (Germany) + German translation. + +Fatih Asici (Turkey) + Turkish translation. + +Balázs Bárány + Hungarian translation. + +Kevin Donnelly + Welsh translation. + +Sahatma Petrus Dolok Marupa Siagian (Indonesia) + Indonesian translation. + +Rizky Tahara Shita + Indonesian translation + +Stefano Palmeri (Italy) + Italian translation. + +Alexander Kazancev (Russia) + Russian translation. + +Peter Mathijssen + Dutch translation. + +Liang Wei (China) + Simplified Chinese translation. + +Alexandros Prekates (Greece) + Greek translation. + +Stefan Lang (Germany) + German translation. + +Radek Fryšták (Czech Republic) + Czech translation. + +Mathias Ebermann (Germany) + German translation. + +Florin Iacob (Romania) + Romanian translation. + +Josef Kubíček (Czech Republic) + Czech translation. + +Regimantas Baublys (Lithuania) + Lithuanian translation. + +Edison Henrique Andreassy (Brazil) + Brazilian Portuguese translation. + +Paul Wheeler (USA) + English Grammar Check & Clarifications to Wiki entries. + +Willy Raets (Netherlands) + Dutch translation. + +Gianluigi Gradaschi + Italian translation. + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..74bf15c7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,149 @@ +# How To Contribute + +This guide provides a step-by-step tutorial on how to contribute to the Gambas source code and translations. + +It will cover the sources' organization, some contributing guidelines and how to use Git and GitLab in order to submit your contribution. + +## Using Git and GitLab + +The Gambas source code is managed by a Git repository, hosted on [GitLab.com](https://gitlab.com/gambas/gambas). + +To handle new contributions, we use the [Project forking workflow](https://docs.gitlab.com/ce/workflow/forking_workflow.html) : +since contributors do not have the permission to write to the Gambas source code directly, you will have to create a separate repository containing your changes, and then create a merge request asking the Gambas developers to merge your changes into the main repository. + +While this might sound complex to new contributors, this document is made to guide through this process, step by step. + +If you are having trouble with the steps mentioned here, or if you have any question regarding a contribution, you can [ask on the mailing-list](http://gambaswiki.org/wiki/doc/forum). + +### Creating a GitLab account + +First, you will need a [GitLab account](https://gitlab.com/users/sign_in) in order to submit any changes. + +We also recommend you [use SSH to work with Git repositories](https://docs.gitlab.com/ce/ssh/README.html), instead of HTTPS. +Not only you won't have to enter your GitLab username and password every time you want to interact with the repository, but it is also more secure as your password is never sent through the network. + +You can also [use GPG to sign your commits](https://docs.gitlab.com/ee/user/project/gpg_signed_commits/index.html), although it is not required. + +### Forking the Gambas Repository + +Now that your GitLab account is set up, we can now [fork the Gambas repository](https://docs.gitlab.com/ce/gitlab-basics/fork-project.html). +This will create a copy of the Gambas repository, but it will belong to you, so you can make any change you want. + +To do this, just go over to the [Gambas project page](https://gitlab.com/gambas/gambas), and click the "Fork" button. + +You will then be asked where to put the forked repository. Once complete, the new repository will appear under your account. + +You can then clone the repository to your local machine, using the following command (replace `` with your GitLab username): + + git clone git@gitlab.com:/gambas.git + +### Making changes to your repository + +Once the cloning is complete, you can make changes to your local copy, which will then have to be commited and pushed. + +First, you can check which files you changed with the `git status`. It's always good to check before commiting ! +You can also view the full diff with the `git diff` command. + +Once everything is done, you will have to select which files you want to commit next using the [git add](https://git-scm.com/docs/git-add) command. + +You can either select specific files or directories using `git add file1.c file2.c main/gbx`, or just select everything using `git add -A`. + +You can then make your commit using the `git commit` command. +This command will open an editor to let you write your commit message. + +For some guidelines on how to write commit messages, see the Writing commit messages section. + +This command will start the default editor (usually `vi`), but you can change this by setting the `EDITOR` environment variable to the command starting your favorite editor. + +Now that the commit is done, you can push it to your GitLab repository using the `git push` command. + +### Creating the merge request + +With your changes now pushed to the GitLab repository, the final step is to create a [Merge Request](https://docs.gitlab.com/ee/user/project/merge_requests/index.html), +kindly asking the Gambas developers to merge your changes to the main Gambas repository. + +Since this process is entirerly made through GitLab, [use the following instructions to create your merge requests](https://docs.gitlab.com/ee/gitlab-basics/add-merge-request.html). + +Its is probable that your changes won't be accepted right away, and you will be asked by the Gambas maintainers to make some changes. + +In this case, you can make your changes, commit them and then push them. +The Merge request on GitLab will be automatically updated, you won't have to recreate it. + +### Keeping your repository up to date + +During the time you make changes to your version of the Gambas source code, or while your request is being reviewed, it is likely that the Gambas repository will receive some updates. + +Since a forked repository is basically a clone, the official version and your version are completely separate, it will not receive newer commits automatically. + +However, you can setup your local repository to connect to both repositories, so you can pull changes from the official Gambas repository, merge them with your changes locally, and then push them to your forked repository. + +First, we will setup your local Git repository by adding the original Gambas repository as a second remote : + + git remote add upstream https://gitlab.com/gambas/gambas.git + +We now have added a new remote named `upstream` to the local repository, pointing to the original Gambas repository. +(You can list all the remotes with the `git remote -v` command.) + +This means Git can now pull changes from the original Gambas repository using the following command : + + git pull upstream master + +This command will take the changes from the `master` branch of the `upstream` remote. + +If you made commits to your version of the repository, it will merge them with the new changes, creating a new merge commit. + +When the merge is complete, you can simply use `git push` to push all these changes to your version of the repository. If you have any Merge Request pending, they will get updated automatically. + + +## Writing commit messages + +In order to automatically generate changelogs for each release, commits in the Gambas repository have to follow a very specific format. + +Here is an example : + + This commit contains things, adds stuff and has lots of fluff. + (but this won't go in the changelog) + + [GB.QT4] + * NEW: Added things to the component. + * BUG: Fixed a bug in the Foo function. + * NEW: Added this very long modification... + ...and it takes more than one Line To Write it. + + This won't go into the changelog either. + + [COMPILER] + * BUG: What an awful bug! + * OPT: Make things go faster. + + [GB.GTK3] + * NEW: The component is now complete! + +As per the Git commit message convention, the first line of each commit is a short description of what it contains. +This line does not end up in the changelog, but it appears in git logs, as well as in the GitLab interface. + +Then, the commit message consists of the following parts : + +* A slot, between square brackets (e.g. `[GB.QT4]`) +* One or more modifications, each prefixed with a tag, which is either `* NEW: `, `* BUG: `, or `* OPT: `, with a space at the end. + +The slot's name is the one of the component modified (in uppercase), or one of these if the changes do not affect a component : +* `[INTERPRETER]` for changes in the interpreter (gbx3). +* `[COMPILER]` for changes in the compiler (gbc3). +* `[ARCHIVER]` for changes in the archiver (gba3). +* `[INFORMER]` for changes in the informer (gbi3). +* `[DEVELOPMENT ENVIRONMENT]` for changes in the IDE (gambas3). +* `[CONFIGURATION]` for changes in the automake/autoconf configuration process +* `[WIKI CGI SCRIPT]` for changes in the wiki CGI script. +* `[WEB SITE MAKER]` for changes in the Gambas web site generator. +* `[EXAMPLES]` for changes in any example. + +The tag's name is one of the following: + +* `NEW` is for new features or translations, updates or other improvements; +* `BUG` is for bug fixes and other corrections +* `OPT` is for optimizations + +Things without an impact for the user (such as refactorings or code cleanups) should not end up in the Changelog. + +All lines without a tag will not appear in the changelog, but if you want a modification to span across multiple lines, you will have to prefix it with two spaces. diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..2060dfa5 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +Visit https://gambaswiki.org/wiki/install diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..f0d2d3a7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,62 @@ +SUBDIRS = \ + main \ + @bzlib2_dir@ \ + @zlib_dir@ \ + @zstd_dir@ \ + @mysql_dir@ \ + @odbc_dir@ \ + @postgresql_dir@ \ + @sqlite2_dir@ \ + @sqlite3_dir@ \ + @net_dir@ \ + @curl_dir@ \ + @mime_dir@ \ + @pcre_dir@ \ + @sdl_dir@ \ + @sdlsound_dir@ \ + @sdl2_dir@ \ + @libxml_dir@ \ + @xml_dir@ \ + @v4l_dir@ \ + @crypt_dir@ \ + @qt4_dir@ \ + @qt5_dir@ \ + @gtk_dir@ \ + @gtk3_dir@ \ + @opengl_dir@ \ + @x11_dir@ \ + @keyring_dir@ \ + @pdf_dir@ \ + @poppler_dir@ \ + @cairo_dir@ \ + @imageio_dir@ \ + @imageimlib_dir@ \ + @dbus_dir@ \ + @gsl_dir@ \ + @gmp_dir@ \ + @ncurses_dir@ \ + @media_dir@ \ + @httpd_dir@ \ + @openssl_dir@ \ + @openal_dir@ \ + comp \ + @htmlview_dir@ \ + app \ + . + +EXTRA_DIST = component.am README README.*[^~] TODO TEMPLATE reconf reconf-all VERSION + +am__tar = ${AMTAR} cof - "$$tardir" + +install-exec-local: + @if test -s $(srcdir)/warnings.log; then \ + echo ; \ + echo "||" ; \ + cat $(srcdir)/warnings.log ; \ + echo "||" ; \ + echo ; \ + fi + @rm -f $(srcdir)/warnings.log + +dist-hook: + @(cd $(distdir); rm -rf `find . -name ".gambas" -o -name ".lock" -o -name ".xvpics" -o -name "*~" -o -name "*.out" -o -name "*.pot" -o -name "*.gambas" -o -name "core.*" -o -name "vgcore.*" -o -name ".kdbg*" -o -name ".svn"`;) diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/README b/README new file mode 100644 index 00000000..ef020706 --- /dev/null +++ b/README @@ -0,0 +1,50 @@ +WELCOME TO GAMBAS! + +GAMBAS is a free implementation of a graphical development environment +based on a BASIC interpreter and a full development platform. It is very +inspired by Visual Basic and Java. + +Go to http://gambas.sourceforge.net to get more information: how to compile +and install it, where to find binary packages, how to report a bug... + +Go to http://gambaswiki.org for language documentation. + +The following pieces of code were borrowed and adapted: + +- The natural string comparison algorithme was adapted from the algorithm + made by Martin Pol. See http://sourcefrog.net/projects/natsort/ for more + details. + +- The hash table implementation was adapted from the glib one. + +- The HTML entities parsing in 'gb.gtk' comes from KHTML sources. + +- The 'gb.image.effect' sources are adapted from KDE 3 image effect routines. + +- The 'gb.clipper' library embeds the Clipper library. See + http://www.angusj.com/delphi/clipper.php for mode details. + +- The function that computes the easter day of a specific year uses an + algorithm made by Aloysius Lilius And Christophorus Clavius. + +- The blurring algoritm is based on the 'StackBlur' algorithm made by Mario + Klingemann. See http://incubator.quasimondo.com/processing/fast_blur_deluxe.php + for more details. + +- The javascript automatic completion is done with 'autoComplete' from Simon + Steinberger / Pixabay, and is published under the MIT license. + See https://github.com/Pixabay/JavaScript-autoComplete for more details. + +- The 'gb.form.htmlview' component embeds the 'litehtml' library from Yuri + Kobets. See http://www.litehtml.com for more details. + +- The 'gb.hash' component embeds the code of the hashing routines of BusyBox made + by Denys Vlasenko and Bernhard Reutner-Fischer. See https://www.busybox.net/ + for more details. + +If I forget some borrowed code in the list above, just tell me. + +Enjoy Gambas! + +-- +Benoît diff --git a/README.commit b/README.commit new file mode 100644 index 00000000..3e4f88fe --- /dev/null +++ b/README.commit @@ -0,0 +1,60 @@ +STANDARD FORMAT FOR COMMIT MESSAGES +----------------------------------- + +This message is for all developers that will commit something +into the git repository. + +I want to have a standard way to write commit messages, so that ChangeLog can +be almost automatically generated. + +The format is the following: + +- One line that will be a summary of the changes displayed next to the commit + in GitLab. + +- A ChangeLog slot, between '[' & ']' + + Slots are the name of the component, in uppercase if possible, or some other + slots like [INTERPRETER], [COMPILER]... + +- A ChangeLog modification: a '*', a space, the word 'BUG','NEW' or 'OPT', a + colon, a space, and the text. + + 'BUG' is for a fix, 'NEW' for a new feature, and 'OPT' for an optimization. + + If a changelog modification is more than one line, you must use a two space + indent. + +- Other ChangeLog modifications for the same slot. + +- Other slots. + +- Void lines are ignored. + +- All other lines won't go into the changelog. + +For example: + +--8<----------- +I did this thing, and this will be the summary displayed in GitLab. + +[GB.QT5] +* BUG: I fixed this bug. +* NEW: I made this very long modification.... +  and it takes more than one line to write it. + +This won't go into the changelog too. + +[GB.SDL2] +* BUG: What an awful bug! + +[GB.GTK3] +* NEW: I finally finished the component :-) + +--8<----------- + +You should really try hard to follow this scheme, otherwise generating the +release notes becomes truly a pain for me. Be nice! + +-- +Benoît. diff --git a/README.md b/README.md new file mode 100644 index 00000000..5d461083 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ + + + +# Gambas Almost Means BASIC + +WELCOME TO GAMBAS! + +GAMBAS is a free implementation of a graphical development environment +based on a BASIC interpreter and a full development platform. It is very +inspired by Visual Basic and Java. + +Go to http://gambas.sourceforge.net to get more information: how to compile +and install it, where to find binary packages, how to report a bug... + +Go to http://gambaswiki.org for language documentation. + +The following pieces of code were borrowed and adapted: + +- The natural string comparison algorithme was adapted from the algorithm + made by Martin Pol. See http://sourcefrog.net/projects/natsort/ for more + details. + +- The hash table implementation was adapted from the glib one. + +- The HTML entities parsing in 'gb.gtk' comes from KHTML sources. + +- The 'gb.image.effect' sources are adapted from KDE 3 image effect routines. + +- The 'gb.clipper' library embeds the Clipper library. See + http://www.angusj.com/delphi/clipper.php for mode details. + +- The function that computes the easter day of a specific year uses an + algorithm made by Aloysius Lilius And Christophorus Clavius. + +- The blurring algoritm is based on the 'StackBlur' algorithm made by Mario + Klingemann. See http://incubator.quasimondo.com/processing/fast_blur_deluxe.php + for more details. + +- The javascript automatic completion is done with 'autoComplete' from Simon + Steinberger / Pixabay, and is published under the MIT license. + See https://github.com/Pixabay/JavaScript-autoComplete for more details. + +- The 'gb.form.htmlview' component embeds the 'litehtml' library from Yuri + Kobets. See http://www.litehtml.com for more details. + +- The 'gb.hash' component embeds the code of the hashing routines of BusyBox made + by Denys Vlasenko and Bernhard Reutner-Fischer. See https://www.busybox.net/ + for more details. + +If I forget some borrowed code in the list above, just tell me. + +Enjoy Gambas! + +-- +Benoît diff --git a/TEMPLATE/README b/TEMPLATE/README new file mode 100644 index 00000000..aa13e0fb --- /dev/null +++ b/TEMPLATE/README @@ -0,0 +1,34 @@ +COMPONENT DIRECTORY TEMPLATE + +Here you will find the 'make-component' script, that creates a initial +component source directory from a configuration file located in the +'conf' directory. + +BE CAREFUL! This template only works inside the complete gambas source +package structure. + +For example, let suppose you want to make a component named 'gb.test', +based on the well known 'test' library: + +1) Copy the file 'TEMPLATE.conf' in the 'conf' directory, and rename it + as 'gb.test.conf' + +2) Open a terminal, with the current directory being the one where + the 'make-component' script is located. This is important, because + otherwise the script won't work. + +3) Edit the file 'gb.test.conf', and replaces the value of each + '#define' directive by the appropriate ones. Each directive has + a commentary that explains its role. + +4) Once done, run './make-component gb.test'. + +5) If everything is ok, you will find a new component directory in the + parent directory named gb.test, with all needed files and symbolic + links ready to be build: configure.ac, Makefile.am, ... + +If something is weird, tell me on the gambas mailing-list! + +Benoît. + + diff --git a/TEMPLATE/TEMPLATE.c b/TEMPLATE/TEMPLATE.c new file mode 100644 index 00000000..99c76741 --- /dev/null +++ b/TEMPLATE/TEMPLATE.c @@ -0,0 +1,28 @@ +$/$*************************************************************************** +## + __SOURCE_NAME##.c +## + __COMPONENT component +## + __COPYRIGHT __AUTHOR __EMAIL +## + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +## + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +## + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +## +***************************************************************************/ +## +$#$define _##_##__SOURCE_UNAME##_C +## +$#$include $:$##__SOURCE_NAME##.h$:$ diff --git a/TEMPLATE/TEMPLATE.conf b/TEMPLATE/TEMPLATE.conf new file mode 100644 index 00000000..92d4351c --- /dev/null +++ b/TEMPLATE/TEMPLATE.conf @@ -0,0 +1,79 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Joe Smith +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.test + +/* Name of the component with points replaced by dashes */ +#define __COMPONENT_DASH gb-test + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_test + +/* Short name of the component */ +#define __NAME test + +/* Short name of the component in uppercase */ +#define __UNAME TEST + +/* Description of the component */ +#define __DESCRIPTION Testing component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME test-1.0 + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION 1.2.8 + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 1 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 1 + + /* Includes to search for */ + #define __SEARCH_INCLUDE test.h test2.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR test/include include test*/include test/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libtest.$SHLIBEXT libjpeg.$SHLIBEXT libpng.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib + + /* Libraries to link with */ + #define __LIBRARY -ljpeg -lpng -ltest + + /* Includes to link with */ + #define __INCLUDE -ljpeg -lpng -ltest + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h test.c test.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/TEMPLATE.cpp b/TEMPLATE/TEMPLATE.cpp new file mode 100644 index 00000000..828c73ad --- /dev/null +++ b/TEMPLATE/TEMPLATE.cpp @@ -0,0 +1,28 @@ +$/$*************************************************************************** +## + __SOURCE_NAME##.c +## + __COMPONENT component +## + __COPYRIGHT __AUTHOR __EMAIL +## + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +## + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +## + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +## +***************************************************************************/ +## +$#$define _##_##__SOURCE_UNAME##_C +## +$#$include $:$##__SOURCE_NAME##.h##$:$ diff --git a/TEMPLATE/TEMPLATE.h b/TEMPLATE/TEMPLATE.h new file mode 100644 index 00000000..894fd805 --- /dev/null +++ b/TEMPLATE/TEMPLATE.h @@ -0,0 +1,35 @@ +$/$*************************************************************************** +## + __SOURCE_NAME##.h +## + __COMPONENT component +## + __COPYRIGHT __AUTHOR __EMAIL +## + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +## + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +## + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +## +***************************************************************************/ +## +$#$ifndef _##_##__SOURCE_UNAME##_H +$#$define _##_##__SOURCE_UNAME##_H +## +$#$include "gambas.h" +## +$#$ifndef _##_##__MAIN_UNAME##_C +extern GB_INTERFACE GB; +$#$endif +## +$#$endif $/$* _##_##__SOURCE_UNAME##_H */ diff --git a/TEMPLATE/conf/gb.cairo.conf b/TEMPLATE/conf/gb.cairo.conf new file mode 100644 index 00000000..33c6c173 --- /dev/null +++ b/TEMPLATE/conf/gb.cairo.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2009 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.cairo + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_cairo + +/* Short name of the component */ +#define __NAME cairo + +/* Short name of the component in uppercase */ +#define __UNAME CAIRO + +/* Description of the component */ +#define __DESCRIPTION Cairo library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME cairo + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION 1.8.0 + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 1 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 1 + + /* Includes to search for */ + #define __SEARCH_INCLUDE test.h test2.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR test/include include test*/include test/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libtest.$SHLIBEXT libjpeg.$SHLIBEXT libpng.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib + + /* Libraries to link with */ + #define __LIBRARY -ljpeg -lpng -ltest + + /* Includes to link with */ + #define __INCLUDE -ljpeg -lpng -ltest + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.dbus.conf b/TEMPLATE/conf/gb.dbus.conf new file mode 100644 index 00000000..828e9f1e --- /dev/null +++ b/TEMPLATE/conf/gb.dbus.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2009 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.dbus + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_dbus + +/* Short name of the component */ +#define __NAME dbus + +/* Short name of the component in uppercase */ +#define __UNAME DBUS + +/* Description of the component */ +#define __DESCRIPTION DBUS management + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME dbus-1 + +/* Minimum version needed */ +//#define __PKGCONFIG_VERSION 2.14.3 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 0 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h cdbus.c cdbus.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.desktop.conf b/TEMPLATE/conf/gb.desktop.conf new file mode 100644 index 00000000..d76ed3d9 --- /dev/null +++ b/TEMPLATE/conf/gb.desktop.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2007 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.desktop + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_desktop + +/* Short name of the component */ +#define __NAME desktop + +/* Short name of the component in uppercase */ +#define __UNAME DESKTOP + +/* Description of the component */ +#define __DESCRIPTION Desktop-neutral routines + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME glib-2.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 1 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 1 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.form.htmlview.conf b/TEMPLATE/conf/gb.form.htmlview.conf new file mode 100644 index 00000000..7271353f --- /dev/null +++ b/TEMPLATE/conf/gb.form.htmlview.conf @@ -0,0 +1,80 @@ +/* Copyrights */ +#define __COPYRIGHT (c) +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.form.htmlview + +/* Name of the component with points replaced by dashes */ +#define __COMPONENT_DASH gb-form-htmlview + + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_form_htmlview + +/* Short name of the component */ +#define __NAME htmlview + +/* Short name of the component in uppercase */ +#define __UNAME HTMLVIEW + +/* Description of the component */ +#define __DESCRIPTION HTML viewer based on the litehtml library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME test-1.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION 1.2.8 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 0 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 1 + +/* If your component uses multi-threading */ +#define __USE_THREAD 0 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.cpp main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.gmp.conf b/TEMPLATE/conf/gb.gmp.conf new file mode 100644 index 00000000..70af8182 --- /dev/null +++ b/TEMPLATE/conf/gb.gmp.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.gmp + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_gmp + +/* Short name of the component */ +#define __NAME gmp + +/* Short name of the component in uppercase */ +#define __UNAME GMP + +/* Description of the component */ +#define __DESCRIPTION GNU multi-precision arithmetic library component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME gsl + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 0 + + /* If your component uses multi-threading */ + #define __USE_THREAD 0 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 0 + + /* Includes to search for */ + #define __SEARCH_INCLUDE gmp.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libgmp.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib/* lib + + /* Libraries to link with */ + #define __LIBRARY -lgmp + + /* Includes to link with */ + #define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.gsl.conf b/TEMPLATE/conf/gb.gsl.conf new file mode 100644 index 00000000..9dc4a603 --- /dev/null +++ b/TEMPLATE/conf/gb.gsl.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Randall Morgan +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.gsl + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_gsl + +/* Short name of the component */ +#define __NAME gsl + +/* Short name of the component in uppercase */ +#define __UNAME GSL + +/* Description of the component */ +#define __DESCRIPTION GNU Scientific Library component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME gsl + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 0 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 0 + + /* Includes to search for */ + #define __SEARCH_INCLUDE + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR /usr/local/include gsl/include include gsl*/include gsl/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libgsl.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR local/lib lib + + /* Libraries to link with */ + #define __LIBRARY libgsl + + /* Includes to link with */ + #define __INCLUDE libgsl + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h c_gsl.c c_gsl.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.image.io.conf b/TEMPLATE/conf/gb.image.io.conf new file mode 100644 index 00000000..a38c7cf8 --- /dev/null +++ b/TEMPLATE/conf/gb.image.io.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2009 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.image.io + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_image_io + +/* Short name of the component */ +#define __NAME image_io + +/* Short name of the component in uppercase */ +#define __UNAME IMAGE_IO + +/* Description of the component */ +#define __DESCRIPTION Image loading and saving + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME gdk-pixbuf-2.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION 2.14.3 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 0 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h CImage.c CImage.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.media.conf b/TEMPLATE/conf/gb.media.conf new file mode 100644 index 00000000..31b75bd0 --- /dev/null +++ b/TEMPLATE/conf/gb.media.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.media + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_media + +/* Short name of the component */ +#define __NAME media + +/* Short name of the component in uppercase */ +#define __UNAME MEDIA + +/* Description of the component */ +#define __DESCRIPTION GStreamer component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME gstreamer-0.10 + +/* Minimum version needed */ +//#define __PKGCONFIG_VERSION 2.14.3 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 0 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.mime.conf b/TEMPLATE/conf/gb.mime.conf new file mode 100644 index 00000000..2866478c --- /dev/null +++ b/TEMPLATE/conf/gb.mime.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.mime + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_mime + +/* Short name of the component */ +#define __NAME mime + +/* Short name of the component in uppercase */ +#define __UNAME MIME + +/* Description of the component */ +#define __DESCRIPTION MIME message management based on gmime library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME gmime-2.6 + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 0 + + /* If your component uses multi-threading */ + #define __USE_THREAD 0 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 0 + + /* Includes to search for */ + #define __SEARCH_INCLUDE gmp.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libgmp.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib/* lib + + /* Libraries to link with */ + #define __LIBRARY -lgmp + + /* Includes to link with */ + #define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.net.pop3.conf b/TEMPLATE/conf/gb.net.pop3.conf new file mode 100644 index 00000000..2ecaafcd --- /dev/null +++ b/TEMPLATE/conf/gb.net.pop3.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2006 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.image + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_image + +/* Short name of the component */ +#define __NAME image + +/* Short name of the component in uppercase */ +#define __UNAME IMAGE + +/* Description of the component */ +#define __DESCRIPTION Image processing component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME test-1.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION 1.2.8 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 0 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 1 + +/* If your component uses multi-threading */ +#define __USE_THREAD 1 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.cpp main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.net.smtp.conf b/TEMPLATE/conf/gb.net.smtp.conf new file mode 100644 index 00000000..7e074c17 --- /dev/null +++ b/TEMPLATE/conf/gb.net.smtp.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2006 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.net.smtp + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_net_smtp + +/* Short name of the component */ +#define __NAME smtp + +/* Short name of the component in uppercase */ +#define __UNAME SMTP + +/* Description of the component */ +#define __DESCRIPTION SMTP client component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME glib-2.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 0 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 1 + +/* If your component uses multi-threading */ +#define __USE_THREAD 1 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.cpp main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.openal.conf b/TEMPLATE/conf/gb.openal.conf new file mode 100644 index 00000000..e5f03861 --- /dev/null +++ b/TEMPLATE/conf/gb.openal.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2013 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.openal + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_openal + +/* Short name of the component */ +#define __NAME openal + +/* Short name of the component in uppercase */ +#define __UNAME OPENAL + +/* Description of the component */ +#define __DESCRIPTION Open AL library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME openal + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION 1.14 + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 1 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 1 + + /* Includes to search for */ + #define __SEARCH_INCLUDE test.h test2.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR test/include include test*/include test/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libtest.$SHLIBEXT libjpeg.$SHLIBEXT libpng.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib + + /* Libraries to link with */ + #define __LIBRARY -ljpeg -lpng -ltest + + /* Includes to link with */ + #define __INCLUDE -ljpeg -lpng -ltest + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.poppler.conf b/TEMPLATE/conf/gb.poppler.conf new file mode 100644 index 00000000..b927e928 --- /dev/null +++ b/TEMPLATE/conf/gb.poppler.conf @@ -0,0 +1,79 @@ +/* Copyrights */ +#define __COPYRIGHT (c) +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.poppler + +/* Name of the component with points replaced by dashes */ +#define __COMPONENT_DASH gb-poppler + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_poppler + +/* Short name of the component */ +#define __NAME poppler + +/* Short name of the component in uppercase */ +#define __UNAME POPPLER + +/* Description of the component */ +#define __DESCRIPTION Pdf reader based on poppler library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME poppler-cpp + + /* Minimum version needed */ + //#define __PKGCONFIG_VERSION 1.2.8 + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 1 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 1 + + /* Includes to search for */ + #define __SEARCH_INCLUDE poppler.h test2.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR poppler/include include poppler*/include poppler/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libtest.$SHLIBEXT libjpeg.$SHLIBEXT libpng.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib + + /* Libraries to link with */ + #define __LIBRARY -ljpeg -lpng -ltest + + /* Includes to link with */ + #define __INCLUDE -ljpeg -lpng -ltest + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/make-component b/TEMPLATE/make-component new file mode 100755 index 00000000..c49eb522 --- /dev/null +++ b/TEMPLATE/make-component @@ -0,0 +1,64 @@ +#!/bin/bash + +if test -d ../$1; then + echo "$0: error: This component already exists"; + exit 1; +fi + +if test ! -e ./conf/$1.conf; then + echo "$0: error: The configuration file for this component does not exist"; + exit 1; +fi + +if test x`which cpp` == x; then + echo "$0: error: cpp was not found"; + exit 1; +fi + +echo "Creating component directory $1..." +cp -R template ../$1 +rm -rf ../$1/.svn ../$1/*/.svn + +echo "Applying template..." +for i in ../$1/* ../$1/*/*; do + if test ! -h $i && test ! -d $i; then + cpp -P -include ./conf/$1.conf -o $i.out $i; + rm -f $i; + cat $i.out | sed s/"\\$\/\\$"/"\/"/g | sed s/"\\$'\\$"/"\""/g | sed s/"\\$:\\$"/"\""/g | sed s/"\\$\#\\$"/"\#"/g | sed s/"\#@\#"/"@"/g | sed s/"\#\#"/""/g > $i; + rm -f $i.out; + fi +done + +echo "Creating source files..." +for i in `cat ../$1/SOURCES`; do + BASENAME=`basename $i`; + SOURCE=../$1/src/$i; + if test ! `basename $i .h` = $BASENAME; then + cpp -P -include ./conf/$1.conf -D__SOURCE_NAME=`basename $i .h` -D__SOURCE_UNAME=`basename $i .h | tr '[:lower:]' '[:upper:]'` -o $SOURCE TEMPLATE.h; + elif test ! `basename $i .c` = $BASENAME; then + cpp -P -include ./conf/$1.conf -D__SOURCE_NAME=`basename $i .c` -D__SOURCE_UNAME=`basename $i .c | tr '[:lower:]' '[:upper:]'` -o $SOURCE TEMPLATE.c; + elif test ! `basename $i .cpp` = $BASENAME; then + cpp -P -include ./conf/$1.conf -D__SOURCE_NAME=`basename $i .cpp` -D__SOURCE_UNAME=`basename $i .cpp | tr '[:lower:]' '[:upper:]'` -o $SOURCE TEMPLATE.cpp; + fi + cat $SOURCE | sed s/"\\$\/\\$"/"\/"/g | sed s/"\\$'\\$"/"\""/g | sed s/"\\$:\\$"/"\""/g | sed s/"\\$\#\\$"/"\#"/g | sed s/"\#@\#"/"@"/g | sed s/"\#\#"/""/g > $SOURCE.tmp; + rm -f $SOURCE; + mv $SOURCE.tmp $SOURCE; +done +rm -f ../$1/SOURCES + +echo "Creating symbolic links..." +pushd . > /dev/null +cd ../$1 +rm -f `find . -name \*~` +for i in ../acinclude.m4 ../component.am ../main/share/gambas.h ../main/share/gb_common.h ../reconf ../INSTALL ../COPYING ../missing ../m4; do + ln -s $i; +done +popd > /dev/null + +pushd . > /dev/null +cd ../$1 +(source ./make-component) +rm -f make-component +popd > /dev/null + + diff --git a/TEMPLATE/template/AUTHORS b/TEMPLATE/template/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/ChangeLog b/TEMPLATE/template/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/Makefile.am b/TEMPLATE/template/Makefile.am new file mode 100644 index 00000000..008b94a7 --- /dev/null +++ b/TEMPLATE/template/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @__UNAME##_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/TEMPLATE/template/NEWS b/TEMPLATE/template/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/README b/TEMPLATE/template/README new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/SOURCES b/TEMPLATE/template/SOURCES new file mode 100644 index 00000000..7e45e083 --- /dev/null +++ b/TEMPLATE/template/SOURCES @@ -0,0 +1,2 @@ +__SOURCES + diff --git a/TEMPLATE/template/configure.ac b/TEMPLATE/template/configure.ac new file mode 100644 index 00000000..b4e99781 --- /dev/null +++ b/TEMPLATE/template/configure.ac @@ -0,0 +1,72 @@ +dnl ---- configure.ac for __COMPONENT + +m4_include([../version.m4]) +AC_INIT(gambas3-__COMPONENT_DASH, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(__COMPONENT) +AC_PROG_LIBTOOL + +#if __USE_XWINDOW || __USE_QT +GB_CHECK_XWINDOW +#endif + +#if __USE_QT +GB_FIND_QT_MOC +#endif + +#if __USE_PKGCONFIG + +GB_COMPONENT_PKG_CONFIG( + __NAME, + __UNAME, + __COMPONENT, + [src], + __PKGCONFIG_NAME) + +#else + +#if __USE_C +#define __LIBRARY_C $C_LIB +#else +#define __LIBRARY_C +#endif +#if __USE_CPP +#define __LIBRARY_CPP $CXX_LIB +#else +#define __LIBRARY_CPP +#endif +#if __USE_THREAD +#define __LIBRARY_THREAD $THREAD_LIB +#define __INCLUDE_THREAD $THREAD_INC +#else +#define __LIBRARY_THREAD +#define __INCLUDE_THREAD +#endif + +GB_COMPONENT( + __NAME, + __UNAME, + __COMPONENT, + [src], +#ifdef __SEARCH_INCLUDE + [GB_FIND(__SEARCH_INCLUDE, __SEARCH_INCLUDE_PATH, __SEARCH_INCLUDE_DIR)], +#else + [], +#endif +#ifdef __SEARCH_LIBRARY + [GB_FIND(__SEARCH_LIBRARY, __SEARCH_LIBRARY_PATH, __SEARCH_LIBRARY_DIR)], +#else + [], +#endif + [__LIBRARY_C __LIBRARY_CPP __LIBRARY_THREAD __LIBRARY], + [__INCLUDE_THREAD __INCLUDE]) + +#endif + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/TEMPLATE/template/make-component b/TEMPLATE/template/make-component new file mode 100755 index 00000000..ddce91cd --- /dev/null +++ b/TEMPLATE/template/make-component @@ -0,0 +1,3 @@ +$#$!/bin/sh +cd src +mv .component __COMPONENT.component diff --git a/TEMPLATE/template/src/.component b/TEMPLATE/template/src/.component new file mode 100644 index 00000000..10e636c3 --- /dev/null +++ b/TEMPLATE/template/src/.component @@ -0,0 +1,3 @@ +[Component] +Author= +Alpha=1 diff --git a/TEMPLATE/template/src/Makefile.am b/TEMPLATE/template/src/Makefile.am new file mode 100644 index 00000000..29c457f4 --- /dev/null +++ b/TEMPLATE/template/src/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = __COMPONENT +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = __COMPONENT.la + +__COMPONENT_UNDERSCORE##_la_LIBADD = #@#__UNAME##_LIB@ +__COMPONENT_UNDERSCORE##_la_LDFLAGS = -module @LD_FLAGS@ #@#__UNAME##_LDFLAGS@ +__COMPONENT_UNDERSCORE##_la_CPPFLAGS = #@#__UNAME##_INC@ + +__COMPONENT_UNDERSCORE##_la_SOURCES = __SOURCES + + diff --git a/TODO b/TODO new file mode 100644 index 00000000..b541cdb5 --- /dev/null +++ b/TODO @@ -0,0 +1,74 @@ +TODO list: always incomplete :-) +-------------------------------- + +COMPILER + +- Compilation error must always indicate the column, not sometimes only the line. + +INTERPRETER + +- A special syntax to make variable writable once only (for controls variables). +- Add global parameters for each component. Or no, a global configuration repository, to + tell, for example, which socket the mysql component must use... +- Make objects printable, with a "_print" hidden method or something else. +- Type mismatch error: print the mismatched values. +- Special runtime debugging commands for dumping all the variables for example. +- A new debugging compilation option to only put line number information in the output files. +- If project compilation version < compiler version then => compile all +- Catch kill signal to remove temporary files. +- MOD with floats. +- Make the error information associated with the current stack frame. +- Timer.TimeLeft. +- OPEN ... LOCK. True lock while reading/writing a file. +- select() system call can fail if a watched file descriptor is in error. But how can I know + the offending file descriptor? Maybe by using poll() instead. + +DEBUGGER + +- Add/Remove a breakpoint without pausing the program! + +DEVELOPMENT ENVIRONMENT + +- Manage Object properties in IDE. +- Make a visual control clipboard. +- Generates an index control->component to suggest components for missing controls. +- Be able to open a .tar.gz project, and compress it back when the project is closed. +- Conditional breakpoints. +- Define a control order somewhere for the toolbox. +- An option to interpret "." and "," when reading float in a CSV import. +- Class template files. +- Redesign the source code navigator (F2 and SHIFT+F2). + +GUI RELATED STUFF + +- Deleting a currently expanding item in TreeView crashes. +- ValueBox.Value should be visible in the IDE. +- ListView and GridView selection interface should be the same. +- More clever vertical toolbar. + +DESKTOP COMPONENT + +- Detect an already running application. + +DATABASE COMPONENT + +- Add Views support in database component. +- Changeable client Charset in database driver. +- Returns the number of records affected by a query. +- A new database driver model. +- Copy a result line into another one. + +DOCUMENTATION WIKI + +- Mass rename command. +- Fix last changes: only those in one language. +- A documentation page to explain the Gambas syntax. +- User comments on documentation pages. + +NETWORK COMPONENT + +- Do a big cleanup. + +COMPONENTS + +- Put version number in *.component files, and use it when making dependencies in the IDE. diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..c5b45eb7 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +3.18.0 diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000..edb5080c --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,1727 @@ +####################################################################################### +## +## The following macros are specific to Gambas. +## Some of them are made by me (Benoît Minisini) +## Feel free to use these macros as you need ! +## +## IMPORTANT: This file is shared by all Gambas +## source packages +## +####################################################################################### + +## --------------------------------------------------------------------------- +## GB_MESSAGE +## Prints a message, and stores it in a summay file to print it later +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MESSAGE], +[ + echo "|| $1" >> $srcdir/warnings.log +]) + +## --------------------------------------------------------------------------- +## GB_MESSAGE +## Prints a warning message, and stores it in a summay file to print it later +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_WARNING], +[ + AC_MSG_WARN($1) + GB_MESSAGE([$1]) +]) + +## --------------------------------------------------------------------------- +## GB_CLEAR_MESSAGES +## Clear summary +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CLEAR_MESSAGES], +[ + rm -f $srcdir/warnings.log + touch $srcdir/warnings.log +]) + +## --------------------------------------------------------------------------- +## GB_PRINT_MESSAGES +## Print summary +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_PRINT_MESSAGES], +[ + if test -s $srcdir/warnings.log; then + echo + echo "||" + cat $srcdir/warnings.log + echo "||" + echo + fi + + rm -f $srcdir/warnings.log.before; + + if test -e FAILED && test "x${GAMBAS_CONFIG_FAILURE}" != "x"; then + AC_MSG_ERROR([Failed to configure $3]) + fi +]) + +## --------------------------------------------------------------------------- +## GB_INIT_AUTOMAKE +## automake initialization with common version number +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_INIT_AUTOMAKE], +[ + AM_INIT_AUTOMAKE([subdir-objects]) + m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)]) + AC_CONFIG_HEADERS([config.h]) + AC_PREFIX_DEFAULT(/usr) + MAKEFLAGS=--silent + AC_SUBST(MAKEFLAGS) + + GAMBAS_VERSION=GB_VERSION_MAJOR + GAMBAS_MINOR_VERSION=GB_VERSION_MINOR + + AC_SUBST(GAMBAS_VERSION) + AC_SUBST(GAMBAS_MINOR_VERSION) + + AC_DEFINE(GAMBAS_VERSION, GB_VERSION_MAJOR, Gambas version) + AC_DEFINE(GAMBAS_MINOR_VERSION, GB_VERSION_MINOR, Gambas minor version) + + AC_DEFINE(GAMBAS_VERSION_STRING, "GB_VERSION_MAJOR", Gambas version string) + AC_DEFINE(GAMBAS_FULL_VERSION_STRING, "GB_VERSION_MAJOR.GB_VERSION_MINOR", Gambas full version string) + + AC_DEFINE(GAMBAS_FULL_VERSION, GB_VERSION_FULL, [Full Gambas version]) + AC_DEFINE(GAMBAS_PCODE_VERSION, GB_PCODE_VERSION, [Gambas bytecode version]) + AC_DEFINE(GAMBAS_PCODE_VERSION_MIN, GB_PCODE_VERSION_MIN, [Minimum Gambas bytecode version]) + + GB_CLEAR_MESSAGES +]) + +## --------------------------------------------------------------------------- +## GB_TRUNK_VERSION +## detect version and branch (svn and git supported) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_TRUNK_VERSION], +[ + gb_detect_git=`which git 2> /dev/null` + gb_vcs_hash="" + gb_vcs_branch="" + gb_vcs_version="" + + AC_MSG_CHECKING(for vcs revision) + + if test "x${gb_detect_git}" != "x"; then + gb_vcs_branch=`git rev-parse --abbrev-ref HEAD 2> /dev/null` + gb_vcs_hash=`git rev-parse --short HEAD 2> /dev/null` + else + gb_detect_svn=`which svn 2> /dev/null` + if test "x${gb_detect_svn}" != "x"; then + gb_vcs_hash=`svn info --show-item last-changed-revision 2> /dev/null` + fi + fi + + if test "x${gb_vcs_branch}" != "x"; then + gb_vcs_version="${gb_vcs_hash} (${gb_vcs_branch})" + else + if test "x${gb_vcs_hash}" != "x"; then + gb_vcs_version="r${gb_vcs_hash}" + fi + fi + + if test "x${gb_vcs_version}" != "x"; then + AC_DEFINE_UNQUOTED(TRUNK_VERSION, "${gb_vcs_version}", [vcs revision]) + AC_MSG_RESULT([$gb_vcs_version]) + else + AC_MSG_RESULT(not found) + fi +]) + +## --------------------------------------------------------------------------- +## GB_CONFIG_SUBDIRS +## configuration of a component sub-directory, with a flag for disabling it +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CONFIG_SUBDIRS], +[ + AC_ARG_ENABLE( + $1, + [ --enable-$1 enable $1 component (default: yes)], + gb_enable_$1=$enableval, + gb_enable_$1=yes + ) + + if test "$gb_enable_$1" = "yes"; then + if test -d $srcdir/$2; then + AC_CONFIG_SUBDIRS($2) + $1_dir=$2 + fi + else + GB_WARNING([$1 component is disabled by configure option]) + $1_dir="" + fi + + AC_SUBST($1_dir) +]) + +## --------------------------------------------------------------------------- +## GB_INIT_SHORT GB_INIT GB_LIBTOOL +## configure.ac initialization +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_INIT_SHORT], +[ + AC_CONFIG_SRCDIR([configure.ac]) + AM_MAINTAINER_MODE + + COMPONENT=$1 + + GB_INIT_AUTOMAKE + + AC_CANONICAL_HOST + + gbbindir=$bindir/gambas$GAMBAS_VERSION + AC_SUBST(gbbindir) + gblibdir=$libdir/gambas$GAMBAS_VERSION + AC_SUBST(gblibdir) + gbdatadir=$datadir/gambas$GAMBAS_VERSION + AC_SUBST(gbdatadir) + + AC_PROG_INSTALL + AC_PROG_LN_S +]) + +AC_DEFUN([GB_LIBTOOL], +[ + dnl AC_LIBTOOL_DLOPEN + dnl AC_LIBTOOL_WIN32_DLL + AC_DISABLE_STATIC + + AC_SUBST(INCLTDL) + AC_SUBST(LIBLTDL) + + dnl LD_FLAGS="-Wl,-O1" + if test $SYSTEM == "CYGWIN"; then + LD_FLAGS="$LD_FLAGS -no-undefined" + fi + AC_SUBST(LD_FLAGS) + + AM_LIBTOOLFLAGS="--silent" + AC_SUBST(AM_LIBTOOLFLAGS) +]) + +AC_DEFUN([GB_INIT], +[ + GB_INIT_SHORT($1) + GB_SYSTEM + GB_LIBTOOL + + dnl ---- Checks for headers needed by the following tests + + AC_CHECK_HEADERS(unistd.h) + + dnl ---- Checks for header files. + + dnl AC_HEADER_DIRENT + dnl AC_HEADER_SYS_WAIT + + dnl ---- Checks for typedefs, structures, and compiler characteristics. + + dnl AC_C_CONST + dnl AC_TYPE_PID_T + dnl AC_TYPE_SIZE_T + dnl AC_CHECK_HEADERS_ONCE([sys/time.h]) + + dnl AC_STRUCT_TM + + AC_TYPE_LONG_DOUBLE_WIDER + ac_cv_c_long_double=$ac_cv_type_long_double_wider + if test $ac_cv_c_long_double = yes; then + AC_DEFINE([HAVE_LONG_DOUBLE],[1],[Define to 1 if the type `long double' works and has more range or + precision than `double'.]) + fi + + dnl ---- Checks for library functions. + + dnl AC_FUNC_ALLOCA + dnl AC_PROG_GCC_TRADITIONAL + dnl AC_FUNC_STRCOLL + dnl AC_FUNC_STRFTIME + dnl AC_FUNC_VPRINTF + dnl AC_FUNC_WAIT3 + dnl AC_CHECK_FUNCS(getcwd gettimeofday mkdir rmdir select socket strdup strerror strtod strtol sysinfo) + + AC_CHECK_FUNCS(setenv unsetenv getdomainname getpt cfmakeraw fstatat) + + dnl ---- Checks for libraries + + dnl AC_CHECK_LIB(m, main, echo) + dnl AC_CHECK_LIB(z, main, echo) + + GB_LIBC + + dnl ---- Check for C++ libraries + + AC_CHECK_LIB(gcc_s, main, CXX_LIB="$CXX_LIB -lgcc_s") + AC_CHECK_LIB(stdc++, main, CXX_LIB="$CXX_LIB -lstdc++") + + AC_SUBST(CXX_LIB) + + dnl ---- Check for shared library extension + + GB_SHARED_LIBRARY_EXT() + + dnl ---- Check for threading + + GB_THREAD() + + dnl ---- Check for mathematic libraries + + GB_MATH() + + dnl ---- Check for gettext library + + GB_GETTEXT() + + dnl ---- Check for inotify library + + GB_INOTIFY() + + dnl ---- Check for monotonic clock + + GB_MONOTONIC() + + dnl ---- Support for colorgcc + dnl ---- WARNING: libtool does not support colorgcc! + + dnl AC_PATH_PROG(COLORGCC, colorgcc) + + if test x"$COLORGCC" != x; then + if test "$gambas_colorgcc" = "yes"; then + CC="colorgcc" + CXX="g++" + fi + fi + + dnl ---- Support for ccache + + AC_ARG_ENABLE( + ccache, + [ --enable-ccache use ccache if present (default: yes)], + gambas_ccache=$enableval, + gambas_ccache=yes + ) + + AC_PATH_PROG(CCACHE, ccache) + + if test "$gambas_colorgcc" = "yes"; then + if test x"$CCACHE" != x; then + + CC="ccache $CC" + CXX="ccache $CXX" + + if test x"$COLORGCC" != x; then + if test "$gambas_colorgcc" = "yes"; then + CC="colorgcc" + CXX="colorgcc" + fi + fi + + fi + fi + + dnl ---- debug option + + AC_ARG_ENABLE( + debug, + [ --enable-debug compile for debugging (default: yes)], + gambas_debug=$enableval, + gambas_debug=yes + ) + + AM_CONDITIONAL(DEBUG, test "$gambas_debug" = yes) + + dnl ---- optimization option + + AC_ARG_ENABLE( + optimization, + [ --enable-optimization compile with optimizations (default: yes)], + gambas_optimization=$enableval, + gambas_optimization=yes + ) + + AM_CONDITIONAL(OPTIMIZE, test "$gambas_optimization" = yes) + + AM_CFLAGS="$AM_CFLAGS -pipe -Wall -Wno-unused-value -fsigned-char" + if test $SYSTEM = "MACOSX"; then + AM_CFLAGS="$AM_CFLAGS -fnested-functions" + fi + + AM_CXXFLAGS="$AM_CXXFLAGS -pipe -Wall -fno-exceptions -Wno-unused-value -fsigned-char" + + dnl ---- Check for gcc visibility flag + + have_gcc_visibility=no + + if test $SYSTEM != "CYGWIN"; then + GB_CFLAGS_GCC_OPTION([-fvisibility=hidden],, + [ + AM_CFLAGS="$AM_CFLAGS -fvisibility=hidden" + AM_CXXFLAGS="$AM_CXXFLAGS -fvisibility=hidden" + have_gcc_visibility=yes]) + fi + + if test "$have_gcc_visibility" = "yes"; then + AC_DEFINE(HAVE_GCC_VISIBILITY, 1, [Whether gcc supports -fvisibility=hidden]) + fi + + dnl ---- check for -flto compiler flag + + GB_CFLAGS_GCC_OPTION([-flto],, + [ + GB_CFLAGS_LTO=" -flto" + have_gcc_lto=yes + ]) + + AC_ARG_ENABLE( + lto, + [ --enable-lto enable link time optimization (default: no)], + gambas_lto=$enableval, + gambas_lto=no + ) + + if test "$gambas_lto" = "no"; then + have_gcc_lto=no; + GB_CFLAGS_LTO=""; + fi + + if test "$have_gcc_lto" = "yes"; then + AC_DEFINE(HAVE_GCC_LTO, 1, [Whether gcc supports -flto]) + fi + + dnl ---- check for -std=c++11 compiler flag + + GB_CXXFLAGS_GCC_OPTION([-std=c++11],, + [ + GB_CXXFLAGS_STD_CPP11=" -std=c++11" + have_gcc_std_cpp11x=yes + ]) + + if test "$have_gcc_std_cpp11" = "yes"; then + AC_DEFINE(HAVE_GCC_STD_CPP11, 1, [Whether g++ supports -std=c++11]) + fi + + dnl ---- check for -std=c++17 compiler flag + + GB_CXXFLAGS_GCC_OPTION([-std=c++17],, + [ + GB_CXXFLAGS_STD_CPP17=" -std=c++17" + have_gcc_std_cpp17x=yes + ]) + + if test "$have_gcc_std_cpp17" = "yes"; then + AC_DEFINE(HAVE_GCC_STD_CPP17, 1, [Whether g++ supports -std=c++17]) + fi + + dnl ---- Debug flags + + if test "$gambas_debug" = "yes"; then + AM_CFLAGS="$AM_CFLAGS -g -ggdb" + AM_CXXFLAGS="$AM_CXXFLAGS -g -ggdb" + fi + + dnl ---- Optimization flags + + if test "x$gambas_optimization" = "xyes"; then + AM_CFLAGS_OPT="$AM_CFLAGS -O3" + AM_CFLAGS="$AM_CFLAGS -O2" + AM_CXXFLAGS_OPT="$AM_CXXFLAGS -O3 -fno-omit-frame-pointer" + AM_CXXFLAGS="$AM_CXXFLAGS -O2 -fno-omit-frame-pointer" + else + AM_CFLAGS_OPT="$AM_CFLAGS -O0" + AM_CFLAGS="$AM_CFLAGS -O0" + AM_CXXFLAGS_OPT="$AM_CXXFLAGS -O0" + AM_CXXFLAGS="$AM_CXXFLAGS -O0" + fi + + dnl ---- Checks for programs + + save_CFLAGS=$CFLAGS + AC_PROG_CPP + AC_PROG_CXX + AC_PROG_CC + AC_PROG_MAKE_SET + CFLAGS=$save_CFLAGS + + AC_SUBST(AM_CFLAGS) + AC_SUBST(AM_CFLAGS_OPT) + AC_SUBST(AM_CXXFLAGS) + AC_SUBST(AM_CXXFLAGS_OPT) + AC_SUBST(GB_CFLAGS_LTO) + AC_SUBST(GB_CXXFLAGS_STD_CPP11) + + rm -f DISABLED DISABLED.* FAILED +]) + + +## --------------------------------------------------------------------------- +## GB_THREAD +## Detect threading compiler options +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_THREAD], +[ + case "${host}" in + *-*-freebsd* | *-*-netbsd* | *-*-darwin* ) + THREAD_LIB="" + THREAD_INC="-pthread -D_REENTRANT" + GBX_THREAD_LIB="" + GBX_THREAD_INC="-pthread -D_REENTRANT" + GBX_THREAD_LDFLAGS="" + ;; + *-*-haiku* ) + THREAD_LIB="" + THREAD_INC="" + GBX_THREAD_LIB="" + GBX_THREAD_INC="" + GBX_THREAD_LDFLAGS="" + ;; + *) + THREAD_LIB="-lpthread" + THREAD_INC="-D_REENTRANT" + GBX_THREAD_LIB="-lpthread" + GBX_THREAD_INC="-D_REENTRANT" + GBX_THREAD_LDFLAGS="-Wl,--no-as-needed" + ;; + esac + + AC_MSG_CHECKING(for threading compiler options) + AC_MSG_RESULT($THREAD_INC) + AC_MSG_CHECKING(for threading linker options) + AC_MSG_RESULT($THREAD_LIB) + + AC_SUBST(THREAD_LIB) + AC_SUBST(THREAD_INC) + AC_SUBST(GBX_THREAD_LIB) + AC_SUBST(GBX_THREAD_INC) + AC_SUBST(GBX_THREAD_LDFLAGS) +]) + + +## --------------------------------------------------------------------------- +## GB_LIBC +## Detect C library +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_LIBC], +[ + case "${host}" in + *-*-haiku* ) + dnl Haiku has implicit C library in libroot. + C_LIB="" + ;; + *) + C_LIB="-lc" + ;; + esac + + AC_MSG_CHECKING(for C library) + AC_MSG_RESULT($C_LIB) + + AC_SUBST(C_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_MATH +## Detect mathematic libraries +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MATH], +[ + case "${host}" in + *-*-haiku* ) + MATH_LIB="" + ;; + *) + MATH_LIB="-lm" + ;; + esac + + AC_MSG_CHECKING(for mathematic libraries) + AC_MSG_RESULT($MATH_LIB) + + AC_SUBST(MATH_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_CHECK_MATH_FUNC +## Check a specific mathematical function +## +## $1 = name of the function +## $2 = macro to define +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CHECK_MATH_FUNC], +[AC_CACHE_CHECK(for $1, + gb_cv_math_$1, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _ISOC9X_SOURCE 1 + #define _ISOC99_SOURCE 1 + #define _GNU_SOURCE 1 + #define __USE_ISOC99 1 + #define __USE_ISOC9X 1 + #include + ]], [[ + int value = $1 (1.0); + ]])],[gb_cv_math_$1=yes],[gb_cv_math_$1=no + ])]) + + if test $gb_cv_math_$1 = yes; then + AC_DEFINE(HAVE_$2, 1, [Define if you have $1 function.]) + fi +]) + +## --------------------------------------------------------------------------- +## GB_MATH_FUNC +## Detect which mathematical functions are available +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MATH_FUNC], +[ + ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -$MATH_LIB" + + GB_CHECK_MATH_FUNC(exp10, EXP10) + GB_CHECK_MATH_FUNC(exp2, EXP2) + GB_CHECK_MATH_FUNC(log2, LOG2) + + LDFLAGS=$ac_save_LDFLAGS +]) + + +## --------------------------------------------------------------------------- +## GB_SYSTEM +## Detects the target system and its architecture +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_SYSTEM], +[ + AC_MSG_CHECKING(target system) + + case "${host}" in + *-*-linux*-gnu* ) + SYSTEM=LINUX + AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_LINUX, 1, [Target system is Linux]) + AC_DEFINE(SYSTEM, "Linux", [Operating system]) + ;; + *-*-linux* ) + SYSTEM=LINUX + AC_DEFINE(OS_LINUX, 1, [Target system is Linux]) + AC_DEFINE(SYSTEM, "Linux", [Operating system]) + ;; + *-*-freebsd* ) + SYSTEM=FREEBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_FREEBSD, 1, [Target system is FreeBSD]) + AC_DEFINE(SYSTEM, "FreeBSD", [Operating system]) + ;; + *-*-netbsd* ) + SYSTEM=NETBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_NETBSD, 1, [Target system is NetBSD]) + AC_DEFINE(SYSTEM, "NetBSD", [Operating system]) + ;; + *-*-openbsd* ) + SYSTEM=OPENBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_OPENBSD, 1, [Target system is OpenBSD]) + AC_DEFINE(SYSTEM, "OpenBSD", [Operating system]) + ;; + *-*-cygwin* ) + SYSTEM=CYGWIN + AC_DEFINE(OS_CYGWIN, 1, [Target system is Cygwin/Windows]) + AC_DEFINE(SYSTEM, "Cygwin", [Operating system]) + ;; + *-*-darwin* | *-*-rhapsody* ) + SYSTEM=MACOSX + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_FREEBSD, 1, [Target system is FreeBSD]) + AC_DEFINE(OS_MACOSX, 1, [Target system is MacOS X]) + AC_DEFINE(SYSTEM, "MacOSX", [Operating system]) + ;; + *-*-solaris* ) + SYSTEM=SOLARIS + AC_DEFINE(OS_SOLARIS, 1, [Target system is Solaris]) + AC_DEFINE(SYSTEM, "Solaris", [Operating system]) + ;; + *-*-k*bsd*-gnu* ) + SYSTEM=KFREEBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_KFREEBSD, 1, [Target system is kFREEBSD]) + AC_DEFINE(SYSTEM, "kFreeBSD", [Operating system]) + ;; + *-gnu* ) + SYSTEM=HURD + AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_HURD, 1, [Target system is Hurd]) + AC_DEFINE(SYSTEM, "Hurd", [Operating system]) + ;; + *-*-haiku* ) + SYSTEM=HAIKU + dnl AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_HAIKU, 1, [Target system is Haiku]) + AC_DEFINE(SYSTEM, "Haiku", [Operating system]) + ;; + * ) + SYSTEM=UNKNOWN + AC_DEFINE(SYSTEM, "unknown", [Operating system]) + GB_MESSAGE([System is unknown]) + ;; + esac + + AC_MSG_RESULT($SYSTEM) + + AC_MSG_CHECKING(target architecture) + + case "${host}" in + i*86-*-* ) + ARCH=X86 + AC_DEFINE(ARCH_X86, 1, [Target architecture is x86]) + AC_DEFINE(ARCHITECTURE, "x86", [Architecture]) + ;; + x86_64-*-* | amd64-* | ia64-* ) + ARCH=X86_64 + AC_DEFINE(ARCH_X86_64, 1, [Target architecture is x86_64]) + AC_DEFINE(ARCHITECTURE, "x86_64", [Architecture]) + ;; + arm*-*-* ) + ARCH=ARM + AC_DEFINE(ARCH_ARM, 1, [Target architecture is ARM]) + AC_DEFINE(ARCHITECTURE, "arm", [Architecture]) + ;; + aarch64*-*-* ) + ARCH=AARCH64 + AC_DEFINE(ARCH_AARCH64, 1, [Target architecture is AARCH64]) + AC_DEFINE(ARCHITECTURE, "aarch64", [Architecture]) + ;; + powerpc*-*-* ) + ARCH=PPC + AC_DEFINE(ARCH_PPC, 1, [Target architecture is PowerPC]) + AC_DEFINE(ARCHITECTURE, "powerpc", [Architecture]) + ;; + *) + ARCH=UNKNOWN + AC_DEFINE(ARCHITECTURE, "unknown", [Architecture]) + GB_MESSAGE([Architecture is unknown]) + ;; + esac + + AC_MSG_RESULT($ARCH) +]) + + +## --------------------------------------------------------------------------- +## GB_SHARED_LIBRARY_EXT +## Detects shared library extension +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_SHARED_LIBRARY_EXT], +[ + AC_MSG_CHECKING(which extension is used for shared libraries) + + case "${host}" in + *-*-cygwin* ) + SHLIBEXT="dll.a" + AC_DEFINE(SHARED_LIBRARY_EXT, "dll", [Shared library extension is '.dll.a']) + ;; + *-*-darwin* ) + SHLIBEXT="dylib" + AC_DEFINE(SHARED_LIBRARY_EXT, "dylib", [Shared library extension is '.dylib']) + ;; + *) + SHLIBEXT="so" + AC_DEFINE(SHARED_LIBRARY_EXT, "so", [Shared library extension is '.so']) + ;; + esac + + AC_SUBST(SHLIBEXT) + + AC_MSG_RESULT([.$SHLIBEXT]) +]) + + +## --------------------------------------------------------------------------- +## GB_GETTEXT +## Detects if we must link to an external gettext library +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_GETTEXT], +[ + AC_MSG_CHECKING(for external gettext library) + + case "${host}" in + *-*-openbsd* ) + GETTEXT_LIB=-llibgettext + ;; + *) + GETTEXT_LIB= + ;; + esac + + AC_SUBST(GETTEXT_LIB) + AC_SUBST(GETTEXT_LDFLAGS) + + AC_MSG_RESULT($GETTEXT_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_INOTIFY +## Detects if we must link to an external inotify library +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_INOTIFY], +[ + AC_MSG_CHECKING(for external inotify library) + + case "${host}" in + *-*-linux* ) + GB_INOTIFY_LIB= + ;; + *) + GB_INOTIFY_LIB=-linotify + ;; + esac + + AC_SUBST(GB_INOTIFY_LIB) + AC_MSG_RESULT($GB_INOTIFY_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_MONOTONIC +## Detect monotonic clock +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MONOTONIC], +[ + AC_CACHE_CHECK(for monotonic clock, gb_cv_monotonic_clock, + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #ifdef HAVE_UNISTD_H + #include + #endif + ]], [[ + #if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0 || !defined(CLOCK_MONOTONIC) + #error Either _POSIX_MONOTONIC_CLOCK or CLOCK_MONOTONIC not defined + #endif + ]])],[ + gb_cv_monotonic_clock=yes + ],[ + gb_cv_monotonic_clock=no + ]) + ) + + if test "$gb_cv_monotonic_clock" = "yes"; then + + AC_DEFINE(HAVE_MONOTONIC_CLOCK,1,[Have a monotonic clock]) + + ac_save_LIBS="$LIBS" + AC_SEARCH_LIBS(clock_gettime, rt) + RT_LIB=$LIBS + LIBS=$ac_save_LIBS + + fi + + AC_SUBST(RT_LIB) + AC_SUBST(RT_LDFLAGS) +]) + + + + +## --------------------------------------------------------------------------- +## GB_FIND +## Find files in directories +## +## $1 = Files to search +## $2 = Directories +## $3 = Sub-directories patterns +## +## Returns a path list in $gb_val +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_FIND], +[ +dnl echo "Searching $1, $2, $3" +gb_val="" +gb_save=`pwd` +gb_file_list="$1" + +gb_main_dir_list="$2" +gb_sub_dir_list="$3" + +gb_sub_dir_list_64=`echo "$gb_sub_dir_list" | sed s/"lib"/"lib64"/g` + +if test $SYSTEM == "HAIKU"; then + gb_arch="`getarch`" + gb_main_dir_list="$gb_main_dir_list `findpaths -c' ' -a "$gb_arch" B_FIND_PATH_DEVELOP_DIRECTORY`" + gb_arch_inc_subdir=headers + gb_arch_lib_subdir=lib + if test "$gb_arch" != "`getarch -p`"; then + gb_arch_inc_subdir="headers/$gb_arch" + gb_arch_lib_subdir="lib/$gb_arch" + fi + gb_sub_dir_list=`echo "$gb_sub_dir_list" | sed "s:include:$gb_arch_inc_subdir:g;s:lib:$gb_arch_lib_subdir:g"` +fi + +## if there is 'lib' inside sub-directories, then we decide to search "lib64" first. + +if test "$gb_sub_dir_list_64" != "$gb_sub_dir_list"; then + gb_sub_dir_list="$gb_sub_dir_list_64 $gb_sub_dir_list"; + + gb_main_dir_list_64=`echo "$gb_main_dir_list" | sed s/"lib"/"lib64"/g` + + if test "$gb_main_dir_list_64" != "$gb_main_dir_list"; then + gb_main_dir_list="$gb_main_dir_list_64 $gb_main_dir_list"; + fi + +fi + +for gb_main_dir in $gb_main_dir_list; do + dnl echo "search $gb_main_dir" + if test -d $gb_main_dir; then + cd $gb_main_dir + for gb_search_dir in $gb_sub_dir_list; do + for gb_dir in $gb_search_dir/ $gb_search_dir/*/ $gb_search_dir/*/*/ $gb_search_dir/*/*/*/; do + + dnl echo "search subdir $gb_dir" + gb_new_file_list="" + gb_find_dir="" + + for gb_file in $gb_file_list; do + + dnl echo "search file $gb_file" + gb_find=no + if test -r "$gb_main_dir/$gb_dir/$gb_file" || test -d "$gb_main_dir/$gb_dir/$gb_file"; then + + ifelse($4,[], + + gb_find=yes, + + for gb_test in $4; do + gb_output=`ls -la $gb_main_dir/$gb_dir/$gb_file | grep "$gb_test"` + if test "x$gb_output" != "x"; then + gb_find=yes + fi + done + ) + + fi + + if test "$gb_find" = "yes"; then + dnl echo "FOUND!" + if test "x$gb_find_dir" = "x"; then + if test "x$gb_val" = "x"; then + gb_val="$gb_main_dir/$gb_dir" + else + gb_val="$gb_val $gb_main_dir/$gb_dir" + fi + fi + gb_find_dir=yes + else + gb_new_file_list="$gb_new_file_list $gb_file" + fi + + done + + gb_file_list=$gb_new_file_list + + if test "x$gb_file_list" = "x " || test "x$gb_file_list" = "x"; then + break 3 + fi + + done + done + fi +done + +if test "x$gb_file_list" != "x " && test "x$gb_file_list" != "x"; then + gb_val=no +fi + +cd $gb_save +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT_PKG_CONFIG +## Component detection macro based on pkg-config +## +## $1 = Component key in lower case (ex: pgsql) +## $2 = Component key in upper case (ex: PGSQL) +## $3 = Component name (ex: gb.db.postgresql) +## $4 = Sub-directory name +## $5 = pkg-config module(s) name(s) with optional required version(s) +## $6 = Warning message (optional) +## +## => defines HAVE_*_COMPONENT (to know if you can compile the component) +## *_INC (for the compiler) and *_LIB / *_LDFLAGS (for the linker) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT_PKG_CONFIG], +[ + AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + AC_ARG_ENABLE( + $1, + [ --enable-$1 enable $3 (default: yes)], + gb_enable_$1=$enableval, + gb_enable_$1=yes + ) + + dnl AC_ARG_WITH($1-includes, + dnl [ --with-$1-includes where the $3 headers are located. ], + dnl [ gb_inc_$1="$withval" ]) + + dnl AC_ARG_WITH($1-libraries, + dnl [ --with-$1-libraries where the $3 libraries are located. ], + dnl [ gb_lib_$1="$withval" ]) + + cp warnings.log warnings.log.before + + have_$1=no + + if test "$gb_enable_$1" = "yes" && test ! -e DISABLED && test ! -e DISABLED.$3; then + + AC_MSG_CHECKING(for $3 component with pkg-config) + + gb_inc_$1="" + gb_lib_$1="" + gb_ldflags_$1="" + have_$1=yes + gb_testval="" + + $PKG_CONFIG --silence-errors --exists $5 + if test $? -eq "0"; then + + ## Checking for headers + + $2_INC="`$PKG_CONFIG --cflags $5`" + + ## Checking for libraries + + $2_LIB="`$PKG_CONFIG --libs-only-l $5`" + $2_LDFLAGS="`$PKG_CONFIG --libs-only-L $5` `$PKG_CONFIG --libs-only-other $5`" + $2_DIR=$4 + + else + + have_$1=no + + fi + + fi + + if test "$have_$1" = "no"; then + + if test "$gb_in_component_search" != "yes"; then + touch DISABLED.$3 + if test "$gb_enable_$1" = "yes"; then + touch FAILED + fi + fi + + if test "$gb_enable_$1" = "yes"; then + AC_MSG_RESULT(failed) + fi + + for pkgcmp in $5 + do + + $PKG_CONFIG --silence-errors --exists $pkgcmp + if test $? -eq "1"; then + GB_WARNING([Unable to met pkg-config requirement: $pkgcmp]) + fi + + done + + else + + AC_DEFINE(HAVE_$2_COMPONENT, 1, [Have $3 component]) + + AC_MSG_RESULT(OK) + + fi + + if test "$have_$1" = "no"; then + + $2_INC="" + $2_LIB="" + $2_LDFLAGS="" + $2_DIR="" + if test "$gb_in_component_search" != "yes"; then + if test x"$6" = x; then + GB_MESSAGE([$3 is disabled]) + else + GB_MESSAGE([$6]) + fi + fi + + fi + + AC_SUBST($2_INC) + AC_SUBST($2_LIB) + AC_SUBST($2_LDFLAGS) + AC_SUBST($2_DIR) +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT_PKG_CONFIG_AGAIN +## Try again a component detection macro based on pkg-config +## +## $1 = Component key in lower case (ex: pgsql) +## $2 = Component key in upper case (ex: PGSQL) +## $3 = Component name (ex: gb.db.postgresql) +## $4 = Sub-directory name +## $5 = pkg-config module(s) name(s) with optional required version(s) +## $6 = Try again message +## $7 = Warning message (optional) +## +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT_PKG_CONFIG_AGAIN], +[ + if test "$have_$1" != "yes"; then + + AC_MSG_WARN([$6...]) + rm DISABLED.$3 FAILED; + cp warnings.log.before warnings.log; + + GB_COMPONENT_PKG_CONFIG($1, $2, $3, $4, $5, $7) + + fi +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT +## Component detection macro that searches for files +## +## $1 = Component key in lower case (ex: postgresql) +## $2 = Component key in upper case (ex: POSTGRESQL) +## $3 = Component name (ex: gb.db.postgresql) +## $4 = Sub-directory name +## $5 = How to get include path (must return it in gb_val) +## $6 = How to get library path (must return it in gb_val) +## $7 = Libraries +## $8 = Compiler flags (optional) +## $9 = Warning message (optional) +## +## => defines HAVE_*_COMPONENT (to know if you can compile the component) +## *_INC (for the compiler) and *_LIB (for the linker) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT], +[ + AC_ARG_ENABLE( + $1, + [ --enable-$1 enable $3 (default: yes)], + gb_enable_$1=$enableval, + gb_enable_$1=yes + ) + + gb_inc_$1=no + gb_lib_$1=no + + if test "$gb_enable_$1" = "yes" && test ! -e DISABLED && test ! -e DISABLED.$3; then + + ## Checking for headers + + AC_MSG_CHECKING(for $3 headers) + + AC_ARG_WITH($1-includes, + [ --with-$1-includes where the $3 headers are located. ], + [ gb_inc_$1="$withval" ]) + + AC_CACHE_VAL(gb_cv_header_$1, [ + + if test "$gb_inc_$1" = no; then + gb_val="" + $5 + gb_inc_$1=$gb_val + fi + + gb_cv_header_$1=$gb_inc_$1 + ]) + + AC_MSG_RESULT([$gb_cv_header_$1]) + + if test "$gb_cv_header_$1" = "no"; then + for gb_result in $gb_file_list; do + GB_WARNING([Unable to find file: $gb_result]) + done + fi + + $2_INC="" + + for gb_dir in $gb_cv_header_$1; do + if test "$gb_dir" != "/usr/include"; then + if test "$gb_dir" != "/usr/include/"; then + $2_INC="$$2_INC -I$gb_dir" + fi + fi + done + + if test "x$8" != "x"; then + $2_INC="$$2_INC $8" + fi + + if test "$gb_cv_header_$1" = no; then + have_inc_$1="no" + $2_INC="" + else + have_inc_$1="yes" + fi + + ## Checking for libraries + + AC_MSG_CHECKING(for $3 libraries) + + AC_ARG_WITH($1-libraries, + [ --with-$1-libraries where the $3 libraries are located. ], + [ gb_lib_$1="$withval" ]) + + AC_CACHE_VAL(gb_cv_lib_$1, [ + + if test "$gb_lib_$1" = no; then + gb_val="" + $6 + gb_lib_$1=$gb_val + fi + + gb_cv_lib_$1=$gb_lib_$1 + ]) + + if test "$gb_cv_lib_$1" = no; then + have_lib_$1="no" + else + have_lib_$1="yes" + fi + + AC_MSG_RESULT([$gb_cv_lib_$1]) + + if test "$gb_cv_lib_$1" = "no"; then + for gb_result in $gb_file_list; do + GB_WARNING([Unable to find file: $gb_result]) + done + fi + + $2_LIB="" + $2_LDFLAGS="" + $2_PATH="" + + for gb_dir in $gb_cv_lib_$1; do + if test "x$$2_PATH" = "x"; then + $2_PATH="$gb_dir/.." + fi + if test "$gb_dir" != "/lib" && test "$gb_dir" != "/lib/"&& test "$gb_dir" != "/usr/lib" && test "$gb_dir" != "/usr/lib/"; then + $2_LDFLAGS="$$2_LDFLAGS -L$gb_dir"; + fi + done + + $2_LIB="$$2_LIB $7" + + fi + + if test "$have_inc_$1" = "yes" && test "$have_lib_$1" = "yes"; then + + have_$1=yes + $2_DIR=$4 + AC_DEFINE(HAVE_$2_COMPONENT, 1, Have $3) + + else + + have_$1=no + touch DISABLED.$3 + if test "$gb_enable_$1" = "yes"; then + touch FAILED + fi + + fi + + if test "$have_$1" = "no"; then + + $2_INC="" + $2_LIB="" + $2_DIR="" + $2_LDFLAGS="" + if test x"$9" = x; then + GB_WARNING([$3 is disabled]) + else + GB_WARNING([$9]) + fi + + fi + + AC_SUBST($2_INC) + AC_SUBST($2_LIB) + AC_SUBST($2_LDFLAGS) + AC_SUBST($2_DIR) + AC_SUBST($2_PATH) + +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT_SEARCH +## Component detection macro that uses GB_COMPONENT_PKG_CONFIG first, and +## then GB_COMPONENT. +## +## $1 = Component key in lower case (ex: postgresql) +## $2 = Component key in upper case (ex: POSTGRESQL) +## $3 = Component name (ex: PostgreSQL) +## $4 = Sub-directory name +## $5 = pkg-config module name (optional) +## $6 = How to get include path (must return it in gb_val) +## $7 = How to get library path (must return it in gb_val) +## $8 = Libraries +## $9 = Compiler flags (optional) +## $10 = Warning message (optional) +## +## => defines HAVE_*_COMPONENT (to know if you can compile the component) +## *_INC (for the compiler) and *_LIB (for the linker) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT_SEARCH], +[ +gb_in_component_search=yes + GB_COMPONENT_PKG_CONFIG( + $1, + $2, + $3, + $4, + $5, + $10 + ) +gb_in_component_search=no + if test -z "${$2_LIB}"; then + GB_COMPONENT( + $1, + $2, + $3, + $4, + $6, + $7, + $8, + $9, + $10 + ) + fi +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT_SEARCH_BOTH +## Component detection macro that uses GB_COMPONENT_PKG_CONFIG and +## GB_COMPONENT. both having to succeed +## +## $1 = Component key in lower case (ex: postgresql) +## $2 = Component key in upper case (ex: POSTGRESQL) +## $3 = Component name (ex: gb.db.postgresql) +## $4 = Sub-directory name +## $5 = pkg-config module name (optional) +## $6 = How to get include path (must return it in gb_val) +## $7 = How to get library path (must return it in gb_val) +## $8 = Libraries +## $9 = Compiler flags (optional) +## $10 = Warning message (optional) +## +## => defines HAVE_*_COMPONENT (to know if you can compile the component) +## *_INC (for the compiler) and *_LIB (for the linker) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT_SEARCH_BOTH], +[ + GB_COMPONENT_PKG_CONFIG( + $1, + $2, + $3, + $4, + $5, + $10 + ) + if test ! -e DISABLED.$3; then + GB_COMPONENT( + $1, + $2, + $3, + $4, + $6, + $7, + $8, + $9, + $10 + ) + fi +]) + + +## --------------------------------------------------------------------------- +## GB_FIND_QT_MOC +## Find QT moc compiler +## +## $1 = QT version +## $2 = components to disable +## +## Returns a path list in $gb_val +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_FIND_QT_MOC], +[ + gb_path_qt_moc=no + if test x$1 = x; then + gb_qt_version=3 + else + gb_qt_version=$1 + fi + + AC_ARG_WITH(moc, + [ --with-moc The path to the QT moc compiler. ], + [ gb_path_qt_moc="$withval" ]) + + AC_MSG_CHECKING(for QT meta-object compiler) + + AC_CACHE_VAL(gb_cv_path_qt_moc, [ + + gb_val="" + if test "$gb_path_qt_moc" = no; then + + for gb_dir in $QTDIR /usr/lib/qt$gb_qt_version /usr/lib/qt/$gb_qt_version /usr/local/lib/qt$gb_qt_version /usr/local/lib/qt/$gb_qt_version /usr/local/qt$gb_qt_version /usr/local/qt/$gb_qt_version /usr/share/qt$gb_qt_version /usr/qt/$gb_qt_version /usr/pkg/qt$gb_qt_version /usr/pkg /usr; do + + gb_dir=$gb_dir/bin + + if test -r "$gb_dir/moc"; then + if test "x`$gb_dir/moc -v 2>&1 | grep " $gb_qt_version\."`" != x; then + gb_val=$gb_dir/moc + break + fi + fi + + done + + gb_path_qt_moc=$gb_val + fi + + gb_cv_path_qt_moc=$gb_path_qt_moc + ]) + + AC_MSG_RESULT([$gb_cv_path_qt_moc]) + + if test x"$gb_cv_path_qt_moc" = x; then + GB_WARNING([QT moc compiler not found. Try --with-moc option.]) + MOC="" + touch DISABLED + else + MOC=$gb_cv_path_qt_moc + fi + + AC_SUBST(MOC) +]) + +## --------------------------------------------------------------------------- +## GB_CHECK_XWINDOW +## Check the X-Window system installation +## +## $1 = components to disable +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CHECK_XWINDOW], +[ + AC_PATH_XTRA + + if test x"$have_x" = xyes; then + if test -z `echo $X_LIBS | grep "\-lX11"`; then + X_LIBS="$X_LIBS -lX11" + fi + if test -z `echo $X_LIBS | grep "\-lXext"`; then + X_LIBS="$X_LIBS -lXext" + fi + X_LIBS="$X_PRE_LIBS $X_LIBS" + else + touch DISABLED + fi + +]) + +## --------------------------------------------------------------------------- +## Some macros +## --------------------------------------------------------------------------- + +dnl Like AC_CHECK_HEADER, but it uses the already-computed -I directories. + +AC_DEFUN([AC_CHECK_X_HEADER], [ + ac_save_CPPFLAGS="$CPPFLAGS" + if test \! -z "$includedir" ; then + CPPFLAGS="$CPPFLAGS -I$includedir" + fi + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + AC_CHECK_HEADER([$1],[$2],[$3]) + CPPFLAGS="$ac_save_CPPFLAGS" +]) + +dnl Like AC_CHECK_LIB, but it used the -L dirs set up by the X checks. + +AC_DEFUN([AC_CHECK_X_LIB], [ + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LDFLAGS="$LDFLAGS" + + if test \! -z "$includedir" ; then + CPPFLAGS="$CPPFLAGS -I$includedir" + fi + + dnl note: $X_CFLAGS includes $x_includes + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + + if test \! -z "$libdir" ; then + LDFLAGS="$LDFLAGS -L$libdir" + fi + + dnl note: $X_LIBS includes $x_libraries + + LDFLAGS="$LDFLAGS $X_LIBS" + AC_CHECK_LIB([$1], [$2], [$3], [$4], [$5]) + CPPFLAGS="$ac_save_CPPFLAGS" + LDFLAGS="$ac_save_LDFLAGS"] +) + +dnl Check if it is possible to turn off run time type information (RTTI) +AC_DEFUN([AC_PROG_CXX_FNO_RTTI], +[AC_CACHE_CHECK(whether ${CXX-g++} accepts -fno-rtti, ac_cv_prog_cxx_fno_rtti, +[echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -fno-rtti -c conftest.cc 2>&1`"; then + ac_cv_prog_cxx_fno_rtti=yes + CXXFLAGS="${CXXFLAGS} -fno-rtti" +else + ac_cv_prog_cxx_fno_rtti=no +fi +rm -f conftest* +])]) + +dnl Check if the type socklen_t is defined anywhere +AC_DEFUN([AC_C_SOCKLEN_T], +[AC_CACHE_CHECK(for socklen_t, ac_cv_c_socklen_t, +[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +]], [[ +socklen_t foo; +]])],[ + ac_cv_c_socklen_t=yes +],[ + ac_cv_c_socklen_t=no + AC_DEFINE(socklen_t,int) +])])]) + +dnl Check for sys_errlist[] and sys_nerr, check for declaration +dnl Check nicked from aclocal.m4 from GNU bash 2.01 +AC_DEFUN([AC_SYS_ERRLIST], +[AC_MSG_CHECKING([for sys_errlist and sys_nerr]) +AC_CACHE_VAL(ac_cv_sys_errlist, +[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[extern char *sys_errlist[]; + extern int sys_nerr; + char *msg = sys_errlist[sys_nerr - 1];]])],[ac_cv_sys_errlist=yes],[ac_cv_sys_errlist=no])])dnl +AC_MSG_RESULT($ac_cv_sys_errlist) +if test $ac_cv_sys_errlist = yes; then +AC_DEFINE(HAVE_SYS_ERRLIST) +fi +]) + +dnl @synopsis AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]]) +dnl +dnl AX_CFLAGS_GCC_OPTION(-fvomit-frame) would show a message as like +dnl "checking CFLAGS for gcc -fvomit-frame ... yes" and adds the +dnl optionflag to CFLAGS if it is understood. You can override the +dnl shellvar-default of CFLAGS of course. The order of arguments stems +dnl from the explicit macros like AX_CFLAGS_WARN_ALL. +dnl +dnl The cousin AX_CXXFLAGS_GCC_OPTION would check for an option to add +dnl to CXXFLAGS - and it uses the autoconf setup for C++ instead of C +dnl (since it is possible to use different compilers for C and C++). +dnl +dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or +dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options. +dnl However, if you use this macro in a few places, it would be great +dnl if you would make up a new function-macro and submit it to the +dnl ac-archive. +dnl +dnl - $1 option-to-check-for : required ("-option" as non-value) +dnl - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case) +dnl - $3 action-if-found : add value to shellvariable +dnl - $4 action-if-not-found : nothing +dnl +dnl note: in earlier versions, $1-$2 were swapped. We try to detect the +dnl situation and accept a $2=~/-/ as being the old +dnl option-to-check-for. +dnl +dnl also: there are other variants that emerged from the original macro +dnl variant which did just test an option to be possibly added. +dnl However, some compilers accept an option silently, or possibly for +dnl just another option that was not intended. Therefore, we have to do +dnl a generic test for a compiler family. For gcc we check "-pedantic" +dnl being accepted which is also understood by compilers who just want +dnl to be compatible with gcc even when not being made from gcc +dnl sources. +dnl +dnl see also: +dnl +dnl AX_CFLAGS_SUN_OPTION AX_CFLAGS_HPUX_OPTION +dnl AX_CFLAGS_AIX_OPTION AX_CFLAGS_IRIX_OPTION +dnl +dnl @category C +dnl @author Guido Draheim +dnl @version 2003-11-04 +dnl @license GPLWithACException + +AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG([C]) + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl ------------------------------------------------------------------------- + +AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG([C]) + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])]) + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[AX_CXXFLAGS_GCC_OPTION_NEW($@)],[AX_CXXFLAGS_GCC_OPTION_OLD($@)])]) + + diff --git a/app/AUTHORS b/app/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/app/COPYING b/app/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/app/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/app/ChangeLog b/app/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/app/INSTALL b/app/INSTALL new file mode 100644 index 00000000..64d33306 --- /dev/null +++ b/app/INSTALL @@ -0,0 +1,231 @@ + +REQUIREMENTS +============ + +Read the IMPORTANT NOTES in the README file. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + + +Gambas Options +============== + + --with-intl-includes where the internationalization headers are located. + --with-intl-libraries where the internationalization libraries are located. + --with-qt-includes where the QT component headers are located. + --with-qt-libraries where the QT component libraries are located. + --with-kde-includes where the KDE 3.x component headers are located. + --with-kde-libraries where the KDE 3.x component libraries are located. + --with-net-includes where the Networking component headers are located. + --with-net-libraries where the Networking component libraries are located. + --with-postgresql-includes where the PostgreSQL driver headers are located. + --with-postgresql-libraries where the PostgreSQL driver libraries are located. + --with-mysql-includes where the MySQL driver headers are located. + --with-mysql-libraries where the MySQL driver libraries are located. + --with-sdl-includes where the SDL component headers are located. + --with-sdl-libraries where the SDL component libraries are located. + +--disable-debug Remove debug information from binary files. + +--enable-optimization Enable optimization during compilation. + +--disable-preloading Disable the preloading of components. + +--disable-qt-component Do not compile the QT component. + + +Component options +================= + +XXX is a component or library name: + + intl internationalization library + kde KDE component + mysql MySQL driver + net Network component + postgresql PostgreSQL driver + qt QT component + sdl SDL component + +--with-XXX-libraries Where the libraries are located. + +--with-XXX-includes Where the headers are located. + +Use these options if the configure script cannot detect the +location of librairies and/or headers. + +The components or libraries that are not detected are automatically disables, +and then not compiled. + +That's all ! Good luck... diff --git a/app/Makefile.am b/app/Makefile.am new file mode 100644 index 00000000..480dee47 --- /dev/null +++ b/app/Makefile.am @@ -0,0 +1,86 @@ +EXTRA_DIST = reconf src spec mime desktop template +SUBDIRS = . man + +install-exec-local: + @if test "x$(ROOT)" != "x"; then \ + echo "[Installing with ROOT=$(ROOT)]"; \ + fi + @if test "x$(DESTDIR)" != "x"; then \ + echo "[Installing with DESTDIR=$(DESTDIR)]"; \ + ROOT=$DESTDIR; \ + fi + + @echo "Installing the development environment..." + @(cd $(srcdir)/src; d=`pwd`; \ + for p in `cat INSTALL`; do \ + echo "Compiling $$p..."; cd $$d/$$p; \ + $(DESTDIR)$(bindir)/gbc$(GAMBAS_VERSION) -agt -r $(DESTDIR)$(prefix); \ + if test $$? -eq 0; then \ + $(DESTDIR)$(bindir)/gba$(GAMBAS_VERSION); \ + rm -rf .gambas; \ + echo "Installing $$p..."; \ + $(INSTALL) $$p.gambas $(DESTDIR)$(bindir); \ + else \ + echo "|| Unable to compile $$p" >> ../../../warnings.log; \ + fi \ + done) + ##@if test "$(bindir)" != "$(ROOT)/usr/bin" && test "$(bindir)" != "$(ROOT)/usr/bin/"; then + @$(LN_S) -f gambas$(GAMBAS_VERSION).gambas $(DESTDIR)$(bindir)/gambas$(GAMBAS_VERSION) || true + @if test x"$(XDG_UTILS)" != x; then \ + echo "Installing IDE application icon..."; \ + xdg-icon-resource install --novendor --context apps --size 256 $(srcdir)/desktop/gambas3.png gambas3; \ + xdg-icon-resource install --novendor --context apps --size 48 $(srcdir)/desktop/gambas3-48.png gambas3; \ + echo "Installing IDE application menu..."; \ + $(INSTALL) -d $(DESTDIR)$(datarootdir)/desktop-directories; \ + xdg-desktop-menu install --novendor $(srcdir)/desktop/gambas3.desktop; \ + rmdir $(DESTDIR)$(datarootdir)/desktop-directories >/dev/null 2>&1 || true; \ + fi + + @echo "Installing the scripter..." + @$(LN_S) -f gbs$(GAMBAS_VERSION).gambas $(DESTDIR)$(bindir)/gbs$(GAMBAS_VERSION) || true + @$(LN_S) -f gbs$(GAMBAS_VERSION).gambas $(DESTDIR)$(bindir)/gbw$(GAMBAS_VERSION) || true + @if test x"$(XDG_UTILS)" != x; then \ + echo "Registering Gambas script mimetype..."; \ + xdg-icon-resource install --context mimetypes --size 256 $(srcdir)/mime/application-x-gambasscript.png application-x-gambasscript; \ + xdg-icon-resource install --context mimetypes --size 48 $(srcdir)/mime/application-x-gambasscript-48.png application-x-gambasscript; \ + xdg-mime install $(srcdir)/mime/application-x-gambasscript.xml; \ + echo "Registering Gambas server page mimetype..."; \ + xdg-icon-resource install --context mimetypes --size 256 $(srcdir)/mime/application-x-gambasserverpage.png application-x-gambasserverpage; \ + xdg-icon-resource install --context mimetypes --size 48 $(srcdir)/mime/application-x-gambasserverpage-48.png application-x-gambasserverpage; \ + xdg-mime install $(srcdir)/mime/application-x-gambasserverpage.xml; \ + fi + + @echo "Installing the Gambas appdata file..." + @$(INSTALL) -d $(DESTDIR)$(datarootdir)/appdata; + @$(INSTALL) $(srcdir)/desktop/gambas3.appdata.xml $(DESTDIR)$(datarootdir)/appdata; + @$(INSTALL) -d $(DESTDIR)$(datarootdir)/metainfo; + @$(INSTALL) $(srcdir)/desktop/gambas3.appdata.xml $(DESTDIR)$(datarootdir)/metainfo; + + @echo "Installing the Gambas template projects..." + @$(INSTALL) -d $(DESTDIR)$(gbdatadir)/template; + @cp -R $(srcdir)/template/* $(DESTDIR)$(gbdatadir)/template; + +uninstall-local: + @echo "Uninstalling the development environment..." + @rm -f $(DESTDIR)$(bindir)/gambas$(GAMBAS_VERSION) + @rm -f $(DESTDIR)$(bindir)/gbs$(GAMBAS_VERSION) + @rm -f $(DESTDIR)$(bindir)/gbw$(GAMBAS_VERSION) + @(cd $(srcdir)/src; for p in `cat INSTALL`; do rm -f $(DESTDIR)$(bindir)/$$p.gambas; done) + @if test x"$(XDG_UTILS)" != x; then \ + xdg-mime uninstall $(srcdir)/mime/application-x-gambasscript.xml; \ + xdg-mime uninstall $(srcdir)/mime/application-x-gambasserverpage.xml; \ + xdg-icon-resource uninstall --context mimetypes --size 256 application-x-gambasscript; \ + xdg-icon-resource uninstall --context mimetypes --size 256 application-x-gambasserverpage; \ + xdg-icon-resource uninstall --context apps --size 256 gambas3; \ + fi + @rm -rf $(DESTDIR)$(datarootdir)/metainfo/gambas3.appdata.xml + @rm -rf $(DESTDIR)$(datarootdir)/appdata/gambas3.appdata.xml + @rm -rf $(DESTDIR)$(gbdatadir)/template + @rmdir $(DESTDIR)$(datarootdir)/metainfo >/dev/null 2>&1 || true + @rmdir $(DESTDIR)$(datarootdir)/appdata >/dev/null 2>&1 || true + @rmdir $(DESTDIR)$(gbdatadir) >/dev/null 2>&1 || true + +dist-hook: + @(cd $(distdir)/src; \ + rm -rf `find . -name ".gambas" -o -name ".action" -o -name ".lock" -o -name ".xvpics" -o -name "*~" -o -name "*.out" -o -name "*.pot" -o -name "*.gambas" -o -name "core*" -o -name ".kdbg*" -o -name ".svn"`;) + diff --git a/app/NEWS b/app/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/app/README b/app/README new file mode 100644 index 00000000..e69de29b diff --git a/app/TODO b/app/TODO new file mode 100644 index 00000000..e69de29b diff --git a/app/acinclude.m4 b/app/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/app/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/app/configure.ac b/app/configure.ac new file mode 100644 index 00000000..d817617c --- /dev/null +++ b/app/configure.ac @@ -0,0 +1,21 @@ +dnl ---- configure.ac for Gambas development environment + +dnl ---- Initialization + +m4_include([../version.m4]) +AC_INIT([gambas3-ide],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +GB_INIT_SHORT(ide) + +dnl ---- Check for Portland scripts + +AC_CHECK_PROGS(XDG_UTILS, [xdg-mime xdg-icon-resource], []) + +dnl ---- Create makefiles + +AC_CONFIG_FILES([\ +Makefile \ +man/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/app/desktop/gambas3-48.png b/app/desktop/gambas3-48.png new file mode 100644 index 0000000000000000000000000000000000000000..8aac3aeb3d9220b590f1ea2c4972ffa5e275c387 GIT binary patch literal 1864 zcmX9;dpy(o8=tIlDP1|PM?*gwBArvyi8ASANE$^_aY_lv?I^L&ccw+mW${BPcgvBI zOA8?@xg1ToM4QW8Hbc?ITzByszt8LSJkR^{yx#Bk^ZfC7eI7VFo!qLVsRV<;wqosU zak9F9EfnNs*=s=YwX7%x*_{uC!Bm>oLhirDDq1ku2DY=K>xq*=j$@J9JfJs@*W*Q~ zuYom;kO3ZEhlfY}|0>I?Hw|R{ae!Eb!PaRAnS=vr@c@FmE|JX{kJ9C?a|XlKOk}DF zybky7xZF~PF@#=f~dcts9fNY13Y}7IBwJ)E`D8z z*FzC%P=x9U=ogvoI<7$yYLHNEByyxBWt-6jytgjj2x)F@l~6F4N5NNkenrZKgmss4$0bT^m3xFSBDOm~0Mx(|AN z7sz>Z{X8#bz zRey`E?px+`@nTGTq5Ho$Y=-X15o(@K)nP`R!}kb z=K^D7<`_i%`68GEZW$431Pd$UKz~InNvdNBmqb?zK4s7c^IL~!=9h7q&BA4oL?X?6 zKX{bfc&Dt(KCOw;&_4q9rquUW0c;z4dYrBcuAw(O7W>qQJq5`B5JqYId) z1Mlz`U8(Q-OFKAz)DCmf+Yks){~evA9Dm)-GlMFh17{A<7jnK zq>BJ^5Y0x<4SmX5O!uzxD!Ne+UU}%8QR0ii9;cV*90&LN`9Fj2MBemRd>M1;w2wlV z*O&ZD`-IKu*R0j1vwlw>%gT)L==h-Q|G)7}F(LmJfd@ z{%Tfg{_2*Q`K9VCfGuEZX5zORTL6izNv3MsLP3;r)vLk4X!S*{$@>F5 zNjY}gt!y(x@}%!dfj{clkgmlC@f!?LJwTCb!AY3d8A-^3jGVWi{13khz3Le(P2K*! zRY}1hBxlzA;l%`bYxA_=4F}<>VL?SRrj>4p&ZlRtV3LM$SU5;1?%}qS5o|Y8wbXv7 z5y$P*NsvmJdKT3<(|p&?X$}7;hJyDr+?|LCC7M2UU(5{SQ7lRCPI;@c@~PBEIrB_1T*<&a`}0(EJ3%4W!B{v_`HNhz;wFiMZo|)w*2Hl6;pC&l zy?y14*%~U1NUNnqw$;ZCo11kAc2@AY4VS#4kJvXL16a3hGSqgLbL0UQxk7YEvx(^v zp=qZao@g8FjckwzEgwZnHbEw%%KQ!c>mT)cZKdgqE&bD%B+Ac9akh*Y8RIPSTV2}u z?t%d?+?OW;)r*OVqPE0jQxCLTE7FwWnUJe%iMoD&BK|U{-4+w(dgfS<2Ki zJs;{>wVKAFgOg&b$2evCZmi(9Pz0&1A|+7AtjN$bx6|yRwziGhtKIfK4tK*)2`BDO zsUWVrqf%WOYDn(nBi;0S``(8CdD4(~iuqmtMzN9O)&_bVRc)P{FCv077Oo2b4lYw9%3B+Wj=blDAkCUh6G z`Dp3alCS<1`S}M{uZhLd(#L_BDewE$rqvTDTJGg7;W}FlSZc + + gambas3.desktop + CC0-1.0 + GPL-2.0+ + IDE for the Gambas language + +

Gambas is a free development environment and a full powerful development platform based on a Basic interpreter with object extensions, as easy as Visual Basic. This application provides a graphical IDE, with a database manager, an image editor, and a report designer, to assist in the creation of programs with Gambas.

+
+ + + http://gambas.sourceforge.net/2014-07-26.png + The Gambas 3 development environment + + + Benoît Minisini + g4mba5@gmail.com + gambas3.desktop + http://gambas.sourceforge.net/ + http://gambaswiki.org/bugtracker + http://gambas.sourceforge.net/ + http://gambas.sourceforge.net/ + + basic + interpreter + visual + graphical + object language + development environment + +
diff --git a/app/desktop/gambas3.desktop b/app/desktop/gambas3.desktop new file mode 100644 index 00000000..075c2e0a --- /dev/null +++ b/app/desktop/gambas3.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Name=Gambas 3 +Exec=gambas3 +GenericName=Gambas 3 IDE +GenericName[fr]=EDI Gambas 3 +GenericName[ru]=Gambas 3 IDE(ИСР) +Comment=Gambas3 Integrated Development Environment +Comment[fr]=Environnement de développement intégré Gambas 3 +Comment[ru]=Gambas 3 IDE(ИСР) - альтернатива для Visual Basic +Icon=gambas3 +Terminal=false +Type=Application +Categories=Development;IDE; +StartupNotify=true +Actions=QT4;QT5;GTK3 + +[Desktop Action QT4] +Name=Run with QT 4 +Name[fr]=Exécuter avec QT 4 +Exec=env GB_GUI=gb.qt4 gambas3 + +[Desktop Action QT5] +Name=Run with QT 5 +Name[fr]=Exécuter avec QT 5 +Exec=env GB_GUI=gb.qt5 gambas3 + +[Desktop Action GTK3] +Name=Run with GTK+ 3 +Name[fr]=Exécuter avec GTK+ 3 +Exec=env GB_GUI=gb.gtk3 gambas3 diff --git a/app/desktop/gambas3.png b/app/desktop/gambas3.png new file mode 100644 index 0000000000000000000000000000000000000000..420a1c77bc3700382f2e2b1e56f2a2c5cd7c7aef GIT binary patch literal 6834 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%0A$04Kd+QVy$i8=pj3(8eu?#$2BIH$xDwicnW zWlo7zU(p!~L`n!VOJUb(wR7GTlKT+cbBHgMv{`LD@em)e8!e7<4mJ2>uoCZ-RDnhb zvHlWTq0772Y%1|4c)`R05Xd<5S5m{& z*l+|V-)f0kobLog($YF5{ySBptEGm(h_9xmrr1v9@3Xn!*&8^PtbZ1BmgzHi zmVR^tmwngLJY*a}v_oK*+Vi59zu9*l(Uc&o0hLOZ_#A~@k2vYa_oQ%lKKCe&m;L=%!9 zp28qiwEJyf|Z7K^O}!d($rr>g?WfZta!G8GtoDiwUi9j97vD4mU8@ z4Hx2$SfguyxV7FMTLdLuu!wFbKE%MaRsWvK{?b$!j;?x@?4&IE$ox>qzpIUCAH=w4 zXv|g`?>-4mF|D`}H`nbq8S^MB4r$T;Nl`VKu)k*odWX9mR^oDa8I!Ft>%QxyQ(;x! zfU?_8e>JH;-|*A}IxzaN5%<2q^JDWcecQhwsz(QvnxJb)+TL__#97;>@mQGS>5$s>OdY|}~ z;!9E~ci?~y-*Kx>lhn06(jh5;Zb1bfVBYO^&}hvo!=kah-q_=*k@V_2NBwgLyTqlel`%EccNxx9llR9FR*-2a4N@WWzmF zS~#Kk!k{r`EIJpyx|gPoB)&TsZLjG4YLWTJ6D|^@QIiLb-^n{+j(?bUmFfHMsn0R2 zCX3GVH}+Veld^N1 zTtUbAoPVoJ&9()gSp%^ez}2eWAGCW6V4nt26~_DTMv=?eJFrcMpOM6#JP96OuIm(| zjd5-L;yF%HJhSzrn}8F0Q~zf(H~xT%;$0ileb^(iq<0ia4&J}&kWa<-~b3upe^gNi25b*8O^kY z+-U7-&7T;9QNhO{=Bup@bbHvEo4)DQKdX;frUcLAo(Ww0@leKwxm3xv@*Lry(JBcf z+7YYTxP^KRBYsoe(P>A`jn6(_>s9CC!Kis&dUu!v)x1&BT9OtjA1-RJQ^Zv;hc;FW z3!d+Z{dklNNDp+&i3h1FB#Cg%RLz|nAMr6Tdfc-6cBy7v+E9geQ#gl4A1W-v{~c+| zy)V$lYYVr`XJKf&5?YgUg%%w#OZ#mlD_g=6?hDE;Ol>-DqBHg;H}$fJ!DN}jYwbd` zDgCoU2g9HE2vZb!ESb3$M7?t2u<)Fv{fDvlIupt#m5$JIKxDr5a@ZH)44OUXmJf9Y z>*lo!j_E|ckj64^<qPiAE<5`2tGKtw9{dJ38Kl1 z@_9Z+ANXU*RPF@B6JeJ5y~wET5VVX*OPA*|byH7Vjm}{LcO69G?lFw8^#W z)t@MznBIVPuXd#&Ws=0dX=kh(t$yU8_x*szl-{6}_;4~2!NJ#wbzXn-#!R$AT1)!& zZm&x|&S*-oV+MK3I#5Mj*WWT``(#nrotz}@x3!HQj2CLz-uECGJy4ZNWP?7H9#XWHQ<7n-YFjoBP=uc}*o-KINpN_X zTce_(r1A~t{oy_BCD|Bt!1_8rW#OVT{{WM3vl%`Cerm6v?_}9|Ts|~8X$ver-u%bT o#=^oZ{)|gh@`FOsmg5b8XtUE8q@O9c`&$R8DQPK|DVPQPA7goEE&u=k literal 0 HcmV?d00001 diff --git a/app/desktop/gambas3.svg b/app/desktop/gambas3.svg new file mode 100644 index 00000000..9133c355 --- /dev/null +++ b/app/desktop/gambas3.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/examples/Basic/Blights/.directory b/app/examples/Basic/Blights/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/Blights/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/Blights/.icon.png b/app/examples/Basic/Blights/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0c1982e11033bafde2fa4e869e77992384b3512e GIT binary patch literal 4209 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 Hu0mjfPqr0Z literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Blights/.lang/ca.po b/app/examples/Basic/Blights/.lang/ca.po new file mode 100644 index 00000000..05b8bc79 --- /dev/null +++ b/app/examples/Basic/Blights/.lang/ca.po @@ -0,0 +1,28 @@ +# Catalan translation of Blights +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Blights package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Blights\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:31+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Llums perpallejants" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Enemple del Llums perpallejants" + diff --git a/app/examples/Basic/Blights/.lang/cs.po b/app/examples/Basic/Blights/.lang/cs.po new file mode 100644 index 00000000..dd532b12 --- /dev/null +++ b/app/examples/Basic/Blights/.lang/cs.po @@ -0,0 +1,20 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Příklad blikajícíh světel" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Blikající světla" diff --git a/app/examples/Basic/Blights/.lang/de.po b/app/examples/Basic/Blights/.lang/de.po new file mode 100644 index 00000000..de41ad66 --- /dev/null +++ b/app/examples/Basic/Blights/.lang/de.po @@ -0,0 +1,21 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Beispiel für blinkende Lichter" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "blinkende Lichter" + diff --git a/app/examples/Basic/Blights/.lang/es.po b/app/examples/Basic/Blights/.lang/es.po new file mode 100644 index 00000000..7445bc3c --- /dev/null +++ b/app/examples/Basic/Blights/.lang/es.po @@ -0,0 +1,20 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2014-11-11 23:48 UTC\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Ejemplo de luces parpadeantes" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Luces parpadeantes" diff --git a/app/examples/Basic/Blights/.lang/fr.po b/app/examples/Basic/Blights/.lang/fr.po new file mode 100644 index 00000000..0ce4e3df --- /dev/null +++ b/app/examples/Basic/Blights/.lang/fr.po @@ -0,0 +1,3 @@ +#: win1.class:54 +msgid "Blinkenlights" +msgstr "" diff --git a/app/examples/Basic/Blights/.lang/nl.po b/app/examples/Basic/Blights/.lang/nl.po new file mode 100644 index 00000000..60cc291d --- /dev/null +++ b/app/examples/Basic/Blights/.lang/nl.po @@ -0,0 +1,20 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-23 01:15+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Blinking lights voorbeeld" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Blinkenlights" + diff --git a/app/examples/Basic/Blights/.lang/ru.po b/app/examples/Basic/Blights/.lang/ru.po new file mode 100644 index 00000000..0701404c --- /dev/null +++ b/app/examples/Basic/Blights/.lang/ru.po @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Basic/Blights/.project:17 +msgid "Blinking lights example" +msgstr "Пример мигающих огней" + +#: app/examples/Basic/Blights/.src/win1.form:5 +msgid "Blinkenlights" +msgstr "Мигающие огни" + diff --git a/app/examples/Basic/Blights/.lang/sv.po b/app/examples/Basic/Blights/.lang/sv.po new file mode 100644 index 00000000..ba0558c1 --- /dev/null +++ b/app/examples/Basic/Blights/.lang/sv.po @@ -0,0 +1,15 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: win1.class:54 +msgid "Blinkenlights" +msgstr "Blinkande lampor" diff --git a/app/examples/Basic/Blights/.project b/app/examples/Basic/Blights/.project new file mode 100644 index 00000000..c9afd747 --- /dev/null +++ b/app/examples/Basic/Blights/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Title=Blinking lights example +Startup=win1 +Icon=ampoule.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Blights/.src/win1.class b/app/examples/Basic/Blights/.src/win1.class new file mode 100644 index 00000000..910404ca --- /dev/null +++ b/app/examples/Basic/Blights/.src/win1.class @@ -0,0 +1,36 @@ +' Gambas class file + + +Public Sub Timer1_Timer() + + Dim diode As PictureBox + Dim l As Integer + + For Each diode In [l1, l2, l3, l4, l5, l6, l7, l8] + l = Rnd() * 2 + If l = 0 Then + diode.Picture = Picture["bloff.xpm"] + Else + diode.Picture = Picture["blon.xpm"] + Endif + Next + +End + + + +Public Sub Form_Open() + + Dim hPict As Picture + Dim diode As PictureBox + + hPict = Picture["blon.xpm"] + + For Each diode In [l1, l2, l3, l4, l5, l6, l7, l8] + diode.Resize(hPict.Width, hPict.Height) + diode.Background = Color.DarkGreen + Next + + Me.Resize(hPict.W * 8 + Me.W - Me.ClientW, hPict.H + Me.H - Me.ClientH) + +End diff --git a/app/examples/Basic/Blights/.src/win1.form b/app/examples/Basic/Blights/.src/win1.form new file mode 100644 index 00000000..2a3bcfa5 --- /dev/null +++ b/app/examples/Basic/Blights/.src/win1.form @@ -0,0 +1,53 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(20,29.5714,65,7) + Text = ("Blinkenlights") + Icon = Picture["blon.xpm"] + Resizable = False + Arrangement = Arrange.Horizontal + { l1 PictureBox + MoveScaled(0,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l2 PictureBox + MoveScaled(7,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l3 PictureBox + MoveScaled(14,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l4 PictureBox + MoveScaled(21,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l5 PictureBox + MoveScaled(28,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l6 PictureBox + MoveScaled(35,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l7 PictureBox + MoveScaled(42,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l8 PictureBox + MoveScaled(49,0,7,7) + Picture = Picture["blon.xpm"] + Alignment = Align.Center + } + { Timer1 #Timer + Enabled = True + Delay = 200 + } +} diff --git a/app/examples/Basic/Blights/ampoule.png b/app/examples/Basic/Blights/ampoule.png new file mode 100644 index 0000000000000000000000000000000000000000..169097084b60f2331cd91695f23bc6d8f92d9650 GIT binary patch literal 1543 zcmV+i2Kf1jP)2L`6zl35iHZ2t*%1s1;SI z+7c>0BDF73gqjB)nyBKTO-YIfML2Z}PD&D+&4s4___JTPekD2&o&;CgOOL(@BV6c|3hCx?$``+7l;s8I@ku#c!pih^RM%- zlbSfaRJGl2?R`G+#(zX$^2nzSh3?z@>%p%)joR~RP&b26!L@uhq4E*9<(6qKra$J! zsh_a8l>5%U7Z#2K&OH;j`tk!KN@U%+?O#8P-1!g$1^^q}YNI=OsLa-%-C(6moS8Gf zWa7r+!{0ob{)>0WG6AWbNMy-8}vn;RBVH zHQ<&SzfQ@^KvUF>wXWc}t^#xdffWhlO3wAZvH`{OU|t7x2S}l1@wvr1A(m>wZVL|J zlowgZT80lUuc(2uljeKh-tZA_WfwvzKy?A6Fh~(kw|7cVE9ENpi3s8>rFi?2Io-Uu zC4pyN%ilVhb&U zi#-zPjUwd0${_7bS%>Qeuoh-`^=$4BKncjRESR>9$UgAW?LSV>mEN%8*T8LVFvYfw z$IFy}zylz4Ve3iWJ^#=5e|5HW9Wa2U`V2_z=~H<2wc?RmbIG`yne#|Ac(Kk4fDLjd zsDlvP22uxhT4&+f`^hJdrGF1(fux^-J1Jc0GLnFv=N=2~fA+D^(csY5Fd@H*Dg7Wt zJwOovx#w;rq!T1#XOmz2LHyXP?xcWe;O{;W=U%Vl0I=Z~hqNc2KB)bifqn?7{-!X5 zc6zP4Ss#fBQrAY4!#~U%`&f4}z$`EYq-ynF%Y#G%HoyK<_ry4SWvxho64)4ox4o-Cs~|Wgzjyk>YX7Mt<~j#&@kg6-AR! zGzqzU4oT`mXrF=1MN*gEUKP9{*n4@B`FiLAwq}n-F5Q-F0-A)P36zi^pb2km!tc&m z?;QAP;my^+*YqazO+Iz%l%;7JrfE_r6v*Xr7=}SMn?+SsB9RFBe4g>~@!yP&j{X>! zZx{JmN>DSqyStl_kr6D*BAd+;kH@)kj$vVZ@65{U#CE?ltu(%GvWDy@!t zH){aLaj-0lN~LnwKFhK+pqHk{$0sIG4{qBw)oK;XvS==&ZQB8$gSuO2=_ zGiMM&kj-XEr_;>M&7tc$(P)$tCr*$^B-RwnnlccJ#STtQP0fZvA!J!b2!X1qsH#dh z9AUZ>6GCwF=1r!jr-{X4OiWC0^5jV_U%rfC81wmj z{w7f59z?jN5&$_()683T^vV84US37Vmji_cld-m-4(#Xijk1WgTG) c #5A301D", +", c #64331B", +"' c #65341B", +") c #6C341C", +"! c #6C321A", +"~ c #6B371D", +"{ c #68361F", +"] c #6D371C", +"^ c #73381D", +"/ c #79381D", +"( c #813C1A", +"_ c #753B1C", +": c #713A1D", +"< c #6E371D", +"[ c #6F391E", +"} c #7E3D19", +"| c #7B3E1C", +"1 c #6E371C", +"2 c #68351F", +"3 c #60311E", +"4 c #4E2C1C", +"5 c #4A2B1D", +"6 c #502D1C", +"7 c #582D1D", +"8 c #63301D", +"9 c #78391C", +"0 c #7A391A", +"a c #763619", +"b c #773A1B", +"c c #76391A", +"d c #76381A", +"e c #77381B", +"f c #773A1C", +"g c #733719", +"h c #743A1A", +"i c #76381B", +"j c #6F3719", +"k c #72391A", +"l c #73381A", +"m c #743619", +"n c #743518", +"o c #743517", +"p c #723719", +"q c #77391B", +"r c #6F3418", +"s c #68321D", +"t c #552D1B", +"u c #4F2C1B", +"v c #502C1B", +"w c #512D1B", +"x c #5B2F1B", +"y c #5F301B", +"z c #66361E", +"A c #6D381C", +"B c #6F391B", +"C c #6D381B", +"D c #6D351B", +"E c #743C1C", +"F c #753C1D", +"G c #823C1A", +"H c #793C1B", +"I c #793B1C", +"J c #773C1C", +"K c #783D1B", +"L c #86461C", +"M c #793E1C", +"N c #793F1C", +"O c #833D1A", +"P c #7A3C1C", +"Q c #77391C", +"R c #70381C", +"S c #66351B", +"T c #6B341B", +"U c #68351B", +"V c #67361C", +"W c #613119", +"X c #643119", +"Y c #63341B", +"Z c #60301A", +"` c #64311A", +" . c #65321A", +".. c #6A371C", +"+. c #61321A", +"@. c #5F301D", +"#. c #61331F", +"$. c #5E331D", +"%. c #5C321D", +"&. c #592F1E", +"*. c #62311B", +"=. c #693119", +"-. c #713519", +";. c #75381A", +">. c #69351E", +",. c #6D381D", +"'. c #62341D", +"). c #60331D", +"!. c #5D301C", +"~. c #5B311C", +"{. c #5C2F1B", +"]. c #62311C", +"^. c #6B361E", +"/. c #6C351A", +"(. c #6B351A", +"_. c #6E351A", +":. c #74381B", +"<. c #7B3B1B", +"[. c #7E3918", +"}. c #833D18", +"|. c #773B1B", +"1. c #713A1C", +"2. c #6C381D", +"3. c #69341B", +"4. c #6C371E", +"5. c #6C361E", +"6. c #6C351E", +"7. c #73391D", +"8. c #77381D", +"9. c #81401B", +"0. c #88451B", +"a. c #80411B", +"b. c #723A1B", +"c. c #703A1D", +"d. c #6A371E", +"e. c #68351D", +"f. c #65341E", +"g. c #6C341D", +"h. c #70351C", +"i. c #6F371F", +"j. c #6E3720", +"k. c #6F371A", +"l. c #6C341A", +"m. c #6B381C", +"n. c #6B391C", +"o. c #68341A", +"p. c #67321A", +"q. c #622D18", +"r. c #612C19", +"s. c #612D19", +"t. c #612E1A", +"u. c #64331C", +"v. c #66361F", +"w. c #6B3720", +"x. c #6F351E", +"y. c #76371A", +"z. c #6F371D", +"A. c #65351E", +"B. c #63321C", +"C. c #6A321A", +"D. c #6C351B", +"E. c #6C361C", +"F. c #6C341B", +"G. c #63341D", +"H. c #66351D", +"I. c #6E361A", +"J. c #72361A", +"K. c #773B1A", +"L. c #733A1B", +"M. c #6B351B", +"N. c #6E381C", +"O. c #743A1C", +"P. c #7A3F1C", +"Q. c #793B1A", +"R. c #72381B", +"S. c #71391B", +"T. c #6B391D", +"U. c #6D371B", +"V. c #6B351C", +"W. c #6A3A1E", +"X. c #6B381D", +"Y. c #6A381E", +"Z. c #6E391C", +"`. c #70361A", +" + c #73351A", +".+ c #6B341C", +"++ c #69341D", +"@+ c #64341D", +"#+ c #62321C", +"$+ c #65351C", +"%+ c #62341C", +"&+ c #57301D", +"*+ c #562E1C", +"=+ c #592E1E", +"-+ c #5C321E", +";+ c #582E1D", +">+ c #572E1E", +",+ c #572E1D", +"'+ c #562E1E", +")+ c #55301E", +"!+ c #5B341E", +"~+ c #5F341E", +"{+ c #61341E", +"]+ c #6A361F", +"^+ c #65331D", +"/+ c #69361E", +"(+ c #60321A", +"_+ c #60321B", +":+ c #5E341C", +"<+ c #6A361E", +"[+ c #6A331D", +"}+ c #70371C", +"|+ c #6B361C", +"1+ c #6C371C", +"2+ c #763B1A", +"3+ c #743C1D", +"4+ c #6F3B1E", +"5+ c #713B1D", +"6+ c #703C1E", +"7+ c #6F3A1D", +"8+ c #6B371B", +"9+ c #69371D", +"0+ c #71371A", +"a+ c #7D3C1B", +"b+ c #803B19", +"c+ c #853D18", +"d+ c #83411A", +"e+ c #803F1A", +"f+ c #7C3D1A", +"g+ c #773A19", +"h+ c #733B1C", +"i+ c #6C391C", +"j+ c #78391B", +"k+ c #7E3A19", +"l+ c #813A18", +"m+ c #7C3A1A", +"n+ c #7E3B1A", +"o+ c #7C3B1A", +"p+ c #71371B", +"q+ c #6D361B", +"r+ c #733B1E", +"s+ c #71381D", +"t+ c #6F351B", +"u+ c #6C351C", +"v+ c #69351B", +"w+ c #69351A", +"x+ c #6A3419", +"y+ c #6B341A", +"z+ c #6F361B", +"A+ c #6E311A", +"B+ c #6F341B", +"C+ c #6C351F", +"D+ c #69331A", +"E+ c #68321A", +"F+ c #5D301A", +"G+ c #5E301B", +"H+ c #64341B", +"I+ c #66321B", +"J+ c #5F311B", +"K+ c #5C301B", +"L+ c #67341C", +"M+ c #672F1B", +"N+ c #66341C", +"O+ c #6C361D", +"P+ c #6E381D", +"Q+ c #733C1C", +"R+ c #6B381E", +"S+ c #67351D", +"T+ c #6A361B", +"U+ c #71391C", +"V+ c #71381C", +"W+ c #763B1C", +"X+ c #61341C", +"Y+ c #58311C", +"Z+ c #532D1B", +"`+ c #533323", +" @ c #513729", +".@ c #503A2D", +"+@ c #4F392C", +"@@ c #4F3627", +"#@ c #50301F", +"$@ c #552E1B", +"%@ c #60331B", +"&@ c #6A341A", +"*@ c #68331B", +"=@ c #70361B", +"-@ c #74381A", +";@ c #723819", +">@ c #743719", +",@ c #75371A", +"'@ c #73371A", +")@ c #6E371B", +"!@ c #6F381C", +"~@ c #71371C", +"{@ c #70371A", +"]@ c #71381B", +"^@ c #6F381A", +"/@ c #69341A", +"(@ c #64321A", +"_@ c #65301A", +":@ c #6A331B", +"<@ c #5E2F1B", +"[@ c #8A3F17", +"}@ c #883E17", +"|@ c #813B18", +"1@ c #7B3818", +"2@ c #783919", +"3@ c #743A1B", +"4@ c #73391A", +"5@ c #6F371B", +"6@ c #733B1B", +"7@ c #72391B", +"8@ c #733A1C", +"9@ c #73391B", +"0@ c #75381C", +"a@ c #6D3A1C", +"b@ c #6F381B", +"c@ c #5F321C", +"d@ c #3F281D", +"e@ c #40342F", +"f@ c #4F4B49", +"g@ c #5E5E5E", +"h@ c #6B6B6B", +"i@ c #757575", +"j@ c #7B7B7B", +"k@ c #787878", +"l@ c #6F6F6F", +"m@ c #646464", +"n@ c #565554", +"o@ c #48413D", +"p@ c #3C2C25", +"q@ c #4A2B1C", +"r@ c #66321A", +"s@ c #68321B", +"t@ c #6C361B", +"u@ c #723B1B", +"v@ c #783C1A", +"w@ c #7E3C1A", +"x@ c #7F3E1A", +"y@ c #853E19", +"z@ c #854019", +"A@ c #894219", +"B@ c #843E18", +"C@ c #823D18", +"D@ c #803D18", +"E@ c #7C3818", +"F@ c #823C17", +"G@ c #823E18", +"H@ c #6A381C", +"I@ c #64341C", +"J@ c #6F3519", +"K@ c #793919", +"L@ c #853F18", +"M@ c #8C4419", +"N@ c #8C4217", +"O@ c #8A4017", +"P@ c #853C17", +"Q@ c #813A17", +"R@ c #7D3C19", +"S@ c #763718", +"T@ c #743819", +"U@ c #793D1B", +"V@ c #69341C", +"W@ c #6B371E", +"X@ c #733B1D", +"Y@ c #743B1B", +"Z@ c #68371E", +"`@ c #3A2921", +" # c #353433", +".# c #5B5A5A", +"+# c #6D6D6D", +"@# c #767676", +"## c #7F7F7F", +"$# c #858585", +"%# c #8B8B8B", +"&# c #8F8F8F", +"*# c #8D8D8D", +"=# c #878787", +"-# c #808080", +";# c #7A7A7A", +"># c #707070", +",# c #666666", +"'# c #484746", +")# c #2C2724", +"!# c #4F2E1E", +"~# c #6C3519", +"{# c #6F3619", +"]# c #763919", +"^# c #773819", +"/# c #7E3C19", +"(# c #7E3B19", +"_# c #77391A", +":# c #6F381D", +"<# c #6B361B", +"[# c #69361A", +"}# c #66341B", +"|# c #64351C", +"1# c #65331A", +"2# c #5F341C", +"3# c #58321D", +"4# c #592E1B", +"5# c #5D311B", +"6# c #703A1B", +"7# c #80421B", +"8# c #82401A", +"9# c #8B4017", +"0# c #873E17", +"a# c #833E18", +"b# c #7D3A18", +"c# c #773718", +"d# c #753719", +"e# c #502E1D", +"f# c #272727", +"g# c #444444", +"h# c #646363", +"i# c #696969", +"j# c #7C7C7C", +"k# c #818181", +"l# c #868686", +"m# c #888888", +"n# c #828282", +"o# c #7E7E7E", +"p# c #717171", +"q# c #5A5A5A", +"r# c #333333", +"s# c #372821", +"t# c #60301B", +"u# c #61321B", +"v# c #66341E", +"w# c #5F321D", +"x# c #62331D", +"y# c #65341C", +"z# c #6C3518", +"A# c #6D361C", +"B# c #6C351D", +"C# c #6D3319", +"D# c #70391B", +"E# c #83421C", +"F# c #833F1A", +"G# c #7C3B1B", +"H# c #763819", +"I# c #68361C", +"J# c #5C321C", +"K# c #592D1A", +"L# c #5D2F1A", +"M# c #63311A", +"N# c #64351B", +"O# c #6A341B", +"P# c #71361A", +"Q# c #763A1A", +"R# c #7B3A19", +"S# c #7A3919", +"T# c #7E3A17", +"U# c #482D1F", +"V# c #2A2A2A", +"W# c #484848", +"X# c #5F5F5F", +"Y# c #6C6C6C", +"Z# c #727272", +"`# c #797979", +" $ c #7D7D7D", +".$ c #747474", +"+$ c #6E6E6E", +"@$ c #686868", +"#$ c #616161", +"$$ c #535353", +"%$ c #373737", +"&$ c #312824", +"*$ c #6A351C", +"=$ c #6E361C", +"-$ c #72381A", +";$ c #70371B", +">$ c #6F361C", +",$ c #703619", +"'$ c #6E381E", +")$ c #6B391E", +"!$ c #6E361B", +"~$ c #733819", +"{$ c #743618", +"]$ c #78381A", +"^$ c #793819", +"/$ c #7F3D18", +"($ c #7F3C18", +"_$ c #864019", +":$ c #853F19", +"<$ c #803D1A", +"[$ c #7D3F1C", +"}$ c #713B1C", +"|$ c #6C381C", +"1$ c #60321C", +"2$ c #5C2E1A", +"3$ c #5C2F1A", +"4$ c #613019", +"5$ c #6E3519", +"6$ c #813E19", +"7$ c #873F18", +"8$ c #432C20", +"9$ c #282828", +"0$ c #414141", +"a$ c #504F4F", +"b$ c #555555", +"c$ c #5D5D5D", +"d$ c #6A6A6A", +"e$ c #777777", +"f$ c #585858", +"g$ c #525151", +"h$ c #4A4949", +"i$ c #2F2F2F", +"j$ c #2D2724", +"k$ c #7E3718", +"l$ c #753519", +"m$ c #7B3C1B", +"n$ c #783418", +"o$ c #773418", +"p$ c #773A1A", +"q$ c #6F3A1C", +"r$ c #6C3A1D", +"s$ c #6E381B", +"t$ c #6D3419", +"u$ c #66351C", +"v$ c #5F351E", +"w$ c #512F1E", +"x$ c #4B2A1B", +"y$ c #4F2B1C", +"z$ c #532D1C", +"A$ c #5E321D", +"B$ c #3E2D22", +"C$ c #252525", +"D$ c #323232", +"E$ c #464545", +"F$ c #4D4D4D", +"G$ c #545454", +"H$ c #5C5C5C", +"I$ c #636363", +"J$ c #737373", +"K$ c #656565", +"L$ c #565656", +"M$ c #4F4F4F", +"N$ c #494949", +"O$ c #3F3E3E", +"P$ c #2F2824", +"Q$ c #6F3B1D", +"R$ c #813F1B", +"S$ c #7F3C1A", +"T$ c #7B3B19", +"U$ c #783A19", +"V$ c #733919", +"W$ c #6D351A", +"X$ c #68371D", +"Y$ c #67361E", +"Z$ c #69361D", +"`$ c #6E391D", +" % c #81431B", +".% c #81441C", +"+% c #7B3F1B", +"@% c #703C1D", +"#% c #703C1C", +"$% c #6D361A", +"%% c #72371A", +"&% c #763B1B", +"*% c #7A3B1A", +"=% c #813D19", +"-% c #883F18", +";% c #56331F", +">% c #272726", +",% c #383838", +"'% c #4B4B4B", +")% c #5B5B5B", +"!% c #626262", +"~% c #4E4E4E", +"{% c #464646", +"]% c #3F3F3F", +"^% c #2C2C2C", +"/% c #362923", +"(% c #5D311C", +"_% c #5B321D", +":% c #582D1A", +"<% c #542B1A", +"[% c #532B1B", +"}% c #502B1C", +"|% c #552D1C", +"1% c #572E1B", +"2% c #783E1C", +"3% c #7C3E1A", +"4% c #7E3B1B", +"5% c #79381B", +"6% c #7F3C1B", +"7% c #843C18", +"8% c #833D17", +"9% c #833C17", +"0% c #823A17", +"a% c #7F3D19", +"b% c #823E1A", +"c% c #803D19", +"d% c #853F1A", +"e% c #863E17", +"f% c #863D18", +"g% c #2A2725", +"h% c #262625", +"i% c #292929", +"j% c #4A4A4A", +"k% c #676767", +"l% c #454545", +"m% c #3C3C3C", +"n% c #303030", +"o% c #4C2D1F", +"p% c #74371A", +"q% c #753A1C", +"r% c #7A3E1C", +"s% c #7E3D1B", +"t% c #894119", +"u% c #914518", +"v% c #954417", +"w% c #8C461B", +"x% c #61351E", +"y% c #63331C", +"z% c #67351B", +"A% c #763C1B", +"B% c #793E1B", +"C% c #7D421B", +"D% c #5F311C", +"E% c #542D1C", +"F% c #332722", +"G% c #262626", +"H% c #252524", +"I% c #404040", +"J% c #3B3B3B", +"K% c #272625", +"L% c #753517", +"M% c #723618", +"N% c #6B3318", +"O% c #663018", +"P% c #673018", +"Q% c #652E19", +"R% c #632F19", +"S% c #633019", +"T% c #592F1A", +"U% c #5A311B", +"V% c #5D341D", +"W% c #5E351D", +"X% c #8F3F17", +"Y% c #874018", +"Z% c #803C19", +"`% c #823C19", +" & c #833E19", +".& c #863E18", +"+& c #8C4117", +"@& c #8B3F17", +"#& c #893E17", +"$& c #61301A", +"%& c #4F2C1C", +"&& c #353535", +"*& c #606060", +"=& c #595959", +"-& c #3A3A3A", +";& c #2E2E2E", +">& c #452C20", +",& c #7D3718", +"'& c #7D3918", +")& c #853B17", +"!& c #843B17", +"~& c #843E19", +"{& c #833F19", +"]& c #873E16", +"^& c #8A3E17", +"/& c #6F3A1B", +"(& c #783A1A", +"_& c #783B1B", +":& c #753819", +"<& c #7E3B18", +"[& c #813C17", +"}& c #7E3917", +"|& c #7D3B1A", +"1& c #4B2E20", +"2& c #2B2B2B", +"3& c #343434", +"4& c #505050", +"5& c #575757", +"6& c #525252", +"7& c #4C4C4C", +"8& c #393939", +"9& c #68341B", +"0& c #65331B", +"a& c #65361C", +"b& c #67361D", +"c& c #68381D", +"d& c #693A1E", +"e& c #6A391E", +"f& c #6A3921", +"g& c #6D391D", +"h& c #68351E", +"i& c #68351C", +"j& c #69381C", +"k& c #723619", +"l& c #773818", +"m& c #793918", +"n& c #302824", +"o& c #2D2D2D", +"p& c #515151", +"q& c #424242", +"r& c #472C1F", +"s& c #562C1A", +"t& c #562E1B", +"u& c #5C311C", +"v& c #5A311C", +"w& c #592F1B", +"x& c #58301C", +"y& c #5E341D", +"z& c #65361D", +"A& c #673821", +"B& c #6E371A", +"C& c #6F361A", +"D& c #70351B", +"E& c #6E3419", +"F& c #3E3E3E", +"G& c #474747", +"H& c #382921", +"I& c #6C371A", +"J& c #6B3419", +"K& c #693419", +"L& c #6A361A", +"M& c #6A351B", +"N& c #6A361C", +"O& c #6E3A1B", +"P& c #71351A", +"Q& c #6D361D", +"R& c #70351A", +"S& c #753B1A", +"T& c #52311F", +"U& c #3D3D3D", +"V& c #65311B", +"W& c #6A361D", +"X& c #753A1B", +"Y& c #6E3A1D", +"Z& c #70391C", +"`& c #6F371C", +" * c #6C371D", +".* c #6D361E", +"+* c #783F1D", +"@* c #402C22", +"#* c #2B2624", +"$* c #67331C", +"%* c #6F391C", +"&* c #6D3A1E", +"** c #75391A", +"=* c #853D17", +"-* c #7D3D1B", +";* c #723E1D", +">* c #703B1C", +",* c #70381B", +"'* c #66361C", +")* c #382A23", +"!* c #434343", +"~* c #313131", +"{* c #2B2725", +"]* c #75391B", +"^* c #70381A", +"/* c #7C401C", +"(* c #7F421B", +"_* c #874119", +":* c #6A371B", +"<* c #6C391D", +"[* c #793D1A", +"}* c #783D1C", +"|* c #743B1D", +"1* c #6B371C", +"2* c #342822", +"3* c #292625", +"4* c #57331F", +"5* c #66361D", +"6* c #713618", +"7* c #723A1C", +"8* c #713A1B", +"9* c #6B3B1D", +"0* c #63331F", +"a* c #63341F", +"b* c #673620", +"c* c #63331B", +"d* c #803A18", +"e* c #7F3919", +"f* c #7D3A19", +"g* c #6D341A", +"h* c #372922", +"i* c #292624", +"j* c #5D3221", +"k* c #6B3521", +"l* c #62321F", +"m* c #833E1A", +"n* c #2B2625", +"o* c #462B1E", +"p* c #363636", +"q* c #372A23", +"r* c #282524", +"s* c #4D3020", +"t* c #83401A", +"u* c #7E3E1B", +"v* c #472E21", +"w* c #6A391D", +"x* c #86421B", +"y* c #763E1D", +"z* c #733D1D", +"A* c #6F3C1D", +"B* c #3C2C23", +"C* c #773B1C", +"D* c #67331B", +"E* c #3A2B23", +"F* c #67341D", +"G* c #72381C", +"H* c #68361D", +"I* c #61331B", +"J* c #62321A", +"K* c #67341B", +"L* c #61331C", +"M* c #462D20", +"N* c #68331C", +"O* c #6E391E", +"P* c #72371B", +"Q* c #6D381A", +"R* c #5D301B", +"S* c #59301C", +"T* c #59311D", +"U* c #512B1B", +"V* c #512A1B", +"W* c #5D2F1B", +"X* c #62311A", +"Y* c #3A2A22", +"Z* c #322823", +"`* c #6C3419", +" = c #69351C", +".= c #683820", +"+= c #65371E", +"@= c #69381E", +"#= c #65371D", +"$= c #62321B", +"%= c #5F341D", +"&= c #61341D", +"*= c #63311B", +"== c #66331B", +"-= c #6C3319", +";= c #352923", +">= c #5F331D", +",= c #733618", +"'= c #77381A", +")= c #76391B", +"!= c #6D371A", +"~= c #6B3A1E", +"{= c #853E18", +"]= c #823F1A", +"^= c #823F19", +"/= c #823A18", +"(= c #7D3818", +"_= c #7C3718", +":= c #78371A", +"<= c #7E3818", +"[= c #472B1E", +"}= c #8A4218", +"|= c #863C17", +"1= c #813C19", +"2= c #873E18", +"3= c #8E3E16", +"4= c #8E3E17", +"5= c #57331E", +"6= c #522E1C", +"7= c #532C1B", +"8= c #5D2D1A", +"9= c #5D2C19", +"0= c #5F2D19", +"a= c #612F19", +"b= c #642F19", +"c= c #6A3318", +"d= c #6E3318", +"e= c #6F3419", +"f= c #84411B", +"g= c #382A22", +"h= c #43291E", +"i= c #7A401B", +"j= c #743D1B", +"k= c #753D1C", +"l= c #6E381A", +"m= c #6E3A1C", +"n= c #62331C", +"o= c #5B351E", +"p= c #944316", +"q= c #884219", +"r= c #823F1B", +"s= c #7A3E1D", +"t= c #753E1D", +"u= c #723A1E", +"v= c #713C1E", +"w= c #6C391E", +"x= c #75361A", +"y= c #75381B", +"z= c #63301A", +"A= c #512A1A", +"B= c #332621", +"C= c #262524", +"D= c #2F2723", +"E= c #88421A", +"F= c #823E1B", +"G= c #7F3D1B", +"H= c #803B1A", +"I= c #844019", +"J= c #7B3C1C", +"K= c #813D1C", +"L= c #813D1B", +"M= c #8A441A", +"N= c #984717", +"O= c #63321B", +"P= c #5F2F1D", +"Q= c #592C1B", +"R= c #572C1B", +"S= c #4D2A1B", +"T= c #4D291B", +"U= c #4F291A", +"V= c #53291A", +"W= c #532A1A", +"X= c #57301C", +"Y= c #5A301B", +"Z= c #68331A", +"`= c #4B2C1C", +" - c #2E2621", +".- c #2B2523", +"+- c #422A1E", +"@- c #743818", +"#- c #853D16", +"$- c #713619", +"%- c #6C381B", +"&- c #6B3A1C", +"*- c #6E3B1D", +"=- c #763D1B", +"-- c #82441C", +";- c #7E411B", +">- c #793B19", +",- c #7E3E1A", +"'- c #67381E", +")- c #63371E", +"!- c #64381E", +"~- c #65361E", +"{- c #63351D", +"]- c #66351A", +"^- c #703719", +"/- c #7F3F1B", +"(- c #78391A", +"_- c #763618", +":- c #693217", +"<- c #602D16", +"[- c #5A2914", +"}- c #432012", +"|- c #2D1C15", +"1- c #241E1B", +"2- c #23211F", +"3- c #291C16", +"4- c #402313", +"5- c #5F3015", +"6- c #723C18", +"7- c #6A351A", +"8- c #4E2B1B", +"9- c #502A1B", +"0- c #532E1D", +"a- c #5C331D", +"b- c #6D3A1D", +"c- c #703519", +"d- c #6A3A1F", +"e- c #743519", +"f- c #783519", +"g- c #773719", +"h- c #763419", +"i- c #763519", +"j- c #7A3618", +"k- c #7F3918", +"l- c #7A3719", +"m- c #79381A", +"n- c #7A3716", +"o- c #733313", +"p- c #602B10", +"q- c #4A220E", +"r- c #351A0B", +"s- c #261309", +"t- c #21150E", +"u- c #211A17", +"v- c #221E1C", +"w- c #232120", +"x- c #242322", +"y- c #23201E", +"z- c #211C19", +"A- c #211712", +"B- c #22120A", +"C- c #2C160A", +"D- c #3F1D0B", +"E- c #59290E", +"F- c #6F3212", +"G- c #7D3815", +"H- c #7C3A18", +"I- c #6E3418", +"J- c #5B2E1A", +"K- c #5A2E1A", +"L- c #6B361D", +"M- c #813F1A", +"N- c #843F1A", +"O- c #7E3D1A", +"P- c #743B1C", +"Q- c #773B1D", +"R- c #6C371F", +"S- c #6E381F", +"T- c #6F351C", +"U- c #703419", +"V- c #713419", +"W- c #703319", +"X- c #6B351E", +"Y- c #633119", +"Z- c #653219", +"`- c #5F2F18", +" ; c #532A16", +".; c #472613", +"+; c #3A1E10", +"@; c #321A0E", +"#; c #2C170C", +"$; c #28160B", +"%; c #25140A", +"&; c #241209", +"*; c #231208", +"=; c #241308", +"-; c #271409", +";; c #2B150A", +">; c #30170A", +",; c #371B0C", +"'; c #3E1E0D", +"); c #4C2410", +"!; c #602D12", +"~; c #703414", +"{; c #773615", +"]; c #763818", +"^; c #6C361A", +"/; c #5D321D", +"(; c #65351D", +"_; c #703B1E", +":; c #7A401D", +"<; c #7F421D", +"[; c #80411C", +"}; c #773D1C", +"|; c #73371C", +"1; c #63321D", +"2; c #61321C", +"3; c #61331D", +"4; c #61321D", +"5; c #60311A", +"6; c #552D1A", +"7; c #542D19", +"8; c #4C2917", +"9; c #452415", +"0; c #3F2214", +"a; c #3C2012", +"b; c #3A2012", +"c; c #3C2011", +"d; c #3B2013", +"e; c #3E2010", +"f; c #401F0F", +"g; c #3F2012", +"h; c #432212", +"i; c #482613", +"j; c #4C2714", +"k; c #582B15", +"l; c #652F15", +"m; c #6B3317", +"n; c #743718", +"o; c #863F18", +"p; c #883F17", +"q; c #893F18", +"r; c #813C18", +"s; c #763918", +"t; c #64301A", +"u; c #5D331D", +"v; c #5C341D", +"w; c #6F3618", +"x; c #803B18", +"y; c #803C18", +"z; c #7D3C18", +"A; c #753919", +"B; c #733718", +"C; c #663218", +"D; c #623118", +"E; c #613117", +"F; c #5D2E17", +"G; c #5B2E18", +"H; c #5A2E17", +"I; c #592F16", +"J; c #542C16", +"K; c #552D16", +"L; c #572B14", +"M; c #592B15", +"N; c #5A2D15", +"O; c #5E2E15", +"P; c #623017", +"Q; c #652F18", +"R; c #693318", +"S; c #6F3819", +"T; c #773918", +"U; c #7B391A", +"V; c #8A3E16", +"W; c #8D4117", +"X; c #8D4218", +"Y; c #68371C", +"Z; c #813D18", +"`; c #803E19", +" > c #723B1C", +".> c #67321B", +"+> c #6E3619", +"@> c #673319", +"#> c #653118", +"$> c #6A3217", +"%> c #6B3218", +"&> c #6A341C", +"*> c #70391D", +"=> c #79361A", +"-> c #7A371A", +";> c #803917", +">> c #883D17", +",> c #7D3B19", +"'> c #72391C", +")> c #6F391D", +"!> c #7A381A", +"~> c #72391D", +"{> c #71371D", +"]> c #77371A", +"^> c #69371C", +"/> c #67371E", +"(> c #67331D", +"_> c #5C301C", +":> c #61301C", +"<> c #69321B", +"[> c #6D341B", +"}> c #67321E", +"|> c #60321D", +"1> c #66331A", +"2> c #64331A", +"3> c #74371B", +"4> c #7C391A", +"5> c #7B391B", +"6> c #74381C", +"7> c #7B3A1A", +"8> c #66341D", +"9> c #62361D", +"0> c #5F311A", +"a> c #60311B", +"b> c #68341D", +"c> c #60331F", +"d> c #60331E", +"e> c #5B2E1C", +"f> c #562D1C", +"g> c #582F1E", +"h> c #582F1D", +"i> c #542E1C", +"j> c #59301D", +"k> c #60311C", +"l> c #6A381D", +"m> c #65321D", +"n> c #65341D", +"o> c #6C3219", +"p> c #6B3119", +"q> c #693019", +"r> c #64321C", +"s> c #62311D", +"t> c #64351F", +"u> c #6B3721", +"v> c #693921", +"w> c #62341F", +"x> c #5A2D1B", +"y> c #592D1B", +"z> c #592F1C", +"A> c #5F2E1A", +"B> c #69391D", +"C> c #6D3621", +"D> c #70351E", +"E> c #6E3620", +"F> c #66331E", +"G> c #61321E", +"H> c #5B311D", +"I> c #5D311D", +"J> c #6A351F", +"K> c #6B3620", +"L> c #6B361F", +"M> c #6E3621", +"N> c #69371E", +". + @ # $ % % & * = - ; > = , ' ) ! ~ { ] ^ / ( _ : < < [ } | ^ / ( _ : < < [ 1 2 3 @ 4 5 6 7 8 1 9 0 a b c d e f g h i ", +"j k l m n n o p q r s t u v w x y z A B C D E F G H I J K L L M N M O P Q R S T ! U V W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.", +"= >.,.'.).; !.~.{.].^.' /./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.K b.c.d.e.f.g.h.i.j.T _.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.", +"A.A.B.C.D.E.F.G.H.I.J.K.L.M.M.1 N.O.P.Q.R.S.T.e.1 M.U.V.A f.W.X.Y.X.Z./.`. +F..+++@+#+$+%+&+*+> =+-+;+>+,+,+'+)+!+~+{+]+", +"^+/+^.+.(+_+(+:+#+<+[+T }+Z.|+1+2+3+4+5+6+7+8+N.9+0+a+b+c+d+e+f+g+h+i+b.Q.j+k+l+m+d q n+o+p+q+r+s+t+u+v+w+x+y+z+! A+B+C+", +"+ D+E+F+G+H+I+J+K+L+M+N+e.O+P+Q+M R+^+S+T+U+V+W+V+X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@-@J.;@p >@,@'@)@!@U+!@~@{@]@^@/@(@' _@:@<@", +"[@[@}@|@1@2@3@4@5@1 D 6@7@b.8@9@0@V+p+J.a@!@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@D.R ] *@t@u@v@w@x@y@z@A@B@C@D@E@F@G@}.k+", +"n.H@I@J@K@L@M@N@O@P@Q@R@S@T@U@l V@W@X@Y@,.Z@`@ #.#+#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]#^#/#(#_#:#:#:#<#[#}#' ' D.3.1+}#|#", +"R.T 1#2#3#4#5#6#7#8#9#0#B@a#b#c#d#'@;@p e#f#g#h#i#l@@#j#k#l#m#l#n#o#k@p#h@m@q#r#s#{.t#u#v#N+, H+w#x#y#z#A#B#M.>@g C#3.D#", +"E#F#G#H#]@I#J#K#L#M#N#O#P#Q#R#S#o+T#R#U#V#W#.#X#,#Y#Z#`# $-#n#k#o#;#.$+$@$#$.#$$%$&${.*$=$V+-$`.;$>$D.,$'$)$R+!$~${$]$^$", +"D@/$($_$:$<$[$P.}$|$1$2$3$3$4$5$6$7$8$9$0$a$b$c$m@d$p#@#`#j# $j#;#e$Z#+#,#X#f$g$h$i$j$B.k$l$>@d#b m$S#n$o$a c#Q#p$_#c 2@", +"M.t@q$r$T.] s$t$u$v$w$x$y$z$A$<#2+B$C$D$E$F$G$H$I$i#+$J$@#k@`#`#e$.$>#h@K$g@L$M$N$O$C$P$Q$R$S$T$U$V$W$e.X$Y$* N+L+Z$* `$", +" %.%+%@%#%q$A $%-${@%%&%*%=%:$-%;%C$>%,%g#'%G$)%!%@$+#>#J$.$i@i@.$Z#+$i#m@g@L$~%{%]%^%C$/%L#(%_%:%<%[%}%y$|%1%x E+z+2%3%", +"4%5%6%7%8%9%0%a%b%c%d%L@e%f%<$T+g%h%i%,%0$j%$$)%!%k%d$+#l@>#p#p#>#+$h@@$I$c$L$~%l%m%n%C$C$o%p%y.,@N.7+U+;$q%r%s%t%u%v%w%", +"x%; G+y%z%n.a@^@A%B%C%&%<#D%E%F%G%H%i%%$I%j%$$q#X#I$k%i#d$Y#Y#Y#d$i#@$m@#$H$L$~%g#J%n%H%C$K%t$L%M%N%O%P%Q%R%S%T%U%V%W%p.", +"X%Y%P@Z%`% &b%.&+&@&#&Z%J@$&%&K%C$G%i%&&I%j%$$f$c$*&!%m@,#,#k%k%,#m@I$*&c$=&b$F$g#-&;&f#h%C$>&,&'&*%l+)&!&~&{&{&F#7$]&^&", +"s$/&Q+W+(&_&:&^#<&[&}&|&p$_#1&C$h%9$2&3&]%N$4&b$=&)%c$*&#$!%I$!%#$*&g@H$q#5&6&7&g#8&;&9$G%C$C$` + (@o.9&0&a&u.b&c&d&e&C ", +"f&g&q+h&i&j&}#/.M%p k&:&l&m&n&C$G%9$o&&&]%W#F$p&G$5&=&)%H$c$c$c$H$)%=&f$b$6&~%j%q&8&n%V#f#h%C$r&K#s&t&x u&v&w&x&v&y&z&A&", +"i+B&C&B&{@]@D&0+Y@X@O+++E&I#C$G%f#V#n%%$F&l%N$F$M$6&b$5&f$f$f$f$5&L$L$$$4&F$'%G&0$-&r#^%9$G%C$H&k.I&x+J&K&o.L&M&N&A O&j&", +"R 5@/.-.P&C&Q&7@R&=$] ]@S&T&C$h%9$2&D$8&]%g#l%W#'%~%M$p&6&$$$$$$6&p&4&~%7&N$G&g#q&U&&&;&i%f#G%j$V&V&i&1+v+W$z+..W&V+X&R ", +"L.Y&..Z&`& *|+U..*$+X.+*_ @*C$G%9$o&3&m%0$g#l%l%G&N$j%'%F$F$F$F$F$7&'%N$W#{%g#g#q&F&%$i$V#f#G%#*Y$t@J+$*..%*A#&*Y&b.c **", +"=*-*{@..q+$%Y&;*Q$>*L.,*'*)*C$G%9$;&&&U&q&l%g#l%l%{%{%G&W#W#W#W#W#G&{%{%l%{%g#l%!*]%8&~*V#f#G%{*D.K@q ]*8+* U.^*Y@/*(*_*", +":*<#N&%*' <*1.=@[*}*|* *1*2*G%f#i%;&&&U&q&g#l%l%{%l%l%l%{%{%{%{%l%l%l%l%{%{%l%l%!*I%8&~*V#f#G%3*4*5*6*k&`&7*z%g&8*;$S.9*", +"b%0*a*b*c*z&Q$X&d*e*f*p+g*h*C$G%i%;&&&m%q&l%l%l%{%{%{%{%l%l%l%l%l%{%{%{%l%{%g#g#!*I%8&~*V#f#G%i*j*k*l*0*a*b*c*z&Q$X&d*m*", +"| ^ / ( _ : < < [ 1 2 3 @ 2*C$G%9$^%3&m%q&!*l%l%{%{%{%{%{%{%{%{%{%{%{%l%{%l%l%g#!*]%,%n%V#f#h%n*~+{ ] ^ / ( _ : < < [ } ", +"L M N M O P Q R S T ! U V o*C$h%9$2&D$-&I%!*l%g#l%{%{%{%{%{%{%{%{%{%l%{%l%l%l%g#0$U&p*;&i%f#h%#*..B C D E F G H I J K L ", +"0.a.K b.c.d.e.f.g.h.i.j.T D%C$h%f#V#n%%$F&q&g#l%l%{%l%{%{%{%{%{%{%{%{%l%l%g#l%g#I%J%r#^%9$G%G%q*[.}.|.1.2.3.4.5.6.7.8.9.", +"W.X.Y.X.Z./.`. +F..+++@+#+%+r*C$G%i%;&&&m%0$!*g#l%l%{%{%l%{%{%{%{%l%{%g#g#l%g#q&F&%$~*2&f#G%C$s*P.Q.R.S.T.e.1 M.U.V.A f.", +"t*u*Q.h+i+b.Q.j+k+l+m+d q m+v*C$h%f#2&D$8&F&q&!*l%l%g#l%l%l%{%{%l%l%g#l%l%g#q&I%J%3&o&i%f#G%C$w*4+5+6+7+8+N.9+0+a+b+.&x*", +"y*z*A*Z.1+N.M.*@=@-@J.;@p >@w#h%C$f#i%;&3&-&]%0$!*l%l%g#g#l%l%l%l%g#l%g#g#q&0$m%%$n%2&f#h%C$B*R+^+S+T+U+V+W+V+9+X.C&|.J ", +"C*P+m.7*] E.N&i&D*s@D.R ] *@z%E*C$G%f#V#i$&&J%]%q&!*g#l%g#g#g#g#l%g#l%g#!*I%U&,%~*^%9$G%C$C$F*V+p+J.a@!@b@G*5@F.U.U.b@|.", +"Y&H*y#%+I*0&J*(+c*K*~#{#]#^#/#L*3*H%G%9$2&n%&&J%F&0$!*!*g#l%l%l%g#g#!*q&I%m%,%D$o&9$f#H%C$M*N*W@X@Y@,.O*R D#s$P*Q*Z&B q+", +"; R*S*x&x&T*1%U*V*<%W*X*t#u#v#N+Y*C$H%h%9$2&i$3&8&U&]%0$q&q&q&q&q&0$I%U&-&%$D$o&9$f#H%G%Z*`*d#'@;@p ~#9&l.M.D.T =.=+=Y ", +"@=#=$=%=&=|#' c*J+*===<#-=*$=$V+k.;=G%H%h%9$2&;&D$&&8&J%m%F&F&F&U&m%-&%$3&n%^%i%G%H%C$3*>=S#o+T#Z%/#g+,=S@'=)=3+%*B&!=~=", +".&{=C@]=^=0%/=(=n _=(=0 :=<=k$l$>@/.q*G%H%h%f#i%2&;&~*r#&&%$%$%$p*3&D$n%o&V#9$f#h%C$G%[=4$5$6$7$[@}=.&f%|=7%1=P@2=3=4==*", +"5=6=7=2$8=9=0=a=b=c=d=e=n c%f=R$S$T$k.g=C$C$G%f#f#i%V#o&o&;&i$i$;&^%2&V#9$f#f#H%C$G%h=z$A$<#2+i=j=k=l=m=m.'*%+V&n=}#i&o=", +"p=q=r=s=t=u=v=w=t@'@x=]$y=-$T z=(%_%:%A=B=C=C$C$h%G%f#9$9$i%i%i%9$9$f#f#h%h%C$h%D=a&*%=%:$-%E=r=} F=G=H=G I=b%J=K=L=M=N=", +"h+O=P=Q=R=S=T=U=V=W=t X=Y=*=Z=E&p%y.,@N.7+`= -h%G%G%h%G%G%G%f#G%G%G%h%C$G%G%.-+-@-L@#-f%<$/#v@-$P&g $-%-&-*-*-=---;->-,-", +"'-)-)-!-~-~-{-]-U.^-H#b (&/-|&(-_-L%M%:-<-[-}-|-1-H%f#f#G%G%G%G%G%G%f#G%2-3-4-5-6-k 7-D%*+8-9-0-a-y#y+q$S.b-<*1 c--=A d-", +")=S@S@M%$-e-_-f-(-g-h-i-j-k-<=l-m-,&'&U$n-o-p-q-r-s-t-u-v-w-x-w-y-z-A-B-C-D-E-F-G-H-I-$&J-:%K-y L-S.&%M-N-_$N-O-{&,-P-Q-", +"0+^#i-U.R-S-)@M&.*T-U-V-W-t+X-:@&@p.+ Y-Z-`- ;.;+;@;#;$;%;&;*;=;-;;;>;,;';);!;~;{;R#p$(&)=];^;I@G.W*{./;(;_;C*:;<;[;};!$", +"O#|;_#/@U N&T+= 1;2;3;L*= v#a*4;` 5;K#s&6;:%7;8;9;0;a;b;c;d;a;e;f;g;h;i;j;k;l;m;r n;l&H-a#o;p;}@q;r;s;t;K-u;v;+.O#q+1+V.", +"U 9&T K*}#K*[#w;6*-$U+;.x;y;(#z;A;B;k.I&x+c=C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;Z&V.++E&X&T@T;m$U;/=V;W;X;D@}* =L*I#Y;c*9&", +"a#C@E@Z;C@o;M@~&Z%`;*%h >9&0&] G*)@.>V&i&1+o.J&l.S 0&(.+>o.o.@>#>$>%>J&&>^*e==$] ]@S&b.b.L.3+*>5@c-p%=>->;>0%>>@&^&,>}.", +"*=|#|#K*I&5@5@R.'>Z&)>X&!>i ~>{>]>c 7.t@J+$*..%*A#&*Y&b.c **L.Y&..Z&`& *|+U..*$+X.+*_ ^>/>/+(>, '.u&_>:>z&x y <>[>}>|>0&", +"<>O#E.1>2>2>0&v+q+Z&u=_.3>4>5>i 6>7>l+K@q ]*8+* U.^*Y@/*(*_*=*-*{@..q+$%Y&;*Q$>*L.,*'*u$1 `&9&8>9>1$5#0>a>5;(+u$b>s Z$<>", +"c>d>e>,+f>f>,+,+g>h>h>i>j>2;k>c@>=(>l>5*6*k&`&7*z%g&8*;$S.9*:*<#N&%*' <*1.=@[*}*|* *1*3.t@L.L.-$F.m>n>[>o>p>q>r>r>s>@+t>", +"u>v>w>>=x>y>z>A>I#B>B>..T E.M.B#C>D>E>k*l*0*a*b*c*z&Q$X&d*m*b%0*a*b*c*z&Q$X&d*e*f*p+g*3.3.M&V@F>G>H>I>4;'.&={+J>K>L>M>N>"}; diff --git a/app/examples/Basic/Blights/blon.xpm b/app/examples/Basic/Blights/blon.xpm new file mode 100644 index 00000000..dee0b520 --- /dev/null +++ b/app/examples/Basic/Blights/blon.xpm @@ -0,0 +1,1503 @@ +/* XPM */ +static char * blon_xpm[] = { +"60 48 1452 2", +" c None", +". c #70341A", +"+ c #65321B", +"@ c #5C301D", +"# c #63311C", +"$ c #642E19", +"% c #642D19", +"& c #642E1A", +"* c #69381D", +"= c #65331C", +"- c #64311B", +"; c #5E321C", +"> c #5A301D", +", c #64331B", +"' c #65341B", +") c #6C341C", +"! c #6C321A", +"~ c #6B371D", +"{ c #68361F", +"] c #6D371C", +"^ c #73381D", +"/ c #79381D", +"( c #813C1A", +"_ c #753B1C", +": c #713A1D", +"< c #6E371D", +"[ c #6F391E", +"} c #7E3D19", +"| c #7B3E1C", +"1 c #6E371C", +"2 c #68351F", +"3 c #60311E", +"4 c #4E2C1C", +"5 c #4A2B1D", +"6 c #502D1C", +"7 c #582D1D", +"8 c #63301D", +"9 c #78391C", +"0 c #7A391A", +"a c #763619", +"b c #773A1B", +"c c #76391A", +"d c #76381A", +"e c #77381B", +"f c #773A1C", +"g c #733719", +"h c #743A1A", +"i c #76381B", +"j c #6F3719", +"k c #72391A", +"l c #73381A", +"m c #743619", +"n c #743518", +"o c #743517", +"p c #723719", +"q c #77391B", +"r c #6F3418", +"s c #68321D", +"t c #552D1B", +"u c #4F2C1B", +"v c #502C1B", +"w c #512D1B", +"x c #5B2F1B", +"y c #5F301B", +"z c #66361E", +"A c #6D381C", +"B c #6F391B", +"C c #6D381B", +"D c #6D351B", +"E c #743C1C", +"F c #753C1D", +"G c #823C1A", +"H c #793C1B", +"I c #793B1C", +"J c #773C1C", +"K c #783D1B", +"L c #86461C", +"M c #793E1C", +"N c #793F1C", +"O c #833D1A", +"P c #7A3C1C", +"Q c #77391C", +"R c #70381C", +"S c #66351B", +"T c #6B341B", +"U c #68351B", +"V c #67361C", +"W c #613119", +"X c #643119", +"Y c #63341B", +"Z c #60301A", +"` c #64311A", +" . c #65321A", +".. c #6A371C", +"+. c #61321A", +"@. c #5F301D", +"#. c #61331F", +"$. c #5E331D", +"%. c #5C321D", +"&. c #592F1E", +"*. c #62311B", +"=. c #693119", +"-. c #713519", +";. c #75381A", +">. c #69351E", +",. c #6D381D", +"'. c #62341D", +"). c #60331D", +"!. c #5D301C", +"~. c #5B311C", +"{. c #5C2F1B", +"]. c #62311C", +"^. c #6B361E", +"/. c #6C351A", +"(. c #6B351A", +"_. c #6E351A", +":. c #74381B", +"<. c #7B3B1B", +"[. c #7E3918", +"}. c #833D18", +"|. c #773B1B", +"1. c #713A1C", +"2. c #6C381D", +"3. c #69341B", +"4. c #6C371E", +"5. c #6C361E", +"6. c #6C351E", +"7. c #73391D", +"8. c #77381D", +"9. c #81401B", +"0. c #88451B", +"a. c #80411B", +"b. c #723A1B", +"c. c #703A1D", +"d. c #6A371E", +"e. c #68351D", +"f. c #65341E", +"g. c #6C341D", +"h. c #70351C", +"i. c #6F371F", +"j. c #6E3720", +"k. c #6F371A", +"l. c #6C341A", +"m. c #6B381C", +"n. c #6B391C", +"o. c #68341A", +"p. c #67321A", +"q. c #622D18", +"r. c #612C19", +"s. c #612D19", +"t. c #612E1A", +"u. c #64331C", +"v. c #66361F", +"w. c #6B3720", +"x. c #6F351E", +"y. c #76371A", +"z. c #6F371D", +"A. c #65351E", +"B. c #63321C", +"C. c #6A321A", +"D. c #6C351B", +"E. c #6C361C", +"F. c #6C341B", +"G. c #63341D", +"H. c #66351D", +"I. c #6E361A", +"J. c #72361A", +"K. c #773B1A", +"L. c #733A1B", +"M. c #6B351B", +"N. c #6E381C", +"O. c #743A1C", +"P. c #7A3F1C", +"Q. c #793B1A", +"R. c #72381B", +"S. c #71391B", +"T. c #6B391D", +"U. c #6D371B", +"V. c #6B351C", +"W. c #6A3A1E", +"X. c #6B381D", +"Y. c #6A381E", +"Z. c #6E391C", +"`. c #70361A", +" + c #73351A", +".+ c #6B341C", +"++ c #69341D", +"@+ c #64341D", +"#+ c #62321C", +"$+ c #65351C", +"%+ c #62341C", +"&+ c #57301D", +"*+ c #562E1C", +"=+ c #592E1E", +"-+ c #5C321E", +";+ c #582E1D", +">+ c #572E1E", +",+ c #572E1D", +"'+ c #562E1E", +")+ c #55301E", +"!+ c #5B341E", +"~+ c #5F341E", +"{+ c #61341E", +"]+ c #6A361F", +"^+ c #65331D", +"/+ c #69361E", +"(+ c #60321A", +"_+ c #60321B", +":+ c #5E341C", +"<+ c #6A361E", +"[+ c #6A331D", +"}+ c #70371C", +"|+ c #6B361C", +"1+ c #6C371C", +"2+ c #763B1A", +"3+ c #743C1D", +"4+ c #6F3B1E", +"5+ c #713B1D", +"6+ c #703C1E", +"7+ c #6F3A1D", +"8+ c #6B371B", +"9+ c #69371D", +"0+ c #71371A", +"a+ c #7D3C1B", +"b+ c #803B19", +"c+ c #883E18", +"d+ c #89431B", +"e+ c #87411A", +"f+ c #823F1B", +"g+ c #7C3B1A", +"h+ c #743B1C", +"i+ c #6C391C", +"j+ c #78391B", +"k+ c #7E3A19", +"l+ c #813A18", +"m+ c #7C3A1A", +"n+ c #7E3B1A", +"o+ c #71371B", +"p+ c #6D361B", +"q+ c #733B1E", +"r+ c #71381D", +"s+ c #6F351B", +"t+ c #6C351C", +"u+ c #69351B", +"v+ c #69351A", +"w+ c #6A3419", +"x+ c #6B341A", +"y+ c #6F361B", +"z+ c #6E311A", +"A+ c #6F341B", +"B+ c #6C351F", +"C+ c #69331A", +"D+ c #68321A", +"E+ c #5D301A", +"F+ c #5E301B", +"G+ c #64341B", +"H+ c #66321B", +"I+ c #5F311B", +"J+ c #5C301B", +"K+ c #67341C", +"L+ c #672F1B", +"M+ c #66341C", +"N+ c #6C361D", +"O+ c #6E381D", +"P+ c #733C1C", +"Q+ c #6B381E", +"R+ c #67351D", +"S+ c #6A361B", +"T+ c #71391C", +"U+ c #71381C", +"V+ c #763B1C", +"W+ c #7F3C1E", +"X+ c #954420", +"Y+ c #AC5328", +"Z+ c #C46938", +"`+ c #CF7543", +" @ c #D47848", +".@ c #D17746", +"+@ c #C56F3E", +"@@ c #B55F31", +"#@ c #9D4821", +"$@ c #8C3F1D", +"%@ c #73361B", +"&@ c #68331B", +"*@ c #70361B", +"=@ c #74381A", +"-@ c #723819", +";@ c #743719", +">@ c #75371A", +",@ c #73371A", +"'@ c #6E371B", +")@ c #6F381C", +"!@ c #71371C", +"~@ c #70371A", +"{@ c #71381B", +"]@ c #6F381A", +"^@ c #69341A", +"/@ c #64321A", +"(@ c #65301A", +"_@ c #6A331B", +":@ c #5E2F1B", +"<@ c #8A3F17", +"[@ c #883E17", +"}@ c #813B18", +"|@ c #7B3818", +"1@ c #783919", +"2@ c #743A1B", +"3@ c #73391A", +"4@ c #6F371B", +"5@ c #733B1B", +"6@ c #72391B", +"7@ c #733A1C", +"8@ c #73391B", +"9@ c #75381C", +"0@ c #6D3A1C", +"a@ c #6F381B", +"b@ c #A24621", +"c@ c #CF622F", +"d@ c #E89558", +"e@ c #FCC281", +"f@ c #FFD599", +"g@ c #FEE0AA", +"h@ c #FEE7B9", +"i@ c #FEE8C0", +"j@ c #FFE8BC", +"k@ c #FEE4B0", +"l@ c #FFDAA0", +"m@ c #FECE8E", +"n@ c #F1AD6C", +"o@ c #DB783F", +"p@ c #B34D25", +"q@ c #76371C", +"r@ c #68321B", +"s@ c #6C361B", +"t@ c #723B1B", +"u@ c #783C1A", +"v@ c #7E3C1A", +"w@ c #7F3E1A", +"x@ c #853E19", +"y@ c #854019", +"z@ c #894219", +"A@ c #843E18", +"B@ c #823D18", +"C@ c #803D18", +"D@ c #7C3818", +"E@ c #823C17", +"F@ c #823E18", +"G@ c #6A381C", +"H@ c #64341C", +"I@ c #6F3519", +"J@ c #793919", +"K@ c #853F18", +"L@ c #8C4419", +"M@ c #8C4217", +"N@ c #8A4017", +"O@ c #853C17", +"P@ c #813A17", +"Q@ c #7D3C19", +"R@ c #763718", +"S@ c #743819", +"T@ c #793D1B", +"U@ c #69341C", +"V@ c #6B371E", +"W@ c #733B1D", +"X@ c #743B1B", +"Y@ c #8D4523", +"Z@ c #E66B34", +"`@ c #FBAD62", +" # c #FFEE9F", +".# c #FFF9B2", +"+# c #FFFEBF", +"@# c #FEFECB", +"## c #FEFED1", +"$# c #FFFFD8", +"%# c #FFFFDB", +"&# c #FFFFD9", +"*# c #FFFFD4", +"=# c #FDFECB", +"-# c #FFFFC3", +";# c #FFFCB7", +"># c #FFF8AA", +",# c #FBD483", +"'# c #F37F40", +")# c #B44E26", +"!# c #6C3519", +"~# c #6F3619", +"{# c #763919", +"]# c #773819", +"^# c #7E3C19", +"/# c #7E3B19", +"(# c #77391A", +"_# c #6F381D", +":# c #6B361B", +"<# c #69361A", +"[# c #66341B", +"}# c #64351C", +"|# c #65331A", +"1# c #5F341C", +"2# c #58321D", +"3# c #592E1B", +"4# c #5D311B", +"5# c #703A1B", +"6# c #80421B", +"7# c #82401A", +"8# c #8B4017", +"9# c #873E17", +"0# c #833E18", +"a# c #7D3A18", +"b# c #773718", +"c# c #753719", +"d# c #753819", +"e# c #BB5729", +"f# c #FF8643", +"g# c #FFE185", +"h# c #FEFEAC", +"i# c #FFFFB3", +"j# c #FFFEBB", +"k# c #FFFFC5", +"l# c #FFFFCD", +"m# c #FFFFDA", +"n# c #FFFFD6", +"o# c #FFFFCF", +"p# c #FFFFC6", +"q# c #FFFEBE", +"r# c #FFFEB4", +"s# c #FFFFAB", +"t# c #FFFFA2", +"u# c #FFB261", +"v# c #DD6531", +"w# c #7A3A1E", +"x# c #60301B", +"y# c #61321B", +"z# c #66341E", +"A# c #5F321D", +"B# c #62331D", +"C# c #65341C", +"D# c #6C3518", +"E# c #6D361C", +"F# c #6C351D", +"G# c #6D3319", +"H# c #70391B", +"I# c #83421C", +"J# c #833F1A", +"K# c #7C3B1B", +"L# c #763819", +"M# c #68361C", +"N# c #5C321C", +"O# c #592D1A", +"P# c #5D2F1A", +"Q# c #63311A", +"R# c #64351B", +"S# c #6A341B", +"T# c #71361A", +"U# c #763A1A", +"V# c #7B3A19", +"W# c #7A3919", +"X# c #7E3A17", +"Y# c #8D421C", +"Z# c #D9622E", +"`# c #FF994D", +" $ c #FFF58F", +".$ c #FEFEA3", +"+$ c #FFFEAC", +"@$ c #FFFFB7", +"#$ c #FFFFC0", +"$$ c #FFFFCA", +"%$ c #FFFFD1", +"&$ c #FFFFDC", +"*$ c #FFFFDE", +"=$ c #FFFFDD", +"-$ c #FFFFD3", +";$ c #FFFFCB", +">$ c #FFFFC2", +",$ c #FFFFB8", +"'$ c #FFFEAE", +")$ c #FEFEA2", +"!$ c #FEFE99", +"~$ c #FFC56A", +"{$ c #F37439", +"]$ c #994622", +"^$ c #6A351C", +"/$ c #6E361C", +"($ c #72381A", +"_$ c #70371B", +":$ c #6F361C", +"<$ c #703619", +"[$ c #6E381E", +"}$ c #6B391E", +"|$ c #6E361B", +"1$ c #733819", +"2$ c #743618", +"3$ c #78381A", +"4$ c #793819", +"5$ c #7F3D18", +"6$ c #7F3C18", +"7$ c #864019", +"8$ c #853F19", +"9$ c #803D1A", +"0$ c #7D3F1C", +"a$ c #713B1C", +"b$ c #6C381C", +"c$ c #60321C", +"d$ c #5C2E1A", +"e$ c #5C2F1A", +"f$ c #613019", +"g$ c #6E3519", +"h$ c #813E19", +"i$ c #8F421A", +"j$ c #E66A32", +"k$ c #FF9148", +"l$ c #FEED83", +"m$ c #FEFE9A", +"n$ c #FFFEA5", +"o$ c #FFFFB1", +"p$ c #FFFFBC", +"q$ c #FFFFD7", +"r$ c #FFFFE1", +"s$ c #FFFFE3", +"t$ c #FFFFD0", +"u$ c #FFFFC8", +"v$ c #FFFFBD", +"w$ c #FFFFB2", +"x$ c #FFFEA6", +"y$ c #FFFF9B", +"z$ c #FEFE90", +"A$ c #FEB65D", +"B$ c #F97237", +"C$ c #B44F25", +"D$ c #7E3718", +"E$ c #753519", +"F$ c #7B3C1B", +"G$ c #783418", +"H$ c #773418", +"I$ c #773A1A", +"J$ c #6F3A1C", +"K$ c #6C3A1D", +"L$ c #6E381B", +"M$ c #6D3419", +"N$ c #66351C", +"O$ c #5F351E", +"P$ c #512F1E", +"Q$ c #4B2A1B", +"R$ c #4F2B1C", +"S$ c #532D1C", +"T$ c #5E321D", +"U$ c #E76E34", +"V$ c #FF803E", +"W$ c #FFC968", +"X$ c #FEFE91", +"Y$ c #FFFF9E", +"Z$ c #FFFFAA", +"`$ c #FFFFB6", +" % c #FFFFCE", +".% c #FFFFE4", +"+% c #FFFFE7", +"@% c #FFFFE8", +"#% c #FFFFE6", +"$% c #FFFFDF", +"%% c #FFFFC4", +"&% c #FFFE9F", +"*% c #FFFF93", +"=% c #FEFE85", +"-% c #FF8340", +";% c #F77137", +">% c #AB5024", +",% c #813F1B", +"'% c #7F3C1A", +")% c #7B3B19", +"!% c #783A19", +"~% c #733919", +"{% c #6D351A", +"]% c #68371D", +"^% c #67361E", +"/% c #69361D", +"(% c #6E391D", +"_% c #81431B", +":% c #81441C", +"<% c #7B3F1B", +"[% c #703C1D", +"}% c #703C1C", +"|% c #6D361A", +"1% c #72371A", +"2% c #763B1B", +"3% c #7A3B1A", +"4% c #813D19", +"5% c #883F18", +"6% c #D4642F", +"7% c #FF7E3D", +"8% c #FE9246", +"9% c #FFEF7E", +"0% c #FFFF94", +"a% c #FFFFBE", +"b% c #FFFFE5", +"c% c #FFFFEB", +"d% c #FFFFEE", +"e% c #FFFFEF", +"f% c #FFFFCC", +"g% c #FFFFA4", +"h% c #FFFE95", +"i% c #FFFF8B", +"j% c #FFC05F", +"k% c #FE7438", +"l% c #E66A34", +"m% c #78391E", +"n% c #5D311C", +"o% c #5B321D", +"p% c #582D1A", +"q% c #542B1A", +"r% c #532B1B", +"s% c #502B1C", +"t% c #552D1C", +"u% c #572E1B", +"v% c #783E1C", +"w% c #7C3E1A", +"x% c #7E3B1B", +"y% c #79381B", +"z% c #7F3C1B", +"A% c #843C18", +"B% c #833D17", +"C% c #833C17", +"D% c #823A17", +"E% c #7F3D19", +"F% c #823E1A", +"G% c #803D19", +"H% c #853F1A", +"I% c #863E17", +"J% c #863D18", +"K% c #A74F23", +"L% c #FA793B", +"M% c #FE823E", +"N% c #FFA651", +"O% c #FFFE88", +"P% c #FFFF98", +"Q% c #FFFFA8", +"R% c #FFFFED", +"S% c #FFFFF2", +"T% c #FFFFF4", +"U% c #FFFFF5", +"V% c #FFFFF1", +"W% c #FFFFC7", +"X% c #FFFF9A", +"Y% c #FFFF8C", +"Z% c #FFDB6C", +"`% c #FD7D3C", +" & c #FE773A", +".& c #C4592B", +"+& c #74371A", +"@& c #753A1C", +"#& c #7A3E1C", +"$& c #7E3D1B", +"%& c #894119", +"&& c #914518", +"*& c #954417", +"=& c #8C461B", +"-& c #61351E", +";& c #63331C", +">& c #67351B", +",& c #763C1B", +"'& c #793E1B", +")& c #7D421B", +"!& c #5F311C", +"~& c #61331E", +"{& c #D06633", +"]& c #FF7F3D", +"^& c #FF8F45", +"/& c #FFB156", +"(& c #FFFF89", +"_& c #FFFFAE", +":& c #FFFFF9", +"<& c #FFFFFC", +"[& c #FFFFFD", +"}& c #FFFFFB", +"|& c #FFFFF8", +"1& c #FFFFF3", +"2& c #FFFFEC", +"3& c #FFFFB0", +"4& c #FFFF9F", +"5& c #FFFF8E", +"6& c #FFDC6C", +"7& c #FE8841", +"8& c #FF7D3D", +"9& c #FF7639", +"0& c #92421E", +"a& c #753517", +"b& c #723618", +"c& c #6B3318", +"d& c #663018", +"e& c #673018", +"f& c #652E19", +"g& c #632F19", +"h& c #633019", +"i& c #592F1A", +"j& c #5A311B", +"k& c #5D341D", +"l& c #5E351D", +"m& c #8F3F17", +"n& c #874018", +"o& c #803C19", +"p& c #823C19", +"q& c #833E19", +"r& c #863E18", +"s& c #8C4117", +"t& c #8B3F17", +"u& c #893E17", +"v& c #61301A", +"w& c #894323", +"x& c #FE8842", +"y& c #FF9A4A", +"z& c #FFBF5C", +"A& c #FFFB89", +"B& c #FFFFA3", +"C& c #FFFFB5", +"D& c #FFFFE0", +"E& c #FFFFEA", +"F& c #FFFFFA", +"G& c #FFFFFE", +"H& c #FFFFFF", +"I& c #FFFFD2", +"J& c #FFFFA5", +"K& c #FFFF91", +"L& c #FFD969", +"M& c #FF9949", +"N& c #FE8440", +"O& c #FF7A3B", +"P& c #DE6431", +"Q& c #7D3718", +"R& c #7D3918", +"S& c #853B17", +"T& c #843B17", +"U& c #843E19", +"V& c #833F19", +"W& c #873F18", +"X& c #873E16", +"Y& c #8A3E17", +"Z& c #6F3A1B", +"`& c #783A1A", +" * c #783B1B", +".* c #7E3B18", +"+* c #813C17", +"@* c #7E3917", +"#* c #7D3B1A", +"$* c #7A3A1A", +"%* c #CF6430", +"&* c #FE813E", +"** c #FE9045", +"=* c #FFA750", +"-* c #FFD164", +";* c #FFFA8B", +">* c #FFFF95", +",* c #FFDD6C", +"'* c #FFAB53", +")* c #FE7C3B", +"!* c #7D3B1E", +"~* c #68341B", +"{* c #65331B", +"]* c #65361C", +"^* c #67361D", +"/* c #68381D", +"(* c #693A1E", +"_* c #6A391E", +":* c #6A3921", +"<* c #6D391D", +"[* c #68351E", +"}* c #68351C", +"|* c #69381C", +"1* c #723619", +"2* c #773818", +"3* c #833D19", +"4* c #FB7B3B", +"5* c #FF8741", +"6* c #FF994A", +"7* c #FFB858", +"8* c #FFE770", +"9* c #FFFF92", +"0* c #FFFFAD", +"a* c #FFFFE9", +"b* c #FFFFBF", +"c* c #FFF277", +"d* c #FF8440", +"e* c #FF793B", +"f* c #B7552A", +"g* c #562C1A", +"h* c #562E1B", +"i* c #5C311C", +"j* c #5A311C", +"k* c #592F1B", +"l* c #58301C", +"m* c #5E341D", +"n* c #65361D", +"o* c #673821", +"p* c #6E371A", +"q* c #6F361A", +"r* c #70351B", +"s* c #6E3419", +"t* c #9A4B23", +"u* c #FF7F3E", +"v* c #FE8B42", +"w* c #FFA24E", +"x* c #FFC960", +"y* c #FFF87D", +"z* c #FFFF9D", +"A* c #FFFEB1", +"B* c #FFFFF7", +"C* c #FFFFA0", +"D* c #FFFE84", +"E* c #FFD366", +"F* c #FFA44F", +"G* c #FF8C43", +"H* c #E76B33", +"I* c #6C371A", +"J* c #6B3419", +"K* c #693419", +"L* c #6A361A", +"M* c #6A351B", +"N* c #6A361C", +"O* c #6E3A1B", +"P* c #71351A", +"Q* c #6D361D", +"R* c #70351A", +"S* c #753B1A", +"T* c #BD5C2C", +"U* c #FF823F", +"V* c #FFAB52", +"W* c #FFD767", +"X* c #FFFF88", +"Y* c #FFFFA6", +"Z* c #FFFEA8", +"`* c #FFFF8F", +" = c #FFE56E", +".= c #FFAF54", +"+= c #FF9146", +"@= c #FE7C3C", +"#= c #F27237", +"$= c #76381D", +"%= c #65311B", +"&= c #6A361D", +"*= c #753A1B", +"== c #6E3A1D", +"-= c #70391C", +";= c #6F371C", +">= c #6C371D", +",= c #6D361E", +"'= c #783F1D", +")= c #D46833", +"!= c #FF9548", +"~= c #FFB356", +"{= c #FFE46F", +"]= c #FFFEAB", +"^= c #FFFFBB", +"/= c #FFFFD5", +"(= c #FFFFB9", +"_= c #FFFF97", +":= c #FFF075", +"<= c #FFB959", +"[= c #FF9648", +"}= c #F67438", +"|= c #934623", +"1= c #67331C", +"2= c #6F391C", +"3= c #6D3A1E", +"4= c #75391A", +"5= c #853D17", +"6= c #7D3D1B", +"7= c #723E1D", +"8= c #6F3B1D", +"9= c #703B1C", +"0= c #70381B", +"a= c #66361C", +"b= c #E57037", +"c= c #FE8640", +"d= c #FFB859", +"e= c #FFEC73", +"f= c #FFFFAF", +"g= c #FFF57A", +"h= c #FF833F", +"i= c #F77438", +"j= c #A74B21", +"k= c #75391B", +"l= c #70381A", +"m= c #7C401C", +"n= c #7F421B", +"o= c #874119", +"p= c #6A371B", +"q= c #6C391D", +"r= c #793D1A", +"s= c #783D1C", +"t= c #743B1D", +"u= c #6B371C", +"v= c #ED7439", +"w= c #FFBB5A", +"x= c #FFFF96", +"y= c #FFFFF0", +"z= c #FFF67C", +"A= c #FFC35E", +"B= c #FF9B4B", +"C= c #F87539", +"D= c #9D4C26", +"E= c #66361D", +"F= c #713618", +"G= c #723A1C", +"H= c #713A1B", +"I= c #6B3B1D", +"J= c #63331F", +"K= c #63341F", +"L= c #673620", +"M= c #63331B", +"N= c #803A18", +"O= c #7F3919", +"P= c #7D3A19", +"Q= c #6D341A", +"R= c #E57238", +"S= c #FFBA5A", +"T= c #FFEF73", +"U= c #FFFF9C", +"V= c #FFF67B", +"W= c #FFC25D", +"X= c #FF9B4A", +"Y= c #F87639", +"Z= c #9A4827", +"`= c #6B3521", +" - c #62321F", +".- c #833E1A", +"+- c #CF6834", +"@- c #FFB758", +"#- c #FFE871", +"$- c #FFFF99", +"%- c #FFF276", +"&- c #FFBC5A", +"*- c #FF9849", +"=- c #F57538", +"-- c #8E4523", +";- c #BB5D2D", +">- c #FFB154", +",- c #FFDE6B", +"'- c #FFFFA9", +")- c #FFFFF6", +"!- c #FFEB72", +"~- c #FFB557", +"{- c #FE7F3C", +"]- c #F17438", +"^- c #7F3F1F", +"/- c #9D4C25", +"(- c #FE8340", +"_- c #FFA851", +":- c #FFFD82", +"<- c #FFDD6A", +"[- c #FF9046", +"}- c #ED7036", +"|- c #6F3A1E", +"1- c #FF813E", +"2- c #FF8D44", +"3- c #FFA04D", +"4- c #FFF176", +"5- c #FFFEB0", +"6- c #FFFA7D", +"7- c #FFA24D", +"8- c #FF8A43", +"9- c #FE7C3D", +"0- c #CA5F2D", +"a- c #83401A", +"b- c #7E3E1B", +"c- c #733B1C", +"d- c #803C1A", +"e- c #DA6C33", +"f- c #FE8741", +"g- c #FE9848", +"h- c #FFB255", +"i- c #FFFE87", +"j- c #FFFEB6", +"k- c #FFFEB5", +"l- c #FFFFA7", +"m- c #FFFF8D", +"n- c #FFB657", +"o- c #FF9749", +"p- c #FF7B3B", +"q- c #954923", +"r- c #86421B", +"s- c #763E1D", +"t- c #733D1D", +"u- c #6F3C1D", +"v- c #A55026", +"w- c #FF9145", +"x- c #FFA44E", +"y- c #FFC65F", +"z- c #FFF177", +"A- c #FFFFBA", +"B- c #FFFEAD", +"C- c #FFF87C", +"D- c #FFCB62", +"E- c #FFA54F", +"F- c #FE8D44", +"G- c #E96F35", +"H- c #773B1C", +"I- c #67331B", +"J- c #7A3D1E", +"K- c #E37438", +"L- c #FFB054", +"M- c #FFD666", +"N- c #FFFA7F", +"O- c #FFFEAF", +"P- c #FFFD84", +"Q- c #FFDC6A", +"R- c #FF9748", +"S- c #FE8540", +"T- c #FE7B3C", +"U- c #9A4924", +"V- c #72381C", +"W- c #68361D", +"X- c #61331B", +"Y- c #62321A", +"Z- c #67341B", +"`- c #B45728", +" ; c #FA823F", +".; c #FE8C42", +"+; c #FFA14D", +"@; c #FFDE6A", +"#; c #FFFE81", +"$; c #FFE56F", +"%; c #FF9F4C", +"&; c #D56631", +"*; c #6E391E", +"=; c #72371B", +"-; c #6D381A", +";; c #5D301B", +">; c #59301C", +",; c #59311D", +"'; c #512B1B", +"); c #512A1B", +"!; c #5D2F1B", +"~; c #62311A", +"{; c #D86E35", +"]; c #FE9144", +"^; c #FFDF6B", +"/; c #FFFC7F", +"(; c #FFFFC1", +"_; c #FFE76F", +":; c #FFC05C", +"<; c #FFA34E", +"[; c #F57639", +"}; c #90431E", +"|; c #69351C", +"1; c #683820", +"2; c #65371E", +"3; c #69381E", +"4; c #65371D", +"5; c #62321B", +"6; c #5F341D", +"7; c #61341D", +"8; c #63311B", +"9; c #66331B", +"0; c #6C3319", +"a; c #813F1D", +"b; c #EB773A", +"c; c #FFB958", +"d; c #FFF378", +"e; c #FFFE8A", +"f; c #FFFEB3", +"g; c #FF843F", +"h; c #B15427", +"i; c #773A19", +"j; c #733618", +"k; c #77381A", +"l; c #76391B", +"m; c #6D371A", +"n; c #6B3A1E", +"o; c #853E18", +"p; c #823F1A", +"q; c #823F19", +"r; c #823A18", +"s; c #7D3818", +"t; c #7C3718", +"u; c #78371A", +"v; c #7E3818", +"w; c #8D431F", +"x; c #EF783A", +"y; c #FFC85F", +"z; c #FFE16C", +"A; c #FFF47A", +"B; c #FFFE86", +"C; c #FFF77C", +"D; c #FFCC61", +"E; c #FFB256", +"F; c #FE9044", +"G; c #FE833F", +"H; c #AC532A", +"I; c #8A4218", +"J; c #863C17", +"K; c #813C19", +"L; c #873E18", +"M; c #8E3E16", +"N; c #8E3E17", +"O; c #57331E", +"P; c #522E1C", +"Q; c #532C1B", +"R; c #5D2D1A", +"S; c #5D2C19", +"T; c #5F2D19", +"U; c #612F19", +"V; c #642F19", +"W; c #6A3318", +"X; c #6E3318", +"Y; c #6F3419", +"Z; c #84411B", +"`; c #8E451E", +" > c #EA7738", +".> c #FF8D43", +"+> c #FFB556", +"@> c #FFC75F", +"#> c #FFE66F", +"$> c #FFF679", +"%> c #FFF67A", +"&> c #FFE870", +"*> c #FFDA69", +"=> c #FFB757", +"-> c #FF9A49", +";> c #FE7D3C", +">> c #984B29", +",> c #7A401B", +"'> c #743D1B", +")> c #753D1C", +"!> c #6E381A", +"~> c #6E3A1C", +"{> c #62331C", +"]> c #5B351E", +"^> c #944316", +"/> c #884219", +"(> c #7A3E1D", +"_> c #753E1D", +":> c #723A1E", +"<> c #713C1E", +"[> c #6C391E", +"}> c #75361A", +"|> c #75381B", +"1> c #63301A", +"2> c #67331D", +"3> c #D76B36", +"4> c #FC833F", +"5> c #FF8842", +"6> c #FD9A49", +"7> c #FFAD53", +"8> c #FFBE5A", +"9> c #FFC55E", +"0> c #FFC35D", +"a> c #FFBE5B", +"b> c #FF8641", +"c> c #FF813F", +"d> c #F4773A", +"e> c #A04D24", +"f> c #88421A", +"g> c #823E1B", +"h> c #7F3D1B", +"i> c #803B1A", +"j> c #844019", +"k> c #7B3C1C", +"l> c #813D1C", +"m> c #813D1B", +"n> c #8A441A", +"o> c #984717", +"p> c #63321B", +"q> c #5F2F1D", +"r> c #592C1B", +"s> c #572C1B", +"t> c #4D2A1B", +"u> c #4D291B", +"v> c #4F291A", +"w> c #53291A", +"x> c #532A1A", +"y> c #57301C", +"z> c #5A301B", +"A> c #68331A", +"B> c #BD582A", +"C> c #ED7539", +"D> c #FF8A42", +"E> c #FF8E44", +"F> c #FE9346", +"G> c #FF9C4B", +"H> c #FF9E4B", +"I> c #FF9E4C", +"J> c #FF9C4A", +"K> c #FD9246", +"L> c #F97A3A", +"M> c #DA632F", +"N> c #9F451D", +"O> c #863D16", +"P> c #713619", +"Q> c #6C381B", +"R> c #6B3A1C", +"S> c #6E3B1D", +"T> c #763D1B", +"U> c #82441C", +"V> c #7E411B", +"W> c #793B19", +"X> c #7E3E1A", +"Y> c #67381E", +"Z> c #63371E", +"`> c #64381E", +" , c #65361E", +"., c #63351D", +"+, c #66351A", +"@, c #703719", +"#, c #7F3F1B", +"$, c #78391A", +"%, c #763618", +"&, c #6C3217", +"*, c #6E2F17", +"=, c #7A2C15", +"-, c #A3361B", +";, c #D14E26", +">, c #F26C34", +",, c #FF8943", +"', c #FF8B43", +"), c #FF8942", +"!, c #FA7539", +"~, c #E4592A", +"{, c #C1441F", +"], c #993918", +"^, c #873E19", +"/, c #4E2B1B", +"(, c #502A1B", +"_, c #532E1D", +":, c #5C331D", +"<, c #6D3A1D", +"[, c #703519", +"}, c #6A3A1F", +"|, c #743519", +"1, c #783519", +"2, c #773719", +"3, c #763419", +"4, c #763519", +"5, c #7A3618", +"6, c #7F3918", +"7, c #7A3719", +"8, c #79381A", +"9, c #863817", +"0, c #963715", +"a, c #A73414", +"b, c #BC3416", +"c, c #D03617", +"d, c #DE3B19", +"e, c #ED4F25", +"f, c #F5642F", +"g, c #FB7337", +"h, c #FE793A", +"i, c #FF7D3C", +"j, c #FC7538", +"k, c #F86932", +"l, c #F15629", +"m, c #E6421D", +"n, c #D73517", +"o, c #C73314", +"p, c #B63514", +"q, c #A33815", +"r, c #933B16", +"s, c #833B18", +"t, c #703418", +"u, c #5B2E1A", +"v, c #5A2E1A", +"w, c #6B361D", +"x, c #813F1A", +"y, c #843F1A", +"z, c #7E3D1A", +"A, c #773B1D", +"B, c #6C371F", +"C, c #6E381F", +"D, c #6F351C", +"E, c #703419", +"F, c #713419", +"G, c #703319", +"H, c #6B351E", +"I, c #6A341A", +"J, c #753119", +"K, c #812F18", +"L, c #952F17", +"M, c #A82C16", +"N, c #B72D16", +"O, c #C12D15", +"P, c #C82E15", +"Q, c #CD2D16", +"R, c #D02D14", +"S, c #D12D14", +"T, c #D12E14", +"U, c #CF2D14", +"V, c #CB2D14", +"W, c #C42E14", +"X, c #B82D14", +"Y, c #AA2E14", +"Z, c #9E3315", +"`, c #923715", +" ' c #853716", +".' c #763818", +"+' c #6C361A", +"@' c #5D321D", +"#' c #65351D", +"$' c #703B1E", +"%' c #7A401D", +"&' c #7F421D", +"*' c #80411C", +"=' c #773D1C", +"-' c #73371C", +";' c #63321D", +">' c #61321C", +",' c #61331D", +"'' c #61331C", +")' c #61321D", +"!' c #60311A", +"~' c #572D1A", +"{' c #6A2E1A", +"]' c #742D19", +"^' c #802A17", +"/' c #892A17", +"(' c #942A17", +"_' c #9D2C17", +":' c #A62D17", +"<' c #A92F19", +"[' c #AD2F19", +"}' c #AE2F16", +"|' c #AC2D15", +"1' c #A52C17", +"2' c #A02D16", +"3' c #9A2F17", +"4' c #8F2D16", +"5' c #882F17", +"6' c #833216", +"7' c #7A3417", +"8' c #753518", +"9' c #753718", +"0' c #7C3A18", +"a' c #863F18", +"b' c #883F17", +"c' c #893F18", +"d' c #813C18", +"e' c #763918", +"f' c #64301A", +"g' c #5D331D", +"h' c #5C341D", +"i' c #6F3618", +"j' c #803B18", +"k' c #803C18", +"l' c #7D3C18", +"m' c #753919", +"n' c #733718", +"o' c #6C3318", +"p' c #6D3318", +"q' c #703219", +"r' c #773318", +"s' c #7C3118", +"t' c #813119", +"u' c #873219", +"v' c #8B3317", +"w' c #893218", +"x' c #8C3218", +"y' c #8D3117", +"z' c #8C3017", +"A' c #893117", +"B' c #883117", +"C' c #843318", +"D' c #7F3119", +"E' c #7B3418", +"F' c #79391A", +"G' c #773918", +"H' c #7B391A", +"I' c #8A3E16", +"J' c #8D4117", +"K' c #8D4218", +"L' c #68371C", +"M' c #813D18", +"N' c #803E19", +"O' c #723B1C", +"P' c #67321B", +"Q' c #72351A", +"R' c #6E351B", +"S' c #70341C", +"T' c #78361A", +"U' c #7D3719", +"V' c #7A351A", +"W' c #793419", +"X' c #753218", +"Y' c #793318", +"Z' c #783319", +"`' c #743419", +" ) c #70391D", +".) c #79361A", +"+) c #7A371A", +"@) c #803917", +"#) c #883D17", +"$) c #7D3B19", +"%) c #72391C", +"&) c #6F391D", +"*) c #7A381A", +"=) c #72391D", +"-) c #71371D", +";) c #77371A", +">) c #69371C", +",) c #67371E", +"') c #5C301C", +")) c #61301C", +"!) c #69321B", +"~) c #6D341B", +"{) c #67321E", +"]) c #60321D", +"^) c #66331A", +"/) c #64331A", +"() c #74371B", +"_) c #7C391A", +":) c #7B391B", +"<) c #74381C", +"[) c #7B3A1A", +"}) c #66341D", +"|) c #62361D", +"1) c #5F311A", +"2) c #60311B", +"3) c #68341D", +"4) c #60331F", +"5) c #60331E", +"6) c #5B2E1C", +"7) c #562D1C", +"8) c #582F1E", +"9) c #582F1D", +"0) c #542E1C", +"a) c #59301D", +"b) c #60311C", +"c) c #5F321C", +"d) c #5F331D", +"e) c #6A381D", +"f) c #65321D", +"g) c #65341D", +"h) c #6C3219", +"i) c #6B3119", +"j) c #693019", +"k) c #64321C", +"l) c #62311D", +"m) c #64351F", +"n) c #6B3721", +"o) c #693921", +"p) c #62341F", +"q) c #5A2D1B", +"r) c #592D1B", +"s) c #592F1C", +"t) c #5F2E1A", +"u) c #69391D", +"v) c #6D3621", +"w) c #70351E", +"x) c #6E3620", +"y) c #66331E", +"z) c #61321E", +"A) c #5B311D", +"B) c #5D311D", +"C) c #6A351F", +"D) c #6B3620", +"E) c #6B361F", +"F) c #6E3621", +"G) c #69371E", +". + @ # $ % % & * = - ; > = , ' ) ! ~ { ] ^ / ( _ : < < [ } | ^ / ( _ : < < [ 1 2 3 @ 4 5 6 7 8 1 9 0 a b c d e f g h i ", +"j k l m n n o p q r s t u v w x y z A B C D E F G H I J K L L M N M O P Q R S T ! U V W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.", +"= >.,.'.).; !.~.{.].^.' /./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.K b.c.d.e.f.g.h.i.j.T _.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.", +"A.A.B.C.D.E.F.G.H.I.J.K.L.M.M.1 N.O.P.Q.R.S.T.e.1 M.U.V.A f.W.X.Y.X.Z./.`. +F..+++@+#+$+%+&+*+> =+-+;+>+,+,+'+)+!+~+{+]+", +"^+/+^.+.(+_+(+:+#+<+[+T }+Z.|+1+2+3+4+5+6+7+8+N.9+0+a+b+c+d+e+f+g+h+i+b.Q.j+k+l+m+d q n+g+o+p+q+r+s+t+u+v+w+x+y+! z+A+B+", +"+ C+D+E+F+G+H+I+J+K+L+M+e.N+O+P+M Q+^+R+S+T+U+V+o+W+X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@J.-@p ;@>@,@'@)@T+)@!@~@{@]@^@/@' (@_@:@", +"<@<@[@}@|@1@2@3@4@1 D 5@6@b.7@8@9@U+o+J.0@)@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@D.R ] &@s@t@u@v@w@x@y@z@A@B@C@D@E@F@}.k+", +"n.G@H@I@J@K@L@M@N@O@P@Q@R@S@T@l U@V@W@X@,.Y@Z@`@ #.#+#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]#^#/#(#_#_#_#:#<#[#' ' D.3.1+[#}#", +"R.T |#1#2#3#4#5#6#7#8#9#A@0#a#b#c#,@-@d#e#f#g#h#i#j#k#l#*#&#%#m#n#o#p#q#r#s#t#u#v#w#x#y#z#M+, G+A#B#C#D#E#F#M.;@g G#3.H#", +"I#J#K#L#{@M#N#O#P#Q#R#S#T#U#V#W#g+X#Y#Z#`# $.$+$@$#$$$%$$#&$*$=$&#-$;$>$,$'$)$!$~${$]$^$/$U+($`._$:$D.<$[$}$Q+|$1$2$3$4$", +"C@5$6$7$8$9$0$P.a$b$c$d$e$e$f$g$h$i$j$k$l$m$n$o$p$p#o#q$=$r$s$r$*$$#t$u$v$w$x$y$z$A$B$C$D$E$;@c#b F$W#G$H$a b#U#I$(#c 1@", +"M.s@J$K$T.] L$M$N$O$P$Q$R$S$T$:#2+U$V$W$X$Y$Z$`$-# %q$*$.%+%@%#%.%$%n#o#%%,$s#&%*%=%-%;%>%,%'%)%!%~%{%e.]%^%* M+K+/%* (%", +"_%:%<%[%}%J$A |%($~@1%2%3%4%8$5%6%7%8%9%0%t#o$a%;$n#*$b%c%d%e%e%c%b%$%n#f%#$o$g%h%i%j%k%l%m%n%o%p%q%r%s%R$t%u%x D+y+v%w%", +"x%y%z%A%B%C%D%E%F%G%H%K@I%J%9$K%L%M%N%O%P%Q%,$p#-$*$#%R%S%T%U%T%V%R%#%=$-$W%,$Z$X%Y%Z%`% &.&+&y.>@N.7+T+_$@&#&$&%&&&*&=&", +"-&; F+;&>&n.0@]@,&'&)&2%:#!&~&{&]&^&/&(&Y$_&#$l#m#.%d%T%:&<&[&}&|&1&2&.%m# %#$3&4&5&6&7&8&9&0&a&b&c&d&e&f&g&h&i&j&k&l&p.", +"m&n&O@o&p&q&F%r&s&t&u&o&I@v&w&8&x&y&z&A&B&C&p#-$D&E&1&F&G&G&H&G&G&:&S%E&$%I&W%@$J&K&L&M&N&O&P&Q&R&3%l+S&T&U&V&V&J#W&X&Y&", +"L$Z&P+V+`& *d#]#.*+*@*#*I$$*%*&***=*-*;*Q%p$;$&#.%e%:&H&H&G&H&G&H&G&|&d%s$$#;$p$Z$>*,*'*^&)* &!*+ /@o.~*{*]*u.^*/*(*_*C ", +":*<*p+[*}*|*[#/.b&p 1*d#2*3*4*5*6*7*8*9*0*#$o#=$a*T%<&H&G&H&H&H&G&H&<&S%+%&$ %b*_&P%c*z&y&d*e*f*O#g*h*x i*j*k*l*j*m*n*o*", +"i+p*q*p*~@{@r*0+X@W@N+++s*t*u*v*w*x*y*z*A*>$I&$%2&|&H&H&H&H&H&H&H&H&G&B*E&=$t$>$3&C*D*E*F*G*O&H*k.I*w+J*K*o.L*M*N*A O*|*", +"R 4@/.-.P*q*Q*6@R*/$] {@S*T*U***V*W*X*Y*@$p#-$D&d%F&H&G&H&H&H&H&H&G&G&|&2&*$%$%%C&Z*`* =.=+=@=#=$=%=}*1+u+{%y+..&=U+*=R ", +"L.==..-=;=>=|+U.,=$+X.'=_ )=d*!=~={=`*]=^=;$$#s$e%}&H&H&H&H&H&H&H&H&G&:&R%D&/=u$(=s#_=:=<=[=V$}=|=s@I+1=..2=E#3===b.c 4=", +"5=6=~@..p+|%==7=8=9=L.0=a=b=c=6*d=e=0%f=a% %m#+%S%<&H&H&H&H&H&H&H&H&G&F&d%s$q$;$p$_&y$g=z&M&h=i=j=J@q k=8+* U.l=X@m=n=o=", +"p=:#N*2=' q=1.*@r=s=t=>=u=v=5*y&w=:=x=o$#$t$&$@%T%[&H&H&H&H&H&H&H&H&G&}&y=b%&#f%a%f=z*z=A=B=-%C=D=E=F=1*;=G=>&<*H=_$S.I=", +"F%J=K=L=M=n*8=*=N=O=P=o+Q=R=5*y&S=T=>*o$#$o#=$a*T%[&H&H&H&H&H&H&H&H&G&<&V%b%&#l#a%f=U=V=W=X=-%Y=Z=`= -J=K=L=M=n*8=*=N=.-", +"| ^ / ( _ : < < [ 1 2 3 @ +-5*M&@-#-K&'$a% %%#+%1&<&H&G&H&H&H&H&H&G&G&F&e%s$q$;$p$_&$-%-&-*-U*=---{ ] ^ / ( _ : < < [ } ", +"L M N M O P Q R S T ! U V ;-c=!=>-,-i%'-^=$$$#.%d%:&G&H&G&H&H&H&G&H&G&)-c%D&/=u$(=s#*%!-~-!={-]-^-B C D E F G H I J K L ", +"0.a.K b.c.d.e.f.g.h.i.j.T /-(-**_--*:-t#`$k#-$$%E&1&F&H&H&H&G&H&H&G&:&y=#%&$%$-#C&x$(&<-V*[-8&}-[.}.|.1.2.3.4.5.6.7.8.9.", +"W.X.Y.X.Z./.`. +F..+++@+#+|-1-2-3-A=4-_=5-a%f%&#s$2&1&F&[&H&H&H&[&|&V%a*D&n#$$v$3&U=6-x*7-8-9-0-P.Q.R.S.T.e.1 M.U.V.A f.", +"a-b-Q.c-i+b.Q.j+k+l+m+d q d-e-f-g-h-<-i-J&j--#t$m#s$c%V%)-B*:&B*T%e%a*D&$# %>$k-l-m-8*n-o-h=p-q-4+5+6+7+8+N.9+0+a+b+r&r-", +"s-t-u-Z.1+N.M.&@*@=@J.-@p ;@v-h=w-x-y-z-0%0*A-k#%$&#D&b%E&R%R%2&a*b%*$q$ %%%,$B-$-C-D-E-F-u*G-Q+^+R+S+T+U+V+U+9+X.q*|.J ", +"H-O+m.G=] E.N*}*I-r@D.R ] &@J-K-5*6*L-M-N-X%O-A-%%l#/=m#*$D&r$D&=$&#-$f%-#(='$Y$P-Q-~=R-S-T-U-U+o+J.0@)@a@V-4@F.U.U.a@|.", +"==W-C#%+X-{*Y-(+M=Z-!#~#{#]#^#`- ;.;+;7*@;#;X%B-,$b*W%l#%$*#*#-$t$l#W%b*@$0*Y$i-$;&-%;v*7%&;t+V@W@X@,.*;R H#L$=;-;-=B p+", +"; ;;>;l*l*,;u%';);q%!;~;x#y#z#M+{;c=];x-&-^;/;>*l-w$(=a%>$%%k#%%(;a%,$A*'-$-P-_;:;<;**&*[;};c#,@-@p !#~*l.M.D.T |;1;2;Y ", +"3;4;5;6;7;}#' M=I+8;9;:#0;^$/$U+a;b;f-8%<;c;W*d;e;y$Y*_&f;C&`$C&w$'$l-z*m-z=@;&-<;];g;4*h;W#g+X#o&^#i;j;R@k;l;3+2=p*m;n;", +"r&o;B@p;q;D%r;s;n t;s;0 u;v;D$E$;@w;x;5*];3-h-y;z;A;B;K&P%U=Y$z*$-9*X*C;$;D;E;3-F;G;8&H;f$g$h$W&<@I;r&J%J;A%K;O@L;M;N;5=", +"O;P;Q;d$R;S;T;U;V;W;X;Y;n G%Z;,%'%)%`; >5*.>X==*+>@>W*#>:=$>C;%>%-&>*>x*=>=*->v*h=;>>>S$T$:#2+,>'>)>!>~>m.a=%+%={>[#}*]>", +"^>/>f+(>_>:><>[>s@,@}>3$|>($T 1>n%o%p%2>3>4>5>w-6><;7>n-8>W=9>0>a>@-7><;y&F;b>c>d>e>3%4%8$5%f>f+} g>h>i>G j>F%k>l>m>n>o>", +"c-p>q>r>s>t>u>v>w>x>t y>z>8;A>s*+&y.>@N.9@B>C>c=D>E>F>*-G>H>%;I>J>*-K>E>x&d*L>M>N>K@O>J%9$^#u@($P*g P>Q>R>S>S>T>U>V>W>X>", +"Y>Z>Z>`> , ,.,+,U.@,L#b `&#,#*$,%,a&b&&,*,=,-,;,>,d*,,',',G*',',',),x&d*!,~,{,],^,$*/.!&*+/,(,_,:,C#x+J$S.<,q=1 [,0;A },", +"l;R@R@b&P>|,%,1,$,2,3,4,5,6,v;7,8,Q&R&V#9,0,a,b,c,d,e,f,g,h,V$i,j,k,l,m,n,o,p,q,r,s,t,v&u,p%v,y w,S.2%x,y,7$y,z,V&X>h+A,", +"0+]#4,U.B,C,'@M*,=D,E,F,G,s+H,_@I,p.+ X 0;J,K,L,M,N,O,P,Q,R,S,S,T,U,V,W,X,Y,Z,`, 'k+I$`&l;.'+'H@G.!;{.@'#'$'H-%'&'*'='|$", +"S#-'(#^@U N*S+= ;'>','''= z#K=)'` !'O#g*~'t.{']'^'/'('_':'<'['}'|'1'2'3'4'5'6'7'8'9'2*0'0#a'b'[@c'd'e'f'v,g'h'+.S#p+1+V.", +"U ~*T Z-[#Z-<#i'F=($T+;.j'k'/#l'm'n'k.I*w+o'p'q'r's't'u'v'w'x'y'z'A'B'C'D'E'F'@&t+++s**=S@G'F$H'r;I'J'K'C@s=|;''M#L'M=~*", +"0#B@D@M'B@a'L@U&o&N'3%h O'~*{*] V-'@P'%=}*1+^@s*Q'R'S'T'U'V'V'W'X'Y'Z'`'h.=@F,/$] {@S*b.b.L.3+ )4@[,+&.)+)@)D%#)t&Y&$)}.", +"8;}#}#Z-I*4@4@R.%)-=&)*=*)i =)-);)c 7.s@I+1=..2=E#3===b.c 4=L.==..-=;=>=|+U.,=$+X.'=_ >),)/+2>, '.i*')))n*x y !)~){)]){*", +"!)S#E.^)/)/){*u+p+-=:>_.()_):)i <)[)l+J@q k=8+* U.l=X@m=n=o=5=6=~@..p+|%==7=8=9=L.0=a=N$1 ;=~*})|)c$4#1)2)!'(+N$3)s /%!)", +"4)5)6),+7)7),+,+8)9)9)0)a)>'b)c)d)2>e)E=F=1*;=G=>&<*H=_$S.I=p=:#N*2=' q=1.*@r=s=t=>=u=3.s@L.L.($F.f)g)~)h)i)j)k)k)l)@+m)", +"n)o)p)d)q)r)s)t)M#u)u)..T E.M.F#v)w)x)`= -J=K=L=M=n*8=*=N=.-F%J=K=L=M=n*8=*=N=O=P=o+Q=3.3.M*U@y)z)A)B))''.7;{+C)D)E)F)G)"}; diff --git a/app/examples/Basic/Collection/.directory b/app/examples/Basic/Collection/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/Collection/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/Collection/.icon.png b/app/examples/Basic/Collection/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..52e66249c734d935df42d897a0ea6ab79841d9c4 GIT binary patch literal 4310 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 Ef@}{+iU0rr literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Collection/.lang/ca.po b/app/examples/Basic/Collection/.lang/ca.po new file mode 100644 index 00000000..721125f0 --- /dev/null +++ b/app/examples/Basic/Collection/.lang/ca.po @@ -0,0 +1,48 @@ +# Catalan translation of Collection +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Collection package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Collection\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:14+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "per juergen@zdero.com" + +#: FStart.form:10 +msgid "Collection" +msgstr "Recoŀlecció" + +#: .project:1 +msgid "Collection example" +msgstr "Exemple de recoŀlecció " + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Crea una recoŀlecció" + +#: FStart.class:18 +msgid "Item" +msgstr "Element" + +#: FStart.class:27 +msgid "myThings" +msgstr "CosesMeves" + +#: FStart.class:31 +msgid "Name = " +msgstr "Nom = " + diff --git a/app/examples/Basic/Collection/.lang/cs.po b/app/examples/Basic/Collection/.lang/cs.po new file mode 100644 index 00000000..fd96208e --- /dev/null +++ b/app/examples/Basic/Collection/.lang/cs.po @@ -0,0 +1,40 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Collection example" +msgstr "Příklad kolekcí" + +#: FStart.class:18 +msgid "Item" +msgstr "Položka" + +#: FStart.class:27 +msgid "myThings" +msgstr "moveVěci" + +#: FStart.class:31 +msgid "Name = " +msgstr "Název =" + +#: FStart.form:10 +msgid "Collection" +msgstr "Kolekce" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Vytvoř kolekci" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "-" diff --git a/app/examples/Basic/Collection/.lang/de.po b/app/examples/Basic/Collection/.lang/de.po new file mode 100644 index 00000000..5cfb1a51 --- /dev/null +++ b/app/examples/Basic/Collection/.lang/de.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Collection example" +msgstr "Beispiel für Collection" + +#: FStart.class:18 +msgid "Item" +msgstr "Ding" + +#: FStart.class:27 +msgid "myThings" +msgstr "meine Sachen" + +#: FStart.class:31 +msgid "Name = " +msgstr "-" + +#: FStart.form:10 +msgid "Collection" +msgstr "-" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Collection erstellen" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "-" + diff --git a/app/examples/Basic/Collection/.lang/es.po b/app/examples/Basic/Collection/.lang/es.po new file mode 100644 index 00000000..f6daebb5 --- /dev/null +++ b/app/examples/Basic/Collection/.lang/es.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "por juergen@zdero.com" + +#: FStart.form:10 +msgid "Collection" +msgstr "Colección" + +#: .project:1 +msgid "Collection example" +msgstr "Ejemplo de colección" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Crear colección" + +#: FStart.class:18 +msgid "Item" +msgstr "Artículo" + +#: FStart.class:27 +msgid "myThings" +msgstr "" + +#: FStart.class:31 +msgid "Name = " +msgstr "Nombre =" + diff --git a/app/examples/Basic/Collection/.lang/nl.po b/app/examples/Basic/Collection/.lang/nl.po new file mode 100644 index 00000000..90c0cc50 --- /dev/null +++ b/app/examples/Basic/Collection/.lang/nl.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-23 01:16+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Collection example" +msgstr "Collectie voorbeeld" + +#: FStart.class:18 +msgid "Item" +msgstr "Element" + +#: FStart.class:27 +msgid "myThings" +msgstr "myThings" + +#: FStart.class:31 +msgid "Name = " +msgstr "Naam =" + +#: FStart.form:10 +msgid "Collection" +msgstr "Collectie" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Creëer collectie" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "door juergen@zdero.com" + diff --git a/app/examples/Basic/Collection/.lang/ru.po b/app/examples/Basic/Collection/.lang/ru.po new file mode 100644 index 00000000..b66dcb5a --- /dev/null +++ b/app/examples/Basic/Collection/.lang/ru.po @@ -0,0 +1,54 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Basic/Collection/.project:18 +msgid "Collection example" +msgstr "Пример коллекции" + +#: app/examples/Basic/Collection/.src/FStart.class:18 +msgid "Item" +msgstr "Элемент" + +#: app/examples/Basic/Collection/.src/FStart.class:27 +msgid "myThings" +msgstr "Мои вещи" + +#: app/examples/Basic/Collection/.src/FStart.class:31 +msgid "Name = " +msgstr "Имя = " + +#: app/examples/Basic/Collection/.src/FStart.form:5 +msgid "Collection" +msgstr "Коллекция" + +#: app/examples/Basic/Collection/.src/FStart.form:10 +msgid "Create Collection" +msgstr "Создать коллекцию" + +#: app/examples/Basic/Collection/.src/FStart.form:18 +msgid "by juergen@zdero.com" +msgstr "от juergen@zdero.com" + diff --git a/app/examples/Basic/Collection/.project b/app/examples/Basic/Collection/.project new file mode 100644 index 00000000..3f5d5a0b --- /dev/null +++ b/app/examples/Basic/Collection/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas +Title=Collection example +Startup=FStart +Icon=collection.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Collection/.src/CThing.class b/app/examples/Basic/Collection/.src/CThing.class new file mode 100644 index 00000000..4b526262 --- /dev/null +++ b/app/examples/Basic/Collection/.src/CThing.class @@ -0,0 +1,6 @@ +' Gambas class file + +Public Name As String +Public X As Integer +Public Y As Integer +Public ID As Integer diff --git a/app/examples/Basic/Collection/.src/FStart.class b/app/examples/Basic/Collection/.src/FStart.class new file mode 100644 index 00000000..66b8e5dd --- /dev/null +++ b/app/examples/Basic/Collection/.src/FStart.class @@ -0,0 +1,40 @@ +' Gambas class file + +Static Public Sub Main() + Dim myForm As Form + myForm = New FStart + myForm.Show +End + +Public myThings As Collection + +Public Sub btnCreate_Click() +'creating objects and add to collection + Dim i As Integer + Dim myThing As CThing + myThings = New Collection + For i = 1 To 7 + myThing = New CThing + myThing.Name = ("Item") & " " & CStr(i) + myThing.X = i + myThing.Y = i + myThing.ID = i + myThings.Add(myThing, CStr(myThing.ID)) + Next + +'show the collectionmembers in treeview + + tvThings.Add("root", ("myThings")) + + For Each myThing In myThings + tvThings.Add(myThing.ID, myThing.Name,, "root") + tvThings.Add(myThing.ID & "Name", ("Name = ") & myThing.Name,, CStr(myThing.ID)) + tvThings.Add(myThing.ID & "X", "X= " & myThing.X,, CStr(myThing.ID)) + tvThings.Add(myThing.ID & "Y", "Y= " & myThing.Y,, CStr(myThing.ID)) + tvThings.Add(myThing.ID & "ID", "ID= " & myThing.ID,, CStr(myThing.ID)) + Next + + tvThings["root"].Expanded = True + btnCreate.Enabled = False +End + diff --git a/app/examples/Basic/Collection/.src/FStart.form b/app/examples/Basic/Collection/.src/FStart.form new file mode 100644 index 00000000..715d33f5 --- /dev/null +++ b/app/examples/Basic/Collection/.src/FStart.form @@ -0,0 +1,20 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(33,14,61,40) + Text = ("Collection") + Icon = Picture["collection.png"] + Resizable = False + { btnCreate Button + MoveScaled(37,1,23,4) + Text = ("Create Collection") + } + { tvThings TreeView + MoveScaled(1,1,35,38) + Sorted = True + } + { Label1 Label + MoveScaled(37,35,24,4) + Text = ("by juergen@zdero.com") + } +} diff --git a/app/examples/Basic/Collection/collection.png b/app/examples/Basic/Collection/collection.png new file mode 100644 index 0000000000000000000000000000000000000000..ce524b157eba9740ff2e1f3fb51baedfe0b85c59 GIT binary patch literal 2062 zcmV+p2=VucP)w^CZR_|!#}AHeu`^X` zr>(Wc4^BI^?esB@_6-AC6>)&ESkV*<5~%~2z)eEl_kGX%oZS!S8Ys2~OWQSb=6tyO zto7S#{r6h?!ll@zzjY+OlKW}wj(vXwsxHabN3y$yXT|S5`RH9cvY8h%QhxW0l=08( zd>&$n|83w;|D_`Gxj)~t{#hgS)QBga_odunNx9pX@{tiK8}HnI5SrUQic4{E%N|nH zyYJYrysPCK>v}3WJK7a&9~|eLlV}u9=iu3Iz4gw}lbbfespJ10fRztimi+AZe%#aT ztXZ-!gbmmZNcq8~kqBf=c=-o?ss10{xf!$f^_kqOGcC~CuHD>q|GIlJH7bL7FfGts z&>bi_P%J+s2XZF3KuvdJZBD!S*3#SW9K-HE^`ZV}%z*Tj6Mz~5F$sqZh=-w4fsiGm7H(}zh|x!$9R1ban}C7;9KiRG9t^55CkZXXaApd8<$?lw!KiaF5U0MQT}Fu=AvSx(L+lmNg6 ztN>(OfZYh~E{vRos!EtQ8yXrw3B!;Hx&iO{pt%xv2G=tJ2p3zP2LO--L;}{-!*B?U z5S+0bA2AQ0M#FbwGs0n~CiN&le(FBD+#9EY!d`Z;=AF2f77DS&Y-gF2fKb#;W+u9LyZ$ z-)P~V`oPdy5?}9Dzm{0@@t><(wyk2tZQ7!yF#f3=e;u#Ij%$G)JP7$v4P-6A=bTu* zz^lwb&CrmR-2VEFInAo)HFIxy1dcdypj-2E|9s|Qz}jg%I!Pk)(U3+DhH zTtxTUo9FGWTfgy(;p=X#BbtCh28wAg3ZNIjEO1qFnAX8Djt;kwomGMA2J<&-GM#^M z+N0(002`9MeBsMynAcTAm6a?~=QDoo$g;7TQkTD1-vdnRLhYFo-n!=APi>*11#&si z^I+tIz(ziRDCVJ5WLbTR%f?b1O|?^OswSdB&IWZ15=Vx(K6-!+cl5G&-2}x*iAk$K z+RGqa!92B{LRWUd%idPDOgi~^W_E~Mz4px#H zNd+B72#`J~f{N-2IvQKKBGE}}MKixC{sCMR%?B_XDA~aW5J1#a!__??Lr_@*`2rLQ zL1`uipishd6@0tg&-^fF#1PGm&CInsNR6Jw*95UxC5;VnIvQGO4kxL0V;sr#F=0#s ziqxc!g6)852F@XIP4t7GXs5SdEG6ME(Ue?+#raQ5EdXaFqr|h+%TmFz8Ana65myj zHsGot-*2iaS(!%0T{KTcH!MtJ0tFRCC`>q$q-B<<6~+OyU!3uuZGUcH?Y31-p9m{n zpm-_EZPkX7ab9W4S0GJ*?qm4n-m0r0&%2#Sjf`3#ZROED6IG~zx}<~UnGjaX)73`> zN(Uy~J>yeG+p3nT%PVlfa;6=TDS#&fFy9AHk{WY({EghyQ}3mppGXQ>(-vl{k$26v))T5MoaPQnqpVgDsG?w8G>{n z2<%JJd7qygE9v(gDg0??$$PkH@PxfD`Y245tb^--*wA-CcEF;?L3Ojz6-DA z-jFT#zdERwmL_UeC!-;C&&h)P>PhFV?~gij-hzf{3!|V9|fsgq^?%X0~YL z`l@dxw=Q@f8m(nCKaQI#V859;?(EZ_CeQDH{)_Mf1lPMdF5=J21*U2%!Vh*lzQn4y swdM#39tJwg{{C+k0#*Z;&$NMm0Pgn9CQ=SiV*mgE07*qoM6N<$g298?_y7O^ literal 0 HcmV?d00001 diff --git a/app/examples/Basic/DragNDrop/.directory b/app/examples/Basic/DragNDrop/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/DragNDrop/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/DragNDrop/.icon.png b/app/examples/Basic/DragNDrop/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7b64ae2517d510933119ae063f03a1a0e736e9f6 GIT binary patch literal 4159 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, YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Basic/DragNDrop/.project:20 app/examples/Basic/DragNDrop/.src/FDragNDrop.form:5 +msgid "Drag & Drop" +msgstr "Бери-и-Брось" + +#: app/examples/Basic/DragNDrop/.src/FDragNDrop.class:107 +msgid "Test" +msgstr "Тест" + diff --git a/app/examples/Basic/DragNDrop/.project b/app/examples/Basic/DragNDrop/.project new file mode 100644 index 00000000..e4b30f58 --- /dev/null +++ b/app/examples/Basic/DragNDrop/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Title=Drag & Drop +Startup=FDragNDrop +Icon=drop.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/DragNDrop/.src/FDragNDrop.class b/app/examples/Basic/DragNDrop/.src/FDragNDrop.class new file mode 100644 index 00000000..2fe80767 --- /dev/null +++ b/app/examples/Basic/DragNDrop/.src/FDragNDrop.class @@ -0,0 +1,138 @@ +' Gambas class file + +Private $iKey As Integer +Private Const MIME_TYPE As String = "text/x-gambas-dragndrop-example" + +Public Sub imgIcon_MouseDrag() + + If Mouse.Left Then + Drag.Icon = Last.Picture + Last.Drag(Last.Picture.Image) + 'LAST.Drag(LAST.Tag) + Endif + +End + +Public Sub TreeView1_Drag() + + If Drag.Type <> Drag.Image Then Stop Event + +End + +Public Sub TreeView1_DragMove() + + 'IF Drag.Type <> Drag.Image THEN STOP EVENT + + With TreeView1 + If Not .FindAt(Drag.X, Drag.Y) Then + Drag.Show(TreeView1, .Item.X, .Item.Y, .Item.W, .Item.H) + Else + Drag.Show(TreeView1) + Endif + End With + +End + +Public Sub TreeView1_Drop() + + Dim sKey As String + + With TreeView1 + + If Not .FindAt(Drag.X, Drag.Y) Then + sKey = .Item.Key + Endif + + Inc $iKey + + If Drag.Type = Drag.Image Then + .Add($iKey, "#" & $iKey, Drag.Data.Picture, sKey).EnsureVisible + ' ELSE IF Drag.Type = Drag.Text THEN + ' .Add($iKey, Drag.Data,, sKey).EnsureVisible + Endif + + End With + +End + +Public Sub TreeView1_MouseDrag() + + Dim hImage As Image + + If Not Mouse.Left Then Return + + With TreeView1 + If .FindAt(Mouse.X, Mouse.Y) Then Return + If Not .Key Then Return + + hImage = New Image(32 + 8 + .Font.TextWidth(.Current.Text), 32, Color.Transparent) + Paint.Begin(hImage) + Try Paint.DrawImage(.Current.Picture.Image, 0, 0) + 'Try Draw.Picture(.Current.Picture, 0, 0) + Paint.Font = .Font + Paint.Text(.Current.Text, 34, 0, hImage.Width, 32, Align.Left) + Paint.Fill + Paint.End + + Drag.Icon = hImage.Picture + 'hImage.Save("~/drag.png") + 'Drag.Icon = .Current.Picture + .Drag(.Key, MIME_TYPE) + End With + +End + +Public Sub imgHole_Drag() + + 'DEBUG Drag.Type;; Drag.Format + If Drag.Type = Drag.Text Then + If Drag.Format = MIME_TYPE Then + Return + Endif + Endif + + Stop Event + +End + +Public Sub imgHole_Drop() + + TreeView1.Remove(Drag.Data) + +End + +Public Sub Form_Open() + + Me.Center + TreeView1.Add("Test", ("Test"), Picture["drop.png"]) + +End + + +Public Sub imgHole_DragMove() + + 'DEBUG Drag.Type;; Drag.Format + If Drag.Type = Drag.Text Then + If Drag.Format = MIME_TYPE Then + Drag.Show(imgHole) + Return + Endif + Endif + + Stop Event + +End + + +Public Sub Test_DragMove() + + Drag.Show(Last) + 'PRINT LAST.ScreenX;; LAST.ScreenY;; LAST.Window.ScreenX;; LAST.Window.ScreenY + +End + +Public Sub Form_DragMove() + + Test_DragMove + +End diff --git a/app/examples/Basic/DragNDrop/.src/FDragNDrop.form b/app/examples/Basic/DragNDrop/.src/FDragNDrop.form new file mode 100644 index 00000000..a9bd890a --- /dev/null +++ b/app/examples/Basic/DragNDrop/.src/FDragNDrop.form @@ -0,0 +1,108 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(56,27,44,56) + Text = ("Drag & Drop") + Icon = Picture["drop.png"] + Arrangement = Arrange.Horizontal + Spacing = True + Margin = True + { VBox2 VBox Test + Name = "VBox2" + MoveScaled(1,3,10,42) + Visible = False + Drop = True + Spacing = True + { Button1 Button Test + Name = "Button1" + MoveScaled(0,2,10,4) + #Translate = False + Drop = True + Text = "Button1" + } + { Panel2 Panel Test + Name = "Panel2" + MoveScaled(2,9,6,8) + Drop = True + Border = Border.Etched + } + { ScrollView1 ScrollView Test + Name = "ScrollView1" + MoveScaled(2,20,7,19) + Drop = True + { ScrollView2 ScrollView Test + Name = "ScrollView2" + MoveScaled(1,5,4,7) + Background = &HF7FFDF& + Drop = True + } + } + } + { TreeView1 TreeView + MoveScaled(14,2,20,41) + Drop = True + Expand = True + } + { VBox1 VBox + MoveScaled(36,2,6,44) + Spacing = True + { PictureBox1 PictureBox imgIcon + Name = "PictureBox1" + MoveScaled(0,0,6,6) + Tag = "Bicycle" + Border = Border.Raised + Picture = Picture["icon:/32/flag"] + Alignment = Align.Center + } + { PictureBox2 PictureBox imgIcon + Name = "PictureBox2" + MoveScaled(0,6,6,6) + Tag = "Eye" + Border = Border.Raised + Picture = Picture["icon:/32/lamp"] + Alignment = Align.Center + } + { PictureBox3 PictureBox imgIcon + Name = "PictureBox3" + MoveScaled(0,12,6,6) + Tag = "Map" + Border = Border.Raised + Picture = Picture["icon:/32/internet"] + Alignment = Align.Center + } + { PictureBox4 PictureBox imgIcon + Name = "PictureBox4" + MoveScaled(0,18,6,6) + Tag = "Happy" + Border = Border.Raised + Picture = Picture["icon:/32/bookmark"] + Alignment = Align.Center + } + { PictureBox5 PictureBox imgIcon + Name = "PictureBox5" + MoveScaled(0,24,6,6) + Tag = "Magnify" + Border = Border.Raised + Picture = Picture["icon:/32/phone"] + Alignment = Align.Center + } + { PictureBox6 PictureBox imgIcon + Name = "PictureBox6" + MoveScaled(0,30,6,6) + Tag = "Highlight" + Border = Border.Raised + Picture = Picture["icon:/32/book"] + Alignment = Align.Center + } + { Panel1 Panel + MoveScaled(1,36,4,3) + Expand = True + } + { imgHole PictureBox + MoveScaled(0,38,6,6) + Drop = True + Picture = Picture["icon:/48/trash"] + Stretch = True + } + } +} diff --git a/app/examples/Basic/DragNDrop/drop.png b/app/examples/Basic/DragNDrop/drop.png new file mode 100644 index 0000000000000000000000000000000000000000..d286286f1313350b52ac0e13470a3c73429e6e81 GIT binary patch literal 1503 zcmah|2~1OG9B-=ytpYM-9)pD$j}Bp=qC+KG83dVttl$QCtf(l_;Uu!@B2GbM8y=hD z)B!48OL2&TAm~Jn77Dgt3*~70+P;>yuk>!;>p@{q_J~_z%#!`{`~K&b@1Oti{gjj# z>qS^bz+f<5aXX^Ps0^Grb7rADnqJ(6!Qh~z_>>sbY-?+5XlM}Le%|nB&GhtiFOM@c z-2b$>sk)XnHs&x}P5lEr#sgYq6&10=_vqyv?6&-(e0mj?+1bwQXhmOmO>8#%U-ci! zVzJ)xylEB|T|iAVBg(N@?3*D}=ka)Nd0v|t85vboRsR|KU#CPS2BacDA`(l4Vn8Sq z4*?RfbIjpz*lczLvB5B6vA|}t#b`1c3?{v6+$92r?M|CYts0pjg#v{_E=M5;K@^}& z2FgU@;lbg4p=d}Asgx?EQZ=Gfj*QFzy)RcNK)C`G17Z-A^80&-g@abaqEabA1t^gM zeSLgL18EJTgTn%g)ud1=WT3SBWmnC^2Y2t43kLf@xomJ~U|_JnPrx@>4Du1V-Z(m{ zgE&3i{63!1tXGVHMzc<580~$<FLSgb{kE43v5DcR&=;_hppts^=?iVTBONr^7r=-2ngtU*=dC>YPCv@ z5~PL^I5swx-Nk|+NTY!e1j)(CArgrlolMwf)r@LnGMU|OFD)%SeE6`Bj}NP(U8B*U zEk;L-ZU~J=^Y->`XSTwK6|o`cC@mI?+wEqu+H4NQ;k1vroUZXPv)SC**3#Pcq6I~3 zGrPOfX0y3muBoZ1$;nBh(O6MYL8VfwtE(p_CiHr})oS(f@)8IHFbqqjQWe@?r4k*F zL?QtI;MB{ei)g{Sa>iOvNPcccWsWKiT^J{DofKYnOA;zmy&qy*(wO z=OBebIYjb^pMU%Ot8IaSVPQmKL`3183nfdFw-dieJso{0B{%*n|5K4Zp^SpUPP+QW z;1m1h-Wp?h-QKP9@!wq=$6t(Lul%Dj6Y~3YayKqGi%@;+=%wQ1^N0@m#^H4(p&+W} zKB06EVb4x7dFKF^yB+|xY;G>>kJlDs^@$-N_|jj*0l>hT<;9GO+HH8^mJdQgS|Vx9 z&DlyiCyT&{Pe>>Wqvl49hgV#m)U99jDmpprSWut+iCyr-E`}i4Ne!>;X9k6tQV}fj zB_-W$Jkft39B->)(6N4WI-L=m_AFP7_^n|l`PHPiFjgHkJ^%5dLHI*<>f;0K`b<*P zUL2Kja9-csGBTNb{nE#^JN7qait8Wttf-{>5}L9@i9z=hfX63=k7Oj%nf5F;VQJc? z`vonZ#gTOTb~-nhw_#FFEV%3)Qt~Ag)2Ojt3uq*bchP?9zB&~Y@_jkpYL<>vI+I=hfP@#e6UR3v_dC_Vz!Cl}<|3_D z|DpE1CAfUwq-MB&ap=vKXLUws%`=?GhD{6SCa({qgq@o*iW@R}D03JXY%usD2FC|8 zh;@1AJm%)LhcmeoswzIGY~$HYPT27D>y1s{24xY90iOqAV^<$p9FAZ0sOY%a{60mY u)9Ik>g1Z;hd3n3@N%+}$XQ#r{9319*N2F&`%xclh;>N`!Mn8(86#NbK&oQY0 literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Object/.directory b/app/examples/Basic/Object/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/Object/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/Object/.icon.png b/app/examples/Basic/Object/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..842f3355e0b50d39b8df71129ce8ee4d26c480f4 GIT binary patch literal 4189 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 literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Object/.lang/ca.po b/app/examples/Basic/Object/.lang/ca.po new file mode 100644 index 00000000..44d7c6d1 --- /dev/null +++ b/app/examples/Basic/Object/.lang/ca.po @@ -0,0 +1,43 @@ +# Catalan translation of Object +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Object package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Object\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:31+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Exemple de manipulacicó d'objectes" + +#: FStart.form:12 +msgid "Object " +msgstr "Objecte" + +#: FStart.form:17 +msgid "create the Thing !" +msgstr "crea l'objecte!" + +#: FStart.form:22 +msgid "check the Thing !" +msgstr "verifica l'objecte!" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "destrueix l'objecte!" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "per juergen@zdero.com" diff --git a/app/examples/Basic/Object/.lang/cs.po b/app/examples/Basic/Object/.lang/cs.po new file mode 100644 index 00000000..52d5e84d --- /dev/null +++ b/app/examples/Basic/Object/.lang/cs.po @@ -0,0 +1,40 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Příklad manipulace objeků" + +#: FStart.class:29 +msgid "You need to create the Thing first!" +msgstr "Potřebujete první vytvořit objekt!" + +#: FStart.form:12 +msgid "Object " +msgstr "Objekt" + +#: FStart.form:18 +msgid "create the Thing !" +msgstr "vytvořit Věc !" + +#: FStart.form:23 +msgid "check the Thing !" +msgstr "zkontroluj Věc !" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "zničit Věc !" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "-" diff --git a/app/examples/Basic/Object/.lang/de.po b/app/examples/Basic/Object/.lang/de.po new file mode 100644 index 00000000..337def3f --- /dev/null +++ b/app/examples/Basic/Object/.lang/de.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Beispiel für Objekt-Manipulation" + +#: FStart.class:29 +msgid "You need to create the Thing first!" +msgstr "Sie müssen das Objekt erst erstellen!" + +#: FStart.form:12 +msgid "Object " +msgstr "-" + +#: FStart.form:18 +msgid "create the Thing !" +msgstr "Objekt erstellen" + +#: FStart.form:23 +msgid "check the Thing !" +msgstr "Objekt prüfen" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "Objekt löschen" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "-" + diff --git a/app/examples/Basic/Object/.lang/es.po b/app/examples/Basic/Object/.lang/es.po new file mode 100644 index 00000000..ddaeb377 --- /dev/null +++ b/app/examples/Basic/Object/.lang/es.po @@ -0,0 +1,37 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "por juergen@zdero.com" + +#: FStart.form:22 +msgid "check the Thing !" +msgstr "¡ Comprobar la cosa !" + +#: FStart.form:17 +msgid "create the Thing !" +msgstr "¡ Crear la cosa !" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "¡ Destruir la cosa !" + +#: FStart.form:12 +msgid "Object " +msgstr "Objeto" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Ejemplo de manipulación de objetos" + diff --git a/app/examples/Basic/Object/.lang/nl.po b/app/examples/Basic/Object/.lang/nl.po new file mode 100644 index 00000000..41b61e99 --- /dev/null +++ b/app/examples/Basic/Object/.lang/nl.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-23 01:18+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Object manipulatie voorbeeld" + +#: FStart.class:29 +msgid "You need to create the Thing first!" +msgstr "Je dient het Ding! eerst te creëren!" + +#: FStart.form:12 +msgid "Object " +msgstr "Object " + +#: FStart.form:18 +msgid "create the Thing !" +msgstr "creëer het Ding!" + +#: FStart.form:23 +msgid "check the Thing !" +msgstr "controleer het Ding!" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "Vernietig het Ding!" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr " door juergen@zdero.com" + diff --git a/app/examples/Basic/Object/.lang/ru.po b/app/examples/Basic/Object/.lang/ru.po new file mode 100644 index 00000000..2f7ac1ac --- /dev/null +++ b/app/examples/Basic/Object/.lang/ru.po @@ -0,0 +1,58 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Basic/Object/.project:18 +msgid "Object manipulation example" +msgstr "Пример манипулирования объектом" + +#: app/examples/Basic/Object/.src/FStart.class:10 +msgid "Dummy-Thing" +msgstr "Вещь-фикция" + +#: app/examples/Basic/Object/.src/FStart.class:29 app/examples/Basic/Object/.src/FStart.class:37 +msgid "You need to create the Thing first!" +msgstr "Вы должны сначала создать вещь!" + +#: app/examples/Basic/Object/.src/FStart.form:5 +msgid "Object " +msgstr "Объект " + +#: app/examples/Basic/Object/.src/FStart.form:10 +msgid "create the Thing !" +msgstr "создать вещь!" + +#: app/examples/Basic/Object/.src/FStart.form:14 +msgid "check the Thing !" +msgstr "проверить вещь!" + +#: app/examples/Basic/Object/.src/FStart.form:22 +msgid "destroy the Thing !" +msgstr "разрушить вещь!" + +#: app/examples/Basic/Object/.src/FStart.form:26 +msgid " by juergen@zdero.com" +msgstr " от juergen@zdero.com" + diff --git a/app/examples/Basic/Object/.project b/app/examples/Basic/Object/.project new file mode 100644 index 00000000..9e18e993 --- /dev/null +++ b/app/examples/Basic/Object/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Object manipulation example +Startup=FStart +Icon=object.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Object/.src/CThing.class b/app/examples/Basic/Object/.src/CThing.class new file mode 100644 index 00000000..4b526262 --- /dev/null +++ b/app/examples/Basic/Object/.src/CThing.class @@ -0,0 +1,6 @@ +' Gambas class file + +Public Name As String +Public X As Integer +Public Y As Integer +Public ID As Integer diff --git a/app/examples/Basic/Object/.src/FStart.class b/app/examples/Basic/Object/.src/FStart.class new file mode 100644 index 00000000..4953cb40 --- /dev/null +++ b/app/examples/Basic/Object/.src/FStart.class @@ -0,0 +1,44 @@ +' Gambas class file + +Public myThing As CThing + +Public Sub btnCreateThing_Click() + + myThing = New CThing + + With mything + .Name = ("Dummy-Thing") + .X = 11 + .Y = 22 + .ID = 33 + End With + + txtCheckResult.Visible = True + +End + +Public Sub btnCheckThing_Click() + + If myThing Then + + With mything + txtCheckResult.Text = Subst("&1, X= &2, Y= &3, ID= &4", .Name, .X, .Y, .ID) + End With + + Else + Message.Warning(("You need to create the Thing first!")) + Endif + +End + +Public Sub btnDestroy_Click() + + If Not mything Then + Message.Warning(("You need to create the Thing first!")) + Return + Endif + + myThing = Null + txtCheckResult.Visible = False + +End diff --git a/app/examples/Basic/Object/.src/FStart.form b/app/examples/Basic/Object/.src/FStart.form new file mode 100644 index 00000000..c667a61a --- /dev/null +++ b/app/examples/Basic/Object/.src/FStart.form @@ -0,0 +1,28 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(35.7143,18.5714,67,17) + Text = ("Object ") + Icon = Picture["object.png"] + Resizable = False + { btnCreateThing Button + MoveScaled(1,1,21,4) + Text = ("create the Thing !") + } + { btnCheckThing Button + MoveScaled(23,1,21,4) + Text = ("check the Thing !") + } + { txtCheckResult TextBox + MoveScaled(1,7,65,4) + Visible = False + } + { btnDestroy Button + MoveScaled(45,1,21,4) + Text = ("destroy the Thing !") + } + { Label1 Label + MoveScaled(1,12,26,4) + Text = (" by juergen@zdero.com") + } +} diff --git a/app/examples/Basic/Object/object.png b/app/examples/Basic/Object/object.png new file mode 100644 index 0000000000000000000000000000000000000000..e2e3e67d7d8275e45eb1b5bc62151de5c2d8a16d GIT binary patch literal 1499 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Timer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 23:21+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +#: FTimer.class:74 +msgid "Timer example" +msgstr "Exemple de temporitzador" + +#: FOtherTimer.class:38 +msgid "Please fill in valid values!" +msgstr "Introduïu valors vàlids!" + +#: FOtherTimer.class:63 +msgid "Time is over!" +msgstr "El temps ha acabat!" + +#: FOtherTimer.class:95 +msgid "Other Example" +msgstr "Un altre exemple" + +#: FOtherTimer.class:103 +#: FTimer.class:80 +msgid "Start" +msgstr "Inicia" + +#: FOtherTimer.class:110 +msgid "3" +msgstr "-" + +#: FOtherTimer.class:117 +msgid ":" +msgstr "-" + +#: FOtherTimer.class:125 +msgid "0" +msgstr "-" + +#: FOtherTimer.class:131 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Un altre exemple de temporitzador per Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.class:81 +msgid "Trigger" +msgstr "Gatell" + +#: FTimer.class:94 +msgid "Reset" +msgstr "Restableix" + +#: FTimer.class:100 +msgid "1500" +msgstr "-" + +#: FTimer.class:106 +msgid "Start Timer in..." +msgstr "Inicia temporitzador en..." + +#: FTimer.class:113 +msgid "Start\ndelay" +msgstr "Temps\nencès" + +#: FTimer.class:120 +msgid "Stop\ndelay" +msgstr "Temps\napagat" + +#: FTimer.class:126 +msgid "200" +msgstr "-" + +#: FTimer.class:132 +msgid "800" +msgstr "-" + +#: FTimer.class:143 +msgid "Start/Stop" +msgstr "Inicia/Atura" + +#: FTimer.class:154 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Exemple de temporitzador per Juergen Zdero <juergen@zdero.com>" + +#: FTimer.class:159 +msgid "ms" +msgstr "-" + +#: FTimer.class:172 +msgid "Or start and stop Timer" +msgstr "O temporitzador d'engega i apaga" + +#~ msgid "Delay" +#~ msgstr "Retard" +#~ msgid "High-Time" +#~ msgstr "Temps en alta" +#~ msgid "Low-Time" +#~ msgstr "Temps en baixa" + diff --git a/app/examples/Basic/Timer/.lang/cs.po b/app/examples/Basic/Timer/.lang/cs.po new file mode 100644 index 00000000..0929fbc5 --- /dev/null +++ b/app/examples/Basic/Timer/.lang/cs.po @@ -0,0 +1,92 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Příklad časovače" + +#: FOtherTimer.class:16 +msgid "The counter is set to stop at 0 seconds!" +msgstr "Počítadlo je nastaveno na 0 sekund!" + +#: FOtherTimer.class:44 +msgid "Stop" +msgstr "Stop" + +#: FOtherTimer.class:64 +msgid "Time is over!" +msgstr "Čas vypršel!" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Jiné příklady" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "-" + +#: FOtherTimer.form:35 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:48 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Další příklad časovače od Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "-" + +#: FTimer.form:45 +msgid "Reset" +msgstr "-" + +#: FTimer.form:51 +msgid "1500" +msgstr "-" + +#: FTimer.form:57 +msgid "Start Timer in..." +msgstr "Start Časovače v..." + +#: FTimer.form:64 +msgid "Start\ndelay" +msgstr "Start\nčas" + +#: FTimer.form:71 +msgid "Stop\ndelay" +msgstr "Stop\nčas" + +#: FTimer.form:77 +msgid "200" +msgstr "-" + +#: FTimer.form:83 +msgid "800" +msgstr "-" + +#: FTimer.form:94 +msgid "Start/Stop" +msgstr "-" + +#: FTimer.form:102 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Příklad časovače od Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:107 +msgid "ms" +msgstr "-" + +#: FTimer.form:120 +msgid "Or start and stop Timer" +msgstr "Nebo start a konec Časovače" diff --git a/app/examples/Basic/Timer/.lang/de.po b/app/examples/Basic/Timer/.lang/de.po new file mode 100644 index 00000000..fec1f00e --- /dev/null +++ b/app/examples/Basic/Timer/.lang/de.po @@ -0,0 +1,93 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Timer-Beispiel" + +#: FOtherTimer.class:16 +msgid "The counter is set to stop at 0 seconds!" +msgstr "Der Timer soll in 0 Sekunden stoppen!" + +#: FOtherTimer.class:44 +msgid "Stop" +msgstr "-" + +#: FOtherTimer.class:64 +msgid "Time is over!" +msgstr "Zeit ist abgelaufen !" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Noch ein Beispiel" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "-" + +#: FOtherTimer.form:35 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:48 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Noch ein Timer-Beispiel von Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "Auslösen" + +#: FTimer.form:45 +msgid "Reset" +msgstr "Zurücksetzen" + +#: FTimer.form:51 +msgid "1500" +msgstr "-" + +#: FTimer.form:57 +msgid "Start Timer in..." +msgstr "Timer starten in..." + +#: FTimer.form:64 +msgid "Start\ndelay" +msgstr "Start\nnach" + +#: FTimer.form:71 +msgid "Stop\ndelay" +msgstr "Stop\nnach" + +#: FTimer.form:77 +msgid "200" +msgstr "-" + +#: FTimer.form:83 +msgid "800" +msgstr "-" + +#: FTimer.form:94 +msgid "Start/Stop" +msgstr "-" + +#: FTimer.form:102 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Timer-Beispiel von Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:107 +msgid "ms" +msgstr "-" + +#: FTimer.form:120 +msgid "Or start and stop Timer" +msgstr "Oder Timer starten und stoppen" + diff --git a/app/examples/Basic/Timer/.lang/es.po b/app/examples/Basic/Timer/.lang/es.po new file mode 100644 index 00000000..376ac855 --- /dev/null +++ b/app/examples/Basic/Timer/.lang/es.po @@ -0,0 +1,96 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Ejemplo de Temporizador" + +#: FOtherTimer.class:38 +msgid "Please fill in valid values!" +msgstr "Ingrese valores validos!" + +#: FOtherTimer.class:63 +msgid "Time is over!" +msgstr "El tiempo ha terminado" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Otro ejemplo" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "Iniciar" + +#: FOtherTimer.form:29 +msgid "3" +msgstr "-" + +#: FOtherTimer.form:36 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:44 +msgid "0" +msgstr "-" + +#: FOtherTimer.form:50 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Otro ejemplo de Temporizador por Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "Disparador" + +#: FTimer.form:48 +msgid "Reset" +msgstr "Reiniciar" + +#: FTimer.form:54 +msgid "1500" +msgstr "-" + +#: FTimer.form:60 +msgid "Start Timer in..." +msgstr "Iniciar temporizador en..." + +#: FTimer.form:67 +msgid "Start\ndelay" +msgstr "Retardar\ninicio" + +#: FTimer.form:74 +msgid "Stop\ndelay" +msgstr "Retardar\nparada" + +#: FTimer.form:80 +msgid "200" +msgstr "-" + +#: FTimer.form:86 +msgid "800" +msgstr "-" + +#: FTimer.form:97 +msgid "Start/Stop" +msgstr "Iniciar/Parar" + +#: FTimer.form:108 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Ejemplo de Temporizador por Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:113 +msgid "ms" +msgstr "-" + +#: FTimer.form:126 +msgid "Or start and stop Timer" +msgstr "O iniciar y detener el Temporizador" diff --git a/app/examples/Basic/Timer/.lang/nl.po b/app/examples/Basic/Timer/.lang/nl.po new file mode 100644 index 00000000..2e27afe9 --- /dev/null +++ b/app/examples/Basic/Timer/.lang/nl.po @@ -0,0 +1,92 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-23 01:22+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Timer voorbeeld" + +#: FOtherTimer.class:16 +msgid "The counter is set to stop at 0 seconds!" +msgstr "The teller is ingestelt om bij 0 seconden te stoppen!" + +#: FOtherTimer.class:44 +msgid "Stop" +msgstr "Stop" + +#: FOtherTimer.class:64 +msgid "Time is over!" +msgstr "Tijd is voorbij!" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Ander Voorbeeld" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "Start" + +#: FOtherTimer.form:35 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:48 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Een ander timer voorbeeld door Maxim Lapsis <maxil_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "-" + +#: FTimer.form:45 +msgid "Reset" +msgstr "" + +#: FTimer.form:51 +msgid "1500" +msgstr "-" + +#: FTimer.form:57 +msgid "Start Timer in..." +msgstr "Start Timer in..." + +#: FTimer.form:64 +msgid "Start\ndelay" +msgstr "Start\nuitstel" + +#: FTimer.form:71 +msgid "Stop\ndelay" +msgstr "Stop\nuitstel" + +#: FTimer.form:77 +msgid "200" +msgstr "-" + +#: FTimer.form:83 +msgid "800" +msgstr "-" + +#: FTimer.form:94 +msgid "Start/Stop" +msgstr "Start/Stop" + +#: FTimer.form:102 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Timer voorbeeld door Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:107 +msgid "ms" +msgstr "-" + +#: FTimer.form:120 +msgid "Or start and stop Timer" +msgstr "Of start en stop Timer" diff --git a/app/examples/Basic/Timer/.lang/ru.po b/app/examples/Basic/Timer/.lang/ru.po new file mode 100644 index 00000000..bf56ec24 --- /dev/null +++ b/app/examples/Basic/Timer/.lang/ru.po @@ -0,0 +1,114 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Basic/Timer/.project:20 app/examples/Basic/Timer/.src/FTimer.form:6 +msgid "Timer example" +msgstr "Пример таймера" + +#: app/examples/Basic/Timer/.src/FOtherTimer.class:16 +msgid "The counter is set to stop at 0 seconds!" +msgstr "Счётчик остановить установлен на 0 секунд!" + +#: app/examples/Basic/Timer/.src/FOtherTimer.class:44 +msgid "Stop" +msgstr "Стоп" + +#: app/examples/Basic/Timer/.src/FOtherTimer.class:49 app/examples/Basic/Timer/.src/FOtherTimer.class:62 app/examples/Basic/Timer/.src/FOtherTimer.form:13 app/examples/Basic/Timer/.src/FTimer.form:12 +msgid "Start" +msgstr "Начать" + +#: app/examples/Basic/Timer/.src/FOtherTimer.class:64 +msgid "Time is over!" +msgstr "Время вышло!" + +#: app/examples/Basic/Timer/.src/FOtherTimer.form:6 +msgid "Other Example" +msgstr "Другой пример" + +#: app/examples/Basic/Timer/.src/FOtherTimer.form:24 +msgid ":" +msgstr ":" + +#: app/examples/Basic/Timer/.src/FOtherTimer.form:35 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Ещё один пример таймера от Максима Ляписа <maxim_lapis@web.de>" + +#: app/examples/Basic/Timer/.src/FTimer.form:13 +msgid "Trigger" +msgstr "Триггер" + +#: app/examples/Basic/Timer/.src/FTimer.form:21 app/examples/Basic/Timer/.src/FTimer.form:22 +msgid "Reset" +msgstr "Сброс" + +#: app/examples/Basic/Timer/.src/FTimer.form:26 +msgid "1500" +msgstr "1500" + +#: app/examples/Basic/Timer/.src/FTimer.form:31 +msgid "Start Timer in..." +msgstr "Запустить таймер в..." + +#: app/examples/Basic/Timer/.src/FTimer.form:38 +msgid "" +"Start\n" +"delay" +msgstr "" +"Задержка\n" +"запуска" + +#: app/examples/Basic/Timer/.src/FTimer.form:44 +msgid "" +"Stop\n" +"delay" +msgstr "" +"Задержка\n" +"остановки" + +#: app/examples/Basic/Timer/.src/FTimer.form:49 +msgid "200" +msgstr "200" + +#: app/examples/Basic/Timer/.src/FTimer.form:54 +msgid "800" +msgstr "800" + +#: app/examples/Basic/Timer/.src/FTimer.form:63 +msgid "Start/Stop" +msgstr "Начать/Остановить" + +#: app/examples/Basic/Timer/.src/FTimer.form:70 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Пример таймера от Юрген Здеро <juergen@zdero.com>" + +#: app/examples/Basic/Timer/.src/FTimer.form:74 app/examples/Basic/Timer/.src/FTimer.form:88 app/examples/Basic/Timer/.src/FTimer.form:92 +msgid "ms" +msgstr "мс" + +#: app/examples/Basic/Timer/.src/FTimer.form:84 +msgid "Or start and stop Timer" +msgstr "Или запустить и остановить таймер" + diff --git a/app/examples/Basic/Timer/.project b/app/examples/Basic/Timer/.project new file mode 100644 index 00000000..8be0e980 --- /dev/null +++ b/app/examples/Basic/Timer/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.10.90 +Title=Timer example +Startup=FTimer +Icon=timer.png +Version=3.10.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Authors="Maxim Lapis" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Timer/.src/FOtherTimer.class b/app/examples/Basic/Timer/.src/FOtherTimer.class new file mode 100644 index 00000000..984d437b --- /dev/null +++ b/app/examples/Basic/Timer/.src/FOtherTimer.class @@ -0,0 +1,85 @@ +' Gambas class file + +'&HC00F47& = red +'&H4BC021& = green + +Private $iCounter As Integer + +Public Sub Button1_Click() + + Dim iSeconds, iMinutes As Integer + + iSeconds = vbSeconds.Value + iMinutes = vbMinutes.Value + + If iSeconds = 0 And iMinutes = 0 Then + Message.Info(("The counter is set to stop at 0 seconds!")) + Return + Endif + + If Not Timer1.enabled Then + + 'Normalize minutes and seconds + If iSeconds >= 60 Then + + While iSeconds > 59 + + Inc iMinutes + iSeconds = iSeconds - 60 + + Wend + + Endif + + 'Update normalized values + vbSeconds.Value = iSeconds + vbMinutes.Value = iMinutes + + 'Store the total count to simplify code + $iCounter = iSeconds + iMinutes * 60 + + 'Let the timer start! + Timer1.enabled = True + + Button1.Text = ("Stop") + Button1.Background = &HC00F47& + + Else + + Button1.Text = ("Start") + Button1.Background = &H4BC021& + Timer1.enabled = False + Endif + + 'thank you Benoit, that is just a great thing +End + +Public Sub Timer1_Timer() + + 'The counter has reached 0. Stop it and update the window + If $iCounter = 0 Then + Timer1.Enabled = False + Button1.Text = ("Start") + Button1.Background = &H4BC021& + Message.Info(("Time is over!")) + Else + + 'Seconds are over, decrease the minute count by 1 and reset the seconds count + If vbSeconds.Value = 0 Then + + Dec vbMinutes.Value + + vbSeconds.Value = 59 + + Else + + Dec vbSeconds.Value + + Endif + + Endif + + 'Also decrease the global counter + Dec $iCounter + +End diff --git a/app/examples/Basic/Timer/.src/FOtherTimer.form b/app/examples/Basic/Timer/.src/FOtherTimer.form new file mode 100644 index 00000000..ea90e61b --- /dev/null +++ b/app/examples/Basic/Timer/.src/FOtherTimer.form @@ -0,0 +1,40 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(52.7143,62.4286,32,18) + Background = &HB1C00E& + Text = ("Other Example") + Icon = Picture["timer.png"] + Resizable = False + { Button1 Button + MoveScaled(17,11,11,5) + Font = Font["14"] + Background = &H4BC021& + Text = ("Start") + } + { vbSeconds ValueBox + MoveScaled(10,11,6,5) + Font = Font["18"] + Background = &H93B8B0& + Value = "3" + } + { TextLabel2 TextLabel + MoveScaled(7,11,3,4) + Font = Font["Adobe Courier,18,Bold"] + Text = (":") + Alignment = Align.Center + } + { vbMinutes ValueBox + MoveScaled(1,11,6,5) + Font = Font["18"] + Background = &H93B8B0& + Value = "0" + } + { TextLabel1 TextLabel + MoveScaled(1,1,29,9) + Text = ("Another timer example by Maxim Lapis <maxim_lapis@web.de>") + } + { Timer1 #Timer + #MoveScaled(23,2) + } +} diff --git a/app/examples/Basic/Timer/.src/FTimer.class b/app/examples/Basic/Timer/.src/FTimer.class new file mode 100644 index 00000000..9400ca76 --- /dev/null +++ b/app/examples/Basic/Timer/.src/FTimer.class @@ -0,0 +1,56 @@ +' Gambas class file + +Public Sub Form_Open() + + FOtherTimer.Show + +End + +Public Sub Button1_Click() + + Timer1.Delay = CInt(TextBox1.Text) + Timer1.Enabled = True + +End + +Public Sub Timer1_Timer() + + Label1.Background = &HFF0000& + +End + +Public Sub Button2_Click() + + Timer1.Enabled = False 'try without this line + Label1.Background = &HDCDCDC& + +End + +Public Sub ToggleButton1_Click() + + If ToggleButton1.Value = True Then + Timer2.Delay = CInt(TextBox2.Text) 'low + Timer3.Delay = CInt(TextBox3.Text) 'high + Timer2.Enabled = True + Else + Timer2.Enabled = False + Timer3.Enabled = False + Endif + +End + +Public Sub Timer2_Timer() + + Label5.Background = &HFF0000& + Timer3.Enabled = True + Timer2.Enabled = False + +End + +Public Sub Timer3_Timer() + + Label5.Background = &H00FF00& + Timer2.Enabled = True + Timer3.Enabled = False + +End diff --git a/app/examples/Basic/Timer/.src/FTimer.form b/app/examples/Basic/Timer/.src/FTimer.form new file mode 100644 index 00000000..1f4cc961 --- /dev/null +++ b/app/examples/Basic/Timer/.src/FTimer.form @@ -0,0 +1,100 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(33.5714,24,57,40) + Expand = True + Text = ("Timer example") + Icon = Picture["timer.png"] + Resizable = False + Spacing = True + { Button1 Button + MoveScaled(35,14,20,4) + ToolTip = ("Start") + Text = ("Trigger") + } + { Label1 Label + MoveScaled(30,14,3.5714,4) + Border = Border.Etched + } + { Button2 Button + MoveScaled(14,14,14,4) + ToolTip = ("Reset") + Text = ("Reset") + } + { TextBox1 TextBox + MoveScaled(2,14,8,4) + Text = ("1500") + Alignment = Align.Right + } + { Label2 Label + MoveScaled(2,10,53,3) + Text = ("Start Timer in...") + Alignment = Align.Left + } + { Label3 Label + MoveScaled(2,24,11,8) + Foreground = &H00AA00& + Expand = True + Text = ("Start\ndelay") + Alignment = Align.Left + } + { Label4 Label + MoveScaled(15,24,13,8) + Foreground = &HFF0000& + Text = ("Stop\ndelay") + Alignment = Align.Left + } + { TextBox2 TextBox + MoveScaled(2,33,8,4) + Text = ("200") + Alignment = Align.Right + } + { TextBox3 TextBox + MoveScaled(14,33,8,4) + Text = ("800") + Alignment = Align.Right + } + { Label5 Label + MoveScaled(29,33,3.5714,4) + Border = Border.Etched + } + { ToggleButton1 ToggleButton + MoveScaled(34,33,21,4) + Text = ("Start/Stop") + } + { Timer2 #Timer + #MoveScaled(36,1) + } + { TextLabel1 TextLabel + MoveScaled(2,1,33,6) + Text = ("Timer example by Juergen Zdero <juergen@zdero.com>") + } + { Label6 Label + MoveScaled(10,14,4,4) + Text = ("ms") + } + { Separator1 Separator + MoveScaled(2,9,53,1) + } + { Separator2 Separator + MoveScaled(2,18,53,3) + } + { Label7 Label + MoveScaled(2,21,53,3) + Text = ("Or start and stop Timer") + } + { Label8 Label + MoveScaled(10,33,4,4) + Text = ("ms") + } + { Label9 Label + MoveScaled(22,33,4,4) + Text = ("ms") + } + { Timer1 #Timer + #MoveScaled(41,1) + } + { Timer3 #Timer + #MoveScaled(45,1) + } +} diff --git a/app/examples/Basic/Timer/timer.png b/app/examples/Basic/Timer/timer.png new file mode 100644 index 0000000000000000000000000000000000000000..6708fdd27788c95fe589ee232dff093831b8041a GIT binary patch literal 2778 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/.directory b/app/examples/Control/ArrayOfControls/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/ArrayOfControls/.icon.png b/app/examples/Control/ArrayOfControls/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cc422dcf5e94a28456f006d4ef0f5368a1fc4a81 GIT binary patch literal 4209 zcmV-%5RUJOP)?A-6lHeF~8f+{f8{hJw`#vmLdh_1AH{JWk(~WE_+nCtjsxH0vrh8_- z-~L_QGZI{2$Y1v&Mr+Up_xKTdx88kL=aU~ap{bzYj;#QshkeX0PDKWC&-$F!2Tkg9 zQ1t1oXf04e@Wq=7`S!Xp>e?Ju-_wtsw}N=b4gm6Q{WAy)?Zt^4_!W`bUtib)lzi?L zv;m`G?d7>__)Hm|<>&DlD9a3!9eH-hp&g0v+&0*USi!~3zK$l0t_yG+Dd{`w#O70pQ28%AK-w4$^NK@!?_-55Di8B&3%tDt z+%Yrmc~E<>lfws^SXdn5q4kR}R*2rP^-SRNQ{K-&m^&U@aknC!=Y-_&L1=AW4W z!vY@e6r70_y&upT*Y$Yj!8>z4Iq}%ZEkK{S008AVXE**F!*{4%-ZI&N$p!F|{&z7! z7$z+YCKmvMca|3L_lA>&w08?|-+eL`*Yr7mCk}j$5OIO~jO0Esj@UQ>RzKXyi+|ZF z>eKp&I2V@>bPY(@`?3YNV}BQ`AK3j+TFAN`ggv5)l9_^w%LM@9p6MO^X5#&k#3u3I ze`@YqSQ1uaU0xf)AN0+>ACQ8B0b7ZSQh>N;`p16P+A~(% z6Oo{$#t{}`YA3!>ASY}9#^O3+DJdN#W%yJbFBYhY-=%acrBp;IHQ~SqeXH*iCRnyo zI%3JB1&li&#ktD>+DlM>`G=;UO{_(4P7kQjfl$m#B2A^ zCo>tyO(PIgD51{!{AU6)A-XHeb1X}!hgo;>7$>>B2z?@wkOt?|nj@M(Clz=hV*W5&3%V93dvZ8J_8!JMH0ySF?_+uh=db7x6I?(l=~zk%XQBs~GYA1s|JSGZ#uwJ| z>0ds|=HLCAAO3zT?u-glAWW_e10unj+n>Q|-i=+HPdGD(rQ~@{xf2UKC52;IN;*o7 zKcc4?0wVog{OT9?v%YXTzq#x4Y<}W#N@iAa&%;mC)*m5~okwW#_2ga_8oCpSIaIo4I>$EC2V%!&E=_JfdVSkL_&WSYH$=MuU$r z2Dhz|`9<@YRy6zkP5tB+Ae1!yma=4r6bLb<`Z1HlfH!Miq9nDSK3_J2fgG}hWZ}$w zc6Ue71|E8B6Q8>IBLuGfEY*h^Y1!RKOoJ6l!5ch*P!_TF2Bt?7R4ls!HHF}x793=L zaTk6oku2wh95Y>-WPaBpg^nAy+0&R!zY{vC0LCDcB|9uznNB6;*bE``gkbT)OTXpz z8<+B@gNk0qLkpLnFi12`Jb_LmP_viwv-`e-T`&VNb20vcGIZ3zee=(x&RL4UklNnD zRcr1>PQme&h*D8fgRv4s$Iag<2H8+i(m>7OOYqF-Gs3Z!ZVbXkcUn?g?b6ax3WhfU zg&uQ%J6_zx9oNicSJY3pu^DXKOWJ}f80_z5z)g@9O2bkTAq3MeyOBqpx*xatX^sht z9KYg6SKLXH>yqEu%Jm=p+UZaG5d5l%o_Gtyr#uv&)r{-S9BTn10%(Jj39H>Qe$lQ3 zVmL(@gBYHmy4o5_i!I(b(##&B*x@u1{oQy2eI)Dv+YU7_ceYK?XEPWXq^G5k|IM7m zFZOO@nSf1cIdm9{Y4x?-aL2t_85!k>>Xt8}oX{nZ1-^#L|T} zBJLOM)`kz;(FeCg-C@$k7@;L3+!(1ThB-^GW8;=>xP2W+Ws!a5T6(KrB7NQpc61wx z+ZwUsDfGUzg=sf@lI@p&giBxEM2YKg5Xr48)-tW6@{Gs>48{i;jCBv)9DHTr-IS)4 z^7CCkqjj)lMGs=7X3}@yZR}u()Y1j`GpF&T#n)i_ zPwz#Alg^@c=^YXvY=7jj924A>6eA`e!GWRY0!rctNf1B} zxqu)+Tk}ymeF2;So79SBB>H>kKlC<6C&()-Aw1B_3EM}{2?+ZXr9l@cI|K|+C_$x_~X5FM|;Q)Wbty_t8~PBCQQEp1g?Qzt*Ous1>;-*F&y8`I02N1 zj+lljUa@;Ur!TJhR15(a zkxVKwDF6{AlX+4NspOLi6PiZRtP0APTu$1em831cisLn}AXBqQ)rQiXLYQ7gU|JzD z*QKfF1P2FTj|r0qxwIawMhecVDFJ@Kk3j)X)<4JVomKo`(LKz|T*QC|EdyMgcO?di zVz_^DcE_DdB1U*BA|n=HNt}@YB0y^SL+V2 z(1O-bIti03HI*h|@tIPDm~RxtLmQY8f#@_|tokV-^{L#Q!kBvmY0xPsS6 zux{mio@;ig-MI}rFpq|Jw$anu!7<&3)7Xm2%17EZ!{<;^P}9`KlFFq#^Ym}HG~Ppf z%}#Fl$Y)8*E|`)477&`SM`=8Vdv^SQFVFrAx6inRfR!wEvi<1@6C*g(-!kKuz{6{R zAw)+lK;R7TffzHYQfhhz3ku43;_=@yEjNYCoEh{FMtSyco0xvxZOEA`SU#tnnqw`P zKsjKBE-3I>id{`z*z>Mru&ar0-utio@!97n?JK8z*22>kz?Kl0tm-py9y#CfQxj_pL;-jV*cG~??4VZ?Au5MwRCQX-;9EI^2{>*mtg^H^g^wl&u?%Zl(? z?J=^iScCS3IC``m5lo@4;V}Ne0m8EvgGsu95CU6C6dpBo?d1S->Yo`}nZO?Exq$Mg))~V}Jy(B+=pEFdTTsw&L<9rautRNVZb+gO&CN^I|TjMj*ueSe{B+&$Y^R8YtYANbo^ z5OKwUeLHan``>k)$y%}@VeE9m0!JPS&@2leVgYVFx0tAa^V=(yV zt>8p-kW49AIcFZz%jQyFy9Z^d)7lTr&Marvp-<3ezl5EVn6O_xu?7GsB=Hl9{Mah8 zE6Y!A0YZ$rfGu%`bEnj(Q!UnJ%vnHs)oVm^i!iBK)C$S|#~$SKYww_P!Ey?V%c!b( zgDtN;OQ>uC{_K4EQ#09b3`gqfs5rEn>(<{M>r|Ej;r3N3rK!Mxy^XxjsMfxQh^i zw2W+4UAvY`magRJ(fw5IdXt9Ec3iEQo}Ej{HER%4eEe+t1LPK!Pif3a#2MixrHLfv z0-islzjeYD_X0SvL{`Gnr}wJ$1qMsWSkQ0v0HpfNPi~qcAG83p(SuH0VT=)xsHe2i z?`>%p8U#pTlo3)0AxzlTgD~U*eLcNL5_X0!5=+Qv)K$h9E=C1}5X!^&Lw-y5^&CGk zqzeMb`PHk9ulQ23W_o7$WvPo#001RS+#TpS^4Y74uLU|s1OV2MTtI~YAMbvl;zEZ< za${`}I0*b@SO7>ao(h54Kmm{m1i8Tet?m@iB-36m&8rMt5z+?Qi9yfAh%vsI_cPR^0t!Gv<;zF zv$w(GKc8-9)3z>pI@{@~dYJybPrYx%hf!KoOkH~imM;T;N*+3XoZQUh(mdd^PD&om zp~+9X=}|)YGmw_TX|AU2ZyV6D6Yo3uVXVA!BU+{7^93PV%ffP(X@#Y1`0AQdjyq@X ztF=G>h4V&sGUVl?;J0FUUX+cGe35leZY3w@?Eks?qR0OOv=&3@\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Matriu de controls" + +#: FMain.class:70 +msgid "Clear" +msgstr "Neteja" + +#: FMain.class:112 +msgid "Dial" +msgstr "Marcador" + +#: FMain.class:80 +msgid "Dialing " +msgstr "Marcant" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Exemple de com fer servir una matriu de controls" + +#: FMain.class:8 +msgid "Quit" +msgstr "Surt" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "No hi ha cap número que pugui marcar." + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Heu fet clic al botó &1" + diff --git a/app/examples/Control/ArrayOfControls/.lang/cs.po b/app/examples/Control/ArrayOfControls/.lang/cs.po new file mode 100644 index 00000000..8d01fbca --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.lang/cs.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Řízení pole" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Příklad jak použít řízení pole" + +#: FMain.class:8 +msgid "Quit" +msgstr "Ukončit" + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Kliknul si na tlačítko &1" + +#: FMain.class:70 +msgid "Clear" +msgstr "Vyčsti" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "Toto není číslo, nelze volat." + +#: FMain.class:80 +msgid "Dialing " +msgstr "Vytáčení " + +#: FMain.form:25 +msgid "Dial" +msgstr "Vytočit" diff --git a/app/examples/Control/ArrayOfControls/.lang/de.po b/app/examples/Control/ArrayOfControls/.lang/de.po new file mode 100644 index 00000000..9ce0bab0 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.lang/de.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Steuerelement-Array" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Beispiel, wie man einen Array von Steuerelementen benutzt" + +#: FMain.class:8 +msgid "Quit" +msgstr "Beenden" + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Du hast die Taste &1 angeklickt" + +#: FMain.class:70 +msgid "Clear" +msgstr "Löschen" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "Es gibt keine Nummer zum Wählen." + +#: FMain.class:80 +msgid "Dialing " +msgstr "Wähle " + +#: FMain.form:25 +msgid "Dial" +msgstr "Wählen" diff --git a/app/examples/Control/ArrayOfControls/.lang/nl.po b/app/examples/Control/ArrayOfControls/.lang/nl.po new file mode 100644 index 00000000..d2b10fc5 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.lang/nl.po @@ -0,0 +1,43 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: ArrayOfControls 3.5.90\n" +"PO-Revision-Date: 2014-10-02 15:13 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Reeks van controles" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Voorbeeld in hoe een 'Reeks van controles' te gebruiken" + +#: FMain.form:25 +msgid "Dial" +msgstr "Draai" + +#: FMain.class:8 +msgid "Quit" +msgstr "Afsluiten" + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Je hebt op de knop &1 geklikt" + +#: FMain.class:70 +msgid "Clear" +msgstr "Opschonen" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "Er is geen nummer wat ik kan draaien" + +#: FMain.class:80 +msgid "Dialing " +msgstr "Nummer kiezen" + diff --git a/app/examples/Control/ArrayOfControls/.lang/ru.po b/app/examples/Control/ArrayOfControls/.lang/ru.po new file mode 100644 index 00000000..2328b02d --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.lang/ru.po @@ -0,0 +1,58 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Control/ArrayOfControls/.project:19 +msgid "Array of Controls" +msgstr "Массив элементов управления" + +#: app/examples/Control/ArrayOfControls/.project:20 +msgid "Example how to use an array of controls" +msgstr "Пример использования массива элементов управления" + +#: app/examples/Control/ArrayOfControls/.src/FMain.class:8 app/examples/Control/ArrayOfControls/.src/FMain.class:68 +msgid "Quit" +msgstr "Выход" + +#: app/examples/Control/ArrayOfControls/.src/FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Вы нажали кнопку &1" + +#: app/examples/Control/ArrayOfControls/.src/FMain.class:70 +msgid "Clear" +msgstr "Очистить" + +#: app/examples/Control/ArrayOfControls/.src/FMain.class:78 +msgid "There is no number I could dial." +msgstr "Нет номера для набора." + +#: app/examples/Control/ArrayOfControls/.src/FMain.class:80 +msgid "Dialing " +msgstr "Набор номера " + +#: app/examples/Control/ArrayOfControls/.src/FMain.form:18 +msgid "Dial" +msgstr "Набрать" + diff --git a/app/examples/Control/ArrayOfControls/.project b/app/examples/Control/ArrayOfControls/.project new file mode 100644 index 00000000..ac6590b4 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Array of Controls +Startup=FMain +Icon=phone.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Example how to use an array of controls" +Authors="Matti (math.eber@t-online.de)" +TabSize=2 +Translate=1 +Language=de +ControlPublic=1 +SourcePath=/home/mathias/Basic +Packager=1 diff --git a/app/examples/Control/ArrayOfControls/.src/FMain.class b/app/examples/Control/ArrayOfControls/.src/FMain.class new file mode 100644 index 00000000..3f90faa8 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.src/FMain.class @@ -0,0 +1,83 @@ +' Gambas class file + +Private aButtons[13] As Button ' Dim the array of controls + +Public Sub Form_Open() + Dim i As Integer + + btnClose.Tooltip = ("Quit") + Me.Center + For i = 1 To 12 + aButtons[i] = New Button(Me) As "Buttongroup" ' Create a new button in the array and store it in an action group + With aButtons[i] + .X = (i - Int((i - 1) / 3) * 3) * 60 - 30 + .Y = Int((i - 1) / 3) * 60 + 170 + .Width = 42 + .Height = 42 + .Font.Grade = 5 + If i = 10 Then + .Text = "*" + Else If i = 11 Then + .Text = "0" + Else If i = 12 Then + .Text = "#" + Else + .Text = i + Endif + End With + Next + +End + + +Public Sub Buttongroup_Click() ' This is an event of the action group. This event is fired if any of the buttons is clicked + ' You see a list of available events if you type "Buttongroup_" +' Dim w As Integer + + tbNumber.Text = tbNumber.Text & Last.Text ' find out with LAST which button was clicked and hand its text over to TextBox + + ' w = Last.Width ' this would return the width of the last button + ' Print w +End + +Public Sub Buttongroup_Menu() ' Another event of the action group: Right-click on any of the buttons + + Message.Info(Subst(("You have clicked the button &1"), Last.Text)) + +End + +Public Sub btnClose_Click() + + If tbNumber.Text = "" Then + Me.Close + Else + tbNumber.Text = "" + Endif + +End + +Public Sub Form_KeyPress() + + If Key.code = Key.Esc Then Me.Close + +End + +Public Sub tbNumber_Change() + + If tbNumber.Text = "" Then + btnClose.Tooltip = ("Quit") + Else + btnClose.Tooltip = ("Clear") + Endif + +End + +Public Sub btnDial_Click() + + If tbNumber.Text = "" Then + Message.Warning(("There is no number I could dial.")) + Else + Message.Info(("Dialing ") & tbNumber.Text) + Endif + +End diff --git a/app/examples/Control/ArrayOfControls/.src/FMain.form b/app/examples/Control/ArrayOfControls/.src/FMain.form new file mode 100644 index 00000000..a4d87f51 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.src/FMain.form @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,32,61) + Background = &H525252& + Icon = Picture["Phone,.png"] + Resizable = False + Utility = True + { tbNumber TextBox + MoveScaled(3,5,26,6) + Font = Font["Bold,+4"] + Background = &HBBBFB7& + Alignment = Align.Right + ReadOnly = True + } + { btnDial Button + MoveScaled(3,15,12,6) + ToolTip = ("Dial") + Picture = Picture["green.png"] + } + { btnClose Button + MoveScaled(17,15,12,6) + Picture = Picture["red.png"] + } +} diff --git a/app/examples/Control/ArrayOfControls/green.png b/app/examples/Control/ArrayOfControls/green.png new file mode 100644 index 0000000000000000000000000000000000000000..930782f8a6a9d45d5f7703e9bd31a7cb1bde4341 GIT binary patch literal 873 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0VAK!r32_B-8R{7r)`8Im28LA( z4C@&ffD9msjDR8=85q{UlmVrHT!;dQ;IAt7$OemBAX9q z17#s@fT#!BvKnR)kOAZ(%L3H^&ELtuFcGF^F_;0g4z3JhGQ@)rHqZ))OCVf`+kgtT zfyIGRKsLl=h)STZVHyo%@>qbLN+}8Q3ua(auGqxvaO}J6;eSv1os@zep9uT*GWv>) znS{rmPtA;bu6YYQ`@2JI-n-imOD?}S{YL1*7CV3QpRp#DJZB*Ed{Z8_YK>NIKf}{qOvPZ`2wEK0f((GVPi5?%+-x&PB($_13$KoY0(a ze_-yUBle8@*WZ`tVJxy(q;YY@B9}u(0$jKLZnRjSR`s7#wBvZrKb0*Z_cuFvIUA*Y zUT%DDfzeAn!5;n(#Y*QMe`sBhnvwBCf1%=$qL0j)r=GdF9iQXE_2KnHmIYiU>z>;g zzGPq#Wo&dg!Ffvj%Navc+spncDIHH7XJnM+?K{D>i|Z=q@7G7c

z=MHo6Vej zy?puSWNC@(6N}}ezHcm#m-&9RI6m%I`QLg*+raF3+@4E6fbxu|tDnm{r-UW|-zYu- literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/green1.png b/app/examples/Control/ArrayOfControls/green1.png new file mode 100644 index 0000000000000000000000000000000000000000..3008e150da0d1f25b311fe7740bbdd0f2f46c0be GIT binary patch literal 1548 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 KMNUMnLSTY~xpcY! literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/red.png b/app/examples/Control/ArrayOfControls/red.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5c61a361c0c7b1c17c1f926e96600fed650da GIT binary patch literal 1452 zcmV;d1ylNoP)0~Q;-*1~xMXUgPMx>~7sPDF&B&BQe3=BkNE{!g&KP52 zbWt}Yl5G*mlr1LUOQLge%g8dfsTs@EEx>b5dHNJOUJ8tt(muBz?t^rtEv@^1JLg>Y z{lDI?>jV?PUfz`P_Kce&%LAS03=BnDO0j*Bqk*Y`?{%?lB6o-JJ~nv6IX>Ft_P~t@ zfo3E_Z(vH~AQs^aT5(x<{1Cp2pHvx|lmd~K1I%x-9ZPXJ2C+NxS3FapHpJPCwhHwD z-k&iT`7l;T-ck~rj(i6{ta5rHUQw_n=dprV&|l*(HQw7~YVQS?UxXgAIvsVcEcjf` zi#ajpRL+m~Cr^!y9@@IOHs^Sr$m=<4dbn~znh(Qa+UmTk;OjYubIugpbMiunMzwrZ z&hDI8yRd6D2DW)!?o3O$>%Y%)aG5ell^ytClO+6d)y zHszcsIGwX$G)?a=$)D?H#YBk@GQGx%8V?mbTjNYlf6lQQy9;i4k!&A}bADeWjz)vU zlvQw!mInQ}EU=-StyggJQsmuyqRAKVLChLOWGL`_Lq;NAG;8ScoShdZR? zc)G%`FGZfSyvBV^euGtGki*%s9JklFzKeZLeue?8Xk$qzFYFD>#^<|u;1c9t4y=z{ zb-szjF{9&TWNx4hf5aCDsI;;h3-Gn9;1-GeK!q2^BJSbpf^XtpT;&4!N&E*p(JO_r zJ8~FvGp2>qJ62}A%^=jwC)ssB`%_et~7-g2l%pzY9DvkDU$X zMwVbRuEl(*WsCF$c6BDpMYaUi1g@#Xlz1YQj->r-(8u!DMlUUM%E@(tX-8O(j}MzzLy@Oid2qBm z*-Ud2TN_MkQm?Wjj4}zQaz&F@0|OOFn>R-m%^4)#g$}e5Q6Qdzg zX-VfgQOP1tmPAWi$}MIAJ%L|IwSGcsoihVdOW6)AzHgl6WGwX4KFDVF57o5V@$S$;JsiR$9=1PB)2U>V4q_9jF4ZzZH|HS^7rrN`SwF|j_ z{;;Mp84RRx{FT!ByI%7#XLGrb#DX8xnO?rXK4)DoGxlC$jUNwr+BLV896e(ReLH6$ zr!QyC|4TL;kx-)}=johg&Ypa%U2{K6i?k2MoKrP69ipT0IxH{vSivK;bmyTOUmf>A zgN}l&d0HMD-P~1Y*)g)7NhorV_8PYpZdu@m8RK6=uUrBt0Er>X!?mG-tK6jx8PjzU+!;r4~K>eI8N?Z>#d~cy~`K zxeBg}d@3>{V|L^{=nl+?9L2vPkK=DGy!>O%et*oJHQ~QVLJ-N|?SZ}k0000O3 literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/red1.png b/app/examples/Control/ArrayOfControls/red1.png new file mode 100644 index 0000000000000000000000000000000000000000..3cbb165d0469195eb69054977ea2a7c057924b2a GIT binary patch literal 1571 zcmV+;2Hg3HP)W)p+=2A+QCKSEUqf=YkVSd7dBy2;Fd@mLSzP`rOJ`WA^fw213kPQC}14z3v7giMq~M~ zue3D4#)5|nz8JYSFo_~E8kj9r=CCZXp}A?~cwk=(Pqnf4*PMJTtz8_zq~ualYCME* zM7CiHFG=2w1omYVZ5*9s6~-br7Tks{k&lN2=p8)S!fy`n#(iZp7LCAlgnevkup2jG zdElAAAC~e82D(zX&&d#>n>b9%C>;$tB6qiNU*zscXJCKekse+R6bqd_C&&3fmEYAU zYSe4|_$2RZrsr}bXH(Aia^A{`IeT;NkMH5QCkdzNtns5NMNVJNgJ)QKnReeLCuytk znJR~?6jcTXxbuvAbv9ODhWAHq2BReb8bY<);_j+jwZ^Cib_VeY)S8+2|mY{Hs?V5jE z%4=!4=4xD964+M)FL&|e5YMzRSZ6;DN4hEvHWzFNj8%B$8YUv4#-sQe*2VJ?5z@~j zPGce@?ysOXur|`=!n(K<+ouzO?JhVInG5t*ur8#YGZ<6u98c7G$9&^WuLGy>=Zrlo zd3J)?nPhQwq`Tnr_-3R#oCgir~Z?HP>1*|~~RGLvLSU*AksnkzYupCPR^%bdOY9*gxIC3_of||@| z#dN{5oxHKwQCrVYoxET)QVB^X3+Y(=1CPVK(u=j=+kHH6ews z6&)F0YS3|!v))-G@2?H1DXlB@H=uil`^(6lAt9V8A52WJ>I{=h*c!-U%3(`0m*u*h ztC(I4Kv{jgQd*nRTJ8~InjI9$cB4|^?b%e3-B`DUV>MnV_$XEc#w4Wzvys>9sV3eN z#7qLYJF=@BUfLpUA%Q3Y`53LI`4C1cjWkB>$Bhkk;MMiKaGawVm4cNGiY(FYOklPr z>0BTYql`}TM1wsT4pf$KszLWv{HvYu6ZC{cW~VArv789Bws3Wcb~5s2aCuU$pGCF2 z=F3~nn;O?dt`8i@ddV{mMOq8)8RDjSn_V$!)9eJ8H&*a2@ zes((1^o60yc`WBx&Qu?N=}(9+dFojx18lDI^_(MBCaV18Bq0h$XD0BXJo~Geg<*7k#TG(MPkTKdvxNDBbu_>mCeFo3Q)JFRvqZxA> z7kuf|*_yGs!LEXP12@Oi|M#xrfpNyy%+p!)xYrXe+#dN{U1E-OOXRuR^eok zyqdsvsZp&7+!~n;yoMk2@~37|&M(f3^35w`*U{ZblyG}wM@*?$Id9pV)bkrL@%?CI zJWDP6k3H#h#R;kJFO*9ryg}B^aD((!vppo*UXS*W4pIZJg+#$aIMiDnvv?6h{{^I3 VY5ioD5IFz<002ovPDHLkV1nS={i^@~ literal 0 HcmV?d00001 diff --git a/app/examples/Control/Embedder/.directory b/app/examples/Control/Embedder/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/Embedder/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/Embedder/.icon.png b/app/examples/Control/Embedder/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b6e995d784509d051e5a749b038edd8d78f83985 GIT binary patch literal 4242 zcmV;D5N+>?P)H3eCIOjBVO z-3*tj0CVnt11%KVgJqQ&JaS(JRplAHbOhcw0geN^n&8@+e75#2q-fJNlu~^8?sC3* z_Z+_Xy+ds6JU*@8`m++C@^5ybJr}K^C_BKn?kVT?Rr$Qu3_pDx%+#5_DcfVvk_7Wh zSi3Bb$L^oUsXm)qzIBwQlQEEi%S(WHU)qV*kQwŸ}f>-9zK>xJ*W1^sbw+%tVo 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Embedder\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:28+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Embedder" +msgstr "Incrustador" + +#: FMain.class:18 +msgid "Window not found!" +msgstr "Finestra no trobada." + +#: FMain.class:21 +msgid "Several windows found. I take the first one!" +msgstr "Diverses finestres trobades. Agafo la primera." + +#: FMain.class:60 +msgid "Embed error" +msgstr "Error d'Incrustat" + +#: FMain.class:106 +msgid "Desktop application embedder" +msgstr "Incrustador d'aplicació d'escriptori" + +#: FMain.class:120 +msgid "Window title" +msgstr "Títol de la finestra" + +#: FMain.class:125 +msgid "Enter there the title of the window you want to embed." +msgstr "Introduïu el títol de la finestra que voleu incrustar." + +#: FMain.class:132 +msgid "" +"Click on the Embed button to search the window whose title \n" +"is specified in the left field, and to embed it in the \n" +"blue rectangle below." +msgstr "" +"Feu clic al botó Incrusta per cercar la finestra, el títol de la qual \n" +"està especificat al camp de l'esquerra, i per incrustar a dins \n" +"del rectangle blau de sota." + +#: FMain.class:133 +msgid "&Embed" +msgstr "&Incrusta" + +#: FMain.class:140 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Feu clic al botó Descarta per alliberar la finestra de l'aplicació de la seva presó." + +#: FMain.class:141 +msgid "&Discard" +msgstr "&Descarta" + diff --git a/app/examples/Control/Embedder/.lang/cs.po b/app/examples/Control/Embedder/.lang/cs.po new file mode 100644 index 00000000..15a28af1 --- /dev/null +++ b/app/examples/Control/Embedder/.lang/cs.po @@ -0,0 +1,62 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Embedder" +msgstr "ZapoÅ¡těč" + +#: FMain.class:20 +msgid "Window not found!" +msgstr "Okno nenalezeno!" + +#: FMain.class:23 +msgid "Several windows found. I take the first one!" +msgstr "Nalezeno několik obek. Beru první!" + +#: FMain.class:62 +msgid "Embed error" +msgstr "Chyba zapuÅ¡tění" + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Aplikace zapuÅ¡těna" + +#: FMain.form:29 +msgid "Window title" +msgstr "Titulek okna" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Zde zadejte název okna, které chcete zapustit." + +#: FMain.form:40 +msgid "" +"Click on the Embed button to search the window whose title \n" +"is specified in the left field, and to embed it in the \n" +"blue rectangle below." +msgstr "" +"Klikněte na tlačítko Zapustit pro vyhledávání okna, jehož název\n" +"je uvedené v levém poli, a zapuÅ¡těno bude do\n" +"modrého obdélníku níže." + +#: FMain.form:41 +msgid "&Embed" +msgstr "Za&pustit" + +#: FMain.form:48 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Klikněte na ZruÅ¡it a okno aplikace bude uvolněno." + +#: FMain.form:49 +msgid "&Discard" +msgstr "&ZruÅ¡it" diff --git a/app/examples/Control/Embedder/.lang/de.po b/app/examples/Control/Embedder/.lang/de.po new file mode 100644 index 00000000..4036899c --- /dev/null +++ b/app/examples/Control/Embedder/.lang/de.po @@ -0,0 +1,59 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Embedder" +msgstr "-" + +#: FMain.class:20 +msgid "Window not found!" +msgstr "Fenster nicht gefunden!" + +#: FMain.class:23 +msgid "Several windows found. I take the first one!" +msgstr "Mehrere Fenster gefunden. Ich nehme das erste!" + +#: FMain.class:62 +msgid "Embed error" +msgstr "Fehler beim Einbinden" + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Desktop-Anwendungs-Einbinder" + +#: FMain.form:29 +msgid "Window title" +msgstr "Fenstertitel" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Geben Sie hier den Titel des Fensters ein, das Sie einbinden wollen." + +#: FMain.form:40 +msgid "" +"Click on the Embed button to search the window whose title \n" +"is specified in the left field, and to embed it in the \n" +"blue rectangle below." +msgstr "Klicken Sie auf die Einbetten Schaltfläche, um das Fenster, dessen Titel im linken Feld angegeben ist, zu suchen und es im blauen Rechteck unten einzubetten." + +#: FMain.form:41 +msgid "&Embed" +msgstr "&Einbinden" + +#: FMain.form:48 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Klicken Sie auf die Lösen Schaltfläche, um das Anwendungsfenster aus der Anbindung zu lösen." + +#: FMain.form:49 +msgid "&Discard" +msgstr "&Lösen" diff --git a/app/examples/Control/Embedder/.lang/es.po b/app/examples/Control/Embedder/.lang/es.po new file mode 100644 index 00000000..f3386436 --- /dev/null +++ b/app/examples/Control/Embedder/.lang/es.po @@ -0,0 +1,57 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.form:50 +msgid "&Discard" +msgstr "&Retornar" + +#: FMain.form:42 +msgid "&Embed" +msgstr "&Empotrar" + +#: FMain.form:49 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Haga click en el botón Retornar para liberar la ventana de la aplicación de su jaula." + +#: FMain.form:41 +msgid "Click on the Embed button to search the window whose title \nis specified in the left field, and to embed it in the \nblue rectangle below." +msgstr "Haga click en el botón Empotrar para buscar la ventana cuyo título \nha especificado en el campo de la izquierda \ncon el fin de empotrarla en el rectángulo azul de abajo. " + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Empotrador de aplicaciones de escritorio" + +#: FMain.class:60 +msgid "Embed error" +msgstr "Error de empotrado" + +#: .project:1 +msgid "Embedder" +msgstr "Empotrador" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Introduzca el título de la ventana que desea empotrar." + +#: FMain.class:21 +msgid "Several windows found. I take the first one!" +msgstr "Multiples ventanas encontradas. ¡Usando la primera!" + +#: FMain.class:18 +msgid "Window not found!" +msgstr "¡Ventana no encontrada!" + +#: FMain.form:29 +msgid "Window title" +msgstr "Título de la ventana" + diff --git a/app/examples/Control/Embedder/.lang/nl.po b/app/examples/Control/Embedder/.lang/nl.po new file mode 100644 index 00000000..65b6f4f0 --- /dev/null +++ b/app/examples/Control/Embedder/.lang/nl.po @@ -0,0 +1,55 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Embedder 3.5.90\n" +"PO-Revision-Date: 2014-10-02 15:16 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Embedder" +msgstr "-" + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Desktop applicatie embedder" + +#: FMain.form:29 +msgid "Window title" +msgstr "Venster titel" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Voer hier de titel in van het venster dat je wilt embedden." + +#: FMain.form:40 +msgid "Click on the Embed button to search the window whose title \nis specified in the left field, and to embed it in the \nblue rectangle below." +msgstr "Klik op de Embed knop om het venster te zoeken wiens titel\ngespecificieerd is in het linker veld en om het te embedden in\n de blauwe rechthoek beneden." + +#: FMain.form:41 +msgid "&Embed" +msgstr "-" + +#: FMain.form:48 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Klik op de Uitsluiten knop op het applicatievenster uit zijn gevangenis te bevrijden." + +#: FMain.form:49 +msgid "&Discard" +msgstr "&Uitsluiten" + +#: FMain.class:20 +msgid "Window not found!" +msgstr "Venster niet gevonden!" + +#: FMain.class:23 +msgid "Several windows found. I take the first one!" +msgstr "Verschillende vensters gevonden. Ik gebruik het eerste!" + +#: FMain.class:62 +msgid "Embed error" +msgstr "Embed fout" + diff --git a/app/examples/Control/Embedder/.lang/ru.po b/app/examples/Control/Embedder/.lang/ru.po new file mode 100644 index 00000000..1e436589 --- /dev/null +++ b/app/examples/Control/Embedder/.lang/ru.po @@ -0,0 +1,77 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Control/Embedder/.project:21 +msgid "Embedder" +msgstr "Встраиватель" + +#: app/examples/Control/Embedder/.project:22 app/examples/Control/Embedder/.src/FMain.form:5 +msgid "Desktop application embedder" +msgstr "Встраиватель для приложения рабочего стола" + +#: app/examples/Control/Embedder/.src/FMain.class:20 +msgid "Window not found!" +msgstr "Окно не найдено!" + +#: app/examples/Control/Embedder/.src/FMain.class:23 +msgid "Several windows found. I take the first one!" +msgstr "Найдено несколько окон. Будет взято первое!" + +#: app/examples/Control/Embedder/.src/FMain.class:62 +msgid "Embed error" +msgstr "Ошибка встраивания" + +#: app/examples/Control/Embedder/.src/FMain.class:86 +msgid "Process_Kill" +msgstr "Процесс убийства" + +#: app/examples/Control/Embedder/.src/FMain.form:17 +msgid "Window title" +msgstr "Заголовок окна" + +#: app/examples/Control/Embedder/.src/FMain.form:21 +msgid "Enter there the title of the window you want to embed." +msgstr "Введите там заголовок окна, которое вы хотите встроить." + +#: app/examples/Control/Embedder/.src/FMain.form:26 +msgid "" +"Click on the Embed button to search the window whose title \n" +"is specified in the left field, and to embed it in the \n" +"blue rectangle below." +msgstr "Нажать кнопку Встроить, чтобы найти окно, заголовок которого указан в левом поле, и встроить его в синий прямоугольник ниже." + +#: app/examples/Control/Embedder/.src/FMain.form:27 +msgid "&Embed" +msgstr "Встроить" + +#: app/examples/Control/Embedder/.src/FMain.form:33 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Нажать кнопку Отказаться, чтобы освободить окно приложения из захвата." + +#: app/examples/Control/Embedder/.src/FMain.form:34 +msgid "&Discard" +msgstr "Отказаться" + diff --git a/app/examples/Control/Embedder/.project b/app/examples/Control/Embedder/.project new file mode 100644 index 00000000..38b4c637 --- /dev/null +++ b/app/examples/Control/Embedder/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +Title=Embedder +Description="Desktop application embedder" +Startup=FMain +Icon=embedder.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop +Environment="GB_GUI=gb.gtk3" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Control/Embedder/.src/FMain.class b/app/examples/Control/Embedder/.src/FMain.class new file mode 100644 index 00000000..1c3ed8f3 --- /dev/null +++ b/app/examples/Control/Embedder/.src/FMain.class @@ -0,0 +1,89 @@ +' Gambas class file + +Public Sub btnEmbed_Click() + + Dim sTitle As String + Dim aHandle As Integer[] + Dim iHandle As Integer + + sTitle = Trim(txtTitle.Text) + If Not sTitle Then Return + + If Left(sTitle, 2) = "0x" Then + iHandle = Val("&" & Mid$(sTitle, 3)) + Else If Left(sTitle) = "&" Then + iHandle = Val(sTitle) + Else +' aHandle = Desktop.FindWindow(Trim(txtTitle.Text)) + aHandle = Desktop.FindWindow(txtTitle.Text) + If aHandle.Count = 0 Then + Message.Warning(("Window not found!")) + Return + Else If aHandle.Count >= 2 Then + Message(("Several windows found. I take the first one!")) + Endif + iHandle = aHandle[0] + Endif + + Try embEmbedder.Embed(iHandle) + If Error Then Message.Warning(Error.Text) + +End + +Public Sub embEmbedder_Embed() + + btnEmbed.Enabled = False + btnDiscard.Enabled = True + +End + +Public Sub btnDiscard_Click() + + embEmbedder.Discard + embEmbedder_Close + +End + +Public Sub embEmbedder_Close() + + btnEmbed.Enabled = True + btnDiscard.Enabled = False + +End + +Public Sub Form_Open() + + lblID.Text = "&" & Hex$(embEmbedder.Id) + +End + +Public Sub embEmbedder_Error() + + Message.Error(("Embed error")) + +End + + +Public Sub Process_Read() + + Dim sStr As String + + 'READ #LAST, sStr, Lof(LAST) + Line Input #Last, sStr + Print "\t"; sStr + +End + +Public Sub Process_Error(sStr As String) + + Print "\t"; sStr; + If Right(sStr) <> "\n" Then Print + +End + +Public Sub Process_Kill() + + Print ("Process_Kill") + Print Last.State;; Last.Value + +End diff --git a/app/examples/Control/Embedder/.src/FMain.form b/app/examples/Control/Embedder/.src/FMain.form new file mode 100644 index 00000000..40465f3d --- /dev/null +++ b/app/examples/Control/Embedder/.src/FMain.form @@ -0,0 +1,54 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(32.7143,25,84,60) + Text = ("Desktop application embedder") + Icon = Picture["embedder.png"] + Arrangement = Arrange.Vertical + { Panel1 Panel + MoveScaled(0,0,82,6) + Arrangement = Arrange.Horizontal + Spacing = True + Margin = True + { Label1 Label + MoveScaled(2,1,16,4) + Font = Font["Bold"] + AutoResize = True + Text = ("Window title") + } + { txtTitle TextBox + MoveScaled(19,1,17,4) + ToolTip = ("Enter there the title of the window you want to embed.") + Expand = True + } + { btnEmbed Button + MoveScaled(37,1,14,4) + ToolTip = ("Click on the Embed button to search the window whose title \nis specified in the left field, and to embed it in the \nblue rectangle below.") + Text = ("&Embed") + Default = True + } + { btnDiscard Button + MoveScaled(52,1,14,4) + Enabled = False + ToolTip = ("Click on the Discard button to free the application window from its jail.") + Text = ("&Discard") + } + { lblID Label + MoveScaled(69,1,11,4) + Visible = False + Alignment = Align.Center + Border = Border.Sunken + } + } + { Panel2 Panel + MoveScaled(3,8,67,43) + Background = &H9FCFFF& + Expand = True + Arrangement = Arrange.Fill + Padding = 4 + { embEmbedder Embedder + MoveScaled(8,8,35,25) + Expand = True + } + } +} diff --git a/app/examples/Control/Embedder/embedder.png b/app/examples/Control/Embedder/embedder.png new file mode 100644 index 0000000000000000000000000000000000000000..fa90352044e39b15665b1fdc476fb55faa601013 GIT binary patch literal 2623 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/HighlightEditor/.directory b/app/examples/Control/HighlightEditor/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/HighlightEditor/.hidden/screenshots/2014-12-17.png b/app/examples/Control/HighlightEditor/.hidden/screenshots/2014-12-17.png new file mode 100644 index 0000000000000000000000000000000000000000..9db951a333c6c49e43dce7b34f41de25afaed402 GIT binary patch literal 58870 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 @|Wx1m7{5P?P@x84BZZm;c2)?VO0dj={x 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/HighlightEditor/.lang/ca.po b/app/examples/Control/HighlightEditor/.lang/ca.po new file mode 100644 index 00000000..2ea14157 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/ca.po @@ -0,0 +1,43 @@ +# Catalan translation of HighlightEditor +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the HighlightEditor package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: HighlightEditor\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:32+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FEditor.class:208 +msgid "Highlight HTML" +msgstr "HTML ressaltat" + +#: FEditor.class:213 +msgid "Highlight immediately" +msgstr "Ressalta immediatament" + +#: FEditor.class:199 +msgid "HTML Highlighting Editor" +msgstr "Editor del ressalt d'HTML" + +#: .project:2 +msgid "HTML highlighting with the editor control" +msgstr "Ressaltador d'HTML amb el control de l'editor" + +#: FEditor.class:204 +msgid "Popup" +msgstr "Emergent" + +#: FEditor.class:221 +msgid "Quit" +msgstr "Surt" + diff --git a/app/examples/Control/HighlightEditor/.lang/cs.po b/app/examples/Control/HighlightEditor/.lang/cs.po new file mode 100644 index 00000000..6e3be1d3 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/cs.po @@ -0,0 +1,40 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTML highlighting with the editor control" +msgstr "HTML zvýrazňovač s editorem" + +#: .project:2 +msgid "This example show how to use the Editor control with custom highlighting." +msgstr "Tento příklad ukazuje jak použít Editor s volitelným zvýrazňováním." + +#: FEditor.form:15 +msgid "HTML Highlighting Editor" +msgstr "Editor zvýrazňující HTML" + +#: FEditor.form:20 +msgid "Popup" +msgstr "Vyskakovaví" + +#: FEditor.form:24 +msgid "Highlight HTML" +msgstr "Zvýraznění HTML" + +#: FEditor.form:29 +msgid "Highlight immediately" +msgstr "Zvýraznit ihned" + +#: FEditor.form:36 +msgid "Quit" +msgstr "Ukončit" diff --git a/app/examples/Control/HighlightEditor/.lang/de.po b/app/examples/Control/HighlightEditor/.lang/de.po new file mode 100644 index 00000000..8abe3e1e --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/de.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTML highlighting with the editor control" +msgstr "HTML-Markierungen mit dem Editor-Steuerelement" + +#: .project:2 +msgid "This example show how to use the Editor control with custom highlighting." +msgstr "Dieses Beispiel zeigt, wie man das Editor-Steuerelement mit benutzerdefinierten Markierungen verwendet." + +#: FEditor.form:15 +msgid "HTML Highlighting Editor" +msgstr "HTML-Markierungseditor" + +#: FEditor.form:20 +msgid "Popup" +msgstr "-" + +#: FEditor.form:24 +msgid "Highlight HTML" +msgstr "HTML-Marks hervorheben" + +#: FEditor.form:29 +msgid "Highlight immediately" +msgstr "Sofort hervorheben" + +#: FEditor.form:36 +msgid "Quit" +msgstr "Beenden" + diff --git a/app/examples/Control/HighlightEditor/.lang/es.po b/app/examples/Control/HighlightEditor/.lang/es.po new file mode 100644 index 00000000..016970b6 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/es.po @@ -0,0 +1,34 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FEditor.class:201 +msgid "HTML Highlighting Editor" +msgstr "Editor con resaltado HTML " + +#: FEditor.class:206 +msgid "Popup" +msgstr "Popup" + +#: FEditor.class:210 +msgid "Highlight HTML" +msgstr "Resaltado HTML" + +#: FEditor.class:215 +msgid "Highlight immediately" +msgstr "Resaltar instantáneamente" + +#: FEditor.class:222 +msgid "Quit" +msgstr "Salir" + +#~ msgid "HTML highlighting with the editor control" +#~ msgstr "Resaltado HTML con el control editor" diff --git a/app/examples/Control/HighlightEditor/.lang/nl.po b/app/examples/Control/HighlightEditor/.lang/nl.po new file mode 100644 index 00000000..2063ffd7 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/nl.po @@ -0,0 +1,39 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: HighlightEditor 3.5.90\n" +"PO-Revision-Date: 2014-10-02 15:21 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTML highlighting with the editor control" +msgstr "HTML markering met editor control" + +#: .project:2 +msgid "This example shows how to use the Editor control with custom highlighting." +msgstr "Dit voorbeeld laat zien hoe je de Editor control kunt gebruiken met aangespaste markering." + +#: FEditor.form:15 +msgid "HTML Highlighting Editor" +msgstr "HTML markeer Editor" + +#: FEditor.form:20 +msgid "Popup" +msgstr "-" + +#: FEditor.form:24 +msgid "Highlight HTML" +msgstr "Markeer HTML" + +#: FEditor.form:29 +msgid "Highlight immediately" +msgstr "Onmiddelijk markeren" + +#: FEditor.form:36 +msgid "Quit" +msgstr "Afsluiten" + diff --git a/app/examples/Control/HighlightEditor/.lang/ru.po b/app/examples/Control/HighlightEditor/.lang/ru.po new file mode 100644 index 00000000..1cef47fd --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/ru.po @@ -0,0 +1,64 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Control/HighlightEditor/.project:20 +msgid "HTML highlighting with the editor control" +msgstr "Подсветка HTML с контролем редактора" + +#: app/examples/Control/HighlightEditor/.project:21 +msgid "" +"Syntax highlighting example\n" +"\n" +"This example shows how to use the Editor control with custom highlighting." +msgstr "" +"Пример подсветки синтаксиса\n" +"\n" +"В этом примере показано, как использовать элемент управления редактора с пользовательской подсветкой." + +#: app/examples/Control/HighlightEditor/.src/FEditor.class:171 +msgid "modified" +msgstr "изменено" + +#: app/examples/Control/HighlightEditor/.src/FEditor.form:5 +msgid "HTML Highlighting Editor" +msgstr "Редактор подсветки HTML" + +#: app/examples/Control/HighlightEditor/.src/FEditor.form:9 +msgid "Popup" +msgstr "Всплывающее окно" + +#: app/examples/Control/HighlightEditor/.src/FEditor.form:12 +msgid "Highlight HTML" +msgstr "Подсветка HTML" + +#: app/examples/Control/HighlightEditor/.src/FEditor.form:16 +msgid "Highlight immediately" +msgstr "Немедленная подсветка" + +#: app/examples/Control/HighlightEditor/.src/FEditor.form:21 +msgid "Quit" +msgstr "Выход" + diff --git a/app/examples/Control/HighlightEditor/.project b/app/examples/Control/HighlightEditor/.project new file mode 100644 index 00000000..c3fa5e30 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +Title=HTML highlighting with the editor control +Startup=FEditor +Icon=editor.png +Version=1.0.0 +Component=gb.image +Component=gb.qt4 +Component=gb.eval +Component=gb.eval.highlight +Component=gb.util +Component=gb.form.editor +Component=gb.qt4.ext +Description="Syntax highlighting example\n\nThis example shows how to use the Editor control with custom highlighting." +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Control/HighlightEditor/.src/FEditor.class b/app/examples/Control/HighlightEditor/.src/FEditor.class new file mode 100644 index 00000000..f82eefe2 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.src/FEditor.class @@ -0,0 +1,184 @@ +' Gambas class file + +Private hEditor As Editor +Private $bIgnore As Boolean +Private $bModified As Boolean + +Public Sub Form_Open() + + $bIgnore = True + Editor1.Text = File.Load("download.html") + + Editor1.Flags[Editor.HighlightBraces] = True + Editor1.Styles[Highlight.Operator].Color = Color.DarkGreen + Editor1.Styles[Highlight.Operator].Bold = True + Editor1.Styles[Highlight.String].Color = Color.DarkRed + Editor1.Flags[Editor.ShowLineNumbers] = True + Editor1.Flags[Editor.NoFolding] = True + Editor1.Flags[Editor.ShowCurrentLine] = True + + Editor2.Flags[Editor.HighlightBraces] = True + Editor2.Styles[Highlight.Operator].Color = Color.DarkGreen + Editor2.Styles[Highlight.Operator].Bold = True + Editor2.Styles[Highlight.String].Color = Color.DarkRed + + VSplit1.Layout = [1, 0] + mnuImmediately_Click + +End + +Public Sub Editor1_Highlight() + + Dim iState As Integer + Dim iNextState As Integer + Dim iInd As Integer + Dim J As Integer + Dim sText As String + Dim sCar As String + Dim iPos As Integer + Dim bMarkup As Boolean + + iState = Highlight.State + sText = Highlight.Text + + 'PRINT "Highlight:";; iState;; sText + + For iInd = 1 To String.Len(sText) + + iNextState = iState + sCar = String.Mid$(sText, iInd, 1) + + If bMarkup Then + + If sCar = ">" Then + bMarkup = False + iState = Highlight.Keyword + iNextState = Highlight.Normal + Else If sCar = " " Then + iNextState = Highlight.Operator + Else If sCar = "=" Then + iNextState = Highlight.String + Endif + + Else + + Select Case iState + Case Highlight.Normal + If sCar = "<" Then + If String.Mid$(sText, iInd, 4) = " +

+What is that new animal ? Well, Gambas +is a free development environment based on a Basic interpreter +with object extensions, like Visual Basic™ (but it is NOT +a clone !). Read the +introduction +for more information. +

+With Gambas, you can quickly design your program GUI, access +MySQL, PostgreSQL and SQLite databases, pilot KDE applications with DCOP, +translate your program into many languages, create network applications +easily, make 3D OpenGL applications, and so on... +

+http://gambas.sourceforge.net + \ No newline at end of file diff --git a/app/examples/Control/TreeView/.directory b/app/examples/Control/TreeView/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/TreeView/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/TreeView/.icon.png b/app/examples/Control/TreeView/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c56ed6572a0f49de4e39c792b26ed178176748e2 GIT binary patch literal 4093 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/TreeView/.lang/ca.po b/app/examples/Control/TreeView/.lang/ca.po new file mode 100644 index 00000000..5aea149b --- /dev/null +++ b/app/examples/Control/TreeView/.lang/ca.po @@ -0,0 +1,88 @@ +# Catalan translation of TreeView +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the TreeView package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: TreeView\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-21 01:56+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Quant a" + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Posició (" + +#: TreeViewExample.class:42 +msgid " children." +msgstr " fills." + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Neteja els esdeveniments" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Element actual" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Esdeveniments" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Femení" + +#: TreeViewExample.class:42 +msgid " has " +msgstr " té " + +#: TreeViewExample.class:46 +msgid " has 1 child." +msgstr " té un fill." + +#: TreeViewExample.class:44 +msgid " has no children." +msgstr " no té fills." + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Ajuda" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Insereix un nom" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Masculí" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Elimina el nom" + +#: .project:1 +msgid "TreeView example" +msgstr "Exemple de visualització en arbre" + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "Exemple de visualització en arbre" + +#: TreeViewExample.class:139 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "Exemple de Vista d'Arbre escrit per C. Packard i Fabien Hutrel." + diff --git a/app/examples/Control/TreeView/.lang/cs.po b/app/examples/Control/TreeView/.lang/cs.po new file mode 100644 index 00000000..23b02a14 --- /dev/null +++ b/app/examples/Control/TreeView/.lang/cs.po @@ -0,0 +1,76 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "TreeView example" +msgstr "Příklad stromového pohledu" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 má &2 potomků." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 nemá potomka." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 má 1 potomka." + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Obdelník položky je (" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "Příklad Stromového pohledu napsaný C. Packard a Fabien Hutrel." + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "Příklad Stromového pohledu" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Nápověda" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "O aplikaci" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Vložit jméno" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Odstranit jméno" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Aktuální položka" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Události" + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Vyčistit události" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Muž" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Žena" diff --git a/app/examples/Control/TreeView/.lang/de.po b/app/examples/Control/TreeView/.lang/de.po new file mode 100644 index 00000000..f7b8900a --- /dev/null +++ b/app/examples/Control/TreeView/.lang/de.po @@ -0,0 +1,77 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "TreeView example" +msgstr "TreeView-Beispiel" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 hat &2 Unterpunkte." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 hat keine Unterpunkte." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 hat 1 Unterpunkt." + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Element auf Position (" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "TreeView-Beispiel geschrieben von C. Packard und Fabien Hutrel." + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "TreeView-Beispiel" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Hilfe" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Uber" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Name einfügen" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Name entfernen" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Gegenwärtiges Element" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Ereignisse" + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Ereignisse löschen" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Männlich" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Weiblich" + diff --git a/app/examples/Control/TreeView/.lang/es.po b/app/examples/Control/TreeView/.lang/es.po new file mode 100644 index 00000000..5140a0e7 --- /dev/null +++ b/app/examples/Control/TreeView/.lang/es.po @@ -0,0 +1,73 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 tiene &2 hijos." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 tiene 1 hijo." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 no tiene hijos." + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Eliminar eventos" + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Acerca de" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Elemento actual" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Eventos" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Mujer" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Ayuda" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Insertar nombre" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Hombre" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Eliminar nombre" + +#: .project:1 TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "Ejemplo de TreeView" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "Ejemplo de TreeView escrito por C. Packard y Fabien Hutrel." + diff --git a/app/examples/Control/TreeView/.lang/nl.po b/app/examples/Control/TreeView/.lang/nl.po new file mode 100644 index 00000000..b3ad76d7 --- /dev/null +++ b/app/examples/Control/TreeView/.lang/nl.po @@ -0,0 +1,75 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: TreeView 3.6.0\n" +"PO-Revision-Date: 2014-10-10 13:53 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "TreeView example" +msgstr "TreeView voorbeeld" + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "TreeView Voorbeeld" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "-" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Over" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Naam tussenvoegen" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Naam verwijderen" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Huidig item" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Gebeurtenissen" + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Gebeurtenissen opschonen" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Mannelijk" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Vrouwelijk" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 heeft &2 kinderen." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 heeft geen kinderen." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 heeft 1 kind." + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Item rect is (" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "TreeView voorbeeld geschreven door C. Packard en Fabien Hutrel." + diff --git a/app/examples/Control/TreeView/.lang/ru.po b/app/examples/Control/TreeView/.lang/ru.po new file mode 100644 index 00000000..87b2c24c --- /dev/null +++ b/app/examples/Control/TreeView/.lang/ru.po @@ -0,0 +1,154 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Control/TreeView/.project:19 +msgid "TreeView example" +msgstr "Пример просмотра дерева" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:15 app/examples/Control/TreeView/.src/TreeViewExample.class:16 app/examples/Control/TreeView/.src/TreeViewExample.class:17 app/examples/Control/TreeView/.src/TreeViewExample.class:23 +msgid "Bill" +msgstr "Билл" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:16 +msgid "Ted" +msgstr "Тэд" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:17 app/examples/Control/TreeView/.src/TreeViewExample.class:18 +msgid "Sally" +msgstr "Салли" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:18 +msgid "Frank" +msgstr "Фрэнк" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 имеет &2 детей." + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 не имеет детей." + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 имеет 1 ребёнка." + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:49 +msgid "Item rect is" +msgstr "Прямоугольник элемента" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:60 +msgid "Click" +msgstr "Щелчок" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:108 +msgid "Collapse" +msgstr "Свернуть" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:115 +msgid "DblClick" +msgstr "Двойной щелчок" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:123 +msgid "Select" +msgstr "Выбрать" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:130 +msgid "Delete" +msgstr "Удалить" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:137 +msgid "Expand" +msgstr "Развернуть" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "Пример просмотра дерева, написанный К. Пакард и Фабьен Хатрел." + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:157 +msgid "Activate" +msgstr "Активировать" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:164 +msgid "Rename" +msgstr "Переименовать" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:174 +msgid "Event" +msgstr "Событие" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:174 +msgid "Item:" +msgstr "Элемент:" + +#: app/examples/Control/TreeView/.src/TreeViewExample.class:182 +msgid "Cancel" +msgstr "Отмена" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:5 +msgid "TreeView Example" +msgstr "Пример просмотра дерева" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:9 +msgid "Help" +msgstr "Справка" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:11 +msgid "About" +msgstr "О программе" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:21 +msgid "Insert Name" +msgstr "Вставить имя" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:25 +msgid "Remove Name" +msgstr "Удалить имя" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:32 +msgid "Current item" +msgstr "Текущий элемент" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:40 +msgid "Events" +msgstr "События" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:48 +msgid "&Clear events" +msgstr "Очистить события" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:52 +msgid "Male" +msgstr "Мужчина" + +#: app/examples/Control/TreeView/.src/TreeViewExample.form:57 +msgid "Female" +msgstr "Женщина" + +#: app/examples/Control/TreeView/.src/workaround:1 +msgid "
Item rect is (" +msgstr "
Прямоугольник элемента (" + diff --git a/app/examples/Control/TreeView/.project b/app/examples/Control/TreeView/.project new file mode 100644 index 00000000..e1ab34f3 --- /dev/null +++ b/app/examples/Control/TreeView/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Title=TreeView example +Startup=TreeViewExample +Icon=treeview.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Packager=1 diff --git a/app/examples/Control/TreeView/.src/TreeViewExample.class b/app/examples/Control/TreeView/.src/TreeViewExample.class new file mode 100644 index 00000000..db79cddf --- /dev/null +++ b/app/examples/Control/TreeView/.src/TreeViewExample.class @@ -0,0 +1,184 @@ +' Gambas class file + +Public intEventNumber As Integer + +Public Sub Form_Open() + + Dim picMale As Picture + Dim picFemale As Picture + + picFemale = Picture["Female.png"] + picMale = Picture["Male.png"] + + 'This will populate our treeview with our starting entries + 'Note: I'll just keep the entries text and its key the same to keep it simple + TreeView1.Add(("Bill"), ("Bill"), picMale) + TreeView1.Add(("Ted"), ("Ted"), picMale, ("Bill")) + TreeView1.Add(("Sally"), ("Sally"), picFemale, ("Bill")) + TreeView1.Add(("Frank"), ("Frank"), picMale, ("Sally")) + 'TreeView1.MoveCurrent + 'TreeView1.Item.Selected = TRUE + 'TreeView1.Item.Expanded = TRUE + + TreeView1[("Bill")].Expanded = True + +End + +Private Sub RefreshInfo() + + 'This little check just updates our label so that we know how many + 'children an entry has. + + Dim sText As String + + If Not TreeView1.Current Then + Textlabel1.Text = "" + Return + Endif + + With TreeView1.Current + + If .Children > 1 Then + sText = Subst(("&1 has &2 children."), .Text, .Children) + Else If .Children = 0 Then + sText = Subst(("&1 has no children."), .Text) + Else + sText = Subst(("&1 has 1 child."), .Text) + End If + + sText &= "
" & ("Item rect is") & " (" & .X & "," & .Y & "," & .W & "," & .H & ")" + + End With + + TextLabel1.Text = sText + +End + +Public Sub TreeView1_Click() + + 'This just updates our event stack + AddLog(("Click")) + +End + +Public Sub Button1_Click() + + Dim sIcon As String + Dim sParent As String + + If Textbox1.Text <> Null Then + If RadioButton1.Value Then + sIcon = "Male.png" + Else + sIcon = "Female.png" + End If + 'Gets the parent item: the current item, or nothing is the treeview is void + sParent = TreeView1.Key + 'Now we will add a new entry with a key and a name of what was in the text box + 'We will place it as a child of the currently selected entry + TreeView1.Add(Textbox1.Text, Textbox1.Text, Picture[sIcon], sParent).EnsureVisible + TreeView1.Item.EnsureVisible 'This will make sure that the item we just added to the list is in the visable area of the control. (Scrolling if necessary) + TextBox1.Text = "" 'This empties out textbox + RefreshInfo ' This will update our label and reflect the new number of kids + End If + +End + +Public Sub Button2_Click() + + If Not TreeView1.Key Then Return + 'Lets remove the current cursor item + TreeView1.Remove(TreeView1.Key) + 'Now move the cursor to the current item (since we are now pointing at a deleted item) + 'But first we check the count to make sure we didn't delete the last item in the list + 'if we did then we obviously don't run this part. + If TreeView1.Count > 0 Then + 'TreeView1.MoveCurrent + 'This selects or 'highlights' our current item + 'TreeView1.Current.Selected = TRUE + 'This will update our label and reflect the new number of kids + RefreshInfo + End If + +End + +Public Sub TreeView1_Collapse() + 'This just updates our event stack + + AddLog(("Collapse")) + +End + +Public Sub TreeView1_DblClick() + 'This just updates our event stack + + AddLog(("DblClick")) + +End + +Public Sub TreeView1_Select() + 'This just updates our event stack + + RefreshInfo + AddLog(("Select")) + +End + +Public Sub TreeView1_Delete() + 'This just updates our event stack + + AddLog(("Delete")) + +End + +Public Sub TreeView1_Expand() + 'This just updates our event stack + + AddLog(("Expand")) + +End + +Public Sub Button3_Click() + + TextArea1.Text = "" + 'IntEventNumber = 0 + +End + +Public Sub About_Click() + + Message.Info(("TreeView example written by C. Packard and Fabien Hutrel.")) + +End + +Public Sub TreeView1_Activate() + 'This just updates our event stack + + AddLog(("Activate")) + +End + +Public Sub TreeView1_Rename() + 'This just updates our event stack + + AddLog(("Rename")) + +End + +Private Sub AddLog(anevent As String) + 'This updates our event stack, this sub is used by all events... it display the current node key. + + Dim sKey As String + + Try sKey = TreeView1.Item.Key + TextArea1.Text = ("Event") & "(" & intEventNumber & "): " & anevent & " " & ("Item:") & " '" & sKey & "'\n" & TextArea1.Text + TextArea1.Pos = 0 + Inc intEventNumber + +End + +Public Sub TreeView1_Cancel() + + AddLog(("Cancel")) + +End diff --git a/app/examples/Control/TreeView/.src/TreeViewExample.form b/app/examples/Control/TreeView/.src/TreeViewExample.form new file mode 100644 index 00000000..0c3b4392 --- /dev/null +++ b/app/examples/Control/TreeView/.src/TreeViewExample.form @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(25,11.1429,92,54) + Text = ("TreeView Example") + Icon = Picture["treeview.png"] + Resizable = False + { Help Menu + Text = ("Help") + { About Menu + Text = ("About") + } + } + { TreeView1 TreeView + MoveScaled(1,1,24,48) + Mode = Select.Multiple + Editable = True + } + { Button1 Button + MoveScaled(54,1,17,4) + Text = ("Insert Name") + } + { Button2 Button + MoveScaled(54,6,17,4) + Text = ("Remove Name") + } + { TextBox1 TextBox + MoveScaled(27,1,25,4) + } + { Frame1 Frame + MoveScaled(27,11,64,13) + Text = ("Current item") + { TextLabel1 TextLabel + MoveScaled(1,4,38,8) + } + } + { Label2 Label + MoveScaled(27,25,39,4) + Font = Font["Bold"] + Text = ("Events") + } + { TextArea1 TextArea + MoveScaled(27,29,64,15) + ReadOnly = True + } + { Button3 Button + MoveScaled(71,45,20,4) + Text = ("&Clear events") + } + { RadioButton1 RadioButton + MoveScaled(27,6,12,4) + Text = ("Male") + Value = True + } + { RadioButton2 RadioButton + MoveScaled(40,6,13,4) + Text = ("Female") + } +} diff --git a/app/examples/Control/TreeView/Female.png b/app/examples/Control/TreeView/Female.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1a3bb21aae6cf2350be5eb3ece0d190ece3d0f GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;wtpJ}8S0Me5;Xebze;|n;UHPLY z1ys#j666;Qqh%dE5=RbAdOHBM$~39e3lbn4!2fxZ)Oo12=9s$~zz+_qK_J{QMUTqEr$)2p4} z_r^uQ{j=7L$-4!PT-?=mF0n;Jm`OXiL1F=mSwo{>%9aU2H!|4RCM@-`O7L!6@a@== zse7+4v`b>N7I|jBL;OI)cOmAc+VAgJ-^hrh^6U}dWH{`Tu{0|3H!;>XS(+ z1FGjM3GxdD()B!%TR%2;n{quciwdz?1r*ut>Eak7aXC35L4bvcjjgTxy_|u8L6Zd& zkJqs>egz>1O|_@T`40%Kt4vkoDLcmRAhhUxgTjxEMl&vdFM7DH@_o+?kp`KI=HjfZ zspnZ6EbHBp#Q*>R literal 0 HcmV?d00001 diff --git a/app/examples/Control/TreeView/treeview.png b/app/examples/Control/TreeView/treeview.png new file mode 100644 index 0000000000000000000000000000000000000000..760ff1f884602717f3388dd9e490e2797d8e48d0 GIT binary patch literal 471 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\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:114 +msgid "Amount" +msgstr "Quantitat" + +#: FMain.class:124 +msgid "Article" +msgstr "-" + +#: FMain.class:169 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Ja que això és només un exemple,
res s'ordenarà,
res es desarà,
ni res s'enviarà." + +#: FMain.class:147 +msgid "image files of the Gambas logo" +msgstr "Fitxers d'imatges del logotip del Gambas" + +#: FMain.class:95 +msgid "Introduction" +msgstr "Introducció" + +#: FMain.class:108 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Ordeno, d'acord amb els termes i condicions, els següents elements:" + +#: FMain.class:152 +msgid "I want to read the terms and conditions" +msgstr "Vull llegir els termes i condicions" + +#: FMain.class:158 +msgid "I want to save my order" +msgstr "Vull desar la meva comanda" + +#: FMain.class:138 +msgid "kg of frozen shrimps" +msgstr "kg de gambes congelades" + +#: FMain.class:129 +msgid "lines of code from gambas 3" +msgstr "Línies de codi del Gambas 3" + +#: FMain.class:100 +msgid "" +"Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n" +"

\n" +"

\n" +"To see the properties window, you have to do save the project in your home directory so that it is not write protected anymore.\n" +"

\n" +"To design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n" +"

\n" +"The essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n" +"

\n" +"In some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n" +"

\n" +msgstr "" +"La majoria de propietats d'aquest assistent es seleccionen a la finestra de propietats, i no al codi. Això es deu al fet que per dissenyar l'assistent es necessita la finestra de propietats de totes maneres. \n" +"

\n" +"

\n" +"Per veure la finestra de propietats, l'heu de desar al directori arrel del vostre projecte projecte, així ja no estarà protegit contra escriptura.\n" +"

\n" +"Per dissenyar les pàgines de l'assistent (o veure'n les propietats), a l'IDE feu clic al botó «Següent».\n" +"

\n" +"La propietat essencial del contenidor de l'assistent és Count. Aquesta defineix el nombre de passos que l'usuari haurà de fer. A l'última pàgina el botó «Següent» canvia automàticament a «D'acord» per acabar la tasca.\n" +"

\n" +"En algunes situacions podeu ometre els passos de l'assistent. Trobareu això en el codi de les caselles de verificació en el pas 2.\n" +"

\n" + +#: FMain.class:177 +msgid "Please enter your address here:" +msgstr "Introduïu la vostra adreça aquí:" + +#: FMain.class:184 +msgid "Save your order" +msgstr "Deseu la vostra comanda" + +#: FMain.class:196 +msgid "Send your order" +msgstr "Envieu la vostra comanda" + +#: FMain.class:162 +msgid "Terms and Conditions" +msgstr "Termes i condicions" + +#: FMain.class:204 +msgid "The following items:" +msgstr "Els següents articles:" + +#: FMain.class:189 +msgid "Where do you want to save your order?" +msgstr "On voleu desar la vostra comanda?" + +#: FMain.class:209 +msgid "will be delivered to:" +msgstr "serà lliurat a:" + +#: .project:1 +msgid "Wizard example" +msgstr "Exemple d'assistent" + +#: FMain.class:26 +msgid "" +"You didn't enter your address.\n" +"Your order can't be submitted." +msgstr "" +"No heu entrat la vostra adreça.\n" +"No es pot processar la vostra comanda." + +#: FMain.class:173 +msgid "Your address" +msgstr "La vostra adreça " + +#: FMain.class:103 +msgid "Your order" +msgstr "La vostra comanda" + +#: FMain.class:219 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "La vostra comanda està llesta per a ser enviada. Reviseu si tot és correcte." + +#: FMain.class:39 +msgid "" +"Your order was submitted successfully.\n" +"The wizard will close now." +msgstr "" +"La vostra comanda s'ha processat correctament.\n" +"L'assistent s'acaba aquí." + diff --git a/app/examples/Control/Wizard/.lang/cs.po b/app/examples/Control/Wizard/.lang/cs.po new file mode 100644 index 00000000..87ec635b --- /dev/null +++ b/app/examples/Control/Wizard/.lang/cs.po @@ -0,0 +1,138 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Wizard example" +msgstr "Příklad průvodce" + +#: FMain.class:26 +msgid "" +"You didn't enter your address.\n" +"Your order can't be submitted." +msgstr "" +"Nezadali jste Vaši adresu.\n" +"Vaše objednávka je nedoručitelná." + +#: FMain.class:39 +msgid "" +"Your order was submitted successfully.\n" +"The wizard will close now." +msgstr "" +"Vaše objednávka byla úspěšně odeslána.\n" +"Průvodce bude ukončen." + +#: FMain.form:38 +msgid "Introduction" +msgstr "Úvod" + +#: FMain.form:43 +msgid "" +"Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n" +"

\n" +"

\n" +"To see the properties window, you have to do save the project in your home directory so that it is not write protected anymore.\n" +"

\n" +"To design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n" +"

\n" +"The essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n" +"

\n" +"In some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n" +"

\n" +msgstr "" +"Většina z vlastností tohoto průvodce jsou vybrané v okně vlastností, nikoli v kódu. Je to proto, že pro navrhování průvodce budete potřebovat okno vlastností tak jako tak. \n" +"

\n" +"

\n" +"Chcete-li zobrazit v okně vlastností, co musíte udělat, uložit projekt ve svém domovském adresáři, a ten není chráněn proti zápisu.\n" +"

\n" +"Chcete-li návrh stránkek průvodce (nebo viz vlastnosti), stačí kliknout na IDE na \"Další\" tlačítko.\n" +"

\n" +"V základní vlastnost je průvodce kontejneru Count(počet). Toto určuje počet kroků, které bude muset uživatele proklikat. Na poslední stráncena tlačítko \"Další\" se automaticky změní na \"OK\"prodokončení úkolu.\n" +"

\n" +"V některých situacích byste mohli chtít přeskočit kroky průvodce. Najdete to v kódu políček v kroku 2.\n" +"

\n" + +#: FMain.form:46 +msgid "Your order" +msgstr "Váše objednávka" + +#: FMain.form:51 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Objednávka, v souladu s podmínkami, následující položky:" + +#: FMain.form:57 +msgid "Amount" +msgstr "Částka" + +#: FMain.form:67 +msgid "Article" +msgstr "Předmět" + +#: FMain.form:72 +msgid "lines of code from gambas 3" +msgstr "řádků ku z gambasu 3" + +#: FMain.form:81 +msgid "kg of frozen shrimps" +msgstr "kg mražených krevet" + +#: FMain.form:90 +msgid "image files of the Gambas logo" +msgstr "obrazové soubory s logem Gambasu" + +#: FMain.form:95 +msgid "I want to read the terms and conditions" +msgstr "Chci si přečíst podmínky" + +#: FMain.form:101 +msgid "I want to save my order" +msgstr "Chci uložit moji objednávku" + +#: FMain.form:105 +msgid "Terms and Conditions" +msgstr "Podmínky" + +#: FMain.form:112 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Protože se jedná pouze o příklad,
nic se neobjedná,
nic se neuloženo
a nic nebude posláno." + +#: FMain.form:116 +msgid "Your address" +msgstr "Vaše adresa" + +#: FMain.form:120 +msgid "Please enter your address here:" +msgstr "Prosím, vložte sem vaši adresu:" + +#: FMain.form:127 +msgid "Save your order" +msgstr "Uložit objednávku" + +#: FMain.form:132 +msgid "Where do you want to save your order?" +msgstr "Kam chcete uložit objednávku?" + +#: FMain.form:139 +msgid "Send your order" +msgstr "Poslat objednávku" + +#: FMain.form:147 +msgid "The following items:" +msgstr "Následující položky:" + +#: FMain.form:152 +msgid "will be delivered to:" +msgstr "bude dodáno:" + +#: FMain.form:162 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "Vaše objednávka je nyní připravena k odeslání. Zkontrolujte prosím, zda je vše v pořádku." diff --git a/app/examples/Control/Wizard/.lang/de.po b/app/examples/Control/Wizard/.lang/de.po new file mode 100644 index 00000000..3860e21b --- /dev/null +++ b/app/examples/Control/Wizard/.lang/de.po @@ -0,0 +1,138 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Wizard example" +msgstr "Beispiel: Wizard" + +#: FMain.class:26 +msgid "" +"You didn't enter your address.\n" +"Your order can't be submitted." +msgstr "" +"Sie haben Ihre Adresse nicht angegeben.\n" +"Ihre Bestellung kann nicht übermittelt werden." + +#: FMain.class:39 +msgid "" +"Your order was submitted successfully.\n" +"The wizard will close now." +msgstr "" +"Ihre Bestellung wurde erfolgreich übermittelt.\n" +"Der Assistent wird nun geschlossen." + +#: FMain.form:38 +msgid "Introduction" +msgstr "Einleitung" + +#: FMain.form:43 +msgid "" +"Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n" +"

\n" +"

\n" +"To see the properties window, you have to do save the project in your home directory so that it is not write protected anymore.\n" +"

\n" +"To design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n" +"

\n" +"The essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n" +"

\n" +"In some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n" +"

\n" +msgstr "" +"Die meisten Einstellungen dieses Assistenten sind im Eigenschaftenfenster eingestellt, nicht mittels Code. Weil man zum Design sowieso nicht um das Eigenschaftenfenster herumkommt. \n" +"

\n" +"

\n" +"Um das Eigenschaftenfenster zu sehen, musst du das Projekt in deinem home-Verzeichnis speichern, wo es nicht mehr schreibgeschützt ist.\n" +"

\n" +"Um die Seiten des Assistenten zu gestalten (oder die Eigenschaften anzusehen), klicke in der IDE einfach auf die \"Weiter\" Schaltfläche.\n" +"

\n" +"Die zentrale Eigenschaft des Assistenten ist Count. Damit wird die Anzahl der Schritte definiert, die der Anwender durchklicken muss. Auf der letzten Seite ändert sich \"Weiter\" automatisch in \"OK\", um den Assistenten abzuschließen.\n" +"

\n" +"In manchen Situationen mag man Schritte des Assistenten überspringen. Siehe hierzu den Code der CheckBoxen in Schritt 2.\n" +"

\n" + +#: FMain.form:46 +msgid "Your order" +msgstr "Ihre Bestellung" + +#: FMain.form:51 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Ich bestelle gemäß den Geschäftsbedingungen folgende Artikel:" + +#: FMain.form:57 +msgid "Amount" +msgstr "Menge" + +#: FMain.form:67 +msgid "Article" +msgstr "Artikel" + +#: FMain.form:72 +msgid "lines of code from gambas 3" +msgstr "Codezeilen aus Gambas 3" + +#: FMain.form:81 +msgid "kg of frozen shrimps" +msgstr "kg tiefgekühlte Scampi" + +#: FMain.form:90 +msgid "image files of the Gambas logo" +msgstr "Bilddateien des Gambas-Logos" + +#: FMain.form:95 +msgid "I want to read the terms and conditions" +msgstr "Ich möchte die Geschäftsbedingungen lesen" + +#: FMain.form:101 +msgid "I want to save my order" +msgstr "Ich möchte meine Bestellung speichern" + +#: FMain.form:105 +msgid "Terms and Conditions" +msgstr "Geschäftsbedingungen" + +#: FMain.form:112 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Da dies nur ein Beispiel ist,
wird nichts bestellt,
nichts gespeichert,
und nichts gesendet." + +#: FMain.form:116 +msgid "Your address" +msgstr "Ihre Adresse" + +#: FMain.form:120 +msgid "Please enter your address here:" +msgstr "Bitte geben Sie hier ihre Adresse ein:" + +#: FMain.form:127 +msgid "Save your order" +msgstr "Bestellung speichern" + +#: FMain.form:132 +msgid "Where do you want to save your order?" +msgstr "Wo möchten Sie Ihre Bestellung speichern?" + +#: FMain.form:139 +msgid "Send your order" +msgstr "Bestellung übermitteln" + +#: FMain.form:147 +msgid "The following items:" +msgstr "Die folgenden Artikel:" + +#: FMain.form:152 +msgid "will be delivered to:" +msgstr "werden geliefert an:" + +#: FMain.form:162 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "Ihre Bestellung kann nun übermittelt werden. Bitte überprüfen Sie, ob alle Angaben korrekt sind." diff --git a/app/examples/Control/Wizard/.lang/nl.po b/app/examples/Control/Wizard/.lang/nl.po new file mode 100644 index 00000000..51708e2c --- /dev/null +++ b/app/examples/Control/Wizard/.lang/nl.po @@ -0,0 +1,107 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Wizard 3.6.0\n" +"PO-Revision-Date: 2014-10-10 13:56 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Wizard example" +msgstr "Wizard voorbeeld" + +#: FMain.form:38 +msgid "Introduction" +msgstr "Introducties" + +#: FMain.form:43 +msgid "Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n

\n

\nTo see the properties window, you have to do save the project in your home directory so that it is not write protected anymore.\n

\nTo design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n

\nThe essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n

\nIn some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n

\n" +msgstr "" + +#: FMain.form:46 +msgid "Your order" +msgstr "Je order" + +#: FMain.form:51 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Ik bestel, volgens de voorwaarden en condities, de volgende items:" + +#: FMain.form:57 +msgid "Amount" +msgstr "Bedrag" + +#: FMain.form:67 +msgid "Article" +msgstr "Artikel" + +#: FMain.form:72 +msgid "lines of code from gambas 3" +msgstr "aantal code lijnen van gambas3" + +#: FMain.form:81 +msgid "kg of frozen shrimps" +msgstr "kg bevroren garnalen" + +#: FMain.form:90 +msgid "image files of the Gambas logo" +msgstr "afbeeldingsbestanden van het Gambas logo" + +#: FMain.form:95 +msgid "I want to read the terms and conditions" +msgstr "Ik wil de voorwaarden en condities lezen" + +#: FMain.form:101 +msgid "I want to save my order" +msgstr "Ik wil mijn order opslaan" + +#: FMain.form:105 +msgid "Terms and Conditions" +msgstr "Voorwaarden en condities" + +#: FMain.form:112 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Gezien dit slechts een voorbeeld is zal er,
niets besteld worden,
niets opgeslagen worden,
en niets verzonden worden." + +#: FMain.form:116 +msgid "Your address" +msgstr "Je adres" + +#: FMain.form:120 +msgid "Please enter your address here:" +msgstr "Voer je adres hier in:" + +#: FMain.form:127 +msgid "Save your order" +msgstr "Order opslaan" + +#: FMain.form:132 +msgid "Where do you want to save your order?" +msgstr "Waar wil je de order opslaan?" + +#: FMain.form:139 +msgid "Send your order" +msgstr "Verzend je order" + +#: FMain.form:147 +msgid "The following items:" +msgstr "De volgende items:" + +#: FMain.form:152 +msgid "will be delivered to:" +msgstr "zal afgeleverd worden aan:" + +#: FMain.form:162 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "Je order is nu klaar voor verzending. Controleer of alles in orde is." + +#: FMain.class:34 +msgid "Your order was submitted successfully.\nThe wizard will close now." +msgstr "Je opdracht is succesvol ingedient.\nDe wizard zal nu sluiten." + +#: FMain.class:58 +msgid "You didn't enter your address.\nYour order can't be submitted." +msgstr "Je hebt je adres niet ingevoerd.\nDe order kan niet worden ingedient." + diff --git a/app/examples/Control/Wizard/.lang/ru.po b/app/examples/Control/Wizard/.lang/ru.po new file mode 100644 index 00000000..c92d60c6 --- /dev/null +++ b/app/examples/Control/Wizard/.lang/ru.po @@ -0,0 +1,152 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Control/Wizard/.project:18 +msgid "Wizard example" +msgstr "Пример мастера" + +#: app/examples/Control/Wizard/.src/FMain.class:34 +msgid "" +"Your order was submitted successfully.\n" +"The wizard will close now." +msgstr "" +"Ваш заказ был успешно отправлен.\n" +"Мастер сейчас закроется." + +#: app/examples/Control/Wizard/.src/FMain.class:58 +msgid "" +"You didn't enter your address.\n" +"Your order can't be submitted." +msgstr "" +"Вы не ввели свой адрес.\n" +"Ваш заказ не может быть отправлен." + +#: app/examples/Control/Wizard/.src/FMain.form:12 +msgid "Introduction" +msgstr "Введение" + +#: app/examples/Control/Wizard/.src/FMain.form:16 +msgid "" +"Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n" +"

\n" +"

\n" +"To see the properties window, you have to do save the project in your home directory so that it is not write protected anymore.\n" +"

\n" +"To design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n" +"

\n" +"The essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n" +"

\n" +"In some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n" +"

\n" +msgstr "" +"Большинство свойств этого мастера выбираются в окне свойств, а не по коду. Это потому, что для разработки мастера вам всё равно нужно окно свойств. \n" +"

\n" +"

\n" +"Чтобы увидеть окно свойств, вам нужно сохранить проект в вашем домашнем каталоге, чтобы он больше не был защищён от записи.\n" +"

\n" +"Чтобы создать страницы мастера (или просмотреть свойства), просто нажмите в среде IDE на кнопку «Далее».\n" +"

\n" +"Основным свойством контейнера мастера является Счёт. Это определяет количество шагов, которые пользователь должен будет пройти. На последней странице кнопка «Далее» автоматически меняется на «ОК» для завершения задачи.\n" +"

\n" +"В некоторых ситуациях вы можете пропустить шаги мастера. Вы найдёте это в коде чекбоксов (флажков) в шаге 2.\n" +"

\n" + +#: app/examples/Control/Wizard/.src/FMain.form:19 +msgid "Your order" +msgstr "Ваш заказ" + +#: app/examples/Control/Wizard/.src/FMain.form:23 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Я заказываю в соответствии с положения и условиями следующие пункты:" + +#: app/examples/Control/Wizard/.src/FMain.form:28 +msgid "Amount" +msgstr "Кол-во" + +#: app/examples/Control/Wizard/.src/FMain.form:36 +msgid "Article" +msgstr "Статья" + +#: app/examples/Control/Wizard/.src/FMain.form:40 +msgid "lines of code from gambas 3" +msgstr "строк кода из gambas 3" + +#: app/examples/Control/Wizard/.src/FMain.form:47 +msgid "kg of frozen shrimps" +msgstr "кг замороженных креветок" + +#: app/examples/Control/Wizard/.src/FMain.form:54 +msgid "image files of the Gambas logo" +msgstr "файлов изображений логотипа Gambas" + +#: app/examples/Control/Wizard/.src/FMain.form:58 +msgid "I want to read the terms and conditions" +msgstr "Я хочу прочитать положения и условия" + +#: app/examples/Control/Wizard/.src/FMain.form:63 +msgid "I want to save my order" +msgstr "Я хочу сохранить свой заказ" + +#: app/examples/Control/Wizard/.src/FMain.form:67 +msgid "Terms and Conditions" +msgstr "Условия и положения" + +#: app/examples/Control/Wizard/.src/FMain.form:73 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Поскольку это только пример,
ничего не будет заказано,
ничего не будет сохранено
и ничего не будет отправлено." + +#: app/examples/Control/Wizard/.src/FMain.form:77 +msgid "Your address" +msgstr "Ваш адрес" + +#: app/examples/Control/Wizard/.src/FMain.form:80 +msgid "Please enter your address here:" +msgstr "Пожалуйста, введите ваш адрес здесь:" + +#: app/examples/Control/Wizard/.src/FMain.form:86 +msgid "Save your order" +msgstr "Сохранить ваш заказ" + +#: app/examples/Control/Wizard/.src/FMain.form:90 +msgid "Where do you want to save your order?" +msgstr "Где вы хотите сохранить свой заказ?" + +#: app/examples/Control/Wizard/.src/FMain.form:96 +msgid "Send your order" +msgstr "Отправить ваш заказ" + +#: app/examples/Control/Wizard/.src/FMain.form:102 +msgid "The following items:" +msgstr "Следующие пункты:" + +#: app/examples/Control/Wizard/.src/FMain.form:106 +msgid "will be delivered to:" +msgstr "будет доставлено на:" + +#: app/examples/Control/Wizard/.src/FMain.form:114 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "Ваш заказ готов к отправке. Пожалуйста, проверьте, всё ли правильно." + diff --git a/app/examples/Control/Wizard/.project b/app/examples/Control/Wizard/.project new file mode 100644 index 00000000..88700328 --- /dev/null +++ b/app/examples/Control/Wizard/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Title=Wizard example +Startup=FMain +Icon=wizard.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop +Component=gb.pcre +Authors="Matti, slightly modified by Benoît Minisini" +TabSize=2 +Translate=1 +Language=de +Packager=1 diff --git a/app/examples/Control/Wizard/.src/FMain.class b/app/examples/Control/Wizard/.src/FMain.class new file mode 100644 index 00000000..7b91b974 --- /dev/null +++ b/app/examples/Control/Wizard/.src/FMain.class @@ -0,0 +1,65 @@ +' Gambas class file + +Public Sub _new() + Me.Center +End + +Public Sub Wizard1_Cancel() ' user clicks abort + + Me.Close + +End + +Public Sub Wizard1_Change() ' user clicks next + + Select Case Wizard1.Index ' be aware that the index starts with 0! + + Case 1 + vbCode.setFocus + + Case 3 + txaAddress.SetFocus + + Case 5 + + txlOrder.Text = vbCode.Text & " " & lblCode.Text & "
" & vbFrozen.Text & " " & lblFrozen.Text & "
" & vbImg.Text & " " & lblImg.Text + txlAddress.Text = Replace(txaAddress.Text, "\n", "
") + + End Select + +End + +Public Sub Wizard1_Close() ' user clicks ok on the last page + + Message.Info(("Your order was submitted successfully.\nThe wizard will close now.")) + Me.Close + +End + +Public Sub chbTerms_Click() ' user wants (not) to read "terms & conditions" + + Wizard1[2].Enabled = chbTerms.Value ' step 3 is visible or not, according to this decision + +End + +Public Sub chbSave_Click() ' user wants (not) to save + + Wizard1[4].Enabled = chbSave.Value ' step 5 is visible or not, according to this decision + +End + +Public Sub Wizard1_BeforeChange() + + Select Case Wizard1.Index + + Case 4 + + If txaAddress.Text = "" Then + Message.Error(("You didn't enter your address.\nYour order can't be submitted.")) + Wizard1.Index = 3 ' move back to step 4 + Stop Event + Endif + + End Select + +End diff --git a/app/examples/Control/Wizard/.src/FMain.form b/app/examples/Control/Wizard/.src/FMain.form new file mode 100644 index 00000000..0625d087 --- /dev/null +++ b/app/examples/Control/Wizard/.src/FMain.form @@ -0,0 +1,118 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,83,65) + Resizable = False + { Wizard1 Wizard + MoveScaled(1,1,81,63) + Count = 6 + TextFont = Font["Bold,Italic,+3"] + ShowIndex = True + Index = 0 + Text = ("Introduction") + { TextLabel1 TextLabel + MoveScaled(2,2,76,48) + Font = Font["+1"] + Text = ("Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n

\n

\nTo see the properties window, you have to do save the project in your home directory so that it is not write protected anymore.\n

\nTo design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n

\nThe essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n

\nIn some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n

\n") + } + Index = 1 + Text = ("Your order") + { TextLabel5 TextLabel + MoveScaled(2,2,75,7) + Font = Font["Bold,+1"] + Text = ("I order, according to the terms & conditions, the following items:") + } + { Label1 Label + MoveScaled(2,8,11,4) + Font = Font["+1"] + Text = ("Amount") + } + { vbCode ValueBox + MoveScaled(2,13,8,4) + } + { Label2 Label + MoveScaled(13,8,11,4) + Font = Font["+1"] + Text = ("Article") + } + { lblCode Label + MoveScaled(13,13,63,4) + Text = ("lines of code from gambas 3") + } + { vbFrozen ValueBox + MoveScaled(2,18,8,4) + } + { lblFrozen Label + MoveScaled(13,18,63,4) + Text = ("kg of frozen shrimps") + } + { vbImg ValueBox + MoveScaled(2,23,8,4) + } + { lblImg Label + MoveScaled(13,23,63,4) + Text = ("image files of the Gambas logo") + } + { chbTerms CheckBox + MoveScaled(2,31,75,5) + Text = ("I want to read the terms and conditions") + Value = CheckBox.True + } + { chbSave CheckBox + MoveScaled(2,37,40,4) + Text = ("I want to save my order") + Value = CheckBox.True + } + Index = 2 + Text = ("Terms and Conditions") + { TextLabel6 TextLabel + MoveScaled(2,2,76,41) + Font = Font["Serif,Bold,Italic,+1"] + Background = Color.TextBackground + Padding = 8 + Text = ("As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent.") + Border = Border.Plain + } + Index = 3 + Text = ("Your address") + { Label3 Label + MoveScaled(2,2,41,3) + Text = ("Please enter your address here:") + } + { txaAddress TextArea + MoveScaled(2,6,76,15) + } + Index = 4 + Text = ("Save your order") + { Label4 Label + MoveScaled(2,1,66,4) + Font = Font["Bold"] + Text = ("Where do you want to save your order?") + } + { DirChooser1 DirChooser + MoveScaled(2,5,76,38) + } + Index = 5 + Text = ("Send your order") + { txlOrder TextLabel + MoveScaled(4,13,70,8) + } + { Label6 Label + MoveScaled(2,9,22,3) + Text = ("The following items:") + } + { Label7 Label + MoveScaled(2,23,25,3) + Text = ("will be delivered to:") + } + { txlAddress TextLabel + MoveScaled(4,27,72,15) + } + { TextLabel2 TextLabel + MoveScaled(2,1,75,7) + Font = Font["Bold,+1"] + Text = ("Your order is now ready to be sent. Please check if everything is correct.") + } + Index = 0 + } +} diff --git a/app/examples/Control/Wizard/wizard.png b/app/examples/Control/Wizard/wizard.png new file mode 100644 index 0000000000000000000000000000000000000000..fff0e3f12ac0dfcd7a5137bca353a46c4d506c4d GIT binary patch literal 1510 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 e@IO~y@%VokW#eSwI&%;J0000, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Database\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:33+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FTest.class:136 +msgid "Active" +msgstr "Actiu" + +#: FTest.class:131 +msgid "Birth" +msgstr "Dia de naixement" + +#: FTest.class:60 +msgid "Bound controls example" +msgstr "Exemple de controls del límit" + +#: FMain.class:321 +msgid "" +"Bound controls\n" +"example..." +msgstr "" +"Controls del límit\n" +"exemple..." + +#: FTest.class:116 +msgid "Color" +msgstr "Color" + +#: FTest.class:146 +msgid "Comment" +msgstr "Comentari" + +#: FMain.class:249 +msgid "Connect" +msgstr "Connecta" + +#: FMain.class:220 +msgid "Connection" +msgstr "Connexió" + +#: FMain.class:298 +msgid "Create" +msgstr "Crea" + +#: FMain.class:282 +msgid "Create database if it does not exist" +msgstr "Crea la base de dades si no existeix" + +#: FMain.class:224 +msgid "Database" +msgstr "Base de dades" + +#: FMain.class:214 +msgid "Database example" +msgstr "Exemple de base de dades" + +#: FMain.class:287 +msgid "Debug" +msgstr "Depuració" + +#: FMain.class:303 +msgid "Delete" +msgstr "Suprimeix" + +#: FMain.class:308 +msgid "&Fill with" +msgstr "&Omple amb" + +#: FMain.class:255 +msgid "firebird" +msgstr "-" + +#: FTest.class:121 +msgid "First Name" +msgstr "Nom de pila" + +#: FMain.class:234 +msgid "Host" +msgstr "Ordinador central" + +#: FTest.class:111 +msgid "Id" +msgstr "Identificador" + +#: FMain.class:255 +msgid "mysql" +msgstr "-" + +#: FTest.class:126 +msgid "Name" +msgstr "Nom" + +#: FMain.class:255 +msgid "odbc" +msgstr "-" + +#: FMain.class:244 +msgid "Password" +msgstr "Contrasenya" + +#: FMain.class:255 +msgid "postgresql" +msgstr "-" + +#: FMain.class:326 +msgid "Records" +msgstr "Registres" + +#: FMain.class:337 +msgid "Run..." +msgstr "Executa..." + +#: FTest.class:141 +msgid "Salary" +msgstr "Salari" + +#: FMain.class:255 +msgid "sqlite3" +msgstr "-" + +#: FMain.class:333 +#: FRequest.class:27 +msgid "SQL request" +msgstr "Petició SQL" + +#: FMain.class:294 +msgid "Tables 'test' && 'color'" +msgstr "Taules «comprova» && «color»" + +#: FMain.class:265 +msgid "test" +msgstr "Comprova" + +#: FTest.class:78 +msgid "Test form" +msgstr "Formulari de prova" + +#: FMain.class:229 +msgid "Type" +msgstr "Tipus" + +#: FMain.class:239 +msgid "User" +msgstr "Usuari" + diff --git a/app/examples/Database/Database/.lang/cs.po b/app/examples/Database/Database/.lang/cs.po new file mode 100644 index 00000000..1961309b --- /dev/null +++ b/app/examples/Database/Database/.lang/cs.po @@ -0,0 +1,157 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:33 +msgid "Database example" +msgstr "Příklad databáze" + +#: FMain.form:39 +msgid "Connection" +msgstr "Připojení" + +#: FMain.form:43 +msgid "Database" +msgstr "Databáze" + +#: FMain.form:48 +msgid "Type" +msgstr "Typ" + +#: FMain.form:53 +msgid "Host" +msgstr "-" + +#: FMain.form:58 +msgid "User" +msgstr "Uživatel" + +#: FMain.form:63 +msgid "Password" +msgstr "Heslo" + +#: FMain.form:68 +msgid "Connect" +msgstr "Připojit" + +#: FMain.form:73 +msgid "firebird" +msgstr "-" + +#: FMain.form:73 +msgid "mysql" +msgstr "-" + +#: FMain.form:73 +msgid "odbc" +msgstr "-" + +#: FMain.form:73 +msgid "postgresql" +msgstr "-" + +#: FMain.form:73 +msgid "sqlite3" +msgstr "-" + +#: FMain.form:83 +msgid "test" +msgstr "-" + +#: FMain.form:98 +msgid "Create database if it does not exist" +msgstr "Vytvořit databázi když neexistuje" + +#: FMain.form:103 +msgid "Debug" +msgstr "Ladit" + +#: FMain.form:108 +msgid "Port" +msgstr "-" + +#: FMain.form:119 +msgid "Tables 'test' && 'color'" +msgstr "Takulky 'test' && 'color'" + +#: FMain.form:123 +msgid "Create" +msgstr "Vytvořit" + +#: FMain.form:128 +msgid "Delete" +msgstr "Smazat" + +#: FMain.form:133 +msgid "&Fill with" +msgstr "&Vyplnit s" + +#: FMain.form:146 +msgid "" +"Bound controls\n" +"example..." +msgstr "" +"Příklad na\n" +"vázané ovládací\n" +"prvky..." + +#: FMain.form:151 +msgid "Records" +msgstr "Záznamy" + +#: FMain.form:158 FRequest.class:26 +msgid "SQL request" +msgstr "SQL dotaz" + +#: FMain.form:162 +msgid "Run..." +msgstr "Spustit..." + +#: FTest.form:31 +msgid "Bound controls example" +msgstr "Příklad na vázané ovládací prvky" + +#: FTest.form:49 +msgid "Test form" +msgstr "Testovací formulář" + +#: FTest.form:82 +msgid "Id" +msgstr "-" + +#: FTest.form:87 +msgid "Color" +msgstr "-" + +#: FTest.form:92 +msgid "First Name" +msgstr "Jméno" + +#: FTest.form:97 +msgid "Name" +msgstr "Název" + +#: FTest.form:102 +msgid "Birth" +msgstr "Narození" + +#: FTest.form:107 +msgid "Active" +msgstr "Aktivita" + +#: FTest.form:112 +msgid "Salary" +msgstr "Plat" + +#: FTest.form:117 +msgid "Comment" +msgstr "Komentář" diff --git a/app/examples/Database/Database/.lang/de.po b/app/examples/Database/Database/.lang/de.po new file mode 100644 index 00000000..8362dcd4 --- /dev/null +++ b/app/examples/Database/Database/.lang/de.po @@ -0,0 +1,153 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:33 +msgid "Database example" +msgstr "Datenbank-Beispiel" + +#: FMain.form:39 +msgid "Connection" +msgstr "Verbindung" + +#: FMain.form:43 +msgid "Database" +msgstr "Datenbank" + +#: FMain.form:48 +msgid "Type" +msgstr "Typ" + +#: FMain.form:53 +msgid "Host" +msgstr "-" + +#: FMain.form:58 +msgid "User" +msgstr "Benutzer" + +#: FMain.form:63 +msgid "Password" +msgstr "Passwort" + +#: FMain.form:68 +msgid "Connect" +msgstr "Verbinden" + +#: FMain.form:73 +msgid "firebird" +msgstr "-" + +#: FMain.form:73 +msgid "mysql" +msgstr "-" + +#: FMain.form:73 +msgid "odbc" +msgstr "-" + +#: FMain.form:73 +msgid "postgresql" +msgstr "-" + +#: FMain.form:73 +msgid "sqlite3" +msgstr "-" + +#: FMain.form:83 +msgid "test" +msgstr "-" + +#: FMain.form:98 +msgid "Create database if it does not exist" +msgstr "Datenbank erstellen, falls sie nicht existiert" + +#: FMain.form:103 +msgid "Debug" +msgstr "Debuggen" + +#: FMain.form:108 +msgid "Port" +msgstr "-" + +#: FMain.form:119 +msgid "Tables 'test' && 'color'" +msgstr "Tabellen 'test' && 'color'" + +#: FMain.form:123 +msgid "Create" +msgstr "Erstellen" + +#: FMain.form:128 +msgid "Delete" +msgstr "Löschen" + +#: FMain.form:133 +msgid "&Fill with" +msgstr "&Füllen mit" + +#: FMain.form:146 +msgid "Bound controls\nexample..." +msgstr "Beispiel:\nGebundene\nSchaltflächen..." + +#: FMain.form:151 +msgid "Records" +msgstr "Datensätze" + +#: FMain.form:158 FRequest.class:26 +msgid "SQL request" +msgstr "SQL-Abfrage" + +#: FMain.form:162 +msgid "Run..." +msgstr "Ausführen..." + +#: FTest.form:31 +msgid "Bound controls example" +msgstr "Beispiel für gebundene Schaltflächen" + +#: FTest.form:49 +msgid "Test form" +msgstr "Testformular" + +#: FTest.form:82 +msgid "Id" +msgstr "ID" + +#: FTest.form:87 +msgid "Color" +msgstr "Farbe" + +#: FTest.form:92 +msgid "First Name" +msgstr "Vorname" + +#: FTest.form:97 +msgid "Name" +msgstr "Nachname" + +#: FTest.form:102 +msgid "Birth" +msgstr "Geburtsdatum" + +#: FTest.form:107 +msgid "Active" +msgstr "Aktiv" + +#: FTest.form:112 +msgid "Salary" +msgstr "Gehalt" + +#: FTest.form:117 +msgid "Comment" +msgstr "Kommentar" + diff --git a/app/examples/Database/Database/.lang/es.po b/app/examples/Database/Database/.lang/es.po new file mode 100644 index 00000000..d41e9b09 --- /dev/null +++ b/app/examples/Database/Database/.lang/es.po @@ -0,0 +1,151 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.class:211 +msgid "Database example" +msgstr "Ejemplo de base de datos" + +#: FMain.class:217 +msgid "Connection" +msgstr "Conexión" + +#: FMain.class:221 +msgid "Database" +msgstr "Base de datos" + +#: FMain.class:226 +msgid "Type" +msgstr "Tipo" + +#: FMain.class:231 +msgid "Host" +msgstr "Servidor" + +#: FMain.class:236 +msgid "User" +msgstr "Usuario" + +#: FMain.class:241 +msgid "Password" +msgstr "Contraseña" + +#: FMain.class:246 +msgid "Connect" +msgstr "Conectar" + +#: FMain.class:251 +msgid "firebird" +msgstr "firebird" + +#: FMain.class:251 +msgid "mysql" +msgstr "mysql" + +#: FMain.class:251 +msgid "odbc" +msgstr "odbc" + +#: FMain.class:251 +msgid "postgresql" +msgstr "postgresql" + +#: FMain.class:251 +msgid "sqlite3" +msgstr "sqlite3" + +#: FMain.class:261 +msgid "test" +msgstr "test" + +#: FMain.class:276 +msgid "Create database if it does not exist" +msgstr "Crear la base de datos si no existe" + +#: FMain.class:281 +msgid "Debug" +msgstr "Depurar" + +#: FMain.class:288 +msgid "Tables 'test' && 'color'" +msgstr "Tablas 'test' && 'color'" + +#: FMain.class:292 +msgid "Create" +msgstr "Crear" + +#: FMain.class:297 +msgid "Delete" +msgstr "Borrar" + +#: FMain.class:302 +msgid "&Fill with" +msgstr "&Llenar con" + +#: FMain.class:315 +msgid "" +"Bound controls\n" +"example..." +msgstr "" +"Ejemplo de\n" +"controles de acceso..." + +#: FMain.class:320 +msgid "Records" +msgstr "Registros" + +#: FMain.class:327 FRequest.class:26 +msgid "SQL request" +msgstr "Consulta SQL" + +#: FMain.class:331 +msgid "Run..." +msgstr "Correr..." + +#: FTest.class:53 +msgid "Bound controls example" +msgstr "Ejemplo de controles de acceso" + +#: FTest.class:71 +msgid "Test form" +msgstr "Formulario de prueba" + +#: FTest.class:104 +msgid "Id" +msgstr "Id" + +#: FTest.class:109 +msgid "Color" +msgstr "Color" + +#: FTest.class:114 +msgid "First Name" +msgstr "Primer nombre" + +#: FTest.class:119 +msgid "Name" +msgstr "Nombre" + +#: FTest.class:124 +msgid "Birth" +msgstr "Nacimiento" + +#: FTest.class:129 +msgid "Active" +msgstr "Activo" + +#: FTest.class:134 +msgid "Salary" +msgstr "Salario" + +#: FTest.class:139 +msgid "Comment" +msgstr "Comentario" diff --git a/app/examples/Database/Database/.lang/nl.po b/app/examples/Database/Database/.lang/nl.po new file mode 100644 index 00000000..73496e2f --- /dev/null +++ b/app/examples/Database/Database/.lang/nl.po @@ -0,0 +1,147 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Database 3.5.90\n" +"PO-Revision-Date: 2014-09-24 16:00 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:33 +msgid "Database example" +msgstr "Database voorbeeld" + +#: FMain.form:39 +msgid "Connection" +msgstr "Verbinding" + +#: FMain.form:43 +msgid "Database" +msgstr "-" + +#: FMain.form:48 +msgid "Type" +msgstr "-" + +#: FMain.form:53 +msgid "Host" +msgstr "-" + +#: FMain.form:58 +msgid "User" +msgstr "Gebruiker" + +#: FMain.form:63 +msgid "Password" +msgstr "Wachtwoord" + +#: FMain.form:68 +msgid "Connect" +msgstr "Verbind" + +#: FMain.form:73 +msgid "postgresql" +msgstr "-" + +#: FMain.form:73 +msgid "mysql" +msgstr "-" + +#: FMain.form:73 +msgid "sqlite3" +msgstr "-" + +#: FMain.form:73 +msgid "odbc" +msgstr "-" + +#: FMain.form:83 +msgid "test" +msgstr "-" + +#: FMain.form:98 +msgid "Create database if it does not exist" +msgstr "Creëer database indien deze niet bestaat" + +#: FMain.form:103 +msgid "Debug" +msgstr "Foutenopsporing" + +#: FMain.form:108 +msgid "Port" +msgstr "Poort" + +#: FMain.form:119 +msgid "Tables 'test' && 'color'" +msgstr "Tabellen 'test' && 'kleur'" + +#: FMain.form:123 +msgid "Create" +msgstr "Creëer" + +#: FMain.form:128 +msgid "Delete" +msgstr "Verwijder" + +#: FMain.form:133 +msgid "&Fill with" +msgstr "&Vullen met" + +#: FMain.form:146 +msgid "Bound controls\nexample..." +msgstr "Gebonden controles\nvoorbeeld..." + +#: FMain.form:151 +msgid "Records" +msgstr "-" + +#: FMain.form:158 FRequest.class:26 +msgid "SQL request" +msgstr "SQL verzoek" + +#: FMain.form:162 +msgid "Run..." +msgstr "Uitvoeren..." + +#: FTest.form:31 +msgid "Bound controls example" +msgstr "Gebonden controles voorbeeld" + +#: FTest.form:49 +msgid "Test form" +msgstr "Test formulier" + +#: FTest.form:82 +msgid "Id" +msgstr "-" + +#: FTest.form:87 +msgid "Color" +msgstr "Kleur" + +#: FTest.form:92 +msgid "First Name" +msgstr "Voornaam" + +#: FTest.form:97 +msgid "Name" +msgstr "Naam" + +#: FTest.form:102 +msgid "Birth" +msgstr "Geboorte" + +#: FTest.form:107 +msgid "Active" +msgstr "Actief" + +#: FTest.form:112 +msgid "Salary" +msgstr "Salaris" + +#: FTest.form:117 +msgid "Comment" +msgstr "Commentaar" + diff --git a/app/examples/Database/Database/.lang/ru.po b/app/examples/Database/Database/.lang/ru.po new file mode 100644 index 00000000..607d1915 --- /dev/null +++ b/app/examples/Database/Database/.lang/ru.po @@ -0,0 +1,198 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Database/Database/.project:21 app/examples/Database/Database/.src/FMain.form:5 +msgid "Database example" +msgstr "Пример базы данных" + +#: app/examples/Database/Database/.src/FMain.class:102 +msgid "Black" +msgstr "Чёрный" + +#: app/examples/Database/Database/.src/FMain.class:102 +msgid "White" +msgstr "Белый" + +#: app/examples/Database/Database/.src/FMain.class:102 +msgid "Red" +msgstr "Красный" + +#: app/examples/Database/Database/.src/FMain.class:102 +msgid "Green" +msgstr "Зелёный" + +#: app/examples/Database/Database/.src/FMain.class:102 +msgid "Blue" +msgstr "Синий" + +#: app/examples/Database/Database/.src/FMain.class:102 +msgid "Yellow" +msgstr "Жёлтый" + +#: app/examples/Database/Database/.src/FMain.class:102 +msgid "Transparent" +msgstr "Прозрачный" + +#: app/examples/Database/Database/.src/FMain.class:118 +msgid "Name #" +msgstr "Имя #" + +#: app/examples/Database/Database/.src/FMain.form:10 +msgid "Connection" +msgstr "Соединение" + +#: app/examples/Database/Database/.src/FMain.form:13 +msgid "Database" +msgstr "База данных" + +#: app/examples/Database/Database/.src/FMain.form:17 +msgid "Type" +msgstr "Тип" + +#: app/examples/Database/Database/.src/FMain.form:21 +msgid "Host" +msgstr "Хост" + +#: app/examples/Database/Database/.src/FMain.form:25 +msgid "User" +msgstr "Пользователь" + +#: app/examples/Database/Database/.src/FMain.form:29 +msgid "Password" +msgstr "Пароль" + +#: app/examples/Database/Database/.src/FMain.form:33 +msgid "Connect" +msgstr "Соединить" + +#: app/examples/Database/Database/.src/FMain.form:37 +msgid "postgresql" +msgstr "postgresql" + +#: app/examples/Database/Database/.src/FMain.form:37 +msgid "mysql" +msgstr "mysql" + +#: app/examples/Database/Database/.src/FMain.form:37 +msgid "sqlite3" +msgstr "sqlite3" + +#: app/examples/Database/Database/.src/FMain.form:37 +msgid "odbc" +msgstr "odbc" + +#: app/examples/Database/Database/.src/FMain.form:58 +msgid "Create database if it does not exist" +msgstr "Создать базу данных, если она не существует" + +#: app/examples/Database/Database/.src/FMain.form:62 +msgid "Debug" +msgstr "Отладка" + +#: app/examples/Database/Database/.src/FMain.form:66 +msgid "Port" +msgstr "Порт" + +#: app/examples/Database/Database/.src/FMain.form:75 +msgid "Tables 'test' && 'color'" +msgstr "Таблицы 'тест' && 'цвет'" + +#: app/examples/Database/Database/.src/FMain.form:78 +msgid "Create" +msgstr "Создать" + +#: app/examples/Database/Database/.src/FMain.form:82 +msgid "Delete" +msgstr "Удалить" + +#: app/examples/Database/Database/.src/FMain.form:86 +msgid "&Fill with" +msgstr "Наполнить" + +#: app/examples/Database/Database/.src/FMain.form:97 +msgid "" +"Bound controls\n" +"example..." +msgstr "" +"Пр-р элементов\n" +"упр. границами..." + +#: app/examples/Database/Database/.src/FMain.form:101 +msgid "Records" +msgstr "Записей" + +#: app/examples/Database/Database/.src/FMain.form:107 app/examples/Database/Database/.src/FRequest.class:26 +msgid "SQL request" +msgstr "SQL запрос" + +#: app/examples/Database/Database/.src/FMain.form:110 +msgid "Run..." +msgstr "Запуск..." + +#: app/examples/Database/Database/.src/FRequest.class:66 +msgid "Cannot exec request." +msgstr "Невозможно выполнить запрос." + +#: app/examples/Database/Database/.src/FTest.form:5 +msgid "Bound controls example" +msgstr "Пример элементов управления границами" + +#: app/examples/Database/Database/.src/FTest.form:21 +msgid "Test form" +msgstr "Тестовая форма" + +#: app/examples/Database/Database/.src/FTest.form:48 +msgid "Id" +msgstr "Идентификатор" + +#: app/examples/Database/Database/.src/FTest.form:52 +msgid "Color" +msgstr "Цвет" + +#: app/examples/Database/Database/.src/FTest.form:56 +msgid "First Name" +msgstr "Первое имя" + +#: app/examples/Database/Database/.src/FTest.form:60 +msgid "Name" +msgstr "Имя" + +#: app/examples/Database/Database/.src/FTest.form:64 +msgid "Birth" +msgstr "Дата рождения" + +#: app/examples/Database/Database/.src/FTest.form:68 +msgid "Active" +msgstr "Активен" + +#: app/examples/Database/Database/.src/FTest.form:72 +msgid "Salary" +msgstr "Зарплата" + +#: app/examples/Database/Database/.src/FTest.form:76 +msgid "Comment" +msgstr "Комментарий" + diff --git a/app/examples/Database/Database/.project b/app/examples/Database/Database/.project new file mode 100644 index 00000000..a89ac14f --- /dev/null +++ b/app/examples/Database/Database/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +Title=Database example +Startup=FMain +Icon=database.png +Version=3.16.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.db.form +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Packager=1 diff --git a/app/examples/Database/Database/.src/FMain.class b/app/examples/Database/Database/.src/FMain.class new file mode 100644 index 00000000..1379445f --- /dev/null +++ b/app/examples/Database/Database/.src/FMain.class @@ -0,0 +1,178 @@ +' Gambas class file + +Private $hConn As Connection + +Public Sub btnConnect_Click() + + Dim sName As String + + Try $hConn.Close + '$hConn = NEW Connection + + sName = txtName.Text + + With $hConn + .Type = cmbType.Text + .Host = txtHost.Text + .Port = txtPort.Text + .Login = txtUser.Text + .Password = txtPassword.Text + .Name = "" + End With + + If chkCreate.Value Then + + $hConn.Open + If Not $hConn.Databases.Exist(sName) Then + $hConn.Databases.Add(sName) + Endif + $hConn.Close + + Endif + + $hConn.Name = sName + $hConn.Open + + frmDatabase.Enabled = True + frmRequest.Enabled = True + +Catch + + Message.Error(DConv(Error.Text)) + +End + +Public Sub btnCreate_Click() + + Dim hTable As Table + + hTable = $hConn.Tables.Add("test") + + hTable.Fields.Add("id", db.Long) + hTable.Fields.Add("color", db.Integer,, 1) + hTable.Fields.Add("firstname", db.String, 16) + hTable.Fields.Add("name", db.String, 32) + hTable.Fields.Add("birth", db.Date) + hTable.Fields.Add("active", db.Boolean) + hTable.Fields.Add("salary", db.Float) + hTable.Fields.Add("comment", db.String) + hTable.Fields.Add("image", db.Blob) + + hTable.PrimaryKey = ["id"] + + hTable.Update + + hTable = $hConn.Tables.Add("color") + + hTable.Fields.Add("color", db.Serial) + hTable.Fields.Add("name", gb.String, 32) + hTable.Fields.Add("french", gb.String, 32) + + hTable.PrimaryKey = ["color"] + + hTable.Update + +Catch + + Message.Error(DConv(Error.Text)) + +End + +Public Sub btnDelete_Click() + + Try $hConn.Tables.Remove("test") + Try $hConn.Tables.Remove("color") + +End + +Public Sub btnFill_Click() + + Dim iInd As Integer + Dim rTest As Result + Dim rColor As Result + Dim sColor As String + Dim aName As String[] = ["Paul", "Pierre", "Jacques", "Antoine", "Mathieu", "Robert", "Stéphane", "Yannick", "Frédéric"] + Dim aFrench As String[] = ["Noir", "Blanc", "Rouge", "Vert", "Bleu", "Jaune", "Transparent"] + Inc Application.Busy + + $hConn.Begin + + rColor = $hConn.Create("color") + + For Each sColor In [("Black"), ("White"), ("Red"), ("Green"), ("Blue"), ("Yellow"), ("Transparent")] + + rColor!name = sColor + rColor!french = aFrench[iInd] + Inc iInd + rColor.Update + + Next + + rTest = $hConn.Create("test") + + For iInd = 1 To txtCount.Value + + rTest!id = iInd + rTest!color = Int(Rnd(6)) + 1 + rTest!firstname = aName[Int(Rnd(aName.Count))] + rTest!name = ("Name #") & iInd + rTest!birth = CDate("01/01/1970") + Int(Rnd(10000)) + rTest!active = Int(Rnd(2)) + rTest!salary = Round(Rnd(1000, 10000), -2) + + rTest.Update + + Next + + $hConn.Commit + +Finally + + Dec Application.Busy + +Catch + + $hConn.Rollback + Message.Error(DConv(Error.Text)) + +End + +Public Sub btnRun_Click() + + Dim rData As Result + Dim hForm As FRequest + + rData = $hConn.Exec(txtRequest.Text) + hForm = New FRequest($hConn, rData) + hForm.Show + +'Catch + + 'Message.Error(DConv(Error.Text)) + +End + +Public Sub Form_Open() + + $hConn = New Connection + FRequest.Init = True + +End + +Public Sub Form_Close() + + $hConn.Close + +End + +Public Sub chkDebug_Click() + + DB.Debug = chkDebug.Value + +End + +Public Sub btnTest_Click() + + FTest.Show + +End diff --git a/app/examples/Database/Database/.src/FMain.form b/app/examples/Database/Database/.src/FMain.form new file mode 100644 index 00000000..11001875 --- /dev/null +++ b/app/examples/Database/Database/.src/FMain.form @@ -0,0 +1,118 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(43.5714,25,63,79) + Text = ("Database example") + Icon = Picture["database.png"] + Resizable = False + { Frame1 Frame + MoveScaled(1,1,61,40) + Text = ("Connection") + { Label5 Label + MoveScaled(2,19,14,4) + Text = ("Database") + } + { Label1 Label + MoveScaled(2,4,15,4) + Text = ("Type") + } + { Label2 Label + MoveScaled(2,9,15,4) + Text = ("Host") + } + { Label3 Label + MoveScaled(2,24,14,4) + Text = ("User") + } + { Label4 Label + MoveScaled(2,29,14,4) + Text = ("Password") + } + { btnConnect Button + MoveScaled(46,4,13,4) + Text = ("Connect") + } + { cmbType ComboBox + MoveScaled(18,4,24,4) + List = [("postgresql"), ("mysql"), ("sqlite3"), ("odbc")] + Text = (" ") + } + { txtHost TextBox + MoveScaled(18,9,24,4) + } + { txtName TextBox + MoveScaled(18,19,24,4) + #Translate = False + Text = "test" + } + { txtUser TextBox + MoveScaled(18,24,24,4) + MaxLength = 16 + } + { txtPassword TextBox + MoveScaled(18,29,24,4) + Password = True + } + { chkCreate CheckBox + MoveScaled(2,34,56,4) + Text = ("Create database if it does not exist") + } + { chkDebug CheckBox + MoveScaled(46,9,13,4) + Text = ("Debug") + } + { Label7 Label + MoveScaled(2,14,15,4) + Text = ("Port") + } + { txtPort TextBox + MoveScaled(18,14,24,4) + } + } + { frmDatabase Frame + MoveScaled(1,42,61,15) + Enabled = False + Text = ("Tables 'test' && 'color'") + { btnCreate Button + MoveScaled(2,4,12,4) + Text = ("Create") + } + { btnDelete Button + MoveScaled(2,9,12,4) + Text = ("Delete") + } + { btnFill Button + MoveScaled(18,4,16,4) + Text = ("&Fill with") + } + { txtCount SpinBox + MoveScaled(18,9,11,4) + MinValue = 10 + MaxValue = 100000 + Step = 1000 + Value = 10000 + } + { btnTest Button + MoveScaled(41,4,18,9) + Text = ("Bound controls\nexample...") + } + { Label6 Label + MoveScaled(30,9,10,4) + Text = ("Records") + } + } + { frmRequest Frame + MoveScaled(1,58,61,20) + Enabled = False + Text = ("SQL request") + { btnRun Button + MoveScaled(49,4,11,4) + Text = ("Run...") + Default = True + } + { txtRequest TextArea + MoveScaled(2,4,46,15) + Wrap = True + } + } +} diff --git a/app/examples/Database/Database/.src/FRequest.class b/app/examples/Database/Database/.src/FRequest.class new file mode 100644 index 00000000..0d82f97f --- /dev/null +++ b/app/examples/Database/Database/.src/FRequest.class @@ -0,0 +1,131 @@ +' Gambas class file + +Static Public Init As Boolean + +Private $hConn As Connection +Private $rData As Result + +Public Sub _new(hConn As Connection, rData As Result) + + $hConn = hConn + $rData = rData + + RefreshTitle + + ReadData + + Me.Move(Int(Rnd(Desktop.W - Me.W)), Int(Rnd(Desktop.H - Me.H))) + +End + + +Private Sub RefreshTitle() + + Dim sTitle As String + + sTitle = ("SQL request") & " - " & $hConn.Name + + Me.Title = sTitle + +End + + +Private Sub ReadData() + + Dim hField As ResultField + Dim iInd As Integer + + Inc Application.Busy + + tbvData.Rows.Count = 0 + + tbvData.Columns.Count = $rData.Fields.Count + + For Each hField In $rData.Fields + + With hField + + 'PRINT .Name; ": "; .Type; " "; .Length + + tbvData.Columns[iInd].Text = .Name + tbvData.Columns[iInd].Width = WidthFromType(tbvData, .Type, .Length, .Name) + + End With + + Inc iInd + Next + + tbvData.Rows.Count = $rData.Count + +Finally + + Dec Application.Busy + +Catch + + Message.Error(("Cannot exec request.") & "\n\n" & DConv(Error.Text)) + +End + + +Public Sub tbvData_Data(Row As Integer, Column As Integer) + + $rData.MoveTo(Row) + + tbvData.Data.Text = Str($rData[tbvData.Columns[Column].Text]) + tbvData.Data.Background = Color.RGB((Row Mod 31) * 8, (Row Mod 17) * 15, (Row Mod 13) * 21) + tbvData.Data.Foreground = Color.White + +End + + +Private Function WidthFromType(hCtrl As Control, iType As Integer, iLength As Integer, sTitle As String) As Integer + + Dim iWidth As Integer + + Select Case iType + + Case gb.Boolean + iWidth = hCtrl.Font.TextWidth(Str(False)) + 32 + + Case gb.Integer + iWidth = hCtrl.Font.TextWidth("1234567890") + 16 + + Case gb.Long + iWidth = hCtrl.Font.TextWidth("12345678901234567890") + 16 + + Case gb.Float + iWidth = hCtrl.Font.TextWidth(CStr(Pi) & "E+999") + 16 + + Case gb.Date + iWidth = hCtrl.Font.TextWidth(Str(Now)) + 16 + + Case gb.String + If iLength = 0 Then iLength = 255 + iLength = Min(32, iLength) + iWidth = hCtrl.Font.TextWidth("X") * iLength + 16 + + End Select + + iWidth = Max(iWidth, hCtrl.Font.TextWidth(sTitle) + 8) + + Return iWidth + +End + +' Private Function rowcount() As Integer +' +' Dim rows As Integer +' +' rows = 0 +' Do +' $rData.MoveTo(rows) +' Inc rows +' Loop +' +' Catch +' +' Return rows +' +' End + diff --git a/app/examples/Database/Database/.src/FRequest.form b/app/examples/Database/Database/.src/FRequest.form new file mode 100644 index 00000000..ba9a0184 --- /dev/null +++ b/app/examples/Database/Database/.src/FRequest.form @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(29.1667,19.3333,77.5,54.1667) + Text = ("") + Arrangement = Arrange.Fill + { tbvData GridView + MoveScaled(1,1,36,29) + Mode = Select.Single + Header = GridView.Both + } +} diff --git a/app/examples/Database/Database/.src/FTest.class b/app/examples/Database/Database/.src/FTest.class new file mode 100644 index 00000000..91803ebd --- /dev/null +++ b/app/examples/Database/Database/.src/FTest.class @@ -0,0 +1,25 @@ +' Gambas class file + +'PRIVATE $hConn AS NEW Connection + +Public Sub Form_Open() + + 'DataSource2.Connection = DB.Current + +End + +Public Sub DataControl6_Validate(Value As Variant) + + If IsNull(Value) Then Return + If Value < 0 Or Value > 10000 Then + Stop Event + Endif + +End + + +' Public Sub DataSource1_BeforeSave(Data As Result) +' +' Data["color"] = DataSource2["color"] +' +' End diff --git a/app/examples/Database/Database/.src/FTest.form b/app/examples/Database/Database/.src/FTest.form new file mode 100644 index 00000000..c81fefdc --- /dev/null +++ b/app/examples/Database/Database/.src/FTest.form @@ -0,0 +1,121 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(3,10,99,92) + Text = ("Bound controls example") + Arrangement = Arrange.Fill + Spacing = True + Margin = True + { DataSource2 DataSource + MoveScaled(1,1,97,90) + Arrangement = Arrange.Vertical + Spacing = True + Table = "color" + ReadOnly = True + { Label7 Label + MoveScaled(1,1,95,4) + Font = Font["Bold,+1"] + Background = Color.SelectedBackground + Foreground = Color.SelectedForeground + Padding = 4 + Text = ("Test form") + } + { DataBrowser2 DataBrowser + MoveScaled(1,6,95,10) + Control = False + Columns = ["name"] + Header = False + } + { DataSource1 DataSource + MoveScaled(1,17,95,72) + Expand = True + Arrangement = Arrange.Vertical + Spacing = True + Table = "test" + { DataBrowser1 DataBrowser + MoveScaled(7,4,87,14) + Expand = True + Columns = ["id", "active", "firstname"] + } + { HBox1 HBox + MoveScaled(2,21,90,50) + Spacing = True + { VBox2 VBox + MoveScaled(1,1,26,39) + Spacing = True + { Label1 Label + MoveScaled(1,3,30,4) + Text = ("Id") + } + { Label9 Label + MoveScaled(1,7,30,4) + Text = ("Color") + } + { Label2 Label + MoveScaled(1,11,30,4) + Text = ("First Name") + } + { Label3 Label + MoveScaled(1,15,30,4) + Text = ("Name") + } + { Label4 Label + MoveScaled(1,19,30,4) + Text = ("Birth") + } + { Label5 Label + MoveScaled(1,23,30,4) + Text = ("Active") + } + { Label6 Label + MoveScaled(1,27,30,4) + Text = ("Salary") + } + { Label8 Label + MoveScaled(1,31,30,4) + Text = ("Comment") + } + } + { VBox1 VBox + MoveScaled(28,1,61,48) + Expand = True + Spacing = True + { DataControl1 DataControl + MoveScaled(1,2,30,4) + Field = "id" + } + { DataCombo1 DataCombo + MoveScaled(1,7,30,4) + Field = "color" + Table = "color" + } + { DataControl2 DataControl + MoveScaled(1,12,30,4) + Field = "firstname" + } + { DataControl3 DataControl + MoveScaled(1,17,30,4) + Field = "name" + } + { DataControl4 DataControl + MoveScaled(1,22,30,4) + Field = "birth" + } + { DataControl5 DataControl + MoveScaled(1,27,30,4) + Field = "active" + } + { DataControl6 DataControl + MoveScaled(1,32,30,4) + Field = "salary" + } + { DataControl7 DataControl + MoveScaled(1,37,42,10) + Expand = True + Field = "comment" + } + } + } + } + } +} diff --git a/app/examples/Database/Database/.src/Form1.class b/app/examples/Database/Database/.src/Form1.class new file mode 100644 index 00000000..8239c98f --- /dev/null +++ b/app/examples/Database/Database/.src/Form1.class @@ -0,0 +1,83 @@ +' Gambas class file + +Private $hConn As New Connection +Private $res As Result +'------------------------------------------------- +Public Sub Form_Open() +Dim iCount As Integer +Dim hTable As Table +Dim rTest As Result +Dim sql As String + +'define the gridview layout +GridView1.header = GridView.Horizontal +GridView1.grid = True +GridView1.Rows.count = 0 +GridView1.Columns.count = 2 +GridView1.Columns[0].text = "ID" +GridView1.Columns[1].text = "Value" +GridView1.Columns[0].width = 55 +GridView1.Columns[1].width = 55 + + +With $hConn + .Type = "sqlite" + .host = User.home + .name = "" +End With + +' 'delete an existing test.sqlite +' If Exist(User.home & "/test.sqlite") Then +' Kill User.home & "/test.sqlite" +' Endif + +' 'create test.sqlite +' $hConn.Open +' $hConn.Databases.Add("test.sqlite") +' $hconn.Close + +'define the table sampleTable +$hconn.name = "test.sqlite" +$hConn.Open +' hTable = $hConn.Tables.Add("sampleTable") +' hTable.Fields.Add("s_seq", db.Integer) +' hTable.Fields.Add("s_rndm", db.Integer) +' hTable.PrimaryKey = ["s_seq"] +' hTable.Update +' +' 'fill the table with generated data +' $hconn.Begin +' rTest = $hConn.Create("sampleTable") +' For iCount = 1 To 1000000 +' rTest!s_seq = iCount +' rTest!s_rndm = Int(Rnd(0, 100)) +' rTest.Update +' Next +' $hConn.Commit + +'read the database +sql = "select s_seq as ID, s_rndm as Value from sampleTable" +$res = $hconn.Exec(sql) + +Catch +$hConn.Rollback +Message.Error(DConv(Error.Text)) + +End +'------------------------------------------------- +Public Sub Form_Activate() +'change the rowcount of the gridview from 0 to the number of records. +'This triggers the data handling event + +GridView1.Rows.Count = $res.Count +End +'------------------------------------------------- +Public Sub GridView1_Data(Row As Integer, Column As Integer) + $res.moveTo(row) + GridView1.Data.text = Str($res[GridView1.Columns[column].text]) +End +'------------------------------------------------- +Public Sub Form_Close() + $hconn.Close +End +'------------------------------------------------- \ No newline at end of file diff --git a/app/examples/Database/Database/.src/Form1.form b/app/examples/Database/Database/.src/Form1.form new file mode 100644 index 00000000..08c1b47e --- /dev/null +++ b/app/examples/Database/Database/.src/Form1.form @@ -0,0 +1,8 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,64) + { GridView1 GridView + MoveScaled(6,5,44,45) + } +} diff --git a/app/examples/Database/Database/database.png b/app/examples/Database/Database/database.png new file mode 100644 index 0000000000000000000000000000000000000000..e809be00a7a242c7d0723424ae6ac56f594d0b34 GIT binary patch literal 1142 zcmV-+1d02JP)kdg000C%Nkl0zzS9% z7Jwp^A|%8LRulnQfI?8(x^0})@z@^69(z1r_v5he%{a8B_v-3M=braD@B5tRyibV! z{}aOT7(ZrFh#w02S?$#?rOzEbJHC=!Y^*Taj1TMgXWl8+e*2g2=N|%#PrbLMwr;My zw$~fSEatI@WqcfWVm}r!kK;IsdxvYUZQZ;z^>oZ6gAnfg@Z67^(?M#ii&RvT$PkrI znN^5}{q~PO`;#Y`Q~-Z!E&gWp>m+Ithe|;}m}E(zCCC-n_+#fgU+qo+{=9hZ?!q~O z)FFbpL{L+g7|TVaQZATAf&F&-#>=}Tgk=1SnR8=fxxS*>EtjjQNi^gNr4og)+}POk za`y9(P(wKVkzJ=J+9>oT3R!g$^$H|Rc0W=Y zoMe!Pwj~|26Z{jAsmgeI_Ry3^M#%(VDDl80j5X9h=5}O88dC30z-thO<*6N|)KxR) zZ3%)@GU4U;aF-qEdC!Jf9jn6$!09woQ@7`y6&EaOxNqd8N{@fV4TZh~ZF_>TD;3K% zDC$$LyDzO|!km^(f0I^+nCQ4{q*NH;vdrVUcnlCn25N5D(9t%h5OkIDioQfmS1_<{ z-!oP{G^+m9V4OAT+eXg%l$JaGq2)j(ksw!ynzlt>(6j5`>iTB03BY{OsrSrz%Z5+5 zZrgd!C>%J%oH;ceZRb7qNZVLlVm6-uICWXmP-?8_z$Hs|ZL2wjeLD)vKBpsB%9NTe zYd4?L!I619aG)ncYD-r!;UKkR8GUVqYl2PL2?zb6nZ9qq^G5cp+0(NxMH0)SF=?*Kq*D7f zl~VI&ENW;-j0}u4)%EoZ6oQeLY&D zMsbqC`)e1jtvl;EL+==xwxq9AD8;I^^d0(-9T#2o&;zC2x5cWde)IXS-QKFL>RWY1 zLs!?Kp-dr4)l4~~WnR>F-@ZBP*}WHEzcXp;-}w9sf4H==r(wm4GiF4&hJT7)G?b~U z8`!dC)6n42?VG><*Z)}C7n?8q>f$THMdz(J>uo#ANePK1pR{e$hWFz3?{0thje!## zz#D_`>MNf<`-A?sij{>B^^}TA2Pt|o@5Z~EZ{6DY>Ff7S9P$I^8Uo+Ge&&0NpP9ap zEHzrS8AeB0cepd&*uT5~%U|F7pjU^FcJ9j9pE%p`@R3XZ3%msZw|{wOI{*Lx07*qo IM6N<$f-aytd;kCd literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/.directory b/app/examples/Database/MySQLExample/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Database/MySQLExample/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Database/MySQLExample/.icon.png b/app/examples/Database/MySQLExample/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0f8466493051fa0414212f65152a1637de7d3239 GIT binary patch literal 4409 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 literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/.lang/ca.po b/app/examples/Database/MySQLExample/.lang/ca.po new file mode 100644 index 00000000..279dac1f --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/ca.po @@ -0,0 +1,539 @@ +msgid "" +msgstr "" +"Project-Id-Version: MySQLExample\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 19:46+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FNewField.class:114 +msgid "Accepts NULLs:" +msgstr "Accepta NULLs:" + +#: FNewField.class:79 +msgid "Add Field" +msgstr "Afegeix camp" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Un exemple sensill de GB.MYSQL" + +#: FNewTable.class:122 +#: FNewTrigger.class:146 +#: FNewView.class:101 +msgid "&Cancel" +msgstr "&Canceŀla" + +#: FTables.class:12 +msgid "Charset" +msgstr "Joc de caràcters" + +#: FNewDatabase.class:90 +#: FNewTable.class:112 +msgid "Charset:" +msgstr "Joc de caràcters:" + +#: FTables.class:23 +msgid "Check" +msgstr "Verificació" + +#: FConnect.class:142 +msgid "&Clear" +msgstr "&Neteja" + +#: FTables.class:13 +msgid "Collation" +msgstr "Coŀlació" + +#: FNewDatabase.class:79 +msgid "Collation:" +msgstr "Coŀlació:" + +#: FTables.class:30 +msgid "Column" +msgstr "Columna" + +#: FNewEvent.class:102 +#: FNewRoutine.class:119 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Valors separats per comes: Hola1 INT,Hola2 CHAR(5)" + +#: FTables.class:43 +msgid "Comment" +msgstr "Comentari" + +#: FNewField.class:146 +msgid "Comment:" +msgstr "Comentari:" + +#: FConnect.class:135 +msgid "&Connect" +msgstr "&Connecta" + +#: FConnect.class:86 +msgid "Connect to a MySQL Server" +msgstr "Connecta a un servidor MySQL" + +#: FNewDatabase.class:100 +msgid "&Create" +msgstr "&Crea" + +#: FTables.class:79 +msgid "Created" +msgstr "Creat" + +#: FNewDatabase.class:53 +msgid "Create Database" +msgstr "Crea una base de dades" + +#: FNewEvent.class:71 +msgid "Create Event" +msgstr "Crea un esdeveniment" + +#: FNewIndex.class:101 +msgid "Create Index" +msgstr "Crea un índex" + +#: FNewRoutine.class:88 +msgid "Create Routine" +msgstr "Crea una rutina" + +#: FNewTable.class:75 +msgid "Create Table" +msgstr "Crea una taula" + +#: FNewTrigger.class:84 +msgid "Create Trigger" +msgstr "Crea un activador" + +#: FNewView.class:63 +msgid "Create View" +msgstr "Crea una vista" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Hora de creació" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Coŀlació de la base de dades" + +#: FTables.class:51 +msgid "DataType" +msgstr "Tipus de dada" + +#: FNewField.class:104 +msgid "Data Type:" +msgstr "Tipus de dada:" + +#: FTables.class:49 +msgid "Default" +msgstr "Per defecte" + +#: FNewField.class:126 +msgid "Default:" +msgstr "Per defecte:" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definidor" + +#: FNewIndex.class:106 +msgid "Delete" +msgstr "Suprimeix" + +#: FTables.class:1336 +msgid "Delete Database" +msgstr "Suprimeix la base de dades" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Suprimeix l'esdeveniment" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Suprimeix el camp" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Suprimeix l'índex" + +#: FTables.class:1267 +msgid "Delete Item" +msgstr "Suprimeix l'element" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Suprimeix la rutina" + +#: FTables.class:882 +msgid "Delete Table" +msgstr "Suprimeix la taula" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Suprimeix l'activador" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Suprimeix la vista" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "Segur que voleu suprimir la base de dades: &1?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "Segur que voleu suprimir l'esdeveniment: &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "Segur que voleu suprimir el camp: &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "Segur que voleu suprimir l'índex: &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Segur que voleu suprimir la rutina: &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "Segur que voleu suprimir la taula: &1?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Segur que voleu suprimir l'activador: &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "Segur que voleu suprimir la vista: &1?" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Edita l'esdeveniment" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Edita el camp" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Edita l'índex" + +#: FTables.class:1260 +msgid "Edit Item" +msgstr "Edita l'element" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Edita la rutina" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Edita la taula" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Edita l'activador" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Edita la vista" + +#: FTables.class:77 +msgid "Ends" +msgstr "Extrems" + +#: FTables.class:11 +msgid "Engine" +msgstr "Motor" + +#: FNewTable.class:101 +msgid "Engine:" +msgstr "Motor:" + +#: FTables.class:61 +msgid "Event" +msgstr "Esdeveniment" + +#: FNewTrigger.class:122 +msgid "Event:" +msgstr "Esdeveniment:" + +#: FTables.class:1416 +msgid "Events" +msgstr "Esdeveniments" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Executa a" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FNewField.class:136 +msgid "Extra:" +msgstr "-" + +#: FTables.class:116 +msgid "Fields" +msgstr "Camps" + +#: FNewIndex.class:138 +msgid "Fields:" +msgstr "Camps:" + +#: FTables.class:149 +msgid "Fields on:" +msgstr "Camps a:" + +#: FTables.class:115 +msgid "Indexes" +msgstr "Índexs" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Índexs a:" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Interval del camp" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Interval del valor" + +#: FTables.class:1248 +msgid "Item" +msgstr "Element" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Element suprimit." + +#: FTables.class:52 +msgid "Key" +msgstr "Clau" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "L'últim modificat" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "L'últim executat" + +#: FTables.class:1292 +msgid "Lock" +msgstr "Bloqueig" + +#: FMessage.class:33 +msgid "Message" +msgstr "Missatge" + +#: .project:1 +#: FConnect.class:79 +msgid "MySQL Example" +msgstr "Exemple de MySQL" + +#: FConnect.class:71 +msgid "MySQL GUI" +msgstr "Interfície del MySQL" + +#: FTables.class:9 +msgid "Name" +msgstr "Nom" + +#: FNewTrigger.class:100 +#: FNewView.class:79 +msgid "Name:" +msgstr "Nom:" + +#: FTables.class:1329 +msgid "New Database" +msgstr "Base de dades nova" + +#: FTables.class:899 +msgid "New Event" +msgstr "Nou esdeveniment" + +#: FTables.class:890 +msgid "New Field" +msgstr "Nou camp" + +#: FTables.class:887 +msgid "New Index" +msgstr "Nou índex" + +#: FTables.class:1253 +msgid "New Item" +msgstr "Nou element" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Nova rutina" + +#: FTables.class:881 +msgid "New Table" +msgstr "Nova taula" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Nou activador" + +#: FTables.class:884 +msgid "New View" +msgstr "Nova vista" + +#: FNewField.class:120 +msgid "No" +msgstr "-" + +#: FTables.class:931 +msgid "&No" +msgstr "-" + +#: FTables.class:334 +msgid "Not available" +msgstr "No disponible" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Anuŀlable" + +#: FNewView.class:94 +msgid "&Ok" +msgstr "D'ac&ord" + +#: FTables.class:78 +msgid "On Completion" +msgstr "En finalitzar" + +#: FTables.class:82 +msgid "Originator" +msgstr "Original" + +#: FNewRoutine.class:114 +msgid "Parameters:" +msgstr "Paràmetres:" + +#: FConnect.class:123 +msgid "Password:" +msgstr "Contrasenya:" + +#: FConnect.class:101 +msgid "Port:" +msgstr "-" + +#: FNewIndex.class:151 +msgid "Primary Key" +msgstr "Clau primària" + +#: FTables.class:1274 +msgid "Refresh" +msgstr "Actualitza" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retorna" + +#: FNewRoutine.class:125 +msgid "Returns:" +msgstr "Retorna:" + +#: FTables.class:1396 +msgid "Routines" +msgstr "Rutines" + +#: FTables.class:10 +msgid "Rows" +msgstr "Files" + +#: FNewEvent.class:97 +msgid "Schedule:" +msgstr "Planificador:" + +#: FTables.class:22 +msgid "Security" +msgstr "Seguretat" + +#: FConnect.class:90 +msgid "Server:" +msgstr "Servidor:" + +#: FNewView.class:88 +msgid "Show a clue" +msgstr "Mostrar una pista" + +#: FTables.class:14 +msgid "Size" +msgstr "Mida" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "Mode SQL" + +#: FTables.class:76 +msgid "Starts" +msgstr "Inici" + +#: FTables.class:62 +msgid "Table" +msgstr "Taula" + +#: FTables.class:1243 +msgid "Tables" +msgstr "Taules" + +#: FNewTrigger.class:110 +msgid "Time:" +msgstr "Hora:" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zona horària" + +#: FTables.class:63 +msgid "Timing" +msgstr "Temps" + +#: FTables.class:1406 +msgid "Triggers" +msgstr "Activadors" + +#: FTables.class:32 +msgid "Type" +msgstr "Tipus" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Actualitzable" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Hora d'actualització" + +#: FConnect.class:112 +msgid "User:" +msgstr "Usuari:" + +#: FTables.class:1367 +msgid "Views" +msgstr "Vistes" + +#: FNewField.class:120 +msgid "Yes" +msgstr "Sí" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Sí" + diff --git a/app/examples/Database/MySQLExample/.lang/cs.po b/app/examples/Database/MySQLExample/.lang/cs.po new file mode 100644 index 00000000..ac4dc8c6 --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/cs.po @@ -0,0 +1,536 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "Příklad MySQL" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Jednoduchý příklad GB.MYSQL" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Připoejní k MySQL Serveru" + +#: FConnect.form:39 +msgid "Server:" +msgstr "Server:" + +#: FConnect.form:50 +msgid "Port:" +msgstr "Port:" + +#: FConnect.form:61 +msgid "User:" +msgstr "Uživate:" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Heslo:" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Připojení" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Vyčistit" + +#: FConnect.form:97 FNewDatabase.form:70 FNewEvent.form:65 FNewField.form:105 +#: FNewIndex.form:80 FNewRoutine.form:82 FNewTable.form:63 FNewTrigger.form:80 +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Zrušit" + +#: FMessage.form:8 +msgid "Message" +msgstr "Zpráva" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Vytvořit databázi" + +#: FNewDatabase.form:32 FNewEvent.form:32 FNewField.form:37 FNewIndex.form:45 +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "Jméno:" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "Porovnání:" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "Charset:" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Vytvořit" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Upravit událost" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Vytvořit událost" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Plán:" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Hodnoty oddělené čárkami: Hola1 INT,Hola2 CHAR(5)" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Ukaž vodítko" + +#: FNewEvent.form:58 FNewField.form:98 FNewIndex.form:73 FNewRoutine.form:75 +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "-" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Upravit pole" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Přidej pole" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "Typ dat:" + +#: FNewField.form:57 +msgid "Accepts NULLs:" +msgstr "Akceptuj NULL:" + +#: FNewField.form:63 +msgid "No" +msgstr "Ne" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Ano" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Výchozí:" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Poznámka:" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Upravit index" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Vytvořit index" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Smazat" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Pole:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Primární klíč" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Upravit rutinu" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Vytvořit rutinu" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "Parametry:" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Návrat:" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Upravit tabulku" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Vytvořit tabulku" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "Motor:" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Upravit spouštěč" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Vytvořit spouštěč" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Čas:" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Událost:" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Upravit pohled" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Vytvořit čas" + +#: FTables.class:9 +msgid "Name" +msgstr "Jméno" + +#: FTables.class:10 +msgid "Rows" +msgstr "Řádky" + +#: FTables.class:11 +msgid "Engine" +msgstr "Motor:" + +#: FTables.class:12 +msgid "Charset" +msgstr "Charset" + +#: FTables.class:13 +msgid "Collation" +msgstr "Porovnání" + +#: FTables.class:14 +msgid "Size" +msgstr "Velikost" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Aktualizace času" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Aktualizovatelné" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definovat" + +#: FTables.class:22 +msgid "Security" +msgstr "Bezpečnost" + +#: FTables.class:23 +msgid "Check" +msgstr "Kontrola" + +#: FTables.class:30 +msgid "Column" +msgstr "Sloupec" + +#: FTables.class:32 +msgid "Type" +msgstr "Typ" + +#: FTables.class:38 +msgid "Returns" +msgstr "Návrat" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Čas vytvoření" + +#: FTables.class:43 +msgid "Comment" +msgstr "Poznámka" + +#: FTables.class:49 +msgid "Default" +msgstr "Výchozí" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Nulovatelné" + +#: FTables.class:51 +msgid "DataType" +msgstr "Typ dat" + +#: FTables.class:52 +msgid "Key" +msgstr "Klíč" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FTables.class:61 +msgid "Event" +msgstr "Událost" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabulka" + +#: FTables.class:63 +msgid "Timing" +msgstr "Časování" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Časová zóna" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Při spuštění" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Interval hodnoty" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Interval pole" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "SQL mod" + +#: FTables.class:76 +msgid "Starts" +msgstr "Začínáme" + +#: FTables.class:77 +msgid "Ends" +msgstr "Končí" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Na dokončení" + +#: FTables.class:79 +msgid "Created" +msgstr "Vytvořeno" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Poslední výstraha" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Poslední spuštění" + +#: FTables.class:82 +msgid "Originator" +msgstr "Průvodce" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Porovnání databází" + +#: FTables.class:112 +msgid "Fields" +msgstr "Pole" + +#: FTables.class:330 +msgid "Not available" +msgstr "Nedostupné" + +#: FTables.class:363 +msgid "Indexes on:" +msgstr "Indexy na:" + +#: FTables.class:880 +msgid "New View" +msgstr "Nový pohled" + +#: FTables.class:881 +msgid "Delete View" +msgstr "Smazat pohled" + +#: FTables.class:883 +msgid "New Index" +msgstr "Nový index" + +#: FTables.class:884 +msgid "Delete Index" +msgstr "Smazat index" + +#: FTables.class:886 +msgid "New Field" +msgstr "Nové pole" + +#: FTables.class:887 +msgid "Delete Field" +msgstr "Smazat pole" + +#: FTables.class:889 +msgid "New Routine" +msgstr "Nový rutina" + +#: FTables.class:890 +msgid "Delete Routine" +msgstr "Smazat rutinu" + +#: FTables.class:892 +msgid "New Trigger" +msgstr "Nový spouštěč" + +#: FTables.class:893 +msgid "Delete Trigger" +msgstr "Smazat spouštěč" + +#: FTables.class:895 +msgid "New Event" +msgstr "Nová událost" + +#: FTables.class:896 +msgid "Delete Event" +msgstr "Smazat událost" + +#: FTables.class:927 +msgid "&No" +msgstr "&Ne" + +#: FTables.class:927 +msgid "&Yes" +msgstr "&Ano" + +#: FTables.class:927 +msgid "Do you realy want to delete the database: &1?" +msgstr "Opravdu chcete smazat databázi: &1?" + +#: FTables.class:1019 +msgid "Do you realy want to delete the table: &1?" +msgstr "Opravdu chcete smazat tabulku: &1?" + +#: FTables.class:1021 +msgid "Do you realy want to delete the view: &1?" +msgstr "Opravdu chcete smazat pohled: &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the index: &1?" +msgstr "Opravdu chcete smazat index: &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the field: &1?" +msgstr "Opravdu chcete smazat pole: &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Opravdu chcete smazat rutinu: &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Opravdu chcete smazat spouštěč: &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the event: &1?" +msgstr "Opravdu chcete smazat událost: &1?" + +#: FTables.class:1034 +msgid "Item deleted." +msgstr "Položka smazána." + +#: FTables.form:35 +msgid "Tables" +msgstr "Tabulky" + +#: FTables.form:40 +msgid "Item" +msgstr "Položka" + +#: FTables.form:45 +msgid "New Item" +msgstr "Nová položka" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Upravit položku" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Smazat polozku" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Obnovit" + +#: FTables.form:84 +msgid "Lock" +msgstr "Zámek" + +#: FTables.form:96 +msgid "New Table" +msgstr "Nová tabulka" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Smazat tabulku" + +#: FTables.form:121 +msgid "New Database" +msgstr "Nová databáze" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Smazat databázi" + +#: FTables.form:159 +msgid "Views" +msgstr "Pohled" + +#: FTables.form:169 +msgid "Indexes" +msgstr "Indexy" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Pole na:" + +#: FTables.form:188 +msgid "Routines" +msgstr "Rutiny" + +#: FTables.form:198 +msgid "Triggers" +msgstr "Spouštěče" + +#: FTables.form:208 +msgid "Events" +msgstr "Události" diff --git a/app/examples/Database/MySQLExample/.lang/de.po b/app/examples/Database/MySQLExample/.lang/de.po new file mode 100644 index 00000000..b49f2663 --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/de.po @@ -0,0 +1,536 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "MySQL-Beispiel" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Ein einfaches gb.mysql-Beispiel" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Zu einem SQL-Server verbinden" + +#: FConnect.form:39 +msgid "Server:" +msgstr "-" + +#: FConnect.form:50 +msgid "Port:" +msgstr "-" + +#: FConnect.form:61 +msgid "User:" +msgstr "Benutzer" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Passwort:" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Verbinden" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Löschen" + +#: FConnect.form:97 FNewDatabase.form:70 FNewEvent.form:65 FNewField.form:105 +#: FNewIndex.form:80 FNewRoutine.form:82 FNewTable.form:63 FNewTrigger.form:80 +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Abbrechen" + +#: FMessage.form:8 +msgid "Message" +msgstr "Nachricht" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Datenbank erstellen" + +#: FNewDatabase.form:32 FNewEvent.form:32 FNewField.form:37 FNewIndex.form:45 +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "-" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "Sortierfolge:" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "Zeichensatz:" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Erstellen" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Ereignis bearbeiten" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Ereignis erstellen" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Zeitplan:" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "-" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Hinweis anzeigen" + +#: FNewEvent.form:58 FNewField.form:98 FNewIndex.form:73 FNewRoutine.form:75 +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "-" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Feld bearbeiten" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Feld hinzufügen" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "Datentyp:" + +#: FNewField.form:57 +msgid "Accepts NULLs:" +msgstr "Leere Eingabe möglich:" + +#: FNewField.form:63 +msgid "No" +msgstr "Nein" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Ja" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Standardwert:" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Kommentar:" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Index bearbeiten" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Index erstellen" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Löschen" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Felder:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Primärschlüssel" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Routine bearbeiten" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Routine erstellen" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "Parameter:" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Rückgabe:" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Tabelle bearbeiten" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Tabelle erstellen" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "-" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Trigger bearbeiten" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Trigger erstellen" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Zeit:" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Ereignis:" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Ansicht bearbeiten" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Ansicht erstellen" + +#: FTables.class:9 +msgid "Name" +msgstr "-" + +#: FTables.class:10 +msgid "Rows" +msgstr "Zeilen" + +#: FTables.class:11 +msgid "Engine" +msgstr "-" + +#: FTables.class:12 +msgid "Charset" +msgstr "Zeichensatz" + +#: FTables.class:13 +msgid "Collation" +msgstr "Sortierfolge" + +#: FTables.class:14 +msgid "Size" +msgstr "Größe" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Update-Zeit" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Aktualisierbar" + +#: FTables.class:21 +msgid "Definer" +msgstr "Bestimmer" + +#: FTables.class:22 +msgid "Security" +msgstr "Sicherheit" + +#: FTables.class:23 +msgid "Check" +msgstr "Überprüfen" + +#: FTables.class:30 +msgid "Column" +msgstr "Spalte" + +#: FTables.class:32 +msgid "Type" +msgstr "Typ" + +#: FTables.class:38 +msgid "Returns" +msgstr "Rückgabe" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Erstellungszeit" + +#: FTables.class:43 +msgid "Comment" +msgstr "Kommentar" + +#: FTables.class:49 +msgid "Default" +msgstr "Standardwert" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Nulleingabe möglich" + +#: FTables.class:51 +msgid "DataType" +msgstr "Datentyp" + +#: FTables.class:52 +msgid "Key" +msgstr "Schlüssel" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FTables.class:61 +msgid "Event" +msgstr "Ereignis" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabelle" + +#: FTables.class:63 +msgid "Timing" +msgstr "Zeitplanung" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zeitzone" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Ausführen ab" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Intervall" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Autoeingabefeld" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "SQL-Modus" + +#: FTables.class:76 +msgid "Starts" +msgstr "Beginnt" + +#: FTables.class:77 +msgid "Ends" +msgstr "Endet" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Bei Vervollständigung" + +#: FTables.class:79 +msgid "Created" +msgstr "Erstellt" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Zuletzt geändert" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Zuletzt ausgeführt" + +#: FTables.class:82 +msgid "Originator" +msgstr "Bearbeiter" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Datenbank-Sortierfolge" + +#: FTables.class:116 +msgid "Fields" +msgstr "Felder" + +#: FTables.class:334 +msgid "Not available" +msgstr "Nicht verfügbar" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Indexes in:" + +#: FTables.class:884 +msgid "New View" +msgstr "Neue Ansicht" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Ansicht löschen" + +#: FTables.class:887 +msgid "New Index" +msgstr "Neuer Index" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Index löschen" + +#: FTables.class:890 +msgid "New Field" +msgstr "Neues Feld" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Feld löschen" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Neue Routine" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Routine löschen" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Neuer Trigger" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Trigger löschen" + +#: FTables.class:899 +msgid "New Event" +msgstr "Neues Ereignis" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Ereignis löschen" + +#: FTables.class:931 +msgid "&No" +msgstr "&Nein" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Ja" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "Wollen Sie wirklich die Datenbank &1 löschen?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "Wollen Sie wirklich die Tabelle &1 löschen?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "Wollen Sie wirklich die Ansicht &1 löschen?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "Wollen Sie wirklich den Index &1 löschen?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "Wollen Sie wirklich das Feld &1 löschen?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Wollen Sie wirklich die Routine &1 löschen?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Wollen Sie wirklich den Trigger &1 löschen?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "Wollen Sie wirklich das Ereignis &1 löschen?" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Datensatz gelöscht" + +#: FTables.form:35 +msgid "Tables" +msgstr "Tabellen" + +#: FTables.form:40 +msgid "Item" +msgstr "Datensatz" + +#: FTables.form:45 +msgid "New Item" +msgstr "Neuer Datensatz" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Datensatz bearbeiten" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Datensatz löschen" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Erneuern" + +#: FTables.form:84 +msgid "Lock" +msgstr "Sperren" + +#: FTables.form:96 +msgid "New Table" +msgstr "Neue Tabelle" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Tabelle löschen" + +#: FTables.form:121 +msgid "New Database" +msgstr "Neue Datenbank" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Datenbank löschen" + +#: FTables.form:159 +msgid "Views" +msgstr "Ansichten" + +#: FTables.form:169 +msgid "Indexes" +msgstr "-" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Felder in:" + +#: FTables.form:188 +msgid "Routines" +msgstr "Routinen" + +#: FTables.form:198 +msgid "Triggers" +msgstr "-" + +#: FTables.form:208 +msgid "Events" +msgstr "Ereignisse" diff --git a/app/examples/Database/MySQLExample/.lang/es.po b/app/examples/Database/MySQLExample/.lang/es.po new file mode 100644 index 00000000..02e62697 --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/es.po @@ -0,0 +1,533 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Cancelar" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Limpiar" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Conectar" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Crear" + +#: FTables.class:931 +msgid "&No" +msgstr "-" + +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "-" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Sí" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Un simple Ejemplo de GB.MYSQL" + +#: FNewField.form:57 +msgid "Accepts NULLs:" +msgstr "Acepta NULOs:" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Agregar Campo" + +#: FTables.class:12 +msgid "Charset" +msgstr "Conjunto de caracteres" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "-" + +#: FTables.class:23 +msgid "Check" +msgstr "Chequeo" + +#: FTables.class:13 +msgid "Collation" +msgstr "-" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "-" + +#: FTables.class:30 +msgid "Column" +msgstr "Columna" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Valores separados por coma: Hola1 INT,Hola2 CHAR(5)" + +#: FTables.class:43 +msgid "Comment" +msgstr "Comentario" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Comentario:" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Conectar a un Servidor MySQL" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Crear Base de Datos" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Crear Evento" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Crear Índice" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Crear Rutina" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Crear Tabla" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Crear Trigger" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Crear Vista" + +#: FTables.class:79 +msgid "Created" +msgstr "Creado" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Hora de creación" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "Tipo de Datos:" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Colación de la Base de Datos" + +#: FTables.class:51 +msgid "DataType" +msgstr "Tipo de Datos" + +#: FTables.class:49 +msgid "Default" +msgstr "Predeterminado" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Predeterminado:" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definidor" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Borrar" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Borrar Base de Datos" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Borrar Evento" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Borrar Campo" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Borrar Índice" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Borrar Item" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Borrar Rutina" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Borrar Tabla" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Borrar Trigger" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Borrar Vista" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "¿Realmente desea borrar la base de datos: &1?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "¿Realmente desea borrar el evento: &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "¿Realmente desea borrar el campo: &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "¿Realmente desea borrar el índice: &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "¿Realmente desea borrar la rutina: &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "¿Realmente desea borrar la tabla: &1?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "¿Realmente desea borrar el trigger: &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "¿Realmente desea borrar la vista: &1?" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Editar Evento" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Editar Campo" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Editar Índice" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Editar item" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Editar rutina" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Editar Tabla" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Editar Trigger" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Editar Vista" + +#: FTables.class:77 +msgid "Ends" +msgstr "Finaliza" + +#: FTables.class:11 +msgid "Engine" +msgstr "Máquina" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "Máquina:" + +#: FTables.class:61 +msgid "Event" +msgstr "Evento" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Evento:" + +#: FTables.form:208 +msgid "Events" +msgstr "Eventos" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Ejecutar En" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FTables.class:116 +msgid "Fields" +msgstr "Campos" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Campos en:" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Campos:" + +#: FTables.form:169 +msgid "Indexes" +msgstr "Índices" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Índices en:" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Campo del Intérvalo" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Valor del Intérvalo" + +#: FTables.form:40 +msgid "Item" +msgstr "-" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Item borrado." + +#: FTables.class:52 +msgid "Key" +msgstr "Llave" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Última Alteración" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Última Ejecución" + +#: FTables.form:84 +msgid "Lock" +msgstr "Bloquear" + +#: FMessage.form:8 +msgid "Message" +msgstr "Mensaje" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "-" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FTables.class:9 +msgid "Name" +msgstr "Nombre" + +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "Nombre:" + +#: FTables.form:121 +msgid "New Database" +msgstr "Nueva Base de Datos" + +#: FTables.class:899 +msgid "New Event" +msgstr "Nuevo Evento" + +#: FTables.class:890 +msgid "New Field" +msgstr "Nuevo Campo" + +#: FTables.class:887 +msgid "New Index" +msgstr "Nuevo Índice" + +#: FTables.form:45 +msgid "New Item" +msgstr "Nuevo Item" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Nueva Rutina" + +#: FTables.form:96 +msgid "New Table" +msgstr "Nueva Tabla" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Nuevo Trigger" + +#: FTables.class:884 +msgid "New View" +msgstr "Nueva Vista" + +#: FNewField.form:63 +msgid "No" +msgstr "-" + +#: FTables.class:334 +msgid "Not available" +msgstr "No disponible" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Anulable" + +#: FTables.class:78 +msgid "On Completion" +msgstr "En completado" + +#: FTables.class:82 +msgid "Originator" +msgstr "Originador" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "Parámetros:" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Contraseña:" + +#: FConnect.form:50 +msgid "Port:" +msgstr "Puerto:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Llave primaria" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Refrescar" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retorna" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Retorna:" + +#: FTables.form:188 +msgid "Routines" +msgstr "Rutinas" + +#: FTables.class:10 +msgid "Rows" +msgstr "Filas" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Programación:" + +#: FTables.class:22 +msgid "Security" +msgstr "Seguridad" + +#: FConnect.form:39 +msgid "Server:" +msgstr "Servidor:" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Mostrar una pista" + +#: FTables.class:14 +msgid "Size" +msgstr "Tamaño" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "Modo SQL" + +#: FTables.class:76 +msgid "Starts" +msgstr "Inicia" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabla" + +#: FTables.form:35 +msgid "Tables" +msgstr "Tablas" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zona de Tiempo" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Hora:" + +#: FTables.class:63 +msgid "Timing" +msgstr "-" + +#: FTables.form:198 +msgid "Triggers" +msgstr "-" + +#: FTables.class:32 +msgid "Type" +msgstr "Tipo" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Actualizable" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Hora de Actualización" + +#: FConnect.form:61 +msgid "User:" +msgstr "Usuario:" + +#: FTables.form:159 +msgid "Views" +msgstr "Vistas" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Sí" + diff --git a/app/examples/Database/MySQLExample/.lang/fr.po b/app/examples/Database/MySQLExample/.lang/fr.po new file mode 100644 index 00000000..436af64d --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/fr.po @@ -0,0 +1,1546 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FConnect.class:71 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.class:79 +#, fuzzy +msgid "MySQL Example" +msgstr "Exemple :" + +#: FConnect.class:86 +msgid "Connect to a MySQL Server" +msgstr "Connecter à un serveur Mysql" + +#: FConnect.class:90 +msgid "Server:" +msgstr "Serveur :" + +#: FConnect.class:101 +msgid "Port:" +msgstr "Port :" + +#: FConnect.class:112 +msgid "User:" +msgstr "Utilisateur :" + +#: FConnect.class:123 +msgid "Password:" +msgstr "Mot de passe :" + +#: FConnect.class:135 +msgid "&Connect" +msgstr "&Connecter" + +#: FConnect.class:142 +msgid "&Clear" +msgstr "&Effacer" + +#: FConnect.class:148 FNewDatabase.class:107 FNewEvent.class:120 +#: FNewField.class:162 FNewIndex.class:163 FNewRoutine.class:151 +#: FNewTable.class:122 FNewTrigger.class:146 FNewView.class:101 +msgid "&Cancel" +msgstr "&Annuler" + +#: FMessage.class:33 +msgid "Message" +msgstr "Message" + +#: FNewDatabase.class:53 +msgid "Create Database" +msgstr "Créer une base de données" + +#: FNewDatabase.class:69 FNewEvent.class:87 FNewField.class:94 +#: FNewIndex.class:128 FNewRoutine.class:104 FNewTable.class:91 +#: FNewTrigger.class:100 FNewView.class:79 +msgid "Name:" +msgstr "Name :" + +#: FNewDatabase.class:79 +msgid "Collation:" +msgstr "Action :" + +#: FNewDatabase.class:90 FNewTable.class:112 +msgid "Charset:" +msgstr "Charset :" + +#: FNewDatabase.class:100 +msgid "&Create" +msgstr "& Créer" + +#: FNewEvent.class:19 +#, fuzzy +msgid "Edit Event" +msgstr "Evénement" + +#: FNewEvent.class:71 +msgid "Create Event" +msgstr "Créer un évènement" + +#: FNewEvent.class:97 +msgid "Schedule:" +msgstr "Horaire :" + +#: FNewEvent.class:102 FNewRoutine.class:119 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Valeurs séparées par des virgules : Hola1 INT,Hola2 CHAR(5)" + +#: FNewEvent.class:107 FNewRoutine.class:138 FNewTrigger.class:133 +#: FNewView.class:88 +msgid "Show a clue" +msgstr "Affiche un truc" + +#: FNewEvent.class:113 FNewField.class:155 FNewIndex.class:156 +#: FNewRoutine.class:144 FNewTable.class:129 FNewTrigger.class:139 +#: FNewView.class:94 +msgid "&Ok" +msgstr "" + +#: FNewField.class:22 +#, fuzzy +msgid "Edit Field" +msgstr "Ajouter un champ" + +#: FNewField.class:79 +msgid "Add Field" +msgstr "Ajouter un champ" + +#: FNewField.class:104 +msgid "Data Type:" +msgstr "Type de donnée :" + +#: FNewField.class:114 +msgid "Accepts NULLs:" +msgstr "Accepte nul" + +#: FNewField.class:120 +msgid "No" +msgstr "Non" + +#: FNewField.class:120 +msgid "Yes" +msgstr "Oui" + +#: FNewField.class:126 +msgid "Default:" +msgstr "Par défaut :" + +#: FNewField.class:136 +msgid "Extra:" +msgstr "Extra :" + +#: FNewField.class:146 +msgid "Comment:" +msgstr "Commentaire :" + +#: FNewIndex.class:24 +#, fuzzy +msgid "Edit Index" +msgstr "Créer un index" + +#: FNewIndex.class:101 +msgid "Create Index" +msgstr "Créer un index" + +#: FNewIndex.class:106 +msgid "Delete" +msgstr "Effacer" + +#: FNewIndex.class:138 +msgid "Fields:" +msgstr "Champs :" + +#: FNewIndex.class:151 +msgid "Primary Key" +msgstr "Clé primaire" + +#: FNewRoutine.class:19 +#, fuzzy +msgid "Edit Routine" +msgstr "Créer une routine" + +#: FNewRoutine.class:88 +msgid "Create Routine" +msgstr "Créer une routine" + +#: FNewRoutine.class:114 +msgid "Parameters:" +msgstr "Paramètres :" + +#: FNewRoutine.class:125 +msgid "Returns:" +msgstr "Retourné :" + +#: FNewTable.class:21 +#, fuzzy +msgid "Edit Table" +msgstr "Créer une table" + +#: FNewTable.class:75 +msgid "Create Table" +msgstr "Créer une table" + +#: FNewTable.class:101 +msgid "Engine:" +msgstr "Moteur" + +#: FNewTrigger.class:28 +#, fuzzy +msgid "Edit Trigger" +msgstr "Déclencheurs" + +#: FNewTrigger.class:84 +msgid "Create Trigger" +msgstr "Créer un déclencheur" + +#: FNewTrigger.class:110 +msgid "Time:" +msgstr "Heure :" + +#: FNewTrigger.class:122 +msgid "Event:" +msgstr "Evénement :" + +#: FNewView.class:18 +#, fuzzy +msgid "Edit View" +msgstr "Editer" + +#: FNewView.class:63 +msgid "Create View" +msgstr "Créer une vue" + +#: FTables.class:9 +msgid "Name" +msgstr "Nom" + +#: FTables.class:10 +msgid "Rows" +msgstr "Lignes" + +#: FTables.class:11 +msgid "Engine" +msgstr "Moteur" + +#: FTables.class:12 +msgid "Charset" +msgstr "--" + +#: FTables.class:13 +msgid "Collation" +msgstr "Action" + +#: FTables.class:14 +msgid "Size" +msgstr "Taille" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Heure de mise à jour" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Mise à jour table" + +#: FTables.class:21 +msgid "Definer" +msgstr "Définir" + +#: FTables.class:22 +msgid "Security" +msgstr "Sécurité" + +#: FTables.class:23 +msgid "Check" +msgstr "Vérifier" + +#: FTables.class:30 +msgid "Column" +msgstr "Colonne" + +#: FTables.class:32 +msgid "Type" +msgstr "" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retourné" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Heure de création" + +#: FTables.class:43 +msgid "Comment" +msgstr "Commentaire" + +#: FTables.class:49 +msgid "Default" +msgstr "Par défaut" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Nul" + +#: FTables.class:51 +msgid "DataType" +msgstr "Type de donnée" + +#: FTables.class:52 +msgid "Key" +msgstr "Clé" + +#: FTables.class:53 +msgid "Extra" +msgstr "--" + +#: FTables.class:61 +msgid "Event" +msgstr "Evénement" + +#: FTables.class:62 +msgid "Table" +msgstr "" + +#: FTables.class:63 +msgid "Timing" +msgstr "Minutage" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zone horaire" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Exécuter à" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Valeur d'interval" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Champ d'interval" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "Mode SQL" + +#: FTables.class:76 +msgid "Starts" +msgstr "Démarrer" + +#: FTables.class:77 +msgid "Ends" +msgstr "Fins" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Complétion " + +#: FTables.class:79 +msgid "Created" +msgstr "Crée" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Dernier changement" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Dernière exécution" + +#: FTables.class:82 +msgid "Originator" +msgstr "Originel" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Base de donnée active " + +#: FTables.class:115 +msgid "Indexes" +msgstr "Indexs" + +#: FTables.class:116 +msgid "Fields" +msgstr "Champs" + +#: FTables.class:149 +msgid "Fields on:" +msgstr "Champs actuel :" + +#: FTables.class:334 +msgid "Not available" +msgstr "Non disponible" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Indexé sur :" + +#: FTables.class:881 +msgid "New Table" +msgstr "Nouvelle table" + +#: FTables.class:882 +msgid "Delete Table" +msgstr "Effacer la table" + +#: FTables.class:884 +msgid "New View" +msgstr "Nouvelle vue" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Effacer la vue" + +#: FTables.class:887 +msgid "New Index" +msgstr "Nouvel index" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Effacer l'index" + +#: FTables.class:890 +msgid "New Field" +msgstr "Nouveau champ" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Effacer le champ" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Nouvelle routine" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Effacer la routine" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Nouveau déclencheur" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Effacer le déclencheur" + +#: FTables.class:899 +msgid "New Event" +msgstr "Nouvel événement" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Evénement effacé" + +#: FTables.class:931 +msgid "&No" +msgstr "&Non" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Oui" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "Voulez vous vraiment effacer la base de données : &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "Voulez vous vraiment effacer cette table : &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "Voulez vous vraiment effacer cette vue : &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "Voulez vous vraiment effacer cet index : &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "Voulez vous vraiment effacer ce champ : &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Voulez vous vraiment effacer cette routine : &1?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Voulez vous vraiment effacer ce déclencheur : &1?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "Voulez vous vraiment effacer cet événement : &1?" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Article effacé" + +#: FTables.class:1243 +msgid "Tables" +msgstr "" + +#: FTables.class:1248 +msgid "Item" +msgstr "Article" + +#: FTables.class:1253 +msgid "New Item" +msgstr "Nouvel article" + +#: FTables.class:1260 +#, fuzzy +msgid "Edit Item" +msgstr "Editer" + +#: FTables.class:1267 +msgid "Delete Item" +msgstr "Effacer l'article" + +#: FTables.class:1274 +msgid "Refresh" +msgstr "Rafraichir" + +#: FTables.class:1292 +msgid "Lock" +msgstr "Bloqué" + +#: FTables.class:1329 +msgid "New Database" +msgstr "Nouvelle base de données" + +#: FTables.class:1336 +msgid "Delete Database" +msgstr "Base de données effacée" + +#: FTables.class:1367 +msgid "Views" +msgstr "Vues" + +#: FTables.class:1396 +msgid "Routines" +msgstr "Routines" + +#: FTables.class:1406 +msgid "Triggers" +msgstr "Déclencheurs" + +#: FTables.class:1416 +msgid "Events" +msgstr "Evénements" + +#~ msgid "&Replace" +#~ msgstr "&Remplace" + +#~ msgid "Are you sure to want to replace all?" +#~ msgstr "Etes vous sûr de vouloir tout remplacer ?" + +#~ msgid "MySQL is great!!!" +#~ msgstr "Mysql est super !!!" + +#~ msgid "Gambas is great!!!" +#~ msgstr "Gambas est super !!!" + +#~ msgid "Wellcome!!! Have a nice day." +#~ msgstr "Bienvenue !!! Bonne continuation." + +#~ msgid "In a world without walls and fences, who needs windows and gates?" +#~ msgstr "Dans un monde sans murs et barrières, qui a besoin de fenetres et de portes ?" + +#~ msgid "In God we trust" +#~ msgstr "Grace à Dieu !" + +#~ msgid "Do you want to save the changes?" +#~ msgstr "Voulez vous sauver les changements ?" + +#~ msgid "Go to line" +#~ msgstr "Aller à la ligne " + +#~ msgid "Search string replaced &1 times." +#~ msgstr "Chaine de recherche remplacée &1 fois" + +#~ msgid "Search string replaced once." +#~ msgstr "Chaine de recherche remplacée une fois." + +#~ msgid "Search string cannot be found." +#~ msgstr "Chaine de recherche non trouvée." + +#~ msgid "Reached the beginning of the document." +#~ msgstr "Le début du document a été atteind" + +#~ msgid "Reached the end of the document." +#~ msgstr "La fin du document a été atteind " + +#~ msgid "Revision:" +#~ msgstr "Révision :" + +#~ msgid "Workig Version:" +#~ msgstr "Version de travail :" + +#~ msgid "Modified" +#~ msgstr "Modifié" + +#~ msgid "Missing or incomplete" +#~ msgstr "Manquant ou incomplet" + +#~ msgid "Not under version control" +#~ msgstr "Pas de CVS" + +#~ msgid "Existed" +#~ msgstr "Sortie" + +#~ msgid "Conflicting changes" +#~ msgstr "Changements pour éviter les conflits" + +#~ msgid "Merged" +#~ msgstr "fusionné" + +#~ msgid "Replaced" +#~ msgstr "Remplacé" + +#~ msgid "Deleted" +#~ msgstr "Effacé" + +#~ msgid "Added" +#~ msgstr "Ajouté" + +#~ msgid "Updated" +#~ msgstr "Mis à jour" + +#~ msgid "Status:" +#~ msgstr "Etats :" + +#~ msgid "Do you really want to revert the changes?" +#~ msgstr "Voulez vous vraiment rétablir les changements ?" + +#~ msgid "File saved." +#~ msgstr "Fichier sauvé." + +#~ msgid "Entire word" +#~ msgstr "Mot entier" + +#~ msgid "Case sensitive" +#~ msgstr "police sensitive" + +#~ msgid "Replace all" +#~ msgstr "Tout remplacer " + +#~ msgid "Replace current" +#~ msgstr "Remplacer l'actuel" + +#~ msgid "Find previous" +#~ msgstr "Recherche le précédent" + +#~ msgid "Find next" +#~ msgstr "Recherche le suivant" + +#~ msgid "Close" +#~ msgstr "Fermer" + +#~ msgid "Update" +#~ msgstr "Mise à jour" + +#~ msgid "Status" +#~ msgstr "Etats" + +#~ msgid "Revert" +#~ msgstr "Retour" + +#~ msgid "Go to symbol" +#~ msgstr "Aller au symbol" + +#~ msgid "Go to line..." +#~ msgstr "Aller à la ligne..." + +#~ msgid "Horizontal" +#~ msgstr "Horizontal" + +#~ msgid "View" +#~ msgstr "Vue" + +#~ msgid "Load" +#~ msgstr "Charger" + +#~ msgid "Save" +#~ msgstr "Sauver" + +#~ msgid "Show/Hide" +#~ msgstr "Afficher/Cacher" + +#~ msgid "Replace" +#~ msgstr "Remplacer" + +#~ msgid "Find" +#~ msgstr "Recherche" + +#~ msgid "Clean" +#~ msgstr "Nettoyer" + +#~ msgid "Show numbers" +#~ msgstr "Affiche les nombres" + +#~ msgid "Completion" +#~ msgstr "Complétion" + +#~ msgid "Uncomment" +#~ msgstr "Décommenter" + +#~ msgid "Load query" +#~ msgstr "Charger la requete" + +#~ msgid "Show/Hide tool bar" +#~ msgstr "Afficher/Cacher la barre d'outils" + +#~ msgid "Find & Replace" +#~ msgstr "Recherche et remplace" + +#~ msgid "Clean editor" +#~ msgstr "Nettoyer l'éditeur" + +#~ msgid "Save query" +#~ msgstr "Sauver une requete" + +#~ msgid "Columns with * are Key fields.
They will be used to perform any operation." +#~ msgstr "Les colonnes avec * sont des champs clés.
Ils seront utilisés comme jokers." + +#~ msgid "User
Schema (database) privileges assigned to the user" +#~ msgstr "Utilisateur
Schéma (base de données) des privilèges assignés à l'utilisateur" + +#~ msgid "Schema Privileges" +#~ msgstr "Droits du schéma" + +#~ msgid "New" +#~ msgstr "Nouveau" + +#~ msgid "&Change" +#~ msgstr "&Changer" + +#~ msgid "Icon:" +#~ msgstr "Icones :" + +#~ msgid "Contact Information:" +#~ msgstr "Information du contact :" + +#~ msgid "E-Mail:" +#~ msgstr "Courriel :" + +#~ msgid "Description:" +#~ msgstr "Description :" + +#~ msgid "Full name:" +#~ msgstr "Nom :" + +#~ msgid "Additional Information" +#~ msgstr "Information additionnelles" + +#~ msgid "Confirm Password:" +#~ msgstr "Confirmer le mot de passe :" + +#~ msgid "New Password:" +#~ msgstr "Nouveau mot de passe :" + +#~ msgid "MySQL User:" +#~ msgstr "Utilisateur Mysql :" + +#~ msgid "Login Information" +#~ msgstr "Information d'entrée" + +#~ msgid "User
Login and additional information on the user" +#~ msgstr "Utilisateur
Login et informations additionnelles de l'utilisateur" + +#~ msgid "User Information" +#~ msgstr "Info utilisateur" + +#~ msgid "Information strored successfully." +#~ msgstr "Information sauvegardée avec succès." + +#~ msgid "Please select a table." +#~ msgstr "SVP sélectionnez une table" + +#~ msgid "Item created." +#~ msgstr "Article créer." + +#~ msgid "Selected Tables:" +#~ msgstr "Tables sélectionnées :" + +#~ msgid "Tables:" +#~ msgstr "Tables :" + +#~ msgid "Do not write the command to the binary log file." +#~ msgstr "Ne pas écrire cette commande dans un fichier binaire" + +#~ msgid "This will repair the selected table(s)." +#~ msgstr "Ceci réparera les tables sélectionnées." + +#~ msgid "This will check whether the selected table(s) are corrupted or have other errors." +#~ msgstr "Ceci vérifiera si les tables sont corrompues ou ont d'autres erreurs." + +#~ msgid "If necessary this will repair the selected table(s), sort index pages and update statistics." +#~ msgstr "Si nécessaire, ceci réparera les tables sélectionnées, trier les index et mettre à jour les statistiques." + +#~ msgid "Repair Tables" +#~ msgstr "Réparer les tables" + +#~ msgid "Check Tables" +#~ msgstr "Vérifier les tables" + +#~ msgid "Optimize Tables" +#~ msgstr "Tables optimisées" + +#~ msgid "Tasks" +#~ msgstr "Taches" + +#~ msgid "Table Maintenance
Please select the task you want to perform on the selected table(s)." +#~ msgstr "Maintenance de la table
SVP sélectionner la tâche que vous vouler effectuée sur la/les table(s) sélectionnée(s)." + +#~ msgid "Tasks and Tables Selection" +#~ msgstr "Sélection des tables et des taches" + +#~ msgid "&Execute" +#~ msgstr "&Exécuter" + +#~ msgid "Do not show this message again" +#~ msgstr "Ne plus montrer ce message" + +#~ msgid "Statements to execute" +#~ msgstr "Commande à exécuter" + +#~ msgid "Server Parameters" +#~ msgstr "Paramètres serveur" + +#~ msgid "Server Parameters (local)" +#~ msgstr "Paramètres serveur local" + +#~ msgid "Replication Status" +#~ msgstr "Etat de réplication" + +#~ msgid "System Variables" +#~ msgstr "Variables système" + +#~ msgid "Server Status:" +#~ msgstr "Etat du serveur :" + +#~ msgid "Clock:" +#~ msgstr "Horloge :" + +#~ msgid "Memory:" +#~ msgstr "Mémoire :" + +#~ msgid "Operating System:" +#~ msgstr "Système d'exploitation :" + +#~ msgid "Processor:" +#~ msgstr "Processeur :" + +#~ msgid "Version:" +#~ msgstr "Version :" + +#~ msgid "Client Information" +#~ msgstr "Information Client" + +#~ msgid "Network Name:" +#~ msgstr "Nom de réseau :" + +#~ msgid "IP:" +#~ msgstr "IP :" + +#~ msgid "MySQL Version:" +#~ msgstr "Version Mysql :" + +#~ msgid "Host:" +#~ msgstr "Serveur :" + +#~ msgid "Socket:" +#~ msgstr "Prise :" + +#~ msgid "Connected to MySQL Server Instance" +#~ msgstr "Connecté à l'instance du serveur Mysql" + +#~ msgid "" +#~ "Server Status:
\n" +#~ "     Server is running" +#~ msgstr "" +#~ "Etat du serveur :
\n" +#~ "     Le serveur est lancé" + +#~ msgid "Couldn't find &1 in &2 or &3" +#~ msgstr "Impossible de trouver &1 dans &2 ou &3" + +#~ msgid "New Script" +#~ msgstr "Nouveau script" + +#~ msgid "Run script" +#~ msgstr "Exécuter un script" + +#~ msgid "Script executed successfully." +#~ msgstr "Script exécuté avec succès" + +#~ msgid "Empty table." +#~ msgstr "Table vide." + +#~ msgid "Paso #5" +#~ msgstr "Passe #5" + +#~ msgid "Local Test" +#~ msgstr "Test local" + +#~ msgid "Changes the restore path" +#~ msgstr "Changement du chemin de restauration" + +#~ msgid "Source schema:" +#~ msgstr "Source du schéma :" + +#~ msgid "Source Schema" +#~ msgstr "Source du schéma" + +#~ msgid "SMaRT" +#~ msgstr "Pratique" + +#~ msgid "Schema:" +#~ msgstr "Schéma :" + +#~ msgid "david123" +#~ msgstr "--" + +#~ msgid "3306" +#~ msgstr "-" + +#~ msgid "localhost" +#~ msgstr "Serveur local" + +#~ msgid "Main server" +#~ msgstr "Serveur principal" + +#~ msgid "Backup Main Server" +#~ msgstr "Sauvegarde du serveur principal" + +#~ msgid "Read this first!!!" +#~ msgstr "A lire en premier !!!!" + +#~ msgid "Remove row" +#~ msgstr "Effacer une ligne" + +#~ msgid "Add row" +#~ msgstr "Ajouter une ligne" + +#~ msgid "Save to file" +#~ msgstr "Sauver dans un fichier" + +#~ msgid "ResultSet" +#~ msgstr "Résultat" + +#~ msgid "Ignore SQL errors" +#~ msgstr "Ignorer les erreurs SQL" + +#~ msgid "Options:" +#~ msgstr "Options :" + +#~ msgid "Total data length:" +#~ msgstr "Longueur totale des données :" + +#~ msgid "Total tables:" +#~ msgstr "Total des tables :" + +#~ msgid "Total schemas:" +#~ msgstr "Schémas totaux :" + +#~ msgid "Character Set:" +#~ msgstr "jeu de caractères :" + +#~ msgid "File:" +#~ msgstr "Fichier " + +#~ msgid "Information:" +#~ msgstr "Informations :" + +#~ msgid "Restore backup" +#~ msgstr "Restauration de la sauvegarde" + +#~ msgid "Restore executed successfully." +#~ msgstr "Restauration effectuée avec succès" + +#~ msgid "Opens the output file" +#~ msgstr "Ouvre le fichier de sortie" + +#~ msgid "Output to file" +#~ msgstr "Fichier de sortie" + +#~ msgid "Prints the output to a specified file. By default /home/user/Tmp.sql." +#~ msgstr "Affiche la sortie d'un fichier spécifié. Par défaut /home/user/Tmp.sql." + +#~ msgid "-t" +#~ msgstr "-" + +#~ msgid "Output in table format" +#~ msgstr "Format de la table de sortie" + +#~ msgid "-vvv" +#~ msgstr "-" + +#~ msgid "Shows the Query and Rows in Set in Table Format" +#~ msgstr "Affiche la requete et les lignes ensemble dans une table " + +#~ msgid "-vv" +#~ msgstr "-" + +#~ msgid "Shows the Query and Rows in Set" +#~ msgstr "Affiche la requete et les lignes ensemble" + +#~ msgid "-v" +#~ msgstr "-" + +#~ msgid "Shows the Query" +#~ msgstr "Affiche la requete" + +#~ msgid "New result tab" +#~ msgstr "Nouveau signet" + +#~ msgid "New Query" +#~ msgstr "Nouvelle requete" + +#~ msgid "Run query" +#~ msgstr "exécuter une requete" + +#~ msgid "Program set to write mode" +#~ msgstr "Programme configuré en modede lecture " + +#~ msgid "Program set to read only mode" +#~ msgstr "Programme configuré en mode de lecture seul" + +#~ msgid "Nothing to show." +#~ msgstr "Rien à afficher" + +#~ msgid "Database changed." +#~ msgstr "La base de données a changé" + +#~ msgid "Action not allowed in read only mode." +#~ msgstr "Action non permise en mode lecture seule" + +#~ msgid "Kill process" +#~ msgstr "Tuer le process" + +#~ msgid "Process" +#~ msgstr "Tache" + +#~ msgid "System Processes" +#~ msgstr "Taches système" + +#~ msgid "Info" +#~ msgstr "Info" + +#~ msgid "State" +#~ msgstr "Etat" + +#~ msgid "Time" +#~ msgstr "Heure" + +#~ msgid "Command" +#~ msgstr "Commande" + +#~ msgid "Database" +#~ msgstr "Base de données" + +#~ msgid "Host" +#~ msgstr "Serveur" + +#~ msgid "Id" +#~ msgstr "ID" + +#~ msgid "Show statements before execute them:" +#~ msgstr "Afficher les commandes avant de les exécuter :" + +#~ msgid "Show statements before execute them" +#~ msgstr "Afficher les commandes avant de les exécuter" + +#~ msgid "Show the information message before start editing:" +#~ msgstr "Affiche le message d'information avant de démarrer l'édition :" + +#~ msgid "Show the information message before start editing" +#~ msgstr "Affiche le message d'information avant de démarrer l'édition" + +#~ msgid "Result:" +#~ msgstr "Résultat :" + +#~ msgid "Result" +#~ msgstr "Résultat" + +#~ msgid "Numbers:" +#~ msgstr "Numéro :" + +#~ msgid "Sets the color for numbers in the editor" +#~ msgstr "Configure la couleur des nombres dans l'éditeur" + +#~ msgid "Font" +#~ msgstr "Police" + +#~ msgid "Font:" +#~ msgstr "Police :" + +#~ msgid "Sets the font type for the editor" +#~ msgstr "Configure le type de police de l'éditeur" + +#~ msgid "Keywords:" +#~ msgstr "Mots clés :" + +#~ msgid "Sets the color for keywords in the editor" +#~ msgstr "Configure la couleur des mots clés dans l'éditeur" + +#~ msgid "Tab size:" +#~ msgstr "Taille tab :" + +#~ msgid "Sets the tab size for the editor" +#~ msgstr "Configure la taille de tabulation de l'éditeur" + +#~ msgid "Strings:" +#~ msgstr "Chaines :" + +#~ msgid "Sets the color for strings in the editor" +#~ msgstr "Configure la couleur des chaines dans l'éditeur" + +#~ msgid "Symbols:" +#~ msgstr "Symboles :" + +#~ msgid "Sets the color for symbols in the editor" +#~ msgstr "Configure la couleur des symbols dans l'éditeur" + +#~ msgid "Functions:" +#~ msgstr "Functions :" + +#~ msgid "Sets the color for functions in the editor" +#~ msgstr "Configure la couleur des fonctions dans l'éditeur" + +#~ msgid "Operators:" +#~ msgstr "Opérateurs :" + +#~ msgid "Sets the color for operators in the editor" +#~ msgstr "Configure la couleur des opérateurs dans l'éditeur" + +#~ msgid "Data Types:" +#~ msgstr "Type des données :" + +#~ msgid "Sets the color for datatypes in the editor" +#~ msgstr "Configure la couleur du type de données dans l'éditeur" + +#~ msgid "&Restore" +#~ msgstr "&Restauration" + +#~ msgid "Show modified lines:" +#~ msgstr "Affiche les lignes modifiées :" + +#~ msgid "Show modified lines" +#~ msgstr "Afficher les lignes modifiées" + +#~ msgid "Comments:" +#~ msgstr "Commentaires :" + +#~ msgid "Sets the color for comments in the editor" +#~ msgstr "Configure la couleur des commentaites dans l'éditeur" + +#~ msgid "Editor:" +#~ msgstr "Editeur :" + +#~ msgid "Editor" +#~ msgstr "Editeur" + +#~ msgid "Path for restore files:" +#~ msgstr "Chemin des fichiers de restauration :" + +#~ msgid "Change the path to look for restore files" +#~ msgstr "Cherche le chemin pour restaurer les fichiers" + +#~ msgid "Dif Color" +#~ msgstr "Dif couleur" + +#~ msgid "Color for rows in results:" +#~ msgstr "Couleur des lignes pour le résultat" + +#~ msgid "Sets the color for rows in results" +#~ msgstr "Configure la couleur des lignes de résultats" + +#~ msgid "Custom messages" +#~ msgstr "Messages personnalisés" + +#~ msgid "Custom messages color:" +#~ msgstr "Couleur de messages personnalisés" + +#~ msgid "Sets the color for custom messages" +#~ msgstr "Configure la couleur des messages personnalisés" + +#~ msgid "Warning messages" +#~ msgstr "Messages d'avertissement" + +#~ msgid "Warning messages color:" +#~ msgstr "Couleur des messages d'avertissement" + +#~ msgid "Sets the color for warning messages" +#~ msgstr "Configure la couleur des avertissements de messages" + +#~ msgid "Error messages" +#~ msgstr "Messages d'erreur" + +#~ msgid "Error messages color:" +#~ msgstr "Couleur des messages d'erreur" + +#~ msgid "Sets the color for error messages" +#~ msgstr "Configure la couleur des messages d'erreur" + +#~ msgid "Information messages" +#~ msgstr "Messages d'information" + +#~ msgid "Information messages color:" +#~ msgstr "Couleur des messages d'information :" + +#~ msgid "Sets the color for information messages" +#~ msgstr "Configure la couleur des messages d'information" + +#~ msgid "Remember last windows opened:" +#~ msgstr "Rappel de la dernière fenetre ouverte :" + +#~ msgid "Remember last windows opened" +#~ msgstr "Rappel de la dernière fenetre ouverte" + +#~ msgid "Show error messages in a popup window:" +#~ msgstr "Affiche tous les messages dans un popup :" + +#~ msgid "Show error messages in a popup window" +#~ msgstr "Affiche les messages d'erreur dans une fenetre surgissante" + +#~ msgid "Store passwords:" +#~ msgstr "Enregistre le mot de passe :" + +#~ msgid "Store passwords" +#~ msgstr "Enregistre le mot de passe " + +#~ msgid "All keywords in uppercase:" +#~ msgstr "Tous les mots clés en majuscule :" + +#~ msgid "Show all Keywords in Uppercase" +#~ msgstr "Affiche tous les mots clés en majuscule" + +#~ msgid "Restore default values" +#~ msgstr "Restauration des valeurs par défaut" + +#~ msgid "Close forms by pessing Alt+Esc:" +#~ msgstr "Fermer le formulaire en appuant sur les touches Alt+Esc" + +#~ msgid "All Tabs/Forms will close if you press Esc key" +#~ msgstr "Tous les formulaires fermeront si vous appuyez sur la touche Esc" + +#~ msgid "General:" +#~ msgstr "Général :" + +#~ msgid "General" +#~ msgstr "Général" + +#~ msgid "Default values restored." +#~ msgstr "Valeurs restaurées par défaut." + +#~ msgid "UPDATE" +#~ msgstr "MAJ" + +#~ msgid "INSERT" +#~ msgstr "INSERT" + +#~ msgid "DELETE" +#~ msgstr "EFFACER" + +#~ msgid "BEFORE" +#~ msgstr "AVANT" + +#~ msgid "AFTER" +#~ msgstr "APRES" + +#~ msgid "URL:" +#~ msgstr "-" + +#~ msgid "Syntax:" +#~ msgstr "Syntaxe :" + +#~ msgid "" +#~ "Could not find any help.\n" +#~ "Please check those tables:\n" +#~ msgstr "" +#~ "Impossible de trouver de l'aide .\n" +#~ "SVP Vérifier ces tables :\n" + +#~ msgid "In this option you will find the information about the application." +#~ msgstr "Dans cette option vous trouverez les informations à propos de cette application" + +#~ msgid "Shows this help" +#~ msgstr "Affiche cet aide" + +#~ msgid "In this option you will find this help" +#~ msgstr "Dans cette option vous trouverez cette aide " + +#~ msgid "Runs a script" +#~ msgstr "Exécute un script" + +#~ msgid "Runs a query" +#~ msgstr "Exécute une requete" + +#~ msgid "Shortcuts" +#~ msgstr "Raccourcis" + +#~ msgid "Generate a Gambas module to create the Database, Tables, Routines..." +#~ msgstr "Générer un module Gambas pour créer la base de données, tables, routines..." + +#~ msgid "Click the status bar to see the complete message in a new window." +#~ msgstr "Cliquer sur la barre d'état afin de voir le message complet dans la nouvelle fenêtre" + +#~ msgid "Features" +#~ msgstr "Caractéristiques" + +#~ msgid "is an application to interact with a MySQL Server and is based on gb.mysql. Its propose is to provide a friendly interface to MySQL, it is the fusion of: gb.database-manager, mysql-query-browser and mysql-administrator" +#~ msgstr "Ceci est une application pour gérer le serveur Mysql, elle est basée sur gb.mysql. Son propos est de procurer une interface conviviale à Mysql, c'est la fusion entre : gb.database-manager, mysql-query-browser et mysql-administrator" + +#~ msgid "General Information:" +#~ msgstr "Information générale :" + +#~ msgid "Generate" +#~ msgstr "Générer" + +#~ msgid "Destination:" +#~ msgstr "Destination" + +#~ msgid "TXT" +#~ msgstr "-" + +#~ msgid "XML" +#~ msgstr "-" + +#~ msgid "HTML" +#~ msgstr "HTML" + +#~ msgid "Format Selection:" +#~ msgstr "Sélection du format :" + +#~ msgid "Procedures" +#~ msgstr "Procédures" + +#~ msgid "Functions" +#~ msgstr "Functions" + +#~ msgid "Databases" +#~ msgstr "Base de données" + +#~ msgid "Object Selection:" +#~ msgstr "Sélection d'objet :" + +#~ msgid "Select the databases:" +#~ msgstr "Base de données sélectionnées :" + +#~ msgid "Generate Documentation" +#~ msgstr "Générer la documentation" + +#~ msgid "Diff" +#~ msgstr "Diff" + +#~ msgid "No difference" +#~ msgstr "Aucune différence" + +#~ msgid "Cancel" +#~ msgstr "Annuler" + +#~ msgid "Changes" +#~ msgstr "Changements" + +#~ msgid "Password" +#~ msgstr "Mot de passe " + +#~ msgid "User" +#~ msgstr "Utilisateur" + +#~ msgid "Authentification" +#~ msgstr "--" + +#~ msgid "Previous description" +#~ msgstr "Description précédentes" + +#~ msgid "Clean up" +#~ msgstr "Nettoyer tout" + +#~ msgid "Unindent" +#~ msgstr "Désindenter" + +#~ msgid "Indent" +#~ msgstr "Indenter" + +#~ msgid "Redo" +#~ msgstr "Refaire " + +#~ msgid "Undo" +#~ msgstr "Défaire" + +#~ msgid "Paste" +#~ msgstr "Coller" + +#~ msgid "Copy" +#~ msgstr "Copier" + +#~ msgid "Cut" +#~ msgstr "Couper" + +#~ msgid "Commit description" +#~ msgstr "Description de validation" + +#~ msgid "Description" +#~ msgstr "Description" + +#~ msgid "Commit" +#~ msgstr "Valider" + +#~ msgid "Please enter the password" +#~ msgstr "SVP entrez le mot de passe" + +#~ msgid "Please enter the change log" +#~ msgstr "SVP entrez le fichier log " + +#~ msgid "Please enter the versioning user name" +#~ msgstr "SVP entrez le nom de version utilisateur" + +#~ msgid "Server" +#~ msgstr "Serveur" + +#~ msgid "Close all forms" +#~ msgstr "Fermer tous les formulaires" + +#~ msgid "Close form" +#~ msgstr "Fermer le formulaire" + +#~ msgid "Hide panels" +#~ msgstr "Cacher le panneau" + +#~ msgid "Main" +#~ msgstr "Principal" + +#~ msgid "File" +#~ msgstr "Fichier" + +#~ msgid "Processes" +#~ msgstr "Taches" + +#~ msgid "Preferences" +#~ msgstr "Préferrences" + +#~ msgid "Documentation" +#~ msgstr "Documentation" + +#~ msgid "Help" +#~ msgstr "Aide" + +#~ msgid "MySQL Help" +#~ msgstr "Aide Mysql" + +#~ msgid "Script" +#~ msgstr "Script" + +#~ msgid "Table Maintenance" +#~ msgstr "Table de maintenance" + +#~ msgid "Query" +#~ msgstr "Requete" + +#~ msgid "Schemas" +#~ msgstr "Schémas" + +#~ msgid "Schema" +#~ msgstr "Schéma" + +#~ msgid "Restore Backup" +#~ msgstr "Restauration de la sauvergarde" + +#~ msgid "User Administration" +#~ msgstr "Administrateur " + +#~ msgid "Server Information" +#~ msgstr "Info serveur" + +#~ msgid "Backup Content" +#~ msgstr "Contenu de la sauvegarde" + +#~ msgid "Catalogs" +#~ msgstr "Catalogues" + +#~ msgid "&Start Backup" +#~ msgstr "&Démarrer la sauvegarde" + +#~ msgid "Project Name:" +#~ msgstr "Nom du projet :" + +#~ msgid "Backup Project
Define the project name and the content of the backup" +#~ msgstr "Projet de sauvegarde
Défini le nom du projet et le contenu de la sauvegarde" + +#~ msgid "Backup" +#~ msgstr "Sauvegarde" + +#~ msgid "Done" +#~ msgstr "Fait" + +#~ msgid "Please select a database" +#~ msgstr "SVP sélectionnez une base de données" + +#~ msgid "Data Length" +#~ msgstr "Longueur des données" + +#~ msgid "Table Type" +#~ msgstr "Type de table" + +#~ msgid "Objects" +#~ msgstr "Objets" + +#~ msgid "" +#~ "MYSQL-GUI MySQL Database administrator for Gambas.\n" +#~ "Copyright (C) 2008-2010 David Villalobos Cambronero.\n" +#~ "\n" +#~ "This program is free software: you can redistribute it and/or modify\n" +#~ "it under the terms of the GNU General Public License as published by\n" +#~ "the Free Software Foundation, either version 3 of the License, or\n" +#~ "(at your option) any later version.\n" +#~ "\n" +#~ "This program is distributed in the hope that it will be useful,\n" +#~ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +#~ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +#~ "GNU General Public License for more details.\n" +#~ "\n" +#~ "You should have received a copy of the GNU General Public License\n" +#~ "along with this program. If not, see ." +#~ msgstr "" +#~ "MYSQL-GUI MySQL administrateur de base de données pour Gambas.\n" +#~ "Copyright (C) 2008-2010 David Villalobos Cambronero.\n" +#~ "\n" +#~ " (traduction française Henri Girard). Ce programme est un logiciel libre : Vous pouvez le distribuer et/ou le modifier\n" +#~ "Selon les termes de GNU General Public License, telle que publiée part\n" +#~ "the Free Software Foundation, Soit en version 3 de la License, ou\n" +#~ "(selon votre choix) dans n'importe-quelle version ultérieure.\n" +#~ "\n" +#~ "Ce programme est distribué avec l'espoir qu'il soit utile,\n" +#~ "mais absolument sans aucune garantie ; Meme sans aucune garantie que l'on pourrait attendre d'un logiciel, de commercialisation ou d'utilité dans un domaine précis.\n" +#~ "Voir GNU General Public License pour plus de détail.\n" +#~ "\n" +#~ "Vous devriez avoir reçu une copie de GNU General Public License\n" +#~ "avec ce programme. Sinon voyez ." + +#~ msgid "License" +#~ msgstr "License" + +#~ msgid "About" +#~ msgstr "A propos" + +#~ msgid "A simple Graphic User Interface for MySQL" +#~ msgstr "Un simple GUI pour MySQL" + +#~ msgid "mysql-gui" +#~ msgstr "-" diff --git a/app/examples/Database/MySQLExample/.lang/nl.po b/app/examples/Database/MySQLExample/.lang/nl.po new file mode 100644 index 00000000..1f684132 --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/nl.po @@ -0,0 +1,531 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: MySQLExample 3.5.90\n" +"PO-Revision-Date: 2014-09-25 20:40 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "MySQL Voorbeeld" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Een eenvoudig GB.MYSQL voorbeeld" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Verbind met een MySQL Server" + +#: FConnect.form:39 +msgid "Server:" +msgstr "-" + +#: FConnect.form:50 +msgid "Port:" +msgstr "Poort:" + +#: FConnect.form:61 +msgid "User:" +msgstr "Gebruiker:" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Wachtwoord:" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Verbind" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Opschonen" + +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Annuleren" + +#: FMessage.form:8 +msgid "Message" +msgstr "Bericht" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Creëer Database" + +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "Naam:" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "Collatie:" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "Karakterset:" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Creëer" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Creëer Gebeurtenis" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Schema:" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Comma gesepareerde waarden: Hola1 INT, Hola2 CHAR(5)" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Aanwijzing weergeven" + +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "&Ok" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Bewerk Gebeurtenis" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Veld toevoegen" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "-" + +#: FNewField.form:57 +msgid "Accepts NULLs:" +msgstr "Accepteert NULL's" + +#: FNewField.form:63 +msgid "No" +msgstr "Neen" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Ja" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Standaard:" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Commentaar:" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Bewerk Veld" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Creëer Index" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Verwijder" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Velden:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Primaire Sleutel" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Bewerk Index" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Creëer Routine" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "-" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Retourneren:" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Bewerk Routine" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Creëer Tabel" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "Motor:" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Bewerk Tabel" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Creëer Trigger" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Tijd:" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Gebeurtenis:" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Bewerk Trigger" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Creëer Weergave" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Bewerk Weergave" + +#: FTables.form:35 +msgid "Tables" +msgstr "Tabellen" + +#: FTables.form:40 +msgid "Item" +msgstr "Element" + +#: FTables.form:45 +msgid "New Item" +msgstr "Nieuw Element" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Bewerk Element" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Verwijder Element" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Vernieuwen" + +#: FTables.form:84 +msgid "Lock" +msgstr "Slot" + +#: FTables.form:96 +msgid "New Table" +msgstr "Nieuwe Tabel" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Verwijder Tabel" + +#: FTables.form:121 +msgid "New Database" +msgstr "Nieuwe Database" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Verwijder Database" + +#: FTables.form:159 +msgid "Views" +msgstr "Weergaven" + +#: FTables.form:169 +msgid "Indexes" +msgstr "Indices" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Velden aan:" + +#: FTables.form:188 +msgid "Routines" +msgstr "-" + +#: FTables.form:198 +msgid "Triggers" +msgstr "-" + +#: FTables.form:208 +msgid "Events" +msgstr "Gebeurtenissen" + +#: FTables.class:9 +msgid "Name" +msgstr "Naam" + +#: FTables.class:10 +msgid "Rows" +msgstr "Rijen" + +#: FTables.class:11 +msgid "Engine" +msgstr "Motor" + +#: FTables.class:12 +msgid "Charset" +msgstr "Karakterset" + +#: FTables.class:13 +msgid "Collation" +msgstr "Collatie" + +#: FTables.class:14 +msgid "Size" +msgstr "Maat" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Update tijd" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Actualiseerbaar" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definiëerder" + +#: FTables.class:22 +msgid "Security" +msgstr "Beveiliging" + +#: FTables.class:23 +msgid "Check" +msgstr "Controleer" + +#: FTables.class:30 +msgid "Column" +msgstr "Kolom" + +#: FTables.class:32 +msgid "Type" +msgstr "-" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retourneren" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Creatietijd" + +#: FTables.class:43 +msgid "Comment" +msgstr "Commentaar" + +#: FTables.class:49 +msgid "Default" +msgstr "Standaard" + +#: FTables.class:50 +msgid "Nullable" +msgstr "-" + +#: FTables.class:51 +msgid "DataType" +msgstr "-" + +#: FTables.class:52 +msgid "Key" +msgstr "Sleutel" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FTables.class:61 +msgid "Event" +msgstr "Gebeurtenis" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabel" + +#: FTables.class:63 +msgid "Timing" +msgstr "-" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Tijd Zone" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Uitvoeren op" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Interval Waarde" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Interval Veld" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "SQL Modus" + +#: FTables.class:76 +msgid "Starts" +msgstr "-" + +#: FTables.class:77 +msgid "Ends" +msgstr "-" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Bij voltooiing" + +#: FTables.class:79 +msgid "Created" +msgstr "Gecreëerd" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Laast gewijzigd" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Laatst uitgevoerd" + +#: FTables.class:82 +msgid "Originator" +msgstr "-" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Database collatie" + +#: FTables.class:112 +msgid "Fields" +msgstr "Velden" + +#: FTables.class:330 +msgid "Not available" +msgstr "Niet beschikbaar" + +#: FTables.class:363 +msgid "Indexes on:" +msgstr "Indices aan:" + +#: FTables.class:880 +msgid "New View" +msgstr "Nieuwe Weergave" + +#: FTables.class:881 +msgid "Delete View" +msgstr "Verwijder Weergave" + +#: FTables.class:883 +msgid "New Index" +msgstr "Nieuwe Index" + +#: FTables.class:884 +msgid "Delete Index" +msgstr "Verwijder Index" + +#: FTables.class:886 +msgid "New Field" +msgstr "Nieuw Veld" + +#: FTables.class:887 +msgid "Delete Field" +msgstr "Verwijder Veld" + +#: FTables.class:889 +msgid "New Routine" +msgstr "Nieuwe Routine" + +#: FTables.class:890 +msgid "Delete Routine" +msgstr "Verwijder Routine" + +#: FTables.class:892 +msgid "New Trigger" +msgstr "Nieuwe Trigger" + +#: FTables.class:893 +msgid "Delete Trigger" +msgstr "Verwijder Trigger" + +#: FTables.class:895 +msgid "New Event" +msgstr "Nieuwe Gebeurtenis" + +#: FTables.class:896 +msgid "Delete Event" +msgstr "Verwijder Gebeurtenis" + +#: FTables.class:927 +msgid "Do you realy want to delete the database: &1?" +msgstr "Wil je werkelijk de database &1 verwijderen?" + +#: FTables.class:927 +msgid "&Yes" +msgstr "&Ja" + +#: FTables.class:927 +msgid "&No" +msgstr "&Neen" + +#: FTables.class:1019 +msgid "Do you realy want to delete the table: &1?" +msgstr "Wil je werkelijk de tabel &1 verwijderen?" + +#: FTables.class:1021 +msgid "Do you realy want to delete the view: &1?" +msgstr "Wil je werkelijk de weergave &1 verwijderen?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the index: &1?" +msgstr "Wil je werkelijk de index &1 verwijderen?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the field: &1?" +msgstr "Wil je werkelijk het veld &1 verwijderen?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Wil je werkelijk de routine &1 verwijderen?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Wil je werkelijk de trigger &1 verwijderen?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the event: &1?" +msgstr "Wil je werkelijk de gebeurtenis &1 verwijderen?" + +#: FTables.class:1034 +msgid "Item deleted." +msgstr "Element verwijdert:" + diff --git a/app/examples/Database/MySQLExample/.lang/ru.po b/app/examples/Database/MySQLExample/.lang/ru.po new file mode 100644 index 00000000..b0907bbc --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/ru.po @@ -0,0 +1,554 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Database/MySQLExample/.project:27 app/examples/Database/MySQLExample/.src/FConnect.form:12 +msgid "MySQL Example" +msgstr "Пример MySQL" + +#: app/examples/Database/MySQLExample/.project:28 +msgid "A simple GB.MYSQL Example" +msgstr "Простой пример GB.MYSQL" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:5 +msgid "MySQL GUI" +msgstr "GUI MySQL" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:18 +msgid "Connect to a MySQL Server" +msgstr "Соединиться с сервером MySQL" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:21 +msgid "Server:" +msgstr "Сервер:" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:30 +msgid "Port:" +msgstr "Порт:" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:39 +msgid "User:" +msgstr "Пользователь:" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:48 +msgid "Password:" +msgstr "Пароль:" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:58 +msgid "&Connect" +msgstr "Соединить" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:64 +msgid "&Clear" +msgstr "Очистить" + +#: app/examples/Database/MySQLExample/.src/FConnect.form:69 app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form:50 app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:46 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:73 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:56 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:58 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form:44 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:57 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form:37 +msgid "&Cancel" +msgstr "Отмена" + +#: app/examples/Database/MySQLExample/.src/FMessage.form:5 +msgid "Message" +msgstr "Сообщение" + +#: app/examples/Database/MySQLExample/.src/FTables.class:9 app/examples/Database/MySQLExample/.src/FTables.class:19 app/examples/Database/MySQLExample/.src/FTables.class:29 app/examples/Database/MySQLExample/.src/FTables.class:37 app/examples/Database/MySQLExample/.src/FTables.class:48 app/examples/Database/MySQLExample/.src/FTables.class:60 app/examples/Database/MySQLExample/.src/FTables.class:68 +msgid "Name" +msgstr "Имя" + +#: app/examples/Database/MySQLExample/.src/FTables.class:10 +msgid "Rows" +msgstr "Строки" + +#: app/examples/Database/MySQLExample/.src/FTables.class:11 +msgid "Engine" +msgstr "Движок" + +#: app/examples/Database/MySQLExample/.src/FTables.class:12 app/examples/Database/MySQLExample/.src/FTables.class:24 app/examples/Database/MySQLExample/.src/FTables.class:54 app/examples/Database/MySQLExample/.src/FTables.class:83 +msgid "Charset" +msgstr "Кодировка" + +#: app/examples/Database/MySQLExample/.src/FTables.class:13 app/examples/Database/MySQLExample/.src/FTables.class:25 app/examples/Database/MySQLExample/.src/FTables.class:31 app/examples/Database/MySQLExample/.src/FTables.class:55 app/examples/Database/MySQLExample/.src/FTables.class:84 +msgid "Collation" +msgstr "Упорядочение" + +#: app/examples/Database/MySQLExample/.src/FTables.class:14 +msgid "Size" +msgstr "Размер" + +#: app/examples/Database/MySQLExample/.src/FTables.class:15 app/examples/Database/MySQLExample/.src/FTables.class:40 +msgid "Update Time" +msgstr "Обновить время" + +#: app/examples/Database/MySQLExample/.src/FTables.class:20 +msgid "Updatable" +msgstr "Обновляемый" + +#: app/examples/Database/MySQLExample/.src/FTables.class:21 app/examples/Database/MySQLExample/.src/FTables.class:42 app/examples/Database/MySQLExample/.src/FTables.class:64 app/examples/Database/MySQLExample/.src/FTables.class:69 +msgid "Definer" +msgstr "Определитель" + +#: app/examples/Database/MySQLExample/.src/FTables.class:22 +msgid "Security" +msgstr "Безопасность" + +#: app/examples/Database/MySQLExample/.src/FTables.class:23 +msgid "Check" +msgstr "Проверить" + +#: app/examples/Database/MySQLExample/.src/FTables.class:30 +msgid "Column" +msgstr "Столбец" + +#: app/examples/Database/MySQLExample/.src/FTables.class:32 app/examples/Database/MySQLExample/.src/FTables.class:41 app/examples/Database/MySQLExample/.src/FTables.class:71 +msgid "Type" +msgstr "Тип" + +#: app/examples/Database/MySQLExample/.src/FTables.class:38 +msgid "Returns" +msgstr "Возвращаемые данные" + +#: app/examples/Database/MySQLExample/.src/FTables.class:39 +msgid "Creation Time" +msgstr "Время создания" + +#: app/examples/Database/MySQLExample/.src/FTables.class:43 app/examples/Database/MySQLExample/.src/FTables.class:56 app/examples/Database/MySQLExample/.src/FTables.class:86 +msgid "Comment" +msgstr "Комментарий" + +#: app/examples/Database/MySQLExample/.src/FTables.class:49 +msgid "Default" +msgstr "По умолчанию" + +#: app/examples/Database/MySQLExample/.src/FTables.class:50 +msgid "Nullable" +msgstr "Допустим null" + +#: app/examples/Database/MySQLExample/.src/FTables.class:51 +msgid "DataType" +msgstr "Тип данных" + +#: app/examples/Database/MySQLExample/.src/FTables.class:52 +msgid "Key" +msgstr "Ключ" + +#: app/examples/Database/MySQLExample/.src/FTables.class:53 +msgid "Extra" +msgstr "Дополнительно" + +#: app/examples/Database/MySQLExample/.src/FTables.class:61 +msgid "Event" +msgstr "Событие" + +#: app/examples/Database/MySQLExample/.src/FTables.class:62 +msgid "Table" +msgstr "Таблица" + +#: app/examples/Database/MySQLExample/.src/FTables.class:63 +msgid "Timing" +msgstr "Тайминг" + +#: app/examples/Database/MySQLExample/.src/FTables.class:70 +msgid "Time Zone" +msgstr "Часовой пояс" + +#: app/examples/Database/MySQLExample/.src/FTables.class:72 +msgid "Execute At" +msgstr "Выполнить в" + +#: app/examples/Database/MySQLExample/.src/FTables.class:73 +msgid "Interval Value" +msgstr "Значение интервала" + +#: app/examples/Database/MySQLExample/.src/FTables.class:74 +msgid "Interval Field" +msgstr "Поле интервала" + +#: app/examples/Database/MySQLExample/.src/FTables.class:75 +msgid "SQL Mode" +msgstr "Режим SQL" + +#: app/examples/Database/MySQLExample/.src/FTables.class:76 +msgid "Starts" +msgstr "Начала" + +#: app/examples/Database/MySQLExample/.src/FTables.class:77 +msgid "Ends" +msgstr "Концы" + +#: app/examples/Database/MySQLExample/.src/FTables.class:78 +msgid "On Completion" +msgstr "На завершение" + +#: app/examples/Database/MySQLExample/.src/FTables.class:79 +msgid "Created" +msgstr "Создан" + +#: app/examples/Database/MySQLExample/.src/FTables.class:80 +msgid "Last Altered" +msgstr "Последний изменённый" + +#: app/examples/Database/MySQLExample/.src/FTables.class:81 +msgid "Last Executed" +msgstr "Последний выполненный" + +#: app/examples/Database/MySQLExample/.src/FTables.class:82 +msgid "Originator" +msgstr "Инициатор" + +#: app/examples/Database/MySQLExample/.src/FTables.class:85 +msgid "Database Collation" +msgstr "Упорядочение базы данных" + +#: app/examples/Database/MySQLExample/.src/FTables.class:111 app/examples/Database/MySQLExample/.src/FTables.form:119 +msgid "Indexes" +msgstr "Индексы" + +#: app/examples/Database/MySQLExample/.src/FTables.class:112 +msgid "Fields" +msgstr "Поля" + +#: app/examples/Database/MySQLExample/.src/FTables.class:145 app/examples/Database/MySQLExample/.src/FTables.form:128 +msgid "Fields on:" +msgstr "Поля вкл:" + +#: app/examples/Database/MySQLExample/.src/FTables.class:330 app/examples/Database/MySQLExample/.src/FTables.class:331 +msgid "Not available" +msgstr "Недоступен" + +#: app/examples/Database/MySQLExample/.src/FTables.class:363 +msgid "Indexes on:" +msgstr "Индексы по:" + +#: app/examples/Database/MySQLExample/.src/FTables.class:877 app/examples/Database/MySQLExample/.src/FTables.form:56 +msgid "New Table" +msgstr "Новая таблица" + +#: app/examples/Database/MySQLExample/.src/FTables.class:878 app/examples/Database/MySQLExample/.src/FTables.form:62 +msgid "Delete Table" +msgstr "Удалить таблицу" + +#: app/examples/Database/MySQLExample/.src/FTables.class:880 +msgid "New View" +msgstr "Новый просмотр" + +#: app/examples/Database/MySQLExample/.src/FTables.class:881 +msgid "Delete View" +msgstr "Удалить вид" + +#: app/examples/Database/MySQLExample/.src/FTables.class:883 +msgid "New Index" +msgstr "Новый индекс" + +#: app/examples/Database/MySQLExample/.src/FTables.class:884 +msgid "Delete Index" +msgstr "Удалить индекс" + +#: app/examples/Database/MySQLExample/.src/FTables.class:886 +msgid "New Field" +msgstr "Новое поле" + +#: app/examples/Database/MySQLExample/.src/FTables.class:887 +msgid "Delete Field" +msgstr "Удалить поле" + +#: app/examples/Database/MySQLExample/.src/FTables.class:889 +msgid "New Routine" +msgstr "Новая подпрограмма" + +#: app/examples/Database/MySQLExample/.src/FTables.class:890 +msgid "Delete Routine" +msgstr "Удалить подпрограмму" + +#: app/examples/Database/MySQLExample/.src/FTables.class:892 +msgid "New Trigger" +msgstr "Новый триггер" + +#: app/examples/Database/MySQLExample/.src/FTables.class:893 +msgid "Delete Trigger" +msgstr "Удалить триггер" + +#: app/examples/Database/MySQLExample/.src/FTables.class:895 +msgid "New Event" +msgstr "Новое событие" + +#: app/examples/Database/MySQLExample/.src/FTables.class:896 +msgid "Delete Event" +msgstr "Удалить событие" + +#: app/examples/Database/MySQLExample/.src/FTables.class:927 +msgid "Do you realy want to delete the database: &1?" +msgstr "Вы действительно хотите удалить БД: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:927 app/examples/Database/MySQLExample/.src/FTables.class:1019 app/examples/Database/MySQLExample/.src/FTables.class:1021 app/examples/Database/MySQLExample/.src/FTables.class:1023 app/examples/Database/MySQLExample/.src/FTables.class:1025 app/examples/Database/MySQLExample/.src/FTables.class:1027 app/examples/Database/MySQLExample/.src/FTables.class:1029 app/examples/Database/MySQLExample/.src/FTables.class:1031 +msgid "&Yes" +msgstr "Да" + +#: app/examples/Database/MySQLExample/.src/FTables.class:927 app/examples/Database/MySQLExample/.src/FTables.class:1019 app/examples/Database/MySQLExample/.src/FTables.class:1021 app/examples/Database/MySQLExample/.src/FTables.class:1023 app/examples/Database/MySQLExample/.src/FTables.class:1025 app/examples/Database/MySQLExample/.src/FTables.class:1027 app/examples/Database/MySQLExample/.src/FTables.class:1029 app/examples/Database/MySQLExample/.src/FTables.class:1031 +msgid "&No" +msgstr "Нет" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1019 +msgid "Do you realy want to delete the table: &1?" +msgstr "Вы действительно хотите удалить таблицу: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1021 +msgid "Do you realy want to delete the view: &1?" +msgstr "Вы действительно хотите удалить вид: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1023 +msgid "Do you realy want to delete the index: &1?" +msgstr "Вы действительно хотите удалить индекс: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1025 +msgid "Do you realy want to delete the field: &1?" +msgstr "Вы действительно хотите удалить поле: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1027 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Вы действительно хотите удалить подпрограмму: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1029 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Вы действительно хотите удалить триггер: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1031 +msgid "Do you realy want to delete the event: &1?" +msgstr "Вы действительно хотите удалить событие: &1?" + +#: app/examples/Database/MySQLExample/.src/FTables.class:1034 +msgid "Item deleted." +msgstr "Элемент удалён." + +#: app/examples/Database/MySQLExample/.src/FTables.form:6 app/examples/Database/MySQLExample/.src/FTables.form:102 +msgid "Tables" +msgstr "Таблицы" + +#: app/examples/Database/MySQLExample/.src/FTables.form:10 +msgid "Item" +msgstr "Элемент" + +#: app/examples/Database/MySQLExample/.src/FTables.form:14 +msgid "New Item" +msgstr "Новый элемент" + +#: app/examples/Database/MySQLExample/.src/FTables.form:20 +msgid "Edit Item" +msgstr "Редактировать элемент" + +#: app/examples/Database/MySQLExample/.src/FTables.form:26 +msgid "Delete Item" +msgstr "Удалить элемент" + +#: app/examples/Database/MySQLExample/.src/FTables.form:32 app/examples/Database/MySQLExample/.src/FTables.form:68 +msgid "Refresh" +msgstr "Освежить" + +#: app/examples/Database/MySQLExample/.src/FTables.form:46 +msgid "Lock" +msgstr "Блокировка" + +#: app/examples/Database/MySQLExample/.src/FTables.form:77 +msgid "New Database" +msgstr "Новая база данных" + +#: app/examples/Database/MySQLExample/.src/FTables.form:83 +msgid "Delete Database" +msgstr "Удалить базу данных" + +#: app/examples/Database/MySQLExample/.src/FTables.form:110 +msgid "Views" +msgstr "Виды" + +#: app/examples/Database/MySQLExample/.src/FTables.form:136 +msgid "Routines" +msgstr "Подпрограммы" + +#: app/examples/Database/MySQLExample/.src/FTables.form:145 +msgid "Triggers" +msgstr "Триггеры" + +#: app/examples/Database/MySQLExample/.src/FTables.form:154 +msgid "Events" +msgstr "События" + +#: app/examples/Database/MySQLExample/.src/modMain.module:38 +msgid "" +"Seems that your MySQL version is lower than 5.1.\n" +"therefore you will get some error and warning messages." +msgstr "" +"Кажется, что ваша версия MySQL ниже, чем 5.1.\n" +"поэтому вы получите сообщения об ошибках и предупреждения." + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form:5 app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form:12 +msgid "Create Database" +msgstr "Создать базу данных" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form:19 app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:19 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:18 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:28 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:20 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form:19 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:19 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form:19 +msgid "Name:" +msgstr "Имя:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form:27 +msgid "Collation:" +msgstr "Упорядочение:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form:36 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form:36 +msgid "Charset:" +msgstr "Кодировка:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form:44 +msgid "&Create" +msgstr "Создать" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class:19 +msgid "Edit Event" +msgstr "Редактировать событие" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:5 app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:12 +msgid "Create Event" +msgstr "Создать событие" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:27 +msgid "Schedule:" +msgstr "Расписание:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:31 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:32 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Значения, разделённые запятыми: Hola1 INT,Hola2 CHAR(5)" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:35 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:47 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:46 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form:26 +msgid "Show a clue" +msgstr "Показать подсказку" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form:40 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:67 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:50 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:52 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form:50 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:51 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form:31 +msgid "&Ok" +msgstr "ОК" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class:22 +msgid "Edit Field" +msgstr "Редактировать поле" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:5 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:11 +msgid "Add Field" +msgstr "Добавить поле" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:26 +msgid "Data Type:" +msgstr "Тип данных:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:34 +msgid "Accepts NULLs:" +msgstr "Принимает NULL:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:39 +msgid "No" +msgstr "Нет" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:39 +msgid "Yes" +msgstr "Да" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:44 +msgid "Default:" +msgstr "По умолчанию:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:52 +msgid "Extra:" +msgstr "Дополнительно:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form:60 +msgid "Comment:" +msgstr "Комментарий:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class:24 +msgid "Edit Index" +msgstr "Редактировать индекс" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:5 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:22 +msgid "Create Index" +msgstr "Создать индекс" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:9 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:13 +msgid "Delete" +msgstr "Удалить" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:36 +msgid "Fields:" +msgstr "Поля:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form:46 +msgid "Primary Key" +msgstr "Первичный ключ" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Редактировать подпрограмму" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:6 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:13 +msgid "Create Routine" +msgstr "Создать подпрограмму" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:28 +msgid "Parameters:" +msgstr "Параметры:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form:37 +msgid "Returns:" +msgstr "Возвращаемые данные:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class:21 +msgid "Edit Table" +msgstr "Редактировать таблицу" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form:5 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form:12 +msgid "Create Table" +msgstr "Создать таблицу" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form:27 +msgid "Engine:" +msgstr "Движок:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Изменить триггер" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:5 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:12 +msgid "Create Trigger" +msgstr "Создать триггер" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:27 +msgid "Time:" +msgstr "Время:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form:37 +msgid "Event:" +msgstr "Событие:" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class:18 +msgid "Edit View" +msgstr "Редактировать вид" + +#: app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form:5 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form:12 +msgid "Create View" +msgstr "Создать вид" + diff --git a/app/examples/Database/MySQLExample/.project b/app/examples/Database/MySQLExample/.project new file mode 100644 index 00000000..c52728fa --- /dev/null +++ b/app/examples/Database/MySQLExample/.project @@ -0,0 +1,26 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=MySQL Example +Startup=modMain +Icon=icons/16/Admin.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.db.mysql +Component=gb.settings +Component=gb.form.mdi +Component=gb.mysql +Description="A simple GB.MYSQL Example" +Authors="David Villalobos Cambronero\ndavid.villalobos.c@gmail.com" +TabSize=2 +Translate=1 +Language=en +SourcePath=/media +Maintainer=David Villalobos Cambronero +Address=david_villalobos_c@yahoo.com +License=General Public License +Packager=1 +Systems=autotools diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class new file mode 100644 index 00000000..ab7f91bf --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class @@ -0,0 +1,33 @@ +' Gambas class file + +Public Sub Form_Open() + + cmbCharset.List = modMain.$Connection.MySQL.Charsets + cmbCharset.Index = cmbCharset.Find("utf8") + Me.Center() + Catch + modMain.Error() + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub btnCreate_Click() + + modMain.$Connection.MySQL.DataBase.Add(txtNombre.Text, cmbCharset.Text, cmbCollations.Text) + modMain.$Connection.MySQL.Use(txtNombre.Text) + Me.Close() + Catch + modMain.Error() + +End + +Public Sub cmbCharset_Click() + + cmbCollations.List = modMain.$Connection.MySQL.CharsetCollations(cmbCharset.Text) + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form new file mode 100644 index 00000000..3f1c8c49 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form @@ -0,0 +1,54 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,36,28) + Text = ("Create Database") + Icon = Picture["icons/16/Database.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,34,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Database") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtNombre TextBox + MoveScaled(13,8,22,3) + } + { lblCollation Label + MoveScaled(1,18,11,3) + Font = Font["+1"] + Text = ("Collation:") + } + { cmbCollations ComboBox + MoveScaled(13,18,22,3) + ReadOnly = True + } + { lblCharset Label + MoveScaled(1,13,11,3) + Font = Font["+1"] + Text = ("Charset:") + } + { cmbCharset ComboBox + MoveScaled(13,13,22,3) + ReadOnly = True + } + { btnCreate Button + MoveScaled(1,23,13,4) + Text = ("&Create") + Picture = Picture["icon:/16/new"] + Default = True + } + { btnCancel Button + MoveScaled(22,23,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class new file mode 100644 index 00000000..497e99c3 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class @@ -0,0 +1,51 @@ +' Gambas class file + +Private $sEvent As String +Private $bEdit As Boolean = False + +Public Sub _new(Optional {Event} As String) + + If {Event} Then + $sEvent = {Event} + $bEdit = True + Endif + +End + +Public Sub Form_Open() + + If $bEdit Then + txtName.Enabled = False + Me.Text = ("Edit Event") + lblTitle.Text = Me.Text + txtName.Text = $sEvent + txtSchedule.Text = modMain.$Connection.MySQL.Event.Schedule($sEvent) + txtData.Text = modMain.$Connection.MySQL.Event.Definition($sEvent) + Else + txtData.Text = "BEGIN\n \nEND" + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + modMain.$Connection.MySQL.Event.Add(txtName.Text, txtSchedule.Text, txtData.Text, IIf($bEdit, True, False)) + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + txtName.Text = "FooEvent" + txtSchedule.Text = "EVERY 1 DAY STARTS NOW()" + txtData.Text = "BEGIN\n SELECT `User` FROM `mysql`.`user`;\nEND" + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form new file mode 100644 index 00000000..434054ee --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form @@ -0,0 +1,53 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,54) + Text = ("Create Event") + Icon = Picture["icon:/16/clock"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Event") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,15,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(17,8,55,3) + } + { lblSchedule Label + MoveScaled(1,12,15,3) + Font = Font["+1"] + Text = ("Schedule:") + } + { txtSchedule TextBox + MoveScaled(17,12,55,3) + ToolTip = ("Comma separated values: Hola1 INT,Hola2 CHAR(5)") + } + { btnClue Button + MoveScaled(1,49,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,49,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,49,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { txtData TextArea + MoveScaled(1,16,71,32) + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class new file mode 100644 index 00000000..afa34505 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class @@ -0,0 +1,53 @@ +' Gambas class file + +Private $sTable As String +Private $sFlied As String +Private $bEdit As Boolean = False + +Public Sub _new(Table As String, Optional Field As String) + + If Field Then + $sFlied = Field + $bEdit = True + Endif + $sTable = Table + +End + +Public Sub Form_Open() + + txtName.Text = $sFlied + If $bEdit Then + txtName.Enabled = False + Me.Text = ("Edit Field") + lblTitle.Text = Me.Text + txtDatatype.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).Datatype() + txtComment.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).Commnet() + txtDefault.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).DefaultValue() + txtExtra.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).Extra() + cmbNull.Index = CInt(modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).IsNullable()) + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + Dim sDefinition As String + + sDefinition = txtDatatype.Text & " " + If cmbNull.Index = 0 Then sDefinition &= "NOT NULL" + If txtDefault.Text <> "" Then sDefinition &= " DEFAULT '" & txtDefault.Text & "'" + If txtExtra.Text <> "" Then sDefinition &= " " & txtExtra.Text + If txtComment.Text <> "" Then sDefinition &= " COMMENT '" & txtComment.Text & "'" + modMain.$Connection.MySQL.Table.ModifyColumn($sTable, IIf($bEdit, "MODIFY", "ADD"), txtName.Text, sDefinition) + Me.Close() + Catch + modMain.Error() + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form new file mode 100644 index 00000000..d143b622 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form @@ -0,0 +1,77 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,40,37) + Text = ("Add Field") + Icon = Picture["icons/16/Field.png"] + { lblTitle Label + MoveScaled(1,1,38,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Add Field") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,26,3) + } + { lblDatatype Label + MoveScaled(1,12,15,3) + Font = Font["+1"] + Text = ("Data Type:") + } + { txtDatatype TextBox + MoveScaled(17,12,22,3) + } + { lblNull Label + MoveScaled(1,16,15,3) + Font = Font["+1"] + Text = ("Accepts NULLs:") + } + { cmbNull ComboBox + MoveScaled(17,16,8,3) + ReadOnly = True + List = [("No"), ("Yes")] + } + { lblDefault Label + MoveScaled(1,20,11,3) + Font = Font["+1"] + Text = ("Default:") + } + { txtDefault TextBox + MoveScaled(13,20,26,3) + } + { lblExtra Label + MoveScaled(1,24,11,3) + Font = Font["+1"] + Text = ("Extra:") + } + { txtExtra TextBox + MoveScaled(13,24,26,3) + } + { lblComment Label + MoveScaled(1,28,15,3) + Font = Font["+1"] + Text = ("Comment:") + } + { txtComment TextBox + MoveScaled(17,28,22,3) + } + { tbnOK Button + MoveScaled(1,32,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(26,32,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class new file mode 100644 index 00000000..c5b9242b --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class @@ -0,0 +1,79 @@ +' Gambas class file + +Private $sTable As String +Private $sIndex As String +Private $bEdit As Boolean = False + +Public Sub _new(Table As String, Optional Index As String) + + If Index Then + $sIndex = Index + $bEdit = True + Endif + $sTable = Table + cmbFields.List = modMain.$Connection.MySQL.Table.Fields(Table) + If Index = "PRIMARY" Then chkPrimaryKey.Value = True + +End + +Public Sub Form_Open() + + Dim sField As String + + If $bEdit Then + Me.Text = ("Edit Index") + lblTitle.Text = Me.Text + If $sIndex <> "PRIMARY" Then txtName.Enabled = False + txtName.Text = $sIndex + For Each sField In modMain.$Connection.MySQL.Index.Columns($sTable, $sIndex) + lstFields.Add(sField) + Next + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + If chkPrimaryKey.Value Then + modMain.$Connection.MySQL.Index.Delete($sTable, "PRIMARY") + modMain.$Connection.MySQL.Index.PrimaryKey($sTable, lstFields.List) + Else + modMain.$Connection.MySQL.Index.Modify(txtName.Text, lstFields.List, $sTable) + Endif + Else + If chkPrimaryKey.Value Then + modMain.$Connection.MySQL.Index.PrimaryKey($sTable, lstFields.List) + Else + modMain.$Connection.MySQL.Index.Add(txtName.Text, lstFields.List, $sTable) + Endif + Endif + Me.Close() + Catch + modMain.Error() + +End + +Public Sub cmbFields_Click() + + lstFields.Add(cmbFields.Text) + +End + +Public Sub lstFields_Menu() + + mnuDelete.Popup() + +End + +Public Sub Action_Activate(Name As String) As Boolean + + If Name = "Delete" Then lstFields.Remove(lstFields.Index) + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form new file mode 100644 index 00000000..1b732e44 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form @@ -0,0 +1,70 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,36,46) + Text = ("Create Index") + Icon = Picture["icons/16/Index.png"] + Resizable = False + { mnuDelete Menu + Text = ("Delete") + Visible = False + { mnuDelete2 Menu + Action = "Delete" + Text = ("Delete") + Picture = Picture["icon:/16/trash"] + Shortcut = "Del" + } + } + { lblTitle Label + MoveScaled(1,1,34,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Index") + Alignment = Align.Center + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,22,3) + } + { lblFields Label + MoveScaled(1,12,11,3) + Font = Font["+1"] + Text = ("Fields:") + } + { lstFields ListBox + MoveScaled(13,12,22,19) + } + { cmbFields ComboBox + MoveScaled(13,32,22,3) + } + { chkPrimaryKey CheckBox + MoveScaled(1,37,34,3) + Text = ("Primary Key") + } + { tbnOK Button + MoveScaled(1,41,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(22,41,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} + +# Gambas Action File 3.0 + +{ Actions + { Action Delete + Text = "Delete" + Shortcut = "Del" + Picture = "icon:/16/trash" + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class new file mode 100644 index 00000000..e0bdb26b --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class @@ -0,0 +1,65 @@ +' Gambas class file + +Private $sRutine As String +Private $bEdit As Boolean = False + +Public Sub _new(Optional Rutine As String) + + If Rutine Then + $sRutine = Rutine + $bEdit = True + Endif + txtName.Text = $sRutine + +End + +Public Sub Form_Open() + + If $bEdit Then + Me.Text = ("Edit Routine") + lblTitle.Text = Me.Text + txtName.Enabled = False + txtParameters.Text = modMain.$Connection.MySQL.Routines.Parameters($sRutine) + txtReturns.Text = modMain.$Connection.MySQL.Routines.Returns($sRutine) + txtData.Text = modMain.$Connection.MySQL.Routines.Definition($sRutine) + Else + txtData.Text = "BEGIN\n \nEND" + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + modMain.$Connection.MySQL.Routines.Modify($sRutine, txtData.Text, Split(Trim(txtParameters.Text)), txtReturns.Text) + Else + modMain.$Connection.MySQL.Routines.Add(txtName.Text, txtData.Text, Split(Trim(txtParameters.Text)), txtReturns.Text) + Endif + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + If txtName.Text = "FooProcedure" Then + txtName.Text = "FooFunction" + txtParameters.Text = "Param1 INT,Param2 VARCHAR(200)" + txtReturns.Text = "INT" + txtData.Text = "BEGIN\n SELECT `User` FROM `mysql`.`user` WHERE `User` = `Param2`;\n" + txtData.Text &= " RETURN `Param1`\nEND" + Else + txtName.Text = "FooProcedure" + txtParameters.Text = "Param1 INT,Param2 VARCHAR(200)" + txtReturns.Text = "" + txtData.Text = "BEGIN\n SELECT `User` FROM `mysql`.`user` WHERE `User` = `Param2`;\nEND" + Endif + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form new file mode 100644 index 00000000..6db63ba7 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form @@ -0,0 +1,71 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,58) + Action = "Show a clue" + Text = ("Create Routine") + Icon = Picture["icons/16/Routine.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Routine") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(15,8,57,3) + } + { lblParameters Label + MoveScaled(1,12,13,3) + Font = Font["+1"] + Text = ("Parameters:") + } + { txtParameters TextBox + MoveScaled(15,12,57,3) + ToolTip = ("Comma separated values: Hola1 INT,Hola2 CHAR(5)") + } + { lblReturns Label + MoveScaled(1,16,10,3) + Font = Font["+1"] + Text = ("Returns:") + } + { txtReturns TextBox + MoveScaled(15,16,57,3) + } + { txtData TextArea + MoveScaled(1,20,71,32) + } + { btnClue Button + MoveScaled(1,53,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,53,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,53,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} + +# Gambas Action File 3.0 + +{ Actions + { Action Show a clue + Text = "Create Routine" + Picture = "icons/16/Routine.png" + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class new file mode 100644 index 00000000..13070aeb --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class @@ -0,0 +1,55 @@ +' Gambas class file + +Private $sTable As String +Private $bEdit As Boolean = False + +Public Sub _new(Optional Table As String) + + If Table Then + $sTable = Table + $bEdit = True + Endif + txtName.Text = $sTable + +End + +Public Sub Form_Open() + + cmbEngine.List = modMain.$Connection.MySQL.Engines + cmbCharset.List = modMain.$Connection.MySQL.Charsets + If $bEdit Then + Me.Text = ("Edit Table") + lblTitle.Text = Me.Text + txtName.Enabled = False + cmbEngine.Index = cmbEngine.Find(modMain.$Connection.MySQL.Table.Engine($sTable)) + cmbCharset.Index = cmbCharset.Find(modMain.$Connection.MySQL.Table.Charset($sTable)) + Else + cmbEngine.Index = cmbEngine.Find("InnoDB") + cmbCharset.Index = cmbCharset.Find("utf8") + Endif + Catch + modMain.Error() + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + modMain.$Connection.MySQL.Table.Modify($sTable,, cmbEngine.Text, cmbCharset.Text) + Else + modMain.$Connection.MySQL.Field.Add("Field01", modMain.$Connection.MySQL.DataTypes.Serial) + modMain.$Connection.MySQL.Table.Add(txtName.Text, cmbEngine.Text, cmbCharset.Text) + Endif + modMain.$hFBrowser.LoadDatabases() + modMain.$hFTables.SearchInfo() + Me.Close() + Catch + modMain.Error() + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form new file mode 100644 index 00000000..a28aa27e --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form @@ -0,0 +1,54 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,36,28) + Text = ("Create Table") + Icon = Picture["icons/16/Table.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,34,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Table") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,22,3) + } + { lblEngine Label + MoveScaled(1,13,11,3) + Font = Font["+1"] + Text = ("Engine:") + } + { cmbEngine ComboBox + MoveScaled(13,13,22,3) + ReadOnly = True + } + { lblCharset Label + MoveScaled(1,18,11,3) + Font = Font["+1"] + Text = ("Charset:") + } + { cmbCharset ComboBox + MoveScaled(13,18,22,3) + ReadOnly = True + } + { btnCancel Button + MoveScaled(22,23,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { tbnOK Button + MoveScaled(1,23,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class new file mode 100644 index 00000000..dc57a785 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class @@ -0,0 +1,62 @@ +' Gambas class file + +Private $sTable As String +Private $sTrigger As String +Private $bEdit As Boolean = False + +Public Sub _new(Table As String, Optional Trigger As String) + + $sTable = Table + $sTrigger = Trigger + cmbTime.Add("AFTER") + cmbTime.Add("BEFORE") + cmbEvent.Add("DELETE") + cmbEvent.Add("INSERT") + cmbEvent.Add("UPDATE") + If Trigger Then + txtName.Text = Trigger + cmbTime.Text = modMain.$Connection.MySQL.Trigger.Time($sTrigger) + cmbEvent.Text = modMain.$Connection.MySQL.Trigger.Event($sTrigger) + $bEdit = True + Endif + +End + +Public Sub Form_Open() + + If $bEdit Then + Me.Text = ("Edit Trigger") + lblTitle.Text = Me.Text + txtName.Enabled = False + Endif + If $sTrigger Then txtData.Text = modMain.$Connection.MySQL.Trigger.Info($sTrigger) + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + modMain.$Connection.MySQL.Trigger.Modify($sTrigger, $sTable, cmbTime.Text, cmbEvent.Text, txtData.Text) + Else + modMain.$Connection.MySQL.Trigger.Add(txtName.Text, $sTable, cmbTime.Text, cmbEvent.Text, txtData.Text) + Endif + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + txtName.Text = "FooTrigger" + cmbEvent.Index = 0 + cmbTime.Index = 0 + txtData.Text = "BEGIN\n INSERT INTO `MyTABLE` VALUES ('VALUES');\nEND)" + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form new file mode 100644 index 00000000..1b38930e --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,55) + Text = ("Create Trigger") + Icon = Picture["icons/16/Trigger.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Trigger") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,59,3) + } + { lblTime Label + MoveScaled(1,13,11,3) + Font = Font["+1"] + Text = ("Time:") + } + { cmbTime ComboBox + MoveScaled(13,13,15,3) + ReadOnly = True + List = [] + } + { lblEvent Label + MoveScaled(30,13,11,3) + Font = Font["+1"] + Text = ("Event:") + } + { cmbEvent ComboBox + MoveScaled(42,13,15,3) + ReadOnly = True + List = [] + } + { btnClue Button + MoveScaled(1,50,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,50,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,50,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { txtData TextArea + MoveScaled(1,17,71,32) + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class new file mode 100644 index 00000000..c81ca558 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class @@ -0,0 +1,45 @@ +' Gambas class file + +Private $bEdit As Boolean = False + +Public Sub _new(Optional View As String) + + If View Then + txtName.Text = View + $bEdit = True + txtData.Text = modMain.$Connection.MySQL.View.Definition(txtName.Text) & "\n" + Endif + +End + +Public Sub Form_Open() + + If $bEdit Then + Me.Text = ("Edit View") + lblTitle.Text = Me.Text + txtName.Enabled = False + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + modMain.$Connection.MySQL.View.Add(txtName.Text, txtData.Text, $bEdit) + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + txtName.Text = "FooView" + txtData.Text = "SELECT * FROM `mysql`.`user`" + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form new file mode 100644 index 00000000..38e94b7d --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form @@ -0,0 +1,44 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,50) + Text = ("Create View") + Icon = Picture["icons/16/View.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create View") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,59,3) + } + { btnClue Button + MoveScaled(1,45,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,45,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,45,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { txtData TextArea + MoveScaled(1,12,71,32) + } +} diff --git a/app/examples/Database/MySQLExample/.src/FConnect.class b/app/examples/Database/MySQLExample/.src/FConnect.class new file mode 100644 index 00000000..f4e5f7e9 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FConnect.class @@ -0,0 +1,46 @@ +' Gambas class file + +Public Sub btnCancel_Click() + + Me.Close(True) + +End + +Public Sub btnConnect_Click() + + modMain.$Host = txtServer.Text + modMain.$Password = txtPassword.Text + modMain.$Port = txtPort.Text + modMain.$User = txtUser.Text + Me.Close(False) + Catch + Message.Error(Error.Text) + +End + +Public Sub btnClear_Click() + + txtPassword.Text = Null + txtPort.Text = Null + txtServer.Text = Null + txtUser.Text = Null + txtPassword.SetFocus() + +End + +Public Sub Form_Open() + + txtServer.Text = Settings["/Conecction/Host", "localhost"] + txtUser.Text = Settings["/Conecction/User", "root"] + txtPort.Text = Settings["/Conecction/Port", "3306"] + txtPassword.SetFocus() + +End + +Public Sub Form_Close() + + If txtServer.Text Then Settings["/Conecction/Host"] = txtServer.Text + If txtUser.Text Then Settings["/Conecction/User"] = txtUser.Text + If txtPort.Text Then Settings["/Conecction/Port"] = txtPort.Text + +End diff --git a/app/examples/Database/MySQLExample/.src/FConnect.form b/app/examples/Database/MySQLExample/.src/FConnect.form new file mode 100644 index 00000000..1e29323b --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FConnect.form @@ -0,0 +1,73 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,49,33) + Text = ("MySQL GUI") + Icon = Picture["icons/16/Admin.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,47,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("MySQL Example") + Alignment = Align.Center + Border = Border.Sunken + } + { frmMain Frame + MoveScaled(1,8,47,19) + Text = ("Connect to a MySQL Server") + { lblServer Label + MoveScaled(1,3,19,3) + Text = ("Server:") + Alignment = Align.Right + } + { txtServer TextBox Texts + Name = "txtServer" + MoveScaled(21,3,25,3) + } + { lblPort Label + MoveScaled(1,7,19,3) + Text = ("Port:") + Alignment = Align.Right + } + { txtPort TextBox Texts + Name = "txtPort" + MoveScaled(21,7,25,3) + } + { lblUser Label + MoveScaled(1,11,19,3) + Text = ("User:") + Alignment = Align.Right + } + { txtUser TextBox Texts + Name = "txtUser" + MoveScaled(21,11,25,3) + } + { lblPassword Label + MoveScaled(1,15,19,3) + Text = ("Password:") + Alignment = Align.Right + } + { txtPassword TextBox + MoveScaled(21,15,25,3) + Password = True + } + } + { btnConnect Button + MoveScaled(1,28,15,4) + Text = ("&Connect") + Picture = Picture["icon:/16/connect"] + Default = True + } + { btnClear Button + MoveScaled(17,28,15,4) + Text = ("&Clear") + Picture = Picture["icon:/16/clear"] + } + { btnCancel Button + MoveScaled(33,28,15,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/FMessage.class b/app/examples/Database/MySQLExample/.src/FMessage.class new file mode 100644 index 00000000..8cdb33f7 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FMessage.class @@ -0,0 +1,21 @@ +' Gambas class file + +Public Sub _new(Message As String) + + txaMesage.Text = Message + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Esc Then Me.Close() + +End + +Public Sub txaMesage_KeyPress() + + If Key.Code = 81 Then '81 is q (in lowercase) + If Key.Alt Then txaMesage.Text &= "\n\n\n" & modMain.$Connection.MySQL.Query + Endif + +End diff --git a/app/examples/Database/MySQLExample/.src/FMessage.form b/app/examples/Database/MySQLExample/.src/FMessage.form new file mode 100644 index 00000000..e0a2267c --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FMessage.form @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,67,39) + Text = ("Message") + Icon = Picture["icons/16/Warning.png"] + Resizable = False + { txaMesage TextArea + MoveScaled(1,1,65,37) + Wrap = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/FTables.class b/app/examples/Database/MySQLExample/.src/FTables.class new file mode 100644 index 00000000..7d4fd2d7 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FTables.class @@ -0,0 +1,1186 @@ +' Gambas class file + +Private $sColumnSort As String + +Public Procedure _new() + + 'tvwTables definition + tvwTables.Columns.Count = 7 + tvwTables.Columns[0].Title = ("Name") + tvwTables.Columns[1].Title = ("Rows") + tvwTables.Columns[2].Title = ("Engine") + tvwTables.Columns[3].Title = ("Charset") + tvwTables.Columns[4].Title = ("Collation") + tvwTables.Columns[5].Title = ("Size") + tvwTables.Columns[6].Title = ("Update Time") + + 'tvwViews definition + tvwViews.Columns.Count = 7 + tvwViews.Columns[0].Title = ("Name") + tvwViews.Columns[1].Title = ("Updatable") + tvwViews.Columns[2].Title = ("Definer") + tvwViews.Columns[3].Title = ("Security") + tvwViews.Columns[4].Title = ("Check") + tvwViews.Columns[5].Title = ("Charset") + tvwViews.Columns[6].Title = ("Collation") + + 'tvwIndex definition + tvwIndexes.Columns.Count = 4 + tvwIndexes.Columns[0].Title = ("Name") + tvwIndexes.Columns[1].Title = ("Column") + tvwIndexes.Columns[2].Title = ("Collation") + tvwIndexes.Columns[3].Title = ("Type") + + 'tvwProcedures definition + tvwProcedures.Columns.Count = 8 + tvwProcedures.Columns[0].Width = 30 + tvwProcedures.Columns[1].Title = ("Name") + tvwProcedures.Columns[2].Title = ("Returns") + tvwProcedures.Columns[3].Title = ("Creation Time") + tvwProcedures.Columns[4].Title = ("Update Time") + tvwProcedures.Columns[5].Title = ("Type") + tvwProcedures.Columns[6].Title = ("Definer") + tvwProcedures.Columns[7].Title = ("Comment") + + 'tvwFields definition + tvwFields.Columns.Count = 10 + tvwFields.Columns[0].Width = 30 + tvwFields.Columns[1].Title = ("Name") + tvwFields.Columns[2].Title = ("Default") + tvwFields.Columns[3].Title = ("Nullable") + tvwFields.Columns[4].Title = ("DataType") + tvwFields.Columns[5].Title = ("Key") + tvwFields.Columns[6].Title = ("Extra") + tvwFields.Columns[7].Title = ("Charset") + tvwFields.Columns[8].Title = ("Collation") + tvwFields.Columns[9].Title = ("Comment") + + 'tvwTrigger definition + tvwTriggers.Columns.Count = 5 + tvwTriggers.Columns[0].Title = ("Name") + tvwTriggers.Columns[1].Title = ("Event") + tvwTriggers.Columns[2].Title = ("Table") + tvwTriggers.Columns[3].Title = ("Timing") + tvwTriggers.Columns[4].Title = ("Definer") + + 'tvwEvents definition + tvwEvents.Columns.Count = 19 + tvwEvents.Columns[0].Title = ("Name") + tvwEvents.Columns[1].Title = ("Definer") + tvwEvents.Columns[2].Title = ("Time Zone") + tvwEvents.Columns[3].Title = ("Type") + tvwEvents.Columns[4].Title = ("Execute At") + tvwEvents.Columns[5].Title = ("Interval Value") + tvwEvents.Columns[6].Title = ("Interval Field") + tvwEvents.Columns[7].Title = ("SQL Mode") + tvwEvents.Columns[8].Title = ("Starts") + tvwEvents.Columns[9].Title = ("Ends") + tvwEvents.Columns[10].Title = ("On Completion") + tvwEvents.Columns[11].Title = ("Created") + tvwEvents.Columns[12].Title = ("Last Altered") + tvwEvents.Columns[13].Title = ("Last Executed") + tvwEvents.Columns[14].Title = ("Originator") + tvwEvents.Columns[15].Title = ("Charset") + tvwEvents.Columns[16].Title = ("Collation") + tvwEvents.Columns[17].Title = ("Database Collation") + tvwEvents.Columns[18].Title = ("Comment") + + Catch + modMain.Error() + +End + +Public Procedure SearchInfo() 'Search for the info in the database + + Inc Application.Busy + tvwTables.Clear() + tvwTables.Rows.Count = 0 + tvwViews.Clear() + tvwViews.Rows.Count = 0 + tvwIndexes.Clear() + tvwIndexes.Rows.Count = 0 + tvwFields.Clear() + tvwFields.Rows.Count = 0 + tvwProcedures.Clear() + tvwProcedures.Rows.Count = 0 + tvwTriggers.Clear() + tvwTriggers.Rows.Count = 0 + tvwEvents.Clear() + tvwEvents.Rows.Count = 0 + + tabData[2].Text = ("Indexes") + tabData[3].Text = ("Fields") + 'Database's info + textResult.Text = modMain.$Connection.MySQL.DataBase.Info(modMain.$Connection.Name) + + TableInfo() + + 'Returns if the database is information_schema + If modMain.$Connection.Name = "information_schema" Then + Dec Application.Busy + Return + Endif + + ViewInfo() + + IndexInfo() + + RoutinesInfo() + + TriggersInfo() + + If modMain.$bSchemaOk Then EventInfo() + + Dec Application.Busy + Catch + modMain.Error() + +End + +Public Procedure FieldInfo(Optional Column As Integer) 'Field's Info + + Dim iCounter As Integer + + If tvwTables.Rows.Count = 0 Or tvwTables.Row = -1 Then Return 'Empty set or not table selected + tabData[3].Text = ("Fields on:") & " " & tvwTables[tvwTables.Row, 0].Text + Select (Column) + Case 1 + $sColumnSort = "COLUMN_NAME" + Case 2 + $sColumnSort = "COLUMN_DEFAULT" + Case 3 + $sColumnSort = "IS_NULLABLE" + Case 4 + $sColumnSort = "COLUMN_TYPE" + Case 5 + $sColumnSort = "COLUMN_KEY" + Case 6 + $sColumnSort = "EXTRA" + Case 7 + $sColumnSort = "CHARACTER_SET_NAME" + Case 8 + $sColumnSort = "COLLATION_NAME" + Case 9 + $sColumnSort = "COLUMN_COMMENT" + Default + $sColumnSort = "COLUMN_NAME" + End Select + modMain.$hResult = modMain.$Connection.Exec("SELECT `COLUMN_KEY`, `DATA_TYPE`, `EXTRA`, `COLUMN_NAME`, `COLUMN_DEFAULT`, `IS_NULLABLE`, `COLUMN_TYPE`, `CHARACTER_SET_NAME`, `COLLATION_NAME`, `COLUMN_COMMENT` FROM `information_schema`.`COLUMNS` WHERE `TABLE_NAME` = '" & tvwTables[tvwTables.Row, 0].Text & "' AND `TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + tvwFields.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + Select (Upper(modMain.$hResult!DATA_TYPE)) + Case "INT", "MEDIUMINT", "TINYINT", "BOOL", "BOOLEAN", "SMALLINT", "INTEGER", "FLOAT", "BIGINT", "SERIAL", "DOUBLE", "DOUBLEPRECISION", "DECIMAL", "DEC" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Numeric.png"] + Case "VARCHAR", "CHAR", "BIT", "BINARY", "VARBINARY", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "SET", "ENUM" + tvwFields[iCounter, 0].Picture = Picture["icons/16/String.png"] + Case "DATE", "DATETIME", "TIME", "TIMESTAMP", "YEAR" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Datetime.png"] + Case "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "BLOB" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Blob.png"] + Default + tvwFields[iCounter, 0].Picture = Picture["icons/16/Field.png"] + End Select + Select (Upper(modMain.$hResult!COLUMN_KEY)) + Case "PRI" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Primarykey.png"] + Case "MUL" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Column_FK.png"] + Default + End Select + tvwFields[iCounter, 0].Alignment = Align.Center + tvwFields[iCounter, 1].Text = modMain.$hResult!COLUMN_NAME + tvwFields[iCounter, 2].Text = modMain.$hResult!COLUMN_DEFAULT + tvwFields[iCounter, 3].Text = modMain.$hResult!IS_NULLABLE + tvwFields[iCounter, 4].Text = modMain.$hResult!COLUMN_TYPE + tvwFields[iCounter, 5].Text = modMain.$hResult!COLUMN_KEY + tvwFields[iCounter, 6].Text = modMain.$hResult!EXTRA + tvwFields[iCounter, 7].Text = modMain.$hResult!CHARACTER_SET_NAME + tvwFields[iCounter, 8].Text = modMain.$hResult!COLLATION_NAME + tvwFields[iCounter, 9].Text = modMain.$hResult!COLUMN_COMMENT + If (iCounter Mod 2) = 0 Then + tvwFields[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 6].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 7].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 8].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 9].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + tvwFields.Columns[0].Width = -1 + tvwFields.Columns[1].Width = -1 + tvwFields.Columns[2].Width = -1 + tvwFields.Columns[3].Width = -1 + tvwFields.Columns[4].Width = -1 + tvwFields.Columns[5].Width = -1 + tvwFields.Columns[6].Width = -1 + tvwFields.Columns[7].Width = -1 + tvwFields.Columns[8].Width = -1 + tvwFields.Columns[9].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure TableInfo(Optional Column As Integer) 'Table's Info + + Dim iCounter As Integer + + tvwTables.Clear() + Select (Column) + Case 0 + $sColumnSort = "TABLE_NAME" + Case 1 + $sColumnSort = "TABLE_ROWS" + Case 2 + $sColumnSort = "ENGINE" + Case 3 + $sColumnSort = "CHARSET" + Case 4 + $sColumnSort = "TABLE_COLLATION" + Case 5 + $sColumnSort = "DATA_LENGTH" + Case 6 + $sColumnSort = "UPDATE_TIME" + Default + $sColumnSort = "TABLE_NAME" + End Select + + modMain.$hResult = modMain.$Connection.Exec("SELECT `a`.`TABLE_NAME` AS `TABLE_NAME`, `a`.`ENGINE` AS ENGINE, `a`.`TABLE_ROWS` AS TABLE_ROWS, `a`.`UPDATE_TIME` AS UPDATE_TIME, `b`.`CHARACTER_SET_NAME` AS CHARSET, `a`.`TABLE_COLLATION` AS TABLE_COLLATION FROM `information_schema`.`TABLES` `a`, `information_schema`.`COLLATIONS` `b` WHERE `a`.`TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' AND `a`.`TABLE_COLLATION` = `b`.`COLLATION_NAME` ORDER BY `" & $sColumnSort & "`") + tvwTables.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwTables[iCounter, 0].Text = modMain.$hResult!TABLE_NAME + If modMain.$hResult!TABLE_ROWS <> Null Then tvwTables[iCounter, 1].Text = Format(modMain.$hResult!TABLE_ROWS, "#,#") + tvwTables[iCounter, 1].Alignment = Align.Right + tvwTables[iCounter, 2].Text = modMain.$hResult!ENGINE + tvwTables[iCounter, 3].Text = modMain.$hResult!CHARSET + tvwTables[iCounter, 4].Text = modMain.$hResult!TABLE_COLLATION + If modMain.$hResult!UPDATE_TIME = Null + tvwTables[iCounter, 6].Picture = Picture["icons/24/Null.png"] + Else + tvwTables[iCounter, 6].Text = modMain.$hResult!UPDATE_TIME + Endif + tvwTables[iCounter, 5].Text = Format((modMain.$Connection.Exec("SELECT `DATA_LENGTH` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' AND `TABLE_NAME` = '" & modMain.$hResult!TABLE_NAME & "'")!DATA_LENGTH / 1024), "#,#.00 KB") + tvwTables[iCounter, 5].Alignment = Align.Right + If (iCounter Mod 2) = 0 Then + tvwTables[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 6].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + tvwTables.Columns[0].Width = -1 + tvwTables.Columns[1].Width = -1 + tvwTables.Columns[2].Width = -1 + tvwTables.Columns[3].Width = -1 + tvwTables.Columns[4].Width = -1 + tvwTables.Columns[5].Width = -1 + tvwTables.Columns[6].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure ViewInfo(Optional Column As Integer) 'View's Info + + Dim iCounter As Integer + + Select (Column) + Case 0 + $sColumnSort = "TABLE_NAME" + Case 1 + $sColumnSort = "IS_UPDATABLE" + Case 2 + $sColumnSort = "DEFINER" + Case 3 + $sColumnSort = "SECURITY_TYPE" + Case 4 + $sColumnSort = "CHECK_OPTION" + Case 5 + $sColumnSort = "CHARACTER_SET_CLIENT" + Case 6 + $sColumnSort = "COLLATION_CONNECTION" + Default + $sColumnSort = "TABLE_NAME" + End Select + modMain.$hResult = modMain.$Connection.Exec("SELECT * FROM `information_schema`.`VIEWS` WHERE `TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + tvwViews.Rows.Count = modMain.$hResult.Count + If modMain.$hResult.Available Then + tvwViews.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwViews[iCounter, 0].Text = modMain.$hResult!TABLE_NAME + tvwViews[iCounter, 1].Text = modMain.$hResult!IS_UPDATABLE + tvwViews[iCounter, 2].Text = modMain.$hResult!DEFINER + tvwViews[iCounter, 3].Text = modMain.$hResult!SECURITY_TYPE + tvwViews[iCounter, 4].Text = modMain.$hResult!CHECK_OPTION + If modMain.$bSchemaOk Then + tvwViews[iCounter, 5].Text = modMain.$hResult!CHARACTER_SET_CLIENT + tvwViews[iCounter, 6].Text = modMain.$hResult!COLLATION_CONNECTION + Else + tvwViews[iCounter, 5].Text = ("Not available") + tvwViews[iCounter, 6].Text = ("Not available") + Endif + If (iCounter Mod 2) = 0 Then + tvwViews[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 6].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwViews.Columns[0].Width = -1 + tvwViews.Columns[1].Width = -1 + tvwViews.Columns[2].Width = -1 + tvwViews.Columns[3].Width = -1 + tvwViews.Columns[4].Width = -1 + tvwViews.Columns[5].Width = -1 + tvwViews.Columns[6].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure IndexInfo(Optional Column As Integer) 'Index's Info + + Dim iCounter As Integer + + If tvwTables.Rows.Count = 0 Or tvwTables.Row = -1 Then Return 'Empty set or not table selected + tabData[2].Text = ("Indexes on:") & " " & tvwTables[tvwTables.Row, 0].Text + tvwIndexes.Clear() + ' Select (Column) + ' Case 0 + ' $sColumnSort = "Key_name" + ' Case 1 + ' $sColumnSort = "Column_name" + ' Case 2 + ' $sColumnSort = "Collation" + ' Case 3 + ' $sColumnSort = "Index_type" + ' Default + ' $sColumnSort = "Key_name" + ' End Select + + modMain.$hResult = modMain.$Connection.Exec("SHOW INDEX FROM `" & modMain.$Connection.Name & "`.`" & tvwTables[tvwTables.Row, 0].Text & "`") + If modMain.$hResult.Available Then + tvwIndexes.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwIndexes[iCounter, 0].Text = modMain.$hResult!Key_name + tvwIndexes[iCounter, 1].Text = modMain.$hResult!Column_name + tvwIndexes[iCounter, 2].Text = modMain.$hResult!Collation + tvwIndexes[iCounter, 3].Text = modMain.$hResult!Index_type + If (iCounter Mod 2) = 0 Then + tvwIndexes[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwIndexes[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwIndexes[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwIndexes[iCounter, 3].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwIndexes.Columns[0].Width = -1 + tvwIndexes.Columns[1].Width = -1 + tvwIndexes.Columns[2].Width = -1 + tvwIndexes.Columns[3].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure RoutinesInfo(Optional Column As Integer) 'Rotines's Info + + Dim iCounter As Integer + + tvwProcedures.Clear() + Select (Column) + Case 0 + $sColumnSort = "ROUTINE_TYPE" + Case 1 + $sColumnSort = "ROUTINE_NAME" + Case 2 + $sColumnSort = "DTD_IDENTIFIER" + Case 3 + $sColumnSort = "CREATED" + Case 4 + $sColumnSort = "LAST_ALTERED" + Case 5 + $sColumnSort = "ROUTINE_TYPE" + Case 6 + $sColumnSort = "DEFINER" + Case 7 + $sColumnSort = "ROUTINE_COMMENT" + Default + $sColumnSort = "ROUTINE_NAME" + End Select + + modMain.$hResult = modMain.$Connection.Exec("SELECT * FROM `information_schema`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + If modMain.$hResult.Available Then + tvwProcedures.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + If modMain.$hResult!ROUTINE_TYPE = "FUNCTION" Then + tvwProcedures[iCounter, 0].Picture = Picture["icons/16/Function.png"] + Else + tvwProcedures[iCounter, 0].Picture = Picture["icons/16/Routine.png"] + Endif + tvwProcedures[iCounter, 0].Alignment = Align.Center + tvwProcedures[iCounter, 1].Text = modMain.$hResult!ROUTINE_NAME + tvwProcedures[iCounter, 2].Text = modMain.$hResult!DTD_IDENTIFIER + tvwProcedures[iCounter, 3].Text = modMain.$hResult!CREATED + tvwProcedures[iCounter, 4].Text = modMain.$hResult!LAST_ALTERED + tvwProcedures[iCounter, 5].Text = modMain.$hResult!ROUTINE_TYPE + tvwProcedures[iCounter, 6].Text = modMain.$hResult!DEFINER + tvwProcedures[iCounter, 7].Text = modMain.$hResult!ROUTINE_COMMENT + If (iCounter Mod 2) = 0 Then + tvwProcedures[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 6].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 7].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwProcedures.Columns[0].Width = -1 + tvwProcedures.Columns[1].Width = -1 + tvwProcedures.Columns[2].Width = -1 + tvwProcedures.Columns[3].Width = -1 + tvwProcedures.Columns[4].Width = -1 + tvwProcedures.Columns[5].Width = -1 + tvwProcedures.Columns[6].Width = -1 + tvwProcedures.Columns[7].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure EventInfo(Optional Column As Integer) 'Event's Info + + Dim iCounter As Integer + + tvwEvents.Clear() + Select (Column) + Case 0 + $sColumnSort = "EVENT_NAME" + Case 1 + $sColumnSort = "DEFINER" + Case 2 + $sColumnSort = "TIME_ZONE" + Case 3 + $sColumnSort = "EVENT_TYPE" + Case 4 + $sColumnSort = "EXECUTE_AT" + Case 5 + $sColumnSort = "INTERVAL_VALUE" + Case 6 + $sColumnSort = "INTERVAL_FIELD" + Case 7 + $sColumnSort = "SQL_MODE" + Case 8 + $sColumnSort = "STARTS" + Case 9 + $sColumnSort = "ENDS" + Case 10 + $sColumnSort = "ON_COMPLETION" + Case 11 + $sColumnSort = "CREATED" + Case 12 + $sColumnSort = "LAST_ALTERED" + Case 13 + $sColumnSort = "LAST_EXECUTED" + Case 14 + $sColumnSort = "ORIGINATOR" + Case 15 + $sColumnSort = "CHARACTER_SET_CLIENT" + Case 16 + $sColumnSort = "COLLATION_CONNECTION" + Case 17 + $sColumnSort = "DATABASE_COLLATION" + Case 18 + $sColumnSort = "EVENT_COMMENT" + Default + $sColumnSort = "EVENT_NAME" + End Select + + modMain.$hResult = modMain.$Connection.Exec("SELECT * FROM `information_schema`.`EVENTS` WHERE `EVENT_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + If modMain.$hResult.Available Then + tvwEvents.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwEvents[iCounter, 0].Text = modMain.$hResult!EVENT_NAME + tvwEvents[iCounter, 1].Text = modMain.$hResult!DEFINER + tvwEvents[iCounter, 2].Text = modMain.$hResult!TIME_ZONE + tvwEvents[iCounter, 3].Text = modMain.$hResult!EVENT_TYPE + tvwEvents[iCounter, 4].Text = modMain.$hResult!EXECUTE_AT + tvwEvents[iCounter, 5].Text = modMain.$hResult!INTERVAL_VALUE + tvwEvents[iCounter, 6].Text = modMain.$hResult!INTERVAL_FIELD + tvwEvents[iCounter, 7].Text = modMain.$hResult!SQL_MODE + tvwEvents[iCounter, 8].Text = modMain.$hResult!STARTS + tvwEvents[iCounter, 9].Text = modMain.$hResult!ENDS + tvwEvents[iCounter, 10].Text = modMain.$hResult!ON_COMPLETION + tvwEvents[iCounter, 11].Text = modMain.$hResult!CREATED + tvwEvents[iCounter, 12].Text = modMain.$hResult!LAST_ALTERED + tvwEvents[iCounter, 13].Text = modMain.$hResult!LAST_EXECUTED + tvwEvents[iCounter, 14].Text = modMain.$hResult!ORIGINATOR + tvwEvents[iCounter, 15].Text = modMain.$hResult!CHARACTER_SET_CLIENT + tvwEvents[iCounter, 16].Text = modMain.$hResult!COLLATION_CONNECTION + tvwEvents[iCounter, 17].Text = modMain.$hResult!DATABASE_COLLATION + tvwEvents[iCounter, 18].Text = modMain.$hResult!EVENT_COMMENT + If (iCounter Mod 2) = 0 Then + tvwEvents[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 6].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 7].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 8].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 9].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 10].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 11].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 12].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 13].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 14].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 15].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 16].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 17].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 18].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwEvents.Columns[0].Width = -1 + tvwEvents.Columns[1].Width = -1 + tvwEvents.Columns[2].Width = -1 + tvwEvents.Columns[3].Width = -1 + tvwEvents.Columns[4].Width = -1 + tvwEvents.Columns[5].Width = -1 + tvwEvents.Columns[6].Width = -1 + tvwEvents.Columns[7].Width = -1 + tvwEvents.Columns[8].Width = -1 + tvwEvents.Columns[9].Width = -1 + tvwEvents.Columns[10].Width = -1 + tvwEvents.Columns[11].Width = -1 + tvwEvents.Columns[12].Width = -1 + tvwEvents.Columns[13].Width = -1 + tvwEvents.Columns[14].Width = -1 + tvwEvents.Columns[15].Width = -1 + tvwEvents.Columns[16].Width = -1 + tvwEvents.Columns[17].Width = -1 + tvwEvents.Columns[18].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure TriggersInfo(Optional Column As Integer) 'Table's Info + + Dim iCounter As Integer + + tvwTriggers.Clear() + Select (Column) + Case 0 + $sColumnSort = "TRIGGER_NAME" + Case 1 + $sColumnSort = "EVENT_MANIPULATION" + Case 2 + $sColumnSort = "EVENT_OBJECT_TABLE" + Case 3 + $sColumnSort = "ACTION_TIMING" + Case 4 + $sColumnSort = "DEFINER" + Default + $sColumnSort = "TRIGGER_NAME" + End Select + + 'Triggers Info + modMain.$hResult = modMain.$Connection.Exec("SELECT `TRIGGER_NAME`, `EVENT_MANIPULATION`, `EVENT_OBJECT_TABLE`, `ACTION_TIMING`, `DEFINER` FROM `information_schema`.`TRIGGERS` WHERE `TRIGGER_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + If modMain.$hResult.Available Then + tvwTriggers.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwTriggers[iCounter, 0].Text = modMain.$hResult!TRIGGER_NAME + tvwTriggers[iCounter, 1].Text = modMain.$hResult!EVENT_MANIPULATION + tvwTriggers[iCounter, 2].Text = modMain.$hResult!EVENT_OBJECT_TABLE + tvwTriggers[iCounter, 3].Text = modMain.$hResult!ACTION_TIMING + tvwTriggers[iCounter, 4].Text = modMain.$hResult!DEFINER + If (iCounter Mod 2) = 0 Then + tvwTriggers[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 4].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwTriggers.Columns[0].Width = -1 + tvwTriggers.Columns[1].Width = -1 + tvwTriggers.Columns[2].Width = -1 + tvwTriggers.Columns[3].Width = -1 + tvwTriggers.Columns[4].Width = -1 + + Catch + modMain.Error() + +End + +Public Sub Form_Open() + + Me.Text = "mysql://" & modMain.$Connection.User & "@" & modMain.$Connection.Host & "/" & modMain.$Connection.Name + cmbDatabases.List = modMain.$Connection.MySQL.Databases + cmbDatabases.Index = cmbDatabases.Find(modMain.$Connection.Name) + SearchInfo() + +End + +'***************************** Procedures for tables ************************** +Public Sub tvwTables_Select() + + If tvwTables.Row = -1 Then Return + textResult.Clear() + textResult.Text = modMain.$Connection.MySQL.Table.Info(tvwTables[tvwTables.Row, 0].Text) + FieldInfo() + IndexInfo() + Select (tvwTables.Column) + Case 0 + tvwTables.Edit() + Case 1, 5 + Return + Case 2 + tvwTables.Edit(modMain.$Connection.MySQL.Engines, True) + Case 3 + tvwTables.Edit(modMain.$Connection.MySQL.Charsets, True) + Case 4 + tvwTables.Edit(modMain.$Connection.MySQL.Collations, True) + Default + tvwTables.Edit() + End Select + Catch + modMain.Error() + +End + +Public Sub tvwTables_Save(Row As Integer, Column As Integer, Value As String) + + 'Alters the table definition, the value is never saved into the TableView, it is used to make the update + If tbtLock.Value Then Return + Select (Column) + Case 0 + modMain.$Connection.MySQL.Table.Rename(tvwTables[Row, 0].Text, Value) + Case 1, 5 + Return + Case 2 + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[Row, 0].Text, "ENGINE",, Value) + Case 3 + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[Row, 0].Text, "CHARACTER SET",, Value & " COLLATE " & Value & "_bin") + Case 4 + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[Row, 0].Text, "CHARACTER SET",, tvwTables[Row, 3].Text & " COLLATE " & Value) + Default + End Select + RefreshData(tvwTables.Row, tabData.Index) + Catch + modMain.Error() + +End + +Public Sub tvwTables_DblClick() + + tabData.Index = 3 'Fields + +End +'***************************** End of Procedures for tables ******************* + +'***************************** Procedures for views *************************** +Public Sub tvwViews_Click() + + If tvwViews.Row = -1 Then Return + textResult.Text = modMain.$Connection.MySQL.View.Info(tvwViews[tvwViews.Row, 0].Text) + ViewInfo() + Catch + modMain.Error() + +End + +Public Sub tvwViews_DblClick() + + modMain.$hFNewView = New FNewView(Null, tvwViews[tvwViews.Row, 0].Text) + modMain.$hFNewView.ShowModal() + Catch + modMain.Error() + +End + +Public Sub tvwViews_Select() + + tvwViews_Click() + +End +'***************************** End of Procedures for views ******************** + +'***************************** Procedures for fields ********************* +Public Sub tvwFields_Click() + + 'If the tbtLock is true then the change is not made + If tbtLock.Value Then Return + Select (tvwFields.Column) + Case 0, 5, 6 + Return + Case 1, 2, 9 + tvwFields.Edit() + Case 3 + tvwFields.Edit(["YES", "NO"], True) + Case 7 + tvwFields.Edit(modMain.$Connection.MySQL.Charsets, True) + Case 8 + tvwFields.Edit(modMain.$Connection.MySQL.Collations, True) + Default + tvwFields.Edit() + End Select + Catch + modMain.Error() + +End + +Public Sub tvwFields_DblClick() + + 'If the tbtLock is true then the change is not made + If tbtLock.Value Then Return + If ValidateTableSelected() Then + modMain.$hFNewField = New FNewField(tvwTables[tvwTables.Row, 0].Text, Null, tvwFields[tvwFields.Row, 1].Text) + modMain.$hFNewField.ShowModal() + Endif + FieldInfo() + Catch + modMain.Error() + +End + +Public Sub tvwFields_Save(Row As Integer, Column As Integer, Value As String) + + Dim sDefinition As String + + Select (Column) + Case 0, 5, 6, 7, 8 + Return + Case 1 'Field name + modMain.$Connection.MySQL.Field.Rename(tvwTables[tvwTables.Row, 0].Text, tvwFields[Row, Column].Text, Value) + Case 2 'Default + If Value = "" Then + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "ALTER", tvwFields[Row, 1].Text, "DROP DEFAULT") + Else + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "ALTER", tvwFields[Row, 1].Text, "SET DEFAULT '" & Value & "'") + Endif + Case 3 'Nullable + sDefinition = tvwFields[Row, 4].Text + If Value = "NO" Then sDefinition &= " NOT NULL" + If tvwFields[Row, 2].Text <> "" Then sDefinition &= " DEFAULT '" & tvwFields[Row, 2].Text & "'" + If tvwFields[Row, 9].Text <> "" Then sDefinition &= " COMMENT '" & tvwFields[Row, 2].Text & "'" + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "MODIFY", tvwFields[Row, 1].Text, sDefinition) + Case 4 'Datatype + sDefinition = Value + If tvwFields[Row, 3].Text = "NO" Then sDefinition &= " NOT NULL" + If tvwFields[Row, 2].Text <> "" Then sDefinition &= " DEFAULT '" & tvwFields[Row, 2].Text & "'" + If tvwFields[Row, 9].Text <> "" Then sDefinition &= " COMMENT '" & tvwFields[Row, 2].Text & "'" + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "MODIFY", tvwFields[Row, 1].Text, sDefinition) + Case 9 'Comment + sDefinition = tvwFields[Row, 4].Text + If tvwFields[Row, 3].Text = "NO" Then sDefinition &= " NOT NULL" + If tvwFields[Row, 2].Text <> "" Then sDefinition &= " DEFAULT '" & tvwFields[Row, 2].Text & "'" + If Value <> "" Then sDefinition &= " COMMENT '" & Value & "'" + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "MODIFY", tvwFields[Row, 1].Text, sDefinition) + Default + End Select + RefreshData(tvwTables.Row, tabData.Index) + Catch + modMain.Error() + +End +'***************************** End of Procedures for fields ******************** + +'***************************** Procedures for procedures *********************** +Public Sub tvwProcedures_Click() + + If tvwProcedures.Row = -1 Then Return + textResult.Text = modMain.$Connection.MySQL.Routines.Info(tvwProcedures[tvwProcedures.Row, 1].Text, modMain.$Connection.Name) + Catch + modMain.Error() + +End + +Public Sub tvwProcedures_DblClick() + + modMain.$hFNewRoutine = New FNewRoutine(Null, tvwProcedures[tvwProcedures.Row, 1].Text) + modMain.$hFNewRoutine.ShowModal() + RoutinesInfo() + Catch + modMain.Error() + +End + +Public Sub tvwProcedures_Select() + + tvwProcedures_Click() + +End +'***************************** End of Procedures for procedures **************** + +'***************************** Procedures for triggers ************************* +Public Sub tvwTriggers_Click() + + If tvwTriggers.Row = -1 Then Return + textResult.Text = modMain.$Connection.MySQL.Trigger.Info(tvwTriggers[tvwTriggers.Row, 0].Text, modMain.$Connection.Name) + Catch + modMain.Error() + +End + +Public Sub tvwTriggers_DblClick() + + modMain.$hFNewTrigger = New FNewTrigger(tvwTables[tvwTables.Row, 0].Text, Null, tvwTriggers[tvwTriggers.Row, 0].Text) + modMain.$hFNewTrigger.ShowModal() + TriggersInfo() + Catch + modMain.Error() + +End + +Public Sub tvwTriggers_Select() + + tvwTriggers_Click() + +End +'***************************** End of Procedures for triggers ****************** + +Public Sub tabData_Click() + + Select (tabData.Index) + Case 0 'Tables + tbtNew.ToolTip = ("New Table") + tbtDelete.ToolTip = ("Delete Table") + Case 1 'Views + tbtNew.ToolTip = ("New View") + tbtDelete.ToolTip = ("Delete View") + Case 2 'Indexes + tbtNew.ToolTip = ("New Index") + tbtDelete.ToolTip = ("Delete Index") + Case 3 'Fileds + tbtNew.ToolTip = ("New Field") + tbtDelete.ToolTip = ("Delete Field") + Case 4 'Routines + tbtNew.ToolTip = ("New Routine") + tbtDelete.ToolTip = ("Delete Routine") + Case 5 'Triggers + tbtNew.ToolTip = ("New Trigger") + tbtDelete.ToolTip = ("Delete Trigger") + Case 6 'Events + tbtNew.ToolTip = ("New Event") + tbtDelete.ToolTip = ("Delete Event") + Default + End Select + Catch + modMain.Error() + +End + +Public Sub Action_Activate(Name As String) As Boolean + + Select (Name) + Case "NewItem" 'tbtNew, F2 + If tbtLock.Value Then Return + NewItem() + RefreshData(tvwTables.Row, tabData.Index) + Case "EditItem" 'tbtNew, Ctrl + E + If tbtLock.Value Then Return + EditItem() + RefreshData(tvwTables.Row, tabData.Index) + Case "DeleteItem" 'tbtDelete, Del + If tbtLock.Value Then Return + DeleteItem() + RefreshData(tvwTables.Row, tabData.Index) + Case "Refresh" 'F5, tbtRefresh + RefreshData(tvwTables.Row, tabData.Index) + Case "NewDatabase" 'tbtNewDatabase + modMain.$hFNewDatabase = New FNewDatabase + modMain.$hFNewDatabase.ShowModal() + RefreshData(tvwTables.Row, tabData.Index) + Form_Open() + Case "DeleteDatabase" 'tbtDeleteDatabase + If Message.Question(Subst(("Do you realy want to delete the database: &1?"), modMain.$Connection.Name), ("&Yes"), ("&No")) = 1 Then + modMain.$Connection.MySQL.DataBase.Delete(modMain.$Connection.Name, False) + modMain.$Connection.MySQL.Use("mysql") + Form_Open() + Endif + RefreshData(tvwTables.Row, tabData.Index) + Case "Lock" + tbtDeleteDatabase.Enabled = Not tbtDeleteDatabase.Enabled + tbtNewDatabase.Enabled = Not tbtNewDatabase.Enabled + tbtNew.Enabled = Not tbtNew.Enabled + tbtDelete.Enabled = Not tbtDelete.Enabled + Default + End Select + Catch + modMain.Error() + +End + +Private Function ValidateTableSelected() As Boolean + + If tvwTables.Row = -1 Then Return False + Return True + +End + +Public Procedure NewItem() + + 'Add the item + Select (tabData.Index) + Case 0 'Tables + modMain.$hFNewTable = New FNewTable + modMain.$hFNewTable.ShowModal() + Case 1 'Views + modMain.$hFNewView = New FNewView(Null) + modMain.$hFNewView.ShowModal() + Case 2 'Indexes + If ValidateTableSelected() Then + modMain.$hFNewIndex = New FNewIndex(tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewIndex.ShowModal() + Endif + Case 3 'Fileds + If ValidateTableSelected() Then + modMain.$hFNewField = New FNewField(tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewField.ShowModal() + Endif + Case 4 'Routines + modMain.$hFNewRoutine = New FNewRoutine + modMain.$hFNewRoutine.ShowModal() + Case 5 'Triggers + If ValidateTableSelected() Then + modMain.$hFNewTrigger = New FNewTrigger(tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewTrigger.ShowModal() + Endif + Case 6 'Events + modMain.$hFNewEvent = New FNewEvent + modMain.$hFNewEvent.ShowModal() + Default + End Select + RefreshData(tvwTables.Row, tabData.Index) + +End + +Public Procedure EditItem() + + Select (tabData.Index) + Case 0 'Tables + modMain.$hFNewTable = New FNewTable(Null, tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewTable.ShowModal() + TableInfo() + Case 1 'Views + tvwViews_DblClick() + Case 2 'Indexes + tvwIndexes_DblClick() + Case 3 'Fileds + tvwFields_DblClick() + Case 4 'Routines + tvwProcedures_DblClick() + Case 5 'Triggers + tvwTriggers_DblClick() + Case 6 'Events + tvwEvents_DblClick() + Default + End Select + Catch + modMain.Error() + +End + +Public Procedure DeleteItem() + + Select (tabData.Index) + Case 0 'Tables + If Message.Question(Subst(("Do you realy want to delete the table: &1?"), tvwTables[tvwTables.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Table.Delete([tvwTables[tvwTables.Row, 0].Text], False) + Case 1 'Views + If Message.Question(Subst(("Do you realy want to delete the view: &1?"), tvwViews[tvwViews.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.View.Delete([tvwViews[tvwViews.Row, 0].Text], False) + Case 2 'Indexes + If Message.Question(Subst(("Do you realy want to delete the index: &1?"), tvwIndexes[tvwIndexes.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Index.Delete(tvwTables[tvwTables.Row, 0].Text, tvwIndexes[tvwIndexes.Row, 0].Text) + Case 3 'Fileds + If Message.Question(Subst(("Do you realy want to delete the field: &1?"), tvwFields[tvwFields.Row, 1].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "DROP", tvwFields[tvwFields.Row, 1].Text) + Case 4 'Routines + If Message.Question(Subst(("Do you realy want to delete the routine: &1?"), tvwProcedures[tvwProcedures.Row, 1].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Routines.Delete(tvwProcedures[tvwProcedures.Row, 1].Text, False) + Case 5 'Triggers + If Message.Question(Subst(("Do you realy want to delete the trigger: &1?"), tvwTriggers[tvwTriggers.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Trigger.Delete(tvwTriggers[tvwTriggers.Row, 0].Text, False) + Case 6 'Events + If Message.Question(Subst(("Do you realy want to delete the event: &1?"), tvwEvents[tvwEvents.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Event.Delete(tvwEvents[tvwEvents.Row, 0].Text, False) + Default + End Select + modMain.$hFBrowser.SetMessage(("Item deleted."), 1) + +End + +Public Procedure RefreshData(Table As Integer, Tab As Integer) + + textResult.Clear() + SearchInfo() + If Table = -1 Then Table = 0 + tvwTables.Row = Table + FieldInfo() + IndexInfo() + tabData.Index = Tab + +End + +Public Procedure SetEditor() + + textResult.SetStyle() + +End + +'***************************** Procedures for events ************************* +Public Sub tvwEvents_Click() + + textResult.Text = modMain.$Connection.MySQL.Event.Info(tvwEvents[tvwEvents.Row, 0].Text, modMain.$Connection.Name) + Catch + modMain.Error() + +End + +Public Sub tvwEvents_DblClick() + + If Not modMain.$bSchemaOk Then Return + modMain.$hFNewEvent = New FNewEvent(Null, tvwEvents[tvwEvents.Row, 0].Text) + modMain.$hFNewEvent.ShowModal() + EventInfo() + Catch + modMain.Error() + +End + +Public Sub tvwEvents_Select() + + tvwEvents_Click() + +End + +'***************************** Procedures for indexes ************************* +Public Sub tvwIndexes_DblClick() + + modMain.$hFNewIndex = New FNewIndex(tvwTables[tvwTables.Row, 0].Text, Null, tvwIndexes[tvwIndexes.Row, 0].Text) + modMain.$hFNewIndex.ShowModal() + IndexInfo() + Catch + modMain.Error() + +End +'***************************** End of Procedures for indexes ************************* + +'**************************** Procedures for sorting info ******************** +Public Sub tvwFields_ColumnClick(Column As Integer) + + FieldInfo(Column) + +End + +Public Sub tvwTables_ColumnClick(Column As Integer) + + TableInfo(Column) + +End + +Public Sub tvwViews_ColumnClick(Column As Integer) + + ViewInfo(Column) + +End + +Public Sub tvwIndexes_ColumnClick(Column As Integer) + + IndexInfo(Column) + +End + +Public Sub tvwProcedures_ColumnClick(Column As Integer) + + RoutinesInfo(Column) + +End + +Public Sub tvwTriggers_ColumnClick(Column As Integer) + + TriggersInfo(Column) + +End + +Public Sub tvwEvents_ColumnClick(Column As Integer) + + EventInfo(Column) + +End +'**************************** End of Procedures for sorting info ************** + +Public Sub tvwTables_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwViews_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwIndexes_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwFields_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwProcedures_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwTriggers_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwEvents_Menu() + + mnuItem.Popup() + +End + +Public Sub cmbDatabases_Click() + + modMain.$Connection.MySQL.Use(cmbDatabases.Text) + Me.Text = "mysql://" & modMain.$Connection.User & "@" & modMain.$Connection.Host & "/" & modMain.$Connection.Name + SearchInfo() + +End diff --git a/app/examples/Database/MySQLExample/.src/FTables.form b/app/examples/Database/MySQLExample/.src/FTables.form new file mode 100644 index 00000000..fb079f0a --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FTables.form @@ -0,0 +1,206 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,99,72) + Tag = "Catalogs" + Text = ("Tables") + Icon = Picture["icons/16/Table.png"] + Arrangement = Arrange.Fill + { mnuItem Menu + Text = ("Item") + Visible = False + { mnuNewItem Menu + Action = "NewItem" + Text = ("New Item") + Picture = Picture["icons/16/New.png"] + Shortcut = "F2" + } + { mnuEditItem Menu + Action = "EditItem" + Text = ("Edit Item") + Picture = Picture["icon:/16/edit"] + Shortcut = "Ctrl+E" + } + { mnuDeleteItem Menu + Action = "DeleteItem" + Text = ("Delete Item") + Picture = Picture["icon:/16/trash"] + Shortcut = "Ctrl+Del" + } + { mnuRefresh Menu + Action = "Refresh" + Text = ("Refresh") + Picture = Picture["icons/16/Refresh.png"] + Shortcut = "F5" + } + } + { vplMain VSplit + MoveScaled(0,0,98,71) + Expand = True + { vbxTables VBox + MoveScaled(1,1,97,37) + { tbrMain ToolBar + MoveScaled(0,1,84,4) + { tbtLock ToolButton + MoveScaled(0,0,4,4) + ToolTip = ("Lock") + Action = "Lock" + Picture = Picture["icons/16/Lock.png"] + Toggle = True + } + { Separator2 Separator + MoveScaled(5,0,1,4) + } + { tbtNew ToolButton + MoveScaled(7,0,4,4) + ToolTip = ("New Table") + Action = "NewItem" + Picture = Picture["icons/16/New.png"] + } + { tbtDelete ToolButton + MoveScaled(11,0,4,4) + ToolTip = ("Delete Table") + Action = "DeleteItem" + Picture = Picture["icon:/16/trash"] + } + { tbtRefresh ToolButton + MoveScaled(15,0,4,4) + ToolTip = ("Refresh") + Action = "Refresh" + Picture = Picture["icons/16/Refresh.png"] + } + { Separator1 Separator + MoveScaled(19,0,1,4) + } + { tbtNewDatabase ToolButton + MoveScaled(21,0,4,4) + ToolTip = ("New Database") + Action = "NewDatabase" + Picture = Picture["icons/16/Database.png"] + } + { tbtDeleteDatabase ToolButton + MoveScaled(27,0,4,4) + ToolTip = ("Delete Database") + Action = "DeleteDatabase" + Picture = Picture["icon:/16/remove"] + } + { Separator3 Separator + MoveScaled(32,0,1,4) + } + { cmbDatabases ComboBox + MoveScaled(35,0,24,4) + ReadOnly = True + Sorted = True + } + } + { tabData TabStrip + MoveScaled(1,8,95,26) + Expand = True + Arrangement = Arrange.Fill + Count = 7 + Index = 0 + Text = ("Tables") + Picture = Picture["icons/16/Table.png"] + { tvwTables TableView + MoveScaled(1,3,41,16) + Mode = Select.Single + Header = GridView.Both + } + Index = 1 + Text = ("Views") + Picture = Picture["icons/16/View.png"] + { tvwViews TableView + MoveScaled(1,2,35,13) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 2 + Text = ("Indexes") + Picture = Picture["icons/16/Index.png"] + { tvwIndexes TableView + MoveScaled(1,3,40,15) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 3 + Text = ("Fields on:") + Picture = Picture["icons/16/Field.png"] + { tvwFields TableView + MoveScaled(2,3,39,16) + Mode = Select.Single + Header = GridView.Both + } + Index = 4 + Text = ("Routines") + Picture = Picture["icons/16/Routine.png"] + { tvwProcedures TableView + MoveScaled(1,3,41,16) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 5 + Text = ("Triggers") + Picture = Picture["icons/16/Trigger.png"] + { tvwTriggers TableView + MoveScaled(1,2,41,16) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 6 + Text = ("Events") + Picture = Picture["icon:/16/clock"] + { tvwEvents TableView + MoveScaled(2,2,41,16) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 0 + } + } + { textResult TextArea + MoveScaled(1,39,96,32) + } + } +} + +# Gambas Action File 3.0 + +{ Actions + { Action DeleteDatabase + Text = "Delete Database" + Picture = "icon:/16/remove" + } + { Action DeleteItem + Text = "Delete Item" + Shortcut = "Ctrl+Del" + Picture = "icon:/16/trash" + } + { Action EditItem + Text = "Edit Item" + Shortcut = "Ctrl+E" + Picture = "icon:/16/edit" + } + { Action Lock + Text = "Lock" + Picture = "icons/16/Lock.png" + } + { Action NewDatabase + Text = "New Database" + Picture = "icons/16/Database.png" + } + { Action NewItem + Text = "New Item" + Shortcut = "F2" + Picture = "icons/16/New.png" + } + { Action Refresh + Text = "Refresh" + Shortcut = "F5" + Picture = "icons/16/Refresh.png" + } +} diff --git a/app/examples/Database/MySQLExample/.src/modMain.module b/app/examples/Database/MySQLExample/.src/modMain.module new file mode 100644 index 00000000..1c969160 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/modMain.module @@ -0,0 +1,69 @@ +' Gambas module file + +Public $Connection As New Connection +Public $hResult As Result +Public $Host As String +Public $Password As String +Public $Port As String +Public $User As String +Public $bSchemaOk As Boolean = True +Public $hFTables As FTables +Public $hFNewTable As FNewTable +Public $hFNewView As FNewView +Public $hFNewDatabase As FNewDatabase +Public $hFNewIndex As FNewIndex +Public $hFNewRoutine As FNewRoutine +Public $hFMessage As FMessage +Public $hFNewTrigger As FNewTrigger +Public $hFNewField As FNewField +Public $hFNewEvent As FNewEvent +Public $hFConnect As FConnect + +Public Procedure Main() + + $hFConnect = New FConnect + If $hFConnect.ShowModal() Or Not $Host Or Not $Password Or Not $Port Or Not $User Then Return + With $Connection + .Type = "mysql" + .Name = "mysql" + .Host = $Host + .Password = $Password + .Port = $Port + .User = $User + .Open() + End With + If Not $Connection.Opened Then Return + If CInt($Connection.MySQL.Version.MinorVersion()) < 1 Then + $bSchemaOk = False + Message.Warning(("Seems that your MySQL version is lower than 5.1.\ntherefore you will get some error and warning messages.")) + Endif + Try $Connection.MySQL.Use(Settings["/General/LastDatabase", "mysql"]) + $hFTables = New FTables + $hFTables.ShowModal() + Catch + Message.Error(Error.Where & "::" & Error.Text) + +End + +Public Procedure Error(Optional Message As String) + + Dec Application.Busy + If Error.Text Like "*MySQL server has gone away" Then + $Connection.Close() + $Connection = Null + $Connection = New Connection + With $Connection + .Type = "mysql" + .Name = "mysql" + .Host = $Host + .Password = $Password + .Port = $Port + .User = $User + .Open() + End With + Endif + If Not Message Then Message = Error.Where & "::" & Error.Text + modMain.$hFMessage = New FMessage(Message) + modMain.$hFMessage.ShowModal() + +End diff --git a/app/examples/Database/MySQLExample/icons/16/Admin.png b/app/examples/Database/MySQLExample/icons/16/Admin.png new file mode 100644 index 0000000000000000000000000000000000000000..44ff99df10f2fb1f32768671d75eca895e88652e GIT binary patch literal 1099 zcmV-R1ho5!P)w{4;0HGr`YL|;LMybw1`OLe3ZZj=>olLreB5j#*3GDHojh)b0s;gJ z4iQapgE(An1qTTN0Rk;MMO%TBZl=F$qr6aeiXJO900000000320SgTe7ab!N8zBe^ z3;+ND0001ox5KN>*tON)v(?|H&DeObz&nSZjJ?c}!_$tz(vri~lf~7P#nqa~*oC~! zE{duhcamhMzG zz|(EC%x$&JZnVv2ugG1g!aSL{2y>Spgr+QwuQ!&ra<$C6+vc~~gv&~zq z$V{TYHZ~)W+cH#Ng@2;p(l_;eWoXORcaJbP>ro%9qy(W&b9fqnwoV#JL%cj!d_WAqr`1;!H z^SR&bp3~)u$K7$c(NU(uGMKy`h^(j5;OX=Cyx!}k*XX3z=$X*sgv8ovw9ZPU#6zIL zE03{EqrrE&(1gO*h{oKL&ESyB--g87bh^`7tjJ5K$xEijIGDIkrNU*h&2_rdd%o3p zywq*C&|k33T(Z$+u*_4d%1@`nR;tHbuF7Ds%xAgOXRpg~vB}p~F}eT%07-OGPE!B? z0000000000Gm_Bo>BxvC0)PAOW&@gndtn~w*AH=6q>Z|vuAGE?Jo@%NsHg-d+}toT z%gj9&@U}WOr=Hcbz7Z7A-VPysK zg@i>sy}W&V{Y1ssfCB8|68-^!LBS!RVc`*y9IOnSQjt;7F|l#+35iL`DblPAGP0>@ z>9HA^S=l+cdHHhk3Ma3nhW#tu>Rn;}MiVRA1^$m?p%`L6XZS5VMUCIn9-95d1 z{SzimnmlFdwCOWc8PsOZnmuRky!i_jE?T@~=`wW&jpZv=u3Eij?Yi|FHg4LyMUz2G zduwmsw(UE1?%KU)?>-$}23EcO2M!)OeB|h{<0np@(l=mWH8eVX=Ipui7cO49e8t#= zm4TJj)a>fD>o;!Rx_!sof)ymdYH4L{bN8;ToxKAqM1a-N$=Su#&D{gU1^}|JV=qq- Rh@b!f002ovPDHLkV1n0V@Bf6>|CG+}V~oa(oW?P&LPD);d(ORUy{(+*|8u?XN}S#RO0|lT#6q<~2y+M! zdk_e75CB?x0At1gW6lg=ylQZLTD4jLQUCx-003hEDU_5DgtZ8S&IonPV|awDxVRyE zApki5LZw1uy<=avU}JoFxz5h#|Np|+*bs9N4PXyofOwP#+`_`r(#o0%EMQ}6 zXK&)*=;Z9;%FF^1aI>La-;UEwZ85JE9 z8yBCz$sm)Ml$4a5lA4yDk&&6j#mxY8v1WF5PHtX)L17UOFCWxLjQj$ELdC)&qGB)s gpoqAHq^KAp0BThwQ%&gz2mk;807*qoM6N<$f+9>2VgLXD literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Column_FK.png b/app/examples/Database/MySQLExample/icons/16/Column_FK.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2999f124ac2d04088eadd26b5394dc115eeb0c GIT binary patch literal 529 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf&j6ng*Z=?j1DQaOlbV_y6Jw;M zl@b?Mk)K}}6Jx5T1{BXoNT|rmEA;R%k(MqKt3=I1Ysj&>9m2Pv?%t#onm zla^*>VgeZi22SGQC7PPvQc^68j4&}MZ73+1p{mM)q!=QhFCgFrRF5H!tbNC#Mdxj=FZOld-nWaz50J==ikW4_aPx`Y;Eu4=KfDe z_!JuYI3Qr3v-5Fx_q%?6TO1swo0%=nWSs>x+NvbT4;UECiib319^TVnlz#Y7UGm;T zy%WdetlmD$l4aqJ`TSx+oHhr$O7WNeTsJ8$jp92!?rfStNy-K?tV!*=ih!o{db&7< zNL)@%aA5DrU{E{IqITfSp)REZ$MTLIJlQJn;MvA)8=pRX+{F2T`^HUc3oA~pLPyq? zxv?>K@7UV7y6jY7N_rG`&u(9x(4?SC0*sQ8mj4?ZDp_4sQ#%^ER?VuKu%~U=G_w%a z>3!?w?Q2}NaEjoWC_UL^;b0E=R|a~r#>L#qDa(r`Uq&pd;^SjrT*3d}drF}QDC9g{ L{an^LB{Ts5Hh!x| literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Database.png b/app/examples/Database/MySQLExample/icons/16/Database.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f2fa4cdd49b69bfef57ea38b3c825fcdd07eda GIT binary patch literal 805 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47&iy_gt-3y{~suFVXgY*^=fBV zDxFv=e`LP&{#g>cr-}CEgS6k@rE_4=?tYK`^DKZ25c`1Z?x~6p_Ol}{ zCufPm*sdpMi2&J84m+QiDSUgo@s2+J$GbK6&y)rV-rQ=uXM!+9<-Oew$EJgH-Q8_} zWSYp8^?Ez{gzjtsI>un{46S9YQb4JmVyTO(RW7bnJhx2##C+)!3uI0%lsz(EA>L0A zVhA$%;zIf7yOaJtTlDwQw0{q${(C&<|$|NCm)-zT&GJz4ng z<;MT-kN^90>F0|ze;-Z#cfaq~lhw~pCqF!q_4Y>J_opkr-JACORN#-rgVZ@UZ8%+gblV9R2_0hFgIf1XYJ{bt|A^`?){)_r__=;xwLi}$y}CZ}=HcoKJ5moU zve`4+@Ypi*Q!9<{?DPHm?%2o-$I?t6Kl3vG`6I{i{f9cw_a7SHe|-A7dMEeWkE&eX zf2e)`@$OUBzki)<1}pEs`1j)BdVd`WVR>ymaW)nQpn-QhT^vIsE+;1>q@<)Ir6r~& zKY#FqX_KUc#Lmi?yLW7D_U@^z`1!+;iD_TmKgM^Bjt3PNHaQ(~V4P@RKf$BIC1A;l z85RK!N?KNG*X$Ma>=XloyreV~CQq3&Y1XuP6K77<)n*r(;D74m+0*(OI$Gz#7`Qd2 z9boaAdVuLv>Qm9FKUqVsnr_t!TIImFb=R-9W6PdRySA;Yl-GAbgV-UST-ga+u`La9 zH+cT?&2U)G7;)gjiq*T-_b=GK{{9V?oVJxSceXCQ%91S|ZC%YCyntc)j>VIwZ0_#f eJZ17?3vmYfr4ApfrT=~hB^FOtKbLh*2~7a3w3D>} literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Datetime.png b/app/examples/Database/MySQLExample/icons/16/Datetime.png new file mode 100644 index 0000000000000000000000000000000000000000..0baf9a18ad1aa2cc5d161065067a22196763c43d GIT binary patch literal 1140 zcmV-)1dIELP)zrFwf z0313xCQVHS4GqP_!q?>N%hlP*&CtTo+L@S^xw^U3=I`$L|NZsr^YrPk-sO&^vxS9* z$=KoW`~Um@|Lp(&z2V|ze21*m?2DPBgMx(rnMMD5DgSB~|7H;XXcwrOlhNAX`R?QC z|Npnu)JsoKX@{Q3 zz=yWb(Bkm4r>%>Om*VE-v)1Lj;J0b{KM=2 z;`98Any$Xz@5tiu(%$Fd+}h#F!SUP9x}TVrueZGB`MJW&eSmZ|S z=JMC!=JxUM(>gVO=&cpQY z>*(9l+uGN0eSNvk&gcLC!r0if)%4cj=lb^Z?&96>@bKp1;N#-p&BnZJgoLBZ%hCS+ z)BgU@{{F1j*VyCi<>cex(8cKH?R4zyWzNpv=gw$7+}z>C#?Gm! zvRbybYS-6WxVXW@&bYY1m6f1KlaoFGP(%O#07`UHPE!B?00000004UV`T6;X0dac` zC^vP9GGBVE(Bu91`M~`^{c`>JPvk7|!PDDD*Y?=>-A@sY)|*=<8ea0+{a&vTbc6WGrdV73*egJ=ffr5jCg@%WSiHeJijUNCYj*pO$l9QB`mY0~B znwud2BAlI`pP-?lqok##r>G+a03@lZtE{cAuduPQv$Q291^~6Tx45~wyS%-=zrex5 zCnx|Z!^FkL$H>Xb%goKr&(JCWE78)^)YaD4*xB0J+uSTI0509$-{9fmFEBCVSAIB`3o8JOr5%W@7_nRUr$)OcH52}Kfiwm`gF_p?}78@Z{D`;``52m|Nrm*`#0tK zb*Hwr-r2KnzkmPv|Nm3}|4;b#E$!B=^lR5*_wF@JNon7|fA;U+wVyvHUAdBS_H6R; z?A>cyTWfpF^ghrL9wkA3zyL-Ard2AM#}erUe1!GU=OEK_TEU2mQY6Nq?pqC}sYEAip+Qgg=G2mg*1a>lPJH|60oV+#2D z`YzC<<(@8%ArhC96BL*Xj0`t(aB*(lw6VCriFJpIi|vskJ0CLH2u+$7cQ5GDo&ZKW z$w>Kl`~M7;M1%>*wwy2}x}2w+ES3 z666OoCaq(p(%pB0F1tfl8J{UXwdX(+P%hEa#W6(U^3=ZDd<_O1&f(>Wvz>qckJrB4 zW2koQ1oy*$jJdycbe6a;==W6ONfBJIfk``>{S3B)nzX w*L>#NvIC!wzr57u9e-9P^_j$jd0*;fJ6c$LcPa(H1X{-6>FVdQ&MBb@0MLYGXaE2J literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Index.png b/app/examples/Database/MySQLExample/icons/16/Index.png new file mode 100644 index 0000000000000000000000000000000000000000..391fd0041c3e5d65b852d9b9db2fe6896fdca447 GIT binary patch literal 1122 zcmV-o1fBbdP)_9tq*ECvaF40s;X5007{wE5e>Kj&d3o5(WSO z0Ih`?n0q90S{7kU5+xoA4h#kW000000JVq+(5pI(a2|DC7g0eJDiPMBrl$S*v;Y6P|NpW7 z|H{9@#s2^Q=>Pwi`u#(dng9Q*|NoT#{+Iv%oaE-Ih>wW0zQyo!;S)!qJby(2U8# ziQLnk^}3;v}ga7J<|LlkV?uY;IiU073|L%eD%18L&r2puU|KooD z=7Rs_d;j5c^wwtX$57U^M*ixl{@HH%&sFBYOU|i1yOblhlr26yxK9880Ah4fPE!B? z0000000000001+E2?h%b2ru211uE9_^WIbrK;D%HpYhA|lLPdpC+~po`+M)uJn#L1 z7VpzH2=DiM^Ug8{=J?uQ7lZP>u5v^h1X2Fx(57fqE*=m923mquDIf*|+Tq&g0003M zNklt)JUABD1%2lh^tX;R> z#MF$ze8a{~b(=SD*}84}j-3{kRt(m=cJJA{Z{NQC2M!*xu{GmhuseL@=&|D`PM$h_ z=B&MgBL{=ix$_q;Ub=kc>NRHPx# literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Lock.png b/app/examples/Database/MySQLExample/icons/16/Lock.png new file mode 100644 index 0000000000000000000000000000000000000000..a39c1a9afa9c5d31e1d7892a1ffccef8416dc2a0 GIT binary patch literal 1195 zcmV;c1XTNpP)f0008bP)t-s00018 zlPr0dq3#GSLnp0mcBvBj0Ez>uZ9ewD6pikg0y zwwbTQtG>;+%G0K|%ay0WhMKc&gPnbrww$rWx60F*tHghjtYvwUdXu!1sllY969@OXZ$*8@_tHR8YtGtArw9nV#tGmjEp{a+Ws;aum zk)^zuufm(J$cme`uEWflv%-a-vW}&=+~MfG$kwB@#HF>x;pOX^t-+D0yq2uQlBL0@ zz{-!RxqX?dsJO`V_4e%V^3vJj?C$aP_4c*G(TbzCkf^_nqQ0KC!ib}@d6lN7wZ-%F z_3G{Lth>kS?eO&U^|8OsgrBjBq`HTlxsIl^g`ci>lA@!t!t(O-^7He!#L%_D&hYW_ zuDr{Co2`SMwSJYcft#v!k)WZlzx4F~9Jac;@Vi85LvF{{Gu<9s&XrA1FP0wAkCZe?lrD9~C(Rh;je`0W3*G zK~xyi0U-h2{r~^~009940|W&I2LS;900000009UI3JVMk4i69!0RR91000sb6cu4& zV`LT=7#RQn000^r9A#!_XlZI|Y#kmS001B%B5iJOaB*^Tbai$kBqabQCwF*xdV73* zet&?0f+#5fDuaZDhKGoWii>lMjgF5i04$J^l9QB`mX~RmnVOrNEdVZ^o}ZwhqNAgv zrKYE-sV@L9s;jK6uCK7Lv9hzYwYD(;GPk(7y1Tr+zQ4S{!NS8c05mnk#m2wK$jQ9Q z%goI-Hvl*}&d<=%($mz{*4NnCIy(S7JwDpo+}+;a;NjxqKR`hM0YXDWTV?=P#TeawY{tz*&@4h`4B3xO|0n{1s-P1LEQ<+>;2@kF=Pxt?S{gM zX*=H9Fkyk%gck;kT5z>*Mlu)BcroL|I~ybxyjsAR5h4Sg>M&wLXYcg5+E2P6wPMnY zQ3Iao@kED58VC)zbYI3{b3{t-lmoBL_}_#PJ%n03)L>8rfgT+_Q%~fc2`gS2@LZ4o zbQsoPNDZC}{YnhXfz>S?lD-#P@tpps$73A?8t^sXs_|eJ1A4IT$=K~|_BG*O7kd7_ zc)zLnPG@JQf|T(Gxq>0y5T8quWIdYl={BrvI9ysrq3J(!^Y`8v z7jMb>x2ICxXQM{<=pH4w$}WZyzh~>?uD!Fa-Z&KmFtSH@1d-`)8hYqrs`3N_*RYj zTax`?nEzv(|8$=4m!7iE-v5ND>VB&EhOzmKv-ym$?~t?bpuY5(q_xZ3|ES6Kp}+2= z!SJBF>8#B5xX9ML$>W!!x5e21uFLqa%k8Pf@wU+Msk_(etANy~m5!OMzt;bux#hCU z_LQvMldtBPuh`0sa=N6cjhnK-)&G{V=6saUpuF*utJt5t@2JG*qrmR0xVX8`|Ea_D zx6tpJwd1wS^^>jKnzQGOrP*wGe7Vlf=l}o0*x0qq|CX@fm$2)Os^ou{(~+*~fSAT( zeuSgT%hCS+)BgU@{{F1j*Rsq1nz!wWq}86g@u|V*n6Teff{JIbu$uk-q5l4({{El- z{+a##Zoa;*#{Z$Z?6uGLjiua{ujYuN-(-NSI&XAh$;ou>?R4zyWzNp9$^Vh8>3)^m zj;!!@j?jCN&1iY1QDS#mwzg{5*IT%_k*MK>n$>oPwsnA^dx4dOhmJ^-lPI0cB>(^b zLUdA2Qvd)5H%3rZaFM8~Vy*i8`S|ez^d`Y9?f&E6D(v7T<=`yo!QtCR&_W?^Mx=iubxW`F=5UMFW4 zS2uSLPpE*Gw~w!%e?VYRFjRmqBs45MA~GsE1}eZG8yBCDn3SB73KbAYOV7y6%FfBn zg9-@d7Zes1mz0*3GYT;R0fTTwWmR=eZC!nXh^QDyK)kW3xuvzOy+cB>Q%V{rAk)>| z)7#fSVdA98Q>MzwF*3+cn?7Uatl4ws&YQnr;UWb^2BpPImM&YqV&$sUYu2t?zd@Nn zW#gvJTefc7zGLUEUAyV|I)z#BCIA>^N rY{Fn_W^Q3=Wo=_?XYb%>Y{Cct>$+5S?~kx!00000NkvXXu0mjf`SVAR literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Primarykey.png b/app/examples/Database/MySQLExample/icons/16/Primarykey.png new file mode 100644 index 0000000000000000000000000000000000000000..e09d139ed0f17256939edef506853bb342c5410f GIT binary patch literal 585 zcmV-P0=E5$P)GV;?QwU!0sy$1H-N%Pe(^4mT0&mePO1M=1{(YPbm!9dcl z5&!@I_u^0Aw-*2a0QBBO(ySB!0088~7W?a0kar0H000000OH3a`sPjg(dPI6at8S0OX{Oef#;!pk5 zDE;zq{q9)(>1h4hF#Xad{qbr2>{9*bLjB-1{n#u0&?EfmO#I+G{Mau1(kA@QA^qh` z{M|PG^N07}JpJ%={qAA?>rwsZL;d48{oXH>xYH^C002RBQchC<000Mb!^wdT0=W0| z&k<0{wu1wb#0`bM5it3D2?25W{j4Mc(Yh(m>^TL`@EXtSG0*Nr&%PMX`$Jmwh8iaq zT3hA40001#NklQT8CjTsoV-GKgrbtN9NbhD)yOC{bzy|P3>wjz zTG~i_2AvpPJw{{!{a6D-WIlsYoUw@tvVdv4nK=`(fJK6(l^n8wwT-PEihzo}1Jnor Xs^=3K{;}=3xW13 zmjw9%?U(Ji{43?(EE5UynSaBbKhJja+4fUh@cSVy_RSBAtPInye|7r(0jRvz)5S4F z;&N&KOQ9wMo|ftfP3#>_>KZ5Z{kNX9q|D9jrv1sf$xEV>|DF8Vqw!hqr$a!XK|ueW z8#(2_WjL5*rcULP%bLK@|2X~Yk?a_qSuei!F=SubP0l+XkK*n639 literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Routine.png b/app/examples/Database/MySQLExample/icons/16/Routine.png new file mode 100644 index 0000000000000000000000000000000000000000..7a83453bbaf40f2db075fcfd4470ae438f6b9541 GIT binary patch literal 827 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 B`jG$t literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Trigger.png b/app/examples/Database/MySQLExample/icons/16/Trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef24449c87af539ad5b2c1b38747f8b327e8da7 GIT binary patch literal 930 zcmV;T16}-yP)aSRvYASqRTZLqr zVMA#|I6~K%Yn6&$8LA-~y&DM93Ujz~MRYJEsv#hvA-j|9}A3aDca>$gkw z!%y$LL)WoYwUkD>hZoJ77tNUtq+kH{#8B$DJ?pkJ>$EK4t|{889oM4~zKboKZWrRN zGv~H3sACARatP3v34?2dJU=_wrY8}#6b#S~cffd%fR95;K>(2h0Imuf%ODEQ3wgVH zk9?0pM?nOR1puuFCf6h&&>#)X4_doihqzVt103psI8P6LQ z$r>-JGZ&N^0G9v&rU(F;1r2oo9?~Bh&K?cQ5d_N$0L%se!3qGh3jm}B8O<6Epa2V# z01C~j1a{8+#y>dW`ut-LjilB2`ttpHdh9+Q+|TF`EeW>akA=~vw6-v2Jxcb^ zyzXnB=^B-~&bI&n0OLtSK~xyiV_<*+MkZz!RyKARmxGhbKY*Kumk-M47YGar4hant z6oT-DMMT3RBBP>X#Ka}Q0+O+D@d=4Z$tkI6QeXk;^o-1`?3~=Z`~n#eUsg_Dp|Gg9 zq_nJDQ3)iVtfHz`QCU@8Q(LFbz@VY2rCr~kqpPRiXkch$%)nsM)ZEhA*4|-iW^Tb? zX~n=`-PzUM)7#f?V`~R-!-R>GCQq3`AhW>=`c+}u4pw|IH``1(Qk01}Wtqwq6OJOBUy07*qoM6N<$ Eg0Fg!u>b%7 literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/View.png b/app/examples/Database/MySQLExample/icons/16/View.png new file mode 100644 index 0000000000000000000000000000000000000000..255a8e23be181824fda3537107fb9df1e694b637 GIT binary patch literal 697 zcmV;q0!ICbP)T)mm)jm%;hw>;2^F{o?8U;pzR}=ltE}{MzOG*W>)v;r!Cz z`_JC{%-#ErxAL9I`R(!jJ4M&&^Zw@X{o?NZ-|YR{>iv+r?wrT>_WJ*!#`@gl{LbC` zm$~od@crKG{o3mN*693>xa*q5_VoGxr_J~J{QvX#{_6Ao;qLv{==_$t=-ur7+3Ee( z=ls&;{EfBenZxt(_x_~E^3&q`&EouuvE-M)@b2~fpTX>wxbMl}{D`gKmA&oi^Zc5* z<;vjvmAU7|-us5C+>^TM=kWZ5rr3g`)q$YWfS=Kkvf;to`-G<1k+$aO^8Uo#`;4pF zz1jPZv*YCO{oU*R(d7HP*!zg3)wB9eVNOAm&y3p@2vm;00wkYPE#B|PZm5oAm{B=0002oNklB?5Kc|frFEii<^g+k6%DgNLWOai9t*pqEWq~w&;G@!QhjLfX;943a`y!?W~A}7z{lGxJhvT`Pdipr{L|A3nG+PeDkhRjAL f24)r}WWWjly{9gAMsoG@00000NkvXXu0mjfQWmu$ literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Warning.png b/app/examples/Database/MySQLExample/icons/16/Warning.png new file mode 100644 index 0000000000000000000000000000000000000000..4997cb9222cb0ed478b3c36eae0b36fc0ff1186b GIT binary patch literal 543 zcmV+)0^t3LP)A{= zedXol@$vD9h=_J}cK!YRxw*N5f`Y=r!j+Ykh=_=ZiHTBDQn|Ug^78VFi;GZDP}SAd zprD{QI5^(k-pXJ=3G605<>O=l}2T@3642hlhv9$H!`FYQDa{VPRpBk&%0Q zd)wpx;Nal;`uc-|gWKEN^YinFh=`Y$m(kJDot>Sks;X~qZ(UtoUS3{pZEa>qnH2y4 z04{V=PE!B?06~bAm6erYYTEqmrekgD?O|c+rUI4irj?bI?WSSsrfur&V{Pi~remb0 z4|6Y{0001SQ3;>{)AYA$?_0a$T002ovPDHLkV1nCU*Nd$B zVb9#-lCGGcGWox7z7aFCa^R_ld1)$6hTK}>NzBT3GJ8BsLJXCetvWp?%aky@*XN$b T_|VJ>XcmK~tDnm{r-UW|sJS1i literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/.directory b/app/examples/Database/PictureDatabase/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Database/PictureDatabase/.icon.png b/app/examples/Database/PictureDatabase/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..983d37059f21a3a981c41ead71ba92c498b2a8c2 GIT binary patch literal 4228 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 akN*eZUZ?bJ#UmI10000, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: PictureDatabase\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:43+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Visualitzador de la base de dades d'imatges" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Miniatura" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Descripció" + +#: FormPictureDatabase.class:101 +msgid "Add image to database" +msgstr "Afegeix una imatge a la base de dades" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Segur que voleu suprimir la imatge" + +#: FormPictureDatabase.class:114 +msgid "" +" from the database:\n" +"\n" +msgstr "" +"de la base de dades:\n" +"\n" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Canceŀla" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Suprimeix" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Desa la imatge de la base de dades" + +#: FormPictureDatabase.class:131 +msgid "" +"Error saving image:\n" +"\n" +msgstr "" +"Error en desar la imatge:\n" +"\n" + +#: FormPictureDatabase.class:252 +msgid "Picture Database" +msgstr "Base de dades d'imatges" + +#: FormPictureDatabase.class:276 +msgid "Remove Image from database" +msgstr "Suprimeix la imatge de la base de dades" + +#: FormPictureDatabase.class:286 +msgid "Export image from database" +msgstr "Exporta la imatge de la base de dades" + +#: FormPictureDatabase.class:296 +msgid "Update image description" +msgstr "Actualitza la descripció de la imatge" + diff --git a/app/examples/Database/PictureDatabase/.lang/cs.po b/app/examples/Database/PictureDatabase/.lang/cs.po new file mode 100644 index 00000000..944ccb3e --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/cs.po @@ -0,0 +1,75 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Prohlížeč obrázkové databáze" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Náhled" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Popis" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Jste si jisti, že chcete smazat obrázek" + +#: FormPictureDatabase.class:114 +msgid "" +" from the database:\n" +"\n" +msgstr "" +" z databáze:\n" +"\n" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Zrušit" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Smazat" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Uložit obázek z databáze" + +#: FormPictureDatabase.class:131 +msgid "" +"Error saving image:\n" +"\n" +msgstr "" +"Chyba při ukládání obrázku:\n" +"\n" + +#: FormPictureDatabase.form:21 +msgid "Picture Database" +msgstr "Obrázková databáze" + +#: FormPictureDatabase.form:39 +msgid "Add image to database" +msgstr "Přidej obtázek do databáze" + +#: FormPictureDatabase.form:45 +msgid "Remove Image from database" +msgstr "Odstranit obrázek z databáze" + +#: FormPictureDatabase.form:55 +msgid "Export image from database" +msgstr "Export obrázku z databáze" + +#: FormPictureDatabase.form:65 +msgid "Update image description" +msgstr "Aktualizace popisu obrázku" diff --git a/app/examples/Database/PictureDatabase/.lang/de.po b/app/examples/Database/PictureDatabase/.lang/de.po new file mode 100644 index 00000000..5a0fb81b --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/de.po @@ -0,0 +1,76 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Bilddatenbank-Betrachter" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Vorschau" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Beschreibung" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Soll dieses Bild wirklich gelöscht werden " + +#: FormPictureDatabase.class:114 +msgid "" +" from the database:\n" +"\n" +msgstr "" +" aus der Datenbank:\n" +"\n" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Abbrechen" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Löschen" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Bild aus der Datenbank spechern" + +#: FormPictureDatabase.class:131 +msgid "" +"Error saving image:\n" +"\n" +msgstr "" +"Fehler beim Speichern des Bildes:\n" +"\n" + +#: FormPictureDatabase.form:21 +msgid "Picture Database" +msgstr "Bilddatenbank" + +#: FormPictureDatabase.form:39 +msgid "Add image to database" +msgstr "Bild zur Datenbank hinzufügen" + +#: FormPictureDatabase.form:45 +msgid "Remove Image from database" +msgstr "Bild aus der Datenbank entfernen" + +#: FormPictureDatabase.form:55 +msgid "Export image from database" +msgstr "Bild aus der Datenbank exportieren" + +#: FormPictureDatabase.form:65 +msgid "Update image description" +msgstr "Bildbeschreibung aktualisieren" diff --git a/app/examples/Database/PictureDatabase/.lang/es.po b/app/examples/Database/PictureDatabase/.lang/es.po new file mode 100644 index 00000000..65910ccc --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/es.po @@ -0,0 +1,36 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FormPictureDatabase.class:270 +msgid "Add image to database" +msgstr "Agregar imagen a la base de datos" + +#: FormPictureDatabase.class:288 +msgid "Export image from database" +msgstr "Exportar imagen desde la base de datos" + +#: FormPictureDatabase.class:252 +msgid "Picture Database" +msgstr "Base de datos de imágenes" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Visor de la base de datos de imágenes" + +#: FormPictureDatabase.class:277 +msgid "Remove Image from database" +msgstr "Remover imagen de la base de datos" + +#: FormPictureDatabase.class:299 +msgid "Update image description" +msgstr "Actualizar descripción de la imagen" + diff --git a/app/examples/Database/PictureDatabase/.lang/nl.po b/app/examples/Database/PictureDatabase/.lang/nl.po new file mode 100644 index 00000000..bf304c93 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/nl.po @@ -0,0 +1,67 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PictureDatabase 3.5.90\n" +"PO-Revision-Date: 2014-09-25 21:01 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "AfbeeldingsDatabase Viewer" + +#: FormPictureDatabase.form:21 +msgid "Picture Database" +msgstr "AfbeeldingsDatabase" + +#: FormPictureDatabase.form:39 +msgid "Add image to database" +msgstr "Afbeelding aan database toevoegen" + +#: FormPictureDatabase.form:45 +msgid "Remove Image from database" +msgstr "Verwijder Afbeelding uit Database" + +#: FormPictureDatabase.form:55 +msgid "Export image from database" +msgstr "Exporteer afbeelding uit database" + +#: FormPictureDatabase.form:65 +msgid "Update image description" +msgstr "Afbeeldingomschrijving updaten" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Pictogrammen" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Omschrijving" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Weet je zeker dat je de afbeelding wilt verwijderen?" + +#: FormPictureDatabase.class:114 +msgid " from the database:\n\n" +msgstr " van de database:\n\n" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Verwijder" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Annuleer" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Afbeelding uit database opslaan" + +#: FormPictureDatabase.class:131 +msgid "Error saving image:\n\n" +msgstr "Fout bij opslaan van afbeelding:\n\n" + diff --git a/app/examples/Database/PictureDatabase/.lang/ru.po b/app/examples/Database/PictureDatabase/.lang/ru.po new file mode 100644 index 00000000..703b9564 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/ru.po @@ -0,0 +1,194 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-30 08:04+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Database/PictureDatabase/.project:25 +msgid "Picture Database Viewer" +msgstr "Просмотрщик базы данных изображений" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:1 +msgid "sqlite3" +msgstr "sqlite3" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:2 +msgid "mysql" +msgstr "mysql" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:3 +msgid "postgresql" +msgstr "postgresql" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:4 +msgid "Could not open database connection" +msgstr "Не удалось открыть соединение с базой данных" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:5 +msgid "Database not found. Creating new database" +msgstr "База данных не найдена. Создание новой базы данных" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:6 +msgid "Could not open database" +msgstr "Не удалось открыть базу данных" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:7 app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:9 app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:12 app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:18 +msgid "pictures" +msgstr "изображения" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:8 +msgid "Database tables not found. Creating new pictures table" +msgstr "Таблицы базы данных не найдены. Создание новой таблицы изображений" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:10 +msgid "Database connection error:" +msgstr "Ошибка подключения к базе данных:" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:11 +msgid "Error:" +msgstr "Ошибка:" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:13 +msgid "Image" +msgstr "Изображение" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:14 +msgid "added:" +msgstr "добавлено:" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:15 +msgid "Done in" +msgstr "Завершено за" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:16 +msgid "s" +msgstr "с" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:17 +msgid "Add database record error


Error:
" +msgstr "Ошибка добавления записи базы данных
Ошибка:
" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:19 +msgid "Select database records error
Error:
" +msgstr "Ошибка выбора записей базы данных
Ошибка:
" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:20 +msgid "Update database record error
Error:
" +msgstr "Ошибка обновления записи базы данных
Ошибка:
" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:21 +msgid "Delete database record error
Error:
" +msgstr "Ошибка удаления записи базы данных
Ошибка:
" + +#: app/examples/Database/PictureDatabase/.src/ModuleDatabase.module:22 +msgid "Error closing database" +msgstr "Ошибка закрытия базы данных" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:101 app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form:19 +msgid "Add image to database" +msgstr "Добавить изображение в базу данных" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Вы уверены, что хотите удалить изображение " + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:114 +msgid "" +" from the database:\n" +"\n" +msgstr "" +" из базы данных:\n" +"\n" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Удалить" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Отмена" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Сохранить изображение из базы данных" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:131 +msgid "" +"Error saving image:\n" +"\n" +msgstr "" +"Ошибка сохранения изображения:\n" +"\n" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:208 +msgid "All Graphics" +msgstr "Все графические" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:211 +msgid "Portable Network Graphics" +msgstr "Портативная сетевая графика" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:213 +msgid "Joint Photographic Experts Group" +msgstr "Объединённая группа фото-экспертов" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:215 +msgid "Windows Bitmap" +msgstr "Растр Windows" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:217 +msgid "Graphics Interchange Format" +msgstr "Графический формат обмена" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:219 +msgid "X PixMap" +msgstr "X PixMap" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class:222 +msgid "All Files" +msgstr "Все файлы" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form:5 +msgid "Picture Database" +msgstr "База данных изображений" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form:24 +msgid "Remove Image from database" +msgstr "Удалить изображение из базы данных" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form:32 +msgid "Export image from database" +msgstr "Экспортировать изображение из базы данных" + +#: app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form:40 +msgid "Update image description" +msgstr "Обновить описание изображения" + +#: app/examples/Database/PictureDatabase/.src/workaround:1 +msgid "Thumb" +msgstr "Эскиз" + +#: app/examples/Database/PictureDatabase/.src/workaround:2 +msgid "Description" +msgstr "Описание" + diff --git a/app/examples/Database/PictureDatabase/.project b/app/examples/Database/PictureDatabase/.project new file mode 100644 index 00000000..1f962604 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Picture Database Viewer +Startup=FormPictureDatabase +Icon=Images/image-x-generic.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Authors="Timothy Marshal-Nichols\ntimothy.marshal-nichols@ntlworld.com" +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Packager=1 +Systems=slackware +Menus=slackware:"Electronics" +Categories=slackware:"Database;Documentation" +Groups=slackware:"Development/Tools" diff --git a/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class new file mode 100644 index 00000000..7e3b884d --- /dev/null +++ b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class @@ -0,0 +1,227 @@ +' Gambas class file + +''' +' Name: FormPictureDatabase +' Author: Timothy Marshal-Nichols +' eMail: timothy.marshal-nichols@ntlworld.com +' Version: 1.0 +' Version Date: April 2007 +' Version History: +' +''' +' Licence Information +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 2 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +' +' http://www.gnu.org/licenses/gpl.html +' +''' +' Description: +' +' Provides the interface to the pictures database. +' +''' +' Developed using Gambas2 Version +' +' Version: 1.9.48 +' +' Gambas Components Used: +' +' gb - Gambas internal native classes +' gb.qt - Graphical QT toolkit component +' +' Look in the Project menu then Properties... and select +' the Components tab. Check that the listed components +' are in the project. +' +''' +' External Dependencies: +' +' None. +' +''' +' Class Usage: +' +' Set as startup class +' +''' + +Public Sub Form_Open() + + 'DB.Debug = True + + Me.Center() + Dialog.Path = User.Home + GridViewImages.Columns.Count = 2 + GridViewImages.Columns[0].Text = ("Thumb") + GridViewImages.Columns[0].Width = 1.5 * ModuleDatabase.ThumbSize + GridViewImages.Columns[1].Text = ("Description") + GridViewImages.Columns[1].Width = 300 + ' Open database + ' + ' Use somthing like this for a SQLite3 database + ModuleDatabase.OpenDatabase("sqlite3", User.Home, Application.Name & ".db", "", "") + ' + ' Use somthing like this for a MySQL database + 'ModuleDatabase.OpenDatabase("mysql", "", Application.Name, "root", "") + ' + ' Use somthing like this for a PostgreSQL database + 'ModuleDatabase.OpenDatabase("postgresql", "localhost", Application.Name, "timothy", "password") + ' + ' Display the database content + ModuleDatabase.Select() + DisplayImages() +Catch + PanelButtons.Enabled = False + Message.Warning(ERROR.Text) +End + +Public Sub Form_Close() + ModuleDatabase.CloseDatabase() +End + +''' +''' Buttons +''' + +Public Sub ToolButtonAdd_Click() + Dialog.Filter = FileFilter(True) + Dialog.Title = ("Add image to database") + If Dialog.OpenFile() Then Return + ModuleDatabase.Add(Dialog.Path) + ModuleDatabase.Select() + DisplayImages() +Catch + Message.Warning(ERROR.Text) +End + +Public Sub ToolButtonRemove_Click() + Dim m As String + If GridViewImages.Row >= 0 Then + m = ("Are you sure you want to delete image ") & (GridViewImages.Row + 1) + m &= (" from the database:\n\n") & TextAreaDescription.Text + If Message.Question(m, ("Delete"), ("Cancel")) = 1 Then + ModuleDatabase.Delete(GridViewImages.Row) + ModuleDatabase.Select() + DisplayImages() + End If + End If +Catch + Message.Warning(ERROR.Text) +End + +Public Sub ToolButtonExport_Click() + Dialog.Filter = FileFilter(False) + Dialog.Title = ("Save image from database") + If Dialog.SaveFile() Then Return + PictureBoxImage.Picture.Save(Dialog.Path) +Catch + Message.Warning(("Error saving image:\n\n") & ERROR.Text) +End + +Public Sub ToolButtonUpdate_Click() + ModuleDatabase.Update(GridViewImages.Row, TextAreaDescription.Text) + GridViewImages[GridViewImages.Row, 1].Text = TextAreaDescription.Text +Catch + Message.Warning(ERROR.Text) +End + +''' +''' GridView events +''' + +Public Sub GridViewImages_Click() + If GridViewImages.Row >= 0 Then + ShowImage(GridViewImages.Row) + End If +End + +''' +''' Functions +''' + +Private Sub DisplayImages() + Dim i As Integer + Dim tempFile As String + Dim tempPicture As String + GridViewImages.Clear() + GridViewImages.Rows.Count = ModuleDatabase.ResultPictures.Count + If ModuleDatabase.ResultPictures.Count > 0 Then + tempFile = Temp() & ".png" + For Each ModuleDatabase.ResultPictures + i = ModuleDatabase.ResultPictures.Index + tempPicture = ModuleDatabase.ResultPictures["thumb"].Data + If tempPicture Then + File.Save(tempFile, tempPicture) + GridViewImages[i, 0].Picture = Picture.Load(tempFile) + End If + GridViewImages[i, 1].Text = ModuleDatabase.ResultPictures["description"] + Next + GridViewImages.Row = 0 + ShowImage(0) + Else + TextAreaDescription.Text = "" + PictureBoxImage.Picture = Null + PictureBoxImage.Resize(1, 1) + End If + If Exist(tempFile) Then Kill tempFile + GridViewImages.Rows.Height = ModuleDatabase.ThumbSize + ToolButtonRemove.Enabled = (GridViewImages.Rows.Count > 0) + ToolButtonExport.Enabled = ToolButtonRemove.Enabled + ToolButtonUpdate.Enabled = ToolButtonRemove.Enabled +End + +Private Sub ShowImage(Row As Integer) + Dim tempFile As String + Dim tempPicture As String + tempFile = Temp() & ".png" + ModuleDatabase.ResultPictures.MoveTo(Row) + TextAreaDescription.Text = ModuleDatabase.ResultPictures["description"] + If tempFile Then + 'tempPicture = ModuleDatabase.ResultPictures["image"].Data + File.Save(tempFile, ModuleDatabase.ResultPictures["image"].Data) + PictureBoxImage.Picture = Picture.Load(tempFile) + PictureBoxImage.Resize(PictureBoxImage.Picture.Width, PictureBoxImage.Picture.Height) + Else + PictureBoxImage.Picture = Null + End If + If Exist(tempFile) Then Kill tempFile +End + +' Filter for our user file open dialog. All image types supported by Gambas +Private Function FileFilter(Optional All As Boolean = False) As String[] + Dim filter As New String[] + If All Then + filter.Add("*.png;*.jpeg;*.jpg;*.bmp;*.gif;*.xpm") + filter.Add(("All Graphics")) + End If + filter.Add("*.png") + filter.Add(("Portable Network Graphics")) + filter.Add("*.jpeg *.jpg") + filter.Add(("Joint Photographic Experts Group")) + filter.Add("*.bmp") + filter.Add(("Windows Bitmap")) + filter.Add("*.gif") + filter.Add(("Graphics Interchange Format")) + filter.Add("*.xpm") + filter.Add(("X PixMap")) + If All Then + filter.Add("*") + filter.Add(("All Files")) + End If + Return filter +End + +''' End of class FormPictureDatabase ''' diff --git a/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form new file mode 100644 index 00000000..49edc095 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form @@ -0,0 +1,67 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,62,47) + Text = ("Picture Database") + Icon = Picture["Images/image-x-generic.png"] + Arrangement = Arrange.Vertical + { HSplit1 HSplit + MoveScaled(0,0,60,46) + Expand = True + { PanelButtons Panel + MoveScaled(0,0,21,42) + Arrangement = Arrange.Vertical + { HPanel1 HPanel + MoveScaled(0,0,21,5) + AutoResize = True + { ToolButtonAdd ToolButton + MoveScaled(0,0,5,5) + ToolTip = ("Add image to database") + Picture = Picture["Images/list-add.png"] + } + { ToolButtonRemove ToolButton + MoveScaled(5,0,5,5) + ToolTip = ("Remove Image from database") + Picture = Picture["Images/list-remove.png"] + } + { Separator1 Separator + MoveScaled(8,0,2,4) + } + { ToolButtonExport ToolButton + MoveScaled(10,0,5,5) + ToolTip = ("Export image from database") + Picture = Picture["Images/document-save-as.png"] + } + { Separator2 Separator + MoveScaled(14,0,2,4) + } + { ToolButtonUpdate ToolButton + MoveScaled(16,0,5,5) + ToolTip = ("Update image description") + Picture = Picture["Images/document-save.png"] + } + } + { GridViewImages GridView + MoveScaled(1,6,19,28) + Expand = True + Header = GridView.Both + Border = False + } + } + { VSplit1 VSplit + MoveScaled(23,1,34,44) + { TextAreaDescription TextArea + MoveScaled(0,0,23,7) + Wrap = True + Border = False + } + { ScrollView1 ScrollView + MoveScaled(0,7,24,35) + { PictureBoxImage PictureBox + MoveScaled(0,0,18,25) + Stretch = True + } + } + } + } +} diff --git a/app/examples/Database/PictureDatabase/.src/ModuleDatabase.module b/app/examples/Database/PictureDatabase/.src/ModuleDatabase.module new file mode 100644 index 00000000..374caf69 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.src/ModuleDatabase.module @@ -0,0 +1,215 @@ +' Gambas module file + +''' +' Name: ModuleDatabase +' Author: Timothy Marshal-Nichols +' eMail: timothy.marshal-nichols@ntlworld.com +' Version: 1.0 +' Version Date: April 2007 +' Version History: +' +''' +' Licence Information +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 2 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +' +' http://www.gnu.org/licenses/gpl.html +' +''' +' Description: +' +' Provides the interface to the pictures database. +' +''' +' Developed using Gambas2 Version +' +' Version: 1.9.48 +' +' Gambas Components Used: +' +' gb - Gambas internal native classes +' gb.db - Database access +' +' Look in the Project menu then Properties... and select +' the Components tab. Check that the listed components +' are in the project. +' +''' +' External Dependencies: +' +' The required type of database must exist. +' +''' +' Class Usage: +' +' Open the pictures database. If the database or table does not +' exist then they are created. +' +' Use somthing like this for a SQLite3 database +' +' ModuleDatabase.OpenDatabase("sqlite3", User.Home, Application.Name, "", "") +' +' Use somthing like this for a MySQL database +' +' ModuleDatabase.OpenDatabase("mysql", "localhost", Application.Name, "mysql", "password") +' +' Use somthing like this for a PostgreSQL database +' +' ModuleDatabase.OpenDatabase("postgresql", "localhost", Application.Name, "timothy", "password") +' +' Call the Select() method to update items and then use ResultPictures to access the data. +' +' ModuleDatabase.Select() +' ModuleDatabase.ResultPictures.MoveTo(Row) +' +' You can then use the Add(), Update() and Delete() methods to change the database. +' +' ModuleDatabase.Add(Path) +' ModuleDatabase.Update(Row, NewDescription) +' ModuleDatabase.Delete(Row) +' +' Call the CloseDatabase method before you quit your application +' +' ModuleDatabase.CloseDatabase() +' +''' + +Public Const ThumbSize As Integer = 36 +Public ResultPictures As Result + +Private databaseConnection As New Connection +Private tempFile As String + +Public Sub _init() + tempFile = Temp() & ".png" +End + +' Opens the pictures database. If the database or table does not +' exist then they are created. +Public Sub OpenDatabase(DBType As String, DBHost As String, DBName As String, UserName As String, UserPassword As String) + Dim pictureTable As Table + Dim errorMessageHeader As String + ' If you wnat to see the commands sent to the + ' database then uncommant this line + ' DB.Debug = TRUE + ' DBName = Lower(DBName) + ' Open a connection (to the database server only) + databaseConnection.Type = Lower(DBType) + databaseConnection.Host = DBHost + databaseConnection.Name = "" + databaseConnection.Login = UserName + databaseConnection.Password = UserPassword + databaseConnection.Port = "" + ' Open the connection + Try databaseConnection.Open() + If Error Then + errorMessageHeader = ("Could not open database connection") & " " & DBHost + Error.Raise(Error.Text) + End If + ' Check if the server connection has a database with the + ' required database name. + If Not databaseConnection.Databases.Exist(DBName) Then + Print ("Database not found. Creating new database") + ' Create a new database + databaseConnection.Databases.Add(DBName) + ' I found I needed this with a SQLite database + ' (but not with a MySQL database) + Wait 0.5 + End If + ' Close the server connection + databaseConnection.Close() + ' Open a connection to the database + databaseConnection.Host = DBHost + databaseConnection.Name = DBName + Try databaseConnection.Open() + If Error Then + errorMessageHeader = ("Could not open database") & " " & DBName & " on " & DBHost + Error.Raise(Error.Text) + End If + ' Check if the database has a pictures table + If Not databaseConnection.Tables.Exist("pictures") Then + Print ("Database tables not found. Creating new pictures table") + ' Add a picture table to the database + pictureTable = databaseConnection.Tables.Add("pictures") + pictureTable.Fields.Add("id", db.Serial) ' id field as autoinc integer + pictureTable.Fields.Add("thumb", db.Blob) ' thumb field as blob + pictureTable.Fields.Add("image", db.Blob) ' ' image field as blob + pictureTable.Fields.Add("description", db.String, 0) ' description field as unlimited string + pictureTable.PrimaryKey = ["id"] + pictureTable.Update() + End If +Catch + If errorMessageHeader = "" Then + errorMessageHeader = ("Database connection error:") & " " & DBName & " on " & DBHost + End If + Error.Raise("" & errorMessageHeader & "
" & ("Error:") & "
" & DConv(Error.Text)) +End + +Public Sub Add(ImagePath As String) + Dim img As Image + Dim newPicture As Result + 'Dim pictureData As String + Dim scale As Float + Dim eTime As Float + newPicture = databaseConnection.Create("pictures") + ' Save temp image as png file + img = Image.Load(ImagePath) + img.Save(tempFile) + newPicture["image"] = File.Load(tempFile) + ' Create image thumb + If img.Width > thumbSize Or img.Height > thumbSize Then + ' Calc factor to scale isotropic + scale = Min(ThumbSize / img.Width, ThumbSize / img.Height) + img = img.Stretch(img.Width * scale, img.Height * scale) + img.Save(tempFile) + End If + newPicture["thumb"] = File.Load(tempFile) + ' Add description and update + newPicture["description"] = ("Image") & " " & File.BaseName(ImagePath) & " " & ("added:") & " " & Format(Now, "dddd, dd mmmm yyyy hh:nn:ss") + eTime = Timer + newPicture.Update() + Print ("Done in") & " "; Format(Timer - eTime, "#.###"); " " & ("s") + If Exist(tempFile) Then Kill tempFile +Catch + Error.Raise(("Add database record error
Error:
") & DConv(Error.Text)) +End + +Public Sub Select() + ResultPictures = databaseConnection.Edit("pictures") +Catch + Error.Raise(("Select database records error
Error:
") & DConv(Error.Text)) +End + +Public Sub Update(Row As Integer, Description As String) + ResultPictures.MoveTo(Row) + ResultPictures["description"] = Conv(Description, Desktop.Charset, databaseConnection.Charset) + ResultPictures.Update() +Catch + Error.Raise(("Update database record error
Error:
") & DConv(Error.Text)) +End + +Public Sub Delete(Row As Integer) + ResultPictures.MoveTo(Row) + ResultPictures.Delete() +Catch + Error.Raise(("Delete database record error
Error:
") & DConv(Error.Text)) +End + +Public Sub CloseDatabase() + Try databaseConnection.Close() + If Error Then Print ("Error closing database") +End + +''' End of ModuleDatabase ''' diff --git a/app/examples/Database/PictureDatabase/Images/document-save-as.png b/app/examples/Database/PictureDatabase/Images/document-save-as.png new file mode 100644 index 0000000000000000000000000000000000000000..fcd8c919711476175e6a2d5cb21df82ec8bcbb74 GIT binary patch literal 845 zcmV-T1G4;yP)hVPfM9)Y zTY7SQfo*PZYj}udZg6XEaBFUGYi@9BZg6XEaBJu1=iuPrZg6X7X=G+-WXsFT$jHc5 zS5sA2Q#fafkG9)&snuhk&O2<8M}4DosMKqv(@KD-UYf^SmcdhuwOEq9V4cfuaBFpW zY-ov-RC0TAb!+za_F$aKgN1rjcZW1-O4+Sr?&ndsQhplq= z?d`a@xasNW=jZ3s*v#VM;jyu>(9qCrbY*UIWzNpd-rwKf-rnBc-`?Kd-{0N<007_L z-{Ilm=;-Lm%F6EU?(p#M@bK~Q@bLQi`v3p`I}llY0000tbW%=J0G;jh$~!Rn^Y#7R zhzxxLg`Nb&yt@eAFq8FSO2vV?YNL@;MowS)ftkFlKo{#H373$lv<00B8k zL_t&-({0Z8Q^GJ5!11{E-g^(MxHSq6YK^A=pBB4$i4gI zT`mYAl$DL4oZLKQEx(|!sCahHQBqoF)$)qUD(AdoVR5OtCe5{V^$n=8soCX{TUy&v z+}_a%XP0wXkypBVQrz1|pjE6uYwKd}7d)^*MG0217>VFPw?-LbSn(*zCT47lxoyBh z+u9C=e+%BlI^JXZ67cZBA?&K^k>^;Ey(h4LY6m7~z~{PZ_zaKX)h`&+;Bnh!;%Prt z4a4Wfe#WSFWkXUx3sQP5JfV~ZwSa`|HzT)qa6v;@)kA>6`_YFnH2z4EJ>lo%^fG}6 zCE>^`5A!JJ;RuiM=o=6saq|9=>NEEB{R1TUH_c|uG|eQNmMol+;K_JKQv^+uf13FN Xun>Ufps_sO00000NkvXXu0mjf*fGY; literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/Images/document-save.png b/app/examples/Database/PictureDatabase/Images/document-save.png new file mode 100644 index 0000000000000000000000000000000000000000..a89f010f2137b11b520783313bc5fdf2ed1837d2 GIT binary patch literal 903 zcmV;219<$2P)hVPfM9)Y zTY7SQfo*PZYj}udZg6XEaBFUGYi@9BZg6XEaBJu1=iuPrZg6X7X=G+-WXsFT$jHc5 zS5sA2Q#fafkG9)&snuhk&O2<8M}4DosMKqv(@KD-UYf^SmcdhuwOEq9V4cfuaBFpW zY-ov-RC0TAb!+za_F$aKgN1rjcZW1-O4+Sr?&ndsQhplq= z?d`a@xasNW=jZ3s*v#VM;jyu>(9qEH^Yg~cz{$?T;^X1*@$t^i&f($V&CSis%*@Eh z$lKf7k&%&@n3$uaqOh>Ava+(fySu!+yv4=E(b3Vsz`)kl*4Nk9l$4a0mzSlbrKzc@ ztgNiMy1K!^!NkPG$;rvZ#l*+Q$1kRBK>z>%H*``?QvjXq^vXLh`t$Yu-Q-){`~Cj* zNx8k$SxE8z{qYOkt{HRFp|XT_X+$t*R<(ov{*SSo{{B`uBMY*N0003-NkliqvqbO1bxWT>a7L_M9Y3~qE{@f+GFS%R*03ad~ z(xRecKxnaX@d=6DJ;tQulu%7gOV2R%8vFVOGP8a;J0~{}T{hx}W1OlPl?)z5< d0-Y#KT|XI$hBxuiCnf*@002ovPDHLkV1n+n60fcu?H8t$*42J5P7M z`+R!Ij+MEcn={)srZ%rnY*-styE>|BWmx(0ppvEjg^RuO7TN{oH_Y67l={b^p)b zcNgz$>{)oQW8T5GIs2Pu?Q58^w|2^oyvnZRtdhLW%~@?5J;IA;tvNM!{qfC*wjaH? zapC5}GghCRw(`W(6~`wpJ2r92(URKk?9#Ttz`(rPsrA$Lctw?ZMi#HwesRT)OUt)k zT(<4v(ybSkY&pMZ^SPdx>w|)WOY0{VRCY{Re%wE<+%+U`{>t6+R_>a&V%MDIJEtz# z(mQi)d}3O7L{vdpV`x}-OaH>YS?j$c3m!dsWbU41>z8ZmpKI-tt?!hiY!<0t60U9$ zVrb)UXzgca?`Lf5t7H;p>=gU>@ngr35{KXt+kip~pF9(fEMxa9BiD2-t8i5lUoGnh zRTDo0=M)R~Br~@px5!$jkTNT;jO>!;qMF|Nwh5=toU-&xjm)0l5mmo=`|cx0j+{Js zvY?{VAt1{!AbZEIU55@Gdi(b6^B2!wynL~4{o1XiqNjn$PNgKsFPNdR>0jP##%cV` z9XdR0ykc_7>RP%6CWdNCvclXf99*32%uL^SZ5}f)Fcx{bIEF}EPEJtZG+33u=)vrfHr7|Kq_lXnW<}kyvbwda>K7YZo7*w9WoggEra2^PXG=$0SF^jTr>BdDo0s$Z z>*vc)*wDCse!W9NK!JxxMnr{6N=OM$M1;PXo}HqhrlqQ?sjjWEv9>jPO2;z4ytq2Y z#K1yN&&5VO+>E{`nc7A@QAF-j};v-e>>ALY7g;e%reVt{x`ue)){fUQ<`Oe;+b9YY;rxeig0sW%Q>Ja%l^k|jJWISveq a9FiD(XU>n6v6Zg`6%wAVelF{r5}E)!3h_Pw literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/Images/list-add.png b/app/examples/Database/PictureDatabase/Images/list-add.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d751c9e4c163533061b83a0a1577f145d6acd3 GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6(*k@#T!FMn>JlJH3;?R!dgket zGfy`I(dnlLF2C4)@%f&M&-Yz^x#`r?-4~v3Klg0g*=JkNJll5W+0^Z~r);}DY0Iq% zn{V}QyxFz>X4~4E&8u$Iuee^j?E3tD50)H!R5_tMtW&-3P7y8Ph% zsw0oJ9!50+9mHP}2G25qZ=bq5x;i_#y*<16?rmaW(_g$u61{f$xJX*kGjoF{>4~Y%VkyrbJYixLHRso77w#%Dvr|*E cGh@qQVkkA{aBa={%?1iDPgg&ebxsLQ06^xHVE_OC literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/Images/list-remove.png b/app/examples/Database/PictureDatabase/Images/list-remove.png new file mode 100644 index 0000000000000000000000000000000000000000..902624de5a28d1191b6f4ce9e6163cea028fcd6d GIT binary patch literal 282 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 M07*qoM6N<$f=#3V(f|Me literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/AnalogWatch/.lang/ru.po b/app/examples/Drawing/AnalogWatch/.lang/ru.po new file mode 100644 index 00000000..28a5cfeb --- /dev/null +++ b/app/examples/Drawing/AnalogWatch/.lang/ru.po @@ -0,0 +1,72 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/AnalogWatch/.project:19 +msgid "Analog watch" +msgstr "Аналоговые часы" + +#: app/examples/Drawing/AnalogWatch/.project:20 +msgid "" +"Analog watch example.\n" +"\n" +"This example uses the Paint class to draw an anti-aliased analog watch." +msgstr "" +"Пример аналоговых часов.\n" +"\n" +"В этом примере класс Paint используется для рисования аналоговых часов с сглаживанием." + +#: app/examples/Drawing/AnalogWatch/.src/FrmClock.class:122 +msgid "" +"Analog Clock Example Program for Gambas\n" +"Written by: Ahmad Kamal and Benoît Minisini" +msgstr "" +"Пример программы аналоговых часов для Gambas\n" +"Авторы: Ахмад Камаль и Бенуа Минисини" + +#: app/examples/Drawing/AnalogWatch/.src/FrmClock.form:5 +msgid "Analog clock" +msgstr "Аналоговые часы" + +#: app/examples/Drawing/AnalogWatch/.src/FrmClock.form:8 +msgid "File" +msgstr "Файл" + +#: app/examples/Drawing/AnalogWatch/.src/FrmClock.form:11 +msgid "&About" +msgstr "О программе" + +#: app/examples/Drawing/AnalogWatch/.src/FrmClock.form:17 +msgid "E&xit" +msgstr "Выход" + +#: app/examples/Drawing/AnalogWatch/.src/FrmClock.form:23 +msgid "" +"Resize Window and the clock will follow \n" +"Right click for a menu" +msgstr "" +"Изменить размер окна, и часы последуют тому же\n" +"Правая кнопка мыши для меню" + diff --git a/app/examples/Drawing/AnalogWatch/.project b/app/examples/Drawing/AnalogWatch/.project new file mode 100644 index 00000000..6e9aed87 --- /dev/null +++ b/app/examples/Drawing/AnalogWatch/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Analog watch +Startup=FrmClock +Icon=timer.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Description="Analog watch example.\n\nThis example uses the Paint class to draw an anti-aliased analog watch." +Environment="GB_GUI=gb.qt" +TabSize=2 +Maintainer=fabien +Vendor=Example +Address=fabien@arcalis +License=General Public Licence +Packager=1 +Screenshot=2014-12-14.png +Translate=1 diff --git a/app/examples/Drawing/AnalogWatch/.src/FrmClock.class b/app/examples/Drawing/AnalogWatch/.src/FrmClock.class new file mode 100644 index 00000000..0a163bfc --- /dev/null +++ b/app/examples/Drawing/AnalogWatch/.src/FrmClock.class @@ -0,0 +1,152 @@ +' Gambas class file + +'***********Analog Clock Example Program************************ +'***********By: Ahmad Kamal ****************** +'***********Written for Gambas: gambas.sourceforge.net********** +'***********V1.0: 22-July-2003*************************************** +'***********V1.1: 24-July-2003 Optimized by Benoit himself ;)***** + +Private $iLast As Integer +Private $dNow As Date + +' Fixed coordinate system +' We use the transformation matrix for scaling +Private Const W As Integer = 1024 +Private Const H As Integer = 1024 + +Public Sub TimerClk_Timer() + + If Second(Now) = $iLast Then Return + $dNow = Now + + dwgArea.Refresh + +End + +' Main routine that updates the clock + +Public Sub DrawClock() + + Dim angle As Float + Dim dNow As Date + Dim eScale As Float + Dim sTime As String + + eScale = Min(dwgArea.Width / W, dwgArea.Height / H) + + Paint.Translate(dwgArea.W / 2, dwgArea.H / 2) + Paint.Scale(eScale, eScale) + + dNow = $dNow + $iLast = Second(dNow) + + Paint.Brush = Paint.Color(&HDCDCDC&) 'Light gray color + Paint.Arc(0, 0, W / 2) + Paint.Fill + Paint.Brush = Paint.Color(dwgArea.Background) + Paint.Arc(0, 0, W / 2 * 0.9) + Paint.Fill + + sTime = Format(dNow, "hh:nn:ss") + + Paint.Font.Bold = True + Paint.Font.Size = 60 + Paint.Brush = Paint.Color(Color.Black) + Paint.Text(sTime, -W / 2, -H * 0.4, W, H * 0.1, Align.Top) + Paint.Fill + + ' Draw seconds + angle = Second(dNow) / 60 * Pi(2) - Pi(0.5) 'The angle that the arm makes, with a line from clock center to 12O'clock + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Red, 128)) + Paint.LineWidth = 6 + Paint.MoveTo(0, 0) + Paint.LineTo(Cos(angle) * W / 2, Sin(angle) * H / 2) + Paint.Stroke + + ' Draw minutes + angle = CFloat(Time(dNow)) * 24 * Pi(2) - Pi(0.5) + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 128)) + Paint.MoveTo(Cos(angle) * W * 0.45, Sin(angle) * H * 0.45) + Paint.LineTo(Sin(angle) * W * 0.05, -Cos(angle) * H * 0.05) + Paint.LineTo(-Sin(angle) * W * 0.05, Cos(angle) * H * 0.05) + Paint.Fill + + ' Draw hours + angle = CFloat(Time(dNow)) * 2 * Pi(2) - Pi(0.5) + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 128)) + Paint.MoveTo(Cos(angle) * W * 0.35, Sin(angle) * H * 0.35) + Paint.LineTo(Sin(angle) * W * 0.05, -Cos(angle) * H * 0.05) + Paint.LineTo(-Sin(angle) * W * 0.05, Cos(angle) * H * 0.05) + Paint.Fill + + ' Draw circle on the center of the clock to hide the arms intersection + + Paint.Brush = Paint.Color(Color.Black) + Paint.Arc(0, 0, 0.1 * W) + Paint.Fill + + DrawFrame + +End + +' Draw the clock frame + +Public Sub DrawFrame() + + Dim I As Integer + Dim angle As Float + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + + For I = 0 To 59 + If I % 5 = 0 Then + Paint.LineWidth = 12 + Else + Paint.LineWidth = 2 + Endif + angle = Pi(2) * I / 60 + Paint.MoveTo(Cos(angle) * 0.45 * W, Sin(angle) * 0.45 * H) + Paint.LineTo(Cos(angle) * W / 2, Sin(angle) * H / 2) + Paint.Stroke + Next + +End + + +Public Sub MenuAbout_Click() + + Dim AboutMessage As String + AboutMessage = ("Analog Clock Example Program for Gambas\nWritten by: Ahmad Kamal and Benoît Minisini") + + Message.info(AboutMessage) + +End + + +Public Sub MenuExit_Click() + + Me.Close + +End + + +Public Sub DwgArea_Menu() + + MenuPopUp.popup + +End + +Public Sub DwgArea_Draw() + + DrawClock + +End + +Public Sub Form_Open() + + TimerClk_Timer + +End diff --git a/app/examples/Drawing/AnalogWatch/.src/FrmClock.form b/app/examples/Drawing/AnalogWatch/.src/FrmClock.form new file mode 100644 index 00000000..c8746890 --- /dev/null +++ b/app/examples/Drawing/AnalogWatch/.src/FrmClock.form @@ -0,0 +1,30 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(52.2857,29,39,39) + Text = ("Analog clock") + Arrangement = Arrange.Fill + { MenuPopUp Menu + Text = ("File") + Visible = False + { MenuAbout Menu + Text = ("&About") + Shortcut = "F11" + } + { MenuSep Menu + } + { MenuExit Menu + Text = ("E&xit") + } + } + { DwgArea DrawingArea + MoveScaled(2,0,21,24) + Background = Color.LightBackground + ToolTip = ("Resize Window and the clock will follow \nRight click for a menu") + } + { TimerClk #Timer + #MoveScaled(17.1429,2.2857) + Enabled = True + Delay = 250 + } +} diff --git a/app/examples/Drawing/AnalogWatch/timer.png b/app/examples/Drawing/AnalogWatch/timer.png new file mode 100644 index 0000000000000000000000000000000000000000..6708fdd27788c95fe589ee232dff093831b8041a GIT binary patch literal 2778 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Barcode/.directory b/app/examples/Drawing/Barcode/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Barcode/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Barcode/.icon.png b/app/examples/Drawing/Barcode/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a4e3ec072e2c2cc337c467973d2ee992b03419c1 GIT binary patch literal 3990 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\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:280 +msgid "About..." +msgstr "Quant a..." + +#: .project:2 +msgid "A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +msgstr "Un mòdul per a imprimir codis de barres EAN-13. Els codis de barres es construeixen des de zero (http://en.wikipedia.org/wiki/European_Article_Number). El codi de barres pot tenir format per pantalla o per impressora." + +#: FMain.class:221 +msgid "Barcode" +msgstr "Codi de barres" + +#: .project:1 +msgid "Barcode Printing" +msgstr "Impressió de codi de barres" + +#: FMain.class:285 +msgid "E&xit" +msgstr "&Surt" + +#: FMain.class:236 +msgid "Height" +msgstr "Alçada" + +#: FMain.class:72 +msgid "Please enter a valid 13 digit EAN-13 number" +msgstr "Introduïu un número EAN-13 vàlid de 13 dígits" + +#: FMain.class:226 +msgid "PosX" +msgstr "-" + +#: FMain.class:231 +msgid "PosY" +msgstr "-" + +#: FMain.class:275 +msgid "&Print Barcode" +msgstr "Im&primeix el codi de barres" + +#: FMain.class:269 +msgid "&Refresh Barcode" +msgstr "&Actualitza el codi de barres" + +#: FMain.class:246 +msgid "Size for printer" +msgstr "Mida per la impressora" + +#: FMain.class:251 +msgid "Size for screen" +msgstr "Mida per la pantalla" + +#: FMain.class:153 +msgid "" +"This program was made by Charles Guerin and\n" +"modified by Benoît Minisini." +msgstr "" +"Aquest programa va ser creat per Charles Guerin i\n" +"modificat per Benoît Minisini." + +#: FMain.class:241 +msgid "Width" +msgstr "Amplada" + diff --git a/app/examples/Drawing/Barcode/.lang/cs.po b/app/examples/Drawing/Barcode/.lang/cs.po new file mode 100644 index 00000000..9368d9a5 --- /dev/null +++ b/app/examples/Drawing/Barcode/.lang/cs.po @@ -0,0 +1,76 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Barcode Printing" +msgstr "Tisk čárových kódů" + +#: .project:2 +msgid "A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +msgstr "Modul pro tisk čárových kódů EAN-13. Čárového kódu je postaven z prvních principů (http://en.wikipedia.org/wiki/European_Article_Number). Čárový kód může být formátovány pro obrazovku nebo tiskárnu." + +#: FMain.class:72 +msgid "Please enter a valid 13 digit EAN-13 number" +msgstr "Zadejte prosím platné 13-ti místné číslo EAN-13" + +#: FMain.class:153 +msgid "" +"This program was made by Charles Guerin and\n" +"modified by Benoît Minisini." +msgstr "" +"Tento program byl vytvořil Charles Guerin a\n" +"upravil Benoît Minisini." + +#: FMain.form:57 +msgid "Barcode" +msgstr "Čárový kód" + +#: FMain.form:62 +msgid "PosX" +msgstr "Pozice X" + +#: FMain.form:67 +msgid "PosY" +msgstr "Pozice Y" + +#: FMain.form:72 +msgid "Height" +msgstr "Výška" + +#: FMain.form:77 +msgid "Width" +msgstr "Šířka" + +#: FMain.form:83 +msgid "Size for printer" +msgstr "Velikost pro tiskárnu" + +#: FMain.form:89 +msgid "Size for screen" +msgstr "Velikost pro obrazovku" + +#: FMain.form:107 +msgid "&Refresh Barcode" +msgstr "&Obnovit čárový kód" + +#: FMain.form:114 +msgid "&Print Barcode" +msgstr "&Tisk čárového kódu" + +#: FMain.form:119 +msgid "About..." +msgstr "O programu..." + +#: FMain.form:124 +msgid "E&xit" +msgstr "&Ukončit" diff --git a/app/examples/Drawing/Barcode/.lang/de.po b/app/examples/Drawing/Barcode/.lang/de.po new file mode 100644 index 00000000..7067c161 --- /dev/null +++ b/app/examples/Drawing/Barcode/.lang/de.po @@ -0,0 +1,76 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Barcode Printing" +msgstr "Barcode drucken" + +#: .project:2 +msgid "A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +msgstr "Ein Modul, um EAN-13 Barcodes zu drucken. Der Barcode wird nach den Regeln (http://en.wikipedia.org/wiki/European_Article_Number) gebildet. Der Barcode kann für den Bildschirm oder für den Drucker formattiert werden." + +#: FMain.class:72 +msgid "Please enter a valid 13 digit EAN-13 number" +msgstr "Bitte gib eine gültige 13-stellige EAN-13 Zahl ein" + +#: FMain.class:153 +msgid "" +"This program was made by Charles Guerin and\n" +"modified by Benoît Minisini." +msgstr "" +"Dieses Programm wurde von Charles Guerin geschrieben\n" +"und von Benoît Minisini verändert." + +#: FMain.form:57 +msgid "Barcode" +msgstr "-" + +#: FMain.form:62 +msgid "PosX" +msgstr "-" + +#: FMain.form:67 +msgid "PosY" +msgstr "-" + +#: FMain.form:72 +msgid "Height" +msgstr "Höhe" + +#: FMain.form:77 +msgid "Width" +msgstr "Breite" + +#: FMain.form:83 +msgid "Size for printer" +msgstr "Größe für Drucker" + +#: FMain.form:89 +msgid "Size for screen" +msgstr "Größe für Bildschirm" + +#: FMain.form:107 +msgid "&Refresh Barcode" +msgstr "Barcode &neu laden" + +#: FMain.form:114 +msgid "&Print Barcode" +msgstr "Barcode &drucken" + +#: FMain.form:119 +msgid "About..." +msgstr "Über..." + +#: FMain.form:124 +msgid "E&xit" +msgstr "&Beenden" diff --git a/app/examples/Drawing/Barcode/.lang/ru.po b/app/examples/Drawing/Barcode/.lang/ru.po new file mode 100644 index 00000000..781038ca --- /dev/null +++ b/app/examples/Drawing/Barcode/.lang/ru.po @@ -0,0 +1,90 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/Barcode/.project:22 +msgid "Barcode Printing" +msgstr "Печать штрих-кодов" + +#: app/examples/Drawing/Barcode/.project:23 +msgid "A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +msgstr "Модуль для печати штрих-кодов EAN-13. Штрих-код создан на основе первых принципов (https://ru.wikipedia.org/wiki/European_Article_Number). Штрих-код может быть отформатирован для экрана или принтера." + +#: app/examples/Drawing/Barcode/.src/FMain.class:72 +msgid "Please enter a valid 13 digit EAN-13 number" +msgstr "Пожалуйста, введите действительный 13-значный номер EAN-13" + +#: app/examples/Drawing/Barcode/.src/FMain.class:153 +msgid "" +"This program was made by Charles Guerin and\n" +"modified by Benoît Minisini." +msgstr "" +"Эта программа была создана Чарльзом Гереном\n" +"и модифицирована Бенуа Минисини." + +#: app/examples/Drawing/Barcode/.src/FMain.form:29 +msgid "Barcode" +msgstr "Штрих-код" + +#: app/examples/Drawing/Barcode/.src/FMain.form:33 +msgid "PosX" +msgstr "ПозX" + +#: app/examples/Drawing/Barcode/.src/FMain.form:37 +msgid "PosY" +msgstr "ПозY" + +#: app/examples/Drawing/Barcode/.src/FMain.form:41 +msgid "Height" +msgstr "Высота" + +#: app/examples/Drawing/Barcode/.src/FMain.form:45 +msgid "Width" +msgstr "Ширина" + +#: app/examples/Drawing/Barcode/.src/FMain.form:50 +msgid "Size for printer" +msgstr "Размер для принтера" + +#: app/examples/Drawing/Barcode/.src/FMain.form:55 +msgid "Size for screen" +msgstr "Размер для экрана" + +#: app/examples/Drawing/Barcode/.src/FMain.form:70 +msgid "&Refresh Barcode" +msgstr "Освежить штрих-код" + +#: app/examples/Drawing/Barcode/.src/FMain.form:76 +msgid "&Print Barcode" +msgstr "Печать штрих-кода" + +#: app/examples/Drawing/Barcode/.src/FMain.form:80 +msgid "About..." +msgstr "О программе..." + +#: app/examples/Drawing/Barcode/.src/FMain.form:84 +msgid "E&xit" +msgstr "Выход" + diff --git a/app/examples/Drawing/Barcode/.project b/app/examples/Drawing/Barcode/.project new file mode 100644 index 00000000..5bbad76a --- /dev/null +++ b/app/examples/Drawing/Barcode/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Barcode Printing +Startup=FMain +Icon=barcode.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Description="A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=de +Maintainer=charles +Address=c_guerin@o2.co.uk +License=General Public Licence +Packager=1 +Systems=ubuntu +Menus=ubuntu:"Apps/Programming" +Groups=ubuntu:"Development/Databases" diff --git a/app/examples/Drawing/Barcode/.src/FMain.class b/app/examples/Drawing/Barcode/.src/FMain.class new file mode 100644 index 00000000..9207a99b --- /dev/null +++ b/app/examples/Drawing/Barcode/.src/FMain.class @@ -0,0 +1,161 @@ +' Gambas class file + +Public bcHeight As Integer +Public barThickness As Integer +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Public Sub SizeForScreen() + 'set up typical defaults for barcode size on screen + txtPosX.Text = "60" + txtPosY.text = "8" + txtHeight.Text = "150" + txtWidth.Text = "5" +End +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Public Sub SizeForPrinter() + 'set up typical defaults for barcode size on printer + txtPosX.Text = "500" + txtPosY.text = "400" + txtHeight.Text = "250" + txtWidth.Text = "7" +End +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Public Sub ChkInput() + + ' make sure only valid characters + Select Key.Code + Case Key.BackSpace, Key.Tab, Key.Delete, Key.Enter, Key.Return, Key.Escape + Default + If Key.Text And If InStr("0123456789", Key.Text) = 0 Then Stop Event + End Select + +End +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + + +Public Sub Form_Open() + 'DrawingArea1.Cached = TRUE + txtBarcode.SetFocus + SizeForScreen + txtBarcode.Text = "8711577011208" 'sample barcode (so what's it for then?) + txtBarcode.Select +End +'------------------------------------------------------------------- +Public Sub btnExit_Click() + + FMain.Close +End + +Private Sub DrawBarCode(Optional bPrinter As Boolean) + + If Not bPrinter Then + + 'DrawingArea1.Refresh + 'Draw.Begin(DrawingArea1) + Draw.Clear + modCrBcode.PrintBarcode(txtBarcode.Text, Val(txtPosX.text), Val(txtPosY.text), Val(txtHeight.text), Val(txtWidth.text), True) + 'Draw.End() + + Else + + Draw.Begin(printer) + modCrBcode.PrintBarcode(txtBarcode.Text, Val(txtPosX.text), Val(txtPosY.text), Val(txtHeight.text), Val(txtWidth.text), False) + Draw.End() + + Endif + +End + + +Public Sub btnRun_Click() + + If Len(txtBarcode.Text) <> 13 Then + Message.Error(("Please enter a valid 13 digit EAN-13 number")) + Else + If Val(txtPosX.Text) = 0 Then + If rbutScreen Then + txtPosX.Text = "40" + Else + txtPosX.Text = "500" + Endif + Endif + If Val(txtPosY.Text) = 0 Then + If rbutScreen Then + txtPosY.Text = "0" + Else + txtPosY.Text = "300" + Endif + Endif + If Val(txtHeight.Text) = 0 Then + If rbutScreen Then + txtHeight.Text = "150" + Else + txtHeight.Text = "250" + Endif + Endif + If Val(txtWidth.Text) = 0 Then + If rbutScreen Then + txtWidth.Text = "5" + Else + txtWidth.Text = "7" + Endif + Endif + + 'DrawBarCode + DrawingArea1.Refresh + + Endif + +End +'------------------------------------------------------------------- +Public Sub rbutPrinter_Click() + SizeForPrinter +End +'------------------------------------------------------------------- +Public Sub rbutScreen_Click() + SizeForScreen +End +'------------------------------------------------------------------- +Public Sub txtPosX_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtBarcode_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtPosY_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtHeight_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtWidth_KeyPress() + ChkInput +End +'------------------------------------------------------------------- + +' PUBLIC SUB Form_Resize() +' +' DrawBarCode +' +' END + +Public Sub btnPrint_Click() + + DrawBarCode(True) + +End + +Public Sub btnAbout_Click() + + Message.Info(("This program was made by Charles Guerin and\nmodified by Benoît Minisini.")) + +End + +Public Sub DrawingArea1_Draw() + + DrawBarCode + +End diff --git a/app/examples/Drawing/Barcode/.src/FMain.form b/app/examples/Drawing/Barcode/.src/FMain.form new file mode 100644 index 00000000..1a293c03 --- /dev/null +++ b/app/examples/Drawing/Barcode/.src/FMain.form @@ -0,0 +1,88 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,106,62) + Icon = Picture["barcode.png"] + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + Padding = 8 + { Panel1 Panel + MoveScaled(1,1,95,8) + { txtBarcode TextBox + MoveScaled(0,4,24,4) + } + { txtPosX TextBox + MoveScaled(25,4,8,4) + } + { txtPosY TextBox + MoveScaled(34,4,8,4) + } + { txtHeight TextBox + MoveScaled(43,4,8,4) + } + { txtWidth TextBox + MoveScaled(52,4,8,4) + } + { Label1 Label + MoveScaled(0,0,18,4) + Text = ("Barcode") + } + { Label2 Label + MoveScaled(25,0,7,4) + Text = ("PosX") + } + { Label3 Label + MoveScaled(34,0,6,4) + Text = ("PosY") + } + { Label4 Label + MoveScaled(43,0,8,4) + Text = ("Height") + } + { Label5 Label + MoveScaled(52,0,8,4) + Text = ("Width") + } + { rbutPrinter RadioButton + MoveScaled(63,3,27,4) + Visible = False + Text = ("Size for printer") + } + { rbutScreen RadioButton + MoveScaled(63,0,27,4) + Visible = False + Text = ("Size for screen") + Value = True + } + } + { DrawingArea1 DrawingArea + MoveScaled(1,10,104,45) + Background = Color.TextBackground + Expand = True + Border = Border.Sunken + } + { HBox1 HBox + MoveScaled(1,56,104,5) + Spacing = True + { btnRun Button + MoveScaled(0,0,25.1429,4) + Text = ("&Refresh Barcode") + Default = True + } + { btnPrint Button + MoveScaled(26,0,25,4) + Visible = False + Text = ("&Print Barcode") + } + { btnAbout Button + MoveScaled(52,0,25.1429,4) + Text = ("About...") + } + { btnExit Button + MoveScaled(78,0,25.1429,4) + Text = ("E&xit") + Cancel = True + } + } +} diff --git a/app/examples/Drawing/Barcode/.src/modCrBcode.module b/app/examples/Drawing/Barcode/.src/modCrBcode.module new file mode 100644 index 00000000..453a0e5a --- /dev/null +++ b/app/examples/Drawing/Barcode/.src/modCrBcode.module @@ -0,0 +1,98 @@ +' Gambas module file + +Public Sub PrintBarcode(bcode As String, posX As Integer, posY As Integer, bcHeight As Integer, barThickness As Integer, SorP As Boolean) + Dim structure As New String[10] + 'DIM enc AS String[10, 3]. I did it the following way 'cos I get not get the prg to read a multi-dimension array! + Dim enc1 As New String[10] + Dim enc2 As New String[10] + Dim enc3 As New String[10] + Dim fontSizeScreen As New Integer[10] + Dim fontSizePrinter As New Integer[10] + + Dim first6 As String ' encoding for first 6: l-code(1), g-code(2) + Dim j As Integer + Dim l As Integer + Dim k As Integer + Dim frst As Integer 'the first digit + Dim num As Integer 'each digit in turn + Dim bars As String ' the bars for the digit + 'Dim thkns As Integer 'thickness of each bar + Dim LinePos As Integer 'used to horizontaly locate the next bar + + ' + structure = ["111111", "112122", "112212", "112221", "121122", "122112", "122211", "121212", "121221", "122121"] + enc1 = ["0001101", "0011001", "0010011", "0111101", "0100011", "0110001", "0101111", "0111011", "0110111", "0001011"] + enc2 = ["0100111", "0110011", "0011011", "0100001", "0011101", "0111001", "0000101", "0010001", "0001001", "0010111"] + enc3 = ["1110010", "1100110", "1101100", "1000010", "1011100", "1001110", "1010000", "1000100", "1001000", "1110100"] + fontSizeScreen = [6, 9, 12, 16, 20, 24, 26, 28, 28, 30] + fontSizePrinter = [2, 2, 2, 4, 4, 6, 6, 8, 8, 10] + + frst = Val(Left(bcode, 1)) + first6 = structure[frst] + + 'draw 1st 2 deep bars----------------------------------------------- + For l = 1 To barThickness + Draw.Line(l + posX, 1 + posY, l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + For l = 1 To barThickness + Draw.Line(LinePos + l + posX, 1 + posY, LinePos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness + '--------------------------------------------------------------- + For j = 2 To 13 'loop through each of the 12 digits + num = Val(Mid$(bcode, j, 1)) 'find the individual number IN the first 6 + If j < 8 Then 'different for first 6 + If Val(Mid(first6, j - 1, 1)) = 1 Then 'find the bars making up that number (L,G or R) + bars = enc1[num] + Else + bars = enc2[num] + Endif + Else + bars = enc3[num] + Endif + + 'put the 2 middle deep bars---------------------------------------- + If j = 8 Then + LinePos = LinePos + barThickness + For l = 1 To barThickness + Draw.Line(linepos + l + posX, 1 + posY, linepos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + For l = 1 To barThickness + Draw.Line(LinePos + l + posX, 1 + posY, LinePos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + Endif + '--------------------------------------------------------------- + For k = 1 To 7 'draw bars for single digit + If Mid(bars, k, 1) = "1" Then + For l = 1 To barThickness + Draw.Line(linepos + l + posX, 1 + posY, LinePos + l + posX, bcHeight + posY) + Next + Endif + linepos = LinePos + barThickness + Next + Next + + 'draw last 2 deep bars------------------------------------------------ + For l = 1 To barThickness + Draw.Line(linepos + l + posX, 1 + posY, linepos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + For l = 1 To barThickness + Draw.Line(LinePos + l + posX, 1 + posY, LinePos + l + posX, bcHeight * 1.2 + posY) + Next + '--------------------------------------------------------------- + 'write the barcode text + If SorP Then + Draw.Font.Size = fontSizeScreen[barThickness - 1] + Else + Draw.Font.Size = fontSizePrinter[barThickness - 1] + Endif + Draw.Text(Left(bcode, 1), posX - barThickness * 6, posY + bcHeight * 1.02) + Draw.Text(Mid(bcode, 2, 6), posX + barThickness * 9, posY + bcHeight * 1.02) + Draw.Text(Right(bcode, 6), posX + barThickness * 58, posY + bcHeight * 1.02) + + +End diff --git a/app/examples/Drawing/Barcode/barcode.png b/app/examples/Drawing/Barcode/barcode.png new file mode 100644 index 0000000000000000000000000000000000000000..a62216848a8334bb7eddc90a77d633afcbfa8ccb GIT binary patch literal 765 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Chart/.lang/ca.po b/app/examples/Drawing/Chart/.lang/ca.po new file mode 100644 index 00000000..612abf34 --- /dev/null +++ b/app/examples/Drawing/Chart/.lang/ca.po @@ -0,0 +1,85 @@ +# Catalan translation of Chart +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Chart package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Chart\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:34+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FormChart.class:129 +msgid "Example to make a Bar Chart" +msgstr "Exemple de fer un gràfic de barres" + +#: FormChart.class:146 FormData.class:172 +msgid "&Close" +msgstr "&Tanca" + +#: FormChart.class:151 +msgid "&About" +msgstr "&Quant a" + +#: FormChart.class:158 +msgid "(%)" +msgstr "-" + +#: FormData.class:62 +msgid "Chart example" +msgstr "Exemple de gràfic" + +#: FormData.class:67 +msgid "Data 1" +msgstr "Dada 1" + +#: FormData.class:72 +msgid "Data 2" +msgstr "Dada 2" + +#: FormData.class:77 +msgid "Data 3" +msgstr "Dada 3" + +#: FormData.class:82 +msgid "Data 4" +msgstr "Dada 4" + +#: FormData.class:87 +msgid "Data 5" +msgstr "Dada 5" + +#: FormData.class:92 +msgid "Data 6" +msgstr "Dada 6" + +#: FormData.class:97 +msgid "Data 7" +msgstr "Dada 7" + +#: FormData.class:102 +msgid "Data 8" +msgstr "Dada 8" + +#: FormData.class:107 +msgid "Data 9" +msgstr "Dada 9" + +#: FormData.class:112 +msgid "Data 10" +msgstr "Dada 10" + +#: FormData.class:167 +msgid "&Draw it" +msgstr "&Dibuixa'l" + +#~ msgid "Bar Chart" +#~ msgstr "Gràfic de barres" diff --git a/app/examples/Drawing/Chart/.lang/cs.po b/app/examples/Drawing/Chart/.lang/cs.po new file mode 100644 index 00000000..b785f3f3 --- /dev/null +++ b/app/examples/Drawing/Chart/.lang/cs.po @@ -0,0 +1,84 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Bar Chart" +msgstr "Sloupcový graf" + +#: FormChart.form:13 +msgid "Example to make a Bar Chart" +msgstr "Příklad na vytvoření sloupcového grafu" + +#: FormChart.form:30 FormData.form:130 +msgid "&Close" +msgstr "&Zavřít" + +#: FormChart.form:35 +msgid "&About" +msgstr "&O..." + +#: FormChart.form:42 +msgid "(%)" +msgstr "-" + +#: FormData.form:30 +msgid "Chart example" +msgstr "Příklad grafu" + +#: FormData.form:35 +msgid "Data 1" +msgstr "-" + +#: FormData.form:40 +msgid "Data 2" +msgstr "-" + +#: FormData.form:45 +msgid "Data 3" +msgstr "-" + +#: FormData.form:50 +msgid "Data 4" +msgstr "-" + +#: FormData.form:55 +msgid "Data 5" +msgstr "-" + +#: FormData.form:60 +msgid "Data 6" +msgstr "-" + +#: FormData.form:65 +msgid "Data 7" +msgstr "-" + +#: FormData.form:70 +msgid "Data 8" +msgstr "-" + +#: FormData.form:75 +msgid "Data 9" +msgstr "Data 9" + +#: FormData.form:80 +msgid "Data 10" +msgstr "-" + +#: FormData.form:125 +msgid "&Draw it" +msgstr "&Kresli" + +#: FormData.form:135 +msgid "&Random" +msgstr "&Náhodně" diff --git a/app/examples/Drawing/Chart/.lang/de.po b/app/examples/Drawing/Chart/.lang/de.po new file mode 100644 index 00000000..f6a4cdbc --- /dev/null +++ b/app/examples/Drawing/Chart/.lang/de.po @@ -0,0 +1,81 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Bar Chart" +msgstr "Balkendiagramm" + +#: FormChart.form:13 +msgid "Example to make a Bar Chart" +msgstr "Beispiel für ein Balkendiagramm" + +#: FormChart.form:30 FormData.form:139 +msgid "&Close" +msgstr "&Schließen" + +#: FormChart.form:35 +msgid "&About" +msgstr "&Über" + +#: FormChart.form:42 +msgid "(%)" +msgstr "-" + +#: FormData.form:29 +msgid "Chart example" +msgstr "Diagramm-Beispiel" + +#: FormData.form:34 +msgid "Data 1" +msgstr "Wert 1" + +#: FormData.form:39 +msgid "Data 2" +msgstr "Wert 2" + +#: FormData.form:44 +msgid "Data 3" +msgstr "Wert 3" + +#: FormData.form:49 +msgid "Data 4" +msgstr "Wert 4" + +#: FormData.form:54 +msgid "Data 5" +msgstr "Wert 5" + +#: FormData.form:59 +msgid "Data 6" +msgstr "Wert 6" + +#: FormData.form:64 +msgid "Data 7" +msgstr "Wert 7" + +#: FormData.form:69 +msgid "Data 8" +msgstr "Wert 8" + +#: FormData.form:74 +msgid "Data 9" +msgstr "Wert 9" + +#: FormData.form:79 +msgid "Data 10" +msgstr "Wert 10" + +#: FormData.form:134 +msgid "&Draw it" +msgstr "&Zeichnen" + diff --git a/app/examples/Drawing/Chart/.lang/es.po b/app/examples/Drawing/Chart/.lang/es.po new file mode 100644 index 00000000..89edca99 --- /dev/null +++ b/app/examples/Drawing/Chart/.lang/es.po @@ -0,0 +1,78 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FormChart.class:129 +msgid "Example to make a Bar Chart" +msgstr "Ejemplo para hacer un Gráfico de Barras" + +#: FormChart.class:146 FormData.class:172 +msgid "&Close" +msgstr "&Cerrar" + +#: FormChart.class:151 +msgid "&About" +msgstr "&Acerca de" + +#: FormChart.class:158 +msgid "(%)" +msgstr "(%)" + +#: FormData.class:62 +msgid "Chart example" +msgstr "Ejemplo de gráficos" + +#: FormData.class:67 +msgid "Data 1" +msgstr "Dato 1" + +#: FormData.class:72 +msgid "Data 2" +msgstr "Dato 2" + +#: FormData.class:77 +msgid "Data 3" +msgstr "Dato 3" + +#: FormData.class:82 +msgid "Data 4" +msgstr "Dato 4" + +#: FormData.class:87 +msgid "Data 5" +msgstr "Dato 5" + +#: FormData.class:92 +msgid "Data 6" +msgstr "Dato 6" + +#: FormData.class:97 +msgid "Data 7" +msgstr "Dato 7" + +#: FormData.class:102 +msgid "Data 8" +msgstr "Dato 8" + +#: FormData.class:107 +msgid "Data 9" +msgstr "Dato 9" + +#: FormData.class:112 +msgid "Data 10" +msgstr "Dato 10" + +#: FormData.class:167 +msgid "&Draw it" +msgstr "&Dibujarlo" + +#~ msgid "Bar Chart" +#~ msgstr "Gráfico de barras" diff --git a/app/examples/Drawing/Chart/.lang/ru.po b/app/examples/Drawing/Chart/.lang/ru.po new file mode 100644 index 00000000..041f5ac3 --- /dev/null +++ b/app/examples/Drawing/Chart/.lang/ru.po @@ -0,0 +1,114 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/Chart/.project:18 +msgid "Bar Chart" +msgstr "Гистограмма" + +#: app/examples/Drawing/Chart/.project:19 +msgid "Example to make bar chart" +msgstr "Пример создания гистограммы" + +#: app/examples/Drawing/Chart/.src/FormChart.class:104 +msgid "

Example to make bar chart

\n" +msgstr "

Пример создания гистограммы

\n" + +#: app/examples/Drawing/Chart/.src/FormChart.class:105 +msgid "This example has made by : " +msgstr "Этот пример сделан: " + +#: app/examples/Drawing/Chart/.src/FormChart.class:109 +msgid "Thank You" +msgstr "Спасибо" + +#: app/examples/Drawing/Chart/.src/FormChart.class:110 app/examples/Drawing/Chart/.src/FormChart.form:20 app/examples/Drawing/Chart/.src/FormData.form:83 +msgid "&Close" +msgstr "Закрыть" + +#: app/examples/Drawing/Chart/.src/FormChart.form:6 +msgid "Example to make a Bar Chart" +msgstr "Пример создания гистограммы" + +#: app/examples/Drawing/Chart/.src/FormChart.form:24 +msgid "&About" +msgstr "О программе" + +#: app/examples/Drawing/Chart/.src/FormChart.form:30 +msgid "(%)" +msgstr "(%)" + +#: app/examples/Drawing/Chart/.src/FormData.form:5 +msgid "Chart example" +msgstr "Пример диаграммы" + +#: app/examples/Drawing/Chart/.src/FormData.form:9 +msgid "Data 1" +msgstr "№ 1" + +#: app/examples/Drawing/Chart/.src/FormData.form:13 +msgid "Data 2" +msgstr "№ 2" + +#: app/examples/Drawing/Chart/.src/FormData.form:17 +msgid "Data 3" +msgstr "№ 3" + +#: app/examples/Drawing/Chart/.src/FormData.form:21 +msgid "Data 4" +msgstr "№ 4" + +#: app/examples/Drawing/Chart/.src/FormData.form:25 +msgid "Data 5" +msgstr "№ 5" + +#: app/examples/Drawing/Chart/.src/FormData.form:29 +msgid "Data 6" +msgstr "№ 6" + +#: app/examples/Drawing/Chart/.src/FormData.form:33 +msgid "Data 7" +msgstr "№ 7" + +#: app/examples/Drawing/Chart/.src/FormData.form:37 +msgid "Data 8" +msgstr "№ 8" + +#: app/examples/Drawing/Chart/.src/FormData.form:41 +msgid "Data 9" +msgstr "№ 9" + +#: app/examples/Drawing/Chart/.src/FormData.form:45 +msgid "Data 10" +msgstr "№ 10" + +#: app/examples/Drawing/Chart/.src/FormData.form:79 +msgid "&Draw it" +msgstr "Рисовать это" + +#: app/examples/Drawing/Chart/.src/FormData.form:87 +msgid "&Random" +msgstr "Случайно" + diff --git a/app/examples/Drawing/Chart/.project b/app/examples/Drawing/Chart/.project new file mode 100644 index 00000000..d8073a77 --- /dev/null +++ b/app/examples/Drawing/Chart/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Bar Chart +Description="Example to make bar chart" +Startup=FormData +Icon=graph.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence diff --git a/app/examples/Drawing/Chart/.src/FormChart.class b/app/examples/Drawing/Chart/.src/FormChart.class new file mode 100644 index 00000000..a78f456f --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormChart.class @@ -0,0 +1,112 @@ +' Gambas class file + +' ====================================== +' This example is to make a bar chart +' using DrawingArea +' may be it can help you to make a chart +' if you have any question you can send to +' yudi@kecoak.or.id +' Thank You + + +Public total As Integer +Public value As Float[] + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub _new() + + Me.Center + +End + + +Public Sub Form_Open() + + Draw_Chart + +End + +Public Sub Draw_Chart() + + Dim i As Integer + + Dim skala_1 As Integer + Dim skala_2 As Integer + + Dim distance_x As Float + Dim distance_y As Float + + Dim width_draw As Integer + Dim tot As Integer + Dim colors As Integer[] + Dim bottom As Integer + Dim sumdata As Integer + + colors = New Integer[] + colors.Resize(total + 1) + + For i = 1 To total + sumdata = sumdata + value[i] + Next + + If sumdata = 0 Then sumdata = 1 + + For i = 1 To total + value[i] = (value[i] / sumdata) * 10 + Next + + drwchart.Clear + + draw.Begin(drwchart) + + skala_1 = drwchart.ClientH / 11 + distance_y = drwchart.ClientH - skala_1 + bottom = distance_y + 8 + + For i = 0 To 100 Step 10 + draw.Foreground = color.black + draw.Text(i, 0, distance_y) + draw.Line(25, distance_y + 8, drwchart.clientw, distance_y + 8) + distance_y = distance_y - skala_1 + Next + + draw.Line(30, 0, 30, drwchart.ClientH) + + skala_2 = (drwchart.ClientW - 30) \ 10 + distance_x = skala_2 + 30 + width_draw = skala_2 / 2 + + For i = 1 To Total + draw.LineWidth = 1 + draw.Foreground = color.Black + draw.Text(i, distance_x - (width_draw / 2) - 16, drwchart.ClientH - drwChart.Font.Height, 32, drwChart.Font.Height, Align.Center) + draw.Line(distance_x - (width_draw / 2), 0, distance_x - (width_draw / 2), drwchart.ClientH - skala_1 + 8) + draw.LineWidth = width_draw + draw.Foreground = color.RGB(i * 100, i * 10, i * 50) + colors[i] = draw.Foreground + tot = skala_1 * value[i] + skala_1 - 8 + draw.Line(distance_x - (width_draw / 2), bottom, distance_x - (width_draw / 2), drwchart.ClientH - tot) + distance_x = distance_x + skala_2 + Next + + Draw.End + +End + +Public Sub btnAbout_Click() + + Dim i As String + i = ("

Example to make bar chart

\n") + i = i & ("This example has made by : ") & Chr(10) + i = i & " Yudi Astira" & Chr(10) + i = i & " yudi@kecoak.or.id" & Chr(10) + i = i & " necrose #hdteam on Dal.Net" & Chr(10) + i = i & ("Thank You") + message.Info(i, ("&Close")) + +End diff --git a/app/examples/Drawing/Chart/.src/FormChart.form b/app/examples/Drawing/Chart/.src/FormChart.form new file mode 100644 index 00000000..241f2a5d --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormChart.form @@ -0,0 +1,32 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,72,51) + Background = &HFFEFBF& + Text = ("Example to make a Bar Chart") + Resizable = False + { bg PictureBox + MoveScaled(1,1,70.1429,43.1429) + Background = &HFFFFFF& + Border = Border.Plain + } + { drwChart DrawingArea + MoveScaled(2,2,69,41) + Background = &HFFFFFF& + Cached = True + } + { btnClose Button + MoveScaled(50,45,21,5) + Text = ("&Close") + } + { btnAbout Button + MoveScaled(1,45,21,5) + Text = ("&About") + Picture = Picture["graph.png"] + } + { TextLabel1 TextLabel + MoveScaled(2,2,4,3.5714) + Background = &HFFFFFF& + Text = ("(%)") + } +} diff --git a/app/examples/Drawing/Chart/.src/FormData.class b/app/examples/Drawing/Chart/.src/FormData.class new file mode 100644 index 00000000..971cb001 --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormData.class @@ -0,0 +1,43 @@ +' Gambas class file + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub btnDraw_Click() + Dim total As Integer + total = 10 + With FormChart + .value = New Float[] + .total = total + .value.Resize(total + 1) + Try .value[1] = Val(textbox1.Text) + Try .value[2] = Val(textbox2.Text) + Try .value[3] = Val(textbox3.Text) + Try .value[4] = Val(textbox4.Text) + Try .value[5] = Val(textbox5.Text) + Try .value[6] = Val(textbox6.Text) + Try .value[7] = Val(textbox7.Text) + Try .value[8] = Val(textbox8.Text) + Try .value[9] = Val(textbox9.Text) + Try .value[10] = Val(textbox10.Text) + .Show + End With + +End + +Public Sub btnRandom_Click() + + Dim hCtrl As Control + + For Each hCtrl In Me.Children + + If hCtrl Is TextBox Then + TextBox(hCtrl).Text = CStr(Int(Exp(Rnd(Log(2), 6)))) + Endif + + Next + +End diff --git a/app/examples/Drawing/Chart/.src/FormData.form b/app/examples/Drawing/Chart/.src/FormData.form new file mode 100644 index 00000000..3a003188 --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormData.form @@ -0,0 +1,89 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(29.7143,16.1429,50,53) + Text = ("Chart example") + Resizable = False + { TextLabel1 TextLabel + MoveScaled(3,2,6.7143,3.1429) + Text = ("Data 1") + } + { TextLabel2 TextLabel + MoveScaled(3,7,6.7143,3.1429) + Text = ("Data 2") + } + { TextLabel3 TextLabel + MoveScaled(3,12,6.7143,3.1429) + Text = ("Data 3") + } + { TextLabel4 TextLabel + MoveScaled(3,17,6.7143,3.1429) + Text = ("Data 4") + } + { TextLabel5 TextLabel + MoveScaled(3,22,6.7143,3.1429) + Text = ("Data 5") + } + { TextLabel6 TextLabel + MoveScaled(3,27,6.7143,3.1429) + Text = ("Data 6") + } + { TextLabel7 TextLabel + MoveScaled(3,32,6.7143,3.1429) + Text = ("Data 7") + } + { TextLabel8 TextLabel + MoveScaled(3,37,6.7143,3.1429) + Text = ("Data 8") + } + { TextLabel9 TextLabel + MoveScaled(3,42,6.7143,3.1429) + Text = ("Data 9") + } + { TextLabel10 TextLabel + MoveScaled(3,47,9,3) + Text = ("Data 10") + } + { TextBox1 TextBox + MoveScaled(13,2,14,4) + } + { TextBox2 TextBox + MoveScaled(13,7,14,4) + } + { TextBox3 TextBox + MoveScaled(13,12,14,4) + } + { TextBox4 TextBox + MoveScaled(13,17,14,4) + } + { TextBox5 TextBox + MoveScaled(13,22,14,4) + } + { TextBox6 TextBox + MoveScaled(13,27,14,4) + } + { TextBox7 TextBox + MoveScaled(13,32,14,4) + } + { TextBox8 TextBox + MoveScaled(13,37,14,4) + } + { TextBox9 TextBox + MoveScaled(13,42,14,4) + } + { TextBox10 TextBox + MoveScaled(13,47,14,4) + } + { btnDraw Button + MoveScaled(30,2,19,4) + Text = ("&Draw it") + } + { btnClose Button + MoveScaled(30,12,19,4) + Text = ("&Close") + } + { btnRandom Button + MoveScaled(30,7,19,4) + Text = ("&Random") + } +} diff --git a/app/examples/Drawing/Chart/graph.png b/app/examples/Drawing/Chart/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..dfa033e005c085fcbae902f0900eb747abae34f4 GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv=>VS)S0MfW|9>EP;J^Ve<3B?K z1Calp;s236#?e3trjj7PUA|@^hjBG|MB2#)B4;(#u zQ-X&_LCeL-**Rd#1EGS#LKi^+PjByoN(@_AGh8z=3@&tS*^)9rSLx|fRgI_u&K)~; zEZ{0B*^(g6a4RS-j%Ul##fv4cip_~SA+TVwu9t>GBcrg!0wo>>hC9aP7QZ8UCj+f# N@O1TaS?83{1OT}0NqhhR literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/.directory b/app/examples/Drawing/Clock/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Clock/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Clock/.icon.png b/app/examples/Drawing/Clock/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..273c0703b97d94a5a105c6a5c20f9824f26e6697 GIT binary patch literal 4367 zcmV+q5%BJbP)x8(&~U%xN&hIN?Ze2)WoKyID5LZnC*H*}_#&R4U1C z!fw?rTlohFED1^MWFe`L#KtxxV6d@;Z_9^n$+|VtNSfo#yzc!&hb7r2wrou7Z&g># z%zLk2f4}c{b-y0LWrm{l+pxxfvABJ;gttF@LH)`1o6z~7_<`*JWTpFNZXQhf2ga=v_D1)u-^ zK{lT}aZdO8s}f+=-|fU24{M<~+t0V|FXyfm`D|-|pS}vQ#Hn)=NkgA23FMcsdPyFS zJv5uPo+P(?>nL^ggCPBvmjJWB@D|oWX1b5RzjHc&duK6wyWx9pLhlfyyi@14xAst1 zQ_u8l!E^tzh|1D*zVl2Ao8A}#oa4>XX94f4&8l=0xL#r)|1 zqI~qbO(eV&r~n9oGJcXWZR!G8As$&)an9rc(v0N;yXw`e8#eZ=d3=4|+kfeT%75I0 zwP3B~<^|b&`y-`fW&7FO1U21|G~?8br8si1m6MI_tiQ34uirP19krUZPrzWeN6PvE z141CIKp7v_N}S@`Nw&Rm{sJgzN<71PV|fKxyBoxocN1a$KkWkPU=3821^M>9C0so# zoj;$1cMgI(?umOI96#E{iJBG`l?8e7k*l#fz?Xgshg!iH$Vl^nP+&bkp^XGjAS|ft zMN<>NSV(#H{1pI}l-aCzn#7jxZ0uR{y-6ZeePch?Sn@L*zIk^s>y~HpPB%Pr7^10R zLZdj@GQhsQEd-^<_dh*@>*nY1+#YP59WVt^0Bj9fSI}g0JHKh?D~rg-?0vP_n8iy zSpaX?|E?qm%NYxUGYf#lyFd&0Ys1+>+UWv3`lw7K4gImd5(mCWh`PX|R`RHrKx~2l ztDov(+aI=z#>{@Au`A05x(6j3dQAiF_}|6q$M-*(8PxZKaK=_j?%NtAj`#sqA0#YzE?lT9PhCKateQa<~qdTDNKan@G`iyzM6sD3`EMsHaE~z zZkStn4fgFlNF{Ki5p-HQQeH|iCi2umMYU2gKG_1W7nTQ8Zk+bMMx63;uoma0yXZf8 zf|a=r4KuDo3B`#$FEeZL8q%{0FCBj|Q2?d9xRy%B)TH2GrzpVwt~5l<{2F%t=10_bA7$pUJCO3y6&+%d3rHnnT1gR`>;ZNPA>hS- z`#4|z>^eU2<7e3Ni=Xk`Uv0;oQHe@Rr@(ahLFQHraGKhKS0o9igfEk$eRUGH_#+a#V^shC#M zMwAo+P9^`d#ekE?_YiTCL`{O4wifQ5SAZ!f<+Xm>y42x$Jt> z6obErw&QhFlkBYpH$p0vBIY>|Dkn7a2K>1*&Pf1k5lYKW?I_!&q#U0igqaj9Hoy9F z?!R*>zdfo5$2^R12?&eC5KE>osT6ANa{lYlZ{ifsK+Il@zj!7l9>aa>_k`vxMPLba zoaBbJ4Z`Za>JaE%&_Qn15ScjqJLu4+zo}oyXK{rKiFcYmL zLI|d>ypyMY`512Xi_{BEzF+aZ>mQ`Wbt&p<);p|Y})UAuO%ZOf0CeaF|4&O{NEWJ~~Kkus$_r1gsqB@s#qloCiO zgp|W;0!_7hsVLLDS=-946U``pkW{1xZ?K<~lg5r?O$3tz1bhyj8>91NGyj=0hlLGI ze6*pCuC_3pRx_>fIBP!qFr)pRZ_A9ljZSo8M}ku#k-&9bGBY#D%F4p?Jd80YrC7Og zB{wXcMcdoYpP?@?##2dz^@|R@;SMMM#MZbwYL(VnVI-v71fdMeyrs9YaqAA;{!XOQ zNF)+t!M3WZikg~Vlk96GEw^Hv1=z831h^Y+ zbK)96OA!-N1V(%VPY4o;D1l@b#>}E8s}#|Dm_%1A$*_-5<#GmU4-v|r#Jc^ z)D}XZwPxkYmFzsWgJ9l_2{B+)00}V|c`u+OF(C*(^O5gT*}RW2hH z=_PXP9jr-FSW-^zuRd;0|uA4Nz;2)pj6b;N~na*g6dsi*M7(*lyA)r!>pXjNPVhF$r z;^PD$I(nL5SS6oS*x)ou=TtJQ7y9D~gp_n_eu3%N+(gK;1oKO%i$zGARzkvcX-T*! zDHzC1ry$r(TU|9JvzJ|v0D(XNzu%AJIB2brQZjwW(wkjCU*ic5y|NwOjBZSwr_)wfPpl%FV0r+@aZpMj{2|8QRw~3;F=WTNfK(zrx@2mt z9V?2iUbKSONwI#_0)E%(a(wp=oU|&M4(y;e+)2IZC)V7C$}K`V4x@c2DcIZ6&64>` zdFjO$xHj2KnOA&X0=P~t`T6&MEG(YMbI<-9y>Bx)`S}|0Ect6Pb>DdbmLTgfT8YIzVeNKW^dgt8tV=)+W)z^74FdxM01OQcQCnL}M)|S{eMw=*xM?xY0<;oQ zGiCuojGs4G%&lUrmh5P4WR8yV`tf@5u3wAs1*xlRL%K z!eehk2ZvwyBQuJMh;}uQG={6@FF7X*h$J;?+8nB?Jlfma0SJXcD5WsQ5DW&9QjWO? zKr|Y~X3oH$J<-~f6k;?e%8?i#M`A$yluDo`wX#|BS-WrTKl3 zfZ{{9-^a4d5Q#%OvBn@q*8PQYa1ZX_>f#bs_`u(O5|LEY9NvvP6gllaXKU}YOA0Ek zqPVztOn{7x417KxsZtAcaalkr85^AodJ=J_ zb-L`lLT+l}lIC_9il}4a!E*6WwY6S!F zA#$W-)x0XE&%BDp;|Eb%o%4F2XW_MasLH9J7{=1#X!&cc#dTdeIy#8^i%6SyBSJZ+ z^)tZ&ggE5_jwCj^Jx?0cyG_<+%v(s-p4W*Nlww1<92b%!KY4=Btb2g@3zt(;Hj_Pj z-(>6SzacntA^yA~BB30PSW9g~1C_`2bL-j%&WZSV7~o(yNQRzGu0M=U^x!yg5{U#u zu4RY}yoy_K(sIw*hmdwG8EGbD0iwc;Jq;<|<@K=7=jWc=?&j%VJ%dxVl2oLR0-v8` z(nSbCW_BL8+`Nu!mad|%?g)GKy+u=32d*(p&nuw(rnQI*c>O%&`5B@JbP;4uBKQ}t z7GvC`w9&C^UOr~NHsFeH0K^ih+>~d|ZL9TV2CZZw;MctXA)o!>UGwA<8o*dH6iX_s zwIUk#l(B{@ZikEjDXg+W3L%6|cgzrsxIlkzxHjcv`=W`IjK^IyJnz2p6c9ov59<&5 zwdwEes~yn=0pvgb=jK28GID2oc677Ul_vmz5;o}$_SSyt#fQy0;j|Y9+JF;43y>H$2SkmK_A_h& zl5@!{mj~>K2<}LW0V&S#9{frnd@h1qJi=w4*o;3rm&J3k`Q=w;)79_t*e{y+V|5SF zmfdv!>2Xp+9q$>N4?Xll4j+%8eHmz95MLmh`7_~`Tb84hpdcr}JvUUG@!E^BTPfDE z1EE%Ps7dpK7hBo1qnqBY4tn=IMdZ*g-!tN)s3cNc|m4(k2fcSA1&2pJmQo)9QSo^Nm zU9|mb9WQ_Ol94?V3iC7Y>ja(`XXDeKWBpIJlb?U#f3Cjb@&Eq>z~RMrUB>_b002ov JPDHLkV1i|-R1^RJ literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/.icon/16.png b/app/examples/Drawing/Clock/.icon/16.png new file mode 100644 index 0000000000000000000000000000000000000000..b298e08375a4526c31c5182fa0986fc20aeb0dba GIT binary patch literal 949 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 QLjV8(07*qoM6N<$f~@Ra7ytkO literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/.lang/ca.po b/app/examples/Drawing/Clock/.lang/ca.po new file mode 100644 index 00000000..aaff886d --- /dev/null +++ b/app/examples/Drawing/Clock/.lang/ca.po @@ -0,0 +1,63 @@ +# Catalan translation of Clock +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Clock package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Clock\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:34+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FClock.class:224 +msgid "About" +msgstr "Quant a" + +#: .project:2 +msgid "A clock example made by Fabien Bodard." +msgstr "Un exemple de rellotge fet per en Fabien Bodard." + +#: FClock.class:212 +msgid "Always on top" +msgstr "Sempre per damunt" + +#: FClock.class:179 +msgid "central" +msgstr "central" + +#: .project:1 +msgid "Clock" +msgstr "Rellotge" + +#: FClock.class:184 +msgid "Clock 1" +msgstr "Rellotge 1" + +#: FClock.class:190 +msgid "Clock 2" +msgstr "Rellotge 2" + +#: FClock.class:196 +msgid "Clock 3" +msgstr "Rellotge 3" + +#: FClock.class:203 +msgid "Clock 4" +msgstr "Rellotge 4" + +#: FClock.class:232 +msgid "Quit" +msgstr "Surt" + +#: FClock.class:216 +msgid "Show Window" +msgstr "Mostra la finestra" + diff --git a/app/examples/Drawing/Clock/.lang/cs.po b/app/examples/Drawing/Clock/.lang/cs.po new file mode 100644 index 00000000..a881de9b --- /dev/null +++ b/app/examples/Drawing/Clock/.lang/cs.po @@ -0,0 +1,56 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Clock" +msgstr "Hodiny" + +#: .project:2 +msgid "A clock example made by Fabien Bodard." +msgstr "Příklad hodiny od Fabien Bodard." + +#: FClock.form:25 +msgid "central" +msgstr "centrální" + +#: FClock.form:30 +msgid "Clock 1" +msgstr "Hodiny 1" + +#: FClock.form:36 +msgid "Clock 2" +msgstr "Hodiny 2" + +#: FClock.form:42 +msgid "Clock 3" +msgstr "Hodiny 3" + +#: FClock.form:49 +msgid "Clock 4" +msgstr "Hodiny 4" + +#: FClock.form:57 +msgid "Always on top" +msgstr "Vždy nahoře" + +#: FClock.form:62 +msgid "Show Window" +msgstr "Ukaž okno" + +#: FClock.form:70 +msgid "About" +msgstr "O programu" + +#: FClock.form:77 +msgid "Quit" +msgstr "Ukončit" diff --git a/app/examples/Drawing/Clock/.lang/de.po b/app/examples/Drawing/Clock/.lang/de.po new file mode 100644 index 00000000..5a54a3bb --- /dev/null +++ b/app/examples/Drawing/Clock/.lang/de.po @@ -0,0 +1,57 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Clock" +msgstr "Uhr" + +#: .project:2 +msgid "A clock example made by Fabien Bodard." +msgstr "Ein Beispiel für eine Uhr von Fabien Bodard." + +#: FClock.form:25 +msgid "central" +msgstr "zentral" + +#: FClock.form:30 +msgid "Clock 1" +msgstr "Uhr 1" + +#: FClock.form:36 +msgid "Clock 2" +msgstr "Uhr 2" + +#: FClock.form:42 +msgid "Clock 3" +msgstr "Uhr 3" + +#: FClock.form:49 +msgid "Clock 4" +msgstr "Uhr 4" + +#: FClock.form:57 +msgid "Always on top" +msgstr "Immer im Vordergrund" + +#: FClock.form:62 +msgid "Show Window" +msgstr "Fenster zeigen" + +#: FClock.form:70 +msgid "About" +msgstr "Über" + +#: FClock.form:77 +msgid "Quit" +msgstr "Beenden" + diff --git a/app/examples/Drawing/Clock/.lang/es.po b/app/examples/Drawing/Clock/.lang/es.po new file mode 100644 index 00000000..30fc57cd --- /dev/null +++ b/app/examples/Drawing/Clock/.lang/es.po @@ -0,0 +1,53 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FClock.class:195 +msgid "central" +msgstr "central" + +#: FClock.class:200 +msgid "Clock 1" +msgstr "Reloj 1" + +#: FClock.class:206 +msgid "Clock 2" +msgstr "Reloj 2" + +#: FClock.class:212 +msgid "Clock 3" +msgstr "Reloj 3" + +#: FClock.class:219 +msgid "Clock 4" +msgstr "Reloj 4" + +#: FClock.class:228 +msgid "Always on top" +msgstr "Siempre al frente" + +#: FClock.class:233 +msgid "Show Window" +msgstr "Mostrar ventana" + +#: FClock.class:242 +msgid "About" +msgstr "Acerca de" + +#: FClock.class:250 +msgid "Quit" +msgstr "Salir" + +#~ msgid "Clock" +#~ msgstr "Reloj" + +#~ msgid "A clock example made by Fabien Bodard." +#~ msgstr "Un ejemplo de reloj hecho por Fabien Bodard." diff --git a/app/examples/Drawing/Clock/.lang/ru.po b/app/examples/Drawing/Clock/.lang/ru.po new file mode 100644 index 00000000..9bc18f6e --- /dev/null +++ b/app/examples/Drawing/Clock/.lang/ru.po @@ -0,0 +1,84 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/Clock/.project:22 +msgid "Clock" +msgstr "Часы" + +#: app/examples/Drawing/Clock/.project:23 +msgid "A clock example made by Fabien Bodard." +msgstr "Пример часов, сделанный Фабьеном Бодаром." + +#: app/examples/Drawing/Clock/.src/FClock.class:137 +msgid "" +"This example was made by Fabien BODARD\n" +"and was optimized by Benoît MINISINI\n" +"\n" +"Note that the 3rd clock is Microsoft copyrighted.\n" +"You will find it on the future version of windows." +msgstr "" +"Этот пример был сделан Фабьен Бодард\n" +"и был оптимизирован Бенуа Минисини\n" +"\n" +"Обратите внимание, что третьи часы принадлежат Microsoft.\n" +"Вы найдёте их в будущей версии Windows." + +#: app/examples/Drawing/Clock/.src/FClock.form:9 +msgid "central" +msgstr "центральный" + +#: app/examples/Drawing/Clock/.src/FClock.form:13 +msgid "Clock 1" +msgstr "Часы 1" + +#: app/examples/Drawing/Clock/.src/FClock.form:18 +msgid "Clock 2" +msgstr "Часы 2" + +#: app/examples/Drawing/Clock/.src/FClock.form:23 +msgid "Clock 3" +msgstr "Часы 3" + +#: app/examples/Drawing/Clock/.src/FClock.form:29 +msgid "Clock 4" +msgstr "Часы 4" + +#: app/examples/Drawing/Clock/.src/FClock.form:35 +msgid "Always on top" +msgstr "Всегда сверху" + +#: app/examples/Drawing/Clock/.src/FClock.form:39 +msgid "Show Window" +msgstr "Показать окно" + +#: app/examples/Drawing/Clock/.src/FClock.form:45 +msgid "About" +msgstr "О программе" + +#: app/examples/Drawing/Clock/.src/FClock.form:50 +msgid "Quit" +msgstr "Выход" + diff --git a/app/examples/Drawing/Clock/.project b/app/examples/Drawing/Clock/.project new file mode 100644 index 00000000..265c5cb9 --- /dev/null +++ b/app/examples/Drawing/Clock/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=Clock +Startup=FClock +Icon=img/clock_bg_big4.png +Version=3.8.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Description="A clock example made by Fabien Bodard." +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Prefix=1 +Packager=1 +Systems=mandrake diff --git a/app/examples/Drawing/Clock/.src/FClock.class b/app/examples/Drawing/Clock/.src/FClock.class new file mode 100644 index 00000000..461dcff5 --- /dev/null +++ b/app/examples/Drawing/Clock/.src/FClock.class @@ -0,0 +1,170 @@ +' Gambas class file + +Private picHour As Image +Private picMinute As Image +Private picSecond As Image +Private Clock As Image +Private Buffer As Picture +Private HE As Integer +Private WI As Integer +'PRIVATE BackPicture AS String +Private $MX As Integer +Private $MY As Integer +Private $hMenu As Menu + + +Public Sub DrawTime() + + Dim tmpImg As Image + 'Dim hPict As Picture + Dim hPict As Image + Dim I As Integer + + WI = Clock.Width + HE = Clock.Height + + ' hPict = Clock.Picture + ' Draw.Begin(hPict) + ' tmpImg = picHour.Rotate(- Hour(Now) * Pi(2 / 12)) + ' Draw.Image(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + ' tmpImg = picMinute.Rotate(- Minute(Now) * Pi(2 / 60)) + ' Draw.Image(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + ' tmpImg = picSecond.Rotate(- Second(Now) * Pi(2 / 60)) + ' Draw.Image(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + ' Draw.End + ' + ' Me.Picture = hPict + + 'hPict = New Image(Clock.W, Clock.H, Color.Transparent) + 'hPict.Draw(Clock, 0, 0) + hPict = Clock.Copy() + + 'Draw.Begin(hPict) + 'tmpImg = picHour.Rotate(- Hour(Now) * Pi(2 / 12)) + tmpImg = picHour.Rotate(-((Hour(Now) * 60) + Minute(Now)) * Pi(1 / 360)) + hPict.PaintImage(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + tmpImg = picMinute.Rotate(-Minute(Now) * Pi(2 / 60)) + hPict.PaintImage(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + tmpImg = picSecond.Rotate(-Second(Now) * Pi(2 / 60)) + hPict.PaintImage(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + 'Draw.End + + Me.Picture = hPict.Picture + 'Me.Mask = Not mnuShowWindow.Value + +End + +Private Sub SetClock(iClock As Integer) + + Dim hImage As Image + Dim hBuffer As Image + Dim hMenu As Menu + Dim X, Y As Integer + Dim iColor As Integer + Dim iGray As Integer + + hImage = Image.Load("img/clock_bg_big" & iClock & ".png") + + ' hBuffer = NEW Image(hImage.Width + 2, hImage.Height + 2, TRUE) + ' hBuffer.Draw(hImage, 1, 0) + ' hBuffer.Draw(hImage, 1, 2) + ' hBuffer.Draw(hImage, 0, 1) + ' hBuffer.Draw(hImage, 2, 1) + ' + ' FOR X = 0 TO hImage.Width - 1 + ' FOR Y = 0 TO hImage.Height - 1 + ' iColor = hBuffer[X, Y] + ' iGray = &H80 'Color[iColor].Value + ' iColor = Color.RGB(iGray, iGray, iGray, Color[iColor].Alpha) + ' hBuffer[X, Y] = iColor + ' NEXT + ' NEXT + ' + ' hBuffer.Draw(hImage, 1, 1) + ' hImage = hBuffer + + Clock = hImage '.Picture + DrawTime + + For Each hMenu In mnuPopup.Children + If hMenu.Tag Then + hMenu.Checked = Val(hMenu.Tag) = iClock + Endif + Next + +End + + +Public Sub Form_Open() + + picMinute = Image.Load("img/arrow_min.png") + picHour = Image.Load("img/arrow_hour.png") + picSecond = Image.Load("img/arrow_sec.png") + + SetClock(3) + Timer1.Enabled = True + +End + +Public Sub Timer1_Timer() + + DrawTime() + +End + + +Public Sub Form_Menu() + + mnuPopup.Popup + +End + +Public Sub mnuClock_Click() + + SetClock(Val(Last.Tag)) + +End + + +Public Sub mnuQuit_Click() + + Me.Close + +End + +Public Sub mnuAbout_Click() + + Message(("This example was made by Fabien BODARD\nand was optimized by Benoît MINISINI\n\nNote that the 3rd clock is Microsoft copyrighted.\nYou will find it on the future version of windows.")) + +End + +Public Sub Form_MouseDown() + + $MX = Mouse.ScreenX - Me.X + $MY = Mouse.ScreenY - Me.Y + +End +' +Public Sub Form_MouseMove() + + If Mouse.Left Then Me.Move(Mouse.ScreenX - $MX, Mouse.ScreenY - $MY) + +End + +Public Sub mnuOntop_Click() + + 'mnuOnTop.Checked = Not mnuOntop.Checked + Me.TopOnly = Not Me.TopOnly + +End + + +Public Sub mnuShowWindow_Click() + + 'mnuShowWindow.Checked = Not mnuShowWindow.Checked + 'ME.Border = If(mnuShowWindow.Checked, Window.Fixed, Window.None) + Me.Mask = Not mnuShowWindow.Value + Me.SkipTaskbar = Not mnuShowWindow.Value + Me.Border = mnuShowWindow.Value + +End diff --git a/app/examples/Drawing/Clock/.src/FClock.form b/app/examples/Drawing/Clock/.src/FClock.form new file mode 100644 index 00000000..798c6fb6 --- /dev/null +++ b/app/examples/Drawing/Clock/.src/FClock.form @@ -0,0 +1,55 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(80.1429,67,43,43) + Mask = True + Border = False + SkipTaskbar = True + { mnuPopup Menu + Text = ("central") + Visible = False + { mnuClock1 Menu mnuClock + Name = "mnuClock1" + Text = ("Clock 1") + Tag = "1" + } + { mnuClock2 Menu mnuClock + Name = "mnuClock2" + Text = ("Clock 2") + Tag = "2" + } + { mnuClock3 Menu mnuClock + Name = "mnuClock3" + Text = ("Clock 3") + Checked = True + Tag = "3" + } + { mnuClock4 Menu mnuClock + Name = "mnuClock4" + Text = ("Clock 4") + Tag = "4" + } + { mn2 Menu + } + { mnuOntop Menu + Text = ("Always on top") + Toggle = True + } + { mnuShowWindow Menu + Text = ("Show Window") + Toggle = True + } + { mnu1 Menu + } + { mnuAbout Menu + Text = ("About") + } + { mn Menu + } + { mnuQuit Menu + Text = ("Quit") + } + } + { Timer1 #Timer + } +} diff --git a/app/examples/Drawing/Clock/img/arrow_hour.png b/app/examples/Drawing/Clock/img/arrow_hour.png new file mode 100644 index 0000000000000000000000000000000000000000..ec7462df9d539033373b317298a4d4c236a140bd GIT binary patch literal 396 zcmeAS@N?(olHy`uVBq!ia0vp^CxG}E2Mdt2jb=|}U| xd-_3~>}@wGCJn6edX4(dbMqKcy^NK&VUGO{6~WgZ6D5m5@}91KF6*2UngC$Cg9-ou literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/arrow_min.png b/app/examples/Drawing/Clock/img/arrow_min.png new file mode 100644 index 0000000000000000000000000000000000000000..af461315674bbc32c6b0b9dec173040777d67ea9 GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^CxG}E2Mdt2jb=|}U|`Jlba4!+xb^n#Lf=+HiMEUU zO-d})=1J@-(?kU{(h`N$b~M>KFiV}4|Fy(2y=i;prI0c#vTtUn4*_E?q zPQQNss`tNlSzj5R|9D&Um7z{p#chIwV-icJ;Gkoq^}m0%?zxHa_00$E+p6zOXx}+| zcV&d_V~%Q;$~l+kJS+U#qx?qjN$tL8+4sWFTO5+hKk@m_xig<%NxI)qK2dou?b)8H sV}jo}KH16}f8JY#2oPAnVA2Q5>KHr>1dVDrS(rhxp00i_>zopr0GpYIG5`Po literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/arrow_sec.png b/app/examples/Drawing/Clock/img/arrow_sec.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c42ad358eba98bf9d357e753223a6f0ada4260 GIT binary patch literal 1253 zcmeAS@N?(olHy`uVBq!ia0vp^e}VWn2Q!eIwkhc{0|VpH0G|+7AU9?E|4}rgL*UYz zN8-SUoL&;-7tFxK&Lb!;qo}5BU~1*y>g694!WzaA!5zgH6DN|8B$Xb5TR9bFPXT(c<0a*Adv&6W%u=cG8sOX=yWO=ggg7v~Y37(&Z}~SFdef zzp;1A#BDpK@0zt|-+}{&mK`~^=ESK@XSbfexcl=&!JBu_ zKD_+&<^8uGUw{4i{jYxSHA@BtCJRp&$B>F!Z*Sgo>SGjfdnj-*W(r%qR;S5DkNt}( z9TY`USDAPUab>Exx`teTEoK$Xe!ps6!rXV|?k*maR6Jpb^}3=JL+K1Y2ib+p0TW9Y zUTMfSSh=w;SoDJ7N+D3DiO<3HGUEyfD+W<-_63h#Fof91Hh3*y4(Kdl(9*oXUiZm! zS3`+btDeHrdCRyxGGCT3YARoN?DtG^SAz)Gmn~CEr|BuAKEJ@ab*^@!zunR zYbqZq#X4y1a1o!t)%}7gsE`xLym-R{~Y~ zsmD6J(ptdnQT`q%^m{4L%-@W$4pkA3;uGXTfkN}*b~Svl02(##3y}A(3ds8+2~_Z} zNl#&K&SGv4=H?Q{%on1u4pQ$HaeF8)2J+U-)>G)+5A>|CEzpXyDnRA8{lzCNy9YGk z<#(XaVTM?TUlz{d6VlvYFkM=H9w>C0CDviqZ!hr)&z1v)4w=L{M8yEjJG~ibUg}gm zh1%IbyY7nvu8U@~X$boFyt=akR{0B_@vFaQ7m literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/clock_bg_big1.png b/app/examples/Drawing/Clock/img/clock_bg_big1.png new file mode 100644 index 0000000000000000000000000000000000000000..3001659b1c75dc56c6aac7aa1975eb9dac7525ba GIT binary patch literal 19642 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

QG{Eg$ zym-;#q~?l|xp~&a6~Z3tprT$QWN zpJRC9bYyw18kKfy=-3Nco#Tg7sP#uWtX3&s+7|`}48{pPKml4Z81wfWmZn&mUJM4w zAttun)$(-w_Cz^s9Z*%% z(6=7W&iFv$>&UQqvF}DTI@Y!e6~z!19_ANS`}Dg@A>GL(u1@XvX%6K#at(%9PE3y%dGTU&YU%~s z`}dn&t6nu)5@}A~Fqs{~wXXI?#Uhb>ILZQylsp`d2D`5;`ATO~E2#*+Egsc*($6Mz0{@MTPw?V~|i%32pjqn?ABV0kVmgll#3@xrtTPSo*K+ z1tntK&5wT*;xHN;3;3V+c$CN6?+8nF(&)bgJ%6p50>CvHRF*r?tT?rtRaaKQaumD9TU0puzuPZ?N3 zOm(%%Tf<~b>@DSYhAO#w=9LObOOp!wr)t?=)KM{3KjIMaeQ3(2M#^bH5pqIR_x@j; zjFdzWV9?gqjIy$U!Xo$7mG{DCPDkY2?GB8S^eHuH(A5uYSXlWGycgYH;xMX)_Yw}{ zm^}0f!Z8>hNSd1nHz@qFb-c2XpEpfUPDRBbB}EGj4`_B{cb#cq?GVw{9^<$V^5u== z>ZZF9hZPiFJr|y+=clIv{q5ZQrED%i>taZVs_+XJUcPP!0=Qplg{*>uaXqfkec=8_ zrPIJd7P1h~Btt1(i?A1)lqZr_N5tUJT+4Sjv>_JgGGps$03nfV-TCo}Nmc2E=;%Ad zREbSTI~;&zm27QipK=oc5Qu*CC{Pm#bu{g*Bheil)ywt7O$e4U_MvL0ng!wM>1lb{ zSNiYdKh_HQ?C$Q)e9L48D8|N#epFn10jEYka9;o9IP5kq zwcI8vJe^a!%*<@3P6lP6u_g-=)qh37LhXPn9^^O{_^}BI>h|{bNvtW08!Ruid6`7% z?B~KECxrbI)_HjfY8H~%SPzXGd}*H9Mw(Xl;SywLKH}D8ZtnQQye)BA*)RQL zK{$MAq<}(zY;>2-(&M|>+5K~Jb1G%{!<4B;!B4J&R9yn8{nPt@aGKa(1}w4_qS3!( z_wX$(B{Q`ZguXUgs>c=(zcVDyc8xFDk`AsWIXPg7Y!yj}h_EHbSBJWMvX!cX?ZRV# zQUuZL_~A5xA(y^<3A7T|Vdca(`7A)<^xO8H_phjfzjEQbyS5<2A*Uqb)|_hE+}wPs zJG!6?cmqFSfIK^})@-+GauRXEUWS8;WTg%t7gCw1khBb2T_4G&rYKDaB9^ z)S%O{4}@+GcV5H;o!uX*ATF~cneHZP#U*x~zA^XJ* z#hB$~&$oliT$!%Fm|z=dcWm+~!#7!y{a#vLc9b(de?_6xtZm(4E6OG>KhC4f&dv_e z6R;`J0M)Z3xYg)@2TPu>3wc_m+@mAVYH9&dvQVi)f5ytXMf{zaG#qfkWSZpUdwP6n z%y^c$dP;n0(^{5%t&5Aisoi=YKd=~#%RdZHL8|2buwTCI`pvU1HpVHRqT1x%`?S8S z_yN^;bq;dE$e2MiNd9DH>Cev2s@`8OMWK^JxTHo~5dn#M+0T>SCu>~L4q(4V9p zE~tCP6`0Mr6YX0Sh zU1X`?moixoW$naIpU@fF&or4@;Y@hx@!f}M5A~^Gl@W|!@EX7D9dlxm-4!wCTTD@{ zkz~Ze;~(qY2D%5Ml}^`WTYSF5 ziG~&}v>@WnR!fP2`Z6>sjj|}SN0G6yv5D#QM!4tJ*1jMZIA$j{R=mrd5+H!LsY=O% z%Esm9>TEpUDqU@`zgv2bTxOi&)UhqX9D(gTXw>hp{A^EQX4SbcBf_Icq zb@?0pcXbwgMnxC@{P}|j|L{S$LCZz=k9#WxO;bC4G;3Z+@NUx9mIp$xR?P37pS#4n z$UqHbI_ZB*67-KV4JpaIu13y_d_VA*@Z-_wCAY*y1qifladAnbhQ`KL#I~g{tn)O$ z5JGPlajIK}7$T1;qj9CCa-*5K`d;Fut{KR7%@tyw7ng>Rp;)+>HUm;%LbarvMySi|x?KUuG&}t7* z6MLb}&3!lcX4xCMiC*Z6EH8|1ZobtnjL~*UD&n5=mJL~^2jLF_R0s2uq^+#9ptE5PK#b4l0nMO5rc+I|FPf4g7xYez?B zNKI~Tb5SI;uc07-63oGQSI~Mn8YV#1-k!&7VhZTwU%%RljYULC zyIbTtJ&wPul*-9hmMn?Jxy;Oni)U&(zg%#7p7#8CcQ8AM2255L=-Y1Nx(dKDACbE) zd<8x~TL0aw)89YyE2aw2!Wx@=?2vWq&cd5JpsG>|3u`nsOG`_GCg)8-P8kfVUEIr; z%=1hOz{pn<1cmeaC-cZ;rP%x>Qy}>WdUqZ6_jCEhwPtUZmzV2CK@MOOtsSAyiK&TT zPzA{fmWGyHS0MaTXe_q)tCB&|d2!dL{y|C=b^q^o6vtmO1|9ETVbRT!Zf?;VCcwuv zB3T5D!s5z`+5cJzkAV`-lJy})=-s+CRr$#m)Yu>qMNFD1G!1G@-E$w6{UdR9U+S+i z`YRnhA*!6~3A8&wv-8ea71UJUk`2tvYKQw|F#H$Bhe^oDU_{A7Te^y8sdGs>Iy&=O zlpLtFjP>UO)U&|&FI*1K4o>qCY&jS%w_(|$(Z{85X5`(ZFTEP|_iHtHWDles%a5hCZT=)F`WRhwGqvgF0o@;YI zGc71B{mzjtH0jCc`K7Zfg(p(WGjfsw+U}qgz;$0@HeiZW&riOx0Eg(d(Y$hk0YYKh z&!rJRV5Ovr5xv{?>rr9tVrM5YVu(-3lC}g?sgPT65uh(?pN~_<7hwX$7FKMA01>a+ zg9q=1WT2Pkyytl11}VZ~77@+^Z9T^G(!N6c{vx#ZvpzKtoti26U|Zc>RUprR);Pl4 ztGhLZYNt9FsYMq<#bPJxg#Yn0G^q@h15ZvYQ3}cbKbSy?~+mXOzgwWlp z<0VrR0hzlGMWt!40P^HugG&9FP~<8wq~&3a{Ks{m?{cghUCDa>oL(|J=Y7yO;gtZ# zKVN*rO6O;8wXq-mIXFoAF=+|7^x2AC`+6J#lH ze1QD!9~hV^Pq^>w9M80}x@v1AEjUIjI29itq-acNsD4G)Mq0#?UsXsE93{)k9Ju54^{*d$(i zWSsG%<3*i$3l13oTBM-t`=)3tZ=Ei)m?_(C1khlk0 zDXi)`Gh(SosH|?A)T`}$Ge)ruI~!Pu5LTv(z3~f|F3lu{opJ!r94X=S`&C23-x=BU ziQ53PLMAHgslI2+5y9zs!XPybd!|*YVT%F_&$1W(QudGF?RNrR>re&ufB4Y z`31#)HA7}&XQ!7e{Bih=io^AI+n@y&51R(LaE%uauyXfKPa6NpLA~@LaL*BY z(;$;?qYB-`iwB)orv1x@D!ENptH!Zbi^7~x`ab!cZ=XVzK*tN(AFPzKZtE)pV9&$T zoxUxYFFZKPAV>mUxw-z}Zyc!B`c`1tDosn6L#oEs-jo{xl`HQiUy zpZ=v|z8-nwS3B%WTqYl`a?3Hcf!d*Z>JbB?n)dEh!^_bl6#PP_=jxP9&H_BCRg!6yU zkAkM#-@LS zKXbx~w@01IC}{fggr8U zA5EX1Y;+tn{tJ-@6WeMXPfjqkC6pvvV1di7&#i_q@b9}{LnL&ufBpIe4Lp37;7uGj ztVn8V+#2(Q7j6x7Ak3_+tnBRWZUMOgRzG^WYOmPp*RaDBd@?pcIQi7|-%-xLShvuf z7tA3qONAI=5B(S%j0W9J0Mm{Slb`n5iFd}5{CdMY8ECibHFmbQKR6EZm3ax8S2qPi z`vnCw?$nwD(dHtc@EE)$gQl`@*w)5qbO+9bZ4zZvEE*FRhlc|0lXl|xcU7HyE^Mu^ z^PLERkICm5?EiuBhPj@}8(g6T}I(@0J>Fi*Fs?u~#;zWRm;MlP;=6lrpvrq; z`eo4imQ4E}2;8G%V>sdWoBTF%SUja;7mHvV*$?+!jQoaRAh&@mC47~U=wD%40<;VC zo3Sctb`A~{3(H2o9n`CUEa4lVx`r0{Lts&RkESfYLu1ckyOIFM3IMKv{=xA|*+C8R zeQxfP zvLz818Pa39>S-uW(9|K=U!*0IknHktBdq@=I(cn_cAp?BTk81y=yNb~e2Vb1X< zy&5DP4(4iLO(88%)nQlYaCE2vn+*|yTRjCyN61L!~RWQ8G(CBYE)*MEir?i37O<6-~K6k2UTXi~pRsnfE_8iUv50wnmt z{Ldko7pyv2()|Yd`s{3ZmFKbw5=L=F#v#YM4?yqiPZ0{Oi`7-;ynK`2dxC0qqq{!MYuKdA?>(Kcx#{lww~h`oq`JR<^-us5m)Ku}q)djMEe?_I@(A~G=)348WsL_;hw7c@PNhc~n035Y zlVaCrJK2&oK>hjy5J^@RqfnxYh6eG^fr0+fQBqLK0~BTF=Kjio=EU@{K-uO$^F#`I z8kc3Ni(jl?rl)WDU1D|zUme^oVEe+^0S%c|CtH>Pi2P^Ip1tx}A#DMTVY%0W z4lI&n$+lVo)ZTU5-TVSorbb4z1G)iETQ$*D>FMc1eZ~_DPgoI~M+-SQcR+cSis1_w zC&yrp)S*ZNi{N;TS^Ik985?ZQiXG!SoOT3H2?tNdFyZ*QpM7 zpF+*-{O3#B{SI)YF|YIlBoy*M0c4rgcizXZjIbJfe_I0mi8=ha8#1$X%F4qGfCZcp zUS8hK?PNGV7cN{-DjKwgng*+`zf2AazdH67{b$s`1jEMer6vK#88#styh}1GF{;}i zbrSIN($}Xh(xXMlIO2@|+wKvPTi8*U&t?Tk?NthyW#GEXVMZv9@jgh@5s-_)$LQ_j z!zLv)r1cckYH-O{;Hd#)C9FQya|K2KPU^1U2_0kwm?%2-*T(J~YJk{zz5c)==Z!h6 z?T|&#ldGYRpFG(k=9tnV2dRxiKl?n%P%gaHwK%#8f#jXms524^&O*;5<hFEt1XiKp0W74MOgr>Cf>m{=}cP@Ep#J=Vu-CR10aTfnymUhAfPDU=?Mrl+le>$kD8LU3?!I5;}0I6JRK+}9#$iQeD$tvg;Wa;|i+ zu=x9FMFx=`bBc{MJ6@o81FtmEdoM(j0bH-qWbwqz9VQj<{O7{rV(pt~e!J=iIdLU0 zfv^>IUdqQpTqzsA+EkDZ+5ylFzRJ(XgN**<$&gGGwFH79cqn{p(-cEz?>%Y@0b%(O7xVU&E!xLxH4X*p|K!1FJi;Wmz4leEuJQE$X z%sDfr?8AiwPNwQ<~zmth{ zoWpR;`$L_|>Z|s+^ZV@}Oa~`bhU9G9l=~#G0K>I@LNp6N++zjcDMmzdb||158#ZePGS_n1oI zF#Jv>V#vA>fTq%Kuj-8=wmdT-37K#;c4EZoP}o^|JjQ}2uWU0|Bb3Dvbp5WiYmzyn z@dEUcxKl2N*e)DAZs@PV2d0hkZZ>_lsTv&I%i ziC1pBy1KsN$N6(muZs4&+x;L-j&|_Hem={Y2Js{p`|&Y3Ro8~0n~`i zH#mZ!vx6n<+iOu2n}o!m{r7Ri8DqtFby3S|0myxdM_sz1XteHkUr6Ty#_M`u8Uinb z-7gV1eBiRsGc#Mc)fa^U2S4_>15){}McBNw2?phgT`8=ksFg~w?*H?pcJy$8QyUf; z2)GyhoLCOyPpw~tl4=u|1OzC&E`RClm{TdTeFRtxRgs9pCEc)N3WN}KGLks>eY+<0 z!DtNbL&X2J=$3K1+`W7EZCTlMFRzO0Br@P`hI|YoVV98b`AgGqZM&00+JBQ3VVv6y zcN%OlCiS8hn_irpRz4koKp_6tFGC9#U@j-2=C%>y0%ty8A0@B3V^aCx4|R2?a{={7 zGLs*%TMMd{S{!(shJNq9GG6IYN-a9MXAWyxd+^{&nTi;oM~DsBv0T3%(_v-=%@3&0 z=${aRN)}Ljrq}pTFio3vIkqhT%nH_#zP7gK&y0=&;uz{=fVMH|-m~w!z|9er_K=mG zlbaiELn29Fr6GX=ci7y*;(RL0HTb2+2nrv;959>A33hv>IRQ2C6mH2Ey*5^$gasEW z*2(JE{Ia}UL|jrb@7=pDdrg_|T4ntXz+g~;=JdTOfqP#CH>TIF^~Vn+_&hzPS_q6p zU6)g7+;8UoUsLyAI%_CGur%SVo3!p+gPlOsz4q_*?ttum8{&5B$r~Y*pcVvY?88FX zP}c~JZ}UHYQWkaMTs2mYVgG-w_pkP;o8EvSihW-h72UVEUq+-A1x|lJt%-`!DmWnc z?f&6Gy6KHyKDGwX5MBaXe!$2?j*Ud>%B7s9~>}=&^04@`*;Z03Wc*>14cRD zZ}|!~K+`-0jrltg=|3VoHQn+5>R`z}7L$aIP)D`qCn^M9L%HA~#V%QrBs-PF4m$3! zk{YXZ)jtYi1-)pzFzg)wfuwKGYxQ^m(lrC6WP)}Ec{F%LaN^pj-OWIFj1}=lW!$8T z{ZG6<1;i8ckTGAHK+K5q&285XLX3kKB;pn$k8BQjCIO;JD0C~Xt?SCr=m1m2;EV^@(G61vrX=?>*IxSFs$7qk?h z_JjA;3Uk4ns%Wb$w!GY29N=EIp1%PR7S>k^Je*g8cZU2tV)GwnvA@UXhQ!6w%1*Vf zFx`$so1H+w?F+(_M<$=w+@ML;0D}j4?H^D~k%BzY)2FhkD!;y-2{d0o`*n78!K!8s ze4*Ca*d#b0PzPRq$AU2>vddNlSW)ne{`mQ`1(!qyHuisyE>RLp@6*)RmD!gG-!L*X z{B^bS$yH?Orq zYl>aFmYwN$>1Tj63q0TA+xS?2|3mO|326c(#Hvi76U>{|Eu;B=0?Gq5{AC|o9TEKu zz@I8ADjc}NaK#Y>03Lbd5eIl?vhE;T|R80)39t zJX2Km&*|y9qpk&M@L5^Dk^H;q>a^T#qjVjhK7bb3;{z!LmqYCr$V7n zAS^5lVPRoVDwRG-!RNW~c(zSM^xewJ%7d3;94sWR7$tosJo)64A|m=X0KX(6B``N~ z_}j-#y*TOO>kT~>L%)C<}5{V#_$sm3+4iWtTK-@x+V(;GXl$Cy$3RgWrI6U*rGh%YpF8K)&WdoNF zIWEPu=c(OO0G=SCSJ$mucQH8YD#nr_2!}uX;ST};*AvlwM06_vd5|1__Zf+3ClPH0 z@KIG&)fMN10YTGt!*wDGyzsco*AVGZK&wu{&wE*rUq76h;2!O6q zaya2~>g^?>e_VInb%z)G*W=FTeb9ED=?~l@)URq(zJ_Q*t;bEl>N1sD_AYHf`E;#l}S~ z27&~cBerbW5;94WTykZu&n2Q9BFX|Fy+q_N6VYiRY6H;fwmo&j4L3|I_A|PY5hTb0 z;ni1P6%tVzT!hJnYsqjZx#Gk;wnP*PfZKwI^Z>?*NKdZ#CXemp@qtO{&`Lzw>gwt% eVgb^XhW{TeJ<3EcSfv;M0000i=HK)R$Qr3C5j5{6Q#0i;1dy1ToPkOt}QZV-46 z|L66(M!$e_?sM;0d#$xYloTX!pO8I4K|#TlmJ(M6|Mon5upWcI3pI;UP*4nBN{frC zy8hiuc5|iZtUUMO`u1bkY=K^uZ0|>dlpgCMC3+VIYd#bqf`*MitTMoct0g;ZQ?z18JnHQ1G>3{aDA6E#NZ}pm?oe^p`wR#ipC=p(`W+su(6aXm!HPC$ zlmS`BvX#QJGaSv5uPXIk=O8dFcx_u}^ta+OA5>h3oqz!c`oy;v9;`#7iS-3y&*8xz zwwm;ti^En9%kFtRYfu0r$d``V=bzn$X7%vacWl4QkpEP&U|3KR94HC;25%2EaJn?e zYq0y2&{iDlC>~d{)cfTM>Vmo7>e|{nET}rl zTWlyvO&HN_xX~Q=*&ymG8F=sjJUG_(?Sl8sURbHOhEaeGrKr7wgBm>eTZFV7FNANN z-PrtDSmlxU+j)2_1LG<_@EtJwr!!wm1{h~sJ1z99KStNjVoY*uR zNepQ=uMBs*kd4^g-L-{wD1On$ff9Bv=TlPUq{qM=O;at)SzHHeAXk{;r#l zfcTwR4qgOG1T420$=sa0k)NN>N>(p|!~*Yhp%E z%#2;;4&Q0i^N$w{OaEAK^M5^tr6PWfWt3<5cLlnWZEu_96q8R#t(fvVKY{L}-<|cn zF4pX>Q^Y_s#SkqkD_h5broh#=d+|v`?9D2)${g%_`v*;s(5RXkSGPixSNhJ9*<)rE zo9%5_qd~phC9G|y?rm*tO8MjH+?U!r6LzPY?_scG`;(gW1dG|C`gzmx4{T(i&qF2n z`1!XsH(Shhf0dU%E3b_eGUQ`q_yU{$Y-5w1o0jr$I!fcGG`u=K2bkXH$G=&8cqhD! ze*O9G^hm<5zrP;?+OdFuyhWvrSOwoE7q8bpmj2?G>8CXH{55!{kXDW=V|qq$x@Sju95D=niOT!_T>&dz6Z^NvsD zbO;)D_OCrXQe_S&CDWFi++!-bYSOw^g+qJGE482D!7SEwTUoIPNVC^bC#jbanh3HpjHASwf1TSWFhJ^$gRYtgfw>8g_mqh-xQi z`k@D|BR4NEy{wEdzjL`Ng)LrgYPdx%6tX*rrcD~ens}epMNb}fV z^bMkh`mzxMC&sd0U03|HbR{b-KNOM8jh30w(ba`SCcq8hh6|_W5fUc>6zciJvNpv= zM^>GNm4@Y7rCM)1ueVIc#wL7Wkw|3QmFy32!r>e)vr4k7Y6&r0B?#F-3x|=uUQX0U z_57H;JjTAhFcod>9nJjHH~(g|v|qh`lUZ2ErL@D#f2aZN@VdXdXjc4&tC#&q^yy=< zuaIS~j|Mb>cfBfErJMgUVVMeTwZZR1YV;IkWS;c)_F_E3#Fxil*?{#bRQDo=&3^qh z>oD7OWXcMS^Khs548wfU`rlbzQBgVwS!v{-owHw=nY(~9@c=BN?-d*}4;Wh>pEt*z~HQ^d8kU$4FF3rzC9I$~~uz;*6t9frok zyboTy=n@3SYzKWH_RD1?(x~s$kR+u!x?|P&T^k+Zqp~^U=g*%v+uJd%p4)7=4+wI; zeLFffD<><9qbot@TrV(wo^`XShzC_Q<23L4{rj_p1v)lu1}&TM^AfE$RBqBz%;?GW zQXlA55kW!EsHr94aN3X%&~*{#<3179d+xb~l`&#L_zt^M?=89Okv@_h{A$F+6&0Kw z6@|?QaI9gh*~{JXA!~{~Ax^^Y+J4WUKIS^~jj+n@km`9au){>cRO>s15YKeTRbyn3 zPe8Orh}uf^M1(F2OwL$j)z*HvTNwLZ`g=+eqgNM$n2b!uwT7xuD9N^ws!>nTR?0RN zPrUHGoXotZ1Fj?<{{+pIn~uw#6Ux+{vn971*{90Ctce3<@RD->urNHnzx@ z*wa-z^QI|opL7&zyXYT6gYhr{Vu<=wKOLFyW6-6jCP8}Z@!Icv^?YerSvo;M|HVa` zRI-j)qx7t-AAkR9@$vC(7LSv_A;iS*bS#+&2#%v)GRYl`{_S-09!TJ#;I+rV4*A#M zSyaDY2PIt@^swS4PsVGjsH#%dUl4I{h+@CyVf>0AF8R)&ryus3BdVl?HFaK6F+2nv z6VLW=QIx5XBuRXC(`@6g6@6;&JTibIk`{U#A@ z2q=uq%we0GW)%soYBGkwCpHCcVhq7BHeTV>1MiD^v8wTpEJNm%f5yhdK><2*asuII z6bq|<&&b6NkxfIE@b^^gYib=*%5_>`2%6oT#;iQ`{wugspF_)lQW zUXH0KdM!%2ym0#d8pKSXwy$Xb#QeFQ*bv{14s*?=@-87KH#Zt#`KQ73oEpt74FAZC z%F2L*Z8C+4bK`EIS)tnZx6!vhq1XGuR^hx z!z-Q`A6biu(ZvuGN5{q*mG(?*BVlRd=DnQHdj@-zXz7 z^?q#;C0kZeQPI-`kFCAE&QI0vHI=a7uBla0&fDADQ}r$^CMMkv4Q^jEsjI6iGB&pR zbZb;vTIL7+J4PpwUQ{I&75{NV(c;Od=xA{Om=@gXGPh!-3e}$x5Qw^Kkv)3!NL=Eb zVd<$MCt2zAE@w3-$V)axMn){WC$x-=%AZ&6K;Mi0L6!0A7evG(y!Ru6XRe134A)y<~(eu5TW-na{-ps3?~hqU!|l2D)E?mm%LllgcZzl`a!$cY9@pe$A4s{b+A6Hk05))G(POG` zF)=}M#+Fs3!Eh1OOc=5s< z+TqYL&<>R}RwbIeKkWXMMQ+FknqN0t3M)O>vI00DM%u})4Ke< zuI@lc8N0i?J5fga)D(|>SviRLvw0D3^X^+dY83lR#x6=MC#RRw+GUezCDAkDq$FLZ zpGSL#hBUHriW`of=zG`30|Y?jx`^uM@6VPvusd7oZ_{-3xEBgxb#_)(`|!moc-T0? zf&~j|?X_Fl-6hw?^+~1R`}gkmy;0r6XUm{Xs5GcW+eX=9z)@=nH9N zPGMnMQxVA79B7P>ed48l>fPtIv!&J5)d=qYaHDb)&(EFRJ>IBlXxLpI8X%Mm*qu)P zS`w$Q@rLmI75;0D^FnD4cez=wi@7A8_hB&UO}JR? zQXqNFDpK|_GY87*Vc?G+Bmlj>dd*+-ia}OZ7W6MV4vr{LLVxe@b(&WmuC251x^6}= z>HUBukdd32n;!>J6m4*0Lz#U}hb0wjU1C&({CIkfuvsVd?LqR`{T{me*7muR2E)Aa zYnvaujJ=#FAn`TASFb!it4m@VK<9CD=-ap9FFHGaWoJKI?)+t3(QCK3h2ao&CI6#$u!Oiih zU#aNbyLY9ITguAFsNtQnhMWC*(1A)Vr`d=8Nk&o>VPRuKIXG|tST^boBOoD>u(f3k z!lQ)ekG~@Cxuz3h4(soiGqPgQOfarPK=3fZPo$0rQzWCz0;VluRAa~)3gqnyZZXI zOoECy-sCYdDvJ01-$_VVSXOB%fvgQnOiYZbs%k_;gzw-xj)Q(-PR{7wM^S7}E~8UZ z!4QBKu-rX71glVP{*XR}2j`xw4`|n${3hw0tP97;{!TMIJUqnl{n3>lJ*n;1_+*-L zu2L=rD9GrFxxU+ZGUq>{|E!cG$;-$YPxQxkyo8;u?PYprf2Dwn=;%;rS6iFh-`y6} ze0ns@#zuAFeKsB|aIss?q*>!j-sIvhNp&}7%Jca#qhh9VWI}?Bo;9;XM&yqP^}6~x z9?#2?c5>PD4Lqrsg#LaeTH3bUoE$LBwVfJFS#g6y-;T$~k-&d}QBh;(*7Li;QZ%F^ zZKI}L-`;cMIG^Vr{eT7`lA*4mswxh~-br>#b)1`vgAMW8h8kQvyiXE#Y#*kl`xstg zQSc&I+|HWU?d{qFamU8SY=6Ib`?~GX#u}X@-pQgrJ~9c}Cg`%T!aYPS*Uk2K)%G1| zc+3W+^n7Z5MJD(^8+40Rk`tGmf*2@L8IIFP)zf3?->s@^Gx8$t0m}J(P`7449 z4*j-PQ2yx|6~WSvK@Iz-7wUpPibabGUXVm7#1GK26rS$ECfUuQSzBMN7U6JCA(^otTo!oVjzoyxDDN3$IEYd07#oSj_kF6LZD zl@>${3{bli=x)vr#PY`@A|MAEwe9k6H5)Pg)br_uS@2_I9$>+=<@(`LJ4i85Oa(@Q zw$6-AP2Q_Jqm|bNEiQiKBpdMcZb*`3*8QDIZi;redy#M$c*v%)ORl5V}FnaU^0%i{vHl9!4p#^AYk7n)P2%S|IA zBbOY&Gi(&j6B84Qi;F)si(R9z3V{6j^$VOHZesH2lS484T3=t^sU}AtA`lrHD{Epx zKc_oamfic4#$ArVCsRB2;Yn`5gUH@`_P_tkpSn0T^nSUx_Zr{&=7rlMm57MWWYDLE z%>~wWx$|R?NWrooJQ@au!1S$AY-g$3KzET1UhI%i^0#G2{`jt-6$?C*AtdLv{0L%O z;A%C>n`=>XN)5OAsf~#=vIB>up#{K?o{dzWeT}=hIbF-yn#jC7a}+SNbY#Zr!IdI5 zZrBn%E{Cq~l|ZZ`#KU{VJv{20M+zCB;@}&Eo*&-AP^m>J_)!-K>{(@-Cn!|DetNYU zw`7>I|KwX{tZqH!4la%0o6s$E$Y)l23SM_acB*$mar0e*oc|ZMll9t6e$P$F{q&8bUp{XAO6IN{zblyl;2(Pws9GI6!zYH~+J5KAtr9?-?4( z&h8fX@)F$Jqf}K_Mi$bs4i7-(ub~%DVf^F&jNV^jKpMGnj&|ZXt-%Kjvb{=PrslqalWe zg=L7N5#KFOm63!+D7<_~h&;l~EXQ;p(PDFV_pnDcf7rg0IQAJSX%GUztQw?{ed3Z= zDS~z6n;I?zL*w(~p_&cL7KQ6Y3?=Y>`$;oYSOVVF?rg39zV$P-S4^*3 zBI38Aa>i#21$h4p@$*BgvYP2ifA3QQH;HBkso7+aRq(q=W%fFZtQxskl zb`B0+k3ow0nUXISMNJ94j~=xT`T(MC(=qK~W?+whC8#d*DeKFhKT6dNoMgDX)4UXd z&e2p3_^j$%Sxt4{arzuBcg_fbrnBO$?jV9;iD6f>?)E~shi#>x{Db=zv6M)J zy6n++!{VLb!9lz#n}x96F@G7XuV24P$tqHNM%(gk?1j(H>HrQSCU%P2&CLzGN?K_o zx!a(lJxYHZo7nW-4xissmsUP6cY;{Y4MJ5$V#;L7hNCWWPR=opU!yx`1vTqr)9G4h z$qE4So|)#+`^Mg1b_-`cEzhIE!V;xWnA2;=pA@)yq*8NSM-IWuXkT77Y;#OezyQE= zs?vfKkV!e?=AQ-<-YF}OEQ%jG4}L={0c;PHhKp-_$BUSFEk_kzduK%x^ehOUT7n)? zR&MueBq?cDr>Nmrrb6pYmi4opiNcJ`P7YUPi3ZEv47mnO@4UE*z*{ z{r%kyIy?UpEK$Vn(I3-n>&W--bxv-ne=6s%5g51=5Zp$)(-iYM@BPh<0GIcv-s+Qb z--UWM>Yc5izM7i6p&_z9>+hO7s2bAJy%?YUo77fOc_h)^5GP~UM-OIYY2T$oSTZS_vOQ-i%H`Z=Gw6&Z$zTp#mlPt?z zi-8Iz#d7C8Eq4#WYB#xLzR<0Aqb8C*=Aqv-$WpHpWU!9LKmp@P7ZOUx7ytBWeFy`%N@O(AE^G~3z&%YB(83;q}S6{;4q2nKS&~AK%Z!I9yv1RV^5Nx40l$e>-FKe zijLl0$_N*FOd}{5nqOS|IP|aXg-NBBD=0OeEiF5&V@ovM1<~%G4&U$B0H~QtW_=sf z7-5G|?52%Hz%I2GZ3M=-w{zDV6{4a!YUZG^irO~cQovi9Zf{I{)RAB+#sgfY5Z86Q zMA-GUJH7afZQo(_JoxgOD;Pb!zi(ci%-_F%KYz9|u)i~|=46aXIQ{N;ey|WeXDxuX+CliTm$IYlr7uQ6z@v`S8SGaG z%L^4fJv^kimV1Jv5$@QgDH?B;)lI_R1b5h2MYWUtY7c>*Uxm0tpR*E(i>BFzm=Pml zPlrX6^%QG3Tnx*(f8h3w4QvR~<2f=qvbDdTUbGJzkeA26#Ee2NGpmhlIf{!T8~`aU zvag6VcEO3+6L8=1fx?3kib7Yb_kkxA?X2ZBi%b?^3pT-&k&r4$0bJtm|S6>hwPLs|3B(Gd^OC84(JXDJ+lmIg|bje5rCAD?dVy*zE{|jKjlq8pf{gp3kf}gfiRfm6~AIH z7|?D&fDW&o9|{DKPK~0IcQVLi^aW412OEQZ6s2W8+VLX*V%fJCl~n!YeCd4+#!d%1SqV!nEzRx%v#;Z|^%mF}Fcr;hFN&c5AWrQTy-WHK*eiUOtMQMy;Il*~4a z!DRi<@U*C!8a3?XxbAH0#D}99o;zj6gRg&p18RoG7}5 zeCza+(A?B=5PZ*-N=Rg3i>uSQ+8ZyIoUlU^)R89ka;naRyptm%LQKHC<$p0q%5Rlqv5(Es2* z%{3XB6pfH5Z2eSO>a?Ri(Qxz_Aambcyo%FJwg?zu6f*c*ICV)Yx_lVwXe!z$0TiRN z^Ib=0K*qSp&@jGqLMai+Y$z;O_v(v8z9>BgcOqiW8t}29=gxH3tb%EEN|5F6u%Krj zK1|Co$9;#jX2~bc>ipmXW1eRiC4U#70j|Jse!b0IF)&d~?R7c~q0U=Mq=GRptiOZ@ zTX;bwhx@0KxLi>G(!5d0R-k8N`*MBFS65fZ<9_-I_w3Bk*?DAqJV2)K(|N&%vVnUg zmL<-Yn7+Nnh8u%|Ne<^s?)c5J;sYCIII&Ro?JahkinK^Ge@;NRw4!%!?QYV_QGXpb zrvoGZydJ-r8nF~Ixr8;DE;`9Zmn(uVY?vYFS(LKMbfd>x0Dl~voY>sT3V5n!tN`gb z^KZ|_N2P6L%aLz+Wo7QKal~C7DSXUow#Keo$>}-rL9f<=cI?1|b5Nf+@as9W%L5+g zk9kFZ9IyYFbW~hYs-lJlDG=)_sw>}XYkwQCHoZRm41+N|{!W43*icFvgu>L=NFm@F zEEa^PqNNo!&iGy_!vAykH*w8vf{@&g2Q6l!&xlCE)$?1eV{@4JEFSoFeUeF6E>6^GD;LpON)?rJ_!vknwC3R;%&??zEa4GnmzkBT0 z289b!W{!{$ySS=89QAIR)A_{<&(>V<#&UCWL8*?&%3_%J zzOj1vfX4r4%kdw;yX*Kqm*@rt2GRVp!nvw>LW(i|7n84`;?tPao=WVRSN=u+XIuzV z+5f#96tJO>MF<2kd>kEA%|dl$73JV>p+>E3kI=6t_wKv5%=nf;z)KcSra2>mqN+7z zbNqK9hW$C9m93qxf4UnR!nGq{w(orn1wc&U`?lU zL51|D&t4M&rur1Tb zf}?4E5&xQ-`en$}sNT;=te%Mbt?-33Uq_kr%%!2?Gtv0B>F@Km_PHbDo_A*rHFa6P zMaFK_cgHM!?yKzA5rW=4j8JclVB@4F;ECo*81$!KUu0))rWH~oKYg+2j+f$XM( z6aakeUiM7=G2?6s_CERY&G!P6(c{&B6Y@bdF8SHb>K~~pS z@vdoMFd^4_uRAat*ARRh+Mk=X>mG7jY{jg-zY{(>T%b~K(7QZVR*cb&c?~#*ICgq` zC{XApM*e7qkDIqsk%bCsqBUX|I9sNZ=8p$<8g`CYv@)ED*y58lc6_hdt4I-$$u1>V zQDk9I!6z#utJHcYCAAsh^qg73pF&qx0(hyC9RN*K+M6>$a zPFTdzRN=3_=;@HxRAmKHso+aRM6DRw`|)OW>Ujx0 zYpQb70kogxqf*UG4FbOxL#bb}?YCUy0YOV5d$iQJm)7}UK3ve5TjI>}kIc`J;bDWd zp2#Ovo|XQB9zxwCDl%qqWSoSR-QzIDW-L(aG$y5-)VRpxNy|;igG0j3uU>d|nicV8 zDa7}!59r%31<3Z=$5CfyX5zY~LSD_k0T2fWbH$PgwGJDMIF9AhyE}ZavEj)^!xF9@ z5R-I5(8w{z=~8=hbH-LEbbpk-ckpPXjJgu9a*;#+x~ukBHCA9C?I30 zC3BduaVw|3OSqG9jdAOKt##!MI#2EZQEF~sbang)kg3lI$>4F!yA@DLAA!pjbP8d; zt~@oCr+P0XGYOwP1H|S=Pb8h^)mm&{^zV?vLtDV-wSvxDs#R!hLBN}eitX_1u%WuF zF^uN_oM33wM9lq34pp8}?X6n(kBj&}Y_OWdHr!}#XRq`O3}n{T$v97~UzQ~axDkFE z{|X)b`Kz#~NCF81G^!$web3PQAO%us1#2=wLMJQ6UAu*L<9fe+E%HV#leq2hKlZ-@ z*Pk6#RI;QATJkPLJ3)wuEd%in4!_igO5iVb<=V4$Fl9z#Oy2vQanpGsR^(vetv#ZGIFQ6 z27*O5hlJ?TVSuy7^K^)F82XPErYS4#`W#Rj8}~wtOC_Zc*)~P!6^R@))=>JAC82%) zqvYO)0=)}t;uxpZwHH8kG3<3axjHRxJSOsMa$!>jt})AnJFec{3&s9(=_)-IiVnwtUc z1%k)VrY7P>q3G%b@6-qgKMrZiyU$zLdOlt(jB2m6LLVDxyw&YeCnP-6@fyDx8%tUJ z&EgV~8XGY1>7`C_+pNVoV=FnH0zEI#l^+~hSh%<*z>U-wJ(Ho}uWG*377!g111w02 zCMKCdgM~Rc!F6?fyUzUT`D$#!FAOCd=@ys%{S`yeeQ+ioYU>APb&@jEL)l*Vf?jZO z^~QcxP0RiJ`8i%&o1>f^HW9~jUU!dyhNEk8A|eu2kMj>c;~?dnS^su$gKyh&C+l9HsPd#S)YBWq_T#9>MT6qCNy-YZ}jV0E(>+8BI8I~pNl zVIjDoUjp%O6;V*&UUKIGZy;`fl1Wb>_OiwmXt$VS=vQA=q{G6(oPWHj4?aGAvObtB zYhgj2>T|~{Dymf-aIJK}%EO{kjy zjWJ&2=0+~+$u6UsKfbz7>I)+vC7rB(+Zq`%@F}C}97a>?#zN6ol$V!BmN=*2uC?Ia z^|8nJ>Ua&vZT!(x{4!eZe9iT@&J3A#{K-_stG4jM3ETZz)89XG2@#0ESJro8V*bD) z2V5`airg8KL4;YOy=fbWkM^RdkNr_IhNYBjApnIJLuoxbHO;zxu72y=A1*>+(hmdH zI{1LBeS)Lg(0!v>3iv)iD?G!a;A!b3)!)tH8QTG3f+;|rd2;2e4MDq;lW5wyzmI5w zA%?w>_*c#vqN1%*r-j5isfA5Vc=>ATH`#pH%l=zZz+S*oq_t+}-T9SR$8C*x%91<$ zax+_tUkJ$#hr^=-DlOcGha)M3bflG(AoVWPAI}S(@{0Q!A+P{=?#4HUZ=8NuE?@NI>;AAU1mGKCYEuXmV+z3K!WJKBR-BzB= zOibT9C4-)-eEk|3A&C@cA*odxKb9gUC6_gmG85U&t03F^EFad|m&le8Tt3M7tFZ8g zoEQe+)vXEhI9iMPQqP}10Y&JmN-Gjc7`_wrQc-=mzeeJZ6BXv>nadzT_})b~dkT&Y@H9 zb?O}V6vn3-5Dgt2xep&+n3$M2Iy%-AN>*yU2?z*?j*fnX5&4@;_46wPEBrgiPo zW}oXd`DRq3xZknaE1w}uDP^a;-X#rp;S*ssw?_pEs0FHD=)4Vq5}~WRf0ru;!r+js zi4ivdqM}};lPmX)2-Iwn8+aI?(hXM#Q9^2X+)i!1=GWIhttC~r@L1!n9bDNlcTp-N zOD5ds_Pze-^u#t3_=<>imE7EFyPdKp<_t?~c;m^_i&>oK42$+OkxPn+lmlfqO+@%VXs>17&zuMglxofc_={V5Dz*&`&yAzRP6h|p>TJRKa$38{Y1 zTpQ8YKdm-+*~8}kaKYI>m4V~Wzep?zqfD#t>z8c&>j=-yL_^|B_P4U}W2Q2bP|VI(Ysrcj+5nsx9<+L<5t4tbofP}{Y>hcobcs?%IwDhD#MC|!j+3w`OzWx)Jt zi8QyiKA(E|47dr*a&~V!x3RQV+Zpnj2r%My+uH(Ob=T1{^*c~IR_AKx#_1Iczmb>x zC%vKAD2co`+jwU7{n5iM0taqIWhHO}%BbB~jA2f{qz$L}KX?EB$G1E_lHw9yV72eK z?Atb%b;YTRy2+acR^)yiIE|QPdY^8id!-gVq6fn-h*r{8 zDwti)jm}r4fjkdK&5*E;Yo3+-Om$i}0go z>hPyF`~x8IjGo~7x___#2l5vG1SVx_`VwWyT~V^nG(mS9gI8ypGmLQ!*7}hS%T+&! zC;WC2$_N#2!UsxaxQ6A2)A=k-T?r9d0Z)|T5O3~X#4q#H<0qQQT^}-&FpjwWY6v_2 z+m1;!_;V)JB!fk8AO`O&&_ywDck#p0k$_A}Rkd4AVcUP#(?iW4 ziAg+@e+GZ19+y9@)11ujfEXkvOBSl*AXl@XE>%&T%LU(mZAu1?mVhj2e8`d`&HpfU zGV>XtdROX3PX?9N8JH*AVhD_L)EXz2D&#YqFT5TNLS^+*%{b?Ehvg!ScK`QqMwvQ` zb&cuwSZJ>lD3X+!s9_(Dv6jW)KurR-IN@rY<|J_zc^%5y#J!8bE5Y)R%mV6p)JYg9 zvlLvX&DPa3UeF)`P+3leVB#6$ew*Z?ItGfndIUkvrVAQ`@YHfbA zdRawIiH9MLN!fvUI_1OC}S^9PVW)@Qcv+kg%{|inoUQ2Frpdt%B;0T&;78-?G zoq)01j#MvH&rr_?HXprg%_0j3b!Wr8B*f;eMTM4#{mGZ{YN^<4O)~nroDKh`(`DJ+ z|NdL5+bLRG3ZkL~CL1+9fmheZ_6$9C=ByK|D7B*tcHNW@?35?qYCs+w0io5Q<@7eW z)2)8JPRfM}GtS;Ej0SyR00>opQvg0q72p<#pty(CeU|_{$@_dYARF%ZIy{fstNOvx zoMff%Bd?`JHmQ4|ivgJ2o|HKc;1K>g>8!6eh{3CX+l9`Xq*)5bLs9$iAw(vV78pKL zQmBg4W@Kb#fop=3lhf7mFF>Y%79=iIpCQfvblB-$)8&Nm!g30jgHBIR|4dK+*WKw5 zflgs;+rIq!@6^GUAn1VG{ei&9l#NBk#AFtD z1l<=1g}4p>Z!IY93t`V{1OURK`NQcrqrK$mL<&+MM}X`1eLfeEo$3f)G0Dx7+ixVh zlv&SdF}}_RzPwJut+l^9MJHQpAka_Q^mhPG? z8UQaXljM{OCoSt{OHIyjX_Vm(Ux1R(;}qO!h$^idy!^L>EcCHktpVUg(v;oIXWm-6 zrarhLW~>`ye*e%9gyeyW#@5BbE3&VVoSZxhwvVu&ar`dKiULOn$tl5?mfVTyQjTh5 zd`m>Pb<87xpt$y4z+xxLNE%KzE{5zs;+KR-Ba1OH-ToTsdY!51cwgCr5irP;7t}4U z?=JQz;L^T>xsZ&U97rdpf46j(@+eMCF<$&S_)Qng(jGd_=5@><_{W^Mg1-6 z`IEUE9V_b9ECr(IB5qi^EU5ogMWA0}Ex+Lo&A0kt(SHif(vswa2aDZ4bFHs!EX2)z zH90lCGz>MKt`L{>0B^~_k;nOxo1i_URRW^n$0G@$o`43awqQdMZovpmCKBKVdaPwC zj*#?)-E^1DSY^*{+WJv2JCTLQkeL9ow)16iH9?0oRj7o&M8;77#V2S7_BhZVEL+(0 z*Ns8E(4@(g>JShRd|547q_j=jZ}(4O-U$`iATD`Gjbt<)|F^A+bW&1M!Nr6HMzK2f zApAqXdcweyXlb(2L_o8~yA+!qBJx}Y}q#@a8!S&aY@(IOfVE__!*9uE^M5+ftuj*P(m zvrzVylT-D_r~f&_C{EK9NbNC{52^rP6fEfP*T`lAbeH`B8x znY_?*b0d}az=ggi3u+C=5*72Jbgrkwb|K_h_@Jyzh=dK#j;$`AQHDDi@(aX%==EX!obNwK@J_pkkDJqX_A)88R}V0iV=5%#=frMdX9;rx^uR*C)$Q{OSDw<-)1w%hn7GxTm1!~oJqzLsHc9j^(qvmUimqx1jDhcN zpv7B%0$Ry6zG%Dr$A^eO#M+vfknp921tl~cGq8H_&&Wty80Adkfzs2Dbm86JmsWI= zKvewM^t2(Zu~9ct>FnYs*74?&;|N%p+u6Abza`akO-r2Ixue5^2P=Ag2pUTk_)kLw z37nmK^$T|WQbS5@RkeB-OhYGNqe|%aJK5Od73zh)<#*pMf2ZE>M*7gOQr#?7hf?Hg z6f+15FWC5jzJ)K1^zQ=7%=F6je4_`d+dbebql=g46mK?GLTN0nO*xeU&(55rM69fE zfwMyuh?S@9 z&G)x9U{{a0-D+!n1t?MkKI}MK{Ty7Jy+j(YM#RNQnV1j*AyZHeOTzDH3cuNE z)fKEw)b3Tyl+5fA5ffiI*?=Lsths&W)ExMczn=DOISyjlQ}6@RTJ`ymY=BF{?zfSW z#==l4mA6t#?)?sAF>!I?a&nwV43G2O_k;KoF9!K`n99n_-R`b7K9m|h%G$REUBAh< zAsV|r@90Yqm3y%M?Y73O>q;n!_p?>tsYEfJ3wnFvym871lzPBR>vz_(c)b4o8VKkt zblRMFwVK8JZiLt&fCA9c))qWwWMqB}GdDLkv$T9(#^G7(#-SDy1EgZ$D)!SWe#ydO z)4^iWaNY{0>HWqFJz%G$QU@TLcOwb?QZgbTfy|_8`3;ICpv=+}zkU055<`K^81iU& zEjm%Gug{E_+Wl?VSd}G#o#k-2h#KBd#?C=ZFuOljkLh&@MCbj669vEo{`NR%`Yn8S zgb(%|-R1}`Y-^qudmYY5gxwr8rvec%A}8mSp6k-12Sp0)u)6tHo{jXylfhv+;6Mc) z!|f^@=hERlSu210n6OQ=0&lXmwurLZi85b9gW=~Zj zQc^l;LLkfgcZJ<-=f9cSb$$pDz@q^k8<{Lp;lir(eNqTy1ZK|?w)bT_wf(e7tO4tD zu(5;)3@o!#LkWcY|F#7DF~73JL`;1|vg zwoV2+Nx&J9>?`*ZP>lLc)H_2Ui%_#)`G;8)jTWR$&=Ie_q(F6K0*>W{3=SYnFmQ4b zNh7D4jDk!*^oU>29e8&G3;{MTaQIzzSI<*t_gz-PyM=f}pSti|;0oTJjtXTiRD6Dh zgaK)+3n;dNMK&{4@*x6?6oM@;kM?E#WBA89Y%&g~U2GED-#{pMY+J-Q@`la+V`k23 z%``ZQ2_Pg5~RJXlheKs@p_~=eISJxV& z`T)gM9yCjef?ZK;Fk|r5LzdI-Uf`>mIhoLdE;loi|1?MeP?BX=-&lYXl9eGUH8GigXzqkyrnU>Dvfa-b}G3eN!kyTaPVD}FOYP5v_ug^o( z1^_nc!r~z$pQkZc=^E zqY`qv989wVGf7giuj=H8-ZA-%_TMS$8Bif1fIm})oSmI(KA=5Fb!2%egFs;?BqWSJ z^>3cyK6P)D-IciiGdwIREBo!(1rA5!OSWz~wX&jnZPQ@*upcVm3U*Tu~0Y+ox6AcXP2GI%vKZT3cHa zxZcc=OXT+Ylamkcsgn5c;X@xsEz{1ql*4F=gSb9TgVnJpW78z-2U%*=?VbLq# z2{>7zyK4b&N9ZDxO323^_*BtdumN^>HMXZ?CWRIM;RBeKRVP)WQiZ1r#T1j-W5h7= zNB;cj0dCL!vq^nLvz)=|y364|NQ_W<`F~eKKI_H!f*qI#%|ieZ#0&cf?d@6p+aBb1 zGg!l+k!Aw>ei}C^uZwE@0&qx&=jXd9U1nQf7Z+qz{lA{RJe*#%5d2!%R<#~0uw znco-t3@W{dBMAfEL6nfNY5Ds~((YLIX^!e5Yij23`_5JT^@&zUg|7|H4mCY6#tCw80l8}ni zSw~F3HuQUUo2hFy4acQ7H;X`r&T#!}ialrvjRM0`kMT2rWj0iMCI7-C_WV(*y}M}J)UP+pjKe$|H$)0(~{}UPywxKYzAo6vJLYH{b#rqEZ3~? ze!+#21xcn|TjrFQJV}l2gdu81ZC4YgXrkDfXy~_J|Ll(w(3^rgS~}&A-%2+*rLjr) zx!Xf}V<8+x;hbBt#ll`q2M+57)1v=dk0scZsKAXLs`z0g{pU$|H54>$)UB4gB zx5m@nE=9IAyl~-QM;`d8_jX4h5yFVV9lvKcH}|`&wO_)pJ{gzTFwaykL|sagD~Qj@ z&St;YF*atCoxOZ#;tjFKiPmQg7luej9w6tbUcZiW$L6pdA;bIUy7*RK|EJ|1rZZC( zK1*hA*nkJ2C42q)bwq3W+IQ21y+5pFd7THVO?rR6fRhlMozka(#a{a}%@deDvbH=} z_%?1TJgXc0W0doRIW|8ofk0)0%`u#L=vbiI!>!}sJn!Y8!IQH2xoEyE zY~9`6h5q{4I&1f1+AttTR#p~w=n2#GS5FLzy4lzt{gYjN3k`#|k`O$=0Y#rh)%bG< z;)Tv}7svh5t`FPE!`|`eoDv`8Ng* z-NKs1i_wnH8m&gglPnB*nRGG~9>CRM(Q=bSJGL;ONaV1#UCJ#S>B8CbKj{^ZGN z8Sbg_C|(R>;ZVRo=h9hOS%pPJL@aI7<=oWN+O{Z{?8*`trwf zxA#)gxJ>;sj2heBiZ3_#o4PkXh=?y%Zhtb|Q*3R1(l6sXOzw+r8;uPOIiA0SKxYK< zibv(;Nq``(mE#;xO(}hX3Axzs_Z5tYE*2!3l-h}I+-dQ7dwteKTUKz}=}8{aEkeC& zxG8HJg=6{I3uYD;A$j>Nl+DmZiy7wgi?(|ffKGf5SzcJn=lh5?dX`tXUrFikTX)T9 zvfkqE8~Kf9mGfI*5%YT&`-?BBJk+hL~-UjyGdh5o2tAg0a%E~Lef`J=}k}r zgnA%3j-7*p<@?aHp)w7aE~|YPu7nqMeq>5sUPMGe-WFH(lW#8h)YwU<*ti#MHX9ho zH+#jA(|*K24dC=xz>Gh}>z1W`MB4*4W3kw-b_DXBoRX3_qPxGO#iH7NN)c2DF%J0r z*`QIs`Sf#GE(Z}$h8cr&!MsKkIt=rmCEMG)0Lp7|CwHs$bBTD=GjV=jEye_C6w*Hy z4mI)Fk;^1CxyAdZH;0E>PD|;V>*<_$ZNqvO^X@1_z`WB*XdmdDI_wa?QF@@S#-Ee8 zL33gqq)=h)uc+e?%@LJcfcT!&67JYxbckX_FehH@492y4xi7Jom#>)-!L zN|<`V81mXBxH5Y6`$ITMm3W$EnJXf{qT4juE+5&s(W3i+8WYU~YK?k+cDOGFh1Bj{U_nRzG zT!>tcJO65c3MQmTghBuO`9DZhn(6%3ZdE)Bd);4pdJL*iOF|ca!(yywM>^*n?{Kk= zPL!>X%^r~*M&@1zKkKM_Ap(=)TVN^j_CD$~6u?&6>#1pj_cK{km-#DlhpJU~;Xw6T zpHoCBQ}Eokoahp$D#?D|9$rzNcJ3AXvBt`)m0=%v@Zu_3|rBpulBJ7(%vvb{F<9Dn5&%v#jg4H3(P&ey)9xz)oOt? zyff0^ix3RBgIo8buh){xCW(w=R|mJ|v`a9Ug;ZD(s%p=#@1Jt0nNGJEv^vY?bUneu z(aXmhez9F8C8bL2TEM;Vxx&~lDk<6IEPGs0VcVxq9edxg7&94zpCd?{566aV;AYL~ zWLG9OdtPBx1QImv5lcKdsbp$!;ldmi?l}sR^6D@B$GTZmo~zX(rE4eS1R&Rb54RQa zY)%yzdbXe9bjKgxG7nc*S37`}cc}*`l$-eeU}%XaaUchItHK%@o&;wH5wdTN>pk61 z6BoFk^uW1~+pJ(F(rxa4NK0$*L1@;1Tt7kE{QgezG6$l_n=ACzA>{ z3QQCE7t12XTa%)-lv2~uR=d)DyuIhZM2H?|fYz}R@>L!&=0KtNcW^NZ~D4)q|+lTvCBc`*WyKR5F3Dr&CTM%!ZrUg zkjd!k;^5{+eCGj*rf2@I3#ik`r(AtnQLuaC!91ZMh zh_7~@IwUU6>!z}`nS0*I-1n6GIj6&1&F|iEv&fTEu%LXJuaq=0_%~Ptum0AYNcYqU zBU!yAu})p1+Lhwdfx~;IWLe0Hr{M&&YW}wEzQ)PRmnnXMT$nN>Yb$}KiVh27{Pam^ z7cZ~0L<_?#;*;iPg4ff~lho4Z&*BbEM&7{RUoJOsqYRQ44TCqyYs^(de#5{J#0qKo zQuw0de_B{b1fq~9pZV6?l@@~*#JSBAFO0_+AH{8}=@Im%ME9e-^7BYzFMjpIlKZc9 zJ?L2^+FPh7%xUby2|@ZmDpb;NAmxpfMeapv&cy;D5s@Buo&Cznnb^$ltnIVe;%w}} z_#rDUmyjBu?Swbjr{6u?Xb3r#czKXWW5~3ZATQzsV!>vhiXxxtgRxQDPRLC~;k6V1 z6hwX*C|3d<5i0K4284q&V=p{S&PS_)j%&57z=MovOo`0nK83+2IyG3miZwqzx38q*=9^ep%#DY={a3^AawD%s=huOk;2z$WbsdBWeVKBh+q5z(t?^U z@5w08gyF;<;qCls`E5w9su=4L4XvOX#BI_UVQ;+)p=v5#$VwQAW?DaNe?U~|yBZV9 z1C-5=OG`tLB^fQ6M1m+=9SS8Byxf_U%Wz{I^t3ct{i3KN1cbFNJ+| zOCPQhT@5zM0ypiCL3V9zCadxvRre5XGMPZca3Ntk7DP`0zNiAhSzP*^}BpsOWb7^q(_doJ?~awYgfhl$EIQ00v|G06DayX1e7MX?vJ1LgmL zks+a|uH*RVzqhw9{TYo$d9+8S2>Hvy$sa$AA^4&Ns^@h|d;c(iM1kJ>bIfmgJ(Oew z#eBKb6TfK2w+4j^&@veAR1^-a@V%qK{YY<%S~CkR^oG3WSGPgm2~mzpVsY*YXYvEL z;MjvTlf%XR%4g;);hR`g2>GcT45$x#r2ZfW&ILMsWci79{{1Q&))>j!KmWQk7@(gA z(6s*gKIL8aI>OeG4=}U8-Ev|BD5b)oZ4~mN z&$IF6vMIjES`-hEym86N&1WTRWd4@Q9&ja+x(L2tmzlx>_5J%t6&1-4Xxwli&xc`F zHWT4KWMZGucr|DtO;4=Vs@Uz@M+#D*PLaApyup`tR~SX^5V#0} z)N&*Wq=$zGeeSxP5Vwbglmt$p0AC>HuxNS*#66B7}w-Lh#rG4+6j zo}qgAqQ`BxdVYL<3N92uaQLn>#GDm79F9@Ue_@uj19y#lq8Nyv+**gax(rY83@E53 z|BS77men^gX#sheVDI_wk~4-P%FK*F>fo}G--5pg>!e%avoR}OniT6!8yRsS!iM(F zGI9yM-FadI*qyq3B8bW9X3}kvdxN6`#U__T)!{%0oWK9^Xr)CiTp=vz3--! z=OzwX9*#k}9jb3}adFUX^;Z*_{(q3K!yx~eNKyH=P-B?)0R27$48+;TIl~<~i0`pGJKpL)=izc>J}8Pii*K3zvgz*4!?;&2 zY^RV_qpk?j1;Zz=rhP!Rm-pmhKrh799Pybp{kNeZuz&-fhLjJu{Hb^?j0SSsC^Ig1 z@7?oUnfay1ivkI)L&*m69exmLgM;QkM$o&#B-;M@^VnitLBR)?eIpS#`Zx+wTsv@q zWT|Z9pXu%?vBmIc+Cc5c&7YdSXul#f<95ZI-0hJ(=dWp9s?UGem6`Si(PuufB%1BA z?^$>9s=R&KMSQrcy1Ry6D!Ma7vIcdxImjFgMe~86%3c@y!M|e8Io#?gwT~`g)H-v< z3MCC|x}0I;>?{uUavLQqXS0iggCz>a&)(9e0y>}{$}63@OtA;3>9}#vH=ixK6vT>d zAh@8%a*s{7ho5EX_M{ehtW%S)G3g8f3%84;&BI8qG1^X<-Wnts`qzCg8HN-QJ0t+t8FHUu!eCkS;K6xchRN-C&xCGp zanVXP4MBVFX6&;96F~uNoX?E-;`-`7VvkTGu-y!U!f`!l^AlnSXczDV9+#MPs$F_hudPjCqRmpRI8jozH*mHWc_@?+t%s`Q2P+qGWEg!?U0h!x;rU zM<0*1Mh^sH_Tt4ej*g-T#sm74_MNwByyactR(QPnwReb~wzhVCh_ym{N5@HDUwK@v zUs1~N&{bxv!0r+f)Lp{LUg`(D>zrQQ$oi0_61?(6u}5U&1?3JKMk6ze7$v(!k!aV3 z+h8D$tyLA)!&M6_%~HwPIQ6SnB|%(3mB$~-FDQ5$clN337&FBjKH+O?^rfBFeIf_y9kocpYqQ}hG8oR znk>89!#!II$_gl!pog|AsL=JHR(C`$|b#mnJ5r)Uf}pbU8mMz2beXgD+&vT<0>)`ZP3!5 z7!rziRILdYLl?-d_U-rb$aLfEgsHF_8d8fjjre^M0WxlHcfTP9FEZ-Q6x!M)rdTPB zeU8meLh)LA-P3oMsV42Kw|_=bQ{1Rn=g^+5U!_Sm7>yLZ!-gv+8DZ_4N?eSTBl$ei zBT6%HZJ>F_c+!PmSQv7$%r0R5ms3oBC%r1b48;HZcqzp@g@r&=DT^ z)6-U(fmNIIMmOKIQu+i_F_-aAer@Gd4Xz1y-k6gfr!$q=)SP##IbQ^Ra&+Q6%8|_9 z%ru8_zz<#lQBp}#^G3=W8;ZIFDIlWxGKU&3n;I`8OC@hK=%al9{4d|U-fljj!3<#$ zM(WbB%<<9Hw#Je+$EC{u|Nl6knoIgu#AZdPJZID|$J$FyAXLu1MKCFygC|Dk7(G~~ Y&*?+QoVXDF=Y$Pfr*zZ{)huuQAHMzFDF6Tf literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/clock_bg_big3.png b/app/examples/Drawing/Clock/img/clock_bg_big3.png new file mode 100644 index 0000000000000000000000000000000000000000..53f378eb97ee763fffb6385fa08cf6f02b949237 GIT binary patch literal 50841 zcmW(-WmJ^g8XhE-4gu+M;2^?)bT`rx(p>`5-JQ~)bR!@g(%m7F1Jcsn(tS5~Eg1j6 zV!qjLJhct~pdf|$g5(7R0>PAiFRl#!y$9d7Xei({$0~jW0`Y@Li;Jkb&F$a2H@qJF zNcrV~W^3V8@btshWcIF7l$P>m2&;g;{t1QRCgPyzGtFmA`t76Lt(UVz^F;F+2396W zXmtL5ZAWzw<4UA>;c8eNhWUP?M{#jEMM=}r$^1YEQhsIeiK_OiR0;ujZ`KZ~c>4-b7 zupYdo)FQRqdcMw7v%rwpgZL)(=+>}#5sNn?ng~gRR5*cEZJNDIt7M8XO49hJa#5+R zwRPDa%i6Ruwa-QH9i2<>$6c1ke-(PIQ=^YE^SZ|M9*~Nf&I3fss}84}4{pwlI&Ghf zj8@CT!^8RhUEh_krTiYJLc>_P9N%#o+i_ZUr|?=r-6AVfLpLN2aKwU)v139yc%+C! z#+-E@B%@fNqmH^nsRf-djO2pnL)8YvC{DVe1s3M)BtH4djSbb+4nEG#=e=cRW$$XW z4z4BhPWCGvZz`5rrcN(p_LlUhZ{~m8ud+O>cK!bSn^ZP`0?~ERd3w)Dw>^uMbIY9~ zbr?z#g)(=*>9rKP<<3fD8aa5BS|Bp*Jj-(5M;t+p*aMBF^MN>=zV|VwwI@SuD(|kd z=8i_3uj?cZJi0yhe(Xzm_bF3LTbs{)tuKnczOIhK_qY>PKMunt;o`;kQrAT%w$Cv~ zC^qH8-{9cjBj#^1OTEN=6X5Y}>Z|JN{)i=m$77c#4xw&jw%7YM6TbY>sIZM)9?_Ud za{H`aW&whOgb+X#8$=P3bN6@02?xo4fo%H*hJhPG4TVBu2Thi_vA_eUsi`sRHaYE( zPM7O?HYgW)9pq8oZ|`UeJT8?Vyv+4|KHq-L^=P(ob+TrwtD|$5mXMI(n+MO8$2}# zb|WIi$VD$}o+F7+aUm-Ul~W$Sz(RD|d2oD3 zpGM8Hz~Yy&cq}7z0#ddNx^OzYBuyfIh`yaZKiZ|4+*4tMCxFUt%l)7J=;`-Xkwu&U zDq+E_{+Su#>RG0`x;neVg+_X2orYtgl9CeNhw&JnvvKYFzir><--!D{4yJt$)Y~uL z+FobYe@?yVPCqpsHp8#g!fZQ|%}X}bjDLe)RU%$tp(Tf;9%b27p0{YzAI8`CACkye zUO!?Oxr(n!I5|$*mzy-kO?sYoOW1eN9^b!?+te#nOoP{aB;}GRaMUaA6jt+nS6AE) zjbyv$yquhz%w9XP`Zqd-@N0OC*E+3!^VHF)c3G?F+3lpy{j|_sYMxX~o2QM

Ctj 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/clock_bg_big4.png b/app/examples/Drawing/Clock/img/clock_bg_big4.png new file mode 100644 index 0000000000000000000000000000000000000000..91bdb307ec43d140ab3fcaa753d85bdf8ebe076d GIT binary patch literal 21425 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

fW?v-S#gJ_5ey@ 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/.directory b/app/examples/Drawing/Fractal/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Fractal/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Fractal/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/Fractal/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..be2de25ed3710725ae1ee2e61e0bbf5d24ab7a92 GIT binary patch literal 152730 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

wP{jbT0hbxc`gsE+TZ7XbB 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 JPDHLkV1hqvs7C+* literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/.lang/cs.po b/app/examples/Drawing/Fractal/.lang/cs.po new file mode 100644 index 00000000..92f007d1 --- /dev/null +++ b/app/examples/Drawing/Fractal/.lang/cs.po @@ -0,0 +1,60 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Fractal" +msgstr "-" + +#: .project:2 +msgid "Mandelbrot Fractal with Just-In-Time compilation" +msgstr "Mandelbrot Fractal s Just-In-Time kompilací" + +#: FFractal.class:95 +msgid "Press F to deactivate Just-In-Time compilation" +msgstr "Stiskněte tlačítko F pro deaktivaci Just-In-Time kompilace" + +#: FFractal.class:97 +msgid "Press F to activate Just-In-Time compilation" +msgstr "Stiskněte tlačítko F pro aktivaci Just-In-Time kompilace" + +#: FFractal.class:102 +msgid "Press R to hide rectangle optimization" +msgstr "Stisknutím klávesy R skrýt obdélník optimalizace" + +#: FFractal.class:104 +msgid "Press R to show rectangle optimization" +msgstr "Stisknutím tlačítka R se zobrazí obdélník optimalizace" + +#: FFractal.class:108 +msgid "Zoom" +msgstr "-" + +#: FFractal.class:108 +msgid "Speed" +msgstr "Rychlost" + +#: FFractal.class:108 +msgid "Fast" +msgstr "Rychle" + +#: FFractal.class:108 +msgid "Slow" +msgstr "Pomalu" + +#: FFractal.class:108 +msgid "Max" +msgstr "-" + +#: FFractal.class:108 +msgid "Tasks" +msgstr "Úkoly" + diff --git a/app/examples/Drawing/Fractal/.lang/fr.po b/app/examples/Drawing/Fractal/.lang/fr.po new file mode 100644 index 00000000..aa2462dc --- /dev/null +++ b/app/examples/Drawing/Fractal/.lang/fr.po @@ -0,0 +1,61 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Fractal" +msgstr "Fractal" + +#: .project:2 +msgid "Mandelbrot Fractal with Just-In-Time compilation" +msgstr "Fractale de Mandelbort avec compilation Just-In-Time" + +#: FFractal.class:95 +msgid "Press F to deactivate Just-In-Time compilation" +msgstr "Appuyez sur F pour désactiver la compilation \"juste à-temps\"" + +#: FFractal.class:97 +msgid "Press F to activate Just-In-Time compilation" +msgstr "Appuyez sur F pour activer la compilation \"juste à-temps\"" + +#: FFractal.class:102 +msgid "Press R to hide rectangle optimization" +msgstr "Appuyez sur R pour cacher l'optimisation par rectangles" + +#: FFractal.class:104 +msgid "Press R to show rectangle optimization" +msgstr "Appuyez sur R pour afficher l'optimisation par rectangles" + +#: FFractal.class:108 +msgid "Fast" +msgstr "Rapide" + +#: FFractal.class:108 +msgid "Max" +msgstr "Max" + +#: FFractal.class:108 +msgid "Slow" +msgstr "Lente" + +#: FFractal.class:108 +msgid "Speed" +msgstr "Vitesse" + +#: FFractal.class:108 +msgid "Tasks" +msgstr "Tâches" + +#: FFractal.class:108 +msgid "Zoom" +msgstr "Zoom" + diff --git a/app/examples/Drawing/Fractal/.lang/ru.po b/app/examples/Drawing/Fractal/.lang/ru.po new file mode 100644 index 00000000..d309da9a --- /dev/null +++ b/app/examples/Drawing/Fractal/.lang/ru.po @@ -0,0 +1,80 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/Fractal/.project:19 +msgid "Fractal" +msgstr "Фрактал" + +#: app/examples/Drawing/Fractal/.project:20 +msgid "" +"Mandelbrot Fractal with Just-In-Time compilation.\n" +"\n" +"This example allows to freely zoom into the Mandelbrot fractal in real-time. You can toggle just-in-time compilation by hitting the \"F\" key." +msgstr "" +"Фрактал Мандельброта с компиляцией точно по времени.\n" +"\n" +"Этот пример позволяет свободно увеличивать фрактал Мандельброта в реальном времени. Вы можете включить компиляцию точно по времени, нажав клавишу «F»." + +#: app/examples/Drawing/Fractal/.src/FFractal.class:104 +msgid "Press F to deactivate Just-In-Time compilation" +msgstr "Нажмите F деактивировать компиляцию точно по времени" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:106 +msgid "Press F to activate Just-In-Time compilation" +msgstr "Нажмите F активировать компиляцию точно по времени" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:111 +msgid "Press R to hide rectangle optimization" +msgstr "Нажмите R скрыть прямоугольную оптимизацию" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:113 +msgid "Press R to show rectangle optimization" +msgstr "Нажмите R показать прямоугольную оптимизацию" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:117 +msgid "Zoom" +msgstr "Масштаб" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:117 +msgid "Speed" +msgstr "Скорость" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:117 +msgid "Fast" +msgstr "Быстро" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:117 +msgid "Slow" +msgstr "Медленно" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:117 +msgid "Max" +msgstr "Макс" + +#: app/examples/Drawing/Fractal/.src/FFractal.class:117 +msgid "Tasks" +msgstr "Задачи" + diff --git a/app/examples/Drawing/Fractal/.project b/app/examples/Drawing/Fractal/.project new file mode 100644 index 00000000..5673cb3e --- /dev/null +++ b/app/examples/Drawing/Fractal/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=Fractal +Startup=FFractal +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Mandelbrot Fractal with Just-In-Time compilation.\n\nThis example allows to freely zoom into the Mandelbrot fractal in real-time. You can toggle just-in-time compilation by hitting the \"F\" key." +Authors="Benoît Minisini" +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=en +Vendor=Example +Packager=1 +Tags=JustInTime +screenshot=2014-12-14.png diff --git a/app/examples/Drawing/Fractal/.src/FFractal.class b/app/examples/Drawing/Fractal/.src/FFractal.class new file mode 100644 index 00000000..2e2769f2 --- /dev/null +++ b/app/examples/Drawing/Fractal/.src/FFractal.class @@ -0,0 +1,245 @@ +' Gambas class file + +Private $hRose As Image +Private $fScale As Float = 0.0078125 +Private ITER_MAX As Integer = 128 +Private $aColor As New Integer[64] +Private $XC As Float +Private $YC As Float +Private $MX As Integer +Private $MY As Integer +Private $XX As Float +Private $YY As Float +Private $bFast As Boolean +Private $bRect As Boolean + +Private NTASK As Integer = 8 +Private $aTask As New FractalTask[NTASK] +Private $aResult As New Image[NTASK] + +Public Sub FractalTask_Kill() + + Dim hTask As FractalTask = Last + Dim aResult As Integer[] + Dim hImage As Image + + 'Print hTask.Index; ": *KILL*" + Try aResult = hTask.Value + If aResult Then + hImage = New Image(hTask.Width, hTask.Height) + hImage.Pixels = aResult + $aResult[hTask.Index] = hImage + dwgFractal.Refresh + Endif + +End + +' Public Sub FractalTask_Read(Data As String) +' +' Dim hTask As FractalTask = Last +' Print hTask.Index; ": "; Data +' +' End +' +' Public Sub FractalTask_Error(Data As String) +' +' Dim hTask As FractalTask = Last +' Print hTask.Index; "= "; Data +' +' End + +Public Sub dwgFractal_Draw() + + Dim hImage As Image + Dim X, Y, I, J As Float + Dim YT As Integer + Dim HT As Integer + Dim H As Integer + + 'hImage = New Image(Draw.Clip.W, Draw.Clip.H) + For I = 0 To Paint.W Step $hRose.W + For J = 0 To Paint.H Step $hRose.H + 'hImage.DrawImage($hRose, I, J) + Paint.DrawImage($hRose, I, J) + Next + Next + + X = $XC - (dwgFractal.W / 2) * $fScale + Y = $YC - (dwgFractal.H / 2) * $fScale + + 'If $bFast Then + + ' FastDrawFractalRect(hImage, X, Y, $fScale, 0, 0, hImage.W, hImage.H) + ' Draw.Image(hImage, Draw.Clip.X, Draw.Clip.Y) + + 'Else + + 'Draw.Image(hImage, Draw.Clip.X, Draw.Clip.Y) + HT = CInt(Paint.H) \ NTASK + For I = 0 To NTASK - 1 + If $aResult[I] Then + If I < (NTASK - 1) Then + H = HT + Else + H = Paint.H - YT + Endif + Paint.DrawImage($aResult[I], 0, YT, Paint.W, H) + Endif + 'RunTask(0, X, Y, 0, YT, hImage.W, HT) + 'Y += HT * $fScale + YT += HT + Next + 'RunTask(I, X, Y, 0, YT, hImage.W, hImage.H - HT) + 'DrawFractalRect(hImage, X, Y, $fScale, 0, 0, hImage.W, hImage.H) + 'Endif + + + Paint.Background = Color.SetAlpha(Color.White, 128) + Paint.Rectangle(4, 4, Draw.Font.Height * 26, Draw.Font.Height * 3 + 32) + Paint.Fill + + YT = 12 + + If $bFast Then + Draw.Text(("Press F to deactivate Just-In-Time compilation"), 12, YT) + Else + Draw.Text(("Press F to activate Just-In-Time compilation"), 12, YT) + Endif + + YT += Draw.Font.Height + 8 + If $bRect Then + Draw.Text(("Press R to hide rectangle optimization"), 12, YT) + Else + Draw.Text(("Press R to show rectangle optimization"), 12, YT) + Endif + + YT += Draw.Font.Height + 8 + Draw.Text(("Zoom") & ": " & CStr((Log2($fScale) + 6) * 8) & " " & ("Speed") & ": " & If($bFast, ("Fast"), ("Slow")) & " " & ("Max") & ": " & ITER_MAX & " " & ("Tasks") & ": " & NTASK, 12, YT) + +End + +Public Sub dwgFractal_MouseWheel() + + Dim fNewScale As Float + + If Mouse.Delta < 0 Then + If Log2($fScale) >= -6 Then Return + fNewScale = $fScale * Sqr(Sqr(Sqr(2))) + ITER_MAX -= 4 + Else + If Log2($fScale) < -50 Then Return + fNewScale = $fScale / Sqr(Sqr(Sqr(2))) + ITER_MAX += 4 + Endif + + $XC += $fScale * (Mouse.X - dwgFractal.W / 2) + $YC += $fScale * (Mouse.Y - dwgFractal.H / 2) + + $fScale = fNewScale + + $XC -= $fScale * (Mouse.X - dwgFractal.W / 2) + $YC -= $fScale * (Mouse.Y - dwgFractal.H / 2) + + If timRedraw.Enabled Then Return + timRedraw.Start + +End + +Public Sub Form_Open() + + Dim I As Integer + + $hRose = Image.Load("rose.jpg") + For I = 0 To $aColor.Max + $aColor[I] = Color.HSV(360 * I / $aColor.Max, 255, 255) + Next + + Me.Center + +End + +Public Sub dwgFractal_MouseDown() + + $MX = Mouse.X + $MY = Mouse.Y + $XX = $XC + $YY = $YC + +End + +Public Sub dwgFractal_MouseMove() + + $XC = $XX + ($MX - Mouse.X) * $fScale + $YC = $YY + ($MY - Mouse.Y) * $fScale + + If timRedraw.Enabled Then Return + timRedraw.Start + +End + + +Public Sub Form_KeyPress() + + If UCase(Key.Text) = "F" Then + $bFast = Not $bFast + If $bFast Then + FractalTask.FastDrawFractalRect(Null, 0, 0, 0, 0, 0, 0, 0) + Endif + Redraw(False) + Else If UCase(Key.Text) = "R" Then + $bRect = Not $bRect + Redraw(False) + Else If Key.Code = Key.Esc Then + Me.Close + Endif + +End + +Private Sub Redraw(bClear As Boolean) + + Dim I As Integer + Dim XO As Float + Dim YO As Float + Dim HT As Integer + + XO = $XC - (Me.ClientW / 2) * $fScale + YO = $YC - (Me.ClientH / 2) * $fScale + + HT = Me.ClientH \ NTASK + + For I = 0 To $aTask.Max + + If $aTask[I] Then Try $aTask[I].Stop + If bClear Then $aResult[I] = Null + + If I = $aTask.Max Then HT = Me.ClientH - HT * $aTask.Max + + $aTask[I] = New FractalTask(XO, YO, $fScale, Me.ClientW, HT, ITER_MAX, $aColor, $bFast, $bRect) As "FractalTask" + $aTask[I].Index = I + + YO += HT * $fScale + + Next + + dwgFractal.Refresh + +End + +Public Sub Form_Resize() + + Redraw(False) '(True) + +End + +Public Sub timRedraw_Timer() + + Dim I As Integer + + For I = 0 To $aTask.Max + If $aTask[I].Running Then Return + Next + + Redraw(False) + timRedraw.Stop + +End diff --git a/app/examples/Drawing/Fractal/.src/FFractal.form b/app/examples/Drawing/Fractal/.src/FFractal.form new file mode 100644 index 00000000..9c92b104 --- /dev/null +++ b/app/examples/Drawing/Fractal/.src/FFractal.form @@ -0,0 +1,18 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,83,64) + Arrangement = Arrange.Fill + { dwgFractal DrawingArea + MoveScaled(6,6,24,24) + Font = Font["+2"] + Background = &HFFFFFF& + Foreground = &H000000& + Focus = True + NoBackground = True + } + { timRedraw #Timer + #MoveScaled(58,26) + Delay = 100 + } +} diff --git a/app/examples/Drawing/Fractal/.src/FractalTask.class b/app/examples/Drawing/Fractal/.src/FractalTask.class new file mode 100644 index 00000000..debfe3a3 --- /dev/null +++ b/app/examples/Drawing/Fractal/.src/FractalTask.class @@ -0,0 +1,324 @@ +' Gambas class file + +Inherits Task + +Public Width As Integer +Public Height As Integer +Public Index As Integer + +Private $XO As Float +Private $YO As Float +Private $SF As Float +Private $bFast As Boolean +Private $iIterMax As Integer +Private $bRect As Boolean +Private $aColor As Integer[] + +Static Private ITER_MAX As Integer +Static Private DRAW_RECT As Boolean +Static Private COLORS As Integer[] + +Public Sub _new(XO As Float, YO As Float, SF As Float, W As Integer, H As Integer, iIterMax As Integer, aColor As Integer[], bFast As Boolean, bRect As Boolean) + + $XO = XO + $YO = YO + $SF = SF + Width = W + Height = H + $aColor = aColor + $bFast = bFast + $bRect = bRect + $iIterMax = iIterMax + +End + +Public Sub Main() As Variant + + Dim hImage As New Image(Width, Height, Color.Transparent) + + ITER_MAX = $iIterMax + DRAW_RECT = $bRect + COLORS = $aColor + + If $bFast Then + FastDrawFractalRect(hImage, $XO, $YO, $SF, 0, 0, Width, Height) + Else + DrawFractalRect(hImage, $XO, $YO, $SF, 0, 0, Width, Height) + Endif + Return hImage.Pixels + +End + +Static Private Sub DrawFractalRect(hImage As Image, XO As Float, YO As Float, SF As Float, X As Integer, Y As Integer, W As Integer, H As Integer) + + Dim I, J, K, C, CC As Integer + Dim XF, YF, XF0, YF0, XF1, YF1 As Float + Dim ZX, ZY, T As Float + Dim bSame As Boolean + Dim bRect As Boolean = DRAW_RECT + + XF0 = XO + X * SF + YF0 = YO + Y * SF + + If W <= 4 And If H <= 4 Then Goto CALC_ALL + + XF1 = XF0 + (W - 1) * SF + YF1 = YF0 + (H - 1) * SF + + If Sgn(XF0) + Sgn(XF1) Or If Sgn(YF0) + Sgn(YF1) Then + + C = 0 + + XF = XF0 + YF = YF0 + I = X + J = Y + GoSub CALC_POINT + CC = C + bSame = True + + XF += SF + For I = X To X + W - 1 + YF = YF0 + J = Y + GoSub CALC_POINT + YF = YF1 + J = Y + H - 1 + GoSub CALC_POINT + XF += SF + Next + + YF = YF0 + SF + For J = Y + 1 To Y + H - 2 + XF = XF0 + I = X + GoSub CALC_POINT + XF = XF1 + I = X + W - 1 + GoSub CALC_POINT + YF += SF + Next + + If bSame Then + If CC Then hImage.FillRect(X + 1, Y + 1, W - 2, H - 2, CC) + If bRect Then + hImage.PaintRect(X + 1, Y + 1, W - 2, H - 2, &HC0FFFFFF&) + Endif + Return + Endif + + Inc X + Inc Y + W -= 2 + H -= 2 + + Endif + + If W >= H Then + DrawFractalRect(hImage, XO, YO, SF, X, Y, W \ 2, H) + DrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y, W - (W \ 2), H) + Else + DrawFractalRect(hImage, XO, YO, SF, X, Y, W, H \ 2) + DrawFractalRect(hImage, XO, YO, SF, X, Y + (H \ 2), W, H - (H \ 2)) + Endif + + Return + +CALC_ALL: + + XF = XF0 + For I = X To X + W - 1 + YF = YF0 + For J = Y To Y + H - 1 + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then hImage[I, J] = COLORS[K And 63] + + YF += SF + Next + XF += SF + Next + Return + +CALC_POINT: + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then + K = K And 63 + C = COLORS[K] + If C <> CC Then bSame = False + hImage[I, J] = C + Else + C = 0 + If C <> CC Then bSame = False + Endif + + Return + +End + +Fast Static Public Sub FastDrawFractalRect(hImage As Image, XO As Float, YO As Float, SF As Float, X As Integer, Y As Integer, W As Integer, H As Integer) + + Dim I, J, K, C, CC As Integer + Dim XF, YF, XF0, YF0, XF1, YF1 As Float + Dim ZX, ZY, T As Float + Dim bSame As Boolean + Dim bRect As Boolean = DRAW_RECT + + If Not hImage Then Return + + XF0 = XO + X * SF + YF0 = YO + Y * SF + + If W <= 4 And If H <= 4 Then Goto CALC_ALL + + XF1 = XF0 + (W - 1) * SF + YF1 = YF0 + (H - 1) * SF + + If Sgn(XF0) + Sgn(XF1) Or If Sgn(YF0) + Sgn(YF1) Then + + C = 0 + + XF = XF0 + YF = YF0 + I = X + J = Y + GoSub CALC_POINT + CC = C + bSame = True + + 'XF += SF + For I = X To X + W - 1 + YF = YF0 + J = Y + GoSub CALC_POINT + YF = YF1 + J = Y + H - 1 + GoSub CALC_POINT + XF += SF + Next + + YF = YF0 + SF + For J = Y + 1 To Y + H - 2 + XF = XF0 + I = X + GoSub CALC_POINT + XF = XF1 + I = X + W - 1 + GoSub CALC_POINT + YF += SF + Next + + If bSame Then + If CC Then hImage.FillRect(X + 1, Y + 1, W - 2, H - 2, CC) + If bRect Then + hImage.PaintRect(X + 1, Y + 1, W - 2, H - 2, &HC0FFFFFF&) + Endif + Return + Endif + + Inc X + Inc Y + W -= 2 + H -= 2 + + Endif + + If W >= H Then + FastDrawFractalRect(hImage, XO, YO, SF, X, Y, W \ 2, H) + FastDrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y, W - (W \ 2), H) + Else + FastDrawFractalRect(hImage, XO, YO, SF, X, Y, W, H \ 2) + FastDrawFractalRect(hImage, XO, YO, SF, X, Y + (H \ 2), W, H - (H \ 2)) + Endif + + ' FastDrawFractalRect(hImage, XO, YO, SF, X, Y, W \ 2, H \ 2) + ' FastDrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y, W - (W \ 2), H \ 2) + ' FastDrawFractalRect(hImage, XO, YO, SF, X, Y + (H \ 2), W \ 2, H - (H \ 2)) + ' FastDrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y + (H \ 2), W - (W \ 2), H - (H \ 2)) + Return + +CALC_ALL: + + XF = XF0 + For I = X To X + W - 1 + YF = YF0 + For J = Y To Y + H - 1 + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then hImage[I, J] = COLORS[K And 63] + + YF += SF + Next + XF += SF + Next + Return + +CALC_POINT: + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then + K = K And 63 + C = COLORS[K] + If C <> CC Then bSame = False + hImage[I, J] = C + Else + C = 0 + If C <> CC Then bSame = False + Endif + + Return + +End diff --git a/app/examples/Drawing/Fractal/icon.png b/app/examples/Drawing/Fractal/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03e7cbbfd0279c192012d0b74085e4db69e21eea GIT binary patch literal 4737 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 P00000NkvXXu0mjfyoc68 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/rose.jpg b/app/examples/Drawing/Fractal/rose.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8913cdf64fa15bc387b556aeedc69d3de576eec8 GIT binary patch literal 26938 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 VW=mx72`jhVa^=bx)Kl9(|JejX?3(}p literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/GSLSpline/.directory b/app/examples/Drawing/GSLSpline/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/GSLSpline/.icon.png b/app/examples/Drawing/GSLSpline/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..884121225d557ea52e34561a8e0f9ef4a73db60d GIT binary patch literal 4045 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/GSLSpline/.lang/ru.po b/app/examples/Drawing/GSLSpline/.lang/ru.po new file mode 100644 index 00000000..bc8666a9 --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.lang/ru.po @@ -0,0 +1,54 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/GSLSpline/.project:16 +msgid "Gnu Scientific Library demo" +msgstr "Демонстрация научной библиотеки Gnu" + +#: app/examples/Drawing/GSLSpline/.src/FMain.class:122 +msgid "

Gnu Scientific Library example

Made by Juergen Zdero (juergen@zdero.eu) and Benoît Minisini (gambas@users.sourceforge.net)" +msgstr "

Пример научной библиотеки Gnu

Сделан Юрген Здеро (juergen@zdero.eu) и Бенуа Минисини (gambas@users.sourceforge.net)" + +#: app/examples/Drawing/GSLSpline/.src/FMain.form:5 +msgid "Gnu Scientific Library Example" +msgstr "Пример научной библиотеки Gnu" + +#: app/examples/Drawing/GSLSpline/.src/FMain.form:21 +msgid "Cubic &spline" +msgstr "Куб. сплайн" + +#: app/examples/Drawing/GSLSpline/.src/FMain.form:26 +msgid "&Clear" +msgstr "Очистить" + +#: app/examples/Drawing/GSLSpline/.src/FMain.form:35 +msgid "About..." +msgstr "О программе..." + +#: app/examples/Drawing/GSLSpline/.src/FMain.form:40 +msgid "Close" +msgstr "Закрыть" + diff --git a/app/examples/Drawing/GSLSpline/.project b/app/examples/Drawing/GSLSpline/.project new file mode 100644 index 00000000..53d83a13 --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.project @@ -0,0 +1,15 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.4.90 +Title=Gnu Scientific Library demo +Startup=FMain +Icon=spline.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Authors="Juergen Zdero\nBenoît Minisini" +TabSize=3 +SourcePath=/home/op/SDB/Programming/gambas/gambas3_projects/GSL/demo +Packager=1 +Translate=1 diff --git a/app/examples/Drawing/GSLSpline/.src/FMain.class b/app/examples/Drawing/GSLSpline/.src/FMain.class new file mode 100644 index 00000000..c81e131c --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.src/FMain.class @@ -0,0 +1,130 @@ +' Gambas class file + +Library "libgsl:0" + +Private Extern gsl_spline_alloc(pInterpType As Pointer, iSize As Integer) As Pointer +Private Extern gsl_spline_init(pSpline As Pointer, ax As Pointer, ay As Pointer, iSize As Integer) As Integer +Private Extern gsl_spline_eval(pSpline As Pointer, x As Float, pAcc As Pointer) As Float +Private Extern gsl_interp_accel_alloc() As Pointer +Private Extern gsl_spline_free(pSpline As Pointer) +Private Extern gsl_interp_accel_free(pAcc As Pointer) + +Public pointsArr_x As New Float[] +Public pointsArr_y As New Float[] +Public splineArr_x As New Integer[] +Public splineArr_y As New Integer[] + +Public Sub btnCubicSpline_Click() + + Dim xi As Integer + Dim acc As Pointer + Dim spline As Pointer + Dim gsl_interp_cspline As Pointer + + If pointsArr_x.Length < 3 Then Return + + gsl_interp_cspline = Pointer@(System.GetExternSymbol("libgsl:0", "gsl_interp_cspline")) + acc = gsl_interp_accel_alloc() + spline = gsl_spline_alloc(gsl_interp_cspline, pointsArr_x.Count) + gsl_spline_init(spline, pointsArr_x.Data, pointsArr_y.Data, pointsArr_x.Count) + + xi = pointsArr_x[0] + While xi < pointsArr_x[pointsArr_x.Max] + splineArr_x.Add(xi) + splineArr_y.Add(gsl_spline_eval(spline, xi, acc)) + xi += 1 + Wend + dwgGraph.Refresh + + gsl_spline_free(spline) + gsl_interp_accel_free(acc) + +End + +Public Sub dwgGraph_MouseDown() + + splineArr_x.Clear + splineArr_y.Clear + If (pointsArr_x.Length = 0) Or If (Mouse.X > pointsArr_x[pointsArr_x.Max]) Then + pointsArr_x.Add(Mouse.X) + pointsArr_y.Add(Mouse.Y) + Else + dwgGraph.Background = &HFF8080 + Endif + dwgGraph.Refresh + +End + +Public Sub dwgGraph_MouseUp() + + If dwgGraph.Background <> Color.White Then dwgGraph.Background = Color.White + +End + +Public Sub dwgGraph_Draw() + + Dim i As Integer + + 'lines + paint.Brush = paint.Color(Color.DarkGray) + Paint.LineWidth = 0.5 + paint.Font.Size = 10 + For i = 0 To pointsArr_x.Max + If i = 0 Then + Paint.MoveTo(pointsArr_x[i], pointsArr_y[i]) + Else + Paint.LineTo(pointsArr_x[i], pointsArr_y[i]) + Endif + Next + paint.Stroke + + 'numbers + For i = 0 To pointsArr_x.Max + paint.Text(i + 1, pointsArr_x[i], pointsArr_y[i] - 8) + Next + paint.Fill + + 'points + paint.Brush = paint.Color(Color.red) + For i = 0 To pointsArr_x.Max + Paint.Arc(pointsArr_x[i], pointsArr_y[i], 3) + Paint.Fill + Next + + 'spline + If splineArr_x.Count Then + paint.Brush = paint.Color(Color.DarkMagenta) + Paint.LineWidth = 1.0 + For i = 0 To splineArr_x.Max + If i = 0 Then + Paint.MoveTo(splineArr_x[i], splineArr_y[i]) + Else + Paint.LineTo(splineArr_x[i], splineArr_y[i]) + Endif + Next + paint.Stroke + Endif + +End + +Public Sub btnClear_Click() + + pointsArr_x.Clear + pointsArr_y.Clear + splineArr_x.Clear + splineArr_y.Clear + dwgGraph.Refresh + +End + +Public Sub btnAbout_Click() + + Message.Info(("

Gnu Scientific Library example

Made by Juergen Zdero (juergen@zdero.eu) and Benoît Minisini (gambas@users.sourceforge.net)")) + +End + +Public Sub btnClose_Click() + + Me.Close + +End diff --git a/app/examples/Drawing/GSLSpline/.src/FMain.form b/app/examples/Drawing/GSLSpline/.src/FMain.form new file mode 100644 index 00000000..50296ecb --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.src/FMain.form @@ -0,0 +1,45 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,93,60) + Text = ("Gnu Scientific Library Example") + Icon = Picture["spline.png"] + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { dwgGraph DrawingArea + MoveScaled(2,2,61,36) + Background = &HFFFFFF& + Expand = True + Border = Border.Sunken + } + { HBox1 HBox + MoveScaled(2,41,87,6) + Spacing = True + { btnCubicSpline Button + MoveScaled(0,0,21,6) + Text = ("Cubic &spline") + Picture = Picture["icon:/medium/pen"] + } + { btnClear Button + MoveScaled(22,0,21,6) + Text = ("&Clear") + Picture = Picture["icon:/medium/erase"] + } + { Panel1 Panel + MoveScaled(44,1,1,5) + Expand = True + } + { btnAbout Button + MoveScaled(44,0,21,6) + Text = ("About...") + Picture = Picture["icon:/medium/info"] + } + { btnClose Button + MoveScaled(66,0,21,6) + Text = ("Close") + Picture = Picture["icon:/medium/close"] + Cancel = True + } + } +} diff --git a/app/examples/Drawing/GSLSpline/spline.png b/app/examples/Drawing/GSLSpline/spline.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2bf708db45123cb5529b36d97bd931528fadd7 GIT binary patch literal 1632 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Gravity\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:34+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:248 +msgid "0" +msgstr "-" + +#: FAbout.class:24 +msgid "About gbGravity" +msgstr "Quant a gbGravity" + +#: FMain.class:302 +msgid "About gbGravity..." +msgstr "Quant al gbGravity..." + +#: FMain.class:167 +msgid "Add a Ball" +msgstr "Afegeix un bola" + +#: FMain.class:192 +msgid "Add more Balls" +msgstr "Afegeix més boles" + +#: FMain.class:289 +msgid "Ball out of Range" +msgstr "Bola fora de rang" + +#: FAbout.class:51 +msgid "by Iman Karim" +msgstr "per Iman Karim" + +#: FMain.class:216 +msgid "Choose Point:" +msgstr "Punt triat:" + +#: FMain.class:313 +msgid "Clear each Frame" +msgstr "Neteja cada marc" + +#: FMain.class:204 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "Fes un clic mantingut dins del camp de joc i mou el teu ratolí!" + +#: FMain.class:221 +msgid "ComboBox1" +msgstr "ComboBox1" + +#: FMain.class:265 +msgid "Current Ball aX:" +msgstr "Bola actual aX:" + +#: FMain.class:276 +msgid "Current Ball aY:" +msgstr "Bola actual aY:" + +#: FMain.class:243 +msgid "Current Ball X:" +msgstr "Bola actual X:" + +#: FMain.class:254 +msgid "Current Ball Y:" +msgstr "Bola actual Y:" + +#: FMain.class:179 +msgid "Floor Slide 0.9" +msgstr "Terra relliscós 0.9" + +#: FMain.class:228 +msgid "Focus Point" +msgstr "Punt amb el focus" + +#: FAbout.class:36 +msgid "gbGravity" +msgstr "gbGravity" + +#: FMain.class:155 +msgid "gbGravity - Iman Karim" +msgstr "gbGravity - Iman Karim" + +#: FMain.class:307 +msgid "Gravity" +msgstr "Gravetat" + +#: FMain.class:161 +msgid "Gravity 0.9" +msgstr "Gravity 0.9" + +#: FAbout.class:41 +msgid "Gravity like Simulator" +msgstr "Simulador de gravetat" + +#: .project:1 +msgid "Gravity Simulator" +msgstr "Simulador de gravetat" + +#: FAbout.class:56 +msgid "Ok - kool!" +msgstr "D'acord - Guai!" + +#: FMain.class:212 +msgid "Point Setup" +msgstr "Configuració del punt" + +#: FMain.class:234 +msgid "Randomize Kick" +msgstr "Sortida aleatoria" + +#: FMain.class:319 +msgid "Sky" +msgstr "Cel" + +#: FAbout.class:62 +msgid "Thanks to the Gambas team!" +msgstr "Gràcies a l'equip del Gambas!" + +#: FAbout.class:46 +msgid "Written in Gambas 1.9.46" +msgstr "Escrit amb Gambas 1.9.46" + diff --git a/app/examples/Drawing/Gravity/.lang/cs.po b/app/examples/Drawing/Gravity/.lang/cs.po new file mode 100644 index 00000000..a55f0a13 --- /dev/null +++ b/app/examples/Drawing/Gravity/.lang/cs.po @@ -0,0 +1,128 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Gravity Simulator" +msgstr "Gravitace sinulítoru" + +#: FAbout.form:14 +msgid "About gbGravity" +msgstr "O gbGravity" + +#: FAbout.form:26 +msgid "gbGravity" +msgstr "-" + +#: FAbout.form:31 +msgid "Gravity like Simulator" +msgstr "Gravitace jako simulátor" + +#: FAbout.form:36 +msgid "Written in Gambas 1.9.46" +msgstr "Napsáno v Gambasu 1.9.46" + +#: FAbout.form:41 +msgid "by Iman Karim" +msgstr "od Iman Karim" + +#: FAbout.form:46 +msgid "Ok - kool!" +msgstr "Ok - skvělé!" + +#: FAbout.form:52 +msgid "Thanks to the Gambas team!" +msgstr "Díky týmu Gambas!" + +#: FMain.form:35 +msgid "gbGravity - Iman Karim" +msgstr "-" + +#: FMain.form:41 +msgid "Gravity 0.9" +msgstr "Gravitace 0.9" + +#: FMain.form:47 +msgid "Add a Ball" +msgstr "Přidej míč" + +#: FMain.form:59 +msgid "Floor Slide 0.9" +msgstr "Zaokrouhlený snímek 0.9" + +#: FMain.form:72 +msgid "Add more Balls" +msgstr "Přidej více míčů" + +#: FMain.form:84 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "Klikněte a podržte na hracím poli a přesuňte myš!" + +#: FMain.form:92 +msgid "Point Setup" +msgstr "Nastavení bodu" + +#: FMain.form:96 +msgid "Choose Point:" +msgstr "Vyber bod:" + +#: FMain.form:103 +msgid "ComboBox1" +msgstr "-" + +#: FMain.form:108 +msgid "Focus Point" +msgstr "Zaměřený bod" + +#: FMain.form:114 +msgid "Randomize Kick" +msgstr "Náhodný kop" + +#: FMain.form:123 +msgid "Current Ball X:" +msgstr "Aktuální míč X:" + +#: FMain.form:128 +msgid "0" +msgstr "-" + +#: FMain.form:134 +msgid "Current Ball Y:" +msgstr "Aktuální míč Y:" + +#: FMain.form:145 +msgid "Current Ball aX:" +msgstr "Aktuální míč aX:" + +#: FMain.form:156 +msgid "Current Ball aY:" +msgstr "Aktuální míč aY:" + +#: FMain.form:169 +msgid "Ball out of Range" +msgstr "Míč mimo rozsah" + +#: FMain.form:182 +msgid "About gbGravity..." +msgstr "O gbGravity..." + +#: FMain.form:187 +msgid "Gravity" +msgstr "Gravitace" + +#: FMain.form:193 +msgid "Clear each Frame" +msgstr "Čistý každý snímek" + +#: FMain.form:199 +msgid "Sky" +msgstr "Obloha" diff --git a/app/examples/Drawing/Gravity/.lang/de.po b/app/examples/Drawing/Gravity/.lang/de.po new file mode 100644 index 00000000..39c38c14 --- /dev/null +++ b/app/examples/Drawing/Gravity/.lang/de.po @@ -0,0 +1,129 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Gravity Simulator" +msgstr "Schwerkraftsimulator" + +#: FAbout.form:14 +msgid "About gbGravity" +msgstr "Über gbGravity" + +#: FAbout.form:26 +msgid "gbGravity" +msgstr "gbSchwerkraft" + +#: FAbout.form:31 +msgid "Gravity like Simulator" +msgstr "Schwerkraftsimulator" + +#: FAbout.form:36 +msgid "Written in Gambas 1.9.46" +msgstr "Geschrieben in Gambas 1.9.46" + +#: FAbout.form:41 +msgid "by Iman Karim" +msgstr "von Iman Karim" + +#: FAbout.form:46 +msgid "Ok - kool!" +msgstr "-" + +#: FAbout.form:52 +msgid "Thanks to the Gambas team!" +msgstr "Danke an das Gambas Team!" + +#: FMain.form:35 +msgid "gbGravity - Iman Karim" +msgstr "gbSchwerkraft - Iman Karim" + +#: FMain.form:41 +msgid "Gravity 0.9" +msgstr "Schwerkraft 0.9" + +#: FMain.form:47 +msgid "Add a Ball" +msgstr "Ball hinzufügen" + +#: FMain.form:59 +msgid "Floor Slide 0.9" +msgstr "Bodenreibung 0.9" + +#: FMain.form:72 +msgid "Add more Balls" +msgstr "Mehrere hinzufügen" + +#: FMain.form:84 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "Aufs Spielfeld klicken und die Maus bewegen!" + +#: FMain.form:92 +msgid "Point Setup" +msgstr "Punkt Auswahl" + +#: FMain.form:96 +msgid "Choose Point:" +msgstr "Punkt wählen:" + +#: FMain.form:103 +msgid "ComboBox1" +msgstr "-" + +#: FMain.form:108 +msgid "Focus Point" +msgstr "Brennpunkt" + +#: FMain.form:114 +msgid "Randomize Kick" +msgstr "Zufälliger Abstoß" + +#: FMain.form:123 +msgid "Current Ball X:" +msgstr "Dieser Ball X:" + +#: FMain.form:128 +msgid "0" +msgstr "-" + +#: FMain.form:134 +msgid "Current Ball Y:" +msgstr "Dieser Ball Y:" + +#: FMain.form:145 +msgid "Current Ball aX:" +msgstr "Dieser Ball aX:" + +#: FMain.form:156 +msgid "Current Ball aY:" +msgstr "Dieser Ball aY:" + +#: FMain.form:169 +msgid "Ball out of Range" +msgstr "Ball außerhalb des Feldes" + +#: FMain.form:182 +msgid "About gbGravity..." +msgstr "Über gbGravity..." + +#: FMain.form:187 +msgid "Gravity" +msgstr "Schwerkraft" + +#: FMain.form:193 +msgid "Clear each Frame" +msgstr "Alle Frames löschen" + +#: FMain.form:199 +msgid "Sky" +msgstr "Himmel" + diff --git a/app/examples/Drawing/Gravity/.lang/es.po b/app/examples/Drawing/Gravity/.lang/es.po new file mode 100644 index 00000000..eb75a96f --- /dev/null +++ b/app/examples/Drawing/Gravity/.lang/es.po @@ -0,0 +1,126 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FAbout.class:26 +msgid "About gbGravity" +msgstr "Acerca de gbGravity" + +#: FAbout.class:38 +msgid "gbGravity" +msgstr "gbGravity" + +#: FAbout.class:43 +msgid "Gravity like Simulator" +msgstr "Gravity como simulador" + +#: FAbout.class:48 +msgid "Written in Gambas 1.9.46" +msgstr "Escrito en Gambas 1.9.46" + +#: FAbout.class:53 +msgid "by Iman Karim" +msgstr "por Iman Karim" + +#: FAbout.class:58 +msgid "Ok - kool!" +msgstr "Ok - ¡Genial!" + +#: FAbout.class:64 +msgid "Thanks to the Gambas team!" +msgstr "¡Gracias al grupo de Gambas!" + +#: FMain.class:270 +msgid "gbGravity - Iman Karim" +msgstr "gbGravity - Iman Karim" + +#: FMain.class:276 +msgid "Gravity 0.9" +msgstr "Gravedad 0.9" + +#: FMain.class:282 +msgid "Add a Ball" +msgstr "Agregar una bola" + +#: FMain.class:294 +msgid "Floor Slide 0.9" +msgstr "Porción del piso 0.9" + +#: FMain.class:307 +msgid "Add more Balls" +msgstr "Agregar más bolas" + +#: FMain.class:319 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "¡Click y sostenga sobre el campo de juego y mueva su ratón!" + +#: FMain.class:327 +msgid "Point Setup" +msgstr "Punto de configuración" + +#: FMain.class:331 +msgid "Choose Point:" +msgstr "Seleccionar punto:" + +#: FMain.class:336 +msgid "ComboBox1" +msgstr "ComboBox1" + +#: FMain.class:343 +msgid "Focus Point" +msgstr "Punto de enfoque" + +#: FMain.class:349 +msgid "Randomize Kick" +msgstr "Golpe aleatorio" + +#: FMain.class:358 +msgid "Current Ball X:" +msgstr "Bola activa X:" + +#: FMain.class:363 +msgid "0" +msgstr "0" + +#: FMain.class:369 +msgid "Current Ball Y:" +msgstr "Bola activa Y:" + +#: FMain.class:380 +msgid "Current Ball aX:" +msgstr "Bola activa aX" + +#: FMain.class:391 +msgid "Current Ball aY:" +msgstr "Bola activa aY" + +#: FMain.class:404 +msgid "Ball out of Range" +msgstr "Bola fuera de rango" + +#: FMain.class:417 +msgid "About gbGravity..." +msgstr "Acerca de gbGravity..." + +#: FMain.class:422 +msgid "Gravity" +msgstr "Gravedad" + +#: FMain.class:428 +msgid "Clear each Frame" +msgstr "Limpiar cada cuadro" + +#: FMain.class:434 +msgid "Sky" +msgstr "Cielo" + +#~ msgid "Gravity Simulator" +#~ msgstr "Simulador de gravedad" diff --git a/app/examples/Drawing/Gravity/.lang/ru.po b/app/examples/Drawing/Gravity/.lang/ru.po new file mode 100644 index 00000000..7ba57083 --- /dev/null +++ b/app/examples/Drawing/Gravity/.lang/ru.po @@ -0,0 +1,162 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/Gravity/.project:20 +msgid "Gravity Simulator" +msgstr "Симулятор гравитации" + +#: app/examples/Drawing/Gravity/.project:21 app/examples/Drawing/Gravity/.src/FAbout.form:19 +msgid "Gravity like Simulator" +msgstr "Симулятор гравитации" + +#: app/examples/Drawing/Gravity/.src/FAbout.form:5 +msgid "About gbGravity" +msgstr "О гравитоне" + +#: app/examples/Drawing/Gravity/.src/FAbout.form:15 +msgid "gbGravity" +msgstr "Гравитон" + +#: app/examples/Drawing/Gravity/.src/FAbout.form:23 +msgid "Written in Gambas 1.9.46" +msgstr "Написано в Gambas 1.9.46" + +#: app/examples/Drawing/Gravity/.src/FAbout.form:27 +msgid "by Iman Karim" +msgstr "от Иман Карим" + +#: app/examples/Drawing/Gravity/.src/FAbout.form:31 +msgid "Ok - kool!" +msgstr "ОК!" + +#: app/examples/Drawing/Gravity/.src/FAbout.form:36 +msgid "Thanks to the Gambas team!" +msgstr "Спасибо команде Gambas!" + +#: app/examples/Drawing/Gravity/.src/FMain.class:100 app/examples/Drawing/Gravity/.src/FMain.class:122 app/examples/Drawing/Gravity/.src/FMain.class:186 +msgid "ALL" +msgstr "ВСЕ" + +#: app/examples/Drawing/Gravity/.src/FMain.class:152 app/examples/Drawing/Gravity/.src/FMain.class:169 +msgid "Gravity:" +msgstr "Гравитация:" + +#: app/examples/Drawing/Gravity/.src/FMain.class:161 +msgid "Gravity: off" +msgstr "Гравитация: выкл" + +#: app/examples/Drawing/Gravity/.src/FMain.class:162 +msgid "Floor Slide: off" +msgstr "Слайд: выкл" + +#: app/examples/Drawing/Gravity/.src/FMain.class:170 app/examples/Drawing/Gravity/.src/FMain.class:205 +msgid "Floor Slide:" +msgstr "Слайд:" + +#: app/examples/Drawing/Gravity/.src/FMain.form:5 +msgid "gbGravity - Iman Karim" +msgstr "Гравитон - Иман Карим" + +#: app/examples/Drawing/Gravity/.src/FMain.form:10 +msgid "Gravity 0.9" +msgstr "Гравитация 0.9" + +#: app/examples/Drawing/Gravity/.src/FMain.form:15 +msgid "Add a Ball" +msgstr "Добавить мяч" + +#: app/examples/Drawing/Gravity/.src/FMain.form:25 +msgid "Floor Slide 0.9" +msgstr "Слайд 0.9" + +#: app/examples/Drawing/Gravity/.src/FMain.form:36 +msgid "Add more Balls" +msgstr "Добавить ещё мячей" + +#: app/examples/Drawing/Gravity/.src/FMain.form:46 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "Нажать и удерживать игровое поле и двигать мышью!" + +#: app/examples/Drawing/Gravity/.src/FMain.form:53 +msgid "Point Setup" +msgstr "Настройка точки" + +#: app/examples/Drawing/Gravity/.src/FMain.form:56 +msgid "Choose Point:" +msgstr "Выбрать точку:" + +#: app/examples/Drawing/Gravity/.src/FMain.form:61 +msgid "ComboBox1" +msgstr "Комбинированный_список_1" + +#: app/examples/Drawing/Gravity/.src/FMain.form:65 +msgid "Focus Point" +msgstr "Точка фокусировки" + +#: app/examples/Drawing/Gravity/.src/FMain.form:70 +msgid "Randomize Kick" +msgstr "Случайный удар" + +#: app/examples/Drawing/Gravity/.src/FMain.form:77 +msgid "Current Ball X:" +msgstr "Текущий мяч X:" + +#: app/examples/Drawing/Gravity/.src/FMain.form:81 app/examples/Drawing/Gravity/.src/FMain.form:90 app/examples/Drawing/Gravity/.src/FMain.form:99 app/examples/Drawing/Gravity/.src/FMain.form:108 +msgid "0" +msgstr "0" + +#: app/examples/Drawing/Gravity/.src/FMain.form:86 +msgid "Current Ball Y:" +msgstr "Текущий мяч Y:" + +#: app/examples/Drawing/Gravity/.src/FMain.form:95 +msgid "Current Ball aX:" +msgstr "Текущий мяч aX:" + +#: app/examples/Drawing/Gravity/.src/FMain.form:104 +msgid "Current Ball aY:" +msgstr "Текущий мяч aY:" + +#: app/examples/Drawing/Gravity/.src/FMain.form:115 +msgid "Ball out of Range" +msgstr "Мяч вне диапазона" + +#: app/examples/Drawing/Gravity/.src/FMain.form:127 +msgid "About gbGravity..." +msgstr "О гравитоне..." + +#: app/examples/Drawing/Gravity/.src/FMain.form:131 +msgid "Gravity" +msgstr "Гравитация" + +#: app/examples/Drawing/Gravity/.src/FMain.form:136 +msgid "Clear each Frame" +msgstr "Очищ. каждый кадр" + +#: app/examples/Drawing/Gravity/.src/FMain.form:141 +msgid "Sky" +msgstr "Небо" + diff --git a/app/examples/Drawing/Gravity/.project b/app/examples/Drawing/Gravity/.project new file mode 100644 index 00000000..3580a1e9 --- /dev/null +++ b/app/examples/Drawing/Gravity/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=Gravity Simulator +Startup=FMain +Icon=gravity.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Description="Gravity like Simulator" +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Drawing/Gravity/.src/FAbout.class b/app/examples/Drawing/Gravity/.src/FAbout.class new file mode 100644 index 00000000..1d524d09 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FAbout.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Button1_Click() + + FAbout.Hide + +End diff --git a/app/examples/Drawing/Gravity/.src/FAbout.form b/app/examples/Drawing/Gravity/.src/FAbout.form new file mode 100644 index 00000000..86cdcc9a --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FAbout.form @@ -0,0 +1,42 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,54,21) + Text = ("About gbGravity") + Resizable = False + { PictureBox1 PictureBox + MoveScaled(1,1,9,9) + Picture = Picture["gravity.png"] + Stretch = True + } + { Label1 Label + MoveScaled(11,1,17,4) + Font = Font["+2"] + Text = ("gbGravity") + } + { Label2 Label + MoveScaled(25,1,28,4) + Text = ("Gravity like Simulator") + Alignment = Align.Right + } + { Label3 Label + MoveScaled(21,5,32,4) + Text = ("Written in Gambas 1.9.46") + Alignment = Align.Right + } + { Label4 Label + MoveScaled(31,9,22,3) + Text = ("by Iman Karim") + Alignment = Align.Right + } + { Button1 Button + MoveScaled(1,13,52,4) + Text = ("Ok - kool!") + } + { Label5 Label + MoveScaled(1,17,52,3) + Font = Font["-1"] + Text = ("Thanks to the Gambas team!") + Alignment = Align.Center + } +} diff --git a/app/examples/Drawing/Gravity/.src/FMain.class b/app/examples/Drawing/Gravity/.src/FMain.class new file mode 100644 index 00000000..3fd7b8c1 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FMain.class @@ -0,0 +1,232 @@ +' Gambas class file + +' Written by Iman Karim +' http://home.inf.fh-rhein-sieg.de/~ikarim2s/ +' 14.11.2006 + +Private Objects As New Collection +Private world_Gravity As Float = 0.9 +Private world_Bounce As Float = 0.7 +Private world_SlideFloor As Float = 0.9 +Private isDrawing As Boolean + +Private Sub AddBall(Optional x As Integer = -1, Optional y As Integer = -1) + Dim cBall As New CBall + If x = -1 Then + cBall.x = Rnd(1, 100) + cBall.y = Rnd(1, 100) + Else + cBall.x = x + cBall.y = y + End If + cBall.ax = Rnd(-100, 100) + cBall.col = Rnd(1, 90000000) + objects.Add(cBall, Str(objects.Count + 1)) + cB.Add(Str(objects.Count)) +End + + +Private Sub DoGravity() + Dim myBall As CBall + For Each myBall In Objects + If togGrav.value Then myBall.ay = myBall.ay + world_Gravity '// Make Gravity + myBall.x = myBall.x + myBall.ax '// Move Ball + myBall.y = myBall.y + myBall.ay + If myBall.x >= dW.width - 5 Or myBall.x <= 0 Then '// Ball collidated on wall (left/right) + myBall.ax = (myBall.ax * world_Bounce) * -1 '// Reverse Ball direction and include world_Bounce + End If + If myBall.y > dW.Height - 10 Or myBall.y < 10 Then '// Ball collidated on Floor or Sky + If myball.y < 10 And togSky.value = False Then myball.ay = (myball.ay * world_Bounce) * -1 '// If Sky is disabled no not bounce + If myball.y > 10 Then myball.ay = (myball.ay * world_Bounce) * -1 '// On floor make bounce for sure + End If + If (((dW.Height - 10) - myBall.y) <= 1) And (Abs(myBall.ay) <= 2) And togGrav.Value Then '// If ball is n floor and to slow to jump up again stop the ball (y) + myball.y = (dW.Height - 10) + myball.ay = 0 + End If + + If myball.ay = 0 And myball.y = (dW.Height - 10) Then '// If ball is already on floor decrease the roll speed depending on world_SlideFloor factor. + If myball.ax < 0.1 Then + myball.ax = Abs(myball.ax) * world_SlideFloor * -1 + Else If myball.ax > 0.1 Then + myball.ax = Abs(myball.ax) * world_SlideFloor + Else + myball.ax = 0 '// If ball is moving to slow stop it + End If + End If + + If myball.y > dW.Height - 10 Then '// Make sure befor painting that the ball is inside your viewport. (floor) + myball.y = dW.Height - 10 + Else If myball.y <= 10 And togSky.Value = False Then + myball.y = 12 + End If + If myball.x > dW.width - 5 Then '// Make sure befor painting that the ball is inside your viewport. (left\right wall) + myball.x = dW.Width - 5 + Else If myball.x < 0 Then + myball.x = 0 + End If + Next +End + +Private Sub DrawWorld() + Dim myBall As CBall + Dim index As Integer + If isDrawing = False Then + isDrawing = True + Try Draw.begin(dW) + 'Draw.Rect(3, 12, dW.Width - 6, dW.Height - 17) + Draw.FillStyle = 1 + Draw.FillColor = Color.White + For Each myBall In Objects + index = index + 1 + Draw.Foreground = myBall.col + Draw.Ellipse(myBall.x, myBall.y, 5, 5) + If Str(index) = cB.Text Then + Draw.FillStyle = 0 + Draw.Foreground = Color.Red + If togFocus.value Then Draw.Ellipse(myBall.x - 3, myBall.y - 3, 11, 11) + lblX.Caption = Str(Round(myBall.x)) + lblY.Caption = Str(Round(myBall.y)) + If myball.y <= 0 Then + lblOutOfRange.Visible = True + Else + lblOutOfRange.Visible = False + End If + lblaX.Caption = Str(Round(myBall.ax)) + lblaY.Caption = Str(Round(myBall.ay)) + Draw.FillStyle = 1 + End If + Next + Draw.End + If cB.Text = ("ALL") Then + lblX.Caption = "%null%" + lblY.Caption = "%null%" + lblaX.Caption = "%null%" + lblaY.Caption = "%null%" + lblOutOfRange.Visible = False + End If + isDrawing = False + End If +End + +Private Sub Render() + + DoGravity + If togClear.Value Then dW.Clear() + Wait 0.001 + DrawWorld + +End + + +Public Sub Form_Open() + cb.Add(("ALL")) +End + +Public Sub Button1_Click() + AddBall +End + +Public Sub Timer1_Timer() + + Render + +End + +Public Sub Form_Resize() + Timer1.Enabled = False + + 'DO + ' WAIT 1 + 'LOOP WHILE modGravity.isDrawing + + dw.Width = Me.Width - dw.Left - 10 + dw.Height = Me.Height - dw.top - 10 + Timer1.Enabled = True + + +End + + +Public Sub Slider1_Change() + + lblGrav.Caption = ("Gravity:") & " " & Str(Slider1.Value / 100) + world_Gravity = Slider1.Value / 100 + +End + + +Public Sub togGrav_Click() + + If Not togGrav.Value Then + lblGrav.Caption = ("Gravity: off") + lblSlide.Caption = ("Floor Slide: off") + Slider1.Enabled = False + Slider2.Enabled = False + world_SlideFloor = 1 + Else + Slider1.Enabled = True + Slider2.Enabled = True + lblGrav.Caption = ("Gravity:") & " " & Str(Slider1.Value / 100) + lblSlide.Caption = ("Floor Slide:") & " " & Str(Slider2.Value / 100) + world_SlideFloor = Slider2.Value / 100 + End If + +End + +Public Sub cmdRandomize_Click() + Dim myBall As CBall + Dim index As Integer + cmdRandomize.Enabled = False + Draw.begin(dW) + Draw.FillStyle = 1 + For Each myBall In Objects + index = index + 1 + Draw.Foreground = myBall.col + Draw.Ellipse(myBall.x, myBall.y, 5, 5) + If Str(index) = cB.Text Or cB.Text = ("ALL") Then + myBall.ax = Rnd(1, 100) + myBall.ay = Rnd(1, 100) + End If + Next + Draw.End + cmdRandomize.Enabled = True +End + + + +Public Sub dW_MouseMove() + If togAddMore.Value Then + AddBall(Mouse.x, Mouse.Y) + End If +End + +Public Sub Slider2_Change() + + lblSlide.Caption = ("Floor Slide:") & " " & Str(Slider2.Value / 100) + world_SlideFloor = Slider2.Value / 100 + +End + + +Public Sub togAddMore_Click() + + If togAddMore.Value Then + lbladdmore.visible = True + Else + lblAddMore.visible = False + End If + +End + +Public Sub dW_MouseUp() + + togAddMore.Value = False + lblAddMore.visible = False + +End + +Public Sub lblAbout_MouseDown() + + FAbout.ShowModal + +End diff --git a/app/examples/Drawing/Gravity/.src/FMain.form b/app/examples/Drawing/Gravity/.src/FMain.form new file mode 100644 index 00000000..97c2845c --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FMain.form @@ -0,0 +1,143 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,121,73) + Text = ("gbGravity - Iman Karim") + Icon = Picture["gravity.png"] + Resizable = False + { lblGrav TextLabel + MoveScaled(61,1,21,3) + Text = ("Gravity 0.9") + Alignment = Align.Left + } + { Button1 Button + MoveScaled(1,1,25,6) + Text = ("Add a Ball") + } + { Slider1 Slider + MoveScaled(42,1,18,3) + MinValue = 1 + Step = 10 + Value = 90 + } + { lblSlide TextLabel + MoveScaled(61,4,21,3) + Text = ("Floor Slide 0.9") + Alignment = Align.Left + } + { Slider2 Slider + MoveScaled(42,4,18,3) + MinValue = 50 + Step = 10 + Value = 90 + } + { togAddMore ToggleButton + MoveScaled(1,8,25,6) + Text = ("Add more Balls") + } + { dW DrawingArea + MoveScaled(27,8,93,64) + Border = Border.Plain + Cached = True + { lblAddMore TextLabel + MoveScaled(1,1,24,10) + Visible = False + Background = &HFFFFDF& + Text = ("Click and hold on Gamefield and move your Mouse!") + Alignment = Align.Center + Border = Border.Plain + } + } + { Frame1 Frame + MoveScaled(1,15,25,53) + Text = ("Point Setup") + { TextLabel1 TextLabel + MoveScaled(1,3,23,4) + Text = ("Choose Point:") + } + { cB ComboBox + MoveScaled(1,7,23,4) + ReadOnly = True + Text = ("ComboBox1") + } + { togFocus ToggleButton + MoveScaled(1,12,23,4) + Text = ("Focus Point") + Value = True + } + { cmdRandomize Button + MoveScaled(1,17,23,5) + Text = ("Randomize Kick") + } + { Separator1 Separator + MoveScaled(1,22,23,2) + } + { Label1 Label + MoveScaled(2,24,21,3) + Text = ("Current Ball X:") + } + { lblX Label + MoveScaled(1,27,14,3) + Text = ("0") + Alignment = Align.Center + } + { Label2 Label + MoveScaled(2,30,21,3) + Text = ("Current Ball Y:") + } + { lblY Label + MoveScaled(1,33,14,3) + Text = ("0") + Alignment = Align.Center + } + { Label6 Label + MoveScaled(2,36,21,3) + Text = ("Current Ball aX:") + } + { lblAX Label + MoveScaled(1,39,14,3) + Text = ("0") + Alignment = Align.Center + } + { Label4 Label + MoveScaled(2,42,21,3) + Text = ("Current Ball aY:") + } + { lblAY Label + MoveScaled(1,45,14,3) + Text = ("0") + Alignment = Align.Center + } + { lblOutOfRange Label + MoveScaled(1,48,23,4) + Visible = False + Foreground = &HFF0000& + Text = ("Ball out of Range") + } + } + { Timer1 #Timer + #MoveScaled(54,20) + Enabled = True + Delay = 50 + } + { lblAbout Label + MoveScaled(1,68,25,4) + Font = Font["Underline"] + Mouse = Mouse.Pointing + Text = ("About gbGravity...") + } + { togGrav ToggleButton + MoveScaled(27,1,14,6) + Text = ("Gravity") + Value = True + } + { togClear ToggleButton + MoveScaled(82,1,22,6) + Text = ("Clear each Frame") + Value = True + } + { togSky ToggleButton + MoveScaled(105,1,15,6) + Text = ("Sky") + } +} diff --git a/app/examples/Drawing/Gravity/.src/cBall.class b/app/examples/Drawing/Gravity/.src/cBall.class new file mode 100644 index 00000000..27e0ae75 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/cBall.class @@ -0,0 +1,9 @@ +' Gambas class file + +' This Class represents a single ball. +Public x As Float +Public y As Float +Public col As Integer +'PUBLIC weight AS Float 'Weight not implemented ;) +Public ax As Float +Public ay As Float diff --git a/app/examples/Drawing/Gravity/gravity.png b/app/examples/Drawing/Gravity/gravity.png new file mode 100644 index 0000000000000000000000000000000000000000..e4e425e74166b10fb70f988c22f6b679a4b3afc1 GIT binary patch literal 7084 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/ca.po b/app/examples/Drawing/OnScreenDisplay/.lang/ca.po new file mode 100644 index 00000000..e01b52ce --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.lang/ca.po @@ -0,0 +1,27 @@ +# Catalan translation of OnScreenDisplay +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the OnScreenDisplay package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: OnScreenDisplay\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:35+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Masked window example" +msgstr "Exemple de finestra enmascarada" + +#: FOnScreenDisplay.form:8 +msgid "What time is it ?" +msgstr "Quina hora és?" diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/cs.po b/app/examples/Drawing/OnScreenDisplay/.lang/cs.po new file mode 100644 index 00000000..8112a521 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.lang/cs.po @@ -0,0 +1,20 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Masked window example" +msgstr "Příklad maskovaného okna" + +#: FOnScreenDisplay.form:8 +msgid "What time is it ?" +msgstr "Kolik je hodin ?" diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/de.po b/app/examples/Drawing/OnScreenDisplay/.lang/de.po new file mode 100644 index 00000000..c5dd7a6b --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.lang/de.po @@ -0,0 +1,21 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Masked window example" +msgstr "Beispiel für ein transparentes Fenster" + +#: FOnScreenDisplay.form:8 +msgid "What time is it ?" +msgstr "Wieviel Uhr ist es?" + diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/es.po b/app/examples/Drawing/OnScreenDisplay/.lang/es.po new file mode 100644 index 00000000..79e8bc33 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.lang/es.po @@ -0,0 +1,15 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FOnScreenDisplay.class:79 +msgid "What time is it ?" +msgstr "¿Qué hora es?" diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/ru.po b/app/examples/Drawing/OnScreenDisplay/.lang/ru.po new file mode 100644 index 00000000..eba336a9 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.lang/ru.po @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/OnScreenDisplay/.project:19 +msgid "Masked window example" +msgstr "Пример маскированного окна" + +#: app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form:5 +msgid "What time is it ?" +msgstr "Сколько времени?" + diff --git a/app/examples/Drawing/OnScreenDisplay/.project b/app/examples/Drawing/OnScreenDisplay/.project new file mode 100644 index 00000000..fb949611 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Title=Masked window example +Startup=FOnScreenDisplay +Icon=icon.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Packager=1 diff --git a/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class new file mode 100644 index 00000000..c42367af --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class @@ -0,0 +1,67 @@ +' Gambas class file + +Private $sLast As String +Private $MX As Integer +Private $MY As Integer + +Public Sub Form_Open() + + Me.Font = Font["64"] + Me.Resize(Me.Font.TextWidth("00:00:00") + 64, Me.Font.Height + 16) + Redraw +End + +Public Sub Timer1_Timer() + + Redraw + +End + +Private Sub Redraw() + + Dim hImage As Image + Dim sText As String + Dim iInd As Integer + + sText = Str(Time) + If sText = $sLast Then Return + + hImage = New Image(Me.Width, Me.Height, Color.Transparent) + + Paint.Begin(hImage) + Paint.Font = Font["64"] + Paint.LineWidth = 4 + + For iInd = 8 To 0 Step -1 + Paint.Brush = Paint.Color(Color.RGB(&h43 - iInd * &h43 / 8, &hC7 - iInd * &hC7 / 8, &hFF - iInd * &HFF / 8)) + Paint.DrawText(sText, iInd, iInd - 8, hImage.W, hImage.H, Align.Center) + Paint.Rectangle(iInd + 4, iInd + 4, hImage.Width - 14, hImage.Height - 14) + Paint.Stroke + Next + + Paint.End + + Me.Picture = hImage.Picture +' Me.Mask = True + $sLast = sText + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key["Esc"] Then Me.Close + +End + +Public Sub Form_MouseDown() + + $MX = Mouse.ScreenX - Me.X + $MY = Mouse.ScreenY - Me.Y + +End + +Public Sub Form_MouseMove() + + Me.Move(Mouse.ScreenX - $MX, Mouse.ScreenY - $MY) + +End diff --git a/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form new file mode 100644 index 00000000..4c71a2e5 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(27,61,49,13) + Text = ("What time is it ?") + Mask = True + Border = False + { Timer1 #Timer + Enabled = True + Delay = 250 + } +} diff --git a/app/examples/Drawing/OnScreenDisplay/icon.png b/app/examples/Drawing/OnScreenDisplay/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..26cc8686565b8f4a62a9948c453962381e83725d GIT binary patch literal 1300 zcmV+v1?&2WP) zO-xi*7>1v7e}+LF6=4J<)P@=)DJeVB6q+=(EE-Kp6Y5S&BxzlMwe7;1F2scfY!fxu zw5E+wNmES<8^S_aAzd{ZQ)$xRPZg`kARsdg^LOt(Ul#*|KWKp#-Sl1EbM8IgdB5-d z&U+91j~5K}b4QoFTvCQImy>cF5P9MwEucLoyvQd3Trwvo|9s9Z0y-# z1YCKjo(q7Nasfyrk|<>$B4qhYjG4?(3d1lK9C)F~UbX^d7$BfMVd-~#3S_#9&z*o$ z3eg(T8pBj`6HrRAd-rbo`ufCTjafdLVb%y{R{ogyOA(9j@& zY~K8Vw6y&qAAS0R)PLM8b5>y6wrv3P_4NU8_3BkdMn>4aeLDcLSPX!!t}be7YS^=9 z&#bnOIcaBS=OZy^^w-*ZdJp91=K~OlL^5;XaF~LE0swaG*g;1}2i4WpoIih_{{DU{ zDk|pGe0+QyfZW_%04&=^3y5e;)0mq;C=>!95C~-E0)YU-!@~gN<>gUVS4VMiF(*%+ zq_nh@wzjr839Mba7J$LQ!Rh{*-O@Ck(uD2p?Eq9&RRK^^QbIvN0jE!&2H@DSV+4ai zhK7b{YH9)?nM}?}ptiOafaAxHKk-gPW}Q$e$@tr;Q>Vzu$zku_y;N3KQdL#Op+kpg zZf*vkyStmSXV22x+e=wl8RyQOqp`7(O`A4l#jYjfh5T zjj0ss#FlaYN@bwYXh&n@gX;^T_M{JzgQBrl6hyO zltL}wc4QeIu_TZhMMSVI1IuwWjw^VcM#LlK%?AH3APAPSFqFyUR1`Jc=%PJP3hUnR zB$3DjQ_%-$olFzIe+B;R0Jbj+!!Q@Ou?Vi4M0=VW53ZA%2=Patf~4}Oj)#avCm^^Q z%ZXj%=JiM2&!6QdxUQ6dnY)<#Cjg#yh)mpP^xhy>e!EC^u8o-o&SZ?+$v8QMtI1jA z$9gFrf~$&fT#cG3y;9s9njkb3#&wbl_Xh=@>o64^XVo%`%JK~q6t5vX79~4Bz+}{B zy{#oPPLbrbXt^7W4pIAJJ@h@7z(@+>(#y3a4d_D%V82k+oBq+c}ribtV0000< KMNUMnLSTaPhgkvu literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.directory b/app/examples/Drawing/Painting/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Painting/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Painting/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/Painting/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..2ec5e978ff0e8c1516da613a362ec3b1c34db905 GIT binary patch literal 90069 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
ae7M*iV{BlxjlJvJnWUl8vf!=OcF|d* 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 Fe*nDbN8SJc literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Tablet/.icon.png b/app/examples/Drawing/Tablet/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d18cb788766d05515525542556383861ffcb9801 GIT binary patch literal 3536 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 KMNUMnLSTZgF~HFP literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Tablet/.lang/ru.po b/app/examples/Drawing/Tablet/.lang/ru.po new file mode 100644 index 00000000..6879e276 --- /dev/null +++ b/app/examples/Drawing/Tablet/.lang/ru.po @@ -0,0 +1,112 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/Tablet/.project:17 +msgid "Tablet" +msgstr "Планшет" + +#: app/examples/Drawing/Tablet/.project:18 +msgid "" +"Tablet event example.\n" +"\n" +"This example shows how to get extended event information about tablet pens. It draws colored lines whose width depends on the pen pressure." +msgstr "" +"Пример события планшета.\n" +"\n" +"В этом примере показано, как получить расширенную информацию о событиях для планшетов. Он рисует цветные линии, ширина которых зависит от давления пера." + +#: app/examples/Drawing/Tablet/.src/FMain.class:79 +msgid "Cursor" +msgstr "Курсор" + +#: app/examples/Drawing/Tablet/.src/FMain.class:81 +msgid "Eraser" +msgstr "Ластик" + +#: app/examples/Drawing/Tablet/.src/FMain.class:83 +msgid "Mouse" +msgstr "Мышь" + +#: app/examples/Drawing/Tablet/.src/FMain.class:85 +msgid "Pen" +msgstr "Перо" + +#: app/examples/Drawing/Tablet/.src/FMain.form:30 +msgid "red" +msgstr "красный" + +#: app/examples/Drawing/Tablet/.src/FMain.form:36 +msgid "Type" +msgstr "Тип" + +#: app/examples/Drawing/Tablet/.src/FMain.form:46 +msgid "X" +msgstr "X" + +#: app/examples/Drawing/Tablet/.src/FMain.form:56 +msgid "Y" +msgstr "Y" + +#: app/examples/Drawing/Tablet/.src/FMain.form:66 +msgid "XTilt" +msgstr "XTilt" + +#: app/examples/Drawing/Tablet/.src/FMain.form:76 +msgid "YTilt" +msgstr "YTilt" + +#: app/examples/Drawing/Tablet/.src/FMain.form:86 +msgid "Pressure" +msgstr "Давление" + +#: app/examples/Drawing/Tablet/.src/FMain.form:96 +msgid "Rotation" +msgstr "Поворот" + +#: app/examples/Drawing/Tablet/.src/FMain.form:120 +msgid "Clear" +msgstr "Очистить" + +#: app/examples/Drawing/Tablet/.src/FMain.form:130 +msgid "black" +msgstr "чёрный" + +#: app/examples/Drawing/Tablet/.src/FMain.form:138 +msgid "brown" +msgstr "коричневый" + +#: app/examples/Drawing/Tablet/.src/FMain.form:145 +msgid "yellow" +msgstr "жёлтый" + +#: app/examples/Drawing/Tablet/.src/FMain.form:152 +msgid "blue" +msgstr "синий" + +#: app/examples/Drawing/Tablet/.src/FMain.form:159 +msgid "green" +msgstr "зелёный" + diff --git a/app/examples/Drawing/Tablet/.project b/app/examples/Drawing/Tablet/.project new file mode 100644 index 00000000..acf53d4e --- /dev/null +++ b/app/examples/Drawing/Tablet/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Tablet +Startup=FMain +Icon=Icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Tablet event example.\n\nThis example shows how to get extended event information about tablet pens. It draws colored lines whose width depends on the pen pressure." +Authors="Benoît Minisini, from an idea of Ricardo Díaz Martín" +TabSize=2 +Vendor=Example +Packager=1 +Screenshot=2014-12-14.png +Translate=1 diff --git a/app/examples/Drawing/Tablet/.src/FMain.class b/app/examples/Drawing/Tablet/.src/FMain.class new file mode 100644 index 00000000..c8124add --- /dev/null +++ b/app/examples/Drawing/Tablet/.src/FMain.class @@ -0,0 +1,178 @@ +' Gambas class file + +Private Const MAX_LINE_WIDTH As Float = 20 + +Private $hBuffer As Picture +Private $X As Float +Private $Y As Float +Private $aCoord As Float[] +Private $iColor As Integer +Private $hBound As Rect +Private $fPressure As Float + +Public Sub _new() + + Dim sLabel As String + Dim Y As Integer + + $hBuffer = New Picture(1024, 768) + $hBuffer.Fill(Color.White) + dwgDraw.ResizeContents($hBuffer.Width, $hBuffer.Height) + +End + +Public Sub Form_Open() + +End + +Private Sub PaintStroke(hDest As Picture) + + Dim I As Integer + Dim hTemp As Image + + If Not $aCoord Then Return + If $aCoord.Count < 6 Then Return + + hTemp = New Image($hBound.W, $hBound.H, Color.Transparent) + + Paint.Begin(hTemp) + Paint.LineCap = Paint.LineCapRound + Paint.Brush = Paint.Color($iColor) + Paint.Translate(-$hBound.X, -$hBound.Y) + + For I = 0 To $aCoord.Max - 3 Step 3 + + Paint.MoveTo($aCoord[I], $aCoord[I + 1]) + Paint.LineTo($aCoord[I + 3], $aCoord[I + 4]) + Paint.LineWidth = Max(0.5, $aCoord[I + 2] * MAX_LINE_WIDTH) + Paint.Stroke + + Next + + Paint.End + + 'hTemp.Opacity(0.5) + Paint.Begin(hDest) + Paint.DrawImage(hTemp, $hBound.X, $hBound.Y,,, 0.5) + Paint.End + +End + + +Public Sub dwgDraw_Draw() + + Dim hDraw As Picture + + hDraw = $hBuffer.Copy() + PaintStroke(hDraw) + + Draw.Picture(hDraw, -dwgDraw.ScrollX, -dwgDraw.ScrollY) + +End + +Private Sub UpdateInfo(Optional bUp As Boolean) + + Dim iColUp, iColDown As Integer + + Select Case Pointer.Type + Case Pointer.Cursor + lblType.Text = ("Cursor") + Case Pointer.Eraser + lblType.Text = ("Eraser") + Case Pointer.Mouse + lblType.Text = ("Mouse") + Case Pointer.Pen + lblType.Text = ("Pen") + End Select + + lblX.Text = Format($X, "0.000") + lblY.Text = Format($Y, "0.000") + lblXTilt.Text = Pointer.XTilt + lblYTilt.Text = Pointer.YTilt + lblPressure.Text = Pointer.Pressure + lblRotation.Text = Pointer.Rotation + + iColUp = Color.Default + iColDown = Color.LightForeground + + If bUp Then + If Mouse.Left Then panButton1.Background = iColUp + If Mouse.Middle Then panButton2.Background = iColUp + If Mouse.Right Then panButton3.Background = iColUp + Else + panButton1.Background = If(Mouse.Left, iColDown, iColUp) + panButton2.Background = If(Mouse.Middle, iColDown, iColUp) + panButton3.Background = If(Mouse.Right, iColDown, iColUp) + Endif + +End + + +Public Sub dwgDraw_MouseDown() + + $X = dwgDraw.ScrollX + Pointer.X + $Y = dwgDraw.ScrollY + Pointer.Y + $fPressure = 0.1 + + UpdateInfo + timScroll.Start + + If Not Mouse.Left Then Return + + $hBound = New Rect($X - MAX_LINE_WIDTH, $Y - MAX_LINE_WIDTH, MAX_LINE_WIDTH * 2, MAX_LINE_WIDTH * 2) + + $aCoord = [$X, $Y, If(Pointer.Type = Pointer.Mouse, $fPressure, Pointer.Pressure)] + +End + +Public Sub dwgDraw_MouseMove() + + $X = dwgDraw.ScrollX + Pointer.X + $Y = dwgDraw.ScrollY + Pointer.Y + + UpdateInfo + + If Not Mouse.Left Then Return + + $fPressure = Min($fPressure + 0.01, 1) + + $aCoord.Add($X) + $aCoord.Add($Y) + $aCoord.Add(If(Pointer.Type = Pointer.Mouse, $fPressure, Pointer.Pressure)) + + $hBound = $hBound.Union(Rect($X - MAX_LINE_WIDTH, $Y - MAX_LINE_WIDTH, MAX_LINE_WIDTH * 2, MAX_LINE_WIDTH * 2)) + + 'Debug $X;; $Y + + dwgDraw.View.Refresh($hBound.X - dwgDraw.ScrollX, $hBound.Y - dwgDraw.ScrollY, $hBound.W, $hBound.H) + +End + +Public Sub dwgDraw_MouseUp() + + UpdateInfo(True) + timScroll.Stop + + PaintStroke($hBuffer) + $aCoord.Clear + +End + +Public Sub timScroll_Timer() + + dwgDraw.EnsureVisible($X - 16, $Y - 16, 32, 32) + +End + +Public Sub btnClear_Click() + + $hBuffer.Fill(Color.White) + dwgDraw.View.Refresh + +End + +Public Sub btnColor_Click() + + $iColor = Last.Background + +End diff --git a/app/examples/Drawing/Tablet/.src/FMain.form b/app/examples/Drawing/Tablet/.src/FMain.form new file mode 100644 index 00000000..faeeeba0 --- /dev/null +++ b/app/examples/Drawing/Tablet/.src/FMain.form @@ -0,0 +1,174 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,105,83) + Icon = Picture["Icon.png"] + Arrangement = Arrange.Horizontal + { dwgDraw ScrollArea + MoveScaled(1,1,52,47) + Expand = True + Border = False + NoBackground = True + Tablet = True + } + { Separator1 Separator + MoveScaled(54,27,0,12) + Visible = False + } + { Panel2 Panel + MoveScaled(55,3,49,79) + Arrangement = Arrange.Vertical + Margin = True + { panInfo Panel + MoveScaled(0,0,49,79) + Margin = True + Border = Border.Plain + { ToggleButton4 ToggleButton btnColor + Name = "ToggleButton4" + MoveScaled(1,43,14,14.2857) + Background = &HFF0000& + Foreground = Color.DarkGray + Text = ("red") + Radio = True + } + { Label1 Label + MoveScaled(1,1,10,3) + Foreground = &H7F7F7F& + Text = ("Type") + Alignment = Align.Right + } + { lblType Label + MoveScaled(13,1,14,3) + Expand = True + } + { Label2 Label + MoveScaled(1,4,10,3) + Foreground = &H7F7F7F& + Text = ("X") + Alignment = Align.Right + } + { lblX Label + MoveScaled(13,4,14,3) + Expand = True + } + { Label3 Label + MoveScaled(1,7,10,3) + Foreground = &H7F7F7F& + Text = ("Y") + Alignment = Align.Right + } + { lblY Label + MoveScaled(13,7,14,3) + Expand = True + } + { Label4 Label + MoveScaled(1,10,10,3) + Foreground = &H7F7F7F& + Text = ("XTilt") + Alignment = Align.Right + } + { lblXTilt Label + MoveScaled(13,10,14,3) + Expand = True + } + { Label5 Label + MoveScaled(1,13,10,3) + Foreground = &H7F7F7F& + Text = ("YTilt") + Alignment = Align.Right + } + { lblYTilt Label + MoveScaled(13,13,14,3) + Expand = True + } + { Label6 Label + MoveScaled(1,16,10,3) + Foreground = &H7F7F7F& + Text = ("Pressure") + Alignment = Align.Right + } + { lblPressure Label + MoveScaled(13,16,14,3) + Expand = True + } + { Label7 Label + MoveScaled(1,19,10,3) + Foreground = &H7F7F7F& + Text = ("Rotation") + Alignment = Align.Right + } + { lblRotation Label + MoveScaled(13,19,14,3) + Expand = True + } + { Panel1 Panel + MoveScaled(18,25,10,5) + { panButton3 Panel + MoveScaled(8,0,2,5) + Border = Border.Plain + } + { panButton1 Panel + MoveScaled(0,0,2,5) + Border = Border.Plain + } + { panButton2 Panel + MoveScaled(4,0,2,5) + Border = Border.Plain + } + } + { btnClear Button + MoveScaled(14,34,18,7) + Text = ("Clear") + Picture = Picture["icon:/large/delete"] + } + { Separator2 Separator + MoveScaled(0,32,47,1) + } + { ToggleButton1 ToggleButton btnColor + Name = "ToggleButton1" + MoveScaled(31,59,14.2857,14.2857) + Background = &H000000& + Foreground = Color.DarkGray + Text = ("black") + Radio = True + Value = True + } + { ToggleButton2 ToggleButton btnColor + Name = "ToggleButton2" + MoveScaled(1,59,14.2857,14.2857) + Background = &HFF7F00& + Foreground = Color.DarkGray + Text = ("brown") + Radio = True + } + { ToggleButton3 ToggleButton btnColor + Name = "ToggleButton3" + MoveScaled(16,59,14.2857,14.2857) + Background = &HFFFF00& + Foreground = Color.DarkGray + Text = ("yellow") + Radio = True + } + { ToggleButton5 ToggleButton btnColor + Name = "ToggleButton5" + MoveScaled(31,43,14.2857,14.2857) + Background = Color.Cyan + Foreground = Color.DarkGray + Text = ("blue") + Radio = True + } + { ToggleButton6 ToggleButton btnColor + Name = "ToggleButton6" + MoveScaled(16,43,14.2857,14.2857) + Background = &H7FFF00& + Foreground = Color.DarkGray + Text = ("green") + Radio = True + } + } + } + { timScroll #Timer + #MoveScaled(30,54) + Delay = 50 + } +} diff --git a/app/examples/Drawing/Tablet/Icon.png b/app/examples/Drawing/Tablet/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a526512bb5a2c1d369e4aefc5d39b60c5914ee9f GIT binary patch literal 3753 zcmV;a4p#ArP)X=!zvIGvOthUTfsi?^zpE59VL!)NHB&al~h&g&L35;@=^&QRU}}aSMZCSds8*%Kb7B34^q{fiK^zTQZ?uQs+#lWGYMYRoO4voDXQiy>WClZ zXCQzI=s>hBroF&uQ#X!>>FeKsR{;t5DKT|pY9m1h61f&G|5} z+zQy^@o?{)dzf|8OcX`2gs?<-TGgC|rf#$;f~q+u11m$|72s}DH(ownOlK_zsG5@i z#LK|%tm94ICf2TAgO)ikc-XtL*!R|r_E(65W4a?^^pr{S_8K!Q$-;ub^ ziU2nFmB5VPmn#aJH*6v`B_(*Rh@dD62hD@by!B>WE?4jr@P9x*Q#Vco@<(t8**+(j zx>3~8*t%Q*8~hd^n31ESqbV&a;r#Q@M^RdbAtHF2nwWm$biO!f5FHh5iQoe60^V%{ zKFidNZQ;Zh9nQsP0KXH+vW~~b#!^vIMsjj;oABW89Z`zHjOjD@{`>FIKi7$lj#hve zU`iYC|6=OK&TwPwG68Jxe*=bF$K&JU*|xQuK7IO}65cL?h>(+&O+rEfyLRs)Dk{pl z$qqlv)QwLfiL*;)0b8~|1Pl&^@7=riY2oc60A!6EO>AsztJA<7Q#bZS8f&NLfB<|n z@D*^0^&Ro?@oX!tASp4i!|;|JG!HUq%4DKrT6I{f2V|SNQ4vYJo!SEe@NvMmc6dj8 zJli9LKcFArhRKs!z{khOTYp-^pj_3QBhRS-0r&*qJKzEfJ}xefiml}&MgYI>vwcj? zpW349D~dwV2b);EvJkJ=YZqa=syVMbCjtcEdjM6yc^15t?URy{!iN9&laI;2v4FU^ zxZp7np=5J07o2|q12Z$(xNbwJ2p_4M^RjTh=ZtfJ4gNczm*q1iI);kUGWz!E8#erh zJ3r*sJ8s9}a0HJv`FxaYDk3!{wZ*ljX>!AqNyNm&KyyyG6!_L!F}7!C)B^(Wy@11Z zctuetE#4XteEGJDR`6c0m(A-pwt@#xRh3QaKfvSlKyyzx1SEB&rVz0JHu&#=o))~% z=c9Pj7Jk*vBIi_ARJ?^d@0#5L-sAD`!P@oo%g8u&Ed5p17p}wYc3UFE;h%-#PA5V{ z0tB2U;&1sL7QEN%rD($@F1h&Pu;JINU(1|(?`iP>20o zF?1NOEq)bOvj~@}n)8$PLU?nhdK1+A!{fO?vvcNw&qCnUqi3$&_E5q zcf;TEN!EQXmy0)-yiRsjc4UDLA8L(REPt=OyohmQ$3+Ye%{7HF7{r z$)A?rp=s_+?24;KaN^_%`=M#3Zmb1=>$V5~g&p4K^O2L4%@dFRF*we`vg&HM?KY&c zl6`B}LSCM|6y9YvY2rjytbEV=b1M~xqTZu5Z5UM#+jjB0c1n#gqX=rHRU+MN+LgJZ{_M-Q-8f}PYe^v`u>%(}^1WLfKgL8fl}qvl9;@VekP zshV>Ru*mMj<#O@j{1=!!={EpelzIu>yLZRq@vy6MC&}lX*YVz9f$iT9Yu3QVjquGk z{+`jZCyW>Y)26{~w?R}?#3G0YUw`v8c@xHk)(EaLb>rjE>+J%F?P6Rm7mF7xWc;}C z06aAJVOFnQLrhEzv9YlvB_*-D(qD!TTLb`~4^EzhBS)aJ(l1I$3B2$E+<$+>@IUUyiOB5t6%~mjCW?$7AE7Is)YR0-z#)Sy4qxCizeDP&n)4l1b4rG@zr3_? zK@0daHABAr_FF%w&nNu{4iHsyO2151?tAco$VF&s64|_2WcYBAs3?(v14Z`ii3tA3 z+FH46*pN{8J30!FsyUNX%_+Z5y;!_nuZW1;|KJ0Xs-{cT_fIp+o(DM`$9#vE#=Wm6OHElPB$`UNLp!p>Vg?wWA|s<(mzf1AJ`upk5;1Ge_+3)^iP+Rerd96_wJTs-0KzDx>aP%7?G({{nKC= zW4@}YN-_s)p<{k!Al8T&+bxRL9I4*XC%NyheLmmifgKvbmY8+p%wR=GQIudlNKQ`X zr`n%5XdYzs+SLrq97t+v;Ej%=KzcgNngvs)Ky=58(*OASYw{+H4Xqz+Fm)sUOj`c5 zB{5SsW&`W&$2}eoxjDIQ7BfVI+?-rI9uE$OgFEk<&6;&P@wR@&x6kfJD*mX)@6r|)n$cxmAR9(i=GeIw*) zZF}W*3U0cw#Vc}Po2eVQot5x9^lHo|LJm--C<;rLFXMoI0Pwe}1$98zS-N~#P@Klb zMtUbD5v4@2{(}unpE;vLBD`N%$nCT5Xz|hn*kS5MR%d4Wu;w9K)u;p*<9555HFFlZ z**O4|m6!4Eisiu;bGcm1fA%@1O_|E0|MeK_HmnDrZ=b%bD_le0zI|Kxmq3m1`IlZ` z(JPBX;dhz3acyT$bzxT>Y$6l`c_MZsRI8M< z+tiI~y0YaX-XUU3j5C10SjVrq`syJ3!+)5|tFJ9#N5ytOyrM96;sh=@{{l+4l#rR3 ziHOjxTer60cXc&u=tH~juCz`q&a0a99$;YrPx>>biYA|r$CH#s1o4O-&8gXOC)Y zI^1FE#>lhP@}0U<)YOedz}T3W7$qtyszp7ay1JT@O4Z zatrxjnD5M)neR9MxeBEe@pxSEG279jM|)GL)VxtDIDGi<#}+&uk7JtV7GMc%X`8QYs+0Ho(rU z8<{}`$O^wAm8Fn{fgnK^07|I@;JRilz=pyrP)eYbDvqeAT7_TwznT~3Kq&~=g?>zv zjRVZwO!4+#=D2nvO**F$4u{y@+s5<5?F{u`B_Z66T@oZufYg!UWn5FR1dFwxs$f& z9js(CSaugTW**^YQ+WAhJU7GTf3NWUg$%D9?B&HhR^8yG(Yvl&{#Wwr0z6vE@XG5a z=^6esc5^F2(-8=ilC(y1IQcwN|Gb5zIfRTQ(uq6#>^Ba32m5*WV2Q+oEFs$>Xz5sn zj%f&Nvs#&k3XEO2$mXGaXu5&tdKjhvp&>L4%Wg(D0`%>Q(%un6GYssYO(c}#cNgbz zle4_|*59wrvuYcuPF3&BJ&acj+K%nV5x)A^%wRn#YCNJ}; zFMXSbo`Ws@V$gaUbq#=P*Kf0U=MJCv%n(5XethT!oV-u0y_sCzW$cgd^XIAC2m~O| z4U>H@eV4_B8KT|Wxt~~I@}iF}2if1N6BLq0RKy#s0EZ3?6Kf3?RnV|Q5w`B!LogIU(+rj#BzgBw@A1~949mWb5TcwKD&V>WO6#m?CnQ9T+ZXm(!C&X^LhUD_e*>@6Jh`E zE_Oc=rpa1o5iTz;)84FOhiry+bXT2Rm8YC4UOKyaBRt*HijO021XVd#AI3p;qSr-fht z^0!P+FEBjxBp-gX$Y5XBnw<(E@O>YE0|yRZ7zR=nj$@@1rfDLjWMpJyJdsGut_c9J z>xnSeZ>8z(j`6|OIi_ds@||x*8?J%My&j9j$}(5li4!L#&YwU3GLUEp(9&eGvnR~p zzyOJ*O}wzLgZ{qmhO$-ytgNh*)d&O%yW`lgV-u%No%$wltJJ39i)i<@F#U(0W4Ql5 z`}b^GcU~*ab=|V;j^i*gGBR=c^y#YL4HWi>7I0vsuobJv(vk+_}2JYXA(xsC&{q4nXPUn@lD-e*E~a z#>U2e1T3u8rV1dJ%l+Q~B^8exIr5Y7@$nx3D~;Ne0pjua{~WlUjgOBXTQ4}ke*oY4 V*+a}sDck@6002ovPDHLkV1oU`2~Yq4 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/.directory b/app/examples/Games/BeastScroll/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/BeastScroll/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/BeastScroll/.icon.png b/app/examples/Games/BeastScroll/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7de9e813eee880887318fe133be91adb7c569805 GIT binary patch literal 3371 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 Bc>DkW literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/.lang/ru.po b/app/examples/Games/BeastScroll/.lang/ru.po new file mode 100644 index 00000000..75375e68 --- /dev/null +++ b/app/examples/Games/BeastScroll/.lang/ru.po @@ -0,0 +1,56 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 04:17+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/BeastScroll/.project:20 +msgid "BeastScroll" +msgstr "Свиток зверя" + +#: app/examples/Games/BeastScroll/.project:21 +msgid "" +"Parallax scrolling demo.\n" +"\n" +"This example shows a parallax scrolling with a music playing in the background. It is based on the SDL2 components." +msgstr "" +"Параллакс-скроллинг демо.\n" +"\n" +"В этом примере показана прокрутка параллакса с фоновой музыкой. Он основан на компонентах SDL2." + +#: app/examples/Games/BeastScroll/.src/MMain.module:190 +msgid "FPS" +msgstr "кадр/с" + +#: app/examples/Games/BeastScroll/.src/MMain.module:192 +msgid "Toggle fullscreen" +msgstr "Переключить полный экран" + +#: app/examples/Games/BeastScroll/.src/MMain.module:193 +msgid "Take screenshot to ~/BeastScroll.png" +msgstr "Сделать скриншот в ~/BeastScroll.png" + +#: app/examples/Games/BeastScroll/.src/MMain.module:194 +msgid "Quit" +msgstr "Выход" + diff --git a/app/examples/Games/BeastScroll/.project b/app/examples/Games/BeastScroll/.project new file mode 100644 index 00000000..5006ab9f --- /dev/null +++ b/app/examples/Games/BeastScroll/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=BeastScroll +Startup=MMain +Icon=logo.png +Version=1.1.0 +Component=gb.image +Component=gb.sdl2 +Component=gb.sdl2.audio +Description="Parallax scrolling demo.\n\nThis example shows a parallax scrolling with a music playing in the background. It is based on the SDL2 components." +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@localhost +Url=www.endoftheinternet.com +License=General Public Licence +GambasVersion=3.7 diff --git a/app/examples/Games/BeastScroll/.src/MMain.module b/app/examples/Games/BeastScroll/.src/MMain.module new file mode 100644 index 00000000..831f1f99 --- /dev/null +++ b/app/examples/Games/BeastScroll/.src/MMain.module @@ -0,0 +1,215 @@ +' Gambas module file + +'Public Screen As New Window As "Screen" +Public ciel As Image +Public montagnes As Image +Public barriere As Image +Public sol1 As Image +Public sol2 As Image +Public sol3 As Image +Public nuage1 As Image +Public nuage2 As Image +Public nuage3 As Image +Public nuage4 As Image +Public arbre As Image +Public fireworks As Image +Public scrolltext As Image + +Public scroll As Integer +Public scrollb As Integer +Public scroll1 As Integer +Public scroll2 As Integer +Public scroll3 As Integer +Public scroll4 As Integer +Public scroll5 As Integer +Public scroll5b As Integer +Public scroll6 As Integer +Public speed As Integer + +Private $hWindow As Window +Private $nFrame As Integer + +Public Sub Main() + + $hWindow = New Window As "Window" + + With $hWindow + .Resize(640, 480) + .Framerate = 100 + .Resizable = False + .Show() + End With + + Music.Load("b-title.mod") + + ciel = image.Load("bgd1_ciel.png") + nuage1 = image.Load("sprite_nuages1.png") + montagnes = image.Load("bgd2_montagnes.png") + sol1 = image.Load("bgd3_sol1.png") + sol2 = image.Load("bgd4_sol2.png") + sol3 = image.Load("bgd5_sol3.png") + nuage1 = image.Load("sprite_nuages1.png") + nuage2 = image.Load("sprite_nuages2.png") + nuage3 = image.Load("sprite_nuages3.png") + nuage4 = image.Load("sprite_nuages4.png") + barriere = image.Load("sprite_barriere.png") + arbre = image.Load("sprite_arbre.png") + fireworks = image.Load("fireworks.png") + scrolltext = image.Load("scrolltext.png") + + speed = 2 + scroll = 0 + scrollb = 0 + scroll1 = 0 + scroll2 = 0 + scroll3 = 0 + scroll4 = 0 + scroll5 = Rnd(0, 640) + scroll5b = Rnd(0, 640) + scroll6 = 0 + + Music.Play(-1, 1) + +End + +Private Sub DrawText(sText As String, X As Integer, Y As Integer) As Integer + + Dim I As Integer + Dim J As Integer + + Draw.Background = Color.Black + For I = -1 To 1 + For J = -1 To 1 + Draw.Text(sText, X + I, Y + J) + Next + Next + Draw.Background = Color.White + Draw.Text(sText, X, Y) + + Return Y + Draw.Font.TextHeight(" ") + 4 + +End + +Public Sub Window_Draw() + + 'Dim hImage As Image + + '$hWindow.Clear + + '$hWindow.Clear + 'Goto PRINT_TEXT + + Dim Y As Integer + + scroll = scroll + speed + Dec (scroll1) + scroll2 = scroll2 - 2 + scroll3 = scroll3 - 3 + scroll4 = scroll4 - 4 + scroll5 = scroll5 - 5 + scroll5b = scroll5b - 2 + scroll6 = scroll6 - 5 + + If (scroll = 320) Then speed = -2 + If (scroll = -960) Then speed = 2 + scrollb = scroll + If (scrollb < -640) Then scrollb = -640 + If (scrollb > 0) Then scrollb = 0 + If (scroll1 = -640) Then scroll1 = 0 + If (scroll2 = -640) Then scroll2 = 0 + If (scroll3 < -640) Then scroll3 = scroll3 + 640 + If (scroll4 < -640) Then scroll4 = scroll4 + 640 + If (scroll5 < -640) Then scroll5 = scroll5 + 1280 + If (scroll5b < -640) Then scroll5b = scroll5b + 1280 + If (scroll6 < -640) Then scroll6 = scroll6 + 640 + + #If False + + hImage = New Image($hWindow.Width, $hWindow.Height) + + hImage.DrawImage(ciel, 0, 0) + hImage.DrawImage(montagnes, scroll1, 200) + hImage.DrawImage(montagnes, scroll1 + 640, 200) + hImage.DrawImage(sol1, scroll2, 420) + hImage.DrawImage(sol1, scroll2 + 640, 420) + hImage.DrawImage(sol2, scroll3, 430) + hImage.DrawImage(sol2, scroll3 + 640, 430) + hImage.DrawImage(sol3, scroll4, 450) + hImage.DrawImage(sol3, scroll4 + 640, 450) + + hImage.PaintImage(nuage1, scroll6, 0) + hImage.PaintImage(nuage1, scroll6 + 640, 0) + hImage.PaintImage(nuage2, scroll4, 82) + hImage.PaintImage(nuage2, scroll4 + 640, 82) + hImage.PaintImage(nuage3, scroll3, 120) + hImage.PaintImage(nuage3, scroll3 + 640, 120) + hImage.PaintImage(nuage4, scroll2, 138) + hImage.PaintImage(nuage4, scroll2 + 640, 138) + hImage.PaintImage(barriere, scroll5, 440) + hImage.PaintImage(arbre, scroll5b, 140) + hImage.PaintImage(fireworks, scrollb, 0) + hImage.PaintImage(scrolltext, scrollb + 640, 0) + + Draw.Image(hImage, 0, 0) + + #Else + + Draw.Image(ciel, 0, 0) + Draw.Image(montagnes, scroll1, 200) + Draw.Image(montagnes, scroll1 + 640, 200) + Draw.Image(sol1, scroll2, 420) + Draw.Image(sol1, scroll2 + 640, 420) + Draw.Image(sol2, scroll3, 430) + Draw.Image(sol2, scroll3 + 640, 430) + Draw.Image(sol3, scroll4, 450) + Draw.Image(sol3, scroll4 + 640, 450) + Draw.Image(nuage1, scroll6, 0) + Draw.Image(nuage1, scroll6 + 640, 0) + Draw.Image(nuage2, scroll4, 82) + Draw.Image(nuage2, scroll4 + 640, 82) + Draw.Image(nuage3, scroll3, 120) + Draw.Image(nuage3, scroll3 + 640, 120) + Draw.Image(nuage4, scroll2, 138) + Draw.Image(nuage4, scroll2 + 640, 138) + Draw.Image(barriere, scroll5, 440) + Draw.Image(arbre, scroll5b, 140) + Draw.Image(fireworks, scrollb, 0) + Draw.Image(scrolltext, scrollb + 640, 0) + + #Endif + +PRINT_TEXT: + + Inc $nFrame + 'If $nFrame = 1000 Then $hWindow.Close + + 'Print $hWindow.Framerate; " FPS\r"; + + Y = 10 + Draw.Font.Size = Font.DefaultHeight * 2 + Y = DrawText($hWindow.Framerate & " " & ("FPS"), 10, Y) + Draw.Font.Size = Font.DefaultHeight + Y = DrawText("[F1] " & ("Toggle fullscreen"), 10, Y) + Y = DrawText("[F5] " & ("Take screenshot to ~/BeastScroll.png"), 10, Y) + Y = DrawText("[ESC] " & ("Quit"), 10, Y) + +End + +Public Sub Window_Close() + + Music.Stop() + +End + +Public Sub Window_KeyPress() + + Select Case Key.Code + Case Key.F1 + $hWindow.FullScreen = Not $hWindow.FullScreen + Case Key.Esc + $hWindow.Close + Case Key.F5 + $hWindow.Screenshot().Save("~/BeastScroll.png") + End Select + +End diff --git a/app/examples/Games/BeastScroll/b-title.mod b/app/examples/Games/BeastScroll/b-title.mod new file mode 100644 index 0000000000000000000000000000000000000000..8eee2218f08f32104332087ef5f84dfd06a1f84c GIT binary patch literal 56838 zcmeFYS8$}+btahJd+)si5}64kyax&>UtMk3&8BIJVyk;aYN?qBjW#y+VPhZmVI%fM zF)=$4GZHl;8j(%5tu9{$6ufttNC4@*cS!HuW}*tsVpmp+8U@MKiuWP^%>Uf~9`l`Z z@A=QY`R@DQ`wxGu`s*M3^$*_%oVR!XH^E>0`#*pG2Y)5_;SYZJ7w^CKz3>09Ns{9L z{gePn@$diY2Y>#9fA_r~jtJgU|F7Tw!C(LN_x|Vi{|dmR0j7{kr11F|zj?(h0j8RF zfWLY7OTYd|ecm8{w8kH;@w^3|{|?0e9{?Z-41q#na0C*C#$cbP{$9SnqQ0&MjSv8U z69E7;!UF=5laO=9Kv(lV0RQXXAD#V%p3|RRTXanCukYd8(Yv40Tm61L-gD1)J)<}K zwWZ6S(VP8Vpf~wp#LwumHuRQ!f@gH`^ZGr#cP2llrTCc|$ykvaxGkUXM zTlrdsYc@=?-wX7Xe7F`B`2CmK7js7c-_Ph@;L*DGbUX~I^$qwGpD)u}_CcJ{TX;T) zZ|h#`d613(0Aic?v)a?2_B;P)KZTbN^`J-|YWwXUKp>Pe$Vhfi|=;y?q~WnuP=<(vQHD& zR(mPF=keddx1E3g8s8V`t?>nC!?(w`Md!5iZ(W=Cx0C;Qe80@U^?VbzR%_whR=y@~ zXY`lh_lJ%Da#O72Z;gLD{eRo|{~y7>iN_c5|FZdA`pmvfKWH0&=l;QK6W=ZTnsF~^ z4uN5{p||*-$3LwNzOCoa{i7AX?fmCF{#*FBqo2n=_9f$=`A3saGtOV|uNMFF_;1E} zTl!i2H~Ycb=yw+Xt@RJc{mu0skZg+k{CFp4zuU?G+v4Bs*H*ss_8<&{oqp%|zHI();@)a4KWHmo%Wy4v%Rk!1&r^Kw zywtwUdA-@5oYC7o{}lhH_feTX?q7|9t%iV%yWtcz^NHR{q~5{w?_xPuah8 zeULt*|8f0q?_X#51G&G6?-}0B{^#reS-iLLk1wD<`_gv)dWQcW*Z(&0-SXd7;5OS! z^S3{2{6Aa&e@pzoJij!5|JLt+-EHjCdj5R=f7$$g9{(?!-_PU!kL&+e#DCMyZP$PB zH=6F*T>t*~{{IJF|DVne&H3W_)>>~{?{%&A()YXb{I6Yp*qVQyuKxfGyxsc$Yy7{o zKD4j@{7d4$w;lhceCP4s-G<(h|2+QN%?~a6oX7wF@cRE;{`Y15TKv!R|Ci-|=lNkP z|7-StzMbKBp8r#u`n98<=l|q3`nU8u&;OeMwavfI@o)M`Tl!bxf0q9>-+#YcA9$+8 z3*)tuzyE1pOly8`M{nM5)~8-lfBH3k%}#Cek7it+wX^Zt;nS?EG{^scn&xS*e~a+6 zwHd!v2Wi!R&gy4v@i~9Eb$!7eEqu=PZ(d)ZH|^1qyVaW4HvE6Bf76fK^8Z4AL7VYg z{Ll1%dTq=9nf}fEqYeL7-SMpc^QHXoOyn2!`Lg|=*T1Rf%kh7CesLbZO^z?h*Ro$z z?^bJz|F8AmZmHRp|1oZ}B@y+6H3Z{yD`S=ML>1b#8 zx3jO{8NGRZ0pDNif93~m>Cg0UkN>&;&-}j)|EJuaezvvmx&L>+#Qx3u&G>B9uV1up zOa5p2H#@bb3tG>=1plTzpZfo^`gt4q&-3r*5N*akpa1^l@!yj11^c(;d#e9GIsY~N z=&R$uW#4xCoyULEjxW$#{F{bvwX^)@h3Eg7_`mRcbKKu0{w?_hXX5`8@&6V3C(rb6 zj`CIbW1HPxq&N9L-(Ik9(;nygxB9h>k0x$S|9?8awi)l-|IhGmXJ0|<`6i#{IBofV z75**xp6UP9_`hWS`?5Uk`0Kiksx z>EC|*^Z0+Je;fUta)0{S*8b1*Z}oVA-jeSO|9^V@`@_cnZ;5|9`~EKRZ`n`q)b?%h zZ{hpY|6g7|edhnoQQG*|`TPg?zna}%r2iKFa324y>&xT+%n#bn@6u=bb+zH&((hdV zcKcty)*pb8wDG?d{kMz%r~6;c{?E7P{rRlE-NqhY=-=A^YDa(S{{Rf>CHkG~-!?ye zPXBiAH%)$D36#g&6SNhb#&H6^$`1?Ztm*M*~{#*5p z_WpOSKlthR*yjL!iq}*9o44EYf3EwB_&%q9tKV1o|JnHM@O`HL8T~nXpN-cVU(ovY zw7uY8E&4P4TRqy+pXuKuzo_3A`+vWe`d=IVP5GO3omOkcNqc&8-aDf=`@NuF%fJ8i z?Ekgb@2tLamcO=-|K_~+?EUFQ{ag5-)qnm~od0RB|5xGP!uNMx|NFh2|7nl!@1_3t z<@#PL?`g%;`TpO_>vJvdJ8y00x8HvM@2l~DS^je#|LyXh=i^_}W`1~X{%p>p?fL)y z&i}OM|A&qL-{<+C_WGa4e>?o2kNn|1yd*2k9r zpV$A}?*E<53(ft%-^=-*ug3r1^!g8Iy}!2ikF)jv{}<DsRkPuKrHp8xrst^fZv&;OvCbL-Q5 z$_J|m)g5AS&$JK->|=I<@Jt|rL(3!z>&ND#`*7vswM@cQN!BE4IeqvoCcY}V!6!zt zJscPUQ%argXz;2qix}$`6aX*<7mAF*!J&htNN9C^Vae+@ zr{E3ZR0m8ueGLj{1HZvD3lOSmk==m!O?I7s$GHEWQpdYhJ#`$xR4ce|s1Z2YB&vw+ zB8!uO4t6MJyB9p&y>r*0cka6IXe~&?kX^rwtO*8%#X4IiClP`PD$T!s*BjgU`9jX~ z#N*2ICsbH`U&j?8rJ&)!;oa;(csZ6MVCFrRMT2GY@v+5ybbudF4S`sHS|_~0Z730N z4zF{x5XeO}iQB=@9pn9D)A51jC;{%)zEx#jk`S2u0aA@9mUQ(L0p0*Cd=%N=da!G? z1P#Y~KG_UK-8WqAWDSs%EF^)$85(d#3MqFWVcFkV)SJQwi>`2y!;lYN6%%OuP9;qY z!F7(~*mnrr`T)}6?;XgAW+5NN_D~I-+7z}TLZDX z2Z@*o4m?tJ52LlcT%iCZdHoWmBZS42gLYfw;c7Y<@ttfLoMt>am=JY!QibC(w2ZBN zZ5%DE7Ew8SXwevcV1z|YX2X6klZ_`jc;qoE{lZY3CmFpoNM|Q$;4Oz|$CLR-SHo@H z4ZHV(JUe)b2T_t}H{>PqjJE6YU<^!j=xqU`PrvG|M)!Tb!zX1R58H)fQnezA0>hi? z9GGNcigl~WSlKll9w*YrCl<3k<}8b+AgXf-6ReCa6f+-(0f+)aNfnk zRM6(kCdPWuc*z8e%%=!1F_@g25-{j+H4N9U1dJ}o&e1X`c*`L|c5oPkS2SQ49CMA+ zt>X0J(SY!}C2uor#eiE|0iSbYtH$TT6)G(cr2&9@W$5lJG$jfdJ&1?5vgY+vZpRf^ z-#3NeNTC8Sq~$_J2oxfZH{L0zVv#N{%()e`9~DeSzwThuL4g$LlFlx%s1ra#cB)yj zOZaR7Q%mWbCXcV=Fqw=N{dy|IM3Fe7Gcv^~g2z|UG@}#}4s6Y5PP`@;?!;x&2agt9 zR-0-7$dzlaAy9adSk@tx@_SJ$0UyNfIf)j6?!ZFmNO!VS8x)BV7wH#qj2vI0q75^p zn)UJ=iZl+{{)Jul5(IV2Uf~M?%-0`}N`S5Axbk9;t&E2wP z*Kk73Odl1>8pPv76#URz5Wd{H>2>MSWS7(K4^|xaewEG}FdEhL6tvFiXT$J(Zr7`9 zI@`GIu$bfEykQk(iQ6_F6#RaYa{4;AhKKg|6uEo_ae9m&D<402QpqC1t9F?AXfu+S zPnLn&zq|x1Bs!!+aHx#e&HWZ9Q~vm;aWBpmwfYe!pX(jPyxGcq_nTxkG9nNN;G_$E zau#Bkbo%2T1)YS7Gaj<;|0Dpk6*CZ~_WRm&y3A4$V2b}qfn&1RAn)@3?Sn)r=yhA< z&X%VXJ4Hbi{eQxXGd<<2G{6nbWgL@3sqQ>p^X@pr^(Fo3!_ayGk_B|Jud}3Vkf$ak z4!nAk0p;{J4t4tDk8CJHdGpZv(Lp(n20*8}vFxd8v7AM#zxJL6CaEeK553V}-Y+!D zNx${sJwGUeC1S3KDuN!eD~Og1{^ zgJRnFU&+NPX3>>SJ_sLd?8T7P;>O>efb&KEh!o9EF(kZ0084T8of)23XtTsUPhzf* zOu$+${@IoT=MAf8)mU^5$VFyx(w_0RZ(_B4H@kfkfx5BRn=*#eMa?AvC-+zi*3f zx`UQnB9bq+{EXh~Q4c@la&xp($OblVM_o zjMImFg)2_H3+pZw0KyAWk{zvaMd?P_*B@Uf5~As zC(yWzE>tT%e)M{+t+?wnTI5HM-J$U6?L8eD24jwi zG<*$3G&6|hYh*0&dN{LeJJP8n6a*2JRR=^hC%wG zKMJnzV3Uvc^mF>d{BcG=qiXtl`>uB&FZA%KU`T`>KaOAnL0n=XU7i2<=)fFH``vw9 zZ0{_5_Udr;#tZ<(h{Z^-WnlRvOOM_Goc{bLhlS00;utRCv%jXok6q_zMAxzef};)r z6z#!a1CrP2Zf*Y}9ykQ0GB|wC4V3~Zp1Cy4Wo3w*M6O=Wn^Lf1X5Db|k6Y_ey@dp# zL0G-A5w!5)Xiv8SMJ6<|Nnpv}NQaRJn_Gu>=ZudKr4kM#lG4Uhc=7nus1~OJhj>LR zHFFxyM%Jw$|7nGr@!V)Mu4HsmBsr9PXcvo zy_-SsbSdK?v|5-AoCH7Nez^=}VxhgF>H z>H5x58W7W$iJ{eBZWkW!tOv-P0V-6WWHLk}uYPSzIn1Z!&7aTbOtEMqL=AqjxqFbk zKZl_UPznK^j*<<&aaDVLnmCC*+`qrJ*H}0efJ+-2z9s+qmKT!g>Lm(>ousZ;-kTbp z8tcM0wk`J_ZLg$QwDPgb?B6x4t;dK^qHtswR6=z9>GwMZFHd1vk*(c_^S@li$Q8Lq zhqdG__raEf#stv6(Sa{v)j#+P*;uzo3`%{zy7SY!ZUP?xS>8r_*NtmIr=pDQRb2z& z!B@Zk{Z5T+0w30`Zry%-*CeE4QdytPH)q+)C6go}>rbW&Rn?n+d6_oRqo&?51)lu< z{W&LBf)9BBN!`JxPC(HHACtc^fZ~Y1`_2V;-?$>Q`B6^)v-`g)O6bI7IGMD4G#>=v zR?(L(%&5^LW1VkI^Mo_>^+&sjz5lwtOk|^xerQ&Z@r@A!v#G?VQF-A-5<21O?@{|AULwNvF^w8Bl^|k9H zgqS|88rybzk{~E+LFIA{n{(iehLqjumS=ia6I?a9h8f%RBNp z5G{RT`e0|X(u;)+-0Yp2Y~}-~s$ATi#~W9+B4*doX{JDRCD1F)Y(IiVOf!0Jj13KO zY1(3bhJY6OqGr>9+YpabeH9PQbgO#n7Fmo@^>^M>j`y%+2+v4&K!$aBBVOmKFPF(C z^x4YEo@vvLrU`rcE)I66x)~kCJWB#4mCKp^pzqjSZa7>!?n3g(fdc}=TpH+BT%<}T z%Jk-6CwK&=4V;M^ZO%Y7y_PkvxZOrWg-OPUUmH;ji0PC9yAKXx-~iDavaoejK8{v= z=J|!ZVJFQZ5IdD$WAUzZ1IlEA3WO1n{1z+G z#C1u@RY+&Vj3<^CH^UKc-nn{zUSH4GhlyhCkZ?qWM`T5*XR(BjcMr|F(H?tk+8!!c?X4rO}qEurmdyW?vv2&QU>LLcMmhhX4zmgqC|tj zWqbkU5}s8|IJb-D2WHRO#)csab08Qj{>Zq7fr3Y&2nMecBa?|F%<56lzHPD`K3?*g zcjF}vvf9ZQzrjSrhy?~RuN@Zhxc#&QG;nwnSaUu4*p&3>(|NoA%TdfsH7u+KMpgo< zugn0@ot?>W?6GIhyuZHe@Sm>jY5HndSx3jK4ReXfsUdjsKfUgf2r)pD?cRRW_i$dP zi)M`l5}w6`%_@1w6I54`C{T=F5)u&@US)G*f2(BpAid^1v_*0q8XN)A*Ck8(knFs? zfBf1cp$;X2vK#kS5}8fwp5A%tTxAngGH_Ki%kY8G4WVR0J$nTW!X(P>quU2L*IvNB zrO!G{*nTmjB$3F0r+5fXGBftNMBVLim9Zlm-a8O!G>pu9h&rE> z$==d@&WnfD9P_@adv7P1^l&?=0_K21qPY3)l@3y!SW1SM@9OkfNF)U}nA|5}dn9cP z%BEm4I{&7U{mOSHlt4~C8Fegd@3??@!zts~w0@Wg6w|JdYFI$wNe2|2?~ja$n=fz1 zx@m5~6{rMlaq@2X=Z^qN|DvF|k!1{AgxJj-i|Li|LZ&8C+>3>9iQ zMpQXb7%Zy!}=|$rXZ7|5EtAMKccx??;|JA)( zEKeAxA(-8#YBs6=H5&WM6o3+s7VNRrC^))vu$zeQtyltnF{WbxL872pgPIPiKzmI_ zt_8rwWnGw1y7R!7cN%y1{I&w0E0+$_0xYdS$wO#o1~_pdvU;+iD^d0Ht0!rP{jU3z z=E8PMM=wW`_)gU@4%YR$oazFS$O zl(GttEH#ZKgVN0GIz3&Y){OsSvz^ACgRM&!X()pW-m zOeB#gK>FHcCCE;_&MV+vpOx{UIbz4@kMHH9)sqJv$oA)vbTq&vq;Xd#us|^W5&$82 zPcEQhlYFlJKRN*i;pnb2v9MT9xWK$LN_ts}ffA52r>NK8VNisWEY=i3qfdFiaja8GF3y8@r$^Q;QnUZ>#6AX%m}oz z=v`Y$Mx!Dn48g)Ol}aIX6d``)8)S%O*-*C~B*IZ+DI0=%=66GZ>Hy0#z~skzuh2Ek z+SmJ+i+SHYT{5*3ulj2Ra{%J`#d;}~p-+NEtY%TKzf(Yz_0DP#w?RkqS(7&-GUwc> zxXpNHuLO5VFTtcLPM~*4OQ&^Sm(v>-#PV&|Zh$IF@0yFKy}50J6PjgS8SG~ufy{69 z;Zd~fLKb){x7KumDnsa5u^?UM&v$kqa4Ka?Jx&4CXU4^Nc0Wim6=zN>v;)HOK)j_H*U1v#G>r*D?lfFO4@8g?`@{rS$^Bj|f?Q=~$zkjVtf z`Pl(P5+ym^HCD=A2PBd_bgb>nc^bu#YLM7;_`XF@xu9K`H)KUvtlXG#|E>{+ewoh}ZBN}wpq zE)^XUO9&G&!ci(+bseuiG&w&w@%GYilOwVlU40M?TEI0SxPp|03WPv2rBi>#y!g;> z{N;MI3hV6c>YMEzp-^cEN)$=}(sIZg3LgiglDX}*E#JenJwqi$l8=l`usbiaQQW+= zY3gbPU4|fuC1}{2)!o<4ZLfy>ZlsELqf?6<8xd3((mEVg27uG>3K~g=xO5)V(&EYI z3*mIFli$%N?xc(kQviH=PJ+tjGEh`0&%@V`3<1;9=0}hBh$S#g(ce$)lk~BOI3ZHN zjAbhoOf^WS0y3#>^X|rjAKR0uG^AsoMqO%jHQ(o-)3R}2R14#$sgxiZ@pL=~-;_tCm|)LuMA+F5E) za0M6yl!7C&b^qbv=Hjwm=dq#rHSy3Wfgxmz1UwoM4P#5AVK&7TCV&b?%fY>!6TMCs z!wE3B!Rrbrj>#O9VIg!VlbMgQ(7`Y|laK4>9-ruUc6}fOPC~rWE5p!o>Jfe&Ff6K< zfoUernuY|bp@aMPtma7>*(jleOwsboZ!2#T7Fx$<$M1+*t5M5JvQkZOJLt-xAtk4R;xZ9^G872Uhy@_-gF~MwTUhh?9~~x9 zsnae2;^KrLu^2t6q1*06i*@-UT=r?d>I!as%Il&bB`pQnImf!iv<4>0Y zgwcLr{ZjWU;(8u2d8v_SAPA7-8oX-P-`%W~wl_ZkCoE+mv%iy!9Fk58LtxgG$s!&? zq_{oV8pwVBVdONf`v(&#?5dD^h%;O!ZAc=`o%X*aPGP80Z`@Hn#c!S5clzo-UhS5m8KL1I-XI1S%#mV<_izp<-fZd2j!T#bgVD zDB9WX!Eq`Hi>VQma2#8q?usX!byLEgi5{(OZZ7OD=aRtA{*j^Kv3_(41)`JiJPuZ^ z1h~p}T{Hxuq>n?r`VL#Ok22aXbd{hOkmAt1C-QjL?@=sr;uZdgxycl-^%tGsk?Gq&v`xEMEQ zx+FKaph^`ncm*dS0_EtC!<_U!$>vVe&c#PN3o%S1CLf&{k@O3TRUlJ+0juWKxL}Xd zv{}|4*HIDu*2Be@J z<`J*)`>&1g2j#gk1+5@=z+t$mGh5uZ<(=rP$FILXe>+w>#q*u#t^PRay5Ci}|X=7&Dnq(XePQ_+jyVo5dbFo`Z^npO*k@j?KU$><9C2VQ@HYg)-$ra)^`3VJR-)y%!+3B#qkV-_hKR9r&olYqGMip2>pOyxsQbkwz zc_w&szgZ#x!vjYp#7TO0dCO@4DAW_ZSO#2vg#;Ir^tc;UH4@+Jyw0n9$q=-{|k=qj;6<(&Jwonhtq=89&)>Jld(d3=M{Q1f!)PQfg86 zYc~}QjDY%J@8jKjOJKePo^R~lPQ>Cq1GA3|AD19eFu3~a_b=nA@CNeMZP%k8IT1p# zpd6V$38#*#PVPvB&@tU8qj_Cl`(8IuNZHRFJbAcvXCI(qL(yiPFFR*LlYnd@MKe=L zk`%=6y@egY!t_q_le@;dC;47BAF{R*4IQ15nH44iuNgz9xOnZKzplZ^vzv#m&z5|5 z)&*h$HGJX;#y+m{R1g#f)!m5#2svZd-;|bm8ABIsu)LDL2I@v8?)sCII zTl2nLoLn^%YUPd9#|{7w%T`_KCG+uA0aPS;OT{iUk0nG_*2DHX5?Id_O%ERX-Czz) zq@C^)6DcAYh$MVfMlI@e7T^9n0GKT1Y8dOzlY>tpT{5!p!nGbEt4mbKXouB2;jYcO z@7OXGiNK%-hV_2F{E-z~P$;`6C#H$)u7RS1dV$wvip5M$r`PKzp}mfD!Smq3o)_C- zUzoa}8IiC!;w+r1L~$HJkZV323WWublcQqum0)i%!by(Z=olTJBI5>#IROy@WyS5V z-JN>Y7tzG^yQfDw&-Pw`tRQ{6zkBu)5s&3D2&h0*!r25SHi`{H4OTf`T`_OwiU$cH zS^dhOc1Fa&Q&dzG#$@b4n^J{n+K~s8Y+L&}<5C{xs+PL0jSOh}rXV!7r1?&iE!LVP z_e#u%+0WNE9=KQShlRXPsNieIdSs(L0HOkoEr8-sTqs5?hVtkIZ{p}Ddao&81_f1P zME0w6+EjmzFpJI*n&nfaH^6j+u+Ybr+Ub{MO|5-O)VSi~B;qf5X>rn%tL8n7I46Hs}>iu2*)WmDWv5%4-0 zQ)E6C%oUSGQW1%OER>=ocd=H02G%xD4nDMfQei-tQt`cChIZ_KgG#qy~V2Q zE)-%q+J3UIngRG@_+FI?*mofn0?LV#i^}a&-aOZ=t>)2h$GG)y@dkmg@dwcHIKKOP0-AVACe`FB!SLoUb}E?U!(T?}L3`F&gGmyl{)tjL zBba$lMH|HT&}DH;&2nei08SSkf1DKf!p0gLfj4*3)W8~s{raoZVzxqpDaAsOqs7m{ z(7eMO(8H~v2#6Fd;sNq;V2soI_S;HM4^tJeL2_$*+YeKyqH`-yO(aZa9zJetZugFX z(X8Vfqry}PD-W;<1HiYS@eDvYICBp}g_p>?~1gVVO*9CiJwknR(=XR)JeQGfj9SZMm zX5#)#BkNq7I|;0AIjcg}_zhu)ij!CIu!9^9Pr%GOss^(&Q7Uy1%d+L0r`If4841yG*x%Kw5eSW{6+) zEuxkssF|NPYs4p zn%VvCmG2N4qS=O>Ol0+=?yIfgStCJ42j~Zc?EvDGkzi z9CU-43+pl!!=5X?aJc)E<+LXN9oD|_oiQ9o3&X-dY?-(&B~a=S&r!5;xKlFfZarGf z9ykCP#y8(+W`E5%CgAuIHiXjW#NiB0Pt{ zrz!?eqghXe9skw*5yWo^9e55`_CouOF*Jzr_Vq!cmPFv<#PT75#J5+*HH$^6E4I>|vZhlTjp2gc}H zQms5Tz?PQ0@W^VazP0Dr-t$6jpE)9HAVx!dQ%3HY>FrU_V~`0YqNidf#P56RrZw;0 zstIR#tP39zWQn1-lmfz~0aX{OjOr)zJ7aO!{;|WAGUh*e9IKkw1B+e)mVEtnxma*T zJUs2n%MgvLiU>G5=Pa512jPbgN}1!G$cjgR=)dtMTr;F0YJ2k#E|fM+jF)^nCuz4i zY`=HEc#t*j_(0XczV6X00wqD#eAP!Kd159JVsYno*RzS##>S(SQg+3Vg~Am>+ON+j zFhqqyjf@}%*)UEq)kJ#RTTLu%d>qV_pBN%c39LP12pP(>uO4Gjr*MUKk=PL3@!phd2wP= zt>PnKgkCX~j;HWZ#Pq!F#F;qR%sK9C?ZtBT2B55Zdt5yNS9H|iYK{ueC8JPq&!JTp z3vI4~PwpQs6rEKpJ}-aknwClBG0I{!vy;um7f~eBrorvC8CFY&_LbYUG>8dn$iF#D z5mUQ_X(mtG1rvb*K(cAc-ds2z=pq5%Pd0J@Di1*$?eC*g2Rbp0I{h6j8kPj3>fXa+ zn{jLTCJrelLr5WH}7`%(`D%N%4<%d;wB#t)YFr z3)Dj(UKbk4uXpk|NP7e0ymy+~KQi9;U=Dr~O=KZj0floNf$2qxhxp~L8)ObOn@)zk zTg~&7_C0IdzNPax5H#pT37t*CjHuOM3Uzuy$}VKE$7{AsZsT)zBzx#w@i+rWoTTGo zM-(#B#TP;t<5OcW3IX8XT|0z$7q<^X3Ge=T9F;$kca2V?ZA^|~vZUe{dZ!caLi8%X~86`EGbL}{|0-l{xVOmA$PTpwi8zN8#c zVqF~OK=G%2q)gYlYDVuE4pJ*_&s!Io2W|AaosE5Tr2=MRP*ayFKmiN`Q}EyEgUcpa z`Q{1FJKMp1Lv&$x&1}hoswlj;S2hELiG)7%_>l0LP)x^FeA(pRuZH7mf&IP1h1FVg z1TK}(@xx;YF*QuNG1k>L01OkGN3ZPv?E_L}e`iy-cC>3HDum7BpR&uBazLbzH!jjl z^d|!eEZDXE;J6Y#TDolsZ0aGSYTjx%z5B7g5anH@&^dg{V84i{kbwK& z<{-+JT{QS4QH!oEMt36a2Ok6?Ala)CNw*TtydmfH6M;Q{3NM$ojAf!N4e;u`8#_+l zFK=gRHTtZC)Ptp5=$ue65sFSNdVz5Q?(|*B>wvy zhkJWr^&~8%@Ff?gwRL3QH@fiUPIS7bt_wlZjsk}gJXu)Z-FkQs+>4BeIgGFO@c3+X z#{dBVNL+cuR<<&C1dqfE1aN$tMDGFWl<^!;f#))@rbLFFZOMV2l(%p6aIycob z!^aaST{(8~FgK7-B@1O`#B5mHd+_Pn&oQGm-{fBo2>EoaNPtDFqC~DGDG&x7NDeaW zHE%9$-hMc5M=Iioi{H}1JG&KFDjv%w*3s24hM!BUXAkx(C>B{fG11Rt zc4KiA0>4h;W|G7_2S|wl;BN1|<4^y~)|{h4W3VSK$y6hCGMWyP3KKXXxRzv41AKTa z;I*tj_=jIKSA;11KsGg~F6|X6M7d`-Np?ISZC)-%v{i znLU*nii`ol)HpH{i>#7MP=q7AyZ)>DckX$NDXQq|>`WwZo_J$uw!ae(7XS(%Q3J}CF^B-Luz`bN!}pBa_x}6h>iqx+HZ-dp zX}$p9gn%@PkRv#0kyzBTvZUKI$#4~$KAvqY#mr4Fl9*a|jTj+2luV6w-xvi`y1&+i}kaz%1? zhx$61Kq503nQDWVk$0=1$T4V|PdK@A-}LCoPv<>Ae}OqT(lJP^lhxF84$MPK=!{M= zfyBgR$~&7&x{p8q)S=JOPC4TjMH)yQ+re@I*eoo+gQgKR@MuWMXI`0GczAaqm@q+E z?5?RU7@kU?Ap)>&d67OL09>PH(y(ywc;(6V?N3)~{y0vK8}5*^5wrvWR_FB4Nh%GO zD211uKwH*0|6pNn?ZBSSp-08&j$V2ZTLq&!DFVEj{T8i??6vRt0D;xVyAStm#!&K< zs_Ci|N0qfqxh|Ptqa;$vkTgTX`S-(#o#VaD$NLt4G=rqk6;P&%4s1l=Giok)be4r@ zp{k@KLt(#}-mgC1NJrf@3Wtwy_l)6z=f5~xr{7}bn!)&e4 zz&?O50PiMndj-VnlhXdn1V^F1uR|Ox8zcTaY-c;1syENBXUb$Klq6I4GT_&KFhJn4 z-KEpHLl114n5$>W-lp-lGz{jLGW4k>B4r}<8TD5MN!VM*`ERUGZ&8}G59 zENf|L!Q}MBEzUR|0K2mZYixw0^b(O!7Y~hhjxZ;_e+3MMG*5|dnb&~PxHSZnxo!tf zgPuqf&7f3i_>1E+eZ+6icCk^huyf^+!yGE6TwW~0em86^$3sChjfG)xId9$2GQKq+ zR25?m!|K+KEd)$Bf&!;M65NXTatS(w$(IuPU%jNB&}f+4f6$jII{o;jAnWz(&i>&Ibx~(l_JsER*a={~i))HdiD$H&TS*0bu-_IGO zD&yJH@Q9pjA1N4UK5=7UUTYx`jVUessHi6cBnB=7%)ETLU)gn!5hBJVtQk{_iT<0fAhRUI`66; zY}-ruRIiqtyFez63`(UVWaV`Mz+YPbY~Q_HHEsfl5o0K0UW?U{Dhf{90aab#4~Z4r zj($-pcYL(EyX37K0);{#;7j;+3r_NANBI&)$q^59tK<^78k)m9fA;ujApi^{YC+#= zw)xkbY(O%=qg;?9#f-iyA~APLk}Fk~*4O5Ca%npXYC2A&jDX!8F_#4$nPSjIyx!{? z{%i(LDdy%LTbK6}Auu|5T z>bJ)u7?R&`tlzO))}sVyD&<~T%14t?0fNmK5cewZ+#%uAm;?-kIh!@*J)OY>LBx_a zo7HZy*2GnO51x8+xG3Vj!Wm{rl$_->r3i=Mz~&!QCdc{B(>qaX)VGk^+OL82{>b5xMGp?k@gV*HjWdPCa(I0c zyzT}I=?Q|5)_up}s?C_tyB~Q=jWCrWkcu=)6r3v83Y8Z~X%-G`G9UZ)!^Mc+dYIlk zIL?EKC05srl9xwy(Nye_ZaP+kNo30V=?5vGOJ~y64j1M>c*DjA??Xka0ND zYY2V5i0~P=TxHvlbw7W!efR{^$w1ME75z*Cw0S;+Bx9>)b7mi`;El~Cd{(Q~VKD^P zmhnmvRxm;zMKjq75}{jw(JGM}sZB#`z>IYdDpqg#y2OL6)p;X5bW~nVD*gC&D32&?M~;K~d#5>X z&7Xqy{Rd6Hg6y9pk@~-fM&OZHv~TTi%J61*eHu%zti^yCPqZKyeisf^R1x2#@n=PS zAS{G=dNjA4IdD_`kHf)b-!TGWOz0MT>yd29@CKl0W%#0iBO#jP^s_#5&r zDt)TIV2MQU>w|HIyRozA+f5xFoMeb`OL3c+H@k zr5R~%CFl=!(;;gv5h1uOdcF6Pdr>N*x4Ds|l#=O;;YkTy*x#G4Z7v<=Qpa&pF<{wG zn48DIBeC&8{{P$7Tfk|3CfUF3?(XjH-LybsjcbrNv6&s8ot@qP?Ck8`|7Vl2WF}4& z2oNB_CAibj&@}Dt?(S~)ThB{&vwQC|69NRf->$cws#E8j`n6X`xTk?bLRz3Td$D*u? zmOGjE$HpAJeOAVLI(d6L_-DkMdi#19wH_ZCt*sdupEj|-d%wK=VnNGbXSl!DD^|`f zF}4QFx7r0nT2C#u+!-9X-(fd6Xk~Wm`mOS7L;dBG{@dLHQY^xwtW6^0*E&VamW`LQyKX(%+-wZu>_fi9PKI%&vbqq#x@-Nn&n{672u(S!=#aN{kE=(=Cao2=ECcR zjSVK&+5ss}?n{%kO(td+ESJtNb@z659WGy-zgaa>n%B2*?*7B+&P83{koW-mbpPo^ zlbLbn$iS)9Ar3PR4TD2>9uK$XmGnF)Ei@SKbPDuO@^)SASf%B*XtWp;pdYrv&|ze{ zt?yv5QQgC$$E}sUS_?A{fxZFWE?e}Q`rNf`>;il}9g;ou2L>ApGh5o)9^{v_PuI;^ zj0T2VSuOW-m>JVHbu%}yNwWytY%o7oT-uP^)sWk8r%jt$F=`XwV-w60c(@v9@(LFbWAy@OAB4Tqv)us><%F zxHHr?>DJO>?c{FnYi?v=i-o~e%iYRsceIJgY~NV#h2q7msvCV{o#KHTn-5MLvGMM- zGj)!(G1YMkOAeTIoE&Ss-Y__P^G^BH{8ay3kGb&-Q?K99?3CNG*cq04-BucEo9NG6 zx!Y_!p1rr!#;nn3YIwoQsLwXTU}nI~e^cU+gRy&IC47=uZ?e%L7W-Zg5nzTcg#o8}~ z+j?4ehlebbj@&OB>MpvOzc4;C-C^T7n>XXF9pN@<@3bMr-qhLK(pqmmWq}Rj=B}#h zhedcEn&&NjjjrhjJGz?B#<{wtMlBkJ`gU9DMvwLP6*birR21bF>W$U%O~@^qZ5+pJ zT^+sMU4jy|-7;pibm!*mXWKij58f$h$SyK28n?7^wKLS8H!z8@5Aia04RP9_-)E}L z)@t9NPWGL9_iBrJ+Oo~eLqh!P?EFXj!(oH9tYQE^?Xii?5}T^WZf#yv9a8^!F4v&c1Fj})j7b#FfJ)j%Wh!I(9KM% zw>vK@=hOR7PCQ;TPK=1!h4psP$k@m>ao%pHb%5)@g4Lpd&csYp`I$>+^S}DWrCuvz zQ}i0&3Cl$@6d5CvEpb}$rnTC(9s>gthP5@{es=xJ$((5&^VKW1B{@SijOiFI)%6Ta z^>mtDFqvPRpByo6y>aNHW7+re2d$Q*u6iZRLEo%x!hJL}+&w7P&$ZLqU}3&xuGcK* z#O=@bT`nsd4BMR^7rNcUaK67wZ*FOZpI4l-O}n|CN%@dLU3dHWQ-3<0U2rdO<;J9x znC)5#TIFYP-kEhVNau`T57i6(eEGcdc-D*$~}3G}SlkyeU22 z!Dneti>~E#e&JMoQ`x<54t$k4WfuC1NBU~io$>k3PMf%dgv2CIEo?+(BVCnJk>8=L0Cpin3Id~ zu!W^xPwdfNxIjm234K4Xp+3(~~uqA4J>r zE&AGqZ!vAQ2#<7hjEMYIc%6sCV%hDB-<6oQoQAq*g?lzurikk7e@e6{}+PCP&8yD=zFUm@+Dz zXc%gnD!gYr?QAtU8oJtl(apmyVM$tWTE^sDb7y`UjEyu`UNB=;xl+-10D-wc+ zPiBt|+&vxRG8#))8C-hCTbGx!jEGOn;VzixO zqL-{+Wvjn(myNmZm5R2D=jZ3DW+u9ujf@L!R^>JY^-V61^wW1;o%BqKrEhZTqW!?N z>Iat^8_g$7#vczYYCkS{bZWt9(QbKMuti`}`Y*z|(j#ZhgX?pvj^3J|)*hIjxj(Gi z`>-bKoVkIG*H(Yi*pTq3mqToqILtWOl@{E({ZLnDV$!Pf-dJ1x$c0B8Gd6)PA!|KC zo&8ttjE-M0vep?IzIdmlKgV3(ZltO9a@&02)w$)kC-?&HDAu{*DkzOKM^qJ9N>g> zEgSCCO<%f~SwGk3VPZJ5IX%(U+mf+0*44&wcCxA8f3mD`++?8RY;#9h-l2L6Z7&Nw z^NkxkU7)*7%>r!w<|lMo7M%1-x=f85Gf(!nHXk}V=3wvSV;`Rw6lNUe?+8B{ovJ_9 z)@^IPINU$qUV65mY4O&_g*w)$PLn=c!$aI$3}W4M+YMK^y0;e@L`{wiPHWYbe0P0# z@Z|NuG2OTz>xhiyb6&o)0WsC}OTx`eb!PiKO&W(R+n!WizuQr}Ki_Ul*LPczYohOL zh?ADP&5&M_$E1O-w!xI%*rHZxdC}wi`wt)Yw)aMQ?ONgF?q}>CZne;5Xcg_CV>Ic% z*f?!z+;`z|Q_YS1{OamTi@2qc>1$j^y@O4y`n%l&9CgPMP0ZS+`^`-YbFvF^DI&$) z_9G!H<2SFLnss(`wjAzv@iDSAOf+t=Xz8~%x|^L>(sw)aQKgaPV8VL$^*g49Tmr1S zChb|YSae9z?`s-aG^x8&FkE~4dQVo1m0z1n`txzIV~gQIz1?!Fg|63&vLP+v%C9Y zXH8|v=+x|-u21BuC~g0k#ZLbvCVn2e9@;wX3uX_#8@0&qZyjuJ+TYmi?a=1n=oI?A zpNEQXEIh1+oc&{x9h{s@{W2U){6k$Hm+RS=_vYlx_SGyjR@JoE z7S8%PYuP!DMecTUa18J>OI$wZ+S1u!QQbMyT=1k-uV1_QTJ~Z~ze|9Qg{xUiLY%X^ zua@}>tF&hN^R)D;i?k*hF0>C%8I@fx8Ee*#x9W;_^3Y$s+&R!~!ReK-ZiC*^&dJeo z)BeYG?XAP(jfV!X8hOXgy1Qs6r6ff;8#_hD1oY;6G#5=bju|YTt!UM2EG{f<1}sO% z>v+XkFQl!GFf;XE=At(?>wW)mOUJ17Wd5a|xuFMzz3q+0gWl=EF$Q*K@vns$E;=r; z(>1TPy8mQAds=rm_eO79@1yRVo(X3Y=fLO`Q!N)d`n+@SxN8KP+apzd^MBm3>`Z zv!>-kHTkz6)Kqki>CSjY1SLjj>n{s)v9*uzv8PGIYV3QEeW$E7|6%G} zt;-8(ratb@0r*n)sT%R1&~>-vWCGBZo-stbo*&F2G`Ch0rHdb*f-TKN0f zx!AT&TbMMKP7RHWlovj}pHo)YGUM&#;Svz)V;gL1?BW>gVi)7qYBr;z-&0$mKiKym zr|N8FWp0<7i-%{dr$?gwtcAU;%Ss!QI2Y~7N$cV!lj^?8?%eXbX9t>ddfnH#>c@uI z2I)EJI@pBA8bpTrw+?9cw@(bs7FG0=-MQGMw|0+pbyXH=mEC)Ixvs{z z&%`GoAl1&a$HvXnz&px)Sy-@@N$Xfs>%>U={i300}4^>GSM_2`=(KHF_lbMwU5=;)K&Zj<4;1OuAR)i(%sfnI$>u#H92KF zHZ!j4?l$9Wpd0^eptt=CzV_w@)pKnp@7=yJ*xOd!?qW7NYHK#7SG=%{ht}v>2fUVU z?yv)KaH!Wd=iP^erFB~M_jR1K=d9-2eS52;Or1>ZY@HIeyN8CvnL01DPmX5YJ6c^< zzBpV~?Q5a!G&Z6?(_(ECfZ;%%q&qb^F+MgrGCVY>&Om=}Pj^pGZ{NW1=)~0Qf|ic1 zp1y&Rv5A?vrIodft*y1CsiDs7Xm3k(;iEg(E}Z`6@cz&De)PfnAAI=X$De%m<^F?* zj~qRA?C6ohhrZgs|EohszBzUN^7Y%94<0>!lAB*#UfbL?G^Jx^@9rNSmz26ReOX#^ zLUdSwx0{olrHP)_-1NlAU|&~jLrq0#QGuMoqT=F`((gt*r{;934t8Zv(>lv9d zvh@s2NME;g=X1}$@WOM??%uWg*%w}Ugwv`bocc34-St_&dh7^d@#Z!al)YB=Agg!+5A+RIxo2YiwDs;i^5slK+VtS~nx`%zXF z+am=fEYqw;BIWAt>E-F};$Ur}Gc(fNSW)os)}=GYzy509mtSzc+_(R$gNF_sBv4;` zMykE{?z``QxcBn|M^2o(eDmJJ+~SJb#+Htrfsx4t0}Ds5;F#oPD^@OFwj?IR*UiDw z#6X)+^>=r)x3#vm@H!gnYuHxIW~=tr?Yj>i=a*Ji*Vf6AV3AnEGkTUT{?Vx!TL{*k zJrb?myLUhL!ppA`t$+UA?|=WhfByAPUwdxL>g1>((w11+*x1+txkxR0J39%Ty`zh} zPe4dSOngFOVnS?G7;B{XlFGW4&fXz>^13G0&OV{B$;(!* zT)s3hGQh*Z!cc{(v!$t_uC|85Gfne(#WZk}Y<;vBYnb~>8^WH`%l0A`Hx>ASSc~V zz8-GQl3IkzhHyEkE5er~UlW-X83P3n9~&JW9N_Ei>F(;{?BwL)=0Sc1`1|>IxjVyR zn;7UW&P{zEE0tIhETYxXGc-P@V`ye&i$8>P^7irZ_45x12=Mpiuk!E{BT231%7W~B zH!hz)b^M#7N2pWBPnP+r^A z(KkB1sApnn@9G^C8JCotOuY*6b+t9sn;ReMWof*+qO7DuN)&li%dblEvNLa8zede_ zm_xyK&R~Ftl>^35#Evv3bXHFTL{G8$WvEjn`j)?X}l`^bbG%+0TCV z^Pm6hCqI7erQKUH(&8h7{UE>O`f((^93&Q=UYf5fASg5}A}U665d-~vh!&BxcW`o6 z(e&_ecatyKSWkO?W)kDvKp%P4(bn41tWIltS09%}&)CAw+1=aEKQJgHlp7qutq$;U zrx2SOXw8iEwbz#yJbrNJ`sEAf&YV7V>g1_YrzBX1_J8rohkyU;9|_i*Z~f_=4?jP2 z{M=*MTs~OtDpS% zjo0?P_}s4T8&@w);*$jWd3(4@akrN%NKGMFk~sl^fkD9>`TDqZVWCuc;^pSdpH;_! zqk00lrL!1@}8s-?BHjn~pYG(MxHZ)#;v1(aGB85PZm3J<0hJ6M|;XwQ!I zcQo>#%O?---?@4H>Xplvz9VKQzB&5!p#uc#y?6d1(R%AoZ@;&9-;q-nuHMRg_@t=3 zuC;e`R@cl9NE4}I6%`)jO>sBTou3%$X{{|Q%%h|~eE8^bZXOZKm#`HSs#B2nBq!%d zeql*PO#_8uU~E>`6qp#1khXH&=IuLoKmX#M*M9u7U#biEkH7xKPk!|3i_h-ZvOZ&Z zDqFh|p}~Ox+$hZ*lPEa5x_kNf1rVyxP(nn!f&w{2E=c`J4>uQQB4%%AV{OTXyM=|B ziBv6#)=*zJ)xD{)shI-b-r3#TKQuZyv#4jxg^_55M@G{yVxl7>q(8XWSsLrijP`dl z)l`)5vaRdMd)M_CW<-+w?R$;p27C_5*w zkUm;lQw^9X1W1%tRM$7R_l^*C2hZT>CCk@r+O}iYv(LZq(krk3_-DWV?eG5e_rL!? zzx~xuUw`S@ZR=L1B}PYtYoZYZHlQeZxVdq`TwN){o?bpQK9yTR1k;DA>?id`N)^}7 zmAh+eWnpS!gttytS4SK8Fg-CkJkTr6P;OyMn*?i^Wu--3LsLsT7f;_nx@$~qTrANF z4+-@1a&?r3INH~N{#H?1Tqy54JwEgH^-JeZA3u8dz?YwX^xoh8`j@}_^__P={N&3+ z$4;KTc=hI;`xLH9d?!=d#?~&rAyIL14THR#tc_Bl;<{pz>B`!_*Z zzxdHhyEd;`mKYTpDm&k0bmxvGB7r`7U1R|92y?U;XMlG-SKjBw36KF z>u9d8sj94?wvG^W z0E^~^I{B3NtlIh~w9Q=tW79G=KeHR$uxsbe-PA4tS_(S+_Lo0?dFRGeDRJRJ{*o^W zYD!rO4wBrF&Mr7XU={fU9L3KjA(D7;S=GPttigV zAy%39?%v_tz03da$&Gv>fVP%W)z#D6+tbz7REIoP)7UXMV`%3cv1H|jZ98`EcxL_;H|yc! zJOD&<*Wmc9wt=aYEeH`bZDFi4H$KqSLaexi6018Dz?(O2+`M(?o)oVq6s?Nt+WN-k zw$9#u;B!YyJy5K zB0`Cce?Z_5qeXuCaUr>m0<4^=_yQ(W)l%uD%7380x4NRWEZP>JB`_5-y-1F+| zZwTsobP%}Od-Ki zp&hJD$gHUeqBSxyG&mrK_pF!G*EcXUGR~t1^o>=lqzp=10Mglkeff~`OM4qjnU|zJ zOpcEX5G)3i+N$!B!Y2Nxn3h=LCF7 zq=*$2E|mI4h4ywMSbDHS;~-MGd#nz3OG@?j5AdL!v5BdfxkVC->JEv^kxAVOykTi0 zzaaobjqZXybQWgDK_05wwKO-?!$Us0dsSflH{YH(eeS!@wX$&30U?_mJTf;V}QZ+U)J-4u^4KHJ8 zY-S;xjGaBl)<$9_Q?(iwWE8apu9=$}5%bAWK8WHg+FKfH$_rJUzj)!o#miT3+`3Jb zrcU3xN1EgmkXZGNEp0?=U`RM2nH@S>8+qJxp)^yV+NDdEuUr#Q_9QPqKM!aMuqvZz+MOo+3ar(Z~P;?mU{w(i*dEN9owXSQu#zjpP?6)RV* zTD^Mpsuj!9Qj!Q5crjK*D<&#jgX?9s5lTWO7f{t(LLVteDO!yz9?oz_20EnH7yyf4 zQP(@#$!#`?rr>4ZuV8xww_5O{rSKW%=H?PAIdEFGM9Rj7WHU21(3M6pI?PoDd`RUg zEy{aL`@eUW_+&kNBp_4Tn>0hF;hF}@G(e6&uJVz;P+kmff0*?k^X}~%*REc@enUV; z4tKT)5XvbkE|F=mO86`u=3d3rG%&HK`YF{J1_ips#?iw+EG~7`hHX0~TDzXvx@mm| z5lT%-Oo;!Nv~m5KRq1KTiSaR!Pw5!2yn+sz!c0>Xw!vS5wIn$? zDFN}=&y(lKQs3nkk`evtsM=0xmwTu>m4wUOOrXA@fxf;0$!2CDS;hV4Qc4_+3=Is_ z7@z~;iido9oE@-MfnCr}H7aJMB}E1KgiGd@tgI{*t$Ln#F*E`S?r!7K7CoVVUS+bp z@Ewf+umBDPwln196C{aJDTr5oQdZ7&EH5o7DF>_dkIyqmd-^h%$UWi3THEu@A;RKQ z8LY{!-MgOIvTjv+>XL*6$f_l&%T}%1xMkb+ZCf_2TeWOSTx77nw;KPPHT08|DqsY# z%hTI0kij(}X~~kL#JK42AYV6o7^GQ{1E4|9$cW}ABV*%}6!-Z>Z9M}cE+V-_xQL2A z;UeAOON9>t99f#1$h@SzxUjIOrLCjG;IueDGcnxP#YEl$f7jgD08*{0EGJwt1Lx#C z&d$ye$W%qNdMQ~Ftm?9STHv*d=gyouaq`sZbAZ=7nY6$sPjd4#lrjaYk{O|dJ1K#K zz+pH9s}1vzcq<1t+Lt$ECPkH(=H%ub5EhfPbk#bh#U0zXtmnC8o#qD zhN{11{i?Kth#((fZ8RJpU4jq=$rKH`2w_@@R&|Yz8BS_BS%UY~@v1ce zP6^i3^vujG0pm6ro0yuJa*QR342?;4O|TS;ZKO}E=I7*((hxWbNH922wJC&xR6+L) zCw!^JFfWQuFDa|2s%>aySnpwWZ?3N{1NRG+54&^hn{SVwJahiijeA*&bx^mEzfeh` zDp!m=ms)CmNo74_z>Kz`xviscc&f%LGSAb;KR7%(J|%tCnhl#aZdkjTvXxAVBqk=M zEL*u|{U)+&J7{8ga%`Btr*NQF^elTCgb!ssA_{_80Sg5#Gz}9{3JPg~iX|;+Xn0gc zp6ThCY0ixLae8WMY6c{vs|R#6qq<9ik!n`Ffm}qnho-;=s-DD_{<%PgahNuzA@wC= z2dVc0D1Z}`F|H4Hsid^50!FF{2CrQcN|};!AKtlo;mq-4N4`FM_{gzuPMrDf>h1fF zzzj;MU<|C53}s-?6HXo)BEO`%rF(d4QP0TS29i$5C%&fuOs#T|3pr;^uwVn-pbJ96qq%;`sY{pgSCW(BW23@D0=(TF zZOja{nO4B0V+=bJ5~~@8>e<;DInxmHWBiASDR6|go>VK!JxBO>6)Q+-=@9cE%1ODn z#7YHA6)&=Fnhyb$ArF~qhx?HW$@D7#pt`=PwVi>OD1u_Eh3?CH{NOgo?AxP<4}SI4 z!NW(tJ$aV-=a&<;V#_E;nsj!A2 zN=q@;L#&WI;ugt)dr*g;%qqo)?2zj@Nx9dfe2Kh6xsulo)*pNxp=r5I=Y5D18{^smaOd*?GRg zr(YdmnvrX$<~?Rbsr;b%S&5c~@bO9tld5RVWGJ+uk{mTLmAj~o#3%=%ObMXOJy@RkA{wlQuc_a9_ySca!z~G3uLZ=N=1hXj)|8XT)uL(gh~Z#c`Cv3b+xkqzk`jqdh=Q$qN=_&uTn~lAZTOq<*0Io zCZPR&oy04z$ zn=$A1wM*yEoIddl%IpCIN94vs$KayxWFd1@*Kr9I8p~u1AY2UBc_mei9epEkuIQw^ zg#s$&Dk)V3egQgxY2N{TkmBv(7aFr<>5A1E!1&Z8fTmD#00np~Rn)2J@d7SZFj{Wh zyk+awEt>&kt5>8Y#fArZK?|EO>}xH`wLxy?sKzFj>c5m}X2g+^QJHsDtVk>&`IT&l zxI-A!pM5$>&dv*h0Blj>=jYY9#BJ0t)^IrF5@>gNPSZ7EX9bl_jVU4-s^P)iTi35# zx_I&YnN!D)9oql-r=Nbl@2evxA@1)Z%>u7Vna(qB-@I}CI<(E*2RVfm^(}~DGg=}* ziL^z1SE{Brd>*|~NI>V8%p`VZfLA zrKVQCGQK_&GbSOSdX?saa-oD&0J{M7NQUnf;EAVEg2qRBe4beep<~6;B~TSe4j#_- z@bCf$j13uA1Wf{{d(}YA&_f;!%jKG)plM6VCi@7hiK(fXnLv8XeNa%6gu>;_B3DNu?0MytS6_Ygm6s8Pwr*5oQ4%UO znuF3xZLI`_s|isRmJXSvJ3xynSej7j$dURFM(2<~+*yD-nFzd4m_s7Uf7BEx!B%`2 zW^!g_6e1;h5GaAjXa-bL>FQxYz8;Pi`txXy$Z6tecnBbP2uXSeqV>CTq7i*Xso%H% zz(E`VhXsBeKXvxPCFlrz0F@1BJ5vifOc`K58Os>>nN!gfbmi4*>VcLvuU9-R%(;{< zA;%T3PPyVh!-OL9rmYZGXZH(xU{`FLCGH5f;Htf@fm&>T?2qfAA#9^U`i$M3)U z_rJf3Ip^cOpMJjo;F14AUnSKIt#E(iQUk@fB4e2(737+(7CoLLkVx9RFim7N^_J$O zlyzuODea^g9*2ZvZsCS4N~0An`e#2yZhYyvo!d0g0%Ryw2T2Q;53H8%{N!+-Ouww0 zVo_-XF~T*0>8UfsK_MyWcoTSP6f)40H&05rv{7!SWW4tL)JT6P#CRpMF1!=CQ#nA$ zD=@Yr6C31QEUj4qKCn(oLS&$)1GxqF&gYiTigd&DQ&pUkdG+kE{hz%5&YwlnRyyt9 zKiK=lfx}0S%jkG3^HE+g%mr-xz|g2_8VniYUtwA`qL>OuB;~pP(si_S^bx2WT*2oI zeW(=F0On)97@Nxb^nYp=a3{3-;`<_&9}GCJgz5MOS{n+96b zs8FqdX}nL>)irhUh4TIOh|S{DgkjPneB(mhNXg_-p@fK_d{W0mshywX**RFM5Z{G< zDq~irTPan-p0t9a6(A$+c{Rim1wlqLH#;*uwnzs1A+n{yjSlyBHCGotym{f9eILE^ z$2b4;>3Q?5x8MKtz}MfNJagfxp!Ng-qoxtQbb#hDO~2+sDk_O`D!oVGpQd2WEnwLo zSl$64GWo%MN`Y4;>-*HoSie~`+UFoxh1%J*W5+WHMbHx)))Fouq(g)JL=nTapd(o! zdUPYiib^igP#^*4xOA|f{72Dy`iJq=Fj*V(qEuaF_K{A+`)NR8@qNF9_mh!C=uN10 zM%}SFJyRPe4>Vel2_&^rmolFbuGQ%{jeJ~eG(rGV0`H90!tBIwPfJxn*7dVT_kH}% zTi*xkKmYU1KmP5bFAslvmRQ}qt;jB=QY$uph)Y1c1h}Ycrzcbh!^z|%+yGM?1Bi*0 zgF7!mqzb?&D2tm2#tP*gg4kEB-MDoJh(UzMXSQz>n8kto+PDF#m8KXMfoVa!L#k%c z37-ye&?;g%>JmkodpHlPHzk}g_BMP~isxcZq*W^v0gwR9^XL!qqYrtf9uG`094mvU7jT`T4uFT84a{9;@ zAHMU)|Blvw60I)|9>YU;L)5WH*>LlDVtar{>>Ct^;+$q+<3mZIqE^62%0x0VgLleQ zBwb#Fxcfw2MUY?+V*MgspaUX@3f3;9$8CfPwqpIdb!*qIo-~r;N>enz4&)-LIs+q| zA4IY%luRH#p2QSL!Oh#{67D9g&^IL_AD;=MEjVVp^X+R|szrxlM!_?tKReW3O=_Juxc8kO1naGLKKh)7DD)sm z^}Lu=`F4Qt@^mB-s!~=D6 zFBUDp%gvP-5qfcA@TLnTDc_^D=+Vt{M?Qb|zl+wJe|i6tFApdmw1!Q+cv<+aVwl86 z%7$I zNd;+`1}02iA~+rwA7hV|xcJ7g&;Y#U28tZU3|<#jJYg;_(9?{q_6Qdh^fke)QSCuMU6B2qc*$lg||hocuBXgoYi1#qN-gT;c+P|*Kga6+Ar{md$?8AcF8LEL2@c3g;*uV zGI8LuRBQ|GKPg*$-$wa--3m1TM@3hcSy^~z8HMLzwLmPIp+Pw(yk*39vcu?2lfYhu z!YT?yr3ierxUb19hyhv=xA3VeRV7z+w7@(jF2-3RC0z;LI|d2C*;0@1pHo}%m@ z-yHnxgSY>vL8b40@X^OG5eL6M`pt<`r_Y={FMx~vuRQVObdymc)k4TZR|9T2nu!bl z;@I?}zNzTy4t63Fn={)&WQwQTQ;3`NjE&oNKKuNOFTX7F^Nwev8ES}o0)^vMXhM7v z8q2bk65ti7Np%t~h3X{pvkW<6WR_^LJiugue8Gi+U}GK?BO$_(q_s76dLj9SC9!~N zVpPY#(ag*4?#FFQ#j+<>Tv#8mTVP~?Yg1l)arvOB@Esy4WaL^j-$sI94Yt*=jBxqv zw_hLleD8<&!?EIg_|aaCr=DmXCtPQ71u!ONJuWD#ZSLq%s&-QyCTDTlv}jmHxh22~ z;hwn;B1b9d1@*;DJZBg+qmwA_cq0&H1&r?8q0aU#&@<_2q!d~h(@wOSbuej(_9x1p zs93_#5E$_}(0he5mDYkqUK1=u9gxpJO!1b$LrPj(nF5Yz&+}7=V4e7mBu>PxZ-)`?@vGQM99BeiC# zR$Ro%vllLlGwz`Vnc@a$Z)Z{xiyIU_@oFMo(EM^w=*P2CK8TUf5lkQKKu~riSg|R~ znRPaA-9|nM5Z$tM8;+%&JGO3;Xwd=@`(-Q?Lz_JcjhUpS@HA=y1@i)i1>SuRSC>{n9&5W>HoO;viSm%*GSvrTF0#3)F~hLjJVh-q5{jTD;{wrBLjzNTW+qlti`Y}S zg7B>n@%)tq0s7+_cSr|E5(~~j<|E*sNGJ07q!VdaP30;OC73p-s=f>&*v3(&Y|QlL zMmlR*N4RlW9OI|YoH=_II_uQQlP6EeIZlN?eeTk=TlXG5DJU*Oqb@5c%+Ezn#6g16 zR$g9)>y6J##md13CVvr=gHZXT zpkk!O;)CDEN~5j z>bZ00zq^DF=@J6Juz=8j)T|4alvlh!Bz+|hWx>z0VvLuoq*OvyAx(z$74bJgI`x5C zC!kH}QQ{1uzIz8pCBnVE`!0sS zYnLy6C;alY8=9pHVpCj*n3k1^xAHdb8?;$MNKY1-Qfx}%aFR?HM5MS#OMs#{l#&>A zNT#R5;97(-G*}|0<{bdM@PB;w0k#@s^#`_EY#!W3a!WbA_4Gu%kcuzT^3!kuipKyf z)E_H!ZVxHcKn8njhG15IWU|+E15!)QQ$Q<GCzN=%&8 zGjUn-?$O*eIlib|Su-0a&wvOlBx^R}LVX7F?o-#BQWjS;60Qm`v}ha}&zqSb z<-bcB(mRP2vE|o6Y~+HHRFpsJrx*gH8t4nfvM{YE zpL(1)I6@xF!SgAkGMR(%L2f*#RH6mIvXQMf;qE9?gN$MmYK&z-0&9?5uqHq= zLj*@#XV|}BHSjB038q|#adZU9ITA}S2)Y%a($aDfiHcE*>40SP@y3i`MFvCX_F%aw z$<4ZZ{o>hEYzPUMr_#egJjgDnsAr{aa$e8eUJ5rVM8^6pVgY18Rzk$fFTaFr1N3}( z&&w~p^un{^-CBh#80PQcV6Hbe*4I)~@+9;6#nay$`eN?~e}DVWYVim_{DbqyzsM+m z{LIDcnK?xj^&S0_iv||7u7DxHt4oF3={pO7b-cOk_6mN5+n}IgQmb+KZ9NquPdvE`b&;Ay>1n=+t;;SR@ zQ#aL4Pi+&dy3ABF3tC!FrA#RZ`=N zCHwKhI6Jm)+a_$MRBB#Vs+w8?yqSh=EYM8F#s}ru+E`c3z7Q6%TiBjZd5<2Jg~|N~ z4xp19I`s7sK=8@a=R}jceGjlg|E;c($sG(QZC?o&Xbyse85ALyXSI#YZJj*>fV^od z*KT@d_e-z;!_R;H+kgGH|M(y05C8I;U;gB^7j|q|nGzS~>uPVVKR?>rT3h<${`Cu| zjE4e%{P4rQdq4f+0CsQm=7)Kub**e!Y8zSDyE3bZ+aN9>5$K$pg1d1k^hjDt z$`TYQP#DxM9&2OWxyhlfmb$XMM|ZDXIC1#PkKTUk&HwyC0=)Ik-u>U4yPEl=w7zR( z-pJO2hd3=?yLtPwFR4}bWN-~EQQqMe)9 zE>DeP#A8{7dd50VH%gcIukPIzJ@6#(^6NutCHTuPzWnk_lrE&MLnvLmgR-#*;QaPD zgYt<}XU?Dh?(+2;cf~qiR902j(Ap_-j<_yp$wFetf=m>KZkX2*ke)_u`6zq~}wXA4h;oub%wIrS5{Nn3BA;16q2e0Glr~dcf|1(>xufFu`&TU&Z zP#nZ8$(|~E0~A5A@GF}&xCe5ozmxSUT;=y}UScWt@V?JK{rH1-|Ms`P{r&Inv9$cj zr!2^See~PYc%ZIfdVLDz@L#Bf-(F#~g<>!iCD#f56}noMUf4K{iHVDcjS^;f#i})H zH*8{ zI&=2SnbT)UCw!$>uVd^McwSN8%32{FZevSfRPYchHcaR+2Dez@EdKkFYcH-rOEXh?qV_!65oC02X+G=2 z__K~2lK z9kxjs#I!Mrazl6-W`+j9N$^3~;E56ILrpEs-_x|Pv;vvLAcA6|Y<+kKpg8!BW!AFC z!#hjf%d#0hhCtaFZ5&>F6k|XM)v5(o`;ugA-qn3v39WTl{fYLD<5AeZrT}-c(mv z!ngOxx__66>niI-SD98XU8ZPUyK(a_n==w&9z9V&828YM{F?1#8J#2#Tlpd&LYbo^qwCvn>mv5&}{ATl9_bAq$}; z_z&zZ|LDp81rG8H@mvAsF*b1H<+d{hg45*(rf1$G(arHl$)24U(lz--3G*k>F^st} z7g70`LB)|J1C^MS=BLL-`dKc(F~EW$o2yJOvgG^l5er}ig{9>+jm_e>;H{Yv`UgOY z5ry0qnOQ7@3{&D|AWxu&v~(E$`8seWI#R(P`l(-3FJTnfI+4ODcM!KXXsRu6!l$nTybv5lP*y882E%35cVFYZW;v7T18rH zT#r$Zv3qGUJKtgaVa6f;#!-)#U}J2ca}V-keSLu zOw@LoyXsG_?yi{DX(8BBO~|!*Jai(rAy~HZ)Cc6bBV4B7d6tql_lPBGV9HRNzzwxnmGyKn5MgHY>GN=miyn)a50Qy%4rv zw!-9yc(2F`VJ)mkO>u_MiiNUA;t?6Y4jmur1b=aU9#VcusXVGPO@(Zs6U}?5%0KxI6_!*jGUe zQgReuXKyEzidr?25!lSkM3zPLG|%AS)vBd5dZEZdY_%b8wTj}w)mHM5d^&2j@a(i< zTrpTZggq28pFvt#OJ!Y>2V+Rp7okKXCX^-ldx~Bbqc=`Y>3UvB5NHrAa!|tAGe>h3 z0+l2;#0Mw=oDr`*`$1?&v}g1ev7ex72&U`k!WSuQh`dAQ zX2Mh`-iVQ2t#fO#0)Kc<7dod}6XDgtd`!-e8zM$ykHT3aR38CT@2;R8q+CL&-WH*2 zNpLYL0lXj#uy`W=OEFbxC>}KOo{&lh)GUHw&>I{Qo=z;~1OOt`|LNksJY<86R;Y+7 zMruMrz(PlwO5^no-pxoViS=n;cmBR(e0vgQ30cRCZ zIFh-@vH{EdCdTQg7~CLCW1=i2Cd9|#Ko>_DPliw?O=*zaUrIv1Y!UF_239F5)aptx zlzK_2dTy3L9?<|%i}Y|k;dr7HKutT?JFwqG{;3T$t_9qZyq|Nb8I6x|g~@8g+~QIv z)Wai)`fyDmQbNYaFah$V`)B&sSt ze)u!d8S&5)e!dFeU1ys-rvw0uGYrTJw~5?P#y6Af6;w5$v;d~A0FpN0HaCMR&DnTSk%*~|s@-sPR+;SyxDe{y11Mw-z zDZ&;S3_HdIKlpGGHWJ94FS(%k}&Z_v5l}K z6=Rx=?hNi3@`HwjOve+HLQJ4E&f2BYIF3dNR; z4(y2r<)HDR&&|*QdAwssN2_}9)zb_Bt?^J9p4r~6p0S1pQI@x%dylv$;`iK|#TGaM|h3V8_7HY?Raj!@YYTGa(*e51I~tK~;rAA^eH zm+HcHm6)(7QXQb=k9TGo`GHI*f zkY4t^6r@4>0^_7nn3~Bt+*3))1D!>ZS{|Sw9b6vo!?!h~L8_+_kdi1}^56$1Pw}~{ z9dvQ7DC-Je3^h`ai0BoXg*+;-mX>Hmsz_??fTm1JtLICiSgOT-hH}nRzdr?tcT2S= z*_2`%Zq2ehhh7xFcrUGh|FHyeOX{8oQMFFn~;ON0i z02U+~^4SGD%ctgOx)9>MyaFA0i^ZJ_5&=&uUO;7(G!Tl4M$ZN4C{aSj0@YccerhGT z5f{`Ch5BUW0CyT|SVANtW=hx*R9FZ>C}6NaDWEw^ms6i#0H-?g@29_b`XA(9{6R8A z9sVk>RP$4Y6xAc8OD?Dms-Y%G4dwC}#Po=hPpY)^OQ}d=US($Bg$Vbd3Ae4FPVSGo zHjqK;H8TIG5lH>0`5C_>Uj^fmdN zM}0WW2T%d$6O%DO4X!`wf-PJQo(1)f@C}*?T;>umiYD^ke-`;Nm83(I)k2*>HJQu= I(C}yfU+STyG5`Po literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/bgd1_ciel.png b/app/examples/Games/BeastScroll/bgd1_ciel.png new file mode 100644 index 0000000000000000000000000000000000000000..f0a38160c3808fbbf328c2e6b84f31b3df599800 GIT binary patch literal 6797 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/bgd2_montagnes.png b/app/examples/Games/BeastScroll/bgd2_montagnes.png new file mode 100644 index 0000000000000000000000000000000000000000..37ca6393ded1fa08b5503ff78879b22c9e87bf1a GIT binary patch literal 177395 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/fireworks.png b/app/examples/Games/BeastScroll/fireworks.png new file mode 100644 index 0000000000000000000000000000000000000000..93fe0b6b202f7d5a730ffcdf1ac34a738f8236cb GIT binary patch literal 46710 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/logo.png b/app/examples/Games/BeastScroll/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ef6427938a7b4bd52f271e6ba61d4484042b270f GIT binary patch literal 4768 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 BB@_Sv literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_arbre.png b/app/examples/Games/BeastScroll/sprite_arbre.png new file mode 100644 index 0000000000000000000000000000000000000000..a117f15c8611aa3428d53f567caf9baeb5717b5d GIT binary patch literal 73213 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

FXBSb%O6J8er)Jx8umU5c`T-6g0FapxGD8 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 b*5UsHy$eSe!oUrs00000NkvXXu0mjfG4u@( literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_barriere.png b/app/examples/Games/BeastScroll/sprite_barriere.png new file mode 100644 index 0000000000000000000000000000000000000000..61a159e7be6c4d5da399810301428d089b6488dd GIT binary patch literal 6455 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_nuages1.png b/app/examples/Games/BeastScroll/sprite_nuages1.png new file mode 100644 index 0000000000000000000000000000000000000000..5e27ccc73ec3c95ea26267177b11915769c15a22 GIT binary patch literal 14285 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_nuages3.png b/app/examples/Games/BeastScroll/sprite_nuages3.png new file mode 100644 index 0000000000000000000000000000000000000000..65375fe4246537dbd4b0481c14baf77fd073c704 GIT binary patch literal 2389 zcmV-b399yqP)Ta^wY9azl$52ty~f7I&ZV5Ky|uNJoScM&jJ3VS&c@cx#?G~^t*xb{jFgnU zz0RDJl%KjrLDcKrMMn*>Rj*N;d*J>;|fL&s6o9vVp z@@i&wH}n7h(ta}Jak@PzuQZvnz0NV%0qH)!`(4xN>?9=U(%ruK_YxBJ*h}lMv+g{4 zExP~XH^JhAuGnW0lP_)7PY@aWeeINQ1|fNACGJFo^nkhuC1{dRfp;%$*t3dOY5X=b zFj76o^5UI7I!bl=EV}2Zh?D2jeH&4^5;kO3>jN#QIdz6bJyuZuJl=a90y~c_iw4Zb z!J&M2FH#I?Fbc*&$&a2v_dK8$F&YFFWzVtx7Gf!_nPHRefJZ0$+4<=0k%D-mD?@pije;_xsX9<6S+C=U zg^v*uusiTEs$a;JtxDSPEMbhZIGFHakCiMpU_*L3>rDb=G)tz@H0sT=og*W)wS}u< zR7O+j*TPm*GmfE)?#)h;0A{ieQ8b9A-QMmPsO`u5cXn9YVptV8oU583wk8O|k{_l7 zDK)2UjHV@HK1PGypf~NFxfgS%M2uq=SB9C&@MLE@RY#dD1CNZmvq6>^6PF*7B#OHK zoRvFfwbIU5vL>eAYrTplqMSR3g)13gx%s?P zyHiyvb#JEwD0M!>)+Ka~lkSEwAVe~5(#BTcB)!Zy}wrPg*55l6FbIVnS+ z6d{*U{;e=QLmYH6@~n-ggsFE^`ds zmp%a*LYBRsnM3(&GpEvoH#+w|B~l8J1OJ*i^I=BAqZ?+RO#S@ga?(+#Rw-Np9Ee!l z`m6#zc!tC<4byF&l&RXJQWVB{{F{RrILFw$#X`9NW!R7R(-4g(p5|s)qu*RJF1T=F~ zEie?$DPvZ1N%V5oU7E1PWrL+hhETH>EeNTnOAI)ax`$tf*#WtkDD zWZ_)Q{Frar8gYw{&QM%{sBw5>>u?CIsj&}$4d$nO1rvc_7?C^S%UfgSraL^+5V zlr-h&2V;Y5d76?)VN{l76YA78SQ~mTdb!7~xDvjzJEV`bz^tt2qqC6%rRqae|JsM( zvbuzmzz3^kwY09~h1MUK5^Yi2W;_~wt@CeIu)&DO#g=ok9S39UlUms^FOYsTIP1~A zvZl!Ur?_9R65JMjvGN*uJ7YG7o#+Jq8q?~SpY?_tSa@%#kAal^mIgi8PjIIX`}@x6 zKK`ZrN8k|*D4WC7I^e30T~3u_W$1w2#(96@Z%M;dj6_@E21T8nobW5~#4WRHBhQZb zeMhn$|KhMhGXVnn1iV?HzOYNEl0p4Y_GkqOWwC=IGN+ z!%s?%u2{c2(pOjYyC1BmQtxdv3@^Gp=ZiGQS?Cl;euSE;EsnZ&-$B5K8EXqE=oKKK zPSv|&JVGWGUmI!xir7#2R8~5s7Fe}N7pVXY0<-eiowiZdQ0~Z5+noLJ z7z4UK1l5-9RuCy_0i(G|dR`ei!WfPJ=Kd@8Aw_g-JM*4}+# zcaeVHL>Kl@OY~#ip*dnia*MIex0E(a|2q3O|If#Lf5XZ>)(tCo^b~$ADjL#C`IxRW zxLGCO?HvXZSLH-3eIGT$9d^rGN6CEE_-<}H-)|Rch#Jc`#t7u0@F(Avd^Gm-$#?fq z>fY5Vt$(mZu3NLDc5SP={nk#~e6_FOaplvxul)bl_hW>@uIW|1IgUS$%KH7?9ZyaQ z1n?BZUgua+roONq=mrwy7Ox%#QICGuTA;h-8*crr;_VsC5HGif`H9=3sHaHE<6&RP z=bqH$1|)0s33X6Af0nGRgCk|D`vLrk`R38&RefCDyT7A^=dV73YbvqIzu(pfj1~;} z>rOYHJ!A`_`%8XF=X*?Ov!c9N7r@O+TkuQ%Uq!C4_(}OMkZ#RH#g$%`00000NkvXX Hu0mjfl0vVg literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_nuages4.png b/app/examples/Games/BeastScroll/sprite_nuages4.png new file mode 100644 index 0000000000000000000000000000000000000000..db1a428ade08f138f8e9c82ce27e5d52277c4bfd GIT binary patch literal 1036 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 QLjV8(07*qoM6N<$f~@Ra7ytkO literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/.lang/ca.po b/app/examples/Games/Concent/.lang/ca.po new file mode 100644 index 00000000..807ad63d --- /dev/null +++ b/app/examples/Games/Concent/.lang/ca.po @@ -0,0 +1,217 @@ +# Catalan translation of Concent +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Concent package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Concent\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:26+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Concentri's Geogràfic" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas IdeAquesta versió incorpora suport per a l'idioma anglès i castellà (nadiu), gaudiu del joc! s'han incorporat les correccions suggerides pel director del projecte Gambas, el projecte s'ha traduït a l'anglès tot fent servir l'eina integrada a l'ide del Gambas" + +#: fotos.class:303 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" +"Benvolgut amic, aquest formulari té com a única funció \n" +"carregar en 40 botons les imatges de les cares que serviran \n" +"en el joc. És possible que hi hagi una manera millor de fer això, \n" +"per tant... escolto suggeriments. Posteriorment en el codi es \n" +"crea una matriu d'objectes per tal de referenciar-los com a \n" +"botoncito[i].Picture i copiar aquest atribut en particular al \n" +"tauler de joc. A: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" + +#: frmAcerca.class:30 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Concentris Geogràfic v 0.3\n" +"\n" +"Programat per a Gambas per:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicat sota els termes de la llicència GPL\n" +"Visca Colòmbia!!!!!\n" +"Visiteu http://www.linuxeam.tk -Una comunitat Linux" + +#: frmAcerca.class:36 +#: frmInstrucciones.class:43 +msgid "&Aceptar" +msgstr "&Accepta" + +#: frmInstrucciones.class:30 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "COM JUGAR A CONCENTRIS?" + +#: frmInstrucciones.class:36 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" +"Es tracta d'un joc bastant senzill: \n" +"\n" +"\n" +"1.) L'objectiu del joc és posar a prova la teva retentiva i nivell de concentració,\n" +" així que has de trobar les parelles de rostres que hi ha amagats al tauler del\n" +" concentris. \n" +"\n" +"2.) El jo te tres nivells (Principiant, Mig, Expert), que et permet escollir\n" +" entre tres mides de tauler per posar més emoció i dificultat al joc.\n" +" Per accedir a aquestes opcions has d'entrar al menú Configuració - Nivell,\n" +" i seleccionar-ne un dels tres preestablerts. \n" +"\n" +" \n" +"3). Així mateix, tens la possibilitat d'incrementar els teus coneixements al jugar\n" +" en Mode Pregunta, cosa que incrementa el teu nivell educatiu, això s'aconsegueix\n" +" per mitjà d'un qüestionari emergent que hauràs de respondre amb un encert\n" +" en el tauler, si respons correctament tindràs una puntuació més alta i podràs\n" +" competir amb els teus amics. (Aquesta funció es troba en construcció degut\n" +" a la migració de Visual Basic a Gambas, i experimentar el poder de Linux.\n" +" Visca el Codi Obert!!! \n" +" \n" +"4.) El joc també et permet activar o desactivar els sons d'ambient, i que per\n" +" fer-ho has de canviar l'estat del menú Configuració -So. \n" +" \n" +"5.) Recordeu: Per a tornar a distribuir el tauler premeu F2, per començar a jugar\n" +" premeu F4, els rostres es carregaran aleatòriament, per tant no heu de provar\n" +" d'aprendre-vos el tauler de memòria...! Això sí que serà un autèntic repte! \n" +" \n" +"6.) Actualment estic dissenyant els mòduls d'aprenentatge que podreu aconseguir\n" +" molt aviat; per a comentaris i / o suggeriments escriviu a: jbadbe@gmail.com.\n" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Jugada Errada" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Felicitats, has acomplert la missió" + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "PREMEU F2 PER A TORNAR A REPARTIR" + +#: principal.class:384 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "PREMEU F4 PER A COMENÇAR LA MISSIÓ" + +#: principal.class:439 +msgid "Enjoy Open Source!" +msgstr "Gaudiu del Codi Obert!" + +#: principal.class:444 +msgid "&Configuración" +msgstr "&Configuració" + +#: principal.class:447 +msgid "&Nuevo Juego" +msgstr "&Joc nou" + +#: principal.class:452 +msgid "&Mejores Puntajes" +msgstr "&Millors puntuacions" + +#: principal.class:458 +msgid "Nivel" +msgstr "Nivell" + +#: principal.class:462 +msgid "&Principiante" +msgstr "&Principiant" + +#: principal.class:470 +msgid "M&edio" +msgstr "M&ig" + +#: principal.class:477 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.class:484 +msgid "&Sonido" +msgstr "&So" + +#: principal.class:494 +msgid "&Salir" +msgstr "S&urt" + +#: principal.class:499 +msgid "&Ayuda" +msgstr "&Ajuda" + +#: principal.class:502 +msgid "A&cerca de" +msgstr "&Quant a" + +#: principal.class:506 +msgid "&Instrucciones" +msgstr "&Instruccions" + +#: principal.class:645 +msgid "Jugadas: " +msgstr "Jugades:" + +#: principal.class:653 +msgid "Tiempo: " +msgstr "Temps:" + +#: principal.class:677 +msgid "0" +msgstr "-" + diff --git a/app/examples/Games/Concent/.lang/cs.po b/app/examples/Games/Concent/.lang/cs.po new file mode 100644 index 00000000..ba617c89 --- /dev/null +++ b/app/examples/Games/Concent/.lang/cs.po @@ -0,0 +1,224 @@ +# #-#-#-#-# fotos.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/fotos.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# frmAcerca.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmAcerca.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# frmInstrucciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmInstrucciones.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# funciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/funciones.module +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# principal.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/principal.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# #project.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent_gb/.project +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-04-19 15:59+0100\n" +"Last-Translator: geniv \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Geografické zaměření" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Tis verze obsahuje anglickou a španělskou verzi, užíjte si tuto hru, byly dokončeny opravy a navržené provedené Gambas vývojové skupiny (Benoit Missini)." + +#: fotos.form:292 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" +"Tento formulář splňuje s jedinečnou funkcí \n" +" zatížení 40 tlačítek s obrázky pro hru \n" +", může být řešena jinou metodou, ale nevím jakou." + +#: frmAcerca.form:14 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Geografické zaměření v 0.3\n" +"\n" +"Napsáno pro Grambas od:\n" +"Jairo Alonso Badillo Bedoya-jbadbe@gmail.com\n" +"Publikováno pod licencí GPL\n" +"Žiji v Kolumbii!!!!!!\n" +"Prosím navštivte http://www.linuxeam.tk - Linuxovou komunitu" + +#: frmAcerca.form:20 frmInstrucciones.form:28 +msgid "&Aceptar" +msgstr "&OK" + +#: frmInstrucciones.form:15 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "Jak hrát tuto hru?" + +#: frmInstrucciones.form:21 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" +"Tato hra je velmi jednoduchá: \n" +"\n" +"\n" +"1.) Cílem hry je najít tváře pod každým tlačítkem, tváře mají duplikáty, \n" +" v pohybu máte najít dvě stejné tváře. \n" +"\n" +"2.) Můžete si vybrat obtížnost úroveňě v konfiguračním menu, v každé úrovni hrací desky\n" +"více tlačítek, užíjte si tuto hru!\n" +"\n" +"3). Vlastně jsem rozvoje zeptat modul, tento modul umožní spravovat řadu věci\n" +" ve hře, přírustek vaších bodů. \n" +"\n" +" 4.) můžete aktivovat nebo deaktivovat zvuky v konfiguračním menu \n" +" (Konfigurace - zvuky). \n" +"\n" +" 5) Pamatujte:. pro start mise stiskněte klávesu F4, chcete-li distribuovat desku stiskněte klávesu F2 \n" +"v každém okamžiku, bude hra obnovovat. tváře jsou zatížení náhodným režimem, je \n" +"nemožné zapamatovat si hrací desku ...!!!! \n" +"\n" +" 6.) Pro připomínky se dotazy, se mě prosím napište mi na jbadbe@gmail.com, prosím omluvte \n" +" mou angličtinu, jsem Kolumbijec a já nemluvím anglicky. \n" +"\n" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Špatný tah!" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Gratulujeme, mise byla splněna." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "Stiskněte F2 začnete hru znovu" + +#: principal.form:45 +msgid "Enjoy Open Source!" +msgstr "Užijte si Open Source" + +#: principal.form:50 +msgid "&Configuración" +msgstr "&Konfigurace" + +#: principal.form:53 +msgid "&Nuevo Juego" +msgstr "&Nová hra" + +#: principal.form:58 +msgid "&Mejores Puntajes" +msgstr "&Nejlepší skóre" + +#: principal.form:64 +msgid "Nivel" +msgstr "Úroveň" + +#: principal.form:68 +msgid "&Principiante" +msgstr "&Začítečník" + +#: principal.form:76 +msgid "M&edio" +msgstr "&Střední" + +#: principal.form:83 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.form:90 +msgid "&Sonido" +msgstr "&Zvuk" + +#: principal.form:100 +msgid "&Salir" +msgstr "&Ukončit" + +#: principal.form:105 +msgid "&Ayuda" +msgstr "&Nápověda" + +#: principal.form:108 +msgid "A&cerca de" +msgstr "&O této Hře" + +#: principal.form:112 +msgid "&Instrucciones" +msgstr "&Pokyny" + +#: principal.form:251 +msgid "Jugadas: " +msgstr "Pohyby" + +#: principal.form:259 +msgid "Tiempo: " +msgstr "Čas:" + +#: principal.form:268 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "Stiskněte F4 pro start mise" + +#: principal.form:283 +msgid "0" +msgstr "0" diff --git a/app/examples/Games/Concent/.lang/de.po b/app/examples/Games/Concent/.lang/de.po new file mode 100644 index 00000000..62bf2ce3 --- /dev/null +++ b/app/examples/Games/Concent/.lang/de.po @@ -0,0 +1,168 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Memory" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "-" + +#: fotos.form:292 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "-" + +#: frmAcerca.form:14 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Memory v 0.3\n" +"\n" +"Für Gambas programmiert von:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Veröffentlicht unter der GPL-Lizenz" + +#: frmAcerca.form:20 frmInstrucciones.form:28 +msgid "&Aceptar" +msgstr "&Ok" + +#: frmInstrucciones.form:15 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "WIE SPIELT MAN MEMORY?" + +#: frmInstrucciones.form:21 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Falscher Zug" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Glückwunsch, du hast die Mission vollendet." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "F2 FÜR NEUSTART" + +#: principal.form:45 +msgid "Enjoy Open Source!" +msgstr "Genieße Open Source!" + +#: principal.form:50 +msgid "&Configuración" +msgstr "&Einstellungen" + +#: principal.form:53 +msgid "&Nuevo Juego" +msgstr "&Neues Spiel" + +#: principal.form:58 +msgid "&Mejores Puntajes" +msgstr "&Beste Ergebnisse" + +#: principal.form:64 +msgid "Nivel" +msgstr "Niveau" + +#: principal.form:68 +msgid "&Principiante" +msgstr "&Anfänger" + +#: principal.form:76 +msgid "M&edio" +msgstr "&Mittel" + +#: principal.form:83 +msgid "E&xperto" +msgstr "E&xperte" + +#: principal.form:90 +msgid "&Sonido" +msgstr "&Sound" + +#: principal.form:100 +msgid "&Salir" +msgstr "&Beenden" + +#: principal.form:105 +msgid "&Ayuda" +msgstr "&Hilfe" + +#: principal.form:108 +msgid "A&cerca de" +msgstr "&Über..." + +#: principal.form:112 +msgid "&Instrucciones" +msgstr "&Anweisungen" + +#: principal.form:251 +msgid "Jugadas: " +msgstr "Züge:" + +#: principal.form:259 +msgid "Tiempo: " +msgstr "Zeit:" + +#: principal.form:268 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "F4 ZUM START DER MISSION" + +#: principal.form:283 +msgid "0" +msgstr "-" diff --git a/app/examples/Games/Concent/.lang/en.po b/app/examples/Games/Concent/.lang/en.po new file mode 100644 index 00000000..a68ad871 --- /dev/null +++ b/app/examples/Games/Concent/.lang/en.po @@ -0,0 +1,226 @@ +# #-#-#-#-# fotos.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/fotos.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# frmAcerca.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmAcerca.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# frmInstrucciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmInstrucciones.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# funciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/funciones.module +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# principal.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/principal.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# #project.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent_gb/.project +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Geografic Concent" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Ths version incorporate the english and spanish support, enjoy this game, the corrections and sugerences made by gambas development group (Benoit Missini) are finished." + +#: fotos.form:292 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" +"This form cumplies with the unique function of \n" +" load 40 buttons with images for the game\n" +", may it would can be resolved with other method, but I know not it." + +#: frmAcerca.form:14 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Geografic Concent v 0.3\n" +"\n" +"Writed for Gambas by:\n" +"Jairo Alonso Badillo Bedoya-jbadbe@gmail.com\n" +"Published under GPL terms license\n" +"Live Colombia!!!!!!\n" +"Please visit http://www.linuxeam.tk - A Linux comunity" + +#: frmAcerca.form:20 frmInstrucciones.form:28 +msgid "&Aceptar" +msgstr "&Ok" + +#: frmInstrucciones.form:15 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "HOW TO PLAY THIS GAME?" + +#: frmInstrucciones.form:21 +#, fuzzy +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" +"This Game is very easy:\n" +"\n" +"\n" +"1.) The Game Objetive is find the faces under each button, the faces have duplicates,\n" +" in a movement you have find two equal faces.\n" +"\n" +" 2.) You can choose the dificult level in the config menu, in each level the board have\n" +" more buttons, enjoy this game!\n" +"\n" +" 3). Actually I am development the ask module, this module permit answer many asks\n" +" in the game for increment your points.\n" +"\n" +" 4.) You can activate or desactivate the sounds in the configuration menu\n" +" (Configuration - Sound).\n" +"\n" +" 5.) Remember: for init a mision press F4, if you want redistribute the board press F2\n" +" in any moment, the game will go to reset. The faces are load in rand mode, Is \n" +" imposible remember the board...!!!!\n" +"\n" +" 6.) For comments and ask, plese write me to jbadbe@gmail.com, please excuseme \n" +" for my English, I am Colombiano and I dont speak english.\n" +"\n" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Bad move!" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Congratulations, the mission has been accomplished." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "PRESS F2 TO PLAY AGAIN" + +#: principal.form:45 +msgid "Enjoy Open Source!" +msgstr "Enjoy Open Source" + +#: principal.form:50 +msgid "&Configuración" +msgstr "&Configuration" + +#: principal.form:53 +msgid "&Nuevo Juego" +msgstr "&New Game" + +#: principal.form:58 +msgid "&Mejores Puntajes" +msgstr "&Best Scores" + +#: principal.form:64 +msgid "Nivel" +msgstr "Level" + +#: principal.form:68 +msgid "&Principiante" +msgstr "&Beginner" + +#: principal.form:76 +msgid "M&edio" +msgstr "M&edium" + +#: principal.form:83 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.form:90 +msgid "&Sonido" +msgstr "&Sound" + +#: principal.form:100 +msgid "&Salir" +msgstr "&Exit" + +#: principal.form:105 +msgid "&Ayuda" +msgstr "&Help" + +#: principal.form:108 +msgid "A&cerca de" +msgstr "&About this Game" + +#: principal.form:112 +msgid "&Instrucciones" +msgstr "&Instructions" + +#: principal.form:251 +msgid "Jugadas: " +msgstr "Movements" + +#: principal.form:259 +msgid "Tiempo: " +msgstr "Time:" + +#: principal.form:268 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "PRESS F4 TO START THE MISSION" + +#: principal.form:283 +msgid "0" +msgstr "0" diff --git a/app/examples/Games/Concent/.lang/es.po b/app/examples/Games/Concent/.lang/es.po new file mode 100644 index 00000000..f6c79229 --- /dev/null +++ b/app/examples/Games/Concent/.lang/es.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: principal.class:694 +msgid "0" +msgstr "0" + +#: frmAcerca.class:37 frmInstrucciones.class:40 +msgid "&Aceptar" +msgstr "&Aceptar" + +#: principal.class:503 +msgid "A&cerca de" +msgstr "A&cerca de" + +#: principal.class:500 +msgid "&Ayuda" +msgstr "&Ayuda" + +#: frmInstrucciones.class:30 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "¿CÓMO JUGAR CONCÉNTRESE?" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Concéntrese Geográfico" + +#: frmAcerca.class:31 +msgid "Concéntrese Geográfico v 0.3\n\nProgramado para Gambas por:\nJairo Alonso Badillo Bedoya - jbadbe@gmail.com\nPublicado bajo los términos de la licencia GPL\nViva Colombia!!!!!\nVisite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "Concéntrese Geográfico v 0.3\n\nProgramado para Gambas por:\nJairo Alonso Badillo Bedoya - jbadbe@gmail.com\nPublicado bajo los términos de la licencia GPL\nViva Colombia!!!!!\nVisite http://www.linuxeam.tk -Una comunidad Linux" + +#: principal.class:444 +msgid "&Configuración" +msgstr "&Configuración" + +#: principal.class:439 +msgid "Enjoy Open Source!" +msgstr "¡Disfrute del Código Abierto!" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gam" + +#: fotos.class:304 +msgid "Estimados amigos, este formulario cumple la única función de\n cargar en 40 botones, las imágenes de las caritas que servirán \npara el juego, es posible que exista una mejor manera de \nhacer esto, por eso... escucho sugerencias, posteriormente \nen el código se crea un array de objetos a fin de referencialos \ncomo botoncito[i].Picture y copiar este atributo en particular al \ntablero de juego. Att: Jairo Alonso Badillo\n jbadbe@gmail.com" +msgstr "Estimados amigos, este formulario cumple la única función de\n cargar en 40 botones, las imágenes de las caritas que servirán \npara el juego, es posible que exista una mejor manera de \nhacer esto, por eso... escucho sugerencias, posteriormente \nen el código se crea un array de objetos a fin de referencialos \ncomo botoncito[i].Picture y copiar este atributo en particular al \ntablero de juego. Att: Jairo Alonso Badillo\n jbadbe@gmail.com" + +#: principal.class:477 +msgid "E&xperto" +msgstr "E&xperto" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Felicitaciones, has cumplido la mision" + +#: principal.class:507 +msgid "&Instrucciones" +msgstr "&Instrucciones" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Jugada Errada" + +#: principal.class:663 +msgid "Jugadas: " +msgstr "Jugadas: " + +#: principal.class:470 +msgid "M&edio" +msgstr "M&edio" + +#: principal.class:452 +msgid "&Mejores Puntajes" +msgstr "&Mejores Puntajes" + +#: principal.class:458 +msgid "Nivel" +msgstr "Nivel" + +#: principal.class:447 +msgid "&Nuevo Juego" +msgstr "&Nuevo Juego" + +#: principal.class:462 +msgid "&Principiante" +msgstr "&Principiante" + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "PULSE F2 PARA REPARTIR DE NUEVO" + +#: principal.class:384 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "PULSE F4 PARA COMENZAR LA MISION" + +#: principal.class:495 +msgid "&Salir" +msgstr "&Salir" + +#: frmInstrucciones.class:35 +msgid "Se trata de un Juego Bastante sencillo: \n\n\n1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n debes encontrar los pares de rostros que hay ocultos en el tablero de\n concéntrese. \n \n2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n y seleccionar uno de los tres preestablecidos. \n\n \n3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n cuestionario emergente que deberás responder con cada acierto en el tablero, si\n contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n amigos. (Esta función se encuentra en construcción debido a la migracion de\n Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n OpenSource!!! \n \n4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n para lo cual debes cambiar el estado del menú Configuración - Sonido. \n \n5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n \n6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "Se trata de un Juego Bastante sencillo: \n\n\n1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n debes encontrar los pares de rostros que hay ocultos en el tablero de\n concéntrese. \n \n2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n y seleccionar uno de los tres preestablecidos. \n\n \n3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n cuestionario emergente que deberás responder con cada acierto en el tablero, si\n contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n amigos. (Esta función se encuentra en construcción debido a la migracion de\n Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n OpenSource!!! \n \n4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n para lo cual debes cambiar el estado del menú Configuración - Sonido. \n \n5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n \n6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" + +#: principal.class:484 +msgid "&Sonido" +msgstr "&Sonido" + +#: principal.class:671 +msgid "Tiempo: " +msgstr "Tiempo: " + diff --git a/app/examples/Games/Concent/.lang/fr.po b/app/examples/Games/Concent/.lang/fr.po new file mode 100644 index 00000000..6dbc7e7f --- /dev/null +++ b/app/examples/Games/Concent/.lang/fr.po @@ -0,0 +1,164 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: fotos.class:303 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" + +#: frmAcerca.class:30 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Geografic Concent v 0.3\n" +"\n" +"Ecrit pour Gambas par:\n" +"Jairo Alonso Badillo Bedoya-jbadbe@gmail.com\n" +"Publié sous license GNU\n" +"Vive la Colombie!!!!!!\n" +"Veuillez visiter http://www.linuxeam.tk - Une communauté Linux" + +#: frmAcerca.class:36 frmInstrucciones.class:43 +msgid "&Aceptar" +msgstr "&OK" + +#: frmInstrucciones.class:30 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "COMMENT JOUER A CE JEU ?" + +#: frmInstrucciones.class:36 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Mouvement incorrect !" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Félicitations, vous avez rempli la mission." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "APPUYEZ SUR F2 POUR RECOMMENCER" + +#: principal.class:384 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "APPUYEZ SUR F4 POUR COMMENCER" + +#: principal.class:439 +msgid "Enjoy Open Source!" +msgstr "Profitez des logiciels libres !" + +#: principal.class:444 +msgid "&Configuración" +msgstr "&Configuration" + +#: principal.class:447 +msgid "&Nuevo Juego" +msgstr "&Nouveau jeu" + +#: principal.class:452 +msgid "&Mejores Puntajes" +msgstr "Meilleurs &scores" + +#: principal.class:458 +msgid "Nivel" +msgstr "Niveau" + +#: principal.class:462 +msgid "&Principiante" +msgstr "&Débutant" + +#: principal.class:470 +msgid "M&edio" +msgstr "&Moyen" + +#: principal.class:477 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.class:484 +msgid "&Sonido" +msgstr "&Son" + +#: principal.class:494 +msgid "&Salir" +msgstr "&Quitter" + +#: principal.class:499 +msgid "&Ayuda" +msgstr "&Aide" + +#: principal.class:502 +msgid "A&cerca de" +msgstr "A propos de..." + +#: principal.class:506 +msgid "&Instrucciones" +msgstr "&Instructions (non traduites)" + +#: principal.class:645 +msgid "Jugadas: " +msgstr "Mouvements :" + +#: principal.class:653 +msgid "Tiempo: " +msgstr "Temps :" + +#: principal.class:677 +msgid "0" +msgstr "" + +#~ msgid "Concéntrese Geográfico" +#~ msgstr "Concentré Géographique" diff --git a/app/examples/Games/Concent/.lang/ru.po b/app/examples/Games/Concent/.lang/ru.po new file mode 100644 index 00000000..38c1b3cb --- /dev/null +++ b/app/examples/Games/Concent/.lang/ru.po @@ -0,0 +1,222 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/Concent/.project:23 +msgid "Concéntrese Geográfico" +msgstr "Концентрация" + +#: app/examples/Games/Concent/.project:24 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Эта версия включает в себя поддержку английского и испанского языка (родного), наслаждайтесь этой игрой! Исправления, предложенные руководителем проекта Gambas, включены, проект переведён на английский язык с использованием интегрированного инструмента Gambas Ide" + +#: app/examples/Games/Concent/.src/fotos.form:208 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" +"Дорогие друзья, эта форма выполняет единственную\n" +"функцию для загрузки из 40 кнопок - изображений\n" +"персонажей, которые будут служить для игры, возможно,\n" +"что есть лучший способ сделать это, поэтому... я слушаю\n" +"предложения, позже в коде создайте массив объектов\n" +"для ссылок, таких как кнопка[i]. Изобразите и скопируйте\n" +"этот конкретный атрибут на игровую доску. Акт: Хайро\n" +"Алонсо Бадильо jbadbe@gmail.com" + +#: app/examples/Games/Concent/.src/frmAcerca.form:8 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Концентрация версии 0.3\n" +"\n" +"Запрограммировано для Gambas:\n" +"Хайро Алонсо Бадильо Бедоя - jbadbe@gmail.com\n" +"Опубликовано в соответствии с условиями лицензии GPL\n" +"Да здравствует Колумбия!!!!!\n" +"Посетите http://www.linuxeam.tk - сообщество Linux" + +#: app/examples/Games/Concent/.src/frmAcerca.form:13 app/examples/Games/Concent/.src/frmInstrucciones.form:20 +msgid "&Aceptar" +msgstr "Принять" + +#: app/examples/Games/Concent/.src/frmInstrucciones.form:9 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "Как играть в концентрацию?" + +#: app/examples/Games/Concent/.src/frmInstrucciones.form:14 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" +"Это довольно простая игра:\n" +"\n" +"1.) Цель игры - проверить вашу внимательность и уровень концентрации,\n" +"для чего вы должны найти пары лиц, которые спрятаны в поле\n" +"концентрации.\n" +" \n" +"2.) В игре есть три уровня (новичок, среднячок, эксперт), которые\n" +"позволяют вам выбирать между тремя размерами доски; чтобы\n" +"добавить эмоции и сложность в игру, чтобы получить доступ к этим\n" +"параметрам, вы должны войти через меню Конфигурация -> Уровень\n" +"и выбрать одну из трёх предустановок. \n" +" \n" +"3). Кроме того, у вас есть возможность повысить свои знания при игре в\n" +"режиме вопросов, которая повышает ваш образовательный уровень; это\n" +"достигается с помощью новой анкеты, на которую вы должны отвечать при\n" +"каждом успехе на доске; если вы ответите правильно, у вас будет более\n" +"высокий балл, и вы сможете соревноваться с друзьями. (Эта функция находится\n" +"в стадии разработки в связи с миграцией Visual Basic на Gambas, и для того,\n" +"чтобы испытать всю мощь Linux, используйте открытый исходный код!!!\n" +" \n" +"4.) Игра также позволяет активировать или деактивировать окружающие\n" +"звуки, для чего необходимо изменить статус в меню Конфигурация -> Звук.\n" +" \n" +"5.) Помните: чтобы перераспределить доску - нажмите F2; чтобы начать играть -\n" +"нажмите F4; лица будут загружены случайным образом, поэтому вы не должны\n" +"пытаться изучить плату памяти...! Это будет настоящим испытанием!\n" +" \n" +"6.) В настоящее время я разрабатываю учебные модули, которые вы можете\n" +"получить очень скоро; для комментариев и/или предложений пишите на:\n" +"jbadbe@gmail.com.\n" + +#: app/examples/Games/Concent/.src/principal.class:166 +msgid "Jugada Errada" +msgstr "Плохой ход" + +#: app/examples/Games/Concent/.src/principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Поздравляю, вы выполнили миссию" + +#: app/examples/Games/Concent/.src/principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "Нажмите F2, чтобы начать заново" + +#: app/examples/Games/Concent/.src/principal.class:384 app/examples/Games/Concent/.src/principal.form:196 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "Нажмите F4, чтобы начать миссию" + +#: app/examples/Games/Concent/.src/principal.form:6 +msgid "Enjoy Open Source!" +msgstr "Наслаждайтесь открытым исходным кодом!" + +#: app/examples/Games/Concent/.src/principal.form:10 +msgid "&Configuración" +msgstr "Конфигурация" + +#: app/examples/Games/Concent/.src/principal.form:12 +msgid "&Nuevo Juego" +msgstr "Новая игра" + +#: app/examples/Games/Concent/.src/principal.form:16 +msgid "&Mejores Puntajes" +msgstr "Лучшие результаты" + +#: app/examples/Games/Concent/.src/principal.form:21 +msgid "Nivel" +msgstr "Уровень" + +#: app/examples/Games/Concent/.src/principal.form:24 +msgid "&Principiante" +msgstr "Новичок" + +#: app/examples/Games/Concent/.src/principal.form:31 +msgid "M&edio" +msgstr "Среднячок" + +#: app/examples/Games/Concent/.src/principal.form:37 +msgid "E&xperto" +msgstr "Эксперт" + +#: app/examples/Games/Concent/.src/principal.form:43 +msgid "&Sonido" +msgstr "Звук" + +#: app/examples/Games/Concent/.src/principal.form:51 +msgid "&Salir" +msgstr "Выход" + +#: app/examples/Games/Concent/.src/principal.form:55 +msgid "&Ayuda" +msgstr "Помощь" + +#: app/examples/Games/Concent/.src/principal.form:57 +msgid "A&cerca de" +msgstr "О программе" + +#: app/examples/Games/Concent/.src/principal.form:60 +msgid "&Instrucciones" +msgstr "Инструкции" + +#: app/examples/Games/Concent/.src/principal.form:181 +msgid "Jugadas: " +msgstr "Ходы: " + +#: app/examples/Games/Concent/.src/principal.form:188 +msgid "Tiempo: " +msgstr "Время: " + +#: app/examples/Games/Concent/.src/principal.form:210 app/examples/Games/Concent/.src/principal.form:217 +msgid "0" +msgstr "0" + diff --git a/app/examples/Games/Concent/.project b/app/examples/Games/Concent/.project new file mode 100644 index 00000000..3b78d34e --- /dev/null +++ b/app/examples/Games/Concent/.project @@ -0,0 +1,22 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Concéntrese Geográfico +Startup=principal +Icon=imagenes/logo.png +Version=3.6.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.db +Component=gb.sdl.sound +Description="Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +Authors="Jairo Alonso Badillo Bedoya" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 +Systems=redhat diff --git a/app/examples/Games/Concent/.src/fotos.class b/app/examples/Games/Concent/.src/fotos.class new file mode 100644 index 00000000..5c4d1122 --- /dev/null +++ b/app/examples/Games/Concent/.src/fotos.class @@ -0,0 +1,7 @@ +' Gambas class file + + + +PUBLIC SUB Form_Open() + +END diff --git a/app/examples/Games/Concent/.src/fotos.form b/app/examples/Games/Concent/.src/fotos.form new file mode 100644 index 00000000..0e79fe06 --- /dev/null +++ b/app/examples/Games/Concent/.src/fotos.form @@ -0,0 +1,210 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(54.3333,9.6667,56.6667,57) + Text = ("") + Resizable = False + { Button1 Button + MoveScaled(1,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an1.gif"] + } + { Button2 Button + MoveScaled(8,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an11.gif"] + } + { Button3 Button + MoveScaled(14.8333,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an12.gif"] + } + { Button4 Button + MoveScaled(21.6667,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an13.gif"] + } + { Button5 Button + MoveScaled(28.5,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an14.gif"] + } + { Button6 Button + MoveScaled(35.3333,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an15.gif"] + } + { Button7 Button + MoveScaled(42.1667,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an16.gif"] + } + { Button8 Button + MoveScaled(49,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an17.gif"] + } + { Button9 Button + MoveScaled(28.5,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an21.gif"] + } + { Button10 Button + MoveScaled(21.6667,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an20.gif"] + } + { Button11 Button + MoveScaled(14.8333,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an2.gif"] + } + { Button12 Button + MoveScaled(8,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an19.gif"] + } + { Button13 Button + MoveScaled(1,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an18.gif"] + } + { Button14 Button + MoveScaled(35.3333,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an22.gif"] + } + { Button15 Button + MoveScaled(42.1667,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an23.gif"] + } + { Button16 Button + MoveScaled(49,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an24.gif"] + } + { Button17 Button + MoveScaled(28.5,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an29.gif"] + } + { Button18 Button + MoveScaled(21.6667,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an28.gif"] + } + { Button19 Button + MoveScaled(14.8333,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an27.gif"] + } + { Button20 Button + MoveScaled(8,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an26.gif"] + } + { Button21 Button + MoveScaled(1,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an25.gif"] + } + { Button22 Button + MoveScaled(35.3333,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an3.gif"] + } + { Button23 Button + MoveScaled(42.1667,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an30.gif"] + } + { Button24 Button + MoveScaled(49,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an31.gif"] + } + { Button25 Button + MoveScaled(28.5,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an36.gif"] + } + { Button26 Button + MoveScaled(21.6667,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an35.gif"] + } + { Button27 Button + MoveScaled(14.8333,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an34.gif"] + } + { Button28 Button + MoveScaled(8,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an33.gif"] + } + { Button29 Button + MoveScaled(1,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an32.gif"] + } + { Button30 Button + MoveScaled(35.3333,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an37.gif"] + } + { Button31 Button + MoveScaled(42.1667,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an38.gif"] + } + { Button32 Button + MoveScaled(49,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an39.gif"] + } + { Button33 Button + MoveScaled(28.5,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an7.gif"] + } + { Button34 Button + MoveScaled(21.6667,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an6.gif"] + } + { Button35 Button + MoveScaled(14.8333,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an5.gif"] + } + { Button36 Button + MoveScaled(8,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an40.gif"] + } + { Button37 Button + MoveScaled(1,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an4.gif"] + } + { Button38 Button + MoveScaled(35.3333,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an8.gif"] + } + { Button39 Button + MoveScaled(42.1667,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an9.gif"] + } + { Button40 Button + MoveScaled(49,50.1667,6.3333,6.3333) + Text = ("") + } + { Label1 Label + MoveScaled(1,1,53.6667,20.5) + Text = ("Estimados amigos, este formulario cumple la \xC3\xBAnica funci\xC3\xB3n de\n cargar en 40 botones, las im\xC3\xA1genes de las caritas que servir\xC3\xA1n \npara el juego, es posible que exista una mejor manera de \nhacer esto, por eso... escucho sugerencias, posteriormente \nen el c\xC3\xB3digo se crea un array de objetos a fin de referencialos \ncomo botoncito[i].Picture y copiar este atributo en particular al \ntablero de juego. Att: Jairo Alonso Badillo\n jbadbe@gmail.com") + } +} diff --git a/app/examples/Games/Concent/.src/frmAcerca.class b/app/examples/Games/Concent/.src/frmAcerca.class new file mode 100644 index 00000000..191c36df --- /dev/null +++ b/app/examples/Games/Concent/.src/frmAcerca.class @@ -0,0 +1,12 @@ +' Gambas class file + +PUBLIC SUB Button1_Click() + + ME.Close + +END + +PUBLIC SUB Form_Open() + + +END diff --git a/app/examples/Games/Concent/.src/frmAcerca.form b/app/examples/Games/Concent/.src/frmAcerca.form new file mode 100644 index 00000000..bb87ee31 --- /dev/null +++ b/app/examples/Games/Concent/.src/frmAcerca.form @@ -0,0 +1,20 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(59.5714,26.7143,67,31) + Resizable = False + { Label1 Label + MoveScaled(1,1,65,24) + Text = ("Concéntrese Geográfico v 0.3\n\nProgramado para Gambas por:\nJairo Alonso Badillo Bedoya - jbadbe@gmail.com\nPublicado bajo los términos de la licencia GPL\nViva Colombia!!!!!\nVisite http://www.linuxeam.tk -Una comunidad Linux") + Alignment = Align.Center + } + { Button1 Button + MoveScaled(24,26,19,4) + Text = ("&Aceptar") + } + { PictureBox1 PictureBox + MoveScaled(8,4,6.7143,4) + Expand = True + Picture = Picture["imagenes/colombia.gif"] + } +} diff --git a/app/examples/Games/Concent/.src/frmInstrucciones.class b/app/examples/Games/Concent/.src/frmInstrucciones.class new file mode 100644 index 00000000..e0dfd43d --- /dev/null +++ b/app/examples/Games/Concent/.src/frmInstrucciones.class @@ -0,0 +1,11 @@ +' Gambas class file + +Public Sub Form_Open() + +End + +Public Sub Button1_Click() + + Me.Close + +End diff --git a/app/examples/Games/Concent/.src/frmInstrucciones.form b/app/examples/Games/Concent/.src/frmInstrucciones.form new file mode 100644 index 00000000..00f5cfc3 --- /dev/null +++ b/app/examples/Games/Concent/.src/frmInstrucciones.form @@ -0,0 +1,22 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(63.8571,1.7143,86,90) + Resizable = False + { Label1 Label + MoveScaled(1,1,83,4) + Font = Font["Bold,+2"] + Text = ("COMO JUGAR CONCÉNTRESE?") + } + { Label2 Label + MoveScaled(1,6,84,78) + Padding = 8 + Text = ("Se trata de un Juego Bastante sencillo: \n\n1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n debes encontrar los pares de rostros que hay ocultos en el tablero de\n concéntrese. \n \n2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n y seleccionar uno de los tres preestablecidos. \n \n3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n cuestionario emergente que deberás responder con cada acierto en el tablero, si\n contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n amigos. (Esta función se encuentra en construcción debido a la migracion de\n Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n OpenSource!!! \n \n4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n para lo cual debes cambiar el estado del menú Configuración - Sonido. \n \n5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n \n6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n") + Alignment = Align.TopNormal + Border = Border.Plain + } + { Button1 Button + MoveScaled(34,85,18,4) + Text = ("&Aceptar") + } +} diff --git a/app/examples/Games/Concent/.src/funciones.module b/app/examples/Games/Concent/.src/funciones.module new file mode 100644 index 00000000..6cbc1714 --- /dev/null +++ b/app/examples/Games/Concent/.src/funciones.module @@ -0,0 +1,162 @@ +' Gambas module file + +'Arreglos para guardar info de las imagenes a cargar y el estado de los botones +'................................................................................ +'Arrays for save images information to load. +Private botones_numero_foticos As New Integer[64] 'Almacena el numero de foto (indice) que llevara cada botoncito al descubrirse + 'Almacenate the image number (index). +Private boton_lleno As New Boolean[64] 'para especificar si un boton ya recibio asignacion de fotico + 'Specify if a button have a image asigned + +'Variables de juego +Public jugadas As Integer 'contador del numero de jugadas durante una partida (counter for number of movements) +Public tiempo As Integer 'contador del numero de segundos durante una partida (counter for game time) +Public aciertos As Integer 'Contar el numero de aciertos del jugador, es decir, cada vez que el jugador logra en una misma + 'jugada encontrar dos botones con el mismo dibujito + '.............................................................................................. + 'Count the acerts for player. +Public num_movimiento_actual As Integer 'Siempre tendra valor de 0, o 1. se usa para saber si el jugador ya destapo un boton el turno actual + 'es decir, una jugada consta de dos movimientos + '.................................................................................. + 'Controle the set of button moved in a movement + +Public Sub distrib(tam_tablero As Integer, tam_botones As Integer) + Dim i As Integer + Dim j As Integer + Dim izq As Integer + Dim arr As Integer + Dim pos As Integer + + izq = (principal.Width - tam_botones * tam_tablero) / 2 + arr = (principal.Height - tam_botones * tam_tablero) / 2 + + pos = 0 + + For I = 0 To tam_tablero - 1 + For J = 0 To tam_tablero - 1 + principal.botones[pos].visible = True + principal.botones[pos].width = tam_botones + principal.botones[pos].height = tam_botones + principal.botones[pos].x = izq + principal.botones[pos].y = arr + principal.botones[pos].picture = principal.initialPicture 'cada vez que se distribuye el tablero, se tapas los botone + 'con el dibujito del signo de interrogacion + '........................................................ + 'Restore the buttons to initial state, when the game is + 'restarted + 'principal.botones[pos].enabled=TRUE + izq = izq + tam_botones + pos = pos + 1 + Next + arr = arr + tam_botones + izq = (principal.Width - tam_botones * tam_tablero) / 2 + + Next + + 'Se ocultan los botones que no sean necesarios de acuerdo al nivel + 'de juego, esto pasa cuando se ha jugado en un nivel superior y + 'luego se vuelve hacia un nivel inferior + '.................................................................. + 'The not necesary button are oculted + For I = tam_tablero * tam_tablero To 63 + principal.botones[pos].visible = False + pos = pos + 1 + Next + + 'se asigna false al array boton_lleno para iniciar una nueva asignacion de foticos + '.................................................................................. + 'The state for each button is restored, it is for permit movement with each button + For I = 0 To 63 + boton_lleno[I] = False + Next + + 'Se cargan las imagenes en cada botoncito + '......................................... + 'Load image for each button + repartir(tam_tablero * tam_tablero) + +End + + +'Este procedimiento busca en el directorio imagenes para capturar los +'nombres de los dibujitos que se van a mostrar en los botones, esta +'funcionalidad permite que el usuario incluya luego sus propias imagenes +'PUBLIC SUB cargar_imagenes(Directory AS String) + + 'DIM File AS String + 'DIM indice AS Integer + 'indice=0 + + 'FOR EACH File IN Dir(Directory, "an*.gif") + 'imagenes[indice]=File + 'indice=indice+1 + 'NEXT + +'END + +Public Sub repartir(tam_tablero As Integer) + Dim i As Integer + Dim j As Integer + Dim aleatorio As Integer + Dim aleatorio1 As Integer + Dim listo As Boolean 'Bandera para informar cuando se encuentre un boton libre para asignacion + 'Variable for controlate the flow in nex loop + + 'Ciclar la mitad de la dimension para ubicar las imagenes en cada boton, se cicla la mitad ya que cada imagen + 'escogida en un ciclo se asigna a una pareja de botoncitos, la imagen escogida se representa como un numero + 'entero, que es utilizado luego como indice del array botones_foticos[] para copiar la propiedad picture, este + 'numero se guarda en el arreglo botones_numero_foticos[64]. + '.............................................................................................................. + 'This For loop permit locate the images in each button + For i = 0 To Int(tam_tablero - 1) / 2 + aleatorio = Int(Rnd() * 40) 'se obtiene un numero entre (0, 39) que es el rango de indices del array botones_foticos + 'Obtain a random number in 0-39 range (there is 40 images for use) + 'IF aleatorio = 40 THEN message ("salio un numero malo") + 'en cada ciclo se ubica dos veces la misma fotico en dos cuadros aleatorios + '.......................................................................... + 'For each loop a image is locate at two times + For j = 0 To 1 + listo = False + While listo = False 'se anida este ciclo para forzar la busqueda de dos cuadritos que no hayan sido asignados + 'This loop is nested for force the search + aleatorio1 = Int(Rnd() * tam_tablero) 'se busca un indice en el rango (0, tam_tablero) + 'An index in 0,tam_table range is searched + If (boton_lleno[aleatorio1]) = False Then 'se verifica que el boton no tenga fotico asignada + 'Verify the button for stablish it is free + botones_numero_foticos[aleatorio1] = aleatorio + boton_lleno[aleatorio1] = True + listo = True + 'ACTIVAR LA LINEA SIGUIENTE PARA DEPURACION, ENTONCES SE PODRA VER LA IMAGEN OCULTA DE LOS BOTONES + 'principal.botones[aleatorio1].picture=principal.botones_foticos[aleatorio].picture + End If + Wend + + Next + + Next + + 'Se inicializan las variables de juego + '.................................... + 'Initialize game variables + jugadas = 0 + tiempo = 0 + aciertos = 0 + num_movimiento_actual = 0 + +End + +'Se hace necesario este metodo para notificar al formulario principal sobre cual imagen debe colocar en un botoncito +'al ser descubierto, esto debido a que el arreglo botones_numero_foticos[] es pridado en este modulo +Public Function devolver_indice_defotico(tag As Integer) As Integer + Return botones_numero_foticos[tag] +End + +'Devuelve un valor booleano indicando si los dos movimientos de una jugada son validos +Public Function comparar_botones_jugados(btjugado1 As Integer, btjugado2 As Integer) As Boolean + Return botones_numero_foticos[btjugado1] = botones_numero_foticos[btjugado2] +End + + + + + diff --git a/app/examples/Games/Concent/.src/principal.class b/app/examples/Games/Concent/.src/principal.class new file mode 100644 index 00000000..964c0d85 --- /dev/null +++ b/app/examples/Games/Concent/.src/principal.class @@ -0,0 +1,390 @@ +' Gambas class file + +' Gambas class +Public boton As Button 'object variable for charge buttons on ejecution time +Public sonido As Sound 'objeto que permite lanzar los sonidos(Play the Sounds) +Public aplauso As Sound 'Sonido de aplauso cuando se acierta en un par de caritas (Applause Sound at each movement) +Public botones As Object[] 'Arreglo de botones del tablero de juego (Buttons array for game board) +Public botones_foticos As Object[] 'Arreglo de botones con las figuritas (Buttons array with images loaded) + 'en el form fotos +'variables del juego +Public tam_tablero As Integer 'para conocer en todo momento cual es el tamaño del tablero (filas y columnas) + 'It is for know in all moment the boad size(row and columns) + +Public habilitar As Boolean 'para establecer si el jugador puede empezar a descubrir los botoncitos + 'Stablish if player can move + + +Private anterior_boton_jugado As Integer 'se utiliza para recordar el numero del ultimo boton pulsado, a fin de poder + 'restaurarlo, en el caso del primer movimiento de una jugada, cuando no hay coincidencia + 'de las imagenes destapadas (remember last pushed button) + +'Las siguientes dos variables fueron necesarias a fin de preservar la informacion contenida en la variable anterior_boton_jugado +'y en LAST.tag durante la ejecucion del evento click sobre los botones, esto debido a que la ejecucion de codigo no se +'interrumpe con la activacion del timer, de esta forma se permite un retardo en la visualizacion de pares de botones descubiertos +'para que el jugador pueda reconocer la figura oculta en caso de que ambos dibujos no coincidan +'............................................................................................... +'Next variables would be needed for save information in variable anterior_boton_jugado and LAST.tag (for grupo_botones group) +'for click event, it is for permit a delay in each movement, so the player can visualize every faces. + +Private boton_actual_paratimer As Integer +Private boton_ultimo_paratimer As Integer + +'FUNCIONALIDAD AÑADIDA DE ACCESO A BASE DE DATOS PARA ALMACENAR LOS DATOS DEL +'JUEGO, MEJORES PUNTAJES POR CATEGORIA +'PRIVATE myarchivo AS File +'PRIVATE mylinea AS String + +'Variable para almacenar el idioma en uso +'........................................ +'Capture and almacenate the language in use +Public language As String + +Public initialPicture As Picture + +Public Sub _new() + +Dim i As Integer + +Randomize + +sonido = New Sound("Wallhit.wav") 'Instancia del objeto sonido - Sound object instance +aplauso = New Sound("applause.wav") + +initialPicture = Picture["imagenes/inter.jpg"] + + 'IF NOT Access(system.Home &/"myarchivo.ini") THEN + ' message ("no existe") + ' OPEN system.Home &/"myarchivo.ini" FOR CREATE AS myarchivo + ' 'WHILE NOT Eof(myarchivo) + ' 'LINE INPUT #myarchivo, mylinea + ' 'message (mylinea) + ' 'WEND + ' myarchivo.Save("hola") + ' + ' CLOSE myarchivo + 'END IF + + + 'Se crea el array de objetos botones, esto con el fin de facilitar la manipulacion + 'de eventos comunes a un grupo de botones. + '.................................................................................. + 'Create the buttons to be used for next game + botones = New Object[] 'Array of buttons + + For i = 1 To 64 + boton = New Button(Me) As "grupobotones" + boton.tag = i - 1 + 'The button object array is created, it is for manipulate the common events to group buttons + botones.Add(boton) ' + Next + + 'Se crea este arreglo para tomar referencia de las foticos de los botones + 'en el form fotos + '.......................................................................... + 'This array contain a reference for images to visualice in the game + botones_foticos = New Object[] + For i = 1 To 40 + boton = New Button(Me) + boton.Visible = False + boton.Picture = Picture["imagenes/an" & CStr(i) & ".gif"] + botones_foticos.Add(boton) + Next + ' botones_foticos.Add(fotos.Button1) + ' botones_foticos.Add(fotos.Button2) + ' botones_foticos.Add(fotos.Button3) + ' botones_foticos.Add(fotos.Button4) + ' botones_foticos.Add(fotos.Button5) + ' botones_foticos.Add(fotos.Button6) + ' botones_foticos.Add(fotos.Button7) + ' botones_foticos.Add(fotos.Button8) + ' botones_foticos.Add(fotos.Button9) + ' botones_foticos.Add(fotos.Button10) + ' botones_foticos.Add(fotos.Button11) + ' botones_foticos.Add(fotos.Button12) + ' botones_foticos.Add(fotos.Button13) + ' botones_foticos.Add(fotos.Button14) + ' botones_foticos.Add(fotos.Button15) + ' botones_foticos.Add(fotos.Button16) + ' botones_foticos.Add(fotos.Button17) + ' botones_foticos.Add(fotos.Button18) + ' botones_foticos.Add(fotos.Button19) + ' botones_foticos.Add(fotos.Button20) + ' botones_foticos.Add(fotos.Button21) + ' botones_foticos.Add(fotos.Button22) + ' botones_foticos.Add(fotos.Button23) + ' botones_foticos.Add(fotos.Button24) + ' botones_foticos.Add(fotos.Button25) + ' botones_foticos.Add(fotos.Button26) + ' botones_foticos.Add(fotos.Button27) + ' botones_foticos.Add(fotos.Button28) + ' botones_foticos.Add(fotos.Button29) + ' botones_foticos.Add(fotos.Button30) + ' botones_foticos.Add(fotos.Button31) + ' botones_foticos.Add(fotos.Button32) + ' botones_foticos.Add(fotos.Button33) + ' botones_foticos.Add(fotos.Button34) + ' botones_foticos.Add(fotos.Button35) + ' botones_foticos.Add(fotos.Button36) + ' botones_foticos.Add(fotos.Button37) + ' botones_foticos.Add(fotos.Button38) + ' botones_foticos.Add(fotos.Button39) + ' botones_foticos.Add(fotos.Button40) +'Pensaba permitir al usuario normal el cambio de las imagenes de los +'botoncitos pero, parece mejor que el usuario que quiera modificar estos +'dibujos, lo haga en el proyecto fuente +'....................................................................... +'Maybe is possible permit to user manipulate the images, but for now it is +'done only by code +tam_tablero = 4 +funciones.distrib(tam_tablero, 45) + +End + +Public Sub grupobotones_Click() + + Dim jugada_valida As Boolean + Dim continuar As Boolean + + 'message(LAST.tag) + + continuar = True + 'Se verifica si el tablero acepta jugadas, es decir si el juego esta en curso + '............................................................................ + 'Verify if board is active for game + If habilitar = True Then + 'Antes que nada se comprueba que el boton pulsado no se haya jugado antes + 'utilizo el atributo btnInicial.picture porque resulta sencillo comparar de esta forma si el cuadrito ya no tiene + 'el dibujo del signo de interrogacion + '............................................................................ + 'Comprobe is current pushed button haven't been discovered. + If (Last.picture <> initialPicture) Then + 'Ojo, si el boton pulsado ya habia sido descubierto durante el primer movimiento de esta jugada + '............................................................................................... + 'Inhabilite the movement for a button when it have been discovered. + continuar = False + message(("Jugada Errada")) + End If + + If continuar Then + sonar(1) + + Last.picture = botones_foticos[funciones.devolver_indice_defotico(Last.tag)].picture + If funciones.num_movimiento_actual = 1 Then 'Se esta haciendo el segundo movimiento de una jugada + + 'Se debe comparar la imagen, para saber si hubo coincidencia + '............................................................ + 'Compare the imagen for stablish if movement is correct + jugada_valida = funciones.comparar_botones_jugados(anterior_boton_jugado, Last.tag) + + 'Se aumenta el contador de jugadas (cada jugada corresponde a 2 movimientos), se actualiza el label + '.................................................................................................. + 'Increase the play counter (each turne have two movements), label is refresh + funciones.jugadas = funciones.jugadas + 1 + lblJugadas.Caption = funciones.jugadas + + If jugada_valida Then + sonar(2) + 'depuracion + 'ME.botones[LAST.tag].enabled=FALSE + 'ME.botones[anterior_boton_jugado].enabled=FALSE + + funciones.aciertos = funciones.aciertos + 1 'El jugador tuvo un acierto mas (player have acerted) + 'se verifica si el numero de aciertos indica el fin del juego, en todo caso el numero de aciertos para + 'finalizar el juego debe ser igual a pow(tam_tablero,2)/2, por ejemplo si el jugador escogio el nivel + 'experto, el tamaño del tablero sera de 8*8, entonces los aciertos necesarios para finalizar el juego son: + '64/2=32 + '................................................................................................... + 'Verify numbers of acerts, for stablish the game end, for end the game acerts maybe be equal to + 'pow(tam_tablero,2)/2 + If funciones.aciertos = tam_tablero * tam_tablero / 2 Then + timer2.Enabled = False 'se termina el conteo de tiempo + message.Info(("Felicitaciones, has cumplido la mision")) + End If + Else + habilitar = False + boton_actual_paratimer = Last.tag + boton_ultimo_paratimer = anterior_boton_jugado + timer1.enabled = True + End If + End If + funciones.num_movimiento_actual = funciones.num_movimiento_actual + 1 'Se ha hecho un movimiento + 'message(ultimo_boton_jugado) + anterior_boton_jugado = Last.tag + If funciones.num_movimiento_actual = 2 Then + funciones.num_movimiento_actual = 0 'Si se hizo el segundo movimiento, se reinicia el turno + End If + End If + End If + +End + + +Public Sub mnuConfiguracionSalir_Click() + Me.Close +End + + +Public Sub mnuConfiguracionNuevoJuego_Click() + Dim tam_botones As Integer 'para seleccionar el tamaño adecuado del boton de acuerdo al nivel de juego escogido + 'Choose the board size + + If tam_tablero = 8 Then + tam_botones = 40 + Else + tam_botones = 45 + End If + funciones.distrib(tam_tablero, tam_botones) + +End + + +Public Sub grupobotones_KeyPress() + + If key.code = key.f4 Then + habilitar = True + timer2.Enabled = True + label5.caption = ("PULSE F2 PARA REPARTIR DE NUEVO") + End If + + + +End + +Public Sub Timer1_Timer() + + + timer1.Enabled = False + botones[boton_ultimo_paratimer].picture = initialPicture + botones[boton_actual_paratimer].picture = initialPicture + 'ME.Label5.Caption="anterior: " & anterior_boton_jugado & " ultimo: " & ultimo_boton_jugado + 'message("hola") + habilitar = True + +End + +'Habilita o deshabilita el menu sonido, esto permite posteriormente en el procedimiento sonar, escoger si se emiten o no +'los sonidos +'................................................................................................ +'Activate or desactivate the sound in menu Configuration. +Public Sub mnuConfiguracionSonido_Click() + mnuConfiguracionSonido.Checked = Not mnuConfiguracionSonido.Checked +End + +'Para emitir los sonidos, si el menu Sonido no esta checkeado, se omite el sonidito +'.................................................................................. +'Play sound only if Sound configuration menu is checked +Public Sub sonar(evento As Integer) + + If mnuConfiguracionSonido.Checked Then + If evento = 1 Then + sonido.Play() + End If + + If evento = 2 Then + aplauso.Play() + End If + End If + +End + + +Public Sub Timer2_Timer() + 'Este timer sirve para llevar la cuenta del tiempo de juego + '.......................................................... + 'This timer count the game time + funciones.tiempo = funciones.tiempo + 1 + lblTiempo.Caption = funciones.tiempo +End + +Public Sub nivel_Click() + + Last.checked = Not Last.checked + If Last.tag = "p" Then + 'message ("Has escogido el nivel Principiante") + mnuConfigNivelExp.Checked = False + mnuConfigNivelMedio.Checked = False + tam_tablero = 4 + funciones.distrib(tam_tablero, 45) + End If + + If Last.tag = "m" Then + mnuConfigNivelExp.Checked = False + mnuConfigNivelPrinci.Checked = False + 'message ("Has escogido el nivel Medio") + tam_tablero = 6 + funciones.distrib(tam_tablero, 45) + End If + + If Last.tag = "e" Then + mnuConfigNivelPrinci.Checked = False + mnuConfigNivelMedio.Checked = False + 'message ("Has escogido el nivel Experto, suerte jugadorazo!") + tam_tablero = 8 + funciones.distrib(tam_tablero, 40) + End If + + + +End + +Public Sub mnuAyudaAcerca_Click() + frmAcerca.ShowModal + +End + +Public Sub mnuAyudaInstr_Click() + + frmInstrucciones.ShowModal + +End + + + +' PUBLIC SUB mnuConfigIdiomaEspanol_Click() +' 'Cambiar el idioma +' '.................. +' 'Set the language +' language = "espanol" +' mnuConfigIdiomaEnglish.Checked = FALSE +' mnuConfigIdiomaEspanol.Checked = NOT mnuConfigIdiomaEspanol.Checked +' 'File.Save("language.txt","espanol") +' +' set_enviroment_language +' +' END +' +' PUBLIC SUB mnuConfigIdiomaEnglish_Click() +' 'Cambiar el idioma +' '.................. +' 'Set the language +' language = "english" +' mnuConfigIdiomaEspanol.Checked = FALSE +' mnuConfigIdiomaEnglish.Checked = NOT mnuConfigIdiomaEnglish.Checked +' 'File.Save("language.txt","espanol") +' set_enviroment_language +' +' END + +Public Sub Initialize() + + 'El timer del contador de segundos ha de estar en ceros + '...................................................... + 'Restore the time game timer + Timer2.Enabled = False + + 'Se inicializan los labels de conteo del juego + '............................................. + 'Initialize labels + lblJugadas.Caption = 0 + lblTiempo.Caption = 0 + 'Se actualiza el mensaje en la barra de estado + '.............................................. + 'Change message in status bar + Label5.caption = ("PULSE F4 PARA COMENZAR LA MISION") + + habilitar = False 'El jugador no puede descubrir las fichas hasta que pulse F4 + '......................................................... + 'Enable game only when player press F4 + +End diff --git a/app/examples/Games/Concent/.src/principal.form b/app/examples/Games/Concent/.src/principal.form new file mode 100644 index 00000000..5156d342 --- /dev/null +++ b/app/examples/Games/Concent/.src/principal.form @@ -0,0 +1,220 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(58.7143,5,49,70) + Mouse = Mouse.Pointing + Text = ("Enjoy Open Source!") + Picture = Picture["imagenes/tierra3.jpg"] + Resizable = False + { mnuConfiguracion Menu + Text = ("&Configuración") + { mnuConfiguracionNuevoJuego Menu + Text = ("&Nuevo Juego") + Shortcut = "F2" + } + { mnuConfiguracionMejorPuntaje Menu + Text = ("&Mejores Puntajes") + Enabled = False + Shortcut = "F3" + } + { mnuConfiguracionNivel Menu + Text = ("Nivel") + { mnuConfigNivelPrinci Menu nivel + Name = "mnuConfigNivelPrinci" + Text = ("&Principiante") + Checked = True + Tag = "p" + Shortcut = "Ctrl+Alt+P" + } + { mnuConfigNivelMedio Menu nivel + Name = "mnuConfigNivelMedio" + Text = ("M&edio") + Tag = "m" + Shortcut = "Ctrl+Alt+M" + } + { mnuConfigNivelExp Menu nivel + Name = "mnuConfigNivelExp" + Text = ("E&xperto") + Tag = "e" + Shortcut = "Ctrl+Alt+X" + } + } + { mnuConfiguracionSonido Menu + Text = ("&Sonido") + Checked = True + Shortcut = "Ctrl+Alt+R" + } + { mnuSep Menu + Tag = "-" + } + { mnuConfiguracionSalir Menu + Text = ("&Salir") + } + } + { mnuAyuda Menu + Text = ("&Ayuda") + { mnuAyudaAcerca Menu + Text = ("A&cerca de") + } + { mnuAyudaInstr Menu + Text = ("&Instrucciones") + } + } + { Menu1 Menu + } + { Button49 Button grupobotones + Name = "Button49" + MoveScaled(2,67.1429,6.1429,6.1429) + Visible = False + Tag = "48" + Picture = Picture["imagenes/inter.jpg"] + } + { Button50 Button grupobotones + Name = "Button50" + MoveScaled(9,67.1429,6.1429,6.1429) + Visible = False + Tag = "49" + Picture = Picture["imagenes/inter.jpg"] + } + { Button51 Button grupobotones + Name = "Button51" + MoveScaled(16,67.1429,6.1429,6.1429) + Visible = False + Tag = "50" + Picture = Picture["imagenes/inter.jpg"] + } + { Button52 Button grupobotones + Name = "Button52" + MoveScaled(22.7143,67.1429,6.1429,6.1429) + Visible = False + Tag = "51" + Picture = Picture["imagenes/inter.jpg"] + } + { Button53 Button grupobotones + Name = "Button53" + MoveScaled(29.7143,67.1429,6.1429,6.1429) + Visible = False + Tag = "52" + Picture = Picture["imagenes/inter.jpg"] + } + { Button54 Button grupobotones + Name = "Button54" + MoveScaled(36.1429,67.1429,6.1429,6.1429) + Visible = False + Tag = "53" + Picture = Picture["imagenes/inter.jpg"] + } + { Button55 Button grupobotones + Name = "Button55" + MoveScaled(2,74,6.1429,6.1429) + Visible = False + Tag = "54" + Picture = Picture["imagenes/inter.jpg"] + } + { Button56 Button grupobotones + Name = "Button56" + MoveScaled(9,74,6.1429,6.1429) + Visible = False + Tag = "55" + Picture = Picture["imagenes/inter.jpg"] + } + { Button57 Button grupobotones + Name = "Button57" + MoveScaled(16,74,6.1429,6.1429) + Visible = False + Tag = "56" + Picture = Picture["imagenes/inter.jpg"] + } + { Button58 Button grupobotones + Name = "Button58" + MoveScaled(22.7143,74,6.1429,6.1429) + Visible = False + Tag = "57" + Picture = Picture["imagenes/inter.jpg"] + } + { Button59 Button grupobotones + Name = "Button59" + MoveScaled(29.7143,74,6.1429,6.1429) + Visible = False + Tag = "58" + Picture = Picture["imagenes/inter.jpg"] + } + { Button60 Button grupobotones + Name = "Button60" + MoveScaled(36.1429,74,6.1429,6.1429) + Visible = False + Tag = "59" + Picture = Picture["imagenes/inter.jpg"] + } + { Button61 Button grupobotones + Name = "Button61" + MoveScaled(2,81,6.1429,6.1429) + Visible = False + Tag = "60" + Picture = Picture["imagenes/inter.jpg"] + } + { Button62 Button grupobotones + Name = "Button62" + MoveScaled(9,81,6.1429,6.1429) + Visible = False + Tag = "61" + Picture = Picture["imagenes/inter.jpg"] + } + { Button63 Button grupobotones + Name = "Button63" + MoveScaled(16,81,6.1429,6.1429) + Visible = False + Tag = "62" + Picture = Picture["imagenes/inter.jpg"] + } + { Button64 Button grupobotones + Name = "Button64" + MoveScaled(22.7143,81,6.1429,6.1429) + Visible = False + Tag = "63" + Picture = Picture["imagenes/inter.jpg"] + } + { Label1 Label + MoveScaled(1,1,12.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("Jugadas: ") + Alignment = Align.Left + } + { Label2 Label + MoveScaled(1,4.1429,12.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("Tiempo: ") + } + { Label5 Label + MoveScaled(0,61,49,5) + Font = Font["10"] + Background = &H000000& + Foreground = &HFFFFFF& + Padding = 8 + Text = ("PULSE F4 PARA COMENZAR LA MISION") + Alignment = Align.Left + } + { Timer1 #Timer + #MoveScaled(9,19) + Delay = 500 + } + { Timer2 #Timer + #MoveScaled(24,18) + } + { lblTiempo Label + MoveScaled(12.1429,4.1429,4.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("0") + Alignment = Align.Right + } + { lblJugadas Label + MoveScaled(12.1429,1,4.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("0") + Alignment = Align.Right + } +} diff --git a/app/examples/Games/Concent/Blockhit.wav b/app/examples/Games/Concent/Blockhit.wav new file mode 100644 index 0000000000000000000000000000000000000000..e4b4834a8ffae709a298b1c951a845f2e1a9bfca GIT binary patch literal 1148 zcmW-hNsDDQ9K~OQ3l}c*8`Q#`(5O3?qFqoT3U;IG(xS2Lx*03Oox)A-kQ*}I_lmOn z+nO>V$$xS9<#00mc5{9G@X6`)$M?U#dfPl)eSSKfUex}@SEv8Si_@p4x3_;iOHQBF z>F)O7_IcG9J$+1v@#+X{_!y$S=drH zLrv&r6XCua&|;1`20=+!xl(DAKtQwxi+f`X>}_x{N!a>9N$FQY5d=Cbyr{)Mtq&a) z3|z!93Ge;=y$)mx_v3;r>M`j{vZ#>OySG|{Cy|yI=B_BWxq0>X4U}=FiCrXh zS@0tJloDQhw`f|HWRGKr1ID-vlyc7lqapKjmijP^isQ(B-Pb@DJEJ~AuVRD%LXSP| zgr!Hp!309*lA{bFCPo4QR>LVo42Ks;yGp&8|zupeT8CX#?%y-A@a-)s<~ z#3oF0SG^`)T0$ASoJA=a?9UKA)j%g)e zNL8+L#tBs!i-Pg9H;2rm1Ty8yN7SnH%tqdc#zEltglt9-w>j0Y5T 0.0-3 +- Correccion de bugs, se añade temporizador + +* Wed Jun 29 2005 Jairo Alonso Badillo Bedoya 0.0-3 +-Versión Inicial + diff --git a/app/examples/Games/Concent/Missed.wav b/app/examples/Games/Concent/Missed.wav new file mode 100644 index 0000000000000000000000000000000000000000..9235541ecdd572be9434e15367a828e66f7f3c7c GIT binary patch literal 15612 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 b|MKg?&cFQKUq=7QZ~lUjKiT*H_XqzMIE7Em literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/Newlevel.wav b/app/examples/Games/Concent/Newlevel.wav new file mode 100644 index 0000000000000000000000000000000000000000..7db791b441d037648f96cdb810c4a480846f0b78 GIT binary patch literal 19842 zcmeHud2kz7nxD6_sY-3_R(3a=%$~D5hdtwKblW;8*_1^c6a^ii1RW6BJep(^Km%wT z4WMx}fX2}P8X%e=cz_giKoTqqvMI@s1s$*i9h6MlqHXzJ&y07yvolk(nbangq$-uk z?}75}CfTa|nN(_8cB7AXeD6Dc-|?Dzwr$?*+40z8y4HPd119@l{O)6qJ@y;;_j^x2 z*8KlB9{X314fOQce-qEQpN^R#lm27>;N3dR8fwRFP#E z7HwFZ@LWJl)AW5=H-iRD83XD!iF|=M!GnAfDs`auWVnR_6c(&PoaVAd2JSxwE zn7qVN6vHw&aUfJsR9TP;<)TbhM(Zh1K;t#dN`+Eh#y|r4$Q*?kNFUOXQ;G#yl=9_r zUTn@n+LK=3hmuz%LClwn67y&*yka?Ckc*XaUXf+=Aj`lS^%N`0SW3NC;si262=U9uV$egZS zOM^!&MT@0sMHPrp@Qwwk;GL?93?r9{f=0*736%eRLsl5qvhj=(t}b#DORvpsktJCg&y!k9<*ME@eAcjxqvPhscrR= z)$MGd@x@;)R3v?COPdZNp){>+S}tFmo3B^$0;AWowX9y9Cke zty)&dkWh7j)wN+goRWt`AU96R7oisryN)I^stTG=Sah3~k&ClXaxj6&AA{*>s5@wa z;;^DxAR@&CP~k<15DhC8R1mLiZ`VFz8KjQ+D5*04#i#W=-MZwd7F~1cU^`iuEJ-35 z0b|3M`BJ&0@(@@{o0cNV51WG zTdSy?wxvZwbDW@58V^5RC^Jv}>py&I$rDSc;@raA+(KP$`@=tLY3hz513Cm)A(R>x zlvi;`98tw4GzgW4^LbjM)oCFrmXl}aW(BR5W@(Dg7gYjN96?TkyD&?vQIsHAV!gad zz=(h(v6^lrS;OTCE~et*cuhZK>3lugYkp)9JZFt*THO9SuopDFG%uR}pnhWm&v|&7%)g znJrWS0Hh_a))wk24GMI6KnY)LG>WW7LpO;87^<9J&jIxz^d^u~tXwLq9M;VOX3>hM zyg=*pFgAke;0LYM38gAomQD}u(emv4e1+Gv65A`(>qWpQoCi7+j{_TFz!!7`xBvwh zxwG* zqh$b%bq>7MD^L>Q>kHCIFXM?sT9XTzTu8W33HT`J#kOg5Y_VFeV7BHQWHL$xOBu|? z;Z2#~B_33%M}0YT;ejTkB~z%@i<-p-ggP?atRBDwJ1pTjA(qTVrVv3XU#ix~lvqub zYSR#U^LUNN2t9cB^$!G9f#N}O5*$?lW49CG z9_JZ6R^{enfh+R7%Bq5(vU$W*k4^wbF3-sl zmuC^V=2=xFr)DET#y<%T4F5^whF6;ZR8Hl|zvdlzPMVOWg;E+T;2(AH;u=HjqPOu&ai5?J` zBLIey4Y!c-cKM!jBF zSFdPkZEbDC0Y#`c=H}<-8!$ECIr;*p;F?H0CN?Jp*dekY4y-u$@L?XPj{$LzTtKKa z<{wr8vLJ{MM|?i%Epm$(ClG`K#sMvB@R}rh0JJfTGTWG&Yn0ks+ch|}l!ExM(IC{4 z(Ghh}@b>cy3k`UaM+fkb6KCfhmJnwX|Br(~tO1(lMdBS;cv_mmx~mKce+e5f4B^E5 z0yr_p5hy2(NU!F?;E+HK+KA=)Tw@Nbp%4(YE2O6O`TdmdV^+L;v z<~=dZCWm2wgb|P`Sw4;?bdymFVCHNI ztrGqb{e{zP5V=WYp@hZczOK^{dPrX!=t6$f+Wf-9hYK9Zj`Yn}h$x6QHY3o={6kpB0UmOaWh5_a|1RsmCgpxuBK@?W9u&^M3{dx)^LJb{Al8Mp4B6*f4 zZ5V|#nkNnq(NMWYh9T_M!54r>N|QsxH_pw;5W8MaJO#uFp=pWNXf$Tws1})6Z@}+0 z1zT(6b#Q?glg7g56o{9i^HmI(M@#{Oq2)@wK#aPn1r>dhMg$u3^L2`ZLb3=2YFZML zuR#s9tr(3hL2PweO9YAtrcy?vL;wV`Z>ohLs`ZB@A`XsN2rdX3AkdlT2@8>aHH{4H zD>fFuFrW`2JJ=e8!WQcYL`Y-+7;h|;iT%OQ9>u2wP6(PHf+gTU#3RCVXGvZP$rS4% z=E8+omWX~5Wh4~wB$<(c6%Do>>?J5s2d94>>ijy?`E{uCe=*d7Lx(N@<4Gu}3W|gT zzR7`K1VBh-n?Q-c2gjnb0rUbs@c^M$^S~8ek}U91=VrKoF;KKbV6@m1g#{9qu&gvY zS7RyS=m~-p8wx2flHf!{6{0yL>NFS$sUoQ0Nl2B}pA-NzB6L&yhg6Ax!!2c1DWeHHi{Sqc{KuEESN&;plNi!oAo0Ac`=jRdR zH|Zwa2MYk0O~w*n<>e|N88sX9LsVCI6xtRiHzN;+w2S#vtN382%6K;L!x3JYPUOiUlI-fG9<{D7MM! znv7%>b&U!fIZ18##q1OrFbNH63~sSp0d@0#_^I*DyZ`N{nIh9|B%MVvNwRaSANoep zSg}IVU`#-AZZybi+L4&f>f5zarOJSUa!qI^u?i|)#VTS3r~&DSM(BYAA33g6Y38KJ zCP_}Zn5rVFC6CEym=u|@tkH0#S-Ks0WitsaAxSOOSd!x6UaFM!c)xg$tQPFmYxpv< zT%n9%rAk4EOmZ<()nam4i%eA_Cy}J90#3+H1>~lBa>1ALNM%t~;mZYb!D%_c~5=9pkJ#tQ+ zj1;zz*K=i2CzV+$U!<6lte26emRP1J=v7rO7Lmj9R9@CgNNf=q6-2$F>V+c16!BQ0 z6lALcQ{dPF3<{wVEvl4^$5Qh#&lWiHxWI^c8exyhp#H2fiomOkio6adlCVMvaguOi z7ZX zlA`_NshRIB-Zzss@&>KQ|BL75)z@8`FNqcZPZz{&h@BzEe)wq_=*A+he)zD#AU7o8 z;=_jmV1VQm3lC9tAgLqbX2=$y{VY;F)ZeJZnj1j^)OCLy^!>WN_3Qdp@&BFr7O8?Q zBE{G6(8LIW4b7d1e>^?9|2oDaSn+>2L_h~$ntS-Eg7`|Boqsro!UzlJ{SZ*X;LXBA z2}prA3w4xtnh}`<6hS~ZPcVcpH&6w~15{-YUg=TwLHQkl4pQO<{28+DI?C}{k}cP0 z{32o?1!DNkI;j_vSOPU__?BvoUMwz?k~J!3By1#a;i!q@Eg{2gy6?q0wu;>rWa7=A z*{^@#Y4s=H?TsF$j@VF(EnpNV<$;{{pa?k}% zSJI%QTqTY2q>c>#h^FX(xj-D+h?Oc$JU1%WcntFJSkREi4Dr_}RZA6g#Bv|`Z`{LQ zv!xnGYST^sU7_Gj;fish;eBz-mr-~YNa0zmphPXr!lyT@&&sSyd^(S|B|RPqTBZaC zEzgRmJy$3=eo0FgE3{UemA@`RH~qDaD}U{;i^Qp;>Re$k5{oW8s?H*+&N3Q_h;vxI z3KtFM-7G1WXu5#9a~XB#GDGDT>&}P-C{?AzB6do6x*$_>fo^(pky1(3x%n7wTP{Ll z7K_m`ofnycz!s5Du#28rM%fvUQFg{-l$}|=*sMn985KLakNh=@-MfF{uUQpN9jAY& zLZiMx%;leT*o*b%M?M`^f{z9s`Dj$&zV_e$Y>oNXHu!5B{Qt`a%@!+bWUV%*!{Kl` z?B-!+XlRIaxc#w_$rDG9Oi$q`OiqlACPE(0Vm1!bJ>6aVG&^?e>g?>?y>r*@^-uo0 z-~8P_dt&MHFKph?sqbZM{`8U4v$b3I-g)r;!;e1t=!1o~@4x%-qfZ|`xOekX<=9xv z!`t0~cs8TNrARcTj--`TDiy^1LNFqaj2DWNImvFM4OWLo@C8Ca(PKCEAKb0ocZllk z@2C6w28~vy6SUeLZeLhVq>{;$q9}=&6b$&i9;b~R?$ht@+`F%H&o0egeZSG-uyHKb zHPFAmV{_~3rH}u~H~!#HzVqbL=UQ4fZ{NMYd%)xrv&YYzzgWF|?K<-ITleP)?H_;q z$wwc)ed|X3>gCI|>V-=A*i;U*%F&n{i^?g<=LQL!g*6T{%rH%Lcl8eR_YJXT%YeRf z$1d&u!&D!2Slhm1x2~(3GP+{>IC@CzF2YpT*(w`ut*8ibSHZ zxSU8T=@E4-KT#;2t-Vn_JrQ>g?rncz!}`rT_w3t$@Gv!GwOAaGw4uMJYs=DgoA=UI zUo1OazV`l)fBEyDeDUEscW+#&o zx9{GpUp+gM0+q3mtU8(-gB-{6W4TdvB%`Dfax4-K`91z{z{~bPOZ$5%dU$AXaA?>_ zckNjJ;->c9TVGiE?Wb03*tYv%pUL4%PQCoboew_!(GS1)?2`{aSa^5-{@vR*u3x@b zIb9r&;me+b`}I9Nz1@co?Ax=mV_VzC*4A~ami^$Fr=R@(6W{&LcfRx8$DjEAQ$JX- zdi|!3&cg#1A$siU&3pIXTKMRb&;Rm^&p-Y6!-pTdxA5TIdyPx4T)0|$qcoCEWkyGH zqoZRJlQ3vP`qz}FGVvhsl);ebXJFGk=8mTx|JIX)E34OSXl-lXrRy3%xn}C(<@)7| zXG_JYaf~yX%Z;g-sLyHfr;f~?zi{Q&wM!>+i6E=*Xj#4L`8BQEwzao!$I;Qzu|q?w z;n2bTSWDZw)-8KLu{%66bL`}spa0@lzxvhR|Kzg=_ZrvUc=h!0nHblzdp|X7=RJZ~ z5Ii2Yi|3oRZg=9!GHim}{oz>)rwQguxyJq9|JqM`) zlfxfRWshHY|L=eCllQLAo-AbJq7(YQ;`hI^YE$RoLDuP$6!2j7?XUj(mv>*AO8V?Z z-AhmZn?L%CC&AMfT3>40ymkA|9UZ1LyL0ExLxTp`NF*C}hSDdm)*Cl(zFB|wM}Pav zzkKV~^)vbLiP4O+Yum~fp;HuWtEY#ex)%?spD~z>#=%}oK$*@d5q~5#QK~IGcu*^) z?EBYm*}ZM!3+s0rW)mM(GlHaZQ?$Va+hx&|G-Wvo^PD~bR zpZxe|caCR#y5&!P>zU`e*3q-xpaMdXZH}wXCu?~FTecq zLH+etP6AQW@qp9LZU64l^_>PjJpKCBTW_2_Iwt$6mzIC)*)?1Cb>X8|Atb|wP9A^j zKmONW{ruzG7mlU{rdO}s-uA+#ZM$^Tu*qt7vHkmZ?C)pYQbrwn<>tqK_wRrC@r`rS zNx@;+@H>C{^oBiM2D>jhlAAnr;o*Pz#g})_PDBFk9p8F-RXb(&hhq6tb3a?SJS|#! zwl4j{$A0S%zP)tK#%+(}oqP4xr*qJupxJD31(W&W%db{XjwOS|>o;z_Hk0(&jBVfi z_7f{!Y~R~8WH1j8=(PuXj3@`@iYLl%|NVddZ-29JWA@Z^Hs-Y++_d65Pc3WNxV)=1vp*H6TkP(r&p|dVbkVq+u;%P1ASEAfI}KPef-FY z@|Ab*-MVmMJS95J!|hLf_nXVw_70d`W9MJ3oH`m~l}fC~H+kcyU;X&nq?(lD`@g?x zqt57wk4zjb-}~9me*X5WQxTV;?av>7diCZ#-6n4!JP6M)U=HO^yjHvY>SWkNX`lWV zzxDgy!x|Y_(6RZ2k8Zp&3Ut!z%<-8E_kQ@^oyLvRiv5uM#_JdEel~yo^oZm-yzx8# z@{fM7W>fpFUAqtV+a;53%kx_g(404x9J%(hpZxXw>nBwI@UX!ZJ$dWmNmU%&x%Bs+ z-n8Fj^W@H5f9LkqVo3jdOY80)d;HX`x8AE)6n58&)f+V3j^xQV@62C48|&Nr)bnlo z27S58-Ft7namu&%#i!S{A2dXdRp;+EDihZB^~+j!4|tIkzFnUkb!eBZTHDbrOq_!k zJ|5}Wux8bk1C}@n{r75TgzWjTsyzcWF^X6$q|IG6nI|s;G-aMZ%bv(U(7cI=Z`6ei$maW@- z$dWyO<<_~$;dLz=cJ=wjXWyz{PzF1ey|mX5I&u5PwSs%^@{M~8k(0OUwMlp9lJy;w zU7fvh`}|Sknl)>8P;T`#NGG`Yh1GjZne$g}zgD1^u6b#{NjZ1r-W#XQ!1*x!a|%?wOX3p~#tc@7BkR z9nZe7+YmkT?%a(j`_8A=?;HYG=WZ2+&S%%}Fr{C8aOaxRwPo2xtv!EX;dV{jvSC$w zceHZ-!G(MeMzQ272m>OT7ng7AiJiMPf9d$plIPYR@*HX0x^q0B{r>8$z24%@+xK2k z4m|$cmVxLQNILk!+EoX9M{nJ}d)&7hPrDq`x8~+&*p1)VCRm5^@S?WwZ=%xIE}lI% zYyQEqm4`yp)hjoSh4y@V&2CGkaQgCmdUwnB4(G=5r!OB*>7Uu8Pu4D;d*x+z<;s-@ z0{L?nYe%EI{&?Nt*!dCj#LdvgtvjFXnX&#hRx$6o&Y-X&Sz`oy|jnRn)H&bW6zzWhb;&0BNl#`NEPatT|h zFVso{Pd@!@f8o*xb61_qo_+FA`us<4z3E@S?1?>r%IEV}0~=R9vBQ4yqx;u|H7l3y zG#~q5{&u{h<=M?te*Ueua))1fvaNS~{@$IDLz{lkrXRU|_uj-%`!j7_qxe2$(y;yM zw(i`$d-wAFTc6oBFn;guoveQI()Ruw+U5JVEomRf-Mn*ujM}2x2R zyLm0NW&O%N^|g1;kI}1IUb4)*{?3KTffX&Aou@9(pAH>d{^CyW%eDD(D);n>Ub_4C~F zRcnSOsvp#j4!}YjFW>m&6~Fe$^?PDh=RY6Uwl3M)J9_(_k29OsF4=86a{ujx2-bg4 zdimX3HTw@%wb;(Q`Qe$szGpY;64&p4n8!>ScJlVzytIDxitbe9ooi>=Wot0%_4muZ zeM{P)3AZ1lJ6?p&oV<7SHEvbQCi}@N?;SDhT(P}BQ@e3bZhv9bK3MGK*SM7}n=I4S z2SA{PlaGmQ5Xcqx#0B zOWY5%Zp+a0gBgQl7)&;DKF3A*Im#-EzWSv9Hq={@TE-p9KTIpSmTl6h_5mP%!= zd!Fm$f+GoO{ETU-`CPO+Q4MT6FdRSzmV4E_ggqx6jLeEVd+f2PIG;UhUg5Z;^hIBG zZ|lO7tUPhr2z{0IC9aDbDSsd+IC2*~FNSVOZIRo-4aS0ucq~5U)T-}A*LtqFS357} z-M*lGi0&3oBG##){5gc6?NL3;A-_$J$N^ZL|hM|y+Dm}R}W z!g1N+5C%IZKF{s&l#R0J^#^XNLnhcH3}r;$+-t_Zkl7-y1vNJ!*K)eqn@lg-tqBc;=1lu+=@# zsXiDxV%@4WVGM=dWJCo@seBG&zP0)nXXzoB3GnaFvX!GC$3(bzL%l&(TnOguHQMe{AARe88PzcTM~#wVJPT zOQK(lZgrnAJj=eCwfkjGZxE(x$-{isv~Bz=WtIDyb7|-Ub(e6;y55LLPDK9C4UOMP z?y^sqH|M_0z9?L>FB9L5Z*;!m+{m9Dv3n!Tj^WtUxdfsrcK`TC>CNtnbye)s?4WcJGA4UrPf-Xl2n}(FhlhHxWZ#Xdh z!;y_V#z_5D_67SH;}0BHRmPLD>@`J;ZzQ`INAJ$5uSQ$Fmz=A@@1?eIGt4T}smYY& zb=eI-*krrwl(i-Gd2+1-=po!3>2i-5H_@?4XuB}ZZdQMoSmmj^mV57H4)Az0m?%`@ z{Z84mC;wRnmSKG^{#m-!amKn@sE?baN$a+uxH=si1GYhNGLHPSZ?hqrip^Yz=(u#i>QAw| zhXU%UNyXXSO+}GOO7;UA22n9QRFt z(^8MwpE)))8h|&m=GDwd*1W^4P98gY)V6uQ!3$fA8|iM(SPoh1@D6)nJeNr_pfELa z^n`f07Zi=D?w&(?ZEB%({Fn!SToleEBJ6%@$Uio7D&sbG+Y=e&w!`}zsMCyQ%sXkX zQXJ2?`=~D8%<=L>+#4Hl;~nohV-s0mKwvJnrnpG9pg6>$zZLYbKni9OIw z3!p8ZV=y@u_Zs!qXzs+3$pAiyvc}|OkT>>tM#q7EHfqQc z0wr#f$s|pWNN(7iBRyFf4O^)Jqck&?4mvDG5iK0ZD~0hBujYNUri)FMPoGNJy0&$r zk|jmGws3CdbUtF*cc{lZQ8<#7O#6mBVhr_5Zz!G_#~)A~ppcJE%&2i5SUr-H{pOxQ zlOI(fFYofi^Eox*G9uFs!@k2PBl^OE-Qje5lT%~apmnHw&>R|5N92%~4=J&b;INtm zIhmF`HWSCkCo>U`nL#EgXL9g4(P+?XM#Yi$k3tG=)M?G3csc>3kD{a|8v73RFw)WK zi8Q$ENMv$Klbqi0(c^v(gL_Xro;S=U-0a-sg?yk|Fcq!(Ci#=Hl@Yzp)QKGCLASWwYNLJu+QT z6Wl?@CCaHx$iWU7ElzJ3_EIcQ$f9eY^AJXLqW+PenK_aXEvWl)s97e!8(&b0i*BzN zLM_lfNcTIU6R(_@mYoAS8s07{*=e&UF;$pW0+!xk&Kpf;11#s{-4Qj3x@W*`N2K9$ z_+!zCpZ7Nlp#nS9%Y`JA;=?8uRlX7YnV>*-AtOzqgcpI&5ppA`Sjf;h;EGR7Oh)a7 zJ~JdUnsW7H-Ms_0pn}#id`d9j;>@V-Cvq{5!|D!YrYFa}y}jU33bh`e%Zi9C%sXtJP$WKe zHf6H8P2jia4@IR=EG2^`Cv+=}NK%wNFaf_i5Rs$+zD>iokr58HWY5Uh)F?dKfKAGe zPo^bXuh|zwY-Jhnq*c_1eS?hAtH@EoVloKHw3<-@oQPe7AWwI84+kfw3i+%sU=M}D zKHiFbnCSGlnn?);&f~>iMlc=@L{c8cjL0}XnUzD}x&ye8z#akHZ-W7+(>{~M=8a?! zvGW5113Vx<8L;#WnIqFP#VqI<78r;yD{HY;`*;=ppj%sE|xo41+$sDDk4 z1yI*wg{T^bR6SmKT#=lpUV2fn%t_WRs?Q!tWb#K+sHAxv9wA;CIm10TX330fAZrGp5XX2Lq`}f5n>5xeUM>6i^XPi?AqbpH=ij!{k&{%$|@@mu3zcd zBPS-2`LXaaan$uJ*op{FPtT02LjMr-I1&r_?ZX1}QdR<1&K5|8++v&`bcCbWMvEo9 ztUnz_l;uaR>6JWI31A(Aw{n61Hbh%83epbR4u2T(hGfAMo=gRTlHcV4PuxRhr$ghT_7Vg%GVdhJoaGk`S;$oV2znUA%5+-?Ee0uuHH+=%lt zYQ*hwnR-2H61%2ej)%X%Kv|!$+mCY8R^M0e=*lYUf=*5efUcl9~XAJ&+L(JdR@> zC{{x@e$h@wK&+66x(qzDM}j}Ja4y*X$b@8d!K#DFC~(3B9Z8KvoHi&$0Q+yi%Wyax zSCX=8(47>)ArCe$Vq}FxmweGo98rbO=u1buUIME_0`w#^9kbbNF25f%_!!paM%_O> zB49H$7+28Of~s#Mnw<_08;l-&_dYW2Ic)HVu#kAz2K18^6;{CZTm62&3~B&Q{$M0E z>L0dZ?=t|*3)*?X>rad)5j7jkd|>2wF6PI!I*(`}fIVCM5l$uwf6E1e;bbmkF`He$ z&qPMFy1>~ec6`hO&R8OyM)YK5oXw2Lhv?58jVDI(At0*{e3pV-zXetYocCKnO(>ck z51WmQ4SV4u<6*Kt20oAo#10>(q{G;=u}2UbjfjweZzLsW^8NuM)*r@BHcxYqMkX&K z({=(w64>)$5HDm9ule9bJeXU_x_~HtQ65c2or8AdGWg?4CyVjJ(#TBQW;NS^9?EC{ zdr`b6g8fm#6E}Q`9JFDz0r>hj_Lm*7#kd**<|B-Q7IHx2ketq?Wp}f^l!mgx3d69T z5Hy=ZbfLgfZNR-)W<(8`%!ni+@Oggl!sCr1>h$29h~2Ru@Rtt-(K8C%_rr$=;ZbZt zFqVLngParliGEP-fZqy3YAI+W4<8|shy{9!|1{TE6iDU$OxCqsf39Q9ovLh;AyaH|DUj(t9D-eQYywDgg zur-+r7aU$y4l*ay*%k z15PWnB96S+Yvn+Pj7VNU9ve!ds-)6@A_|hce1}_8YOjeP- z7K_z~%AN$1wmO2CONP(4VqI~3e&E6%hJ-L~RN^7iFfx9`_JdY*uW-ttZ#V%-R-6AA%*LlOvhVMC#~g!sb)@C!n> z!oV!RJnSeC4ge0x3M24kJmRqcaFVz3{#Z7Rr$noK$p45wx)F)Wk)Y3oFL?aGvtZB% zES2EzgH7%B$71j+UTA9olE7-QzZk?fH{qZQxmY9}$CnnI!GVY{2D;pM8EMk)fbWQ4 zjR9nVZumF}xtP$2IPL!Z6y%U*aX33IuDY4LOf zv4j(L8i5V_eFC_KdPBgE_{!@GHP<0{d_b$H==ZuIqmUd4K{)WZA>_^I0a+m{f@FL? zIH8Ex{KmqE@xa`G2U&&>o+E^>SiFca$%jJ%x^4Bf0|j9|U~?JSSgw`SA^u7csU7bPh=Y&>sM$ z0nipAp5IXGz{p3R!A7o7E@gd)6)+-RV?90yPsH0Gu1H^8V)X(m&1%wqT|YV-XPr0%7D8 n9^QkyAk>^>WrWQlo{_5{I(Q)?pBIEeG07-i=mEy|dLR2ARhK+e literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/Paddle.wav b/app/examples/Games/Concent/Paddle.wav new file mode 100644 index 0000000000000000000000000000000000000000..38ade2780d4d1626c6a8ac84f92ff1066752393e GIT binary patch literal 1754 zcmeH{OK;;;6vsUh8nJ|Lz@8;5SRk=r1Y%}`Ak(3h+GNtCNz=ry_}ab?-|K5Tb{yN8 zH1(tpI&?ZP0#Oz)AhAXQ)-2fb8CbAk!Lc(uBqa7AY}xnkbC0j=bN;{k_M10u{BT`S zZok%ey>no`@SLJ3*Wi5qMWw#3DbFgM=3Vm{h+jZnyJgNGO3pcDJirXo zU`hq{JsA*Y+k8I65zk}B5E@(d5acif!y}h;BXl&fM-Rx@eUZ{0v{;oh zs`d5_k(Fg3t%H4yCW~beI_l6M@%*p|v9208%;s50Ox?u6G@Ay*GAtsKG?v5~BjQiu zNDyS%#E&MSppNZO5k!HYuH#Z611T8Bgb67nX9N`Yr4M~0)Z@MnicwGk6laWq5(0V) zs*;?q6?C|2cvZyTRW0k-I{Ft`C;fl>Z&sl42H3>+J<3ENJ+Ks5fq}z>=Ya=(>bleu z;5wH;9*5K@4t;n8I|dQF;Mxcw+aY2y%kwOqCX)dC=%TSXwjk~}7&{0#goo34d3Juf zOgu}|Z5~F|+OTMKz{1oWYpQM-x~8g{K6Y4`A3yr+tFNDYSfEzp&K^5FKg)6Vy^W?8 zEKkeC?loHnG+Ui40uy$OHKA1P^>uG{bd(8eIM8rZEbkz9>@Z8i43}9_fb7<19;rYZ{1!=`8WFWg^%V(?k*kw8}V{NLp=wP!#x` zn$|D^ZfjbDu;v#3!7Z+7h3b-uTi@rb=2if#9<8~pX#JDhT2nh7p^LJTQ9I(Wb|lL#D;*L>9F&zTDJxl4 zl!9SHWW^GRtSE}D37 zPH=ViwQKE7Z?A{D_5HJ(=##LIzg-^fR65c1Nt@RP7Z;x!Tkh;~(b}#aF3$(`@@{xO zYq70vwCvXnYq*?x<#Ky|+OKJ%e>(9@V{Z}#qQnnRAlUpkS_Fc!?tJVSv~e^KMA7U_ zAy8|BSs+xsH4Z(4RC?2(MoP_5*fR3l2NMX?re9UFsiZ8+$z(E>N~P23jSYmd*<20- z`9h&URG?U-l$J`HfTn4>t{aA70%hO>Pywn{xByJ$|7IQb|Le)W?f(nt;{C^-{Y5+= z6UMayzKnBjIgVu#qM1ZdsO|d>r-V>YP57-5YuZuh2PTL{PH%B$B1BLym$G9fG{u5iudjL0$~SWz%R3bDgQ&*spO LGiK72{!gdhd3 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/Wallhit.wav b/app/examples/Games/Concent/Wallhit.wav new file mode 100644 index 0000000000000000000000000000000000000000..858bbc32ed3fac0689ee3194ba35e0ff83bd2234 GIT binary patch literal 768 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an1.gif b/app/examples/Games/Concent/imagenes/an1.gif new file mode 100644 index 0000000000000000000000000000000000000000..0fca6237a252997af37a9d221464b304afed168a GIT binary patch literal 1601 zcmd7P=~EL27zXeSSs=j$vSBkQEHor*6bzU^RKTbqEcfDIMJ*T=BwTIOfYD;hv>_}J zNSuj?q7n#ERH^~NiFi>A7#O6eQPFzD84t8*vBsm0wG@l}EBgEJ%roz&cjie-NsNxk z4F|%3K?0aYqXHoG9|RGBAc8>1XDEbD2SR`#grPVDV$%l{L#8_dicp9^AQ1-PD1`C( zDhTBBF%ciZ`A8;)5`2Wq8EM-f~sl8MBbV!lEomW%mvu}qGORAMnfWLhyyfvZRY zSI7t>L6plfNkyhgrK(qutqNJaN?xzhlO$;@Z#C-6TlIP?skhXVV=Bvt-e|E{tX6A} zwROT`9kW{OcKfK^GSOq5w2n@5I-QfFW78(boD&lhpZ>=}rdaW%`MIimIs4mURS`S0 zD4(5_lAZzZKTq>{B*1e@O{W3?Rl8FQ)#`K%FE2V>zo))7lmOh}Mt67WL8Afms6J9n zQupc_8ewfydwa8~%a6msc;_#0JlghJI$GdB3}c<@>|%}h4O}>n`H=MuOe^d+ct6mu zv*{8%@?_-P1c_f&=WG9OYQ&tbuM*Zn#D;&pZzG2|~&fS$s)kGlk zOt!dNL;GRf{Mmr=;c+uOCzEvDq4cHu18(HLZ?+Xzc(tWNXYDzR2s$7=JO1$TP#sqo z>>Ml682Yy#-W6&D!hcorYQGBHs$?h9c;naa2S<_#)p2Hj9@K6)(ek#0u;%Z_b6tzFpAU#iPP4S=>u?tFtHrL~%AX0sa zk=&UD!V(B3&5EX3E5U7EN_s@ltU9A9Mx~`f+;MFBhpX zrq{-yQv%U1l{4m!Ip#Iqe*Y2ZJhs^tOxdSi;{?p!Hbpx=Cg}I>)ug`cxZM#5gd8l2 zAC4Wk;@3x=ZO@oz{+OtBfKQ+t;m;jYRq%arXyj2;o4`0cBEq>u z_2v%Ry}KP%OBeUkV)WUY;8pNpb=ocUnSDD;spp_kO5Cmb7M z7$w_h_$yo#xZv$~VM0Ze0qCLaS>WyNXIQsz7%x}${bq_<9)7ZTixOC`DZ4Bzqk^_W zw7i1hGII^fVRM_IaA>X-?@^;6M-w7)c}>v_X(5hXkk__8NDAlTc{b*qhj*MqIah6BmQ}piaOqa0ZKq} zFOg0uJE?q>hr;99$mVoKQemz0Z(fOos+nK$<;uJEh-Z5*|FubjV7j5$n+KlncVw|$ zrZDRWy<01-6zwo(OYUTvIu1+Qn!Bh&c_+RXA B*j)es literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an10.gif b/app/examples/Games/Concent/imagenes/an10.gif new file mode 100644 index 0000000000000000000000000000000000000000..e541a123423dd4dbe95ecef4b01f4991f1730fb1 GIT binary patch literal 2209 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 mzp0#)CB0GiNOO#ZpGIi%9fDRqH(1YJ7 zYj3U*(i$)-MYLEi5^HMNN@uE8v+f3>LyHdDY;`TVh@2ffWu2a}+4V5CxW8hr^E|I} zp6B`TeI9E?neioOHpm7yt3U!F3=s5x;J5)na2&;-qej&U;iyJ!K@p7_MKv0=T7xG> z95E!08V#=3*b;ZWUZXc8q)te0Fz79b))X3SiFIKDgQd_=V=>rlHQQ{Knr$@=4NXlg z+ge&$n%WXHcsz;nwE3RX=WFZk?)LTd4R(9_5)QNl0)f84(BR-;C=?2Z0^x9YEF1_& zpA(A?j*X2?k9EglvDsK?HWr?ooSdGXo}UiS&d$!yPR`BE&CgB$=l`4Zg~jIAwmVz4 zHxT=GwloqojoS%p#X39CKab{lNIjnyW7&^{Gk5;^G3GAW04~D@yeR6_hbItLUA) zOnH)!vJ_KSDlBX~-`Iiait@FYnFNM_TUB|M(yFTbi`oLUT1#3Px;)-F>J?>r^|xNF z_s|NXtuf9*7Lh8a92kcJ9U{D9&%fhtGt9$vZ_l}&rc=i=WJ;5Y>~EJywHpd;3ONnV zifn}kTmz51DMX3()GJel6+v?hbSh( z7fg~D?~v8vFSCzDbXie zignGuFMQT1smY85M+B{QK*%*2Zy{K?T z&_3K*-BiWJH;Mq>+Ni)5Y=pZ}JLaGuaBsg(@l-W54Hv5)GJG}5jw}Av63B`oV?&J! zfzBR*`cJuFgpc@bMj31el6)OG0`kaOhCj9MM2Ihk9b8#4gi!$VY$}oF3;%j)6Uw

lR-^*L9GJzu3aV?4*s z6g$ONOGrwv^$nLomh3yo!J68U%`!n~=-y+wFMe5+E_DopUh4h{fP-i5zvKq2U9F^z z<=`?uYeKd2CXa|`+-ni#F#5J&Mk4|0_kR6e{;s^>eprmD%)^R>gU9c|O+54;X7Q2F z3f%Ht$oKwaVXQ`~~S0R{xWFgv~NBxv{KrUPBI)ctyL{|7t&(uk`eEjF+{ZmL# z2;9s1I6lnAkcIu84mE)sSq<@(04(?2s$O#SjQH=D`=16JIq+x%F>w$VAfQ#{B~rxC zL%L}S1M8}B!lUHA+t>2=E?UPi=XdS`E3pXhyBHDX0LRM9lzWU9UuR8QUp+)CO6)vz zz7%?61qa1~z`G8N7Dv7WUQ%eR{&idq&V_r+IbnrNV==>wxgSILYT#Gym!vqahp z!Og3?u$;^}&`GYQ?7v!=;$7277GbbSF!d1bB%HRQ6b@^}%^9wE=SzT-w6Q8F7euh{ zf$X@wD_IY1Ngm}^yiCxmKH!9|Bfop(ag>dVoc#4EC$IvN9x#2|WS)quoT~5rGhsHs zx_6YVCA^HeDEN31lD3fuKUxpcy%5iZT|mY#=&s{W@?9(>nJWk9B0l8~9=%jY?NfON z_}fTN`&#z&8k%#r*cpjF4xjiTDrJUFik^r7E2_K3mLr+HNx(!7V}N893nQK^)w?3u zO%d*KAT!>c+e$W|>;rsaltUPAr;^^;2S{EjwfN{jl;#0Pm?+Piu1IX?|+PT5e!(L^puR?rugTuXik`{`e=!9ab1THmJN2>R|}c-&Xo6xVl^u^9-?c<<;Uc)aps29Z0be^rdkbD%(f9woFv+nb>eh z<|hyVNBvr6fA?^>lmDqnC^Y^6meghp&8+*V^;Df33v^LQD)%prkK;l-T__16vfG>B dv@szM$cAVE>UiJ8HEHeM;-YqjX9D2m{{nb(fu{ff literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an14.gif b/app/examples/Games/Concent/imagenes/an14.gif new file mode 100644 index 0000000000000000000000000000000000000000..8c218ce0c877fdc92c0574884da7efefa5321dc0 GIT binary patch literal 1924 zcmd7P{ZkWn0>|<1?j~UqHn4%<#Sm~I3*jMjLWnIw+HN2`C_#e+20VHVB?{VjrCwUG zlXf-_MX0F+43DP0pc#>aR&1FT$BcwPJ**i@<-yvaSFqHogLl+&N~az@yuadJKfLGj z`=?-Me&&wqG>`_a7J*F+2FAethrbDX8ome+k`8w} zkBzW#-YG)ZCv2BU2sVoLicbk$uFme$9bM9%D8kzkHGE}&JQpRFN(2nHcsTl#&r-g; z+UdJA*53KWD33>`ON7zFl=NZZ%GG{J+NF~t_b-I9g$Q6<5tjG|Jh0m{pj9m<7l^?t zZ%s4{*$kKg1X_XzxL3EV3wbtsqcuA6P({)}TW&K_K3S1`wy~%)HOGW2+SKq~ah1D? z8n|IaB^3_A{kkOpiOc6lde8RHoO|xjB+j@!(Zt}U+_P)hIc2@MsdQ;MMZMRdIePx3 z%Uk}|+`Lhi`pqvt-TvCDOL4r9hsKvOLG7z|^_M#e!ebAe{*s>4->|ScDkjz|%U*kz zDX_Kyef5vrKsNSRQ2bT_m)TorA*QFk$gjDtXq#4)^v_IK^G?Nnlq;pt+&Elz@YTHx zB3KSRk%wbqNLnu;h$wTI>$n+r2?sF|s4cRtBqy zDn!rIe#XPd-SrvY1KJT`xu@0WEgypscVp?+HM#q6U3{>pgQ%74f2B6~JV1pdswH+DLI|DuLrwxUO{)P)Sc^n%8Nw-ou{r%;^Ozrm zd4Kk(xamDN(L@e<14daJlYV4W>~-dxVBTCr!o81%#t%0_=|V<^cUr)hnVSC&Jd>yY zxlZ1+YWWNL7@zxl*1z(FIE6qet{9_=u5@?9w0``bGGTaOJZ1_nZhll5o>&tze2o{> z+5qaLTVPT^zZ@!;`MvYUiw_?uJO?_X_T&z`bLkC|vI-MQF_k|z% z>cOMg=NCSJA$UE$@N1q9d>WKb$E?+*DNbzXm3*gtc!yfmzRut!bEzD#So>?`*^G~K zLyy1xXTgH8r5tfbBs~5D)FQwi01WlCeYK~Lvd^gTF@%@3EEE1f%~Ajwy%umI-n;w!-+@$Zv?|}E3E9S zuCc*qYv^0SJrJh%1H#ktpx&Rbg)9jtbO^CR)nr#J-F@cH3m1bd23JeN&JxGy`I_XJ z@o(|Y*{!ej(nw_L?zS^G5=HvY=Eoc%DTbvm<&al(5oJzY^mFom3P2T8 z+r(Y3NTyK0a_KXXkui?NX%#jM97x;OimGYr)H1=$#bF%Gt#;nhgJi)-xqNMwu7fanplWGD6P>qZ+R;+l8q&3aqe5gKBleUQL!*=Xlful1rV1$27XP-4q1gxNh68D0Jo(t6#fJo8T1^aO+9A1{sIoUBB0ereLnV}k(ZUGGGgkR;_^ m+RJWbKJu_^2eeodFLeIncFMP~`EXG~d$b)0pi2}0+5ZJ}(=-SG literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an15.gif b/app/examples/Games/Concent/imagenes/an15.gif new file mode 100644 index 0000000000000000000000000000000000000000..ea0ecb950e1b7678aeac55f015375022035b5876 GIT binary patch literal 1132 zcmbu)k5f{2008hW4}33%DB_Pe1AT%px1I|PD>m8ypZH^#9LLSvbceroTAF4of1sX- zg?5Bb&&-q(71K&x8K%3KV`5?3!Qd5BI zH0he0oC2LrS5R5mqBFPXtaWvD)=InCY_^*1R;zVjV8G#U%nZ1lPUnon?VNng?RGCL zEON{kok|L9Art5daV?GBva!WpYES>O(HV00SXXP`BOm@1w$639PS;&ebQ~gW09BqAr zmSg7V8;J_sH`vTdi6gb+qYDm5geLU< z-N4;2RtCTXk&K@5X%c4M+c_)o2bGe3NLouC1S|%m_L}yVmu)S;Anr&ISMeJ~wP=&j z5Pzs__IV!yj{GVHTE;PTSO%R72$IJ+%oDOBRxH`wl=o_!vy&6q9m{1|jHY-94GZ9;-z=uNSUl>_ z!8uj8Nsl^tNt-VRCm*&z39G`D@9`?k165ioX zn@<~+j~9($5eE%XQ?98=DYHPA>ctXjUFD4hDp{1hE8(12v3UTHnJQ@ryubAe$jg3a z*i_;1^#86_Xp5)e3FW$hDyUR}d`CX|l;2Fim=C{7mW6yQ9%GVyODVd&f&6+Oxq((d4b=TYB4yOjdm%PF*{6pJ2+_2a zFTKxtL+5VWFNMbS;8+7=o_ZvFC{|NPNjE`3SG23Xd2Hy}(CvsU zs8CJ9HxxYoyn&BHI7QE+#_X4+zJ1`B2mqgodl;=qS)$h`B* zv@V~LjR_);GU@RkS{}$sQmSdsb#%_|5s0AUjB0Xb=rOZ0^9t|MJbs|G|DxA@e)#)stOCX>fJY$YKPYN}AQVLq3`H>%Au$p|P!e;XkOf05B;v*plZmvLP=^V1 zlbFkdx=f^-LS2-}O__WalA%!M(RWz<6y`Ikd)Ym9A8ICiGY5Oy&YU``2tFUNqm6xz$)!&^T9LctNTs^U6 zVo zZtx4z>5MgI=!09&8U3=Po!+LxowS*9{?xA0EoG{;vj<9nap(jx1~ zB1rn7YG+L|Lw{_MgOi%VKT+wEQq(cCu(DseC$u*l8&%FzqhjmLGQCQvo%H`S> z{`~LYru*$_YYS&Y`|3!T&>Q%T_aq|=50ZRt{f#8l^cNnQ&#{%jbsly3ZEbMBj;8Uq z_RB#Uz8c#-CmL7QavLTeVbV57U zo#{&qgp?s5Sbn^5_(nR^T9g}niv7WUf{WGDz=UTWd;!xlrvuq=!ASb&u02O9bH)#c z+bDwpRM>9O?+y^*`M|;2<(oTuO3?tg#9}c{h9-upZx8;NRSl*-;qBfIq)_M8ht>wR z3=Xy~iV~=|K2MR)W}JUsM)Y(cDTyoe>3yDsa4^jt6U%CizmPkX>K)OWg=**Yoz#|( zze3*o{SQ?DUNB{QZM<&h8=GAA<4FXa`YdbrV z|J%OB4ry8jdTAt`Xzl~oTsQa*m&X^nUW9k%m4Q#@eL7>8+AO9j`ug+%7*JrN*N_#0 zp5AQ?hXZ>FMr_%;{A{=xPz(p- SO{X68hQp=Ter%P2K=Hqmc2fob literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an17.gif b/app/examples/Games/Concent/imagenes/an17.gif new file mode 100644 index 0000000000000000000000000000000000000000..01402272d4d8c94fc23196110d81c152284435c2 GIT binary patch literal 1845 zcmb`H{Zo^N0mq*wdGbPDA3_L8dD#=mDC{0FcoBg{?+GM8Fl7;9DUxoXhRHNC&SlDV zx%0^j5G3?KQc5LOEDuaw+JWX&%F`|hX`o%UVw>wZm+cVKH%(it?z-8GF79vG=ZDXI z@4i2N@4gM%`l{;A?Z9>*+6`oYIR8VKf%8Pd0!~3jLXNCJmWc&2p)4brLdKDys0>wP zq(Frzs!*UxH7Zn~>P*sQ8f6BBTB*<}QMJ-wP@-lfYEUc8YK2v))aeXHo!X)+FdNhc zqrqa-*^CCOS!XjVy=Jw=YIIo)R;$%w%V73e%|4sX}g`FuXoYm0a-q}N3dM9gQ6_&g+G^Z5fY!shq;V}vIV2#^6H;ddnh9+D(u zBoPb5<(g`w|Or{g@bdpRb z6RA`xok~1<^yr8G6Vk2Ry5n7))-E%0@|V_b#L(S^G-#jI1L%`!o{R)Aws4Na>FOUC zbPr`^4Gr@7Ts|zKOD05UA!J9MeWdOYFTy?B{^!JmHlUun(PvM?H?pun;;ki~hqaD`X>euiu_>Hv82iy);eEZJZIGRtb08?IXsV+E zIVWgUFou2pVd;yhP5a{&-rP?QZ}u*1I2mw(yz$kd3*|VIbNni6{?B&jhiI!ikDp&d z=Y3JFheGfEr~f+LO`shXE^3ozIIvfW&Lq2jj8iUah%-{BNqquvg^ zPLl=cJIFN(gJp^b@fjEgD3B+TRRcouAi%eFvv z3x!>;4~?azTL{28sQ6MN0m!-_cQ=lY0ecD_M1;m>unVeXE*yj&(r_I5O8Z^&L_KY2vuYEf4!bB)YRg?k_VLsx;f$o7|(MvP)UzPyKn%N~BSwwS;ndQp*Y@Mn{?;yH*wz4|b{je;b!@d5vRD7)PfqKVC09u-~8`Tcrm^qnR+ zMU$<{UAfQcLTXm6LE6u3!Y;_g%}B)`nmY^&@8z02ss%~MI#LUqgJ6^{AU2nC4yr3O zg3{H`p5l*&??G^RLJ#G(-?GspnG=eNcc&9WzY5hOPJZhs`G}n}v@Zw7pw2p7Wfgc| z^nyCp{};3Ef|E&p`{MbPY+`;H-tYBA_&IN`yv~riryXqNs-}PGVZE1pUk zo

O%wjdNsOgb#gJp2OE2Ga?Ak!+>z7|7i%xvYEg${{^?O1q=WH literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an18.gif b/app/examples/Games/Concent/imagenes/an18.gif new file mode 100644 index 0000000000000000000000000000000000000000..f148f8c3e7615a277b76d1942b8fd2420e676c37 GIT binary patch literal 1571 zcmci9`&W{80LSq!@D!vE`lLXYVUj(S7iw7#MT+fFK+!|S5>Mt#v1nRRVX>@@Iw-hu zYGM+{Obd;!l3G!A9}t=?Gdst5DLhuvZCS_Cj>pY(x<6vCAKpKFe*MJ6MsruDF9M4| zQ!hOuc(jE%8*0yYg} z3#2G>Vq)_qU?@*5<E# zwpm?avAWDWi^bxy3anPE%PPM;!8yt8RJF9U3^@zkEe2=1YqVX^-rhdiZg#m`qi(g^ z?H+aC?(gp(8X6k^-_0GP^LK4dS8bNFvbU%*S<=kSteDu<62P9kX7VIJ9@BtatuF7^ zgNO1x3JQx37wh$29$pCIRa#PhytvG0q*qlLk5!y0I*L@EI(dPPoHdl4F0s?6}u>^6mp)thKk51C?B?dEIfrNCGG~B8-cD z{@BW`YtR3p>|s(P1eR61?i|?&^g#*n&|4EpIfeWHSZtzALf7n|Eh?q``cH`gNp;O9 ziV_EV^K%zqr|c{oBTbD8_=q%c^1%$*8e_zi_qiZG%U4;YZ8}Jp2JfwSe!X(*%cgH& zme2g$|-p2zOolIA#IMLgHJMD>dW=? zu9x)EqktrD=a#F{OzO^K9I%00oh_pYU!@f8q=(Erv)9$h1E#oI}$?lz9fA*}>K0 zU|KEvodfdZ%m6@wd2KdmMeq|RLd0y5mm^gPNoF9*Is%qUN*a9@Fam=123urS&*+-@ zH5RI=UPoi9HObz$D6+gg?}JJN)2Vdx>Gi=p5uYkA?J-u{H|nJa;=d?E>loe+8#%4i z6!j#gVA12QbCrH;$ll06PC{6UpACtjIPji5ecOlnx^C7tbZ_U(3;ZKKC?^j~-A1|A zSNz6r#GSFiH$H_&VSyfH4*A-X?E9%Ich|>9!wJuaaP41>BWF+d+xYRfJ{XRBwZ`7P zwPV=B79Ri0htlEI-rNWObaDqT9lYxpYPLI|!Bua|v+>acXB~5>|t5b^u5dJR%;lrZ< literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an19.gif b/app/examples/Games/Concent/imagenes/an19.gif new file mode 100644 index 0000000000000000000000000000000000000000..21adb25555637be4b1bc32575b7bd858c8e7b07a GIT binary patch literal 1849 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 fqZ<#Bj!b*KMTzW?4e8=bi<&zp%qh1;pQ&PYg literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an21.gif b/app/examples/Games/Concent/imagenes/an21.gif new file mode 100644 index 0000000000000000000000000000000000000000..f970b811c335423f4cedea60570950a118df354a GIT binary patch literal 1720 zcmd7Pi(k?Q0swFjyBIWN=MM0t<6Z;{* z!(y=%ES5^aFo>v7DjLfOpfCbLX=FYPN8_+!!*St}thdS(_u_0w#zk^bYk&BYaqm40)14)79iB#3T;G<&y18H&P`zYmdzD^cgq)a%L zg?34OjagB~%+#)|H=9d*drI)u5ByI2IjOrezEP8T;R90pp)yx(Bv@N8QL)ugpW0tt zIadcCKU~*iR8ATsQ%$J8rqUar@COUpW2a1l?Q7t!H9Mv6jB(1TU3SLayjVVQ>G)Uu z?aPA+uLr0IjaItPK|ae0wjxBwvJ2%B3uJYKV`9cbgAe00)y z5hO>VUE~pur(HFFnzO%U#BMn`T*lwLcE6;Q6jQV8&Vcb6{C1GTnr`8bCgW{L|AHP}su@BeoIj2roJ~4f?9=h!; zyMJ3N`a-_98el~!@+H{WKOs-mWpG#XDZi2yfr8-GM7t6;>lULMX?_OWdfr30AtPC} zWM<2t-lVv}`jnkkHz-ClT4{FgUCBw~dfb^+mpOy4li&h-(%Y1M%FR0uFs2;Gd{Zcp zv1G{v7U6^t>R)o|Z}Cs)(4NwV2kQ3i`>hb%8=w*wHkEpt$%3gDO3m#rHpZ=)z$-YI zwR;wh3gb`Q#xW1ANHBqOw0A!g?5H_YmrCh#ky07ZRMltCvyazv2f_BTxCK$3tJn+e zv`HyW1M#<^F*0+`12S4z;j@O?PV($HIW76og703Ucnsfm8U*%U`qt>v_qjH^B`ond zC?O@nV+za}$sd1%VNTh^=C>7fQhK67)8rPBWKG{4UX@}eK$|hMq&BI{o3Z&e~TVf^Nt>ZlJH~K zIyJ}%qQvJqogd@v7ovwcQJ!=d#$k>OATvL$VG?#aL?Q@;5-);oC*)2knRbWQ43R+- z&B7V9!_~M`YNqeYXHVaOCb~cgJ_g=k!B7@j3F_O+JSQV%AXR)urw`A z2amWEpl-g;b27x7qU{wTcGX6-`n? zPO#cK_T9oA%nXuFiqS$6s$8knUYLXhcSSdjOn-K*>UvOc198bc`g4tdrq__Ov(L|u zenVWb#-C)LvWT0^^UzI=G%ev}6@d;s&Y0oFcd&sGgJ~~YboQ_7&4eOsh{6(h_u1mt zU(SC&HnLi2q;0%9U%736nmQfaeek~o?fT+~kJSGY#|=jY0rSrvGj|&Nhw~9vTIXSV zSZ5F@pgWbB44@v?_S68go$Fq<%R_Zfewoljx#Au%(PpOgq_4K S_!@LH9Qp3Uh9oQ;BKRMkt_>#u literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an22.gif b/app/examples/Games/Concent/imagenes/an22.gif new file mode 100644 index 0000000000000000000000000000000000000000..6022f7746e72ce377d484360990e67595737aa03 GIT binary patch literal 1332 zcmdVX3r`bw008j+wb$3}T01PZb|6=&ObXR%i$bi@*O7 zTCgYrN{LcYON)$6P;8jn9O&^_Ej|)O*qky04h+7Q649t}F#Q<&eTAPPRUaFds{~5m zO91boD9Q=qi%?V~5=lfzxP*twL*5g~L&CA3Sd1SN$&ZYVSgW3=)yUJdB0a9$sz=tR zsj>~BxtqeWH)?lgMdxKD**;U2=gAB6i9M!_-6chiuNE9DG*p|78uXo_ORSk{K`gkkz01{m5 zK7ogaENIZF&NxomnQo7`0|yPCv^Q$wd+K8jKs92~ywgCoYF#5AYLIa2*UAwJ5-wcP zBd(lZ!>)pvFm>Fq`k<8+c9mj_pFd_Vb5erEiZ^c~#RvqZ1$@;Qpn9!r(L2A9$ym2f zD08+ECS9*pqD}yvokO+upyWLP7c`^?;+P*aTvFdEd837mnw$t3C1E=Jau4HYvyL|c z7vFSQPBs`(G9c@3Hn4#H<$x=xI)Swp&qR934YfV{o1$&u$p?cJ8Z-xtQy)oZHN}#l zSK}^Q&6U>;^t3GVX0wff5@WRRa4jcljPFiNMm}aCNyo`48Sv=-lp31`D=wqLR>X}Z zZbd_q)wA<8bqT&-o!Uf4hIAXc2wwzph}<)~xQ8Np;l_>kTNYB%xKij&yB&=xoEajg zpSDU@c(1lu?r)B9_fxjeu#5lTPk+?>n}XXZ&)B)FESej?_Pdd}2ftLzdDs^mff8ct zl6}D|(~9FK0^BV^6F4nkw|sgJ6U1&}WcQR45L0AySvZQoZVS?pN%~dG%rl8-4M70A z!ryt zRBqf8R6XfjdRGiH+K&YKPuiCvG*iq~qujrzgG>>jpm=;<#Tn~k3_;b2{GixQ5_%mM zz$2sJuczQjDYl*`@n51l2LwCFWsOscl+Tg&myt|Vmm_Qv$2xd;7dim` zsE&yhUf*`9BYh5+Wba+ra_dO*wR2|ytTBP*-`0Z?#`Z4Q zGTH-d5ACI5>pQrpi=E^HOC)I&PCy}a8|LkLYtQJ$k23hmv0JzMF2WI{80000C3=9+$6dW8JBqSs$EG9HGG(0>!KutkINl#Q% zR9swKWMpJ_acz8iae;+=jEsz*n3tofr>v~3w6v_m#Kg_b+0@k3+}zyc_CX>@2HRA^-&M@dak04x9i001ojEdT%o{vaTZ zq*zQ9H8F)@6oI%%m6f8|#_fDxmWIe8Fi0>O5DBHi5EOVD9cQD_^sbzrDBw^K8CTuph1R?@|0|Fug6Bij24tzT#0|gAl2MP=d2!OfK(gOhq4Gj+w5D^m+4h@drJR<@a zNFd?>1q&Gx(u#0EfCDHL=0%WzFhi9n8fs0LkO816Uj!!P*?~bqg9UW{HdKf};lju( zBLf0JP(h8i1`I5$xlmz{nx9jg84!s@B?AHvdr+wY6ex)XIY9WvdsuJ~ zVO17fKL(sc04l%<$93-!oixH1F_2}J;>B9P(gKDYS1uel-~g0ZfDp}Thy2DEF4;DEt`1?mozld#+-wB+xRAOJ^E=+#381~~rEA1uz0KtKeuB-RuJ z0z3dd^REX9PH*Km-;@xE>{a?X!+&kjaS8ENpor-~q^K^1uQKP@@e2#UZdj z0~BEflm-(}P(lH$rE!x23-rc-0}t54VOSYBk^yc8%#acz_dsZolRsU+Pm@0wv%qEs z0=2;k3jCHUf|yd*GC1UXponJx>mm5~Gs6tG_oQXE+v1TI}g z1qLheNM<>6Fb5eN1}N|V3Km>ogFX>F&_Dv>F(ad<7%(2Gj55m%@CO01CQ076)-W(? jTt|LO9tkxrvj|q!qATqH@RBKQpcAmw?!EYq5CQ-@J33%z literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an24.gif b/app/examples/Games/Concent/imagenes/an24.gif new file mode 100644 index 0000000000000000000000000000000000000000..7cbc5d78008b457bdedddf51b749931eeecea156 GIT binary patch literal 1627 zcmd7PjXTo`0KoB|ZEP5O@w3hB-8!*xGgE7_Hk)msYaE5dh|pa{s8f2{_=$PYdeNyH zt(r~`WxJ@Ox2+eqO5KulSGtFEh3ZaEDp%sV|KdJ>!S{K-%H;~-lB^IQ1n64)1&dN@~I4%wyz&%`v2g;MG})bR#!sb0WAoUZ{(u9}m-2uX2Z0g+NJh zt*iVmigxbJ^Etk9)A^K;+p$RpiU}tRqD~d#e)x9N@g3a0LceQ8<<@%oFO@P!1IpS^ zaNQtwws_fEmJS=imxd*dquQrOBS!ahKR)9*+!=nqYyEV$*ZdjMt8+=i7oh1u#Qae3 z%YNgtyXKdJ=J{t$v(x?a|5wa8U)h%2tdiUu#|X{_7x^R~wA63Yl2i4U%83Ns_?Nv8Fd8OdH(J$_aI%m%^`)&(@QnBmmO zF>FxKHaMl}+v@|SU30!(_XJ4@5o{WKtG&L5-tNIy@v^IL=3-c;$Q*Xnz{bF|{tsd6 zvTA)qrTQjAO_gT_deKv}+K}AqiB^+t*f$)C$eR;Dq?1d=CNCrPC|`7s4zia_kP4%& zWIg7WQ@Tz9Z{f)LT|qgNCD~@eLxiVzM3aDR79m=V!KLkATC3L-=K=9Y*SWfOxhdG> zXf<^N232$|xSd7sBIuJ|h^8xjt6XaEud!wR2lhSQwBwDBotbbJOs}{6JY|yo!5W@F z!Qs7dcwW7`^Y1Zrg%jJN7nc7-%P5DGt+l3QFF(S%kS0N^4#`n(mBRHkti;q;*kec5 z5LKVEe(aZ^u%dqo5w17H~hXw(#v*FOiSF%yD znPi0_se>ib0RC>(I##S?Zafp3WA46LMN65vYJji_If)%;g9;lP-L?|Kv?bqMP@JrE zc*MZoX%9lPSh@S!eKjdtPox)D9#XV10yG^XLn8#(OQu+TP%O7RLsjHvOpat?g`Uzf)38E@aqIWVyuN zshDfhIJ>F*BKnoSUSF5%4WS7s?TZr(p}B*2s3;((^?r@HWiM7^{Ro6Tz*K$G^1U$~ zMo0Dg2v%)gY!tv}h8G&51BmhMYXK_RD%<$Yvu;G>B#13#(elk4cxZR3G$XR`eX3R7 z*<pA2}kTZ7Z_e;>6D6k9iVJ8k`(;X;JZL+w;$tXmlG+-dZxW+Dp1(Aa*fditnN zd@7VzDK&{cR3fMr&3iaf_`yCFez~MU>~!pNtPYMmj7s($441+hp4i^S;XVv?i=P@f zAbR7$E#Ix6woPsi;K*x4%b0IX1J5yL(CQw?Et+|K{V&t)hW>E1i+8)P#hVp@OUnF| zv%lw(d{O1(ouw{yexnZ>2Rba#*r8jm#!Wq)&o50vZND}SM-vc2&3f4j`WwQyjyYTJ zn{|jT<*Gx@Cv$@Gi?nd@2(aOT8y%Bx-^J5b_>Ie4gqCK{)1~`6)7xQO%L9Pm?D?pQ zC*R}QF=>T{JEK|`{FHk#Nz##s22u$q$@w}p;P7*C18h3)G@CB4r#c9d*zol>K!tz- Gk^cgv$mc=; literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an25.gif b/app/examples/Games/Concent/imagenes/an25.gif new file mode 100644 index 0000000000000000000000000000000000000000..842fde89b07f902e2419fbb3ec524ce2284a3bb1 GIT binary patch literal 1694 zcmdVX3s+Ki008j+y@0^=f+*;8Fb^r2&c|?Kq0t6WK1z@HTF%H}LZW7&Vfbhb@R?}c zrKPPhE3<4`=vk$y4K*yCZnO;3Xmib6ZKm$bd2(yE_A&PR3cv8kFn&m?Kkx^GBJc)^ zqC_VO14S7OhARVMFj$_hWR$~tgTY~WvYi-yoHy9MUOs+aLEhdho*zfRdqcqEga-Qv z1cES;zc5-Pj*Qt7#Z8G3BqSv4jpECto1{|duH>YWgspP9JX0abSH@~`vJa|btJDbx zigK``ox0KkSlOwl|r$@pGMHy^7_hF zluAEK#zPQ*V65Wutx{t-;&_ct2E$Na-{&S%#Xt$_=t#v6cbiZ;b_` zWyfDA9456Fl@CvuYh0PC-R8X(zijB}V|%WwuZ-fJJJZwB*N=m7>JzgJ=$w~k4N++c zIrp68% zD(7;ArDD&HkM+d7H07O~;*#Z4Q<1!j+3Q0c+`k{XBMVFuUm*r6C7;=*^u(^W{Zu-8 z{zL9DB3Z)K@4gm!W1vXz3=`|J+lBW6g0MRSf zI5VnQ1$->BfwFa{6*2Zt3xp<|{NDH!)&DP<+z>Lh0+Q`%J#=xN4*>mkxf3;Kh)f3C zZTq7QNgSH+<-$2ikR@C%S^ZEYMppp|d@KNK9JR|XSTMmeZJU|YO-oY)4ylzxII3c1 zU2;(nMgYiGb6(;;U-qTG_`+Yue>oF|RN^QBt=OtPf%O_3cweg$A%|ZqjRF3jtL4;C z#)%9=@qU+LvJWFY1zEcwyo@j_UR*SmdrvFqK9d}ZF*r{h^S--Efy z=|OiJjcEP^(*zF{N}TuJTZs;LS1L11AC;|F+XBJK)n4LK&Th$futO4dKmO9UOIC7- zze#Ur-M`$PW*9v-LSS3f+RuDzWE&H=U3dWL-C3BPu!gTGraI_tEUBMKAcWtZg*~G2 z64m|jIK}fFx#Uh2w0|ayQh%OAmFP6wUU57mdR#L_vR6uV`$Bq%rm^k@)%WpJ%3b(v}?@4!*&@qlq+O(D&po|ia?Is#U)(!rD z2mwWC8kvQXFl_)u`*Qh6dR(Le{q%KPCV{MU|5!-w+_whcD;oZevDB?-7afe4mY|AE zdG*O$Xpsy8NyuV6xGTI`1Xwa`1h6eDx*1!V5lA_-`K+wiL@sQtd$jpQEuf}7=XBq`sOBK>gs%vE;K%nxZD=39IAZT1oKYGGH@}! zYf~H4OlCwlQHFYseT3Iqlb%+^v9>1sbrinDxTp^_`pe+c&#Q8XP93gWT!!w3WqQ)U`hNijv?K}u literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an26.gif b/app/examples/Games/Concent/imagenes/an26.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef51d69e820ccb5859317741e29bcbd1e8b8a49b GIT binary patch literal 1931 zcmbu!X#0dmQOgc#ulLL@bjAR&t^a1tRQ@=$^ifn^wMA}AmMJt#`` z<^hC{;p(W@n)y^iobUr3BTV-Pv6s@b%w#trW<>1=Cq2GVubIvz?om#f0 zSOi31ybSytjYeDUM?`5f6h&Dm6-7BbI+=s=5(CN6EDDFiK`<%G<3wX2Nvr@KTg&6| zQjkm;7Kp9nZ&*eX2>A)Iu^fqzCsrpX1aA}|5{V=!QBcA|WKwm3ge;fKv*hUS6Qk5h z(N0O&#zfN=HC2(VOkKAjBTH(<=b^EIh z)*S8IFB~_Ax7NAu*O5GjB3oNqPwctew4CkYoXj9Bb?I|+H)Qto_Q_;%d_OCuRA;oPb*)&ImI30BP$^f?Rdk}eZ#YvP zCsLG&)7wNR7*oe2itoW$EssSr2O!Zm| z*$?rud8$92QZSC@JaWya4ig(K%%vXm27Q^yfirZ>6Rr^V%D@m2=IfksHOKMv7vu|) z!;KUrL<*enmafH#(sEY8+}6xCN&wkWGh^@(BCZW#Q`cEdXUTxTa_AlSgWD~2d%G20 z{Q1{@w6qBirI9K2dOusDW7W+GJ0|V3U=99&0m^9$uNS;75Rq}@m8q$P)EIOX|Fg*_a3&nkzsamR;?H0 z`8iE6DF1ARfP4)`i6pc3R9w%%ri|`^z{xQ-hcqw@!TD<}1T5zak~Zy?3A3y1ja zvn@>j%VTc1Y$9)jy&mL19nMwN!4>I0K7^rURJ#_a*lW(A2^}FsVCr|OJOx0k3{DK9 zkFHE1!CLeqEWd@C1Z%-EViQuz-uuWI6kv&yepL-&r{5?4-2;!ldhp^!gy6#O@2cKjmFD&Zi69liSy zX^1iId}sUiuC`#7x-bxqty+dyoMGQ%^^;w85j^GmtZ%W!L$Yo z=jbh!-|b_3FMB#nENZ4e7?SmRX@f`+2{LT=z?bko9}&{v$hWS8@9+hfWWepsVUE`x zERz1J9?HQuw^R7ilPAd4B}(h!SmK+t(3Krmqe+Dq2}9Veb*M4WRV`k*z!`)r{ZZdu_@qYWkTvA2%I7TshRBB4KcHkFseyYM>r|{&B5t zgh-X9OM;&CRFWq+!2~aGvz`T=aoJ1m#gM5p0CjGReYNRy-yLom7@{OqtoKrNj|3A}-1Hke3-roDh+cwO;FucbsU(d$67W>q`Xvnu=;^JyDU+U;H7g6oBF(OM# zT)Ld2gyOC#K9B05k8MVY>qgw=lR8S}x~`8peLj6Y58S8w@$>xU^#{D7Vx;`dS;0Us zFnR>|fQa>kVHoS|Nd6GA51CA+k?qN329_fBq2XZ`;{zw;V={~3gE*6eSRWvPY{Ew= z8~q*qgVE6*u2BQrBIm2!3N&U9Wu zFR!3qsZ{REkria>0uERdd0Jj^e_4Lg=7S@>d4$rU!o~xX^5Ue7x=;1Ra`lm-qX*9z zj_lD^srBj(Lv^lRuWvoBGMp*ztKWOHv!tW3Sl4R&rumGm)$wXe;am&(YQqnGow^H` zyLx*YdwY9FuhjMT_nNMr8|(hraKm=2N&n8b8~aAADCawP5kTG=<8QcR#zteSpH>YWo31B<&Xcz zTz8dxo|9FaqeM#g7Uv`K{2U}ICSC>v{ki7PNdR_T14vR+MIQkh6*+Z^^w^WJ5jYIk z)P$o&7!1)HTQ_om4QW|GXIJ;7%RRl{0(c`nFwlh&DB!b0Lj_y#E@%kC59naWuWr)GR)A@pj0>PvUNO27a2#<{0U6`8@K!nF1@wXtM_lMZC1CN9% zppm;EMWLYRrS~A#(T$(m5gqq7vIY&IotyI)vMY#)OYg0HolbK)3bJjEBb+zG?1T>G zfA+BBDS_OF6SddSRj9%z&g#{70mG0Bd&rfp`q4%z#9-kiN5;b$8B{!q`T1k&td zTxK>ou*8sS#!WR!&~+~&tnekwp3AKxZ-^9=q5<%dsR9u17&rN7052y2c(uUyiTfgfF$<3nM`IV2fqnH3+&9K^qmqGwG6|{*X3QX-x zw@)UPEe8|%W-F1)$&xZgnKe-!zmA#A($CgHMF6nq0y=5>CTCTJ(N4p}t)!&KE^*4D z^T#V^))&x6g86quN$9~TF~p^vr7|LZd@}+k7f)8@m(G=(S6nDbJ?8E{*aQ&Q4R<^mV&%wvV14F`$*Y7aIDuq4E7P+iAKj9l(M%VI|?{iBmt-7p^W- z8DG4;I7|JsfccUl=Q)Y(y#4;WxU+8nF=4pYHp!uE{hLu$;^2HFwXoy*NO@Qr(nGLP zK9k^ToqaX_bcF)2Bj_|1IAJ9<2?q<>@~Gx^ZY6;!Uj}@eI5KU&U$;#I8(Yqw@(=#n z-7@%e$CUHMMCGX}k}Dt4LW`|p4GM~#eSL0?zfI!{`%E{816I1kLxgp{>Nr4c6dF;4 z)egTNKzR5K2!{j#u~@kf#0TK~fAGi16+*n5yGrMgXT<&-xCY29po3Cv#)vzFwH#}i z&;sXA7g_DbJ8ilNuf}B*l(*DZjbqoCUS_xoWfs6|@stFYS2n354g0A8E@g>?^(-t? zm@xA0td$8~6c=sJ8KgbC5Z!_jIyoT7Z6{ufXoMaw(C8l$(}h-TvB6}b8@^#ej?O_G zU@UXRRMI%$S5!?=ztB{;@%Zm5F&+XHfP&MPhqWb8PZdD$Tc~z#Iq=;m-QQFjj>V}S zN5V{85ylZJZo3Ej$W*4Wo4?nm;?|)sl9m+DYlj@?9XKc)*Sez~b(#3jZGZC^1fm#m z1{n$kG+!9jmfoSdb}Z|>1`&?{M2i*JuAJsAaA`{#Zh#C$BtI3aIQOW#fPwASLPFVf zi5o1(*0h=7-1%5at4Z96Tj2itkQUClb1q1u;_Q}b!MKI3-%YoN1%%#Ly(D2nx>?Z5 zjYLZERCQo&F6ZDKF_vH&wtsN4MpfqBa94-vL;wIS^G7m{=gk;0*-vC1dvA`Ub*5K2yWV1^%_t=FQEwl%mme70c zl7SS!4p=8??aR$!@o^H$iyCaWQHY_Y&@WmQmtb?dbWB zWQC{9Ol$zEf?oOJu>uX~r_vi|rr;7}7_O1E@kslklsD~M(~Y1UH;?jsZ*ZznKpx$u z3P@(M^IO)~+onJz;_Xy!^YW(Qd4=8z$ZI#qA)1Kh==kWTwxGM!( zIJXQOKMv6zf5Aits`Q(#J=6ZxwI5u|*0_9i9ql0_wOT|H#`csU^~J3HrTN2792HV3G>TCWmsDUN`A0N*M^ z+yB_m9hh3dy*z{F7mvUR%cju%#@PY@klz;j6BF0m7f8dD$;i1;6`txEs^ literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an28.gif b/app/examples/Games/Concent/imagenes/an28.gif new file mode 100644 index 0000000000000000000000000000000000000000..10f6ee31942468b08b661b06904c5663a350cbaf GIT binary patch literal 1944 zcmV;J2WR+4Nk%w1VJ!eH0LFd*0000HAT}c)5;!0Y4lYwQDGezsCL}OM4={i|GY>R0 zG(0pUIyEUcHbz-A2qZjwJUl!YMruqv6h=WUA4Zm6MhZYpK|@JTN=z_>F(h6`9U@Yg zJW7QlTAg81A6`#9R!eSgQWR8FR3c=XL0X?|Tr6ZEQo{DgcjH<_jL&%N@ke7a>lyt?AK25Cfgr2b9j|rccm(ZRKz@9v^ zoNfG!1l*hy?3@&_riApI1c|W7qpGLmp*5_ml>eXy-LnhauQST7Z?v?m)wE{xwGHI7 zME|oG%)E^4w^Yf-q4d-Q|JEwz(WKPW)a=xx_C zX>@2HRA^-&M@dak04x9i001ojEdT%q{(t~9gM>PUN{ESFX=!+lkC1(mla!Txf?Z-_ zYjd5Dk9VPQqok##r*mUkV{?L>p?9NWN=HFKI<~n%wYt5&y+Bs3vAIA%D=Hiy92^_Y z&Mqz;F3%esBhlG5+uSY$K>#{ycN`QH59kQ!>+BEB)z;Gw5Ft23M@=r!6Ce&A7Z7|Paq&RQs2w&Q$c)p*Cb|d>79>a@K|+NPF($ORL%`94 z4`0^&7{MXVoiRyi0O)cDOvecR2LvRja0Qm#P&n-LMfn6wJBf<%oaQ<&t)!EahLCvzs} zkS1ar6&IoLOkt-ugwGutC=e(>H3SM2A+CmC(F2#0CLO#X}r>c)?F{A3Zb83$rz0)*WBqfPx1VK;aWL8RB6C1++zA zfCN{NXv7O05cI$WA7K8Vfd?hPfY1mm+Tda-ci@qP3mm8=+7cZ+fF2QZgbfP$z_MAm^b5lnDL6CKPp0uXDgp=c;yJczTDd%u3<(OR}eu)IGFBMtPgYGL0du(oDc^OKy(m640q&8VF=@h znL`Uskl}0_TK-79h8Xy&VFQ+=$}z(TBc!{*4`f_{0|q|aK?MXAu5J26K2IBg`+u) zU{oD$k15Rs5`g)}ZqX!lgA%UP!FLXR9}&mgb1w1X6*GqVQ)?iIu;j0IO!2XGNdbX5 z4Pw->a|C3NI0UWy43WfEqQqfFJvG~F{u{?v`PbIcuKC6n+;K(@d~;I~Hyb#y1|D0uol^Mv@FVB=K$QPH^y_0^;#L3z zC+uKn9r(dG*td=RRSs+V*hUBXCkWB#LmxSq!ER1~1G0^R4YTQh3>N6V@HOy+5)^{} zPUpT2hyYA^*wqs5@Pv=Sz$^^xfC)x&!4gW4g0%YyKiD@cDbVkJ9f$)M*vEl!T;~BE zK-v|qfD|9vV1g^$-^Pygxx_VrFKvK>1|NXK=&^xuJD`{quc*Qb;-Ll6c)`z3a0fKx zU;|R{K@8?#$07Lb4o=`)8{)8p5j0^AC1l_TexMff!7&>Fi-Q-~_XIh>;S4DmLIl?7 zcAX)dfeR0i!X7l}z6@fqf^~QlB+Jr<*(6~LTZq(3F0clPflwtQD8U@K0KLs=a0E_h zWbNQ+n+J@b2xK5a6L!YL5)k1ISO^vyj&Ov!z=4_A9KjL%R|jIcAY>FsNfe~O10VPR z2Rz_G7e1GSA*7KNb%><~v8k+WqLKt4%cnE2mjo_^;h%1>!x>xkOM|H`iMDTjCP&y1V=|2(s-HlVw#8-#xjrrLKK1qIzR*s eQjmg~(v+qw%|I8rpaetcU=Tk2=}%o62mm|KqCRE- literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an29.gif b/app/examples/Games/Concent/imagenes/an29.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5a845030f248fd8fe3401bd0cff86f4f0b72ce8 GIT binary patch literal 1414 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an31.gif b/app/examples/Games/Concent/imagenes/an31.gif new file mode 100644 index 0000000000000000000000000000000000000000..aec9e5268040b0c50ecd816fdebe7d0755a1a6c0 GIT binary patch literal 1910 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 J4h(?o{{dXF4H*0{1F0r%1 z(~KObTUw~(Bi*u15Xne8&Bs^HOv<|MJG)OE&E3uE-TsFC{)Jy^I*XE!9|MR1xMhI< z`*07kh=|N4!*b$wo*-j#S%G>ojFEm=&cqH9AqHCbvBOC`9#77uXg_#MCX5ydQ-nfc zB|o5rg(^HM(DP&TybO^@R3;)8i#bzN_*Z=VDGB#N5$d)e##R)fR(@>y1XEv}+Ad8t zs(2lrAj>{ezRvn%?PmpU1x{O0W~s=SE`zKWhuHK&qtV!OPI0%KF?t?WTYvWEd16#zZdk0$Yxvegj%Qp>~@w6}8 zw~miL-kObC9y8BPeLp=ty*%BzG(9*!KfgRbwzRag^|Ilo7w&&AKilw)ZLR#%=kslB z__ltB9h+f{lj3}pSVT}uRT2VEA||A!XL111zt#Mf1b}X8igo4s3PWX872u0n)NaXB|78JJWeNV*hNde zJDQX7#TBRk98K$(nQbBqYJ=aE$F4OMaM*!yq*U~dg<=Oc&%YaJQwpd zHR{stOZyUA{%f8)u4}j<_MDxpGZkLmy(uW3JlL>;t>Q55uaGQ;%(>2tr+idm_Yb7g z1X%q3<1XP-n`S02<-h>*T-e@|qCI0{RmtL{wlemIHFrVS^F~)vzbZ2hwdWRMj=hGJ zh-Z%CUMt*P-58uiM}Fqb=)BtZFRk0#(sUABqTb|3C6pqnYn<0RZNTH4Z29z!esVGdr$mwAKo88s*z5=3B>5+aqV|OK7t7$Lg$B+EsGNN*)B= zbXu*`V!z>!!yyTO$kMG&KF@)ju>!}H{i^^vL`J&6D!IAR8;EhW`Om3~kfjBm_V?L` z!lk}`)2CeRt8ul+w>jbm!w5&pgFNgBZ7xa@E8&GCN=xc~~%UC0uNN z=!bnbDm&lur*G|4*tbddhJhLFN~WA+CuE{MqfRiY#ID3&V?0r4{O`QYWdy!00bC=C z*%m#y&e;QF2}ToQSrNHq-U=LJkm7ltV%t+=OK#>`_Xi1-b%DgeML)iUOEQR{ ztbAI?Hv}&j`QD-ed?)H>#c;6SDPM433ZRva(} zg_eRol!9bN9O@mU0cF&5mxHi(nGWt)ls&)F_eeGM>sqk*duq?M%G!1#JdMLu1F`JMlRzf%*I1xTzM>6|#L>>U9JOr65bpTtB zwlw%+E6o9uDF})9bU6Y5&tN~HhV(xF&VgXS7k>dB@zuEzFW*hN!UU%pZ#qNBsa_o@ zfc@hyDTUKH{cxmtoep{y|9bcb>HH#NHs6vRE~ILMd@s~zq>UiY8&HT{<43(cclz8< zz%+~IM7RnVh(`2%r_pLgGr-VjfZ_964eBm}UN95vM>6KaLu)~em%U(28$G;@ zQyy-i6&;G?-#<}F!$lws_(z(@gYU#Q|HlGft8Vtc@Uu4R=;XOwDgYQkKv!I~g2E=K zaK1u^k^qc(G7V&Q)EyVUSQ8AR!AOMEgL6th`hfEc%lb}il{e@tycIi2N@HQy5C9ki GkoZ4oMhkxc literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an37.gif b/app/examples/Games/Concent/imagenes/an37.gif new file mode 100644 index 0000000000000000000000000000000000000000..32bc6a9d1d3f8ac97e3d1b7dba6ca06a157673b5 GIT binary patch literal 2003 zcmbuXS$i>ePW!_f**$9TU69`lNeoAAQ=v?BB4@zwmvY?}p6u z#J6nGAR64<1Ktpe#Y-?AAr=z^p(KO^q0&h41PLCd6sS}x8M%hgsKQ8jl2SxOY2HXA zC1m7^+)#1CD#@y-74cfFN*|RHAGmpyOp~mCBU!IWN?5f^BU@owlboEKolI;?Se|Z- zeqSH7JzeP3<5Z3@H8VFWTjz|^6m3}26SL@|Mq|rgR+O7umQPqLmQrh!)oMLlAaE8$ zJ1rZFi;L@uHG6iNE&FpmEeJiaYtf&#L|5RNwhNWAy4wBcYNJ|f z-?i7*arGOAt1Bwo4-6e$*3!~4aSU&5KQwe~_Xj-(x?Js(os!SnPoL{QKQJ(G@#4VC zOOk~@D!;pInYrrpdcFVnOZTYdiweFC$lr&nVFf{+1Z7st{?w( z^TqQgFJHb~_`j*=6{!_FZB;vq$?DxzC8VWfC%GXr#|)x=N%Ko2z@KLTfG|BWXQzF!Np#F4t?79E&z<3Mg^E{|OOq zyt()t5CW%qj8j-9kd0;$@Y3u*kvtY+3eM+;#24FUgs8g2q|;6STx#&8LDK2jQ_2X5 zCD8ffQ#gwnx@uk(GbK5b@aco)W zi!Wy%AO2@@{g$sQAO-(OowMtTds zxnm=D7kY8wc1v#^T9@r93qJc|4or&kH;9;XSs{uK@c zquryw!{SEnT5(`@D{>NXN}vXyAy`L*WuP1N5RVV|MeyX+`{UrCh#l&ZL%ph0|g923N7w)Bkb+L zwBT*Gb#~lglG=G8H9jyB^6Xou!XjM+hMG&bPWb*Nze{d&+Un&2tv*lf(&@oeNrG9# z;DuXkXdEJCPLuqOa;JPHPP6>~%)^{1*k&@Ob42B>_tvsCIyB_R{?M-FNQZCq0#x-* z+_hqB_RUivy*|$kINN8JY1)Ryj*BTjgRQ(>J}NjiGzp6gp;TFL))pfcqCmhVuOHr3-@gk_6*=w7scNqz1ElWq~=uoX*moMTpum(jy@=9?ebi zVZ37iNG81FoWY)Q;;+9A@aGd8EisNXf3N}1T)WQTP-f6_L`mTfhT)OA(#>avJAVKi zM3{|y(XBwBT%JJaC??kL(~wdicGIZJ=iVs4$EEbO)-Rj1%hzZx9Aqeg`#vz6xKv&# zDT^tGkYXQ83>-g_)^W;uq{oTjlS{f%-I9~Oc%J!tHF5-MwK5Lj1mM?L?Z=f!3J`yb z878*AQ2D>WUc}a5J{wv`pESNYi*bE`UzBhj&5Hz|dD_2o%|8W54X43tJY!eLaf!5> zy;Nb8CEk~yv>UBe1&e-~{3?tDq zZ9EGLE_@6?tiO2riW`An`H<~DXQNTT9y|be>*UrZy|2^gB^74~lVTI6EnP)H8@RcI z{POYW)2R)md)|v{S>3+rH5ud8I_RLU(BIs4^A(fgr8_yMK1PD?nhd_J7=B{c!*Q literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an38.gif b/app/examples/Games/Concent/imagenes/an38.gif new file mode 100644 index 0000000000000000000000000000000000000000..9aa66afb3da19501fe0fea21c813687549754270 GIT binary patch literal 1782 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an4.gif b/app/examples/Games/Concent/imagenes/an4.gif new file mode 100644 index 0000000000000000000000000000000000000000..2deb615b9247c2b6f95904d5a04ce0c1ac9f1f72 GIT binary patch literal 1227 zcmb8q|5K880KoCj^92w@MkQ7V^HCoV^`V@53NvQNP$|D*(M0ENpkVl=Wku!+3Lf!W zXN@lMX1S)#?Od0QPS$xhuM_56xmM(ta=X@98MCf7YhLF%&&}Fju-7l|pWj)VGgCGg zq(BP3Tnm<v$J7XBXrTM*911#BD_2yg+8hZ8tX5I8{!NP-|pl8}>xYAN_q~bvvC-YwIg+x4X6Dwe#L314BdCu3Z}%zq4d| zdU|ed?w|iJdXe|;9+SahGGhBHEM`n=Hep$tb2Wf?wf1Tf&=&`f)~E^oCs%O&)#}vG zbD{eC7>Xj4(!iC=0-n0~00&SixGJgR^whJz60RSB9FF4MJBA;Yye>~F+y%x1&7&H;5BPZ`~YC_g)x$NJc8DCCeKt^$sWIw)w_pX#IqgoOX?mR|yM#nC`BQE4@sK6EJ2uC#ZbIG%9t~MHaYiH3^Y`ntZJeZi*EOAvOYqqw*o>A4vb{zKicU%vo2ZzIj^G~+Bz}9iK@vii( zDF8d^Hj%gXY3e0&e$<$^4+g96G1QwZW{tw%#3R~9=3EBSw&i9}nPZyK1fZVg4Z+YIV_yJ^;E6B*56faXkAAa(1y07{d%GWIss*!`?1BB?!2 z+x7d|!$B?v7oCyrrI(B+*ppDDP?RlpRx1CGXHyss36%P(Ml zLv_V0#1LBa-N^>?=q(Uh)9wg~w`v@Scpnx~EwIi)D+@p-z!6vZ1Q>X#Q`TZu4V9Lt zQ+K;09Z64w4<}ya0e`qh!C3kDfwV8;Qc^;#L;@z){7Q9>X5sh;brhXL1VFEPT~*hG z$@C-#J?-48>=i4fV_|e=%S#9I9`(JFHBsU6MLN0moG6yN7^-Z2b`gcb%=)#Vy%#|d zjYobA;1Db}BoUc!9L;?D=HF`=FODQd%R2PC2RBx-Q|;)U=d!Vbyt9~}RaV}YzD|R8 zrqd$d&w6B|kA#27l6tt;;hyq6iUibJb|TSNeSpjw`3V25{p%QRF1)5(kqL7!QQM)o p8UD=+vmFcj#32^xq)eFO8G<<8px-Z2%Wib0e$i^*8Hqw*jnS- literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an40.gif b/app/examples/Games/Concent/imagenes/an40.gif new file mode 100644 index 0000000000000000000000000000000000000000..b4ae4a6eedb8fc378965d2bb9b7b40ccfb2184a1 GIT binary patch literal 2582 zcmeH`=~q*C0)~J0-YhpHgd|*&Kmy!ISVYjsVuMD{4Qs%t$fD=~Cdd*f4M9OrDRapJ zxHeJ?u~Gq{6kDoLnYt9`+|-C@(b7UIwoE5F*s2FLb?7qf437OL=F>c%-Vg7m=bR@k zJyn^Yj|S1;2OD?^hr0>4FAS5)c_>qu81IdSh*=!D^d)?J6(*O4 z;DXg+wi5TomxXbcXK#{l;+J|ajS7p6h>$B%*T(u}FXhL@#i`=ttK*lJ$@$yjP`Ro> zrBaDgE3_)CAbv@zI@%Z~3{Gv|k?LiQLx~OQr1VW0T7`9`yf7_rNtQ#EVXI0JkIHHN z%YF4(!Odz>;U-m87M7EfQ>9g|Pe0$35v<9B9Nd`%@D3eP+tb2+<8L~x!$1Z)cm6-iOuj1i&Nl~ zjm-qDSbBGJQh-pvQ&KTawg5qNDQ3Va4S?i1HAmgf>J)l2jfJv!C`er5cLXsW zo%8VS-){8{&5z(WdrM+A8fV?)c?cY!zjmK*EW0Cnk1u}LuB`n%Zo?1uyk++37Gqc z>Ofq(b)s(J;n%yve<{6P+KdV#zDz7SmuIEv?yoxZ&!|`6%7>rz*X{WB%-!Pe8d7Qy z1Z-UVCG`B;PEMHC7{~24UzW~EJTVdaomUxt8|P+^h0uF9gcUxz^KGMO^yhCH?*86D@mHudSh+ank5_1^v4hN-;Q&s${Df7w!!g_(aN({}XOv{6oj%Z&%U1&_kD}U2 zXRAvNG-kf?AkZ}TaoStb$6ufH4v(lM#Mw{#zpX0S^4>dJ4>*Tgf(mp^Hs6E+ULr~Z z3bg#&EojP)BC@;n&vv{CWbb-`fAchQeE&L^$sp}KvGniVm zmNsPJuQ5?hj;rFmB>altOJ;C6<^as-^D-B0{sy0b|1vzmPcKmDDIdj5cZy$nj{YhX zIipME$9}G&15v|IZ~OWxplfv6_^`UQQbA-b0-?_7q7B^a*;z);u@PkZVV-NlvP*-2$YghPWB3oo$}6eP$d7-j|0~&SI!K#1n<#$Qci;?GT|$821s$7JAM_YOfGTo_qN(*6Hj<@v59a<19Z)c>rS z@Y^+T&l%7tmO+(gj>)J>K}`+RcdSC@WK<0CQ@P9;($VOSg$JBIdjSP4f761-$ zW)@uE^5SoYkj3;#Eo%oL6>|m#OH4A30843s4$*(6W_h{D;!r0eZ?tIOa+Y}@&rQ#L z`PCYdiBSrMBAjjCUu|QVbTHF?-HR>M9dCpgZeT2dsO?v*7qawVi2B6h^k$WKj@TJ40e0 zO!nPTw6S6z!kA+ffOWT7J#zB%<~Bq^0s6Cj`OADf0PO_aOb6kY;kK^D4Cpd9K<1|` zK@(+g7xt!N^CZldI+-zKFYLoJvv8cQ@u+!oLbD=sihFMHhWS;DX9+(Hykb$HECU%T zQ#o^!+njjG#8@8T1~i8g$?DGZp9k$y9{m3IJiy@SQOFS?njNASo2Ss+A&WYJpvheX zBj(``nrt3eqH=ndO;WVt?5omoHCyUU(4r>Ap)ShXI76ORgxl7-^(WW0s_D<$AU>|| z)6^oOk0&>T)t_Ir4^;R)*kkcm;9emglBYAttThvQSlp`i^2!|e@FKy`)Wd>g9gV!% ze~_a?@R1>A>?ot-qoqllfX5`;N}+j*tJ38Jd?|4S$JY}wZOprd7SG3XrYyL_w7Q)Y z-woND5|5dhx^iV4-NlC_n#Ds928(tAA$c=yE9`GTiZ5F51&S7K_@IEXgk%;xN)OlFL(XIYQ*Y2f6P?bfdlw|w&N!3zuTZeeY8TzxcI!<6*l^e1mY44#12QQ^)fl}(zB1XDe) zW*fgSx26p?uTetmZN4kbpV-3^l5zTcu3`T%(t3$YXz%{U$Hzd?1#2E#A5$$#jVij( hSDq9(KyVr;q~mlp=Xd|iqxHwn2K_D`N4!w5=D+CWtbYIi literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an5.gif b/app/examples/Games/Concent/imagenes/an5.gif new file mode 100644 index 0000000000000000000000000000000000000000..19e3dde6d1ab4f2edd8faa9981123d2e3a01773d GIT binary patch literal 1628 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 W3cWp-(!r9{`m&`5FpvNO(*FXC;qg@f literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an9.gif b/app/examples/Games/Concent/imagenes/an9.gif new file mode 100644 index 0000000000000000000000000000000000000000..89b2b7b390d5abd8c9026cb2bda5d50c9e77008b GIT binary patch literal 2486 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 A#Q*>R literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/colombia.gif b/app/examples/Games/Concent/imagenes/colombia.gif new file mode 100644 index 0000000000000000000000000000000000000000..60c8c145103debd0f90600660178e3ac373d962d GIT binary patch literal 145 zcmZ?wbhEHb6k_0J*v!E2|3AZj28J_djMLH>(imo@F)$b#GXSA60}udZKms5p3@H9& zVG#jJ=zs)3W-zeWP2e?NxwqJWdDnl1go4DIyv>dsBgj fju2l*L($0`HUZY^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<gTWM0TY@5u?V53ptdXHXalWy7)oG zIH{I3zSIJR&kGIVCkMJtH%#xTLhK zyrQzIxuvzOy`!^h(&Q;qr%j(RbJn88OO`HMzGCI7O`ErD-L`$l&RvHNA31vL_=%IJ zE?vHI_1g6tH*Yuqec!9r-=(U9^_Ou4*DRPRCJL`OvU7(>PL{* zz&<0+V@+iF4DK<6ziu(`Ff#%pk6Dnxp5f!+d=1yK-fhCYHy)eT`|(O|`ttbNqR;xX zJworIxFLIl4AAES}2JW<9m@->a7u+YgsnIlr7WZRU>5U2E(!nl$aNxfrb7 z^7X{w9+}BkWFD(cnVfy*@mx!lyWj4#du^Mj%X`D;2*2gJO^4+?6VKIVcU*p#8nbY+ z;N^MvJKkpk4CyDB?MvMy+4J0^o|tIgNZ58|$t2(D@yQu$E7Wz~tayBR#;m~e z>t!A-=yO`(AgMO1e{bD!l_x)=-j$TKs_g19KP9!B+kz!9PO|7+MlT7;qA5ilY!RaIdgx!J}xqG;~n#D322`aIA5T-W`%?|<&kbwBQ&ZdAMAFE9^S9}I?u zaYzUb3C%G@aL5R*DTzzQb1l(a3Yuq0;!)8&I-ci&=X)<4BYpshA4C!a8VP9Y1VN^P z5F~fA6@}47iB(d9Ac}|5HbCG}QcdG`*~Y4E>5#2A zXd9)lWwC-PRJK8CTMvb8q{^15v85|*BOu$rQCk{B4Ny`eRMapH)dQk3HB?3mou{D& z<}<`vs;i0?p{6l4v_Lg2WR%7jrPH)DZ>4LPmQGi=MnSGjPKZPtAlA|Yw663?I%A6N zF-7;Dbakx`5RH0MH6B4)Z(3AjwU$9sGeV}kJ+y&Td5~}_z?B;%&1Xp`gJ}9dkIA3_ z6_Y*1q-(>d+AvyvY(oR9LLcHW89@`VWs@wrCNijkEt9gP`bbx8EKMKjp^VCxa%3%R zshZ7|bEPFhxt{Bx3qd{l z-&LzLYR%|>WFd}fG@4P(s8*xaYDWJLt#(wS9o7CHlat!fN$unUrlzL!di|Pd1SSNg zhgriG@ZV0r(0Z62W9A_dJx)X~-4G;~6g6KaS?tL7xO<~D)%3S;mHcjTTRQpo6f@7N zlJ+c1|MH;rs((9kD2F@pJ@4J@&ZGV_qH4ca+H;-0d}igy$)9>}IAgx;>!?2ZC~-c2 z$C%~))qY94v;S%oZrV=^WCkD4`&M1@=wxuPq1X5B15Fjb%@#EM{@}!`y)1oMXUK!k zThmU@&QITB9IR{5&Af^K^RF8Zk6y#Vgx-5xZXW$9*;7Q@!$v$o5k4gKr|)Dx7w^&^ zrLl533Q6*!tp(c`1#@P6hPRjkXW8Brs|W0}W#_^%RexMOf86Y^AyxgctLF5^m@_|* z;7@ol8_%D<^8B`!UV5A*UG$-$9)SIl@!IOEHsf-@ca-DT&+zWzocYV_g{fWBUUB{i z3nMS;guPtNe!C{bcRhwAS?#PdlOJ$xSy+loK3w>-vnywEe4GI{YH(1TndnW*#vK&0 z^E|2f4G*#uP6V#yQzhGSNH`kOCRNPht>-74R#u)0}$U`KFe5#j;3b-lWdjWDsqeX^?9vCgMT z>LW8CUkmx}OMC2g%g>2RA&*Pdb#^wjn$40=e?T{8Hzs|=+B0bD6~{hXJ=uWpsl!vA zrBPdgWSiDbM{=y|h$IVNrxC3MgQkngTOuU{MCe*0q+3W;Cg)VLx@Eg!b@dZ5)_3S5 z+~O}CncLOAR_5><$9X1EU2dH#lKm%I#a}|JS}fydWuU9ch2NaVTp5$k04m&ctyk6$ z%JXX29O*{D!JkZ?`~6)q@htB|-u<1B3$v!59mJ}^KqPfnKS^>5t|Cz{Sgegaw+eVg z3eX?_d5GmpdPj%EJI~p#8rym9Zi8;@K_rhsu2ok^ew{`HVby?5mcQ0hT`)*6*JOQq zRjW?_r-Vo@J!g~lt~3#U*y$ZN%lDzM&}Y%x+UtceLA(HRgZ2T!sbLHJ>qNWO*r+EK zdE?~Do=Js;Bi~8!_d%a8Pk@67C0g%CT@%LnnRE0vA-)dat*l2I9ogu(W&)d1#mk)M z`u{MQblQA{hQb`AJ^9|?%kW*bcpZ77%e_%bYg$a=KsGxJopGwgR?{$7WPwgbIPiIy zF^Bmc@hqL_raj;O+Zx2KDd3cf%%N11{oX8U_JhXB+Qc>Fnj9L-+xLhz5bL6!GE6nO zq+QIWUvdsIZN?7KkGMtLK-^xRx#SR(vb&-67YTRK${VA&Kn0y<{`e0(_vz{fmKFwX zQI?xeXCxiBw!pQXLToA5B>?`#Caa#M?@$_&AWPwUvjpI;Fj?Ae>&KNEu7|a`^N}+V zA)78# z$*zzVInO-C?hO?ouES7{+Uzuz$!e@&#K z)NbRi-tmIB1&d$FObU}h&eJSgKE!tYDR-x_x8mC3MQyHjd~TF$WO1oV?7DlpH@RGN zWX`h8)i)g6#GU-tO!znLKzPrkK={qI1ZSsJS4junO#ZvZa|(H#WF+4I^b7d9cjefqDDmw>XDV#wMs^>Wd6Bz#5*
z$%87l%eGfZ@vNlI)Y#ZV(zViqt20u0u?Ume?03l1N%b8ch&lHzS5>l{2QPh$*^Our z7Mkq#brmYmER8Xn(T{dLuesIsHUhEsiy8lZP^?tHK4 zb%$hQ8uvJq*{emf59)~Hi}o0s1)E<@Bod!$(ckKC*_F91FoLnNqeba(j~Q$-oTs4!1dYu+lG^y;?IXt@F9c z`f`;QV%~8$#^vm3%c^a^|C|Li8#=oI4c?)vZ^>%l?O*ukVS{l~7W!oeYIIJOX22GG zROj<7RKeKjVVdvBwt&Yi7y}25wk8q$*@Hvt=!LrsN>?YiMMfGrX#RA|1^w0!|BEX4 z7` zfk!@7evC!O1ADv%x$)cjDH#bdefCO|nr%x)B0d{24_Qr)Ke#u$|D@MaGtB#!laH4= zZb~R+4hm08A9!8CZ>Rov1GuNeV+y(Fj5ub*>#cvj8D??)#cH-bbi?|;WT&s924yw} zo$W&nCxTqIzu(B(`gAw*Xuib{mE+%2&1X-1i5`o6V`$Z8&6i9R=e%p8UZ>g#&HwRh zOVn^5Z||q-5-QI*DCeNMzKnS6oky^j!P{nW`M1BBRQQb^(A!~#htAaY+#35ME^siz z+%S#>H}(jt@1KQqar{JL`1Prz7f+L)w7@@eF@44?W$Crc z&Uvp!Q^96vtmyON=H|kfqLZDI(Qy|v)4v$T@)MVOP#oU}y~ zO@+I0;|Krd{3eR&lHq$K9Gxi1Z7P1^G>1k`*uUh`iLk^u3GjhMd~Si4K{1!Bu;Fqz z<^eIBdr2>gbN9fkUk1c+O)rQ2{Dh6;DY;TTo3KZRPg8Qc_&^~aA><~bm8fgVWKTL~ zmH{Z~7#l{)ZxqBX4ba3$v7x|)VJD`^$(>BhO>S~fRC0mXKtV~?$x?2j@!a8LPuV48 zFOaF>wo9-VWXVD*yg`On%EI*$Vv`VIXbLomaja8B0hC+}MKz(hgGE0l=E&yngHsgaFuLFTkA_Mblb~;$r zcXZ}zcl4A#EDLy>G0C-}?McJz0*w398YwQ*6oJ44V@rqc0tL+sqggs;r(L%HwBYh) z>@E_*mJHtv;TyI1X$WZMXYXszZeSCRJifA%gos#$++s#f7X#R|Jio~>geRbU=UZ>5Kcp}ODzP>-g4ixx z^EmB_5_dBh_k^FT1i815!cD{BbY}KfCT@9okslu!OahE&(zc4=bUtzqpJ2S9sD^!G z4;e{k!UcRhFARU1UqmO}2&WL2jbPT4-`GP(19qD>m&2EDz$w{;_mqOcwIwdwg@yw; zpt9tTyTo6y2#=Ja*63m-h4|Y{QJtLoijHwEzoGPWxefbw!`>3#j+>a7n~V-oc6DyL zG!wZL7}sEYwFoC|X$_P3M+V{IOljNk#Iyh^s>JzFp)B_3my z2{#~J>FB3yoIa=bp1aMio z#N{J*XI*X&qr4D;J4z4-Sjh1X?pqP+u=c(e^Zu6Hv^f!$dF=i(5ciSma9C3L*RlJ7 zgEhNZ$l%!;d}oPQ5%vJ%ful@9+(y`NxjP%Oe1gOVOu=E>QbDrPP?m6Ln#R+HPaE*+~){RE#+DSS^S9+N%z>pbQyh1kJFCC=W*uK;e@8}x>g;_K^@&P&(t z%1x8w=SYCZQDQj*A;TeqDEKxZK23r+0vViO7dICqK3{}$fJW?H51j*iDbYU zWT_3$_8IKeOB#3c2~x`aBexokh#KE^a+%Y>2MGOa<)e;Igb6=`kM)SbL8esmVPIk@)G2~xe$EtHvc zc63g7z7lvI&yiP^}NoG2&6xYfV@|K zW_gRix}(kv^;9MIXW~A6?0n=chwF$X+Rl3Gr|Z{LN=0eYFl?yy>1Q*1s-?I_YoPt{ z)FOm99^2DLAw^o@zW*@zM}j&}K^T@TkZ+L-MG zHL;c@;B&Kr-i^9yfe4yTBgDZKX+mX_PSWxWX8kBWv+ z2^4hX)QdCZ-o^0>uPZMS*~C^c{#dsn*an?IR}8xJMcJV5kP6en@WcuuoA6E%3w3DK zi)d2cIZEH*x#kP2o{dW|7g@@{0%g+9zFIA@hb^ey+aLJ~U%ZPDy=vg>?IqG-X0fmy;};RVvmxmT~dU)k(x>VmMT6bxwy-7U%e z1fs9NhO@}nuDuWMu!plibiKpS3lM#Y@n-!vx?NO2GRLGc(RpA?^}z7Phv-f7qC-M_ z73J+^=9@}3!O;{jCk_{nh3Z64$D??S3_=?7&1Dds!5+?F4p(V$?v+m;f%s+P!&f9| zdm!T^7x1Kitc+Ll_VV3fwA5tqZxe`qp)No-r z!12*^v+dKPb1z^Be@pMl7SXx`(7yo^{-i!C1K$n7jIvcjX>LFT8 z)4O6ZMJMF={M?Cp0q~wL1!g8sNDDX5eIA!Sr&J~S58#*whUoo<{SUtqyh)L@UmfmD zL>^2=E&6uf=T@$TF6!j>^Vh%66P_5(WSa`4`+Q#+_X5mKx`tKnfm2_ZG{K)&NRa1u z$I9u0`BIPCNzY$rN)FE4jGGZ(pDBMdQ}KGHQa>YEJzM28TYYf0CT>=GeYWn=Y{To> Mhx%D#LlVsXe|2a0c>n+a literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/logo.png b/app/examples/Games/Concent/imagenes/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..27a8611ce32d3d795ebfe834f506adbad7c8c71c GIT binary patch literal 1591 zcmV-72FUq|P)NPN5d`x^~JY+m9R6Hz9JS;>k6m)cSY;;@{92A6vguKMW+}zys^z`iP z?1Y45YNsyyVQ3 zlw?F~OdR~=-0ak}#FT9O?A+AEto-D>q@<+G#N2c|JnX!*%(T?})TI2>v{YPF%*@Ob zP}2PX0004WQchC32z1d<*{0UbC%mF18PMNzEH zNUSkk(xzs?Km%%0+G+d$f5E&YCYp4aPjk)VWj^3@pLabZllebHQCVsw_hfZ-HJ8h+ z{+oLxyGAdISzL85!YjfQq8|%OJudh5f9hlXW`Wf+b9h6&P=*LPC=7+3)^erRn zYv>UHPxDN6Bb&|MIM5?vb%TBk{87klY&<@>$Z~vX9)o#MM!EcS<2z^M8fcs(F9;=8fk#CBk~p1thFRb zYTKr=DG{*4r)e9InNF=Ng($G>YApiypXlMzj}garNWRGE-V3s0^_>801Zmj;lSsCIN?@2&*KtZE-!Vd zz~o(uq8!(C;72&q^E#d=&PQ(0d5jr)sY~T@89i~`V$pT>9nAA3B=-9+y&?eFTgVs; zjKV#TXBZ}*r;76f=bMOLzC;JZ!&k3C$ao{^_>L$eyuZKi@B{Sr=;-M1H7uXeXb6C# zckhpfpw~36*L4Zs)jV*ZGRs2ryZ4|EG`$IXAuHYjC{S;T!bn870CI#ALGV2K4iI#G zdqkQVLT^vcj)xy!9n$okVT?}h04$_^u>eSQM04Oj_x&9Uq5G?`=TM{G63}zflpt&P z!)UrR8earqFP%ocUWaWNk&l;`;(QW_f(l@y(0I(UT1PeOqX-mFVqa-YNEn839K`C$ zAfLG_0t3Jcn%S+{nl>8iqwzF~f>{)k-7p>p(Nz=#vrg}R*2r-zo>i|ueHyl__1P$q z&Zkj8cEXU5cou&Su7YS9n+bEjOi+PSZ5tVOsH9JYFlaG@LTsuO-IP~Lb;OmQ&GRSZ+)>PFz>(k+`MZtFrG#!|IKn}n6DffeU;|ZMl3%DY^fU~jF`1YNei$Gv+~WHOee=19 zG4546nx3w;A3*xs`t!v5!Jqcl;rh^|_Nm>qCCP?M5x|l+Wm8MspZI}57_(;AK0iA< zp3M%wU&Jw#EvsBo$oyP+cmd+{oov=1#0;XyI@qbqFI8sff#F*4KZxt>fncjHU6lE) p;HAc(QmI%h8t`=skGuKb>mNB=@4jy!9+dz9002ovPDHLkV1kqJ{~Q1S literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/ok.gif b/app/examples/Games/Concent/imagenes/ok.gif new file mode 100644 index 0000000000000000000000000000000000000000..e3529cad3c9b6fa67f348da6830e742b375bcc2f GIT binary patch literal 1355 zcmW+#e{9o56u))_3u$%GvK8F08&%3SlC3Ob3>J$UGUgy93{nY%!N`QTkP7`{B#qAT z1vXe_jm1o}j3yNFHGCx(ILU=v%;9c$;1RyS7yU|*3^Q>8CK7*5!w(b!HGkxgpYOh!V^&Hy}a{ zOiW4yBP=T*3WvgCL~v|^O^qidW;*AB5Q9^~Xxi2=6PyUb7^9G_I8j-ZlY|N)hRh+t zB;~pxNKR!r<&r>8Dk$YtFlus!p)RL%L4ktgR7xVT0A|ECB%_uf7Gs=K#h{KEh8o2G z7js6j%rPbaOc7y7OqoeJ+c+aG^jJIDpNiV32Py5F(%@ zm+;JiFgTC_)j*sPrAsEUnT7d*Jjs*}3IKx($xPkONG@~>n+%v@i<*|xGx z^Om$Fx5rG&RMt3ofJ^>yO`DC}r~hM$)QH|d`SyS zJ>I>)W9Fv?s|;;k-n45AHus0?&$a%1L$A@k+hgJ=?DxXPqy7@s)^oA0`L`19+{!NOeBr0gJ3a2ASP*%ijB#&4y#J$tm-OnYmYVkQ zN@w0&#`k$<*G3)Azl5);V9f9MApZxm=TPBDYTJdSQ!-=Su1KpHbx#Y$_IW3lE%Gio z!rvJU6Sem8jIaGySD+VLV(+^`$!PTO-=3=@HMb|zFBJ9Yk>*)Pp>4 Rcd07A;J*K$*x^7+{{v}U)Eoc+ literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/tierra.gif b/app/examples/Games/Concent/imagenes/tierra.gif new file mode 100644 index 0000000000000000000000000000000000000000..97532a23d4a9b28de6114076ad7b42b2731dfa7b GIT binary patch literal 32485 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/move.wav b/app/examples/Games/Concent/move.wav new file mode 100644 index 0000000000000000000000000000000000000000..d6d5436d3e7ca871ab44631250cb682ec572daa4 GIT binary patch literal 13318 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/DeepSpace/.lang/ca.po b/app/examples/Games/DeepSpace/.lang/ca.po new file mode 100644 index 00000000..2f860fbc --- /dev/null +++ b/app/examples/Games/DeepSpace/.lang/ca.po @@ -0,0 +1,51 @@ +# Catalan translation of DeepSpace +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the DeepSpace package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: DeepSpace\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:37+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:109 +msgid "&About DeepSpace ..." +msgstr "&Quant al DeepSpace ..." + +#: FAbout.class:31 +msgid "About DeepSpace" +msgstr "Quant al DeepSpace" + +#: .project:1 +msgid "DeepSpace" +msgstr "-" + +#: FMain.class:102 +msgid "&DeepSpace" +msgstr "-" + +#: FMain.class:97 +msgid "DeepSpace Vector Engine" +msgstr "Motor vectorial del DeepSpace" + +#: FMain.class:117 +msgid "&Exit" +msgstr "&Surt" + +#: FAbout.class:41 +msgid "&OK" +msgstr "&D'acord" + +#: FMain.class:105 +msgid "&Show object labels" +msgstr "&Mostra les etiquetes dels objectes" + diff --git a/app/examples/Games/DeepSpace/.lang/cs.po b/app/examples/Games/DeepSpace/.lang/cs.po new file mode 100644 index 00000000..01c0830e --- /dev/null +++ b/app/examples/Games/DeepSpace/.lang/cs.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DeepSpace" +msgstr "-" + +#: FAbout.form:10 +msgid "About DeepSpace" +msgstr "O DeepSpace" + +#: FAbout.form:20 +msgid "&OK" +msgstr "-" + +#: FMain.form:14 +msgid "DeepSpace Vector Engine" +msgstr "DeepSpace vektor motorů" + +#: FMain.form:19 +msgid "&DeepSpace" +msgstr "-" + +#: FMain.form:22 +msgid "&Show object labels" +msgstr "&Zobrazit Å¡títky objektu" + +#: FMain.form:26 +msgid "&About DeepSpace ..." +msgstr "&O DeepSpace ..." + +#: FMain.form:33 +msgid "&Exit" +msgstr "&Ukončit" diff --git a/app/examples/Games/DeepSpace/.lang/de.po b/app/examples/Games/DeepSpace/.lang/de.po new file mode 100644 index 00000000..e45c2dd3 --- /dev/null +++ b/app/examples/Games/DeepSpace/.lang/de.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DeepSpace" +msgstr "-" + +#: FAbout.form:10 +msgid "About DeepSpace" +msgstr "Über DeepSpace" + +#: FAbout.form:20 +msgid "&OK" +msgstr "-" + +#: FMain.form:14 +msgid "DeepSpace Vector Engine" +msgstr "-" + +#: FMain.form:19 +msgid "&DeepSpace" +msgstr "-" + +#: FMain.form:22 +msgid "&Show object labels" +msgstr "&Zeige Objektbeschriftungen" + +#: FMain.form:26 +msgid "&About DeepSpace ..." +msgstr "&Über DeepSpace ..." + +#: FMain.form:33 +msgid "&Exit" +msgstr "&Beenden" diff --git a/app/examples/Games/DeepSpace/.lang/es.po b/app/examples/Games/DeepSpace/.lang/es.po new file mode 100644 index 00000000..044514f2 --- /dev/null +++ b/app/examples/Games/DeepSpace/.lang/es.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.class:111 +msgid "&About DeepSpace ..." +msgstr "&Acerca de DeepSpace ..." + +#: FAbout.class:33 +msgid "About DeepSpace" +msgstr "Acerca de DeepSpace ..." + +#: .project:1 +msgid "DeepSpace" +msgstr "DeepSpace" + +#: FMain.class:104 +msgid "&DeepSpace" +msgstr "&DeepSpace" + +#: FMain.class:99 +msgid "DeepSpace Vector Engine" +msgstr "DeepSpace Vector Engine" + +#: FMain.class:119 +msgid "&Exit" +msgstr "&Salir" + +#: FAbout.class:43 +msgid "&OK" +msgstr "&OK" + +#: FMain.class:107 +msgid "&Show object labels" +msgstr "&Mostrar etiquetas de los objetos" + diff --git a/app/examples/Games/DeepSpace/.lang/ru.po b/app/examples/Games/DeepSpace/.lang/ru.po new file mode 100644 index 00000000..92c9997e --- /dev/null +++ b/app/examples/Games/DeepSpace/.lang/ru.po @@ -0,0 +1,78 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/DeepSpace/.project:18 +msgid "DeepSpace" +msgstr "Дальний космос" + +#: app/examples/Games/DeepSpace/.project:19 app/examples/Games/DeepSpace/.src/FMain.form:5 +msgid "DeepSpace Vector Engine" +msgstr "Векторный двигатель дальнего космоса" + +#: app/examples/Games/DeepSpace/.src/FAbout.class:16 +msgid "DeepSpace  " +msgstr "Дальний космос  " + +#: app/examples/Games/DeepSpace/.src/FAbout.class:16 +msgid "
A vector engine written in Gambas.

Contact subjugator@gmail.com with questions.
Copyright (C) 2004, Michael Isaac. All rights reserved." +msgstr "

Векторный движок, написанный на Gambas.

Напишите на subjugator@gmail.com вопросы.
Copyright (C) 2004, Майкл Исаак. Все права защищены." + +#: app/examples/Games/DeepSpace/.src/FAbout.form:5 +msgid "About DeepSpace" +msgstr "О дальнем космосе" + +#: app/examples/Games/DeepSpace/.src/FAbout.form:13 +msgid "&OK" +msgstr "ОК" + +#: app/examples/Games/DeepSpace/.src/FMain.form:9 +msgid "&DeepSpace" +msgstr "Дальний космос" + +#: app/examples/Games/DeepSpace/.src/FMain.form:11 +msgid "&Show object labels" +msgstr "Показать метки объекта" + +#: app/examples/Games/DeepSpace/.src/FMain.form:14 +msgid "&About DeepSpace ..." +msgstr "О дальнем космосе..." + +#: app/examples/Games/DeepSpace/.src/FMain.form:19 +msgid "&Exit" +msgstr "Выход" + +#: app/examples/Games/DeepSpace/.src/MMain.module:328 +msgid "Object Count:" +msgstr "Кол-во объектов:" + +#: app/examples/Games/DeepSpace/.src/MMain.module:329 +msgid "Bullet Count:" +msgstr "Кол-во пуль:" + +#: app/examples/Games/DeepSpace/.src/MMain.module:330 +msgid "FPS" +msgstr "кадр/с" + diff --git a/app/examples/Games/DeepSpace/.project b/app/examples/Games/DeepSpace/.project new file mode 100644 index 00000000..cd39d095 --- /dev/null +++ b/app/examples/Games/DeepSpace/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Title=DeepSpace +Description="DeepSpace Vector Engine" +Startup=MMain +Icon=images/deepspace.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=4 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Games/DeepSpace/.src/CBullet.class b/app/examples/Games/DeepSpace/.src/CBullet.class new file mode 100644 index 00000000..bdf6bdda --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/CBullet.class @@ -0,0 +1,11 @@ +' Gambas class file +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public X As Float 'Center X +Public Y As Float 'Center Y +Public Direction As Float +Public Owner As String 'The array space of the owning object +Public Damage As Integer +'PUBLIC Destroy AS Boolean diff --git a/app/examples/Games/DeepSpace/.src/CObject.class b/app/examples/Games/DeepSpace/.src/CObject.class new file mode 100644 index 00000000..21f7f12a --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/CObject.class @@ -0,0 +1,87 @@ +' Gambas class file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public ID As String + +Public X As Float 'Center X +Public Y As Float 'Center Y + +Public MX As Float 'Motion X +Public MY As Float 'Motion Y + +Public Size As Float + +Public Points As Integer[] +Public Distance As Float[] +Public Degree As Float[] + +Public Direction As Float +Public Agility As Float +Public Acceleration As Float +Public Torque As Float + +Public Hull As Integer +Public Shield As Integer + +Public ShieldOn As Boolean + +Public Thrust As Boolean +Public Attack As Boolean +Public TurnRight As Boolean +Public TurnLeft As Boolean + +Public Sub _new() + Points = New Integer[] + Distance = New Float[] + Degree = New Float[] +End + +Public Sub Load2DObject(sFilename As String, sID As String, X As Integer, Y As Integer) + 'DIM F AS File + Dim I As Integer + Dim sData As String + + Dim aLine As New String[] + + sData = File.Load(Application.Path &/ "object.data/" &/ sFilename) + 'OPEN Application.Path &/ "object.data/" &/ sFilename FOR READ AS #F + 'READ #F, sData, Lof(F) + + 'Split this into an array and remove the CR character + aLine = Split(Replace(sData, Chr$(13), Null), "\n") + + With Me + .X = X + .Y = Y + .ID = sID + + If sID Like "Object*" Then + .Torque = -1 + '.Attack = TRUE + .MX = Rnd(-4, +5) + .MY = Rnd(-4, +5) + End If + + .Agility = 5 + .Acceleration = 0.75 + .Hull = 100 + + .Direction = Rad(180) + + For I = 0 To aLine.Count - 1 + If (Not (Left$(aLine[I], 1) = "'")) And (Not (aLine[I] = "")) Then + .Degree.Add(CFloat(Split(aLine[I], ",")[1])) + .Distance.Add(CFloat(Split(aLine[I], ",")[0])) + + If .Distance[.Distance.Count - 1] > .Size Then + .Size = .Distance[.Distance.Count - 1] + End If + End If + Next + End With + + 'CLOSE #F +End diff --git a/app/examples/Games/DeepSpace/.src/FAbout.class b/app/examples/Games/DeepSpace/.src/FAbout.class new file mode 100644 index 00000000..f59117a0 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FAbout.class @@ -0,0 +1,19 @@ +' Gambas class file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public Sub cmdOK_Click() + Me.Close() +End + +Public Sub Form_Open() + + Me.Center + + lblInfo.Text = + ("DeepSpace  ") & Application.Version & ("
" + "A vector engine written in Gambas.

Contact subjugator@gmail.com with questions.
" + "Copyright (C) 2004, Michael Isaac. All rights reserved.") +End diff --git a/app/examples/Games/DeepSpace/.src/FAbout.form b/app/examples/Games/DeepSpace/.src/FAbout.form new file mode 100644 index 00000000..5d9934a7 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FAbout.form @@ -0,0 +1,19 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(51.6667,32.1667,54,26) + Text = ("About DeepSpace") + Resizable = False + { lblInfo TextLabel + MoveScaled(1,1,35,19) + Text = ("") + } + { cmdOK Button + MoveScaled(19,21,13,4) + Text = ("&OK") + } + { pbIcon PictureBox + MoveScaled(36,1,17,16) + Picture = Picture["images/deepspace.png"] + } +} diff --git a/app/examples/Games/DeepSpace/.src/FMain.class b/app/examples/Games/DeepSpace/.src/FMain.class new file mode 100644 index 00000000..f685e24a --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FMain.class @@ -0,0 +1,81 @@ +' Gambas class file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Private Attacking As Boolean + +Public Sub _new() + mMain.Canvas = daCanvas +End + +Public Sub tmrMainLoop_Timer() + mMain.MainLoop() +End + +Public Sub Form_KeyPress() + + Select Case Key.Code + Case Key.Left + MMain.Obj[0].TurnLeft = True + Case Key.Right + MMain.Obj[0].TurnRight = True + Case Key.Up + MMain.Obj[0].Thrust = True + Case Key["F"] + MMain.Obj[0].Attack = True + Case Key["D"] + MMain.Obj[0].MX = 0 + MMain.Obj[0].MY = 0 + End Select +End + +Public Sub Form_KeyRelease() + + Select Case Key.Code + Case Key.Left + MMain.Obj[0].TurnLeft = False + Case Key.Right + MMain.Obj[0].TurnRight = False + Case Key.Up + MMain.Obj[0].Thrust = False + Case Key["F"] + MMain.Obj[0].Attack = False + End Select +End + +Public Sub Form_Close() + mMain.Exit() +End + +Public Sub Form_Resize() + mMain.SCREEN_WIDTH = Me.ClientW + mMain.SCREEN_HEIGHT = Me.ClientH + + daCanvas.H = Me.ClientH + daCanvas.W = Me.ClientW +End + +Public Sub daCanvas_KeyPress() + Form_KeyPress() +End + +Public Sub daCanvas_KeyRelease() + Form_KeyRelease() +End + +Public Sub mnuMainExit_Click() + Me.Delete +End + +Public Sub mnuMainAbout_Click() + FAbout.Show() +End + +Public Sub mnuShowText_Click() + + mnuShowText.Checked = Not mnuShowText.Checked + MMain.ShowObjectLabel = mnuShowText.Checked + +End diff --git a/app/examples/Games/DeepSpace/.src/FMain.form b/app/examples/Games/DeepSpace/.src/FMain.form new file mode 100644 index 00000000..761e7034 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FMain.form @@ -0,0 +1,35 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(19,0,75.2857,70.7143) + Text = ("DeepSpace Vector Engine") + Icon = Picture["images/deepspace.png"] + Resizable = False + { mnuMain Menu + Text = ("&DeepSpace") + { mnuShowText Menu + Text = ("&Show object labels") + } + { mnuMainAbout Menu + Text = ("&About DeepSpace ...") + } + { mnuMainSep1 Menu + } + { mnuMainExit Menu + Text = ("&Exit") + Shortcut = "Ctrl+Q" + } + } + { daCanvas DrawingArea + MoveScaled(0,0,54,50) + Font = Font["Monospace"] + Background = &H000000& + Cached = True + Focus = True + } + { tmrMainLoop #Timer + #MoveScaled(65,43) + Enabled = True + Delay = 20 + } +} diff --git a/app/examples/Games/DeepSpace/.src/MMain.module b/app/examples/Games/DeepSpace/.src/MMain.module new file mode 100644 index 00000000..90f5f504 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/MMain.module @@ -0,0 +1,348 @@ +' Gambas module file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public Obj As Object[] +Public Bullet As Object[] + +Public Canvas As DrawingArea + +Public BULLET_SIZE As Float +Public BULLET_SPEED As Float + +Public KEY_LEFT As Boolean +Public KEY_RIGHT As Boolean +Public KEY_UP As Boolean +Public KEY_DOWN As Boolean + +Public KEY_FIRE As Boolean +Public KEY_FIRESTATE As Boolean + +Public SCREEN_WIDTH As Integer +Public SCREEN_HEIGHT As Integer + +Public BOT_SPACE As Integer 'Which Obj[] is the bot + +Public FPS_TIME As Float +Public FPS_COUNTER As Integer +Public FPS_COUNT As Integer + +Public ShowObjectLabel As Boolean + +Public Sub Main() + + System.Profile = False + MMath.InitializeSineTable() + ShowObjectLabel = False + + Obj = New Object[] + Bullet = New Object[] + + FPS_TIME = Timer + + BULLET_SIZE = 7 + BULLET_SPEED = 15 + + SCREEN_WIDTH = FMain.ClientW + SCREEN_HEIGHT = FMain.ClientH + + LoadObjectList() + + FMain.Show() +End + +Public Sub Exit() + Obj = Null + Bullet = Null + Canvas = Null +End + +Sub LoadObjectList() + Dim I As Integer + Dim J As Integer + Dim sData As String + Dim tmpO As CObject + + 'DIM F AS File + + Dim V As New String[] + Dim aLine As New String[] + + sData = File.Load(Application.Path &/ "object.data/main.lst") + + 'OPEN Application.Path &/ "object.data/main.lst" FOR READ AS #F + 'READ #F, sData, Lof(F) + + 'Split this into an array and remove the CR character + aLine = Split(Replace(sData, Chr$(13), Null), "\n") + + For I = 0 To aLine.Count - 1 + If (Not (Left$(aLine[I], 1) = "'")) And (Not (aLine[I] = "")) Then + V = Split(aLine[I], ",") + If V.Count = 4 Then + For j = 1 To If(v[1] = "Object1", 100, 1) + tmpO = New CObject + tmpO.Load2DObject(V[0], V[1], CInt(V[2]), CInt(V[3])) + Obj.Add(tmpO) + Next + End If + End If + Next + + 'CLOSE #F +End + +Sub ApplyPhysics() + Dim I As Integer + Dim U As Integer + Dim Ob As CObject + + If Obj = Null Then Return + + For Each Ob In Obj + With Ob + .Direction = .Direction + Rad(.Torque) + + If .TurnLeft = True Then .Direction = .Direction + Rad(.Agility) + If .TurnRight = True Then .Direction = .Direction - Rad(.Agility) + + If .Direction > Rad(360) Then .Direction = Rad(0) + If .Direction < Rad(0) Then .Direction = Rad(360) + + If .Thrust = True Then + .MX = .MX + ((Sin(.Direction)) * .Acceleration) + .MY = .MY + ((Cos(.Direction)) * .Acceleration) + End If + + If (.MX <> 0) Or (.MY <> 0) Then + .X = .X + (.MX / 20) + .Y = .Y + (.MY / 20) + End If + + If .Attack Then + AddBullet(CInt(.X), CInt(.Y), .Direction, .ID, 10) + End If + End With + Next +End + +Function IsObjectCollision(O1 As CObject, O2 As CObject) As Boolean + If GetDistance(O1.X, O1.Y, O2.X, O2.Y) <= O2.Size Then + Return True + End If +End Function + +Function IsBulletCollision(O As CObject, B As CBullet) As Boolean + If GetDistance(O.X, O.Y, B.X, B.Y) <= O.Size Then + Return True + End If +End Function + +Sub CollisionHandler() + + Dim I As Integer + Dim J As Integer + Dim hBullet As CBullet + Dim hObj As CObject + + If Obj = Null Then Return + If Bullet = Null Then Return + + For I = 0 To Obj.Count - 1 + For J = 0 To Bullet.Count - 1 + + If J > Bullet.Count - 1 Then Break + If I > Obj.Count - 1 Then Break + + hObj = Obj[I] + hBullet = Bullet[J] + + If Not (hBullet.Owner = hObj.ID) Then + If IsBulletCollision(hObj, hBullet) Then + + hObj.Hull = hObj.Hull - hBullet.Damage + Bullet.Remove(J) + + If hObj.Hull <= 0 Then + If I = 0 Then + 'Message("Player was just killed.") + Else + Obj.Remove(I) + End If + End If + End If + End If + Next + Next + +' FOR J = 0 TO (Obj.Count - 1) +' FOR I = 0 TO (Obj.Count - 1) +' IF I <> J THEN +' IF IsObjectCollision(Obj[J], Obj[I]) THEN +' 'This is where Objects collide, and we need +' 'calculate forces here. +' END IF +' END IF +' NEXT +' NEXT +End + +Public Sub AddBullet(CX As Integer, CY As Integer, D As Float, OwnMe As String, Dmg As Integer) + Dim B As New CBullet + + With B + .X = CX + .Y = CY + .Direction = D + .Owner = OwnMe + .Damage = Dmg + End With + + Bullet.Add(B) +End + +Sub MoveBullets() + Dim I As Integer + + If Bullet = Null Then Return + + For I = 0 To Bullet.Count - 1 + If I > Bullet.Count - 1 Then Break + + With Bullet[I] + .X = .X + ((Sin(.Direction)) * BULLET_SPEED) + .Y = .Y + ((Cos(.Direction)) * BULLET_SPEED) + End With + + If ((Bullet[I].X > SCREEN_WIDTH) Or (Bullet[I].X < 0)) Or ((Bullet[I].Y > SCREEN_HEIGHT) Or (Bullet[I].Y < 0)) Then + Bullet.Remove(I) + End If + Next +End + +Sub RenderBullets() + Dim B As CBullet + + Draw.Foreground = Color.Cyan + Draw.FillColor = Color.Blue + Draw.FillStyle = 1 + + Randomize + + For Each B In Bullet + Draw.Ellipse(B.X, B.Y, BULLET_SIZE, BULLET_SIZE) + Next +End + +Public Sub RenderObjects() + Dim I, J As Integer + Dim Ob As CObject + Dim Size As Integer + Dim aPoint As Integer[] + Dim A, D As Float + Dim Tag As String + + Draw.Foreground = Color.Cyan + For Each Ob In Obj + With Ob + Size = .Degree.Count + + aPoint = .Points + + aPoint.Resize(Size * 2) + + J = 0 + For I = 0 To Size - 1 + A = .Direction + Rad(.Degree[I]) + D = .Distance[I] + aPoint[J] = .X + (Sin(A) * D) + Inc J + aPoint[J] = .Y + (Cos(A) * D) + Inc J + Next + + Draw.Polygon(aPoint) + End With + Next + + If ShowObjectLabel Then + Draw.Foreground = Color.Green + For Each Ob In Obj + With Ob + Tag = .ID & " " & .Hull & "%" + Draw.Text(Tag, .X - (Draw.TextWidth(Tag) / 2), .Y - (35)) + End With + Next + Endif +End + +Sub CheckObjectWarp() + Dim Ob As CObject + + For Each Ob In Obj + With Ob + If .Y > SCREEN_HEIGHT Then .Y = 0 + If .Y < 0 Then .Y = SCREEN_HEIGHT + + If .X > SCREEN_WIDTH Then .X = 0 + If .X < 0 Then .X = SCREEN_WIDTH + End With + Next +End + +Public Sub MainLoop() + + Dim eTime As Float + + ApplyPhysics() + CollisionHandler() + + MoveBullets() + + If Canvas = Null Then Return + + CheckObjectWarp() + + 'Canvas.Clear() + + Draw.Begin(Canvas) + Draw.FillRect(0, 0, Draw.W, Draw.H, Draw.Background) + + eTime = Timer + If (eTime - FPS_TIME) > 1 Then + FPS_TIME = eTime + FPS_COUNT = FPS_COUNTER + FPS_COUNTER = 0 + Else + Inc FPS_COUNTER + Endif + + RenderObjects() + RenderBullets() + + Draw.Foreground = Color.Red + + Draw.Font.Bold = True + Draw.Text(("Object Count:") & " " & Obj.Count, 1, 0) + Draw.Text(("Bullet Count:") & " " & Bullet.Count, 1, Draw.Font.Height * 1) + Draw.Text(("FPS") & " : " & FPS_COUNT, 1, Draw.Font.Height * 2) + + Draw.End() +End + +Function GetDistance(X1 As Float, Y1 As Float, X2 As Float, Y2 As Float) As Float + 'DIM A AS Integer + 'DIM B AS Integer + + 'A = (X1 - X2) + 'B = (Y1 - Y2) + + 'I'm not sure about gambas, but in VB the ^ operator + 'makes calculations we dont really need. This might speed up + 'the process. And we need as much speed as we can get. + + 'RETURN Sqr((A * A) + (B * B)) + Return Hyp(X1 - X2, Y1 - Y2) +End diff --git a/app/examples/Games/DeepSpace/.src/MMath.module b/app/examples/Games/DeepSpace/.src/MMath.module new file mode 100644 index 00000000..32348369 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/MMath.module @@ -0,0 +1,64 @@ +' Gambas module file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +'PUBLIC CONST Radian AS Float = 0.01745329252 + +Public SineArray As New Float[] +Public CoSineArray As New Float[] + +Public Sub InitializeSineTable() + Dim I As Integer + + 'We create the cosine/sine array so we dont have to call + 'a function which had to calculate a value everytime we call it. + 'These calls are made once here, and then from now we can just + 'point at the value we want. Should be much faster this way. + + For I = 0 To 359 + SineArray.Add(Sin(I * (Pi / 180))) + CoSineArray.Add(Cos(I * (Pi / 180))) + Next +End + + +Function GetDistance(X1 As Float, Y1 As Float, X2 As Float, Y2 As Float) As Float + 'RETURN Sqr((X1 - X2) ^ 2 + (Y1 - Y2) ^ 2) + Return Hyp(X1 - X2, Y1 - Y2) +End + + +' FUNCTION CosE(A AS Float) AS Float +' DIM Angle AS Integer +' +' 'Convert our radian angle to a degree value +' Angle = CInt(A * 180 / Pi) +' +' 'Ensure we're between 0 and 359 +' DO WHILE ((Angle < 0) OR (Angle > 359)) +' IF Angle > 359 THEN Angle = Angle - 360 +' IF Angle < 0 THEN Angle = Angle + 360 +' LOOP +' +' 'Return the value +' RETURN CoSineArray[Angle] +' END +' +' +' FUNCTION SinE(A AS Float) AS Float +' DIM Angle AS Integer +' +' 'Convert our radian angle to a degree value +' Angle = CInt(A * (180 / Pi)) +' +' 'Ensure we're between 0 and 359 +' DO WHILE ((Angle < 0) OR (Angle > 359)) +' IF (Angle > 359) THEN Angle = Angle - 360 +' IF (Angle < 0) THEN Angle = Angle + 360 +' LOOP +' +' 'Return the value +' RETURN SineArray[Angle] +' END diff --git a/app/examples/Games/DeepSpace/doc/.html_files/eg3.gif b/app/examples/Games/DeepSpace/doc/.html_files/eg3.gif new file mode 100644 index 0000000000000000000000000000000000000000..afdb4e80a294d9f3ec99e8995581727e23f8f5a9 GIT binary patch literal 2782 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 + + + Polar Coordinates + + + +
+ + + + + + +
+
Polar +Coordinates
+ converting +(r, d) to (x, y)
+
+
+
+ + + + + + +

+
+
Here's a +point shown using polar +coordinates.
+ It would be labeled (r, d) + or (4, 300°) for this point.
+

+
+
+
+
+
+
+
+ + + + + + + +
+
+
The +point (x, y) +can be located by finding the horizontal and vertical components +of r.  We can do that by +substituting the radius and the degree + (r, d) into the +following formulas.
+
+
+
+
+
x += r * cos(d)
+ y = r * +sin(d)
+
+
+
So we end up with;
+
+
+
+
x += 4 * cos(300) =     2
+y = 4 * sin(300) = -3.46
+
+
+
As we can +see, the +point (4, 300°) +in +polar coordinates is equivalent to a point with the rectangular +coordinates (2, -3.46)
+
+

+
+

+x = r * cos(d)

+ y = r * +sin(d)
+
+
+
+
+
+ + + + + + +
Content by Michael Isaac, 2004
+
Original Graphics, & +Design by Bill Willis 2001
+
+
+
+
+
+
+ + diff --git a/app/examples/Games/DeepSpace/doc/howto.txt b/app/examples/Games/DeepSpace/doc/howto.txt new file mode 100644 index 00000000..7b77c1ba --- /dev/null +++ b/app/examples/Games/DeepSpace/doc/howto.txt @@ -0,0 +1,16 @@ + + +HOWTO make objects float around the screen + + set the Torque property of a CObject which will determine its + spin. and then set its motion path, which is defined by its + MX and MY properties. + +HOWTO make objects fire + + this is useful only if you plan to write bot ai. + you can use the Attack property to tell an object to release a + bullet. One bullet will be released every cycle of the mainloop + so make sure you set Attack to false or you will get auto fire + like the ship has. + diff --git a/app/examples/Games/DeepSpace/doc/todo.txt b/app/examples/Games/DeepSpace/doc/todo.txt new file mode 100644 index 00000000..b10d6d4e --- /dev/null +++ b/app/examples/Games/DeepSpace/doc/todo.txt @@ -0,0 +1,17 @@ + +Things that need to be finished or started. + + - object collision. i removed the old object collision cause it + sucked. new collision code needs to be written which includes + elastic collisions so asteroids that bump eachother or the ship + will around + + - some kind of asteroids style game as an example of what + this little engine is for + + - write bot ai, so there is an opponent for everyone to fight + + - single bullet fire mode, not full on automatic + - network play mode, where players can fight head to head + - more stuff i havent thought of + diff --git a/app/examples/Games/DeepSpace/images/deepspace.png b/app/examples/Games/DeepSpace/images/deepspace.png new file mode 100644 index 0000000000000000000000000000000000000000..cb4454f5e59f283251010558593f3a4d2750973a GIT binary patch literal 14195 zcmWk#WmFq$6vT@|fl}NWT#CDUaCdhoQrumNyA&^8+}#~Yixdg&p5pFrU(QMLBRQMh zZ|^sE?#v`gMM(w&l?W9E1_lExE2##&oBaPoMgm^9zkBk+!0_;cCB-znmQT7pQ!TXJ z&))cWHhvYj-YHC77nA*ot8{w}ZEe)Fy| zE=~+7cm#tOUFojQ2Kl5l%Q7kDKb7}jAt(S8{X{mf58Y{a_sX8)<< zWBHCJEJF3$)65GB^JIM1NAhUP4yw7z~yAU8`{ur;Bt4m=AR&RyLeeJ_zlg~ z)fJ8T-0;T?6{sOtc?C7H*vLVV9SH=}*xTE?arbha-*wkL^Cw+b{V(_D&%^!ORHw6q87 z&-*+Z+-z)HO-8j13-a4X6rvp_f1N2%emeQ>5ZAsuK)Uv&AZHta{@R{*b%k`hogI!( zm!zIKW}0uF9VJtEn|~Jkhk`7)vrl|xv)7wm5T^oPUk$a5{WY2Jq2RJvs+94j4Z0+S z+aU!G=Lz5&J5$2<{kmEwhHlMgXL2|chO9>jiHoj(xIWc(KI~UIweO|3S~f08^5=3F zeGD@tI0DzYKRk~TZKB0N!*R&p#d-;@YgW#i1-@tp-dti3^ThvKYdw4DIs4YYBp<(n z9Wz|`WP4Wj+e>g^b|u?l{(PWf!haYg^4bE#j6(4$67Y7H|JHRNbcP5KdRnT@%+JOx zF-z)s4e;^x1X8dD@{^+kI5}@|1tq zb#Q%m=ad3M8FfDQH}=YtVZtv`D^Hv{SjlFhL};<}wx*#$t?J0j=xWVRhnay5D2Aqp z{^Q7h_+5*MiD|U4u@U&EJcV}ndJeC{l$Dh?^6V)`!1MGUt;kLJXpk8}L|lB7@ERjU z^8HxR41Z9Z8*^(Uv;`{@JrNu#AN3T$?nkvl1W{p4Z z?fIZpJLtfhJsIx=w$Q&%>h5k9n?ffohX9)c{gab!y7S$d=iBM|`FSPN1pEHM{?PU| zRdQjSvju{tW@c`t?sJjIeWz??f0--Kvt$JgEWw3N)x=fTZguPWV}pZ! z>nZVio3lO>zWM;`kb|61i=}F{#!nui)MzQKXeqf8oS2xHDq^C-vet~fJ3IAD+}Je;_}PO=k%S3HkH9u1=l!tq81N?DzNgAN-k``X+`QDP0s?14Rc; zer+wzQg`0T?*;1l2l#B}`mM5RIk-&zQNXi0NuQ*Af;W+bgyi4T$;M-KhSN&RNrgjE z5d@i1TvJo3SS^I2?@G`BGK>XBGTMLTABJ5#YpYg{mu8n1VQHF0lXsvy%fS)8Af#7? zx|nsLfqaM{0!vU-R0Q3)AK~da4FBx6r3jp)Zy60O8@hy7-Z!QRU>h7BAB(G|Zmzbw zrfl3Vw32=2_4TMg3@Dmj~4c1m}j-_3pAZs-^hPf(TUZ(-VG@2 z4^m>uAj%1L@kpA*yl*?ai!G!}Fd#4$SwayV>V+4{sI!XZhlvKe){THCAyHlA8RWrf zGk4ru_3|HR{XDhHJ^uHGy?4G&tIjv`pOwmHKMk8D=?JcKN{h1O)27Lp5+LOrL z?KJ*>1as!iPe)!Ae>aZQJx?jcS-Q!iR>!YJ1K;{S_g=(7&XyVvM%MH4f(?Ypfr{4D zOahfVsF05KyBm_MILh^&;YLt4Fqsn|C4&lViN+=2bkpC9wrf=63Oa8Ks$dP)O;(_G z$aV`8!m%J2Z?rmXl(zQVRQGn7A57-U)1*M~>Yx*+meB+}7b*6OJ*NH)bogt2|WD~>EU^(j*!Sr#d+(Ps*1dM1kZOIN zWf9hC^AR&(sS-g06r$;tcv52Ndt7Q8d|}wKq?_-05)wgfhBCA67P`{umZfE72?5vU zCSm`u&fg1PT9aq-&>{pzu+#R^^jk79lUwG*|NP5r{ zC1vZ@&Y$~{SI*ern5VU%RVtB?B{(h7HTSP3xj<6$|yMLMTjZ zZOg&YXS1`jp%oPX0-ky22zobk^YhQEH!;94OeO>kcr`zmr6e z>_`>r^t@YteuTVj13%vA(r(bD!^AGcB~`C8#Hm=bTpw!BB0~^?hl&VPwoE+9*6Y*h z&PYa2s*vyP_rR;xz^7$p|H~ASE@(~X$XVyxt9q4>x~*26W^n|+wy0!KlkIHjs{sq- zrIe@VVmQjnPhHvR%x9$qUN^nOp1V^hWn81C`*!)`>0-TkcMa53knH1;Tb3I)8Y!6) z=%HCJPOmz`l;Ha2CTU_Xa&$-XwfF344(h)D5g%DEAP~W;2@Yr;Emh}3_xA<$7Z*qA z*ivvG9$uAy8j)f7!SLlzjXHH*j6gll)}FHLg?lHRNvy1`=&7SI>;eD=-*)=rH-1)N=)sf8Dw zmq+$q86*qBaX6s3w5gb9NCuhe;%9R^3~(yqL`i0Uj|7$PJzz%+;i710I@AUocKdCN zUimveULVOMkO=vdQY&PT>Uj&oQB8ef!b81T^Sxconc8cqj2`&+8@)sgMR9hq$(wO- z5IP-4ARlvu7b8VOL&NKKh;3+S_`Ni$q9RAYH{G6lVr(qZWe|g>iky_xuou7!MO<9m zZ~%2(zcHrCxtp049`eRhPc#sak}5vdOicBjx(h%sFm5oe@6Ns&Lg>;aqOn7Kx+hEj zWY2J6K-`@TKd!XYGok6GFV#7JD%Yr}u3b^1meil$b>VARVgkVD?K4;{`={)hWD5Xk zx4vyPD>VJiBqO7vfo{;JjXl{qMe+nR~Z ziwAf7??kzQ?Jkpg6L#-cCfbqF@q?=&GdA==QHZ;Sre#1to*TcSu`xM3Jbc$b%g-xn z)mp7xyVReT)GM_@!-t1IY}^TDCFeNJL4$XAtq2hQ9z~(3jf1jk8ojTqI1ObW?tUAz z$Z%ECp?cLLfB)jt`ps7!LZO51Yc0;`AwOOgITg7bS7qyUiglP;z|q5ILuU22!W2G+ zC3;dk=;(tsp=3#WPr`dH?E%+gNrN3V+72tH=+y=bNLR@Sz=M1D>=Zdoi5j^@2M$uhO|Ub6tjNI^!&UOz$?%PTM+EBq>GG38<>9WvFo?k7qqs1 zochwS*1)vGi@E2L>Bb-Durk*0dvTGVW$ilTgA^rl$nA*p*ROTe2jSQn6P6(qHjX2D zf6qEtI%;Z`yLUe#guH{(09{iBIw|{W?|HFEN2Ub{%DRMy7bSGkb&WgmvKgVfjJBndXo=7(_im_(dr0Lqf$~3OLwy zpG~2$oiTOKYPFa;D)%pW+~J)|yF#^={?!`=a@N_*)~S>zO!4ed3kZqSG)#kGcy8BX zWdT4tyAm~5?MgbPQVu2>9ItY`UrvUDe{LH_6oMP34SfjsmjZ_ zQ4$Eg4YlVMT82PAxjgu-&@ohd$sT;D4zl5LOQ!OKSpzH4Ql`m;12Y76h$PjFU|PdT z3-M1nR3mj#Rz5y3cS?b&?&1WZ6=%wlTl|!9b1u9@D9M-=-H<}MdtAE#rbs4e{~bY` zPqzImCjbSha@2OsL{<~5GWm>PoEx9q!EcyC*PelQp2l`M3+yTuHzwoV2Iqm>sF5T2 zFH8BaFHh@NdA_AqR=;la=KYo!jWSj7sJ{@5V2oR+N#e|w2MUPAYD|S!Zi=4^G@=f$ z7HcpxQ;XooGS7Ar(oHS7W61dUdVjU}cwkOBQ6bKb&K6FAEWrfJ z9}=Y#Yr!N9sS1T$R!{|J=I1-rvd%Pcs17x$u{@=uq?Dz{3`_m^t?XRZ>g6EoZD{z*J)(+C$&VhjE}&-`E*?DG zu8Y2A^6R&=hZnr@I=k#@NK=sTM;{n499Nhwyg_^W6BH9J;B-@r6C#yvxln7I;IP8P z#FPjOLCZ$zwpkj#EJsF0;(PZToI1JCL%=2|I!6>+U{WS<#?z1i%g2^tJ{Yf~3FFsZ z;lGcI6o{!Ro?WKp-^be zc8pM{g&phRAU;KzsCbAUOepNX8g5et_~Gv>lGOKYGZI4{QL5fy@>|(>rIWJTC+jV8f&c3jgP-CRg%rqcoKO4<(P7@ zOX2AId+|uE@5Fiq5nL?@qRE!4(xKATBi#~H`}NN-SIz`VeLk>*IOu;~O<&E}(qUvU zg-n@`d2f1auzz=ME;P%O1gL}}-~!?-toxt?Gu}fG@b8j7ZOw3s^A+dJNyu13EaWE# zkI)}$-`h(atDpw_26{$%15-uE3vensRi(3%*2~KDx%&;8(#w(I9Jsh=hTrq^ zS@2`Na2sbt4;mo*(b9hmskKElCD`O$m$CpShpqhN;NTmV@x8l@Tcxlhee6J_cfUF3 zk>^-tkmQKZwL$Fv0(Xov^b)jYJ%#%yOu5;?0&s`Jj&-A4F>I3+ZlLdM6}`h+f4Hd) zd}*_h^Dl#3rOw(0Fu=jAUoNyNE!Z{R)l^k!6p>JLn}5QGefIiv=|S~T8G^y7NP`Z4 z?EVGAHc7ap;zQ|F{z1`$zuAhNwEPBj6XX>@7-!rpziwEMDY+%X%@GM^}x5S-!a}r@VA73Ax z1)g5z2L{c5b+xXUQYX_63;JLB2`w?Zz)d-rk~bx2f&ZGM?M9D7b^gg|l+)ZME9A`g zBRGJwN-u3-0FFFHB>U_L8Yy6c8DuI-6ZVG^eRl5QV&RKj9~CS0CJ8IEiC)No0&{)4 zn#?bY6`9necV%>_anhl7CF1P{`}Y?+BCnNLf%hI+j#jBqLel(Kjh$$0nDeasw+Cg& z@l7wFmkF8@)Qo2z34){hoH&Vi<3mBVyTA~Nq|i1@t>|{ta;|H^zxEk9bJzbm`Yu)tW z=T#DbsApzoKD;CMIh1K&V&tS2?#OG{Iw8@n65bpkc1p!RI5<-%n}w{jyyO5KeIJ4T z7fU2MF{l4CFi4uxrOHWZX~{)mI?=MXd367}-sO8OZOA7dgMx`EvJ-!a_96)cRrg_6 zmc5qX-_MG2*&oLiapP(ByN6SID3O(!=z`S#?6R%2sA{IEl@9?6>#7KsgG;NF<9%50 zP3f_R+*?4TdHsw;gbm-V-Q3KKs>#9E$IZxarS+$D9N(@HqICWU?c>NAlp2p@|9Nw;nhPKFG&%eF1 zC0fJL=FPm1PN!RYn6K^d47jNbeC`88mA{hg?SPU@4o+UiA#ZAkhVAI=oN>=hON0A{ z71s<~_Q}mb-+fY1;XI*nYER4)K8oZX{~Eo2Uu;oP{gDUlu#kRFYE$ql$#*z8N*zij zJX4E;Ax3Lj9hNj*5q9yav|hLSb5lTLGbKn?pjmk*mv>Me3J*`Hsp{6+-Wkhnpj{Px zbwu~~DkD8zcyVp*rM2ClM%l?SuHTMEEHz)9<)in#%)7*}Fvq4zt3hmVy7NDz@Nn-Q z&c>L*d000LaUM_#b}V`y7sEfCy4=$BS{TGl17upn97KG-I=`P)eI^ByQ=fBaj0~k` zG!j9NPfw~`aqH{rw|0u9YF;(+1uCMdc=Z8ZZTj<^8#i5tKlY**SOu98Qsqm?a{(3h zo#EOV&rt`9=V`#F+MimLF4u>qVKBP7^I^%Lt#)p-E7w)`mTyn{+$xFa78qs)CcG$9 z#}RAn{}810Ha~}b)$1epiu1j>panf0O-L%+T3c(enAh!Q_r2UzB zm&=_z5xZKPD$-!OI4|zMD|?q&TLSZ~4X;j^^m3JyvQu#p(Z0y`eOb^BTs(|991T=O z5GxJGjd|(cXO@d?lj#2M;)1#ThsY$4Jy^+MGyIkD><54pq>2|$E)p=*P({t6)Zo95 z11Q7k7cEeMNyH-OyL@SHZ+}NMNL(EvL*|`Y zQnKfv_9Zn27lE=QO}8hIX5!_*Gq_b!LFanZ#8~7XVOTE8qU7vm9$Wy)fH|z7AK8fV zz$IH9(X?^1w})xc!IC+87RBfSjm<+(!r2obiPb=L(wk|P7}Dfa%QckN{Li}IwR;`- z4$GD2;lt@k1f2P9`1<|3zi{2z**Ou(e zaa(#|9U07iCQuv@T|}qNfg$sdATtVkhuJqHQ{aMM5i<>`YwO`3MeAad{>xy+zcTqg znyNYXU>q2*ZAyP4gUwuAlJ~?5ggu_`RhtR(BA3w(F2o1QDzzn3vuhQU6)i_+ZQX#^ z$?7>fO+jTTRjrcuKMV{ulW-|LNeF}cq!75hB~0%UM<#{)(;W}x2jt-Qs}l!5^nW_o z+JsYJ68kC9j5_F(dr*GWCCWEGQWcRoS97I_e@m)`b9GE0@Ff8G1!pAW#QG-9zpn`uMe*8_U=)!}BTRpmXYt1- zBEx;0s%&W5Rb^PZf3^E%LJ*=^%igJCDK@G8nP-}arAXh^{$y4+Uww=~rCO|jg}@k@ zPmuU>F=tc*L&cJ)<#(d)zEnHdC`s2uVnH;fY}tD5BOJLUL@L}PtCSLqqpq%=Bjg%U zt>4Pt+r~R#R{^%pd;37D9TGIz!Gys&$QtXk-sDJ^&tZJb%e`bDt)WMajL<_)F?8%) zY^j-% z+-F{(f#>O1n3%UG?m5&XWto9T-oj}OQUq0S8cZy%_P?zim#il`h(1URs^D?HS7YvE zV(I+NB#`sHK9l7^gPD|l?Yf-fA8F2(qQHo5YXHHT0)^hN`%GkzrY50ox(z2u7&bw~ zI+Doc1K_5_4igX&QRPpRd${iWJt`DQ%XPAL`cbA)c-^*nhB&US!cOSMr6gFCbVXz_ z$kfgAdS~pn5%C9NI6J#GF`nX?IDzqZEk#Px-CkfQb{LpR4?C3F4f?K3M{B%s=tB`T zrNct>#!a1PEbEV5o~y!f(aIlbK#l+Ykei=%=o&TnhxZr!A+YM|F6t)mLzGIgoIlMo*;N!;?j$ck;N9{d!l5S$&x8V{Gg- zjA~2f#CRiR=c7l(WLF)=f>85m?gOJ*-jP9}bA z*+usXNzchp3_{=LqOjw?G$FTh%JXuHuW9=oRKV7Bkx}a_Z#C%Z;o;d$B*iv^@o^%n zL@m>*Jki>xV(f47VBe-_V_UzwEhn6~_$4122N`?Y7JAe(T=<93s&}r8G+B<@zlpm% z-VmNV?+@!8{~qB*7jC%+^)4Ir(IzPrLK)`m;3+}q62|ZWO>bXG-c2;fQm0rnF8vf1 zJv)KM7}+npEG>lp+TF#4FPdTG>FHMCG$HxU`+$qmvFwxLLJN2HR9pPUZ-1E(Yq zql4TczU9rkhORbS>N7l0;Ys~~lBG9`77?)u=0sr2N%fD7Auk!=Z`{$>RQ-UO9I_9K z!zq5D0852w(5AXoT+$l_|CMO=@-&U z+T9S9Tg&&G@UQ%1B`T>bu{VS+wkbW|CsnjeE7KJm>*xF!(ugQ>$t2UK)GGbaI9k5h zx8zF?nq2K0`S|!IS!O;wc-yZ?M`BYed_A=ww^o{G!^J3_+&emZkABQAa5p!@m8-xl zrFXZ)eRn;|WJg6r$EHxkmjB6IcD-rCQ@ z!^{j{m`y$<{A^U$v94_SNnfpSb+2aSE>d4=xo3&{6(drSaMzCqs0=r?vXwc4v7yB* zuEl@UO=9}LQpVj<e0c+L_7m~qnK1~u;#IlyJ=Zqgb>=1LoP*@S-vp<{b4->ndODE=3Ba+Nj( zv_sCGhITHj)V?!)LJ?!h*EYWRrG5H%2N=ydXp5lA;oaQ!w_Kr1vyK|ir4|RjO?dJ) zvcy2Y&AvHOB`$iN^k{*TOsuSsY=~AT$*>c6!musa3FAqGf1AItVn5;> z930F%S!uZ;At!IuOU^I!rXz0w2~trY4Rv&MAiZu&ko(-63i;M7yWh+|4YKa$b)LOG z1Cf<0^o-jd%Z!_}R#L?&k%mu+v;5Kuv>!N*{!lLx@hq8TRVh;w{KE;qztBtYaN%bD zd-O&eB2%s%h{Z7@6B%#&9WWr`NrisZn~#3~{#{fxYiSHHJFIrbXgWGN+5YfWR5VW{ zWSNHkAQiT;s^kOV&@9;}3;EU-RaRCSzM(a_Cb1yChF8Mc>`|A{HKR`H-&mE~Rci%o z2EU_%DXMm#^Jhf*-sy3=`oAiq|Mmim;%DnXg2rj7!P3<&xuva%^Dau+Z@YitxV(-# z;rdb|5#6sIuFM{!DKUd?HqN_NA>CRrnW0weiAb= zeOf`ChS{Xy?sd6(Y(fGD2ZscpQNO&r*e^8zU`?r^Sz^GVUJI$W&vt1S$L3Vm=2Ub@ z6DYT6Aj(wusNd=+smXEpXy-}?xTKbnHBck=8~-@8U&{fa4NQ%7bwPl&b$fdmt=+YK zEQ3Xs^SW&4Ns8#gOuwK{vE2LmrPt$T!szvqxTg2@@gty2JB~U7-W70v0e$we*Z)}k zZsuQ>S-9z<_m98_<_W*eQ)|iwB)NnTGRU|MJD(!0dGR5)OL3DRk}W0FhvL>EFp^-r zuHl}@e$>LuEZwF2_uL#_nR31rAU1gkPHt{eth72=&+h*LrGNo@3dlIkeQ_7x7;O21 zj=2v8DQ>fcDsW2^bnLpk>|PaB@lz_6$AfybDx0+@aKUdxhSPRZxG|$ZVrTHqKjPcB zrB)~7(|N!jI|M2~YcfZO*u9r4cw1oOVe;eJC1bD4)#mgxzow@70U!neD_o`Dn>RC~ zI+@4E;qT}7O!B9OUUly`by2ypY~MSh$hley=B1w(WZMnmY71ioD38sYy41=F*9D1x zB?;tFTOPESuyw9MSvGWGbk*gsaP2pD`Mqn5yD9Jtow z7UtKtmIQh{;_Q|!cK$*keWhAg^;I?H0Ylv1BLoZb@QjY*($+Z; z*Xm}F_&o!f%q*7Rc^^ua#|}zosURh6Ic}fTZh@nl1PzOPF=wtLvzXCTQmXHCaSoLv zH)B}C`&z+R%CpggVXxg8Hl_(j@~6yFFi?L$^yL~09XkOy_P6qK`r~FxBqUC5htp5U z2Ke{}a@6@|^^?yA?^kYhjBXE43{7WC*|m*bZ=`<6P+DnfLYn9gnc|L9AJJ+lke8Vq z;>iW1oAoT8Zcmn<9J_BmV3G5xe6?TkUD4m*f9%M13P>uXTWnFBtO+@^sc#(I3fEoAprS;E1JW={ zq;bGjDpswmcZIO*pc&r}WRnYdov+?J-8s#>=lXO5l+3DKg(6RuGy9erXX|-&wLbJ6 zmav)b0-WOD6E5QcIRwq;D=eE(7=xPvhcthNi9gk3i)5DKg(K1&37D_;P>q{g%)p@6Gq=r1i~PSsU`665Lqg^1ui9sB7#eYMQ(W zcNnU=INW-}2va&;k4lbEJ-IDm$ z^IQYSNQ|6UNc_GdLXLD#&f0xKJfA)=j``C!g^4Yz9I-3Jr5v=~a$~@9?5yVmR;r&UET|01}3?RV~bIRi&gEpiL*y9KQJJ&a8A1$3!l7AGh$X z3hWF`H{CS60z0_RpMKs1_aIWzCR(PQ*07{!5VysLLzk|vL1PwX#p*~(gIja=CprrW zN2fw#%~RgO(T@|0w~h5ox#Fg%E5f=}8d^$zSJ~!o&9y%2Q71awW__*F9l)nM|9uJ8dcrS1h|0XaT%e2ROh)9s|N=i@faO z+S(uEcK}Q&pSL@K_nT-KJGcUR{>x$RncqQg!*cWkN_O+BB-}5EsFH*nNM)P9lqN;> zgU^Skrl9YIsD^4Fa9Am}Td+u&Y_*I{`GSi={g(seWIRzD^A+I;{{HqT33P{7xXe1G z9>!qo{GmIDYVAQe2(ULkQDztKQNst(tS49Bnw|W2?t!e~ko7kpc~;Qd`(}xDb=7LW)X|*#fQ@*{ua4ut;l#ZSB#>_Sff1&rh6QlSHjs>y zL4e;5Sh@}gznpV^(jEhis;uYb|65dV*61Ks?8CD3^Qi%Okx1qqXz|2+)wmF9_GAir z`C<(*Jy=7Ra1asB07TqCVw3pKw*C|i9vmDNQ6usJNri7v>@@*kG4}s*`AK@SHvB8c zK3gPRzNFmhAZbJl70q1Tb++v6b%AGU^|ku!cS5Tpm0V;8U3@?}8#nj*VWzqg92psj0BNb$bFkaKuo^4{Vz__j=jR>8c{w;{Sy(Ov zvB2pU2CZ5Kx_ps5I%+W(!P3#`TYrKC#H~n#T*53Eq?DDh@(#E@LmcojGRU8< zMBc6vNcdcK735X&)Mjp_91k@qET5*49%W%4+9toMS?eoU>S&Kuphgt_KsLk?*F>m5 zSwb(UqO|xp$4+2P5g>@Z{SA3ly}&pVGemsY6<=_rLZxwH%Z%6gwEnXF;mLZ`>a^po z#WlaDz38B;TB+fs8AeTMr{YBxQ{ z1)t9cPa!?G&1v1fck7H3mrdp?0LfkHk#mWf_zi64&dXqje@?7nIu)^xI(#50Lu;lz~uFOiVl-9a$0Oubiyj2hcTz${wpq zXrHGcq|srt(O{GXfx1(6m_hX?3x_VVYm3g-szLa;T5fo|WwgwcI(=##*1}i#WhoA2 zC=dP1tjNnOnx&NQxAKVI`w9&5599rx6m$c}v9RvUmRK?sBDOGym)DH#SBze#fh^R@ z-Ky)9c~#zxFnz*-T9HH8M_5mE_D-#41y!G%=de^l5t39vj5 z8^7*3t-DSMeY35|Li3dp+?%1<4D3OupO;|6{n-w3mY}SW17UN}aY|U!YMd#cfpOK4((cNkSUtV5QOEANyJ-%6I1dP@-9<=!uTm4Yl zUgkcu;|zpP^kt+qD0iJW7*8A8Do}ZoatdBy3KKGWF=pOjaR~eFULnrg`-^QXL0zx2pU48jU-df6Kr zH~pQXq`hilEq=hSxld$hC8;@v7R*9%?GMM(cj?FAp7CMAeC9&uLL8r%r7?Xp8QP#$ zpZS?bhd!-b3qh+o7I)!XiEQA>Bf73q!RygF+;8;0FW(=?CuI)3eMq2fNM=%=vul>= zx$8djf8LG4y?RIHZBZURBeenVCxz1XWK~RtNd51VBl06g=uFsTBVH6~*6$x6*#}B0 z$)?#0hgsWSx4FeLEIJNRf_*j!}(TWv9+OQT)zMyY&UFHOeWxR>1AX zhc2tGgYL66NrLDS@!-JcZ-Km0SO0j_*k*VF1esm&VYUR`BjUQZh%w}vh-xlUfuy;@ zbCZ8Suo6SC?ei;6>NQ;HbTDIDlqu2a((UnuGLp&Fa`g%gF6-niNg88$I3y^b_vFTY zR>7~l5=hhThLR&$We=Uq*$DQMOwVu?_@9;%548}8!%Yyx$frLb(6uO+Y^jznF)fhd zGiMZ%sx;e2W5jfzH&eg!bw+8`7*;1A6hQLB2(cb$Wnhxk7`c7 z?;5A}6JotZSLd1nhz3!9fg7CATmAyPgAqCnrGp}b%!t_asdnSru*yr*CgHY*R_pxaC*Lck#3 zsNB+|_WP1V!7F)Bn14PU2phO$u9*Dw`rQxkz-I~~bJ-NlR7XNG25bg0i!b*FsA1Ob z-vt%VL-+fgOLgiu5y4n%>6T=NybKbym`JvR1) mzjf_IWo5iU?g``Guy9m0W}IW8M!=uxV8BvJk~QKcA^!omG6d}a literal 0 HcmV?d00001 diff --git a/app/examples/Games/DeepSpace/object.data/kite.2do b/app/examples/Games/DeepSpace/object.data/kite.2do new file mode 100644 index 00000000..02cee166 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/kite.2do @@ -0,0 +1,8 @@ +' Generated with the DeepSpace Polygon Factory. + +11,5 +12,85 +20,177 +20,183 +12,275 +11,355 diff --git a/app/examples/Games/DeepSpace/object.data/main.lst b/app/examples/Games/DeepSpace/object.data/main.lst new file mode 100644 index 00000000..110a7135 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/main.lst @@ -0,0 +1,12 @@ +' +' Object List File. +' +' This file is used to tell the game which files to load on start up. +' The first object is the player object. +' +' data_file.2do,ID,X,Y +' + +ship.2do,Subjugator,320,240 +triangle.2do,Object1,200,100 +triangle.2do,Object2,300,100 diff --git a/app/examples/Games/DeepSpace/object.data/ship.2do b/app/examples/Games/DeepSpace/object.data/ship.2do new file mode 100644 index 00000000..8615f9e8 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/ship.2do @@ -0,0 +1,13 @@ +' +' All ordered pairs are Polar Coordinates (Radius, Degree) +' See doc/coordinates.html for more information +' + +8,000 +8,120 +20,040 +15,140 +20,180 +15,220 +20,320 +8,240 diff --git a/app/examples/Games/DeepSpace/object.data/triangle.2do b/app/examples/Games/DeepSpace/object.data/triangle.2do new file mode 100644 index 00000000..85c2fc8d --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/triangle.2do @@ -0,0 +1,8 @@ +' +' All ordered pairs are Polar Coordinates (Radius, Degree) +' See doc/coordinates.html for more information +' + +30,000 +30,120 +30,240 diff --git a/app/examples/Games/DeepSpace/object.data/x-wing.2do b/app/examples/Games/DeepSpace/object.data/x-wing.2do new file mode 100644 index 00000000..f777e8d2 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/x-wing.2do @@ -0,0 +1,38 @@ +' +' All ordered pairs are Polar Coordinates (Radius, Degree) +' See doc/coordinates.html for more information +' + +18,3 +16,10 +16,7 +6,38 +5,53 +15,82 +16,79 +18,60 +19,59 +22,50 +19,59 +20,63 +18,96 +17,100 +16,97 +6,116 +6,141 +6,218 +6,243 +16,262 +17,259 +18,263 +20,296 +19,300 +22,309 +19,300 +18,299 +16,280 +15,277 +5,306 +16,349 +16,352 +18,356 diff --git a/app/examples/Games/GNUBoxWorld/.directory b/app/examples/Games/GNUBoxWorld/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/GNUBoxWorld/.hidden/screenshots/2014-12-14.png b/app/examples/Games/GNUBoxWorld/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b1b7648619c004bc0b3a08fc1f2fdf2e08382f GIT binary patch literal 185109 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�- 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%0Db{Pw*B*R@gJ zvK(6)q1c-wu04(%+`+0fH(#;<(O8U%%1S2ufYLo5;Z-gqUSCUMEJ<@q16_fl#C3_+ z*Wox*lpfg4WMvgq)zwU-QghmST4d(470H|yK-shu_R1>n0b!-FC3P&kZaJ-EFrLXG zN-^}>b2Mz)Le#gER5#M$q$sLuBy3Bv-(5#oV_>kP9M9%UO)dG8BE6>^7Bn=o=h$96(X_O#M(K-de2gcP zc!20F=PGE~m`S!L$z zhh~LduD{pMak5!>I-oeJLcfzS5tQD-aGyTM{(d_LTCA9LhOpW&Q zhu{8V_H=wmcgKDT^PkV?>Fi|h&Yf6mu~@1q7tq_+OI2lzj5EdLP_ZBe zHoxcCw|h5T?d@~s608(>n%N#;jHH8U>jhe5cK3AA(v@s8iXHvkTxZg}d%TmX&D#jV zC3JLjBgJKm_naUyHA(5xbztqZ5JE^RG(LN}hB)-xf6`K4PkN+_To7>miuLE^0gGB& zi8nUVJ(!~U`6u9kZ&F%Xj3AWk`v?Nd)cP9`@&>5h{tQ!}`4!qWZ6Yx`I;Z1gRK5_( zdjqF)fKg<;>E%NSXKYlOT34{GqKduychOkBfK*X2LC&RexSMq=){?vZ%Ou(luuHA&qxrEBANYynnJTk})%T{sMwg>pq#?8d~j^Okj2c@xCA}eoX!i3RE zar3g3e0l2^xM}@nw9%aHcjk|}c%18&EhCpYNmEl5$U0-9$}>SSe(OJ6O2D zFxI)BriOC7^cYK)Eu+4%an4)?56GANYE}pcj!eHB@h$q}Hft@*R#U#~T{1NbvC&G7 zi{kLpkMoVYA7I7mjWjkbX4jtgc=O#?C|SIkNL4+lXa$F@rM;_*<;V8%tJ@wp? znj)D@;<`B!BO`d8M>3Hh<2X#DQu#tClVNOh6fc(}F*-`lbvczxGBq(Vx8o#MnQ5Nd z&dLEwt90Q=ki}Q>F|UWi;Rs*cdM{7D_!Oa)H{qpDQ4@}k%ejaMu?1D!dduBxYP*?^ zj>GKQ`#wD*L%2aeLsbpUTecw=@cQ}shkpb>;n-@SiC#b3DD$qJv(YZ1WV!=h$N>WI z>Jj^`i5%Ys$YgVsUSK0!g)mC8MUl`LKs3bP+`U2`Hvm@rDaX@T8;Fzjbzm)5-3b{1 zC87hRL_};^*iON82#hCAwR_=MIGy!X#>r0CPFJAL~CZ1py#` zjIfkEH|&t8m{4g01=r7*_|k(;{X5v07*qoM6N<$f-zMzSO5S3 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/.lang/ca.po b/app/examples/Games/GNUBoxWorld/.lang/ca.po new file mode 100644 index 00000000..a4a49c22 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.lang/ca.po @@ -0,0 +1,112 @@ +# Catalan translation of GNUBoxWorld +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the GNUBoxWorld package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: GNUBoxWorld\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 18:06+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.form:44 +msgid "About" +msgstr "Quant a" + +#: FrmAbout.form:13 +msgid "About..." +msgstr "Quant a..." + +#: FMain.class:254 +msgid "Are you sure?" +msgstr "N'esteu segur?" + +#: FrmAbout.form:20 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:24 +msgid "Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n
pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Podeu enviar les vostres preguntes, suggeriments, errors, etc. a aquesta adreça de correu electrònic:\n pablomileti@gmail.com" + +#: FrmAbout.form:42 +msgid "Aim
\nThe shrimp has to push the movable boxes to the marked positions.

\nMove
\nTo move around the game board, use the arrow keys: up, down, left and right.

\nClear
\nTo restart the level, press the spacebar key." +msgstr "Objectiu
\nLa gambeta ha d'empènyer les caixes mòbils a les posicions marcades.

\nMoviment
\nPer moure-us per tot el tauler de joc, feu servir les tecles de fletxa: amunt, avall, esquerra i dreta.

\nNeteja
\nPer a reiniciar el nivell, premeu la tecla d'espai." + +#: FMain.form:21 +msgid "Clear" +msgstr "Neteja" + +#: FrmAbout.form:50 +msgid "&Close" +msgstr "&Tanca" + +#: FMain.form:18 +msgid "Game" +msgstr "Joc" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "GNUBoxWorld" + +#: FMain.class:81 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Felicitats! Ets molt inteŀligent!" + +#: FMain.class:222 +msgid "GNUBoxWorld - Level " +msgstr "GNUBoxWorld - Nivell" + +#: FMain.class:83 +msgid "Good Luck!" +msgstr "Bona sort!" + +#: FMain.form:36 +msgid "Help" +msgstr "Ajuda" + +#: FMain.form:39 FrmAbout.form:38 +msgid "How to play?" +msgstr "Com jugar?" + +#: FMain.form:32 +msgid "Level" +msgstr "Nivell" + +#: FrmAbout.form:28 +msgid "License" +msgstr "Llicència" + +#: FrmAbout.form:32 +msgid "\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "\n Copyright (C) 2010. Autor: Pablo Mileti \n\nAquest programa és programari lliure; el podeu distribuir i/o modificar sota els termes de la llicència GNU General Public License tal i com està publicada per la Free Software Foundation; ja sigui la versió 3 de la llicència, o bé (si ho preferiu) qualsevol versió posterior.\n\nAquest programa es distribueix amb la voluntat que pugui ser útil però SENSE CAP GARANTIA; ni tant sols les garanties implícites MERCANTILS o ESPECÍFIQUES PER UN PROPÒSIT DETERMINAT. Si voleu més informació, vegeu la llicència GNU General Public Licence.\n\nHauríeu d'haver rebut una còpia de la GNU General Public Licence juntament amb aquest programa. Si no fos així, mireu ." + +#: FMain.class:83 +msgid "Next level" +msgstr "Nivell següent" + +#: FMain.class:254 +msgid "No" +msgstr "-" + +#: FMain.form:26 +msgid "Quit" +msgstr "Surt" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "Aquesta és una altra versió del popular joc Box Wolrd. Conté 16 nivells, i tots es poden resoldre. El dissenys dels nivells s'han agafat del joc Box per a telèfons mòbils." + +#: FMain.class:254 +msgid "Yes" +msgstr "Sí" + diff --git a/app/examples/Games/GNUBoxWorld/.lang/cs.po b/app/examples/Games/GNUBoxWorld/.lang/cs.po new file mode 100644 index 00000000..a890bbf8 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.lang/cs.po @@ -0,0 +1,116 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "-" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "To je další verze populární hry Box World. Obsahuje 16 úrovní, všechny je možné vyřešit. Vzory úrovňí jsou převzaty z Box hry pro mobilní telefony." + +#: FMain.class:81 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Gratulujeme! Jsi velmi chytrý!" + +#: FMain.class:83 +msgid "Good Luck!" +msgstr "Hodně štěstí!" + +#: FMain.class:83 +msgid "Next level" +msgstr "Další level" + +#: FMain.class:222 +msgid "GNUBoxWorld - Level " +msgstr "GNUBoxWorld - úroveň " + +#: FMain.class:254 +msgid "Are you sure?" +msgstr "Jste si jisti?" + +#: FMain.class:254 +msgid "No" +msgstr "Ne" + +#: FMain.class:254 +msgid "Yes" +msgstr "Ano" + +#: FMain.form:18 +msgid "Game" +msgstr "Hra" + +#: FMain.form:21 +msgid "Clear" +msgstr "Vyčistit" + +#: FMain.form:26 +msgid "Quit" +msgstr "Ukončit" + +#: FMain.form:32 +msgid "Level" +msgstr "Úroveň" + +#: FMain.form:36 +msgid "Help" +msgstr "Nápověda" + +#: FMain.form:39 FrmAbout.form:38 +msgid "How to play?" +msgstr "Jak hrát?" + +#: FMain.form:44 +msgid "About" +msgstr "O" + +#: FrmAbout.form:13 +msgid "About..." +msgstr "O..." + +#: FrmAbout.form:20 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:24 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Autor: Pablo Mileti.

Můžete odeslat Vaše dotazy, připomínky, chyby, atd., nanásledující e-mail:\n" +" pablomileti@gmail.com" + +#: FrmAbout.form:28 +msgid "License" +msgstr "Licence" + +#: FrmAbout.form:42 +msgid "" +"Aim
\n" +"The shrimp has to push the movable boxes to the marked positions.

\n" +"Move
\n" +"To move around the game board, use the arrow keys: up, down, left and right.

\n" +"Clear
\n" +"To restart the level, press the spacebar key." +msgstr "" +"Aim
\n" +"krevety musí tlačit pohyblivé boxy označených místech.

\n" +"Pohyb
\n" +"Pro pohyb na hrací ploše, pomocí kláves se šipkami: nahoru,dolů, doleva a doprava.

\n" +"Vymazat
\n" +"Chcete-li restartovat úroveň, stiskněte klávesu mezerník." + +#: FrmAbout.form:50 +msgid "&Close" +msgstr "&Zavřít" diff --git a/app/examples/Games/GNUBoxWorld/.lang/de.po b/app/examples/Games/GNUBoxWorld/.lang/de.po new file mode 100644 index 00000000..b28ef375 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.lang/de.po @@ -0,0 +1,105 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "-" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "Dies ist noch eine Version des populären Spiels Box World. Enthält 16 Levels, alle lösbar. Die Level-Designs stammen von dem Spiel \"Box It\" für Handys." + +#: FMain.class:81 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Gratulation! Du bist verdammt schlau!" + +#: FMain.class:83 +msgid "Good Luck!" +msgstr "Viel Glück!" + +#: FMain.class:83 +msgid "Next level" +msgstr "Nächstes Level" + +#: FMain.class:222 +msgid "GNUBoxWorld - Level " +msgstr "-" + +#: FMain.class:254 +msgid "Are you sure?" +msgstr "Bist du sicher?" + +#: FMain.class:254 +msgid "No" +msgstr "Nein" + +#: FMain.class:254 +msgid "Yes" +msgstr "Ja" + +#: FMain.form:18 +msgid "Game" +msgstr "Spiel" + +#: FMain.form:21 +msgid "Clear" +msgstr "Neustart" + +#: FMain.form:26 +msgid "Quit" +msgstr "Beenden" + +#: FMain.form:32 +msgid "Level" +msgstr "-" + +#: FMain.form:36 +msgid "Help" +msgstr "Hilfe" + +#: FMain.form:39 FrmAbout.form:38 +msgid "How to play?" +msgstr "Spielanleitung" + +#: FMain.form:44 +msgid "About" +msgstr "Über" + +#: FrmAbout.form:13 +msgid "About..." +msgstr "Über..." + +#: FrmAbout.form:20 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:24 +msgid "Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Du kannst Fragen, Vorschläge, Bugs, etc, an diese E-Mail-Adresse senden:\n pablomileti@gmail.com" + +#: FrmAbout.form:28 +msgid "License" +msgstr "Lizenz" + +#: FrmAbout.form:32 +msgid "\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "\n Copyright (C) 2010. Autor: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." + +#: FrmAbout.form:42 +msgid "Aim
\nThe shrimp has to push the movable boxes to the marked positions.

\nMove
\nTo move around the game board, use the arrow keys: up, down, left and right.

\nClear
\nTo restart the level, press the spacebar key." +msgstr "Ziel
Der Shrimp muss die beweglichen Boxen auf die markierten Felder schieben.

\nBewegen
\nMit den Pfeiltasten Hoch, Runter, Links und Rechts bewegt man den Shrimp auf dem Spielfeld.

\nNeustart
\nUm das Level neu zu starten, Leertaste drücken." + +#: FrmAbout.form:50 +msgid "&Close" +msgstr "&Schließen" + diff --git a/app/examples/Games/GNUBoxWorld/.lang/es_AR.po b/app/examples/Games/GNUBoxWorld/.lang/es_AR.po new file mode 100644 index 00000000..1bbb7e86 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.lang/es_AR.po @@ -0,0 +1,101 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "-" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "Esta es otra versión del popular juego Box World. Contiene 16 niveles, todos son posibles de resolver. El diseño de los niveles fue tomado del juego Box It para teléfonos celulares." + +#: FMain.class:59 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Felicitaciones! Eres muy listo!" + +#: FMain.class:61 +msgid "Good Luck!" +msgstr "Suerte!" + +#: FMain.class:61 +msgid "Next level" +msgstr "Próximo nivel " + +#: FMain.class:197 +msgid "GNUBoxWorld - Level " +msgstr "GNUBoxWorld - Nivel " + +#: FMain.class:224 +msgid "Are you sure?" +msgstr "Desea salir?" + +#: FMain.class:224 +msgid "Yes" +msgstr "Si" + +#: FMain.class:258 +msgid "Game" +msgstr "Juego" + +#: FMain.class:261 +msgid "Clear" +msgstr "Limpiar" + +#: FMain.class:266 +msgid "Quit" +msgstr "Salir" + +#: FMain.class:272 +msgid "Help" +msgstr "Ayuda" + +#: FMain.class:275 FrmAbout.class:61 +msgid "How to play?" +msgstr "Cómo jugar?" + +#: FMain.class:280 +msgid "About" +msgstr "Acerca de..." + +#: FrmAbout.class:36 +msgid "About..." +msgstr "Acerca de..." + +#: FrmAbout.class:43 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.class:47 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Pueden enviar sus dudas, sugerencias, bugs, etc, a la siguiente dirección de correo electrónico: pablomileti@gmail.com" + +#: FrmAbout.class:51 +msgid "License" +msgstr "Licencia" + +#: FrmAbout.class:65 +msgid "" +"Move
\n" +"To move around the game board, use the arrow keys: up, down, left and right.

\n" +"Clear
\n" +"To restart the level, press the spacebar key." +msgstr "" +"Movimientos
\n" +"Para moverse por el tablero de juego utilice las teclas de dirección arriba, abajo, izquierda y derecha.

\n" +"Limpiar
\n" +"Para reiniciar un nivel presione la barra espaciadora." + +#: FrmAbout.class:73 +msgid "&Close" +msgstr "&Cerrar" diff --git a/app/examples/Games/GNUBoxWorld/.lang/ru.po b/app/examples/Games/GNUBoxWorld/.lang/ru.po new file mode 100644 index 00000000..e8fe2ec9 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.lang/ru.po @@ -0,0 +1,160 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/GNUBoxWorld/.project:20 +msgid "GNUBoxWorld" +msgstr "Мир GNU-блоков" + +#: app/examples/Games/GNUBoxWorld/.project:21 +msgid "" +"This is another version of the popular game Box World.\n" +"\n" +"It contains 16 levels, all are possible to resolve. Level designs are taken from the Box It game for mobile phones." +msgstr "" +"Это ещё одна версия популярной игры Мира блоков.\n" +"\n" +"Она содержит 16 уровней, все можно решить. Дизайн уровней взят из игры «Box It/Заблокуй» для мобильных телефонов." + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:42 +msgid "Level &1" +msgstr "Уровень &1" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:81 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "Мир GNU-блоков - Поздравляем! Вы очень умные!" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:83 +msgid "Next level" +msgstr "Следующий уровень" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:83 +msgid "Good Luck!" +msgstr "Удачи!" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:223 +msgid "GNUBoxWorld - Level " +msgstr "Мир GNU-блоков - Уровень " + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:255 +msgid "Are you sure?" +msgstr "Вы уверены?" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:255 +msgid "Yes" +msgstr "Да" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.class:255 +msgid "No" +msgstr "Нет" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.form:8 +msgid "Game" +msgstr "Игра" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.form:10 app/examples/Games/GNUBoxWorld/.src/FMain.form:35 +msgid "Clear" +msgstr "Очистить" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.form:14 +msgid "Quit" +msgstr "Выход" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.form:19 +msgid "Level" +msgstr "Уровень" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.form:22 +msgid "Help" +msgstr "Справка" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.form:24 app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:28 +msgid "How to play?" +msgstr "Как играть?" + +#: app/examples/Games/GNUBoxWorld/.src/FMain.form:28 +msgid "About" +msgstr "О программе" + +#: app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:5 +msgid "About..." +msgstr "О программе..." + +#: app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:12 +msgid "Author" +msgstr "Автор" + +#: app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:15 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Автор: Пабло Милети.

Вы можете отправить свои вопросы, предложения, ошибки и т. д. на следующий электронный адрес:\n" +"pablomileti@gmail.com" + +#: app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:19 +msgid "License" +msgstr "Лицензия" + +#: app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:22 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" +"\n" +" Copyright (C) 2010. Автор: Пабло Милети\n" +"\n" +"Эта программа ― свободное программное обеспечение. Вы можете распространять или изменять его при условиях соответствия лицензии GNU General Public License опубликованной Free Software Foundation; либо версии 3 лицензии, либо (на ваше усмотрение) любой более поздней версии.\n" +"\n" +"Программа распространяется в надежде на то, что приложение будет полезно, но БЕЗ ВСЯКИХ ГАРАНТИЙ; не гарантируется даже ПРИГОДНОСТЬ или СООТВЕТСТВИЕ КАКИМ-ЛИБО ТРЕБОВАНИЯМ. Для получения дополнительной информации ознакомьтесь с лицензией GNU General Public License.\n" +"\n" +"Вы должны получить копию лицензии GNU General Public License вместе с программой. Если этого не произошло, посмотрите ." + +#: app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:31 +msgid "" +"Aim
\n" +"The shrimp has to push the movable boxes to the marked positions.

\n" +"Move
\n" +"To move around the game board, use the arrow keys: up, down, left and right.

\n" +"Clear
\n" +"To restart the level, press the spacebar key." +msgstr "" +"Цель
\n" +"Креветка должна толкать подвижные блоки в отмеченные позиции.

\n" +"Перемещение
\n" +"Для перемещения по игровому полю используйте клавиши со стрелками: вверх, вниз, влево и вправо.\n" +"Очистить
\n" +"Чтобы перезапустить уровень, нажмите клавишу пробела." + +#: app/examples/Games/GNUBoxWorld/.src/FrmAbout.form:38 +msgid "&Close" +msgstr "Закрыть" + diff --git a/app/examples/Games/GNUBoxWorld/.project b/app/examples/Games/GNUBoxWorld/.project new file mode 100644 index 00000000..8fdb90f7 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=GNUBoxWorld +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="This is another version of the popular game Box World.\n\nIt contains 16 levels, all are possible to resolve. Level designs are taken from the Box It game for mobile phones." +Authors="Pablo Mileti from Buenos Aires, Argentina." +TabSize=2 +Translate=1 +Language=en +Vendor=Example +Packager=1 +Tags=Example,Game,LogicGame +Screenshot=.hidden/screenshots/2014-12-14.png +CreateMenu=1 diff --git a/app/examples/Games/GNUBoxWorld/.src/Cell.class b/app/examples/Games/GNUBoxWorld/.src/Cell.class new file mode 100644 index 00000000..f3f2f333 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/Cell.class @@ -0,0 +1,78 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + +Public Const gbFloor As Integer = 0 +Public Const gbMovable As Integer = 1 +Public Const gbMovableOnTarget As Integer = 2 +Public Const gbTarget As Integer = 3 +Public Const gbObstacle As Integer = 4 + +Public Busy As Boolean +Public Movable As Boolean +Public Target As Boolean +Public Pic As Picture +Public Type As Integer + +Public Sub _new(type As Integer) + Select Case type + + Case 0 + Me.Type = Me.gbFloor + Me.Busy = False + Me.Movable = False + Me.Pic = Picture["piso.png"] + Me.Target = False + + Case 1 + Me.Type = Me.gbMovable + Me.Busy = True + Me.Movable = True + Me.Pic = Picture["movible.png"] + Me.Target = False + + Case 2 + Me.Type = Me.gbMovableOnTarget + Me.Busy = True + Me.Movable = True + Me.Pic = Picture["movibleendestino.png"] + Me.Target = True + + Case 3 + Me.Type = Me.gbTarget + Me.Busy = False + Me.Movable = False + Me.Pic = Picture["destino.png"] + Me.Target = True + + Case 4 + Me.Type = Me.gbObstacle + Me.Busy = True + Me.Movable = False + Me.Pic = Picture["obstaculo.png"] + Me.Target = False + +End Select + +End + +Public Sub FixObstaculo(hLeft As Cell, hRight As Cell) + + Dim sSuffix As String + + If Me.Type <> gbObstacle Then Return + + If Not hLeft Or If hLeft.Type <> gbObstacle Then sSuffix &= "l" + If Not hRight Or If hRight.Type <> gbObstacle Then sSuffix &= "r" + + If Not sSuffix Then Return + + Pic = Picture["obstaculo-" & sSuffix & ".png"] + +End diff --git a/app/examples/Games/GNUBoxWorld/.src/FMain.class b/app/examples/Games/GNUBoxWorld/.src/FMain.class new file mode 100644 index 00000000..da712599 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FMain.class @@ -0,0 +1,274 @@ +' Gambas class file + +' +' GNUBoxWorld +' This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. +' Level designs are taken From Box It game For mobile phones. +' +' Copyright (C) Pablo Mileti from Buenos Aires, Argentina. +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, see http://www.gnu.org/licenses +' + +Public Game As GameBoard +Public level As Integer +Public PicturesBoxes As New PictureBox[10, 11] +Public PictureBoxPlayer As PictureBox +Public PictureBoxMover As PictureBox +Public DesX As Integer +Public DesY As Integer +Public XPosMove As Integer +Public YPosMove As Integer +Public IgnoreKeys As Boolean + +Public Sub Form_Open() + + Dim iInd As Integer + Dim hMenu As Menu + + For iInd = 1 To GameBoard.NUM_LEVELS + hMenu = New Menu(mnuLevel) As "mnuLevel" + hMenu.Text = Subst(("Level &1"), iInd) + hMenu.Tag = iInd + Next + + level = 1 + ignorekeys = False + newGame + Me.h = 64 * 10 + Me.w = 64 * 11 + btnClear.X = (Me.W / 2) - (btnClear.W / 2) + Me.Window.Center +End + +Public Sub btnClear_KeyPress() + If Key.code <> Key.up And Key.code <> Key.down And Key.code <> Key.left And Key.code <> Key.Right Then Return + Select Case Key.code + Case Key.Right + PictureBoxPlayer.Picture = picture["derecha.png"] + Case Key.Left + PictureBoxPlayer.Picture = picture["izquierda.png"] + Case Key.Up + PictureBoxPlayer.Picture = picture["arriba.png"] + Case Key.Down + PictureBoxPlayer.Picture = picture["abajo.png"] + End Select + If Game.ValidateMover(Key.code) And Not IgnoreKeys Then + Move(Key.code, Game.GetCell(Key.code, 1).Movable) + End If + If Game.isDone() Then + Inc level + If level > GameBoard.NUM_LEVELS Then + btnClear.Visible = False + MnuClear.Enabled = False + PictureBoxPlayer.X = 64 + PictureBoxPlayer.Y = 64 + PictureBoxPlayer.W = 560 + PictureBoxPlayer.H = 482 + PictureBoxPlayer.Picture = Picture["ganador.png"] + PictureBoxPlayer.SetFocus + Me.Title = ("GNUBoxWorld - Congratulation! You're very clever!") + Else + Message.Info(("Next level") & gb.NewLine & ("Good Luck!")) + newGame + End If + End If +End + +Public Sub btnClear_Click() + newGame() +End + +Private Sub Move(direction As Integer, desplace As Boolean) + Dim i As Integer + Select Case direction + Case Key.Right + DesX = 1 + DesY = 0 + PictureBoxPlayer.Picture = picture["derecha.png"] + Case Key.Left + DesX = -1 + DesY = 0 + PictureBoxPlayer.Picture = picture["izquierda.png"] + Case Key.Up + DesX = 0 + DesY = -1 + PictureBoxPlayer.Picture = picture["arriba.png"] + Case Key.Down + DesX = 0 + DesY = 1 + PictureBoxPlayer.Picture = picture["abajo.png"] + End Select + If desplace Then + PictureBoxMover.Picture = WhoMove(direction) + PictureBoxMover.X = XPosMove + PictureBoxMover.Y = YPosMove + PictureBoxMover.Raise + 'here will walk on + If Game.cells[Game.RowPlayer + DesY, Game.ColPlayer + DesX].Type = Cell.gbMovable Then + PicturesBoxes[Game.RowPlayer + DesY, Game.ColPlayer + DesX].picture = Picture["piso.png"] + End If + If Game.cells[Game.RowPlayer + DesY, Game.ColPlayer + DesX].Type = Cell.gbMovableOnTarget Then + PicturesBoxes[Game.RowPlayer + DesY, Game.ColPlayer + DesX].picture = Picture["destino.png"] + End If + End If + IgnoreKeys = True + For i = 1 To 64 + PictureBoxPlayer.Move(PictureBoxPlayer.x + Desx, PictureBoxPlayer.y + DesY) + If desplace Then PictureBoxmover.Move(PictureBoxmover.x + Desx, PictureBoxmover.y + DesY) + Wait 0.003 + Next + IgnoreKeys = False + 'here i was + 'cambio el contenido de la celda donde deje de estar + If Game.cells[Game.RowPlayer, Game.ColPlayer].Target Then + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbTarget) + Else + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbFloor) + End If + Game.RowPlayer = Game.RowPlayer + DesY + Game.ColPlayer = Game.ColPlayer + DesX + 'here i go + 'donde termino parado + If Not Game.cells[Game.RowPlayer, Game.ColPlayer].Target Then + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbFloor) + Else + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbTarget) + End If + If desplace Then 'here move obstacle + If Game.cells[Game.RowPlayer + desy, Game.ColPlayer + desX].Target Then 'go on target + Game.cells[Game.RowPlayer + desy, Game.ColPlayer + desX] = New Cell(Cell.gbMovableOnTarget) + Else 'go on floor + Game.cells[Game.RowPlayer + desy, Game.ColPlayer + desX] = New Cell(Cell.gbMovable) + End If + End If + ShowGameBoard + PictureBoxmover.Lower +End + +Private Function WhoMove(direction As Integer) As Picture + Dim row As Integer + Dim col As Integer + Select Case direction + Case Key.Up + row = Game.RowPlayer - 1 + col = Game.ColPlayer + Case Key.Down + row = Game.RowPlayer + 1 + col = Game.ColPlayer + Case Key.left + row = Game.RowPlayer + col = Game.ColPlayer - 1 + Case Key.right + row = Game.RowPlayer + col = Game.ColPlayer + 1 + End Select + XPosMove = PicturesBoxes[row, col].X + YPosMove = PicturesBoxes[row, col].Y + Return PicturesBoxes[row, col].Picture.Copy() +End + +Public Sub ShowGameBoard() + + Dim row As Integer + Dim col As Integer + For row = 0 To 9 + For col = 0 To 10 + PicturesBoxes[row, col].Picture = Game.Cells[row, col].Pic + Next + Next + +End + +Public Sub MakePictureBoxes() + Dim row As Integer + Dim col As Integer + For row = 0 To 9 + For col = 0 To 10 + PicturesBoxes[row, col] = New PictureBox(Me) + PicturesBoxes[row, col].h = 64 + PicturesBoxes[row, col].w = 64 + PicturesBoxes[row, col].Stretch = True + PicturesBoxes[row, col].top = (row * 64) + PicturesBoxes[row, col].left = (col * 64) + PicturesBoxes[row, col].Lower + Next + Next + PictureBoxPlayer = New PictureBox(Me) + PictureBoxMover = New PictureBox(Me) + PictureBoxmover.w = 64 + PictureBoxmover.h = 64 + PictureBoxPlayer.Picture = picture["derecha.png"] + PictureBoxPlayer.h = 64 + PictureBoxPlayer.w = 64 + PictureBoxPlayer.left = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].left + PictureBoxPlayer.top = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].top +End + +Private Sub newGame() + + Dim hMenu As Menu + + Me.Title = ("GNUBoxWorld - Level ") & level + Game = New GameBoard(level) + destroyPicturesBoxes + MakePictureBoxes + PictureBoxPlayer.left = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].left + PictureBoxPlayer.top = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].top + ShowGameBoard + + For Each hMenu In mnuLevel.Children + hMenu.Checked = hMenu.Tag = level + Next + +End + +Public Sub destroyPicturesBoxes() + Dim control As Object + For Each control In Me.Children + If Object.Type(control) = "PictureBox" Then + control.delete + End If + Next +End + +Public Sub MnuAbout_Click() + FrmAbout.Show +End + +Public Sub MnuQuit_Click() + Me.close +End + +Public Sub Form_Close() + If Message.Question(("Are you sure?"), ("Yes"), ("No")) = 2 Then + Stop Event + End If +End + +Public Sub MnuClear_Click() + newGame() +End + +Public Sub MnuHelpPlay_Click() + FrmAbout.TabStrip1.index = 2 + FrmAbout.Show +End + +Public Sub mnuLevel_Click() + + level = Last.Tag + newGame + +End diff --git a/app/examples/Games/GNUBoxWorld/.src/FMain.form b/app/examples/Games/GNUBoxWorld/.src/FMain.form new file mode 100644 index 00000000..116ef9a4 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FMain.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,99,83) + Icon = Picture["icon.png"] + Resizable = False + { MnuGame Menu + Text = ("Game") + { MnuClear Menu + Text = ("Clear") + Picture = Picture["icon:/32/clear"] + } + { MnuQuit Menu + Text = ("Quit") + Picture = Picture["icon:/32/quit"] + } + } + { mnuLevel Menu + Text = ("Level") + } + { MnuHelp Menu + Text = ("Help") + { MnuHelpPlay Menu + Text = ("How to play?") + Picture = Picture["icon:/32/help"] + } + { MnuAbout Menu + Text = ("About") + Picture = Picture["icon:/32/info"] + } + } + { btnClear Button + MoveScaled(15,2,16,6) + Font = Font["Bold,+4"] + Text = ("Clear") + } +} diff --git a/app/examples/Games/GNUBoxWorld/.src/FrmAbout.class b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.class new file mode 100644 index 00000000..33a4a0fb --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.class @@ -0,0 +1,19 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + +Public Sub Form_Open() + Me.Window.Center + TextArea1.Pos = 0 +End + +Public Sub Button1_Click() + Me.Close +End diff --git a/app/examples/Games/GNUBoxWorld/.src/FrmAbout.form b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.form new file mode 100644 index 00000000..165e8850 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.form @@ -0,0 +1,45 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,79,39) + Text = ("About...") + Resizable = False + { !TabStrip1 TabStrip + MoveScaled(2,2,50,35) + #Public = True + Count = 3 + Index = 0 + Text = ("Author") + { TextLabel1 TextLabel + MoveScaled(2,2,45,21) + Text = ("Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com") + Alignment = Align.Justify + } + Index = 1 + Text = ("License") + { TextArea1 TextArea + MoveScaled(1,1,46,25) + Text = ("\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see .") + ReadOnly = True + Wrap = True + ScrollBar = Scroll.Vertical + } + Index = 2 + Text = ("How to play?") + { TextLabel2 TextLabel + MoveScaled(2,1,45,27) + Text = ("Aim
\nThe shrimp has to push the movable boxes to the marked positions.

\nMove
\nTo move around the game board, use the arrow keys: up, down, left and right.

\nClear
\nTo restart the level, press the spacebar key.") + Alignment = Align.Justify + } + Index = 0 + } + { Button1 Button + MoveScaled(59,30,14,6) + Text = ("&Close") + } + { PictureBox1 PictureBox + MoveScaled(54,5,22.7143,22.7143) + Picture = Picture["logo.png"] + Stretch = True + } +} diff --git a/app/examples/Games/GNUBoxWorld/.src/GameBoard.class b/app/examples/Games/GNUBoxWorld/.src/GameBoard.class new file mode 100644 index 00000000..09212783 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/GameBoard.class @@ -0,0 +1,595 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + +Public Const NUM_LEVELS As Integer = 16 + +Public RowPlayer As Integer +Public ColPlayer As Integer +Public Cells As New Cell[10, 11] + +'See below constructor + +'This Function return true if player can move or false if exist obstacle +Public Function ValidateMover(Direction As Integer) As Boolean + Select Case Me.GetCell(Direction, 1).Type + Case Cell.gbMovableOnTarget + If Me.GetCell(Direction, 2).Busy Then + Return False + Else + Return True + End If + Case Cell.gbMovable + If Me.GetCell(Direction, 2).Busy Then + Return False + Else + Return True + End If + Case Cell.gbFloor + Return True + Case Cell.gbObstacle + Return False + Case Cell.gbTarget + Return True + End Select +End + +'This function return an object Cell if distance = 1 then Cell by side, distance = 2 Cell away 2 cells +Public Function GetCell(Where As Integer, distance As Integer) As Cell +Select Case Where + Case Key.up + Return Cells[Me.rowplayer - distance, Me.colplayer] + Case Key.down + Return Cells[Me.rowplayer + distance, Me.colplayer] + Case Key.left + Return Cells[Me.rowplayer, Me.colplayer - distance] + Case Key.right + Return Cells[Me.rowplayer, Me.colplayer + distance] +End Select +End + +'This function return true if all target are busy (Winner!) +Public Function isDone() As Boolean + Dim obj As Object + For Each obj In Me.cells + If obj.type = Cell.gbTarget Then Return False + Next + Return True +End + + +Public Sub _new(level As Integer) + Dim row As Integer + Dim col As Integer + 'All cells type Floor + For row = 0 To 9 + For col = 0 To 10 + Me.Cells[row, col] = New Cell(Cell.gbFloor) + Next + Next + 'Construct levels + Select Case level + 'LEVEL 1 + Case 1 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[5, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbTarget) + Me.Cells[2, 7] = New Cell(Cell.gbTarget) + Me.Cells[4, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + RowPlayer = 3 + ColPlayer = 5 + 'LEVEL 2 + Case 2 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[5, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 4] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[2, 6] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[4, 3] = New Cell(Cell.gbMovable) + RowPlayer = 3 + ColPlayer = 5 + 'LEVEL 3 + Case 3 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 6] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + RowPlayer = 5 + ColPlayer = 2 + 'LEVEL 4 + Case 4 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[8, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbObstacle) + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[5, 6] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[7, 5] = New Cell(Cell.gbObstacle) + Me.Cells[7, 6] = New Cell(Cell.gbObstacle) + Me.Cells[7, 7] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[6, 2] = New Cell(Cell.gbTarget) + Me.Cells[7, 2] = New Cell(Cell.gbTarget) + RowPlayer = 7 + ColPlayer = 4 + 'LEVEL 5 + Case 5 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 6 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[6, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 6 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[2, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 3] = New Cell(Cell.gbTarget) + For row = 2 To 5 + Me.Cells[row, 6] = New Cell(Cell.gbMovable) + Next + For row = 2 To 5 + Me.Cells[row, 7] = New Cell(Cell.gbTarget) + Next + RowPlayer = 3 + ColPlayer = 2 + 'LEVEL 6 + Case 6 + For col = 1 To 9 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 9 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 9] = New Cell(Cell.gbObstacle) + Next + Me.Cells[3, 5] = New Cell(Cell.gbObstacle) + Me.Cells[2, 5] = New Cell(Cell.gbMovableOnTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 7] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 6] = New Cell(Cell.gbTarget) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 2] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[5, 7] = New Cell(Cell.gbObstacle) + Me.Cells[5, 8] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[6, 6] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 8] = New Cell(Cell.gbObstacle) + RowPlayer = 6 + ColPlayer = 5 + + 'LEVEL 7 + Case 7 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbObstacle) + Me.Cells[2, 4] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[5, 4] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + Me.Cells[4, 7] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 7] = New Cell(Cell.gbTarget) + Me.Cells[5, 5] = New Cell(Cell.gbTarget) + Me.Cells[5, 7] = New Cell(Cell.gbTarget) + RowPlayer = 3 + ColPlayer = 3 + + 'LEVEL 8 + Case 8 + For col = 1 To 9 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 9 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 9] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 8] = New Cell(Cell.gbObstacle) + Me.Cells[3, 8] = New Cell(Cell.gbObstacle) + Me.Cells[4, 2] = New Cell(Cell.gbObstacle) + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[5, 2] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[5, 4] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 7] = New Cell(Cell.gbMovable) + RowPlayer = 2 + ColPlayer = 6 + + 'LEVEL 9 + Case 9 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[8, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + For row = 4 To 8 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Me.Cells[row, 7] = New Cell(Cell.gbObstacle) + Next + For row = 6 To 8 + Me.Cells[row, 3] = New Cell(Cell.gbObstacle) + Me.Cells[row, 6] = New Cell(Cell.gbObstacle) + Me.Cells[row, 7] = New Cell(Cell.gbObstacle) + Next + Me.Cells[3, 5] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbTarget) + Me.Cells[6, 4] = New Cell(Cell.gbTarget) + Me.Cells[7, 4] = New Cell(Cell.gbTarget) + Me.Cells[7, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + RowPlayer = 2 + ColPlayer = 5 + + 'LEVEL 10 + Case 10 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[8, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 4 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Me.Cells[row, 7] = New Cell(Cell.gbObstacle) + Next + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 5] = New Cell(Cell.gbObstacle) + Me.Cells[6, 6] = New Cell(Cell.gbObstacle) + Me.Cells[2, 3] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[2, 6] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbTarget) + Me.Cells[3, 6] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[6, 4] = New Cell(Cell.gbMovable) + RowPlayer = 7 + ColPlayer = 5 + + 'LEVEL 11 + Case 11 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[6, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovableOnTarget) + RowPlayer = 2 + ColPlayer = 7 + + 'LEVEL 12 + Case 12 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[2, 3] = New Cell(Cell.gbObstacle) + Me.Cells[3, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[5, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 5] = New Cell(Cell.gbTarget) + Me.Cells[5, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + RowPlayer = 5 + ColPlayer = 2 + +'LEVEL 13 + Case 13 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 4] = New Cell(Cell.gbObstacle) + Me.Cells[4, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovableOnTarget) + RowPlayer = 4 + ColPlayer = 2 + +'LEVEL 14 + Case 14 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 2] = New Cell(Cell.gbObstacle) + Me.Cells[2, 6] = New Cell(Cell.gbObstacle) + Me.Cells[2, 7] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[3, 7] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[5, 7] = New Cell(Cell.gbTarget) + Me.Cells[6, 7] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + RowPlayer = 3 + ColPlayer = 5 + +'LEVEL 15 + Case 15 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[2, 3] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[2, 7] = New Cell(Cell.gbTarget) + Me.Cells[4, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[6, 3] = New Cell(Cell.gbTarget) + Me.Cells[6, 5] = New Cell(Cell.gbTarget) + Me.Cells[6, 7] = New Cell(Cell.gbTarget) + RowPlayer = 4 + ColPlayer = 5 + +'LEVEL 16 + Case 16 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[2, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[2, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 6] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + For col = 2 To 7 + Me.Cells[4, col] = New Cell(Cell.gbTarget) + Next + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + RowPlayer = 6 + ColPlayer = 5 + + + End Select + + For row = 0 To 9 + Cells[row, 0].FixObstaculo(Null, Cells[row, 1]) + For col = 1 To 9 + Cells[row, col].FixObstaculo(Cells[row, col - 1], Cells[row, col + 1]) + Next + Cells[row, 10].FixObstaculo(Cells[row, 9], Null) + Next + +End diff --git a/app/examples/Games/GNUBoxWorld/License b/app/examples/Games/GNUBoxWorld/License new file mode 100644 index 00000000..b17f91b9 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/License @@ -0,0 +1,189 @@ +GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. +TERMS AND CONDITIONS +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. +1. Source Code. + +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. +2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + * a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + * b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + * c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + * d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + * a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + * b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + * c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + * d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + * e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. +7. Additional Terms. + +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + * a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + * b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + * c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + * d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + * e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + * f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. +8. Termination. + +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. +11. Patents. + +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. +13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/app/examples/Games/GNUBoxWorld/abajo.png b/app/examples/Games/GNUBoxWorld/abajo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e716fbc7cee2fed0a7d82c7be5173276d44e352 GIT binary patch literal 5205 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 P00000NkvXXu0mjf8lo=L literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/arriba.png b/app/examples/Games/GNUBoxWorld/arriba.png new file mode 100644 index 0000000000000000000000000000000000000000..86e88f3f8767a36487fedd117e7556f1ffbeae16 GIT binary patch literal 5291 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/derecha.png b/app/examples/Games/GNUBoxWorld/derecha.png new file mode 100644 index 0000000000000000000000000000000000000000..4f37b4423076d694ffe46f4ca69bde88565c9e5d GIT binary patch literal 4347 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 pKRGwU*W4T7JGo6%%dMpeWv)ps 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/ganador.png b/app/examples/Games/GNUBoxWorld/ganador.png new file mode 100644 index 0000000000000000000000000000000000000000..f6672bf77dc04adbc3cac19d7bfceca31f837ca7 GIT binary patch literal 255686 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

7=SStZ1=X?AZb9iC858}c}1<{7*u#UE&kVJ@C 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�Ai9aA*8b$jtFEnA_e-HG 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 z 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�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

P~ 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/icon.png b/app/examples/Games/GNUBoxWorld/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d1737db0f210850e690a3280d53d731ac058d6c6 GIT binary patch literal 5889 zcmV+c7yjspP)H0bCJ6|m1VRF_u#gC#ke5i2lE+Aq z@)9X85mF>e2|*DMAd%Q2z?hKO2#_6Yyp6}pFmuE(KiQqpMD zeXFbL|Lb4Ae<}8f-~3JTb2&^JN-4xqgj_y9aSjn-(C;H(C}8p=px+OX&ldnTd0IaV zk;~`7*yKrrejlFigL6J{PBa*Ra}MEo6X(b{hUe#USUzA&`i0u)mLky*eeCFVyVzV?L$y}J ziF!R@LTYXphS*qLMY&SJ-28mPgw&iUimWz#K;^eB^sNX@BqN~eJq_i zg|sA)QrO+z#_rAz06++V#ib?Wi^VkcIN0CE;lTj_fOC%7g$0x=mCVhznn!52TKWu( z;Y7WT>U0fhQRsC#2(DdobE?zRm`O2#8RFN>8~~aL3kzT=Nr(mmY_6@j&-p?@Ga)4j zGL~p;ZU6w>w|*{%*||A)rf$2Fy&lFOJP-BRS+gObAA~sA-_K0roMWb5hY;KxC_=l{ z99oK@Ha%k)PEb-I==DaVYpPmBp-==EffQkTYYSR*62_IvWt1yZ0Mnwjx4S#CPN7&t zZFGoBcB{GZ?Yp#QIh^l%bDN}=27xPwMfG_)%N5uw-Zx;ZkIV;Al9dhUpdD7%aq zVHjf29{`|>L1ugup0@G6rvCKP1+Egem6S zo^^HB+K(_{FaQzZaBm-n`v+s^SdO-uO=s=L9*Srz@o6RiKo|sA8zMe62}FcwFu=wb z;?w)ZaXglIJK{Ko7e^5~tyacI4j<~mgYSo*FS+ly;2@<%mvaWC6gU@9N`V1TN`f2T zOGFS{K*|_g2q;B*4k_ouiD(G;FfGdFOk8kCDdBk@l$2T_83QT(I|6}o4jD@bPXGk; z+DCZp=T9KcsPZV2yj(t?fCXzQDbcyZ{eAd_5{i|YJ|xqJBT`_DgK>s9j={h*LB^91 z03{_DW8gwF0HpDRW*B#($T`;pNC`y>p77lIL<9nXfq`)jW#$O38O%9{G}6Tw17i$| z2vHoNR4!w7egT}b6m0-NP)cEMXGhzBLZN`ggF$|J+4=%R=Z|1$Smm2;pvsl$oO>K!ozopG5Dj`@st(l&-&l{?aKZA;1}jBIOQh zZ(4YI{j{_wrBJ-`0^-^XqQw>D4|YLd=+7hos$3~!ZebwW0S32R(0dv2^n6dN z59bgzr*13_v(?s@z5MdA?41$b*2WjcZ zHP#`gs%ZS_U!nb;OPKl2*C1qU^wt_7fHR?uk|A6O;ru-30zB-&-`m0VBcH?m$NvPS zm!CuNjaM{-z#xRD=bGO!Fe8=81u@3Kz)*bs6&yYEFdCoxOE3_sKmMM%h$yoclu$7wd>b$_8ku)S1LOlv^VJ=>|$_Y7MyXTqsloEZgg3D z4#Pyo^lch9XV>zN4iML-!935n(!|JF%RVfTTnLa-W{zf#7lu$uf+`gRttMXo+4phk z^yzGLYDTxy!P>QJ@MtKc9Ud}{^JXkHqt)j7ggq7u! zIJJBdMVDF9w>%gOaBX!R8=DO*%+KQF;yk7*Wu)_Kv5c|4zJ)Ld5;f%7W5C!201kFD z#!#)*GMObqj0@}>5Gq$HI8m>=IlXSrMRgYETewV$99EFA1Q!Y8a=9FCI+f(%qA0?z zu4RK3&-1Xdyy)_fQcA3@ZDi(f&at>SkD|##5)n4H8crph2y;)POEZ*GU|eJ(%j$Fu z)oRUsZa14Qla zN^aD61>p$;mQuuz19^sdUf2(@y}316KPi=MF$j$d?y@@C04m&Z@f_ASe}YD1Co6)c zLUh~tGkD zY9PV+Pp#`kD2;-umA$O-HwjFOkiTgolI;^ruOyhFE7LQ ze5|aT9J|-SVH@ik4Xi9LLP;&k`}?~H`yulAJXoOw#ROK@Hn6jEfR*L(>nNr0;>)k$ z?mKUT=LrxcA+(MV35+yME|tru)@oUomLk5@Y{CQBXf4i+Zl{CbS`UhpMuUprwVQ~h zPk=EFAtOk}pp-zvaQ~iEF zr3nNuAUt;wy}Rzi{8t_Wh;aCUkAnQ%2oUmp-vJow#;o{q#=#g63?lr(p9Y^_LT+aj zTTefSStu-W1sj?PAzVc|jrNFij?wdd9RBX_WB#$f$I=)67V&f)?RUM;5Sju)jAh_C zn+$};4`rY)Qv}2@pcE)yKzQ?6hsjgQHE|v5dI$`yscBq}d~B?(;>vCl=l8DS!F-(k z{gB+Uaa;8JFfb5lQ!qYV8t%a8TKJv{SRJw;?XYa!1?Z1I?Z3ekq5-BRB+&XbAGsREN zbw+6BTVKIoaTzckwWjzd(pr3HmFJ! z`=@V3>6cG~QUwqI7z`q;u4St+7UbADoO8^}J}&qYSG^i4Td(7V@+|t@Cd$UQE2YqA z*iv^A4Gs&y?k7G2l&3(PWB-$%2N^Jw9?fPGtrX4~L4w_G*EJ^$GeD=Gxb_0VgQ`@) z7(?vm(0RvuUE;G=n?k$YI~L^BYEv%fFSx+{)dF&1ALm7k-QIObE|5DhgU0qgwi-Lw z!e}N(#uC+P8C(d&Q3N(sbJ7*ue2YVajO%z{63ElE0bV~0#}*ieBnVam)5Q-(1i>|P z-hTJ(c>IZGEz~UDr0$N5j(pFxP0Y} zv3fjPpT>jlycTBTG9#ZqZ(Le!3KrvoqP*_j@hV6(!?)B<5^Z&K+@jLGtnBJAxS zVyaTX$;J7R%u*antZ!`M@|CMtSw4xC<&!$KmKsVaY&CZ9%9X2_pR42MF_|SI!tUNa z_79FA}SvAzzX>uug&N*i1=O<=`gI>=7A%mEvs?`h*u=RNje-mJ#6@t0kAQ#+y zKUJw6=y@o*)qR_71SOcL12$fs`?r zCtRA23y#=irR^nf&M~*JfKn-`Zufe<1mxK-8Dl6Ei;e-AINo>xVa3hl0hvik+8Uk} z*kYkD76_$BKA&?SM>k@Os-W-ts8q^ELL`#hHM#P-6@QCIIMY?+wEf_mqmY8t=`-CB z5Ryu|aXI zdwU0o*fU^Yx;<=1h*HX3g5bgwY9$!5)#2f`oHRQ10Mmha{koB>)J-ZaZ=C7PJX^ct z98$(E2;z*RR4kxBh>y+W)KAn5!rMV(%p4g@)K5%98EdZ%UUm!y0~aQz#UDh7<2aih z1JLbs5d_ypXwzjl=+fB6>gusQLN+BvsqbuQ%JvAo{hr(L!i%pW2m`$rtb5Y#a&e)K z^S7SCOP60mtCe)2CQ`;wtCjKKJMYG8Z>(Z>@4%JBn5{){jzS@ix7~3rxkZ~3G+ihb z34#bgw~O`b*Dz`5OZEL@!9p|`XqT31S#jb=q){Oea=wrI?|qwI*rv+NoTS(Z+OoKF{ zq)E$4g6DY?yWB(5hv?Zu&MhpUR4!+Tw=sfFgA_1ArKqtJ}M3jgbl-ML7A7e}}{0{xG7O&f>(kzKWywz8`XN*QZZQMrQVLl0x-hu_9vs)pA6?{+}Qb-AaQU=c2m211;pT&cL* zNVlKb4v=VZ833^G&;J0?ZX!H?2OvDx|2J$qh_u&`N`fH;PI7sKr*FmV*S-i@nbPfW zQ&k_7ZRAeLB1i^Z*aXi<$b7T6Njec;_-O2Rujz}^Qw z0$HuOP7~PTi&;~5l#q!>l5=s!5uCk%Xm%dC-3HqCJ&3@x?H%`;Bt3@P!BQSQ^R>e#HAY%@&FcZf7+?5^{TvQ2J&fMn z_k)9Q;y?Zsyxj&WRh=v`1=o?m64YotQTyI^;qc@N;^`A$l`2H@XhZ_SFhI1M)IHPP z;_T5&muTtKDdhaoz7E?+efEJ%$d{)wo7)>Ehq`o;l5ysIwp7Y#Rl3zR?XE0{(ZDhr z#H9C&<5)La4%GvdGqA~-7-Q(R4)Mm%et?CgC3s>q|KQ#X4ZpD&$An&|nFJc9UB;HS zxbeA~KrN!msD$kq<_0pW)WOq+&O+OCV#78}Ic9-jU_lN`2(~SJM9CRQ5yD;@VGxY8 z+-H~&#~KItzMsjuq(>abc=e}`qf{M%dR>$&70k}hkG168j)u2|Vln$9E={}vIqo&w81=R|#{&|ukcN$N=&^wX zOKB#gyRLyGvs9g~W!hsiC2I2|E>nvnpqYAF2P9A8(oE=fItYSaxzB|$PvXKL%;10D z_w~ETRC+D_*uwo%cphrgGfsrlK*%1PbB=1Q<_W#g3e8;-DLt^Csgh zri8LD&Tga!>8&4b1PHz9QQmT1YZ67j=Ad8uk7I{Z0V(6mi^?=uu$g(Klq)L(<6F{+ zD5J~i%#v%Ug4XArvjN!`V|MgX`P#wV$itPWr~tBg{i1+(z$I&Kf)A~USp5E?|pP! z(jbmw4Ep^{s628ghXaoD@q})-i}IVy>2P7tcHa_04~HxaV0$lg8}{z Xbp(Gj=?hmn00000NkvXXu0mjf2*@sw literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/izquierda.png b/app/examples/Games/GNUBoxWorld/izquierda.png new file mode 100644 index 0000000000000000000000000000000000000000..4424149335a9c5e67a7b5b1dcd352f916fd5aa60 GIT binary patch literal 4350 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/logo.png b/app/examples/Games/GNUBoxWorld/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1124d398c176adb4b36132f69d04bf00fb5d66a0 GIT binary patch literal 108170 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∋|_EhT@74dls>cXL(_ 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/movible.png b/app/examples/Games/GNUBoxWorld/movible.png new file mode 100644 index 0000000000000000000000000000000000000000..7d63ed940c23180fe9e1fed18c201cc1ca8dcebf GIT binary patch literal 3831 zcmV2x2T-}wsvee?uUS5twK76Qp@3$n%}W*$+-n2-vH)T#1MegNeL01>*^ zDCj!pHKiE54zAMKKv7)3dKvG(e;xbXD|E9d78jQ>Kbgn;`~uQsJi+-37qGsuiWA3A z;nCCQ_{(2EhUx((Y2p@VZ>UyTvnR@e1_CVx5Xoqe=uXvaK9!pUwwcJ=T9T05uQJPhD(<&BdxA|kK;=xu)K5@l4zj}ztWCE?i3xYr#j2X(slSARC28?iNP}%ak&t_3) zW2DHWHYbgZ?g#M@`#_$^JuWLTGyQbXD_A+`orBK4$4`AVsJ z0gA4h(NuyLs~cE6b{y&Mz58W3wah9c%w#e)?`spUc3mCvmH=y_wDj)IsU;G%TGRoO zy(6vi0YIMQtQZ%RX~~$P)d=}kRo+dc5Cxun`x5EV<7Zw@)XSM;K{1|;;WyZuoB|oy zF%yS~0YViKsAcM-lcZM`D>BlI*Cygb&~;rvU^aIa>d}*DNLyRG<-3@2Vl(^C$vFLp zDC^%FpiaHSbyQNt&^-lJgE#>{({&x73K1Trxx@lkTi-<5-F=0WQh|>rFb$~7aI)$! zSo4NN{Xx?E|BY=agvsRQ?z)-X&bfvj5kOS1_i7JGOQ48HM}sWM^}bx3j1w_9G$B(L zR6~?%-zZL_9oj-`JH>XzGOLUv%Q02O%@0hwF37|M#|@!SRiq*@nMb1lB|(}sMKDgu z3=;E(Iy?iF9il1~!!-whN-Y_-VWY|wrbV6r(`gsOK_XC`<7Jwj4EP%6v4n=g=Y7ij zWjvnnZ0yVsbB#3(ZCa&?CEuDPX{(O%_c&kuO#6F zvbO|^@p#;8lr;1TZJ1bvk3rE?O>g8Rn?GK&H)= z6~^k_2ybJ!D8_rfbR-e&e3cbWr!!NPk;eimu{+icKsJDRy6Ix+ai2hJxzGiV(}cY3 z49}`ce+PtOhS%gk(ZR5(b5_sWUIB8oU?S;uAr2xwU$ToeVGz5JMmX?b;_oF_Vw@&{ zs`eVyw#GGGjua{!_7ehtN<9+EF>kC?-BE?+dfuxgcPRY03{qM1X~n%Ao@O6$v!fWN zaoEzbY3mXLx>@35^pdgEN<$tuozD8h`Wv#*vQCBqA22&ngoEFcASS~_oj%6x&zPjPAnLJhR*jlxQYl$lXq*<_1_HolxuWBtHN}Nc@ z%dL_aFi5!OH#Gi7MiO$6_amIG6UVhSImt8%wvHjtdNi~Qf=B>K#G2uynWN1WL4WEV znH^SpIAalaiuk^v7e%jPBc9ueyy@#3jjll5Vj9AxgdePdIZb*_HK3_9(`aK{+S#C- zJWytN?y~Dlp_~hl5)+ghvkkHuW=~4irX^^?q@%3}K4D;geYR`kINl)<84;7(d~ai> zPU72qqVDh%rs*a8>Xuk}wQ;mHJ0@IYear<|p%yqcc3@mB&$X;64GcBs+B$}}(Cct!0;Rza(tu8V*{lt$o3l_M zLAE4Fl!8&CGEBdF-r(2%^}kZGN5YFpUnxZ-@ro)om zVFxKp%(g-y9@s#6pV%@y@@R4xW`4*}Q*vM6+rpltY(@}dlEX4Ehi~O-$B?z-oF*Mf zPy(FExRh7p8lWY?;=V0TBsTu0K@MRr*u=5q8BvUxl0|E-@fV?I55u*;U@<_{yg)BU zYPsgqFP@^M^a5s>>e(Pt5Z2Mt=f^^vL8Kl|AVUcd1EK%%>LDH{wMogoI`C&u&^?REFMNqtfjrWKe?c@i0bGWrX#hhItJA7Tx)s z>u=loWj}zuBCV^+X{(ToY{FVj@;_6)r&K#Z5ds#WZrZjUsy0toej+xvlAPjRL)hhM z>SqnL6o?17S)k(y;XPnJS`tq#`ys^WxR!k8m@tLQ9NIi-$6?@atm3XtvmqApjB&t0 zR5|s|rcX>2BmE@q+z=sw1ai~ynVi8q=iPb#ItK3a6JbC}Gv2F~n=|w$geYZX!!|`F zAf*>}^cGf8;g$zar(K`l4;W%dMHZf4wtDW!88o~+4M{LE*DNd3PATstJZoo=cv487 z=`%cAni`17*nIVCX*8lz0cLb< z&2ghw*hePEDhem@khHAaiYGzG;84CF;6Y_#uUJaJYY30VG|O|yNRSp67BKB*;1^wy zehN*i=(;J@a#Fl~)BA&;!&qU{Hucu}aUYH#2^aFbs)B|23DWW7OL+PHngvKqMAiFQ zC_Z(~q8=5R(u@THs#|rV@B?cM^iI7sn73FrsD1yucJ3_FJ1cKtb#>j629@^ETIK^u zbux`o9O2jq@>ut|0@S}p;Bs2uGpga+Q<~9S``aS%en9-iZap*-AW^QLJ9850+?f;6 zimtsal4G9!0*qhl(AJJ8Cm_I=--h-M3J~ntzcD~F4;!yJS?T$Z*Udk9Z_-hy_}SGf zNN3NU#!oN3g@;eRjjk(#PrIo%Dacm#Z;j)%`3}~b8!knCJso{oyl2XK9TrrC`ax@} z1TLLDV}FNW27}{IfVVEbj=7{r^Yin#e(ha6dHOBdD9K0B8n5)(%5Z=NQ{c!X>ApQG zo(@Zzb%Nph-iVHZ^QX{tQ>4AUJm@#-1*|ZDb+Q{Plp=0mTI@$Whj9i zmCs}6&~_6?o8d6-6vyO*@bep2aQ)h4tgWvht*x)(eJSGHPGDgk37@O_paf}<+m|T39p^|3DU~S zyV&19!1m@QKK$h^EH5wPZy(*pftl44O=wcutWv+UyC6)D%{s=1`~Ks^$O~|CV@_G~ z>S6FJpOFYYxOD?pFTaD0jdh$nbpkKGdx13TW`GEmmY1=%zK)eQ-@w`5oy5og_zaJq zJST|Pew{gdu`c!YAxBvH5K89c8I3)n{k^d*>6i%lnW~YRZ1osz8+L+AD{tbxUtGnB z%z~%| z+Q!b#E2w!Q!ngh%;9&d_&+i>2W~~4#x2^?o0Af169}+bd2o@J7ID6(aR^EOSD=QZv tP;70k26xU;9$UPMi`2?oGRxbPp&gBcDEUt%;`!eDWL!C;7HabXb`E?&ge_6Am#&tU!8Px#lrKgRJfV>}vm zT+3B?`>vi7fmyuErd=;g$Z5lF4x6vcB^HwT_(XIBA|U9h<|*D6z%Vh0 zNxjd($d!K$@1u;QKwc;6F<7Z3PY|0W+-Qxo3MX2fO$$`cvcsk2<1? z1YbT8_S&hf$!s!LD{OD=pqWi)7>)*23f7Au#>5EYk#i|KNEJg!u6NT^9?fJLE5=yy z#W2xW@rKQdAWR1Um`u?n8~MCsFKS;s-s=?npkGO56q%i_n!Kx2+9c~$JesT^Zl9i#EkiR{vwL}X=a zE6Nqq=pH7$hF#B?-Ya3wB5T`Dp*4%^Ex?qV${=PmT9pc#svwcP$d@PpsJBm$#9L5r z(O<&Dxe%grUv2N8^})J*9fkTat{nM&n_( zh!g7v2=+w$N&ST)#NF#P@jQ$Y^TO~+G4^Vi*>VJ#xKzK~0ll1;}FeY#(F;os zb*D8x<~`{hzyhlTHcC$buI~6=4fFxIyoL=L4c--I-dq# zvH`r;g^gl6WBUn?;AAMr8s%;joyoSx7NRp( zFX=m}6EZi(f!%)MHG0>s&U@awg+%0n0B>lvQ1Fu$4qz`5yBny)iO%g+p&(hYtl^75 zl@qM2)#4}=9cCd(t%XC$rDCfG+IAYO znQOzsHFXkvT#;8i10r~{7{L$F^d>cl^d9vyMFCail8q?)Ock25Pvn%WHe*)nu@^+| zsOQoQ5$T2nxGzqA%VFYEq4Am`%}8OzWN77NPXr9i6(>pXaMCRJz%p81u7|xS+WR*j zp`>vsuE#!A;a4?f60U?f&x+hpN@~tdwS*u73w{h|rwxF-bJQ26ng$X1M*EHqihT&A zSpvL!EF9`WA}dt@J&+cGv=(vGpf^PZ8DbaCPM@%Q|G+x%8paDK(O5NZAFkos}1-U{jdG%bdb%YDwdN7`hCn{(M| z9t1m{Lz*ERz_x@R$t>5{?d?Uc^m!_8_?*V!pHBvUMs4Oq zDGB4As@D1|Qb7v`^zd8OSJGbO1rIwmcKTglK-yv0_Jn7~TFa!h)qy|{8IojyGE&K2 z(oHA$MQ7^g2EfAP)QN!W*0^uZx=<_n+LNukq2e8;y#RYoQTX&JS)eAHBWz#U?7GTn zLx7xoNn>Ym*_GD2)R`Q^v&!udU{|SHM^vqwGNInrG)c0L@_Xlku=8Ti0lb!BD9KPr z5kYpUhjBoikXZ6;D6(uK7(zS(JmgQk3JiuV3NslesRMnrSU-EWs56~-jUP>ZXzK3t z*aJ1Qw^v*F8lKTpC3iTwdyNs{>(Y^>$Y}1osS)>(>ZYjFa;29h2lbeE{jM>ku}UYDkg1U7|Oq08Ms@nK#3N;WyT$%_lbz1t^s7%C4|W8*+Pio#X^o zrz{--?R16^2V!e^jpoGdtoT-Nc!!MPILV+8`}=%GkavvYN;Z;M9*k5H?0+Y4 zhm8|3Fs<+8bOG&IB2_8=fo5MM_(w~@;G`iDT5~_QZu6g@4%G)EmRr3)yh#l@?C*Aw zP+%Y566x!x{j=bhI5~ORJZ~f7ykvJOVq@5+<|XR@vS)+cZS2~71Zm<`S#7G1t5nN0 zFn+Y>^JZ>(g{xloo`Hr&kk1)JiAU-Io1^hlblc%nYo~UTATbYpI3m9h@UHYoH{IRq zwQLtuxbW7gGp-`TheZ!Z<;-KSa2qRJ(xAtNIh_4a{j5)ph4A{`{0L@>8!J+1l~$EB zvr^3hY;w9#N!?l!OO;c9S$%HRpO@3-Yp7bn!%YETIdMs1o^4wy$*3Y^`?%-` zbm47477_kk;85fV2-)t58Mz`S)D`L32bFF8W;_~#kJ$mv`-PG{xTZR-WP-#>T4?zwj_u_5llaP(v6C*%(z|NgJi{|>ZEBN&O7ZH;_RI0LV zyB~3x9TSY_z#7JMfb5XL{UFHq`@STNiFHCOxB`+Z6wbY|ist;;m1;B67l&l>8Y)`h zDSu}h2o)sVQiK>mCSLH?_NEDD%KVXR+RF`LP_`;?YwaeQbLY?E#`Vj1_WXs#j)RZ9 zlViUMf!6f?mtmoN^WJwB*RS-sp8sW;P=_={-J(im&}H0>9T{YPS8+INUVQ6K%+E8L z!C-*fx31&)^Pio}kENKd^eM$@Dt|wj*9lBkhhjVP6ECLLT?07*MFCxO- zJ2!FV?Tgsn-oomc6}%p4c z;I6%n>R-`E___d&gD9i~FGmmC6L9tNCEU4l6U&n^j*kv-cyxgAXo8Cu2WS=-7qPpy z13m_JcDHeOaEJ?UUBF*I{0nw>r}*ys2YB@ODRy>Rw9~2mic~r_io_(@0Tn65n=V*$ zX^eQulYBXe^|HrVFnXP2jtGO{5a-UG!PP65aP`VX0F2$8EgX*)am>J()i*Gm&2V^d ai2nh3sd068KCmkQ0000{OZN=sd-l$xEXv8k#1b-Uf|ZvXG@^L(D~J$!3i)}7#to3hY{eg z7$hIXc7VujY9EHlKEtE8kx)^gGFpgWMmQ!ABRQ*G#Wss19X~* zP8U!pi8x$58gr4$xy0pM^8;DF#{GnqmPMJW&{g+irJpcDx6gn~SwP|D|XBcn*Z zzS`_;tvFkoE!K*~1tO7*&&y^ql5n^Rxx7NIs8Gl&6pAZy`4t5SMVUmR{-oh&F`0=t zd^{Qq>B|le2Wa$b8ci*TA`nG&8W5UlReo6xs0U;`&?_a3tq(qsRL!eIN#cOrYK~IyJX^bFN~^PU$_CG(3zg+gO)D8 zP}Q#E5=xPRI%QY&ycgToU1L3w+IHD6)!4Da=TxVAqruIG(w}S`=Xk~Ji{34Mpzu!~lGxys6jF7$!sdM}@cD%z4OGH1qkKOAyI}6t++Gl|Z*(-HZ z%M~}zV{P`Ku1yB@fn9}G+3}fBOse|FHTg~M5h=dK#{JHdq+t@QcDRMUek1JAE=In4RqS=* z>*QzHuG2v8Na3yb0p$%(XoCKhGmGYHCUY@y-;<$nN}AT6y(;~|D9kYiOI9q__V08k z6Q`bO{&fVKJ4nf$yVdYy>TYG5WnuqH`gBjT!tCp8O7@(bA?daIYoPDmA#a>Ei_dZF zj5k5A^&%e7Ju}mUtb5u%Jf&IO>VEGbU)rfSX3mA~^GcX(JE{J7*j4Y9#%;yY{Rx=n9r7U>s^du7T~O{ngT zhjV{Z><~d0%I_X^`^=}OcjV2(xE@Yx{m|z9o{ZI#0)Zf>UtH>6*2SZ+o?Zf z=s&DY{bZQ71$nL{bNN24z*J{qZM)gMWG7LazW@#~ghTRdXX|ece{U|j{m0^xsT|Ys z%EPO+ew~-rX`B1Ku|77zQMJ52WLw2cisz0)Cy)7FOx-K%I5B<4yYmCGp~VaCo#xGF z&&;jvD(ATM+3Yf=IrVFeadcILRAv|>&bPcsR2gJ18;$*7W;Hleh1LGIcSae$eK|Ir)(df;fk% z+gCRomTes`%-G(#F3Vn&u|0k5wV|)FYRJ`=D=At;u4oUJ7K^^3{vHZXH9@&g%)S-HKEB|Q7`v8})G?AV?qzlWBj zkR`vzmZZ!9+w+36W0fEMy8k))`{MuMTy8Fpm&@bka(Q_?ZXS=96(7$cQ~cm?g;1yv2^B(- zLL@2_2(sfjLV8Rz4p%9WR7#|kQc0y$S|yQGNdcs#85y!4J@Kh@It_;pMFA$^u|y(2 zGO9r?Zv-d?C~lGi$ZPWpO2t4wKbr&WB1hwKAt@G zDnnez=Vt-iW9T$IPL+|NRw`?LD2PMQXtheE5(5o_nu7c+PJA>T?*nm^@%fGN;?x+r zn8nQFa?go`%}QkppjMDylb@f>;i$wJSsV_VOeqxbWqbh@iAbf#q{hT>j~{1K$Skry z%?syy2u3ya)q_CGC?2i|f?rQ&!8m8L+pnS%r5eG3Jwvu_RrclQlbe&-Hf+Q$nItT? z)r@Z2ydyRFxv4A0!24KeheK;r9dVKR(%-WhIvpO-a!9S2gcgl=r%1xom>f2$Wzbz6 zmj-)!*Ztn@dSm7LxGj*&T9fLDysb_Bdt?)m@IL=)t&XjUy<+a!XKsg9Pt~PU^zrLI zLv_#QM%vTM+Tbrn;OTv@p-o@DwwpiRI=waF?Zu0T8|-9@0-c10@AI` zp6Iml0_^f5_AYklI9b_>f{VL++3<4@_gjv?!=gr9hG*+E{nf*<1+s0LrDpG_of68O z^-~p^x2seC9-!H_ZRl+`_b^#5EF3ZtHg`7Dsn-&F_Bu2sk)G`K_3olI+|jeRt2lZAuczx);H%NYlB zTZ(t*C?Ty^;a}c;K!ks~zKOdKP{A7!B|zn;6Y7a~Hj%)bPpT*T7kUuTRSVR*X=b5- z!>^y3S~og-_4CyLR#KR;{Q>1`UH{HA9^fKH^DM6k^W9~g8hRyZy~zN1%{21>i{x*` zqIN#C@==(*0Y5Ev={5xe_bWdVEj4qR2{zVSSi#j$Coz2k&Z!OMA#Vc1Q<;6uG*~`Q=VlM(dm$}B&-VU9U+rC>K*f#u(;jl31GXE`c`c+SezOcP9 z)(i?wD60Q$k4#v8@BGZ$*nKv}*5w}hVcOWKizx}vb|Z^NZ`+*z*Z?s^TYIhe?4?v3 zK0|19eNuYM$qwyMJ==0|U{_zCj#oWakNXP>RU&rPpfXyUR_|c zZ?Tx{?fy7;Q!{*W!-!sCdE(npu;SLE?D5lW%}FK}i?4N~w?1FL0KV{5Jeh>I44#uE zT8$d)_WE}CfuY~`iA literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/obstaculo-r.png b/app/examples/Games/GNUBoxWorld/obstaculo-r.png new file mode 100644 index 0000000000000000000000000000000000000000..fda4df00bd665740b2a2df6a6166c580bfd9047b GIT binary patch literal 1627 zcmb`Ic~H`69L6cT#@2KQNHsI8>i-sYPc@{zhIh+I!Ai?2eID!m^lMo0J0!~K26H%x{ zG&&K5rlHV@XcP^FibEoIZNsPH@u?9JsrZQ02>dQkc>HcWXd;ra*w`RsI+>DAp`=r( z05~bsbSfo{NKC?FVv$H18qME9=hNxgbUKd?nw|UNQy03c*AlmdZLC{zjsN`bIUC@2#Oi}`$Rd_rtckXj;9 zOC@TFR4tWOip3>-o&?~q355F6()vUp*UVzdRzfYp_j`m<`{lH0HhI!$r!L? z&|V5f#AKbWu5JcZ1FGgsHK^*7s&c&y@$IEqH{y*wj zkL&sF`oxY?EHind58?AmquP%iS4d@iNy3-VIBK?0iG-Vp@9I%_|H)-Lk&Jv^l>u4PdJFCx2{*C)n?7 z00Wiq?>0?NRnUthtS_lwfTNbD8-87ZE0}vk4{7+_rSR(M+o_kQu$P~uZxJB8VM?oQ zprX^9)P|<{*kS951HCU8+nu8@%9n=w_pHpt;|TRa`%_$+o1?5I&!F_ooA!*3(jnK! zxR2aP)^sawvs}xm*{>(h(*RY8Sov% z~C>B488fB+r<1z8kx+7 z;-dQl`!rs44o?I7@|Y8V7eh!2nrzR@hOW)HbpA=>{p+odkM%wjIX2~Yc8mf1OD;=0 zgO^JUkGz|$n}d5k>#rbWt!@v8_NIJ=lD}FrJcex=)IqY(kk_~D!-R)21{WXO60&*R zhUmsGjGhg-w|K`pQn16syqi^S1%E@CZ~lRF>M79)dqU3)NP6wgT{UP^=J$W#R}d3m kh5v5fFxmL89-n7|TVo#NFrM0-0Y420E<7r%8NE~d7fVJ3mH+?% literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/obstaculo.png b/app/examples/Games/GNUBoxWorld/obstaculo.png new file mode 100644 index 0000000000000000000000000000000000000000..8ee35f44bf47b01b373908189a1a435b0118e8cf GIT binary patch literal 1575 zcmchY|2NZn9LLwKi|UHhJ$2osBIy#Uiw(2d&e~$q#-^Fgkg&0dEnC{KVY`}a!+hNs zvhtnHx2{sI%83dk^hITEb@C+?CrNcb{S)_`$LsZe{_s4{*X#Y$D--93G2LLf0R#e> zdLKji1MiU^WwI9N>UQA_2(+dj=Sw&m0C5X|KnM^h0RjntLIa=>0u(|;AgD+r6@jE6 zkW?gsfID9x7SkmhZktmF& z*B2Kz0F(ffG!_FC*C_MLrGQ^3;{vnDF?d`U8lx{O(`(A~Wg1;+X*dS0F9mdIjZ&$} zk`@Yu*}!xri-E^!vaO`O0dgQpV+Kq*>WqE{7akC=wP3MHD0=gT>5XGWn-Yae~QgauDML z?xZJ-Vt#S~1X{Pv8-*Z{dX)KJxRyt^rp_UIL@rK)_8r=Tl~+=mQ#pTd5DqeVL~ff! zh<&qdM(VgZ3cJ?-czEmMJ8sO@sj_MHYp^UTx#-WHnR_=OlB*v@W=C{SY`r~fp1j#3 zp;j=z{N`QE9&2K@KkInzL@C!z2EUvq9^siIV)qyYK;MZ4St*L z=g$4+T2`^os&VdYqhTp&z~LK6mNs5}t-L0#W@AlKcVA1BJ(0?f$()Cdy$BrUisp>y zNzU6>Q@$qe1Mh}yN-Q{Gn%uNHacFPtvNJVH9KHaO^{8foO`KBlIxg9|I9Ai2R-q16 zQ@iTu)tr4`GazUDf+c07fikq@iyla>i_!&AzoSMQwH0M|tlh1Q?@r0@{k!q@s!tb5 zL)=Z9{^(|JF1n-SZ1+rX+2sDd!(S1B3^@{3`fT z?X^SGgL9wWFD1R{9@}sbR`fQ$=`P#@=@u88@^Wryt~a7h`>D}@2$F5Fl3dmIS*s`e zTKB3Ab&}6L4@P9$4Poi?P;ihmZ#aIP!>#jURdH>1dTXJ`IzOI@Rrzap_j2ZgY^TlH z2ezA?ti{9VYpf(Tms|Ua4Cvg3!MZK)KQcXfNve71H|wAsi>ou5dqxM1J%eYBc@GWg zz3-eY5|=+0NYIa5b{5?zb}2DFt+Fvy%tMbkB?Y-$=T)8^WSEj~m>Q*xO!ihjTz|cR zXW_02+3Rpi`5~HORgkjt_~q9_9GIh55ccrJv+HG()OiEkZZWL7#T5n@xLr$Gnb|r$ z>Errzy|XJ19P2c#YjLQxvs3zXCiv6a9h7#3^@2^n|Gaw5owfdt8B^U-MjwMP8WQ@o zH}-dTuvrdwcVRZV@?Erty3J=r-1hyK`V%Iy9z}mGe3la;_E6*O(rrh|3j!a1t{pK2 z79a5HY#!gPJpk`)?jg;xps@mf&(%&W54dheW74Jy s_{{|Kr8W6!SInBgC_(D$|HHCM)1nL`gb$0)fdc^YKI(^RK!%F`1JGmal>h($ literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/piso.png b/app/examples/Games/GNUBoxWorld/piso.png new file mode 100644 index 0000000000000000000000000000000000000000..e35e5a442aaca2d400a5e038c3235126e476f16f GIT binary patch literal 10046 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GameOfLife/.lang/ca.po b/app/examples/Games/GameOfLife/.lang/ca.po new file mode 100644 index 00000000..ecb1d53a --- /dev/null +++ b/app/examples/Games/GameOfLife/.lang/ca.po @@ -0,0 +1,131 @@ +msgid "" +msgstr "" +"Project-Id-Version: GameOfLife\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:58+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:265 +msgid "0 Neighbour" +msgstr "0 veïns" + +#: FMain.class:258 +msgid "1 Neighbour" +msgstr "1 veí" + +#: FMain.class:251 +msgid "2 Neighbours" +msgstr "2 veïns" + +#: FMain.class:245 +msgid "3 Neighbours" +msgstr "3 veïns" + +#: FMain.class:238 +msgid "4 Neighbours" +msgstr "4 veïns" + +#: FMain.class:231 +msgid "5 Neighbours" +msgstr "5 veïns" + +#: FMain.class:224 +msgid "6 Neighbours" +msgstr "6 veïns" + +#: FMain.class:217 +msgid "7 Neighbours" +msgstr "7 veïns" + +#: FMain.class:210 +msgid "8 Neighbours" +msgstr "8 veïns" + +#: FMain.class:291 +msgid "Alive" +msgstr "Viu" + +#: FMain.class:296 +msgid "Dead" +msgstr "Mort" + +#: FMain.class:302 +msgid "Draw cell borders" +msgstr "Dibuixa la vora de les ceŀles" + +#: FMain.class:72 +msgid "Evolution Delay: " +msgstr "Retard d'evolució:" + +#: FMain.class:199 +msgid "Evolution Delay: 500ms" +msgstr "Retard d'evolució: 500ms" + +#: .project:1 +msgid "Game of Life" +msgstr "Joc de la Vida" + +#: FMain.class:147 +msgid "GameOfLife" +msgstr "JocDeLaVida" + +#: FMain.class:317 +msgid "Horizontal symmetry" +msgstr "Simetria horitzontal" + +#: FMain.class:276 +msgid "Options" +msgstr "Opcions" + +#: FMain.class:272 +msgid "Select the Count of Neighbours where a cell will die or keep its state." +msgstr "Seleccioneu el comptatge de veïns on una cèŀlula morirà o mantindrà el seu estat." + +#: FMain.class:280 +msgid "Set here the probability that a Cell will be born or not when you spawn the first Generation." +msgstr "Ajusteu aquí la probabilitat de que una ceŀlula neixi o no quan s'engendra la primera generació." + +#: FMain.class:307 +msgid "Small generation" +msgstr "Generació petita" + +#: FMain.class:176 +msgid "Spawn First Generation" +msgstr "Engendra la primera generació" + +#: FMain.class:194 +msgid "Start Evolution" +msgstr "Inicia l'evolució" + +#: FMain.class:206 +msgid "Survival" +msgstr "Supervivència" + +#: FMain.class:136 +msgid "The Game of Life" +msgstr "El Joc de la Vida" + +#: FMain.class:312 +msgid "Vertical symmetry" +msgstr "Simetria vertical" + +#: FMain.class:164 +msgid "" +"Written in Gambas
\n" +"by Iman Karim
\n" +"and Benoît Minisini\n" +"

\n" +"Thanks to the Gambas team!" +msgstr "" +"Escrit al Gambas
\n" +"per Iman Karim
\n" +"i Benoît Minisini\n" +"

\n" +"Gràcies a l'equip del Gambas!" + diff --git a/app/examples/Games/GameOfLife/.lang/cs.po b/app/examples/Games/GameOfLife/.lang/cs.po new file mode 100644 index 00000000..9e616257 --- /dev/null +++ b/app/examples/Games/GameOfLife/.lang/cs.po @@ -0,0 +1,130 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Game of Life" +msgstr "Hra o život" + +#: FMain.class:72 +msgid "Evolution Delay: " +msgstr "Prodleva evoluce: " + +#: FMain.form:37 +msgid "The Game of Life" +msgstr "Hra života" + +#: FMain.form:48 +msgid "GameOfLife" +msgstr "Hra o život" + +#: FMain.form:65 +msgid "" +"Written in Gambas
\n" +"by Iman Karim
\n" +"and Benoît Minisini\n" +"

\n" +"Thanks to the Gambas team!" +msgstr "" +"Napsano v Gambas
\n" +"od Iman Karim
\n" +"a Benoît Minisini\n" +"

\n" +"Díky Gambas týmu!" + +#: FMain.form:78 +msgid "Spawn First Generation" +msgstr "Vyvolat první generace" + +#: FMain.form:96 +msgid "Start Evolution" +msgstr "Start evoluce" + +#: FMain.form:101 +msgid "Evolution Delay: 20ms" +msgstr "Prodleva evoluce: 20ms" + +#: FMain.form:108 +msgid "Survival" +msgstr "Přežití" + +#: FMain.form:112 +msgid "8 Neighbours" +msgstr "8 sousedů" + +#: FMain.form:119 +msgid "7 Neighbours" +msgstr "7 sousedů" + +#: FMain.form:126 +msgid "6 Neighbours" +msgstr "6 sousedů" + +#: FMain.form:133 +msgid "5 Neighbours" +msgstr "5 sousedů" + +#: FMain.form:140 +msgid "4 Neighbours" +msgstr "4 sousedé" + +#: FMain.form:147 +msgid "3 Neighbours" +msgstr "3 sousedé" + +#: FMain.form:153 +msgid "2 Neighbours" +msgstr "2 sousedé" + +#: FMain.form:160 +msgid "1 Neighbour" +msgstr "1 soused" + +#: FMain.form:167 +msgid "0 Neighbour" +msgstr "0 sousedů" + +#: FMain.form:174 +msgid "Select the Count of Neighbours where a cell will die or keep its state." +msgstr "Vyberte počet sousedů, kde buňka zemře nebo si ponechá svůj stav." + +#: FMain.form:178 +msgid "Options" +msgstr "Možnosti" + +#: FMain.form:182 +msgid "Set here the probability that a Cell will be born or not when you spawn the first Generation." +msgstr "Nastavte zde pravděpodobnost, že se buňky narodí, nebo ne, když se Vám vyvolá první generace." + +#: FMain.form:193 +msgid "Alive" +msgstr "Naživu" + +#: FMain.form:198 +msgid "Dead" +msgstr "Mrtvý" + +#: FMain.form:204 +msgid "Draw cell borders" +msgstr "Kreslení okrajů buněk" + +#: FMain.form:209 +msgid "Small generation" +msgstr "Malé generace" + +#: FMain.form:214 +msgid "Vertical symmetry" +msgstr "Vertikální symetrie" + +#: FMain.form:219 +msgid "Horizontal symmetry" +msgstr "Horizontální symetrie" diff --git a/app/examples/Games/GameOfLife/.lang/de.po b/app/examples/Games/GameOfLife/.lang/de.po new file mode 100644 index 00000000..ab364aa0 --- /dev/null +++ b/app/examples/Games/GameOfLife/.lang/de.po @@ -0,0 +1,121 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Game of Life" +msgstr "Spiel des Lebens" + +#: FMain.class:72 +msgid "Evolution Delay: " +msgstr "Evolutionsspanne: " + +#: FMain.form:37 +msgid "The Game of Life" +msgstr "Spiel des Lebens" + +#: FMain.form:48 +msgid "GameOfLife" +msgstr "-" + +#: FMain.form:65 +msgid "Written in Gambas
\nby Iman Karim
\nand Benoît Minisini\n

\nThanks to the Gambas team!" +msgstr "Geschrieben in Gambas
\nvon Iman Karim
\nund Benoît Minisini\n

\nDanke an das Gambas team!" + +#: FMain.form:78 +msgid "Spawn First Generation" +msgstr "Erste Generation erzeugen" + +#: FMain.form:96 +msgid "Start Evolution" +msgstr "Evolution starten" + +#: FMain.form:101 +msgid "Evolution Delay: 20ms" +msgstr "Evolutionsspanne: 20ms" + +#: FMain.form:108 +msgid "Survival" +msgstr "Überleben" + +#: FMain.form:112 +msgid "8 Neighbours" +msgstr "8 Nachbarn" + +#: FMain.form:119 +msgid "7 Neighbours" +msgstr "7 Nachbarn" + +#: FMain.form:126 +msgid "6 Neighbours" +msgstr "6 Nachbarn" + +#: FMain.form:133 +msgid "5 Neighbours" +msgstr "5 Nachbarn" + +#: FMain.form:140 +msgid "4 Neighbours" +msgstr "4 Nachbarn" + +#: FMain.form:147 +msgid "3 Neighbours" +msgstr "3 Nachbarn" + +#: FMain.form:153 +msgid "2 Neighbours" +msgstr "2 Nachbarn" + +#: FMain.form:160 +msgid "1 Neighbour" +msgstr "1 Nachbar" + +#: FMain.form:167 +msgid "0 Neighbour" +msgstr "0 Nachbarn" + +#: FMain.form:174 +msgid "Select the Count of Neighbours where a cell will die or keep its state." +msgstr "Wähle die Anzahl der Nachbarn, wann eine Zelle stirbt." + +#: FMain.form:178 +msgid "Options" +msgstr "Optionen" + +#: FMain.form:182 +msgid "Set here the probability that a Cell will be born or not when you spawn the first Generation." +msgstr "Wahrscheinlichkeit, dass eine Zelle geboren wird, wenn du die erste Generation erzeugst." + +#: FMain.form:193 +msgid "Alive" +msgstr "Lebend" + +#: FMain.form:198 +msgid "Dead" +msgstr "Tot" + +#: FMain.form:204 +msgid "Draw cell borders" +msgstr "Zellengrenzen zeichnen" + +#: FMain.form:209 +msgid "Small generation" +msgstr "Kleine Generation" + +#: FMain.form:214 +msgid "Vertical symmetry" +msgstr "Vertikale Symmetrie" + +#: FMain.form:219 +msgid "Horizontal symmetry" +msgstr "Horizontale Symmetrie" + diff --git a/app/examples/Games/GameOfLife/.lang/ru.po b/app/examples/Games/GameOfLife/.lang/ru.po new file mode 100644 index 00000000..d040922a --- /dev/null +++ b/app/examples/Games/GameOfLife/.lang/ru.po @@ -0,0 +1,158 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/GameOfLife/.project:18 +msgid "Game of Life" +msgstr "Игра жизни" + +#: app/examples/Games/GameOfLife/.project:19 +msgid "" +"The Game Of Life.\n" +"\n" +"This example runs the Game Of Life cellular automaton. It allows to define many parameters of the automaton." +msgstr "" +"Игра жизни\n" +"\n" +"Этот пример запускает клеточный автомат игры жизни. Это позволяет определить многие параметры автомата." + +#: app/examples/Games/GameOfLife/.src/FMain.class:72 +msgid "Evolution Delay: " +msgstr "Задержка эволюции: " + +#: app/examples/Games/GameOfLife/.src/FMain.class:72 +msgid "ms" +msgstr "мс" + +#: app/examples/Games/GameOfLife/.src/FMain.form:5 app/examples/Games/GameOfLife/.src/FMain.form:18 +msgid "The Game of Life" +msgstr "Игра жизни" + +#: app/examples/Games/GameOfLife/.src/FMain.form:14 +msgid "GameOfLife" +msgstr "Игра жизни" + +#: app/examples/Games/GameOfLife/.src/FMain.form:28 +msgid "" +"Written in Gambas
\n" +"by Iman Karim
\n" +"and Benoît Minisini\n" +"

\n" +"Thanks to the Gambas team!" +msgstr "" +"Написано в Gambas
\n" +"Иман Каримом
\n" +"и Бенуа Минисини\n" +"

\n" +"Спасибо команде Gambas!" + +#: app/examples/Games/GameOfLife/.src/FMain.form:39 +msgid "Spawn First Generation" +msgstr "Породить первое поколение" + +#: app/examples/Games/GameOfLife/.src/FMain.form:55 +msgid "Start Evolution" +msgstr "Начать эволюцию" + +#: app/examples/Games/GameOfLife/.src/FMain.form:59 +msgid "Evolution Delay: 20ms" +msgstr "Задержка эволюции: 20мс" + +#: app/examples/Games/GameOfLife/.src/FMain.form:65 +msgid "Survival" +msgstr "Выживание" + +#: app/examples/Games/GameOfLife/.src/FMain.form:68 +msgid "8 Neighbours" +msgstr "8 соседей" + +#: app/examples/Games/GameOfLife/.src/FMain.form:74 +msgid "7 Neighbours" +msgstr "7 соседей" + +#: app/examples/Games/GameOfLife/.src/FMain.form:80 +msgid "6 Neighbours" +msgstr "6 соседей" + +#: app/examples/Games/GameOfLife/.src/FMain.form:86 +msgid "5 Neighbours" +msgstr "5 соседей" + +#: app/examples/Games/GameOfLife/.src/FMain.form:92 +msgid "4 Neighbours" +msgstr "4 соседа" + +#: app/examples/Games/GameOfLife/.src/FMain.form:98 +msgid "3 Neighbours" +msgstr "3 соседа" + +#: app/examples/Games/GameOfLife/.src/FMain.form:103 +msgid "2 Neighbours" +msgstr "2 соседа" + +#: app/examples/Games/GameOfLife/.src/FMain.form:109 +msgid "1 Neighbour" +msgstr "1 сосед" + +#: app/examples/Games/GameOfLife/.src/FMain.form:115 +msgid "0 Neighbour" +msgstr "0 соседей" + +#: app/examples/Games/GameOfLife/.src/FMain.form:121 +msgid "Select the Count of Neighbours where a cell will die or keep its state." +msgstr "Выбрать количество соседей, где клетка умрёт или сохранит своё состояние." + +#: app/examples/Games/GameOfLife/.src/FMain.form:125 +msgid "Options" +msgstr "Опции" + +#: app/examples/Games/GameOfLife/.src/FMain.form:128 +msgid "Set here the probability that a Cell will be born or not when you spawn the first Generation." +msgstr "Установить здесь вероятность того, что клетка родится или нет, когда вы породите первое поколение." + +#: app/examples/Games/GameOfLife/.src/FMain.form:137 +msgid "Alive" +msgstr "Жив" + +#: app/examples/Games/GameOfLife/.src/FMain.form:141 +msgid "Dead" +msgstr "Мёртв" + +#: app/examples/Games/GameOfLife/.src/FMain.form:146 +msgid "Draw cell borders" +msgstr "Границы клетки" + +#: app/examples/Games/GameOfLife/.src/FMain.form:150 +msgid "Small generation" +msgstr "Малое поколение" + +#: app/examples/Games/GameOfLife/.src/FMain.form:154 +msgid "Vertical symmetry" +msgstr "Вертикал. симметрия" + +#: app/examples/Games/GameOfLife/.src/FMain.form:158 +msgid "Horizontal symmetry" +msgstr "Горизонт. симметрия" + diff --git a/app/examples/Games/GameOfLife/.project b/app/examples/Games/GameOfLife/.project new file mode 100644 index 00000000..250cecc3 --- /dev/null +++ b/app/examples/Games/GameOfLife/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Title=Game of Life +Startup=FMain +Icon=glob2-icon-48x48.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Description="The Game Of Life.\n\nThis example runs the Game Of Life cellular automaton. It allows to define many parameters of the automaton." +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Example +Address=benoit@localhost +License=General Public Licence +Packager=1 +Tags=Example diff --git a/app/examples/Games/GameOfLife/.src/CGameField.class b/app/examples/Games/GameOfLife/.src/CGameField.class new file mode 100644 index 00000000..3f998436 --- /dev/null +++ b/app/examples/Games/GameOfLife/.src/CGameField.class @@ -0,0 +1,236 @@ +' Gambas class file + +Fast + +'PRIVATE Game AS Boolean[] +Private Game As New Image +Private MaxX As Integer +Private MaxY As Integer +Private dW As DrawingArea +Private $iZoom As Integer + +Public Function Init(bSmall As Boolean, iLife As Integer, bSymetryH As Boolean, bSymetryV As Boolean) As Integer + + Dim i, j, n, size As Integer + + Randomize + + $iZoom = 3 + MaxX = dw.W \ $iZoom - 3 + MaxY = dw.H \ $iZoom - 3 + + 'Game = NEW Boolean[MaxX + 1, MaxY + 1] + Game = New Image(MaxX + 1, MaxY + 1) + Game.Fill(Color.White) + + If bSmall Then + + size = 12 + + For i = MaxX \ 2 - size To MaxX \ 2 + size + For j = MaxY \ 2 - size To MaxY \ 2 + size + If Rnd(1, 100) >= iLife Then Game[i, j] = Color.Red + Next + Next + + If bSymetryH Then + For i = MaxX \ 2 - size To MaxX \ 2 + size + n = size * 2 + 1 + For j = MaxY \ 2 - size To MaxY \ 2 + Game[i, j] = Game[i, j + n] + n -= 2 + Next + Next + Endif + + If bSymetryV Then + For i = MaxX \ 2 - size To MaxX \ 2 + size + n = size * 2 + 1 + For j = MaxY \ 2 - size To MaxY \ 2 + Game[j, i] = Game[j + n, i] + n -= 2 + Next + Next + Endif + + Else + + For i = 0 To MaxX + For j = 0 To MaxY + If Rnd(1, 100) >= iLife Then Game[i, j] = Color.Red + Next + Next + + Endif + + 'PRINT MaxX + 1;; "x";; MaxY + 1; " game" + +End + +Public Sub SetDrawArea(pdW As DrawingArea) + + dW = pdW + +End + +Public Sub DrawGame(bBorder As Boolean) + + Dim SquareSize As Integer + + 'IF GameRow.Count = 0 OR GameRow[1].Count = 0 THEN RETURN -1 + + SquareSize = 4 'dw.Height / GameRow.Count + + Draw.Begin(dw) + + 'Draw.FillStyle = Fill.Solid + 'Draw.LineStyle = Line.None + 'Draw.FillColor = Color.LightBackground + 'Draw.Rect(4, 4, Game.Width * $iZoom, Game.Height * $iZoom) + + If bBorder Then + Draw.LineStyle = Line.Solid + Else + 'Draw.FillRect(0, 0, dw.W, dw.H, Color.Background) + Draw.LineStyle = Line.None + Endif + + Draw.FillColor = Color.Red + Draw.Foreground = Color.LightBackground + + Draw.Zoom(Game, $iZoom, 4, 4) + + Draw.End + +End + +Private Function CountNeighboursOnBorder(x As Integer, y As Integer) As Integer + + Dim iTot As Integer + Dim i, j, ti, tj As Integer + + For i = x - 1 To x + 1 + If i < 0 Then + ti = MaxX + Else If i > MaxX Then + ti = 0 + Else + ti = i + Endif + For j = y - 1 To y + 1 + If j < 0 Then + tj = MaxY + Else If j > MaxY Then + tj = 0 + Else + tj = j + Endif + iTot += Game[ti, tj] + 'IF Game[ti, tj] = Color.Red THEN INC count + Next + Next + + iTot = iTot - Game[x, y] - Color.White * 8 + + Return iTot \ (Color.Red - Color.White) + +End + +' Private Function CountNeighbours(x As Integer, y As Integer) As Integer +' +' Dim iTot As Integer +' +' iTot = Game[x - 1, y - 1] + Game[x, y - 1] + Game[x + 1, y - 1] + +' Game[x - 1, y] + Game[x + 1, y] + +' Game[x - 1, y + 1] + Game[x, y + 1] + Game[x + 1, y + 1] +' +' iTot -= Color.White * 8 +' 'DEBUG iTot / (Color.Red - Color.Background) +' Return iTot \ (Color.Red - Color.White) +' +' End + +Public Sub SpawnNextGeneration(Live As Boolean[], Keep As Boolean[]) + + Dim x As Integer + Dim y As Integer + Dim myCount As Integer + Dim newGame As Image + Dim myCountSub As Integer = Color.White * 8 + + newgame = Game.Copy() + + For x = 1 To MaxX - 1 + For y = 1 To MaxY - 1 + + 'myCount = CountNeighbours(x, y) + + myCount = Game[x - 1, y - 1] + Game[x, y - 1] + Game[x + 1, y - 1] + + Game[x - 1, y] + Game[x + 1, y] + + Game[x - 1, y + 1] + Game[x, y + 1] + Game[x + 1, y + 1] + + myCount -= myCountSub + myCount \= (Color.Red - Color.White) + + If keep[myCount] Then Continue + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Next + Next + + For x = 0 To MaxX + y = 0 + myCount = CountNeighboursOnBorder(x, y) + If Not keep[myCount] Then + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Endif + y = MaxY + myCount = CountNeighboursOnBorder(x, y) + If keep[myCount] Then Continue + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Next + + For y = 1 To MaxY - 1 + x = 0 + myCount = CountNeighboursOnBorder(x, y) + If Not keep[myCount] Then + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Endif + x = MaxX + myCount = CountNeighboursOnBorder(x, y) + If keep[myCount] Then Continue + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Next + ' IF Rnd < 0.1 THEN + ' x = Int(Rnd(0, MaxX - 30)) + ' y = Int(Rnd(0, MaxY - 30)) + ' FOR i = x TO x + 29 + ' FOR j = y TO y + 29 + ' newGame[i, j] = Rnd(1, 100) >= FMain.Slider2.Value + ' NEXT + ' NEXT + ' ENDIF + + Game = newGame + +End + diff --git a/app/examples/Games/GameOfLife/.src/FMain.class b/app/examples/Games/GameOfLife/.src/FMain.class new file mode 100644 index 00000000..50bcfc24 --- /dev/null +++ b/app/examples/Games/GameOfLife/.src/FMain.class @@ -0,0 +1,95 @@ +' Gambas class file + +Private Game As New CGameField + +Private Sub DrawGame() + + Game.DrawGame(chkBorder.Value) + +End + + +Public Sub Form_Open() + + 'System.Profile = False + 'Randomize 1972 + Button1_Click + Slider1_Change + 'togEvolution.Value = True + +End + +Public Sub Button1_Click() + + Game.SetDrawArea(dwgGame) + Game.Init(chkSmall.Value, Slider2.Value, chkSymetryH.Value, chkSymetryV.Value) + DrawGame + +End + +Public Sub Button2_Click() + + tmrEvolution_Timer + 'Game.SpawnNextGeneration + 'DrawGame + +End + +Public Sub tmrEvolution_Timer() + + Dim Live As New Boolean[9] + Dim Keep As New Boolean[9] + + If Not togEvolution.Value Then Return + + live[0] = chkZero.Value = CheckBox.False + live[1] = chkOne.Value = CheckBox.False + live[2] = chkTwo.Value = CheckBox.False + live[3] = chkThree.Value = CheckBox.False + live[4] = chkFour.Value = CheckBox.False + live[5] = chkFive.Value = CheckBox.False + live[6] = chkSix.Value = CheckBox.False + live[7] = chkSeven.Value = CheckBox.False + live[8] = chkEight.Value = CheckBox.False + + keep[0] = chkZero.Value = CheckBox.None + keep[1] = chkOne.Value = CheckBox.None + keep[2] = chkTwo.Value = CheckBox.None + keep[3] = chkThree.Value = CheckBox.None + keep[4] = chkFour.Value = CheckBox.None + keep[5] = chkFive.Value = CheckBox.None + keep[6] = chkSix.Value = CheckBox.None + keep[7] = chkSeven.Value = CheckBox.None + keep[8] = chkEight.Value = CheckBox.None + + Game.SpawnNextGeneration(Live, Keep) + DrawGame + +End + +Public Sub Slider1_Change() + + lblDelay.Caption = ("Evolution Delay: ") & Str(slider1.Value) & ("ms") + tmrEvolution.Delay = slider1.Value + +End + +Public Sub chkBorder_Click() + + If Not togEvolution.Value Then + DrawGame + Endif + +End + +Public Sub togEvolution_Click() + + tmrEvolution.Enabled = togEvolution.Value + +End + +' PUBLIC SUB dW_Draw() +' +' Game.DrawGame(chkBorder.Value) +' +' END diff --git a/app/examples/Games/GameOfLife/.src/FMain.form b/app/examples/Games/GameOfLife/.src/FMain.form new file mode 100644 index 00000000..f2cf88ac --- /dev/null +++ b/app/examples/Games/GameOfLife/.src/FMain.form @@ -0,0 +1,162 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,125,95) + Text = ("The Game of Life") + Icon = Picture["glob2-icon-48x48.png"] + Resizable = False + { Panel1 Panel + MoveScaled(1,1,29,23) + Border = Border.Plain + { Label3 Label + MoveScaled(1,1,19,4) + Font = Font["Bold,+2"] + Text = ("GameOfLife") + } + { Label7 Label + MoveScaled(1,4,19,4) + Text = ("The Game of Life") + Alignment = Align.Left + } + { PictureBox1 PictureBox + MoveScaled(20,1,8,8) + Picture = Picture["glob2-icon-48x48.png"] + } + { Label4 TextLabel + MoveScaled(1,10,26,12) + Font = Font["-1"] + Text = ("Written in Gambas
\nby Iman Karim
\nand Benoît Minisini\n

\nThanks to the Gambas team!") + } + } + { dwgGame DrawingArea + MoveScaled(31,1,93,93) + Background = &HFFFFFF& + Border = Border.Plain + Cached = True + } + { Button1 Button + MoveScaled(1,26,29,6) + Text = ("Spawn First Generation") + } + { Slider1 Slider + MoveScaled(1,44,29,4) + MinValue = 20 + MaxValue = 1000 + Step = 10 + PageStep = 50 + Value = 20 + } + { tmrEvolution #Timer + #MoveScaled(39,20) + Delay = 300 + } + { togEvolution ToggleButton + MoveScaled(1,33,29,6) + Text = ("Start Evolution") + } + { lblDelay TextLabel + MoveScaled(1,39,29,6) + Text = ("Evolution Delay: 20ms") + } + { TabStrip1 TabStrip + MoveScaled(1,49,29,45) + Count = 2 + Index = 0 + Text = ("Survival") + { chkEight CheckBox + MoveScaled(2,36,25,3) + Text = ("8 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkSeven CheckBox + MoveScaled(2,33,25,3) + Text = ("7 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkSix CheckBox + MoveScaled(2,30,25,3) + Text = ("6 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkFive CheckBox + MoveScaled(2,27,25,3) + Text = ("5 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkFour CheckBox + MoveScaled(2,24,25,3) + Text = ("4 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkThree CheckBox + MoveScaled(2,21,25,3) + Text = ("3 Neighbours") + Tristate = True + } + { chkTwo CheckBox + MoveScaled(2,18,25,3) + Text = ("2 Neighbours") + Tristate = True + Value = CheckBox.None + } + { chkOne CheckBox + MoveScaled(2,15,25,3) + Text = ("1 Neighbour") + Tristate = True + Value = CheckBox.True + } + { chkZero CheckBox + MoveScaled(2,12,25,3) + Text = ("0 Neighbour") + Tristate = True + Value = CheckBox.True + } + { TextLabel1 TextLabel + MoveScaled(1,1,25,10) + Text = ("Select the Count of Neighbours where a cell will die or keep its state.") + Alignment = Align.Top + } + Index = 1 + Text = ("Options") + { TextLabel3 TextLabel + MoveScaled(1,1,26,12) + Text = ("Set here the probability that a Cell will be born or not when you spawn the first Generation.") + Alignment = Align.Top + } + { Slider2 Slider + MoveScaled(1,14,25,4) + Value = 50 + } + { Label1 Label + MoveScaled(1,18,7,3) + Text = ("Alive") + } + { Label2 Label + MoveScaled(19,18,7,3) + Text = ("Dead") + Alignment = Align.Right + } + { chkBorder CheckBox + MoveScaled(1,36,26,4) + Text = ("Draw cell borders") + } + { chkSmall CheckBox + MoveScaled(1,24,26,4) + Text = ("Small generation") + } + { chkSymetryV CheckBox + MoveScaled(1,32,26,4) + Text = ("Vertical symmetry") + } + { chkSymetryH CheckBox + MoveScaled(1,28,26,4) + Text = ("Horizontal symmetry") + } + Index = 0 + } +} diff --git a/app/examples/Games/GameOfLife/glob2-icon-48x48.png b/app/examples/Games/GameOfLife/glob2-icon-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab8fccb1727f6c93db8e5488cc8ac2d0f4ca335 GIT binary patch literal 3390 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 $aEnemies[$aEnemies.Max].Y Or If iY < $aEnemies[0].Y Then Return Null + For iInd = 0 To $aEnemies.Max + If iY = $aEnemies[iInd].Y Then Break + Next + For iInd = iInd To $aEnemies.Max + If iY <> $aEnemies[iInd].Y Then Break + ' The ship look strings have spaces around them but a 'hit' is + ' only to hit the ship, not the spaces ;-) + If iX > $aEnemies[iInd].X And If iX < $aEnemies[iInd].X + $aEnemies[iInd].Width - 1 Then Return $aEnemies[iInd] + Next + Return Null +End + +Public Sub Enemy_Destroyed() + Dim iInd, iJ As Integer + Dim iAdd As Integer + Dim hEnemy As Enemy + Dim iShift As Integer = 0 + + ' Higher level ships crumble into lower level ones >:-) + iInd = $aEnemies.FindByRef(Last) + $aEnemies.Remove(iInd) + Last.Undraw() + iAdd = Last.Type - 1 + If Not iAdd Then Goto _Out + ' Insert the new ships directly where the destroyed one was + ' shifting all successors forward which is even more diabolic + For iJ = 0 To iAdd + hEnemy = New Enemy(Last.Type - 1) As "Enemy" + hEnemy.Init($hWnd, Last.Shifted + iShift) + $aEnemies.Add(hEnemy, iInd + iJ) + iShift += hEnemy.Width + Next + For iInd = iInd + iAdd + 1 To $aEnemies.Max + $aEnemies[iInd].Move(iShift - Last.Width) + Next +_Out: + If $aEnemies.Count = 0 Then Raise Triumph +End + +Public Sub Enemy_SuperDestroyed() + $aEnemies.Remove($aEnemies.FindByRef(Last)) + Last.Undraw() + If $aEnemies.Count = 0 Then Raise Triumph +End + +Public Sub PlayerDestroyed() + Raise GameOver +End + +Public Sub Draw() + Dim hEnemy As Enemy + + For Each hEnemy In $aEnemies + hEnemy.Draw() + Next +End diff --git a/app/examples/Games/Invaders/.src/Enemy.class b/app/examples/Games/Invaders/.src/Enemy.class new file mode 100644 index 00000000..8e83aaa7 --- /dev/null +++ b/app/examples/Games/Invaders/.src/Enemy.class @@ -0,0 +1,161 @@ +' Gambas class file + +' Types in ascending difficulty +Public Enum Normal = 1, Tough, Borg, BorgQueen +' See looking strings below +Public Const AverageWidth As Integer = 5 + +Property X As Integer +Property Y As Integer +'' Shift distance from 0,0 (may *not* equal Y * Window.Width + X) +Property Read Shifted As Integer +Property Read Width As Integer +Property Read Type As Integer + +Event Destroyed +Event SuperDestroyed + +Private $iType As Integer +Private $sLook As String +Private $iWidth As Integer +Private $iColor As Integer + +Private $hWnd As Window +Private $iX As Integer +Private $iY As Integer +Private $iShifted As Integer + +Public Sub _new(Optional iType As Integer) + ' Calculate a fair share of enemies + If Not iType Then + Select Case CInt(Rnd(0, 100)) + Case 0 To 69 + iType = Normal + Case 70 To 94 + iType = Tough + Case 95 To 98 + iType = Borg + Case 99 + iType = BorgQueen + End Select + Endif + Select Case iType + Case Normal + $sLook = " -o- " + $iColor = Color.Green + Case Tough + $sLook = " ,^, " + $iColor = Color.Cyan + Case Borg + $sLook = " [%] " + $iColor = Color.Magenta + Case BorgQueen + $sLook = " " + $iColor = Color.Red + End Select + $iWidth = Len($sLook) + $iType = iType +End + +Public Sub Init(hWnd As Window, Optional iShift As Integer) + $hWnd = hWnd + $iX = 0 + $iY = 0 + $iShifted = 0 + If iShift Then Shift(iShift) +End + +Public Sub Draw() + $hWnd.Print($sLook, $iX, $iY,, Pair[$iColor, $hWnd.Background]) +End + +Public Sub Undraw() + $hWnd.Print(Space$($iWidth), $iX, $iY) +End + +Public Sub Move(Optional iShift As Integer = 1) + Undraw() + Shift(iShift) + '' FIXME: Value is arbitrary but works kind of + If CInt(Rnd(0, 2000)) = 0 Then + ' Resistance is futile + Missiles.Shoot(Missile.Borg, $iX, $iY + 1) + Endif +End + +'' Shift this ship some places in its row (probably into some other row). +'' Even very huge values are supported. +'' +'' The mathematics in this function should guarantee that no ship ever +'' covers another (accidentally) and that they always stay togehter +'' tightly. +Public Sub Shift(iShift As Integer) + Dim iCur As Integer + + $iShifted += iShift + While iShift <> 0 + ' Handle one row at max at a time + If iShift > $hWnd.Width Then + iCur = $hWnd.Width + Else + iCur = iShift + Endif + iShift -= iCur + ' All even rows go to the right, all odd lines to the left + Select Case $iY % 2 + Case 0 + $iX += iCur + If $iX + $iWidth >= $hWnd.Width Then + ' Goes to an odd row, must start at the end of the row + While $iX + $iWidth >= $hWnd.Width + $iX = ($iX + $iWidth) % $hWnd.Width + Wend + $iX = $hWnd.Width - $iX - $iWidth + Inc $iY + Endif + Case 1 + $iX -= iCur + If $iX < 0 Then + ' Goes to an even row, must start at the beginning + $iX = (- $iX) % $hWnd.Width + Inc $iY + Endif + End Select + Wend +End + +Public Sub Destroy() + Raise Destroyed +End + +Public Sub SuperDestroy() + Raise SuperDestroyed +End + +Private Function X_Read() As Integer + Return $iX +End + +Private Sub X_Write(Value As Integer) + $iX = Value +End + +Private Function Y_Read() As Integer + Return $iY +End + +Private Sub Y_Write(Value As Integer) + $iY = Value +End + +Private Function Width_Read() As Integer + Return $iWidth +End + +Private Function Type_Read() As Integer + Return $iType +End + +Private Function Shifted_Read() As Integer + Return $iShifted +End diff --git a/app/examples/Games/Invaders/.src/MMain.module b/app/examples/Games/Invaders/.src/MMain.module new file mode 100644 index 00000000..7cde3c93 --- /dev/null +++ b/app/examples/Games/Invaders/.src/MMain.module @@ -0,0 +1,139 @@ +' Gambas module file + +' TODO: Make real setup for playing +' TODO: There is an occasional flickering when shooting and the ships are already near the player and few + +Public X As Integer +Public Y As Integer +' Hardcoded corresponding to the strings in Draw() below! +Public Width As Integer = 3 +Public Height As Integer = 2 + +Private $hEnemies As Timer +Private $hMissiles As Timer + +Public Sub Main() + ' Debug aid. Watch /tmp/pipe with "tail -f" + ' Dim hPipe As Stream + ' + ' hPipe = Pipe "/tmp/pipe" For Write + ' Error To #hPipe + + Screen.Cursor = Cursor.Hidden + Screen.Echo = False + Window.Attributes = Attr.Bold + Object.Attach(Window, Me, "Window") + Window.SetFocus() + + X = (Window.Width - Width) \ 2 + Y = Window.Height - Height + + Object.Attach(Enemies, Me, "Enemies") + + Draw() + ' Populate ca. 1/3 of the screen with enemies. It will actually look more + ' because of the enemy arrangement. But that's good :-) + Enemies.Init(Window, Window.Width * Window.Height / 3 / Enemy.AverageWidth) + Missiles.Init(Window) + Window.Buffered = True + + $hEnemies = New Timer As "Enemies" + ' TODO: The Timers are made for a 80*24 terminal. + ' Make it 100 and you'll likely win, 75 really depends on the amount of Borg for me... + $hEnemies.Delay = 75 + $hMissiles = New Timer As "Missiles" + $hMissiles.Delay = 50 + + $hEnemies.Start() + $hMissiles.Start() +End + +Public Sub Window_Read() + Select Window.Read() + ' Navigate left/right + Case Key.Left + If X = 0 Then Return + Undraw() + Dec X + Draw() + Case Key.Right + If X + Width >= Window.Width Then Return + Undraw() + Inc X + Draw() + ' Navigate up/down + Case Key.Up + If Y <= 0 Then Return + Undraw() + Dec Y + Draw() + Case Key.Down + If Y >= Window.Height - Height Then Return + Undraw() + Inc Y + Draw() + ' Shoot normal missile + Case Key[" "] + Missiles.Shoot(Missile.Normal, X, Y - 1) + 'If Not $hMissiles.Enabled Then $hMissiles.Start() + ' Shoot super missile + Case Key["v"] + Missiles.Shoot(Missile.Super, X, Y - 1) + 'If Not $hMissiles.Enabled Then $hMissiles.Start + ' Of course, manipulate the timers + Case Key["+"] + $hEnemies.Delay -= 10 + $hMissiles.Delay -= 5 + Case Key["-"] + $hEnemies.Delay += 10 + $hMissiles.Delay += 5 + End Select +End + +Public Sub Enemies_Triumph() + End("TRIUMPH!", Color.Yellow) +End + +Public Sub Enemies_GameOver() + End("GAME OVER!", Color.Blue) +End + +Private Sub End(sMessage As String, iColor As Integer) + $hEnemies.Stop() + $hMissiles.Stop() + '' TODO: The message display is glitchy + Window.Buffered = False + Window.PrintCenter(sMessage,, Pair[iColor, Window.Background]) + '' TODO: What now? Note that the user can still interact at this point... +End + +Private Sub Draw() + Window.Print(" | ", X, Y) + Window.Print("^'^", X, Y + 1) +End + +Private Sub Undraw() + Window.Print(Space$(Width), X, Y) + Window.Print(Space$(Width), X, Y + 1) +End + +Public Sub Enemies_Timer() + Enemies.Move() + If Not $hMissiles.Enabled Then + Enemies.Draw() + Screen.Refresh() + Endif +End + +Public Sub Missiles_Timer() + If Missiles.Move() And Missiles.Count = 0 Then + '$hMissiles.Stop() + Return + Endif + ' Missiles draw at a higher rate so we can safely redraw everything + ' here. Note that, thanks to ncurses' buffering, only the changed + ' parts are redrawn so that there is no overhead in more frequent + ' Screen.Refresh()es here. + Enemies.Draw() + Screen.Refresh() +End diff --git a/app/examples/Games/Invaders/.src/Missile.class b/app/examples/Games/Invaders/.src/Missile.class new file mode 100644 index 00000000..d06f6af0 --- /dev/null +++ b/app/examples/Games/Invaders/.src/Missile.class @@ -0,0 +1,84 @@ +' Gambas class file + +Public Enum Normal, {Super}, Borg + +Property Read Type As Integer + +Event Destroyed + +Private $iType As Integer +Private $sLook As String +Private $iColor As Integer +Private $iDir As Integer + +Private $hWnd As Window +Private $iX As Integer +Private $iY As Integer + +Public Sub _new(iType As Integer, iX As Integer, iY As Integer) + Select Case iType + Case Normal + $sLook = "|" + $iColor = Color.Yellow + $iDir = -1 + Case {Super} + $sLook = "*" + $iColor = Color.Red + $iDir = -1 + Case Borg + $sLook = "*" + $iColor = Color.Green + $iDir = 1 + End Select + $iType = iType + $iX = iX + $iY = iY +End + +Public Sub Init(hWnd As Window) + $hWnd = hWnd +End + +Public Function Move() As Boolean + Dim hEnemy As Enemy + + Undraw() + $iY += $iDir + If $iType <> Borg Then + hEnemy = Enemies.Hit($iX, $iY) + If hEnemy Then + If $iType = Normal Then + hEnemy.Destroy() + Else + hEnemy.SuperDestroy() + Draw() + Endif + Raise Destroyed + Return True + Endif + Else + If $iY >= MMain.Y And If $iY < MMain.Y + MMain.Height And If + $iX >= MMain.X And If $iX < MMain.X + MMain.Width Then + Enemies.PlayerDestroyed() + Draw() + Endif + Endif + If $iY = -1 Or $iY = $hWnd.Height Then + Raise Destroyed + Return True + Endif + Draw() + Return False +End + +Public Sub Draw() + Window.Print($sLook, $iX, $iY,, Pair[$iColor, $hWnd.Background]) +End + +Public Sub Undraw() + Window.Print(" ", $iX, $iY) +End + +Private Function Type_Read() As Integer + Return $iType +End diff --git a/app/examples/Games/Invaders/.src/Missiles.class b/app/examples/Games/Invaders/.src/Missiles.class new file mode 100644 index 00000000..1219bfb8 --- /dev/null +++ b/app/examples/Games/Invaders/.src/Missiles.class @@ -0,0 +1,65 @@ +' Gambas class file + +Create Static + +'' Maximum number of missiles concurrently displayed +Public Const MaxMissiles As Integer = 3 + +Property Read Count As Integer + +Private $aMissiles As Missile[] +Private $hWnd As Window +Private $iCount As Integer + +Public Sub _new() + $aMissiles = New Missile[] + $iCount = 0 +End + +Public Sub Init(hWnd As Window) + $hWnd = hWnd +End + +Public Function Move() As Boolean + Dim hMissile As Missile + Dim bRes As Boolean = False + + For Each hMissile In $aMissiles + bRes = bRes Or hMissile.Move() + Next + Return bRes +End + +Public Sub Shoot(iType As Integer, iX As Integer, iY As Integer) + Dim hMissile As Missile + + Select Case iType + Case Missile.Normal + ' Consumes one missile + If $iCount >= MaxMissiles Then Return + Inc $iCount + Case Missile.Super + ' Consumes all missiles + If $iCount Then Return + $iCount = MaxMissiles + Case Missile.Borg + ' Consumes none of the (player's) missiles + End Select + hMissile = New Missile(iType, iX, iY) As "Missile" + hMissile.Init($hWnd) + $aMissiles.Add(hMissile) +End + +Public Sub Missile_Destroyed() + Last.Undraw() + $aMissiles.Remove($aMissiles.FindByRef(Last)) + If Last.Type = Missile.Normal Then + Dec $iCount + Else If Last.Type = Missile.Super Then + $iCount = 0 + Endif +End + +Private Function Count_Read() As Integer + Return $iCount +End diff --git a/app/examples/Games/Invaders/invaders.png b/app/examples/Games/Invaders/invaders.png new file mode 100644 index 0000000000000000000000000000000000000000..cfad58b988e93f734bb650be4630ac68286af67c GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$efS0K$$&+xy2q4t+i1(2=p z>Eak7aXC3bfp-Jj1~!3&jy5Jo4o0_YS2j#-k=U`dy@`>TIho@`P$0XJqzePXvzyAh TnC9;RnaAMi>gTe~DWM4fgJd8H literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/.directory b/app/examples/Games/MineSweeper/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/MineSweeper/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/MineSweeper/.hidden/screenshots/2014-12-14.png b/app/examples/Games/MineSweeper/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b09fa6cff0de41ae3a53448ee3113bb68fa60 GIT binary patch literal 48121 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 M07*qoM6N<$f}|9lUH||9 literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/.lang/cs.po b/app/examples/Games/MineSweeper/.lang/cs.po new file mode 100644 index 00000000..dece6115 --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/cs.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:24 +msgid "Minesweeper" +msgstr "-" + +#: FMain.form:30 +msgid "&Game" +msgstr "&Hra" + +#: FMain.form:33 +msgid "&Change Game Parameters" +msgstr "&Změna parametrů hry" + +#: FMain.form:38 +msgid "&Quit" +msgstr "&Ukončit" + +#: FSettings.form:21 +msgid "Settings" +msgstr "Nastavení" + +#: FSettings.form:37 +msgid "Width" +msgstr "Výška" + +#: FSettings.form:54 +msgid "Height" +msgstr "Víška" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "Počet min" + +#: FSettings.form:91 +msgid "OK" +msgstr "-" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "Zrušit" + diff --git a/app/examples/Games/MineSweeper/.lang/ja.po b/app/examples/Games/MineSweeper/.lang/ja.po new file mode 100644 index 00000000..dabdbf68 --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/ja.po @@ -0,0 +1,53 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "キャンセル" + +#: FMain.form:26 +msgid "&Change Game Parameters" +msgstr "パラメータを設定(&C)" + +#: FMain.form:23 +msgid "&Game" +msgstr "ゲーム(&G)" + +#: FSettings.form:54 +msgid "Height" +msgstr "縦の長さ" + +#: .project:1 FMain.form:19 +msgid "Minesweeper" +msgstr "マインスイーパ" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "地雷の数" + +#: FSettings.form:91 +msgid "OK" +msgstr "-" + +#: FMain.form:30 +msgid "&Quit" +msgstr "終了(&Q)" + +#: FSettings.form:21 +msgid "Settings" +msgstr "設定" + +#: FSettings.form:37 +msgid "Width" +msgstr "横の長さ" + diff --git a/app/examples/Games/MineSweeper/.lang/ru.po b/app/examples/Games/MineSweeper/.lang/ru.po new file mode 100644 index 00000000..b07ef7dc --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/ru.po @@ -0,0 +1,80 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/MineSweeper/.project:20 app/examples/Games/MineSweeper/.src/FMain.form:5 +msgid "Minesweeper" +msgstr "Сапёр" + +#: app/examples/Games/MineSweeper/.project:21 +msgid "" +"Minesweeper.\n" +"\n" +"This example is an implementation of the Minesweeper game." +msgstr "" +"Сапёр.\n" +"\n" +"Этот пример является реализацией игры Сапёр." + +#: app/examples/Games/MineSweeper/.src/FMain.form:10 +msgid "Game" +msgstr "Игра" + +#: app/examples/Games/MineSweeper/.src/FMain.form:12 +msgid "Change Game Parameters" +msgstr "Изменить параметры игры" + +#: app/examples/Games/MineSweeper/.src/FMain.form:16 +msgid "Quit" +msgstr "Выход" + +#: app/examples/Games/MineSweeper/.src/FSettings.form:5 +msgid "Settings" +msgstr "Параметры" + +#: app/examples/Games/MineSweeper/.src/FSettings.form:18 +msgid "Width" +msgstr "Ширина" + +#: app/examples/Games/MineSweeper/.src/FSettings.form:32 +msgid "Height" +msgstr "Высота" + +#: app/examples/Games/MineSweeper/.src/FSettings.form:46 +msgid "Number of mines" +msgstr "Количество мин" + +#: app/examples/Games/MineSweeper/.src/FSettings.form:62 +msgid "OK" +msgstr "ОК" + +#: app/examples/Games/MineSweeper/.src/FSettings.form:67 +msgid "Cancel" +msgstr "Отмена" + +#: app/examples/Games/MineSweeper/.src/MineSweeperGame.class:123 +msgid "Index out of bounds" +msgstr "Индекс вне границ" + diff --git a/app/examples/Games/MineSweeper/.lang/zh.po b/app/examples/Games/MineSweeper/.lang/zh.po new file mode 100644 index 00000000..183467dc --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/zh.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "取消" + +#: FMain.form:26 +msgid "&Change Game Parameters" +msgstr "修改游戏参数(&C)" + +#: FMain.form:23 +msgid "&Game" +msgstr "游戏" + +#: FSettings.form:54 +msgid "Height" +msgstr "高度" + +#: FMain.form:19 .project:1 +msgid "Minesweeper" +msgstr "扫雷" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "地雷数" + +#: FSettings.form:91 +msgid "OK" +msgstr "确定" + +#: FMain.form:30 +msgid "&Quit" +msgstr "离开(&Q)" + +#: FSettings.form:21 +msgid "Settings" +msgstr "参数设置" + +#: FSettings.form:37 +msgid "Width" +msgstr "宽度" + diff --git a/app/examples/Games/MineSweeper/.lang/zh_TW.po b/app/examples/Games/MineSweeper/.lang/zh_TW.po new file mode 100644 index 00000000..681019e6 --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/zh_TW.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:19 +msgid "Minesweeper" +msgstr "踩地雷" + +#: FMain.form:23 +msgid "&Game" +msgstr "遊戲(&G)" + +#: FMain.form:26 +msgid "&Change Game Parameters" +msgstr "修改遊戲參數(&C)" + +#: FMain.form:30 +msgid "&Quit" +msgstr "離開(&Q)" + +#: FSettings.form:21 +msgid "Settings" +msgstr "設定" + +#: FSettings.form:37 +msgid "Width" +msgstr "寬度" + +#: FSettings.form:54 +msgid "Height" +msgstr "高度" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "地雷數" + +#: FSettings.form:91 +msgid "OK" +msgstr "確定" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "取消" diff --git a/app/examples/Games/MineSweeper/.project b/app/examples/Games/MineSweeper/.project new file mode 100644 index 00000000..eabe3b13 --- /dev/null +++ b/app/examples/Games/MineSweeper/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Minesweeper +Startup=FMain +Icon=image/expr_win.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Minesweeper.\n\nThis example is an implementation of the Minesweeper game." +Authors="Timothy Lin" +TabSize=3 +Translate=1 +Language=en +Vendor=Example +Packager=1 +Tags=Game,LogicGame +Screenshot=.hidden/screenshots/2014-12-14.png +CreateMenu=1 diff --git a/app/examples/Games/MineSweeper/.src/FMain.class b/app/examples/Games/MineSweeper/.src/FMain.class new file mode 100644 index 00000000..224b4e6a --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FMain.class @@ -0,0 +1,249 @@ +' Gambas class file + +Private img_cover As Picture = Picture.Load("image/cover.png") +Private img_cover_hovered As Picture = Picture.Load("image/coveron.png") +Private img_false As Picture = Picture.Load("image/false.png") +Private img_flag As Picture = Picture.Load("image/flag.png") +Private img_face_normal As Picture = Picture.Load("image/expr_normal.png") +Private img_face_win As Picture = Picture.Load("image/expr_win.png") +Private img_face_lose As Picture = Picture.Load("image/expr_lose.png") +Private img_face_o As Picture = Picture.Load("image/expr_o.png") +Private img_mine As Picture +Private img_num[10] As Picture + +Private dlgSettings As FSettings = New FSettings + +Private game_width As Integer = 20 +Private game_height As Integer = 15 +Private game_mines As Integer = game_width * game_height / 10 + +Private game As MineSweeperGame +Private blocks As PictureBox[] + +Private play_time As Integer = 0 + +Public Sub _new() + ' img_num[0] is none, img_num[9] is mine + ' img_num[1~8] is the number of adjacent mines + ' load number images + Dim i As Integer + Dim a As Integer[] + img_num[0] = Picture.Load("image/empty.png") + img_num[9] = Picture.Load("image/mine.png") + For i = 1 To 8 + img_num[i] = Picture.Load("image/number_" & Str(i) & ".png") + Next + img_mine = img_num[9] + + btnStatus.Resize(img_face_normal.Width + 10, img_face_normal.Height + 10) + btnStatus.Picture = img_face_normal + + Timer1.Enabled = False +End + +' ----- Window Events ----- + +Public Sub Form_Open() + ReDimGame(game_width, game_height, game_mines) + 'Me.Maximized = True +End + +' Public Sub Form_Resize() +' adjust_components() +' End + +Public Sub Block_MouseDown() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + + If Mouse.Left Then + If game.Status = game.GAME_STARTED And game.GetBlockStatus(x, y) = game.BLOCK_COVERED Then + btnStatus.Picture = img_face_o + Endif + Endif +End + + +Public Sub Block_MouseUp() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + 'Message("Clicked (" & x & "," & y & ")") + + If game.Status = game.GAME_STARTED Then + btnStatus.Picture = img_face_normal + Endif + + ' Skip the event if the mouse button is released outside the block it is pressed + If Mouse.X > blocks[0, 0].Width Or Mouse.y > blocks[0, 0].Height Then Return + If Mouse.X < 0 Or Mouse.y < 0 Then Return + + If Mouse.Right Then + game.ToggleFlag(x, y) + Else If Mouse.Left Then + game.OpenBlock(x, y) + Endif +End + +' highlight the block under cursor +Public Sub Block_Enter() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + + If game.Status = game.GAME_WIN Or game.Status = game.GAME_LOSE Then Return + + If game.GetBlockStatus(x, y) = game.BLOCK_COVERED Then + blocks[x, y].Picture = img_cover_hovered + Endif +End + +' un-highlight the block under cursor +Public Sub Block_Leave() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + + If game.Status = game.GAME_WIN Or game.Status = game.GAME_LOSE Then Return + + If game.GetBlockStatus(x, y) = game.BLOCK_COVERED Then + blocks[x, y].Picture = img_cover + Endif +End + +Public Sub btnStatus_Click() + game.Reset() +End + +Public Sub Game_OnStart() + Timer1.Enabled = True +End + +Public Sub Game_OnEnd() + Timer1.Enabled = False +End + +Public Sub Game_OnRefresh() + Dim x, y As Integer + Dim s As Integer + For x = 0 To game.Width - 1 + For y = 0 To game.Height - 1 + Select Case game.Status + Case game.GAME_READY + blocks[x, y].Picture = img_cover + btnStatus.Picture = img_face_normal + play_time = 0 ' reset the timer + Timer1.Enabled = False + refresh_timebox() + Case game.GAME_STARTED + s = game.GetBlockStatus(x, y) + If s = game.BLOCK_COVERED Then + blocks[x, y].Picture = img_cover + Else If s = game.BLOCK_FLAGGED Then + blocks[x, y].Picture = img_flag + Else + blocks[x, y].picture = img_num[game.GetBlockNumber(x, y)] + Endif + Case game.GAME_WIN, game.GAME_LOSE + If game.GetBlockStatus(x, y) = game.BLOCK_FLAGGED Then + If game.GetBlockNumber(x, y) = game.NUM_MINE Then + blocks[x, y].Picture = img_flag + Else + blocks[x, y].Picture = img_false + Endif + Else ' the block has been uncovered + blocks[x, y].Picture = img_num[game.GetBlockNumber(x, y)] + Endif + btnStatus.Picture = IIf(game.Status = game.GAME_WIN, img_face_win, img_face_lose) + End Select + Next + Next + refresh_flagbox() +End + +Public Sub Timer1_Timer() + play_time += 1 + refresh_timebox() +End + +Public Sub menuQuit_Click() + Me.Close() +End + +Public Sub menuReDim_Click() + dlgSettings.FieldWidth = game_width + dlgSettings.FieldHeight = game_height + dlgSettings.MineCount = game_mines + If dlgSettings.ShowModal() Then + game_width = dlgSettings.FieldWidth + game_height = dlgSettings.FieldHeight + game_mines = dlgSettings.MineCount + ReDimGame(game_width, game_height, game_mines) + Endif +End + + + +' ----- Private Functions ----- + + +' Generate the a width x height board and adjust control positions +Private Sub ReDimGame(width As Integer, height As Integer, mines As Integer) + Dim x, y As Integer + Dim blk As PictureBox + + blocks = New PictureBox[width, height] + + For x = 0 To width - 1 + For y = 0 To height - 1 + blk = New PictureBox(panelBoard) + With blk + .Picture = img_cover + .Resize(img_cover.Width, img_cover.Height) + .Move(x * .Width, y * .Height) + .Tag = x + y * width + Object.Attach(blk, Me, "Block") + End With + blocks[x, y] = blk + Next + Next + + game = New MineSweeperGame(width, height, mines) + Object.Attach(game, Me, "Game") + game.Reset() + + panelBoard.Resize(width * img_cover.Width, height * img_cover.Height) + 'adjust_components() +End + +' Private Sub adjust_components() +' center_control(panelBoard) +' center_control_horizontal(btnStatus) +' btnStatus.top = panelBoard.top - btnStatus.Height +' TimeBox.Left = 0 +' TimeBox.Top = panelBoard.Top - TimeBox.Height +' FlagBox.Left = Me.Width - FlagBox.Width +' FlagBox.Top = panelBoard.Top - FlagBox.Height +' End + +Private Sub refresh_timebox() + lblTime.Text = Str(play_time) +End + +Private Sub refresh_flagbox() + lblFlags.Text = Str(game.FlagCount) +End + + + +' Center the control in the main window +' Private Sub center_control(c As Control) +' c.Move((Me.Width - c.Width) / 2, (Me.Height - c.Height) / 2) +' End +' +' ' Center the control horizontally in the main window +' Private Sub center_control_horizontal(c As Control) +' c.left = (Me.Width - c.Width) / 2 +' End + diff --git a/app/examples/Games/MineSweeper/.src/FMain.form b/app/examples/Games/MineSweeper/.src/FMain.form new file mode 100644 index 00000000..7e6d2cd6 --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FMain.form @@ -0,0 +1,77 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,60,66) + Text = ("Minesweeper") + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { menuGame Menu + Text = Shortcut(("Game"), "G") + { menuReDim Menu + Text = Shortcut(("Change Game Parameters"), "C") + Picture = Picture["icon:/32/edit"] + } + { menuQuit Menu + Text = Shortcut(("Quit"), "Q") + Picture = Picture["icon:/32/quit"] + } + } + { HBox1 HBox + MoveScaled(0,2,59,15) + Spacing = True + Margin = True + { PictureBox2 PictureBox + MoveScaled(0,0,13,13) + Picture = Picture["icon:/64/clock"] + Stretch = True + } + { lblTime Label + MoveScaled(13,2,12,7) + Font = Font["20"] + AutoResize = True + } + { Panel2 Panel + MoveScaled(25,3,1,7) + Expand = True + } + { btnStatus Button + MoveScaled(26,2,7,7) + } + { Panel3 Panel + MoveScaled(33,3,1,7) + Expand = True + } + { PictureBox1 PictureBox + MoveScaled(33,0,13,13) + Picture = Picture["image/bigflag.png"] + Stretch = True + } + { lblFlags Label + MoveScaled(44,5,15,4) + Font = Font["20"] + AutoResize = True + } + } + { Panel6 Panel + MoveScaled(25,17,10,1) + } + { Panel4 HBox + MoveScaled(1,19,58,35) + Expand = True + { Panel1 Panel + MoveScaled(4,7,1,13) + Expand = True + } + { panelBoard Panel + MoveScaled(7,6,47,25) + } + { Panel5 Panel + MoveScaled(55,7,1,20) + Expand = True + } + } + { Timer1 #Timer + #MoveScaled(2,54) + } +} diff --git a/app/examples/Games/MineSweeper/.src/FSettings.class b/app/examples/Games/MineSweeper/.src/FSettings.class new file mode 100644 index 00000000..23366286 --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FSettings.class @@ -0,0 +1,66 @@ +' Gambas class file + +Property FieldWidth As Integer +Property FieldHeight As Integer +Property MineCount As Integer + +Public Sub Run() As Boolean + + Return Not Me.ShowModal() + +End + +Public Sub btnOK_Click() + + Me.Close(True) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub spinWidth_Change() + UpdateMineRange() + spinMines.Value = spinWidth.Value * spinHeight.Value / 10 +End + +Public Sub spinHeight_Change() + UpdateMineRange() + spinMines.Value = spinWidth.Value * spinHeight.Value / 10 +End + +Public Sub Form_Open() + UpdateMineRange() +End + +Private Sub UpdateMineRange() + spinMines.MinValue = 1 + spinMines.MaxValue = spinWidth.Value * spinHeight.Value - 1 +End + +Private Function FieldWidth_Read() As Integer + Return spinWidth.Value +End + +Private Sub FieldWidth_Write(Value As Integer) + spinWidth.Value = Value +End + +Private Function FieldHeight_Read() As Integer + Return spinHeight.Value +End + +Private Sub FieldHeight_Write(Value As Integer) + spinHeight.Value = Value +End + +Private Function MineCount_Read() As Integer + Return spinMines.Value +End + +Private Sub MineCount_Write(Value As Integer) + spinMines.Value = Value +End diff --git a/app/examples/Games/MineSweeper/.src/FSettings.form b/app/examples/Games/MineSweeper/.src/FSettings.form new file mode 100644 index 00000000..0c4c815a --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FSettings.form @@ -0,0 +1,71 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,61,30) + Text = ("Settings") + Resizable = False + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { VBox1 VBox + MoveScaled(1,1,54,22) + { HBox2 HBox + MoveScaled(2,1,50,6) + { lblX Label + MoveScaled(1,1,24,4) + Expand = True + AutoResize = True + Text = ("Width") + } + { spinWidth SpinBox + MoveScaled(14,1,9,4) + MinValue = 5 + MaxValue = 50 + } + } + { HBox4 HBox + MoveScaled(2,8,50,6) + { lblY Label + MoveScaled(1,1,24,4) + Expand = True + AutoResize = True + Text = ("Height") + } + { spinHeight SpinBox + MoveScaled(26,1,9,4) + MinValue = 5 + MaxValue = 25 + } + } + { HBox6 HBox + MoveScaled(2,15,50,6) + { lblMines Label + MoveScaled(0,1,14,4) + Expand = True + AutoResize = True + Text = ("Number of mines") + } + { spinMines SpinBox + MoveScaled(37,1,9,4) + } + } + } + { HBox1 HBox + MoveScaled(1,25,59,4) + Spacing = True + { Panel1 Panel + MoveScaled(4,0,4,4) + Expand = True + } + { btnOK Button + MoveScaled(24,0,16,4) + Text = ("OK") + Default = True + } + { btnCancel Button + MoveScaled(41,0,16,4) + Text = ("Cancel") + Cancel = True + } + } +} diff --git a/app/examples/Games/MineSweeper/.src/MineSweeperGame.class b/app/examples/Games/MineSweeper/.src/MineSweeperGame.class new file mode 100644 index 00000000..af0a5acf --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/MineSweeperGame.class @@ -0,0 +1,194 @@ +' Gambas class file + +Property Read Width As Integer +Property Read Height As Integer +Property Read Status As Integer +Property Read FlagCount As Integer + +Event OnStart() +Event OnEnd() +Event OnRefresh() + +Public Enum BLOCK_COVERED, BLOCK_FLAGGED, BLOCK_REVEALED +Public Enum GAME_READY, GAME_STARTED, GAME_WIN, GAME_LOSE +Public Enum NUM_MINE = 9 + +Private $width As Integer +Private $height As Integer +Private $blocks As New Integer[] +Private $status As New Integer[] +Private $mine_count As Integer +Private $flag_count As Integer +Private $covered_block_count As Integer +Private $game_state As Integer + +Public Sub _new(w As Integer, h As Integer, n As Integer) + $width = w + $height = h + $mine_count = n + $covered_block_count = 0 + $blocks = New Integer[w, h] + $status = New Integer[w, h] + Reset() +End + +Public Function Reset() + Dim x, y As Integer + For x = 0 To $width - 1 + For y = 0 To $height - 1 + $status[x, y] = BLOCK_COVERED + $blocks[x, y] = 0 + Next + Next + $game_state = GAME_READY + $covered_block_count = $width * $height + $flag_count = $mine_count + Raise OnRefresh() +End + +Public Function GetBlockNumber(x As Integer, y As Integer) As Integer + Return $blocks[x, y] +End + +Public Function GetBlockStatus(x As Integer, y As Integer) As Integer + Return $status[x, y] +End + +Public Sub OpenBlock(x As Integer, y As Integer) + If $game_state = GAME_READY Then + $game_state = GAME_STARTED + GenerateRandomMines($mine_count, x, y) + Raise OnStart() + Endif + + If $game_state = GAME_STARTED And $status[x, y] = BLOCK_COVERED + If $blocks[x, y] = NUM_MINE Then ' stepped on a mine + $game_state = GAME_LOSE + Raise OnEnd() + Else + RecursiveOpen(x, y) + If AllMinesFound() Then + FlagAllMines() + $game_state = GAME_WIN + Raise OnEnd() + Endif + Endif + Endif + + Raise OnRefresh() +End + +Public Sub ToggleFlag(x As Integer, y As Integer) + If $game_state = GAME_STARTED Then + If $status[x, y] = BLOCK_COVERED Then + $status[x, y] = BLOCK_FLAGGED + $flag_count -= 1 + Else If $status[x, y] = BLOCK_FLAGGED Then + $status[x, y] = BLOCK_COVERED + $flag_count += 1 + Else + Return + Endif + Raise OnRefresh() + Endif +End + + +Private Function Width_Read() As Integer + Return $width +End + +Private Function Height_Read() As Integer + Return $height +End + +Private Function Status_Read() As Integer + Return $game_state +End + +Private Function FlagCount_Read() As Integer + Return $flag_count +End + +' Generate n mines in the field and guarantee that (nx, ny) does not contain a mine. +Private Sub GenerateRandomMines(n As Integer, nx As Integer, ny As Integer) + Dim i As Integer + Dim x, y As Integer + Dim x2, y2 As Integer + Dim count As Integer = $width * $height + Dim index As Integer + + ' check if the range is correct + ' note that the field should contain at most (w*h-1) mines, not w*h + If n >= count Then Error.Raise(("Index out of bounds")) + + ' put n mines into the first blocks, and fill in zero in the rest + For i = 0 To count - 1 + x = i Mod $width + y = i / $width + $blocks[x, y] = IIf(i < n, NUM_MINE, 0) + Next + + ' if a mine is located in (nx, ny), then move the mine to the last block + If $blocks[nx, ny] = NUM_MINE Then Swap $blocks[nx, ny], $blocks[$width - 1, $height - 1] + + ' shuffle + For i = 0 To count - 1 + x = i Mod $width + y = i / $width + x2 = Int(Rnd(0, $width)) + y2 = Int(Rnd(0, $height)) + If (x = nx And y = ny) Or (x2 = nx And y2 = ny) Then Continue ' skip (nx, ny) + Swap $blocks[x, y], $blocks[x2, y2] + Next + + ' generate the numbers + For x = 0 To $width - 1 + For y = 0 To $height - 1 + If $blocks[x, y] = NUM_MINE Then + For x2 = x - 1 To x + 1 + For y2 = y - 1 To y + 1 + Try IncrementBlockMineCount(x2, y2) + Next + Next + Endif + Next + Next +End + +Private Sub IncrementBlockMineCount(x As Integer, y As Integer) + If $blocks[x, y] <> NUM_MINE Then $blocks[x, y] += 1 +End + +Private Sub RecursiveOpen(x As Integer, y As Integer) + Dim x2, y2 As Integer + If $status[x, y] = BLOCK_COVERED Then + $status[x, y] = BLOCK_REVEALED + $covered_block_count -= 1 + If $blocks[x, y] = 0 Then + For x2 = x - 1 To x + 1 + For y2 = y - 1 To y + 1 + RecursiveOpen(x2, y2) + Next + Next + Endif + Endif +Catch + +End + +Private Function AllMinesFound() As Boolean + Return $covered_block_count = $mine_count +End + +' Automatically mark all mines as flag +Private Sub FlagAllMines() + Dim x, y As Integer + For x = 0 To $width - 1 + For y = 0 To $height - 1 + If $blocks[x, y] = NUM_MINE Then + $status[x, y] = BLOCK_FLAGGED + Endif + Next + Next +End diff --git a/app/examples/Games/MineSweeper/image/bigflag.png b/app/examples/Games/MineSweeper/image/bigflag.png new file mode 100644 index 0000000000000000000000000000000000000000..334b47127d5a9adb5d4b4e2945f86defc2cb1678 GIT binary patch literal 6066 zcmV;j7ftAiP)|y73R#sX&{s!~&bAP?nYW@9z zfq~bT7MFhWYhU|4ze~X1@C?56<~s-~C1}TiHU?p(jG*hI*XzMlE4W;C@#1(Lt)dIH zjZD!1_6b2nF=ibLEnDH))d0u*7)?LKP%nVP0v&{i3k5`4BXVq*@loj6IWQ4WtwKZ~ z#y}hgVhrN>1xUG!!pRdT4>d7Uu?lyd1?5S)in@rS`5*mwUDn+GwYXgW_9-Emxg{m(Et z-GLP`@Z3|tFFyxrwV|RI)awD$vsLrMvrh&uzxrf+WA;B8MQ4CH9!O$+y@u~ab2KL0O~cU$q5u6eF)av0-T>*a2Nmh z+mE!~di#-v?|=Dt9RBy$4vs$`+E!>Ae!qF$6!*jj3Mo+3K2mEmCo~(VRvY)V2&h+p ziE$v-0CQp$w1)TrIE_zlBBN)0AYv-SOm-rW`-C6 z3qv@$P2vb(!N4c(huf%O?*IH>j9+-?*Pe;up+7%);2+LZi!-if5Z<3IeGe2!D2V@P ztvRTcTMIbkStzyx;LtvxSOi$o%|J}nNDL%4U~OQGhQu1IV@Mo)x{kKNFa_-rr^n9r2%hw0h28TY#TUn1i}m$ zPT=V@T{VNX0h?d#u^gGFkTO&6o!BPDb&9BG^#;#`j=n1@SR^dckQ=bffYE` zPhE#Qn!-W^JM7$7an<7tAX-_%q+bM@HQ?X@P!K}2>8Gyr(>A$e!w})L2AX7_6W}lm zq_>eUs;BNpp;|@wr{Ap{zcBT$Mm_)Ji+e|YYh|eUy~wg4^wXILcUFOmL7>JptOjpI1Eo+^&DSyN!7!BLz%CXT^j1n%|V?%9Rv z&p!n@auB-~SAOnqzk2ac{?;4s{{G=r?@?{rh%9x_DekBOi49!wpQtwXVs3Q-N5e84 zKL++sCiW$al8(y^W&<|TS6;H8yUaRrn)YHel;L%ClY0T|N(t_n&p|F-hMc=t+%voI ztNZ7derfX1#BcoZp5fnRrDjy%rrA)dN~<82FY7&v-3u`jVs zQnYVdvhJHeGsdJq)AY^M+D~5tm0lO4liRfhL}2fkfHgV-xiSSlf3f_z3sb-K$c@E+ zx?HP#b#EU*5PPPz0A z+bR5^DC_%xEI<*^H{;t>CjrW~;aFDx89N4$NWqB~pQ|-?mD}AGhCN2n3xUUvCiSo9 z_ft}G>dGsQ^ZRL+26Mk|sRVGL83X7ws04r}c0-yC(Cj=gw=i65dA}}J@7I~s zg4os@WcVzY{}k2gj{s?d6o?3-2*k-e52X^rN(8bLh`AiSbGde@S)Pm4jshgWL~wQI zsbb|A7Ur(tux|mD1x)Nplo!W|V*0?6&9Eewx!c%q3Q#AW(HJPBAv){GNexWkWe_!> z8N>-pF$M$z#zuik1+uUR>G_Z-9uXa%kvKjhME_c%=w0^xm)V$WqSOqS0}-)=*=O5Y z79%2I#87G=I)CZ(U+fwBpFfPuT()+)BLIa6R^%P1mYhe)aiLos91BZ;V}S7yzz-mC zq8jNN=rzR|od>$+3$bBSX$m9l;&~NjN~F9hGX*OJ85xGeF(e2f-5%KY!P*crAL35u zkN{u+=ys7^&n}q=wA#W_6uxx!==f!_6eykNcSwN>miA5!43E}=poh|G2ji_GFg60z zYd{bt%T{R$uTxt7QtN&SC-sl}^^dg%8BTwI4Jpev?`J6wmgK(FONvsEN*Pjck{?y4 z;6*?p5SAouh1XU&L}0OCYHaY^k!y>iO$k5jh;9A4#|8OkH`*3RJ z6+y&G9t)H7lXQtFo6|`IUb(Pve%IhxVwnWc0ub*IKmtr?mk>PRmTT~)F5r>S23!}| zzZVEYNEG*BNZMW3>K<>Xf2Oa%Oi^mXN=;&4%o@lPwlWFOY+OVRVp2X5+hjO-Pa^^` z0uhrQ3udrLVzp##wa7M%iz5Rs&eqGJqs_b97*S^CS`z8FrFW&}nAMzV=W zQq{>iaM^WN8=M;-d~L2#nX(NB06IwP-;MwzVH6R3rd*%E^x`xg=@1YzFgl#1MXZy# zH}C!WH8AbM#=v9%b*@ot=(7DLkyD?_sVz~WB&w7&#W_t=!!VMhEvKo>X@l4ZF59MN?C(p?x%6G&Y0D_L~$^Zrf#}OD*}@>kQ_9^dgYM-nxJ1i zzW4tv*2>eaHXQ)V0J=y{qquv3LKd80>9K01_&^+oK)Zv9unr8=fNC`v-(sCxo-*re zpI)nfc{?L{#if%=et*sDandx{fQ+Q7W56T}w#aM=Gmx%&n-pB;^ETT6z%m9uo*4fA zwW0b2R~sKm?ORRLw=dSa0!Rc{(dv`c-Q#F^tC-Y9GdTI=P%c%Gu6Adi^QO@Zo2M-RINIPRf>S11=wDaQccI*xbA3EqNoeI;~^G6D?;Shh(?B?caOsqi{Mx% zHIUbvX{=kV;eJy{#;x>vy1UNNLznkKY0g^BZ@yZuUwfo?=mN0|zU;O5ShhD06#|H0P?M^jZ1 z`D*$43n%w~z3bX-+i)Ae95!hjI{=WNOi(LG_*9`-#lrMeZQP-Yp|I~&inG^h<>{Ap z51qAy-AbfOk6mAU^nvROkK2*1t0+cydIs@o3uq1irDD=Zab`vlhOtyC%zWb$hn{bh3k$a44uI)YeVeB4T>%tQps;naKF~P8Mx*8V zGh83IY6-U?64-1=2t`B6T`9WOHxBRl#$Qg3ytHqzeRS`_>Z$R?)=BAfRnYT+av4^w z0$5f*g|qQ3OG`?jDi$coRL7tcTy|t^BgD<>Umh?3C=qxCr~R!5_W!{`wRFSP#shE- zz;ysC*rajXy#k3~o8GC$z>wv8s|YkrLpjTU%pozGOo9?1Wm`}LOr`BQy;mnk7k<2Z z=Jj6i&t5Sw?oFpAHzn4qV3?e}ikSVau7Ueg>+nB!j$JmB( zZcp>&siDTDLaf77dDj3e0SI#3cM2c@CPoRpGo|`|%w2sOkyW_>Wvzhq9FmqF18@Mi z015yqmdK!`^`H>ym&cp0+!(4~+<$%XllyNhey-;EyLHC{ISy>ch6PGYu}R@c3QrQX zW$<#JXkG2gU*8HOkW3q)rTCq(=Ie^NmwxssB&qA)3f`RoB!Y_l;gZsyu^b1U*Y&w{ z@Fyf3BAFAt6kztftQ6!jfI119w&A9>RQK&eyI#1qYw*?kFWq={|Mi8xCKADD4YDlQ zL`k|zVpZ~r%eFj7N%yycwpjyFB3P=GuFloUmu;41Y_sm&6+i*OiaQTf>y6!U&_ge# z%cwPOKqNpS`K(@NKeS1KhDhwd3V<4k3^}nL4GK>9+VRP+FW1Xcho|PwG(CS8MEXa} zR8Do9LEKIXZ`FB81YO7Ww53)7S-4$zcLWe1IP9OQ)|=FAEhDsRZ;R!$iM5Zd+r6X^ zb@~`Dh1pijBTUr2v~RRMKioL?z{TsoxNl+gw4>RwEOiq&YYULT&GjwYHvXbn7)2QE z`1^*sL3yECT(Xj}ZBxIuRov#O9R%VSW%Qmdlxt`$EkIkL6>jzhTcz(tH5wvRqqx3zseRZe z)lPrp+d4n#wiK8gJntMRIr15R(CM_>GH~!3iG(SX+xhi0*3#Pt(6bDCLNs=5Y#^8! zZd^Jtv-HrXFVFnKST7v5iJ%nqYhT{FYz1;Fc==!edzM31-aeMoA-d&@Hj(+rXL-04G)BtTDaX4HE%D4S;Ljy=c)rk=(d&+7l+S6 z*&RT-$SrflMG}qx^l%6D zV?$((#voB;4?x>u8MfGrtQOtqPY&;%duzOT_Vn!1=?CXm9~=v!(PC^|C<2JybLAxu zZsVH@fD_jj{?7SbgMZL4|K5lnHJ_TAf9%2e)dwraG?X@^L_kWx5G8-d zh(uCWn&*-p?}bW$6A_G3XnOwfhp){4gFipE=ighr{jaiZfJ7FYXz2@;;eoR6wGoMP zU6kD>;V!l@^4vk>n@l$axok5JgD|9(YD`y4o#&75ooO7}{hd9X-tGy{-(3&m#)#*S z6k}7fqPR?ki(OlFI<6DC+BlkswQXrP)}=xeH%VaG+E@T^|JB)lIWt&$>HKKpo37p- z#BBm3kO|3+TVE{J_G9VB6-4g9n;_Lg0`OM$o|Q655Nip^9nMkLYHiRI4ZuEo{^ zpMU+GuZ9mF{)H<;wU@U8aa#a|2rBCBEjjd{ZQJm?Zbu5^mp0VDdmUMQ%uh5G0_v7X z-4bblxeTBvgdnz*9b2kD@ev?eL9&LU6%e=y1sr}i= zRtpQ&(iOVJU&?P&feI>apB@+*9Sr?0yg*-*N_i<~Uv{eg-6|M~5;O0(isV>r5kM6{ z89Hld^@Adt*0Wdjvs4V_7g;EVOS1*B`p>sqOAURt9o(qoe1I+wF&Bj4x4or1a9;Bl)^NNRZo zQ*HNkZmmF4u;TW^_0fS!*loi{@h!1Si>ZD2paHzo$UHZYWYlJhJe$U?#}*M35jDmEKrHVyUOuR^d)P%a^O+Ab8)ZmnwLRHsNJLQ=T(t={0H z9`CI{vS0;^pRSgRCv+U4=SSC2s9%S$kB?6J-c`IOKq9~jS3h5_Hz@E{5!to#V!11T z^pJGCj{$Cd7eP#ff-GAdxK?sbDa(fEb(c+{ zIR)lCz@PcpT7hIi`HPR&>h%d7h3Lk(47;#`1YQq6{psTa8EWf01a7=~rd+L|+g?FT z(?Qitv=t@vD1%oY_32eSi1XQqd8;;-BtKnU4`YgQu}f*R-Zq#u_-`e zgEIM_E!UgqtSln7%2!BP9zNRR+a1Nt0Es}lJ6xpr^OjS9=Xt&<4Znep$G1)6CV&D3 ziaIB1^~Nq81?a`NjKaW;T>bl*pFZ+&6F^G-^U%Z9MniR5%LuI6+oGJ-$K%`Pu>oLu z@}%_66e|s^E-ygawRdtS4}Cm+Y>0ILg$S1R4wM~v97OOs9Z%fabn5?nd_4c&6zdg8 z3aUH%bB%hf8T%f3#=0O*c@82z?(F2Dk9@2HNNk`=&3FCcuHRo?UY_OR&^f67`1XDr zefz-2hGQb4qSDxR-)+kUWyW8S8dZsnMq*z+PTx)9rh`htrUanImfHdlLd3(z_GQc1 s2rxV0aTOvrkQ|_1L^6SV3~-b9|Jmkc3`un-P5=M^07*qoM6N<$f_Z+NZU6uP literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/cover.png b/app/examples/Games/MineSweeper/image/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..c366339327dbbbd66eae130f249411c7bf59be01 GIT binary patch literal 331 zcmV-R0kr;!P)xN#0001lP)t-sOw033 z%k)gR^Gvw(+wlC`@%>7*^wRA7O11Mxvh>8}`$w|#M6UC*-ughQ^PklDpVat3s`5Ri z^NY&(i^}&srt&$V^L4%Vb-ngEq4G4F@?Nm@FqZO3qx3A1@;8_BH<$7(k?|*s@+XV& zBZl!Gg7F-F@f?2e7<%y-dhix@@Dz0Ln=38B0001jNklGSSqJkr$LX;^X5X;#-^^1!ovqD^Nm@Q(pV=BabE$fJs2gGBe znut;-#B;0Lh}IXx?#gtC{f+4n@54Am#&JS4#+ryV7V(imd}a`x^Dg3@M-1VW3W)Cv dE`Kug*B3jkAOVA4(5V0b002ovPDHLkV1i*{rYisd literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/coveron.png b/app/examples/Games/MineSweeper/image/coveron.png new file mode 100644 index 0000000000000000000000000000000000000000..95f9be6eb96d6ccc33088f3c87606d73e9a425e7 GIT binary patch literal 238 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/false.png b/app/examples/Games/MineSweeper/image/false.png new file mode 100644 index 0000000000000000000000000000000000000000..0f649881a704583e26366d324ff53ff8c5eb0c97 GIT binary patch literal 1021 zcmVk#hqzPa`LXtvA$p!^Q3POc0A|Z8ERjSk<(EbqAU(iLH4T~)L z50bJepoBJn1tffv#D>He`x>x)@7yk?fkx?~%8{-#I#=_YbKZ9}qfJds1$=4*0fy&c zmCFzWR2&C`AfUdc1|cO<2&7^BFP88;!beBMwzr97GDyQns|DFH@ZKF{TJn%L$hqEf-1 zogp|qg1I@ka)s}79d6y?*M$Z2YL)u^eUgidB+_YW4-OE%PoV3RrKDop7-qRls8GOm zUCiTS{C8Qdg~IUgA@ujdg$rPseB=8(cU_#B8KN&`H~u+qYD+do;>0Y;3@*SJ2S`u^1GK+{oo1mjg|Ma2OgIpsS1G*cfkufMPC( zaa>4geeJXN;t7!|pC* zGLX%xQH4;SX{thti%_kq+{KF=OL@i+?>mgg<7}p%(N*-}%^S7p@K9~4t%c@hrCp%_ z^Yg0M_BQnQF4xwKBl6AeDmAQD|vV|8hCl+XKsDr@bB9 zw$C&a??+x=nkSXIN%{8~s%vYiGsjWKW?}-y$03nW1vCv@7a|d4CWB+!ACJ88e2$y3 z&!~I+7YHOAFBO}n*s6fdiw709Z=fV0q(bZLK&->66+p@_1HP2UVw{e!1&^!;> z)I@P~l+(#c;@)pOv>l?5W755kRm1P2Gmo*uNJA&k1Zk1s;Q zfX+^Wi3tK-2j53)Z^sCQ&VgTk`vaAdi?G8`t)hp+=#Hb@{I6(efZ<`ZL;_qF(b0iu rY63v}5q(4|E4eWYO^g#h4hk(GTiNwyp000000NkvXXu0mjf%y;Pi literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/flag.png b/app/examples/Games/MineSweeper/image/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..47a05b3183b7806f597b77a6e91bb7640d12a50d GIT binary patch literal 765 zcmWlX>rYYv0L3qrW;9!wE44n=m#ntzu{E`o7Fa6gT|Sc3LNNnpR-zP^Gww7WQ>OU> zlt$O2F)&3%&>1B*n;YgFIy3BHr86OVShJejNzZnE=MVUua`NnHZ)O0KAPDcsGputf z>$_U7wJYp9|MdhxFdmYec(zO?Q!14zm1=Hs%zE`AY&MLK(=^?umDgQl!)gUh!*17T zG!2Cro$4xdbzxut8V%ao(AsLXSadpFe%zsHa|e`4$mM8i!p)mVOG9BH#>Ox@3WMRz z$cVvU;015}```iedR(~zi3CYWh>b-<15_&Lbf~Pv@#A><)ND4}o_C8Aj-b6A_4UZf zL1H2zBGA%;Yu9k#z=!d1i^Za?D^e9^LL@?XID&&wRD{QmVK$?<7+mhhsi~o%q0+3F zm(>(=w(cFHli2 zGc%)WprBB|XheQKZr^s!ujZB2gz0JY^`WK)4##)bm!IEUZnt}JaZxE1E-fwn`r&pj z%q=f3&qs?>R%C6DWQE83+4;QZOuvH!SzTUUt|+Om+Q7I=xm>QD%_;inW3C{=_w(%R z?5FLUeSNlVnJppIYPno)E`C(kU8!%b?x*PL`+g+J<#O2xF{!8GqLWXbkBWUC=D$ZE z5M*ShXJzK3y=vPh5MI8@_wwyF8oMSZMA!3j#Wxc2M5dn|9?XJ*0*UkQpMTsx>(_aD zlosEUtnnD!y{m=h>-nEjKKAl`d}^(OQffG`Ml1Z{zd^wjP6f+)CBwsE174fj%kF@( z(&9}0znWJYCEB=` tUU*7*}phM6%Dao}A=S{{hlKt``6R literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/mine.png b/app/examples/Games/MineSweeper/image/mine.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d8b18c0221a2ac44acbbe0b5ba483030f1125d GIT binary patch literal 755 zcmVxN#00057P)t-sj*gC+ znwp)RouZpozPrjm^BQ+|R??&ce~bvBa>O#>B(M#>Tm%j)Pq|j%-Yi zZB3AGPL6C!jA=)UXhn-?MtoK(@(a_r0)w`mK(!#UJwxheFjI^79xSxi=sg%U8oXfeW z%DAS*u$;lHnA_Xiqlj&$if_KEmcOc&!L6CWt(w5Am%XTzy{VR_jB>7)f8pWb+|tQy zMIUrdDR)pScTp>MP%3s%Ds@gMa!Vy^LmO;G9>}<==;-L()y!x?8(1v^TQUerLCi@r*8n{0tF2X4ULRVOwG*9EiA39Z47MaAPR=f_ZtfnQ zUfyh5U*0ia+n l1^^27VE~|DKL)@a835KzR1(yVKIi}d002ovPDHLkV1gF%VZ#6b literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/number_1.png b/app/examples/Games/MineSweeper/image/number_1.png new file mode 100644 index 0000000000000000000000000000000000000000..e5bad5a5de6ac307ce01d14ff1ef6f79f4f0139e GIT binary patch literal 388 zcmV-~0ek+5P)xN#00024P)t-sj*gC+ znwp)RouZbSVLhlk5CG3>s+zKe^`Gc)eS#>SD6)ipKn%*@P|mfAKp@zc}O zo15P_IP=@v+o7T5Iy&{?;o+sF={!95=;-LGs_s5M`R?xSzP|B4Kl(5*{16cSZ*TYW z^YgN@_CZ1X_xJbw{QNqE+4BGZ0Fg;VK~xyiZPCXT!Y~j+(NF`3P(rVv_hvf(|6|C< z+BtWndD{tsR2dIr*29>Se4(ht7+R%r1*nD&V`SA(Z=e}DjIq^f#~pNIhp{Jp473=B zGQwDkaUxSBT8uN9W1+>kloi%mj2qcvr^UFJ1CCmZCpqJy#dx)D#@z8R=4lVcm-UA8 iu?OSV!}#;0KjRPRWg|ZpO3Fh30000xN#0002AP)t-sj*gC+ znwp)RouZ~gv9X!3q@}Q{Q>%Vntc19@xMHw~zP`RQtZtaOsK&;|k-euVtZ2;4%%jA! z4yjqw)6=QSxelpXf5D&I+uN_uzYnTfgT|!c;o-E^#1E=lhsmet=;*rJ%8Smd?(Xgo zt6M+0d%ob$^Yinv-pKd&_x$|)WBG8%0001EF3pd1l=x~%%v?997*^`SK zLGxN#0002JP)t-sj*gC+ znwp)RouZZRR?|69V=;-dMs_NO<{2w3io153&#sB~Ss!2paR2b83(MJM;Pz*&; z3!qUHdj)$JD0K+Fq8)ihNJcfT)19B26Nhh^h!6JU$Gz8E4n#i`1i^3f5kCp(KjRCX W&?F-bpPs`20000xN#0001=P)t-sj*gC+ znwp)RouZVvYDEIdo%F4On;cCpx%;e;4+S-=W)6?ACsp{%t z=;(3V+uQT=N#*6C;o;%&@oD$>Jp20|=jXKO=;-tFeD3b<_V$1C^Yi)ng7^3L{QUfu zzQqLq005s!L_t&-({0h)0)apjh2an_=R=~9L{3E#@BeJpY&AF2cQJeK|8^{^QOm&C zHZXQH+wRI_?A08=k%h5e8DNN!Y>eZ|WIDrKHpa!Di=_)!Rz^>(u=afTvNHx^v)u(D zLfIMjN`!-YA~_giI^x7PL86YB@k|%guDlp;bWfEGS^D6`_|$UbG8tc5fkGzZTPsn@ hWc(NyzlP?|_yIdMAPZ2QuyX(a002ovPDHLkV1gD?)1UwV literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/number_5.png b/app/examples/Games/MineSweeper/image/number_5.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e88fa2e1b94a1a482082c6ccf4ebeb777f8a40 GIT binary patch literal 381 zcmV-@0fPRCP)xN#0001)P)t-sj*gC+ znwp)RouZ? z#)4>1zrDK5%*>jHc8hdfXG0%$Ryfns)62=mZ%!?(pp)C%+oF$t;o;$&hjXQqf#~Sy z&BeXn+}deHA@1((teS{vMIxDjYxDE-!nLSsMIx@8iud>T&&0af(aHS$`~t5m3;+NC zpGibPR2b83(Mb}5KoCSxoJvJRoNyLYR0a|B{)aOw+kgeU1)cpj5P@a2>NFsB8xVWc zH@Fc8BnCI)kYqGg+=w>G#I7JZBvYq?=#tDlZ*HcGxFEh}x#C7#lkfYRt?alF_m#s@ zPI@AqYk>rMAcnPzT=hV_spzhcheUcoe5yD}<1~?!7sO1xa(q$#=FSicqbxuFRUm#F b+JD9mxN#0002eP)t-sj*gC+ znwp)RouZ?gbwirl+uLqAmb|UU;o;$jU8b;^!P(W^=;-LQoyCAsqN|a=k!!B* z?(Tt9qCnO4+Rx+s{QO3+ z2mSy60Jlj*K~xyiZPCYa!cY)IQNVzqMHV@Ou?^Vdob&(x65S8MMx4cH=5C}ac|31< zT^M~Xj2{kvAQ=2K8)N7p3^6OCWke8-SytTaj0s5ssl*ebEoo%z7sjmQkS`RAd9!C3 zOHxLq%=c7lIvDH1&}e?O(9ps7Ex-@wFWNd7JJQ9U9)Kp}zx2@O9H6U%aVR5xN#0001-P)t-sj*gC+ znwp)RouZvq$tDvi}eXPAutZE;)}Y=#>U31!^;}6>def{Qo7-Kz}nN()3?so4zTUT(be1A+jYj| zX2a&;;o;KW;t#Rz!`kEM=;(aS>ps2h?(Xg&x9^|X?(_5WlGpJdxA6D(_x$|)Zd7m5 z0001vNklRNGk$peAJOc2R6PIy N002ovPDHLkV1gp*!rK4< literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/number_8.png b/app/examples/Games/MineSweeper/image/number_8.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f0896f0c6a4ee0c9d4fd22e218c75c3f189479 GIT binary patch literal 454 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

Qid@c*Q=tJRs+sk=fNd$&W z!3{u@{TrZ0%+ z%)zUpCGDRhdHsYi+HasIOwtz!u=hDXptvcn17YIT7a!o$=f0<4lCDrYbQpZypM7-S zHa60-y+XJJM{ONM6VEoLn#E{q=|kn4L|81Ak+JcnHFod`frHYr$B{R>+S<-h0wI@@ zcB2G`A>)Jy1Y?=vK%nFkVylPyfBe{D0W6NOJW$66uD>d@!e!C0{Xe=XTsHek-xd`P zgJGi?@x}H6PYXx=%LjeSC;_1TJC*7&9Wu65ce!$?B?_W%q1-{&7#B?{Z9|{Z*}Ef2 zN*>sA!U#~fwhyuwM@;wD28W@5$;a>imSFou;N!8inH&)yNeU`_m)I72>LHl>On`~$ zvCWwrMTM^in5*4;K|ig2yPBJu&ofw2D^u~C491*soS|I#ByHjQcMAq> z(z&F+@GvIxyg-FzSofrF?EsAtYXz6vEosaHCx9!BPM-XN(t37yMOzV`-wF6$kf$5u z+MbSFG4b~H{^_%UvOGQqg+dp&9M;dt92O(0>4qe8t2#V#8d`(GC(0Z<#4RUY05nr zdqxOT+4NrAIbk>88Xv7vB?!9h(;WmW4c3y$tbfuo`!S4j`+j=5HC?bz_agWfrM2?# zPPk>*#K+Fg&XzeX&1)y_Vf2DktE?R;jwl0M+-O-XV?7npY><$wMohdxEmKqXxn z@6b65a`$g)(BGL zgJlGRIlMW^Lx9A&+ggak+%yr=Y6ZXJ+S>j&e*F0S#Qv#ILDGBvS3v%0u=4_@;~+!p zW%ry<;rz<_Z%TE7?f19!=kOn?`6)^NKM0M*MKV%Yf9^{Djm9<>Kytp5!)Sc=vi~TQ8Xw;LJ8&`= hP1.Y And If $iY < hP1.Y + hP1.Height Then GoSub _OnPaddle + If hP2 And If $iX = hP2.X And If $iY >= hP2.Y And If $iY < hP2.Y + hP2.Height Then GoSub _OnPaddle + ' Flip at top/bottom border? + If $iY < 0 Or $iY >= $hWindow.Height Then + GoSub _UndoMovement + $iVD = -$iVD + Move(hP1, hP2) + Endif + ' Made a point (left/right border)? + If $iX < 0 Or $iX >= $hWindow.Width Then + GoSub _UndoMovement + Return $iHD + Endif + Return 0 + +_UndoMovement: + $iX -= $iHD + $iY -= $iVD + Return + +_OnPaddle: + GoSub _UndoMovement + $iHD = -$iHD + Move(hP1, hP2) + $bHitPaddle = True + +End + +Public Sub Draw() + + If $bSimulate Then Return + $hWindow.Print("o", $iX, $iY) + +End + +Public Sub Undraw() + + If $bSimulate Then Return + $hWindow.Print(" ", $iX, $iY) + +End + +Private Sub HDir_Read() As Integer + + Return $iHD + +End + +Private Sub HDir_Write(Value As Integer) + + $iHD = Value + +End + +Private Sub VDir_Read() As Integer + + Return $iVD + +End + +Private Sub VDir_Write(Value As Integer) + + $iVD = Value + +End + +Private Function X_Read() As Integer + + Return $iX + +End + +Private Sub X_Write(Value As Integer) + + $iX = Value + +End + +Private Function Y_Read() As Integer + + Return $iY + +End + +Private Sub Y_Write(Value As Integer) + + $iY = Value + +End + +Private Sub HitPaddle_Read() As Boolean + + Return $bHitPaddle + +End diff --git a/app/examples/Games/Pong/.src/MMain.module b/app/examples/Games/Pong/.src/MMain.module new file mode 100644 index 00000000..486dcb4c --- /dev/null +++ b/app/examples/Games/Pong/.src/MMain.module @@ -0,0 +1,191 @@ +' Gambas module file + +Private $hConfig As Window +Private $hTimer As Timer +' Scores +Private $iS1 As Integer +Private $iS2 As Integer +' Players' paddles +Private $hP1 As Paddle +Private $hP2 As Paddle +' If Paddle2 shall be controlled by an NPC object +Private $bNPC As Boolean +Private $hNPC As NPC +' The ball +Private $hBall As Ball + +Public Sub Main() + + ' Make our Window raise events + Object.Attach(Window, Me, "Window") + Window.SetFocus() + + Screen.Cursor = Cursor.Hidden + Screen.Echo = False + Screen.Input = Input.CBreak + + Window.Border = Border.Ascii + Window.Caption = Subst$(("Pong v&1 - gb.ncurses Example"), Application.Version) + Window.Clear() + + $hConfig = New Window(True, 0, 0, 30, 5) + $hConfig.Border = Border.Ascii + $hConfig.Center() + + $hTimer = New Timer As "Timer" + + $iS1 = 0 + $iS2 = 0 + + $bNPC = True + GameInit() + +End + +Private Sub Configure() + + $hConfig.Show() + $hConfig.Clear() + $hConfig.PrintCenter(("What to do?\nPlay again: [P]\nChange opponent: [o]\n\nQuit [q]")) + Select Case Window.Ask(("Poq")) + Case ("o") + Configure_Opponent() + Case ("q") + Quit + End Select + $hConfig.Hide() + +End + +Private Sub Configure_Opponent() + + $hConfig.Clear() + $hConfig.PrintCenter(("Play against whom?\n[h]uman, [N]ormal, [m]aster")) + $bNPC = True + Select Case Window.Ask(("hNm")) + Case ("h") + $bNPC = False + Case ("n") + $hNPC.Mode = NPC.Normal + Case ("m") + $hNPC.Mode = NPC.Master + End Select + +End + +Private Sub GameInit() + + ' If both as 'master' NPCs we could make a screensaver out of it :-) + $hP1 = New Paddle(Window, 1) + $hP2 = New Paddle(Window, -1) + $hBall = New Ball(Window) + $hNPC = New NPC(Window, $hBall, $hP2) + GameStart() + +End + +Private Sub GameStart() + + Window.Buffered = False + Configure() + $hP1.Reset() + $hP2.Reset() + $hBall.Reset() + $hNPC.Init() + Window.Buffered = True + Redraw() + ' See SPEED. The width means actually the intra-paddle width: + $hTimer.Delay = 4000 / Sqr(2 * (Window.Width - 4) ^ 2) + $hTimer.Start() + +End + +Public Sub Timer_Timer() + + If $bNPC Then $hNPC.Move() + Select Case $hBall.Move($hP1, $hP2) + Case -1 + Inc $iS2 + Goto _NewGame + Case 1 + Inc $iS1 + Goto _NewGame + End Select + ' I suppose that this does not go down to zero + If $hBall.HitPaddle Then Dec $hTimer.Delay + Redraw() + Return + +_NewGame: + ' We want to see it + Redraw() + $hTimer.Stop() + ' FIXME: Makes stack overflow eventually + GameStart() + +End + +Public Sub Window_Read() + + Dim iKey As Integer + + iKey = Window.Read() + Window.Drain() + ' Go two steps per keystroke + ' FIXME: Can't reach some coordinate with odd Window.Height + Select Case iKey + ' Player 1 + Case Key.Up + If $hP1.Y > 0 Then $hP1.Y -= 2 + Case Key.Down + If $hP1.Y + $hP1.Height < Window.Height Then $hP1.Y += 2 + ' Human player 2 + Case Asc("j") + If Not $bNPC And $hP2.Y > 0 Then $hP2.Y -= 2 + Case Asc("k") + If Not $bNPC And $hP2.Y + $hP2.Height < Window.Height Then $hP2.Y += 2 + ' Change ball speed :-) + Case Asc("+") + If $hTimer.Delay > 10 Then $hTimer.Delay -= 10 + Case Asc("-") + $hTimer.Delay += 10 + ' Pause + Case Asc(("p")) + Pause() + ' Quit + Case Asc(("q")) + Quit + Case Else + Return + End Select + Redraw() + +End + +Private Sub Redraw() + + Dim iX As Integer = Window.Width / 4 + + ' Scores and net + Window.Print(" " & Str$($iS1) & " ", iX, 1, Attr.Reverse) + Window.Print(" " & Str$($iS2) & " ", iX * 3, 1, Attr.Reverse) + Window.DrawVLine(Window.Width / 2 + 1, 0, Window.Height, ".") + ' Players and ball may overwrite the unimportant stuff above + $hP1.Draw() + $hP2.Draw() + $hBall.Draw() + Screen.Refresh() + +End + +Private Sub Pause() + + $hTimer.Stop() + Window.PrintCenter(("PAUSE")) + Screen.Refresh() + Window.Ask(("p")) + Window.Clear() + Redraw() + $hTimer.Start() + +End diff --git a/app/examples/Games/Pong/.src/NPC.class b/app/examples/Games/Pong/.src/NPC.class new file mode 100644 index 00000000..fd651a8f --- /dev/null +++ b/app/examples/Games/Pong/.src/NPC.class @@ -0,0 +1,116 @@ +' Gambas class file + +Public Const Normal As Integer = 0 +Public Const Master As Integer = 1 + +Property Mode As Integer + +Private $hWindow As Window +' Ball is needed to calculate movements, of course >:-) +Private $hBall As Ball +Private $hPaddle As Paddle +Private $iMode As Integer + +' This is the Y coordinate we wish to reach with the middle of our paddle +Private $iY As Integer +Private $bReady As Boolean + +' Master's control data +Private $iLastDir As Integer +Private $hMyPaddle As Paddle + +Public Sub _new(hWnd As Window, hBall As Ball, hPaddle As Paddle, Optional iMode As Integer = Normal) + + $hWindow = hWnd + $hBall = hBall + $hPaddle = hPaddle + $iMode = iMode + +End + +Public Sub Init() + + $bReady = False +$iLastDir = $hBall.HDir + ' We need to insert an opponent paddle that would block everything to not + ' go into an infinite loop when trying to calculate the ball's positions + $hMyPaddle = New Paddle($hWindow, -$hPaddle.Dir) + $hMyPaddle.Reset() + $hMyPaddle.Y = 0 + $hMyPaddle.Height = $hWindow.Height + +End + +Public Sub Move() + + If $iMode = Normal Then + Move_Normal() + Else If $iMode = Master Then + Move_Master() + Endif + +End + +Private Sub Move_Normal() + + ' Make him beatable... + If CInt(Rnd(0, 2)) Then Return + ' Just follow the ball + $iY = $hBall.Y + Move_Generic() + +End + +Private Sub Move_Master() + + ' Calculate the future ball's position and move accordingly + If Not $bReady Then MasterCalc() + Move_Generic() + ' If the ball hits this paddle, we can begin calculating again + If $hBall.HDir <> $iLastDir And $iLastDir = -$hPaddle.Dir Then + $bReady = False + Endif + $iLastDir = $hBall.HDir + +End + +Private Sub Move_Generic() + + Dim iMid As Integer = $hPaddle.Y + ($hPaddle.Height / 2) + Dim iDiff As Integer = $iY - iMid + + If Abs(iDiff) <= 1 Then Return + $hPaddle.Y += 2 * Sgn(iDiff) + +End + +Private Sub MasterCalc() + + Dim hMyBall As New Ball($hWindow, True) + + ' Use the CBall class to sneakily get the position we have to sit on + ' when the ball arrives at this end + hMyBall.HDir = $hBall.HDir + hMyBall.VDir = $hBall.VDir + hMyBall.X = $hBall.X + hMyBall.Y = $hBall.Y + ' Simulate the ball flying thither and back again + While hMyBall.X <> $hPaddle.X + hMyBall.Move($hMyPaddle, Null) + Wend + $iY = hMyBall.Y + $bReady = True + +End + +Private Sub Mode_Read() As Integer + + Return $iMode + +End + +Private Sub Mode_Write(Value As Integer) + + $iMode = Value + +End diff --git a/app/examples/Games/Pong/.src/Paddle.class b/app/examples/Games/Pong/.src/Paddle.class new file mode 100644 index 00000000..09a745fb --- /dev/null +++ b/app/examples/Games/Pong/.src/Paddle.class @@ -0,0 +1,96 @@ +' Gambas class file + +Property X As Integer +Property Y As Integer +Property Height As Integer +Property Read Dir As Integer + +' Direction in which to play +Private $iDir As Integer +Private $hWindow As Window +Private $iOrigX As Integer +Private $iOrigY As Integer +Private $iX As Integer +Private $iY As Integer +Private $iHeight As Integer + +Public Sub _new(hWnd As Window, iDir As Integer) + + $hWindow = hWnd + $iHeight = hWnd.Height / 6 + 1 + $iDir = iDir + If iDir > 0 Then + $iOrigX = 1 + Else + $iOrigX = hWnd.Width - 2 + Endif + $iOrigY = (hWnd.Height - $iHeight) / 2 + If Odd($iOrigY) Then Inc $iOrigY + +End + +Public Sub Reset() + + Undraw() + $iX = $iOrigX + $iY = $iOrigY + +End + +Public Sub Draw() + + $hWindow.DrawVLine($iX, $iY, $iHeight, "|") + +End + +Public Sub Undraw() + + $hWindow.DrawVLine($iX, $iY, $iHeight, " ") + +End + +Private Sub X_Read() As Integer + + Return $iX + +End + +Private Sub X_Write(Value As Integer) + + If Value < 0 Or If Value + 1 > $hWindow.Width Then Return + Undraw() + $iX = Value + +End + +Private Sub Y_Read() As Integer + + Return $iY + +End + +Private Sub Y_Write(Value As Integer) + + If Value < 0 Or If Value + $iHeight > $hWindow.Height Then Return + Undraw() + $iY = Value + +End + +Private Function Height_Read() As Integer + + Return $iHeight + +End + +Private Sub Height_Write(Value As Integer) + + $iHeight = Value + +End + +Private Function Dir_Read() As Integer + + Return $iDir + +End diff --git a/app/examples/Games/Pong/SPEED b/app/examples/Games/Pong/SPEED new file mode 100644 index 00000000..2d7658ab --- /dev/null +++ b/app/examples/Games/Pong/SPEED @@ -0,0 +1,47 @@ +Different people have different terminal dimensions. The following shall +outline my attempt to make Pong soundly playable with every one. + +Consider this picture. Assume that it is your terminal: + +........... Legend: +, -, | = Real border (of terminal) +. '. . = Imaginary border +. ' . o = Ball +. ' . " = Real flying behaviour +. ' . ' = Equivalent imaginary flying behaviour ++----'----+ The real and imaginary border make a square w^2. +| " | \ +| " " "| | +| " " " | | Height h +|o " | / ++---------+ + \_______/ + Width w + +We clearly want that the ball gets some velocity linearly proportional to +the distance it has to fly so that the time the ball needs (and the human +player has time to react) is constant. + +As you can see, the distance from paddle 1 to paddle 2 is sqrt(2w^2) units. +The ball goes one unit per Timer event. We need a Timer.Delay that makes the +constant time elapse after sqrt(2w^2) Timer events. + +Let's arbitrarily set the time from one paddle to the other to 4s: + + Timer.Delay = 4000 / Sqr(2 * w ^ 2). + +This is the starting delay. With each hit on a paddle, it decrements. + +This was tested with ANSI's 80*24 terminal dimensions in xterm and my home +180*74 on the Linux console. Feel free to set the LINES and COLUMNS +environment variables to try different proportions. + +One may see the crux with this approach: The larger/smaller the terminal, +the faster/slower the ball. I found both scenarios equally hard: with a +large terminal, the paddle is large; with a small terminal, the paddle is +small. It compensates. + +TODO: The above is not true for terminals which are much _wider_ than high. + I found it problematic to play "LINES=10" but easy with "COLUMNS=30". + +BTW: The NPCs make one step per each ball step. You may have to resize your + terminal to be able to win against 'master' ;-) diff --git a/app/examples/Games/Pong/pong.png b/app/examples/Games/Pong/pong.png new file mode 100644 index 0000000000000000000000000000000000000000..e17e6d06a6a181530d684b20f09fbc2135c32732 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp9L@-6KI9PZ!4!j_b(@4b0qsTo^lEI4Gq! jBpE40F5+Ml;^bgpFyQ6bn!qvh4M>lttDnm{r-UW|)npR= literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/.directory b/app/examples/Games/Puzzle1To8/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/Puzzle1To8/.icon.png b/app/examples/Games/Puzzle1To8/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8974e481fb528ee79b660581b3a16beef405f087 GIT binary patch literal 4303 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/.lang/ca.po b/app/examples/Games/Puzzle1To8/.lang/ca.po new file mode 100644 index 00000000..698cb846 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/ca.po @@ -0,0 +1,98 @@ +# Catalan translation of Puzzle1To8 +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Puzzle1To8 package. +# Jordi Sayol , 2007-2010. +# +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Puzzle1To8 3.13.90\n" +"PO-Revision-Date: 2019-06-04 19:27 UTC\n" +"Last-Translator: benoit \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "Puzzle 1 a 8" + +#: .project:2 FrmAyuda.form:19 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Poseu els números de l'1 al 8 a cada una de les caixes sense repetir-los, en que cada caixa no contingui números correlatius amb les caixes veïnes o adjacents. Mireu l'exemple següent:" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "N'esteu segurs?" + +# gb-ignore +#: FMain.class:69 +msgid "No" +msgstr "" + +#: FMain.class:69 +msgid "Yes" +msgstr "Sí" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Felicitats! Ho heu fet!" + +#: FMain.form:22 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzle 1 a 8 - Troba els números!" + +#: FMain.form:27 +msgid "Game" +msgstr "Joc" + +#: FMain.form:30 +msgid "Clear" +msgstr "Neteja" + +#: FMain.form:35 +msgid "Quit" +msgstr "Surt" + +#: FMain.form:41 +msgid "Help" +msgstr "Ajuda" + +#: FMain.form:44 FrmAyuda.form:14 +msgid "How to play?" +msgstr "Com jugar-hi?" + +#: FMain.form:49 FrmAbout.form:12 +msgid "About" +msgstr "" + +#: FrmAbout.form:21 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:25 +msgid "Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n
pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Podeu enviar les vostres preguntes, suggeriments, errors, etc. a aquesta adreça de correu electrònic:\n pablomileti@gmail.com" + +#: FrmAbout.form:29 +msgid "License" +msgstr "Llicència" + +#: FrmAbout.form:33 +msgid "\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "\n Copyright (C) 2010. Autor: Pablo Mileti \n\nAquest programa és programari lliure; el podeu distribuir i/o modificar sota els termes de la llicència GNU General Public License tal i com està publicada per la Free Software Foundation; ja sigui la versió 3 de la llicència, o bé (si ho preferiu) qualsevol versió posterior.\n\nAquest programa es distribueix amb la voluntat que pugui ser útil però SENSE CAP GARANTIA; ni tant sols les garanties implícites MERCANTILS o ESPECÍFIQUES PER UN PROPÒSIT DETERMINAT. Si voleu més informació, vegeu la llicència GNU General Public Licence.\n\nHauríeu d'haver rebut una còpia de la GNU General Public Licence juntament amb aquest programa. Si no fos així, mireu ." + +#: FrmAbout.form:43 FrmAyuda.form:43 +msgid "Close" +msgstr "" + +#: FrmAyuda.form:31 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Manera incorrecta:

Els números 1 i 2 són consecutius i estan en caixes adjacents, o sigui que no està permès." + +#: FrmAyuda.form:48 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Manera correcta:

No hi ha correlativitat entre els números respecte de les caixes adjacents, o sigui que està permès." + diff --git a/app/examples/Games/Puzzle1To8/.lang/cs.po b/app/examples/Games/Puzzle1To8/.lang/cs.po new file mode 100644 index 00000000..1e6a9407 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/cs.po @@ -0,0 +1,92 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Puzzle1To8 3.13.90\n" +"PO-Revision-Date: 2019-06-04 19:28 UTC\n" +"Last-Translator: benoit \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "Puzzle 1 z 8" + +#: .project:2 FrmAyuda.form:19 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Umístěte čísla 1 až 8 v každém z polí bez opakování, takže každý box neobsahuje souvztažnosti čísla na své sousedy nebo přilehlehlé pole. Viz následující příklady:" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "Jsi si jistý?" + +#: FMain.class:69 +msgid "No" +msgstr "Ne" + +#: FMain.class:69 +msgid "Yes" +msgstr "Ano" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Gratuluji! Dokázal si to!" + +#: FMain.form:22 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzzle 1 z 8 - Umísťování čísel!" + +#: FMain.form:27 +msgid "Game" +msgstr "Hra" + +#: FMain.form:30 +msgid "Clear" +msgstr "Vyčistit" + +#: FMain.form:35 +msgid "Quit" +msgstr "Ukončit" + +#: FMain.form:41 +msgid "Help" +msgstr "Nápověda" + +#: FMain.form:44 FrmAyuda.form:14 +msgid "How to play?" +msgstr "Jak hrát?" + +#: FMain.form:49 FrmAbout.form:12 +msgid "About" +msgstr "" + +#: FrmAbout.form:21 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:25 +msgid "Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Můžete odeslat Vaše dotazy, připomínky, chyby, atd., na následující e-mailovou adresu:\n pablomileti@gmail.com" + +#: FrmAbout.form:29 +msgid "License" +msgstr "Kicence" + +# gb-ignore +#: FrmAbout.form:33 +msgid "\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" + +#: FrmAbout.form:43 FrmAyuda.form:43 +msgid "Close" +msgstr "" + +#: FrmAyuda.form:31 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Špatná cesta:

Čísla 1 a 2 jsou po sobě jdoucí a jsou v přilehlých polí, tak to není dovoleno." + +#: FrmAyuda.form:48 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Správná cesta:

Není mezi korelativností na zadaná čísla s ohledem na jejich přilehlé pole, takže je to dovoleno." + diff --git a/app/examples/Games/Puzzle1To8/.lang/de.po b/app/examples/Games/Puzzle1To8/.lang/de.po new file mode 100644 index 00000000..1ec4d723 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/de.po @@ -0,0 +1,112 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2019-06-04 19:27 UTC\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "Puzzle 1 bis 8" + +#: .project:2 FrmAyuda.form:19 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Setze die Zahlen von 1 bis 8 ohne Wiederholung so in die Kästchen, dass kein direkt oder schräg angrenzendes Feld eine benachbarte Zahl enthält. Siehe die folgenden Beispiele:" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "Bist du sicher?" + +#: FMain.class:69 +msgid "No" +msgstr "Nein" + +#: FMain.class:69 +msgid "Yes" +msgstr "Ja" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Glückwunsch! Du hast es geschafft!" + +#: FMain.form:22 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzzle 1 bis 8 - Platziere die Zahlen!" + +#: FMain.form:27 +msgid "Game" +msgstr "Spiel" + +#: FMain.form:30 +msgid "Clear" +msgstr "Löschen" + +#: FMain.form:35 +msgid "Quit" +msgstr "Beenden" + +#: FMain.form:41 +msgid "Help" +msgstr "Hilfe" + +#: FMain.form:44 FrmAyuda.form:14 +msgid "How to play?" +msgstr "Spielanleitung" + +#: FMain.form:49 FrmAbout.form:12 +msgid "About" +msgstr "Über" + +#: FrmAbout.form:21 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:25 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Autor: Pablo Mileti.

Du kannst Fragen, Vorschläge, Bugs usw. an diese E-Mail-Adresse senden:\n" +" pablomileti@gmail.com" + +#: FrmAbout.form:29 +msgid "License" +msgstr "Lizenz" + +#: FrmAbout.form:33 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" +"\n" +" Copyright (C) 2010. Autor: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." + +#: FrmAbout.form:43 FrmAyuda.form:43 +msgid "Close" +msgstr "Schließen" + +#: FrmAyuda.form:31 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Unkorrekt:

Die Zahlen 1 und 2 sind fortlaufend und in benachbarten Feldern, also ungültig." + +#: FrmAyuda.form:48 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Korrekt:

Keine aufeinanderfolgenden Zahlen stehen in benachbarten Feldern, das ist erlaubt." diff --git a/app/examples/Games/Puzzle1To8/.lang/es_AR.po b/app/examples/Games/Puzzle1To8/.lang/es_AR.po new file mode 100644 index 00000000..f3efd468 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/es_AR.po @@ -0,0 +1,94 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Puzzle1To8 3.13.90\n" +"PO-Revision-Date: 2019-06-04 19:27 UTC\n" +"Last-Translator: benoit \n" +"Language: es_AR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# gb-ignore +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "" + +#: .project:2 FrmAyuda.form:19 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Ubique los números del 1 al 8 en cada uno de los casilleros sin repetirlos, de manera tal que cada casillero no contenga un número consecutivo al de sus casilleros vecinos o limítrofes. Observe los siguientes ejemplos:" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "Desea salir?" + +# gb-ignore +#: FMain.class:69 +msgid "No" +msgstr "" + +#: FMain.class:69 +msgid "Yes" +msgstr "Si" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Felicitaciones! Lo lograste!" + +#: FMain.form:22 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzzle 1 to 8 - Disperse los números!" + +#: FMain.form:27 +msgid "Game" +msgstr "Juego" + +#: FMain.form:30 +msgid "Clear" +msgstr "Limpiar" + +#: FMain.form:35 +msgid "Quit" +msgstr "Salir" + +#: FMain.form:41 +msgid "Help" +msgstr "Ayuda" + +#: FMain.form:44 FrmAyuda.form:14 +msgid "How to play?" +msgstr "Cómo jugar?" + +#: FMain.form:49 FrmAbout.form:12 +msgid "About" +msgstr "" + +#: FrmAbout.form:21 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:25 +msgid "Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Pueden enviar sus dudas, sugerencias, bugs, etc, a la siguiente dirección de correo electrónico: pablomileti@gmail.com" + +#: FrmAbout.form:29 +msgid "License" +msgstr "Licencia" + +# gb-ignore +#: FrmAbout.form:33 +msgid "\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" + +#: FrmAbout.form:43 FrmAyuda.form:43 +msgid "Close" +msgstr "" + +#: FrmAyuda.form:31 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Disposición incorrecta:

Los números 1 y 2 son consecutivos y estan en casilleros lindantes, por lo tanto es una disposición no permitida." + +#: FrmAyuda.form:48 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Disposición válida:

No existe correlatividad en los números ingresados con respecto a sus casilleros lindantes, por lo tanto es una disposición permitida." + diff --git a/app/examples/Games/Puzzle1To8/.lang/fr.po b/app/examples/Games/Puzzle1To8/.lang/fr.po new file mode 100644 index 00000000..4a2db834 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/fr.po @@ -0,0 +1,111 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Puzzle1To8 3.13.90\n" +"POT-Creation-Date: 2019-06-04 19:28 UTC\n" +"PO-Revision-Date: 2019-06-04 19:25 UTC\n" +"Last-Translator: benoit \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "Puzzle 1 à 8" + +#: .project:2 FrmAyuda.form:19 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Placez les nombres 1 à 8 dans chacune des cases sans vous répéter, de sorte que chaque boîte ne contienne pas un nombre corrélatif à ses voisins ou à des cases adjacentes. Voir les exemples suivants :" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "Es-tu sûr ?" + +#: FMain.class:69 +msgid "No" +msgstr "Non" + +#: FMain.class:69 +msgid "Yes" +msgstr "Oui" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Félicitations ! Vous avez réussi !" + +#: FMain.form:22 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzzle 1 à 8 - Trouvez les chiffres !" + +#: FMain.form:27 +msgid "Game" +msgstr "Jeu" + +#: FMain.form:30 +msgid "Clear" +msgstr "Effacer" + +#: FMain.form:35 +msgid "Quit" +msgstr "Quitter" + +#: FMain.form:41 +msgid "Help" +msgstr "Aide" + +#: FMain.form:44 FrmAyuda.form:14 +msgid "How to play?" +msgstr "Comment jouer ?" + +#: FMain.form:49 FrmAbout.form:12 +msgid "About" +msgstr "A propos" + +#: FrmAbout.form:21 +msgid "Author" +msgstr "Auteur" + +#: FrmAbout.form:25 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Auteur: Pablo Mileti.

Vous pouvez soumettre vos questions, suggestions, bogues, etc. à l’adresse E-Mail suivante :\n" +"pablomileti@gmail.com" + +#: FrmAbout.form:29 +msgid "License" +msgstr "Licence" + +#: FrmAbout.form:33 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" +"\n" +"Copyright (C) 2010. Auteur: Pablo Mileti\n" +"\n" +"Ce programme est un logiciel libre: vous pouvez le redistribuer et/ou le modifier selon les termes de la licence publique générale GNU telle que publiée par la Free Software Foundation, soit la version 3 de la licence, soit (à votre discrétion) toute version ultérieure.\n" +"\n" +"Ce programme est distribué dans l’espoir qu’il sera utile, mais sans aucune garantie; sans même la garantie implicite de qualité marchande ou d’adéquation à un usage particulier. Pour plus de détails, consultez la licence publique générale GNU.\n" +"\n" +"Vous auriez dû recevoir une copie de la licence publique générale GNU ainsi que ce programme. Sinon, voyez ." + +#: FrmAbout.form:43 FrmAyuda.form:43 +msgid "Close" +msgstr "Fermer" + +#: FrmAyuda.form:31 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Manière incorrecte:

Les nombres 1 et 2 sont consécutifs et sont dans des cases adjacentes, il n’est donc pas autorisé." + +#: FrmAyuda.form:48 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Façon correcte:

Il n’y a pas de corrélation entre les nombres entrés par rapport à leurs cases adjacentes, donc c’est permis." diff --git a/app/examples/Games/Puzzle1To8/.lang/ru.po b/app/examples/Games/Puzzle1To8/.lang/ru.po new file mode 100644 index 00000000..b7a01ec7 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/ru.po @@ -0,0 +1,126 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-06-17 02:57+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/Puzzle1To8/.project:18 +msgid "Puzzle 1 to 8" +msgstr "Головоломка с 1 по 8" + +#: app/examples/Games/Puzzle1To8/.project:19 app/examples/Games/Puzzle1To8/.src/FrmAyuda.form:10 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Разместите числа от 1 до 8 в каждом из блоков без повторения, чтобы каждый блок не содержал номера, соответствующего его соседям или смежным блокам. Смотрите следующие примеры:" + +#: app/examples/Games/Puzzle1To8/.src/FMain.class:61 app/examples/Games/Puzzle1To8/.src/FMain.form:6 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Головоломка с 1 по 8 - разместите номера!" + +#: app/examples/Games/Puzzle1To8/.src/FMain.class:69 +msgid "Are you sure?" +msgstr "Вы уверены?" + +#: app/examples/Games/Puzzle1To8/.src/FMain.class:69 +msgid "Yes" +msgstr "Да" + +#: app/examples/Games/Puzzle1To8/.src/FMain.class:69 +msgid "No" +msgstr "Нет" + +#: app/examples/Games/Puzzle1To8/.src/FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Поздравляем! Вы сделали это!" + +#: app/examples/Games/Puzzle1To8/.src/FMain.form:10 +msgid "Game" +msgstr "Игра" + +#: app/examples/Games/Puzzle1To8/.src/FMain.form:12 +msgid "Clear" +msgstr "Очистить" + +#: app/examples/Games/Puzzle1To8/.src/FMain.form:16 +msgid "Quit" +msgstr "Выход" + +#: app/examples/Games/Puzzle1To8/.src/FMain.form:21 +msgid "Help" +msgstr "Справка" + +#: app/examples/Games/Puzzle1To8/.src/FMain.form:23 app/examples/Games/Puzzle1To8/.src/FrmAyuda.form:6 +msgid "How to play?" +msgstr "Как играть?" + +#: app/examples/Games/Puzzle1To8/.src/FMain.form:27 app/examples/Games/Puzzle1To8/.src/FrmAbout.form:5 +msgid "About" +msgstr "О программе" + +#: app/examples/Games/Puzzle1To8/.src/FrmAbout.form:13 +msgid "Author" +msgstr "Автор" + +#: app/examples/Games/Puzzle1To8/.src/FrmAbout.form:16 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Автор: Пабло Милети.

Вы можете отправить свои вопросы, предложения, ошибки и т. д. на следующий электронный адрес:\n" +"pablomileti@gmail.com" + +#: app/examples/Games/Puzzle1To8/.src/FrmAbout.form:20 +msgid "License" +msgstr "Лицензия" + +#: app/examples/Games/Puzzle1To8/.src/FrmAbout.form:23 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" +"\n" +" Copyright (C) 2010. Автор: Пабло Милети\n" +"\n" +"Эта программа ― свободное программное обеспечение. Вы можете распространять или изменять его при условиях соответствия лицензии GNU General Public License опубликованной Free Software Foundation; либо версии 3 лицензии, либо (на ваше усмотрение) любой более поздней версии.\n" +"\n" +"Программа распространяется в надежде на то, что приложение будет полезно, но БЕЗ ВСЯКИХ ГАРАНТИЙ; не гарантируется даже ПРИГОДНОСТЬ или СООТВЕТСТВИЕ КАКИМ-ЛИБО ТРЕБОВАНИЯМ. Для получения дополнительной информации ознакомьтесь с лицензией GNU General Public License.\n" +"\n" +"Вы должны получить копию лицензии GNU General Public License вместе с программой. Если этого не произошло, посмотрите ." + +#: app/examples/Games/Puzzle1To8/.src/FrmAbout.form:32 app/examples/Games/Puzzle1To8/.src/FrmAyuda.form:30 +msgid "Close" +msgstr "Закрыть" + +#: app/examples/Games/Puzzle1To8/.src/FrmAyuda.form:20 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Неверный путь:

числа 1 и 2 являются последовательными и находятся в соседних блоках, поэтому это не допускается." + +#: app/examples/Games/Puzzle1To8/.src/FrmAyuda.form:34 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Неверный путь:

не существует корреляции между числами относительно их соседних блоков, поэтому это разрешено." + diff --git a/app/examples/Games/Puzzle1To8/.project b/app/examples/Games/Puzzle1To8/.project new file mode 100644 index 00000000..06150596 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Title=Puzzle 1 to 8 +Startup=FMain +Icon=logo.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.vb +Description="Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +Authors="Pablo Mileti" +Environment="GB_GUI=gb.gtk3" +TabSize=2 +Translate=1 +Language=en_US +Packager=1 diff --git a/app/examples/Games/Puzzle1To8/.src/Casillero.class b/app/examples/Games/Puzzle1To8/.src/Casillero.class new file mode 100644 index 00000000..3d3fc714 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/Casillero.class @@ -0,0 +1,66 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + +'****English documentation***** +'This class represents each box of the game (8) + +' Public Var: +' Value ---> Contains the number that was put in a box +' isEmpty ---> Return true if value is 0, else return false +' isInvalid ---> Return true if Value there are a conflict with the value of another box + +' Public Methods: +'setValue(nValue As Integer) ---> Put a value in a box +'validateWith(nBox As Casillero) ---> Return true if there are a conflict with a neighbor box + + + +'* * * * Documentación en español******* +' Esta clase corresponde a representar cada uno de los 8 casilleros del juego + +' Variables públicas: +' Value ---> Contiene el numero que se ingreso en el casillero +' isEmpty ---> True si no hay valor, False si lo tiene +' isInvalid ---> True si hay un numero que esta en conflicto con otro casillero + +' Metodos públicos: +'setValue(nValue As Integer) - - > Se encarga de almacenar el nro de un casillero +'validateWith(nBox As Casillero) ---> Devuelve true si hay conflicto con otro casillero + +Public Value As Integer +Public isEmpty As Boolean +Public isInvalid As Boolean + +Public Sub _new() + Value = 0 + isEmpty = True + isInvalid = True +End + +Public Sub setValue(nValue As Integer) + Value = nvalue + If nvalue = 0 Then + isEmpty = True + Else + isEmpty = False + End If +End + +Public Function validateWith(nBox As Casillero) As Boolean + If isEmpty Or nBox.isEmpty Then Return False + If value + 1 = nBox.Value Then Return True + If value - 1 = nBox.Value And Value > 1 Then Return True +End + + + + + diff --git a/app/examples/Games/Puzzle1To8/.src/Esquema.class b/app/examples/Games/Puzzle1To8/.src/Esquema.class new file mode 100644 index 00000000..17cc9657 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/Esquema.class @@ -0,0 +1,110 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + + +'****English documentation***** +'This class represents the gameboard, all boxes of the game. + +'Public var: +'Boxes---> Array of Casillero (box), there are 9 elements, it uses from 1 to 8. + +'Public methods: +'Set---> Put a value in a object Casillero (box). Receive 2 parameters: the position of the box in the gameboard and the value to put, return true if there are a conflict with another box. +'isRepeats---> Return true if value already exists in another box. +'isInvalid---> Compare a determinated box (parameter), with your neighbor boxes. Return true if there are conflicts. +'isWin---> If all boxes have a correct value and nothing is empty then return true - Win! + + + + +'* * * * * Documentación en español ****** +'Esta clase representa el juego en conjunto, es decir integro los 8 casilleros + +'Variables públicas: +'Boxes---> Array de Casilleros, son 9 elementos, se usan del 1 al 8. + +'Métodos públicos: +'Set---> Se encarga de darle un valor a cada Casillero. Recibe 2 parametros el nro de casillero y el valor, devuelve true si ese valor entra en conflicto con otro casillero +'isRepeats--->Devuelve true si el valor de un casillero ya esta en otro. +'isInvalid---> Compara un determinado casillero, segun su parametro, con sus casilleros vecinos. Devuelve true si hay conflicto +'isWin---> Verifica si todos los casilleros tienen un valor correcto, si todos son correctos y no hay ninguno vacio devuelve true. + +Public Boxes As New Casillero[9] + +Public Sub _new() + Dim i As Integer + For i = 1 To 8 + Boxes[i] = New Casillero + Next +End + +Public Function Set(nBox As Integer, nValue As Integer) As Boolean + Boxes[nbox].setValue(nValue) + If (isRepeats(nBox) And nValue <> 0) Or isInvalid(nBox) Then + Boxes[nBox].isInvalid = True + Return True + Else + Boxes[nBox].isInvalid = False + Return False + End If +End + +Private Function isRepeats(nBox As Integer) As Boolean + Dim i As Integer + For i = 1 To 8 + If i <> nBox Then + If Boxes[nBox].Value = Boxes[i].Value Then + Return True + End If + End If + Next + Return False +End + +Private Function isInvalid(nBox As Integer) As Boolean +Select Case nBox +Case 1 + Boxes[1].isInvalid = Boxes[1].validateWith(Boxes[2]) Or Boxes[1].validateWith(Boxes[3]) Or Boxes[1].validateWith(Boxes[4]) + Return Boxes[1].isInvalid +Case 2 + Boxes[2].isInvalid = Boxes[2].validateWith(Boxes[1]) Or Boxes[2].validateWith(Boxes[3]) Or Boxes[2].validateWith(Boxes[5]) Or Boxes[2].validateWith(Boxes[6]) + Return Boxes[2].isInvalid +Case 3 + Boxes[3].isInvalid = Boxes[3].validateWith(Boxes[1]) Or Boxes[3].validateWith(Boxes[2]) Or Boxes[3].validateWith(Boxes[4]) Or Boxes[3].validateWith(Boxes[5]) Or Boxes[3].validateWith(Boxes[6]) Or Boxes[3].validateWith(Boxes[7]) + Return Boxes[3].isInvalid +Case 4 + Boxes[4].isInvalid = Boxes[4].validateWith(Boxes[1]) Or Boxes[4].validateWith(Boxes[3]) Or Boxes[4].validateWith(Boxes[6]) Or Boxes[4].validateWith(Boxes[7]) + Return Boxes[4].isInvalid +Case 5 + Boxes[5].isInvalid = Boxes[5].validateWith(Boxes[2]) Or Boxes[5].validateWith(Boxes[3]) Or Boxes[5].validateWith(Boxes[6]) Or Boxes[5].validateWith(Boxes[8]) + Return Boxes[5].isInvalid +Case 6 + Boxes[6].isInvalid = Boxes[6].validateWith(Boxes[2]) Or Boxes[6].validateWith(Boxes[3]) Or Boxes[6].validateWith(Boxes[4]) Or Boxes[6].validateWith(Boxes[5]) Or Boxes[6].validateWith(Boxes[7]) Or Boxes[6].validateWith(Boxes[8]) + Return Boxes[6].isInvalid +Case 7 + Boxes[7].isInvalid = Boxes[7].validateWith(Boxes[3]) Or Boxes[7].validateWith(Boxes[4]) Or Boxes[7].validateWith(Boxes[6]) Or Boxes[7].validateWith(Boxes[8]) + Return Boxes[7].isInvalid +Case 8 + Boxes[8].isInvalid = Boxes[8].validateWith(Boxes[5]) Or Boxes[8].validateWith(Boxes[6]) Or Boxes[8].validateWith(Boxes[7]) + Return Boxes[8].isInvalid +End Select +End + + +Public Function isWin() As Boolean + Dim i As Integer + For i = 1 To 8 + If Boxes[i].isInvalid Or Boxes[i].isEmpty Then + Return False + End If + Next + Return True +End diff --git a/app/examples/Games/Puzzle1To8/.src/FMain.class b/app/examples/Games/Puzzle1To8/.src/FMain.class new file mode 100644 index 00000000..de579899 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FMain.class @@ -0,0 +1,111 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + + +'****English documentation***** +'Public sub +'Winner() ---> Procedure to Finish the game and write a congratulation message! +'updateBoxes()---> Rewrite the values in each box to compare with the last value wrote. If it presents conflict, mark it with red color. + + +'* * * * * Documentación en español ****** +'Public sub +'Winner() ---> Ha ganado el juego, finalizarlo y felicitar! +'updateBoxes()---> Volver a escribir los valores en cada casillero para que se compare con el ultimo valor ingresado y se coloree si ha conflicto + + +Public Game As Esquema + +Public Sub Form_Open() + Game = New Esquema + Me.Window.Center +End + +Public Sub TXT_Change() + Dim value As Integer + If Last.text = "" Then + value = 0 + Else + value = Val(Last.text) + End If + Game.Set(Last.tag, value) + updateBoxes() + If Game.isWin() Then Winner() +End + +Public Sub TXT_KeyPress() + If Not (Key.code = Key.BackSpace Or Key.code = Key.Delete Or Key.Code = Key.F10) Then + If InStr("12345678", Key.Text) = 0 Then Stop Event + End If +End + +Public Sub MnuReiniciar_Click() + Dim control As Object + Game = New Esquema + For Each control In Me.Children + If Object.Type(control) = "TextBox" Then + control.text = "" + control.foreground = &H000000& + control.background = &HFFFFFF& + control.enabled = True + End If + Next + Me.Title = ("Puzzle 1 to 8 - Locate the numbers!") +End + +Public Sub MnuSalir_Click() + Me.Close +End + +Public Sub Form_Close() + If Message.Question(("Are you sure?"), ("Yes"), ("No")) = 2 Then + Stop Event + End If +End + +Public Sub MnuAutor_Click() + FrmAbout.Showmodal() +End + +Public Sub MnuComoJugar_Click() + FrmAyuda.Show() +End + +Public Sub Winner() + Dim control As Object + For Each control In Me.Children + If Object.Type(control) = "TextBox" Then + control.background = &FFFF9F& + control.enabled = False + End If + Next + Me.Title = ("Congratulations! You did it!") +End + +Private Sub updateBoxes() + Dim control As Object + Dim value As Integer + For Each control In Me.Children + If Object.Type(control) = "TextBox" Then + If control.text = "" Then + value = 0 + Else + value = Val(control.text) + End If + If Game.Set(control.tag, value) Then + control.foreground = &HFF0000& + Else + control.foreground = &000000& + End If + End If + Next +End + diff --git a/app/examples/Games/Puzzle1To8/.src/FMain.form b/app/examples/Games/Puzzle1To8/.src/FMain.form new file mode 100644 index 00000000..98665517 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FMain.form @@ -0,0 +1,95 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,62,50) + Background = Color.LightForeground + Text = ("Puzzle 1 to 8 - Locate the numbers!") + Icon = Picture["logo.png"] + Resizable = False + { MnuJuego Menu + Text = ("Game") + { MnuReiniciar Menu + Text = ("Clear") + Picture = Picture["icon:/small/refresh"] + } + { MnuSalir Menu + Text = ("Quit") + Picture = Picture["icon:/small/quit"] + } + } + { Mnuayuda Menu + Text = ("Help") + { MnuComoJugar Menu + Text = ("How to play?") + Picture = Picture["icon:/small/help"] + } + { MnuAutor Menu + Text = ("About") & "..." + Picture = Picture["icon:/small/gambas"] + } + } + { TextBox1 TextBox TXT + Name = "TextBox1" + MoveScaled(25,5,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "1" + Alignment = Align.Center + MaxLength = 1 + } + { TextBox2 TextBox TXT + Name = "TextBox2" + MoveScaled(13,14,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "2" + Alignment = Align.Center + MaxLength = 1 + } + { TextBox3 TextBox TXT + Name = "TextBox3" + MoveScaled(25,14,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "3" + Alignment = Align.Center + MaxLength = 1 + } + { TextBox4 TextBox TXT + Name = "TextBox4" + MoveScaled(37,14,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "4" + Alignment = Align.Center + MaxLength = 1 + } + { TextBox5 TextBox TXT + Name = "TextBox5" + MoveScaled(13,23,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "5" + Alignment = Align.Center + MaxLength = 1 + } + { TextBox6 TextBox TXT + Name = "TextBox6" + MoveScaled(25,23,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "6" + Alignment = Align.Center + MaxLength = 1 + } + { TextBox7 TextBox TXT + Name = "TextBox7" + MoveScaled(37,23,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "7" + Alignment = Align.Center + MaxLength = 1 + } + { TextBox8 TextBox TXT + Name = "TextBox8" + MoveScaled(25,32,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "8" + Alignment = Align.Center + MaxLength = 1 + } +} diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAbout.class b/app/examples/Games/Puzzle1To8/.src/FrmAbout.class new file mode 100644 index 00000000..217d0bdd --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAbout.class @@ -0,0 +1,18 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + +Public Sub Form_Open() + Me.Window.Center +End + +Public Sub Button1_Click() + Me.Close +End diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAbout.form b/app/examples/Games/Puzzle1To8/.src/FrmAbout.form new file mode 100644 index 00000000..7898c4dd --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAbout.form @@ -0,0 +1,39 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,75,36) + Text = ("About") & "..." + Resizable = False + { TabStrip1 TabStrip + MoveScaled(1,1,52,34) + Arrangement = Arrange.Fill + Margin = True + Count = 2 + Index = 0 + Text = ("Author") + { TextLabel1 TextLabel + MoveScaled(2,2,41,21) + Text = ("Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com") + Alignment = Align.Justify + } + Index = 1 + Text = ("License") + { TextArea1 TextArea + MoveScaled(2,3,42,22) + Text = ("\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see .") + ReadOnly = True + Wrap = True + ScrollBar = Scroll.Vertical + } + Index = 0 + } + { Button1 Button + MoveScaled(54,31,20,4) + Text = Shortcut(("Close"), "C") + } + { PictureBox1 PictureBox + MoveScaled(54,1,20,20) + Picture = Picture["logo.png"] + Stretch = True + } +} diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAyuda.class b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.class new file mode 100644 index 00000000..42f43ffd --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.class @@ -0,0 +1,18 @@ +' Gambas class file + + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + +Public Sub Button1_Click() + Me.close +End + +Public Sub Form_Open() + Me.Window.center +End diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAyuda.form b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.form new file mode 100644 index 00000000..2b7d295c --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,66) + Background = Color.LightForeground + Text = ("How to play?") + Resizable = False + { TextLabel1 TextLabel + MoveScaled(2,2,63,11) + Text = ("Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:") + Alignment = Align.Justify + } + { PictureBox1 PictureBox + MoveScaled(32,15,32,21) + Picture = Picture["ejemplo1.png"] + Stretch = True + } + { TextLabel3 TextLabel + MoveScaled(2,15,28,21) + Text = ("Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed.") + Alignment = Align.Justify + } + { PictureBox2 PictureBox + MoveScaled(32,38,32,21) + Picture = Picture["ejemplo2.png"] + Stretch = True + } + { Button1 Button + MoveScaled(23,60,22,5) + Text = Shortcut(("Close"), "C") + } + { TextLabel2 TextLabel + MoveScaled(2,39,28,18) + Text = ("Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed.") + Alignment = Align.Justify + } +} diff --git a/app/examples/Games/Puzzle1To8/Licence b/app/examples/Games/Puzzle1To8/Licence new file mode 100644 index 00000000..b17f91b9 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/Licence @@ -0,0 +1,189 @@ +GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. +TERMS AND CONDITIONS +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. +1. Source Code. + +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. +2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + * a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + * b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + * c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + * d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + * a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + * b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + * c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + * d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + * e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. +7. Additional Terms. + +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + * a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + * b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + * c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + * d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + * e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + * f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. +8. Termination. + +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. +11. Patents. + +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. +13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/app/examples/Games/Puzzle1To8/ejemplo1.png b/app/examples/Games/Puzzle1To8/ejemplo1.png new file mode 100644 index 0000000000000000000000000000000000000000..fa41d8272840de5b877321438986b3efa602b9d2 GIT binary patch literal 5594 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 Uxya5^Ea2e44NVO2H*Y`qC#&<-5dZ)H literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/ejemplo2.png b/app/examples/Games/Puzzle1To8/ejemplo2.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd108c2eb12eeded962c8024fb618748d72a5a3 GIT binary patch literal 5481 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 Warw)WKiOF&z~qMIb?i0Ignt2a#J73? literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/logo.png b/app/examples/Games/Puzzle1To8/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bd7a6ecb124c71d724093c1265d18f04e6d6abda GIT binary patch literal 41279 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

    aoPw@^hX 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/RobotFindsKitten/.directory b/app/examples/Games/RobotFindsKitten/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/RobotFindsKitten/.icon.png b/app/examples/Games/RobotFindsKitten/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..22619f882e95662d476a4c1fc66c62e6b446aed6 GIT binary patch literal 4141 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/RobotFindsKitten/.lang/ca.po b/app/examples/Games/RobotFindsKitten/.lang/ca.po new file mode 100644 index 00000000..e1325016 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.lang/ca.po @@ -0,0 +1,40 @@ +# Catalan translation of RobotFindsKitten +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the RobotFindsKitten package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: RobotFindsKitten\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:58+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "gambrfk" +msgstr "-" + +#: Frfk.class:180 +msgid "You found kitten! Way to go, robot!" +msgstr "Heu trobat el gatet! Ben fet robot!" + +#: Frfk.class:203 +msgid "robotfindskitten" +msgstr "robottrobaelgatet" + +#: Frfk.class:211 +msgid "Esc" +msgstr "Esc" + +#: Frfk.class:231 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "En aquest joc, sou el robot (#). La vostra missió es trobar el gatet. Aquesta tasca es complica per l'existència de diverses coses que no son el gatet. El robot ha de tocar les coses per determinar si son el gatet o no. El joc acaba quan el robot troba el gatet. També podeu acabar el joc tot prement la tecla Esc.\n" + diff --git a/app/examples/Games/RobotFindsKitten/.lang/cs.po b/app/examples/Games/RobotFindsKitten/.lang/cs.po new file mode 100644 index 00000000..12cbca63 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.lang/cs.po @@ -0,0 +1,32 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "gambrfk" +msgstr "-" + +#: Frfk.class:180 +msgid "You found kitten! Way to go, robot!" +msgstr "NaÅ¡el jsi kočku! Jdi do cesty, robot!" + +#: Frfk.form:13 +msgid "robotfindskitten" +msgstr "-" + +#: Frfk.form:21 +msgid "Esc" +msgstr "-" + +#: Frfk.form:40 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "V této hře, jsi robot (#). Vaším úkolem je najít kotě. Tato úloha je komplikována existencí různých věcí, které nejsou kotě. Robot se musí dotknout položek k určení, jsou-li kotě nebo ne. Hra končí, když zjistí, kotě robot. Alternativně můžete hru ukončit a stisknouttlačítko Esc.\n" diff --git a/app/examples/Games/RobotFindsKitten/.lang/de.po b/app/examples/Games/RobotFindsKitten/.lang/de.po new file mode 100644 index 00000000..2acb6415 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.lang/de.po @@ -0,0 +1,32 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "gambrfk" +msgstr "-" + +#: Frfk.class:180 +msgid "You found kitten! Way to go, robot!" +msgstr "Du hast das Kätzchen gefunden. Gut gemacht, Roboter!" + +#: Frfk.form:13 +msgid "robotfindskitten" +msgstr "-" + +#: Frfk.form:21 +msgid "Esc" +msgstr "-" + +#: Frfk.form:40 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "In diesem Spiel bist du ein Roboter (#). Deine Aufgabe ist es, das Kätzchen zu finden. Das ist kompliziert, weil verschiedene Dinge existieren, die kein Kätzchen sind. Du musst die Dinge berühren, um zu bestimmen, ob sie das Kätzchen sind oder nicht. Das Spiel endet, wenn der Roboter das Kätzchen gefunden hat. Alternativ kannst du das Spiel mit der ESC-Taste beenden.\n" diff --git a/app/examples/Games/RobotFindsKitten/.lang/es.po b/app/examples/Games/RobotFindsKitten/.lang/es.po new file mode 100644 index 00000000..463341c0 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.lang/es.po @@ -0,0 +1,28 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: Frfk.class:220 +msgid "Esc" +msgstr "Esc" + +#: .project:1 +msgid "gambrfk" +msgstr "gambrfk" + +#: Frfk.class:211 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robotfindskitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "En este juego, usted es robot (#). Su trabajo es encontrar a Kitten. Esta tarea es complicada por la existencia de varias cosas que no son Kitten. Robot debe tocar las cosas para determinar si son Kitten o no. El juego finaliza cuando Robot encuentra a Kitten. Alternativamente usted puede terminar el juego presionando la tecla Esc.\n" + +#: Frfk.class:204 +msgid "robotfindskitten" +msgstr "robotfindskitten" + diff --git a/app/examples/Games/RobotFindsKitten/.lang/ru.po b/app/examples/Games/RobotFindsKitten/.lang/ru.po new file mode 100644 index 00000000..7a3bd023 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.lang/ru.po @@ -0,0 +1,50 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/RobotFindsKitten/.project:19 +msgid "RobotFindsKitten" +msgstr "Робот ищет котёнка" + +#: app/examples/Games/RobotFindsKitten/.project:20 +msgid "Your job is to find kitten" +msgstr "Ваша задача найти котёнка" + +#: app/examples/Games/RobotFindsKitten/.src/Frfk.class:187 +msgid "You found kitten! Way to go, robot!" +msgstr "Вы нашли котёнка! Молодец, робот!" + +#: app/examples/Games/RobotFindsKitten/.src/Frfk.form:7 +msgid "robotfindskitten" +msgstr "Робот ищет котёнка" + +#: app/examples/Games/RobotFindsKitten/.src/Frfk.form:14 +msgid "Esc" +msgstr "Esc" + +#: app/examples/Games/RobotFindsKitten/.src/Frfk.form:30 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "В этой игре вы робот (#). Ваша задача найти котёнка. Эта задача усложняется наличием различных вещей, которые не являются котятами. Робот должен коснуться предметов, чтобы определить, котёнок они или нет. Игра заканчивается, когда робот находит котёнка. Кроме того, вы можете закончить игру, нажав клавишу Esc.\n" + diff --git a/app/examples/Games/RobotFindsKitten/.project b/app/examples/Games/RobotFindsKitten/.project new file mode 100644 index 00000000..0e3dd113 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=RobotFindsKitten +Description="Your job is to find kitten" +Startup=Frfk +Icon=heart.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Games/RobotFindsKitten/RobotFindsKitten +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Games/RobotFindsKitten/.src/Frfk.class b/app/examples/Games/RobotFindsKitten/.src/Frfk.class new file mode 100644 index 00000000..fb90f90e --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.src/Frfk.class @@ -0,0 +1,193 @@ +' Gambas class file + +Public NKIs As Object[] +Public Robot As Object +Public OrigHeight As Integer +Public OrigWidth As Integer +Public OrigFontSize As Integer +Public KittenIsFound As Boolean +Public NumNKIs As Integer +Public KittenIndex As Integer + +Public Sub Timer1_Timer() + + Dim middle As Float + Dim kitten As Object + + Try TextLabel1.setfocus() + If kittenisfound Then + kitten = nkis[kittenindex] + middle = CFloat(kitten.x + robot.x) / 2 + robot.x = robot.x + Round(CFloat(middle - 8 - robot.x) / 2) + kitten.x = kitten.x + Round(CFloat(middle + 8 - kitten.x) / 2) + End If +End + +Public Sub Form_KeyPress() + + Dim i As Integer + + If Key.Code = Key.Escape Then Esc_Click + If kittenisfound Then Return + + Select Case Key.Code + Case Key.Up + For i = 0 To nkis.count - 1 + If Abs(robot.y - (nkis[i].y + nkis[i].height)) < 4 And Abs(robot.x - nkis[i].x) < 4 Then + Textlabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.y - robot.height >= Textlabel1.height Then + robot.y = robot.y - robot.height + Endif + Case Key.Down + For i = 0 To nkis.count - 1 + If Abs(robot.y + robot.height - nkis[i].y) < 4 And Abs(robot.x - nkis[i].x) < 4 Then + TextLabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.y + robot.height + robot.height < Me.clientheight Then + robot.y = robot.y + robot.height + Endif + Case Key.Left + For i = 0 To nkis.count - 1 + If Abs(robot.x - (nkis[i].x + nkis[i].width)) < 4 And Abs(robot.y - nkis[i].y) < 4 Then + TextLabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.x - robot.width >= 0 Then + robot.x = robot.x - robot.width + Endif + Case Key.Right + For i = 0 To nkis.count - 1 + If Abs(robot.x + robot.width - nkis[i].x) < 4 And Abs(robot.y - nkis[i].y) < 4 Then + TextLabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.x + robot.width + robot.width < Me.clientwidth Then + robot.x = robot.x + robot.width + Endif + End Select + +End + +Public Sub Esc_Click() + + Me.close + +End + + +Public Sub Form_Open() + + Dim tmp As Label + Dim i As Integer + Dim nkistring As String + Dim nkitext As String[] + NKIs = New Object[] + nkitext = New String[] + Dim sLanguage As String + + Randomize + origheight = Me.height + origwidth = Me.width + origfontsize = 12 + sLanguage = Left$(System.Language, 2) + Try nkistring = File.load("nkis_" & sLanguage & ".txt") + If Error Then + Try nkistring = File.load("nkis.txt") + Endif + + nkitext = Split(nkistring, "\n") + KittenisFound = False + numnkis = 20 + + For i = 1 To NumNKIs + tmp = New Label(Me) + tmp.x = Int(Rnd(0, Me.width / 16)) * 16 + tmp.y = Int(Rnd(5, Me.height / 16)) * 16 + tmp.AutoResize = False + tmp.width = 16 + tmp.height = 16 + tmp.font.size = 12 + tmp.Foreground = Int(Rnd(32, 255)) * 65536 + Int(Rnd(32, 255)) * 256 + Int(Rnd(32, 255)) + tmp.alignment = Align.center + tmp.text = "#" + Do While tmp.text = "#" + tmp.text = Chr(Rnd(33, 126)) + Loop + tmp.tag = nkitext[Int(Rnd(0, nkitext.count))] + NKIs.add(tmp) + Next + nkis[nkis.count - 1].tag = "kitten" + robot = New Label(Me) + robot.x = 192 + robot.y = 192 + robot.AutoResize = False + robot.height = 16 + robot.width = 16 + robot.font.size = 12 + robot.Background = Color.white + robot.Foreground = Color.black + robot.alignment = Align.center + robot.text = "#" + + +End + +Public Sub Form_Resize() + + Dim scalex As Float + Dim scaley As Float + Dim i As Integer + + 'STOP + scalex = CFloat(Me.width) / origwidth + scaley = CFloat(Me.height) / origheight + If nkis.count < NumNKIs Then Return + For i = 0 To nkis.count - 1 + NKIs[i].x = NKIs[i].x * scalex + NKIs[i].y = NKIs[i].y * scaley + NKIs[i].width = Round(NKIs[i].width * scalex) + NKIs[i].height = Round(NKIs[i].height * scaley) + If nkis[i].y < TextLabel1.height Then nkis[i].y = Round(TextLabel1.height / nkis[i].height) * nkis[i].height + NKIs[i].font.size = Round(NKIs[i].font.size * scaley) + Next + robot.x = robot.x * scalex + robot.y = robot.y * scaley + robot.width = Round(robot.width * scalex) + robot.height = Round(robot.height * scaley) + robot.font.size = Round(robot.font.size * scaley) + origwidth = Me.width + origheight = Me.height + +End + +Sub FoundKitten(nki As Integer) + + Dim i As Integer + For i = 0 To nkis.count - 1 + If i <> nki Then + nkis[i].visible = False + nkis[i].x = Me.width + 100 + nkis[i].y = Me.height + 100 + Endif + Next + heart.visible = True + KittenIsFound = True + kittenindex = nki + TextLabel1.text = ("You found kitten! Way to go, robot!") + nkis[nki].y = heart.y + heart.height + robot.y = heart.y + heart.height + robot.x = heart.x - robot.width + nkis[nki].x = heart.x + heart.width + +End diff --git a/app/examples/Games/RobotFindsKitten/.src/Frfk.form b/app/examples/Games/RobotFindsKitten/.src/Frfk.form new file mode 100644 index 00000000..8246f54f --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.src/Frfk.form @@ -0,0 +1,32 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(25.1429,22.5714,110,79) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("robotfindskitten") + Resizable = False + { Esc Button + MoveScaled(102,1,7,5) + Visible = False + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("Esc") + Border = False + Cancel = True + } + { Timer1 #Timer + Enabled = True + Delay = 200 + } + { Heart PictureBox + MoveScaled(40,20,29.1429,24) + Visible = False + Foreground = &HFFFFFF& + Picture = Picture["heart.png"] + } + { TextLabel1 TextLabel + MoveScaled(1,1,100,10) + Text = ("In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n") + } +} diff --git a/app/examples/Games/RobotFindsKitten/COPYING b/app/examples/Games/RobotFindsKitten/COPYING new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/app/examples/Games/RobotFindsKitten/heart.png b/app/examples/Games/RobotFindsKitten/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..80a25fecc65eb6b55acfd0f06dbe059898e11113 GIT binary patch literal 9149 zcmV;uBSPGXP)I!V&LF*Kuna&K{-T$^Qdsdqp}>&~9pbSl8aX8FLot<=fy#Q2nbz#KgBz-J=3 z;fc03T6=oN1K*aKE2Z<8i#ENv~lL>FUChNC1%Z`OuU)^mE+rry~%+ z)HFOwp<5Q~yPA)Wqej%_ zwYBQh)>e$(UM#N{J(+|dzPIvt0I+l&rD?nqiEw{?{iGu9QIUu`+v{a|M+e^C-agN@ zQvd2e958?Y;01aRVwR?kcLSNaSj)JE1$g>;AH!frMFneGTIh_&M@8NvO4Q}GwQ50k zH^F!uUq3<~7U(*0ua~w!fF_@h-AeIFL&Jn5Ze=j2F7tYcw72(po(0%eX%Hv}Du5aw zgb>gVRHe@AMYO5`B!E_+1&F0yy{Yr;>-NhIU1v*WCI4@P6~u@T^`hEZwICK7uwic@ zg0=71O#NRU&{Z}ziym!w@#S*eyM5`??D93OI< zC_&&zpcX;#Qn9|FQaC9+&;DFK8KEs#jPY~i;cgl$a;%S6Z;PrW)wleRDWD(?4lu(Xud zl^R!$*bSkO3Uqfn{w|rir!X)BI1#8#{cIHb27`o&*t0zdY2O8QA@;5Po#uUBpN|KF zL3XyZ44Zt1MAV8vK>drR5$NeDcuo>1L5%oHM8Go;0Z%8bMKhn$+$Bn-&VjV{>;xJS z0yiVeR1eUV`nM>fL)W<{6dF?v>o*MbZJ!S(lB$&aN`VEyEZ~@w3 zI0m+rmr49gbcI4}2!+PP>AIk{R-Mq^o<~yq8}X%Nb_!y521fe>7C;hN!S(_>fp-w) zkQoL8K-bw(Svf3`m_ZTs>hf|`)yGm_hG@=tz+uQz?;QmJ+A+f8j0}4D8&cA9*wHn*48|S$wX|>8pJ_5Atg`GfZt(skU&gzwjD8#Kp zOx_Hbs5g1NN>3&W{(c@X2RIrLcp*(T8t6rA+ICGE#WXfX>A?RkNg9} zP&hbC3B*P}B_+qIF_vGkh$GI{>xi6_Z9(IJYSh;r3y41ZNg> z7BM<2&~T#xA?jO*-(cU@q2HI6^V30+wJ4%`bX|Qn7$n%$mG?Uz5+eB+FcZsd85>Wt=Qa!n=^3C$gac=a2#f)m6fa>r02S-s!AQ%)I_eF6G+(ZjF!c!u8kVU9kdhb+t1|0m5O-^UfoF(M1sM^NPa? zJC^%VgfI~t^4pfhu?-D;v!Y_a;gOG4RjFCA7*5I{1e{IUrk_kC1(EF$h|N70A!()* zW;8eR7|&n zXOFT-(>C_S!11Khw3(=GY2gQ&rm91sqJ+D+vQnMj(UEn`4xd$|0~6B-{(>_eLDJQK7(FcJD?lV>g(ayV@KTk?@JiA zcAcMb-iRgwoEnCfw7QZK~y5C;S+F9xz3NpSY?{biN&&xn@H$x31aVt zaABhq(W+6hAO5nA4(3Ipj%>{$YK24Iy#)D^N{amy@i?CPdOUS?MSpOsrw41}MtJ=3 zv6P=9G{g)z4T-nfEgOemn)rHq^K5zpA?X=R&N%5%pdt3^67uD;qP^WQs&a^^r{x&4 z79woB7iU0%e&!jpH{XOJ8v2xJlKk7>Fn{}7*tTt~W#|YE3CAr!QpL9I=>!Wg6BZ&B zP%fO|Lu~eWWJgx?WRf!?`DIOqh`K1J?92{KAITu{&uwjR+ij>vA0^q+(sx_*E3e?Y z^G>`QHoy?4e2g>>N3?38+?icCd@Q0ZqGSDm$2lsHa?l9b5B_|Im0bt%^s61(a%UiM z#Q{Sp*tQLI=ba=UdI+nr5oMb4>h6Z-W+C%E_n_T)BRu$^92@7bNf@O-uMlF(FF-gYR()9 zT^&3Kn>WLjE#uC4>JK_n?eremjdixEw2XX@)za3XE46h}JAcs;vw}*ky=zI2xCv1A&H^0LKBF zGL8r1aVmpB73=QSAVpx015v9O<_tt)~gvr*P|WeALmZdP*Dk-g?Ow7kQnJvxUtf~!h`ri zQKW^%cHU3S@SBWt%E~Yr8?(GF3sJ{KYt*tbm^&9LD#Yg8yB7u;@Sk8PLx_18@<309 z%KWiJB@(WBpLFwOW^b8Z2U)>tJH(<>1W|PzrcEPx-F2uR{RnIIYRpZWP`BR>M;{H^ zx^vmaRVnY^sLLrwQ;4n5?=of>s5x`!{MpY4E?tUNQj&gyfBZ-I z;~!zqo>A+8axt-?BP5-J^jg^tX(FZUjB0PT`EPec_NNQg2`9h}H&C){8CuRYNG2f= za7pT7K=303&0L{sQp>+SH^-6Ekd#)R%_CkfiL=ip{)tcIlN4b0ZfI;A>-%;w!4O2k zqJGkuD!WBr*K*REIx*RlCx$N%syGcAeN>{F5iWV0m66~=bnTZ5ed3iyNs)a&m9Ub7GioKRDhKe|{n%mnA0aUfOW4xVH2WmT01#9z5 zO9>o2C~;B;Xodmp?UO#=?_y-2khZ7HrN5!9481%@^V#$9?F}9BkW9kXt*AnC`x_f& z+3#bnzY7~t>t z@gZ~X3+`L|!3RQChvH{F7{oJkrcCp5;bMU3LI|3^YD2mL3l{3V{Wi(nyRmZYTg@==wYHAx8tx@8!wvz;k`oetGd zZO9glj!+7kn$RD30F^Q8Uv+n51p<@4xzEMOpn>erlbe|6+k&rDUUAPzk6ltQ`#7w3(9?1&$*XMZZ9%LXuzl-?l)v#`zXjn~yl9Dmo z*~G=fMFV1=9)hm3GnL`YV$+j3PLbUj@Gki~S!5xRfFJ%4o_`)p6RWIDhETY0F#yz) zz3e9qgEgsb>9dHsyR}v8$S5e$5r*#p`fb(Qw+~jYM(x;v>G8OQ(=G;#Ld!ll4Ec!X z5X=L72<%5PRRs}`!^Vwh>(*gd7R+!1+{-a zJoFH~ZEYlH%y6};i=v45$}H=GRxAyD=; z5Rx{~2N~&OwC8^}Hfp)0e-^NvBAhB0QyVj8z!g`(FMo+zz8tk<2fXu+%{zG?@EZLj09;I6ghH7A{LgUJRdDRF^wig5J^pyXp?Uj(I?^Gj z_E3nYQbGPq^hwm`bHYm+5=(s-QPr+yb&-Wo2>$7xP}f`ohaV1r@2RKoy!^7`oQDLm zZBG|zB5o_t6xt_IH^*XHYfc0}M-l*g>7)AJ#neL}fcoSov97rW!>(<9_gy^e)=BG9 z4(v|n-G~!qtI$Usv?&xZe*OW69aDOc1b|y{>H?7R&_#>TF1ZBlm}3Ch~C7mAk-RBrL?EtnRE3mEdn#;=gMIW}QjRBQ6{bx^)*5fc2^#Je&1*ZSGn0)Yf z;Npwnf(y_x<~MZj*@L?KZYfA|KqHcc%3OWl3KX~F?tts?9(3p~O#<&A=~Wk#9DYCM zNhi^L@x|z+rRfPfZEbiTei-eA7aR+c;=qf@(r+W-^YKhSwyBK)6ZPHJR;@l~3W|oz z3wjmt=G=_cB!yw1mMp<@_0{+nE0{D*iPD<@o~BRSj6xQT#Ci3ok^^m{hK9-i-d6-@x|#HPu3R@eZ=` z>HwO?#%OdvZ#^iY)^&DjZ#j5s2D1FS_Ua@=D1`adui`oLO!N%9((3JnhaZNQUUDpy z??jZ>F48g6hDd~UMK1X^2Hh+2P+gtYWV9}CyB@7 zBwv0RZoeJs>Ky;{Jw%&kI#54tZXWPV(jhf9-P+ly<<9@N?bX*AKsSJk@e7ZK#93!y zUVeGH*aR>;I!LZxkMAcxanz(1^8If@8ux6}4|2-mkh||*E-%kZ1SFACz}Il?)d>x+ z7nUx?bHx?tnekG9S6_v@?t&+ubo|qL;1#6a)Mm%NXq2A~LYu-c5OrN$o%UfkoHYno zN67j%LRh!OZ-QdcB6@%F6SUJ$N6+Y?OEff~|N7S$FTN;2{v7lozDjOQ|9x$33>L%E z&wNiTrga5_S;stxA-w^ySKX}D1cG6}qD815{}|s%C!yIMzEaTLjpx=|(H?oE;AqiZ zNC{sjDT3)bk3^$%ClZ66mp^Qc_iG)tl=P-P>|c-+s~caPAecWNwQ3dC$tR;_v^iVN z&BWKNLI3^lp*MeTM}<^G{gv!OrTTD~H)63NZR#*=MBUodlvlTEfB1b!$f|vK#pz-s zuy7$DEN3LMvnL`Z!9`*6`kS@UgGGKuxUYIinZn_E2`RB`gTN|bcn>Jxwdo6l_pgzd= z`gf#!&kpQA=1^@nSd7SUsE2|<$J*6)#JTza*SQ)u2nJD0mSA0dHRh$4W@VjJJdXLo z3uxC}2Rn8+YEldNK3^ew1HFCGDBFffo5BbYbz5_@w$;G|@FAM@Rir?{CF?lAvSpap zUyr){ay%Kqc$G||z48kBU3bCDFFXEe94SJ20*RL!0J=^?c{%qC(Rs>+rqsy!;#X8w zs>5^kp;Cx;JsLO@s2Rj{T?`Y>I0J6I6&5ds9CIzPbt|6lei!|P7oa`g4YrW<=&MM) zR3TdL&6*k>9$+577>wv#$a`Wji3DUM12n{;+Kptc1|I@;F(~Ldgu|$1%ShaE3!Hjt zHc2fD-h3194L6`a^Nb@&K|K6dk%W#-(ymZwL`VuFtWj-zv!X&(x3*?AdYP%4I}=z0 zl#;!zpo_4p5{W<4wrwXn$}2ThlNt zYvK|W;D%sO1sp2B1HdW3LcrZPeS~rJ(WtAgLR+~KrcKM*R}4Ts`z-42yU`zete|(_ zfn=RtBwZ2J8wl`7Fv!*-dY=Z1Q5y-pHYdeuqZ_GL+=3gn8c|G}M)KNgQD6BA9Clb1 zNtJ2Bh7D-9-6s3n79eReqD9{%W5S89vu1=z3Ztk|0Zyx~R_Eq)C#uw|3OEBefl+*V zrUJZP%-OSv-+C*)Pks_l?lf`BBKh{)7#CkG!Th9iN~Rmwh{Q_m-Jdm0r+45to)#|l3z78j*4qB>O=)NrK(DOy16+|FbQNci%%foEB_#_?IML~ z(opiFrHJnCz5($a zwjfz250LQtd9JLCmvUz8k2J=Nr~v;K4y$m1INfwav(6o}Pd5P(48qKr@a=D7u3VXK zk1B=P*@@?_yWqe6OUCaMSnk_^XOUojH|gbmZDl2Y9i`XOD`dH#1^R{ z6HQ}JG%9;Li~+`+r~oT!Yt{T%EYD6d5wf0+I9RUDS^#tB!ljqO7rzKc9+|(VlVxFT z*nqn4K0JT=Q^Cr76X|vOD-traGfoM=pPk__zaK@LG#BH7r~p@&m#ZoV0b0OJU#H<=pNY#O<{@b5~E3zEV( zBr3qA)z#{lrlvfP)B=LQd|)wBA~#54~6(~d;7Q&?YJZ=q{v!e6D^ypCnF}olz^s*J$mDf zXe(FZsi+`#XGN4ktyv>6($`*t&d!3Dt3#IizagP09l+yZOJya07=@JRfNy`i5)}%^ zSuN!0KM{!tM4057H5i0rj*)Ms;|0g%=TQN(6l#3a$Ix(?s?28r){2k*3LSx%GL*@@ZEfam6$ z(bum>+q<`5rYVVZiEcw2r$e5-$HT5@l-1*Osn5lPAu7Ov%1ZT_t}aK-Y64*-&bk1o zW`fqW8is_QR;++)ua(@reV@rBiM@MCKK(SI>#l?DZpYC&nb?fPM-PxO@fmoss*0CK zxv*p?m{3HuaY;>$n(I(dkwliXV}Oq$Cc%VCyBY>+-aPo~SK-17(WXzA<4R$5cVoWz zBI>ul#kY2?0g-92GQ3KNv50RO9|MjhwiWGRHKvk=XC zJQFCBwQwOl-}w&4dFRQ{TupCL0gddUfU6}+GvbKcvLCgZEc;9B!vl1RDgNWsQPSgFCmAe&_b5HIY`>| z2qg77uD&f@ml)|4S72UwB_6+Dmh}xA;DHC=(MJmwlGsQhPST%|*`%5EqfLh18Dj1D+xs8}%UVA{#0ySU*9$`u;F!hzhApR!2u(Zrd(u2_UK0Q;=}hm`4TX z%)z?zPWbr8;l&rxo_rGRi6@|;K|K0Cz8i(4LEl0`Pj;&a9uFTzBK)ScmEL4>QjioT z8Bqa_DKA%-batW_=mRo=V-d|d1L=Gn{k{qfYHHw?TWFs@pOXLiA1TM{M}yk1xAfeM z%zx@7J%ap|sw$oshq?M8i)^wH)yCJOQ8lfl1*iKn5RG~SQsX+R6Iu)dqETrVIpPS^ z^UtGobQG+*&qWIo7u|x`r6!>1Iz4_rH+OYSI)d7mv_yr}%&V3b$KRQVCap%YSw|ub zg(Ln=8;0~V83xqXi*_yIJ4hh5=u5y3Bm`%F)*aQ=+&@Wd(L$KiL0g+vEx8T&**66vjf6|qG#t4seFjq>+l9+o?vF=dDf@cG(WwWy(?pf)p8 zuOkq1;Ao_Nz$o?&h7mnT(EfSSTOVo24ANW2aXH>dVM-Cz#&=6gRcRm9?-t^0%|((0 zvl+cmmWgQ5w~*ldLF7Tvbe)}*l@q4gX}B>Zi3)IbO^y0kdpk}uTkSem9dTMtMx3p~ z5c_qcn9_^v(eyep$vKIbAMaIFaqk4GbQ%dvS)u|=FDX%90mt;K4cmU5h7`EWMTTMw zH(C*I{tYB}p9JE;AP;)I6FNsfT$s{C&BUszDmAO6#j)tamQVoEtn(1<8fJJLu6K~( zxbKi2W*mz|xMLFJ=!Xj~Q8Q4Cvt_qz&qjt0&tY&g1{6{}vK{g2>k!RtdA+PDD;x6O zp_2_RQ62F4+FG@!zP`_MJcz?}5>h`f5Zg6@WSIVrWSCk2-Q%I5tZdk}WSnepiCO?< ze!sf7w3NdORAhl@SPxR9JqNL0`zv;7N7|MDj`T9cf!<({&1GdgJ_&R5!v&Y9{oqrz zwQ6BU2f+dkm))-wL5f`#B952W;oLaV8nPA9q8=Ew-IpgjT%s0<)2gf0$?-VR_IAfY z7lm{bR3RfW?fF<1LeM5;N0F^SLes!7*il~2gHtpvI#9So9U!hQFIUxVZH{EM5bYWS zK8=_Hbx4kW3*tQW`2FlHE919QJT5v=xI`T!R@T(0W7^yCJ0uJez&QwEw;)wd7O+1W zXvpbqMeqhWeJ@@0c(Md%b9h1kHfv@$gebr>)&8dc|+ zCgp7zTM+9ymacO!9OlVHg7+uD*qreUm#D*t&(_wekH%tXrb*oIXG2*TFS=TESTN@O zZIcOW>*}<|)Rs991%ob0hY_wu9cCOAiKy0ie8P>)8SnW2<><~hMwpmf00000NkvXX Hu0mjf?sI`2 literal 0 HcmV?d00001 diff --git a/app/examples/Games/RobotFindsKitten/nkis.txt b/app/examples/Games/RobotFindsKitten/nkis.txt new file mode 100644 index 00000000..743506b4 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/nkis.txt @@ -0,0 +1,622 @@ +"201 Kitten Verbs, Fully Conjugated". You look for "find". +"50 Years Among the Non-Kitten Items", by Ann Droyd. +99 bottles of beer are on a wall here. +A 100 meter long chain of jumbo paper clips. +A 256 kilobyte write-only memory chip. +A 3-inch floppy disk. +A 3-sided Monty Python record. +A 540Hz tuning fork. +A baboon with a bassoon hoots angrily at you. +A badly dented high-hat cymbal lies on its side here. +A bag of groceries taken off the shelf before the expiration date. +A ball of pocket fluff. +A ball of yarn. +A big bass drum bearing a hole and suspicious clawmarks. +A big chunk of frozen chocolate pudding. +A bitchin' homemade tesla coil. +A blank deposit slip. +A bobolink is twittering a happy tune here. +A book: Feng Shui, Zen: the art of randomly arranging items that are not kitten. +A book with "Don't Panic" in large friendly letters across the cover. +A bottle of distilled water. +A bottle of hair tonic. +A bottle of oil! Refreshing! +A bottle of smelling salts. +A bowling ball with the name "Bob" inscribed on it. +A bowl of cherries. +A box of brand-new nixie tubes. +A box of dancing mechanical pencils. They dance! They sing! +A box of fumigation pellets. +A brain cell. Oddly enough, it seems to be functioning. +A breadbox. Nope, Kitten isn't in the breadbox. +A briefcase filled with spy stuff. +A broken metronome sits here, it's needle off to one side. +A Buttertonsils bar. +A caboodle. +A canister of pressurized whipped cream, sans whipped cream. +A can of Spam Lite. +A card shark sits here, practicing his Faro shuffle. He ignores you. +A card sharp sits here, practicing his Faro shuffle. He ignores you. +A chain hanging from two posts reminds you of the Gateway Arch. +A cluster of cattails are growing here. +A coat hanger hovers in thin air. Odd. +A cockatoo shrieks at you. +A compendium of haiku about metals. +A copy of DeCSS. They're a dime a dozen these days. +A copy of the Weekly World News. Watch out for the chambered nautilus! +A coupon for one free steak-fish at your local family diner. +A crouton. +A crowd of people, and at the center, a popular misconception. +A crystal ball. It doesn't seem to know where Kitten is. +A cyclops glowers angrily at you. +A dangly thing mangled by Kitten. +A dark-emitting diode. +"Address Allocation for Private Internets" by Yakov Rekhter et al. +A demonic voice proclaims "There is no kitten, only Zuul". You flee. +A digital clock. It's stuck at 2:17 PM. +A discarded bagpipe chanter reed. +A discarded pop bottle. +A discarded refrigerator box. Nope, Kitten isn't in the box. +A discredited cosmology, relic of a bygone era. +A dodecahedron bars your way. +A dumpster full of hair. +A family of integrals are here integrating. +A flamboyant feather boa. Now you can dress up like Carol Channing! +A flyer advertising a sale at Spatula City. +A flyer reads, "Please donate hydraulic fluid" +A forgotten telephone switchboard. +A forgotten telephone switchboard operator. +A freshly-baked pumpkin pie. +A frosted pink party-cake, half eaten. +A "Get Out of Jail Free" card. +A geyser sprays water high into the air. +A glorious fan of peacock feathers. +A gravestone stands here. "Izchak Miller, ascended." +A green yo-yo. +A grin. +A haircut and a real job. Now you know where to get one! +A hairless rat. +A half-eaten cheese sandwich. +A hammock stretched between a tree and a volleyball pole. +A hedgehog. It looks like it knows something important. +A helicopter has crashed here. +A herd of wild coffee mugs slumber here. +A herd of wild coffee mugs slumbers here. +A historical marker showing the actual location of /dev/null. +A hollow voice says "Fool." +A hollow voice says "Plugh". +Ah, the skirl of the pipes and the rustle of the silicon... +Ah, the uniform of a Revolutionary-era minuteman. +Air. +Air Guitar!!! NA na NA na!! +A jar of dehydrated water. +A jar of library paste. +A jar of Vegemite is playing hopscotch here. +A ketchup bottle (nearly empty). +A kitten sink, for washing kitten (if only kitten liked water). +A kitten source (to match the kitten sink). +A knight who says "Either I am an insane knave, or you will find kitten." +A large blue eye floats in midair. +A large pile of rubber bands. +A large snake bars your way. +A largish bath towel. +A leather pouch filled with multisided dice. +Alien underwear. +A little glass tub of Carmex. ($.89) Too bad you have no lips. +A livery stable! Get your livery! +A lone, forgotten comma, sits here, sobbing. +A lotus. You make an interesting pair. +A magical... magic thing. +A marijuana brownie. +A mason jar lies here open. It's label reads: "do not open!". +A mathematician calculates the halting probability of a Turing machine. +A meerkat... not even close. +A Mentos wrapper. +A mere collection of pixels. +A milk carton, with a black and white picture of kitten on the side. +A mouse. +An 80286 machine. +An abandoned used-car lot. +An AK-47 rifle. +An AK-97 rifle. +An animate blob of acid. Being metallic, you keep well away. +An aromatherapy candle burns with healing light. +An atomic vector plotter. +An autographed copy of "Primary Colors", by Anonymous. +An automated robot-doubter. It doesn't believe in you. +An automated robot-hater. It frowns disapprovingly at you. +An automated robot-liker. It smiles at you. +An eminently forgettable zahir. +An empty Altoids tin. +An empty coaxial cable spool. +An empty Penguin Mints tin. +An empty shopping bag. Paper or plastic? +An Enfield Mk3 rifle. +A neural net -- maybe it's trying to recognize kitten. +An expired transistor. +An FN-FAL rifle. +An ice cube. +An incredibly expensive "Mad About You" collector plate. +An M16 rifle. +An M1911A1 pistol. +An M9 pistol. +An old bootable business card, unfortunately cracked down the middle. +An old pattern is here going on and on. +An old rusty revolver. +A non-descript box of crackers. +Another rabbit? That's three today! +An oven mitt with kittens on it. +An overflowing bit bucket. +An overturned bottle of ink and lots of kitten pawprints. +A number of short theatrical productions are indexed 1, 2, 3, ... n. +An unlicensed nuclear accelerator. +Any ordinary robot could see from a mile away that this wasn't kitten. +A packet of catnip. +A packet of pipe cleaners. +A pair of combat boots. +A pair of saloon-style doors swing slowly back and forth here. +A paper shopping bag. Nope, Kitten isn't in the bag. +A parrot, kipping on its back. +A patch from the Mammoth Caves. +A patch of grape jelly grows here. +A patch of mushrooms grows here. +A pile of coaxial plumbing lies here. +A pile of coconuts. +A pink plastic watering can. +A piping-hot pizza. Useless. +A pizza, melting in the sun. +A plastic model of Kitten. +A plush Chewbacca. +Apparently, it's Edmund Burke. +Approaching. One car. J. Followed by. Two car. M, M. In five. Minutes. +A puddle of chocolate sauce. +A puddle of mud, where the mudskippers play. +A punch bowl, filled with punch and lemon slices. +A radio hisses away. Kitten must have been here. +A rancid corn dog. +A Remington 870 shotgun. +A robot comedian. You feel amused. +A rusty melon-baller. +A rusty slinky. It was such a wonderful toy! +A sack of doorknobs. +A sack of hammers. +A sack of wet mice. +A salmon hatchery? Look again. It's merely a single salmon. +A Sanrio catalog. +A scrap of parchment bears the single word, "meow". +A scratching-post. +A screwdriver. +A section of glowing phosphor cells sings a song of radiation to you. +A set of keys to a 2001 Rolls Royce. Worthless. +A shameless plug for Crummy: http://www.crummy.com/ +A shameless plug for Frotz: http://www.cs.csubak.edu/@@126dgriffi/proj/frotz/ +A shameless plug for the UCLA Linux Users Group: http://linux.ucla.edu/ +Ash is mumbling "KLAATU BARATA NI" here. +A signpost saying "TO KITTEN". It points in no particular direction. +A sign reads "Don't step on the Mome Raths". +A sign reads: "Go home!" +A sign reads: "No robots allowed!" +A singing frog. Useless. +A slightly-used smellovision set. +A smoking branding iron shaped like a 24-pin connector. +A spindle, and a grindle, and a bucka-wacka-woom! +A stack of 7 inch floppies wobbles precariously. +A statue of a girl holding a goose like the one in Gottingen, Germany. +A steam-powered bunnytron. +A stegosaurus, escaped from the stegosaurusfindsrobot game. It finds you. +A sub-atomic particle languishes here all alone. +A Swiss-Army knife. All of its appendages are out. (toothpick lost) +A team of arctic explorers is camped here. +A technical university in Australia. +A tetradrachm dated "42 B.C." +A Texas Instruments of Destruction calculator. +A tiny ceramic Kitten. It's probably not the Kitten you're looking for. +A toenail? What good is a toenail? +A toilet bowl occupies this space. +A ton of feathers. +A traffic signal. It appears to have been recently vandalized. +A train of thought chugs through here. +A trash compactor, compacting away. +A travel-sized cyclotron. +A tree with some jelly nailed to it. +A tribe of cannibals lives here. They eat Malt-O-Meal for breakfast, you know. +A troll. Ewww!!! +A tube of toothpaste. Too bad you have no teeth. +A tube of white lithium grease. Perfect for your robotic joints. +A tuft of kitten fur, but no kitten. +A vase full of artificial flowers is stuck to the floor here. +A vase of roses. +A voice booms out "Onward, kitten soldiers..." +A willing, ripe tomato bemoans your inability to digest fruit. +A wireframe model of a hot dog rotates in space here. +A wondrous and intricate golden amulet. Too bad you have no neck. +A zorkmid coin. +Baling wire and chewing gum. +Bibbidy bibbidy bibbidy bibbidy bibbidy bibbidy... +Big Bird is here looking for Mr. Looper. +Billions and billions of things that aren't Kitten. +Biscuits. +"Blup, blup, blup", says the mud pot. +Bright copper kettles. +BURRRRP!!!! Flavorful and full of protein! +Butane!!! +Carbonated Water, High Fructose Corn Syrup, Color, Phosphoric Acid, Flavors, Caffeine. +Carlos Tarango stands here, doing his best impression of Pat Smear. +Catsup and Mustard all over the place! It's the Human Hamburger! +Chewing gum and baling wire. +Clang, clang, clang goes the tranny! +Clifford Stoll is here selling Klein bottles. +Conan O'Brian, sans jawbone. +Could it be... a big ugly bowling trophy? +Daily hunger conditioner from Australasia +Dancing cold water pipes. Mikey must have been here. +Darth Vader is here looking for his Teddywookie. +"Dear robot, you may have already won our 10 MILLION DOLLAR prize..." +Definitely not Kitten. +Dinsdale! +Diogenes is here demanding whisky. +Dirty socks. +"Dogbert's tech support, how may I abuse you?" +Doodles Weaver is here looking over a horse race schedule. +Ed McMahon stands here, lost in thought. Seeing you, he bellows, "YES SIR!" +Ed Witten sits here, pondering string theory. +Empty jewelboxes litter the landscape. +Faboo! +Fonzie sits here, mumbling incoherently about a shark and a pair of waterskis. +For a moment, you feel something in your hands, but it disappears! +Free Dmitry Sklyarov! +Free Jon Johansen! +Gibble, Gobble, we ACCEPT YOU ... +"Go back to Libraria!", says Pat Schroeder. +Grind 'em up, spit 'em out, they're twigs. +Heart of Darkness brand pistachio nuts. +Heeeeeeeeeeeeres Johnny! +Hello, nurse! +Hello, sailor! +Here is a book about Robert Kennedy. +Here is no kitten but only rock, rock and no kitten and the sandy road. +Here's Cal Worthington and his dog "Spot"! +Here's Pete Peterson. His batteries seem to have long gone dead. +Hey, I bet you thought this was kitten. +Hey, look, it's war. What is it good for? Absolutely nothing. Say it again. +Hey, robot, leave those lists alone. +"Hi, I'm Anson Williams, TV's 'Potsy'." +Ho hum. Another synthetic a posteriori. +"How in heck can I wash my neck if it ain't gonna rain no more?" asks Farmer Al. +Hydraulic fluid and jagged metal bits. You recoil from the scene of carnage. +I don't know what that is, but it's not kitten. +If it's not one thing, it's another. +If it's one thing, it's not another. +"I pity the fool who mistakes me for kitten!", sez Mr. T. +Is that an elephant's head or a winged sandal? +It is a cloud shaped like an ox. +It is a marzipan dreadnought that appears to have melted and stuck. +It is an ancient mariner, and he stoppeth one of three. +It is a set of wind-up chatter teeth. +It is -- I just feel something wonderful is about to happen. +It pleases you to be kind to what appears to be kitten -- but it's not! +It's 1000 secrets the government doesn't want you to know! +It's a banana! Oh, joy! +It's a big block of ice. Something seems to be frozen inside it. +It's a big smoking fish. +It's a black hole. Don't fall in! +It's a blatant plug for Ogg Vorbis, http://www.vorbis.com/ +It's a blind man. When you touch, he exclaims "It's a kitten prospecting robot!" +It's a blob of white goo. +It's a bottle of nail polish remover. +It's a bug. +It's a burrito stand flyer. "Taqueria El Ranchito". +It's a business plan for a new startup, kitten.net. +It's a cardboard box full of 8-tracks. +It's a Cat 5 cable. +It's a catalog from some company called Infocom. +It's a charcoal briquette, smoking away. +It's a clue! +It's a cookie shaped like a kitten. +It's a copy of Knuth with the chapter on kitten-search algorithms torn out. +It's a copy of the robotfindskitten EULA. +It's a copy of "The Rubaiyat of Spike Schudy". +It's a copy of "Zen and The Art of Robot Maintenance". +It's a dark, amphorous blob of matter. +It's a DVD of "Crouching Monkey, Hidden Kitten", region encoded for the moon. +It's a Dvorak keyboard. +It's a fly on the wall. Hi, fly! +It's a free beer cozy (with your paid subscription.) +It's a free Dmitry Sklyarov! +It's a free Jon Johansen! +It's a funky beat! +It's a giant slorr! +It's a gun of some sort. +It's a hologram of a crashed helicopter. +It's a "HOME ALONE 2: Lost in New York" novelty cup. +It's a hundred-dollar bill. +It's a Java applet. +It's a limbo bar! How low can you go? +It's a Linux install CD. +IT'S ALIVE! AH HA HA HA HA! +It's all a dream. +It's a lost wallet. It's owner didn't have pets, so you discard it. +It's a mighty zombie talking about some love and prosperity. +It's a moment of silence. +It's a mousetrap, baited with soap. +It's an altar to the horse god. +It's an autographed copy of "Secondary Colors," by Bob Ross. +It's an autographed copy of "Secondary Colors", by Bob Ross. +It's an automated robot-disdainer. It pretends you're not there. +It's Andrew Plotkin plotting something. +It's a NetBSD install CD. +It's an Internet chain letter about sodium laureth sulfate. +It's an inverted billiard ball! +It's an old Duke Ellington record. +It's an ordinary bust of Beethoven... but why is it painted green? +It's another robot, more advanced in design than you but strangely immobile. +It's a perpetual immobility machine. +It's a piece of cloth used to cover a stage in between performances. +It's a pool with a straw in it. +It's a portable hole. A sign reads: "Closed for the winter". +It's a Quaker Oatmeal tube, converted into a drum. +It's a recursive recursive recursive recursive recursive... +It's a revised business plan for a new startup, my.kitten.net. +It's a rim shot. Ba-da-boom! +It's a roll of industrial-strength copper wire. +It's a rotten old shoe. +It's a segmentation fault. Core dumped, by the way. +It's a Shamrock Shake(tm). At least I hope it is. +It's Asimov's Laws of Robotics. You feel a strange affinity for them. +It's a solitary vaccuum tube. +It's a solitary vacuum tube. +It's a squad of Keystone Kops. +It's a square. +It's a stupid mask, fashioned after a beagle. +It's a symbol. You see in it a model for all symbols everywhere. +It's a synthetic a priori truth! Immanuel would be so pleased! +It's a tape of '70s rock. All original hits! All original artists! +It's a tribute to fishnet stockings. +It's a U.S. president. +It's a wonderful life. +It's a zen simulation, trapped within an ASCII character. +It's Babe Flathead's favorite bat. +It's Bach's Mass in B-minor! +It's Brian Kernigan. +It's "Chicken Soup for the Kitten-seeking Soulless Robot." +It's creepy and it's kooky, mysterious and spooky. It's also somewhat ooky. +It's cute like a kitten, but isn't a kitten. +It's Death. +It's Dennis Ritchie. +It seems to be a copy of "A Tail of Two Kitties". +It's either a mirror, or another soulless kitten-seeking robot. +It's Emporer Shaddam the 4th's planet! +It's "Finding kitten", published by O'Reilly and Associates. +It's Grundle, the Green Dragon. +It's just an object. +It's Kieran Hervold. Damn dyslexia! +It's KITT, the talking car. +It's Lucy Ricardo. "Aaaah, Ricky!", she says. +It's Mary Poppins! +It's nothing but a corrupted floppy. Coaster anyone? +It's nothing but a G-thang, baby. +It's nothing in particular. +It's Princess Leia, the yodel of life. +It's Professor Feedlebom. +It's Rhindle, the Red Dragon. +It's Richard Nixon's nose! +It's Roya Naini. +It's scenery for "Waiting for Godot". +It's Sirhan-Sirhan, looking guilty. +It's some compromising photos of Babar the Elephant. +It's the amazing self-referential thing that's not kitten. +It's the ASCII Floating Head of Seth David Schoen! +It's the astounding meta-object. +It's the author of "Randomness and Mathematical Proof". +It's the Bass-Matic '76! Mmm, that's good bass! +It's the constellation Pisces. +It's the cork to someone's lunch. +It's the crusty exoskeleton of an arthropod! +It's the Donation of Constantine! +It's the embalmed corpse of Vladimir Lenin. +It's the Golden Banana of Discord! +It's the handheld robotfindskitten game, by Tiger. +It's the horizon. Now THAT'S weird. +It's the instruction manual for a previous version of this game. +It's the local draft board. +It's the missing chapter to "A Clockwork Orange". +It's the phrase "and her", written in ancient Greek. +It's the proverbial wet blanket. +It's the Tiki Room. +It's the triangle leg adjacent to an angle divided by the leg opposite it. +It's the Will Rogers Highway. Who was Will Rogers, anyway? +It's this message, nothing more. +It's TV's lovable wisecracking Crow! "Bite me!", he says. +It's Uncle Doctor Hurkamur! +It's "War and Peace" (unabridged, very small print). +It's Yorgle, the Yellow Dragon. +It's your favorite game -- robotfindscatan! +Judith Platt insults librarians. +Just a big brick wall. +Just a box of backscratchers. +Just a broken hard drive containg the archives of Nerth Pork. +Just a cage of white mice. +Just a man selling an albatross. +Just a moldy loaf of bread. +Just a monitor with the blue element burnt out. +Just an autographed copy of the Kama Sutra. +Just a pincushion. +Just some glop of some sort. +Just some old play by a Czech playwright, and you can't read Czech. +Just some stuff. +Just some swamp gas. +Just the empty husk of a locust. +Just Walter Mattheau and Jack Lemmon. +"Kibo was here" +"Kilroy was here" +Kitten is the letter 'Q'. Oh, wait, maybe not. +"Lend us a fiver 'til Thursday", pleas Andy Capp. +Leonard Richardson is here, asking people to lick him. +Long lost needle nose pliers. +Look at that, it's the Crudmobile. +Look, it's Fanny the Irishman! +Look out! Exclamation points! +Lysine, an essential amino acid. Well, maybe not for robots. +"Mail Routing and the Domain System" by Craig Partridge. +Marvin is complaining about the pain in the diodes down his left side. +"Meow meow meow meow..." How discouraging! It's only a recording. +More grist for the mill. +"Move along! Nothing to see here!" +Mr. Hooper is here, surfing. +Mr. Kamikaze and Mr. DNA are here drinking tea. +Ne'er but a potted plant. +Nipples, dimples, knuckles, NICKLES, wrinkles, pimples!! +No kitten here. +"No!" says the bit. +Not kitten, just a packet of Kool-Aid(tm). +Oh boy! Grub! Er, grubs. +ONE HUNDRED THOUSAND CARPET FIBERS!!!!! +One of the few remaining discoes. +One of those stupid "Homes of the Stars" maps. +One person shouts "What do we want?" The crowd answers "Free Dmitry!" +"On this spot in 1962, Henry Winkler was sick." +Ooh, shiny! +Paul Moyer's necktie. +Plenty of nothing. +"Plexar was here" +Preoccupation with finding kitten prevents you from investigating further. +Pumpkin pie spice. +Quidquid Latine dictum sit, kitten non est. +Rene Descarte is whistling a happy tune here. +"Robot may not injure kitten, or, through inaction, ..." +Robot should not be touching that. +Roger Avery, persona un famoso de los Estados Unidos. +Run away! Run away! +Seargent Duffy is here. +Seven 1/4" screws and a piece of plastic. +Several meters of cat5 cable. +Sigmund Freud is here asking about your mother. +Slack! +Snarf? +Some coconut crabs are milling about here. +Someone has written "ad aerarium" on the ground here. +Someone is talking to Ralph on the big white phone here. +Someone's identity disk lies here. +Some sort of electronic handheld game from the 1970s. +Something is written here in the dust. You read: "rJbotfndQkttten". +So, THAT's what an invisible barrier looks like! +Spoon!!! +Stimutacs. +"Sure hope we get some rain soon," says Farmer Joe. +Sutro Tower is visible at some distance through the fog. +Talcum powder. +Tea and/or crumpets. +Ten yards of avocado-green shag carpet. +Thar's Mobius Dick, the convoluted whale. Arrr! +That's just a charred human corpse. +That's just an old tin can. +The boom box cranks out an old Ethel Merman tune. +The Digital Millennium Copyright Act of 1998. +The dirty old tramp bemoans the loss of his harmonica. +The ghost of your dance instructor, his face a paper-white mask of evil. +The Inform Designer's Manual (4th edition) +The intermission from a 1930s silent movie. +The ionosphere seems charged with meaning. +The letters O and R. +The Monolith of Spam towers above you. +The non-kitten item bites! +The non-kitten item like this but with "false" and "true" switched is true. +The non-kitten item like this but with "true" and "false" switched is false. +The object pushes back at you. +There is an opulent throne here. +"There is no kitten!" cackles the old crone. You are shocked by her blasphemy. +There is no tea here. +There's nothing here; it's just an optical illusion. +The rothe hits! The rothe hits! +The score for a Czech composer's "Kitten-Finding Symphony in C". +These aren't ordinary beans. They're magic beans! +The spectre of Sherlock Holmes wills you onwards. +The swampy ground around you seems to stink with disease. +"The Theory and Practice of Oligarchical Collectivism" by Emmanuel Goldstein. +The United States Court of Appeals for the Federal Circuit. +The World's Biggest Motzah Ball! +The world's largest ball of pocket lint. +...thingy??? +This appears to be a rather large stack of trashy romance novels. +This appears to be a statue of Perseus. +This balogna has a first name, it's R-A-N-C-I-D. +This copy of "Steal This Book" has been stolen from a bookstore. +This corroded robot is clutching a mitten. +This grain elevator towers high above you. +This invisible box contains a pantomime horse. +This is a Lagrange point. Don't come too close now. +This is a large brown bear. Oddly enough, it's currently peeing in the woods. +This is an anagram. +This is another fine mess you've gotten us into, Stanley. +This is a porcelain kitten-counter. 0, 0, 0, 0, 0... +This is a remote control. Being a robot, you keep a wide berth. +This is a tasty-looking banana creme pie. +This is a television. On screen you see a robot strangely similar to yourself. +This isn't the item you're looking for. +This is the chapter called "A Map of the Cat?" from Feynman's autobiography. +This is the forest primeval. +This is the tenth key you've found so far. +This is the world-famous Chain of Jockstraps. +This jar of pickles expired in 1957. +This jukebox has nothing but Cliff Richards albums in it. +This kind of looks like kitten, but it's not. +This kit is the fourteenth in a series of kits named with Roman letters. +This looks like Bradley's "Appearance and Reality", but it's really not. +This map is not the territory. +This might be the fountain of youth, but you'll never know. +This non-kitten item no verb. +This nonkitten may contain peanuts. +This object here appears to be Louis Farrakhan's bow tie. +This object is like an analogy. +This particular monstrosity appears to be ENIAC. +This peg-leg is stuck in a knothole! +This place is called Antarctica. There is no kitten here. +This seems to be junk mail addressed to the finder of the Eye of Larn. +This smiling family is happy because they eat LARD. +This subwoofer was blown out in 1974. +This toaster strudel is riddled with bullet holes! +This tomography is like, hella axial, man! +This TRS-80 III is eerily silent. +This was no boating accident! +Three half-pennies and a wooden nickel. +Thunder, Thunder, Thunder, Thunder Cats!!! +Tigerbot Hesh. +"Topsoil's all gone, ma," weeps Lil' Greg. +TV says donuts are high in fat. +'Twas brillig in the slivey-toves... +Two crepes, two crepes in a box. +Vladimir Lenin's casket rests here. +Wait! This isn't the poker chip! You've been tricked! DAMN YOU, MENDEZ! +Werner's "Pocket Field Guide to Things That Are Not Kitten". +We wish you a merry kitten, and a happy New Year! +What in blazes is this? +What's that blue thing doing here? +Why are you touching this when you should be finding kitten? +YEEEEEEEEEEEHAAAAAAAA!!!!! +"Yes!" says the bit. +You can see right through this copy of Brin's "Transparent Society". +You disturb a murder of crows. +You feel strangely unfulfilled. +You find a bright shiny penny. +You find a fraud scheme in which loans are used as security for other loans. +You found Chinchilla! Too bad this isn't "Robot Finds Chinchilla". +You found kitten! No, just kidding. +You found netkit! Way to go, robot! +You found nettik, but that's backwards. +You found Parakeet. Too bad this isn't "Robot Finds Parakeet". +You found Puppy! Too bad this isn't "Robot Finds Puppy". +You found the marble in the oatmeal! +You have found some zinc, but you must not stop here, for you must find kitten. +You have new mail in /var/spool/robot +You hit the non-kitten item. The non-kitten item fails to yowl. +You really don't want to know what this is. +Your pal Floyd is here and wants to play Hucka-Bucka-Beanstalk. +Your State Farm Insurance(tm) representative! +You see a snowflake here, melting slowly. +You stumble upon Bill Gates' stand-up act. +You suddenly yearn for your distant homeland. +You've found Harvey, the Wonder Hamster! +You've found... Oh wait, that's just a cat. +You've found the fabled America Online disk graveyard! +You've found the fish! Not that it does you much good in this game. +You've found the snows of yesteryear! So that's where they all went to. diff --git a/app/examples/Games/RobotFindsKitten/nkis_ru.txt b/app/examples/Games/RobotFindsKitten/nkis_ru.txt new file mode 100644 index 00000000..6fe5f4db --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/nkis_ru.txt @@ -0,0 +1,622 @@ +«201 глагол для котёнка, полностью спрягаемые». Вы ищете глагол «найти». +«50 лет среди предметов, не относящихся к котятам», Энн Дройд. +Здесь на стене 99 бутылок пива. +100-метровая цепь гигантских скрепок. +256-килобайтный чип памяти только для записи. +3-дюймовая дискета. +3-сторонняя запись Монти Пайтона. +Камертон 540 Гц. +Бабуин с фаготом сердито кричит на вас. +Здесь на боку лежит сильно помятая высокая цимбала. +Мешок с продуктами снят с полки до истечения срока годности. +Шарик из карманного пуха. +Клубок пряжи. +Большой бас-барабан с дыркой и подозрительными следами от когтей. +Большой кусок замороженного шоколадного пудинга. +Самодельная катушка Тесла. +Пустой депозитный чек. +Боболинк здесь щебечет счастливую мелодию. +Книга: Фен-шуй, Дзен: искусство случайного расположения предметов, которые не являются котятами. +Книга с надписью «Не паникуй» большими дружескими буквами на обложке. +Бутылка дистиллированной воды. +Бутылка с тоником для волос. +Бутылка масла! Освежающая! +Бутылка вонючих солей. +Шар для боулинга с надписью «Боб». +Ваза с вишнями. +Коробка с новыми трубками Никси. +Коробка с танцующими механическими карандашами. Они танцуют! Они поют! +Коробка фумигационных гранул. +Мозговая клетка. Как ни странно, она, кажется, функционирует. +Хлебница. Нет, котёнка нет в хлебнице. +Портфель со шпионскими штучками. +Сломанный метроном сидит здесь, он накренился в сторону. +Бар Маслянка. +Капуста. +Канистра взбитых сливок под давлением, без взбитых сливок. +Консервы тушёнки. +Здесь сидит карточная акула, тренирующая свои плавники. Она игнорирует вас. +Здесь сидит колдун, практикуя свою фаро-перетасовку. Он игнорирует вас. +Цепочка, висящая на двух столбах, напоминает вам арку ворот. +Заросли камышей растут здесь. +Вешалка парит в воздухе. Странно. +Какаду вопит на вас. +Сборник хайку о металлах. +Копия Мурзилки. В эти дни она стоит десять рублей. +Копия еженедельных мировых новостей. Остерегайтесь камерного наутилуса! +Купон на одну бесплатную стейк-рыбу в местном ресторане. +Сухарик. +Толпа людей, а в центре популист. +Хрустальный шар. Кажется, он не знает, где котёнок. +Циклоп сердито смотрит на вас. +Качающаяся вещь, изуродованная котёнком. +Тёмно-излучающий диод. +«Распределение адресов для домашнего Интернета» Якова Рехтера и соавторов. +Демонический голос провозглашает: «Нет котёнка, только Зуул». Вы бежите. +Цифровые часы. Они встали в 14:17. +Выброшенная волынка из певчего тростника. +Выброшенная разбитая бутылка. +Выброшенный холодильник. Нет, котёнка нет в нём. +Дискредитированная космология, пережиток ушедшей эпохи. +Додекаэдр преграждает вам путь. +Корзина, полная волос. +Семейство интегралов здесь интегрируется. +Яркая боа из перьев. Теперь вы можете одеваться как Филипп Киркоров! +Листовка, рекламирующая продажу в городе Париже. +Листовка гласит: «Пожалуйста, пожертвуйте гидравлическую жидкость» +Забытый телефонный коммутатор. +Забытый оператор телефонного коммутатора. +Свежеиспечённый тыквенный пирог. +Замороженое розовое праздничное пирожное, наполовину съеденное. +Карта «Выйти из тюрьмы свободным». +Гейзер распыляет воду высоко в воздух. +Славный поклонник перьев павлина. +Здесь стоит надгробие. «Вечная память». +Зелёный йо-йо. +Смайлик. +Машинка для стрижки волос. Теперь вы знаете, где её взять! +Безволосая крыса. +Недоеденный бутерброд с сыром. +Гамак растянулся между деревом и волейбольным шестом. +Ёжик. Похоже, он знает что-то важное. +Здесь разбился вертолёт. +Здесь дремлет стадо диких кофейных кружек. +Здесь притаилась дюжина одичавших кофейных кружек. +Исторический маркер, показывающий фактическое местоположение /dev/null. +Гулкий голос говорит: «Дурак». +Гулкий голос говорит «Паши». +Ах, юбка с волынками и шелест кремния... +Ах, мундир эпохи Революции. +Воздух. +Воображаемая гитара!!! Нету-нету-нетушки!!! +Баночка с дистиллированной водой. +Баночка с библиотечной пастой. +Баночка Вегемита играет здесь классики. +Бутылка кетчупа (почти пустая). +Раковина котёнка, для мытья котёнка (если только котёнку понравилась вода). +Туалет котёнка (рядом с раковиной котёнка). +Рыцарь, который говорит: «Или я безумный мошенник, или вы найдёте котёнка». +Большой голубой глаз плавает в воздухе. +Большая куча резинок. +Большая змея преграждает вам дорогу. +Огромное банное полотенце. +Кожаный чехол, наполненный многогранными кубиками. +Чужое нижнее бельё. +Маленькая стеклянная ванночка от бальзама для губ. Жаль, что у вас нет губ. +Костюм члена гильдии! Получи свой! +Одинокая, забытая запятая сидит здесь, рыдая. +Лотос. Вы образуете интересную пару. +Волшебная... преволшебная вещь. +Марихуана домового. +Здесь находится открытое варенье. На этикетке написано: «Не открывай!». +Математик вычисляет вероятность остановки машины Тьюринга. +Сурикат... даже не близко. +Обёртка от Ментос. +Простая коллекция пикселей. +Коробка с молоком, с чёрно-белым изображением котёнка сбоку. +Мышь. +Процессор 80286. +Заброшенная стоянка подержанных машин. +Винтовка АК-47. +Винтовка АК-97. +Живой шарик кислоты. Будучи металлическим, вы держитесь от него подальше. +Ароматерапевтическая свеча горит целительным светом. +Атомно-векторный плоттер. +Копия с автографом «Основные цвета», анонимный автор. +Автоматизированный робот-сомневающийся. Он не верит в вас. +Автоматизированный робот-ненавистник. Он неодобрительно смотрит на вас. +Автоматизированный робот-улыбашка. Он улыбается вам. +В высшей степени забытый Гоголь. +Пустая оловянная банка. +Пустая катушка коаксиального кабеля. +Пустая жестянка от мятной вкусняшки. +Пустая корзина. Бумага или пластик? +Винтовка Маузера. +Нейронная сеть - возможно, она пытается распознать котёнка. +Транзистор с истёкшим сроком годности. +Винтовка Генри. +Ледяной айсберг. +Невероятно дорогая коллекционная тарелка «Схожу по вам с ума». +Винтовка Шарпса. +Пистолет Макарова. +Пистолет Колибри. +Старая загрузочная дискета, к сожалению, сломанная посередине. +Старый шаблон здесь продолжается и продолжается. +Старый ржавый револьвер. +Неописанное поле крекеров. +Ещё один кролик? Сегодня три! +Рукавица с вышитыми на ней котятами. +Переполненное ведро с битами. +Опрокинутая бутылка чернил и множество следов котёнка. +Ряд коротких театральных постановок индексируются 1, 2, 3, ... n. +Нелицензированный ядерный ускоритель. +Любой обычный робот может видеть за милю, что это не котёнок. +Пакет из кошачьей мяты. +Очистители для труб. +Пара боевых ботинок. +Пара дверей в салонном стиле медленно качаются взад и вперёд. +Бумажная хозяйственная сумка. Нет, котёнка нет в сумке. +Попугай, бьющий по спине. +Реликвия из пещер мамонта. +Виноградное желе варится здесь. +Здесь поляна грибов. +Здесь лежит куча коаксиальных кабелей. +Куча кокосовых орехов. +Розовая пластиковая лейка. +Горячая пицца. Бесполезная. +Пицца, тающая на солнце. +Пластиковая модель котёнка. +Плюшевый чебурашка. +Видимо, это Эдмунд Берк. +Врезаюсь. Одна машина. Далее две машины. Пять. Протокол. +Лужа с шоколадным соусом. +Лужа грязи, где играют прыгуны. +Чаша для чая, наполненная чаем и дольками лимона. +Радио хрипит. Котёнок, должно быть, был здесь. +Протухший собачий корм. +Ружьё Оленебой. +Робот-комик. Вы чувствуете себя удивленными. +Переспелая дыня. +Ржавая слинки. Это была такая замечательная игрушка! +Мешок с дверными ручками. +Мешок с молотками. +Мешок мокрых мышей. +Инкубаторий с лососем? Посмотрите снова. Это просто один лосось. +Каталог компании Санрио. +Кусок пергамента с единственным словом «мяу». +Приспособление для царапания. +Отвёртка. +Секция светящихся люминофорных клеток поёт вам песню радиации. +Набор ключей для Запорожца. Безделушка. +Бесстыдный форк для Crummy: http://www.crummy.com/ +Бесстыдный форк для Frotz: http://www.cs.csubak.edu/@@126dgriffi/proj/frotz/ +Бесстыдный плагин для UCLA Linux Users Group: http://linux.ucla.edu/ +Ash здесь бормочет «KLAATU BARATA NI <кашель>». +Указатель «Котёнок». Это не указывает ни на какое конкретное направление. +Вывеска гласит: «Не наступайте на маму-крыску». +Надпись гласит: «Идите домой!» +Надпись гласит: «Роботы не допускаются!» +Поющая лягушка. Бесполезная. +Слегка использованный набор для фильма с запахом. +Дымящийся утюг в форме 24-контактного разъёма. +Шпиндель и точильный станок! +Пачка 7-дюймовых дискет шатко шатается. +Статуя девушки с гусем, как в Геттингене, Германия. +Паровой двигатель. +Стегозавр, сбежавший из игры «стегозавр ищет робота». Он нашёл вас. +Субатомная частица томится здесь одна. +Швейцарский армейский нож. Все его придатки отсутствуют. (зубочистка потеряна) +Команда арктических исследователей находится здесь. +Технический университет в Австралии. +Тетрадрахма, датированная «42 г. до н.э.» +Калькулятор. +Крошечный керамический котёнок. Вероятно, это не тот котёнок, которого вы ищете. +А ногти на ногах? Что хорошего в ногтях на ногах? +Унитаз занимает это место. +Тонна перьев. +Светофор. Похоже, что недавно был разбит вандалами. +Здесь проходит поток мыслей. +Уплотнитель мусора, уплотняющий. +Циклотрон путешествующего размера. +Дерево с желе, намазанном по нему. +Здесь живёт племя людоедов. Вы знаете, они едят кашу на завтрак. +Тролль!!! +Тюбик зубной пасты. Жаль, что у вас нет зубов. +Тюбик белой литиевой смазки. Идеально подходит для ваших роботизированных суставов. +Клок шерсти котёнка, но без котёнка. +Ваза, полная искусственных цветов, поставлена на пол. +Ваза с розами. +Раздаётся голос «Вперёд, кошачьи солдаты...» +Готовый спелый помидор оплакивает вашу неспособность переваривать фрукты. +Каркасная модель хот-дога вращается здесь в космосе. +Удивительный и замысловатый золотой амулет. Жаль, что у вас нет шеи. +Монета Зоркмида. +Уплотнительная проволока и жвачка. +Бибиди Бибиди Бибиди Бибиди Бибиди Бибиди... +Большая птица здесь ищет мистера Лупера. +Миллиарды и миллиарды вещей, которые не являются котятами. +Печенье. +«Блеф, блеф, блеф», говорит грязевой котелок. +Яркие медные чайники. +Бургер!!!! Вкусный и полный белка! +Бутан!!! +Газированная вода, кукурузный сироп с высоким содержанием фруктозы, краситель, фосфорная кислота, ароматизаторы, кофеин. +Карлос Таранго стоит здесь, производя лучшее впечатление на Пэт Смир. +Кетчуп и горчица повсюду! Это человеческий гамбургер! +Жевательная резинка и проволока. +Клэнг, клэнг, клэнг идёт поездом! +Клиффорд Столл здесь продаёт бутылки Кляйн. +Конан О'Брайан, без челюсти. +Это может быть... большой уродливый трофей для боулинга? +Ежедневный подвешивающийся кондиционер из Австралии +Танцуют трубы холодной воды. Майк, должно быть, был здесь. +Дарт Вейдер здесь ищет своего тедди-вуки. +«Дорогой робот, вы, возможно, уже выиграли приз в 10 МИЛЛИОНОВ ДОЛЛАРОВ...» +Определённо не котенок. +Динсдейл! +Диоген здесь требует виски. +Грязные носки. +«Техническая поддержка Догберта, как я могу оскорбить тебя?» +Дудлс Уивер просматривает расписание скачек. +Эд МакМэхон стоит здесь, задумавшись. Увидев вас, он ревёт: «Да, сэр!» +Эд Виттен сидит здесь, размышляя над теорией струн. +Пустые шкатулки засоряют пейзаж. +Невероятно! +Фонзи сидит здесь, бессвязно бормоча о акуле и паре водных лыж. +На мгновение вы чувствуете что-то в своих руках, но это исчезает! +Свободный Дмитрий Скляров! +Свободный Джон Йохансен! +Гибл, Гоббл, мы принимаем тебя... +«Возвращайтесь в библиотеку!» - говорит Пэт Шредер. +Измельчить их, выплюнуть, они ветки. +Фисташковые орехи. +Здеееееееесь Джонни! +Здравствуйте, медсестра! +Привет, моряк! +Вот книга о Роберте Кеннеди. +Здесь нет котёнка, а есть только камень, булыжник и песчаная дорога. +Здесь Кэл Уортингтон и его собака «Спот»! +Здесь Пит Питерсон. Его батареи, кажется, давно разряжены. +Эй, держу пари, вы думали, что это котёнок. +Эй, смотрите, это война. Для чего это? Совершенно не для чего. Скажите это снова. +Эй, робот, оставьте эти списки в покое. +«Привет, меня зовут Энсон Уильямс, телевизионная «Потси»». +Гул. Ещё один синтетический апостериорный. +«Как, чёрт возьми, я могу вымыть свою шею, если больше не будет дождя?», - спрашивает фермер Аль. +Гидравлическая жидкость и зубчатые металлические биты. Вы отпрыгиваете от сцены резни. +Я не знаю, что это, но это не котёнок. +Если это не одно, то это другое. +Если это одно, то это не другое. +«Мне жаль дурака, который принимает меня за котёнка!». +Это голова слона или крылатая сандалия? +Это облако в форме вола. +Это марципановый дредноут, который, похоже, расплавлен и застрял. +Это древний моряк, и он останавливает одного из трёх. +Это вставные зубы. +Это - я просто чувствую, - что должно произойти что-то чудесное. +Приятно быть добрым к котёнку, но это не так! +Это 1000 секретов, которые правительство не хочет, чтобы вы знали! +Это банан! О, радость! +Это большой кусок льда. Кажется, что-то застыло внутри. +Это большая курящая рыба. +Это чёрная дыра. Не падай! +Это явный штекер для Ogg Vorbis, http://www.vorbis.com/ +Это слепой человек. Когда вы касаетесь, он восклицает: «Я робот для поиска котят!» +Это капля белого липкого вещества. +Это бутылка для снятия лака. +Это баг. +Это стенд для буррито. +Это бизнес-план для нового стартапа, котёнок.нет. +Это картонная коробка, полная 8-миллиметровой ленты. +Это кабель Кошак-5. +Это каталог какой-то компании Инфоком. +Это угольный брикет, дымящий по ветру. +Это ключ! +Это печенье в форме котёнка. +Это копия Кнута с вырванной главой об алгоритмах поиска котят. +Это копия лицензионного соглашения с роботом. +Это копия «Рубайят Шипа Шуди». +Это копия «Дзен и искусство обслуживания роботов». +Это тёмная амфорическая капля материи. +Это DVD «Крадущаяся обезьяна, скрытый котенок», регион, закодированный для Луны. +Это клавиатура Дворака. +Это муха на стене. Привет, лети! +Это бесплатное пиво (с вашей платной подпиской.) +Это свободный Дмитрий Скляров! +Это свободный Джон Йохансен! +Это фанк-бит! +Это гигантский слорр! +Это какой-то пистолет. +Это голограмма разбитого вертолета. +Это кружка с новеллой «ОДИН ДОМА 2: Затерянный в Нью-Йорке». +Это сто долларов. +Это Java-апплет. +Это подвешенный бар! Как низко ты можешь пасть? +Это установочный компакт-диск Linux. +ОНО ЖИВОЕ! АХ ХА ХА ХА ХА! +Это всё мечта. +Это потерянный кошелёк. У его владельца не было домашних животных, так что вы отказываетесь от него. +Это могучий зомби, говорящий о любви и процветании. +Это минута молчания. +Это мышеловка, приманка с мылом. +Это алтарь бога лошади. +Это копия с автографом «Вторичные цвета» Боба Росса. +Это копия с автографом «Боярыня Морозова» Сурикова. +Это автоматический робот-презиратор. Он делает вид, что вас там нет. +Это Андрей Плоткин что-то замышляет. +Это установочный компакт-диск NetBSD. +Это интернет-письмо о лауретсульфате натрия. +Это перевёрнутый бильярдный шар! +Это старый альбом Дюк Эллингтона. +Это обычный бюст Бетховена... но почему он окрашен в зелёный цвет? +Это ещё один робот, более продвинутый по дизайну, чем вы, но странно неподвижный. +Это вечная машина неподвижности. +Это кусок ткани, используемый для закрытия сцены между выступлениями. +Это бассейн с соломой. +Это портативное отверстие. Вывеска гласит: «Закрыто на зиму». +Это коробка от овсяных хлопьев, превращённая в барабан. +Это рекурсивная рекурсивная рекурсивная рекурсивная рекурсия... +Это пересмотренный бизнес-план для нового стартапа - моего.котёнка.нет. +Это выстрел обода. Ба-да-бум! +Это рулон медной проволоки промышленной прочности. +Это гнилая старая обувь. +Это ошибка сегментации. Ядро сброшено, кстати. +Это сезонный молочный коктейль со вкусом зелёной мяты. По крайней мере, я на это надеюсь. +Это законы робототехники Азимова. Вы чувствуете странную близость к ним. +Это одиночная вакуумная трубка. +Это синхрофазотрон. +Это команда Кейстоун Копс. +Это квадрат. +Это глупая маска, созданная по образцу бигля. +Это символ. Вы видите в нём модель для всех символов повсюду. +Это синтетическая априорная истина! Иммануил был бы так рад! +Это лента рока 70-х. Все оригинальные хиты! Все оригинальные художники! +Это дань чулкам в сеточку. +Это президент США. +Это прекрасная жизнь. +Это симуляция дзен, заключённая в символ ASCII. +Это любимая летучая мышь детки Флэтхеда. +Это «Реквием» Баха си-минор! +Это Брайан Керниган. +Это «Куриный суп для котёнка, ищущего бездушного робота». +Это жутко и странно, таинственно и жутко. Это также несколько странно. +Это милое создание как котёнок, но не котёнок. +Это погибель. +Это Деннис Ритчи. +Кажется, это копия «Хвоста двух котят». +Это либо зеркало, либо другой бездушный робот, ищущий котёнка. +Это Эмпорер Шаддам четвёртая планета! +Это «В поисках котёнка», изданная «О'Рейли и Партнеры». +Это зелёный дракон Grundle. +Это просто объект. +Это Киран Хервольд. Чёртова дислексия! +Это КОТ, говорящая машина. +Это Люси Рикардо. «Ааааа, Рикки!», - говорит она. +Это Мэри Поппинс! +Это не что иное, как испорченная дискета. У кого-нибудь есть подставка? +Это всего лишь рэпчик, детка. +Ничего особенного. +Это принцесса Лея, йодел жизни. +Это профессор Фидлбом. +Это красный дракон Rhindle. +Это нос Ричарда Никсона! +Это Роя Найни. +Это декорация для «В ожидании Годо». +Это Сирхан-Сирхан, выглядит виноватым. +Это несколько компрометирующих фотографий слона Бабара. +Это удивительная вещь, которая не является котёнком. +Это таблица ASCII! +Это поразительный мета-объект. +Это автор «Случайности и математические доказательства». +Это Бас-Матик 76 года! Ммм, это хороший бас! +Это созвездие Рыб. +Это пробка для чьей-то бутылки. +Это хрустящий экзоскелет членистоногого! +Это пожертвование Константина! +Это забальзамированный труп Владимира Ленина. +Это золотой банан раздора! +Это портативная игра для роботов и тигров от Tiger. +Это горизонт. Теперь это странно. +Это инструкция к предыдущей версии этой игры. +Это местная доска объявлений. +Это недостающая глава к «Заводному апельсину». +Это фраза «и она», написанная на древнегреческом языке. +Это общеизвестное мокрое одеяло. +Это комната Тики. +Это треугольная нога, примыкающая к углу, разделенному на ногу напротив нее. +Это шоссе Уилла Роджерса. Кто такой Уилл Роджерс? +Это сообщение, не более того. +Это привлекательная ворона! «Угостите сыром!» - говорит она. +Это дядя доктор Хуркамур! +Это «Война и мир» (без ограничений, очень мелкий шрифт). +Это жёлтый дракон Yorgle. +Это твоя любимая игра - робот ищет кошака! +Джудит Платт оскорбляет библиотекарей. +Просто большая кирпичная стена. +Просто ящик с царапинами. +Просто сломанный жёсткий диск. +Просто клетка белых мышей. +Просто человек, продающий альбатроса. +Просто заплесневелый кусок хлеба. +Просто монитор с сгоревшим синим элементом. +Просто копия Йоги с автографом. +Просто подушечка для булавок. +Просто какая-то жижа. +Просто какая-то старая пьеса чешского драматурга, и вы не умеете читать по-чешски. +Просто кое-что. +Просто немного болотного газа. +Просто пустая шелуха саранчи. +Просто Уолтер Матто и Джек Леммон. +«Кибо был здесь» +«Килрой был здесь» +Котёнок это буква «К». Ой, подожди, может быть нет. +«Одолжите нам пятёрку до четверга», - радует Энди Кэпп. +Леонард Ричардсон здесь, просит людей поцеловать его. +Длинные утерянные плоскогубцы. +Посмотрите на это, это Моторолла. +Смотри, это чудной ирландец! +Быдьте осторожны! Восклицательные знаки! +Лизин, незаменимая аминокислота. Ну, может быть, не для роботов. +«Маршрутизация почты и доменная система» Крейга Партриджа. +Марвин жалуется на боль в диодах внизу слева. +«Мяу мяу мяу мяу...» Как обескураживает! Это только запись. +Больше зерна для мельницы. +«Двигайтесь! Здесь нечего смотреть!» +Мистер Хупер здесь, занимается серфингом. +Мистер Камикадзе и мистер ДНК здесь пьют чай. +Не то, но растение в горшке. +Соски, ямочки, костяшки, щипцы, морщины, прыщи! +Здесь нет котёнка. +«Нет!» говорит немного. +Не котёнок, просто пакет Юпи. +О, парень! Grub! Эээ, загрузчики. +СТО ТЫСЯЧ КОВРОВЫХ ВОЛОКОН!!!!! +Одна из немногих оставшихся дискотек. +Одна из тех глупых карт «Дома звёзд». +Один человек кричит «Что мы хотим?» Толпа отвечает «Свободу Дмитрию!» +«На этом месте в 1962 году Генри Винклер заболел». +О, блестяще! +Галстук Саакашвили. +Много пустоты. +«Плексар был здесь» +Озабоченность поиском котёнка мешает вам продолжить расследование. +Тыквенный пирог со специями. +Что угодно, сказанное на латыни, звучит как котёнок. +Рене Декарт насвистывает счастливую мелодию здесь. +«Робот не может причинить вред котёнку или вследствие бездействия...» +Робот не должен прикасаться к этому. +Роджер Эвери, человек, известный в Соединённых Штатах. +Убегайте! Убегайте! +Сирджент Даффи здесь. +Семь 1/4 винтов и кусок пластика. +Несколько метров кабеля кот5. +Зигмунд Фрейд здесь спрашивает о вашей матери. +Отнимите! +Снарф? +Некоторые кокосовые крабы валяются здесь. +Кто-то написал здесь «ad aerarium». +Здесь кто-то разговаривает с Ральфом по большому белому телефону. +Чей-то личный диск лежит здесь. +Какая-то электронная портативная игра 1970-х годов. +Здесь что-то написано в пыли. Вы читаете: «Робат ищит катёнка». +Так вот как выглядит невидимый барьер! +Ложка!!! +Стимутакс. +«Надеюсь, скоро будет дождь», - говорит фермер Джо. +Башня Сутро видна на некотором расстоянии сквозь туман. +Тальк. +Чай и/или сухарики. +Десять ярдов авокадо-зелёного ковра. +Тар Мобиус Дик, извилистый кит. Аррр! +Это просто мумия. +Это просто старая консервная банка. +Бумбокс заводит старую мелодию Этель Мерман. +Закон о защите авторских прав в цифровую эпоху 1998 года. +Грязный старый бродяга оплакивает потерю его гармоники. +Призрак вашего преподавателя танцев, его лицо - бумажно-белая маска зла. +Руководство дизайнера Информ (4-е издание) +Перерыв в немом фильме 1930-х годов. +Ионосфера кажется заряженной смыслом. +Буквы О и Р. +Монолит спама возвышается над вами. +Укус не котёнка! +Элемент без котёнка, подобный этому, но с «ложь» и «истина», переключенным в значение истины. +Элемент без котенка, подобный этому, но с «истина» и «ложь», переключенным в значение лжи. +Объект отталкивает вас. +Здесь есть роскошный трон. +«Там нет котёнка!» хихикает старая карга. Вы в шоке от её кощунства. +Здесь нет чая. +Здесь ничего нет; это просто оптический обман. +Хиты Роты! Хиты Роты! +Партитура для чешской композиции «Симфония ищущего котёнка до мажор». +Это не обычные бобы. Это волшебные бобы! +Призрак Шерлока Холмса побеждает вас впереди. +Болотистая земля вокруг вас, кажется, воняет болезнью. +«Теория и практика олигархического коллективизма» Эммануила Гольдштейна. +Апелляционный суд США по федеральному округу. +Самый большой в мире бал! +Самый большой в мире шар из карманных ворсинок. +...штучка??? +Похоже, это довольно большой сборник романов. +Это похоже на статую Персея. +У этой кассеты есть имя, это R-A-N-C-I-D. +Этот экземпляр «Украсть эту книгу» был украден из книжного магазина. +Этот ржавый робот сжимает варежку. +Этот элеватор возвышается над вами. +Эта невидимая шкатулка содержит лошадь пантомимы. +Это точка Лагранжа. Не подходи слишком близко. +Это большой бурый медведь. Как ни странно, в настоящее время он мочится в лесу. +Это анаграмма. +Это ещё один хороший беспорядок, в который ты нас втянул, Стэнли. +Это фарфоровый котёнок-счётчик. 0, 0, 0, 0, 0... +Это пульт дистанционного управления. Будучи роботом, вы имеете широкую койку. +Это вкусный пирог с банановым кремом. +Это телевизор. На экране вы видите робота, странно похожего на вас. +Это не тот предмет, который вы ищете. +Эта глава называется «Карта кота?» из автобиографии Фейнмана. +Это первобытный лес. +Это десятый ключ, который вы нашли. +Это всемирно известная сеть Jockstraps. +Срок хранения этой банки с маринадом истёк в 1957 году. +Этот музыкальный автомат содержит только альбомы Клиффа Ричардса. +Этот вид похож на котёнка, но это не так. +Этот набор является четырнадцатым в серии наборов, названных римскими буквами. +Это похоже на «Внешний вид и реальность» Брэдли, но на самом деле это не так. +Эта карта не является территорией. +Это может быть источником молодости, но вы никогда не узнаете. +Это кошатник, предмет без глагола. +Этот кошатник может содержать арахис. +Этот объект здесь кажется бабочкой Луи Фаррахана. +Этот объект похож на аналогию. +Это конкретное чудовище, похоже ЭНИАК. +Этот колышек застрял в норе! +Это место называется Антарктида. Здесь нет котёнка. +Похоже, это нежелательная почта, адресованная искателю Ока Ларна. +Эта улыбающаяся семья счастлива, потому что они едят сало. +Этот сабвуфер был взорван в 1974 году. +Этот тостер штрудель пронизан пулевыми отверстиями! +Это томография! +Этот TRS-80 III жутко тихий. +Это не было несчастным случаем на лодке! +Три полпенни и деревянный никель. +Гром, Гром, Гром, Гром Кошек!!! +Тигрэбот Хеш. +«Всё прошло верхний слой почвы, ма», плачет Лил Грег. +Телевизор говорит, что пончики с высоким содержанием жира. +«Алиса в Стране чудес» Кэрролла +Два блинчика, два блинчика в коробке. +Шкатулка Владимира Ленина отдыхает здесь. +Подождите! Это не покерная фишка! Вы были обмануты! +«Карманный полевой путеводитель по вещам, которые не котята» Вернера. +Желаем вам прощённого котёнка и счастливого Нового года! +Что это полыхает? +Что эта синяя штука делает здесь? +Почему вы касаетесь этого, когда вам нужно найти котёнка? +Ох!!!!! +«Да!» говорит немного. +Вы можете видеть прямо сквозь эту копию «Прозрачного общества» Брина. +Вы мешаете убийству ворон. +Вы чувствуете себя странно неудовлетворённым. +Вы нашли яркую блестящую копейку. +Вы нашли схему мошенничества, в которой кредиты используются в качестве обеспечения для других кредитов. +Вы нашли шиншиллу! Жаль, что это не «Робот ищет шиншиллу». +Вы нашли котёнка! Нет, просто прикалываюсь. +Вы нашли неткот! В путь, робот! +Вы нашли конёток, но это задом наперед. +Вы нашли попугая. Жаль, что это не «Робот ищет попугая». +Вы нашли щенка! Жаль, что это не «Робот ищет щенка». +Вы нашли мрамор в овсянке! +Вы нашли немного цинка, но вы не должны останавливаться на достигнутом, потому что вы должны найти котёнка. +У вас есть новая почта в /var/spool/робот +Вы попали в предмет не из котят. Не котёнок не может выкрикнуть. +Вы действительно не хотите знать, что это такое. +Ваш приятель Флойд здесь и хочет сыграть Хака-Бука-Бобовое дерево. +Ваш страховщик! +Вы видите снежинку, которая медленно тает. +Вы наткнулись на постоянный акт Билла Гейтса. +Вы вдруг жаждете своей далёкой родины. +Вы нашли Харви, чудо-хомяка! +Вы нашли... Ой, подождите, это просто кот. +Вы нашли легендарное дисковое кладбище! +Вы нашли рыбу! Не то, чтобы это принесло вам много пользы в этой игре. +Вы нашли снег прошлых лет! Так вот, куда они все пошли. \ No newline at end of file diff --git a/app/examples/Games/RobotFindsKitten/readme.txt b/app/examples/Games/RobotFindsKitten/readme.txt new file mode 100644 index 00000000..67d00522 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/readme.txt @@ -0,0 +1,24 @@ +gambrfk 1.00 +Copyright 2003 Rob Kudla + +In this game, you are robot (#). Your job is to find kitten. This task +is complicated by the existence of various things which are not +kitten. Robot must touch items to determine if they are kitten or +not. The game ends when robotfindskitten. Alternatively, you may end +the game by hitting the Esc key. + +Please see http://www.robotfindskitten.org for more information. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/app/examples/Games/Snake/.directory b/app/examples/Games/Snake/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/Snake/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/Snake/.icon.png b/app/examples/Games/Snake/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..401780e5af5caa4c9594f42894a573f8567d3c74 GIT binary patch literal 3584 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\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FrmMain.form:90 +msgid "&About" +msgstr "Qu&ant a..." + +#: FrmMain.form:72 +msgid "&Auto Advance Speed (Challenge ME)" +msgstr "&Increment automàtic de velocitat (Desafia'm)" + +#: FrmMain.form:64 +msgid "&Cool" +msgstr "&Moderat" + +#: FrmMain.class:318 +msgid "Do not Hit the walls, or bite yourself!\n\n" +msgstr "No colpegis les parets, ni et mosseguis a tu mateix!\n\n" + +#: FrmMain.class:317 +msgid "Eat as many apples as you can\n" +msgstr "Mengeu tantes pomes com podeu\n" + +#: FrmMain.form:34 +msgid "&Game" +msgstr "&Joc" + +#: FrmMain.form:60 +msgid "&GrandMa" +msgstr "À&via" + +#: FrmMain.form:78 +msgid "&Help" +msgstr "A&juda" + +#: FrmMain.form:81 +msgid "&How To Play" +msgstr "Com &jugar-hi" + +#: FrmMain.form:37 +msgid "&New" +msgstr "&Nou" + +#: FrmMain.class:245 +msgid "\nPress Game -> New to play again." +msgstr "\nPrem Joc -> Nou per tornar a jugar." + +#: FrmMain.form:42 +msgid "&Pause" +msgstr "&Pausa" + +#: FrmMain.form:51 +msgid "&Quit" +msgstr "&Surt" + +#: FrmMain.class:358 +msgid "Score: " +msgstr "Puntuació:" + +#: FrmMain.class:58 +msgid "Snake" +msgstr "Serp" + +#: .project:1 +msgid "Snake game" +msgstr "Joc de la serp" + +#: FrmMain.form:57 +msgid "&Speed" +msgstr "&Velocitat" + +#: FrmMain.class:359 +msgid "Speed: " +msgstr "Velocitat:" + +#: FrmMain.form:68 +msgid "&Speed Freak" +msgstr "&Velocitat fora de control" + +#: FrmMain.class:316 +msgid "The Rules are simple:\n" +msgstr "Les normes son senzilles:\n" + +#: FrmMain.class:245 +msgid "The snake's soul will always be remembered.\nGame Over!!\nYou scored: " +msgstr "Sempre recordarem l'ànima de la serp.\nJoc acabat!!\nLa teva puntuació: " + +#: FrmMain.class:319 +msgid "Use the arrow keys to move the snake" +msgstr "Utilitzeu les tecles de fletxa per moure la serp" + diff --git a/app/examples/Games/Snake/.lang/cs.po b/app/examples/Games/Snake/.lang/cs.po new file mode 100644 index 00000000..5da464fa --- /dev/null +++ b/app/examples/Games/Snake/.lang/cs.po @@ -0,0 +1,114 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Snake game" +msgstr "Hadí hra" + +#: FrmMain.class:58 +msgid "Snake" +msgstr "Had" + +#: FrmMain.class:245 +msgid "" +"\n" +"Press Game -> New to play again." +msgstr "" +"\n" +"Pro novou hru -> Zmáčkni Hra." + +#: FrmMain.class:245 +msgid "" +"The snake's soul will always be remembered.\n" +"Game Over!!\n" +"You scored: " +msgstr "" +"DuÅ¡e hada bude uložena.\n" +"Konec hry!!\n" +"Tvoje skore:" + +#: FrmMain.class:316 +msgid "The Rules are simple:\n" +msgstr "Pravidla jsou jednoduchá:\n" + +#: FrmMain.class:317 +msgid "Eat as many apples as you can\n" +msgstr "MužeÅ¡ jíst mnoho jablek\n" + +#: FrmMain.class:318 +msgid "" +"Do not Hit the walls, or bite yourself!\n" +"\n" +msgstr "" +"Nenařážejte do zdí, nebo se nekousejte!\n" +"\n" + +#: FrmMain.class:319 +msgid "Use the arrow keys to move the snake" +msgstr "Použij klávesy Å¡ipek pro pohyb hada" + +#: FrmMain.class:358 +msgid "Score: " +msgstr "Skóre:" + +#: FrmMain.class:359 +msgid "Speed: " +msgstr "Rychlost:" + +#: FrmMain.form:34 +msgid "&Game" +msgstr "&Hra" + +#: FrmMain.form:37 +msgid "&New" +msgstr "&Nová" + +#: FrmMain.form:42 +msgid "&Pause" +msgstr "&Pauza" + +#: FrmMain.form:51 +msgid "&Quit" +msgstr "&Ukončit" + +#: FrmMain.form:57 +msgid "&Speed" +msgstr "&Rychlost" + +#: FrmMain.form:60 +msgid "&GrandMa" +msgstr "&Babička" + +#: FrmMain.form:64 +msgid "&Cool" +msgstr "&Užasný" + +#: FrmMain.form:68 +msgid "&Speed Freak" +msgstr "&Šílená rychlost" + +#: FrmMain.form:72 +msgid "&Auto Advance Speed (Challenge ME)" +msgstr "&Auto zvyÅ¡ování rychlosti (moje výzva)" + +#: FrmMain.form:78 +msgid "&Help" +msgstr "&Nápověda" + +#: FrmMain.form:81 +msgid "&How To Play" +msgstr "&Jak hrát" + +#: FrmMain.form:90 +msgid "&About" +msgstr "&O aplikaci" diff --git a/app/examples/Games/Snake/.lang/de.po b/app/examples/Games/Snake/.lang/de.po new file mode 100644 index 00000000..f48ae5dc --- /dev/null +++ b/app/examples/Games/Snake/.lang/de.po @@ -0,0 +1,101 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Snake game" +msgstr "Schlangenspiel" + +#: FrmMain.class:58 +msgid "Snake" +msgstr "Schlange" + +#: FrmMain.class:245 +msgid "\nPress Game -> New to play again." +msgstr "\nWähle Spiel -> Neu, um nochmal zu spielen." + +#: FrmMain.class:245 +msgid "The snake's soul will always be remembered.\nGame Over!!\nYou scored: " +msgstr "Die Seele der Schlange bleibt unvergessen.\nSpiel ist aus!!\nDeine Punkte: " + +#: FrmMain.class:316 +msgid "The Rules are simple:\n" +msgstr "Die Regeln sind einfach:\n" + +#: FrmMain.class:317 +msgid "Eat as many apples as you can\n" +msgstr "Iss so viele Äpfel, wie du kannst\n" + +#: FrmMain.class:318 +msgid "Do not Hit the walls, or bite yourself!\n\n" +msgstr "Do not Hit the walls, or bite yourself!\n\n" + +#: FrmMain.class:319 +msgid "Use the arrow keys to move the snake" +msgstr "Die Schlange wird mit den Pfeiltasten bewegt." + +#: FrmMain.class:358 +msgid "Score: " +msgstr "Punkte:" + +#: FrmMain.class:359 +msgid "Speed: " +msgstr "Geschwindigkeit:" + +#: FrmMain.form:34 +msgid "&Game" +msgstr "&Spiel" + +#: FrmMain.form:37 +msgid "&New" +msgstr "&Neues Spiel" + +#: FrmMain.form:42 +msgid "&Pause" +msgstr "-" + +#: FrmMain.form:51 +msgid "&Quit" +msgstr "&Beenden" + +#: FrmMain.form:57 +msgid "&Speed" +msgstr "&Geschwindigkeit" + +#: FrmMain.form:60 +msgid "&GrandMa" +msgstr "&Großmutter" + +#: FrmMain.form:64 +msgid "&Cool" +msgstr "&Mäßig" + +#: FrmMain.form:68 +msgid "&Speed Freak" +msgstr "-" + +#: FrmMain.form:72 +msgid "&Auto Advance Speed (Challenge ME)" +msgstr "&Automatischer Geschwindigkeitsfortschritt" + +#: FrmMain.form:78 +msgid "&Help" +msgstr "&Hilfe" + +#: FrmMain.form:81 +msgid "&How To Play" +msgstr "&Spielanleitung" + +#: FrmMain.form:90 +msgid "&About" +msgstr "&Über" + diff --git a/app/examples/Games/Snake/.lang/ru.po b/app/examples/Games/Snake/.lang/ru.po new file mode 100644 index 00000000..f90d7505 --- /dev/null +++ b/app/examples/Games/Snake/.lang/ru.po @@ -0,0 +1,162 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/Snake/.project:19 +msgid "Snake game" +msgstr "Игра змея" + +#: app/examples/Games/Snake/.src/FrmMain.class:46 app/examples/Games/Snake/.src/FrmMain.class:58 +msgid "Snake" +msgstr "Змея" + +#: app/examples/Games/Snake/.src/FrmMain.class:46 +msgid "v1.0" +msgstr "- версия 1.0" + +#: app/examples/Games/Snake/.src/FrmMain.class:61 +msgid "ver 1.0 by: Ahmad Kamal" +msgstr "версия 1.0 от: Ахмад Камаль" + +#: app/examples/Games/Snake/.src/FrmMain.class:245 +msgid "" +"The snake's soul will always be remembered.\n" +"Game Over!!\n" +"You scored: " +msgstr "" +"Душа змеи навсегда останется в памяти.\n" +"Игра окончена!!\n" +"Вы набрали: " + +#: app/examples/Games/Snake/.src/FrmMain.class:245 +msgid "" +"\n" +"Press Game -> New to play again." +msgstr "" +"\n" +"Нажмите Игра -> Новый, чтобы играть снова." + +#: app/examples/Games/Snake/.src/FrmMain.class:268 app/examples/Games/Snake/.src/FrmMain.class:275 app/examples/Games/Snake/.src/FrmMain.form:18 +msgid "&Pause" +msgstr "Пауза" + +#: app/examples/Games/Snake/.src/FrmMain.class:271 app/examples/Games/Snake/.src/FrmMain.class:272 +msgid "Un&Pause" +msgstr "Возобновить" + +#: app/examples/Games/Snake/.src/FrmMain.class:290 app/examples/Games/Snake/.src/FrmMain.class:366 +msgid "GrandMa" +msgstr "Неспеша" + +#: app/examples/Games/Snake/.src/FrmMain.class:299 app/examples/Games/Snake/.src/FrmMain.class:367 +msgid "Cool" +msgstr "Спокойный" + +#: app/examples/Games/Snake/.src/FrmMain.class:308 +msgid "Speed Freak" +msgstr "Быстрый" + +#: app/examples/Games/Snake/.src/FrmMain.class:316 +msgid "The Rules are simple:\n" +msgstr "Правила просты:\n" + +#: app/examples/Games/Snake/.src/FrmMain.class:317 +msgid "Eat as many apples as you can\n" +msgstr "Ешьте как можно больше яблок\n" + +#: app/examples/Games/Snake/.src/FrmMain.class:318 +msgid "" +"Do not Hit the walls, or bite yourself!\n" +"\n" +msgstr "" +"Не попадайте по стенам и не кусайте себя!\n" +"\n" + +#: app/examples/Games/Snake/.src/FrmMain.class:319 +msgid "Use the arrow keys to move the snake" +msgstr "Использовать клавиши со стрелками для перемещения змеи" + +#: app/examples/Games/Snake/.src/FrmMain.class:358 +msgid "Score: " +msgstr "Счёт: " + +#: app/examples/Games/Snake/.src/FrmMain.class:359 +msgid "Speed: " +msgstr "Скорость: " + +#: app/examples/Games/Snake/.src/FrmMain.class:405 +msgid "" +"Snake v1.0\n" +"By: Ahmad Kamal \n" +"Written for Gambas http://gambas.sf.net" +msgstr "" +"Змея версии 1.0\n" +"От: Ахмад Камаль \n" +"Написано для Gambas http://gambas.sf.net" + +#: app/examples/Games/Snake/.src/FrmMain.form:12 +msgid "&Game" +msgstr "Игра" + +#: app/examples/Games/Snake/.src/FrmMain.form:14 +msgid "&New" +msgstr "Новый" + +#: app/examples/Games/Snake/.src/FrmMain.form:25 +msgid "&Quit" +msgstr "Выход" + +#: app/examples/Games/Snake/.src/FrmMain.form:30 +msgid "&Speed" +msgstr "Скорость" + +#: app/examples/Games/Snake/.src/FrmMain.form:32 +msgid "&GrandMa" +msgstr "Неспеша" + +#: app/examples/Games/Snake/.src/FrmMain.form:35 +msgid "&Cool" +msgstr "Спокойный" + +#: app/examples/Games/Snake/.src/FrmMain.form:38 +msgid "&Speed Freak" +msgstr "Быстрый" + +#: app/examples/Games/Snake/.src/FrmMain.form:41 +msgid "&Auto Advance Speed (Challenge ME)" +msgstr "Автоматическая скорость (Сразись со мной)" + +#: app/examples/Games/Snake/.src/FrmMain.form:46 +msgid "&Help" +msgstr "Справка" + +#: app/examples/Games/Snake/.src/FrmMain.form:48 +msgid "&How To Play" +msgstr "Как играть" + +#: app/examples/Games/Snake/.src/FrmMain.form:55 +msgid "&About" +msgstr "О программе" + diff --git a/app/examples/Games/Snake/.project b/app/examples/Games/Snake/.project new file mode 100644 index 00000000..915eafcc --- /dev/null +++ b/app/examples/Games/Snake/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Snake game +Startup=FrmMain +Icon=head.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.sdl.sound +Environment="GB_GUI=gb.qt" +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Games/Snake/.src/FrmMain.class b/app/examples/Games/Snake/.src/FrmMain.class new file mode 100644 index 00000000..5a53af7d --- /dev/null +++ b/app/examples/Games/Snake/.src/FrmMain.class @@ -0,0 +1,430 @@ +' Gambas class file + +'***********Snake Example Game********************************* +'***********By: Ahmad Kamal ****************** +'***********Written for Gambas: gambas.sourceforge.net********* +'***********V1.0: 02-Aug-2003********************************** +'***********V1.1: 16-Apr-2004********************************** +'***********Added SDL sound support**************************** + +Public Head_Direction As String + +Public Xpos As New Integer[] 'Snake's Xpositions, Xpos[0] = head +Public Ypos As New Integer[] 'Snake's Ypositions, Ypos[0] = head +Public Tailpos As New Integer[] 'Array holding the index of the square to be moved. Xpos[ TailPos[0] ] is Xposition of the tail. TailPos keeps rotating +Public AppleXpos As New Integer[] 'Apples Xpos +Public AppleYpos As New Integer[] 'Apples Ypos + +Public Score As Integer +Public Speed As String + +Public hPictHead As Picture +Public hPictBody As Picture +Public hPictApple As Picture + +Public StartSound As Sound +Public EatSound As Sound +Public DeadSound As Sound + +Private $eLast As Float + +Public Sub Form_Open() + + Dim i As Integer + + Randomize + + StartSound = New Sound("start.wav") 'Pre-load all sounds FOR speed issues + EatSound = New Sound("eat.wav") 'Although, one SDL component could handle + DeadSound = New Sound("dead.wav") 'All of them! + + MenuGrandMa_Click() + InitSnake + UpdateScoreBoard + Play("start.wav") + + Me.Text = ("Snake") & " " & ("v1.0") + hPictHead = Picture.Load("head.png") + ' ME.Icon = hPictHead 'The same as head icon but in a standard size + + Dwg.background = Color.Black 'BM: ?????????? Doesn't work + + Draw.Begin(dwg) + Draw.Foreground = &HAA0000& 'Faded red + Draw.Font.Name = "Serif" + Draw.Font.Size = 120 'Give me real big font + For i = 1 To 10 'Trick to draw 3D text + If i = 10 Then Draw.Foreground = &HFF0000& 'This is the last layer, so make it pure red + Draw.text(("Snake"), (dwg.clientW - Draw.TextWidth("Snake")) / 2 - i, (dwg.Height + Draw.TextHeight("Snake")) / 2 - i - 50) + Next + Draw.Font.Size = 18 + Draw.Text(("ver 1.0 by: Ahmad Kamal"), (dwg.clientW - Draw.TextWidth(("ver 1.0 by: Ahmad Kamal"))) / 2, 100) + Draw.End + + hPictHead = Picture.Load("head.png") 'Load images + 'hPictHead.Mask(Color.white) + hPictBody = Picture.Load("body.png") + hPictApple = Picture.Load("apple.png") + + TimerApple_Timer() 'Init apples location array + +End + + +Public Sub Form_KeyPress() +'This function stores the snake direction taken from the keyboard in head_direction + + Dim tmp As Integer + + Select Key.Code + Case Key.Left + If (Head_Direction <> "right") Then Head_Direction = "left" 'Snake isn't supposed to go backwards! + Case Key.Right + If (Head_Direction <> "left") Then Head_Direction = "right" + Case Key.Up + If (Head_Direction <> "down") Then Head_Direction = "up" + Case Key.Down + If (Head_Direction <> "up") Then Head_Direction = "down" + End Select + +End + +Public Sub TmrEngine_Timer() +'This is the main game engine. With every tick, the game status get updated. + + Dim i As Integer + Dim hPictHeadRotated As New Picture + + 'Here we will move the snake. Instead of moving each & every square of the snake + 'We will use an optimized method. Only the head moves one step forward & the + 'Tail moves to where the head previously was. That way, all the snake seems to be moving! + +' If $eLast Then Debug TmrEngine.Delay;; Format(Timer - $eLast, "0.###") + $eLast = Timer + + Xpos[TailPos[0]] = Xpos[0] 'Tail goes to head + Ypos[TailPos[0]] = Ypos[0] + TailPos.add(TailPos.pop(), 0) 'Array is rotated => Last element is put as first element, i.e. new tail is calculated + + Select Head_Direction 'See the current head direction and start moving + Case "left" + Xpos[0] = Xpos[0] - 20 + hPictHeadRotated = hPictHead.Image.Rotate(Pi).Picture 'Snake looks to where it's going :) + Case "right" + Xpos[0] = Xpos[0] + 20 + hPictHeadRotated = hPictHead + Case "up" + Ypos[0] = Ypos[0] - 20 + hPictHeadRotated = hPictHead.Image.Rotate(Pi / 2).Picture + Case "down" + Ypos[0] = Ypos[0] + 20 + hPictHeadRotated = hPictHead.Image.Rotate(-Pi / 2).Picture + End Select + + + 'Begin drawing + Dwg.Clear 'Clear old game drawings + Draw.Begin(Dwg) + + For i = 1 To Xpos.count - 1 'Draw snake + Draw.Picture(hPictBody, Xpos[i], Ypos[i]) + Next + + For i = 0 To 2 'Draw apples + Draw.Picture(hPictApple, AppleXpos[i], AppleYpos[i]) + Next + + Draw.Picture(hPictHeadRotated, Xpos[0], Ypos[0]) 'BM: Why doesn't the head appear on top? put hPictHead, and it will !!! + + Draw.End + + If (SnakeHasEatenApple()) Then + Play("eat") + MakeSnakeLonger '4th Generation programming :) + Inc Score + End If + + UpdateSpeed 'Should snake go faster? + UpdateScoreBoard 'Write score & speed on screen + + If (SnakeHitTheWall()) Then SnakeDies 'G4 again ;) + + If (SnakeBitHimself()) Then SnakeDies 'So cool + + If (SnakeAteAllApples()) Then 'If you've eaten the 3 apples, throw some more apples + TimerApple_Timer() 'Call the timer manually + TimerApple.Enabled = False 'Timer is disabled and re-enabled to reset it's internal timer + TimerApple.Enabled = True + End If + +End + +Public Sub TimerApple_Timer() + + Dim I, J As Integer 'Throw 3 random apples all over the screen + Dim X, Y As Integer + + AppleXpos.Clear + AppleYpos.Clear + + For I = 0 To 2 + + ' Do not add an apple on the snake + Do + X = Int(Rnd(1, 30)) * 20 + Y = Int(Rnd(1, 20)) * 20 + + For J = 0 To Xpos.Max + If X = Xpos[J] And Y = Ypos[J] Then Break + Next + + If J = Xpos.Count Then Break + Loop + + AppleXpos.Add(X) + AppleYpos.Add(Y) + + Next + +End + +Public Sub MakeSnakeLonger() + + Xpos.add(Xpos[Tailpos[0]]) 'Add a new square to where the tail square is, to make the snake longer + Ypos.add(Ypos[Tailpos[0]]) + + TailPos.add(TailPos[0]) 'Remember when we said, tailpos keeps rotating. Now we have put an extra + TailPos.remove(0) 'Square, to make it move instead of the real tail, we make a back rotation (in + TailPos.add(Xpos.Length - 1, 0) 'opposite direction). First element => Last position, then index of new tail + 'gets added at location 0. Remember, TailPos[0] is the index of the tail!!! + +End + +Public Function SnakeHasEatenApple() As Boolean + + Dim RetVal As Boolean + Dim TmpAppleX As Integer + Dim TmpAppleY As Integer + Dim i As Integer + + RetVal = False + + For i = 0 To 2 + + TmpAppleX = AppleXpos[i] + TmpAppleY = AppleYpos[i] + If (Xpos[0] = (TmpAppleX) And Ypos[0] = (TmpAppleY)) Then 'Snake head on an apple? + AppleXpos[i] = -100 'Hide eaten apples + AppleYpos[i] = -100 + RetVal = True + End If + + Next + + + Return RetVal + +End + + +Public Function SnakeHitTheWall() As Boolean + + If (Xpos[0] < 0 Or Ypos[0] < 0 Or Xpos[0] >= 600 Or Ypos[0] >= 400) Then 'Duh + Return True + Else + Return False + End If + +End + +Public Sub SnakeDies() + + TmrEngine.Enabled = False + TimerApple.Enabled = False + Play("dead") + Message(("The snake's soul will always be remembered.\nGame Over!!\nYou scored: ") & score & ("\nPress Game -> New to play again.")) + +End + + +Public Sub MenuNew_Click() + + Dim i As Integer + + Head_Direction = "" 'Reset everything + Score = 0 + MenuGrandMa_Click() 'Reset speed + + InitSnake + TimerApple_Timer + + TmrEngine.Enabled = True + TimerApple.Enabled = True + +End + +Public Sub MenuPause_Click() + + If (MenuPause.Caption = ("&Pause") And (TmrEngine.Enabled = True)) Then + TmrEngine.Enabled = False + TimerApple.Enabled = False + MenuPause.Caption = ("Un&Pause") + Else If (MenuPause.Caption = ("Un&Pause") And (TmrEngine.Enabled = False)) Then + TmrEngine.Enabled = True + TimerApple.Enabled = True + MenuPause.Caption = ("&Pause") + End If + +End + +Public Sub MenuQuit_Click() + + FrmMain.Close + +End + +Public Sub MenuGrandMa_Click() + + TmrEngine.delay = 200 + TimerApple.delay = 8000 + Speed = ("GrandMa") + +End + + +Public Sub MenuCool_Click() + + TmrEngine.delay = 150 + TimerApple.delay = 5000 + Speed = ("Cool") + +End + + +Public Sub MenuSpeedFreak_Click() + + TmrEngine.delay = 100 + TimerApple.delay = 2500 + Speed = ("Speed Freak") + +End + +Public Sub MenuHowToPlay_Click() + + Dim Help As String + + Help = ("The Rules are simple:\n") + Help = Help & ("Eat as many apples as you can\n") + Help = Help & ("Do not Hit the walls, or bite yourself!\n\n") + Help = Help & ("Use the arrow keys to move the snake") + + Message(Help) + +End + + +Public Sub InitSnake() + + Dim i As Integer + + Xpos.Clear() + Ypos.Clear() + TailPos.Clear() + + For i = 0 To 3 + Xpos.add(0, i) + Ypos.add(0, i) + Next 'BM: Here i exists with value 4, shouldn't it be 3?! + + TailPos.add(3, 0) + TailPos.add(2, 1) + TailPos.add(1, 2) + +End + +Public Function SnakeAteAllApples() As Boolean + + 'IF ( AppleXpos[0] = AppleXpos[1] = AppleXpos[2] = -100) THEN 'BM: Doesn't work + If ((AppleXpos[0] = -100) And (AppleXpos[1] = -100) And (AppleXpos[2] = -100)) Then + Return True + Else + Return False + End If + +End + +Public Sub UpdateScoreBoard() + + LblScore.Text = ("Score: ") & Score + LblSpeed.Text = ("Speed: ") & Speed + +End + +Public Sub UpdateSpeed() + + If MenuAutoAdvanceSpeed.checked Then + If (Score > 19 And Speed = ("GrandMa")) Then MenuCool_Click + If (Score > 79 And Speed = ("Cool")) Then MenuSpeedFreak_Click 'Let's kill ur snake :) + End If + +End + +Public Sub MenuAutoAdvanceSpeed_Click() + + MenuAutoAdvanceSpeed.Checked = Not MenuAutoAdvanceSpeed.Checked 'Toggle menu checking + +End + +Public Function SnakeBitHimself() As Boolean + + Dim RetVal As Boolean + Dim TmpAppleX As Integer + Dim TmpAppleY As Integer + Dim i As Integer + + RetVal = False + + For i = 1 To (Xpos.Length - 1) + + TmpAppleX = Xpos[i] + TmpAppleY = Ypos[i] + If (Xpos[0] = (TmpAppleX) And Ypos[0] = (TmpAppleY) And Xpos[0] <> 0) Then 'Snake head on part of its body? + RetVal = True + End If + + Next + + Return RetVal + +End + +Public Sub MenuAbout_Click() + + Dim About As String + + About = ("Snake v1.0\nBy: Ahmad Kamal \nWritten for Gambas http://gambas.sf.net") + Message(About) + +End + +Public Sub Play(sound As String) +'I know it's slow, but I had to give it sound!!! +'Now I can safely move on to writing Quake17 ;) + +' EXEC [ "playwave", "/tmp/" & sound & ".wav" ] + Select Case sound + Case "start" + startsound.Play() + Case "eat" + eatsound.Play() + Case "dead" + deadsound.Play() + End Select +End + + +Public Sub dwg_KeyPress() + + Form_KeyPress + +End diff --git a/app/examples/Games/Snake/.src/FrmMain.form b/app/examples/Games/Snake/.src/FrmMain.form new file mode 100644 index 00000000..c506eab1 --- /dev/null +++ b/app/examples/Games/Snake/.src/FrmMain.form @@ -0,0 +1,89 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34.3333,27.3333,105.3333,81.6667) + Font = Font["Bold"] + Background = &H000000& + Foreground = &HFF0000& + Text = ("") + Icon = Picture["head.png"] + Resizable = False + { MenuGame Menu + Text = ("&Game") + { MenuNew Menu + Text = ("&New") + Shortcut = "Ctrl+N" + } + { MenuPause Menu + Text = ("&Pause") + Shortcut = "F3" + } + { MenuSep Menu + Text = ("") + } + { MenuQuit Menu + Text = ("&Quit") + Shortcut = "Ctrl+Q" + } + } + { MenuSpeed Menu + Text = ("&Speed") + { MenuGrandMa Menu + Text = ("&GrandMa") + } + { MenuCool Menu + Text = ("&Cool") + } + { MenuSpeedFreak Menu + Text = ("&Speed Freak") + } + { MenuAutoAdvanceSpeed Menu + Text = ("&Auto Advance Speed (Challenge ME)") + Checked = True + } + } + { MenuHelp Menu + Text = ("&Help") + { MenuHowToPlay Menu + Text = ("&How To Play") + Shortcut = "F1" + } + { Menu1 Menu + Text = ("") + } + { MenuAbout Menu + Text = ("&About") + } + } + { LblScore Label + MoveScaled(3,1,49.3333,6.6667) + Font = Font["16"] + Background = &H000000& + Foreground = &HFF0000& + Text = ("") + } + { LblSpeed Label + MoveScaled(54,1,49.3333,6.6667) + Font = Font["16"] + Background = &H000000& + Foreground = &HFF0000& + Text = ("") + } + { dwg DrawingArea + MoveScaled(3,8,100,66.6667) + Font = Font["14"] + Mouse = Mouse.Blank + Cached = True + Focus = True + } + { TmrEngine #Timer + #X = 0 + #Y = 432 + Delay = 100 + } + { TimerApple #Timer + #X = 592 + #Y = 432 + Delay = 10000 + } +} diff --git a/app/examples/Games/Snake/apple.png b/app/examples/Games/Snake/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..cef23a2c1546d91cc63572321f1533d24469d25f GIT binary patch literal 1141 zcmV-*1d98KP)W@%!HPTf4Ck8T+tRM`bCn!V- z6nZYRpwd5jqES?W5(Xj>N-~?y&`sUF+r8U;_&t74e_T`i!=Ur`IWIVe563hjym$N{ zjwhyWx_ehstGaVavzm18qS{=#c}+C**Gc|Qsji+xZU1?Hr5nf^uf$AIm?%V73Jg!I z*RQXBxZ&`t^0MtdwwU|AJxCX?{5SYOr&(F@qi&^0+lczWCsnB+p^p=WxS^{)Tyoaw zoiI^HgT>%V(6J)F^XxWx@goO{3pui4!j0e~Yim=5u(TzDE~{{CNILQnu}~@y3(G`z zpM_hJT)CK|@pc$TaXPQ`k?f@8!P&dkUHTxkRz^D?tbzdh-n~a3`r*W^Vp*CpS=y#w zvd%YVS}NP5Nb@3^c*m82+qU!X|4CfGqd&_-v2_BUA6%O@ZC3qvSQ1V zxy2t3$ijYqZ)=|BnIqI@#z<-xJshE}!DL$SOdQqb5rq-41k@t3C|?@!<~1((ZRf8) zRzZLTK~3V0Qv-)4i_DFI3PCF{Nyus;DMGAVB;MMH9?z0Y2wnwr1;i6+A31U4=$Cu% zzkCf`eg5*QjI~>1pcGoibzH}lR*tCDG!(5kL9=hmPL3fXcmc!$NE9F{gSe4QtIHR@ zG60XySgU5`PQKQFYSUZ#CZVW|KhO2SAo>z|K|;U@KrBcEIsj8vGDibA zpB$};!9Z_;oLp!C}(3W~jTh1xLq}sm8(z5P^yy=7G{+LQrK<^$x(~w)!AC zI~)oag+j!t;S0Q~Cl zl?$o})&(;L+6C)r_4&4?HoOLQtlFCIi^n%zgz-_V9m7fqOR0*fGAO@_i3>6cQUsHS zs00IUZS8^m-_PJ0xNXl%GXKqvqy6z%*Eu9t02u*Bs##NIP-8$AxCC~ry0Sk8m)*GO z{-Wi%v19aI2LZPC{45PU2YRoh5-Tq%Ih6xD3ThO{0i#t=^1vXtS@_#^RM(>qJ@fnG zMLTZ(BzGM6R6W+zRJTs+?HgcdGXxvK>w;i8c(Wnb4DU}(#;0%W+zlev|KZKhO;0~d zygRM_SoTKR^W6^LN_!GgIT-X3=BFtuJJ0QFfhBK!cEj910HYLlJcP&F00000NkvXX Hu0mjfGj0~# literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/body.png b/app/examples/Games/Snake/body.png new file mode 100644 index 0000000000000000000000000000000000000000..1baff801f471c73643bd9d2194766d5d84cff58c GIT binary patch literal 1138 zcmX|93s96*6#nl2FT2Y=1QCM)gnS^5q96<^ln}T(f+;Nsp@PO(L$S2vA`LHbcquSL zMkoo&64_vyqpmumi<%ngG=*cEC=W@%p&>vKBU&Ejp8j2{nRCB;&Ufa{Irp5eQWLvg zDf5s4P{u^7;)UDQcBH(3my<>#0PeVEV}e=;(Fz;{(t$MKJ}?Maz^sfB0|7?hN61O; zKn@&nCdH@UO+G*`_ewJH~F&9|4E_5BgRkglD{QNQaX_nT7bI{jfj*pO0O4Q0Td8U z;b9cEl$PQv;2QW2UN$E4dF~Tt4NIYvLMkkxB0cFov7qA2VKn!rp<`ipeV$eiHG;N%y{2k5{}x;a1t9n@hWla{o8@F6CxY zriL`WG@7 zE*X+=0~~~vWdmh4fw5zZJInD`c+o8p-^TN1o|zeL14Tds@HLo`v5O3sflQzpr~?{- z9GeENcqa>gMPzn->}H!{v8J%Dde^+9PHRh$sBtE(&blRj{MGiu!B6RYb?FtZ2$j$lmy2 z%ATm5@mU+RN6HpOSE-k-T^PLVHNVvX*PDzdOe&8>OEzzNdq>>X#O<4tllshE_xzVh zTn~MeuFDu3dNT5Kc+>FXF9P*7oJOM`KXkJF_|J8r8J32YKL-Q0n4%q*oOnI&kDBJT z&(CgY(f)O$JEQY-UlZq=V7RJpA6%Y%N!NF7cjwc!1(TymyzHB|8h_2|@Bi|2o0Csy z{*$T7gC40l+StZDBNO!(e!ugMDR+3h_SCl2NZq{Z2N`dk?K9;)w&=F6tvEd4$KUTw z-*mlTZ14W!`TnY^s_$Bzyfas=RF@g*TO2gG=bLLoHZ1AsdeGgrroU1whI(60@965| Pf=GZEb*$=4RBFjT&knM( literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/dead.wav b/app/examples/Games/Snake/dead.wav new file mode 100644 index 0000000000000000000000000000000000000000..f8d4292d0fb411872b8676e3ed8fd1938e03d40e GIT binary patch literal 10026 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 L|42+7neY4`JMe+U literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/eat.wav b/app/examples/Games/Snake/eat.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc8ff1e38449462023247971bd26d878c3060d6c GIT binary patch literal 1192 zcmeHG&uM5-wza#KGb~%o34a5}&PX%wy^+3e=6&B> zoyue$$N&oy^XcMO>&yZGEM{aLV0b|!vJ4D5Qa=ECf8>f4wjcqv*#AU^`#t)^E#_~n z3lR7P0~GS>c@PQ4=O7M0xFvK6-GOYuvpMj)cO|~@&5LgC=_@_5+G%vNr);%yCAR$^ zi#R(zmVDkA!4Ss!&nJ%ISxY4-H|X@5J^=fSWS55b72|nL<>TMIT$G9CsK9prQFEQg z1ET?3)f|QVdJr;q8~08F4`~4WN#ll~NW@yb=BMpY^~aA{v_4>AF#h_jq0s8}KCUbU z_m4v1de2pjwKuhqlw9cwHdWImI;w`Y4nz3Sfu4-kkM3TU_Fn0j_ohcBuFQ-{_fHVS z6=G7yC1sDj<7L#@yD~Z2Rs_K2fnrE5Ssg}p2=UrOZv;DPJ}y;!VDB9E5@~UXg2z?? z@rGhrDVeSH^B$0DL%(&w59DT_&5v=K)7N5cm4xfvN=*-2e|#H{Viw(f_}1uvy*OsI zpInOJjgNpJf^B^+u;pbD@p4!>h1_wOnA-Npnhq=^{S9MTdYG3Wdw z;z!I+rTl9Zk+psVxjurxOpUhBte$pMquA~lgQD3mAp>WTZIuNpH_{R$Cx;wC#n4ax zF7*q4ATR>^dy9j0#Y_dQ`c60ZW>XG@O()zrWfeRYkMp znPNbr2uQfi6B%4t9215T8c~2`8b3M=MKMX;nfD`L(N@hDrj$l;IC&N^%Js?FvzVB- z)5i|}e|)G8d^R;Zml0r7+067*>guFd#OB3|7tR~jc_S|B>Fj0kuOa#6@-kZ}Ef-7W af~d}B=7^}ymTrGtV3|_+i_ckX#DI5=O%5^u literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/head.png b/app/examples/Games/Snake/head.png new file mode 100644 index 0000000000000000000000000000000000000000..c6576d3fc8568e7ca518403fbbf437409b797b78 GIT binary patch literal 1197 zcmYjR3ozAL82_FB|2}f>&DG7svU@&HjcxN-kDFaAxo7XS)6C9%=XbvIeRIBZ&hPx@OY_?1K3iZU z05IFr!_`|e>ubj>R)cr_2Au(>*=x&AH_Z}f0Sbfzp+Ggz3}GiQ1~N?uCE8RGtiJ}2 z@)*h>5x4*em2fE+a%eFU`c$8#EF!?Cd`%G%6=^qy;y<)((E29LGxd9BumfnFacX8z zbM77B=NUg2`2Iss6Hl74P#ABZ3k-nFv z(X4+44~*A-3i*NX%6Ymix@K?9Tqax&s6Wr@lvS6=pInQ5P#ANeBEY@RT1>?_2t)&^ zAi0DsW_2Fw7<3!hJ}`}4A}A!JT__y4x2JMB&CjP%QI)$4w4`N#xz2EZ<%%AQ7FW*o zwdEgSkW5G}hock-YQ^GOi7->j(Xt%_l0}$&hNtAseK~i*OCzfX#x_brtMYkEo=8M0 z9cGw8EN~LY26Kzy{KDba@P*AnahF*4jjpI)#INGBZ&d4qBK;#@|QwO?z^dfruaLf+Bs6Xs8E zt{xK|-H;lR5)|&ROvQhgQs+~*zh*`a_Fn?lo-9FKH2n-z{r?W`*DiVBoBN)&;DCFVgp{C5TU z?Dp$wY-Qv2=`IIrRS2w>c~Kl%5#8JFiXk!l5pESEus$ zz}V%o@ge8N*5qTJiS5Ow&&+o|KB>6hQ<}7B&4%>!t-<$Xe^1tzBu8zn*V}vW$lia3 rrca0aKC?By8X0kLxV!h{QX@lDElMy8>071gKY*v(HrJmvhRFW~B!a^H literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/start.wav b/app/examples/Games/Snake/start.wav new file mode 100644 index 0000000000000000000000000000000000000000..a23faacb29c03708040e51ad597a5ac191f5c0da GIT binary patch literal 31810 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 LFFLto!{+}FrKSsa literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.directory b/app/examples/Games/Solitaire/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/Solitaire/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/Solitaire/.icon.png b/app/examples/Games/Solitaire/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fa870190609f8e8604fbe60579cb818f75902353 GIT binary patch literal 4209 zcmV-%5RUJOP)ntQ>!2m`96JiV?2oe!R8Fj{C)KN##(J82cLQ7{xnW>t= z@*hAL6*bWT9Z>@zEZUHmgwDQn(w*Lt^q$_{a+h=Fk6zN<>BJ-*5|eM$se13-d(XYU z&;9*=XMKXpOhs#7!5Ra`;+{B6_ofFXRiC`n2AzwF@7V-ER$7Qz{3k?|%&| z6xP6^=@~qHZzVG;GWgAYc7;IdJtf(K zXa9KtRb^>>^XX>R|7ieJm^2Z^Q_=GW@X)%h2doeeX=O{#^7)f-m+cW1eEzY!3u?~A z@R<*61|g9GKC--kFMqI%6ISuVUC`7s=X*6ULK~^9NvscxcJgvo;YcNxKCD^uIP`Y}_%;j}5CUNZ z%7n01;uPPEckJbJ2cV=W4Gib3rBlh;dPKZ*CJ1xBxCNwxH88I{#y37x%EIYsym1uX z-VN@kHy#8~d!U^|`?9tnX^U;{v*jf6lTEU4@S=Lf)8 z@B@491b{{5HtX$1@zOWfb*}o(I0*9|+=(@o{B(z}-CoSCOS5^q1D@UsaevUDVI6Jm zV@FLhF&XgPPfX*wIXOJB30~Mf_PkIu3|1h}SZmP%SYyFBh;aU2d|olpkN>j1!_F-_ zKLEo3548)<$BW(x8H4KvyzuzF`5zg7?DQUB&L054^!&+Pe}NS`*eY+I=)lAPg2DK^ zk|Zn>b_^y4fF+ou2mH0^bSdpr0FOK(Q=XwG@mCVS7f5jzc*IH`5n~J+17O9I?Y#2) zO`;*Qhj`-3ih+)P3431CfIIqkvEs3vPh`gQ-5{J1DoW-EuB;FM@B-U?@|*Q~0iC2o$`0r9Cv{EXrlp1&7Qa|cDq;a0D zl!02BB$9Db9YhJ7qr^;t~dBh@9Ehmm6mNGTKAkt(Ujd4RY;0E`LP zQvD2e)*NQext`XB8aj^dXW@H3erfEtV*xnQB(>5qq2xFK7oZPdOu)`9>kxD12CdYdKc5>~Y?Q$s=6<$31#aIAJ1xYQHOsH|m!Ja38og1I#_@QkC7W^g4$jZpZcrLNr ze3FN^vu|r71r^t@`Pbj4zT*IuOWu!^7q8PH#)W`XGNF|eiSZF&&m#pq|8F1V3!l1` zkNxmzUi$e@`Sve2;ZCbUMbap6pkMgBwfO~f%TAo~BGPiAXeBRdlRLhHr=&<|tz<%} zu}91~rhs^F2S5J7qdZhv!gKe3l9!%+hKd<;`0|s_bG$cBJg<=0)hj8uHa7Gm5_NDd zA+0ZJr)#{oX(ba{3KElf3qiac4*ZIZfAbYyTD+LKJw2og$)fpHEV^Ya+q!&|4KQwU zkgO1-I$EgAEF>CBzo-Dl+bIPJsg+7dDHv14>5C0~x;tAq+E!29o>!Qc+ro?$E64=W z3JYmzZR4^3cpNX1j^_uML_fw&j>KQ!Cb3O*6z7(qwR6UMUGOr_POVfzD`|U`6ar2* z{-?_UM{Bpy>v+UXiv7o$xqWs4rl5>xx3}=Xo@4yaQ%|z}w=W_p=JKPhjnwxfk>X_X zvDV@qZ(@Gge5RDmx_GaCVh<2X+Fq@+Y?A^ZM)iK|1UcZXnpdgF=%pu=$3P^XJRw;y zqllfINsNUjezcyC-u3|^H{QqggH0UW*+j~Kj-?X}^dXcc)!Il&(x+<4^{7iQ;v5aY zT7=TFO*_iAD=9~}5W<%>&3u2EL!@p{KQ;o~}-m4wF+@Oh#S_!a4JT z=x9&37U5vpwKTd_Y9pm!coI;UQO~#KmG#_n!whyL!*p7Qfu=oVUUfYKz1{S?KDn_> zw2}xRD7khuPyOOi-0ja(FEsgK#dog1muAB04c?Yrwe0+7Z$Ao;PH*g zqbC7a?fF50@22Q#-^=X9(^vOk^B;LS_a+QUnOhaGI~4P`EW&;&B=7?t6!#Y zy~x+rQ{g5!faH!3KERZUIg>ITV1M-s?BDh})+(Gx29fj}!fCmL({qWYEjPs+QpQcF}wAZLIMrEUh4|zneZML{}fAg%nexE?PlWehCZT zb@QYRA69+MMw0P9l#UWoVXO_X#>WOe#`o~u#2^T3@KSM7CPiQ8adJzikEusW#BhGM zV*n_T93~?SLP>#gLSz({;$&vhzx6FrZX6*5;o?e)5?x5GY4r@j;T@Qni^-Z+g-r_+ z_4_GSKKu7nGk?MI3j(0EsTMzxfQTUQU5xJ%crLz|M5YEn2m&t&SbW#T_;EU0k5N!Q zW2^@Zb%p?}AUO&E@!^I~Fi0sR6*e}7vYAy(?}DCW3Lzz}8=j-&nj6RnEV2Aj>Jq&~ zrj(L$U7AxaN(%Zi(b8rW~P)wVC6;hr5#7FszDr$FC+12}5&4dT2zb z7!HL8O$8sp0R>|xo*G@Roq@)nTvtM#32gsou zkUU8V)VNYUeGY4`T0+Q+Qks>Gj6^Y>OIBM0?~N*MyZKI*WM+`svl(j)VkkKY<>2nF zW?^wD%R&%7eiY#;_V3+_JJ5T|ea=+E;V3tM^ov}#>O&M1mEt&>AQ{I`4WOhYCohjh z*WbXqSKmd*2~Svu6eqn~%TcX>R5CF<77Qd}B6`_*h1_`48p`%<=T}WlgbK>&6_&R4 zW4wRmhhX-6rtjUxpAPJzO+|>7&%$B}&s#=ca)2BuSw4FnC6#k&sNIdy>YV2TLy;KE z*4)RuYj380_huSu_aK#G>gV^;z58!%Fp!!b9P&;FEeQ+Q_-;Q0>}v2@7*$6KCS_bH3JDxVXYPM zWT1>STyZ;O1V~|(6;cQxY?@;RU?>Upbafy0o$OFN<;!H!Rn}UrL;^wx6=1`$ur@ti zCk_vBK>+zbexvF2PA?XfCIqqhXDYDfl^==Pz>Y%Q7*H;)ty0x!SR1La11yEGy|zoV?fl9(P0KXKysSh zoB5CnIqBmkcY*1@k7gU<%Yg)_MPV;cx(=W_MTY<}^j8MODfJo@uS{;<81c=J{| zUVjXKp!FTI=C=F4$KKjrbSNDiiV=!tGp7=6x@jp|2?}ze-0_~N6JC2kb}J>Cs}X7i zdm1(W`g{xPt2^jwZ>4M7ll1QS#XDyBu%?!kbELHm9m*!0UWoBdP>^F00wN)OTI1mY zy8M(|o+ef_4XG80mhBvW`C&||?;SUOSj+EShf!ICLQzQ8vS7N)l+vj@{10o+c-;lt zuh#nFr!JbY6R9viov=<31WDFC^=a1rWE1)MlmBz|6|esXXo8T@et{|w00000NkvXX Hu0mjfc$N3r literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.lang/ca.po b/app/examples/Games/Solitaire/.lang/ca.po new file mode 100644 index 00000000..c9e2e690 --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/ca.po @@ -0,0 +1,96 @@ +# Catalan translation of Solitaire +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Solitaire package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Solitaire\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 22:40+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FGameArea.form:48 +msgid "&About" +msgstr "&Quant a" + +#: FBoardSelect.form:21 +msgid "Board Layout" +msgstr "Format del tauler" + +#: FBoardSelect.form:31 +msgid "Cancel" +msgstr "Canceŀla" + +#: FGameArea.form:25 +msgid "&File" +msgstr "&Fitxer" + +#: FGameArea.form:37 +msgid "&Game" +msgstr "J&oc" + +#: FGameArea.form:45 +msgid "&Help" +msgstr "&Ajuda" + +#: FGameArea.form:28 +msgid "&New Game" +msgstr "&Joc nou" + +#: FGameArea.form:57 +msgid "New Game" +msgstr "Joc nou" + +#: FBoardSelect.form:26 +msgid "OK" +msgstr "D'acord" + +#: FGameArea.form:87 +msgid "Quit" +msgstr "Surt" + +#: FGameArea.form:32 +msgid "&Quit" +msgstr "&Surt" + +#: FGameArea.form:80 +msgid "Redo" +msgstr "Refés" + +#: FGameArea.form:65 +msgid "Restart" +msgstr "torna a iniciar" + +#: FBoardSelect.form:11 +msgid "Select board" +msgstr "Seleccioneu tauler" + +#: FGameArea.form:40 +msgid "&Select Board" +msgstr "&Seleccioneu tauler" + +#: FGameArea.form:20 +msgid "Solitaire" +msgstr "Solitari" + +#: FGameArea.class:259 +msgid "Solitaire v0.3\nBy: Grahame White \nWritten for Gambas http://gambas.sf.net" +msgstr "Solitari v0.3\nPer: Grahame White \nEscrit per al Gambas http://gambas.sf.net" + +#: .project:1 +msgid "Some sort of solitaire game" +msgstr "Una mena de joc del solitari" + +#: FGameArea.form:72 +msgid "Undo" +msgstr "Desfés" + diff --git a/app/examples/Games/Solitaire/.lang/cs.po b/app/examples/Games/Solitaire/.lang/cs.po new file mode 100644 index 00000000..24468c0b --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/cs.po @@ -0,0 +1,94 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Some sort of solitaire game" +msgstr "Nějaké solitaire hry" + +#: FBoardSelect.form:11 +msgid "Select board" +msgstr "Vyber hru" + +#: FBoardSelect.form:21 +msgid "Board Layout" +msgstr "Rozložení hry" + +#: FBoardSelect.form:26 +msgid "OK" +msgstr "-" + +#: FBoardSelect.form:31 +msgid "Cancel" +msgstr "ZruÅ¡it" + +#: FGameArea.class:259 +msgid "" +"Solitaire v0.3\n" +"By: Grahame White \n" +"Written for Gambas http://gambas.sf.net" +msgstr "" +"Solitaire v0.3\n" +"Od: Grahame White \n" +"Napsaný pro Gambas http://gambas.sf.net" + +#: FGameArea.form:20 +msgid "Solitaire" +msgstr "-" + +#: FGameArea.form:25 +msgid "&File" +msgstr "&Soubor" + +#: FGameArea.form:28 +msgid "&New Game" +msgstr "&Nová hra" + +#: FGameArea.form:32 +msgid "&Quit" +msgstr "&Ukončit" + +#: FGameArea.form:37 +msgid "&Game" +msgstr "&Hra" + +#: FGameArea.form:40 +msgid "&Select Board" +msgstr "&Vyber hru" + +#: FGameArea.form:45 +msgid "&Help" +msgstr "&Nápověda" + +#: FGameArea.form:48 +msgid "&About" +msgstr "&O aplikaci" + +#: FGameArea.form:57 +msgid "New Game" +msgstr "Nová hra" + +#: FGameArea.form:65 +msgid "Restart" +msgstr "-" + +#: FGameArea.form:72 +msgid "Undo" +msgstr "Vpřed" + +#: FGameArea.form:80 +msgid "Redo" +msgstr "Zpět" + +#: FGameArea.form:87 +msgid "Quit" +msgstr "Ukončit" diff --git a/app/examples/Games/Solitaire/.lang/de.po b/app/examples/Games/Solitaire/.lang/de.po new file mode 100644 index 00000000..1ccd7676 --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/de.po @@ -0,0 +1,89 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Some sort of solitaire game" +msgstr "Eine Solitär-Variante" + +#: FBoardSelect.form:11 +msgid "Select board" +msgstr "Spielfeld auswählen" + +#: FBoardSelect.form:21 +msgid "Board Layout" +msgstr "Spielfeld" + +#: FBoardSelect.form:26 +msgid "OK" +msgstr "-" + +#: FBoardSelect.form:31 +msgid "Cancel" +msgstr "Abbrechen" + +#: FGameArea.class:259 +msgid "Solitaire v0.3\nBy: Grahame White \nWritten for Gambas http://gambas.sf.net" +msgstr "Solitaire v0.3\nVon: Grahame White \nGeschrieben für Gambas http://gambas.sf.net" + +#: FGameArea.form:20 +msgid "Solitaire" +msgstr "-" + +#: FGameArea.form:25 +msgid "&File" +msgstr "&Datei" + +#: FGameArea.form:28 +msgid "&New Game" +msgstr "&Neues Spiel" + +#: FGameArea.form:32 +msgid "&Quit" +msgstr "&Beenden" + +#: FGameArea.form:37 +msgid "&Game" +msgstr "&Spiel" + +#: FGameArea.form:40 +msgid "&Select Board" +msgstr "&Spielfeld auswählen" + +#: FGameArea.form:45 +msgid "&Help" +msgstr "&Hilfe" + +#: FGameArea.form:48 +msgid "&About" +msgstr "&Über" + +#: FGameArea.form:57 +msgid "New Game" +msgstr "Neues Spiel" + +#: FGameArea.form:65 +msgid "Restart" +msgstr "Neu starten" + +#: FGameArea.form:72 +msgid "Undo" +msgstr "Rückgängig" + +#: FGameArea.form:80 +msgid "Redo" +msgstr "Wiederherstellen" + +#: FGameArea.form:87 +msgid "Quit" +msgstr "Beenden" + diff --git a/app/examples/Games/Solitaire/.lang/es.po b/app/examples/Games/Solitaire/.lang/es.po new file mode 100644 index 00000000..bc88c09b --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/es.po @@ -0,0 +1,79 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FBoardSelect.class:50 +msgid "Select board" +msgstr "Seleccionar tablero" + +#: FBoardSelect.class:60 +msgid "Board Layout" +msgstr "Tablero de diseño" + +#: FBoardSelect.class:65 +msgid "OK" +msgstr "OK" + +#: FBoardSelect.class:70 +msgid "Cancel" +msgstr "Cancelar" + +#: FGameArea.class:392 +msgid "Solitaire" +msgstr "&Seleccionar tablero" + +#: FGameArea.class:397 +msgid "&File" +msgstr "&Archivo" + +#: FGameArea.class:400 +msgid "&New Game" +msgstr "&Nuevo juego" + +#: FGameArea.class:404 +msgid "&Quit" +msgstr "&Salir" + +#: FGameArea.class:409 +msgid "&Game" +msgstr "&Juego" + +#: FGameArea.class:412 +msgid "&Select Board" +msgstr "&Seleccionar tablero" + +#: FGameArea.class:417 +msgid "&Help" +msgstr "&Ayuda" + +#: FGameArea.class:420 +msgid "&About" +msgstr "&Acerca de" + +#: FGameArea.class:429 +msgid "New Game" +msgstr "Nuevo juego" + +#: FGameArea.class:437 +msgid "Restart" +msgstr "Reiniciar" + +#: FGameArea.class:444 +msgid "Undo" +msgstr "Deshacer" + +#: FGameArea.class:452 +msgid "Redo" +msgstr "Rehacer" + +#: FGameArea.class:459 +msgid "Quit" +msgstr "Salir" diff --git a/app/examples/Games/Solitaire/.lang/ru.po b/app/examples/Games/Solitaire/.lang/ru.po new file mode 100644 index 00000000..ae7b85ea --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/ru.po @@ -0,0 +1,116 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Games/Solitaire/.project:19 +msgid "Some sort of solitaire game" +msgstr "Своего рода игра в пасьянс" + +#: app/examples/Games/Solitaire/.project:20 app/examples/Games/Solitaire/.src/FGameArea.form:5 +msgid "Solitaire" +msgstr "Пасьянс" + +#: app/examples/Games/Solitaire/.src/FBoardSelect.form:5 +msgid "Select board" +msgstr "Выбрать доску" + +#: app/examples/Games/Solitaire/.src/FBoardSelect.form:13 +msgid "Board Layout" +msgstr "Макет доски" + +#: app/examples/Games/Solitaire/.src/FBoardSelect.form:17 +msgid "OK" +msgstr "ОК" + +#: app/examples/Games/Solitaire/.src/FBoardSelect.form:21 +msgid "Cancel" +msgstr "Отмена" + +#: app/examples/Games/Solitaire/.src/FGameArea.class:259 +msgid "" +"Solitaire v0.3\n" +"By: Grahame White \n" +"Written for Gambas http://gambas.sf.net" +msgstr "" +"Пасьянс версии 0.3\n" +"От: Грэхем Уайт \n" +"Написан для Gambas http://gambas.sf.net" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:9 +msgid "&File" +msgstr "Файл" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:11 +msgid "&New Game" +msgstr "Новая игра" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:14 +msgid "&Quit" +msgstr "Выход" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:18 +msgid "&Game" +msgstr "Игра" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:20 +msgid "&Select Board" +msgstr "Выбрать доску" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:24 +msgid "&Help" +msgstr "Справка" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:26 +msgid "&About" +msgstr "О программе" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:33 +msgid "New Game" +msgstr "Новая игра" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:40 +msgid "Restart" +msgstr "Перезапустить" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:46 +msgid "Undo" +msgstr "Откатить" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:53 +msgid "Redo" +msgstr "Вернуть" + +#: app/examples/Games/Solitaire/.src/FGameArea.form:59 +msgid "Quit" +msgstr "Выход" + +#: app/examples/Games/Solitaire/.src/MBoards.module:27 +msgid "Original" +msgstr "Оригинал" + +#: app/examples/Games/Solitaire/.src/MBoards.module:49 +msgid "Plus" +msgstr "Плюс" + diff --git a/app/examples/Games/Solitaire/.project b/app/examples/Games/Solitaire/.project new file mode 100644 index 00000000..f9f4bf26 --- /dev/null +++ b/app/examples/Games/Solitaire/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Some sort of solitaire game +Description="Solitaire" +Startup=FGameArea +Icon=ball.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Games/Solitaire/Solitaire +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Games/Solitaire/.src/CBoardDesign.class b/app/examples/Games/Solitaire/.src/CBoardDesign.class new file mode 100644 index 00000000..3e2c55ef --- /dev/null +++ b/app/examples/Games/Solitaire/.src/CBoardDesign.class @@ -0,0 +1,9 @@ +' Gambas class file + +' each row is stored as a number between 0-1023 (inc) +' each column numbered in 2^ and the row is the sum of the active columns +' leftmost column is 512, rightmost is 1 +Public Name As String ' Board desidn name +Public Row As New Byte[] ' Board layout +Public Placed As New Byte[] ' The cells with balls on them +Public Finish As Byte ' The cell that the last ball must be on diff --git a/app/examples/Games/Solitaire/.src/CMove.class b/app/examples/Games/Solitaire/.src/CMove.class new file mode 100644 index 00000000..6698dd82 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/CMove.class @@ -0,0 +1,5 @@ +' Gambas class file + +Public Source As Byte ' Start cell +Public Target As Byte ' Finish cell +Public Captured As Byte ' Ball taken diff --git a/app/examples/Games/Solitaire/.src/FBoardSelect.class b/app/examples/Games/Solitaire/.src/FBoardSelect.class new file mode 100644 index 00000000..adc3e845 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FBoardSelect.class @@ -0,0 +1,35 @@ +' Gambas class file + +Public Sub cmdCancel_Click() + + FBoardSelect.Close + +End + +Public Sub Form_Show() + + Dim i As Byte + + ' Fill in the combo box text section + cboLayout.Text = Global.boarddesign[Global.selectedlayout].Name + + ' Fill in the combo box dropdown section + For i = 0 To Global.boarddesign.Count - 1 + cboLayout.Add(Global.boarddesign[i].Name) + Next + +End + + +Public Sub cmdOK_Click() + + ' Set board layout index + Global.selectedlayout = cboLayout.Index + + ' Reset board + FGameArea.reset_board() + + ' Close this window + FBoardSelect.Close + +End diff --git a/app/examples/Games/Solitaire/.src/FBoardSelect.form b/app/examples/Games/Solitaire/.src/FBoardSelect.form new file mode 100644 index 00000000..3d2c9094 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FBoardSelect.form @@ -0,0 +1,23 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(42.6667,7.5,40,11) + Text = ("Select board") + Resizable = False + { cboLayout ComboBox + MoveScaled(16,1,23,4) + Text = (" ") + } + { lblLayout TextLabel + MoveScaled(1,1,14,4) + Text = ("Board Layout") + } + { cmdOK Button + MoveScaled(1,6,11,4) + Text = ("OK") + } + { cmdCancel Button + MoveScaled(28,6,11,4) + Text = ("Cancel") + } +} diff --git a/app/examples/Games/Solitaire/.src/FGameArea.class b/app/examples/Games/Solitaire/.src/FGameArea.class new file mode 100644 index 00000000..c2312114 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FGameArea.class @@ -0,0 +1,368 @@ +' Gambas class file + +Public board As New Object[] + +Public Sub Form_Open() + + Dim i As Byte + Dim j As Byte + + Global.POW2 = [64, 32, 16, 8, 4, 2, 1] + Global.Ball = Picture.Load("ball.png") + + board.Resize(49) + + 'FGameArea.Border = window.Resizable + FGameArea.Resize((8 * 2) + (48 * 7), (8 * 2) + (48 * 8) + panToolBar.Height) + 'FGameArea.Border = window.Fixed + Global.selectedlayout = 0 + + ' Create master geometry (7 x 7 grid) + For i = 0 To 6 + For j = 0 To 6 + Wait + board[(i * 7) + j] = New PictureBox(Me) As "GameBoard" + board[(i * 7) + j].Alignment = Align.Center + board[(i * 7) + j].Border = Border.Plain + board[(i * 7) + j].height = 48 + board[(i * 7) + j].width = 48 + board[(i * 7) + j].x = 8 + (48 * j) + board[(i * 7) + j].y = 8 + (48 * i) + panToolBar.Height + Next + Next + + ' Create board layouts + MBoards.make_boards() + MBoards.fill_boards() + + reset_board() + +End + +Private Sub set_row(row_value As Byte, row_no As Byte) + + Dim i As Byte + + ' Just to be sure + If row_value > 127 Then row_value = 127 + + For i = 0 To 6 + If row_value >= global.POW2[i] Then + board[(row_no * 7) + i].visible = True + board[(row_no * 7) + i].border = border.Raised + row_value = row_value - global.POW2[i] + board[(row_no * 7) + i].Tag = (row_no * 7) + i + Else + board[(row_no * 7) + i].visible = False + Endif + Next + +End + +Private Sub place_balls(row_value As Byte, row_no As Byte) + + Dim i As Byte + + ' Just to be sure + If row_value > 127 Then row_value = 127 + + For i = 0 To 6 + If row_value >= global.POW2[i] Then + If board[(row_no * 7) + i].visible = True Then + board[(row_no * 7) + i].picture = Global.Ball + row_value = row_value - global.POW2[i] + Global.BallCount = Global.BallCount + 1 + Endif + Else + board[(row_no * 7) + i].picture = Null + Endif + Next + +End + +Public Sub GameBoard_MouseUp() + + ' If not enough balls left then return + If Global.BallCount <= 1 Then Return + ' If a ball is not already selected then select it + If Last.Picture = global.Ball Then + global.Selected = True + If global.ClickedBall <> Null Then + board[global.ClickedBall].background = 15658726 + Endif + global.ClickedBall = CByte(Last.Tag) + Last.background = &HFF0000& + Else + If global.Selected = True Then + try_take(global.ClickedBall, CByte(Last.tag)) + Endif + Endif + +End + +Private Sub try_take(source_cell As Byte, target_cell As Byte) + + Dim current_row As Byte + Dim target_row As Byte + Dim current_col As Byte + Dim target_col As Byte + Dim i As Byte + + + If board[target_cell].visible = False Then Return + + current_row = source_cell \ 7 + target_row = target_cell \ 7 + + ' Are both cells on the same row + If current_row = target_row Then + ' (YES) Check that they are close enough and have a ball in between + ' Check to the right + If source_cell + 2 < 49 Then + If source_cell + 2 = target_cell Then + ' In range + If board[source_cell + 1].picture = global.Ball Then + ' Ah good! A move can be made + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell + 1) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].picture = Null + board[source_cell].background = 15658726 + board[source_cell + 1].picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + + ' Then to the left + If source_cell - 2 >= 0 Then + If source_cell - 2 = target_cell Then + ' In range + If board[source_cell - 1].picture = global.Ball Then + ' Ah good! A move can be made + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell - 1) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].picture = Null + board[source_cell].background = 15658726 + board[source_cell - 1].picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + Endif + + ' Ok, so not on the same row ... how about the same column? + current_col = source_cell Mod 7 + target_col = target_cell Mod 7 + + If current_col = target_col Then + If source_cell + 14 < 49 Then + If source_cell + 14 = target_cell Then + If board[source_cell + 7].picture = global.Ball Then + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell + 7) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].Picture = Null + board[source_cell].background = 15658726 + board[source_cell + 7].Picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + + If source_cell - 14 >= 0 Then + If source_cell - 14 = target_cell Then + If board[source_cell - 7].picture = global.Ball Then + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell - 7) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].picture = Null + board[source_cell].background = 15658726 + board[source_cell - 7].picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + Endif + +End + +Public Sub mnuQuit_Click() + + Me.Close + +End + +Public Sub mnuNew_Click() + + reset_board() + +End + +Public Sub mnuAbout_Click() + + Dim About As String + + About = ("Solitaire v0.3\nBy: Grahame White \nWritten for Gambas http://gambas.sf.net") + Message(About) + +End + +Public Sub tbtnQuit_Click() + + Me.Close + +End + +Public Sub tbtnNewGame_Click() + + reset_board() + +End + +Public Sub reset_board() + + Dim j As Byte + + ' Clear ball count + Global.BallCount = 0 + + ' Display the board layout + For j = 0 To 6 + set_row(Global.boarddesign[Global.selectedlayout].Row[j], j) + place_balls(Global.boarddesign[Global.selectedlayout].Placed[j], j) + Next + + Global.totalballs = Global.ballcount + + ' Make sure there is enough room for all the moves (number of balls, which leaves 1 extra just in case) + Global.gamemove.Resize(Global.ballcount) + + ' Reset the move recorder + For j = 0 To Global.ballcount - 1 + Global.gamemove[j] = New CMove + set_move(j, 0, 0, 0) + Next + + ' Disable Undo/Redo buttons + tbtnUndo.Enabled = False + tbtnRedo.Enabled = False + +End + +Private Sub set_move(movenumber As Byte, source As Byte, target As Byte, capture As Byte) + + With Global.gamemove[movenumber] + .Source = Source + .Target = Target + .Captured = capture + End With + +End + +Private Sub undo_move(movenumber As Byte) + + tbtnUndo.Enabled = False + + ' Put balls in correct places + board[Global.gamemove[movenumber].target].Picture = Null + board[Global.gamemove[movenumber].captured].picture = Global.ball + board[Global.gamemove[movenumber].source].picture = Global.ball + + ' update ball counter + global.ballcount = global.ballcount + 1 + + If global.ballcount < global.totalballs Then tbtnUndo.Enabled = True + tbtnRedo.Enabled = True + +End + +Public Sub tbtnUndo_Click() + + undo_move((global.totalballs - global.ballcount) - 1) + +End + +Private Sub redo_move(movenumber As Byte) + + tbtnRedo.Enabled = False + + ' Put balls in correct places + board[Global.gamemove[movenumber].target].picture = Global.ball + board[Global.gamemove[movenumber].captured].Picture = Null + board[Global.gamemove[movenumber].source].Picture = Null + + ' Update ball counter + global.ballcount = global.ballcount - 1 + + If movenumber + 1 < global.totalballs Then + If global.gamemove[movenumber + 1].target <> global.gamemove[movenumber + 1].source Then tbtnRedo.Enabled = True + Endif + tbtnUndo.Enabled = True + +End + +Public Sub tbtnRedo_Click() + + redo_move(global.totalballs - global.ballcount) + +End + +Public Sub mnuBoardSelect_Click() + + FBoardSelect.ShowModal + +End diff --git a/app/examples/Games/Solitaire/.src/FGameArea.form b/app/examples/Games/Solitaire/.src/FGameArea.form new file mode 100644 index 00000000..0af8fb6f --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FGameArea.form @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(57.5714,37.1429,50,58) + Text = ("Solitaire") + Icon = Picture["ball.png"] + Resizable = False + { mnuFile Menu + Text = ("&File") + { mnuNew Menu + Text = ("&New Game") + } + { mnuQuit Menu + Text = ("&Quit") + } + } + { mnuGame Menu + Text = ("&Game") + { mnuBoardSelect Menu + Text = ("&Select Board") + } + } + { mnuHelp Menu + Text = ("&Help") + { mnuAbout Menu + Text = ("&About") + } + } + { panToolBar Panel + MoveScaled(0,0,82.6667,4) + { tbtnNewGame ToolButton + MoveScaled(0,0,4,4) + ToolTip = ("New Game") + Text = ("") + Picture = Picture["new.png"] + } + { tbtnRestartGame ToolButton + MoveScaled(4,0,4,4) + Enabled = False + ToolTip = ("Restart") + Text = ("") + } + { tbtnUndo ToolButton + MoveScaled(4,0,4,4) + Enabled = False + ToolTip = ("Undo") + Text = ("") + Picture = Picture["undo.png"] + } + { tbtnRedo ToolButton + MoveScaled(8,0,4,4) + Enabled = False + ToolTip = ("Redo") + Text = ("") + Picture = Picture["redo.png"] + } + { tbtnQuit ToolButton + MoveScaled(12,0,4,4) + ToolTip = ("Quit") + Text = ("") + Picture = Picture["quit.png"] + } + } +} diff --git a/app/examples/Games/Solitaire/.src/Global.class b/app/examples/Games/Solitaire/.src/Global.class new file mode 100644 index 00000000..4c3d6ca2 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/Global.class @@ -0,0 +1,11 @@ +' Gambas class file + +Static Public boarddesign As New Object[] ' The game grid +Static Public POW2 As Integer[] ' Array for display algo +Static Public ball As Picture ' The ball graphic +Static Public selected As Boolean ' Is a ball selected? +Static Public clickedball As Byte ' Which ball, ignored if above is false +Static Public ballcount As Byte ' The number of balls in play +Static Public totalballs As Byte ' The number of balls at the start +Static Public gamemove As New Object[] ' Stores the moves made to allow for undoing +Static Public selectedlayout As Byte ' The active board layout diff --git a/app/examples/Games/Solitaire/.src/MBoards.module b/app/examples/Games/Solitaire/.src/MBoards.module new file mode 100644 index 00000000..ec7e8ebb --- /dev/null +++ b/app/examples/Games/Solitaire/.src/MBoards.module @@ -0,0 +1,69 @@ +' Gambas module file + +Public Sub make_boards() + + Dim i As Byte + Dim j As Byte + + ' Create the board design objects + + Global.boarddesign.Resize(2) + + For i = 0 To Global.boarddesign.Count - 1 + Global.boarddesign[i] = New CBoardDesign + For j = 1 To 7 + Global.boarddesign[i].Row.Add(0) + Global.boarddesign[i].Placed.Add(0) + Next + Next + +End + +Public Sub fill_boards() + + ' Fill the board design Array + + With Global.boarddesign[0] + .Name = ("Original") + + .Row[0] = 28 + .Row[1] = 28 + .Row[2] = 127 + .Row[3] = 127 + .Row[4] = 127 + .Row[5] = 28 + .Row[6] = 28 + + .Placed[0] = 28 + .Placed[1] = 28 + .Placed[2] = 127 + .Placed[3] = 119 + .Placed[4] = 127 + .Placed[5] = 28 + .Placed[6] = 28 + + .Finish = 28 + End With + + With Global.boarddesign[1] + .Name = ("Plus") + + .Row[0] = 28 + .Row[1] = 28 + .Row[2] = 127 + .Row[3] = 127 + .Row[4] = 127 + .Row[5] = 28 + .Row[6] = 28 + + .Placed[0] = 0 + .Placed[1] = 8 + .Placed[2] = 8 + .Placed[3] = 62 + .Placed[4] = 8 + .Placed[5] = 8 + .Placed[6] = 0 + + .Finish = 28 + End With +End diff --git a/app/examples/Games/Solitaire/ball.png b/app/examples/Games/Solitaire/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..5e5e042e2f7b73ca288b0cbbe68961df1796dd3c GIT binary patch literal 3327 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 VZ-apY?{=W+44$rjF6*2UngFwdJ>38R literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/redo.png b/app/examples/Games/Solitaire/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..72b4ce5cc8cdaa52f3740cfde3b48acbfdd4d9c9 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|uJ`WUYi?|2U})Abigp7E zGL{7S1v5B2yO9RuXn49fhHzX@PB_3S5zfeI*2c=kBf}%Z-M}K;@gRyp4;#Cuxa@2uUG{2v!Lei3nK-7lsZF#x6qx1IvWV a%nW69%#vXnX3PibVeoYIb6Mw<&;$Sq{2`wJ literal 0 HcmV?d00001 diff --git a/app/examples/Games/StarField/.directory b/app/examples/Games/StarField/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/StarField/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/StarField/.icon.png b/app/examples/Games/StarField/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..236e2ed08cdf5d55458da6809196a99a16380280 GIT binary patch literal 3140 zcmV-K47>A*P)=h(Q=m1hIJ^E=OX-!|REu1WywAdK9Uo3)Ph^|pLCFFXJIV1Z$9!!e5Wjo@7zu1Q5~54cxln*d9#Q#%Wx)R>ao}r&Yydps6pzXTViyW%iItrP_`&371t~4oK9sSpuz% zczf5=xTPytvf>Upn-9>_ew3R(`J2-sfU*+>C@7aR+9+QuJy8I;dI7ABIQaH+$cohz zPQ1^6s$i(Pj6_nSMPGIQSAq*Hz(l_Bl+G$M@y50AfVQUn3|5vRVSsBn!`XVv(&Za* zdmB+&2(lSWB85`dPYgGSnHTD;(OTs$umE>;77zwG&VA5~S6>g#;cfXW1MMfdz0%{% z!e2p)=49i~Xjr$6RC&$y6JXN%uC>h>(c&9XlYV$*8=-@*KFPV0jYJN@4Vx%W&%zc0 zN-C?!o!ZZleP^kz-^iYqzDHBfF&1t3H0nC8I5MdK7HIK}5%I6Zy8ps=zseu}?tOgi zAD?9Bzx*?Q{rrCuEL@IGq^R~_NJ4h+c@fig5bvhBq$-jat2(!gvb8DYeB~+aD5a_Q!xkpA8_+Pj)Kedsk-RkpGC?z<@i zQZ+TSb$0RC-#tztQA#0KG>$RVQvC<=18meb6uoR2Zyhd%3?w@ExbrF|pfGFlMF z`2v8u#H_TRXe8qm$l5$dJ6gGaSv9tLKF{oL9;9Lu<=dWhY{H51#>Wecz&I3k<AtKb?elJh_>!Sqw zU!X}0RdLPV-0>T<1_5)sJJ|a9-=7xZMHoTc`C6X}t#q0&mF!Fu@-l9T7~2b-IdOmm zHyPeK)yDhe@KR-jnO>ry0YWdquH$D}xzr;W^BB$!)7Rd@_bZn0!$Z5+Ah4sXiY{lE z(|m$$4?aAt4`=U&}K%RrVy zRV^l!rhotIl&{)Ernj3f)yLVBE2U}IE7WX#fb3uf^@|M(vhR2s*cls zhQypY@;T_fxlc!__IEH9@wYq24oe*#bcrBC#?uy&dF+-{BM6 zew|JmCsVVL(uJGRCWiK67wyW(c?)pjvgZZR_+!6PpdF;k%Q$>E!-*&U0bMqi{?-l% z1B`;ZHr>nroH@!`105yhgl;6&)G~5Xq~!yjS+(^XIQ}l7XcjiCLFp?id}R8&3DYYf zy%JFh;~XEf?j`AU(B9Lef?cD*#Z2P4s_*Ql;=^bC21ubXzk&a73+ENg=e{~ z&`0xuecb<kJpB{qRF_gwwUEqkj+b89LG7o$h+4dfTbFfmps5{~ zXaL;kOBEqzH1D?d;H~-u!#%D1(ZhejzrFM_3kDi!Sh98s5(q}0f3@#Yc>!oB&jQ(g-Rr`g@E#|W_~rP`P@BUWJ6h+{GmNK zYmw2qzi5x({axHVuZ~SIh@Wdm3Yw#b_Yn+dE_lvI-9(MV03{=w$1Ff;1pWn))r*81ZkY8dLCHX|$>jOx8{Ox@!&+;Ask1eA!jN{NWL(wG~D zQ5P8K{oqs>D~o0Gk;?h`(Uk25Q~-1c@shZ<1HGA3#j65T{rHWuZ^YA;iz8QDXm3aX z&|(Yu!M;CiyR>qBr5R5h-2mv>l;Eh7~as-*d e3}!Hc8T=n6Y29`>ozrOm0000 screen.Width Then + Stars.x = 0 + Stars.y = Int(Rnd(screen.Height)) + Stars.speed = Int(Rnd(1, 4)) + Endif + Next + + ' Attention la couleur affecte aussi les images ! + Draw.ForeColor = &hFFFFFF& + + Draw.Font.Size = Font.DefaultFontSize * 2 + Draw.Text(Screen.Framerate, 10, Screen.Height - 40) + + ' on applique la rotation et on affiche le logo + Draw.Rotate(rotation) + Draw.Image(img, Screen.Width \ 2 - 64, Screen.Height \ 2 - 64) + + ' rotation de 0 a 360 :-) + rotation = (rotation + 1) Mod 360 + +End diff --git a/app/examples/Games/StarField/enterprise.png b/app/examples/Games/StarField/enterprise.png new file mode 100644 index 0000000000000000000000000000000000000000..e76709e4227732091a19a2b225498f8413006efb GIT binary patch literal 4623 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/ImageViewer/.directory b/app/examples/Image/ImageViewer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Image/ImageViewer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Image/ImageViewer/.icon.png b/app/examples/Image/ImageViewer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ca93bd80534e39fec653bd33b959a86998f0765 GIT binary patch literal 4419 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 JPDHLkV1lkhc-8;_ literal 0 HcmV?d00001 diff --git a/app/examples/Image/ImageViewer/.lang/ca.po b/app/examples/Image/ImageViewer/.lang/ca.po new file mode 100644 index 00000000..4073799a --- /dev/null +++ b/app/examples/Image/ImageViewer/.lang/ca.po @@ -0,0 +1,64 @@ +# Catalan translation of ImageViewer +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the ImageViewer package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: ImageViewer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:07+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FViewer.form:48 +msgid "&?" +msgstr "-" + +#: FViewer.form:51 +msgid "&About..." +msgstr "&Quant a..." + +#: FViewer.class:101 +msgid "A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)" +msgstr "Un exemple senzill de visualitzador d'imatges per\n Benoit Minisini (gambas@users.souceforge.net)" + +#: FViewer.form:40 +msgid "&Draw" +msgstr "&Dibuixa" + +#: FViewer.form:23 +msgid "&File" +msgstr "&Fitxer" + +#: .project:1 +msgid "Image viewer example" +msgstr "Exemple de visor d'imatges" + +#: FViewer.form:26 +msgid "&Open..." +msgstr "&Obre..." + +#: FViewer.form:34 +msgid "&Quit" +msgstr "&Surt" + +#: FViewer.form:18 +msgid "Simple Image Viewer" +msgstr "Visor d'imatges senzill" + +#: FViewer.form:43 +msgid "&Start" +msgstr "&Inicia" + +#: FViewer.class:51 +msgid "&Stop" +msgstr "&Atura" + diff --git a/app/examples/Image/ImageViewer/.lang/cs.po b/app/examples/Image/ImageViewer/.lang/cs.po new file mode 100644 index 00000000..6b8d73b6 --- /dev/null +++ b/app/examples/Image/ImageViewer/.lang/cs.po @@ -0,0 +1,60 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Image viewer example" +msgstr "Příklad prohlížeče obrázků" + +#: FViewer.class:53 +msgid "&Stop" +msgstr "-" + +#: FViewer.class:103 +msgid "" +"A simple image viewer example by\n" +"Benoit Minisini (gambas@users.sourceforge.net)" +msgstr "" +"Jednoduchý například prohlížeče obrázků od\n" +"Benoit Minisini (gambas@users.sourceforge.net)" + +#: FViewer.form:19 +msgid "Simple Image Viewer" +msgstr "Ukázka prohlížeče obrázků" + +#: FViewer.form:24 +msgid "&File" +msgstr "&Soubor" + +#: FViewer.form:27 +msgid "&Open..." +msgstr "&Otevřít..." + +#: FViewer.form:35 +msgid "&Quit" +msgstr "&Ukončit" + +#: FViewer.form:41 +msgid "&Draw" +msgstr "&Kresli" + +#: FViewer.form:44 +msgid "&Start" +msgstr "-" + +#: FViewer.form:49 +msgid "&?" +msgstr "-" + +#: FViewer.form:52 +msgid "&About..." +msgstr "&O programu..." diff --git a/app/examples/Image/ImageViewer/.lang/de.po b/app/examples/Image/ImageViewer/.lang/de.po new file mode 100644 index 00000000..5e24c275 --- /dev/null +++ b/app/examples/Image/ImageViewer/.lang/de.po @@ -0,0 +1,57 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Image viewer example" +msgstr "Beispiel für einen Bildbetrachter" + +#: FViewer.class:53 +msgid "&Stop" +msgstr "&Stopp" + +#: FViewer.class:103 +msgid "A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)" +msgstr "Ein einfaches Bildbetrachter-Beispiel von\nBenoit Minisini (gambas@users.sourceforge.net)" + +#: FViewer.form:19 +msgid "Simple Image Viewer" +msgstr "Einfacher Bildbetrachter" + +#: FViewer.form:24 +msgid "&File" +msgstr "&Datei" + +#: FViewer.form:27 +msgid "&Open..." +msgstr "&Öffnen..." + +#: FViewer.form:35 +msgid "&Quit" +msgstr "&Beenden" + +#: FViewer.form:41 +msgid "&Draw" +msgstr "&Zeichnen" + +#: FViewer.form:44 +msgid "&Start" +msgstr "-" + +#: FViewer.form:49 +msgid "&?" +msgstr "-" + +#: FViewer.form:52 +msgid "&About..." +msgstr "&Über..." + diff --git a/app/examples/Image/ImageViewer/.lang/es.po b/app/examples/Image/ImageViewer/.lang/es.po new file mode 100644 index 00000000..cce89879 --- /dev/null +++ b/app/examples/Image/ImageViewer/.lang/es.po @@ -0,0 +1,43 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FViewer.class:147 +msgid "Simple Image Viewer" +msgstr "Visor de imágenes simple" + +#: FViewer.class:151 +msgid "&File" +msgstr "&Archivo" + +#: FViewer.class:154 +msgid "&Open..." +msgstr "&Abrir..." + +#: FViewer.class:163 +msgid "&Quit" +msgstr "&Salir" + +#: FViewer.class:169 +msgid "&Draw" +msgstr "&Dibujar" + +#: FViewer.class:172 +msgid "&Start" +msgstr "&Inicio" + +#: FViewer.class:177 +msgid "&?" +msgstr "&?" + +#: FViewer.class:180 +msgid "&About..." +msgstr "&Acerca de..." diff --git a/app/examples/Image/ImageViewer/.lang/nl.po b/app/examples/Image/ImageViewer/.lang/nl.po new file mode 100644 index 00000000..2d106e7f --- /dev/null +++ b/app/examples/Image/ImageViewer/.lang/nl.po @@ -0,0 +1,55 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: ImageViewer 3.5.90\n" +"PO-Revision-Date: 2014-09-25 21:29 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Image viewer example" +msgstr "Image viewer voorbeeld" + +#: FViewer.class:53 +msgid "&Stop" +msgstr "&Stop" + +#: FViewer.class:103 +msgid "A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)" +msgstr "Een eenvoudig Image Viewer voorbeeld door\nBenoît Minisini (gambas@users.sourceforge.net)" + +#: FViewer.form:19 +msgid "Simple Image Viewer" +msgstr "Eenvoudige Image Viewer" + +#: FViewer.form:24 +msgid "&File" +msgstr "&Bestand" + +#: FViewer.form:27 +msgid "&Open..." +msgstr "&Open..." + +#: FViewer.form:35 +msgid "&Quit" +msgstr "&Afsluiten" + +#: FViewer.form:41 +msgid "&Draw" +msgstr "&Teken" + +#: FViewer.form:44 +msgid "&Start" +msgstr "&Start" + +#: FViewer.form:49 +msgid "&?" +msgstr "-" + +#: FViewer.form:52 +msgid "&About..." +msgstr "&Over..." + diff --git a/app/examples/Image/ImageViewer/.lang/ru.po b/app/examples/Image/ImageViewer/.lang/ru.po new file mode 100644 index 00000000..2eaee01e --- /dev/null +++ b/app/examples/Image/ImageViewer/.lang/ru.po @@ -0,0 +1,82 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-30 09:26+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Image/ImageViewer/.project:22 +msgid "Image viewer example" +msgstr "Пример просмотрщика изображений" + +#: app/examples/Image/ImageViewer/.project:23 +msgid "A simple image viewer example" +msgstr "Простой пример просмотрщика изображений" + +#: app/examples/Image/ImageViewer/.src/FViewer.class:14 +msgid "Picture files" +msgstr "Файлы изображений" + +#: app/examples/Image/ImageViewer/.src/FViewer.class:53 +msgid "&Stop" +msgstr "Стоп" + +#: app/examples/Image/ImageViewer/.src/FViewer.class:55 app/examples/Image/ImageViewer/.src/FViewer.form:24 +msgid "&Start" +msgstr "Начать" + +#: app/examples/Image/ImageViewer/.src/FViewer.class:103 +msgid "" +"A simple image viewer example by\n" +"Benoit Minisini (gambas@users.sourceforge.net)" +msgstr "" +"Простой пример просмотрщика изображений\n" +"от Бенуа Минисини (gambas@users.sourceforge.net)" + +#: app/examples/Image/ImageViewer/.src/FViewer.form:5 +msgid "Simple Image Viewer" +msgstr "Простой просмотрщик изображений" + +#: app/examples/Image/ImageViewer/.src/FViewer.form:9 +msgid "&File" +msgstr "Файл" + +#: app/examples/Image/ImageViewer/.src/FViewer.form:11 +msgid "&Open..." +msgstr "Открыть..." + +#: app/examples/Image/ImageViewer/.src/FViewer.form:17 +msgid "&Quit" +msgstr "Выход" + +#: app/examples/Image/ImageViewer/.src/FViewer.form:22 +msgid "&Draw" +msgstr "Рисовать" + +#: app/examples/Image/ImageViewer/.src/FViewer.form:28 +msgid "&?" +msgstr "&?" + +#: app/examples/Image/ImageViewer/.src/FViewer.form:30 +msgid "&About..." +msgstr "О программе..." + diff --git a/app/examples/Image/ImageViewer/.project b/app/examples/Image/ImageViewer/.project new file mode 100644 index 00000000..96d71cdb --- /dev/null +++ b/app/examples/Image/ImageViewer/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Image viewer example +Description="A simple image viewer example" +Startup=FViewer +Icon=image.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.form.dialog +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Image/ImageViewer/.src/FViewer.class b/app/examples/Image/ImageViewer/.src/FViewer.class new file mode 100644 index 00000000..78362945 --- /dev/null +++ b/app/examples/Image/ImageViewer/.src/FViewer.class @@ -0,0 +1,106 @@ +' Gambas class file + +Public Sub mnuOpen_Click() + + Dim hImage As Image + + Dialog.Path = "/usr/share/wallpapers" + If Not Exist(Dialog.Path) Then + Dialog.Path = User.Home + Endif + + 'Dialog.Path = Application.Path + + Dialog.Filter = ["*.jpg;*.jpeg;*.png;*.bmp", ("Picture files")] + + If Dialog.OpenFile() Then Return + + hImage = Image.Load(Dialog.Path) + + dwgImage.Clear() + dwgImage.Resize(hImage.Width, hImage.Height) + + Draw.Begin(dwgImage) + Draw.FillRect(0, 0, hImage.Width, hImage.Height, Color.Gray) + Draw.Image(hImage, 0, 0) + Draw.End + + dwgImage.Visible = True + +Catch + + Message.Warning(Error.Text & " !") + +End + +Public Sub mnuQuit_Click() + + Me.Close + +End + +'PUBLIC SUB dwgImage_Draw() +' +' IF hPict THEN Draw.Picture(0, 0, hPict) +' +'END + +Public Sub mnuStart_Click() + + timDraw.Enabled = Not timDraw.Enabled + + If timDraw.Enabled Then + mnuStart.Text = ("&Stop") + Else + mnuStart.Text = ("&Start") + Endif + +End + +Public Sub timDraw_Timer() + + Dim iInd As Integer + Dim X1 As Float + Dim Y1 As Float + Dim X2 As Float + Dim Y2 As Float + Dim W As Float + Dim H As Float + Dim eTime As Float + + Paint.Begin(dwgImage) + + Paint.LineWidth = 1 + + For iInd = 1 To 16 + + Paint.Brush = Paint.Color(Color.RGB(Int(Rnd(256)), Int(Rnd(256)), Int(Rnd(256)), 128)) + + X1 = Rnd(dwgImage.Width) + Y1 = Rnd(dwgImage.Height) + W = Rnd(16, 32) + H = Rnd(16, 32) + + Paint.Rectangle(X1, Y1, W, H) + Paint.Fill() + + Paint.Rotate(Pi(0.125)) + + 'Paint.Brush = Paint.Color(Color.Black) + 'Paint.Stroke + + Next + + Paint.End + + 'dwgImage.Refresh + +End + + +Public Sub mnuAbout_Click() + + Message.Info(("A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)")) + +End + diff --git a/app/examples/Image/ImageViewer/.src/FViewer.form b/app/examples/Image/ImageViewer/.src/FViewer.form new file mode 100644 index 00000000..91b3c7a5 --- /dev/null +++ b/app/examples/Image/ImageViewer/.src/FViewer.form @@ -0,0 +1,52 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34.4286,25.4286,106,81) + Text = ("Simple Image Viewer") + Icon = Picture["image.png"] + Arrangement = Arrange.Vertical + { mnuFile Menu + Text = ("&File") + { mnuOpen Menu + Text = ("&Open...") + Shortcut = "Ctrl+O" + } + { Menu1 Menu + } + { mnuQuit Menu + Text = ("&Quit") + Shortcut = "Ctrl+Q" + } + } + { Menu3 Menu + Text = ("&Draw") + { mnuStart Menu + Text = ("&Start") + } + } + { Menu2 Menu + Text = ("&?") + { mnuAbout Menu + Text = ("&About...") + } + } + { Separator1 Separator + MoveScaled(42,1,33,0) + } + { svwImage ScrollView + MoveScaled(1,4,52,46) + Background = Color.LightForeground + Expand = True + Border = False + { dwgImage DrawingArea + MoveScaled(0,0,29,27) + Visible = False + Background = &H00AA7F& + Cached = True + } + } + { timDraw #Timer + #MoveScaled(51,10) + Delay = 50 + } +} diff --git a/app/examples/Image/ImageViewer/image.png b/app/examples/Image/ImageViewer/image.png new file mode 100644 index 0000000000000000000000000000000000000000..a415746af95a1a1ce9ea0520657bfd2b61dc19d9 GIT binary patch literal 8428 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ǵhm?bQk81p5xpaeDS7vuB&!c3*?TGZ*o$H{SpT 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 O0000mpL{Qv*oQPxBdD5C7? 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 Y12T8Jaaw$ZcK`qY07*qoM6N<$f>!i~0{{R3 literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/.lang/ca.po b/app/examples/Image/Lighttable/.lang/ca.po new file mode 100644 index 00000000..c3c49747 --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/ca.po @@ -0,0 +1,337 @@ +# Catalan translation of Lighttable +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Lighttable package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Lighttable\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-01-01 02:59+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Lighttable" +msgstr "-" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Una eina per a ordenar les fotografies" + +#: FHelp.form:11 +msgid "Help" +msgstr "Ajuda" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Tanca" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "No hi ha informació Exif en aquest fitxer." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Menys" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Informacions de la imatge" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Més" + +#: FMain.class:33 +msgid "Lighttable - " +msgstr "-" + +#: FMain.class:47 +msgid "Right-click for Main Menu" +msgstr "Menú principal amb un clic del botó dret" + +#: FMain.class:48 +msgid " pictures" +msgstr "Imatge" + +#: FMain.class:49 +msgid "sorted alphabetically" +msgstr "Ordenat alfabèticament" + +#: FMain.class:75 +msgid "Right-click on the background or on a frame for menus." +msgstr "Menú amb un clic del botó dret al fons o a un marc" + +#: FMain.class:95 +msgid "Right-click for Picture Menu" +msgstr "Menú de la imatge amb un clic del botó dret" + +#: FMain.class:174 +msgid "&Rename all files..." +msgstr "Canvia de nom tots els &fitxers..." + +#: FMain.class:269 +msgid "Loading picture..." +msgstr "Carregant imatges..." + +#: FMain.class:344 +msgid "sorted chronologically" +msgstr "Ordenat cronològicament" + +#: FMain.class:384 +msgid "The file &1 already exists in the current directory!" +msgstr "El fitxer &1 ja existeix al directori actual!" + +#: FMain.class:436 FRename.form:32 FRenameAll.form:80 +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Canceŀla" + +#: FMain.class:436 FRename.form:26 FRenameAll.form:74 FStart.form:20 +#: FTime.form:45 +msgid "&OK" +msgstr "D'ac&ord" + +#: FMain.class:436 +msgid "The file &1 will be deleted." +msgstr "El fitxer &1 serà esborrat." + +#: FMain.class:729 +msgid "Loading of pictures is being aborted..." +msgstr "La càrrega d'imatges s'està canceŀlant..." + +#: FMain.class:780 +msgid "Files are being renamed..." +msgstr "S'està canviant el nom dels fitxers..." + +#: FMain.class:809 +msgid "&1 files renamed" +msgstr "&1 fitxers han canviat de nom" + +#: FMain.class:825 +msgid "Setting time informations in all files..." +msgstr "Establint la informació de l'hora a tots els fitxers..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Menú principal" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "Ordenat &alfabèticament" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "Ordenat &cronològicament" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "Presentació de &diapositives" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "Atura &la Càrrega" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "Correcció de &temps..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Obre la carpeta..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Ajuda" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Surt" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Menú de la imatge" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Mou la imatge: arrossega el marc amb el ratolí)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "&Visualitza la imatge (clic a la imatge)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Vista de pantalla completa" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Tanca la vista" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Propera imatge" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "Imatge &anterior" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "&Informacions de la imatge" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "Canvia de nom la &imatge" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "&Suprimeix la imatge" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "El nom de fitxer antic i el nou són idèntics." + +#: FRename.form:11 +msgid "Rename" +msgstr "Canvi de nom" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Nom de fitxer nou:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "LaMevaImatge.JPG" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Canvia de nom totes les imatges" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "Manté el nom original del fitxer com a sufix" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Format del nombre" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "dígits" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Prefix" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Valor inicial" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Aquesta funció canviarà el nom de tots els fitxers del directori i afegirà un número de serie al nom dels fitxers.
    Les imatges es gestionaran en el mateix ordre en que es mostren ara, en files d'esquerra a dreta, files de dalt a baix." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Opcions" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Exemple:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Conflictes de fitxers" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "Continua de totes m&aneres" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "No es pot canviar el nom d'aquests fitxers perquè els noms de fitxer de destí ja existeixen al directori.
    Si continueu, només els fitxers sense conflicte canviaran de nom.
    Si canceŀleu, podreu triar noves opcions de nom de fitxer." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pausa entre imatges:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "seg" + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "Atura" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "La carpeta &1 no conté cap fitxer jp(e)g." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Lighttable - Trieu una carpeta d'imatges " + +#: FStart.form:41 +msgid "New selection" +msgstr "Nova selecció" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Últimes seleccions" + +#: FTime.class:21 +msgid "There is no time correction selected." +msgstr "No hi ha correcció de temps seleccionat." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Correcció de temps" + +#: FTime.form:39 +msgid "earlier" +msgstr "previ" + +#: FTime.form:39 +msgid "later" +msgstr "posterior" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Aquesta funció corregirà les informacions de data/hora dins de la secció Exif de tots els fitxers .jp(e)g de la carpeta.
    Es farà una còpia de seguretat dels fitxers amb noms de fitxers com «LaMevaImatge.jpg_original».
    Si no hi ha informacions Exif de data/hora dins el fitxer, no es farà res." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Estableix l'hora original de totes les imatges de" + +#: FTime.form:70 +msgid "day(s)" +msgstr "dia(es)" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "Hora(es)" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "minut(s)" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Per poder executar aquest programa, l'exiftool ha d'estar instaŀlat." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Per poder executar aquest programa, el convertidor ha d'estar instaŀlat." diff --git a/app/examples/Image/Lighttable/.lang/cs.po b/app/examples/Image/Lighttable/.lang/cs.po new file mode 100644 index 00000000..9b909c49 --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/cs.po @@ -0,0 +1,330 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Lighttable" +msgstr "-" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Nástroj na třídění fotografií" + +#: FHelp.form:11 +msgid "Help" +msgstr "Nápověda" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Zavři" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "O tomto souboru nejsou Exif informace." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Méně" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Informace o obrázku" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Více" + +#: FMain.class:32 +msgid "Lighttable - " +msgstr "-" + +#: FMain.class:46 +msgid "Right-click for Main Menu" +msgstr "Klikni pravým pro menu" + +#: FMain.class:47 +msgid " pictures" +msgstr " obrázek" + +#: FMain.class:48 +msgid "sorted alphabetically" +msgstr "seřadit abecedně" + +#: FMain.class:74 +msgid "Right-click on the background or on a frame for menus." +msgstr "Klikni pravým na pozadí a nebo rám pro menu." + +#: FMain.class:94 +msgid "Right-click for Picture Menu" +msgstr "Klikni pravým pro menu obrázku" + +#: FMain.class:173 +msgid "&Rename all files..." +msgstr "&Přejmenovat všechny soubory..." + +#: FMain.class:268 +msgid "Loading picture..." +msgstr "Načítání obrázku..." + +#: FMain.class:343 +msgid "sorted chronologically" +msgstr "seřadit chronologicky" + +#: FMain.class:383 +msgid "The file &1 already exists in the current directory!" +msgstr "Soubor &1 již existuje v aktuální složce!" + +#: FMain.class:435 FRename.form:32 FRenameAll.form:80 +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Zrušit" + +#: FMain.class:435 FRename.form:26 FRenameAll.form:74 FStart.form:20 +#: FTime.form:45 +msgid "&OK" +msgstr "-" + +#: FMain.class:435 +msgid "The file &1 will be deleted." +msgstr "Soubor &1 bude smazán." + +#: FMain.class:727 +msgid "Loading of pictures is being aborted..." +msgstr "Načítání obrázků bylo přerušeno..." + +#: FMain.class:778 +msgid "Files are being renamed..." +msgstr "Soubor byl přejmenován..." + +#: FMain.class:807 +msgid "&1 files renamed" +msgstr "Soubor &1 přejmenován" + +#: FMain.class:823 +msgid "Setting time informations in all files..." +msgstr "Nastavení časové informace ve všech souborech..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Hlavní menu" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "Seřadit &abecedně" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "Seřadit &chronologicky" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "&Promítání" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "Přerušit &načítání" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "&Korekce času..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Otevřít složku..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Nápověda" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Ukončit" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Menu obrázku" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Přesun obrázku: uchop s myší rám)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "&Zobrazení obrázku (klikni na obrázek)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Plný náhled obrázku" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Zavřít pohled" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Další obrázek" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "&Předchozí obrázek" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "&Informace o obrázku" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "&Přejmenovat obrázek" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "&Smazat obrázek" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "Starý a nový název je identický." + +#: FRename.form:11 +msgid "Rename" +msgstr "Přejmenovat" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Nový název souboru:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "-" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Přejmenovat všechy obrázky" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "Nechat originální název jako příponu" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Číselný formát" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "číslice" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Předpona" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Startovní hodnota" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Tato funkce přejmenuje všechny .jp(e)g soubory v adresáři a přidat sériová čísla do názvu souborů.
    Fotografie budou zpracovány v pořadí, v jakém jsou právě teď zobrazeny, v řádcích zleva doprava, řádky od shora dolů." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Možnosti" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Příklad:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Konflikt souboru" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "Pokračuj &stejně" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "Následující soubory nelze přejmenovat, protože cílový souborů již v adresáři existuje.
    Budete-li pokračovat, budou přejmenovány pouze soubory bez konfliktů.
    Pokud zrušíte, můžete zvolit možnost nového jména souboru." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pauza mezi obrázky:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "-" + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "-" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "Složka &1 neobsahuje žádný soubor typu jp(e)g." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Lighttable - Výběr složky obrázků" + +#: FStart.form:41 +msgid "New selection" +msgstr "Nový výběr" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Poslední výběry" + +#: FTime.class:18 +msgid "There is no time correction selected." +msgstr "Není vybrán čas korekce." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Korekce času" + +#: FTime.form:39 +msgid "earlier" +msgstr "dříve" + +#: FTime.form:39 +msgid "later" +msgstr "později" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Tato funkce opraví všechny informace o datu/času v sekci EXIF všechny. jp(e)g soubory ve složce.
    Záloha souborů s názvy souborů bude jako 'MyPicture.jpg_original'.
    Pokud není EXIF informace o datu/času v souboru, nebude dělat nic." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Nastav původní čas všech obrázků na" + +#: FTime.form:70 +msgid "day(s)" +msgstr "dní" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "hodin" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "minut" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Aby program běžel, musí být nainstalován exiftool." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Aby program běžel, musí být nainstalován convert." diff --git a/app/examples/Image/Lighttable/.lang/de.po b/app/examples/Image/Lighttable/.lang/de.po new file mode 100644 index 00000000..a30ab99d --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/de.po @@ -0,0 +1,330 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Lighttable" +msgstr "Leuchttisch" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Fotos sortieren" + +#: FHelp.form:11 +msgid "Help" +msgstr "Hilfe" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Schließen" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "Diese Datei entält keine Exif-Informationen." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Weniger" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Bildinformationen" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Mehr" + +#: FMain.class:33 +msgid "Lighttable - " +msgstr "Leuchttisch - " + +#: FMain.class:47 +msgid "Right-click for Main Menu" +msgstr "Rechtsklick für das Hauptmenü" + +#: FMain.class:48 +msgid " pictures" +msgstr " Bilder" + +#: FMain.class:49 +msgid "sorted alphabetically" +msgstr "alphabetisch sortiert" + +#: FMain.class:75 +msgid "Right-click on the background or on a frame for menus." +msgstr "Menüs: auf den Hintergrund oder auf einen Rahmen rechtsklicken." + +#: FMain.class:95 +msgid "Right-click for Picture Menu" +msgstr "Rechtsklick für das Bildmenü" + +#: FMain.class:174 +msgid "&Rename all files..." +msgstr "Alle Dateien &umbenennen..." + +#: FMain.class:269 +msgid "Loading picture..." +msgstr "Bild wird geladen..." + +#: FMain.class:344 +msgid "sorted chronologically" +msgstr "chronologisch sortiert" + +#: FMain.class:384 +msgid "The file &1 already exists in the current directory!" +msgstr "Die Datei &1 existiert im aktuellen Verzeichnis bereits!" + +#: FMain.class:436 FRename.form:32 FRenameAll.form:80 +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Abbrechen" + +#: FMain.class:436 FRename.form:26 FRenameAll.form:74 FStart.form:20 +#: FTime.form:45 +msgid "&OK" +msgstr "-" + +#: FMain.class:436 +msgid "The file &1 will be deleted." +msgstr "Die Datei &1 wird gelöscht." + +#: FMain.class:729 +msgid "Loading of pictures is being aborted..." +msgstr "Das Laden der Bilder wird abgebrochen..." + +#: FMain.class:780 +msgid "Files are being renamed..." +msgstr "Dateien werden umbenannt..." + +#: FMain.class:809 +msgid "&1 files renamed" +msgstr "&1 Dateien umbenannt" + +#: FMain.class:825 +msgid "Setting time informations in all files..." +msgstr "Die Zeitinformationen in allen Dateien werden geändert..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Hauptmenü" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "&Alphabetisch sortiert" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "&Chronologisch sortiert" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "Dia&show" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "&Laden abbrechen" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "&Zeitkorrektur..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Ordner öffnen..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Hilfe" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Beenden" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Bildermenü" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Bild verschieben: Rahmen mit Maus ziehen)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "Vergrößerte &Ansicht (Bild anklicken)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Vollbildansicht" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Ansicht schließen" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Nächstes Bild" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "Vor&heriges Bild" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "&Bildinformationen" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "Bild &umbenennen" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "Bild &löschen" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "Alter und neuer Dateiname sind gleich." + +#: FRename.form:11 +msgid "Rename" +msgstr "Umbenennen" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Neuer Dateiname:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "MeinBild.JPG" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Alle Bilder umbenennen" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "originalen Dateinamen als Suffix behalten" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Zahlenformat" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "Stellen" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Präfix" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Startwert" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Diese Funktion benennt alle .jp(e)g-Dateien im Verzeichnis um und fügt Seriennummern zu den Dateinamen hinzu.
    Die Dateien werden in der Reihenfolge bearbeitet, in der sie jetzt gezeigt werden, die Reihen von oben nach unten, in der Reihe von links nach rechts." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Optionen" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Beispiel:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Dateikonflikte" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "&Trotzdem fortfahren" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "Folgende Dateien können nicht umbenannt werden, weil die Zielnamen im Verzeichnis bereits existieren.
    Wenn Sie fortfahren, werden nur die Dateien ohne Konflikt umbenannt.
    Wenn Sie abbrechen, können Sie neue Optionen für die Dateinamen einstellen." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pause zwischen Bildern:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "Sek." + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "-" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "Das Verzeichnis &1 enthält keine jp(e)g-Dateien." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Leuchttisch - Bilderverzeichnis auswählen" + +#: FStart.form:41 +msgid "New selection" +msgstr "Neue Auswahl" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Zuletzt ausgewählt" + +#: FTime.class:21 +msgid "There is no time correction selected." +msgstr "Es ist keine Zeitkorrektur gesetzt." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Zeitkorrektur" + +#: FTime.form:39 +msgid "earlier" +msgstr "früher" + +#: FTime.form:39 +msgid "later" +msgstr "später" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Diese Funktion korrigiert alle Datums-/Zeitinformationen im EXIF-Abschnitt aller .jp(e)g-Dateien im Verzeichnis.
    Es werden Sicherungskopien mit Namen wie 'MeinBild.jpg_original' erstellt.
    Falls eine Datei keine Zeitinformationen enthält, wird nichts unternommen." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Die Originalzeit aller Bilder ändern auf" + +#: FTime.form:70 +msgid "day(s)" +msgstr "Tag(e)" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "Stunde(n)" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "Minute(n)" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Für dieses Programm muss exiftool installiert sein." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Für dieses Programm muss convert installiert sein." diff --git a/app/examples/Image/Lighttable/.lang/en.po b/app/examples/Image/Lighttable/.lang/en.po new file mode 100644 index 00000000..9ebff4fb --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/en.po @@ -0,0 +1,264 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.class:40 +msgid "Lighttable" +msgstr "" + +#: FHelp.class:30 +msgid "Help" +msgstr "" + +#: FHelp.class:38 +msgid "&Close" +msgstr "" + +#: FMain.class:67 +msgid "Choose picture folder" +msgstr "" + +#: FMain.class:76 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "" + +#: FMain.class:80 +#, fuzzy +msgid "Lighttable - " +msgstr "Lighttable - " + +#: FMain.class:81 +msgid " pictures" +msgstr "" + +#: FMain.class:82 +msgid "sorted alphabetically" +msgstr "" + +#: FMain.class:100 +msgid "To see the menus, right-click on the background or on a frame." +msgstr "" + +#: FMain.class:189 +msgid "&Rename all..." +msgstr "" + +#: FMain.class:332 +msgid "sorted chronologically" +msgstr "" + +#: FMain.class:389 +msgid "This filename already exists in the current directory !" +msgstr "" + +#: FMain.class:421 FRename.class:71 FRenameAll.class:69 +msgid "&Cancel" +msgstr "" + +#: FMain.class:421 FRename.class:65 FRenameAll.class:76 +#, fuzzy +msgid "&OK" +msgstr "-" + +#: FMain.class:421 +msgid "The file &1 will be deleted." +msgstr "" + +#: FMain.class:787 +msgid "Main menu" +msgstr "" + +#: FMain.class:792 +msgid "Sorted &alphabetically" +msgstr "" + +#: FMain.class:799 +msgid "Sorted &chronologically" +msgstr "" + +#: FMain.class:805 +msgid "Abort &Loading" +msgstr "" + +#: FMain.class:810 +msgid "&Help" +msgstr "" + +#: FMain.class:816 +msgid "&Quit" +msgstr "" + +#: FMain.class:823 +msgid "Frames menu" +msgstr "" + +#: FMain.class:827 +msgid "(Move picture: drag frame with mouse)" +msgstr "" + +#: FMain.class:832 +msgid "&View picture (click on picture)" +msgstr "" + +#: FMain.class:838 +msgid "&Full Screen View" +msgstr "" + +#: FMain.class:844 +msgid "&Close view" +msgstr "" + +#: FMain.class:850 +msgid "&Next picture" +msgstr "" + +#: FMain.class:856 +msgid "&Previous picture" +msgstr "" + +#: FMain.class:862 +#, fuzzy +msgid "Picture &informations" +msgstr "Picture informations" + +#: FMain.class:868 +msgid "&Rename picture" +msgstr "" + +#: FMain.class:874 +msgid "&Delete picture" +msgstr "" + +#: FRename.class:43 +msgid "Rename" +msgstr "" + +#: FRename.class:50 +msgid "New filename:" +msgstr "" + +#: FRename.class:56 +msgid "lblHidden" +msgstr "" + +#: FRenameAll.class:65 +msgid "Rename all pictures" +msgstr "" + +#: FRenameAll.class:83 +msgid "This function will number serially all the pictures.
    The pictures will be handled in the order they are
    shown right now, in rows from left to right,
    rows from top to bottom." +msgstr "" + +#: FRenameAll.class:94 +msgid "&Add number, keep the filename" +msgstr "" + +#: FRenameAll.class:100 +msgid "&Replace filename with number" +msgstr "" + +#: FRenameAll.class:111 +msgid "Number format:" +msgstr "" + +#: FRenameAll.class:116 +msgid "digits" +msgstr "" + +#: FRenameAll.class:123 +msgid "Options" +msgstr "" + +#: FRenameAll.class:129 +msgid "TextBox1" +msgstr "" + +#: FRenameAll.class:136 +msgid "Example:" +msgstr "" + +#~ msgid "sortiert nach Dateinamen" +#~ msgstr "Sorted by file names" + +#~ msgid "sortiert nach Aufnahmedatum und -zeit" +#~ msgstr "Sorted by picture date/time" + +#~ msgid "abbrechen" +#~ msgstr "Cancel" + +#~ msgid "Ansicht schließen" +#~ msgstr "Close current view" + +#~ msgid "beenden" +#~ msgstr "Quit" + +#~ msgid " Bilder" +#~ msgstr " pictures" + +#~ msgid "Bilderverzeichnis auswählen" +#~ msgstr "Choose picture folder" + +#~ msgid "Bild löschen" +#~ msgstr "Delete picture" + +#~ msgid "Bild umbenennen" +#~ msgstr "Rename picture" + +#~ msgid "(Bild verschieben: Rahmen mit der Maus ziehen)" +#~ msgstr "(Move picture: drag frame with mouse)" + +#~ msgid "Das Verzeichnis &1 enthält keine jp(e)g-Dateien." +#~ msgstr "The folder &1 doesn't contain .jp(e)g files." + +#~ msgid "Die Datei &1 wird gelöscht." +#~ msgstr "The file &1 will be deleted." + +#~ msgid "Dieser Dateiname existiert im aktuellen Verzeichnis schon!" +#~ msgstr "This filename already exists in the current directory!" + +#~ msgid "Für die Menüs mit der rechten Maustaste überall klicken!" +#~ msgstr "Just right-click everywhere to get the menus!" + +#~ msgid "Größeres Bild (Bild anklicken)" +#~ msgstr "View picture (click picture)" + +#~ msgid "Hauptmenü" +#~ msgstr "-" + +#~ msgid "Hilfe" +#~ msgstr "Help" + +#~ msgid "Label1" +#~ msgstr "-" + +#~ msgid "Leuchttisch" +#~ msgstr "Lighttable" + +#~ msgid "Nächstes Bild" +#~ msgstr "Next picture" + +#~ msgid "neuer Dateiname:" +#~ msgstr "New filename:" + +#~ msgid "Rahmenmenü" +#~ msgstr "-" + +#~ msgid "schließen" +#~ msgstr "Close" + +#~ msgid "sortiert nach Aufnahmedatum/-zeit" +#~ msgstr "Sorted by picture date/time" + +#~ msgid "Vollbildansicht" +#~ msgstr "Full Screen View" + +#~ msgid "Vorheriges Bild" +#~ msgstr "Previous picture" diff --git a/app/examples/Image/Lighttable/.lang/nl.po b/app/examples/Image/Lighttable/.lang/nl.po new file mode 100644 index 00000000..3c79a5c3 --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/nl.po @@ -0,0 +1,327 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Lighttable 3.5.90\n" +"PO-Revision-Date: 2014-09-25 21:48 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Lighttable" +msgstr "Lichttabel" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Gereedschap om foto's mee te sorteren" + +#: FHelp.form:11 +msgid "Help" +msgstr "Help" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Sluiten" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "Er is geen Exif informatie in dit bestand." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Minder" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Afbeeldingsinformatie" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Meer" + +#: FMain.class:32 +msgid "Lighttable - " +msgstr "Lichttabel - " + +#: FMain.class:46 +msgid "Right-click for Main Menu" +msgstr "Rechts klikken voor Hoofdmenu" + +#: FMain.class:47 +msgid " pictures" +msgstr " afbeeldingen" + +#: FMain.class:48 +msgid "sorted alphabetically" +msgstr "alfabetisch gesorteerd" + +#: FMain.class:74 +msgid "Right-click on the background or on a frame for menus." +msgstr "Rechts klikken op achtergrond of frame voor menus." + +#: FMain.class:94 +msgid "Right-click for Picture Menu" +msgstr "Rechts klikken voor Afbeeldingsmenu" + +#: FMain.class:173 +msgid "&Rename all files..." +msgstr "&Hernoem alle bestanden..." + +#: FMain.class:268 +msgid "Loading picture..." +msgstr "Afbeelding laden..." + +#: FMain.class:343 +msgid "sorted chronologically" +msgstr "chronologisch gesorteerd" + +#: FMain.class:383 +msgid "The file &1 already exists in the current directory!" +msgstr "Het bestand &1 bestaat reeds in de huidige map!" + +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Annuleer" + +#: FTime.form:45 +msgid "&OK" +msgstr "&OK" + +#: FMain.class:435 +msgid "The file &1 will be deleted." +msgstr "Het bestand &1 wordt verwijdert." + +#: FMain.class:727 +msgid "Loading of pictures is being aborted..." +msgstr "Laden van afbeeldingen wordt afgebroken..." + +#: FMain.class:778 +msgid "Files are being renamed..." +msgstr "Bestanden worden hernoemt..." + +#: FMain.class:807 +msgid "&1 files renamed" +msgstr "&1 bestanden hernoemt" + +#: FMain.class:823 +msgid "Setting time informations in all files..." +msgstr "Instellen van de tijd informatie in alle bestanden ..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Hoofdmenu" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "&Alfabetisch gesorteerd" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "&Chronologisch gesorteerd" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "&Diavoorstelling" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "Laden &Afbreken" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "&Tijd correctie..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Open map..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Help" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Afsluiten" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Afbeeldingsmenu" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Verplaats afbeelding: sleep frame met muis)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "&Afbeelding weergeven (klik op afbeelding)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Volledig scherm weergave" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Sluit weergave" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Volgende afbeelding" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "&Vorige afbeelding" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "Afbeeldings&informatie" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "&Hernoem afbeelding" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "&Verwijder afbeelding" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "Oude en nieuwe bestandnaam zijn identiek." + +#: FRename.form:11 +msgid "Rename" +msgstr "Hernoem" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Nieuwe bestandsnaam:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "MijnAfbeelding.JPG" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Hernoem alle afbeeldingen" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "Bewaar originele bestandnaam as achtervoegsel" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Nummerformaat" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "cijfers" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Prefix" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Aanvangswaarde" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Deze functie hernoemt alle .jp(e)g bestanden in de map en voegt serieële nummers toe aan de bestandnamen.
    De afbeeldingen worden behandelt in de volgorde waarop nu weergegeven, in rijen van links naar rechts, rijen van boven naar beneden." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Opties" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Voorbeeld:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Bestandsconflicten" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "Tock &doorgaan" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "De volgende bestanden kunnen niet hernoemt worden, omdat de doel bestandnamen reeds bestaan in de map.
    Indien je verder gaat worden enkel de bestand zonden conflicten hernoemt.
    Indien je annuleert, kun je nieuwe bestandnamen kiezen." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pause tussen afbeeldingen:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "sec" + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "Stop" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "De map &1 bevat geen jp(e)g bestanden." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Lichttabel - Selecteer afbeeldingsmap" + +#: FStart.form:41 +msgid "New selection" +msgstr "Nieuwe selectie" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Laatste selecties" + +#: FTime.class:18 +msgid "There is no time correction selected." +msgstr "Er is geen tijdscorrectie geselecteerd." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Tijdscorrectie" + +#: FTime.form:39 +msgid "earlier" +msgstr "eerder" + +#: FTime.form:39 +msgid "later" +msgstr "-" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Deze functie zal alle datum/tijd informatie in de EXIF sectie van alle .jp(e)g bestanden in de map corrigeren.
    Een backup van de bestanden zal worden gemaakt met bestandnamen zoals 'MijnAfbeelding.jpg_original'.
    Indien er geen EXIF datum/tijd informatie beschikbaar is in het bestand, wordt er niets gedaan." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Stel de oorspronkelijke tijd van alle afbeeldingen in" + +#: FTime.form:70 +msgid "day(s)" +msgstr "dag(en)" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "U(u)r(en)" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "minu(u)t(en)" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Op dit programma uit te voeren dient exiftool geïnstalleert te zijn." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Op dit programma uit te voeren dient convert geïnstalleert te zijn." + diff --git a/app/examples/Image/Lighttable/.lang/ru.po b/app/examples/Image/Lighttable/.lang/ru.po new file mode 100644 index 00000000..3acac0d5 --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/ru.po @@ -0,0 +1,350 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Image/Lighttable/.project:28 +msgid "Lighttable" +msgstr "Световой планшет" + +#: app/examples/Image/Lighttable/.project:29 +msgid "A tool to sort photographs" +msgstr "Инструмент для сортировки фотографий" + +#: app/examples/Image/Lighttable/FStart.form:5 +msgid "Lighttable - Choose picture folder" +msgstr "Световой планшет - Выбрать директорию с изображениями" + +#: app/examples/Image/Lighttable/FStart.form:8 app/examples/Image/Lighttable/.src/FMain.class:435 app/examples/Image/Lighttable/.src/FRename.form:17 app/examples/Image/Lighttable/.src/FRenameAll.form:47 app/examples/Image/Lighttable/.src/FStart.form:9 app/examples/Image/Lighttable/.src/FTime.form:27 +msgid "&OK" +msgstr "ОК" + +#: app/examples/Image/Lighttable/FStart.form:13 app/examples/Image/Lighttable/.src/FMain.class:435 app/examples/Image/Lighttable/.src/FRename.form:22 app/examples/Image/Lighttable/.src/FRenameAll.form:52 app/examples/Image/Lighttable/.src/FRenameAllWarning.form:9 app/examples/Image/Lighttable/.src/FStart.form:14 app/examples/Image/Lighttable/.src/FTime.form:32 +msgid "&Cancel" +msgstr "Отмена" + +#: app/examples/Image/Lighttable/FStart.form:25 app/examples/Image/Lighttable/.src/FStart.form:26 +msgid "New selection" +msgstr "Новое выделение" + +#: app/examples/Image/Lighttable/FStart.form:33 app/examples/Image/Lighttable/.src/FStart.form:34 +msgid "Last selections" +msgstr "Последние" + +#: app/examples/Image/Lighttable/.src/FHelp.class:11 app/examples/Image/Lighttable/.src/FHelp.class:16 +msgid "help" +msgstr "справка" + +#: app/examples/Image/Lighttable/.src/FHelp.form:5 +msgid "Help" +msgstr "Справка" + +#: app/examples/Image/Lighttable/.src/FHelp.form:21 app/examples/Image/Lighttable/.src/FInfo.form:10 +msgid "&Close" +msgstr "Закрыть" + +#: app/examples/Image/Lighttable/.src/FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "В этом файле нет информации Exif." + +#: app/examples/Image/Lighttable/.src/FInfo.class:25 app/examples/Image/Lighttable/.src/FInfo.class:34 app/examples/Image/Lighttable/.src/FInfo.form:16 +msgid "&More" +msgstr "Ещё" + +#: app/examples/Image/Lighttable/.src/FInfo.class:29 +msgid "&Less" +msgstr "Меньше" + +#: app/examples/Image/Lighttable/.src/FInfo.form:5 +msgid "Picture Informations" +msgstr "Информация об изображениях" + +#: app/examples/Image/Lighttable/.src/FMain.class:32 +msgid "Lighttable - " +msgstr "Световой планшет - " + +#: app/examples/Image/Lighttable/.src/FMain.class:46 +msgid "Right-click for Main Menu" +msgstr "Правый клик для главного меню" + +#: app/examples/Image/Lighttable/.src/FMain.class:47 app/examples/Image/Lighttable/.src/FMain.class:159 app/examples/Image/Lighttable/.src/FMain.class:449 +msgid " pictures" +msgstr " изображения" + +#: app/examples/Image/Lighttable/.src/FMain.class:48 app/examples/Image/Lighttable/.src/FMain.class:315 +msgid "sorted alphabetically" +msgstr "отсортировано по алфавиту" + +#: app/examples/Image/Lighttable/.src/FMain.class:74 +msgid "Right-click on the background or on a frame for menus." +msgstr "Щёлкнуть правой кнопкой мыши на фоне или на кадре для меню." + +#: app/examples/Image/Lighttable/.src/FMain.class:94 +msgid "Right-click for Picture Menu" +msgstr "Правый клик для меню изображения" + +#: app/examples/Image/Lighttable/.src/FMain.class:173 +msgid "&Rename all files..." +msgstr "Переименовать все файлы..." + +#: app/examples/Image/Lighttable/.src/FMain.class:268 +msgid "Loading picture..." +msgstr "Загрузка изображения..." + +#: app/examples/Image/Lighttable/.src/FMain.class:343 +msgid "sorted chronologically" +msgstr "отсортировано в хронологическом порядке" + +#: app/examples/Image/Lighttable/.src/FMain.class:383 +msgid "The file &1 already exists in the current directory!" +msgstr "Файл &1 уже существует в текущей директории!" + +#: app/examples/Image/Lighttable/.src/FMain.class:435 +msgid "The file &1 will be deleted." +msgstr "Файл &1 будет удалён." + +#: app/examples/Image/Lighttable/.src/FMain.class:727 +msgid "Loading of pictures is being aborted..." +msgstr "Загрузка изображений прерывается ..." + +#: app/examples/Image/Lighttable/.src/FMain.class:778 +msgid "Files are being renamed..." +msgstr "Файлы переименовываются..." + +#: app/examples/Image/Lighttable/.src/FMain.class:807 +msgid "&1 files renamed" +msgstr "&1 файлов переименовано" + +#: app/examples/Image/Lighttable/.src/FMain.class:823 +msgid "Setting time informations in all files..." +msgstr "Установка информации о времени во все файлы..." + +#: app/examples/Image/Lighttable/.src/FMain.form:9 +msgid "Main menu" +msgstr "Главное меню" + +#: app/examples/Image/Lighttable/.src/FMain.form:12 +msgid "Sorted &alphabetically" +msgstr "Отсортировано по алфавиту" + +#: app/examples/Image/Lighttable/.src/FMain.form:18 +msgid "Sorted &chronologically" +msgstr "Отсортировано в хронологическом порядке" + +#: app/examples/Image/Lighttable/.src/FMain.form:23 +msgid "&Slideshow" +msgstr "Слайд-шоу" + +#: app/examples/Image/Lighttable/.src/FMain.form:28 +msgid "Abort &Loading" +msgstr "Прервать загрузку" + +#: app/examples/Image/Lighttable/.src/FMain.form:33 +msgid "&Time correction..." +msgstr "Коррекция времени..." + +#: app/examples/Image/Lighttable/.src/FMain.form:38 +msgid "&Open folder..." +msgstr "Открыть директорию..." + +#: app/examples/Image/Lighttable/.src/FMain.form:43 +msgid "&Help" +msgstr "Справка" + +#: app/examples/Image/Lighttable/.src/FMain.form:48 +msgid "&Quit" +msgstr "Выход" + +#: app/examples/Image/Lighttable/.src/FMain.form:54 +msgid "Picture menu" +msgstr "Меню изображения" + +#: app/examples/Image/Lighttable/.src/FMain.form:57 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Переместить картинку: перетащить кадр мышью)" + +#: app/examples/Image/Lighttable/.src/FMain.form:61 +msgid "&View picture (click on picture)" +msgstr "Просмотр изображения (нажать на изображение)" + +#: app/examples/Image/Lighttable/.src/FMain.form:66 +msgid "&Full Screen View" +msgstr "Полный экран" + +#: app/examples/Image/Lighttable/.src/FMain.form:71 +msgid "&Close view" +msgstr "Закрыть вид" + +#: app/examples/Image/Lighttable/.src/FMain.form:76 +msgid "&Next picture" +msgstr "Следующее изображение" + +#: app/examples/Image/Lighttable/.src/FMain.form:81 +msgid "&Previous picture" +msgstr "Предыдущее изображение" + +#: app/examples/Image/Lighttable/.src/FMain.form:86 +msgid "Picture &informations" +msgstr "Информация об изображениях" + +#: app/examples/Image/Lighttable/.src/FMain.form:91 +msgid "&Rename picture" +msgstr "Переименовать изображение" + +#: app/examples/Image/Lighttable/.src/FMain.form:96 +msgid "&Delete picture" +msgstr "Удалить изображение" + +#: app/examples/Image/Lighttable/.src/FRename.class:43 +msgid "Old and new filename are identical." +msgstr "Старое и новое имена файла идентичны." + +#: app/examples/Image/Lighttable/.src/FRename.form:5 +msgid "Rename" +msgstr "Переименовать" + +#: app/examples/Image/Lighttable/.src/FRename.form:10 +msgid "New filename:" +msgstr "Новое имя файла:" + +#: app/examples/Image/Lighttable/.src/FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "MyPicture.JPG" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:5 +msgid "Rename all pictures" +msgstr "Переименовать все изображения" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:20 +msgid "1" +msgstr "1" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:25 +msgid "Keep original filename as suffix" +msgstr "Сохранить оригинальное имя файла как суффикс" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:30 +msgid "Number format" +msgstr "Числовой формат" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:34 +msgid "digits" +msgstr "цифры" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:38 +msgid "Prefix" +msgstr "Префикс" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:42 +msgid "Start value" +msgstr "Начальное значение" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:59 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Эта функция переименует все файлы .jp(e)g в директории и добавит серийные номера в имена файлов.
    Изображения будут обрабатываться в том порядке, в котором они отображаются прямо сейчас, в строках слева направо, рядами сверху вниз." + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:64 +msgid "Options" +msgstr "Опции" + +#: app/examples/Image/Lighttable/.src/FRenameAll.form:68 +msgid "Example:" +msgstr "Пример:" + +#: app/examples/Image/Lighttable/.src/FRenameAllWarning.form:5 +msgid "File Conflicts" +msgstr "Конфликты файлов" + +#: app/examples/Image/Lighttable/.src/FRenameAllWarning.form:15 +msgid "Continue &anyway" +msgstr "Всё равно продолжить" + +#: app/examples/Image/Lighttable/.src/FRenameAllWarning.form:19 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "Следующие файлы не могут быть переименованы, поскольку целевые имена файлов уже существуют в директории.
    Если вы продолжите, только файлы без конфликтов будут переименованы.
    При отмене вы можете выбрать новые параметры имени файла." + +#: app/examples/Image/Lighttable/.src/FSlideshow.form:24 +msgid "Pause between pictures:" +msgstr "Пауза между изобр-ми:" + +#: app/examples/Image/Lighttable/.src/FSlideshow.form:33 +msgid "sec" +msgstr "сек" + +#: app/examples/Image/Lighttable/.src/FSlideshow.form:37 +msgid "Stop" +msgstr "Стоп" + +#: app/examples/Image/Lighttable/.src/FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "Директория &1 не содержит файлов jp(e)g." + +#: app/examples/Image/Lighttable/.src/FStart.form:5 +msgid "Lighttable - Select picture folder" +msgstr "Световой планшет - Выбрать директорию с изображениями" + +#: app/examples/Image/Lighttable/.src/FTime.class:18 +msgid "There is no time correction selected." +msgstr "Коррекция времени не выбрана." + +#: app/examples/Image/Lighttable/.src/FTime.form:5 +msgid "Time correction" +msgstr "Коррекция времени" + +#: app/examples/Image/Lighttable/.src/FTime.form:22 +msgid "earlier" +msgstr "ранее" + +#: app/examples/Image/Lighttable/.src/FTime.form:22 app/examples/Image/Lighttable/.src/FTime.form:23 +msgid "later" +msgstr "познее" + +#: app/examples/Image/Lighttable/.src/FTime.form:39 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Эта функция исправит всю информацию о дате/времени в разделе EXIF всех файлов .jp(e)g в директории.
    Резервное копирование файлов будет сделано с именами файлов, такими как «MyPicture.jpg_original».
    Если в файле нет информации о дате/времени EXIF, то ничего не будет сделано." + +#: app/examples/Image/Lighttable/.src/FTime.form:44 +msgid "Set the original time of all pictures to" +msgstr "Установить оригинальное время всех изображений на" + +#: app/examples/Image/Lighttable/.src/FTime.form:48 +msgid "day(s)" +msgstr "дней" + +#: app/examples/Image/Lighttable/.src/FTime.form:52 +msgid "hour(s)" +msgstr "часов" + +#: app/examples/Image/Lighttable/.src/FTime.form:56 +msgid "minute(s)" +msgstr "минут" + +#: app/examples/Image/Lighttable/.src/MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Для запуска этой программы необходимо установить exiftool." + +#: app/examples/Image/Lighttable/.src/MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Для запуска этой программы необходимо установить convert." + diff --git a/app/examples/Image/Lighttable/.project b/app/examples/Image/Lighttable/.project new file mode 100644 index 00000000..c5bf14f3 --- /dev/null +++ b/app/examples/Image/Lighttable/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +Title=Lighttable +Startup=MMain +Icon=lighttable.png +Version=3.6.2 +Component=gb.image +Component=gb.gui.qt +Component=gb.form +Component=gb.form.dialog +Component=gb.settings +Component=gb.gui.qt.webkit +Description="A tool to sort photographs" +Authors="Matti, with tips and some changes from Benoît" +TabSize=2 +Translate=1 +Language=en +SourcePath=/home/mathias/Basic +Maintainer=mathias +Address=mathias@mattitux +License=General Public Licence +Packager=1 +Systems=suse +Menus=suse:"Graphics/Photograph" +Categories=suse:"Graphics" +Groups=suse:"Productivity/Graphics/Viewers" diff --git a/app/examples/Image/Lighttable/.src/FHelp.class b/app/examples/Image/Lighttable/.src/FHelp.class new file mode 100644 index 00000000..38257efb --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FHelp.class @@ -0,0 +1,28 @@ +' Gambas class file + +Public Sub _new() + + Dim sLanguage As String + Dim sPath As String + Dim sDest As String + + sLanguage = Left$(System.Language, 2) + sPath = "Help_" & sLanguage & ".html" + sDest = File.SetExt(Temp$("help"), "html") + Try Copy sPath To sDest + + If Error Then + sPath = "Help_en.html" + sDest = File.SetExt(Temp$("help"), "html") + Try Copy sPath To sDest + Endif + + WebViewHelp.Url = "file://" & sDest + +End + +Public Sub btnClose_Click() + + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FHelp.form b/app/examples/Image/Lighttable/.src/FHelp.form new file mode 100644 index 00000000..588decc0 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FHelp.form @@ -0,0 +1,26 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,84,73) + Text = ("Help") + Arrangement = Arrange.Vertical + { WebViewHelp WebView + MoveScaled(2,1,80,64) + Expand = True + } + { Panel1 HBox + MoveScaled(2,66,80,6) + Background = Color.TextBackground + Margin = True + { Panel2 Panel + MoveScaled(0,1,16,2) + Expand = True + } + { btnClose Button + MoveScaled(64,1,15,4) + Text = ("&Close") + Default = True + Cancel = True + } + } +} diff --git a/app/examples/Image/Lighttable/.src/FInfo.class b/app/examples/Image/Lighttable/.src/FInfo.class new file mode 100644 index 00000000..7d6c1424 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FInfo.class @@ -0,0 +1,37 @@ +' Gambas class file + +sCommon As String = "" +sDetail As String = "" + +Public Sub Form_Open() + + sCommon = FMain.GetExifInfoCommon(Me.Tag) + If sCommon = "" Then + Message.Info(("There are no Exif informations in this file.")) + Me.Close + Endif + txlExif.Text = sCommon + +End + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub btnDetails_Click() + + If btnDetails.Text = ("&More") Then + If sDetail = "" Then sDetail = FMain.GetExifInfoAll(Me.Tag) + txlExif.Text = sDetail + txlExif.Adjust + btnDetails.Text = ("&Less") + Else + txlExif.Text = sCommon + txlExif.Width = 567 + txlExif.Height = 259 + btnDetails.Text = ("&More") + Endif + +End diff --git a/app/examples/Image/Lighttable/.src/FInfo.form b/app/examples/Image/Lighttable/.src/FInfo.form new file mode 100644 index 00000000..8cda99a1 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FInfo.form @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,84,50) + Text = ("Picture Informations") + Resizable = False + Utility = True + { btnClose Button + MoveScaled(66,44,15,4) + Text = ("&Close") + Default = True + Cancel = True + } + { btnDetails Button + MoveScaled(50,44,15,4) + Text = ("&More") + } + { ScrollView1 ScrollView + MoveScaled(1,2,82,41) + { txlExif TextLabel + MoveScaled(0,0,81,37) + Background = &HFFFCCC& + } + } +} diff --git a/app/examples/Image/Lighttable/.src/FMain.class b/app/examples/Image/Lighttable/.src/FMain.class new file mode 100644 index 00000000..a832e8c2 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FMain.class @@ -0,0 +1,903 @@ +' Gambas class file + +Private aFiles As String[] +Private aPFiles As New String[] +Private aThumbs As New String[] +Private aTime As New String[] +Private aSort As New String[] +Private aFrame As New Object[] +Private aPicture As New Object[] +Private aOri As New String[] +Private alblFile As New Object[] +Private alblTime As New Object[] +Private bStop As Boolean = False +Private hCurClose As Cursor +Private hCurHand As Cursor +Private hCurZoom As Cursor +Private iDelay As Integer +Private iPicCount As Integer +Private iPicRow As Integer +Private iRow As Integer +Private iOffset As Integer +Private iMark As Integer +Private iNr As Integer +Private sPath As String + +Public Sub Form_Open() + + sPath = Me.Tag + aFiles = MMain.GetFiles() + iPicCount = aFiles.Count + + Me.Caption = ("Lighttable - ") & sPath + Me.Move(Desktop.X, Desktop.Y, Desktop.Width, Desktop.Height) + + mnuSortA.Enabled = False + mnuSortT.Enabled = False + mnuDelete.Enabled = False + mnuTimecorr.Enabled = False + mnuSlide.Enabled = False + mnuOpen.Enabled = False + hCurClose = New Cursor(Picture.Load("close.png")) + hCurHand = New Cursor(Picture.Load("hand1.png")) + hCurZoom = New Cursor(Picture.Load("zoom-in.png")) + PicBox.Mouse = -2 + PicBox.Cursor = hCurZoom + Me.Tooltip = ("Right-click for Main Menu") + lblCount.Text = iPicCount & (" pictures") + lblSort.Text = ("sorted alphabetically") + aPFiles.Resize(iPicCount) + aThumbs.Resize(iPicCount) + aOri.Resize(iPicCount) + aTime.Resize(iPicCount) + aFrame.Resize(iPicCount + 1) + alblFile.Resize(iPicCount) + alblTime.Resize(iPicCount) + aPicture.Resize(iPicCount) + + Timer1.Trigger ' Finish this sub, so the user can do something + +End + +Public Sub Timer1_Timer() ' Load the pictures in the background + + Dim PicInfo As String + Dim i As Integer + Dim hImg As Image + + If iPicRow = 0 Then +' Print Me.Width + iPicRow = Int((Me.Width) / 200) + iOffset = (Me.Width + 30 - iPicRow * 200) / 2 + Endif + + lblStatus.Text = ("Right-click on the background or on a frame for menus.") + For i = 0 To iPicCount - 1 + aPFiles[i] = sPath & "/" & aFiles[i] + aThumbs[i] = Thumb(aFiles[i]) + Shell "exiftool -b -orientation -createdate '" & aPFiles[i] & "'" To PicInfo ' Get orientation and create time of the picture + aOri[i] = Left$(PicInfo, 1) + aTime[i] = Right$(PicInfo, 19) + + aFrame[i] = New Panel(ScrollView1) As "Framegroup" ' Make frame + With aFrame[i] + .Width = 140 + .Height = 175 + .Border = 3 + .Background = &CFCFCF& + .Tag = i + .Mouse = -2 + .Cursor = hCurHand + iRow = Int((i) / iPicRow) + .X = (i - (iPicRow * iRow)) * 200 + iOffset + .Y = iRow * 210 + 30 + .Tooltip = ("Right-click for Picture Menu") + End With + + alblFile[i] = New Label(aFrame[i]) As "Framegroup" ' Label filename + With alblFile[i] + .Width = 130 + .Height = 16 + .X = 5 + .Y = 140 + .Font.Grade = -1 + .Alignment = 3 + .Tag = i + .Text = aFiles[i] + If Len(.Text) > 22 Then + .Alignment = 1 + .ToolTip = .Text + Endif + End With + + alblTime[i] = New Label(aFrame[i]) As "Framegroup" ' Label Timestamp + With alblTime[i] + .Width = 130 + .Height = 16 + .X = 5 + .Y = 155 + .Font.Grade = -1 + .Alignment = 3 + .Tag = i + .Text = aTime[i] + End With + If i = 0 Then + iNr = 0 + Mark_Frame(iNr) + Endif + + aPicture[i] = New PictureBox(aFrame[i]) As "Thumbnail" ' Thumbnail + With aPicture[i] + .Mouse = -2 + .Cursor = hCurZoom + .Tag = i + .Alignment = 3 + .X = 5 + .Y = 5 + .Width = 128 + .Height = 128 + + Try hImg = Image.Load(aThumbs[i]) ' Image, because it might have to be rotated + If Error Then ' If thumbnail doesn't exist, it has to be created + Shell "convert -define jpeg:size=150x150 '" & aPFiles[i] & "' -auto-orient -thumbnail 128x128 '" & aThumbs[i] & "'" Wait + hImg = Image.Load(aThumbs[i]) + Endif + + If aOri[i] > "1" And hImg.Width > hImg.Height Then ' If a system's thumbnail isn't rotated correctly + If aOri[i] = 6 Then + hImg = hImg.Rotate(Rad(-90)) + Else If aOri[i] = 8 Then + hImg = hImg.Rotate(Rad(90)) + Endif + Endif + .Picture = hImg.Picture + End With + + ProgressBar1.Value = (i + 1) / iPicCount + If bStop = True Then 'if loading is stopped + iPicCount = i + 1 + lblCount.Text = iPicCount & (" pictures") + Break + Endif + Wait 0.01 + Next + + iRow = Int(iPicCount / iPicRow) + 1 + panEmpty.Y = iRow * 210 ' Insert an empty panel to get some space at the bottom + ProgressBar1.Visible = False + lblStatus.Text = "" + mnuSortA.Enabled = False + mnuSortT.Enabled = True + mnuDelete.Enabled = True + mnuTimecorr.Enabled = True + mnu_StopLoad_RenameAll.Caption = ("&Rename all files...") + mnu_StopLoad_RenameAll.Picture = Stock["16/save-as"] + mnu_StopLoad_RenameAll.Shortcut = "Ctrl+N" + mnuSlide.Enabled = True + mnuOpen.Enabled = True + bStop = True + Me.Tag = "alpha" + Me.Tooltip = "" + For i = 0 To iPicCount - 1 + aFrame[i].Tooltip = "" + Next + +End + +Public Sub Framegroup_MouseDown() ' a frame is clicked + + iNr = Last.Tag + Mark_Frame(iNr) + If PicBox.Visible = True And PicBox.Tag <> aFiles[iNr] Then + If PicBox.Cursor = hCurClose Then + mnuFullscreen_Click + Else + mnuView_Click + Endif + Endif + +End + +Public Sub Framegroup_MouseMove() ' Move a frame + + iNr = Last.Tag + PicBox.Visible = False + FInfo.Close + aFrame[iNr].X += Mouse.X - Mouse.StartX + aFrame[iNr].Y += Mouse.Y - Mouse.StartY + If aFrame[iNr].Y < ScrollView1.ScrollY Then + ScrollView1.Scroll(ScrollView1.ScrollX, ScrollView1.ScrollY - 10) + Endif + If aFrame[iNr].Y > Me.Height + ScrollView1.ScrollY - 175 Then + ScrollView1.Scroll(ScrollView1.ScrollX, ScrollView1.ScrollY + 10) + Endif + Mark_Frame(iNr) + +End + +Public Sub Framegroup_Menu() ' Context menu of the frames + + iNr = Last.Tag + FInfo.Close + mnuDrag.Enabled = True + mnuView.Enabled = True + mnuESC.Enabled = False + mnuFramegroup.Popup + +End + +Public Sub Thumbnail_MouseDown() ' a thumbnail is clicked + + iNr = Last.Tag + FInfo.Close + Framegroup_MouseDown + If Mouse.Left Then ' view picture + If PicBox.Visible = False Then mnuView_Click + Else ' Show menu + mnuFramegroup.Popup + Endif + +End + +Public Sub mnuView_Click() ' Show picture + + Dim iFormat As Single + + Load_Picture + iFormat = aPicture[iNr].Picture.Width / aPicture[iNr].Picture.Height + If iFormat > 1 Then ' Landscape + PicBox.Width = 800 + PicBox.Height = 800 / iFormat + Else ' Portrait + PicBox.Height = 800 + PicBox.Width = 800 * iFormat + Endif + PicBox.X = (Me.Width - PicBox.Width) / 2 ' center picture + PicBox.Y = (Me.Height - PicBox.Height) / 2 + mnuView.Enabled = False + mnuESC.Enabled = True + PicBox.Visible = True + PicBox.SetFocus + +End + +Public Sub Load_Picture() + + Dim hImg As Image + + lblStatus.Text = ("Loading picture...") + Wait + hImg = Image.Load(aPFiles[iNr]) ' Image, because it might have to be rotated + If aOri[iNr] = 6 Then + hImg = hImg.Rotate(Rad(-90)) + Else If aOri[iNr] = 8 Then + hImg = hImg.Rotate(Rad(90)) + Endif + PicBox.Picture = hImg.Picture + PicBox.Tag = aFiles[iNr] + lblStatus.Text = "" + Wait + +End + +Public Sub Form_Menu() ' Main context menu + + PicBox.Visible = False + FInfo.Close + mnuMain.Popup + +End + +Public Sub mnuSortA_Click() ' Sort pictures alphabetically by file names + + Dim i, pos As Integer + Dim n As String + + PicBox.Visible = False + FInfo.Close + aSort.Resize(iPicCount) + For i = 0 To iPicCount - 1 + aSort[i] = aFiles[i] & "/" & i ' Workaround. A 2-dim array would be correct, but I can't sort it... + Next + aSort.Sort(1) + + For i = 0 To iPicCount - 1 + pos = InStr(aSort[i], "/") + n = Right$(aSort[i], Len(aSort[i]) - pos) + iRow = Int((i) / iPicRow) + aFrame[n].X = (i - (iPicRow * iRow)) * 200 + iOffset + aFrame[n].Y = iRow * 210 + 30 + Next + mnuSortT.Checked = False + mnuSortT.Enabled = True + mnuSortA.Checked = True + mnuSortA.Enabled = False + lblSort.Text = ("sorted alphabetically") + Me.Tag = "alpha" + Show_marked_frame + +End + +Public Sub mnuSortT_Click() ' Sort pictures chronologically by timestamp + + Dim i, pos As Integer + Dim n As String + + aSort.Resize(iPicCount) + For i = 0 To iPicCount - 1 + aSort[i] = aTime[i] & "/" & i ' Workaround. A 2-dim array would be correct, but I can't sort it... + Next + aSort.Sort() + + For i = 0 To iPicCount - 1 + pos = InStr(aSort[i], "/") + n = Right$(aSort[i], Len(aSort[i]) - pos) + iRow = Int((i) / iPicRow) + aFrame[n].X = (i - (iPicRow * iRow)) * 200 + iOffset + aFrame[n].Y = iRow * 210 + 30 + Next + mnuSortA.Checked = False + mnuSortA.Enabled = True + mnuSortT.Checked = True + mnuSortT.Enabled = False + lblSort.Text = ("sorted chronologically") + Me.Tag = "chron" + Show_marked_frame + +End + +Public Sub Show_marked_frame() + + If iMark > -1 Then ' Only if a picture is selected + If ScrollView1.ScrollY > (aFrame[iMark].Y - 30) Then ' If necessary, scroll up + ScrollView1.Scroll(0, (aFrame[iMark].Y - 30)) + Return + Endif + If ScrollView1.ScrollY + Me.Height < aFrame[iMark].Y + 210 Then ' If necessary, scroll down + ScrollView1.Scroll(0, aFrame[iMark].Y + 230 - Me.Height) + Endif + Endif + +End + +Public Sub mnuRename_Click() ' Show the form FRename (the renaming itself is done in the function FileRename) + + If iMark = -1 Then Return ' Only if a picture is selected + PicBox.Visible = False + FInfo.Close + mnuESC.Enabled = False + mnuView.Enabled = True + mnuFullscreen.Enabled = True + PicBox.Cursor = hCurZoom + FRename.Tag = aFiles[iNr] + FRename.ShowModal + +End + +Public Function TestRenameOne(sOldname As String, sNewname As String) As Boolean + + Dim n As Integer + + For n = 0 To iPicCount - 1 ' Check if new name already exists + If aFiles[n] = sNewname Then + Message.Error(Subst(("The file &1 already exists in the current directory!"), sNewname)) + Return False ' Give error back to FRename + Endif + Next + Return True + +End + +Public Function FileRename(sOldname As String, sNewname As String) As Boolean ' Rename a file + + Dim sThumbname As String + + Try Move sPath & "/" & sOldname To sPath & "/" & sNewname ' Rename file + If Error Then Return False + aFiles[iNr] = sNewname ' Rename all entries in arrays and labels + alblFile[iNr].Text = sNewname + alblFile[iNr].ToolTip = "" + alblFile[iNr].Alignment = 3 + If Len(sNewname) > 20 Then + alblFile[iNr].Alignment = 1 + alblFile[iNr].ToolTip = sNewname + Endif + Wait + aPFiles[iNr] = sPath & "/" & sNewname + sThumbname = Thumb(aFiles[iNr]) ' Rename thumbnail + Try Move aThumbs[iNr] To sThumbname + If Error Then + Kill sThumbname + Wait + Move aThumbs[iNr] To sThumbname + Endif + aThumbs[iNr] = sThumbname + Return True ' give ok back + +End + +Public Sub mnuDelete_Click() ' Delete a picture + + Dim i, x, y As Integer + Dim sPicState As String + + If iMark = -1 Then Return ' Only if a picture is selected + FInfo.Close + If PicBox.Visible = True Then ' Remember the state of PictureBox + If PicBox.Cursor = hCurZoom Then + sPicState = "on" + Else + sPicState = "full" + Endif + Else + sPicState = "off" + Endif + If Message.Warning(Subst(("The file &1 will be deleted."), aFiles[iNr]), ("&OK"), ("&Cancel")) = 2 Then Return ' If user aborts, cancel + + Try Shell "kioclient move '" & aPFiles[iNr] & "' trash:/" ' Move file to trash - kioclient preferred, because it writes the restore infos + If Error Then ' Otherwise move directly to trash + Move aPFiles[iNr] To "trash:/" + Endif + Try Shell "kioclient move '" & aThumbs[iNr] & "' trash:/" ' Move Thumbnail to trash + If Error Then + Move aThumbs[iNr] To "trash:/" + Endif + + x = aFrame[iNr].X ' Remember position of deleted frame + y = aFrame[iNr].Y + iPicCount = iPicCount - 1 + lblCount.Text = iPicCount & (" pictures") + + aFrame[iNr].Delete ' Remove picture + aFiles.Remove(iNr) ' Delete all array-elements, arrays are 1 smaller + aPFiles.Remove(iNr) + aThumbs.Remove(iNr) + aTime.Remove(iNr) + aFrame.Remove(iNr) + aPicture.Remove(iNr) + alblFile.Remove(iNr) + alblTime.Remove(iNr) + aOri.Remove(iNr) + + For i = iNr To iPicCount - 1 ' Set the tags of all next frames in the arrays -1 + aFrame[i].Tag = i + aPicture[i].Tag = i + alblFile[i].Tag = i + alblTime[i].Tag = i + Next + + If FindNextFrame(x, y) = False Then + iMark = -1 + PicBox.Visible = False + mnuFullscreen.Enabled = True + Return + Endif + mnuView.Enabled = True + mnuFullscreen.Enabled = True + Mark_Frame(iNr) + Show_marked_frame + Select Case sPicState ' Show PictureBox again with next picture + Case "on" + mnuView_Click + Case "full" + mnuFullscreen_Click + End Select +End + +Public Sub mnuInfo_Click() ' Show picture infos + + Dim posx, posy As Integer + + If iMark = -1 Then Return + posx = aFrame[iNr].X + posy = aFrame[iNr].Y - ScrollView1.ScrollY + If posx < Me.Width - 740 Then ' Calculate position for FInfo + FInfo.X = posx + 145 + Else + FInfo.X = posx - 595 + Endif + If posy < Me.Height - 350 Then + FInfo.Y = posy + 24 + Else + FInfo.Y = posy - 180 + Endif + FInfo.Tag = iNr + FInfo.Show + +End + +Public Sub GetExifInfoCommon(iNumber As Integer) As String + + Dim sExif As String + + Shell "exiftool -common -h '" & aPFiles[iNumber] & "'" To sExif + Return sExif + +End + +Public Sub GetExifInfoAll(iNumber As Integer) As String + + Dim sExif As String + + Shell "exiftool -a -h '" & aPFiles[iNumber] & "'" To sExif + Return sExif + +End + +Public Sub mnuHelp_Click() ' Show help + + FHelp.X = (Me.Width - FHelp.Width) / 2 + FHelp.Show + +End + +Public Sub PicBox_MouseDown() ' Click on PictureBox + + If Mouse.Left = True Then + If PicBox.Cursor = hCurZoom Then ' if smaller picture + mnuFullscreen_Click + Else ' if full screen + BackToSmallView + Endif + Else ' right-klick + mnuDrag.Enabled = False + mnuFramegroup.Popup + Endif + +End + +Public Sub mnuFullscreen_Click() ' Full screen view (we ignore that a picture might be smaller) + + Dim iFormat As Single + + If PicBox.Visible = False Or PicBox.Tag <> aFiles[iNr] Then + Load_Picture + Endif + iFormat = aPicture[iNr].Picture.Width / aPicture[iNr].Picture.Height + PicBox.Y = 0 + PicBox.Height = Me.Height + PicBox.Width = Me.Height * iFormat + If PicBox.Width > Me.Width Then ' If picture format is 16:9 or wider + PicBox.Width = Me.Width + PicBox.Height = Me.Width / iFormat + Endif + PicBox.X = (Me.Width - PicBox.Width) / 2 + PicBox.Visible = True + mnuFullscreen.Enabled = False + mnuView.Enabled = False + mnuESC.Enabled = True + PicBox.Cursor = hCurClose + PicBox.SetFocus + +End + +Public Sub BackToSmallView() ' Close FullScreen view + + Dim w, h As Integer + + w = PicBox.Width + h = PicBox.Height + If w / h > 1 Then + PicBox.Width = 800 + PicBox.Height = 800 / w * h + Else + PicBox.Width = 800 * w / h + PicBox.Height = 800 + Endif + PicBox.X = (Me.Width - PicBox.Width) / 2 ' centered + PicBox.y = (Me.Height - PicBox.Height) / 2 + PicBox.Cursor = hCurZoom + mnuView.Enabled = False + mnuFullscreen.Enabled = True + mnuESC.Enabled = True + +End + +Public Sub mnuESC_Click() ' ESC is pressed + + If PicBox.Visible = True Then + If PicBox.Cursor = hCurClose Then ' we have full screen + BackToSmallView + Else ' Close small view + PicBox.Visible = False + mnuView.Enabled = True + mnuFullscreen.Enabled = True + mnuESC.Enabled = False + aFrame[iNr].SetFocus + Endif + Endif + +End + +Public Sub mnuNext_Click() ' Move to next picture + + If iMark = -1 Then Return ' Only if a picture is selected + If FindNextFrame(aFrame[iNr].X, aFrame[iNr].Y) = False Then Return ' if no next picture is found, exit + Mark_Frame(iNr) + Show_marked_frame + If PicBox.Visible = True Then + If PicBox.Cursor = hCurClose Then ' if fullscreen + mnuFullscreen_Click + Else + mnuView_Click + Endif + Endif + +End + +Public Function FindNextFrame(x As Integer, y As Integer) As Boolean + + Dim i As Integer + + Do + x = x + 70 + If x > (ScrollView1.ScrollWidth - 150) Then ' if we are at the end of a line, go to next line + x = 0 + y = y + 210 + Endif + If y > panEmpty.Y + 200 Then Break ' if we are in last line, end + For i = 0 To iPicCount - 1 + If aFrame[i].X > x And aFrame[i].X < x + 140 And aFrame[i].Y > y - 100 And aFrame[i].Y < y + 100 Then + iNr = i + Return True ' found + Endif + Next + Loop + Return False 'none found + +End + +Public Sub mnuPrevious_Click() ' Move to previous picture + + Dim x, y, i As Integer + + If iMark = -1 Then Return ' Only if a picture is selected + x = aFrame[iNr].X + y = aFrame[iNr].Y + Do + x = x - 70 + If x < 70 Then ' if we are at the beginning of a line, go to end of previous line + x = Me.Width + y = y - 210 + Endif + If y < 0 Then Break ' if we are at top, end + For i = 0 To iPicCount - 1 + If aFrame[i].X < x And aFrame[i].X > x - 140 And aFrame[i].Y > y - 100 And aFrame[i].Y < y + 100 Then + iNr = i + Mark_Frame(iNr) + Show_marked_frame + If PicBox.Visible = True Then + If PicBox.Cursor = hCurClose Then 'fullscreen + mnuFullscreen_Click + Else + mnuView_Click + Endif + Endif + Goto found + Endif + Next + Loop + found: + +End + +Public Sub Mark_Frame(iNumber As Integer) 'Mark selected frame + + If iMark > -1 Then + aFrame[iMark].Background = &CFCFCF& + alblFile[iMark].Foreground = &000000& + alblTime[iMark].Foreground = &000000& + Endif + aFrame[iNumber].Background = &0000FF& + alblFile[iNumber].Foreground = &FFFFFF& + alblTime[iNumber].Foreground = &FFFFFF& + aFrame[iNumber].Raise + iMark = iNumber + If FInfo.Visible = True Then + FInfo.Close + mnuInfo_Click + Endif + +End + +Public Function Thumb(sFilename As String) As String ' Get the file name for thumbnail using md5sum + + Dim sThumb As String + Dim i As Integer + + sFilename = sPath & "/" & sFilename + sFilename = Replace$(sFilename, " ", "%20") ' we have to replace spaces and special characters + For i = 123 To 255 + sFilename = Replace$(sFilename, Chr(i), "%" & Hex$(i)) + Next + sFilename = "file://" & sFilename + Shell "echo -n '" & sFilename & "' | md5sum" To sThumb + sThumb = Left$(sThumb, 32) + sThumb = User.Home & "/.thumbnails/normal/" & sThumb & ".png" + Return sThumb + +End + +Public Sub mnu_StopLoad_RenameAll_Click() 'Abort Loading or Rename all + + PicBox.Visible = False + FInfo.Close + + If bStop = False Then ' abort + lblStatus.Text = ("Loading of pictures is being aborted...") + Wait + bStop = True + Return + Endif + FRenameAll.ShowModal ' rename + +End + +Public Function TestRenameAll(sPref As String, sFmt As String, iStart As Integer, bKeep As Boolean) As String ' Check if target filenames already exist + + Dim x, y, i, n As Integer + Dim sResult As String = "" + Dim aOldname As New String[iPicCount] + Dim aNewname As New String[iPicCount] + + x = -60 + y = 100 + i = iStart + n = 0 + Do While FindNextFrame(x, y) + aOldname[n] = aFiles[iNr] + aNewname[n] = sPref & Format(i, sFmt) + If bKeep = True Then ' keep old filename + aNewname[n] = aNewname[n] & "_" & aOldname[n] + Else ' just number + aNewname[n] = aNewname[n] & ".jpg" + Endif + x = aFrame[iNr].X + y = aFrame[iNr].Y + n = n + 1 + i = i + 1 + Loop + Mark_Frame(iNr) + Show_marked_frame + For i = 0 To iPicCount - 1 ' Check if there would be a conflict with existing filenames + For n = i + 1 To iPicCount - 1 + If aNewname[i] = aOldname[n] Then + sResult = sResult & aOldname[i] & " --> " & aOldname[n] & "\n" + Endif + Next + Next + Return sResult + +End + +Public Function RenameAll(sPref As String, sFmt As String, iStart As Integer, bKeep As Boolean) As Boolean ' Rename all files + + Dim x, y, n, i As Integer + Dim oldname, newname As String + + lblStatus.Text = ("Files are being renamed...") + Wait + x = -60 + y = 100 + n = 0 + i = iStart + + Do While FindNextFrame(x, y) + oldname = aFiles[iNr] + newname = sPref & Format(i, sFmt) + If bKeep = True Then ' keep old filename + newname = newname & "_" & oldname + Else ' just number + newname = newname & ".jpg" + Endif + If FileRename(oldname, newname) = False Then + n = n - 1 + Endif + x = aFrame[iNr].X + y = aFrame[iNr].Y + n = n + 1 + i = i + 1 + Wait + Loop + + Mark_Frame(iNr) + Show_marked_frame + lblStatus.Text = "" + Dec Application.Busy + Message.Info(Subst(("&1 files renamed"), n)) + +End + +Public Sub mnuTimecorr_Click() ' Time correction + + FTime.Tag = sPath + FTime.ShowModal + +End + +Public Sub TimeCorrection(idays As Integer, ihours As Integer, iminutes As Integer, PlusMinus As String) + + Dim i As Integer + Dim Hrs As Single + + lblStatus.Text = ("Setting time informations in all files...") + Wait + If PlusMinus = "0" Then + PlusMinus = "-" + Else + PlusMinus = "+" + Endif + Hrs = idays * 24 + ihours + iminutes / 60 + + Shell "exiftool -alldates" & PlusMinus & "=" & Hrs & " '" & sPath & "'" Wait + For i = 0 To iPicCount - 1 + Shell "exiftool -b -createdate '" & aPFiles[i] & "'" To alblTime[i].Text + Next + lblStatus.Text = "" + +End + +Public Sub mnuSlide_Click() ' Slide Show + + Dim i, iLast As Integer + + iLast = iNr + FSlideshow.Show + + Do + FSlideshow.PictureLoad(aPFiles[iNr], aOri[iNr]) + FSlideshow.Title = aFiles[iNr] + i = 1 + Do + If i > iDelay Then Break + If FSlideshow.Visible = False Or FSlideshow.Minimized = True Then Break + Wait 1 + Inc i + Loop + + If FSlideshow.Visible = False Or FSlideshow.Minimized = True Then Break + mnuNext_Click + If iNr = iLast Then Break + iLast = iNr + Loop + + FSlideshow.Close + Settings["SlideShow/Delay"] = iDelay + +End + +Public Function GetDelay(iSec As Integer) As Boolean + + iDelay = iSec + +End + +Public Sub mnuOpen_Click() ' Open another folder + + MMain.Main + Me.Close + +End + +Public Sub mnuQuit_Click() ' Quit + + Me.Close + +End + +Public Sub Form_Close() + + If bStop = False Then ' if loading of pictures is still in progress + bStop = True + Stop Event + Wait + Timer2.Enabled = True + Endif + +End + +Public Sub Timer2_Timer() + + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FMain.form b/app/examples/Image/Lighttable/.src/FMain.form new file mode 100644 index 00000000..41224a39 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FMain.form @@ -0,0 +1,162 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,116,65) + Background = Color.Background + Icon = Picture["LTicon.png"] + Arrangement = Arrange.Vertical + { mnuMain Menu + Text = ("Main menu") + Visible = False + { mnuSortA Menu + Text = ("Sorted &alphabetically") + Picture = Picture["icon:/16/sort-ascent"] + Checked = True + Shortcut = "Ctrl+A" + } + { mnuSortT Menu + Text = ("Sorted &chronologically") + Picture = Picture["icon:/16/clock"] + Shortcut = "Ctrl+C" + } + { mnuSlide Menu + Text = ("&Slideshow") + Picture = Picture["icon:/16/video"] + Shortcut = "Ctrl+S" + } + { mnu_StopLoad_RenameAll Menu + Text = ("Abort &Loading") + Picture = Picture["icon:/16/cancel"] + Shortcut = "End" + } + { mnuTimecorr Menu + Text = ("&Time correction...") + Picture = Picture["icon:/16/camera"] + Shortcut = "Ctrl+T" + } + { mnuOpen Menu + Text = ("&Open folder...") + Picture = Picture["icon:/16/open"] + Shortcut = "Ctrl+O" + } + { mnuHelp Menu + Text = ("&Help") + Picture = Picture["help-contents.png"] + Shortcut = "F1" + } + { mnuQuit Menu + Text = ("&Quit") + Picture = Picture["icon:/16/quit"] + Shortcut = "Ctrl+End" + } + } + { mnuFramegroup Menu + Text = ("Picture menu") + Visible = False + { mnuDrag Menu + Text = ("(Move picture: drag frame with mouse)") + Picture = Picture["move.png"] + } + { mnuView Menu + Text = ("&View picture (click on picture)") + Picture = Picture["icon:/16/zoom-in"] + Shortcut = "Enter" + } + { mnuFullscreen Menu + Text = ("&Full Screen View") + Picture = Picture["icon:/16/fullscreen"] + Shortcut = "Alt+Enter" + } + { mnuESC Menu + Text = ("&Close view") + Picture = Picture["icon:/16/close"] + Shortcut = "Esc" + } + { mnuNext Menu + Text = ("&Next picture") + Picture = Picture["icon:/16/next"] + Shortcut = "Right" + } + { mnuPrevious Menu + Text = ("&Previous picture") + Picture = Picture["icon:/16/previous"] + Shortcut = "Left" + } + { mnuInfo Menu + Text = ("Picture &informations") + Picture = Picture["icon:/16/info"] + Shortcut = "I" + } + { mnuRename Menu + Text = ("&Rename picture") + Picture = Picture["icon:/16/save-as"] + Shortcut = "N" + } + { mnuDelete Menu + Text = ("&Delete picture") + Picture = Picture["icon:/16/delete"] + Shortcut = "Del" + } + } + { ScrollView1 ScrollView + MoveScaled(27,13,41,32) + Background = Color.TextBackground + Expand = True + Border = False + { panEmpty Panel + MoveScaled(4,10,16,16) + } + } + { Timer1 #Timer + #MoveScaled(105,22) + Delay = 10 + } + { Timer2 #Timer + #MoveScaled(105,30) + Delay = 100 + } + { Separator1 Separator + MoveScaled(27,56,14,0) + } + { StatusBar Panel + MoveScaled(2,60,99,4) + Arrangement = Arrange.Horizontal + { lblCount Label + MoveScaled(1,0.5714,13,3) + Font = Font["-1"] + Alignment = Align.Center + Border = Border.Sunken + } + { lblSort Label + MoveScaled(15,0.5714,29,3) + Font = Font["-1"] + Alignment = Align.Center + Border = Border.Sunken + } + { Panel1 Panel + MoveScaled(45,0,1,4) + } + { lblStatus Label + MoveScaled(47,0,27,4) + Font = Font["-1"] + Expand = True + Alignment = Align.Center + } + { Panel2 Panel + MoveScaled(75,0,22,4) + Arrangement = Arrange.Fill + Margin = True + Padding = 4 + { ProgressBar1 ProgressBar + MoveScaled(0,0,20,3) + } + } + } + { PicBox PictureBox + MoveScaled(7,37,9,9) + Visible = False + Ignore = True + Stretch = True + Border = Border.Etched + } +} diff --git a/app/examples/Image/Lighttable/.src/FRename.class b/app/examples/Image/Lighttable/.src/FRename.class new file mode 100644 index 00000000..05d57c47 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRename.class @@ -0,0 +1,58 @@ +' Gambas class file + +Private sOldName As String + +Public Sub Form_Open() + + txtRename.Text = Me.Tag + sOldName = Me.Tag + MarkFilename + +End + +Public Sub MarkFilename() + + Dim lentext, posp As Integer + Dim right3 As String + + right3 = Right$(txtRename.Text, 3) + lentext = Len(txtRename.Text) + If right3 = "jpg" Or right3 = "JPG" Then + posp = lentext - 4 + Else + posp = lentext - 5 + Endif + Me.SetFocus + txtRename.SetFocus + txtRename.Select(0, posp) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub btnOK_Click() + + Dim sNewName As String + + sNewName = txtRename.Text + If sOldName = sNewName Then + Message.Error(("Old and new filename are identical.")) + MarkFilename + Return + Endif + If FMain.TestRenameOne(sOldName, sNewName) = False Then ' if name already exists, cancel + txtRename.Text = sOldName + MarkFilename + Return + Endif + FMain.FileRename(sOldName, sNewName) + If FMain.Tag = "alpha" Then ' if sorted alphabetically, sort anew + FMain.mnuSortA_Click + Endif + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FRename.form b/app/examples/Image/Lighttable/.src/FRename.form new file mode 100644 index 00000000..58fa4574 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRename.form @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,51,15) + Text = ("Rename") + Resizable = False + SkipTaskbar = True + { lblRename Label + MoveScaled(2,1,23,3) + Text = ("New filename:") + } + { txtRename TextBox + MoveScaled(1,4,48,4) + } + { btnOK Button + MoveScaled(34,10,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(18,10,15,4) + Text = ("&Cancel") + Cancel = True + } +} diff --git a/app/examples/Image/Lighttable/.src/FRenameAll.class b/app/examples/Image/Lighttable/.src/FRenameAll.class new file mode 100644 index 00000000..b6a78ecf --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAll.class @@ -0,0 +1,101 @@ +' Gambas class file + +Private sExpl As String +Private sDigits As String = "000" +Private sOldStart As String = "1" +Private iStartVal As Integer = 1 + +Public Sub Form_Open() + + FMain.Enabled = False + sExpl = ("MyPicture.JPG") + SpinBox1.Value = 3 + RenewExample + +End + +Public Sub txtPre_Change() + + RenewExample + +End + +Public Sub SpinBox1_Change() + + If SpinBox1.Value < Len(txtStart.Text) Then + SpinBox1.Value = Len(txtStart.Text) + Return + Endif + sDigits = String$(SpinBox1.Value, "0") + RenewExample + +End + +Public Sub CheckFile_Click() + + RenewExample + +End + +Public Sub txtStart_Change() + + Dim sNewStart As String + Dim iLenStart As Integer + + sNewStart = txtStart.Text + If Not IsDigit(sNewStart) Then + txtStart.Text = sOldStart + Return + Endif + + iLenStart = Len(sNewStart) + iStartVal = Val(sNewStart) + If iLenStart > SpinBox1.Value Then SpinBox1.Value = iLenStart + sOldStart = sNewStart + RenewExample + +End + +Public Sub RenewExample() + + lblExample.Text = txtPre.Text & Format$(iStartVal, sDigits) + If CheckFile.Value = True Then + lblExample.Text = lblExample.Text & "_" & sExpl + Else + lblExample.Text = lblExample.Text & ".jpg" + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub btnOK_Click() + + Dim sProblems As String + Dim iContinue As Integer + + sProblems = FMain.TestRenameAll(txtPre.Text, sDigits, iStartVal, CheckFile.Value) + If sProblems <> "" Then + FRenameAllWarning.Tag = sProblems + iContinue = FRenameAllWarning.ShowModal() + Else + iContinue = 1 + Endif + + If iContinue = 0 Then Return + + Inc Application.Busy + FMain.RenameAll(txtPre.Text, sDigits, iStartVal, CheckFile.Value) + Me.Close + +End + +Public Sub form_Close() + + FMain.Enabled = True + +End diff --git a/app/examples/Image/Lighttable/.src/FRenameAll.form b/app/examples/Image/Lighttable/.src/FRenameAll.form new file mode 100644 index 00000000..aee280c1 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAll.form @@ -0,0 +1,77 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,59) + Text = ("Rename all pictures") + Resizable = False + { Panel1 Panel + MoveScaled(2,21,60,22) + Border = Border.Sunken + { txtPre TextBox + MoveScaled(24,2,35,4) + } + { SpinBox1 SpinBox + MoveScaled(24,7,6,4) + MinValue = 1 + MaxValue = 5 + } + { txtStart TextBox + MoveScaled(24,12,10,4) + Text = ("1") + MaxLength = 5 + } + { CheckFile CheckBox + MoveScaled(2,17,55,3) + Text = ("Keep original filename as suffix") + Value = CheckBox.True + } + { Label2 Label + MoveScaled(2,7,22,4) + Text = ("Number format") + } + { Label3 Label + MoveScaled(31,7,13,4) + Text = ("digits") + } + { Label5 Label + MoveScaled(2,2,22,4) + Text = ("Prefix") + } + { Label6 Label + MoveScaled(2,12,22,4) + Text = ("Start value") + } + } + { btnOK Button + MoveScaled(46,53,16,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(28,53,16,4) + Text = ("&Cancel") + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(2,2,60,14) + Background = &HFFFCCC& + Padding = 7 + Text = ("This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom.") + Border = Border.Etched + } + { Label1 Label + MoveScaled(2,18,19,3) + Text = ("Options") + } + { Label4 Label + MoveScaled(2,46,11,4) + Text = ("Example:") + } + { lblExample Label + MoveScaled(13,46,49,4) + Font = Font["Bold"] + Background = Color.TextBackground + Alignment = Align.Center + Border = Border.Etched + } +} diff --git a/app/examples/Image/Lighttable/.src/FRenameAllWarning.class b/app/examples/Image/Lighttable/.src/FRenameAllWarning.class new file mode 100644 index 00000000..8c4626e7 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAllWarning.class @@ -0,0 +1,19 @@ +' Gambas class file + +Public Sub Form_Open() + + txaList.Text = Me.Tag + +End + +Public Sub btnCancel_Click() + + Me.Close(0) + +End + +Public Sub btnContinue_Click() + + Me.Close(1) + +End diff --git a/app/examples/Image/Lighttable/.src/FRenameAllWarning.form b/app/examples/Image/Lighttable/.src/FRenameAllWarning.form new file mode 100644 index 00000000..e5ae683a --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAllWarning.form @@ -0,0 +1,29 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,54) + Text = ("File Conflicts") + Resizable = False + { btnCancel Button + MoveScaled(51,48,15,4) + Text = ("&Cancel") + Default = True + Cancel = True + } + { btnContinue Button + MoveScaled(28,48,21,4) + Text = ("Continue &anyway") + } + { TextLabel1 TextLabel + MoveScaled(11,2,54,14) + Text = ("The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options.") + } + { txaList TextArea + MoveScaled(2,18,64,28) + ReadOnly = True + } + { PictureBox1 PictureBox + MoveScaled(2,3,7,7) + Picture = Picture["icon:/48/warning"] + } +} diff --git a/app/examples/Image/Lighttable/.src/FSlideshow.class b/app/examples/Image/Lighttable/.src/FSlideshow.class new file mode 100644 index 00000000..73752d11 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FSlideshow.class @@ -0,0 +1,56 @@ +' Gambas class file + +Public Sub Form_Open() + + Dim sDelay As String + + Me.Width = Desktop.Width + Me.Height = Desktop.Height + panControl.Y = Me.Height - 55 + panControl.Width = Me.Width + sDelay = Settings["SlideShow/Delay"] + If sDelay = "" Then sDelay = "5" + spinDelay.Value = sDelay + FMain.GetDelay(sDelay) + btnStop.SetFocus + +End + +Public Function PictureLoad(sPath As String, iOri As String) As Boolean + + Dim hPic As Image + Dim iFormat As Single + + hPic = Image.Load(sPath) + If iOri = 6 Then + hPic = hPic.Rotate(Rad(-90)) + Else If iOri = 8 Then + hPic = hPic.Rotate(Rad(90)) + Endif + PicBox1.Picture = hPic.Picture + Wait + + iFormat = PicBox1.Picture.Width / PicBox1.Picture.Height + PicBox1.Y = 0 + PicBox1.Height = Me.Height - 35 + PicBox1.Width = Me.Height * iFormat + If PicBox1.Width > Me.Width Then + PicBox1.Width = Me.Width + PicBox1.Height = Me.Width / iFormat + Endif + PicBox1.X = (Me.Width - PicBox1.Width) / 2 + PicBox1.Visible = True + +End + +Public Sub spinDelay_Change() + + FMain.GetDelay(spinDelay.Value) + +End + +Public Sub btnStop_Click() + + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FSlideshow.form b/app/examples/Image/Lighttable/.src/FSlideshow.form new file mode 100644 index 00000000..337e6046 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FSlideshow.form @@ -0,0 +1,46 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,81,37) + Background = Color.Background + Resizable = False + SkipTaskbar = True + { PicBox1 PictureBox + MoveScaled(22,12,10,9) + Visible = False + Stretch = True + } + { panControl Panel + MoveScaled(0,31,79,4) + Background = Color.Background + Arrangement = Arrange.Horizontal + Border = Border.Raised + { Panel1 Panel + MoveScaled(0,1,7,2) + Expand = True + } + { Label1 Label + MoveScaled(8,0,25,4) + Text = ("Pause between pictures:") + Alignment = Align.Right + } + { spinDelay SpinBox + MoveScaled(34,0,7,4) + MaxValue = 60 + } + { Label2 Label + MoveScaled(42,0,10,4) + Text = ("sec") + } + { btnStop Button + MoveScaled(59,0,15,4) + Text = ("Stop") + Picture = Picture["icon:/16/stop"] + Default = True + Cancel = True + } + { Panel2 Panel + MoveScaled(75,0,4,3) + } + } +} diff --git a/app/examples/Image/Lighttable/.src/FStart.class b/app/examples/Image/Lighttable/.src/FStart.class new file mode 100644 index 00000000..c93266a5 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FStart.class @@ -0,0 +1,135 @@ +' Gambas class file + +Private iNr As Integer +Private aRecentDirs As New String[] +Private atxLabel As New TextLabel[] + +Public Sub Form_Open() + + Dim i, j, pos, lastpos As Integer + Dim sShort, sPath As String + + aRecentDirs.Resize(10) + atxLabel.Resize(10) + + i = 0 + For j = 0 To 9 + aRecentDirs[i] = Settings["Recent/" & j] + If aRecentDirs[i] = "" Then Break + If Not Exist(aRecentDirs[i]) Then + i -= 1 + Goto directory_does_not_exist + Endif + pos = -1 + Do While pos <> 0 + lastpos = pos + pos = InStr(aRecentDirs[i], "/", pos + 1) + Loop + sShort = Right$(aRecentDirs[i], Len(aRecentDirs[i]) - lastpos) + sPath = Left$(aRecentDirs[i], Len(aRecentDirs[i]) - Len(sShort)) + + atxLabel[i] = New TextLabel(svwLast) As "lblRecent" + With atxLabel[i] + .Border = Border.Raised + .Font.Grade = -1 + .Padding = 1 + .Tag = i + .Text = "" & sShort & "
    " & sPath + .AutoResize = True + End With + directory_does_not_exist: + i += 1 + Next + Me.Center + +End + +Public Sub btnOK_Click() + + Dim i, j As Integer + Dim sDir As String + + sDir = DirChooser1.SelectedPath + If MMain.ReadDir(sDir) = False Then + Message.Error(Subst(("The folder &1 doesn't contain any jp(e)g files."), sDir)) + Return + Endif + + For i = 0 To 8 ' write recent selections into settings + If aRecentDirs[i] = sDir Then + For j = i To 8 + aRecentDirs[j] = aRecentDirs[j + 1] + Next + Break + Endif + Next + For i = 9 To 1 Step -1 + aRecentDirs[i] = aRecentDirs[i - 1] + Next + aRecentDirs[0] = sDir + For i = 0 To 9 + Settings["Recent/" & i] = aRecentDirs[i] + Next + + MMain.GoAhead(True) + +End + +Public Sub btnCancel_Click() + + MMain.GoAhead(False) + +End + +Public Sub lblRecent_Enter() + + iNr = Last.Tag + If atxLabel[iNr].Background = -1 Then + atxLabel[iNr].Background = Color.LightBackground + Endif + +End + +Public Sub lblRecent_Leave() + + If atxLabel[iNr].Background = Color.LightBackground Then + atxLabel[iNr].Background = -1 + Endif +End + +Public Sub lblRecent_MouseDown() + + DirChooser1.SelectedPath = aRecentDirs[iNr] + +End + +Public Sub lblRecent_DblClick() + + lblRecent_MouseDown + btnOK_Click + +End + +Public Sub DirChooser1_Change() + + Dim i As Integer + + For i = 0 To 9 + If Not Exist(aRecentDirs[i]) Then Break + If aRecentDirs[i] = "" Then Break + If aRecentDirs[i] = DirChooser1.SelectedPath Then + atxLabel[i].Background = Color.SelectedBackground + atxLabel[i].Foreground = Color.SelectedForeground + Else + atxLabel[i].Background = -1 + atxLabel[i].Foreground = -1 + Endif + Next + +End + +Public Sub DirChooser1_Activate() + + btnOK_Click + +End diff --git a/app/examples/Image/Lighttable/.src/FStart.form b/app/examples/Image/Lighttable/.src/FStart.form new file mode 100644 index 00000000..357c0d8b --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FStart.form @@ -0,0 +1,42 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,101,65) + Text = ("Lighttable - Select picture folder") + Resizable = False + { btnOK Button + MoveScaled(69,60,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(85,60,15,4) + Text = ("&Cancel") + Cancel = True + } + { DirChooser1 DirChooser + MoveScaled(2,6,51,51) + } + { Separator1 Separator + MoveScaled(0,58,102,1) + } + { Label1 Label + MoveScaled(2,2,27,3) + Font = Font["Bold,+1"] + Text = ("New selection") + } + { Separator2 Separator + MoveScaled(54,0,2,58) + } + { Label2 Label + MoveScaled(58,2,23,3) + Font = Font["Bold,+1"] + Text = ("Last selections") + } + { svwLast ScrollView + MoveScaled(58,7,41,49) + Arrangement = Arrange.Vertical + Border = False + ScrollBar = Scroll.Vertical + } +} diff --git a/app/examples/Image/Lighttable/.src/FTime.class b/app/examples/Image/Lighttable/.src/FTime.class new file mode 100644 index 00000000..d807f8f8 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FTime.class @@ -0,0 +1,27 @@ +' Gambas class file + +Public Sub Form_Open() + + Me.Center + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub btnOK_Click() + + If SpinDay.Value + SpinHrs.Value + SpinMins.Value = 0 Then + Message.Info(("There is no time correction selected.")) + Return + Endif + + Inc Application.Busy + FMain.TimeCorrection(SpinDay.Value, SpinHrs.Value, SpinMins.Value, cmbPlusMinus.Index) + Dec Application.Busy + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FTime.form b/app/examples/Image/Lighttable/.src/FTime.form new file mode 100644 index 00000000..90070ebc --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FTime.form @@ -0,0 +1,58 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,71,40) + Text = ("Time correction") + Resizable = False + { SpinDay SpinBox + MoveScaled(2,25,8,4) + MaxValue = 365 + } + { SpinHrs SpinBox + MoveScaled(19,25,7,4) + MaxValue = 23 + } + { SpinMins SpinBox + MoveScaled(36,25,7,4) + MaxValue = 59 + } + { cmbPlusMinus ComboBox + MoveScaled(54,25,15,4) + ReadOnly = True + List = [("earlier"), ("later")] + Text = ("later") + } + { btnOK Button + MoveScaled(54,34,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(37,34,15,4) + Text = ("&Cancel") + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(2,3,67,16) + Background = &HFFFCCC& + Padding = 7 + Text = ("This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done.") + Border = Border.Etched + } + { Label1 Label + MoveScaled(2,21,60,4) + Text = ("Set the original time of all pictures to") + } + { Label2 Label + MoveScaled(10,25,9,4) + Text = ("day(s)") + } + { Label3 Label + MoveScaled(26,25,10,4) + Text = ("hour(s)") + } + { Label4 Label + MoveScaled(43,25,11,4) + Text = ("minute(s)") + } +} diff --git a/app/examples/Image/Lighttable/.src/MMain.module b/app/examples/Image/Lighttable/.src/MMain.module new file mode 100644 index 00000000..22771979 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/MMain.module @@ -0,0 +1,53 @@ +' Gambas module file + +Private sPath As String +Private aFiles As String[] + +Public Sub Main() + + Dim sTest As String + + Try Shell "exiftool -ver" To sTest + If Error Or sTest = "" Then + Message.Error(("To run this program, exiftool must be installed.")) + Return + Endif + + Try Shell "convert -version" To sTest + If Error Or sTest = "" Then + Message.Error(("To run this program, convert must be installed.")) + Return + Endif + + FStart.Show + +End + +Public Sub ReadDir(sDir As String) As Boolean + + Dim iCount As Integer + + aFiles = Dir(sDir, "*.{jpg,jpeg}").Sort(1) ' Sorted by file names, 1 = case insensitive + iCount = aFiles.Count + If iCount > 0 Then + sPath = sDir + Return True + Endif + +End + +Public Sub GoAhead(bOk As Boolean) + + FStart.Close + If bOk = False Then Return + FMain.Tag = sPath + FMain.Show + +End + +Public Function GetFiles() As String[] + + Return aFiles + aFiles.Delete + +End diff --git a/app/examples/Image/Lighttable/CHANGELOG b/app/examples/Image/Lighttable/CHANGELOG new file mode 100644 index 00000000..5cb10caf --- /dev/null +++ b/app/examples/Image/Lighttable/CHANGELOG @@ -0,0 +1,3 @@ +* Sun Feb 28 2010 mathias 0.1 +- Initial release + diff --git a/app/examples/Image/Lighttable/FStart.class b/app/examples/Image/Lighttable/FStart.class new file mode 100644 index 00000000..c3a9c61f --- /dev/null +++ b/app/examples/Image/Lighttable/FStart.class @@ -0,0 +1,92 @@ +' Gambas class file + +aRecentDirs As New String[] +iNr As Integer +iLast As Integer +atxLabel As New Object[] + + +Public Sub Form_Open() + Dim i, pos, lastpos As Integer + Dim sShort, sPath As String + + aRecentDirs.Resize(10) + atxLabel.Resize(10) + + For i = 0 To 9 + aRecentDirs[i] = Settings["Recent/" & i] + If aRecentDirs[i] = "" Then Break + pos = -1 + Do While pos <> 0 + lastpos = pos + pos = InStr(aRecentDirs[i], "/", pos + 1) + Loop + sShort = Right$(aRecentDirs[i], Len(aRecentDirs[i]) - lastpos) + sPath = Left$(aRecentDirs[i], Len(aRecentDirs[i]) - Len(sShort)) + + atxLabel[i] = New TextLabel(Me) As "lblRecent" + With atxLabel[i] + .X = 406 + .Y = 49 + (i * 35) + .Width = 287 + .Height = 35 + .Border = 4 + .Font.Grade = -1 + .Padding = 1 + .Tag = i + .Text = "" & sShort & "
    " & sPath + End With + Next + Me.Center +End + + +Public Sub btnOK_Click() + Dim i, j As Integer + + For i = 0 To 8 + If aRecentDirs[i] = DirChooser1.SelectedPath Then + For j = i To 8 + aRecentDirs[j] = aRecentDirs[j + 1] + Next + Break + Endif + Next + For i = 9 To 1 Step -1 + aRecentDirs[i] = aRecentDirs[i - 1] + Next + aRecentDirs[0] = DirChooser1.SelectedPath + For i = 0 To 9 + Settings["Recent/" & i] = aRecentDirs[i] + Next + Message.Info(aRecentDirs[0] & " wird geöffnet.") + Me.Close +End + +Public Sub btnCancel_Click() + Me.Close +End + +Public Sub lblRecent_Enter() + iNr = Last.Tag + If atxLabel[iNr].Background = -1 Then + atxLabel[iNr].Background = Color.LightBackground + Endif +End + +Public Sub lblRecent_Leave() + If atxLabel[iNr].Background = Color.LightBackground Then + atxLabel[iNr].Background = -1 + Endif +End + +Public Sub lblRecent_MouseDown() + atxLabel[iLast].Background = -1 + atxLabel[iLast].Foreground = -1 + atxLabel[iNr].Background = Color.SelectedBackground + atxLabel[iNr].Foreground = Color.SelectedForeground + iLast = iNr + DirChooser1.SelectedPath = aRecentDirs[iNr] +End + + diff --git a/app/examples/Image/Lighttable/FStart.form b/app/examples/Image/Lighttable/FStart.form new file mode 100644 index 00000000..b1309cd7 --- /dev/null +++ b/app/examples/Image/Lighttable/FStart.form @@ -0,0 +1,35 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,101,66) + Text = ("Lighttable - Choose picture folder") + { btnOK Button + MoveScaled(84,60,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(67,60,15,4) + Text = ("&Cancel") + Cancel = True + } + { DirChooser1 DirChooser + MoveScaled(2,6,51,51) + } + { Separator1 Separator + MoveScaled(2,58,97,1) + } + { Label1 Label + MoveScaled(3,2,27,3) + Font = Font["Bold,+1"] + Text = ("New selection") + } + { Separator2 Separator + MoveScaled(54,0,2,58) + } + { Label2 Label + MoveScaled(58,2,23,3) + Font = Font["Bold,+1"] + Text = ("Last selections") + } +} diff --git a/app/examples/Image/Lighttable/Help_ca.html b/app/examples/Image/Lighttable/Help_ca.html new file mode 100644 index 00000000..8bf512d5 --- /dev/null +++ b/app/examples/Image/Lighttable/Help_ca.html @@ -0,0 +1,60 @@ + + + + + + + + + + + +AjudaCàrrega de les imatges
    Les imatges es carregen en segon pla.
    Mentre es van carregant, ja podeu treballar amb les imatges que es mostren.
    Excepció: algunes de les funcions, con ordenar cronològicament, no estan disponibles fins que s'han carregat totes les imatges.
    +La càrrega es pot cancel·lar via el menú del programa (Fi).
     
    +Ús
    Totes les funcions es poden cridar des dels menús de context:
    Clic amb el botó secundari al fons del lighttable: Menú de programa
    Clic amb el botó secundari en un marc o a sobre d'una imatge: Menú de la imatge

    +Dreceres de teclat
    Hi ha dreceres per a totes les tasques, tal com es mostra als menús de context:
    +Ctrl+A Ordena les imatges alfabèticament
    +Ctrl+C Ordena les imatges cronològicament
    +Ctrl+S Projecció amb diapositives, començant per la imatge marcada
    +Ctrl+N Reanomena totes les imatges
    +Ctrl+T Correcció de l'hora
    + +F1 Ajuda
    + +Ctrl+End Surt del programa
    Arrossega un marc amb el ratolí: podeu moure el marc a qualsevol posició dins del lighttable
    Enter o fer clic sobre una imatge: Amplia la imatge
    +Alt+Enter o fer clic sobre una imatge ampliada: Vista a pantalla completa
    +Dreta Mou a la imatge següent
    + + +Esquerra Mou a la imatge anterior
    I Informacions de la imatge
    + +N Reanomena la imatge
    +Del Suprimeix la imatge
    + +ESC ho cancel·la i ho tanca tot
    +
    Visualitza i compara imatges
    +Quan estigui oberta la vista ampliada o la pantalla completa, mou-te amb les tecles per les fotografies (els fitxers grans triguen un moment a carregar-se).
    + +O salta a una altra imatge: Feu clic en una altra imatge de «sota». També podeu desplaçar-vos per la «taula».
    +
    +Ordena les imatges
    Si teniu imatges de més d'una càmera, és útil primer ordenar-les cronològicament (Menú de programa). Aquesta funció llegeix la Exif-metadata de les fotografies.
    Per ordenar-les manualment, només heu de moure els marcs al voltant de la taula. Llavors el podeu reanomenar manualment com per exemple «005 la meva millor imatge».

    +Reanomena totes les imatges (Ctrl+N)
    +Una bona alternativa a reanomenar-les manualment. Si he mogut totes les fotografies als llocs desitjats, els fitxers poden ser numerats i reanomenats en aquest ordre exactament. Podeu triar entre afegir o esborrar els noms de fitxer antics.
    +
    +Correcció de l'hora (Ctrl+T)
    +Quan etiqueteu geogràficament les fotografies o quan compareu fotos de dues càmeres, observeu que una càmera té una hora incorrecte? Cap problema. Amb aquesta funció podeu corregir la Exif-metadata de la data i la hora de totes les fotografies a dins la carpeta.

    +Nota: Manipulació de imatges i miniatures
    +Quan s'editen les fotografies amb un programa d'edició d'imatges (p.e. GIMP), les imatges en miniatura de les fotos no es modifiquen. O sigui que el Lighttable seguirà mostrant les miniatures de les imatges abans dels canvis. +Solució:
    +a) aneu a previsualitza al Gwenview, Dolphin, Konqueror o qualsevol programa similar, així les miniatures queden actualitzades,
    +b) elimina totes les miniatures (de la carpeta /home/usurai/.thumbnails/normal), així les miniatures es crearan posteriorment.
    +
    +
    + diff --git a/app/examples/Image/Lighttable/Help_de.html b/app/examples/Image/Lighttable/Help_de.html new file mode 100644 index 00000000..5c7ef22d --- /dev/null +++ b/app/examples/Image/Lighttable/Help_de.html @@ -0,0 +1,74 @@ + + + + + + + + + + + + + HilfeLaden der Bilder
    Das Laden der Bilder läuft im Hintergrund.
    +Währenddessen kann man schon mit den bereits angezeigten Bildern arbeiten.
    Allerdings sind einige Funktionen wie chronologisches Sortieren erst verfügbar, wenn alle Bilder geladen sind.
    +Das Laden der Bilder lässt sich im Programm-Menü abbrechen (Ende).
     
    Bedienung
    Alle Funktionen lassen sich über Kontextmenüs aufrufen:
    Rechtsklick auf den Hintergrund des Leuchttischs: Programm-Menü
    +Rechtsklick auf einen Rahmen oder ein Bild: Bild-Menü

    +Kurzbefehle
    Für alle Funktionen gibt es Kurzbefehle, wie in den Kontextmenüs angegeben:
    +Strg+A Bilder alphabetisch sortieren
    + +Strg+C Bilder chronologisch sortieren
    +Strg+S Diashow ab dem markierten Bild
    + +Strg+N Alle Bilder umbenennen
    +Strg+T Zeitkorrektur
    + +F1: Hilfe
    + +Strg+Ende Programm beenden
    Ziehen eines Rahmens mit der Maus: Bild lässt sich beliebig verschieben
    +Enter oder Mausklick auf ein Bild: vergrößerte Ansicht
    +Alt+Enter oder auf das Großbild klicken: Vollbildansicht
    Rechts gehe zum nächsten Bild
    +Links    gehe zum vorherigen Bild
    I Bildinformationen
    +N Bild umbenennen
    +Entf: Bild löschen
    + +ESC schließt alle Ansichten und bricht alle Vorgänge ab

    +Bilder ansehen und vergleichen
    +Bei geöffneter Großbild- oder Vollbildansicht einfach mit den Tasten die Bilder durchgehen (bei großen Bilddateien dauert das Laden etwas).
    +Oder zu einem anderen Bild springen: "unten" ein anderes Bild anklicken. Man kann den "Tisch" auch scrollen.

    +Bilder sortieren
    Hat +man Bilder von mehreren Kameras im Verzeichnis, ist es nützlich, die +Bilder zunächst einmal chronologisch zu sortieren (Programm-Menü). Diese Funktion wertet die Exif-Metadaten der Fotos aus.
    Manuell lassen sich die Bilder zum Sortieren beliebig +auf dem Tisch herumschieben. Dann kann man sie einzeln umbenennen, zB +"005 mein liebstes Foto".

    +Alle Bilder umbenennen (Strg+N)
    +Eine gute Alternative zum manuellen Umbenennen. Hat man alle Fotos in +die gewünschte Reihenfolge verschoben, lassen sich die Dateien in genau +dieser Reihenfolge mit Nummern versehen und umbenennen. Die alten +Dateinamen werden hierbei nach Wunsch entweder angehängt oder +überschrieben.
    +
    +Zeitkorrektur (Strg+T)
    +Beim Geokodieren von Fotos oder beim Vergleich von Fotos zweier Kameras +stellt sich heraus, dass die Zeit bei einer Kamera falsch eingestellt +war? Kein Problem. Mit dieser Funktion lassen sich die Exif-Metadaten +zu Datum und Zeit von allen Bildern im Verzeichnis korrigieren.
    +
    +Anmerkung: Bildbearbeitung und Vorschaubilder
    +Wenn man Fotos mit einem Bildbearbeitungsprogramm (z.B. GIMP) +bearbeitet, werden die zugehörigen Vorschaubilder nicht aktualisiert. +Leuchttisch zeigt daher weiterhin die alten Miniaturansichten an. +Abhilfe:
    +a) in Gwenview, Dolphin, Konqueror o.ä. die Vorschauansicht wählen und die Thumbnails aktualisieren lassen,
    +b) die Thumbnails (im Verzeichnis /home/user/.thumbnails/normal) löschen, so dass sie danach neu erstellt werden.
    +
    +
    + \ No newline at end of file diff --git a/app/examples/Image/Lighttable/Help_en.html b/app/examples/Image/Lighttable/Help_en.html new file mode 100644 index 00000000..4148cf8b --- /dev/null +++ b/app/examples/Image/Lighttable/Help_en.html @@ -0,0 +1,71 @@ + + + + + + + + + + + +HelpLoading the pictures
    The pictures are loaded in the background.
    While loading goes on, +you can already work with the pictures that are displayed.
    Exception: some of the functions like sorting chronologically are not available before all pictures are loaded.
    +The loading can be aborted via the program menu (End).
     
    +Usage
    All functions can be called by context menus:
    Right-click on the background of the lighttable: Program menu
    Right-click on a frame or on a picture: Picture menu

    +Shortcuts
    For all tasks there are shortcuts as shown in the context menus:
    +Ctrl+A Sort pictures alphabetically
    +Ctrl+C Sort pictures chronologically
    +Ctrl+S Sildeshow, beginning with the marked picture
    +Ctrl+N Rename all pictures
    +Ctrl+T Time correction
    + +F1 Help
    + +Ctrl+End Quit program
    Drag a frame with the mouse: you can move the frame to any position on the lighttable
    Enter or click on a picture: Enlarge picture
    +Alt+Enter or click on an enlarged picture: Fullscreen view
    +Right Move to next picture
    + + +Left   Move to previous picture
    I Picture informations
    + +N Rename picture
    +Del Delete picture
    + +ESC will close and cancel everything
    +
    View and compare pictures
    +When view or fullscreen view is open, just move with the keys through the photos (big files take a moment to load).
    + +Or jump to another picture: click "down" on another picture. You can also scroll the "table".
    +
    +Sort pictures
    If +you have pictures of more than one camera, it is useful to first sort +them chronologically (Program menu). This function reads the Exif-metadata of the photographs.
    To sort manually, just  move the frames around on the table. Then you can rename them manually like "005 my best picture".

    +Rename all pictures (Ctrl+N)
    +A good alternative to renaming manually. If you have moved all +photographs to the desired places, the files can be numbered and renamed +exactly in this order. As you like, the old filenames are either +attatched or overwritten.
    +
    +Time correction (Ctrl+T)
    +When geotagging your photographs or when comparing photos of two +cameras, you notice that a camera had a wrong time? +No problem. With this function you can correct the Exif-metadata of +date and time of all the photographs in the folder.

    +Note: Image manipulation and thumbnails
    +When editing photographs with an image manipulation program (i.e. +GIMP), the thumbnail pictures of the photos are not changed. So, +Lighttable will still show the old miniature pictures before the +changes. Solution:
    +a) go to preview in Gwenview, Dolphin, Konqueror or any similar program, so the thumbnails are updated,
    +b) delete all the thumbnails (in the folder /home/user/.thumbnails/normal), so they will be newly created afterwards.
    +
    +
    + \ No newline at end of file diff --git a/app/examples/Image/Lighttable/Help_ru.html b/app/examples/Image/Lighttable/Help_ru.html new file mode 100644 index 00000000..d119cea7 --- /dev/null +++ b/app/examples/Image/Lighttable/Help_ru.html @@ -0,0 +1,29 @@ + + + + +Справка + + + + +Загрузка изображений
    Изображения загружаются в фоновом режиме.
    Пока идёт загрузка, +вы уже можете работать с изображениями, которые отображаются.
    Исключение: некоторые функции, такие как сортировка в хронологическом порядке, недоступны до загрузки всех изображений.
    +Загрузка может быть прервана через меню программы (Конец).

    Использование
    Все функции могут быть вызваны через контекстные меню:
    Щелчок правой кнопкой мыши на фоне программы: Меню программы
    Щелчок правой кнопкой мыши на кадре или на изображении: Меню изображения

    Горячие клавиши
    Для всех задач есть горячие клавиши, как показано в контекстных меню:
    Ctrl+A Сортировка изображений по алфавиту
    Ctrl+C Сортировка изображений хронологически
    Ctrl+S Слайд-шоу, начиная с отмеченного изображения
    Ctrl+N Переименовать все изображения
    Ctrl+T Коррекция времени
    F1 Справка
    Ctrl+End Выйти из программы
    Перетащить кадр мышью: вы можете переместить кадр в любую позицию светового планшета
    Enter или щелчок на изображении: увеличить изображение
    Alt+Enter или щелчок на увеличенном изображении: Полный экран
    Вправо Перейти к следующему изображению
    Влево Перейти к предыдущему изображению
    I Информация об изображении
    N Переименовать изображение
    Del Удалить изображение
    ESC Закрыть и отменить всё

    Просмотр и сравнение изображения
    +Когда открыт просмотр или полноэкранный режим, просто перемещайтесь с помощью клавиш через фотографии (для загрузки больших файлов требуется время).
    + +Или перейдите к другому изображению: нажмите «вниз» на другом изображении. Вы также можете прокрутить «стол».

    Сортировка изображений
    Если у вас есть изображения с более чем с одной камеры, полезно сначала отсортировать их в хронологическом порядке (меню программы). Эта функция читает Exif-метаданные фотографий.
    Чтобы отсортировать вручную, просто переместите кадры на столе. Затем вы можете переименовать их вручную как «005 моя лучшая фотография».

    Переименовать все изображения (Ctrl+N)
    +Хорошая альтернатива переименованию вручную. Если вы переместили все фотографии в нужные места, файлы могут быть пронумерованы и переименованы именно в этом порядке. Как вам нравится, старые имена файлов либо привязаны, либо перезаписаны.

    Коррекция времени (Ctrl+T)
    +При геотегировании ваших фотографий или при сравнении фотографий с двух камер вы замечаете, что у камеры было неправильное время? Нет проблем. С помощью этой функции вы можете исправить Exif-метаданные даты и времени всех фотографий в папке.

    Примечание: манипуляции с изображениями и миниатюры
    +При редактировании фотографий с помощью программы для работы с изображениями (например, GIMP) уменьшенные изображения фотографий не изменяются. Итак, программа покажет старые изображения миниатюр до изменений. Решение:
    +a) перейдите к предварительному просмотру в Gwenview, Dolphin, Konqueror или любой другой подобной программе, чтобы миниатюры обновлялись,
    +b) удалите все миниатюры (в папке /home/user/.thumbnails/normal), чтобы впоследствии они были созданы заново.


    + + diff --git a/app/examples/Image/Lighttable/LTicon.png b/app/examples/Image/Lighttable/LTicon.png new file mode 100644 index 0000000000000000000000000000000000000000..3faf640ba74b45e8899eee605064ef163a5ee6f8 GIT binary patch literal 296 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvtpJ}8R|bav_V)Jw|NjRHI3NGN zfB$|4AP2~+X8;oa8-N(d1cLf^h88A3dZ(w0V~EE2xX{p0 zAU7%|B0ec5Au$e!`X+YAC&k4l#56S5#U;chB*k|2w)IcyO-zadvfDaaTG|`q6Jr`0 z8WIwtdna`EPwI(JiUo>v_Ox|$wZ_HA#3jb|P3W37bIQ~ile&64fJOmDrq7-_b>^fv z5Ibhll>Uj6`+zEeMopPEamv&QO|5k?anV4PO)a%uJ#Fy`QLzcpZJo^xP1P}p(LiU^ zHdM8DHpeDJ#l%Oqv^CZ>Rs(GWip0f7H@7v!#zzC)21Id*(GCs{KoTepmdLvBSsq9X)yQ@bUfo5AWW4aObYQTX*i+vSs`Fja$})zd6kT z3=Y4NAirRSu9wT08TY=P@aprrRi9tY`V6E#eV)y<_VefU>pp#1x8cj@&tEoe{`zI> z$}eA6fBN?A)9U4)zU($#)Mhy_1_4m#}+JZB6G# aErzuT!ofv?v$%oIWbkzLb6Mw<&;$SrEfq2V literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/hand1.png b/app/examples/Image/Lighttable/hand1.png new file mode 100644 index 0000000000000000000000000000000000000000..ab56c037b5d150241f983b5b94a24abef1f33c57 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#=`Bp4pvH+~MJm`Z~Df*FpnOj)%T$jkI}aSV~T z+&b|z?*Ri2XWuC+Z^W;8Di`#TGvsMYoKA9+#ImT1tK9ZC3o%6;b@%K@6;?g{dslm) zd(98FE9_TQS{M`@j=%POAlB#mOqYFM!A_MsPi?o)c&zsRRgZJsz7NiC(^tIvRDC*z Xe~-9$n@f1N8OSM~u6{1-oD!M<4sk;u literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/help-contents.png b/app/examples/Image/Lighttable/help-contents.png new file mode 100644 index 0000000000000000000000000000000000000000..917ef83e7f1ca4ff020a5bd33b979e698cc1b0e7 GIT binary patch literal 742 zcmV0u zcgvZ(<>v1BHhIhr8!IM8Zk4&_|NsAlt>HR-&^dh2Nru$^{{MTZ-whfnDoAijh}Nmb z>{pN3PKwt+fz*t%<^KQwU6k7m8YwMHaz=*PO^Vr5j@#?<`;oWkNQc=E8YwYPbx@Aq zuFCMI#_v&&;1L`vI8=LFm*;4n>B!voUzz7#m*-uV=M^C^NM(#?pzepX@|(f*X`%0F zq3&m(?HVLBQEilQr}d1u_kXbVbg1-lr}c5C^d2ZUQ*4uZuKS0!`+l+ed$0O?uKOV@ zJ4eBfp@I=fwKAD>;A&p{4+X27$Pn(OL9<&*Gh)d zUAoL~Ve&)?raATTJ{KZFM? z5E>Q^1QC(p;ZeL`f#{gn*x0!Egv8h+KCnP?N@{9qT6#uiRw_SOAUh{FH#aXox1g{{ z04z{kQd(MCR$f|KQ7H%(sH(21sjaK8scC2s0t+-YH8;1kwlxERFi=3Gy`!@W3c5uZ Y0CxpDmCMY#o&W#<07*qoM6N<$g1#V7)&Kwi literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/lighttable.png b/app/examples/Image/Lighttable/lighttable.png new file mode 100644 index 0000000000000000000000000000000000000000..4497010b76ecf7b2e5e8fe76bdbf3efe37aaa9b1 GIT binary patch literal 6536 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/zoom-in.png b/app/examples/Image/Lighttable/zoom-in.png new file mode 100644 index 0000000000000000000000000000000000000000..660d752a49824dd934984b4579b91b727b68d0f9 GIT binary patch literal 710 zcmV;%0y+JOP)C@hm6V8zg@}uZhl_`XjEjehh=+`ehm4Dei-)ng z!Lqx-h>M4cjEZq{Zyurl1!o`Vekr zi;annk&SS2ZdX}VSzA|CS5sMAS5;S2R#;S4SX9l{-Q?%#&)DG8-Q}de+?l!Bnz`Df zzunK;<+RD)oxR<)&E{N$xLbs{u*&3`y4{$$+`Q4~Ux&H>|NmNqx2wkDk+j;KyV|zR z=3RxjUWT|?g11+Jwx7M;p1azPvesFHw@`hwhOE?yuGCI@vr2ff&)D9l!QFkl(yQIx7$*FwNrkzm$=*7;^^1i;>y_Oq`=>%!r-UF;n?2e+TY{h z=IY1S=)KbBzS8E%*y!Ko>f7Mtm6(>?;Nt1)>$$wPy}!JAdw7S6gtxf1c6f83qMvpi zRo4Ij04{V=PE!C6gU0hn?E6CX4(fly^25e|`}>3H8}=BIg`#6(;~X91?Gh4&ML^by zCMBn&dU>U#Ca1HAfgL8Ek(rg9lbe@cAR#FQ7Lk@IEGjCLm6IOhX;xHfAqy}lYU}6%01S317%7GyO8@`>07*qoM6N<$f`^TGzyJUM literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/.directory b/app/examples/Image/PhotoTouch/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Image/PhotoTouch/.hidden/screenshots/phototouch.jpg b/app/examples/Image/PhotoTouch/.hidden/screenshots/phototouch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..477d6bafb546b62f41548761b9e297c9e4412666 GIT binary patch literal 150309 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%0AWaO?PkFjX*LXB1j2(n$c-E@-_^!N1m;n zII26jVRwl8D?~KY;!FL(Y5I8JKuu%gj|UFI>l{+U#V4(#elO{q-L@#S+%Tr%#>f_TQvU#vRupmF2jk5fPvt*3U_Q}<#jHo>%(}X?HfbhTnJ<^Mrlrl{ zJ-gQQ=#d7X)Rs+G50q;#`Zu=7sVX4u7`|^M0>ypX+V4S=(`NWgY zSqoRC@oeN=l&Vh_rNTpT#)@zRP*ljxBkLK}pHNb&Y-g8X{U~LVc3~Q{mS_ObB0>@h ziXF6Rd2h&8g=LEW@=;A=s>}a+jW1+E4Y}%?^hkeQT3L2 z*v73t6F4d5tlq4k-f zP0;ij0a4<28GJXRSClsm}7A3*NEwkh9Jia>aa%F(Q2(vA~c|nA=bMs+%%xZ z)10-|f@&}F&?wUt34iwq=NKMS(0&H)o?QP<_^#>ozX0Dw(O9EW(wjt=j#D}#S`CF+ zkk?A{B~BdnI?hYLvg_uTei!|r8$#nu6@k+y@h=!6V6nCPP)RZE9WOiM{(6CL z(bJO$zKD*k|JY2gVOpXy~tA0=&Omt4LVF<##$OP@4PEY%8yq8iw#j_>4iw!Rt-IxMz z=j2eNkjuIpMw3FkB-L7miNQL!FVGnTSNgid%;otbW+$ZnY-ubc{KVBn{M8(clUi^- zKFuHc@B-M^@G8MLcmn=}VzX!tyG#?VZW~gl=Zf1$vD0({ScxOe>j+tjO~9}u=(B`h z#VZ8Px;}-#Ax&g0<^eU|T8bh)g&W86W;M9l6xeIk*@p*Yqg2krnSjzX?IwTtsRD!o z7L88NmOwEW-b7Iv0)O@uT}uilu~kk=<1J#F%egBJ>8epHR&0|2%CJGC@>#e@IfZl! zQyJkA&!wVBY->JOG)xjyZw87*tMu7rmo}*%*mob%F2Z1gxX6CtuY6+C02X}UIk-X1 zP#a`m&4f?wHBf_xi3r*zivbe7oV|#ANkDN0gy;mY));tgOjIQw4<`Vhz`dn#i1Un6 z-K`Rt(dmcqj0uILyNVS*ZzGgHZZPciXKW+K?B=STB$J9%&oEgnCnKRlE?q7h*9)89 z-@9Fqhp@nxp7OEHpL2T-_%T=4F-D*l`;<8jZz*pS^iikpSJiI^>^rEQfKf$HhMyMQ znQm~U_reMBsB0z+9=hbz80OL8xg33xyJE`ThH;+&F`Id6_FmJ;~>KiO?Xh9~gLn`tiGLP4iLY;FQ&T)SZ zLmEm8?(e%0gE_|pVf{NU&0SuGbrJ0nKg1@yZcgjrvWHXU*T0a5pXWF_vxo8-OAjnc zg5S$NPDpR~;G~&iaEr=4xK2BnaC*mpU*uKhVZgrA00M%BcK?la0>e$@W0reWadM@WE`CS|JFoV+}nyc`!oju5`(cs97=lIB0yxC^2Ik7TQJxG}gUX|a3> z{Hdho?#$Ltb7f8;2xv56Av>>Y5zpt)FB}-0Ot6VC{8>9!R(&3C?#2p=8-8$tj_vzqWa^#Q@v$!3 zCJZ@hQTX;|?F!lKTd3ad>_8U^E&S(WHrT#O^LS#gc#MRDa>w)48+95Wca@V2fk6Gu zv|#;N{PMo7vFf`6wDauC#*+H4(0RUeu%0O$(hjO&SpS-%h;%vU_d?EKJamz#YQ13h ztkTgY+r5eyI96~ojEgjr0W`U1Dl7(oD^hPQ;ezHfT4{^8Qwjd+ z{S+vU2mR9Ar&hL18?6+9;`x!zWP2k;WjJG$s=AX!uIGG1qq(T8~ve39f&2%UGAE;p~A}-D5s$??k2m8WDMtXI~aFldxibAve&-jxoe5 z$EqCN7v^3Obd&8I_z?bHDb$j?N#rv&IThG?+}W*7lPqf$3=G z0agK&dy!~PGf%hEo|H2>bqs%PeS`YwsUB{ip<6q-O*8EXZXI1im)Wx;2Y(QSO%sxuDT|r5u>80Un9*M5K2R+dvg^hnRs>OR~mi2 zGY{YXQOf46czCHR>sr#hA8e zA9vY6&9Smo3&8Vg2GTtK^jyR^F|*jP8F9u+)y(m6owxuQa2b5=Jc5hLI`4q>V*RX4 z8<>lTG<9qcvu{>1Wi~@ZinHusKmbXqZ_3i6WHBNlXA4ccXw27K3vd`nKL?h!N3!6L zKDyk*Hey4}TRVo=)YR+bzjD=6RoMH0@g;utYv6-Rt8 z_jYH*fm9Q&k$>01L9{XE_t;jq&KEly<@?eZQyN1MdW@VTg=tvf45>P{kqA-_9`!XqMr2^YWyN=gXnChi(a zlC>>sy|K?kg1PX;Y^8QXzQ^z=V5L-XEPke?1#LeBttza3RDagBsUA!0qPs6e+b813 z)k*f2`I}Y%h!zFdTCM@0 zWc2y2B>no5o2RUO=4^be;S=PB56@1}JdTOY_W5lv!sV3TJsW`? z%`VF5mBtae!&+-w)F;)z3r^^ZpcBG>W<~V#S0k*aj$3=%K!wT!jDnI;cJW9HZpfsv zNkp&nN3B{O!|8nFIUbzm4k4QKlr`yuXPTqOoCr?!gskc%=w&8TMv?A}c%FkK$GOxz>Dj*Lz7A}J5?hffqvSYSNX1%7IW~f( zd^&7^jagw-UZA8amR{yZIQz~iM8hsG0W__DCJ)gkFE$$#(Gl(dIfJtfvsy|>p{!MA zzo8n0*^eBNjj|IPK;GS%jKo5cVaW2Y_3}VcOJ0zHlN$HZ^R{b|)i;e=V)3zJ;-A6! z^aCMOa^E1XwO$=Jbx*u(Hu`jEZA+OH2vs0QseSbB-nlQB*%K>w2DFa=yWPyFU zZl*R$cKc(UA)sGR&Dv2`xijqLQZ=&?aJrG!n#+eO+U)$Q2Sz>S#F#Ua%{4AuyWm#a zrewo9`RyFz$m4A1*%`(h?RoL2a_=K*a6!;kd~@hyw|xnom`^K&6zN#T=zY4Cw;)92 zP{Tu?6V&9>B?)%%TBJ+EYW8d*XqSBD67h+y1x?AE`Q_zbPSORhu|bU#GUDAFlHP)A zIO{oAkzSbGP9P6kH<53NaBRYoH&l+O_G6~~z&ggK>2z0Zq-2Y=BnP5H*+5Xy*icK@ zl&&`M;#e<4*7{~w&E=@-OESWy2@C*B%CQEaj}bQ69$7fw=d7*AQ2Kt~vmlS?t*Zn) ziVo$+E;JulcB16^OAN?t9>~7u`^OK7UOCh7(t6Qbb%Lu~7j7xvvi%`5L5!Hc$+qJ* zspB|!Ye?B8GmJ1i+Qr6&*fA&@J(j} zuqi1XsZ@iM;L{QdRuV1=$QH{MOGPJC5}0u22Dr0F6b?(XCt24%x>bYZ2w!^T?3E$5 zb!-rz9rN{0yeI^xqS!x>G9*j5xu?FH!Vqj0l0v?m+yJPIt4X` zA3nIcz9@G-G;n;NA6)c4<{NA!qsS<|MGZDWeev#2n(%fAPIi|2)Iv`=6aE{0+h2rK zNNwWFlD|fWWb5ZoFQY>iYrR4^FNdlb2H_irjtQ?00dexnLC-_?9Nv7FSE#*xANSN4 zWA5duQn(EjdJ1{=8%BOb%@z21li^Fb%LKu)c9NhIGBVg_xNAKxa!|U~60qQVoi}P| zO2VBLMqY$j1u!%7HlWd~#ShkHP^@#*5H#Bk$RHe$9!7UQ(WPeREew)Ata@(qOmhX2 zzr8nCYQV#HPP&%GK)R}o1BOy0a`3U?-Oy`-=0usSVCACG;V?KS_9v)csZ$=pJ2R`! zWHmn}$J%Hb5SDuho+6*&ZL^_f60Te^_3lL;`8az2UCFYiEoT3hX3d2H_afG=Ky<4O z6;kStt#IZF&P#VB{#3D-h!p+_J!t|itg^0%zjDbRNnD)n-7U8`_#J6Rh=etUHKb|2 zf-dcf-T46M8-aS;d^~!f8}{yd@geY?5&WbC#l3wT{2E?p_T9_-n+kq*_+6|LSX!L0G|u4)4Y66oxgHCA!pAP*+(XLA%Rq%^vEj@#6lKz-O2FI!+umj@cLan8eJB-|VlI zcQTV0ZKTB*wQf?3{JO^@9S6Y{FNN1dIZHb@f8rI%9Rs`&h5$7O(TZj~{Sa-HAqK|kkp?s^L z?d~SScYj~8grz@VT}5N3k7AZZy)$|7;Lk73Z%)|NOW#@Oe*ss{{RLDs1YC)#W%3Y8 zO}{@FE7uLV0vcCmk%-SsLX!)3A@8EMzv|ci1sJ@X`1$em8=F7FjK6@l;?aKrx5a-c zcmD+>y?p-k%gMihVf8zoAWuJ*M&0<6aOOLw?H`3q1Ba4ld}q+iIfA!;f4lmz_Kx@N zoiBV$9OI{ae}Kzb;{Wg2qCY}+`2PYX&Hn0W*F`Ulrvz=r6hZ-gt~5WjT(0t`?5N>uy{5VBe<*ni-iqHXq%+w@( z?r%Q*?*VXf>skEC%+daph>9LA@C#EkXB;p^`tJ%Xf9Ei9peD8dYr%;BNbvNn+`kfF zFTK(H_~AGErO&(%ej)S?WBnK2xLV9!dMkP3KbBI`{*Mv&{$oV<*Z;0R>;JC*Kg$1~ z_0MdaZu94a+_Gb;`#;m6o;E`bE^YifmNK*Ui@Ds_|981}p8dxdcm87wm_V#!=+i~9 z@;e)n|Me1(O%Bub{uP{gdCA@J|D(F_6E8LHikUH&YVcoS5qajbCoU~^JUDvu^x5P7 z_5Y8g)0!E-bh5s4{W~?wT(FdXL^bYZ=gj##AD-X2YssHM-Dlq^dhm_^^&9suMwx`(H+b)~cpxlfP$*yj>z)+W6PO!I$0zqp!_ez4XsuF=u`$tVj<1O8T#o znXbxof|kDkng4OS{0{pvfvGmzADk<9UNf!9;~lW{Fj)P+7OS}PEbM=|?7#N9^B;Rj zR9#n%emm9Xc4zp%Pos*=W|=0I9{Z2IE+^;)znS?9Q2TcxbEX_p6aOQ}sna@I(5XYD z8RTErk|Y&TxU$8MSN;VAx&BhV&fF@?S>KBPtD~8$!9S8*m)1O)jw8=>U7VXKXn8ZU zlnlD`_Vksb&8N@m?rYeq7(QZd?WgDd02j}Qf4lbJe>B|qN9*Iqygx?&-QX0HiQ

    4Dm&jdb^r3b`myoxKbtz||0&3bsqfK+KU2(IBFKDMl$q5q zOUW0t;}n0h{vW!&GoY!hSvz#4SxBUJ1VR%eL3;0mnoy)EMIb?t5I{hsix`Sjg$NiR z0YVi41r)--X696 z&GV9l*b}k;Dx{GCK=SzR{6s``|J=&?OKIfZ?B)=8NV#`#X9l2oyALckZk3QFy7eh6 zb|W?RcKAZq>|Z+Ozk7`MW8r@(h(ZSQ#VMwk^hF>;q??G`ZnHSQ0n4w&`H zKOkUF&9`s=qgWGx9~1fm3ix{w-SJdhVZ-p=IT_Ip`xpOiaUaEp>|aax!XD@> zz)Rfu_P-i!#2;w?ujHO(zWpA18ZD5+`3K|=V1546`IJ@7{7VOH9>99J1DgG%2>jdD z!qGpIJ(3gI_cm4ZXHx7oGoWBx4VTr`!)g&qdW~QIfHwPLZ^Hs6^J5R#{+WsmvkYxb zz2$$m+FL;CruF81{!y4%7RfvTQ=<6vRi(Rs_g^U5v#ee3@_)td?+Kvi{~r8XrNVjt zQd#|aO@Hq_CHoz|r_X~9_|z^)ithHdzoD!hdJ->MPIw4K8v;AMs2$J?X~2B__khUQ z?|LOH75nde(9?_;L&gOBNYYw0N?o@AVXb~9;7N>K>iWF!JO9}aXDiJpNB(}D z%QeBs@H_u(I9(5;{P_j?9xynizeTMk{hZxpwu#`_zh)wAec>86Fe`vA*Z)gU$p6un z9sf%Zhu#Tws}rn$Nd9775#0lNUw|vS)X>w=`cnds`&wYXE{xm#cifVZAK#K<|Ci8@ zXk@4d9LCxIPQ*PrT>SXSp6`Q88Po}xz;`*(v%n|n8pYM@dZ0`JbHU`4dH->?!~wg& zgb`D?6|i>(fWu%g9V!pu4UHFyMxADFLUOzy8^oQcl~Oyia)Ra|CBouYoh4)WDgzQ4 z5TIoK=v***=M}QeFumR3@4zO*ZcdY)e;q}^4tWVxF2ZhUXmW3>pCSL@P}k889Va|dbz zCvg>`SeN}f9QdVsoe|X83#$3wFr>aLr{n90EAPfpVanO9h3kp-S)yrWSqJuF)# z^@5+1Mbnt-j7NCaCbkCfJ3b)z$@QHz?lCC+qDG4CwqD*XhNy3AbPnsGMpfArco)W%+3CX^)}^a)xpDe)g-wxT zq*if|e~J5S)NRS2%2pZv>ieF&FP|6rpkB)e;rC)0SF^07(zen`8;jxfmd!&=>sR@L z#EsU#NXI3pdKvo@vcV(}&7I-+q`mWVKlF>|Yqm@M&zdhKe?ODiHORV}aGVyM#a#T^NEJ!WaG<}IO7U9RwZ0mQ_K6E+F2?8KTV6yV0Uq8=os9$a&*G0;f z7g%saOKs1s%(gVR9ER!4e5loQpfohOM~?)_LJE~$wp=%ls!6T&k61!_y6UBMSEqxcyLho2jpCRBec;qlR(v#e9WX9qWk9Yo}RGl zR8a#XZPR5yjeKpULgq7?Qy8rkT6Bd*H{$oZ%HS^DR?n(X^^^NU%`IE%um^=*U!<#5 zkY+%Nn6{ur(JP!Gy3vzL-IU}vU3c0PF9shW(Nag88r)sSq?< zpB3T0*1c{a zbVEv1bpuGhN{7qsX{_r+=i_g_VO8}aT!E!`?!j$7$z|f7e)TSJiGWX`x1*k}y=|!z zTOSuY-WU5>-rY96eev3>KcJ_vhl+tuzlr{gTLv(; zfGsxY)bdC$w&&w5zy|&U+I6M7G5?mv?b@51HzAYCV9APNFo)|zHu|sunOG7)D$P8G zIde1$t#sRTl+@s+siV9fS>xxIbHc^TRlQT0eEN{L4bNjt{sJ*XlKV|Az?jUh3qb|R z(orQDCJdoX0AuP)q~Ip9W4@+0SPz!`kbpEV0abX%K_`gJ;HDy38j~Rx5s|VV3sP03)b*<=6GtU&qGH<{}1?uEuWs^<+8R z1b9-r9J>PC@IRa87`Z~Gn;u!FRhk~$fqgsj>Czmr!SzyP{ol!~v|Hv)r!mbE)fJAf z+dN!evIe=Nf3Ms0ltZg7{=($@$sQM$3&%;U5ShrxtjrnrVrntuyX{b;&?tlRr z!RP0j_*2&os2UTCNXDHU-fQGefXpBVl>gjSKA+-sdtI&=rq9k=ml%tTalP0Df?@AkqdAs%tbD@ z<~#eT8%7cO&3;Qn5!2a($IW6!&0*NOo-Oy^HOe^m%oi>rd-?OfzQ<&A>Vo$Ou)`H zQSlc=B!`j6p*pcE%(?2{;q}&DS@`~%DB@7a!=&5?M$0-jLmNt@+uiSz5-*;fb0+^Z z8Pe^m)On)>X`KtNhd)g8yrtoRiQZ}4zyH2!Pf$dU9oKB=hM(bsNeZMreogXQ9Pu43 zHBm(PEhm^LXCaJS9$9)x7h032od8xM!wP}Dq^$GYk6^t*6uUjNun`FGX%aFVUr+P4b9X54$PAD(L$ z2CyAp*j$rsIj1gKLhNES_ii~XD|2EqW~%SC-QM4XW*orD>^qd20WY>S`z6%0>6IeeyjMvASMJ*zjpftGn*~lJK#Uauwt` z6X-hLIUD*Na@#eu{k*0MU$~QV>qpy4i3~ybgK&z8l(k^!4d)Be^)LOy9dCS8{n7C= zFat$~dLmGdGcy+U1W6f)I{EVp_LFT`0=deaWZg<;6Ta=$TV07@YEG~Mk8)01^RIu-gsB($;93Xkfz{WhNFr3HqK0Wsy4I+p3%s7 zTCJ5DJ+pM^=61Iz&fOoLt0I5m{Ugfa0=jk7I{X|g-psXkBs@hibE#|7^7X5&B0{He z=;H32z|O5}osI4uKD5iNb54-Jaq5j!^Nb9xbI~QM_YW&uAI5xRy%kvcw8h!Z$nT}$ zxZC|{R@3FCk}dKVw#a-TY=U~-wW^Ww17g@{347{HgihhKX`(`h)2-Qd|Eo3HqYA88 z!glLVTU>bG`)-{^m;L8ybf%r+z*k4pQ?tkRl z$L`f!YMMLs0^e8cm#~Pa&}}Bknrceqj_C3{*CaAEbbF$=F*iyM+_`m|d!jFSCA*1Deb6W`vJf$+@0imcHBPS+(Il>!t$o$`H(N!!0sI5!#*;gz;!51|E= z`YlhaJPbFcpN39Br$AMMshM-lFtZ%mMUD*vcY7@4=d0Z)M-3t5aWeKKm@&<}aO5rO zt`tO81Hzbt6elKIY7P}54Hl!3h3OMQ!=^zQV1dgTWYz+yRGJ0iWRi7VoHjqa_~eJa*QtwCgO)MQo~Ig~{Rvu&W%TGyr( zG@`NFmv<{{2jF>}R2x<2`yT7P<>L6;BoKG`^EQ>6EpLvT&O8d1dJya7p;#3Jp?P|6 zQEE9-sp`WfN?+pL%SpJj&JVkFx}UxM7KFyHh`sk~72KN&86_*eGvP6Tmh<30^Q{Hi z69Uy(w2nW~(5T$OJ7K)wXuhYM=(rtA>kDn?FN(ny==n_x^a3ld)cvY2q7jtJgQ`Wn zh*CF*Ex?`42DpAS+SixbcOi9>T_3kEe2ksa`*o&y@(18G@63ojJh=9YCGhT6!ygd- z)zR+{z!BVBaupiv|6Z>LS+%PCxEjMiUl}g@ zsB$?e>dNm+`MW<^808E^9?a`;VJ=)F8C3LMO(H!`CuSc1!g}l2e%&RDZ+l#vB0V{& zm>pr>F_V_rj`)q@>=@nUnBgW<#bf* zLQ{Xs>Rh-|=EhQBo72*e28lVYy)TUEPWfU=o^RtGdH?NBO7`cFxBS{7-FSUm`?o|4 z5pwzi{7v1?Rq0>Ly7}GfC|Lc8*|omN%PM8AN~@<4$vTlBrUBL$>2g}?FR$pl6hOVP z3K!qJ(h?yP$Hta%10^DSjV3sffGc<$PKW}^NO&3dlQRan#NjTn~ieU?-~Shstc zkK{1i%KY1}?@ylSw4{rtZqnp~OnwHI{)qYV*4fG1v4lAxx;VD$kzXzL+w*yO6Ggec z0!Iy*WwryKZ6bG|^S7n%_c(F1C97-VZ)i+k4R5JByq(*y3@FLA2vQ_?<(C$F)B#}- ze4avc;4N;B)K-4jBaPV5&9t904|d?gkKcsmBXkMCW~#KlkCd-oUzu+5EJ z)u`aHm`##qEQXfGMU|w5%S3aD5m^Rcs#psHFoP)4;neovnL;27M=FK#CcViUNX+Y4 zCO|vp2D{)C;_yR401;6<{c|)t5>q1j&cxk_VBD|)pgzdTan7*HD8>Y{;$}f8YYlU6 zIYL<#!JN8O(h6(%HR-f&evH*PcV7~=c}}`O_Vs-JsfM``wqlGCq8rA5`S766=ciG# z+-|vRnvg)z15SO?dA)OKLusfuI};{J9l0hN&7(u2+JYA*CMZNwV)1N@uX|W5DQs=v z@8=&JX>!z6-rDAPsd4ICVuD$;0!!2LdmHjW?CFUba3_~zEtxez&oqRvj*?l#O!Jm$ z9n7y+6!oKbNw;Kg93x90PifGC+|9T)D8*+d*Y|HNG<&c8PaVsP$aE zt0%`@gLYx}iitt50F_7Dn*ts9gnMFBP?NWanf&GR;{7M0y#>($!Z$y>a=B92=@!o{at6Rd)$_?MMDB5O68p`~xZx zxbFZ}C@|WvR(!E)oo;iy8~m*miBCwm7!%y|r4R(Y`GggBFr2M6Qydb&k>MU^s!4y- z2{nu!d7;wn8YpYZMutiI-I{~4&mrv(AoAO-HC*phi<|9j2b?O{;LgEgwUUoh#lGpF zk1rDh@eKW3(s9k8J<5Yk)X7CcBOfP*uQwu|PMF}QVC^5T;|=9n7x4G6+79nKm}pKH zbumpz9!blj@NNm>v!L=?Jvnt!N0>&noUUiH2e(|P`;E&PE5nWSE#0z-wDk9?6kGv9=GWB)kUK>(Vqs0GF!r$hjGzm|(Y9Y6Ee z6_uq+XDNb8CaTxq=m%*?_R9{fHjJfPNZHEFkv<2Sgcdjn*{tz8b^n6(I zUV2#K>Mmcs&|vVs3(R-BLJY70b-!5!N=X5K-*B?nc1nBk*(dEDT>iTzLAJCF*UcUr zApNj7@Ob|uclX(a4Xur6({CD{TsQxKVvi?WPftE3yR!bml-c^}yX&9Y4n^+pi&uTYB6XZ`wD8Vkct1uOwvADPm#X^Mx>_DDU#3U| zL&3TJF#7bUOEPuC1RSnTH0Tc?Q`K+Df*6ZgtT;J8svw)f)ysR7yM>|dbxwI*?2+#H zfcZjEa9l(OdX>yTE*OnJZ0{bpDZpHSTYE|@Z_b1kGdDVbz9E-tcv+Dxaytr4YhI^rxD*d zxE#L!M)hg19W_s_DnB{jnDb-&-l~W^z6-{g(Rtz%HOy*D8l1-R^nos!8W~xmV@8s= zfoT$g`&SMKT}c&K)Hb4+ar+pmx|3HrTbEKuPOnewr>-12OWv3ld`RJcD7534^u{rx zQRVx$gyydic7Y5sOWm$}^~W%QI1EWy41bXM@txb|qc?5x9mVIKJ$|O*f#A8Op;5aP zw!*h#7IsAr-{j7}L6{{ACl@HZpJ1?#bgHdCySEPUN{H;G21NFC53M<+qcLBc)fJ16 z)hkEkH)!31d9C)7id-KS3^yO!uI+y#pKlPlWFfl2j#*FTI9a3Ojq;3wH7?RhP36MUR9uAotT^JtUGcjGOxL>27P_RHQ?F|89L3_jbHJ3Iq6h$t0}y<2VO`-`G&1Wdl5A9 z;^c51O|xqFBLlS^HPK*I!uIu-(%P*37}e0l%NN#NY`Fx|FANS(3cFp}bP_)lptiTp z-07Vg3%%6jn=pMU(ViA2Y`#|u(NfWTpeEkN!lqYiu4Eb&%!LaK>Fa=f3j1CDsPS~c zaO!-e#v)RBB;L~+SGsgmIJDe&eZFEMZ-Zm`OKQEch+OZeD!t+Qj{eQj8!#oDSF^>j zb$t4!r^#!0LHlgct9yQT`aOA-K!WM#3T+)Q+3nyxjC9pGFr7UrK z(ax${5Go{x7jh)3bBn!a579AliU;5^oaO8HF5Fm~kHvc~dTr`@a3%534h)AsK1JXH z-ihO$beVK!vuXr-LPQbB4-4 zgn)~iN2`6q7G_%ltIbyvpUaeKBh4g=T<%K>LF=5I9G)>Bj@wz|CI=by^{E~@uldT$ zqN9K;(ku^i!9j-k{GUSvBa~6}DFV7eq$#;FJ{j+hGnGAAdOwHEJj};Hb4&RoS4AjR zr6TajE9A((Y*qY!ZB_jLu~q8^I`p;~AN_vi23S0<^DhJcfFy72|7!dL8VWR=C?|Coq zB8K(#!q!SGf9pGil*SYGMP$b6NVk_qq!c-V5g?+dJHx<&jjrq%V)*?voYZH@%mehh zw7IYa%IdnyZZ1Qt&FE%R*|~{|?U$oK*i+)E**up(Ol>ULobEFH^6abc$_9stud6^i zRs@64-{cyMK%@L65ACU#&+4EUCcM~|X|oUGl-tLi2H7_h$Ezpw&7~Zf97#u`_R_vN zwuf7hiG9AqFi=MNtuT477fY4j~TS}6%wB|(^0iBjY8`@T?)b0*T^hDWn-$i1Me0gFNV?qogJyzmb9IpK*!WP|748sB ze^r&D8R9D+k^Q;l5*ay&FZF&a3b4Pd-R$L7pF$PI`0SN-m~k=&CZ!cJM!`eBo8FEY z5cuLAzE|ZhmcV^%)iuOIcsB+yZLTTmY9u=~T`6Mx2L$sk(V^sc6g>3Cp8Tv6(9#?j z#oj)53*#oQ+};0hDFrtW&A52q2@ud*7fx|UZCcz23)Z_XdWu_R-75Un_j}p85}vco zb_7t}yX(kNYi(|f{!2Svoola|e#;(-9-;q$$^=@iEmUhfkNW?B4o}s+eSJ*t(@EgQ z_Vm%AN_GAyn9J$gy#pZDe0Ykim|Ret^}XWiTko9pKdKY?2 zeU;#hE!L#BaLM6S$4YL(HI0g)r`V8_$=yE1LO3U;L}8Ty*&+p-N$RocUe@2%<0)+NO1M5>bsSfFNT63JZc87%Pquv!C7kC zCSr27#h8b}HS(t6%M=UIW_Opt7-?X&+jE%Ui*>foje!DhYqDi4*M&x+S#>Hn`6Lsd*h)su=Hn;A;s+478no@h< zW0#+S=?*+J;nj?}04m_)WJ05{7UrPgV-*0}LFK$Ogh$cF(YWXt0Gv}NL^W1bMoO&3$d zix0|T7Djhq^<5(&iUbm@9;2^8!CG?+SUQW!=2yg9;O#A*WIx`UC$R!3q)NWSM$E)j zM|-5nTPBMyu<+-K1Gy8uujku1-UwtSI=Xy#brCRfmk2joylCNS7yORT=L(%PfY4H` zhlfKCP^I&S#a}BVVQVF1kJ_aqBvVn}z*zlwMRHXY9qD1qip2u1;^oYY+_k_EdCi6= zp{M<9s+FCNZvb^8!kXF}izQkWNc9bMWe$GXpBFOaa)+*j1TG6ZZxjx%-H3EEls)MG zs+N2`J)Xrzqw1Q+y^NYqPe+SNyIdC2oFU@9^(AFWkrWd7++<2HP%wSJQN*r3z`z6PZew>8h6*4i|MR-aKBFz!5A;@*t$fcQn|S?ZYHE4 zD$EYes9be%+XcV_oWIPh&kdB*QIR@pv1(_*>nKC^66PP3i7ZnWhn4DvMvs<=utE); z3cGbuPAdo$d4sD8;OaIBCGQmskr3B!xp7&p-rD$zSl&Y|dw!4fTJESRoCq20g{V=PRU?wf zNARj>Rvt|5S}AUpK=i0fmq_P?Yq>w1cs$NIV{Ljxx>Wih0nL;nrDPjz*i}GKwRzLB z@u5t2_x%uoB~*Xp1#qaoN##1Pe`|GWst(8nz*)XU2m7;G+*(qNIp zT5+dpZaC3y@T<#5%S&+9HTNy#<(6d)a!SHWlrP;hhF5R+~3WbYI2p@BSYKVXcuP%f7tcP(JBL)k$!=* z{Z-dTWbBSGov|89vR9Vf;$jhq=7VZ zk?3z!h3#YW>5I4_dkdLwBo!0z6BC8gd`syI*irYBv2vD1?3P-{00wl&u&N~#zi_u| z`_@|FZq+6Lk;yM00wL2f6%)k^&9X$c2vRBDh#iWCrn#z8!O3xqBL$?=c=zbSk)o=z z>kjM)60{5qoasr|iO4N|aHpn(5j#D_mkHmWc7=-R)VH@>?B!uVc;W3jpF(Y6U-c(V zMRIF9%9=s!%Fx=^G+4Vj$*+fa+`f^jJ?D7kra;zoNC2bhBGx{`26`Q9op!n_J_Fl? zggi!XuLL$0USq9_{u^05XQRn89P?}(fy#J z@7_{MOf%xW(8dWzZ^FG?h08z{l^U5-SC~@wVc@D`A=3b2H1IZiWN8P!9x8l5nd28L97Qqr z;D+Hre8Ei3O7_zoOiSx_y;Q5h;j{cgPUaI;o11p`$+%>y;cpP zP?(PB&@^`m2T2z!bB$hHS^+9Q%V?D4IxAR656ach zeTQJJRW;o@m~4*w&z4M-n^m}-a;~$cvUK~y$pJ5Nog50so)TaQDx1SqiRmi0qF z%*P>)mw#POK&zL_XOuO|uIiJHq4_r~o|341rJua=N%Djo@SFya^~2S&VJdgsM%CUs ziR$H4Zm43j#VWhO@BdQT7(O*|Y~YFxsRr!KwR!R{Vl za7txW(0sYm)>aoaN9QLy7}loQSGz~PyF^(CH4#_fVE_B4`&TMyb|ZerUL$KIX2FK< z%;0&rP&zF;3cfWMnlrIqIe2sOO9orFW^6%(630oQuno^B?3pexq4BaGPY(Q$Cmh;T z+?j`#TIT)9Ed>Hna-0z;dOl_YsV-y*1Q6^`K(6@(oEl!1$*pQQLRIrh zl)HShw#f=3EuIl`X`{?Kak;xt)w|@Pl2NDTANbqI^~r+s7gjCj*VLpVd*!eMWJ@-# zcio2Z!M94KFG*%JrFS{2CcF$ULLwnDpR6k~@<|;bKz$(A&+a!IOY9LB7KkrsyU+ zrud3P`{i6XlW}VqTx89iF5JBg1dx^=z*u@!ZD=M0hSImK>R0K>m5B~t$=*3?3Pi>u+ho?+|?Bk zACp;2r_mSeFE5{a&?9PT{Q+`O6l&{Cc|0sV1LXS|5kfH5kkB5TO6d~kifc8K!reJ$ z^fI6*gn$#n>@E^#8Ewa8tyYL`qhxmxf%S6y$FEwvnj{sI0Dl36xv#mOZuRnKikw&QH8bnV7bt6W>Rg0956-Ix}9v= zLW!&FW-Tr!=kW~vWY(KE(ff7t+3$qrw?97qHa*%r^8W)W9RnyC3qPy=0sWoUW0v0i z({caf_klkklUj#~ek)`VD>>cO3}t`cCr-!O3|~x})oSsCDXe`;h|dl|XJ_O`ihS-k z{vrio8gUYPFgj(0`=rBuqjiXD{psk6p26;>UE~{$GdF|{StA#-ZoXtRVj79nY+y^i z2AB28x%jH!p{RoXuaA^Hl2*p^XS(u(#24`=Jdsinw`lWm-pQmd+g1WsUrRN7jm?wD(cm>r{~+X*J9p0r zLHRnf_@ZFZFr(jv!Y3(BB=Y5!KD1$tXOBj4?XTV3vJ-8yX4glztp=3qH#s;g$DN6{ zAh@zBg6#>cU;)JN3p-{%Ja!d9yswR1t^TXXlB+C<=(CEB8*Vf|PUMend%?_3zBX3# zSPF04)0D)G?pz_8CdSEaK60mdaY;G&7S5Q1K^=&kI5Qk8ksSqn)Ghp4E26J5!ESqAE3jJfEeS`)GP zEUC#62Cseth+O7b?>NqzE#Sd?*w+3p%ESr`)GNq=5WOx@+#vwQ;z41T z!thsP*Uc*`kcfMXIQ=uji56ljFk+e?!6s@$pB;LM#Z+@Rn%%(^TEd0vASoK*tUsgU zICBOtd`lGml;wvnmS~I$iFq@2s#kW*g`WmhZMI)`b>hFQSsjYG27^=;U-apvWQC|$ zcHB6TK>9b+Xyw}UQ*zV>01^6Z`dpMH?$yc1|Gh&OzEU4Or=z|9S6(zCb68@B&nrV? zm&|SJn$|+|%TnDyJZxNOz16C`l{H0eo(PA1> zNMAh_^0Tjjb(8!un`hV*jlQ9s>(GFWiRnVd&leEv5t%vx#2pimx)3scIUj03f_2mB zisxKSSY%Efu)3gQ?m=_@v=ndce)j zq+u&NTkzZ760xf}>Hy$&kn4RzhT&*h#L{^YS!n_nl*A~qn_E#Uyc)-yd_N1_iFz!6oh&Fms5sRR zC32(F!evRqvTOtd8#lBLX8;?AWedL-wl^H;SVu*SWl)lHR#7~JuNB|Q2!XSb64~Wf z$|2RNkmHOsk}GY__BQT#BOB`L9-268R0GUo@8gy(O+Z^L=etr)i&p}rpV8%3fALX3 zQ)J5rEr={M{_-3P!eu>JYq?+AAg>hg*)LbXNR*%o6k!DYq%Q+RgM+*xTv~8G*jxax zig9SDDU|yYLDix-KsbP5zEDmZxS=}uY}qmrNXXYBqi4k!)0iJKplaR*{rX2`06NFNcw{cB(#TMJMZP4Q4)+mM-(LFX-SaFNlX&sCb+XuBa)hswY6T=eV3@ zTeC`^U>&NL8}EB%G@qZA1-F{uOx)lSYVtZtPBjX1`1|=uquf`gbgJ|8BFD{QrPo8{ zi*&5f78m(PrbG9tLqss8qbV7DPF;uw*?A&kjTIn6w+&g28I3&`)(kg~qv86TgUFLF z#~dB!gRF4G?8S)rg75Fs{GQOdoNJB8A$>hzE)z$749k@swyiEasPxxtwMJ6Z3{07= zoxFJ&w)`sPo^&Suvt(|XY{fHqQvm98!uF?m1&}5SfWm<^*-e3G|MT?xIZB+Z|dr-xe{?( zeZX=vJyiEx>yg4_SZdQHvz_~wY`@V0(Vmh|Mutwd;1@HzuU+z)jaR+w#DU&KS53Q5 z*bMcm^p(l_`s}VBj(DEUmOLAG`N}2buO@46Gvta2@H$QtQ)Y_AE?;+^FdcC>F)Bos z&PWgHj$h{tnJojU34L7=?IV0TUN=Rx92al{$VEy*%;rO4i$WIZ!c?Mw-n&bZD zWE08qo%+IcL(GR~%`^KO0BmXg4=CMtxl9LLqU!(Ud;7;{0OT$8H|$pN_|e!O(Ee6z zS3_mFMdSAymo}#Wloa(3NPzRqKVO5V5{&c(4LSbjSxCb8(f7aGJ*ISQ>1Vb6|5{p@ z`wjljmy5-kyZ`%ccoKAkOHg|D-yb3{lO{WMe%$bD zF;uUZa<>Rl&X$&YQ=qU9T;+A;-`}+Tu9Y6~DUaCo^-EdtuUk8)<*nq(Z&9W`3CcE6 zKM%(>u<}-F`&SzlhYc^>pB-je{(Ju6EY#+DkN++}fg={uSIs7gt1cRXvD_vCPZ)_+7JHeK(__avWN!0g&?)xcM6Y?|Um zlwAR!;D6pbd>>e&e}v?rU^;(CIqUq?q|d*Cl3;b?N{qcLqQIC9W(|ni}^epA?+Dhp(*aWEld4Rhcf^=blCP6-{^ac-ZuE<%5S&7 zQb)?aS^2*?qxXII56BcAN~rd6d&?UFmz z@20bMbGd|&=j#c$XoT$LarzoMm?Wcf;H;-^gs_RKExiGJb%6AjzIUv0Pz`msHlNO# ze0+0NbmD~fLbKZ3TnyMW2IE$2ibdU(BShbz`@~yDx;97{nzG92&68#D`p2o}r@KN% zR!>A5^N|Xk$gU|%O5~ftmSh3|dfgIE_f3lLhJE}Fs=f!TQj*eIJ9A+%rS{5HhdJ`c z7)i0AO3DFNYr$)zBPV*=rxz@_k!O9u^X(*%t-JkWXxPjT+lbug?z1>2U36{id2yH3 zyn^fs{%W(_LYs-AU%F@KMKT+~5AE1mEwS)|K5XlH9b;r*<7!|jDLdoiCa*-sFm!ozuO>+~Zg%HGN*i^fcP4nt2?k)DI*-jv345|)a zIMt6@6qpze*AOdmQCWA-tDNx=pvds@HtN=za>vK_NaCeDww@fOIm^t&o~)}SD+JHc zM1`aA>n-q)hEJ-wuCGOoimmp)KNz_5G!RHamQV&Nn|hneH~&bywiroRU`l=)6W(Rf zTdyP1ceB^3)t;(ZAxz)~NH-g{S zfXD7p7@rH^+sSfjFAOra@3XSKkPa+O@< zQ~GTptGz9ikHmz=+EDq3K2c<<7Es7U0SKvCP6ED5ZL1t3f|TPh7md3BG2Hb;$0s-&=HbV!sUGMS>al4D0;>l4 z7ccxIY&*1t5)k0G7I`f?X^+gB7k|DZ8jGnBC>ET}OGV_1Kwr038X;d(0!Y28_AN9m zn23pZo8>^39lzXgmo>o5y;}=O^Cd{0KN`OIj`PH-#iCwG_27X0{H5sFqD{Ml6Q9w> zww67vIW<4`s!6VaZoqd;c!-Se8?C}Mf_+UBX zu0Ugd#XcP$hzNAOhpH~SX&@C@!6tUu?k#I3s)x?~K4O{pDB9A<-X}Oj5kZ5NFBjd# z@xq9Ii?HiANr@EH$ zwk8RbKP{Y^Hank|KcUPvSqF+2y)JA0YBa#^r>fJKc45|xA8AQ-|JuyK?~CuB7gf-{)_!`?znl#S zR|R3C6s*I(Z*>c@O@XcgnL{e)@ZlC;wU_fiUD}V^6{TKDk4RZpFPX-^OdVLW24NyA z?Ce2J4xHJ_{!EY8f39Z*=SjY>T$)K$70R};C_km2vSULNcXvQa#h{aRtgK}Sm%AfK zWZGE}xTvx;o`u6imgrLn%Uv@%X>S1-?NBJz917Z^0~dz?C^uNZz!J^rAAJi3&`+h_ zPb56AP+~zf@k6|9q$p?acUuNe!!>MS7i3_kjb||Y3^RmB?XcTXC7=;?2Wb61dMT>> zopMu0)$|s^;OHIqbw4tyT;-^RY7B77xzCq=Os0w{sQj9h6O8eNOA3)q7&_m?DL%S? zTLb(gV^82h@Gj_#;Og5<4A1_Wqc#q~CYr}3WqcAi;X`tJ2KvTw7&d2#r$fBgE0`ZdCX&}}06#iu@x>kNYhQPuMAp2z4<o5f1;Ow11U=V}Lb7$R``7|lJML7tGScBPM7FocZf z8QibcSkOPlL?~~+A>`<8Q6L|0+0SUdbn-ZyM;ijT)#p7;E z8b>)sc3#&X9?aLbK(C3?yI|@26u-4(1083I0I(!CY)mBTwst8`$(IcI7Tyj?tRy<0 zQMe3HHEVAn9_B){u9E{Ye1Zx74f4g#@@-P?>p?W}SM6@kO){fW3ciZF1H;+OGshHH7<`k>T6|$pEqELABADJsBC; zl16z%#aSywm5$-nI2?eD&io4a@om9G`s1(a1O^h!FAv}H$#&fyN;BsI2yvIFa6t%gia{r+TyYEptS8i&ERXu=kYYK6&jEW^%{K`1RR|c^%z=;%`W2{Y zK)Ik-$SRZ)6(W7ud^rs$A9+BL%&%sTBLu>w>x9O1cwfgz-a7wXP3h*7kheP3A7Ql) ziiSgH&DbOhoqH#5^`_rZhkuTNY=s^A;>mr+(RG0t*dmP`{cv%ChG$6P4>DU`4ORW< zNtd3AXGnK;;Ls~Ej51~tp7+J(xRYE*M9;u(J)EmpPGV}5U;<~(AFC9twRt;R>Xorq z$|I(OQFVB#I#%kfgURAdzpYmIUhe#xCS#Qk`(!Sfp)a^NSZmZJd+ZAyApE-nl)H)VNnGUj8P=-GNYQEFEtiz#7k8o@EKOW)V+#0B)fvGCiKQ$A_$% zFnG0;1!=RZ=)3p0|(%Y%9RO@Ld(^1 zq!l@EE1Ed~!Ihe`vaG~aqyn0mEj7JXR%XlA{hjWu?&tS-eE*80=bVSSjNi1`-hL)HCJakqVAnmVg9r~~B3Jv@F+0q)Z3@$M1Yg<*q0PF=NsZprSzyh8dz zmodjMSn$a)LEaYe2J*>xXf4`4G@g{{{#<7!jh7kTFS$i?5t|51;S$4?M4%WOvb(S` z;QPrI0w1){uuy17e~B5}O-{hV)_qt&_O%&LZ?>0F931yTXx&Xu3gtvThDjkF!+u7P z{BtU`-wlVNCR>U7pip>Gx{*qbc^tCDqpe-k*O zFEse#RSFSeEqIX%I+l>>se;WE?Bt<@?0Bq-`Xq2*k_@Dd8}zRb#PA;>$lsuUlpu4TH=X)Jt~-15LyEtLTs zCUmY+lC5h;_9MHh8^t_vqzc{^pmPk_S|=6{e96HY2qFAtvh>K>` zk)aaK4m5deD}V z!$>pHrDjykdzwgE5|Y~7@)d{%uhL|azD_#GS=}a|Os1C~ZzQCB>jaIe!>M^fCD!z! zG}jg#=R@Ebsa>g6qQF3F2ezc$L7dfHzEH&*a=QmriSQ9JAA*h$f#}?b6$d%WCT6z^ zf6Nhx*45w&_NrS#QA0$hazxHmM}&Y<5x02FL1kir3i|LjNNDTdpiQ~?-#Y>BVlMvC zdi(Sx*F*MzY*~Bn_VgQzKEDZH)z^Klw5+^7_!EL2p=k|SMod{x`MteyVNb5NwVhfC zxJ{zaTl+ChJS*iRYd#)cwdQkZ2@}conr5Dn=d|~$NoXuC>?qXH5 z68!M6`*oavPw_ocaA0MLzo`@vYW&HPct^5fwrXGj5oqEnkt}3k?MOYcDexdfTTc=W zq2+6*`_hsX-UOI#vW6%%a8M=)6A(T5GX$!jF`uV&USurW&fboJ^t`?Ffo7Ozu%#qe zg-c6b^>v=Lu2aM@p5})d@$Fx=5;fPyZxSPzw=~PU~!lWlT-$o>9$@3gkU%v zMnVYF;O-`h;~R6-*m5bf{r%3;)DoNNM^Yzgx9Rpst@jQ7kt`9-hLGxFo&#GIibP<) z`fS?QIc){Lk!vvy2G2m37^010)=jciWF_$4Q2~w(Rm?&|foTj<02;I%8x!zj+^xg? z!aOt_U!qjyMYKS?;?L07bjA`eNELDba|tiG*@EdFZdDlh`I0k-5%=D2V{B_em9lpQ zd%@AhZl8Ht^F_Xw6WAhcFiRjq_d%qdws2yLa`b}oQHv7Ad0PRf9zz zp`a^MBuk}+5bMUulSF34orF5x>m3@))F*kml!JNE2%zB%8-|wCw_|UeP z5>Qjww_XdcmYB#-`uVSUEbXs-_5EhT_D~VqF3U6(8(yy2g>@z5AT+_jzd;y*zbXk< zI#tHmUgrKRG@}?hc;xy%8*FdjK}45EUbWUZqSY4i7d!BsG%X* z6Y`2D~4IR%pe+W@$M}cu&Z20XOav4NC8TUbBkEpf|$$O7!f0OU^0|KtZoZSNv z=hHY^Xk-b17+Fi?=q6UawaXk+=n+6O$Qy|T{wH%C1iNk0^?0j=ew|sp;;$dQ|w{eda z9efwyDJSae1xRUXQ4M2}i|%SgCt*O}j%}CPUCA+7N3%6mftkMgwJ$u+&bz$LY@k?D zoqf+N737-6M*;EX5+u}~b{cr-B^zW|4J~e;AG7N8VS4-0qDgZ8(+}x_vd~0q( z=#c}nJObCD_WoXH;MR5lMcPB}At?i`_QK1ygzFKeWOV2H&3ecVli`|ViBo@sl4sU> z|EE5-z8v^zg!PMj{_Gnt#?#LFMLrKU&P$0)=%w#AxrvLY27$Y38;074b??0QO~;{{ zVhYi+QUpK9tWQa$P+ynbl7RbU8CJlNd9parQ;HB=Key5M0 z-NgKwzZ}POnw@bT`5py1TOkhj7nbE?qrH7|_gTI6Iec66GRe~^vP#k%>?3@E0C%IB zqmm?i$>v@R;pDn10^HG3jtrGF-G;ytZl$TBqQE|aKg;hkFNQcUc9JipLXnztl9t81 zV?5omSlg+hby>gW4G!LygF45Ttl?UXBR;6>x`|x7pFopw;kqvq8}r;~TD|y|-P#XL zqT6T}--V}~EP% zP=q8pctzWIsZ4?9zVSPN2{l#JLQ1Xml2xNRJAiV$u1H|{xdKz@AR?BIS9OTM6P7umG+>Px3~1r8|a2TVnfZeOg&>`wqcpMB&)|js7LsMJMcY#WV8wXxCt!T!Bp^>Ha2ozsM`w#Gi|jg4^fzd` z+^N4@7zcKD@<3HeO*$=_qxP_?_+{$py9sP}7`vly1v4aI^~SH3VwQkOsST7Ny8jhO zwQIE}_MXV^rpYZ^1Aeq|^UD1gZRT{5R*{`w$c7NLc!!W)r`CYCop@4k+*1_d(9PQR znS%Y%A6Btjdm~wvNkFE`Ni0WD%|(EDZq}aTY5fH!S5O_*yTveFhFP-CK^pZb(DMsW ziN?rmYeehqM3{j}%GCt64L+M8TzI&T*^@t1LDJJ&;5M=?mk{Y%pF{YWg(#*xx*ptq zXE@l|O@8wRu`{>p^L(jWTNlow@5+R&QbG6kYrKXSf%lCCed5*)UJHkwd9V^EygLO( z5KVg0_AjCIosmzRu_tQe_oaXXUD#C*;{=zB{9D17d-XpjH&>>dnbjsfh(Wp7-nU0a z9>t`p937jF6q{};xyjkj#~yf?lhl|MZnX+lI;86o$gYM8ppL&2yhL@F+)HXq)J2E=) zv3Ld+Vwb|UYUqNA$IcZukAku7Y%ShpACAoJYJT|=Z2`=4IL$`zu1=;?L&VL!*;~@x zf$%&Ha4&5f5lI=;ruq)2I-^%SH$z)b20Nh&@9Ca+;ouU-RYxHow%vNM@)d+#%Jxb^yz;eJy(;P)0xtMzz zxgZh7=pL#iwzm)E&U2s2?Tnev3ym*ufn%zR(+M+l5A#*wBuVxq_z?%wsOMv&aw-tVTdZ2N4#T|}4v#vsh+cf2 zgQQ?{4L%Rnox-BLmDIzlo@=|t4Z$2#1?HJlJe*?!t^8}wpe;fAo(O^(_X^-)-CEMY z<`I!Pe1S$J5GS^zkLB2Rw<$v8di5;~G~UxgmSpybkuo25zj}tEh>s{b7X+8Six50v z2s?KHr_*iRDZY>%bV<6z&>;AJcR}Dn9<2YVFFeNI_k>ghGA<0y6tiMGZOfT@nb9iU z65u#9O%7J*B|40G(=D)imtOhU;3O(2BH)Dl2Qz*>$_ak2wFKG>x5x zsZ=Y&z|mZrrDm<=uAsmGppP3M$CGeN2E2BGw9atB5>1Wi3|KKUax9a4`k^t6%!9s= zb`QD7LlaQ?2%Q(;rN+CQ;DsQwhUxmK0X((n-oEUDd(Y=DJn-KwS;7|otig-0koq!d zEV1YEj#%-YeO;u<#0Y`FX>6JUCr)qrymfO6E54elcuD<@`7Av*W9_9^p;^2?3x1Tc zmD}ih1!8@5W@3ARTfgIcqhcEz9puJDO?F`Q?;#!G%2zO#C6@c0-cY>pezb5+`uE1H z+j2wA*XpbM6fcrKwO~xEIu)x4j_>Eop^qA0g+ir(wzI6u+>)(IL@F}Vmzp)^)11=T z+Qyz@;Gw!_kpYIjB^IG)YM3)vPa_AU_%;4j!X4FZQvL{I!(ps2_GulP(E0;` zQSF|FuE2(Z`$^N@1~m~E0lhLf{+#R;+#XVZW#)AGC|l*$k+B>U_@;iphs`rsg+(+L zYG^i(4yZZl$O$*3L_3LXI5OO7wHL8M3;j+JBcB*4<%q$+@DW6Rqs#_NV7`NSn1CJP1xGRLaE zN4=j+ex35|rA=pZyM1x@;yY`|?$Q>JJZXW;;P~>EHYBgUeUU7BDJ;V?(1?nZSdOw6 zORea9KRotI`i6zhVkzq8000!-I4C|)3rqOG0a*r)5k%A*t9RLqwOnur8);3IL5EQV zvUY%9xQ7`9H5 z+HZt$=lGvrrg1Y-k>d~k0KWvr>>oV1gRrT_<;H{6UB3Y1DEU8FxJ5qMJtX%0{|$hX z4G;pC7ym$_lmCl$v+L4!i1au7fv&gzclu}C z9RGzZw+@Rm2>pbh_ph}Q;f1iUhh78$ZdX^W_pif=Io;j0m#_bG{FK$bzg}$foH!k` zVad?g@%tetwLP}?#;>~pQVC;t$){K9%}!SSxrZehU0eQKMM&)(B}$bNh(~?QS9$Xj zkU#eW!8=5B{Y_9P($}h}5)UDskH(z*7iXpi8sirJ{Ue)CzjVJ%1Scm2 z{dpiqPKszQ`iRcn{{39KPXwo5`1lzR$ekK$3uy7O24~xUJQwJaq?c?DZ6GJkiAPI8 z(;7d|pHnbf^Hqa_^h!=2Q&0E)=KB$t-!p#B@3Zc|v$)CydNTE&ZP(RP!V$^8J2Jzd zjjqZ!Up@mpnUjSXOfPYvr{4y2_t<oo8lv=JpeGd}@@^_ny@U z+m_ZGxcC(OIbUqHQcB4*0h69TmZl@=XQI%G4qEj)l$?*CkflxbD>hlK_9@#NBC7iL zcWH;JuHl5{PZUK|!w8GE3AmYW4_=aW$_{I#=~6v!@|P~pmp9L_sy&Cv^;I5J8T_PK z(Mva#xerW$`0v#v|7}OGk!{(&n5jdyl0F+}>W^kOLgrP5Zv`Ls831#S= zO`{J<+N$w=<74b?Fgp-w++`|qlNjPf6hJZ&s;SGFtbQ+nKTJpqt3sPr6Ef8rz<`d^a%ejyRUH%l0~72nj%lVVyy7aOtQgEAp98M0Inh+19eY#Px(~YYDkJ^d02*4dQ;2 zmS)`W-af57U^^$7ISS&p&f%nvQ70AMXb37ntAJWTozzxD%Pr?jQghV!eOecxl@+=Y zNK*?<;_?%Q<~@$M zMM6o!(4n#4=IqS{^u%TXVHvOEG#1%QM^6P!|Ax?;?jFAL}@G7J7RN>Mpa~0h#a$^Mm?QlH$u8k zKhQD?{u|TU{muKQQYw?${}t;@zH+#Ra~R#|zbnrpYW_kflKm6VhmUtZ)x} z&M-{*Qjub_&$CpF?x>vXOU%Lv*Sn?JFYr0@jeEy-cBH~?xooD=u)e^jd ztiJfk1m@tz4nyDNS9R<$oGxGOwh6nQy5`m^%h74}C>iT$Go&_pq0yQuQzZU0b2+BK z6&Q(&30}ZO%#7hD2#j>8{nueHq(pr4-;gJ~GU{9?0jfRZP}N4K!_fY9Y=W0>mec<2^orK?v{9U<#llI~NoHn=qHyJs%)&n$BzsUMg z-SE=oT^MI`M}wm;Y*YD1usZxgg*Rzbu?Ge(piQI{fh|V5!%RqZ^GFg@;|sa%O3AGp zxt1SqYqnh4Tn@!&Sc`#E%rYbj4TLThu^MApc=gxPHO$9f5>;{PJ_fRuwL1gujO!P@ zGDNIu9wFiG zJbl5<)5+HbKqRv03@RRwzDHI9Iz0u9FH%tF?6WN5tp|b~GS8ZVAAaBKA$Xq{c{oZx z9gR5cuzC(~nTap)+W(=hrQs1+K;vr{Cd_%we>31Gl&Lcz+)eSR4Fuq~S!A&d=t z9woqhVwr%=o+H-iC>6tnF^Yy*I8|ZI0U_Z-Q{nb5Rh_Z(xs|3JTHq;8Jc9qwkdlnp z+`-y=jZY>zs}baFLP-zCc-|HUzBxWJkraF@t_UMCz;e&wJ`F2xP3_6R?);-3&z*`L z|Hxj`{;>}rdQIde?c6l{P0RlmvnxTVTr8k}*L%Ru{k=9;v#FdF_mD)6caT|}dL`i7 zCQL1C-F+5c;QU~9*+NUEg%rcvP08sW3gh_@_}Sm{m*a_45Z6Aa4eryuL! zhXq$ezxXW3k%P|4bbFaQ*E~CbV|zDD^9hW#Z}; zn+o366)B7kN&2h9o00+__-R?E%G@jyp89U-xl=loIhMqGYO4o`%Ia!XwTY`ew_NE5 zs=HGCH4l3W`-#28PulK&gnq=)v}TUB$mw?M<;MX8oE~gj>n_BF6>}uWgoJYAfV0no z`Vf(o9-g4BFTFUX*mQb`>UfJLy&f##j>k+ofB?pj_(^Npn35Uh_QR*{%n zm3^y0-Y&7;xPW)r26=?aed!Q`y=ov97O$On0ORi*G|+f&gK@buoMU;Vev8Uh7K!fzCEjfnY7s-!U{>eT@u7%{ z23b!wTq4|a9Px>{4)kc~T@WQIF#eSy)D`ohK*U3-2}%b*HWV&b36#UlBs-%F1kpd! zySVEPQYt*Q=|OIlK#h@=l(`p{jW7(SD`=%ZDBP|`3+1?~85IZrRsq2Cc*5CXl2oXcCnYigCiQ+x8YLqs3M<@rn!l=80cL%w;aA`Wg)Z6!v3CvH84OZZw=mm6$$$r z)I?)Ai|3FF%aI_X{mFjMtsq_&OD$0L?p$aGei4Ip<0vvMdS7u5S~Iu@B{_njv%%u#^#LE{TpDprtSpotr3+)vpvX8Q1YNlVKov#jyQ)RMAeAS7hZHD3tN)$ z7#{%-D%{OegkzG-n1D*0(A5Ki?Sv7tBpO>#?gG`*7YMU+M(Rb$)MxIoJO~$Ow|FR; zmLq7JdifRe1sApIjonns;n@n`n^poEBtxm!Shyi zPq7XpY>obk7d(M&A0S8>=?>wp&eIHw995aU5<^H0kW$Ku@6%;OwmYM0E)NFhRx{Ez zjaxvqYX2g91&EcxOB50ct@ny*BCi8fb}m1Tepn?QiKr6afs6PDR2QyFcKnF`%eMrRdoXLg~3tGgiE;V@yLV7e|e1Gc-xb69g0DBv63{Q=?W ztEi4}A+7Wr3Mh3Q+4p@3y)`5c+IC?eb;9h~#zn2dWj#Ag-lm2zfqlhVspW=)gSdxn z@z9PTOc>#6x`l&h?mEd?W)Cn^mJ*G+m%_5{FJSzpcbS->?6LR~jP^S9Vxo(rL#AAU zlW5>Qz~%bxCI);dedJrz0d0SmyQmY5_0`0qwzUwUqph(Ov)x*VVTh&*7s3e+i#iJg zF3wkN1Fu#nHjs3HCmC~gJoV(5iLC@%gy;jTpgz(`%nsE~I?Si|ZE@e3v4D3gH$}WJ zgHw)A-c_O5LM@d#v^mgxGXC~+!x%)2#lbs^*lX85)|Xqmzf>dq2x{5_aeL111J~hj zh_{S}84=A})Bx49-I&;=s{K_MiKTKW9DNx4tO_u2K@D#4-h^V1<0s%w6L+R*`wX-# zb|@JZa18SG>AOOoVC$sbTblKhB3;6pUNsV~%9oi2)=U=o(N9lbHh9lGkR!{+9}Km^ z58iY$-;;=ma}2sXWX?u6jCQ?6CEe`B1E~D6Dxo{Ag3V>g1OnkbTKX-G*zcg9nltqOP%F&5cN=H))#)!w>vqwo%`>Q1sCMs9K1u0 zxtuM)vZFk4cg^xQi-(n*eh9k*)uGnfRaWMe*m2T`=T)dH4O{a%zetIeR=P{Z#CO|a z$RUUii(Huxcz$}k!ETEKp|%O+0N7o|9ddeaQ=!x1TbO)zYLfHffdJ-<(Vi1C42!F* z734(U&##7;Xlf5V{9wfs-+WZpIy`*#!gI@1OmBS0R^ZhVPe5Nss*KRll&}mKNq*9I zr%P1thEbQ6)b8i!hCT16eqw4LCtn7iPNK7-OQfh4 z$YJ6=inLja*Q8gaJxHRssFtc&RG2U)VM-1^oN(dx-9hC9x@3XpZj!SO7^ zqKm}4hU+RAa@QoCu#c-x#)}sN4q5pSO+XgKaVvR#Tq!CHX>`8GsTFqidR;tvdoaZq zAc(_VV4l9+l#yH$0-Laj;h~}qexSqI0gkOHIHOmjcaaQtXx8^*eo><2!7N`oY+JyD zj1v#09v3s6FHhy^o=v7{BR;BN1RTVxaDB}CqRt;2P{LY`2f*wm0JU&$&4UH7Q!Pp!Ue78QNs?JJtLv+i(^2 zAn~DP$48$vP7M|w`ttcFg*$U?_`kJe(qjN#r5HW=@b)KzTWlfyZxG~vX7Zck67B?- zzB2B7V~^Vw{^TY3PK!LSKkURQR-?>^-s=Uh+70kw!GvC(KOZIlC|3x}=)d%lQ zf{YC|pGk5GplfKYAC=yi-vx^vKTZy+14(&9;u4aC58QTndQ{*Tw)J{6RJw6OWJ}Io z!sBBGQVl)#Ar)l4FOcFF@a1oiZ_rgQ!rZ1Yt}GV9C_oORXCcVtIXrRR?hgCT*oOHs z6>M*IEDb2r1aPaomR3x^Y}CpdbFo5*Wuk&Bu2AvK#; zpo3wRo{sJ!;Abe&5{|N@JM=|g-9o$Q*kuqbmAkQ9Jl;f7j~&L%X%R@SwedS2O=x|M zdHmS2Fu=-hAA=IudOowrKj8I!q<-4b^3`k47H!juIr~Kx)74Rv4gQA~=07Bfsi~m@j z>72Ty{@a8_wb9a|^2wLaKN)bIy(M3^YgIKXgw^YO!4{;6SyY6dRfT(QS(n9#iZvk4 z%&a>|7-pj5kal@X6+>1Cfxy;h98h?s`?Z|Os`%&xO_k&2_QdLi-l>Ys+AWSf?HtX# zId`SP840Bv2|;^SsBR!x@Odfe-Nn?ZYJtWJfqr!)2;gqJzfHmvM(yGFZ&%lu(zW9` z9mstG1&eNw(EGA0odw?Q^ske?35GxAYxc3_1#}m2Ay30fU6#tx7@4c>UXKcVskk_j zgM14IU(itwao?IP*kD}6?{T#=p+Sk@HB`tHW_=*mUu?67yYv>+W^tgZ7K5 z0@Q0;tv)nw*?hcbHo7h?+n0Qccz0Inukf1%p7LK)*n1bqQgALEm1y7wx@jR+n3^6e zmG+)F6XG{lskpkiamU$FfiKfMfybARnhA$<8@FS0+wYAHez1^EEyb4YERlsgfz&SH z=FCx&q7^ttJ>sK`l){U|TI&5tteLeIqrNk4uSH7W4b?W)#zQ*d-Rq>68< z2~{iRf$Wg@d13tml)P=rIg;JcrDJU{Hp1Ah!YR2tP6kk~GBh)8MIDn@eL znV_ZY!6HMkh!DcwuU~kJg7pz2wQ{^oi>xG472iXFuJO(wQTS2z-kpDug36aJc~h=U zneHTV4V}eXUtN~DMK0fpc4Y5T@z2&N02t82R3fGqFp;?0_YLm(U{tt@dr6vO-)AjY z=h8Vf3Gz|kodvIUW|2^BPBys8Mwv~B*MP@#LXmF>@2qu$05wT#@R*kkFEIf-Ula$O zGjd@fp)WD>ISGK{h`>I=O_-;+58%HXIV)HQ^Ng$#*`|6ngV&9_nzG^o6%f)-4GnI) zZO`guXbE;!!iFXi&=1GK_}$7-asF2V-2f0L<6>Zm!RNAU&%FxlvUY;rg?n~CL!*#p zp!nk1QTqraln6(e1)t9C3^cLo zCS46?x3&ciRk9q@UpI2!SZ zC7e0UJfwADsQ^2MIk2Yj60_OG5aE7;a4QZPUqbqFUXo|_3Ml@FpXkH{pjwTg7zIauu zRi91p znRjwLY8UxmoL29|in)Vw#xEW2&yza;2CxBq9>8d}RPICmhiHrb512g&sPzLZCHw#T zt>AxJ?{+3wpl!PQkGlKG<=|7P|H9ZoiZsFUK>k;YOP_v2%Ld)QwcgDaIt3hZ(+@>o z{EZ4nWK5|4`W(FloJ2FHy^FQ{4O+i5-t~{{cntJHP+Nn&6eEyGvG)310@)}i^>8>(c=jkCy{E30?vAHJLW_QHDD z{(H3QwF6CVv5OV=Hr))pzy2~1t&MtKUg#Zcq`3lPeGqU_9TPmWP>j* z{F2^R`?dZ;m7DQoLQUl7>_04pf|@_qtJ@$;6^`BXN8wID|IZB#%W6>Zk+)FV;`=S} zUon2xe$majPZPC&#pmhi3`7O@BkIX#u?{0v;+L{g3OWSd+ z(gFSxZP)+IJAVGRW_d5SXdE${jlgXbz3|Bs;G zmmC6IhwDE8`OgQ*KZB?E1mK>Wxb%5i)6Pc|kaN3H`{;#fl-c4me_o zxf=-0*vXvwuW!nos#IdH{0(Y2w+oOB0lX6aUPJrza zTzx*zmbZ6DE&50g<@4*=a2yARnN zaRN54{-|m~;`Y^%gcU!^)2XlgjogFpWBfGDVt*9h8lJAG?yT%>Y-_dZfykdEV)(aG zYJ@3^Yv>n%HIow$^pWnE>G+g)rb|OFXC2e0!G~Wg$8pG>QQ`4Wfmx)9h^yw!ngewY z>nv2nE)LbZS+U)upU~TQeBODn7lgal<||@Liy)DX_o7Qv%_kWBTv4%nqGQ!mZXZ}g z@gB^C5L$D*t_-kGgj&&Tr5(Ms!R*l4>OGH(6o4hne7*k?Z5*7f$pHbHniWqI`7||0 z&hCXG6nDOk&{()teK^%OKe%bB>QfCWVY+b;-;I}g&odYgZoK!nOW7WgJ~9y`@CMT) ze#gz4RqBkHM{qQr@(wDGu(57Zq+l7zl@VsDe3#+IZtk%BTv34VNq(+6xYc%3BlJb) zb!htA&c>TeL7%#UdNFc5MMM)IYw;c)XjE?pIs2c!_K{IC*KgwowUyz@gEw46f?$Gt znYUdA7P^D|oo zZ9qE5O+}|ciy?bis>F^Y1GSdaHm0x*Lid!KN5XL?p(Bh3W4^bzrOa(tIB)r{*8CQZ zw-UZ~ST&!AtmN{*OT=@Pk6CgxTTMg3Ym!;KGmdlv&_rPvr6W8ujlOQ92p;k@|XVIz8&Y&FC10VzGrB4;u(3SfB)RH!`G=!OY0Y} z_xipgA6CIMF6q(*n+HH_u>y~mND#ot?_HonxuDYDThah+Oox?3K$*;gg)g0BcQDga*;XwW6Vbm z()x%bMqK?-xU|~)pC3*LsT_{n zM1lx;Ul0vWoiI%UWDNLaFkXD;UbUd6>b+YOz<47JT#1k~ot~45l?bv2I;WjMuc6s? z)azj%844*4q`-#fRA=DKerkKSsRAcrK(sEEvlvEd#jg#)-Bm~Eps5=Mm<_=10y|5P zdKNI~%%z5Y1Qxj$G<1pWV)ll6Ys9)7roX*sRLJf25C4=&o*uB8XR<}7MK8hB3=a7Q zCbKnFKC&Qij!4Q2;8tp_p+<|S$>3)rqP1mmg1um&3|ORKZD{pHx?5erUpqHzf0h3= zV#xacRpJi^p1c$OGqJxGJ$(L`N*v&6K3o zij_U7yit2DTz)Z2;hv=1_XcZi%VpKKl9(U&FDA|9zuUFo1$_G2{=@YfU_>xMxt7=A zlGyv#2_*d6Mw{+|TUF1Hm><*s?m)OP%ljJ?ziR~GzsU=Jcs5l1^wZETn;|>i^3~GC z+{KV1LUb1c0dw*WOo691u$F#dj%FL13KD2sYOJ6HH!>3`C<8biLBX?Os#K_mVvSJ0 z71rE?3OXuS1W=VnMZjsiv-9caa=7z=9f2fU7|TL}%te6##hM!#l&_D7yqHH2UqcB5 zS4&%{!M#M&^j<)F*&3-%4{E~MVlAeYUeNbxzHB?Cs1;YObm>QK)F=ed+MdRes3bdX zhP{D6O;}mKXqo{C5l&9tXYkPdsjI_FZ>=KmKw^|ClumGDYsiVtRyh=mG=lp&@r)$UH?@r$`m*^@~i#MNqxX}r0uP%+8^Y)V9NU&`i=K@ zJGY;CHTPneB@QF`zXi!jJGefC}x|JpE18LaO>a7 zu8xr{*Sz;%fztR&xpvr1*1-SzI8#E}WPwNTe6=Xrj@dD~fq`9Z{a8$O5#Ec-aWz{^_YlTO)UK3efr%`jkQJ^i*YwEzQu< z=^CzydckO8#UJxfjP6kggGb1+gjM`om`}ye#eRP3HxvFaGjJ@xf~?{bSpi$~-({XID#k0ta7Y5^q!VNIp%F8*1ADijx=T1~v=OBkZy-A9cv=YAc{)vU&#a_J zxceeh;WhNDgWCIdATSR(R6^!4wK^6>t$U3_U9Xosp*g6h)y$1le`eER@(yo+Iyp)RytQs zephXp$Qv%(b@qS;c-}yenGO%OagM#J_Q4rKyxBb`OL7#N~_du0&|o2BVZAzf`_eexA~`Tdn96VpK<;>P(LOg60p9Q~)a&G=C_; zb9Xri7h~|4Vpt;_^$9a|GnxY^EN^J2xZ*Wl3VR1g=G>*cWGmvR-;9x~qb}fze z#QodzN{TrvknUp`Aa^u{t>I;8j_J!Qy3=H9Reu14fqFs%#IfRj0s#j!-Mc#Sx6htG z%k-J-A$Nq+-mT2}8$uAL{W8iK;Gh1!GMip{U^ktkzw2lA2DUnK5LJGc^s9@f$>+1k z8}%03OVDs?6M0a#-Km@Ca<%~dZe-PK@xgh(2)$B?Vwj5ABF^!&<5Ec6N+1D+bLBbl zlu3P;hH)@Hfo)sSv1_|RL-B?SbY`5GthlcjFDpzq!`Ua6?$@3TIp7VEgBca6U{P;8 zu3^+lDq^z?z;o*!h0v^FOK7?9x}()n<@NL8PhHS@Rvf*PF%u}Y&R5%{-iH7XltrcL zUm$HnRw~J(7xN}K5i*qZk!I*6WrNwK)+sWyx^&_BQqf)TGhG4Fb#Hs7E>ts2$tR_5Ake=y&lMu|q9_Rr3IW7kHEUj?l-uBEXu2R} zi(?HoS5O5isR6W1o2LQOb%z=ed+3E1j-K{W3TmBoMSy8cjd0DHJJMv5400T)X)$c_{{zzGih@{rzKL)pURVQ-DJZ&N}l;q8-=xVEWLB z)_)-5-FMQxkP39i0&a0D5|!EZGKeZ^NCr#aI)j0xJ>|;08;}!mc)COjA3|y>GDKnD z?vNo)g;719=%nc@o|_jcVFp#D=ktgi5kh3&9m2Soq)mIIV2cad2YwUOm>+WdNsWkD! z^(vT8HBLJa89)MGdV<4HUgwYEN7uO3#R8hg$w*>8LX-^(lx8b;q%f069#^T(x^X4) zB#fGWt-ffQjq%p<(H84o$i>?HIlql733$j(%hV%BGPIqCpbt}+Q|;6Oz@ZE68e4y- z0XnjR!6-`I#i&77-!s@4QIbM`czmL`7ohgd@ho|nm<2t`*gHVPiJNaQeCfUwxL?{!WwBo1R1Bmh zLN}Rt_xMy@C#P5>XJcU;g1_Zuh(X;{g{g0!Wtjy-Q;sDw%17f$l5g$l(l1IQ%=v3( zfmX?vrzx7Cuxc>BJ6QAhDx)b5Vh%M(@X-Lk57{BdMl6H)RZ7yozzkMktSFb8@Bgd} z1~WW+PLb`nbyXv>>yg4Mj$aH}sm4td`t?POWTw)kgp0b37~61#tqqY$GA~qpaG$L4jRzGt8*T>&nl;wYiRp z8?Nq&$*tSAPGa;VH!!c6j!PG&%M3Qo$PX}z++C2S`R>+E3q6=P8VwNdD_?eb;RJWf zgU*;hiJ?Ti3Raw&2w^{Rc@6DFWM4Q96r`)HGl=YR=_jat0FffOAnSy?+scx;<~*k5Jjq;fd!bn)skI8f2(QgLXI(auvtt#;KPeEUusq7-oa zCmn~4`NwcYLwM=nhR8uA`Kt1p1$#rUY!R`Z5N|bp`*4PN0K+r?Rt;{0peIpo0F&-g zpkdReFH-MCcFx@DUxAZ>ten})7QJ^P=F5b@&o~M%odqqyoNN4MNplFwNmvYg)tYU$ z9r4zdt0;Q+BcuGqO;tk9F|3p3yCEB_C!SB^rEyF(->m=y?s6083}EzdYP%q1n0h%M zh}a=pY$-Wy4J_)VN{+ZvlS&P?RQ&Mn((HK;NoJ0_`#F*=AYpDhg52=jZM_ z?@A(O6Syry@iVNu;SjfuJxB%P?kc}@8LZcq?%YRE%D@n)7AFN=E*O69yEvg4B%0vO zcJT%X?tzwSAk`JcHfR!LO|2VfDp^3v-jX~@y{&`plSm@Qv-1Ng!` zIaOg9517aA9-(bt0m3@si91fE<{!hwxz*i_-)~mA6yEQU@Di;EP<=0QhL2-5u7AKS zBg7L`@9aBhAGO~8xCyhU6;_AAP{gdC6++Nn8_+uc3EF!0k2EUl zZ_vLF&)sziy2PPVL15@^B$Uo!DCvfw zLmDrQI+D^OC5?1Y+a{59gGVdL}9fK{Cclm7Kh z-zE-%`Yv9iV&neRFTNQ{m>Z z-+?x##n~qEc%Fh7>t7NAVz<%%_Mg5V>hc?3K=I@|u->F2WyNd8v@-sfIkFP^?-NMXTIuW~VbS})ygmP;&|Hiu#uqRU=|315emV?xH5cYv z8U2TvR%P)YU}k^+``5oP&HBhoAw;Zq|G%4n$=&z*{x`5%T6w{ufVz;|1pt;|frx+f zyg%Rk#~T0k1Te?-BhGXsPCVc50pm?cEdPCLb6PQ71sL_8p$T1Kbohf6q$R>~=9>rp zwIZOA?(HY^*v)V?klh8Gc_hjqKAhh1K={a;wkEo06W*$rMEv+2F`C~wofGpn=pNHQ zZl^T6=?4E<2WMQ%%8h_O{g0DmNYn_Q{w{_8>-=|;skjp7j|HmXF}L=T1%HHOOXP_u ze!u$%Yv2XClY9@R%uwfRWuyDAU4Y?Ms#TS3DSEg3YYjkYKGzZY&7Q?;i+`S}`=e5! z+mbh6>VF~Ni%TXR6L&~h?8<}b9b8`9{BcZO+1k8hAOY(?Vk{|1kCd=IAKaFL{?|_W z{|W*81q`1;_e``r2abjQb*R|+^DXr4f6V36uu+WQTa9RWcJlLoVlh?l&j8MG{|u^V zJ@_e1chCCdcU5`8&xrr@{K5#+xBFiq&uB$&fZgrn|3(EQ0keT^kuc!3|C8j^L*(`g zE`9y{A8B=u=APOZcL&%`VminulHe=2?a6nSs*bHylx`5LVT z#3bS?WN}0xbhFF9;gqcK+>N3CdPi1|SJi-tqefCsc?-GM@p3%M$?er5{@lWgO!J32 z*ldJ{2D|Zi_D@@5`y4hcr2G+B_;EkC_p>}6?Gs=!;XoN!PP8fm0BX>lu3_Yv(a~a{vNP8?FxC~sy>9CeS5=bXv z@D3$qSLLK`r(wF3?N37j6jc*hsLj|aHGLzGAQ63N8 zOxTEf(ug%nLpy%=jj5uQ#?3x9c6gR@&>qRz!bZCRw+h2r!WD!mQ9+L3rm#D zLOuqw@|Y_lePH?NppHUnxC$~@OBKXyC_48(z1Qk~24c2;{vA21zPP0cZ(MoYqYOti z*}Hlq&9$QSD)3FNDqe74IRsoQS^=TC(Z8@2LL-b^g?7prlnE{J_LM>Z2%QN6|gu?;+F{Je1MCnk?lqg|l;E__c9nv&Q6Ra%J95CpQmF*@7{yQw4lEH7QPw-(HwVQI zQ~nKlU-*Vf*7(p7#wd?zXSkM`hIAwPc9JfbQz)8`?+|WO&Q2Q5p;dE{FYIVcO}Gt* zy}Qr!ZE2c}(|Cqx_!E36-5v{!`lECn4vZKe^kMp6R*ahO)c=M*UZ2;OBEuw)7YPtda2@qJ0l^bjk@ajBDz8e7bSQT4C9;8!*h4`CeIIr)3}h;>gFL+%rF)W zEE3o1GYzMw@fLTfMmf1=%tL$4LXZs0Uc!}hIfWXWXqxoJ2n3q%s~6Et9;!HGbx%84 zeL>!;YN=*L)`t(D0>^M8=w8f#dQk| z-DH`(ry<%pp$_sNAA8hM^@#a29H|#W@peO>vS+$;>^W*^0GnxjMdBZoBC~Z06nDsX zJS97Y%Dai6!lZ^Z#9w)bKlH6yUjFPRCB)>`prW+Qu-p}>@W__Zy2sZ_cVw-{d#j6_ zNpIHk?VzDbSH7IB9k0=L7Bd-ZOy8C|Pi^>WJ&DT{^89rljZ}2FZ(RZL>&Gtgh(b{> z<)}Aw_`dD+=Ocl$R|gg^Hu5feHgVioe@Olc=!T{XYOpss=F8@<2(`cC6zewv&2=e- zn^uQ&FEqauaM3^?J5S<}{F#X&}jjF8PQcikip!t9Eybnlc1o$-M=w|Ap`jL1&k=$9%J zj_N8OqXqO;F%M@~8y+^Q1o4JBuIGj(u(=6&j;4jN$}l>#c)}gXV@z(;xsoF`nO^4+ zOuNew6FEg&I#Gs$^axvnD?B-P*3YZm`?c$3%4W*_-B+p(jBevvVJd^F= zbCr=D5-dZGh_CWEg5g2MJb!}xyr~6u zZ1eJSb%Hd$<4WOSmnO5Na9_%$bIyw0~ z!b(@t{Zs76<(ej)O|jVme9xIJ^(GG%vvLRgR!Z>EXPS?X*O%i@GcuzTw%xKSu>&F0 zKVNY=Y4hQv<{piizt#WtOg8t2`SZ*caOX&;SW9K>%TIM{#_74#kFtM^iHAXiFK1=^ z#(JKh@kZT&MPaNn`b2xq>)EQCTvy&BA<5u7T@TU;@1#!rjQoMy;)3x!>7~DNc3)MK zBh_&3q=Y1lk0AAE4E*%*FY&-znh4`JpVnjYN6Qc@Ovr@=dH!GF<^cZp_k{Klwxc(7Edd zb6Kg_6A2a}r(FaQa@neRt*?=F(SFa{RKJC@`0_C3?peqgbG!w+8!sQ^r-D7;}XuY`JLb6lsn!PRK ztGHU#hZjBRR9#d27*^a!S&HiCafEG z3nIpFB00C6-_1Ao9d?lm{^BcWws4~gL0n57`uI%D?GiH2$r`GTR$K6TV`JE%6IG%4 z8NYMN_JTxLj@$cQ&Rjmv{=PL0-jzQ-KWhdela4b zEhlnz-Ii1eq=5egui>qOSp{S^g>5R521i|7CT%il;4QB-Yt=9h-@GruS;XA9$;nk+ zQ{sGDz48+Ec5F`#Ab$5!lh^j<^~aPF|BGJ>y%RyA0r@C5zi6dZa>Zagu1+@EF3qB^ zrslB_iY`$NuXJ{GNAmAt6<;-OepU-`?N2V6w6@O)B+gfRc5J{BZ+v8qa-E9|v`oa~ z^jz1537q6zknmI`Y*#YbRe|9t_si+L%7@)c{Hf!Z)GHhdWPL|R) zPi&ua#Ukd*Z^QzeO5_#pF+UFQ8MbNEH#!%)MC+m4{H^WV*ypIn2}2JAh00FVqb>v< z8+_&}EkWI-zYY9PY8_@ii1C^4u zTGuhva~7=?>rdaaN#iPu&xmC67V@L|L5^wGIVH-Tg;x>7<^=Q44ZfmR-KdkoTltt=71wOKc;V}Y{z;p9KmU09Jf3C;2-m!Stm^6Ikk@NsO4b6dCfu=paf%0R znU`nSkb6sN=jqh>T`sumE+DxtPa0hGUe&w&B)m>%Yq z`zv%a$tbT@U(UBDS*T;w9|~bMExsDu9SRK^zIN|uzVfQxOp*~gJYooM@@+nbE;F@m z2W^|wMc+782M>+tmb%|H*WcK)&p8&*?c_IUz~>aU6>XPO^Z&Al+(V$pm{(V4xK>0^ zpxx3?f;iq_(+yyc48cz=HCG#;czDhCs+htyLGb%NQNIJG^bz)#H&eLue2cD;x?o+A zrHbn2XvOy-$Z|t#RPNnefXsjYrTnk_f^dh0(niEm*w+VL9q#TfK8<<}CnA;m%YI_X zH386Y#cmt=71yTKG4hONwPRgvF9+IB! zFIN}gPKO#3V;HVZ6w?S}MpBx0S+J>Y@P*3E!}&RbS3B7*J0MRvBg(Udj-yHp>fIzQ zVUM_mSklEx@TRNv%uo>HYPavfH1mE+KMkkrkHonC4a?dH#lp(w6Sn>x#yey6CALCxDy2@8wl&32RE;B3ERj%c1>Ej`UY(1Pz+Quse# z%ZC{Kuj_@5Wyc_OPco zCM-JnM@S_+Vsq=vFWs4X{}nT+iU!<56|&q?PvE2G4{%h}bnN(VJ%2p7oqR~&-1?bd zwgJ>e@sRDvYd6T+b(_*P~# zpzv2Rn76==+t}=fOnrk$G(Z!ABlW0x!CT8U?LoF<|2C}N(lF#ek5FNs{HJ(v(7RIG zjz>Na(q!}3l=-0slXDb@DH?=5iqrJc5(I7X~6oCiQU5#k>TOm6@11GJaE4b_BuV(<+JuCS%_muKO7}Cc?9yT$+0VV))MZ$}5 zS?6fRu_a}>NK*=Elo*P~0HT$uM%g3xf+yDS4*0GTxb_j07>l$a$Q>*m6F+7yeti$c zb*AnHvE*k;G-tA<`!ye{KVZE3!UD0luoLQ-6xingj(@c9`(ZE>*vb4=J89aBV*{ zURHvc1cF?7f#4x_A|NwHx^e(~bORI;;QOi0!t*`3R3Aw{>(c;AM)w{QL_F-?emR`^bkvI?iR-C=@RE{6KeNPya~gK~%i2E;o2fx9oli zI(`$Vj1bApG>3^H!$8PEX85osUy?NxwOCA*+Q;m7SSD_5M~3yBqOl)N5@W{9MC?kND9Eh7`HzADby4-1IW_8r)n$ zFbImS z?7n&{A_nV~Wtr^P2Kd)ssg0Pv!>4B4Kp_fyCD?Sb+p9GN%hfSZD#NYK?O-odL|$lWI@b8#&s z3BtRhRHt|45a9m)%E9W}vmeCXzM+g3&VMD0C@EUfz>;--nPzDU&nQ$DMdX-K6UpGS zW-R0-g|WV{`&K^uLv}cDvDMwvweEVkPJ%(@2QSWq^xoJa4f;j`bNH<8mH9|!-t)_4 zSC7e8myyJ4Ie7THgb6=JLUe8)EC>qpq9hw4`0;Ire+W(`>)b;mjT!e~IN{H(mtSi# z1U4;Cj%yqwL`$#3I!VRQ@{cHQc)u{Num3IqX1?KzniX!DTm4N}8~u zC2gih&~`^bd&Kl_&}Ui7AuFXyJ9n^OW738pwfYV$_-s%%`Bi{>1UT4My<*vNI4;(Y zhj*%9J~cIgEC8SJdsvP6_v1Ap!8$MQ_|?E6=Ef#|2auhzG#=>LdBu@LkGjt4CAr!$ zCP?5iWJ&(FkEC|*F5jA>gPHIhh(62@EcfpT` zX>ZvYPZeV&lmkM9MNs&zh>yve4ED+cc=dM|_$ka9ru5W5J@hShG(_%a6Cd zYHl^4tgoj2>ihQQ>DR!msV4Lh)bRf7)A)#Us+eze5{O&(r(5}J@ca3aD{s_Bp6XWs z+jyOG$1P0d9X*{$Y*0VkIi)L?j4W+p-LM@!cdi&`&SDl6G6zBirIX}RjKeVx=3 zv9|d>{P8zz3tfT)-%k&&q{;njN3r%n5wF}^f(~tc>VpN^Do>|cHFz;t7IYavf)!-ukv> zi>|ZgveYk2YVn7uDG_c-(}(GOoN-WED$sdJ&tib5%3bfU_g^dryI=0M2SKmyA;51S z1HI+d;SYPlgb1#Npa}7xSLV+&)r?Y^>hURx>UXc2HebJ%VQ-Dm!Q2eQ2$Xi1vgv+M z-$HCP_o&>Wr`^Nc53<>F?OlsG5I)PGI~t0xrBYuCEhm!kRwzGEBL@54N%T z7KfS~@{Q;;W10ElY(O~|{oZ#j0n;tE(f9I}rmM3R-F$6CcLzW1!`Imj|7e-%M-I!6 z=69n&>@o3#>@nk2jR)o*QbMQ?7-NPKLOlI;e9uu@?>iIG&t>y$MzyOfzR+2ahHT{& zHs76UXliI{82B+j(?8r^{s7bsOJk_W{h5cdqcFBtH5@idA_8yYae$i=%afn56lr#cd1P)ty9Cdm_lKLFd88UpnlN1ish5{_Olctg(S2Z^^}`_R8UD z$La4CpHC|qZKgHiup;U>mNtS_42W%rrqZ|eY1^pw?pj^BO~K(oG$mQ}YldBn5A(Gt z338#Frr`5EShu!^!^G21TnduAG}^)ROFqiu53&?m*_ynA&|?;xRS7=RXF&|Ah@gUyv&fc6!x01vpV@zi58rVuD8?Y8+2=J`ysInqMeZ zEC>rw`0!~NtA9VR|Ep+75O=8Mo)pB`_biB4K=_~c1 zW6Fz({h)wKf#X^MXEBSFDY9ykti;I%!V1n%^d<7$wE*LHscx$F=2QLOQQWr~ZhwPx zx_hr+8)Gfenm0MM+GC8F9t=dQQPVhE3XKZxnFy>ql&7sd^S&tk_T3Jvuc1{A6_@4e z6<<`?&Ag9`EuODwXl4_&JwIxfcj=Wy!%Ui(E4x-%O8Dg!HwuVU-*YGnU5+iA%MdV1 zE01od^rj@jhmxgBSCx1AH;Cd**K(j&pV{-3~kAO4`x95n9_-@={a|`XKp$S zgjfRq;O0&b7;|aAIK6=NBz>X(ZWu_MT;^+hWHNXu2lTA{LIo~28|CNKA@dE$hI2+VzbZF^^&ER11l6YfJE3#5JL2yQ1~i&*v-b$t{3=Y)-5Ca-}$If1=aU^2Wlz^XTYsfq~QD zid83k$hZu=PZ@@kUiLIqh-)Jb-1s!>fu(SX2EYUKeZ2WNlSN>6UeEaiCyM zV9xMSce2XoX}%!sIq^Qqy)3EArP!>{`PGgn4znEE#h^9&u@k8>NF~?By%>O-wPrKV zP{DM#4W=CBPE32*|LktzQv0th#YDLwbFzH)zEt}0%)r^4TE|3EVdxDnp@{;hn&zh( z??`QVM0u7Z-4BzvehS;1;?<;_f!!MW>KznEm;{B{+oj*54bQz=TbIZ$3yPL@irTK_ zl2#@-95ToJ(RCBQOI_9#7`KKuY*5U4UA&xh ze>24{z+6Bx+ci96~o^zruPZ|96Hx<3A$$_D9GKJSJsjLbjPbv zYbX1b>vh=FTc#*q7k`Q7eIn||_th8<#<69(GFDmOEhdiU1+`)qxM0-A5uKqn*(UaI zsV^sVs6%=~f$qkQMV2YiWn%=J0VkcP*jswp>F#9c=!)==wl0Z3u>2cz^YGCF!do>K zVCseE2n6`dAda!5eJvva;z2S3yPUC)^kMD($TsPfIAXKI2hm$Ye}l*`{3`}87a6<1 z4SAEjH=Y~~67!Vb%5^Te3M<>^dvBxn>-o}*1+=F?9Fd%Vef25R?xRT<+n(8sFAc!eO=J0zOo*LblV=EttVUSSbr!SQ@w z?!uR1?WJ>$(v_r$gGsI}BbBy)E7fKVeh)TT4=xH84EX7orJZ-YhLvsubJFs~@0=Zs zgj`&fKChJRefQiy@T+i*FQ10eIer=u_`5&e=(Fc_!T%^l0U@i`j)WgW_*cs6wT5g(MS4UVNpu$7UT3;e#qs(6I67 zu&zikzlfDrVr{SqW}wLxNHtwHu(F!LTI)K1bsnl-zsj@?Ollu9-vP0jzV!_$I%S#gz3r(ax~f_UNL(cjJUE*cwalZGO5q$R&)~ zUp=6#_@}>bzikHtgS>=K*{^*IGXbuSoTHi_A;QhEC1FVxuD};Y`oOe0;K)=jcEI8N z7xUqj?Dh6%uq!;Hiy$%0Qun-q#BLG6uC1DT8jXlG1-IkTdzD0yL}}|;&UleNqP|B5 z#urx-R&si8hgfMpN|>Fm9Ch_d`h|4HP;76#{0;s~!9C@LK~GcHwP)$k2fFavTgX$k z{61kBYz-TIaACv;6cD|%9^Q9j>`rSZ{q~nt#&plO{Oc!8(V79ed`5JMZtryx?{N*q z?Q3}TrXPgy)scnsEUH!FXJ*OnjC4zW)G6h3eD%8NNd9P-;cw7gOCzba%pYZ!q#c`` zD;6`FB)xz;GH;{TY^?WTKY#qlqVsoPb^P9(m|Q9Iz9SFu(E=6VOViS=%a3k{ucMmx9ofR4FXY}cC^2oCR;^Z;5>HXT^O?0h`hPDh`Nu3`$}4JBv|rV7zX^Ngh=kKOO>odUb60YH z*L~VB?|t6pf;l;^VQ6>o=2O#O#5XcSACHA>9`IfF5eK_pTdx#=S68}StD{^pseO8F z5Aud_@6~5UpvR% zf==!2;og}pf~csdHzD#kDr=Tw@P|c~I<4q>_! zD#L|{G+QOj+l^~-z1`8*cIErT<@BK7=k)Ce-cP5`k29*=_GjQ?Jez@YfjgVPj={^6JaA8R+pD7 zue>(`+kThhXST`C4^u4P?c<5-@D zv{k@y=FJB$nGKcQzEa;sWE{3ws^EE$Z-Au8pXupXnI?)eC`)8n*OO(q(pv<^ueHsQ zit+jCyR5*2*VQt=dn)iE0ZF8#vp`joQSI=^xY@53s$OX6aw?IvS--GG^DU!vveA0X ziElhfcu+DYA>@WHBmQ`EdP7h^@L^ltqr+4gHJf+Lh`Eo){NCYDvevlza*nospO5N( zpYSm)u|1pe5!C>>An~8E-J?KRJdT<c)R!)eeNy3DZLpx0D;3HMsg zj1KZ?Y;GKuiO#)~&In_ZSa`NYjtOi-493jw{6o>ty065V=N$s#=#8i%RD zsn`%1EMSj95ZZ0LqDKj8g8PB2oPtn6TB>&nUq3yEf8LA)s-07d|5Y2^-#C`P|Q`)dXYH$d3mIWCc@_iZdpYDm&GJOh8RkHb{J;dh$PrY z;VbqTlMO~EM|vdNKPsn1R2q|b+Wk_pm$IR8<|~lWIKHG|XiUD_^GFyD_^AI!G4vBj zQu7Bg%oJHb8MO>H$_%=YaHg}0;t{{|;@(2`7+wn>6{KUL5Kn$q*DDA78VD4sSUEL3 z;+*rV<)Imm`3(;;Bsx`_hNSr_Z|0Lr(N_|PAo=AIS>P1@hCVVSTQxX-3gzgusmcKZ z2t(u^KzxyS|HVb+V@E?;`VqO8F;m!lFphRUTq^A2{dJP89Z0fPEjg0U`zi@ia;D2^pm9+WFVCnlhR$gZMIX}_4L&3v6DN>h zJ=N@X&WP8cfDm&dlOuY>Cr(tO-WWRP^ri*K;c#u};fK!q!TbN@C~=R2y(*$gOYMMc z(bZ2yZ`UGODO82T=SLL9LGOJHi;ZM(vw~Un$XdD{WW#*QhzVrpQtMIdeBw_eAqjD+ z*6g$>NsZADv%cvu_60z4@JuQ9W0VLf0#q{713FuX)`_GqnykD zPiaIR3Q%DmC=JN~6kPTQD2g3yn5M?_Cj+(3Tv@}F{xAxtm0Alaxu;~VB==z#H|Ku7L*xz{jB}wm7jm<)!CBjZum8VMg2r5i3Z_IT^DGa9O zKcd}?XQ;}m|Dbj|&0>5gn)e%L!e%8-OVn51LmdV;Gb+t4Y20k+%gSYdu8o_eLP>=- zpLkDe@{SBS=)tyx+1Ml`n;wNTMqk25L8O6=H-8rXqmr0@EW%t#4bhI$@F8%M;NS!@ zz5~i|X>TgmcYF={7(m~7UhYfq_FKxGtw(-W=4#Wv&HFU6z{^IIH?t;_+}i(h`e+(D>_(KHga<(*FLe2r(Fj zR80_F&zGC?76BxA7>|Z*li3&35&q??3O=M081V@bFwfVikS;>R8+xGH{~|X!BPv3) z*ARO=7Bk>}fZ#ohJ`8U6fT10bJe6>Y!^X4%t#rNTJViw#Tq9>QrT5!qM!-l^k#K@{zHxyKvesXOo>?95`w4xc$7i+e)cACWtG|t$P|a_4R$d)%Akah ze?}jD_!z>mlT%Tx5oTW#= z#>5$p9*p){K~PnyQV~Ll)$TyE56M%8*F6mctf9aLB2Qj`L*fw-P`r(s*LR+x zNU#R{)@}rB0Aik-9d|8Cp%A4GsmPlVhbqy0%0;AF0*cXpJ1f+N~sG%c?t{9UR{*_fshY$3MroELO|{e1L&2R6d?jhBeT&z zJN_tq?}PXTqzHk#fkk>}`>TS@bP;Tbz+0Z86id=v*dIOU@VIfK1bK?GYgt1Gkm$_q zSyem^xULjR9I8SUtrAimQig!Ml*j0b0)80@g^Umw0;9M${U|*jJ{Ah7OIC(-1iS@b z2njE1NR98Z(Uaai24=efZss6*hHWW4glyyI)bMJKiYXji;L5C zR+USnBwEb4L>_=n>4_x*KOVswtI^14BR;EoAHm6$Ec~b0pq7pBtm+-Y9HfmHO9g;C zHRGQfIKEHPd*LXsA!$@Pnt`|oV0`FxU2`pp0)=0ep6LPx95hsw3jrYJ!u=Vat^vRo zX(P6jEc^?qibwZBJR}{ep%Ri^&IcgY{`;H|fO#u67HCQiHl*p-p3x%P4lF{Lu#CPf z#()dl0AGeRlD@i3n*kRE0=U#$2Iabn2?JKTPga5^7!!x8zVID1#MvwsV}`2I(u^mR z6~h5a6|ga<+)Hw;Hx9sET#xi#HDCQCLkgavXyqMFjEVJM=};9Mb89h#Y8GKBpi8nC zvjYtq5^M!6251W-ExH6QP_zkL z!6@nmja#Hm5Cn*~6m?R|hV8--prf#-t8YGB`%Vo zg^_AiA8?7`dvDI%@4M&Rcka0tZsI0x;wEn5CjQ?-TVj!Jz zsE>&3AMJd&q~Us#x@rObde>0r+69+JegeLM{VgQImbdN6-!A^}dK0@;$O~Om(y#D* z!{sz%4C|^)Eaj-Tzx{IG`s+>Xss!*2xSqM}=KF@aNNm?u z%@rH<4V>x|c>eXKHwMo)PS)kMr@vhM@br87Vgy|O$^`&eSafyYpJfD2o{{%o=*EQw z@WAt~cb{h$0)m$=0Amx|jRfx^bY=?h#1k@;^-0+OM*?`3kaB=0jAW;nLTsi0M^}ip zSb~&&W5q!Kh=ilR)POVjy14tv#_#9l=!XQtI;V<~a|Ab52moX~Gc@tpV6ww0cbE9i z>z#>J3v!h|$uSH>u6JjD9S|@&@mWgvsBOg4AGW4P4z)Q`@_~SsH>3cL>nA7w@B6;d zt}jv{t)~zc;_?iCS|H`r#Mg1NDJ_MxwVXQQQeKwbPYv|6X-x`gYo%mL>8XsByg^++ zOjSU+GNqMN_H@^nP5!>;($@R|iMC^`+4hI{o{Loa>J483%rs(3DqCx*(w3asAt7hy z73ljO2lhUXSyId5+D*jU8tCsn&gwgU=eh_W{iyP6jmsquG;>UV1@^1&(@YyDXFL4n2iYNYOQCuAQ#~}n2yu5SNbej zx(@Tl^3z%p6C?o#M)M?0@~cwDzB%dhKVC*YJ%arWL>(C2XyQuxI1F|LS+X#2a#1aR_TyJMlT48+oJ-CvTPfa{Gd>g&b@C_y zt*>d)C8ke;zOuEH#-5f3aXAZNFVzPpUSap|eUrEA>R6UYkSio>RxD-B=f23HK^J8_ zd?yXQ4?>XX@1i<7mvBz*H5FjGw@GPiX-Q>EDVWm4#hdY5h6cLm?ro*z$S+u4(8Z#= z?raedEIgr#(^4l1ut2qh+B(SR^!YVY3K^HUzWw~qffp%}6~eBlrl28oZIb@5#n zwd7X*`EULLtE>vK=oW%y)%a-}XWxI4S5t?;kQeXfj_r@lSe9N5s)L81u2%-4L-+{MSFu_962}>r#XxH!I8bJ6|gH>M6c15v^BBqk;iA1JU}9Tiobv7 zW#%tkkC(MMar`KC4?TI&=cPKI3QCMfN5XNv^DdG8?mKB`B0hX$gp%Mo8S-)sHFbCK z((b)DiC(1A6nOC7K@Mv<)g+h!xHumP}AO zr-b#lY{m+PubQYaJ_DU6NDX!p6d3~2!_5p6%5>1u(n2^KA`l1wf{dO^D1RQYg`3DP zsh(mdpk<#B!X2}T{U>(?7ij<`jExfEF%LjU9|SIEx?1Q9gs?{}@|Lc}O%9Sg`9B!n zWo|_!k&z*WtpJ0=5D6$2gdMbkSW!8v*WWWMqdd@g46peO3Znt6;bHRgW5mz&@`VQ; z1nB_V$s^G62Nsm8XFsl#bN}4LEe3L55Diw(AdDT)$15Z4AI)s zf-#UA$)TmSlk(C+T-TjZ+SB5^2N+>e=LJyM=bn6w2)MN1j#>;cz7 zOSp5xz5MF*aaL*Q$%)~bu~buuv6CVlXKnJ!D`_}+7*{B&7Oq69D=T~m;o~?C&CSh> zjEvxU?pQZffI|oN)78;VcV`E|V2JruRVd3M7R^D9@tOjur>uu2g@6+2iE4@Q%tYqO z>Qx(fAD1uQzJfQp9GVX9#R@H_{iD4M4)xONC$KwuPz9w(%VMIIq@%KM($;X0`|tY-(ZaIJ62P4MGZo2XlAfM8B}Iii_6NVun{U2JdwV;P{5ja^EG0z+ zGxj$o)&Pw4G(A4vpuyG>v;-v(N{*$Cd9i#}mR0k@^FKu&ev3IpMfgJS`a8SuqJ_v? zw{h#%d*N&xV+6*SbM?f4Rvhl^$6EdwM*F+?)5rguhL!`gwS2^c{Yz;AGlRbY(wPiy z*1@)IwrttLgAYDPd3iaxkqEY(CX>lr$d;JksV94Y)*|JPkDjlDm|Q(wuw*&gwPbHs z8;f;{_nTTN+_W7(kV8vL8zP)bqWu)X(Gem`ZUJM)g%DUuqVQ-q9p~ta|3y`4DXG5G zWPP7ywQDZP12T4MO4TS73L%BUc3g7v^62jF=BcNiV)5d|l$V#&-gAa@+Gg1H$YxRm z!{_&Wv=m}u5|onEc@L1Jr5IZ+NdanFo4>G@?JL(3$cCwi;6JnI3Mq2by)=)0uVgYjmRpFA3caOnw;@F7i#lrH(;Aw5CH_YIUVQ)~%2HU~&C#`&BF zNF{AGUIp?E!n;77F28UtTkhP>ykqse($PVncpgb%=H%U>2ncL3zBl3-)F&<0s%lmeJM=#3;(3_7 z0-A*6*mFxeVU~w8@X%yBbVg< zj89i890q1)lr?pCuxRyd^mR003=~%^LI(n$V9H26&QqC5bAVJbHE|@!;7Rcb+rxoC zkO#Kj&oeJSi?w_sZt^U}fgstegAjsfej#^m*}=NH+i7Vz#-YReXzz>T_&()@#Z+$I zj+n*mXRR&Ds+V7)mO_g03i!kvAOPMtZoWC3<#B*?CR^b8CU|KUKV8sLWI{n}5FpRu zA9mD=r!{~P-l**=jPZq?@sw{2H{1ys0a6I%ODTj9CO2S4VLSv9181AvKs1obcrtBg z#`C8ePytXb1apGQPYfiRC$0*h=qK;BzZc9aSmYUL-M|8%grCg}4>teq=E^NV-#Gz* z?Z8qX2M7SyXAAe&2M@?Hc2MdB@aBX7Fm{Zz0$2i+0dp98@7ySdWqig`-yxs}I0bY9 znF#>|Vi0*Xe}+O$9b O0000\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Simple Photo Editor" +msgstr "Editeur de photographie simplifié" + +#: .project:2 +msgid "This is a simple photo editor I made for my father. It is too complex for him, but it is still a good Gambas example!" +msgstr "" + +#: FBrightness.form:40 FMain.class:200 +msgid "Balance" +msgstr "Balance" + +#: FMain.class:107 +msgid "No image in directory" +msgstr "Aucune image dans ce répertoire" + +#: FMain.class:109 +msgid "Unable to load image" +msgstr "Impossible de charger l'image" + +#: FMain.class:200 +msgid "Automatic correction" +msgstr "Correction automatique" + +#: FMain.class:200 +msgid "Blur" +msgstr "Flou" + +#: FMain.class:200 +msgid "Browse photos" +msgstr "Parcourir les photos" + +#: FMain.class:200 +msgid "Crop image" +msgstr "Découper l'image" + +#: FMain.class:200 +msgid "Fit to window" +msgstr "Zoom à la taille de l'écran" + +#: FMain.class:200 +msgid "Flip horizontally" +msgstr "Retourner horizontalement" + +#: FMain.class:200 +msgid "Flip vertically" +msgstr "Retourner verticalement" + +#: FMain.class:200 +msgid "Invert" +msgstr "Inverser" + +#: FMain.class:200 +msgid "Normalize" +msgstr "Normaliser" + +#: FMain.class:200 +msgid "Oil painting effect" +msgstr "Effet de peinture à l'huile" + +#: FMain.class:200 +msgid "Quit" +msgstr "Quitter" + +#: FMain.class:200 +msgid "Remove speckles" +msgstr "Enlever les tâches" + +#: FMain.class:200 FResize.form:42 +msgid "Resize" +msgstr "Redimensionner" + +#: FMain.class:200 +msgid "Rotate left" +msgstr "Rotation vers la gauche" + +#: FMain.class:200 +msgid "Rotate right" +msgstr "Rotation vers la droite" + +#: FMain.class:200 +msgid "Save" +msgstr "Enregistrer" + +#: FMain.class:200 +msgid "Save all" +msgstr "Tout enregistrer" + +#: FMain.class:200 +msgid "Select photo directory" +msgstr "Choisir le répertoire des photographies" + +#: FMain.class:200 +msgid "Sharpen" +msgstr "Netteté" + +#: FMain.class:200 +msgid "Show photo" +msgstr "Afficher la photographie" + +#: FMain.class:200 +msgid "Undo all changes" +msgstr "Annuler tous les changements" + +#: FMain.class:200 +msgid "Zoom 100%" +msgstr "Zoom 100%" + +#: FMain.class:200 +msgid "Zoom in" +msgstr "Zoom avant" + +#: FMain.class:200 +msgid "Zoom out" +msgstr "Zoom arrière" + +#: FMain.class:222 +msgid "*" +msgstr "*" + +#: FMain.class:222 +msgid "/" +msgstr "/" + +#: FMain.class:222 +msgid "B" +msgstr "F" + +#: FMain.class:222 +msgid "D" +msgstr "T" + +#: FMain.class:222 +msgid "E" +msgstr "E" + +#: FMain.class:222 +msgid "H" +msgstr "H" + +#: FMain.class:222 +msgid "I" +msgstr "I" + +#: FMain.class:222 +msgid "M" +msgstr "M" + +#: FMain.class:222 +msgid "N" +msgstr "N" + +#: FMain.class:222 +msgid "O" +msgstr "P" + +#: FMain.class:222 +msgid "R" +msgstr "R" + +#: FMain.class:222 +msgid "S" +msgstr "S" + +#: FMain.class:222 +msgid "V" +msgstr "V" + +#: FScissors.form:24 +msgid "Cut" +msgstr "Découper" diff --git a/app/examples/Image/PhotoTouch/.lang/nl.po b/app/examples/Image/PhotoTouch/.lang/nl.po new file mode 100644 index 00000000..0676709e --- /dev/null +++ b/app/examples/Image/PhotoTouch/.lang/nl.po @@ -0,0 +1,183 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PhotoTouch 3.5.90\n" +"POT-Creation-Date: 2014-09-27 00:14 UTC\n" +"PO-Revision-Date: 2014-09-25 21:54 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Simple Photo Editor" +msgstr "Eenvoudige Foto Editor" + +#: .project:2 +msgid "This is a simple photo editor I made for my father. It is too complex for him, but it is still a good Gambas example!" +msgstr "Dit is een eenvoudige foto editor die ik voor mijn vader maakte. Het is te complex voor hem, maar nog steeds een goed Gambas voorbeeld!" + +#: FBrightness.form:40 FMain.class:200 +msgid "Balance" +msgstr "Balans" + +#: FMain.class:107 +msgid "No image in directory" +msgstr "Geen afbeeldingsmap" + +#: FMain.class:109 +msgid "Unable to load image" +msgstr "Onmogelijk om afbeelding te laden" + +#: FMain.class:200 +msgid "Automatic correction" +msgstr "Automatische correctie" + +#: FMain.class:200 +msgid "Blur" +msgstr "Vervagen" + +#: FMain.class:200 +msgid "Browse photos" +msgstr "Browse foto's" + +#: FMain.class:200 +msgid "Crop image" +msgstr "Afbeelding snijden" + +#: FMain.class:200 +msgid "Fit to window" +msgstr "Aanpassen aan venster" + +#: FMain.class:200 +msgid "Flip horizontally" +msgstr "Horizontaal spiegelen" + +#: FMain.class:200 +msgid "Flip vertically" +msgstr "Verticaal spiegelen" + +#: FMain.class:200 +msgid "Invert" +msgstr "Omkeren" + +#: FMain.class:200 +msgid "Normalize" +msgstr "Normaliseren" + +#: FMain.class:200 +msgid "Oil painting effect" +msgstr "Olieverf effect" + +#: FMain.class:200 +msgid "Quit" +msgstr "Afsluiten" + +#: FMain.class:200 +msgid "Remove speckles" +msgstr "Verwijder spikkels" + +#: FMain.class:200 FResize.form:42 +msgid "Resize" +msgstr "Formaat wijzigen" + +#: FMain.class:200 +msgid "Rotate left" +msgstr "Links roteren" + +#: FMain.class:200 +msgid "Rotate right" +msgstr "Rechts roteren" + +#: FMain.class:200 +msgid "Save" +msgstr "Opslaan" + +#: FMain.class:200 +msgid "Save all" +msgstr "Alles opslaan" + +#: FMain.class:200 +msgid "Select photo directory" +msgstr "Selecteer foto map" + +#: FMain.class:200 +msgid "Sharpen" +msgstr "Verscherpen" + +#: FMain.class:200 +msgid "Show photo" +msgstr "Foto weergeven" + +#: FMain.class:200 +msgid "Undo all changes" +msgstr "Alle wijzigingen ongedaan maken" + +#: FMain.class:200 +msgid "Zoom 100%" +msgstr "Zoom 100%" + +#: FMain.class:200 +msgid "Zoom in" +msgstr "Zoom in" + +#: FMain.class:200 +msgid "Zoom out" +msgstr "Zoom uit" + +#: FMain.class:222 +msgid "*" +msgstr "-" + +#: FMain.class:222 +msgid "/" +msgstr "-" + +#: FMain.class:222 +msgid "B" +msgstr "-" + +#: FMain.class:222 +msgid "D" +msgstr "-" + +#: FMain.class:222 +msgid "E" +msgstr "-" + +#: FMain.class:222 +msgid "H" +msgstr "-" + +#: FMain.class:222 +msgid "I" +msgstr "-" + +#: FMain.class:222 +msgid "M" +msgstr "-" + +#: FMain.class:222 +msgid "N" +msgstr "-" + +#: FMain.class:222 +msgid "O" +msgstr "-" + +#: FMain.class:222 +msgid "R" +msgstr "-" + +#: FMain.class:222 +msgid "S" +msgstr "-" + +#: FMain.class:222 +msgid "V" +msgstr "-" + +#: FScissors.form:24 +msgid "Cut" +msgstr "Knippen" diff --git a/app/examples/Image/PhotoTouch/.lang/ru.po b/app/examples/Image/PhotoTouch/.lang/ru.po new file mode 100644 index 00000000..453cec3a --- /dev/null +++ b/app/examples/Image/PhotoTouch/.lang/ru.po @@ -0,0 +1,212 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Image/PhotoTouch/.project:25 +msgid "Simple Photo Editor" +msgstr "Простой редактор фотографий" + +#: app/examples/Image/PhotoTouch/.project:26 +msgid "" +"Photo editor example.\n" +"\n" +"This is a simple photo editor I made for my father. It has an overlay GUI with transparent buttons and state transitions. It is too complex for him, but it is still a good Gambas example!" +msgstr "" +"Пример редактора фотографий.\n" +"\n" +"Это простой редактор фотографий, который я сделал для своего отца. Он имеет оверлейный графический интерфейс с прозрачными кнопками и переходами между состояниями. Это слишком сложно для него, но это всё ещё хороший пример Gambas!" + +#: app/examples/Image/PhotoTouch/.src/FBrightness.form:29 app/examples/Image/PhotoTouch/.src/FMain.class:195 +msgid "Balance" +msgstr "Баланс" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:107 app/examples/Image/PhotoTouch/.src/FMain.class:1050 +msgid "No image in directory" +msgstr "Нет изображения в директории" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:109 +msgid "Unable to load image" +msgstr "Невозможно загрузить изображение" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:176 +msgid "Browse photos" +msgstr "Просмотр фотографий" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:177 +msgid "Show photo" +msgstr "Показать фотографию" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:178 app/examples/Image/PhotoTouch/.src/FMain.class:450 +msgid "Select photo directory" +msgstr "Выбрать директорию фотографий" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:179 +msgid "Zoom in" +msgstr "Увеличить" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:180 +msgid "Zoom out" +msgstr "Уменьшить" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:181 +msgid "Zoom 100%" +msgstr "Масштаб 100%" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:182 +msgid "Fit to window" +msgstr "Уместить в окне" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:183 +msgid "Flip horizontally" +msgstr "Отразить по горизонтали" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:184 +msgid "Flip vertically" +msgstr "Отразить по вертикали" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:185 +msgid "Rotate left" +msgstr "Поворот влево" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:186 +msgid "Rotate right" +msgstr "Поворот вправо" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:187 +msgid "Automatic correction" +msgstr "Автоматическая коррекция" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:188 +msgid "Invert" +msgstr "Инвертировать" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:189 +msgid "Blur" +msgstr "Размытие" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:190 +msgid "Sharpen" +msgstr "Повысить резкость" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:191 +msgid "Normalize" +msgstr "Нормализовать" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:192 +msgid "Remove speckles" +msgstr "Удаление пятен" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:193 +msgid "Oil painting effect" +msgstr "Эффект масляной живописи" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:194 +msgid "Crop image" +msgstr "Обрезать изображение" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:196 app/examples/Image/PhotoTouch/.src/FResize.form:31 +msgid "Resize" +msgstr "Изменение размера" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:197 +msgid "Save" +msgstr "Сохранить" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:198 +msgid "Save all" +msgstr "Сохранить все" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:199 +msgid "Undo all changes" +msgstr "Откатить все изменения" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:200 +msgid "Quit" +msgstr "Выход" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:208 +msgid "H" +msgstr "H" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:209 +msgid "V" +msgstr "V" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:212 +msgid "M" +msgstr "M" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:213 +msgid "I" +msgstr "I" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:214 +msgid "B" +msgstr "B" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:215 +msgid "N" +msgstr "N" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:216 +msgid "E" +msgstr "E" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:217 +msgid "D" +msgstr "D" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:218 +msgid "O" +msgstr "O" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:219 +msgid "/" +msgstr "/" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:220 +msgid "R" +msgstr "R" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:221 +msgid "*" +msgstr "*" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:222 +msgid "S" +msgstr "S" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:922 +msgid "image" +msgstr "изображение" + +#: app/examples/Image/PhotoTouch/.src/FMain.class:923 +msgid "image2" +msgstr "изображение2" + +#: app/examples/Image/PhotoTouch/.src/FScissors.form:16 +msgid "Cut" +msgstr "Вырезать" + diff --git a/app/examples/Image/PhotoTouch/.project b/app/examples/Image/PhotoTouch/.project new file mode 100644 index 00000000..aa188e8f --- /dev/null +++ b/app/examples/Image/PhotoTouch/.project @@ -0,0 +1,24 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Simple Photo Editor +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop.x11 +Component=gb.desktop +Component=gb.form.dialog +Component=gb.settings +Component=gb.image.effect +Description="Photo editor example.\n\nThis is a simple photo editor I made for my father. It has an overlay GUI with transparent buttons and state transitions. It is too complex for him, but it is still a good Gambas example!" +Authors="Benoît Minisini" +TabSize=2 +Translate=1 +Language=fr +Vendor=Example +Packager=1 +Tags=Example,2DGraphics,ImageProcessing,Graphics +Screenshot=.hidden/screenshots/phototouch.jpg +CreateMenu=1 diff --git a/app/examples/Image/PhotoTouch/.src/CAnimation.class b/app/examples/Image/PhotoTouch/.src/CAnimation.class new file mode 100644 index 00000000..6884983b --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/CAnimation.class @@ -0,0 +1,97 @@ +' Gambas class file + +Static Private All As New CAnimation[] + +Property Read Object As Object + +Private $hObject As Object +Private $sProperty As String +Private $fTarget As Float +Private $fTime As Float +Private $hTimer As Timer + +Static Public Sub Start(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer) + + Dim hAnim As CAnimation + Dim aStop As New CAnimation[] + + For Each hAnim In All + If hAnim.Object = hObject Then aStop.Add(hAnim) + Next + + For Each hAnim In aStop + hAnim.Stop + Next + + hAnim = New CAnimation(hObject, sProperty, fTarget, iTime) + All.Add(hAnim) + +End + +Static Public Sub Exit() + + While All.Count + All[0].Stop + Wend + +End + + + +Public Sub _new(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer) + + 'Debug hObject.Tag;; sProperty;; fTarget + + $hObject = hObject + $sProperty = sProperty + $fTarget = fTarget + $fTime = Timer + iTime / 1000 + + $hTimer = New Timer As "Timer" + $hTimer.Delay = 50 + $hTimer.Start + +End + +Public Sub Timer_Timer() + + Dim fValue As Float + Dim iSign As Integer + + fValue = Object.GetProperty($hObject, $sProperty) + + If Abs(fValue - $fTarget) < 1E-6 Then + Object.SetProperty($hObject, $sProperty, $fTarget) + {Stop} + Else + iSign = Sgn($fTarget - fValue) + fValue += ($fTarget - fValue) / (1000 * ($fTime - Timer) / $hTimer.Delay) + If Sgn($fTarget - fValue) <> iSign Then + Object.SetProperty($hObject, $sProperty, $fTarget) + {Stop} + Else + Object.SetProperty($hObject, $sProperty, fValue) + Endif + Endif + +Catch + + {Stop} + +End + +Public Sub Stop() + + $hTimer.Stop + $hTimer = Null + $hObject = Null + + All.Remove(All.Find(Me)) + +End + +Private Function Object_Read() As Object + + Return $hObject + +End diff --git a/app/examples/Image/PhotoTouch/.src/CButton.class b/app/examples/Image/PhotoTouch/.src/CButton.class new file mode 100644 index 00000000..d4313720 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/CButton.class @@ -0,0 +1,189 @@ +' Gambas class file + +Export + +Inherits DrawingArea + +Event Click + +Property Image As Image +Property Opacity As Float +Property Highlight As Boolean +Property Shortcut As String + +Public Const MIN_OPACITY As Float = 0.3 +Public Const MAX_OPACITY As Float = 0.8 + +Private $hObs As Observer +Private $hImage As Image +Private $hDraw As Image +'Private $bInside As Boolean +'Private $hTimer As Timer +Private $fOpacity As Float = MIN_OPACITY +Private $bHighlight As Boolean +Private $hTimer As Timer +Private $sShortcut As String + +Public Sub _new() + + $hObs = New Observer(Me) As "DrawingArea" + '$hTimer = New Timer As "Timer" + '$hTimer.Delay = 50 + Me.Mouse = Mouse.Pointing + $hTimer = New Timer As "Timer" + +End + +Public Sub DrawingArea_Enter() + + 'Debug + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MAX_OPACITY, 250) + +End + +Public Sub DrawingArea_Leave() + + 'Debug + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MIN_OPACITY, 250) + +End + +Public Sub DrawingArea_MouseUp() + + If Not Me.Enabled Then Return + Raise Click + Stop Event + +End + +'Public Sub DrawingArea_DblClick() +' +' Raise Click +' Stop Event +' +'End + + +' Public Sub Timer_Timer() +' +' If $bInside Then +' $fOpacity = Min(MAX_OPACITY, $fOpacity + 0.1) +' If $fOpacity >= MAX_OPACITY Then +' $hTimer.Stop +' Endif +' Else +' $fOpacity = Max(MIN_OPACITY, $fOpacity - 0.1) +' If $fOpacity <= MIN_OPACITY Then +' $hTimer.Stop +' Endif +' Endif +' +' SetOpacity +' +' End + + +Public Sub DrawingArea_Draw() + + If $hDraw Then Draw.Image($hDraw, 2, 2) + + If $bHighlight Then + Paint.Begin(Me) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + Paint.Rectangle(0, 0, Me.W, Me.H) + Paint.Fill + Paint.End + Endif + +End + +Private Function Image_Read() As Image + + Return $hImage + +End + +Private Sub Image_Write(Value As Image) + + $hImage = Value.Stretch(Me.W - 4, Me.H - 4) + SetOpacity + +End + +Private Sub SetOpacity() + + Dim X, Y As Integer + + $hDraw = $hImage.Copy() + If $sShortcut Then + + Paint.Begin($hDraw) + + Paint.Font = Font["Bold,+3"] + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 64)) + 'For X = -2 To 2 + ' For Y = -2 To 2 + ' Paint.DrawText($sShortcut, $hDraw.W - 16 + X, $hDraw.H - 16 + Y, 16, 16, Align.Center) + ' Next + 'Next + Paint.Arc($hDraw.W - 12, $hDraw.H - 12, 12) + Paint.Fill + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + Paint.DrawText($sShortcut, $hDraw.W - 24, $hDraw.H - 24, 24, 24, Align.Center) + + Paint.End + + Endif + + $hDraw.Opacity($fOpacity) + Me.Refresh + +End + +Private Function Opacity_Read() As Float + + Return $fOpacity + +End + +Private Sub Opacity_Write(Value As Float) + + 'Debug Value + $fOpacity = Max(0, Min(1, Value)) + SetOpacity + +End + +Private Function Highlight_Read() As Boolean + + Return $bHighlight + +End + +Private Sub Highlight_Write(Value As Boolean) + + $bHighlight = Value + Me.Refresh + +End + + +Private Function Shortcut_Read() As String + + Return $sShortcut + +End + +Private Sub Shortcut_Write(Value As String) + + $sShortcut = Value + SetOpacity + Me.Refresh + +End diff --git a/app/examples/Image/PhotoTouch/.src/FBrightness.class b/app/examples/Image/PhotoTouch/.src/FBrightness.class new file mode 100644 index 00000000..8a9bc6df --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FBrightness.class @@ -0,0 +1,146 @@ +' Gambas class file + +Private $bNoChange As Boolean + +Private $MX As Integer +Private $MY As Integer +Private $hRect As Rect +Private $bBegin As Boolean + +Private btnUndo As CButton +Private btnApply As CButton +Private btnBrightness As CButton +Private btnContrast As CButton +Private btnGamma As CButton + +Public Sub _new() + + btnUndo = New CButton(Me) As "btnUndo" + btnUndo.Move(Me.W - 96 - Desktop.Scale * 4, Desktop.Scale * 3, 48, 48) + btnUndo.Image = Image.Load("undo.png") + + btnApply = New CButton(Me) As "btnApply" + btnApply.Move(btnUndo.X + btnUndo.W + Desktop.Scale, btnUndo.Y, 48, 48) + btnApply.Image = Image.Load("ok.png") + + btnBrightness = New CButton(Me) + btnBrightness.Enabled = False + btnBrightness.Move(Desktop.Scale * 3, Desktop.Scale * 10, 48, 48) + btnBrightness.Image = Image.Load("brightness.png") + btnBrightness.Shortcut = "B" + sldBrightness.Move(btnBrightness.X + 64, btnBrightness.Y + 4) + sldBrightness.Tag = btnBrightness + + btnContrast = New CButton(Me) + btnContrast.Enabled = False + btnContrast.Move(btnBrightness.X, btnBrightness.Y + 64, 48, 48) + btnContrast.Image = Image.Load("contrast.png") + btnContrast.Shortcut = "C" + sldContrast.Move(btnContrast.X + 64, btnContrast.Y + 4) + sldContrast.Tag = btnContrast + + btnGamma = New CButton(Me) + btnGamma.Enabled = False + btnGamma.Move(btnBrightness.X, btnContrast.Y + 64, 48, 48) + btnGamma.Image = Image.Load("gamma.png") + btnGamma.Shortcut = "G" + sldGamma.Move(btnGamma.X + 64, btnGamma.Y + 4) + sldGamma.Tag = btnGamma + +End + + +Public Sub Form_Open() + + Me.Center + +End + +Public Sub btnUndo_Click() + + $bNoChange = True + sldBrightness.Value = 50 + sldContrast.Value = 50 + sldGamma.Value = 50 + $bNoChange = False + Balance_Change + +End + +Public Sub btnApply_Click() + + If $bBegin Then FMain.Apply + +End + +Public Sub Balance_Change() + + If $bNoChange Then Return + If Not $bBegin Then + FMain.Begin + $bBegin = True + Endif + FMain.Balance(sldBrightness.Value, sldContrast.Value, sldGamma.Value) + +End + +Public Sub panBrightness_MouseDown() + + $MX = Mouse.ScreenX + $MY = Mouse.ScreenY + $hRect = Rect(Me.X, Me.Y, Me.W, Me.H) + +End + +Public Sub panBrightness_MouseMove() + + Dim X, Y As Integer + + X = Min(Max($hRect.X + Mouse.ScreenX - $MX, 0), FMain.W - $hRect.W) + Y = Min(Max($hRect.Y + Mouse.ScreenY - $MY, 0), FMain.H - $hRect.H) + Me.Move(X, Y) + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Backspace Then + btnUndo_Click + Stop Event + Return + Endif + + If Key.Code = Key.Return Or If Key.Code = Key.Enter Then + btnApply_Click + Stop Event + Return + Endif + + Select Case UCase(Key.Text) + Case "B" + sldBrightness.SetFocus + Case "C" + sldContrast.SetFocus + Case "G" + sldGamma.SetFocus + Case Else + FMain.Form_KeyPress + Return + End Select + + Stop Event + +End + +Public Sub Balance_GotFocus() + + CAnimation.Start(Last.Tag, "Opacity", CButton.MAX_OPACITY, 250) + +End + +Public Sub Balance_LostFocus() + + CAnimation.Start(Last.Tag, "Opacity", CButton.MIN_OPACITY, 250) + +End + diff --git a/app/examples/Image/PhotoTouch/.src/FBrightness.form b/app/examples/Image/PhotoTouch/.src/FBrightness.form new file mode 100644 index 00000000..37326354 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FBrightness.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,74,40) + Background = &HFFFFFF& + Border = False + Utility = True + Stacking = Window.Above + SkipTaskbar = True + Opacity = 50 + { panBrightness Panel + MoveScaled(1,1,72,38) + Background = &H000000& + { sldBrightness Slider Balance + Name = "sldBrightness" + MoveScaled(13,13,56,3) + Value = 50 + } + { sldContrast Slider Balance + Name = "sldContrast" + MoveScaled(13,22,56,3) + Value = 50 + } + { Label1 Label + MoveScaled(2,2,52,6) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + AutoResize = True + Text = ("Balance") + } + { sldGamma Slider Balance + Name = "sldGamma" + MoveScaled(13,31,56,3) + Value = 50 + } + } +} diff --git a/app/examples/Image/PhotoTouch/.src/FMain.class b/app/examples/Image/PhotoTouch/.src/FMain.class new file mode 100644 index 00000000..f0c33a07 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FMain.class @@ -0,0 +1,1117 @@ +' Gambas class file + +Private CACHE_ROOT As String = "~/.cache/PhotoTouch" +Private CACHE_DIR As String + +Private $aPath As String[] +Private $sPath As String +Private $sDir As String +Private $iIndex As Integer +Private $hImage As Image +Private $hZoom As Image +Private $hTemp As Image +Private $fZoom As Float = 1 +'Private $sInfo As String +Private $sMode As String +Private $bModify As Boolean +Private $cButton As New Collection +Private $bFilm As Boolean = True +Private $bReloadFilm As Boolean +Private $cShortcut As New Collection +Private $hSave As Image +Private $hUndoStack As Image[] + +Private $MX As Integer +Private $MY As Integer + +Private btnPrev As CButton +Private btnNext As CButton +Private dwgPath As DrawingArea + +Private Sub UpdateSaveIcon() + + $cButton["save"].Visible = Exist(CACHE_DIR &/ File.Name($sPath)) Or $bModify + +End + +Private Sub UpdateFilmMode() + + Dim sTag As String + Dim hCtrl As Control + + If $bFilm Then + 'ivwImage.IconSize = 128 + svwImage.Hide + panToolbar.Hide + panBrowser.Show + panToolbarBrowser.Show + ivwImage.SetFocus + Else + svwImage.Show + panToolbar.Show + panBrowser.Hide + panToolbarBrowser.Hide + $cButton["film"].Opacity = CButton.MAX_OPACITY + svwImage.SetFocus + Endif + + btnPrev.Visible = Not $bFilm + btnNext.Visible = Not $bFilm + + If $bFilm Then + $hImage = Null + FillImageBrowser + Else + Try $iIndex = CInt(ivwImage.Key) + LoadImage + Endif + +End + +Private Sub GetImage(Optional bReset As Boolean) As Boolean + + Dim hImage As Image + + If Not bReset Then + Try $sPath = CACHE_DIR &/ $aPath[$iIndex] + If Exist($sPath) Then Try hImage = Image.Load($sPath) + Endif + + If Not hImage Then + Try $sPath = $sDir &/ $aPath[$iIndex] + If $sPath Then Try hImage = Image.Load($sPath) + Endif + + $hImage = hImage + +End + + +Private Sub LoadImage(Optional bReset As Boolean) As Boolean + + Inc Application.Busy + + Me.End + SaveImage + + GetImage(bReset) + + If $hImage Then + svwImage.ResizeContents($hImage.W, $hImage.H) + svwImage.Show + lblError.Hide + Else + svwImage.Hide + lblError.Show + If $aPath.Count = 0 Then + lblError.Text = ("No image in directory") + Else + lblError.Text = ("Unable to load image") + Endif + Endif + + $bModify = False + $hUndoStack = New Image[] + SetZoom(0) + UpdateSaveIcon + dwgInfo.Raise + dwgInfo.Refresh + Me.Refresh + + Dec Application.Busy + +End + +Private Sub CreateButtons(aButton As String[], cTooltip As Collection, hParent As Container) + + Dim sKey As String + Dim sImg As String + Dim hPanel As Panel + Dim hButton As CButton + Dim iPos As Integer + + For Each sKey In aButton + + If sKey = "<->" Then + hPanel = New Panel(hParent) + hPanel.Expand = True + hPanel.Resize(8, 48) + Else If sKey = "-" Then + hPanel = New Panel(hParent) + hPanel.Resize(8, 48) + Else + + sImg = sKey + iPos = InStr(sImg, "#") + If iPos Then sImg = Left(sImg, iPos - 1) + + hButton = New CButton(hParent) As "Button" + hButton.Resize(48, 48) + hButton.Image = Image.Load(sImg & ".png") + hButton.Tag = sKey + hButton.Tooltip = cTooltip[sImg] + hButton.Shortcut = $cShortcut[sImg] + $cButton[hButton.Tag] = hButton + Endif + Next + +End + + +Public Sub _new() + + Dim hCtrl As Control + Dim sImg As String + Dim hButton As CButton + Dim cTooltip As Collection + Dim aButton As String[] + + Application.MainWindow = Me + + Try Mkdir File.Dir(File.Dir(CACHE_ROOT)) + Try Mkdir File.Dir(CACHE_ROOT) + Try Mkdir CACHE_ROOT + + cTooltip = [ + "film": ("Browse photos"), + "photo": ("Show photo"), + "usb": ("Select photo directory"), + "zoom-in": ("Zoom in"), + "zoom-out": ("Zoom out"), + "zoom-original": ("Zoom 100%"), + "zoom-fit": ("Fit to window"), + "hflip": ("Flip horizontally"), + "vflip": ("Flip vertically"), + "rotate-left": ("Rotate left"), + "rotate-right": ("Rotate right"), + "magic": ("Automatic correction"), + "invert": ("Invert"), + "blur": ("Blur"), + "sharpen": ("Sharpen"), + "normalize": ("Normalize"), + "despeckle": ("Remove speckles"), + "oil": ("Oil painting effect"), + "scissors": ("Crop image"), + "balance": ("Balance"), + "resize": ("Resize"), + "save": ("Save"), + "save-all": ("Save all"), + "undo": ("Undo all changes"), + "quit": ("Quit")] + + $cShortcut = [ + "usb": "!", + "zoom-in": "+", + "zoom-out": "-", + "zoom-original": "1", + "zoom-fit": "0", + "hflip": ("H"), + "vflip": ("V"), + "rotate-left": "(", + "rotate-right": ")", + "magic": ("M"), + "invert": ("I"), + "blur": ("B"), + "sharpen": ("N"), + "normalize": ("E"), + "despeckle": ("D"), + "oil": ("O"), + "scissors": ("/"), + "resize": ("R"), + "balance": ("*"), + "save": ("S")] + + aButton = ["film", "-", "zoom-in", "zoom-out", "zoom-original", "zoom-fit", "-", "hflip", "vflip", "rotate-left", "rotate-right", "-", + "magic", "invert", "blur", "sharpen", "normalize", "despeckle", "oil", "-", "scissors", "resize", "balance", "<->", "save", "undo", "quit"] + CreateButtons(aButton, cTooltip, panToolbar) + + aButton = ["photo", "usb", "save-all", "-"] + CreateButtons(aButton, cTooltip, panToolbarBrowser) + + dwgPath = New DrawingArea(panToolbarBrowser) As "dwgPath" + dwgPath.Expand = True + dwgPath.Font = Font["Bold,+5"] + + aButton = ["hflip#2", "vflip#2", "rotate-left#2", "rotate-right#2", "-", "save#2", "undo#2", "quit#2"] + CreateButtons(aButton, cTooltip, panToolbarBrowser) + + btnPrev = New CButton(Me) As "Button" + btnPrev.Resize(64, 64) + btnPrev.Ignore = True + btnPrev.Tag = "previous" + btnPrev.Image = Image.Load("previous.png") + + btnNext = New CButton(Me) As "Button" + btnNext.Resize(64, 64) + btnNext.Ignore = True + btnNext.Tag = "next" + btnNext.Image = Image.Load("next.png") + + panToolbar.Raise + panToolbarBrowser.Raise + +End + + +Public Sub svwImage_Draw() + + Dim X As Integer + Dim Y As Integer + Dim XR As Integer + Dim YR As Integer + Dim SX, SX2 As Integer + Dim SY, SY2 As Integer + Dim DX As Integer + Dim DY As Integer + Dim C As Integer + Dim SW, SH As Integer + Dim hZoom As Image + Dim iZoom As Integer + + If Not $hImage Then Return + + If $fZoom > 1 Then + + iZoom = $fZoom + + Draw.LineStyle = Line.None + Draw.FillStyle = Fill.Solid + + DX = Max(0, (Me.W - $hImage.W * iZoom) / 2) + DY = Max(0, (Me.H - $hImage.H * iZoom) / 2) + + SX = (Draw.Clip.X - DX) \ iZoom + SY = (Draw.Clip.Y - DY) \ iZoom + SX2 = (Draw.Clip.X - DX + Draw.Clip.W - 1) \ iZoom + SY2 = (Draw.Clip.Y - DY + Draw.Clip.H - 1) \ iZoom + + SX = Max(0, SX) + SX2 = Min($hImage.Width - 1, SX2) + SW = SX2 - SX + 1 + + SY = Max(0, SY) + SY2 = Min($hImage.Height - 1, SY2) + SH = SY2 - SY + 1 + + 'If $fZoom > 5 Then + ' Draw.LineStyle = Line.Solid + ' Draw.Foreground = &H989898 + 'Else + ' Draw.LineStyle = Line.None + 'Endif + + Draw.Zoom($hImage, iZoom, SX * iZoom + DX, SY * iZoom + DY, SX + svwImage.ScrollX \ iZoom, SY + svwImage.ScrollY \ iZoom, SW, SH) + + Else If $fZoom = 1 Then + + If $hImage.W < Me.W Then + X = (Me.W - $hImage.W) / 2 + Else + X = - svwImage.ScrollX + Endif + + If $hImage.H < Me.H Then + Y = (Me.H - $hImage.H) / 2 + Else + Y = - svwImage.ScrollY + Endif + + Draw.Image($hImage, X, Y) + + Else + + If $hZoom.W < Me.W Then + X = (Me.W - $hZoom.W) / 2 + Else + X = - svwImage.ScrollX + Endif + + If $hZoom.H < Me.H Then + Y = (Me.H - $hZoom.H) / 2 + Else + Y = - svwImage.ScrollY + Endif + + Draw.Image($hZoom, X, Y) + + Endif + +End + +Public Sub Form_Resize() + + panToolbar.Move(0, 0, Me.W, 48 + Desktop.Scale * 2) + panToolbarBrowser.Move(0, 0, Me.W, 48 + Desktop.Scale * 2) + panMargin.H = panToolbar.H + dwgInfo.Move(8, Me.H - dwgInfo.H, Me.W, dwgInfo.H) + btnPrev.Move(8, (Me.H - btnPrev.H) / 2) + btnNext.Move(Me.W - 8 - btnNext.W, (Me.H - btnNext.H) / 2) + +End + +Public Sub Button_Click() + + Action(Last.Tag) + +End + +Private Sub UpdateZoom() + + Dim X, Y As Float + + If Not $hImage Then Return + + If $fZoom < 1 Then + $hZoom = $hImage.Stretch($hImage.W * $fZoom, $hImage.H * $fZoom) + Else + $hZoom = Null + Endif + + X = (svwImage.ScrollX + svwImage.ClientW / 2) / svwImage.ScrollW + Y = (svwImage.ScrollY + svwImage.ClientH / 2) / svwImage.ScrollH + + svwImage.ResizeContents($hImage.W * $fZoom, $hImage.H * $fZoom) + svwImage.Scroll(X * svwImage.ScrollW - svwImage.ClientW / 2, Y * svwImage.ScrollH - svwImage.ClientH / 2) + + dwgInfo.Refresh + Me.Refresh + +End + +Private Sub SetZoom(fZoom As Float) + + If Not $hImage Then Return + + If fZoom = 0 Then + $fZoom = 0 + fZoom = Min(Me.W / $hImage.W, Me.H / $hImage.H) + fZoom = Min(1, fZoom) + Endif + + fZoom = Max(1 / 32, Min(32, fZoom)) + If fZoom = $fZoom Then Return + If fZoom <> 1 Then + If ($hImage.W * fZoom) < 96 Or If ($hImage.H * fZoom) < 96 Then Return + Endif + + $fZoom = fZoom + UpdateZoom + +End + +Public Sub SetMode(sMode As String) + + If $sMode Then + $cButton[$sMode].Highlight = False + Cancel + Select Case $sMode + Case "balance" + FBrightness.Close + Case "scissors" + FScissors.Close + Case "resize" + FResize.Close + End Select + Endif + + If sMode = $sMode Then + $sMode = "" + Return + Endif + + If sMode Then + + $cButton[sMode].Highlight = True + + Select Case sMode + Case "balance" + FBrightness.Show + Case "scissors" + FScissors.Show + Case "resize" + FResize.Show + End Select + + Endif + + $sMode = sMode + +End + + +Private Sub Action(sAction As String) + + Dim iPos As Integer + + Select Case sAction + + Case "usb" + SetMode("") + Dialog.Title = ("Select photo directory") + Dialog.Path = $sDir + If Dialog.SelectDirectory() Then Return + + End Select + + Inc Application.Busy + + iPos = RInStr(sAction, "#") + If iPos Then sAction = Left(sAction, iPos - 1) + + Select Case sAction + + Case "film" + Me.End + SaveImage + $bFilm = True + UpdateFilmMode + + Case "photo" + If $aPath.Count Then + $bFilm = False + UpdateFilmMode + Endif + + Case "previous" + Dec $iIndex + If $iIndex < 0 Then $iIndex = $aPath.Max + LoadImage + + Case "next" + Inc $iIndex + If $iIndex > $aPath.Max Then $iIndex = 0 + LoadImage + + Case "zoom-in" + SetZoom(2 ^ Int(Log2($fZoom)) * 2) + + Case "zoom-out" + SetZoom(2 ^ Int(Log2($fZoom) - 0.001)) + + Case "zoom-original" + SetZoom(1) + + Case "zoom-fit" + SetZoom(0) + + Case "quit" + SetMode("") + SaveImage + Me.Close + + Case "balance", "scissors", "resize" + 'If Not $hImage Then Return + SetMode(sAction) + + Case "usb" + SetDir(Dialog.Path) + + Case "undo" + + If Not $hImage Then + + ivwImage.MoveFirst + While ivwImage.Available + If ivwImage.Item.Selected Then + $iIndex = CInt(ivwImage.Item.Key) + GetImage + RemoveImage + Endif + ivwImage.MoveNext + Wend + + FillImageBrowser(True) + $hImage = Null + + Else + + SetMode("") + If $hUndoStack.Count Then + $hImage = $hUndoStack[$hUndoStack.Max] + $hUndoStack.Remove($hUndoStack.Max) + $bModify = $hUndoStack.Count > 0 + If Not $bModify Then RemoveImage + UpdateZoom + Else + LoadImage(True) + RemoveImage + Endif + + Endif + + Case "save" + + If Not $hImage Then + + $iIndex = CInt(ivwImage.Key) + $sPath = CACHE_DIR &/ File.Name($aPath[$iIndex]) + If Exist($sPath) Then + SaveImageDefinitely + FillImageBrowser(True) + Endif + $hImage = Null + + Else + + SetMode("") + SaveImageDefinitely + + Endif + + Case "save-all" + + ivwImage.MoveFirst + While ivwImage.Available + 'If ivwImage.Item.Selected Then + $iIndex = CInt(ivwImage.Item.Key) + GetImage + SaveImageDefinitely + 'Endif + ivwImage.MoveNext + Wend + + FillImageBrowser(True) + $hImage = Null + + Case Else + + If Not $hImage Then + + ivwImage.MoveFirst + While ivwImage.Available + If ivwImage.Item.Selected Then + $iIndex = CInt(ivwImage.Item.Key) + GetImage + Select Case sAction + Case "hflip" + $hImage.Mirror(True, False) + Case "vflip" + $hImage.Mirror(False, True) + Case "rotate-left" + $hImage = $hImage.Rotate(Pi(0.5)) + Case "rotate-right" + $hImage = $hImage.Rotate(Pi(-0.5)) + End Select + $bModify = True + SaveImage + Endif + ivwImage.MoveNext + Wend + + FillImageBrowser(True) + $hImage = Null + + Else + + SetMode("") + + Select Case sAction + Case "hflip" + PushUndo() + $hImage.Mirror(True, False) + Case "vflip" + PushUndo() + $hImage.Mirror(False, True) + Case "rotate-left" + PushUndo() + $hImage = $hImage.Rotate(Pi(0.5)) + Case "rotate-right" + PushUndo() + $hImage = $hImage.Rotate(Pi(-0.5)) + Case "oil" + PushUndo() + $hImage = ImageMagick("-paint " & CStr(Max(3, Min($hImage.W, $hImage.H) \ 256))) + Case "magic" + PushUndo() + $hImage = ImageMagick("-auto-gamma -auto-level") + Case "invert" + PushUndo() + $hImage.Invert() + 'Case "equalize" + ' $hImage = ImageMagick("-equalize") + Case "despeckle" + PushUndo() + $hImage = ImageMagick("-despeckle") + Case "normalize" + PushUndo() + $hImage = ImageMagick("-normalize") + Case "blur" + PushUndo() + $hImage = ImageMagick("-blur 8") '$hImage.OilPaint() + Case "sharpen" + PushUndo() + $hImage = ImageMagick("-sharpen 8") '$hImage.OilPaint() + + End Select + + $bModify = True + UpdateSaveIcon + UpdateZoom + + Endif + + End Select + + Dec Application.Busy + +End + +Public Sub svwImage_MouseDown() + + $MX = Mouse.X + svwImage.ScrollX + $MY = Mouse.Y + svwImage.ScrollY + 'Debug $MX;; $MY + +End + +Public Sub svwImage_MouseMove() + + If Mouse.Left Then + 'Debug Mouse.X - $MX;; Mouse.Y - $MY + svwImage.Scroll($MX - Mouse.X, $MY - Mouse.Y) + Endif + +End + +Private Sub PaintOutlineText(sText As String, X As Float, Y As Float, W As Float, H As Float) + + ' Dim I, J As Integer + ' + ' Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 128)) + ' For I = -1 To 1 + ' For J = -1 To 1 + ' Paint.DrawRichText(sText, X + I + 2, Y + J + 2, 4096, H - 4, Align.Left) + ' Next + ' Next + + Paint.Brush = Paint.Color(Color.Black) + Paint.DrawRichTextShadow(sText, X + 2, Y + 2, 4096, H - 4, Align.Left, 4) + Paint.Brush = Paint.Color(Color.White) + Paint.DrawRichText(sText, X + 2, Y + 2, 4096, H - 4, Align.Left) + +End + + +Public Sub dwgInfo_Draw() + + Dim sText As String + Dim sSpace As String + + If Not $hImage Then Return + + With Stat($sPath) + sSpace = String$(6, " ") + sText = "" & Html(File.Name($sPath)) & "" & sSpace & Html(Format(.LastModified, gb.LongDate)) + sText &= sSpace & $hImage.W & " x " & $hImage.H & " (" & Format($fZoom, "0%") & ")" + End With + + PaintOutlineText(sText, 0, 0, dwgInfo.W, dwgInfo.H) + +End + +Public Sub Form_KeyPress() + + Dim sShortcut As String + + Select Case Key.Code + + Case Key.Esc + If $sMode Then + SetMode("") + Else + Action("quit") + Endif + Stop Event + Return + + Case Key.BackSpace + Action("undo") + Stop Event + Return + + End Select + + If Key.Text Then + For Each sShortcut In $cShortcut + If String.UCase(Key.Text) = sShortcut Then + If $cButton[$cShortcut.Key].Parent.Visible Then + Action($cShortcut.Key) + Stop Event + Return + Endif + Endif + Next + Endif + +End + +Public Sub Begin() + + $hTemp = $hImage.Copy() + +End + +Public Sub End() + + $hTemp = Null + UpdateZoom + SetMode("") + 'SaveImage + Me.Refresh + +End + +Public Sub Apply() + + $hTemp = $hImage + {End} + +End + +Public Sub Cancel() + + If Not $hTemp Then Return + $hImage = $hTemp.Copy() + UpdateZoom + Me.Refresh + +End + +Public Sub GetCurrentImage() As Image + + Return $hImage + +End + +Public Sub Balance(iBrightness As Integer, iContrast As Integer, iGamma As Integer) + + $hImage = $hTemp.Copy() + $hImage.Balance((iBrightness - 50) / 50, (iContrast - 50) / 50, (iGamma - 50) / 50) + $bModify = True + UpdateZoom + Me.Refresh + +End + +Public Sub Cut() + + Dim X, Y, W, H As Integer + Dim hRect As Rect + + W = FScissors.W / $fZoom + H = FScissors.H / $fZoom + X = (svwImage.ScrollX + FScissors.X) / $fZoom + Y = (svwImage.ScrollY + FScissors.Y) / $fZoom + + If $hImage.W * $fZoom <= Me.W Then X -= (Me.W / $fZoom - $hImage.W) / 2 + If $hImage.H * $fZoom <= Me.H Then Y -= (Me.H / $fZoom - $hImage.H) / 2 + + hRect = Rect(X, Y, W, H) + hRect = hRect.Intersection(Rect(0, 0, $hImage.W, $hImage.H)) + + If Not hRect Then Return + + $hImage = $hImage.Copy(hRect.X, hRect.Y, hRect.W, hRect.H) + $bModify = True + $hTemp = $hImage + Me.End + SetZoom(0) + +End + +Public Sub Stretch(W As Integer, H As Integer) + + Inc Application.Busy + $hImage = $hImage.Stretch(W, H) + $bModify = True + $hTemp = $hImage + Me.End + SetZoom(0) + Dec Application.Busy + +End + + +Public Sub Form_Activate() + + If $sDir = "" Then SetDir(Settings["Directory", User.Home]) + +End + + +Private Sub SetDir(sDir As String) + + Inc Application.Busy + + 'Shell "cd " & Shell(CACHE_DIR) & "; rm -f *" Wait + + CACHE_DIR = CACHE_ROOT &/ Replace(sDir, "/", ":") + + $sDir = sDir + Settings["Directory"] = $sDir + $aPath = New String[] + Try $aPath = Dir(sDir, "*.{jpg,JPG,jpeg,JPEG,png,PNG,bmp,BMP,gig,GIF}").Sort() + $iIndex = 0 + $bReloadFilm = True + UpdateFilmMode + + Dec Application.Busy + +End + +Private Sub SaveImage() As String + + If Not $hImage Then Return + If Not $bModify Then Return + + Try Mkdir CACHE_DIR + Try Kill CACHE_DIR &/ ".thumb." & File.Name($sPath) + $bReloadFilm = True + $hImage.Save(CACHE_DIR &/ File.Name($sPath), 80) + $bModify = False + UpdateSaveIcon + Return CACHE_DIR &/ File.Name($sPath) + +Catch + + Message.Error(Error.Text) + +End + +Private Sub RemoveImage() + + Try Kill CACHE_DIR &/ File.Name($sPath) + Try Kill CACHE_DIR &/ ".thumb." & File.Name($sPath) + $bReloadFilm = True + If Dir(CACHE_DIR).Count = 0 Then Rmdir CACHE_DIR + UpdateSaveIcon + +Catch + + Message.Error(Error.Text) + +End + +Private Sub SaveImageDefinitely() + + Dim sPath As String + + SaveImage + If Exist(CACHE_DIR &/ File.Name($sPath)) Then + sPath = $sDir &/ File.Name($sPath) + Try Kill sPath & "~" + Move sPath To sPath & "~" + Copy CACHE_DIR &/ File.Name($sPath) To sPath + RemoveImage + Endif + +Catch + + Message.Error(Error.Text) + +End + + +Private Sub ImageMagick(sCommand As String) As Image + + Dim sPath, sPath2 As String + Dim hImage As Image + + Inc Application.Busy + + sPath = File.SetExt(Temp$("image"), File.Ext($sPath)) + sPath2 = File.SetExt(Temp$("image2"), File.Ext($sPath)) + + $hImage.Save(sPath, 100) + + Shell "convert " & Shell(sPath) & " " & sCommand & " " & Shell(sPath2) Wait + hImage = Image.Load(sPath2) + Kill sPath + Kill sPath2 + + Dec Application.Busy + UpdateSaveIcon + + Return hImage + +End + +Private Sub GetThumb(sPath As String) As Image + + Dim sName As String + Dim sThumb As String + Dim hImage As Image + Dim bModified As Boolean + + sName = File.Name(sPath) + + If Exist(CACHE_DIR &/ sName) Then + sPath = CACHE_DIR &/ sName + bModified = True + Endif + + sThumb = CACHE_DIR &/ ".thumb." & sName + If Exist(sThumb) Then + 'If Stat(sThumb).LastModified >= Stat(sPath).LastModified Then + hImage = Image.Load(sThumb) + 'Endif + Endif + + If Not hImage Then + hImage = Image.Load(sPath) + If hImage.W > 256 Or If hImage.H > 256 Then + If hImage.W > hImage.H Then + hImage = hImage.Stretch(256, 256 * hImage.H / hImage.W) + Else + hImage = hImage.Stretch(256 * hImage.W / hImage.H, 256) + Endif + Endif + Paint.Begin(hImage) + Paint.Rectangle(0, 0, hImage.W, hImage.H) + Paint.LineWidth = 0.5 + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke + If bModified Then + If Not $hSave Then $hSave = Image.Load("save.png").Stretch(24, 24) + Paint.DrawImage($hSave, Paint.W - 24 - 8, 8) + Endif + Paint.End + Try Kill sThumb + hImage.Save(sThumb) + Endif + + Return hImage + +End + +Private Sub GetSize(iSize As Long) As String + + Dim sSize As String + + If iSize < 1024 Then Return iSize & " b" + Return Format(iSize \ 1024, "#,##0") & " K" + +End + + +Private Sub FillImageBrowser(Optional bNoWait As Boolean) + + Dim I As Integer + Dim hImage As Image + Dim sText As String + Dim fLastTime As Float + Dim sKey As String + + If $bReloadFilm Then + + Inc Application.Busy + + fLastTime = Timer + + Try Mkdir CACHE_DIR + + sKey = ivwImage.Key + + ivwImage.Clear + ivwImage.GridSize = 280 \ Desktop.Scale + + For I = 0 To $aPath.Max + + Try hImage = GetThumb($sDir &/ $aPath[i]) + If Error Then + Error Error.Where; ": "; Error.Text + hImage = Picture["icon:/256/image"].Image + Endif + With Stat($sDir &/ $aPath[I]) + sText = "" & Html(File.Name($aPath[I])) & "
    " & Format(.LastModified, gb.LongDate) & "
    " & GetSize(.Size) + End With + ivwImage.Add(I, "", hImage.Picture).RichText = sText + If (Timer - fLastTime) > 1 Then + fLastTime = Timer + Wait + Endif + Next + + Try ivwImage.Key = sKey + + Dec Application.Busy + $bReloadFilm = False + + Endif + + If ivwImage.Count Then + ivwImage.Key = $iIndex + ivwImage[$iIndex].EnsureVisible + panBrowser.Show + '$cButton["photo"].Show + Else + '$cButton["photo"].Hide + panBrowser.Hide + lblError.Text = ("No image in directory") + lblError.Show + Endif + +End + +Public Sub ivwImage_KeyPress() + + If Key.Code = Key.Enter Or If Key.Code = Key.Return Then ivwImage_Activate + +End + + +Public Sub ivwImage_Activate() + + $bFilm = False + $iIndex = CInt(ivwImage.Key) + UpdateFilmMode + +End + +Public Sub Form_Close() + + CAnimation.Exit + +End + +Public Sub svwImage_KeyPress() + + Select Case Key.Code + + Case Key.Up + svwImage.Scroll(svwImage.ScrollX, svwImage.ScrollY - 64) + + Case Key.Down + svwImage.Scroll(svwImage.ScrollX, svwImage.ScrollY + 64) + + Case Key.Left + svwImage.Scroll(svwImage.ScrollX - 64, svwImage.ScrollY) + + Case Key.Right + svwImage.Scroll(svwImage.ScrollX + 64, svwImage.ScrollY) + + Case Key.PageUp + Action("previous") + + Case Key.PageDown + Action("next") + + Case Key.Enter, Key.Return + Action("film") + + End Select + +End + +Public Sub PushUndo() + + $hUndoStack.Add($hImage.Copy()) + +End + +Public Sub dwgPath_Draw() + + 'Debug Paint.Font.ToString() + PaintOutlineText($sDir, 0, 0, Paint.W, Paint.H) + +End diff --git a/app/examples/Image/PhotoTouch/.src/FMain.form b/app/examples/Image/PhotoTouch/.src/FMain.form new file mode 100644 index 00000000..cc67ddd8 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FMain.form @@ -0,0 +1,65 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,82,72) + Icon = Picture["icon.png"] + Border = False + Maximized = True + Arrangement = Arrange.Fill + { svwImage ScrollArea + MoveScaled(6,25,24,24) + Visible = False + Background = Color.Background + Mouse = Mouse.SizeAll + Border = False + ScrollBar = Scroll.None + } + { panToolbar HBox + MoveScaled(1,1,75,5) + Ignore = True + AutoResize = True + Spacing = True + Margin = True + } + { lblError Label + MoveScaled(15,52,46,9) + Visible = False + Font = Font["Bold,+5"] + Background = Color.Background + Foreground = Color.LightForeground + Alignment = Align.Center + } + { panBrowser Panel + MoveScaled(48,17,24,30) + Background = Color.Background + Arrangement = Arrange.Vertical + Margin = True + { panMargin Panel + MoveScaled(4,1,12,4) + } + { ivwImage IconView + MoveScaled(3,11,16,16) + Font = Font["+2"] + Background = Color.Background + Foreground = Color.LightForeground + Expand = True + Mode = Select.Multiple + IconLines = 3 + Border = False + } + } + { panToolbarBrowser HBox + MoveScaled(1,7,75,5) + Visible = False + Ignore = True + AutoResize = True + Spacing = True + Margin = True + } + { dwgInfo DrawingArea + MoveScaled(5,64,20,6) + Font = Font["+5"] + Foreground = Color.LightForeground + Ignore = True + } +} diff --git a/app/examples/Image/PhotoTouch/.src/FResize.class b/app/examples/Image/PhotoTouch/.src/FResize.class new file mode 100644 index 00000000..7ee42411 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FResize.class @@ -0,0 +1,57 @@ +' Gambas class file + +Private $aSize As Float[] = [1 / 4, 1 / 3, 1 / 2, 2 / 3, 3 / 4, 1] +Private btnApply As CButton + +Public Sub sldResize_Change() + + Dim W, H As Integer + + With FMain.GetCurrentImage() + W = .W * $aSize[sldResize.Value] + H = .H * $aSize[sldResize.Value] + End With + lblSize.Text = Format($aSize[sldResize.Value], "0%") & " - " & W & " x " & H + +End + +Public Sub _new() + + btnApply = New CButton(Me) As "btnApply" + btnApply.Move(Me.W - 48 - Desktop.Scale * 2, Desktop.Scale * 2, 48, 48) + btnApply.Image = Image.Load("ok.png") + +End + +Public Sub Form_Open() + + Me.Center + sldResize.SetFocus + sldResize_Change + +End + +Public Sub btnApply_Click() + + Dim W, H As Integer + + With FMain.GetCurrentImage() + W = .W * $aSize[sldResize.Value] + H = .H * $aSize[sldResize.Value] + End With + + FMain.Stretch(W, H) + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Enter Or If Key.Code = Key.Return Then + btnApply_Click + Stop Event + Return + Endif + + FMain.Form_KeyPress + +End diff --git a/app/examples/Image/PhotoTouch/.src/FResize.form b/app/examples/Image/PhotoTouch/.src/FResize.form new file mode 100644 index 00000000..931fdfa3 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FResize.form @@ -0,0 +1,40 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,55,24) + Background = &HFFFFFF& + Border = False + Utility = True + Stacking = Window.Above + SkipTaskbar = True + Opacity = 50 + { panBrightness Panel + MoveScaled(1,1,53,22) + Background = &H000000& + { PictureBox1 PictureBox + MoveScaled(2,11,8,8) + Picture = Picture["resize.png"] + Stretch = True + Alignment = Align.Center + } + { sldResize Slider + MoveScaled(13,11,36,3) + MaxValue = 5 + PageStep = 1 + Value = 5 + } + { lblTitle Label + MoveScaled(2,2,52,6) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + AutoResize = True + Text = ("Resize") + } + { lblSize Label + MoveScaled(14,14,34,5) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + AutoResize = True + } + } +} diff --git a/app/examples/Image/PhotoTouch/.src/FScissors.class b/app/examples/Image/PhotoTouch/.src/FScissors.class new file mode 100644 index 00000000..a2a11522 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FScissors.class @@ -0,0 +1,260 @@ +' Gambas class file + +Private $aScissors As Panel[] + +Private Enum S_N, S_S, S_W, S_E, S_NW, S_NE, S_SW, S_SE +Private HANDLE_SIZE As Integer + +Private $aMouse As Integer[] +Private $aOpacity As Integer[] + +Private $MX As Integer +Private $MY As Integer +Private $hRect As Rect +Private $bResize As Boolean + +Private btnCut As CButton + +Public Sub Form_Resize() + + $aScissors[S_NW].Move(0, 0, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_NE].Move(Me.W - HANDLE_SIZE, 0, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_SW].Move(0, Me.H - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_SE].Move(Me.W - HANDLE_SIZE, Me.H - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_N].Move(HANDLE_SIZE, 0, Me.W - HANDLE_SIZE * 2, HANDLE_SIZE) + $aScissors[S_S].Move(HANDLE_SIZE, Me.H - HANDLE_SIZE, Me.W - HANDLE_SIZE * 2, HANDLE_SIZE) + $aScissors[S_W].Move(0, HANDLE_SIZE, HANDLE_SIZE, Me.H - HANDLE_SIZE * 2) + $aScissors[S_E].Move(Me.W - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE, Me.H - HANDLE_SIZE * 2) + + btnCut.Move(Me.W - btnCut.W - Desktop.Scale * 3, Desktop.Scale * 3) + + dwgNW.Move(HANDLE_SIZE, HANDLE_SIZE) + dwgNE.Move(Me.W - HANDLE_SIZE - dwgNE.W, dwgNW.Y) + dwgSE.Move(dwgNE.X, Me.H - HANDLE_SIZE - dwgSE.H) + dwgSW.Move(dwgNW.X, dwgSE.Y) + +End + + +Public Sub Form_Open() + + Dim I As Integer + + $aMouse = [Mouse.SizeN, Mouse.SizeS, Mouse.SizeW, Mouse.SizeE, Mouse.SizeNW, Mouse.SizeNE, Mouse.SizeSW, Mouse.SizeSE] + HANDLE_SIZE = Desktop.Scale + + $aScissors = New Panel[8] + + For I = 0 To 7 + $aScissors[I] = New Panel(Me) As "Panel" + With $aScissors[I] + .Mouse = $aMouse[I] + .Background = Color.White 'IIf(I >= S_NW, Color.RGB(192, 192, 192), Color.White) + .Tag = I + End With + Next + + btnCut = New CButton(Me) As "btnCut" + btnCut.Resize(48, 48) + btnCut.Image = Image.Load("scissors.png") + + Me.Move(FMain.ClientW \ 4, FMain.ClientH \ 4, FMain.ClientW \ 2, FMain.ClientH \ 2) + 'Form_Resize + +End + +Public Sub Panel_MouseDown() + + $MX = Mouse.ScreenX + $MY = Mouse.ScreenY + $hRect = Rect(Me.X, Me.Y, Me.W, Me.H) + +End + +Public Sub Panel_MouseMove() + + Dim X, Y As Integer + Dim iTag As Integer = Last.Tag + Dim MIN_HEIGHT As Integer = btnCut.W + Desktop.Scale * 6 + Dim MIN_WIDTH As Integer = btnCut.H + Desktop.Scale * 6 + + Select Case Last.Tag + + Case S_N + Y = $hRect.Y + Mouse.ScreenY - $MY + Y = Min($hRect.Bottom - MIN_HEIGHT, Max(0, Y)) + Me.Move(Me.X, Y, Me.W, $hRect.H + $hRect.Y - Y) + + Case S_S + Y = $hRect.H + Mouse.ScreenY - $MY + Y = Min(FMain.H - $hRect.Y, Max(MIN_HEIGHT, Y)) + Me.Move(Me.X, Me.Y, Me.W, Y) + + Case S_W + X = $hRect.X + Mouse.ScreenX - $MX + X = Min($hRect.Right - MIN_WIDTH, Max(0, X)) + Me.Move(X, Me.Y, $hRect.W + $hRect.X - X, Me.H) + + Case S_E + X = $hRect.W + Mouse.ScreenX - $MX + X = Min(FMain.W - $hRect.X, Max(MIN_WIDTH, X)) + Me.Move(Me.X, Me.Y, X, Me.H) + + Case S_NW + Y = $hRect.Y + Mouse.ScreenY - $MY + Y = Min($hRect.Bottom - MIN_HEIGHT, Max(0, Y)) + X = $hRect.X + Mouse.ScreenX - $MX + X = Min($hRect.Right - MIN_WIDTH, Max(0, X)) + Me.Move(X, Y, $hRect.W + $hRect.X - X, $hRect.H + $hRect.Y - Y) + + Case S_NE + Y = $hRect.Y + Mouse.ScreenY - $MY + Y = Min($hRect.Bottom - MIN_HEIGHT, Max(0, Y)) + X = $hRect.W + Mouse.ScreenX - $MX + X = Min(FMain.W - $hRect.X, Max(MIN_WIDTH, X)) + Me.Move(Me.X, Y, X, $hRect.H + $hRect.Y - Y) + + Case S_SW + Y = $hRect.H + Mouse.ScreenY - $MY + Y = Min(FMain.H - $hRect.Y, Max(MIN_HEIGHT, Y)) + X = $hRect.X + Mouse.ScreenX - $MX + X = Min($hRect.Right - MIN_WIDTH, Max(0, X)) + Me.Move(X, Me.Y, $hRect.W + $hRect.X - X, Y) + + Case S_SE + Y = $hRect.H + Mouse.ScreenY - $MY + Y = Min(FMain.H - $hRect.Y, Max(MIN_HEIGHT, Y)) + X = $hRect.W + Mouse.ScreenX - $MX + X = Min(FMain.W - $hRect.X, Max(MIN_WIDTH, X)) + Me.Move(Me.X, Me.Y, X, Y) + + End Select + + 'Form_Resize + +End + + +Public Sub Form_MouseDown() + + $MX = Mouse.ScreenX + $MY = Mouse.ScreenY + $hRect = Rect(Me.X, Me.Y, Me.W, Me.H) + +End + +Public Sub Form_MouseMove() + + Dim X, Y As Integer + + X = Min(Max($hRect.X + Mouse.ScreenX - $MX, 0), FMain.W - $hRect.W) + Y = Min(Max($hRect.Y + Mouse.ScreenY - $MY, 0), FMain.H - $hRect.H) + Me.Move(X, Y) + +End + +Public Sub btnCut_Click() + + FMain.Cut + +End + +Public Sub dwgCorner_Draw() + + Dim hCtrl As DrawingArea = Last + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + + Select Case Last.Tag + + Case "NW" + + If Not $bResize Then Paint.Brush = Paint.Color(Color.White) + + Paint.MoveTo(2, 2) + Paint.LineTo(hCtrl.W - 2, 2) + Paint.LineTo(2, hCtrl.H - 2) + + Case "NE" + + Paint.MoveTo(hCtrl.W - 2, 2) + Paint.LineTo(hCtrl.W - 2, hCtrl.H - 2) + Paint.LineTo(2, 2) + + Case "SW" + + Paint.MoveTo(2, hCtrl.H - 2) + Paint.LineTo(2, 2) + Paint.LineTo(hCtrl.W - 2, hCtrl.H - 2) + + Case "SE" + + If $bResize Then Paint.Brush = Paint.Color(Color.White) + + Paint.MoveTo(hCtrl.W - 2, hCtrl.H - 2) + Paint.LineTo(2, hCtrl.H - 2) + Paint.LineTo(hCtrl.W - 2, 2) + + End Select + + Paint.Fill + +End + +Public Sub Form_KeyPress() + + Dim MIN_HEIGHT As Integer = btnCut.W + Desktop.Scale * 6 + Dim MIN_WIDTH As Integer = btnCut.H + Desktop.Scale * 6 + Dim X, Y, W, H As Integer + + X = Me.X + Y = Me.Y + W = Me.W + H = Me.H + + Select Case Key.Code + Case Key.Up + If $bResize Then + H = Max(MIN_HEIGHT, H - 16) + Else + Y = Max(0, Y - 16) + Endif + Case Key.Down + If $bResize Then + H = Min(FMain.H - Me.Y, H + 16) + Else + Y = Min(FMain.H - Me.H, Y + 16) + Endif + Case Key.Left + If $bResize Then + W = Max(MIN_WIDTH, W - 16) + Else + X = Max(0, X - 16) + Endif + Case Key.Right + If $bResize Then + W = Min(FMain.W - Me.X, W + 16) + Else + X = Min(FMain.W - Me.W, X + 16) + Endif + Case Key.Space + $bResize = Not $bResize + dwgNW.Refresh + dwgSE.Refresh + Stop Event + Return + Case Key.Return, Key.Enter + btnCut_Click + Stop Event + Return + End Select + + If X <> Me.X Or If Y <> Me.Y Or If W <> Me.W Or If H <> Me.H Then + Me.Move(X, Y, W, H) + Stop Event + Return + Endif + + FMain.Form_KeyPress + +End diff --git a/app/examples/Image/PhotoTouch/.src/FScissors.form b/app/examples/Image/PhotoTouch/.src/FScissors.form new file mode 100644 index 00000000..cfddd6ce --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FScissors.form @@ -0,0 +1,38 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,64) + Background = &H000000& + Mouse = Mouse.SizeAll + Border = False + Utility = True + Stacking = Window.Above + SkipTaskbar = True + Opacity = 50 + { Label1 Label + MoveScaled(3,3,52,6) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + Text = ("Cut") + } + { dwgNW DrawingArea dwgCorner + Name = "dwgNW" + MoveScaled(7,31,4,4) + Tag = "NW" + } + { dwgNE DrawingArea dwgCorner + Name = "dwgNE" + MoveScaled(14,31,4,4) + Tag = "NE" + } + { dwgSW DrawingArea dwgCorner + Name = "dwgSW" + MoveScaled(7,38,4,4) + Tag = "SW" + } + { dwgSE DrawingArea dwgCorner + Name = "dwgSE" + MoveScaled(14,38,4,4) + Tag = "SE" + } +} diff --git a/app/examples/Image/PhotoTouch/balance.png b/app/examples/Image/PhotoTouch/balance.png new file mode 100644 index 0000000000000000000000000000000000000000..c66bf62b495459636ab7806b76a3042558eaa662 GIT binary patch literal 1125 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/contrast.png b/app/examples/Image/PhotoTouch/contrast.png new file mode 100644 index 0000000000000000000000000000000000000000..49bda45dcbac7c6144da67e8853dfbbae71e1fa9 GIT binary patch literal 1340 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/film.png b/app/examples/Image/PhotoTouch/film.png new file mode 100644 index 0000000000000000000000000000000000000000..58ce250007f96276517d235a8080365108fdb568 GIT binary patch literal 756 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Explorer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:09+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FExplorer.form:35 +msgid "&About..." +msgstr "&Quant a..." + +#: FExplorer.form:14 +msgid "FExplorer" +msgstr "-" + +#: FExplorer.form:19 +msgid "&File" +msgstr "&Fitxer" + +#: .project:1 +msgid "File browser example" +msgstr "Exemple de navegador de fitxers" + +#: FExplorer.class:158 +msgid "' has been renamed to '" +msgstr "' s'ha canviat el nom a '" + +#: FExplorer.class:152 +msgid "IconView example written by\nBenoît Minisini" +msgstr "Exemple de visualitzador d'icones escrit per\nBenoît Minisini" + +#: FExplorer.form:39 +msgid "&Quit" +msgstr "&Surt" + +#: FExplorer.form:26 +msgid "&Refresh" +msgstr "&Actualitza" + +#: FExplorer.form:22 +msgid "&View hidden files" +msgstr "&Mostra els fitxers ocults" + diff --git a/app/examples/Misc/Explorer/.lang/cs.po b/app/examples/Misc/Explorer/.lang/cs.po new file mode 100644 index 00000000..1d68587c --- /dev/null +++ b/app/examples/Misc/Explorer/.lang/cs.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "File browser example" +msgstr "Příklad prohlížeče souboru" + +#: FExplorer.class:152 +msgid "" +"IconView example written by\n" +"Benoît Minisini" +msgstr "" +"Příklad IconView od\n" +"Benoît Minisini" + +#: FExplorer.class:158 +msgid "' has been renamed to '" +msgstr "' bylo přejmenováno na '" + +#: FExplorer.form:14 +msgid "FExplorer" +msgstr "-" + +#: FExplorer.form:19 +msgid "&File" +msgstr "&Soubor" + +#: FExplorer.form:22 +msgid "&View hidden files" +msgstr "&Zobrazit skryté soubory" + +#: FExplorer.form:26 +msgid "&Refresh" +msgstr "&Obnovit" + +#: FExplorer.form:35 +msgid "&About..." +msgstr "&O aplikaci..." + +#: FExplorer.form:39 +msgid "&Quit" +msgstr "&Ukončit" diff --git a/app/examples/Misc/Explorer/.lang/de.po b/app/examples/Misc/Explorer/.lang/de.po new file mode 100644 index 00000000..ed66f8d6 --- /dev/null +++ b/app/examples/Misc/Explorer/.lang/de.po @@ -0,0 +1,49 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "File browser example" +msgstr "Beispiel für einen Dateibrowser" + +#: FExplorer.class:152 +msgid "IconView example written by\nBenoît Minisini" +msgstr "IconView Beispiel, geschrieben von\nBenoît Minisini" + +#: FExplorer.class:158 +msgid "' has been renamed to '" +msgstr "' wurde umbenannt zu '" + +#: FExplorer.form:14 +msgid "FExplorer" +msgstr "-" + +#: FExplorer.form:19 +msgid "&File" +msgstr "&Datei" + +#: FExplorer.form:22 +msgid "&View hidden files" +msgstr "&Versteckte Dateien anzeigen" + +#: FExplorer.form:26 +msgid "&Refresh" +msgstr "&Aktualisieren" + +#: FExplorer.form:35 +msgid "&About..." +msgstr "&Über..." + +#: FExplorer.form:39 +msgid "&Quit" +msgstr "&Beenden" + diff --git a/app/examples/Misc/Explorer/.lang/es.po b/app/examples/Misc/Explorer/.lang/es.po new file mode 100644 index 00000000..4d569041 --- /dev/null +++ b/app/examples/Misc/Explorer/.lang/es.po @@ -0,0 +1,35 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FExplorer.class:190 +msgid "FExplorer" +msgstr "FExplorer" + +#: FExplorer.class:195 +msgid "&File" +msgstr "&Archivo" + +#: FExplorer.class:198 +msgid "&View hidden files" +msgstr "&Ver archivos ocultos" + +#: FExplorer.class:202 +msgid "&Refresh" +msgstr "&Refrescar" + +#: FExplorer.class:212 +msgid "&About..." +msgstr "&Acerca de..." + +#: FExplorer.class:216 +msgid "&Quit" +msgstr "&Salir" diff --git a/app/examples/Misc/Explorer/.lang/ru.po b/app/examples/Misc/Explorer/.lang/ru.po new file mode 100644 index 00000000..f4e80484 --- /dev/null +++ b/app/examples/Misc/Explorer/.lang/ru.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/Explorer/.project:21 +msgid "File browser example" +msgstr "Пример браузера файлов" + +#: app/examples/Misc/Explorer/.project:22 +msgid "IconView example" +msgstr "Пример IconView" + +#: app/examples/Misc/Explorer/.src/FExplorer.class:153 +msgid "" +"IconView example written by\n" +"Benoît Minisini" +msgstr "" +"Пример просмотра значков, написанный\n" +"Бенуа Минисини" + +#: app/examples/Misc/Explorer/.src/FExplorer.class:159 +msgid "' has been renamed to '" +msgstr "' был переименован в '" + +#: app/examples/Misc/Explorer/.src/FExplorer.form:5 +msgid "FExplorer" +msgstr "Обзорщик" + +#: app/examples/Misc/Explorer/.src/FExplorer.form:9 +msgid "File" +msgstr "Файл" + +#: app/examples/Misc/Explorer/.src/FExplorer.form:11 +msgid "View hidden files" +msgstr "Просмотреть скрытые файлы" + +#: app/examples/Misc/Explorer/.src/FExplorer.form:14 +msgid "Refresh" +msgstr "Освежить" + +#: app/examples/Misc/Explorer/.src/FExplorer.form:21 +msgid "About" +msgstr "О программе" + +#: app/examples/Misc/Explorer/.src/FExplorer.form:24 +msgid "Quit" +msgstr "Выход" + diff --git a/app/examples/Misc/Explorer/.project b/app/examples/Misc/Explorer/.project new file mode 100644 index 00000000..a90b74d3 --- /dev/null +++ b/app/examples/Misc/Explorer/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.0 +Title=File browser example +Description="IconView example" +Startup=FExplorer +Icon=folder.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Misc/Explorer/.src/FExplorer.class b/app/examples/Misc/Explorer/.src/FExplorer.class new file mode 100644 index 00000000..1ab8c315 --- /dev/null +++ b/app/examples/Misc/Explorer/.src/FExplorer.class @@ -0,0 +1,175 @@ +' Gambas class file + +Private $sPath As String +Private $bHidden As Boolean +Private $bCtrl As Boolean + +Static Public Sub Main() + + Dim hForm As Form + + hForm = New FExplorer(System.User.Home) + Application.MainWindow = hForm + hForm.Show + +End + +Public Sub _new(sPath As String) + + $sPath = sPath + RefreshExplorer + +End + +Private Sub RefreshExplorer() + + Dim sFile As String + Dim hPictDir As Picture + Dim hPictFile As Picture + Dim cDir As New String[] + Dim sName As String + + Inc Application.Busy + + Me.Title = Conv($sPath, System.Charset, Desktop.Charset) + + ivwExplorer.Clear + 'ivwExplorer.Arrangement = IconView.Row + + hPictDir = Picture["icon:/48/directory"] + hPictFile = Picture["icon:/48/file"] + + If $sPath <> "/" Then ivwExplorer.Add("D..", "..", hPictDir) + + For Each sFile In Dir($sPath) + + If Not $bHidden Then + If Stat($sPath &/ sFile).Hidden Then + Continue + Endif + Endif + + If IsDir($sPath &/ sFile) Then + cDir.Add("D" & sFile) + Else + cDir.Add("F" & sFile) + Endif + + Next + + cDir.Sort + + For Each sFile In cDir + + sName = Mid$(sFile, 2) + + If Left$(sFile) = "D" Then + ivwExplorer.Add(sFile, sName, hPictDir) + Else + ivwExplorer.Add(sFile, sName, hPictFile) + Endif + + 'ivwExplorer.Item.Editable = TRUE + + Next + + 'ivwExplorer.Sorted = FALSE + 'ivwExplorer.Ascending = TRUE + 'ivwExplorer.Sorted = TRUE + +Finally + + Dec Application.busy + +Catch + + Message.Error(Error.Text) + +End + + +Public Sub mnuQuit_Click() + + Me.Close + +End + +Public Sub mnuViewRefresh_Click() + + RefreshExplorer + +End + +Public Sub ivwExplorer_Activate() + + Dim sNewPath As String + Dim hForm As Form + + Debug Last.Current.Key + + If Last.Current.Key = "D.." Then + If $sPath = "/" Then Return + sNewPath = File.Dir($sPath) + Else + sNewPath = $sPath &/ Mid$(Last.Current.Key, 2) + Endif + + If IsDir(sNewPath) Then + + If $bCtrl Then + $bCtrl = False + hForm = New FExplorer(sNewPath) + hForm.Move(Me.X + 16, Me.Y + 16, Me.W, Me.H) + hForm.Show + Else + $sPath = sNewPath + RefreshExplorer + Endif + + Endif + +End + + +Private Sub ToggleViewHidden() + + $bHidden = Not mnuViewHidden.Checked + + mnuViewHidden.Checked = $bHidden + + RefreshExplorer + +End + + +Public Sub mnuViewHidden_Click() + + ToggleViewHidden + +End + +Public Sub mnuAbout_Click() + + Message(("IconView example written by\nBenoît Minisini")) + +End + +Public Sub ivwExplorer_Rename() + + Message("'" & Mid$(Last.Item.Key, 2) & ("' has been renamed to '") & Last.Item.Text & "'") + +End + +Public Sub ivwExplorer_KeyPress() + + If Key.Control Then $bCtrl = True + +End + +Public Sub ivwExplorer_KeyRelease() + + If Key.Control Then $bCtrl = False + +End + + diff --git a/app/examples/Misc/Explorer/.src/FExplorer.form b/app/examples/Misc/Explorer/.src/FExplorer.form new file mode 100644 index 00000000..8d50e1c4 --- /dev/null +++ b/app/examples/Misc/Explorer/.src/FExplorer.form @@ -0,0 +1,38 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(27.7143,27.7143,61.5714,46.7143) + Text = ("FExplorer") + Icon = Picture["icon:/16/directory"] + Arrangement = Arrange.Vertical + { mnuFile Menu + Text = Shortcut(("File"), "F") + { mnuViewHidden Menu + Text = Shortcut(("View hidden files"), "V") + } + { mnuViewRefresh Menu + Text = Shortcut(("Refresh"), "R") + Picture = Picture["icon:/16/refresh"] + Shortcut = "F5" + } + { Menu1 Menu + } + { mnuAbout Menu + Text = Shortcut(("About"), "A") & "..." + } + { mnuQuit Menu + Text = Shortcut(("Quit"), "Q") + Picture = Picture["icon:/16/quit"] + } + } + { Separator1 Separator + MoveScaled(27,4,19,0) + } + { ivwExplorer IconView + MoveScaled(2,9,44,30) + Expand = True + Mode = Select.Multiple + Editable = True + Border = False + } +} diff --git a/app/examples/Misc/Explorer/folder.png b/app/examples/Misc/Explorer/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..0e6b21c1ef6a6b34c0f1f598a1d6e1a2ef23a552 GIT binary patch literal 2450 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 iB|H1v|6G09>;D1E^Mnj!=ooqc0000, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Notepad\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:09+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FNotepad.form:122 +msgid "&?" +msgstr "-" + +#: FNotepad.form:125 +msgid "&About..." +msgstr "&Quant a..." + +#: FAbout.form:10 +msgid "About..." +msgstr "Quant a..." + +#: .project:1 +msgid "A little text editor" +msgstr "Un petit editor de text" + +#: FNotepad.class:118 +msgid "All files" +msgstr "Tots els fitxers" + +#: FNotepad.class:66 +msgid "Cancel" +msgstr "Canceŀla" + +#: FNotepad.class:118 +msgid "C/C++ files" +msgstr "Fitxers C/C++" + +#: FNotepad.form:111 +msgid "Choose &Font..." +msgstr "&Tria el tipus de lletra..." + +#: FNotepad.form:42 +msgid "Close" +msgstr "&Tanca" + +#: FNotepad.form:75 +msgid "&Copy" +msgstr "&Copia" + +#: FNotepad.form:81 +msgid "C&ut" +msgstr "&Retalla" + +#: FNotepad.class:118 +msgid "Desktop files" +msgstr "Fitxers d'escriptori" + +#: FNotepad.form:72 +msgid "&Edit" +msgstr "&Edita" + +#: FNotepad.form:33 +msgid "&File" +msgstr "&Fitxer" + +#: FNotepad.form:28 +msgid "Little notepad" +msgstr "Petit bloc de notes" + +#: FNotepad.class:30 +msgid "(New document)" +msgstr "(Document nou)" + +#: FNotepad.class:66 +msgid "\n\nFile has been modified. Do you want to save it ?" +msgstr "\n\nS'ha modificat el fitxer. El voleu desar?" + +#: FNotepad.class:66 +msgid "No" +msgstr "-" + +#: FNotepad.class:97 +msgid "\nUnable to load file.\n" +msgstr "\nNo es pot carregar el fitxer.\n" + +#: FAbout.form:21 +msgid "OK" +msgstr "D'acord" + +#: FNotepad.form:36 +msgid "&Open..." +msgstr "&Obre..." + +#: FNotepad.form:87 +msgid "&Paste" +msgstr "&Enganxa" + +#: FNotepad.form:65 +msgid "&Quit" +msgstr "&Surt" + +#: FNotepad.form:102 +msgid "&Redo" +msgstr "Re&fés" + +#: FNotepad.form:51 +msgid "&Save" +msgstr "&Desa" + +#: FNotepad.form:57 +msgid "S&ave As..." +msgstr "D&esa com..." + +#: FNotepad.class:118 +msgid "Text files" +msgstr "Fitxers de text" + +#: FAbout.form:28 +msgid "This is a little notepad sample program." +msgstr "Aquest és un petit programa exemple de bloc de notes." + +#: FNotepad.form:133 +msgid "txtNotepad" +msgstr "-" + +#: FNotepad.form:96 +msgid "&Undo" +msgstr "&Desfés" + +#: FNotepad.form:115 +msgid "&Wrap text" +msgstr "&Ajusta el text" + +#: FNotepad.class:66 +msgid "Yes" +msgstr "Sí" + diff --git a/app/examples/Misc/Notepad/.lang/cs.po b/app/examples/Misc/Notepad/.lang/cs.po new file mode 100644 index 00000000..2cb92fb1 --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/cs.po @@ -0,0 +1,150 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "A little text editor" +msgstr "Malý textový editor" + +#: FAbout.form:10 +msgid "About..." +msgstr "O aplikaci..." + +#: FAbout.form:21 +msgid "OK" +msgstr "-" + +#: FAbout.form:28 +msgid "This is a little notepad sample program." +msgstr "Toto je ukázka malého poznámkového bloku." + +#: FNotepad.class:30 +msgid "(New document)" +msgstr "(Nový dokument)" + +#: FNotepad.class:66 +msgid "" +"\n" +"\n" +"File has been modified. Do you want to save it ?" +msgstr "" +"\n" +"\n" +"Soubor byl změněn. Chcete jej uložit ?" + +#: FNotepad.class:66 +msgid "Cancel" +msgstr "Zrušit" + +#: FNotepad.class:66 +msgid "No" +msgstr "Ne" + +#: FNotepad.class:66 +msgid "Yes" +msgstr "Ano" + +#: FNotepad.class:97 +msgid "" +"\n" +"Unable to load file.\n" +msgstr "" +"\n" +"Nelze načíst soubor.\n" + +#: FNotepad.class:118 +msgid "All files" +msgstr "Všechny soubory" + +#: FNotepad.class:118 +msgid "C/C++ files" +msgstr "C/C++ soubory" + +#: FNotepad.class:118 +msgid "Desktop files" +msgstr "Desktop soubory" + +#: FNotepad.class:118 +msgid "Text files" +msgstr "Textové soubory" + +#: FNotepad.form:28 +msgid "Little notepad" +msgstr "Malý poznámkový blok" + +#: FNotepad.form:33 +msgid "&File" +msgstr "&Soubor" + +#: FNotepad.form:36 +msgid "&Open..." +msgstr "&Otevřít..." + +#: FNotepad.form:42 +msgid "Close" +msgstr "Zavřít" + +#: FNotepad.form:51 +msgid "&Save" +msgstr "&Uložit" + +#: FNotepad.form:57 +msgid "S&ave As..." +msgstr "Uložit &jako..." + +#: FNotepad.form:65 +msgid "&Quit" +msgstr "&Zavřít" + +#: FNotepad.form:72 +msgid "&Edit" +msgstr "&Upravit" + +#: FNotepad.form:75 +msgid "&Copy" +msgstr "&Kopírovat" + +#: FNotepad.form:81 +msgid "C&ut" +msgstr "&Vyjmout" + +#: FNotepad.form:87 +msgid "&Paste" +msgstr "&Vložit" + +#: FNotepad.form:96 +msgid "&Undo" +msgstr "&Zpět" + +#: FNotepad.form:102 +msgid "&Redo" +msgstr "&Vrátit" + +#: FNotepad.form:111 +msgid "Choose &Font..." +msgstr "Vyber &Font..." + +#: FNotepad.form:115 +msgid "&Wrap text" +msgstr "&Zalamovat text" + +#: FNotepad.form:122 +msgid "&?" +msgstr "-" + +#: FNotepad.form:125 +msgid "&About..." +msgstr "&O aplikaci..." + +#: FNotepad.form:133 +msgid "txtNotepad" +msgstr "-" diff --git a/app/examples/Misc/Notepad/.lang/de.po b/app/examples/Misc/Notepad/.lang/de.po new file mode 100644 index 00000000..201f2c8c --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/de.po @@ -0,0 +1,141 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "A little text editor" +msgstr "Ein kleiner Texteditor" + +#: FAbout.form:10 +msgid "About..." +msgstr "Über..." + +#: FAbout.form:21 +msgid "OK" +msgstr "-" + +#: FAbout.form:28 +msgid "This is a little notepad sample program." +msgstr "Dies ist ein kleines Notepad-Beispiel." + +#: FNotepad.class:30 +msgid "(New document)" +msgstr "(Unbenannt)" + +#: FNotepad.class:66 +msgid "\n\nFile has been modified. Do you want to save it ?" +msgstr "\n\nDatei wurde geändert. Wollen Sie sie speichern ?" + +#: FNotepad.class:66 +msgid "Cancel" +msgstr "Abbrechen" + +#: FNotepad.class:66 +msgid "No" +msgstr "Nein" + +#: FNotepad.class:66 +msgid "Yes" +msgstr "Ja" + +#: FNotepad.class:97 +msgid "\nUnable to load file.\n" +msgstr "\nDatei kann nicht geladen werden.\n" + +#: FNotepad.class:118 +msgid "All files" +msgstr "Alle Dateien" + +#: FNotepad.class:118 +msgid "C/C++ files" +msgstr "C/C++ Dateien" + +#: FNotepad.class:118 +msgid "Desktop files" +msgstr "Desktop Dateien" + +#: FNotepad.class:118 +msgid "Text files" +msgstr "Textdateien" + +#: FNotepad.form:28 +msgid "Little notepad" +msgstr "Kleines Notepad" + +#: FNotepad.form:33 +msgid "&File" +msgstr "&Datei" + +#: FNotepad.form:36 +msgid "&Open..." +msgstr "&Öffnen..." + +#: FNotepad.form:42 +msgid "Close" +msgstr "Schließen" + +#: FNotepad.form:51 +msgid "&Save" +msgstr "&Speichern" + +#: FNotepad.form:57 +msgid "S&ave As..." +msgstr "Speichern &als..." + +#: FNotepad.form:65 +msgid "&Quit" +msgstr "&Beenden" + +#: FNotepad.form:72 +msgid "&Edit" +msgstr "&Bearbeiten" + +#: FNotepad.form:75 +msgid "&Copy" +msgstr "&Kopieren" + +#: FNotepad.form:81 +msgid "C&ut" +msgstr "&Ausschneiden" + +#: FNotepad.form:87 +msgid "&Paste" +msgstr "&Einfügen" + +#: FNotepad.form:96 +msgid "&Undo" +msgstr "&Rückgängig" + +#: FNotepad.form:102 +msgid "&Redo" +msgstr "&Wiederherstellen" + +#: FNotepad.form:111 +msgid "Choose &Font..." +msgstr "&Schriftart wählen..." + +#: FNotepad.form:115 +msgid "&Wrap text" +msgstr "&Zeilenumbruch" + +#: FNotepad.form:122 +msgid "&?" +msgstr "-" + +#: FNotepad.form:125 +msgid "&About..." +msgstr "&Über..." + +#: FNotepad.form:133 +msgid "txtNotepad" +msgstr "-" + diff --git a/app/examples/Misc/Notepad/.lang/es.po b/app/examples/Misc/Notepad/.lang/es.po new file mode 100644 index 00000000..f7343279 --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/es.po @@ -0,0 +1,95 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FAbout.class:39 +msgid "About..." +msgstr "Acerca de..." + +#: FAbout.class:50 +msgid "OK" +msgstr "OK" + +#: FAbout.class:57 +msgid "This is a little notepad sample program." +msgstr "Este es un pequeño ejemplo del programa bloc de notas." + +#: FNotepad.class:256 +msgid "Little notepad" +msgstr "Pequeño bloc de notas" + +#: FNotepad.class:262 +msgid "&File" +msgstr "&Archivo" + +#: FNotepad.class:265 +msgid "&Open..." +msgstr "&Abrir" + +#: FNotepad.class:271 +msgid "Close" +msgstr "Cerrar" + +#: FNotepad.class:280 +msgid "&Save" +msgstr "&Guardar" + +#: FNotepad.class:285 +msgid "S&ave As..." +msgstr "G&uardar como..." + +#: FNotepad.class:294 +msgid "&Quit" +msgstr "&Salir" + +#: FNotepad.class:300 +msgid "&Edit" +msgstr "&Editar" + +#: FNotepad.class:303 +msgid "&Copy" +msgstr "&Copiar" + +#: FNotepad.class:308 +msgid "C&ut" +msgstr "C&ortar" + +#: FNotepad.class:313 +msgid "&Paste" +msgstr "&Pegar" + +#: FNotepad.class:322 +msgid "&Undo" +msgstr "&Deshacer" + +#: FNotepad.class:327 +msgid "&Redo" +msgstr "&Rehacer" + +#: FNotepad.class:336 +msgid "Choose &Font..." +msgstr "Seleccionar &fuente..." + +#: FNotepad.class:340 +msgid "&Wrap text" +msgstr "&Ajustar texto" + +#: FNotepad.class:346 +msgid "&?" +msgstr "&?" + +#: FNotepad.class:349 +msgid "&About..." +msgstr "&Acerca de..." + +#: FNotepad.class:357 +msgid "txtNotepad" +msgstr "txtNotepad" diff --git a/app/examples/Misc/Notepad/.lang/ru.po b/app/examples/Misc/Notepad/.lang/ru.po new file mode 100644 index 00000000..8120f4d5 --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/ru.po @@ -0,0 +1,164 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/Notepad/.project:21 +msgid "A little text editor" +msgstr "Небольшой текстовый редактор" + +#: app/examples/Misc/Notepad/.project:22 app/examples/Misc/Notepad/.src/FAbout.form:20 +msgid "This is a little notepad sample program." +msgstr "Это небольшой пример программы-блокнота." + +#: app/examples/Misc/Notepad/.src/FAbout.form:5 +msgid "About..." +msgstr "О программе..." + +#: app/examples/Misc/Notepad/.src/FAbout.form:14 +msgid "OK" +msgstr "ОК" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:27 +msgid "(New document)" +msgstr "(Новый документ)" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:63 +msgid "" +"\n" +"\n" +"File has been modified. Do you want to save it ?" +msgstr "" +"\n" +"\n" +"Файл был изменён. Вы хотите сохранить его?" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:63 +msgid "Yes" +msgstr "Да" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:63 +msgid "No" +msgstr "Нет" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:63 +msgid "Cancel" +msgstr "Отмена" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:94 +msgid "" +"\n" +"Unable to load file.\n" +msgstr "" +"\n" +"Невозможно загрузить файл.\n" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:115 +msgid "All files" +msgstr "Все файлы" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:115 +msgid "C/C++ files" +msgstr "Файлы C/C++" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:115 +msgid "Text files" +msgstr "Текстовые файлы" + +#: app/examples/Misc/Notepad/.src/FNotepad.class:115 +msgid "Desktop files" +msgstr "Файлы Рабочего стола" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:5 +msgid "Little notepad" +msgstr "Маленький блокнот" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:9 +msgid "File" +msgstr "Файл" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:11 +msgid "Open" +msgstr "Открыть" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:16 +msgid "Close" +msgstr "Закрыть" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:23 +msgid "Save" +msgstr "Сохранить" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:28 +msgid "Save As" +msgstr "Сохранить как" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:34 +msgid "Quit" +msgstr "Выход" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:40 +msgid "Edit" +msgstr "Редактировать" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:42 +msgid "Copy" +msgstr "Копировать" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:47 +msgid "Cut" +msgstr "Вырезать" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:52 +msgid "Paste" +msgstr "Вставить" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:59 +msgid "Undo" +msgstr "Откатить" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:64 +msgid "Redo" +msgstr "Вернуть" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:71 +msgid "Choose Font" +msgstr "Выбор шрифта" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:74 +msgid "Wrap text" +msgstr "Переносить текст" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:80 +msgid "?" +msgstr "?" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:82 +msgid "About" +msgstr "О программе" + +#: app/examples/Misc/Notepad/.src/FNotepad.form:89 +msgid "txtNotepad" +msgstr "Текстовый блокнот" + diff --git a/app/examples/Misc/Notepad/.project b/app/examples/Misc/Notepad/.project new file mode 100644 index 00000000..43fb11d2 --- /dev/null +++ b/app/examples/Misc/Notepad/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=A little text editor +Description="This is a little notepad sample program." +Startup=FNotepad +Icon=notepad.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Misc/Notepad/.src/FAbout.class b/app/examples/Misc/Notepad/.src/FAbout.class new file mode 100644 index 00000000..4eb7eefb --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FAbout.class @@ -0,0 +1,25 @@ +' Gambas class file + + +Static Public Sub Run() + + Dim hForm As Form + + hForm = New FAbout + hForm.ShowModal + +End + + +Public Sub _new() + + Me.Center + +End + + +Public Sub btnOK_Click() + + Me.Close + +End diff --git a/app/examples/Misc/Notepad/.src/FAbout.form b/app/examples/Misc/Notepad/.src/FAbout.form new file mode 100644 index 00000000..ccca69c6 --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FAbout.form @@ -0,0 +1,22 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,29,17) + Text = ("About...") + Resizable = False + { Image1 PictureBox + MoveScaled(1,1,4,4) + Picture = Picture["notepad.png"] + Stretch = True + } + { btnOK Button + MoveScaled(7,12,15,4) + Text = ("OK") + Default = True + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(7,1,21,9) + Text = ("This is a little notepad sample program.") + } +} diff --git a/app/examples/Misc/Notepad/.src/FNotepad.class b/app/examples/Misc/Notepad/.src/FNotepad.class new file mode 100644 index 00000000..26000b5f --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FNotepad.class @@ -0,0 +1,222 @@ +' Gambas class file + +Private $sPath As String +Private $bModify As Boolean + +Static Public Sub Main() + + FNotepad.Show + +End + + +Public Sub _new() + + txtNotepad.Text = "" + $bModify = False + RefreshTitle + txtNotePad.SetFocus + +End + + +Private Function GetName() As String + + If $sPath Then Return $sPath + + Return ("(New document)") + +End + + +Private Sub RefreshTitle() + + Dim sTitle As String + + If $bModify Then sTitle = "*" + sTitle = sTitle & GetName() + + Me.Title = sTitle + +End + + +Private Sub SetPath(sPath As String) + + $sPath = sPath + RefreshTitle + +End + +Private Sub SetModify(bModify As Boolean) + + If $bModify = bModify Then Return + $bModify = bModify + RefreshTitle + +End + + +Private Function CloseDoc() As Boolean + + If $bModify Then + Select Case Message.Question(GetName() & ("\n\nFile has been modified. Do you want to save it ?"), ("Yes"), ("No"), ("Cancel")) + Case 1 + Save + Case 3 + Return True + End Select + Endif + + $sPath = "" + txtNotepad.Text = "" + $bModify = False + RefreshTitle + +End + + + +Public Sub LoadFile(sPath As String) + + Dim sData As String + + If CloseDoc() Then Return + + sData = File.Load(sPath) + Try txtNotepad.Text = Conv(sData, System.Charset, Desktop.Charset) + If Error Then txtNotepad.Text = Conv(sData, "ISO_8859-1", Desktop.Charset) + $bModify = False + SetPath(sPath) + +Catch + + Message.Error(sPath & ("\nUnable to load file.\n") & Error.Text) + +End + + +Public Sub Save(Optional bSaveAs As Boolean) + + If bSaveAs Or Not $sPath Then + If Dialog.SaveFile() Then Return + SetPath(Dialog.Path) + Endif + + File.Save($sPath, txtNotepad.Text) + +End + + +Public Sub mnuOpen_Click() + + Dim sPath As String + + Dialog.Filter = ["*", ("All files"), "*.{c;cpp;h}", ("C/C++ files"), "*.txt", ("Text files"), "*.desktop", ("Desktop files")] + + If Dialog.OpenFile() Then Return + LoadFile(Dialog.Path) + +End + + +Public Sub mnuSave_Click() + + Save + +End + + +Public Sub mnuSaveAs_Click() + + Save(True) + +End + + +Public Sub mnuQuit_Click() + + Me.Close + +End + + +Public Sub txtNotepad_Change() + + SetModify(True) + +End + + +Public Sub mnuClose_Click() + + CloseDoc + +End + + +Public Sub Form_Close() + + If CloseDoc() Then Stop Event + +End + + +Public Sub mnuAbout_Click() + + 'Inc Application.Busy + FAbout.Run + 'Dec Application.Busy + +End + + +Public Sub mnuCopy_Click() + + txtNotepad.Copy + +End + + +Public Sub mnuPaste_Click() + + txtNotepad.Paste + +End + + +Public Sub mnuCut_Click() + + txtNotepad.Cut + +End + + +Public Sub mnuUndo_Click() + + txtNotepad.Undo + +End + + +Public Sub mnuRedo_Click() + + txtNotepad.Redo + +End + + +Public Sub mnuFont_Click() + + Dialog.Font = txtNotepad.Font + If Dialog.SelectFont() Then Return + txtNotepad.Font = Dialog.Font + +End + +Public Sub mnuWrap_Click() + + mnuWrap.Checked = Not mnuWrap.Checked + txtNotePad.Wrap = mnuWrap.Checked + +End diff --git a/app/examples/Misc/Notepad/.src/FNotepad.form b/app/examples/Misc/Notepad/.src/FNotepad.form new file mode 100644 index 00000000..ac8112d0 --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FNotepad.form @@ -0,0 +1,93 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(12.5714,12.5714,50,50) + Text = ("Little notepad") + Icon = Picture["notepad.png"] + Arrangement = Arrange.Fill + { mnuFile Menu + Text = Shortcut(("File"), "F") + { mnuOpen Menu + Text = Shortcut(("Open"), "O") & "..." + Picture = Picture["icon:/16/new"] + Shortcut = "Ctrl+O" + } + { mnuClose Menu + Text = ("Close") + Picture = Picture["icon:/16/close"] + Shortcut = "Ctrl+W" + } + { Menu1 Menu + } + { mnuSave Menu + Text = Shortcut(("Save"), "S") + Picture = Picture["icon:/16/save"] + Shortcut = "Ctrl+S" + } + { mnuSaveAs Menu + Text = Shortcut(("Save As"), "a") & "..." + Shortcut = "Ctrl+Shift+S" + } + { Menu2 Menu + } + { mnuQuit Menu + Text = Shortcut(("Quit"), "Q") + Picture = Picture["icon:/16/quit"] + Shortcut = "Ctrl+Q" + } + } + { mnuEdit Menu + Text = Shortcut(("Edit"), "E") + { mnuCopy Menu + Text = Shortcut(("Copy"), "C") + Picture = Picture["icon:/16/copy"] + Shortcut = "Ctrl+C" + } + { mnuCut Menu + Text = Shortcut(("Cut"), "u") + Picture = Picture["icon:/16/cut"] + Shortcut = "Ctrl+X" + } + { mnuPaste Menu + Text = Shortcut(("Paste"), "P") + Picture = Picture["icon:/16/paste"] + Shortcut = "Ctrl+V" + } + { Menu3 Menu + } + { mnuUndo Menu + Text = Shortcut(("Undo"), "U") + Picture = Picture["icon:/16/undo"] + Shortcut = "Ctrl+Z" + } + { mnuRedo Menu + Text = Shortcut(("Redo"), "R") + Picture = Picture["icon:/16/redo"] + Shortcut = "Ctrl+Y" + } + { Menu4 Menu + } + { mnuFont Menu + Text = Shortcut(("Choose Font"), "F") & "..." + } + { mnuWrap Menu + Text = Shortcut(("Wrap text"), "W") + Picture = Picture["icon:/16/text-fill"] + Checked = True + } + } + { mnuHelp Menu + Text = Shortcut(("?"), "?") + { mnuAbout Menu + Text = Shortcut(("About"), "A") & "..." + } + } + { txtNotepad TextArea + MoveScaled(1,1,35,26) + Font = Font["Liberation Mono,9"] + Expand = True + Text = ("txtNotepad") + Wrap = True + Border = False + } +} diff --git a/app/examples/Misc/Notepad/notepad.png b/app/examples/Misc/Notepad/notepad.png new file mode 100644 index 0000000000000000000000000000000000000000..3be5a715d315f1ae71f5aa52871fef3547f1e4de GIT binary patch literal 4015 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 literal 0 HcmV?d00001 diff --git a/app/examples/Misc/PDFViewer/.directory b/app/examples/Misc/PDFViewer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Misc/PDFViewer/.icon.png b/app/examples/Misc/PDFViewer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ea9495756338ebad0b64dc3c63afb034bfde3afd GIT binary patch literal 4253 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: PDFViewer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:38+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "PdfViewer" +msgstr "VisualitzadorPdf" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Un visualitzador simple de PDF, com a exemple de les capacitats de gb.pdf" + +#: FMain.class:40 +msgid "Pdf documents" +msgstr "Documents pdf" + +#: FMain.class:114 +msgid "From" +msgstr "Des de" + +#: FMain.class:253 +msgid "Simple PDF document viewer" +msgstr "Visualitzador simple de documents PDF" + +#: FMain.class:261 +msgid "Open file..." +msgstr "Obre fitxer..." + +#: FMain.class:268 +msgid "About..." +msgstr "Quant a..." + +#: FMain.class:276 +msgid "Rotate" +msgstr "Rota" + +#: FMain.class:284 +msgid "Zoom out" +msgstr "Allunya" + +#: FMain.class:292 +msgid "Zoom in" +msgstr "Apropa" + +#: FMain.class:300 +msgid "Find text" +msgstr "Cerca el següent" + +#: FMain.class:319 +msgid "Previous page" +msgstr "Pàgina anterior" + +#: FMain.class:327 +msgid "Next page" +msgstr "Pàgina següent" + +#: Fabout.class:61 +msgid "OK" +msgstr "D'acord" + +#: Fabout.class:68 +msgid "Simple PDF document viewer.

    Gambas example by Daniel Campos Fernández " +msgstr "Visualitzador simple de documents PDF.

    Exemple del Gambas per Daniel Campos Fernández " + diff --git a/app/examples/Misc/PDFViewer/.lang/cs.po b/app/examples/Misc/PDFViewer/.lang/cs.po new file mode 100644 index 00000000..6e4fb0fb --- /dev/null +++ b/app/examples/Misc/PDFViewer/.lang/cs.po @@ -0,0 +1,80 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "PdfViewer" +msgstr "-" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Ukázka PDF prohlížeče, příklad užití schopnosti gb.pdf" + +#: FMain.class:52 +msgid "Pdf documents" +msgstr "PDF dokument" + +#: FMain.form:30 +msgid "Simple PDF document viewer" +msgstr "Ukázka prohížeče PDF dokumentů" + +#: FMain.form:38 +msgid "Open file..." +msgstr "Otevřít soubor..." + +#: FMain.form:44 +msgid "About..." +msgstr "O aplikaci..." + +#: FMain.form:51 +msgid "Rotate" +msgstr "Rotace" + +#: FMain.form:58 +msgid "Zoom out" +msgstr "Oddálit" + +#: FMain.form:65 +msgid "Zoom in" +msgstr "Přiblížit" + +#: FMain.form:76 +msgid "Find text" +msgstr "Najdi text" + +#: FMain.form:90 +msgid "Previous page" +msgstr "Předchozí stránka" + +#: FMain.form:99 +msgid "Next Found" +msgstr "Další nalezeno" + +#: FMain.form:119 +msgid "Page" +msgstr "Stránka" + +#: FMain.form:130 +msgid "Go to this page" +msgstr "Jdi na tuto stránku" + +#: FMain.form:143 +msgid "Next page" +msgstr "Další stránka" + +#: Fabout.form:20 +msgid "

    Simple PDF document viewer

    Gambas example by Daniel Campos Fernández and Bernd Brinkmann" +msgstr "

    Jednotuchý prohlížeč PDF dokumentů

    Gambas příklad od Daniel Campos Fernández a Bernd Brinkmann" + +#: Fabout.form:34 +msgid "OK" +msgstr "-" diff --git a/app/examples/Misc/PDFViewer/.lang/de.po b/app/examples/Misc/PDFViewer/.lang/de.po new file mode 100644 index 00000000..cc0d9373 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.lang/de.po @@ -0,0 +1,80 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "PdfViewer" +msgstr "PDF-Betrachter" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Ein einfacher PDF-Betrachter, Beispiel für die Fähigkeiten von gb.pdf" + +#: FMain.class:52 +msgid "Pdf documents" +msgstr "pdf-Dateien" + +#: FMain.form:30 +msgid "Simple PDF document viewer" +msgstr "Einfacher Betrachter für PDF-Dokumente" + +#: FMain.form:38 +msgid "Open file..." +msgstr "Datei öffnen..." + +#: FMain.form:44 +msgid "About..." +msgstr "&Über..." + +#: FMain.form:51 +msgid "Rotate" +msgstr "Drehen" + +#: FMain.form:58 +msgid "Zoom out" +msgstr "Verkleinern" + +#: FMain.form:65 +msgid "Zoom in" +msgstr "Vergrößern" + +#: FMain.form:76 +msgid "Find text" +msgstr "Text finden" + +#: FMain.form:90 +msgid "Previous page" +msgstr "Vorherige Seite" + +#: FMain.form:99 +msgid "Next Found" +msgstr "Nächste Fundstelle" + +#: FMain.form:119 +msgid "Page" +msgstr "Seite" + +#: FMain.form:130 +msgid "Go to this page" +msgstr "Gehe zu Seite" + +#: FMain.form:143 +msgid "Next page" +msgstr "Nächste Seite" + +#: Fabout.form:20 +msgid "

    Simple PDF document viewer

    Gambas example by Daniel Campos Fernández and Bernd Brinkmann" +msgstr "

    Einfacher PDF-Betrachter

    Gambas-Beispiel von Daniel Campos Fernández und Bernd Brinkmann" + +#: Fabout.form:34 +msgid "OK" +msgstr "-" diff --git a/app/examples/Misc/PDFViewer/.lang/es.po b/app/examples/Misc/PDFViewer/.lang/es.po new file mode 100644 index 00000000..73d95061 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.lang/es.po @@ -0,0 +1,64 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Un simple Visor PDF, usado como ejemplo de las capacidades de gb.pdf" + +#: FMain.class:276 +msgid "Find text" +msgstr "Buscar texto" + +#: FMain.class:110 +msgid "From" +msgstr "de" + +#: FMain.class:321 +msgid "Next page" +msgstr "Página siguiente" + +#: Fabout.class:61 +msgid "OK" +msgstr "Aceptar" + +#: FMain.class:255 +msgid "Open file..." +msgstr "Abrir archivo..." + +#: .project:1 +msgid "PdfViewer" +msgstr "Visor Pdf" + +#: FMain.class:313 +msgid "Previous page" +msgstr "Página anterior" + +#: FMain.class:283 +msgid "Rotate" +msgstr "Rotar" + +#: FMain.class:245 +msgid "Simple PDF document viewer" +msgstr "Visor simple de documentos PDF" + +#: Fabout.class:68 +msgid "Simple PDF document viewer.

    Gambas example by Daniel Campos Fernández " +msgstr "Visor simple de documentos PDF.

    Ejemplo para Gambas por Daniel Campos Fernández." + +#: FMain.class:299 +msgid "Zoom in" +msgstr "Aumentar" + +#: FMain.class:291 +msgid "Zoom out" +msgstr "Disminuir" + diff --git a/app/examples/Misc/PDFViewer/.lang/ru.po b/app/examples/Misc/PDFViewer/.lang/ru.po new file mode 100644 index 00000000..ed649400 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.lang/ru.po @@ -0,0 +1,112 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/PDFViewer/.project:23 +msgid "PdfViewer" +msgstr "Просмотрщик PDF" + +#: app/examples/Misc/PDFViewer/.project:24 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Простой просмотрщик PDF, используемый как пример возможностей gb.pdf" + +#: app/examples/Misc/PDFViewer/.src/FMain.class:47 +msgid "Pdf documents" +msgstr "Документы PDF" + +#: app/examples/Misc/PDFViewer/.src/FMain.class:131 +msgid "Can't set zoom to " +msgstr "Невозможно установить масштаб на " + +#: app/examples/Misc/PDFViewer/.src/FMain.class:131 app/examples/Misc/PDFViewer/.src/Fabout.form:23 +msgid "OK" +msgstr "ОК" + +#: app/examples/Misc/PDFViewer/.src/FMain.class:134 +msgid "of" +msgstr "из" + +#: app/examples/Misc/PDFViewer/.src/FMain.class:159 app/examples/Misc/PDFViewer/.src/FMain.class:384 app/examples/Misc/PDFViewer/.src/FMain.class:433 +msgid "" +"An error occurred while trying to view the document.\n" +"\n" +"If this persists please report this problem." +msgstr "" +"Произошла ошибка при попытке просмотра документа.\n" +"\n" +"Если это повторяется, то сообщите об этой проблеме." + +#: app/examples/Misc/PDFViewer/.src/FMain.form:5 +msgid "Simple PDF document viewer" +msgstr "Простой просмотрщик PDF документов" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:11 +msgid "Open file..." +msgstr "Открыть файл..." + +#: app/examples/Misc/PDFViewer/.src/FMain.form:16 +msgid "About..." +msgstr "О программе..." + +#: app/examples/Misc/PDFViewer/.src/FMain.form:22 +msgid "Rotate" +msgstr "Вращение" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:28 +msgid "Zoom out" +msgstr "Уменьшить" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:34 +msgid "Zoom in" +msgstr "Увеличить" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:43 +msgid "Find text" +msgstr "Найти текст" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:55 app/examples/Misc/PDFViewer/.src/FMain.form:74 +msgid "Previous page" +msgstr "Предыдущая страница" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:63 +msgid "Next Found" +msgstr "Следующий найденный" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:80 +msgid "Page" +msgstr "Страница" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:89 +msgid "Go to this page" +msgstr "Перейти на эту страницу" + +#: app/examples/Misc/PDFViewer/.src/FMain.form:100 +msgid "Next page" +msgstr "Следующая страница" + +#: app/examples/Misc/PDFViewer/.src/Fabout.form:12 +msgid "

    Simple PDF document viewer

    Gambas example by Daniel Campos Fernández and Bernd Brinkmann" +msgstr "

    Простой просмотрщик PDF документов

    Пример Gambas Даниэля Кампоса Фернандеса и Бернда Бринкмана" + diff --git a/app/examples/Misc/PDFViewer/.project b/app/examples/Misc/PDFViewer/.project new file mode 100644 index 00000000..6f8d363c --- /dev/null +++ b/app/examples/Misc/PDFViewer/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +Title=PdfViewer +Startup=FMain +Icon=pdf.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.pdf +Description="A simple PDF Viewer, used as example of gb.pdf capabilities" +Authors="Daniel Campos Fernández " +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Misc/PDFViewer/.src/FMain.class b/app/examples/Misc/PDFViewer/.src/FMain.class new file mode 100644 index 00000000..3cd2794b --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/FMain.class @@ -0,0 +1,472 @@ +' Gambas class file + +'*************************************************************************** +' +' FMain.class +' +' PdfViewer gb.pdf component example +' +' (C) 2007 Daniel Campos Fernández +' 2012 Bernd Brinkmann (modifications on the search and zoom function) +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** + +Public hPdf As New PdfDocument +Public cIndex As Integer +Public CurrentPage As Integer +Public CurrentZoom As Float = 1.0 +Private bExit As Boolean +Private currentSearchResult As Short = 0 'Contains the information which of the search results on this page is marked +Private NumberOfSearchResults As Integer +Private currentSearchResultSynonyms As Short = 0 +Private ScrollXPositionBeforeZoom As Float +Private ScrollYPositionBeforeZoom As Float + + +Public Sub BtOpen_Click() + + Dialog.Filter = ["*.pdf", ("Pdf documents")] + If Dialog.OpenFile(False) Then Return + + hPdf.Close() + Try hPdf.Open(Dialog.Path) + If Error Then + Message.Error(Error.Text) + Return + End If + + CurrentZoom = 1 + CurrentPage = 1 + RenderPage() + BtPrev.Enabled = False + If hPdf.Count > 1 Then + BtNext.Enabled = True + Else + BtNext.Enabled = False + End If + txtGotoPage.Enabled = True + btZoomIn.Enabled = True + btZoomOut.Enabled = True + btRotate.Enabled = True + pBox.Visible = True + txtFind.Enabled = True + + tvIndex.Clear() + If hPdf.HasIndex Then + tvIndex.Visible = True + AddIndex(0, "") + splIndex.Layout = [1, 3] + Else + tvIndex.Visible = False + End If + +End + +Public Function AddIndex(nItem As Integer, pItem As String) As Integer + + Dim pR As String + Dim iPage As Integer + + Do + iPage = 1 + Try iPage = hPdf.Index.Data.Page + If Error Then Print Error.Text + pR = nItem & "-" & iPage + tvIndex.Add(nItem & "-" & iPage, hPdf.Index.Title, Null, pItem) + Inc nItem + If hPdf.Index.HasChildren Then + hPdf.Index.MoveChild() + nItem = AddIndex(nItem + 1, pR) + hPdf.Index.MoveParent() + End If + Loop Until hPdf.Index.MoveNext() + + Return nItem + +End + +Public Sub tvIndex_Click() + + CurrentPage = Mid(tvIndex.Current.Key, InStr(tvIndex.Current.Key, "-") + 1) + BtPrev.Enabled = True + BtNext.Enabled = True + If CurrentPage = 1 Then BtPrev.Enabled = False + If CurrentPage = hPdf.Count Then BtNext.Enabled = False + RenderPage() + +End + +Public Sub RenderPage(Optional FoundText As String, Optional Casesensetivity As Boolean) + '--------------------------------------------------------------------------- + 'This function is called everytime something changed for example a new search result has to be displayed or the page + 'has canged the parameters FoundText and Casesensetivity are only of interrest if the funktion is called from a search frunction + '-------------------------------------------------------------------------- + Dim hPic As Picture + + ScrollXPositionBeforeZoom = ViewPort.ScrollX / ViewPort.ScrollWidth + ScrollYPositionBeforeZoom = ViewPort.ScrollY / ViewPort.ScrollHeight + + If CurrentZoom > 0.0 Then + hPdf.Zoom = CurrentZoom + Else + Message.Error(("Can't set zoom to ") & CurrentZoom, ("OK")) + Endif + + lblInfo.Text = " " & ("of") & " " & hPdf.Count + If txtGotoPage.text <> CurrentPage Then txtGotoPage.text = CurrentPage 'if the parameter currentPage is different from the current page the current page gets changed + If currentSearchResult = 0 Then 'if a search result is highlighted the focus will not be changed to the top + ViewPort.Scroll(0, 0) + Endif + + hPic = hPdf[CurrentPage].Image.Picture + + 'the search result gets highlighted here and the focus is set to the right position again if something has changed for example the zoomfactor + If currentSearchResult > 0 And FoundText <> "" Then ' resets the mark on the current search result + hPdf[CurrentPage].Find(FoundText, Casesensetivity) + Paint.Begin(hPic) + Paint.Brush = Paint.Color(Color.RGB(0, 0, 255, 192)) + Paint.Rectangle(hPdf[CurrentPage].Result[currentSearchResult - 1].Left, hPdf[CurrentPage].Result[currentSearchResult - 1].Top, hPdf[CurrentPage].Result[currentSearchResult - 1].Width, hPdf[CurrentPage].Result[currentSearchResult - 1].Height) + ViewPort.ScrollY = ViewPort.ScrollHeight * (hPdf[CurrentPage].Result[currentSearchResult - 1].Top / Paint.Height) + Paint.Fill + Paint.End + PBox.Picture = hPic + Endif + + PBox.Picture = hPic + PBox.Resize(hPdf[CurrentPage].Width, hPdf[CurrentPage].Height) + Form_Resize() + +Catch + Message.Info(("An error occurred while trying to view the document.\n\nIf this persists please report this problem.")) + +End + + +Public Sub Form_Resize() + + If CurrentPage = 0 Then Return + ViewPort.ScrollX = ScrollXPositionBeforeZoom * ViewPort.ScrollWidth + ViewPort.Scrolly = ScrollYPositionBeforeZoom * ViewPort.ScrollHeight + +End + + +Public Sub splIndex_Resize() + + Form_Resize() + +End + +Public Sub BtNext_Click() + + Inc CurrentPage + currentSearchResult = 0 + currentSearchResultSynonyms = 0 + If CurrentPage = hPdf.Count Then + BtNext.Enabled = False + End If + BtPrev.Enabled = True + BtPrev.SetFocus + txtGotoPage.text = CurrentPage 'this automatically calls the function txtGotoPage_Change and changes the page to the new "currentPage" + +End + +Public Sub BtPrev_Click() + + Dec CurrentPage + currentSearchResult = 0 + currentSearchResultSynonyms = 0 + If CurrentPage = 1 Then + BtPrev.Enabled = False + End If + BtNext.Enabled = True + BtNext.SetFocus + txtGotoPage.text = CurrentPage 'this automatically calls the function txtGotoPage_Change and changes the page to the new "currentPage" + +End +Public Sub btZoomIn_Click() + + If CurrentZoom < 3 Then CurrentZoom += 0.1 + If CurrentZoom = 3 Then btZoomIn.Enabled = False + btZoomOut.Enabled = True + RenderPage() + +End + +Public Sub btZoomOut_Click() + + If CurrentZoom > 0.5 Then CurrentZoom -= 0.1 + If CurrentZoom = 0.5 Then btZoomOut.Enabled = False + btZoomIn.Enabled = True + RenderPage() + +End + +Public Sub Form_Close() + + hPdf.Close() + +End + +Public Sub Button1_Click() + + Fabout.ShowDialog() + +End + +Public Sub btRotate_Click() + + Select Case hPdf.Orientation + Case PdfDocument.Normal + hPdf.Orientation = PdfDocument.Sideways + Case PdfDocument.Sideways + hPdf.Orientation = PdfDocument.Inverted + Case PdfDocument.Inverted + hPdf.Orientation = PdfDocument.SidewaysInverted + Case PdfDocument.SidewaysInverted + hPdf.Orientation = PdfDocument.Normal + End Select + + RenderPage() + +End + +Public Sub txtGotoPage_Change() + + '--------------------------------------------------------------------------- + 'This function changes the page to the page number written in the textbox "txtGotoPage" + 'the text in this textbox can be changed by the user for example by klicking on the next button or other funktions such as + 'a search funktion + '-------------------------------------------------------------------------- + ' + If Bexit Or Last.text = "" Then Return + 'the last search results get removed by the next four lines + BtSearchNext.Enabled = False + BtSearchPrev.Enabled = False + currentSearchResult = 0 + If Val(Last.text) > hPdf.Count Or Val(Last.text) = hPdf.Count Then 'hPdf.count contains the length of the pdf document + bExit = True + txtGotoPage.text = hPdf.Count + BtNext.Enabled = False 'because the last page is now displayed the next page button gets disabled + BtPrev.Enabled = True + bExit = False + Else + BtNext.Enabled = True + If Val(Last.text) = 1 Or Val(Last.text) < 1 Then + bExit = True + txtGotoPage.text = 1 + bExit = False + BtPrev.Enabled = False + Else + BtPrev.Enabled = True + End If + End If + currentPage = Val(Last.text) + Bexit = False + RenderPage() + +End + +Public Sub txtGotoPage_KeyPress() + + ' If modUtil.AllowKeys(const.AllowKeys_NumbersOnly, Key.code) = False Then + ' Stop Event + ' Return + ' End If + +End + +Public Sub txtFind_Activate() + + '--------------------------------------------------------------------------- + 'This function is called if the user wants fo find the string inside the textbox "TxtFind" by hitting the enter key + '-------------------------------------------------------------------------- + + If Bexit Then Return + If txtFind.Text <> "" Then + If currentSearchResult > 0 Then + If currentSearchResult = NumberOfSearchResults Then + If currentpage = hPdf.Count Then + CurrentPage = 1 + Else + CurrentPage = CurrentPage + 1 + Endif + FindNext() + Else + currentSearchResult = currentSearchResult + 1 + RenderPage(txtFind.Text) + Endif + Else + FindNext() + Endif + End If + +End + +Public Sub txtFind_Click() + + txtFind_Activate + +End + + +Private Sub FindNext() + '--------------------------------------------------------------------------- + ' This function finds the next string in the pdf matching the search string located after the current search result + '--------------------------------------------------------------------------- + Dim hPic As Picture + Dim currentSearchPage As Short = CurrentPage ' contains the information on which page the search funktion searched the last time + Dim LastPageToSearch As Short ' contains the information which page is the last page for the find function + + If CurrentPage = 1 Then + LastPageToSearch = 1 + Else + LastPageToSearch = CurrentPage + + Endif + currentSearchResultSynonyms = 0 + currentSearchResult = 0 + BtSearchNext.Enabled = False + BtSearchPrev.Enabled = False + Repeat + hPic = hPdf[currentSearchPage].Image.Picture + hPdf[currentSearchPage].Find(txtFind.Text, False) + + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentsearchPage + currentSearchResult = 1 + Else + If currentSearchPage = hPdf.Count Then + currentSearchPage = 1 + Else + currentSearchPage = currentSearchPage + 1 + Endif + Endif + Until currentSearchResult <> 0 Or currentSearchPage = LastPageToSearch + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentSearchPage + + txtGotoPage.text = CurrentPage + currentSearchResult = 1 + NumberOfSearchResults = hPdf[currentSearchPage].Result.Count + BtSearchNext.Enabled = True + BtSearchPrev.Enabled = True + + + RenderPage(txtFind.Text) + + Else + txtFind.Background = Color.Lighter(16711680) '16711680 = Red + RenderPage() + + Endif + +Catch + Message.Info(("An error occurred while trying to view the document.\n\nIf this persists please report this problem.") & "\n\n" & Error.Where & ": " & Error.Text) + +End + +Private Sub FindPrevious() + '--------------------------------------------------------------------------- + ' This function finds the next string in the pdf matching the search string located before the current search result + '--------------------------------------------------------------------------- + Dim hPic As Picture + Dim currentSearchPage As Short = CurrentPage ' contains the information on which page the search funktion searched the last time + Dim LastPageToSearch As Short ' contains the information which page is the last page for the find function + + If CurrentPage = hPdf.Count Then + LastPageToSearch = 0 + Else + LastPageToSearch = CurrentPage + Endif + currentSearchResultSynonyms = 0 + currentSearchResult = 0 + BtSearchNext.Enabled = False + BtSearchPrev.Enabled = False + + Repeat + hPic = hPdf[currentSearchPage].Image.Picture + hPdf[currentSearchPage].Find(txtFind.Text, False) + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentsearchPage + currentSearchResult = 1 + Else + If currentSearchPage = 1 Then + currentSearchPage = hPdf.Count + Else + currentSearchPage = currentSearchPage - 1 + Endif + Endif + Until currentSearchResult <> 0 Or currentSearchPage = LastPageToSearch + + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentSearchPage + + txtGotoPage.text = CurrentPage + currentSearchResult = hPdf[currentSearchPage].Result.Count + NumberOfSearchResults = hPdf[currentSearchPage].Result.Count + BtSearchNext.Enabled = True + BtSearchPrev.Enabled = True + RenderPage(txtFind.Text) + Endif + +Catch + Message.Info(("An error occurred while trying to view the document.\n\nIf this persists please report this problem.")) + +End + +Public Sub BtSearchNext_Click() + '--------------------------------------------------------------------------- + 'This funktion is highlighting the next seach string located after the current search string + '--------------------------------------------------------------------------- + If currentSearchResult = NumberOfSearchResults Then + If currentpage = hPdf.Count Then + CurrentPage = 1 + Else + CurrentPage = CurrentPage + 1 + Endif + FindNext() + Else + currentSearchResult = currentSearchResult + 1 + RenderPage(txtFind.Text) + Endif + +End + +Public Sub BtSearchPrev_Click() + '--------------------------------------------------------------------------- + 'This funktion is highlighting the next seach string located before the current search string + '--------------------------------------------------------------------------- + If currentSearchResult > 0 Then + If currentpage = 1 Then + currentpage = hPdf.Count + Else + CurrentPage = CurrentPage - 1 + Endif + FindPrevious() + Else + currentSearchResult = currentSearchResult - 1 + RenderPage(txtFind.Text) + Endif + +End + diff --git a/app/examples/Misc/PDFViewer/.src/FMain.form b/app/examples/Misc/PDFViewer/.src/FMain.form new file mode 100644 index 00000000..fb055f2c --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/FMain.form @@ -0,0 +1,126 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,106,99) + Text = ("Simple PDF document viewer") + Arrangement = Arrange.Vertical + { HBox1 HBox + MoveScaled(1,2,95,4) + { BtOpen ToolButton + MoveScaled(0,0,4,4) + ToolTip = ("Open file...") + Picture = Picture["icon:/small/open"] + } + { Button1 ToolButton + MoveScaled(4,0,4,4) + ToolTip = ("About...") + Picture = Picture["icon:/small/question"] + } + { btRotate ToolButton + MoveScaled(8,0,4,4) + Enabled = False + ToolTip = ("Rotate") + Picture = Picture["icon:/small/rotate-right"] + } + { btZoomOut ToolButton + MoveScaled(12,0,4,4) + Enabled = False + ToolTip = ("Zoom out") + Picture = Picture["icon:/small/zoom-out"] + } + { btZoomIn ToolButton + MoveScaled(16,0,4,4) + Enabled = False + ToolTip = ("Zoom in") + Picture = Picture["icon:/small/zoom-in"] + } + { Separator3 Separator + MoveScaled(22,0,0,4) + } + { txtFind ButtonBox + MoveScaled(24,0,15,4) + Enabled = False + ToolTip = ("Find text") + Expand = True + Picture = Picture["icon:/small/find"] + Border = False + } + { Panel1 Panel + MoveScaled(40,0,8,4) + Visible = False + Background = Color.TextBackground + { BtSearchPrev Button + MoveScaled(0,0,4,4) + Enabled = False + ToolTip = ("Previous page") + Picture = Picture["icon:/small/left"] + Border = False + } + { BtSearchNext Button + MoveScaled(4,0,4,4) + Enabled = False + Background = Color.TextBackground + ToolTip = ("Next Found") + Picture = Picture["icon:/small/right"] + Border = False + } + } + { Separator1 Separator + MoveScaled(51,0,0,4) + } + { BtPrev ToolButton + MoveScaled(54,0,4,4) + Enabled = False + ToolTip = ("Previous page") + Picture = Picture["icon:/small/left"] + } + { Page Label + MoveScaled(59,0,4,4) + AutoResize = True + Text = ("Page") + Alignment = Align.Right + } + { Panel2 Panel + MoveScaled(64,0,1,4) + } + { txtGotoPage TextBox + MoveScaled(66,0,9,4) + Enabled = False + ToolTip = ("Go to this page") + Alignment = Align.Center + Border = False + } + { lblInfo Label + MoveScaled(75,0,7,4) + AutoResize = True + } + { BtNext ToolButton + MoveScaled(84,0,4,4) + Enabled = False + ToolTip = ("Next page") + Picture = Picture["icon:/small/right"] + } + } + { Separator2 Separator + MoveScaled(36,8,27,0) + } + { splIndex HSplit + MoveScaled(9,11,81,60) + Expand = True + { tvIndex TreeView + MoveScaled(0,0,19,25) + Visible = False + Border = False + } + { ViewPort ScrollView + MoveScaled(36,5,30,32) + Background = Color.TextBackground + Expand = True + Border = False + { PBox PictureBox + MoveScaled(3,3,24,21) + Visible = False + } + } + } +} diff --git a/app/examples/Misc/PDFViewer/.src/Fabout.class b/app/examples/Misc/PDFViewer/.src/Fabout.class new file mode 100644 index 00000000..10033197 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/Fabout.class @@ -0,0 +1,43 @@ +' Gambas class file + +'*************************************************************************** +' +' FAbout.class +' +' PdfViewer gb.pdf component example +' +' (C) 2007 Daniel Campos Fernández +' +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** + +Public Sub btOK_Click() + + Me.Close() + +End + +Public Sub Form_Open() + + + +End diff --git a/app/examples/Misc/PDFViewer/.src/Fabout.form b/app/examples/Misc/PDFViewer/.src/Fabout.form new file mode 100644 index 00000000..1b8565a2 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/Fabout.form @@ -0,0 +1,32 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,48,22) + Resizable = False + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { TextLabel1 TextLabel + MoveScaled(2,2,45,13) + Expand = True + Text = ("

    Simple PDF document viewer

    Gambas example by Daniel Campos Fernández and Bernd Brinkmann") + Alignment = Align.Center + } + { Panel1 HBox + MoveScaled(13,16,32,4) + { Panel2 Panel + MoveScaled(4,0,2,4) + Expand = True + } + { btOK Button + MoveScaled(10,0,15,4) + Text = ("OK") + Default = True + Cancel = True + } + { Panel3 Panel + MoveScaled(26,0,2,4) + Expand = True + } + } +} diff --git a/app/examples/Misc/PDFViewer/pdf.png b/app/examples/Misc/PDFViewer/pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..651ded047317f9ff354a60a1ef9fd656a2720939 GIT binary patch literal 4588 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 z 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 iflC>9-Uboi{R7GHb4$42TLtETRC~JmxvX, YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/SystemTray/.project:16 +msgid "SystemTray" +msgstr "Системный трей" + +#: app/examples/Misc/SystemTray/.project:17 +msgid "" +"X11 System tray example.\n" +"\n" +"This example shows how to create a system tray with the gb.desktop.x11 component." +msgstr "" +"Пример системного трея X11.\n" +"\n" +"В этом примере показано, как создать системный трей с компонентом gb.desktop.x11." + +#: app/examples/Misc/SystemTray/.src/FMain.form:5 +msgid "System tray" +msgstr "Системный лоток" + diff --git a/app/examples/Misc/SystemTray/.project b/app/examples/Misc/SystemTray/.project new file mode 100644 index 00000000..1553b823 --- /dev/null +++ b/app/examples/Misc/SystemTray/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Title=SystemTray +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop +Component=gb.desktop.x11 +Description="X11 System tray example.\n\nThis example shows how to create a system tray with the gb.desktop.x11 component." +TabSize=2 +Translate=1 +Language=fr +Vendor=Example +Packager=1 diff --git a/app/examples/Misc/SystemTray/.src/FMain.class b/app/examples/Misc/SystemTray/.src/FMain.class new file mode 100644 index 00000000..2c792ffe --- /dev/null +++ b/app/examples/Misc/SystemTray/.src/FMain.class @@ -0,0 +1,45 @@ +' Gambas class file + +'Private $hImage As Image + +Public Sub _new() + + 'Me.Background = &H007FFF 'Color.SetAlpha(&H007FFF, 192) + +End + +Public Sub Form_Open() + + X11Systray.Show(dwgSystemTray.Handle, dwgSystemTray.Background) + +End + +Static Public Sub X11Systray_Arrange() + + Dim I As Integer + Dim X, Y, H As Integer + + Debug X11Systray.Count + X = 2 + Y = 2 + For I = 0 To X11Systray.Count - 1 + With X11Systray[I] + Debug I;; .IconW;; .IconH + If (X + .IconW) >= (Me.ClientW - 2) Then + X = 2 + Y += H + 2 + H = 0 + Endif + .Move(X, Y, .IconW, .IconH) + H = Max(H, .IconH) + X += .IconW + 2 + End With + Next + +End + +Public Sub dwgSystemTray_Arrange() + + X11Systray.Move(0, 0, dwgSystemTray.Width, dwgSystemTray.Height) + +End diff --git a/app/examples/Misc/SystemTray/.src/FMain.form b/app/examples/Misc/SystemTray/.src/FMain.form new file mode 100644 index 00000000..160008fc --- /dev/null +++ b/app/examples/Misc/SystemTray/.src/FMain.form @@ -0,0 +1,14 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,48,27) + Text = ("System tray") + Arrangement = Arrange.Fill + Margin = True + { dwgSystemTray DrawingArea + MoveScaled(8,2,24,24) + Background = &HFFFF00& + Border = Border.Plain + Cached = True + } +} diff --git a/app/examples/Misc/SystemTray/bg.png b/app/examples/Misc/SystemTray/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..7aa2ddb11f4b1cbde5ba7af09c5fb5b1379f47ae GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^3JeU43>+*#R>g`ve}Pn%r;B4q#jUsJ4stRm@US?F za&J$zp0H<)g21O4zwaA;o;UHzCz2f|g6G~Y?TNylE L{an^LB{Ts5O#vu< literal 0 HcmV?d00001 diff --git a/app/examples/Misc/SystemTray/icon.png b/app/examples/Misc/SystemTray/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7b22ad890590a54e372bb70ab931baf3249b907b GIT binary patch literal 4363 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, YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/WatchGambasDirectory/.project:15 +msgid "WatchGambasDirectory" +msgstr "Наблюдать за директорией Gambas" + +#: app/examples/Misc/WatchGambasDirectory/.project:16 +msgid "" +"Watch the Gambas temporary directory, /tmp/gambas.UID, to detect starting and ending Gambas processes.\n" +"\n" +"This project shows basic usage of gb.inotify's Watch class without going very far in demonstrating its capabilities/available events." +msgstr "" +"Смотреть временный каталог Gambas, /tmp/gambas.UID, чтобы определить начало и конец процессов Gambas.\n" +"\n" +"Этот проект демонстрирует базовое использование класса Watch из gb.inotify, но не слишком далеко демонстрирует его возможности/доступные события." + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:10 +msgid "I'm watching... You may now start Gambas projects." +msgstr "Я наблюдаю... Теперь вы можете запустить проекты Gambas." + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:11 +msgid "Return toggles Pause/Resume. Enter \"quit\" to quit." +msgstr "Клавиша Enter переключает Пауза/Возобновление. Введите «выйти» для выхода." + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:18 +msgid "quit" +msgstr "выйти" + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:20 +msgid "Currently" +msgstr "В данный момент" + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:20 +msgid "inactive" +msgstr "неактивен" + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:20 +msgid "active" +msgstr "активен" + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:25 +msgid "New Gambas process:" +msgstr "Новый процесс Gambas:" + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:34 app/examples/Misc/WatchGambasDirectory/.src/MMain.module:51 +msgid "(unknown)" +msgstr "(неизвестный)" + +#: app/examples/Misc/WatchGambasDirectory/.src/MMain.module:52 +msgid "terminated (normally)" +msgstr "прервано (нормально)" + diff --git a/app/examples/Misc/WatchGambasDirectory/.project b/app/examples/Misc/WatchGambasDirectory/.project new file mode 100644 index 00000000..67b64484 --- /dev/null +++ b/app/examples/Misc/WatchGambasDirectory/.project @@ -0,0 +1,14 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=WatchGambasDirectory +Startup=MMain +Icon=watch.svg +Version=3.6.2 +VersionFile=1 +Component=gb.inotify +Description="Watch the Gambas temporary directory, /tmp/gambas.UID, to detect starting and ending Gambas processes.\n\nThis project shows basic usage of gb.inotify's Watch class without going very far in demonstrating its capabilities/available events." +Authors="(C) 2014 Tobias Boege , GPLv2+." +TabSize=2 +SourcePath=/tmp +Packager=1 +Translate=1 diff --git a/app/examples/Misc/WatchGambasDirectory/.src/MMain.module b/app/examples/Misc/WatchGambasDirectory/.src/MMain.module new file mode 100644 index 00000000..074904a8 --- /dev/null +++ b/app/examples/Misc/WatchGambasDirectory/.src/MMain.module @@ -0,0 +1,54 @@ +' Gambas module file + +Private $hWatch As Watch + +Public Sub Main() + ' The watched events are determined automatically by the event handlers defined + $hWatch = New Watch("/tmp/gambas." & Str$(User.Id), True) As "GambasDirectory" + $hWatch.Tag = New Collection ' Save descendants here + + Print ("I'm watching... You may now start Gambas projects.") + Print ("Return toggles Pause/Resume. Enter \"quit\" to quit.") +End + +Public Sub Application_Read() + Dim sBuf As String + + Line Input sBuf + If sBuf = ("quit") Then Quit + $hWatch.IsPaused = Not $hWatch.IsPaused + Print ("Currently");; IIf($hWatch.IsPaused, ("inactive"), ("active")) +End + +Public Sub GambasDirectory_Create() + If Not Watch.IsDir Then Return ' Skip files + Print ("New Gambas process:");; + ' Watch.Name is the PID of the new Gambas process. Try to get its name + ' from the /proc filesystem. + With Split(File.Load("/proc" &/ Watch.Name &/ "cmdline"), "/") + Last.Tag[Watch.Name] = Left$(.[.Max], -1) + Print Last.Tag[Watch.Name] + End With + + Catch + Print ("(unknown)") + ' Opening the /proc file may fail because the directory may have been + ' deleted already (if the process was relatively short-lived and this + ' event took longer than that to get from the kernel to us). +End + +Public Sub GambasDirectory_Delete() + Dim sName As String + + If Not Watch.IsDir Then Return + sName = Last.Tag[Watch.Name] + ' Sadly, we can only provide a name for dying processes that we watched + ' being created -- the /proc file is already gone at this point. + ' + ' Note that if the interpreter crashes (the process ends abnormally), + ' directories won't be removed so we won't notice that the process is + ' gone. + If Not sName Then sName = ("(unknown)") + Print sName;; ("terminated (normally)") + Last.Tag.Remove(Watch.Name) +End diff --git a/app/examples/Misc/WatchGambasDirectory/watch.svg b/app/examples/Misc/WatchGambasDirectory/watch.svg new file mode 100644 index 00000000..758d0b7a --- /dev/null +++ b/app/examples/Misc/WatchGambasDirectory/watch.svg @@ -0,0 +1,1257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/examples/Multimedia/CDPlayer/.directory b/app/examples/Multimedia/CDPlayer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/CDPlayer/.icon.png b/app/examples/Multimedia/CDPlayer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..66db8113c4362dc5ddc299203d0108488652af6a GIT binary patch literal 4311 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 FV1i$pHsk;R literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/CDPlayer/.lang/ca.po b/app/examples/Multimedia/CDPlayer/.lang/ca.po new file mode 100644 index 00000000..201e1f47 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.lang/ca.po @@ -0,0 +1,60 @@ +# Catalan translation of CDPlayer +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the CDPlayer package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: CDPlayer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-04-07 05:18+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:2 +msgid "A Tiny CDplayer" +msgstr "Un petit reproductor de CD" + +#: .project:1 Fcdplayer.form:16 +msgid "CDplayer" +msgstr "Reproductor de CD" + +#: Fcdplayer.form:33 +msgid "&Eject" +msgstr "&Expulsa" + +#: Fcdplayer.class:22 +msgid "I Could not load cd-rom drive" +msgstr "No es pot carregar el cd-rom" + +#: Fcdplayer.form:28 +msgid "&Play" +msgstr "&Reprodueix" + +#: Fcdplayer.form:48 +msgid "Play &Track" +msgstr "Reprodueix la &pista" + +#: Fcdplayer.form:43 +msgid "&Stop" +msgstr "&Atura" + +#: Fcdplayer.form:22 +msgid "Track" +msgstr "Pista" + +#: Fcdplayer.form:57 +msgid "Volume" +msgstr "Volum" + +#: Fcdplayer.class:26 +msgid "Your PC does not have cd-rom drive" +msgstr "El vostre PC no té unitat de cd-rom." + diff --git a/app/examples/Multimedia/CDPlayer/.lang/cs.po b/app/examples/Multimedia/CDPlayer/.lang/cs.po new file mode 100644 index 00000000..b94759f5 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.lang/cs.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 Fcdplayer.form:16 +msgid "CDplayer" +msgstr "-" + +#: .project:2 +msgid "A Tiny CDplayer" +msgstr "Lehký CDpřehlávač" + +#: Fcdplayer.class:22 +msgid "I Could not load cd-rom drive" +msgstr "Nemohu načíst CD-ROM" + +#: Fcdplayer.class:26 +msgid "Your PC does not have cd-rom drive" +msgstr "Váš počítač nemá cd-rom mechaniku" + +#: Fcdplayer.form:22 +msgid "Track" +msgstr "Stopa" + +#: Fcdplayer.form:28 +msgid "&Play" +msgstr "-" + +#: Fcdplayer.form:33 +msgid "&Eject" +msgstr "&Vysunout" + +#: Fcdplayer.form:43 +msgid "&Stop" +msgstr "-" + +#: Fcdplayer.form:48 +msgid "Play &Track" +msgstr "&Přehrát stopu" + +#: Fcdplayer.form:57 +msgid "Volume" +msgstr "Hlasitost" diff --git a/app/examples/Multimedia/CDPlayer/.lang/es.po b/app/examples/Multimedia/CDPlayer/.lang/es.po new file mode 100644 index 00000000..bfb15eaa --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.lang/es.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "A Tiny CDplayer" +msgstr "Un pequeño CDPlayer" + +#: Fcdplayer.class:151 .project:1 +msgid "CDplayer" +msgstr "CDplayer" + +#: Fcdplayer.class:169 +msgid "&Eject" +msgstr "&Expulsar" + +#: Fcdplayer.class:164 +msgid "&Play" +msgstr "&Tocar" + +#: Fcdplayer.class:186 +msgid "Play &Track" +msgstr "Tocar &Canción" + +#: Fcdplayer.class:181 +msgid "&Stop" +msgstr "&Parar" + +#: Fcdplayer.class:157 +msgid "Track" +msgstr "Canción" + +#: Fcdplayer.class:196 +msgid "Volume" +msgstr "Volumen" + diff --git a/app/examples/Multimedia/CDPlayer/.lang/ru.po b/app/examples/Multimedia/CDPlayer/.lang/ru.po new file mode 100644 index 00000000..639e0b82 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.lang/ru.po @@ -0,0 +1,90 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Multimedia/CDPlayer/.project:20 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form:5 +msgid "CDplayer" +msgstr "CD-плеер" + +#: app/examples/Multimedia/CDPlayer/.project:21 +msgid "A Tiny CDplayer" +msgstr "Крошечный CD-плеер" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:22 +msgid "I Could not load cd-rom drive" +msgstr "Не удалось загрузить CD-ROM привод" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:26 +msgid "Your PC does not have cd-rom drive" +msgstr "Ваш компьютер не имеет привода CD-ROM" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:35 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:109 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:136 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form:15 +msgid "&Play" +msgstr "Игра" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:58 +msgid "No CD in Drive" +msgstr "В приводе нет CD" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:64 +msgid "Tracks" +msgstr "Треки" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:65 +msgid "Total Length :" +msgstr "Общая длина:" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:72 +msgid "Playing Track" +msgstr "Играющий трек" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:81 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form:10 +msgid "Track" +msgstr "Трек" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:115 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:127 +msgid "&Pause" +msgstr "Пауза" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class:121 +msgid "&Resume" +msgstr "Возобновить" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form:19 +msgid "&Eject" +msgstr "Извлечь" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form:27 +msgid "&Stop" +msgstr "Стоп" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form:31 +msgid "Play &Track" +msgstr "Воспроизвести трек" + +#: app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form:39 +msgid "Volume" +msgstr "Громкость" + diff --git a/app/examples/Multimedia/CDPlayer/.project b/app/examples/Multimedia/CDPlayer/.project new file mode 100644 index 00000000..09f960c3 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +Title=CDplayer +Startup=Fcdplayer +Icon=cdrom.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.qt5 +Component=gb.qt5.ext +Component=gb.sdl.sound +Description="A Tiny CDplayer" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class new file mode 100644 index 00000000..33d6508d --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class @@ -0,0 +1,139 @@ +' Gambas class file + +' Simple CDplayer +' Carlier Laurent - (c) 2005 +' Under GNU GPL V2 or Later +' +' Done for Testing the sdl component +' CDROM part + +Static mycd As CDRom +Static HaveCD As Boolean + +Public Sub Form_open() + If CDRoms.Count > 0 Then + Try mycd = New CDRom + If Not IsNull(mycd) Then + Volume.Value = Abs(mycd.Volume - Volume.MaxValue) + TrackPos.Value = 0 + Me.Center + Timer1.Enabled = True + Else + Message.Warning(("I Could not load cd-rom drive")) + Me.Close + Endif + Else + Message.Warning(("Your PC does not have cd-rom drive")) + Me.Close + Endif + +End + +Public Sub SButton_Click() + + mycd.Stop() + PButton.Text = ("&Play") + TrackPos.Value = 0 + +End + +Public Sub PTButton_Click() + + If Not HaveCD Then + Return + Endif + + mycd.Tracks[TrackInfo.Index + 1].Play() + +End + +Public Sub Timer1_Timer() + + Dim trckloop As Integer + Dim string1 As String + Dim string2 As String + + If Not mycd.Ready Then + TrackInfo.Clear() + TextBox1.Text = ("No CD in Drive") + HaveCD = False + Return + Endif + + If mycd.Stopped Then + string1 = mycd.Tracks.Count & " " & ("Tracks") + string2 = ("Total Length :") & " " & ToTime(mycd.Length) + TextBox1.Text = string1 & Space$(41 - (Len(string1) + Len(string2))) & string2 + TrackPos.Value = 0 + Endif + + If mycd.Playing Or mycd.Paused Then + TrackPos.Value = (mycd.Tracks[mycd.Tracks.Current].Position) / mycd.Tracks[mycd.Tracks.Current].length + TextBox1.Text = ("Playing Track") & " " & mycd.Tracks.Current + Endif + + If HaveCD Then + Return + Endif + + TrackInfo.Clear() + For trckloop = 1 To mycd.Tracks.Count + string1 = ("Track") & " " & trckloop + string2 = "<" & ToTime(mycd.Tracks[trckloop].Length) & ">" + TrackInfo.Add(string1 & Space$(49 - (Len(string1) + Len(string2))) & string2) + Next + + Volume.Value = Abs(mycd.Volume - 255) + HaveCD = True + +End + +Public Function ToTime(length As Integer) As String + + Dim myString As String + + mystring = Format$(length \ 60, "00") & ":" & Format$((length Mod 60), "00") + Return myString + +End + +Public Sub Volume_Change() + + mycd.Volume = Abs(Volume.Value - Volume.MaxValue) + +End + +Public Sub PButton_Click() + + If Not HaveCD Then + PButton.Text = ("&Play") + Return + Endif + + If mycd.Stopped Then + mycd.Play() + PButton.Text = ("&Pause") + Return + Endif + + If mycd.Playing Then + mycd.Pause() + PButton.Text = ("&Resume") + Return + Endif + + If mycd.Paused Then + mycd.Resume() + PButton.Text = ("&Pause") + Endif + +End + +Public Sub EButton_Click() + + Try mycd.Eject + HaveCD = False + PButton.Text = ("&Play") + TrackPos.Value = 0 + +End diff --git a/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form new file mode 100644 index 00000000..e52dc819 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form @@ -0,0 +1,47 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(69.7143,114.7143,50,26) + Text = ("CDplayer") + Icon = Picture["cd.png"] + Resizable = False + { TrackInfo ComboBox + MoveScaled(1,1,48,4) + ToolTip = ("Track") + ReadOnly = True + } + { PButton Button + MoveScaled(1,11,21,4) + Text = ("&Play") + } + { EButton Button + MoveScaled(1,16,21,4) + Text = ("&Eject") + } + { TextBox1 TextBox + MoveScaled(1,6,43,4) + ReadOnly = True + } + { SButton Button + MoveScaled(23,11,21,4) + Text = ("&Stop") + } + { PTButton Button + MoveScaled(23,16,21,4) + Text = ("Play &Track") + } + { Timer1 #Timer + #MoveScaled(36,1) + } + { Volume Slider + MoveScaled(46,6,3,17) + Mouse = Mouse.SizeS + ToolTip = ("Volume") + MaxValue = 255 + PageStep = 30 + Mark = True + } + { TrackPos ProgressBar + MoveScaled(1,21,43,4) + } +} diff --git a/app/examples/Multimedia/CDPlayer/cdrom.png b/app/examples/Multimedia/CDPlayer/cdrom.png new file mode 100644 index 0000000000000000000000000000000000000000..fa1dbc8b79bbe3c7bfd5cba80cca665c66f7266f GIT binary patch literal 3987 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/.directory b/app/examples/Multimedia/MediaPlayer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/MediaPlayer/.hidden/screenshots/MediaPlayer.jpg b/app/examples/Multimedia/MediaPlayer/.hidden/screenshots/MediaPlayer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65929eb055a6e80d4aa6e5a7a5cdf909b56cfc1a GIT binary patch literal 144138 zcmeEu2T)Vp_U}odcL*9VKqvwUMM^-bgc1lHLN6*Ef+9_%h|&=Q(m{xbp@d#U0l^AN zM>;5?C<210sE7p-;T`n*?)~ol&zt{!ciww5@6E+I!*I?%d#|=xL#NluvQtH}jN?P*DIHlc5AS^5_>}>2v4i2P}C`wf6fB9#p1K?qX z?_r9CLBs$k4+O>o+35mA006=OQoBp=A3qQ%49>vF#LU9V1`cT92A~ia3<`%aFu>v9 z=riDR0M5g}E2^l=$Y<@&Bo>TNipwft7T0U)AuK>>V7PoISm~eSH1M{-GzsPK8HAMkORBC7(+z}`T{kHLabC)j&0Q=3>-#GgxzIZ^spl~=0&a}%H1R4%rFdjIAs3Ie;t~HZ;FrS!G z95X^MtE8!uMO>Nmk>4X^jCGF$?&bcqUDkea_OCG(|8H^jH^%PKovR&9%V~IIHlgghrsgyZ@M<-U4%i0Eyx%hsJJ>yA`rYb5N%M5dAJ^4+NAJH~hh zg!TU)7ytbuM|J>@jmOqgvjxr s7Cpu(={4bpR)!Rc!_wK(@%P5jZ8qy9XRJqk&g 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

    F;%X(yiUoBjvLC;R35KMLJn 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/.lang/fr.po b/app/examples/Multimedia/MediaPlayer/.lang/fr.po new file mode 100644 index 00000000..d930dc66 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.lang/fr.po @@ -0,0 +1,64 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Media player based on GStreamer" +msgstr "" + +#: .project:2 +msgid "This example is a demonstration of how to use GStreamer with Gambas to play any sound or video file." +msgstr "" + +#: FControl.form:39 +msgid "MediaPlayer, a media player example based on GStreamer made by Benoît Minisini" +msgstr "MediaPlayer, un exemple de lecteur multimedia basé sur GStreamer programmé par Benoît Minisini" + +#: FMain.class:176 +msgid "Select a media file" +msgstr "Choisissez un fichier son ou vidéo" + +#: FMain.class:197 FTags.form:109 +msgid "Video device" +msgstr "Périphérique video" + +#: FMain.class:223 +msgid "Select a subtitle file" +msgstr "Choisissez un fichier de sous-titres" + +#: FTags.class:12 +msgid "Hue" +msgstr "Teinte" + +#: FTags.class:14 +msgid "Saturation" +msgstr "Saturation" + +#: FTags.class:16 +msgid "Contrast" +msgstr "Contraste" + +#: FTags.class:18 +msgid "Brightness" +msgstr "Luminosité" + +#: FTags.class:109 +msgid "No video device" +msgstr "Aucun périphérique video" + +#: FTags.form:45 +msgid "Information" +msgstr "Information" + +#: FTags.form:78 +msgid "Balance" +msgstr "Balance" diff --git a/app/examples/Multimedia/MediaPlayer/.lang/ru.po b/app/examples/Multimedia/MediaPlayer/.lang/ru.po new file mode 100644 index 00000000..347b2881 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.lang/ru.po @@ -0,0 +1,92 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Multimedia/MediaPlayer/.project:56 +msgid "Media player based on GStreamer" +msgstr "Медиапроигрыватель на основе GStreamer" + +#: app/examples/Multimedia/MediaPlayer/.project:57 +msgid "" +"Media player example.\n" +"\n" +"This example is a demonstration of how to use GStreamer with Gambas to play any sound or video file." +msgstr "" +"Пример медиаплеера.\n" +"\n" +"Этот пример демонстрирует, как использовать GStreamer с Gambas для воспроизведения любого звукового или видеофайла." + +#: app/examples/Multimedia/MediaPlayer/.src/FControl.form:26 +msgid "MediaPlayer, a media player example based on GStreamer made by Benoît Minisini" +msgstr "Медиапроигрыватель от Бенуа Минисини, пример медиаплеера на основе GStreamer" + +#: app/examples/Multimedia/MediaPlayer/.src/FMain.class:206 +msgid "Select a media file" +msgstr "Выбрать медиа файл" + +#: app/examples/Multimedia/MediaPlayer/.src/FMain.class:227 app/examples/Multimedia/MediaPlayer/.src/FTags.form:80 +msgid "Video device" +msgstr "Видеоустройство" + +#: app/examples/Multimedia/MediaPlayer/.src/FMain.class:253 +msgid "Select a subtitle file" +msgstr "Выбрать файл субтитров" + +#: app/examples/Multimedia/MediaPlayer/.src/FTags.class:12 +msgid "Hue" +msgstr "Тон" + +#: app/examples/Multimedia/MediaPlayer/.src/FTags.class:14 +msgid "Saturation" +msgstr "Насыщенность" + +#: app/examples/Multimedia/MediaPlayer/.src/FTags.class:16 +msgid "Contrast" +msgstr "Контраст" + +#: app/examples/Multimedia/MediaPlayer/.src/FTags.class:18 +msgid "Brightness" +msgstr "Яркость" + +#: app/examples/Multimedia/MediaPlayer/.src/FTags.class:109 +msgid "No video device" +msgstr "Нет видеоустройства" + +#: app/examples/Multimedia/MediaPlayer/.src/FTags.form:26 +msgid "Information" +msgstr "Информация" + +#: app/examples/Multimedia/MediaPlayer/.src/FTags.form:54 +msgid "Balance" +msgstr "Баланс" + +#: app/examples/Multimedia/MediaPlayer/.src/MTest.module:30 +msgid "I will take a screenshot in 5 seconds..." +msgstr "Я сделаю скриншот через 5 секунд..." + +#: app/examples/Multimedia/MediaPlayer/.src/MTest.module:40 +msgid "Screenshot saved to '~/screenshot.png'!" +msgstr "Скриншот сохранён в ~/screenshot.png!" + diff --git a/app/examples/Multimedia/MediaPlayer/.project b/app/examples/Multimedia/MediaPlayer/.project new file mode 100644 index 00000000..1d2c495a --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.project @@ -0,0 +1,55 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=Media player based on GStreamer +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop.x11 +Component=gb.desktop +Component=gb.form.dialog +Component=gb.settings +Component=gb.media +Description="Media player example.\n\nThis example is a demonstration of how to use GStreamer with Gambas to play any sound or video file." +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Example +Address=benoit@black-tower +Url=www.endoftheinternet.com +License=General Public License +PackageName=mediaplayer-3.5.0 +PackageVersion=2 +CreateEachDirectory=1 +Packager=1 +Systems=slackware +Menus=archlinux:"Applications/Video" +Categories=archlinux:"Video" +Groups=archlinux:"video" +Menus=debian:"Applications/Video" +Categories=debian:"Video" +Groups=debian:"video" +Menus=fedora:"Audio Video/Video/Player" +Categories=fedora:"AudioVideo;Player;Video" +Groups=fedora:"Applications/Multimedia" +Menus=mageia:"Multimedia/Video" +Categories=mageia:"AudioVideo;Video" +Groups=mageia:"Video" +Menus=mandriva:"Multimedia/Video" +Categories=mandriva:"AudioVideo;Video" +Groups=mandriva:"Video" +Menus=slackware:"Audio Video/Video/Player" +Categories=slackware:"AudioVideo;Player;Video" +Groups=slackware:"Applications/Multimedia" +Menus=suse:"Audio Video/Video/Player" +Categories=suse:"AudioVideo;Player;Video" +Groups=suse:"Productivity/Multimedia/Video/Players" +Menus=ubuntu:"Applications/Video" +Categories=ubuntu:"Video" +Groups=ubuntu:"video" +Tags=Audio,AudioVideo,Video,Multimedia +CreateMenu=1 diff --git a/app/examples/Multimedia/MediaPlayer/.src/CAnimation.class b/app/examples/Multimedia/MediaPlayer/.src/CAnimation.class new file mode 100644 index 00000000..a756ab55 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/CAnimation.class @@ -0,0 +1,101 @@ +' Gambas class file + +Static Private All As New CAnimation[] + +Event Stop + +Property Read Object As Object + +Private $hObject As Object +Private $sProperty As String +Private $fTarget As Float +Private $fTime As Float +Private $hTimer As Timer + +Static Public Sub Start(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer, Optional hParent As Object) + + Dim hAnim As CAnimation + Dim aStop As New CAnimation[] + + For Each hAnim In All + If hAnim.Object = hObject Then aStop.Add(hAnim) + Next + + For Each hAnim In aStop + hAnim.Stop + Next + + hAnim = New CAnimation(hObject, sProperty, fTarget, iTime) + All.Add(hAnim) + + If hParent Then Object.Attach(hAnim, hParent, "Animation") + +End + +Static Public Sub Exit() + + While All.Count + All[0].Stop + Wend + +End + + + +Public Sub _new(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer) + + 'Debug hObject.Tag;; sProperty;; fTarget + + $hObject = hObject + $sProperty = sProperty + $fTarget = fTarget + $fTime = Timer + iTime / 1000 + + $hTimer = New Timer As "Timer" + $hTimer.Delay = 50 + $hTimer.Start + +End + +Public Sub Timer_Timer() + + Dim fValue As Float + Dim iSign As Integer + Dim fDiff As Float + + fValue = Object.GetProperty($hObject, $sProperty) + + iSign = Sgn($fTarget - fValue) + fDiff = $fTime - Timer + fValue += ($fTarget - fValue) / (1000 * fDiff / $hTimer.Delay) + + If fDiff <= 0 Or If iSign = 0 Or If iSign > 0 And fValue >= $fTarget Or If iSign < 0 And fValue <= $fTarget Then + Object.SetProperty($hObject, $sProperty, $fTarget) + {Stop} + Else + Object.SetProperty($hObject, $sProperty, fValue) + Endif + +Catch + + {Stop} + +End + +Public Sub Stop() + + $hTimer.Stop + $hTimer = Null + $hObject = Null + + All.Remove(All.Find(Me)) + + Raise Stop + +End + +Private Function Object_Read() As Object + + Return $hObject + +End diff --git a/app/examples/Multimedia/MediaPlayer/.src/CButton.class b/app/examples/Multimedia/MediaPlayer/.src/CButton.class new file mode 100644 index 00000000..37c68705 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/CButton.class @@ -0,0 +1,215 @@ +' Gambas class file + +Export + +Inherits DrawingArea + +Event Click + +Property Image As Image +Property Opacity As Float +Property Highlight As Boolean +Property Shortcut As String +Property Text As String +Property Font As Font + +Public Const MIN_OPACITY As Float = 0.2 +Public Const MAX_OPACITY As Float = 0.8 + +Private $hObs As Observer +Private $hImage As Image +Private $hDraw As Image +'Private $bInside As Boolean +'Private $hTimer As Timer +Private $fOpacity As Float = MIN_OPACITY +Private $bHighlight As Boolean +'Private $hTimer As Timer +Private $sShortcut As String +Private $sText As String +Private $hFont As Font + +Public Sub _new() + + $hObs = New Observer(Me) As "DrawingArea" + '$hTimer = New Timer As "Timer" + '$hTimer.Delay = 50 + Me.Mouse = Mouse.Pointing + '$hTimer = New Timer As "Timer" + +End + +Public Sub DrawingArea_Enter() + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MAX_OPACITY, 250) + +End + +Public Sub DrawingArea_Leave() + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MIN_OPACITY, 250) + +End + +Public Sub DrawingArea_MouseUp() + + If Not Me.Enabled Then Return + Raise Click + Stop Event + +End + +Public Sub DrawingArea_Draw() + + If $hDraw Then + Draw.Image($hDraw, 2, 2) + Endif + + If $bHighlight Then + Paint.Begin(Me) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + Paint.Rectangle(0, 0, Me.W, Me.H) + Paint.Fill + Paint.End + Endif + +End + +Private Function Image_Read() As Image + + Return $hImage + +End + +Private Sub Image_Write(Value As Image) + + $hImage = Value '.Stretch(Me.W - 4, Me.H - 4) + SetOpacity + +End + +Private Sub SetOpacity() + + Dim W, H As Integer + Dim hFont As Font + + If Not $hImage Then Return + + $hDraw = $hImage.Stretch(Me.W - 4, Me.H - 4) + Paint.Begin($hDraw) + + If $sShortcut Then + + Paint.Font = Font["Bold,+2"] + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 64)) + 'For X = -2 To 2 + ' For Y = -2 To 2 + ' Paint.DrawText($sShortcut, $hDraw.W - 16 + X, $hDraw.H - 16 + Y, 16, 16, Align.Center) + ' Next + 'Next + Paint.Arc($hDraw.W - 12, $hDraw.H - 12, 12) + Paint.Fill + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + Paint.DrawText($sShortcut, $hDraw.W - 24, $hDraw.H - 24, 24, 24, Align.Center) + + Endif + + If $sText Then + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 64)) + If $hFont Then + hFont = $hFont + Else + hFont = Font["Bold,+1"] + Endif + Paint.Font = hFont + W = hFont.TextWidth($sText) + 4 + H = hFont.TextHeight($sText) + 2 + Paint.Rectangle(($hDraw.W - W) / 2, $hDraw.H - H, W, H) + Paint.Fill + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + Paint.DrawText($sText, ($hDraw.W - W) / 2, $hDraw.H - H, W, H, Align.Center) + Endif + + Paint.End + + $hDraw.Opacity($fOpacity) + Me.Refresh + +End + +Private Function Opacity_Read() As Float + + Return $fOpacity + +End + +Private Sub Opacity_Write(Value As Float) + + $fOpacity = Max(0, Min(1, Value)) + SetOpacity + +End + +Private Function Highlight_Read() As Boolean + + Return $bHighlight + +End + +Private Sub Highlight_Write(Value As Boolean) + + $bHighlight = Value + Me.Refresh + +End + + +Private Function Shortcut_Read() As String + + Return $sShortcut + +End + +Private Sub Shortcut_Write(Value As String) + + $sShortcut = Value + SetOpacity + Me.Refresh + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + SetOpacity + Me.Refresh + +End + +Private Function Font_Read() As Font + + Return $hFont + +End + +Private Sub Font_Write(Value As Font) + + $hFont = Value.Copy() + +End + +Public Sub DrawingArea_Arrange() + + SetOpacity + +End + diff --git a/app/examples/Multimedia/MediaPlayer/.src/FControl.class b/app/examples/Multimedia/MediaPlayer/.src/FControl.class new file mode 100644 index 00000000..2e0f4c0f --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FControl.class @@ -0,0 +1,133 @@ +' Gambas class file + +Private sldTime As DrawingArea +Private lblTime As DrawingArea + +Private $sInfo As String +Private $iMinOpacity As Integer = 70 +Private $sTitle As String + +Public Sub SetMinOpacity(iOpacity As Integer) + + $iMinOpacity = iOpacity + If Me.Opacity < $iMinOpacity Then + CAnimation.Start(Me, "Opacity", $iMinOpacity, 250) + Endif + +End + + +Public Sub _new() + + panToolbar.H = 48 + Desktop.Scale * 2 + + FMain.CreateButtons(["eject", "video", "config", "play", "pause", "stop", "screenshot", "fullscreen", "subtitle", "visualisation", "-"], panToolbar) + + sldTime = New DrawingArea(panToolbar) As "sldTime" + sldTime.Resize(8, 8) + sldTime.Expand = True + sldTime.Tracking = True + sldTime.Mouse = Mouse.Pointing + + lblTime = New DrawingArea(panToolbar) As "lblTime" + lblTime.Resize(8, 8) + lblTime.Font = Font["Bold,+5"] + lblTime.W = lblTime.Font.TextWidth("99:99:99 / 99:99:99") + 16 + + FMain.CreateButtons(["-", "volume", "quit"], panToolbar) + + Me.H = 48 + Desktop.Scale * 6 + lblTitle.Font.Height + Me.Arrangement = Arrange.Vertical + panToolbar.Arrangement = Arrange.Horizontal + + FMain.GetButton("visualisation").Font = Font["-2"] + +End + +Public Sub lblTime_Draw() + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 128)) + Paint.Font = lblTime.Font + Paint.DrawText($sInfo, 0, 0, Paint.W, Paint.H, Align.Center) + +End + +Public Sub sldTime_Draw() + + Dim fLength As Float = FMain.GetLength() + If fLength = 0 Then Return + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + Paint.Rectangle(0, (Paint.H - 16) / 2, Paint.W, 16) + Paint.Fill + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 128)) + Paint.Rectangle(0, (Paint.H - 16) / 2, Paint.W * FMain.GetPos() / fLength, 16) + Paint.Fill + +End + +Public Sub sldTime_MouseDown() + + If Mouse.Left Then FMain.SetPos(Mouse.X / sldTime.W) + +End + +Public Sub sldTime_MouseMove() + + If Mouse.Left Then FMain.SetPos(Mouse.X / sldTime.W) + +End + +Public Sub SetInfo(sInfo As String) + + $sInfo = sInfo + lblTime.Refresh + sldTime.Refresh + +End + +Public Sub Form_KeyPress() + + FMain.Form_KeyPress + +End + +Public Sub SetTitle(sTitle As String) + + $sTitle = sTitle + lblTitle.Text = sTitle + lblTitle.Foreground = &HBFBFBF& + +End + +Public Sub SetError(sError As String) + + If lblTitle.Foreground = Color.Red Then + sError = Trim(lblTitle.Text & " " & sError) + Endif + + lblTitle.Text = sError + lblTitle.Foreground = Color.Red + timError.Start + +End + +Public Sub timError_Timer() + + SetTitle($sTitle) + timError.Stop + +End + + +' Public Sub Form_Move() +' +' Debug Me.X;; Me.Y;; Me.W;; Me.H;; "->";; Me.Y + Me.H +' +' End +' +' Public Sub Form_Resize() +' +' Debug Me.X;; Me.Y;; Me.W;; Me.H;; "->";; Me.Y + Me.H +' +' End diff --git a/app/examples/Multimedia/MediaPlayer/.src/FControl.form b/app/examples/Multimedia/MediaPlayer/.src/FControl.form new file mode 100644 index 00000000..b32b80ee --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FControl.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,23) + Background = &H000000& + Resizable = False + Border = False + Utility = True + Opacity = 70 + Spacing = True + { Panel3 Panel + MoveScaled(25,0,12,1) + Background = &H3F3F3F& + } + { Panel1 HBox + MoveScaled(1,3,61,6) + AutoResize = True + { Panel2 Panel + MoveScaled(2,0,2,4) + } + { lblTitle Label + MoveScaled(13,1,43,2) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + Expand = True + Text = ("MediaPlayer, a media player example based on GStreamer made by Benoît Minisini") + } + } + { panToolbar Panel + MoveScaled(1,10,57,9) + Margin = True + } + { timError #Timer + #MoveScaled(54,13) + Delay = 5000 + } +} diff --git a/app/examples/Multimedia/MediaPlayer/.src/FMain.class b/app/examples/Multimedia/MediaPlayer/.src/FMain.class new file mode 100644 index 00000000..581ca152 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FMain.class @@ -0,0 +1,616 @@ +' Gambas class file + +'Property TagsX As Integer + +Private $hPlayer As MediaPlayer +Private $hImage As MediaControl +Private $hFilter As MediaFilter +Private $hOutput As MediaContainer +Private $aVisualisation As MediaControl[] + +Private $fPos As Float +Private $fLength As Float +Private $iVisualisation As Integer +Private $fVolume As Float +Private $bSuspend As Boolean +Private $bShowTags As Boolean +'Private $iTagsX As Integer + +Public Sub _new() + + Application.MainWindow = Me + '$iTagsX = - FTags.W + +End + +Private Sub AddVisualisation(sType As String, sTitle As String) + + Dim hVisualisation As MediaControl + + If sType Then + hVisualisation = New MediaControl($hPlayer, sType) + hVisualisation.Tag = sTitle + Endif + + $aVisualisation.Add(hVisualisation) + +Catch + + Error sType; ": "; Error.Text + +End + +Private Sub MakeMediaPlayer() + + $hPlayer = New MediaPlayer As "MediaPlayer" + + '$hOutput = New MediaContainer($hPlayer) + '$hOutput.Name = "MyOutput" + + $hFilter = New MediaFilter($hPlayer) + $hImage = New MediaControl($hPlayer, "ximagesink") + '$hFilter.LinkTo($hImage) + + '$hOutput.AddInput($hImage) + + $hPlayer.Video.Output = $hImage + + $aVisualisation = New MediaControl[] + AddVisualisation("", "") + AddVisualisation("goom", "Goom") + AddVisualisation("goom2k1", "Goom2") + AddVisualisation("libvisual_bumpscope", "Bump") + AddVisualisation("libvisual_corona", "Corona") + AddVisualisation("libvisual_infinite", "Infinite") + AddVisualisation("libvisual_jakdaw", "Jakdaw") + AddVisualisation("libvisual_jess", "Jess") + AddVisualisation("monoscope", "Mono") + AddVisualisation("libvisual_oinksie", "Oinksie") + AddVisualisation("libvisual_lv_analyzer", "Analyzer") + AddVisualisation("libvisual_lv_scope", "Scope") + AddVisualisation("spacescope", "Space") + AddVisualisation("spectrascope", "Spectra") + AddVisualisation("synaescope", "Synae") + AddVisualisation("wavescope", "Wave") + + $iVisualisation = 0 + UpdateVisualisation + +End + +Public Sub GetButton(sKey As String) As CButton + + Return FControl.Controls["#" & sKey] + +End + + +Public Sub CreateButtons(aButton As String[], hParent As Container) + + Dim sKey As String + Dim sImg As String + Dim hPanel As Panel + Dim hButton As CButton + Dim iPos As Integer + + For Each sKey In aButton + + If sKey = "<->" Then + hPanel = New Panel(hParent) + hPanel.Expand = True + hPanel.Resize(8, 48) + Else If sKey = "-" Then + hPanel = New Panel(hParent) + hPanel.Resize(8, 48) + Else + + sImg = sKey + iPos = InStr(sImg, "#") + If iPos Then sImg = Left(sImg, iPos - 1) + + hButton = New CButton(hParent) As "Button" + hButton.Resize(48, 48) + hButton.Image = Image.Load(sImg & ".png") + hButton.Tag = sKey + hButton.Name = "#" & sKey + 'If cTooltip Then hButton.Tooltip = cTooltip[sImg] + 'If $cShortcut Then hButton.Shortcut = $cShortcut[sImg] + '$cButton[hButton.Tag] = hButton + Endif + Next + +End + + +Public Sub Form_Open() + + MakeMediaPlayer + + FControl.Show + FControl.Raise + + RefreshVolume + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Escape Then Action("quit") + +End + +Private Sub GetDevice(sName As String) As String + + Try Return Scan(sName, "* (*)")[1] + +End + +' Private Sub FindChildFromType(hCont As MediaContainer, sType As String) As MediaControl +' +' Dim hCtrl As MediaControl +' +' For I = 0 To hCont.Children.Count - 1 +' hCtrl = hCont.Children[I] +' If hCtrl.Type = sType Then Return hCtrl +' Next +' +' End + +Private Sub Dump(hCont As MediaContainer, Optional iIndent As Integer) + + Dim I As Integer + Dim hCtrl As MediaControl + Dim sOutput As String + + If iIndent = 0 Then + Print "Source: "; $hPlayer.Input.Name + Print "[-------------------------------------------" + Endif + For I = 0 To hCont.Children.Count - 1 + hCtrl = hCont.Children[I] + Print Space$(iIndent * 2); hCtrl;; hCtrl.Name;; "["; hCtrl.Type; "]";; "=>";; hCtrl.Parent.Name + + For Each sOutput In hCtrl.Inputs + With hCtrl.GetLink(sOutput) + If Not .Peer Then Continue + Print Space$(iIndent * 2); "| "; sOutput; " <--- "; .Peer.Name; "."; .Output + End With + Next + + For Each sOutput In hCtrl.Outputs + With hCtrl.GetLink(sOutput) + If Not .Peer Then Continue + Print Space$(iIndent * 2); "| "; sOutput; " ---> "; .Peer.Name; "."; .Input + End With + Next + + If hCtrl Is MediaContainer Then + Dump(hCtrl, iIndent + 1) + Endif + Next + If iIndent = 0 Then Print "-------------------------------------------]" + +End + +Private Sub Action(sAction As String) + + Dim fPos As Float + Dim iState As Integer + Dim sName As String + Dim iName As Integer + Dim sVideo As String + + Select sAction + + Case "eject" + Dialog.Title = ("Select a media file") + If Not Dialog.OpenFile() Then + FControl.SetTitle(File.Name(Dialog.Path)) + Action("stop") + $hPlayer.URL = Media.URL(Dialog.Path) + $hPlayer.Subtitles.Enabled = False + If Exist(File.SetExt(Dialog.Path, "srt")) Then + $hPlayer.Subtitles.URL = Media.URL(File.SetExt(Dialog.Path, "srt")) + $hPlayer.Subtitles.Enabled = True + Endif + UpdateSubtitle + FTags.Clear($hPlayer) + + Action("play") + + Endif + + Case "video" + + sVideo = FTags.GetVideoDevice() + If sVideo Then + FControl.SetTitle(("Video device") & " " & sVideo) + Action("stop") + $hPlayer.Subtitles.Enabled = False + $hPlayer.URL = "v4l2://" & GetDevice(sVideo) + UpdateSubtitle + FTags.Clear($hPlayer) + Action("play") + Endif + + Case "info", "config" + If $bShowTags Then + CAnimation.Start(FTags, "Opacity", 0, 250, Me) + 'CAnimation.Start(Me, "TagsX", - FTags.W, 250) + Else + FTags.X = - FTags.W + FTags.Show + CAnimation.Start(FTags, "Opacity", 70, 250) + 'CAnimation.Start(Me, "TagsX", 0, 250) + Endif + $bShowTags = Not $bShowTags + + Case "subtitle" + 'If $hPlayer.State = Media.Playing Or If $hPlayer.State = Media.Paused Then + ' $hPlayer.Subtitles.Enabled = Not $hPlayer.Subtitles.Enabled + ' UpdateSubtitle + 'Else + Dialog.Title = ("Select a subtitle file") + If Not Dialog.OpenFile() Then + $hPlayer.Subtitles.URL = Media.URL(Dialog.Path) + $hPlayer.Subtitles.Enabled = True + UpdateSubtitle + Endif + 'Endif + + Case "play" + SuspendScreenSaver + $hImage.SetWindow(dwgVideo) ', panLeft.X + panLeft.W + 8, panLeft.Y + 8, Me.W - panLeft.X - panLeft.W - 16, Me.H - panLeft.Y - 16) + EnableVideoFilter + Sleep 0.1 + Try $hPlayer.Play + If Not Error Then + dwgVideo.Mouse = Mouse.Blank + timTime.Start + Dump($hPlayer) + Else + ResumeScreenSaver + Try $hPlayer.Stop + $fLength = 0 + Endif + + Case "stop" + ResumeScreenSaver + Try $hPlayer.Stop + $fLength = 0 + timTime.Stop + dwgVideo.Mouse = Mouse.Default + + Case "pause" + ResumeScreenSaver + $hPlayer.Pause + dwgVideo.Mouse = Mouse.Default + timTime.Stop + + Case "fullscreen" + Me.FullScreen = Not Me.FullScreen + Me.Maximized = Not Me.FullScreen + Form_Resize + + Case "volume" + $hPlayer.Audio.Mute = Not $hPlayer.Audio.Mute + RefreshVolume + + 'Case "balance" + 'FBalance.Visible = Not FBalance.Visible + + Case "visualisation" + + iState = $hPlayer.State + If iState <> Media.Null And If iState <> Media.Ready Then + fPos = $hPlayer.Position + $hPlayer.Stop + $hPlayer.Close + timTime.Stop + 'FadeOut + Endif + + Inc $iVisualisation + If $iVisualisation >= $aVisualisation.Count Then $iVisualisation = 0 + UpdateVisualisation + + If iState <> Media.Null And If iState <> Media.Ready Then + $hPlayer.Pause + $hPlayer.Position = fPos + $hPlayer.State = iState + If iState = Media.Playing Then timTime.Start + 'FadeIn + Endif + + Case "quit" + Me.Close + + Case "screenshot" + + Do + Inc iName + sName = "~/MediaPlayerScreenshot" + If iName > 1 Then sName &= "-" & CStr(iName) + sName &= ".jpg" + If Not Exist(sName) Then Break + Loop + Try $hPlayer.Video.Image.Save(sName) + If Error Then FControl.SetError(Error.Text) + + Case "seek-forward" + FControl.Y = Screen.H - FControl.H + + Case "seek-backward" + FControl.Y = 600 + + End Select + +End + +Public Sub Button_Click() + + Action(Last.Tag) + +End + +' Public Sub Form_Arrange() +' +' panToolbar.Move(0, Me.H - panToolbar.H, Me.W, panToolbar.H) +' +' End + +Public Sub timTime_Timer() + + $fPos = $hPlayer.Position + If $fLength = 0 Then $fLength = $hPlayer.Duration + FControl.SetInfo(Format(CDate(($fPos + 0.5) / 86400), "hh:nn:ss") & " / " & Format(CDate($fLength / 86400), "hh:nn:ss")) + +End + +Public Sub GetLength() As Float + + Return $fLength + +End + +Public Sub GetPos() As Float + + Return $fPos + +End + +Public Sub SetPos(fPos As Float) + + If $hPlayer.State = Media.Paused Or If $hPlayer.State = Media.Playing Then + If $fLength Then + $fPos = fPos * $fLength + '$hPlayer.Pause + 'FadeOut + $hPlayer.Position = $fPos + '$hPlayer.Play + 'FadeIn + Endif + Endif + +End + +Public Sub Form_Resize() + + 'Debug Me.X;; Me.Y;; Me.W;; Me.H;; FControl.H;; Me.Y + Me.H - FControl.H + 'Debug "FControl.Move:";; Me.X;; Me.Y + Me.H - FControl.H;; Me.W;; FControl.H + FControl.Move(dwgVideo.ScreenX, dwgVideo.ScreenY + dwgVideo.H - FControl.H, dwgVideo.W, FControl.H) + FTags.Move(dwgVideo.ScreenX, dwgVideo.ScreenY, FTags.W, dwgVideo.H - FControl.H) + +End + +Public Sub Form_Close() + + ResumeScreenSaver + CAnimation.Exit + +End + +Public Sub Button_MouseWheel() + + $hPlayer.Audio.Mute = False + + If Mouse.Delta > 0 Then + $hPlayer.Audio.Volume = Min(1, (Sqr($hPlayer.Audio.Volume) + 0.05) ^ 2) + Else + $hPlayer.Audio.Volume = Max(0, (Sqr($hPlayer.Audio.Volume) - 0.05) ^ 2) + Endif + + RefreshVolume + +End + +Private Sub RefreshVolume() + + Dim sImage As String + Dim fVolume As Float + + With GetButton("volume") + + If $hPlayer.Audio.Mute Then + sImage = "mute" + .Text = "" + Else + fVolume = Sqr($hPlayer.Audio.Volume) + .Text = Format(fVolume, "0%") + sImage = "volume-" & Min(3, CInt(fVolume * 4)) + Endif + + .Image = Image.Load(sImage & ".png") + + End With + +End + +Private Sub UpdateSubtitle() + + GetButton("subtitle").Text = If($hPlayer.Subtitles.Enabled, "ON", "") + +End + +Private Sub UpdateVisualisation() + + Dim hVis As MediaControl = $aVisualisation[$iVisualisation] + + $hPlayer.Video.Visualisation = hVis + If hVis Then + GetButton("visualisation").Text = hVis.Tag + $hImage.SetWindow(dwgVideo) + $hPlayer.Position = $hPlayer.Position + Else + GetButton("visualisation").Text = "" + $hImage.SetWindow(Null) + Endif + +End + +Public Sub dwgVideo_Draw() + + If $hPlayer.State = Media.Null Then + Draw.Font = Font["+16"] + Draw.Foreground = Color.Gray + Draw.RichText("Gambas Almost Means Basic!", 0, 0, dwgVideo.Width, dwgVideo.Height, Align.Center) + Endif + +End + +Public Sub MediaPlayer_End() + + Action("stop") + +End + +Public Sub MediaPlayer_Message((Source) As MediaControl, Type As Integer, Message As String) + + Select Case Type + Case Media.Info + Print "(i)"; + Case Media.Warning + Print "/!\\"; + Case Media.Error + Print "[*]"; + End Select + Print " "; Message + + If Type = Media.Error Then + FControl.SetError(Message) + Action("stop") + Endif + +End + + +Public Sub MediaPlayer_Tag(TagList As MediaTagList) + + Dim sTag As String + Dim vTag As Variant + Dim I As Integer + + For Each sTag In TagList.Tags + vTag = TagList[sTag] + If TypeOf(vTag) = gb.Object And If vTag Is Array Then + For I = 0 To vTag.Max + FTags.AddTag(sTag & "[" & CStr(I) & "]", Str(vTag[I])) + Next + Else + FTags.AddTag(sTag, Str(vTag)) + Endif + Next + +End + + +Private Sub FadeOut() + + $fVolume = $hPlayer.Audio.Volume + + Do + Debug $hPlayer.Audio.Volume + $hPlayer.Audio.Volume = Max(0, $hPlayer.Audio.Volume - 0.05) + If $hPlayer.Audio.Volume = 0 Then Break + Sleep 0.01 + Loop + +End + +Private Sub FadeIn() + + Do + Debug $hPlayer.Audio.Volume + $hPlayer.Audio.Volume = Min($fVolume, $hPlayer.Audio.Volume + 0.05) + If $hPlayer.Audio.Volume >= $fVolume Then Break + Sleep 0.01 + Loop + +End + +Private Sub SuspendScreenSaver() + + If $bSuspend Then Return + Desktop.ScreenSaver.Suspend(Me) + +End + +Private Sub ResumeScreenSaver() + + If Not $bSuspend Then Return + Desktop.ScreenSaver.Resume(Me) + +End + +Public Sub Form_Enter() + + If $hPlayer.State = Media.Playing Then + CAnimation.Start(FControl, "Opacity", 0, 400) + CAnimation.Start(FTags, "Opacity", 0, 400) + Endif + +End + +Public Sub Form_Leave() + + If $hPlayer.State = Media.Playing Then + CAnimation.Start(FControl, "Opacity", 70, 250) + If $bShowTags Then CAnimation.Start(FTags, "Opacity", 70, 250) + Endif + +End + +Public Sub Animation_Stop() + + FTags.Hide + +End + +' Private Function TagsX_Read() As Integer +' +' Return $iTagsX +' +' End +' +' Private Sub TagsX_Write(Value As Integer) +' +' $iTagsX = Value +' FTags.X = Me.X + $iTagsX +' +' End + +Public Sub SetBalance(iIndex As Integer, iValue As Integer) + + $hPlayer.Balance[iIndex].Value = iValue + +End + +Private Sub EnableVideoFilter() + + 'If $hPlayer.URL Begins "v4l2://" Then + ' $hFilter.Filter = "video/x-raw,width=640,height=480,framerate=30/1" + 'Else + ' $hFilter.Filter = "video/x-raw" + 'Endif + +End diff --git a/app/examples/Multimedia/MediaPlayer/.src/FMain.form b/app/examples/Multimedia/MediaPlayer/.src/FMain.form new file mode 100644 index 00000000..673e70e3 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FMain.form @@ -0,0 +1,18 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,110,85) + Icon = Picture["icon.png"] + Border = False + FullScreen = True + Arrangement = Arrange.Vertical + { dwgVideo DrawingArea + MoveScaled(12,27,24,24) + Background = &H000000& + Expand = True + } + { timTime #Timer + #MoveScaled(85,47) + Delay = 200 + } +} diff --git a/app/examples/Multimedia/MediaPlayer/.src/FTags.class b/app/examples/Multimedia/MediaPlayer/.src/FTags.class new file mode 100644 index 00000000..130275cc --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FTags.class @@ -0,0 +1,147 @@ +' Gambas class file + +Private $cTags As New Collection +Private $aTags As New String[] + +Private btnResetBalance As CButton + +Private Sub GetBalanceName(sName As String) As String + + Select Case LCase(sName) + Case "hue" + Return ("Hue") + Case "saturation" + Return ("Saturation") + Case "contrast" + Return ("Contrast") + Case "brightness" + Return ("Brightness") + Case Else + Return sName + End Select + +End + +Public Sub Clear(hPlayer As MediaPlayer) + + Dim I As Integer + Dim hLabel As Label + Dim hSlider As Slider + Dim H As Integer + + $cTags.Clear + $aTags.Clear + gvwTags.Rows.Count = 0 + + panBalance.Children.Clear + + For I = 0 To hPlayer.Balance.Count - 1 + + With hPlayer.Balance[I] + + hLabel = New Label(panBalance) + hLabel.Text = GetBalanceName(.Name) + hLabel.Foreground = lblBalance.Foreground + hLabel.ResizeScaled(16, 4) + + hSlider = New Slider(panBalance) As "sldBalance" + hSlider.ResizeScaled(4, 4) + hSlider.Expand = True + hSlider.MinValue = .Min + hSlider.MaxValue = .Max + hSlider.Value = .Value + hSlider.Tag = I + + H += Desktop.Scale * 5 + + End With + + Next + + panBalance.Parent.H = H + lblBalance.H + Desktop.Scale * 4 + panBalance.Parent.Show + +End + +Public Sub AddTag(sTag As String, sValue As String) + + $cTags[sTag] = sValue + + If Not $aTags.Exist(sTag) Then + $aTags.Add(sTag) + $aTags.Sort + gvwTags.Rows.Count = $aTags.Count + gvwTags.Columns[0].Width = -1 + Endif + + gvwTags.Refresh + +End + + +Public Sub gvwTags_Data(Row As Integer, Column As Integer) + + If Column = 0 Then + gvwTags.Data.Text = $aTags[Row] + Else + gvwTags.Data.Text = $cTags[$aTags[Row]] + gvwTags.Data.WordWrap = True + Endif + +End + +Public Sub _new() + + Dim sFile As String + + gvwTags.Columns.Count = 2 + + btnResetBalance = New CButton(panLabel) As "btnResetBalance" + btnResetBalance.Resize(panLabel.H, panLabel.H) + btnResetBalance.Image = Image.Load("undo.png") + + cmbVideoDevice.Clear + If Exist("/sys/class/video4linux") Then + For Each sFile In Dir("/sys/class/video4linux") + cmbVideoDevice.Add(Trim(File.Load("/sys/class/video4linux" &/ sFile &/ "name")) & " (/dev/" & sFile & ")") + Next + Else + cmbVideoDevice.Add(("No video device")) + cmbVideoDevice.Enabled = False + Endif + +End + +Public Sub Form_Resize() + + panTags.Move(0, 0, Me.W - Desktop.Scale, Me.H) + +End + +Public Sub sldBalance_Change() + + FMain.SetBalance(Last.Tag, Last.Value) + +End + + +Public Sub btnResetBalance_Click() + + Dim hCtrl As Control + Dim hSlider As Slider + + For Each hCtrl In panBalance.Children + If hCtrl Is Slider Then + hSlider = hCtrl + CAnimation.Start(hSlider, "Value", 0, 250) + Endif + Next + +End + +Public Sub GetVideoDevice() As String + + If cmbVideoDevice.Enabled Then Return cmbVideoDevice.Text + +End + diff --git a/app/examples/Multimedia/MediaPlayer/.src/FTags.form b/app/examples/Multimedia/MediaPlayer/.src/FTags.form new file mode 100644 index 00000000..4c47b166 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FTags.form @@ -0,0 +1,91 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,82,82) + Background = &H3F3F3F& + Resizable = False + Border = False + Utility = True + SkipTaskbar = True + Opacity = 0 + Spacing = True + Margin = True + { panTags Panel + MoveScaled(2,3,75,78) + Background = &H000000& + Arrangement = Arrange.Vertical + { Panel2 VBox + MoveScaled(1,1,59,29) + Expand = True + Spacing = True + Margin = True + { Label1 Label + MoveScaled(0,0,47,5) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + Text = ("Information") + } + { gvwTags GridView + MoveScaled(2,7,56,21) + Font = Font["+2"] + Background = &H000000& + Foreground = &HBFBFBF& + NoTabFocus = True + Expand = True + Border = False + Grid = False + } + } + { Panel1 Panel + MoveScaled(2,31,18,1) + Background = &H3F3F3F& + } + { VBox1 VBox + MoveScaled(3,33,59,7) + Spacing = True + Margin = True + { panLabel HBox + MoveScaled(0,0,57,5) + { lblBalance Label + MoveScaled(1,0,47,5) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + Expand = True + Text = ("Balance") + } + } + { panBalance HPanel + MoveScaled(0,5,57,4) + Font = Font["+2"] + Expand = True + Spacing = True + Indent = True + } + } + { panVideo VBox + MoveScaled(3,59,68,8) + { Panel3 Panel + MoveScaled(0,0,18,1) + Background = &H3F3F3F& + } + { VBox3 HBox + MoveScaled(1,1,66,7) + Spacing = True + Margin = True + { lblBalance2 Label + MoveScaled(0,0,24,5) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + AutoResize = True + Text = ("Video device") + } + { cmbVideoDevice ComboBox + MoveScaled(35,0,24,5) + Font = Font["+2"] + Expand = True + ReadOnly = True + } + } + } + } +} diff --git a/app/examples/Multimedia/MediaPlayer/.src/MTest.module b/app/examples/Multimedia/MediaPlayer/.src/MTest.module new file mode 100644 index 00000000..3cf3c627 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/MTest.module @@ -0,0 +1,67 @@ +' Gambas module file + +Private $hPlayer As MediaPipeline +Private $bStop As Boolean + +Public Sub Main() + + Dim hSrc As MediaControl + Dim hFilter As MediaFilter + Dim hSink As MediaControl + Dim I As Integer + + $hPlayer = New MediaPipeline As "MediaPlayer" + + hSrc = New MediaControl($hPlayer, "v4l2src") + 'hSrc["norm"] = "SECAM" + Print hSrc["norm"] + + hFilter = New MediaFilter($hPlayer) + + hSink = New MediaControl($hPlayer, "xvimagesink") + + hSrc.LinkTo(hFilter) + hFilter.LinkTo(hSink) + + hFilter.Filter = "video/x-raw,width=640,height=480,framerate=30/1" + + $hPlayer.Play + + Print ("I will take a screenshot in 5 seconds...") + Repeat + Wait 1 + Inc I + If I <= 5 Then + Print "..."; I; + Flush + If I = 5 Then + Print + hSink.GetLastImage().Save("~/screenshot.png") + Print ("Screenshot saved to '~/screenshot.png'!") + Endif + Endif + Until $bStop + +End + +Public Sub MediaPlayer_Message((Source) As MediaControl, Type As Integer, Message As String) + + Select Case Type + Case Media.Info + Print "(i)"; + Case Media.Warning + Print "/!\\"; + Case Media.Error + Print "[*]"; + $bStop = True + End Select + Print " "; Message + +End + +Public Sub MediaPlayer_State() + + Debug Last.State + +End + diff --git a/app/examples/Multimedia/MediaPlayer/brightness.png b/app/examples/Multimedia/MediaPlayer/brightness.png new file mode 100644 index 0000000000000000000000000000000000000000..f3621e244a2516609358815cefb1aed6d0f8d7af GIT binary patch literal 1534 zcmVTp$#z;vE7PZjKt1i`lc|6Y z^R9+B&T>|8LJ%0?6Azz2oxm$22$suzP#>?ofDKG43Xz8ZZle|F1NZd&5V{eXOt)br z#sMUv1xqxA2yz0$x^@uxSb>?Cz$N?~xbL-%i7ex7sZ|kv$08;I0N)ka+lkm$Ecd}X z0Kg<}WdkEpejh(zf*1G%{%ASDx_Oujc~SfAWr|K9$bW=hJM&o1>y|doc%Z=j^dv`g zco3}gR*5WigHL9!7dxZOHW0|>s5b(AOg18_U9uT-wB${+;I0kPAJA|3@5gBy%~7=A zmiW!a^BxD_7PANjS;((>$JCHDhW3NtDw9r>UvV>o1ZT*Rd!I9bk2Sqk$#@n^_;)P7 zhn0NN1P_9kW_Y25OAIa+@N$zwe zP#`q7xynWKJeu#ZL}Y*JBg%@HocNgC3kkvxDwyA{oJ-a27Ud zRPQOSrhPfAW0z^lb+VT8E%oPeOsJiLo5=n3p2^q@);M_l2&qkg(t}Y78txjl7k-L0WJ`Gj4IvG z8V-AcC&?kM*6ZyN*%J)AgNL|}mqeXyF*7rH*)u#zcJR?y&BYv-v-k25d+<4|Gpyje zriY!Aalw;hz1|0n#)1fInQaEoVvRZEm&Hshw1tAD71ZflJwHW#a#Z;yOK&T882lE|Eru={#m$ zjI`arJVxV&AJTV}MdrdND=n^bIGdkxNY=ZdpA9PFCU8g}fHNwyI*a94C!uUeUXK|G zS&xyeTwtc%h9m?xVV;PU)p|Q}%`??j`lQ2~v?rcmt|#x6FMU=^j`XbOP3bdIt*7Lo zGB5B+;&$V_62lkpf-8J8bCgia=t9|_dJk;iD5bSa4`8^)4gM1%+Jy6F`06M zil;1COhdnRI6Z|o9EfU%Q=lK0(d2kIZ4k%PA!Lh7Kdbf^2(pDXt9kt2OLA8!$8+_L zXqlE6#cxW{MjXeRrnOQv_W}-JgOd9zq((1uG~j+kbDQ4jzAlZ8DRAo#FH({&w-b(siE-C|KsCD9}C6e%#p3lr4!GbY_U$gn=kIKXth{TcEq(atarsi&=&oc4BKp2$+eKt$NULi2U~ z4xNC-b$ueYu+gx)xW)Ze@OBRtDJa4s=Ld`HSd0+>>FB^bO(BB$fvB!6KUnzA0f6Z& k;W-X*n7?y7Q~lcc52WTH)a{vGU07*qoM6N<$f-MH;`Tzg` literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/config.png b/app/examples/Multimedia/MediaPlayer/config.png new file mode 100644 index 0000000000000000000000000000000000000000..b4248ceb8dc1bb60b208bca3d79c15d328de2f9b GIT binary patch literal 831 zcmV-F1Hk-=P)8RH)C42qCIXM6?OYh;&gwL9~iCZKQ=1VpOTl_p+N~^ zq^n@Ae$Gv56Yk~&J7Gm`GRK+K=$#D91nuN6s9<$ z^@9eog^!f@>>eP*VJ(~n+_t(OqKO_RJ@A)9IhWXFDuUEdi{#6Uu`u(}C;`F4D?=X2 zyqO6R$wh9O<2p~B8&`uh8E+u%hTJddz7Pn=kV&9Tr_@-IX6Ag!y(rD*phPFnkiYxl z9w20j1v;6AJnI{Qh$&|2sm)vB%h#gxcxUzs1iZxtfi(9YS@mB=Ae{>GNe7^q?J?~X zKLMCyoX<&qbFL6L#B+ivYZO7~DkC;8qGxXunGyBw~0L!eaIa=$kO z)YHh$a@9Y>4FcAOT_Sn5}>hq6Y%hJY?_l|tGfx(ftslJE*A zl|p}t%RIBuA7y7u8sUA)r`QlUuDtpuYzf@rLQHz0P@dT&VP?un2C|eb9TD&s8wA!O zfxpU=)h!(n=uw{B?G1sa3@c3?HeR zE+QmZQg5cCr`%N^A0Rs7H? z;bRu%qrZut+8w_oNB7IPk(;=Q;0g~B4><>;C(l6e4cU5#YqGj5<`%xiL2E(G=ZCfY z$u8kFFZ#!2%YG)aoZ7`vhgb6tMUwq?$o5?HwY;WVony?_O&Pm+!!arS2>@d}CxPRv zcB40VR=4?)6}B!m^LM(NzYj9WOA^%QvO2O=*H5xufDOLxwzvcm0Isu7%bia83XW;l zZWX{HzDqCjKP?}Xun{Kh0y=fw2?0`kj|@owcv1rQN%}Iq?oYBxKn^gU1rbmsf34$; zYXL9n@)rbPTaK}xFHnGuh-}vaQf66Ntoh41lH;ul7Z8^|xM0%XEe1w3?#}D}-~t?j zx^Y3kVKYC+)mFnA&PF2OPPwb?S`aW`=D!dP-}FTvTmaas!RG<6QV;2KR-;?8w?m-` zXxHF5Rw%USePN&VSw4s|AA5(wtXC%$S`?bi+$gRH0nyn2#&xDyp;1@RDsPEU05PC5 zjSBTs|-H8`#2391>P2SS#|hRazM-Jusz{2?gs2`KMJH z&Z#oDX(p5^@#Xz5rnDUWcg#dgn$XEE|@3qQ5)1OA@ zcArkpqhA@L+6UXLnpvEROu$y1yl9NWz9p-+whg!OdL$c?8o8zI0u+EAQ;@<_mbgD~ zG?Mpkm2okXS)Ux!;!GUp)zCcsjhX57v?T`dKAz>p-I7UYXD5kkq z01fW(h|&9{Z(v5kc4mDZ_7KgWBSdrm5<@gA?Xj{x*8sLXNcCmjAeDasF@7Z>?m;R5 z;AXi@4RDpCqr$>;tM%cNB-L#wo#-ojGvKNytIm=7IkRAO81UV zgz}-~S7jVS?3U8MgfZ0000N7)M%hhqo_GlL0e)ANlyV0EG38pJs1lTJs63Ig1vbVFV%w- z^&mZYFzKbGRG})gJ@^MyB;X;@h^dmKYyN07*^h_K?tI^-K>3*)_3qiv@&U0NYGv={0X~to73WjXg20108Zo&k8sAU zag3&E@j^5)W?MK-Bp-Z~i)L+cw#Y0n0PNe@c&8>N>HU@Iu76VO!|oliBd=d1{vUmC~H? zLX`5GZQ&{FXToo?)9odS^TG>J%q82xAe*Mcr=;6+6y=QTb&mc7?9Vk{UPI}!(O)) z(_&9XPBl(j8cQtW4F5WZ5Ah?_p||(+vd)N@uLd<}sg^mSJ<)*v&{c0dT?Z7=5kdz5 z^mFK){Sr&K8VcOk?<_X)#as&&5AY}Rq^Ws8M^qpB=-Z-0Q-hG09&4e0e@r(Ncf>5y zmnsdyxK?<>lGqWnODBt*CgT{LW;Zk^yI`YlX+S%j=0Yj1of-_njY$I)e*9M`>w&+6 zpfYKo$d6w-$4k;^@GWK%(?gv6R1tg}OB#6O$6siz zbNo6P=Mz1WB)&G+3N3k@3C(tuvCDqpQlHF*5#3j9ZvQ7Q-BX>Hh)7;S?dJY4B%y;| zB8YwneMNLU)zCg(h6W#{HC>ym;wOfj!-qJ_%7B_O_b^X2s#3OR+nH)akJRl=gg)N> a*Ln*hs%v809a`i70000_}6?;+-5iRkg zL`nhyp;SD0@Zza>G1>+jYoCYBFL!scGrP&AYQBN6%gny-H}l?nGqVE&hShl^7Fi}Z zOsr3Abv{wyg(aS?a!8gW3cQsAUi4$~Arg8Kk!6WDc}H3bu5&E1#Ghi$Enqq_`ajrO zMJPWQwXe#2?i1@llFiF?E8%FAU(E#Ngg$+8?JB6dQ!pUxP&LeQg)Q##5`fcOW!xZa zl?9eP;&g#C1sftF@=*|Ezf_H9+E>}@ier?5SjTsgG2@{fC*_Lc1+5wg=FFp7y_5ph z0ayz}Z4yUWggG(@`OpIV;wS|aD1;{0i8F8?Pzso(z# z5Fgy;D3IZy=?M7EUtQP{)KhZ%*1$jG19N1tcx^7AS3Xdp)U|#4*1#0^xYJ%j1x8>W zcuE||a3Z9Dz&>Cv-0hWPu(|LZ=#dW;d2dX=LQ$)qG6i*FfW2~BC&h%is|PlzP-{F3 zfqdWw7Z4sb?$&r?JO(!We4vHlzQBnovTX5~a!3NdIGR3?;GE&cWT{bpetx19(CGtl zO2&q?LXw&zju*6QU^0Loo@7$1pHe`*<2#`h%8A{sI9|~7f#C3qqZBaDd}wl=I71)k z`+C4Hj@Ei040_@N{e+eegu&1U{$3wQa~>g2B_u(LX-3HN%2dEJ_5hbdze0b&abTwL z*W`vW1@;3bix6-0#0P>+#tp4rN&%Yy`~;xPH)Hy0V^>`F5u>buTcm;K0IZTGXSgwW z!WRH;GJ~b_rs==k7Yypire?*R0^V^Z!XI9-&e|946f8tY@5Ls&q#W77Wl?r|h#Jh% mt=Hl8zCI59YL~<7!QeODA$j&oiky%D0000c^-+qOMoWW66vOH$nce$T9 z0mpJBVV$NaPlDwbJpyOFj}L5V82LFr=yH=%_cYkU?;uJ9&@BsK0W3hK_0+dqZQE{% z>GS}-^t27I8G2%T3XB8zQ*eVp`siwE>F*rC1)ec(asL@8KP|u=W)uJ{r3X-}_xwCj|JG@MRaO%+3@yT&W&vEL z{BTF}6x`!h=vgH5lE|ZO0rAlO?6t%w}6*-V>)I}1KlbUvbiRhaw1j%bgD2WpffaIho kYf@KkU6pw$#NXxp1%f`{@Ox$XPXGV_07*qoM6N<$f|Xbe#sB~S literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/gamma.png b/app/examples/Multimedia/MediaPlayer/gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2cefd4a4fea964560dca5c37b1f9b28fd591aa GIT binary patch literal 693 zcmV;m0!safP)6INX4#5Pc|g6Y5|)oj-7<5~jb6I(h|<>W5lU&;0~m@? z=jP)QKP;twp{BXu4K~ME<;Ej{*aHlCVItwhGe)uOvS9n1&&3=V1f`bNa?b)lBlk

    }2e9Ov(6AVbpkamGdlF7kjJedK(sov;DWN-NMqmoe8#CrJSE zJmoQuYy!+{n-OQYVvHJr%N%0{_~83`W#0uUBi^~>Y!|%%ytW7MI~Z5Z90amV1_4l7 z=STpr!UI^(o-zQ+EJsZyqMFGKsqLeH0O0pt~=W6?V^io++c#ZrFUMSnhp+|HPGUK`mOXQ z{Ahp%2egR+)`VdHl?G=x2F@sLOa*SAr`;JYj#RI~KzpVyw0#kf934=frmS0k*Ijj} bScg9WsNVZDMo|oq00000NkvXXu0mjfq5LxQ literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/icon.png b/app/examples/Multimedia/MediaPlayer/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d321ebccd32db289220ce9d45efcac9476a02086 GIT binary patch literal 2509 zcmV;;2{QJHP)nv46CsojO{#Y2B(b z&UE@m?X-51GHMo!6|$)Rx$ow|>cR#kE&EwW7{g zYnDTWS#8|R9HwaslRlp36a%c_yP(Hv_~;T6s0>f36Ls2hvC1wAIdfIHoI;eaQOW{L zdNEPF%3%Q7(I~S!YcC0bZ?0Rnx6~%6Zypr=4HW@B8lb{`WAz z9PQj1o|`;@fY6F7tt$OCswmXKB*BC1VGw{4YW<&dvEQ$Euue3uLc{r*1pJI%W0h&D z*tt>`QbM6Voh2mb$Hd_X@A>{WA^rNlP+m>LU^#Yl>4F2FO^|?_(b^3sR?GYXdo|1D ze1^pVIYEK87`rW@B_}#66`VPWWQbiS&tiT3TQO*!tJDWPS+Nie{DLKnrtS=Dt z%#Z>Llb`V}JpfGQTRyzS4*%EHTn&IEL_go542=Q4F12&IU7L2w@tLWGbg_Hlw%E?8g6hzNIBAJM3q4;&8&l#2izZNkC9;(fNT5r_Hw5sMg5 zNev26DBhredCcWXD!2>j3w<>0y%LIDfW$xn8$qsiC87XOUhJ-Rmd3Vactvg6>r=YbL89=JP==dzX$ zt5#KAg}Z_}%-{+%kAH|0Sb&L+q=X9cym57Nj1b@;fEy_A>@oS%Th$g`N+AGx%~o+6 zD=`@%pRas-gtZLNz|E9;7Dc@WBYhn!J*=bDYf*G^jM0>lZ1wylHGEi|mII*l z2o?&HsnoO0w=c%vB0ap#8GmWp&9feipp0f3eeXSNLqj0YxRIy0=(pst1%ZY{7oLDH zRskHx8{dThJp6}dG}dF2=iA<*8NB@go~6KV+084ST~W@{fqovR!?O#aeB6Kn!U4f7 z4$zk&b-zS%3vc+>Pw{v1Jw;~mAkX?OzoMQp+}-0I{>?j{{VRULb_AdyGAf`P;4x;i zpBDeqP7giwAbFTaxypAw%kMnB6pDLT?zar_guizvUhp#qcJUV!3N3&e%mK$iFppoe z)9c(VFHqvwU*u^N5JIPeE~;@eTks-J2B!C6YP_+Q@e057?JeBFYzR#MAc5XCrHSdl zJs3jgXuSjv~HvaHu|01 z?pjp9$MqPs2ue6Y*Pb&gGlT%_9_5tdkO&Y+?tHS}!CsOrT59v>po}n^n86hoe8r3`BQIdigl9!s4yZts7H}`K(1BZ6#30vkidV6S zPzb~WyJIqdPLynA0tDPY0UYHKB)7APBMei)R2K0ZFpnPqiwN@xtrUMjQDEN;AMK4C4M4u7CssS(QjrU@QfrUMj^< zR8WM)Heh;EH+g9tE1-a3&(AG`9sz|&O7e^Z#zZ>n+s@F)=UGKghM&R~%6T|LXfjX3 zY@0S?X{dOd?J3dw26rSaR%1X#+(;1%c>;IG4KqI}qf(dRGzDz;+?r%FldcpCd5TWn zqKZwNq!EoDQp8b?(?~Z^$kRxQ*~|58;N`&b?&QjW+|vo54?!`C2bjw;Hgk$Drn8Dp z1j7sw#RMvNh&Oq19Qm^<;FkF*H+{jfpC+zh4?9?b&0Z3G04S<}A)@qg7)c9ZmH-L- zBVd{a8MkBCrI&D40`8-_eJI+~G)n-jfv2X5SimD^4vcj){-Yk1v95qODd8VjDQ%gz z_>Akos8gnQ8ERaMTW`oXJx9M)t`JQ85SDvQNVa6wAV|P)LI~yW1+#MkC+K0kU}XI- z2{YoR!zckkm^>98O%eqd}IezstYLqk%4h5$^L zK!^}|M3mtwF!A$lb}HFv02*Nw4mOrcK%%3gD~V8q150OW2dRmaS`&d=Ohh1DJ1;Qm@yPv)fX6^jsrU~mm+0r%m zhfzcv;e{aLeZqR7%tt5i6z5VEVI#0H6TQTV6C*~9_^1ML1mIv! z7A7Mkh!ZCsTyHhPga~0Ek%+*1w@XzVFY`^0!U&`;_!=Q}BpTu}D4?)$--=i+IKck^ X_fr*v`{lr800000NkvXXu0mjfMIWNp literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/info.png b/app/examples/Multimedia/MediaPlayer/info.png new file mode 100644 index 0000000000000000000000000000000000000000..71e6db8e1c73b506b1e80f25d59cbc7aa9b280e5 GIT binary patch literal 2413 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/pause.png b/app/examples/Multimedia/MediaPlayer/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..db2961a31799fb220a5fe10112df2924cff448ba GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7`%AFa~(KIEG~0dppzDkI7M>b#=PB z*B%Du&JDrB{*D$co!qf61Hb6Fm3Thk%Sd1q`pV+={ru66tDh&A1p5>`tlqq6()VAl zU!?`>e!Bm(i&e%e`clK|*tolEZ(mgL_?O|O2WNK28m=?{VHNlMgUa#1=oS9<{9>z} zci#N;N$L8|_-F2`t2eK&$i8@>;O&g)Hxad>wvJ8QI*Xj2S^TfvVe-=`)c)=})^qQ@ f@7>uVC;yfA=*xeb;RGS;GoR*@VW0wkGo@N|O@8l(Z-pawBg1x!YW;)ovsy7b6lSi6taU zv3u$&OwrxN^^{ayZ9n5ky*XexdNPJ?E1zg7xDFEuMrMK59ly?Dcbqu zMKnhnUuu%Nn2M0#FhiQq#)RkqOHEE4^84D#cTKp>l&ApPOiJM&fUt^>n$XGgXaL(y zhN!E87n<;j1x^9NY@TYu2bMbp2$Q+42`M%>1qel))`TJUIt2(Zn!KA+vr~W|9A!i! zS}1S|5Vm-ChAT{R3J})tSrZ=QWM}XX5Ejv^3Ej+b3J_-TL=)by#3?{1Awg6!_gPPDr+$lh)r(Y9pS+p%-075Nq zHQ{F9<}DmxjJBo4F+iBXqtSptDzate>i^4ANm(s7%rY+f9RoyC(jeVtS?e4il9qF3 zF+UFgkzpS>5dspj%PhLHeJV0Qx!jd1vwR%r%i$lxdAuC`7^a!&^bS-@hg6P!;bg4O z;T>4d*L-fpI~maN=P3?+3+(|--j3invB2@RfC5@|_%H`7e?&HbiCot4gEU&k+W<-gu)4HX{@u+lp>y3e$b@j(C${Lt~&Cx2R1eOO&%GR(L>qt&=z(Q5bvl$b}nlQPCUu&`@2d=v6}WFx`~vR9Z^BLE@4 n$9Ci3;L_F6?RY-Cc2mCqDrG93~DH&)Bw&~?s175mg;NO7LbqSnDmQTX_ra`x{_3dbQlJ=B`q5@ z2*@mX5evL6jROf-f;A9+6t|gfm)khSBLh>Qlp1Q>N;g&3{Xr}sdu^gU@`YG}u`4C7 zTpOGjWC&!KJhrvA8w9tn)h>ZPnGiYzQqCQKmz450#Gu6_NKitO@gylBL4qWs+02m~ zLvf#3b|Gc`;t~k%1C#CBT@QXL^GT8*K`9BwQ^MGg*MFLmz)hFHq)c0ofKz|tHMBDU zQ_`(K9hR$;Ie4|?BoMd!PvCta(5GlS%TlU28j(Peo?0D0_!lqmGj#y`%2U3H-vXU1 z;>CcLX0P;pBnxQIfd`~W`|6pks$2IJ(Fnj5*86HJywYcp2>9lJjHRAhyIjss3gyF; zvCS!O;4v-qvVwh)2zbY_ER?QrH>T1#QY|?Ws?(D5KyGnWn= zU>QEs>~@(|^v6L!Urw;2umoT)2MbF8n)s)%1hm&~JOsXCPh|KA1X3I@0G~I31svyt zI0%f>>NJg$j5GqD*$JILu_orxqfq;jhdz?627gyv0=HNIP|QN5+Gwxz5$Bj`3_ddi zyZKX(?~hc4Jo`He0qZF#onXj8mV`XHm*>9dxn$OL`Gk6Es0QfbtkeFvPkASe%9o_s z$$onCA`py_EVF&43!WR5amm(%#O;2QIhrw&L0q!jQp=%0I18YUUCgtCDtU_LpHU^> zDK?(pY2aD}aM0cfTe6Owe!it5GQ740g7T0C+9HDw!2*5}nwe_|J`W3Y(8vWt;CWc! z5amYUhh%{+*3oJdT+0F-&ixEkF~DmOSVuQ6X`q(BVt~&c0w);sq1SMpsTAuSl{v+8 zVtg(~kU*z>o6QI>TKvS=*Lnwer>`+^7!|qMlKsZR^&8xys8^Cc^sIzMvm4akWHe#1 zQeG<)7TGDA67JwIY~z&jXMN~nznH~tC0EJ@WRax*yQGymZMR(hjRia{Uk8vv6PIPu zY8<{@Dzb30=Ya3wY^s@VD4hL>i>lPE6h3)v6*_(Y4s_;d&yA|7)&Kwi07*qoM6N<$ Eg7$gZqW}N^ literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/screenshot.png b/app/examples/Multimedia/MediaPlayer/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8ef46d2a63a7ab20664728c05e9d81c4e38ce9d1 GIT binary patch literal 1090 zcmV-I1ikx-P)7+`B+r&Zf+WF!&tv`aT)7oSfv&f9{mvY2bP8tyC zaS7PGD;=KUK65K%WOFjzM)OV1U8uD$7y-m znSP4&huaUyWMcwla?6CmHJLhm40kdud}sGEV6gwgxM7s6RIm$>Ihy$2D2Ds~sR`j2|1NANz+CQqQ%tylnD;cccfkx5J; z%P-nkifzUOkU=LOvjKTKL+)z%zF$hU@wBvgPQ;P`Z_Bh(U=)E>TJzY&8g6Oh_h_PB zyRs(PEqGOtbFAlX0h?y+k=+n*taSoLb)ZI(J3Tw@V1UmQt&+!+i@;+{P_(1Ic1JX= z%GWM(9jMT>tG;&nh<~FQIRb;4R_1G07I4>?2>eY>(dPTw)o9vZ$q~qLQPCQBw4jwu zqoVywUvdQCOGPf{oq{&6Ge^-n1A2oP1omrM3$Kl`Sj;X>|02MfUW%_cMR8+5@wDWW zR%r9`u}mKsEtjpHQr#yL0-G8ahCQLs!ZAg+c!Mlw=q1OKEaDN5eQRKIroEj@pTng& zq>=i_^mR)PODfRQ^U)th02?@j34UUgc5H)lAOe9LYxyR0`W{{>*rX&t0IpNd_Q2JB z&I?-2NSFZJV-q!eU2yCii)r-q3E>1{ycu;Q|Alxk*TbD4{Dae!{a`@IAry%#w6 zjW*gM*!vTRxCO~T?+0QZJ^l0sf~C?Bk) zLOw0(^gm1O@XX4Ql&7fWxtM>?m}7`@oaIOB&O_Svag4)@`;buGAuSC!LI3~&07*qo IM6N<$g4h!bDF6Tf literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/seek-backward.png b/app/examples/Multimedia/MediaPlayer/seek-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..d108a3f171ac516949c28353ec4d4ec35ff0d624 GIT binary patch literal 1316 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/skip-backward.png b/app/examples/Multimedia/MediaPlayer/skip-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d4f073317572bb781964bee8066acd6506692a GIT binary patch literal 1093 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 DM)e0F literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/stop.png b/app/examples/Multimedia/MediaPlayer/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac59d99b338bb0ee4ee9317190fd38c7ee453b0 GIT binary patch literal 495 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7`%AFmCg7aSX}0_jcxaFQ!0=qvuz2 zFHpI>vE$xCd+G46Os=^~N*U`LyYDTT>gX>pRZL>b%8o2i*WA~PPR^GPbcr3zG`)9m zby{J*V{N+G^VvJ^eBNzbZg!v5<$tW->e+9O$}F4M(7mFuhwIdi1B{=}Ws5JCOzCpH z70~F(X4ftMXyw!b|KmO7UHu==W!ZS%GrFc{ccl3BN0rKB6TH4gE-_r$B~`S;<>Udm z9_7Z$FDs>kJ(L!&w?E^~y7Qz&;)kN0&+FM$zrA8O{jcJ9h7AYXj>qm1ml$82*Y1(% zXFtdoCVF7n^ws}P8=lX3eDc8b>!Ge;qKmB#Y)H>rbHx0t%_8P2+cU39IYi#v!1uvh zh=EZ68wy}JKhN;u{5;+8o3r==KAhQM+Q6CmzO*6xypGbfr!DcZ|C_X4WpXLJpUH7e zH>a1u?85p}d(0MW44u6=dyA*!^Ss$%Yu_JfSsf7^${QK;aP7j`e`WVBUcNJucgb9z z?u9&`Ubx@-+Rn3WVd@umJ{_yS&t;x;Uj3oG>xB;6&O_|l4_%jinSTG>jXW{qWBqTx h%@t?f-1%>g9Yfofu;uq+4n7A(q^GN&%Q~loCID#<-xdG> literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/subtitle.png b/app/examples/Multimedia/MediaPlayer/subtitle.png new file mode 100644 index 0000000000000000000000000000000000000000..a124f785039b1c809ace5af38ab01c77ad675730 GIT binary patch literal 1174 zcmV;H1Zn$;P)(X@-G^x zmP&0K75sl{!L-Fj`~ztrM2&)4(Ik8Nu-^IZPG;}lZl1gY%iY=Ue&3mM?%X|d2YB6E z4K<;B1>nL6a|(DD)Q%9`R+9t5unfIBoS z_$CP!;BD1C;tgLL(SrG54D3^v@RE(*3^Z~`IQS-{d^HD>71g^uShRuca7<1ktEv<5i#!xfXi96GA1FlrL!9}09~qmX16?!%9jOM<|OMb_*9-2 z%y+=Y+GnYzWr*;Jn*fOk`O`STYF{lh&pF;0_m|O}jAB4HAzenGi!crVFb<21aaxuP zb23_j4G9z+b`ZcWLYF>|Ho)BUX=^hfy$Q3|ERmWyxyRTHV6nt*H70G+Ojn*w#@KE8 zYLL<^jpsAf8A7*30P$H7J~GB0!*E6gHCDg=8uVjgq2GgL#-#0N)4tunAI9h&nJ0s2 zie8oyV5Km%G5Ak7>)51=u%h$~DBFaTI-JZ2>-a>)bXBxG2p$yxPgpBe^+~t}iE9Zr zA*Gs@u^wx(8AwdX16Eg@foeW5n-g~v5_)8t6Oo`3RT*@RjR;lf9L+$o6Y>w^F<~I) zzrsO!=Y&&bW!*a_K5MzCz^QtU+(O&5Fi5)1>B3wgr#`&9= zXNAxw3wUodTLhRT+ycI()$io!*LbqN%U2?1a*c6Q5GWKL979}@yby3tOx zTMiR3ZBAkU^F>no1e}|xf1$Nvj&;0Np&9QN03-s~Oh}665p2U|e1ToayNgg{PSycn z4F?q`_MMO-0nVsAJg-jOHa`7QDoKx(ybfqj zmnVvT`d)Qf9YoM7O$To&Fuz!y@#^$f2Sc1x#K0tR6BjH}j_0r1(gFU!kajkGRXiJ% z=Op-H(7+|504xd0vk(AJdk@Qe85Qty>!-rNoY+yPb}IXgU>SKlUQ8NaNQUHzWH8#$ oijtek0EThf{z|q|d0wCPKeKj|gU!d-<^TWy07*qoM6N<$f>iq#YXATM literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/undo.png b/app/examples/Multimedia/MediaPlayer/undo.png new file mode 100644 index 0000000000000000000000000000000000000000..76e6a8a29d19659b27cebc4115f558933b07e768 GIT binary patch literal 1091 zcmV-J1ibr+P)*R=Z;HR=trhbqc zt5T0IX0uG(%M3mh%_FJC&)^T)%Ke;48kN9A%DsEG7mmbK$rdGA@)rUznH(pIKR16f!7c~ zlJ7V-JbsxIzgUUa6hL-xkBZ{E30aDZ$g*1suz)wB zIzTDF`hrbJBvIL@HZ0+%LV(A(Sq@sQM$f}eF>phGPN#$>78w(;R!UC-U6m>?luLo2 zo7GB<1eE}vl+3CmaLV4I*|>})xa}i$f8xNlqXo@)iAvO)*t5EO!JCxUGgsRK0WsIA z#9GAdR+!K<7zeed85s1f7%F55`k`sI83BM5!*=n8$i`(&FAOmy0I({tRY=hFg8u!d z#(UQ(AuvF9ce;}k;FX}e5N^v7)wWOql3fV5N#E&BAq8l9A+hx8wHO#hfY%E_ap9Bl z>3C879NK;VX6A{GnS4gC9aBca`=UegS8Q$xPZju&?61J*$e*culNR8X;I&sAt(^Y~ zxl>^T0l^ZgIMnB*RV6w#GBc6@KM9WmK4wSD-~8tz;sJoA{4T@aa~(4Rl94L8^Fjjx z01guOC^hUGm z!K+BDW*pDqPWCN~#VF^a&zNi(Nf^5X6b45GkF~Pc82q1v<00_xnQs^mFpDQO@i(ZS zr-c-;fiD8lovdZ4-_bL2B55HH`Iv+p&!3v{0ynL9DVnK!SohAGL5TOg#hi2RyXX7vIrm=XJ_r$Fnc?Jb zR-8EX)GXDX!3-n3;lA^+sG1a4pKS0;Nv$g$eL{0q^l1!hV{zyY>30G2k~E~s;Pe$5 zEs$Q4t`x$Vwa9dmg&^}h=Npe1MtKDm+F zjpz|tp)pUuIeX4Xmbcn9hK&QvGUPdxK?QtvjRO`T<~*}3sDNJAILrl1&zAY6qS-?A z>NVYQUwy0Ksn?v+4OM5NIciatCUS;Os7uYc~iU>-I+7t6Ywav`?5y<8RE2`ey{BTJn0kgz;39QsTe>MR7TRjd}c}gbG^*??4NuC zDr~pF8XD+^D6p9=ZtEZkUNCDF=_mj(_LF48qQ1LaTh!9&nbmFqj=Ncz)+XZV!Z literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/visualisation.png b/app/examples/Multimedia/MediaPlayer/visualisation.png new file mode 100644 index 0000000000000000000000000000000000000000..a97ee5fa15543f2d885e2c359697c9314f4cf9c0 GIT binary patch literal 1093 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 P00000NkvXXu0mjfY;kTe literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/volume-2.png b/app/examples/Multimedia/MediaPlayer/volume-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3fd2482db747bd838eaf51d23c7b59c19dc35d64 GIT binary patch literal 1052 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MoviePlayer/.directory b/app/examples/Multimedia/MoviePlayer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/MoviePlayer/.icon.png b/app/examples/Multimedia/MoviePlayer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c308b41be2fb91db340ecbfb89035b67961777d4 GIT binary patch literal 4342 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 z, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: MoviePlayer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMoviePlayer.form:20 +msgid "

    Gambas Movie Player

    \n\n

    This example is based on the MPlayer movie player and was made by Benoît Minisini

    " +msgstr "

    Reproductor de peŀlícules del Gambas

    \n\n

    Aquest exemple està basat en el reproductor de peŀlícules MPlayer i ha estat fer per en Benoît Minisini

    " + +#: FMoviePlayer.form:15 +msgid "Movie player" +msgstr "Reproductor de peŀlícules" + +#: .project:1 +msgid "Movie player example" +msgstr "Exemple de reproductor de peŀlícules" + diff --git a/app/examples/Multimedia/MoviePlayer/.lang/cs.po b/app/examples/Multimedia/MoviePlayer/.lang/cs.po new file mode 100644 index 00000000..f7cb6247 --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.lang/cs.po @@ -0,0 +1,30 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Movie player example" +msgstr "Příklad přehravače videa" + +#: FMoviePlayer.form:15 +msgid "Movie player" +msgstr "Přehravač videa" + +#: FMoviePlayer.form:20 +msgid "" +"

    Gambas Movie Player

    \n" +"\n" +"

    This example is based on the MPlayer movie player and was made by Benoît Minisini

    " +msgstr "" +"

    Gambas přehravač filmů

    \n" +"\n" +"

    Tento příklad je založený na přehravači filmů MPlayer a byl vyvtořen Benoît Minisini

    " diff --git a/app/examples/Multimedia/MoviePlayer/.lang/es.po b/app/examples/Multimedia/MoviePlayer/.lang/es.po new file mode 100644 index 00000000..a156ddfc --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.lang/es.po @@ -0,0 +1,27 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMoviePlayer.class:172 +msgid "Movie player" +msgstr "Movie player" + +#: FMoviePlayer.class:177 +msgid "" +"

    Gambas Movie Player

    \n" +"\n" +"

    This example is based on the MPlayer movie player and was made by Benoît " +"Minisini

    " +msgstr "" +"

    Gambas Movie Player

    \n" +"\n" +"

    Este ejemplo está basado en el MPlayer movie player y fue echo por Benoît " +"Minisini

    " diff --git a/app/examples/Multimedia/MoviePlayer/.lang/ru.po b/app/examples/Multimedia/MoviePlayer/.lang/ru.po new file mode 100644 index 00000000..8b455b77 --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.lang/ru.po @@ -0,0 +1,52 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Multimedia/MoviePlayer/.project:20 +msgid "Movie player example" +msgstr "Пример киноплеера" + +#: app/examples/Multimedia/MoviePlayer/.project:21 +msgid "Movie player based on the MPlayer" +msgstr "Киноплеер, основанный на MPlayer" + +#: app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class:54 +msgid "pause\n" +msgstr "пауза\n" + +#: app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form:5 +msgid "Movie player" +msgstr "Киноплеер" + +#: app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form:9 +msgid "" +"

    Gambas Movie Player

    \n" +"\n" +"

    This example is based on the MPlayer movie player and was made by Benoît Minisini

    " +msgstr "" +"

    Киноплеер Gambas

    \n" +"\n" +"

    Этот пример основан на киноплеере MPlayer и сделан Бенуа Минисини

    " + diff --git a/app/examples/Multimedia/MoviePlayer/.project b/app/examples/Multimedia/MoviePlayer/.project new file mode 100644 index 00000000..a2884b0f --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +Title=Movie player example +Description="Movie player based on the MPlayer" +Startup=FMoviePlayer +Icon=video.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Environment="GB_GUI=gb.gtk3" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class new file mode 100644 index 00000000..582bce0d --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class @@ -0,0 +1,136 @@ +' Gambas class file + +Private $hProcess As Process +Private $bQuit As Boolean +Private $sPath As String +Private $bShow As Boolean + +Public Sub Form_Resize() + + embPlayer.Move(8, 8, Me.ClientW - 16, Me.ClientH - panButton.H - 8) + panButton.Move(0, Me.CLientH - panButton.H, Me.CLientW) + txtAbout.Move(16, 16, embPlayer.W - 16, embPlayer.H - 16) + +End + +Public Sub btnPlay_Click() + + If $hProcess Then + Print #$hProcess, " "; + btnPlay.Enabled = False + btnPause.Enabled = True + 'PRINT "CONTINUE" + Return + Endif + + txtAbout.Visible = False + + With embPlayer + Form_Resize + .Show + .Enabled = False + ' '.Mouse = Mouse.Default + ' Form_Resize + ' '.Enabled = FALSE + End With + + $bShow = True + + $hProcess = Exec ["mplayer", "-wid", CStr(embPlayer.Handle), Conv$($sPath, Desktop.Charset, System.Charset)] For Input Output As "Process" + + btnStop.Enabled = True + btnPlay.Enabled = False + btnPause.Enabled = True + + 'embPlayer.Hide + 'timShow.Enabled = True + +End + +Public Sub btnPause_Click() + + If Not $hProcess Then Return + Print #$hProcess, " "; + '$hProcess.Send(" ") '("pause\n") + btnPlay.Enabled = True + btnPause.Enabled = False + 'PRINT "PAUSE" + +End + +Public Sub btnStop_Click() + + If Not $hProcess Then Return + If $bQuit Then + $hProcess.Kill + Else + Print #$hProcess, "q"; + $bQuit = True + Endif + +End + +Public Sub Process_Read() + + Dim sData As String + + sData = Read #Last, -255 + Print sData; + +End + + +Public Sub Process_Kill() + + $hProcess = Null + timShow.Enabled = False + $bQuit = False + $bShow = False + btnPause.Enabled = False + btnPlay.Enabled = True + btnStop.Enabled = False + embPlayer.Hide + txtAbout.Show + 'PRINT "STOP" + +End + + +Private Sub StopMovie() + + If Not $hProcess Then Return + + $hProcess.Kill + 'While $hProcess + ' Wait + 'Wend + +End + +Public Sub Form_Close() + + StopMovie + +End + + +Public Sub btnOpen_Click() + + Dialog.Path = $sPath + If Dialog.OpenFile() Then Return + $sPath = Dialog.Path + + StopMovie + btnPlay.Enabled = True + btnPlay_Click + +End + +Public Sub timShow_Timer() + + embPlayer.Resize(1, 1) + embPlayer.Show + Form_Resize + timShow.Enabled = False + +End diff --git a/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form new file mode 100644 index 00000000..40f5631d --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form @@ -0,0 +1,42 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(33.1429,15,51,45) + Text = ("Movie player") + Icon = Picture["video.png"] + { txtAbout TextLabel + MoveScaled(2,1,46,20) + Text = ("

    Gambas Movie Player

    \n\n

    This example is based on the MPlayer movie player and was made by Benoît Minisini

    ") + } + { panButton Panel + MoveScaled(2,35,46,6) + { btnPlay Button + MoveScaled(1,1,4,4) + Enabled = False + Picture = Picture["icon:/small/play"] + } + { btnPause Button + MoveScaled(6,1,4,4) + Enabled = False + Picture = Picture["icon:/small/pause"] + } + { btnStop Button + MoveScaled(11,1,4,4) + Enabled = False + Picture = Picture["icon:/small/stop"] + } + { btnOpen Button + MoveScaled(16,1,4,4) + Picture = Picture["icon:/small/open"] + } + } + { timShow #Timer + #MoveScaled(32,17) + Delay = 500 + } + { embPlayer Embedder + MoveScaled(7,20,23,13) + Visible = False + Background = &H000000& + } +} diff --git a/app/examples/Multimedia/MoviePlayer/video.png b/app/examples/Multimedia/MoviePlayer/video.png new file mode 100644 index 0000000000000000000000000000000000000000..687755442f5c765a8a93804555ac3c833994c690 GIT binary patch literal 5244 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MusicPlayer/.icon/32.png b/app/examples/Multimedia/MusicPlayer/.icon/32.png new file mode 100644 index 0000000000000000000000000000000000000000..66d2fcb0726bcd0d1237048f1dcd26019d10980c GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iS0D`n|Nn#V|NjRL9B620 z*t+I*1W=H%B*-tA!Qt7BG$3cCr;B5V#`)Nb2YH(fcvvq~F#C9(*OdzsawOGXgZ_rKn`cYgw~BxF|uuv(h4uNCbF(5D4!7I z#S$vQe%{%akN-yKVX?MVr&lz;l{djBF0xe)K3GxeOXgJ_-;6Ov4MUMy2$^uUp z#}JR>Q>R`mYgXWCn|(`nL(}Z79@}5O`ECESiN*W8rcm*Y4@&cEo;-i?FhKV})%oE6 zh3XAExEVsc6ghTFH%z|A%#ieAI^z<#-BaBpKP>zjxpMP0^#h9=PQ>d=i=0iLDwyhi zK-%H(`9A@S_cj-Ho|`-CLc~QwiHrq*w>JM2 z3twmChca#`*FI3`xFf7}PifpU~>F| zc&!Y3xQyUE8&%O?b+vpVhr7J=*4KT0<(@DnCMGFiMuu;F-!z_xu6uH3D;YjpoZ~St z=5FZB;&7, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: MusicPlayer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Trieu un fitxer de música..." + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Reproductor de música del Gambas" + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "Obre..." + diff --git a/app/examples/Multimedia/MusicPlayer/.lang/cs.po b/app/examples/Multimedia/MusicPlayer/.lang/cs.po new file mode 100644 index 00000000..c11a030f --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/cs.po @@ -0,0 +1,24 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Hudební přehrávač Gambas" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Vyber hudební soubor..." + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "Otevřít..." diff --git a/app/examples/Multimedia/MusicPlayer/.lang/es.po b/app/examples/Multimedia/MusicPlayer/.lang/es.po new file mode 100644 index 00000000..5186acbb --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/es.po @@ -0,0 +1,24 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Gambas Music Player" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Seleccione un archivo de música..." + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "" diff --git a/app/examples/Multimedia/MusicPlayer/.lang/fr.po b/app/examples/Multimedia/MusicPlayer/.lang/fr.po new file mode 100644 index 00000000..bedef664 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/fr.po @@ -0,0 +1,35 @@ +# #-#-#-#-# FSoundPlayer.pot (PACKAGE VERSION) #-#-#-#-# +# /home/benoit/gambas/2.0/src/examples/examples/Sound/MusicPlayer/FSoundPlayer.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# #project.pot (PACKAGE VERSION) #-#-#-#-# +# /home/benoit/gambas/2.0/link/share/gambas2/examples/Sound/MusicPlayer/.project +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Choisissez un fichier de musique..." + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Lecteur de musique Gambas" + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "Ouvrir..." + diff --git a/app/examples/Multimedia/MusicPlayer/.lang/ru.po b/app/examples/Multimedia/MusicPlayer/.lang/ru.po new file mode 100644 index 00000000..5dc72170 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/ru.po @@ -0,0 +1,38 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Multimedia/MusicPlayer/.project:26 app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form:5 +msgid "Gambas Music Player" +msgstr "Музыкальный проигрыватель Gambas" + +#: app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form:12 +msgid "Choose a music file..." +msgstr "Выбрать музыкальный файл..." + +#: app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form:40 +msgid "Open..." +msgstr "Открыть..." + diff --git a/app/examples/Multimedia/MusicPlayer/.project b/app/examples/Multimedia/MusicPlayer/.project new file mode 100644 index 00000000..430d9819 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.90 +Title=Gambas Music Player +Startup=FSoundPlayer +Icon=sound.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.sdl.sound +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Sound/MusicPlayer/MusicPlayer +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Systems=mandrake +Menus=Multimedia/Sound +Categories= +Groups=Sound +ExtraDependencies= +ExtraFiles= diff --git a/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class new file mode 100644 index 00000000..642a6be4 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class @@ -0,0 +1,145 @@ +' Gambas class file + +Static Private $bDoNotMove As Boolean +Private $iLength As Integer + +Private Sub GetMusicLength(sPath As String) As Integer + + Dim sOutput As String + Dim aScan As String[] + + Select Case LCase(File.Ext(sPath)) + + Case "mp3" 'mp3info -p "%S\n" + Exec ["mp3info", "-p", "%S", sPath] To sOutput + Return CInt(sOutput) + + Case "ogg", "flac" + Exec ["ogginfo", sPath] To sOutput + For Each sOutput In Split(sOutput, "\n") + sOutput = Trim(sOutput) + aScan = Scan(sOutput, "*: *m:*s") + If aScan.Count = 3 Then + Return CInt(CInt(aScan[1]) * 60 + CFloat(Replace(aScan[2], ",", ".")) + 0.5) + Endif + Next + + End Select + +Catch + +End + +Public Sub btnOpen_Click() + + If Dialog.OpenFile() Then Return + + lblTitle.Text = File.Name(Dialog.Path) + ' Approximation... + $iLength = GetMusicLength(Dialog.Path) + If $iLength = 0 Then $iLength = 600 + sldPos.MaxValue = $iLength + + Music.Load(Dialog.Path) + btnPlay_Click + +Catch + + Message.Error(Error.Text) + +End + +Public Sub btnPlay_Click() + + timMusic.Enabled = True + Music.Play + +End + +Public Sub btnPause_Click() + + Music.Pause + +End + +Public Sub btnStop_Click() + + Music.Stop + timMusic.Enabled = False + lblPos.Text = "" + sldPos.Value = 0 + +End + +Private Sub FormatTime(iPos As Integer) As String + + Dim iInd As Integer + Dim iVal As Integer + Dim sPos As String + + For iInd = 0 To 2 + + iVal = iPos Mod 60 + iPos = iPos \ 60 + If iInd Then + sPos = Format(iVal, "00") & ":" & sPos + Else + sPos = Format(iVal, "00") + Endif + + Next + + Return sPos + +End + + +Public Sub timMusic_Timer() + + Dim iPos As Integer + Dim sPos As String + + iPos = Music.Pos + + If Not $bDoNotMove Then + Object.Lock(sldPos) + If iPos > sldPos.MaxValue Then + sldPos.MaxValue = sldPos.MaxValue * 2 + Endif + sldPos.Value = iPos + Object.Unlock(sldPos) + Endif + + lblPos.Text = FormatTime(iPos) & " / " & FormatTime($iLength) + +End + +Public Sub sldPos_Change() + + Music.Pos = sldPos.Value + +End + +Public Sub sldPos_MouseDown() + + $bDoNotMove = True + +End + +Public Sub sldPos_MouseUp() + + $bDoNotMove = False + +End + +Public Sub sldVolume_Change() + + Music.Volume = sldVolume.Value / sldVolume.MaxValue + +End + +Public Sub Form_Open() + + Dialog.Path = User.Home + +End diff --git a/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form new file mode 100644 index 00000000..e8d6be34 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form @@ -0,0 +1,62 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(41.5714,35.1429,66,16) + Text = ("Gambas Music Player") + Icon = Picture["sound.png"] + Resizable = False + { lblTitle TextLabel + MoveScaled(1,1,59,5) + Font = Font["+2"] + Padding = 4 + Text = ("Choose a music file...") + Alignment = Align.Normal + Border = Border.Plain + } + { sldPos Slider + MoveScaled(1,7,59,4) + MaxValue = 3600 + Step = 2 + } + { timMusic #Timer + #MoveScaled(33,0) + } + { sldVolume Slider + MoveScaled(61,1,4,10) + MaxValue = 128 + PageStep = 8 + Value = 128 + } + { PictureBox1 PictureBox + MoveScaled(61,11,4,4) + Picture = Picture["icon:/small/volume"] + Alignment = Align.Center + } + { HBox1 HBox + MoveScaled(1,11,59,4) + { btnOpen ToolButton + MoveScaled(0,0,12,4) + AutoResize = True + Text = ("Open...") + Picture = Picture["icon:/16/open"] + } + { btnPlay ToolButton + MoveScaled(13,0,4,4) + Picture = Picture["icon:/16/play"] + } + { btnPause ToolButton + MoveScaled(17,0,4,4) + Picture = Picture["icon:/16/pause"] + } + { btnStop ToolButton + MoveScaled(21,0,4,4) + Picture = Picture["icon:/16/stop"] + } + { lblPos Label + MoveScaled(27,0,27,4) + Font = Font["14,Bold"] + Expand = True + Alignment = Align.Right + } + } +} diff --git a/app/examples/Multimedia/MusicPlayer/sound.png b/app/examples/Multimedia/MusicPlayer/sound.png new file mode 100644 index 0000000000000000000000000000000000000000..034a6cfa1adec8a9d80cec81c4f9f7e2ba7f29e7 GIT binary patch literal 3551 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: MyWebCam\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Webcam example" +msgstr "Exemple de càmera web" + +#: Form1.class:40 +msgid "Unable to open video device" +msgstr "No es pot obrir el dispositiu de vídeo" + +#: Form1.class:45 +msgid "Stop" +msgstr "Atura" + +#: Form1.form:59 +msgid "Bright" +msgstr "Lluminositat" + +#: Form1.form:70 +msgid "Contrast" +msgstr "Contrast" + +#: Form1.form:87 +msgid "Whiteness" +msgstr "Blancor" + +#: Form1.form:92 +msgid "Hue" +msgstr "Matís" + +#: Form1.form:103 +msgid "Color" +msgstr "Color" + +#: Form1.form:115 +msgid "160x120" +msgstr "-" + +#: Form1.form:121 +msgid "320x240" +msgstr "-" + +#: Form1.form:128 +msgid "176x144" +msgstr "-" + +#: Form1.form:134 +msgid "352x288" +msgstr "-" + +#: Form1.form:140 +msgid "128x96" +msgstr "-" + +#: Form1.form:146 +msgid "640x480" +msgstr "-" + +#: Form1.form:156 +msgid "Tuner frequency:" +msgstr "Freqüència de sintonització:" + +#: Form1.form:162 +msgid "+" +msgstr "-" + +#: Form1.form:168 +msgid "-" +msgstr "-" + +#: Form1.form:174 +msgid "Device Information" +msgstr "Informació del dispositiu" + +#: Form1.form:180 +msgid "Take a shot" +msgstr "Fes una captura" + +#: Form1.form:190 +msgid "Device:" +msgstr "Dispositiu:" + +#: Form1.form:196 +msgid "/dev/video" +msgstr "-" + +#: Form1.form:201 +msgid "Capture" +msgstr "Captura" diff --git a/app/examples/Multimedia/MyWebCam/.lang/cs.po b/app/examples/Multimedia/MyWebCam/.lang/cs.po new file mode 100644 index 00000000..72aa7ce7 --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.lang/cs.po @@ -0,0 +1,112 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Webcam example" +msgstr "Příklad webkamery" + +#: Form1.class:36 +msgid "Unable to open video device" +msgstr "Nelze otevřít zařízení videa" + +#: Form1.class:41 +msgid "Stop" +msgstr "-" + +#: Form1.form:54 +msgid "Bright" +msgstr "Světlost" + +#: Form1.form:65 +msgid "Contrast" +msgstr "Kontrast" + +#: Form1.form:82 +msgid "Whiteness" +msgstr "Bělost" + +#: Form1.form:87 +msgid "Hue" +msgstr "Odstín" + +#: Form1.form:98 +msgid "Color" +msgstr "Barva" + +#: Form1.form:110 +msgid "1024x768" +msgstr "-" + +#: Form1.form:110 +msgid "128x96" +msgstr "-" + +#: Form1.form:110 +msgid "160x120" +msgstr "-" + +#: Form1.form:110 +msgid "176x144" +msgstr "-" + +#: Form1.form:110 +msgid "320x240" +msgstr "-" + +#: Form1.form:110 +msgid "352x288" +msgstr "-" + +#: Form1.form:110 +msgid "640x480" +msgstr "-" + +#: Form1.form:111 +msgid "ComboBox1" +msgstr "-" + +#: Form1.form:116 +msgid "Size" +msgstr "Velikost" + +#: Form1.form:126 +msgid "Tuner frequency:" +msgstr "Ladit frekvenci:" + +#: Form1.form:132 +msgid "+" +msgstr "-" + +#: Form1.form:138 +msgid "-" +msgstr "-" + +#: Form1.form:144 +msgid "Device Information" +msgstr "Informace zažízení" + +#: Form1.form:150 +msgid "Take a shot" +msgstr "Udělej snímek" + +#: Form1.form:160 +msgid "Device:" +msgstr "Zařízení:" + +#: Form1.form:166 +msgid "/dev/video" +msgstr "-" + +#: Form1.form:171 +msgid "Capture" +msgstr "Získat" diff --git a/app/examples/Multimedia/MyWebCam/.lang/es.po b/app/examples/Multimedia/MyWebCam/.lang/es.po new file mode 100644 index 00000000..46ab59c7 --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.lang/es.po @@ -0,0 +1,95 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: Form1.class:14 +msgid "Capture" +msgstr "Captura" + +#: Form1.class:39 +msgid "Unable to open video device" +msgstr "Imposible abrir el dispositivo de video" + +#: Form1.class:44 +msgid "Stop" +msgstr "Detener" + +#: Form1.class:292 +msgid "Bright" +msgstr "Brillante" + +#: Form1.class:303 +msgid "Contrast" +msgstr "Contraste" + +#: Form1.class:320 +msgid "Whiteness" +msgstr "Claridad" + +#: Form1.class:325 +msgid "Hue" +msgstr "Tono" + +#: Form1.class:336 +msgid "Color" +msgstr "Color" + +#: Form1.class:348 +msgid "160x120" +msgstr "160x120" + +#: Form1.class:354 +msgid "320x240" +msgstr "320x240" + +#: Form1.class:361 +msgid "176x144" +msgstr "176x144" + +#: Form1.class:367 +msgid "352x288" +msgstr "352x288" + +#: Form1.class:373 +msgid "128x96" +msgstr "128x96" + +#: Form1.class:379 +msgid "640x480" +msgstr "640x480" + +#: Form1.class:389 +msgid "Tuner frequency:" +msgstr "Afinador de frecuencia:" + +#: Form1.class:395 +msgid "+" +msgstr "+" + +#: Form1.class:401 +msgid "-" +msgstr "-" + +#: Form1.class:407 +msgid "Device Information" +msgstr "Información del dispositivo" + +#: Form1.class:413 +msgid "Take a shot" +msgstr "Tomar una foto" + +#: Form1.class:423 +msgid "Device:" +msgstr "Dispositivo:" + +#: Form1.class:429 +msgid "/dev/video" +msgstr "/dev/video" diff --git a/app/examples/Multimedia/MyWebCam/.lang/ru.po b/app/examples/Multimedia/MyWebCam/.lang/ru.po new file mode 100644 index 00000000..b7467f9e --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.lang/ru.po @@ -0,0 +1,158 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-30 09:26+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Multimedia/MyWebCam/.project:20 +msgid "Webcam example" +msgstr "Пример веб-камеры" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:29 app/examples/Multimedia/MyWebCam/.src/Form1.form:118 +msgid "Capture" +msgstr "Захватить" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:36 +msgid "Unable to open video device" +msgstr "Невозможно открыть видеоустройство" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:41 +msgid "Stop" +msgstr "Стоп" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:62 app/examples/Multimedia/MyWebCam/.src/Form1.class:166 app/examples/Multimedia/MyWebCam/.src/Form1.class:175 app/examples/Multimedia/MyWebCam/.src/Form1.form:81 +msgid "Tuner frequency:" +msgstr "Частота тюнера:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:142 +msgid "fps" +msgstr "кадр/с" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:183 +msgid "Device Bus:" +msgstr "Шина устройства:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:184 +msgid "Device Driver:" +msgstr "Драйвер устройства:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:184 +msgid "Version:" +msgstr "Версия:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:185 +msgid "Device Name:" +msgstr "Имя устройства:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:186 +msgid "Max. Resolution:" +msgstr "Макс. разрешение:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:187 +msgid "Min. Resolution:" +msgstr "Мин. разрешение:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.class:196 +msgid "Image saved as " +msgstr "Изображение сохранено как " + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:22 +msgid "Bright" +msgstr "Яркость" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:31 +msgid "Contrast" +msgstr "Контраст" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:45 +msgid "Whiteness" +msgstr "Белизна" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:49 +msgid "Hue" +msgstr "Тон" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:58 +msgid "Color" +msgstr "Цвет" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:68 +msgid "128x96" +msgstr "128x96" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:68 +msgid "160x120" +msgstr "160x120" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:68 +msgid "176x144" +msgstr "176x144" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:68 +msgid "320x240" +msgstr "320x240" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:68 +msgid "352x288" +msgstr "352x288" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:68 +msgid "640x480" +msgstr "640x480" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:68 +msgid "1024x768" +msgstr "1024x768" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:69 +msgid "ComboBox1" +msgstr "Комбинированный_список_1" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:73 +msgid "Size" +msgstr "Размер" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:86 +msgid "+" +msgstr "+" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:91 +msgid "-" +msgstr "-" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:96 +msgid "Device Information" +msgstr "Информация об устройстве" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:101 +msgid "Take a shot" +msgstr "Сделать снимок" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:109 +msgid "Device:" +msgstr "Устройство:" + +#: app/examples/Multimedia/MyWebCam/.src/Form1.form:114 +msgid "/dev/video0" +msgstr "/dev/video0" + diff --git a/app/examples/Multimedia/MyWebCam/.project b/app/examples/Multimedia/MyWebCam/.project new file mode 100644 index 00000000..831cb11a --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Webcam example +Startup=Form1 +Icon=camera.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.v4l +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/dcampos/MyWebCam/MyWebCam +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Multimedia/MyWebCam/.src/Form1.class b/app/examples/Multimedia/MyWebCam/.src/Form1.class new file mode 100644 index 00000000..0948ec26 --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.src/Form1.class @@ -0,0 +1,203 @@ +' Gambas class file + +Private hWebcam As VideoDevice +Private OnSet As Boolean +Private Fps As Date +Private nFps As Integer + +Public Sub Button1_Click() + + + Dim num As Integer + Dim Buf As String + Dim sSize As String + + If hWebCam Then + Bright.Enabled = False + Contrast.Enabled = False + Hue.Enabled = False + Whiteness.Enabled = False + Colour.Enabled = False + cmbSize.Enabled = False + FreqUp.Enabled = False + FreqDown.Enabled = False + TxtDevice.Enabled = True + BtnTakeShot.Enabled = False + Button2.Enabled = False + hWebCam = Null + Tmr.Enabled = False + Button1.Caption = ("Capture") + Return + End If + + + Try hWebCam = New VideoDevice(TxtDevice.Text) + If Error Then + Message.Error(("Unable to open video device")) + Return + End If + hWebCam.Source = hWebCam.TV + hWebCam.PAL + + Button1.Caption = ("Stop") + BtnTakeShot.Enabled = True + Button2.Enabled = True + Bright.Enabled = True + Contrast.Enabled = True + Hue.Enabled = True + Whiteness.Enabled = True + Colour.Enabled = True + cmbSize.Enabled = True + sSize = CStr(hWebCam.Width) & "x" & CStr(hWebCam.Height) + If cmbSize.Find(sSize) < 0 Then cmbSize.Add(sSize) + Try cmbSize.Text = sSize + FreqUp.Enabled = True + FreqDown.Enabled = True + TxtDevice.Enabled = False + OnSet = True + Bright.Value = hWebcam.Bright + Contrast.Value = hWebcam.Contrast + Hue.Value = hWebCam.Hue + Whiteness.Value = hWebCam.Whiteness + Colour.Value = hWebCam.Color + LblFreq.Text = ("Tuner frequency:") & " " & hWebCam.Tuner.Frequency + + Wait 0.001 + OnSet = False + Tmr.Delay = 10 + Tmr.Enabled = True + Me.Caption = hWebCam.Name + Fps = Now() + nFps = 0 + + +End + + + + + +Public Sub Bright_Change() + + If OnSet Then Return + hWebCam.Bright = Bright.Value + +End + +Public Sub Contrast_Change() + + If OnSet Then Return + hWebCam.Contrast = Contrast.Value + +End + +Public Sub Whiteness_Change() + + If OnSet Then Return + hWebCam.Whiteness = Whiteness.Value + +End + +Public Sub Colour_Change() + + If OnSet Then Return + hWebcam.Color = Colour.Value + +End + +Public Sub Hue_Change() + + If OnSet Then Return + hWebCam.Hue = Hue.Value + +End + +Public Sub cmbSize_Click() + + Dim aSize As String[] + + aSize = Split(cmbSize.Text, "*x*") + hWebcam.Resize(CInt(aSize[0]), CInt(aSize[1])) + +End + + +Public Sub Tmr_Timer() + + Dim T1 As Date + Dim sBuf As String + Dim hPict As Picture + + Tmr.Enabled = False + + 'Try PictureBox1.Picture = hWebCam.Picture + Draw.Begin(dwgVideo) + hPict = hWebCam.Image.Picture + Draw.Picture(hPict, (dwgVideo.W - hPict.W) \ 2, (dwgVideo.H - hPict.H) \ 2) + Draw.End + + If Not Error Then + nFps = nFps + 1 + T1 = Now() - Fps + If Second(T1) >= 1 Then + Me.Caption = hWebCam.Name & " (" & nFps & " " & ("fps") & ")" + Fps = Now() + nFps = 0 + End If + End If + Tmr.Enabled = True + +End + + +Public Sub Form_Close() + + Tmr.Enabled = False + hWebCam = Null + +End + + + + + +Public Sub FreqUP_Click() + + hWebCam.Tuner.Frequency = hWebCam.Tuner.Frequency + 5 + LblFreq.Text = ("Tuner frequency:") & " " & hWebCam.Tuner.Frequency + +End + + + +Public Sub FreqDown_Click() + + hWebCam.Tuner.Frequency = hWebCam.Tuner.Frequency - 5 + LblFreq.Text = ("Tuner frequency:") & " " & hWebCam.Tuner.Frequency + +End + +Public Sub Button2_Click() + + Dim sCad As String + + sCad = ("Device Bus:") & " " & hWebCam.Bus & "\n" + sCad = sCad & ("Device Driver:") & " " & hWebCam.Driver & " " & ("Version:") & " " & hWebCam.Version & "\n" + sCad = sCad & ("Device Name:") & " " & hWebCam.Name & "\n" + sCad = sCad & ("Max. Resolution:") & " " & hWebCam.MaxWidth & "x" & hWebCam.MaxHeight & "\n" + sCad = sCad & ("Min. Resolution:") & " " & hWebCam.MinWidth & "x" & hWebCam.MinHeight & "\n" + + Message.Info(sCad) + +End + +Public Sub BtnTakeShot_Click() + + Try hWebCam.Save(User.Home & "/webcam_shot.png") + If Not Error Then Message.Info(("Image saved as ") & User.Home & "/webcam_shot.png") +End + +Public Sub Panel2_MouseDown() + + + +End diff --git a/app/examples/Multimedia/MyWebCam/.src/Form1.form b/app/examples/Multimedia/MyWebCam/.src/Form1.form new file mode 100644 index 00000000..7b6cc9df --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.src/Form1.form @@ -0,0 +1,122 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(38,2.4286,76,90) + Resizable = False + { dwgVideo DrawingArea + MoveScaled(0,0,75,51) + Background = &H000000& + Cached = True + } + { Tmr #Timer + #MoveScaled(1.1429,20.5714) + Delay = 10 + } + { Panel2 Panel + MoveScaled(0,52,75,37) + { Panel7 Panel + MoveScaled(1,0,74,15) + Border = Border.Raised + { Label1 Label + MoveScaled(2,1,10,3) + Text = ("Bright") + } + { Bright Slider + MoveScaled(13,1,25,3) + Enabled = False + MaxValue = 65535 + } + { Label2 Label + MoveScaled(2,5,9,3) + Text = ("Contrast") + } + { Contrast Slider + MoveScaled(13,5,25,3) + Enabled = False + MaxValue = 65535 + } + { Whiteness Slider + MoveScaled(13,9,25,3) + Enabled = False + MaxValue = 65535 + } + { Label3 Label + MoveScaled(2,9,11,3) + Text = ("Whiteness") + } + { Label5 Label + MoveScaled(38,1,9,4) + Text = ("Hue") + } + { Hue Slider + MoveScaled(47,1,25,3) + Enabled = False + MaxValue = 65535 + } + { Label4 Label + MoveScaled(38,5,8,4) + Text = ("Color") + } + { Colour Slider + MoveScaled(47,5,25,3) + Enabled = False + MaxValue = 65535 + } + { cmbSize ComboBox + MoveScaled(47,9,25,4) + ReadOnly = True + List = [("128x96"), ("160x120"), ("176x144"), ("320x240"), ("352x288"), ("640x480"), ("1024x768")] + Text = ("ComboBox1") + } + { Label7 Label + MoveScaled(38,9,8,4) + Text = ("Size") + } + } + { Panel3 Panel + MoveScaled(1,16,74,13) + Border = Border.Raised + { LblFreq Label + MoveScaled(1.1429,1.1429,36,4) + Text = ("Tuner frequency:") + } + { FreqUP Button + MoveScaled(1.1429,5.1429,6.4286,5.1429) + Enabled = False + Text = ("+") + } + { FreqDown Button + MoveScaled(9.1429,5.1429,6.4286,5.1429) + Enabled = False + Text = ("-") + } + { Button2 Button + MoveScaled(44,7,29.1429,5.1429) + Enabled = False + Text = ("Device Information") + } + { BtnTakeShot Button + MoveScaled(44,1,29.1429,5.1429) + Enabled = False + Text = ("Take a shot") + } + } + { Panel1 Panel + MoveScaled(1,30,74,7) + Border = Border.Raised + { Label6 Label + MoveScaled(1.1429,1.1429,12,4) + Text = ("Device:") + } + { TxtDevice TextBox + MoveScaled(14,1,29,5) + Expand = True + Text = ("/dev/video0") + } + { Button1 Button + MoveScaled(44,1,29,5) + Text = ("Capture") + } + } + } +} diff --git a/app/examples/Multimedia/MyWebCam/camera.png b/app/examples/Multimedia/MyWebCam/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..84b228200af8f4e1cc179f2cb6d2ae75e404cf5c GIT binary patch literal 5363 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/WaveGenerator/.directory b/app/examples/Multimedia/WaveGenerator/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/WaveGenerator/.icon.png b/app/examples/Multimedia/WaveGenerator/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a778a55df6f9b359f982fb7cfa071d0720a555bc GIT binary patch literal 4098 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 A-T(jq literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/WaveGenerator/.lang/ru.po b/app/examples/Multimedia/WaveGenerator/.lang/ru.po new file mode 100644 index 00000000..18102b25 --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.lang/ru.po @@ -0,0 +1,106 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Multimedia/WaveGenerator/.project:15 +msgid "WaveGenerator" +msgstr "Генератор волн" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.class:69 +msgid "Hz" +msgstr "Гц" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:5 +msgid "Wave generator" +msgstr "Генератор волн" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:14 +msgid "Frequency" +msgstr "Частота" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:26 +msgid "Volume" +msgstr "Громкость" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Sine" +msgstr "Синусусоида" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Square" +msgstr "Квадрат" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Saw" +msgstr "Пила" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Triangle" +msgstr "Треугольник" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Silence" +msgstr "Тишина" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "White uniform noise" +msgstr "Белый однообразный шум" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Pink noise" +msgstr "Розовый шум" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Sine table" +msgstr "Таблица синусов" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Periodic ticks" +msgstr "Периодические тики" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "White gaussian noise" +msgstr "Белый гауссов шум" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Red brownian noise" +msgstr "Красный броуновский шум" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Blue noise" +msgstr "Синий шум" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:45 +msgid "Violet noise" +msgstr "Фиолетовый шум" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:53 +msgid "440 Hz" +msgstr "440 Гц" + +#: app/examples/Multimedia/WaveGenerator/.src/FMain.form:58 +msgid "Play / Stop" +msgstr "Играть / Остановить" + diff --git a/app/examples/Multimedia/WaveGenerator/.project b/app/examples/Multimedia/WaveGenerator/.project new file mode 100644 index 00000000..6818e88c --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.project @@ -0,0 +1,14 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=WaveGenerator +Startup=FMain +Icon=audio-headphones.png +Version=3.8.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.media +TabSize=2 +Packager=1 +Translate=1 diff --git a/app/examples/Multimedia/WaveGenerator/.src/FMain.class b/app/examples/Multimedia/WaveGenerator/.src/FMain.class new file mode 100644 index 00000000..0336f048 --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.src/FMain.class @@ -0,0 +1,102 @@ +' Gambas class file + +Private $bPlay As Boolean +Private $hAudio As MediaControl +Private $hConv As MediaControl +Private $hPlayer As MediaPipeline +Private $hOutput As MediaControl +Private $hFilter As MediaControl +Private $hEncoder As MediaControl + +Public Sub Form_Open() + + ' audiotestsrc is-live=1 ! + ' audio / x - raw, channels = 2, rate = 48000!flacenc!filesink + ' location = generated.flac + + $hPlayer = New MediaPipeline As "Pipeline" + + $hAudio = New MediaControl($hPlayer, "audiotestsrc") + $hAudio["is-live"] = True + + $hFilter = New MediaControl($hPlayer, "audio/x-raw,channels=2,rate=48000") + 'Print $hFilter["caps"] + + '$hEncoder = New MediaControl($hPlayer, "flacenc") + + + $hConv = New MediaControl($hPlayer, "audioconvert") + $hOutput = New MediaControl($hPlayer, "autoaudiosink") + ' + + '$hOutput = New MediaControl($hPlayer, "filesink") + '$hOutput["location"] = User.Home &/ "output.flac" + + '$hAudio.LinkTo($hConv) + '$hConv.LinkTo($hOutput) + + Media.Link($hAudio, $hFilter, $hConv, $hOutput) + + SetFreq(440) + +End + +Public Sub Form_Close() + + $hPlayer.Stop + +End + + +Public Sub btnPlay_Click() + + $bPlay = Not $bPlay + + If $bPlay Then + $hPlayer.Play + Else + $hPlayer.Pause + Endif + +End + +Private Sub SetFreq(fFreq As Float) + + $hAudio["freq"] = fFreq + Object.Lock(sldFrequency) + sldFrequency.Value = (Log(fFreq) - Log(20)) / (Log(20000) - Log(20)) * 1000 + Object.Unlock(sldFrequency) + lblFrequency.Text = Format(fFreq, "0.000") & " " & ("Hz") + +End + +Public Sub sldFrequency_Change() + + SetFreq(Exp(Log(20) + (Log(20000) - Log(20)) * sldFrequency.Value / 1000)) + +End + +Public Sub sldVolume_Change() + + $hAudio["volume"] = sldVolume.Value / 1000 + +End + +Public Sub cmbType_Click() + + $hAudio["wave"] = cmbType.Index + +End + +Public Sub Pipeline_State() + + Debug Last.State + +End + + +Public Sub Pipeline_End() + + Debug + +End diff --git a/app/examples/Multimedia/WaveGenerator/.src/FMain.form b/app/examples/Multimedia/WaveGenerator/.src/FMain.form new file mode 100644 index 00000000..ba04af4a --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.src/FMain.form @@ -0,0 +1,61 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,72,20) + Text = ("Wave generator") + Icon = Picture["audio-headphones.png"] + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { Panel1 HBox + MoveScaled(2,2,68,4) + { Label1 Label + MoveScaled(0,0,11,4) + Text = ("Frequency") + } + { sldFrequency Slider + MoveScaled(14,0,53,4) + Expand = True + MaxValue = 1000 + } + } + { Panel2 HBox + MoveScaled(2,7,68,4) + { Label2 Label + MoveScaled(0,0,11,4) + Text = ("Volume") + } + { sldVolume Slider + MoveScaled(14,0,53,4) + Expand = True + MaxValue = 1000 + Value = 800 + } + } + { Panel5 Panel + MoveScaled(31,12,13,1) + Expand = True + } + { Panel3 HBox + MoveScaled(2,14,68,4) + Spacing = True + { cmbType ComboBox + MoveScaled(0,0,27,4) + ReadOnly = True + List = [("Sine"), ("Square"), ("Saw"), ("Triangle"), ("Silence"), ("White uniform noise"), ("Pink noise"), ("Sine table"), ("Periodic ticks"), ("White gaussian noise"), ("Red brownian noise"), ("Blue noise"), ("Violet noise")] + } + { Panel4 Panel + MoveScaled(28,0,3,4) + Expand = True + } + { lblFrequency Label + MoveScaled(27,0,13,4) + Text = ("440 Hz") + Alignment = Align.Right + } + { btnPlay Button + MoveScaled(41,0,27,4) + Text = ("Play / Stop") + } + } +} diff --git a/app/examples/Multimedia/WaveGenerator/audio-headphones.png b/app/examples/Multimedia/WaveGenerator/audio-headphones.png new file mode 100644 index 0000000000000000000000000000000000000000..7d16771fa0e36a2a2e8b5b5957bf207f8d438d18 GIT binary patch literal 2578 zcmV+t3hniYP)mQn? z`C&-c_ejUN_?-fCl**V>)&V7R<{+4x>(e-|>FnxSe7>cHG=Q;K_9<{g6tky*a|Ema za4MBBzNzlYcDogNt2JnqulA#L+gBPu4A}5ZgHA~YFyJW$Jj=SP1lG?k#hM`A4Y99w z06Ks$00|%$z<2yXIBjx-T@s`(mlV3Pe$tUPK4TvTl z5~)1w2Gd_$y~zgvOdih4mTJXL4L%06BoyO=Z{6|I68}j6iUAh@WT*3vDgnkh0BXsS z;(uSsJlB;vdT8CcU30Fx{+^2$FCGd6gM|Qix|5G-`;_1CGgvhwY8XFHJ2oH=v2v9XaB78Tz7@WX3UYTNbRf%nX+swzF*Ne&_R z*|TS5Rn@!0+E*CvX;a&^>$TmvGF{&LGd7B4QQvLF3$z>SkZ z7r}~(ij7%WS%3Ka^Uu-H&@ic~si`B~$z6%&%$YNF-n@Bj1qB7rb^ZRAUV3Th>z9=) zR{{Wb?%a8}y1JS+Y}jxTfYXOGq&l_!pWc0GW4 z0FR|R*_gHgSXf?O-h9ig^Sy0tZJS+M*2Uc1+|b&!Yw5Y?o_qW5Wy{J8)B4n3;vf9Z zweidu`s~zh~&5zea z_w0R(w!iW+H8(d?X=&+<($Z1@KvQF5!>NW-lsGZzSwJe)DukE-05vr=SigS#rxPbm zM3yVd0n=bvrb-xoN4m2IaA)kBMIsT$>+!;I9GRV+jn%7HAFZjW0RW7jFky^kT77Qb zk^*pSCul^Cd;nTeQPCd@3JNB%*q$yC!4~!y^laKStS5;TlTt!T z2_d*9rNe}BE5hM0EX#rr0#U<&<2aB~QZnb60KkYwi3<+Sc~9XYkq9{FV2nZ6bpYtuDY*UuYi}b>)6mh;0V0BBSqKCI;M^i9CD8Z!A+rD&2Drr`I490IEXzV95`mO5o(lme zj*q`=5xj8WLQz{=8zUm*<>i5h5Q#)!Sr(b53DpfCE1C0506;`8(=@veS<52MISYrw z@&6$d3NZjn;^Qyt0HxHdU@!8}>QX&SU=hU5S5!9C7TMA9nuz71aqR}V}!+>R3&~+U`*gys!0svT7C=5k}Xfz7W zIa-=qux!&+6}XyRo_aNw_o@q>_CmChLGcG>HhDbLhGb!!U+)o)G{H6Q*IH zv$GRI2vGNnR+l9|5P+us0E7@dYXO1!kIy&&rfI-5<7-wgfCQZz48T9pd!y3>z%UG0 zrUkm3K*j;EZJW5o$uNv$00_Xp1_SUB5?1**0M0ptBZ!oWy{MJ30F>i6j0+ASL^1#j z-9;V{z@PfyiUANpKspk!J40sx=a~S&5yI{+SaJXW@Y-Mi0N~z40C8i|k?~96jP8K$ zn;^TVbNc~!F!7Ip0K6B2AY&asoO3wRfszX7eyuH20Z7NTVaKX3grEcf4+Gde8~||$ zS)B97ZQEu-2uP{mNT)x5On6${69Z#R*|q~=+a!d5ZQIN+jAzk(?C!0DrOZ-F!ke9)jow#)pB~D_2*iM$N>u7ClE%f{S-^_YRNeKX8 z=Bycum)==&(B~Ul0bn~a>q+r60NZ^&-%|?;Z#k4ZA-5O+mMvSBIuAv+rQ=PwtJ9*Q o@5E~iqZ-wyMm4HYjVg`$FEg;6C=gT&f&c&j07*qoM6N<$f>E&KaR2}S literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/WebCam/.directory b/app/examples/Multimedia/WebCam/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/WebCam/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/WebCam/.icon.png b/app/examples/Multimedia/WebCam/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..97c27e7ffd7533ed15bbc52401313fe66e6169e2 GIT binary patch literal 4272 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 SuD5Cc0000, YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-31 05:33+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Multimedia/WebCam/.project:17 +msgid "V4L2 Compatible Webcam" +msgstr "V4L2-совместимая веб-камера" + +#: app/examples/Multimedia/WebCam/.project:18 app/examples/Multimedia/WebCam/.src/FMain.form:46 +msgid "Encryptec WebCam Viewer" +msgstr "Просмотрщик веб-камеры Encryptec" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:28 +msgid "devName" +msgstr "Имя_устройства" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:36 +msgid "currentTime" +msgstr "текущее время" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:56 +msgid "Picture Size" +msgstr "Разрешение" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:61 +msgid "160 x 120" +msgstr "160 x 120" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:61 app/examples/Multimedia/WebCam/.src/FDevice.form:62 +msgid "320 x 240" +msgstr "320 x 240" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:61 +msgid "640 x 480" +msgstr "640 x 480" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:69 +msgid "Brightness" +msgstr "Яркость" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:76 +msgid "Contrast" +msgstr "Контраст" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:83 +msgid "Colour" +msgstr "Цвет" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:90 +msgid "Hue" +msgstr "Тон" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:97 +msgid "Whiteness" +msgstr "Белизна" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:101 +msgid "&Hide" +msgstr "Скрыть" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:106 +msgid "&Close" +msgstr "Закрыть" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:111 +msgid "Refresh Rate" +msgstr "Част. обновл." + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:121 +msgid "&Pause" +msgstr "Пауза" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:127 +msgid "&Play" +msgstr "Игра" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:132 +msgid "&Snap" +msgstr "Скрин" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:139 +msgid "0.00 fps" +msgstr "0.00 кадр/с" + +#: app/examples/Multimedia/WebCam/.src/FDevice.form:143 +msgid "&Reset" +msgstr "Сброс" + +#: app/examples/Multimedia/WebCam/.src/FMain.class:26 +msgid "Searching:" +msgstr "Поиск:" + +#: app/examples/Multimedia/WebCam/.src/FMain.class:46 +msgid "Found" +msgstr "Найдено" + +#: app/examples/Multimedia/WebCam/.src/FMain.class:46 +msgid "devices" +msgstr "устройств" + +#: app/examples/Multimedia/WebCam/.src/FMain.class:112 +msgid "Settings Saved!" +msgstr "Настройки сохранены!" + +#: app/examples/Multimedia/WebCam/.src/FMain.form:14 +msgid "WebCam Options" +msgstr "Параметры веб-камеры" + +#: app/examples/Multimedia/WebCam/.src/FMain.form:19 +msgid "&Scan for Devices" +msgstr "Сканирование устройств" + +#: app/examples/Multimedia/WebCam/.src/FMain.form:24 +msgid "Save Settings" +msgstr "Сохранить настройки" + +#: app/examples/Multimedia/WebCam/.src/FMain.form:29 +msgid "&Quit" +msgstr "Выход" + +#: app/examples/Multimedia/WebCam/.src/FMain.form:51 +msgid "(c) Encryptec Limited 2009" +msgstr "(c) Encryptec Limited 2009" + +#: app/examples/Multimedia/WebCam/.src/FMain.form:58 +msgid "Scanning for Devices " +msgstr "Сканирование устройств " + +#: app/examples/Multimedia/WebCam/.src/FDevice.class:14 app/examples/Multimedia/WebCam/.src/FDevice.class:106 app/examples/Multimedia/WebCam/.src/FDevice.class:118 +msgid "Camera_&1" +msgstr "Камера_&1" + +#: app/examples/Multimedia/WebCam/.src/FDevice.class:154 +msgid "fps" +msgstr "кадр/с" + +#: app/examples/Multimedia/WebCam/.src/FDevice.class:162 +msgid "Can't recover picture from Camera!" +msgstr "Не удаётся восстановить изображение с камеры!" + +#: app/examples/Multimedia/WebCam/.src/FDevice.class:248 +msgid "** Paused **" +msgstr "** Приостановлено **" + +#: app/examples/Multimedia/WebCam/.src/FDevice.class:264 +msgid "Saved as" +msgstr "Сохранено как" + +#: app/examples/Multimedia/WebCam/.src/FDevice.class:264 +msgid "Snapshot was not saved" +msgstr "Скриншот не был сохранён" + diff --git a/app/examples/Multimedia/WebCam/.project b/app/examples/Multimedia/WebCam/.project new file mode 100644 index 00000000..a08bddfa --- /dev/null +++ b/app/examples/Multimedia/WebCam/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=V4L2 Compatible Webcam +Description="Encryptec WebCam Viewer" +Startup=FMain +Icon=camera.png +Version=3.8.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.settings +Component=gb.v4l +Environment="GB_GUI=gb.gtk" +TabSize=2 +Packager=1 +Translate=1 diff --git a/app/examples/Multimedia/WebCam/.src/FDevice.class b/app/examples/Multimedia/WebCam/.src/FDevice.class new file mode 100644 index 00000000..4b56a94f --- /dev/null +++ b/app/examples/Multimedia/WebCam/.src/FDevice.class @@ -0,0 +1,304 @@ +' Gambas class file + +' +' This form / class is created for each webcam found on the system. +' +Private $device As VideoDevice +Private $date_format As String +Private $index As Integer +Private $fps As Integer +Private $fps_now As Date + +Public Sub Save() + + Dim key As String = Subst(("Camera_&1"), $index) + + Settings[key & "/top"] = Me.Top + Settings[key & "/left"] = Me.Left + Settings[key & "/refrate"] = refrate.Value + Settings[key & "/size"] = picture_size.Index + 1 + Settings[key & "/brightness"] = slider_bright.Value + Settings[key & "/contrast"] = slider_contrast.Value + Settings[key & "/color"] = slider_colour.Value + Settings[key & "/hue"] = slider_hue.Value + Settings[key & "/whiteness"] = slider_whiteness.Value + +End + +Public Sub _new(dev As VideoDevice, i As Integer) + + $index = i + $device = dev + $date_format = "ddd, d mmm hh:nn:ss" + $fps_now = Now() + $fps = 0 + + slider_bright.MinValue = $device.BrightMin + slider_bright.MaxValue = $device.BrightMax + slider_contrast.MinValue = $device.ContrastMin + slider_contrast.MaxValue = $device.ContrastMax + slider_colour.MinValue = $device.ColorMin + slider_colour.MaxValue = $device.ColorMax + slider_hue.MinValue = $device.HueMin + slider_hue.MaxValue = $device.HueMax + slider_whiteness.MinValue = $device.WhitenessMin + slider_whiteness.MaxValue = $device.WhitenessMax + +End + +Public Sub ResizeDevice() + + Dim $spacing As Integer + + $spacing = 2 + + pic.Width = $device.Width + pic.Height = $device.Height + + If pic.Width < 220 Then pic.Width = 220 + If pic.Height < 140 Then pic.Height = 140 + + title.Left = $spacing + title.top = $spacing + title.Width = pic.Width + + devName.Width = pic.Width - buttonSettings.Width - buttonSettings.Left - $spacing * 6 + currentTime.Width = devName.Width + + container.top = $spacing * 2 + container.left = $spacing * 2 + container.width = pic.Width + 4 + container.Height = pic.Height + $spacing * 3 + title.Height + + pic.Left = 2 + pic.Top = title.top + title.Height + $spacing + + devName.Text = $device.Card + currentTime.Text = Format(Now(), $date_format) + + outer.Left = 0 + outer.Top = 0 + outer.Width = container.Width + $spacing * 4 + outer.Height = container.Height + $spacing * 4 + + If tools.Visible Then + outer.Height = outer.Height + tools.Height + 2 + tools.Width = container.Width + tools.Left = $spacing * 2 + tools.Top = $spacing * 4 + pic.Top + pic.Height + picture_size.Width = tools.Width - picture_size.Left - $spacing * 4 + slider_bright.Width = tools.Width - slider_bright.Left - $spacing * 6 + slider_contrast.Width = tools.Width - slider_contrast.Left - $spacing * 6 + slider_colour.Width = tools.Width - slider_colour.Left - $spacing * 6 + slider_hue.Width = tools.Width - slider_hue.Left - $spacing * 6 + slider_whiteness.Width = tools.Width - slider_whiteness.left - $spacing * 6 + refrate.Width = tools.Width - refrate.Left - $spacing * 6 + + Endif + + Me.Width = outer.Width + Me.Height = outer.Height + +End + +Public Sub Form_Show() + + Dim key As String = Subst(("Camera_&1"), $index) + + Me.Left = Settings[key & "/left", $index * 100] + Me.Top = settings[key & "/top", 0] + + Refresh.Delay = refrate.Value + Refresh.Start + +End + +Public Sub Form_Open() + + Dim key As String = Subst(("Camera_&1"), $index) + + Me.Left = Settings[key & "/left", $index * 100] + Me.Top = settings[key & "/top", 0] + + picture_size.Index = Settings[key & "/size", 1] - 1 + picture_size_Click() + ResizeDevice() + Wait 0.1 + + slider_bright.Value = Settings[key & "/brightness", $device.Bright] + slider_contrast.Value = Settings[key & "/contrast", $device.Contrast] + slider_colour.Value = Settings[key & "/color", $device.Color] + slider_hue.Value = Settings[key & "/hue", $device.Hue] + slider_whiteness.Value = Settings[key & "/whiteness", $device.Whiteness] + refrate.Value = Settings[key & "/refrate", 100] + + $device.Bright = slider_bright.Value + $device.Contrast = slider_contrast.Value + $device.Color = slider_colour.Value + $device.Hue = slider_hue.Value + $device.Whiteness = slider_whiteness.Value + +End + +Public Sub Refresh_Timer() + + Dim when As Date + Dim at As Date + + at = Now() + Refresh.Stop + + $fps += 1 + when = at - $fps_now + If Second(when) >= 3 Then + frame_rate.Text = Format($fps / 3, "#.00 " & ("fps")) + $fps = 0 + $fps_now = at + Endif + + currentTime.Text = Format(at, $date_format) + Try pic.Picture = $device.Image.Picture + If Error Then + Message(("Can't recover picture from Camera!")) + Else + Wait + Try Refresh.Delay = refrate.Value + Try Refresh.Start + Endif + +End + +Public Sub picture_size_Click() + + Select picture_size.Index + Case 0 + $device.Resize(160, 120) + Case 1 + $device.Resize(320, 240) + Case 2 + $device.Resize(640, 480) + + End Select + ResizeDevice() + +End + +Public Sub slider_bright_Change() + + $device.Bright = slider_bright.Value + +End + +Public Sub slider_contrast_Change() + + $device.Contrast = slider_contrast.Value + +End + +Public Sub slider_colour_Change() + + $device.Color = slider_colour.Value + +End + +Public Sub hue_Change() + + $device.Hue = slider_hue.Value + +End + +Public Sub whiteness_Change() + + $device.Whiteness = slider_whiteness.Value + +End + +Public Sub refrate_Change() + + Refresh.Stop + Refresh.Delay = refrate.Value + Refresh.Start + +End + +Public Sub button_hide_Click() + + tools.Hide() + ResizeDevice() + +End + +Public Sub button_close_Click() + + button_hide_Click() + Refresh.Stop + Wait 0.2 + Refresh.Stop + Me.Hide + +End + +Public Sub button_pause_Click() + + button_pause.Hide + button_play.Show + Refresh.Stop + Wait + Refresh.Stop + frame_rate.Text = ("** Paused **") + +End + +Public Sub button_play_Click() + + button_pause.Show + button_play.Hide + Refresh.Start + +End + +Public Sub button_snap_Click() + + If Dialog.SaveFile() Then Return + Try $device.Save(Dialog.Path) + If Not Error Then Message(("Saved as") & " (" & Dialog.Path & ")") Else Message(("Snapshot was not saved")) + +End + +Public Sub buttonSettings_Click() + + If tools.Visible Then + tools.Hide + Else + tools.Show + Endif + ResizeDevice() + + Select $device.Width + Case 160 + picture_size.Index = 0 + Case 320 + picture_size.Index = 1 + Case 640 + picture_size.Index = 2 + End Select + +End + +Public Sub button_reset_Click() + + slider_colour.Value = $device.ColorDefault + slider_bright.Value = $device.BrightDefault + slider_contrast.Value = $device.ContrastDefault + slider_whiteness.Value = $device.WhitenessDefault + slider_hue.Value = $device.HueDefault + +End + +Public Sub Form_Close() + + Refresh.Stop + $device.Close + $device = Null + +End diff --git a/app/examples/Multimedia/WebCam/.src/FDevice.form b/app/examples/Multimedia/WebCam/.src/FDevice.form new file mode 100644 index 00000000..cfb588ec --- /dev/null +++ b/app/examples/Multimedia/WebCam/.src/FDevice.form @@ -0,0 +1,149 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,71,104) + Font = Font["Bitstream Vera Sans"] + Icon = Picture["camera.png"] + Border = False + { outer Panel + MoveScaled(3.4286,1.7143,64,95.1429) + Background = &HBFFFFF& + Foreground = &H000000& + Border = Border.Sunken + { container Panel + MoveScaled(5.1429,2.5714,53.7143,34.2857) + Background = Color.Background + Border = Border.Sunken + { pic PictureBox + MoveScaled(6,10.2857,26.5714,20.5714) + Alignment = Align.Center + } + { title Panel + MoveScaled(1.7143,0.8571,34.2857,4.5714) + Background = Color.ButtonForeground + { devName Label + MoveScaled(6,0,26.5714,2.5714) + Font = Font["Bitstream Vera Sans Mono,-1"] + Foreground = Color.TextBackground + Text = ("devName") + Alignment = Align.Left + } + { currentTime Label + MoveScaled(6,2,24.8571,2) + Font = Font["Bitstream Vera Sans Mono,-2"] + Background = Color.Foreground + Foreground = Color.TextBackground + Text = ("currentTime") + Alignment = Align.Left + } + { buttonSettings Button + MoveScaled(0.5714,0.5714,4.2857,3.4286) + Picture = Picture["settings.png"] + } + } + } + { Refresh #Timer + #MoveScaled(32,25) + } + { tools Panel + MoveScaled(3,40,53.7143,38.5714) + Visible = False + Font = Font["-1"] + Background = Color.Background + Border = Border.Sunken + { Label1 Label + MoveScaled(2,2,13,3) + Text = ("Picture Size") + } + { picture_size ComboBox + MoveScaled(16,1,17,4) + ReadOnly = True + List = [("160 x 120"), ("320 x 240"), ("640 x 480")] + Text = ("320 x 240") + } + { slider_bright Slider + MoveScaled(16,5,16,3.5714) + } + { Label2 Label + MoveScaled(2,5,13,3) + Text = ("Brightness") + } + { slider_contrast Slider + MoveScaled(16,8,16,3.5714) + } + { Label3 Label + MoveScaled(2,8,13,3) + Text = ("Contrast") + } + { slider_colour Slider + MoveScaled(16,11,16,3.5714) + } + { Label4 Label + MoveScaled(2,11,13,3) + Text = ("Colour") + } + { slider_hue Slider + MoveScaled(16,14,16,3.5714) + } + { Label5 Label + MoveScaled(2,14,13,3) + Text = ("Hue") + } + { slider_whiteness Slider + MoveScaled(16,17,16,3.5714) + } + { Label6 Label + MoveScaled(2,17,13,3) + Text = ("Whiteness") + } + { button_hide Button + MoveScaled(0,27,10,4) + Text = ("&Hide") + Picture = Picture["icon:/16/apply"] + } + { button_close Button + MoveScaled(0,32,10,4) + Text = ("&Close") + Picture = Picture["icon:/16/close"] + } + { Label7 Label + MoveScaled(2,20,13,3) + Text = ("Refresh Rate") + } + { refrate Slider + MoveScaled(16,20,16,3.5714) + MinValue = 20 + MaxValue = 1999 + Value = 200 + } + { button_pause Button + MoveScaled(11,27,10,4) + Text = ("&Pause") + Picture = Picture["icon:/16/pause"] + } + { button_play Button + MoveScaled(11,27,10,4) + Visible = False + Text = ("&Play") + Picture = Picture["icon:/16/play"] + } + { button_snap Button + MoveScaled(11,32,10,4) + Text = ("&Snap") + Picture = Picture["icon:/16/camera"] + } + { frame_rate Label + MoveScaled(2,23,30,3) + Font = Font["-1"] + Foreground = Color.SelectedBackground + Text = ("0.00 fps") + } + { button_reset Button + MoveScaled(22,27,10,4) + Text = ("&Reset") + Picture = Picture["icon:/16/apply"] + Default = True + } + } + } +} diff --git a/app/examples/Multimedia/WebCam/.src/FMain.class b/app/examples/Multimedia/WebCam/.src/FMain.class new file mode 100644 index 00000000..746731f5 --- /dev/null +++ b/app/examples/Multimedia/WebCam/.src/FMain.class @@ -0,0 +1,126 @@ +' Gambas class file + +' +' WebCam +' Demo program to show off the Gambas V4L module +' (c) Gareth Bult, Encryptec Ltd 2009 +' +' License: as-is +' +Private $windows As Form[] + +Public Sub ScanDevices() + + Dim i As Integer + Dim dev As VideoDevice + Dim myWin As Form + Dim count As Integer + Dim item As Menu + Dim status As String + Dim sDevice As String + + $windows = New Form[] + count = 0 + Me.Show + + status = ("Searching:") & " " + For i = 0 To 20 + sDevice = "/dev/video" & Str(i) + Print sDevice + Try dev = New VideoDevice(sDevice) + If Not Error Then + status &= "!" + myWin = New FDevice(dev, count) + $windows.Add(myWin) + item = New Menu(tooltray_popup) As "Camera" + item.Text = dev.Card + item.Picture = Picture["icon:/16/camera"] + item.Tag = i + count += 1 + Else + status &= "." + Endif + Wait 0.1 + text_status.Text = status + Next + text_status.Text = ("Found") & " (" & Str($windows.Count) & ") " & ("devices") + Wait 2 + Me.Hide + + For Each myWin In $windows + myWin.Show + Next + +End + +Public Sub menu_scan_Click() + + Dim item As Menu + Dim items As Menu[] + + While $windows.Count > 0 + $windows.Pop().Close + Wend + $windows.Clear + + items = New Menu[] + For Each item In tooltray_popup.Children + If item.Tag <> "X" + items.Add(item) + Endif + Next + + For Each item In items + item.Delete + Next + + ScanDevices() + +End + +Public Sub Camera_Click() + + $windows[Last.Tag].Show + $windows[Last.Tag].SetFocus + +End + +Public Sub tooltray_Menu() + + tooltray_popup.Popup + +End + +Public Sub menu_quit_Click() + + While $windows.Count > 0 + $windows.Pop().Close + Wend + Me.Close + 'tooltray.Hide + +End + +Public Sub menu_save_Click() + + Dim mywin As FDevice + + For Each myWin In $windows + myWin.Save() + Next + Settings.Save + Message(("Settings Saved!")) + +End + +Public Sub Form_Open() + + Me.Center + +End + +Public Sub Form_Activate() + + If Not $windows Then ScanDevices + +End diff --git a/app/examples/Multimedia/WebCam/.src/FMain.form b/app/examples/Multimedia/WebCam/.src/FMain.form new file mode 100644 index 00000000..8d689ecf --- /dev/null +++ b/app/examples/Multimedia/WebCam/.src/FMain.form @@ -0,0 +1,62 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,42,18) + Background = Color.SelectedBackground + Icon = Picture["camera.png"] + Border = False + Arrangement = Arrange.Fill + Margin = True + Padding = 4 + { tooltray_popup Menu + Tag = "X" + { Menu3 Menu + Text = ("WebCam Options") + Enabled = False + Tag = "X" + } + { menu_scan Menu + Text = ("&Scan for Devices") + Picture = Picture["icon:/16/find"] + Tag = "X" + } + { menu_save Menu + Text = ("Save Settings") + Picture = Picture["icon:/16/floppy"] + Tag = "X" + } + { menu_quit Menu + Text = ("&Quit") + Picture = Picture["icon:/16/quit"] + Tag = "X" + } + { Menu1 Menu + Tag = "X" + } + } + { Panel1 Panel + MoveScaled(0.7143,0,40.4286,17) + Background = Color.LightBackground + Arrangement = Arrange.Vertical + Margin = True + Border = Border.Sunken + { TextLabel1 TextLabel + MoveScaled(2,1,36,9) + Font = Font["Impact,+4"] + Text = ("Encryptec WebCam Viewer") + } + { TextLabel2 TextLabel + MoveScaled(2,10,36,3) + Foreground = Color.SelectedBackground + Text = ("(c) Encryptec Limited 2009") + Alignment = Align.Center + } + { text_status TextLabel + MoveScaled(2,12,36,4) + Font = Font["-1"] + Foreground = &H600080& + Text = ("Scanning for Devices ") & "..." + Alignment = Align.Left + } + } +} diff --git a/app/examples/Multimedia/WebCam/camera.png b/app/examples/Multimedia/WebCam/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c343ec895f7bde3361df4cf2f8ebf819fc92e8 GIT binary patch literal 1027 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 Z@qcqIe_a>Ugr5Ka002ovPDHLkV1hp@x1#_6 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ClientSocket/.lang/ca.po b/app/examples/Networking/ClientSocket/.lang/ca.po new file mode 100644 index 00000000..a58fa3ed --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/ca.po @@ -0,0 +1,127 @@ +# Catalan translation of ClientSocket +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the ClientSocket package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: ClientSocket\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Exemple de Sòcol Client" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "S'ha connectat al camí" + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "S'ha connectat a l'ordinador remot" + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr "- Fent servir l'adreça local:" + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Connexió tancada per un ordinador aliè" + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Orinador trobat. Connectant..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "El sistema no permet crear un sòcol" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "No s'ha trobat l'ordinador" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "No s'ha pogut connectar. Connexió rebutjada" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "S'ha produït un error en llegir les dades" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "S'ha produït un error en escriure les dades" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "cercant el nom d'ordinador..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Temps d'espera probant d'establir la connexió" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Connexió tancada per l'usuari" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Exemple de sòcol client" + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Connecta" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Tanca" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "Servidor:" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "Port :" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Envia dades" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Escriviu aquí les dades per enviar, després premeu \"Envia dades\"" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Seleccioneu el protocol:" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Dades rebudes :" diff --git a/app/examples/Networking/ClientSocket/.lang/cs.po b/app/examples/Networking/ClientSocket/.lang/cs.po new file mode 100644 index 00000000..40be43ef --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/cs.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Příklad Soket Klienta" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "Připojeno na cestu " + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "Připojen ke vzdálenému hostiteli " + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr " - Použita lokální adresa : " + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Připojení ukončeno cizím hostem." + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Host nalezen. Připojování..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "Tento systém neumožňuje vytvořit socket" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "Host nenalezen" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "Nelze se připojit. Spojení odmítnuto" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "Chyba čtení dat" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "Chyba zápisu dat" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "Vyhledávám hostitele..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Timeout snažím založit připojení" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Připojení ukončeno uživatelem" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Příklad coket klienta" + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Připojit" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Zavřít" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "-" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "-" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Pošli data" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Zde zapiš data, pošli zmáčknutím \"Poslat data\"" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Vyber protokol :" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Příjmané data :" diff --git a/app/examples/Networking/ClientSocket/.lang/de.po b/app/examples/Networking/ClientSocket/.lang/de.po new file mode 100644 index 00000000..2e085c9d --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/de.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Client-Socket-Beispiel" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "Zu einem Pfad verbinden" + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "Zu einem Remote Host verbinden" + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr "Lokale Adresse verwenden:" + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Verbindung vom Host getrennt" + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Host gefunden. Verbinde..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "Das System verhindert, einen Socket zu erstellen" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "Host nicht gefunden" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "Kann nicht verbinden: Verbindung abgelehnt" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "Fehler beim Lesen der Daten" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "Fehler beim Schreiben der Daten" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "Schlage Hostname nach..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Timeout bei der Verbindungsherstellung" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Verbindung vom Benutzer getrennt" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Client-Socket-Beispiel" + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Verbinden" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Schließen" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "-" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "-" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Daten senden" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Zu sendende Daten eingeben" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Protokoll wählen:" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Daten empfangen:" diff --git a/app/examples/Networking/ClientSocket/.lang/es.po b/app/examples/Networking/ClientSocket/.lang/es.po new file mode 100644 index 00000000..4ecb347f --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/es.po @@ -0,0 +1,63 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FrmMain.class:245 +msgid "Client socket example " +msgstr "Ejemplo de Cliente de puerto" + +#: FrmMain.class:250 +msgid "Connect" +msgstr "Conectar" + +#: FrmMain.class:266 +msgid "Close" +msgstr "Cerrar" + +#: FrmMain.class:271 +msgid "localhost" +msgstr "localhost" + +#: FrmMain.class:276 +msgid "Host :" +msgstr "Servidor :" + +#: FrmMain.class:281 +msgid "Port :" +msgstr "Puerto :" + +#: FrmMain.class:286 +msgid "7" +msgstr "7" + +#: FrmMain.class:291 +msgid "Data Received :" +msgstr "Datos recibidos :" + +#: FrmMain.class:303 +msgid "Send Data" +msgstr "Enviar datos" + +#: FrmMain.class:308 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Escriba aquí los datos a enviar, luego presione \"Enviar datos\"" + +#: FrmMain.class:313 +msgid "Select protocol :" +msgstr "Seleccione un protocolo :" + +#: FrmMain.class:320 +msgid "TCP" +msgstr "TCP" + +#: FrmMain.class:320 +msgid "UNIX" +msgstr "UNIX" diff --git a/app/examples/Networking/ClientSocket/.lang/nl.po b/app/examples/Networking/ClientSocket/.lang/nl.po new file mode 100644 index 00000000..7286efe2 --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/nl.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-22 00:27+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Client Socket Voorbeeld" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Client socket voorbeeld " + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Verbind" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Sluiten" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "-" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "Poort :" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Verzend Data" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Noteer hier data om te verzenden, druk vervolgens \"Verzend Data\"" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Selecteer protocol :" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Data ontvangen :" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "Verbonden met pad" + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "Verbonen met afgelegen host" + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr " - Using lokaal adres : " + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Connectie gesloten door vreemde host." + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Host gevonden. Verbinding maken..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "Het systeem staat niet toe een socket te creëren" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "Host niet gevonden" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "Onmogelijk om te verbinden. Verbinding geweigert" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "Fout bij het lezen van data" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "Fout bij schrijven van data" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "Host naam opzoeken..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Timeout tijdens het trachten een connectie te maken" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Connectie gesloten door gebruiker" + diff --git a/app/examples/Networking/ClientSocket/.lang/ru.po b/app/examples/Networking/ClientSocket/.lang/ru.po new file mode 100644 index 00000000..317b58c7 --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/ru.po @@ -0,0 +1,148 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/ClientSocket/.project:21 +msgid "Client Socket Example" +msgstr "Пример сокета клиента" + +#: app/examples/Networking/ClientSocket/.project:22 +msgid "" +"Client socket example.\n" +"\n" +"This example shows how to use a socket to connect to a server. You should use the \"ServerSocket\" example as a testing peer." +msgstr "" +"Пример сокета клиента.\n" +"\n" +"В этом примере показано, как использовать сокет для подключения к серверу. Вы должны использовать пример «Сокет сервера» в качестве тестового партнёра." + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:26 +msgid "Connected to path " +msgstr "Подключено к пути " + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:28 +msgid "Connected to remote host " +msgstr "Подключен к удалённому хосту " + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:29 +msgid " - Using local address : " +msgstr " - Используя локальный адрес: " + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Соединение закрыто сторонним хостом." + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Хост найден. Соединение..." + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "Система не позволяет создать сокет" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:86 +msgid "Host not Found" +msgstr "Хост не найден" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "Невозможно подключиться. В соединении отказано" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:90 +msgid "Error Reading Data" +msgstr "Ошибка чтения данных" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:92 +msgid "Error Writing Data" +msgstr "Ошибка записи данных" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:158 +msgid "Looking up host name..." +msgstr "Поиск имени хоста..." + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Тайм-аут при попытке укрепить соединение" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Соединение закрыто пользователем" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:224 app/examples/Networking/ClientSocket/.src/FrmMain.form:27 +msgid "Host :" +msgstr "Хост:" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.class:228 +msgid "Path :" +msgstr "Путь:" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:5 +msgid "Client socket example " +msgstr "Пример сокета клиента " + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:10 +msgid "Connect" +msgstr "Соединить" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:19 +msgid "Close" +msgstr "Закрыть" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:23 +msgid "localhost" +msgstr "localhost" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:31 +msgid "Port :" +msgstr "Порт:" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:35 +msgid "32340" +msgstr "32340" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:44 +msgid "Send Data" +msgstr "Отправить данные" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:48 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Напишите здесь данные для отправки, затем нажмите «Отправить данные»" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:52 +msgid "Select protocol :" +msgstr "Протокол:" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:57 +msgid "TCP" +msgstr "TCP" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:57 +msgid "UNIX" +msgstr "UNIX" + +#: app/examples/Networking/ClientSocket/.src/FrmMain.form:70 +msgid "Data Received :" +msgstr "Данные получены:" + diff --git a/app/examples/Networking/ClientSocket/.project b/app/examples/Networking/ClientSocket/.project new file mode 100644 index 00000000..dbf9a2f1 --- /dev/null +++ b/app/examples/Networking/ClientSocket/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Client Socket Example +Startup=FrmMain +Icon=socket.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.net +Description="Client socket example.\n\nThis example shows how to use a socket to connect to a server. You should use the \"ServerSocket\" example as a testing peer." +Authors="Daniel Campos Fernández\nBenoît Minisini" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/ClientSocket/.src/FrmMain.class b/app/examples/Networking/ClientSocket/.src/FrmMain.class new file mode 100644 index 00000000..d8cd1f3f --- /dev/null +++ b/app/examples/Networking/ClientSocket/.src/FrmMain.class @@ -0,0 +1,231 @@ +' Gambas class file + +'******************************************** +' THIS EXAMPLE SHOWS HOW TO USE THE 'Socket' +' CLASS FROM 'gb.net' COMPONENT +' +' (C) 2003-2004 Daniel Campos Fernández +' (danielcampos@netcourrier.com) +'******************************************** +Private CurProtocol As Integer + +Public Sub Form_Open() + + CurProtocol = 0 + +End + +Public Sub MySocket_Ready() + '*********************************** + ' When connection proccess has finished + ' successfully, "Connected" event will raise + '*********************************** + + timTimeout.Enabled = False + If CurProtocol = 1 Then + lblMessage.Text = ("Connected to path ") & MySocket.Path + Else + lblMessage.Text = ("Connected to remote host ") & MySocket.Path + lblMessage.Text = lblMessage.Text & (" - Using local address : ") & MySocket.LocalHost & ":" & MySocket.LocalPort + End If + Me.Enabled = True + Set_Interface(True) + +End + +Public Sub MySocket_Closed() + '******************************************** + ' this event will raise when foreing host + ' closes the socket by any reason + '******************************************** + + lblMessage.Caption = ("Connection Closed by foreign host.") + Me.Enabled = True + Set_Interface(False) + +End + +Public Sub MySocket_Found() + '******************************************** + ' this event will raise when foreing host + ' name has been translated to IP + '******************************************** + + lblMessage.Caption = ("Host Found. Connecting...") + +End + +Public Sub MySocket_Read() + '**************************************** + ' When some data arrives from the remote + ' part of the socket, "DataAvailable" event + ' is raised + '**************************************** + + Dim S As String + + If MySocket.Status = Net.Connected Then + Read #MySocket, S, Lof(MySocket) + txtReceive.Pos = txtReceive.Length + txtReceive.Insert(S) + End If + +End + +Public Sub MySocket_Error() + '********************************** + ' this is the function to + ' handle Errors when trying + ' to read or write to the socket + '********************************** + + Select Case MySocket.Status + Case Net.CannotCreateSocket + lblMessage.Text = ("The system does not allow to create a socket") + Case Net.HostNotFound + lblMessage.Text = ("Host not Found") + Case Net.ConnectionRefused + lblMessage.Text = ("Unable to Connect. Connection Refused") + Case Net.CannotRead + lblMessage.Text = ("Error Reading Data") + Case Net.CannotWrite + lblMessage.Text = ("Error Writing Data") + End Select + Set_Interface(False) + +End + +Private Sub Set_Interface(bState As Boolean) + '**************************************** + ' An auxiliar method to enable/disable + ' controls in the formulary , when + ' connection is stablished or closed + '**************************************** + + btnConnect.Enabled = Not bState + txtHost.Enabled = Not bState + txtPort.Enabled = Not bState + cmbProtocol.Enabled = Not bState + Label5.Enabled = Not bState + btnClose.Enabled = bState + timTimeout.Enabled = bState + 'TextArea1.Enabled=bState + btnSend.Enabled = bState + txtSend.Enabled = bState + + If bState Then + txtReceive.Text = "" + txtReceive.SetFocus + Endif + +End + +Public Sub btnConnect_Click() + + '****************************************** + ' To connect to remote host we call to + ' connectsocket method, passing Host Name + ' and port as arguments + '****************************************** + Dim RetVal As Integer + + btnConnect.Enabled = False + Select Case CurProtocol + Case 0 + ' Stablishing a TCP connection. + ' Here we use Host and + ' Port properties, we could also + ' do directly MySock.Connect(TextBox1.Text,VAL(TextBox2.Text)) + MySocket.Host = txtHost.Text + MySocket.Port = Val(txtPort.Text) + MySocket.Connect() + Case 1 + ' Stablishing a Local connection. + ' Here we use Path and + ' Port properties, we could also + ' do directly MySock.Connect(TextBox1.Text,0) + MySocket.Path = txtHost.Text + MySocket.Port = Net.Local + MySocket.Connect() + End Select + If MySocket.Status > Net.Inactive Then + If CurProtocol = 0 Then + '************************** + ' TCP : connection in progress... + '************************** + btnClose.Enabled = True + 'ME.Enabled=FALSE + lblMessage.Text = ("Looking up host name...") + timTimeout.Delay = 10000 ' we'll wait a maximun time of 10 seconds + timTimeout.Enabled = True + End If + End If + +End + +Public Sub timTimeout_Timer() + + '*************************** + ' timeout trying to connect + '*************************** + Me.Enabled = True + timTimeout.Enabled = False + If MySocket.Status <> Net.Connected Then + Close MySocket + Set_Interface(False) + lblMessage.Text = ("Timeout trying to stablish connection") + End If + +End + +Public Sub btnClose_Click() + + '********************************** + ' Here we close the connection + ' to remote host + '********************************** + Close MySocket + Set_Interface(False) + lblMessage.Text = ("Connection closed by user") + +End + +Public Sub btnSend_Click() + '**************************************************** + ' Here we send data to the remote part of the socket. + ' We have to be sure that connection is active + '**************************************************** + + If MySocket.Status = Net.Connected Then + Write #MySocket, txtSend.Text, Len(txtSend.Text) + txtSend.Text = "" + End If + +End + +Public Sub Form_Close() + + '********************* + ' Close possible Stablished connections + '********************* + If MySocket.Status > 0 Then Close #MySocket + +End + +Public Sub cmbProtocol_Click() + '************************************************** + ' Here we select protocol to use : TCP,UDP or UNIX + '************************************************** + + CurProtocol = cmbProtocol.Index + If CurProtocol = 0 Then + Label2.Visible = True + txtPort.Visible = True + Label1.Text = ("Host :") + Else + Label2.Visible = False + txtPort.Visible = False + Label1.Text = ("Path :") + End If + +End diff --git a/app/examples/Networking/ClientSocket/.src/FrmMain.form b/app/examples/Networking/ClientSocket/.src/FrmMain.form new file mode 100644 index 00000000..4c049648 --- /dev/null +++ b/app/examples/Networking/ClientSocket/.src/FrmMain.form @@ -0,0 +1,75 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34,23.4286,83,60) + Text = ("Client socket example ") + Icon = Picture["socket.png"] + Resizable = False + { btnConnect Button + MoveScaled(64,7,17,4) + Text = ("Connect") + } + { txtReceive TextArea + MoveScaled(1,44,81,15) + ReadOnly = True + } + { btnClose Button + MoveScaled(64,12,17,4) + Enabled = False + Text = ("Close") + } + { txtHost TextBox + MoveScaled(14,7,49,4) + Text = ("localhost") + } + { Label1 Label + MoveScaled(1,7,12,4) + Text = ("Host :") + } + { Label2 Label + MoveScaled(1,12,12,4) + Text = ("Port :") + } + { txtPort TextBox + MoveScaled(14,12,49,4) + Text = ("32340") + } + { txtSend TextArea + MoveScaled(1,27,81,6) + Enabled = False + } + { btnSend Button + MoveScaled(13,34,23,4) + Enabled = False + Text = ("Send Data") + } + { Label4 Label + MoveScaled(1,24,81,3) + Text = ("Write here data to send, then press \"Send Data\"") + } + { Label5 Label + MoveScaled(1,1,17,4) + Text = ("Select protocol :") + } + { cmbProtocol ComboBox + MoveScaled(19,1,63,4) + ReadOnly = True + List = [("TCP"), ("UNIX")] + } + { lblMessage TextLabel + MoveScaled(1,17,81,6) + Font = Font["Bold"] + Alignment = Align.Center + Border = Border.Sunken + } + { timTimeout #Timer + #MoveScaled(26,47) + } + { Label3 Label + MoveScaled(1,40,81,4) + Text = ("Data Received :") + } + { MySocket #Socket + #MoveScaled(38,35) + } +} diff --git a/app/examples/Networking/ClientSocket/socket.png b/app/examples/Networking/ClientSocket/socket.png new file mode 100644 index 0000000000000000000000000000000000000000..bf16a70ad3137ebc3e6c1a9e01f81d4f07d97ffe GIT binary patch literal 2200 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 wc(nk, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: DnsClient\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:09+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Assincrònic" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Canceŀla" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Canceŀlat" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Copia" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "Exemple de client Dns" + +#: .project:1 +msgid "DNS client example" +msgstr "Exemple de client DNS" + +#: FMain.class:217 +msgid "Finished" +msgstr "Acabat" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:34 +msgid "Go!" +msgstr "Endavant!" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Nom de domini a IP" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP a nom de domini" + +#: FMain.class:14 +msgid "Not Found" +msgstr "No s'ha trobat" + +#: FMain.class:295 +msgid "Write here 7 host names then press GO!" +msgstr "Escriu 7 noms d'ordinador i pitja Endavant!" + +#: FMain.class:297 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Escriu 7 adreces ip i pitja Endavant!" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mageia.org" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + diff --git a/app/examples/Networking/DnsClient/.lang/cs.po b/app/examples/Networking/DnsClient/.lang/cs.po new file mode 100644 index 00000000..0bd1585c --- /dev/null +++ b/app/examples/Networking/DnsClient/.lang/cs.po @@ -0,0 +1,92 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DNS client example" +msgstr "Příklad DNS klienta" + +#: FMain.class:14 +msgid "Not Found" +msgstr "Nenalezeno" + +#: FMain.class:217 +msgid "Finished" +msgstr "Dokončeno" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Zrušené" + +#: FMain.class:295 +msgid "Write here 7 host names then press GO!" +msgstr "Napiš zde 7 host jmen a pak zmačkni GO!" + +#: FMain.class:297 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Napiš zde 7 IP adres a pak zmačni GO!" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "Příklad DnsKlienta" + +#: FMain.form:34 +msgid "Go!" +msgstr "Go!" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Zrušit" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mageia.org" +msgstr "-" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Host name z IP" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP z Host name" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Koírovat" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Asynchronně" diff --git a/app/examples/Networking/DnsClient/.lang/de.po b/app/examples/Networking/DnsClient/.lang/de.po new file mode 100644 index 00000000..79726ee6 --- /dev/null +++ b/app/examples/Networking/DnsClient/.lang/de.po @@ -0,0 +1,93 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DNS client example" +msgstr "Beispiel für einen DNS-Client" + +#: FMain.class:14 +msgid "Not Found" +msgstr "Nicht gefunden" + +#: FMain.class:217 +msgid "Finished" +msgstr "Fertig" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Abgebrochen" + +#: FMain.class:295 +msgid "Write here 7 host names then press GO!" +msgstr "Gib 7 Hostnamen ein und klicke auf Los!" + +#: FMain.class:297 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Gib 7 IP-Adressen ein und klicke auf Los!" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "Beispiel für einen DNS-Client" + +#: FMain.form:34 +msgid "Go!" +msgstr "Los!" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Abbrechen" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mageia.org" +msgstr "-" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Host Name nach IP-Adresse" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP-Adresse nach Host Name" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Kopieren" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Asynchron" + diff --git a/app/examples/Networking/DnsClient/.lang/es.po b/app/examples/Networking/DnsClient/.lang/es.po new file mode 100644 index 00000000..2df0d745 --- /dev/null +++ b/app/examples/Networking/DnsClient/.lang/es.po @@ -0,0 +1,67 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.class:356 +msgid "DnsClient Example" +msgstr "Ejemplo de Cliente DNS" + +#: FMain.class:361 +msgid "Go!" +msgstr "¡Ir!" + +#: FMain.class:371 +msgid "gambas.sourceforge.net" +msgstr "gambas.sourceforge.net" + +#: FMain.class:376 +msgid "www.kudla.org" +msgstr "www.kudla.org" + +#: FMain.class:381 +msgid "www.freshmeat.net" +msgstr "www.freshmeat.net" + +#: FMain.class:422 +msgid "Cancel" +msgstr "Cancelar" + +#: FMain.class:427 +msgid "www.slashdot.org" +msgstr "www.slashdot.org" + +#: FMain.class:432 +msgid "www.rpmfind.net" +msgstr "www.rpmfind.net" + +#: FMain.class:437 +msgid "www.debian.org" +msgstr "www.debian.org" + +#: FMain.class:442 +msgid "www.mageia.org" +msgstr "www.mageia.org" + +#: FMain.class:481 +msgid "Host Name to IP" +msgstr "Nombre de servidor a IP" + +#: FMain.class:481 +msgid "IP to Host Name" +msgstr "IP a nombre de servidor" + +#: FMain.class:486 +msgid "<< Copy" +msgstr "<< Copiar" + +#: FMain.class:491 +msgid "Asynchronous" +msgstr "Asincrónimo" diff --git a/app/examples/Networking/DnsClient/.lang/nl.po b/app/examples/Networking/DnsClient/.lang/nl.po new file mode 100644 index 00000000..70de5745 --- /dev/null +++ b/app/examples/Networking/DnsClient/.lang/nl.po @@ -0,0 +1,92 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-22 00:24+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DNS client example" +msgstr "DNS cliënt voorbeeld" + +#: FMain.class:14 +msgid "Not Found" +msgstr "Niet gevonden" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "DnsCliënt Voorbeeld" + +#: FMain.form:34 +msgid "Go!" +msgstr "-" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Annuleer" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mageia.org" +msgstr "-" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Host naam naar IP" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP naar Host naam" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Kopiëer" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Asynchrone" + +#: FMain.class:217 +msgid "Finished" +msgstr "Beëindigt" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Geannuleerd" + +#: FMain.class:279 +msgid "Write here 7 host names then press GO!" +msgstr "Noteer hier 7 host namen en druk GO!" + +#: FMain.class:281 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Noteer hier 7 IP adressen en druk GO!" + diff --git a/app/examples/Networking/DnsClient/.lang/ru.po b/app/examples/Networking/DnsClient/.lang/ru.po new file mode 100644 index 00000000..76f7246a --- /dev/null +++ b/app/examples/Networking/DnsClient/.lang/ru.po @@ -0,0 +1,98 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-06-04 19:30 UTC\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: .project:1 +msgid "DNS client example" +msgstr "Пример DNS-клиента" + +#: FMain.class:14 +msgid "Not Found" +msgstr "Не найдено" + +#: FMain.class:217 +msgid "Finished" +msgstr "Готово" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Отменено" + +#: FMain.class:279 +msgid "Write here 7 host names then press GO!" +msgstr "Напишите здесь 7 имён хостов и нажмите 'Вперёд'!" + +#: FMain.class:281 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Напишите здесь 7 IP-адресов и нажмите 'Вперёд'!" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "Пример DNS-клиента" + +#: FMain.form:34 +msgid "Go!" +msgstr "Вперёд!" + +#: FMain.form:90 +msgid "Cancel" +msgstr "Отмена" + +#: FMain.form:144 +msgid "Host Name to IP" +msgstr "Имя хоста по IP" + +#: FMain.form:144 +msgid "IP to Host Name" +msgstr "IP к имени хоста" + +#: FMain.form:150 +msgid "<< Copy" +msgstr "<< Копировать" + +#: FMain.form:155 +msgid "Asynchronous" +msgstr "Асинхронный" + +#~ msgid "www.mageia.org" +#~ msgstr "www.mageia.org" + +#~ msgid "www.debian.org" +#~ msgstr "www.debian.org" + +#~ msgid "www.rpmfind.net" +#~ msgstr "www.rpmfind.net" + +#~ msgid "www.slashdot.org" +#~ msgstr "www.slashdot.org" + +#~ msgid "www.freshmeat.net" +#~ msgstr "www.freshmeat.net" + +#~ msgid "www.kudla.org" +#~ msgstr "www.kudla.org" + +#~ msgid "gambas.sourceforge.net" +#~ msgstr "gambas.sourceforge.net" diff --git a/app/examples/Networking/DnsClient/.project b/app/examples/Networking/DnsClient/.project new file mode 100644 index 00000000..731be02f --- /dev/null +++ b/app/examples/Networking/DnsClient/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=DNS client example +Startup=FMain +Icon=dnsclient.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.net +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@desnouettes +License=General Public Licence +Packager=1 diff --git a/app/examples/Networking/DnsClient/.src/FMain.class b/app/examples/Networking/DnsClient/.src/FMain.class new file mode 100644 index 00000000..146aa10a --- /dev/null +++ b/app/examples/Networking/DnsClient/.src/FMain.class @@ -0,0 +1,308 @@ +' Gambas class file +'*********************************** +' We define 7 dns objects +'*********************************** +Private A As DnsClient +Private B As DnsClient +Private C As DnsClient +Private D As DnsClient +Private E As DnsClient +Private F As DnsClient +Private G As DnsClient + +Private Working As Integer +Private sNoResult As String = ("Not Found") + +Public Sub Button1_Click() + Label2.Text = "" + Label3.Text = "" + Label4.Text = "" + Label6.Text = "" + Label7.Text = "" + Label8.Text = "" + Label9.Text = "" + Working = 7 + Button1.Enabled = False + Button1.Enabled = False + ComboBox1.Enabled = False + Button3.Enabled = False + CheckBox1.Enabled = False + Label5.Text = "|" + Timer1.Delay = 200 + Timer1.Enabled = True + Select Case ComboBox1.Index + Case 0 + A.HostName = TextBox1.Text + B.HostName = TextBox2.Text + C.HostName = TextBox3.Text + D.HostName = TextBox4.Text + E.HostName = TextBox5.Text + F.HostName = TextBox6.Text + G.HostName = TextBox7.Text + A.GetHostIP() + B.GetHostIP() + C.GetHostIP() + D.GetHostIP() + E.GetHostIP() + F.GetHostIP() + G.GetHostIP() + Case 1 + A.HostIP = TextBox1.Text + B.HostIP = TextBox2.Text + C.HostIP = TextBox3.Text + D.HostIP = TextBox4.Text + E.HostIP = TextBox5.Text + F.HostIP = TextBox6.Text + G.HostIP = TextBox7.Text + A.GetHostName() + B.GetHostName() + C.GetHostName() + D.GetHostName() + E.GetHostName() + F.GetHostName() + G.GetHostName() + End Select + +End + +Public Sub A_Finished() + + Select Case ComboBox1.index + Case 0 + If A.HostIP = "" Then + Label2.Text = sNoResult + Else + Label2.Text = A.HostIP + End If + Case 1 + If A.HostName = "" Then + Label2.Text = sNoResult + Else + Label2.Text = A.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub B_Finished() + + Select Case ComboBox1.index + Case 0 + If B.HostIP = "" Then + Label3.Text = sNoResult + Else + Label3.Text = B.HostIP + End If + Case 1 + If B.HostName = "" Then + Label3.Text = sNoResult + Else + Label3.Text = B.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub C_Finished() + + Select Case ComboBox1.index + Case 0 + If C.HostIP = "" Then + Label4.Text = sNoResult + Else + Label4.Text = C.HostIP + End If + Case 1 + If C.HostName = "" Then + Label4.Text = sNoResult + Else + Label4.Text = C.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub D_Finished() + + Select Case ComboBox1.index + Case 0 + If D.HostIP = "" Then + Label6.Text = sNoResult + Else + Label6.Text = D.HostIP + End If + Case 1 + If D.HostName = "" Then + Label6.Text = sNoResult + Else + Label6.Text = D.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub E_Finished() + + Select Case ComboBox1.index + Case 0 + If E.HostIP = "" Then + Label7.Text = sNoResult + Else + Label7.Text = E.HostIP + End If + Case 1 + If E.HostName = "" Then + Label7.Text = sNoResult + Else + Label7.Text = E.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub F_Finished() + + Select Case ComboBox1.index + Case 0 + If F.HostIP = "" Then + Label8.Text = sNoResult + Else + Label8.Text = F.HostIP + End If + Case 1 + If F.HostName = "" Then + Label8.Text = sNoResult + Else + Label8.Text = F.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub G_Finished() + + Select Case ComboBox1.index + Case 0 + If G.HostIP = "" Then + Label9.Text = sNoResult + Else + Label9.Text = G.HostIP + End If + Case 1 + If G.HostName = "" Then + Label9.Text = sNoResult + Else + Label9.Text = G.HostName + End If + End Select + Working = working - 1 + +End + + + + + + +Public Sub Timer1_Timer() + + Timer1.Enabled = False + If Working = 0 Then + Label5.Text = ("Finished") + Button1.Enabled = True + Button3.Enabled = True + ComboBox1.Enabled = True + CheckBox1.Enabled = True + Else + Select Case Label5.Text + Case "|" + Label5.Text = "/" + Case "/" + Label5.Text = "-" + Case "-" + Label5.Text = "\\" + Case "\\" + Label5.Text = "|" + End Select + Timer1.Delay = 200 + Timer1.Enabled = True + + End If + +End + +Public Sub Button2_Click() + + If Timer1.Enabled = True Then + Timer1.Enabled = False + A.Stop() + B.Stop() + C.Stop() + D.Stop() + E.Stop() + F.Stop() + G.Stop() + Label5.Text = ("Cancelled") + Working = 0 + Button1.Enabled = True + ComboBox1.Enabled = True + Button3.Enabled = True + CheckBox1.Enabled = True + End If + +End + +Public Sub Form_Open() + + A = New DnsClient As "A" + B = New DnsClient As "B" + C = New DnsClient As "C" + D = New DnsClient As "D" + E = New DnsClient As "E" + F = New DnsClient As "F" + G = New DnsClient As "G" + CheckBox1_Click + ComboBox1_Click + Working = 0 +End + +Public Sub ComboBox1_Click() + + Select Case ComboBox1.Index + Case 0 + Label1.Text = ("Write here 7 host names then press GO!") + Case 1 + Label1.Text = ("Write here 7 IP addresses then press GO!") + End Select +End + +Public Sub Button3_Click() + + TextBox1.Text = Label2.Text + TextBox2.Text = Label3.Text + TextBox3.Text = Label4.Text + TextBox4.Text = Label6.Text + TextBox5.Text = Label7.Text + TextBox6.Text = Label8.Text + TextBox7.Text = Label9.Text + + +End + +Public Sub CheckBox1_Click() + + A.ASync = CheckBox1.Value + B.ASync = CheckBox1.Value + C.ASync = CheckBox1.Value + D.ASync = CheckBox1.Value + E.ASync = CheckBox1.Value + F.ASync = CheckBox1.Value + G.ASync = CheckBox1.Value + +End diff --git a/app/examples/Networking/DnsClient/.src/FMain.form b/app/examples/Networking/DnsClient/.src/FMain.form new file mode 100644 index 00000000..8e0f157a --- /dev/null +++ b/app/examples/Networking/DnsClient/.src/FMain.form @@ -0,0 +1,119 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(26,20,63,51) + Text = ("DnsClient Example") + Resizable = False + { Button1 Button + MoveScaled(1,45,16,4) + Text = ("Go!") + } + { Label1 Label + MoveScaled(1,6,61,4) + } + { TextBox1 TextBox + MoveScaled(1,11,28,4) + #Translate = False + Text = "gambas.sourceforge.net" + } + { TextBox2 TextBox + MoveScaled(1,15,28,4) + #Translate = False + Text = "www.kudla.org" + } + { TextBox3 TextBox + MoveScaled(1,19,28,4) + #Translate = False + Text = "www.freshmeat.net" + } + { Label2 Label + MoveScaled(30,11,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + } + { Label3 Label + MoveScaled(30,15,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + } + { Label4 Label + MoveScaled(30,19,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + } + { Timer1 #Timer + #MoveScaled(25.625,37.625) + } + { Label5 Label + MoveScaled(18,45,27,4) + Font = Font["12,Bold"] + Background = &HAA0000& + Foreground = &HFFFFFF& + Alignment = Align.Center + } + { Button2 Button + MoveScaled(46,45,16,4) + Text = ("Cancel") + } + { TextBox4 TextBox + MoveScaled(1,23,28,4) + #Translate = False + Text = "www.slashdot.org" + } + { TextBox5 TextBox + MoveScaled(1,27,28,4) + #Translate = False + Text = "www.rpmfind.net" + } + { TextBox6 TextBox + MoveScaled(1,31,28,4) + #Translate = False + Text = "www.debian.org" + } + { TextBox7 TextBox + MoveScaled(1,35,28,4) + #Translate = False + Text = "www.mageia.org" + } + { Label6 Label + MoveScaled(30,23,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + } + { Label7 Label + MoveScaled(30,27,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + } + { Label8 Label + MoveScaled(30,31,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + } + { Label9 Label + MoveScaled(30,35,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + } + { ComboBox1 ComboBox + MoveScaled(1,1,37,4) + ReadOnly = True + List = [("Host Name to IP"), ("IP to Host Name")] + Text = (" ") + } + { Button3 Button + MoveScaled(18,40,23,4) + Text = ("<< Copy") + } + { CheckBox1 CheckBox + MoveScaled(41,1,21,4) + Text = ("Asynchronous") + } +} diff --git a/app/examples/Networking/DnsClient/dnsclient.png b/app/examples/Networking/DnsClient/dnsclient.png new file mode 100644 index 0000000000000000000000000000000000000000..5c2ca2807566beed571e49547ce8f3e792b3848b GIT binary patch literal 1808 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: HTTPGet\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:10+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Autenticació" + +#: F.form:82 +msgid "Basic" +msgstr "Bàsic" + +#: F.class:243 +msgid "Cancelled by user" +msgstr "Canceŀlat per l'usuari" + +#: FConfig.form:65 +msgid "Close" +msgstr "Tanca" + +#: F.form:91 +msgid "&Configuration..." +msgstr "&Configuració..." + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Connectat, s'està esperant resposta..." + +#: F.class:101 +msgid "Connecting..." +msgstr "S'està connectant..." + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Fitxer de galetes (si el deixeu en blanc, l'HttpClient no gestionarà les galetes)" + +#: F.form:82 +msgid "DIGEST" +msgstr "-" + +#: F.class:188 +msgid "Error " +msgstr "-" + +#: F.form:24 +msgid "GET!" +msgstr "OBTÉ!" + +#: F.form:82 +msgid "GSS" +msgstr "-" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "Exemple GET de client HTTP" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:82 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.class:203 +msgid "OK" +msgstr "D'acord" + +#: F.form:62 +msgid "Page needs authorization" +msgstr "La pàgina necessita autorització" + +#: F.form:74 FConfig.form:73 +msgid "Password" +msgstr "Contrasenya" + +#: FConfig.form:25 +msgid "Plain" +msgstr "Pla" + +#: F.class:157 +msgid "Receiving data..." +msgstr "S'està rebent dades..." + +#: F.form:57 +msgid "Stop" +msgstr "Atura" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Actualitza el fitxer de galetes" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Usa servidor intermediari" + +#: F.form:68 FConfig.form:80 +msgid "User" +msgstr "Usuari" + diff --git a/app/examples/Networking/HTTPGet/.lang/cs.po b/app/examples/Networking/HTTPGet/.lang/cs.po new file mode 100644 index 00000000..c21950c1 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/cs.po @@ -0,0 +1,121 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "Příklad GET klienta" + +#: F.class:101 +msgid "Connecting..." +msgstr "Připojování..." + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Připojeno, čekám na odpověď..." + +#: F.class:158 +msgid "Receiving data..." +msgstr "Přijímám data..." + +#: F.class:162 +msgid "&1 bytes" +msgstr "-" + +#: F.class:195 +msgid "Error " +msgstr "Chyba" + +#: F.class:210 +msgid "OK" +msgstr "-" + +#: F.class:250 +msgid "Cancelled by user" +msgstr "Zrušeno uživatelem" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:48 +msgid "Stop" +msgstr "-" + +#: F.form:53 +msgid "Page needs authorization" +msgstr "Autorizace na stránce" + +#: F.form:59 FConfig.form:80 +msgid "User" +msgstr "Uživatel" + +#: F.form:65 FConfig.form:73 +msgid "Password" +msgstr "Heslo" + +#: F.form:73 +msgid "Basic" +msgstr "-" + +#: F.form:73 +msgid "DIGEST" +msgstr "-" + +#: F.form:73 +msgid "GSS" +msgstr "-" + +#: F.form:73 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:82 +msgid "&Configuration..." +msgstr "&Konfigurace..." + +#: FConfig.form:25 +msgid "Plain" +msgstr "-" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Užít proxy" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Aktualizuj soubor cookie" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Soubor cookie (když nezaškrknete tak HttpClient nezpracuje cookie)" + +#: FConfig.form:65 +msgid "Close" +msgstr "Zavřít" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Autorizace" + diff --git a/app/examples/Networking/HTTPGet/.lang/de.po b/app/examples/Networking/HTTPGet/.lang/de.po new file mode 100644 index 00000000..baf3c9c0 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/de.po @@ -0,0 +1,117 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "Beispiel für HTTP-Client GET" + +#: F.class:101 +msgid "Connecting..." +msgstr "Verbinden" + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Verbunden, warte auf Antwort..." + +#: F.class:157 +msgid "Receiving data..." +msgstr "Empfange Daten..." + +#: F.class:188 +msgid "Error " +msgstr "Fehler" + +#: F.class:203 +msgid "OK" +msgstr "-" + +#: F.class:243 +msgid "Cancelled by user" +msgstr "Durch Benutzer abgebrochen" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:57 +msgid "Stop" +msgstr "-" + +#: F.form:62 +msgid "Page needs authorization" +msgstr "Seite verlangt Authentifizierung" + +#: F.form:68 FConfig.form:80 +msgid "User" +msgstr "Benutzer" + +#: F.form:74 FConfig.form:73 +msgid "Password" +msgstr "Passwort" + +#: F.form:82 +msgid "Basic" +msgstr "Einfach" + +#: F.form:82 +msgid "DIGEST" +msgstr "-" + +#: F.form:82 +msgid "GSS" +msgstr "-" + +#: F.form:82 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:91 +msgid "&Configuration..." +msgstr "&Konfiguration..." + +#: FConfig.form:25 +msgid "Plain" +msgstr "Einfach" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Proxy verwenden" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Cookie-Datei neu laden" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Cookie-Datei (wenn leer, verwaltet der HTTP-Client keine Cookies)" + +#: FConfig.form:65 +msgid "Close" +msgstr "Schließen" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Authentifizierung" + diff --git a/app/examples/Networking/HTTPGet/.lang/en_GB.po b/app/examples/Networking/HTTPGet/.lang/en_GB.po new file mode 100644 index 00000000..e1dc855c --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/en_GB.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "-" + +#: F.class:101 +msgid "Connecting..." +msgstr "-" + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "-" + +#: F.class:158 +msgid "Receiving data..." +msgstr "-" + +#: F.class:162 +msgid "&1 bytes" +msgstr "-" + +#: F.class:195 +msgid "Error " +msgstr "-" + +#: F.class:210 +msgid "OK" +msgstr "-" + +#: F.class:250 +msgid "Cancelled by user" +msgstr "-" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:48 +msgid "Stop" +msgstr "-" + +#: F.form:53 +msgid "Page needs authorization" +msgstr "-" + +#: F.form:59 FConfig.form:80 +msgid "User" +msgstr "-" + +#: F.form:65 FConfig.form:73 +msgid "Password" +msgstr "-" + +#: F.form:73 +msgid "Basic" +msgstr "-" + +#: F.form:73 +msgid "DIGEST" +msgstr "-" + +#: F.form:73 +msgid "GSS" +msgstr "-" + +#: F.form:73 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:82 +msgid "&Configuration..." +msgstr "-" + +#: FConfig.form:25 +msgid "Plain" +msgstr "-" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "-" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "-" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "-" + +#: FConfig.form:65 +msgid "Close" +msgstr "-" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "-" diff --git a/app/examples/Networking/HTTPGet/.lang/es.po b/app/examples/Networking/HTTPGet/.lang/es.po new file mode 100644 index 00000000..3f8781f1 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/es.po @@ -0,0 +1,91 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FConfig.class:106 +msgid "Plain" +msgstr "Plano" + +#: FConfig.class:106 F.class:382 +msgid "NTLM" +msgstr "NTLM" + +#: FConfig.class:112 +msgid "Authentication" +msgstr "Autenticación" + +#: FConfig.class:118 F.class:367 +msgid "User" +msgstr "Usuario" + +#: FConfig.class:124 F.class:373 +msgid "Password" +msgstr "Contraseña" + +#: FConfig.class:142 +msgid "Use Proxy" +msgstr "Usar Proxy" + +#: FConfig.class:147 +msgid "127.0.0.1:3128" +msgstr "127.0.0.1:3128" + +#: FConfig.class:152 +msgid "TextBox1" +msgstr "TextBox1" + +#: FConfig.class:157 +msgid "Update cookies file" +msgstr "Actualizar achivos cookies" + +#: FConfig.class:162 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Archivos Cookies (si lo deja en blanco HttpClient no manejará cookies)" + +#: FConfig.class:167 +msgid "Close" +msgstr "Cerrar" + +#: F.class:322 +msgid "GET!" +msgstr "¡Obtener!" + +#: F.class:337 +msgid "URL :" +msgstr "URL :" + +#: F.class:342 +msgid "http://gambasdoc.org/help" +msgstr "http://gambasdoc.org/help" + +#: F.class:356 +msgid "Stop" +msgstr "Detener" + +#: F.class:361 +msgid "Page needs authorization" +msgstr "Página necesita autorización" + +#: F.class:382 +msgid "Basic" +msgstr "Básico" + +#: F.class:382 +msgid "DIGEST" +msgstr "DIGEST" + +#: F.class:382 +msgid "GSS" +msgstr "GSS" + +#: F.class:390 +msgid "&Configuration..." +msgstr "&Configuración..." diff --git a/app/examples/Networking/HTTPGet/.lang/nl.po b/app/examples/Networking/HTTPGet/.lang/nl.po new file mode 100644 index 00000000..5290baf2 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/nl.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-22 13:13+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "HTTP client GET voorbeeld" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:48 +msgid "Stop" +msgstr "-" + +#: F.form:53 +msgid "Page needs authorization" +msgstr "Pagina vereist authorisatie" + +#: F.form:59 FConfig.form:80 +msgid "User" +msgstr "Gebruiker" + +#: F.form:65 FConfig.form:73 +msgid "Password" +msgstr "Wachtwoord" + +#: F.form:73 +msgid "Basic" +msgstr "-" + +#: F.form:73 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:73 +msgid "DIGEST" +msgstr "-" + +#: F.form:73 +msgid "GSS" +msgstr "-" + +#: F.form:82 +msgid "&Configuration..." +msgstr "&Configuratie..." + +#: F.class:101 +msgid "Connecting..." +msgstr "Verbinding maken..." + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Verbonden, wacht op antwoord..." + +#: F.class:158 +msgid "Receiving data..." +msgstr "Ontvang data..." + +#: F.class:162 +msgid "&1 bytes" +msgstr "-" + +#: F.class:195 +msgid "Error " +msgstr "Fout" + +#: F.class:210 +msgid "OK" +msgstr "-" + +#: F.class:250 +msgid "Cancelled by user" +msgstr "Geannuleerd door gebruiker" + +#: FConfig.form:25 +msgid "Plain" +msgstr "Plat" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Gebruik Proxy" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Update cookies bestand" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Cookies bestand (indien leeg zal HTTP cliënt geen cookies beheren)" + +#: FConfig.form:65 +msgid "Close" +msgstr "Sluiten" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Autenticatie" + diff --git a/app/examples/Networking/HTTPGet/.lang/ru.po b/app/examples/Networking/HTTPGet/.lang/ru.po new file mode 100644 index 00000000..042fa962 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/ru.po @@ -0,0 +1,144 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-30 09:26+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/HTTPGet/.project:23 +msgid "HTTP client GET example" +msgstr "Пример GET клиента HTTP" + +#: app/examples/Networking/HTTPGet/.project:24 +msgid "" +"HttpClient GET request example.\n" +"\n" +"This example how to send a GET HTTP request to a server and get the response. You can configure an HTTP proxy if needed." +msgstr "" +"Пример GET-запроса HTTP-клиента.\n" +"\n" +"В этом примере показано, как отправить GET HTTP запрос на сервер и получить ответ. При необходимости вы можете настроить HTTP-прокси." + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:10 +msgid "Plain" +msgstr "Обычный" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:10 app/examples/Networking/HTTPGet/.src/FHttpGet.form:48 +msgid "NTLM" +msgstr "NTLM" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:23 +msgid "Use Proxy" +msgstr "Использовать прокси" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:27 +msgid "127.0.0.1:3128" +msgstr "127.0.0.1:3128" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:34 +msgid "Update cookies file" +msgstr "Обновить куки-файл" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:38 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Куки-файл (если вы оставите его пустым, клиент Http не будет управлять куки-файлами)" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:42 +msgid "Close" +msgstr "Закрыть" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:49 app/examples/Networking/HTTPGet/.src/FHttpGet.form:41 +msgid "Password" +msgstr "Пароль" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:55 app/examples/Networking/HTTPGet/.src/FHttpGet.form:36 +msgid "User" +msgstr "Пользователь" + +#: app/examples/Networking/HTTPGet/.src/FConfig.form:61 +msgid "Authentication" +msgstr "Аутентификация" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.class:97 +msgid "Connecting..." +msgstr "Соединение..." + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.class:138 +msgid "Connected, waiting for reply..." +msgstr "Подключено, в ожидании ответа..." + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.class:155 +msgid "Receiving data..." +msgstr "Получение данных..." + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.class:159 +msgid "&1 bytes" +msgstr "&1 байт" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.class:191 +msgid "Error " +msgstr "Ошибка " + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.class:205 +msgid "OK" +msgstr "ОК" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.class:246 +msgid "Cancelled by user" +msgstr "Отменено пользователем" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:8 +msgid "GET!" +msgstr "GET!" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:19 +msgid "URL :" +msgstr "URL :" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:23 +msgid "http://gambaswiki.org/wiki" +msgstr "http://gambaswiki.org/wiki" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:27 +msgid "Stop" +msgstr "Стоп" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:31 +msgid "Page needs authorization" +msgstr "Страница требует авторизации" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:48 +msgid "Basic" +msgstr "Основы" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:48 +msgid "DIGEST" +msgstr "DIGEST" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:48 +msgid "GSS" +msgstr "GSS" + +#: app/examples/Networking/HTTPGet/.src/FHttpGet.form:56 +msgid "&Configuration..." +msgstr "Конфигурация..." + diff --git a/app/examples/Networking/HTTPGet/.project b/app/examples/Networking/HTTPGet/.project new file mode 100644 index 00000000..70c4d5d9 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.project @@ -0,0 +1,22 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=HTTP client GET example +Startup=FHttpGet +Icon=httpclient.png +Version=1.0.0 +Component=gb.image +Component=gb.qt5 +Component=gb.net +Component=gb.net.curl +Component=gb.qt5.ext +Description="HttpClient GET request example.\n\nThis example how to send a GET HTTP request to a server and get the response. You can configure an HTTP proxy if needed." +TabSize=2 +Translate=1 +Language=fr +ControlPublic=1 +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/HTTPGet/.src/ClsParams.class b/app/examples/Networking/HTTPGet/.src/ClsParams.class new file mode 100644 index 00000000..ceea7236 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/ClsParams.class @@ -0,0 +1,8 @@ +' Gambas class file +Static Public UseProxy As Boolean +Static Public ProxyHost As String +Static Public ProxyUser As String +Static Public ProxyPwd As String +Static Public ProxyAuth As Integer +Static Public CookiesFile As String +Static Public UpdateCookies As Boolean diff --git a/app/examples/Networking/HTTPGet/.src/FConfig.class b/app/examples/Networking/HTTPGet/.src/FConfig.class new file mode 100644 index 00000000..6a740382 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FConfig.class @@ -0,0 +1,75 @@ +' Gambas class file + +Public Sub ChkProxy_Click() + + + If ChkProxy.Value = True Then + TxtProxy.Enabled = True + LblAuth.Enabled = True + LblProxyUser.Enabled = True + TxtProxyUser.Enabled = True + LblProxyPassword.Enabled = True + TxtProxyPassword.Enabled = True + CmbProxyAuth.Enabled = True + Else + TxtProxy.Enabled = False + LblAuth.Enabled = False + LblProxyUser.Enabled = False + TxtProxyUser.Enabled = False + LblProxyPassword.Enabled = False + TxtProxyPassword.Enabled = False + CmbProxyAuth.Enabled = False + End If + +End + + +Public Sub Form_Open() + + + TxtCookies.Text = ClsParams.CookiesFile + ChkUpdate.Value = ClsParams.UpdateCookies + + ChkProxy.Value = ClsParams.UseProxy + TxtProxy.Text = ClsParams.ProxyHost + TxtProxyUser.Text = ClsParams.ProxyUser + TxtProxyPassword.Text = ClsParams.ProxyPwd + + + Select Case ClsParams.ProxyAuth + Case Net.AuthBasic + CmbProxyAuth.Index = 0 + Case Net.AuthNTLM + CmbProxyAuth.Index = 1 + End Select + + +End + +Public Sub Form_Close() + + ClsParams.CookiesFile = TxtCookies.Text + ClsParams.UpdateCookies = ChkUpdate.Value + + ClsParams.UseProxy = ChkProxy.Value + ClsParams.ProxyHost = TxtProxy.Text + ClsParams.ProxyUser = TxtProxyUser.Text + ClsParams.ProxyPwd = TxtProxyPassword.Text + + If ClsParams.ProxyUser = "" And ClsParams.ProxyPwd = "" Then + ClsParams.ProxyAuth = Net.AuthNone + Else + Select Case CmbProxyAuth.Index + Case 0 + ClsParams.ProxyAuth = Net.AuthBasic + Case 1 + ClsParams.ProxyAuth = Net.AuthNTLM + End Select + End If +End + +Public Sub Button1_Click() + + Me.Close + +End diff --git a/app/examples/Networking/HTTPGet/.src/FConfig.form b/app/examples/Networking/HTTPGet/.src/FConfig.form new file mode 100644 index 00000000..13518228 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FConfig.form @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34.5714,30.7143,49,45) + Resizable = False + { CmbProxyAuth ComboBox + MoveScaled(27,6,21,4) + Enabled = False + ReadOnly = True + List = [("Plain"), ("NTLM")] + } + { TxtProxyPassword TextBox + MoveScaled(27,16,21,4) + Enabled = False + Password = True + } + { TxtProxyUser TextBox + MoveScaled(27,11,21,4) + Enabled = False + } + { ChkProxy CheckBox + MoveScaled(1,1,26,4) + Text = ("Use Proxy") + } + { TxtProxy TextBox + MoveScaled(27,1,21,4) + Text = ("127.0.0.1:3128") + } + { TxtCookies TextBox + MoveScaled(1,29,47,4) + } + { ChkUpdate CheckBox + MoveScaled(1,34,47,4) + Text = ("Update cookies file") + } + { TextLabel1 TextLabel + MoveScaled(1,22,47,7) + Text = ("Cookies file (if you leave it blank HttpClient will not manage cookies)") + } + { Button1 Button + MoveScaled(16,40,16,4) + Text = ("Close") + Default = True + Cancel = True + } + { LblProxyPassword Label + MoveScaled(1,16,26,4) + Enabled = False + Text = ("Password") + Transparent = True + } + { LblProxyUser Label + MoveScaled(1,11,26,4) + Enabled = False + Text = ("User") + Transparent = True + } + { LblAuth Label + MoveScaled(1,6,26,4) + Enabled = False + Text = ("Authentication") + Transparent = True + } +} diff --git a/app/examples/Networking/HTTPGet/.src/FHttpGet.class b/app/examples/Networking/HTTPGet/.src/FHttpGet.class new file mode 100644 index 00000000..f3b0584b --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FHttpGet.class @@ -0,0 +1,285 @@ +' Gambas class file + +'/////////////////////////////////////// +' Here we define a HttpClient object +'/////////////////////////////////////// +'PRIVATE MyHTTP AS HttpClient +'/////////////////////////////// +' This is a buffer I use when +' a link is clicked (ugly hack:) +'/////////////////////////////// +Private CurHost As String + +Public Sub Form_Open() + + '/////////////////////////////////// + ' We set Default configuration values + '/////////////////////////////////// + ClsParams.ProxyHost = "127.0.0.1:3128" + ClsParams.ProxyUser = "" + ClsParams.ProxyPwd = "" + ClsParams.ProxyAuth = Net.AuthNone + ClsParams.CookiesFile = User.Home & "/gbcookies.txt" + '//////////////////////////////////// + ' Now we create the HttpClient object + '//////////////////////////////////// + 'MyHTTP=NEW HttpClient AS "MyHTTP" + +End + +Public Sub Button1_Click() + '///////////////////////////////////////////////// + ' When we press 'Get' button we start 'getting' + ' proccess + '///////////////////////////////////////////////// + + '///////////////////////////////////////////// + ' If user has configured a proxy, we set + ' that values in HttpClient object + '///////////////////////////////////////////// + If ClsParams.UseProxy And ClsParams.ProxyHost <> "" Then + '////////////////////////////////// + ' Host address and port + '////////////////////////////////// + MyHTTP.Proxy.Host = ClsParams.ProxyHost + MyHTTP.Proxy.Type = Net.ProxyHTTP + '////////////////////////////////// + ' If Proxy needs authentication, + ' we set it + '////////////////////////////////// + If ClsParams.ProxyUser <> "" Or ClsParams.ProxyPwd <> "" Then + MyHTTP.Proxy.User = ClsParams.ProxyUser + MyHTTP.Proxy.Password = ClsParams.ProxyPwd + MyHTTP.Proxy.Auth = ClsParams.ProxyAuth + Else + '/////////////////////////////////// + ' No authentication needed + '/////////////////////////////////// + MyHTTP.Proxy.Auth = Net.AuthNone + End If + Else + '////////////////////////////////////// + ' No proxy selected (direct connection) + '////////////////////////////////////// + MyHTTP.Proxy.Host = "" + ' todo authnone + End If + + '///////////////////////////////////////////////// + ' User and Password settings to access to that + ' document (this is a HTTP server authorization, + ' not a Proxy authorization) + '///////////////////////////////////////////////// + ' 1º User and password + If ChkPassword.Value Then + MyHTTP.User = TxtUser.Text + MyHTTP.Password = TxtPassword.Text + ' 2º Authorization type + Select Case CmbAuth.Index + Case 0 + MyHTTP.Auth = Net.AuthBasic + Case 1 + MyHTTP.Auth = Net.AuthNTLM + Case 2 + MyHTTP.Auth = Net.AuthDigest + Case 3 + MyHTTP.Auth = Net.AuthGSSNEGOTIATE + End Select + Else + MyHTTP.User = "" + MyHTTP.Auth = Net.AuthNone + End If + + '////////////////////////////////// + ' A bit of feedback for user... + '////////////////////////////////// + TextArea1.Text = "" + txtInfo.Text = ("Connecting...") + Navigator.Text = "" + + '//////////////////////////////////// + ' URL to Get + '//////////////////////////////////// + MyHTTP.Url = TxtHost.Text + + '//////////////////////////////////// + ' a little buffer for me + '//////////////////////////////////// + CurHost = Mid(MyHTTP.Url, 8) + + '//////////////////////////////////// + ' Cookies + '//////////////////////////////////// + MyHTTP.CookiesFile = ClsParams.CookiesFile + MyHTTP.UpdateCookies = ClsParams.UpdateCookies + + '//////////////////////////////////// + ' Let's get it! + '//////////////////////////////////// + Print MyHTTP.Proxy.Auth + MyHTTP.Get() + + '/////////////////////////////////////////// + ' If we'd want to download remote document + ' to a file, instead of receving it in + ' memory, we could do that: + ' MyHTTP.Get( "FilePath" ) + '/////////////////////////////////////////// + +End + +Public Sub MyHTTP_Connect() + + '/////////////////////////////////////////////// + ' This event from HttpClient raises when + ' we connect successfully with remote server + ' and allows us to give more feed-back to user + '/////////////////////////////////////////////// + txtInfo.Text = ("Connected, waiting for reply...") + +End + +Public Sub MyHTTP_Read() + '/////////////////////////////////////////// + ' This event raises when a new piece of data + ' arrives to us from server, so we read that + ' part of the document + '/////////////////////////////////////////// + + Dim sBuf As String + Dim sText As String + + '///////////////////////////////// + ' more feedback... + '///////////////////////////////// + sText = ("Receiving data...") & " " + If MyHttp.TotalDownloaded Then + sText &= Format(MyHttp.Downloaded / MyHttp.TotalDownloaded, "0 %") + Else + sText &= Subst(("&1 bytes"), MyHttp.Downloaded) + Endif + txtInfo.Text = sText + + '///////////////////////////////// + ' Header of HTTP document received + ' from server + '///////////////////////////////// + If TextArea1.Text = "" Then + TextArea1.Text = MyHTTP.Headers.Join("\n") + End If + + '///////////////////////////////// + ' If really there's data to read, + ' we read it and place it in our + ' "navigator" screen + '////////////////////////////////// + If Lof(MyHTTP) Then + Read #MyHTTP, sBuf, Lof(MyHTTP) + Navigator.Text = Navigator.Text & sBuf + End If + +End + +Public Sub MyHTTP_Error() + + '//////////////////////////// + ' If something fails, this + ' event raises and connection + ' is stopped + '//////////////////////////// + CurHost = "" + txtInfo.Text = ("Error ") & MyHTTP.Status + +End + +Public Sub MyHTTP_Finished() + + '///////////////////////////////////// + ' When all document has been received, + ' this event raises + '///////////////////////////////////// + Dim sBuf As String + '////////////////////////////// + ' feeback... + '////////////////////////////// + txtInfo.Text = ("OK") + If TextArea1.Text = "" Then + TextArea1.Text = MyHTTP.Headers.Join("\n") + End If + + '/////////////////////////////////// + ' we extract all possible data + ' buffered in HttpClient + '/////////////////////////////////// + If Lof(MyHTTP) Then + Read #MyHTTP, sBuf, Lof(MyHTTP) + Navigator.Text = Navigator.Text & sBuf + End If + +End + +Public Sub mnuOptions_Click() + '//////////////////////////////////// + ' If user wants to modify parameters + '//////////////////////////////////// + + FConfig.ShowModal + +End + +Public Sub Form_Close() + + '////////////////////////////////////// + ' When program finishes, we must ensure + ' that we close HttpClient object + '/////////////////////////////////////// + MyHTTP.Stop() + +End + +Public Sub BtnStop_Click() + + '/////////////////////////////////////// + ' If user wants to close the + ' connection... + '/////////////////////////////////////// + If MyHttp.Status > 0 Then txtInfo.Text = ("Cancelled by user") + MyHTTP.Stop() + +End + +Public Function Correct_Url(sCad As String) As String + + If InStr(sCad, "://") Then Return sCad + + If Left(sCad, 1) = "/" Then + If InStr(CurHost, "/") Then CurHost = Left(CurHost, InStr(CurHost, "/") - 1) + Return CurHost & sCad + Else + If Right(CurHost, 1) = "/" Then Return CurHost & sCad + Return CurHost & "/" & sCad + End If + +End + +Public Sub Navigator_Link(Path As String) + + If MyHTTP.Status > 0 Then MyHTTP.Stop() + TxtHost.Text = Correct_Url(Path) + Button1_Click() + +End + +Public Sub ChkPassword_Click() + + If ChkPassword.Value Then + TxtUser.Enabled = True + TxtPassword.Enabled = True + CmbAuth.Enabled = True + Else + TxtUser.Enabled = False + TxtPassword.Enabled = False + CmbAuth.Enabled = False + End If + +End diff --git a/app/examples/Networking/HTTPGet/.src/FHttpGet.form b/app/examples/Networking/HTTPGet/.src/FHttpGet.form new file mode 100644 index 00000000..cc2861fe --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FHttpGet.form @@ -0,0 +1,65 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(30,14,104,73) + Resizable = False + { Button1 Button + MoveScaled(1,6,14,4) + Text = ("GET!") + } + { TextArea1 TextArea + MoveScaled(1,11,63,14) + } + { Navigator TextEdit + MoveScaled(1,26,102,46) + ReadOnly = True + } + { Label1 Label + MoveScaled(1,1,7,4) + Text = ("URL :") + } + { TxtHost TextBox + MoveScaled(8,1,56,4) + Text = ("http://gambaswiki.org/wiki") + } + { BtnStop Button + MoveScaled(16,6,14,4) + Text = ("Stop") + } + { ChkPassword CheckBox + MoveScaled(66,6,37,4) + Text = ("Page needs authorization") + } + { TxtUser TextBox + MoveScaled(66,16,37,4) + Enabled = False + Text = ("User") + } + { TxtPassword TextBox + MoveScaled(66,21,37,4) + Enabled = False + Text = ("Password") + Password = True + } + { CmbAuth ComboBox + MoveScaled(66,11,37,4) + Enabled = False + ReadOnly = True + List = [("Basic"), ("NTLM"), ("DIGEST"), ("GSS")] + Text = (" ") + } + { MyHttp #HttpClient + #MoveScaled(27,36) + } + { mnuOptions Button + MoveScaled(66,1,37,4) + Text = ("&Configuration...") + } + { txtInfo Label + MoveScaled(32,6,32,4) + Font = Font["Bold"] + Background = Color.SelectedBackground + Foreground = &HFFFF00& + Padding = 8 + } +} diff --git a/app/examples/Networking/HTTPGet/httpclient.png b/app/examples/Networking/HTTPGet/httpclient.png new file mode 100644 index 0000000000000000000000000000000000000000..613d858f2cbcc49382048288ca7a5b04e9db60be GIT binary patch literal 1768 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: HTTPPost\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:10+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.class:72 +msgid "Connecting..." +msgstr "S'està connectant..." + +#: F.class:7 +msgid "Error " +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Finalment premeu el botó Envia >>>" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "Exemple POST de client HTTP" + +#: F.form:48 +msgid "HTTP Data" +msgstr "Dades HTTP" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "Capçaleres HTTP" + +#: F.form:23 +msgid "Internet Calculator" +msgstr "Calculadora d'Internet" + +#: F.form:28 +msgid "Post" +msgstr "Envia" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Usa servidor intermediari" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Usant el poder d'Internet per tenir una mínima màquina de calcular!" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "S'està esperant resposta..." + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Escriviu aquí un altre nombre:" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Escriviu aquí un nombre:" + diff --git a/app/examples/Networking/HTTPPost/.lang/cs.po b/app/examples/Networking/HTTPPost/.lang/cs.po new file mode 100644 index 00000000..03288a56 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/cs.po @@ -0,0 +1,72 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "Příklad HTTP POST klienta" + +#: F.class:7 +msgid "Error " +msgstr "Chyba" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "Čekání na odpověď..." + +#: F.class:72 +msgid "Connecting..." +msgstr "Připojování..." + +#: F.form:23 +msgid "Internet Calculator" +msgstr "Internetová kalkulačka" + +#: F.form:28 +msgid "Post" +msgstr "-" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "HTTP Hlavička" + +#: F.form:48 +msgid "HTTP Data" +msgstr "-" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Zapiš zde číslo :" + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Zapiš zde ještě číslo :" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Nakonec zmáčkni tlačítko Post >>>" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Použití síly internetu na minimální kalkulačku!" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Použít proxy" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" diff --git a/app/examples/Networking/HTTPPost/.lang/de.po b/app/examples/Networking/HTTPPost/.lang/de.po new file mode 100644 index 00000000..ec7afa61 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/de.po @@ -0,0 +1,73 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "Beispiel fürHTTP-Client POST" + +#: F.class:7 +msgid "Error " +msgstr "Fehler" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "Warte auf Antwort..." + +#: F.class:72 +msgid "Connecting..." +msgstr "Verbinden..." + +#: F.form:23 +msgid "Internet Calculator" +msgstr "Internet-Rechner" + +#: F.form:28 +msgid "Post" +msgstr "Übermitteln" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "-" + +#: F.form:48 +msgid "HTTP Data" +msgstr "HTTP Daten" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Eine Zahl eingeben" + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Noch eine Zahl eingeben" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Dann auf Übermitteln klicken >>>" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Die Macht des Internets für einen kleinen Taschenrechner nutzen!" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Proxy benutzen" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" + diff --git a/app/examples/Networking/HTTPPost/.lang/es.po b/app/examples/Networking/HTTPPost/.lang/es.po new file mode 100644 index 00000000..0901fdfa --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/es.po @@ -0,0 +1,56 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: F.class:132 +msgid "Internet Calculator" +msgstr "Calculador de Internet" + +#: F.class:137 +msgid "Post" +msgstr "Post" + +#: F.class:152 +msgid "HTTP Header :" +msgstr "HTTP Encabezado :" + +#: F.class:157 +msgid "HTTP Data :" +msgstr "HTTP Datos :" + +#: F.class:162 +msgid "Write Here a Number :" +msgstr "Escriba aquí un número :" + +#: F.class:167 +msgid "Write Here Another Number :" +msgstr "Escriba aquí otro número :" + +#: F.class:172 +msgid "5" +msgstr "5" + +#: F.class:182 +msgid "Finally Press Post button ->" +msgstr "Finalmente presione el botón Post" + +#: F.class:190 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "" +"¡Usando el poder de Internet para tener una mínima máquina calculadora!" + +#: F.class:224 +msgid "Use Proxy" +msgstr "Usar Proxy" + +#: F.class:230 +msgid "127.0.0.1:3128" +msgstr "127.0.0.1:3128" diff --git a/app/examples/Networking/HTTPPost/.lang/nl.po b/app/examples/Networking/HTTPPost/.lang/nl.po new file mode 100644 index 00000000..bbb2ca66 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/nl.po @@ -0,0 +1,72 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"PO-Revision-Date: 2014-09-22 00:19+0100\n" +"Last-Translator: Willy Raets \n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "HTTP cliënt POST voorbeeld" + +#: F.form:23 +msgid "Internet Calculator" +msgstr "-" + +#: F.form:28 +msgid "Post" +msgstr "-" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "-" + +#: F.form:48 +msgid "HTTP Data" +msgstr "-" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Schrijf hier een nummer:" + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Schrijf hier een ander nummer:" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Uiteindelijk druk the Post knop >>>" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Gebruik de kracht van het internet om een minimale calculator machine te hebben!" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Gebruik Proxy" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" + +#: F.class:7 +msgid "Error " +msgstr "Fout" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "Wachten op antwoord..." + +#: F.class:72 +msgid "Connecting..." +msgstr "Verbinding maken..." + diff --git a/app/examples/Networking/HTTPPost/.lang/ru.po b/app/examples/Networking/HTTPPost/.lang/ru.po new file mode 100644 index 00000000..bfc033a1 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/ru.po @@ -0,0 +1,104 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/HTTPPost/.project:21 +msgid "HTTP client POST example" +msgstr "Пример POST HTTP-клиента" + +#: app/examples/Networking/HTTPPost/.project:22 +msgid "" +"HttpClient POST request example.\n" +"\n" +"This example how to send a POST HTTP request to a server and get the response. You can configure an HTTP proxy if needed." +msgstr "" +"Пример POST-запроса клиента HTTP.\n" +"\n" +"В этом примере показано, как отправить запрос POST HTTP на сервер и получить ответ. При необходимости вы можете настроить HTTP-прокси." + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.class:7 +msgid "Error " +msgstr "Ошибка " + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.class:22 +msgid "Waiting for reply..." +msgstr "Ожидание ответа..." + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.class:33 +msgid "OK" +msgstr "ОК" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.class:59 app/examples/Networking/HTTPPost/.src/FHttpPost.class:62 +msgid "Error" +msgstr "Ошибка" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.class:72 +msgid "Connecting..." +msgstr "Соединение..." + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:5 +msgid "Internet Calculator" +msgstr "Интернет-калькулятор" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:9 +msgid "Post" +msgstr "Отправить" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:20 +msgid "HTTP Headers" +msgstr "Заголовки HTTP" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:25 +msgid "HTTP Data" +msgstr "Данные HTTP" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:29 +msgid "Write Here a Number :" +msgstr "Написать здесь число:" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:33 +msgid "Write Here Another Number :" +msgstr "Написать здесь другое число:" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:37 app/examples/Networking/HTTPPost/.src/FHttpPost.form:41 +msgid "5" +msgstr "5" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:45 +msgid "Finally press the Post button >>>" +msgstr "В заключение нажать кнопку >>>" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:52 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Использование возможностей Интернета, чтобы иметь минимальный калькулятор!" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:78 +msgid "Use Proxy" +msgstr "Использовать прокси" + +#: app/examples/Networking/HTTPPost/.src/FHttpPost.form:83 +msgid "127.0.0.1:3128" +msgstr "127.0.0.1:3128" + diff --git a/app/examples/Networking/HTTPPost/.project b/app/examples/Networking/HTTPPost/.project new file mode 100644 index 00000000..149b45db --- /dev/null +++ b/app/examples/Networking/HTTPPost/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=HTTP client POST example +Startup=FHttpPost +Icon=httpclient.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.net +Component=gb.net.curl +Description="HttpClient POST request example.\n\nThis example how to send a POST HTTP request to a server and get the response. You can configure an HTTP proxy if needed." +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/HTTPPost/.src/FHttpPost.class b/app/examples/Networking/HTTPPost/.src/FHttpPost.class new file mode 100644 index 00000000..fc233522 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.src/FHttpPost.class @@ -0,0 +1,105 @@ +' Gambas class file + +Public P As HttpClient + +Public Sub P_Error() + + LblInfo.Text = ("Error ") & P.Status + +End + + +Public Sub Form_Open() + + TextArea1.Text = "" + P = New HttpClient As "P" + +End + + +Public Sub P_Connect() + + LblInfo.Text = ("Waiting for reply...") + +End + + + +Public Sub P_Finished() + + Dim sBuf As String + Dim MyLoop As Integer + + LblInfo.Text = ("OK") + + TextArea2.Insert(P.Headers.Join("\n") & "\n") + + If Lof(P) Then + sBuf = Read #P, Lof(P) + TextArea1.Text = sBuf + Endif + + If (InStr(sBuf, "")) Then + sBuf = Mid(sBuf, InStr(sBuf, "") + 12) + MyLoop = 1 + Label6.Text = TextBox1.Text & " + " & TextBox2.Text & " = " + Do While Mid(sBuf, MyLoop, 1) <> "<" + Label6.Text = Label6.Text & Mid(sBuf, MyLoop, 1) + MyLoop = MyLoop + 1 + Loop + If (InStr(sBuf, "")) Then + sBuf = Mid(sBuf, InStr(sBuf, "") + 12) + MyLoop = 1 + Label7.Text = TextBox1.Text & " - " & TextBox2.Text & " = " + Do While Mid(sBuf, MyLoop, 1) <> "<" + Label7.Text = Label7.Text & Mid(sBuf, MyLoop, 1) + MyLoop = MyLoop + 1 + Loop + Else + Message.Error(("Error")) + End If + Else + Message.Error(("Error")) + End If + + +End + + +Public Sub Button1_Click() + + Dim sCad As String + LblInfo.Text = ("Connecting...") + TextBox1.Text = Val(Trim(TextBox1.Text)) + TextBox2.Text = Val(Trim(TextBox2.Text)) + Wait + TextArea1.Text = "" + sCad = "" & Chr(13) & Chr(10) + sCad = sCad & "" '& Chr(13) & Chr(10) + sCad = sCad & "sample.sumAndDifference" '& Chr(13) & Chr(10) + sCad = sCad & "" + sCad = sCad & "" + sCad = sCad & "" & TextBox1.Text & "" + sCad = sCad & "" + sCad = sCad & "" + sCad = sCad & "" & TextBox2.Text & "" + sCad = sCad & "" + sCad = sCad & "" + sCad = sCad & "" + If ChkProxy.Value Then + P.Proxy.Host = TxtProxy.Text + Else + P.Proxy.Host = "" + End If + P.URL = "dansoft.krasnokamensk.ru/xmlrpc-c-api/sample.php" + + P.Post("text/xml", sCad) + +End + + +Public Sub ChkProxy_Click() + + TxtProxy.Enabled = ChkProxy.Value + +End diff --git a/app/examples/Networking/HTTPPost/.src/FHttpPost.form b/app/examples/Networking/HTTPPost/.src/FHttpPost.form new file mode 100644 index 00000000..69bf3555 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.src/FHttpPost.form @@ -0,0 +1,85 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(38.1429,12.7143,95,85) + Text = ("Internet Calculator") + Resizable = False + { Button1 Button + MoveScaled(42,18,14.7143,4) + Text = ("Post") + } + { TextArea1 TextArea + MoveScaled(1,56,93,28) + } + { TextArea2 TextArea + MoveScaled(1,35,93,16) + } + { Label1 Label + MoveScaled(1,31,89.1429,4) + Font = Font["Bold"] + Text = ("HTTP Headers") + } + { Label2 Label + MoveScaled(1,52,90.7143,4) + Font = Font["Bold"] + Text = ("HTTP Data") + } + { Label3 Label + MoveScaled(4,8,38,4) + Text = ("Write Here a Number :") + } + { Label4 Label + MoveScaled(4,13,38,4) + Text = ("Write Here Another Number :") + } + { TextBox1 TextBox + MoveScaled(42,8,14.7143,4) + Text = ("5") + } + { TextBox2 TextBox + MoveScaled(42,13,14.7143,4) + Text = ("5") + } + { Label5 Label + MoveScaled(4,18,38,4) + Text = ("Finally press the Post button >>>") + } + { TextLabel1 TextLabel + MoveScaled(0,0,96,6) + Font = Font["Bold"] + Background = &H101A9E& + Foreground = &HF7F02A& + Text = ("Using the power of internet to have a minimal calculator machine!") + Alignment = Align.Center + } + { Label6 Label + MoveScaled(60,8,34,5) + Font = Font["12,Bold"] + Background = &H092773& + Foreground = &HFFFFFF& + Padding = 4 + } + { Label7 Label + MoveScaled(60,14,34,5) + Font = Font["12,Bold"] + Background = &H092773& + Foreground = &HFFFFFF& + Padding = 4 + } + { LblInfo Label + MoveScaled(60,20,34,4) + Font = Font["Bold"] + Background = &H003DB6& + Foreground = &HFFFF00& + Padding = 4 + } + { ChkProxy CheckBox + MoveScaled(1,26,36,4) + Text = ("Use Proxy") + } + { TxtProxy TextBox + MoveScaled(28,26,67,4) + Enabled = False + Text = ("127.0.0.1:3128") + } +} diff --git a/app/examples/Networking/HTTPPost/httpclient.png b/app/examples/Networking/HTTPPost/httpclient.png new file mode 100644 index 0000000000000000000000000000000000000000..613d858f2cbcc49382048288ca7a5b04e9db60be GIT binary patch literal 1768 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, YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/POPMailbox/.project:20 +msgid "POP-Mailbox" +msgstr "Почтовый ящик POP" + +#: app/examples/Networking/POPMailbox/.project:21 +msgid "Simple mail reader based on the POP3 component." +msgstr "Простая читалка почты на основе компонента POP3." + +#: app/examples/Networking/POPMailbox/.src/FMain.class:49 +msgid "Login failed!" +msgstr "Ошибка логина!" + +#: app/examples/Networking/POPMailbox/.src/FMain.form:5 +msgid "POP3 Mailbox Example" +msgstr "Пример почтового ящика POP3" + +#: app/examples/Networking/POPMailbox/.src/FMain.form:31 +msgid "Connect" +msgstr "Соединить" + +#: app/examples/Networking/POPMailbox/.src/FMain.form:39 +msgid "Disconnect" +msgstr "Отсоединить" + +#: app/examples/Networking/POPMailbox/.src/FMain.form:47 app/examples/Networking/POPMailbox/.src/FSettings.form:5 +msgid "Settings" +msgstr "Параметры" + +#: app/examples/Networking/POPMailbox/.src/FSettings.class:28 +msgid "Missing username" +msgstr "Отсутствует имя пользователя" + +#: app/examples/Networking/POPMailbox/.src/FSettings.class:33 +msgid "Missing password" +msgstr "Отсутствует пароль" + +#: app/examples/Networking/POPMailbox/.src/FSettings.class:38 +msgid "Missing server" +msgstr "Отсутствует сервер" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:13 +msgid "Login Details" +msgstr "Данные для логина" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:17 +msgid "Username" +msgstr "Имя пользователя" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:26 +msgid "Password" +msgstr "Пароль" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:36 +msgid "Server Details" +msgstr "Детали сервера" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:40 +msgid "Server" +msgstr "Сервер" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:49 +msgid "Port" +msgstr "Порт" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:59 +msgid "Use SSL" +msgstr "Использовать SSL" + +#: app/examples/Networking/POPMailbox/.src/FSettings.form:64 +msgid "Save" +msgstr "Сохранить" + diff --git a/app/examples/Networking/POPMailbox/.project b/app/examples/Networking/POPMailbox/.project new file mode 100644 index 00000000..15fc0f24 --- /dev/null +++ b/app/examples/Networking/POPMailbox/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=POP-Mailbox +Startup=FMain +Icon=pop3client.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.settings +Component=gb.net +Component=gb.mime +Component=gb.net.pop3 +Description="Simple mail reader based on the POP3 component." +Authors="sebikul " +TabSize=2 +Packager=1 +Translate=1 diff --git a/app/examples/Networking/POPMailbox/.src/FMain.class b/app/examples/Networking/POPMailbox/.src/FMain.class new file mode 100644 index 00000000..3838a456 --- /dev/null +++ b/app/examples/Networking/POPMailbox/.src/FMain.class @@ -0,0 +1,106 @@ +' Gambas class file + + + +Public Sub Form_Open() + + Settings.Read(FMain) + + FSettings.ShowModal() + + MailClient.Debug = True + + 'Connect() + + If Not MailClient.User Then Me.Close + +End + +Public Sub btnSettings_Click() + + If MailClient.Status <> Net.Connected Then + FSettings.ShowModal() + Endif + +End + +Public Sub btnConnect_Click() + + Connect() + +End + +Private Sub Connect() + + 'Wait 0.5 + + Inc Application.Busy + + If MailClient.Host Then + btnSettings.Enabled = False + MailClient.Open() + Endif + + If MailClient.Status = Net.Connected Then + LoadInbox() + btnDisconnect.Enabled = True + Else + btnSettings.Enabled = True + Message.Warning(("Login failed!")) + Endif + + Dec Application.Busy + +End + +Private Sub LoadInbox() + + Dim oMail As _Pop3Client_Message + + If MailClient.Status <> Net.Connected Then Return + + For Each oMail In MailClient + + If oMail.Index = 3 Then Return + + lvInbox.Add(oMail.Index, Subst("&1 &2", oMail.Message.Sender, oMail.Message.Subject)) + + Wait + + Next + +End + +Public Sub btnDisconnect_Click() + + If MailClient.Status <> Net.Connected Then Return + + lvInbox.Clear() + + MailClient.Close() + + btnDisconnect.Enabled = False + + btnSettings.Enabled = True + +End + +Public Sub lvInbox_Select() + + ''Please note that this is not an example of the gb.mime component!!! + txtContent.Text = MailClient[lvInbox.Key].Message.Body[0].ToString() + + ''THIS SHOULD WORK!!! + 'txtContent.Text = Mime.Decode(MailClient[lvInbox.Key].Message.Body[0].ToString(), MailClient[lvInbox.Key].Message.Body[0].ContentEncoding) + +End + +Public Sub Form_Close() + + If btnDisconnect.Enabled Then + MailClient.Close() + Endif + + Settings.Write(FMain) + +End diff --git a/app/examples/Networking/POPMailbox/.src/FMain.form b/app/examples/Networking/POPMailbox/.src/FMain.form new file mode 100644 index 00000000..2c8f7391 --- /dev/null +++ b/app/examples/Networking/POPMailbox/.src/FMain.form @@ -0,0 +1,50 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,77,38) + Text = ("POP3 Mailbox Example") + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + Padding = 10 + { !MailClient #Pop3Client + #MoveScaled(23,1) + #Public = True + } + { HSplit1 HSplit + MoveScaled(1,9,69,19) + Expand = True + { lvInbox ListView + MoveScaled(2,3,29,14) + } + { txtContent TextArea + MoveScaled(33,1,31,16) + ReadOnly = True + Wrap = True + ScrollBar = Scroll.Vertical + } + } + { HBox1 HBox + MoveScaled(3,30,67,4) + { btnConnect Button + MoveScaled(0,0,16,4) + Text = ("Connect") + } + { Panel2 Panel + MoveScaled(16,0,7,4) + } + { btnDisconnect Button + MoveScaled(23,0,16,4) + Enabled = False + Text = ("Disconnect") + } + { Panel1 Panel + MoveScaled(39,0,7,4) + Expand = True + } + { btnSettings Button + MoveScaled(46,0,16,4) + Text = ("Settings") + } + } +} diff --git a/app/examples/Networking/POPMailbox/.src/FSettings.class b/app/examples/Networking/POPMailbox/.src/FSettings.class new file mode 100644 index 00000000..ed66a025 --- /dev/null +++ b/app/examples/Networking/POPMailbox/.src/FSettings.class @@ -0,0 +1,68 @@ +' Gambas class file + +Public Sub Form_Close() + + Settings.Write(FSettings) + +End + +Public Sub Form_Open() + + Settings.Read(FSettings) + + txtUsername.Text = Settings["LastUser", FMain.MailClient.User] + + txtPassword.Text = FMain.MailClient.Password + + txtServer.Text = Settings["LastServer", FMain.MailClient.Host] + + vbPort.Value = IIf(FMain.MailClient.Port, FMain.MailClient.Port, vbPort.Value) + +End + +Public Sub btnSave_Click() + + Balloon.Delay = 3000 + + If Not txtUsername.Text Then + Balloon.Error(("Missing username"), txtUsername) + Return + Endif + + If Not txtPassword.Text Then + Balloon.Error(("Missing password"), txtPassword) + Return + Endif + + If Not txtServer.Text Then + Balloon.Error(("Missing server"), txtServer) + Return + Endif + + FMain.MailClient.User = txtUsername.Text + Settings["LastUser"] = FMain.MailClient.User + + FMain.MailClient.Password = txtPassword.Text + + FMain.MailClient.Host = txtServer.Text + Settings["LastServer"] = FMain.MailClient.Host + + FMain.MailClient.Port = vbPort.Value + + Me.Close + +End + +Public Sub chbSSL_Click() + + Select Case chbSSL.Value + + Case -1 + If vbPort.Value = 110 Then vbPort.Value = 995 + + Case 0 + If vbPort.Value = 995 Then vbPort.Value = 110 + + End Select + +End diff --git a/app/examples/Networking/POPMailbox/.src/FSettings.form b/app/examples/Networking/POPMailbox/.src/FSettings.form new file mode 100644 index 00000000..62814211 --- /dev/null +++ b/app/examples/Networking/POPMailbox/.src/FSettings.form @@ -0,0 +1,66 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,48,53) + Text = ("Settings") + Resizable = False + Arrangement = Arrange.Vertical + AutoResize = True + Spacing = True + Margin = True + { Frame1 Frame + MoveScaled(1,2,46,18) + Text = ("Login Details") + { Label1 Label + MoveScaled(1,5,16,4) + AutoResize = True + Text = ("Username") + } + { txtUsername TextBox + MoveScaled(21,5,24,4) + Expand = True + } + { Label2 Label + MoveScaled(1,11,16,4) + AutoResize = True + Text = ("Password") + } + { txtPassword TextBox + MoveScaled(21,11,24,4) + Expand = True + Password = True + } + } + { Frame2 Frame + MoveScaled(1,26,46,21) + Text = ("Server Details") + { Label3 Label + MoveScaled(1,5,17,4) + AutoResize = True + Text = ("Server") + } + { txtServer TextBox + MoveScaled(21,5,24,4) + Expand = True + } + { Label4 Label + MoveScaled(1,11,32,4) + AutoResize = True + Text = ("Port") + } + { vbPort SpinBox + MoveScaled(36,11,9,4) + MaxValue = 999999 + Value = 110 + } + { chbSSL CheckBox + MoveScaled(1,16,42,4) + AutoResize = True + Text = ("Use SSL") + } + } + { btnSave Button + MoveScaled(31,48,16,4) + Text = ("Save") + } +} diff --git a/app/examples/Networking/POPMailbox/pop3client.png b/app/examples/Networking/POPMailbox/pop3client.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac6e3cd827550e3f24ac0cfd9e9c3e8f1ff1707 GIT binary patch literal 957 zcmV;u148_XP)Z0B9%xbu9o!8~^|R$;!_5_V)Vv z`uX|!_4W0~$;{^I?X|bO+uPgz{{8p&_x}F=+S=Q+x4Y`?@!a3z!^X(E|<=^7v)7ILks;%VZ<@@{l@$vEU^78!r{Nv>0?Curta_W?d|RE?(XjH@1mrp@ACEI=IP|- z>et!aqot?D$H?jF>FVn0#>U6-^Y-QE>gDI_y1c%kqonxw`2GF;>+9>HqNMZn_~+^D z=jrR{>g?p^=&rA@goK6J+1c3G*sia!_4fMd>h0<4?%?6%y1TvS=;*Djt*or9tE;Q$ z=jh<#g?|6?C#Lg)XdGz=;-L=VJkiRI^yi;0hljDU!RffXbqfRoc}dAyA}U zQ(IA9R$5vDR!~>p(Ad-*+*0iT2h}aX%}tFB^>ttct!?ccol*{tD8NCgv!lJO6{w&K zClK!L=>;k1!v*@4TPEOD093)x4psn4I(=XYmEG6R!3j|S62U?ORWRUC&<7*?k*t6! zfEeBfAwep-kah4w*$5N3uq%MOfr|m5pbu^l!WUeO_``vVfdOwwa)YyGKTf~{&Ahx^ f*a08md=CZy+L9lmVaOD500000NkvXXu0mjfUIQU6 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/SerialPort/.directory b/app/examples/Networking/SerialPort/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Networking/SerialPort/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Networking/SerialPort/.icon.png b/app/examples/Networking/SerialPort/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7cda40d9199bbf380bda87817e786e26a2413869 GIT binary patch literal 4320 zcmV<65FhV}P)g=<3E5fB5I-n9#O**5DX+Fp|f0|{gk`)`V`*u>a=_QP7$j|Z;k1Oor>)=8a zpz`ilF+yQ9tgg=F{=2GJRFTQ=4#TUB;5e|O3Es0T#MZ$YiZ{K0Qi?C#UcpyxujC6q zIKbAfW2bemzbOH#|9%HXyBGt-**?B?M+G;p3-NLbJoO61lT&?@w&r+B;t!RwesvxX z-o23SL7VHpb%dtoQINh1OMr!6d=q0JnC|5tZYtsLZYrj30KWeQ3`N0lr~3AE4$^eE znUZY5li#kTrYxQBKHkBWzeGX#NEd!=c0 z`E_SPe9=8SKuDy3k6o3|*FIFnaijRjerOMmnfnBe563vNzXc~g$~Qk!&Ry>-=10Gc z@zL+Kk#rqUeh>nseb_SX>;*7F+_$#!w8;acX~TPWHmg_e-#mEZL!0v6`fDfD{L^lX z0b>l;EzjmVA1Na%+sD>6I6MHho~CZx;n0Cjx>|eKbWH)@xP1xR8!a|I45I@sj`0B+ zgg_X9(q4>_c#3bp?tbyi1yE9#x|%b_rIlpuY7sA-N`$5Vyc47cqhVQjfbV>`l$F)# zyw(M8AAr=fC+@n?aHOAOhdZb(5Aeu+D>0UzFFytKouD;jq>w+gfcvQ^VVW+xip)%2jKC85Oc-| zO`xk|guQhg1fUhP8e*D)h z1Ll&VvlC!Kz+?S_v$3Lc0j)`;T%LXSuF%J3A3M1P=(869pgJ_S^XD1fqrLLh6CF6Q z0PdLoT}%*$6D9^H765}gM+^8{!^uM0I|X>)0hzQl;rQQ(1J4s;Dc}Jkc|gn{Hba2* zkM{HOAGe9tV3=6^;_`ukQ3>_0T0m<0?_&K!`yL4ftUEw>CRI@~M{seu0D$e9;i+%N zj>McT;y-@Z8Lq8JSJPcy8`9tH!JZ39L3G+@_P0e{>#gSa=B9?V8B+ z?^?UYvRx7LTWSVjAM(Mq0#eGj<&i33&9VS7 zPXcJ|vUB?rn8iz3wDd}PTk9C;I?T$eKl$$PH!}tBNS&~hCF4rY5?~&30Igm2?c9u5 zx*WUV0AZQKXnv41zd{K$=j)#h%&h3HEH`dhLdDcOoPO?UaA)%#MshO{P72fBLR*Dq zam{6zw{|0yAQg*XrKKa~`4nR&PfMtnrIbv}wgAlB=0PWwpyzlio{9=E2G6xO6Ye_3 z<+&bO7F>Z6ietNFdX67U$@4np&Q9=@ z6miQ^GOpCjBl-+OKrAxA&wl(M_m!6L^xdD~g(sh&V&PK0_UJS8L}JA93J9#)K>p={ z@sUW>(Svv`>%1miv#rfiGHyvhd@fJHjrGHk-|*6Bzs?J5)^JHUOoouGUS7lMn>Vq0 z$Uzwwol1-;D+I}bPO5?h_yZZ|Re;$%r64XXrQ%WwW;Ai~W?YBi!A`pRnrW(knPs`1 zEL^{yAdp^AKxc0s5B=xE*l8Kqj*E_uqEm@U|8r9bOnVc>xn)?E=al!F_cn{CrBvKf z(nORL0;VSalf{6phTTLwHZh&#aCZl{F3Csdm+|DDPVTAi=Kmghls&(H9#L@#Ki$q$EL(j39{q_eKqzS>JWk-{$w5>4E8(2j#zd@29)z&G&}FF-6@g|D~@orsfq^ABV$S%bik z+1tg{8}C8RgX4RW5xRH0#z;>$N(p{mFSzE~#e860KTUi8n}dB{kzx+Vc#}!Xo5TeE-V3=t!j~>hEU5hrTpt;?hZA^)wM}ZKg6mfX=PLwq0!7 zrm0c0f1jVMEJN$@c1j9H@CI|ITfn3M+90K4^;*U!dX+#-OcBN)CLT~rLmidn7H>3m za)1P$^dL@T5O*|;<4I%t(Kaqw?7{E#5RF9{>T2h|au)I9`t7V0uq7Cx&sY?;HgMye z_s)s9g(f4`hi9mZV5WsRNI4~dlw#7-g*L+W ziCzKl@JorVDFKXSJ8r&{N;1@ckR@xX+5FOW+QTu@LM2$~nGElFg{)=khz$1g(Fz|s z5*ajaf02R>n~04>kgsl`A{FNdlG{FX4}}#==R`h7M+lDgkR1^C{XQJWMY|eT8(gh1 z+Tc1GS8KFuupO7~?qU9NxP^5e`|On4SgKD5;R0xF`1*#l5(~hRA}*xxk6Qq)5G0c^ z{B}QDS2LJZh8Q|XvcD61*h^;3rHnMzlNl=Hl&S` zp9inkVq`Q*R$lImL`zDnt~W`hVhADd6<1Ld zA3|Cdy|%_}+>2hchO7lOm~)aJYUu%WJQivjD-CSE;%to1+c&Sn>w)LLt0f zFBuuWvAiOs8R#FOzP^K4G(k~O4pmhJbVo*UODdSY&{N~d5P%URrU^i7qMKk$C9hPN zKp|y|YN#H9a3YD2lHRRPQ*zn0WV(hxsFbF7gtWp^lBpCO$rMToMuO?&2L|YF+C%BW zwQ~~SQ02F2c>XDBa`$73TIuZkC`H93fFUJYf@yCOC4mjg;e(k(owuW`JSD zj4GItQvu#UY*GMA5@(tMN<(H=kb?&!G(7gds9+Jp9o^uhuq0f+?j~MqIZUku-GM9| zGnQ&9VKBzfemqWgNde}g6b7Wb#{f1U|~7Q;8$qqsiW=nt)!Pn8HjX}k>;bN zb%2WUJlZ>l7)=x8EUTiswU2fYnRa@a!{B z^B#MM*1BEX`hiao%qu=)5z+z~m<8{pqM#OI4B?S5Ek_UI869QFPSTasWS7^Fms`N7 z=WVpsq*9tlI6PwsJh28C$CL|Lg7`!Y#F#0SGPAN-QC!87PyB+y{0wqJ3y4G$Jon-j zO5S%na^X5IUD8Wka~CG98ZhHG6nHJg-i`r0%ic?Lpo6d8`!D?Vx!+S6uBLiX?P(F8 z0V&A{W=)POE5Cr@o_0nAEGR6;>+|6o?4qzRokToAQ&SHkZB1UAYnM4baroituL;ZaaoAI!gNDRba;4KnQ_H zNfa)1ExjCk`cEtdY{lV&yGTVN@3_y|G)oGUfbJtlQA(0X zIvj0mC!@NMQ03woeWe()ZY9S!Pm2LEKCu>bCE`SNy6n6HuDgCCWrz0goA!3R`DH|e zp|8K2_iy+xELl$V!QK4j$bR}%8vgRd7!1B;>ljHy$&r$)mMo*B>JnNT4xlVGul2xz z{^N`kXHZm_Ma`0O(iT^cQCxX)3lL(;1w4}Y#P&R?QKwq0Em*RGtlh7Z$S=cW=F%V} zhkp7npSk5umae#z(()>H*S*0@uRlkiY6ZT$A|jbN95RN+mKJJ`?&EzM@0>T{69_G> z=B=hCib7e$ErWtV0g$88&K<*{Esj%V5B zIFaMzdwtk;3Lyl+>^!c&?iMava}`ZZhuFRMP1^ceJh5eSyJJGU@kOLjajx^TV5$$cHTe zZFDqlD~vHBmT;9enu~6Si~uQ&GC~R=gh}`4D2%&6cxbrM@nm~rNk=9UDP@e|VpKo~ zpC}E(jp>v)9`H?9Iqs=$eVmQWu{907@7;H9FMzscXuw1NtWg05$?O zKmhRa&J7h8I$Xd8qQDX0j}rm__<&MiF;EQT0Ddm8ztx=rnz3nb80ZF$0Ubbc+8Piw zPTI$q1xQY^oBRTV85bcn-r|763EqQWEQC)-kohBA{)w&lvU6FrD4Sn>bs_!X6c7Hg zjX&)fB-XKufj>Wl6YV``Y(9AR4>{Nn!SZHcc>{R;*(|Ms>#x5QO9}FG{M>eRhEP}_oGDWe|64?PoT1_oR;1`EN?cxi~_WMocx@z zrFojyIw^UWN0TqS`Ede83y_u~-noaK7w<-9sTf!gX6g{76; z|Bo9_dEUJ3SL=QLbLWlhiBJ&Az-J|K-2|H-`#hVT+D0fe_di!(^!R_j@!Xhg>|DbD O0000, YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-28 09:00+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/SerialPort/.project:18 +msgid "Serial Port example" +msgstr "Пример последовательного порта" + +#: app/examples/Networking/SerialPort/.project:19 app/examples/Networking/SerialPort/.src/FMain.form:5 +msgid "RS232 Serial port example" +msgstr "Пример последовательного порта RS232" + +#: app/examples/Networking/SerialPort/.src/FAbout.class:6 app/examples/Networking/SerialPort/.src/FMain.form:17 +msgid "About" +msgstr "О программе" + +#: app/examples/Networking/SerialPort/.src/FAbout.class:8 +msgid "Version" +msgstr "Версия" + +#: app/examples/Networking/SerialPort/.src/FAbout.form:10 +msgid "Label1" +msgstr "Метка1" + +#: app/examples/Networking/SerialPort/.src/FAbout.form:15 +msgid "Provided under GPL License" +msgstr "Предоставлено по лицензии GPL" + +#: app/examples/Networking/SerialPort/.src/FAbout.form:20 +msgid "by" +msgstr "от" + +#: app/examples/Networking/SerialPort/.src/FAbout.form:25 +msgid "Claus Dietrich" +msgstr "Клаус Дитрих" + +#: app/examples/Networking/SerialPort/.src/FAbout.form:30 +msgid "Close" +msgstr "Закрыть" + +#: app/examples/Networking/SerialPort/.src/FMain.class:47 +msgid "Unable to open" +msgstr "Невозможно открыть" + +#: app/examples/Networking/SerialPort/.src/FMain.class:48 app/examples/Networking/SerialPort/.src/FMain.class:127 +msgid "Apply" +msgstr "Применить" + +#: app/examples/Networking/SerialPort/.src/FMain.class:111 +msgid "Please open serial port first." +msgstr "Пожалуйста, сначала откройте последовательный порт." + +#: app/examples/Networking/SerialPort/.src/FMain.class:126 app/examples/Networking/SerialPort/.src/FMain.class:150 app/examples/Networking/SerialPort/.src/FMain.form:152 +msgid "Change" +msgstr "Изменить" + +#: app/examples/Networking/SerialPort/.src/FMain.class:146 +msgid "Error while opening" +msgstr "Ошибка при открытии" + +#: app/examples/Networking/SerialPort/.src/FMain.form:9 +msgid "File" +msgstr "Файл" + +#: app/examples/Networking/SerialPort/.src/FMain.form:11 +msgid "Exit" +msgstr "Выход" + +#: app/examples/Networking/SerialPort/.src/FMain.form:15 +msgid "Help" +msgstr "Справка" + +#: app/examples/Networking/SerialPort/.src/FMain.form:25 +msgid "TextArea1" +msgstr "Текстовая_область_1" + +#: app/examples/Networking/SerialPort/.src/FMain.form:39 +msgid "TextArea2" +msgstr "Текстовая_область_2" + +#: app/examples/Networking/SerialPort/.src/FMain.form:53 +msgid "Send" +msgstr "Послать" + +#: app/examples/Networking/SerialPort/.src/FMain.form:68 +msgid "Parity" +msgstr "Паритет" + +#: app/examples/Networking/SerialPort/.src/FMain.form:72 +msgid "Stopbits" +msgstr "Стоп-биты" + +#: app/examples/Networking/SerialPort/.src/FMain.form:76 +msgid "Bits" +msgstr "Бит" + +#: app/examples/Networking/SerialPort/.src/FMain.form:80 +msgid "Baud" +msgstr "Бод" + +#: app/examples/Networking/SerialPort/.src/FMain.form:84 +msgid "Device" +msgstr "Устройство" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "50" +msgstr "50" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "75" +msgstr "75" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "100" +msgstr "100" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "150" +msgstr "150" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "200" +msgstr "200" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "300" +msgstr "300" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "600" +msgstr "600" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "1200" +msgstr "1200" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "2400" +msgstr "2400" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "4800" +msgstr "4800" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 app/examples/Networking/SerialPort/.src/FMain.form:95 +msgid "9600" +msgstr "9600" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "19200" +msgstr "19200" + +#: app/examples/Networking/SerialPort/.src/FMain.form:94 +msgid "38400" +msgstr "38400" + +#: app/examples/Networking/SerialPort/.src/FMain.form:101 +msgid "5" +msgstr "5" + +#: app/examples/Networking/SerialPort/.src/FMain.form:101 +msgid "6" +msgstr "6" + +#: app/examples/Networking/SerialPort/.src/FMain.form:101 +msgid "7" +msgstr "7" + +#: app/examples/Networking/SerialPort/.src/FMain.form:101 app/examples/Networking/SerialPort/.src/FMain.form:102 +msgid "8" +msgstr "8" + +#: app/examples/Networking/SerialPort/.src/FMain.form:108 app/examples/Networking/SerialPort/.src/FMain.form:109 +msgid "1" +msgstr "1" + +#: app/examples/Networking/SerialPort/.src/FMain.form:108 +msgid "2" +msgstr "2" + +#: app/examples/Networking/SerialPort/.src/FMain.form:115 +msgid "None" +msgstr "Нет" + +#: app/examples/Networking/SerialPort/.src/FMain.form:115 +msgid "Even" +msgstr "Чётный" + +#: app/examples/Networking/SerialPort/.src/FMain.form:115 +msgid "Odd" +msgstr "Нечётный" + +#: app/examples/Networking/SerialPort/.src/FMain.form:116 +msgid "N" +msgstr "N" + +#: app/examples/Networking/SerialPort/.src/FMain.form:122 +msgid "NONE" +msgstr "НЕТ" + +#: app/examples/Networking/SerialPort/.src/FMain.form:122 +msgid "CRTSCTS" +msgstr "CRTSCTS" + +#: app/examples/Networking/SerialPort/.src/FMain.form:122 +msgid "XON/XOFF" +msgstr "XON/XOFF" + +#: app/examples/Networking/SerialPort/.src/FMain.form:122 +msgid "CRTSCTS + XON/XOFF" +msgstr "CRTSCTS + XON/XOFF" + +#: app/examples/Networking/SerialPort/.src/FMain.form:127 +msgid "DCD" +msgstr "DCD" + +#: app/examples/Networking/SerialPort/.src/FMain.form:132 +msgid "DSR" +msgstr "DSR" + +#: app/examples/Networking/SerialPort/.src/FMain.form:137 +msgid "CTS" +msgstr "CTS" + +#: app/examples/Networking/SerialPort/.src/FMain.form:142 +msgid "DTR" +msgstr "DTR" + +#: app/examples/Networking/SerialPort/.src/FMain.form:147 +msgid "RTS" +msgstr "RTS" + +#: app/examples/Networking/SerialPort/.src/FMain.form:157 +msgid "RNG" +msgstr "RNG" + +#: app/examples/Networking/SerialPort/.src/FMain.form:161 +msgid "Handshake" +msgstr "Рукопожатие" + +#: app/examples/Networking/SerialPort/.src/FMain.form:174 +msgid "Message" +msgstr "Сообщение" + diff --git a/app/examples/Networking/SerialPort/.project b/app/examples/Networking/SerialPort/.project new file mode 100644 index 00000000..bb5450c6 --- /dev/null +++ b/app/examples/Networking/SerialPort/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Serial Port example +Description="RS232 Serial port example" +Startup=FMain +Icon=serialport.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.settings +Component=gb.net +Authors="Claus Dietrich\nBenoît Minisini" +TabSize=2 +Packager=1 +Translate=1 diff --git a/app/examples/Networking/SerialPort/.src/FAbout.class b/app/examples/Networking/SerialPort/.src/FAbout.class new file mode 100644 index 00000000..3e40f230 --- /dev/null +++ b/app/examples/Networking/SerialPort/.src/FAbout.class @@ -0,0 +1,16 @@ +' Gambas class file + + +Public Sub Form_Open() + + FAbout.text = ("About") + Label1.width = FAbout.width + Label1.text = Application.name & " " & ("Version") & " " & Application.Version + +End + +Public Sub Button1_Click() + + Me.close + +End diff --git a/app/examples/Networking/SerialPort/.src/FAbout.form b/app/examples/Networking/SerialPort/.src/FAbout.form new file mode 100644 index 00000000..b4656819 --- /dev/null +++ b/app/examples/Networking/SerialPort/.src/FAbout.form @@ -0,0 +1,32 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,43,30) + Resizable = False + Utility = True + SkipTaskbar = True + { Label1 Label + MoveScaled(1,3,42,4) + Text = ("Label1") + Alignment = Align.Center + } + { Label2 Label + MoveScaled(1,8,42,4) + Text = ("Provided under GPL License") + Alignment = Align.Center + } + { Label3 Label + MoveScaled(1,12,42,4) + Text = ("by") + Alignment = Align.Center + } + { Label4 Label + MoveScaled(1,16,42,4) + Text = ("Claus Dietrich") + Alignment = Align.Center + } + { Button1 Button + MoveScaled(26,24,15,4) + Text = ("Close") + } +} diff --git a/app/examples/Networking/SerialPort/.src/FMain.class b/app/examples/Networking/SerialPort/.src/FMain.class new file mode 100644 index 00000000..ab3de3b9 --- /dev/null +++ b/app/examples/Networking/SerialPort/.src/FMain.class @@ -0,0 +1,246 @@ +' Gambas class file + +Public Sub _new() + +End + +Public Sub Form_Open() + 'This is a tested and fully operational example for how to use GAMBAS for serial (RS232) communications. + 'The sourcecode is provided under GNU Public Licence. So feel free to use it for non-commercial or commercial purposes. + 'The code is based on the example provided with Gambas 3.5.1 and the application published in the Gambas book available at: + 'http://www.gambas-buch.de/dw/doku.php?id=k19: k19.1: k19.1.2: start + + 'Features: + ' -Enumerates serial ports including USB types + ' -Change of comms parameters + ' -Storage and retrieval of comms parameters + ' -Display of incoming data + ' -Transmission of data + ' -Display of handshake line status + ' -Restoring of last window position as program start + ' + 'Have Fun + 'Claus Dietrich/ 23.12.2013 + ' + '############################################################################################################## + 'Example of RS232 properties for reference only: + 'SerialPort1.PortName = "/dev/ttyS0" + 'SerialPort1.Speed = "19200" + 'SerialPort1.Parity = 0 + 'SerialPort1.DataBits = "8" + 'SerialPort1.StopBits = "1" + 'SerialPort1.FlowControl = 0 + '############################################################################################################## + + 'Enumerate all available RS232 Interfaces and Display them in the Combo + Module_RS232.EnumerateSerialInterfaces + + 'Set the Default-Path for Storage of Program Parameters + Module_Config.InitDefaultPath + + 'Load Port-Configuration from the config file and set the combos accordingly + Module_Config.RestoreSerialSettings(FMain.SerialPort1, 0) + + 'Open the serial port + Try SerialPort1.Open() + If Error Then + FMain.Message(("Unable to open") & " " & SerialPort1.PortName & ": " & Error.Text) + FMain.ButtonChange.text = ("Apply") + FMain.ComboPortDeviceName.text = "" + Else + FMain.ComboPortDeviceName.enabled = False + FMain.ComboSpeed.enabled = False + Fmain.ComboDataBits.enabled = False + Fmain.ComboStopBits.enabled = False + FMain.ComboParity.enabled = False + FMain.ComboHandShake.enabled = False + 'Display Status of RS232 handshake lines + Module_RS232.CheckRS232Status() + Endif + + 'Empty the TextArea controls + TextArea1.text = "" + TextArea2.text = "" + TextArea2.SetFocus + + 'Restore the last position of the main window + Module_Config.RestoreFormPosition(FMain) + +End + +Public Sub SerialPort1_Read() + 'This routine reads the RS232 port + + Dim Rx As String + 'Sleep 0.025 + + 'Read it + Read #SerialPort1, Rx, Lof(SerialPort1) + 'Display it + Module_RS232.DisplaySerialInput(Rx) + +End + +Public Sub Form_Close() + 'This routine is the central exit point of the appplication + + Dim cwin As Window + + 'Close all open windows + For Each cwin In Windows + cwin.Close + Next + + 'Store last position of the FMain Window + Module_Config.StoreFormPosition(FMain) + + 'If open, close the serial port + If SerialPort1.Status = Net.active Then + SerialPort1.Close() + End If + +End + +Public Sub TextArea1_KeyPress() + +End + +Public Sub btnSend_Click() + + If SerialPort1.status = Net.Inactive Then + FMain.Message(("Please open serial port first.")) + Else + 'FMain.CheckRTS.Value = True + 'Wait 0.01 + Write #SerialPort1, TextArea2.text, Len(TextArea2.text) + 'Wait 0.5 + 'FMain.CheckRTS.Value = False + Endif + +End + +Public Sub ButtonChange_Click() + + With Fmain + + If FMain.ButtonChange.text = ("Change") Then + .ButtonChange.text = ("Apply") + .SerialPort1.close + .ComboPortDeviceName.enabled = True + .ComboSpeed.enabled = True + .ComboDataBits.enabled = True + .ComboStopBits.enabled = True + .ComboParity.enabled = True + .ComboHandShake.enabled = True + Else + 'Transfer combo settings to port + .SerialPort1.PortName = FMain.ComboPortDeviceName.text + .SerialPort1.Speed = FMain.ComboSpeed.text + .SerialPort1.Parity = FMain.ComboParity.index + .SerialPort1.DataBits = Fmain.ComboDataBits.Text + .SerialPort1.StopBits = Fmain.ComboStopBits.Text + .SerialPort1.FlowControl = Fmain.ComboHandShake.Index + 'Open serial port with new settings + Try FMain.SerialPort1.Open() + If Error Then + FMain.Message(("Error while opening") & " " & FMain.SerialPort1.PortName & ": " & Error.Text) + Else + 'Store new Settings + Module_Config.StoreSerialSettings(FMain.SerialPort1, 0) + FMain.ButtonChange.text = ("Change") + 'Disabling all RS232 Combos + .ComboPortDeviceName.enabled = False + .ComboSpeed.enabled = False + .ComboDataBits.enabled = False + .ComboStopBits.enabled = False + .ComboParity.enabled = False + .ComboHandShake.enabled = False + 'Update Status of RS232 Handshake Lines + Module_RS232.CheckRS232Status() + Endif + Endif + + End With + +End + +Public Sub CheckDTR_Click() + 'For setting DTR manually + + If FMain.SerialPort1.status = Net.Active + FMain.SerialPort1.DTR = CheckDTR.Value + Module_RS232.CheckRS232Status + Endif + +End + +Public Sub CheckRTS_Click() + 'For setting RTS manually + + If FMain.SerialPort1.Status = Net.Active + FMain.SerialPort1.RTS = CheckRTS.Value + Module_RS232.CheckRS232Status + Endif + +End + +'The following routines cause following warnings: +'"gbx3: warning: 3 allocation(s) non freed." +'I couldn't find any solution yet. +Public Sub SerialPort1_RTSChange(CurrentValue As Boolean) + + FMain.CheckRTS.Value = CurrentValue + +End + +Public Sub SerialPort1_DSRChange(CurrentValue As Boolean) + + FMain.CheckDSR.Value = CurrentValue + +End + +Public Sub SerialPort1_CTSChange(CurrentValue As Boolean) + + FMain.ChecCTS.Value = CurrentValue + +End + +Public Sub SerialPort1_DCDChange(CurrentValue As Boolean) + + FMain.CheckDCD.Value = CurrentValue + +End + +Public Sub SerialPort1_DTRChange(CurrentValue As Boolean) + + FMain.CheckDTR.Value = CurrentValue + +End + +Public Sub SerialPort1_RNGChange(CurrentValue As Boolean) + + FMain.CheckRNG.Value = CurrentValue + +End + +Public Sub MenuExit_Click() + 'Close application by closing FMain. The close-event of Fmain is doing the rest. + + Me.close + +End + +Public Sub MenuAbout_Click() + + 'Center FAbout over FMain + FAbout.Move(FMain.left + FMain.width / 2 - FAbout.width / 2, Fmain.Top + FMain.height / 2 - FAbout.height / 2) + 'Show it + FAbout.show + +End + +Public Sub Message(sMsg As String) + + txtMessage.Text = sMsg + +End diff --git a/app/examples/Networking/SerialPort/.src/FMain.form b/app/examples/Networking/SerialPort/.src/FMain.form new file mode 100644 index 00000000..49b1b5a3 --- /dev/null +++ b/app/examples/Networking/SerialPort/.src/FMain.form @@ -0,0 +1,189 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,102,80) + Text = ("RS232 Serial port example") + Icon = Picture["serialport.png"] + Arrangement = Arrange.Vertical + { MenuFile Menu + Text = ("File") + { MenuExit Menu + Text = ("Exit") + } + } + { MenuHelp Menu + Text = ("Help") + { MenuAbout Menu + Text = ("About") + } + } + { !TextArea1 TextArea + MoveScaled(1,1,98,30) + #Public = True + Font = Font["Monospace,+2"] + Expand = True + Text = ("TextArea1") + Alignment = Align.Left + ReadOnly = True + Border = False + } + { Separator1 Separator + MoveScaled(42,32,16,0) + } + { panSend HBox + MoveScaled(2,34,98,12) + { TextArea2 TextArea + MoveScaled(0,0,77,12) + Font = Font["Monospace,+2"] + Expand = True + Text = ("TextArea2") + Alignment = Align.Left + Border = False + } + { Panel1 VBox + MoveScaled(78,1,17,10) + Background = Color.TextBackground + Margin = True + { Panel4 Panel + MoveScaled(5,1,6,2) + Expand = True + } + { btnSend Button + MoveScaled(0,4,15,5) + Text = ("Send") + Picture = Picture["icon:/medium/redo"] + } + } + } + { Separator2 Separator + MoveScaled(16,48,16,0) + } + { Panel3 HBox + MoveScaled(3,50,98,26) + Spacing = True + { Panel2 Panel + MoveScaled(0,0,56,26) + { Label5 Label + MoveScaled(1,17,20,4) + Text = ("Parity") + } + { Label4 Label + MoveScaled(1,13,20,4) + Text = ("Stopbits") + } + { Label3 Label + MoveScaled(1,9,20,4) + Text = ("Bits") + } + { Label2 Label + MoveScaled(1,5,20,4) + Text = ("Baud") + } + { Label1 Label + MoveScaled(1,1,20,4) + Text = ("Device") + } + { !ComboPortDeviceName ComboBox + MoveScaled(21,1,17,4) + #Public = True + } + { !ComboSpeed ComboBox + MoveScaled(21,5,17,4) + #Public = True + ReadOnly = True + List = [("50"), ("75"), ("100"), ("150"), ("200"), ("300"), ("600"), ("1200"), ("2400"), ("4800"), ("9600"), ("19200"), ("38400")] + Text = ("9600") + } + { !ComboDataBits ComboBox + MoveScaled(21,9,17,4) + #Public = True + ReadOnly = True + List = [("5"), ("6"), ("7"), ("8")] + Text = ("8") + } + { !ComboStopBits ComboBox + MoveScaled(21,13,17,4) + #Public = True + ReadOnly = True + List = [("1"), ("2")] + Text = ("1") + } + { !ComboParity ComboBox + MoveScaled(21,17,17,4) + #Public = True + ReadOnly = True + List = [("None"), ("Even"), ("Odd")] + Text = ("N") + } + { !ComboHandShake ComboBox + MoveScaled(21,21,17,4) + #Public = True + ReadOnly = True + List = [("NONE"), ("CRTSCTS"), ("XON/XOFF"), ("CRTSCTS + XON/XOFF")] + } + { !CheckDCD CheckBox + MoveScaled(40,14,9,3) + #Public = True + Text = ("DCD") + } + { !CheckDSR CheckBox + MoveScaled(40,11,9,3) + #Public = True + Text = ("DSR") + } + { !CheckCTS CheckBox + MoveScaled(40,8,9,3) + #Public = True + Text = ("CTS") + } + { !CheckDTR CheckBox + MoveScaled(40,5,9,3) + #Public = True + Text = ("DTR") + } + { !CheckRTS CheckBox + MoveScaled(40,2,9,3) + #Public = True + Text = ("RTS") + } + { !ButtonChange Button + MoveScaled(40,21,15,4) + #Public = True + Text = ("Change") + } + { !CheckRNG CheckBox + MoveScaled(40,17,9,3) + #Public = True + Text = ("RNG") + } + { Label6 Label + MoveScaled(1,21,20,4) + Text = ("Handshake") + } + } + { Separator3 Separator + MoveScaled(58,7,0,14) + } + { Panel5 VBox + MoveScaled(60,1,36,24) + Expand = True + Margin = True + { Label7 Label + MoveScaled(2,1,16,3) + Font = Font["Bold"] + Text = ("Message") + } + { txtMessage TextLabel + MoveScaled(0,10,35,13) + Expand = True + } + } + } + { !SerialPort1 #SerialPort + #MoveScaled(75,4) + #Public = True + Parity = SerialPort.Even + Speed = 75 + DataBits = SerialPort.Bits5 + } +} diff --git a/app/examples/Networking/SerialPort/.src/Module_Config.module b/app/examples/Networking/SerialPort/.src/Module_Config.module new file mode 100644 index 00000000..fa21f22d --- /dev/null +++ b/app/examples/Networking/SerialPort/.src/Module_Config.module @@ -0,0 +1,92 @@ +' Gambas module file + +Public AppSettings As Settings + +Public Sub InitDefaultPath() + + AppSettings = New Settings + +End + +Public Sub StoreSerialSettings(Port As SerialPort, PIndex As Integer) + 'Parameters are for example stored in home/.config/Gambas3/RS232.conf + + Dim Headline As String + + HeadLine = "SerialPort " & Format(PIndex, "0") + + 'Store all parameters + With Port + AppSettings[HeadLine & "/Portname"] = .PortName + AppSettings[HeadLine & "/Speed"] = .Speed + AppSettings[HeadLine & "/DataBits"] = .DataBits + AppSettings[HeadLine & "/Stopbits"] = .StopBits + AppSettings[HeadLine & "/Parity"] = .Parity + AppSettings[HeadLine & "/FlowControl"] = .FlowControl + End With + +End + +Public Sub RestoreSerialSettings(Port As SerialPort, Pindex As Integer) + 'Parameters are for example stored in home/.config/Gambas3/RS232.conf + + Dim i As Integer + Dim HeadLine As String + + 'Set parameter headline + HeadLine = "SerialPort " & Format(PIndex, "0") + + 'Retrieve all RS232 settings from config file + With Port + .PortName = AppSettings[HeadLine & "/Portname", "/dev/ttyS0"] + .Speed = AppSettings[HeadLine & "/Speed", "9600"] + .DataBits = AppSettings[HeadLine & "/DataBits", "8"] + .StopBits = AppSettings[HeadLine & "/StopBits", "1"] + .Parity = AppSettings[HeadLine & "/Parity", 0] + .FlowControl = AppSettings[HeadLine & "/FlowControl", 0] + End With + + 'Set the RS232 combos accordingly: + + 'Finding Serial Device in Combo for Displaying + i = FMain.ComboPortDeviceName.Find(FMain.SerialPort1.PortName) + FMain.ComboPortDeviceName.index = i + + 'Finding Speed in Combo for Displaying + i = FMain.ComboSpeed.find(FMain.SerialPort1.Speed) + FMain.ComboSpeed.Index = i + + 'Finding DataBits in Combo for Displaying + i = Fmain.ComboDataBits.find(FMain.Serialport1.DataBits) + Fmain.ComboDataBits.Index = i + + 'Finding StopBits in Combo for Displaying + i = Fmain.ComboStopBits.find(FMain.Serialport1.StopBits) + Fmain.ComboStopBits.Index = i + + 'Displaying Parity in Combo (0=NON, 1=EVEN, 2=ODD) + FMain.ComboParity.index = FMain.SerialPort1.Parity + + 'Displaying Flow-Control in Combo + FMain.ComboHandShake.index = FMain.SerialPort1.FlowControl + +End + +Public Sub StoreFormPosition(F As Form) + + With F + AppSettings[.name & "/left"] = .left + AppSettings[.name & "/top"] = .top + End With + +End + +Public Sub RestoreFormPosition(F As Form) + + With F + .left = AppSettings[.name & "/left", 0] + .top = AppSettings[.name & "/top", 0] + End With + +End + diff --git a/app/examples/Networking/SerialPort/.src/Module_RS232.module b/app/examples/Networking/SerialPort/.src/Module_RS232.module new file mode 100644 index 00000000..dcac6bda --- /dev/null +++ b/app/examples/Networking/SerialPort/.src/Module_RS232.module @@ -0,0 +1,93 @@ +' Gambas module file + +Public Sub EnumerateSerialInterfaces() + + Dim i As Integer + + With FMain + + 'Set operational parameters (used for testing only) + .SerialPort1.Speed = "19200" + .SerialPort1.Parity = 0 + .SerialPort1.DataBits = "8" + .SerialPort1.StopBits = "1" + .SerialPort1.FlowControl = 0 + + 'Clear Device Combo + FMain.ComboPortDeviceName.Clear() + + 'Standard USB Ports + For i = 0 To 8 + .SerialPort1.PortName = "/dev/ttyS" & Format(i, "0") + Try .SerialPort1.Open + If Not Error + .ComboPortDeviceName.Add(.SerialPort1.PortName) + .SerialPort1.close + Endif + Next + + 'USB based ports + For i = 0 To 8 + .SerialPort1.PortName = "/dev/ttyUSB" & Format(i, "0") + Try .SerialPort1.Open + If Not Error + .ComboPortDeviceName.Add(.SerialPort1.PortName) + .SerialPort1.close + Endif + Next + + End With + +End + +Public Sub DisplaySerialInput(RX As String) + 'This Routine displays serial data + + Dim R As String + Dim i As Integer + + With FMain + + 'Set cursor to the end of the text + .TextArea1.Pos = .TextArea1.Length + + 'CR is ommited because the TextArea control displays it as CR+LF + For i = 1 To Len(RX) + R = Mid$(RX, i, 1) + If R <> Chr$(13) Then + 'Amend character + .TextArea1.insert(R) + Endif + Next + + ' 'Use the following alternatively to the above. It displays ASCII-values in [] if it is a control character (ASCII-value < 32) + ' For i = 1 To Len(RX) + ' R = Mid$(RX, i, 1) + ' If Asc(R) > 31 Then + ' .TextArea1.insert(R) + ' Else + ' If R <> Chr$(13) 'Ommit CR because the TextArea control display it as CR+LF + ' .TextArea1.insert("[" & Format(Asc(R), "0") & "]" & R) + ' Else + ' .TextArea1.insert("[" & Format(Asc(R), "0") & "]") + ' Endif + ' Endif + ' Next + + End With + +End + +Public Sub CheckRS232Status() + 'This dipslays the status of the RS232 handshake lines + + With FMain.SerialPort1 + FMain.CheckDSR.Value = .DSR + FMain.CheckDTR.Value = .DTR + FMain.CheckCTS.Value = .CTS + FMain.CheckRTS.Value = .RTS + FMain.CheckDCD.Value = .DCD + FMain.CheckRNG.Value = .RNG + End With + +End diff --git a/app/examples/Networking/SerialPort/serialport.png b/app/examples/Networking/SerialPort/serialport.png new file mode 100644 index 0000000000000000000000000000000000000000..e1d380b9de706bf37bfce82f0019b9b266f556c9 GIT binary patch literal 2498 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 c8O-2+0W=z, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: ServerSocket\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 22:37+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Server socket example" +msgstr "Exemple de sòcol de servidor" + +#: FrmMain.class:200 +msgid "Server Socket Example" +msgstr "Exemple de servidor de sòcol" + +#: FrmMain.class:206 +msgid "Listen" +msgstr "Escolta" + +#: FrmMain.class:211 +msgid "32340" +msgstr "-" + +#: FrmMain.class:221 +msgid "Close" +msgstr "Tanca" + +#: FrmMain.class:226 +msgid "Port" +msgstr "-" + +#: FrmMain.class:231 +msgid "Pause" +msgstr "Pausa" + +#: FrmMain.class:236 +msgid "Max. number of clients" +msgstr "Nombre màxim de clients" + +#: FrmMain.class:243 +msgid "1" +msgstr "-" + +#: FrmMain.class:243 +msgid "10" +msgstr "-" + +#: FrmMain.class:243 +msgid "2" +msgstr "-" + +#: FrmMain.class:243 +msgid "3" +msgstr "-" + +#: FrmMain.class:243 +msgid "4" +msgstr "-" + +#: FrmMain.class:243 +msgid "5" +msgstr "-" + +#: FrmMain.class:243 +msgid "6" +msgstr "-" + +#: FrmMain.class:243 +msgid "7" +msgstr "-" + +#: FrmMain.class:243 +msgid "8" +msgstr "-" + +#: FrmMain.class:243 +msgid "9" +msgstr "-" + +#: FrmMain.class:243 +msgid "No limit" +msgstr "Sense límit" + +#: FrmMain.class:248 +msgid "Type" +msgstr "Tipus" + +#: FrmMain.class:254 +msgid "TCP" +msgstr "-" + +#: FrmMain.class:254 +msgid "UNIX" +msgstr "-" + +#: FrmMain.class:259 +msgid "Path" +msgstr "Camí" + diff --git a/app/examples/Networking/ServerSocket/.lang/cs.po b/app/examples/Networking/ServerSocket/.lang/cs.po new file mode 100644 index 00000000..3eb2bf05 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/cs.po @@ -0,0 +1,104 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Server socket example" +msgstr "Příklad servrového soketu" + +#: FrmMain.form:20 +msgid "Server Socket Example" +msgstr "Příkald Servrového soketu" + +#: FrmMain.form:26 +msgid "Listen" +msgstr "Naslouchat" + +#: FrmMain.form:31 +msgid "32340" +msgstr "-" + +#: FrmMain.form:41 +msgid "Close" +msgstr "Zavřír" + +#: FrmMain.form:46 +msgid "Port" +msgstr "-" + +#: FrmMain.form:51 +msgid "Pause" +msgstr "Pauza" + +#: FrmMain.form:56 +msgid "Max. number of clients" +msgstr "Max. počet klientů" + +#: FrmMain.form:63 +msgid "1" +msgstr "-" + +#: FrmMain.form:63 +msgid "10" +msgstr "-" + +#: FrmMain.form:63 +msgid "2" +msgstr "-" + +#: FrmMain.form:63 +msgid "3" +msgstr "-" + +#: FrmMain.form:63 +msgid "4" +msgstr "-" + +#: FrmMain.form:63 +msgid "5" +msgstr "-" + +#: FrmMain.form:63 +msgid "6" +msgstr "-" + +#: FrmMain.form:63 +msgid "7" +msgstr "-" + +#: FrmMain.form:63 +msgid "8" +msgstr "-" + +#: FrmMain.form:63 +msgid "9" +msgstr "-" + +#: FrmMain.form:63 +msgid "No limit" +msgstr "Bez limitu" + +#: FrmMain.form:68 +msgid "Type" +msgstr "Typ" + +#: FrmMain.form:74 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:74 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:79 +msgid "Path" +msgstr "Cesta" diff --git a/app/examples/Networking/ServerSocket/.lang/es.po b/app/examples/Networking/ServerSocket/.lang/es.po new file mode 100644 index 00000000..9cf52137 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/es.po @@ -0,0 +1,99 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FrmMain.class:161 +msgid "Server Socket Example" +msgstr "Ejemplo de Servidor Socket" + +#: FrmMain.class:166 +msgid "Listen" +msgstr "Escuchar" + +#: FrmMain.class:171 +msgid "32340" +msgstr "32340" + +#: FrmMain.class:181 +msgid "Close" +msgstr "Cerrar" + +#: FrmMain.class:186 +msgid "Port :" +msgstr "Puerto :" + +#: FrmMain.class:191 +msgid "Wait" +msgstr "Esperar" + +#: FrmMain.class:196 +msgid "Maximum number of clients:" +msgstr "Número máximo de clientes:" + +#: FrmMain.class:202 +msgid "(no Limit)" +msgstr "(sin límite)" + +#: FrmMain.class:202 +msgid "1" +msgstr "1" + +#: FrmMain.class:202 +msgid "2" +msgstr "2" + +#: FrmMain.class:202 +msgid "3" +msgstr "3" + +#: FrmMain.class:202 +msgid "4" +msgstr "4" + +#: FrmMain.class:202 +msgid "5" +msgstr "5" + +#: FrmMain.class:202 +msgid "6" +msgstr "6" + +#: FrmMain.class:202 +msgid "7" +msgstr "7" + +#: FrmMain.class:202 +msgid "8" +msgstr "8" + +#: FrmMain.class:202 +msgid "9" +msgstr "9" + +#: FrmMain.class:202 +msgid "10" +msgstr "10" + +#: FrmMain.class:207 +msgid "Type :" +msgstr "Tipo :" + +#: FrmMain.class:214 +msgid "TCP" +msgstr "TCP" + +#: FrmMain.class:214 +msgid "UNIX" +msgstr "UNIX" + +#: FrmMain.class:219 +msgid "Path :" +msgstr "Ruta :" diff --git a/app/examples/Networking/ServerSocket/.lang/nl.po b/app/examples/Networking/ServerSocket/.lang/nl.po new file mode 100644 index 00000000..dbe1f7b7 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/nl.po @@ -0,0 +1,104 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-21 23:59+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Server socket example" +msgstr "Server socket voorbeeld" + +#: FrmMain.form:20 +msgid "Server Socket Example" +msgstr "Server Socket voorbeeld" + +#: FrmMain.form:26 +msgid "Listen" +msgstr "Luister" + +#: FrmMain.form:31 +msgid "32340" +msgstr "-" + +#: FrmMain.form:41 +msgid "Close" +msgstr "Sluiten" + +#: FrmMain.form:46 +msgid "Port" +msgstr "Poort" + +#: FrmMain.form:51 +msgid "Pause" +msgstr "-" + +#: FrmMain.form:56 +msgid "Max. number of clients" +msgstr "Max. aantal cliënten" + +#: FrmMain.form:63 +msgid "No limit" +msgstr "Geen limiet" + +#: FrmMain.form:63 +msgid "1" +msgstr "-" + +#: FrmMain.form:63 +msgid "2" +msgstr "-" + +#: FrmMain.form:63 +msgid "3" +msgstr "-" + +#: FrmMain.form:63 +msgid "4" +msgstr "-" + +#: FrmMain.form:63 +msgid "5" +msgstr "-" + +#: FrmMain.form:63 +msgid "6" +msgstr "-" + +#: FrmMain.form:63 +msgid "7" +msgstr "-" + +#: FrmMain.form:63 +msgid "8" +msgstr "-" + +#: FrmMain.form:63 +msgid "9" +msgstr "-" + +#: FrmMain.form:63 +msgid "10" +msgstr "-" + +#: FrmMain.form:68 +msgid "Type" +msgstr "-" + +#: FrmMain.form:74 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:74 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:79 +msgid "Path" +msgstr "Pad" + diff --git a/app/examples/Networking/ServerSocket/.lang/ru.po b/app/examples/Networking/ServerSocket/.lang/ru.po new file mode 100644 index 00000000..7aa6de13 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/ru.po @@ -0,0 +1,172 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/ServerSocket/.project:22 +msgid "Server socket example" +msgstr "Пример сокета сервера" + +#: app/examples/Networking/ServerSocket/.project:23 +msgid "" +"Server socket example.\n" +"\n" +"This example shows how to implement a TCP server accepting and managing multiple connections in the same process. You can use the 'ClientSocket' example as a testing peer." +msgstr "" +"Пример сокета сервера\n" +"\n" +"В этом примере показано, как реализовать сервер TCP, принимающий и управляющий несколькими подключениями в одном процессе. Вы можете использовать пример «Сокет клиента» в качестве тестового партнёра." + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:34 +msgid "Unable to bind socket" +msgstr "Невозможно привязать сокет" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:47 +msgid "Connection request from :" +msgstr "Запрос на соединение от:" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:49 +msgid "Connection request accepted" +msgstr "Запрос на соединение принят" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:58 +msgid "Connection from" +msgstr "Соединение от" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:58 +msgid "accepted" +msgstr "принято" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:58 +msgid "local port" +msgstr "локальный порт" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:106 +msgid "Socket #" +msgstr "Сокет #" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:117 +msgid "-- Client working --\n" +msgstr "-- Клиент работает --\n" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:123 +msgid "Client #" +msgstr "Клиент #" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:123 +msgid "Closed" +msgstr "Закрыт" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:131 app/examples/Networking/ServerSocket/.src/FrmMain.form:53 +msgid "Path" +msgstr "Путь" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:157 app/examples/Networking/ServerSocket/.src/FrmMain.form:30 +msgid "Pause" +msgstr "Пауза" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.class:160 +msgid "Resume" +msgstr "Возобновить" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:5 +msgid "Server Socket Example" +msgstr "Пример сокета сервера" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:10 +msgid "Listen" +msgstr "Слушать" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:14 +msgid "32340" +msgstr "32340" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:22 +msgid "Close" +msgstr "Закрыть" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:26 +msgid "Port" +msgstr "Порт" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:34 +msgid "Max. number of clients" +msgstr "Макс. количество клиентов" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "No limit" +msgstr "Неограниченно" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "1" +msgstr "1" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "2" +msgstr "2" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "3" +msgstr "3" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "4" +msgstr "4" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "5" +msgstr "5" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "6" +msgstr "6" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "7" +msgstr "7" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "8" +msgstr "8" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "9" +msgstr "9" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:40 +msgid "10" +msgstr "10" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:44 +msgid "Type" +msgstr "Тип" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:49 +msgid "TCP" +msgstr "TCP" + +#: app/examples/Networking/ServerSocket/.src/FrmMain.form:49 +msgid "UNIX" +msgstr "UNIX" + diff --git a/app/examples/Networking/ServerSocket/.project b/app/examples/Networking/ServerSocket/.project new file mode 100644 index 00000000..a456eefd --- /dev/null +++ b/app/examples/Networking/ServerSocket/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Server socket example +Startup=FrmMain +Icon=serversocket.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.net +Component=gb.net.curl +Description="Server socket example.\n\nThis example shows how to implement a TCP server accepting and managing multiple connections in the same process. You can use the 'ClientSocket' example as a testing peer." +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/ServerSocket/.src/FrmMain.class b/app/examples/Networking/ServerSocket/.src/FrmMain.class new file mode 100644 index 00000000..285366b7 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.src/FrmMain.class @@ -0,0 +1,176 @@ +' Gambas class file + +Private Waiting As Boolean +Private $iId As Integer + +Public Sub btnListen_Click() + + If cmbType.Index = 0 Then + 'TCP + MyServerSocket.Type = Net.Internet + ' The port to listen to + MyServerSocket.Port = Val(txtPort.Text) + ' we start listening + MyServerSocket.Listen(cmbMaxClient.Index) + Else + ' UNIX + MyServerSocket.Type = Net.Local ' You could also use Net.Unix + MyServerSocket.Path = txtPath.Text + MyServerSocket.Listen(cmbMaxClient.Index) + End If + If MyServerSocket.Status = Net.Active Then + ' listening + btnListen.Enabled = False + btnClose.Enabled = True + cmbMaxClient.Enabled = False + cmbType.Enabled = False + txtPath.Enabled = False + End If + +End + +Public Sub MyServerSocket_Error() + + Message.Error(("Unable to bind socket")) + +End + +Public Sub MyServerSocket_Connection(sHost As String) + '******************************* + ' A client has arrived! + ' let's accept it + + Dim Obj As Socket + + If MyServerSocket.Status <= Net.Inactive Then Return + If cmbType.Index = 0 Then + txtLog.Text = txtLog.Text & ("Connection request from :") & " " & sHost & Chr(13) & Chr(10) + Else + txtLog.Text = txtLog.Text & ("Connection request accepted") & Chr(13) & Chr(10) + End If + + Obj = MyServerSocket.Accept() + Obj.Blocking = False + Inc $iId + Obj.Tag = [$iId, 0, ""] + + If Obj.Status = Net.Connected And cmbType.Index = 0 Then + txtLog.Text = txtLog.Text & ("Connection from") & " " & Obj.RemoteHost & ":" & Obj.RemotePort & " " & ("accepted") & " (" & ("local port") & " " & Obj.LocalPort & ")" & Chr(13) & Chr(10) + End If + +End + +Public Sub Socket_Write() + + Dim hSocket As Socket = Last + Dim iInd As Integer + + 'Debug hSocket;; hSocket.Tag + iInd = hSocket.Tag[1] + If iInd < 0 Then Return + + Do + Inc iInd + If iInd > 10 Then + hSocket.Tag[1] = -1 + Return + Endif + + 'Debug iInd + Try Print #hSocket, iInd & ":" & hSocket.Tag[2] & Space$(512) & "\n"; + If Error Then + Debug Error.Text + Break + Endif + Loop + + hSocket.Tag[1] = iInd + +Catch + +End + +Public Sub Socket_Read() + + Dim sBuf As String + Dim iInd As Integer + + '****************************** + ' When some data arrives to + ' our server, we respond with + ' an echo + '***************************** + + If Last.Status <> Net.Connected Then Return + Read #Last, sBuf, Lof(Last) + txtLog.Text &= ("Socket #") & Last.Tag[0] & " --> " & sBuf & "\n" + + Last.Tag[1] = 0 + Last.Tag[2] = sBuf + + Socket_Write + +End + +Public Sub Socket_Ready() + + txtLog.Text &= ("-- Client working --\n") + +End + +Public Sub Socket_Closed() + + txtLog.Text &= ("Client #") & Last.Tag[0] & " " & ("Closed") & "\n" + +End + +Public Sub Form_Open() + + txtPath.Text = Application.Path & "/" & "gambas" + txtPath.Enabled = False + TextLabel3.Text = ("Path") + +End + +Public Sub btnClose_Click() + + MyServerSocket.Close() + btnClose.Enabled = False + btnListen.Enabled = True + cmbMaxClient.Enabled = True + cmbType.Enabled = True + cmbType_Click() + +End + +Public Sub Form_Close() + + ' We have to be sure of closing the server before exiting + MyServerSocket.Close() + +End + +Public Sub btnPause_Click() + + If Waiting Then + MyServerSocket.Resume() + btnPause.Text = ("Pause") + Else + MyServerSocket.Pause() + btnPause.Text = ("Resume") + End If + Waiting = Not Waiting + +End + +Public Sub cmbType_Click() + + If cmbType.Index = 0 Then + txtPort.Enabled = True + txtPath.Enabled = False + Else + txtPort.Enabled = False + txtPath.Enabled = True + End If + +End diff --git a/app/examples/Networking/ServerSocket/.src/FrmMain.form b/app/examples/Networking/ServerSocket/.src/FrmMain.form new file mode 100644 index 00000000..c560ad75 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.src/FrmMain.form @@ -0,0 +1,61 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(36,28,79,51) + Text = ("Server Socket Example") + Icon = Picture["serversocket.png"] + Resizable = False + { btnListen Button + MoveScaled(60,1,18,4) + Text = ("Listen") + } + { txtPort TextBox + MoveScaled(16,6,43,4) + Text = ("32340") + } + { txtLog TextArea + MoveScaled(1,22,77,28) + } + { btnClose Button + MoveScaled(60,6,18,4) + Enabled = False + Text = ("Close") + } + { TextLabel1 TextLabel + MoveScaled(1,6,14,4) + Text = ("Port") + } + { btnPause Button + MoveScaled(1,17,16,4) + Text = ("Pause") + } + { Label1 Label + MoveScaled(18,17,38,4) + Text = ("Max. number of clients") + Alignment = Align.Right + } + { cmbMaxClient ComboBox + MoveScaled(57,17,21,4) + ReadOnly = True + List = [("No limit"), ("1"), ("2"), ("3"), ("4"), ("5"), ("6"), ("7"), ("8"), ("9"), ("10")] + } + { TextLabel2 TextLabel + MoveScaled(1,1,14,4) + Text = ("Type") + } + { cmbType ComboBox + MoveScaled(16,1,43,4) + ReadOnly = True + List = [("TCP"), ("UNIX")] + } + { TextLabel3 TextLabel + MoveScaled(1,11,14,4) + Text = ("Path") + } + { txtPath TextBox + MoveScaled(16,11,62,4) + } + { MyServerSocket #ServerSocket + #MoveScaled(8,3) + } +} diff --git a/app/examples/Networking/ServerSocket/serversocket.png b/app/examples/Networking/ServerSocket/serversocket.png new file mode 100644 index 0000000000000000000000000000000000000000..9049f7acf57321531fc3f227f17e209bcabc3054 GIT binary patch literal 2122 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 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/UDPServerClient/.lang/ca.po b/app/examples/Networking/UDPServerClient/.lang/ca.po new file mode 100644 index 00000000..9decadc0 --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/ca.po @@ -0,0 +1,104 @@ +# Catalan translation of UDPServerClient +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the UDPServerClient package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: UDPServerClient\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:10+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FrmClient.form:54 +msgid "127.0.0.1" +msgstr "-" + +#: FrmClient.form:59 FrmServer.form:42 +msgid "32340" +msgstr "-" + +#: FrmClient.form:18 +msgid "Client" +msgstr "Client" + +#: FrmClient.form:30 +msgid "Client Side" +msgstr "Costat del client" + +#: FrmClient.class:60 FrmServer.class:73 +msgid "Data --> " +msgstr "Dades -->" + +#: FrmClient.class:50 FrmServer.class:59 +msgid "Error Receiving Data" +msgstr "Error rebent dades" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Sending Data" +msgstr "Error enviant dades" + +#: FrmClient.form:39 +msgid "Host IP" +msgstr "IP del servidor" + +#: FrmServer.form:19 +msgid "New UDP Client window" +msgstr "Nova finestra de client UDP" + +#: FrmClient.form:48 +msgid "Port" +msgstr "Port" + +#: FrmServer.form:30 +msgid "Port :" +msgstr "Port:" + +#: FrmClient.class:59 FrmServer.class:72 +msgid "Received data from : " +msgstr "Dades rebudes des de:" + +#: FrmClient.form:76 +msgid "Send Data" +msgstr "Envia dades" + +#: FrmClient.form:64 FrmServer.form:47 +msgid "Start !" +msgstr "Endavant!" + +#: FrmClient.class:15 FrmServer.class:24 +msgid "Stop" +msgstr "Atura" + +#: FrmServer.form:24 +msgid "Super String Reverse Server" +msgstr "Super servidor de cadenes inverses" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "System does not allow to create a socket" +msgstr "El sistema no permet crear un sòcol" + +#: .project:1 +msgid "UDP sockets example" +msgstr "Exemple de sòcols UDP" + +#: FrmServer.form:14 +msgid "UDP Test" +msgstr "Prova l'UDP" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "Unable to Bind to that port" +msgstr "No es pot vincular amb aquell port" + +#: FrmClient.form:91 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Escriviu aquí dades per enviar i després premeu \"Envia dades\"" + diff --git a/app/examples/Networking/UDPServerClient/.lang/cs.po b/app/examples/Networking/UDPServerClient/.lang/cs.po new file mode 100644 index 00000000..82d4237b --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/cs.po @@ -0,0 +1,96 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "UDP sockets example" +msgstr "Příklad UDP spojení" + +#: FrmClient.class:15 FrmServer.class:24 +msgid "Stop" +msgstr "-" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "Unable to Bind to that port" +msgstr "Nelze se spojit s portem" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "System does not allow to create a socket" +msgstr "Systém nemá přístup k vytváření soketu" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Sending Data" +msgstr "Chyba posílaných dat" + +#: FrmClient.class:50 FrmServer.class:59 +msgid "Error Receiving Data" +msgstr "Chyba příjmaných dat" + +#: FrmClient.class:59 FrmServer.class:72 +msgid "Received data from : " +msgstr "Příjmané data od :" + +#: FrmClient.class:60 FrmServer.class:73 +msgid "Data --> " +msgstr "-" + +#: FrmClient.form:18 +msgid "Client" +msgstr "Klient" + +#: FrmClient.form:30 +msgid "Client Side" +msgstr "Klientská strana" + +#: FrmClient.form:39 +msgid "Host IP" +msgstr "-" + +#: FrmClient.form:48 +msgid "Port" +msgstr "-" + +#: FrmClient.form:54 +msgid "127.0.0.1" +msgstr "-" + +#: FrmClient.form:59 FrmServer.form:42 +msgid "32340" +msgstr "-" + +#: FrmClient.form:64 FrmServer.form:47 +msgid "Start !" +msgstr "-" + +#: FrmClient.form:76 +msgid "Send Data" +msgstr "Poslat data" + +#: FrmClient.form:91 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Zapište zde data na poslání a zmáčkněte \"Poslat data\"" + +#: FrmServer.form:14 +msgid "UDP Test" +msgstr "-" + +#: FrmServer.form:19 +msgid "New UDP Client window" +msgstr "Nové okno UDP klienta" + +#: FrmServer.form:24 +msgid "Super String Reverse Server" +msgstr "Super server na obrácení řetězeců" + +#: FrmServer.form:30 +msgid "Port :" +msgstr "-" diff --git a/app/examples/Networking/UDPServerClient/.lang/es.po b/app/examples/Networking/UDPServerClient/.lang/es.po new file mode 100644 index 00000000..71c1f02b --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/es.po @@ -0,0 +1,101 @@ +# #-#-#-#-# FrmClient.pot (PACKAGE VERSION) #-#-#-#-# +# /home/daniel/GAMBAS/gambas-0.93pre1/examples/Networking/UDPServerClient/FrmClient.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# FrmServer.pot (PACKAGE VERSION) #-#-#-#-# +# /home/daniel/GAMBAS/gambas-0.93pre1/examples/Networking/UDPServerClient/FrmServer.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FrmClient.class:14 FrmServer.class:22 +msgid "Stop" +msgstr "Parar" + +#: FrmClient.class:20 FrmServer.class:27 +msgid "Start !" +msgstr "¡ Iniciar !" + +#: FrmClient.class:42 FrmServer.class:51 +msgid "Unable to Bind to that port" +msgstr "Imposible abrir el puerto" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "System does not allow to create a socket" +msgstr "El sistema no permite crear un nuevo socket" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "Error Sending Data" +msgstr "Error enviando datos" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Receiving Data" +msgstr "Error recibiendo datos" + +#: FrmClient.class:57 FrmServer.class:70 +msgid "Received data from : " +msgstr "Datos recibidos desde:" + +#: FrmClient.class:58 FrmServer.class:71 +msgid "Data --> " +msgstr "Datos -->" + +#: FrmClient.class:93 +msgid "Client" +msgstr "Cliente" + +#: FrmClient.class:105 +msgid "Client Side" +msgstr "Lado cliente" + +#: FrmClient.class:114 +msgid "Host IP" +msgstr "IP del Host" + +#: FrmClient.class:123 +msgid "Port" +msgstr "Puerto" + +#: FrmClient.class:129 +msgid "127.0.0.1" +msgstr "127.0.0.1" + +#: FrmClient.class:134 FrmServer.class:129 +msgid "32340" +msgstr "32340" + +#: FrmClient.class:151 +msgid "Send Data" +msgstr "Enviar datos" + +#: FrmClient.class:165 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Escriba aquí los datos a enviar y pulse \"Enviar datos\"" + +#: FrmServer.class:101 +msgid "UDP Test" +msgstr "Test UDP" + +#: FrmServer.class:106 +msgid "New UDP Client window" +msgstr "Nueva ventana de cliente UDP" + +#: FrmServer.class:111 +msgid "Super String Reverse Server" +msgstr "Superservidor de Inversión de Cadenas" + +#: FrmServer.class:117 +msgid "Port :" +msgstr "Puerto:" diff --git a/app/examples/Networking/UDPServerClient/.lang/nl.po b/app/examples/Networking/UDPServerClient/.lang/nl.po new file mode 100644 index 00000000..34cb63bc --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/nl.po @@ -0,0 +1,96 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-21 23:57+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "UDP sockets example" +msgstr "UDP sockets voorbeeld" + +#: FrmClient.form:18 +msgid "Client" +msgstr "Cliënt" + +#: FrmClient.form:30 +msgid "Client Side" +msgstr "Cliënt zijde" + +#: FrmClient.form:39 +msgid "Host IP" +msgstr "Gast IP" + +#: FrmClient.form:48 +msgid "Port" +msgstr "Poort" + +#: FrmClient.form:54 +msgid "127.0.0.1" +msgstr "-" + +#: FrmClient.form:59 FrmServer.form:42 +msgid "32340" +msgstr "-" + +#: FrmClient.form:64 FrmServer.form:47 +msgid "Start !" +msgstr "-" + +#: FrmClient.form:76 +msgid "Send Data" +msgstr "Verzend Data" + +#: FrmClient.form:91 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Schrijf hier de data om te verzenden \"Verzend Data\"" + +#: FrmClient.class:15 FrmServer.class:24 +msgid "Stop" +msgstr "-" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "Unable to Bind to that port" +msgstr "Kan niet binden met die poort" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "System does not allow to create a socket" +msgstr "Systeem staat niet toe een socket te creëren" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Sending Data" +msgstr "Fout bij data verzending" + +#: FrmClient.class:50 FrmServer.class:59 +msgid "Error Receiving Data" +msgstr "Fout bij data ontvangst" + +#: FrmClient.class:59 FrmServer.class:72 +msgid "Received data from : " +msgstr "Data ontvangen van:" + +#: FrmClient.class:60 FrmServer.class:73 +msgid "Data --> " +msgstr "-" + +#: FrmServer.form:14 +msgid "UDP Test" +msgstr "-" + +#: FrmServer.form:19 +msgid "New UDP Client window" +msgstr "Nieuw UDP cliënt venster" + +#: FrmServer.form:24 +msgid "Super String Reverse Server" +msgstr "-" + +#: FrmServer.form:30 +msgid "Port :" +msgstr "Poort:" + diff --git a/app/examples/Networking/UDPServerClient/.lang/ru.po b/app/examples/Networking/UDPServerClient/.lang/ru.po new file mode 100644 index 00000000..e59d53f4 --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/ru.po @@ -0,0 +1,110 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/UDPServerClient/.project:18 +msgid "UDP sockets example" +msgstr "Пример сокетов UDP" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:15 app/examples/Networking/UDPServerClient/.src/FrmServer.class:24 +msgid "Stop" +msgstr "Стоп" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:21 app/examples/Networking/UDPServerClient/.src/FrmClient.class:39 app/examples/Networking/UDPServerClient/.src/FrmClient.form:44 app/examples/Networking/UDPServerClient/.src/FrmServer.class:29 app/examples/Networking/UDPServerClient/.src/FrmServer.class:49 app/examples/Networking/UDPServerClient/.src/FrmServer.form:32 +msgid "Start !" +msgstr "Начать!" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:44 app/examples/Networking/UDPServerClient/.src/FrmServer.class:53 +msgid "Unable to Bind to that port" +msgstr "Невозможно привязать к этому порту" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:46 app/examples/Networking/UDPServerClient/.src/FrmServer.class:55 +msgid "System does not allow to create a socket" +msgstr "Система не позволяет создать сокет" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:48 app/examples/Networking/UDPServerClient/.src/FrmServer.class:57 +msgid "Error Sending Data" +msgstr "Ошибка при отправке данных" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:50 app/examples/Networking/UDPServerClient/.src/FrmServer.class:59 +msgid "Error Receiving Data" +msgstr "Ошибка при получении данных" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:59 app/examples/Networking/UDPServerClient/.src/FrmServer.class:72 +msgid "Received data from : " +msgstr "Полученные данные из: " + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.class:60 app/examples/Networking/UDPServerClient/.src/FrmServer.class:73 +msgid "Data --> " +msgstr "Данные --> " + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:5 +msgid "Client" +msgstr "Клиент" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:15 +msgid "Client Side" +msgstr "Сторона клиента" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:23 +msgid "Host IP" +msgstr "IP хоста" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:31 +msgid "Port" +msgstr "Порт" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:36 +msgid "127.0.0.1" +msgstr "127.0.0.1" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:40 app/examples/Networking/UDPServerClient/.src/FrmServer.form:28 +msgid "32340" +msgstr "32340" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:53 +msgid "Send Data" +msgstr "Отправить данные" + +#: app/examples/Networking/UDPServerClient/.src/FrmClient.form:65 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Напишите здесь данные для отправки, затем нажмите «Отправить данные»" + +#: app/examples/Networking/UDPServerClient/.src/FrmServer.form:5 +msgid "UDP Test" +msgstr "Тест UDP" + +#: app/examples/Networking/UDPServerClient/.src/FrmServer.form:9 +msgid "New UDP Client window" +msgstr "Новое окно UDP-клиента" + +#: app/examples/Networking/UDPServerClient/.src/FrmServer.form:13 +msgid "Super String Reverse Server" +msgstr "Суперстроковый обрат. сервер" + +#: app/examples/Networking/UDPServerClient/.src/FrmServer.form:18 +msgid "Port :" +msgstr "Порт:" + diff --git a/app/examples/Networking/UDPServerClient/.project b/app/examples/Networking/UDPServerClient/.project new file mode 100644 index 00000000..24f98966 --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=UDP sockets example +Startup=FrmServer +Icon=udpsocket.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.net +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@desnouettes +License=General Public Licence diff --git a/app/examples/Networking/UDPServerClient/.src/FrmClient.class b/app/examples/Networking/UDPServerClient/.src/FrmClient.class new file mode 100644 index 00000000..dd1ba40e --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmClient.class @@ -0,0 +1,73 @@ +' Gambas class file +Public UDPClient As UdpSocket +Public Sub Form_Open() + UDPClient = New UdpSocket As "UDPClient" +End +'/////////////////////////////////////////////////// +' Client stuff +'/////////////////////////////////////////////////// +Public Sub Button2_Click() + + If UDPClient.Status <= Net.Inactive Then + 'UDPClient.Path = "/tmp/gambas-udp-client" + UDPClient.Bind + If UDPClient.Status = Net.Active Then + Button2.Text = ("Stop") + TxtData.Enabled = True + Button3.Enabled = True + End If + Else + Close #UDPClient + Button2.Text = ("Start !") + TxtData.Enabled = False + Button3.Enabled = False + End If + + +End + +Public Sub Button3_Click() + + UDPCLient.TargetPort = TxtPort.Text + UDPCLient.TargetHost = TxtIP.Text + 'UDPClient.TargetPath = "/tmp/gambas-udp-socket" + Write #UDPCLient, TxtData.Text, Len(TxtData.Text) +End + +Public Sub UDPClient_Error() + + Button2.Text = ("Start !") + TxtData.Enabled = False + Button3.Enabled = False + Select Case UDPClient.Status + Case Net.CannotBindSocket + Message.Error(("Unable to Bind to that port")) + Case Net.CannotCreateSocket + Message.Error(("System does not allow to create a socket")) + Case Net.CannotRead + Message.Error(("Error Sending Data")) + Case Net.CannotWrite + Message.Error(("Error Receiving Data")) + End Select + +End + +Public Sub UDPClient_Read() + + Dim sCad As String + Read #UDPClient, sCad, Lof(UDPClient) + TxtRecClient.Text = TxtRecClient.Text & ("Received data from : ") & UDPClient.SourceHost & ":" & UdpClient.SourcePort & Chr(13) & Chr(10) + TxtRecClient.Text = TxtRecClient.Text & ("Data --> ") & sCad & Chr(13) & Chr(10) + +End + + +Public Sub Form_Close() + + If UDPClient.Status > 0 Then Close #UDPClient + + +End + + + diff --git a/app/examples/Networking/UDPServerClient/.src/FrmClient.form b/app/examples/Networking/UDPServerClient/.src/FrmClient.form new file mode 100644 index 00000000..657d7201 --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmClient.form @@ -0,0 +1,68 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(10.1429,48.5714,93,43) + Text = ("Client") + Resizable = False + { Panel2 Panel + MoveScaled(0,0,93,43) + Background = &H4F87E7& + { Label3 Label + MoveScaled(0,1,29,4) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF7F& + Text = ("Client Side") + Alignment = Align.Center + } + { Label4 Label + MoveScaled(1,5,12,4) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF7F& + Text = ("Host IP") + Alignment = Align.Center + } + { Label5 Label + MoveScaled(1,9,12,4) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF7F& + Text = ("Port") + Alignment = Align.Center + } + { TxtIP TextBox + MoveScaled(14,5,14,4) + Text = ("127.0.0.1") + } + { TxtPort TextBox + MoveScaled(14,10,8,4) + Text = ("32340") + } + { Button2 Button + MoveScaled(35,5,11,4) + Text = ("Start !") + } + { TxtData TextBox + MoveScaled(2,19,60,4) + Enabled = False + } + { Button3 Button + MoveScaled(63,19,29,4) + Enabled = False + Text = ("Send Data") + Default = True + } + { TxtRecClient TextArea + MoveScaled(2,24,90,17) + ReadOnly = True + } + { Label1 Label + MoveScaled(2,15,90,3) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF00& + Text = ("Write Here data to send then press \"Send Data\"") + } + } +} diff --git a/app/examples/Networking/UDPServerClient/.src/FrmServer.class b/app/examples/Networking/UDPServerClient/.src/FrmServer.class new file mode 100644 index 00000000..06a96c0e --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmServer.class @@ -0,0 +1,86 @@ +' Gambas class file + +'PRIVATE UDPServer AS UdpSocket +Public Sub Button1_Click() + + Dim MyFrm As FrmClient + MyFrm = New FrmClient + MyFrm.Visible = True + + + +End + +'////////////////////////////////////////////////// +' Server Stuff +'////////////////////////////////////////////////// +Public Sub Button2_Click() + + If UDPServer.Status <= Net.Inactive Then + UDPServer.Port = CInt(TextBox1.Text) + 'UDPServer.Path = "/tmp/gambas-udp-server" + UDPServer.Bind + If UDPServer.Status = Net.Active Then + Button2.Text = ("Stop") + TextBox1.Enabled = False + End If + Else + Close #UDPServer + Button2.Text = ("Start !") + TextBox1.Enabled = True + End If + +End + +'PUBLIC SUB Form_Open() +' +' UDPServer = NEW UdpSocket AS "UDPServer" +' +'END + +Public Sub Form_Close() + + If UDPServer.Status > 0 Then Close #UDPServer + +End + +Public Sub UDPServer_Error() + + Button2.Text = ("Start !") + TextBox1.Enabled = True + Select Case UDPServer.Status + Case Net.CannotBindSocket + Message.Error(("Unable to Bind to that port")) + Case Net.CannotCreateSocket + Message.Error(("System does not allow to create a socket")) + Case Net.CannotRead + Message.Error(("Error Sending Data")) + Case Net.CannotWrite + Message.Error(("Error Receiving Data")) + End Select + +End + +Public Sub UDPServer_Read() + + Dim sCadRet As String + Dim sCad As String + Dim MyLoop As Integer + + sCadRet = "" + Read #UDPServer, sCad, Lof(UDPserver) + TextArea1.Text = TextArea1.Text & ("Received data from : ") & UDPServer.SourceHost & ":" & UDPServer.SourcePort & Chr(13) & Chr(10) + TextArea1.Text = TextArea1.Text & ("Data --> ") & sCad & Chr(13) & Chr(10) + If Len(sCad) > 0 Then + For myloop = String.Len(sCad) To 1 Step -1 + sCadRet = sCadRet & String.Mid(sCad, myloop, 1) + Next + UDPServer.TargetHost = UDPServer.SourceHost + UDPServer.TargetPort = UDPServer.SourcePort + 'UDPServer.TargetPath = UDPServer.SourcePath + Write #UDPServer, sCadRet, Len(sCadRet) + End If + +End + + diff --git a/app/examples/Networking/UDPServerClient/.src/FrmServer.form b/app/examples/Networking/UDPServerClient/.src/FrmServer.form new file mode 100644 index 00000000..3c4a97dc --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmServer.form @@ -0,0 +1,38 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(9,10.5,37,41) + Text = ("UDP Test") + Resizable = False + { Button1 Button + MoveScaled(4,1,30,4) + Text = ("New UDP Client window") + } + { Label1 Label + MoveScaled(2,7,33,4) + Text = ("Super String Reverse Server") + Alignment = Align.Center + } + { Label2 Label + MoveScaled(1,12,10,4) + Text = ("Port :") + Alignment = Align.Center + } + { TextArea1 TextArea + MoveScaled(1,17,35,23) + Text = ("") + ReadOnly = True + } + { TextBox1 TextBox + MoveScaled(12,12,10,4) + Text = ("32340") + } + { Button2 Button + MoveScaled(23,12,13,4) + Text = ("Start !") + } + { UDPServer #UdpSocket + #X = 132 + #Y = 174 + } +} diff --git a/app/examples/Networking/UDPServerClient/udpsocket.png b/app/examples/Networking/UDPServerClient/udpsocket.png new file mode 100644 index 0000000000000000000000000000000000000000..d53f902f8dab20080e181ae74c0bb13501ba6203 GIT binary patch literal 2161 zcmV-%2#)uOP)}EH0W7bP50d0vU8YQ4D7^EVhl+ubt2(=ViCAE}7 zg|ww!h?G(i+mcvpB-NOt2Gk}}@Eea@UYr+=)qBD6F^UU>QV z{`h!*JkRreoCDWTM~@!u86O`%G&wnW?D+BHv1>W|-%9@c<`4+o5b%QvP=Hzv3J-vN z|F8ASeQ{WH?8{_f4owKQ@eK!QcjD|o{aMo%=+N|}}%@00UOe7M) zY&I**%*>>wr>CDV4C90A2GHSh4Rqaq{|~*#ejDb&jo|ZuEK{^KAsyYJ^>5#;<@0%_ zE?j6_dhflTXW*6n`}f<24av?hX1MYWbpNX`$|6yQY z;Crg7x|f%iE4f^5t|E04xwmJ6hTq21&xaAr@pev(n1CX+9v)9GI~1>k{gT0XCmFMt#Q6Ewd>zxhv+6KVQIgZ_k$ zs`+sQiBzK#?V)>-TrTnI^t3rKF>xrfwDhCL9(!!LDFC-9Ch+?p9D?{}NM1y^#ZRZp zqBD>MSpreV7DST8AoAc2WKWQ*$t3AFUeBjqKk?%C$3AFkIDqI@Sc%4h3DA%NL?50;=YIdO%#^QVZ6o$z%p%x-;NKx`U7)m^Q$N8s;o!O7QQXe(SU zLB#?|21x-)X4!OOS`O)%t7N9e=+c*I83?KIP|MvNEkRGS0J5#A4OW+7brmE5u6zvL zF?fF-w)H@p3uKwTV3|wFF$6QmXtYQq7{T4Jh1Cz2dhND=##*k>6o4tqSxUwA;hF-o zDbQ5t-T?2UVRH?Jy6A3`2nP*BQ9@B#5Cj2g7DCX6xMuL@7QfgYj|E&Fy;qUtyWAc< zp}W*}pG)&QRnso78Af3(H)E_BCyK?=`**GyMF82b8y|-(a~rbsspm3e7S@4!<8W>c z77Tp{AzQ?mHLzrPY9*I<58r;vL#pitW7YYq0fTLS^Xtc!(AEnlgc znR2NzRky6aRZEprW_s4>>5R0>mX#1<0SM?g4U2l+#V-hRBCmSIhm~8Lcc3Zhg?DEZ-Lj&gTEE>1ntN+6sx3*8kL@%L^^v&E?%Ow@)j-?Xe@!>N6qgyOWEwZO#uuK4?o`9 z+xu`h9G1)F^3wUY-~NsI+gH9}+#cI#4@ZR+J%$tCP9pIoT&hBLE{|x{z^;SWhI)-c zEOxn`&7Ny21HoXhI}{4JOw%k+PEJ03F_U>_E|Y&^@pN)><#H8yU?=h6EriY0}qTs)0Wtbt`gp#&c-QRyqqO`bdVyZfLnG#kP%WI_xC0$-2E(J29 zi{0JbL667dT)cSkFEims%lM5O`V*WnE3AA%s44&=d{1LNPruNy$6Qu%8pleyb8&HVWI n_>cGO*)t390N8->zs~*(t`eRES13jV00000NkvXXu0mjfthg;C literal 0 HcmV?d00001 diff --git a/app/examples/Networking/WebBrowser/.directory b/app/examples/Networking/WebBrowser/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Networking/WebBrowser/.icon.png b/app/examples/Networking/WebBrowser/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..891cc0287affd5cb5a64dc2bb2120470c7e9f51e GIT binary patch literal 3585 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: WebBrowser\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 23:31+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Negreta" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Canceŀla" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Sensible a caixa" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Centrat" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Copia" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Retalla" + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Gestor de baixades" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Família de la font" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Mida de la font" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Ressaltat" + +#: FOption.form:33 +msgid "Host" +msgstr "Amfitrió" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "Servidor intermediari HTTP" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Sagnat" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Cursiva" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Justificat" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Alineat a l'esquerra" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Navegador lleuger basat en el component WebKit" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Sense servidor intermediari" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "D'acord" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Llista ordenada" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Contrasenya" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Enganxa" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Si us plau, autentiqueu" + +#: FOption.form:38 +msgid "Port" +msgstr "-" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Configuració del servidor intermediari" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Refés" + +#: FBrowser.form:159 +msgid "Refresh" +msgstr "Refresca" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Alineat a la dreta" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "Servidor intermediari SOCKS5" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Iniciant la descàrrega..." + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Ratllat" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Fons del text" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Color del text" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Commuta el mode d'edició" + +#: FOption.form:28 +msgid "Type" +msgstr "Tipus" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Subratllat" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Desfés" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Desfés sagnat" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Llista desordenada" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Usuari" + diff --git a/app/examples/Networking/WebBrowser/.lang/cs.po b/app/examples/Networking/WebBrowser/.lang/cs.po new file mode 100644 index 00000000..d5ee66a8 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.lang/cs.po @@ -0,0 +1,204 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Lehký prohlížeč založený na komponentě WebKit" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Prosím autorizujte se" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Uživatel" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Heslo" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "-" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Zrušit" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:144 +msgid "Refresh" +msgstr "Obnovit" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Přepnout režim úprav" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Kopírovat" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Výjmout" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Vložit" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Dopředu" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Zpět" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Tučné" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Kurzíva" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Podtržené" + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Přeškrtnutí" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Zarovnat vlevo" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Vycentrovat" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Zarovant vpravo" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Zarovnáná do bloku" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Odrážka" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Neodrážkovat" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Číslovaný seznam" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Nečíslovaný seznam" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Barva textu" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Pozadí textu" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Rodina fontů" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Velikost fontu" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Rozlišovat velikost" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Zvýraznění" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Zahájení stahování..." + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Manažer stahování" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Konfigurace proxy" + +#: FOption.form:28 +msgid "Type" +msgstr "Typ" + +#: FOption.form:33 +msgid "Host" +msgstr "-" + +#: FOption.form:38 +msgid "Port" +msgstr "-" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "-" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Žádné proxy" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "-" diff --git a/app/examples/Networking/WebBrowser/.lang/de.po b/app/examples/Networking/WebBrowser/.lang/de.po new file mode 100644 index 00000000..e3b6b13c --- /dev/null +++ b/app/examples/Networking/WebBrowser/.lang/de.po @@ -0,0 +1,205 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Ein schlanker Browser, basierend auf der WebKit-Komponente" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Anmeldung" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Benutzer" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Passwort" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "-" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Abbrechen" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:144 +msgid "Refresh" +msgstr "Neu laden" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Editiermodus umschalten" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Kopieren" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Ausschneiden" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Einfügen" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Rückgängig" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Wiederholen" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Fett" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Kursiv" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Unterstrichen" + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Durchgestrichen" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Linksbündig" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Zentriert" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Rechtsbündig" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Blocksatz" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Einrücken" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Ausrücken" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Geordnete Liste" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Ungeordnete Liste" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Textfarbe" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Texthintergrund" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Schriftarten" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Schriftgröße" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Groß-/Kleinschreibung beachten" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Hervorheben" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Download wird gestartet..." + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Download-Manager" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Proxy-Konfiguration" + +#: FOption.form:28 +msgid "Type" +msgstr "Typ" + +#: FOption.form:33 +msgid "Host" +msgstr "-" + +#: FOption.form:38 +msgid "Port" +msgstr "-" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "HTTP-Proxy" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Kein Proxy" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "SOCKS5-Proxy" + diff --git a/app/examples/Networking/WebBrowser/.lang/es.po b/app/examples/Networking/WebBrowser/.lang/es.po new file mode 100644 index 00000000..e077297d --- /dev/null +++ b/app/examples/Networking/WebBrowser/.lang/es.po @@ -0,0 +1,62 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "" + +#: FAuth.class:59 +msgid "Please authenticate" +msgstr "" + +#: FAuth.class:81 +msgid "User" +msgstr "" + +#: FAuth.class:92 +msgid "Password" +msgstr "" + +#: FAuth.class:114 +msgid "OK" +msgstr "" + +#: FAuth.class:120 +msgid "Cancel" +msgstr "" + +#: FBrowser.class:473 +msgid "Gambas WebKit" +msgstr "" + +#: FBrowser.class:587 +msgid "Case sensitive" +msgstr "" + +#: FBrowser.class:594 +msgid "Highlight" +msgstr "" + +#: FDownload.class:72 +msgid "Starting download..." +msgstr "" + +#: FDownloadList.class:33 +msgid "Download manager" +msgstr "" + +#~ msgid "Browsing the web with Gambas!" +#~ msgstr "¡Buscando en la red con Gambas!" + +#~ msgid "Print selection" +#~ msgstr "Imprimir selección" diff --git a/app/examples/Networking/WebBrowser/.lang/nl.po b/app/examples/Networking/WebBrowser/.lang/nl.po new file mode 100644 index 00000000..b78bd485 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.lang/nl.po @@ -0,0 +1,204 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-21 23:52+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Lichte browser gebaseerd op het Webkit component" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Gelieve te authenticeren" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Gebruiker" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Wachtwoord" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "-" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Annuleer" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:144 +msgid "Refresh" +msgstr "Ververs" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Wissel bewerken modus" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Kopiëer" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Knippen" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Plakken" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Ongedaan maken" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Opnieuw" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Vet" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Schuin" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Onderlijn" + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Doorstreept" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Links uitgelijnd" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Gecentreerd" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Rechts uitgelijnd" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Aangepast" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Inspringen" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Ongedaan maken inspringen" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Geordende lijst" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Ongeordende lijst" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Tekst kleur" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Tekst achtergrond" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Lettertype familie" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Lettertype grootte" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Case gevoelig" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Uitlichten" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Start download..." + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Donload beheerder" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Proxy configuratie" + +#: FOption.form:28 +msgid "Type" +msgstr "Type" + +#: FOption.form:33 +msgid "Host" +msgstr "Gast" + +#: FOption.form:38 +msgid "Port" +msgstr "Poort" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Geen proxy" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "-" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "-" + diff --git a/app/examples/Networking/WebBrowser/.lang/ru.po b/app/examples/Networking/WebBrowser/.lang/ru.po new file mode 100644 index 00000000..d07bf8c6 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.lang/ru.po @@ -0,0 +1,258 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Networking/WebBrowser/.project:27 +msgid "Light browser based on WebKit component" +msgstr "Лёгкий браузер на основе компонента WebKit" + +#: app/examples/Networking/WebBrowser/.project:28 +msgid "Light browser based on the QT4 or QT5 Webkit component" +msgstr "Лёгкий браузер на основе компонента QT4 или QT5 Webkit" + +#: app/examples/Networking/WebBrowser/.src/FAuth.form:5 +msgid "Please authenticate" +msgstr "Пожалуйста, аутентифицируйтесь" + +#: app/examples/Networking/WebBrowser/.src/FAuth.form:23 app/examples/Networking/WebBrowser/.src/FOption.form:23 +msgid "User" +msgstr "Пользователь" + +#: app/examples/Networking/WebBrowser/.src/FAuth.form:31 app/examples/Networking/WebBrowser/.src/FOption.form:27 +msgid "Password" +msgstr "Пароль" + +#: app/examples/Networking/WebBrowser/.src/FAuth.form:48 app/examples/Networking/WebBrowser/.src/FOption.form:38 +msgid "OK" +msgstr "ОК" + +#: app/examples/Networking/WebBrowser/.src/FAuth.form:53 app/examples/Networking/WebBrowser/.src/FOption.form:43 +msgid "Cancel" +msgstr "Отмена" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.class:72 +msgid "Loading..." +msgstr "Идёт загрузка..." + +#: app/examples/Networking/WebBrowser/.src/FBrowser.class:95 +msgid "Unable to load:" +msgstr "Невозможно загрузить:" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.class:96 +msgid "

    Unable to find the following URL:

    " +msgstr "

    Невозможно найти следующий URL:

    " + +#: app/examples/Networking/WebBrowser/.src/FBrowser.class:556 +msgid "Select a color" +msgstr "Выбрать цвет" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.class:577 +msgid "Select an image" +msgstr "Выбрать изображение" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:5 +msgid "Gambas WebKit" +msgstr "Веб-комплект Gambas WebKit" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:16 +msgid "-2" +msgstr "-2" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:20 +msgid "-1" +msgstr "-1" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:24 +msgid "0" +msgstr "0" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:28 app/examples/Networking/WebBrowser/.src/FBrowser.form:281 +msgid "+1" +msgstr "+1" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:32 +msgid "+2" +msgstr "+2" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:36 +msgid "+3" +msgstr "+3" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:40 +msgid "+4" +msgstr "+4" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:75 +msgid "Refresh" +msgstr "Освежить" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:89 +msgid "Toggle edit mode" +msgstr "Переключить в режим редактирования" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:129 +msgid "Copy" +msgstr "Копировать" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:136 +msgid "Cut" +msgstr "Вырезать" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:143 +msgid "Paste" +msgstr "Вставить" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:150 +msgid "Undo" +msgstr "Откатить" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:157 +msgid "Redo" +msgstr "Вернуть" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:167 app/examples/Networking/WebBrowser/.src/FEditable.form:11 +msgid "Bold" +msgstr "Полужирный" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:174 +msgid "Italic" +msgstr "Курсив" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:181 +msgid "Underline" +msgstr "Подчёркнутый" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:188 +msgid "Strikethrough" +msgstr "Зачёркнутый" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:198 +msgid "Left aligned" +msgstr "По левому краю" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:205 +msgid "Centered" +msgstr "По центру" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:212 +msgid "Right aligned" +msgstr "По правому краю" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:219 +msgid "Justified" +msgstr "По ширине" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:229 +msgid "Indent" +msgstr "Отступ" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:236 +msgid "Unindent" +msgstr "Отменить отступ" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:246 +msgid "Ordered list" +msgstr "Упорядоченный список" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:253 +msgid "Unordered list" +msgstr "Неупорядоченный список" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:261 +msgid "Text color" +msgstr "Цвет текста" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:266 +msgid "Text background" +msgstr "Фон текста" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:271 +msgid "Font family" +msgstr "Гарнитура шрифта" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:280 +msgid "Font size" +msgstr "Размер шрифта" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:322 +msgid "Case sensitive" +msgstr "С учётом регистра" + +#: app/examples/Networking/WebBrowser/.src/FBrowser.form:328 +msgid "Highlight" +msgstr "Подсветка" + +#: app/examples/Networking/WebBrowser/.src/FDownload.class:24 +msgid "Cancelled" +msgstr "Отменено" + +#: app/examples/Networking/WebBrowser/.src/FDownload.class:26 +msgid "Finished" +msgstr "Готово" + +#: app/examples/Networking/WebBrowser/.src/FDownload.class:28 +msgid "Error:" +msgstr "Ошибка:" + +#: app/examples/Networking/WebBrowser/.src/FDownload.class:30 +msgid "Downloading..." +msgstr "Скачивание..." + +#: app/examples/Networking/WebBrowser/.src/FDownload.form:16 +msgid "Starting download" +msgstr "Начинается загрузка" + +#: app/examples/Networking/WebBrowser/.src/FDownloadList.form:5 +msgid "Download manager" +msgstr "Менеджер загрузок" + +#: app/examples/Networking/WebBrowser/.src/FOption.form:5 +msgid "Proxy configuration" +msgstr "Конфигурация прокси" + +#: app/examples/Networking/WebBrowser/.src/FOption.form:11 +msgid "Type" +msgstr "Тип" + +#: app/examples/Networking/WebBrowser/.src/FOption.form:15 +msgid "Host" +msgstr "Хост" + +#: app/examples/Networking/WebBrowser/.src/FOption.form:19 +msgid "Port" +msgstr "Порт" + +#: app/examples/Networking/WebBrowser/.src/FOption.form:54 +msgid "No proxy" +msgstr "Без прокси" + +#: app/examples/Networking/WebBrowser/.src/FOption.form:54 +msgid "HTTP proxy" +msgstr "HTTP-прокси" + +#: app/examples/Networking/WebBrowser/.src/FOption.form:54 +msgid "SOCKS5 proxy" +msgstr "SOCKS5-прокси" + diff --git a/app/examples/Networking/WebBrowser/.project b/app/examples/Networking/WebBrowser/.project new file mode 100644 index 00000000..e1b37c20 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.project @@ -0,0 +1,26 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.7.90 +Title=Light browser based on WebKit component +Startup=FBrowser +Icon=icon.png +Version=1.0.3 +Component=gb.image +Component=gb.gui.qt +Component=gb.form +Component=gb.desktop.x11 +Component=gb.desktop +Component=gb.form.dialog +Component=gb.gui.qt.webkit +Description="Light browser based on the QT4 or QT5 Webkit component" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Systems=mandriva +Menus=mandriva:"More Applications/Development/Other" +Groups=mandriva:"Development/Other" +Tags=Network,Web,WebBrowser,Example diff --git a/app/examples/Networking/WebBrowser/.src/FAuth.class b/app/examples/Networking/WebBrowser/.src/FAuth.class new file mode 100644 index 00000000..01e7ed69 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FAuth.class @@ -0,0 +1,38 @@ +' Gambas class file + +Static Public User As String +Static Public Password As String + +'Private $sUrl As String +'Private $sRealm As String + +Public Sub Run(sUrl As String, sRealm As String) As Boolean + + lblRealm.Text = sRealm + Me.Title = sUrl + '$sUrl = sUrl + '$sRealm = sRealm + Return Not Me.ShowModal() + +End + +Public Sub btnOK_Click() + + User = txtUser.Text + Password = txtPassword.Text + Me.Close(True) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + + +Public Sub Form_Open() + + txtUser.SetFocus + +End diff --git a/app/examples/Networking/WebBrowser/.src/FAuth.form b/app/examples/Networking/WebBrowser/.src/FAuth.form new file mode 100644 index 00000000..6b0c115b --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FAuth.form @@ -0,0 +1,57 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,54,26) + Text = ("Please authenticate") + Icon = Picture["icon:/small/unlock"] + Resizable = False + Arrangement = Arrange.Vertical + AutoResize = True + Spacing = True + Margin = True + { HPanel1 HPanel + MoveScaled(1,1,52,18) + AutoResize = True + Spacing = True + { lblRealm TextLabel + MoveScaled(2,1,48,4) + Expand = True + AutoResize = True + } + { Label1 Label + MoveScaled(2,6,17,3) + Text = ("User") + } + { txtUser TextBox + MoveScaled(19,6,32,4) + Expand = True + } + { Label2 Label + MoveScaled(2,11,17,3) + Text = ("Password") + } + { txtPassword TextBox + MoveScaled(19,11,32,4) + Expand = True + Password = True + } + } + { HBox1 HBox + MoveScaled(1,20,52,4) + Spacing = True + { Panel1 Panel + MoveScaled(4,0,4,4) + Expand = True + } + { btnOK Button + MoveScaled(17,0,16,4) + Text = ("OK") + Default = True + } + { btnCancel Button + MoveScaled(34,0,16,4) + Text = ("Cancel") + Cancel = True + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FBrowser.class b/app/examples/Networking/WebBrowser/.src/FBrowser.class new file mode 100644 index 00000000..e81b90f4 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FBrowser.class @@ -0,0 +1,584 @@ +' Gambas class file + +'Static Private $aZoom As Float[] + +'Private $sStatus As String +'Private $iZoom As Integer +'Private $iHidden As Integer +Private $sLastLink As String + +' Static Public Sub _init() +' +' Dim iInd As Integer +' +' $aZoom = New Float[17] +' +' For iInd = 0 To $aZoom.Max +' $aZoom[iInd] = 2 ^ (- (iInd - $aZoom.Max / 2) / 4) +' Next +' +' End + +Public Sub Form_Open() + + WebSettings.IconDatabase.Path = File.Dir(File.Dir(Temp$())) + WebSettings.Fonts.FixedFont = "Monospace" + WebSettings[WebSettings.PluginsEnabled] = True + WebSettings[WebSettings.JavascriptEnabled] = True + WebSettings[WebSettings.JavaEnabled] = True + WebSettings.Fonts.DefaultFontSize = 12 + WebSettings.Fonts.DefaultFixedFontSize = 12 + + CreateView() + btnZoomNormal_Click + txtURL.SetFocus + tabBrowser_Click + + txtURL.Text = "http://gambas.sourceforge.net" + txtURL_Activate + +End + +Private Sub GetView() As WebView + + Try Return tabBrowser[tabBrowser.Index].Children[0] + +End + +Private Sub IsLastCurrentView() As Boolean + + Dim hView As WebView = GetView() + Return hView = Last + +End + +Public Sub WebView_Link(Url As String) + + If Not IsLastCurrentView() Then Return + $sLastLink = Url + lblStatus.Text = Url + +End + +Public Sub WebView_Status() + + If Not IsLastCurrentView() Then Return + lblStatus.Text = GetView().Status + +End + +Public Sub WebView_Progress() + + GetView().Status = ("Loading...") + If Not IsLastCurrentView() Then Return + + lblStatus.Text = GetView().Status + pgbLoad.Value = GetView().Progress + panLoad.Show + +End + +Public Sub WebView_Error() + + Dim hView As WebView = GetView() + Dim sUrl As String + + sUrl = $sLastLink + If Not sUrl Then sUrl = txtURL.Text + + ' If Not (sUrl Begins "www.") Then + ' txtURL.Text = "www." & sUrl + ' btnGo.Value = True + ' Return + ' Endif + + hView.Status = ("Unable to load:") & " " & sUrl + hView.HTML = ("

    Unable to find the following URL:

    ") & sUrl + If Not IsLastCurrentView() Then Return + + lblStatus.Text = GetView().Status + pgbLoad.Hide + +End + +Public Sub WebView_Load() + + 'Dim iInd As Integer + Dim hView As WebView = GetView() + Dim hIcon As Picture + + hView.Status = "" + hIcon = hView.Icon 'WebSettings.IconDatabase[hView.Url] + tabBrowser[hView.Tag].Picture = hIcon + + If Not IsLastCurrentView() Then Return + + lblStatus.Text = "" + txtURL.Text = hView.Url + pgbLoad.Hide + + UpdateIcon + + ' Debug GetView().Frame + ' For iInd = 0 To GetView().Frame.Children.Count - 1 + ' Debug "["; iInd; "] "; GetView().Frame.Children[iInd] + ' Next + +End + +Public Sub btnGo_Click() + + Dim sText As String = txtURL.Text + + If InStr(sText, "://") = 0 Then sText = "http://" & sText + + $sLastLink = sText + GetView().Url = sText + +End + +Public Sub txtURL_Activate() + + btnGo.Value = True + +End + +Public Sub btnBack_Click() + + GetView().Back + +End + +Public Sub btnForward_Click() + + GetView().Forward + +End + +Public Sub btnStop_Click() + + GetView().Stop + +End + +Public Sub btnReload_Click() + + Dim hView As WebView = GetView() + + hView.Reload + +End + +Public Sub btnZoomIn_Click() + + GetView().Zoom = Round(GetView().Zoom * 1.25, -2) + +End + +Public Sub btnZoomOut_Click() + + GetView().Zoom = Round(GetView().Zoom / 1.25, -2) + +End + +Public Sub btnZoomNormal_Click() + + GetView().Zoom = 1 + +End + +Public Sub WebView_Title() + + Dim hView As WebView = Last + tabBrowser[hView.Tag].Text = hView.Title + If Not IsLastCurrentView() Then Return + Me.Title = hView.Title & " - Gambas WebKit" + +End + +Public Sub WebView_Icon() + + Dim hView As WebView = Last + 'hView.Icon.Save("~/icon.png") + tabBrowser[hView.Tag].Picture = hView.Icon + UpdateIcon + +End + +Public Sub btnClear_Click() + + txtURL.Clear + txtURL.SetFocus + +End + +Public Sub WebView_NewWindow((Modal) As Boolean) + + CreateView() + + Last.NewView = GetView() + +End + +Private Sub CreateView() + + Dim iLast As Integer = tabBrowser.Count - 1 + Dim hView As WebView + + Object.Lock(tabBrowser) + Inc tabBrowser.Count + Object.Lock(tabBrowser) + tabBrowser[iLast + 1].Picture = tabBrowser[iLast].Picture + tabBrowser[iLast + 1].Text = tabBrowser[iLast].Text + tabBrowser[iLast].Text = "" + tabBrowser[iLast].Picture = Null + tabBrowser[iLast].Closable = True + Object.Lock(tabBrowser) + tabBrowser.Index = iLast + hView = New WebView(tabBrowser) As "WebView" + hView.Tag = tabBrowser.Index + hView.Editable = btnEdit.Value + hView.Resize(1, 1) + 'hView.Show + 'Print WebSettings.Fonts.FixedFont + Object.Unlock(tabBrowser) + Object.Unlock(tabBrowser) + Object.Unlock(tabBrowser) + tabBrowser_Click + txtURL.SetFocus + +End + + +Public Sub tabBrowser_Click() + + Dim iLast As Integer = tabBrowser.Count - 1 + Dim hView As WebView + + If tabBrowser.Index = iLast Then + CreateView() + Else + hView = GetView() + If hView.Title Then + Me.Title = hView.Title & " - Gambas WebKit" + Else + Me.Title = "Gambas WebKit" + Endif + tabBrowser.Text = hView.Title + UpdateIcon + tabBrowser.Picture = hView.Icon + txtURL.Text = hView.Url + lblStatus.Text = hView.Status + pgbLoad.Value = hView.Progress + If hView.Progress > 0 And If hView.Progress < 1 Then + panLoad.Show + Else + panLoad.Hide + Endif + Endif + +End + +Public Sub btnDownloadList_Click() + + FDownloadList.Show + +End + +Public Sub WebView_Auth() + + Dim hView As WebView = Last + + If Not FAuth.Run(hView.Auth.Url, hView.Auth.Realm) Then + + hView.Auth.User = FAuth.User + hView.Auth.Password = FAuth.Password + 'Debug hView.Auth.Url;; hView.Auth.User;; hView.Auth.Password + + Endif + +End + +Public Sub WebView_Click(Frame As WebFrame) + + Dim sName As String = Frame.Name + If sName Then sName &= ": " + Debug sName; Frame.Url + +End + +Public Sub WebView_NewFrame(Frame As WebFrame) + + Debug Frame.Name + +End + +Public Sub WebView_Download(Download As WebDownload) + + Dialog.Path = System.User.Home &/ File.Name(Download.Url) + If Not Dialog.SaveFile() Then + Download.Path = Dialog.Path + FDownloadList.AddDownload(Download) + FDownloadList.Show + Endif + +End + +Public Sub WebView_MouseDown() + + Dim hView As WebView = Last + Dim hTest As WebHitTest = hView.HitTest(Mouse.X, Mouse.Y) + Dim sMsg As String + + If hTest.Document Then sMsg &= "DOCUMENT " + If hTest.Link Then sMsg &= "LINK " + If hTest.Image Then sMsg &= "IMAGE " + If hTest.Selected Then sMsg &= "SELECTED " + If hTest.Editable Then sMsg &= "EDITABLE " + Debug sMsg; hTest.Url + +End + +Public Sub tabBrowser_Close(Index As Integer) + + Dim hView As WebView + + Try hView = tabBrowser[Index].Children[0] + If Not hView Then Return + + hView.Delete + + Object.Lock(tabBrowser) + tabBrowser[Index].Delete + Object.UnLock(tabBrowser) + If Index = tabBrowser.Index Then + If tabBrowser.Index = (tabBrowser.Count - 1) And If tabBrowser.Index > 0 Then + tabBrowser.Index = tabBrowser.Index - 1 + Else + tabBrowser_Click + Endif + Endif + +End + +Public Sub btnFind_Click() + + Dim hView As WebView = GetView() + + panFind.Visible = btnFind.Value + If btnFind.Value Then + DoFind + txtFind.SetFocus + Else + hView.FindText("") + Endif + +End + +Private Sub DoFind(Optional bBackward As Boolean) + + Dim hView As WebView = GetView() + Dim sText As String + + sText = Trim(txtFind.Text) + + If sText And If hView.FindText(sText, bBackward, chkCaseSensitive.Value, True) Then ', chkHighlight.Value) Then + panFind.Background = &HFFDFDF + Else + panFind.Background = Color.Default + If Not sText Then hView.FindText("") + Endif + +End + + +Public Sub txtFind_Change() + + DoFind + +End + +Public Sub Form_KeyPress() + + If Key.Control And If Key.Code = Key["F"] Then + btnFind.Value = True + Else If Key.Code = Key.Escape Then + btnFind.Value = False + Else If Key.Code = Key.F3 Then + DoFind(Key.Shift) + Endif + +End + +Public Sub chkCaseSensitive_Click() + + DoFind + +End + +Public Sub chkHighlight_Click() + + Dim hView As WebView + + If Not chkHighlight.Value Then + hView = GetView() + hView.FindText("",,,, True) + Else + DoFind + Endif + +End + +Public Sub btnNext_Click() + + DoFind + +End + +Public Sub btnPrevious_Click() + + DoFind(True) + +End + +Public Sub btnClearFind_Click() + + txtFind.Text = "" + +End + +Public Sub txtFind_Activate() + + DoFind + +End + +Private Sub UpdateIcon() + + Dim hView As WebView = GetView() + Dim hIcon As Picture + + hIcon = hView.Icon 'WebSettings.IconDatabase[hView.Url] + + If hIcon Then + Me.Icon = hIcon + Else + Me.Icon = Picture["icon:/16/internet"] + Endif + +End + +Public Sub btnConfig_Click() + + With WebSettings.Proxy + + FOption.Type = .Type + FOption.Host = .Host + FOption.Port = .Port + FOption.User = .User + FOption.Password = .Password + + If FOption.Run() Then Return + + .Type = FOption.Type + .Host = FOption.Host + .Port = FOption.Port + .User = FOption.User + .Password = FOption.Password + + End With + +End + +Public Sub btnEdit_Click() + + Dim hWebView As WebView + + For Each hWebView In tabBrowser.Children + hWebView.Editable = btnEdit.Value + Next + + WebSettings[WebSettings.JavascriptCanAccessClipboard] = btnEdit.Value + panEdit.Visible = btnEdit.Value + +End + +Public Sub btnAction_Click() + + Dim hWebView As WebView = GetView() + + Debug "Action " & Last.Tag & ": "; hWebView.Eval(Subst("document.execCommand('&1', false, false)", Last.Tag)) + +End + +Public Sub btnColor_Click() + + Dim hWebView As WebView = GetView() + + If Dialog.SelectColor() Then Return + hWebView.Eval(Subst("document.execCommand('forecolor', false, '&1')", "#" & Hex$(Dialog.Color, 6))) + +End + +Public Sub mnuFont_Show() + + Dim sFont As String + Dim hMenu As Menu + + If mnuFont.Children.Count > 1 Then Return + + mnuFont.Children.Clear + + For Each sFont In Fonts + + hMenu = New Menu(mnuFont) As "mnuSelectFont" + hMenu.Text = sFont + + Next + +End + + +Public Sub mnuSelectFont_Click() + + Dim hWebView As WebView = GetView() + Dim sFont As String = Last.Text + + hWebView.Eval(Subst("document.execCommand('fontname', false, '&1')", sFont)) + +End + + +Public Sub btnBackground_Click() + + Dim hWebView As WebView = GetView() + + Dialog.Title = ("Select a color") + If Dialog.SelectColor() Then Return + hWebView.Eval(Subst("document.execCommand('backcolor', false, '&1')", "#" & Hex$(Dialog.Color, 6))) + +End + +Public Sub mnuSelectSize_Click() + + Dim hWebView As WebView = GetView() + Dim sSize As String = Last.Text + + hWebView.Eval(Subst("document.execCommand('fontsize', false, '&1')", sSize)) + +End + + + +Public Sub btnInsertImage_Click() + + Dim hWebView As WebView = GetView() + + Dialog.Title = ("Select an image") + Dialog.Filter = ["*.jpg;*.jpeg;*.png;*.gif;*.xpm;*.bmp", "Image files"] + If Dialog.OpenFile() Then Return + + Print Dialog.Path + hWebView.Eval(Subst("document.execCommand('insertImage', false, '&1')", "file://" & Replace(Dialog.Path, "'", "\\'"))) + +End diff --git a/app/examples/Networking/WebBrowser/.src/FBrowser.form b/app/examples/Networking/WebBrowser/.src/FBrowser.form new file mode 100644 index 00000000..4fd51e3b --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FBrowser.form @@ -0,0 +1,352 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,169,98) + Text = ("Gambas WebKit") + Icon = Picture["icon:/small/gambas"] + Arrangement = Arrange.Vertical + { mnuFont Menu + { mnuSelectFont Menu mnuSelectFont + Name = "mnuSelectFont" + } + } + { mnuSize Menu + { mnuSizeM2 Menu mnuSelectSize + Name = "mnuSizeM2" + Text = ("-2") + } + { mnuSizeM1 Menu mnuSelectSize + Name = "mnuSizeM1" + Text = ("-1") + } + { mnuSize0 Menu mnuSelectSize + Name = "mnuSize0" + Text = ("0") + } + { mnuSizeP1 Menu mnuSelectSize + Name = "mnuSizeP1" + Text = ("+1") + } + { mnuSizeP2 Menu mnuSelectSize + Name = "mnuSizeP2" + Text = ("+2") + } + { mnuSizeP3 Menu mnuSelectSize + Name = "mnuSizeP3" + Text = ("+3") + } + { mnuSizeP4 Menu mnuSelectSize + Name = "mnuSizeP4" + Text = ("+4") + } + } + { HBox2 HBox + MoveScaled(1,1,99,4) + { btnBack ToolButton + MoveScaled(0,0,4,4) + Picture = Picture["icon:/small/left"] + } + { btnForward ToolButton + MoveScaled(4,0,4,4) + Picture = Picture["icon:/small/right"] + } + { Separator9 Separator + MoveScaled(9,0,0,4) + } + { Panel2 HBox + MoveScaled(12,0,44,4) + Background = Color.TextBackground + Expand = True + { txtURL TextBox + MoveScaled(0,0,28,4) + Expand = True + Border = False + } + { btnClear ToolButton + MoveScaled(28,0,4,4) + Picture = Picture["icon:/small/clear"] + } + { btnStop ToolButton + MoveScaled(33,0,4,4) + Picture = Picture["icon:/small/delete"] + } + { btnReload ToolButton + MoveScaled(38,0,4,4) + ToolTip = ("Refresh") + Picture = Picture["icon:/small/refresh"] + } + } + { btnGo ToolButton + MoveScaled(57,0,4,4) + Visible = False + Picture = Picture["icon:/small/apply"] + } + { Separator1 Separator + MoveScaled(62,0,0,4) + } + { btnEdit ToolButton + MoveScaled(64,0,4,4) + ToolTip = ("Toggle edit mode") + Picture = Picture["icon:/small/edit"] + Toggle = True + } + { btnZoomIn ToolButton + MoveScaled(69,0,4,4) + Picture = Picture["icon:/small/zoom-in"] + } + { btnZoomOut ToolButton + MoveScaled(73,0,4,4) + Picture = Picture["icon:/small/zoom-out"] + } + { btnZoomNormal ToolButton + MoveScaled(77,0,4,4) + Picture = Picture["icon:/small/zoom-normal"] + } + { Separator2 Separator + MoveScaled(81,0,0,4) + } + { btnFind ToolButton + MoveScaled(83,0,4,4) + Picture = Picture["icon:/small/find"] + Toggle = True + } + { btnDownloadList ToolButton + MoveScaled(87,0,4,4) + Picture = Picture["icon:/small/archive"] + } + { btnConfig ToolButton + MoveScaled(91,0,4,4) + Picture = Picture["icon:/small/options"] + } + } + { panEdit HBox + MoveScaled(1,6,97,4) + Visible = False + { btnCopy ToolButton btnAction + Name = "btnCopy" + MoveScaled(0,0,4,4) + Tag = "Copy" + ToolTip = ("Copy") + Picture = Picture["icon:/small/copy"] + } + { btnCut ToolButton btnAction + Name = "btnCut" + MoveScaled(4,0,4,4) + Tag = "Cut" + ToolTip = ("Cut") + Picture = Picture["icon:/small/cut"] + } + { btnPaste ToolButton btnAction + Name = "btnPaste" + MoveScaled(8,0,4,4) + Tag = "Paste" + ToolTip = ("Paste") + Picture = Picture["icon:/small/paste"] + } + { btnUndo ToolButton btnAction + Name = "btnUndo" + MoveScaled(12,0,4,4) + Tag = "Undo" + ToolTip = ("Undo") + Picture = Picture["icon:/small/undo"] + } + { btnRedo ToolButton btnAction + Name = "btnRedo" + MoveScaled(16,0,4,4) + Tag = "Redo" + ToolTip = ("Redo") + Picture = Picture["icon:/small/redo"] + } + { Separator7 Separator + MoveScaled(20,0,1,4) + } + { btnBold ToolButton btnAction + Name = "btnBold" + MoveScaled(21,0,4,4) + Tag = "bold" + ToolTip = ("Bold") + Picture = Picture["icon:/small/text-bold"] + } + { btnItalic ToolButton btnAction + Name = "btnItalic" + MoveScaled(25,0,4,4) + Tag = "italic" + ToolTip = ("Italic") + Picture = Picture["icon:/small/text-italic"] + } + { btnUnderline ToolButton btnAction + Name = "btnUnderline" + MoveScaled(29,0,4,4) + Tag = "underline" + ToolTip = ("Underline") + Picture = Picture["icon:/small/text-underline"] + } + { btnStrike ToolButton btnAction + Name = "btnStrike" + MoveScaled(33,0,4,4) + Tag = "strikethrough" + ToolTip = ("Strikethrough") + Picture = Picture["icon:/small/text-strike"] + } + { Separator3 Separator + MoveScaled(37,0,1,4) + } + { btnLeft ToolButton btnAction + Name = "btnLeft" + MoveScaled(39,0,4,4) + Tag = "justifyleft" + ToolTip = ("Left aligned") + Picture = Picture["icon:/small/text-left"] + } + { btnCenter ToolButton btnAction + Name = "btnCenter" + MoveScaled(43,0,4,4) + Tag = "justifycenter" + ToolTip = ("Centered") + Picture = Picture["icon:/small/text-center"] + } + { btnRight ToolButton btnAction + Name = "btnRight" + MoveScaled(47,0,4,4) + Tag = "justifyright" + ToolTip = ("Right aligned") + Picture = Picture["icon:/small/text-right"] + } + { btnFill ToolButton btnAction + Name = "btnFill" + MoveScaled(51,0,4,4) + Tag = "justifyfull" + ToolTip = ("Justified") + Picture = Picture["icon:/small/text-fill"] + } + { Separator5 Separator + MoveScaled(55,0,1,4) + } + { btnIndent ToolButton btnAction + Name = "btnIndent" + MoveScaled(56,0,4,4) + Tag = "indent" + ToolTip = ("Indent") + Picture = Picture["icon:/small/indent"] + } + { btnUnindent ToolButton btnAction + Name = "btnUnindent" + MoveScaled(60,0,4,4) + Tag = "outdent" + ToolTip = ("Unindent") + Picture = Picture["icon:/small/unindent"] + } + { Separator6 Separator + MoveScaled(64,0,1,4) + } + { btnOrdered ToolButton btnAction + Name = "btnOrdered" + MoveScaled(65,0,4,4) + Tag = "insertOrderedList" + ToolTip = ("Ordered list") + Picture = Picture["list-ordered.png"] + } + { btnUnordered ToolButton btnAction + Name = "btnUnordered" + MoveScaled(69,0,4,4) + Tag = "insertUnorderedList" + ToolTip = ("Unordered list") + Picture = Picture["list-unordered.png"] + } + { Separator4 Separator + MoveScaled(73,0,1,4) + } + { btnColor ToolButton + MoveScaled(74,0,4,4) + ToolTip = ("Text color") + Picture = Picture["icon:/small/color"] + } + { btnBackground ToolButton + MoveScaled(78,0,4,4) + ToolTip = ("Text background") + Picture = Picture["icon:/small/fill"] + } + { btnFont MenuButton + MoveScaled(82,0,6,4) + ToolTip = ("Font family") + Picture = Picture["icon:/small/font"] + Border = False + Menu = "mnuFont" + MenuOnly = True + } + { btnFont2 MenuButton + MoveScaled(88,0,6,4) + Font = Font["Bold,-1"] + ToolTip = ("Font size") + Text = ("+1") + Border = False + Menu = "mnuSize" + MenuOnly = True + } + } + { Separator8 Separator + MoveScaled(8,12,17,0) + } + { tabBrowser TabPanel + MoveScaled(1,14,76,40) + Expand = True + Arrangement = Arrange.Fill + Border = False + Index = 0 + Text = ("") + Picture = Picture["icon:/small/add"] + Index = 0 + } + { panFind HBox + MoveScaled(1,54,101,4) + Visible = False + { btnClearFind ToolButton + MoveScaled(4,0,4,4) + Picture = Picture["icon:/small/clear"] + } + { txtFind TextBox + MoveScaled(8,0,32,4) + Expand = True + } + { btnNext ToolButton + MoveScaled(41,0,4,4) + Picture = Picture["icon:/small/down"] + } + { btnPrevious ToolButton + MoveScaled(45,0,4,4) + Picture = Picture["icon:/small/up"] + } + { chkCaseSensitive CheckBox + MoveScaled(54,0,17,4) + AutoResize = True + Text = ("Case sensitive") + } + { chkHighlight CheckBox + MoveScaled(70,0,20,4) + Visible = False + AutoResize = True + Text = ("Highlight") + } + { Panel1 Panel + MoveScaled(91,1,1,2) + } + } + { HBox1 HBox + MoveScaled(1,61,66,2) + { lblStatus Label + MoveScaled(5,0,21,2) + Font = Font["-2"] + Expand = True + } + { panLoad Panel + MoveScaled(46,0,19,2) + Visible = False + Arrangement = Arrange.Fill + Padding = 4 + { pgbLoad ProgressBar + MoveScaled(1,0,17,2) + Label = False + } + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FDownload.class b/app/examples/Networking/WebBrowser/.src/FDownload.class new file mode 100644 index 00000000..ce500c5e --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownload.class @@ -0,0 +1,50 @@ +' Gambas class file + +Private $hDownload As WebDownload + +Public Sub Init(hDownload As WebDownload) + + $hDownload = hDownload + Redraw + +End + + +Public Sub Redraw() + + Dim sTitle As String + Dim sStatus As String + + If Not $hDownload Then Return + + With $hDownload + + sTitle = .Url + If .Status = .Cancelled Then + sStatus = ("Cancelled") + Else If .Status = .Finished Then + sStatus = ("Finished") + Else If .Status = .Error Then + sStatus = ("Error:") & " " & .ErrorText + Else + sStatus = ("Downloading...") + Endif + lblTitle.Text = sTitle + lblStatus.Text = sStatus + + pgbDownload.Value = .Progress + + End With + +End + +Public Sub btnAbort_Click() + + If $hDownload.Status = WebDownload.Downloading Then + $hDownload.Cancel + Else + $hDownload.Delete + Me.Delete + Endif + +End diff --git a/app/examples/Networking/WebBrowser/.src/FDownload.form b/app/examples/Networking/WebBrowser/.src/FDownload.form new file mode 100644 index 00000000..b14e5303 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownload.form @@ -0,0 +1,34 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,69,13) + Arrangement = Arrange.Fill + Margin = True + { Panel1 Panel + MoveScaled(1,1,67,11) + Background = Color.Background + Arrangement = Arrange.Vertical + Margin = True + Border = Border.Plain + { lblTitle Label + MoveScaled(1,1,65,3) + Expand = True + Text = ("Starting download") & "..." + } + { lblStatus Label + MoveScaled(1,4,11,2) + Font = Font["Italic,-2"] + } + { HBox2 HBox + MoveScaled(1,6,65,4) + { pgbDownload ProgressBar + MoveScaled(0,0,56,4) + Expand = True + } + { btnAbort ToolButton + MoveScaled(61,0,4,4) + Picture = Picture["icon:/small/close"] + } + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FDownloadList.class b/app/examples/Networking/WebBrowser/.src/FDownloadList.class new file mode 100644 index 00000000..eed15dfe --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownloadList.class @@ -0,0 +1,20 @@ +' Gambas class file + +Public Sub AddDownload(hDownload As WebDownload) + + Dim hForm As FDownload + + hForm = New FDownload(svwDownload) + hForm.Init(hDownload) + +End + +Public Sub timRefresh_Timer() + + Dim hForm As FDownload + + For Each hForm In svwDownload.Children + hForm.Redraw + Next + +End diff --git a/app/examples/Networking/WebBrowser/.src/FDownloadList.form b/app/examples/Networking/WebBrowser/.src/FDownloadList.form new file mode 100644 index 00000000..baaaf84c --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownloadList.form @@ -0,0 +1,20 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,39) + Text = ("Download manager") + Icon = Picture["icon:/small/archive"] + Persistent = True + Arrangement = Arrange.Fill + { svwDownload ScrollView + MoveScaled(1,1,66,36) + Background = Color.SelectedForeground + Arrangement = Arrange.Vertical + Border = False + ScrollBar = Scroll.Vertical + { timRefresh #Timer + #MoveScaled(52,15) + Enabled = True + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FEditable.class b/app/examples/Networking/WebBrowser/.src/FEditable.class new file mode 100644 index 00000000..98308606 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FEditable.class @@ -0,0 +1,15 @@ +' Gambas class file + + +Public Sub Form_Open() + + WebView1.Url = "http://gambasdoc.org/" + +End + +Public Sub Button1_Click() + + Print WebView1.Frame.EvalJavaScript("1+2") + Print WebView1.Frame.EvalJavaScript("document.execCommand('bold', false, false)") + +End diff --git a/app/examples/Networking/WebBrowser/.src/FEditable.form b/app/examples/Networking/WebBrowser/.src/FEditable.form new file mode 100644 index 00000000..7d7ec8f7 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FEditable.form @@ -0,0 +1,19 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,64) + Arrangement = Arrange.Vertical + Margin = True + { HBox1 HBox + MoveScaled(2,1,63,4) + { Button1 Button + MoveScaled(0,0,28,4) + Text = ("Bold") + } + } + { WebView1 WebView + MoveScaled(4,7,54,40) + Expand = True + Editable = True + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FOption.class b/app/examples/Networking/WebBrowser/.src/FOption.class new file mode 100644 index 00000000..d6a6cc34 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FOption.class @@ -0,0 +1,67 @@ +' Gambas class file + +Static Public Type As Integer +Static Public Host As String +Static Public Port As Integer +Static Public User As String +Static Public Password As String + +Public Sub Run() As Boolean + + Return Not Me.ShowModal() + +End + +Public Sub btnOK_Click() + + Select Case cmbType.Index + Case 0 + Type = WebSettings.NoProxy + Case 1 + Type = WebSettings.HttpProxy + Case 2 + Type = WebSettings.Socks5Proxy + End Select + + Host = Trim(txtHost.Text) + Port = txtPort.Value + User = Trim(txtUser.Text) + Password = txtPassword.Text + + Me.Close(True) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + + +Public Sub cmbType_Click() + + txtHost.Enabled = cmbType.Index > 0 + txtPassword.Enabled = cmbType.Index > 0 + txtPort.Enabled = cmbType.Index > 0 + txtUser.Enabled = cmbType.Index > 0 + +End + +Public Sub Form_Open() + + Select Type + Case WebSettings.NoProxy + cmbType.Index = 0 + Case WebSettings.HttpProxy + cmbType.Index = 1 + Case WebSettings.Socks5Proxy + cmbType.Index = 2 + End Select + + txtHost.Text = Host + txtPort.Value = Port + txtUser.Text = User + txtPassword.Text = Password + +End diff --git a/app/examples/Networking/WebBrowser/.src/FOption.form b/app/examples/Networking/WebBrowser/.src/FOption.form new file mode 100644 index 00000000..1faee652 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FOption.form @@ -0,0 +1,69 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,61,35) + Text = ("Proxy configuration") + Resizable = False + Spacing = True + Margin = True + { Label1 Label + MoveScaled(1,1,15,4) + Text = ("Type") + } + { Label2 Label + MoveScaled(1,6,15,4) + Text = ("Host") + } + { Label3 Label + MoveScaled(1,11,15,4) + Text = ("Port") + } + { Label4 Label + MoveScaled(1,16,15,4) + Text = ("User") + } + { Label5 Label + MoveScaled(1,21,15,4) + Text = ("Password") + } + { HBox1 HBox + MoveScaled(1,30,59,4) + Spacing = True + { Panel1 Panel + MoveScaled(4,0,4,4) + Expand = True + } + { btnOK Button + MoveScaled(11,0,16,4) + Text = ("OK") + Default = True + } + { btnCancel Button + MoveScaled(28,0,16,4) + Text = ("Cancel") + Cancel = True + } + } + { txtHost TextBox + MoveScaled(17,6,43,4) + Enabled = False + } + { cmbType ComboBox + MoveScaled(17,1,43,4) + ReadOnly = True + List = [("No proxy"), ("HTTP proxy"), ("SOCKS5 proxy")] + } + { txtPort SpinBox + MoveScaled(17,11,12,4) + Enabled = False + } + { txtUser TextBox + MoveScaled(17,16,43,4) + Enabled = False + } + { txtPassword TextBox + MoveScaled(17,21,43,4) + Enabled = False + Password = True + } +} diff --git a/app/examples/Networking/WebBrowser/icon.png b/app/examples/Networking/WebBrowser/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..268485b35ff1ec22b2fde11239acf698bd053acb GIT binary patch literal 2932 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 SXLkZ!#^CAd=d#Wzp$Pz4)fQ0z literal 0 HcmV?d00001 diff --git a/app/examples/Networking/WebBrowser/list-unordered.png b/app/examples/Networking/WebBrowser/list-unordered.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6ec74c95807328dc700cfdf4dcdb5f177af8fb GIT binary patch literal 237 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/3DWebCam/.lang/ru.po b/app/examples/OpenGL/3DWebCam/.lang/ru.po new file mode 100644 index 00000000..b15ed83d --- /dev/null +++ b/app/examples/OpenGL/3DWebCam/.lang/ru.po @@ -0,0 +1,46 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/OpenGL/3DWebCam/.project:20 +msgid "3DWebCam" +msgstr "3D-веб-камера" + +#: app/examples/OpenGL/3DWebCam/.src/Mmain.module:30 +msgid "Unable to open video device" +msgstr "Невозможно открыть видеоустройство" + +#: app/examples/OpenGL/3DWebCam/.src/Mmain.module:231 +msgid "frames in" +msgstr "кадров за" + +#: app/examples/OpenGL/3DWebCam/.src/Mmain.module:231 +msgid "seconds =" +msgstr "секунд =" + +#: app/examples/OpenGL/3DWebCam/.src/Mmain.module:231 +msgid "FPS" +msgstr "кадр/с" + diff --git a/app/examples/OpenGL/3DWebCam/.project b/app/examples/OpenGL/3DWebCam/.project new file mode 100644 index 00000000..4f8290ea --- /dev/null +++ b/app/examples/OpenGL/3DWebCam/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=3DWebCam +Startup=Mmain +Icon=webcam.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.image.io +Component=gb.sdl +Component=gb.opengl +Component=gb.opengl.glu +Component=gb.v4l +TabSize=2 +Maintainer=Benoît Minisini +Vendor=ASAP +Address=bminisini@asap-info.com +License=General Public Licence +Translate=1 diff --git a/app/examples/OpenGL/3DWebCam/.src/Mmain.module b/app/examples/OpenGL/3DWebCam/.src/Mmain.module new file mode 100644 index 00000000..909efb64 --- /dev/null +++ b/app/examples/OpenGL/3DWebCam/.src/Mmain.module @@ -0,0 +1,256 @@ +' Gambas module file + +Private hWebCam As VideoDevice +Private Const ScrWidth As Integer = 640 +Private Const ScrHeight As Integer = 480 + +' Needed for frame count +Private Frames As Integer +Private CTime As Float + +' Rotations +Private xrot As Float +Private yrot As Float +Private zrot As Float + +' texture +Private textures As New Integer[] +Private Screen As New Window(True) As "Screen" + +Private logo As Image +Private tmpLogo As Image +Private hTimer As New Timer As "Timer1" +Private count As Integer +Private UpdateLogo As Boolean + +Public Sub Main() + + Try hWebCam = New VideoDevice("/dev/video0") + If Error Then + Print ("Unable to open video device") + Return + + End If + hWebCam.Hue = 10 + hWebCam.Color = 10 + hWebcam.Resize(320, 240) + + logo = hWebCam.Image + logo.Resize(256, 256) + + screen.Width = ScrWidth + screen.Height = ScrHeight + Screen.show() + Screen.Resizable = True + InitGL() + textures = Gl.GenTextures(1) + LoadTextures() + Screen_resize() + CTime = Timer() + hTimer.Delay = 200 + hTimer.Enabled = True + +End + +Public Sub LoadTextures() + + Gl.BindTexture(gl.TEXTURE_2D, textures[0]) + Gl.TexImage2D(logo) + Gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) + Gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + +End + +Public Sub InitGL() + + ' Enable smooth shading + Gl.ShadeModel(gl.SMOOTH) + ' Set the background black + Gl.ClearColor(0.0, 0.0, 0.0, 0.5) + ' Depth buffer setup + Gl.ClearDepth(1.0) + ' Enables Depth Testing + Gl.Enable(gl.DEPTH_TEST) + ' Enable texturing + Gl.Enable(gl.TEXTURE_2D) + ' The Type OF Depth Test TO DO + Gl.DepthFunc(gl.LESS) + ' Really Nice Perspective Calculations + Gl.Hint(gl.PERSPECTIVE_CORRECTION_HINT, gl.NICEST) + +End + +Public Sub Screen_close() + + ' Delete textures if needed + If (textures.count > 0) Then Gl.DeleteTextures(textures) + +End + +Public Sub Screen_resize() + + ' Width/Height Ratio + Dim ratio As Float + Dim Height As Integer + + Height = Screen.Height + ' Protect against a divide by zero + If Height = 0 Then Height = 1 + + ratio = Screen.Width / Height + + ' Setup our viewport + Gl.Viewport(0, 0, Screen.Width, Screen.Height) + ' change to the projection matrix AND set our viewing volume. + Gl.MatrixMode(gl.PROJECTION) + Gl.LoadIdentity() + + ' Set our perspective + Glu.Perspective(45.0, ratio, 0.1, 100.0) + + ' Make sure we're changing the model view and not the projection + Gl.MatrixMode(gl.MODELVIEW) + GL.LoadIdentity() + +End + +Public Sub Screen_Draw() + + Dim calc As Float + Inc count + If UpdateLogo Then + 'count = 0 + 'logo = tmpLogo + logo.Resize(256, 256) + LoadTextures() + UpdateLogo = False + Endif + + Gl.Clear(gl.COLOR_BUFFER_BIT Or gl.DEPTH_BUFFER_BIT) + + Gl.LoadIdentity() + Gl.Translatef(0.0, 0.0, -5.0) + + Gl.Rotatef(xrot, 1.0, 0.0, 0.0) ' Rotate On The X Axis + Gl.Rotatef(yrot, 0.0, 1.0, 0.0) ' Rotate On The Y Axis + Gl.Rotatef(zrot, 0.0, 0.0, 1.0) ' Rotate On The Z Axis + + ' Select our texture + Gl.BindTexture(gl.TEXTURE_2D, textures[0]) + + Gl.Begin(gl.QUADS) + ' front face + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, -1.0, 1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, -1.0, 1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, 1.0, 1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, 1.0, 1.0) + + ' Back face + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, -1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, -1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, 1.0, -1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, -1.0, -1.0) + + ' Top face + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, -1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(-1.0, 1.0, 1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(1.0, 1.0, 1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(1.0, 1.0, -1.0) + + ' Bottom ace + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, -1.0, -1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, -1.0, -1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, -1.0, 1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, 1.0) + + ' Right face + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(1.0, -1.0, -1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(1.0, 1.0, -1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, 1.0, 1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, -1.0, 1.0) + + ' Left face + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, -1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, 1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, 1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, -1.0) + + Gl.End() + + Inc (Frames) + If (Timer() > CTime + 5) Then + calc = Timer() - CTime + Print CStr(Frames) & " " & ("frames in") & " " & Format$(calc, "#.0") & " " & ("seconds =") & " " & Format$((Frames / calc), "######.000") & " " & ("FPS") + Frames = 0 + CTime = Timer() + Endif + + xrot += 0.3 ' X Axis Rotation + yrot += 0.2 ' Y Axis Rotation + zrot += 0.4 ' Z Axis Rotation + Sleep 0.05 + +End + +Public Sub Screen_keyPress() + + If key.Code = key.F1 Then Screen.Fullscreen = Not Screen.Fullscreen + If key.Code = key.Esc Then Screen.Close() + +End + +Public Sub Timer1_Timer() + + logo = hWebCam.Image + + UpdateLogo = True + +End diff --git a/app/examples/OpenGL/3DWebCam/webcam.png b/app/examples/OpenGL/3DWebCam/webcam.png new file mode 100644 index 0000000000000000000000000000000000000000..e90b27a8e5be2e8b39ac125fbbf4b67e20ff510b GIT binary patch literal 6442 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/GambasGears/.directory b/app/examples/OpenGL/GambasGears/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/OpenGL/GambasGears/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/OpenGL/GambasGears/.icon.png b/app/examples/OpenGL/GambasGears/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f4ee5b433917b29da800bcd02cbc5488c809c0c0 GIT binary patch literal 3450 zcmV-=4TbWFP)1I;1n0K-!QLLxB`RF;Jjj{J_RGSbocvWLcK=vR-?4@7{Y(|5!=3WJBdg z?BM!4b7pq$-TT|~{hr_Xo!|N01DA0bmvI@FaT)(_A*Z`|?~@&?t*Cp9c9o+s;vk*! z*_toeuigKJ@!KvosdE|WcDWkPYpW*@T+&m!g(;PkCK%}Y)oFlUIGjt!>&o#vn z4o&_TS~UB&yg>E5733F=yKn-GSl_j_X(L)ZBWlDC&u$~MCw~nYW z7c}XTkwMUE&nV@*5qS`2vJnnbZLsHQUis~BvTpu-W^{KmMif`gn$A_9xt%SY0osLF zKMmG`2$}X~rXb74SNM>9u`9i)EetIRBJX6g+$ zkOcC_jib5s7!Q2^L9%&c$Oa+S>%;nKWFP>Aej3+Q&xGQM7!&!>|2pq&1kZ@lo)K|9 zS`f#H0)YE~Q8}`23q6r6DVt$;%VBPvR)#H`$m3g^S-rD`AFp|YtC$UEook2W!D;w;8bthsPQKRDti&466DT#t!pb4-*Ib&>$5tA%XX_8L_MJ#YZ0ZxHseHEwL&SCi+$pdlc&I@ zSJ!gK(y44u$LMeo`kQu=oV|qpo-X?QfZ{|FqZJ}T#WgEg^Y8cLZ+(V?VknJi{%*-{ zaM<@LZ);)sEx&(u!wy($+4f=sH4AI_*yk##-(JU-|IE;RG)-|u-l%LCLEMR2p9-wB zsvtk!Y80ZCpp~Gsh*G(lK;waJOuEAG_JL+L?QcTI5(GURgniuvkv!J#Z6uNHB_53s z`X0xQH1UI?E17+$k=qW{)7H|(F=rUxu#XkDtY*;e*|fHXqqH5$GG$&NN`bQh1tpr| zirDDwXc^*Z6cXgeTLs{IFQ<!pY)javidYZaZiV#JFghEjfm051PK8`>-eJ^^C}ELM2jav96&j+R>)*h#ItQ! zTTMsdM5MEhOj|SAt|$f57t(uRCk3VBd3FCWlIcB|f+D(ize^;ZpkUH$VnyTm>b#{y zV)1h(51=B32@}WC(Y1p;KRnFDgwIWtrDS?ZamMxa5?!P4p8N|Mzx^UX)lB|l^ZgW0sb+5SMv9^nPO%~S zq+~F3CkbFwdQce~B321H5~W~VC6Q#3zIQf|@l%Khu?bTs_c~F=(3-V`2exCcoKNB8 z>A3tD@t}_hI$-zC^~{=c?fGHvr|GDFpC$kDRie&f^kA-TiT1WL>7l#$liTOAw|@if zc$63P!`vHvhC(%br>RAT%6(2GeL?``QZl$#RyhxdDve8wXX2I9sqTdCbOuq1)|Z~3 zV$o6xLPw&sl6tR)yz!M}e4oP^AFYJmWIknyc3SGUQaN?OIrC4y^%P5fcsF?hYDAKv z1PYq!C_my+zpR*`*pkKamV26;W%aP%8ad$sPF(7Q02=RvMbHik3X{~;^{{WvKcSQ5 zbRBMiz{e=Ke(_5F=g@BE7-&fp61ZHdsU>$(r0KZF*orE)?cGiwn#t9(QTpr(AK8u; z$~HVnD1st4^6AQ2I!daj+_4UDSOsF!WQ#-eW)HhCL0spbn){n!`yp?DE@&-jr3Ix0 z83+_2Fn7*kA{BGEedYDc`Sfk%l}{o$`#KumTTf@>ZVv9>h*|FUkD&*rPy}3 zowC~bJp1PJtl#qn|MruAptrMyLEEEk(V{Zw?WBMcThyleUSR9w#dHlMY}UlsS2-3* zJHo{YicC2x^_NevrE<$WN()-&rhpo71J5WhDiPhGaRjyBdeySe0yicIdv1CrQuv zI5nU6UH-46*r;401fUeD6CR*QE6D{EC7?&NE2?W*HG2WkY@Eu%v8cQ_HtSP(tbtF& zH8Ttkbj?H}M1TG3 zwAfR3Pc~7AH)k2g@A|7z*%FnD0ZMY5hde-OU3jE#TYKK)n9d`9MGX!|Z02Hm)BO}F#kJFBQZZ!)4g20l z8$IfJVC3ezNp&5e;i(^xj77;)3Z^Z@uUSObSN@jKZmouUKrRsyfS=cee;o?H1CYsv zNk8j8@a=Fm>+{GH50jpCEjWu7MX>#K?z-s?O2${x+ug~QZEy3+woN3a%qCV+jzl78 z=ZN+lXS(0RCs*Au$}SIwYB@w^=XzA_Vq#hmOsGDi=Oae?@}jyIKs(mlGE?6(=mDw0 zBSDT(4dsicK{OiUv&(N~%~OvOnRyLC&vDA4F|t`75g|FYgzJ~x#G?7vQeVG|E!#KH z*w%_~EfpnYR4rY_huQxyxQZ!6E2aRsvfUY55m7Qw0UwM30`UB9_s!lc-vUTyvc-XQ zu`{*(QGijB$%{og0SY2~_oiBT&;U3I`@KNpoE0w=A+zA;0C8DiTA|h@~)b+za z5bW+aejtb@qp3`&(q3jDWxE6w03ARq5!1H2qvycjRRKzW{6^y&v4Y~Mp;M7dSOB!x zY^JyKz?YX+Ed$zy1OQe6(}4sK1uo7j++Q9-AWQC`)E?l)K>;9ljIsbb$P&a#qF-h~N)24+sD^lHjF6cr=1s#${Z_ cWn9Mp0&t&Kv?XDDEC2ui07*qoM6N<$g1b7gwg3PC literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/GambasGears/.lang/ru.po b/app/examples/OpenGL/GambasGears/.lang/ru.po new file mode 100644 index 00000000..8cd90749 --- /dev/null +++ b/app/examples/OpenGL/GambasGears/.lang/ru.po @@ -0,0 +1,38 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/OpenGL/GambasGears/.project:20 +msgid "GambasGears" +msgstr "Шестерни Gambas" + +#: app/examples/OpenGL/GambasGears/.project:21 +msgid "glxgears ported to Gambas!" +msgstr "glxgears портированы на Gambas!" + +#: app/examples/OpenGL/GambasGears/.src/Module1.module:116 +msgid "FPS" +msgstr "кадр/с" + diff --git a/app/examples/OpenGL/GambasGears/.project b/app/examples/OpenGL/GambasGears/.project new file mode 100644 index 00000000..eca46a6f --- /dev/null +++ b/app/examples/OpenGL/GambasGears/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +Title=GambasGears +Startup=Module1 +Icon=gears.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.sdl2 +Component=gb.opengl +Description="glxgears ported to Gambas!" +Authors="Fabien Bodard\nLaurent Carlier" +TabSize=2 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 +Translate=1 diff --git a/app/examples/OpenGL/GambasGears/.src/Module1.module b/app/examples/OpenGL/GambasGears/.src/Module1.module new file mode 100644 index 00000000..abb49a88 --- /dev/null +++ b/app/examples/OpenGL/GambasGears/.src/Module1.module @@ -0,0 +1,256 @@ +' Gambas module file + +' GambasGears +' Released under GPL v2 or later +' aka glxgears for gambas :) +' based on gears.c by Brian Paul / Mark J. Kilgard +' +' Code : Bodard Fabien & Carlier Laurent +' + +Private screen As New Window(True) As "Screen" +Private gearlists As Integer + +Private angle As Float +Private Frames As Integer +Private fTime As Float + +Public Sub Main() + + With screen + '.Resizable = True + .Resize(480, 480) + .Show() + End With + + fTime = Timer + +End + +Public Sub Screen_Open() + + Dim red As Float[] + Dim green As Float[] + Dim blue As Float[] + Dim pos As Float[] + + red = [0.8, 0.1, 0.0, 0.2] + green = [0.0, 0.8, 0.2, 0.8] + blue = [0.1, 0.1, 0.8, 1.0] + + ' we enable lights, depth test, cull face + Gl.Lightfv(Gl.LIGHT0, Gl.POSITION, [5.0, 5.0, 10.0, 0.0]) + Gl.Enable(Gl.CULL_FACE) + Gl.Enable(Gl.LIGHTING) + Gl.Enable(Gl.LIGHT0) + Gl.Enable(Gl.DEPTH_TEST) + Gl.ClearDepth(1.0) + + ' We need 3 displaylists for the 3 gears + gearlists = Gl.GenLists(3) + + Gl.NewList(gearlists, Gl.COMPILE) + Gl.Materialfv(Gl.FRONT, Gl.AMBIENT_AND_DIFFUSE, red) + Gear(1.0, 4.0, 1.0, 20, 0.7) + Gl.EndList() + Gl.NewList(gearlists + 1, Gl.COMPILE) + Gl.Materialfv(Gl.FRONT, Gl.AMBIENT_AND_DIFFUSE, green) + Gear(0.5, 2.0, 2.0, 10, 0.7) + Gl.EndList() + Gl.NewList(gearlists + 2, Gl.COMPILE) + Gl.Materialfv(Gl.FRONT, Gl.AMBIENT_AND_DIFFUSE, blue) + Gear(1.3, 2.0, 0.5, 10, 0.7) + Gl.EndList() + Gl.Enable(Gl.NORMALIZE) + +End + +Public Sub Screen_Resize() + + Gl.Viewport(0, 0, Screen.Width, Screen.Height) + Gl.MatrixMode(Gl.PROJECTION) + Gl.LoadIdentity() + Gl.Frustum(-1.0, 1.0, -(Screen.Height / Screen.Width), (Screen.Height / Screen.Width), 5.0, 60.0) + Gl.MatrixMode(Gl.MODELVIEW) + Gl.LoadIdentity() + Gl.Translatef(0.0, 0.0, -40.0) + +End + +Public Sub Screen_Draw() + + Dim calc As Float + + angle += 0.05 + + Gl.Clear(Gl.COLOR_BUFFER_BIT Or Gl.DEPTH_BUFFER_BIT) + + Gl.PushMatrix() + + Gl.Rotatef(20, 1.0, 0.0, 0.0) + Gl.Rotatef(30, 0.0, 1.0, 0.0) + Gl.Rotatef(0, 0.0, 0.0, 1.0) + + Gl.PushMatrix() + Gl.Translatef(-3.0, -2.0, 0.0) + Gl.Rotatef(angle, 0.0, 0.0, 1.0) + Gl.CallList(gearlists) + Gl.PopMatrix() + + Gl.PushMatrix() + Gl.Translatef(3.1, -2.0, 0.0) + Gl.Rotatef((-2.0 * angle) - 9.0, 0.0, 0.0, 1.0) + Gl.CallList(gearlists + 1) + Gl.PopMatrix() + + Gl.PushMatrix() + Gl.Translatef(-3.1, 4.2, 0.0) + Gl.Rotatef((-2.0 * angle) - 25.0, 0.0, 0.0, 1.0) + Gl.CallList(gearlists + 2) + Gl.PopMatrix() + + Gl.PopMatrix() + + If (Timer > (fTime + 1)) Then + Inc fTime + Print Screen.Framerate; " " & ("FPS") + Endif + +End + +Public Sub Screen_Close() + + Gl.DeleteLists(gearLists, 3) + +End + +Public Sub Screen_KeyPress() + + If (key.code = key.F1) Then Screen.Fullscreen = Not Screen.Fullscreen + If (key.Code = key.Esc) Then Screen.Close() + +End + +Public Sub Screen_MouseMove() + + If Mouse.Button = 0 Then Return + + Gl.Rotatef(Mouse.StartY - Mouse.Y, 0, 0, 1) + Gl.Rotatef(Mouse.StartX - Mouse.X, 1, 0, 0) + +End + +Public Sub Gear(inner_radius As Float, outer_radius As Float, width As Float, teeth As Integer, tooth_depth As Float) + + Dim i As Integer + Dim r0 As Float + Dim r1 As Float + Dim r2 As Float + Dim angle As Float + Dim da As Float + Dim u As Float + Dim v As Float + Dim fLen As Float + + r0 = inner_radius + r1 = outer_radius - tooth_depth / 2.0 + r2 = outer_radius + tooth_depth / 2.0 + + da = 2.0 * Pi / teeth / 4.0 + + Gl.ShadeModel(Gl.FLAT) + Gl.Normal3f(0.0, 0.0, 1.0) + + ' Draw front face + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), width * 0.5) + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), width * 0.5) + If i < teeth Then + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), width * 0.5) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), width * 0.5) + Endif + Next + Gl.End() + + ' Draw front sides of teeth + Gl.Begin(Gl.QUADS) + da = 2.0 * Pi / teeth / 4.0 + For i = 0 To teeth - 1 + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), width * 0.5) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), width * 0.5) + Next + Gl.End() + + Gl.Normal3f(0.0, 0.0, -1.0) + + ' Draw back face + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), -width * 0.5) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), -width * 0.5) + If i < teeth Then + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), -width * 0.5) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), -width * 0.5) + Endif + Next + Gl.End() + + ' Draw back sides of teeth + Gl.Begin(Gl.QUADS) + da = 2.0 * Pi / teeth / 4.0 + For i = 0 To teeth - 1 + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), -width * 0.5) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), -width * 0.5) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), -width * 0.5) + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), -width * 0.5) + Next + Gl.End() + + ' Draw outward faces of teeth + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth - 1 + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), width * 0.5) + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), -width * 0.5) + u = r2 * Cos(angle + da) - r1 * Cos(angle) + v = r2 * Sin(angle + da) - r1 * Sin(angle) + fLen = Sqr(u * u + v * v) + u /= fLen + v /= fLen + Gl.Normal3f(v, -u, 0.0) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), -width * 0.5) + Gl.Normal3f(Cos(angle), Sin(angle), 0.0) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), -width * 0.5) + u = r1 * Cos(angle + 3 * da) - r2 * Cos(angle + 2 * da) + v = r1 * Sin(angle + 3 * da) - r2 * Sin(angle + 2 * da) + Gl.Normal3f(v, -u, 0.0) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), width * 0.5) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), -width * 0.5) + Gl.Normal3f(Cos(angle), Sin(angle), 0.0) + Next + Gl.Vertexf(r1 * Cos(0), r1 * Sin(0), width * 0.5) + Gl.Vertexf(r1 * Cos(0), r1 * Sin(0), -width * 0.5) + Gl.End() + + Gl.ShadeModel(Gl.SMOOTH) + + ' Draw inside radius cylinder + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth + angle = i * 2.0 * Pi / teeth + Gl.Normal3f(-Cos(angle), -Sin(angle), 0.0) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), -width * 0.5) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), width * 0.5) + Next + Gl.End() + +End diff --git a/app/examples/OpenGL/GambasGears/gears.png b/app/examples/OpenGL/GambasGears/gears.png new file mode 100644 index 0000000000000000000000000000000000000000..6eed1fa86ad3f75a71131ccd7b9349be424d80c4 GIT binary patch literal 3488 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/Md2Model/.directory b/app/examples/OpenGL/Md2Model/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/OpenGL/Md2Model/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/OpenGL/Md2Model/.icon.png b/app/examples/OpenGL/Md2Model/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..22e52cbf7cf15fa7739846c602b7c482a5da47d8 GIT binary patch literal 3521 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, YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/OpenGL/Md2Model/.project:23 +msgid "Md2Model example" +msgstr "Пример Md2Model" + +#: app/examples/OpenGL/Md2Model/.project:24 +msgid "This example shows how to use the 'gb.opengl.sge' component and OpenGL to display a lot of animated monster." +msgstr "В этом примере показано, как использовать компонент 'gb.opengl.sge' и OpenGL для отображения большого количества анимированных монстров." + +#: app/examples/OpenGL/Md2Model/.src/FMain.class:85 +msgid "objects" +msgstr "объектов" + +#: app/examples/OpenGL/Md2Model/.src/FMain.class:148 +msgid "FPS" +msgstr "кадр/с" + +#: app/examples/OpenGL/Md2Model/.src/FMain.class:153 +msgid "vertices in" +msgstr "вершин за" + +#: app/examples/OpenGL/Md2Model/.src/FMain.class:153 +msgid "seconds" +msgstr "секунд" + diff --git a/app/examples/OpenGL/Md2Model/.project b/app/examples/OpenGL/Md2Model/.project new file mode 100644 index 00000000..278d09a2 --- /dev/null +++ b/app/examples/OpenGL/Md2Model/.project @@ -0,0 +1,22 @@ +# Gambas Project File 3.0 +Title=Md2Model example +Startup=FMain +Profiling=1 +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.db +Component=gb.db.mysql +Component=gb.gui.opengl +Component=gb.opengl +Component=gb.opengl.glu +Component=gb.opengl.sge +Description="This example shows how to use the 'gb.opengl.sge' component and OpenGL to display a lot of animated monster." +Authors="Benoît Minisini" +Environment="GB_GUI=gb.qt5" +TabSize=2 +Language=fr +Vendor=Example +Packager=1 +Translate=1 diff --git a/app/examples/OpenGL/Md2Model/.src/FMain.class b/app/examples/OpenGL/Md2Model/.src/FMain.class new file mode 100644 index 00000000..e3e7bbda --- /dev/null +++ b/app/examples/OpenGL/Md2Model/.src/FMain.class @@ -0,0 +1,243 @@ +' Gambas class file + +Private $aModel As Md2Model[] +Private $aObject As Md2Object[] +Private $iFrames As Integer +Private $fTime As Single +Private $fFramerate As Float +Private $iEndWidth As Integer +Private $iEndZ As Integer + +Private $iDisk As Integer +Private $hQuadric As GluQuadric +Private $nDraw As Integer + +' Private $fRotX As Float +' Private $fRotY As Float +' Private $fStartX As Float +' Private $fStartY As Float + +Public Sub glaScreen_Open() + + Init() + Gl.ClearDepth(100.0) ' Enables clearing of the depth buffer + Glu.ClearColor(&H3398C3) ' This will clear the background color to blue + Gl.DepthFunc(Gl.LESS) ' The type of depth test to do + Gl.Enable(Gl.DEPTH_TEST) ' Enables depth testing + Gl.ShadeModel(Gl.SMOOTH) ' Enables smooth color shading + + $fTime = Timer + timAnim.Enabled = True + +End + +Public Sub glaScreen_Resize() + + Gl.Viewport(0, 0, glaScreen.Width, glaScreen.Height) + Gl.MatrixMode(Gl.PROJECTION) + Gl.LoadIdentity() 'Reset The Projection Matrix + Glu.Perspective(45.0, glaScreen.Width / glaScreen.Height, 0.1, 3000.0) 'Calculate The Aspect Ratio Of The Window + Glu.LookAt(0, 100, 120, 0, 0, -300, 0, 100, 0) + Gl.MatrixMode(Gl.MODELVIEW) + +End + +Public Sub Init() + + Dim X, D, Z As Float + Dim sModel As String + Dim hModel As Md2Model + Dim I As Integer + Dim aModel As String[] = ["bauul", "goblin", "knight", "ogro", "rat", "rhino"] + Dim hObject As Md2Object + + 'Randomize 1972 + $aModel = New Md2Model[] + $aObject = New Md2Object[] + + For Each sModel In aModel + $aModel.Add(Md2Model.Load(sModel & ".md2")) + $aModel[$aModel.Max].Texture = LoadTexture(sModel & ".jpg") + Next + + D = 100 + + X = -D + Z = -100 + + Do + + hModel = $aModel[Int(Rnd(0, aModel.Count))] + hObject = New Md2Object(hModel) + $aObject.Add(hObject) + $aObject[$aObject.Max].Move(X, -10, Z) + X += 50 + If X > D Then + D += 100 + X = -D + Z -= 100 + Inc I + If I = 10 Then Break + Endif + + Loop + + Print $aObject.Count; " " & ("objects") + + $iEndWidth = D + $iEndZ = Z + + sldFrame.MinValue = 0 + sldFrame.MaxValue = $aModel[0].Count + + $iDisk = Gl.GenLists(1) + $hQuadric = Glu.NewQuadric() + + Gl.NewList($iDisk, Gl.COMPILE) + Gl.Rotatef(90, 1, 0, 0) + Glu.Disk($hQuadric, 0, 20, 30, 1) + Gl.EndList + +End + + +Public Sub glaScreen_Draw() + + Dim fTime As Float = Timer + Dim I, N As Integer + + Gl.Clear(Gl.COLOR_BUFFER_BIT Or Gl.DEPTH_BUFFER_BIT) ' Clear The Screen And The Depth Buffer + + Gl.PushMatrix + + Gl.Disable(Gl.TEXTURE_2D) + + Glu.Color(&HD96800&) + Gl.Begin(Gl.QUADS) + Gl.Vertex3f(-100, -34.2, -100) + Gl.Vertex3f(100, -34.2, -100) + Gl.Vertex3f($iEndWidth, -34.2, $iEndZ) + Gl.Vertex3f(-$iEndWidth, -34.2, $iEndZ) + Gl.End + + Glu.Color(Color.Lighter(&HD96800&)) + For I = 0 To $aObject.Max + + Gl.PushMatrix() + Gl.Translatef($aObject[I].X, -34, $aObject[I].Z) + Gl.CallList($iDisk) + Gl.PopMatrix() + + Next + + Gl.Enable(Gl.TEXTURE_2D) + Gl.Color3f(1, 1, 1) + For I = 0 To $aObject.Max + N += $aObject[I].Draw() + Next + + ' You can use this code to get FPS printed in terminal + + Inc $iFrames + If Timer >= ($fTime + 1) Then + $fFrameRate = $iFrames / (Timer - $fTime) + $iFrames = 0 + Inc $fTime + Endif + + lblInfo.Text = Format($aObject[0].Frame, "0.00") & " / " & $aObject[0].Model.Count & " ( " & CInt($fFramerate) & " " & ("FPS") & " )" + + Gl.PopMatrix + + Inc $nDraw + Print "\r"; Format($nDraw, "#####0"); ": "; N; " " & ("vertices in") & " "; Format(Timer - fTime, "0.000000"); " " & ("seconds"); + +End + +Public Sub Form_KeyPress() + + If Key.code = Key.F1 Then + Me.FullScreen = Not Me.FullScreen + sldFrame.Visible = Not Me.FullScreen + Else If Key.Code = Key.Esc Then + Me.Close + Else If Key.code = Key.Space Then + timAnim.Enabled = Not timAnim.Enabled + Else If LCase(Key.Text) = "w" Then + Gl.PolygonMode(Gl.FRONT_AND_BACK, Gl.LINE) + Else If LCase(Key.Text) = "f" Then + Gl.PolygonMode(Gl.FRONT_AND_BACK, Gl.FILL) + Endif + +End + +Public Sub timAnim_Timer() + + ' At every timer call we increase interpolation. It makes Frame number increase every 10 calls. + ' You can control frame flow any way you want. The smaller incrementation, the smoother movement. + + Dim I As Integer + + For I = 0 To $aObject.Max + With $aObject[I] + .Frame += 0.1 + If .Frame >= .Count Then .Frame = 0 + End With + Next + + Object.Lock(sldFrame) + sldFrame.Value = CInt($aObject[0].Frame) + Object.Unlock(sldFrame) + + glaScreen.Refresh + +End + +' Just the subroutine to load textures for our models +Private Sub LoadTexture(sPath As String) As Integer + + Dim iTex As Integer + Dim hImage As Image + + iTex = Gl.GenTextures(1)[0] + hImage = Image.Load(sPath) + Gl.BindTexture(Gl.TEXTURE_2D, iTex) + Gl.TexImage2D(hImage) + Glu.Build2DMipmaps(hImage) + Gl.Texparameteri(Gl.TEXTURE_2D, Gl.TEXTURE_MIN_FILTER, Gl.LINEAR_MIPMAP_LINEAR) + Gl.Texparameteri(Gl.TEXTURE_2D, Gl.TEXTURE_MAG_FILTER, Gl.LINEAR) + Return iTex + +End + + +Public Sub sldFrame_Change() + + $aObject[0].Frame = sldFrame.Value + +End + +' Public Sub glaScreen_MouseMove() +' +' $fRotX = $fStartX + 180 * (Mouse.X - Mouse.StartX) / glaScreen.Width +' $fRotY = $fStartY + 180 * (Mouse.Y - Mouse.StartY) / glaScreen.Height +' glaScreen.Refresh +' +' End +' +' Public Sub glaScreen_MouseDown() +' +' $fStartX = $fRotX +' $fStartY = $fRotY +' +' End + +' Public Sub Form_Activate() +' +' While $nDraw < 10 +' glaScreen.Refresh +' Wait 1 +' Wend +' Me.Close +' +' End diff --git a/app/examples/OpenGL/Md2Model/.src/FMain.form b/app/examples/OpenGL/Md2Model/.src/FMain.form new file mode 100644 index 00000000..6ca8c351 --- /dev/null +++ b/app/examples/OpenGL/Md2Model/.src/FMain.form @@ -0,0 +1,31 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,160,72) + Arrangement = Arrange.Vertical + { Panel1 HBox + MoveScaled(1,2,116,4) + Spacing = True + { sldFrame Slider + MoveScaled(0,0,24,4) + Expand = True + } + { Separator1 Separator + MoveScaled(54,0,0,4) + } + { lblInfo Label + MoveScaled(64,0,18,4) + Padding = 4 + AutoResize = True + } + } + { timAnim #Timer + #MoveScaled(52,13) + Delay = 20 + } + { glaScreen GLArea + MoveScaled(4,14,16,16) + Background = &H7FBFFF& + Expand = True + } +} diff --git a/app/examples/OpenGL/Md2Model/Weapon.md2 b/app/examples/OpenGL/Md2Model/Weapon.md2 new file mode 100644 index 0000000000000000000000000000000000000000..560facea70735ad5e0232ba4fb9b71401fe6efd0 GIT binary patch literal 71708 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 z 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 z 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/Md2Model/Weapon.png b/app/examples/OpenGL/Md2Model/Weapon.png new file mode 100644 index 0000000000000000000000000000000000000000..e2eab7addb7e11aa441c9551dd5707a94cca1d63 GIT binary patch literal 17376 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 X&M~jdJ_R2(00000NkvXXu0mjfA%%l8 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/Md2Model/bauul.jpg b/app/examples/OpenGL/Md2Model/bauul.jpg new file mode 100644 index 0000000000000000000000000000000000000000..403d146f618d76dffa9a6e376cd59dc3711181ea GIT binary patch literal 25801 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 )d@x@=n>pwd* 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 bScg9WsNVZDMo|oq00000NkvXXu0mjfq5LxQ literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/hflip.png b/app/examples/Image/PhotoTouch/hflip.png new file mode 100644 index 0000000000000000000000000000000000000000..72f46a74625595b9b4575f93bd99043804e858db GIT binary patch literal 953 zcmV;q14jIbP)7NO6vzK>gepNAgh(Z&iACwoqC_MlB`gThg$N5>kyz=14M`=eDpoHfzC(3mYf&sp z5G)$RM^TArd_;92ZK&$|*}T`ecji8wnfE3VdFO53%$$4vznQsn&$&mcSj8$j z4%5pJAIP$zs8=IdJ}^WthnQVX`FdXB+X&m7UfUTlyk4-hT=I1cVB09mLSD-l4R{Su zR}T5PJSS`yLw=VWUeB3Zs(cMkaoO9DXTQ_$2{on2S8>~ncF+Gi9`YS8T)J-~+@dPE ze770tI4#Ce$a}(CIAJ4nCqlrZ(~NSOOk{uzXKaK{y!Zeo%?Mq1C^CTX=&}(y;>sU0 zN8ey7iUlC1a>GVwk0ak^j=akZ6dORy;I56(7FE8LoIk3UIr)Hb0mK}7O@JJ&QRFxC z%OCTOMHAs{fa2C-j9A1w^U^OiO(ws_T+csJZv;yMAnN&KUL0Y~-{e=D@m)B zU2s4yP-BE?{7hdQXZPxCK}?cfXl>@3fA_O^WCHGU+(YTV{q}=;$gG0=bUWuANkv|W zqc%bzPjcBj+mV{Q5FIwcB}r^B&zvKZDnN$wHbS$w=^tHZN~-cgOyRl-&@Da}{`XLw zy1Wq8^!NauJWZ4eW6DMN$V#`I#{2_M+%ldA(^9|xK%I9pFbS{Xr79n&^PoBO8D>)j z$c8_|@Ius{ntZ!^Aqp?W2UC%6v*q(ku`?Ovv`bx%`CF@15oY-V4LHv3)D;=Ext)w8 ziTS+p@qV+zaSzGU@eY2QF0YvHU}Q1xeVxK+e0Cpg)oiny5*2&z4W#q>?16xr594LY}Xk zemzu|ATRy%Mx_+YlK>1;8}h2Px4;z3d5IAIZL?a+x9cC%Pv4~N`ab%3T;_It#VS^@ b|Jwcl^XW?!Ud0Rp00000NkvXXu0mjf@VU3= literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/icon.png b/app/examples/Image/PhotoTouch/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3107012937f062a452029c64c7469d837a07f718 GIT binary patch literal 4904 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/magic.png b/app/examples/Image/PhotoTouch/magic.png new file mode 100644 index 0000000000000000000000000000000000000000..a97ee5fa15543f2d885e2c359697c9314f4cf9c0 GIT binary patch literal 1093 zcmV-L1iJf)P)`VYx+lh!jRHokw%f{THdSuG>IkPTWH|JHe7DJ=jSu!6DA@XFwiHSiuG&E-^|J+2wXi zyUZ5?{*}r(btJrnbR^5YGBrmb*i75AdT~ zyf4pP3kQ`Z5vpBfKA@jA1{~{_TVdsyniumukN6yQoJ#r)ylrZ{%6Ior#wsIk6fnbg z_W*_I3kVYd2pFYX9cEz*xWE$1DP##C^RW?x_h?b8!5 zz=%i9RhILG#;m7&bl*Y3%5nal=>DaH13Ji_fH{UOrF;st&v+!-Io&4>TGFF z+5C4&bb11IraZh}Mny#AuoP%~fxKrte_WGw*$X%+wn2Zrh%}0&aS{1yl0(p5nURfv zUu3q&zII|v^Q2QIM~Bv3ssPVfaGe{YKFoJiPBD`n=TnL@nTBI*;0MRD zIKoHdDYY&tXd^oHT&9L+G7+$yPZX=3I@Yky&~7bll&0Z+JPadDW1vbKVo%>0_aa*- z^!;-(DCr}X{unr~{I8X9*hy0Zo3wUuZloE#H;e+@b$=$un5`snKTj#`x~bESGrkFM z9Uh`wWAZu5V~X9%Mr}y?EZ}wOQn&~ovp})mXGc&1R?}$Y7xI~QZfxLNe+1|!w}pH_ zp2tH=IZD1`348nz@Vi16af}Bhd90QeWnTZ`kAM<|-aqByx{-RPym}e}lq?rJr6@0@ z@#qfnY2qd?v&EkbxLA^=_U$AX;b5AlY4dr}DBv4@okmP%#QEAI3?C3_T7A3`<<`BeaXCXxVOQHA!TG#~}%QXIrV8BX{YjYNM5@Qo$Kr2xMQ9(>Z zEK*27Nk9utVG%H(CLtj~{JYmeHoLbobGv)r+=!5Go1NL8_xZk=eP?%p)%bsdB_80q zehdJ*b zffJ!#AHiMxB1CAB;Y{KvzD7-j{=5i606BsfK#Jf1K!)H1z<_Wgt1mbL&=7#D*$@J~ ziwcLZ2OH69ykb*5gy+~0byT>h26E;>#p8UzZEtd= zOdklA!?Qn#<+CHijYzGaWSlkiV;o14?r1=8=7gru40=GHj!gwH7<7dvyaE`iFv#6r z0W^d)xJ4B;Jplamg@vfug)Ywkz&MV@jcvbo0N@>#qeg6@_XJSD6BMK7$7namf3y5i z!f1Th=P>HE0ttEGvijx`K$E|thU3j918DO1ai;c+rUDq`d(sq8mOdy9g*0!WY1wAN42yKK2O%5gGl0@_nd+MQu9@~b}<`QgA zt;K_l9wfOGdZ;u|XcMeC2t^W%F=9e4(IzR1uF-!Fb@q)rv(F~(yuJ9}%X{B{=6%1J z_h$C(eux5m-c-LPwWR;_P*?Sls{Y@(J5OGt%Zk#HK9(0hz&_npehsN4UVy#U^pbQj zUVsbQP8({rkC711tt9tJ`JHh_nMaiII^DcvjvD5qb(9@k|8aAn16wA$M$^VxRYhD= z@TH$)LCGAeD>x1B*hcZ5e+H=a8xt(&Sic23`)z~l1YnQfSPWTz2Rr3)0Te{L0HqFc zl<+Ps?56@)VUBBz@zf>2H|BMumg!;Km>muPMp@s|UuV>G z>Ky>g+ai??0A!66{kx#(0MGImR?pHBiobG*L$xkYS!b&9A@s0-ixyr%#Q$@=I`iqwyN_mYBOgGQ|Jyw8T^Rmgie~(cf z?S9j`e~%I1RljN7zsCshqTjUc-(v*G%D#Vh08kw50_+0p0(fSxl?s``NL9#9N3^1j zwvM~%nb%!w=)hp4EyqjJm~A(b%yHZDj1r2>p%K5Zv969w>WEt0v%;Xkbfi z%2Kkik2{!9CqMZ(8w~LWkAM`doXS}s5BZ)cG9;+u1kE{DmA7bRAGbXrkH4s=IJ!Zj z#|CE$w8ei`Ez`|r)$Ev7^Ohc0b;VMcoYJht9m^K9!&=heVA-xkQ+X1!2=!2NRl#|(TF;;_3sySRzK^(<^q1K>-s|bvgg{%_iY-~ kEv-nBR&+~)YBLYTzs=P?KoiDjVE_OC07*qoM6N<$f;|SVB>(^b literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/oil.png b/app/examples/Image/PhotoTouch/oil.png new file mode 100644 index 0000000000000000000000000000000000000000..a19068ff368e5d0a59d7a4190a2f65d886f5f2bf GIT binary patch literal 1189 zcmV;W1X}xvP)2*I+eKJeTcMFE8u`(PjS-EgO_iXgrbLZ5^g<(QP3nb^YRINyVghQ2VvHiB zDiWFSgs6b9QHYc4u0@b2Ddm=6&98 z&N<)AIU){&DxRgAetu#N7r0aohhNKCRP=DqbiyNSBR5i1P7glDJLHeQSUDZp@5CHq zyg9}9125>jUY_Ah&_p+PP96RcophMX5f<}x?mTQKF%|e)hIIVJ2!aq{9fLs=UEJXs zALn7x*%MQ<|O3Fc=UbJf1rbE%pW)b-e~7K66yl;Wu!aDm#3PBTDU?N$`SD#(MpM zfd}jiH0fHmg)krpHT0=Lo9wQCms08oi5DsvR>Rh1v?3)rxK|dalh;EJYmk^a>9CoB z&s8G*#6!=(4{F#Sc%|bPF{+fdg~s2c-!{+LC<9?sJhi!DUHJ?`F6L++Ml{q!Yo$YusU)RU7Ge$j z)V~bdy)AL0uJ!Gtc#n7e`1h>vA|!RSt&)$wjj-Jv$!Bt>$5Py`p_W9T8ispS_lvjfI5pFT5mKEXP2K~gep{bHx za%=F%r@im2^B3W5J*m}sv%l7Vn`Gh*2XGOVa#UBnDhD26qrU0P#xAS1ZyfHC3~DEt z<-=b@8+qaj|I$+$KEjTC+C8kDcpL#vvypV}8UZj`@ynulgrFxJ#f%`sAGFa~c`nk&qTm( zw%sm$hF-&}+2$Q^YCvw5i5*PJ*r{AK2aCrSHgJEoF(MxsJIzK#+|Y!P85{km$qq5jRM)&JEq z&1G&3T(2}w@g&h4bbeU)xh-2(vC8d4l&WSwb zli5-)buw2fBq6gUE+Rvcmb9d$SN6+3@9-4l{r|;(7n5AF;0)RI00000NkvXXu0mjf Dh?qrE literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/ok.png b/app/examples/Image/PhotoTouch/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..0bf4198acefa8cab107b35aadbcaf7c38f806df2 GIT binary patch literal 655 zcmV;A0&x9_P)L%DwOnbX_9-B_kQKR_tYUL>zngDzjNM%OCk+xSchDtq5+I)PNQn-ot8`c z#_1^Ylx}@w)E0k)_k?SciNnNODn#I|6e#mL$ov*o*kIf={W3>+VmAdBUdT$Jcld3* zL!pR8#NnL^VWclD-Vk_L~PKxx4h_n1~Ou=OMiwAF>>{Boq{=GnhNq&aM zw~_-NG5j9<{hlW0!U`GiGlSqCI2ySXlJU(H_z~8EryfQ$JI}9tc=zrl9tBS=#WV~n zcu6lwtfs>YaiOE&DZM1oXrFqKfEVH_4U)fhF0-93y(ICaeXh>M6bje)kbu9E1+UQK z$qrK~6yjf%w7c&JoaDPdg-ZI}2NdRM1r?_4(svXVd?*Zz_oI*t?^2k_gsSJ zb$c{3C{zczdj1r=;av(9TYUdrNK&Zz*!|#BsIlueeKv){@Gga_Ene!s3)MKhpWP=- zp=Ouft+0EbaNP>;R=B}mJG|6;7aD1v^NXE=Y5G_NJG@iD7B5NM3_R>^GwZ8whCi)1 prdgeq^jU99K7QXD*0BDM`Um~?H5zz-WO4ui002ovPDHLkV1kE*I>!J2 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/photo.png b/app/examples/Image/PhotoTouch/photo.png new file mode 100644 index 0000000000000000000000000000000000000000..eb7c3c23ab9113faacaa337822aa7b678741ee21 GIT binary patch literal 1068 zcmV+{1k?M8P)>5HUeejGyrvl%Phf$?oYxn9RMKotZnE>-y0A_vM_uXU_fK zbI(2Z%)l7_?{G|vBt<7~OwU4lYFEJKB3>5=x3oB!=WRel;Mxff`TDQt4a?w@zx4O((^a5&jaM*vN4M0^L3V zmye}2XSk1E=g!D=PLMk343B5GVE8*7wKb=uP9~_+SIC*@4XrmOu+tU-Kk30pwpVAvYOi z@SD;Vc&M++IwK!zlxLJ2FzHPiXlGI2Xo}xxCb|t$BjCo#flv9uzncS4M@yuqtRlxC zP)nmhjG&gi&m#imH0SyEv3jP5@d-=q6KG_Trr%&U6}-%3rjh2HHkRN^V*=spq>T?!PmA`?M!Q?ER*^I8U?7jp z9_`9r9dfL70!DS9N|D=gcHBcBKPy@puiA^iR3<9g(LlQ+npPfY7rPEj*R)%KcKXN+ zdl7h`X(fSnB_U6Ziojhminb)su1eEx+9QzRvZAf#mAqCiwTkwXUV8-K4@EBJqr5g- zS*U1jA-zEX1P*9g16xK|RP(K-{~qE^FU2>!qPQ!hcsg=IE3{epUMhx1OJ!e9sqU4D zp-qhs!=6xRphM9ewvpx}7sxP+w-}#e-x%7QWi{8m9+y24jnsFhuUm3R5}}@6i+>pb z?BpOOILmtN*oNmo3<4Q8aX51NZdT=OQY;XF`>f=v(AE9I>srmQOaKP?kShMkJJv}x zwK;u4G=XINSFVZh;->{XncY zBRf!+>T=H$=M{~BHj^}aHR+kGEXoI(*>b@zgx4LZ_FFmORNzM{TbRspwZq)+h{IXt z7cKfa2AJzQLLE%uEsD1EBFuCVLekV2RWGl0000+T0( z`86IQIHqqDZO2R4$|JAe`HEAR+oVHQ0P)LP0l1L20B|F348R-;F60dXn8O_?7xF-t zfqWLXjpciJGxUBHUoejuMQg&absxOOXlSo_)`7eQ07>48UEUIayd1Nd-mmj`r1z#*ie)|ERdIO`e5Z%J-t-f=O1j}G zpdk}-)K$_Aw*UY~aMxARH2`25d0F{j1ny(Ith^6^2XUmA8YmJ?FirpOmc#f4?^DtgQ=FIH@P!(L$y)>pp5PZu}J;sDILNFCy!XsIP z0D!7sSQeq-XQZmK2qEW!MuaCZEXoA->dsHy4Be8cDu)4FsP&Ix3TIG?9Q-6sw*(kO zN_Q3wh?(T1cPIIlJ#lSH%k%^^@@VF8E<8{r!zWFnU=@Km?y!J0Lof#T&4=vuE3`PA z2B%{h(-^{P^b5ZbRi^re_b8~3ZG18+-fJEO$3-edT*0{R$D5jr3KWVMb(I|rOEm5l zrE!tU6@o>X&JBW9q0$9{WvMpoPq{}QrerLb9Y1qTcV5XL1X7d7ZgeOL8}^<1g#aQD z&Wp(d029oyR2MICq@7VVIf0o%2K!LNJc_C!>i?F-Wq$#ZBt3$)7?ceF0000rG93~DH&)Bw&~?s175mg;NO7LbqSnDmQTX_ra`x{_3dbQlJ=B`q5@ z2*@mX5evL6jROf-f;A9+6t|gfm)khSBLh>Qlp1Q>N;g&3{Xr}sdu^gU@`YG}u`4C7 zTpOGjWC&!KJhrvA8w9tn)h>ZPnGiYzQqCQKmz450#Gu6_NKitO@gylBL4qWs+02m~ zLvf#3b|Gc`;t~k%1C#CBT@QXL^GT8*K`9BwQ^MGg*MFLmz)hFHq)c0ofKz|tHMBDU zQ_`(K9hR$;Ie4|?BoMd!PvCta(5GlS%TlU28j(Peo?0D0_!lqmGj#y`%2U3H-vXU1 z;>CcLX0P;pBnxQIfd`~W`|6pks$2IJ(Fnj5*86HJywYcp2>9lJjHRAhyIjss3gyF; zvCS!O;4v-qvVwh)2zbY_ER?QrH>T1#QY|?Ws?(D5KyGnWn= zU>QEs>~@(|^v6L!Urw;2umoT)2MbF8n)s)%1hm&~JOsXCPh|KA1X3I@0G~I31svyt zI0%f>>NJg$j5GqD*$JILu_orxqfq;jhdz?627gyv0=HNIP|QN5+Gwxz5$Bj`3_ddi zyZKX(?~hc4Jo`He0qZF#onXj8mV`XHm*>9dxn$OL`Gk6Es0QfbtkeFvPkASe%9o_s z$$onCA`py_EVF&43!WR5amm(%#O;2QIhrw&L0q!jQp=%0I18YUUCgtCDtU_LpHU^> zDK?(pY2aD}aM0cfTe6Owe!it5GQ740g7T0C+9HDw!2*5}nwe_|J`W3Y(8vWt;CWc! z5amYUhh%{+*3oJdT+0F-&ixEkF~DmOSVuQ6X`q(BVt~&c0w);sq1SMpsTAuSl{v+8 zVtg(~kU*z>o6QI>TKvS=*Lnwer>`+^7!|qMlKsZR^&8xys8^Cc^sIzMvm4akWHe#1 zQeG<)7TGDA67JwIY~z&jXMN~nznH~tC0EJ@WRax*yQGymZMR(hjRia{Uk8vv6PIPu zY8<{@Dzb30=Ya3wY^s@VD4hL>i>lPE6h3)v6*_(Y4s_;d&yA|7)&Kwi07*qoM6N<$ Eg7$gZqW}N^ literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/redo.png b/app/examples/Image/PhotoTouch/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a64d046861d4a66ddc24edea617f9d822bb950 GIT binary patch literal 1042 zcmV+t1nv8YP)qi6Xdw3o42tf?L3Y;6YJAgD5DjC>}&ah;K-Xn(kKx{AaV$d6;ob z!$`z&7uT%T$l1Ay?`c~T7ep<;kE*qEO_cJJsk5!o0bHiuwnro1m(t|_P!Tpp0WeeO zm9v6PD#E7B11u71AUK*IRfH`O1Aygxts?A*7yul?1{Gmf!~o!6zETnPL<|7-;|mpG zulHC+-vVZ#0z*-RV)Q~M0HqkOAD}O;qQBg+-`bE~tZ~j`4d2n#;JEtmnd)$W!9RFh z!l(2wBwb}rgz7*DEMc9OxE=s3b8;W@jlgkiG?CW>fW>^Mx*c{yh->A?;}UY!zBar> zGn&zC6u?`|#uW?{9V?KVB2-iJFBPW|_$6@V5NaIhOjC zwy)lNtRfs`%o|{v@NeVVz~cX|5*Y z6`|I3EilG<+pB?HLjnMM^1h0&-VI=u@L3m{JP79Th79nN1$uxZw#P3PgbD!m;2jxZ zp{=RT#zebu9!*B?40HR08K@Vx6EpzqhH|@oHDviDu|K*Dj1_mcLl@6S9m>S*O^pjz zi8YcP@(RdTSHMs9v{mJgQK8*vK)74oVqC#6xnr~SK{o~6?8cYO%AZ2DxRC+!G69ez zUyEfH^#F+q;A?2|`MBVeuT+mR7P zjQVc>@+v^${gV@j%5U=jnLGvG?MANGQz1p~%)mDhi_WCHCoP9y(K`Ve?ZzPOf_v*a zh@by`3gaA6vh>zeHB+?P^Wn~Fhdi5d?~?*xfEZ)3wy-*;0eHjb;m8BD#97cWEkF)W z(!+ww1CT;&a@m;0xWRci9iBYEN-sQJ0OG6L`0QTZnG3se{P5}^Oi)OQ#KEi4_uzQ@#xXV9cm+Uw+s9w@5 zj}~e%U+(#g2l(yq>WEQd9|L?tcf%t%qa5U^=VS&}6PkDe;i3=ucPt7S0AP%}*=i(T z&td87=^;k&IDe%S|HA#uOHak4&|@y9q5>tFpPu6uu3Ie`iat8~3u81;%~hE31poj5 M07*qoM6N<$f^dBExBvhE literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/resize.png b/app/examples/Image/PhotoTouch/resize.png new file mode 100644 index 0000000000000000000000000000000000000000..b822742094700595336daa8127a20b5d464e0e32 GIT binary patch literal 852 zcmV-a1FQUrP)dkwWQ{xR|oI^hkkMzC>n?i97Z|PB`-U8ITtdc`Jv&2BK7J8uM_g z;XYVW&H_1(Z-GJP5lxB~r)lDp>xdIU?zlM_kX%9YFINV=!*C0E>9pr0N4tCiVcrFJ zVY?X_WW&hN$9d`=L~M>a?XO1U0lv7+V>Xu4-t_5ONI~Fb3J(n6$+T^Ubu40<8H#;l z54fl#8zBHij8Q`kV?GF7bSRMz0Z7OLR5Q+z9kD3wisS_HfzhZh9F^QnWIFpeBd*%LLIMCyz1pc z-hx#w_#hYrFoh6W2~8l_;o(&-v6M*wxJjv2fb8&am%^fZ0D!2{qPuT^ZpB4+p8&ne zi>|-kk2r~X;1yA4QI*9(s+lq6?swXKDL% z`pPQ;NN*iP10qHM+UX9A831UbCnTyJpq0MRxYpNDXh|cl8DNQlw0*P!lriP5e`Epx z&ZPXaHVlwJC=Q7mmX-9Zu03nyh-dvAbQqDZNNg$rENS-F~bxzXkm0u?UNN+}V z4Mju}-)mAJ?=m7kEBV`*kP>-gT>gD>>vlT}{jy<9{EtMPySF?LiS7F`j%(b$|IQ*~ eJhYOlpML?dnOYZ)U)^p100006o!#P=62mOY;oOkPRWE(YKUKl&k>(|{{nh&6#eMO5U3L(?f=w86jZgg+pIByhZMkiUJ;E)*=h{;6QDZ@ccIob zlW4(jR&&U@aHUY5=A%}<_ZtDd<*-wj9q7Bey$85l=>EaF_hClhIoHUdGxF{Q3Sb;< zu6+PUBNq_73f7>?HTM{Wu0azLxF2y;qXKcrq;BM|?R9-KVM3S9v!M61OC_?@~hBfl^q2tjk; zA=em3ztZtlY%N}K86z0Q4U8a-G5myX9CxHt6eM82Yn(+^;Wh_vcyA2j0D5r<15QeF zuzeoEK6l){RSy5B@(?Gvk>#PVl7K!p>RSqY9d8Q!InFg91nq=%u5nS})P{S+#b)R9 z9TAXJIIR)pU-;StA0U8L$UnP%X?#ekoO?aS~kyi;@Zc z>YqUpwn^wsDn~g#0-S;#%=35jAAx{fw-Y6J&A(cx+oHm&1O%q`4nC|S;AwS~5CTfk zqN;RDemNh(U=c@u0ewCaR*BPG6M~EQ*mQ{xhB|&s{5s`{xLrrU9^rqAbs_N^nUe*q z)e$g@qoPF*%R}Iw@qS3zr-26mZjxJd(Xqs@m0wTDbDX0o00n!>4lDd}rsW`4xKJAp z(DA<9wHxD?@LxH=Fc%r%0f1%vQM4cDY9su7CT096n+@>*z*_z(VvKN+Hok#>%lMmI zZGi^>Hu8dq^AGE_@H03d<$srMhn(BzZ< SSoQ$`0000UB7qPD zA2b*nHA;<9QGBW<+6OA&M^cPnYeK5UTCvdDE%x*815D@6vO9ZccDkNE;B->2UgD zBhhkQPsk z0dTK6y{wURh$jmOfY4&?(Ub`=se1|vfY1h|OoX#qT3`UQT7xD+d%*$F?HcpCnbF-g zx`_zOC98eMQ(nViXTe7Rp$>Bz?keyI0BmQ*Ydrct0Q^XY*Vv?Fu6VB&q};v05&9S= z#lH+Q#_tSpl5-&tnt9J_G&-MKrs-Hvsq0ajnpA4F3Q9EWCGN83)^oWl?uhoQ#sc9} zFXgyBzyiA+!H8ON0&Fu&Q<*(NwT{{#NU!94AsM#DauEV3!CD%*mLv-)BgrDlsK}JI zg?#~?7L%gbZESYlvL1ts<5H?S)vEV(&iq+pIo3k27rG-ac>`p=T6EUy_dp=PVK4Bb zm;eA&>lM5US{wkd*8}JZ?laVz(CKBX>nC|QAacNSt6~HI4$|+s>jJ_CJ-0Ge0B4#{ zT))8%phSz}90BNbohq)*8uYi>D32H5gy*iz8Z>S;<`H1w0eX?^)?^KuFdOq@1#rf_ z@vXK42poaHPdxzDvXMF#X8J`}X3g?V!7jPmi3tYzjIW&;>`V#m3@S=Hvc8Cx2Bm$g zYFmKa!BufjR)C7#;}7O)0%6I+4-vv2q&f)i`mW&yzZTq`6p0BmrbAc-4YXNWH| z2K*2J@L`6#E2zJ$?*P+_-lT2M>tp5ORcBW7Q#Jtid+{@RJWu&0<|ajY+X4V|$lP6S z%tJn@PE&qV*VzFqGmnDCwJxH3LI?eiVT+YKfNs%{mzUC-NB}SStB@8&knws#lmrS30e0VwML`g?{oh%u=0JbVqdS9LNJ`iB6i&SW~8= zJFc~JBoCk(oi?@ZQ$p?jwg1>0%0qgo9@YWBe7K?;5ux6Ha%5Qeq8kyxSE^?%TG5S& z;49UV@+rCz5jL5Bl&D+F3MvnvReD{g^sA2PnGi>_0Q?77-u`9h26vuzlPFp2JGLg(AqDYnaim!$EXxbnqNQ597($uPnNQ7X+!b%8>CM-rQm<5rj zZwp1V8bLx-L}F4+szJ~M@!6^3SWIW$ee?D1n|b4P;eQwJp7Y+h=l|}x@7{av^9Tu) zvWOtdnMtnr&OO>_;~@juHWDkP5|(e`6nbT@pjTp-Vrh2Fc?_SWTAzSNPC4veMC6C8 z&?RuR0PaOZe(D6G2tV6$9(37J!dmNxyws@T0L|RwPmUgaG%!O|1leo-6@-jFi%Ny= z;LTW?)FAwkG1g-sVmyB)Z}&)r{8A}wvK|AIjoxT?An-kOsp53&F`$NH@LI-a+m4U2 zJE;`_JhliJR%Nycl-Rb2p(sZ?$MBzk>jb>{H<1-fXi_BMIc;?QO9Jzlm!f;i9S-w2 zCb$uT02^tg+C2gQqqxorHwi#Fmsp=+3<&RSxA(<9g%S>NE-it6{CT`uglL94MNBon z@dPFn;44^@{1oMe>?`>vWFL{VtJMxC}%BWOq%bU$eV(2k^_cVlZC)NW+iDN z2?DEFWZU&VegW`NRgTC=U^26S7A__<$CSHE*>0P_{y_p}WF$}yKnKSYnkL&Wb>RAH z_@BTKAfW7`i(Mh`*br8_K)_22bK^pgDMSYAeE`g+QF*i=kZLuR>sHd!j+BJ|dHNJo z6T=2=8%Gd6#6)w(JagZ+pd0Q2mvxm zP0>X}dStVdIgqNSFXj+<1fYsL$(x`Lyh-4j^J4Ug9ZctnelO&Y)cLiO)y!uOV;K&>!Zhh+K?wjnQ0AF~g(WZq z_5(wnK(A5t7ho!~CUC>3j;7890?&A32>Yo>)H?v^OO$n4f{FEtrTt2>8-~4xxu_xcjtMie@1OAqGREe_GAiGc^v0Q)uuuoK?8msGFDam z<{DSiCY#w%#4^^IkG1Rc2tXyRW(p1=pgR$DQOj$`-5=wfo$yu4D@T6k#c@hJqV*LR zWun}4%zd*I$9`JbEvu6E&^h3ngrzjrp6(!2;bklJj7!Q|^zf7xu5&k2lHdaV0-sT8 U4^P$zJOBUy07*qoM6N<$f`Zr282|tP literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/save.png b/app/examples/Image/PhotoTouch/save.png new file mode 100644 index 0000000000000000000000000000000000000000..122f75e862ef0915c988c450c71df14ce6dc0574 GIT binary patch literal 674 zcmV;T0$u%yP)P1=ZJd;rl1mA9+*T$-wE98e%|egHzIvY13<3(xeyB=hp3kN zTVMe!fCb0^_$!>~Z?9f}b>Yn5cq#xNgp;Asvj_bEpv$)j$v^WI`%lW{d;;>Xgx>$tb(!l%<=@Z=2LK)3 z=Z<#aeR_P%i&CC&LjFYMFoAO#r^r9CieJ4!C++EZ>c5Eo0mQyS|JW=jdH?_b07*qo IM6N<$f*6%9ga7~l literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/scissors.png b/app/examples/Image/PhotoTouch/scissors.png new file mode 100644 index 0000000000000000000000000000000000000000..de748cfbef2a7dc2d990364465bb49ea257747eb GIT binary patch literal 1549 zcmV+o2J-odP)rM5f-n?^CBlmGz?Mgk;CurUF_A{d{E`avO9q6mB#P-95Y2ofVA zrUVl|L=vzGzJ;g_5JdsQL&c&kv_NQ~t(3NR`O%rP_nA3o=FF{$dp>6FnYH#>_x7B9 z9wqvpBNYcgIW45o)Iq!H+f>>uDYEDus-(fxKp&-k?}t0&U?Vv8A>WBrfc=8C5jmO4 zXJZHSs7GNes}S`8)`!T>%)r|S$GfqtEe6)+OynPjDbkn~$(jXoJeE{`B-)sLQKmYI zfmYT)9J8=mQIgC+Epvatjrdt6TV-XfX3$}j;HYG6Ng`i?UZ2;3kswS&52xxxxsg?l ze|$(c?gU{J&NHhAQugg}q{%*gtj0h>J563MD{l^=q3pR?afM99Y(nQU>0 zCA3Z>Pw_-aXvbhT1B;n6S0y4WkOt0SsKyzJW`6mgU{y-iA~yr7{4^s+q#}GGlW7$} zj{cn*X8syh1g`_7&7=Y@fo9M`OP~5fL`~hC49C_?-x!hQ9wE3$lQ_2=Jzxfw|0B zZb>@|mo@oTlv+AIIhQUzb3h z2!n8vx$jsc3eki{6k6VD%i~iHB`9EFCeLudQj5p}ZSI%Xaw(t2%!KkpMA*a`dN9u- zWWE;483TAL19>9k%BimhPddv#rOO{cZbk-(Fa~EiPcK#?6)T>?OS*gu$}*HEBEkf@ zK>_wyyPV%G14a4@Cig?0hzPTJb_9S!xY;USh97nLj*L2Az(6V1%6bBHqT2Y=SL;rJ zYgmWU{)x}gcKRYd2pOn_CHCRI%*2ba3)Xlwy6OjGm%~ylAU_FB_Ixd>D=xzl@2Lna zQ5G68A&LBCo{UWKj3+u6m(i#;FV7+}+EC#tUm{080Gz~L>{XM)5oCLkeG6`X@t&i$ z<2qi&Uyk!3c1zZ7j8}i>3hV(eLw()~0C-P*o?*nxzThG+7G%As4%EIB9#+r(!s)!& zK~{zE%k{c=f4qT%jIOcF^L=-A>hB*4jQFL}Kz*G2M8?*s6j`_n(?avWR)q|1VuScP zX<<^df%QHvjaxkW2=9dGFh(c=LY5eI&0dQQKUOX+)<+vqr%_+2zI{M(4~oC`6s>ph zs?d!8f}uY}$q(Tk_RhtJy*;IG;ZT;xapX!p1V_%-l0Bm4!sfd+o` z|9W*Apf2z>Bm8Ny2O9m+vdtSn#p2Za5{8AUU}Ag&MFk&HwyVlsiE99WYp7If-zy;l z&iLrVN-Sg~FT3u*25i8%nB|uveqOYJTt4&d!ery@Rv{P3XA|O^Fo2>1D;a%GO-Ou= z)fbC3N%0gH$ir`pZ^I*ET20|meWP>=gOcKddWN%GOsB6&oaUUMkLeF0Dx>*ys|5Da z?9?~O@eYAyaEY2s@h%ot%Qb`{(s-dicoz#*XtkzmMOAhbQ_M2l8sVJ(62H00000NkvXXu0mjfq_)>v literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/sharpen.png b/app/examples/Image/PhotoTouch/sharpen.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6f67e7375afc3fe4c80c47865be5d91ef85c2b GIT binary patch literal 1301 zcmV+w1?u{VP)Xv59?(t!>?fl(4bGN(kyAONserL`( zvvKZwnF8cVa++=PH4x!of#Uhtt8zvH zlv@N}V}mJi9m7ZRX*f0nGZ5vN%y5~j0*u{t{6{a!1wjlPR^{sg zR&R@LVOK5{a+r*k;Q~T)mor#_GYTY(`>_%$QHvx_;v_CPBCIL z@QjEbky7tC$dg&N3T?vtS57yTSxPW2UJME6x1z&o1`FYUaFtODIBql0EM6p}M7fgz zpcc1#Z~$nw8Hjorc*Id&#VQ7(HUm9>@u9lCb4GF9WW!(#ybp{ioc^M%J*A zPw_zo+|1t%@+YM60)l%~NC!REgrBO&v|))U9t-&vzv3Sx$qdi2pD(dY!K_k${&enc z>TErF@1M4=+j7TkT*1Z0OdT()w0*ghgZ#6O^4`aCU4AsbWEOED9#C;N`Mf7>tCM(` zJN4b13|sO5QU0JV|AG(o2Ry12rGC^hVJeW$ImjILpl)-MFZpJYlkCz)d;X|rKW~`i z2gR+s{7Hu{|B9t|&&?u#W3n7Bgfgl)^F2XE?M_91O}UoeEf z9ae0-AX>h|(*OOYejc*kHJ=0W2HbtV=zS*nFSMCApn+kL?Fk=jR|(_^Wufw;C*Q{< zkte_hWX|nI+=nHS&pio$5?RxHOhfH1nS9Q`yE1c{+Xe1$Nrs+mpo-%v{|T<)yA9-;}_K}nPKyl^eRq^}Q7jhlN6;};_kE?G+gt%h) zdsGwyV3cq3tR_Cq9xf?`yw@9(2E2^bSc*v`kwg+_G2yr!5;*9j;6vzL!Mf$Wp>N7T?L9jW-n>u_xI~Z$cQ-xo~A5>Wnr=|kGnC&vP zNgfkwDBuZ zFXImx!D~l=g}94osfnN*H;mvlBfvGNmGS$G;k6>bBGh2C!0$n|*VF=51BjpvZTNyF zJhQqJR>)%h5^3JgQo_}$>1n;Z$3q;G2!1_(%lMr!cwYhktb?bS86SQldu4YvEAScu zNV1Vd!SP$1_>D@urT{X+N)^R33Ar6NG0GkdU@vMtW2AHIx##LQn@X8;d^Me=PMyz#m05%YSu)({>9ugWnf z@Dflj=5%toK|DBJUfHl39}>CW#nndfpu@X%Wg>Egm|-&bcH1d0!d#mwEBPb3+6Sco z_6QV%d{qQ8*e8~tFbOgEBqDH8;i}b(2X;RTcTs3O1}{Z-jyl#y5k0|Vp$=;e3-~O$ z6O{t2kEjrt5JHV%37w)l-g6QwoVC#8emEj-+~K2>`(+6x({ZcQ-ZlDluCtQ znAJ*-1eE}fBbgN@aM@1L5N=}=9(#yA8Qrk`=s`E$BaAi^Ijfrsv4_$&=4wa4r{-E= z9K@hK3R0RT;f%JMzD3_HLBBMuG9$n(A%xqSQJ8E>K)g}V|Ng1*+;vC6r$7(4yWn5xz#OIjCGsTwQW;c26{mPSw5p`@ z5CeRn4Itr_&&zC%{LXLUGb2mbDZ?9C#x$S9NR>Rf*nj|lS;DDWj`2MU9hC8}nb1#V zLcjS!x6i0Ade5;ZzTJ9z zvB+vQ7l927W=tpNBYE;ft@7~&mG{C!R{jt2l*Ea&@S;7_^DYwMZWk>GqR p#{&7tGi2R(i-%TYQhYaKe*q^8PQ?h+p4|Wd002ovPDHLkV1j`T>&5^8 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/usb.png b/app/examples/Image/PhotoTouch/usb.png new file mode 100644 index 0000000000000000000000000000000000000000..848aa05735c945e4a7e7555154b08584524772b1 GIT binary patch literal 739 zcmV<90v!E`P)^hwihNcRqHW-()AV?+y?vR;*a-T~<&d zJJ5?1wknPE70<1LAu3wj&+Giaa9ZXacA2oB;LDKKQg68%4@iJ(vcAm8kmi~IV7k=VXp~5PPqX0Wz9J)doZQg zFTvyfwf-0YlPrt!Gdh|8VXi9@;EMy`Bpk68&ZDBe0Ssf8La!`7j#Q##gr4jeda>au+os2Sc*(z~I0cFFK3iKmf0G3GWR8-1VaIKmZ$qBAf>e0;tM{ z_x^s}0(-zgrwG3p9eBozM(O}~@Be~PfQ({48;QmMsLGZ(Q7e`pV9SX|&BgTOpn&ae z*lBd&tQU=7XB~is`~u@%G!wWWZF~~pUZVi2vauk-A#7*?fP<$Z+=4v@cf+Gz6r3eE z{_G%)X=4Cc#eTMxM@|$@!!`z>DhI$VTyP707{e3op|1M&|2nglApx+5DB_DEk z;{i$>^{vd>&+`iBIK_S@8U^s3him>EJjkEAk20i|c!^2Zwfcx=6tf17Xwvu**rSLS z*2@3xZu%kBS`ZU_rEMSenC>5fdr-L;(p>AHGWe)=GkVdZm?qHRQEjYPv0|+&{RNwB V{H;l`n7{x4002ovPDHLkV1g&0TFn3e literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/vflip.png b/app/examples/Image/PhotoTouch/vflip.png new file mode 100644 index 0000000000000000000000000000000000000000..63d2ad2c276b38b178b1a0031e518f5ddb72d66e GIT binary patch literal 1018 zcmV_wC#L zz2EOiiGu<|Ofkg}oyk>_!t)GZ#tHIsiC@fb_~QbL=L)}!F~Z^xmdyozH4{{e307r| zf1gQ{ILxqZVUpEZ;yai!X+N`ooNqNUMMqZntxTJwBX|hmam2PT&DJdN+bNoq9%NiS zwuL8bt2O>JW-9t64qA924pK5L%uuK$zS~rPL014TL>Dhi3njX1h3_?WpYcg3UWiY5 zW`7mE>ERyVn~IOwQWY=679QIczQap{&vC+3dqDfFcp=((U|Z-XmlWT?8B^&lYa{VO ztmUq4;S3E)@VEyPb$v@mWeTxGd4UWnyfwJpR;RJ8DyIeL^OvG78? z!>DaxoEBGntDSIuVNqjcR>J#II zsOPwC;frVkS1RxBE9bj3mq(vz;CDYS@IS@0n*M+Fr@j>-7Dsibiuo1#FyyJHLt6j( zX4-3xnt|&?L}%4c+Zi^kO#jpnUDB9()$W~6p%Hfxy6V=szA>wqH!yEt-ax1U)^U{EW-;deyMCi# z#tgSP$_K7PciClV&9Y&UoencVn+DXF$!s_3ByG`O!C@`T1TRSobtu}vN15S6)o0m~ zUDP+BTe5NeTSfg{tqXbT>1%CzedDktyIff{{p3<_N^|s^3-MCq9NF9QAsSNQef6!= zw%k{k{7Q3Td@~o~7bd~d>&|Em0Yv&?!~DT><-+dEf&6w zn>K!!rWklhBF~_yaE}en_zm2%@q>w)`7GckQ{^F>obVs>(8eEkT6&=te3fO9?UDE$ z{!G@Fu21G`)6Gzr6<@G(c-l5y*~9@}RTN*cw<>-gFHQI>_NRuIE$rcWMYqJ4q4*xV zTl}2eHNs2&W;OKsS{I((&#iT{T9VkzBa?89x;OYbez5V6*j!`0B(ahECS{?4AMloIr1i$6#iy-bVmq5g(sATtK-9nw1u7;g=nq*;K#dYL z1+A$|j7yYi)KH{oA?%bO1#F;F*4om(E}uW_%sqGB%)EK;wP*gEd+xdC`|iGsfk47^ z6yh$7$ITdO8?WLZ4qy*<8V$jsBqYq>JXSJ7KRL{m9M&7)gZL^hGnjO;g17Y~cp6Lj zZ$NNMx>&*CJp*6F<8gvp(##b!gCiL(;3;?$OQLgHP>mxvhRbL{2m_FVu_(k~x2V5j zj!~az-81;Lvr?D&E{m8-xP19s$=c|4IK%wJ;0N$GJyIQ)GSd}WeFh)k&rS*#cxU|Z z{a7tyTw^KIVt}i!geRS&VN!hXA$}{%?_y41@c+LwuGcA?0W zVivQ2*SnHWkBTK?tl-B@Q!78{sQ>E#~U;Ij_?wY?PUM*(Vl!n5}B)=4r#~ ziEyIg=-OfzKYy@A>B51%dJC%oh=xZfTV64&MUH=TV zGY8O7NjOl`xMj&jdCv4*wwS*)j>M-<| zG>6@MO=MZ>727x@DR^1_qBQq&Cy;5W&9*TU$%$Pj5z{NIX-`@)@7s`^sMRD*nq9-1 zXo}{2AxQ$Nyw>VXZoVciS*p6y98Z#fDz9CWTXV=GgFOZKC6^0dPmE-or`#`yUaZ< z`9-{{Q&>;Ki$b1r07tlu)5N9I`Q`F8t>MdJrAwtl1~tt&qx1YHf^Pp7o$GnsvJ43x4aut*lCvKo_6#pl~Di zO2-xs_M;Hud>)Mhw@s;?ob5SRw71~r;4d^eljUrQS>-A&W)A;WrdCe%ppYdy;(G)U z_Tvn$=N{)=VuZ(7#wVEF?UyglAWfX;K|#C;o;Ct8P*lPgf-Lj}a0yK~X|y}?3s9+U zBNuTm4!T5{j}I+1f}R!@$z`wJb*mCDsjbN)MpGz}i~k9RqX@hwvU`C5z7Oidoio5EW$m#*Hc zKLF6+R$ut7y$TNi?8EKe@J#L{EO0Bh{qH#M_XnZpNepEv_jmt)Z{{ytLiHMi{{T#$ VPtIeME!O}5002ovPDHLkV1k6y^sxW{ literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/zoom-in.png b/app/examples/Image/PhotoTouch/zoom-in.png new file mode 100644 index 0000000000000000000000000000000000000000..1e8ed3aae01a38a66f880b6bbe4e113b221b69e6 GIT binary patch literal 1634 zcmV-o2A%ndP)OyqxoI-*?t~&KU!~gnf{U z@fd^KktURlsKS0!V5d>A>@Dlw=Crf$v^#o61A^-CU zZb}zRIrRF#=kZ9C;HF&R5(fMu84lnHcoz%omS*h1KRAp_XhsnIk%5uO#UQ7w+cDEP z6>Hm*`J=T_m-rF$m`FJMvbmJi_I{{kc5LwdxkJxX!-Y(7gqA;pkMa*Ih4UO2J$zs8 zQFB~nA(JA2%U=OcSZBk8=-`9=MUCIZ4Bz10za*~KDV*W3Xb7xQV}D>D2Z&^5GoSg) zXAW<2B=I6U)inE=>=%BL8d=PsC2-g zXNd(`l}bKtp)gP1oF4K+V7ZJq$W(2HFX7dQyU%yLL8Gu%HST3EcVq(q7{YNRb}O2Z zgM-4Hgc3X{YIWG91*c)WC|6+$YD6soJCG|K=Nd)c?SDtoeO+lsBDZSo++^wMshYK( zS&D76`T;%4pcj0y{NmLp>)zldOGkSXAgeh*v2IXJQ@jW~EB&5V5-hfjbR+^~83V$a z#ujPV;zi(h)2l&gf2JDln*cdSv41JmtsLNt9$*T@GATxjFf27^Wt6f^iTgyV6Oivh z029#Ltb8lfX_yfcJdlC8qP!hvr6JFafcR!LDT{0tyr!+U%n_w7lu5PAw=sxOX7O)y z2z3Df`?}v{39iFtt=_#aGv&01N#tSZxT+30`38_`7B36s;JDz`_zSAKuQP!Zvv@@)QxKnsHYqvX z(gx6K7UlYFMSLQ6lO$E`m^OeWJMZ&x5|Hs)uQxgQ25`|V$}7#0I0?vj?V9A+t{@I$ zWOp%B@vKHBfFYQwIeVQ40J+zc3ysW)4rQ6CB9Ee{mDAmN$8(RLkCAJhqt3k*K&4q6 zj4Yupj$;5ef}*T^04klm1K1}0<*A_@J7a`zgFFTgH%Au$wz?27&S9Tf2`h#bF%tL; zwW1tWZ@AlBL-?8;sL9F*Z;oLO)N`P)rm{gA%H7Zd0E*FRR+lKIFe;*KeKQ_IgQz`+ z8>Q(h&p>`JsXnFMhU%m0fv{q`nHQv?omph!vDX}~lehzo!aM|9aF3|%#(P@u zI4l#3RKDkbWH~6hY%FvEw$Q#dBzuReGuZmD%}s5 z5apQ=oW>0{U6}1^+Ac11ABGY7y4%AsQGoa7ha?*;Zwnw;V7=fMx>K0I%c^HP*D>7( zcpuK^nQJlkJJ|Ess8d)?!;Qjd>jZA&Mn0lkI<3DfzO8lKDo3xeJg28Ea=o(iHdfl_ zdp%1ypLq;1nP89uIhM1ymIrK}hg^t`vbOAN_g+ZkI(9K;dD(@+2-fJ1uX^nZcdY>H4{1di+>!6qczt)m+5;IFT#$zadXC zwzoi^6yA@xb@g2P0e}XJJmHJt$B0Cs7famX8Q)8o?_O~FzvCR|#Gl(gi8K~+-?gX5 gEBuoSC|`r{KS$aeF1a)NJOBUy07*qoM6N<$g6W0+ZU6uP literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/zoom-original.png b/app/examples/Image/PhotoTouch/zoom-original.png new file mode 100644 index 0000000000000000000000000000000000000000..61f9dec6378c622b7e0af94f7378ea00b51410a5 GIT binary patch literal 1615 zcmV-V2C(^wP)p1>SXHEcOzj@{T%L{++>}PHq0c*#;Q-zTZ(~JhPZJK|Br0(gO^8E(q~Uht zV2D%JpRmBF?dsSQ`K5hOSNR@u8BaL0EUsl$=yN#FtggZL=g)ek8m?rLBeZG;ALI{q z3YU3L^zeOoNanb~0{SC>tCq)Vdo@gs4nB@w%lLy#^9=rf^RrN=aFHXUA+SNle!&C> zh(u;_CTDUcvpL9-#B*$yX)2iL6@G?{+{HLs;xxX>GPY?0tmZaOqha%0pnGld2A{-p zDyEpdw3MT{#|AB>oDbV5EY&|w)4dQ_ry@=;S*!3hyba-SlB0yGgfau>QyXJ%=uvqy$EfTzFS-lbrF^D}$9I#?5{Ur$tmWZjoicBA#`nQmvw}EinHM5l*@hQ2=Q>cL%r0xA z4`WUBH?&#iye`87*<$0YhJ*~~eUcAjO!cH?^yBUr2mopoKB0^Wf|o`hV5(;)`r zL_lqZ{*vZ^ldliSrh3gX4vh(3#y3cFUuObIrrKy3lMtI&V-h*t()!S1s_~Yw1+hu9 zrTL}VF|7~5P~MkgB%tE8ryg|j_2G)Csw2(m7zwC&?U>{^E*~mQHC=dC#YCXg(o{~g ziD~9cAV(aA_QwE!0mm(Uk}{t)TAc}$o9a+xSms?Z5ZDb`dag2;ySQ@@JIwWBwO9>* z5zEVQjjQNM@Qy~aEBnXs8Jd)H{zT1WM4Y^2;awo7_ zdCeB#pLT&j9S2&rWY(+1rEnJp)TV#GNSa_B6ulB?1v|!(;f& z>P$l!1_KD9EJEQ;fgiwP?6fR{aSFGZKiV+Ps0mmsb@|&0x9xBYL%AE;5_@uUz1jDVm~b+iR*bj)G^2h z!cllddA9R$Tao#>iboy5|KS#nbScbC7Vwzv-okl~MF;@&;Tz1heqkNfg^*0eRE)+j z^urC*p$3Ohj&_^R0DTjJ0=zDNwqdEU6VAc_FG#xYvQNb6XK!=4EyR|uZ}B_0()~4z zksEpP5XVOW-k)1Z*4yqDKrqjKz%Ov8Fqzk+=S^;5iU;rnF5|_{m?;kZxxB7ZD5T*= zA=6&Kt=!F7;?QYt8GK87a2p)GCi0S=c8BYcougPDTJLq-!)456fQba-IFJ*$kehit z#PgI3(XsZCeeC`e;<-hxq|wUrE)>RyRg)z7Qn>HJohJA($$+9-s6G z0e~xHt^O)*pHuXG#k|wiPmGCp=BcM9)B{B-r)_yK?hcZ7xC z(WCGHz+T+r4$s(b!aP3(r~f-HMf?Y$TS*LO0graxNH_9FuAsUN!hcM%A>ivStNj1~ N002ovPDHLkV1k#a2Y>(o literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/zoom-out.png b/app/examples/Image/PhotoTouch/zoom-out.png new file mode 100644 index 0000000000000000000000000000000000000000..19f24bb0d7c265e6b9311545d5db9f279b825f9e GIT binary patch literal 1568 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, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FConsole.class:122 +msgid "Console" +msgstr "" diff --git a/app/examples/Misc/Console/.lang/ru.po b/app/examples/Misc/Console/.lang/ru.po new file mode 100644 index 00000000..7d03070b --- /dev/null +++ b/app/examples/Misc/Console/.lang/ru.po @@ -0,0 +1,46 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/Console/.project:18 +msgid "Console example" +msgstr "Пример консоли" + +#: app/examples/Misc/Console/.src/FConsole.form:5 +msgid "Console" +msgstr "Консоль" + +#: app/examples/Misc/Console/.src/FConsole.form:18 +msgid "^C" +msgstr "^C" + +#: app/examples/Misc/Console/.src/FConsole.form:23 +msgid "^D" +msgstr "^D" + +#: app/examples/Misc/Console/.src/FConsole.form:28 +msgid "^Z" +msgstr "^Z" + diff --git a/app/examples/Misc/Console/.project b/app/examples/Misc/Console/.project new file mode 100644 index 00000000..f34360d0 --- /dev/null +++ b/app/examples/Misc/Console/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Title=Console example +Startup=FConsole +Icon=terminal.png +Version=3.13.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Misc/Console/.src/FConsole.class b/app/examples/Misc/Console/.src/FConsole.class new file mode 100644 index 00000000..d9a9947f --- /dev/null +++ b/app/examples/Misc/Console/.src/FConsole.class @@ -0,0 +1,162 @@ +' Gambas class file + + +Private $hProcess As Process +Private $sText As String + +Static Public Sub Main() + + Dim hForm As Form + + hForm = New FConsole + hForm.Show + +End + + +Public Sub _new() + + $hProcess = Exec ["bash", "--noediting"] For Input Output As "Process" + +End + + +Public Sub Form_Close() + + $hProcess.Kill + +End + + +Public Sub Process_Read() + + Dim sStr As String + + 'Debug Eof(Last);; Lof(Last);; + 'While Not sStr + Read #$hProcess, sStr, -256 + 'Wend + 'Error sStr + $sText = $sText & sStr + 'Debug Quote(sStr) + UpdateConsole + +End + + +Public Sub Process_Error(sStr As String) + + $sText = $sText & sStr + UpdateConsole + +End + +Private Sub UpdateConsole() + + Dim iPos As Integer + Dim sStr As String + + While Len($sText) + + iPos = InStr($sText, "\n") + If iPos = 0 Then iPos = Len($sText) + + sStr = Normalize(Left$($sText, iPos)) + 'Debug sStr + $sText = Mid$($sText, iPos + 1) + + txtConsole.Pos = txtConsole.Length + txtConsole.Insert(sStr) + + Wend + +End + + + +Public Sub Process_Kill() + + 'hProcess = NULL + Try Me.Close + +End + + + +Public Sub txtCommand_Activate() + + Dim sLig As String + + sLig = txtCommand.Text & gb.NewLine + + 'txtConsole.Pos = txtConsole.Length + 'txtConsole.Insert("# " & sLig) + txtCommand.Clear + + sLig = Conv$(sLig, Desktop.Charset, System.Charset) + + Print #$hProcess, sLig; + +End + + +Static Private Function Normalize(sStr As String) As String + + Dim sNorm As String + Dim iInd As Integer + Dim iCar As Integer + Dim bEsc As Boolean + + ' For iInd = 1 To Len(sStr) + ' + ' iCar = Asc(sStr, iInd) + ' + ' If iCar = 27 Then + ' bEsc = True + ' Continue + ' Endif + ' + ' If bEsc Then + ' If iCar < 32 Then bEsc = False + ' Continue + ' Endif + ' + ' If iCar < 32 And iCar <> 10 Then iCar = 32 + ' + ' sNorm = sNorm & Chr$(iCar) + ' + ' Next + + sNorm = sStr + + If System.Charset = Desktop.Charset Then + Return sNorm + Else + Return Conv$(sNorm, System.Charset, Desktop.Charset) + Endif + +End + +Public Sub Form_Open() + + txtCommand.SetFocus + +End + +Public Sub btnCtrlC_Click() + + Print #$hProcess, Chr$(3); + +End + +Public Sub btnCtrlD_Click() + + Print #$hProcess, Chr$(4); + +End + +Public Sub btnCtrlZ_Click() + + Print #$hProcess, Chr$(26); + +End diff --git a/app/examples/Misc/Console/.src/FConsole.form b/app/examples/Misc/Console/.src/FConsole.form new file mode 100644 index 00000000..6f20aa1a --- /dev/null +++ b/app/examples/Misc/Console/.src/FConsole.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(38.4286,16.1429,99,50) + Text = ("Console") + Icon = Picture["terminal.png"] + Arrangement = Arrange.Vertical + { HBox1 HBox + MoveScaled(1,1,85,4) + { txtCommand TextBox + MoveScaled(0,0,23,4) + Font = Font["Monospace,10"] + Expand = True + } + { btnCtrlC ToolButton + MoveScaled(31,0,4,4) + Font = Font["Bold"] + Text = ("^C") + } + { btnCtrlD ToolButton + MoveScaled(36,0,4,4) + Font = Font["Bold"] + Text = ("^D") + } + { btnCtrlZ ToolButton + MoveScaled(41,0,4,4) + Font = Font["Bold"] + Text = ("^Z") + } + } + { txtConsole TextArea + MoveScaled(1,7,47,37) + Font = Font["Monospace,10"] + Expand = True + ReadOnly = True + } +} diff --git a/app/examples/Misc/Console/terminal.png b/app/examples/Misc/Console/terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..3c032bff6147b835d505cd4cc3d3502e2b5b5124 GIT binary patch literal 2312 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⁢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%0D2oB{^m)z z2MPxb0Bt)m5CUY#55UU?r_FK1;|cl|b2$^FM4Q|9$_1niky%d=OhK9wXYI|8!QIM& z&ItLDEe`&_iC<#ycKrw48fc4A=kqTba7r$YfBZRwHF%%1BmD*dbO1_&fFjXHsb_>N z&5#!i)e#L14IsJ3;y!qkiCn7%5C(T{@4Y8A`TveSkQ$TC@>pfUFg`^RZ8uER&#SY z1pyF;FcEV(1#VklSXfxXB4|LXL+tGAp4e`h!af(z05+CDf%8ZUz6%^$WN-#VRabk` zIDcrD^nwP2bnij%kp>99p!Aa2!q$mj#Vh2zI z1@wX8+)xaPSUMEHht3K(```@bf(jomgFrImtQTc@nbaH|9YIU9OMevwYXuOXoRB9O zi1p^po3GRU5?LnBT?U#yAX|()@u15CcNDH)vK~z3M-8xLXR0i9jiEda%2Cnjec&}YIXP+(k#qp3$A?{1eHD3^G=NY5TQt;R^Wo>jFAS9XH>(_SM(e`y z%#Q)KegleWE(mu4aFdjj4Bty#+1e_s34QVtn1F{zN29M2!@`~bW%KA92=U0UY#sTlmEab)t5SWpbHT($?d-tL_)T&=icX-r<8v63cXJBE#8LzPtE8$DyPWER z>>)VOT|jh70!o2Go<^Ve1Vv(0)H877-MzhLe-Ut(ok=+Gk2NaI<*U{zUBZ>OdZLsX zuEto2fD{VoBH+Q?9*ZdTO-*+3a5x-rE$y@E4P>9YyZHMQ_}p?}D?Lv(qkZ;Pvst`> zSPj0$4)uY+#@xY5$65ges)VVAiRvBjUkX7cYjJT=Tm^d(gd+uD>*2y^Z6NdrghUXS zBt>!|pqF*8fnt!7fa=H_yc-_VUrsiDY5|w z&JgyDaT>Y;JZ!qwA@uK~K~xH+cYhAzuUMBIv-%`uZt81MtvLko6@1 z8z}8`2i!To%Qd2zS0KIO0))fW6HvRzfP2*{HYO&GfIu?1994irDFPg9&;cHTK?7*d z$k^s;$OyQPwf{%in}Fr`c5UC6G2`D%#tbDC5uv2alqeyDGG!*pRHDo?X%r2jA|xVH zk-1SQWUO!%2`P!v@ctJ6=Y601ec$i}z>u9A7C%vrX z>l>{dRw6QPw4QzDjCDj9>mL_V4gFCIVM(Kl3knL%F~{MN35J#{8k<0Al1-s_3Y_dt02<{F#&L<<%U-Yfenw)UHtlpY!9Bx|Y@+{`B$rxucuXJq~mqTESI& zf#oKiw(6+>^4vp>b@NYDB2y@1aY6SbDrx)`841+x>Lw*gI@2$iCHL;n>S&8J-(bAW zK03e56j%lT0M*?{pmUck8%N077dSwHOLw-k2i&{I8Wo?LvaWxw>ixTSvp;+o&ALfU zNN~^c!xHC{V;jBLyLZ!1#%1dXmL$gD8FYQ$rC$n`#{f5GN$Hn$4zQJ@y9;Jc+u@vUy9W zPd^d|GLhs=32H(81PmB)e$KslU7Uc?^W$cHf-*-DIb`f=1m2Y$$+ctRXrNS`{P^)> zYQ?u7KEz{-S59`q1!zVh;M@17OdJwE$&%XQ#q)lXtU@jOEJ{&qMp@Pu7m8S1i0dEu+9~bN`gV78 z1!rq>PedzY^VWul@ci%q#3!ynsSSEgZ#jDOXeWo0`rFAJxsd-@wwv+IP>B`+FP6Q$ z_!dZns3(OuE(JjfNYS3QV>0F`6At!Ul(v=MSe&0TQtsT;sfV6B*-#ifJ3CWr+xG2? z+rP#Pj?~|!M~`|WVb8)~7N`<~@;eYpIW7iq(I6!^Y11YN?5)~ojuZm!-(5+IvdlqG zkAP!r+Czp;x_D!w251Z#bcE254(DIHfojLNrM2`sRH9ZkrIKb7?ApKoS<%u}A0KTR zkhyLP=7d8xZw|rd*g0GW8f zakgjN(%R?#YCy94a?0|bo{r~Jl)F-R&z;`hEu=~}FgVPn)?NE`2DRc{j2@)rqr^a$ zqC`!xYJzLn;dl7HrlbPDfh#*p4Md6oF7{rLJ>*X|YHzSLCT0-z#ZR|pZjP>SHsT<>gpVI3wd-m*h2CH^*nP~4c z(Rh?!hN{rdw7<2vmK4-3#g9iLjEhHvM*utX4*ni=CbmUdVGij#&JV1+Bdky0p3|pK zTWR}i=v1geKqY0{Vt1i(ZKOcM3MNKNRmf+~v9lY=%5p0I)yKEj%-cyxyV*}onl|0c z4S?H`C(bwWJbc)cI;1tivXzxp)DAGiZd{bXmf;`4hy0V!+km#*2Fj`;yn#nBRP)Z; zf`Wp|SKZ>pPzK+A70x|gAszayMlM%fuw0cOtwpK#*x{WFMmxHqcmBQU!vo7xQp^Z^ zQkdm<*W)Gk`GVqO78@!lk0K;*=0Bz$>i+)zc+@%*c9krLVmg3WK@`zh+4=ce@waDQ zS+NOJ_O75HGP?S<9XnJW1{r5~6v|#K$_dM_r1|%k)kO1tkQTjkqP=6Lzo%UC#5 zBH}UG)X3tX0I`5z?{-(;1+9bE1VdXNR5I~iyOg|G;GpL4YX7cX&=W(omiO)3cjDqbKe!ct+)ZfPP9(-w?BX~xO0EX!yCe74Mj?GK-ppczb@JZv(-~-iK4sWcd zPwz{(XV-R%Pdfc{@g@7LR1vZABQ8^Ob_D}KPAK$su1dJ0P&;aMT6hwY{g70LiS!)7 zQLK1>zaeR$^LnM0)fp^PV>{G{<;#4289lb7f!q=l)i>Fvs@J;psJ{Kx4f`=w9k_b! zS{kK2CV@o4By5u?&tQyg;5jk763I3V3%UuW`ydzHTA2Osdme^EeBqZ<&LXiIJ_dTRg$$HWM0Jz|15 zRC!()J^Mg%8U4zx0eK{SRb&b@=YxRpbc#)2s<2-e`n!PRSzU{xvyKj}Au1Rw)OfO; z)G@{t#m#7((HXZ38?^ScsI73UV{C4W#bLfAqt{uR>pdd#Y)N+i@`ia&!aZZ}WL;VL zUesmJQI_LL<^iulzurNwLUw8;?tQwnt`p8%zK>VUavKAcA7Yslm?~_YbH&BrAMxSMd0_3g%e%xSx6H@!6@H$!zIm%e5oB z?{&I{iR&RX=t#`3lzI-(y}x< z*y7QZm2=VO%y_ZBb8$Dq(tA&HqqoUAaW%6eE@yUT;$LK|EcYEY>plyajxJzhkK?+& zLpQ54s@Oku{T?PK?_p0);AxajWl1Pkbe&24)+2Ie1O1q4iGfZ~oh`q0$oTfKkyuti0J6nl zJK{c_{hxyr=Wab1se-*EDaqqG8HN%agbW2C$Mlxgq0^Q?=@>mPPb%e zh|=@*bscS|#p|jnH&AZgAFZ}QL_C|-t>lU<6!LZ$a#?oDf|8OFmZ+~Oo+6v*Is_;; zrGX}@$BURLG}<+3(&QE_acNPq&Z5^>3bnorCkYJPSUynb88{A?;AKwh)}3?yu^5~5 z;Q|9mz>qYlOPH8t=nLl*hxmwk4#bM~% zWg6O9Utc^-7uH~o9v-HJ{3EK)H;jO!zQo?V73ZNN_@V*!JvZq4b6jgzeZpib?S9|UM)@; z)~MZJ7KNW8BrGfn=2X5Ncqm%gy<64&?>v3KL(7x}uPzr%PL}vsRoD8`(rMWS4r^~w zy&};(IHT!7XtA}mr41noM*C0OLh;8YprZZ@&5#U)8EV$P{XWZZ!NNGuS}?{_kSGO$ zglV-6#b^h6XW{tKo_=XB^-U3)g)Y1t^ydf`mC~SDvu2AP9j&GSRL!Qx2kO2J-15e% zinU;$WcWj3T05*nN>U4m3r1&fSM$IIL*~s>=bf{ms9AB3PEP33QhTZ!Od0;aKfrGK zWoaUJ{`ocHsped}hopBEHJ6(ZvnXlO=gLle`|;z+$?=5aanz`X2H1B1rb|1j(8(-d zF|O-^e%E|?Sxb55aDT2^dd>-<-NAbhk0O*vzYAWX3m{v=Wgq7(STGu>3o_T})@`S` zafKI+D}Bt3uy)3*`SJ}QN|`Hs2=Tow4^2G$3cA0>P*8APc;5*)!tT0%U-g^DMmUcr zO8vUj1njfqo|?CMB$tC&GNdgJD6QO1TTnkj~a^ zMni!-T7o_`G&Pr0M9hq_GBq>vLYkmMEz|_ZVH)I?hJ=^csh5_&=@A+d()^_@C2}!M z!~ELhC5?yEGKy%;%mct!60JjsIVRZmGLGm#Iyn$RDW|%ac&LvYJ$fwkhL5!B^J6%c zRW`}K5U9ed_Fun#O=Izd@J5RO${x@VtFmuyGF)x!S~;V72u&oUA`_hWo0K^@8gF8q zCi3I%efzutAzkSdoy3o98b2lYVi~=g^5gF>mqX!7O`*svM~&JJC#wmNpUtn*NHgkd z*RH+#m5Uw=nc;J1>I-VGMJ1nK@N0Q>G0V}E(%TEyUXrhl4xxXXbCGh_B>0v^mG)j5@HM{5B2cU4tXW5!tEuVmV^8agV~UE3JaFlRsx;~_#;xC+yLGPaLmZk|My4s= zzbPgr<{}`)Fy57(jl@^4rt_iKR=(fv(~E2r3Y%a`8udLu>>t}^u=B+kl{jZTEW_nm zP(8GroSaNx*5pbVocn}gzdJj_JcjzplQ8b};fk-X??sGro@Et$UJ2QSE@73kVXEq&Jo3|4a>$(YTjJJ!$1wet^x_4}3LmDQ{QM(jE$3VK%(WB7M z{6=9XA|0D;AW2O?{NE-$0;nhmcKQx$7pVyKY*%rpM}A&jBF}FiQX0*e__H4)B2Bvw z4?s9dqz-SmJoXBPOUjjD5DlCB8#bX98@RsYjK9?WLsHLv;0gtUx7@cLmUZW@U5^c{ z=s}q=V+M^*+4wbcE<7~qtL1VM#0PcIjud3=9tQ9SL$;YRL%+ zN7+v!Mvt!X=-TQfFB(g4b>F_8#l@@eG!r(wlBZ3dp6qa_$L~9hjC&J^WQqkHCm)}L zH{&kfzjv>e8wI1NDa;ILP%?aqJrx1fPh;5TpYikujHj0>aqhVz@1UfuH`J7`L~TL9 zVRS=YR-7@(G$nBGtA#V^KCz>gl_rw9&AM8k9EO?sN#*+^*A@&Y2+h6OFyy4Ubb}IFbXZ%;g2S0ML~d#?WdTG!oj6B&2F;%dOhvVHq*OF4Wz|-b?rX4e zs^`~FpG;@W@Gl>%r#HDWFed77@c%RU>gFGCpAyxKUK6^D9>rMuoy{b1%92T&PDWur zsyK8VeKqL8P-Flhel#0gbJm9UAk@!AKjR*?4Ydr9{SWRsBCX}Ntf|BP z8#&p!X<^ALdru#qt~;zpj2sD{Q}5cUyki4gA%3Kx;j=dj*FgBfG8}%-W7bAabJ)Ih ztGmB{3g~`QJDp0&(%1)AI5-^RNv0gu8~ZFBVxYAA`$M~{B!gslv})MI_m^M1J#|{N z^*AK6I*l5sIPCoa{9oKU0Qil2KMj82+L|vt3NFu?JGaZv7??MKK3>4%mEq$qCjF~gIY@M0GBT>^*VlN zH~qr=ef~eu`NC+gqJlm?sRxZd|KOsS)FTm68?_lb@StG3i(q2f0Y}T%wS&-2N z>Ax(_EqE5u7AzP7Yb~q`Rk<|Hy%>{;(gm9vF)hl#z#wYD!wztA2ZDmsL=Pe1*}QV) zv9V#_fB3M2f5v}&l)XP>|G_uV&{7rw#VvYxL;8>EZ_vxHaLh_F$I8% zYGxDlot|B4L<@3i>OW9fl#1fP=b1iyUjfCdjlcZ)ix*8AHy%Q0km_0iRMVi8g`#?N zYO-}&>c^&Qm59qtzl7dPq5 z)PsDXrY&1`o%5~MW|yP%)kcKGG1h-~pk%!Tqcj{kNU(zN;7MsolU8 zB26Fh76KR+GBI;ot6RyhKaT9!xO|YX_O~?pRh8xI^s>F7?HW1lLRT2MQTY?QHD{`f z*SmXr9J6m$bMsx(Gh;M2ZRt4^j}Zquj~qzr3CcmFS(t0nMruYO(4BYh-%nauNl7tl zFWN!yL!T=1bw!A6lvyXs$CL&Q`8rAm^#5lVc~_@j-#W2!W%2+G3#7iQtMZy+E^&T( z%pkVE#5&bW7Pq)nec!w^+LouEwZ1}z0izknVj>paTHF0{V^ZQjvTnlsH9pi3)9ZQK!Fy=ESU>~Qz; zy6dTFPP&zFjIt{I{1W;J;tiem0NP~j(Ye$aF^OcM|Ahbx zkyHH-PT5*|Vj?S3IB$rKVX~|)E^H!Q$zu+7jeEv#CUY zsoN3~#@wi?TrYjaWR5GYRYrU~ZJ5LC2eZiM0lNIr;)&9;h&*va)mpV0O`%Yye*O1g zXLUnEf0U$iP#_%_gh%}!h6VgqHT1{c5;24X-e>9a-ca5tg!S(Uq(T!ZOGsjH-E)A5 z);g|Iq*Jd*wxXW$gDj_cR`YL5wv;LksKdhR3lW6isGyxdSSe}|$MRn*rSlD6_kPL_ zgg}LK&lVSCRZR|3fm={ZL4x7+HEY#^q*Deg-LiQz5uqq*YgO(44=Sc+^`V;*FCTP8 z7Xid5HLh{@6Tr<hiAO3*0R+=YGNXgI2 zVy^vAedTrc(R@v}TjNX|`mDXP_vFcL)7HF+x_aQ2-tWU&LHccPEnHsgG3agMv72D2 z#`bQR+IiSap#qK`a9LmU=FOY$OE=3?@bK`E9%g!r-UIQ`)ADE9Xt$bp#6wU44zSV! zD9F!QacVNGLf0;-xpd$21a|J+IV;JlyTi=f<%fc`YTRT$DK}QP-u@d-b=Tg#{g!l5 zm9HawMz)=&eU~`y3H5T)lFuS0g31nJ(xQ8cMh`|!$lMt=EXKiu$<$91_wO5Usr#<; z55}jw@EXzVuac&;==fmV6o=va@H5v`e!lCg6-1{wYn@21PYFx2x+o2<<%K)$wY~LU zL`qGaEiP?KsY>lIE4fts$PoDnTgQdjcWT4ziQ)nudz`{v9C;Kp6R8SCZo0|1nC_8x z|6Kr6qq^4}f+TST0}kc|Y%7bGU3>TC%L8ifkKjBw%7eC@Qv({GvAKSjvc&?mm=qEZ z9c2#4>zP?+6gNYg$|~x@1#+-%nQuTq#qBk>7|jc(allnMe<}~>o9#%)zGtw~OLGU_ z5GGzY1y1VGyq>zruwV;FT0raFJ9maJxokbQ|D9`WaxVV;@dHNEnigr@g16|{drMXE z-#4=#?x7B_NdlYXX!KoO1;tZt>!D8k9YJw_V{jQ@7NJeV0Scp~R;?+WBlgwqtE;<^ zStLkvyh^cx*#vkq|6sj^%j?@GTVrL$y4m9U{?-+2%g zdpiuaw(0!59nuqNxxS2VczLldD*@m{#n|1}wL*{DUub>88{88!!}P?3rof8fKXBz{ z;-S;68VQ9Ki_R{ezkGww$7$aaM-RP!y7woE#J7jdI4Da~ZKt|Pku8{`VVhs>7m8OP zXWBK|xN&gl|0HODE|Z0z`Bj!5*|689GrOP#aQ`TxHZ{^eeNdxo*EBx+MV(%77nq5% zDT&fQ*`q3#A~;2Vs(=?~uuF9L8~-xCifGEQ6U)F;J?&M)V!D(8zE(1gA;>^c4&NrR z1(D!*%-Ke5`t2keO{BjIyjWKKVQrr5Ian~xUAblK|9WO@{loF?90pV*zj2&LFqUEFAG8Iy+zU+0@8_0bvj;!&xEI;EbrWMti6epu%&4;S+^&ghZKOv<6z6v;+@- z>EsQaxN;Bb9|xpYrag@#%j7+aIN}rNuy%wRJTgo$B9Zy>xqPBf=}P51u2pC_?0us9 z!YkR{OyC-aRyG;+r;@u!5ilF;%%4GL?1;7xjB!Q~@9t4x6)i0}VHqymBJQWz?2~?U zr;=o`gPBd53wGBzle@f!u(q2wA9t$g zImYS1k}Fq#K>hiGuN+VuXnMPYgtl*cy=szVW$qb*mvAMTu_s8&GEoh!N$Pz>B8d(R z>r3dewyLs17Bl|=0qke8Rt_HZ4d66!I8zY_T(5V zfoc}clsRJKqnE1(|Fp+MX?;ZVaLk2k!X+)eyXUfO^#4oUH%;doeAM)RW{T&sH705b zj9P+a4V{X%GQ^;ozSb}4=E2EZ}ppn##U>Z>(F`lCDd0Ac4m+s@<;21n)MG1%V zzhw9qQ<*k2Ha5nEkqj>fg`xAUD&{Q8*{fMu+nSk?xF$AvYao~9E*KiR{wQ2u%g9F) z!TnHu_qYf@xIp&{B^!CrB74Cs4N09h2l}v4$9HMIO@2@gT!H6csq@ND%fFNld z77rqeL8w|BaiZQ6#hdrE_5#9XzkPd)La6vzA%cvjhsQ9M{r3>RiCH)`7o~;NW!zFQ zJfs-&U|^1W`k?h9cxDD7LUz#pI)8#*HTP!DbMkr&KD_elRocxF7<;%D z&>Z);eR@Sj1;Zgc;^?ssBq}Cnfz`*6mmZvbNQ1nL0ObEVn_C0ve)Q_|>Fd{C#;qTz z<5=0)WU4Q9S1Yr(+~IH4n*?&Q%)iN1kNK^&vWXSIj|lIU^)BouXkcc9_jF2w?!V}> zvhQ7PK4U!H<8-E;ySem*nD@`0m)mFkX9^JgbHS^%*G?}ZlHxmwM~dkAY~=FxWo1Wf z$*MBS1WMrkn>X$80#G>LX0A+*A#V7``TH)`oH_X4v!G&4w;r@rz%{IcwV~H3(z^K; z;e4r!s_7>ujSDn<@i~fr=V_nE|5G~kYnx}xN$O-^U~P1Re?VR_Wfr6q9WbHJPiLxR z2dvHfSNh{R#K20`Wqtle(1wy}oA&*C4>r3tt)*LZp#7jx_?daCVu42Kt>zvWm`d6H z?dQ*UMhwzFh&QCZ@~%DEbR&ZxV8KktEPlObS7$8+gAk~iL~Gl!tk=|YZb%ewN$*7J z2eh3l`>Qli9U@*0Dh=+9M(aetp47RCz z6N;v_k;_2n4r&usHd1NMSxh+Z^YTnwTw+Z&sCq>7=1M<+7fxU)5Fc36W+RM$8mJ#LfSh2`W75w(d`cjehmY{m>xE7n zhWjTBoC>FU6}lK@q$9njmd@X@H_7`@U4=7sM}CFSu08|?Uhh<+xlxOr1t%kEY79wA zNlEd{{qm(JOKv!XfeX0j!=Ks!JGfB+2>j}V6Z zjkTmxK(L+x3VF9>Xz-^4;;JgAS65aZXx(>I8{>_!%(gQOpAu=qo5>eNBGarO=*f>D zpHQ)*dM$(Lu>>@P(hAib-TzZs=$_YExSSH7KAmh0wZvCx-oAa$$!Nl5XgMLQw{+eb{C0 z+%w@Hur@9XsN#4K0`_FnapSzns-eh8Gt2PNr1Warp+hfrmZ@kgP|7)RJP%WtOc7bW z&q$U9_O0N)d(R+y^GfP)fmPQpgA|nDRRLDeQN5Xj$4I4mH_cCl*<0w0%?v1CjtG-I_Dh3`f(8t41EA_9RqCA^(|B|93wqXzWH2byKXphN}x zPO0?L9G~U2RQ&9qQ|k5Omf@=X7P`;KC5zF5mYhF11~&vFCD(j?y$fCiLK35v8tvyY zi%*8Lp;0IXuKMV1_zcvL!)z2K`$$j^vFtG*7u!+D6sx&&?UwF!oOXUrE#^kN;}X0d z{%I8>1^bS>;rnRs?%l^Yn9w>Nx@VKWd%Fh_HzLlQ!41=i8HV)0C~&tZl&kl&Z{I$r z9Py|iOoM8d$xKOE_zQB@{bD)w1tQY*zsWKjSNr%SOIE+RQu?~T!`$z&0FM z;{u^cqvw)#p>z(jh>8%bnRU4DFAwvUuOtHrYiB&|uRa|VdZx>pK92_PaER9!u=f1VB@I@U|(ck_Rmg}JU zuCuM_jmX z&sn1&MpfmjXCxS}`q)?&^_@F4OgsiM-V-T)(n*U=lwP3F)00ONQ%WJ;P1vznA09p3 z6?|T||9$tqE3bo-$uhWof``og_mD`7#zo7N1DLLY*<0L+%2@Sj(9FaD>7&BCe{f$A zBZL5JLlFz_eZd6j2{&E+5djh5@eUbK@$Z}#N@aQ_hGTz#C!$o3g73dz;|yIL1`8ot zgNX<5NW8>0-l4sVXYX2ixB^`vn4Kd;j!-SQ-=&3%$-2biOH+do2e7m!2Gi`gM%;ER zsoD1SVka$1P<*34JT|F^c%g|hUI#8SA(cOKAj`ntFE4zKALo^fDk7vZdPt^yTUnFy zV$Bv}2Paw|Wvree_Q$j7eDoH;OJa<9f5dcH`=nq4_wVnDarO0xYZu_|ev_=af-gd< zin3vSDE996KHq1%zh)Dl&r$?7in@b1HSWy`pp)2xIg&DVv@~a(EVuIY&|kI}7fuiu z&pICm&=i#ln*Ao7D*sobseQMd-NlOGRLhfOF2*8|(=I%8>q|aUeKl01`0IOeTcz0s zkK|391KlwWLT2}QaUW_ig*s0xN+hh~6ihDoNVkPG8aprjvyM2U*h6*kDBMa++{NC3 zG=B&b*FL;eAutY5l8uZB@$KgC9w2xnc~7L)7Hko0&>48XusbBgAL+M=@Eb)h6etI>7kl_ur)bCy>sVbf&Q^PCB_-f z$bYj(EYEbXaRFPg_d^}~Gs%%1k)ZpNhfh7zhtb)`B^2s)M+_i5pFnG2)N#g~>)iKx z#D2nn>cABpv6}Tiy%%K%UC?>$o(*{3QZj$b5LL+8+nt?;4N(~*>EZLd4qgoGn5hT zvs8e8Gz6*|nVx_T4TdThGjdV+V*0&DHXUcV9B9do>#Z_dSv6XJ zMYh7m#)kN@=*>bbOs;Jq&Zy~SIEDNBPP!MBOMA&|HOOu$a)zYP~(I|UIM>XRN2 zC(dXi!$}t4slmXMuDDM&9$P`fGVO7<0Tc3AMVNbRRPul0?gzY##NmOheH0xr(V33b zWkxLP%;neH9h}RvhdU0huFCb)E`l zi}ulh{M>x#kgC&^Z+LeSb;8@;#+T1xJI{Aeu*pUOc?y60@cvG^m(D_@BgIMB1-1>O)R3Xt0KU!B3`6?*iVn`J5#V z3UqEU#2D7Swz0y12z366?N!xR_0?NBmMr|UD3$92{#2N;8`;*rgt|f*#JacjaGuAw z`{^}et15rXph|P2(ybD7*#jREFHkEujqJH?*ez~ygYDH@zxq8Ff@21~x`^+kQyH)% zbMIhl71o`0vdUo9lTI(p3yZmOWnpMu)AsEf9*s8N9_Hdxtkr7b`b{6xtIc}Wp&Qs% z@t5AXRkiM0b~ULZ>Dp#lutbkV=LPl=At@lBJwYC&G_{>5cp1G1^2`wq7q&^! zvFfr6a4kd{ZmOmhsaJZa-=e1CxT2RtVSV2Ly%J&ohJ1#R&u?8_rS{{~eqnn+Co{UP zom0p#QJNj;N;11qX17?UG3&cqx6U*Eh<3WDNAviiNFL9Z)-d+PvCdhCWe-1Bnkl@*=nfJywmDp=Z9Y_3~x+*AUW3n{ITF}-|TxNY1G z|0?>_6djq6OjXwQO+Hhhy(vPa50w~)-YxT;t24}At$&ZP zRyg#}KJu3rf;29Abw}DKq1blO&MtmUoitNmnA4Q2w7W5 zrgTh4o)wgrwE$Xo)6*yBCvFeFJ_JJg=+T|4<)H0swY#ThXjZKr>KL(pW(OmeF;JIG z;d;6|?+@3dHRtczBzSJhHs1UnXLFywwqLj*8?9Aqvz}t}(0n-doYY1JMUh2Hf_Q6J z*D4q9_C#kiuY~U}EFo@(Lz;p#1-MU&u=XbOCFGHXiWb)mJEtDmhr0~O>`uu5PPc^6 zqkUeV!RVXvpV<+q2z$+)uk#r~No~IU-qudpswI1DKIL^w-$6t_iH}elA6SbB$kO`5 zMB`+9WD1;2Ctqxj(Qw*V_+mlOdF;q^%q2idtOLqIhlYW;AusP9yJog5TV^kz{!E(X z_b;7LQ)hqm)}=g$E*(26K+V$aN}gAUT#VL_t}k6OOs>LWU&~=lDSEe18&bc~ej>+T z$d^ag`S$($T!xm{el;n4k^maK9q%dV;E$9SK`AmiN+ul3ta`xmkL5j$I2^;MRM|`c zznqM}UdDK{I^#1krYxDhavUSEclr9Z66VE4o3Y8PfizO`el_D%*0)So{`|#=K2X$k zFRdy25!Sw;7u5LpApiaQF8PgTA%Zb!u(=zR7*vow#{aQoA@tD$u^o!R+$Y%@$|2F+Vik&`e;fNb5wQK(wy7!k}sy<}Y zsM4S!sGNav~HG3oDDi8A)pO*cEaFjL+G$9d% zX(ver&AYXGUm{0H5N=U7cFBgvXnnlmU#*xpMDKDcv-Qk@LUuK*Jy}jo5u>FW>;m}B7QVjHhmIh1%173SE(46X%XhF^00s@vv64J9nq#RvhUJLD?Fd(c zN-V>x6(T8s10>mjn1b+JDsVk39|-TYKj4dR&~=5vO<^)(=%j%5obi&4wL_X3Ogy_* zV;=njZi&nz6(t|CACNwD`EQW1cw)I#W5(;G+&(psSW-S`O2U=ZbHbVup`7#zMI@h zcUd%4uQ=-H9={KkO9Tc~lZjQV+W%~)zc_5092#+8B-#852^ynJD+NxVP}Fj#OB zBt_FemqQb~(gZ8)ip`P)C+C&S%qWckSAgwKFgG0=xq*2l1*#C`JnZoxN_LL!DXav;;1QC#DVW<^6w~~$5PGONP4WK_@d1+W1qe-950H4OyX<8@to;G; zA3?9ZXxNj90nW+S!AGn`IcxwkC*qY)`;I>DL7M(P7E>KsZf*5uG;!*Yde*3o7>Qpn z&RE2G%mODU;)r(DNX&wp8Oq8MxQ7@+UKarku%^BI15G(ktXJMgjJtT0NG84$2K3Oq zvoy+#er~w*dz81*M2GleBzJwMqj8kN0iHnG}=;oN4INJ zR@RM(qO*;xY|7+YYgZz%H{mWsD0tduEEg|6CF(|A(0J0M$OzLWh4JvysEKW(KS~-y z2@rMX{~xX2&?fmpR8$Cy(Ugz&L5>bVhY05(OTz#y4%M*kPq)8H(L9#%7ipAZp+HAd zCN5d$(QxmkPUEr9+>=BQU3B{9=wOgmMvRW!M4kfXk(&9 z7gO%ZkBkNN0pw{hyvP(i-;#J2!ZYDlChTbsOf?tR|6DGw1fF?BM@MsNmEHxVnYp>I zv{%3o($Z-{CFC;?=N+~|OKDb^N2DG(a%AGQBASh8#q4zN{(V!jl_xzxJ$N&y3bP_g zUq<*JyL^B6Dg-W#Uucd5#Y6!6ARv!Kf+2>({S8mozCgI^@#$ zjZ?b+?F;DXhd{tM?8BKx_1JF7@1k{!c#Zv@WE!bHPu|m$bzY@PbIRxIjq23teQkwA zJXVq%_EL17s^B}zAqtj*>-JvI;w*%2CFj5)UDsO}`8ntP`#XaQV(-Z7E_%0J@5*>7 z>cwRff>I9l5wR zF;WU6LZ2n)w^B=fClDo#t?a6<-kTvS+?sICiIQ0oSpJKWW-}Zi7?sbtVHTGuB$3md zn+T-o$458f-DhS`jxpmDSG|kyCDKI7Rg7g7$yrkhR+1QJP)j;4XbNCgWq610l)-zY z&fNdE&U9Cq*vY`X*-!fF>(V=FN}~$l=zd0qubg4S0Z=GBb!gv3I}asxLY7erc3=|O z(aSd|fcsGW$z%k=&qDlqY;69tRYKLNnYjt2s1vF)8#Z*G`$ca`I}R3@#}`ToGFBs? z;<6n!V@5lQpN&t46OSt2|LOeCx!)~U4%B&h+1{Mx^0)hf-uj#z&Nig%orC$gEem^C zgq0&!CsL^Wjnc4bc8)*4F`)g_v&@!YK`{klYN)Yu$yHggqy8l%WzxwX3=XB+`Y-?T zw;1aCA1AEKJl8*n3xn>1#(#ZJe?I$gp>^w0aiC<~xc(@AFb)f)6dPirIqLVFLKKcN?u~8jO&I^;e=~QAM!)Q+Um3ap$D((`NsdM$m)&y z-+j_^DU}ouoATK3w=*q5v)jk2=P?CWy6vwjd>^@`#)J_TjYpnp-u+mg64jBPpU=5l zz2(mPP3Ml&c3*gGs}APL^DlbOhG_^-;!JOT`D+piDqSKtsps`yKvz>{@Df8%9!3|>^{jg z`E?dz+%UG~>RcG)mDq6YXu9x$44b`ZQ6d{>I8DzAIkbpUJjoe4)Z+fZ8@?wX=(FFy z--2jKw~CDYBjEx*Wg=f|${%XF5uug`Jw1)tuS)8J#l!^KVY1I^M6CT)wLHW{!7x7@8;HyQuT;G|hgmh2%*IFwU^$bmZ$LKC7F zPnPMz0q+Uv@?RUZhwYSER~>h7tl0rn5E*;5k+w9}xlH=RVci+B3yu@djreFV&UG<1 zwyk?+;>3wBuCE_J0-X;w5^J_lsE}A&WMpqhNXUYu4C+RC4H=h$tv5(2ez~KLIZJ2` zyp$Xd0EvcFCJuP)#N81bX(mx_^RJ0`g_tWcG1&M6=}jP`y!YnK)umD)TFXg5j7sCQ zGC9PNdZl-tJ~Gt_Yx5D#h1|p?%PbW%u$61p*kS}~%^@2M;5aZP zK+mKs-Z{6UG4w68O@7L0hB(pUhZBY*Qsz47arzzZBTv>YaM!N7ZcSHMotZjeT3BS{ z0jtv+!d#^Nu=LgI*Z%L`%|S{GTsVE=#Pm3gMh3OcoS2e3eAMjO!Q;;@TfY44h1a|d zuKVLBPQ+7HB|dx@PkuqT7I*#D#-V9)5TPh7U>TXLB04$m8L;}Ru?{QS)i?4+*>3*O zx86VW64!0eK*{FVc(zzA)@Lvk>{^*w$0)+t;MIrT&x~)ey==$0XOUt60c1;Qg@Gv9 zyLYd21O@g8Xf-oTYG%kf`u%(%SvW_`l<>|lytgb!omE*n`KlUu1tlp%Hv(B@h^!x) zhrWY@1H(YlriDFSEDkVCk?YQ;e9F(yFV{?89IedF{dC5$YGZm@+M|q&6ReNWOAk^~ zTyhN+=Kem+&!e@XyIvKZlP8c=DK1&bF)S1d%a{w|D{R*61*0}eTRImmoq&X3_0e}N zo9B?BlSBCGNj;uA1|V*SL~+!de{8HFOYR;4;nZRcz4rFb&B+-_2J@;M|C8Jd`=$mlMLVe`^$ab)N0nu2&6{uK zzySclY;+!j->y$C0nAPikYr>$-P4$y#;ZlHrzd(R_XH;DK4lm~U$I0af&$cD6!h%V zrwKI=dW4MngBgLEI-RmX;m_H0FiNwQE^Xj7X-x&vnW@^Kb?d5PPV}6(Z`7Suwl2Zi z-%Cnz(R~%t!+|9GEvTw|2W%W;TyDcFtuA`OWDtryH;?&eft$I&^r_a?J>oNOY*-JY ziYKEX3qL6u|N|cgeRdOj*o>1Wdql;DA^Fl7S-BR1w@mX zLuF-Uh{Y7cDsGK814WEhc4*MBp{z5DqA()f5ECt^2g)_Ad>aPF0B5?VvuTvjjtdY? zluD(X$4V2xROY+$VUwQG)AtXBTFq=UX(0>YHW{)rnib%Exg+58ke?rYE4};mQ#Upa zVnrzg=aLq%`U~))a1TAxVJ(WR$;0EQVsbDOF%uQhAe7K@Mwh1@l1qgS8=sJ3psk~s^~WxsrRFV0A) zZu)hsSb{K;$g}};Z3tkm3ggAEfCujwXGX@6K|F*d3?c#Lni?8!!OQp=V1|C-hz%PX;>=v9`gQ8eLcSGwktsqP588)WdzcK+YD3cS|=+cBpud17e>ACgZ_DCW( z;Y$fz#JJJ$6T}8N3sbdi+Y3~AIah`c8z%i8+@}MEwv9R*SFboN1%`wn7owOZ(Q3q; zImZ$nK5WH0ox@{)HxyQ?CG~g<+Lmv_Um>j5@3d83K}%v|mIO01Cu1@k1@l#})7CNL z#|pht-L&(!znnFsB&l34|K)0fV>1x!i16 z9gHbDLIY)P_&M`!c6LYPs;;!{bllqb9o)w|I=5+e=+~Hfu(5Q%Dn{Uy^K&Z8-qPqU zP-1K2VcZ%sRD_`*r@EcCO19?&$O|%1ki@)`?;l5ajA}e=C#$3Gh7B7691K=uyU{cf z-CLLwttl-tGc!#Cw>6%_W<16!p2MVyV+gv(;J4l}5XI65B77#Xsh*083iBemiC#Zp z#tdx-N5^~89bqi7j$<{t`*E<~u=h3aOqzz-}UQ}lQ+Vi9_WDDt@bPq>BMj1Jfd z6;pQz+e9mwXgK{bS$#*~Wnw@*ymow-E-*kHAs@QpUhK5Bu?)1lotip+K?Qw{-0<>A zc0IN>#$NW0Z}+780Cp`xwWfGCWpKfT2u4Xfzv_5GcLRNSa#)p&NMRWJNSq)enZOoR zZqvZA>Zcv*1NW+{*`ba?m27R?SyOW~du9)8j8Tcp;B|XFip`79O|_{oY*dH)nnOMg z&Gkk3LKx`-D7Q@3*48$S)^uG_R@uM!=YjWUfi0lYRnG!?&)!_L7Pd6p=_h`U;R{ZV zbE5}wa6fanHjBy*Krn8@B;RK$LCp}(A zqJESUDboEiPEXMZ$ooC0k@32{jXL<^$^HH)J^eU=;dQK&Csn3+B_Z)1rEC2TE!ueO0cKHEJ#J$0ACdZqh*TKUynYw+Lzh((AwFDD)@>W4>Q;6Fn^>Ry*? z-n(~iVZS!@>eU-FcB~A{f00UBNsNDgX(o;oa=smY+Zi`pS~CxZRky{@ueF6`I^)G` zbF=F;(z^Wn_w_e}0)PK@dXxYDM{g3vaiEhz6fqPqDEyJbhTR_iJ8SSVzs%`Eh!#Rn z^hoBhdEcnrC@@*&_x-1^(tuj6MccM+l>v9KO+zt_)0p#?tp-kOO})UT1ow z3Nag-4s`J{>&%jl)X=Ms?xbdHiSK4o)%9Tu{6M=0`L`;k zTj<6X$vv{)J$L^6uzu-^gv3OrQs**=WT7n^yh%2 zlO#gBGMm1>JC;p{%w#meZ8QXeQEjp4&}S&Nu^}cJ-uTYwkQcWUY1`=1>!eRVef<5; z6z5-yJCIQUBrjzmaULA2?n4}92pk|X;iwYuy}fL$KI2k072eKXFCnw-mOALuu}|4%R3=|KNbhW$ED8S zz^qv#D&Iv=4HjS4?blBLwQ8F-Mh=4^bBexypN2MI(R43{%h)Gv6W7xp@%+Yy7)!0e zknI5*thBiWrF-# zxhOOp71N*0dtR!_Hnr;DLmEWzt{oN@2AKbFu%aL^%&Zz);1_I@mr67!2sORx`N7W( z8Z;0qo$R~J@vU04Xu@D@L>$If*J2cN^XARF*{$HzXq1svwLdV?a~?bZa~HZd>VIHR zhDWh|*Dd9)Zqyc90ekSS+o&{Ja?H_~idaU{m6v`8AjdO)iy3tkt9NuWet!RcCq8}$ zQOq)K;|7@i@EKR5nyT;Ewr%jmEqPny96KIyDQ;#S4{p>guyW$c_G<8&CXoFY_#o;< zc5OW~l*&X{cX^)~GpHEiGc#@KHdVV6!Bk*4a2ak-N`k>kNKV#rRKG}<`mv{rnMPOL z)F6-y<}F$+PbVFz1ta%XDm*rd^vxQ6LkoCJD+O&5Cpp1V91|8rr@#H&-aLZw`;@aC zIog#YKgn$?SFKXIR{d53;Q+uytmUomQG4&9I1#}flEmPFci=@c27^qL!L9s*9E*v5 zsL{Lkw_O`!>=32U=^k-93UAnFzyJyzeesxrglB>H=|9JFCe2>b*bQUJe`LV9g2k=@ zG6vIs?6<)A$yJ{a*S=4=w|B2!TK7}V$QCMtQ6j|u{f#%XHoF^7~AmPA`?bZb7$f|);<*l9b+ zJe~T|_%<$y^U=|wf|?F;<>}kY2?#BclMlP_A$hjamLwh&d`i_c^v*GlDM{yqgq*6; zYhu0GWR)$r?{^t|J_`E1@vvEo7MVaCV+nJ8I*o>mviO5TZ^&MZY|^}W$i<1irWeki z@2)o^O5Mc1>x&umK>j6e`syYS^yR0JjJ*wtvehcC%{E+3t6+bWL8_qD2b^<{UoJCZm)RNmO9m*p)rwxrpLxr4VJ` zN@6B2EKXnCrsAAmSL2A|iRRN68t8bP>Up@S_u=95H&&ib4dc_t7FupDr_fsPvKNOt zl$W~@afCDi47BAay?bMOcI;G>f!On))WrAbI{g~bg48dr$5!J$N})4p(Tk{@v4hue zCtUZ$T()-{;N13~!3IP19kXk@*Ltn7`or426~!u__6FW2BHOICl0CcoL%kwKLYNVn zkTLJE=?DPF9xqhzlO2o}{8?|BO?L0u!#UyiU%zgL6e}Z6*o;v8<`;GFuM3mL1b}~T z{ueFytWV#*@3|m@?^_!qQ~?ZSI06a)%W~Yb4!AK9=V&G99^IfLrUQ!o!mO%FU&s>F z(lG59gixpyfB$xNnYfJ}QfF4XLMbPcHfT^0ml!15xM?~V^+7n@(rNR*lb+s;OOc6& z;#t>rq)`E>fc;SU#a1DUug(|tKIU?~>&?~KZQN4>HqGdS`9qqzS1WCm80X|MzH;B8CuYfCt9_ZuIlW!doqTfr6sIZ)f01$>MtD-6uub1W<8vh9oKs0H@%A&lgT+Rs@gUAuN2VzO@?1tW8`YV%aF6j)51`fJ4Rro&cH z!NQG>hCB-D5d=vG%5 zeql9Z27$~BfQ@+j?%lBQyf_luG(S=Y{2243E8)~0mO_dyT4 znML3M5VCaE$!bXnWJq5;L z^dx+Yjk-<=+}Af$(#4A7H%fh0PF#?7YDv$}&N0>3x;3~MUf2KiD#i4^dO1VCmV0?A{{$TtUGBoJ`_*E(@BQF_K0x@NrBFz8XApMV6hf} zWP}qFi35sLp)s`Fty+z#cyPoN&BZN!ePzPc6Tg4|o;z>eUC{egB07jNsRnUj;D3@! zq3_H*LVWZ6wz&L-+1^vUa3U~M%%5_^`|1t880@!PwSu1eadn?plQVVLFoEl;NCudF zdJJEDcG`uolpmNk61fhPpCPukK^Vz9Z{>f8=E&5>BrboG$IWXH7Vt`9s-#WUw4o!! z@e(!@oW8LLr$qRqT6%xE!hEmClnvdrwL{LGvv3-?&VmOlDkMTx&1^=<$SoiNSLvHa z5^Rrj`uS4NVdJlzK0&R6Z7X#e$8URKA!P};YVPdWg*-HF2j0^0AI5#V1IYY_1FgEomVOe1 zaidm0hf|#(%4zqvpFckYXzccLP`9f=96F}ZcrXGmo|v4>cdNYl?3tC*+($&2*Kgib zgO%?@4r1#aw>sUYckfx8nq{gj-h8Mh5up{7P$TLTo{H<>Q9Q~ySM83jo}O_2%(UNM zRsO-46LW+E+4EqF7-Sf4$jZBymL}&FSTe98bbd#26CjgA7~(Zwy5dHZ@gFFFvb_Px z@L5Oqeswg5aiNEWv4gdU+K3FozrwFU;D6_;(_n-dB1u6n2k^o(72Q%%>sHb5>xsxn zbLN3b1O)YH$iNWlN%r}>&!1&hzn3A`ST&pO9R6(%s-zf&kZVn6eiOR|#R>fgBac58 zP)l+@F=LYW8u7QnA1#cw4s8aVcebeiP>|#}E3YHTYA<^9GT#?m>OLo>jn|Xed3mSD zRJG_@d(p~vsFXpI=NCWi5#R)yX;FE@^*8l~VZQ+G ziBIbkwSglK3cX#;4ypqaXzn^i}_wC%7jWju%cO@Z= zI}tQ#_Uuj=9Zs-N4x~(>{e@0A4Vm3bA9HxZwTv07=;05(<-`}Ss<#)^ddP#31zu5l_y!VzXFmKmFM zH%Uu(S`@c#J%??DzUg=qmK-2Eag^6V!gI(6{5cVe-`svwJ7N39$-QTv>GNRo2cv#B zRRT{s&1WqNy_RN(=j|%awMO2i+k%5T;WM(c8V@GhMG0TXLl>Kyo$ANYdaZggXa)-Z zPb(BmYG*w?Z&^T))8NG&p`kf*v%p)qmKGLrijkX#?YILwcdlEQ75)1}^gIs|SQq@sjnU%q_#uT#qI-@h;C z0glp(e8exoYWMY zZG|WcG7uDIyU8%|>SFAxJoR0Ron!q9O_UoNa`NO(k{L3OjMfTV_=-EewJ|l`^b;lh zpxe-=|J2CVHpzOFTW4Ks7;1KP_|VP8=y>ObVGo^_y6JdFT)Cn;_mXGVA2dlaB>UkqgUPU)M@o7rpUbkHr#w{#=@Kg7u>!@0_YDuK$ z9#+RV9uM55zr$H7J|3ZE4)$rym8L4)KupQMliWI zx7KYYCvZSAgw)L{p-hMP1!57sY?C z&!6Wm^L*vMk*)v!nSAP!QMF6|{;Nmv;e+k%DJDZQlAazreYeRDQ`>Djl4=b4Y_nPh zE!bRhT{fh7 z41`bPQm|dlUA}xjG^ZSBNjDsG)^yV=sYHa@%FOJ5CmC;pw;fZisOp#(_(u2gO!|)D z?yvw)W^3AgnC*$eSOdg)hjQ6#wUkiEf`3Y6D8cw! zx2nmYOQr)e4AkcJGzKO+Z)tzcW#`-9+mBCPG$B8{&*Q$gnvBkT+rGT;Lk**&ZQuOb zUoi0ICf#&FK;XNo>ECncv?Vdxt}YzoQM_G@4!8r68+$t_$FTY0*;pdE!${ObUUlPh zr8({2ZlI}G9WKbvezMh_Uh_XTXvntBVudMmf z1~-m^8E3m7<}6VAE%R-ztf)qDfgn!tD5n4NpK((SDIU2sJZBI{Ff%JTl*kDLR>-(vwLhp86}94n2`RX1xh-+0$&8mG7MwY z9UStuEGh%LqTKj;p(f5Myh(` zYNdS^(=TQlj%=^+a8(oy61+nf9gLg@v66&YQ`^)~XWw8QcdUS^LZa82*;$h9dLmi2 zIZOK8n>Qm@u1w9c)r^Lmrn!^%_{78T`Nqh zYllrA`#m?iV!liJwDOYRie``$K{Z~ahW75$CytAPFssc1ngs5o!oNQ8B^QYco|M;b z-Z;2f^P|_hKU2@o$qC}Z?uI0l?uOZf?Xi$E0P?`q&+N+gL_#2-#nqw4q0RlWuSOb* zHuG*K^Y8Z+Zxi4@J9RgJi|gy(ws5?OR)NcU?UpS~=&IY3Wc1UG$qBcMA$N7NDaTKN zts|WHAXVoTu??2O)``bJq2j+DU=Pzw)#-EnJu6QIj^cO`VFZE)36t>>w;b&9V;DbB z6}pfyrY%*#IA9+j(tt`Yv&F`zE-y#=is{DQIzMU4GWdT}ac6s-H@%RV3soIt(8>h1=Lz=hCc zkfNJdB~cAdnW7Ggw(RyrnD;gXDJwr5V^NT*wCAd0qhka6&=AsyW*pcyNLd10Vbm0e zQLXMU8=*Jcea^j993C0@d^F2vhXWOvu3TB!;p03o5FVF|byEOrWz_s>3f2i0(CaNu za^V6bzPwuT#7|!#F{J`shwm09ApB;Z2Wh*?j$hO1th;u^fO@;FBClodzIJDj`+Ab)4Djc%2{JpHhM_vM*r~qp21x>=0Px&;B;Ohqs&cNGaAR)zX;{9jeD&_*T+garU*9Efsb!We z>C+53d>=RLv5l8lpLWDbL-DQP-`V@^71y-!lnW}Ghd?}}qLQV&0f!^9RIu}3H zkc8V!n>YWo*Uus_y3d8hw+l@C!TH2odkaJS+QulanxeI*CUD3-yYlnxae~^3=1dTY zm^b5iwH^n{`xFH%b6e8M+j8cO(EiO^G#{w6=Wx!E53|qTos<^4YTg>}`JPE{R!nft z*|V$u39D_~>OJtK+kDiYd}`0vc^ne;^GV|RWN_+;x*9I=7r}R?3ABX)FzwJcsUY=7 zRj;V^)$rvwgSRsmYQ#hb1pCF*o7v*(WVbutOTQ46?}cWT-^{URH<~u|pX}CK{yu(| z>ZE^AIMJS+{##l3odM$O|E&t`cS5SuPj)GGuEBkuSQRC_w{oB2{UG5wFDT1GYt45C z>S*I&m5b4NTYY-_nNAuyvfr3~pKd;#_i$!i+xM3$#>^s9jB+k&$19RTetq%cMaYR8 zR6b}{0qd4NuE4W#k=7FTQ(De+si1DRjF>zkZ~L(!bE3K%^gd^ozoR@SuK%0v=X}oA zpIOI(_a$`7j*zAW_&d{HKCLl@6HveZWzPxXP6c`p2P(ARire>SCq0}RPBOmme#QA) zdynk-oU{E!=p&yF@7_18Ed8jYcGG|U^7JKb9}Ng~qR{I>tgBFwaE~l#@#KDoeV`5i zr%H}Le?WD~tz-PlX`~>`m8crxWIAjZZ$w*HSNG`EOP8K)$aZfD8$KB2JiA)!;j_xR z$y$l6+ZZkFMl*87HpIW%O*tspkTz7--7ZKrHPMel#yO;PEJJ5IC>kNA@Hq;yu|A#q zhpi;ePs|XaM3RIXl9_ky+BJevTUY}*UYR%8ns1g0-iwp|FicO#mzuI$Zg-j&v3Bh2 zfz>@!`e(G*7By|r(9Q?W)NJ+AR!#rZ!t?$`BlLUtW9%fpSHi!!1^^PL0B|mQce?ff z`cBa-INa-QI&|o_4%P2f=zYLdk-?Vw2|*snug8;~ZIvVRQO8ZnjonT{9F##wft>gobTwyfR}vu zo4Ohorr&MXecD<8h34EVw#5uNy`BE-on)Rg^WtKRWHB~{FxbsKJPjQPznA;9qs&-@ zS`NW=1%fAr_4(j(S6YS)E|u_H<+B(~>pp^Mv`?~*Y+SV7THMaRb09YAJ@1r5r( zqlm{A5p%sII9OA@4>QjVt%qNb%(o|&a)zVh2EY|x`9BJkMfgWZobN*Di2X~u$^{`o zp&dte7_)jod&D{I*I;j%DkO*QKSUp}}b)so3(6 zq1DV);mOwW_aYcXOh?HxASIuSiSY`@hgJTI#?!@kltuG9BXA<8na$(rL^}~9K?VEL^1u@hS9@%#9T-lK*MWYU1Lu{-;{%Emz zT3q0#_We6=^L)1BpCn@H3MCncjQ{Lr{H-q>!xt{p@Ip|`Hw^j_1tycDDTocS8APKY zd+FA#CSJ6{$cCg9qr;8`L1MwGcp>FQIx>o7*u7`Z0v5dRgI>}rk#7VeVDaXu*&Pgx zy~qVqRi}`#Awak$7YmYXxuWx530*~WBNjrK4b*~4UYPtS$9_csPDV`N&YhVUV#Aj@(BMfD zf{+a`Kq&J>Nd})yCwG&Thh@4SerNFpP-HL)@+SrF@t1cGuYu~x!%=_${w}_K^7id0 z@fu>jIFtrUDArWzhB$p@qx42*EW81auPG4@ftn{)bb>sfHe6#s(w#e{DLrF^!Mn4E z%BYCMdCaG3b4TJISO!bCI1Dx`A0`-tj?Mzi_h1FK1t* zvlcOsMllX-JGX7rir%Mf*rF|2W8X3Q@e(_Gq+OYo1tUlM`z?yDRZ{!WcTaWkJO26 zpM*I*Mj<3|=TAX0uiYBYcmv-mD3WoqMc0@``>C_Lx-Zj~0#9gh#fhLP0v3Q;R(B|*lRdGwrRF^i76`5fQJy_>gb9(C*PW=$^1 zOs#CAX5Z?TH;?yAp13?@_sOBj|Th)68tB~oytQDUtI8ks831c zJ#_WVnMViTyoI(O0oEi|bL2iX(NZlhd9Wm)ivVpOMH~|u8EMmE6$gv^vmO7SO0_Cb z9sq*xuaVG*Q|n(F{GzXMKmXp|KVm0hQu)2Vm#{LgEREarhoEn0tA@~^3Ii4V2507O z`|UF2T@?RsFi-@4GnxNjn7e)DTa#_e-rk-q30l-)GUp71H}ngwZ!Lv{z))CdWvrnp zukq~Cvm-9^YTBvS8@}#I^t7IRdKOH&(&4E~t)h_ZE6ITaA09Q@F{^jFO}>qmKhIv+ zw;sQXJhw6ihH@gjpC^ylaKWsjcJo5-mM#l6<`j6uqTztdhNU*hJ~b|0T;qra$fT6~ z6+9E(K;AmR^Wac#-FwRpqdw3*T10)?nc*<_-Gl>%_Y>5T!=7YSJtwVBom}va1L8+T zyLoCpq^*%7k2-}jP&-1d@RSm^+Q{c4J^9=@n3XkDC#(c+f#BYPj*`ip6PzFUAEFOk z1H+@!0*hM1<^Gn)xp73G%8V|~q2PKV z#ctX;fU}ar0Q$!icJKw`oP>-3M-%Q!+uf!Lu)($R;}@D=@C@{ivzZVH@IbC3X0@vz z4Cd?_DXmGdT>kn*KymWd2cDfv)^|%FZph_6-dc&%`;n+4SUYFkbh;H8k;p|WoXg&{ zHte>ly7{hEwFV!ZYIZnmgnFF|BUgP{-}2g+20QdreD^osmAY@{w!(b|*#cdP^7LW1 zS&oi!`y*sTICQP2J=U*1?C+>rN2b5H8AeVtPe?%&6qFgV6WQZiVD!N&`@h&HO3 zgd>Cv2`xddxk01@9&?0+qSv5q5*IaT2wM@(D2OGpkghy_v|)>djga^^huv&jGBzNlA5e(d|gcOY=q=$Su|wb|d+j)Prt!P|G~@@C8uXDE*H zyvw)T!mqu2ar@k(>DA9#)lk!Dz3$QC8dXCH4(xVgz&_)X<-77C5N5c(dpJobPO1ts z2v|QrMctA;6~pcVG;T$5H|CiC#*NLX%4}n0%9H$3)c$sfV_7-M5N1*mb|>PRAk5Be zYpu06+fCBy+jl(6+3)y64+|L-ghFlNc1lWCv*cg;pg`In|Hqo>=qi?3YM+kSto3Ps z;tH^9jbrwf8MZoGAgwz5JBLC?1XaOq#2j^P+pgWZHEY%^baidc!Ja|`*mBU<{1=9Z zcNlwUR-edh+*8UL=B77J_8CN|M{GCTJf`(LtdKEXL}lA~t@ck&moHs11+l1$eNx;; zyeBk!&B*d-%&studUd}Cyn>Ps!aBW=Z~gZ#es}+YL+_GjN*O*Lfu9VQjqjW!?)Fss~Z^ymu|HRN}S*VyvNJc#!}%CI{@Dwe=Yz{)LKw#;}J zXmsP~*NE4p!B0mtjJO)!W0|&YS9ia?y{)n@MKy3(@@+@0V+|&|wTubr2swyYiQ@@9 zuO|(EbSquL!wyX=oAq{S#gV_zy}q!mT`KA|rg^~ky!g6QaIsG_=S<1>av1i_{L+K^ zU893GUVbvIX!V4ehjE`4X=cstDXwVw0BV(HwRsa$vrK{33B6_iaVk9f4!wGPcAP1Z z8Rf0ICrmNz;BA^@*d|L)T^4uMsT_4i$bhUA4c#Kwif6dHYdayrW?pgJldOkq|^d@A>d_7k(h6aQj z+zNXqF1f?>&;}u#2U~)IJ`E}c!SVsa1qDyPcyS#^6DH~1`*4;7AeZ@_Ze$91T-KE7EvxPE@0wu$C`c9#Ad!s~46Tio#7qW}}N_eu|5Tw1a`U8y0W2&|)7RM(ty zIvA2kM-_G4r%#`>u4f_wG{07X%|~1)oG6xV_9bz>Xpl&m+d6Ji4sX=&RFHaFa&l?> zoit2_6qJuC^iiyTO35nM+D;cyuTHTNXoc8Pan4S~QwP)`ntANlyABf<+`g==jDo9} z*eIFrLG3=-?G|i9|KnzPTDw}<-Si4K4RBdkxM#e5lX{&Oo?Ta-lU`5%{=(^oj#YJE z%B&+G6beciDT48`83AmR>hHgNi9K-O{lPL~_nw1$YWMP_`4$%9{rmS?E2k{&CaIC# zNEG}1eHe|~>{0v4v0NUZ3fg}CJqa$A{dh!vSujGA#Zs18Lg zFC9f%oqsC)+A%^Z{&3Z>dCPD)5sl-E>X^=$vvc#-t+spZ|C?==LS_nuHgX06%}jx6 z`u_5|wN+D}gweX}#vxq7GBA=m#PRLZfwWSLxACIUWExUB2B=JXOXOypl{SA(t$^WMaLI^D@>gK3m|L4QPgeKP+qXrp7c?nkXs zyJ66&QU0_yHzNlk7m+pvMy*bR2AYKWp<99VyUdg92V^SfAYHG549Gu=3AwcDc)fqW zkd1PYLtj+bfLi3Z>4OV?j=^GFX1fMbF&nTQpen`Q{M5nd#$;aJENjDIu3hk3&Kbh-;-WI4% zqI3gB>Z7c~l`GSipr>V`9@RZ!x+Tl2I9$Um2H92HJ$yiKpU$g#56*mW_2m7m`|UUN zt$fs|{f3U~TL+rO`n2!rQ_s*z>S{t(%0A zlFLIBRC88vR?5mR$dLFSD3L(VfCwGLSs3c1gh#a&00ymm;?;U}>g?i_;zKYhRz;Pv zv+!T^+KS1BebT<1x4Z|VD=dd z?W4Bxl!u2Tp3z+?kUon8TA+`YUnv7TmOazx$v`8?xw&ox_72b$T=bodcv@Onh-M;v~;->Cxk`jZ(!^ zUHisgAd@yoV%mUl4uSyinUVo z(f7*egv}u#!+69j3~9mG)5Zp#bYRPFV^N9@d4U?4ClqFFulPeDJ}-$=1no_RD=K@I zqvffzitIxN51!4<-GQXj=GB8a%a+oVzJ6_9rI2ZJqWX^gSX>D_ri zBVQd;+vtHi<6_hk6uMsBn$N!VB<9PxLnq!IO`x|I2Nx;A-GDX}kOaf&5|x5tg91(t zGkSp{P}-*KpIT^f+n_bU7hneN?-e+w@^r>-`K42)yua+)|3B=Oww1h&)BP7OZuh(9 z`>xktXuiie$6(oyb)8X7GHDSicghm4k9N{@(OWpEgYe|?O;hOt_xAS zq5bCip!IErjXZ1xGMMZE;(MTR@gaZW#vaCfOcNI0;wiils!zz<>{nD8G@?n4Rjel{HVJox z0KwKtiM#*fnWvO@sA-9{iei&U`=mftv`xjPXSZ&RMSF;g35_*Vd|H!y=>Z#Eek0BpbS}&JFB+ zSEVY=)``EBR>xkc>8i{r3(tBvUeBCupRy>_iNfYE@>2$*;U>L+hM&T0*R9zyJ`D+u zlIjXi)B+dIpRdk|gt}@N3p3+&MGgjVLF(A#ysXE3TC~?$$&02#sSRQ&x@GCk8FCo$ zc2iZ=QM@Hr#*}45;GJwK9?>B{9}&8 zIj>wcKVSon5V7buUT7;Vs)uTW*KOXv+T(B~dFR=6L4i{ZCUu_FEn&(Rk=6!&$~t{S zJq-$E`ImdMq}tJ_fxB$&`t>$o0pljs;s<2Sxv+2bPyTIHPUB)@ecR}Fxm=sHJDc2i zix|ukm(@b~D)}!wug8+_hE@4vQH0sQ%3l9kpAZ_7`(dnn>*ON|L&t6C&@ec)ZlmDZ zzx;OkrHnN@?b@h8@jJD`p_@wE^ID6QHNsz=SXT9=WaXHzBa=4`i!tq9^rhqOYrCz- zS=){p)3E=ir8|#@o@;*U!^+#=#hgwv7E7yMNl!`ixgFL|FH(WumGzU$MUQ=ckotM0hYRW5O}xW}<=sR9R~02<}tQQqviBi&%k zGaOM4z~3m@tKtmzS7fXFE+gm>B3=(*wMO4~(vq<&`h=3O)*|=toV)qmt5@QRhJsr8 zxMD9tQ*fIV`+Lg9r~*FM$g$n;%>x&_}g+K_CAss8?+;hrc{z`mo6=BX41`Q z`yHqP&ez-?A=a=y!87fF?FU>rpMKgM&uujD?k#Zvn?j)pNPEPZqcK$SzxTmL4L4vDpd#(SU4f0kh9mto<85!s37BQjK^TJlo z!)CGXM#jg!vY(X?K^ny$#EwTdFStTb=p7LEGF;B|>St&}p6{BDfRIxL(X{H0G%Z+c zbAI>t%w$UE=i~&&p+BHpl=L6OACk)iG_rQxI-R`rYu8#a0~f3so__}YZ&G=JhS`Es z|0DOYq0>x4qhOpBs8g>(sFfi(6~7=wbTpy~1=60jAdY~a>p%jsFt^bKAp;rDpO0*p6qJb*(d-bI{i1WpM3$qgz@rPKzk^kX9~ew%oi_Kutobo4t8of42vml=mW6CN*6*^U$2%8`#eRLCl&&%H=@{60+VD=H?R}Hg zyS|8CL_7oaKJ=+W{3j{{9Qk8n8Bf~Xz~Bil1ipUQ^O|7K`L%R~80K+5bF~7tg z&kmJnhO7sEA9Y9v=5+FVr1v2dU{ob-7}wrZPK(m=@v%514Lt*BG2%y`NI7wW6vywGIYr-v zYX9cn#@T()#d2H6qT5R$^bHIGnbvZXh#mz6={bIqa-&A$?jVc(OW8-^hTOgnMUGB* zh?4jSs0krRzZ;9_o=8LaHeKHZAYJBbas5;myMpHC759%}#}ns1`CHT~GB=pzI;I#@ zU<2Aq!j$L$BCkFLCwq$RP&~^D83|3CXiRT4QLzrVvL-Mo>7DK8RuYqN^=f?p6ywrI z)niA6?QQ!iiepg*TvLWI7}%!k%|^y+xuL&p?K{yhq(-^Qqu$NqT1J^=ZFbZ2i_SFr z)zZ+?-n8xW#n-n61`o3KX!2HfNm$R$UAh27s`u(OCbZ9wF_mY@V!-L#NnhX6$ub{S zS_h|QaO`TQQecz8<%e_5r z5Lrn_>R4V`$VQ>y)ybw(fNnDWQ2AE@N-ymT-qzg1iJLLB>)*m1Y$_yLf!>AOPtPYV z+4zD;n&Vd%F2zM3TnN;Hh`nM%W)YmPAV(TCNJfAK@!>IarvVx^e;nCX5K>{aSxtE`)95BU z>vv9=_%d2zU_==VVvfuAfG-}r9Us*9o zuTn_$0sN|NzGK6~lK~XZ-wqB;<*tT}e$6N+NQ&-L&Unqr$j>J(d_5J)0XoT$W0vF+ zybGOE%%DvSy0=0@aF_$;1{GDzSw$EoceqXLVi%XoHyC7_yf!K-#Wa#o3q8dj2=`6zbj9>9bkWNTp0xwu4} zkNZbwffsqpv7hSn_-FXyeup%vTS-d3dv6@M2eXM1VASL;-mVM><=nCvJ$eX1zEY^b zN#>X(bcR1*BHuPn>_Y$Yr^O|^dC><6zmKUJ$D5EKOAU?3F)M0g#(DFHJ3Y4Ze@7}d zj#Vt=9s$4pJfvbi)elAr^Y?b+$6p5byvL&DY(gc8}Ti%sQ`Six$Ma zPbZkMT2PHw4I4HzY@|llD%S@#e!@c&T?h5<*>m)!la!v2^uvoroTOqU*k=@XU6AYB z2Fu*hK7yxOQlYI1R0)rZ*WnzRhM3npt_yt|Ok@g^AQZHU)^{f1&H`IRP@(`ba!l^E zV#T*v-+ZMDYEDCRu;)UZTIIDOW1>x~IxVa@yhhhGXKPm9)=xibQQ;-W*vF9SG>tx( zwvpX&3W`s{H0m*^D)F;5yx`il{yD6vF@gV+9Lw0%1)$032TcKy)R1nr(37ba zl4?_UdoKwXsHW0q!iG5`bOM?v20{O><1)sxUsXQ7Nnw6BcJ8ULIPMoJap8>PW6+Br3bO+0bf6-kL4S5oiydlel-_ zK|w^aQr!8VzuA6Xkfm1g^6fu~Z0*(6tY*bKJ-xL)>Ez0h)$?ivj5wc{*JiK_||$#L>oY$8_xPRQWcZ;bWm|bV+A% zC_B5q;^We!xo_3F#39Kjc>Y{38@zwaLv1v>H@^FEm%e{;a9&<-dHEh5hTPJCSqzUl z{(OE!;%(>P9aA-Gu%fgO@m8LDSha*PxUeAX=S|nO8?j*fF;_uc@!Gn&?MdY#_?FbW zk3;e4zHNTf*Nlk$T4-X7ixd@Q;tz5#s&U ztt~JMoC%c|+;I%7Dqdo3-2}PNrEAxOhWOp?i?BJpo}S+y7L5I_zP`So3244TQjACg zM~J~Ua)P<z$*6lKTfMwLMlyfel zJMZ_Y*)sXjpcOBlu4^?vxqDcVW9&g-P)^s8#CjxqeAcDO2PhQl7p6&4KbFrmM$T7; zUn!^vIg=>4WZo{D5&GeK09|S6LRNk&o0d>vk9Y}`L8&{oeo%(|9j3gh@oFJZ36~sE zPXkvw^JI2#$fUm9XP!0Zv09sgit_SlQ>UJNa?#JBG?l~rI^c4Ip8v4h#d znbGB6|4P$RjP<9#wMxnJH)nIVSKK&@I}t^JWw>72kMyR>4H+X?J!TzeX&$aRxAJoB zG2wN$bh$ZW&gEMLo6U9ari@Z?ntgN1#_m;5J?)>L7^On4EyTkr$Q}dAO)5pGIT`qc zT3u#~@7gu>N@d_ybTN&9Hl?j+rXm^F|BRMX0wNoaOcl-25Dp+=0h|#J0jQC+K(8(&{EcmktWzYQ{2>uZ&t4RR4 z1mttv6ms1-Zu`gscCY$YLf0Du>GqYbwo!jA?rr|HHZ~lB9%&i#nnm1zo;?J}s_7|7 zAsmnwuU+%!!*D^pAV&{4fTU*f)&jZ=GxM-L{Wk;}NYBa`$XmJX)Mw_OV7h9jy`mvU z{CNP!aJ?i#T?_}N?mM`pW0AXNw5GamjTvh$H=W(%$o*@lGP_*s^2Fz9qe}I_;nRFH zLTC7DKUtj|x%CnN8pWMVy^6`|)3fIVGFx!gl%ylpgrjo7#mjFE#Z=42nni&LjD)E+ z6C378YD{68CHWEreTJi>l4gsx$SC;`ro8^`AjQoVs5#V=(j(Az{nZG8g^URcJM~|? z@c~x`r?bk%6H6HL5$D_H?&Fu<{EK)pKbU<|BjjSov3PF%#FLurUH`YdM=zp7!@@>lmsg15nMbQ8PeojJOHb!NdWT>n>I+5rGCO8M4hnz4srU zd(S}9MzK349)CCuRhA;AxaAMvEW@bw?~02Wk=R^0Bd0Ud4l^wUidLUKt;C6JL)w-k zs|y7(`OCZf8)OgMJH8;I_}HULm7T*@gvhj9Br{??ti0S>xX^ubY zuO)v^m+rP;1%#uF6QV2D`3S9I2BRB%gI_^2@*inIhQX~$K7Rfz0d+P?WT^yKDA?~^K1VJjXt@Su`B~;yu#ON5jeh0pWIDFbU(&?pvb!Ttv+hkf6~GuPb~BQRh3@eTS$;Bu0u; zj}NgvitH!!^i*+St|NayOw&jIU5F{grHOk^vlY~@)Ls7(r*4s$@eQ8v{{T#CGLmGQ zKZ8l$;k7wona%Q((b9#X3;H!|+xHC1LS#Q6P9){a$W5Hi6Lc2@7}hS?DT6Y}X8?4Q zdM%TwoU7dt9aNjbMaJo`bF|FLW^n_}M&^WB z1iaQ)Upoc$82bHjvqpPYl$1y&qT@LFAS$(MP{I+PbDEL<%Yf$L_yM}nStE;L!cr}W zWE50GXx9{yh6^bEs|h;bGz;pWvAIXN4}SMdNth7oEfxVL=jfc){Y&3#n`OPa@&jub zP;Z7(yw$IxH$Gdx{*<2k4EyWq-&_oT8fS3%ahR_QrgX1X==MPx0cz)(aSf2ZEDxNEt!Q#+E(?J`|9aw^1m#J0^shTnm@npEcH>0Dv3Np~9pbL?a8C=?0x_uJIW>RdrWxT0; z)EgYLUOm6`ZI(3LVpx+*67q|dy{ODEw6sj19x!x2g|Zi1lFCc0dywUqDB*oJZCbhe zo6!Qv2*1b&NFSkzo7%)A&8CPZvD~@frccZ&i|#l3l%}bC+j*vidXwb(wOZ95|LoMp zOTleILK!k>a#;T7IRWj0&!<9iUN+jSZY2O~QXVKG3 zk|W2^z-E-_r16d7=tlamd{_ReVPUPzJf?H9{4t1wOwJ2byJ(?PFJHU%hYy-^qkYGY zLzFdu9QSfEZ5w}R#s>ViyjHzT+_ls$=O~}ycp10>*VzxE=1p5MYW(%^Kn9^~O ztB@;82W>Wg{{#V7OSXWyvFfoz)2JrEw@oRQ`DsV&5{Xc6PHZpsUa?>RU9{&L2)=8A zh_?lzb-s$dt-rGB+Y){hx{6jXna$}-`3Ffm!oIF8FN!m^hB`TeT^P`SeM73BXZyW@dUYEWA-es6e>rN~N6EeAqADPCM}O?xif&5}hpj2MG(n`9RwHRl`hf zm9SWmI0@89+7Vq9?Xcd_CO2#niX&+teovN<6wdE|5iPGbz1!d`33qv%66_8|EUtVd zI-xGTqF;PtH*=;nY1Q>>{`-aC)U&vt;25#iUf--Sc>MAC?HKP7Q~V{GGSBJZseZD_ z`}VbBxGOfDQD7KCOyP3i$AkT`;-?iCToKnillxX-*am(z?{(4rPHrsn+Ug>rXM;&)R=tp&wZXeDLn8*<2`r9d1Yqf0VQ9YV=M+WQbrm$s8+zN?4(z?HD! zrLPa)n4KNVDF99kzq?}gHIT5Zog-pwbWEzbCiVFCboJL|d6&PNXgq#kdz-UHP13!F z(W%l05wRfyWerAEoK{cUr#?Bx=E^H?z`f|D@jpWCUEel(t@e)*9RFs$8Tmc-{(l~B zvgLcQV6^Ayi*c>8d&hRUl{j5*cCuWyvD>t6YbKlv92ds$+~|-Waee@2 zyE467r!MGg7u@n_xJtAB&Dt(1X!r1}ny2xj>L+gbzboBTy{&c+acMAv0>AuuD#NzvQUDDIkfl08za#qje@=o-)$a)F5u~yR)_gS41PeC zmH(6IjglemAkIVM)vF+UWpEY;7j5}&E#q*w9nH&0e*|v)`IjCIJREq>bE-+f8ePkE zms`$0)T>~&%W%!$SN2-|FE+1lqEL7#%!Ujc-LvQHZkM)KeK6U-`1EK)F(+aaW++Z| zOc)uENmscUg4_Ve_Yjs2R3;c2SFJ)K!8!aS>6!pi>pK#yP(;w2@Cejv(FAMEiXa{^ z5)Taiy{8NW0oax-U0CW_9EP9bj|Sg8#LC>2-Ml_Q*XCxb||D3x`DH(G*|~X ziWq_Peae)+ks zM>q!CGs+D$hA@t)sVhU3zGNgJ`A;0F9REOatx6qMxBxf4R% z=C5rm!Vx&Zmjw&Rt9E^vV>as{7$QSYR3)08loYLbfPgDYAJ-O)Nye|!CNSP1=+IY2 zRiqgqV!K)~HUMsZ118Scb_aR8UMcgAzB4V0CUlDDgdq zCn7>0O#31g-NkRn!iRuiT%d-hmXy*Ro%sh+l_=o}k3GgD+!#aiV|H2wP8#Vu>w3qn^jFX*AdJ(|E5Ogsz=ur}cqN9~*qnLWsX>H?kd3h5B zcLY$fMzYLNco9VuUxhMbs93hmmT@i&BAfL}depJc#l_-pp^}#`M!2R!nk%mvL5$$p zK3siuWB?pW@WQ1?#4bXV(rL)dyrwdR za%v9p5nAJ+)O3)kd$iosxUs(v59GyLS*b(7btY&dvETuUxwgayl{MtonnTv|D_ZyW zIKrTd5ZVS}P~-_ykVz^N)v|ct2SwfD_oDmXUy4$c2VkGPaG+!>RIv8?moGon7fD5E1jVlCB4v`a{S6E_ z-}^_xK7T_k5I?8vRphl_d#~Q5yk4WU{@J-}ony>jEV$dMR_z9B&sNo6YdE&c=!3eq z3{oBA-yrr2dR6d^k7q5f51P3t$4v`~cvz~Md!z{;3Nc)Zz$eki?6MDd8%h{(DZMSv z0o#{3%6`2}>DM?2hZ4diBOfJyy73_Gl65pxJh|UaxFyI_k}gcf*&hl>FSH~``R;kV z;`^@zY}_b!F}CnlUPyT)T|F#!G;0vOqk^o4NZL@KSuzkWGG|R2&hp8(UQSs<+~1`9 z5eO3rm(4~MvEpB70FM~h3m~Vl()My% ze!6_9>#O_sv$@}adG1jTS=soR5C=y=eP|N`O+fkGOmR@a=+tA6(MTrkd&*o=E@p+S z#H{>C$Uu_8>an>M;?lsd#fT)f&pOUGosp-}oChDTuV7-@Bieo$W;x)>oKJk5iN9{P36Ocfx7=`>{MJgRMG)GV%Q(tu;YvVWHy6_y8~ z_5x-dK76>seE%%Cm|BmpSc&a;&+QXweTx1%&7#ue%Uh%UzI#0%l|FaTSFYx}TKiB& zdq1Cwz<}L*bav>)CkH;;;S{7YwtPsymmmwLLYuc0o7&hp@30wL%{g}Ul)AfDuRc`o z(Bx+&wN0Kqu4~`8)sgFYt@PiW?y>k;=f#h&UJd=oFk=)ByR5XKP&)MQKdCZrv>oNC zD2gPj1_jHu66*<-H3pVGkN@RD9w049Y~+VfcI~CUud^~K1u!IT&cB;DvE*QE4ZX0j z=H_(~<)X8`5>fd4`BT!P_#3OQeg~olPe7;z`VaDiCy5Cd8Vkmiw{&vM1NYq_*)JqZ zfwZhbwTZxs2%#EYkPkQ}{Sp)B1-}|OY*=mdIJAfb#jx=inXonmCT$B*@=gbf8}K2~ z9*hc&qpedL<|(QnOA@q2Dn9N!C_&PB>hhNgq4{WzCr_Oea<{i;^SU*mMPx9KXwXPw zS%{8X>M;M${47a##QZyKm(@bjWc;6ZHCOAvd2!UP!2s4cFiQcIhIaG1?mKbsm3?t7 zT8_a4?yKwZk7QAdKehDWsrxr?N=6L|N2P+TwlOw!kn327E}#8#L%j?W^^k#$*TzMK zP5SvxS>n@cBNy02cJ&&e9`dBhUunh7+qWE7y!(74>2B)PFTh12j+WUMXQ>Z3C|%v$ z1S3RKEaO4M`^98p@d&Xe3=Itt01QmM*$q#XusI}#lp*8q2f!2hjRv4CFezN^C0b=M z0(|)Lr4xJ!sGWQ3QDiwz0C8JBHNNMhNt5u1T&LGbA+hkQyJK0i8jmC5a>JHW$+L^% zKHP)TbVB(1HTBb&iF`i5JcT9HEX6Ih?$r+4h5{Ybt#I-#?Vn6Mi4mt+$mpCzR*k9E z7T!bIiYqPd`W{Q9yo4CxC<}zi#-D|^I4Dkk8LiMFpbw;7JZd)VB&X91(4YwD^|FhO zP901`k);KD3f$bu`@0HVnQ+?_Bw40#fYlv>zy%2|yWv?WDntGu_>P~;3L}vZsPUT(-9F@CQIZxB3oS=g-;&^lzqquX#T9lAk{qvV?Hf>b=I?@Dx~+F zdAj!{TsPP_+7L>k?D~r+I8Cj9fV-UlFIV;_zqf4EqbPTs{u9nejr)WU@)G#Is-EX+ z^{*dCFH0W9@dQp9?A?j&BM9*NhVAU$mSdeE-FJAZE}6v3VMYrIDX7KcHYWEt3m zecr5DaktL8Em|ZspQJ``3SViY5t8cv^Nh=l@!M&dj5vws0GegU&fZlOPi+n6y`a?< z)D51Znr{a+bexs(N4Eg~BgGSy0`yYm;O!_XHBHnL68Gz&F*l<+_-^ztS-Cv4kTV7i zoa+|+1$dz3jxeIiN;jCv zHW^{%zQ@MIkCq%!?%1ibW_d%4(n2jqPUTp99HI#>37S+vJ>m;l`w{k2k`CzBm^a-H zv_hIk8jMBfo-#iSKhFV3FBZHcTo3rrB63(<^A&-;ozc_c3piDrp;v4rgA-i1C?~J@ zd|UUnn0cB2L+@SZAo&Rovj zPx~qU>PC*8j*gr?>+YssetFX^ex6PCy(TpV?rGvnSz|%HmQ%`5h&AG`Ct;4J7-0b6 z3L{pQ9ykF8eSb?}$2u@{g9Z;~n2_VhyXWhX0ilYcnYN}tLu=4yXscaOw#!v3&|o!D zRh>NbjKRRA)-wNz=CFfVQ&g7tuXbB$XM7DJ))pX6R zqH{*hvS?gmVB;E(Q}%pWb37mc6_d>Ik_c=w0YtTMz>;shV}JF>3ufbelY9)$?;vv# z`#vJ5Vt>)xI_K&=Hl5}%auz>l;L=Os6SdSZ6AVy|>l)smte{E@q}F2Y#=x z2J%IX*Q$KDzy1|3{QH0JmmA&HE0aIG-=`VB)u%(lb(^Y9T-T+6)A;NOP5GH`hiEzQ zZKV@SdwjFJV>~{yMUlL# zPRHC-{`Bedo;ce^i?7BH>#th8d1&Wp9Xk{aOWSdMue$QN#Tu6i*m27GM}1B<>HKZF z&d}z&a;&FCb~{t8-i})v-@2x^SzwqQI_dS3rXd+4r{hd&-PsKDS15T=DJ#^Ok|Eu#x+8CJ z%~jW{kOT&3X~pf>KXzc_%32o|wv&|=+EqD@RWj$?-g^-V&)QY1lsJ%^ zw_852arll_$F{x$0RdY;rL*!`1-)?-tx>-&-QAN@upcczN+t?Si)quI^tf{T$Bcv* zuU;`|GExPi;x^g6sF^8~} zAg667=K@T8pBVFM*v0g8zL}o2cKCztddikNHw~)YQN8w3g-z^`m3GEXMNaoKQZB~q zwy=Lxt;vvu-ai+#c(%)SbXi2Igf2u5{BV0EdJs-=ocCKp8mSt!j3`uI$PHc5nrUqR#904BK zsZXB})B;QcTWu5H`S_Y|MnOmXiPS;Q(}@9M)5}1kN#LLCU+`uq{p&f-)`+r|RD9>K z!;CKS5CJ_k8L=0wmL5yrr*1TI+vm=HKbNmNQ(fC!e^A)oXH&BV+q{cy8lhroKep}h z#q+m@hix*n_4hv)o8?M6UBO*L!;7A}7FFN6BS&}83(lxp)tg${VWp^5A^bsT_7*u) z0;)aQU|5DDXBS-;I2K;XOMK)~-79z=mDzmFCF_8X5=%AI6Dvx?AYhUujX_j>3(9Oc;b|4-A_O~URovP7N zMf0k_hRVuka&vdkY04yV|8H~hM8!`v-vqpldK25|h?KN_6rGzGHzH6oav{+`N$Eli zDTO=|!*dre)};JJGJ1|RrSS3d8@$Wv03Z+dDaI_(-i)bSGiBz?OUyOCgd}I+ph2te z^j1M0R|N`LIIlLNQAsl@Ea8)krFs8eY;u86|4f5URw)BGYKIK&cIj);OfWQoOK-}Y zISE^iK|GR=-9Vfhpb%6P3yZR8J4@vc6!j%*K4kNJ6{_=?NbiJ zC)=n_*f3$n!)qP&bo@?Lxc9sHK6uJ6lt)O`#=L;O%aUsfKNwyz-kNWwo5MUMN7M z_mDxD{8mO%QJZI0+&%V&Dw)|Jn(QM=0P(N_B%4;!Akk{ggm@wH=m2VZ?r?sqNHgdR znlL^YO(C^~)O4U@Bs%+s3vj_?IkHFB&;$3N zf1pk(G>}<2xZ#QJ@(l?Ixxc{}6*z^aNIOt-Z{@xAfU8LPir!%=#TRC-k39WD2x7~| zSHP|+H*DAmi5VfE%nS5lyy~eNfgY0e045|s1L#oKvCcoxj~-t7VH#^yFR=|s6gIC= z6VFtmu6OhEndecR2fGnpvgqVME$8gqRse8s(YC)wkh~901Df*!HxWc~$zzHFr(Hv; zRLIUd!2GkxfCd?o{v38;ym3QwBaf?f&-u-_2{C)2>*nYEW=8dR^_bz!AL#Gy7Qgvi z^q`y#&Ou9pM|2M@w*UBr>y3wB?+ntO*|+xwFP{|&P6&zQFum_)7UI^uzi3tQqh%-w zUsCPj@s6}L35$B&PEQ|}I&Bdlb%e7FgPC9(N?S{>SojSkx)OmXk)*(#>8BABuJ(IbvqLJ$9oTIEA-ey8zM=$f^M-Lo$gccv8bggxR z&T=~wOPBU*I_7`u4MIxt$>z_0{5)fb+9MLLy z$vaiR?p*+6XsWH|w1MaKqZk3#InU=z^Bu!Kxp*fk$|0N8E}+Ja~F~eiRZ5 z-#zRv!1EHTcJ0~~o~*BcP7!%S@jTFuxW&*t+=uOxMCGbTvDdWCn_y=*ZT9TTl>Z5% z`(P(a&&V*pUd5%#)Q0%tMGnj;Ry)dZ}xT+)Cta^Kwwm95n)>G%#<@bxR!4uRXLl`CC z0rxCUM(dfU<@}~C1Fw?wwUOiE)RjG(-3}kr1Do|q?RbH<{~aV7r&=|!MNGCis_PCN zs&hw{9fp-;)B}Z`lCRn?xAowjm?k&#g;B#a265t(yvOR^n*seom#nMP~3&TZ1=p2ARivJ!AJ57}@m^|BjKBdMmJvyGo>bp!AS@A0d^MGY)mg@ zup~`9-esSm_Vm*pa=TeA_f8(M(qTm~u7?(4pXB4V%^stzd@g^)(VxrcMjdB1OZKcX z=Gkw{4|zL+C%+j5O4^N#3}g>c85Mf}fV(7kXhPDAUyE762M_90EF8myRru2o1;gM9 zN;JpW>*XG!sN`HAj%he!n6Y+r#Gp&Ovx;_Q;oFxleObg@z0@@)vOLi4fl52)OoU8? znH%uoiuILO#i;R9?whEOaf%Am_?hh{6R6Ko2gwNE;_dG;UYwPyh~t``Hct@HBJ!sGEN-gCWZ%pdBX@T zx+D@QYWXIGU;hu}%BcP*yWhw?)`h9g)LA&KlrFZL%^ve_ zD1+=M5@ui>VF=BLmC6%H(Ck_w;;W<1!C$+*fuR+y6=-8iwvPL6A3%&=6Dp1JMd@O8 z+y6h4Gd2sK8D}?@c^=_m#6ZC_HKx*I>+yhI=s>z%r zS-VsZYMMZLwU)&8|c#|F`OL#-TX|Pt&Q8ZC0zEvr8*+a07t@HWtLRs>*K!CABdN6jL|zG1d*Teimm0 z_!Jzxj%2V?RMrg{f+&1SqYuZIUrdPg(*d|K(%cod$!fAgYVP!6wYzLfagP8i%gqj& z#|2|Eb8O3jOI4)6Lv)N#9G}k`SqqJ{>zy;1wAHjzuQ2w1jmJ855+8khe&@;pFiExL z@0Exq9uU|OHl$A7y1bdl*jJ{dZR`%ss6*b`Iedob>G-^4YQY@Ci9evJ8y6S9DyYs{ zx%@O|-66z|1+%KJ)xuA)vc{T@!wXueii*<)fM0 zJV8GxcGgnMLAQ6kX!CM<{E_HhiA6*8Ki!&Dt=afBKDv6!=S;j`{D0KFc{tYXx;{>m zQc0yrqB2v2RA$OdrVJreQXxddV{R^G$dHgJnWqLaB#O*M=87mhA( zoHCp*onM|Vy=Kx}N;g02Otkh?s3jZzF5BE=)xnIGTS9kX>;c_UQasIXQ3=5B~Ip!EU{bIE?f;)A5w_Kii&hcWkMW)yO z2+@VGO~MA_3JN*iBOp%+?*?P`IL^b84bq4HwroVOZlFWWy#T6C7tNhC^0sYEXjRN9 z)HRSF(Syo*7efJD=1D9K?i@!~*FKa6@%P(+9@6~=9NnzTc`CQAQ6Htdr@6$XF!IWV z6$PfJ!&SFVMZ`*!^Or8+y~f4J-y7+iX)>*ss!!@Rx|4DE9pA@v0g_Sr(_s(^Fulm- z!@L7v_bF_9J94lwtHiWO28UO#GGC5v`PY#nEh$Qg`Qkd?^;w(*%*=6SkmBR8`{v`vO~es%d;z2%Bj$Cl*H$!<|LvlD{=`XP9yBcuG>tQW5Rn&n>( zHWYHBeqB~i+>3+*B>my?G8mS{vV$-%71#LxypfyX4Gw=n5058YPIO7 zEmAq6z9oyXO%&~De`vd79d@Wll^FpXCs-mhbtuy?JSwlIH4`h78IwdkgWFnQ=JvJ$ zCSqB$2FSR1lY#K!(xt-%&NPm^kdRxqh#YOn(xsL#MAZ&$y-qUaDLm1h#H%|5p1KGS z#?BFZo=d=3@8j<6Gb~PS;6XXZwMll0K!n~FQN%EJq>cuf8(g}4xn@O@K^vSqZo?o2 zg)5V`3)W5Xn8FD+lLIucYlD4`wU0oAChAPQK>1jT1J0;uYx|x`-G|zcbUDdQ!s8rk z)q>%Dx<^+oT(YpYp0%98xM^;AnDwCaY~Bu_Y39I`g&*#vW2%rSSzBDmCuW(lK1 z^T`dkX?aT^yHZZox4A=5_|U$;gVne!yj;$nC7=P;>SLhoBUt$bnmD*&l*5mckjqdD zfhCJKdX9deM}sl%LvEY7C5i?l9>m-XCRDY!w(0husldLF$hLuv5oVQyKkR>q6%-ab z*pY*=F!tg3{J8vmtjx^5(B%=fI^4Qg38aBjh15Sz#+=}X|4sw2h5&h zz(~k+gp?72NP@5noRpvoxXHmt--`hZ6dpF6js^ohmJE|zk{1E`*?LLmlLga!c_J?0 zvd7QI5CWloZR`ldH30N_dV4Pdgo2UU0>3%mg$ouOc5oo3p%tQb?K1C*0UyFk>LMm6 z$`pD^3*a1NX@s3I?JDOd5n9CsvGRpocp`77P*zR-Y<# zz=n3AYb0WGqBaHj_fV&D`xr`-BB)W2*%-{)1CS5KE{@pj6i*LJ`CH+PV|jegdk!B; z14vl{n_?4d6NaYqz+od;Xa1 zp_``1gYe*wy*Rd)g*!&|Wu{-zkK08&_m zy}Jh^Q#jrEL*YucMFmXQJ5*u&T!2Jx)FVqoLPG@zJNHs7BZ+Skgr<;a#sJCKq88jb z#BSFGJc(cqxGktfsF@e=UlP*O({r^1i7zW;880wh=R79xKhbLg!U)b=j{XK~2&4?0 zyeEMk7fuJNnwnhOw%vf{=?wPh>Ve64#S^l#EvO?E=sjUqb^@mPWW5eDIQS;71nf-T zjv2R%ke`oS-HsbSG3`bYCQ`R*9bbK@RS+F`u z#r4t%&gynykHO5mdnit|BQX5pmX`$h|1||)qT%ZdXCvZ@0`Tw_Y<9K@cVQde3|}sP zJHnQ;G^O$wY6D@390Fav=RIuM(NyO(l6DWF6k+zZ#^J+%57Kud4Y@xzP8 zyckVGDyCvR3XB~2TC&Vc)4)8_2N6IGkxVx4a#C|(fa>qgf#kA^v#7BlBxX}dQBf7c z-YrrW3bN84no)+PA;i_dWDj*oFe)V%+6Rqn})AtX5KsjgrIRJ*`dSc zULC2f`2B(l&khOny+Y8~yAq$0JIWXgD@1N1tZ~9kUDBS~ZLss@^4liKaXfh9}s*)D~y;>s5 zW!RM2tgDmI@SW!GQN_VFe(Fb8tqKX!cSyh2Dd?`zXCIwD@CC@d^y)Pe)O49AS`g@h zBl~*tB_f}3@l1IQP|@^aF03N0LHd}Eo|SrXSa9%OfczL3X(vGPz|@3o*F1T=Yt$zT z;H=2P!a_{m;rRtnhIY2Fu+T|Xp#3bM%WdGZ;T3TLbr_!2kPR;3;o*@V3!BAS4gF2} zAqTuO1h)wdEp2G*a%lbX!7s34%OHa1sUmS(dYsEhQ^6FVPjK})0(>Lw;zK3J0Wt;z?&eY z2-iO|R)Am_h&KwTC4Ak}+Bjt1F!nVcH0d|RUQ+liX*z^bwhWHoaDsW=($Z4+^l{ID zc1L}uj!@4ztS~5(qwy6qaINtQvp>;1Kl<>bW&7okWFPGxWzJde1!H)csymj1yu%vE>AuE@dG2(d;mdLqok!YCjqmyt%mOBVof| zP4+{|sr;0in;UkUi&$KLsGA#of@1J6He`Vfw!C`u<#hz-)3{6IG*A`aAU!^E9LoPsYRt}zZ>#a8y&yMbzF zwFieh1alamdM>+mpe_D%NK_xaT(*bw+rLyfmzVED4+5sTk>oS|3j zWZXu5(Zp1`XUQ|08~!GvWF1b~f;p1R9voc=vFcr1Xik#cO_yaoCxdXJ8`F)1 zk?8;za%c_kUhk%Vryr&JNYxJ~_kC2=`~aqJL>)h=ME!_XSSY=ngNL4l2x z1B2fQ4j89A2eAw29igz**N9=PhO&V8Yonv*3sXnEq1IU{wWsL|5G6K1B6!o60YCxm zQFeB+M|29;Pz%d`?V zjXzLmd`Mw|fi;cTf~`S560viFbDYz79=0j)k@N>-4+!oAZlh)F()o=&sW^9de=un| zV;@JA_}OA3jw=~@fSb_0jA1j0bW?5MQng<^V7^LJte|!L%E82}(StV^zzAyQP3^^k zQnKkker`ng0j~Ubd^N0CNomLaK{s(E{1l!kFPD7Nm=pZ)^#&HP#_eq{MZTFO;!^#r}=XM8s3P<+DODw!fHJ%rRj>(_3DuMej>d z<4gM?V8d?{e}O6Uq|)Yd&IzXbPw3)16j1Cf|Da^XUKDYeGLKbhOiEok92$w-nb!M- z-QxpgSKofd+S=^A3t(DIRApiC+{eOLTgei@Y(BO_gKf@C=CUR4C;d}-lkc9@OGr+* z@*qIu+nc80xjAY`y>T6Yt`Fyzn)Tm(;02Z5B)Xv5{ZG&iVsnj`bv{;r8W{Ft_LTJ1 z$6{V1@y_$g52()JExwEW48WN%XsFK1&CNVYN;d#u>&_o^s`(RJJjSj2{L)yxUCdoT?r(A}Q479}H7Fqe&@L54MQKBGb(Ke1RhXS1 z_zmmwK}056{#Ivk#`B}QIVB0}tyDvhU+kJi*3WqbaaFpTWrFzJbmR>c)_yP0mLB5@ z(WGCv{%94)jDqx7%eyx4f`PHZI{gNq1~3HapMh2fi&7F2b?Kdg_A#TaohfKT_qm6$1 z1C#_pKhUd2torOMzG6_i*Z%zzMOMXmaDCfwEJ$(HB^Iasg_pxhKR@{H0tP1v49Sfp z5zV-tsA#d3G4#PZ3RXea9z7Bx_d1v#=yQq39@zK9R~0IB;&FwZ^)*~}2>eI~W&{E0 z;8=~%#h|kb9j7u9-*Ak;&5Z@vLejSI<>1$#(kqwpcb(2`Ek?cLEkW!tYQ~foj8}Wx z0E&ZXchJ%l3{Tun_`&@A{KWhOgIaCuhTrSTs3=qa)+hLwia~FaD?A|J_c+7-Bsw#(#l9VoF=QOL>vITb}KC z%N}<=7w6Xvy38KD{v&0Us`WJOJosC>FOJICs5OaICsL$sqY;kF} ze|Y;3W^msGz8A@R_^=dg3;2|fbi^epc$b>lNKNq0$k9@0IF(#%_Ajt+K<3u;g5Lv09jNcf4OOc ztY?13Ce*`=e$owLo*Ty{i&~$bgRFx;m@BJ8^V16s$CT?_$C<3_r);k|pMLNmuWEt! z^&6E>GG*+dw^!9w_i5_So3lAy{=6&Oar;Lfqt~_r0+e(MLAuU^XM;x^5y|=8(vh9>@d7POTAu?Y|;^@6>BhQu@dr1cG5@qjmpM(!J zNiNcK&*Xl7BI{$ri#wMmmThQx?{6X0^(oL+?W;}Gf^|{dBDdZaEcE1H-RDW0|9Qi} z>JRPb=9m1ubmgf8kMfTWHScFJO-8j}=Et48wxz^EZ8M;7WBgzS?Op?((=V3yD*Zfe%g#&L3*d~Wqzc+F;mUi9= zu=g*B2~rMGb`cS2MEh)Cp!9EV;;xm895{eM<1Y*m*MnU z)`2CTu7H@WClvNGedlnA4HY=^7%i5Jx)L9-;?|SEX+%5Y{Lk8v> zz}20$2H)God(iDnQxr?eQt&eR^kzy#;#L$&7G{h*jrlh5PHue0dAK72h3R8Y4-C`k z;miV7@Ft_S?jw7lbFe}~G&3_^PyYWehCI^2UL#XNBAP7l0FMC{UFwU*8-y32)2+^7 zOV5w4N;%7GVWC~V@oD6l{+HsjHwsjSOayqo+m>IrR29lFs6$A2ne!_oAndHr{uEKZ?Sy}TC&maGgC6N1xGB}A7{Iq{-F28%iI5|N@~J~TqdSH2X8aG z2r6}UzL5`fd>VD;>#1!w9jg^9xO+NZgxK+BpWe-VeK_;fgS?blzY9J=MsZ0OG%PEM zKG$`>Kf?0U$7<(})K`%lLU)^WcMRm-a(2w?PF}jZHs|@JLA7OHdM_GBzdKYCqP&+j zpSHBcDY+}|mfvpof=7Z}ekrE>Z^ScdCuP~Ub*#BusOp{}In-LZ$jJ0S@$%UVS16a9 z&*mLfblqLt>B^MLb)+edY0El?v8GE=>o@d|Js%CY^VB`uV_-!AbI6Ou!n^(aHh(r0 z%^S7~Hj3ln7E=+beWs;#?*#SX$9`^S1N9`PlS;|QZ_RiPtev`~v@VDVk_InRbBd-FgxTf}{wTPE$YOlXURWC!Yp4Y^|32W@QNx zP43i)&zxyf3wD7TDeuZ$zHHaz6ka0e@hjA(v4-#5At?= z{7QB6^W(yqDN*8iU0Uq6EGfiFCX4%Cbx%+IAvJjgM_?e|o|kz>nzU3_6fv!0_WV?+ z%z#(9de6wG8P=5+pO_SuxJrrbO&G44Smiyz^rKK6Rf_6Tde^TuQ7HK8RgH>6bv2ARCxdHc2u*Fxm>nydfs)1-6yEMAz>lfC zUmvRJ#`ayFaz8zWeJHV`E6SG4?YJ|2efW&?=LP|nQ8cZyT4`R~U3mNP4E^g-;0_-f zEfHx=4Bz;9(B@FVt5?EYyG!naud|_*UdPm8otMm-M_y7@vHUL1s+zS)XEVFgX_g-1 z41U=~SjVDt>8o$D862d2_&CSzYp_o37uwX7Z8Y%3zC2D#t68j-S5zpX?1%{M8xEtQ zQR+SuLwg7ogE#Y*Fr&m$o?M~KnZ`s}@b#rj8`<;Ju9%K&6^VQFWKGC)?y-if9m^(M zs;hnO`O{{N`V2Cgf(r*a8=2}qwbE!a^Kzw=W*Z)daxGCx4at|{H)fmp8F|NWDZ?3M zw}J-)m9>X7RLcq#Hc3aBS{~Dp{#yG&HP&{lyeuCt&x_{SOmqw%Y;4t?l$oxjzNIZu zRvMZ(Nh<6KNNM&;JvsYJ3v!XwU`T5g_3g&8ol>!ANRHk5EPvqh@NrxjKiucqqhqr4 zdc3UEyZByJ)p3eR1u8u$tI06Z$aBl+P8$&pl`7NJGN)gZ_$@uXSz&Ij@_zV@4cRB# z_qvR2x*99gaZhW=WZ8EoYD?=`HpcDV`Jca1+dDLTs_thAc|Mvex)Y=PROV&do|TQd zd?{uc;V-YunYN`gU-OMGwvPx&4R&GM7**w}N2e!xW>R+BWu`2D>%&KNI+TAzu6VpF z#Wd>1T$d5v!p-AH*h`dNjH^hv@^wV*jN7WdY2omG*3;W%_sdc!w~d}f2rkKS-cece z^7h5vZ50#tb;Gd<1)e>!bh6P=cboZ#Q+N}tLV_dq9T@zPle4>T#^RcR&95>-_ywcd zx2$6LceebimPvi-sCFHvO$ByM%FYhff_+a?4ZJ?j%&qCK z8#Y7@md&wCPe}MMM4$Pof%aG|}5cdtR_0pIHnzW4XH9_(yI zyR*{VzF%L|&c9H#+u~Km`tE@n)pIMl8X9yz*tgLwJ-}(tWfhm4enCBzv+Z11goxIy ziPr~yj*QOqxUb4&V?F9HRn(=KlYtt^yj5~(J84_?6slZnf*0GZdL?yUp$D z9Bz)0lKsP7AZhu@<&0G?>nz8|7W=P`nclF1d~+V<+^Yp4b=fP&=yqnDsQOU1U*fyb zR%3~&49OW@8IyL=GpbU!yPB1hIy7sJO{`c)6`9)@V0L`8B(gC(VZO1%!Y_L$N|yP0 z27_Pou1u2U(C*UJHJIA*F(9uP7au$WJ zdcC(-*KOY{Bogwo#LN;+1p0Ny?!Y~ygNZ%+?NC8jhR7W?Q&R$30E;-lPxd*>+4ku4 zl!2+#h=C8w7I5|DGY!wcVxjav`q{*%TO}DUb@1!cjaQsKLXBVAvA;TPz};<2vv@{1 zLzxsUc6*6T>o^o&GhR8_vFiUb@Pr@xH@d?hj*FXVlGlKrxC~M!sIeM5w(+qCh`H+X zY7VZad!1y>=`_AnO>3Q4EPi3~pVFqUiqG$}tIYglxOk|r{Gu$o@A-@l&&NNVFU@n& z{f(cD?siHaU3*jUu8zRz1NlcT?B9Pvjo=<0_FxNG2)+Llu90V3_)<9I#usVXL*LKr zKd}%0b0W)sPYSBH%MT#GhP2>+MG7j5Y1&@m!>@>fiza(6U}m1sI55n8&!|MG%~&+`7|qv*FzeVs1SY*$9s#bV7h> z(uS;fL!KcP6D=9R^OThxGlKrJJc#aZwMWENmB=+fSRi;KprF+&S3V~!PlPu|cn$w6 zsUTzEI3{Qbx2zSNc!%+Nu|%<5*XMgaKS-3jdBBFfl~VP-(DmBRs|%j3w-l|~{D)_> zT<}th<%k9>!h!k2qaqL@cPundfVF;Oy~y1fYcL)!!))*ZpmM^zz^<7BK3hXe3!+S~ zA|Ijq%NI>7xWMzX1T7Q39KldEBj`>OxER5$R07!IVK`VcJ^X|Yadsa??XN9RJSB=rymh zl}eoa4$M?IyjZ27rNAWl`SaYsZ>~}~?_zKqI!b(EfgB4s$iW|4UtkSHh@cN#27**w zi>;$CfFnBSQ0@ZqCT=3W_;g656Ch*`!4j}_iRu^DO?x|4z#xE|dDr+!b_Hgepugy4 zY_B#o==SUA>nupsxo4_)?xC+He@MrjL!z2uI<1C!DFVGm8ul@AtnGgHELRgtFiDoR zxBPd;#Ks2drG^rd2_tVaT7gMsmXkMvTYo_|tnIr_zLxinzI{7oj2+R5SS#MEOpApW z*c)NWpfB~-|K8xWiP^3Oz_IKv<)-HH{bNqwx|923H?MwuZmS<3-!}Q^1Ri0r6*d>{ z@y09rC)cbVSN>$x2y#m=Kn7$>*2by@zW~?;%24SU{_A_=gDgxB9wZ6|?9xWMk})O( zh9%)`0Fhe~$Ja;53bmzttdyw@h&Fg!868)y#yo~;QJ9p=yB;uTn{TVd=QJLOlGz@% zzb)w%LLhq`@6^^!iBx@ zZrwdP%(K^IIc6l>1)g>vk=h`?Rn?WNc?n!_*XW#n1SueJMu2YOX3dpp^O1Dan$fVV3Ytob!qgG-ijnxy z?=5v%n+9iE zH-hva15Mcpi1y(LMcA7p66NXBpJ#+aHZQJkTkS#P}z?P*gT$8rX4WJ`S8%su?^`-Dm5z5&w;f|Gj?>%)z792}q;YgpWf z;6dYbBEpzBVt^DyR~{XL?GbjY#5EKa8O?7>9@~LVuZ;qYkhMxMrXhfVDUCsrIY{h( z^pApKxyJuv(Ia&@LG)dXt_ctpfW={h4^Rvgm6Qzl<%okY(#=7z$1i|imkZn*?6#m3 zRDq`uw8Yr9qLfQ=f86E{-2!Nq%s?WsQ% z$iEn|F$Osv8(;r|bfb)e3iu$gmA;3c4%MH5kcyJsLvT1yUeh0}z!u;8=Y$d6p+koz z;mtra_P9q7tb74mQ-gyH*h5+Yh3}Pr@(a5$ZXqw#CV zV(@&ykH1`G2Uj#GH}8TN1F$HDQD;kO4T*RmBy8czf+Y|kRo$JE( zE~LmarmWKcn$;2|CVOK2(A}TIUAWdJA##Ip#V4F5?5pQvT}Z(;v33d}H@)A%a8M1t zH}-xZ%m#&PI^K$liR}yK@(-E9HA^G$wNbmsRXYXr{;Ho}H;T~9XelE&T0qr-^E!_2 zJ)b0R;#Zpj%td0teP*YU@6>_ORsbRsBu+hmwu|Owr^VkNLg&KJGTa-kIU8G$o!8mlzG@x)#W-b;+&%ytxenOZK}O5ie>7prhB!?t&3 za*Wn|6i=2D_ZmzSVH;t9WCyZ0#>Hd_-aPFTJz5GC3yWB&Qt<|ALDd1h89j*-#k%jz zw=c#p{=ETCqH@qGyY{-RaL&cN2VL>sC+|@>Sa0c z)8UAQAF~0k3cRf7!ansyITUX}Vkmx36hIOxOZff-R7C0pyw+epM^>P8ICflkSwSZN z-vJq@l61sA2({WN1{5-Q0}8wDsl%cP#GVz{LK4;LFrkAo(P)3 z-d!pE9tYok|J*=c=9B+dH91j4D8ylv60oj0>tIpn;F z!ECkB0W5-eT(bn)TcMpG>_lv_V9tcbC&6cK)`?wAR1~iG$8q6-A(zzQ^ab&ld7bXp z;VVX0o_7_!B2}=`SNg1(k!$`&~YVUPe(^S3;g{xo0RRQC~zVn zB4B*Lz<3~=q&mh@i9ZXvAUgOh64efTl<;og#g7^tJxvGV!afW*)^F~|S9}ZR8pENi z;bkBU64)Bw8b5<)4w-N7E`(Q#!7vDW-ADLt5g^*4^)wo%?*dmh_bSL_gZ|-k_-Xion;sM!-oE{GZJsGiJ>@@sc ziWCRP$CsjM6ZIOmAqas-^xE$wJpyB0hcgX~D}$u(0#BtSBsQSvO?iRlB%ZW2U2`A@ z*C1$wuWzr~2IU^gEak@8|9yhhr1t97Ax<1gjY$ukqzi}tSYpjY!0HVL+$lNl?hIrx zS0K1wwPwvFy;Lq(kPRLjQb`WD&^^`A4GQWl~eFI(vJmkW(wH;I++@EO}5*eAM>wumJCbjhC(GP>$ z3=E7V*v*JG8rwS2TEq_Le(FPFdou3jiAt#6ba2BLurO$SYzKuEk%4X7wv2B$qy6n$YiD<~0^}+n5_Pal zoGR14HXp~j{Rj*)X=Ulq(ZqpjkYhbwGIp0cx)=gE!rwxNxRAxQFkQ6G(aJ?95<(rA zIfqJlgQ#!;4dLh0CoiF zx}CTwh^H)t&FdOU)oYNhK#JdoATJOfZ|*CM#jFTgCSl#f&#MO9LV2W7cMM|`+#9cR z(rz`piXd0+bqZrzXR*P~We4a3@Y@bF$xI?!u3NV0rgpghi781Sc@v2AeCmbbui~Ii z-rkGh8j!Ds?H`%o!_|sPH#RyNhg)Nhl+*#pfeS*4V1yao$1-{n5chZI|Wh*xeT=t!9lF>X#^h(&7ISU{JVAr0&8%;AAw8*S_Om?$fJnj zHgJrX*?uUW89YF!?&2hJ-~PWS{dAA3H~5`QztBj0dBK`dK*wQ?usGD!()xOB&QeRI zDPwS{z}QQGIR;6-12F-?nDWFr-_@1GdtvR3UGl+1=|Je1VLniOMlW1w1g@3+=>7_t zGy~R)oCoKOu)M*zgK?h-l(t0k3~311v0Lg};d`+k9nBO-W^Zt*__ppM*9Fdb0!SZU zA)7}V3zsh5*k{%WnM~L|UO-ABdIMosQ#+S*f2ib0|BtsGXw5^|Vc|nB#|;XnyXGG< z8DBiOe<578`yN27U=4aWD+l~df(XAPIQaafOAEmi!|yEuk`wPc!d#XL4CXq5zY*#L zG|L$Tmis3`VBK77XeE9a0$89h^7s6vnV=sb zvqI}g@q?Szm$;(hnKRejAdoSJ|D!ylr#haAZ;7j6^A@`DvZrWoYvKQ*-();xy1Hv0 zshX1~{yQ(L_{+Hd0x|3DpwNzCv#;Ou_SaW`GzJA`Ek4X1(jg?*0?A^wsW|f_cm*>v zvpo88V(S3@Cf2_NNwsr(NiqRaK98ol2JGk8ZrX$+hJd#F%H!Jrp6!%r`>hcCZe?vVv_}}=6PUsi27h+(4?oa*;UiD7G(nfmIQW1lfnlC%ZrzFEs|3A_+vMmLFdYzp zy%GE_X9T&!0E%J_f$dPIfXfMdV<@{Ldp(Zikb?*o8BA zR9}c!RP6W3n41 zB|eE$)0j*#9`FhX_+f}8c`ppCYzSAAF5mHtSj~VBhMM0PX#_tSI#hgsy+pA(61+=E-mwnbO;602aQ&+tguBfDtwk?xEh0=b z)Qr=B`Qf_HnS5ZCvv#7M2o4A3=kM0N97q%$5&q%fIen;XgkBfz%n1};r)1J6qGu*{ zUHEUnc9zhgvLR&mUWd~V+`*95^!l#S(zrc)4y&2`UDw}GrHy|~Rqgu+;>eacs*Ek9m)+Vn5aTJcU`iQyzFopog+A!hRPFa=nu#4R>l7 z(_1)eZ^TefdmVy@`UjANeE~`lTA-Vek&Hy%`Oghy)yT%3Cw*~g61E^_9%7K%If9E9 zS~}zcl8>~y9S`nibTrWzoa?TeLPOGPr!ivrw=8#Vf(C*Kz8UFUz9<@^5aK#XJwpcs zuOxYPp)(NqLVBQZ>eM!e+uV)Sakz0BH;&{qHE@g;O!QUCY*$i9x^i{rZjl7 ziV#*y%^DdTybgC&oGH?k!DfiOm#%kk(rUvkyp4|or;|eCNK)#qUBBKVH#fBcX7I!@ z1V9hrd&ar#VugyIG^+p(0)T)Zhyi|Buq^m4I=x=nZ3i8g?f;2G&X44;k3dOZw6{m0 zOQgXs;-vIT6BFE+#>JizXtV5$Mw}9Q53~M_( z=b#0W;1?9snDvT*Fz44pz;KF}>U)PzxSSonoHmFygZ%hLP>*eXIVy-J-9COD^;psAn3RyYZRc;p9K#nE9{uP$+w2DrX7gyr;BJ*e z{~hL_YqQkw?)KBFz}ewdhMa;XyiTI@ayfP$F!YAX_bS@7UAuM>c_kE1q7zQIurc;x zOT~c`#k+Oud6Hn`K4Ame^lfq5wJ^{H$o3l8u*6|ibe(8!P&PhNsh8lxi+Zr#l_RXd zFrX1B0Jq_za3~OUhEqTwO?@hr@3uO|9$d?uVq!$JBy=dBP98%9P_2rZ8b<6gC~WrF zqt)c>tIiy5l${yfhq30lYnRs*I1~zmsmE;ZhH+Q$iAcZ_+b}l)yFu|K_9(qO(HbDO zVI7I(M)Ad%85AA8{29&|%+Z$4!R@;L?IT_sRN78oU@W+(0SI0~VE`acR#+}+ne4f# zFonS}A%HL3ggKlpz6RzyN@#3@jz;gl(buqrLwKhdEx87B6VQDi%Q=UH|Hbop;u7T$BV*dm zgV!EE+w%PRbKtEJ=qHFc8E)1)lV%(L_ssMJ<=(IWCx%22d5t_nmjWv9Q0 zVT%cM0CDU?lniL?OLeTh1sIc#9zOgGI$bzo^57l8NunpN8qc%G4N56L^*YdQ%_sP+vBp zHUo_=0GA!d9or~PAqTXvc!Nm3Ceg@jvnaj;Frv!EaiEbqfL*2rw~(s3`cc56VXc-1 z#1s~XC+u2L>QH{V{WP6bKrQxrq1aPlIkFD}8wLs1N}}o_=N(rNtSPEoHz*!v`$$=e z>~GQWkTjTdJOx?)<;OHaVC&I}&AHY5GritlL&Gt&bYZS)~>uq;7@7aa=xJ-~)Q0}q%F_w_OO!XgJ}hxRb7+0qKr@8)_|Jrk?S z_f03sd`&}FulMAv9bQ$u#ogG*gog)i5L<++CvH+mL(R+Y*&ae(89kczO?s+j$x+ZJ zHkV{?yIP$fz-XukM?nI<`aj#k?Yab4MenmMfQ48$l{eC>|Cf^1HEn+@X}u@5uzY-P zc-vUhKM>d6sf75+8n>fI?|N?4)Ah#CT!t3#nxEg^RQ=$Xm@JnLmw{(HxQ*f=)cQBX zHB;opB0s5NpyUJ{B_>O@QyL@5$1J!l-N18r5N~#b-1ZB2vPwI_i+y>=0X(ftJEOu5?sJan(w>vdXfvmtF1|rLRuiF}zqk zepOrB^J;fjXtt0MwzUiK=3^eI1-QK!qNC(G4?Tla6+4vgJ+M2DaGD>+?)v+Qz~00# zk&>-3uP0RKJ89y-anI}dg$9iqftUG+>F8bm-)C~!xx+npIdbr;w1RC0#L1W>G{e8r8X=AqA3WVA+E{xZ?`js9(-Z+qxz z7N6X^^Tay?6ah?2T4C>Ua2Jw81u73=0zhP28|q42KJ!nX!#P5fj|Nx<*h})_#^L7S zQSMPpm4^P6Y-3%#(7>QC%Np~wb}>j~CwU@eVET|OJ&5U!5g4pC-KoPM*-QOhN^d^; zcs@WaTi`^7?g?AAN(>)niB8x59J20I*m6=9ituY&Jy*|J$h6`A0jAU{FjB$WbJZl1 zXY*z{UzE27u&2n~jJfX;G`pzAWeDMf<120oaV+aD6}pSAn6fW_xy2R8+^?A()}Kd~QAh4+N4d zP7q68+cj{eID$PC_EU`ytu$b8Pz3~YkM~S|McCNq6m^+f$Kgi_Pijcvd$6CX08Iez z)2)KfW??;9l0)PDjuJ>~LWRG9?jGCc^&2)A@ZU}PbNGaLerYLHvf2L*d8}?2Pa{go zXm)tg@7S>!t46{*bCxM$#wy~$&pv}@{7fq}=y&(LMXy}y38Xt8$XzW|S0 z7%$y@sZHTh+bSry6t2q@czoGw~}3P+2xAwmIo@LjMOB5re7re+L$A)#x?We!Lt71H$I?ju#D9i zeuyU{8}h*g{I$K>+AE-XMaCI1mj-gs^Y#HiE-c%C?GPLCW5-}aGcA_dhNd_{FSXaJ z1#=kspQ;14Z8=bntY&3pWU)92n2WW33chQ-1uirt=GF0A2_%h;=dw+bp}3w~oX?Wq z5?(;q_g%+CE4}i0)7GuYa!(ew;Pxf{->7RagH=Pn3F$666a~wt!K&{uczDiE^`Rk~ zi0Gh1!?6foOq`8iW(u?o_m)G%%(HRZ+Tfq3pkEm7g+;?{%$2gOn&-}sO3Oe^dUZ2T z*dL_ycz^kL&3{HpOZ3fidX$*)7h|`KHggePPO>fndX4pN}{_tI`1jWO<-y6h5)&m?Q36pk44+t~-gV1r~+p*q!AZUpu3Vz8XY!6#d zD*HMe=>p>HgC?6q{h@<5NR`b|66^vM6wM^ne|P6IKoPK)55|3bA0Rd%z2P3fZ60)j zvH7gD-TT5dVA2#YrO{r7Y?e;#d-n^Fo$bWmfB?_Juw(H zDsTgC%P5gAFzx^)=fN=((7q3Rd#yIE+eEBE9;Lz zgAi3fasl!~8a^S|3R`=fyl$8U+iAdN=g?$hm12brHHi{}YaSpHR{t4msTlPnN+)a` zaC`|~!l{ zPFzt6m~wzi^P&Rcq20WFn@^=YOMRI?*1cwdO0PTDNJ{5MNZS{(N;k->l! zL8>5t%u7w}hP+Ph1K+g;o!qPIQGZ~m-*%PN{2>9kSZ_9INM6!$?EOl6Y=(;khB!w7 zG2&Z~p;5At&X=&PSVrc(kRgrj zhF<&Fy@Z4^yhVFq5Z2k*iLsC<&A&ewKDKzTOG=FN60)lB#qk06Z4XRsjPD<&$0~A7 zg`e*_PA7NDxpwu6dc@_hZrm6pTAR))@wDh~jW4Jl&XBz2fAPJ@^TVI#ti!R}$sN-l zdU4_1^IX+rBq77ckHdRXhO|P&y|C7sOTQDs&jlnEZ$6|VV$w=udtll=wxC>$H*DEHDXa&wpMGNncvF8$ z(o})Yk(UT19NjveCy0sIqaXkZuRmHyDs)FenZ z!JO!@cYwF;xo!qK4R+6kZ{QMzn}}OjctxU~8o*#Et>N`)l6f3=BdgJSjy7Pnb@)aF za7fqbxHb!l*Usa%=_#`8yGplXQv z+ln`$vZW=r@rPT-Nh$&Vep3On3}j;<0aL~s(U0gYGJ|6h0oz3M`E`W5cx0_~AP$s6 z=AW+S)nB-JCoB5u#qFQ>Av|sDYTmCgfJ?F089UiDk0Vl3TiFWE0_qj~{yAoEJn^d@ zd<^XC1DHWxX;b|5e~~~#l6QEl0t)^dcCGMEgx4RqJeHtr-b+lRFQ->g&*~5J{>cSg ztyiRsYTcZ2vx0{12+WgO05a9}^_5jsDW#8zD@N{kE}VftCOrsXbal8cv-zT}PKBEn z?`@F@*D7ADGW)gcCI?~x)q)|OfaRSiI@KvJWF_oh7Xe0X<_bH5N`S_M0ZCd6Y=Dkg zSrPXYAX4hYQ2S-L49q9Ie+(q^b~+&5vQ^958txCc|5#9K(7Iee`GK8+Q#;`mV}`96 zf$f-irZkAPn1gF^^$0{?x?33In(wBN!njhxOlsv4(~y)EA7j5AQ{78MbecwJ@jXnVa3};OgRG1{Dxjo z?rWswzZ26C;Ecq$32=~Zsy@xL*5T>ITF@I=2N@PF#42jV)K9c$1e-+1YgL-|BtL%? zjXue2B*4;@D-8Hv-9TB7pw``yraWwXG7s3B)9cV+CT|1%7}8yBBH zVC;_4?)mNQ3EV&-@CYLrQW86VCLHOY2VMqmZ<5qR40{I$HL%YFC|QYBMo;f&aB;Cp zJD{TzH?e-3gB*FOHcQKZpmP0muki6bw6xHKopxKE3f7YJmVHPQ(ACxb6PQTW9O!Y# z3BWjvPqpem#wP5TK_Nv4heh-;yi{~#=?%(FHAP)jbu$QY`1@;Pk-7%sfa;A7MY>7L zz(69Q#ZF(#Xz+9($LO*B`x-3aNRmAoKFVr(duLa~G-y!;w(xutCF3UDfp@?Ow88>K^BQW?XNdfSEdvbVg%5 zm~T~$qch$4%U`eZWKrn-d%DIDG*aPV~X?jKME? zg3#RPFjQgr2SWZEflU_-maik0u8T{1(h=PF`64H=MC+2vJl)Gp8y|8!CCo9Lk0W^(=gt_u+wYiBw z`_CsM#XwsRa28U5=8~U)lr62WMh|B=ZaTfFYdm8a-Z8-E^)fOt5*sXS6BAafHc9Fk zK3X_U-@tAjs}ov~uwCVc9>ou2(+0m4{b}{t>&yYtLPA=z7&Vxn>PS#l9SgQX{K-~i z`5@iEK!S}zX>Wyh`m<;9Kzc#n;R0WF!JvvM$5T1S;36wQCLS<-^k=(ddJ7u?6597D?etlCu&L2y>{` zt7<0-9}aj3pe*am5HbZPkX4VzUFq;vloHB8gl)3mbh&-mnbtTAFgF7cI|%bbs17w2 ztx62AYy*j>D&a6_t#UoXzI@$^lWdP5Vjx^yU>n501T!MQh9l_r(E=J~IlT5>=65pi z*ERM<|4E2&>CKXZAFDLdGnmxV)m2CQXyB~*p%~)$!rP7P>F`ldMf)(Pgdq8H7nx}> zZ5^!%-%?GH>qVIrRP(AXG1K4lzMV{R-H-r0o-G!^WEh%3IBg0ukifKz@Z zae)3*`(uCj7SiEgSHa_YyTR!L9i1cCh0?A{bR5Jx7NtLJAOlBjrU!T_c_`~YRtetC{Oh5RP$J;RDi{S3 zeK(Ipn?8Hy3;Xa7|A@I`ee~w!zhMBL!7YGYaiNr<3MPp{l_a9{u?*MV?b!gYzHWZRCX*8>8;5st$@MaKq| zG5^UExe#~EbHDPsO!6MC=Mj#qwBBb|Svv0L!a1}2zuoX@r`^A=<87v|&jlDzqww^{ z($&e+u8z~+?3M&!t7Ri8C>|UOznfHylj3Gy`i8CB&R7jd{NDa$4;)>(=FR_x0isM8 zunO$uff|N**+M!7J#sG@|1z*T*-v5AQ~_^R-O)Pr5*0#%f{Fs{@|2MR3l@97I9Vh~P_1bOh2 zDn5{`#zLeB5eNBNvv1qpO{&RgZM2bEiwrli%4~CXytqzP=~phH;YH4MIci#3$AJTT zer-?@k6xp|oTe79183cOw6?Gr?YS;od>DhX2}jksF+=k^Zh}BTNV|qt=?KWJAH_5< zN(0d$5xy(VA&)1g;^Gnje94}!2Ekm|jQnOI6>HuJ_Jhq5vJ(km4&xU-b+G7OVcMv9 zv7_Lh2}AR7aU|!)n@y&>odvOT)HM=w*x!8 z94&E^fCiC5n9`-I;f(VaKWWd|QeyFAX7s|8BGQuyBex{Yh*I3gHD=}rV?jp)-UV^B z3rkw}cb2i$Wyb{d^lm|T;)pf`7?C2{Hw2{cuP##rGo4SiTJ1Nn$7E2=GzN44ut1LC zc!QoaMtVx}r#*ZqAY)J5nW2pUFJ&3769j;WV!I-0=Y|yx9x=hg5sSxzP1$k8gtM$& zTSM)_Dia+LH(876f@5(H{WC|9*++IR?#J()-;ihH@U7zq(@i#!a9B>F&-Ter3C z+%EKB7~LP)weKpJUyN=>p#DC*Ss7U5#y<`ZXNg}Xc5c)YH90K@tSFxW>$bMG%8$L@ z?2Ef7vvPm+FZ*+wwk&_l1$c!C%4J1EQW;4K5|mrliWCMsj$Sndy3XG|>A={Cwi85a z+dB*7TaLb8Ja?!*_gCEH%+vDnauMhe;k-#UxU;jfYY=x7?IxIr#>Y*NTMmQeR#412 zTUDzcLKt=Og(?4lzC!t!Va>132QsW{*bN><$Q#rksUn%<*cRtQW|Z5L8JbnOs^wbV zDh>_{p2-i`NP!Q<3&0LjJwAgNf&J?`U@b$gBG905iW1;VgA~z{8VcpkdY;5VB~uxL zTgi>qt%4I zZjAcp?5}6xwg)f>*Mr4J$N8_Zrt`OJYHU>1&=3?0X~$k_ilh-ab}s<-4WLj#_N;nn zod@o3^+dg%1@rs@cf-wm%KpmcP@dmPSMB{E>=-{ds&whWI`!2kR~c@C_#I&22S?$T zs5IEk+h3Xp2T+PTA8e2{ba(^dbv&CSO zuN`F`@#5~iwiod1H5k8u>iyciL*DW0xVUX=P)v*#mK?yOLbN}q=N$n{4~YmQPX~ke zRK&I5Np3pSfSX+oAlY|>d`f#!xN^4OPrQKu(42!{%V0p z#@X19#Tvoa5e)FM#RnT6O4bbEu%ouNwfLLpZ=)`v^~a@ii%{=SHNn~n<^ogrFdgJ< zB)kwdYDC6_yNIwAfNx+{_xRG2>AWn2m^T||Wuo*hIgE7+nJ(aUNqf$~aUW+IKizD= zQS4ur2GzV*65CG$z&eD;IpFumlv)pd5I~oYFrMec5**DF9C-UdHpX1DMO0MJd-Kzr z-+wj>z?JYtOl&D*9|08+1rbyP2)c^HzWnT$+cSxTwH`xaG6~#+9RWyJ063zBl2L}E=TdKNC-DDpQwQg}ta|PICbEMY2xZ1kB%jl$4c~#rs_?wm@0~t*-kQ`7MD?NZrNFOciQJ zFM1&M(PK2G)Bq$)vEV+meSa2W4Wjhc7MG*qMZPrS4(>QrdC-Fm-7^WE2MI~E7S9?w z-=KC~QxE;}ri=t|0WgqahaiE=8B}|i$2z0!iBbQNI5_1PmX(Z)aT6eysppb-iLeI! znH;P_z)t3v?7qfoVHsC@1K>vweCE7MOG|5B&Ku3Qe!3U{b4YS$wH#pAU@q|2;6qM! zaI|SGwjzZ@@j00!Tciv965|>tjDd!J90UZ`bNi4R2p)uX?svlR{Zt=2_hRa)+t^l8 z%OLI|t3WK{2_k^PIq)ub&{An|Ti|2&th(>^Xx}hd*2sZQsWS zARBHSTHMsAt@#G=LBE1h!z#r)?HYxu$Yr* zN~|i-9Em`-2bth{V!UgV&4URItDldE#Kt-nM7f*$ZdR6;$LsJRb7~F1Uc#h9=z-IB zU7}0OgcBwv%$kWe@TkWHmKGWd`v5GQeWRl{p=4p_w+gQI|Hdm~VK_*1pHLx@(C{;s zu9z4!rLZ$h>_{CY7$EpHRS<{BNdYZqVq(I|uR7KR4yrM5W^c^|#Du4i?U;ds1;5dm z0xV^I#VsUg%}#gC-KsU#jl$CQUwgRiZ4;nFszP5e2_-9pxr)f`xw)pxeQdd6WB35n@`=x9|49VUy?U5^X5VV10 z0rJrBz3YO8O5Y&|NubJ&yLW$K?KN#nImW~PJ!XLtTyhislLoxGYMWO?ZipJ2)_NB@ zP(m9Ngm=%U@Y#EYf(hC`Tz&rXljeLj{RM>FpoZ;7QB7;?o&Yx@mLV+y`FDT1V?C- ztW!NlGDBW#*Hnx9zu}pY2Sbtj`xs7$GH}nGLmsaRot%QCMpQo7B4gg4JB%$2?It8? zu>hp>6?BAqW@K&iw9;ZJz;{Ap|BRkmw0T{IOnr8aU~OQl%uq&HVE;8mE`nL^bJdMi z-DTEp-eZpm7E7bs^r-K?x2Kk-?^g%MnHsg-k{lA!ZgARK<%Q$E4(K>3WW0US*}n}; zZ=acvwxRuM!#y$tHenRLbw^0jD1UTZv7`gg+ z*WkFU{%Fu#I)hanwTxC7%>YW&9<`!pvNYL!hH2gPnnbz9v}zP8-%PIkb(u~h!e^sYAG*+bIIh~3d~7kT9+7oCni^{h@vGQA3BE=5SvU- zycobPHz*s(vRt(zkG}o<>bJ=gkm)KIV%!Q8*>c7PrG&SMG|^z)Mcs8w?z^hSbBT%Y zo%=@7aJR?W_14urnNuc`JW$@*u-D^5FMx%Uj}mrw*;!EK-+pUV7(Fv^`}WZQVVp$| zlUMJNJ#>6QUVjEvL{O3LsN$m?Z5k?q75DcM_g!iSOF44q*j02F$cI881c4jPQawGy z9nh#nL%k_THixp=3qMEeH`aAK3%IArNmz0fO+SJ2wI|hOYo}g1 zEw1TIaK0R49k&75tbj1`2>}y9-FNQIL!5>vqZyR2&hm!Qn#9;!EUwaiqr|0iG;{VI z3+KGjAMfM)F_@Ca5G53j3BR^gwF<(PasXoDCNd4K^Ggh{wwbH{4xTfr{>EaZtgBZK z8To6W$Bc@uk_M2$*6Gh>#;GSG-SMBN^Btq^PP=>e%$0b)KulPhvEwnEgF^6mRBVcY zsjB3o@}4ilb8+vPpSxvV7^Jo)pnQ)@tWAQJWObiiex%`x&c0n$oHxr<<+(*bQh9B$ ze*GL;MsOWjw<1E6lNFP6+mODV)vt$$oR0e=v%ha!12C;r%G_?a;y+XqYofxEJ=}}2ehW9k<&B~ zVjNQYbxCb-2CXg$yR z+La2?fSl6W^W#FY8^AoDb3To>eK*hEK2*)*-h&4m8$HOgz5%zIb7718I@dNG2Y6HH zMXj@vHfvQ|JYRJ9>!TgJMR(|%NGW(kiMdin+?~Iw9<54_N0lS)7i5s8Hx3LK7ve?lLD_C zrK%P}5cAXsl2j1%`EuIg;sk#-Xg!~nY*UD)%OtmylMmwxgt%ZG$}Xg*ot``6*Gg9l9Q68Qy(^L*a-LcKg+~LvP+D%>ttNZ&TbFZ&v}?Tep|8O zeCO45aq&eD&Mf_!_bmUGf|#I+DbyHez3Nje6R8D2qIrj#@&acvWO+y7tfu0kw2e)YGEhJIV9=>IMbl^9({FtwVNBD%T^9l zvH4*$l=EiHGJ@m-oBv`NIn9T$Ww)2WWH&l3 z&%@aP5#450qUZxlPwCuBedRGw8e|!BQF`DKlJ=dR!*^kF5TF|0M#ulb8ls zUhwNO9v&F)bB2Ns9XkD0OXhctBo_T3eSxT!WtfUL-Hhs|tfNCUa>tC}J@!K3Dfo%} zJunR_u28iX9jt=jn@VxY+&nz-OAaY(vOhTG7q~)49HN%XhL4} zt#F?a@AOk(U?;3~S_88vEe~+di)}$>d+tAQKw@@qB@njO#v9n{79UB9W{%&4#NJ8{ ziH_-*q0Lh}o&x_wa5@k|92a*QjVpd5?aI}W2eV7g9&=-UG3pdgv=|ZAaZGm+ZGMSQ zytl8_#_^$tkbWJTb*=--Gm*y2tjDRnmKcS*{@v|Ya){0 zPzEWi9@xK6pNWyCp&Ljj>0oH8s=+MnrAwBWNIs9IZ-^!&SzC5vFRji?L!VXU=yNy-Z>(PY zgY&m&68dK_@nkm*s|JK33&b4lSp=^`;)oSk-Ko8mA%=dUXGrL((d|`1K}cVl`0q_s z4T&K)r(rutRiw@90_>Y3!-~SSGFrkF#PnU^&Th)B*GWZN7shipqQ3~y^gSu+b}m07 z8=JkK&ej6HL=-#a#T+QR=NPQ!)&>+N8ulsr<_BI=Gaw*s$X zxIO^;@dv*=dO29%%a%UKLe~kgoYd*N6$=l& zmU|tcw5?06C!z!+YY?3tz7D{YWFMrJxfr83W}3#o^9yqp(sD)mj@MXXh= z!7ta*;KF-$?9!#2fbMsEWaoYc)AygeG0$>0GR^^uACKUaS>o#2FTpcY*Zv`c3{_k+ zRSPT*SB6Z@;dB{Q_tBp7c|(Y%fcQj6;B~ZJ(e39wzX_9(?yDvWHjd1c-wImv3L3*D zkD0fSEiCqh*Kelp_Pv&UhmBuRQNA~OZ>YW|rgQsScd~yq!Uf zp49aaJICx)W0}?Nu~iN|~d9_S?RT03flbB;B>X!8yb#od>cE zx$nmsKSY_Y_o3irwM=JbwwZXNxaHHhVdz97e_A;Pv76xyBg=qK3D_MW5)ETYcch-N zv-TX*w!q@u#l%-FdaYkop~r0?$#5klHZFTw1ctqi)tfYf*W!i_qmmFKlT5K)ei>pI89OW=`{)AvOg{xaGb$0VCb&87DAi4&&9v5W5<>qLLx z$erGxodMjiIkj*ir<{mo`E8oM(+Vcfr#Rl+edOLQLw?v@HB9^E(D?dS!5N(->st?t`+=UA_j}Pth-iqxk zQ-~fvww)4+FaV+mJ3~o+W@@>k8?K1X$NTuM<+5N89R`TfIp^f{Ih?n=e@gMl+8Up> zv4BITOwIf1^s6#x(W|b*ynt_et=BxKKKz{}UwMRPVAzxvy)ve*UDIoQTdyvHwZJ7qEN(aMzXX{ZPqfv!JV?0;W#~a*HwhX+!~h=^5_1FJ>t$BD2<0Is02XyY zj?lOY3eHJDnZx}M?q7^vcMO1s(kg%oku4!>bYSH%8)(|BcLltHc>m5Uwa4&tFlI$_ zw@kM--ADXFgekbxFY#7bauGBA|d#Q8w#VSCIfGG@^k?>u*mJ+Uz*zU!19 zF*XU=w+P@+2?JlvAqot!+XhVL)TtX9mD0jLvE7=qC7FUcm7o9d^?iS2M(FU)f0cDe zQ-xyt2-O6HoPwH%awDgfUls}hj|C&*W(o*11)~lZ#`OiU$-kly3GIHH>#|`OX}^Ax zj%@>Qq322`m}Kd)Wd*n}g&1hwym`W8<+qy}AIDOyu7!d{JbikB7=SaJVN?%dbtBxN z2^_vL5++o8^U-mTPAZ`a!c!u9K@?L!x!}{mHIfdL#!n$w2(vHIEQ+X=dV3BlkcM2; zz-Z1yntPEOOIjB;jDoJr9(s|hO~gQOWE>$ay>VQK)d4(Mus=Eskg14botJkJiRIHc zP*jUsfIFn?uw~<-YM@I&)(7EZBhc|4?FKj^)7JJ;N)8~Efw5GQ{Dm$~Wcg4gus1RN z{kp5ErE^qD&>q~jVN5>2&kbl6nFxw^NV*El%1VZ&>{5Z##HYw^K8_ESfwv)bdjWGz zs6r{GWx%GKBk*i|@+u_pGS`|5N8`>$HY7iFn0s^P*=5RDUybVM=4Lyn3YFThwFu1Ec|v%jHZ58iXerI<6MFj$WGvE2?T-V7L)N^&e`8k z4#Wh|Pj%7*(2t%jN3+?Z1Ovl`BzKH!t)sEqnjI z@3JIs*b6bkGqoAN79pC~sm*UX4VDSsQl-Z`nDO~_j*anRC@Zo;Qy~hB} z3Wy$9rzL?Up*{ZtGF9t4YOcZ0>XRo-*vL6M5V0ZUP|Zm{&aQ$^=0mS++qCHrwXMwq zP6&cPKsFu*Z{L2f;y#*tlFM|1WAjh&JP8*=q83^gZJWFo>YO852@CF?#Kr7x1h5Q% z8pE_%rgNK=XVbf7n%+NwOX(12lZYuf4$A<^ckbSO__-dVgGpI6ojbew$3O8TIkr=$ zOqs$+$2o`A{4=g2N4FH7$y<4Hrcs`vv^6kT>%N6cJ1hNO;@WzBP1VS?H|K{RZV=RyMoSPpik&1wL~ZKw-CAsrwf5yA`p56SqmpWh`+&+N|yxyE>8kn*Prdwyv7!*eT*J!*d( zxyQVRaa=-z19!DZLpyf7!V>j8u=M*DrFo&ZoFvi&Tqhl26k98ReAe9^57u|GkK-~M zaZaUq6+8{@vRSVacWmG8O>4{uvA(*=fab8#E>Z6x3GhCdR7z@>Oc#6kkl(I>YY8b+ zlkE_H=wz6Kc!=#S!d_acsOT#Y3|7s5T7iv=A9=?*ZYqQ66Li*0yo_HwwqrTmpO0Lz|Re4gU5$f>_zYD9RLW z7e1ooP?A_OJRUu<-_oT}jev5XgN34(iJhr6Wi7W?7A*iXk!-`xdM=by8E6s7Dg7J% z7chV_#`LM77Awg{ss`>z{Vg-38-SF)jX;Wx(>0&A1+o27yF8%;_D)i&!a&(q_jRz| zx05If;-dcNNJWn~IQI>|7|@R*)PioVhp^%(dxj1l&gNN<98CcRB%Ck!MRasO6uu^o zMH1l&f=wLGey(SV%}Jcpxjj#vv9EalUSuF-z+E5+muqw~C^;j-WyuhBaW6AwW9oWp z@HvYXUFV=5pr_|?FTI^X(r5fxI-zNXrt;K5OPvPqHSnSt^a1S!%gG%K21+?Ld;a|J zFA8*C#*M%gh3usa7=lhH#@Tq}Yt1)RFO( zk}iRakQI>L?BlzF%kw{Ps9-x3o_5Lg6|EE3pww)@yI1+@2kPp&-*fHB`Cq@_;hBMC z%$%oBt}RL!nzediKwE>KizmFj7pHj+Han^|$8ZiEfo{>^wgq8$=Ol>*=c~W-fQ5B; zk5RUiakF!%7-SS-0jX{z z&k^XE>`GnM2r>3{&qrX7zy{D)?-8~xw4H=yZf7rIg>5bBE2`J>BQs|pEbhFenXz$} zK{d#K#b-KU697bh4uD)l5d)DcGymrEPEB@s`}lIv@Gn1Z?aSigNH6%%kQwq9Rl1b8M}003Q%BQwss98{Ey;`B2CX3i_sEJ6 zjw|435ha4L6eL$ZHml^b$$)4%JCN^?Dln}w0c{U=hl0jn3K#6E*9m!&48#e3I6Sf6_8A&u%Bt_;wPi@9wBDN^7WlUg3^$xJR_rrk;%$5@aEtC@jVZ{VSI)$Fis|UP(T)zX=vHY^UKvlF`YR#al#WXXTfyf&mFowr_p`D9oi?po7ZEinQJJW8 zMJ&$g0E}2jX##2i-D;-!HanY9#_hPo!4@T(jmuV?SfBMIiGwrjOY#6&jqm_-sW)b# zMH-o$(>y=k)|E2 zaBP#by^b$tAhmYI8si?0_V$;g-JmCuCJx?KBPfd2h1G1lsI|@}@Nr;K5oHTw2TUOT z2;w?y=6ynTF4L(2s&W=l&wtgcmlDb;*BKKl?CvLL78ev;B6A-+a!j4un;>FkfWQUbzKnZviUTMb=%xg%(6@X05~`XC-g756FrHq5YvyziI*bY2aZGe-n7i76kaW|!Z5mT zh26ts?WxFW#T68~Z4{$Pnp5Y%o?W9d%zZPA7Ey)~&#W9TM?ryw#Z$G#WTE9Kz76J+ zTL!;gTm5DG_Jk`R`t*CH|5bVaN%z;ry1Fum>#Av{1)XgBO#k}yu}F5kwJeURW@vMz zdc#>nj#-G8lyhX>ym=90T%5U6noH6cZ9)z6v!O*Is)I+k8Bn*j?6&gLduQPO(Xdza zRiFyZhYUHbdmsx2e6oY^*Y1m4Tx<%|Kcki6!M!UlmoQN=8nA+#6YBPa)%l%UnSQlC zEHuA-9-+{D8SK|42RUaQ66f>b41V@nOW6urz@~OyJB~%ipOw@?7~FNj+)eru5^5pCXL&p5CU=+7lHb6F~qHz^S17lxaS^jB51{ zIC{E8Sww|DcLqQcg@W&IA1GK@xW*Pw-wgkw+)mlqP;RGo-aYC%84Iktgs@dN_78)k zj;PMsKP>c%;r;q8nnCUFTbT{ zntR-n*&Gr-L-8kP`}Vvk8kFy^SA5x{M-~ZT#KxSua{`=!hbftbiW@;ACm}^V(R6W- zk?!lM|2h7OpxZ;J&=q_|LQ?m9>o@Jfn3Sr^uOFWM(wk;paumsLQW}jk|9+2@p7cjV zEL;PSy2wMVi~lr)xCBJm+}{HS9-_8_FKA(CdgaEA`N&}iYPMUuy5UiN#hE!|;J`W9 zU&M~emnkvEL`)Jz|HF=#(2kO3%UlVVIWDyc07SVx`;kTuI~34o(L_p3X?J9l4s*t# zIpRn-d#pE=|dslQ@ncBe)SpL!Z64 zd*W+oMS-7es^s=2GAh5IzyLn*BS)t-Zf}xOAoII;v#Sjc?0Uj7gACLdbknGOL}G>Wo0|cQV1YMQIrwxH_78Rl}$B zuP_x#X%Nfs<=oWg~~otx`W#@EY(+;1GgB1r)%x~U_k=m6qF{zHK)r_W$?4_(Rb$`l+iw_(OD0Kv>I!w3Y{$I8C+-9?hh94 zUflYco^3s>2{)0K6zuY&Udwg*UTOXdfFu~9*Bj^hHzY|;H88l?a%3@#Nk?wj%#5FI6%+VWq*$d;N-NtDNm6-z>bIb85>lf?U+La2}m3P`)j zJ3Qk%m{lA`6oa8b!CfbckamRG)+g(go>Xq092 zqnIhO$hM|VTYac39H=fZa3Y$UoJF9f9J~{1GuvFG+`%xWdE$p%%o{=7O(1nUdU#ff zE-T=}+b6LrJeIk-re|arCI1R{E>iMWml_^?uezZ3TJE!9mqc1{AFMKbMIWX3552$n zMg$-g37a;3j`*&jo>aDbRZ>Dz7#a}8Q`ZR4uJ_xBoml^yH36$tU!PdJaH)a8S11EG}v->+G`9@}PzkIog4pEvYWGE=zcUO0= zt*yoSMpj1$jyoKW7f_5T&M;jFfX?(bmBBA?Uob2y2obOUv(@JR)eT=$EU_x*&!5+?=n6qEVq%@@X$`+L&F*1K z>(XzB3{mgat!h+hUkT1ZjMbTFeRaczUs{ts(yY+q|NmEOj=PvBIam>3HTUKa#;#$2 zpit0Ss#pz$fJOkaIm15yiHoxBIjav^O|%=LeSyaa)OAn^_Wa}=>1q;`*Z_XCjSZqF zqwOMOlTm@6;SIjgIO<^@yo3UNjli%B@DBM=T#vGJ@f&;zRDqV}3|Q!nez(KIav8Y? z>q-fOyLuwZiKESKaHfkr7OfH<5y|7>94Y|2fAO-Yc1gP)J%pQuo)ax2SFZ?rIWQ&F z2vYPf>D8z-cUo6bhXa-%XqiY~Ed%f%25ZVPb)e!^u-tI~UP2U|Ns+|>il5wTpeQ?W z%L)A?JPR44?DK>hX4AUuJ`&ToNTQBh98(9QDm#}XXr+T(>F6%kLg(sjIdY^o%U!uA zGg}Zn(DeI3c=KCmaytaBw9a;Sa}%+!0#KFr^@W1M;Qi|d>X_eLgCPQ7V@ep5@j=<- zwn+CBR7FUyL6Q6al>i@lB>C=eRF=Q;F~Myc=4&AsnSe(BT}6fRK{!AVm*Y&{papnI zqXVSWBp)C~dR=PMD7P=VeA|k^g0HfyX8zMB^e6*Zpt29R^DVm{pG0n+a^-*XYMkKt zZf2N~zkwSDnD1UgGN&+-TAj`F^z8Xr=&5locd&LOd33ZKy+!H&Wb!%A2BB`f|mQM{5@$}Nha>&~b zfsYBqC#jY7D6 zptnosZVefW0|pIxhTKq*Rb$;Bh%MHh=0{a6GxfOIQ6A@5ysRR1+n#6i0}|95OU!P? zd#waC6OrcT&Uz(hl|R-W{{1ufByU$+tR$io8QwyNY@(`6fylfsq^`FHh1Grsm1WPg zO#s7*uV2-Vs4@F}eW||oQLa+(JRMO`WsQ9OBo`I+#5HSbf_GNxA8LHA<8IkbU)ink z0!{Pl_1B!pf6>?e#YI{dFh&Al1e8{AO^*$|wSD()^OB`V*D1;8I63_cy+T0JD)-(odtr zUrz~5g^}Jp6~_WD{E+@B_)v@ZH?f2V* z_wB2mSzZl7liM>j3E7AE)=3XUae>fz5*Xkagv&dMY`MUxEZxG(7>cI9JpatxbQKWQ z$s~TTqHL-$j0-poz0l{wN2;9s{CxOvuR|Uv(!v4X06s^Ub*gVgX@lp8oR+8r57q>` zr1tE7d(=*n+oP=I85>kNh!sOw)iPIH>2^Ojy%rW5|)YtxZR5U+{nK+plblADJkU^fr{Pb?=R*MJPG-jmqlN|#v#fkgV2YL`ko^w-YsYNu@yRD zai+TqA7jU=T-hCY2%^x6k7)CbBXlOH!=!RKhBXtrJnbbSw9jZ+=-e{Q)qO)`b^@5sLD!VO^E&^AN#uC2qx;TE z+>KiGHkk`@*>aXEfZf9(Tn!&b;SLuPfr^MT4Nt(N+KVT!g$um--P@#fVPOvGPrh$B ziL$KegBi~;V#EkZ#epU(=Is(LAlhaHGAJmS|C;dGnGMkDPu(oCJwj5WTpv=uxFLz< z$NQalm2sPcl2OFkCm&Xc|I^ z<2Xt2A}d{a_3gZWm6=7|E%V?pTl4FF z{aRaD<$aYIZMl2v7r=B5T_uvkpCUh%4uyT5` zd%|8%5JwEAUvPi1VOOyWBP^pkD`-BSQkxr|dcG$d5JHmGup8kAkRc2FTK-@*un%pt z#kkd6-T@E!FuYH?0dzXJ!lDa5Oum6+IH99vC zgkO-*x8Mz60tf`{Vs}}cD~973KvBIcZ(HL7m>3(=S45#)U)w8((;Y*_un7n zyDeEjg+e5Z@3nJ$;jPgxA|R!Do-ubgP5{EkG)#P*Qxp*+z2^6_5Q3wdk%iWmn*k9eUSNS zhxzkmN23S0{?)5DFH=fH_EtX-E7YM}U}q*}JzxNgKNk%BkPH z1ec_5kIqBt6?VsP)L)Ta5}KojfphYq)%EYor@!lzp)+`+XyJZNz|ESEv>h*qOw&8AKJe(Kczx^K%4Uq4M_CFvc+l(6?K&OI4% zoq#^t5dUASOlSQpVHwQU7VkNgo5iAZW6%m32JF6K`%#EoXREO$gHB-2Y=u3W_Nt1e z=ovIY?%t=xtRln|W#|CZRTctA&U%r;3Dk|&gqwUVsxBf!W^x?;YOA# zf?X-;?HqqDsqn^mQMH$rFIxt?DHDh(*-OzN6Fc`+{_Ha% zt1s$?b%Wsm7&{EGU{fq^a1K7~?@{YUUEbWOe5Uq7s#@Ga&z?UIKjlqO;(zAUxY@Zw zz;Jtl+|!VYO@KM@AT)??24GoKP&jpjF(dhVwg|_mJ1HVmwJyUa5bC3yQ*@XTn{id% zh#pO_3kkq3c0WsJnY>P}kue7(jnbAx4{&7>p3@c2p~Rp9fLXF%l3%0nK!sdxc;f?b~c!1qk2oO{ni zYp?hrIGzthUu?QK^S_7GHI;eOG^P5LfQSu9Tzz??xJ%A?+gAT5%KD!-Ab;w-%1^hF z5gC`c^Oer5eA~VAmJQ&N)3%j=_%NPed!lo=5`cdWv0o_7Rab9DQQiYQZ;(G^^A5^y ztB)T6ix>?HHu449O9bR=*#pa;UR2hb(jfO5pZqPa!{e5a4oF$0*~r|uP2U)&ZlFk! zSXatl(VlR~nw$O=Gnm5y7< zudf*MIMr_O$wrJwqL6_Yml+LQ9s9pqDOaE*xdTkIJ_}*DY+hoV$$iz5(v)o??jiT* z!*#Os*3?$yiJAlS%a?QkD$QTv#T3#F!1p&yss05j`8WbAO85F}4z+{e1mxaN=m(X= zl|N!n4a+za4-cHblDJr+Ot#l2Jy{@2;p1xe@SgP`C-IA)bO+MHHGo&nIOon({sZ;D z-uG;ho2UMsdYIMOvwOax-Owh~BVe&31O)#H1Y2obx82{yNQ=@nCLj z*5k)PSbR;ihh9hSfY5BZwYA7Gqt7RwskYA1+x_EPvTHXopM@>n*bEP#^&1PY4-z45 zEleR1qx3Gt3*pdJ-6RCvJy9{P)WaYyc)`qv)F zk8(d@GDqEf=xS_2QXbNPC_X~%igk3RZt25lNWdHzx&PLQ9 znQQt{fPqq|S-)9>!r&75IS{O_bn6e033R0B6~`>y_Y%@H1-qta5W6|4TSV`*gPBZN zy#*T+B#n$i<;unqoPpZcw{IS|IT)MMjv`MqioVBE*OJqiyC5_60SDo75#wMiWZ_Rr z4mP$M>s(n!mC7Zsf$5;?tb+dLw3??wnE`J|E#3M*WcgYT?{W|&-kzE2E~xX6dwjVw)RF9^$z)5v}H9-7oLI{ zZ-xt`mKGIxag66bQQ=b*4OF;O^0zqEt9$52|K6A3n)rKP<~rMjeU|I_N;#|Yy=xJ5 zbg!X9fBAbuX4dpMi)9c!h8RcLhWE(5+ff@ztM^mVn;y7A*o%_#(3o6v(`W`RFO*9V zMKOudv?r3UMwvN)SAcv^*b4rEbh|Bh?_Djy>sSM(6iwg@QDht8B z%-?S0nt~DP1BD#YH5hl>i`_A(HJ$4uG2_0&j zCl>`cVR}lQ)KtBY7aaUY3V$sW{=|QvD8fve6zX7QcF)$5iv`&+%)b^CWkh1 zwr<~zNAcLQ2pdZDiYkzjK7*&9I)>e=MXOfk=d9APe^GlhIM$;nUHKxJDA1C6G}_}o zbZ>jUYoYUT^FMTp=NnB^H`lF%a==6fr|5blPlMTQ)hRr%}MfJYdH z-V&V+ZE&-~)-Sr!;)$*&X4XFH*X4u(i%?6X5OhhDxd=CZLOl!K8I6&z8zf)HR4t2yVO4=(a?84u2 z$aWYu!^~OCnrP3&dxaQ}x{WZ(sN5%`o`4EpLijo@B5G<1F#rSbktQTbF~UbH>VKQt z(2a{ZkO^JE9v?bEk;|AFLsO)BUr9QQUu@eV8o0-MW_`vc@Y1~wwB?UQ*J)8J6%`z+ z>jUrMNZ9}e(QfD+bHyKmR8UO^;$^8zXn_=WZn+e z{sp7w8)i4?Ok4Dd7k1MPRn#9oBQ_5!2CH$GufGm;H2Oo`@!{gyHCJXNyEc$i(Q@L^ z*3>5*|HHKNv-;RSJ@gF%j)TN2K4#8+=uCAvJv}%IFAC0jhJgSjXsErEyL(m2}>YYFgZ|1ox^a zP10JC9F?&5JE~_steWJmpQ_0WGS=Z3o#9t>Zp>JmoX#tB z(sNPebbE#cyCenp6)o0Qz+ZBU4kdbUkjZQmflv6W=o%Ra!99U4bUuh+$p@qr8&E5i zG9J)5ESPNo8(YA6DnI1YVF=+SO-c5%c|i3$g|uqXLQ$DMCu6K>KHM)Mza5xTgxd7F zM64*Nu0=OnkhTeuq9bib9bN0RSK()LwS6yQRuv^a$E;P;JFr;ry5qs`nBJ3|*g|r1 zWz3C?Nui~a-{05z8O0eY0Osh;@I+$rZ>Y%o>%k zFVabAJT5LM8Aj9GX4FS--j1NQEc>g7!!yjA8xj;W3QgBD93vO$Svdlk6l6AcB==7M zZ|`QztncJ<2ZU4rq8;R_k~hu)Bm|DAb)VlP;aldgF&4t6B+hgve2wJYqSI`Mksu*+ z`|07ed`=k^wCH~PQ?7@Sfuxi=()i-KL-=bOzIxWf)uD%VSt$Ex7?6}kRXo;wGiq#U z@C?HPsqvNNe%~~taTv|Rm5Bu99svf3gB-bEM?dyj1LJu4o=Ip8 z^E7wjalF(h$yFkJGL0pHN&8#qf61ukFq5P)YJe(6+YHpxlbYP7ig|wiR31y~>pWZ^ zpnaF<2{f)uOWq)JxvA{+Mq?dx;4p&R35JLh(nRG^aY05a5R&nrl$2e9t;<*xut_Ka z_(=^Q@FI{gxoeT6&T(+C`0Z@m$&*7@{MJ#JYyO&nD`f-^!QZEG4t}J6gc7-}Qs*~_ z`A!JX34re`iYNgOcnSFooME?-Q6kYareAE?Z8bMGEhq@DRnun$ZGAWqsef+L_5Q~@+Um-oW__$F_on&sHXlwWiNUAi6 z9Z1c}r7q$Pu4fk3h1^`V-o1~^4c@X-8pW*N*y=g2`7G%?Z%DmBOg-7DUS23{b9Lr% zQa=+LEn!*xW=!;TU%dPMh^9i0a!f9H_G@d&;!YW2l$e$7@q?PHr@j5N_D`~Oc8Y4S zRQ>QRCqC8$)7`qKuW9>Ei+MB9As6p*Ln*^49rJ@1WRv*O6^Kb{Kp701eNavY{Dam4 z6PgINA|_&5ui%+c2K$N#Vre6%-cb8!*K! zHm1zkia0ghDh(=1)YU)_3e*4!QEX#y6jOj$l7Ibm8S95OAk$+j`?r+4MgCvuChH#o zd2Ubr`XFl?n}9w8`mdUNp?c=bK-R!k;^A20q$qqzO)0JkG&gXZRCVRtxk^OWoQF*? zYE?!Uf(*ssAH(vUq?i}eBjk<&-JKhRB6ZzU4k)dP;l##>pApw##C?ZCjscgO4Qk&| zhB}#qC%E|@Ev~U^h5XD36Ed0tt;$-Dy~JTaP%BE7TTB~?{b;ugfx6`cq#N{U6x&13 z&Z9yNuaYcf1Q?1X97`bVP?pYO0Rkgp!f)4Og+{m>1Zfd`sc>|*e^Rb*ANc?cb;BPVv2Z7Cjmz#$^vEo zM>e`&UoZSOz99;?S$!Kh{M+|!Z|Qb$y#^S9H$r4jqk*fTqob7$Wtm-AZFy(8s%!8h zt>txk&wCuX<%C}f;q5#2XVe%=>za3$+6el@IWMD#As&I*(9CS&M3cuR9Fxqgkz!h} ztq6M&Qwj*t5tYDB%0cBa7Eo+D({n&hbZ_ecW9aIbUTs(Saio%has0 zup9^}#QDhgbkWMr!;m0M1$*Z(b*&tx94`dNel(bsZ*=a4Z&Fs~(WDBy9lGi@54Apy z(%DvgPopgB{psI!c_AQhO)F#J1N{L__`B~1MZ*|aGQ@&zRf;P9@5E(tcgu)?8)4?g zpK|=K_SsqimLGacqTmgx`NTf>jVYz+-8$ft{p|@Ci8Z0(Y=M#rWMzSpb4Z9zlXqN| z+i-9r47GbP6Cs{~mDL`q^|_gee@Ka3Eo`YX3@|iR^th&uIELua>PMse)@EIZduZG% z!^l)swgiGM-ZXhiM}KlM_3{l7P%% z{2n?oSB45CS4dxR+0n}MT)bxV7{r3l!1H^f&J*@qytRW=BFZmq^he7;dqzL^=Kq&PaREr?>-bt2kx$1VXYw(eA+tr%|9ii@lplu?@hZ{Fz@V8EN zZbz>aTe>~Y@(NJT-ie@V#*2Nw#fj;TSiw0y$4Hat(ch9W26z;ye<6X?tJ-ZNH8gy<0GHL(xJ4gaO}QcQ zo9Ih=E4#Y)CVo2zfB^M1U$|y^`u=A1!5s*;?Tg)5v!QNb4`D8z2jKBFu2|h&Wh$NemlJ z_|(pd=v1y{NiwB_pTHv9?Y{$phG$-KOi%(R`N}p%}rvW&i%Jqv!&r%~*~0L0(&sR;mpi z+>EGwSwt^L*-Y6YBSfj|LhmLSt^U?e1ZB&?O8I^3xZ@DGx~O-#(xu}!BAwTTDQ@hHR#UNj>6MjbC z0?^>pD2L<`MwB2{&Xs2)vZAwZ*yU7L8QvDfg`0cKGgx{QWZ$E_fP>_tpn07hQ9w-k z1v)S6Bg|0R16|#ayumav`Yyaz^H{9(ted3n=L;<;F5bvp3O*xatd7k*yOWa8O#2C! z<=or0v??qUSyv)e!wgS9U1Dqp0w@)joafZ53yNO7e!V3!()p0m*Rbi&B%POuQ9c>n zm|shYBK5xbqEVZdH0^|*LIha!7YGd;!G0t)02wzY`g)ixjyikf6`;(w4vuLm9U-?B z1gsL(ArYgySnYAh8p&a{dC|;m^=dQPe(}dhdG+BmlkfD2&+aD{AV>n(tyv?(5BKzf z>2lc~oP{!*1O5a%nwIf8?livc4V`mFabR(=p2Pqw21HS5q^n5#5abz=qUY1a%StPm0x98$K{Gt1 z1Z+s9`IYyo2wG65GlOrM6cNGcFmm<(L9v*-8$qN-|Nd_r?6$5ldO>4dCtDiJZ8GEs3*5p#(MnEX#;&tD@vUHditLHLEg0u;lQ$fBLjR41TQxfw2 z#G}2M9H0u6*92MNjB( z5l|5V$h`E30Q%2KyxOwHp%TpL++}&sFELmncM~%Wc%(8k#Pz)j}8+ycN`-|{6!aWm@hi; zMML(**w>>Z))Y(Z%z2NCChmKSWrlBdGqqU2%i?iY796sLJ_O1viKsY|dBB)PSV~wx zT`~4@$)JEKiTF_CW~TQ?b_G&&OA|84IPhEsv~bh6Yu$C|@hhH1RcNdc+=yTulwb60 zoZV@ZjTX0Nd{3D~ibXG6J-A?cE-pF3cefF!B`Wm`sM-LaHo4dcTOs&xK~9G_?#A1Q zGE`c3e}yqr!Kth$(J}nf2nwr}VQxo{6~gsNmD&-RvKa1JAa~C!4c#jeQI5-_#moO5 z=)wMNUa0(ARpo=LS0i&Q1|z02ikAB&qF6QLE5@$*#l-xu1A6GP#>hvH-US~%rk}ME zI13o9!~K{eM=G~DrR&^nJIw^)WlksWwDNWxI(#_k_wL9i(vT$Uu;rp5y1H}O-dV3l zz@6(Xb1mSec1m9q;sn?Ek9L{p$V3%UHr_1pnm>2$TI$IiZhLw!O5v;DxnqZ^OOJ)W ziyC__eqSpy=cwBgGCnm@Xwn_Bd)qO)m0JMvarEQ&W)ks&K#JNB5rJ8uaY5BO57?ft zI$voLEG+|u?;35&yGf(eyuywbXP@A0w=eqVa!!SulAJVwC;0l~6RRb@4+SbtVHsye zd>w$;xJA}F)Aj!@MhsoHN2A`)yzaLqRk!aKEk>BsCn2Rwk8=;;OcoHL>WPykT}n?k z)Rw%#KJV9zsJ+%<&5g25lbt7XCN9m|)z(3e&E;8JVI`cmo?(%AHs#iQc&2pHjD?0Z z9&`jLzkT(y)i|zgznPBtZrYiDnOSc& z^CkIK{5uz+_Hqwg z(=x=7qlRm=4M$C%tk=`jx~`hhRdnk=zBtbU6HW*m_u{o{lcR5g#kl61@%_=X`!qX^ z3e+Xx4K$)C9N&}$97$orSd$*XBg7hV^k!W_koANKmsN5dJcGh=uRd`&+OITE4>1Q* ztUQFu<(~9G!Y?s1R4bChk~&T}vns8Jz5n3M%s&5HgWD*!*;(V|4f7BhOt@~Dz16## zdf=dC7?nwQyo@VIS%V55>}nf_BQAWU!rILTQg@e?gQ8vH8B&&8ozn^n4`)e^0jpFD zUHzdi@PLcSg4wf20~Pp&m{3f}+*n$L+&V}J;{G-u;mj{Cwkj;ppmF?tBtcCdvl(iG zk;;+|b&*>?H}?tsfryHE-D$;zwn?i7a2kM3L zhlw_l&W|9FsK~m>RW&r|KfZYwD6vyuoYAG4gm7r{vVB(u0egA$pVhPM+=_q+q+Ej0 z6;Tbjg)fIL^??#1Qj)UI)d`w3@YnWqh#2$NI;O1Xm8L)GKRBw?zhhM3UnwVB{8!3J zp$$=^Fb(J0^62B7_%gbA-@bjrqh|pGGc2GPA{G1=n`Oi?=PL1HzXEO&+x)ebmJ0nC zf@f-t-h}yJLIx=MopESV_US5}zr-`E4qwUBz~7{O(*Kj29J|o8f}Oo;nU2bz_>xN( zE-1k^!@&D?okVt`%seIt>@fEtC%%kGeeM?CQY$h=1#GB2A_tqOB^`FB`i5b8L<1=? z&>TGh-;H+veWqr zx&gQ(B!)vmNW)L43frVr5Ey>5Z!E=jLVZGrCXQnn=KGbpiIy?O<5d>B2m6GBH|UcA z&Wf1Fy(WcnShJ4~2Mo$VgpmOX;VA&}Kw6;H`P+yQGm~S0QrQIS z5FsFSg8VUAFg~_Gg2)=)$ndeYPwY9$m|XDl=FRh9dXS4reJvZ0Z6pFN8&Avy0&F2L zqDlW8?Hcc{1AhopzY50zDCZG24tVaYZb{Q04pflVD2blZUce$k+kFq}qV-9$WiQ+9 z#iM-YyB#1=l>HbPb(4v`3@WiHJoa_bL@301RX90lR4MNXr>LB1b*dJyj6D2wk_#6c z%}1+o6RejWX$;L}dJ+}c$oM6s7drlC}Q5pP! zjkEx=oHNvT*7*gz4Fb7$(k(>o8mrLgzDoz=nt;~uN4i<&z1M$S;67)JC*R~pXs?%d zqPxhaph@~i3buYQtmvoZJ<69d&;|uFL5?H_egFDKHZXQgP0cT4@39!ub8_Nyl0@1? z`ESSd|0^Y?sgv^S0*o_Jw+qzvTO|!;E`Up>gvzJZ*L_F-jd}7mifDmRNfO=@I=A;4&8Ql??^)7TE>#Fpr$F7~u;+6N z`w}t8{lEyfX^V2TSJi5C+qIaGzmn99tN+G~2K z*cv+{>mJ;hT8A4+YeoI-JiGe?)y_5tOO8>$BOGc$>`Oo}@Ykih1U>M1&eEI?9IuNR zJidwT$vJ7*zpOGIAuCgv37Q^x0)R<8u^4$ZQAqM zHUEa;<^FUPGwoqi>=IWXXA5Abe$if;#Rgcjcf%R9hBH9`IAEH(nN49%Mt5&WWM;S2 z8RXez4yOU{Od zgh-(O)vFU9-2wA-4LCsH-+tjH86!rT{#dRkqiZ@|D%U|&iS8)SH9qt?AGC+;JYH`q z-7bsD5ss|n<3(v`91uss#2j^JReg;DCh6uq$$;8@?C?ETt&n4Juab;F z5OW636oWN5I|DbB+-A9o)sK2S-twNOKRQ4HE9npV6TP3$01RrZLI?FpBue%9Va0*= z)rA~AyhsGs-uB91{7gTm5;!04_3+|C@LBlf&*)ENnn^;284FcmCy6x8ZbscPUH&B7mrYPVI;{T&(qM&Av$IRRQglIb z!YXQZeaa_AOY_*>o~eh`N_i?Z9#@Oc5y|z*w^dni&;m1&jKIcK_DU^o6rYq5xAhmc m-%*hwe=_Ghx4%%6Ah=1i!qqJ_WuE~R#>wD literal 0 HcmV?d00001 diff --git a/app/examples/Misc/DBusExplorer/.icon.png b/app/examples/Misc/DBusExplorer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ce52b8b8c9fd042385a5ccbc08ce1029f9fba8 GIT binary patch literal 17373 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 literal 0 HcmV?d00001 diff --git a/app/examples/Misc/DBusExplorer/.lang/ru.po b/app/examples/Misc/DBusExplorer/.lang/ru.po new file mode 100644 index 00000000..b379fb75 --- /dev/null +++ b/app/examples/Misc/DBusExplorer/.lang/ru.po @@ -0,0 +1,72 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/DBusExplorer/.project:20 app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:5 +msgid "Gambas DBus Explorer" +msgstr "Проводник Gambas DBus" + +#: app/examples/Misc/DBusExplorer/.project:21 +msgid "" +"DBus explorer.\n" +"\n" +"This example allows to explore all applications connected to both DBus system and application buses. You can see all exported interfaces, methods, properties and events, with their signature. But you cannot use them." +msgstr "" +"Проводник DBus.\n" +"\n" +"Этот пример позволяет исследовать все приложения, подключенные как к системе DBus, так и к шинам приложений. Вы можете увидеть все экспортированные интерфейсы, методы, свойства и события с их подписью. Но вы не можете их использовать." + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:14 +msgid "Refresh" +msgstr "Освежить" + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:20 +msgid "Show bus ids" +msgstr "Показать идентификаторы шин" + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:37 +msgid "Legend:" +msgstr "Легенда:" + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:48 +msgid "Method" +msgstr "Метод" + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:59 +msgid "Property" +msgstr "Свойство" + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:70 +msgid "Signal" +msgstr "Сигнал" + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:85 +msgid "Session" +msgstr "Сеанс" + +#: app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form:93 +msgid "System" +msgstr "Система" + diff --git a/app/examples/Misc/DBusExplorer/.project b/app/examples/Misc/DBusExplorer/.project new file mode 100644 index 00000000..1c9c59c5 --- /dev/null +++ b/app/examples/Misc/DBusExplorer/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +Title=Gambas DBus Explorer +Startup=FVersiongbXML +Icon=dbus64.png +Version=1.0.2 +Component=gb.image +Component=gb.gui +Component=gb.dbus +Component=gb.settings +Component=gb.libxml +Description="DBus explorer.\n\nThis example allows to explore all applications connected to both DBus system and application buses. You can see all exported interfaces, methods, properties and events, with their signature. But you cannot use them.\n\nUpdated to Correctly report property Names, Looks for Name attribute now \n Was expecting to be first in List, but now randomly located\nUpdated to display Property direction read or read/write\nUpdated to correctly report full details of signal handling" +Authors="Fabien Bodard\nBenoît Minisini" +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=en_US +Vendor=Example +Packager=1 +Tags=Example,Utility +CreateMenu=1 diff --git a/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class new file mode 100644 index 00000000..dc57ae1f --- /dev/null +++ b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class @@ -0,0 +1,313 @@ +' Gambas class file + +Private $cArgs As New Collection +Private $cType As New Collection + +Public Sub _new() + + $cType["i"] = "Integer" + $cType["u"] = "Integer" + $cType["s"] = "String" + $cType["g"] = "String" + $cType["o"] = "DBusObject" + $cType["b"] = "Boolean" + $cType["y"] = "Byte" + $cType["v"] = "Variant" + $cType["d"] = "Float" + $cType["n"] = "Short" + $cType["q"] = "Short" + $cType["x"] = "Long" + $cType["t"] = "Long" + $cType["a{sv}"] = "Collection" + +End + +Public Sub Form_Open() + + btnRefresh_Click + 'HSplit1.Layout = [1, 1] + +End + +Public Sub ShowPathContent(sPath As String, sBus As String, sApplication As String) + + Dim xmlDoc As New XmlDocument + Dim sFullDbusPath, s, sAppPath As String + Dim hattr, hattr2, hNode, hNode2 As XmlNode + Dim i, j, k As Integer + Dim aArgs As New String[] + Dim sIntr As String + + 'Dim haArgs As New Object[] + + sIntr = DBus[sBus & sApplication]._Introspect(sPath) + Print sIntr + Try xmlDoc.FromString(sIntr) + + If Error Then Return + + For i = 0 To xmlDoc.Root.Children.Count - 1 + hNode = xmlDoc.Root.Children[i] + If hNode.Name = "interface" Then + For Each hattr In hNode.Attributes + sFullDbusPath = [sBus, sApplication, sPath, CStr(hattr.Value)].Join("|") + If Not tvDbus.Exist(sBus & "|" & sApplication & "|" & sPath) Then + tvDbus.Add(sFullDbusPath, hattr.Value) + Else + tvDbus.Add(sFullDbusPath, hattr.Value,, sBus & "|" & sApplication & "|" & sPath) + Endif + + For j = 0 To hNode.Children.Count - 1 + hNode2 = hNode.Children[j] + If hNode2.Name = "method" Or hNode2.Name = "property" Or hNode2.Name = "signal" Then + For Each hattr In hNode2.Attributes + If hattr.name = "name" Then Break + Next + + Try tvDbus.Add(sFullDbusPath & "|" & hattr.Value, hattr.Value, Picture[hNode2.Name & ".png"], sFullDbusPath) + If Error Then Continue + $cArgs[sFullDbusPath & "|" & hattr.Value] &= "ref=" & hNode2.Name + + If hNode2.name = "property" Then + + For Each hattr2 In hNode2.Attributes + If hattr2.name = "name" Then Continue + aArgs.Add(hattr2.Name & "=" & hattr2.Value) + Next + + $cArgs[sFullDbusPath & "|" & hattr.Value] &= "|" & aArgs.Join() + aArgs.Clear + + Else + + For k = 0 To hNode2.Children.Count - 1 + If hNode2.Children[k].Name = "arg" Then + + For Each hattr2 In hNode2.Children[k].Attributes + + aArgs.Add(hattr2.Name & "=" & hattr2.Value) + + Next + + $cArgs[sFullDbusPath & "|" & hattr.Value] &= "|" & aArgs.Join() + + 'If k < hNode2.Children.Count - 2 Then $cArgs[sFullDbusPath & "|" & hattr.Value] &= "|" + aArgs.Clear + Endif + Next + Endif + Endif + Next + Next + Endif + Next + + For Each s In DBus[sBus & sApplication][sPath].Children + sAppPath = [sBus, sApplication, sPath].join("|") + + If tvDbus.Exist(sAppPath) Then + tvDbus.Add(sAppPath &/ s, s,, sAppPath) + Else + tvDbus.Add(sAppPath &/ s, s) + Endif + tvDbus.Add(sAppPath &/ s & "|child", "child",, sAppPath &/ s) + tvDbus[sAppPath &/ s].Picture = Picture["icon:/small/directory"] + + Next + +Catch + Print Error.Where; ": "; Error.Text + +End + +Public Sub lstb_activate() + + tvDbus.Clear + $cArgs.Clear + ShowPathContent("/", Last.Tag & "://", Last.current.text) + +End + +Public Sub tvDbus_Expand() + + Dim ars As String[] + + If Not tvDbus.Exist(tvDbus.item.Key & "|" & "child") Then Return + tvDbus.Remove(tvDbus.item.Key & "|" & "child") + ars = Split(tvDbus.Item.Key, "|") + ShowPathContent(ars[2], ars[0], ars[1]) + +End + +Private Sub RemoveIds(aList As String[]) + + Dim iInd As Integer + + While iInd < aList.Count + If Left(aList[iInd]) = ":" Then + aList.Remove(iInd) + Else + Inc iInd + Endif + Wend + +End + +Public Sub btnRefresh_Click() + + Dim aList As String[] + + lstbSystem.Clear + lstbSession.Clear + $cArgs.Clear + tvDbus.Clear + + aList = DBus.Session.Applications.Sort(gb.Natural) + If Not btnShowId.Value Then RemoveIds(aList) + lstbSession.List = aList + aList = DBus.System.Applications.Sort(gb.Natural) + If Not btnShowId.Value Then RemoveIds(aList) + lstbSystem.List = aList + +End + +Public Sub lstb_DblClick() + + Try Print DBus[Last.current.text]._Introspect("/") + +End + +Private Sub GetType(sType As String) As String + + If $cType.Exist(sType) Then Return $cType[sType] + + If Left(sType) = "a" Then Return GetType(Mid$(sType, 2)) & "[]" + + Return "Variant" + +End + +Public Function MakeSignature(sKey As String) As String + + Dim ars As String[] + Dim aIn As New String[] + Dim s, t As String + Dim aArg As String[] + Dim sName, sType As String + Dim sDirection As String + Dim sRef As String + Dim iArg As Integer = 1 + Dim aOut As New String[] + Dim sAccess As String = "" + Dim bHasAccess As Boolean = False + + ars = Split(skey, "|") + If Not $cArgs.Exist(skey) Then + If ars.Max = 4 Then + Return ars[ars.Max] & "()" + Else + Return + Endif + Endif + + Dim sParameters As String[] = Split($cArgs[sKey], "|") + For Each s In sParameters + + sName = "Arg" ' + sDirection = "" + sType = "" + For Each t In Split(s) + aArg = Scan(t, "*=*") + + Select Case aArg[0] + Case "ref" + sRef = aArg[1] + Case "direction" + sDirection = aArg[1] + Case "type" + sType = GetType(aArg[1]) + Case "name" + sName = aArg[1] + Case "access" + sAccess = aArg[1] + bHasAccess = True + End Select + Next + + If sDirection == "in" Then + If sName = "Arg" Then + sName &= CStr(iArg) + Inc iArg + Endif + aIn.Add(sName & " As " & sType) + Else + If sName = "Arg" Then + If stype <> "" Then aOut.Add(sType) + Else + aOut.Add(sName & " As " & sType) + Endif + Endif + + Next + + If sRef = "property" Then + s = ars[ars.max] & " AS " & aOut.last & " For " & sAccess + Else + If sRef = "method" Then + s = ars[ars.Max] & "(" & aIn.Join(", ") & ")" + Else If sRef = "signal" Then + s = ars[ars.Max] + Endif + If aOut.Count Then + s &= IIf(sRef = "signal", " Sends ", " As ") + If aOut.Count = 1 Then + s &= aOut.last + Else + s &= "[" & aOut.Join(", ") & "] as variant[]" + Endif + Else + If sRef = "signal" Then s &= " Sends Nothing" + Endif + Endif + + Return s + +End + +Public Sub tvDbus_Select() + + MakeSignature(Last.item.key) + +End + +Public Sub tvDbus_MouseMove() + + Dim s As String + Dim ix, iy As Integer + Dim hcont As Object + + If Not tvDbus.FindAt(Mouse.x, Mouse.y) Then + + s = MakeSignature(tvDbus.item.key) + + hcont = tvDbus.Parent + ix = tvDbus.Item.X + tvDbus.Item.w / 2 + iy = tvDbus.Item.Y + + 'lblsignature.Text = s + 'lblsignature.Left = Min(iX, tvDbus.Width - lblsignature.Width) + 'lblsignature.Y = iY + 'lblsignature.Visible = True + 'lblsignature.Refresh + 'Else + 'lblsignature.Visible = False + Endif + lblsignature.Text = s + +End + +Public Sub btnShowId_Click() + + btnRefresh_Click + +End diff --git a/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form new file mode 100644 index 00000000..524034b5 --- /dev/null +++ b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form @@ -0,0 +1,121 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,107,84) + Text = ("Gambas DBus Explorer") + Icon = Picture["dbus64.png"] + Arrangement = Arrange.Vertical + { Panel1 Panel + MoveScaled(3,0,102,4) + Arrangement = Arrange.Horizontal + { btnRefresh ToolButton + MoveScaled(0,0,11,4) + AutoResize = True + Text = ("Refresh") + Picture = Picture["icon:/small/refresh"] + } + { btnShowId ToolButton + MoveScaled(12,0,15,4) + AutoResize = True + Text = ("Show bus ids") + Picture = Picture["icon:/small/lamp"] + Toggle = True + } + { Panel2 Panel + MoveScaled(37,1,2,3) + Expand = True + } + { HBox1 HBox + MoveScaled(47,0,46,4) + Spacing = True + { Label4 Label + MoveScaled(0,0,9,4) + Font = Font["Bold"] + Text = ("Legend:") + } + { PictureBox1 PictureBox + MoveScaled(8,0,3,4) + Picture = Picture.Load("method.png") + Alignment = Align.Center + } + { Label1 Label + MoveScaled(11,0,8,4) + AutoResize = True + Text = ("Method") + } + { PictureBox2 PictureBox + MoveScaled(20,0,3,4) + Picture = Picture.Load("property.png") + Alignment = Align.Center + } + { Label2 Label + MoveScaled(25,0,8,4) + AutoResize = True + Text = ("Property") + } + { PictureBox3 PictureBox + MoveScaled(34,0,3,4) + Picture = Picture.Load("signal.png") + Alignment = Align.Center + } + { Label3 Label + MoveScaled(38,0,6,4) + AutoResize = True + Text = ("Signal") + } + } + } + { Separator2 Separator + MoveScaled(20,5,18,0) + } + { HSplit1 HSplit + MoveScaled(0,8,106,74) + Expand = True + { TabStrip3 TabStrip + MoveScaled(2,5,39,57) + Arrangement = Arrange.Fill + Count = 2 + Index = 0 + Text = ("Session") + { lstbSession ListBox lstb + Name = "lstbSession" + MoveScaled(2,1,30,51) + Tag = "session" + Expand = True + } + Index = 1 + Text = ("System") + { lstbSystem ListBox lstb + Name = "lstbSystem" + MoveScaled(3,1,30,50) + Tag = "system" + Expand = True + } + Index = 0 + } + { VBox1 HBox + MoveScaled(46,2,59,65) + { Separator3 Separator + MoveScaled(2,19,0,30) + } + { VBox2 VBox + MoveScaled(3,8,54,56) + Expand = True + { tvDbus TreeView + MoveScaled(7,4,33,32) + Expand = True + Border = False + } + { Separator4 Separator + MoveScaled(12,45,23,0) + } + { lblsignature TextLabel + MoveScaled(5,49,34,3) + Font = Font["Monospace,-2"] + Padding = 4 + AutoResize = True + } + } + } + } +} diff --git a/app/examples/Misc/DBusExplorer/dbus22.png b/app/examples/Misc/DBusExplorer/dbus22.png new file mode 100644 index 0000000000000000000000000000000000000000..eb4592e8a31fdfb42176a2a111be0ead97ce2618 GIT binary patch literal 894 zcmV-^1A+XBP)by}iAU3r;NZr_#=5$?#KgqR%*@{2-r3pNy1Kf_$;s~T@4~{u#>U3R#>Tq3y3EYX z-rnB2y1K{5$Hc_M?eOx--Ra2O=-KA)^7i@d@bS#v>YBaPXq~`6g0MS)u2PS<z%;YYoEeEgRqLT&-L~7$=vB~p~FIiu|9&YQIEG>m%L4kwn~b$@bmS4tIAJ~ zwqBRKe5=T^&EHm$xmJ?7yVK+F_WA$+|CPGa*yirl606@8moIFMz`B&l{!M>*pTZ{&2Ux0@&^|%8Z#OxbFY}0YgbdK~xyiV_-l4MkZz! zRyKAHBq2^NZXRAf4NWa=9ex3bkf4yT2%D&`p1y&hk&%(Hi5N&wTtZUH)Xdz%(g+Hy zq-B7jveq`Xb}%7(2S+DoIe7*K1s7L)H+K(DFOaCWkFTG=6mVwMJuK-(|p~j%9l3rO=ZDdqqVPsTW2XT+5GJ}$241`_ZP}W!pVKzxJ zFenB<*v)}{fni`)ORE9{gS?zGnA6tY(b;8WWM|vmBP-6pAS2xiQQp@-!NT0kR7z4p z926*G6DI)$4Gr}5bVb=jgoOkl0Vu#fdCF894L)8TZZ1xwu;yTAV`X7xVnh-G0F1&; UGQ-lT6aWAK07*qoM6N<$f}{cRmH+?% literal 0 HcmV?d00001 diff --git a/app/examples/Misc/DBusExplorer/dbus64.png b/app/examples/Misc/DBusExplorer/dbus64.png new file mode 100644 index 0000000000000000000000000000000000000000..49d37d24788424d12d195ed72d581cb38295d723 GIT binary patch literal 4222 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 Cs0%Pjt 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Evaluator\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:08+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FEval.form:61 +msgid "45" +msgstr "-" + +#: FEval.form:67 +msgid "60" +msgstr "-" + +#: FEval.form:45 +msgid "a =" +msgstr "-" + +#: FEval.form:77 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "-" + +#: FEval.form:40 +msgid "b =" +msgstr "-" + +#: FEval.form:24 +msgid "Evaluator" +msgstr "Avaluador" + +#: FEval.form:72 +msgid "Expression" +msgstr "Expressió" + +#: .project:1 +msgid "Expression evaluation example" +msgstr "Exemple d'avaluació d'expressions" + +#: FEval.form:98 +msgid "&Quit" +msgstr "&Surt" + +#: FEval.form:82 +msgid "Result" +msgstr "Resultat" + +#: FEval.form:108 +msgid "This is an example of how to use the gb.eval expression evaluator component." +msgstr "Això és un exemple de com fer servir el component d'avaluació d'expressions gb.eval." + +#: FEval.form:92 +msgid "&Update result" +msgstr "&Actualitza el resultat" + +#: FEval.form:35 +msgid "x =" +msgstr "-" + +#: FEval.form:30 +msgid "y =" +msgstr "-" + diff --git a/app/examples/Misc/Evaluator/.lang/cs.po b/app/examples/Misc/Evaluator/.lang/cs.po new file mode 100644 index 00000000..cb920ab0 --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/cs.po @@ -0,0 +1,68 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Expression evaluation example" +msgstr "Příklad vykonávače výrazů" + +#: FEval.form:24 +msgid "Evaluator" +msgstr "-" + +#: FEval.form:30 +msgid "y =" +msgstr "-" + +#: FEval.form:35 +msgid "x =" +msgstr "-" + +#: FEval.form:40 +msgid "b =" +msgstr "-" + +#: FEval.form:45 +msgid "a =" +msgstr "-" + +#: FEval.form:61 +msgid "45" +msgstr "-" + +#: FEval.form:67 +msgid "60" +msgstr "-" + +#: FEval.form:72 +msgid "Expression" +msgstr "Výraz" + +#: FEval.form:77 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "-" + +#: FEval.form:82 +msgid "Result" +msgstr "Výsledek" + +#: FEval.form:92 +msgid "&Update result" +msgstr "&Vykonej" + +#: FEval.form:98 +msgid "&Quit" +msgstr "&Zavřít" + +#: FEval.form:108 +msgid "This is an example of how to use the gb.eval expression evaluator component." +msgstr "To je příklad, jak použít komponentu gb.eval pro vyhodnocení výrazu." diff --git a/app/examples/Misc/Evaluator/.lang/de.po b/app/examples/Misc/Evaluator/.lang/de.po new file mode 100644 index 00000000..eb17d871 --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/de.po @@ -0,0 +1,69 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Expression evaluation example" +msgstr "Beispiel für einen Ausdrucks-Auswerter" + +#: FEval.form:24 +msgid "Evaluator" +msgstr "Auswerter" + +#: FEval.form:30 +msgid "y =" +msgstr "-" + +#: FEval.form:35 +msgid "x =" +msgstr "-" + +#: FEval.form:40 +msgid "b =" +msgstr "-" + +#: FEval.form:45 +msgid "a =" +msgstr "-" + +#: FEval.form:61 +msgid "45" +msgstr "-" + +#: FEval.form:67 +msgid "60" +msgstr "-" + +#: FEval.form:72 +msgid "Expression" +msgstr "Ausdruck" + +#: FEval.form:77 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "-" + +#: FEval.form:82 +msgid "Result" +msgstr "Resultat" + +#: FEval.form:92 +msgid "&Update result" +msgstr "Be&rechnen" + +#: FEval.form:98 +msgid "&Quit" +msgstr "&Beenden" + +#: FEval.form:108 +msgid "This is an example of how to use the gb.eval expression evaluator component." +msgstr "Dies ist ein Beispiel, wie man die gb.eval -Komponente benutzt." + diff --git a/app/examples/Misc/Evaluator/.lang/es.po b/app/examples/Misc/Evaluator/.lang/es.po new file mode 100644 index 00000000..cea4870e --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/es.po @@ -0,0 +1,71 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FEval.class:72 +msgid "Evaluator" +msgstr "Evaluador" + +#: FEval.class:78 +msgid "y =" +msgstr "y =" + +#: FEval.class:83 +msgid "x =" +msgstr "x =" + +#: FEval.class:88 +msgid "b =" +msgstr "b =" + +#: FEval.class:93 +msgid "a =" +msgstr "a =" + +#: FEval.class:99 +msgid "0,5" +msgstr "0,5" + +#: FEval.class:105 +msgid "1" +msgstr "1" + +#: FEval.class:111 +msgid "45" +msgstr "45" + +#: FEval.class:117 +msgid "60" +msgstr "60" + +#: FEval.class:122 +msgid "Expression" +msgstr "Expresión" + +#: FEval.class:127 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "a * Cos(Rad(x)) + b * Sin(Rad(y))" + +#: FEval.class:132 +msgid "Result" +msgstr "Resultado" + +#: FEval.class:143 +msgid "&Update result" +msgstr "&Actualizar resultado" + +#: FEval.class:149 +msgid "&Quit" +msgstr "&Salir" + +#: FEval.class:155 +msgid "This is a sample of how to use the expression evaluator." +msgstr "Este es un ejemplo de como usar el evaluador de expresiones." diff --git a/app/examples/Misc/Evaluator/.lang/ru.po b/app/examples/Misc/Evaluator/.lang/ru.po new file mode 100644 index 00000000..dceaba59 --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/ru.po @@ -0,0 +1,82 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Misc/Evaluator/.project:19 +msgid "Expression evaluation example" +msgstr "Пример вычисления выражения" + +#: app/examples/Misc/Evaluator/.src/FEval.form:5 +msgid "Evaluator" +msgstr "Вычислитель" + +#: app/examples/Misc/Evaluator/.src/FEval.form:10 +msgid "y =" +msgstr "y =" + +#: app/examples/Misc/Evaluator/.src/FEval.form:14 +msgid "x =" +msgstr "x =" + +#: app/examples/Misc/Evaluator/.src/FEval.form:18 +msgid "b =" +msgstr "b =" + +#: app/examples/Misc/Evaluator/.src/FEval.form:22 +msgid "a =" +msgstr "a =" + +#: app/examples/Misc/Evaluator/.src/FEval.form:35 +msgid "45" +msgstr "45" + +#: app/examples/Misc/Evaluator/.src/FEval.form:40 +msgid "60" +msgstr "60" + +#: app/examples/Misc/Evaluator/.src/FEval.form:44 +msgid "Expression" +msgstr "Выражение" + +#: app/examples/Misc/Evaluator/.src/FEval.form:48 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "a * Cos(Rad(x)) + b * Sin(Rad(y))" + +#: app/examples/Misc/Evaluator/.src/FEval.form:52 +msgid "Result" +msgstr "Результат" + +#: app/examples/Misc/Evaluator/.src/FEval.form:60 +msgid "&Update result" +msgstr "Обновить результат" + +#: app/examples/Misc/Evaluator/.src/FEval.form:65 +msgid "&Quit" +msgstr "Выход" + +#: app/examples/Misc/Evaluator/.src/FEval.form:74 +msgid "This is an example of how to use the gb.eval expression evaluator component." +msgstr "Это пример того, как использовать компонент вычисления выражения gb.eval." + diff --git a/app/examples/Misc/Evaluator/.project b/app/examples/Misc/Evaluator/.project new file mode 100644 index 00000000..39a65dde --- /dev/null +++ b/app/examples/Misc/Evaluator/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Expression evaluation example +Startup=FEval +Icon=calculator.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.eval +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Miscellaneous/Evaluator/Evaluator +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Misc/Evaluator/.src/FEval.class b/app/examples/Misc/Evaluator/.src/FEval.class new file mode 100644 index 00000000..589d4a5a --- /dev/null +++ b/app/examples/Misc/Evaluator/.src/FEval.class @@ -0,0 +1,53 @@ +' Gambas class file + +Private $hExpr As New Expression +Private $cEnv As New Collection + +Static Public Sub Main() + + Dim hForm As Form + + hForm = New FEval + hForm.Show + +End + + +Public Sub btnUpdate_Click() + + Dim cCol As New Collection + + If $hExpr.Text <> txtExpr.Text Then + $hExpr.Text = txtExpr.Text + Endif + + 'cEnv.Compare = gb.Case + + $cEnv["a"] = CFloat(Val(txtValueA.Text)) + $cEnv["b"] = CFloat(Val(txtValueB.Text)) + $cEnv["x"] = CFloat(Val(txtValueX.Text)) + $cEnv["y"] = CFloat(Val(txtValueY.Text)) + + $hExpr.Compile + $hExpr.Environment = $cEnv + + txtResult.Text = Str($hExpr.Value) + +Catch + + txtResult.Text = Error.Text + +End + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub Form_Open() + + txtValueA.Text = Str(0.5) + txtValueB.Text = Str(1) + +End diff --git a/app/examples/Misc/Evaluator/.src/FEval.form b/app/examples/Misc/Evaluator/.src/FEval.form new file mode 100644 index 00000000..722a4b9b --- /dev/null +++ b/app/examples/Misc/Evaluator/.src/FEval.form @@ -0,0 +1,83 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(32,28,51,41) + Text = ("Evaluator") + Icon = Picture["calculator.png"] + Resizable = False + { Label4 Label + MoveScaled(27,17,7,4) + Text = ("y =") + } + { Label3 Label + MoveScaled(27,12,7,4) + Text = ("x =") + } + { Label2 Label + MoveScaled(2,17,7,4) + Text = ("b =") + } + { Label1 Label + MoveScaled(2,12,7,4) + Text = ("a =") + } + { txtValueA TextBox txtValue + Name = "txtValueA" + MoveScaled(6,12,17,4) + } + { txtValueB TextBox txtValue + Name = "txtValueB" + MoveScaled(6,17,17,4) + } + { txtValueX TextBox txtValue + Name = "txtValueX" + MoveScaled(31,12,17,4) + Text = ("45") + } + { txtValueY TextBox txtValue + Name = "txtValueY" + MoveScaled(31,17,17,4) + Text = ("60") + } + { Label6 Label + MoveScaled(2,23,13,4) + Text = ("Expression") + } + { txtExpr TextBox + MoveScaled(15,23,35,4) + Text = ("a * Cos(Rad(x)) + b * Sin(Rad(y))") + } + { Label7 Label + MoveScaled(2,28,13,4) + Text = ("Result") + } + { txtResult TextBox + MoveScaled(15,28,35,4) + ReadOnly = True + } + { btnUpdate Button + MoveScaled(13,36,26,4) + Text = ("&Update result") + Default = True + } + { btnClose Button + MoveScaled(40,36,10,4) + Text = ("&Quit") + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(1,1,49,7) + Font = Font["Bold,Italic"] + Background = Color.SelectedBackground + Foreground = Color.SelectedForeground + Padding = 6 + Text = ("This is an example of how to use the gb.eval expression evaluator component.") + Alignment = Align.Normal + } + { Separator1 Separator + MoveScaled(1,9,49,1) + } + { Separator2 Separator + MoveScaled(0,34,50,1) + } +} diff --git a/app/examples/Misc/Evaluator/calculator.png b/app/examples/Misc/Evaluator/calculator.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5c2b73885e0daf93305bc16dfcd8fc1c664899 GIT binary patch literal 3612 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

" Then + If Not Trim(aLine[iLine - 1]) Then + If iLine < aLine.Max And If GetIndent(aLine[iLine + 1]) >= hMarkupList.iIndent Then + bJustList = False + Endif + 'aResult[aResult.Max - 1] = "

\n" & aResult[aResult.Max - 1] + Endif + aResult.Remove(aResult.Max) + Else + GoSub CLOSE_PARA + aResult.Add(Space$(iCurrentIndent) & "
    ") + Endif + iCurrentIndent = hMarkupList.iIndent + aResult.Add(Space$(iCurrentIndent) & "
  1. ") + sLine = Mid$(sLine, 3) + Endif + + Endif + + ' Blockquote again! + + I = iBlockQuote + Do + If Left(sLine) <> ">" Then Break + sCar = Mid$(sLine, 2, 1) + If sCar <> " " And If sCar <> gb.Tab Then Break + Inc I + sLine = LTrim(Mid$(sLine, 3)) + Loop + + If I > iBlockQuote Then + While I > iBlockQuote + aResult.Add("
    ") + Inc iBlockQuote + Wend + Else If I < iBlockQuote Then + While I < iBlockQuote + aResult.Add("
    ") + Dec iBlockQuote + Wend + Endif + + If sLine = "[[" Or If sLine Begins "[[ " Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + $aTable.Push("[[") + sLine = Trim(Mid$(sLine, 3)) + $aTableClass.Push(sLine) + $aTablePos.Push(aResult.Count) + If sLine Then + If sLine Begins "code " Then + Inc Verbatim + Else + aCommand = $hMarkdown.Enter(sLine) + If aCommand Then aResult.Insert(aCommand) + Endif + Endif + 'aResult.Add("") + bIgnorePar = False + bInsidePar = False + Continue + + Endif + + If $aTable.Count Then + + If sLine = "]]" Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + sTable = $aTable.Pop() + sClass = $aTableClass.Pop() + iPos = $aTablePos.Pop() + + aCommand = aResult.Extract(iPos, -1) + + If Left(sClass) <> " " Then + aResult.Add("
    ") + If sClass Begins "code " Then + aTemp = ProcessCode(sClass, aCommand) + Else + aTemp = $hMarkdown.Process(sClass, aCommand) + Endif + If aTemp Then aCommand = aTemp + aResult.Insert(aCommand) + aResult.Add("
    ") + Else + sClass = Trim(sClass) + If Not sClass Then sClass = "table" + aResult.Add("") + If aCommand.Count And If aCommand[0] = "" Then + aCommand.Remove(0) + Else + aResult.Add("") + aResult.Add("
    ") + Endif + aResult.Insert(aCommand) + aResult.Add("
    ") + Endif + + If sClass Begins "code " Then + Dec Verbatim + Else If sClass Then + aCommand = $hMarkdown.Leave(sClass) + If aCommand Then aResult.Insert(aCommand) + Endif + + Continue + + Else If sLine = "--" Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + sTable = $aTable[$aTable.Max] + If sTable = "[[" Then + aResult.Add("") + Else + aResult.Add("") + Endif + bIgnorePar = True + bInsidePar = True + $aTableClass[$aTableClass.Max] = " " & Trim($aTableClass[$aTableClass.Max]) + Continue + + Else If sLine = "==" Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + sTable = $aTable[$aTable.Max] + If sTable = "[[" Then + aResult.Add("") + Else + aResult.Add("") + Endif + aResult.Add("") + $aTable[$aTable.Max] = "==" + $aTableClass[$aTableClass.Max] = " " & Trim($aTableClass[$aTableClass.Max]) + bIgnorePar = True + bInsidePar = True + Continue + + Endif + + Endif + + If sLine Begins "==" And If sLine = String$(Len(sLine), "=") Then + sLine = aResult[aResult.Max] + If sLine Not Begins "" Then + sLine = Mid$(sLine, 4) + bInsidePar = False + Endif + GoSub CLOSE_PARA + aResult[aResult.Max] = "

    " & sLine & "

    " + Endif + Continue + Endif + + If sLine Begins "--" And If sLine = String$(Len(sLine), "-") And If bInsidePar Then + sLine = Trim(aResult[aResult.Max]) + If sLine Then + If sLine Not Begins "" Then + sLine = Mid$(sLine, 4) + bInsidePar = False + Endif + GoSub CLOSE_PARA + aResult[aResult.Max] = "

    " & sLine & "

    " + Endif + Continue + Endif + Endif + + ' Code + + If EnableCode >= 0 Then + If sLine Begins " " Or If sLine Begins gb.Tab Then + + If Left(sLine) = gb.Tab Then + sLine = Mid$(sLine, 2) + Else + sLine = Mid$(sLine, 5) + Endif + If sLine = "----" Then + sLine = "
    " + Else + sLine = Html$(sLine) + Endif + If Not bCode Then + GoSub CLOSE_PARA + bCode = True + sLine = "
    " & sLine
    +        Endif
    +        aResult.Add(sLine)
    +        Continue
    +        
    +      Endif
    +    Endif
    +    
    +    GoSub CLOSE_CODE
    +  
    +    ' Title
    +    
    +    If Left(sLine) = "#" Then
    +      I = InStr(sLine, " ")
    +      If I > 0 And If I <= 7 Then
    +        Dec I
    +        If Left(sLine, I) = String$(I, "#") Then
    +          sLine = Mid$(sLine, I + 2)
    +          While sLine Ends "#"
    +            sLine = Left(sLine, -1)
    +          Wend
    +          sLine = RTrim(sLine)
    +          If Left(sLine) = "[" And If Right(sLine) = "]" Then
    +            sLine = ConvertLine(Mid$(sLine, 2, -1))
    +            $aIndex.Add(String$(I - 1, "  ") & "- [" & sLine & "] (#t" & CStr($aIndex.Count + 1) & ")")
    +            sLine = "" & "" & sLine & ""
    +          Else
    +            sLine = "" & ConvertLine(sLine) & ""
    +          Endif
    +          GoSub CLOSE_PARA
    +          aResult.Add(sLine)
    +          Continue
    +        Endif
    +      Endif
    +    Endif
    +      
    +    If Trim(sLine) Then
    +      If Not bInsidePar And If LTrim(sLine) Not Begins "" & LTrim(sLine)
    +      bInsidePar = True
    +      bAddPar = False
    +    Endif
    +    
    +    aResult.Add(sLine)
    +    
    +  Wend
    +  
    +  GoSub CLOSE_PARA
    +  GoSub CLOSE_CODE
    +  GoSub CLOSE_BLOCKQUOTE
    +  iIndent = 0
    +  GoSub CLOSE_LIST
    +  'If $aMarkup.Count Then Error.Raise("Missing markup: " & $aMarkup[$aMarkup.Max])
    +  
    +  If $aIndex.Count And If iIndexPos >= 0 Then
    +    iIndent = GetIndent($aIndex[0])
    +    For I = 1 To $aIndex.Max
    +      iIndent = Min(iIndent, GetIndent($aIndex[I]))
    +    Next
    +    If iIndent Then
    +      For I = 0 To $aIndex.Max
    +        $aIndex[I] = Mid$($aIndex[I], iIndent + 1)
    +      Next
    +    Endif
    +    aResult[iIndexPos] = "
    \n" & ConvertMarkup($aIndex, True) & "
    \n" + Endif + + If $hMarkdown.LinkAnchors And If Not bNoLineAnchors Then + + iLine = 0 + For I = 0 To aResult.Max + If cLine.Exist(I) Then + aLine = New String[] + 'If iLine Then aLine.Add("") + iNextLine = cLine[I] + For J = iLine To iNextLine - 1 + aLine.Add("") ' & "") + Next + J = Len(aResult[I]) - Len(LTrim(aResult[I])) + aResult[I] = Left(aResult[I], J) & aLine.Join("") & "" & Mid$(aResult[I], J + 1) + iLine = iNextLine + 1 + Endif + Next + + 'aLine = New String[] + 'If iLine Then aLine.Add("") + aResult.Add("") ' & "") + + Endif + + $bComment = bSaveComment + $aMarkup = aSaveMarkup + $aTable = aSaveTable + $aTableClass = aSaveTableClass + $aTablePos = aSaveTablePos + Return aResult.Join("\n") + +CLOSE_CODE: + + If bCode Then + aResult.Add("
    ") + bCode = False + Endif + Return + +CLOSE_BLOCKQUOTE: + + While iBlockQuote + aResult[aResult.Max] &= "" + Dec iBlockQuote + Wend + Return + +CLOSE_LIST: + + While iIndent < iCurrentIndent + GoSub CLOSE_PARA + GoSub CLOSE_CODE + 'aResult.Add(Space$(iCurrentIndent) & "
  2. ") + aResult[aResult.Max] &= "" + bJustList = False + aResult.Add(Space$(aList[aList.Max].iIndent) & "") + aList.Remove(aList.Max) + If aList.Count Then + iCurrentIndent = aList[aList.Max].iIndent + Else + iCurrentIndent = 0 + Endif + Wend + Return + +CLOSE_PARA: + + If bInsidePar Then + If Not bIgnorePar Then aResult[aResult.Max] &= "

    " + 'aResult.Add("") + bInsidePar = False + bIgnorePar = False + 'Else If iLine > 0 And If aResult[aResult.Max] Then + ' 'aResult.Add("") + Endif + Return + +ADD_LINE: + + cLine[aResult.Count] = iLine + Return + +End + + +Private Sub ConvertLine(sLine As String) As String + + Dim sResult As String + Dim I, L As Integer + Dim sCar As String + Dim I1, I2 As Integer + Dim sPattern As String + Dim bCode As Boolean + Dim bEmph As Boolean + Dim bStrong As Boolean + Dim sText, sTitle, sLink As String + Dim bBlank As Boolean + Dim bUnderline As Boolean + Dim bLimitBefore, bLimitAfter As Boolean + Dim iLen As Integer + Dim sCommand As String + Dim aCommand As String[] + Dim iPos As Integer + + iLen = String.Len(sLine) + +MAIN_LOOP: + + If I >= iLen Then + If bEmph Then + sResult &= "" + Else If bStrong Then + sResult &= "" + Endif + If bCode Then sResult &= "" + Return sResult + Endif + + GoSub NEXT_CAR + + If sCar = "\\" Then + If I = iLen Then + sResult &= "
    " + Else + GoSub NEXT_CAR + sResult &= Html(sCar) + Endif + Goto MAIN_LOOP + Endif + + If sCar = "<" Then Goto ENTER_MARKUP + + If sCar = "&" Then Goto ENTER_AMPERSAND + + If sCar = "[" And If String.Mid$(sLine, I + 1, 1) <> " " Then Goto ENTER_LINK + + If sCar = "@" And If String.Mid$(sLine, I + 1, 1) = "{" Then Goto ENTER_COMMAND + + If I = 1 Or If IsWordLimit(String.Mid$(sLine, I - 1, 1)) Then + bLimitBefore = True + Else + bLimitBefore = False + Endif + + If IsWordLimit(String.Mid$(sLine, I + 1, 1)) Then + bLimitAfter = True + Else + bLimitAfter = False + Endif + + If bLimitBefore Or If bLimitAfter Then + If sCar = "`" And If Not bCode Then Goto ENTER_CODE + If sCar = "'" Then Goto ENTER_LIGHT_CODE + If sCar = "*" Then Goto ENTER_STAR + If sCar = "~" Then Goto ENTER_UNDERLINE + Endif + + If sCar = ">" Then + sCar = ">" + Endif + + sResult &= sCar + + Goto MAIN_LOOP + +NEXT_CAR: + + If I >= iLen Then + I = iLen + 1 + sCar = "" + Else + Inc I + sCar = String.Mid$(sLine, I, 1) + Endif + Return + +LOOK_CAR: + + sCar = String.Mid$(sLine, I + 1, 1) + Return + +ENTER_MARKUP: + + If String.Mid$(sLine, I, 4) = " "; sData + Print #$hStream, sData + Flush #$hStream + Return GetLine(bMulti) + +End + +Public Sub Print(Optional sData As String) + + If Not Me.Connected Then Return + + If _Debug Then Error "--> "; sData + Print #$hStream, sData + Flush #$hStream + +End + + +Public Function GetLine(Optional bMulti As Boolean) As String + + Dim sLine As String + Dim sResponse As String + Dim iPos As Integer + + If Not Me.Connected Then Return + + bMulti = True + + If bMulti Then + + Do + Line Input #$hStream, sLine + sResponse &= sLine & "\n" + If sLine Like "??? *" Then Break + If $hStream.EndOfFile Then Break + Loop + + Else + + Line Input #$hStream, sLine + sResponse = sLine + + Endif + + LastAnswer = sLine + iPos = InStr(sLine, " ") + If iPos = 0 Then + LastCode = "" + Else + LastCode = Left(sLine, iPos - 1) + Endif + + If _Debug Then Error sResponse + Return sResponse + +End + +Private Function Stream_Read() As Stream + + Return $hStream + +End + +Private Sub Stream_Write(Value As Stream) + + $hStream = Value + +End + +Private Function Connected_Read() As Boolean + +End + +Public Sub PrintHeader(sHeader As String, sStr As String, Optional bAddress As Boolean) + + If Not sStr Or If IsAscii(sStr) Then + If sStr Not Begins "=?utf-8?q?" Or If sStr Not Ends "?=" Then + Print(sHeader & ": " & sStr) + Return + Endif + Endif + + Encode.PrintQuotedHeader($hStream, sHeader, sStr, bAddress, _Debug) + +End + +Private Function Client_Read() As SmtpClient + + Return $hClient + +End diff --git a/comp/src/gb.net.smtp/.src/SslSession.class b/comp/src/gb.net.smtp/.src/SslSession.class new file mode 100644 index 00000000..4f5368ea --- /dev/null +++ b/comp/src/gb.net.smtp/.src/SslSession.class @@ -0,0 +1,76 @@ +' Gambas class file + +Inherits SmtpSession + +Property Read Connected As Boolean + +Private $hProcess As Process +Private $sError As String +Private $bConnected As Boolean +Private $bDisconnect As Boolean + +Public Sub Connect(hClient As SmtpClient, sHost As String, iPort As Integer) + + Dim aExec As String[] + + If Not sHost Then sHost = "localhost" + If iPort = 0 Then iPort = 465 + + Super.Connect(hClient, sHost, iPort) + + aExec = [SmtpSession.GetOpenSSLPath(), "s_client", "-quiet", "-connect", sHost & ":" & iPort] + If Me._Debug Then Error "gb.net.smtp: running: "; aExec.Join(" ") + + $hProcess = Exec aExec For Read Write As "OpenSSL" + $hProcess.Blocking = True + $hProcess.EndOfLine = gb.Windows + + $bConnected = False + Repeat + Wait 0.1 + If Not $hProcess Then + If $sError Then + Error.Raise(Trim(Split($sError, "\n")[0])) + Else + Error.Raise("SSL session has stopped unexpectedly") + Endif + Endif + Until $bConnected + + Me.Stream = $hProcess + +End + +Public Sub Disconnect() + + $bDisconnect = True + $hProcess.Kill() + +End + +Public Sub OpenSSL_Error(Text As String) + + If Me._Debug Then Error "openssl: "; Quote(Text) + $sError &= Text + +End + +Public Sub OpenSSL_Read() + + $bConnected = True + +End + + +Public Sub OpenSSL_Kill() + + Super.Disconnect + $hProcess = Null + +End + +Private Function Connected_Read() As Boolean + + If $hProcess And If $hProcess.State = Process.Running Then Return True + +End diff --git a/comp/src/gb.net.smtp/.src/TcpSession.class b/comp/src/gb.net.smtp/.src/TcpSession.class new file mode 100644 index 00000000..c7d4be85 --- /dev/null +++ b/comp/src/gb.net.smtp/.src/TcpSession.class @@ -0,0 +1,89 @@ +' Gambas class file + +Inherits SmtpSession + +Property Read Connected As Boolean + +Private $hSocket As New Socket + +Public Sub _new() + + 'Wait 10 seconds before timing out + $hSocket.Timeout = 10000 + $hSocket.EndOfLine = gb.Windows + +End + +Public Sub Client_Ready() + + 'Debug "Connected to remote host " & sSocket.Path + +End + +Public Sub Client_Closed() + + 'Debug "Connection Closed by foreign host." + +End + +Public Sub Client_Found() + + 'Debug "Host Found. Connecting..." + +End + +Public Sub Connect(hClient As SmtpClient, sHost As String, iPort As Integer) + + If Not sHost Then sHost = "localhost" + If iPort = 0 Then iPort = 25 + + Super.Connect(hClient, sHost, iPort) + + $hSocket.Timeout = 10000 + $hSocket.Connect(sHost, iPort) + $hSocket.Blocking = True + Me.Stream = $hSocket + + Do + 'Print $hSocket.Status + If $hSocket.Status = Net.Connected Or If $hSocket.Status <= 0 Then Break + Wait 0.1 + Loop + + If $hSocket.Status < 0 Then + + Select Case $hSocket.Status + + Case Net.CannotCreateSocket + Error.Raise("Cannot create socket") + + Case Net.HostNotFound + Error.Raise("Host not Found") + + Case Net.ConnectionRefused + Error.Raise("Connection Refused") + + Case Net.CannotRead + Error.Raise("Read error") + + Case Net.CannotWrite + Error.Raise("Write error") + + End Select + + Endif + +End + +Public Sub Disconnect() + + Try $hSocket.Close + Super.Disconnect + +End + +Private Function Connected_Read() As Boolean + + Return $hSocket.Status = Net.Connected + +End diff --git a/comp/src/gb.net.smtp/.src/TlsSession.class b/comp/src/gb.net.smtp/.src/TlsSession.class new file mode 100644 index 00000000..d90a8c08 --- /dev/null +++ b/comp/src/gb.net.smtp/.src/TlsSession.class @@ -0,0 +1,77 @@ +' Gambas class file + +Inherits SmtpSession + +Property Read Connected As Boolean + +Private $hProcess As Process +Private $bConnected As Boolean +Private $sError As String + +Public Sub Connect(hClient As SmtpClient, sHost As String, iPort As Integer) + + Dim aExec As String[] + Dim I As Integer + + If Not sHost Then sHost = "localhost" + If iPort = 0 Then iPort = 587 + + Super.Connect(hClient, sHost, iPort) + + aExec = [SmtpSession.GetOpenSSLPath(), "s_client", "-quiet", "-starttls", "smtp", "-connect", sHost & ":" & iPort] + If Me._Debug Then Error "gb.net.smtp: running: "; aExec.Join(" ") + + $hProcess = Exec aExec For Read Write As "OpenSSL" + $hProcess.Blocking = True + $hProcess.EndOfLine = gb.Windows + + $bConnected = False + For I = 1 To 10 + Wait 0.1 + If Not $hProcess Then + If $sError Then + Error.Raise(Trim(Split($sError, "\n")[0])) + Else + Error.Raise("TLS session has stopped unexpectedly") + Endif + Endif + Next + + $bConnected = True + + Me.Stream = $hProcess + +End + +Public Sub Disconnect() + + $hProcess.Kill() + +End + +Public Sub OpenSSL_Error(Text As String) + + If Me._Debug Then Error "openssl: "; Quote(Text) + $sError &= Text + +End + +Public Sub OpenSSL_Read() + + $bConnected = True + +End + + +Public Sub OpenSSL_Kill() + + Super.Disconnect + $hProcess = Null + +End + +Private Function Connected_Read() As Boolean + + If $hProcess And If $hProcess.State = Process.Running Then Return True + +End diff --git a/comp/src/gb.report/.component b/comp/src/gb.report/.component new file mode 100644 index 00000000..a8dfb6ad --- /dev/null +++ b/comp/src/gb.report/.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.report +Version=3.15.90 +State=3 +Authors=Fabien Bodard +Needs=Form,ImageIO +Requires=gb.form diff --git a/comp/src/gb.report/.connection/Connection1.connection b/comp/src/gb.report/.connection/Connection1.connection new file mode 100644 index 00000000..a918b624 --- /dev/null +++ b/comp/src/gb.report/.connection/Connection1.connection @@ -0,0 +1,10 @@ +[Connection] +Type="mysql" +Host="localhost" +RememberPassword=False +IgnoreCharset=False +Database="test" +User="root" +DisplayMetadata=False +Requests=["select distinct firstname from test"] + diff --git a/comp/src/gb.report/.connection/Connection2.connection b/comp/src/gb.report/.connection/Connection2.connection new file mode 100644 index 00000000..cf224bc1 --- /dev/null +++ b/comp/src/gb.report/.connection/Connection2.connection @@ -0,0 +1,10 @@ +[Connection] +Type="mysql" +Host="localhost" +User="root" +RememberPassword=False +IgnoreCharset=False +Database="Laurux01" +DisplayMetadata=False +Requests=[""] + diff --git a/comp/src/gb.report/.connection/MainConn.connection b/comp/src/gb.report/.connection/MainConn.connection new file mode 100644 index 00000000..c6f0d11e --- /dev/null +++ b/comp/src/gb.report/.connection/MainConn.connection @@ -0,0 +1,9 @@ +[Connection] +Type="sqlite" +Path="/home/fabien/Documents/Gestion/Bases" +RememberPassword=False +IgnoreCharset=False +DisplayMetadata=False +Database="gestion" +Requests=[""] + diff --git a/comp/src/gb.report/.dir_icon.png b/comp/src/gb.report/.dir_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..04f17072df13d7cb5c6a2530f1393161a436fc0c GIT binary patch literal 1571 zcmV+;2Hg3HP)4Z zatrxjnD5M)neR9MxeBEe@pxSEG279jM|)GL)VxtDIDGi<#}+&uk7JtV7GMc%X`8QYs+0Ho(rU z8<{}`$O^wAm8Fn{fgnK^07|I@;JRilz=pyrP)eYbDvqeAT7_TwznT~3Kq&~=g?>zv zjRVZwO!4+#=D2nvO**F$4u{y@+s5<5?F{u`B_Z66T@oZufYg!UWn5FR1dFwxs$f& z9js(CSaugTW**^YQ+WAhJU7GTf3NWUg$%D9?B&HhR^8yG(Yvl&{#Wwr0z6vE@XG5a z=^6esc5^F2(-8=ilC(y1IQcwN|Gb5zIfRTQ(uq6#>^Ba32m5*WV2Q+oEFs$>Xz5sn zj%f&Nvs#&k3XEO2$mXGaXu5&tdKjhvp&>L4%Wg(D0`%>Q(%un6GYssYO(c}#cNgbz zle4_|*59wrvuYcuPF3&BJ&acj+K%nV5x)A^%wRn#YCNJ}; zFMXSbo`Ws@V$gaUbq#=P*Kf0U=MJCv%n(5XethT!oV-u0y_sCzW$cgd^XIAC2m~O| z4U>H@eV4_B8KT|Wxt~~I@}iF}2if1N6BLq0RKy#s0EZ3?6Kf3?RnV|Q5w`B!LogIU(+rj#BzgBw@A1~949mWb5TcwKD&V>WO6#m?CnQ9T+ZXm(!C&X^LhUD_e*>@6Jh`E zE_Oc=rpa1o5iTz;)84FOhiry+bXT2Rm8YC4UOKyaBRt*HijO021XVd#AI3p;qSr-fht z^0!P+FEBjxBp-gX$Y5XBnw<(E@O>YE0|yRZ7zR=nj$@@1rfDLjWMpJyJdsGut_c9J z>xnSeZ>8z(j`6|OIi_ds@||x*8?J%My&j9j$}(5li4!L#&YwU3GLUEp(9&eGvnR~p zzyOJ*O}wzLgZ{qmhO$-ytgNh*)d&O%yW`lgV-u%No%$wltJJ39i)i<@F#U(0W4Ql5 z`}b^GcU~*ab=|V;j^i*gGBR=c^y#YL4HWi>7I0vsuobJv(vk+_}2JYXA(xsC&{q4nXPUn@lD-e*E~a z#>U2e1T3u8rV1dJ%l+Q~B^8exIr5Y7@$nx3D~;Ne0pjua{~WlUjgOBXTQ4}ke*oY4 V*+a}sDck@6002ovPDHLkV1oU`2~Yq4 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.directory b/comp/src/gb.report/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.report/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.report/.hidden/control/reportdrawingarea.png b/comp/src/gb.report/.hidden/control/reportdrawingarea.png new file mode 100644 index 0000000000000000000000000000000000000000..cd577a0649a337c0b3ab44729e985cd474d3909c GIT binary patch literal 621 zcmV-z0+RiSP)7yFc8&Z>5i}a$fLzfB%Y# z|M>WJ`iM#4wMf#onsa2NZe+l2f8uw4|GvIR;kI|2ho5d=zixl%d4KK zaACx6fBb%b|FyN&|JJAfr=YpDtZr=Ocz^$af#mrAf876!#pKj< zfB&ni*Yy9cilEMNfB%4h|K{e~_5X>>>#mET#gU=^p`ri5!Snq8fZYGN?EjzT|K|Ar z;P?Nj=l{p>|CQqZ_Wl3T^Z$q5|MmR;_x=CL@c*3T|Dfgnxa|Ms`2U_^$~pi50QN~l zK~y-)V_+Z%81Vx7DPUqo02XWtSlJMOov;E90tz^}xbZ9C;pOARr+{C8msb#<0wG>r zUSSdJ3Pi=kB_ySQ3V5ZlDUgwslb4rQP*egcP*%ZIpsEIxmRHx%)Y1m3(7{k3qYILk z*VETDFfiohH8Mt5APbf^F$F0wGw0>CKv7_6Wo=^%0(SQH4h9AWj=a219H_DB>;eL= z1`yx|bcH*r0uQhPPpASfpaO3n6a~H@c|U(B2nYm%AQT0`AudQD6ibQp#IC@u00000NkvXX Hu0mjf^C?oX literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportgridview.png b/comp/src/gb.report/.hidden/control/reportgridview.png new file mode 100644 index 0000000000000000000000000000000000000000..32872a01a296ff50965b0c8f4cb4d0f25053e216 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk@BpAX3RW*QAkf)1dh{fsToP-1X5fU;I5_}d4 z+zUVO83-CQG%jFrdvqtDU#H}PT<2wvozfcN2M!f9G&Vj|=(^z;!pg_P!~Q1l$;8SK kj+24$J-?b%;swrBF*rY$xn^-Uc0R}!Pgg&ebxsLQ0J@+nH2?qr literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reporthbox.png b/comp/src/gb.report/.hidden/control/reporthbox.png new file mode 100644 index 0000000000000000000000000000000000000000..792f8fda3752fec145613e3e18628bb6a932a923 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@o&cW^S0Mc#2pSsxGccU9c-IW% zGnEAS1v4;|O+IS@$w9j<+{{sLF0Hu^Ffl>dZeFgADM9Le-N~)I({*yoiaDfNF z0|W2@Po+}nOSWU9ltQ6UnktO}wT4Hj4RfV=Rqgu((RCfuG@k>;jMAKGJ2szx{S&@0 z`763I)8w7&7{wI#qixI@M`*cgW9Ey5YN8Z7o4ITKnL}!vaJZ55O(7l%6L-GIk7GS7 zG(NK}fa#z01DLQD1By}@)TU;M7c)4{H9WtMbXz<1&Mb`XM{`{OqT;dpzQ^H6C%5~P zw50FS^jw^l1)F$%J>gWCMSc6&)-jqfFtuG*wzi4poz6BL)4vPIlLdwr>`%z zObbIaX5tTNkKe=Y-Aiu|=r8t>?~HILJ&vZSZM6c-`ba61xrAAlCfD3d!KfuuUrQt~ zi*6Jj0kA9zy2<;+Pk2A^G6(jh(0lthbgYYsYZv%NXMm$-0!`PpRHfA80F+Wla0Rb6 z&$2&=8*{Mq3=?iQSt9~~&*#JPnPhz?AuYtsrAdB%>je&V*Rzn->F&Ers3}FnXQ4E0 zORdU)Qi?*LmKmd!8>)c;mB<3{}Oy>abTWbIe(b$y}UR(GM9UUF~ z@<$2gV$Tq2Y$eyw$CT$s)76%`5rDCgj^$7vK6je-`8)UuHks~yeD1FSlsF{@ObpS` zMZ;&A99<3*dqeP42P3EN(9+hz+rQpjj`AHzWf1BbPV}4P>vmzStk8QUfD(967vN5_ z@M)7nUiZ*N!>aLoHIwsT2LsgQhz^y)fOgr$|62(Gj=Pw_KzoM)fhlM^3m;m$4Gr8gHO5i zsw>2l$1AB(ULT)9&(AQ_@<;s3I$Bc*=QW287Wm0%cw4`3)Gf1M9id@x@4)xScGb~V z-$3@MB>SUro;4DzIz>V?I=Qw0xu52_)iA-kSAu+OH}B~5nx;|e!UT|tAl&F5N8iij zt8cNm_%@T{DPmun!pnIyCpw7k>tS+0u%}k|&EHSs1hlG`ka!N_IZ$rg!plz<6Q31i&yjC&x$#RBuL_PUU{vpF#4RFT0bBWOAi~;Yt^5@~Q5_d+9xLvllQrkE4vh&e~`(5Z9n* zPYAkdg0004qNklj!v1=+jk{TC+yZAzak0&(z4UTfjL&qHaB} zMkd|K2N6mH>vq~GA_zC`Zsaz?j%9C4dl+vl4^u{nS$0Piu-_Pv(xc|DVl~n_lb;*| zJn`}a+)fmLB)d-aXAr9N7%~yw-57a_kg>^SbPR_Z*-H&!+9n%Uz@9x|1L5Ab#a5op zMt(<#BEp)(fScMw%A8Ijd=PN}#1xPxU}~^{aOvbQJ_?Fhv?ek4U7M?DSAs)xyp4rIBV8FwCF#g2v z{)ZEvn3cM4u+`S{TWhyu^mFh0lU4AEiysK;4-{JJwSW6@pDj?Y_i2*a3kRSX44$rj JF6*2UngH&LBuM}O literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportpagebreak.png b/comp/src/gb.report/.hidden/control/reportpagebreak.png new file mode 100644 index 0000000000000000000000000000000000000000..3ca2f103d56721395579e192a2543a595529095c GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4Ush%#5As)xyUNPiqFyLu@XfIT7 z`FBOVO}}3d%jL5NT|aQXosh$O^16suPsC3-ZobV74TrCY87M9J=D15v$m4o3t%*J6Eu$YKgkNTkipZ{&t;uc GLK6VR!8YRn literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportpanel.png b/comp/src/gb.report/.hidden/control/reportpanel.png new file mode 100644 index 0000000000000000000000000000000000000000..fce67f7d052048a841374079576e350d53dc010f GIT binary patch literal 154 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 UirGzFm;e9(07*qoM6N<$g7*PU$p8QV literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportvbox.png b/comp/src/gb.report/.hidden/control/reportvbox.png new file mode 100644 index 0000000000000000000000000000000000000000..bc04bb7f24319fc37979abc9713c1a8f99d569b0 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{S0MfW|NjFA4m310{0AZi zhBM|fJ_1EqN`m}?fi%PB$!W8IRF|iVV~EE2r4tSL8Web3yE}Kw-e^3+T*1tyzo7H` z{;vHCA}%;SPFJY?`_p8~v#5zmp-d0F7~H=0HY{9R(xkz7=P{e8g6)-T)?+yqj7*yz zo+xlxr~dRncI0};f@7y-rYlRh?bUd2v&)fL%=x51!p_1E>J2?*eE;kD{?uEjG>Crq hvt{Q>#_eyn-(qa*xBJd9EhHc0I!{+Wmvv4FO#ng6T^9fV literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportview.png b/comp/src/gb.report/.hidden/control/reportview.png new file mode 100644 index 0000000000000000000000000000000000000000..8f07053ba0d28f7cbd0e1f5c778234cfce9b1e17 GIT binary patch literal 732 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\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Dissenyador d'informes" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "Motor d'informes pel Gambas" + +#: FExample.form:40 +msgid "Demo 1" +msgstr "-" + +#: FExample.form:47 +msgid "Demo 2" +msgstr "-" + +#: FExample.form:54 +msgid "Cloner" +msgstr "Clonador" + +#: FExample.form:61 myReport2.report:51 +msgid "Facture" +msgstr "Factura" + +#: FExample.form:68 +msgid "Report in report" +msgstr "Informe en l'informe" + +#: FExample.form:98 +msgid "Test Print" +msgstr "Prova d'impressió" + +#: FOptions.form:25 +msgid "Size" +msgstr "Mida" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Mida del paper" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Misa de paper personalitzada" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Amplada:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "Alçada:" + +#: FOptions.form:67 +msgid "Orientation" +msgstr "Orientació" + +#: FOptions.form:72 +msgid "Landscape" +msgstr "Apaïsat" + +#: FOptions.form:72 +msgid "Portrait" +msgstr "Vertical" + +#: FPreview.form:28 +msgid "Report preview" +msgstr "Vista prèvia de l'informe" + +#: FPreview.form:38 +msgid "Print..." +msgstr "Imprimeix..." + +#: FPreview.form:48 +msgid "First page" +msgstr "Primera pàgina" + +#: FPreview.form:61 +msgid "Page" +msgstr "Pàgina" + +#: FPreview.form:87 +msgid "Last page" +msgstr "Última pàgina" + +#: FPreview.form:97 +msgid "Fit to window" +msgstr "Ajusta a la finestra" + +#: FPreview.form:104 +msgid "Zoom 100%" +msgstr "Escala 100%" + +#: FPreview.form:110 +msgid "Zoom out" +msgstr "Allunya" + +#: FPreview.form:124 +msgid "Zoom in" +msgstr "Apropa" + +#: Report1.report:15 +msgid "#3" +msgstr "-" + +#: Report1.report:17 +msgid "#1" +msgstr "-" + +#: Report1.report:27 +msgid "COUCOU" +msgstr "Cucut" + +#: Report1.report:56 +msgid "#2" +msgstr "-" + +#: Report4.report:30 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "Impressió del plà contable" + +#: Report4.report:54 +msgid "Code" +msgstr "Codi" + +#: Report4.report:62 +msgid "Intitulé" +msgstr "Dret" + +#: Report4.report:105 myReport2.report:254 +msgid "Page $PAGE" +msgstr "Pàgina $PAGE" + +#: Report6.report:13 +msgid "coucou" +msgstr "cucut" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Primera pàgina" + +#: myReport1.report:37 +msgid "Report About" +msgstr "Informe Quant a" + +#: myReport1.report:45 +msgid "All my friends" +msgstr "Tots els meus amics" + +#: myReport1.report:53 +msgid "Friend Table" +msgstr "Taula d'amics" + +#: myReport1.report:59 +msgid "Report about all my frends" +msgstr "Informe sobre tots els meus amics" + +#: myReport1.report:66 +msgid "Test Database" +msgstr "Base de dades de prova" + +#: myReport1.report:89 +msgid "Name" +msgstr "Nom" + +#: myReport1.report:99 +msgid "FirstName" +msgstr "Nom" + +#: myReport1.report:110 +msgid "Birth" +msgstr "Naixement" + +#: myReport1.report:151 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "Secció 1" + +#: myReport2.report:131 +msgid "Id" +msgstr "Identificació" + +#: myReport2.report:140 +msgid "Designation" +msgstr "Designació" + +#: myReport2.report:148 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:157 +msgid "Quantité" +msgstr "Quantitat" + +#: myReport2.report:165 +msgid "Total HT" +msgstr "HT total" + +#: myReport5.report:42 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:77 +msgid "gambas" +msgstr "-" + +#: myReport5.report:97 +msgid "Page $PAGE on $NPAGE" +msgstr "Pàgina $PAGE de $NPAGE" diff --git a/comp/src/gb.report/.lang/cs.po b/comp/src/gb.report/.lang/cs.po new file mode 100644 index 00000000..bbbb4bb9 --- /dev/null +++ b/comp/src/gb.report/.lang/cs.po @@ -0,0 +1,233 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Tvůrce designu" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "Report nástroj pro gambas" + +#: FOptions.form:25 +msgid "Size" +msgstr "Velikost" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Velikost papíru" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Volitelná velikost papíru" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Šířka:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "Výška:" + +#: FOptions.form:67 +msgid "Orientation" +msgstr "Orientace" + +#: FOptions.form:72 +msgid "Landscape" +msgstr "Na šířku" + +#: FOptions.form:72 +msgid "Portrait" +msgstr "Na výšku" + +#: FPreview.form:28 +msgid "Report preview" +msgstr "Náhled reportu" + +#: FPreview.form:39 +msgid "Print..." +msgstr "Tisk..." + +#: FPreview.form:49 +msgid "First page" +msgstr "První stránka" + +#: FPreview.form:55 +msgid "Previous page" +msgstr "Předchozí stránka" + +#: FPreview.form:62 +msgid "Page" +msgstr "Stránka" + +#: FPreview.form:82 +msgid "Next page" +msgstr "Další stránka" + +#: FPreview.form:88 +msgid "Last page" +msgstr "Poslední stránka" + +#: FPreview.form:98 +msgid "Fit to window" +msgstr "Přizpůsobit oknu" + +#: FPreview.form:105 +msgid "Zoom 100%" +msgstr "100% velikost" + +#: FPreview.form:111 +msgid "Zoom out" +msgstr "Oddálit" + +#: FPreview.form:125 +msgid "Zoom in" +msgstr "Přiblížit" + +#: Report.class:100 +msgid "Section " +msgstr "Sekce" + +#: Report1.report:15 +msgid "#3" +msgstr "-" + +#: Report1.report:17 +msgid "#1" +msgstr "-" + +#: Report1.report:27 +msgid "COUCOU" +msgstr "-" + +#: Report1.report:56 +msgid "#2" +msgstr "-" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "-" + +#: Report4.report:55 +msgid "Code" +msgstr "-" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "-" + +#: Report4.report:106 myReport2.report:253 +msgid "Page $PAGE" +msgstr "Stránka $PAGE" + +#: Report6.report:13 Report7.report:64 +msgid "coucou" +msgstr "-" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "-" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "-" + +#: Report8.report:79 +msgid "Page $PAGE / $NPAGE" +msgstr "Stránka $PAGE / $NPAGE" + +#: Report9.report:13 +msgid "ReportLabel1" +msgstr "-" + +#: myReport1.report:27 +msgid "First Page" +msgstr "První stránka" + +#: myReport1.report:37 +msgid "Report About" +msgstr "O reportu..." + +#: myReport1.report:45 +msgid "All my friends" +msgstr "Všeichni mí přátelé" + +#: myReport1.report:53 +msgid "Friend Table" +msgstr "-" + +#: myReport1.report:59 +msgid "Report about all my frends" +msgstr "-" + +#: myReport1.report:66 +msgid "Test Database" +msgstr "Test databáze" + +#: myReport1.report:89 +msgid "Name" +msgstr "Název" + +#: myReport1.report:99 +msgid "FirstName" +msgstr "Jméno" + +#: myReport1.report:110 +msgid "Birth" +msgstr "Narození" + +#: myReport1.report:151 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "Sekce 1" + +#: myReport2.report:51 +msgid "Facture" +msgstr "Faktura" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "-" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "-" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "-" + +#: myReport5.report:42 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:77 +msgid "gambas" +msgstr "-" + +#: myReport5.report:96 +msgid "Page $PAGE on $NPAGE" +msgstr "Stránka $PAGE z $NPAGE" + diff --git a/comp/src/gb.report/.lang/es.po b/comp/src/gb.report/.lang/es.po new file mode 100644 index 00000000..81df2af1 --- /dev/null +++ b/comp/src/gb.report/.lang/es.po @@ -0,0 +1,423 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.6.0\n" +"PO-Revision-Date: 2014-10-13 17:30 UTC\n" +"Last-Translator: jesus \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Diseñador de reportes" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "-" + +#: FOptions.form:25 +msgid "Size" +msgstr "Tamaño" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Tamaño de papel" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Tamaño de papel personalizado" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Ancho:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Orientación" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Paisaje" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Disposición..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:117 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:177 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:189 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "" + +#: FPreview.form:229 +msgid "Executive" +msgstr "-" + +#: FPreview.form:229 +msgid "Legal" +msgstr "-" + +#: FPreview.form:229 +msgid "Letter" +msgstr "-" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "-" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Escala de grises" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Página completa" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.form:327 +msgid "Collate copies" +msgstr "" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:385 +msgid "Print" +msgstr "Imprimir" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "Configurar Impresora" + +#: FPrint.form:58 +msgid "More..." +msgstr "Más..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "" + +#: Form1.form:56 +msgid "One Page" +msgstr "Una página" + +#: Form1.form:64 +msgid "Two Page" +msgstr "Dos páginas" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "Ajustar ancho" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "Fecha" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:108 +msgid "Section " +msgstr "Sección" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "Página $NPAGE" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "" + +#: Report4.report:55 +msgid "Code" +msgstr "" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "Page $PAGE de $NPAGE" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "Página $PAGE / $NPAGE" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Primera página" + +#: myReport1.report:38 +msgid "Report About" +msgstr "" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "" + +#: myReport1.report:91 +msgid "Name" +msgstr "Nombre" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "" + +#: myReport1.report:112 +msgid "Birth" +msgstr "" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "" + +#: myReport2.report:51 +msgid "Facture" +msgstr "" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "Cantidad" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "Página $PAGE" + +#: myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:75 +msgid "gambas" +msgstr "-" + +#: myReport5.report:94 +msgid "Page $PAGE on $NPAGE" +msgstr "Página $PAGE de $NPAGE" + diff --git a/comp/src/gb.report/.lang/es_ES.po b/comp/src/gb.report/.lang/es_ES.po new file mode 100644 index 00000000..9c94e56f --- /dev/null +++ b/comp/src/gb.report/.lang/es_ES.po @@ -0,0 +1,436 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.6.0\n" +"POT-Creation-Date: 2015-09-20 17:55 UTC\n" +"PO-Revision-Date: 2014-10-13 17:30 UTC\n" +"Last-Translator: jesus \n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Diseñador de reportes" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "-" + +#: FOptions.form:25 +msgid "Size" +msgstr "Tamaño" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Tamaño de papel" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Tamaño de papel personalizado" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Ancho:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Orientación" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Paisaje" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Disposición..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:117 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:177 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:189 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "" + +#: FPreview.form:229 +msgid "Executive" +msgstr "-" + +#: FPreview.form:229 +msgid "Legal" +msgstr "-" + +#: FPreview.form:229 +msgid "Letter" +msgstr "-" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "-" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Escala de grises" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Página completa" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.form:327 +msgid "Collate copies" +msgstr "" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:385 +msgid "Print" +msgstr "Imprimir" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "Configurar Impresora" + +#: FPrint.form:58 +msgid "More..." +msgstr "Más..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "" + +#: Form1.form:56 +msgid "One Page" +msgstr "Una página" + +#: Form1.form:64 +msgid "Two Page" +msgstr "Dos páginas" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "Ajustar ancho" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "Fecha" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:108 +msgid "Section " +msgstr "Sección" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "Página $NPAGE" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "" + +#: Report14.report:19 myReport2.report:40 +msgid "Section 1" +msgstr "" + +#: Report14.report:39 myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: Report14.report:59 +msgid "All friends list !" +msgstr "" + +#: Report14.report:77 +msgid "Gambas Report Demo" +msgstr "" + +#: Report14.report:95 +#, fuzzy +msgid "Page $PAGE on $NPAGE " +msgstr "Página $PAGE de $NPAGE" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "" + +#: Report4.report:55 +msgid "Code" +msgstr "" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "Page $PAGE de $NPAGE" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "Página $PAGE / $NPAGE" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Primera página" + +#: myReport1.report:38 +msgid "Report About" +msgstr "" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "" + +#: myReport1.report:91 +msgid "Name" +msgstr "Nombre" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "" + +#: myReport1.report:112 +msgid "Birth" +msgstr "" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:51 +msgid "Facture" +msgstr "" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "Cantidad" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "Página $PAGE" + +#: myReport5.report:76 +msgid "gambas" +msgstr "-" + +#: myReport5.report:95 +msgid "Page $PAGE on $NPAGE" +msgstr "Página $PAGE de $NPAGE" diff --git a/comp/src/gb.report/.lang/fr.po b/comp/src/gb.report/.lang/fr.po new file mode 100644 index 00000000..5622e2c2 --- /dev/null +++ b/comp/src/gb.report/.lang/fr.po @@ -0,0 +1,454 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2014-12-16 11:06 UTC\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "" + +#: FOptions.form:25 +msgid "Size" +msgstr "" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "" + +#: FOptions.form:45 +msgid "Width:" +msgstr "" + +#: FOptions.form:51 +msgid "Height:" +msgstr "" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Orientation" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Paysage" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Portrait" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Annuler" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Mise en page..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Aperçu de l'état" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "Imprimer..." + +#: FPreview.form:117 +msgid "One page" +msgstr "Une page" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Deux pages" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Pleine largeur" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Taille réelle" + +#: FPreview.form:177 +msgid "Printer" +msgstr "Imprimante" + +#: FPreview.form:189 +msgid "File" +msgstr "Fichier" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papier" + +#: FPreview.form:229 +msgid "A3" +msgstr "" + +#: FPreview.form:229 +msgid "A4" +msgstr "" + +#: FPreview.form:229 +msgid "A5" +msgstr "" + +#: FPreview.form:229 +msgid "B5" +msgstr "" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "Personnel" + +#: FPreview.form:229 +msgid "Executive" +msgstr "Executive" + +#: FPreview.form:229 +msgid "Legal" +msgstr "Legal" + +#: FPreview.form:229 +msgid "Letter" +msgstr "Lettre" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Largeur" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Hauteur" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Coté long" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Aucun" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Coté court" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Niveaux de gris" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Pleine page" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Inverser" + +#: FPreview.form:327 +msgid "Collate copies" +msgstr "Assembler" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Echantillon" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Copies" + +#: FPreview.form:385 +msgid "Print" +msgstr "Imprimer" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "" + +#: FPrint.form:58 +msgid "More..." +msgstr "" + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "" + +#: Form1.form:56 +msgid "One Page" +msgstr "" + +#: Form1.form:64 +msgid "Two Page" +msgstr "" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "" + +#: Report.class:108 +msgid "Section " +msgstr "" + +#: Report1.report:16 +msgid "#3" +msgstr "" + +#: Report1.report:23 +msgid "#1" +msgstr "" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "" + +#: Report1.report:62 +msgid "#2" +msgstr "" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "" + +#: Report4.report:55 +msgid "Code" +msgstr "" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "" + +#: myReport1.report:27 +msgid "First Page" +msgstr "" + +#: myReport1.report:38 +msgid "Report About" +msgstr "" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "" + +#: myReport1.report:91 +msgid "Name" +msgstr "" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "" + +#: myReport1.report:112 +msgid "Birth" +msgstr "" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "" + +#: myReport2.report:51 +msgid "Facture" +msgstr "" + +#: myReport2.report:130 +msgid "Id" +msgstr "" + +#: myReport2.report:139 +msgid "Designation" +msgstr "" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "" + +#: myReport5.report:41 +msgid "Gambas" +msgstr "" + +#: myReport5.report:75 +msgid "gambas" +msgstr "" + +#: myReport5.report:94 +msgid "Page $PAGE on $NPAGE" +msgstr "" + +#~ msgid "Zoom in" +#~ msgstr "Agrandir" + +#~ msgid "Zoom out" +#~ msgstr "Réduire" + +#~ msgid "Zoom 100%" +#~ msgstr "Zoom 100%" + +#~ msgid "Fit to window" +#~ msgstr "Adapter à la fenêtre" + +#~ msgid "Last page" +#~ msgstr "Dernière page" + +#~ msgid "Next page" +#~ msgstr "Page suivante" + +#~ msgid "Page" +#~ msgstr "Page" + +#~ msgid "Previous page" +#~ msgstr "Page précédente" + +#~ msgid "First page" +#~ msgstr "Première page" + +#~ msgid "Print..." +#~ msgstr "Imprimer..." diff --git a/comp/src/gb.report/.lang/nl.po b/comp/src/gb.report/.lang/nl.po new file mode 100644 index 00000000..6b19f31e --- /dev/null +++ b/comp/src/gb.report/.lang/nl.po @@ -0,0 +1,436 @@ +# Willy Raets < gbWilly@openmailbox.org >, 2014 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.10.90\n" +"POT-Creation-Date: 2019-04-06 18:41 UTC\n" +"PO-Revision-Date: 2017-08-26 19:45 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Rapport ontwerper" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "Rapportage voor Gambas" + +#: FOptions.form:25 +msgid "Size" +msgstr "Afmeting" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Papierformaat" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Aangepast papierformaat" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Breedte:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "Hoogte:" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Oriëntatie" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Landschap" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Portret" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Annuleren" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Lay-out..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Rapport voorbeeld" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "-" + +#: FPreview.form:117 +msgid "One page" +msgstr "Een pagina" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Twee pagina's" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Volledige breedte" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Echte afmeting" + +#: FPreview.form:177 +msgid "Printer" +msgstr "-" + +#: FPreview.form:189 +msgid "File" +msgstr "Bestand" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papier" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "Aangepast" + +#: FPreview.form:229 +msgid "Executive" +msgstr "-" + +#: FPreview.form:229 +msgid "Legal" +msgstr "-" + +#: FPreview.form:229 +msgid "Letter" +msgstr "-" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Breedte" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "mm" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Hoogte" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "-" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Lange zijde" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Geen" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Korte zijde" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Grijs schaal" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Volledige pagina" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Omgekeerde volgorde" + +#: FPreview.form:327 +msgid "Collate copies" +msgstr "Soteer kopiën" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Bereik" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Kopiën" + +#: FPreview.form:385 +msgid "Print" +msgstr "-" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "-" + +#: FPrint.form:58 +msgid "More..." +msgstr "Meer..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "-" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "Kopiën:" + +#: Form1.form:56 +msgid "One Page" +msgstr "Een Pagina" + +#: Form1.form:64 +msgid "Two Page" +msgstr "Twee Pagina's" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "VolledigeBreedte" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "Datum" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "-" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "-" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "Bedrijf:" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "Ontwerper:" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "-" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "-" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "-" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "-" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "-" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "-" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "-" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Pagina $PAGE van $NPAGE" + +#: Report.class:108 +msgid "Section " +msgstr "Sectie" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "-" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "-" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "-" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "-" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "Pagina $NPAGE" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "-" + +#: Report14.report:19 myReport2.report:40 +msgid "Section 1" +msgstr "-" + +#: Report14.report:39 myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: Report14.report:59 +msgid "All friends list !" +msgstr "-" + +#: Report14.report:77 +msgid "Gambas Report Demo" +msgstr "-" + +#: Report14.report:95 +msgid "Page $PAGE on $NPAGE " +msgstr "-" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "-" + +#: Report4.report:55 +msgid "Code" +msgstr "-" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "-" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "Pagina $PAGE van $NPAGE" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "-" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "Lijst van al mijn vrienden" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "Lijst Van Mijn Vrienden" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "Pagina $PAGE / $NPAGE" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Eerste Pagina" + +#: myReport1.report:38 +msgid "Report About" +msgstr "Over rapport" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "Al mijn vrienden" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "Vrienden tabel" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "Rapport over al mijn vrienden" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "-" + +#: myReport1.report:91 +msgid "Name" +msgstr "Naam" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "Voornaam" + +#: myReport1.report:112 +msgid "Birth" +msgstr "Geboorte" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:51 +msgid "Facture" +msgstr "Factuur" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "Aanwijzing" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "-" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "Totale HT" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "Pagina $PAGE" + +#: myReport5.report:76 +msgid "gambas" +msgstr "-" + +#: myReport5.report:95 +msgid "Page $PAGE on $NPAGE" +msgstr "Pagina $PAGE op $NPAGE" diff --git a/comp/src/gb.report/.lang/ru.po b/comp/src/gb.report/.lang/ru.po new file mode 100644 index 00000000..4ce9344d --- /dev/null +++ b/comp/src/gb.report/.lang/ru.po @@ -0,0 +1,450 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: comp/src/gb.report/.project:26 +msgid "Report designer" +msgstr "Дизайнер отчётов" + +#: comp/src/gb.report/.project:27 +msgid "Report engine for gambas" +msgstr "Движок отчётов для gambas" + +#: comp/src/gb.report/.src/Report.class:108 comp/src/gb.report/.src/Report.class:435 +msgid "Section " +msgstr "Раздел " + +#: comp/src/gb.report/.src/Tests/OutputReport.report:24 comp/src/gb.report/.src/Tests/OutputReport2.report:26 +msgid "Version 1.0" +msgstr "Версия 1.0" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:31 comp/src/gb.report/.src/Tests/OutputReport2.report:33 +msgid "Date" +msgstr "Дата" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:46 comp/src/gb.report/.src/Tests/OutputReport2.report:48 +msgid "Project Title:" +msgstr "Название проекта:" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:53 comp/src/gb.report/.src/Tests/OutputReport2.report:55 +msgid "Project No.:" +msgstr "№ проекта:" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:60 comp/src/gb.report/.src/Tests/OutputReport2.report:62 +msgid "Company:" +msgstr "Компания:" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:67 comp/src/gb.report/.src/Tests/OutputReport2.report:69 +msgid "Designer:" +msgstr "Дизайнер:" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:81 comp/src/gb.report/.src/Tests/OutputReport2.report:83 +msgid "Base Plate ID:" +msgstr "Идентификатор базовой платы:" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:136 comp/src/gb.report/.src/Tests/OutputReport2.report:134 +msgid "Bearing Pressue" +msgstr "Давление подшипника" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:146 comp/src/gb.report/.src/Tests/OutputReport.report:164 comp/src/gb.report/.src/Tests/OutputReport2.report:144 comp/src/gb.report/.src/Tests/OutputReport2.report:162 +msgid "Node #" +msgstr "Узел #" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:153 comp/src/gb.report/.src/Tests/OutputReport.report:171 comp/src/gb.report/.src/Tests/OutputReport2.report:151 comp/src/gb.report/.src/Tests/OutputReport2.report:169 +msgid "Brg. Press., psi" +msgstr "Давление подшипника, psi" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:218 comp/src/gb.report/.src/Tests/OutputReport2.report:215 +msgid "Anchor Rod Tension" +msgstr "Натяжение анкерного стержня" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:228 comp/src/gb.report/.src/Tests/OutputReport.report:246 comp/src/gb.report/.src/Tests/OutputReport2.report:225 comp/src/gb.report/.src/Tests/OutputReport2.report:243 +msgid "Rod #" +msgstr "Стержень #" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:235 comp/src/gb.report/.src/Tests/OutputReport.report:253 comp/src/gb.report/.src/Tests/OutputReport2.report:232 comp/src/gb.report/.src/Tests/OutputReport2.report:250 +msgid "Tension, lbs" +msgstr "Натяжение, lbs" + +#: comp/src/gb.report/.src/Tests/OutputReport.report:300 comp/src/gb.report/.src/Tests/OutputReport2.report:302 +msgid "Page $PAGE of $NPAGE" +msgstr "Страница $PAGE из $NPAGE" + +#: comp/src/gb.report/.src/Tests/Report11.report:13 +msgid "C'est la maison !" +msgstr "Это дом!" + +#: comp/src/gb.report/.src/Tests/rpTestShadowGrid.report:16 comp/src/gb.report/.src/Tests/Old/Report7.report:55 +msgid "coucou" +msgstr "кукушка" + +#: comp/src/gb.report/.src/Tests/Old/Report1.report:7 +msgid "#3" +msgstr "#3" + +#: comp/src/gb.report/.src/Tests/Old/Report1.report:14 +msgid "#1" +msgstr "#1" + +#: comp/src/gb.report/.src/Tests/Old/Report1.report:24 +msgid "COUCOU" +msgstr "КУКУШКА" + +#: comp/src/gb.report/.src/Tests/Old/Report1.report:53 +msgid "#2" +msgstr "#2" + +#: comp/src/gb.report/.src/Tests/Old/Report10.report:17 comp/src/gb.report/.src/Tests/Old/Report10.report:39 comp/src/gb.report/.src/Tests/Old/Report6.report:13 +msgid "ReportLabel1" +msgstr "Метка_отчёта_1" + +#: comp/src/gb.report/.src/Tests/Old/Report10.report:23 comp/src/gb.report/.src/Tests/Old/Report10.report:45 +msgid "ReportLabel2" +msgstr "Метка_отчёта_2" + +#: comp/src/gb.report/.src/Tests/Old/Report10.report:29 comp/src/gb.report/.src/Tests/Old/Report10.report:51 +msgid "ReportLabel3" +msgstr "Метка_отчёта_3" + +#: comp/src/gb.report/.src/Tests/Old/Report10.report:59 +msgid "Page $NPAGE" +msgstr "Страница $NPAGE" + +#: comp/src/gb.report/.src/Tests/Old/Report14.report:7 comp/src/gb.report/.src/Tests/Old/myReport2.report:8 +msgid "Section 1" +msgstr "Раздел 1" + +#: comp/src/gb.report/.src/Tests/Old/Report14.report:27 comp/src/gb.report/.src/Tests/Old/myReport5.report:30 +msgid "Gambas" +msgstr "Gambas" + +#: comp/src/gb.report/.src/Tests/Old/Report14.report:47 +msgid "All friends list !" +msgstr "Список всех друзей!" + +#: comp/src/gb.report/.src/Tests/Old/Report14.report:65 +msgid "Gambas Report Demo" +msgstr "Демонстрация отчёта Gambas" + +#: comp/src/gb.report/.src/Tests/Old/Report14.report:83 +msgid "Page $PAGE on $NPAGE " +msgstr "Страница $PAGE на $NPAGE " + +#: comp/src/gb.report/.src/Tests/Old/Report4.report:16 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "ПЕЧАТЬ БУХГАЛТЕРСКОГО ПЛАНА" + +#: comp/src/gb.report/.src/Tests/Old/Report4.report:40 +msgid "Code" +msgstr "Код" + +#: comp/src/gb.report/.src/Tests/Old/Report4.report:48 +msgid "Intitulé" +msgstr "Заголовок" + +#: comp/src/gb.report/.src/Tests/Old/Report4.report:91 +msgid "Page $PAGE sur $NPAGE" +msgstr "Страница $PAGE на $NPAGE" + +#: comp/src/gb.report/.src/Tests/Old/Report8.report:15 +msgid "List of all my friends" +msgstr "Список всех моих друзей" + +#: comp/src/gb.report/.src/Tests/Old/Report8.report:25 +msgid "List Of My Friends" +msgstr "Список моих друзей" + +#: comp/src/gb.report/.src/Tests/Old/Report8.report:74 +msgid "Page $PAGE / $NPAGE" +msgstr "Страница $PAGE / $NPAGE" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:9 +msgid "First Page" +msgstr "Первая страница" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:20 +msgid "Report About" +msgstr "Отчёт о" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:28 +msgid "All my friends" +msgstr "Все мои друзья" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:37 +msgid "Friend Table" +msgstr "Таблица друзей" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:43 +msgid "Report about all my frends" +msgstr "Отчёт обо всех моих друзьях" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:50 +msgid "Test Database" +msgstr "Тестовая база данных" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:73 +msgid "Name" +msgstr "Имя" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:83 +msgid "FirstName" +msgstr "Первое имя" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:94 +msgid "Birth" +msgstr "Дата рождения" + +#: comp/src/gb.report/.src/Tests/Old/myReport1.report:135 +msgid "$PAGE/$NPAGE" +msgstr "$PAGE/$NPAGE" + +#: comp/src/gb.report/.src/Tests/Old/myReport2.report:19 +msgid "Facture" +msgstr "Фактура" + +#: comp/src/gb.report/.src/Tests/Old/myReport2.report:98 +msgid "Id" +msgstr "Идентификатор" + +#: comp/src/gb.report/.src/Tests/Old/myReport2.report:107 +msgid "Designation" +msgstr "Десигнация" + +#: comp/src/gb.report/.src/Tests/Old/myReport2.report:115 +msgid "PU HT" +msgstr "PU HT" + +#: comp/src/gb.report/.src/Tests/Old/myReport2.report:124 +msgid "Quantité" +msgstr "Количество" + +#: comp/src/gb.report/.src/Tests/Old/myReport2.report:132 +msgid "Total HT" +msgstr "Всего HT" + +#: comp/src/gb.report/.src/Tests/Old/myReport2.report:223 +msgid "Page $PAGE" +msgstr "Страница $PAGE" + +#: comp/src/gb.report/.src/Tests/Old/myReport5.report:65 +msgid "gambas" +msgstr "gambas" + +#: comp/src/gb.report/.src/Tests/Old/myReport5.report:84 +msgid "Page $PAGE on $NPAGE" +msgstr "Страница $PAGE на $NPAGE" + +#: comp/src/gb.report/.src/Preview/FOptions.form:10 +msgid "Size" +msgstr "Размер" + +#: comp/src/gb.report/.src/Preview/FOptions.form:17 +msgid "Paper size" +msgstr "Размер бумаги" + +#: comp/src/gb.report/.src/Preview/FOptions.form:23 +msgid "Custom paper size" +msgstr "Пользовательский размер бумаги" + +#: comp/src/gb.report/.src/Preview/FOptions.form:26 +msgid "Width:" +msgstr "Ширина:" + +#: comp/src/gb.report/.src/Preview/FOptions.form:31 +msgid "Height:" +msgstr "Высота:" + +#: comp/src/gb.report/.src/Preview/FOptions.form:44 comp/src/gb.report/.src/Preview/FPreview.form:122 +msgid "Orientation" +msgstr "Ориентация" + +#: comp/src/gb.report/.src/Preview/FOptions.form:48 comp/src/gb.report/.src/Preview/FPreview.form:127 +msgid "Portrait" +msgstr "Книжная" + +#: comp/src/gb.report/.src/Preview/FOptions.form:48 comp/src/gb.report/.src/Preview/FPreview.form:127 +msgid "Landscape" +msgstr "Альбомная" + +#: comp/src/gb.report/.src/Preview/FPreview.class:313 comp/src/gb.report/.src/Preview/FPrint.form:128 +msgid "Cancel" +msgstr "Отмена" + +#: comp/src/gb.report/.src/Preview/FPreview.class:327 +msgid "Layout..." +msgstr "Макет..." + +#: comp/src/gb.report/.src/Preview/FPreview.class:339 comp/src/gb.report/.src/Preview/FPreview.form:29 +msgid "Printing..." +msgstr "Печать..." + +#: comp/src/gb.report/.src/Preview/FPreview.class:363 comp/src/gb.report/.src/Preview/FPreview.form:264 +msgid "Print" +msgstr "Печать" + +#: comp/src/gb.report/.src/Preview/FPreview.form:5 +msgid "Report preview" +msgstr "Предпросмотр отчёта" + +#: comp/src/gb.report/.src/Preview/FPreview.form:45 +msgid "One page" +msgstr "Одна страница" + +#: comp/src/gb.report/.src/Preview/FPreview.form:54 +msgid "Two pages" +msgstr "Две страницы" + +#: comp/src/gb.report/.src/Preview/FPreview.form:62 +msgid "Full width" +msgstr "Полная ширина" + +#: comp/src/gb.report/.src/Preview/FPreview.form:70 +msgid "Real size" +msgstr "Реальный размер" + +#: comp/src/gb.report/.src/Preview/FPreview.form:97 +msgid "Printer" +msgstr "Принтер" + +#: comp/src/gb.report/.src/Preview/FPreview.form:107 +msgid "File" +msgstr "Файл" + +#: comp/src/gb.report/.src/Preview/FPreview.form:134 comp/src/gb.report/.src/Preview/FPrint.form:62 +msgid "Paper" +msgstr "Бумага" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 comp/src/gb.report/.src/Preview/Form1.form:56 +msgid "Custom" +msgstr "Пользовательский" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 +msgid "A3" +msgstr "А3" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 +msgid "A4" +msgstr "A4" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 +msgid "A5" +msgstr "А5" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 +msgid "B5" +msgstr "B5" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 +msgid "Letter" +msgstr "Лист" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 +msgid "Executive" +msgstr "Экзекьютив" + +#: comp/src/gb.report/.src/Preview/FPreview.form:139 +msgid "Legal" +msgstr "Легал" + +#: comp/src/gb.report/.src/Preview/FPreview.form:150 comp/src/gb.report/.src/Preview/FPrint.form:79 +msgid "Width" +msgstr "Ширина" + +#: comp/src/gb.report/.src/Preview/FPreview.form:158 comp/src/gb.report/.src/Preview/FPreview.form:170 comp/src/gb.report/.src/Preview/FPrint.form:93 comp/src/gb.report/.src/Preview/FPrint.form:97 +msgid "mm" +msgstr "мм" + +#: comp/src/gb.report/.src/Preview/FPreview.form:162 comp/src/gb.report/.src/Preview/FPrint.form:83 +msgid "Height" +msgstr "Высота" + +#: comp/src/gb.report/.src/Preview/FPreview.form:185 +msgid "Recto Verso" +msgstr "Перед. сторона" + +#: comp/src/gb.report/.src/Preview/FPreview.form:190 comp/src/gb.report/.src/Preview/FPrint.form:55 comp/src/gb.report/.src/Preview/FPrint.form:71 +msgid "None" +msgstr "Нет" + +#: comp/src/gb.report/.src/Preview/FPreview.form:190 comp/src/gb.report/.src/Preview/FPrint.form:55 comp/src/gb.report/.src/Preview/FPrint.form:71 +msgid "Short side" +msgstr "Короткая сторона" + +#: comp/src/gb.report/.src/Preview/FPreview.form:190 comp/src/gb.report/.src/Preview/FPrint.form:55 comp/src/gb.report/.src/Preview/FPrint.form:71 +msgid "Long side" +msgstr "Длинная сторона" + +#: comp/src/gb.report/.src/Preview/FPreview.form:202 +msgid "Gray Scale" +msgstr "Серая шкала" + +#: comp/src/gb.report/.src/Preview/FPreview.form:208 +msgid "Full Page" +msgstr "Полная страница" + +#: comp/src/gb.report/.src/Preview/FPreview.form:213 +msgid "Reverse order" +msgstr "Обратный порядок" + +#: comp/src/gb.report/.src/Preview/FPreview.form:218 +msgid "Collate copies" +msgstr "Собрать по копиям" + +#: comp/src/gb.report/.src/Preview/FPreview.form:235 comp/src/gb.report/.src/Preview/FPrint.form:32 +msgid "Range" +msgstr "Диапазон" + +#: comp/src/gb.report/.src/Preview/FPreview.form:246 +msgid "Copies" +msgstr "Копии" + +#: comp/src/gb.report/.src/Preview/FPrint.form:5 +msgid "Printer Config" +msgstr "Конфигурация принтера" + +#: comp/src/gb.report/.src/Preview/FPrint.form:22 +msgid "More..." +msgstr "Дополнительно..." + +#: comp/src/gb.report/.src/Preview/FPrint.form:46 +msgid "RectoVerso" +msgstr "Передняя сторона" + +#: comp/src/gb.report/.src/Preview/FPrint.form:117 +msgid "Copies :" +msgstr "Копии :" + +#: comp/src/gb.report/.src/Preview/Form1.form:37 +msgid "One Page" +msgstr "Одна страница" + +#: comp/src/gb.report/.src/Preview/Form1.form:44 +msgid "Two Page" +msgstr "Две страницы" + +#: comp/src/gb.report/.src/Preview/Form1.form:50 +msgid "FullWidth" +msgstr "Полная ширина" + diff --git a/comp/src/gb.report/.lang/zh.po b/comp/src/gb.report/.lang/zh.po new file mode 100644 index 00000000..2aa20410 --- /dev/null +++ b/comp/src/gb.report/.lang/zh.po @@ -0,0 +1,423 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.6.90\n" +"PO-Revision-Date: 2014-12-16 11:07 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "报表设计器" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "Gambas报表引擎" + +#: FOptions.form:25 +msgid "Size" +msgstr "尺寸" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "纸张尺寸" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "自定义纸张大小" + +#: FOptions.form:45 +msgid "Width:" +msgstr "宽度:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "高度:" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "纸张方向" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "横向" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "纵向" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "取消" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "布局..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "预览报表" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "打印中..." + +#: FPreview.form:117 +msgid "One page" +msgstr "一页" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "两页" + +#: FPreview.form:136 +msgid "Full width" +msgstr "全宽" + +#: FPreview.form:145 +msgid "Real size" +msgstr "实际尺寸" + +#: FPreview.form:177 +msgid "Printer" +msgstr "打印机" + +#: FPreview.form:189 +msgid "File" +msgstr "文件" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "纸张" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "自定义" + +#: FPreview.form:229 +msgid "Executive" +msgstr "行政" + +#: FPreview.form:229 +msgid "Legal" +msgstr "法律" + +#: FPreview.form:229 +msgid "Letter" +msgstr "信函" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "宽度" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "毫米" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "高度" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "双面" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "长边" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "无" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "短边" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "灰度" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "整页" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "逆序" + +#: FPreview.form:327 +msgid "Collate copies" +msgstr "校对稿" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "范围" + +#: FPreview.form:362 +msgid "Copies" +msgstr "份数" + +#: FPreview.form:385 +msgid "Print" +msgstr "打印" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "打印机设置" + +#: FPrint.form:58 +msgid "More..." +msgstr "更多..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "双面" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "份数:" + +#: Form1.form:56 +msgid "One Page" +msgstr "一页" + +#: Form1.form:64 +msgid "Two Page" +msgstr "两页" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "全宽" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "版本1.0" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "日期" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "项目标题:" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "项目编号:" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "公司:" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "设计者:" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "-" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "-" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "-" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "-" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "-" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "-" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "-" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "-" + +#: Report.class:108 +msgid "Section " +msgstr "节" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "-" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "-" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "-" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "-" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "-" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "-" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "印刷计划" + +#: Report4.report:55 +msgid "Code" +msgstr "-" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "-" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "-" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "-" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "-" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "-" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "-" + +#: myReport1.report:27 +msgid "First Page" +msgstr "-" + +#: myReport1.report:38 +msgid "Report About" +msgstr "-" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "-" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "-" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "-" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "-" + +#: myReport1.report:91 +msgid "Name" +msgstr "-" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "-" + +#: myReport1.report:112 +msgid "Birth" +msgstr "-" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "-" + +#: myReport2.report:51 +msgid "Facture" +msgstr "-" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "-" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "-" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "-" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "-" + +#: myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:75 +msgid "gambas" +msgstr "-" + +#: myReport5.report:94 +msgid "Page $PAGE on $NPAGE" +msgstr "-" + diff --git a/comp/src/gb.report/.project b/comp/src/gb.report/.project new file mode 100644 index 00000000..09c192cf --- /dev/null +++ b/comp/src/gb.report/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +Title=Report designer +Startup=Test +Icon=printer1.png +Version=3.15.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.settings +Component=gb.net +Component=gb.net.curl +Component=gb.report +Description="Report engine for gambas" +Authors="Fabien Bodard" +TabSize=2 +Translate=1 +Language=fr +Type=Component +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/comp/src/gb.report/.src/Borders/ReportBorder.class b/comp/src/gb.report/.src/Borders/ReportBorder.class new file mode 100644 index 00000000..a839380b --- /dev/null +++ b/comp/src/gb.report/.src/Borders/ReportBorder.class @@ -0,0 +1,209 @@ +' Gambas class file + +Export + +Static Public Const None As Integer = 0 +Static Public Const Solid As Integer = 1 +Static Private $aShadowStyle As String[] = ["none", "solid", "gradiant"] +'Static Private $aAlign As String[] = Classes["Align"].Symbols +Private hTop As New _ReportBorderSide +Private hBottom As New _ReportBorderSide +Private hLeft As New _ReportBorderSide +Private hRight As New _ReportBorderSide +Private hRCorner As New _ReportRoundCorner + + +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float + +Property Read Top As _ReportBorderSide + +Property Read {Left} As _ReportBorderSide + +Property Read bottom As _ReportBorderSide + +Property Read {Right} As _ReportBorderSide + +Property Read RoundCorner As _ReportRoundCorner + +Property Width As String +Property Brush As ReportBrush + +Public Style As Integer + + +' Static Private Sub ResetBorder(hReportBorder As ReportBorder) As Boolean +' +' With hReportBorder +' .Bottom = False +' .Top = False +' .Left = False +' .Right = False +' End With +' +' Return True +' +' End + +Static Public Function _get(sValue As String) As ReportBorder + + Dim hReportBorder As New ReportBorder + + Dim sBorder As String + Dim aScan As String[] + + If Not sValue Then Return hReportBorder + hReportBorder.Style = ReportBorder.Solid + hReportBorder.RoundCorner._Active = False + For Each sBorder In Split(sValue, ";") + + aScan = Scan(sBorder, "*:*") + Select Case LCase(Trim(aScan[0])) + Case "border" + FillObject(hReportBorder, aScan[1]) + Case "left" + FillObject(hReportBorder.Left, aScan[1]) + Case "right" + FillObject(hReportBorder.Right, aScan[1]) + Case "bottom" + FillObject(hReportBorder.bottom, aScan[1]) + Case "top" + FillObject(hReportBorder.Top, aScan[1]) + Case "topleftcorner" + hReportBorder.RoundCorner.TopLeft = aScan[1] + Case "toprightcorner" + hReportBorder.RoundCorner.TopRight = aScan[1] + Case "bottomleftcorner" + hReportBorder.RoundCorner.BottomLeft = aScan[1] + Case "bottomrightcorner" + hReportBorder.RoundCorner.BottomRight = aScan[1] + + + + End Select + + Next + +Finally + Return hReportBorder + +End + +Static Private Sub FillObject(hObj As Object, sValue As String) + + Dim sBrush As String + + For Each sBrush In Split(sValue, " ") + + If IsDigit(Left(sBrush)) Then + Try hObj.Width = sBrush + Else + Try hObj.Brush = ReportBrush[sBrush] + If hObj.Brush = Null Then hObj.Brush = ReportBrush["&H0"] + Endif + Next + +End + + + +Public Function ToString() As String + + Dim aValue As New String[] + + If Me.Style = ReportBorder.None Then Return 'aValue.Add("None") + 'If Me.Style = ReportBorder.Solid Then aValue.Add("Solid") + aValue.Add(Me.Width) + If Me.Top Then aValue.Add("Top") + If Me.Bottom Then aValue.Add("Bottom") + If Me.Left Then aValue.Add("Left") + If Me.Right Then aValue.Add("Right") + aValue.Add("#" & Hex(Me.Color, 6)) + Return aValue.Join() + +End + +Private Function Top_Read() As _ReportBorderSide + + Return hTop + +End + +Private Function Left_Read() As _ReportBorderSide + + Return hLeft + +End + +Private Function bottom_Read() As _ReportBorderSide + + Return hBottom + +End + +Private Function Width_Read() As String + + Return hLeft.Width + +End + +Private Sub Width_Write(Value As String) + + hLeft.Width = Value + hTop.Width = Value + hBottom.Width = Value + hRight.Width = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return hLeft.Brush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + hLeft.Brush = Value + hTop.Brush = Value + hBottom.Brush = Value + hRight.Brush = Value + +End + +Private Function Right_Read() As _ReportBorderSide + + Return hRight + +End + +Private Function RoundCorner_Read() As _ReportRoundCorner + + Return hRCorner + +End + + + +Public Sub _SetUnifiedValues() + + Dim hSizeParse As TSizeParse + + hSizeParse = New TSizeParse(hLeft.Width) + _Left = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hRight.Width) + _Right = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hTop.Width) + _Top = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hBottom.Width) + _Bottom = hSizeParse.GetValue() + + hRCorner._SetUnifiedValues() + + + +End + + diff --git a/comp/src/gb.report/.src/Borders/_ReportBorderSide.class b/comp/src/gb.report/.src/Borders/_ReportBorderSide.class new file mode 100644 index 00000000..968e7c3c --- /dev/null +++ b/comp/src/gb.report/.src/Borders/_ReportBorderSide.class @@ -0,0 +1,16 @@ +' Gambas class file + +Export +Public Width As String +Public Brush As New ReportBrush +Public Round1 As String +Public Round2 As String +Public _Width As Float + +Public Sub _SetUnifiedValues() + Dim hSizeParse As New TSizeParse(Width) + + _Width = hSizeParse.GetValue() + + +End diff --git a/comp/src/gb.report/.src/Borders/_ReportRoundCorner.class b/comp/src/gb.report/.src/Borders/_ReportRoundCorner.class new file mode 100644 index 00000000..2cd2b0fe --- /dev/null +++ b/comp/src/gb.report/.src/Borders/_ReportRoundCorner.class @@ -0,0 +1,136 @@ +' Gambas class file + +Public _Active As Boolean = False +Private $sTopLeft As String = "0cm" +Private $sTopRight As String = "0cm" +Private $sBottomLeft As String = "0cm" +Private $sBottomRight As String = "0cm" + +Property TopLeft As String +Property TopRight As String +Property BottomLeft As String +Property BottomRight As String + +Public _TopLeft1 As Float +Public _TopLeft2 As Float +Public _TopRight1 As Float +Public _TopRight2 As Float +Public _BottomLeft1 As Float +Public _BottomLeft2 As Float +Public _BottomRight1 As Float +Public _BottomRight2 As Float + +Private Function TopLeft_Read() As String + + Return $sTopLeft + +End + +Private Sub TopLeft_Write(Value As String) + + $sTopLeft = Value + _Active = True + +End + +Private Function TopRight_Read() As String + + Return $sTopRight + +End + +Private Sub TopRight_Write(Value As String) + + $sTopRight = Value + _Active = True + +End + +Private Function BottomLeft_Read() As String + + Return $sBottomLeft + +End + +Private Sub BottomLeft_Write(Value As String) + + $sBottomLeft = Value + _Active = True + +End + +Private Function BottomRight_Read() As String + + Return $sBottomRight + +End + +Private Sub BottomRight_Write(Value As String) + + $sBottomRight = Value + _Active = True + +End + +Public Sub _SetUnifiedValues() + + Dim aEl As String[] + + Dim hSizeParse As TSizeParse + + + ael = Split($sTopLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + _TopLeft2 = _TopLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopLeft2 = hSizeParse.GetValue() + Endif + Endif + + ael = Split($sTopRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + _TopRight2 = _TopRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + _BottomRight2 = _BottomRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + _BottomLeft2 = _BottomLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomLeft2 = hSizeParse.GetValue() + Endif + Endif + +End diff --git a/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class new file mode 100644 index 00000000..4d8ed076 --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class @@ -0,0 +1,26 @@ +' Gambas class file + +Private hBorder As New ReportBorder +Public Sub Form_Open() + + + +End + +Public Sub DrawingArea1_Draw() + + Dim X, Y, Width, Height As Float + + X = Paint.Width / 3 / 2 + Y = Paint.Height / 3 / 2 + Width = Paint.Width / 3 * 2 + Height = Paint.Height / 3 * 2 + + 'Paint.FillRect(0, 0, Paint.Width, Paint.Height, Color.White) + + + Paint.Rectangle(X, Y, Width, Height) + Paint.Stroke + + +End diff --git a/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form new file mode 100644 index 00000000..89e4d028 --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form @@ -0,0 +1,11 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,78,57) + Arrangement = Arrange.Vertical + { DrawingArea1 DrawingArea + MoveScaled(4,5,67,44) + Background = &HFFFFFF& + Expand = True + } +} diff --git a/comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class b/comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class new file mode 100644 index 00000000..b8f3e6b9 --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class @@ -0,0 +1,222 @@ +' Gambas class file + +Private $aBoxShadow As New _ReportBoxShadow[] + +Property XOffset As String +Property YOffset As String +Property Blur As String +Property Spread As String +Property Inset As Boolean +Property Color As Integer +Property Read Count As Integer +Property Read Max As Integer + + +Property Read _XOffset As Float +Property Read _YOffset As Float +Property Read _Blur As Float +Property Read _Spread As Float +Property Read _Active As Boolean + + +Public Sub _new() + + Dim hBoxShadow As New _ReportBoxShadow + $aBoxShadow.Add(hBoxShadow) + +End + +Static Public Function _get(sValue As String) As ReportBoxShadow + + Dim hBoxShadow As New ReportBoxShadow + Dim s As String + Dim i As Integer + For Each s In Split(sValue, " ") + If IsDigit(Left(s)) Then + + Select Case i + Case 0 + hBoxShadow.XOffset = s + Case 1 + hBoxShadow.YOffset = s + Case 2 + hBoxShadow.Blur = s + Case 3 + hBoxShadow.Spread = s + End Select + Inc i + Continue + Endif + If LCase(s) = "inset" Then + hBoxShadow.Inset = True + Continue + Endif + Try hBoxShadow.Color = Val(s) + + Next + Return hBoxShadow + +End + + + +' Public Function _get(Index As Integer) As _ReportBoxShadow +' +' Return $aBoxShadow[Index] +' +' End + +Public Sub Add(Optional XOffset As String, YOffset As String, iColor As Integer, Spread As String, Blur As String, Inset As Boolean) + + Dim hBoxShadow As New _ReportBoxShadow + + If XOffset Then hBoxShadow.XOffset = XOffset + If YOffset Then hBoxShadow.YOffset = YOffset + If iColor Then hBoxShadow.Color = iColor + If SPread Then hBoxShadow.Spread = Spread + If Blur Then hBoxShadow.Blur = Blur + hBoxShadow.Inset = Inset + $aBoxShadow.Add(hBoxShadow) + +End + +Public Sub Remove(Index As Integer) + + $aBoxShadow.Remove(Index) + +End + + + +Private Function XOffset_Read() As String + + Return $aBoxShadow[0].XOffset + +End + +Private Sub XOffset_Write(Value As String) + + $aBoxShadow[0].XOffset = Value + +End + +Private Function YOffset_Read() As String + + Return $aBoxShadow[0].YOffset + +End + +Private Sub YOffset_Write(Value As String) + + $aBoxShadow[0].YOffset = Value + +End + +Private Function Blur_Read() As String + + Return $aBoxShadow[0].Blur + +End + +Private Sub Blur_Write(Value As String) + + $aBoxShadow[0].Blur = Value + +End + +Private Function Spread_Read() As String + + Return $aBoxShadow[0].Spread + +End + +Private Sub Spread_Write(Value As String) + + $aBoxShadow[0].Spread = Value + +End + +Private Function Inset_Read() As Boolean + + Return $aBoxShadow[0].Inset + +End + +Private Sub Inset_Write(Value As Boolean) + + $aBoxShadow[0].Inset = Value + +End + +Private Function Color_Read() As Integer + + Return $aBoxShadow[0].Color + +End + +Private Sub Color_Write(Value As Integer) + + $aBoxShadow[0].Color = Value + +End + +Private Function Count_Read() As Integer + + Return $aBoxShadow.Count + +End + +Private Function Max_Read() As Integer + + Return $aBoxShadow.Max + +End + +Public Sub _SetUnifiedValues() + + Dim i As Integer + Dim SizeParse As TSizeParse + For i = 0 To $aBoxShadow.Max + 'With + $aBoxShadow[i]._XOffset = TSizeParse[$aBoxShadow[i].XOffset].GetValue() + $aBoxShadow[i]._YOffset = TSizeParse[$aBoxShadow[i].YOffset].GetValue() + $aBoxShadow[i]._Spread = TSizeParse[$aBoxShadow[i].Spread].GetValue() + $aBoxShadow[i]._Blur = TSizeParse[$aBoxShadow[i].Blur].GetValue() + + '._Blur = TSizeParse[.Blur].GetValue() + 'End With + + + Next + +End + +Private Function _XOffset_Read() As Float + + Return $aBoxShadow[0]._XOffset + +End + +Private Function _YOffset_Read() As Float + + Return $aBoxShadow[0]._YOffset + +End + +Private Function _Blur_Read() As Float + + Return $aBoxShadow[0]._Blur + +End + +Private Function _Spread_Read() As Float + + Return $aBoxShadow[0]._Spread + +End + +Private Function _Active_Read() As Boolean + + Return Not ($aBoxShadow[0]._XOffset = 0 And $aBoxShadow[0]._YOffset = 0 And $aBoxShadow[0]._Blur = 0 And $aBoxShadow[0]._Spread = 0) + +End diff --git a/comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class b/comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class new file mode 100644 index 00000000..c204ac4c --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class @@ -0,0 +1,13 @@ +' Gambas class file + +Public XOffset As String '= "8mm" +Public YOffset As String '= "2mm" +Public Spread As String +Public Inset As Boolean +Public Color As Integer +Public Blur As String + +Public _XOffset As Float +Public _YOffset As Float +Public _Spread As Float +Public _Blur As Float diff --git a/comp/src/gb.report/.src/Brush/ReportBrush.class b/comp/src/gb.report/.src/Brush/ReportBrush.class new file mode 100644 index 00000000..38f19006 --- /dev/null +++ b/comp/src/gb.report/.src/Brush/ReportBrush.class @@ -0,0 +1,222 @@ +' Gambas class file + +Export + +Public _Type As Integer +Public _X As Float +Public _Y As Float +Public _X2 As Float +Public _Y2 As Float +Public _radius As Float +Public _Image As Image +Public _Color As Integer[] = [0, &HFFFFFF&] +Public _Pos As Float[] = [0, 1] +Private _ImageDir As String + +Public Function _PaintBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + Dim fradius As Float + Dim Width As Integer = X2 - X1 + Dim Height As Integer = Y2 - Y1 + + Select Case Me._Type + Case 0 + hBrush = Paint.Color(_Color[0]) + Case 1 + hBrush = paint.Image(_Image, X1, Y1) + Case 2 + hBrush = paint.LinearGradient(Width * _X, Height * _Y, Width * _X2, Height * _Y2, _Color, _Pos) + Case 3 + fradius = Width * _radius + hBrush = paint.RadialGradient(Width * _X, Height * _Y, fradius, Width * _X2, Height * _Y2, _Color, _Pos) + End Select + hBrush.Translate(x1, Y1) + Return hBrush + +End + +Static Public Sub _get(sValue As String) As ReportBrush + + Dim hBrush As New ReportBrush + Dim iPos As Integer + Dim sType As String + + Dim ars As String[] + + + + If Not svalue Then Return Null + + sValue = Trim(sValue) + + iPos = InStr(sValue, "(") + + If iPos Then sType = Left(sValue, iPos - 1) + 'pas de parenthese de fin + If ipos And Not (sValue Ends ")") Then + Error.Raise + Else + If iPos Then svalue = Left(Mid(svalue, ipos + 1), -1) + Endif + ars = Split(sValue, ",", "[]") + Select Case LCase(sType) + Case "image" + hBrush._Type = 1 + + hBrush._Image = Image.Load(ars[0]) + hBrush._ImageDir = ars[0] + Case "radialgradient" + hBrush._Type = 3 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._Radius = CFloat(ars[2]) + hBrush._X2 = CFloat(ars[3]) + hBrush._Y2 = CFloat(ars[4]) + hBrush._Color = GetIntegerArray(ars[5]) + hBrush._Pos = GetFloatArray(ars[6]) + + Case "lineargradient" + + hBrush._Type = 2 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._X2 = CFloat(ars[2]) + hBrush._Y2 = CFloat(ars[3]) + hBrush._Color = GetIntegerArray(ars[4]) + hBrush._Pos = GetFloatArray(ars[5]) + + Case Else + 'correction de l'hexon + '= Val(Replace(ars[0], "#", "&H") & "00&") + If ars[0] Begins "#" Then ars[0] = Mid(ars[0], 2) + hBrush._Color[0] = Val("&H" & IIf(Len(ars[0]) = 8, ars[0], 00 & ars[0])) + End Select + Return hBrush + +Finally + If hBrush._Color.Count = 0 Then hBrush._Color = [0, &hFFFFFF&] + If hBrush._Color.Count < 2 Then hBrush._Color.Add(&hFFFFFF&) + If hBrush._Pos.Count < 2 Then hBrush._Pos = [0.0, 1.0] + + +Catch + Return hBrush + +End + +Static Private Function GetIntegerArray(sValue As String) As Integer[] + + Dim ari As New Integer[] + Dim s As String + + For Each s In Split(sValue) + If s Begins "#" Then s = Mid(s, 2) + + ari.Add(Val("&H" & IIf(Len(s) = 8, s, "00" & s))) + Next + + Return ari + +End + +Static Private Function GetFloatArray(sValue As String) As Float[] + + Dim arf As New Float[] + Dim s As String + + For Each s In Split(sValue) + arf.Add(CFloat(s)) + Next + + Return arf + +End + +Public Function ToString() As String + + Dim i As Integer + Dim f As Float + Dim sValue As String + + Select Case _Type + Case 0 + sValue = "&H" & Hex(_Color[0], 8) & "&" + Case 2, 3 + If _Type = 3 Then + sValue = "RadialGradient(" & _X & "," & _Y & "," & _Radius & "," & _X2 & "," & _Y2 & ",[" + + Else + sValue = "LinearGradient(" & _X & "," & _Y & "," & _X2 & "," & _Y2 & ",[" + Endif + + For Each i In _Color + sValue &= "&H" & Hex(i, 6) & "&," + + Next + + sValue = Left(sValue, -1) + + sValue &= "],[" + + For Each f In _Pos + sValue &= f & "," + + Next + sValue = Left(sValue, -1) + + sValue &= "])" + Case 1 + sValue = "Image(" & _ImageDir & ")" + + End Select + + Return svalue + +End + +Static Public Function Color(iColor As Integer) As ReportBrush + + Dim hBrush As New ReportBrush + + hBrush._Color[0] = iColor + hBrush._Type = 0 + Return hBrush + +End + +Static Public Function LinearGradient(X As Float, Y As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 2 + Return hBrush +End + +Static Public Function RadialGradient(X As Float, Y As Float, Radius As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._radius = Radius + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 3 + Return hBrush +End + +Static Public Function Image(hImage As Image) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._Image = hImage + hBrush._Type = 1 + Return hBrush + +End diff --git a/comp/src/gb.report/.src/Controls/ReportControl.class b/comp/src/gb.report/.src/Controls/ReportControl.class new file mode 100644 index 00000000..acf0c2cb --- /dev/null +++ b/comp/src/gb.report/.src/Controls/ReportControl.class @@ -0,0 +1,596 @@ +' Gambas class file + +Export +Create Private + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +Public Const _Properties As String = "Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Ignore,Expand,AutoResize,Tag,Range" +Public Const _Family As String = "Report" +Public _SizeInt As TSizeHint +Public _Count As Integer = 1 +Public _Finished As Boolean +Private $iDataindex As Integer +Property _DataIndex As Integer + +Private $iIndex As Integer + +Public Name As String +'Object Management +Static Public _ObjectFromId As New Collection +Static Public _iCurPagePos As Integer +Static Private $iLastId As Integer + +Private $iMyId As Integer +Private $iParentId As Integer + +Private $vTag As Variant +Private $fPadding As Float +Private $iReportId As Integer + +Private $hBrush As ReportBrush +Private $iColor As Integer + +Private $fLeft As Float = 0.0 +Private $fTop As Float = 0.0 +Private $fWidth As Float = 0.0 +Private $fHeight As Float = 0.0 +Private $sHeight As String = "0cm" +Private $sLeft As String = "0cm" +Private $sWidth As String = "0cm" +Private $sTop As String = "0cm" +Private $hPadding As New ReportPadding +'Private $sPadding As String = "0cm" + +Private $iVisible As Boolean = True +Private $hFont As New Font +Private $bExpand As Boolean +Private $bFixed As Boolean +Private $bAutoresize As Boolean = False +Private $bRelativeLeft As Boolean +Private $bRelativeTop As Boolean +Private $bRelativeWidth As Boolean +Private $bRelativeHeight As Boolean +Private $bRelativePadding As Boolean +Private $bIgnore As Boolean = False +Private $sRange As String +Property Read Id As Integer +Property Read Parent As ReportContainer +Property Tag As Variant +Property Padding As ReportPadding + +Property Brush As ReportBrush +'Property {Color} As Integer + +Property Read _Top As Float ''Top of the control in cm +Property Read _Height As Float ''Height of the control in cm +Property Read _Width As Float ''Width of the control in cm +Property Read _Left As Float ''Left of the control in cm +Property Read _Padding As Float '' Padding of the control in cm +Property Read _RelativeLeft As Boolean ''Use percentage for Left pos? +Property Read _RelativeTop As Boolean ''Use percentage for Top Pos? +Property Read _RelativeWidth As Boolean ''Use percentage for width? +Property Read _RelativeHeight As Boolean ''Use percentage for height ? +Property Read _RelativePadding As Boolean ''Use percentage for padding ? +Property _Index As Integer +Property Range As String +Property Left As String +Property Top As String +Property X As String +Property Y As String + +Property Width As String +Property Height As String + +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean + +Property Autoresize As Boolean +Property Read {Report} As Report +Property _ReportId As Integer +Property Read DataIndex As Integer +Property ForceNewPage As Boolean +Private $bForceNewPage As Boolean + +Public Sub _New(Optional Parent As ReportContainer = Null) + + Dim hRep As Report + + $iMyId = $iLastId + Inc $iLastId + If IsNull(Parent) Then + $iParentId = -1 + $iReportId = $iMyId + + Else + If Parent Is Report Then + hRep = Parent + $iParentId = hRep._Container.id + hRep._Container._Add(Me) + $iReportId = hRep.id + Else + $iParentId = Parent.Id + Parent._Add(Me) + $iReportId = Parent._ReportId + Endif + Endif + +End + +Private Function Id_Read() As Integer + + Return $iMyId + +End + +Private Function Parent_Read() As ReportContainer + + Return ReportControl._ObjectFromId[$iParentId] + +End + +Private Function Left_Read() As String + + Return $sLeft + +End + +Private Sub Left_Write(Value As String) + + $sLeft = Value + +End + +Private Function Top_Read() As String + + Return $sTop + +End + +Private Sub Top_Write(Value As String) + + $sTop = Value + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + $bExpand = Value + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, TotalWidth As Float, TotalHeight As Float, DataIndex As Integer) As TSizeHint + + 'Error.Raise("Something goes wrong the _GetSizeHints is not correctly implemented") + 'Implementaion standart du sizeInt hors autoresize et hbox + ' + + Dim hMyHints As New TSizeHint + + If Me._RelativeHeight Then + hMyHints.Height = TotalHeight * Me._Height / 100 + Else + hMyHints.Height = Me._Height + Endif + + If Me._RelativeWidth Then + hMyHints.Width = TotalWidth * Me._Width / 100 + Else + hMyHints.Width = Me._Width + Endif + Return hMyHints + +End + +Public Sub _PaintBeFore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + +End + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Me._Paint(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Private Function Visible_Read() As Boolean + + Return $iVisible + +End + +Private Sub Visible_Write(Value As Boolean) + + $iVisible = Value + +End + +Public Sub Move(X As Float, Y As Float, W As Float, H As Float) + + $fLeft = X + $fTop = Y + $fWidth = W + $fHeight = H + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + +End + +Private Function Font_Read() As Font + + Return $hFont + +End + +Private Sub Font_Write(Value As Font) + + $hFont = Value + +End + +Private Function Padding_Read() As ReportPadding + + Return $hPadding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + $hPadding = Value + +End + +Private Function _Top_Read() As Float + + Return $fTop + +End + +Private Function _Height_Read() As Float + + Return $fHeight + +End + +Private Function _Width_Read() As Float + + Return $fWidth + +End + +Private Function _Left_Read() As Float + + Return $fLeft + +End + +Private Function _Padding_Read() As Float + + Return $fPadding + +End + +Public Function _SetChildGeometry((X) As Float, (Y) As Float, (W) As Float, (H) As Float, (ContPage) As Integer, (TCont) As TControl, (bInFixed) As Boolean) + +End + +Private Function Autoresize_Read() As Boolean + + Return $bAutoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + $bAutoresize = Value + +End + +Private Function _RelativeWidth_Read() As Boolean + + Return $bRelativeWidth + +End + +Private Function _RelativeHeight_Read() As Boolean + + Return $bRelativeHeight + +End + +Private Function _RelativePadding_Read() As Boolean + + Return $bRelativePadding + +End + +''Convert recursively string values to unified values in cm +Public Sub _SetUnifiedValues() + + Dim hSizeParse As TSizeParse + + 'Left + hSizeParse = New TSizeParse($sLeft, True) + $fLeft = hSizeParse.GetValue() + $bRelativeLeft = hSizeParse.IsRelative() + + 'Right + hSizeParse = New TSizeParse($sTop, True) + $fTop = hSizeParse.GetValue() + $bRelativeTop = hSizeParse.IsRelative() + + 'Width + hSizeParse = New TSizeParse($sWidth, True) + $fWidth = hSizeParse.GetValue() + $bRelativeWidth = hSizeParse.IsRelative() + + 'Height + hSizeParse = New TSizeParse($sHeight, True) + $fHeight = hSizeParse.GetValue() + $bRelativeHeight = hSizeParse.IsRelative() + + 'Padding + ' hSizeParse = New TSizeParse($sPadding, True) + ' $fPadding = hSizeParse.GetValue() + ' $bRelativePadding = hSizeParse.IsRelative() + + hSizeParse = New TSizeParse($hPadding.Left, True) + $hPadding._Left = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Right, True) + $hPadding._Right = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Top, True) + $hPadding._Top = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Bottom, True) + $hPadding._Bottom = hSizeParse.GetValue() + + ' + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + $bIgnore = Value + +End + +Private Function Report_Read() As Report + + Return ReportControl._ObjectFromId[$iReportId] + +End + +Private Function _ReportId_Read() As Integer + + Return $iReportId + +End + +Private Function Fixed_Read() As Boolean + + Return $bFixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + $bFixed = Value + +End + +Public Sub _Reset() + + Me._DataIndex = 0 + +End + +Private Function X_Read() As String + + Return $sLeft + +End + +Private Sub X_Write(Value As String) + + $sLeft = Value + +End + +Private Function Y_Read() As String + + Return $sTop + +End + +Private Sub Y_Write(Value As String) + + $sTop = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return $hBrush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + $hBrush = Value + +End + +' Private Function Color_Read() As Integer +' +' Return $iColor +' +' End +' +' Private Sub Color_Write(Value As Integer) +' +' $iColor = Value +' +' End + +Public Function _GetActualBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + + If $hBrush Then + hBrush = $hBrush._PaintBrush(X1, Y1, X2, Y2) + Else If $iColor Then + hBrush = Paint.Color($iColor) + Else + Try hBrush = Me.Parent._GetActualBrush(X1, Y1, X2, Y2) + If Not hBrush Then + hBrush = Paint.Color(0) + Endif + Endif + Return hBrush + +End + +Private Sub _ReportId_Write(Value As Integer) + + $iReportId = Value + +End + +Private Function _RelativeLeft_Read() As Boolean + + Return $bRelativeLeft + +End + +Private Function _RelativeTop_Read() As Boolean + + Return $bRelativeTop + +End + +Private Function DataIndex_Read() As Integer + + Return Me._Index 'Return Me._GetIndex() + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + $sRange = Value + +End + +Public Function _GetIndex() As Integer + + Return Me._Index + 'Return Me.Parent._GetIndex() + +End + +Private Function _Index_Read() As Integer + + Return $iIndex + +End + +Private Sub _Index_Write(Value As Integer) + + $iIndex = Value + +End + +Private Function _DataIndex_Read() As Integer + + Return $iDataindex + +End + +Private Sub _DataIndex_Write(Value As Integer) + 'If Me.Tag = "Boite 2" And Value = 0 Then Stop + 'If Me.Report.$iCurPage = 0 Then Print "DataIndex: " & value + + $iDataindex = Value + 'Stop + 'Print "_DataIndex " & Me.Tag & " = " & $iDataindex + +End + +Private Function ForceNewPage_Read() As Boolean + + Return $bForceNewPage + +End + +Private Sub ForceNewPage_Write(Value As Boolean) + + $bForceNewPage = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report/.src/MainTools/MReport.module b/comp/src/gb.report/.src/MainTools/MReport.module new file mode 100644 index 00000000..a81b4d10 --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/MReport.module @@ -0,0 +1,26 @@ +' Gambas module file + +Public DrawCount As Integer +Public Resolution As Integer +Private Dash As Float[] = [2.0, 1.0] +Private Dot As Float[] = [1.0, 1.0] +Private DashDot As Float[] = [2.0, 1.0, 1.0, 1.0] +Private DashDotDot As Float[] = [2.0, 1.0, 1.0, 1.0, 1.0, 1.0] + + +Public Sub GetBorder(iStyle As Integer) As Float[] + + Select Case iStyle + + Case Line.Dash + Return Dash + Case Line.Dot + Return Dot + Case Line.DashDot + Return DashDot + Case Line.DashDotDot + Return DashDotDot + End Select + +End + diff --git a/comp/src/gb.report/.src/MainTools/ReportUnits.module b/comp/src/gb.report/.src/MainTools/ReportUnits.module new file mode 100644 index 00000000..642cde8b --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/ReportUnits.module @@ -0,0 +1,92 @@ +' Gambas module file + +Fast +Export +Public AllowedUnits As String[] = ["m", "cm", "mm", "in", "pt", "px"] +Property Read DesktopScale As Float +Public _ReportWidth As Float +Public _Scale As Float = 1.0 +'Public Ratio As Float +Public Function GetFactorUnitToInch(Unit As String, Optional Resolution As Integer) As Float + + + Select Case Unit + Case "cm" + Return 0.3937 + Case "ft" + Return 12 + Case "in" + Return 1 + Case "m" + Return 39.37 + Case "mm" + Return 0.03937 + Case "px" + Return 1 / Paint.ResolutionX 'If(Resolution, Resolution, ReportUnits.Resolution) + Case "pt" + Return 1 / 72 + Case Else + Error.Raise("Unknown unit") + End Select + +End + +Public Function ReportUnitsConverter(Value As Float, Unit1 As String, Unit2 As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorUnitToInch(Unit1) + finch = value * f + f = GetFactorUnitToInch(Unit2) + Return finch / f + +End + + +Public Function UnitToCm(Value As Float, Unit As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorUnitToInch(Unit) + finch = value * f + f = 0.3937 + Return finch / f + +End + + +Public Function _ReportUnitsToPixels(Value As Float) As Float + + Dim f As Float + +'Return Value * Ratio +' If Value > 0 Then Stop + f = Value / _ReportWidth * (Paint.Width / _Scale) + 'f = 0.3937 * Paint.ResolutionX + 'f = ReportUnits.GetFactorUnitToInch("cm") * Resolution 'ReportResolution + + 'Return Value * f + Return f + +End + +Public Function _PixelsToReportUnits(Value As Float) As Float + + Dim f As Float + + 'f = 0.3937 * Paint.ResolutionX 'ReportResolution + Return (value / Paint.ResolutionX * 2.54) '/ _ReportWidth * Paint.Width + 'Return (Value) / f + +End + + + + +Private Function DesktopScale_Read() As Float + + Return Paint.ResolutionX / Desktop.Resolution + +End diff --git a/comp/src/gb.report/.src/MainTools/Types/TControl.class b/comp/src/gb.report/.src/MainTools/Types/TControl.class new file mode 100644 index 00000000..27bf86d8 --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TControl.class @@ -0,0 +1,76 @@ +' Gambas class file + +'Fast + + +Property Read RealLeft As Float ''Return the left relative position size in Pixel + +Property Read RealTop As Float ''Return the top relative position size in Pixel + +Property Read RealWidth As Float ''Return the width in pixel + +Property Read RealHeight As Float ''Return the height in pixel + +Public SizeHint As TSizeHint ''Size hints buffer + +Property Ctrl As ReportControl ''Return or set the link between real widget and virtual object + +Public _PageChildren As New Collection ''The links between page and objects + +Public Index As Integer ''Remember the dataindex + +Public Cache As Variant ''For future data cache + +Private $iCtrl As Integer +Private fX As Float +Private fY As Float +Private fW As Float +Private fH As Float + +''Define the virtual widget size in internal unit (cm) +Public Function _SetGeometry(X As Float, Y As Float, W As Float, H As Float) As Float + + fX = X + fY = Y + fW = W + fH = H + +End + + +Private Function Ctrl_Read() As ReportControl + + Return ReportControl._ObjectFromId[$iCtrl] + +End + + +Private Sub Ctrl_Write(Value As ReportControl) + + $iCtrl = Value.id + +End + +Private Function RealLeft_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fX) + +End + +Private Function RealTop_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fY) + +End + +Private Function RealWidth_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fX + fW) - ReportUnits._ReportUnitsToPixels(fX) + +End + +Private Function RealHeight_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fY + fH) - ReportUnits._ReportUnitsToPixels(fY) + +End diff --git a/comp/src/gb.report/.src/MainTools/Types/TPageColumn.class b/comp/src/gb.report/.src/MainTools/Types/TPageColumn.class new file mode 100644 index 00000000..9ca0701d --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TPageColumn.class @@ -0,0 +1,8 @@ +' Gambas class file + +Public Exp As Float +Public NExp As Integer +Public X As Float +Public Width As Float +Public TCtrls As New TControl[] +Public TH As Float \ No newline at end of file diff --git a/comp/src/gb.report/.src/MainTools/Types/TSizeHint.class b/comp/src/gb.report/.src/MainTools/Types/TSizeHint.class new file mode 100644 index 00000000..2b5422ff --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TSizeHint.class @@ -0,0 +1,5 @@ +' Gambas class file + +Public Height As Float = 0 +Public Width As Float = 0 +Public NotFinished As Boolean diff --git a/comp/src/gb.report/.src/MainTools/Types/TSizeParse.class b/comp/src/gb.report/.src/MainTools/Types/TSizeParse.class new file mode 100644 index 00000000..128ad2f6 --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TSizeParse.class @@ -0,0 +1,70 @@ +' Gambas class file + +'Fast +Public Value As Float +Public Unit As String + +'' Create a new size object from its description. +'' #bAllowRelative# can be TRUE to allow the use of "%" as unit. +Public Sub _new(Size As String, Optional bAllowRelative As Boolean) + + Dim I As Integer + Dim sCar As String + + Size = Trim(Size) + + If Not Size Then + Unit = "px" + Return + Endif + + For I = 1 To Len(Size) + sCar = Mid$(Size, I, 1) + If Not IsDigit(sCar) And If sCar <> "-" And If sCar <> "." Then Break + Next + + Value = CFloat(Left$(Size, I - 1)) + Unit = Trim(Mid$(Size, I)) + + If Not Unit Then Unit = "%" + + If Not ReportUnits.AllowedUnits.Exist(Unit) Then + If Not bAllowRelative Or Unit <> "%" Then + Error.Raise("Unknown unit") + Endif + Endif + +End + +Public Sub ToCm() As Float + + Return ReportUnits.UnitToCm(Value, Unit) + +End + +Public Sub IsRelative() As Boolean + + Return Unit = "%" + +End + +Public Sub GetValue() As Float + + + If IsRelative() Then + Return Value + Else + Return ToCm() + Endif + +End + + +Static Public Sub _get(Size As String) As TSizeParse + + Dim hSize As TSizeParse = New TSizeParse(Size) + + Return hSize + +End + diff --git a/comp/src/gb.report/.src/Optional/Align.class b/comp/src/gb.report/.src/Optional/Align.class new file mode 100644 index 00000000..485bc6a1 --- /dev/null +++ b/comp/src/gb.report/.src/Optional/Align.class @@ -0,0 +1,10 @@ +' Gambas class file + +Export Optional + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Enum + Normal = &H00, {Left} = &H01, {Right} = &H02, Center = &H03, + TopNormal = &H10, TopLeft = &H11, TopRight = &H12, Top = &H13, + BottomNormal = &H20, BottomLeft = &H21, BottomRight = &H22, Bottom = &H23 diff --git a/comp/src/gb.report/.src/Optional/Arrange.class b/comp/src/gb.report/.src/Optional/Arrange.class new file mode 100644 index 00000000..1090a9e1 --- /dev/null +++ b/comp/src/gb.report/.src/Optional/Arrange.class @@ -0,0 +1,9 @@ +' Gambas class file + +Export Optional + +Public Const {None} As Integer = 0 +Public Const {Horizontal} As Integer = 1 +Public Const {Vertical} As Integer = 2 +Public Const {Column} As Integer = 4 +Public Const {Fill} As Integer = 5 diff --git a/comp/src/gb.report/.src/Optional/Line.class b/comp/src/gb.report/.src/Optional/Line.class new file mode 100644 index 00000000..c345df55 --- /dev/null +++ b/comp/src/gb.report/.src/Optional/Line.class @@ -0,0 +1,13 @@ +' Gambas class file + +Export Optional + + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Const {None} As Integer = 0 +Public Const {Solid} As Integer = 1 +Public Const {Dash} As Integer = 2 +Public Const {Dot} As Integer = 3 +Public Const {DashDot} As Integer = 4 +Public Const {DashDotDot} As Integer = 5 \ No newline at end of file diff --git a/comp/src/gb.report/.src/Padding/ReportPadding.class b/comp/src/gb.report/.src/Padding/ReportPadding.class new file mode 100644 index 00000000..97c6a169 --- /dev/null +++ b/comp/src/gb.report/.src/Padding/ReportPadding.class @@ -0,0 +1,63 @@ +' Gambas class file + +Export +Public {Left} As String = "0cm" +Public Right As String = "0cm" +Public Top As String = "0cm" +Public Bottom As String = "0cm" + +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float +Property Read _Width As Float +Property Read _Height As Float + +Static Public Sub _Get(Value As String) As ReportPadding + + Dim hPadding As New ReportPadding + Dim hSize As TSizeParse + Dim s As String + Dim ars As String[] + + For Each s In Split(Value, ";") + ars = Scan(s, "*:*") + If ars.count > 1 Then + Select Case LCase(ars[0]) + Case "left" + hPadding.Left = ars[1] + Case "top" + hPadding.top = ars[1] + Case "bottom" + hPadding.bottom = ars[1] + Case "right" + hPadding.Right = ars[1] + Case Else + + End Select + Else + Try hSize = New TSizeParse(s) + If hSize <> Null Then + hPadding.Left = s + hPadding.Right = s + hPadding.Bottom = s + hPadding.Top = s + Endif + Endif + + Next + Return hPadding + +End + +Private Function _Width_Read() As Float + + Return _Left + _Right + +End + +Private Function _Height_Read() As Float + + Return _top + _Bottom + +End diff --git a/comp/src/gb.report/.src/Preview/CPrint.class b/comp/src/gb.report/.src/Preview/CPrint.class new file mode 100644 index 00000000..49d00296 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/CPrint.class @@ -0,0 +1,54 @@ +' Gambas class file + +Create Static +Private hPrinter As New Printer As "Printer" +Private $hPrint As Report + +Public Sub PrintReport(hReport As Report, Optional MyPrinter As Printer) + + Dim hSizeParse As TSizeParse + $hPrint = hReport + '$hPrint = hReport.Clone() + If MyPrinter Then + hPrinter = MyPrinter + Else + hPrinter = New Printer + Endif + Object.Attach(hPrinter, Me, "Printer") + + hPrinter.Paper = $hPrint.Paper + hPrinter.Orientation = $hPrint.Orientation + If $hPrint.Paper = Printer.Custom Then + hSizeParse = New TSizeParse($hPrint.Width) + hPrinter.PaperWidth = hSizeParse.ToCm() * 10 + hSizeParse = New TSizeParse($hPrint.Height) + hPrinter.PaperHeight = hSizeParse.ToCm() * 10 + Endif + If Not MyPrinter Then + If Not hPrinter.Configure() Then + hPrinter.Print + Endif + Else + hPrinter.Print + Endif +End + +Public Sub Printer_Draw() + + $hPrint.Paint(hPrinter.Page) + +End + +Public Sub Printer_Begin() + + 'Debug Object.Type($hReport) + $hPrint.Layout + hPrinter.Count = $hPrint.PageCount + +End + +Public Sub Printer_End() + + $hPrint = Null + +End diff --git a/comp/src/gb.report/.src/Preview/FOptions.class b/comp/src/gb.report/.src/Preview/FOptions.class new file mode 100644 index 00000000..bd549416 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FOptions.class @@ -0,0 +1,9 @@ +' Gambas class file + + +Public Sub Form_Open() + + ComboBox1.List = ["A3", "A4", "A5", "B5", "Executive", "Legal", "Letter", "Custom"] + ComboBox1.Index = 1 + +End diff --git a/comp/src/gb.report/.src/Preview/FOptions.form b/comp/src/gb.report/.src/Preview/FOptions.form new file mode 100644 index 00000000..d6c086a2 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FOptions.form @@ -0,0 +1,52 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,64) + { VBox1 VBox + MoveScaled(0,0,36,52) + Spacing = True + { Frame1 Frame + MoveScaled(0,3,36,38) + Text = ("Size") + { ComboBox1 ComboBox + MoveScaled(2,7,33,4) + ReadOnly = True + } + { Label1 Label + MoveScaled(3,4,19,3) + Text = ("Paper size") + Transparent = True + } + { Frame3 Frame + MoveScaled(2,12,33,22) + Enabled = False + Text = ("Custom paper size") + { Label2 Label + MoveScaled(2,7,9,3) + Text = ("Width:") + Transparent = True + } + { Label3 Label + MoveScaled(2,13,9,3) + Text = ("Height:") + Transparent = True + } + { txtWidth TextBox + MoveScaled(9,6,14,5) + } + { txtHeight TextBox + MoveScaled(9,12,14,5) + } + } + } + { Frame2 Frame + MoveScaled(0,42,36,9) + Text = ("Orientation") + { ComboBox2 ComboBox + MoveScaled(2,4,28,4) + ReadOnly = True + List = [("Portrait"), ("Landscape")] + } + } + } +} diff --git a/comp/src/gb.report/.src/Preview/FPreview.class b/comp/src/gb.report/.src/Preview/FPreview.class new file mode 100644 index 00000000..8b5f9f41 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPreview.class @@ -0,0 +1,400 @@ +' Gambas class file + +Private $aModeButtons As New ToolButton[] +Private bFlag As Boolean +Private $hPrinter As New Printer As "Printer" +Private $iCurPrinterResolution As Integer + +Static Public Function Run(hReport As Report) + + Dim hFPreview As FPreview + + hFpreview = New FPreview + + hFPreview.SetReport(hReport) + hFpreview.ShowModal() + +End + +Public Sub _new() + + Dim s As String + Dim hButton As ToolButton + + $aModeButtons = [ToolButton1, ToolButton2, ToolButton4, ToolButton3] + If Component.IsLoaded("gb.settings") Then + 'Settings.Read(Me, "FPreview") + Settings.Read(View, "View") + + If View.ScaleMode = View.Custom Then + Slider1.Value = View.Scale + Else + For Each hButton In $aModeButtons + hButton.Value = False + Next + $aModeButtons[View.ScaleMode - 1].Value = True + + Endif + + ButtonBox1.Text = Settings["FPreview/OutputFile"] + $hPrinter.Name = Settings["FPreview/Printer"] + If Settings["FPreview/PrintToFile"] = True Then TabPanel1.Index = 1 + + Try $hPrinter.Duplex = Settings["FPreview/Duplex"] + Try $hPrinter.GrayScale = Settings["FPreview/GrayScale"] + Try $hPrinter.ReverseOrder = Settings["FPreview/ReverseOrder"] + Try $hPrinter.CollateCopies = Settings["FPreview/CollateCopies"] + + Endif + + For Each s In Printer.List + cmbPrinter.Add(s) + Next + + SelectPrinter($hPrinter.Name) + Button1.Tag = False + +End + +Private Sub SelectPrinter(Optional sName As String) + + If sName Then + $hPrinter.Name = sName + Object.Lock(cmbPrinter) + Try cmbPrinter.Index = cmbPrinter.List.Find(sName) + Object.Unlock(cmbPrinter) + Else + $hPrinter.Name = Printer.Default + Object.Lock(cmbPrinter) + cmbPrinter.Index = cmbPrinter.List.Find(Printer.Default) + Object.Unlock(cmbPrinter) + Endif + + 'NOTE: force the printer fullpage mode because it make error on report placement + $hPrinter.FullPage = True + + tgbGrayScale.Value = $hPrinter.GrayScale + tgbFullPage.Value = $hPrinter.FullPage + tgbReverseCopies.Value = $hPrinter.ReverseOrder + tgbCollateCopies.Value = $hPrinter.CollateCopies + +End + +Public Sub SetReport(hReport As Report) + + View.Report = hReport + GetValuesFromReport() + +End + +Private Sub GetValuesFromReport() + + cmbPaper.Index = View.Report.Paper + $hPrinter.Orientation = View.Report.Orientation + + If View.Report.Paper = Printer.Custom Then + pnlCustom.Enabled = True + $hPrinter.PaperWidth = TSizeParse[View.Report.Width].ToCm() * 10 + $hPrinter.PaperHeight = TSizeParse[View.Report.Height].ToCm() * 10 + Else + $hPrinter.Paper = View.Report.Paper + Endif + + cmbOrientation.Index = $hPrinter.Orientation + cmbDuplex.Index = $hPrinter.Duplex + cmbPaper.Index = $hPrinter.Paper + Object.Lock(spWidth) + spWidth.Value = $hPrinter.PaperWidth + Object.Unlock(spWidth) + Object.Lock(spHeight) + spHeight.Value = $hPrinter.PaperHeight + Object.Unlock(spHeight) + +End + +Public Sub Slider1_Change() + + Dim hButton As ToolButton + + View.Scale = Slider1.Value / 100 + lblZoom.Text = Str(Slider1.Value) & " %" + bFlag = True + For Each hButton In $aModeButtons + hButton.Value = False + Next + + bFlag = False + +End + +Public Sub View_Change() + + Object.Lock(Slider1) + Slider1.Value = View.Scale * 100 + Object.Unlock(Slider1) + lblZoom.Text = Str(Slider1.Value) & " %" + +End + +Public Sub Mode_Click() + + Dim hButton As ToolButton + Dim hCurButton As ToolButton + + If bFlag Then Return + bFlag = True + hCurButton = Last + + For Each hButton In $aModeButtons + If hButton = hCurButton Then + hButton.Value = True + Else + hButton.Value = False + Endif + Next + + bFlag = False + + View.ScaleMode = hCurButton.Tag + +End + +Public Sub tgbGrayScale_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.GrayScale = Last.Value + View._GrayScale = Last.Value + +End + +Public Sub tgbFullPage_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.FullPage = Last.Value + +End + +Public Sub tgbReverseCopies_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.ReverseOrder = Last.value + +End + +Public Sub tgbCollateCopies_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.CollateCopies = Last.Value + +End + +Public Sub cmbOrientation_Click() + + If View.Report.Orientation = Last.Index Then Return + View.Report.Orientation = Last.Index + 'View.Report.Refresh + View.Refresh + GetValuesFromReport + +End + +Public Sub cmbPaper_Click() + + Dim i As Integer = Last.Index + 'Print "id "; View.Report.paper + If View.Report.Paper = i Then Return + View.Report.Paper = i + If i = 0 Then + pnlCustom.Enabled = True + Else + pnlCustom.Enabled = False + View.Refresh + GetValuesFromReport + Endif + +End + +Public Sub cmbDuplex_Click() + + $hPrinter.Duplex = Last.index + +End + +Public Sub ButtonBox1_Click() + + Dialog.Path = User.Home + Dialog.Filter = ["*.pdf", "Pdf", "*.ps", "Postscript"] + If Not Dialog.saveFile() Then + ButtonBox1.Text = Dialog.Path + $hPrinter.OutputFile = Dialog.Path + Endif + +End + +Public Sub TabPanel1_Click() + + If TabPanel1.Index = 0 Then + $hPrinter.OutputFile = "" + $hPrinter.Name = cmbPrinter.Text + $iCurPrinterResolution = $hPrinter.Resolution + Else + $hPrinter.OutputFile = ButtonBox1.Text + $hPrinter.Resolution = 600 + Endif + +End + +Public Sub TextBox1_Change() + + View.Range = Last.Text + +End + +Public Sub TextBox1_Click() + + Last.Text = "" + +End + +Public Sub spWidth_Change() + + View.Report.Width = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub spHeight_Change() + + View.Report.Height = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub Button2_Click() + + View._ForceLayout + +End + +Public Sub cmbPrinter_Click() + + SelectPrinter(Last.Text) + +End + +Public Sub Button1_Click() + + Dim iReturn As Integer + + If Button1.Tag Then + $hPrinter.Cancel + Button1.Tag = False + + Else + If Not View.Report Then Return + If TabPanel1.Index = 1 Then + If $hPrinter.OutputFile Then + If Exist($hPrinter.OutputFile) Then + iReturn = Message.Warning("This file already exist.\nDo you want to replace it ?", "Yes", "No") + If iReturn = 2 Then + ButtonBox1.SetFocus + + Return + Endif + Endif + Else + ButtonBox1_Click + Return + Endif + + Endif + Button1.Tag = True + Button1.Picture = Picture["icon:/22/cancel"] + Button1.Text = ("Cancel") + + $hPrinter.Print + Endif + +End + +Public Sub Printer_Begin() + + hbZoom.Hide + hbPrinting.Show + Inc Application.Busy + ProgressBar1.Value = 0 + ProgressBar1.Pulse = True + lblPrint.Text = ("Layout...") + +End + +Public Sub Printer_Paginate() + + If View.Report._LayoutNotFinished Then + View.LockLayout + View.Report._Layout(View.Report.PageCount) + Else + $hPrinter.Count = View.PageCount + ProgressBar1.Pulse = False + lblPrint.Text = ("Printing...") + View.UnlockLayout + Endif + +End + +Public Sub Printer_Draw() + + If View._RangePages.Count = 0 Then + View.Report.Paint($hPrinter.Page) + Else + View.Report.Paint(View._RangePages[$hPrinter.Page - 1]) + Endif + ProgressBar1.Value = $hPrinter.Page / View.PageCount + +End + +Public Sub Printer_End() + + 'View.UnlockLayout + hbPrinting.Hide + hbZoom.Show + Dec Application.Busy + Button1.Picture = Picture["icon:/22/print"] + Button1.Text = ("Print") + Button1.Tag = False + +End + +Public Sub Panel12_MouseDown() + +End + +Public Sub ButtonBox1_Change() + + $hPrinter.OutputFile = Last.Text + +End + +Public Sub TextBox2_Change() + + $hPrinter.NumCopies = Last.Value + +End + +Public Sub Form_Close() + + If Component.IsLoaded("gb.settings") Then + 'Settings.Write(Me, "FPreview") + Settings.Write(View, "View") + Settings["FPreview/OutputFile"] = ButtonBox1.Text + Settings["FPreview/Printer"] = $hPrinter.Name + Settings["FPreview/PrintToFile"] = TabPanel1.Index = 1 + Settings["FPreview/Duplex"] = cmbDuplex.Index + Settings["FPreview/GrayScale"] = tgbGrayScale.Value + Settings["FPreview/ReverseOrder"] = tgbReverseCopies.Value + Settings["FPreview/CollateCopies"] = tgbCollateCopies.Value + + Settings.Save() + Endif + +End diff --git a/comp/src/gb.report/.src/Preview/FPreview.form b/comp/src/gb.report/.src/Preview/FPreview.form new file mode 100644 index 00000000..246d5ba0 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPreview.form @@ -0,0 +1,275 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,114.2857,85.7143) + Text = ("Report preview") + Arrangement = Arrange.Vertical + { Panel4 HBox + MoveScaled(1,1,138,86) + Expand = True + { Panel1 VBox + MoveScaled(0,0,73,84) + Expand = True + { View ReportView + MoveScaled(2,0,70,65) + Background = Color.LightForeground + Tag = "3" + Expand = True + } + { hbPrinting HBox + MoveScaled(1,70,71,4) + Visible = False + { Panel18 Panel + MoveScaled(-1,0,19,3) + Expand = True + Arrangement = Arrange.Horizontal + } + { lblPrint Label + MoveScaled(23,0,11,3) + Text = ("Printing...") + } + { ProgressBar1 ProgressBar + MoveScaled(34,0,37,4) + } + } + { hbZoom HBox + MoveScaled(1,80,73,4) + { Panel2 Panel + MoveScaled(-1,0,19,3) + Expand = True + Arrangement = Arrange.Horizontal + { ToolButton1 ToolButton Mode + Name = "ToolButton1" + MoveScaled(0,0,4,4) + Tag = "1" + ToolTip = ("One page") + Picture = Picture["img/22/OnePage.png"] + Toggle = True + Value = True + } + { ToolButton2 ToolButton Mode + Name = "ToolButton2" + MoveScaled(4,0,4,4) + Tag = "2" + ToolTip = ("Two pages") + Picture = Picture["img/22/TwoPage.png"] + Toggle = True + } + { ToolButton4 ToolButton Mode + Name = "ToolButton4" + MoveScaled(8,0,4,4) + Tag = "3" + ToolTip = ("Full width") + Picture = Picture["img/22/FullWidth.png"] + Toggle = True + } + { ToolButton3 ToolButton Mode + Name = "ToolButton3" + MoveScaled(12,0,4,4) + Tag = "4" + ToolTip = ("Real size") + Picture = Picture["img/22/RealSize.png"] + Toggle = True + } + } + { Slider1 Slider + MoveScaled(36,0,26,4) + MinValue = 12 + MaxValue = 200 + } + { lblZoom Label + MoveScaled(63,1,8,2) + Alignment = Align.Center + } + } + } + { Separator1 Separator + MoveScaled(74,23,0,15) + } + { VBox1 VBox + MoveScaled(75,2,38,83) + Spacing = True + Margin = True + { TabPanel1 TabPanel + MoveScaled(0,-1,36,19) + Count = 2 + Index = 0 + Text = ("Printer") + { PictureBox1 PictureBox + MoveScaled(1,1,7,7) + Picture = Picture["icon:/48/printer"] + } + { cmbPrinter ComboBox + MoveScaled(1,9,34,4) + ReadOnly = True + } + Index = 1 + Text = ("File") + { PictureBox2 PictureBox + MoveScaled(1,1,7,7) + Picture = Picture["icon:/48/pdf"] + } + { ButtonBox1 ButtonBox + MoveScaled(1,9,34,4) + Picture = Picture["icon:/16/open"] + } + Index = 0 + } + { Panel14 HBox + MoveScaled(1,20,36,4) + { Label10 Label + MoveScaled(0,0,12,4) + Text = ("Orientation") + } + { cmbOrientation ComboBox + MoveScaled(14,0,21,4) + ReadOnly = True + List = [("Portrait"), ("Landscape")] + } + } + { Panel8 HBox + MoveScaled(1,24,36,4) + { Label3 Label + MoveScaled(0,0,12,4) + Text = ("Paper") + } + { cmbPaper ComboBox + MoveScaled(13,0,21,4) + ReadOnly = True + List = [("Custom"), ("A3"), ("A4"), ("A5"), ("B5"), ("Letter"), ("Executive"), ("Legal")] + } + } + { pnlCust Panel + MoveScaled(2,30,35,16) + { pnlCustom Panel + MoveScaled(2,0,31,15) + Enabled = False + Border = Border.Etched + { Label5 Label + MoveScaled(3,2,8,4) + Text = ("Width") + } + { spWidth SpinBox + MoveScaled(11,2,13,4) + MaxValue = 1000 + } + { Label7 Label + MoveScaled(24,2,4,4) + Text = ("mm") + } + { Label6 Label + MoveScaled(3,9,8,4) + Text = ("Height") + } + { spHeight SpinBox + MoveScaled(11,9,13,4) + MaxValue = 1000 + } + { Label8 Label + MoveScaled(24,9,4,4) + Text = ("mm") + } + } + } + { Panel11 Panel + MoveScaled(2,46,35,3) + Expand = True + } + { Panel16 VBox + MoveScaled(2,48,37,12) + Spacing = True + { Panel7 HBox + MoveScaled(0,1,36,4) + { Label2 Label + MoveScaled(0,0,15,4) + Text = ("Recto Verso") + } + { cmbDuplex ComboBox + MoveScaled(16,0,20,4) + ReadOnly = True + List = [("None"), ("Short side"), ("Long side")] + } + } + { HBox3 HBox + MoveScaled(-1,6,35,6) + Spacing = True + { Panel13 Panel + MoveScaled(1,1,3,4) + Expand = True + } + { tgbGrayScale ToggleButton + MoveScaled(5,0,6,6) + ToolTip = ("Gray Scale") + Picture = Picture["img/32/grayscale.png"] + } + { tgbFullPage ToggleButton + MoveScaled(11,0,6,6) + Visible = False + ToolTip = ("Full Page") + Picture = Picture["img/32/Empty.png"] + } + { tgbReverseCopies ToggleButton + MoveScaled(17,0,6,6) + ToolTip = ("Reverse order") + Picture = Picture["img/32/reverse.png"] + } + { tgbCollateCopies ToggleButton + MoveScaled(24,0,6,6) + ToolTip = ("Collate copies") + Picture = Picture["img/32/Collatecopie.png"] + } + { Panel15 Panel + MoveScaled(32,2,3,3) + Expand = True + } + } + } + { Panel12 Panel + MoveScaled(2,57,35,5) + Expand = True + } + { Panel6 HBox + MoveScaled(2,61,36,4) + { Label1 Label + MoveScaled(0,0,17,4) + Text = ("Range") + } + { TextBox1 ButtonBox + MoveScaled(17,0,18,4) + Picture = Picture["icon:/16/clear"] + } + } + { HBox1 HBox + MoveScaled(2,66,34,4) + { Label4 Label + MoveScaled(0,0,15,4) + Text = ("Copies") + } + { TextBox2 SpinBox + MoveScaled(15,0,12,4) + MinValue = 1 + Value = 1 + } + } + { Panel5 Panel + MoveScaled(2,71,33,2) + } + { HBox2 HBox + MoveScaled(2,75,33,6) + { Panel10 Panel + MoveScaled(2,1,10,3) + Expand = True + } + { Button1 Button + MoveScaled(7,0,22,6) + Text = ("Print") + Picture = Picture["icon:/22/print"] + } + { Panel9 Panel + MoveScaled(24,4,10,3) + Expand = True + } + } + } + } +} diff --git a/comp/src/gb.report/.src/Preview/FPrint.class b/comp/src/gb.report/.src/Preview/FPrint.class new file mode 100644 index 00000000..cea78187 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPrint.class @@ -0,0 +1,37 @@ +' Gambas class file + + +Public Sub Form_Open() +Dim hprinter As New Printer + +Dim sPrinter As String + For Each sPrinter In Printer.List + ListView1.Add(sPrinter, sPrinter, IIf(sPrinter = Printer.Default, Picture["icon:/16/ok"], Picture["icon:/16/printer"])) + If sPrinter = Printer.Default Then ListView1[sPrinter].RichText = "" & sPrinter & "" + Next + +' hprinter.FirstPage +' hprinter.LastPage +' hprinter.NumCopies +' hprinter.Orientation +' hprinter.Page +' hprinter.Paper +' hprinter.PaperHeight +' hprinter.PaperWidth +' + +' hprinter.ReverseOrder + 'hprinter.GrayScal +' hprinter.FullPage +' hprinter.OutputFile + 'hprinter.CollateCopie +' hprinter.ReverseOrder + + +End + +Public Sub Panel6_Show() + + Me.Refresh + +End diff --git a/comp/src/gb.report/.src/Preview/FPrint.form b/comp/src/gb.report/.src/Preview/FPrint.form new file mode 100644 index 00000000..c9a09105 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPrint.form @@ -0,0 +1,132 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,81,67) + Text = ("Printer Config") + Resizable = False + Arrangement = Arrange.Horizontal + AutoResize = True + Spacing = True + Margin = True + { Panel3 VBox + MoveScaled(3,4,53,60) + Expand = True + AutoResize = True + Spacing = True + Padding = 20 + { ListView1 ListView + MoveScaled(0,0,52,20) + } + { Panel6 Expander + MoveScaled(1,21,51,37) + Text = ("More...") + Hidden = True + { Panel7 VBox + MoveScaled(1,2,44,32) + Spacing = True + Padding = 20 + { HBox3 HBox + MoveScaled(1,2,50,4) + { Label8 Label + MoveScaled(1,1,18,3) + Text = ("Range") + } + { Panel10 Panel + MoveScaled(15,1,12,3) + Expand = True + } + { TextBox1 TextBox + MoveScaled(19,0,22,4) + } + } + { HBox1 HBox + MoveScaled(2,8,50,4) + { Label1 Label + MoveScaled(1,1,18,3) + Text = ("RectoVerso") + } + { Panel5 Panel + MoveScaled(15,1,12,3) + Expand = True + } + { ComboBox1 ComboBox + MoveScaled(20,1,18,3) + ReadOnly = True + List = [("None"), ("Long side"), ("Short side")] + } + } + { HBox2 HBox + MoveScaled(2,13,50,4) + { Label3 Label + MoveScaled(1,1,18,3) + Text = ("Paper") + } + { Panel8 Panel + MoveScaled(15,1,12,3) + Expand = True + } + { cmbPaper ComboBox + MoveScaled(20,1,18,3) + ReadOnly = True + List = [("None"), ("Long side"), ("Short side")] + } + } + { Panel9 Panel + MoveScaled(2,19,40,11) + Border = Border.Etched + { Label4 Label + MoveScaled(1,1,13,3) + Text = ("Width") + } + { Label5 Label + MoveScaled(23,1,13,3) + Text = ("Height") + } + { SpinBox2 SpinBox + MoveScaled(1,4,13,4) + } + { SpinBox3 SpinBox + MoveScaled(23,4,13,4) + } + { Label6 Label + MoveScaled(14,4,4,4) + Text = ("mm") + } + { Label7 Label + MoveScaled(36,4,4,4) + Text = ("mm") + } + } + } + } + } + { Separator1 Separator + MoveScaled(58,4,1,16) + } + { Panel1 VBox + MoveScaled(61,6,17,45) + { Button1 Button + MoveScaled(0,0,16,16) + Picture = Picture["icon:/64/printer"] + } + { Panel4 Panel + MoveScaled(1,16,14,5) + } + { Label2 Label + MoveScaled(1,21,10,2) + Text = ("Copies :") + } + { SpinBox1 SpinBox + MoveScaled(0,23,16,4) + } + { Panel2 Panel + MoveScaled(2,30,12,6) + Expand = True + } + { Button2 Button + MoveScaled(2,38,15,5) + Text = Shortcut(("Cancel"), "C") + Picture = Picture["icon:/22/cancel"] + } + } +} diff --git a/comp/src/gb.report/.src/Preview/Form1.class b/comp/src/gb.report/.src/Preview/Form1.class new file mode 100644 index 00000000..126ad137 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/Form1.class @@ -0,0 +1,30 @@ +' Gambas class file + + +Public Sub Form_Open() +Dim hreport As New Report4 + ReportView1.Report = hreport +Slider1.Value = ReportView1.Scale * 100 + +End + +Public Sub Slider1_Change() + + ReportView1.Scale = Last.value / 100 + ReportView1.Refresh + +End + +Public Sub ReportView1_Change() + + Object.Lock(Slider1) + Slider1.Value = Last.Scale * 100 + If ReportView1.ScaleMode = ReportView.Custom Then lblCustom.Value = True + Object.Unlock(Slider1) +End + +Public Sub Radio_Click() + + ReportView1.ScaleMode = Last.tag + +End diff --git a/comp/src/gb.report/.src/Preview/Form1.form b/comp/src/gb.report/.src/Preview/Form1.form new file mode 100644 index 00000000..5253ab7c --- /dev/null +++ b/comp/src/gb.report/.src/Preview/Form1.form @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,113,64) + Arrangement = Arrange.Horizontal + { Panel1 Panel + MoveScaled(2,1,68,51) + Expand = True + Arrangement = Arrange.Vertical + { ReportView1 ReportView + MoveScaled(0,0,48,40) + Expand = True + } + { Slider1 Slider + MoveScaled(8,46,59,4) + MaxValue = 200 + } + } + { PictureBox1 PictureBox + MoveScaled(72,30,8,8) + Background = Color.SelectedForeground + Picture = Picture["icon:/16/printer"] + Alignment = Align.Center + } + { PictureBox2 PictureBox + MoveScaled(74,11,14,14) + Background = Color.SelectedForeground + Picture = Picture["icon:/22/file"] + Alignment = Align.Center + } + { VBox1 VBox + MoveScaled(86,1,25,59) + { RadioButton1 RadioButton Radio + Name = "RadioButton1" + MoveScaled(2,2,27,4) + Tag = "0" + Text = ("One Page") + Value = True + } + { RadioButton2 RadioButton Radio + Name = "RadioButton2" + MoveScaled(2,7,27,4) + Tag = "1" + Text = ("Two Page") + } + { RadioButton3 RadioButton Radio + Name = "RadioButton3" + MoveScaled(3,12,24,4) + Tag = "2" + Text = ("FullWidth") + } + { lblCustom RadioButton Radio + Name = "lblCustom" + MoveScaled(2,16,25,4) + Tag = "3" + Text = ("Custom") + } + } +} diff --git a/comp/src/gb.report/.src/Preview/ReportView.class b/comp/src/gb.report/.src/Preview/ReportView.class new file mode 100644 index 00000000..0d157ae5 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/ReportView.class @@ -0,0 +1,709 @@ +' Gambas class file + +Export + +Inherits UserControl +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +'Public Const _Properties As String = "Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Ignore,Expand,AutoResize,Tag,Range" +'Public Const _Family As String = "Report" + + + +Private $View As ScrollArea +Private $hReport As Report +Private $fScale As Float = 1.0 +Private fReportWidthCm As Float +Private fReportHeightCm As Float +Private $iNbrePage As Integer +Private $iTotNbrePage As Integer +Private $iNbrePageByLine As Integer +Private $fPageWidth As Float +Private $fPageHeight As Float +Property {Report} As Report +Private $fSpace As Float +Property Scale As Float +Property _GrayScale As Boolean +Private $bGrayScale As Boolean +Property Settings As Variant[] + + +Private $fRealPageHeight As Float +Private $fRealPageWidth As Float +Private $fRealSpace As Float + +Private $cBuffer As New Collection +Private $aImageToLoad As New Integer[] +Private $aImageLoaded As New Integer[] + +Private $aTask As New ReportViewTask[8] +Private $Timer As New Timer As "Timer" +Private $tmrRedraw As New Timer As "tmrRedraw" +Private $hSpinner As Spinner +Property Read LayoutInProgress As Boolean +Private $bLayoutInProgress As Boolean +Private htmpImage As New Image(1, 1) +Private $bLayoutEnd As Boolean +Private bNeedToLoad As Boolean = True +Private $iMinLine As Integer +Private $iMaxLine As Integer +Private $iFirstPage As Integer +Private $iLastPage As Integer +Private $iRealCenter As Integer + +Public Const Custom As Integer = 0 +Public Const Page As Integer = 1 +Public Const DualPage As Integer = 2 +Public Const FullWidth As Integer = 3 +Public Const RealSize As Integer = 4 + +Property ScaleMode As Integer +Private $iScaleMode As Integer = Me.Custom +Private $fCurScale As Float = 1.0 +Private $bForceReload As Boolean +Property ShowPageNumbers As Boolean +Property Range As String +Private $sRange As String +Public _RangePages As New Integer[] +Private $bShowPageNumbers As Boolean +Property Read Count As Integer +' Property Read Status As Integer +' Private $iStatus As Integer + +Private $bLayoutLocked As Boolean + +Property Read PageCount As Integer + +Event Progress +Event Change +Event Layout(LayoutInProgress As Boolean) +Private $bLayout As Boolean +Private $MX As Integer +Private $MY As Integer +Public Sub _new(Optional hReport As Report) + + If hReport Then Me.Report = hReport + '$hReport = hReport + $fCurScale = 0.3 + $fRealSpace = 2 * Desktop.Resolution / 2.54 + + $Timer.Delay = 5 + $View = New ScrollArea(Me) As "View" + $View.ScrollBar = Scroll.Both + 'If Not $hReport Then $view.Enabled = False + '$View.Background = Color.Red + +End + +Public Sub Timer_Timer() + + Dim hImg As New Image($fPageWidth, $fPageHeight, Color.White) + 'Dim hSvg As New SvgImage($fRealPageWidth, $fRealPageHeight) + Dim i As Integer + Dim aDiff As Integer[] + + If $bLayoutLocked Then + $Timer.Stop + Return + Endif + + If $aImageToLoad.Count > 0 Then + i = $aImageToLoad.Pop() + 'If $aImageLoaded.Exist(i) Then Return + 'Print "load : "; i + Paint.Begin(hImg) + ReportUnits._Scale = $fCurScale + Paint.Scale($fCurScale, $fCurScale) + + If _RangePages.Count > 0 Then + + $hReport.Paint(_RangePages[i - 1]) + Else + + $hReport.Paint(i) + + Endif + ReportUnits._Scale = 1.0 + + Paint.End + + ' Paint.Begin(hImg) + ' hsvg.Paint() + ' Paint.End + + If $bGrayScale Then hImg = hImg.Desaturate() + If Not $aImageLoaded.Exist(i) Then $aImageLoaded.Add(i) + $cBuffer[i] = hImg + $bLayoutInProgress = $hReport._LayoutNotFinished + $tmrRedraw.Trigger + + Else + + If $bLayoutInProgress Then + Raise Layout(True) + Paint.Begin(htmpImage) + 'Print "Layout : "; $hReport.PageCount + $hReport._layout($hReport.PageCount) + Paint.End + $iTotNbrePage = $hReport.PageCount + $bLayoutInProgress = $hReport._LayoutNotFinished + Layout + Else + If Not $bLayoutEnd Then + $cBuffer.Clear + $aImageLoaded.Clear + $aImageToLoad.Clear + $Timer.Stop + $bLayoutEnd = True + Layout + $tmrRedraw.Trigger + Raise Layout(False) + Else + $Timer.Stop + Endif + Endif + Endif + +End + +Public Sub tmrRedraw_Timer() + + $View.Refresh + +End + +Public Sub View_Draw() + + Dim i, j, iline As Integer + Dim X As Integer + Dim Y As Integer + Dim hImg As Image + Dim bBreak As Boolean + Dim aDiff As Integer[] + Dim iTextWidth As Integer + Dim sText As String + If Not $hReport Then Return + Paint.Scale($fCurScale, $fCurScale) + i = $iFirstPage + iline = $iMinLine + '$aImageToLoad.Clear + 'PrintIArray($aImageLoaded) + 'Print $aImageLoaded.Count + + Do + + For j = 0 To $iNbrePageByLine - 1 + + X = $iRealCenter + $fRealSpace + (($fRealPageWidth + $fRealSpace) * j) - $View.ScrollX / $fCurScale + Y = $fRealSpace + (($fRealPageHeight + $fRealSpace) * iline) - $View.ScrollY / $fCurScale + + Paint.Rectangle(X + 10, Y + 10, $fRealPageWidth, $fRealPageHeight) + Paint.Brush = Paint.Color(&HA0000000) + Paint.Fill + Paint.Rectangle(X, Y, $fRealPageWidth, $fRealPageHeight) + Paint.LineWidth = 2 + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke(True) + Paint.Brush = Paint.Color(Color.White) + Paint.Fill + + hImg = $cBuffer[i + 1] + + If hImg Then Draw.Image(hImg, X, Y, $fRealPageWidth, $fRealPageHeight) + + If $bShowPageNumbers Then + Paint.Font.size = 200 + Paint.Font.Bold = True + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Gray, 128)) + Paint.Text(i + 1, X, Y, $fRealPageWidth, $fRealPageHeight, Align.Center) + Paint.Fill + Endif + Inc i + + If i >= $iLastPage + 1 Then + bBreak = True + Break + Endif + Next + + + + If bBreak Then Break + Inc iline + + Loop + 'Draw.Background = Color.Yellow + Paint.Reset + sText = Str($iFirstPage + 1) & "/" & Str($iNbrePage) + iTextWidth = Paint.TextSize(sText).Width + 10 + Paint.Rectangle(Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, 5) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.DarkGray, 125)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.DarkGray) + Paint.Stroke + Paint.Brush = Paint.Color(Color.White) + Paint.Font.Bold = True + Paint.Text(sText, Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, Align.Center) + Paint.fill + 'Draw.FillRect(Paint.Width - 50, 10 + (Paint.h - 40) * $View.scrolly / $View.ScrollH, 40, 25, Color.Yellow) + + + bNeedToLoad = False + +End + +Private Function GetDiffArray(Array1 As Integer[], Array2 As Integer[]) As Integer[] + + Dim aRet As New Integer[] + Dim i As Integer + + For Each i In Array2 + If Array1.Exist(i) Then Continue + aRet.Add(i) + Next + Return aRet + +End + +Public Function MakePageImage(Index As Integer) As Image + + Dim hImg As New Image($fRealPageWidth, $fRealPageHeight, Color.White) + Dim hSvg As New SvgImage($fRealPageWidth, $fRealPageHeight) + + Paint.Begin(hsvg) + $hReport.Paint(Index) + Paint.End + + Paint.Begin(hImg) + Paint.Scale($fCurScale, $fCurScale) + hsvg.Paint + Paint.End + + Return hImg + +End + +Public Sub View_Arrange() + + Layout + Raise Change + +End + +Private Sub Layout() + + Dim iH, iW, iiS As Integer + Dim $atmpA As New Integer[] + Dim i As Integer + Dim OldScale As Float = $fCurScale + If Not $hReport Then Return + If _RangePages.Count > 0 Then + $iNbrePage = _RangePages.Count + Else + $iNbrePage = $iTotNbrePage + Endif + $iNbrePageByLine = 0 + $aImageToLoad.Clear + 'Dim hImg As New Image($fRealPageWidth, $fRealPageHeight) + $fRealPageWidth = fReportWidthCm * Desktop.Resolution / 2.54 '* $fCurScale + $fRealPageHeight = fReportHeightCm * Desktop.Resolution / 2.54 '* $fCurScale + 'Print $iNbrePage + 'Paint.Begin(hImg) + + 'Paint.Scale($fCurScale, $fCurScale) + '$hReport._Layout(1) + 'Paint.End + '$iNbrePage = 200 + 'If Error Then Return + Select Case $iScaleMode + Case Me.Page + $fCurScale = $View.ClientHeight / ($fRealPageHeight + $fRealSpace * 2) + $iNbrePageByLine = 1 + Case Me.DualPage + $fCurScale = Min($View.ClientWidth / ($fRealPageWidth * 2 + $fRealSpace * 3), $View.ClientHeight / ($fRealPageHeight + $fRealSpace * 2)) + $iNbrePageByLine = 2 + Case Me.FullWidth + $fCurScale = $View.ClientWidth / ($fRealPageWidth + $fRealSpace * 2) + Case Custom + $fCurScale = $fScale + Case RealSize + $fCurScale = 1 + End Select + + If OldScale <> $fCurScale Then $bForceReload = True + + bNeedToLoad = True + $fPageHeight = Max($fRealPageHeight * $fCurScale, 1) + $fPageWidth = Max($fRealPageWidth * $fCurScale, 1) + $fSpace = Max($fRealSpace * $fCurScale, 1) + If Not $iNbrePageByLine Then $iNbrePageByLine = Max(Floor(($View.ClientWidth - $fSpace) / ($fPageWidth + $fSpace)), 1) + + $iMinLine = FindPageLineByPos($fSpace + $View.ScrollY) - 1 + $iMaxLine = Max(1, FindPageLineByPos($View.ClientHeight + $View.ScrollY)) + 'Print $iMinLine, $iMaxLine + $iFirstPage = $iMinLine * $iNbrePageByLine + $iLastPage = Min($iMaxLine * $iNbrePageByLine, $iNbrePage - 1) + If $iNbrePageByLine <= $iNbrePage Then + $iRealCenter = ($View.ClientWidth - ($fSpace + ($fPageWidth + $fSpace) * $iNbrePageByLine)) / 2 / $fCurScale + Else + $iRealCenter = 0 + Endif + 'Calcule des images a charger + + 'Print "Afficher pages de: "; $iFirstPage + 1; " à "; $iLastPage + 1 + 'PrintCol + ' Print "Pages chargées: " + ' PrintIArray($aImageLoaded) + For i = 0 To $aImageLoaded.Max + + If $aImageLoaded[i] < $iFirstPage Or If $aImageLoaded[i] > $iLastPage + 1 Then + 'Print "supprimer : " & i + $cBuffer[$aImageLoaded[i]] = Null + 'Print "Efface : "; $aImageLoaded[i] + Else + $atmpA.Add($aImageLoaded[i]) + Endif + + Next + + $aImageLoaded = $atmpA + 'If not forcing consign is given + 'Add only the not load page to the loading queue + For i = $iFirstPage To $iLastPage + If Not $bForceReload And If $aImageLoaded.Exist(i + 1) Then + Continue + Else + $aImageToLoad.Add(i + 1) + Endif + Next + + If $bForceReload Then $bForceReload = False + + ' Print "Pages à charger: " + ' PrintIArray($aImageToLoad) +' Print + If Not $Timer.Enabled And If $aImageToLoad.Count > 0 Then + $aImageToLoad = $aImageToLoad.Reverse() + $Timer.Start + Endif + $View.ResizeContents(Max($View.ClientWidth, ($fPageWidth + 2 * $fSpace)), $fSpace + Ceil($iNbrePage / $iNbrePageByLine) * ($fPageHeight + $fSpace)) + '$View.ScrollX = Max($View.ClientWidth, ($fPageWidth + 2 * $fSpace)) / 8 +End + +Public Sub View_MouseWheel() + + If Mouse.Control Then + Stop Event + Me.Scale += (Mouse.Delta * 0.1) + Endif +End + + + +Public Sub View_Scroll() + + '$aImageToLoad.Clear + 'bNeedToLoad = True + '$cBuffer.Clear + '$aImageLoaded.Clear + $Timer.Stop + Layout + +End + +Private Sub FindPageLineByPos(Y As Integer) As Integer + + Dim yPage As Integer + Dim i As Integer + + For i = 1 To Ceil($iNbrePage / $iNbrePageByLine) + yPage = $fSpace + ($fPageHeight + $fSpace) * (i - 1) + 'If i = 3 Then Stop + If Y >= yPage And If Y <= (yPage + $fPageHeight + $fSpace) Then Return i + Next + Return -1 + +End + +Private Function Scale_Read() As Float + + Return $fCurScale + +End + +Private Sub Scale_Write(Value As Float) + + $fScale = Max(0.1, Min(2, Value)) + + '$cBuffer.Clear + $bForceReload = True + '$aImageLoaded.Clear + $iScaleMode = Me.Custom + Layout + +End + +Private Function Report_Read() As Report + + Return $hReport + +End + +Private Sub Report_Write(Value As Report) + + Dim hSizeParse As TSizeParse + + If Not Value Then Return + $hReport = Value + '$hReport.Orientation = Printer.Landscape + Paint.Begin(htmpImage) + $hReport._Layout(0) + Paint.End + + $iTotNbrePage = $hReport.PageCount + $bLayoutInProgress = $hReport._LayoutNotFinished + $bLayoutEnd = False + fReportWidthCm = TSizeParse[$hReport.Width].ToCm() + fReportHeightCm = TSizeParse[$hReport.Height].ToCm() + Layout + '$view.Enabled = True + $tmrRedraw.Trigger + +End + +Public Sub View_MouseUp() + + 'Print FindPageByPos(Mouse.y) + +End + +Private Function LayoutInProgress_Read() As Boolean + + Return $bLayoutInProgress + +End + +Private Sub PrintIArray(Array1 As Integer[]) + + Dim i As Integer + + For Each i In Array1 + Print i; " "; + Next + Print "\n"; + +End + +Public Sub PrintCol() + + Dim o As Object + + Print "Collection: " + For Each o In $cBuffer + Print $cBuffer.Key; + + Next + Print "\n" + +End + +Private Function ScaleMode_Read() As Integer + + Return $iScaleMode + +End + +Private Sub ScaleMode_Write(Value As Integer) + + $iScaleMode = Value + $bForceReload = True + Layout + Raise change + $tmrRedraw.Trigger + +End + +Private Function GetScale() As Float + +End + +Private Sub SetCurScale(Value As Integer) + + If $fCurScale <> Value Then + $fCurScale = Value + $bForceReload = True + Endif + +End + +Private Function ShowPageNumbers_Read() As Boolean + + Return $bShowPageNumbers + +End + +Private Sub ShowPageNumbers_Write(Value As Boolean) + + $bShowPageNumbers = Value + $tmrRedraw.Trigger + +End + + +Public Sub Refresh() + $Timer.Stop + $aImageLoaded.Clear + $aImageToLoad.Clear + $cBuffer.Clear + $bLayoutEnd = False + $hReport.Refresh + Me.Report = Me.Report + +End + +Private Function _GrayScale_Read() As Boolean + + Return $bGrayScale + +End + +Private Sub _GrayScale_Write(Value As Boolean) + + If $bGrayScale = Value Then Return + $bGrayScale = Value + $cBuffer.Clear + $aImageLoaded.Clear + $aImageToLoad.Clear + $Timer.Stop + Layout + $tmrRedraw.Trigger + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + $sRange = Value + SetRangePages + $Timer.Stop + $aImageLoaded.Clear + $aImageToLoad.Clear + $cBuffer.Clear + Layout +End + +Public Sub SetRangePages() + + Dim ars As String[] + Dim ars2 As String[] + Dim s As String + Dim iStart, iEnd, i As Integer + _RangePages.Clear + If Not $sRange Then Return + ars = Split($sRange, ";") + + For Each s In ars + If InStr(s, "-") Then + ars2 = Scan(s, "*-*") + iStart = CInt(Val(ars2[0])) + iEnd = Val(ars2[1]) + If iStart < 0 Or If iEnd < 0 Or If iEnd < iStart Then Goto Fin + If _RangePages.Count > 0 And If iStart < _RangePages[_RangePages.Max] Then Goto Fin + For i = iStart To iEnd + + _RangePages.Add(i) + + Next + Else + iStart = CInt(Val(s)) + If _RangePages.Count > 0 And If iStart < _RangePages[_RangePages.Max] Then Goto Fin + _RangePages.Add(iStart) + Endif + Next + Return +Catch +Fin: +'_RangePages.Clear + +End + + +Private Function PageCount_Read() As Integer + + Return $iNbrePage + +End + +Private Function Count_Read() As Integer + + Return $iTotNbrePage + +End + +Public Sub LockLayout() + + $bLayoutLocked = True + +End + + +Public Sub UnlockLayout() + + $bLayoutLocked = False + +End + +Public Sub _ForceLayout() + $Timer.Stop + Paint.Begin(htmpImage) + $hReport.Layout() + Paint.End + $iTotNbrePage = $hReport.PageCount + Layout +End + +Public Sub View_MouseDown() + + If Mouse.Left Then + $MX = Mouse.X + $MY = Mouse.Y + Endif + + +End + +Public Sub View_MouseMove() + + If Mouse.Left Then + 'Print $MX - Mouse.X + $View.ScrollX += $MX - Mouse.X + $MX = Mouse.X + + $View.ScrollY += $MY - Mouse.Y + $MY = Mouse.Y + + $tmrRedraw.Trigger + + Endif + +End + +Private Function Settings_Read() As Variant[] + + Return [$fScale, CVariant($iScaleMode)] + +End + +Private Sub Settings_Write(Value As Variant[]) + + $fScale = Value[0] + $iScaleMode = Value[1] + +Catch + +End diff --git a/comp/src/gb.report/.src/Preview/ReportViewTask.class b/comp/src/gb.report/.src/Preview/ReportViewTask.class new file mode 100644 index 00000000..22c0f6c0 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/ReportViewTask.class @@ -0,0 +1,33 @@ +' Gambas class file + +Inherits Task +Private $hReport As Report + +Public Width As Integer +Public Height As Integer +Private Scale As Float +Public Page As Integer + +Public Sub _new(hReport As Report, iPage As Integer, fScale As Float, W As Integer, H As Integer) + + $hReport = hReport + Width = W + Height = H + Scale = fScale + Page = iPage +End + + +Public Sub Main() As Variant + + Dim hImage As New Image(Width, Height, Color.White) + + Paint.Begin(hImage) + Paint.Scale(Scale, Scale) + $hReport.Paint(page) + Paint.End + + Return hImage.Pixels + + +End diff --git a/comp/src/gb.report/.src/Report.class b/comp/src/gb.report/.src/Report.class new file mode 100644 index 00000000..644698d2 --- /dev/null +++ b/comp/src/gb.report/.src/Report.class @@ -0,0 +1,794 @@ +' Gambas class file + +Create Static +Export +Inherits ReportSection + +' For Stretch properties +Public Enum None, Proportional, Fill + +Public Const _Properties As String = "*,-Left,-Top,-Width,-Height,Count{Range:1;256}=1,Index,Text," +"Paper{Printer.A3;A4;A5;B5;Letter;Executive;Legal;Custom}=A4," +"Orientation{Printer.Portrait;Landscape}=Portrait" +Public Const _HiddenControls As String = "Report,ReportControl,ReportContainer,ReportFrame,ReportSection" +Public Const _IsForm As Boolean = True +Public Const _IsContainer As Boolean = True +Public Const _IsMultiContainer As Boolean = True +Public Const _DrawWith As String = "TabPanel" +Public Const _DefaultEvent As String = "Open" +Public _LayoutNotFinished As Boolean +Public _Container As ReportSection +'Public Constants +Public {Debug} As Boolean = False +Public _bInExec As Boolean +'Public _cPages As New Object[] + +Private $aSections As New TControl[] +Private iCurPage As Integer = 0 +Private $iPageCount As Integer = -1 +Private $bLayoutIsDirty As Boolean = True +Private $fScale As Float = 1.0 +Private $iIndex As Integer +Private $iResolution As Integer = -1 +Private $iCurSection As Integer +Private $iCurPage As Integer = -1 +Private $iPaper As Integer = 2 +Private $iOrientation As Integer +' Private $hPrinter As New Printer As "Printer" +Private $bOpened As Boolean + +' Property Read Printer As Printer +Property Resolution As Integer +Property Scale As Float +Property Read PageCount As Integer +Property Count As Integer +Property Index As Integer + +'All ReportControl Properties +Property Left As String +Property Top As String +Property X As String +Property Y As String + +Property Width As String +Property Height As String + +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean + +Property Autoresize As Boolean +Property Padding As ReportPadding +'All ReportFrame Properties +Property Border As ReportBorder +Property BackGround As ReportBrush + +'All ReportContainer Properties +Property Read Children As ReportControl[] +Property Spacing As String +Property Data As Object + +'All ReportSection Properties +Property Text As String + +Property Paper As Integer +Property Orientation As Integer + +Public _Scale As Float + +Event Open + +Static Public Sub Main() + + Dim hObject As Object + + hObject = Application.Startup.AutoCreate() + If Not hObject Is Report Then Return + FPreview.Run(hObject) + +End + +Public Sub _New() + + Dim hSection As New ReportSection + Dim TSection As New TControl + + object.Attach(Me, Me, "Report") + + '$hReportTControl.RelPage = 0 + ReportControl._ObjectFromId[Me.id] = Me + hSection = New ReportSection + hSection._ReportId = Me.Id + + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + $aSections.Add(TSection) + hSection.Text = ("Section ") & $aSections.Max + _Container = hSection + Me.paper = Printer.A4 + 'Me.Orientation = Printer.Landscape +End + +Public Sub Layout() + + _Layout() + +End + + + +Public Sub _Layout(Optional iPage As Integer = -1) + + 'Dim iCurPage As Integer + Dim TSection As TControl + Dim CSection As ReportContainer + Dim CBaseSection As ReportContainer + Dim i As Integer + + If Not $bOpened Then + Raise Open + $bOpened = True + Endif + + 'Print Paint.ResolutionX + 'If iPage < 0 Then Return + If Not $bLayoutIsDirty Then Return + If _bInExec Then Return + If iPage > -1 Then + 'La page a déja été calculée + If iPage <= $iCurPage Then Return + Else + 'Mise a zéro + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + Me._Reset + Endif + 'Set execution Flag to true + _bInExec = True + CBaseSection = $aSections[0].Ctrl + + 'ReportUnits.Resolution = If($iResolution < 0, Paint.ResolutionX, $iResolution) + + For i = $iCurSection To $aSections.Max + Tsection = $aSections[i] + CSection = TSection.Ctrl + CSection.Padding = CBaseSection.Padding + CSection.Spacing = CBaseSection.Spacing + CSection._SetUnifiedValues() + ReportUnits._ReportWidth = CBaseSection._Width + TSection._SetGeometry(0, 0, CSection._Width, CSection._Height) + Inc $iCurPage + Do + + CSection._SetChildGeometry(0, 0, CSection._Width, CSection._Height, $iCurPage, TSection, False) + + If CSection._CurItem > CSection.Children.Max Then Break + + Inc $iCurPage + $iPageCount = $iCurPage + 'Print "Fin Page " & $iCurPage + If iPage > -1 And If $iCurPage > iPage Then + Dec $iCurPage + _LayoutNotFinished = True + $iCurSection = i + _bInExec = False + Return + + Endif + + Loop + 'Inc iCurPage + Next + + $iPageCount = $iCurPage + 1 + 'Debug Me._PageChildren.Count + _LayoutNotFinished = False + 'ReportUnits.PageCount = $iCount + $iCurSection = 0 + '$iCurPage = -1 + $bLayoutIsDirty = False + + 'End of execution + _bInExec = False + +End + +Public Sub Paint(Page As Integer) + + Dim TSection As TControl + Dim fScale As Float + 'Debug "**** "; Page;; Paint.ResolutionX;; Paint.ResolutionY + + If Me.Parent = Null Then + 'If $sOldWidth <> ((Paint.Width / $fScale) & " px") Then $bLayoutIsDirty = True + 'If $sOldHeight <> ((Paint.Height / $fScale) & " px") Then $bLayoutIsDirty = True + Endif + ReportUnits._ReportWidth = $aSections[0].Ctrl._Width + 'ReportUnits.Resolution = If($iResolution < 0, Paint.ResolutionX, $iResolution) + If $bLayoutIsDirty And $iPageCount = -1 Then Layout() + Paint.Save + 'Set execution Flag to true + _bInExec = True + + ' If page < 1 Or page > $iCount Then + ' Error.Raise("This page does not exist") + ' Endif + 'Dec page + + 'Paint.Clip + 'ReportUnits.DrawCount = 0 + + 'Paint.Reset + ' If Paint.Device Is Printer Then + ' Paint.Scale(Paint.ResolutionX / 1200, Paint.ResolutionY / 1200) + ' Endif + ' If Paint.Device Is Printer Then + ' If ReportUnits._ReportUnitsToPixels($aSections[0].Ctrl._Width) > Paint.Width Then + ' + ' fScale = Paint.Width / ReportUnits._ReportUnitsToPixels($aSections[0].Ctrl._Width) + ' + ' Paint.Scale($fScale, $fScale) + ' Endif + ' Endif + 'Print Paint.Width + 'ReportUnits.Ratio = Paint.Width / $aSections[0].Ctrl._Width + ' With Paint.ClipExtents + ' Debug "ClipExtents: "; .X;; .Y;; .Width;; .Height + ' End With + Dec page + 'Me._ClipChildren(Page, 0, 0, $hReportTControl, -1, $fSCale) + For Each TSection In $aSections + TSection.Ctrl._PaintBefore(Page, 0, 0, TSection, -1) + TSection.Ctrl._Paint(Page, 0, 0, TSection, -1) + TSection.Ctrl._PaintAfter(Page, 0, 0, TSection, -1) + Next + 'Debug "DrawCount = "; ReportUnits.DrawCount + 'End of execution + _bInExec = False + Paint.Restore + +End + +Private Function PageCount_Read() As Integer + + If $bLayoutIsDirty And Not _LayoutNotFinished Then Layout() '$iCount = -1 + + Return $iPageCount + +End + +Private Function Height_Read() As String + + Return $aSections[0].Ctrl.Height + +End + +Private Sub Height_Write(Value As String) + + SetHeight(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetHeight(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Height <> Value Then + + For Each TSection In $aSections + TSection.Ctrl.Height = Value + Next + $bLayoutIsDirty = True + Endif + +End + +Private Function Width_Read() As String + + Return $aSections[0].Ctrl.Width + +End + +Private Sub Width_Write(Value As String) + + SetWidth(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetWidth(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Width <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Width = Value + Next + $bLayoutIsDirty = True + Endif + +End + +Private Function Scale_Read() As Float + + Return $fScale + +End + +Private Sub Scale_Write(Value As Float) + + $fScale = Value + +End + +Public Sub Clear() + + Super._Free + iCurPage = 0 + $iPageCount = -1 + '$iResolution = Desktop.Resolution + $bLayoutIsDirty = True + $fScale = 1.0 + 'Me.Brush = Null + 'Me.Border = New ReportBorder + 'Me.Spacing = "0 cm" + 'Me.Padding = New ReportPadding + +End +' + +Public Function Preview() + + FPreview.Run(Me) + +End + +' Public Function Image(Value As String, X As String, Y As String) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 1 +' hBrush._X = New TSizeParse(X) +' hBrush._Y = New TSizeParse(Y) +' hBrush._Image = Image.Load(Value) +' Return hBrush +' +' End +' +' Public Function LinearGradient(Colors As Integer[], Positions As Float[]) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 2 +' 'hBrush.X = MRTools.ScanValue(X) +' 'hBrush.Y = MRTools.ScanValue(Y) +' +' hBrush._Color = Colors +' hBrush._Pos = Positions +' Return hBrush +' +' End + +' Public Function RadialGradient(Colors As Integer[], Positions As Float[]) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 3 +' 'hBrush.X = MRTools.ScanValue(X) +' 'hBrush.Y = MRTools.ScanValue(Y) +' +' hBrush._Color = Colors +' hBrush._Pos = Positions +' Return hBrush +' +' End + +' Public Sub {Color}(iColor As Integer) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 0 +' hBrush._iValue = iColor +' Return hBrush +' +' End + +Public Sub Refresh() + + $bLayoutIsDirty = True + Me._Reset + +End + +Private Function Count_Read() As Integer + + Return $aSections.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim i As Integer + Dim hSection As ReportSection + Dim TSection As TControl + + If Value < 0 Then Return + If Value < $aSections.Count Then + $aSections.Resize(Value) + Else If Value > $aSections.Count Then + For i = $aSections.Count To Value - 1 + + TSection = New TControl + hSection = New ReportSection + hSection.Text = "Section " & CStr(i) + hSection._ReportId = Me.Id + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + hSection.Padding = Me.Padding + hSection.Height = Me.Height + hSection.Width = Me.Width + $aSections.Add(TSection) + hSection.Text = ("Section ") & $aSections.Max + Next + Endif + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + $iIndex = Max(Min($aSections.Max, Value), 0) + _Container = $aSections[$iIndex].Ctrl + +End + +Public Sub _get(Index As Integer) As ReportSection + + Return $aSections[Index].Ctrl + +End + +Private Function Padding_Read() As ReportPadding + + Return $aSections[0].Ctrl.Padding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + Dim TSection As TControl + + If Me.Padding <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Padding = Value + Next + Endif + +End + +Private Function Resolution_Read() As Integer + + Return $iResolution + +End + +Private Sub Resolution_Write(Value As Integer) + + $bLayoutIsDirty = True + $iResolution = Max(0, Value) + +End + +Private Function Left_Read() As String + + Return _Container.Left + +End + +Private Sub Left_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Top_Read() As String + + Return _Container.Top + +End + +Private Sub Top_Write(Value As String) + + _Container.Top = Value + +End + +Private Function X_Read() As String + + Return _Container.Left + +End + +Private Sub X_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Y_Read() As String + + Return _Container.Top + +End + +Private Sub Y_Write(Value As String) + + _Container.Top = Value + +End + +Private Function Visible_Read() As Boolean + + Return _Container.Visible + +End + +Private Sub Visible_Write(Value As Boolean) + + _Container.Visible = Value + +End + +Private Function Font_Read() As Font + + Return _Container.Font + +End + +Private Sub Font_Write(Value As Font) + + _Container.Font = Value + +End + +Private Function Expand_Read() As Boolean + + Return _Container.Expand + +End + +Private Sub Expand_Write(Value As Boolean) + + _Container.Expand = Value + +End + +Private Function Ignore_Read() As Boolean + + Return _Container.Ignore + +End + +Private Sub Ignore_Write(Value As Boolean) + + _Container.Ignore = Value + +End + +Private Function Fixed_Read() As Boolean + + Return _Container.Fixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + _Container.Fixed = Value + +End + +Private Function Autoresize_Read() As Boolean + + Return _Container.Autoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + _Container.Autoresize = Value + +End + +Private Function Border_Read() As ReportBorder + + Return _Container.Border + +End + +Private Sub Border_Write(Value As ReportBorder) + + _Container.Border = Value + +End + +Private Function BackGround_Read() As ReportBrush + + Return _Container.BackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + _Container.BackGround = Value + +End + +Private Function Children_Read() As ReportControl[] + + Return _Container.Children + +End + +Private Function Spacing_Read() As String + + Return _Container.Spacing + +End + +Private Sub Spacing_Write(Value As String) + + Dim TSection As TControl + Dim hCont As ReportContainer + + If Me.Spacing <> Value Then + For Each TSection In $aSections + hCont = Tsection.Ctrl + hCont.Spacing = Value + Next + Endif + +End + +Private Function Data_Read() As Object + + Return _Container.Data + +End + +Private Sub Data_Write(Value As Object) + + _Container.Data = Value + +End + +Private Function Text_Read() As String + + Return _Container.Text + +End + +Private Sub Text_Write(Value As String) + + _Container.Text = Value + +End + +''Return a new report object +Public Sub Clone() As Report + + 'Return Object.New(Object.Type(Me)) + Return Object.Class(Me).New() + +End + +Private Function Paper_Read() As Integer + + Return $iPaper + +End + +Private Sub Paper_Write(Value As Integer) + + $iPaper = Value + SetPaper + +End + +Private Function Orientation_Read() As Integer + + Return $iOrientation + +End + +Private Sub Orientation_Write(Value As Integer) + + $iOrientation = Value + SetPaper + +End + +Public Sub Print(Optional hPrinter As Printer) + + If hPrinter Then + CPrint.PrintReport(Me, hPrinter) + Else + CPrint.PrintReport(Me) + Endif + +End + +Private Sub SetPaper() + + Dim sPrev As String + + Select Case $iPaper + Case Printer.A3 + SetWidth("29.7cm") + SetHeight("42cm") + Case Printer.A4 + SetWidth("21cm") + SetHeight("29.7cm") + Case Printer.A5 + SetWidth("14.8cm") + SetHeight("21cm") + Case Printer.B5 + SetWidth("18.7cm") + SetHeight("25.7cm") + Case Printer.Executive + SetWidth("19.1cm") + SetHeight("25.4cm") + Case Printer.Legal + SetWidth("21.6cm") + SetHeight("35.6cm") + Case Printer.Letter + SetWidth("21.6cm") + SetHeight("27.9cm") + + End Select + 'Print $iOrientation, $iPaper + + If $iOrientation = Printer.Landscape And Not ($iPaper = Printer.Custom) Then + sPrev = Me.Height + SetHeight(Me.Width) + SetWidth(sPrev) + ' Print "width = " & Me.Width + ' Print "height = " & Me.Height + Endif + +End + +Public Sub _Reset() + + Dim hSection As TControl + + For Each hSection In $aSections + hSection.Ctrl._Reset + hSection._PageChildren = New Collection + Next + + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + +End + +' Private Function Printer_Read() As Printer +' +' Return $hPrinter +' +' End + +' Public Sub Print2() +' +' _Reset +' +' End diff --git a/comp/src/gb.report/.src/ReportContainer.class b/comp/src/gb.report/.src/ReportContainer.class new file mode 100644 index 00000000..c4dee7a7 --- /dev/null +++ b/comp/src/gb.report/.src/ReportContainer.class @@ -0,0 +1,945 @@ +' Gambas class file + +Export +Create Private +Inherits ReportFrame + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*,Spacing{ReportCoord},OnePiece,ForceNewPage" +'Public Const _DefaultEvent As String = "Data" +Private $bDataIsResult As Boolean +'Private $iCount As Integer + +Property _CurItem As Integer +Private $iCurItem As Integer + +Public _Arrangement As Integer +Private $bIndexChange As Boolean +Private $aChild As New ReportControl[] +Private $fSpacing As Float +Private $sSpacing As String +Private $bRelativeSpacing As Boolean + +Private $aChildCopy As TControl[] +Private $bOnePiece As Boolean +Private $iIndex As Integer +Private $hData As Object +Private $fFixedSize As Float +Property _Index As Integer + +Property DataCount As Integer + +Property Read Children As ReportControl[] + +'Property Read Index As Integer +Property Spacing As String +'Property Data As Object '<==== remettre pour automation + +Property Read _Spacing As Float +Property Read _RelativeSpacing As Boolean + +Property OnePiece As Boolean +Public _RealSpacing As Integer +Public _NotFinished As Boolean + +Event BeforeData +Event AfterData + +Public Function _Add(cControl As ReportControl) 'As TControl + + 'Dim hTControl As New TControl + + 'hTControl.Ctrl = cControl + $aChild.Add(cControl) + ReportControl._ObjectFromId[cControl.Id] = cControl + 'Return hTControl + +End + +Public Sub _Remove(id As Integer) + + $aChild.Remove($aChild.Find(ReportControl._ObjectFromId[id])) + +End + +Public Sub _Free() + +End + +Private Function Children_Read() As ReportControl[] + + Return $aChild + +End + +Private Function Spacing_Read() As String + + Return $sSpacing + +End + +Private Sub Spacing_Write(Value As String) + + $sSpacing = Value + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, (DataIndex) As Integer) + + Dim hChild As TControl + + X += hControl.RealLeft + Y += hControl.RealTop + If Not hControl._PageChildren.Exist(Page) Then Return + 'Me._Index = hControl.Index + For Each hChild In hControl._PageChildren[Page] + 'Print object.Type(hchild.Ctrl), hControl.RelPage, hControl.RealPage + hchild.RelPage, Page - 1 + + 'If hchild.RelPage = -1 Or hControl.RealPage + hchild.RelPage = Page - 1 Then + + 'Inc ReportUnits.DrawCount + 'hchild.Ctrl._Index = hchild.Index + '$iIndex = DataIndex + 'If hControl.DataIndex > -1 Then $iIndex = hControl.DataIndex + Me._Index = hchild.Index + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + hChild.Ctrl._Paintframe(Page, X, Y, hChild, hChild.Index) + + Next + +End + +Private Function _Spacing_Read() As Float + + Return $fSpacing + +End + +Private Function _RelativeSpacing_Read() As Boolean + + Return $bRelativeSpacing + +End + +Public Sub _SetUnifiedValues() + + Dim hChild As ReportControl + Dim hSizeParse As TSizeParse + + Super._SetUnifiedValues() + + hSizeParse = New TSizeParse($sSpacing, True) + $fSpacing = hSizeParse.GetValue() + $bRelativeSpacing = hSizeParse.IsRelative() + + For Each hChild In $aChild + 'hchild.SizeHint = Null + hChild._SetUnifiedValues() + Next + +End + +Public Sub _GenerateClones() + + Dim hNewTControl, hTc As TControl + + Dim i As Integer + Dim hCont As ReportContainer + Dim aNewChild As TControl[] + + Dim aCopy As TControl[] + + If $aChildCopy Then + $aChild = $aChildCopy.Copy() + Endif + aCopy = $aChild.Copy() + + For Each hTc In aCopy + If hTc.Ctrl Is ReportContainer Then + hCont = hTc.Ctrl + If hCont.Data Then + aNewChild = New TControl[] + + For i = 0 To hCont.Data.Count - 1 + + hNewTControl = New TControl + hNewTControl.Ctrl = hTc.Ctrl + hNewTControl.DataIndex = i + aNewChild.Add(hNewTControl) + + Next + $aChild = $aChild.Insert(aNewChild, $aChild.Find(hTc)) + $aChild.Remove($aChild.Find(hTc)) + + Endif + hCont._GenerateClones + Endif + Next + If $aChild.Count <> aCopy.Count Then $aChildCopy = aCopy + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + + 'If Me._SizeInt.StoreSize Then Return Me._SizeInt + 'If Me.tag = "*" Then Stop + If Me._CurItem > Me.Children.max Then + + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + ' 'Me._DataIndex = 0 + Endif + Me._Index = DataIndex + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + Select Case _Arrangement + + Case Arrange.Vertical + Return GetVSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Horizontal + Return GetHSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Column + Return GetVSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Fill, Arrange.None + Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'hMyHints.Height = AvailableH + 'Me._SizeInt.StoreSize = True + 'Me._SizeInt = hMyHints + Return hMyHints + + End Select + Raise AfterData + +End + +Private Function GetHSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hChild As ReportControl + Dim fHeight, fWidth As Float + Dim hChildHints As TSizeHint + Dim hMyHints As New TSizeHint + Dim fSpacing As Float + + 'D'abord utiliser la méthode du controle pour définir la taille + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + 'Puis si besoins voir le besoin des enfants + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + fSpacing = IIf(Me._RelativeSpacing, TotalWidth * Me._Spacing / 100, Me._Spacing) + For Each hChild In Me.Children + hChildHints = hchild._GetSizeHints(AvailableW - fWidth, AvailableH, AvailableW, AvailableH, DataIndex) + fWidth += hChildHints.Width + fSpacing + If fHeight < hChildHints.Height Then fHeight = hChildHints.Height + 'Si l'enfant n'est pas finit alors moi non plus + If hChildHints.NotFinished Then hMyHints.NotFinished = True + Next + If fWidth Then fWidth -= fSpacing + hMyHints.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._bottom + hMyHints.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + Return hMyHints + +End + +Private Function GetVSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hSizeInt As New TSizeHint + Dim htmpInts As TSizeHint + Dim hChild As ReportControl ''Enfants + Dim fHeight, fSpacing, fWidth As Float + Dim i, j As Integer + Dim bExitLoop As Boolean + 'If Me.Tag = "Boite 2" Then Stop + 'D'abord utiliser la méthode du controle pour définir la taille + hSizeInt = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + fSpacing = IIf(Me._RelativeSpacing, TotalHeight * Me._Spacing / 100, Me._Spacing) + + ' Si ce n'est pas fait on enregistre la place nécéssaire a tout les éléments fixes + + If Not $fFixedSize Then + For i = 0 To Me.Children.Max + hChild = Me.Children[i] + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH, AvailableW, AvailableH, DataIndex) + If hchild.Fixed Then $fFixedSize += htmpInts.Height + fSpacing + + Next + + Endif + + 'La taille ne peut être inférieur a celle de tout les éléments fixes + hSizeInt.Height = Max(hSizeInt.Height, $fFixedSize) + + 'On ajoute la taille des objets fixes précédents + fHeight = $fFixedSize + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'On définit la taille au besoins des enfants + For i = Me._CurItem To Me.Children.Max + hChild = Me.Children[i] + For j = hchild._DataIndex To hchild._Count - 1 + 'If hchild.Tag = "head" Then Print "VDataHint = " & DataIndex + If hChild.Ignore Then Continue + If hChild.Fixed Then Continue + hchild._Index = j + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH - fHeight, AvailableW, AvailableH, j) + 'Si l'enfant n'est pas terminé alors moi non plus + If htmpInts.NotFinished Then hSizeInt.NotFinished = True + 'on ajoute la hauteur aux besoins + fHeight += htmpInts.Height + fSpacing + If fHeight - fSpacing > AvailableH Then + 'Les enfants ne loge pas ... on ne peut pas finir + hSizeInt.NotFinished = True + Break + bExitLoop = True + Endif + 'On récupère la largeur de l'enfant le plus large + If fWidth < htmpInts.Width Then fWidth = htmpInts.Width + Next + If bExitLoop Then Break + Next + + 'on enlève le dernier espace + If fHeight > 0 Then fHeight -= fSpacing + 'bogue bizzard + fHeight += 0.01 + + 'On indique la place nécéssaire aux enfants+les éléments fixes + hSizeInt.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._Bottom + hSizeInt.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + 'On ne peut pas dépasser la taille disponible (report sur prochaine page) + 'FIXME: Onepiece partially desactivated + If Not Me.OnePiece Then + hSizeInt.Height = Min(hSizeInt.Height, AvailableH) + Endif + + hSizeInt.Height = Min(TotalHeight, hSizeInt.Height) + + Me._SizeInt = hSizeInt + ' If Me.Tag = "Boite 2" Then + ' Print hSizeInt.Height + ' Stop + ' Endif + 'If Me.tag = "toto" Then Stop + Return hSizeInt + +End + +Public Sub _Reset() + + Dim hChild As ReportControl + 'mise a zéro de l'index de suivit de progression + Me._CurItem = 0 + 'Mise a zéro de l'index de reproduction + Me._DataIndex = 0 + 'Netoyage du layout précédent + 'Me._PageChildren.Clear + 'Nettoyage récurssif des enfants + For Each hChild In Me.Children + + hChild._Reset + + Next + +End + +Public Sub _SetChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + 'If Me.Tag = "*" Then Stop + 'Print "Geometry " & Object.Type(Me) + + Me._Index = TCont.Index + + If Me._CurItem > Me.Children.max Then + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + 'Me._DataIndex = 0 + Endif + + ' If $bIndexChange Then + ' Raise BeforeData + ' $bIndexChange = False + ' Endif + + Select Case _Arrangement + Case Arrange.Vertical + SetVChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Horizontal + SetHChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Column + Me._SetCChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Fill + SetFChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.None + SetNChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + End Select + Raise AfterData + +End + +Private Sub SetVChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As TSizeHint ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim fTH As Float ''Hauteur restante + Dim fSpc As Float ''Taille d'un espace + 'Dim X, Y As Float ''Position Haut gauche de départ + Dim hTItem As TControl ''Un objet virtuel + Dim oChild As Object ''Un objet gambas générique + Dim fExp As Float ''taille des objets étendus + Dim iNExp As Integer ''Nombre d'objets étendus + Dim ftmpHeight As Float ''Tampon pour le calcul de la taille répartie + Dim ftmpY As Float ''Curseur temporaire de position haute + Dim fX, fY As Float ''Tampon de position + Dim i, j As Integer ''Des indexs + Dim bExitLoop As Boolean ''Flag de sortie de traitement + 'Dim iPrevIndex As Integer + Dim bForceNewPage As Boolean ''Flag d'anticipation de sortie 1 par page + Dim iPreIndex As Integer + Dim bFirst As Boolean + 'Détermination de la taille d'un espace + 'et prise en compte de la taille relative + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) + + 'Retrait du padding et des bordures de la hauteur de travail + H = H - Me.Padding._Top - Me.Border._Top - Me.Border._Bottom - Me.Padding._Bottom + + 'Retrait du padding et des bordures a la largeur de travail + W = W - Me.Padding._Left - Me.Border._Left - Me.Border._Right - Me.Padding._Right + + 'Positioinnement du curseur en haut a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'La hauteur restante est initialisée avec la hauteur de travail + fTH = H + + '***************************************************************************************** + 'On enumere tous les enfants a la recherche des éléments fixes + 'Si des éléments fixes sont trouvés alors leur taille est déduite de la taille restante. + For i = 0 To Me.Children.Max + 'On récupère l'enfant + hchild = Me.Children[i] + 'Si l'enfant est fixe alors on le traite. + If hchild.Fixed Then + ' hchild._Index = TCont.Index + 'on commence par récupérer la taille de l'objet + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + 'On retire la taille de l'objet a la hau'teur restante + 'si l'objet n'est pas ignoré par l'agencement + If Not hchild.Ignore Then fTH = fTH - hChildHints.Height - fSpc + + 'Si l'énumération n'est pas encore arrivée a l'index sauvegardé + 'alors on ajoute directement l'élément a la page + If i < Me._CurItem Then + + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + + hTItem.Index = TCont.Index + 'L'ajouter a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Endif + Next + + '********************************************************************************* + 'On parcour a présent le reste des éléments + For i = Me._CurItem To Me.Children.Max + 'On récupère l'enfant + hChild = Me.Children[i] + 'Je traite ici la boucle de clonage + 'On définit le point de départ + 'If ContPage = 1 And hchild.Tag = "**" Then Stop + j = hChild._DataIndex + Do + '## Début boucle répétition + + 'On fixe l'index de données des enfants si + 'on est le duplicateur + If hChild._IsContainer And If hChild._count > 1 Then + iPreIndex = j + Else + iPreIndex = TCont.Index + Endif + 'If hchild.tag = "**" Then Print iPreIndex + 'If iPreIndex = 9 Then Stop + 'on récupère la taille de l'enfant + + hChildHints = hchild._GetSizeHints(W, fTH, W, H, iPreIndex) + + 'Les éléments fixes ont déja été traités on ne tient donc pas compte de leur hauteur + 'car elle a déja été déduite de l'espace restant. De meme on ignore les objets flottants (ignore=true) + If Not hchild.Fixed And If Not hchild.Ignore Then + + 'Si l'élément ne loge pas dans la place restante ou + 'si la place restante est insuffisante + 'on provoque la sortie en fin de boucle + If (fTH - hChildHints.Height) < 0 Or If fTH <= 0 Or bForceNewPage Then + ' If hchild.Tag = "*" Then Stop + If Not bFirst Then + hChildHints.Height = fTH + Else + bForceNewPage = False + hchild._DataIndex = j + + 'If Not bForceNewPage Then + bExitLoop = True + Break + Endif + Endif + bFirst = True + 'On déduit la taille de l'objet courant (et l'espace suivant) + fTH = fTH - hChildHints.Height - fspc + + Endif + 'L'objet loge dans la page, on génère donc une représentation de celui-ci + 'c'est a dire un objet virtuel pointant vers celui-ci + 'et apportant les informations nécéssaire au layout final + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'If hchild.Tag = "head" Then Stop + 'On associe l'index de donnée + hTItem.Index = iPreIndex + 'TItem.Index = IIf(hchild._count > 0, j, TCont.Index) + 'On l'ajoute a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + + 'If hChildHints.NotFinished Then hchild._DataIndex = j + 'un élément fixe ou ignore ne peut être répété + If hchild.Fixed Or hchild.Ignore Then Break + + 'Sachant que hChild._count peut être à -1 on le considère dans ce cas la comme étant a 0 + 'si j est égale au compte alors on quitte la boucle + If j >= Max(hchild._Count - 1, 0) Then + If Not hChildHints.NotFinished Then hChild._DataIndex = 0 + Break + Endif + + 'sinon on incrémente le compte a condition que le dernier enfant aie finit d'afficher ses enfants + If Not hChildHints.NotFinished Then Inc j + + 'On prévoie un sortie de boucle si l'enfant demande un affichage 1 par page + If hchild.ForceNewPage Then bForceNewPage = True + + Loop + 'Next '## fin de la boucle de répétition + 'Si la sortie anticipé est demandée alors on sort de la boucle + If bExitLoop Then Break + If i < Me.Children.Max Then + If Me.Children[i + 1] Is ReportPageBreak Then + bForceNewPage = True + Inc Me._CurItem + Inc i + Endif + Endif + 'Tout les objets on été répété alors on remet l'index à 0 + 'hchild._DataIndex = 0 + 'On incrémente le compteur des éléments traités + Inc Me._CurItem + Next + + If (H - fTH) > 0 Then + fTH += fspc 'Heu?????? + Endif + + '*************************************************************************** + 'Pour tous les éléments fixes jusqu'à la fin du document + For i = Max(Me._CurItem, 0) To Me.Children.Max + hchild = Me.Children[i] + If hchild.Fixed Then + hChildHints = hchild._GetSizeHints(W, fTH, W, H, iPreIndex) + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'On l'ajoute a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Next + + '****************************************************************************** + 'A présent tous les éléments pouvant être placé sur la page ont été marqué. + 'On peut donc procéder a la mise en forme de ceux-ci + + ftmpY = Y 'On définit la position de départ + If iNExp Then + fExp = (fExp + fTH) / iNExp + Endif + + For i = 0 To aPageItems.Max + 'on récupère l'objet virtuel + hTItem = aPageItems[i] + 'on récupère l'instance de l'objet réel + ochild = hTItem.Ctrl + + 'Si l'objet est étendu alors on calcul sa hauteur + 'Un objet flottant ne peut pas être étendu + If Not oChild.Ignore And If ochild.Expand Then + 'Calcul de la taille répartie + ftmpHeight = fExp + Else + 'Si l'objet n'est pas étendu alors sa taille est celle demandée par celui-ci + ftmpHeight = hTItem.SizeHint.Height + Endif + 'oChild._Index = hTItem.Index + 'Traitement des objets ignoré + 'Les propriété de taille sont celle fournie par l'objet + If oChild.Ignore Then + 'Calcul de la position de l'objet + 'si sa position est relative (%) alors on fait le ratio a partir de la largeur ou de la hauteur + 'sinon on utilise la position fournie par l'objet + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + 'Fixer la position de l'objet + hTItem._SetGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + + Else + 'Fixer la position de l'objet + hTItem._SetGeometry(X, fTmpY, W, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(X, fTmpY, W, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + 'on incrémente la position verticale de l'objet + 'pour définir sa place sur la page relativement aux autres éléments + fTmpY += ftmpHeight + fspc + + Endif + + 'Si l'objet est un conteneur alors il faut vérifier si il a finit d'être traité + + If oChild Is ReportContainer Then + 'If oChild._NotFinished Then Me.NotFinished = True + 'Si son traitement n'est pas terminé alors on trouve + 'sa position dans la liste des enfant et on l'assigne a l'index de traitement + 'si sa position est > a l'index courant + If ochild._CurItem <= oChild.Children.Max Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'Si on est dans la lignée d'un conteneur fixe on incrémente pas la lecture des enfants + 'En clair ce sont toujours les mêmes éléments qui apparaitrons + If bInFixed Then ochild._CurItem = 0 + Endif + Next + + 'On ajoute cette page au dossier du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function SetFChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim hTItem As TControl + Dim hChildHints As TSizeHint + Dim aPageItems As New TControl[] + Dim oChild As Object + Dim j As Integer + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + For Each hChild In Me.Children + hChildHints = hchild._GetSizeHints(W, H, W, H) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + For Each oChild In aPageItems + oChild._Index = hTItem.Index + hTItem._SetGeometry(X, Y, W, H) + hChild._SetChildGeometry(X, Y, W, H, ContPage, hTItem, bInFixed Or Me.Fixed) + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function SetNChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim ochild As Object + Dim hTItem As TControl + Dim hChildHints As TSizeHint + Dim aPageItems As New TControl[] + Dim iX, iY As Integer + Dim J As Integer + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + + For Each hChild In Me.Children + + hChildHints = hchild._GetSizeHints(W, H, W, H) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + + For Each hTItem In aPageItems + oChild = hTItem.Ctrl + hChildHints = hTItem.SizeHint + iX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + iY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + oChild._Index = hTItem.Index + hTItem._SetGeometry(iX, iY, hChildHints.Width, hChildHints.Height) + hChild._SetChildGeometry(iX, iY, hChildHints.Width, hChildHints.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenterla lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Sub SetHChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As TSizeHint ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim oChild As Object + Dim TW, fWidth, fSpc As Float + Dim fExp As Float + Dim iNExp As Integer + + Dim i, j As Integer + Dim fTmpX, fX, fY As Float + Dim hTItem As TControl + + 'Initialisation des variables + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) 'ME._Spacing + 'On retire les marges a la hauteur et les bordures + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + 'Print H + 'on retire a la largeur les paddings (et les bordure ?) + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + + 'On positionne le curseur de position au coin a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'On initialise la largeur totale avec la largeur disponible + TW = W + + For i = 0 To Me.Children.Max + + hChild = Me.Children[i] + + 'hchild._Index = IIf(Me.DataCount > 0, j, TCont.Index) + hChild._Index = TCont.Index + hChildHints = hChild._GetSizeHints(w, h, w, h, TCont.Index) + + 'Si l' éléments ne loge pas on quitte et on oublit + If TW - hChildHints.Width < 0 Then Break + + 'Si l'élément n'est pas ignoré + If Not hChild.Ignore Then + TW = TW - hChildHints.Width - fSpc + 'Si il est étendu on en tient compte + If hChild.Expand Then + Inc iNExp + fExp += hChildHints.Width + Endif + Endif + + 'On ajoute l'élément a la page + hTItem = New TControl + hTItem.Ctrl = hChild + hTItem.SizeHint = hChildHints + hTItem.Index = TCont.Index + aPageItems.Add(hTItem) + + Next + + 'De toute les façon je ne cherche pas a parcourir tout + 'Les objet donc j'indique que j'ai tout vu + Me._CurItem = Me.Children.count + + If (W - TW) > 0 Then + TW += fspc + Endif + + 'On va mettre en page a présent + 'On définit la taille des éléments étendus + If iNexp Then + fExp = (TW + fExp) / iNexp + Endif + + fTmpX = X + For Each hTItem In aPageItems + oChild = hTItem.Ctrl + 'Si l'élément est étendu on lui applique la taille répartie + If oChild.Expand And Not oChild.Ignore Then + 'If Me.Tag = "*" Then Stop + fWidth = fExp + Else + 'sinon il maintien sa taille + fWidth = hTItem.SizeHint.Width + Endif + 'oChild._Index = hTItem.Index + If Not oChild.Ignore Then + hTItem._SetGeometry(fTmpX, Y, fWidth, H) + oChild._SetChildGeometry(fTmpX, Y, fWidth, H, ContPage, hTItem, bInFixed Or Me.Fixed) + fTmpX += fWidth + fspc + Else + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + hTItem._SetGeometry(fX, fY, fWidth, hTItem.SizeHint.Height) + oChild._SetChildGeometry(fX, fY, fWidth, hTItem.SizeHint.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + Endif + + If oChild Is ReportContainer Then + If oChild._CurItem <= oChild.Children.max Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + + Endif + Next + + 'On ajoute la page a la collection de page du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function DataCount_Read() As Integer + + Return Me._Count + +End + +Private Sub DataCount_Write(Value As Integer) + 'If value = 0 Then value = 1 + + Me._Count = Value + +End + +' Public Function _GetIndex() As Integer +' +' If Me._Count > 0 Then 'Si c'est moi le répéteur alors je fournis mon index +' Return Me._Index +' Else +' Try Return Me.Parent._GetIndex 'Sinon je ontinue a remonter la lignée jusqu'au répéteur +' Endif +' +' End + +Private Function _Index_Read() As Integer + + Return Super._Index + +End + +Private Sub _Index_Write(Value As Integer) + + If Super._Index = Value And If Super._Index > 0 Then Return + $bIndexChange = True + Super._Index = Value + +End + +Private Function _CurItem_Read() As Integer + + Return $iCurItem + +End + +Private Sub _CurItem_Write(Value As Integer) + 'Print "_CurItem = " & Value + 'If Me.tag = "Boite 1" And Value = 0 Then Stop + 'If Me.tag = "Boite 1" And ContPage = 1 Then Print "CurItem: " & Me._CurItem + + $iCurItem = Value + +End + +Private Function OnePiece_Read() As Boolean + + Return $bOnePiece + +End + +Private Sub OnePiece_Write(Value As Boolean) + + $bOnePiece = Value + +End + +Public Sub _SetCChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + +End diff --git a/comp/src/gb.report/.src/ReportDrawingArea.class b/comp/src/gb.report/.src/ReportDrawingArea.class new file mode 100644 index 00000000..53fd2453 --- /dev/null +++ b/comp/src/gb.report/.src/ReportDrawingArea.class @@ -0,0 +1,63 @@ +' Gambas class file + +Export +Inherits ReportFrame +Public Const _Properties As String = "*,Cached" +Public Const _Similar As String = "ReportImage" +Public Const _DefaultEvent As String = "Draw" + +Property Cached As Boolean + +Private $bCached As Boolean +Private $iOldResolution As Integer + +Event Layout(Width As Integer, Height As Integer, Index As Integer) +Event Draw(Width As Integer, Height As Integer, Index As Integer) + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim hRect As Rect + Dim iX, iY, iW, iH As Integer + Dim hImg As Image + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + iH = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + Paint.Save + + + If $bCached Then + If $iOldResolution <> Paint.ResolutionX Or If hControl.Cache = Null Then + $iOldResolution = Paint.ResolutionX + hImg = New Image(iW, iH, Color.Transparent) + Paint.Begin(hImg) + Raise Draw(iW, iH, DataIndex) + Paint.End + hControl.Cache = himg + Else + If hControl.cache = Null Then Stop + hImg = hControl.Cache + Endif + Draw.Image(hImg, iX, iy) + Else + + Paint.Translate(iX, iY) + Raise Draw(iW, iH, DataIndex) + Paint.Translate(- iX, - iY) + Endif + Paint.Restore + +End + +Private Function Cached_Read() As Boolean + + Return $bCached + +End + +Private Sub Cached_Write(Value As Boolean) + + $bCached = Value + +End diff --git a/comp/src/gb.report/.src/ReportFrame.class b/comp/src/gb.report/.src/ReportFrame.class new file mode 100644 index 00000000..96cd44df --- /dev/null +++ b/comp/src/gb.report/.src/ReportFrame.class @@ -0,0 +1,354 @@ +' Gambas class file + +Export +Create Private +Inherits ReportControl +' Static Private iLevel As Integer +Public Const _Properties As String = "*,Border,Background{ReportBrush}" + +' Private $fLeftBorderWidth As Float +' Private $fRightBorderWidth As Float +' Private $fTopBorderWidth As Float +' Private $fBottomBorderWidth As Float + +Private $hBorder As New ReportBorder + +Private $hBackGround As ReportBrush + +Property Border As ReportBorder +Property BoxShadow As ReportBoxShadow + +Private $hBoxShadow As New ReportBoxShadow +Property BackGround As ReportBrush +'Property Read _BorderWidth As Float + +Private Function Border_Read() As ReportBorder + + Return $hBorder + +End + +Private Sub Border_Write(Value As ReportBorder) + + $hBorder = Value + +End + +Public Sub _PaintBefore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2 As Float + Dim iBorder As Integer + Dim hShadowActive As Boolean = $hBoxShadow._Active + Dim himgShadow As Image + Dim fSpread As Float + Dim fBlur As Float + Dim fbx As Float + Dim fby As Float + Dim TL1, TL2, TR1, TR2, BR1, BR2, BL1, BL2 As Float + 'If Me.tag = "*" Then Stop + + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + X2 = (x + hControl.RealLeft + hControl.RealWidth) + Y2 = (y + hControl.RealTop + hControl.RealHeight) + 'Clipping + paint.save + + ' Print String(iLevel, " ") & "sauvé" + ' Inc iLevel + ' Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + ' paint.Clip(True) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Green) + 'Paint.Dash = ReportUnits.GetBorder(Line.Dot) + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + Paint.Stroke + + Else + 'If $hBorder.Shadow.Style > ReportShadowStyle.None Then PaintShadow(X1, Y1, hControl.RealWidth, hControl.RealHeight, $hBorder.RoundCorner._Active) + If $hBorder.RoundCorner._Active Then + TL1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft1) + TR1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight1) + BR1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight1) + BL1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft1) + TL2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft2) + TR2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight2) + BR2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight2) + BL2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft2) + + If hShadowActive Then + fbx = ReportUnits._ReportUnitsToPixels($hBoxShadow._XOffset) + fby = ReportUnits._ReportUnitsToPixels($hBoxShadow._YOffset) + fSpread = ReportUnits._ReportUnitsToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = ReportUnits._ReportUnitsToPixels($hBoxShadow._Blur) + himgShadow = New Image(hControl.RealWidth + fSpread * 2 + fBlur * 2, hControl.RealHeight + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(X1 + fbx - fSpread, Y1 + fby - fSpread, hControl.RealWidth + fSpread * 2, hControl.RealHeight + fSpread * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Endif + Endif + + iBorder = ReportUnits._ReportUnitsToPixels($hBorder._Top) + + RoundRect(X1 + iBorder, Y1 + iBorder, hControl.RealWidth - iBorder * 2, hControl.RealHeight - iBorder * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + + Else + + If hShadowActive Then + fbx = ReportUnits._ReportUnitsToPixels($hBoxShadow._XOffset) + fby = ReportUnits._ReportUnitsToPixels($hBoxShadow._YOffset) + fSpread = ReportUnits._ReportUnitsToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = ReportUnits._ReportUnitsToPixels($hBoxShadow._Blur) + himgShadow = New Image(hControl.RealWidth + fSpread * 2 + fBlur * 2, hControl.RealHeight + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.rectangle(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + Endif + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.Rectangle(X1 + fbx - fSpread, Y1 + fby - fSpread, hControl.RealWidth + fSpread * 2, hControl.RealHeight + fSpread * 2) + Paint.Fill + Endif + + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + + Endif + + If Me.BackGround = Null Then + paint.Clip + Return + Else + paint.Clip(True) + Endif + paint.Brush = Me.BackGround._PaintBrush(X1, Y1, X2, Y2) + + Paint.Fill + + Endif + +End + +Private Sub PaintBoxShadow(X As Integer, Y As Integer, Width As Integer, Height As Integer) + +End + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + _PaintBefore(Page, X, Y, hControl, DataIndex) + Me._Paint(Page, X, Y, hControl, DataIndex) + _PaintAfter(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2 As Float + 'Dim fWidth As Float + + Dim fTopWidth, fBottomWidth, fLeftWidth, fRighWidth As Float + + 'Fin du clipping + paint.Restore + + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + X2 = (x + hControl.RealLeft + hControl.RealWidth) + Y2 = (y + hControl.RealTop + hControl.RealHeight) + 'If Me.Tag = "**" Then Stop + If $hBorder.RoundCorner._Active Then + Paint.AntiAlias = True + fLeftWidth = ReportUnits._ReportUnitsToPixels($hBorder._Left) / 2 + paint.LineWidth = fLeftWidth * 2 + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + ' RoundRect(Paint.LineWidth / 2, Paint.LineWidth / 2, Paint.Width - Paint.LineWidth, Paint.Height - Paint.LineWidth, aX, aY) + RoundRect(X1 + fLeftWidth, Y1 + fLeftWidth, hControl.RealWidth - fLeftWidth * 2, hControl.RealHeight - fLeftWidth * 2, + [ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft1), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight1), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight1), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft1)], + [ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft2), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight2), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight2), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft2)]) + paint.Stroke + + Else + 'Paint.AntiAlias = False + fLeftWidth = ReportUnits._ReportUnitsToPixels($hBorder._Left) + fRighWidth = ReportUnits._ReportUnitsToPixels($hBorder._Right) + fTopWidth = ReportUnits._ReportUnitsToPixels($hBorder._Top) + fBottomWidth = ReportUnits._ReportUnitsToPixels($hBorder._Bottom) + + 'If $hBorder.Style <> Line.None Then + + 'fWidth = ReportUnits._ReportUnitsToPixels($fBorderWidth) + + 'Paint.Brush = Paint.Color($hBorder.Brush._iValue) + + If fTopWidth > 0 Then + paint.Brush = $hBorder.Top.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, X2 - X1, fTopWidth) + paint.Fill + Endif + If fRighWidth > 0 Then + paint.Brush = $hBorder.Right.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X2 - fRighWidth, Y1, fRighWidth, Y2 - Y1) + paint.Fill + Endif + If fBottomWidth > 0 Then + paint.Brush = $hBorder.Bottom.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y2 - fBottomWidth, X2 - X1, fBottomWidth) + paint.Fill + Endif + If fLeftWidth > 0 Then + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, fLeftWidth, Y2 - Y1) + paint.Fill + Endif + 'Paint.AntiAlias = True + ' + + Endif + 'Dec iLevel + 'Print String(iLevel, " ") & "restoré" + + 'Endif + +End + +Private Function BackGround_Read() As ReportBrush + + Return $hBackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + $hBackGround = Value + +End + +Public Sub _SetUnifiedValues() + + Super._SetUnifiedValues() + $hBorder._SetUnifiedValues + $hBoxShadow._SetUnifiedValues + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As TSizeHint + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If hMyHints.Height <= 0 Then + hMyHints.Height = Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + 'hMyHints.Height += Abs($hBoxShadow._YOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + If hMyHints.Width <= 0 Then + hMyHints.Width = Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right + 'hMyHints.Width += Abs($hBoxShadow._xOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + Return hMyHints + +End + +Static Private Sub RoundRect(x As Float, y As Float, w As Float, h As Float, Radius_X As Float[], radius_y As Float[]) + + Dim ARC_TO_BEZIER As Float = 0.55228475 + Dim c1, c2 As Float + Dim i As Integer + + For i = 0 To 3 + If radius_x[i] > w - radius_x[i] Then + radius_x[i] = w / 2 + Endif + + If radius_y[i] > h - radius_y[i] Then + radius_y[i] = h / 2 + Endif + + Next + 'approximate(quite Close )the arc using a bezier curve + + ' A**********B + ' H C + ' * * + ' * * + ' G D + ' F**********E + + '-->A + Paint.MoveTo(x + radius_x[0], y) + + '-->B + Paint.LineTo(x + w - radius_x[1], y) + + '-->C + c1 = ARC_TO_BEZIER * radius_x[1] + c2 = ARC_TO_BEZIER * radius_y[1] + Paint.RelCurveTo(c1, 0.0, radius_x[1], c2, radius_x[1], radius_y[1]) + + '-->D + Paint.LineTo(x + w, y + h - radius_y[2]) + + '-->E + c1 = ARC_TO_BEZIER * radius_x[2] + c2 = ARC_TO_BEZIER * radius_y[2] + Paint.RelCurveTo(0.0, c2, c1 - radius_x[2], radius_y[2], - radius_x[2], radius_y[2]) + + '-->F + Paint.LineTo(x + radius_x[3], y + h) + + '-->G + c1 = ARC_TO_BEZIER * radius_x[3] + c2 = ARC_TO_BEZIER * radius_y[3] + Paint.RelCurveTo(- c1, 0, - radius_x[3], - c2, - radius_x[3], - radius_y[3]) + + '-->H + Paint.LineTo(x, y + radius_y[0]) + + '-->A + c1 = ARC_TO_BEZIER * radius_x[0] + c2 = ARC_TO_BEZIER * radius_y[0] + Paint.relcurveto(0.0, - c2, radius_x[0] - c1, - radius_y[0], radius_x[0], - radius_y[0]) + + 'Paint.closepath() + +End + +Private Function BoxShadow_Read() As ReportBoxShadow + + Return $hBoxShadow + +End + +Private Sub BoxShadow_Write(Value As ReportBoxShadow) + + $hBoxShadow = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report/.src/ReportGridView.class b/comp/src/gb.report/.src/ReportGridView.class new file mode 100644 index 00000000..3b61daf1 --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView.class @@ -0,0 +1,188 @@ +' Gambas class file + +Export +Inherits ReportFrame +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" + +Property Read Columns As _ReportGridViewColumns +Property Read Rows As _ReportGridViewRows +'Property Autoresize As Boolean + +Private $bAutoresize As Boolean +Private $hColumns As _ReportGridViewColumns +Private $hRows As _ReportGridViewRows + + + + +Event Data(Row As Integer, Column As Integer) + +Public Sub _new() + $hColumns = New _ReportGridViewColumns As "Columns" + $hRows = New _ReportGridViewRows As "Rows" + + +End + + +Public Sub _SetUnifiedValues() + + Dim i As Integer + Super._SetUnifiedValues + $hRows._SetUnifiedValues() + $hColumns._SetUnifiedValues() + ' For i = 0 To $hColumns.Max + ' $hColumns[i]._Width = TSizeParse[$hColumns[i].Width].ToCm() + ' Next + ' + ' For i = 0 To $hRows.Max + ' $hRows[i]._Height = TSizeParse[$hRows[i].Height].ToCm() + ' Next + + +End + + + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + Dim fWidth As Float + Dim i As Integer + 'On Récupère la taille standard + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + If hMyHints.Width = 0 Then hMyHints.Width = 1 + If hMyHints.Height = 0 Then hMyHints.Height = 5 + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + + 'Largeur + + ' For i = 0 To $hColumns.Max + ' + ' fWidth += $hColumns[i].Width + ' + ' Next + + + + Endif + + + + + + Return hMyHints + + End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iColWidth As Float = ReportUnits._ReportUnitsToPixels(Me.Rows._Width) + Dim iRowHeight As Float = ReportUnits._ReportUnitsToPixels(Me.Rows._Height) + Dim i, j As Integer + Dim fCurY, fCurX, fH, fRow As Float + Dim fRowWidth As Float = ReportUnits._ReportUnitsToPixels($hRows._Width) + Dim fColHeight As Float = ReportUnits._ReportUnitsToPixels($hColumns._Height) + Dim fX As Float = X + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Border._Left) + Dim fY As Float = Y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Border._Top) + 'Paint.Brush = Paint.Color(Color.Red) + 'Paint.Rectangle(X + hControl.RealLeft, Y + hControl.RealTop, hControl.RealWidth, hControl.RealHeight) + 'Paint.Fill + Paint.LineWidth = ReportUnits._ReportUnitsToPixels(Me.Border._Top) + + + fCurY = fY + fColHeight + + + For i = 0 To $hRows.Max + + Paint.MoveTo(fX, fCurY) + Paint.LineTo(fX + hControl.RealWidth, fCurY) + fCurY += ReportUnits._ReportUnitsToPixels($hRows[i]._Height) + + Next + + fCurX = fX + fRowWidth + + For i = 0 To $hColumns.max + + Paint.MoveTo(fCurX, fY) + Paint.LineTo(fCurX, fY + hControl.RealHeight) + fCurX += ReportUnits._ReportUnitsToPixels($hColumns[i]._Width) + + Next + + + + + + + Paint.Stroke + + + + + + + + + + + + fCurY = fY + fColHeight + For i = 0 To $hRows.Max + fH = ReportUnits._ReportUnitsToPixels($hRows[i]._Height) + Paint.Text($hRows[i].Text, fX, fCurY, fRowWidth, fH, Align.Right) + Paint.Fill + fCurY += fH + Next + + + + + + + + + + + +End + + + + + + + + + + + + +Private Function Columns_Read() As _ReportGridViewColumns + + Return $hColumns + +End + +Private Function Rows_Read() As _ReportGridViewRows + + Return $hRows + +End + +Private Function Autoresize_Read() As Boolean + + Return $bAutoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + $bAutoresize = Value + +End diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class new file mode 100644 index 00000000..f5baf4d7 --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class @@ -0,0 +1 @@ +' Gambas class file diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class new file mode 100644 index 00000000..f26daa8a --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class @@ -0,0 +1,18 @@ +' Gambas class file + +Property Width As String +Public _Width As Float + + + +Private Function Width_Read() As String + + + +End + +Private Sub Width_Write(Value As String) + + + +End diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class new file mode 100644 index 00000000..bc35faca --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class @@ -0,0 +1,117 @@ +' Gambas class file + +Property Count As Integer +Property Read max As Integer + +Property Width As String +Private $sWidth As String +Property Height As String +Private $sHeight As String +Public _Height As Float +Private $aColumns As New _ReportGridViewColumn[] +Event _Foo + + + + + +Public Sub _SetUnifiedValues() + Dim hCol As _ReportGridViewColumn + If $sHeight Then + _Height = TSizeParse[$sHeight].ToCm() + Else + _Height = ReportUnits._PixelsToReportUnits(GetView().Font.TextHeight("")) + Endif + + For Each hcol In $aColumns + + If hcol.Width Then + hcol._Width = TSizeParse[hcol.Width].ToCm() + Else + 'TODO: Calcul de la largeur des colonnes + hcol._Width = 2 + Endif + Next + + + + +End + + + + + + + + +Private Function Count_Read() As Integer + + Return $aColumns.Max + +End + +Public Function _get(Index As Integer) As _ReportGridViewColumn + + Return $aColumns[Index] + +End + + + +Private Sub Count_Write(Value As Integer) +Dim hRow As _ReportGridViewColumn +Dim i As Integer + If Value = $aColumns.Count Then Return + If Value > $aColumns.Count Then + For i = $aColumns.Count To Value + hRow = New _ReportGridViewColumn + $aColumns.Add(hRow) + Next + + Else + + $aColumns.Remove(Value, $aColumns.Count - Value) + + Endif + +End + +Private Function max_Read() As Integer + + Return $aColumns.Max + +End + +Private Function Width_Read() As String + + If $aColumns.Count Then + Return $aColumns[0].Width + Endif + +End + +Private Sub Width_Write(Value As String) + Dim hCol As _ReportGridViewColumn + For Each hcol In $aColumns + hcol.Width = Value + Next +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Sub GetView() As ReportGridView + + Return Object.Parent(Me) + +End diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class new file mode 100644 index 00000000..153324af --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class @@ -0,0 +1,51 @@ +' Gambas class file + +Property Height As String +Property Text As String + +Property _Height As Float + +Public _Row As Integer +Event _Foo + +Private Function Height_Read() As String + + + +End + +Private Sub Height_Write(Value As String) + + + +End + +Private Function Text_Read() As String + + Return GetView()._GetRowText(_Row) + +End + +Private Sub Text_Write(Value As String) + + + +End + +Private Function _Height_Read() As Float + + Return GetView()._GetHeight() + +End + +Private Sub _Height_Write(Value As Float) + + + +End + +Private Sub GetView() As _ReportGridViewRows + + Return Object.Parent(Me) + +End \ No newline at end of file diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class new file mode 100644 index 00000000..5d9fda26 --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class @@ -0,0 +1,160 @@ +' Gambas class file + +Property Count As Integer +Property Read Max As Integer + +Private $cHeight As Collection +Private $aHeight As Integer[] +Private $cText As Collection +Private $nRows As Integer + +Property Height As String +Property Width As String + +Private $sHeight As String +Private $sWidth As String + +Private $fWidth As Float +Private $fHeight As Float +Property Read _Height As Float +Property Read _Width As Float + + + +Event _Foo + +Public Sub _SetUnifiedValues() + Dim hView As ReportGridView = GetView() + If $sHeight Then + $fHeight = TSizeParse[$sHeight].ToCm() + Else + $fHeight = ReportUnits._PixelsToReportUnits(hView.Font.Height + 4) + Endif + + If $sWidth Then + $fWidth = TSizeParse[$sWidth].ToCm() + + Else + $fWidth = ReportUnits._PixelsToReportUnits(hView.Font.TextWidth("9") * (1 + CInt(Log10(Max(1, $nRows)))) + 8) + Endif + + +End + +Public Sub _GetHeight() As Float + + Return $fHeight + +End + + + +Public Sub _GetRowText(iRow As Integer) As String + + Dim sText As String + + If $cText Then sText = $cText[iRow] + If Not sText Then sText = CStr(iRow + 1) + Return sText + +End + +Public Sub _SetRowText(iRow As Integer, sText As String) + + Dim W As Float + + If Not $cText Then $cText = New Collection + $cText[iRow] = sText + If sText Then + W = ReportUnits._PixelsToReportUnits(GetView().Font.TextWidth(sText) + 8) + If W > _Width_Read() Then $fWidth = W + Endif + +End + + + +Public Function _get(Index As Integer) As _ReportGridViewRow + Dim hRow As New _ReportGridViewRow As "Row" + hRow._Row = Index + Return hRow + +End + + +Private Function Count_Read() As Integer + + Return $nRows + +End + +Private Sub Count_Write(Value As Integer) +Dim hView As ReportGridView = GetView() + Dim iRow As Integer + + If Value = $nRows Then Return + + $nRows = Value + + If $aHeight Then + While $aHeight.Count + If $aHeight[$aHeight.Max] < $nRows Then Break + iRow = $aHeight[$aHeight.Max] + $cHeight.Remove(iRow) + $aHeight.Remove($aHeight.Max) + 'If hView.Mode = Select.Multiple Then $hSel.UnSelect(iRow) + Wend + Endif + +End + +Private Function Max_Read() As Integer + + Return $nRows - 1 + +End + +Private Sub GetView() As ReportGridView + + Return Object.Parent(Me) + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Function _Height_Read() As Float + + + Return $fHeight + +End + +Private Function _Width_Read() As Float + + Return $fWidth + +End + + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = value + +End + diff --git a/comp/src/gb.report/.src/ReportHBox.class b/comp/src/gb.report/.src/ReportHBox.class new file mode 100644 index 00000000..95bff9e9 --- /dev/null +++ b/comp/src/gb.report/.src/ReportHBox.class @@ -0,0 +1,21 @@ +' Gambas class file + +Export + +Inherits ReportContainer + +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "H" + +Public Sub _new() + + Super._Arrangement = Arrange.Horizontal + +End + +Public Sub _Free() + + Super._Free + +End diff --git a/comp/src/gb.report/.src/ReportImage.class b/comp/src/gb.report/.src/ReportImage.class new file mode 100644 index 00000000..78e95f01 --- /dev/null +++ b/comp/src/gb.report/.src/ReportImage.class @@ -0,0 +1,186 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{Image}" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" + +Private $iAlignment As Integer = Align.Normal +Private $hPic As Image +Private $iStretchMode As Integer = Report.Proportional + +Property Alignment As Integer +Property Image As Image +Property Stretch As Integer + + + +Public Data As Image + +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + + + Dim hPic As Image + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + ReportUnits.UnitToCm(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Width, Me.Padding._Top + ReportUnits.UnitToCm(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + Dim hBrush As PaintBrush + Dim hPic As Image + + Dim w, h As Float + + + ix = x + hControl.RealLeft '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + hBrush = Paint.Image(hpic) + + If Me.Stretch = Report.Fill Then + iX += ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left) + iY += ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + hBrush.Translate(ix, iy - 1) + hBrush.Scale(w / hPic.Width, h / hPic.Height) + Paint.Brush = hBrush + Paint.Rectangle(ix, iy, w, h) + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hpic.Width >= hpic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + Endif + Endif + + Else + w = hpic.Width + h = hpic.H + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Bottom) - h + End Select + hBrush.Translate(ix, iy - 1) + hBrush.Scale(w / hpic.Width, h / hpic.H) + Paint.Brush = hBrush + Paint.Rectangle(ix, iy, w, h - 1) + Endif + Paint.Fill + +End + +Private Function Image_Read() As Image + + Return $hPic + +End + +Private Sub Image_Write(Value As Image) + + $hPic = Value + 'If Left(Me.Width, 1) = "0" Then Me.Width = $hpic.Width & " px" + 'If Left(Me.Height, 1) = "0" Then Me.Height = $hpic.Height & " px" + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + diff --git a/comp/src/gb.report/.src/ReportLabel.class b/comp/src/gb.report/.src/ReportLabel.class new file mode 100644 index 00000000..c44fb1f7 --- /dev/null +++ b/comp/src/gb.report/.src/ReportLabel.class @@ -0,0 +1,226 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Format,Alignment{Align.*},Rotate{Angle:Degrees},UseField" '"*,Text,Key,Format,Alignment{Align.*},UseField" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" +Private $sText As String +Private $iAlignment As Integer = Align.Normal +Private $bIsSpecial As Boolean + +Private $sKey As String +Private $sFormat As String +Private $bUseField As Boolean +Private $fAngle As Float + +Property Text As String +Property Alignment As Integer +Property IsSpecial As Boolean +'Property Key As String +Property {Format} As String +Property UseField As Boolean +Property Rotate As Float + +Public Data As String +Event Data(Index As Integer) + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + 'Me._Count = 2 + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + Dim fTextHeight As Float + Dim fTextWidth As Float + Dim hext As PaintExtents + Dim hRect As RectF + Dim sText As String + + 'On Récupère la taille standard + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + 'if the size choosed by the user is less than the font height, set the object to the font height + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If Not IsNull(Me.Font) Then Paint.Font = Me.Font + If Not $sText Then + Raise Data(DataIndex) + sText = Data + + Else + sText = $sText + Endif + If $fAngle = 0.0 Then + + hRect = Paint.TextSize(sText) + fTextWidth = Me.Padding._Left + Me.Border._Left + ReportUnits._PixelsToReportUnits(hRect.Width) + Me.Padding._Right + Me.Border._Right + fTextHeight = ReportUnits._PixelsToReportUnits(hRect.Height) + Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + 'Debug Paint.Font.Height * Paint.ResolutionX / Desktop.Resolution + + Else + Paint.Save + Paint.Rotate(Rad($fAngle)) + Paint.Text(sText, 0, 0) + hExt = Paint.PathExtents + fTextWidth = Me.Padding._Left + Me.Border._Left + ReportUnits._PixelsToReportUnits(hExt.Width) + Me.Padding._Right + Me.Border._Right + fTextHeight = ReportUnits._PixelsToReportUnits(hExt.Height) + Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + Paint.Restore + Endif + hMyHints.Height = Max(hMyHints.Height, fTextHeight) + hMyHints.Width = Max(hMyHints.Width, fTextWidth) + Endif + 'Me._SizeInt = hMyHints + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim sTempText As Variant + Dim iX, iY, iW, iH As Integer + + + If $sText Then + sTempText = $sText + Else + Raise Data(DataIndex) + sTempText = Data + Endif + + If $sFormat Then + Try sTempText = Format(sTempText, $sFormat) + Endif + If $bUseField Then + sTempText = DecodeText(sTempText, Page) + Endif + + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + + iH = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Red) + Paint.Rectangle(ix, iy, iw, ih) + Paint.Stroke + + Endif + + 'Set the Font if it is initialized + If Not IsNull(Me.Font) Then + Paint.Font = Me.Font + + Endif + + + If $fAngle = 0.0 Then + + Paint.Text(sTempText, iX, iY, iW, iH, $iAlignment) + Else + Paint.Translate(iX + iW / 2, iY + iH / 2) + Paint.Rotate(Rad($fAngle)) + Paint.Translate(- (iX + iW / 2), - (iY + iH / 2)) + Paint.Text(sTempText, iX, iY, iW, iH, Align.Center) + + Endif + Paint.Fill + + 'Paint.ResetClip + 'Paint.Restore + 'Paint.Restore + 'If $sKey Then $sText = Null + 'Draw.Clip.Enabled = False + +End + +Private Function IsSpecial_Read() As Boolean + + Return $bIsSpecial + +End + +Private Sub IsSpecial_Write(Value As Boolean) + + $bIsSpecial = Value + +End + + +Private Function Format_Read() As String + + Return $sFormat + +End + +Private Sub Format_Write(Value As String) + + $sFormat = Value + +End + +Private Function DecodeText(sText As String, Optional Page As Integer, Optional ForSize As Boolean = False) As String + + If ForSize Then + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", "999") + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", "999") + Else + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", Str(Page + 1)) + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", Str(Me.Report.PageCount)) + Endif + + Return sText + +End + +Private Function UseField_Read() As Boolean + + Return $bUseField + +End + +Private Sub UseField_Write(Value As Boolean) + + $bUseField = Value + +End + +Private Function Rotate_Read() As Float + + Return $fAngle + +End + +Private Sub Rotate_Write(Value As Float) + + $fAngle = Value + +End diff --git a/comp/src/gb.report/.src/ReportLine.class b/comp/src/gb.report/.src/ReportLine.class new file mode 100644 index 00000000..de2723e1 --- /dev/null +++ b/comp/src/gb.report/.src/ReportLine.class @@ -0,0 +1,127 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "*,Direction{Align.TopLeft;Top;TopRight;Left;Right;BottomLeft;Bottom;BottomRight}=BottomRight,LineWidth{ReportCoord}=2 px" +Public Const _Similar As String = "ReportTextLabel" +Property Direction As Integer + +Private $fLineWidth As Float = 1.0 + +Private $iLineStyle As Integer = Line.solid +Private $iDirection As Integer = Align.BottomRight +Private $sLineWidth As String = "2px" +Property LineWidth As String +Property LineStyle As Integer + +Public Sub _new() + + Me.Height = "1cm" + Me.Width = "1cm" + +End + +Private Function LineWidth_Read() As String + + Return $sLineWidth + +End + +Private Sub LineWidth_Write(Value As String) + + $sLineWidth = Value + +End + +Public Sub _SetUnifiedValues() + + Dim hSizeParse As TSizeParse + + Super._SetUnifiedValues() + + hSizeParse = New TSizeParse($sLineWidth) + $fLineWidth = hSizeParse.GetValue() + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + + hMyHints.Height = Me._Height + hMyHints.Width = Me._Width + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iX, iY, W, H, W2, H2 As Integer + + If Me.LineStyle = Line.None Then Return + If Me.LineStyle <> Line.Solid Then Paint.Dash = ReportUnits.GetBorder(Me.LineStyle) + Paint.LineWidth = ReportUnits._ReportUnitsToPixels($fLineWidth) + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top)) + + W = iX + hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Right) + H = iY + hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Bottom) + Paint.Brush = Me._GetActualBrush(iX, iY, W, H) + W2 = (iX + W) / 2 + H2 = (iY + H) / 2 + Select Case $iDirection + Case Align.TopLeft + Paint.MoveTo(W, H) + Paint.LineTo(iX, iY) + Case Align.Top + Paint.MoveTo(W2, H) + Paint.LineTo(W2, iY) + Case Align.TopRight + Paint.MoveTo(iX, H) + Paint.LineTo(W, iY) + Case Align.Left + Paint.MoveTo(W, H2) + Paint.LineTo(iX, H2) + Case Align.Right + Paint.MoveTo(iX, H2) + Paint.LineTo(W, H2) + Case Align.BottomLeft + Paint.MoveTo(W, iY) + Paint.LineTo(iX, H) + Case Align.Bottom + Paint.MoveTo(W2, iY) + Paint.LineTo(W2, H) + Case Align.BottomRight + Paint.MoveTo(iX, iY) + Paint.LineTo(W, H) + End Select + + Paint.Stroke + +End + +Private Function LineStyle_Read() As Integer + + Return $iLineStyle + +End + +Private Sub LineStyle_Write(Value As Integer) + + $iLineStyle = Value + +End + +Private Function Direction_Read() As Integer + + Return $iDirection + +End + +Private Sub Direction_Write(Value As Integer) + + $iDirection = Value + +End diff --git a/comp/src/gb.report/.src/ReportPageBreak.class b/comp/src/gb.report/.src/ReportPageBreak.class new file mode 100644 index 00000000..ea95ef98 --- /dev/null +++ b/comp/src/gb.report/.src/ReportPageBreak.class @@ -0,0 +1,6 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "-*" diff --git a/comp/src/gb.report/.src/ReportPanel.class b/comp/src/gb.report/.src/ReportPanel.class new file mode 100644 index 00000000..d05ebd32 --- /dev/null +++ b/comp/src/gb.report/.src/ReportPanel.class @@ -0,0 +1,25 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,Arrangement{Arrange.None;Vertical;Horizontal;Column;Fill}=Vertical" +Public Const _Similar As String = "ReportVBox" +Property Arrangement As Integer + +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + +End + +Private Function Arrangement_Read() As Integer + + Return Super._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Super._Arrangement = Value + +End diff --git a/comp/src/gb.report/.src/ReportSection.class b/comp/src/gb.report/.src/ReportSection.class new file mode 100644 index 00000000..94224b5b --- /dev/null +++ b/comp/src/gb.report/.src/ReportSection.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits ReportVBox +Public Const _Properties As String = "*,Text" +Property Text As String +Private $sText As String + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report/.src/ReportSvgImage.class b/comp/src/gb.report/.src/ReportSvgImage.class new file mode 100644 index 00000000..9927574d --- /dev/null +++ b/comp/src/gb.report/.src/ReportSvgImage.class @@ -0,0 +1,185 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Private $hPic As SvgImage +Private $iStretchMode As Integer = Report.Proportional +Private $iAlignment As Integer = Align.Normal +'Private $sPath As String +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{SvgImage}" +Public Const _Similar As String = "ReportTextLabel" +Public Data As SvgImage +Property Image As SvgImage +Property Stretch As Integer +Property Alignment As Integer +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + 'Dim h As Float = IIf(Me._Height > AvailableH, AvailableH, Me._Height) + 'Dim w As Float = IIf(Me._Width > AvailableW, AvailableW, Me._Width) + + Dim hPic As SvgImage + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalWidth, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + ReportUnits.UnitToCm(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Width, Me.Padding._Top + ReportUnits.UnitToCm(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + 'Dim hBrush As PaintBrush + Dim hPic As SvgImage + + Dim w, h As Float + + + ix = x + hControl.RealLeft '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + 'hBrush = Paint.Image(hpic) + + If $iStretchMode = Report.Fill Then + iX += ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left) + iY += ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + ' hBrush.Translate(ix, iy - 1) + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + hpic.Width = w + hpic.Height = h + Paint.MoveTo(ix, iy) + hpic.Paint + 'Paint.Rectangle(ix, iy, w, h) + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hPic.Width >= hPic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + Endif + Endif + + Else + w = hPic.Width + h = hPic.Height + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Bottom) - h + End Select + 'hBrush.Translate(ix, iy - 1) + hpic.Width = W + hPic.Height = H + Paint.MoveTo(iX, iY) + hpic.Paint + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + 'Paint.Rectangle(ix, iy, w, h - 1) + Endif + +End + +Private Function Image_Read() As SvgImage + + Return $hPic + +End + +Private Sub Image_Write(Value As SvgImage) + + $hPic = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End diff --git a/comp/src/gb.report/.src/ReportTextLabel.class b/comp/src/gb.report/.src/ReportTextLabel.class new file mode 100644 index 00000000..f5e11372 --- /dev/null +++ b/comp/src/gb.report/.src/ReportTextLabel.class @@ -0,0 +1,97 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Alignment{Align.*}" +Public Const _Similar As String = "ReportControl" +Public Const _DefaultEvent As String = "Data" +Property Text As String +Property Alignment As Integer + +Private $sText As String +Private $iAlignment As Integer = Align.TopNormal + + +Public Data As String +Event Data(Index As Integer) + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + Dim fTextHeight As Float + Dim sTmp As String + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalWidth, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If Not $sText Then + Raise Data(DataIndex) + sTmp = Data + Else + sTmp = $sText + Endif + 'La largeur est fixée par l'utilisateur + 'Calcule de la hauteur + If Not IsNull(Me.Font) Then Paint.Font = Me.Font + fTextHeight = ReportUnits._PixelsToReportUnits(Paint.RichTextExtents(sTmp, ReportUnits._ReportUnitsToPixels(hMyHints.Width)).Height) + hMyHints.Height = Max(hMyHints.Height, Me.Border._Top + Me.Padding._Top + fTextHeight + Me.Padding._Bottom + Me.Border._Bottom) + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iX, iY, iW, iH As Integer + Dim sTmp As String + + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + + If Not $sText Then + Raise Data(DataIndex) + sTmp = Data + Else + sTmp = $sText + Endif + + 'Set the Font if it is initialized + If Not IsNull(Me.Font) Then Paint.Font = Me.Font + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + iH = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + 'Paint.Rectangle(iX, iY, iW, iH) + 'Paint.Clip + Paint.RichText(sTmp, iX, iY, iW, iH, $iAlignment) + Paint.Fill + 'Paint.ResetClip + +End diff --git a/comp/src/gb.report/.src/ReportVBox.class b/comp/src/gb.report/.src/ReportVBox.class new file mode 100644 index 00000000..1cb254c3 --- /dev/null +++ b/comp/src/gb.report/.src/ReportVBox.class @@ -0,0 +1,33 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,ForceNewPage" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "V" + +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + +End + +Public Sub _Free() + +End + +' Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint +' +' 'Print Me.Tag +' If Me.tag Then Print "Besoins de " & Me.Tag & ": ", AvailableW, AvailableH +' Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight) +' +' +' End +' +' +' Public Sub _SetChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, bInFixed As Boolean) +' If Me.Tag Then Print "Disponibilité pour " & Me.Tag & ": H= " & H & " W= " & W +' Super._SetChildGeometry(x, y, w, h, ContPage, bInFixed) +' +' End diff --git a/comp/src/gb.report/.src/ReportVPanel.class b/comp/src/gb.report/.src/ReportVPanel.class new file mode 100644 index 00000000..fe439b23 --- /dev/null +++ b/comp/src/gb.report/.src/ReportVPanel.class @@ -0,0 +1,336 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportVBox" + +Public Sub _new() + + Super._Arrangement = Arrange.Column + +End + +Public Sub _Free() + + Super._Free + +End + +Public Sub _SetCChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageColumns As New TPageColumn[] + Dim PageColumn As New TPageColumn + Dim fMaxWidth As Float + Dim fColPos As Float + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As TSizeHint ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim fTH As Float ''Hauteur restante + Dim fSpc As Float ''Taille d'un espace + 'Dim X, Y As Float ''Position Haut gauche de départ + Dim hTItem As TControl ''Un objet virtuel + Dim oChild As Object ''Un objet gambas générique + Dim fExp As Float ''taille des objets étendus + Dim iNExp As Integer ''Nombre d'objets étendus + Dim ftmpHeight As Float ''Tampon pour le calcul de la taille répartie + Dim ftmpY As Float ''Curseur temporaire de position haute + Dim fX, fY As Float ''Tampon de position + Dim i, j As Integer ''Des indexs + Dim bExitLoop As Boolean ''Flag de sortie de traitement + 'Dim iPrevIndex As Integer + Dim bForceNewPage As Boolean ''Flag d'anticipation de sortie 1 par page + Dim iPreIndex As Integer + 'Détermination de la taille d'un espace + 'et prise en compte de la taille relative + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) + + 'Retrait du padding et des bordures de la hauteur de travail + H = H - Me.Padding._Top - Me.Border._Top - Me.Border._Bottom - Me.Padding._Bottom + + 'Retrait du padding et des bordures a la largeur de travail + W = W - Me.Padding._Left - Me.Border._Left - Me.Border._Right - Me.Padding._Right + + 'Positioinnement du curseur en haut a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'La hauteur restante est initialisée avec la hauteur de travail + fTH = H + fColPos = X + 'aColX.Add(fNextColPos) + '***************************************************************************************** + 'On enumere tous les enfants a la recherche des éléments fixes + 'Si des éléments fixes sont trouvés alors leur taille est déduite de la taille restante. + For i = 0 To Me.Children.Max + 'On récupère l'enfant + hchild = Me.Children[i] + 'Si l'enfant est fixe alors on le traite. + If hchild.Fixed Then + ' hchild._Index = TCont.Index + 'on commence par récupérer la taille de l'objet + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + 'On retire la taille de l'objet a la hau'teur restante + 'si l'objet n'est pas ignoré par l'agencement + If Not hchild.Ignore Then fTH = fTH - hChildHints.Height - fSpc + + + 'Si l'énumération n'est pas encore arrivée a l'index sauvegardé + 'alors on ajoute directement l'élément a la page + If i < Me._CurItem Then + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + + hTItem.Index = TCont.Index + 'L'ajouter a la page + PageColumn.TCtrls.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Endif + Next + + + + + '********************************************************************************* + 'On parcour a présent le reste des éléments + For i = Me._CurItem To Me.Children.Max + 'On récupère l'enfant + hChild = Me.Children[i] + 'Je traite ici la boucle de clonage + 'On définit le point de départ + 'If ContPage = 1 And hchild.Tag = "**" Then Stop + j = hChild._DataIndex + Do + '## Début boucle répétition + + 'On fixe l'index de données des enfants si + 'on est le duplicateur + If hChild._IsContainer And If hChild._count > 1 Then + iPreIndex = j + Else + iPreIndex = TCont.Index + Endif + 'If hchild.tag = "**" Then Print iPreIndex + 'If iPreIndex = 9 Then Stop + 'on récupère la taille de l'enfant + + hChildHints = hchild._GetSizeHints(W, fTH, fMaxWidth, H, iPreIndex) + fMaxWidth = Min(Max(fMaxWidth, hChildHints.Width), W) + 'Les éléments fixes ont déja été traités on ne tient donc pas compte de leur hauteur + 'car elle a déja été déduite de l'espace restant. De meme on ignore les objets flottants (ignore=true) + If Not hchild.Fixed And If Not hchild.Ignore Then + + 'Si l'élément ne loge pas dans la place restante ou + 'si la place restante est insuffisante + 'on provoque la sortie en fin de boucle + If (fTH - hChildHints.Height) < 0 Or If fTH <= 0 Or bForceNewPage Then + ' If hchild.Tag = "*" Then Stop + + + aPageColumns.Add(PageColumn) + PageColumn.Exp = fExp + PageColumn.NExp = iNExp + PageColumn.X = fColPos + fColPos += fMaxWidth + fspc + PageColumn.TH = fTh + + iNExp = 0 + fExp = 0 + + fTH = H + PageColumn = New TPageColumn + + 'Si la nouvelle colonne ne loge pas on arrête la + If fColPos + fMaxWidth > x + W Then + bForceNewPage = False + hchild._DataIndex = j + + 'If Not bForceNewPage Then + bExitLoop = True + Break + Else + Continue + End If + Endif + + 'On déduit la taille de l'objet courant (et l'espace suivant) + fTH = fTH - hChildHints.Height - fspc + + Endif + 'L'objet loge dans la page, on génère donc une représentation de celui-ci + 'c'est a dire un objet virtuel pointant vers celui-ci + 'et apportant les informations nécéssaire au layout final + 'on génère l'objet virtuel + + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'If hchild.Tag = "head" Then Stop + 'On associe l'index de donnée + hTItem.Index = iPreIndex + 'TItem.Index = IIf(hchild._count > 0, j, TCont.Index) + 'On l'ajoute a la page + PageColumn.TCtrls.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + + 'If hChildHints.NotFinished Then hchild._DataIndex = j + 'un élément fixe ou ignore ne peut être répété + If hchild.Fixed Or hchild.Ignore Then Break + + 'Sachant que hChild._count peut être à -1 on le considère dans ce cas la comme étant a 0 + 'si j est égale au compte alors on quitte la boucle + If j >= Max(hchild._Count - 1, 0) Then + If Not hChildHints.NotFinished Then hChild._DataIndex = 0 + Break + Endif + + 'sinon on incrémente le compte a condition que le dernier enfant aie finit d'afficher ses enfants + If Not hChildHints.NotFinished Then Inc j + + 'On prévoie une sortie de boucle si l'enfant demande un affichage 1 par page + If hchild.ForceNewPage Then bForceNewPage = True + + Loop + 'Next '## fin de la boucle de répétition + 'Si la sortie anticipé est demandée alors on sort de la boucle + If bExitLoop Then Break + If i < Me.Children.Max Then + If Me.Children[i + 1] Is ReportPageBreak Then + bForceNewPage = True + Inc Me._CurItem + Inc i + Endif + Endif + 'Tout les objets on été répété alors on remet l'index à 0 + 'hchild._DataIndex = 0 + 'On incrémente le compteur des éléments traités + Inc Me._CurItem + Next + + If (H - fTH) > 0 Then + fTH += fspc 'Heu?????? + Endif + + '*************************************************************************** + 'Pour tous les éléments fixes jusqu'à la fin du document + For i = Max(Me._CurItem, 0) To Me.Children.Max + hchild = Me.Children[i] + If hchild.Fixed Then + hChildHints = hchild._GetSizeHints(W, fTH, W, H, iPreIndex) + 'fNextColPos = Max(fNextColPos, fNextColPos + hChildHints.Width) + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'On l'ajoute a la page + PageColumn.TCtrl.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Next + PageColumn.Exp = fExp + PageColumn.Nexp = iNExp + PageColumn.TH = fTH + aPageColumns.Add(PageColumn) + PageColumn.X = fColPos + '****************************************************************************** + 'A présent tous les éléments pouvant être placé sur la page ont été marqué. + 'On peut donc procéder a la mise en forme de ceux-ci + 'k = 0 + For Each PageColumn In aPageColumns + ftmpY = Y 'On définit la position de départ + If PageColumn.NExp Then + fExp = (PageColumn.Exp + PageColumn.TH) / PageColumn.NExp + Endif + + For i = 0 To PageColumn.TCtrls.Max + 'on récupère l'objet virtuel + hTItem = PageColumn.TCtrls[i] + 'on récupère l'instance de l'objet réel + ochild = hTItem.Ctrl + + 'Si l'objet est étendu alors on calcul sa hauteur + 'Un objet flottant ne peut pas être étendu + If Not oChild.Ignore And If ochild.Expand Then + 'Calcul de la taille répartie + ftmpHeight = fExp + Else + 'Si l'objet n'est pas étendu alors sa taille est celle demandée par celui-ci + ftmpHeight = hTItem.SizeHint.Height + Endif + 'oChild._Index = hTItem.Index + 'Traitement des objets ignoré + 'Les propriété de taille sont celle fournie par l'objet + If oChild.Ignore Then + 'Calcul de la position de l'objet + 'si sa position est relative (%) alors on fait le ratio a partir de la largeur ou de la hauteur + 'sinon on utilise la position fournie par l'objet + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + 'Fixer la position de l'objet + hTItem._SetGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + + Else + + 'Fixer la position de l'objet + hTItem._SetGeometry(PageColumn.X, fTmpY, fMaxWidth, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(PageColumn.X, fTmpY, fMaxWidth, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + 'on incrémente la position verticale de l'objet + 'pour définir sa place sur la page relativement aux autres éléments + fTmpY += ftmpHeight + fspc + + Endif + + 'Si l'objet est un conteneur alors il faut vérifier si il a finit d'être traité + + If oChild Is ReportContainer Then + 'If oChild._NotFinished Then Me.NotFinished = True + 'Si son traitement n'est pas terminé alors on trouve + 'sa position dans la liste des enfant et on l'assigne a l'index de traitement + 'si sa position est > a l'index courant + If ochild._CurItem <= oChild.Children.Max Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'Si on est dans la lignée d'un conteneur fixe on incrémente pas la lecture des enfants + 'En clair ce sont toujours les mêmes éléments qui apparaitrons + If bInFixed Then ochild._CurItem = 0 + Endif + Next + 'Inc k + aPageItems.Insert(PageColumn.TCtrls) + Next + 'On ajoute cette page au dossier du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End \ No newline at end of file diff --git a/comp/src/gb.report/.src/Tests/Old/Report1.class b/comp/src/gb.report/.src/Tests/Old/Report1.class new file mode 100644 index 00000000..5be74ff1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report1.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub ReportLabel2_Data(Index As Integer) + + Last.data = "coucou\n c'est
    moi" + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report1.report b/comp/src/gb.report/.src/Tests/Old/Report1.report new file mode 100644 index 00000000..2051d2ae --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report1.report @@ -0,0 +1,55 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,68,64) + Count = 3 + Index = 0 + Text = ("#3") + { ReportLabel2 ReportLabel + #MoveScaled(3,4,33,8) + AutoResize = True + Background = ReportBrush["#FFFF00"] + } + Index = 1 + Text = ("#1") + { ReportHBox1 ReportHBox + #MoveScaled(3,1,58,16) + Height = "5cm" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#00FF00"] + { ReportLabel1 ReportLabel + #MoveScaled(7,4,40,9) + Font = Font["Bold,+10"] + Expand = True + Text = ("COUCOU") + Alignment = Align.Center + } + } + { ReportHBox2 ReportHBox + #MoveScaled(6,21,57,34) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["LinearGradient(0.84,0.6,0,0,[#000000,#FFFFFF],[0,1])"] + { ReportVBox1 ReportVBox + #MoveScaled(0,4,28,20) + Expand = True + } + { ReportVBox2 ReportVBox + #MoveScaled(33,3,18,24) + Fixed = True + Expand = True + { ReportHBox3 ReportHBox + #MoveScaled(3,2,12,14) + Expand = True + } + { ReportVBox3 ReportVBox + #MoveScaled(2,20,16,3) + Height = "60mm" + Background = ReportBrush["#FFFF00"] + } + } + } + Index = 2 + Text = ("#2") + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report10.class b/comp/src/gb.report/.src/Tests/Old/Report10.class new file mode 100644 index 00000000..9673987f --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report10.class @@ -0,0 +1,8 @@ +' Gambas class file + +Public Sub _new() + + ReportHBox2.DataCount = 200 + + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report10.report b/comp/src/gb.report/.src/Tests/Old/Report10.report new file mode 100644 index 00000000..f5e3d89b --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report10.report @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:20mm;Bottom:10mm;Left:25mm;Right:15mm"] + Paper = Printer.A3 + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,4,59,5) + Height = "1cm" + Fixed = True + { ReportLabel1 ReportLabel + #MoveScaled(1,1,17,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:6mm"] + Text = ("ReportLabel1") + } + { ReportLabel2 ReportLabel + #MoveScaled(21,1,14,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000"] + Text = ("ReportLabel2") + } + { ReportLabel3 ReportLabel + #MoveScaled(40,0,13,5) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopRightCorner:6mm"] + Text = ("ReportLabel3") + } + } + { ReportHBox2 ReportHBox + #MoveScaled(1,12,59,5) + Height = "1cm" + { ReportLabel4 ReportLabel + #MoveScaled(1,1,17,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Text = ("ReportLabel1") + } + { ReportLabel5 ReportLabel + #MoveScaled(21,1,14,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000"] + Text = ("ReportLabel2") + } + { ReportLabel6 ReportLabel + #MoveScaled(40,0,13,5) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Text = ("ReportLabel3") + } + } + { ReportLabel7 ReportLabel + #MoveScaled(3,49,60,4) + Fixed = True + Padding = ReportPadding["Top:10mm;Bottom:10mm;Left:10mm;Right:10mm"] + AutoResize = True + Text = ("Page $NPAGE") + Alignment = Align.Right + UseField = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report12.class b/comp/src/gb.report/.src/Tests/Old/Report12.class new file mode 100644 index 00000000..f816f33e --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report12.class @@ -0,0 +1,16 @@ +' Gambas class file + +Public Sub _New() + + ReportHBox1.DataCount = 100 + ReportLabel1.BackGround = ReportBrush.Color(Color.Red) + ReportLabel1.Brush = ReportBrush.Color(Color.Yellow) + +End + + +Public Sub ReportLabel1_Data(Index As Integer) + + Last.Data = Index + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report12.report b/comp/src/gb.report/.src/Tests/Old/Report12.report new file mode 100644 index 00000000..5f402eed --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report12.report @@ -0,0 +1,17 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Spacing = "1mm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(2,3,60,7) + Height = "1cm" + { ReportLabel1 ReportLabel + #MoveScaled(0,0,58,4) + Expand = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report13.class b/comp/src/gb.report/.src/Tests/Old/Report13.class new file mode 100644 index 00000000..d2ce3331 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report13.class @@ -0,0 +1,38 @@ +' Gambas class file + +Private hResut As Result +Private hResut2 As Result +Private ColResult As New Collection +Public Sub ReportLabel1_Data(Index As Integer) + + hResut.MoveTo(Index) + Last.Data = hResut!firstname + + +End + +Public Sub Report_Open() +Connections["Connection1"].Open +hResut = db.Exec("select distinct firstname from test") + + ReportVBox1.DataCount = hResut.Count + 'ReportVBox2.DataCount = 10 +End + +Public Sub ReportLabel2_Data(Index As Integer) +hResut2 = ColResult[ReportVBox1.DataIndex] +hResut2.MoveTo(Index) +Last.Data = hResut2!birth +Print index +Catch +End + +Public Sub ReportVBox1_BeforeData() + If Not ColResult.Exist(Last.DataIndex) Then + hResut.MoveTo(Last.DataIndex) + hResut2 = db.Exec("select * FROM test where firstname=&1", hResut!firstname) + ColResult[Last.DataIndex] = hResut2 + ReportVBox2.DataCount = hResut2.Count + Endif +Catch +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report13.report b/comp/src/gb.report/.src/Tests/Old/Report13.report new file mode 100644 index 00000000..4a117ceb --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report13.report @@ -0,0 +1,21 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportVBox1 ReportVBox + #MoveScaled(3,5,60,44) + { ReportLabel1 ReportLabel + #MoveScaled(2,1,31,3) + } + { ReportVBox2 ReportVBox + #MoveScaled(2,8,56,12) + { ReportLabel2 ReportLabel + #MoveScaled(0,2,28,3) + Brush = ReportBrush["#00FF00"] + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report14.class b/comp/src/gb.report/.src/Tests/Old/Report14.class new file mode 100644 index 00000000..13c1ce07 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report14.class @@ -0,0 +1,32 @@ +' Gambas class file + +'Static Public Type As String + +Public Sub _new(Optional sType As String = "mysql") + + Dim hResult As Result + Dim Rlbl As ReportLabel + Dim hConn As Connection + + hConn = Connections["Connection1"] + hConn.Type = sType + If hConn.Type = "sqlite" Then hConn.Host = User.Home + Try hConn.Open + If Not hConn.Opened Then + Message.Error(" You have to create the test database with the Database example.") + Return + Endif + + hResult = db.Limit(200).Find("test") + If Not hResult.Available Then Return + + For Each hResult + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Autoresize = True + Rlbl.Text = hResult!name & " " & hResult!firstname + Rlbl.Border.bottom.Width = "1px" + + Next + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report14.report b/comp/src/gb.report/.src/Tests/Old/Report14.report new file mode 100644 index 00000000..e83b2ebe --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report14.report @@ -0,0 +1,90 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,100,77) + Padding = ReportPadding["Top:1cm;Bottom:15mm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("Section 1") + { ReportHBox2 ReportHBox + #MoveScaled(0,0,67,14) + Height = "5cm" + Fixed = True + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#3398C3"] + { ReportSvgImage1 ReportSvgImage + #MoveScaled(1,1,14,12) + Width = "4cm" + Image = SvgImage.Load("gambas.svg") + } + { ReportLabel3 ReportLabel + #MoveScaled(20,2,43,10) + Brush = ReportBrush["LinearGradient(0.71,0.07,0.34,0.06,[#FFFF00,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["DejaVu Sans,Bold,+14"] + Expand = True + AutoResize = True + Text = ("Gambas") + Alignment = Align.Left + } + } + { ReportHBox1 ReportHBox + #MoveScaled(0,15,87,56) + Padding = ReportPadding["0cm"] + Expand = True + Border = ReportBorder["Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + { ReportVBox1 ReportVBox + #MoveScaled(1,1,25,47) + Width = "6cm" + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Background = ReportBrush["#DF6B00"] + { ReportLabel4 ReportLabel + #MoveScaled(3,2,20,4) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold"] + AutoResize = True + Text = ("All friends list !") + } + } + { ReportVBox2 ReportVBox + #MoveScaled(27,1,60,54) + Expand = True + Border = ReportBorder["Left:1mm #000000"] + Background = ReportBrush["#FFFFFF"] + { RVBCont ReportVBox + #MoveScaled(4,4,53,42) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Tag = "*" + Spacing = "1mm" + { ReportLabel1 ReportLabel + #MoveScaled(0,0,44,9) + Font = Font["Bitstream Charter,Bold,Italic,+2"] + AutoResize = True + Text = ("Gambas Report Demo") + Alignment = Align.Center + } + { ReportVBox3 ReportVBox + #MoveScaled(0,11,38,4) + Height = "1cm" + AutoResize = True + } + } + { ReportLabel2 ReportLabel + #MoveScaled(7,49,47,3) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold,+1"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000"] + Background = ReportBrush["#3398C3"] + Text = ("Page $PAGE on $NPAGE ") + Alignment = Align.Right + UseField = True + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report2.class b/comp/src/gb.report/.src/Tests/Old/Report2.class new file mode 100644 index 00000000..ea9925d3 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report2.class @@ -0,0 +1,20 @@ +' Gambas class file + +Public Sub _new() + + Dim Rlbl As ReportLabel + Dim i As Integer + 'Report.Debug = True + For i = 0 To 200 + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Text = "Ligne " & i + Rlbl.Autoresize = True + Rlbl.Border.bottom.Width = "1px" + Rlbl.Padding.Top = "3 mm" + Rlbl.Padding.Bottom = "1mm" + Rlbl.Padding.Left = "2 mm" + + Next + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report2.report b/comp/src/gb.report/.src/Tests/Old/Report2.report new file mode 100644 index 00000000..0aad391a --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report2.report @@ -0,0 +1,19 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,1,61,8) + Height = "10cm" + Fixed = True + Expand = True + Background = ReportBrush["#007FFF"] + } + { RVBCont ReportVBox + #MoveScaled(2,11,56,26) + Expand = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report3.class b/comp/src/gb.report/.src/Tests/Old/Report3.class new file mode 100644 index 00000000..8ea03ec7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report3.class @@ -0,0 +1,20 @@ +' Gambas class file + +Public Sub _new() + + ReportHBox1.DataCount = 300 + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + 'Print "coucou" + Last.data = Index + +End + +Public Sub ReportLabel2_Data(Index As Integer) + + Last.data = Index + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report3.report b/comp/src/gb.report/.src/Tests/Old/Report3.report new file mode 100644 index 00000000..59d12d97 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report3.report @@ -0,0 +1,42 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Paper = Printer.A3 + Index = 0 + Text = ("") + { ReportHBox2 ReportHBox + #MoveScaled(1,1,61,7) + Height = "18mm" + Fixed = True + Background = ReportBrush["#FFFF00"] + } + { ReportHBox1 ReportHBox + #MoveScaled(1,9,59,6) + Height = "15mm" + { ReportLabel1 ReportLabel + #MoveScaled(0,1,26,5) + Expand = True + } + { ReportLabel2 ReportLabel + #MoveScaled(28,0,30,6) + Font = Font["Bold,+5"] + Expand = True + Alignment = Align.Center + } + } + { ReportPanel1 ReportPanel + #MoveScaled(1,16,60,4) + Fixed = True + Expand = True + Background = ReportBrush["#00FF00"] + Arrangement = Arrange.None + } + { ReportHBox3 ReportHBox + #MoveScaled(0,21,61,7) + Height = "18mm" + Fixed = True + Background = ReportBrush["#FFFF00"] + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report4.class b/comp/src/gb.report/.src/Tests/Old/Report4.class new file mode 100644 index 00000000..e9023949 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report4.class @@ -0,0 +1,46 @@ +' Gambas class file + +Private $hres As Result + +Public Sub _new() + + 'Report.Debug = True + + Try Connections["Connection2"].Open + $hres = db.Exec("SELECT fc.cd_sc as codesoc, fc.int_sc as intsoc, fcc.compte_cc as compte, fcc.intitule_cc as intitule From Fiches_Comptes as fcc " + "Left join Fiches_Societes as fc on fc.cd_sc=1") + ReportHBox4.DataCount = $hres.Count + +End + +Public Sub lblCpte_Data(Index As Integer) + + $hres.MoveTo(Index) + Last.data = $hres!compte + +End + +Public Sub lblcpteLab_Data(Index As Integer) + + $hres.MoveTo(Index) + Last.data = $hres!intitule + +End + +Public Sub lblSoc_Data(Index As Integer) + + Last.data = $hres!codesoc & " " & $hres!intsoc + +End + +Public Sub lblDate_Data(Index As Integer) + + Last.data = Format(Now(), "dddd dd mmmm yyyy") + +End + +Public Sub ReportImage1_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report4.report b/comp/src/gb.report/.src/Tests/Old/Report4.report new file mode 100644 index 00000000..e41447cf --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report4.report @@ -0,0 +1,96 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,92) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportLabel3 ReportLabel + #MoveScaled(0,0,58,4) + Height = "2cm" + Fixed = True + Font = Font["Bold,+6"] + Tag = "**" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:6mm;BottomLeftCorner:7mm"] + Background = ReportBrush["LinearGradient(0.7,0,0.7,1,[#FFCFBF,#FFFFFF],[0,1])"] + Text = ("IMPRESSION DU PLAN COMPTABLE") + Alignment = Align.Center + } + { ReportPanel1 ReportVBox + #MoveScaled(2,7,50,69) + Padding = ReportPadding["Bottom:2cm;Left:2cm;Right:2cm"] + Expand = True + Tag = "Contenu1" + { lblSoc ReportLabel + #MoveScaled(1,2,53,4) + Height = "2cm" + Fixed = True + Font = Font["Bold,12"] + } + { ReportHBox1 ReportHBox + #MoveScaled(1,14,45,4) + Height = "1cm" + Fixed = True + Background = ReportBrush["#F7DFFF"] + { ReportLabel1 ReportLabel + #MoveScaled(1,0,14,4) + Width = "20%" + Font = Font["Bold,12"] + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000"] + Text = ("Code") + Alignment = Align.Center + } + { ReportLabel2 ReportLabel + #MoveScaled(20,0,14,4) + Font = Font["Bold,12"] + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Intitulé") + Alignment = Align.Center + } + } + { VCont ReportVBox + #MoveScaled(1,20,45,39) + AutoResize = True + Tag = "ListeElements" + { ReportHBox4 ReportHBox + #MoveScaled(1,2,44,7) + Height = "5mm" + { lblCpte ReportLabel + #MoveScaled(8,1,18,4) + Width = "20%" + Padding = ReportPadding["Left:3mm"] + Tag = "*" + } + { lblcpteLab ReportLabel + #MoveScaled(27,0,18,4) + Width = "20%" + Padding = ReportPadding["Left:3mm"] + Expand = True + Border = ReportBorder["Left:1px #000000"] + } + } + } + } + { ReportHBox2 ReportHBox + #MoveScaled(0,77,61,4) + Fixed = True + AutoResize = True + { lblDate ReportLabel + #MoveScaled(0,1,16,4) + Width = "5cm" + } + { ReportHBox3 ReportHBox + #MoveScaled(9,1,24,4) + Expand = True + } + { ReportLabel4 ReportLabel + #MoveScaled(47,1,13,3) + Width = "3cm" + AutoResize = True + Text = ("Page $PAGE sur $NPAGE") + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report5.class b/comp/src/gb.report/.src/Tests/Old/Report5.class new file mode 100644 index 00000000..f5baf4d7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report5.class @@ -0,0 +1 @@ +' Gambas class file diff --git a/comp/src/gb.report/.src/Tests/Old/Report5.report b/comp/src/gb.report/.src/Tests/Old/Report5.report new file mode 100644 index 00000000..f2bdbb65 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report5.report @@ -0,0 +1,85 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,95) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(4,2,56,6) + Height = "12mm" + Expand = True + { ReportLabel9 ReportLabel + #MoveScaled(2,1,20,5) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FFFF00"] + } + { ReportHBox2 ReportHBox + #MoveScaled(30,2,24,8) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FFFF00"] + } + } + { ReportPageBreak1 ReportPageBreak + #MoveScaled(5,10,60,5) + } + { ReportLabel1 ReportImage + #MoveScaled(1,12,59,8) + Height = "7cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel2 ReportLabel + #MoveScaled(2,18,59,8) + Height = "2cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["LinearGradient(1,0,0.28,0.35,[#00FF00,#FFFFFF],[0,1])"] + } + { ReportLabel4 ReportImage + #MoveScaled(3,23,59,8) + Height = "6cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportPageBreak2 ReportPageBreak + #MoveScaled(2,31,59,2) + } + { ReportLabel5 ReportLabel + #MoveScaled(2,33,59,8) + Height = "2cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportPageBreak3 ReportPageBreak + #MoveScaled(2,42,58,3) + } + { ReportLabel3 ReportLabel + #MoveScaled(1,44,59,8) + Height = "2cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel6 ReportLabel + #MoveScaled(2,52,59,8) + Height = "10cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel7 ReportLabel + #MoveScaled(2,62,59,8) + Height = "2cm" + Expand = True + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel8 ReportLabel + #MoveScaled(2,72,59,8) + Height = "2cm" + Expand = True + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report6.class b/comp/src/gb.report/.src/Tests/Old/Report6.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report6.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report/.src/Tests/Old/Report6.report b/comp/src/gb.report/.src/Tests/Old/Report6.report new file mode 100644 index 00000000..e4975fe7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report6.report @@ -0,0 +1,17 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportPanel1 ReportPanel + #MoveScaled(4,5,59,31) + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + { ReportLabel1 ReportLabel + #MoveScaled(2,4,53,20) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Text = ("ReportLabel1") + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report7.class b/comp/src/gb.report/.src/Tests/Old/Report7.class new file mode 100644 index 00000000..2aa731de --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report7.class @@ -0,0 +1,29 @@ +' Gambas class file + +Public Sub _new() + + 'Report.Debug = True + ' ReportPanel2.Border.RoundCorner.TopLeft = "5mm/5mm" + ' ReportPanel2.Border.RoundCorner.BottomLeft = "5mm/5mm" + ' ReportPanel2.Border.RoundCorner.TopRight = "5mm/5mm" + ' ReportPanel2.Border.RoundCorner.BottomRight = "5mm/5mm" + +End + +Public Sub ReportImage1_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End + +Public Sub ReportImage2_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End + +Public Sub ReportImage3_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report7.report b/comp/src/gb.report/.src/Tests/Old/Report7.report new file mode 100644 index 00000000..45cf606d --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report7.report @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportPanel1 ReportHBox + #MoveScaled(0,5,61,10) + Height = "4cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + { ReportPanel3 ReportPanel + #MoveScaled(1,2,9,8) + Expand = True + } + { ReportPanel2 ReportPanel + #MoveScaled(10,0,41,10) + Width = "10cm" + AutoResize = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:1cm;TopRightCorner:1cm;BottomRightCorner:1cm;BottomLeftCorner:1cm"] + Arrangement = Arrange.Horizontal + { ReportImage1 ReportImage + #MoveScaled(1,0,11,9) + Width = "3cm" + Height = "6mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Alignment = Align.Center + } + { ReportImage3 ReportImage + #MoveScaled(26,0,11,9) + Width = "3cm" + Height = "6mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Left:1mm #000000"] + Alignment = Align.Center + } + { ReportImage2 ReportImage + #MoveScaled(14,0,11,9) + Width = "3cm" + Height = "6mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Left:1mm #000000"] + Alignment = Align.Center + } + } + { ReportPanel4 ReportPanel + #MoveScaled(52,1,9,9) + Expand = True + } + } + { ReportLabel1 ReportLabel + #MoveScaled(5,18,57,13) + Height = "2cm" + Font = Font["Bold,+6"] + Text = ("coucou") + Alignment = Align.Center + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report8.class b/comp/src/gb.report/.src/Tests/Old/Report8.class new file mode 100644 index 00000000..d9c700c7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report8.class @@ -0,0 +1,56 @@ +' Gambas class file + +Private hResult1 As Result +Private hResult2 As Result + +Public Sub _new() + + Connections["Connection1"].Open + 'Report.Debug = True + hResult1 = db.Exec("select distinct firstname from test") + + ReportPanel1.DataCount = hResult1.Count + 'ReportHBox1.DataCount = 4 + 'ReportHBox1.Border.RoundCorner.TopRight = "5mm 5mm" + 'ReportHBox1.Border.RoundCorner.BottomRight = "5mm/5mm" +'Report.Debug = True +ReportLabel3.BoxShadow.XOffset = "1mm" +ReportLabel3.BoxShadow.YOffset = "1mm" +ReportLabel3.BoxShadow.Blur = "1mm" + + + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + hResult2.MoveTo(Index) + Last.data = hResult2!name + Print "FillData = " & hResult1!firstname +End + +Public Sub ReportPanel1_BeforeData() +If hResult1.Max < ReportPanel1.DataIndex Then + Print "error" & ReportPanel1.DataIndex + Return +Endif + hResult1.MoveTo(ReportPanel1.dataindex) + Print "BeforeData = " & hResult1!firstname + hResult2 = db.Exec("Select * From test where firstname=&1 LIMIT 1,20", hResult1!firstname) + ReportHBox1.DataCount = hResult2.Count + +End + +Public Sub ReportLabel2_Data(Index As Integer) + hResult1.MoveTo(Index) + Last.data = hResult1!firstname + + 'Print hResult1!firstname, Index + +End + +Public Sub Report_Open() + + + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report8.report b/comp/src/gb.report/.src/Tests/Old/Report8.report new file mode 100644 index 00000000..68d32d17 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report8.report @@ -0,0 +1,79 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,88) + Padding = ReportPadding["Top:15mm;Bottom:2cm;Left:2cm;Right:2cm"] + Tag = "\"toto\"" + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportLabel5 ReportLabel + #MoveScaled(2,0,61,4) + Brush = ReportBrush["#9F9F9F"] + Fixed = True + AutoResize = True + Text = ("List of all my friends") + Alignment = Align.Right + } + { ReportLabel3 ReportLabel + #MoveScaled(2,3,60,18) + Font = Font["Bold,+10"] + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #9F9F9F;Bottom:1mm #9F9F9F;Left:1mm #9F9F9F;Right:1mm #9F9F9F;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["#1FFF8F"] + Text = ("List Of My Friends") + Alignment = Align.Center + } + { ReportPanel2 ReportVBox + #MoveScaled(0,23,65,54) + Expand = True + Tag = "Boite 1" + Spacing = "1cm" + { ReportPanel1 ReportVBox + #MoveScaled(7,3,64,45) + Width = "4cm" + Height = "5cm" + AutoResize = True + Tag = "Boite 2" + Spacing = "1mm" + ForceNewPage = True + { ReportLabel2 ReportLabel + #MoveScaled(2,1,54,8) + Fixed = True + Font = Font["Bold,+3"] + Padding = ReportPadding["Left:5mm"] + AutoResize = True + Tag = "head" + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000;TopLeftCorner:3mm;TopRightCorner:3mm;BottomRightCorner:3mm;BottomLeftCorner:3mm"] + Background = ReportBrush["#1FFF8F"] + } + { ReportHBox1 ReportHBox + #MoveScaled(2,11,71,31) + Width = "5cm" + Height = "2cm" + Padding = ReportPadding["Left:5mm"] + AutoResize = True + Tag = "Boite 3" + Background = ReportBrush["#FFFFFF"] + { ReportLabel1 ReportLabel + #MoveScaled(5,5,47,17) + Font = Font["+1"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Background = ReportBrush["#DFDF6F"] + } + } + } + } + { ReportLabel4 ReportLabel + #MoveScaled(0,79,62,4) + Fixed = True + AutoResize = True + Text = ("Page $PAGE / $NPAGE") + Alignment = Align.Right + UseField = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/myReport1.class b/comp/src/gb.report/.src/Tests/Old/myReport1.class new file mode 100644 index 00000000..dd781dce --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport1.class @@ -0,0 +1,43 @@ +' Gambas class file + +Private hCon As Connection = Connections["Connection1"] +Private hResult As Result + +Public Sub _new() + + 'Dim hresult As Result + 'Report.debug = True + ' ReportLabel8.Fixed = True + ' ReportHBox4.Fixed = True + 'ReportHBox3._Count = 200 + ' ReportLabel2.Fixed = True + ' hCon.Type = "sqlite" + ' hCon.Host = User.Home + ' hCon.Name = "test" + hresult = db.Exec("SELECT * FROM test") + 'Print hresult.Count + ReportHBox1.DataCount = hresult.Count + 'ReportLabel1.Brush = ReportBrush["&h00FF00&"] + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + hresult.MoveTo(Index) + Last.data = hresult!name + +End + +Public Sub ReportLabel6_Data(Index As Integer) + + hresult.MoveTo(Index) + Last.data = hresult!firstname + +End + +Public Sub ReportLabel7_Data(Index As Integer) + + hresult.MoveTo(Index) + Last.data = hresult!birth + +End diff --git a/comp/src/gb.report/.src/Tests/Old/myReport1.report b/comp/src/gb.report/.src/Tests/Old/myReport1.report new file mode 100644 index 00000000..81683e3d --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport1.report @@ -0,0 +1,142 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,71,97) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "2cm" + Count = 3 + Index = 0 + Text = ("First Page") + { ReportVBox2 ReportVBox + #MoveScaled(2,3,51,11) + Expand = True + } + { ReportLabel9 ReportLabel + #MoveScaled(4,16,48,13) + Font = Font["Bitstream Charter,Bold,+9"] + Padding = ReportPadding["Top:6mm;Bottom:6mm;Left:6mm;Right:6mm"] + AutoResize = True + Border = ReportBorder["Top:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);Bottom:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);Left:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);Right:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);TopLeftCorner:8mm;TopRightCorner:8mm;BottomRightCorner:8mm;BottomLeftCorner:8mm"] + Text = ("Report About") + Alignment = Align.Center + } + { ReportLabel10 ReportLabel + #MoveScaled(6,35,43,7) + Font = Font["Bitstream Charter,+9"] + AutoResize = True + Border = ReportBorder["Top:4mm #FFAF5F;Bottom:4mm #FFAF5F;Left:4mm #FFAF5F;Right:4mm #FFAF5F;TopLeftCorner:4mm;TopRightCorner:4mm;BottomRightCorner:4mm;BottomLeftCorner:4mm"] + Text = ("All my friends") + Alignment = Align.Center + } + { ReportVBox3 ReportVBox + #MoveScaled(2,47,52,11) + Expand = True + Border = ReportBorder["Top:2px #000000;Bottom:2px #000000;Left:2px #000000;Right:2px #000000"] + } + Index = 1 + Text = ("Friend Table") + { ReportLabel11 ReportLabel + #MoveScaled(5,3,57,2) + Height = "15mm" + Brush = ReportBrush["#BFBFBF"] + Fixed = True + Text = ("Report about all my frends") + Alignment = Align.TopRight + } + { ReportLabel2 ReportLabel + #MoveScaled(5,6,45,5) + Height = "3cm" + Font = Font["Bitstream Charter,+7"] + Text = ("Test Database") + Alignment = Align.Center + } + { ReportVBox1 ReportVBox + #MoveScaled(5,13,45,38) + Expand = True + AutoResize = True + Tag = "Boite 1" + { ReportVBox4 ReportVBox + #MoveScaled(3,12,41,24) + AutoResize = True + Tag = "Boite 2" + { ReportHBox2 ReportHBox + #MoveScaled(0,0,37.7143,5.1429) + Height = "15mm" + Fixed = True + AutoResize = True + { ReportLabel3 ReportLabel + #MoveScaled(1,1,10.2857,3.4286) + Width = "5 cm" + Height = "10mm" + Font = Font["Bitstream Charter,Bold,16"] + AutoResize = True + Text = ("Name") + Alignment = Align.Center + } + { ReportLabel4 ReportLabel + #MoveScaled(12,1,10.2857,3.4286) + Width = "5 cm" + Height = "10mm" + Font = Font["Bitstream Charter,Bold,16"] + Padding = ReportPadding["0.2 mm"] + AutoResize = True + Text = ("FirstName") + Alignment = Align.Center + } + { ReportLabel5 ReportLabel + #MoveScaled(24.8571,0.8571,10.2857,3.4286) + Width = "5 cm" + Height = "10mm" + Font = Font["Bitstream Charter,Bold,16"] + Padding = ReportPadding["0.2 mm"] + Expand = True + AutoResize = True + Text = ("Birth") + Alignment = Align.Center + } + } + { ReportHBox1 ReportHBox + #MoveScaled(1,8,38,4) + Height = "10mm" + Tag = "Boite 3" + { ReportLabel1 ReportLabel + #MoveScaled(0,0,10.2857,3.4286) + Width = "5 cm" + Font = Font["12"] + Padding = ReportPadding["0.2 mm"] + Alignment = Align.Center + UseField = True + } + { ReportLabel6 ReportLabel + #MoveScaled(11,0,10.2857,3.4286) + Width = "5 cm" + Font = Font["12"] + Padding = ReportPadding["0.2 mm"] + Alignment = Align.Center + } + { ReportLabel7 ReportLabel + #MoveScaled(24,0,10.2857,3.4286) + Width = "5 cm" + Font = Font["12"] + Padding = ReportPadding["2.2mm"] + Expand = True + Format = "dd/mm/aaaa" + Alignment = Align.Right + } + } + } + } + { ReportLabel8 ReportLabel + #MoveScaled(2,51,46,3) + Height = "6mm" + Fixed = True + Padding = ReportPadding["2mm"] + AutoResize = True + Text = ("$PAGE/$NPAGE") + Alignment = Align.Right + UseField = True + } + Index = 2 + Text = ("") + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/myReport2.class b/comp/src/gb.report/.src/Tests/Old/myReport2.class new file mode 100644 index 00000000..3588055e --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport2.class @@ -0,0 +1,85 @@ +' Gambas class file + +Private cList As New Collection[] +Private cFactHead As New Collection[] + +Public Sub _new() + + Dim cLine As Collection + Dim i As Integer + + 'Report.Debug = True + + cLine = New Collection + cLine["nom"] = "Berton Toto " + cLine["adresse"] = "La belle route" + cLine["CP"] = "17520" + cLine["ville"] = "Archiac" + cLine["TOTALHT"] = 0 + cFactHead.Add(cLine) + 'Me.Data = cFactHead + For i = 0 To 12 + cLine = New Collection + cLine["designation"] = "Patin a roulette" + cLine["PU"] = 19.90 + cLine["Q"] = 1 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Cuilliere en bois" + cLine["PU"] = 8.90 + cLine["Q"] = 1 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Lapin en peluche" + cLine["PU"] = 19.90 + cLine["Q"] = 5 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Cloche de noël" + cLine["PU"] = 20 + cLine["Q"] = 10 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Cheque en bois" + cLine["PU"] = 1000 + cLine["Q"] = 1 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Pense bête" + cLine["PU"] = 2 + cLine["Q"] = 3 + cList.Add(cLine) + Next + 'ReportHBox3.Data = cList + 'ReportHBox12.Data = cList + 'Compute Summaries + i = 0 + For Each cLine In cList + Inc i + cLine["id"] = i + cLine["THT"] = Round(cLine["PU"] * cLine["Q"], -2) + cFactHead[0]["TOTALHT"] += cLine["THT"] + + Next + ReportHBox3.DataCount = cList.Count + +End + +Public Sub ligne_Data(Index As Integer) + + If Not Last.tag Then Return + Last.data = cList[Index][Last.tag] + +End + +Public Sub entete_Data(Index As Integer) + + Last.data = cFactHead[Index][Last.tag] + +End + +Public Sub ReportVBox1_BeforeArrange() + + Debug "Before Arrange" + +End diff --git a/comp/src/gb.report/.src/Tests/Old/myReport2.report b/comp/src/gb.report/.src/Tests/Old/myReport2.report new file mode 100644 index 00000000..643c85f7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport2.report @@ -0,0 +1,228 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,106,113) + Padding = ReportPadding["1cm"] + Spacing = "1cm" + Index = 0 + Text = ("Section 1") + { ReportVBox2 ReportVBox + #MoveScaled(4,4,80,42) + Fixed = True + AutoResize = True + { ReportLabel9 ReportLabel + #MoveScaled(0,0,68,7) + Fixed = True + Font = Font["Bitstream Charter,+11"] + AutoResize = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Facture") + Alignment = Align.Center + } + { ReportHBox1 ReportHBox + #MoveScaled(1,9,72,25) + Height = "4cm" + Fixed = True + Expand = True + Border = ReportBorder["Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + { ReportHBox5 ReportHBox + #MoveScaled(2,1,34,22) + Width = "5cm" + Height = "5cm" + Fixed = True + Expand = True + AutoResize = True + { ReportSvgImage1 ReportSvgImage + #MoveScaled(3,2,25,17) + Width = "50mm" + Height = "30mm" + Alignment = Align.Center + Image = SvgImage.Load("img/logo.svg") + } + } + { ReportVBox3 ReportVBox + #MoveScaled(38,1,34,20) + Padding = ReportPadding["Top:4mm;Bottom:4mm;Left:4mm;Right:4mm"] + Expand = True + AutoResize = True + Tag = "*" + { Nom ReportLabel entete + Name = "Nom" + #MoveScaled(2,1,28,3) + AutoResize = True + Tag = "nom" + } + { Adresse ReportLabel entete + Name = "Adresse" + #MoveScaled(2,6,28,3) + AutoResize = True + Tag = "adresse" + } + { ReportHBox6 ReportHBox + #MoveScaled(1,9,30,5) + AutoResize = True + Tag = "*" + Spacing = "2mm" + { CP ReportLabel entete + Name = "CP" + #MoveScaled(2,1,10,3) + Width = "25mm" + Padding = ReportPadding["Right:1mm"] + AutoResize = True + Tag = "CP" + } + { ville ReportLabel entete + Name = "ville" + #MoveScaled(13,1,16,3) + Expand = True + AutoResize = True + Tag = "ville" + } + } + } + } + } + { ReportVBox1 ReportVBox + #MoveScaled(4,49,90,26) + Expand = True + { ReportHBox2 ReportHBox + #MoveScaled(2,0,86,5) + Height = "1cm" + Fixed = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + { ReportLabel11 ReportLabel + #MoveScaled(0,0,21,4) + Width = "15mm" + Font = Font["Bitstream Charter,Bold"] + Border = ReportBorder["Right:1px #000000"] + Text = ("Id") + Alignment = Align.Center + } + { ReportLabel1 ReportLabel + #MoveScaled(17,1,21,4) + Width = "40%" + Font = Font["Bitstream Charter,Bold"] + Expand = True + Border = ReportBorder["Top=1mm &H00000000&; Bottom=1mm &H00000000&; Left=1mm &H00000000&; "] + Text = ("Designation") + Alignment = Align.Center + } + { ReportLabel2 ReportLabel + #MoveScaled(38,1,13,4) + Width = "3cm" + Font = Font["Bitstream Charter,Bold"] + Border = ReportBorder["Left:1px #000000"] + Text = ("PU HT") + Alignment = Align.Center + } + { ReportLabel3 ReportLabel + #MoveScaled(51,1,13,4) + Width = "3cm" + Font = Font["Bitstream Charter,Bold"] + Tag = "*" + Border = ReportBorder["Left:1px #000000"] + Text = ("Quantité") + Alignment = Align.Center + } + { ReportLabel4 ReportLabel + #MoveScaled(68,1,13,4) + Width = "3cm" + Font = Font["Bitstream Charter,Bold"] + Border = ReportBorder["Left:1px #000000"] + Text = ("Total HT") + Alignment = Align.Center + } + } + { ReportHBox3 ReportHBox + #MoveScaled(2,7,86,5) + Height = "8mm" + AutoResize = True + Tag = "*" + { ReportLabel12 ReportLabel ligne + Name = "ReportLabel12" + #MoveScaled(0,0,17,4) + Width = "15mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Left:1px #000000"] + Alignment = Align.Center + } + { ReportLabel5 ReportLabel ligne + Name = "ReportLabel5" + #MoveScaled(19,0,21,4) + Width = "40%" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Expand = True + Tag = "designation" + Border = ReportBorder["Left:1px #000000"] + } + { ReportLabel6 ReportLabel ligne + Name = "ReportLabel6" + #MoveScaled(39,1,13,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Tag = "PU" + Border = ReportBorder["Left:1px #000000"] + Alignment = Align.Right + } + { ReportLabel7 ReportLabel ligne + Name = "ReportLabel7" + #MoveScaled(52,1,13,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Tag = "Q" + Border = ReportBorder["Left:1px #000000"] + Alignment = Align.Right + } + { ReportLabel8 ReportLabel ligne + Name = "ReportLabel8" + #MoveScaled(70,1,13,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Border = ReportBorder["Left:1px #000000;Right:1px #000000"] + Format = "#.00 €" + Alignment = Align.Right + } + } + { ReportHBox8 ReportHBox + #MoveScaled(3,13,83,2) + Height = "1mm" + Fixed = True + Border = ReportBorder["Top:1px #000000"] + Background = ReportBrush["#FFFF00"] + } + { ReportHBox4 ReportHBox + #MoveScaled(4,17,81,5) + AutoResize = True + { ReportHBox7 ReportHBox + #MoveScaled(4,2,62,2) + Expand = True + } + { ReportLabel14 ReportLabel + #MoveScaled(68,1,11,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm"] + AutoResize = True + Border = ReportBorder["Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Format = "#.00 €" + Alignment = Align.Right + } + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,100,101,6) + Fixed = True + AutoResize = True + { ReportHBox10 ReportHBox + #MoveScaled(2,1,31,3) + Expand = True + } + { ReportLabel10 ReportLabel + #MoveScaled(68,0,31,4) + AutoResize = True + Text = ("Page $PAGE") + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/myReport5.class b/comp/src/gb.report/.src/Tests/Old/myReport5.class new file mode 100644 index 00000000..391ceb22 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport5.class @@ -0,0 +1,22 @@ +' Gambas class file + +Public Sub _new() + + Dim Rlbl As ReportLabel + Dim i As Integer + 'Report.Debug = True + For i = 0 To 100 + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Autoresize = True + Rlbl.Text = "Ligne " & i + Rlbl.Border.bottom.Width = "1px" + Rlbl.Padding.Top = "3 mm" + Rlbl.Padding.Bottom = "1mm" + Rlbl.Padding.Left = "2 mm" + Rlbl.Top = i & "cm" + Rlbl.Left = i & "cm" + Rlbl.Width = "1 cm" + Next + +End diff --git a/comp/src/gb.report/.src/Tests/Old/myReport5.report b/comp/src/gb.report/.src/Tests/Old/myReport5.report new file mode 100644 index 00000000..4f106dc2 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport5.report @@ -0,0 +1,91 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,90,76) + Tag = "*" + Index = 0 + Text = ("") + { ReportHBox2 ReportHBox + #MoveScaled(0,0,77,14) + Height = "4cm" + Fixed = True + Padding = ReportPadding["Top:7mm;Bottom:7mm;Left:7mm;Right:7mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#3398C3"] + { ReportSvgImage1 ReportSvgImage + #MoveScaled(2,2,11,11) + Width = "4cm" + Image = SvgImage.Load("img/logo.svg") + } + { ReportLabel3 ReportLabel + #MoveScaled(20,2,48,10) + Left = "1mm" + Top = "1mm" + Width = "3cm" + Brush = ReportBrush["LinearGradient(1,0.04,0,0.04,[#DF6B00,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["DejaVu Sans,Bold,+14"] + Padding = ReportPadding["Top:6mm;Bottom:6mm;Left:6mm;Right:6mm"] + Expand = True + Text = ("Gambas") + Alignment = Align.Left + } + } + { ReportHBox1 ReportHBox + #MoveScaled(0,15,87,56) + Expand = True + Border = ReportBorder["Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FF007F"] + { ReportVBox1 ReportVBox + #MoveScaled(1,1,25,47) + Width = "6cm" + Background = ReportBrush["#DF6B00"] + } + { ReportVBox2 ReportVBox + #MoveScaled(28,2,56,54) + Expand = True + Border = ReportBorder["Left:1mm #000000"] + Background = ReportBrush["#FF7F00"] + { RVBCont ReportPanel + #MoveScaled(3,5,45,36) + Expand = True + Tag = "*" + Background = ReportBrush["#FFFF00"] + { ReportLabel1 ReportLabel + #MoveScaled(3,5,43,23) + Left = "25%" + Top = "5cm" + Width = "50%" + Height = "5cm" + Brush = ReportBrush["LinearGradient(0.21,0.91,0.81,0.11,[#FF000000,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["Bold,+15"] + Ignore = True + Background = ReportBrush["#FF1F57"] + Text = ("gambas") + Alignment = Align.Center + } + { ReportImage1 ReportImage + #MoveScaled(10,17,14,8) + Height = "3cm" + Alignment = Align.Center + Image = Image.Load("printer1.png") + } + } + { ReportLabel2 ReportLabel + #MoveScaled(9,47,47,5) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold,+1"] + Padding = ReportPadding["Top:4mm;Bottom:4mm;Left:2mm;Right:2mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000"] + Background = ReportBrush["#3398C3"] + Text = ("Page $PAGE on $NPAGE") + Alignment = Align.Right + UseField = True + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/OutputReport.class b/comp/src/gb.report/.src/Tests/OutputReport.class new file mode 100644 index 00000000..7fc4dc12 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport.class @@ -0,0 +1,146 @@ +' Gambas class file + +Public nnoderows As Integer +Public nanchorrows As Integer + +Public Sub _new() + + 'enter current date on the header + ReportLabel2.Text = Date + + 'enter the project information in the header on the print out page + 'With FMain.Project + ReportLabel3.Text = "Project Title: " ' & .JobTitle + ReportLabel4.Text = "Project No.: " ' & .Number + ReportLabel5.Text = "Company: " ' & .Company + ReportLabel6.Text = "Designer: " '& .Designer + ReportLabel7.Text = "Base Plate ID: " ' & .BasePlateID + 'End With + + 'set count to four to display the results for qmax. Tmax, L, and phi + ReportHBox9.DataCount = 4 + + 'set the number of plate nodes to display in print out tables + 'nnoderows = Module1.RoundUpInt(FMain.ConcreteSection.cNodes.Count / 2) + ReportHBox5.DataCount = 9 + + 'set the number of anchor rods to display in print out tables + ' nanchorrows = Module1.RoundUpInt(FMain.cAnchorRods.Count / 2) + ReportHBox7.DataCount = 3 + FullRepeat.DataCount = 3 + +End + + +Public Sub ReportImage2_Data(Index As Integer) + ' Dim current_results As Results + ' Dim pSection As Picture + ' + ' 'create a new picture object for drawing the base plate + ' pSection = New Picture(300, 300, False) + ' + ' 'get the results for first load case + ' current_results = FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)] + ' + ' 'refresh base plate drawing with results + ' Module1.DrawBasePlate(pSection, 1, current_results) + ' + ' 'add the picture of the base plate to the page + ' ReportImage2.Image = pSection.Image + +End + + +Public Sub ReportLabel9_Data(Index As Integer) + + Last.Data = "Load Case: " '& FMain.cLoads[Str$(ReportHBox3.DataIndex)].label + +End + + +Public Sub ReportLabel14_Data(Index As Integer) + Dim $value As String + + Select Case Index + Case 0 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].Tmax, 2) + Last.Data = "T(max) = " & $value & " lbs" + Case 1 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].maxstress, 2) + Last.Data = "q(max) = " & $value & " psi" + Case 2 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].L, 2) + Last.Data = "L = " & $value & " in" + Case 3 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].phi, 2) + Last.Data = "phi = " & $value & " degrees" + Case Else + End Select + +End + + +Public Sub ReportLabel25_Data(Index As Integer) + + Last.Data = Str(Index + 1) &/ Str(FullRepeat.DataIndex) + +End + +Public Sub ReportLabel29_Data(Index As Integer) + + Last.Data = nnoderows + Index + 1 + +End + +Public Sub ReportLabel26_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportVBox4.DataIndex)].stress[Index] + 'Last.Data = Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel30_Data(Index As Integer) + ' Dim value As Float + ' Dim current_results As Results + ' + ' current_results = FMain.AnalysisResults[Str$(ReportVBox4.DataIndex)] + ' If nnoderows + Index < current_results.stress.Count Then + ' value = current_results.stress[nnoderows + Index] + ' Last.Data = Module1.AdvFormat(value, 2) + ' Endif + +End + +Public Sub ReportLabel49_Data(Index As Integer) + + Last.Data = Str(Index + 1) &/ Str(FullRepeat.DataIndex) + +End + +Public Sub ReportLabel52_Data(Index As Integer) + + Last.Data = nanchorrows + Index + 1 + +End + +Public Sub ReportLabel50_Data(Index As Integer) + Dim value As Float + + ' value = FMain.AnalysisResults[Str$(ReportVBox6.DataIndex)].T[Index] + ' Last.Data = Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel53_Data(Index As Integer) + ' Dim value As Float + ' Dim current_results As Results + ' + ' current_results = FMain.AnalysisResults[Str$(ReportVBox6.DataIndex)] + ' If nanchorrows + Index < current_results.T.Count Then + ' value = current_results.T[nanchorrows + Index] + ' Last.Data = Module1.AdvFormat(value, 2) + ' Endif + +End + diff --git a/comp/src/gb.report/.src/Tests/OutputReport.report b/comp/src/gb.report/.src/Tests/OutputReport.report new file mode 100644 index 00000000..045630c1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport.report @@ -0,0 +1,306 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,76,85) + Fixed = True + Padding = ReportPadding["Top:20mm;Bottom:20mm;Left:20mm;Right:20mm"] + Expand = True + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(2,0,62,11) + Height = "25mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportImage1 ReportImage + #MoveScaled(1,1,18,9) + Width = "51mm" + AutoResize = True + } + { ReportLabel1 ReportLabel + #MoveScaled(21,1,10,9) + AutoResize = True + Text = ("Version 1.0") + Alignment = Align.BottomLeft + } + { ReportLabel2 ReportLabel + #MoveScaled(31,1,30,9) + Expand = True + AutoResize = True + Text = ("Date") + Alignment = Align.BottomRight + UseField = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(2,11,62,9) + Height = "28.01mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel3 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project Title:") + Alignment = Align.TopLeft + } + { ReportLabel4 ReportLabel + #MoveScaled(0,2,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project No.:") + Alignment = Align.TopLeft + } + { ReportLabel5 ReportLabel + #MoveScaled(0,4,12,2) + Height = "6mm" + AutoResize = True + Text = ("Company:") + Alignment = Align.TopLeft + } + { ReportLabel6 ReportLabel + #MoveScaled(0,6,12,2) + Height = "6mm" + AutoResize = True + Text = ("Designer:") + Alignment = Align.TopLeft + } + } + { ReportHBox2 ReportHBox + #MoveScaled(2,20,62,3) + Height = "8mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel7 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Base Plate ID:") + Alignment = Align.TopLeft + } + } + { FullRepeat ReportPanel + #MoveScaled(2,25,63,50) + AutoResize = True + Tag = "**" + ForceNewPage = True + { ReportHBox3 ReportHBox + #MoveScaled(0,0,62,17) + Height = "61mm" + Fixed = True + Expand = True + { ReportVBox3 ReportVBox + #MoveScaled(1,1,18,14) + AutoResize = True + { ReportHBox8 ReportHBox + #MoveScaled(1,2,15,4) + Width = "51mm" + Height = "12mm" + { ReportLabel9 ReportLabel + #MoveScaled(1,1,13,2) + Font = Font["+1"] + Expand = True + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,8,15,4) + Width = "51mm" + Height = "6mm" + { ReportLabel14 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + } + } + } + { ReportImage2 ReportImage + #MoveScaled(31,1,17,15) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Background = ReportBrush["#0000FF"] + Alignment = Align.Center + } + } + { ReportVBox4 ReportVBox + #MoveScaled(1,18,62,14) + AutoResize = True + Tag = "Serie 1" + { ReportLabel19 ReportLabel + #MoveScaled(2,0,16,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Bearing Pressue") + } + { ReportHBox4 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel20 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel21 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + { ReportLabel22 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel23 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel24 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + } + { ReportHBox5 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel25 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel26 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel27 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel29 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel30 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + { ReportVBox6 ReportVBox + #MoveScaled(1,33,62,14) + AutoResize = True + Tag = "Serie2" + { ReportLabel31 ReportLabel + #MoveScaled(2,0,19,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Anchor Rod Tension") + } + { ReportHBox6 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel44 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel45 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + { ReportLabel46 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel47 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel48 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + } + { ReportHBox7 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel49 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel50 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel51 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel52 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel53 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + } + { ReportHBox10 ReportHBox + #MoveScaled(2,75,62,4) + Fixed = True + Expand = True + AutoResize = True + { ReportLabel8 ReportLabel + #MoveScaled(22,1,21,2) + Expand = True + AutoResize = True + Text = ("Page $PAGE of $NPAGE") + Alignment = Align.Bottom + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/OutputReport2.class b/comp/src/gb.report/.src/Tests/OutputReport2.class new file mode 100644 index 00000000..4142a133 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport2.class @@ -0,0 +1,166 @@ +' Gambas class file + +Public nnoderows As Integer +Public nanchorrows As Integer + +Public Sub _new() +'Report.Debug = True + 'enter current date on the header + ReportLabel2.Text = Date + + 'enter the project information in the header on the print out page + 'With FMain.Project + ReportLabel3.Text = "Project Title: " ' & .JobTitle + ReportLabel4.Text = "Project No.: '" '& .Number + ReportLabel5.Text = "Company: " '& .Company + ReportLabel6.Text = "Designer: " '& .Designer + ReportLabel7.Text = "Base Plate ID: " ' & .BasePlateID + 'End With + + 'set count to four to display the results for qmax. Tmax, L, and phi + ReportHBox9.DataCount = 4 + + 'set the number of plate nodes to display in print out tables + ' nnoderows = Module1.RoundUpInt(FMain.ConcreteSection.cNodes.Count / 2) + ReportHBox5.DataCount = 11 'nnoderows + + 'set the number of anchor rods to display in print out tables + 'nanchorrows = Module1.RoundUpInt(FMain.cAnchorRods.Count / 2) + ReportHBox7.DataCount = 13 'nanchorrows + + 'set the number of report panels - one for each load combination result + ReportPanel1.DataCount = 6 'FMain.AnalysisResults.Count + +End + + +Public Sub ReportImage2_Data(Index As Integer) + 'Dim current_results As Results + Dim pSection As Picture + + 'create a new picture object for drawing the base plate + pSection = New Picture(300, 300, False) + + 'get the results for first load case + 'current_results = FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)] + + 'refresh base plate drawing with results + 'Module1.DrawBasePlate(pSection, 1, current_results) + + 'add the picture of the base plate to the page + ReportImage2.Image = pSection.Image + +End + + +Public Sub ReportLabel9_Data(Index As Integer) + + Last.Data = "Load Case: " '& FMain.cLoads[Str$(ReportHBox3.DataIndex)].label + +End + + +Public Sub ReportLabel14_Data(Index As Integer) + Dim $value As String + + Select Case Index + Case 0 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].Tmax, 2) + Last.Data = "T(max) = " & 3 & " lbs" + Case 1 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].maxstress, 2) + Last.Data = "q(max) = " & 3 & " psi" + Case 2 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].L, 2) + Last.Data = "L = " & 3 & " in" + Case 3 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].phi, 2) + Last.Data = "phi = " & 3 & " degrees" + Case Else + End Select + +End + + +Public Sub ReportLabel25_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel29_Data(Index As Integer) + ' Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nnoderows + Index < current_results.stress.Count Then + Last.Data = nnoderows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel26_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].stress[Index] + Last.Data = "tt" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel30_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nnoderows + Index < current_results.stress.Count Then + 'value = current_results.stress[nnoderows + Index] + Last.Data = "mm" 'Module1.AdvFormat(value, 2) + ' Else + Last.Data = Null + 'Endif + +End + +Public Sub ReportLabel49_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel52_Data(Index As Integer) + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nanchorrows + Index < current_results.T.Count Then + Last.Data = nanchorrows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel50_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].T[Index] + Last.Data = "kkkk" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel53_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + ' current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nanchorrows + Index < current_results.T.Count Then + ' value = current_results.T[nanchorrows + Index] + Last.Data = "qqqqqq" 'Module1.AdvFormat(value, 2) + ' Else + ' Last.Data = Null + 'Endif + +End + diff --git a/comp/src/gb.report/.src/Tests/OutputReport2.report b/comp/src/gb.report/.src/Tests/OutputReport2.report new file mode 100644 index 00000000..f12422c8 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport2.report @@ -0,0 +1,308 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,76,88) + Fixed = True + Padding = ReportPadding["Top:20mm;Bottom:10mm;Left:20mm;Right:20mm"] + Ignore = True + AutoResize = True + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(2,0,62,11) + Height = "25mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportImage1 ReportImage + #MoveScaled(1,1,18,9) + Width = "51mm" + AutoResize = True + } + { ReportLabel1 ReportLabel + #MoveScaled(21,1,10,9) + Width = "25mm" + AutoResize = True + Text = ("Version 1.0") + Alignment = Align.BottomLeft + } + { ReportLabel2 ReportLabel + #MoveScaled(31,1,30,9) + Expand = True + AutoResize = True + Text = ("Date") + Alignment = Align.BottomRight + UseField = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(2,11,62,9) + Height = "28.01mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel3 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project Title:") + Alignment = Align.TopLeft + } + { ReportLabel4 ReportLabel + #MoveScaled(0,2,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project No.:") + Alignment = Align.TopLeft + } + { ReportLabel5 ReportLabel + #MoveScaled(0,4,12,2) + Height = "6mm" + AutoResize = True + Text = ("Company:") + Alignment = Align.TopLeft + } + { ReportLabel6 ReportLabel + #MoveScaled(0,6,12,2) + Height = "6mm" + AutoResize = True + Text = ("Designer:") + Alignment = Align.TopLeft + } + } + { ReportHBox2 ReportHBox + #MoveScaled(2,20,62,3) + Height = "8mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel7 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Base Plate ID:") + Alignment = Align.TopLeft + } + } + { ReportPanel1 ReportPanel + #MoveScaled(2,25,67,52) + AutoResize = True + ForceNewPage = True + { ReportHBox3 ReportHBox + #MoveScaled(1,1,62,17) + Height = "61mm" + Expand = True + { ReportVBox3 ReportVBox + #MoveScaled(1,1,18,14) + AutoResize = True + { ReportHBox8 ReportHBox + #MoveScaled(1,2,15,4) + Width = "51mm" + Height = "12mm" + { ReportLabel9 ReportLabel + #MoveScaled(1,1,13,2) + Font = Font["+1"] + Expand = True + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,8,15,4) + Width = "51mm" + Height = "6mm" + { ReportLabel14 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + } + } + } + { ReportImage2 ReportImage + #MoveScaled(31,1,17,15) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Alignment = Align.Center + } + } + { ReportVBox4 ReportVBox + #MoveScaled(1,20,62,14) + AutoResize = True + { ReportLabel19 ReportLabel + #MoveScaled(2,0,16,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Bearing Pressue") + } + { ReportHBox4 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel20 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel21 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + { ReportLabel22 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel23 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel24 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + } + { ReportHBox5 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel25 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel26 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel27 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel29 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel30 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + { ReportVBox6 ReportVBox + #MoveScaled(1,36,62,14) + AutoResize = True + { ReportLabel31 ReportLabel + #MoveScaled(2,0,19,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Anchor Rod Tension") + } + { ReportHBox6 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel44 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel45 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + { ReportLabel46 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel47 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel48 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + } + { ReportHBox7 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel49 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel50 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel51 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel52 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel53 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + } + { ReportPanel2 ReportPanel + #MoveScaled(2,77,69,1) + Fixed = True + Expand = True + } + { ReportHBox10 ReportHBox + #MoveScaled(2,79,62,4) + Height = "1cm" + Fixed = True + AutoResize = True + { ReportLabel8 ReportLabel + #MoveScaled(22,1,21,2) + Expand = True + AutoResize = True + Text = ("Page $PAGE of $NPAGE") + Alignment = Align.Bottom + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Paints.class b/comp/src/gb.report/.src/Tests/Paints.class new file mode 100644 index 00000000..01960f38 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Paints.class @@ -0,0 +1,33 @@ +' Gambas class file + +'Export +' +' Static Public Sub Begin(Device As Object) +' +' Debug +' Super.Begin(Device) +' +' End +' +' Static Public Sub End(Device As Object) +' +' Debug +' Super.End(Device) +' +' End +' +' ' +' Static Public Sub Rectangle(X As Float, Y As Float, W As Float, H As Float) +' +' Debug CStr(X); ", "; CStr(Y); ", "; CStr(W); ", "; CStr(H) +' Super.Rectangle(X, Y, W, H) +' +' End +' +' +' Static Public Sub Text(Text As String, Optional X As Float, Y As Float, Width As Float, Height As Float, Alignment As Integer) +' +' 'Debug Paint.Font.Size +' Super.Text(Text, X, Y, Width, Height, Alignment) +' +' End diff --git a/comp/src/gb.report/.src/Tests/Report11.class b/comp/src/gb.report/.src/Tests/Report11.class new file mode 100644 index 00000000..ddd552fa --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report11.class @@ -0,0 +1,29 @@ +' Gambas class file + +' 'Private hMap As New Map +' 'Private hImgCache As Image + Public Sub _new() + ReportDrawingArea1.BoxShadow.XOffset = "1mm" + ReportDrawingArea1.BoxShadow.YOffset = "1mm" + ReportDrawingArea1.BoxShadow.Color = Color.Gray + ReportDrawingArea1.BoxShadow.Blur = "1mm" +' ' +' ' hMap.AddTile("gg", "https://khms{s}.google.fr/kh/v={version}&src=app&x={x}&y={y}&z={z}&s=Galile", ["version": "145"]).SubDomains = ["0", "1", "2"] +' ' +' ' hmap.Zoom = 17 +' ' 'ReportPanel1.DataCount = 200 + End +' ' +' ' + Public Sub ReportDrawingArea1_Draw(width As Integer, Height As Integer, Index As Integer) +' ' +Draw.FillRect(0, 0, width, height, Color.White) +' ' 'Print width, Height +' ' 'hMap.Resize(Width, Height) +' ' 'If Paint.Device Is Printer Then Stop +' ' hMap.Center = MapPoint(Geo.SexToDec("45°31'33,33''N"), Geo.SexToDec("0°18'43,50''W")) +' ' 'hMap.Center = MapPoint(Geo.SexToDec(Str(CInt(Rnd(30, 45))) & "°31'33,33''N"), Geo.SexToDec("0°18'43,50''W")) +' ' 'If Not hImgCache Then +' ' hImgCache = hMap.Grab(hMap.Bounds, width / ReportUnits.DesktopScale, Height / ReportUnits.DesktopScale, 0, 18) +' ' Draw.Image(hImgCache, 0, 0, width, Height) +End diff --git a/comp/src/gb.report/.src/Tests/Report11.report b/comp/src/gb.report/.src/Tests/Report11.report new file mode 100644 index 00000000..5b63930c --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report11.report @@ -0,0 +1,29 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:3cm;Bottom:3cm;Left:3cm;Right:3cm"] + Spacing = "2cm" + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(7,1,54,8) + Font = Font["Bold,+12"] + AutoResize = True + Text = ("C'est la maison !") + Alignment = Align.Center + } + { ReportPanel1 ReportPanel + #MoveScaled(6,12,53,31) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + AutoResize = True + { ReportDrawingArea1 ReportDrawingArea + #MoveScaled(5,6,52,30) + Width = "7mm" + Height = "6cm" + Border = ReportBorder["Top:2mm #000000;Bottom:2mm #000000;Left:2mm #000000;Right:2mm #000000;TopLeftCorner:1mm;TopRightCorner:1mm;BottomRightCorner:1mm;BottomLeftCorner:1mm"] + Cached = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report41.class b/comp/src/gb.report/.src/Tests/Report41.class new file mode 100644 index 00000000..f19d0fdd --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report41.class @@ -0,0 +1,36 @@ +' Gambas class file + +Private hResult As Result +Private hResult2 As Result + + +Public Sub Report_Open() + Connections["MainConn"].Open + hResult = db.Exec("select * from parc_ilots") + ReportHBox1.DataCount = 3 'hResult.Count + Report.Debug = True +End + +Public Sub ReportLabel1_Data(Index As Integer) + + hResult.MoveTo(Index) + Last.Data = hResult!nom + +End + +Public Sub ReportHBox1_BeforeData() + hResult.MoveTo(ReportHBox1.DataIndex) + Print "Section : " & hResult!id & "/" & ReportHBox1.DataIndex + + hResult2 = db.Exec("select * from liaisonilotparc where id_ilot=&1", hResult!id) + ReportHBox2.DataCount = hResult2.Count + Print "DataCount : " & hResult2.Count +End + +Public Sub ReportLabel2_Data(Index As Integer) + + hResult2.MoveTo(Index) + Print " " & ReportHBox1.DataIndex & "/" & Index + Try Last.Data = hResult2!id_parc + 'If Error Then Stop +End diff --git a/comp/src/gb.report/.src/Tests/Report41.report b/comp/src/gb.report/.src/Tests/Report41.report new file mode 100644 index 00000000..f09d6cdd --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report41.report @@ -0,0 +1,34 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,109,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportVBox1 ReportVBox + #MoveScaled(4,5,101,53) + Spacing = "1cm" + { ReportHBox1 ReportHBox + #MoveScaled(3,4,94,35) + { ReportLabel1 ReportLabel + #MoveScaled(0,0,6,32) + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + Rotate = 90 + } + { ReportVBox2 ReportVBox + #MoveScaled(13,1,79,32) + Expand = True + { ReportHBox2 ReportHBox + #MoveScaled(1,1,77,4) + Tag = "*" + { ReportLabel2 ReportLabel + #MoveScaled(2,0,46,4) + Expand = True + } + } + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report51.class b/comp/src/gb.report/.src/Tests/Report51.class new file mode 100644 index 00000000..a2dec1db --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report51.class @@ -0,0 +1,69 @@ +' Gambas class file + +Private hResult As Result +Private hResult2 As Result + Private hVBox As ReportVBox + +Public Sub Report_Open() + hVBox = New ReportVBox(ReportHBox1) + hVBox.Spacing = "1cm" + Connections["MainConn"].Open + hResult = db.Exec("select * from parc_ilots") + 'Report.Debug = True + 'ReportHBox1.BackGround = ReportBrush.Color(Color.Yellow) + 'hVBox.BackGround = ReportBrush.Color(Color.red) + hVBox.Width = "50%" + For Each hResult + + AddIlot(hResult!nom, hResult!id) + + Next + +End + + +Private Sub AddIlot(sIlot As String, iIlot As Integer) + Dim hHBox1 As New ReportHBox(hVBox) + Dim hLblIlot As New ReportLabel(hHBox1) + Dim hVBoxList As New ReportVBox(hHBox1) + Dim hHBoxLine As ReportHBox + Dim hLblParcelle As ReportLabel + Dim i As Integer + hLblIlot.Rotate = 90 + hVBoxList.Expand = True + + hVBoxList.Border.Top.Width = "0.1mm" +hVBoxList.Border.Right.Width = "0.1mm" +hHbox1.Border.Bottom.Width = "0.1mm" + hLblIlot.Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + + hLblIlot.Text = sIlot + hLblIlot.Border = ReportBorder["Top:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + hResult2 = db.Exec("select p.nom parcelle from liaisonilotparc lip left join parcelles p on p.id=lip.id_parc where id_ilot=&1 order by lip.ordre", iIlot) + + + + For Each hResult2 + hHBoxLine = New ReportHBox(hVBoxList) + hHBoxLine.Expand = True + 'hHBoxLine.Height = "5mm" + hLblIlot = New ReportLabel(hHBoxLine) + hHBoxLine.Border = ReportBorder["Bottom:0.1mm #000000;"] + hLblIlot.Text = hResult2!parcelle + hLblIlot.Padding.Left = "1mm" + hLblIlot.Expand = True + 'hHBoxLine.Expand = True + 'hLblIlot.Border = ReportBorder["Bottom:0.1mm #00000"] + For i = 0 To 3 + hLblIlot = New ReportLabel(hHBoxLine) + hLblIlot.Width = "5mm" + hLblIlot.Border = ReportBorder["Left:0.1mm #000000;"] + + Next + Next + + + + + +End diff --git a/comp/src/gb.report/.src/Tests/Report51.report b/comp/src/gb.report/.src/Tests/Report51.report new file mode 100644 index 00000000..d8c8fed1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report51.report @@ -0,0 +1,15 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(0,2,62,57) + Height = "10cm" + Expand = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report52.class b/comp/src/gb.report/.src/Tests/Report52.class new file mode 100644 index 00000000..a2dec1db --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report52.class @@ -0,0 +1,69 @@ +' Gambas class file + +Private hResult As Result +Private hResult2 As Result + Private hVBox As ReportVBox + +Public Sub Report_Open() + hVBox = New ReportVBox(ReportHBox1) + hVBox.Spacing = "1cm" + Connections["MainConn"].Open + hResult = db.Exec("select * from parc_ilots") + 'Report.Debug = True + 'ReportHBox1.BackGround = ReportBrush.Color(Color.Yellow) + 'hVBox.BackGround = ReportBrush.Color(Color.red) + hVBox.Width = "50%" + For Each hResult + + AddIlot(hResult!nom, hResult!id) + + Next + +End + + +Private Sub AddIlot(sIlot As String, iIlot As Integer) + Dim hHBox1 As New ReportHBox(hVBox) + Dim hLblIlot As New ReportLabel(hHBox1) + Dim hVBoxList As New ReportVBox(hHBox1) + Dim hHBoxLine As ReportHBox + Dim hLblParcelle As ReportLabel + Dim i As Integer + hLblIlot.Rotate = 90 + hVBoxList.Expand = True + + hVBoxList.Border.Top.Width = "0.1mm" +hVBoxList.Border.Right.Width = "0.1mm" +hHbox1.Border.Bottom.Width = "0.1mm" + hLblIlot.Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + + hLblIlot.Text = sIlot + hLblIlot.Border = ReportBorder["Top:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + hResult2 = db.Exec("select p.nom parcelle from liaisonilotparc lip left join parcelles p on p.id=lip.id_parc where id_ilot=&1 order by lip.ordre", iIlot) + + + + For Each hResult2 + hHBoxLine = New ReportHBox(hVBoxList) + hHBoxLine.Expand = True + 'hHBoxLine.Height = "5mm" + hLblIlot = New ReportLabel(hHBoxLine) + hHBoxLine.Border = ReportBorder["Bottom:0.1mm #000000;"] + hLblIlot.Text = hResult2!parcelle + hLblIlot.Padding.Left = "1mm" + hLblIlot.Expand = True + 'hHBoxLine.Expand = True + 'hLblIlot.Border = ReportBorder["Bottom:0.1mm #00000"] + For i = 0 To 3 + hLblIlot = New ReportLabel(hHBoxLine) + hLblIlot.Width = "5mm" + hLblIlot.Border = ReportBorder["Left:0.1mm #000000;"] + + Next + Next + + + + + +End diff --git a/comp/src/gb.report/.src/Tests/Report52.report b/comp/src/gb.report/.src/Tests/Report52.report new file mode 100644 index 00000000..d8c8fed1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report52.report @@ -0,0 +1,15 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(0,2,62,57) + Height = "10cm" + Expand = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report9.class b/comp/src/gb.report/.src/Tests/Report9.class new file mode 100644 index 00000000..083224ac --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report9.class @@ -0,0 +1,17 @@ +' Gambas class file + +Public Sub _new() + 'Report.Debug = True + ' Me.Width = "5cm" + ' Me.Height = "10cm" + ReportPanel1.DataCount = 100 +End + +Public Sub Report_Open() + +ReportPanel1.BoxShadow.XOffset = "1mm" +ReportPanel1.BoxShadow.YOffset = "1mm" + 'ReportPanel1.BoxShadow.Spread = "2mm" + ReportPanel1.BoxShadow.Blur = "4mm" + Me.Clear +End diff --git a/comp/src/gb.report/.src/Tests/Report9.report b/comp/src/gb.report/.src/Tests/Report9.report new file mode 100644 index 00000000..c797b67d --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report9.report @@ -0,0 +1,22 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportVPanel1 ReportVPanel + #MoveScaled(4,1,58,51) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Spacing = "1cm" + { ReportPanel1 ReportPanel + #MoveScaled(12,6,15,16) + Width = "6cm" + Height = "6cm" + Background = ReportBrush["#FFFFFF"] + OnePiece = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Test.module b/comp/src/gb.report/.src/Tests/Test.module new file mode 100644 index 00000000..593c3ae9 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Test.module @@ -0,0 +1,9 @@ +' Gambas module file + + Public Sub Main() +' + Dim hreport As New Report9 + Dim hprinter As New Printer + hprinter.OutputFile = User.Home &/ "sortie.pdf" + hreport.Print(hprinter) + End diff --git a/comp/src/gb.report/.src/Tests/rpTestShadowGrid.class b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.class new file mode 100644 index 00000000..f3a08abe --- /dev/null +++ b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.class @@ -0,0 +1,23 @@ +' Gambas class file + +Public Sub ReportDrawingArea1_Draw() + + Paint.MoveTo(0, 0) + Paint.LineTo(Paint.Width, Paint.Height) + Paint.Stroke + +End + +Public Sub Report_Open() + + ReportGridView1.Columns.Count = 3 + ReportGridView1.Rows.Count = 20 + + 'Report.Debug = True +End + +Public Sub rpTestShadow_Open() + + + +End diff --git a/comp/src/gb.report/.src/Tests/rpTestShadowGrid.report b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.report new file mode 100644 index 00000000..502101d7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.report @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Font = Font["+9"] + Padding = ReportPadding["Top:6cm;Bottom:6cm;Left:6cm;Right:6cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(3,2,58,9) + Height = "7cm" + Font = Font["Georgia,+12"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Text = ("coucou") + Rotate = -1 + } + { ReportGridView1 ReportGridView + #MoveScaled(5,18,52,30) + Height = "5cm" + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + Index = 0 +} diff --git a/comp/src/gb.report/ChangeLog b/comp/src/gb.report/ChangeLog new file mode 100644 index 00000000..d2decfd1 --- /dev/null +++ b/comp/src/gb.report/ChangeLog @@ -0,0 +1,6 @@ +[GB.REPORT] +* NEW: Negative numbers are allowed on string size properties. It allow to use + negative value on XOffset or YOffset for shadows. + +* BUG: Resolve the bug on clear (Even if i don't remember for what i've done this property) + diff --git a/comp/src/gb.report/gambas.svg b/comp/src/gb.report/gambas.svg new file mode 100644 index 00000000..5bfafe4d --- /dev/null +++ b/comp/src/gb.report/gambas.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/comp/src/gb.report/img/16/red-arrow-h.png b/comp/src/gb.report/img/16/red-arrow-h.png new file mode 100644 index 0000000000000000000000000000000000000000..9ad41c2e596c8b409b5160f89452256327e10a0a GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^&H$ef*Z)9JU;m$hA?NbP6+l78 zk|4ie28U-i(tsQ(PZ!4!j_b(+i3t-@5+)QSOqj*s#;353gPB3bkttbHsl@}Riow&> K&t;ucLK6VBDIaYB literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/16/red-arrow-v.png b/comp/src/gb.report/img/16/red-arrow-v.png new file mode 100644 index 0000000000000000000000000000000000000000..a787fe89c60ce899a23198ffa582b5bc1540384d GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^&H$ef*Z)9JU;m$hA?NbP6+l78 zk|4ie28U-i(tsRUPZ!4!j_b(@2?-p$sVAaNDr{|O%w=L;%*+flz<{4Q_>%I`W}rd_ MPgg&ebxsLQ04Dk&EdT%j literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/22/FullWidth.png b/comp/src/gb.report/img/22/FullWidth.png new file mode 100644 index 0000000000000000000000000000000000000000..7882c75930df17079b07d001e364a110e5365cb6 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2f;?RuLp07`y?Bs|L4kw$g6^;X z@!PyzZY;40mHVo=t-(dGLG_KI*@BdFKjhc%jA$usSynLT+l-Lw9oveJz3C~^{wwJc nk{lEgeEII>IaQx_uYbs zFl1V%NP3T!Twqt^hg1L5T@7_ICrx-;9Oc@wv|K?C>pBwQW PXbyv?tDnm{r-UW|Mdc*X literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/22/RealSize.png b/comp/src/gb.report/img/22/RealSize.png new file mode 100644 index 0000000000000000000000000000000000000000..f93d1289b4a9f944672e452e305930159a57637c GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2CVRR#hG?8mPEcSpFfuUsqwc6X zvFpFFfx*N_JhwE{8ZYJWY~l&xsh8+eKA9+-{NwzA0|LhjraTlrTq@EWaP*(DPgsh@ zkq=#t%&vxJh7}h|7tQR^YBPTEFPXo3lM}P3L6G7c&m!Y9l5#v%JZU_Kcv5-pOR7B< y1e;{g{?J}zUz>D`);D?6IUk!3PImDO;$eteYM}V|B)=EXc?_PeelF{r5}E*|>qzAQ literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/22/TwoPage.png b/comp/src/gb.report/img/22/TwoPage.png new file mode 100644 index 0000000000000000000000000000000000000000..306e7cee92a4e4f56325c4578c0a91fc034eba0e GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L296VhdLp07OCnzu)82ve}b*dp$ zhWkgq%OVD~pgD%CDqJ+v9Fv57(seX!-2TimnEO$FRV&BQiYZCYMBG$zOIR2_SaK=7 TmCRcUG>gI0)z4*}Q$iB}hN~o7 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/32/Collatecopie.png b/comp/src/gb.report/img/32/Collatecopie.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0a284961fe7362bd9bc98c0020bdb460f4f017 GIT binary patch literal 449 zcmV;y0Y3hTP)2wR%gW*5 z;m*v<k@N&FAOl=;!C_>gww0>FMd{?Ca~&(9iAc>+I|5>gnjt%*^fV?BCwq zy1BU0(b3M$&BMRG?(OW*&(Gf7-N(km?(Oa7=H z>F3-1oB03$010$bPE!E*#1F|8st?Je;|_TM006~FL_t(I%e|4=4uU`oMeBkJjE-0x z#SPr|1=RolHB)d52~GOOdF{=4=mFRDd|#<-`RMX?UVk;SkM#IZ%O z4#bH?u@0@na5~G-V|#e~wn0`V09%$`4Y0kn)3+AEf*s rgwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<@9*!aq@v>D;_&b9>+9>|vw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&G~)6>z^)}Nl8&Bn#2p`h~c@bvTY>FMjz z)z_n;pSrZNrl6m#r=`rs#BKCF>;M1&0d!JMQvg8b*k%9#0We8KK~y-)jnipUfC*1|J}gbU@9e(o`@c+T7l9ti-wdyw|L!n5^Ds;Ud`%vFB=ECb zfDeX3LbE8gNRlMWa@Z8{lOW2qg4&`ahy~&xB}Rkx4iK^ez7xdKKuvUkSd|8X2GVJe z6uLo?BneXK9#C(J2AMvPP6F(G1OA?4?;9{S=YRV@s1>ThgR(0A`3WQJ5g3@fWPcEp zr7vj=BtV0P6$qdqdc&~tdzjPm`H@lj)JocJYM002ovPDHLkV1nd{ BwRHdh literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/32/grayscale.png b/comp/src/gb.report/img/32/grayscale.png new file mode 100644 index 0000000000000000000000000000000000000000..670e944d6aeb111916160f2bdf358937d265d71c GIT binary patch literal 1564 zcmV+%2IKjOP)gwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<;^N}tlq(=H=(+<>%+-=;!C?=;-O_=;`U{>gnm~>gn(A@2RAs zgn+B@9XR9cq)=I7_<;NRc5xwzoq;Ogq?@$m22+1RqNvAnyx$j8U6 ztgOk&$+0p?K?W&CR>Ix#HpB!ok4M&(8Al@$T*HxVN{O zn3(0`K_n-QDQt=GfQQ?CR>&)6=G-q0P$2hKBL+ z@bmKW^z-xM;^NlU)%ErC>ged0mX?EpfrNvDtgEWy;o)P4ay1BW>#>MjS@R^yI zg@l8KgoNVY;G?3U^YQV#ySm-o+|SOFMjz)z_n;pSrZNrl6m#r=`rs#6oTBNB{r;0d!JMQvg8b*k%9#0@+DKK~y-)V_+Zx zFf!o)j8Fy4EUawo9Gsk7+&nzIe0+TT{QLqSAjk|=AjB#xA}S^>At@y-BO@y-Coiv{ z2m(q%PzB0tBBJ6dl9C{4Igq3(8yKi5!xX4%h-*q}X=%&K>FDa}>9eYU&gJ1@v4$z&0?E6% zJ2-fFdTDZb`{?@efq)-Ofj^h6bUaL6nuWU35%L ztfyC8d_rQ9rJ`DLN=mAML0S|{LAs1=MogwlR(4KqUR-j1N`XvDN}+*)A51}!m7SbK zv9o7hLJ2Sw)$;QB)TF}s3=9N|pbAP^*viTic`GV|tJ120Kp|0~rnXM8roI$r0#8F@ zQ*%jpORIpIni>mBTRWGkPe*4L+yrYiw(g$ZzVQCOuCA^L6DDv4bFmvwG^we9hl3jX zr1Hdw$y26+q^ql?rb*Q#Pgkv(0aGwjfZbAN)@%dzq&a?m)zva{=gqe$n>S;|jF~VK z7I365T(r1dYDrQ+9Q)E`#>=GwSF8jofSa(&a`l?EYuC+NzkI{SO`BJ3*}AQzW&8a3 zH8bG~xOjH#+|06Tx1dX$<+d7`ZGL<9*7EV~gPE{jjf*EjjV0qiO$J-dhx!kWdnDgK^JYN76M1T~40%`vI3CnFB zJ~Ei|_z5E8JT>6i0#r~13Z?n;t5{v0J%8~Mp`U@_6<7fa3q%1+-0L@Q-yzF`6$pS8 z2nx>MY7qAR!^cl3dGm8M%RG>7kpB7e{l0wthL(B1|3C#le-V@AVE_QtsRkb4V6q1Q O0000fzww@9*!fs;R=gys4z3>+9;`;^Oe{@9^*L^YZbkrljlZ>*M3&@$m2L>+9s? zvw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&HL=;zbZ)6vw{pPrq~#>JFd(f*Q24Iy0o&Upr5U$rOd{}5gA^>00001bW%=J06^y0W&i*Il1W5CR5;6Z(^pf2 zKoo}I6E%W}N@Njpk4$I3fxT zOskh$!bC)+13Q^ag-Rw36A_gFI956x9;95^LBzm8Mis;1BAY=(j-!yvbHas_mYsx& zuPKQl*C1jxW3WMt7Y_2WBRc?I>pufe^hq{@LSF$(xrMs$8AvQ3Oz77%4PQ&E4bOcq z7I;3P16}8Q2YrAP0utb0P~r`ShK7rp?*$%yYRR%(yoFV5WOQtNVsdJFW_At{Dwy-O z?Yb$~t|l!kE-kODuC13g5b;siRP5T6t?iw%QrRsrWPpSQd)@m7O6BmViVPP4j+I<( z4cjNDXXhn`Py^tiez0|Ub$wG}2s1*1+q?URM}|;?{ufVW_1Q4!^@16%Z|_xv8uT1K yKI{AXW(YOlw=lwtzjF>k4X7<_Kz(83_v8o7|B&yS(h6z-0000|_zC&mB( literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/label.png b/comp/src/gb.report/img/control/label.png new file mode 100644 index 0000000000000000000000000000000000000000..7c73e49ff4bcec6c259224ba4ec6ff89e9334bce GIT binary patch literal 466 zcmV;@0WJQCP)kdg0004*NklbO=!sJ`Fy-_?LYs(;F zv-|z&tegtobNcGv%xwNs1${L*U^*-LU_tD#H@aUfEujjGF%iA#vJy^c5x@D}iiq8o zl<-Epr^urinzt0e9r2wavoZ9f48a$%-Cu|t(-|U(1RFp>9-dKoBy+Y-k)WX;=Gi{4WH50LgV@+X?A6xc~qF07*qo IM6N<$f?43scmMzZ literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/picturebox.png b/comp/src/gb.report/img/control/picturebox.png new file mode 100644 index 0000000000000000000000000000000000000000..a25a9f4e302f5e8d2abcf6f1d3264f06d8f9c40a GIT binary patch literal 2381 zcmV-T39|NyP) zX>e8L8OML`S?<1Mzgccb2q7$ifNX*h5T&3fTSZ(^sBJ|pR;RT(>P#()%UD-Ns$&a+ zD9U04WKmEe2qI!sAYn@aArQ#Uy~(|~+c|GPARyItYCHYVY5$+jGv|4q`JMm!Jnwtp ze_i6AP0a86upWzJwb|0UYWLo2{Ko+@a!0D+P~a*FHwdAXf|lF+iigyLNuXq;)rpU>_$qpzab0EtjkRLy~AaiUqA=vEiHJ(vB@eXKO)2}g0dn$3h}M>)^AO^}>>ec|J-3KZr-`nGks^suG!-EfRNaAQbz!l2 zvDlNT+Poe`AGX9|@!!!{T^jk90j!R+m-73Kn3LCc7%5r#sH%>X28ycF)fu3^x*j3o z1cG)XHZ+?Xi_MGGnSs@j%AO6o7Pr^mG4s1M?|bW{rRikIIdo`x=-yG@*!KW#ynZ$U z!K$^(XKK2-Xv&)%I`m98j)Wrs-CW>&JzfwUbiI0b(<@ssFr|kcUFH^Hm3?e6b+t8^RWY3!iXH1 zde8HIMNu(L6QL*wArL}2Ln_XgWf%#X8yZm*gH&G@LQ$|-Y-pN7vfD*|kIYERvHGaf zVOz1{y+e<0TQ;NaZ@+rT_<3nIcXFoP<36gX-ee3ZFPr?U=lnuZ=?r#KU3HX#UfI|j z4ouU;YO|xL+8GEb@%gjQ6_e5=($#@OM38O=9fs4^77r)p{e03jE>CiF_~iK;z3I7| zKlt;MbG@e6k5?gfR?3WyKMkE*-nm zi4Y(Z6(JP9H3TUQ!ofDSuUpK%ogZMaB~f(I2up@P)wN^&Du4a{b+fYy#`NiP$&8Mg zij|485W|LC$%(41amDL)A|;cmzOJG2i?8VjhVXbTcvCzyH-<4ylVE#0rjfuf66dz! zYzs>%sjJ*U?Vhb1sr-!1OCP5*5a;sg_mYzBZwB~uYY0qPgNNW!in1-Lq+X{Kq;1#X`(>;CiJBfs+~&qo5h$!TmV zm*1pM95K0khEn&`6Z2X6_8Q_24}yS_rn^CN?uxZ~s)Mq(!LQ;-8E%(`E>ush@hkr0AS%in9adip$+IezxS zZ4WzR0HScvbiE^RGI+%eciKK&JnvTD#W%g7_v?l143VfkhGkHH%utA?s-1*ttI&sB zgi&3KG!2BVqeNmjJEF;ncxZ^jo|b;a^{ zX5pxvs;XV4DC$>gB90m8K(X7fjU5T9ijtN>N@EkYhE9}y0i-jN_V&Zb+)VTX4TMvy z?W#R}vuU+#=HddRreM|`C&NDgSMfmNt<8w{-(c|5FR*Y_-UZHlYnxaN;7ToN_SGLl z358Je{GgtJPn>SYHgpj7yzZEJDFkFIGS!Py^^oz4yAXWLy3NGajRU*{cXm zCD9SY;>*I5Q-HH~3D(RUVh!~uQb9{e#q3I8A2SlWY2yB-l0Y;-%la}r!%DICAIjbp zuQ74$Vb)IUc@97Y=xD78+dV!%cujqDpMB#CdvI#omqhkdA&#HK5;D-b4BXv|Nf|x{ z=fELoZVw1S2wBzOq+NG6_aOBKBn+Iz{J^aiKCU3dvBXP zt$6jjn>lv0j$FT+qM{r;N$G5O_w9ZMzbvZ+`1Sn799+JJXvjk#GaGqP2}-sPRF%Z% z73A*QODGZ}>~_-`Gf2XqTap8JzQ)E4Tj5{b| zCC!Id#!^y~Z#i*f&kFv5Mn3a8iD-b+P4%SuN(e?<*)#XP3vvcMy$C(KJFe_*d~`*@ z4;LCR`qAs>zFNL+==8Ev112qzj-*~6^CL9vxn8 zdqd>+zI3?PH>Q`zH)*3gefT9m2H^ktuhU-vP5hv;;=4p&00000NkvXXu0mjfecGPf literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/vbox.png b/comp/src/gb.report/img/control/vbox.png new file mode 100644 index 0000000000000000000000000000000000000000..07004bb15185d11b45ea7631afd59b025b5d220b GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{S0MfW|NjFA4m310fY=NS zB|fH8fGn1hAirQB&9HfL+AJV7+0(@_MC1I@iH>}S6?j;Dm)zUT{w_h2{{{c2*059m z_g^wqIT)}+ZkNn~(-|8#ylH;oZEZD?B!eZmjZzx<~rnEmg45%mS+Oix!omvv4FO#p&DT9p6* literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/vpanel.png b/comp/src/gb.report/img/control/vpanel.png new file mode 100644 index 0000000000000000000000000000000000000000..64178f994a00f5e6077488054dcee92106a1b5bc GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*Z=?j1DOpC4gZ0Nf#KDH zIr2b0b4ie2Fp&OLcH|zAO7(Pc4AD5BY~aSq#?#hhpunKiz{KOxFi}vYVTn_Npf4+P zz=4j&qpTbYR9sjmJY`T-WODFi)8I5^Uc%tibe6%Zi8m#&VYS1?)(s4+tR*hoJ=pM$ pk8RhEWQD`bz6{FWSOXgw7}nlVFYj8dc^haUgQu&X%Q~loCIFnAJpcdz literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/logo.svg b/comp/src/gb.report/img/logo.svg new file mode 100644 index 00000000..31e20aa0 --- /dev/null +++ b/comp/src/gb.report/img/logo.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/comp/src/gb.report/printer1.png b/comp/src/gb.report/printer1.png new file mode 100644 index 0000000000000000000000000000000000000000..a6ceca7f115a77b989b77315c9d62abff9dbeaf5 GIT binary patch literal 1630 zcmV-k2BGxclxJUNPw!?gxu z3`(i35#akiTI+Sf4N7Ya{Qm{44xf9*7?cXPNw_z$=LN_^hYo#bQxQ0R{P=Ug zKBd%~FTcF^l-3%fSr=S~a}f~2igk8&eiDnte(Sn!FVJ`S^5xPK1)Ml>qFpKVy8{Of zy>{Thw?$*)Gq|qH)vJHS7>#Cq@VOL#=Xt#GMvwK@TW`Oc&1T;n9==6eTl@VRHwHd( zUH8|$y}geX1ax(zkcS-PwYs8SfA09{>2J5#AtgEYpxR$E&`B(icr zDGjwXh3%YC6Xt5qFE*I2EtJ^L!$XAag&D-847t4j=B4AAb13 z5h;XtCK`=mE$OSZ!Sl+@%uG{qU*L9r&gq44J7-AGT1yW3)gm`%CAAaBiK3LIwI)ex zO>)7PNbJD2?G~9xB_5d)m-rhu2m5bH}%d!~FZ>O&x9uzhX2Qav#MhhHkND_$%d_QDz zGRxT56zTK`mSshyl+i~3p68QF4FeF5S5Q}1&5ynn=eglDV;RLn&M;GokPmE%%BG}k z%ElprB4(ATi1<`WpKZ2Jt!qeb7c^B1UTAG(WMq=5sT^UrSR+U&QA*9oQmHTziHN<{ zl3D0^KKJfDpb|J%2i8t-T?f~V5s8$DLDppb9^2#fn%ifjCC4wM$D1pf!DB(DZ zo6Wb#|d2rs>~f5BN>o8ait*GVRuNG6k; zVPgz5H#c+h<}mg3wZ!8NvDlmdr9wtVZrz=pp1vn9Uc7kMaVq|E>eLV0<~NMpyZ2CC zT~9KZAepS$6kI8d5Ee&{bko>akCcM>>V9zW2IJ%7e* za+`#kuUCU22ts!6Zee8P9$=BpW*8j2!Ka`0{_Fbn>%RfiLUQBrxO4E}!JkSgcQrIL zwZHSuFOTitzh@J0z2s6&?F@xAvx_*E`f zFdH$gwF!dIc%E+x1<&Mi1(V6-O`+hKYuEl^y1Kf*zZz@RqV(Bk8-7+*IGjC@WAG^3pFwMf WlJR=e?HWLX7(8A5T-G@yGywoT?lcqt literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/.hidden/control/reportpagebreak.png b/comp/src/gb.report2/.hidden/control/reportpagebreak.png new file mode 100644 index 0000000000000000000000000000000000000000..b4de50ef05d84aeed795caf6cc42f1e2c881d6da GIT binary patch literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!I14-?iy0XBj({-ZRBb+KpkQWz zPl&5~flpdmT5N1=LPA1lXlQP3Zao+P<$>Z^Sy}O5keHYVQ~(4(22dFgKtzC&5OIhU zkc^Iw29n9i$w2K8ssBL0z|axnSOzpyp(MyJn1PFX1sgjP3nMGf?$sN&>{!RieC)vC zy&Ovx&gByj;-5ABP{0kK+8LfMjv*0;-(KG-)?^^y_VBb^fxBQ#;QjxKN163*7$z&H z|5LmAca;dI_|qSZ1)*mcn0X8iFfbqEx--l3oFQWwKaatK{|)P}*DYXqHJR^5aR{UJ z@kr(nK0Ypmjz%9opU=yVi!+~oa`CZeW9044LQJ2xyzNXmp!BB7NzkBkp6yDO8`IX$ tb79EYQ|@o`=X2~2dD}dL4d-6GUCX z$@}~J?d|Q@*x2Xi=hD*B_V)JE)6>n(&GGT^&d$#A^77r?-SzeL=;-Lh#l_s*-0bY^ z-rnBV*VoF*%KZHN88VIj0002ONklQspE5J?UtTGtDylz2&xzutXazdOY z(lGTaCva}XX|(x`Sk}@wCu!`2XzGYR?*m;rK@Jc#8xSBt2 zEw+g*_8Gy0gdSykzK5w7g{dCmeE4ReJXG3AW2M?arMeT=`mpY$7t0@9u5*7Qg|~75 d`~jZ--mmPlbHZH>nYRD{002ovPDHLkV1h!8#LfT! literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/.hidden/control/reporttextlabel.png b/comp/src/gb.report2/.hidden/control/reporttextlabel.png new file mode 120000 index 00000000..2fec7698 --- /dev/null +++ b/comp/src/gb.report2/.hidden/control/reporttextlabel.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textlabel.png \ No newline at end of file diff --git a/comp/src/gb.report2/.hidden/control/reportvbox.png b/comp/src/gb.report2/.hidden/control/reportvbox.png new file mode 120000 index 00000000..50d7662d --- /dev/null +++ b/comp/src/gb.report2/.hidden/control/reportvbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/vbox.png \ No newline at end of file diff --git a/comp/src/gb.report2/.hidden/control/reportview.png b/comp/src/gb.report2/.hidden/control/reportview.png new file mode 120000 index 00000000..a34539f3 --- /dev/null +++ b/comp/src/gb.report2/.hidden/control/reportview.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/documentview.png \ No newline at end of file diff --git a/comp/src/gb.report2/.hidden/control/reportvpanel.png b/comp/src/gb.report2/.hidden/control/reportvpanel.png new file mode 120000 index 00000000..41a8d261 --- /dev/null +++ b/comp/src/gb.report2/.hidden/control/reportvpanel.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/vpanel.png \ No newline at end of file diff --git a/comp/src/gb.report2/.icon.png b/comp/src/gb.report2/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d7da24ccda9dd2a98f121a28ac04b157136fc52e GIT binary patch literal 6238 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 Q>w^JcbiqWwOb;9VA5256t^fc4 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/.lang/de.po b/comp/src/gb.report2/.lang/de.po new file mode 100644 index 00000000..f99a69d7 --- /dev/null +++ b/comp/src/gb.report2/.lang/de.po @@ -0,0 +1,222 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.17.90\n" +"PO-Revision-Date: 2022-07-26 07:53 UTC\n" +"Last-Translator: gian \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# gb-ignore +#: FPreview.form:194 +msgid "100 %" +msgstr "" + +# gb-ignore +#: FPreview.form:397 +msgid "A3" +msgstr "" + +# gb-ignore +#: FPreview.form:397 +msgid "A4" +msgstr "" + +# gb-ignore +#: FPreview.form:397 +msgid "A5" +msgstr "" + +# gb-ignore +#: FPreview.form:397 +msgid "B5" +msgstr "" + +#: FPreview.class:228 +msgid "Cancel" +msgstr "Abbruch" + +#: FPreview.form:467 +msgid "Collate copies" +msgstr "Kopien sortieren" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Kopien" + +#: FPreview.form:397 +msgid "Custom" +msgstr "Benutzerdefiniert" + +# gb-ignore +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +# gb-ignore +#: FPreview.form:397 +msgid "Executive" +msgstr "" + +#: FPreview.form:275 +msgid "File" +msgstr "Datei" + +#: FPreview.form:462 +msgid "Full page" +msgstr "Ganze Seite" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Volle Breite" + +#: FPreview.form:456 +msgid "Grayscale" +msgstr "Graustufen" + +#: FPreview.form:433 +msgid "Height" +msgstr "Höhe" + +#: FPreview.form:380 +msgid "Landscape" +msgstr "Querformat" + +# gb-ignore +#: FPreview.class:343 +msgid "Layout..." +msgstr "" + +# gb-ignore +#: FPreview.form:397 +msgid "Legal" +msgstr "" + +#: FPreview.form:397 +msgid "Letter" +msgstr "Brief" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lange Kante" + +# gb-ignore +#: FPreview.form:423 +msgid "mm" +msgstr "" + +#: FPreview.form:271 +msgid "None" +msgstr "Nein" + +#: FPreview.form:145 +msgid "One page" +msgstr "Eine Seite" + +#: FPreview.form:373 +msgid "Orientation" +msgstr "Orientierung" + +#: FPreview.form:390 +msgid "Paper" +msgstr "Papier" + +#: FPreview.class:213 +msgid "PDF files" +msgstr "PDF-Dateien" + +#: FPreview.form:380 +msgid "Portrait" +msgstr "Hochformat" + +#: FPreview.class:213 +msgid "Postscript files" +msgstr "Postscript-Dateien" + +#: FPreview.form:208 +msgid "Print" +msgstr "Drucken" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Drucker" + +#: FPreview.form:115 +msgid "Printing" +msgstr "Drucke" + +#: FPreview.class:357 +msgid "Printing..." +msgstr "Drucke..." + +#: FPreview.form:86 +msgid "Print to file" +msgstr "Drucke in Datei" + +#: FPreview.form:336 +msgid "Range" +msgstr "Seiten" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Wahre Größe" + +#: FPreview.class:318 +msgid "Replace" +msgstr "Ersetzen" + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Report-Vorschau" + +# gb-ignore +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "Auflösung" + +#: FPreview.form:472 +msgid "Reverse order" +msgstr "Umgekehrte Reihenfolge" + +#: Report.class:123 +msgid "Section " +msgstr "Sektion" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Kurze Kante" + +#: FPreview.form:216 +msgid "Show options" +msgstr "Zeige Optionen" + +#: FPreview.class:318 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "Die Datei existiert bereits.\nSoll sie ersetzt werden?" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "Zweiseitig" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Zwei Seiten" + +#: FPreview.form:412 +msgid "Width" +msgstr "Breite" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "Reinzoomen" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "Rauszoomen" + diff --git a/comp/src/gb.report2/.lang/es.po b/comp/src/gb.report2/.lang/es.po new file mode 100644 index 00000000..b1d24335 --- /dev/null +++ b/comp/src/gb.report2/.lang/es.po @@ -0,0 +1,343 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.8.90\n" +"PO-Revision-Date: 2015-09-20 17:58 UTC\n" +"Last-Translator: Jesus \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "" + +#: FPreview.form:115 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:145 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "" + +#: FPreview.form:193 +msgid "100 %" +msgstr "" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "" + +#: FPreview.form:208 +msgid "Print" +msgstr "Imprimir" + +#: FPreview.form:216 +msgid "Show options" +msgstr "" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:264 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:271 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:275 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "" + +#: FPreview.form:308 +msgid "75" +msgstr "" + +#: FPreview.form:308 +msgid "150" +msgstr "" + +#: FPreview.form:308 +msgid "300" +msgstr "" + +#: FPreview.form:308 +msgid "600" +msgstr "" + +#: FPreview.form:308 +msgid "1200" +msgstr "" + +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientación" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Paisaje" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:396 +msgid "Custom" +msgstr "" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "" + +#: FPreview.form:461 +msgid "Full page" +msgstr "" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.class:211 +msgid "PDF files" +msgstr "" + +#: FPreview.class:211 +msgid "Postscript files" +msgstr "" + +#: FPreview.class:226 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:316 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "" + +#: FPreview.class:316 +msgid "Replace" +msgstr "" + +#: FPreview.class:341 +msgid "Layout..." +msgstr "Disposición..." + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "Fecha" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:112 +msgid "Section " +msgstr "Sección" + +#: Report1.report:28 +msgid "toto" +msgstr "" + +#: Report11.report:19 +msgid "Section 1" +msgstr "" + +#: Report11.report:39 +msgid "Gambas" +msgstr "-" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "" + +#: Report11.report:93 +msgid "DEMO" +msgstr "" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "" + +#: Report3.report:17 +msgid "PARCELLAIRE" +msgstr "" + +#: Report5.report:20 +msgid "Coucou" +msgstr "" + +#: Report51.report:30 +msgid "Recto" +msgstr "" + +#: Report51.report:38 +msgid "Verso" +msgstr "" + +#: Report51.report:51 +msgid "N°" +msgstr "" + +#: Report51.report:99 +msgid "Observations" +msgstr "" + +#: Report6.report:12 +msgid "Testtest

    \n\n
    " +msgstr "" + diff --git a/comp/src/gb.report2/.lang/es_ES.po b/comp/src/gb.report2/.lang/es_ES.po new file mode 100644 index 00000000..62698041 --- /dev/null +++ b/comp/src/gb.report2/.lang/es_ES.po @@ -0,0 +1,343 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.8.90\n" +"PO-Revision-Date: 2015-09-20 17:57 UTC\n" +"Last-Translator: Jesus \n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "" + +#: FPreview.form:115 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:145 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "" + +#: FPreview.form:193 +msgid "100 %" +msgstr "" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "" + +#: FPreview.form:208 +msgid "Print" +msgstr "Imprimir" + +#: FPreview.form:216 +msgid "Show options" +msgstr "" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:264 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:271 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:275 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "" + +#: FPreview.form:308 +msgid "75" +msgstr "" + +#: FPreview.form:308 +msgid "150" +msgstr "" + +#: FPreview.form:308 +msgid "300" +msgstr "" + +#: FPreview.form:308 +msgid "600" +msgstr "" + +#: FPreview.form:308 +msgid "1200" +msgstr "" + +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientación" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Paisaje" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:396 +msgid "Custom" +msgstr "" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "" + +#: FPreview.form:461 +msgid "Full page" +msgstr "" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.class:211 +msgid "PDF files" +msgstr "" + +#: FPreview.class:211 +msgid "Postscript files" +msgstr "" + +#: FPreview.class:226 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:316 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "" + +#: FPreview.class:316 +msgid "Replace" +msgstr "" + +#: FPreview.class:341 +msgid "Layout..." +msgstr "Disposición..." + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "Fecha" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:112 +msgid "Section " +msgstr "Sección" + +#: Report1.report:28 +msgid "toto" +msgstr "" + +#: Report11.report:19 +msgid "Section 1" +msgstr "" + +#: Report11.report:39 +msgid "Gambas" +msgstr "-" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "" + +#: Report11.report:93 +msgid "DEMO" +msgstr "" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "" + +#: Report3.report:17 +msgid "PARCELLAIRE" +msgstr "" + +#: Report5.report:20 +msgid "Coucou" +msgstr "" + +#: Report51.report:30 +msgid "Recto" +msgstr "" + +#: Report51.report:38 +msgid "Verso" +msgstr "" + +#: Report51.report:51 +msgid "N°" +msgstr "" + +#: Report51.report:99 +msgid "Observations" +msgstr "" + +#: Report6.report:12 +msgid "Testtest

    \n\n
    " +msgstr "" + diff --git a/comp/src/gb.report2/.lang/fr.po b/comp/src/gb.report2/.lang/fr.po new file mode 100644 index 00000000..22e34629 --- /dev/null +++ b/comp/src/gb.report2/.lang/fr.po @@ -0,0 +1,389 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.10.90\n" +"POT-Creation-Date: 2020-11-18 18:39 UTC\n" +"PO-Revision-Date: 2017-09-30 01:38 UTC\n" +"Last-Translator: benoit \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.class:210 +msgid "PDF files" +msgstr "Fichiers PDF" + +#: FPreview.class:210 +msgid "Postscript files" +msgstr "Fichiers Postscript" + +#: FPreview.class:225 +msgid "Cancel" +msgstr "Annuler" + +#: FPreview.class:315 +msgid "Replace" +msgstr "Remplacer" + +#: FPreview.class:315 +msgid "" +"This file already exists.\n" +"Do you want to replace it?" +msgstr "" +"Ce fichier existe déjà.\n" +"Voules-vous le remplacer ?" + +#: FPreview.class:340 +msgid "Layout..." +msgstr "Mise en page..." + +#: FPreview.class:354 +msgid "Printing..." +msgstr "Impression en cours..." + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Aperçu de l'état" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "Imprimer dans un fichier" + +#: FPreview.form:115 +#, fuzzy +msgid "Printing" +msgstr "Impression en cours..." + +#: FPreview.form:145 +msgid "One page" +msgstr "Une page" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Deux pages" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Pleine largeur" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Taille réelle" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "Réduire" + +#: FPreview.form:193 +msgid "100 %" +msgstr "-" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "Agrandir" + +#: FPreview.form:208 +msgid "Print" +msgstr "Imprimer" + +#: FPreview.form:216 +msgid "Show options" +msgstr "Afficher les options" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Imprimante" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Côté long" + +#: FPreview.form:271 +msgid "None" +msgstr "Aucun" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Côté court" + +#: FPreview.form:275 +msgid "File" +msgstr "Fichier" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "" + +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "Plage" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Copies" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientation" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Paysage" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Portrait" + +#: FPreview.form:389 +msgid "Paper" +msgstr "-" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Custom" +msgstr "Personnalisé" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Largeur" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Hauteur" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "Niveaux de gris" + +#: FPreview.form:461 +msgid "Full page" +msgstr "Pleine page" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "Assembler les copies" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Inverser" + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "" + +#: Report.class:120 +msgid "Section " +msgstr "" + +#: Report1.report:29 +msgid "=\"Page \" & Page" +msgstr "" + +#: Report1.report:46 +msgid "=pi() + 4" +msgstr "" + +#: Report10.report:22 Report14.report:53 +msgid "=index" +msgstr "" + +#: Report10.report:31 +msgid "=Index" +msgstr "" + +#: Report10.report:46 +msgid "=\"Index = \" & index" +msgstr "" + +#: Report10.report:54 +msgid "=page & \" / \" & pages" +msgstr "" + +#: Report11.report:19 Report12.report:17 +msgid "Section 1" +msgstr "" + +#: Report11.report:39 +msgid "Gambas" +msgstr "" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "" + +#: Report11.report:93 +msgid "DEMO" +msgstr "" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "" + +#: Report13.report:36 +msgid "=\"GAMBAS - \" & index" +msgstr "" + +#: Report13.report:44 +msgid "=\"PAGE \" & Page & \" / \" & pages" +msgstr "" + +#: Report14.report:28 +msgid "Reference" +msgstr "" + +#: Report14.report:35 +msgid "Description" +msgstr "" + +#: Report14.report:42 +msgid "Valeur" +msgstr "" + +#: Report14.report:73 +msgid "=page & \" on \" & pages" +msgstr "" + +#: Report16.report:21 Report17.report:22 Report5.report:20 +msgid "Coucou" +msgstr "" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "" + +#: Report3.report:18 +msgid "PARCELLAIRE $NPAGE" +msgstr "" + +#: Report51.report:30 +msgid "Recto" +msgstr "" + +#: Report51.report:38 +msgid "Verso" +msgstr "" + +#: Report51.report:51 +msgid "N°" +msgstr "" + +#: Report51.report:99 +#, fuzzy +msgid "Observations" +msgstr "Orientation" + +#: Report6.report:12 +msgid "" +"Testtest

    \n" +"\n" +"
    " +msgstr "" + +#~ msgid "75" +#~ msgstr "-" + +#~ msgid "300" +#~ msgstr "-" + +#~ msgid "150" +#~ msgstr "-" diff --git a/comp/src/gb.report2/.lang/it.po b/comp/src/gb.report2/.lang/it.po new file mode 100644 index 00000000..d22a0750 --- /dev/null +++ b/comp/src/gb.report2/.lang/it.po @@ -0,0 +1,211 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.12.90\n" +"PO-Revision-Date: 2019-01-15 14:42 UTC\n" +"Last-Translator: gian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "-" + +#: FPreview.class:212 +msgid "PDF files" +msgstr "File PDF" + +#: FPreview.class:212 +msgid "Postscript files" +msgstr "File Postscript" + +#: FPreview.class:227 +msgid "Cancel" +msgstr "Annulla" + +#: FPreview.class:317 +msgid "Replace" +msgstr "Sostituisci" + +#: FPreview.class:317 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "Questo file già esiste.\nDesideri sostituirlo?" + +#: FPreview.class:342 +msgid "Layout..." +msgstr "-" + +#: FPreview.class:354 +msgid "Printing..." +msgstr "Stampa..." + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Anteprima del report" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "Stampa su file" + +#: FPreview.form:115 +msgid "Printing" +msgstr "Stampa" + +#: FPreview.form:145 +msgid "One page" +msgstr "Una pagina" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Due pagine" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Intera larghezza" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Dimensione reale" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "Rimpicciolisci" + +#: FPreview.form:193 +msgid "100 %" +msgstr "-" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "Ingrandisci" + +#: FPreview.form:208 +msgid "Print" +msgstr "Stampa" + +#: FPreview.form:216 +msgid "Show options" +msgstr "Mostra le opzioni" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Stampante" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "Fronte retro" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lato lungo" + +#: FPreview.form:271 +msgid "None" +msgstr "Nessuno" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Lato corto" + +#: FPreview.form:275 +msgid "File" +msgstr "-" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "Risoluzione" + +#: FPreview.form:313 +msgid "dpi" +msgstr "-" + +#: FPreview.form:336 +msgid "Range" +msgstr "Pagina" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Copie" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientamento" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Orizzontale" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Verticale" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Carta" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Custom" +msgstr "Personalizzato" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "Legale" + +#: FPreview.form:396 +msgid "Letter" +msgstr "Lettera" + +#: FPreview.form:411 +msgid "Width" +msgstr "Larghezza" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Altezza" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "Scala di grigi" + +#: FPreview.form:461 +msgid "Full page" +msgstr "Pagina intera" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "Assembla le copie" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Ordine inverso" + +#: Report.class:112 +msgid "Section " +msgstr "Sezione" + diff --git a/comp/src/gb.report2/.lang/nl.po b/comp/src/gb.report2/.lang/nl.po new file mode 100644 index 00000000..fa0105e7 --- /dev/null +++ b/comp/src/gb.report2/.lang/nl.po @@ -0,0 +1,212 @@ +# Willy Raets < gbWilly@openmailbox.org >, 2015 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.12.2\n" +"PO-Revision-Date: 2019-04-06 18:43 UTC\n" +"Last-Translator: gbWilly \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "Rapport Evolutie" + +#: FPreview.class:212 +msgid "PDF files" +msgstr "PDF bestanden" + +#: FPreview.class:212 +msgid "Postscript files" +msgstr "Postscript bestanden" + +#: FPreview.class:227 +msgid "Cancel" +msgstr "Annuleer" + +#: FPreview.class:317 +msgid "Replace" +msgstr "Vervangen" + +#: FPreview.class:317 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "Dit bestand bestaat reeds.\nWil je het vervangen?" + +#: FPreview.class:342 +msgid "Layout..." +msgstr "-" + +#: FPreview.class:354 +msgid "Printing..." +msgstr "Afdrukken..." + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Rapport voorbeeld" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "Afdrukken naar bestand" + +#: FPreview.form:115 +msgid "Printing" +msgstr "Afdrukken" + +#: FPreview.form:145 +msgid "One page" +msgstr "Een pagina" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Twee pagina's" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Volledige breedte" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Echte afmeting" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "Zoom uit" + +#: FPreview.form:193 +msgid "100 %" +msgstr "-" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "-" + +#: FPreview.form:208 +msgid "Print" +msgstr "Afdrukken" + +#: FPreview.form:216 +msgid "Show options" +msgstr "Opties weergeven" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Printer" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "Dubbelzijdig" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lange zijde" + +#: FPreview.form:271 +msgid "None" +msgstr "Geen" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Korte zijde" + +#: FPreview.form:275 +msgid "File" +msgstr "Bestand" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "Resolutie" + +#: FPreview.form:313 +msgid "dpi" +msgstr "-" + +#: FPreview.form:336 +msgid "Range" +msgstr "Bereik" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Kopieën" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Oriëntatie" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Landschap" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Portret" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Papier" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Custom" +msgstr "Aangepast" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Breedte" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Hoogte" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "Grijswaarden" + +#: FPreview.form:461 +msgid "Full page" +msgstr "Volledige pagina" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "Sorteer kopieën" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Omgekeerde volgorde" + +#: Report.class:120 +msgid "Section " +msgstr "Sectie" + diff --git a/comp/src/gb.report2/.lang/pt_BR.po b/comp/src/gb.report2/.lang/pt_BR.po new file mode 100644 index 00000000..a4fc8869 --- /dev/null +++ b/comp/src/gb.report2/.lang/pt_BR.po @@ -0,0 +1,221 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.14.90\n" +"PO-Revision-Date: 2020-06-12 05:32 UTC\n" +"Last-Translator: Gen Braga \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# gb-ignore +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Visualizar relatório" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "Imprimir para arquivo" + +#: FPreview.form:115 +msgid "Printing" +msgstr "Imprimindo" + +#: FPreview.form:145 +msgid "One page" +msgstr "Uma página" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Duas páginas" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Largura cheia" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Tamanho real" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "Menos zoom" + +# gb-ignore +#: FPreview.form:193 +msgid "100 %" +msgstr "" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "Mais zoom" + +#: FPreview.form:208 +msgid "Print" +msgstr "Imprimir" + +#: FPreview.form:216 +msgid "Show options" +msgstr "Exibir opções" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Impressora" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "Dois lados" + +#: FPreview.form:271 +msgid "None" +msgstr "Nenhum" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Lado curto" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lado longo" + +#: FPreview.form:275 +msgid "File" +msgstr "Arquivo" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "Resolução" + +# gb-ignore +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "Intervalo" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Cópias" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientação" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Paisagem" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:396 +msgid "Custom" +msgstr "Personalizado" + +# gb-ignore +#: FPreview.form:396 +msgid "A3" +msgstr "" + +# gb-ignore +#: FPreview.form:396 +msgid "A4" +msgstr "" + +# gb-ignore +#: FPreview.form:396 +msgid "A5" +msgstr "" + +# gb-ignore +#: FPreview.form:396 +msgid "B5" +msgstr "" + +#: FPreview.form:396 +msgid "Letter" +msgstr "Carta" + +#: FPreview.form:396 +msgid "Executive" +msgstr "Executivo" + +# gb-ignore +#: FPreview.form:396 +msgid "Legal" +msgstr "" + +#: FPreview.form:411 +msgid "Width" +msgstr "Largura" + +# gb-ignore +#: FPreview.form:422 +msgid "mm" +msgstr "" + +#: FPreview.form:432 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "Escala de cinza" + +#: FPreview.form:461 +msgid "Full page" +msgstr "Página cheia" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "Agrupar cópias" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Ordem inversa" + +#: FPreview.class:212 +msgid "PDF files" +msgstr "Arquivos PDF" + +#: FPreview.class:212 +msgid "Postscript files" +msgstr "Arquivos postscript" + +#: FPreview.class:227 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:317 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "Este arquivo já existe. Sobrescrever?" + +#: FPreview.class:317 +msgid "Replace" +msgstr "Substituir" + +# gb-ignore +#: FPreview.class:342 +msgid "Layout..." +msgstr "" + +#: FPreview.class:356 +msgid "Printing..." +msgstr "Imprimindo..." + +#: Report.class:120 +msgid "Section " +msgstr "Seção" + diff --git a/comp/src/gb.report2/.lang/ru.po b/comp/src/gb.report2/.lang/ru.po new file mode 100644 index 00000000..00f83943 --- /dev/null +++ b/comp/src/gb.report2/.lang/ru.po @@ -0,0 +1,396 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: comp/src/gb.report2/.project:20 +msgid "ReportsEvolution" +msgstr "Эволюция отчётов" + +#: comp/src/gb.report2/.src/Report.class:120 comp/src/gb.report2/.src/Report.class:368 +msgid "Section " +msgstr "Раздел " + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:212 +msgid "PDF files" +msgstr "Файлы PDF" + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:212 +msgid "Postscript files" +msgstr "Файлы Postscript" + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:227 comp/src/gb.report2/.src/Viewer/FPreview.class:317 +msgid "Cancel" +msgstr "Отмена" + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:232 comp/src/gb.report2/.src/Viewer/FPreview.form:114 comp/src/gb.report2/.src/Viewer/FPreview.form:346 +msgid "Print" +msgstr "Печать" + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:317 +msgid "" +"This file already exists.\n" +"Do you want to replace it?" +msgstr "" +"Этот файл уже существует.\n" +"Вы хотите заменить его?" + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:317 +msgid "Replace" +msgstr "Заменить" + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:342 +msgid "Layout..." +msgstr "Макет..." + +#: comp/src/gb.report2/.src/Viewer/FPreview.class:354 +msgid "Printing..." +msgstr "Печать..." + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:5 +msgid "Report preview" +msgstr "Предпросмотр отчёта" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:10 +msgid "Print to file" +msgstr "Печать в файл" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:34 +msgid "Printing" +msgstr "Печать" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:59 +msgid "One page" +msgstr "Одна страница" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:67 +msgid "Two pages" +msgstr "Две страницы" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:75 +msgid "Full width" +msgstr "Полная ширина" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:83 +msgid "Real size" +msgstr "Реальный размер" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:90 +msgid "Zoom out" +msgstr "Уменьшить" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:101 +msgid "100 %" +msgstr "100%" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:106 +msgid "Zoom in" +msgstr "Увеличить" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:121 +msgid "Show options" +msgstr "Показать параметры" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:141 +msgid "Printer" +msgstr "Принтер" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:161 +msgid "Two-sided" +msgstr "Двусторонний" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:167 +msgid "None" +msgstr "Нет" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:167 +msgid "Short side" +msgstr "Короткая сторона" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:167 +msgid "Long side" +msgstr "Длинная сторона" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:171 +msgid "File" +msgstr "Файл" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:192 +msgid "Resolution" +msgstr "Разрешение" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:203 +msgid "dpi" +msgstr "dpi" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:222 +msgid "Range" +msgstr "Диапазон" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:235 +msgid "Copies" +msgstr "Копии" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:251 +msgid "Orientation" +msgstr "Ориентация" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:257 +msgid "Portrait" +msgstr "Книжная" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:257 +msgid "Landscape" +msgstr "Альбомная" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:265 +msgid "Paper" +msgstr "Бумага" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "Custom" +msgstr "Пользовательский" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "A3" +msgstr "А3" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "A4" +msgstr "A4" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "A5" +msgstr "А5" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "B5" +msgstr "B5" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "Letter" +msgstr "Лист" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "Executive" +msgstr "Экзекьютив" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:271 +msgid "Legal" +msgstr "Легал" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:283 +msgid "Width" +msgstr "Ширина" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:292 comp/src/gb.report2/.src/Viewer/FPreview.form:309 +msgid "mm" +msgstr "мм" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:300 +msgid "Height" +msgstr "Высота" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:319 +msgid "Grayscale" +msgstr "Оттенки серого" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:324 +msgid "Full page" +msgstr "Полная страница" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:328 +msgid "Collate copies" +msgstr "Собрать по копиям" + +#: comp/src/gb.report2/.src/Viewer/FPreview.form:332 +msgid "Reverse order" +msgstr "Обратный порядок" + +#: comp/src/gb.report2/.src/Tests/Report10.report:14 comp/src/gb.report2/.src/Tests/Report14.report:42 +msgid "=index" +msgstr "=индекс" + +#: comp/src/gb.report2/.src/Tests/Report10.report:23 +msgid "=Index" +msgstr "=Индекс" + +#: comp/src/gb.report2/.src/Tests/Report10.report:38 +msgid "=\"Index = \" & index" +msgstr "=\"Индекс = \" & индекс" + +#: comp/src/gb.report2/.src/Tests/Report10.report:46 +msgid "=page & \" / \" & pages" +msgstr "=страница & \" / \" & страницы" + +#: comp/src/gb.report2/.src/Tests/Report13.report:30 +msgid "=\"GAMBAS - \" & index" +msgstr "=\"GAMBAS - \" & индекс" + +#: comp/src/gb.report2/.src/Tests/Report13.report:38 comp/src/gb.report2/.src/Tests/Report13.report:45 +msgid "=\"PAGE \" & Page & \" / \" & pages" +msgstr "=\"СТРАНИЦА \" & Страница & \" / \" & страницы" + +#: comp/src/gb.report2/.src/Tests/Report14.report:17 +msgid "Reference" +msgstr "Ссылка" + +#: comp/src/gb.report2/.src/Tests/Report14.report:24 +msgid "Description" +msgstr "Описание" + +#: comp/src/gb.report2/.src/Tests/Report14.report:31 +msgid "Valeur" +msgstr "Значение" + +#: comp/src/gb.report2/.src/Tests/Report14.report:62 +msgid "=page & \" on \" & pages" +msgstr "=страница & \" на \" & страницы" + +#: comp/src/gb.report2/.src/Tests/Report16.report:17 comp/src/gb.report2/.src/Tests/Report17.report:19 comp/src/gb.report2/.src/Tests/old/Report5.report:16 +msgid "Coucou" +msgstr "Кукушка" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:26 +msgid "Version 1.0" +msgstr "Версия 1.0" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:33 comp/src/gb.report2/.src/Tests/old/Report51.report:39 +msgid "Date" +msgstr "Дата" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:48 +msgid "Project Title:" +msgstr "Название проекта:" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:55 +msgid "Project No.:" +msgstr "№ проекта:" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:62 +msgid "Company:" +msgstr "Компания:" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:69 +msgid "Designer:" +msgstr "Дизайнер:" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:83 +msgid "Base Plate ID:" +msgstr "Идентификатор базовой платы:" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:134 +msgid "Bearing Pressue" +msgstr "Давление подшипника" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:144 comp/src/gb.report2/.src/Tests/old/OutputReport2.report:162 +msgid "Node #" +msgstr "Узел #" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:151 comp/src/gb.report2/.src/Tests/old/OutputReport2.report:169 +msgid "Brg. Press., psi" +msgstr "Давление подшипника, psi" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:215 +msgid "Anchor Rod Tension" +msgstr "Натяжение анкерного стержня" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:225 comp/src/gb.report2/.src/Tests/old/OutputReport2.report:243 +msgid "Rod #" +msgstr "Стержень #" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:232 comp/src/gb.report2/.src/Tests/old/OutputReport2.report:250 +msgid "Tension, lbs" +msgstr "Натяжение, lbs" + +#: comp/src/gb.report2/.src/Tests/old/OutputReport2.report:302 +msgid "Page $PAGE of $NPAGE" +msgstr "Страница $PAGE из $NPAGE" + +#: comp/src/gb.report2/.src/Tests/old/Report1.report:22 +msgid "=\"Page \" & Page" +msgstr "=\"Страница \" & Страница" + +#: comp/src/gb.report2/.src/Tests/old/Report1.report:39 +msgid "=pi() + 4" +msgstr "=пи() + 4" + +#: comp/src/gb.report2/.src/Tests/old/Report11.report:7 comp/src/gb.report2/.src/Tests/old/Report12.report:7 +msgid "Section 1" +msgstr "Раздел 1" + +#: comp/src/gb.report2/.src/Tests/old/Report11.report:27 +msgid "Gambas" +msgstr "Gambas" + +#: comp/src/gb.report2/.src/Tests/old/Report11.report:47 +msgid "All friends list !" +msgstr "Список всех друзей!" + +#: comp/src/gb.report2/.src/Tests/old/Report11.report:69 +msgid "Gambas Report Demo" +msgstr "Демонстрация отчёта Gambas" + +#: comp/src/gb.report2/.src/Tests/old/Report11.report:81 +msgid "DEMO" +msgstr "ДЕМО" + +#: comp/src/gb.report2/.src/Tests/old/Report11.report:94 +msgid "Page $PAGE on $NPAGE " +msgstr "Страница $PAGE на $NPAGE " + +#: comp/src/gb.report2/.src/Tests/old/Report2.report:16 +msgid "ReportLabel1" +msgstr "Метка_отчёта_1" + +#: comp/src/gb.report2/.src/Tests/old/Report3.report:15 +msgid "PARCELLAIRE $NPAGE" +msgstr "ПОСЫЛКА $NPAGE" + +#: comp/src/gb.report2/.src/Tests/old/Report51.report:9 +msgid "Recto" +msgstr "Перёд" + +#: comp/src/gb.report2/.src/Tests/old/Report51.report:17 +msgid "Verso" +msgstr "Оборот" + +#: comp/src/gb.report2/.src/Tests/old/Report51.report:30 +msgid "N°" +msgstr "№" + +#: comp/src/gb.report2/.src/Tests/old/Report51.report:78 +msgid "Observations" +msgstr "Наблюдения" + +#: comp/src/gb.report2/.src/Tests/old/Report6.report:10 +msgid "" +"Testtest

    \n" +"\n" +"
    " +msgstr "" +"Тесттест

    \n" +"\n" +"
    " + diff --git a/comp/src/gb.report2/.lang/zh.po b/comp/src/gb.report2/.lang/zh.po new file mode 100644 index 00000000..8abe6b42 --- /dev/null +++ b/comp/src/gb.report2/.lang/zh.po @@ -0,0 +1,381 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.15.90\n" +"POT-Creation-Date: 2020-11-18 18:39 UTC\n" +"PO-Revision-Date: 2020-11-18 18:39 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "报表解决方案" + +#: FPreview.class:210 +msgid "PDF files" +msgstr "PDF文件" + +#: FPreview.class:210 +msgid "Postscript files" +msgstr "Postscript文件" + +#: FPreview.class:225 +msgid "Cancel" +msgstr "取消" + +#: FPreview.class:315 +msgid "Replace" +msgstr "替换" + +#: FPreview.class:315 +msgid "" +"This file already exists.\n" +"Do you want to replace it?" +msgstr "" +"此文件已存在。\n" +"是否替换它?" + +#: FPreview.class:340 +msgid "Layout..." +msgstr "布局..." + +#: FPreview.class:354 +msgid "Printing..." +msgstr "打印中..." + +#: FPreview.form:79 +msgid "Report preview" +msgstr "报表预览" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "打印到文件" + +#: FPreview.form:115 +msgid "Printing" +msgstr "打印" + +#: FPreview.form:145 +msgid "One page" +msgstr "单页" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "双页" + +#: FPreview.form:163 +msgid "Full width" +msgstr "全宽" + +#: FPreview.form:172 +msgid "Real size" +msgstr "实际尺寸" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "缩小" + +#: FPreview.form:193 +msgid "100 %" +msgstr "" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "放大" + +#: FPreview.form:208 +msgid "Print" +msgstr "打印" + +#: FPreview.form:216 +msgid "Show options" +msgstr "显示设置" + +#: FPreview.form:239 +msgid "Printer" +msgstr "打印机" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "双面" + +#: FPreview.form:271 +msgid "Long side" +msgstr "长边" + +#: FPreview.form:271 +msgid "None" +msgstr "无" + +#: FPreview.form:271 +msgid "Short side" +msgstr "短边" + +#: FPreview.form:275 +msgid "File" +msgstr "文件" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "分辨率" + +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "范围" + +#: FPreview.form:352 +msgid "Copies" +msgstr "份数" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "方向" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "横向" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "纵向" + +#: FPreview.form:389 +msgid "Paper" +msgstr "纸张" + +#: FPreview.form:396 +msgid "A3" +msgstr "" + +#: FPreview.form:396 +msgid "A4" +msgstr "" + +#: FPreview.form:396 +msgid "A5" +msgstr "" + +#: FPreview.form:396 +msgid "B5" +msgstr "" + +#: FPreview.form:396 +msgid "Custom" +msgstr "自定义" + +#: FPreview.form:396 +msgid "Executive" +msgstr "" + +#: FPreview.form:396 +msgid "Legal" +msgstr "" + +#: FPreview.form:396 +msgid "Letter" +msgstr "" + +#: FPreview.form:411 +msgid "Width" +msgstr "宽度" + +#: FPreview.form:422 +msgid "mm" +msgstr "" + +#: FPreview.form:432 +msgid "Height" +msgstr "高度" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "灰度" + +#: FPreview.form:461 +msgid "Full page" +msgstr "全页面" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "逐份打印" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "倒序" + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "" + +#: Report.class:120 +msgid "Section " +msgstr "节" + +#: Report1.report:29 +msgid "=\"Page \" & Page" +msgstr "" + +#: Report1.report:46 +msgid "=pi() + 4" +msgstr "" + +#: Report10.report:22 Report14.report:53 +msgid "=index" +msgstr "" + +#: Report10.report:31 +msgid "=Index" +msgstr "" + +#: Report10.report:46 +msgid "=\"Index = \" & index" +msgstr "" + +#: Report10.report:54 +msgid "=page & \" / \" & pages" +msgstr "" + +#: Report11.report:19 Report12.report:17 +#, fuzzy +msgid "Section 1" +msgstr "节" + +#: Report11.report:39 +msgid "Gambas" +msgstr "" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "" + +#: Report11.report:93 +msgid "DEMO" +msgstr "" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "" + +#: Report13.report:36 +msgid "=\"GAMBAS - \" & index" +msgstr "" + +#: Report13.report:44 +msgid "=\"PAGE \" & Page & \" / \" & pages" +msgstr "" + +#: Report14.report:28 +msgid "Reference" +msgstr "" + +#: Report14.report:35 +#, fuzzy +msgid "Description" +msgstr "节" + +#: Report14.report:42 +msgid "Valeur" +msgstr "" + +#: Report14.report:73 +msgid "=page & \" on \" & pages" +msgstr "" + +#: Report16.report:21 Report17.report:22 Report5.report:20 +msgid "Coucou" +msgstr "" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "" + +#: Report3.report:18 +msgid "PARCELLAIRE $NPAGE" +msgstr "" + +#: Report51.report:30 +msgid "Recto" +msgstr "" + +#: Report51.report:38 +msgid "Verso" +msgstr "" + +#: Report51.report:51 +msgid "N°" +msgstr "" + +#: Report51.report:99 +#, fuzzy +msgid "Observations" +msgstr "方向" + +#: Report6.report:12 +msgid "" +"Testtest

    \n" +"\n" +"
    " +msgstr "" diff --git a/comp/src/gb.report2/.project b/comp/src/gb.report2/.project new file mode 100644 index 00000000..cf3a86c6 --- /dev/null +++ b/comp/src/gb.report2/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=ReportsEvolution +Startup=Report13 +Icon=.hidden/control/reportview.png +Version=3.17.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.eval +Component=gb.settings +TabSize=2 +Translate=1 +Language=en +Type=Component +Packager=1 +DoNotTranslate=".src/Tests" diff --git a/comp/src/gb.report2/.src/Evaluator/CResult.class b/comp/src/gb.report2/.src/Evaluator/CResult.class new file mode 100644 index 00000000..2903a454 --- /dev/null +++ b/comp/src/gb.report2/.src/Evaluator/CResult.class @@ -0,0 +1,55 @@ +' Gambas class file + +Public Enum TypeValue, TypeString, TypeOp, TypeVar, TypeFunc, TypeBool, TypeDate +Public Value As Variant +Public {Type} As Integer +Public Pos As Integer +Property Read isValue As Boolean +Property Read isOperator As Boolean +Property Read isString As Boolean +Property Read isVariable As Boolean +Property Read isFunction As Boolean +Property Read isBoolean As Boolean + +Public Sub _new(vValue As Variant, iType As Integer) + + Value = vValue + Me.Type = iType + +End + +Private Function isValue_Read() As Boolean + + Return Me.Type = TypeValue + +End + +Private Function isOperator_Read() As Boolean + + Return Me.Type = TypeOp + +End + +Private Function isString_Read() As Boolean + + Return Me.Type = TypeString + +End + +Private Function isVariable_Read() As Boolean + + Return Me.Type = TypeVar + +End + +Private Function isFunction_Read() As Boolean + + Return Me.Type = TypeFunc + +End + +Private Function isBoolean_Read() As Boolean + + Return Me.Type = TypeBool + +End diff --git a/comp/src/gb.report2/.src/Evaluator/_RepExp.class b/comp/src/gb.report2/.src/Evaluator/_RepExp.class new file mode 100644 index 00000000..ea18e606 --- /dev/null +++ b/comp/src/gb.report2/.src/Evaluator/_RepExp.class @@ -0,0 +1,50 @@ +' Gambas class file + +Inherits Expression +Public Data As Variant +Event Data(Name As String) +Static Private $aFunctions As String[] +Static Private $aAllowedIdentifiers As String[] = ["page", "pages", "index"] + +Static Private Sub LoadFunctions() + + $aFunctions = Split(File.Load("FunctionsList"), "\n") + +End + + + + +Static Public Sub IsIdentifier(Name As String) As Boolean + + 'Print "Identifier : " & Name + + +Return $aAllowedIdentifiers.Exist(LCase(Name)) +End + +Static Public Sub IsSubr(Name As String) As Boolean + If Not $aFunctions Then LoadFunctions + ' Return if a the 'Name' Gambas subroutine is allowed. + Return $aFunctions.Exist(LCase(Name)) +'Print Name + +End + +Public Sub GetValue(Name As String) As Variant + + 'Return the value Of the 'Name' identifier. + Data = 0 + 'Print object.parent(Me) + Raise Data(Name) + Return Data + 'Print "Get Value : " & Name + 'Return "2" + +End + +' Public Sub Coucou(Value As String) As String +' +' Return "Coucou " & Value +' +' End diff --git a/comp/src/gb.report2/.src/Optional/Align.class b/comp/src/gb.report2/.src/Optional/Align.class new file mode 100644 index 00000000..485bc6a1 --- /dev/null +++ b/comp/src/gb.report2/.src/Optional/Align.class @@ -0,0 +1,10 @@ +' Gambas class file + +Export Optional + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Enum + Normal = &H00, {Left} = &H01, {Right} = &H02, Center = &H03, + TopNormal = &H10, TopLeft = &H11, TopRight = &H12, Top = &H13, + BottomNormal = &H20, BottomLeft = &H21, BottomRight = &H22, Bottom = &H23 diff --git a/comp/src/gb.report2/.src/Optional/Arrange.class b/comp/src/gb.report2/.src/Optional/Arrange.class new file mode 100644 index 00000000..20388fdc --- /dev/null +++ b/comp/src/gb.report2/.src/Optional/Arrange.class @@ -0,0 +1,11 @@ +' Gambas class file + +' Gambas class file + +Export Optional + +Public Const {None} As Integer = 0 +Public Const {Horizontal} As Integer = 1 +Public Const {Vertical} As Integer = 2 +Public Const {Column} As Integer = 4 +Public Const {Fill} As Integer = 5 diff --git a/comp/src/gb.report2/.src/Optional/Line.class b/comp/src/gb.report2/.src/Optional/Line.class new file mode 100644 index 00000000..c345df55 --- /dev/null +++ b/comp/src/gb.report2/.src/Optional/Line.class @@ -0,0 +1,13 @@ +' Gambas class file + +Export Optional + + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Const {None} As Integer = 0 +Public Const {Solid} As Integer = 1 +Public Const {Dash} As Integer = 2 +Public Const {Dot} As Integer = 3 +Public Const {DashDot} As Integer = 4 +Public Const {DashDotDot} As Integer = 5 \ No newline at end of file diff --git a/comp/src/gb.report2/.src/Report.class b/comp/src/gb.report2/.src/Report.class new file mode 100644 index 00000000..7c6c0d30 --- /dev/null +++ b/comp/src/gb.report2/.src/Report.class @@ -0,0 +1,802 @@ +' Gambas class file + +Create Static +Export +Inherits ReportVBox + +' For Stretch properties +Public Enum None, Proportional, Fill + +Public Const _Properties As String = "*,-Left,-Top,-Width,-Height,Count{Range:1;256}=1,Index,Text," +"Paper{Printer.A3;A4;A5;B5;Letter;Executive;Legal;Custom}=A4," +"Orientation{Printer.Portrait;Landscape}=Portrait" +Public Const _HiddenControls As String = "Report,ReportControl,ReportContainer,ReportFrame,_ReportSection" +Public Const _IsForm As Boolean = True +Public Const _IsContainer As Boolean = True +Public Const _IsMultiContainer As Boolean = True +Public Const _DefaultText As String = "Section &1" +Public Const _DrawWith As String = "TabPanel" +Public Const _DefaultEvent As String = "Open" +Static Public AllowedUnits As String[] = ["m", "cm", "mm", "in", "pt", "px"] + +Public _LayoutNotFinished As Boolean + +Public _Container As _ReportSection + +Public {Debug} As Boolean = False +Public _bInExec As Boolean + + +Private $aSections As New TControl[] +Private iCurPage As Integer = 0 +Private $iPageCount As Integer = -1 +Public _LayoutIsDirty As Boolean = True +Private $fScale As Float = 1.0 +Private $iIndex As Integer +Private $iResolution As Integer = -1 +Private $iCurSection As Integer +Private $iCurPage As Integer = -1 +Private $iPaper As Integer = 2 +Private $iOrientation As Integer +' Private $hPrinter As New Printer As "Printer" +Private $bOpened As Boolean +Private $fCurReportWidth As Float + +' Property Read Printer As Printer +Property Resolution As Integer +Property Scale As Float +Property Read PageCount As Integer +Property Count As Integer +Property Index As Integer + +'All ReportControl Properties +Property Left As String +Property Top As String +Property X As String +Property Y As String + +Property Width As String +Property Height As String + +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean + +Property Autoresize As Boolean +Property Padding As ReportPadding +'All ReportFrame Properties +Property Border As ReportBorder +Property BackGround As ReportBrush + +'All ReportContainer Properties +Property Read Children As ReportControl[] +Property Spacing As String +Property Data As Object + +'All ReportSection Properties +Property Text As String + +Property Paper As Integer +Property Orientation As Integer +Property Read _Width As Float +Property Read _Height As Float + +Property Path As String Use $sPath + +Event Open + +Static Public Sub Main() + + Dim hObject As Object + + hObject = Application.Startup.AutoCreate() + If Not hObject Is Report Then Return + FPreview.Run(hObject) + +End + +Static Public Sub _init() + + If Not Component.IsLoaded("gb.eval") Then Component.Load("gb.eval") + +End + + + +Public Sub _New() + + Dim hSection As New _ReportSection + Dim TSection As New TControl + + object.Attach(Me, Me, "Report") + + '$hReportTControl.RelPage = 0 + ReportControl._ObjectFromId[Me.id] = Me + hSection = New _ReportSection + hSection._ReportId = Me.Id + + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + $aSections.Add(TSection) + hSection.Text = ("Section ") & $aSections.Max + _Container = hSection + Me.paper = Printer.A4 + 'Me.Orientation = Printer.Landscape +End + +Public Sub Layout(Optional iPage As Integer = -1) + + + 'Dim iCurPage As Integer + Dim TSection As TControl + Dim CSection As ReportContainer + Dim CBaseSection As ReportContainer + Dim i As Integer + Dim hImg As Image + 'Utilise un device par défaut si nécéssaire + If Not Paint.Device Then + hImg = New Image(1, 1) + Paint.Begin(hImg) + Endif + Paint.Font = Me.Font + If Not $bOpened Then + Raise Open + $bOpened = True + Endif + + If Not _LayoutIsDirty Then Return + If _bInExec Then Return + If iPage > -1 Then + 'La page a déja été calculée + If iPage <= $iCurPage Then Return + Else + 'Mise a zéro + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + Me._Reset + Endif + + 'Set execution Flag to true + _bInExec = True + CBaseSection = $aSections[0].Ctrl + + For i = $iCurSection To $aSections.Max + Tsection = $aSections[i] + CSection = TSection.Ctrl + CSection.Padding = CBaseSection.Padding + CSection.Spacing = CBaseSection.Spacing + CSection._NormalizeUnits() + $fCurReportWidth = CBaseSection._Width + TSection._SetGeometry(0, 0, CSection._Width, CSection._Height) + Inc $iCurPage + Do + + CSection._SetChildGeometry(0, 0, CSection._Width, CSection._Height, $iCurPage, TSection, False) + + If CSection._CurItem > CSection.Children.Max Then Break + + Inc $iCurPage + $iPageCount = $iCurPage + 'Print "Fin Page " & $iCurPage + If iPage > -1 And If $iCurPage > iPage Then + Dec $iCurPage + _LayoutNotFinished = True + $iCurSection = i + _bInExec = False + Return + + Endif + + Loop + 'Inc iCurPage + Next + + $iPageCount = $iCurPage + 1 + _LayoutNotFinished = False + $iCurSection = 0 + _LayoutIsDirty = False + + 'End of execution + _bInExec = False + 'Print "Pages: " & $ipageCount +End + +Public Sub Paint(Page As Integer) + + Dim TSection As TControl + 'Dim fScale As Float + + + If Me.Parent = Null Then + + Endif + Paint.Font = Me.Font + $fCurReportWidth = $aSections[0].Ctrl._Width + + If _LayoutIsDirty Or $iPageCount = -1 Then Layout(Page) + Paint.Save + Paint.Scale($fScale, $fScale) + 'Set execution Flag to true + _bInExec = True + + + Dec page + + For Each TSection In $aSections + TSection.Ctrl._PaintBefore(Page, 0, 0, TSection, -1) + TSection.Ctrl._Paint(Page, 0, 0, TSection, -1) + TSection.Ctrl._PaintAfter(Page, 0, 0, TSection, -1) + Next + 'End of execution + _bInExec = False + Paint.Restore + + +End + +Private Function PageCount_Read() As Integer + + If _LayoutIsDirty And Not _LayoutNotFinished Then Layout(1) + + Return $iPageCount + +End + +Private Function Height_Read() As String + + Return $aSections[0].Ctrl.Height + +End + +Private Sub Height_Write(Value As String) + + SetHeight(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetHeight(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Height <> Value Then + + For Each TSection In $aSections + TSection.Ctrl.Height = Value + Next + _LayoutIsDirty = True + Me._reset + Endif + +End + +Private Function Width_Read() As String + + Return $aSections[0].Ctrl.Width + +End + +Private Sub Width_Write(Value As String) + + SetWidth(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetWidth(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Width <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Width = Value + Next + _LayoutIsDirty = True + Me._Reset + Endif + +End + +Private Function Scale_Read() As Float + + Return $fScale + +End + +Private Sub Scale_Write(Value As Float) + + $fScale = Value + +End + +Public Sub Clear() + + Super._Free + iCurPage = 0 + $iPageCount = -1 + + _LayoutIsDirty = True + $fScale = 1.0 + +End +' + +Public Function Preview() + If Me.Path Then FPreview.Path = Me.Path + FPreview.Run(Me) + If FPreview.Path Then Me.Path = FPreview.Path +End + + + +Public Sub Refresh() + + _LayoutIsDirty = True + Me._Reset + +End + +Private Function Count_Read() As Integer + + Return $aSections.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim i As Integer + Dim hSection As _ReportSection + Dim TSection As TControl + + If Value < 0 Then Return + If Value < $aSections.Count Then + $aSections.Resize(Value) + Else If Value > $aSections.Count Then + For i = $aSections.Count To Value - 1 + + TSection = New TControl + hSection = New _ReportSection + hSection.Text = "Section " & CStr(i) + hSection._ReportId = Me.Id + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + hSection.Padding = Me.Padding + hSection.Height = Me.Height + hSection.Width = Me.Width + $aSections.Add(TSection) + 'hSection.Text = ("Section ") & $aSections.Max + Next + Endif + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + $iIndex = Max(Min($aSections.Max, Value), 0) + _Container = $aSections[$iIndex].Ctrl + +End + +Public Sub _get(Index As Integer) As _ReportSection + + Return $aSections[Index].Ctrl + +End + +Private Function Padding_Read() As ReportPadding + + Return $aSections[0].Ctrl.Padding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + Dim TSection As TControl + + If Me.Padding <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Padding = Value + Next + Endif + +End + +Private Function Resolution_Read() As Integer + + Return $iResolution + +End + +Private Sub Resolution_Write(Value As Integer) + + _LayoutIsDirty = True + $iResolution = Max(0, Value) + +End + +Private Function Left_Read() As String + + Return _Container.Left + +End + +Private Sub Left_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Top_Read() As String + + Return _Container.Top + +End + +Private Sub Top_Write(Value As String) + + _Container.Top = Value + +End + +Private Function X_Read() As String + + Return _Container.Left + +End + +Private Sub X_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Y_Read() As String + + Return _Container.Top + +End + +Private Sub Y_Write(Value As String) + + _Container.Top = Value + +End + +Private Function Visible_Read() As Boolean + + Return _Container.Visible + +End + +Private Sub Visible_Write(Value As Boolean) + + _Container.Visible = Value + +End + +Private Function Font_Read() As Font + + Return _Container.Font + +End + +Private Sub Font_Write(Value As Font) + + _Container.Font = Value + +End + +Private Function Expand_Read() As Boolean + + Return _Container.Expand + +End + +Private Sub Expand_Write(Value As Boolean) + + _Container.Expand = Value + +End + +Private Function Ignore_Read() As Boolean + + Return _Container.Ignore + +End + +Private Sub Ignore_Write(Value As Boolean) + + _Container.Ignore = Value + +End + +Private Function Fixed_Read() As Boolean + + Return _Container.Fixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + _Container.Fixed = Value + +End + +Private Function Autoresize_Read() As Boolean + + Return _Container.Autoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + _Container.Autoresize = Value + +End + +Private Function Border_Read() As ReportBorder + + Return _Container.Border + +End + +Private Sub Border_Write(Value As ReportBorder) + + _Container.Border = Value + +End + +Private Function BackGround_Read() As ReportBrush + + Return _Container.BackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + _Container.BackGround = Value + +End + +Private Function Children_Read() As ReportControl[] + + Return _Container.Children + +End + +Private Function Spacing_Read() As String + + Return _Container.Spacing + +End + +Private Sub Spacing_Write(Value As String) + + Dim TSection As TControl + Dim hCont As ReportContainer + + If Me.Spacing <> Value Then + For Each TSection In $aSections + hCont = Tsection.Ctrl + hCont.Spacing = Value + Next + Endif + +End + +Private Function Data_Read() As Object + + Return _Container.Data + +End + +Private Sub Data_Write(Value As Object) + + _Container.Data = Value + +End + +Private Function Text_Read() As String + + Return _Container.Text + +End + +Private Sub Text_Write(Value As String) + + _Container.Text = Value + +End + +''Return a new report object +Public Sub Clone() As Report + + 'Return Object.New(Object.Type(Me)) + Return Object.Class(Me).New() + +End + +Private Function Paper_Read() As Integer + + Return $iPaper + +End + +Private Sub Paper_Write(Value As Integer) + + $iPaper = Value + SetPaper + +End + +Private Function Orientation_Read() As Integer + + Return $iOrientation + +End + +Private Sub Orientation_Write(Value As Integer) + + $iOrientation = Value + SetPaper + +End + +Public Sub Print(Optional hPrinter As Printer) + + If hPrinter Then + CPrint.PrintReport(Me, hPrinter) + Else + CPrint.PrintReport(Me) + Endif + +End + +Private Sub SetPaper() + + Dim sPrev As String + + Select Case $iPaper + Case Printer.A3 + SetWidth("29.7cm") + SetHeight("42cm") + Case Printer.A4 + SetWidth("21cm") + SetHeight("29.7cm") + Case Printer.A5 + SetWidth("14.8cm") + SetHeight("21cm") + Case Printer.B5 + SetWidth("18.7cm") + SetHeight("25.7cm") + Case Printer.Executive + SetWidth("19.1cm") + SetHeight("25.4cm") + Case Printer.Legal + SetWidth("21.6cm") + SetHeight("35.6cm") + Case Printer.Letter + SetWidth("21.6cm") + SetHeight("27.9cm") + + End Select + + + If $iOrientation = Printer.Landscape Then + sPrev = Me.Height + SetHeight(Me.Width) + SetWidth(sPrev) + Endif + +End + +Public Sub _Reset() + + Dim hSection As TControl + + For Each hSection In $aSections + hSection.Ctrl._Reset + hSection._PageChildren = New Collection + Next + + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + +End + +''Convert Internal units to scaled pixels. + +Fast Public Function _ToPixels(Value As Float) As Float + + Dim f As Float + + f = Value / $fCurReportWidth * (Paint.Width / $fScale) + + Return f + +End + +''Shortcut to Convert a Pixel Value to Internal unit (Need a paint device). +Fast Public Function _FromPixels(Value As Float) As Float + + Return (value / Paint.ResolutionX) '* 2.54) + +End + +Fast Static Private Function GetFactorToInch(Unit As String) As Float + + Select Case Unit + Case "cm" + Return 0.3937 + Case "ft" + Return 12 + Case "in" + Return 1 + Case "m" + Return 39.37 + Case "mm" + Return 0.03937 + Case "px" + If Paint.ResolutionX Then + Return 1 / Paint.ResolutionX + Else + Return 1 / Desktop.Resolution + Endif + Case "pt" + Return 1 / 72 'http://en.wikipedia.org/wiki/Point_(typography)#Current_DTP_point_system + Case Else + Error.Raise("Unknown unit") + End Select + +End + +''Convert any value from any report allowed Unit to another one. +''For Pixel convertions, be carefull to give the Device +''Resolution in parameter. +Fast Static Public Function UnitTo(Value As Float, Unit1 As String, Unit2 As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorToInch(Unit1) + finch = value * f + f = GetFactorToInch(Unit2) + Return finch / f + +End + +''Shortcut to convert any report allowed unit to Inch. +Fast Static Public Function UnitToInch(Value As Float, Unit As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorToInch(Unit) + finch = value * f + Return finch + 'f = 0.3937 + 'Return finch / f + +End + +Private Function _Width_Read() As Float + + Return $aSections[0].Ctrl._Width + +End + +Private Function _Height_Read() As Float + + Return $aSections[0].Ctrl._Height + +End diff --git a/comp/src/gb.report2/.src/ReportContainer.class b/comp/src/gb.report2/.src/ReportContainer.class new file mode 100644 index 00000000..d062e7c2 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportContainer.class @@ -0,0 +1,1088 @@ +' Gambas class file + +Export +Create Private +Inherits ReportFrame + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*,Spacing{ReportCoord},OnePiece,ForceNewPage" +'Public Const _DefaultEvent As String = "Data" +'Private $bDataIsResult As Boolean +'Private $iCount As Integer + +Property _CurItem As Integer +Private $iCurItem As Integer + +Public _Arrangement As Integer +Private $bIndexChange As Boolean +Private $aChild As New ReportControl[] +Private $fSpacing As Float +Private $sSpacing As String +Private $bRelativeSpacing As Boolean + +'Private $aChildCopy As TControl[] +Private $bOnePiece As Boolean +'Private $iIndex As Integer +'Private $hData As Object +Private $fFixedSize As Float +Property _Index As Integer + +Property DataCount As Integer + +Property Read Children As ReportControl[] + +'Property Read Index As Integer +Property Spacing As String +'Property Data As Object '<==== remettre pour automation + +Property Read _Spacing As Float +Property Read _RelativeSpacing As Boolean + +Property OnePiece As Boolean +Public _RealSpacing As Integer +Public _NotFinished As Boolean + +Event BeforeData +Event AfterData + +Public Function _Add(cControl As ReportControl) 'As TControl + + 'Dim hTControl As New TControl + + 'hTControl.Ctrl = cControl + $aChild.Add(cControl) + ReportControl._ObjectFromId[cControl.Id] = cControl + 'Return hTControl + +End + +Public Sub _Remove(id As Integer) + + $aChild.Remove($aChild.Find(ReportControl._ObjectFromId[id])) + +End + +Public Sub _Free() + +End + +Public Sub _Raise(hControl As ReportControl) + + Dim iPos As Integer + + iPos = $aChild.Find(hControl) + If iPos = -1 Then Return + $aChild.Remove(iPos) + $aChild.add(hControl, 0) + +End + +Public Sub _Lower(hControl As ReportControl) + + Dim iPos As Integer + + iPos = $aChild.Find(hControl) + If iPos = -1 Then Return + $aChild.Remove(iPos) + $aChild.add(hControl) + +End + +Private Function Children_Read() As ReportControl[] + + Return $aChild + +End + +Private Function Spacing_Read() As String + + Return $sSpacing + +End + +Private Sub Spacing_Write(Value As String) + + $sSpacing = Value + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, (DataIndex) As Integer) + + Dim hChild As TControl + Dim i As Integer + + X += hControl.RealLeft + Y += hControl.RealTop + ' Paint.Text(DataIndex, X + 20, Y + 20) + ' Paint.Brush = Paint.Color(Color.Black) + ' Paint.Fill + + If Not hControl._PageChildren.Exist(Page) Then Return + 'For Each hChild In hControl._PageChildren[Page] + For i = 0 To hControl._PageChildren[Page].Max + hchild = hControl._PageChildren[Page][i] + Me._Index = hchild.Index + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + hChild.Ctrl._Paintframe(Page, X, Y, hChild, hchild.Index) 'hChild.Index) + + Next + +End + +Private Function _Spacing_Read() As Float + + Return $fSpacing + +End + +Private Function _RelativeSpacing_Read() As Boolean + + Return $bRelativeSpacing + +End + +Public Sub _NormalizeUnits() + + Dim hChild As ReportControl + Dim hSizeParse As TSizeParse + + Super._NormalizeUnits() + + hSizeParse = New TSizeParse($sSpacing, True) + $fSpacing = hSizeParse.GetValue() + $bRelativeSpacing = hSizeParse.IsRelative() + + For Each hChild In $aChild + 'hchild.SizeHint = Null + hChild._NormalizeUnits() + Next + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + + 'If Me._SizeInt.StoreSize Then Return Me._SizeInt + 'If Me.tag = "*" Then Stop + If Me._CurItem > Me.Children.max Then + + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + ' 'Me._DataIndex = 0 + Endif + Me._Index = DataIndex + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + Select Case _Arrangement + + Case Arrange.Vertical + Return GetVSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Horizontal + Return GetHSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Column + Return GetVPSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Fill, Arrange.None + Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'hMyHints.Height = AvailableH + 'Me._SizeInt.StoreSize = True + 'Me._SizeInt = hMyHints + Return hMyHints + + End Select + Raise AfterData + +End + +Private Function GetHSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hChild As ReportControl + Dim fHeight, fWidth As Float + Dim hChildHints As ReportSizeHints + Dim hMyHints As New ReportSizeHints + Dim fSpc As Float + Dim fMaxSpc As Float + Dim i As Integer + 'D'abord utiliser la méthode du controle pour définir la taille + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + 'Puis si besoins voir le besoin des enfants + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + fSpc = IIf(Me._RelativeSpacing, TotalWidth * Me._Spacing / 100, Me._Spacing) + 'on ajoute la marge supérieur du premier objet + If Me.Children.Max > 0 Then fWidth += Me.Children[0].Margin._Top + For i = 0 To Me.Children.Max + hChild = Me.Children[i] + If Not hchild.Visible Then Continue + hChildHints = hchild._GetSizeHints(AvailableW - fWidth, AvailableH, AvailableW, AvailableH, DataIndex) + fMaxSpc = Max(fspc, hchild.Margin._Right) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Left) + fWidth += hChildHints.Width + fMaxSpc + If fHeight < hChildHints.Height Then fHeight = hChildHints.Height + 'Si l'enfant n'est pas finit alors moi non plus (:-P) + If hChildHints.NotFinished Then hMyHints.NotFinished = True + Next + 'Retrait du dernier espace + If fWidth Then fWidth -= fSpc + hMyHints.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._bottom + hMyHints.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + Return hMyHints + +End + +Private Function GetVPSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + ' Dim hSizeInt As New ReportSizeHints + ' Dim htmpInts As ReportSizeHints + ' Dim hChild As ReportControl ''Enfants + ' Dim fHeight, fSpc, fWidth As Float + ' Dim i, j As Integer + ' Dim bExitLoop As Boolean + ' Dim fMaxSpc As Float + ' + ' 'D'abord utiliser la méthode du controle pour définir la taille + ' hSizeInt = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + ' 'Circuit court + ' If Me.Children.Count = 0 Then Return hSizeInt + ' 'Largeur d'un espace + ' fSpc = IIf(Me._RelativeSpacing, TotalHeight * Me._Spacing / 100, Me._Spacing) + ' ' Si ce n'est pas fait on enregistre la place nécéssaire a tout les éléments fixes + ' If Not $fFixedSize Then + ' + ' Endif + ' +End + + + + +Private Function GetVSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hSizeInt As New ReportSizeHints + Dim htmpInts As ReportSizeHints + Dim hChild As ReportControl ''Enfants + Dim fHeight, fSpc, fWidth As Float + Dim i, j As Integer + Dim bExitLoop As Boolean + Dim fMaxSpc As Float + 'Dim IndexKey As String = Str(Me.Id) &/ Str(DataIndex) + 'If Me.Tag = "**" Then Stop + 'D'abord utiliser la méthode du controle pour définir la taille + hSizeInt = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'Circuit court + If Me.Children.Count = 0 Then Return hSizeInt + + fSpc = IIf(Me._RelativeSpacing, TotalHeight * Me._Spacing / 100, Me._Spacing) + + ' Si ce n'est pas fait on enregistre la place nécéssaire a tout les éléments fixes + + If Not $fFixedSize Then + 'On ajoute la marge superieur du premier objet7 + 'If Me.tag = "**" Then Stop + For i = 0 To Me.Children.Max + hChild = Me.Children[i] + If Not hchild.Visible Then Continue + If hchild.Fixed Then + 'On ajoute la marge supérieur du premier objet fixe + If $fFixedSize = 0 Then $fFixedSize = hchild.Margin._Top + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH, AvailableW, AvailableH, DataIndex) + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + $fFixedSize += htmpInts.Height + fMaxSpc + Endif + Next + 'If $fFixedSize Then $fFixedSize = Me.Children[0].Margin._Top + Endif + + 'La taille ne peut être inférieur a celle de tout les éléments fixes ou a défaut + 'a celle du premier élément. + hSizeInt.Height = Max(hSizeInt.Height, $fFixedSize) + + 'On ajoute la taille des objets fixes précédents + If $fFixedSize > 0 Then + fHeight = $fFixedSize + Else + 'On ajoute la marge supérieur du premier objet listé si aucun objet fixe n'est a l'Horizon + fHeight = Me.Children[Me._CurItem].Margin._Top + Endif + + 'If Me.Tag = "**" Then Stop + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'On définit la taille au besoins des enfants + For i = Me._CurItem To Me.Children.Max + hChild = Me.Children[i] + If Not hchild.Visible Then Continue + For j = hchild._DataIndex To hchild._Count - 1 + 'If hchild.Tag = "head" Then Print "VDataHint = " & DataIndex + If hChild.Ignore Then Continue + If hChild.Fixed Then Continue + hchild._Index = j + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH - fHeight, AvailableW, AvailableH, j) + 'Si l'enfant n'est pas terminé alors moi non plus + If htmpInts.NotFinished Then hSizeInt.NotFinished = True + fMaxSpc = Max(fspc, hchild.Margin._Bottom) + If hchild._Count <= 0 Or If j = hchild._Count - 1 Then + If i < Me.Children.Max Then + fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + Endif + Else + fMaxSpc = Max(fMaxSpc, hchild.Margin._Top) + Endif + + 'on ajoute la hauteur aux besoins + fHeight += htmpInts.Height + fMaxSpc + ' If Me.Tag = "**" Then + ' Print "fHeight , AvailableH : ", fHeight - fMaxSpc, AvailableH + ' Print "fMaxSpc : ", fMaxSpc + ' 'If fMaxSpc = 0 Then Stop + ' Endif + + If fHeight - fMaxSpc > AvailableH Then + 'Les enfants ne loge pas ... on ne peut pas finir + hSizeInt.NotFinished = True + bExitLoop = True + 'Print "Avec Parent : " & DataIndex & " Je loge : " & j + Break + Endif + 'On récupère la largeur de l'enfant le plus large + If fWidth < htmpInts.Width Then fWidth = htmpInts.Width + Next + If bExitLoop Then Break + Next + + 'on enlève le dernier espace + If fHeight > 0 Then fHeight -= fSpc + 'bogue bizzard + fHeight += 0.01 + + 'On indique la place nécéssaire aux enfants+les éléments fixes + hSizeInt.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._Bottom + hSizeInt.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + 'On ne peut pas dépasser la taille disponible (report sur prochaine page) + 'FIXME: Onepiece partially desactivated + If Not Me.OnePiece Then + hSizeInt.Height = Min(hSizeInt.Height, AvailableH) + Endif + + hSizeInt.Height = Min(TotalHeight, hSizeInt.Height) + + Me._SizeInt = hSizeInt + ' If Me.Tag = "Boite 2" Then + ' Print hSizeInt.Height + ' Stop + ' Endif + 'If Me.tag = "toto" Then Stop + ' If hSizeInt.NotFinished = False Then + ' Print Me.Tag & " : EST FINIT" + ' Endif + Return hSizeInt + +End + +Public Sub _Reset() + + Dim hChild As ReportControl + 'mise a zéro de l'index de suivit de progression + Me._CurItem = 0 + 'Mise a zéro de l'index de reproduction + Me._DataIndex = 0 + 'Netoyage du layout précédent + 'Me._PageChildren.Clear + 'Nettoyage récurssif des enfants + For Each hChild In Me.Children + + hChild._Reset + + Next + +End + + + + + + + + +Public Sub _SetChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + 'If Me.Tag = "*" Then Stop + 'Print "Geometry " & Object.Type(Me) + 'If ContPage > 0 Then Return + + Me._Index = TCont.Index + + If Me._CurItem > Me.Children.max Then + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + 'Me._DataIndex = 0 + Endif + + ' If $bIndexChange Then + ' Raise BeforeData + ' $bIndexChange = False + ' Endif + + Select Case _Arrangement + Case Arrange.Vertical + SetVChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Horizontal + SetHChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Column + Me._SetCChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Fill + SetFChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.None + SetNChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + End Select + Raise AfterData + +End + +Private Function SetFChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim hTItem As TControl + Dim hChildHints As ReportSizeHints + Dim aPageItems As New TControl[] + Dim oChild As Object + Dim j As Integer + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + For Each hChild In Me.Children + If Not hchild.Visible Then Continue + hChildHints = hchild._GetSizeHints(W, H, W, H) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + For Each oChild In aPageItems + oChild._Index = hTItem.Index + hTItem._SetGeometry(X, Y, W, H) + hChild._SetChildGeometry(X, Y, W, H, ContPage, hTItem, bInFixed Or Me.Fixed) + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function SetNChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim ochild As Object + Dim hTItem As TControl + Dim hChildHints As ReportSizeHints + Dim aPageItems As New TControl[] + Dim iX, iY As Float + Dim J As Integer + + If Me.Children.Count = 0 Then Return + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + + For Each hChild In Me.Children + If Not hchild.Visible Then Continue + hChildHints = hchild._GetSizeHints(W, H, W, H, TCont.Index) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + + For Each hTItem In aPageItems + oChild = hTItem.Ctrl + hChildHints = hTItem.SizeHint + iX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + iY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + oChild._Index = hTItem.Index + hTItem._SetGeometry(iX, iY, hChildHints.Width, hChildHints.Height) + hChild._SetChildGeometry(iX, iY, hChildHints.Width, hChildHints.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenterla lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Sub SetHChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As ReportSizeHints ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim oChild As Object + Dim TW, fWidth, fSpc As Float + Dim fExp As Float + Dim iNExp As Integer + + Dim i, j As Integer + Dim fTmpX, fX, fY As Float + Dim hTItem As TControl + Dim fMaxSpc As Float + Dim fChildY, fChildH As Float + 'Initialisation des variables + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) 'ME._Spacing + 'On retire les marges a la hauteur et les bordures + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + 'Print H + 'on retire a la largeur les paddings (et les bordure ?) + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + + 'On positionne le curseur de position au coin a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'On initialise la largeur totale avec la largeur disponible + TW = W + + For i = 0 To Me.Children.Max + + hChild = Me.Children[i] + If Not hchild.Visible Then Continue + 'hchild._Index = IIf(Me.DataCount > 0, j, TCont.Index) + hChild._Index = TCont.Index + hChildHints = hChild._GetSizeHints(w, h, w, h, TCont.Index) + + 'Si l' éléments ne loge pas on quitte et on oublit + If TW - hChildHints.Width < 0 Then Break + + 'Si l'élément n'est pas ignoré + If Not hChild.Ignore Then + fMaxSpc = Max(fspc, hchild.Margin._Right) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Left) + + TW = TW - hChildHints.Width - fMaxSpc + 'Si il est étendu on en tient compte + If hChild.Expand Then + Inc iNExp + fExp += hChildHints.Width + Endif + Endif + + 'On ajoute l'élément a la page + hTItem = New TControl + hTItem.Ctrl = hChild + hTItem.SizeHint = hChildHints + hTItem.Index = TCont.Index + aPageItems.Add(hTItem) + + Next + + 'De toute les façon je ne cherche pas a parcourir tout + 'Les objet donc j'indique que j'ai tout vu + Me._CurItem = Me.Children.count + + If (W - TW) > 0 Then + TW += fspc + Endif + + fTmpX = X + If aPageItems.Count > 0 Then + fTmpX += aPageItems[0].Ctrl.Margin._Left + fExp = fExp - aPageItems[0].Ctrl.Margin._Left '- aPageItems[aPageItems.Max].Ctrl.Margin._Right + Endif + + 'On va mettre en page a présent + 'On définit la taille des éléments étendus + If iNexp Then + fExp = (TW + fExp) / iNexp + Endif + + For i = 0 To aPageItems.Max + hTItem = aPageItems[i] + oChild = hTItem.Ctrl + 'Si l'élément est étendu on lui applique la taille répartie + If oChild.Expand And Not oChild.Ignore Then + 'If Me.Tag = "*" Then Stop + fWidth = fExp + Else + 'sinon il maintien sa taille + fWidth = hTItem.SizeHint.Width + Endif + 'oChild._Index = hTItem.Index + If Not oChild.Ignore Then + fChildH = H - ochild.Margin._Top - oChild.Margin._Bottom + fChildY = Y + oChild.Margin._Top + hTItem._SetGeometry(fTmpX, fChildY, fWidth, fChildH) + oChild._SetChildGeometry(fTmpX, fChildY, fWidth, fChildH, ContPage, hTItem, bInFixed Or Me.Fixed) + fMaxSpc = Max(fspc, ochild.Margin._Right) + If i < aPageItems.Max Then fMaxSpc = Max(fMaxSpc, aPageItems[i + 1].Ctrl.Margin._Left) + fTmpX += fWidth + fMaxspc + Else + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + hTItem._SetGeometry(fX, fY, fWidth, hTItem.SizeHint.Height) + oChild._SetChildGeometry(fX, fY, fWidth, hTItem.SizeHint.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + Endif + + If oChild Is ReportContainer Then + If oChild._CurItem <= oChild.Children.max Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + + Endif + Next + + 'On ajoute la page a la collection de page du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function DataCount_Read() As Integer + + Return Me._Count + +End + +Private Sub DataCount_Write(Value As Integer) + 'If value = 0 Then value = 1 + + Me._Count = Value + +End + +' Public Function _GetIndex() As Integer +' +' If Me._Count > 0 Then 'Si c'est moi le répéteur alors je fournis mon index +' Return Me._Index +' Else +' Try Return Me.Parent._GetIndex 'Sinon je ontinue a remonter la lignée jusqu'au répéteur +' Endif +' +' End + +Private Function _Index_Read() As Integer + + Return Super._Index + +End + +Private Sub _Index_Write(Value As Integer) + + If Super._Index = Value And If Super._Index > 0 Then Return + $bIndexChange = True + Super._Index = Value + +End + +Private Function _CurItem_Read() As Integer + + Return $iCurItem + +End + +Private Sub _CurItem_Write(Value As Integer) + 'Print "_CurItem = " & Value + 'If Me.tag = "Boite 1" And Value = 0 Then Stop + 'If Me.tag = "Boite 1" And ContPage = 1 Then Print "CurItem: " & Me._CurItem + + $iCurItem = Value + +End + +Private Function OnePiece_Read() As Boolean + + Return $bOnePiece + +End + +Private Sub OnePiece_Write(Value As Boolean) + + $bOnePiece = Value + +End + +Public Sub _SetCChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + +End + +Private Sub SetVChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As ReportSizeHints ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim fTH As Float ''Hauteur restante + Dim fSpc As Float ''Taille d'un espace + Dim hTItem As TControl ''Un objet virtuel + Dim oChild As Object ''Un objet gambas générique + Dim fExp As Float ''taille des objets étendus + Dim iNExp As Integer ''Nombre d'objets étendus + Dim ftmpHeight As Float ''Tampon pour le calcul de la taille répartie + Dim ftmpY As Float ''Curseur temporaire de position haute + Dim fX, fY As Float ''Tampon de position + Dim i, j As Integer ''Des indexs + Dim bExitLoop As Boolean ''Flag de sortie de traitement + Dim bForceNewPage As Boolean ''Flag d'anticipation de sortie 1 par page + Dim iPreIndex As Integer + Dim fMaxSpc As Float ''Résultat de la comparaison ds espaces (marges/spacing) + Dim fChildX, fChildW As Float + Dim iIndex As Integer + Dim bFirst As Boolean + 'Dim hSizeInt As Boolean + Dim bTop As Boolean + Dim fPrevTH As Float + Dim cChildFixe As New Collection + Dim iVal As Integer + 'Dim IndexKey As String = Str(Me.Id) &/ Str(TCont.Index) + + 'Détermination de la taille d'un espace + 'et prise en compte de la taille relative + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) + + 'Retrait du padding et des bordures de la hauteur de travail + H = H - Me.Padding._Top - Me.Border._Top - Me.Border._Bottom - Me.Padding._Bottom + + 'Retrait du padding et des bordures a la largeur de travail + W = W - Me.Padding._Left - Me.Border._Left - Me.Border._Right - Me.Padding._Right + + 'Positioinnement du curseur en haut a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'La hauteur restante est initialisée avec la hauteur de travail + fTH = H + + 'On énumère tout les éléments an tête en ne retenant que les fixes + For i = 0 To Me.Children.Max + hchild = Me.Children[i] + If Not hchild.Visible Then Continue + If hchild.Fixed Then + cChildFixe[j] = hchild + Inc j + Endif + Next + + j = 0 + + For i = 0 To Me.Children.Max + If Not hchild.Visible Then Continue + hchild = Me.Children[i] + 'If Not hchild._DataIndex.Exist(IndexKey) Then hchild._DataIndex[IndexKey] = 0 + If hchild.Fixed Then + + If Not bTop And If i < Me._CurItem Then + fTH = fTH - Me.Children[i].Margin._Top + bTop = True + Endif + 'trouver le fixe suivant + For j = i + 1 To Me._CurItem + If Me.Children[j].Fixed Then + iVal = j + Break + Endif + Next + 'Si le fixe suivant n'est pas définit alors on utlise l'objet suivant + If Not iVal Then iVal = i + 1 + + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + 'Calcul de l'espace interobjet (c'est a dire la plus grande valeur entre la marge basse de l'objet, + 'le spacing, et la marge haute de l'objet suivant.) + + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + + If Not hchild.Ignore Then fTH -= hChildHints.Height + fMaxSpc + If i < Me._CurItem Then + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + 'TODO: Restorer + 'hTItem.Index = TCont.Index + hTItem.Index = iIndex + Inc iIndex + 'L'ajouter a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + + Endif + + Endif + Next + 'If ContPage = 2 And Me.Tag = "**" Then Stop + 'On parcour les élément de l'index a la fin + For i = Me._CurItem To Me.Children.Max + + If Not bTop Then + fTH = fTH - Me.Children[i].Margin._Top + bTop = True + Endif + hchild = Me.Children[i] + If Not hchild.Visible Then Continue + 'Je traite ici la boucle de clonage + 'On définit le point de départ + 'If ContPage = 1 And hchild.Tag = "**" Then Stop + j = hChild._DataIndex + 'hchild._PageDataIndex = hchild._DataIndex + Do + 'Calcul de l'espace interobjet (c'est a dire la plus grande valeur entre la marge basse de l'objet, + 'le spacing, et la marge haute de l'objet suivant.) + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + 'Définit l'espace en fonction du contexte + If hchild._Count > 0 And If j <= hchild._count - 1 Then + fMaxSpc = Max(fMaxSpc, Me.Children[i].Margin._Top) + Else + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + Endif + 'If Me.Tag = "**" And j = 3 Then Stop + 'If TCont.Index = 2 Then Stop + 'If ContPage = 2 And Me.Tag = "**" Then Stop + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + 'If ContPage = 2 And Me.Tag = "**" Then Print fTH, hChildHints.Height + 'Les éléments fixes ont déja été traités on ne tient donc pas compte de leur hauteur + 'car elle a déja été déduite de l'espace restant. De meme on ignore les objets flottants (ignore=true) + If Not hchild.Fixed And If Not hchild.Ignore Then + 'Si l'élément ne loge pas dans la place restante ou + 'si la place restante est insuffisante + 'on provoque la sortie en fin de boucle + If fTH - hChildHints.Height <= 0 Or If bForceNewPage Then + ' Debug "Lélément index : " & j & " Ne loge pas dans le conteneur" + ' Debug "fTH : " & fTH + ' Debug "fTH - hChildHints.Height : " & (fTH - hChildHints.Height) + ' Print + If Not bFirst Then + 'SI il est le premier de la série alors il doit loger dans la page coute que coute + hChildHints.Height = fTH - hchild.Margin._bottom + Else + + bForceNewPage = False + hchild._DataIndex = j + + bExitLoop = True + Break + Endif + Endif + bFirst = True + fTH = fTH - hChildHints.Height - fMaxSpc + Endif + 'On l'ajoute a la page + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + 'TODO: Restorer + 'hTItem.Index = TCont.Index + 'Print hchild._count + hTItem.Index = IIf(hchild._count = 1, TCont.Index, j) 'iIndex + ' Print hchild.Tag + ' Print "Set J= " & hTItem.Index + Inc iIndex + 'L'ajouter a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + 'If hChildHints.NotFinished Then hchild._DataIndex = j + 'un élément fixe ou ignore ne peut être répété + If hchild.Fixed Or hchild.Ignore Then Break + + 'Sachant que hChild._count peut être à -1 on le considère dans ce cas la comme étant a 0 + 'si j est égale au compte alors on quitte la boucle + If j >= Max(hchild._Count - 1, 0) Then + 'Remise a 0 si le contenu de l 'enfant est entièrement affiché + If Not hChildHints.NotFinished Then + hChild._DataIndex = 0 + Endif + Break + Endif + 'sinon on incrémente le compte a condition que le dernier enfant aie finit d'afficher ses enfants + If Not hChildHints.NotFinished Then Inc j + + 'On prévoie un sortie de boucle si l'enfant demande un affichage 1 par page + If hchild.ForceNewPage Then bForceNewPage = True + + ' If fTH = 0 Then + ' hchild._DataIndex = j + ' Break + ' bExitLoop = True + ' Endif + + Loop + 'Si la sortie anticipé est demandée alors on sort de la boucle + 'If bExitLoop Then Stop + If bExitLoop Then Break + + 'Si l'objet suivant est un objet spécial de rupture de page alors + 'On force la rupture de page + If i < Me.Children.Max And If Me.Children[i + 1] Is ReportPageBreak Then + Inc Me._CurItem + bForceNewPage = True + Inc i 'on saute l'objet. + Endif + + 'Enfin on incrémente la variable de saugarde de l'objet en cour. + 'If Me.Tag = "**" Then Debug "Inc" + Inc Me._CurItem + + Next + '*************************************************************************** + 'Pour tous les éléments fixes jusqu'à la fin du document + For i = Max(Me._CurItem, 0) To Me.Children.Max + hchild = Me.Children[i] + If Not hchild.Visible Then Continue + If hchild.Fixed Then + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + hChildHints = hchild._GetSizeHints(W, fPrevTH, W, H, iPreIndex) + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'NOTE: Restorer + hTItem.Index = iIndex + Inc iIndex + 'On l'ajoute a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + fPrevTH = fPrevTH - fMaxSpc + Endif + Next + + 'Si on est en mode spacing alors le dernier expace doit être enlevé + If fspc > 0 And If fMaxSpc = fspc Then + fTH += fSpc + Endif + + '****************************************************************************** + 'A présent tous les éléments pouvant être placé sur la page ont été marqué. + 'On peut donc procéder a la mise en forme de ceux-ci + + ftmpY = Y 'On définit la position de départ + If aPageItems.Count > 0 Then + fTmpY += aPageItems[0].Ctrl.Margin._Top + 'fExp = fExp - aPageItems[0].Ctrl.Margin._Top '- aPageItems[aPageItems.Max].Ctrl.Margin._Right + Endif + If iNExp Then + fExp = (fExp + fTH) / iNExp + Endif + + For i = 0 To aPageItems.Max + 'on récupère l'objet virtuel + hTItem = aPageItems[i] + 'on récupère l'instance de l'objet réel + ochild = hTItem.Ctrl + + 'Si l'objet est étendu alors on calcul sa hauteur + 'Un objet flottant ne peut pas être étendu + If Not oChild.Ignore And If ochild.Expand Then + 'Calcul de la taille répartie + ftmpHeight = fExp + Else + 'Si l'objet n'est pas étendu alors sa taille est celle demandée par celui-ci + ftmpHeight = hTItem.SizeHint.Height + Endif + 'oChild._Index = hTItem.Index + 'Traitement des objets ignoré + 'Les propriété de taille sont celle fournie par l'objet + If oChild.Ignore Then + 'Calcul de la position de l'objet + 'si sa position est relative (%) alors on fait le ratio a partir de la largeur ou de la hauteur + 'sinon on utilise la position fournie par l'objet + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + 'Fixer la position de l'objet + hTItem._SetGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + + Else + fChildX = X + oChild.Margin._Left + fChildW = W - oChild.Margin._Left - oChild.Margin._Right + 'Fixer la position de l'objet + hTItem._SetGeometry(fChildX, fTmpY, fChildW, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fChildX, fTmpY, fChildW, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + 'Calcul de l'espace interobjet (c'est a dire la plus grande valeur entre la marge basse de l'objet, + 'le spacing, et la marge haute de l'objet suivant.) + fMaxSpc = Max(ochild.Margin._Bottom, fspc) + If i < aPageItems.Max Then fMaxSpc = Max(fMaxSpc, aPageItems[i + 1].Ctrl.Margin._Top) + 'on incrémente la position verticale de l'objet + 'pour définir sa place sur la page relativement aux autres éléments + fTmpY += ftmpHeight + fMaxSpc + + Endif + + 'Si l'objet est un conteneur alors il faut vérifier si il a finit d'être traité + + If oChild Is ReportContainer Then + 'If oChild._NotFinished Then Me.NotFinished = True + 'Si son traitement n'est pas terminé alors on trouve + 'sa position dans la liste des enfant et on l'assigne a l'index de traitement + 'si sa position est > a l'index courant + 'If oChild.Tag = "VB1" Then Stop + If ochild._CurItem <= oChild.Children.Max Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'Si on est dans la lignée d'un conteneur fixe on incrémente pas la lecture des enfants + 'En clair ce sont toujours les mêmes éléments qui apparaitrons + If bInFixed Then ochild._CurItem = 0 + Endif + Next + 'On ajoute cette page au dossier du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End diff --git a/comp/src/gb.report2/.src/ReportControl.class b/comp/src/gb.report2/.src/ReportControl.class new file mode 100644 index 00000000..9e7f9cf2 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportControl.class @@ -0,0 +1,648 @@ +' Gambas class file + +Export +Create Private + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +Public Const _Properties As String = "Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Margin,Ignore,Expand,AutoResize,Tag,Range" +Public Const _Family As String = "Report" +Public _SizeInt As ReportSizeHints +Public _Count As Integer = 1 +Public _Finished As Boolean +Private $iDataindex As Integer + +Private $iIndex As Integer +Public _PageDataIndex As Integer +Public Name As String +'Object Management +Static Public _ObjectFromId As New Collection +Static Public _iCurPagePos As Integer +Static Private $iLastId As Integer +Static Private $aFont As String[] = ["DejaVu Serif", "Liberation Serif", "Bitstream Vera Serif", "Serif", "Arial"] + +Property Read Id As Integer +Property Read Parent As ReportContainer +Property Tag As Variant +Property Padding As ReportPadding +Property Margin As ReportMargin +Property Brush As ReportBrush +'Property {Color} As Integer + +Property Read _Top As Float ''Top of the control in cm +Property Read _Height As Float ''Height of the control in cm +Property Read _Width As Float ''Width of the control in cm +Property Read _Left As Float ''Left of the control in cm +Property Read _RelativeLeft As Boolean ''Use percentage for Left pos? +Property Read _RelativeTop As Boolean ''Use percentage for Top Pos? +Property Read _RelativeWidth As Boolean ''Use percentage for width? +Property Read _RelativeHeight As Boolean ''Use percentage for height ? +Property _Index As Integer +Property Range As String +Property Left As String +Property Top As String +Property X As String +Property Y As String +Property Width As String +Property Height As String +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean +Property Autoresize As Boolean +Property Read {Report} As Report +Property _ReportId As Integer +Property Read DataIndex As Integer +Property ForceNewPage As Boolean +Property _DataIndex As Integer +'Public _DataIndex As New Collection +Private $bForceNewPage As Boolean +Private $iMyId As Integer +Private $iParentId As Integer +Private $vTag As Variant +Private $iReportId As Integer +Private $hBrush As ReportBrush +Private $iColor As Integer +Private $fLeft As Float = 0.0 +Private $fTop As Float = 0.0 +Private $fWidth As Float = 0.0 +Private $fHeight As Float = 0.0 +Private $sHeight As String = "0cm" +Private $sLeft As String = "0cm" +Private $sWidth As String = "0cm" +Private $sTop As String = "0cm" +Private $hPadding As New ReportPadding +Private $hMargin As New ReportMargin +Private $iVisible As Boolean = True +Private $hFont As New Font +Private $bExpand As Boolean +Private $bFixed As Boolean +Private $bAutoresize As Boolean = False +Private $bRelativeLeft As Boolean +Private $bRelativeTop As Boolean +Private $bRelativeWidth As Boolean +Private $bRelativeHeight As Boolean +Private $bIgnore As Boolean = False +Private $sRange As String + +Public Sub _New(Optional Parent As ReportContainer = Null) + + Dim hRep As Report + + $iMyId = $iLastId + Inc $iLastId + If IsNull(Parent) Then + $iParentId = -1 + $iReportId = $iMyId + + Else + If Parent Is Report Then + hRep = Parent + $iParentId = hRep._Container.id + hRep._Container._Add(Me) + $iReportId = hRep.id + Else + $iParentId = Parent.Id + Parent._Add(Me) + $iReportId = Parent._ReportId + Endif + Endif + +End + +Private Function Id_Read() As Integer + + Return $iMyId + +End + +Private Function Parent_Read() As ReportContainer + + Return ReportControl._ObjectFromId[$iParentId] + +End + +Private Function Left_Read() As String + + Return $sLeft + +End + +Private Sub Left_Write(Value As String) + + $sLeft = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Top_Read() As String + + Return $sTop + +End + +Private Sub Top_Write(Value As String) + + $sTop = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + $bExpand = Value + Me.Report._LayoutIsDirty = True + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, TotalWidth As Float, TotalHeight As Float, DataIndex As Integer) As ReportSizeHints + + 'Error.Raise("Something goes wrong the _GetSizeHints is not correctly implemented") + 'Implementaion standart du sizeInt hors autoresize et hbox + ' + + Dim hMyHints As New ReportSizeHints + + If Me._RelativeHeight Then + hMyHints.Height = TotalHeight * Me._Height / 100 + Else + hMyHints.Height = Me._Height + Endif + + If Me._RelativeWidth Then + hMyHints.Width = TotalWidth * Me._Width / 100 + Else + hMyHints.Width = Me._Width + Endif + Return hMyHints + +End + +Public Sub _PaintBeFore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + +End + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Me._Paint(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Private Function Visible_Read() As Boolean + + Return $iVisible + +End + +Private Sub Visible_Write(Value As Boolean) + + $iVisible = Value + Me.Report._LayoutIsDirty = True + +End + +Public Sub Move(X As Float, Y As Float, W As Float, H As Float) + + $fLeft = X + $fTop = Y + $fWidth = W + $fHeight = H + Me.Report._LayoutIsDirty = True + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + +End + +Private Function Font_Read() As Font + + Dim hFont As Font + 'Dim sFont As String + Dim s As String + + If Not $hFont Then + If $iParentId = -1 Then + For Each s In $aFont + If Fonts.Exist(s) Then + hFont = New Font + hFont.Name = s + Break + Endif + Next + If hFont Then + hFont.Size = 12 + Else + hFont = Paint.Font.Copy + Endif + + Else + hFont = _ObjectFromId[$iParentId].font + Endif + Return hFont + Else + Return $hFont + Endif + +End + +Private Sub Font_Write(Value As Font) + + $hFont = Value '.Copy() + Me.Report._LayoutIsDirty = True + +End + +Private Function Padding_Read() As ReportPadding + + Return $hPadding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + $hPadding = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function _Top_Read() As Float + + Return $fTop + +End + +Private Function _Height_Read() As Float + + Return $fHeight + +End + +Private Function _Width_Read() As Float + + Return $fWidth + +End + +Private Function _Left_Read() As Float + + Return $fLeft + +End + +Public Function _SetChildGeometry((X) As Float, (Y) As Float, (W) As Float, (H) As Float, (ContPage) As Integer, (TCont) As TControl, (bInFixed) As Boolean) + +End + +Private Function Autoresize_Read() As Boolean + + Return $bAutoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + $bAutoresize = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function _RelativeWidth_Read() As Boolean + + Return $bRelativeWidth + +End + +Private Function _RelativeHeight_Read() As Boolean + + Return $bRelativeHeight + +End + +''Convert recursively string values to unified values in cm +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + 'Left + hSizeParse = New TSizeParse($sLeft, True) + $fLeft = hSizeParse.GetValue() + $bRelativeLeft = hSizeParse.IsRelative() + + 'Right + hSizeParse = New TSizeParse($sTop, True) + $fTop = hSizeParse.GetValue() + $bRelativeTop = hSizeParse.IsRelative() + + 'Width + hSizeParse = New TSizeParse($sWidth, True) + $fWidth = hSizeParse.GetValue() + $bRelativeWidth = hSizeParse.IsRelative() + + 'Height + hSizeParse = New TSizeParse($sHeight, True) + $fHeight = hSizeParse.GetValue() + $bRelativeHeight = hSizeParse.IsRelative() + + 'Padding + ' hSizeParse = New TSizeParse($sPadding, True) + ' $fPadding = hSizeParse.GetValue() + ' $bRelativePadding = hSizeParse.IsRelative() + + hSizeParse = New TSizeParse($hPadding.Left, True) + $hPadding._Left = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Right, True) + $hPadding._Right = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Top, True) + $hPadding._Top = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Bottom, True) + $hPadding._Bottom = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Left, True) + $hMargin._Left = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Right, True) + $hMargin._Right = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Top, True) + $hMargin._Top = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Bottom, True) + $hMargin._Bottom = hSizeParse.GetValue() + + ' + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + $bIgnore = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Report_Read() As Report + + Return ReportControl._ObjectFromId[$iReportId] + +End + +Private Function _ReportId_Read() As Integer + + Return $iReportId + +End + +Private Function Fixed_Read() As Boolean + + Return $bFixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + $bFixed = Value + +End + +Public Sub _Reset() + + Me._DataIndex = 0 + +End + +Private Function X_Read() As String + + Return $sLeft + +End + +Private Sub X_Write(Value As String) + + $sLeft = Value + +End + +Private Function Y_Read() As String + + Return $sTop + +End + +Private Sub Y_Write(Value As String) + + $sTop = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return $hBrush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + $hBrush = Value + +End + +' Private Function Color_Read() As Integer +' +' Return $iColor +' +' End +' +' Private Sub Color_Write(Value As Integer) +' +' $iColor = Value +' +' End + +Public Function _GetActualBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + + If $hBrush Then + hBrush = $hBrush._PaintBrush(X1, Y1, X2, Y2) + Else If $iColor Then + hBrush = Paint.Color($iColor) + Else + Try hBrush = Me.Parent._GetActualBrush(X1, Y1, X2, Y2) + If Not hBrush Then + hBrush = Paint.Color(0) + Endif + Endif + Return hBrush + +End + +Private Sub _ReportId_Write(Value As Integer) + + $iReportId = Value + +End + +Private Function _RelativeLeft_Read() As Boolean + + Return $bRelativeLeft + +End + +Private Function _RelativeTop_Read() As Boolean + + Return $bRelativeTop + +End + +Private Function DataIndex_Read() As Integer + + Return Me._Index 'Return Me._GetIndex() + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + $sRange = Value + +End + +Public Function _GetIndex() As Integer + + Return Me._Index + 'Return Me.Parent._GetIndex() + +End + +Private Function _Index_Read() As Integer + + Return $iIndex + +End + +Private Sub _Index_Write(Value As Integer) + + $iIndex = Value + +End + +Private Function _DataIndex_Read() As Integer + + Return $iDataindex + +End + +Private Sub _DataIndex_Write(Value As Integer) + 'If Me.Tag = "Boite 2" And Value = 0 Then Stop + 'If Me.Report.$iCurPage = 0 Then Print "DataIndex: " & value + 'If Value = 0 Then Stop + + $iDataindex = Value + 'Stop + 'Print "_DataIndex " & Me.Tag & " = " & $iDataindex + +End + +Private Function ForceNewPage_Read() As Boolean + + Return $bForceNewPage + +End + +Private Sub ForceNewPage_Write(Value As Boolean) + + $bForceNewPage = Value + +End + +Public Sub _Free() + +End + +Private Function Margin_Read() As ReportMargin + + Return $hMargin + +End + +Private Sub Margin_Write(Value As ReportMargin) + + $hMargin = Value + +End + +Public Sub Raise() + + _ObjectFromId[$iParentId]._Raise(Me) + +End + +Public Sub Lower() + + _ObjectFromId[$iParentId]._Lower(Me) + +End diff --git a/comp/src/gb.report2/.src/ReportDrawingArea.class b/comp/src/gb.report2/.src/ReportDrawingArea.class new file mode 100644 index 00000000..d9b9deae --- /dev/null +++ b/comp/src/gb.report2/.src/ReportDrawingArea.class @@ -0,0 +1,96 @@ +' Gambas class file + +Export +Inherits ReportFrame +Public Const _Properties As String = "*,Cached" +Public Const _Similar As String = "ReportImage" +Public Const _DefaultEvent As String = "Draw" +Public SizeHints As ReportSizeHints +Property Cached As Boolean + +Private $bCached As Boolean +Private $iOldResolution As Integer +Event Layout(Width As Float, Height As Float, Index As Integer) +Event Draw(Width As Float, Height As Float, Index As Integer) + + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + Dim hMySize As ReportSizeHints + SizeHints = Null + Raise Layout(AvailableW, AvailableH, DataIndex) + + hMySize = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If SizeHints <> Null Then + hMySize.Width = SizeHints.Width + hMySize.Height = SizeHints.Height + Endif + Endif + Try hMySize.NotFinished = SizeHints.NotFinished + Return hMySize + + +End + + + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + 'Dim hRect As Rect + Dim iX, iY, iW, iH As Integer + Dim hImg As Image + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + iH = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + Paint.Save + + + If $bCached Then + If $iOldResolution <> Paint.ResolutionX Or If hControl.Cache = Null Then + $iOldResolution = Paint.ResolutionX + hImg = New Image(iW, iH, Color.Transparent) + Paint.Begin(hImg) + Raise Draw(iW, iH, DataIndex) + Paint.End + hControl.Cache = himg + Else + 'If hControl.cache = Null Then Stop + Try hImg = hControl.Cache + Endif + Draw.Image(hImg, iX, iy) + Else + + 'Paint.Translate(iX, iY) + 'Print "Paint.Begin: "; iX;; iY;; iW;; iH;; "(";; Paint.W;; Paint.H; ")" + Paint.Begin(Paint.Device, RectF(iX, iY, iW, iH)) + Raise Draw(iW, iH, DataIndex) + Paint.End + 'Paint.Translate(- iX, - iY) + Endif + Paint.Restore + +End + +Private Function Cached_Read() As Boolean + + Return $bCached + +End + +Private Sub Cached_Write(Value As Boolean) + + $bCached = Value + +End + +' Public Sub SetHints(Width As Integer, Height As Integer, NotFinished As Boolean) +' $hSizeHints = New ReportSizeHints +' $hSizeHints.Width = Width +' $hSizeHints.Height = Height +' $hSizeHints.NotFinished = NotFinished +' +' End + diff --git a/comp/src/gb.report2/.src/ReportFrame.class b/comp/src/gb.report2/.src/ReportFrame.class new file mode 100644 index 00000000..cb15dd20 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportFrame.class @@ -0,0 +1,366 @@ +' Gambas class file + +Export +Create Private +Inherits ReportControl +' Static Private iLevel As Integer +Public Const _Properties As String = "*,Border,Background{ReportBrush},BoxShadow" +Private $hBorder As New ReportBorder +Private $hBackGround As ReportBrush + +Property Border As ReportBorder +Property BoxShadow As ReportBoxShadow + +Private $hBoxShadow As New ReportBoxShadow +Property BackGround As ReportBrush +'Property Read _BorderWidth As Float + +Private Function Border_Read() As ReportBorder + + Return $hBorder + +End + +Private Sub Border_Write(Value As ReportBorder) + + $hBorder = Value + +End + +Public Sub _PaintBefore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2, W, H As Float + Dim iBorder As Integer + Dim hShadowActive As Boolean = $hBoxShadow._Active + Dim himgShadow As Image + Dim fSpread As Float + Dim fBlur As Float + Dim fbx As Float + Dim fby As Float + Dim TL1, TL2, TR1, TR2, BR1, BR2, BL1, BL2 As Float + ' If Me.Tag = "**" Then Stop + 'If Me.tag = "*" Then Stop + 'Return + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + W = hControl.RealWidth + H = hControl.RealHeight + X2 = (x + hControl.RealLeft + W) + Y2 = (y + hControl.RealTop + H) + + 'Clipping + paint.save + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Green) + + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + Paint.Stroke + + Else + + If $hBorder.RoundCorner._Active Then + TL1 = Me.Report._ToPixels($hBorder.RoundCorner._TopLeft1) + TR1 = Me.Report._ToPixels($hBorder.RoundCorner._TopRight1) + BR1 = Me.Report._ToPixels($hBorder.RoundCorner._BottomRight1) + BL1 = Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft1) + TL2 = Me.Report._ToPixels($hBorder.RoundCorner._TopLeft2) + TR2 = Me.Report._ToPixels($hBorder.RoundCorner._TopRight2) + BR2 = Me.Report._ToPixels($hBorder.RoundCorner._BottomRight2) + BL2 = Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft2) + + If hShadowActive Then + fbx = Me.Report._ToPixels($hBoxShadow._XOffset) + fby = Me.Report._ToPixels($hBoxShadow._YOffset) + fSpread = Me.Report._ToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = Me.Report._ToPixels($hBoxShadow._Blur) + himgShadow = New Image(W + fSpread * 2 + fBlur * 2, H + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(X1 + fbx - fSpread, Y1 + fby - fSpread, W + fSpread * 2, H + fSpread * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Endif + Endif + + iBorder = Me.Report._ToPixels($hBorder._Top) + + RoundRect(X1 + iBorder, Y1 + iBorder, W - iBorder * 2, H - iBorder * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + + Else + + If hShadowActive Then + fbx = Me.Report._ToPixels($hBoxShadow._XOffset) + fby = Me.Report._ToPixels($hBoxShadow._YOffset) + fSpread = Me.Report._ToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = Me.Report._ToPixels($hBoxShadow._Blur) + himgShadow = New Image(W + fSpread * 2 + fBlur * 2, H + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.rectangle(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.Rectangle(X1 + fbx - fSpread, Y1 + fby - fSpread, W + fSpread * 2, H + fSpread * 2) + Paint.Fill + Endif + Endif + + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + + Endif + + If Me.BackGround = Null Then + If hShadowActive Then + Paint.Clip(True) + Paint.Brush = _GetActualBackGround(X1, Y1, X2, Y2) + Paint.Fill + Else + paint.Clip + Endif + 'Return + Else + paint.Clip(True) + paint.Brush = Me.BackGround._PaintBrush(X1, Y1, X2, Y2) + Paint.Fill + Endif + + Endif + +End + +Public Function _GetActualBackGround(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBackground As PaintBrush + + If $hBackGround Then + hBackground = $hBackGround._PaintBrush(X1, Y1, X2, Y2) + Else + Try hBackground = Me.Parent._GetActualBackGround(X1, Y1, X2, Y2) + If Not hBackground Then + hBackground = Paint.Color(Color.White) + Endif + Endif + Return hBackground + +End + + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + _PaintBefore(Page, X, Y, hControl, DataIndex) + Me._Paint(Page, X, Y, hControl, DataIndex) + _PaintAfter(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2, W, H As Float + 'Dim fWidth As Float + + Dim fTopWidth, fBottomWidth, fLeftWidth, fRighWidth As Float + 'Return + 'Fin du clipping + paint.Restore + + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + X2 = (x + hControl.RealLeft + hControl.RealWidth) + Y2 = (y + hControl.RealTop + hControl.RealHeight) + 'If Me.Tag = "**" Then Stop + If $hBorder.RoundCorner._Active Then + Paint.AntiAlias = True + fLeftWidth = Me.Report._ToPixels($hBorder._Left) / 2 + paint.LineWidth = fLeftWidth * 2 + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + ' RoundRect(Paint.LineWidth / 2, Paint.LineWidth / 2, Paint.Width - Paint.LineWidth, Paint.Height - Paint.LineWidth, aX, aY) + RoundRect(X1 + fLeftWidth, Y1 + fLeftWidth, hControl.RealWidth - fLeftWidth * 2, hControl.RealHeight - fLeftWidth * 2, + [Me.Report._ToPixels($hBorder.RoundCorner._TopLeft1), + Me.Report._ToPixels($hBorder.RoundCorner._TopRight1), + Me.Report._ToPixels($hBorder.RoundCorner._BottomRight1), + Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft1)], + [Me.Report._ToPixels($hBorder.RoundCorner._TopLeft2), + Me.Report._ToPixels($hBorder.RoundCorner._TopRight2), + Me.Report._ToPixels($hBorder.RoundCorner._BottomRight2), + Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft2)]) + paint.Stroke + + Else + 'Paint.AntiAlias = False + fLeftWidth = Me.Report._ToPixels($hBorder._Left) + fRighWidth = Me.Report._ToPixels($hBorder._Right) + fTopWidth = Me.Report._ToPixels($hBorder._Top) + fBottomWidth = Me.Report._ToPixels($hBorder._Bottom) + + 'If $hBorder.Style <> Line.None Then + + 'fWidth = Me.Report._ToPixels($fBorderWidth) + + 'Paint.Brush = Paint.Color($hBorder.Brush._iValue) + + If fTopWidth > 0 Then + paint.Brush = $hBorder.Top.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, X2 - X1, fTopWidth) + paint.Fill + Endif + If fRighWidth > 0 Then + paint.Brush = $hBorder.Right.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X2 - fRighWidth, Y1, fRighWidth, Y2 - Y1) + paint.Fill + Endif + If fBottomWidth > 0 Then + paint.Brush = $hBorder.Bottom.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y2 - fBottomWidth, X2 - X1, fBottomWidth) + paint.Fill + Endif + If fLeftWidth > 0 Then + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, fLeftWidth, Y2 - Y1) + paint.Fill + Endif + Paint.AntiAlias = True + If Report.Debug Then + Paint.Text(DataIndex, X1, Y1, 30, 30, Align.Center) + Paint.Fill + Endif + Endif + 'Dec iLevel + 'Print String(iLevel, " ") & "restoré" + + 'Endif + +End + +Private Function BackGround_Read() As ReportBrush + + Return $hBackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + $hBackGround = Value + +End + +Public Sub _NormalizeUnits() + + Super._NormalizeUnits() + $hBorder._NormalizeUnits + $hBoxShadow._NormalizeUnits + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As ReportSizeHints + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If hMyHints.Height <= 0 Then + hMyHints.Height = Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + 'hMyHints.Height += Abs($hBoxShadow._YOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + If hMyHints.Width <= 0 Then + hMyHints.Width = Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right + 'hMyHints.Width += Abs($hBoxShadow._xOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + Return hMyHints + +End + +Static Private Sub RoundRect(x As Float, y As Float, w As Float, h As Float, Radius_X As Float[], radius_y As Float[]) + + Dim ARC_TO_BEZIER As Float = 0.55228475 + Dim c1, c2 As Float + Dim i As Integer + + For i = 0 To 3 + If radius_x[i] > w - radius_x[i] Then + radius_x[i] = w / 2 + Endif + + If radius_y[i] > h - radius_y[i] Then + radius_y[i] = h / 2 + Endif + + Next + 'approximate(quite Close )the arc using a bezier curve + + ' A**********B + ' H C + ' * * + ' * * + ' G D + ' F**********E + + '-->A + Paint.MoveTo(x + radius_x[0], y) + + '-->B + Paint.LineTo(x + w - radius_x[1], y) + + '-->C + c1 = ARC_TO_BEZIER * radius_x[1] + c2 = ARC_TO_BEZIER * radius_y[1] + Paint.RelCurveTo(c1, 0.0, radius_x[1], c2, radius_x[1], radius_y[1]) + + '-->D + Paint.LineTo(x + w, y + h - radius_y[2]) + + '-->E + c1 = ARC_TO_BEZIER * radius_x[2] + c2 = ARC_TO_BEZIER * radius_y[2] + Paint.RelCurveTo(0.0, c2, c1 - radius_x[2], radius_y[2], -radius_x[2], radius_y[2]) + + '-->F + Paint.LineTo(x + radius_x[3], y + h) + + '-->G + c1 = ARC_TO_BEZIER * radius_x[3] + c2 = ARC_TO_BEZIER * radius_y[3] + Paint.RelCurveTo(-c1, 0, -radius_x[3], -c2, -radius_x[3], -radius_y[3]) + + '-->H + Paint.LineTo(x, y + radius_y[0]) + + '-->A + c1 = ARC_TO_BEZIER * radius_x[0] + c2 = ARC_TO_BEZIER * radius_y[0] + Paint.relcurveto(0.0, -c2, radius_x[0] - c1, -radius_y[0], radius_x[0], -radius_y[0]) + + 'Paint.closepath() + +End + +Private Function BoxShadow_Read() As ReportBoxShadow + + Return $hBoxShadow + +End + +Private Sub BoxShadow_Write(Value As ReportBoxShadow) + + $hBoxShadow = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report2/.src/ReportGridView/ReportGridView.class b/comp/src/gb.report2/.src/ReportGridView/ReportGridView.class new file mode 100644 index 00000000..8da07e70 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/ReportGridView.class @@ -0,0 +1,52 @@ +' Gambas class file + +'Export +Inherits ReportFrame + +Private $hRows As New _ReportGridViewRows +Private $hColumns As New _ReportGridViewColumns +Private $hData As New _ReportGridViewData + +Property Read Columns As _ReportGridViewColumns +Property Read Rows As _ReportGridViewRows +Property Read Data As _ReportGridViewData + +Private Function Columns_Read() As _ReportGridViewColumns + + Return $hColumns + +End + +Private Function Rows_Read() As _ReportGridViewRows + + Return $hRows + +End + +Private Function Data_Read() As _ReportGridViewData + + Return $hData + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, TotalWidth As Float, TotalHeight As Float, DataIndex As Integer) As ReportSizeHints + + Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + +End + +Public Sub _NormalizeUnits() + + Super._NormalizeUnits() + + +End + +Public Function _SetChildGeometry((X) As Float, (Y) As Float, (W) As Float, (H) As Float, (ContPage) As Integer, (TCont) As TControl, (bInFixed) As Boolean) + +End + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class new file mode 100644 index 00000000..84582cfe --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class @@ -0,0 +1,96 @@ +' Gambas class file + +Property Width, W As String +Property Text, Title As String +Property Resizable As Boolean +Property Expand As Boolean +Property Alignment As Integer +Property Background As ReportBrush + +Private $sWidth As String +Private $sText As String +Private $bExpand As String +Private $iAlignment As Integer +Private $hBackGround As New ReportBrush +Private $bResizable As Integer +Public _Width As Float + +Public _Column As Integer ''Column Number + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + $bExpand = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Background_Read() As ReportBrush + + Return $hBackGround + +End + +Private Sub Background_Write(Value As ReportBrush) + + $hBackGround = Value + +End + +Public Sub _NormalizeUnits() + + _Width = ReportSizeParser($sWidth) + +End diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class new file mode 100644 index 00000000..cb044baf --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class @@ -0,0 +1,141 @@ +' Gambas class file + +Property Count As Integer +Property Read Max As Integer +Property Read Height, H As String +Property Resizable As Boolean +Property Width, W As String +Property Sort As Integer +Property Ascending As Boolean + +Public _Width As Float +Public _Height As Float + + +Private $iCount As Integer +Private $sHeight As String +Private $bResizable As String +Private $sWidth As String = "2cm" +Private $iSort As Integer +Private $bAscendig As Boolean + +Private $aColumns As New _ReportGridViewColumn[] + + + + +Public Sub _NormalizeUnits() + + Dim i As Integer + + For i = 0 To $aColumns.Max + $aColumns[i]._NormalizeUnits() + Next + + _Width = ReportSizeParser[$sWidth].GetValue() + _Height = ReportSizeParser[$sHeight].GetValue() +End + + + +Private Function Count_Read() As Integer + + Return $aColumns.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim iOldCount As Integer = $aColumns.Count + Dim i As Integer + Dim hCol As _ReportGridViewColumn + + If Value = iOldCount Then Return + + Try $aColumns.Resize(Value) + If Error Then Error.Raise("Bad argument") + + If Value > iOldCount Then + + For i = iOldCount To Value - 1 + hcol = New _ReportGridViewColumn + hcol.Width = "1cm" + $aColumns[i] = hcol + Next + + Endif + + For i = iOldCount To Value + + Next + + +End + +Private Function Max_Read() As Integer + + Return $aColumns.Max + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Sort_Read() As Integer + + Return $iSort + +End + +Private Sub Sort_Write(Value As Integer) + + $iSort = Value + +End + +Private Function Ascending_Read() As Boolean + + Return $bAscendig + +End + +Private Sub Ascending_Write(Value As Boolean) + + $bAscendig = Value + +End + +Private Function GetView() As ReportGridView + + Return Object.Parent(Me) + +End + + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class new file mode 100644 index 00000000..69fe1c11 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class @@ -0,0 +1,52 @@ +' Gambas class file + +Property Height, H As String +Property Text, Title As String +'Property Border As Border + +Public _Height As Float + + + +Private $sHeight As Integer +Private $sText As Integer + +Event _Foo + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _NormalizeUnits() + + _Height = ReportSizeParser[$sHeight].GetValue() + +End + + +Private Function GetRows() As _ReportGridViewRows + + Return Object.Parent(Me) + +End + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class new file mode 100644 index 00000000..0d896f04 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class @@ -0,0 +1,120 @@ +' Gambas class file + +Property Count As Integer +Property Read Max As Integer +Property Width, W As String +Property Resizable As Boolean +Property Height, H As String +'Property Border As Border + +Private $iCount As Integer +Private aLineCol As New Collection +Private $sHeight As String +Private $sWidth As String +Private $bResizable As Boolean + + +Private Function Count_Read() As Integer + + Return $iCount + +End + +Private Sub Count_Write(Value As Integer) + + + Dim i As Integer + Value = Max(1, Value) + If Value < $iCount Then + For i = $iCount DownTo Value + aLineCol[i - 1] = Null + Next + Endif + + $iCount = Value + +End + +Private Function Max_Read() As Integer + + Return $iCount - 1 + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Public Sub _NormalizeUnits() + + Dim hRow As New _ReportGridViewRow + + For Each hRow In aLineCol + + hRow._NormalizeUnits() + + Next + +End + + +Public Function _GetStrRowHeight(iVal As Integer) As String + + If aLineCol.Exist(iVal) Then + Return aLineCol(iVal).Height + Else + Return $sHeight + Endif + +End + +Public Function _SetStrRowHeight(iVal As Integer, sValue As String) + Dim hRow As _ReportGridViewRow + If iVal < 0 Then Return + If iVal > $iCount - 1 Then Return + + hRow = aLineCol[iVal] + If Not hRow Then + hRow = New _ReportGridViewRow + aLineCol[iVal] = hRow + Endif + hRow._Row = iVal + hRow.Height = sValue + + + +End + + diff --git a/comp/src/gb.report2/.src/ReportHBox.class b/comp/src/gb.report2/.src/ReportHBox.class new file mode 100644 index 00000000..95bff9e9 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportHBox.class @@ -0,0 +1,21 @@ +' Gambas class file + +Export + +Inherits ReportContainer + +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "H" + +Public Sub _new() + + Super._Arrangement = Arrange.Horizontal + +End + +Public Sub _Free() + + Super._Free + +End diff --git a/comp/src/gb.report2/.src/ReportImage.class b/comp/src/gb.report2/.src/ReportImage.class new file mode 100644 index 00000000..57ca16d0 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportImage.class @@ -0,0 +1,200 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{Image}" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" + +Private $iAlignment As Integer = Align.Normal +Private $hPic As Image +Private $iStretchMode As Integer = Report.Proportional + +Property Alignment As Integer +Property Image As Image +Property Stretch As Integer + +Public Data As Image + +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + + Dim hPic As Image + Dim fRatio As Float + 'Dim fSize As Float + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + If Me.Stretch = Report.Proportional Then + If hpic.Width > hpic.Height Then + fRatio = hpic.Height / hpic.Width + + hMyHints.Width = Min(Report.UnitToInch(hpic.Width, "px"), AvailableW) + hMyHints.Height = ((hMyHints.Width - (Me.Border._Width + Me.Padding._Width)) * fRatio) + (Me.Padding._Height + Me.Border._Height) + Else + fRatio = hpic.Width / hpic.Height + hMyHints.Height = Min(Report.UnitToInch(hpic.Height, "px"), AvailableH) + hMyHints.Width = ((hMyHints.Height - (Me.Border._Height + Me.Padding._Height)) * fRatio) + (Me.Border._Width + Me.Padding._Width) + Endif + Else + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) 'Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Height, Me.Padding._Top + Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) 'Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + 'Dim hBrush As PaintBrush + Dim hPic As Image + + Dim w, h As Float + + ix = x + hControl.RealLeft '+ Me.Report._ToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ Me.Report._ToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + + Paint.Save + Paint.Rectangle(ix, iy, hControl.RealWidth, hControl.RealHeight) + Paint.Clip + + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + 'hBrush = Paint.Image(hpic) + + If Me.Stretch = Report.Fill Then + iX += Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left) + iY += Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + Paint.DrawImage(hpic, ix, iy, w, h - 1) + + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hpic.Width >= hpic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + Endif + Endif + + Else + w = hpic.Width + h = hpic.H + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += Me.Report._ToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += Me.Report._ToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Bottom) - h + End Select + + Paint.DrawImage(hpic, ix, iy, w, h - 1) + + Endif + Paint.Fill + Paint.Restore +End + +Private Function Image_Read() As Image + + Return $hPic + +End + +Private Sub Image_Write(Value As Image) + + $hPic = Value + 'If Left(Me.Width, 1) = "0" Then Me.Width = $hpic.Width & " px" + 'If Left(Me.Height, 1) = "0" Then Me.Height = $hpic.Height & " px" + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + diff --git a/comp/src/gb.report2/.src/ReportLabel.class b/comp/src/gb.report2/.src/ReportLabel.class new file mode 100644 index 00000000..7bac5c9c --- /dev/null +++ b/comp/src/gb.report2/.src/ReportLabel.class @@ -0,0 +1,239 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Format,Alignment{Align.*},Rotate{Angle:Degrees},UseField" '"*,Text,Key,Format,Alignment{Align.*},UseField" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" +Public Data As String + +Property Text As String +Property Format As String +Property Alignment As Integer +Property Rotate As Float +Property UseField As Boolean +Private $sText As String +Private $sFormat As String +Private $iAlignment As Integer +Private $fAngle As Integer +Private $bUseField As Boolean +Private $hEval As _RepExp +Static Private $iPage As Integer +Static Private $iIndex As Integer +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hSizeHint As ReportSizeHints + Dim hRect As RectF + Dim fWidth, fHeight As Float + Dim sText As String + Dim hExt As PaintExtents + 'Obtenir la taille de l'objet de base en tenant compte + 'de padding et des dimensions fournies + hSizeHint = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'Inutile de chercher a connaitre la taille réclamée par l'objet si elle est + 'soit définie par le parent, soit imposée par l'utilisateur + + If $sText Begins "=" Then + If Not $hEval Or If $hEval.Text <> Right($sText, -1) Then + $hEval = New _RepExp As "Eval" + $hEval.Text = Right($sText, -1) + Endif + Endif + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'Sinon on cherche la tailmle du texte + + If $sText Then + sText = $sText + Else + + Raise Data(DataIndex) + sText = Data + Endif + + + If $sFormat Then + Try sText = Format(sText, $sFormat) + Endif + + + + + Paint.Font = Me.Font + 'If sText = "Gambas" Then Stop + If $fAngle = 0 Then + hRect = Paint.TextSize(sText) + hSizeHint = New ReportSizeHints + fWidth = Me.Border._Left + Me.Padding._Left + Me.Report._FromPixels(hRect.Width) + Me.Padding._Right + Me.Border._Right + fHeight = Me.Border._Top + Me.Padding._Top + Me.Report._FromPixels(hRect.Height) + Me.Padding._Bottom + Me.Border._Bottom + Else + Paint.Save + Paint.Rotate(Rad($fAngle)) + Paint.Text(sText, 0, 0) + hExt = Paint.PathExtents + fWidth = Me.Border._Left + Me.Padding._Left + Me.Report._FromPixels(hExt.Width) + Me.Padding._Right + Me.Border._Right + fHeight = Me.Border._Top + Me.Padding._Top + Me.Report._FromPixels(hExt.Height) + Me.Padding._Bottom + Me.Border._Bottom + Paint.Restore + Endif + + Endif + + hSizeHint.Width = Max(hSizeHint.Width, fWidth) + hSizeHint.Height = Max(hSizeHint.Height, fHeight) + + Return hSizeHint + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim sTempText As Variant + Dim iX, iY, iW, iH As Integer + + $iPage = Page + $iIndex = DataIndex + + If $sText Then + sTempText = $sText + Else + Raise Data(DataIndex) + sTempText = Data + Endif + + If sTempText Begins "=" Then + $hEval.Compile() + sTempText = $hEval.Value + Endif + + If $sFormat Then + Try sTempText = Format(sTempText, $sFormat) + Endif + + If $bUseField Then + sTempText = DecodeText(sTempText, Page) + Endif + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + + iH = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Red) + Paint.Rectangle(ix, iy, iw, ih) + Paint.Stroke + + Endif + + 'Set the Font if it is initialized + + Paint.Font = Me.Font + + If $fAngle = 0.0 Then + + Paint.Text(sTempText, iX, iY, iW, iH, $iAlignment) + Else + Paint.Translate(iX + iW / 2, iY + iH / 2) + Paint.Rotate(Rad($fAngle)) + Paint.Translate(-(iX + iW / 2), -(iY + iH / 2)) + Paint.Text(sTempText, iX, iY, iW, iH, Align.Center) + + Endif + Paint.Fill + +End + +Private Function DecodeText(sText As String, Optional Page As Integer, Optional ForSize As Boolean = False) As String + + If ForSize Then + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", "999") + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", "999") + Else + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", Str(Page + 1)) + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", Str(Me.Report.PageCount)) + Endif + + Return sText + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Private Function Format_Read() As String + + Return $sFormat + +End + +Private Sub Format_Write(Value As String) + + $sFormat = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Rotate_Read() As Float + + Return $fAngle + +End + +Private Sub Rotate_Write(Value As Float) + + $fAngle = Value + +End + +Private Function UseField_Read() As Boolean + + Return $bUseField + +End + +Private Sub UseField_Write(Value As Boolean) + + $bUseField = Value + +End + +Public Sub Eval_Data(Value As String) + 'Print "appel" + Select Case LCase(Value) + Case "page" + Last.Data = $iPage + 1 + Case "index" + Last.Data = $iIndex + Case "pages" + Last.Data = Me.Report.PageCount + End Select + +End + diff --git a/comp/src/gb.report2/.src/ReportLine.class b/comp/src/gb.report2/.src/ReportLine.class new file mode 100644 index 00000000..61183082 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportLine.class @@ -0,0 +1,127 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "*,Direction{Align.TopLeft;Top;TopRight;Left;Right;BottomLeft;Bottom;BottomRight}=BottomRight,LineWidth{ReportCoord}=2 px" +Public Const _Similar As String = "ReportTextLabel" +Property Direction As Integer + +Private $fLineWidth As Float = 0.1 + +Private $iLineStyle As Integer = Line.solid +Private $iDirection As Integer = Align.BottomRight +Private $sLineWidth As String = "2px" +Property LineWidth As String +Property LineStyle As Integer + +Public Sub _new() + + Me.Height = "1cm" + Me.Width = "1cm" + +End + +Private Function LineWidth_Read() As String + + Return $sLineWidth + +End + +Private Sub LineWidth_Write(Value As String) + + $sLineWidth = Value + +End + +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + Super._NormalizeUnits() + + hSizeParse = New TSizeParse($sLineWidth) + $fLineWidth = hSizeParse.GetValue() + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + + hMyHints.Height = Me._Height + hMyHints.Width = Me._Width + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iX, iY, W, H, W2, H2 As Integer + + If Me.LineStyle = Line.None Then Return + If Me.LineStyle <> Line.Solid Then Paint.Dash = MUtil.GetBorder(Me.LineStyle) + Paint.LineWidth = Me.Report._ToPixels($fLineWidth) + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top)) + + W = iX + hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Right) + H = iY + hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Bottom) + Paint.Brush = Me._GetActualBrush(iX, iY, W, H) + W2 = (iX + W) / 2 + H2 = (iY + H) / 2 + Select Case $iDirection + Case Align.TopLeft + Paint.MoveTo(W, H) + Paint.LineTo(iX, iY) + Case Align.Top + Paint.MoveTo(W2, H) + Paint.LineTo(W2, iY) + Case Align.TopRight + Paint.MoveTo(iX, H) + Paint.LineTo(W, iY) + Case Align.Left + Paint.MoveTo(W, H2) + Paint.LineTo(iX, H2) + Case Align.Right + Paint.MoveTo(iX, H2) + Paint.LineTo(W, H2) + Case Align.BottomLeft + Paint.MoveTo(W, iY) + Paint.LineTo(iX, H) + Case Align.Bottom + Paint.MoveTo(W2, iY) + Paint.LineTo(W2, H) + Case Align.BottomRight + Paint.MoveTo(iX, iY) + Paint.LineTo(W, H) + End Select + + Paint.Stroke + +End + +Private Function LineStyle_Read() As Integer + + Return $iLineStyle + +End + +Private Sub LineStyle_Write(Value As Integer) + + $iLineStyle = Value + +End + +Private Function Direction_Read() As Integer + + Return $iDirection + +End + +Private Sub Direction_Write(Value As Integer) + + $iDirection = Value + +End diff --git a/comp/src/gb.report2/.src/ReportPageBreak.class b/comp/src/gb.report2/.src/ReportPageBreak.class new file mode 100644 index 00000000..ea95ef98 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportPageBreak.class @@ -0,0 +1,6 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "-*" diff --git a/comp/src/gb.report2/.src/ReportPanel.class b/comp/src/gb.report2/.src/ReportPanel.class new file mode 100644 index 00000000..f0ba1039 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportPanel.class @@ -0,0 +1,25 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,Arrangement{Arrange.None;Vertical;Horizontal}=None" +Property Arrangement As Integer + +Public Sub New() + + Super._Arrangement = Arrange.None + +End + + +Private Function Arrangement_Read() As Integer + + Return Super._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Super._Arrangement = Value + +End diff --git a/comp/src/gb.report2/.src/ReportSvgImage.class b/comp/src/gb.report2/.src/ReportSvgImage.class new file mode 100644 index 00000000..31820c81 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportSvgImage.class @@ -0,0 +1,200 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Private $hPic As SvgImage +Private $iStretchMode As Integer = Report.Proportional +Private $iAlignment As Integer = Align.Normal +'Private $sPath As String +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{SvgImage}" +Public Const _Similar As String = "ReportTextLabel" +Public Data As SvgImage +Property Image As SvgImage +Property Stretch As Integer +Property Alignment As Integer +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + Dim fRatio As Float + 'Dim h As Float = IIf(Me._Height > AvailableH, AvailableH, Me._Height) + 'Dim w As Float = IIf(Me._Width > AvailableW, AvailableW, Me._Width) + + Dim hPic As SvgImage + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalWidth, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + If Me.Stretch = Report.Proportional Then + If hpic.Width > hpic.Height Then + fRatio = hpic.Height / hpic.Width + + hMyHints.Width = Min(Report.UnitToInch(hpic.Width, "px"), AvailableW) + hMyHints.Height = ((hMyHints.Width - (Me.Border._Width + Me.Padding._Width)) * fRatio) + (Me.Padding._Height + Me.Border._Height) + Else + fRatio = hpic.Width / hpic.Height + hMyHints.Height = Min(Report.UnitToInch(hpic.Height, "px"), AvailableH) + hMyHints.Width = ((hMyHints.Height - (Me.Border._Height + Me.Padding._Height)) * fRatio) + (Me.Border._Width + Me.Padding._Width) + Endif + Else + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) 'Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Height, Me.Padding._Top + Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) 'Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + 'Dim hBrush As PaintBrush + Dim hPic As SvgImage + + Dim w, h As Float + + ix = x + hControl.RealLeft '+ Me.Report._ToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ Me.Report._ToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + Paint.Save + Paint.Rectangle(ix, iy, hControl.RealWidth, hControl.RealHeight) + Paint.Clip + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + 'hBrush = Paint.Image(hpic) + + If $iStretchMode = Report.Fill Then + iX += Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left) + iY += Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + ' hBrush.Translate(ix, iy - 1) + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + hpic.Width = w + hpic.Height = h + Paint.MoveTo(ix, iy) + hpic.Paint + 'Paint.Rectangle(ix, iy, w, h) + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hPic.Width >= hPic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + Endif + Endif + + Else + w = hPic.Width + h = hPic.Height + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += Me.Report._ToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += Me.Report._ToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Bottom) - h + End Select + 'hBrush.Translate(ix, iy - 1) + hpic.Width = W + hPic.Height = H + Paint.MoveTo(iX, iY) + hpic.Paint + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + 'Paint.Rectangle(ix, iy, w, h - 1) + Endif + Paint.Restore +End + +Private Function Image_Read() As SvgImage + + Return $hPic + +End + +Private Sub Image_Write(Value As SvgImage) + + $hPic = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End diff --git a/comp/src/gb.report2/.src/ReportTextLabel.class b/comp/src/gb.report2/.src/ReportTextLabel.class new file mode 100644 index 00000000..9f121ffe --- /dev/null +++ b/comp/src/gb.report2/.src/ReportTextLabel.class @@ -0,0 +1,143 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Alignment{Align.*}" '"*,Text,Key,Format,Alignment{Align.*},UseField" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" +Public Data As String + +Property Text As String +Property Alignment As Integer + +Private $sText As String +Private $iAlignment As Integer + +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hSizeHint As ReportSizeHints + Dim hRect As RectF + Dim fWidth, fHeight As Float + Dim sText As String + 'Dim hExt As PaintExtents + 'Obtenir la taille de l'objet de base en tenant compte + 'de padding et des dimensions fournies + hSizeHint = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'Inutile de chercher a connaitre la taille réclamée par l'objet si elle est + 'soit définie par le parent, soit imposée par l'utilisateur + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'Sinon on cherche la tailmle du texte + + If $sText Then + sText = $sText + Else + Raise Data(DataIndex) + sText = Data + Endif + + Paint.Font = Me.Font + 'If sText = "Gambas" Then Stop + 'If $fAngle = 0 Then + hRect = Paint.RichTextSize(sText) + hSizeHint = New ReportSizeHints + fWidth = Me.Border._Left + Me.Padding._Left + Me.Report._FromPixels(hRect.Width) + Me.Padding._Right + Me.Border._Right + fHeight = Me.Border._Top + Me.Padding._Top + Me.Report._FromPixels(hRect.Height) + Me.Padding._Bottom + Me.Border._Bottom + + 'Endif + + Endif + + hSizeHint.Width = Max(hSizeHint.Width, fWidth) + hSizeHint.Height = Max(hSizeHint.Height, fHeight) + + Return hSizeHint + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim sTempText As Variant + Dim iX, iY, iW, iH As Integer + + If $sText Then + sTempText = $sText + Else + Raise Data(DataIndex) + sTempText = Data + Endif + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + + iH = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Red) + Paint.Rectangle(ix, iy, iw, ih) + Paint.Stroke + + Endif + + 'Set the Font if it is initialized + + 'Paint.Font = Me.Font + 'If InStr($sText, "color=") Then + Draw.Font = Me.Font + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + Draw.RichText(sTempText, iX, iY, iW, iH, $iAlignment) + 'Else + ' Paint.Font = Me.Font + ' Paint.RichText(sTempText, iX, iY, iW, iH, $iAlignment) + ' Endif + 'Paint.Fill + +End + +Private Function DecodeText(sText As String, Optional Page As Integer, Optional ForSize As Boolean = False) As String + + If ForSize Then + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", "999") + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", "999") + Else + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", Str(Page + 1)) + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", Str(Me.Report.PageCount)) + Endif + + Return sText + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + + + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + + + diff --git a/comp/src/gb.report2/.src/ReportVBox.class b/comp/src/gb.report2/.src/ReportVBox.class new file mode 100644 index 00000000..48a385e1 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportVBox.class @@ -0,0 +1,17 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,ForceNewPage" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "V" + +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report2/.src/ReportVPanel.class b/comp/src/gb.report2/.src/ReportVPanel.class new file mode 100644 index 00000000..9a4a55c6 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportVPanel.class @@ -0,0 +1,26 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,ForceNewPage" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "V" + + +' Definition +' C'est la largeur maximal des objets qui donnes la largeur de la colonne. +' Mais un objet ne peu être plus large que le container principal... soit une colonne. + + + + + +Public Sub _new() + + Super._Arrangement = Arrange.Column + +End + +Public Sub _Free() + +End \ No newline at end of file diff --git a/comp/src/gb.report2/.src/Tests/Module2.module b/comp/src/gb.report2/.src/Tests/Module2.module new file mode 100644 index 00000000..cf0cd71c --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Module2.module @@ -0,0 +1,13 @@ +' Gambas module file + +Public Sub main() + + Dim hreport As New Report13 + + hreport.Path = User.Home &/ "Test.pdf" + + hreport.Preview + + Print hreport.Path + +End diff --git a/comp/src/gb.report2/.src/Tests/Report10.class b/comp/src/gb.report2/.src/Tests/Report10.class new file mode 100644 index 00000000..15819ab8 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report10.class @@ -0,0 +1,15 @@ +' Gambas class file + + +Public Sub Report_Open() + ReportVBox1.DataCount = 100 + ReportVBox2.DataCount = 4 + ReportPanel1.Datacount = 3 + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + 'Last.data = Index + +End diff --git a/comp/src/gb.report2/.src/Tests/Report10.report b/comp/src/gb.report2/.src/Tests/Report10.report new file mode 100644 index 00000000..ed13cf05 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report10.report @@ -0,0 +1,47 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,91) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Tag = "Report" + { ReportVBox1 ReportVBox + #MoveScaled(1,1,62,42) + Expand = True + { ReportLabel1 ReportLabel + #MoveScaled(1,1,60,6) + Text = ("=index") + } + { ReportVBox2 ReportVBox + #MoveScaled(1,7,60,15) + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FF7F00"] + { ReportLabel2 ReportLabel + #MoveScaled(1,1,58,6) + AutoResize = True + Text = ("=Index") + } + { ReportPanel1 ReportPanel + #MoveScaled(1,7,58,6) + Height = "7mm" + Margin = ReportMargin["Top:0mm;Bottom:2mm;Left:1cm;Right:1cm"] + Tag = "hBox" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#00FF00"] + Arrangement = Arrange.Vertical + { ReportLabel3 ReportLabel + #MoveScaled(4,2,62,7) + Padding = ReportPadding["Left:5mm"] + Expand = True + Tag = "label" + Text = ("=\"Index = \" & index") + } + } + } + } + { ReportLabel4 ReportLabel + #MoveScaled(1,43,62,3) + Fixed = True + Text = ("=page & \" / \" & pages") + Alignment = Align.Right + } +} diff --git a/comp/src/gb.report2/.src/Tests/Report13.class b/comp/src/gb.report2/.src/Tests/Report13.class new file mode 100644 index 00000000..c1793151 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report13.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + ReportHBox1.DataCount = 99 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report13.report b/comp/src/gb.report2/.src/Tests/Report13.report new file mode 100644 index 00000000..b90754fc --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report13.report @@ -0,0 +1,41 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,109,91) + Padding = ReportPadding["Top:25mm;Bottom:25mm;Left:25mm;Right:25mm"] + Expand = True + Spacing = "8mm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,1,107,20) + Height = "4cm" + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #7F7F7F;Bottom:1mm #7F7F7F;Left:1mm #7F7F7F;Right:1mm #7F7F7F;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["#EFDFFF"] + BoxShadow = ReportBoxShadow["1mm 1mm 2px 0px #C2C2C2"] + { ReportImage1 ReportImage + #MoveScaled(1,1,19,18) + Width = "4cm" + Height = "4cm" + Visible = False + AutoResize = True + Image = Image.Load("gambas.svg") + } + { ReportLabel1 ReportLabel + #MoveScaled(20,1,83,18) + Brush = ReportBrush["LinearGradient(1,0,1,1,[#5F5F5F,#BFBFBF],[0,1])"] + Font = Font["Bold,+10"] + Expand = True + Text = ("=\"GAMBAS - \" & index") + Alignment = Align.Center + } + } + { ReportLabel3 ReportLabel + #MoveScaled(1,22,107,29) + Fixed = True + Font = Font["+12"] + Text = ("=\"PAGE \" & Page & \" / \" & pages") + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/Report14.class b/comp/src/gb.report2/.src/Tests/Report14.class new file mode 100644 index 00000000..51ffa578 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report14.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + RHBList.DataCount = 300 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report14.report b/comp/src/gb.report2/.src/Tests/Report14.report new file mode 100644 index 00000000..91b4b76b --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report14.report @@ -0,0 +1,63 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,91) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + { ReportHBox1 ReportHBox + #MoveScaled(1,1,62,7) + Fixed = True + Margin = ReportMargin["Bottom:5mm"] + BoxShadow = ReportBoxShadow["1mm 1mm 3px 0px #000000"] + { ReportLabel1 ReportLabel + #MoveScaled(1,1,12,5) + Width = "3cm" + Border = ReportBorder["Top:3px #000000;Bottom:3px #000000;Left:3px #000000;Right:3px #000000;TopLeftCorner:5mm"] + Text = ("Reference") + Alignment = Align.Center + } + { ReportLabel2 ReportLabel + #MoveScaled(13,1,25,5) + Expand = True + Border = ReportBorder["Top:3px #000000;Bottom:3px #000000;Left:3px #000000"] + Text = ("Description") + Alignment = Align.Center + } + { ReportLabel3 ReportLabel + #MoveScaled(38,1,21,5) + Width = "5cm" + Border = ReportBorder["Top:3px #000000;Bottom:3px #000000;Left:3px #000000;Right:3px #000000;TopRightCorner:0mm;BottomLeftCorner:0mm"] + Text = ("Valeur") + Alignment = Align.Center + } + } + { RHBList ReportHBox + #MoveScaled(1,8,62,6) + Margin = ReportMargin["Bottom:1mm"] + { ReportLabel4 ReportLabel + #MoveScaled(1,1,12,4) + Width = "3cm" + Border = ReportBorder["Top:0px #000000;Bottom:3px #000000;Left:3px #000000"] + Text = ("=index") + } + { ReportLabel5 ReportLabel + #MoveScaled(13,1,25,4) + Expand = True + Border = ReportBorder["Top:0px #000000;Bottom:3px #000000;Left:3px #000000"] + } + { ReportLabel6 ReportLabel + #MoveScaled(38,1,21,4) + Width = "5cm" + Border = ReportBorder["Top:0px #000000;Bottom:3px #000000;Left:3px #000000;Right:3px #000000"] + } + } + { ReportPanel1 ReportPanel + #MoveScaled(1,14,62,6) + Expand = True + } + { ReportLabel7 ReportLabel + #MoveScaled(1,20,62,3) + Fixed = True + Text = ("=page & \" on \" & pages") + Alignment = Align.Right + } +} diff --git a/comp/src/gb.report2/.src/Tests/Report15.class b/comp/src/gb.report2/.src/Tests/Report15.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report15.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/Tests/Report15.report b/comp/src/gb.report2/.src/Tests/Report15.report new file mode 100644 index 00000000..e6833c39 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report15.report @@ -0,0 +1,13 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,91) + Index = 0 + Text = ("Section 1") + { ReportImage1 ReportImage + #MoveScaled(1,1,62,22) + Expand = True + Image = Image.Load("tortueface.gif") + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/Report16.class b/comp/src/gb.report2/.src/Tests/Report16.class new file mode 100644 index 00000000..74251a7a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report16.class @@ -0,0 +1,14 @@ +' Gambas class file + + +Public Sub ReportLabel2_Data(Index As Integer) + + Last.Data = "Coucou " & Index + +End + +Public Sub Report_Open() + + ReportHBox1.DataCount = 100 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report16.report b/comp/src/gb.report2/.src/Tests/Report16.report new file mode 100644 index 00000000..8b8aa424 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report16.report @@ -0,0 +1,24 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,119,91) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + { ReportLabel1 ReportLabel + #MoveScaled(1,1,117,8) + Height = "3cm" + Brush = ReportBrush["LinearGradient(0,0,0,0,[#DFBC53,#FFFFFF],[0.97,0.05])"] + Font = Font["Bold,+8"] + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:21mm 6mm;TopRightCorner:21mm 6mm;BottomRightCorner:21mm 6mm;BottomLeftCorner:21mm 6mm"] + Background = ReportBrush["RadialGradient(0.6,0.46,0.5,0.81,0.21,[#FFFFFF,#9F1313,#CF4DCF],[0,1,0.5])"] + BoxShadow = ReportBoxShadow["1mm 1mm 5px 0px #FF5500"] + Text = ("Coucou") + Alignment = Align.Center + } + { ReportHBox1 ReportHBox + #MoveScaled(1,9,117,10) + { ReportLabel2 ReportLabel + #MoveScaled(1,1,112,8) + } + } +} diff --git a/comp/src/gb.report2/.src/Tests/Report17.class b/comp/src/gb.report2/.src/Tests/Report17.class new file mode 100644 index 00000000..c689a4b4 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report17.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + ReportPanel1.DataCount = 30 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report17.report b/comp/src/gb.report2/.src/Tests/Report17.report new file mode 100644 index 00000000..643fb7fa --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report17.report @@ -0,0 +1,27 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,100,91) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Count = 2 + Index = 0 + Text = ("Section 1") + { ReportPanel1 ReportPanel + #MoveScaled(1,1,98,22) + Height = "5cm" + AutoResize = True + { ReportLabel1 ReportLabel + #MoveScaled(0,0,98,21) + Brush = ReportBrush["RadialGradient(0.95,0.04,0.5,0.53,0.21,[#FFFFFF,#FF007F],[0,1])"] + Font = Font["Bold,+17"] + Border = ReportBorder["Top:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);Bottom:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);Left:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);Right:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);TopLeftCorner:12mm;BottomRightCorner:12mm"] + Background = ReportBrush["RadialGradient(0.49,0.46,0.5,0.84,0.14,[#3737DF,#FFFFFF],[0,1])"] + BoxShadow = ReportBoxShadow["1mm 1mm 2mm 0mm #000000"] + Text = ("Coucou") + Alignment = Align.Center + } + } + Index = 1 + Text = ("Section 2") + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/FMain.class b/comp/src/gb.report2/.src/Tests/old/FMain.class new file mode 100644 index 00000000..a593d25e --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/FMain.class @@ -0,0 +1,16 @@ +' Gambas class file + +Public Sub _new() +Dim hLabel As New ReportLabel(Report) +hLabel.Text = "Test" +Report.Padding = ReportPadding["1cm"] +hLabel.Border = ReportBorder["border:1px;toprightcorner:5mm 1cm"] +hLabel.BackGround = ReportBrush["image(icon.png)"] +Report.Preview() +End + +Public Sub Form_Open() +GridView1.Columns.Count = 6 +GridView1.Columns[0].Text = "titi" +GridView1.Rows.Count = 6 +End diff --git a/comp/src/gb.report2/.src/Tests/old/FMain.form b/comp/src/gb.report2/.src/Tests/old/FMain.form new file mode 100644 index 00000000..993e7c82 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/FMain.form @@ -0,0 +1,10 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,112,67) + { GridView1 GridView + MoveScaled(17,14,61,36) + Grid = False + Header = GridView.Both + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Module1.module b/comp/src/gb.report2/.src/Tests/old/Module1.module new file mode 100644 index 00000000..06e1d891 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Module1.module @@ -0,0 +1,19 @@ +' Gambas module file + +Public Sub Main() + + Dim hrc As New Report + Dim vb As ReportVBox + Dim himg As New Image(1, 1) + + hrc.tag = + hrc.Width = "21cm" + hrc.Height = "29.7cm" + vb = New ReportVBox(hrc) + Paint.Begin(himg) + hrc.Layout + Paint.End + Print hrc._Height + Print hrc._Width + +End diff --git a/comp/src/gb.report2/.src/Tests/old/OutputReport2.class b/comp/src/gb.report2/.src/Tests/old/OutputReport2.class new file mode 100644 index 00000000..80e40957 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/OutputReport2.class @@ -0,0 +1,166 @@ +' Gambas class file + +Public nnoderows As Integer +Public nanchorrows As Integer + +Public Sub _new() +'Report.Debug = True + 'enter current date on the header + ReportLabel2.Text = Date + + 'enter the project information in the header on the print out page + 'With FMain.Project + ReportLabel3.Text = "Project Title: " ' & .JobTitle + ReportLabel4.Text = "Project No.: '" '& .Number + ReportLabel5.Text = "Company: " '& .Company + ReportLabel6.Text = "Designer: " '& .Designer + ReportLabel7.Text = "Base Plate ID: " ' & .BasePlateID + 'End With + + 'set count to four to display the results for qmax. Tmax, L, and phi + ReportHBox9.DataCount = 4 + + 'set the number of plate nodes to display in print out tables + ' nnoderows = Module1.RoundUpInt(FMain.ConcreteSection.cNodes.Count / 2) + ReportHBox5.DataCount = 11 'nnoderows + + 'set the number of anchor rods to display in print out tables + 'nanchorrows = Module1.RoundUpInt(FMain.cAnchorRods.Count / 2) + ReportHBox7.DataCount = 13 'nanchorrows + + 'set the number of report panels - one for each load combination result + ReportPanel1.DataCount = 6 'FMain.AnalysisResults.Count + +End + + +Public Sub ReportImage2_Data(Index As Integer) + 'Dim current_results As Results + Dim pSection As Picture + + 'create a new picture object for drawing the base plate + pSection = New Picture(300, 300, False) + + 'get the results for first load case + 'current_results = FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)] + + 'refresh base plate drawing with results + 'Module1.DrawBasePlate(pSection, 1, current_results) + + 'add the picture of the base plate to the page + ReportImage2.Image = pSection.Image + +End + + +Public Sub ReportLabel9_Data(Index As Integer) + + Last.Data = "Load Case: " '& FMain.cLoads[Str$(ReportHBox3.DataIndex)].label + +End + + +Public Sub ReportLabel14_Data(Index As Integer) + 'Dim $value As String + + Select Case Index + Case 0 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].Tmax, 2) + Last.Data = "T(max) = " & 3 & " lbs" + Case 1 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].maxstress, 2) + Last.Data = "q(max) = " & 3 & " psi" + Case 2 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].L, 2) + Last.Data = "L = " & 3 & " in" + Case 3 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].phi, 2) + Last.Data = "phi = " & 3 & " degrees" + Case Else + End Select + +End + + +Public Sub ReportLabel25_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel29_Data(Index As Integer) + ' Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nnoderows + Index < current_results.stress.Count Then + Last.Data = nnoderows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel26_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].stress[Index] + Last.Data = "tt" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel30_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nnoderows + Index < current_results.stress.Count Then + 'value = current_results.stress[nnoderows + Index] + Last.Data = "mm" 'Module1.AdvFormat(value, 2) + ' Else + Last.Data = Null + 'Endif + +End + +Public Sub ReportLabel49_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel52_Data(Index As Integer) + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nanchorrows + Index < current_results.T.Count Then + Last.Data = nanchorrows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel50_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].T[Index] + Last.Data = "kkkk" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel53_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + ' current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nanchorrows + Index < current_results.T.Count Then + ' value = current_results.T[nanchorrows + Index] + Last.Data = "qqqqqq" 'Module1.AdvFormat(value, 2) + ' Else + ' Last.Data = Null + 'Endif + +End + diff --git a/comp/src/gb.report2/.src/Tests/old/OutputReport2.report b/comp/src/gb.report2/.src/Tests/old/OutputReport2.report new file mode 100644 index 00000000..a595b981 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/OutputReport2.report @@ -0,0 +1,305 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,76,88) + Fixed = True + Padding = ReportPadding["Top:20mm;Bottom:10mm;Left:20mm;Right:20mm"] + Ignore = True + AutoResize = True + { ReportHBox1 ReportHBox + #MoveScaled(2,0,62,11) + Height = "25mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportImage1 ReportImage + #MoveScaled(1,1,18,9) + Width = "51mm" + AutoResize = True + } + { ReportLabel1 ReportLabel + #MoveScaled(21,1,10,9) + Width = "25mm" + AutoResize = True + Text = ("Version 1.0") + Alignment = Align.BottomLeft + } + { ReportLabel2 ReportLabel + #MoveScaled(31,1,30,9) + Expand = True + AutoResize = True + Text = ("Date") + Alignment = Align.BottomRight + UseField = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(2,11,62,9) + Height = "28.01mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel3 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project Title:") + Alignment = Align.TopLeft + } + { ReportLabel4 ReportLabel + #MoveScaled(0,2,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project No.:") + Alignment = Align.TopLeft + } + { ReportLabel5 ReportLabel + #MoveScaled(0,4,12,2) + Height = "6mm" + AutoResize = True + Text = ("Company:") + Alignment = Align.TopLeft + } + { ReportLabel6 ReportLabel + #MoveScaled(0,6,12,2) + Height = "6mm" + AutoResize = True + Text = ("Designer:") + Alignment = Align.TopLeft + } + } + { ReportHBox2 ReportHBox + #MoveScaled(2,20,62,3) + Height = "8mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel7 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Base Plate ID:") + Alignment = Align.TopLeft + } + } + { ReportPanel1 ReportPanel + #MoveScaled(2,25,67,52) + AutoResize = True + ForceNewPage = True + { ReportHBox3 ReportHBox + #MoveScaled(1,1,62,17) + Height = "61mm" + Expand = True + { ReportVBox3 ReportVBox + #MoveScaled(1,1,18,14) + AutoResize = True + { ReportHBox8 ReportHBox + #MoveScaled(1,2,15,4) + Width = "51mm" + Height = "12mm" + { ReportLabel9 ReportLabel + #MoveScaled(1,1,13,2) + Font = Font["+1"] + Expand = True + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,8,15,4) + Width = "51mm" + Height = "6mm" + { ReportLabel14 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + } + } + } + { ReportImage2 ReportImage + #MoveScaled(31,1,17,15) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Alignment = Align.Center + } + } + { ReportVBox4 ReportVBox + #MoveScaled(1,20,62,14) + AutoResize = True + { ReportLabel19 ReportLabel + #MoveScaled(2,0,16,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Bearing Pressue") + } + { ReportHBox4 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel20 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel21 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + { ReportLabel22 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel23 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel24 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + } + { ReportHBox5 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel25 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel26 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel27 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel29 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel30 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + { ReportVBox6 ReportVBox + #MoveScaled(1,36,62,14) + AutoResize = True + { ReportLabel31 ReportLabel + #MoveScaled(2,0,19,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Anchor Rod Tension") + } + { ReportHBox6 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel44 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel45 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + { ReportLabel46 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel47 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel48 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + } + { ReportHBox7 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel49 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel50 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel51 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel52 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel53 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + } + { ReportPanel2 ReportPanel + #MoveScaled(2,77,69,1) + Fixed = True + Expand = True + } + { ReportHBox10 ReportHBox + #MoveScaled(2,79,62,4) + Height = "1cm" + Fixed = True + AutoResize = True + { ReportLabel8 ReportLabel + #MoveScaled(22,1,21,2) + Expand = True + AutoResize = True + Text = ("Page $PAGE of $NPAGE") + Alignment = Align.Bottom + UseField = True + } + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report1.class b/comp/src/gb.report2/.src/Tests/old/Report1.class new file mode 100644 index 00000000..414000b0 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report1.class @@ -0,0 +1,43 @@ +' Gambas class file + + +Public Sub Report_Open() +Dim hBox As ReportHBox +Dim i As Integer +'Report.Debug = True +'Me.Spacing = "1cm" + ReportHBox1.Margin.Bottom = "2cm" + 'Report.Debug = True + 'ReportHBox2.Margin.Top = "2cm" + ' ReportHBox1.BoxShadow.Blur = "0.5mm" + ' ReportHBox1.BoxShadow.XOffset = "1mm" + ' ReportHBox1.BoxShadow.YOffset = "1mm" + ' 'ReportHBox1.BoxShadow.Spread = "0.5mm" + ' ReportHBox1.BoxShadow.Color = Color.Gray + + For i = 0 To 14 + hbox = New Reporthbox(ReportVBox1) + HBox.Height = "1cm" + HBox.Border.Width = "2px" + HBox.Margin.Top = "5mm" + HBox.Margin.Bottom = "3mm" + HBox.Margin.Left = "1cm" + HBox.Margin.Right = "2mm" + Next + hBox = New ReportHBox(ReportVBox1) + HBox.BackGround = ReportBrush.Color(Color.Green) + HBox.Height = "3cm" + HBox.Margin.Top = "1cm" + + For i = 0 To 4 + hbox = New Reporthbox(ReportHBox2) + HBox.Width = "1cm" + HBox.Border.Width = "2px" + HBox.Margin.Left = "3mm" + HBox.Margin.Right = "8mm" + HBox.Expand = True + HBox.Margin.Top = "3mm" + HBox.Margin.Bottom = "3px" + Next + ReportHBox2.Padding = ReportPadding["2mm"] +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report1.report b/comp/src/gb.report2/.src/Tests/old/Report1.report new file mode 100644 index 00000000..e29ecb56 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report1.report @@ -0,0 +1,47 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,93,73) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + BoxShadow = ReportBoxShadow["2mm 2mm 0mm 0px #C05800"] + Spacing = "5mm" + { ReportHBox1 ReportHBox + #MoveScaled(1,1,91,14) + Height = "2cm" + Fixed = True + Tag = "**" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:3mm;TopRightCorner:3mm;BottomRightCorner:3mm;BottomLeftCorner:3mm"] + BoxShadow = ReportBoxShadow["0mm 3mm 1mm -4px #FF8000"] + { ReportLabel1 ReportLabel + #MoveScaled(1,1,58,12) + Font = Font["+10"] + Expand = True + AutoResize = True + Text = ("=\"Page \" & Page") + Alignment = Align.Center + } + } + { ReportVBox1 ReportVBox + #MoveScaled(1,16,91,10) + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:10mm;TopRightCorner:20mm;BottomRightCorner:10mm;BottomLeftCorner:1mm"] + Background = ReportBrush["LinearGradient(0.35,0.74,0.95,0.07,[#FFFFFF,#FFFFFF,#007FFF],[0,1,0.36])"] + BoxShadow = ReportBoxShadow["1mm 1mm 1mm 0px #000000"] + } + { ReportHBox2 ReportPanel + #MoveScaled(1,27,91,16) + Height = "20mm" + Fixed = True + { ReportLabel2 ReportLabel + #MoveScaled(10,3,59,9) + Expand = True + Text = ("=pi() + 4") + Format = "0.00" + } + } + { ReportHBox3 ReportHBox + #MoveScaled(1,44,91,6) + Height = "10mm" + Brush = ReportBrush["#FF0000"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report11.class b/comp/src/gb.report2/.src/Tests/old/Report11.class new file mode 100644 index 00000000..ebaa73e2 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report11.class @@ -0,0 +1,32 @@ +' Gambas class file + +'Static Public Type As String + +Public Sub _new(Optional sType As String = "sqlite") + + Dim hResult As Result + Dim Rlbl As ReportLabel + Dim hConn As Connection +'Report.Debug = True + hConn = Connections["Connection1"] + hConn.Type = sType + If hConn.Type = "sqlite" Then hConn.Host = User.Home + Try hConn.Open + If Not hConn.Opened Then + Message.Error(" You have to create the test database with the Database example.") + Return + Endif + + hResult = db.Limit(200).Find("test") + If Not hResult.Available Then Return + + For Each hResult + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Autoresize = True + Rlbl.Text = hResult!name & " " & hResult!firstname + Rlbl.Border.bottom.Width = "1px" + + Next + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report11.report b/comp/src/gb.report2/.src/Tests/old/Report11.report new file mode 100644 index 00000000..0cd75b4b --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report11.report @@ -0,0 +1,98 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,100,77) + Padding = ReportPadding["Top:1cm;Bottom:15mm;Left:2cm;Right:2cm"] + { ReportHBox2 ReportHBox + #MoveScaled(0,0,67,14) + Height = "5cm" + Fixed = True + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#3398C3"] + { ReportSvgImage1 ReportSvgImage + #MoveScaled(1,1,14,12) + Width = "4cm" + Image = SvgImage.Load("gambas.svg") + } + { ReportLabel3 ReportLabel + #MoveScaled(20,2,40,10) + Brush = ReportBrush["LinearGradient(0.71,0.07,0.34,0.06,[#FFFF00,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["DejaVu Sans,Bold,+14"] + Expand = True + AutoResize = True + Text = ("Gambas") + Alignment = Align.Left + } + } + { ReportHBox1 ReportHBox + #MoveScaled(0,15,87,56) + Padding = ReportPadding["0cm"] + Expand = True + Border = ReportBorder["Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + { ReportVBox1 ReportVBox + #MoveScaled(1,1,25,47) + Width = "6cm" + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Background = ReportBrush["#DF6B00"] + { ReportLabel4 ReportLabel + #MoveScaled(3,2,20,4) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold"] + AutoResize = True + Text = ("All friends list !") + } + } + { ReportVBox2 ReportVBox + #MoveScaled(27,1,60,54) + Expand = True + Border = ReportBorder["Left:1mm #000000"] + Background = ReportBrush["#FFFFFF"] + { RVBCont ReportVBox + #MoveScaled(4,4,53,42) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Tag = "*" + Spacing = "1mm" + { ReportLabel1 ReportLabel + #MoveScaled(0,1,44,9) + Fixed = True + Font = Font["Bitstream Charter,Bold,Italic,+2"] + Margin = ReportMargin["Bottom:1cm"] + AutoResize = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + BoxShadow = ReportBoxShadow["1mm 1mm 0px 0px #000000"] + Text = ("Gambas Report Demo") + Alignment = Align.Center + } + { ReportLabel5 ReportLabel + #MoveScaled(3,17,40,21) + Width = "100%" + Height = "100%" + Brush = ReportBrush["#DFDFDF"] + Visible = False + Fixed = True + Font = Font["Bold,+15"] + Ignore = True + Text = ("DEMO") + Alignment = Align.Center + } + } + { ReportLabel2 ReportLabel + #MoveScaled(7,49,47,3) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold,+1"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000"] + Background = ReportBrush["#3398C3"] + Text = ("Page $PAGE on $NPAGE ") + Alignment = Align.Right + UseField = True + } + } + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report12.class b/comp/src/gb.report2/.src/Tests/old/Report12.class new file mode 100644 index 00000000..b833bfd8 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report12.class @@ -0,0 +1,20 @@ +' Gambas class file + + +Public Sub Report_Open() + + TableLine.DataCount = 8 + +End + +Public Sub ReportLabel4_Data(Index As Integer) +Print Index + Last.Data = Index + +End + +Public Sub ReportLabel5_Data(Index As Integer) +Print Index + Last.Data = TableLine.DataIndex + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report12.report b/comp/src/gb.report2/.src/Tests/old/Report12.report new file mode 100644 index 00000000..01ca5b7f --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report12.report @@ -0,0 +1,44 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + { ReportHBox1 ReportHBox + #MoveScaled(0,2,60,6) + Height = "1cm" + Border = ReportBorder["Top:2px #000000;Bottom:2px #000000;Left:2px #000000;Right:2px #000000;TopLeftCorner:2mm;TopRightCorner:2mm"] + { ReportLabel1 ReportLabel + #MoveScaled(1,1,16,4) + Width = "5cm" + } + { ReportLabel2 ReportLabel + #MoveScaled(19,1,18,4) + Width = "5cm" + Border = ReportBorder["Left:2px #000000;Right:2px #000000"] + } + { ReportLabel3 ReportLabel + #MoveScaled(39,1,19,4) + Expand = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(1,12,61,44) + { TableLine ReportHBox + #MoveScaled(0,0,60,4) + Height = "1cm" + Border = ReportBorder["Bottom:2px #000000;Left:2px #000000;Right:2px #000000"] + { ReportLabel4 ReportLabel + #MoveScaled(0,0,17,4) + Width = "5cm" + } + { ReportLabel5 ReportLabel + #MoveScaled(19,1,20,4) + Width = "5cm" + Border = ReportBorder["Left:2px #000000;Right:2px #000000"] + } + { ReportLabel6 ReportLabel + #MoveScaled(42,0,15,4) + } + } + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report2.class b/comp/src/gb.report2/.src/Tests/old/Report2.class new file mode 100644 index 00000000..cc36bd1d --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report2.class @@ -0,0 +1,22 @@ +' Gambas class file + + + + +Public Sub ReportGridView1_Data(Row As Integer, Column As Integer) + + + +End + +Public Sub Report_Open() + + ' ReportGridView1.Columns.Count = 6 + ' ReportGridView1.Rows.Count = 8 + ' ReportGridView1.Columns[2].Width = "6cm" + ' ReportGridView1.Rows[2].Height = "2cm" + ' ReportGridView1.Rows[4].Height = "8px" + ' ReportGridView1.Rows[4].Text = "totolkjdhdolhldhdhl" + ' ReportGridView1.Header = ReportGridView.Both + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report2.report b/comp/src/gb.report2/.src/Tests/old/Report2.report new file mode 100644 index 00000000..a14f53eb --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report2.report @@ -0,0 +1,26 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:15mm;Bottom:15mm;Left:15mm;Right:15mm"] + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(1,3,59,12) + Height = "2cm" + Font = Font["+12"] + Margin = ReportMargin["Bottom:1cm;Left:2cm;Right:2cm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["LinearGradient(0.49,0,0.49,1,[#00BF5C,#FFE79F],[0,1])"] + BoxShadow = ReportBoxShadow["1mm 1mm 1mm 0mm #000000"] + Text = ("ReportLabel1") + Alignment = Align.Center + } + { ReportGridView1 ReportGridView + #MoveScaled(4,24,46,25) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + BoxShadow = ReportBoxShadow["0mm 0mm 2mm 0mm #000000"] + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report3.class b/comp/src/gb.report2/.src/Tests/old/Report3.class new file mode 100644 index 00000000..3b57e5de --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report3.class @@ -0,0 +1,26 @@ +' Gambas class file + + +Public Sub _new() + + Dim i As Integer + Dim hCont As ReportLabel + Dim hBreak As ReportPageBreak + 'Report.Debug = True + ReportLabel1.Border.Width = "0.5mm" + + 'ReportVBox1.Border.Left.Width = "1mm" + 'ReportVBox1.DataCount = 3 + +For i = 0 To 800 + 'If i = 3 Then hBreak = New ReportPageBreak(Me) + hCont = New ReportLabel(Me) + hCont.BoxShadow.Blur = "5mm" + hCont.Border.Width = "0.5mm" + hCont.Height = "1cm" + hCont.Margin.Top = "5mm" + hCont.Text = i +Next + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report3.report b/comp/src/gb.report2/.src/Tests/old/Report3.report new file mode 100644 index 00000000..223afeef --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report3.report @@ -0,0 +1,24 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,57) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + AutoResize = True + Spacing = "5mm" + Paper = Printer.A3 + { ReportLabel1 ReportLabel + #MoveScaled(6,0,52,7) + Font = Font["+14"] + BoxShadow = ReportBoxShadow["3mm 3mm 3mm 0mm #FF8000"] + Text = ("PARCELLAIRE $NPAGE") + Alignment = Align.Center + UseField = True + } + { ReportImage1 ReportImage + #MoveScaled(8,10,41,28) + Height = "14cm" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + BoxShadow = ReportBoxShadow["1mm 1mm 2px 2px #000000"] + Stretch = Report.None + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report4.class b/comp/src/gb.report2/.src/Tests/old/Report4.class new file mode 100644 index 00000000..eb556dc7 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report4.class @@ -0,0 +1,18 @@ +' Gambas class file + + +Public Sub Report_Open() +'Report.Debug = True +rhh.DataCount = 5 +'rv.DataCount = 10 +rv2.DataCount = 3 +' Me.Layout +' Print Me.PageCount +' rv.DataCount = 4 +' Me.Layout +' Print Me.PageCount +' rv.DataCount = 5 +' Print Me.PageCount + +'rh.Raise +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report4.report b/comp/src/gb.report2/.src/Tests/old/Report4.report new file mode 100644 index 00000000..79dfe225 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report4.report @@ -0,0 +1,20 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,111,87) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + { rv2 ReportVBox + #MoveScaled(3,9,107,51) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Tag = "**" + { rhh ReportHBox + #MoveScaled(12,6,84,7) + Height = "1cm" + Margin = ReportMargin["Top:3mm;Bottom:1mm"] + Background = ReportBrush["#00FF00"] + } + { ReportDrawingArea1 ReportDrawingArea + #MoveScaled(10,16,85,27) + } + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report5.class b/comp/src/gb.report2/.src/Tests/old/Report5.class new file mode 100644 index 00000000..6f5e67c2 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report5.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report5.report b/comp/src/gb.report2/.src/Tests/old/Report5.report new file mode 100644 index 00000000..72b9cc31 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report5.report @@ -0,0 +1,31 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,109,80) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + { ReportLabel1 ReportLabel + #MoveScaled(16,1,62,12) + Height = "20mm" + Brush = ReportBrush["LinearGradient(0.67,0,0.35,0,[#000000,#FFFFFF],[0.33,0.73])"] + Font = Font["Bold,+10"] + Margin = ReportMargin["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["RadialGradient(0.7,0.14,0.5,0.84,0.18,[#FFFFFF,#000000],[0,1])"] + Text = ("Coucou") + Alignment = Align.Center + } + { ReportHBox1 ReportHBox + #MoveScaled(19,19,65,16) + Height = "8cm" + Margin = ReportMargin["Bottom:10mm;Left:2cm;Right:2cm"] + Background = ReportBrush["RadialGradient(0.53,0.53,0.5,0.88,0.18,[#000000,#FFFFFF,#0000FF],[0,1,0.5])"] + BoxShadow = ReportBoxShadow["2px 2px 10px 0px #55FF7F"] + } + { ReportVBox1 ReportVBox + #MoveScaled(32,44,62,12) + Height = "2cm" + Margin = ReportMargin["Bottom:10mm"] + Expand = True + Background = ReportBrush["#FF7F00"] + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report6.class b/comp/src/gb.report2/.src/Tests/old/Report6.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report6.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/Tests/old/Report6.report b/comp/src/gb.report2/.src/Tests/old/Report6.report new file mode 100644 index 00000000..4d0b7ae7 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report6.report @@ -0,0 +1,11 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + { ReportLabel1 ReportTextLabel + #MoveScaled(8,4,48,48) + Border = ReportBorder["Top:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);Bottom:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);Left:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);Right:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);TopLeftCorner:1mm 0cm;TopRightCorner:5mm 1cm;BottomRightCorner:1mm 15mm;BottomLeftCorner:3mm 7mm"] + Text = ("Testtest

    \n\n
    ") + Alignment = Align.Center + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report7.class b/comp/src/gb.report2/.src/Tests/old/Report7.class new file mode 100644 index 00000000..b009d3b2 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report7.class @@ -0,0 +1,30 @@ +' Gambas class file + + +Public Sub ReportDrawingArea_Layout() + + + +End + + +Public Sub ReportDrawingArea1_Layout(Width As Float, Height As Float, Index As Integer) + + Last.SizeHints = ReportSizeHints.FromString("3cm", "4cm", False) + +End + + +Public Sub ReportDrawingArea1_Draw(Width As Float, Height As Float, Index As Integer) + +'Print "Largeur passée = " & Width +'Print "Paint.Width = " & Paint.Width + Draw.line(0, 0, Paint.Width, Paint.Height) + +End + +Public Sub Report_Open() + + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report7.report b/comp/src/gb.report2/.src/Tests/old/Report7.report new file mode 100644 index 00000000..c83aaa02 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report7.report @@ -0,0 +1,10 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + { ReportDrawingArea1 ReportDrawingArea + #MoveScaled(4,8,59,34) + Border = ReportBorder["Top:2px #000000;Bottom:2px #000000;Left:2px #000000;Right:2px #000000"] + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report8.class b/comp/src/gb.report2/.src/Tests/old/Report8.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report8.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/Tests/old/Report8.report b/comp/src/gb.report2/.src/Tests/old/Report8.report new file mode 100644 index 00000000..135a17b9 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report8.report @@ -0,0 +1,19 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + { ReportPanel1 ReportPanel + #MoveScaled(1,4,61,53) + Expand = True + { ReportPanel2 ReportPanel + #MoveScaled(16,7,20,14) + Left = "23mm" + Top = "16mm" + Width = "55mm" + Height = "38mm" + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000;TopLeftCorner:5mm 4mm;TopRightCorner:7mm 15mm;BottomRightCorner:5mm;BottomLeftCorner:5mm 2mm"] + Background = ReportBrush["#00FF00"] + BoxShadow = ReportBoxShadow["1mm 1mm 6px -1px #000000"] + } + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report9.class b/comp/src/gb.report2/.src/Tests/old/Report9.class new file mode 100644 index 00000000..fdfb323f --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report9.class @@ -0,0 +1,9 @@ +' Gambas class file + + +Public Sub Report_Open() + + ReportImage1.Image = Image.Load("~/Documents/SCEA BODARD/Banque/Factures Prêt/Prêt N°00000221849/SCB-Distillerie-1-4.jpg") + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report9.report b/comp/src/gb.report2/.src/Tests/old/Report9.report new file mode 100644 index 00000000..c5ed59c3 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report9.report @@ -0,0 +1,9 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + { ReportImage1 ReportImage + #MoveScaled(1,1,53,51) + Expand = True + } +} diff --git a/comp/src/gb.report2/.src/Tools/CPrint.class b/comp/src/gb.report2/.src/Tools/CPrint.class new file mode 100644 index 00000000..414e1d2d --- /dev/null +++ b/comp/src/gb.report2/.src/Tools/CPrint.class @@ -0,0 +1,72 @@ +' Gambas class file + +Create Static + +Private $hPrinter As New Printer As "Printer" +Private $hReport As Report + +Static Public Sub InitPrinterFromReport(hPrinter As Printer, hReport As Report) + + hPrinter.FullPage = True + + hPrinter.Orientation = hReport.Orientation + + If hReport.Paper = Printer.Custom Then + 'pnlCustom.Enabled = True + hPrinter.PaperWidth = Report.UnitTo(TSizeParse[hReport.Width].ToInch(), "in", "mm") + hPrinter.PaperHeight = Report.UnitTo(TSizeParse[hReport.Height].ToInch(), "in", "mm") + Else + hPrinter.Paper = hReport.Paper + Endif + +End + +Public Sub PrintReport(hReport As Report, Optional MyPrinter As Printer) + + Dim hImage As Image + + $hReport = hReport + $hReport.Scale = 1 + '$hPrint = hReport.Clone() + If MyPrinter Then + $hPrinter = MyPrinter + Else + $hPrinter = New Printer + Endif + + Object.Attach($hPrinter, Me, "Printer") + + InitPrinterFromReport($hPrinter, $hReport) + + If Not MyPrinter Then + If $hPrinter.Configure() Then Return + Endif + + ' The report layout only works on Image, i.e. when DPI = 72 + hImage = New Image(1, 1) + Paint.Begin(hImage) + $hReport.Layout() + Paint.End + + $hPrinter.Print + +End + +Public Sub Printer_Draw() + + Paint.FontScale = 25.4 / 72 * Paint.W / $hPrinter.PaperWidth * 72 / 96 * 96 / $hPrinter.Resolution + $hReport.Paint($hPrinter.Page) + +End + +Public Sub Printer_Begin() + + $hPrinter.Count = $hReport.PageCount + +End + +Public Sub Printer_End() + + $hReport = Null + +End diff --git a/comp/src/gb.report2/.src/Tools/MUtil.module b/comp/src/gb.report2/.src/Tools/MUtil.module new file mode 100644 index 00000000..7ff8550f --- /dev/null +++ b/comp/src/gb.report2/.src/Tools/MUtil.module @@ -0,0 +1,24 @@ +' Gambas module file + +Private Dash As Float[] = [2.0, 1.0] +Private Dot As Float[] = [1.0, 1.0] +Private DashDot As Float[] = [2.0, 1.0, 1.0, 1.0] +Private DashDotDot As Float[] = [2.0, 1.0, 1.0, 1.0, 1.0, 1.0] + + +Public Sub GetBorder(iStyle As Integer) As Float[] + + Select Case iStyle + + Case Line.Dash + Return Dash + Case Line.Dot + Return Dot + Case Line.DashDot + Return DashDot + Case Line.DashDotDot + Return DashDotDot + End Select + +End + diff --git a/comp/src/gb.report2/.src/Types/Base/ReportBrush.class b/comp/src/gb.report2/.src/Types/Base/ReportBrush.class new file mode 100644 index 00000000..49b334b2 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Base/ReportBrush.class @@ -0,0 +1,222 @@ +' Gambas class file + +Export + +Public _Type As Integer +Public _X As Float +Public _Y As Float +Public _X2 As Float +Public _Y2 As Float +Public _radius As Float +Public _Image As Image +Public _Color As Integer[] = [0, &HFFFFFF&] +Public _Pos As Float[] = [0, 1] +Private _ImageDir As String + +Public Function _PaintBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + Dim fradius As Float + Dim Width As Integer = X2 - X1 + Dim Height As Integer = Y2 - Y1 + + Select Case Me._Type + Case 0 + hBrush = Paint.Color(_Color[0]) + Case 1 + hBrush = paint.Image(_Image, X1, Y1) + Case 2 + hBrush = paint.LinearGradient(Width * _X, Height * _Y, Width * _X2, Height * _Y2, _Color, _Pos) + Case 3 + fradius = Width * _radius + hBrush = paint.RadialGradient(Width * _X, Height * _Y, fradius, Width * _X2, Height * _Y2, _Color, _Pos) + End Select + hBrush.Translate(x1, Y1) + Return hBrush + +End + +Static Public Sub _get(sValue As String) As ReportBrush + + Dim hBrush As New ReportBrush + Dim iPos As Integer + Dim sType As String + + Dim ars As String[] + + + + If Not svalue Then Return Null + + sValue = Trim(sValue) + + iPos = InStr(sValue, "(") + + If iPos Then sType = Left(sValue, iPos - 1) + 'pas de parenthese de fin + If ipos And Not (sValue Ends ")") Then + Error.Raise + Else + If iPos Then svalue = Left(Mid(svalue, ipos + 1), -1) + Endif + ars = Split(sValue, ",", "[]") + Select Case LCase(sType) + Case "image" + hBrush._Type = 1 + + hBrush._Image = Image.Load(Replace(ars[0], "\"", "")) + hBrush._ImageDir = ars[0] + Case "radialgradient" + hBrush._Type = 3 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._Radius = CFloat(ars[2]) + hBrush._X2 = CFloat(ars[3]) + hBrush._Y2 = CFloat(ars[4]) + hBrush._Color = GetIntegerArray(ars[5]) + hBrush._Pos = GetFloatArray(ars[6]) + + Case "lineargradient" + + hBrush._Type = 2 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._X2 = CFloat(ars[2]) + hBrush._Y2 = CFloat(ars[3]) + hBrush._Color = GetIntegerArray(ars[4]) + hBrush._Pos = GetFloatArray(ars[5]) + + Case Else + 'correction de l'hexon + '= Val(Replace(ars[0], "#", "&H") & "00&") + If ars[0] Begins "#" Then ars[0] = Mid(ars[0], 2) + hBrush._Color[0] = Val("&H" & IIf(Len(ars[0]) = 8, ars[0], 00 & ars[0])) + End Select + Return hBrush + +Finally + If hBrush._Color.Count = 0 Then hBrush._Color = [0, &hFFFFFF&] + If hBrush._Color.Count < 2 Then hBrush._Color.Add(&hFFFFFF&) + If hBrush._Pos.Count < 2 Then hBrush._Pos = [0.0, 1.0] + + +Catch + Return hBrush + +End + +Static Private Function GetIntegerArray(sValue As String) As Integer[] + + Dim ari As New Integer[] + Dim s As String + + For Each s In Split(sValue) + If s Begins "#" Then s = Mid(s, 2) + + ari.Add(Val("&H" & IIf(Len(s) = 8, s, "00" & s))) + Next + + Return ari + +End + +Static Private Function GetFloatArray(sValue As String) As Float[] + + Dim arf As New Float[] + Dim s As String + + For Each s In Split(sValue) + arf.Add(CFloat(s)) + Next + + Return arf + +End + +Public Function ToString() As String + + Dim i As Integer + Dim f As Float + Dim sValue As String + + Select Case _Type + Case 0 + sValue = "&H" & Hex(_Color[0], 8) & "&" + Case 2, 3 + If _Type = 3 Then + sValue = "RadialGradient(" & _X & "," & _Y & "," & _Radius & "," & _X2 & "," & _Y2 & ",[" + + Else + sValue = "LinearGradient(" & _X & "," & _Y & "," & _X2 & "," & _Y2 & ",[" + Endif + + For Each i In _Color + sValue &= "&H" & Hex(i, 6) & "&," + + Next + + sValue = Left(sValue, -1) + + sValue &= "],[" + + For Each f In _Pos + sValue &= f & "," + + Next + sValue = Left(sValue, -1) + + sValue &= "])" + Case 1 + sValue = "Image(" & _ImageDir & ")" + + End Select + + Return svalue + +End + +Static Public Function Color(iColor As Integer) As ReportBrush + + Dim hBrush As New ReportBrush + + hBrush._Color[0] = iColor + hBrush._Type = 0 + Return hBrush + +End + +Static Public Function LinearGradient(X As Float, Y As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 2 + Return hBrush +End + +Static Public Function RadialGradient(X As Float, Y As Float, Radius As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._radius = Radius + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 3 + Return hBrush +End + +Static Public Function Image(hImage As Image) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._Image = hImage + hBrush._Type = 1 + Return hBrush + +End diff --git a/comp/src/gb.report2/.src/Types/Base/ReportMargin.class b/comp/src/gb.report2/.src/Types/Base/ReportMargin.class new file mode 100644 index 00000000..3574c68a --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Base/ReportMargin.class @@ -0,0 +1,40 @@ +' Gambas class file + +Export +Inherits ReportPadding +Static Public Sub _Get(Value As String) As ReportMargin + + Dim hMargin As New ReportMargin + Dim hSize As TSizeParse + Dim s As String + Dim ars As String[] + + For Each s In Split(Value, ";") + ars = Scan(s, "*:*") + If ars.count > 1 Then + Select Case LCase(ars[0]) + Case "left" + hMargin.Left = ars[1] + Case "top" + hMargin.top = ars[1] + Case "bottom" + hMargin.bottom = ars[1] + Case "right" + hMargin.Right = ars[1] + Case Else + + End Select + Else + Try hSize = New TSizeParse(s) + If hSize <> Null Then + hMargin.Left = s + hMargin.Right = s + hMargin.Bottom = s + hMargin.Top = s + Endif + Endif + + Next + Return hMargin + +End diff --git a/comp/src/gb.report2/.src/Types/Base/ReportPadding.class b/comp/src/gb.report2/.src/Types/Base/ReportPadding.class new file mode 100644 index 00000000..6c4ab28a --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Base/ReportPadding.class @@ -0,0 +1,94 @@ +' Gambas class file + +Export + +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Public {Left} As String +Public Right As String +Public Top As String +Public Bottom As String +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float + +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +Property Read _Width As Float +Property Read _Height As Float + +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> + +'##################### PUBLIC PROCEDURES ######################### +Public Sub IsActive() As Boolean + + If {Left} Or If Top Or If Right Or If Bottom Then Return True + Return False + +End + +Static Public Sub _Get(Value As String) As ReportPadding + + Dim hPadding As New ReportPadding + Dim hSize As TSizeParse + Dim s As String + Dim ars As String[] + + For Each s In Split(Value, ";") + ars = Scan(s, "*:*") + If ars.count > 1 Then + Select Case LCase(ars[0]) + Case "left" + hPadding.Left = ars[1] + Case "top" + hPadding.top = ars[1] + Case "bottom" + hPadding.bottom = ars[1] + Case "right" + hPadding.Right = ars[1] + Case Else + + End Select + Else + Try hSize = New TSizeParse(s) + If hSize <> Null Then + hPadding.Left = s + hPadding.Right = s + hPadding.Bottom = s + hPadding.Top = s + Endif + Endif + + Next + Return hPadding + +End + +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + hSizeParse = TSizeParse[{Left}] + _Left = hSizeParse.ToInch() + hSizeParse = TSizeParse[Top] + _Top = hSizeParse.ToInch() + hSizeParse = TSizeParse[Right] + _Right = hSizeParse.ToInch() + hSizeParse = TSizeParse[Bottom] + _Bottom = hSizeParse.ToInch() + +End + +'##################### PRIVATE PROCEDURES ######################## + +'######################## PROPERTIES ############################# +Private Function _Width_Read() As Float + + Return _Left + _Right + +End + +Private Function _Height_Read() As Float + + Return _top + _Bottom + +End diff --git a/comp/src/gb.report2/.src/Types/Border/ReportBorder.class b/comp/src/gb.report2/.src/Types/Border/ReportBorder.class new file mode 100644 index 00000000..eb22f118 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Border/ReportBorder.class @@ -0,0 +1,205 @@ +' Gambas class file + +Export +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Static Public Const None As Integer = 0 +Static Public Const Solid As Integer = 1 + +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float +Public Style As Integer +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +Property Read Top As _ReportBorderSide +Property Read {Left} As _ReportBorderSide +Property Read bottom As _ReportBorderSide +Property Read {Right} As _ReportBorderSide +Property Read RoundCorner As _ReportRoundCorner +Property Width As String +Property Brush As ReportBrush +Property Read _Height As Float +Property Read _Width As Float + + +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> +' Static Private $aShadowStyle As String[] = ["none", "solid", "gradiant"] +' Static Private $aAlign As String[] = Classes["Align"].Symbols + +Private hTop As New _ReportBorderSide +Private hBottom As New _ReportBorderSide +Private hLeft As New _ReportBorderSide +Private hRight As New _ReportBorderSide +Private hRCorner As New _ReportRoundCorner + + +'##################### PUBLIC PROCEDURES ######################### + +Static Public Function _get(sValue As String) As ReportBorder + + Dim hReportBorder As New ReportBorder + + Dim sBorder As String + Dim aScan As String[] + + If Not sValue Then Return hReportBorder + hReportBorder.Style = ReportBorder.Solid + hReportBorder.RoundCorner._Active = False + For Each sBorder In Split(sValue, ";") + + aScan = Scan(sBorder, "*:*") + Select Case LCase(Trim(aScan[0])) + Case "border" + FillObject(hReportBorder, aScan[1]) + Case "left" + FillObject(hReportBorder.Left, aScan[1]) + Case "right" + FillObject(hReportBorder.Right, aScan[1]) + Case "bottom" + FillObject(hReportBorder.bottom, aScan[1]) + Case "top" + FillObject(hReportBorder.Top, aScan[1]) + Case "topleftcorner" + hReportBorder.RoundCorner.TopLeft = aScan[1] + Case "toprightcorner" + hReportBorder.RoundCorner.TopRight = aScan[1] + Case "bottomleftcorner" + hReportBorder.RoundCorner.BottomLeft = aScan[1] + Case "bottomrightcorner" + hReportBorder.RoundCorner.BottomRight = aScan[1] + + End Select + + Next + +Finally + Return hReportBorder + +End + +Public Function ToString() As String + + Dim aValue As New String[] + + If Me.Style = ReportBorder.None Then Return 'aValue.Add("None") + 'If Me.Style = ReportBorder.Solid Then aValue.Add("Solid") + aValue.Add(Me.Width) + If Me.Top Then aValue.Add("Top") + If Me.Bottom Then aValue.Add("Bottom") + If Me.Left Then aValue.Add("Left") + If Me.Right Then aValue.Add("Right") + aValue.Add("#" & Hex(Me.Color, 6)) + Return aValue.Join() + +End + +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + hSizeParse = New TSizeParse(hLeft.Width) + _Left = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hRight.Width) + _Right = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hTop.Width) + _Top = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hBottom.Width) + _Bottom = hSizeParse.GetValue() + + hRCorner._NormalizeUnits() + +End + +'##################### PRIVATE PROCEDURES ######################## + +Static Private Sub FillObject(hObj As Object, sValue As String) + + Dim sBrush As String + + For Each sBrush In Split(sValue, " ") + + If IsDigit(Left(sBrush)) Then + Try hObj.Width = sBrush + Else + Try hObj.Brush = ReportBrush[sBrush] + If hObj.Brush = Null Then hObj.Brush = ReportBrush["&H0"] + Endif + Next + +End + +'######################## PROPERTIES ############################# + +Private Function Top_Read() As _ReportBorderSide + + Return hTop + +End + +Private Function Left_Read() As _ReportBorderSide + + Return hLeft + +End + +Private Function bottom_Read() As _ReportBorderSide + + Return hBottom + +End + +Private Function Width_Read() As String + + Return hLeft.Width + +End + +Private Sub Width_Write(Value As String) + + hLeft.Width = Value + hTop.Width = Value + hBottom.Width = Value + hRight.Width = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return hLeft.Brush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + hLeft.Brush = Value + hTop.Brush = Value + hBottom.Brush = Value + hRight.Brush = Value + +End + +Private Function Right_Read() As _ReportBorderSide + + Return hRight + +End + +Private Function RoundCorner_Read() As _ReportRoundCorner + + Return hRCorner + +End + + + +Private Function _Height_Read() As Float + + Return _Top + _Bottom + +End + +Private Function _Width_Read() As Float + + Return _Left + _Right + +End diff --git a/comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class b/comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class new file mode 100644 index 00000000..346443be --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class @@ -0,0 +1,16 @@ +' Gambas class file + +Export +Public Width As String +Public Brush As New ReportBrush +Public Round1 As String +Public Round2 As String +Public _Width As Float + +Public Sub _NormalizeUnits() + Dim hSizeParse As New TSizeParse(Width) + + _Width = hSizeParse.GetValue() + + +End diff --git a/comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class b/comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class new file mode 100644 index 00000000..e3eb4a5b --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class @@ -0,0 +1,137 @@ +' Gambas class file + +Export +Public _Active As Boolean = False +Private $sTopLeft As String = "0cm" +Private $sTopRight As String = "0cm" +Private $sBottomLeft As String = "0cm" +Private $sBottomRight As String = "0cm" + +Property TopLeft As String +Property TopRight As String +Property BottomLeft As String +Property BottomRight As String + +Public _TopLeft1 As Float +Public _TopLeft2 As Float +Public _TopRight1 As Float +Public _TopRight2 As Float +Public _BottomLeft1 As Float +Public _BottomLeft2 As Float +Public _BottomRight1 As Float +Public _BottomRight2 As Float + +Private Function TopLeft_Read() As String + + Return $sTopLeft + +End + +Private Sub TopLeft_Write(Value As String) + + $sTopLeft = Value + _Active = True + +End + +Private Function TopRight_Read() As String + + Return $sTopRight + +End + +Private Sub TopRight_Write(Value As String) + + $sTopRight = Value + _Active = True + +End + +Private Function BottomLeft_Read() As String + + Return $sBottomLeft + +End + +Private Sub BottomLeft_Write(Value As String) + + $sBottomLeft = Value + _Active = True + +End + +Private Function BottomRight_Read() As String + + Return $sBottomRight + +End + +Private Sub BottomRight_Write(Value As String) + + $sBottomRight = Value + _Active = True + +End + +Public Sub _NormalizeUnits() + + Dim aEl As String[] + + Dim hSizeParse As TSizeParse + + + ael = Split($sTopLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + _TopLeft2 = _TopLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopLeft2 = hSizeParse.GetValue() + Endif + Endif + + ael = Split($sTopRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + _TopRight2 = _TopRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + _BottomRight2 = _BottomRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + _BottomLeft2 = _BottomLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomLeft2 = hSizeParse.GetValue() + Endif + Endif + +End diff --git a/comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class b/comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class new file mode 100644 index 00000000..95957db3 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class @@ -0,0 +1,225 @@ +' Gambas class file + +Export +Private $aBoxShadow As New _ReportBoxShadow[] + +Property XOffset As String +Property YOffset As String +Property Blur As String +Property Spread As String +Property Inset As Boolean +Property Color As Integer +Property Read Count As Integer +Property Read Max As Integer + + +Property Read _XOffset As Float +Property Read _YOffset As Float +Property Read _Blur As Float +Property Read _Spread As Float +Property Read _Active As Boolean + + +Public Sub _new() + + Dim hBoxShadow As New _ReportBoxShadow + $aBoxShadow.Add(hBoxShadow) + +End + +Static Public Function _get(sValue As String) As ReportBoxShadow + + Dim hBoxShadow As New ReportBoxShadow + Dim s As String + Dim i As Integer + For Each s In Split(sValue, " ") + If IsDigit(Left(s)) Or If Left(s) = "-" Then + + Select Case i + Case 0 + hBoxShadow.XOffset = s + Case 1 + hBoxShadow.YOffset = s + Case 2 + hBoxShadow.Blur = s + Case 3 + hBoxShadow.Spread = s + End Select + Inc i + Continue + Endif + If LCase(s) = "inset" Then + hBoxShadow.Inset = True + Continue + Endif + Try hBoxShadow.Color = Val(Replace(s, "#", "&H")) + + Next + Return hBoxShadow + +End + + + +' Public Function _get(Index As Integer) As _ReportBoxShadow +' +' Return $aBoxShadow[Index] +' +' End + + + +Public Sub Add(Optional XOffset As String, YOffset As String, iColor As Integer, Spread As String, Blur As String, Inset As Boolean) + + Dim hBoxShadow As New _ReportBoxShadow + + If XOffset Then hBoxShadow.XOffset = XOffset + If YOffset Then hBoxShadow.YOffset = YOffset + If iColor Then hBoxShadow.Color = iColor + If SPread Then hBoxShadow.Spread = Spread + If Blur Then hBoxShadow.Blur = Blur + hBoxShadow.Inset = Inset + $aBoxShadow.Add(hBoxShadow) + +End + +Public Sub Remove(Index As Integer) + + $aBoxShadow.Remove(Index) + +End + + + +Private Function XOffset_Read() As String + + Return $aBoxShadow[0].XOffset + +End + +Private Sub XOffset_Write(Value As String) + + $aBoxShadow[0].XOffset = Value + +End + +Private Function YOffset_Read() As String + + Return $aBoxShadow[0].YOffset + +End + +Private Sub YOffset_Write(Value As String) + + $aBoxShadow[0].YOffset = Value + +End + +Private Function Blur_Read() As String + + Return $aBoxShadow[0].Blur + +End + +Private Sub Blur_Write(Value As String) + + $aBoxShadow[0].Blur = Value + +End + +Private Function Spread_Read() As String + + Return $aBoxShadow[0].Spread + +End + +Private Sub Spread_Write(Value As String) + + $aBoxShadow[0].Spread = Value + +End + +Private Function Inset_Read() As Boolean + + Return $aBoxShadow[0].Inset + +End + +Private Sub Inset_Write(Value As Boolean) + + $aBoxShadow[0].Inset = Value + +End + +Private Function Color_Read() As Integer + + Return $aBoxShadow[0].Color + +End + +Private Sub Color_Write(Value As Integer) + + $aBoxShadow[0].Color = Value + +End + +Private Function Count_Read() As Integer + + Return $aBoxShadow.Count + +End + +Private Function Max_Read() As Integer + + Return $aBoxShadow.Max + +End + +Public Sub _NormalizeUnits() + + Dim i As Integer + 'Dim SizeParse As TSizeParse + For i = 0 To $aBoxShadow.Max + 'With + $aBoxShadow[i]._XOffset = TSizeParse[$aBoxShadow[i].XOffset].GetValue() + $aBoxShadow[i]._YOffset = TSizeParse[$aBoxShadow[i].YOffset].GetValue() + $aBoxShadow[i]._Spread = TSizeParse[$aBoxShadow[i].Spread].GetValue() + $aBoxShadow[i]._Blur = TSizeParse[$aBoxShadow[i].Blur].GetValue() + + '._Blur = TSizeParse[.Blur].GetValue() + 'End With + + + Next + +End + +Private Function _XOffset_Read() As Float + + Return $aBoxShadow[0]._XOffset + +End + +Private Function _YOffset_Read() As Float + + Return $aBoxShadow[0]._YOffset + +End + +Private Function _Blur_Read() As Float + + Return $aBoxShadow[0]._Blur + +End + +Private Function _Spread_Read() As Float + + Return $aBoxShadow[0]._Spread + +End + +Private Function _Active_Read() As Boolean + + Return Not ($aBoxShadow[0]._XOffset = 0 And $aBoxShadow[0]._YOffset = 0 And $aBoxShadow[0]._Blur = 0 And $aBoxShadow[0]._Spread = 0) + +End diff --git a/comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class b/comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class new file mode 100644 index 00000000..46f8af6c --- /dev/null +++ b/comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class @@ -0,0 +1,14 @@ +' Gambas class file + +Export +Public XOffset As String '= "8mm" +Public YOffset As String '= "2mm" +Public Spread As String +Public Inset As Boolean +Public Color As Integer +Public Blur As String + +Public _XOffset As Float +Public _YOffset As Float +Public _Spread As Float +Public _Blur As Float diff --git a/comp/src/gb.report2/.src/Types/ReportSizeHints.class b/comp/src/gb.report2/.src/Types/ReportSizeHints.class new file mode 100644 index 00000000..20421e8f --- /dev/null +++ b/comp/src/gb.report2/.src/Types/ReportSizeHints.class @@ -0,0 +1,26 @@ +' Gambas class file + +Export +Public Width As Float +Public Height As Float +Public NotFinished As Boolean + +Static Public Function _call(Width As Float, Height As Float, Optional NotFinished As Float) As ReportSizeHints + Dim hSH As New ReportSizeHints + hSH.Width = Width + hSH.Height = Height + hSH.NotFinished = NotFinished + Return hSH +End + +Static Public Function FromString(Width As String, Height As String, Optional NotFinished As Boolean) As ReportSizeHints + + Dim hSH As New ReportSizeHints + hSH.Width = TSizeParse[Width].ToInch() + hSH.Height = TSizeParse[Height].ToInch() + hSH.NotFinished = NotFinished + Return hSH + +End + + diff --git a/comp/src/gb.report2/.src/Types/ReportSizeParser.class b/comp/src/gb.report2/.src/Types/ReportSizeParser.class new file mode 100644 index 00000000..8ca8b425 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/ReportSizeParser.class @@ -0,0 +1,83 @@ +' Gambas class file + +' Gambas class file + +Export +Fast +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Public Value As Float +Public Unit As String + +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> + +'##################### PUBLIC PROCEDURES ######################### +'' Create a new size object from its description. +'' #bAllowRelative# can be TRUE to allow the use of "%" as unit. +Public Sub _new(Size As String, Optional bAllowRelative As Boolean) + + Dim I As Integer + Dim sCar As String + + If Not size Then + Unit = "cm" + Value = 0 + Endif + + Size = Trim(Size) + + If Not Size Then + Unit = "px" + Return + Endif + + For I = 1 To Len(Size) + sCar = Mid$(Size, I, 1) + If Not IsDigit(sCar) And If sCar <> "-" And If sCar <> "." Then Break + Next + + Value = CFloat(Left$(Size, I - 1)) + Unit = Trim(Mid$(Size, I)) + + If Not Unit Then Unit = "px" + + If Not Report.AllowedUnits.Exist(Unit) Then + If Not bAllowRelative Or Unit <> "%" Then + Error.Raise("Unknown unit") + Endif + Endif + +End + +Public Sub ToInch() As Float + + Return Report.UnitToInch(Value, Unit) + +End + +Public Sub IsRelative() As Boolean + + Return Unit = "%" + +End + +Public Sub GetValue() As Float + + If IsRelative() Then + Return Value + Else + Return ToInch() + Endif + +End + +Static Public Sub _get(Size As String) As TSizeParse + + Dim hSize As TSizeParse = New TSizeParse(Size) + + Return hSize + +End + +'##################### PRIVATE PROCEDURES ######################## +'######################## PROPERTIES ############################# diff --git a/comp/src/gb.report2/.src/Types/TControl.class b/comp/src/gb.report2/.src/Types/TControl.class new file mode 100644 index 00000000..071dad65 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/TControl.class @@ -0,0 +1,3 @@ +' Gambas class file + +Inherits _ReportVirtualControl diff --git a/comp/src/gb.report2/.src/Types/TSizeParse.class b/comp/src/gb.report2/.src/Types/TSizeParse.class new file mode 100644 index 00000000..c6ca4e2d --- /dev/null +++ b/comp/src/gb.report2/.src/Types/TSizeParse.class @@ -0,0 +1,5 @@ +' Gambas class file + +Export + +Inherits ReportSizeParser diff --git a/comp/src/gb.report2/.src/Types/_ReportVirtualControl.class b/comp/src/gb.report2/.src/Types/_ReportVirtualControl.class new file mode 100644 index 00000000..ca8fa033 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/_ReportVirtualControl.class @@ -0,0 +1,78 @@ +' Gambas class file + +Export +'Fast +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Public SizeHint As ReportSizeHints ''Size hints buffer +Public _PageChildren As New Collection ''The links between page and objects +Public Index As Integer ''Remember the dataindex +Public Cache As Variant ''For future data cache + +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +Property Read RealLeft As Float ''Return the left relative position size in Pixel +Property Read RealTop As Float ''Return the top relative position size in Pixel +Property Read RealWidth As Float ''Return the width in pixel +Property Read RealHeight As Float ''Return the height in pixel +Property Ctrl As ReportControl ''Return or set the link between real widget and virtual object + +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> +Private $iCtrl As Integer +Private fX As Float +Private fY As Float +Private fW As Float +Private fH As Float + + +'##################### PUBLIC PROCEDURES ######################### +''Define the virtual widget size in internal unit (cm) +Public Function _SetGeometry(X As Float, Y As Float, W As Float, H As Float) As Float + + fX = X + fY = Y + fW = W + fH = H + +End + +'##################### PRIVATE PROCEDURES ######################## + + + +'######################## PROPERTIES ############################# + +Private Function Ctrl_Read() As ReportControl + + Return ReportControl._ObjectFromId[$iCtrl] + +End + + +Private Sub Ctrl_Write(Value As ReportControl) + + $iCtrl = Value.id + +End + +Private Function RealLeft_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX) 'ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX) + +End + +Private Function RealTop_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fY) + +End + +Private Function RealWidth_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX + fW) - ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX) + +End + +Private Function RealHeight_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fY + fH) - ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fY) + +End diff --git a/comp/src/gb.report2/.src/Viewer/FPreview.class b/comp/src/gb.report2/.src/Viewer/FPreview.class new file mode 100644 index 00000000..ac7f63a8 --- /dev/null +++ b/comp/src/gb.report2/.src/Viewer/FPreview.class @@ -0,0 +1,550 @@ +' Gambas class file + +Private $aModeButtons As New ToolButton[] +Private bFlag As Boolean +Private $hPrinter As New Printer As "Printer" +'Private $iCurPrinterResolution As Integer +Private $bVerif As Boolean +Private $bPrinting As Boolean +Private $iFileRes As Integer = 150 +Static Property Path As String Use $sPath + + +Static Public Function Run(hReport As Report) + + Dim hFPreview As FPreview + + hFpreview = New FPreview + + hFPreview.SetReport(hReport) + hFPreview.ShowModal() + +End + +Public Sub _new() + + Dim s As String + Dim hMenu As Menu + + For Each s In Printer.List + cmbPrinter.Add(s) + hMenu = New Menu(mnuPrint) As "mnuPrintPrinter" + hMenu.Tag = s + hMenu.Text = s + Next + +End + +Private Sub SelectPrinter(Optional sName As String) + + Dim iPrinter As Integer + + If sName Then + $hPrinter.Name = sName + Else + $hPrinter.Name = Printer.Default + Endif + + Object.Lock(cmbPrinter) + iPrinter = Max(0, cmbPrinter.List.Find($hPrinter.Name)) + Try cmbPrinter.Index = iPrinter + Object.Unlock(cmbPrinter) + + 'NOTE: force the printer fullpage mode because it make error on report placement + $hPrinter.FullPage = True + $hPrinter.Duplex = cmbDuplex.Index + + tgbGrayScale.Value = $hPrinter.GrayScale + tgbFullPage.Value = $hPrinter.FullPage + tgbReverseCopies.Value = $hPrinter.ReverseOrder + tgbCollateCopies.Value = $hPrinter.CollateCopies + +End + +Public Sub SetReport(hReport As Report) + + View.Report = hReport + GetValuesFromReport() + +End + +Private Sub GetValuesFromReport() + + cmbPaper.Index = View.Report.Paper + + CPrint.InitPrinterFromReport($hPrinter, View.Report) + + If View.Report.Paper = Printer.Custom Then + pnlCustom.Enabled = True + Endif + + cmbOrientation.Index = $hPrinter.Orientation + cmbDuplex.Index = $hPrinter.Duplex + cmbPaper.Index = $hPrinter.Paper + View.Refresh + + Object.Lock(spWidth) + spWidth.Value = $hPrinter.PaperWidth + Object.Unlock(spWidth) + Object.Lock(spHeight) + spHeight.Value = $hPrinter.PaperHeight + Object.Unlock(spHeight) + + +End + +Public Sub sldZoom_Change() + + Dim hButton As ToolButton + + View.ScaleMode = View.Custom + View.Zoom = sldZoom.Value / 100 + 'lblZoom.Text = Str(Slider1.Value) & " %" + bFlag = True + For Each hButton In $aModeButtons + hButton.Value = False + Next + + bFlag = False + +End + +Public Sub View_Zoom() + + Object.Lock(sldZoom) + sldZoom.Value = View.Zoom * 100 + Object.Unlock(sldZoom) + + lblZoom.Text = Str(sldZoom.Value) & " %" + +End + +Public Sub Mode_Click() + + Dim hButton As ToolButton + Dim hCurButton As ToolButton + + If bFlag Then Return + bFlag = True + hCurButton = Last + + For Each hButton In $aModeButtons + If hButton = hCurButton Then + hButton.Value = True + Else + hButton.Value = False + Endif + Next + + bFlag = False + + 'View.ScaleMode = hCurButton.Tag + View.ScaleMode = hCurButton.Tag + 'View.Refresh + 'View.Refresh + +End + +Public Sub tgbGrayScale_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.GrayScale = Last.Value + View._GrayScale = Last.Value + View.Refresh() + +End + +Public Sub tgbFullPage_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.FullPage = Last.Value + +End + +Public Sub tgbReverseCopies_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.ReverseOrder = Last.value + +End + +Public Sub tgbCollateCopies_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.CollateCopies = Last.Value + +End + +Public Sub cmbOrientation_Click() + + If View.Report.Orientation = Last.Index Then Return + View.Report.Orientation = Last.Index + 'View.Report.Refresh + View.Refresh + GetValuesFromReport + +End + +Public Sub cmbPaper_Click() + + Dim i As Integer = Last.Index + 'Print "id "; View.Report.paper + If View.Report.Paper = i Then Return + View.Report.Paper = i + If i = 0 Then + pnlCustom.Visible = True + Else + pnlCustom.Visible = False + View.Refresh + GetValuesFromReport + Endif + +End + +Public Sub cmbDuplex_Click() + + $hPrinter.Duplex = Last.index + +End + +Public Sub txtFile_Click() + + Dialog.Path = txtFile.Text + Dialog.Filter = ["*.pdf", ("PDF files"), "*.ps", ("Postscript files")] + + If Dialog.SaveFile() Then Return + + txtFile.Text = Dialog.Path + $bVerif = True + $hPrinter.OutputFile = Dialog.Path + +End + +Private Sub RefreshPrintButton() + + If $bPrinting Then + + btnPrint.Picture = Picture["icon:/small/cancel"] + btnPrint.Text = ("Cancel") + + Else + + btnPrint.Picture = Picture[If(tabPrint.Index = 0, "icon:/small/printer", "icon:/small/pdf")] + btnPrint.Text = ("Print") + + Endif + + btnPrint2.Picture = btnPrint.Picture + btnPrint2.Text = btnPrint.Text + +End + +Public Sub tabPrint_Click() + + If tabPrint.Index = 0 Then + $hPrinter.OutputFile = "" + $hPrinter.Name = cmbPrinter.Text + $hPrinter.Resolution = 1200 + Else + $hPrinter.OutputFile = txtFile.Text + $hPrinter.Resolution = $iFileRes + Object.Lock(cmbResolution) + pnlResolution.Enabled = True + Try cmbResolution.Index = Max(cmbResolution.Find(Str($iFileRes)), 0) + Object.Unlock(cmbResolution) + Endif + + RefreshPrintButton + +End + +Public Sub TextBox1_Change() + + View.Range = TextBox1.Text + +End + +Public Sub TextBox1_Click() + + Last.Text = "" + 'TextBox1_Change + +End + +Public Sub spWidth_Change() + + View.Report.Width = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub spHeight_Change() + + View.Report.Height = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub Button2_Click() + + View._ForceLayout + +End + +Public Sub cmbPrinter_Click() + + SelectPrinter(Last.Text) + +End + +Public Sub btnPrint_Click() + + Dim iReturn As Integer + + If btnPrint.Tag Then + + $hPrinter.Cancel + btnPrint.Tag = False + + Else + If Not View.Report Then Return + + If tabPrint.Index = 1 Then + + If Not $hPrinter.OutputFile Then txtFile_Click + If Not $hPrinter.OutputFile Then Return + + If Exist($hPrinter.OutputFile) And Not $bVerif Then + iReturn = Message.Warning("" & $hPrinter.OutputFile & "\n\n" & ("This file already exists.\nDo you want to replace it?"), ("Replace"), ("Cancel")) + If iReturn = 2 Then Return + Try Kill $hPrinter.OutputFile + $bVerif = False + Endif + + Endif + + $bPrinting = True + btnPrint.Tag = True + RefreshPrintButton + + $hPrinter.Print + + Endif + +End + +Public Sub Printer_Begin() + + hbZoom.Hide + hbPrinting.Show + Inc Application.Busy + ProgressBar1.Value = 0 + ProgressBar1.Pulse = True + lblPrint.Text = ("Layout...") + + Paint.FontScale = 25.4 / 72 * Paint.W / $hPrinter.PaperWidth * 72 / 96 * 96 / $hPrinter.Resolution + +End + +Public Sub Printer_Paginate() + + If View.Report._LayoutNotFinished Then + View.LockLayout + View.Report.Layout(View.Report.PageCount) + Else + $hPrinter.Count = View.PageCount + ProgressBar1.Pulse = False + lblPrint.Text = ("Printing...") + View.UnlockLayout + Endif + +End + +Public Sub Printer_Draw() + + 'Debug Paint.W;; $hPrinter.PaperWidth;; $hPrinter.Resolution;; Paint.FontScale + + If View.RangePages.Count = 0 Then + View.Report.Paint($hPrinter.Page) + Else + View.Report.Paint(View.RangePages[$hPrinter.Page - 1] + 1) + Endif + ProgressBar1.Value = $hPrinter.Page / View.PageCount + +End + +Public Sub Printer_End() + + 'View.UnlockLayout + hbPrinting.Hide + hbZoom.Show + Dec Application.Busy + btnPrint.Tag = False + $bPrinting = False + RefreshPrintButton + +End + +Public Sub TextBox2_Change() + + $hPrinter.NumCopies = Last.Value + +End + +Public Sub Form_Close() + + If Component.IsLoaded("gb.settings") Then + 'Settings.Write(Me, "FPreview") + Settings.Write(View, "View") + Settings["FPreview/OutputFile"] = txtFile.Text + Settings["FPreview/Printer"] = $hPrinter.Name + Settings["FPreview/PrintToFile"] = tabPrint.Index = 1 + Settings["FPreview/Duplex"] = cmbDuplex.Index + Settings["FPreview/GrayScale"] = tgbGrayScale.Value + Settings["FPreview/ReverseOrder"] = tgbReverseCopies.Value + Settings["FPreview/CollateCopies"] = tgbCollateCopies.Value + Settings["FPreview/FileResolution"] = $iFileRes + Settings.Save() + Endif + + FPreview.Path = IIf(tabPrint.Index = 1, txtFile.Text, "") + + +End + +Public Sub Form_Open() + + Dim hButton As ToolButton + + $aModeButtons = [btnOnePage, btnTwoPages, btnFullWidth, btnRealSize] + + If Component.IsLoaded("gb.settings") Then + 'Settings.Read(Me, "FPreview") + Settings.Read(View, "View") + + If View.ScaleMode = View.Custom Then + sldZoom.Value = View.Zoom + Else + For Each hButton In $aModeButtons + hButton.Value = False + Next + $aModeButtons[View.ScaleMode - 1].Value = True + + Endif + + txtFile.Text = Settings["FPreview/OutputFile", User.Home &/ "report.pdf"] + $hPrinter.OutputFile = txtFile.Text + $hPrinter.Name = Settings["FPreview/Printer"] + If Settings["FPreview/PrintToFile"] = True Then + tabPrint.Index = 1 + Endif + + Try $hPrinter.Duplex = Settings["FPreview/Duplex"] + Try $hPrinter.GrayScale = Settings["FPreview/GrayScale"] + Try $hPrinter.ReverseOrder = Settings["FPreview/ReverseOrder"] + Try $hPrinter.CollateCopies = Settings["FPreview/CollateCopies"] + Try $iFileRes = Settings["FPreview/FileResolution", 150] + Try $hPrinter.Duplex = Settings["FPreview/Duplex", Printer.Simplex] = cmbDuplex.Index + Endif + + SelectPrinter($hPrinter.Name) + btnPrint.Tag = False + $bPrinting = False + + btnOnePage.Value = True + tabPrint_Click + + If FPreview.Path And If Not IsDir(FPreview.Path) Then + tabPrint.Index = 1 + txtFile.Text = FPreview.Path + Endif + + +End + +Public Sub btnZoomIn_Click() + + sldZoom.Value = 100 * 2 ^ (Int((Log2(sldZoom.Value / 100) + 0.5) * 2 + 0.5) / 2) + 0.5 + +End + +Public Sub btnZoomOut_Click() + + sldZoom.Value = 100 * 2 ^ (Int((Log2(sldZoom.Value / 100)) * 2 - 0.5) / 2) + 0.5 + +End + +Public Sub panSide_Arrange() + + With btnCloseSide + .Move(.Parent.W - .W, 0, Desktop.Scale * 4, Desktop.Scale * 4) + End With + +End + +Public Sub btnCloseSide_Click() + + panSide.Hide + btnShowSide.Show + btnPrint2.Show + +End + +Public Sub btnShowSide_Click() + + panSide.Show + btnShowSide.Hide + btnPrint2.Hide + +End + +Public Sub mnuPrint_Show() + + Dim hMenu As Menu + + mnuPrintPDF.Checked = tabPrint.Index = 1 + For Each hMenu In mnuPrint.Children + If Not hMenu.Tag Then Continue + If tabPrint.Index = 1 Then + hMenu.Checked = False + Else + hMenu.Checked = hMenu.Text = cmbPrinter.Text + Endif + Next + +End + +Public Sub mnuPrintPDF_Click() + + tabPrint.Index = 1 + txtFile_Click + +End + +Public Sub mnuPrintPrinter_Click() + + tabPrint.Index = 0 + cmbPrinter.Text = Last.Text + +End + +Public Sub spResolution_Change() + +End + +Public Sub SpinBar1_Change() + + Last.Text = "toto" & Last.Value + +End + +Public Sub cmbResolution_Click() + + If tabPrint.Index = 0 Then + $hPrinter.Resolution = 1200 + Else + $iFileRes = cmbResolution.Text + $hPrinter.Resolution = $iFileRes + Endif + +End diff --git a/comp/src/gb.report2/.src/Viewer/FPreview.form b/comp/src/gb.report2/.src/Viewer/FPreview.form new file mode 100644 index 00000000..b96c8bc4 --- /dev/null +++ b/comp/src/gb.report2/.src/Viewer/FPreview.form @@ -0,0 +1,363 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,107,78) + Text = ("Report preview") + Arrangement = Arrange.Horizontal + { mnuPrint Menu + Visible = False + { mnuPrintPDF Menu + Text = ("Print to file") & "..." + } + { Menu1 Menu + } + } + { Panel1 VBox + MoveScaled(1,1,63,72) + Expand = True + Invert = True + { View ReportView + MoveScaled(1,1,50,48) + Background = Color.LightForeground + Tag = "3" + Mouse = Mouse.SizeAll + Expand = True + } + { hbPrinting HBox + MoveScaled(1,57,55,4) + Visible = False + Spacing = True + Indent = True + { lblPrint Label + MoveScaled(11,0,14,4) + AutoResize = True + Text = ("Printing") & "..." + } + { Panel11 Panel + MoveScaled(26,0,25,4) + Expand = True + Arrangement = Arrange.Fill + Margin = True + Padding = 4 + { ProgressBar1 ProgressBar + MoveScaled(0,0,37,3) + Expand = True + Border = False + } + } + } + { hbZoom HBox + MoveScaled(1,62,62,4) + { Panel2 Panel + MoveScaled(0,0,17,4) + Expand = True + Arrangement = Arrange.Horizontal + { btnOnePage ToolButton Mode + Name = "btnOnePage" + MoveScaled(0,0,4,4) + Tag = "1" + ToolTip = ("One page") + Picture = Picture["icon:/small/page"] + Toggle = True + } + { btnTwoPages ToolButton Mode + Name = "btnTwoPages" + MoveScaled(4,0,4,4) + Tag = "2" + ToolTip = ("Two pages") + Picture = Picture["icon:/small/page-two"] + Toggle = True + } + { btnFullWidth ToolButton Mode + Name = "btnFullWidth" + MoveScaled(8,0,4,4) + Tag = "3" + ToolTip = ("Full width") + Picture = Picture["icon:/small/zoom-width"] + Toggle = True + } + { btnRealSize ToolButton Mode + Name = "btnRealSize" + MoveScaled(12,0,4,4) + Tag = "4" + ToolTip = ("Real size") + Picture = Picture["icon:/small/zoom-normal"] + Toggle = True + } + } + { btnZoomOut ToolButton + MoveScaled(18,0,4,4) + ToolTip = ("Zoom out") + Picture = Picture["icon:/small/zoom-out"] + } + { sldZoom Slider + MoveScaled(22,0,19,4) + MinValue = 13 + MaxValue = 400 + Value = 100 + } + { lblZoom Label + MoveScaled(41,0,8,4) + Alignment = Align.Center + Text = ("100 %") + } + { btnZoomIn ToolButton + MoveScaled(48,0,4,4) + ToolTip = ("Zoom in") + Picture = Picture["icon:/small/zoom-in"] + } + { btnPrint2 MenuButton btnPrint + Name = "btnPrint2" + MoveScaled(51,0,8,4) + Visible = False + AutoResize = True + Text = ("Print") + Border = False + Menu = "mnuPrint" + } + { btnShowSide ToolButton + MoveScaled(58,0,4,4) + Visible = False + ToolTip = ("Show options") + Picture = Picture["icon:/small/view-split-h"] + } + } + } + { panSide Panel + MoveScaled(65,1,41,76) + Arrangement = Arrange.Horizontal + Border = Border.Plain + { Panel4 VBox + MoveScaled(1,1,39,73) + Expand = True + { tabPrint TabPanel + MoveScaled(0,0,37,15) + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + Border = False + Count = 2 + Index = 0 + Text = ("Printer") + { pnlResolution HBox + MoveScaled(1,1,37,4) + Spacing = True + { PictureBox1 PictureBox + MoveScaled(0,0,4,4) + Picture = Picture["icon:/32/printer"] + Stretch = True + } + { cmbPrinter ComboBox + MoveScaled(5,0,31,4) + Expand = True + ReadOnly = True + } + } + { Panel7 HBox + MoveScaled(1,6,35,4) + Spacing = True + { Label2 Label + MoveScaled(0,0,12,4) + Text = ("Two-sided") + } + { cmbDuplex ComboBox + MoveScaled(15,0,21,4) + Expand = True + ReadOnly = True + List = [("None"), ("Short side"), ("Long side")] + } + } + Index = 1 + Text = ("File") + { Panel12 HBox + MoveScaled(1,1,35,4) + Spacing = True + { PictureBox2 PictureBox + MoveScaled(0,0,4,4) + Picture = Picture["icon:/48/pdf"] + Stretch = True + } + { txtFile ButtonBox + MoveScaled(5,0,29,4) + Expand = True + Picture = Picture["icon:/16/open"] + ReadOnly = True + } + } + { HBox3 HBox + MoveScaled(1,6,35,4) + Spacing = True + { TextLabel1 TextLabel + MoveScaled(1,1,12,3) + Text = ("Resolution") + Alignment = Align.Normal + } + { cmbResolution ComboBox + MoveScaled(15,0,14,4) + #Translate = False + ReadOnly = True + List = ["75", "150", "300", "600", "1200"] + } + { Label11 Label + MoveScaled(30,0,4,4) + Text = ("dpi") + } + } + Index = 0 + } + { Separator2 Separator + MoveScaled(3,15,32,0) + Visible = False + } + { VBox1 VBox + MoveScaled(3,16,38,59) + Expand = True + Spacing = True + Margin = True + { Panel6 HBox + MoveScaled(2,0,34,4) + Spacing = True + { Label1 Label + MoveScaled(0,0,12,4) + Text = ("Range") + } + { TextBox1 ButtonBox + MoveScaled(15,0,18,4) + Expand = True + Picture = Picture["icon:/16/clear"] + } + } + { HBox1 HBox + MoveScaled(2,5,34,4) + Spacing = True + { Label4 Label + MoveScaled(0,0,12,4) + Text = ("Copies") + } + { TextBox2 SpinBox + MoveScaled(15,0,12,4) + Expand = True + MinValue = 1 + Value = 1 + } + } + { Panel5 Panel + MoveScaled(2,10,33,5) + } + { Panel14 HBox + MoveScaled(1,17,36,4) + Spacing = True + { Label10 Label + MoveScaled(0,0,12,4) + Text = ("Orientation") + } + { cmbOrientation ComboBox + MoveScaled(14,0,21,4) + Expand = True + ReadOnly = True + List = [("Portrait"), ("Landscape")] + } + } + { Panel8 HBox + MoveScaled(1,21,36,4) + Spacing = True + { Label3 Label + MoveScaled(0,0,12,4) + Text = ("Paper") + } + { cmbPaper ComboBox + MoveScaled(13,0,21,4) + Expand = True + ReadOnly = True + List = [("Custom"), ("A3"), ("A4"), ("A5"), ("B5"), ("Letter"), ("Executive"), ("Legal")] + } + } + { pnlCustom VBox + MoveScaled(0,25,36,9) + Visible = False + Spacing = True + { Panel3 HBox + MoveScaled(1,0,32,4) + Spacing = True + { Label5 Label + MoveScaled(0,0,12,4) + Text = ("Width") + } + { spWidth SpinBox + MoveScaled(14,0,6,4) + Expand = True + MaxValue = 1000 + } + { Label7 Label + MoveScaled(27,0,4,4) + Text = ("mm") + } + } + { Panel17 HBox + MoveScaled(1,4.125,31,4) + Spacing = True + { Label6 Label + MoveScaled(0,0,12,4) + Text = ("Height") + } + { spHeight SpinBox + MoveScaled(13,0,6,4) + Expand = True + MaxValue = 1000 + } + { Label8 Label + MoveScaled(26,0,4,4) + Text = ("mm") + } + } + } + { Panel13 Panel + MoveScaled(2,34,33,4) + Expand = True + } + { tgbGrayScale CheckBox + MoveScaled(1,36,34,4) + Text = ("Grayscale") + } + { tgbFullPage CheckBox + MoveScaled(1,40,24,4) + Visible = False + Text = ("Full page") + } + { tgbCollateCopies CheckBox + MoveScaled(1,45,35,3) + Text = ("Collate copies") + } + { tgbReverseCopies CheckBox + MoveScaled(1,48,36,4) + Text = ("Reverse order") + } + { Panel15 Panel + MoveScaled(1,52,35,2) + } + { HBox2 HBox + MoveScaled(2,55,33,4) + { Panel10 Panel + MoveScaled(2,1,10,3) + Expand = True + } + { btnPrint Button + MoveScaled(7,0,14,4) + AutoResize = True + Text = ("Print") + Picture = Picture["icon:/small/print"] + } + { Panel9 Panel + MoveScaled(21,1,10,3) + Expand = True + } + } + } + } + { btnCloseSide ToolButton + MoveScaled(35,0,3,3) + Ignore = True + Picture = Picture["icon:/small/close"] + } + } +} diff --git a/comp/src/gb.report2/.src/Viewer/ReportView.class b/comp/src/gb.report2/.src/Viewer/ReportView.class new file mode 100644 index 00000000..63fe5acd --- /dev/null +++ b/comp/src/gb.report2/.src/Viewer/ReportView.class @@ -0,0 +1,410 @@ +' Gambas class file + +Export +Inherits UserControl + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False + +Public Enum Custom, Page, DualPage, FullWidth, RealSize + +Private $aRangePage As New Integer[] + +Property Report As Report +Property Read LayoutInProgress As Boolean +Property ScaleMode As Integer +Property ShowPageNumbers As Boolean +Property Range As String +Property _GrayScale As Boolean +Property Zoom As Float +Property Read Count, PageCount As Integer +Private $bGrayScale As Boolean +Private $hReport As Report +Private $hView As New DocumentView(Me) As "View" +Private hOBS As Observer +Private tmrLayout As New Timer As "TmrLayout" +Private $sRange As String +Private $iScaleMode As Integer +Private $bShowPageNumbers As Boolean +Private $bLockLayout As Boolean +Private $bStopLayout As Boolean +Private tmrView As New Timer As "tmrView" +Property Read RangePages As Integer[] + +Event Zoom + +Public Sub _new() + + hOBS = New Observer($hView.Children[0], True) As "Area" + 'tmrLayout.Delay = 5 + Me.Proxy = $hView + +End + + +Private Function _GrayScale_Read() As Boolean + + Return $bGrayScale + +End + +Private Sub _GrayScale_Write(Value As Boolean) + + $bGrayScale = Value + +End + +Public Sub View_Draw((Page) As Integer, Width As Integer, Height As Integer) + Dim hImg As Image + Dim iPage As Integer + + If $aRangePage.Count > 0 Then + Try iPage = $aRangePage[Page] + If Error Then iPage = Page + Else + iPage = Page + Endif + + Paint.AntiAlias = True + If $bGrayScale Then + hImg = New Image(Width, Height, Color.White) + Paint.Begin(hImg) + Endif + + $hReport.Scale = $hView.Zoom + $hReport.Paint(ipage + 1) + If $bShowPageNumbers Then + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 125)) + Paint.Font = Font["Arial,Bold,+20"] + Paint.Text(iPage + 1, 0, 0, Width, Height, Align.Center) + Paint.Fill + Endif + + If $bGrayScale Then + Paint.End + Paint.DrawImage(hImg.Desaturate(), 0, 0) + Endif + $hReport.Scale = 1.0 + +End + +Public Sub View_Zoom() + + 'tmrLayout.Stop + Raise Zoom + +End + +Public Sub View_Finished() + + If $hReport._LayoutNotFinished Then + $bStopLayout = False + tmrLayout.Trigger + Endif + +End + +Public Sub Area_Scroll() + + tmrLayout.Stop + +End + + +Public Sub Area_MouseWheel() + + If Not $hView.Arrangement = Arrange.Row Then + $hView.Arrangement = Arrange.Row + $hView.Columns = 0 + Endif + +End + +Public Sub Area_Draw() + + Dim sText As String + Dim iTextWidth As Integer + + sText = Str($hView.FirstVisiblePage + 1) & "/" & Str($hView.Count) + iTextWidth = Paint.TextSize(sText).Width + 10 + Paint.Rectangle(Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, 5) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.black, 125)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.black) + Paint.Stroke + + Paint.Brush = Paint.Color(Color.White) + Paint.Font.Bold = True + Paint.Text(sText, Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, Align.Center) + Paint.fill + + +End + +Public Sub tmrLayout_Timer() + + If $bLockLayout Or If $bStopLayout Then Goto ESCAPE + If Not $hReport._LayoutNotFinished Then Goto ESCAPE + 'Need to add wait to allow refreshing + Wait 0.01 + tmrLayout.Trigger + $hReport.Layout($hReport.PageCount) + 'Add a try because closing the object cause an invalid object error + 'Certainly because the loop come after deleting + Try $hView.Count = $hReport.PageCount + Return + +ESCAPE: + tmrView.Trigger + +End + +Private Function Report_Read() As Report + + Return $hReport + +End + +Private Sub Report_Write(Value As Report) + + + If Not Value Then Return + + $hReport = Value + $hReport.Layout(1) + $hView.Count = $hReport.PageCount + $hView.Padding = Report.UnitTo(5, "mm", "px") + $hView.Spacing = $hView.Padding + $hView.DocWidth = Report.UnitTo(TSizeParse[Value.Width].ToInch(), "in", "px") + $hView.DocHeight = Report.UnitTo(TSizeParse[Value.Height].ToInch(), "in", "px") + $hView.Reset + + tmrLayout.Trigger + + +End + +' Public Sub MoveNext() +' +' $hView.MoveNext +' $hView.Item.EnsureVisible +' +' End +' +' Public Sub MovePrevious() +' +' $hView.MovePrevious +' $hView.Item.EnsureVisible +' +' End +' +' Public Sub MoveFirst() +' +' $hView[0].EnsureVisible +' +' End +' +' Public Sub MoveLast() +' +' $hView[$hView.Count - 1].EnsureVisible +' +' End +' +' Public Sub MoveTo((Page) As Integer) +' +' $hView. +' +' End + + +Private Function LayoutInProgress_Read() As Boolean + + Return $hReport._LayoutNotFinished + +End + +Private Function ScaleMode_Read() As Integer + + Return $iScaleMode + +End + +Private Sub ScaleMode_Write(Value As Integer) + + $iScaleMode = Value + + $hView.Center = False + + Select Case Value + Case Me.Custom + $hView.Columns = 0 + $hView.Arrangement = Arrange.Row + Case Me.Page + $hView.Columns = 0 + $hView.Arrangement = Arrange.Fill + $hView.Center = True + Case Me.DualPage + $hView.Columns = 2 + $hView.Arrangement = Arrange.Row + $hView.Center = True + Case Me.FullWidth + $hView.Columns = 0 + $hView.Arrangement = Arrange.Vertical + Case Me.RealSize + $hView.Columns = 0 + $hView.Arrangement = Arrange.Row + $hView.Zoom = 1 + End Select + + tmrView.Trigger + +End + +Private Function ShowPageNumbers_Read() As Boolean + + Return $bShowPageNumbers + +End + +Private Sub ShowPageNumbers_Write(Value As Boolean) + + $bShowPageNumbers = Value + tmrView.Trigger + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + + + Dim ars As String[] + Dim ars2 As String[] + Dim s As String + Dim iStart, iEnd, i As Integer + + $sRange = Value + $aRangePage.Clear + + If Not $sRange Then Goto Fin + ars = Split($sRange, ";") + + For Each s In ars + If InStr(s, "-") Then + ars2 = Scan(s, "*-*") + iStart = CInt(Val(ars2[0])) - 1 + iEnd = Val(ars2[1]) - 1 + If iStart < 0 Or If iEnd < 0 Or If iEnd < iStart Then Goto Fin + If $aRangePage.Count > 0 And If iStart < $aRangePage[$aRangePage.Max] Then Goto Fin + For i = iStart To iEnd + + $aRangePage.Add(i) + + Next + Else + iStart = CInt(Val(s)) - 1 + If $aRangePage.Count > 0 And If iStart < $aRangePage[$aRangePage.Max] Then Goto Fin + $aRangePage.Add(iStart) + Endif + Next + +Finally +Fin: + If $aRangePage.Count > 0 Then + $hView.Count = $aRangePage.Count + Try $hView[0].EnsureVisible + Else + Try $hView[0].EnsureVisible + $hView.Count = $hReport.PageCount + Endif + tmrView.Trigger +Catch + +End + +Private Function Zoom_Read() As Float + + Return $hView.Zoom + +End + +Private Sub Zoom_Write(Value As Float) + + $hView.Zoom = Value + +End + +Private Function Settings_Read() As Variant[] + + Return [$hView.Zoom, CVariant($iScaleMode)] + +End + +Private Sub Settings_Write(Value As Variant[]) + + $hView.Zoom = Value[0] + $iScaleMode = Value[1] + +Catch + +End + +Public Sub _ForceLayout() + + Dim htmpImage As New Image(1, 1) + $bStopLayout = True + Paint.Begin(htmpImage) + $hReport.Layout() + Paint.End + $hView.Count = $hReport.PageCount + +End + +Public Sub LockLayout() + + $bLockLayout = True + +End + +Public Sub UnlockLayout() + + + $bLockLayout = False + $bStopLayout = False + tmrLayout.Trigger + +End + +Public Sub Refresh() + + Me.Report = Me.Report + tmrView.Trigger + +End + + + +Private Function Count_Read() As Integer + + If $aRangePage.Count > 0 Then Return $aRangePage.Count + Return $hReport.PageCount + +End + + +Private Function RangePages_Read() As Integer[] + + Return $aRangePage + +End + +Public Sub tmrView_Timer() + + $hView.Refresh + +End diff --git a/comp/src/gb.report2/.src/_ReportSection.class b/comp/src/gb.report2/.src/_ReportSection.class new file mode 100644 index 00000000..94224b5b --- /dev/null +++ b/comp/src/gb.report2/.src/_ReportSection.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits ReportVBox +Public Const _Properties As String = "*,Text" +Property Text As String +Private $sText As String + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report2/16/full-width.png b/comp/src/gb.report2/16/full-width.png new file mode 100644 index 0000000000000000000000000000000000000000..f2ad55e4c5d97469dc38c5b21ff753ccbfb1045b GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc^6Wib977~7 z&+YT%V^H8=c0aT~yZ)VH^XZF|0&X$Sytea-@Z6g${dL@%%dc#;IT#u_LBW4oCS&We fs#SCC57zMTsF?GvozTC*8)Si}tDnm{r-UW|hkPpo literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/one-page.png b/comp/src/gb.report2/16/one-page.png new file mode 100644 index 0000000000000000000000000000000000000000..c4355cffaba40f228e3652f2651d98a3be78bb57 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc^2|M5977~7 zpY7ks%V5B9*yrf|CG!ua$2M}m(w(NH+VbP0l+XkK6Q(9p literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/real-size.png b/comp/src/gb.report2/16/real-size.png new file mode 100644 index 0000000000000000000000000000000000000000..ddfd33bd6ad9ffe3bc5e0a69eac4467b25fbdb20 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc@+v)D977~7 z=bqij+iW1 K&t;ucLK6VBDIaYB literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/red-arrow-v.png b/comp/src/gb.report2/16/red-arrow-v.png new file mode 100644 index 0000000000000000000000000000000000000000..a787fe89c60ce899a23198ffa582b5bc1540384d GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^&H$ef*Z)9JU;m$hA?NbP6+l78 zk|4ie28U-i(tsRUPZ!4!j_b(@2?-p$sVAaNDr{|O%w=L;%*+flz<{4Q_>%I`W}rd_ MPgg&ebxsLQ04Dk&EdT%j literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/two-pages.png b/comp/src/gb.report2/16/two-pages.png new file mode 100644 index 0000000000000000000000000000000000000000..252dbd11e46e32e4e07f7e49855f63b5ffb10e39 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc@~k~w977~7 zZ|yVWVo=~=UVo|n_MbZkMUDg-oSqmk%Z1UwbTX&HtY&Y1$6uIaQx_uYbs zFl1V%NP3T!Twqt^hg1L5T@7_ICrx-;9Oc@wv|K?C>pBwQW PXbyv?tDnm{r-UW|Mdc*X literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/22/RealSize.png b/comp/src/gb.report2/22/RealSize.png new file mode 100644 index 0000000000000000000000000000000000000000..f93d1289b4a9f944672e452e305930159a57637c GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2CVRR#hG?8mPEcSpFfuUsqwc6X zvFpFFfx*N_JhwE{8ZYJWY~l&xsh8+eKA9+-{NwzA0|LhjraTlrTq@EWaP*(DPgsh@ zkq=#t%&vxJh7}h|7tQR^YBPTEFPXo3lM}P3L6G7c&m!Y9l5#v%JZU_Kcv5-pOR7B< y1e;{g{?J}zUz>D`);D?6IUk!3PImDO;$eteYM}V|B)=EXc?_PeelF{r5}E*|>qzAQ literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/22/TwoPage.png b/comp/src/gb.report2/22/TwoPage.png new file mode 100644 index 0000000000000000000000000000000000000000..306e7cee92a4e4f56325c4578c0a91fc034eba0e GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L296VhdLp07OCnzu)82ve}b*dp$ zhWkgq%OVD~pgD%CDqJ+v9Fv57(seX!-2TimnEO$FRV&BQiYZCYMBG$zOIR2_SaK=7 TmCRcUG>gI0)z4*}Q$iB}hN~o7 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/32/Collatecopie.png b/comp/src/gb.report2/32/Collatecopie.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0a284961fe7362bd9bc98c0020bdb460f4f017 GIT binary patch literal 449 zcmV;y0Y3hTP)2wR%gW*5 z;m*v<k@N&FAOl=;!C_>gww0>FMd{?Ca~&(9iAc>+I|5>gnjt%*^fV?BCwq zy1BU0(b3M$&BMRG?(OW*&(Gf7-N(km?(Oa7=H z>F3-1oB03$010$bPE!E*#1F|8st?Je;|_TM006~FL_t(I%e|4=4uU`oMeBkJjE-0x z#SPr|1=RolHB)d52~GOOdF{=4=mFRDd|#<-`RMX?UVk;SkM#IZ%O z4#bH?u@0@na5~G-V|#e~wn0`V09%$`4Y0kn)3+AEf*s rgwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<@9*!aq@v>D;_&b9>+9>|vw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&G~)6>z^)}Nl8&Bn#2p`h~c@bvTY>FMjz z)z_n;pSrZNrl6m#r=`rs#BKCF>;M1&0d!JMQvg8b*k%9#0We8KK~y-)jnipUfC*1|J}gbU@9e(o`@c+T7l9ti-wdyw|L!n5^Ds;Ud`%vFB=ECb zfDeX3LbE8gNRlMWa@Z8{lOW2qg4&`ahy~&xB}Rkx4iK^ez7xdKKuvUkSd|8X2GVJe z6uLo?BneXK9#C(J2AMvPP6F(G1OA?4?;9{S=YRV@s1>ThgR(0A`3WQJ5g3@fWPcEp zr7vj=BtV0P6$qdqdc&~tdzjPm`H@lj)JocJYM002ovPDHLkV1nd{ BwRHdh literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/32/grayscale.png b/comp/src/gb.report2/32/grayscale.png new file mode 100644 index 0000000000000000000000000000000000000000..670e944d6aeb111916160f2bdf358937d265d71c GIT binary patch literal 1564 zcmV+%2IKjOP)gwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<;^N}tlq(=H=(+<>%+-=;!C?=;-O_=;`U{>gnm~>gn(A@2RAs zgn+B@9XR9cq)=I7_<;NRc5xwzoq;Ogq?@$m22+1RqNvAnyx$j8U6 ztgOk&$+0p?K?W&CR>Ix#HpB!ok4M&(8Al@$T*HxVN{O zn3(0`K_n-QDQt=GfQQ?CR>&)6=G-q0P$2hKBL+ z@bmKW^z-xM;^NlU)%ErC>ged0mX?EpfrNvDtgEWy;o)P4ay1BW>#>MjS@R^yI zg@l8KgoNVY;G?3U^YQV#ySm-o+|SOFMjz)z_n;pSrZNrl6m#r=`rs#6oTBNB{r;0d!JMQvg8b*k%9#0@+DKK~y-)V_+Zx zFf!o)j8Fy4EUawo9Gsk7+&nzIe0+TT{QLqSAjk|=AjB#xA}S^>At@y-BO@y-Coiv{ z2m(q%PzB0tBBJ6dl9C{4Igq3(8yKi5!xX4%h-*q}X=%&K>FDa}>9eYU&gJ1@v4$z&0?E6% zJ2-fFdTDZb`{?@efq)-Ofj^h6bUaL6nuWU35%L ztfyC8d_rQ9rJ`DLN=mAML0S|{LAs1=MogwlR(4KqUR-j1N`XvDN}+*)A51}!m7SbK zv9o7hLJ2Sw)$;QB)TF}s3=9N|pbAP^*viTic`GV|tJ120Kp|0~rnXM8roI$r0#8F@ zQ*%jpORIpIni>mBTRWGkPe*4L+yrYiw(g$ZzVQCOuCA^L6DDv4bFmvwG^we9hl3jX zr1Hdw$y26+q^ql?rb*Q#Pgkv(0aGwjfZbAN)@%dzq&a?m)zva{=gqe$n>S;|jF~VK z7I365T(r1dYDrQ+9Q)E`#>=GwSF8jofSa(&a`l?EYuC+NzkI{SO`BJ3*}AQzW&8a3 zH8bG~xOjH#+|06Tx1dX$<+d7`ZGL<9*7EV~gPE{jjf*EjjV0qiO$J-dhx!kWdnDgK^JYN76M1T~40%`vI3CnFB zJ~Ei|_z5E8JT>6i0#r~13Z?n;t5{v0J%8~Mp`U@_6<7fa3q%1+-0L@Q-yzF`6$pS8 z2nx>MY7qAR!^cl3dGm8M%RG>7kpB7e{l0wthL(B1|3C#le-V@AVE_QtsRkb4V6q1Q O0000fzww@9*!fs;R=gys4z3>+9;`;^Oe{@9^*L^YZbkrljlZ>*M3&@$m2L>+9s? zvw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&HL=;zbZ)6vw{pPrq~#>JFd(f*Q24Iy0o&Upr5U$rOd{}5gA^>00001bW%=J06^y0W&i*Il1W5CR5;6Z(^pf2 zKoo}I6E%W}N@Njpk4$I3fxT zOskh$!bC)+13Q^ag-Rw36A_gFI956x9;95^LBzm8Mis;1BAY=(j-!yvbHas_mYsx& zuPKQl*C1jxW3WMt7Y_2WBRc?I>pufe^hq{@LSF$(xrMs$8AvQ3Oz77%4PQ&E4bOcq z7I;3P16}8Q2YrAP0utb0P~r`ShK7rp?*$%yYRR%(yoFV5WOQtNVsdJFW_At{Dwy-O z?Yb$~t|l!kE-kODuC13g5b;siRP5T6t?iw%QrRsrWPpSQd)@m7O6BmViVPP4j+I<( z4cjNDXXhn`Py^tiez0|Ub$wG}2s1*1+q?URM}|;?{ufVW_1Q4!^@16%Z|_xv8uT1K yKI{AXW(YOlw=lwtzjF>k4X7<_Kz(83_v8o7|B&yS(h6z-0000>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> +'##################### PUBLIC PROCEDURES ######################### +'##################### PRIVATE PROCEDURES ######################## +'######################## PROPERTIES ############################# + diff --git a/comp/src/gb.report2/gambas.svg b/comp/src/gb.report2/gambas.svg new file mode 100644 index 00000000..5bfafe4d --- /dev/null +++ b/comp/src/gb.report2/gambas.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/comp/src/gb.report2/icon.png b/comp/src/gb.report2/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..622c737863940f037e52c57ec5d213056a5dcf26 GIT binary patch literal 2357 zcmV-53Ci|~P)yzkY*rqhtx$LfYcg~H6-7}floxNYYhhYn$AXb4KF z_KY9|T5CutA%wuz))tgf$Ye5DU0u~$>lxQ|zkc-S(M-1i&`vwhS_1&2l<+*S?Z!Dr zu~R<{6jk_JHlj8PB{EG{lWX$=BE2mz%O;_*0y z5Hy)g#z&x&wIWyvqm^c64?>7P9ZD$?i^V`G17i%7G30VNWU^UU zmPK9H9g5$(I3u3r}$_4g%QQQvh7Z^v)p{iG&+6+#GizDJ3k+@*TGwZ_nnodAG*ev5A8H=b*C%r*cBa3Np|5pudy=Z%CAV2lAM)dY(%Xi7t4 zFvcLuP(05A0C=2(mu>1T050Hp92WC@qqB0#|7JOrTI5fG(d-@&N!LG-f*aF4?(hh=4G_CvujO$6|M)__t35Z{Pir9eHI zCHS=%oFCIX~tZ|t@sK+CW({Z=Ia z1%h^V_}T^_wF(*1jrBmYikbyL8DQD{0U)J>58SAzvJ4%HApEjF)@Lmp&?k7 zRkbp#+%2kfpuU&2PZ2^;EEci3xryu7uY*#GFC0077xuk?ojZ3%0n?8jJ@PV{j1Az8 zGiT2Hv{xuyo;{I<1EtV==Lhd#Vc{C0)i?@tdzVH2*c?5Veh%Mz z`}?r0+KI5ZxQJV~ZWXlFd(NFZcOM9(fMr>qS4zcq@7@j0IYgcNYTL4~YjhX(f9e2~ zQl+A(lz(hm@O!OuwPuf2(&4oqpR1MQVN`NodAL<*hrNr7Xm;C3FT_d z0hNNs1#-FEU;t3E)MVoYky0uY8CK=VW&kKD;T4O>WHN&RKuV}4YJllC7%7nvU?9{S zP$_tv4-5dIR8vI|Xd(iFQ&r7QqyqOiMA7Ss^Q{6QVT&3V>)0C7UaPmHoDv0;OjIazFrB<@X7iO@X=*@c4;r zofMD?Qplbaz*Ygkvbt9Ujq{D}1kK&xAOIkCEK~$N&%@Hv67u;xMn*<3K0Y3@J;cr& zn2VbMAeDqB1a|oeannw<@T*7wx<+4wh9Y2da})U7pRq7Di$m{y1EaZ7IF923XqW=# z*0Pq;&!+MCbpJO*qqH~utZ#EH7Q@G^003~DxL@3(z*a4W%u={s1iW(MI9@q%e7k+F zlCjxw9DMr2*KlC@G;G@jjZ^Dli5>-6CkVnWL7HQCnQw^<1D` z)h&WbDg0`Fu5Jh2ej}tQwLMhzg^aSA5wIquRDby6dmX+y*V<-hzhmd+FCA3?*39={ zoU`%q@g3=j=kG%)kQOY9N|$b`8~rP-Ap!jU>Qx7T03dM-o(1#Bn zN+INy;KDUeevBN)!GQw@000*+Uc}PUQda?Vs#Ge4{rmR=01}A=ip65sf5t$x7RJ|d zQwD(Fik$z3Qab4gVe9gnSO8Ec6p+v70RXP+VsvzLyL}GHbzR@Sd_Iptp#YSliVzCh zu>`pMU8TPkN_EI@_y7ITTTku!#Ek8@q4)9?s6W8e)D)(srncMXDjEB@RG{&MM<|V% zb6m>5Y;6ALo0a|=0MZaySy@@f%F6Ml)9L5qj+53}GuKVN$p!ynKA-Pxwd;~fnm?tFm9#m_Wt3W5T_%P0kzBg1SMa?)91)^2w+l3$le{buqx|dbGmg9}8sBtI? bMA!9Sq6r@{L;RH=00000NkvXXu0mjf^6p<( literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/tmpJournal b/comp/src/gb.report2/tmpJournal new file mode 100644 index 00000000..eb76ce09 --- /dev/null +++ b/comp/src/gb.report2/tmpJournal @@ -0,0 +1,12 @@ +[GB.REPORT2] +* BUG: The ReportControl font property now make a copy of the font given in argument. +* BUG: Now a too big children element is clipped if it is the first one listed + after the fixed ones (no more infinite loop) +* NEW: ReportUnit module is merged with Report class. All functions for convertion + are now available as static functions in Report. The two internal functions scale and + internal width are now public function of report instance. +* NEW: Return of the ReportImage and ReportSvgImage with no change for now. +* NEW: Continue to improve the ReportGridView widget ... really not ready for use. +* NEW: Restore the UseField property in ReportLabel so for now the component is + backward compatible. + \ No newline at end of file diff --git a/comp/src/gb.report2/tortueface.gif b/comp/src/gb.report2/tortueface.gif new file mode 100644 index 0000000000000000000000000000000000000000..249942783056e283b62934c309699e8229228327 GIT binary patch literal 1019 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 2 Then Break + hMenu = New Menu(mnuSources) As "mnuSources" + hMenu.Text = aSources[i] + hMenu.Tag = aList[i] + + If sCur = aList[i] Then + mnbSource.Text = ("Source : ") & hMenu.Text + mnbSource.Tag = hMenu.Tag + Endif + Next + + 'Link menu to button + mnbSource.Menu = "mnuSources" + Endif + + 'Loading Resolution menu + + If $hScan.Exist("Resolution") Then + sCur = $hScan["Resolution"].Value + For Each s In $hScan["Resolution"].List + hMenu = New Menu(mnuResolutions) As "mnuResolutions" + hMenu.Text = s + hMenu.Tag = s + + If s = sCur Then + MnbResolution.Text = ("Resolution : ") & hMenu.Text + MnbResolution.Tag = hMenu.Tag + Endif + + Next + MnbResolution.Menu = "mnuResolutions" + Endif + + If $hScan.Exist("Mode") Then + sCur = $hScan["Mode"].Value + For Each s In $hScan["Mode"].List + 'Well loading Modes as it because + 'they are too many modes style to manage + 'and I don't want to limit my scanners options + + hMenu = New Menu(mnuModes) As "mnuModes" + hMenu.Text = s + hMenu.Tag = s + If sCur = s Then + mnbMode.Text = ("Mode : ") & s + mnbMode.Tag = s + Endif + Next + mnbMode.Menu = "mnuModes" + Endif + +hbScanOptions.Show + +'Set Values from settings +'Take a look if settings for this scanner exist +If Settings[$hScan.Name &/ "mode"] = Null Then Return + +'Set the Mode +mnbMode.Text = Split(Settings[$hScan.Name &/ "mode"])[0] +mnbMode.Tag = Split(Settings[$hScan.Name &/ "mode"])[1] +'Set the Resolution +MnbResolution.Text = Split(Settings[$hScan.Name &/ "resolution"])[0] +MnbResolution.Tag = Split(Settings[$hScan.Name &/ "resolution"])[1] +'Set the Source +mnbSource.Text = Split(Settings[$hScan.Name &/ "source"])[0] +mnbSource.Tag = Split(Settings[$hScan.Name &/ "source"])[1] + +End + +Public Sub mnuScanners_Click() + + If Last.Text = $hScan.Name Then Return + $hScan = Scanners[Last.Text] + SetScanner() + +End + + + +Public Sub mnuSources_Click() + + mnbSource.Text = ("Source : ") & Last.Text + mnbSource.Tag = Last.Tag + Settings[$hScan.Name &/ "source"] = [mnbSource.Text, CStr(mnbSource.Tag)].Join() + +End + + +Public Sub mnuResolutions_Click() + + MnbResolution.Text = ("Resolution : ") & Last.Text + MnbResolution.Tag = Last.tag + Settings[$hScan.Name &/ "resolution"] = [MnbResolution.Text, CStr(MnbResolution.Tag)].Join() +End + + +Public Sub mnuModes_Click() + + mnbMode.Text = ("Mode : ") & Last.Text + mnbMode.Tag = Last.Tag + Settings[$hScan.Name &/ "mode"] = [mnbMode.Text, CStr(mnbMode.Tag)].Join() +End + + +Public Sub Scanner_Begin() + +Spring1.Hide +hbProgress.Show + +End + +Public Sub Scanner_PageBegin() + + + +End + + +Public Sub Scanner_PageEnd() + + Dim hImg As Image + Dim s As String + + Try hImg = Last.Peek() + + If Not hImg Then + msvError.Open("Error : Can't load image.", Picture["icon:/32/warning"]) + Return + Endif + + $aImg.Add(hImg) + dvPages.Count = $aImg.Count + + 'Show the toolbar + hbToolbar.Show + +End + +Public Sub Scanner_End() + + hbProgress.Hide + Spring1.Show +End + + +Public Sub Scanner_Error(ErrorText As String) + + msvError.Open(ErrorText, Picture["icon:/32/error"]) + +End + +Public Sub Scanner_Progress() + + pgbScan.Value = Last.Progress + +End + + + + + +Public Sub dvPages_Draw(Page As Integer, Width As Integer, Height As Integer) + + Paint.DrawImage($aImg[Page], 0, 0, Width, Height) + +End + +Public Sub btnPrint_Click() + + Dim hPrinter As New Printer As "Printer" + hPrinter.FullPage = True + hPrinter.Paper = hPrinter.A4 + hPrinter.Preview() + +End + +Public Sub Printer_Begin() + + Last.count = $aImg.Count + +End + +Public Sub Printer_Draw() + + Paint.DrawImage($aImg[Last.Page - 1], 0, 0, Paint.Width, Paint.Height) + +End + + +Public Sub btnNew_Click() + + If Not $bSaved Then + If Message.Warning("You have not saved the current scanning session !\nAre you sure you want to clear all ?", "&No", "&Yes") = 0 Then + Return + Endif + Endif + $aImg.Clear + dvPages.Count = 0 + hbToolbar.Hide + +End + + +Public Sub mnuScannersRefresh_Click() + + Scanners.Search + +End + + +Public Sub btnSave_Click() + + Dim hPrinter As New Printer As "Printer" + Dim sPath As String + Dim hImg As Image + Dim i As Integer + + Dialog.Path = Settings["General/SavePath", User.home & "/"] + If Not Dialog.SaveFile() Then + sPath = Dialog.Path + If File.Ext(sPath) = "jpg" Then + For i = 0 To $aImg.Max + $aImg[i].Save(File.SetBaseName(sPath, File.BaseName(sPath) & Format(i, "000"))) + Next + Endif + Endif +End + + +Public Sub tmrTry_Timer() + + Dec $iTryDelayCnt + Me.Title = $sAppName & " - No scanner available... New try in " & $iTryDelayCnt & "s" + If $iTryDelayCnt = 0 Then + Me.Title = $sAppName & " - Searching scanners..." + $tmrTry.Stop + $tmrTry = Null + Scanners.Search + Endif +End diff --git a/comp/src/gb.scanner/.src/Demo/FScan.form b/comp/src/gb.scanner/.src/Demo/FScan.form new file mode 100644 index 00000000..f7047823 --- /dev/null +++ b/comp/src/gb.scanner/.src/Demo/FScan.form @@ -0,0 +1,211 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,150,86) + Font = Font["+1"] + Text = ("Gambas Easy Scan") + Arrangement = Arrange.Vertical + { mnuScanners Menu + Text = ("mnuScanners") + Visible = False + } + { mnuResolutions Menu + Text = ("mnuResolutions") + Visible = False + } + { mnuModes Menu + Text = ("mnuModes") + Visible = False + } + { mnuSources Menu + Text = ("mnuSources") + Visible = False + } + { HBox1 HBox + MoveScaled(1,1,149,6.8571) + { mnbScanner MenuButton + MoveScaled(0,0,9,7) + Enabled = False + NoTabFocus = True + Picture = Picture["icon:/32/scanner"] + Border = False + MenuOnly = True + } + { PictureBox1 PictureBox + MoveScaled(15.2857,0,3.4286,6.8571) + Picture = Picture["next.png"] + } + { hbScanOptions HBox + MoveScaled(20,0,129,7) + Visible = False + Expand = True + Spacing = True + { mnbMode MenuButton + MoveScaled(0,0,28.5714,7) + Font = Font["+1"] + AutoResize = True + Text = ("Mode :") + Border = False + MenuOnly = True + } + { PictureBox2 PictureBox + MoveScaled(25.2857,1,3.4286,6.8571) + Picture = Picture["next.png"] + } + { MnbResolution MenuButton + MoveScaled(35,1,28.5714,7) + Font = Font["+1"] + AutoResize = True + Text = ("Resolution :") + Border = False + MenuOnly = True + } + { PictureBox3 PictureBox + MoveScaled(65,1,3.4286,6.8571) + Picture = Picture["next.png"] + } + { mnbSource MenuButton + MoveScaled(71,1,28.5714,7) + Font = Font["+1"] + AutoResize = True + Text = ("Source :") + Border = False + MenuOnly = True + } + { PictureBox4 PictureBox + MoveScaled(97,1,3.4286,6.8571) + Picture = Picture["next.png"] + } + { hbProgress HBox + MoveScaled(101,0,7,7) + Visible = False + Expand = True + Margin = True + Padding = 5 + { pgbScan ProgressBar + MoveScaled(1,0,8,6) + Expand = True + Border = False + } + } + { Spring1 Spring + MoveScaled(110,3,2,3) + } + { btnScan Button + MoveScaled(112,0,16,7) + Font = Font["Bold"] + Background = Color.SelectedBackground + Text = ("Scan") + } + } + } + { Separator1 Separator + MoveScaled(20,9,104,0) + } + { msvError MessageView + MoveScaled(7,10,133,6) + } + { Panel2 Panel + MoveScaled(5,11,163.7143,63.7143) + Expand = True + Arrangement = Arrange.Horizontal + { VBox1 VBox + MoveScaled(0,6,6,50) + Spacing = True + { ToolButton1 ToolButton + MoveScaled(0,0,6,6) + Picture = Picture["icon:/32/left"] + } + { ToolButton2 ToolButton + MoveScaled(0,7,6,6) + Picture = Picture["icon:/32/right"] + } + { Separator3 Separator + MoveScaled(1,13,5,0) + } + { ToolButton3 ToolButton + MoveScaled(0,14,6,6) + Picture = Picture["icon:/32/rotate-left"] + } + { ToolButton4 ToolButton + MoveScaled(0,20,6,6) + Picture = Picture["icon:/32/rotate-right"] + } + { Separator4 Separator + MoveScaled(0,27,5,0) + } + { ToolButton5 ToolButton + MoveScaled(0,29,6,6) + Picture = Picture["icon:/32/cut"] + } + { Separator5 Separator + MoveScaled(0,41,5,0) + } + { ToolButton7 ToolButton + MoveScaled(1,42,6,6) + Picture = Picture["icon:/32/delete"] + } + } + { Panel1 Panel + MoveScaled(17,0,145.8571,62.8571) + Expand = True + Arrangement = Arrange.Fill + { dvPages DocumentView + MoveScaled(17,12,134,51) + Visible = False + Expand = True + Arrangement = Arrange.Horizontal + } + { vbWait VBox + MoveScaled(1,2,125,55) + Background = &HBCBEC0& + Expand = True + { Spring2 Spring + MoveScaled(61,2,3,3) + } + { spnWait Spinner + MoveScaled(32,7,60,40) + Foreground = &HDBDDE0& + } + { lblWait Label + MoveScaled(19,35,89,8) + Font = Font["Bold,+8"] + Foreground = &HDBDDE0& + Text = ("CONNECTING SCANNER") & "..." + Alignment = Align.Center + } + { Spring3 Spring + MoveScaled(50,48,2,4) + } + } + } + } + { Separator2 Separator + MoveScaled(2,77,139,0) + } + { hbToolbar HBox + MoveScaled(2,78,129,6) + Visible = False + Spacing = True + Margin = True + Padding = 2 + { btnNew Button + MoveScaled(0,0,21,5) + Text = ("New") + Picture = Picture["icon:/22/new"] + } + { Spring4 Spring + MoveScaled(22,1,27,1) + } + { btnPrint Button + MoveScaled(85,0,22,5) + Text = ("Print") & "..." + Picture = Picture["icon:/22/print"] + } + { btnSave Button + MoveScaled(108,0,21,5) + Text = ("Save") & "..." + Picture = Picture["icon:/22/save"] + } + } +} diff --git a/comp/src/gb.scanner/.src/Demo/Form1.class b/comp/src/gb.scanner/.src/Demo/Form1.class new file mode 100644 index 00000000..952651f1 --- /dev/null +++ b/comp/src/gb.scanner/.src/Demo/Form1.class @@ -0,0 +1,25 @@ +' Gambas class file + +Private $himage As New Image + + +Public Sub Button1_Click() + Dim hscan As New Scanner("hpaio:/net/OfficeJet_5200_series?ip=192.168.1.25") + '$himage = Scanners["hpaio:/net/OfficeJet_5200_series?ip=192.168.1.25"].Scan() + hscan["Mode"].Value = "Color" + $himage = hscan.Scan() + DrawingArea1.Refresh + +End + +Public Sub Form_Open() + + + +End + +Public Sub DrawingArea1_Draw() + + Draw.Image($himage, 0, 0, Paint.Width, Paint.Height) + +End diff --git a/comp/src/gb.scanner/.src/Demo/Form1.form b/comp/src/gb.scanner/.src/Demo/Form1.form new file mode 100644 index 00000000..0e256646 --- /dev/null +++ b/comp/src/gb.scanner/.src/Demo/Form1.form @@ -0,0 +1,11 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,64) + { Button1 Button + MoveScaled(13,6,25,18) + } + { DrawingArea1 DrawingArea + MoveScaled(16,30,38,25) + } +} diff --git a/comp/src/gb.scanner/.src/Demo/Main.module b/comp/src/gb.scanner/.src/Demo/Main.module new file mode 100644 index 00000000..833075c7 --- /dev/null +++ b/comp/src/gb.scanner/.src/Demo/Main.module @@ -0,0 +1,8 @@ +' Gambas module file + +Public Sub Main() + + Application.Theme = "gambas" + FScan.Show + +End diff --git a/comp/src/gb.scanner/.src/Demo/Main2.module b/comp/src/gb.scanner/.src/Demo/Main2.module new file mode 100644 index 00000000..f363507c --- /dev/null +++ b/comp/src/gb.scanner/.src/Demo/Main2.module @@ -0,0 +1,31 @@ +' Gambas module file + + +Public Sub Main() + + +Dim hScan As New Scanner("hpaio:/net/OfficeJet_5200_series?ip=192.168.1.25") +Dim s As String +Dim sUnit As String + +For Each s In hScan + Print hScan[s].Name + 'Print hScan[s].Info + + + If hScan[s].List Then + sUnit = hScan[s].Unit + Print " " & hScan[s].List.Join("|") & sUnit & " (Default: " & hScan[s].Value & sUnit & ") " & IIf(hScan[s].IsActive, "Active", "Inactive") + Endif + + If hScan[s].IsRange Then + Print " " & hScan[s].MinValue & " .. " & hScan[s].MaxValue & hScan[s].Unit & " (Default:" & hScan[s].Value & hScan[s].Unit & ") " & IIf(hScan[s].IsActive, "Active", "Inactive") + Endif + + Print +Next + + + + +End diff --git a/comp/src/gb.scanner/.src/MTest.module b/comp/src/gb.scanner/.src/MTest.module new file mode 100644 index 00000000..607dc4fb --- /dev/null +++ b/comp/src/gb.scanner/.src/MTest.module @@ -0,0 +1,17 @@ +' Gambas module file + +Public Sub Main() + + +Dim hImg As Image + +'Dim hscan As New Scanner("hpaio:/net/HP_LaserJet_M1536dnf_MFP?ip=192.168.1.25") +Dim hscan As New Scanner("v4l:/dev/video0") +hscan.Debug = True +hScan["Mode"].Value = "color" +'hScan[" Resolution "].Value = 30 +hScan!Width.Value = 2560 +hImg = hscan.Scan() +hImg.Save("~/fa.png") +End + diff --git a/comp/src/gb.scanner/.src/MTest2.module b/comp/src/gb.scanner/.src/MTest2.module new file mode 100644 index 00000000..11f7d6ae --- /dev/null +++ b/comp/src/gb.scanner/.src/MTest2.module @@ -0,0 +1,21 @@ +' Gambas module file + +Private hSCan As Scanner + + +Public Sub main() + + + Dim s As String + + hSCan = New Scanner("hpaio:/net/HP_LaserJet_MFP_M426fdn?ip=192.168.1.25") + + For Each s In hSCan + If hSCan[s].IsRange Then + Print "IsRange : ", s, " ", "==>", "min : ", hSCan[s].MinValue, "Max :", hSCan[s].MaxValue, "Units : ", hSCan[s].Unit + Else + Print "IsList : ", s, " ", "==>", hSCan[s].List.Join() + Endif + Next + +End diff --git a/comp/src/gb.scanner/.src/Module1.module b/comp/src/gb.scanner/.src/Module1.module new file mode 100644 index 00000000..abb1cc3b --- /dev/null +++ b/comp/src/gb.scanner/.src/Module1.module @@ -0,0 +1,28 @@ +' Gambas module file + +Private hscan As New Scanner("hpaio:/net/HP_LaserJet_M1536dnf_MFP?ip=192.168.1.25") As "Scan" +Public Sub Main() +Print Object.Parent(hscan) + hScan.ASync = True + +hScan["Mode"].Value = "Color" +'hScan["Resolution"].Value = 300 + +hscan.Scan() +'hImg.Save("~/fa.png") + +End + + + Public Sub Scan_Progress() + + Print hscan.Progress + +End + +Public Sub Scan_End() + + Print "End" + +End + diff --git a/comp/src/gb.scanner/.src/Module2.module b/comp/src/gb.scanner/.src/Module2.module new file mode 100644 index 00000000..1cd27e1b --- /dev/null +++ b/comp/src/gb.scanner/.src/Module2.module @@ -0,0 +1,10 @@ +' Gambas module file + +Public Sub Main() + + Dim s As String + For Each s In Scanners + Print Scanners[s].Name + Next + +End diff --git a/comp/src/gb.scanner/.src/Scanner.class b/comp/src/gb.scanner/.src/Scanner.class new file mode 100644 index 00000000..c05816e1 --- /dev/null +++ b/comp/src/gb.scanner/.src/Scanner.class @@ -0,0 +1,456 @@ +' Gambas class file + +Export +Private $sVendor As String +Private $sModel As String +Private $sType As String +Private $bDebug As Boolean +Property Debug As Boolean +Property ASync As Boolean +Property Read Name As String +Property Read Vendor As String +Property Read Type As String +Property Read Model As String +Private $sDeviceName As String +Private $bASync As Boolean +'Property _Name As String +Private $sName As String +'Private $Options As ScannerOptions + +Property Read Progress As Float +Private bHaveInfo As Boolean + +Private $aOptionsNames As String[] +Private $aOptions As ScannerOption[] +Private $aImgStack As New String[] +Private $sCurImagePath As String + +Public Struct ScannerInfo + Vendor As String + Model As String + Type As String +End Struct + +Private $fProgress As Float +Private $stmpPath As String = File.Dir(Temp()) +Event Begin +Event PageBegin +Event PageEnd +Event Finished +Event End +Event Progress +Event Error(ErrorText As String) + + +Public Sub _New(sDevice As String) + + $sDeviceName = "--device-name='" & sDevice & "'" + $sName = sDevice + 'If Not IsAvailable() Then + ' Error.Raise("Unknown Device") + 'Endif + +End + +Private Sub GetInfo() + + Dim hInfo As ScannerInfo + + hInfo = Scanners._GetInfo($sName) + $sVendor = hInfo.Vendor + $sModel = hInfo.Model + $sType = hInfo.Type + bHaveInfo = True + +End + +Private Function Name_Read() As String + + Return $sName + +End + +Private Function Vendor_Read() As String + + If Not bHaveInfo Then GetInfo + Return $sVendor + +End + +Private Function Type_Read() As String + + If Not bHaveInfo Then GetInfo + Return $sType + +End + +Private Function Model_Read() As String + + If Not bHaveInfo Then GetInfo + Return $sModel + +End + +Private Sub GetOptions() + + Dim sRet As String + Dim s, sCurGroup As String + Dim aLine As String[] + Dim hOption As ScannerOption + Dim a As String[] + Dim iTiret As Integer + Dim sName As String + Dim sValues As String + Dim sDefault As String + Dim iStartOptions As Integer + Dim iEndOptions As Integer + + If Not IsAvailable() Then Error.Raise("Unknown device or device not available") + + $aOptionsNames = New String[] + $aOptions = New ScannerOption[] + Shell "scanimage " & $sDeviceName & " -A" To sRet + + For Each s In Split(sRet, "\n") + s = Trim(s) + If InStr(s, ":") Then + sCurGroup = Trim(Left(s, -1)) + Continue + Endif + + If s Begins "-" Then + 'Correction of some specific mode name (ex: [=(yes|no)]) + If InStr(s, "[=(") Then + s = Replace(s, "[=(", " ") + s = Replace(s, ")]", "") + Endif + + iStartOptions = InStr(s, " ") + If s Ends "]]" Then + iEndOptions = RInStr(s, "[", RInStr(s, "[") - 1) - 1 + Else + iEndOptions = RInStr(s, "[") - 1 + Endif + If iEndOptions = -1 Then iEndOptions = Len(s) + + sName = Left(s, iStartOptions - 1) + sValues = Trim(Mid(s, iStartOptions + 1, iEndOptions - iStartOptions - 1)) + sDefault = Mid(s, iEndOptions + 1) + 'aLine = Split(Mid(s, iStartOptions), " ", "()") + hOption = New ScannerOption + 'Store the option group + hOption._Group = sCurGroup + 'Manage the option cutting + iTiret = IIf(sName Begins "--", -2, -1) + + 'SOme option are standardised so we can manage them with our + 'own way. + Select Case Right(sName, iTiret) + Case "l" + hOption._Name = "Left" + Case "t" + hOption._Name = "Top" + Case "x" + hOption._Name = "Width" + Case "y" + hOption._Name = "Height" + Case Else + hOption._Name = Right(sName, iTiret) + hOption._Name = UCase(Left(hOption._Name)) & Right(hOption._Name, -1) + End Select + 'Remember the real option command line + hOption._Command = sName + 'sOptions = Mid(s, iStartOptions, iEndOptions - iStartOptions) + 'Define the option style (Range/List) + hOption._IsRange = InStr(sValues, "..") + 'Manage the range style option + If hOption._IsRange Then + 'if find a coma then remove it and the text next + If InStr(sValues, ",") Then sValues = Left(sValues, InStr(sValues, ",") - 1) + aLine = Split(sValues, " ", "()") + sValues = aLine[0] + a = Scan(sValues, "*..*") + hOption._MinValue = a[0] + s = GetUnit(a[1]) + If s Then + hOption._MaxValue = Left(a[1], -Len(s)) + hOption._Unit = s + Else + hOption._MaxValue = a[1] + Endif + If aLine.Count = 2 Then + hOption._Steps = CFloat(Mid(aLine[1], RInStr(aLine[1], " "))) + Endif + + Else + 'If hOption.Name = "Mode" Then Stop + 'Manage the list style option + 'remove also empty list entry + hOption._List = Split(Replace(sValues, "||", "|"), "|") + 'If the option list have a unit then use it and remove it from the list + s = GetUnit(hOption._List[hOption._List.Max]) + If s Then + hOption._List[hOption._List.Max] = Left(hOption._List[hOption._List.Max], -Len(s)) + hOption._Unit = s + Endif + Endif + + 'Manage the default values and the not activate fag + If sDefault Begins "[" Then + If InStr(sDefault, "inactive") Then + hOption._IsActive = False + Else + If hOption._IsRange Then + hOption._Value = CFloat(Mid(sDefault, 2, -1)) + Else + hOption._Value = Mid(sDefault, 2, -1) + Endif + Endif + Endif + $aOptionsNames.Add(hOption.Name) + $aOptions.Add(hOption) + '$Options._Add(hOption) + Continue + Endif + + Next + +End + +Private Function GetUnit(sValue As String) As String + + Dim i As Integer + Dim s, sRet As String + + If Not sValue Then Return + If IsLetter(Left(sValue)) Then Return + For i = 1 To Len(sValue) + s = Mid(sValue, i, 1) + If (Asc(s) >= 97 And Asc(s) <= 122) Or If (s = "%") Then + sRet &= s + Endif + Next + If Len(sRet) > 3 Then sRet = "" + Return sRet + +End + + +''Return if the current scanner is available +Public Function IsAvailable() As Boolean + + Dim s As String + + Shell "scanimage " & $sDeviceName & " -n 2>&1" To s + + Return InStr(s, "Error during device I/O") = 0 + +End + +Public Function Scan() As Image + + 'Make the option Line + + Dim ss As String + Dim sOptions As String + Dim sCommand As String + 'Dim sTemp As String = Temp + Dim hImage As Image + Dim hOption As ScannerOption + Dim sCount As String + + If Not $aOptionsNames Then GetOptions + $aImgStack.Clear + + For Each hOption In $aOptions + + If hOption.Modified Then + + If hOption._Command Begins "--" Then + sOptions &= hOption._Command & "=" & IIf(hOption.IsRange, hOption.Value, "'" & hOption.Value & "'") & " " + Else + sOptions &= hOption._Command & " " & IIf(hOption.IsRange, hOption.Value, "'" & hOption.Value & "'") & " " + Endif + + Endif + + Next + If Not $bASync Then + $sCurImagePath = Temp + sCommand = "scanimage " & $sDeviceName & " " & sOptions & "--format=pnm > " & $sCurImagePath + If $bDebug Then Debug sCommand + Shell sCommand Wait + 'Print ss + Try hImage = Image.Load($sCurImagePath) + If Error Then + Error.Raise("Can't get the image, something goes wrong" & Error.Text) + Return + Endif + Return hImage + Else + If Me.Exist("Source") Then + If (Me["Source"].Value = Me["Source"].List[0]) Or Me["Source"].Value = "" Then + sCount = "--batch-count=1" + Endif + Endif + sCommand = Subst("scanimage &1 &2 --batch='&3' &4 --batch-print -p --format=pnm", $sDeviceName, sOptions, $stmpPath &/ "out%d.pnm", sCount) + If $bDebug Then Print sCommand + Shell sCommand For Read As "Process" + + $fProgress = 0 + Raise Begin + Endif + 'Shell "scanimage" + +End + +Public Sub Process_Error(sError As String) + + Dim fRet As Float + + If $bDebug Then + Debug "Error : " & sError + Endif + + If sError Begins "Progress:" Or If sError Ends "%" Then + fRet = Round(CFloat(Left(RTrim(Scan(Split(sError, "\r")[0], "* *")[1]), -1)) / 100, -2) + If fRet > $fProgress Then + $fProgress = fRet + Raise Progress + Endif + Return + Endif + If InStr(sError, "Scanning page") Then + Raise PageBegin + Return + Endif + + If InStr(sError, "scanimage: sane_start:") Then + Raise Error(("Scanner error : ") & Mid(sError, RInStr(sError, ":"))) + Return + Endif + +End + +Public Sub Process_Kill() + + '$aImgStack.Push($sCurImagePath) + $fProgress = 1 + Raise Finished + Raise End + +End + +Public Sub Process_Read() + + Dim s As String + + s = Read #Last, Lof(Last) + + If $bDebug Then + Debug "Read : " & s + Endif + + $aImgStack.Add(Trim(s)) + Wait 0.2 + Raise PageEnd + +End + + +Private Function Progress_Read() As Float + + Return $fProgress + +End + + + +''Return recurssively all the options available +Public Function _next() As String + + Dim s As String + + If Not $aOptionsNames Then GetOptions + If IsNull(Enum.Index) Then + Enum.Index = 0 + Else + Inc Enum.Index + Endif + If Enum.Index >= $aOptions.Count Then + Enum.Stop + Return + Endif + + s = $aOptionsNames[Enum.Index] + Return s + +End + +''Find an option from it's name (same as hScan[key]) +Public Sub Find(Key As String) As ScannerOption + + If Not $aOptionsNames Then GetOptions + Try Return $aOptions[$aOptionsNames.Find(Key)] + +End + +''Return if the given option exist +Public Sub Exist(Key As String) As Boolean + + If Not $aOptionsNames Then GetOptions + Return $aOptionsNames.Exist(Key) + +End + +''Return an Option from it's name +Public Function _get(Name As String) As ScannerOption + + If Not $aOptionsNames Then GetOptions + If Not $aOptionsNames.Exist(Name) Then + Error.Raise("Unknown option '" & Name & "' for device : " & $sName) + Return + Endif + Return $aOptions[$aOptionsNames.Find(Name)] + +End + + +''Pop and return the last images. +''Return null if not image available +Public Function Peek() As Image + + Dim hImg As Image + Dim sFile As String + If $aImgStack.Count > 0 Then + sFile = $aImgStack.Pop() + Try hImg = Image.Load(sFile) + Try Kill sFile + Return hImg + Endif + +End + +Private Function Debug_Read() As Boolean + + Return $bDebug + +End + +Private Sub Debug_Write(Value As Boolean) + + $bDebug = Value + +End + +Private Function ASync_Read() As Boolean + + Return $bASync + +End + +Private Sub ASync_Write(Value As Boolean) + + $bASync = Value + +End diff --git a/comp/src/gb.scanner/.src/ScannerOption.class b/comp/src/gb.scanner/.src/ScannerOption.class new file mode 100644 index 00000000..d92c0e8f --- /dev/null +++ b/comp/src/gb.scanner/.src/ScannerOption.class @@ -0,0 +1,126 @@ +' Gambas class file + +Export +Property Read Group As String ''Return the group option name +Property Read IsRange As Boolean ''Indicate if the option give a range between too value +Property Read MaxValue As Float ''Return the range max value +Property Read MinValue As Float ''Return the range min value +Property Value As Variant ''Return the current value of the option +Property Read List As String[] ''If the value is a string return the list of the available values +Property Read IsActive As Boolean ''Return if this option is activate +Property Read Info As String ''Return some info about the option +Property Read Name As String ''Return the option name +Property Read Unit As String ''Return the Unit if option is numbers +Property Read Steps As Integer ''Return Steps +Property Read Modified As Boolean ''Return if the current value have been modified + +Private $bModified As Boolean + +Public _Group As String +Public _IsRange As Boolean +Public _MaxValue As Float +Public _MinValue As Float +Public _Value As Variant +Public _List As String[] +Public _IsActive As Boolean = True +Public _Info As String +Public _Name As String +Public _Command As String +Public _Unit As String +Public _Steps As Float +Public _Relative As Boolean + + +Private Function Group_Read() As String + + Return _Group + +End + +Private Function IsRange_Read() As Boolean + + Return _IsRange + +End + +Private Function MaxValue_Read() As Float + + Return _MaxValue + +End + +Private Function MinValue_Read() As Float + + Return _MinValue + +End + +Private Function Value_Read() As Variant + + Return _Value + +End + +Private Sub Value_Write(Value As Variant) + Dim iFind As Integer + _Value = Value + If Me.IsRange Then + If TypeOf(Value) <> gb.Integer And If TypeOf(Value) <> gb.Float Then Error.Raise("This option require a number value") + _Value = Max(Min(Me.MaxValue, _Value), Me.MinValue) + If _Value <> Value Then Error "Warning: Option: '" & Me.Name & "' -> Value out of range... change to : " & _Value + Else + If Not _List.Exist(Value) Then + + Error "Warning: Option '" & Me.Name & "' -> Value not in the list..." + iFind = _List.Find(Value & "*", gb.Like) + If iFind > 0 Then + _Value = _List[iFind] + Error "Warning: Option '" & Me.Name & "' -> Near value found: Set to '" & _Value & "'" + Endif + Endif + Endif + $bModified = True + +End + +Private Function List_Read() As String[] + + Return _List + +End + +Private Function IsActive_Read() As Boolean + + Return _IsActive + +End + +Private Function Info_Read() As String + + Return _Info + +End + +Private Function Name_Read() As String + + Return _Name + +End + +Private Function Unit_Read() As String + + Return _Unit + +End + +Private Function Steps_Read() As Integer + + Return _Steps + +End + +Private Function Modified_Read() As Boolean + + Return $bModified + +End diff --git a/comp/src/gb.scanner/.src/Scanners.class b/comp/src/gb.scanner/.src/Scanners.class new file mode 100644 index 00000000..5b984548 --- /dev/null +++ b/comp/src/gb.scanner/.src/Scanners.class @@ -0,0 +1,168 @@ +' Gambas class file + +Export +Create Static + +Public Struct ScannerInfo + Vendor As String + Model As String + Type As String +End Struct + +Static Private colCache As New Collection + +Static Private $colScannerList As New Collection +Static Private $aScannerNames As String[] +' Static _Vendors As New String[] +' Static _Models As New String[] +' Static _Types As New String[] + +Static hProcess As Process +Static Private $ret As String +Static Private $IsInit As Boolean +Event Found +Static Public Sub _init() + + If Not System.Exist("scanimage") Then + Error.Raise("Scanner CLI tool is not detected. Please Install the Sane package") + Endif + ' GetList(True) + '$IsInit = True +End + + + +Public Sub Search(Optional bWait As Boolean) + + Dim sRet As String + + If hProcess Then Return + $colScannerList.Clear + $aScannerNames = Null + If Not bWait Then + $ret = "" + hProcess = Exec ["scanimage", "--formatted-device-list", "%d|%v|%m|%t%n"] For Read As "Process" + Else + Exec ["scanimage", "--formatted-device-list", "%d|%v|%m|%t%n"] To sRet + FillList(sRet) + Endif + +End + +Public Function _GetInfo(sDeviceName As String) As ScannerInfo + + If Not $aScannerNames Then Search(True) + + If $colScannerList.Exist(sDeviceName) Then + Return $colScannerList[sDeviceName] + Else + 'If Not hProcess Then + 'FillList(GetList(False)) + Search(True) + Return $colScannerList[sDeviceName] + 'Endif + Endif + +End + +Private Sub FillList(sRet As String) + + Dim s As String + Dim ars As New String[] + Dim hScanner As ScannerInfo + + + + $colScannerList.Clear + $aScannerNames = New String[] + For Each s In Split(sRet, "\n") + If Not s Then Break + ars = Split(s, "|") + hScanner = New ScannerInfo + + 'hScanner._Name = ars[0] + hScanner.Vendor = ars[1] + hScanner.Model = ars[2] + hScanner.Type = ars[3] + $colScannerList[ars[0]] = hScanner + $aScannerNames.Add(ars[0]) + Next + +End + + +''List all the existing devices +Public Function _next() As String + + Dim s As String + + + If Not $aScannerNames Then Search(True) + If IsNull(Enum.Index) Then + Enum.Index = 0 + Else + Inc Enum.Index + Endif + If $aScannerNames.Count = 0 Or If Enum.Index >= $aScannerNames.Count Then + Enum.Stop + Return + Endif + + s = $aScannerNames[Enum.Index] + Return s + +End + + +''Return the given device if exist or fail. +Public Function _get(sDeviceName As String) As Scanner + + Dim hScan As Scanner + + If Not $aScannerNames Then Search(True) + + If Not colCache.Exist(sDeviceName) Then + + hScan = New Scanner(sDeviceName) + colCache[sDeviceName] = hScan + Else + hScan = colCache[sDeviceName] + Endif + + Return hScan + +End + +Public Sub Process_Read() + + $ret &= Read #hProcess, Lof(hProcess) + +End + +Public Sub Process_Error(sError As String) + + Print sError + +End + + +Public Sub Process_kill() + + + If hProcess.State <> hProcess.Crashed Then + FillList($ret) + hProcess = Null + Raise Found + Endif + +End + + +Static Public Sub Close() + + Try hProcess.Kill + +End + + + diff --git a/comp/src/gb.scanner/next.png b/comp/src/gb.scanner/next.png new file mode 100644 index 0000000000000000000000000000000000000000..341372937c91fd3bf5b853e66944fee4a2705569 GIT binary patch literal 1304 zcmV+z1?T#SP)o3s;=zk2FVkqE9)x6MkSP!*48z0$-Jgt^#buxo`~1Kd(T&Vx6goE8 z){bs#e{1)6=x>hc)^*#D@1^O#&+B=fex72Q<^$6-U*#!{M+mVWxLi|HBcBG~RZ9IG z3yLnQpy6L&hPg(Tb4Ds7C^_x0n)kB_?! zILH@5h}XPc?;G{?^{YCroB$vWA3j{~@pxYL`Fxc}jvSeG8<<1B5<)y*R8;h4U0q#j zC4eW7&*)ez_AW4P+xGd|+S&rQ0h*heGr)deT{Id!U0Yk5*Z1-U02&$^X2!gR+IZCdLOPMkRLBfyIO8FS69UAs0G z6&1CoQmIpC&YWqQd)*aa+qP{RHf`E;DISkUE?l_K1o(g{AmhdndADrYa&mHVa&t>d z%lE(wKnbuO@T|2(!Z3_aQ>j#COG`_mZQBA^2V}_ZAi1U;96EH!%w#e(?d|Q~B$LTB zPzXE&6ayOo@2Y_V2M$zetv~PX?rt0(A5Q=pNCQb{^)@iODxkW$y29)Ap1pqk`uRvC z^2iCF0TRG4Fa*Sb^ooJKd-uLrP*4!Mb?a8Budna!6Dyx`!XwV(G%IaURaMnypU>Ad zFfedKDfNdFo*{cI4}p8m>K{93{x?}l=_@HIxiT~~6z%Eh`N;{N1yaDMv-11EgcGh; z0u*fBy0tMLkNY}1J3DiP$H@M;dhUz)0KzbgFKyfQheDx?v$M0>39y~O`@mgb?7!i8 z0fu3`m&s(_Zf$KnXWRDN)@`yaxep9FyGS~_S}-{Q!!X_gKI-V`Xq=dsm}b6?6F}4n zf4Ee5PT=6dgWH7=r+a#O&W((W#GVL$OtvY5zz9wqm&#cb*tc)rD;|$0WLeggfq{W2 z^Ie|=9*{lrVdwdh;fn&hckeDOEG+Em@9*!udGqF<%y)g7>~TaKB;lYrX92Kt=gwz~ zi;LSMkw|}UZ|^V6*D(n^a#kK?zU#}*zd%`8Sy5?e>9?_1jIOS(ZWeZZjQOs|$^I64 zk-Q}V&yF2C>ZYcqUJ8Xm7oFg_I?jdP1IAgT>&pU$VSG3mksdj_ zS+tHz0)}C{qqTnj>eZ`_i9{mFD!QH%5JDX5?(RM{JUkp{Vb|9j4)711RV$pG@ZZG% O0000x*$DcC3g0HA)Lq52O1K)|;UfQ$tE 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 "/" And If Left(Path) <> "~" Then + If Not Path Then + Path = Settings.DefaultDir &/ Application.Name & ".conf" + Else If Path Begins "./" Then + Path = "." & Path + Else If Path Begins "../" Then + Else + Path = Settings.DefaultDir &/ Path & ".conf" + Endif + If Left(Path) = "/" Then + For Each sElt In Split(Settings.DefaultDir, "/") + sPath &/= sElt + Try Mkdir "/" & sPath + Next + Endif + Endif + + $sTitle = Title + + $sPath = Path + + Load() + +End + +Public Sub _free() + + Try Me.Save + If Error Then Error "gb.settings: warning: unable to save settings: "; $sPath; ": "; Error.Where;; Error.Text + +End + +Public Sub Save() + + Dim aKey As New String[] + Dim cSave As Collection + Dim cSlot As Collection + Dim sKey As String + Dim hFile As File + Dim vVal As Variant + Dim sTemp As String + Dim cModify As Collection + Dim sSlot As String + + If Not $bModify Then Return + + If Not IsDir(File.Dir($sPath)) Then + Error "gb.settings: warning: unable to save settings: directory does not exist: "; File.Dir($sPath) + Return + Endif + + 'Debug $sPath + + $bModify = False + + sTemp = $sPath & "." & CStr(Application.Id) & "~" + Try Kill sTemp + + If Not $bModifyAll Then + + cSave = $cSlot + cModify = $cModify + Load + For Each cModify + sKey = cModify.Key + sSlot = GetSlot(sKey) + sKey = File.Name(sKey) + If cSave.Exist(sSlot) Then + Me[sSlot &/ sKey] = cSave[sSlot][sKey] + Else + Me[sSlot &/ sKey] = Null + Endif + Next + + Endif + + For Each cSlot In $cSlot + aKey.Add($cSlot.Key) + Next + aKey.Sort + + hFile = Open sTemp For Create + + If $sTitle Then + Print #hFile, "# "; $sTitle + Endif + + For Each sKey In aKey + + cSlot = $cSlot[sKey] + If cSlot.Count Then + Print #hFile, "[" & sKey & "]" + For Each vVal In cSlot + Print #hFile, cSlot.Key; "="; ToString(vVal) + Next + Print #hFile + Endif + Next + + Close #hFile + + Try Move sTemp Kill $sPath + +Catch + + Error.Raise("Unable to save settings: " & Error.Where & " " & Error.Text) + +End + +Private Function GetSlot(sKey As String) As String + + Dim sSlot As String + + sSlot = File.Dir(sKey) + If Not sSlot Then sSlot = "/" + If sSlot = "/" Then + sSlot = "General" + Endif + If Left(sSlot) = "/" Then sSlot = Mid$(sSlot, 2) + + Return sSlot + +End + +Public Function _get(Key As String, Optional {Default} As Variant) As Variant + + Dim hSlot As Collection + Dim sSlot As String + 'Dim dDate As Date + + sSlot = GetSlot(Key) + Key = File.Name(Key) + + hSlot = $cSlot[sSlot] + If hSlot And If hSlot.Exist(Key) Then + Return hSlot[Key] + Endif + + Return {Default} + +End + +'' Return if the specified settings key has a value + +Public Sub Exist(Key As String) As Boolean + + Dim hSlot As Collection + Dim sSlot As String + 'Dim dDate As Date + + sSlot = GetSlot(Key) + Key = File.Name(Key) + + hSlot = $cSlot[sSlot] + If hSlot And If hSlot.Exist(Key) Then Return True + +End + +Private Sub SameSetting(vOld As Variant, vNew As Variant) As Boolean + + Dim I As Integer + Dim vOldVal As Variant + Dim vNewVal As Variant + + If IsNull(vOld) And If IsNull(vNew) Then Return True + If TypeOf(vOld) <> gb.Object And If TypeOf(vNew) <> gb.Object Then Return vOld = vNew + If TypeOf(vOld) <> gb.Object Or If TypeOf(vNew) <> gb.Object Then Return False + + If Not (vOld Is Array) Then Return + If Not (vNew Is Array) Then Return + If vOld.Count <> vNew.Count Then Return + If vOld = vNew Then Return + + For I = 0 To vOld.Max + vOldVal = vOld[I] + vNewVal = vNew[I] + If TypeOf(vOldVal) = gb.Object Or If TypeOf(vNewVal) = gb.Object Then Return + If vOldVal <> vNewVal Then Return + Next + + Return True + +End + + +Public Sub _put(Value As Variant, Key As String) + + Dim hSlot As Collection + Dim sSlot As String + + sSlot = GetSlot(Key) + hSlot = $cSlot[sSlot] + If Not hSlot Then + hSlot = New Collection + $cSlot[sSlot] = hSlot + Endif + + Key = File.Name(Key) + + If Not SameSetting(hSlot[Key], Value) Then + hSlot[Key] = Value + $cModify[sSlot &/ Key] = True + $bModify = True + 'Save() + Endif + +End + + +Public Sub Clear(Optional ParentKey As String, Recursive As Boolean) + + Dim sSlot As String + Dim hSlot As Collection + Dim vVal As Variant + Dim sRemove As String + + If Not ParentKey Then + If $cSlot.Count Then + $cSlot.Clear + $bModify = True + $bModifyAll = True + Endif + Return + Endif + + sSlot = GetSlot(ParentKey &/ "g") + + If Not Recursive Then + hSlot = $cSlot[sSlot] + If hSlot Then + sRemove = sSlot + GoSub REMOVE_SLOT + Endif + Else + + For Each hSlot In $cSlot + sRemove = $cSlot.Key + If sRemove = sSlot Or If sRemove Begins sSlot & "/" Then + GoSub REMOVE_SLOT + Endif + Next + Endif + + Return + +REMOVE_SLOT: + + For Each vVal In hSlot + $cModify[sRemove &/ hSlot.Key] = True + Next + $cSlot.Remove(sRemove) + $bModify = True + Return + +End + +Private Sub ReadWindow(hWindow As Window, sKey As String) + + Dim aPos As Integer[] + Dim X, Y, W, H, S As Integer + ' Dim D As Integer + ' Dim hObserver As Observer + + aPos = Me[sKey &/ "Geometry"] + + If Not aPos Or If aPos.Count < 2 Then Return + + X = aPos[0] + Y = aPos[1] + If aPos.Count > 2 Then + W = aPos[2] + H = aPos[3] + If aPos.Count > 4 Then + S = aPos[4] + If S >= Screens.Count Then S = 0 + Endif + Else + W = hWindow.W + H = hWindow.H + Endif + + X += Screens[S].AvailableX + Y += Screens[S].AvailableY + + If hWindow.Resizable Then + W = Max(32, W) + H = Max(32, H) + If hWindow.Modal Then + hWindow.Resize(W, H) + hWindow.Center + Else + hWindow.Move(X, Y, W, H) + Endif + Else + If Not hWindow.Modal Then hWindow.Move(X, Y) + Endif + + If aPos.Count > 5 Then hWindow.Maximized = aPos[5] + +End + +Private Sub WriteWindow(hWindow As Window, sKey As String) + + Dim X, Y, W, H, S As Integer + 'Dim D As Integer + Dim aVal As Integer[] + + X = hWindow.X + Y = hWindow.Y + W = hWindow.Width + H = hWindow.Height + + If hWindow.TopLevel Then + + S = hWindow.Screen + + If S >= 0 Then + X -= Screens[S].AvailableX + Y -= Screens[S].AvailableY + Else + S = 0 + Endif + + Endif + + aVal = [X, Y, W, H, S, If(hWindow.Maximized, 1, 0)] + + ' If Not hWindow.Parent And If Component.IsLoaded("gb.desktop") Then + ' hDesktopWin = New DesktopWindow(hWindow.Id) + ' D = -1 + ' Try D = hDesktopWin.Desktop + ' If Error Then Debug Error.Text + ' Debug hWindow.Name;; "Desktop:";; D + ' If D >= 0 Then aVal.Add(D) + ' Endif + + Me[sKey &/ "Geometry"] = aVal + +End + +Private Sub GetTopLevel(hCtrl As Object) As String + + Return Object.Type(hCtrl.Window) + +End + +Public Sub Read(hObject As Object, Optional sKey As String, Optional vDefault As Variant) + + If Object.Is(hObject, "Window") Then + If Not sKey Then sKey = Object.Type(hObject) + ReadWindow(hObject, sKey) + Else + If Not sKey Then + Try sKey = GetTopLevel(hObject) &/ hObject.Name + Else If Left(sKey) <> "/" Then + Try sKey = GetTopLevel(hObject) &/ sKey + Endif + Try hObject.Settings = Me[sKey, vDefault] + Endif + +End + +Public Sub Write(hObject As Object, Optional sKey As String) + + If Object.Is(hObject, "Window") Then + If Not sKey Then sKey = Object.Type(hObject) + WriteWindow(hObject, sKey) + Else + If Not sKey Then + Try sKey = GetTopLevel(hObject) &/ hObject.Name + Else If Left(sKey) <> "/" Then + Try sKey = GetTopLevel(hObject) &/ sKey + Endif + Try Me[sKey] = hObject.Settings + Endif + +End + +Private Function Path_Read() As String + + Return $sPath + +End + +' Static Public Function Array(...) As String[] +' +' Dim aVal As New String[] +' Dim iInd As Integer +' Dim sVal As String +' Dim vVal As Variant +' +' For iInd = 0 To Param.Max +' vVal = Param[iInd] +' If IsBoolean(vVal) Then +' sVal = IIf(vVal, "1", "0") +' Else +' sVal = CStr(vVal) +' Endif +' aVal.Add(sVal) +' Next +' +' Return aVal +' +' End + +' STATIC PUBLIC FUNCTION FromString(Value AS String) AS String[] +' +' DIM aRet AS NEW String[] +' DIM iInd AS Integer +' +' aRet = Split(Value, ",", "\"") +' FOR iInd = 0 TO aRet.Max +' aRet[iInd] = Replace(aRet[iInd], "\"\"", "\"") +' NEXT +' +' RETURN aRet +' +' END +' + +Public Sub Reload() + + Load + +End + +Private Function Keys_Read() As _Settings_Keys + + _Settings_Keys._Slot = $cSlot + _Settings_Keys._Parent = "" + Return _Settings_Keys + +End + +Static Private Function DefaultDir_Read() As String + + Dim sPath As String = Application.Env["XDG_CONFIG_HOME"] + If Not sPath Then sPath = System.User.Home &/ ".config" + Return sPath &/ "gambas" & System.Version + +End + +Static Private Sub WriteValue(vVal As Variant) + + Dim sStr As String + Dim iPos As Integer + Dim aArray As Array + Dim cCol As Collection + + Select Case TypeOf(vVal) + + Case gb.Null + $sStr &= "Null" + + Case gb.Boolean + If vVal Then + $sStr &= "True" + Else + $sStr &= "False" + Endif + + Case gb.Byte, gb.Short, gb.Integer, gb.Long + $sStr &= CStr(vVal) + + Case gb.Float + sStr = CStr(vVal) + If InStr(sStr, ".") = 0 And If InStr(sStr, "E", 1, gb.IgnoreCase) = 0 Then sStr &= ".0" + $sStr &= sStr + + Case gb.Date + $sStr &= Chr$(34) & CStr(vVal) & Chr$(34) + + Case gb.String + sStr = Quote(vVal) + Do + iPos = InStr(sStr, "\\", iPos + 1) + If iPos = 0 Then Break + If Mid$(sStr, iPos + 1, 1) = "x" Then + Mid$(sStr, iPos, 4) = "\\u00" & Mid$(sStr, iPos + 1, 2) + Else + Inc iPos + Endif + Loop + $sStr &= sStr + + Case Else + If vVal Is Array Then + aArray = vVal + $sStr &= "[" + For iPos = 0 To vVal.Max + If iPos Then $sStr &= "," + WriteValue(vVal[iPos]) + Next + $sStr &= "]" + Else If vVal Is Collection Then + cCol = vVal + $sStr &= "{" + For Each vVal In cCol + If iPos Then $sStr &= "," + WriteValue(cCol.Key) + $sStr &= ":" + WriteValue(vVal) + Inc iPos + Next + $sStr &= "}" + Else + Error.Raise("Only arrays and collection can be written to a settings file") + Endif + + End Select + +End + +'' Convert a value into its settings internal string representation + +Static Public Sub ToString(Value As Variant) As String + + $sStr = "" + WriteValue(Value) + Return $sStr + +End + +Static Private Sub ConvertArray(aArray As Variant[]) As Object + + Dim iType As Integer + Dim iInd As Integer + Dim aBoolean As Boolean[] + Dim aInteger As Integer[] + Dim aFloat As Float[] + Dim aString As String[] + Dim aObject As Object[] + + iType = TypeOf(aArray[0]) + For iInd = 1 To aArray.Max + If TypeOf(aArray[iInd]) <> iType Then Return aArray + Next + + Select Case iType + Case gb.Boolean + aBoolean = aArray + Return aBoolean + Case gb.Integer + aInteger = aArray + Return aInteger + Case gb.Float + aFloat = aArray + Return aFloat + Case gb.String + aString = aArray + Return aString + Case gb.Object + aObject = aArray + Return aObject + Default + Return aArray + End Select + +End + +Static Private Sub ReadValue(Optional bNotStrict As Boolean) As Variant + + Dim sToken As String + Dim sCar As String + Dim sString As String + Dim iPos As Integer + Dim cObject As Collection + Dim sKey As String + Dim aArray As Object + Dim aVariant As Variant[] + Dim vNumber As Variant + Dim bFloat As Boolean + Dim sNumber As String + + 'sCar = ReadToken() + GoSub READ_TOKEN + + If sToken = "{" Then + GoSub READ_OBJECT + Return cObject + Else If sToken = "[" Then + GoSub READ_ARRAY + Return aArray + Else If sToken = Chr$(34) Then + GoSub READ_STRING + Return sString + Else If sToken = "-" Or If IsDigit(sToken) Then + GoSub READ_NUMBER + Return vNumber + Else If sToken == "null" Then + Return Null + Else If sToken == "true" Then + Return True + Else If sToken == "false" Then + Return False + Else If bNotStrict Then + Return $sStr + Else + Error.Raise("Incorrect token: " & Quote(sCar)) + Endif + +READ_TOKEN: + + GoSub READ_CHAR + sToken = sCar + If Not IsLetter(sCar) Then Return + + Do + GoSub GET_CHAR + If Not sCar Then Break + If Not IsLetter(sCar) Then + Dec $iPos + Break + Endif + sToken &= sCar + Loop + + Return + +GET_CHAR: + + If $iPos > Len($sStr) Then + sCar = "" + Return + Endif + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +READ_CHAR: + + Do + GoSub GET_CHAR + If Not sCar Or If sCar > " " Then Return + Loop + +READ_STRING: + + sString = "" + + Do + GoSub GET_CHAR + If Not sCar Then Error.Raise("Non terminated string") + If sCar = Chr$(34) Then Return + If sCar = "\\" Then + GoSub GET_CHAR + If Not sCar Then Error.Raise("Non terminated string") + iPos = InStr("bfrtn", sCar) + If iPos Then + sCar = Mid$("\b\f\r\t\n", iPos, 1) + ' Else If sCar = "u" Then + ' Try sCar = String.Chr$(Val("&H" & Mid$($sStr, $iPos, 4))) + ' If Not Error Then $iPos += 4 + Else + ' Keep character + Endif + Endif + sString &= sCar + Loop + +READ_OBJECT: + + cObject = New Collection + + Do + GoSub READ_CHAR + If sCar = "}" Then Return + If sCar <> Chr$(34) Then Error.Raise("String expected") + GoSub READ_STRING + sKey = sString + GoSub READ_CHAR + If sCar <> ":" Then Error.Raise("Colon expected") + cObject[sKey] = ReadValue() + GoSub READ_CHAR + If sCar = "}" Then Return + If sCar <> "," Then Error.Raise("Comma expected") + Loop + +READ_ARRAY: + + aVariant = New Variant[] + + Do + GoSub READ_CHAR + If sCar = "]" Then Break + Dec $iPos + aVariant.Add(ReadValue()) + GoSub READ_CHAR + If sCar = "]" Then Break + If sCar <> "," Then Error.Raise("Comma expected") + Loop + + If aVariant.Count = 0 Then + aArray = Null + Return + Endif + + aArray = ConvertArray(aVariant) + Return + +READ_NUMBER: + + sNumber = sCar + + Do + GoSub GET_CHAR + If Not sCar Then Break + iPos = InStr(".eE-+0123456789", sCar) + If iPos = 0 Then + Dec $iPos + Break + Endif + If iPos <= 3 Then bFloat = True + sNumber &= sCar + Loop + + If bFloat Then + Try vNumber = CFloat(sNumber) + Else + Try vNumber = CInt(sNumber) + Endif + If IsNull(vNumber) Then Error.Raise("Incorrect number") + Return + +End + +'' Return a value from its settings internal string representation + +Static Public Sub FromString(Value As String) As Variant + + Dim vVal As Variant + $sStr = Value + $iPos = 1 + vVal = ReadValue(True) + $sStr = "" + Return vVal + +End + +Private Function Count_Read() As Integer + + Dim cCol As Collection + Dim iCount As Integer + + For Each cCol In $cSlot + iCount += cCol.Count + Next + + Return iCount + +End diff --git a/comp/src/gb.settings/.src/_Settings_Keys.class b/comp/src/gb.settings/.src/_Settings_Keys.class new file mode 100644 index 00000000..29b58a1f --- /dev/null +++ b/comp/src/gb.settings/.src/_Settings_Keys.class @@ -0,0 +1,104 @@ +' Gambas class file + +''' This virtual class allows to enumerate all top-level keys of a settings file, or all keys under a specific slot. + +Export +Create Static + +'' Return the number of keys of the current parent slot, or the number of top-level slots. +Property Read Count As Integer + +'' Return all keys of the current parent slot, or all top-level keys, as a string array. +Property Read All As String[] + +Public _Slot As Collection +Public _Parent As String + +Private $aKeys As String[] + +'' Return a virtual object used for enumerating all keys under the given ~Parent~ key of the setting file. + +Public Sub _get(Parent As String) As _Settings_Keys + + Dim hKeys As _Settings_Keys + + hKeys = New _Settings_Keys + hKeys._Slot = _Slot + hKeys._Parent = _Parent &/ Parent + Return hKeys + +End + +Private Sub GetKeys() As Boolean + + Dim vVal As Variant + Dim cCol As Collection + + If _Parent Then + Try cCol = _Slot[_Parent] + Else + cCol = _Slot + Endif + If Not cCol Then Return True + + $aKeys = New String[] + For Each vVal In cCol + If Not _Parent And If vVal.Count = 0 Then Continue + $aKeys.Add(cCol.Key) + Next + $aKeys.Sort + + 'Debug $aKeys.Join() + +End + +'' Enumerate the keys of the current parent slot, or the settings files top-level slots. + +Public Sub _next() As String + + Dim sKey As String + + If IsNull(Enum.Index) Then + If GetKeys() Then + Enum.Stop + Return + Endif + Enum.Index = 0 + Endif + + If Enum.Index >= $aKeys.Count Then + Enum.Stop + Else + sKey = $aKeys[Enum.Index] + Inc Enum.Index + Return sKey + Endif + +End + +'' Return if the current parent slot has the specific key, or if there is a top-level slot with that key. + +Public Sub Exist(Key As String) As Boolean + + If Not $aKeys Then GetKeys() + If $aKeys Then Return $aKeys.Exist(Key) + +End + +Private Function Count_Read() As Integer + + If Not $aKeys Then GetKeys() + If $aKeys Then Return $aKeys.Count + +End + +Private Function All_Read() As String[] + + If Not $aKeys Then GetKeys() + If $aKeys Then + Return $aKeys.Copy() + Else + Return New String[] + Endif + +End diff --git a/comp/src/gb.term.form/.component b/comp/src/gb.term.form/.component new file mode 100644 index 00000000..d5435a27 --- /dev/null +++ b/comp/src/gb.term.form/.component @@ -0,0 +1,8 @@ +[Component] +Key=gb.term.form +Version=3.15.90 +State=2 +Authors=Fabien Bodard (gambas.fr@gmail.com) +Needs=ImageIO +Requires=gb.term +Excludes=gb.gui,gb.gui.qt,gb.gtk3,gb.gtk,gb.qt4,gb.qt5 diff --git a/comp/src/gb.term.form/.directory b/comp/src/gb.term.form/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.term.form/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.term.form/.hidden/CHANGELOG b/comp/src/gb.term.form/.hidden/CHANGELOG new file mode 100644 index 00000000..af8761e0 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/CHANGELOG @@ -0,0 +1,4 @@ +[GB.TERM.FORM] +* OPT: Make the object placement more logical. the 0,0 pos now + is the left corner after the border. + diff --git a/comp/src/gb.term.form/.hidden/control/termbutton.png b/comp/src/gb.term.form/.hidden/control/termbutton.png new file mode 120000 index 00000000..23d989b3 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termbutton.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/button.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termcheckbox.png b/comp/src/gb.term.form/.hidden/control/termcheckbox.png new file mode 120000 index 00000000..b8fe6fbd --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termcheckbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/checkbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termframe.png b/comp/src/gb.term.form/.hidden/control/termframe.png new file mode 120000 index 00000000..0f21ed63 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termframe.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/frame.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termhbox.png b/comp/src/gb.term.form/.hidden/control/termhbox.png new file mode 120000 index 00000000..891ef5b1 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termhbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/hbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termlabel.png b/comp/src/gb.term.form/.hidden/control/termlabel.png new file mode 120000 index 00000000..4f2f9803 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termlabel.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/label.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termlistbox.png b/comp/src/gb.term.form/.hidden/control/termlistbox.png new file mode 120000 index 00000000..3b461414 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termlistbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/listbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termpanel.png b/comp/src/gb.term.form/.hidden/control/termpanel.png new file mode 120000 index 00000000..9c8883d4 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termpanel.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/panel.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termpicturebox.png b/comp/src/gb.term.form/.hidden/control/termpicturebox.png new file mode 120000 index 00000000..10a12fe3 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termpicturebox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/picturebox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termradiobutton.png b/comp/src/gb.term.form/.hidden/control/termradiobutton.png new file mode 120000 index 00000000..8a6735e3 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termradiobutton.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/radiobutton.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termscrollbar.png b/comp/src/gb.term.form/.hidden/control/termscrollbar.png new file mode 120000 index 00000000..d3308d96 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termscrollbar.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/scrollbar.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termtextbox.png b/comp/src/gb.term.form/.hidden/control/termtextbox.png new file mode 120000 index 00000000..276853b4 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termtextbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termvbox.png b/comp/src/gb.term.form/.hidden/control/termvbox.png new file mode 120000 index 00000000..50d7662d --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termvbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/vbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.icon.png b/comp/src/gb.term.form/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb57088d4d27e28a1e2efdf17950e82cf02d8bf GIT binary patch literal 11170 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 cBO2S-%+hm\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "This components allows to dialog with a VT102 terminal.\n\nIt provides a way to use common visual objects like windows, standard controls like labels, checkboxes, lists and so on... as in graphical mode.\n\nIt will also provide a standalone class with common VT100 commands to be used by more simple applications." +msgstr "Questo componente consente di dialogare con un terminale VT102.\n\nConsente di utilizzare oggetti visivi comuni come finestre, controlli standard come etichette, checkbox, liste e così via... come in modalità grafica.\n\nFornisce anche una classe standalone con comandi comuni VT100 da utilizzare con applicazioni più semplici." + diff --git a/comp/src/gb.term.form/.lang/nl.po b/comp/src/gb.term.form/.lang/nl.po new file mode 100644 index 00000000..f4642445 --- /dev/null +++ b/comp/src/gb.term.form/.lang/nl.po @@ -0,0 +1,15 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.term.form 3.13.0\n" +"PO-Revision-Date: 2019-05-18 13:57 UTC\n" +"Last-Translator: gbWilly \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "This components allows to dialog with a VT102 terminal.\n\nIt provides a way to use common visual objects like windows, standard controls like labels, checkboxes, lists and so on... as in graphical mode.\n\nIt will also provide a standalone class with common VT100 commands to be used by more simple applications." +msgstr "Dit component staat de dialoog toe met een VT102 terminal.\n\nHet voorziet in een manier om gebruikelijke visuele objecten zoals vensters, standaart controls zoals labels,checkboxen, en zo verder te gebruiken...zoals in grafische modus.\n\nHet voorziet ook in een alleenstaande klasse met gebruikelijke VT100 commando's voor gebruik in eenvoudigere applicaties." + diff --git a/comp/src/gb.term.form/.lang/ru.po b/comp/src/gb.term.form/.lang/ru.po new file mode 100644 index 00000000..9e4c6635 --- /dev/null +++ b/comp/src/gb.term.form/.lang/ru.po @@ -0,0 +1,40 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: comp/src/gb.term.form/.project:19 +msgid "" +"This components allows to dialog with a VT102 terminal.\n" +"\n" +"It provides a way to use common visual objects like windows, standard controls like labels, checkboxes, lists and so on... as in graphical mode.\n" +"\n" +"It will also provide a standalone class with common VT100 commands to be used by more simple applications." +msgstr "" +"Этот компонент позволяет вести диалог с терминалом VT102.\n" +"\n" +"Он позволяет использовать общие визуальные объекты, такие как окна, стандартные элементы управления, такие как метки, чекбоксы (флажки), списки и т. д. как в графическом режиме.\n" +"\n" +"Он также предоставляет отдельный класс с общими командами VT100, которые будут использоваться более простыми приложениями." + diff --git a/comp/src/gb.term.form/.lang/zh.po b/comp/src/gb.term.form/.lang/zh.po new file mode 100644 index 00000000..81c45a7c --- /dev/null +++ b/comp/src/gb.term.form/.lang/zh.po @@ -0,0 +1,29 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.term.form 3.15.90\n" +"POT-Creation-Date: 2020-11-18 18:39 UTC\n" +"PO-Revision-Date: 2020-11-18 18:39 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "" +"This components allows to dialog with a VT102 terminal.\n" +"\n" +"It provides a way to use common visual objects like windows, standard controls like labels, checkboxes, lists and so on... as in graphical mode.\n" +"\n" +"It will also provide a standalone class with common VT100 commands to be used by more simple applications." +msgstr "" +"该组件允许与VT102终端对话。\n" +"\n" +"它提供了一种使用常见可视对象(如窗口)、标准控件(如标签、复选框、列表等)的方法...,就像在图形模式中一样。\n" +"\n" +"它还提供一个独立类,其中包含供更简单的应用程序使用的通用VT100命令。" + +#: Termform1.termform:17 +msgid "Bonjour" +msgstr "" diff --git a/comp/src/gb.term.form/.project b/comp/src/gb.term.form/.project new file mode 100644 index 00000000..23db7346 --- /dev/null +++ b/comp/src/gb.term.form/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Startup=Termform1 +UseTerminal=1 +RedirectStderr=1 +Version=3.15.90 +VersionFile=1 +Component=gb.image +Component=gb.image.io +Component=gb.term +Description="This components allows to dialog with a VT102 terminal.\n\nIt provides a way to use common visual objects like windows, standard controls like labels, checkboxes, lists and so on... as in graphical mode.\n\nIt will also provide a standalone class with common VT100 commands to be used by more simple applications." +Authors="Fabien Bodard (gambas.fr@gmail.com)" +TabSize=2 +Translate=1 +Language=en_US +Type=Component +Packager=1 +DoNotTranslate=".src/Test" diff --git a/comp/src/gb.term.form/.src/Align.class b/comp/src/gb.term.form/.src/Align.class new file mode 100644 index 00000000..93d652dc --- /dev/null +++ b/comp/src/gb.term.form/.src/Align.class @@ -0,0 +1,8 @@ +' Gambas class file + +Export + +Public Enum + Normal = &H00, {Left} = &H01, {Right} = &H02, Center = &H03, + TopNormal = &H10, TopLeft = &H11, TopRight = &H12, Top = &H13, + BottomNormal = &H20, BottomLeft = &H21, BottomRight = &H22, Bottom = &H23 diff --git a/comp/src/gb.term.form/.src/Arrange.class b/comp/src/gb.term.form/.src/Arrange.class new file mode 100644 index 00000000..26c19ea6 --- /dev/null +++ b/comp/src/gb.term.form/.src/Arrange.class @@ -0,0 +1,4 @@ +' Gambas class file + +Export +Public Enum None, Vertical, Horizontal, Column, Row, Fill diff --git a/comp/src/gb.term.form/.src/Attr.class b/comp/src/gb.term.form/.src/Attr.class new file mode 100644 index 00000000..c4b9af23 --- /dev/null +++ b/comp/src/gb.term.form/.src/Attr.class @@ -0,0 +1,341 @@ +' Gambas class file + +Private Const CSI As String = "\e[" +Property Read Modified As Boolean + +Property ColorMode As Integer +Property Foreground As Integer +Property Background As Integer +Property Bold As Boolean +Property Dim As Boolean +Property Underline As Boolean +Property Reverse As Boolean +Property Blink As Boolean +Private Enum FLAG_BG, FLAG_FG, FLAG_BOLD, FLAG_DIM, FLAG_REV, FLAG_UND, FLAG_BLK +'Private Enum FLAG_BOLD, FLAG_DIM, FLAG_UND, FLAG_REV, FLAG_BLK, FLAG_FG, FLAG_BG + +Private $aModified As New Boolean[8] +Private $aValues As New Boolean[5] + +Private $iColorMode As Integer = TermColor.Default +Private $iForeground As Integer +Private $iBackGround As Integer +Private $bBold As Boolean +Private $bDim As Boolean +Private $bReverse As Boolean +Private $bUnderline As Boolean +Private $bBlink As Boolean + +Public Sub _new() + + $iForeground = TermColor.Black + $iBackGround = TermColor.White + +End + +Private Function Modified_Read() As Boolean + + Dim b As Boolean + + For Each b In $aModified + If b Then Return True + Next + +End + +Private Function Foreground_Read() As Integer + + Return $iForeground + +End + +Private Sub Foreground_Write(Value As Integer) + + If $iForeground <> Value Then $aModified[FLAG_FG] = True + $iForeground = Value + +End + +Private Function Background_Read() As Integer + + Return $iBackGround + +End + +Private Sub Background_Write(Value As Integer) + + If $iBackGround <> Value Then $aModified[FLAG_BG] = True + $iBackGround = Value + +End + +Private Function Bold_Read() As Boolean + + Return $bBold + +End + +Private Sub Bold_Write(Value As Boolean) + + If $bBold <> Value Then + $aModified[FLAG_BOLD] = True + Endif + $bBold = Value + +End + +Private Function Dim_Read() As Boolean + + Return $bDim + +End + +Private Sub Dim_Write(Value As Boolean) + + If $bDim <> Value Then $aModified[FLAG_DIM] = True + $bDim = Value + +End + +Private Function Underline_Read() As Boolean + + Return $bUnderline + +End + +Private Sub Underline_Write(Value As Boolean) + + If $bUnderline <> Value Then $aModified[FLAG_UND] = True + $bUnderline = Value + +End + +Private Function Reverse_Read() As Boolean + + Return $bReverse + +End + +Private Sub Reverse_Write(Value As Boolean) + + If $bReverse <> Value Then $aModified[FLAG_REV] = True + $bReverse = Value + +End + +Private Function Blink_Read() As Boolean + + Return $bBlink + +End + +Private Sub Blink_Write(Value As Boolean) + + If $bBlink <> Value Then $aModified[FLAG_BLK] = True + $bBlink = Value + +End + +Private Function ColorMode_Read() As Integer + + Return $iColorMode + +End + +Private Sub ColorMode_Write(Value As Integer) + + $iColorMode = Value + '$bModified = True + +End + +Public Sub Send() + + Print Me._GetString(); + +End + +Public Function _GetString(Optional Force As Boolean) As String + + Dim sValue As String + + If $aModified[FLAG_BOLD] Then sValue &= IIf($bBold, "1;", "22;") + If $aModified[FLAG_DIM] Then sValue &= IIf($bDim, "8;", "28;") + If $aModified[FLAG_BLK] Then sValue &= IIf($bBlink, "5;", "25;") + If $aModified[FLAG_UND] Then sValue &= IIf($bUnderline, "4;", "24;") + If $aModified[FLAG_REV] Then sValue &= IIf($bReverse, "7;", "27;") + + If $aModified[FLAG_FG] Or Force Then + Select Case $iColorMode + Case TermColor.Default + sValue &= (30 + Max(Min($iForeground, 8), 0)) & ";" + Case TermColor.Mode256 + sValue &= "38;5;" & Max(Min($iForeground, 255), 0) & ";" + Case TermColor.ModeRGB + sValue &= "38;2;" & Subst("&1;&2;&3;", GetRed($iForeGround), GetGreen($iForeground), GetBlue($iForeground)) + End Select + Endif + + If $aModified[FLAG_BG] Or Force Then + Select Case $iColorMode + Case TermColor.Default + sValue &= (40 + Max(Min($iBackGround, 8), 0)) & ";" + Case TermColor.Mode256 + sValue &= "48;5;" & Max(Min($iBackGround, 255), 0) & ";" + Case TermColor.ModeRGB + sValue &= "48;2;" & Subst("&1;&2;&3;", GetRed($iBackGround), GetGreen($iBackGround), GetBlue($iBackGround)) + End Select + Endif + + If Not sValue Then Return + + If sValue Ends ";" Then sValue = Left(sValue, -1) + + sValue = CSI & sValue & "m" + + $aModified = New Boolean[8] + 'Debug sValue + Return sValue + +End + +Public Function WriteToStream(hStream As Stream) + + Dim bFirst As Boolean + Dim iFlag As Integer + + For iFlag = 0 To 6 + If $aModified[iFlag] Then + If Not bFirst Then + Write #hStream, CSI + bFirst = True + Else + Write #hStream, ";" + Endif + + Select Case iFlag + Case FLAG_BOLD + Write #hStream, IIf($bBold, "1", "22") + Case FLAG_DIM + Write #hStream, IIf($bDim, "8", "28") + Case FLAG_BLK + Write #hStream, IIf($bBlink, "5", "25") + Case FLAG_UND + Write #hStream, IIf($bUnderline, "4", "24") + Case FLAG_REV + Write #hStream, IIf($bReverse, "7", "27") + + Case FLAG_FG + Select Case $iColorMode + Case TermColor.Default + Write #hStream, Str((30 + Max(Min($iForeground, 8), 0))) + Case TermColor.Mode256 + Write #hStream, ("38;5;" & Max(Min($iForeground, 255), 0)) + Case TermColor.ModeRGB + Write #hStream, ("38;2;" & Subst("&1;&2;&3", GetRed($iForeGround), GetGreen($iForeground), GetBlue($iForeground))) + End Select + + Case FLAG_BG + Select Case $iColorMode + Case TermColor.Default + Write #hStream, Str((40 + Max(Min($iBackGround, 8), 0))) + + Case TermColor.Mode256 + Write #hStream, ("48;5;" & Max(Min($iBackGround, 255), 0)) + + Case TermColor.ModeRGB + Write #hStream, ("48;2;" & Subst("&1;&2;&3", GetRed($iBackGround), GetGreen($iBackGround), GetBlue($iBackGround))) + + End Select + End Select + + Endif + + Next + If bFirst Then + Write #hStream, "m" + Endif + + $aModified = New Boolean[8] + +End + +Private Function GetRed(iValue As Integer) As Integer + + Return Lsr(iValue, 16) And 255 + +End + +Private Function GetGreen(iValue As Integer) As Integer + + Return Lsr(iValue, 8) And 255 + +End + +Private Function GetBlue(iValue As Integer) As Integer + + Return iValue And 255 + +End + +Public Sub FillFrom(iAttr As Integer) + + If BTst(iAttr, FLAG_BG) Then + Me.Background = Lsr(iAttr, 16) And 255 + Else + Me.Background = 0 + Endif + + If BTst(iAttr, FLAG_FG) Then + Me.Foreground = Lsr(iAttr, 24) And 255 + Else + Me.Foreground = 0 + Endif + + Me.Bold = BTst(iAttr, FLAG_BOLD) + Me.Dim = BTst(iAttr, FLAG_DIM) + Me.Reverse = BTst(iAttr, FLAG_REV) + Me.Underline = BTst(iAttr, FLAG_UND) + Me.Blink = BTst(iAttr, FLAG_BLK) + +End + +Public Sub Reset() + + $iForeground = -1 + $iBackGround = -1 + $bBold = False + $bDim = False + $bUnderline = False + $bReverse = False + $bBlink = False + $aModified = New Boolean[8] + Print CSI & "0m"; + +End + +Public Sub IsVoid() As Boolean + + If $iForeground >= 0 Then Return + If $iBackground >= 0 Then Return + If $bBold Or If $bDim Or If $bUnderline Or If $bReverse Or If $bBlink Then Return + Return True + +End + +Public Function GetInteger() As Integer + + Dim iAttr As Integer + + If Me.Background <> -1 Then iAttr = BSet(iAttr, FLAG_BG) + Lsl(Me.Background And 255, 16) + If Me.Foreground <> -1 Then iAttr = BSet(iAttr, FLAG_FG) + Lsl(Me.Foreground And 255, 24) + + If Me.Bold Then iAttr = BSet(iAttr, FLAG_BOLD) + If Me.Dim Then iAttr = BSet(iAttr, FLAG_DIM) + If Me.Reverse Then iAttr = BSet(iAttr, FLAG_REV) + If Me.Underline Then iAttr = BSet(iAttr, FLAG_UND) + If Me.Blink Then iAttr = BSet(iAttr, FLAG_BLK) + + Return iAttr + +End diff --git a/comp/src/gb.term.form/.src/Border.class b/comp/src/gb.term.form/.src/Border.class new file mode 100644 index 00000000..4e7c413f --- /dev/null +++ b/comp/src/gb.term.form/.src/Border.class @@ -0,0 +1,4 @@ +' Gambas class file + +Export +Public Enum None, Simple, Double \ No newline at end of file diff --git a/comp/src/gb.term.form/.src/Char.class b/comp/src/gb.term.form/.src/Char.class new file mode 100644 index 00000000..e6c22f72 --- /dev/null +++ b/comp/src/gb.term.form/.src/Char.class @@ -0,0 +1,14 @@ +' Gambas class file + +Export +Public c As Integer +Public Attr As Integer + +Static Public Sub _call(ch As String, iAttr As Integer) As Char + + Dim hChar As New Char + hChar.c = String.Code(ch) + hChar.Attr = iAttr + Return hChar + +End diff --git a/comp/src/gb.term.form/.src/Desktop.class b/comp/src/gb.term.form/.src/Desktop.class new file mode 100644 index 00000000..e7be161b --- /dev/null +++ b/comp/src/gb.term.form/.src/Desktop.class @@ -0,0 +1,45 @@ +' Gambas class file + +Export +Create Static +Property Read Width, W As Integer +Property Read Height, H As Integer +Property Background As Char + +Static Private $hChar As New Char + +Static Public Sub _init() + + Dim hAttr As New Attr + 'hAttr.ColorMode = TermColor.Mode256 + hAttr.Background = TermColor.Desktop + $hChar.Attr = hAttr.GetInteger() + $hChar.c = Asc(" ") + +End + + + +Private Function Width_Read() As Integer + + Return File.Out.Term.Width + +End + +Private Function Height_Read() As Integer + + Return File.Out.Term.Height + +End + +Private Function Background_Read() As Char + + Return $hChar + +End + +Private Function Background_Write(Value As Char) + + $hChar = Value + +End diff --git a/comp/src/gb.term.form/.src/Dialog/Message.class b/comp/src/gb.term.form/.src/Dialog/Message.class new file mode 100644 index 00000000..a5a40b54 --- /dev/null +++ b/comp/src/gb.term.form/.src/Dialog/Message.class @@ -0,0 +1,42 @@ +' Gambas class file + +Create Private + + +Static Public Sub _call(Message As String) + + Dim hLabel As TermLabel + Dim hHbox As TermHBox + Dim hButton As TermButton + Dim $hForm As TermForm + $hForm = New TermForm + $hForm.Resizable = False + hLabel = New TermLabel($hForm) + hHbox = New TermHBox($hForm) + hButton = New TermButton(hHbox) As "btnOk" + + $hForm.resize(30, 5) + $hForm.Center + $hForm.Text = "Message Box" + $hForm._Arrangement = Arrange.Vertical + hHbox._Arrangement = Arrange.Horizontal + hHbox.Invert = True + hLabel.Expand = True + $hForm.Border = Border.Simple + hLabel.Text = Message + hButton.Text = "Ok" + 'hButton._Shadow = True + hButton.Background = TermColor.Yellow + hButton.Alignment = Align.Center + $hForm.Show + +End + + +Static Public Sub btnOk_MouseUp() + + Last.window.Close + +End + + diff --git a/comp/src/gb.term.form/.src/Key.class b/comp/src/gb.term.form/.src/Key.class new file mode 100644 index 00000000..51476d70 --- /dev/null +++ b/comp/src/gb.term.form/.src/Key.class @@ -0,0 +1,65 @@ +' Gambas class file + +Export +Public Enum Normal, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BackSpace, Enter, {Return}, Tab, BackTab, Up, Down, Left, Right, Home, {End}, Insert, Delete, PageDown, PageUp, Menu +Static Property Read Shift As Boolean +Static Property Read Control As Boolean +Static Property Read Code As Integer +Static Property Read Text As String +Static Property Read Alt As Boolean +Static Private $bControl As Boolean +Static Private $bShift As Boolean +Static Private $bAlt As Boolean +Static Private $iCode As Integer +Static Private $sText As String + +Static Public Sub _SetKey(sKey As String, code As Integer, Alt As Boolean, Control As Boolean, Shift As Boolean) + + $bShift = Shift + $bControl = Control + $bALt = ALt + $iCode = Code + $sText = sKey + If TermWindows.Debug = TermWindows.DebugInput Then Error "Alt: " & alt & " Control: " & Control & " Shift: " & Shift & " Code :" & code & " Text :" & sKey + +End + +Static Private Function Shift_Read() As Boolean + + Return $bShift + +End + +Static Private Function Control_Read() As Boolean + + Return $bControl + +End + +Static Private Function Code_Read() As Integer + + Return $iCode + +End + +Static Private Function Text_Read() As String + + Return $sText + +End + +Static Private Function Alt_Read() As Boolean + + Return $bAlt + +End + +Static Public Sub _Reset() + + $bShift = False + $bControl = False + $bALt = False + $iCode = 0 + $sText = "" + +End diff --git a/comp/src/gb.term.form/.src/Mouse.class b/comp/src/gb.term.form/.src/Mouse.class new file mode 100644 index 00000000..bb5e4920 --- /dev/null +++ b/comp/src/gb.term.form/.src/Mouse.class @@ -0,0 +1,102 @@ +' Gambas class file + +Create Private + +Static Property Read {Left} As Boolean +Static Property Read {Right} As Boolean +Static Property Read {Middle} As Boolean +Static Property Read X, Col As Integer +Static Property Read Y, Row As Integer +Static Property Read ScreenX, ScreenCol As Integer +Static Property Read ScreenY, ScreenRow As Integer + +Static Private $iRow As Integer +Static Private $iCol As Integer +Static Private $iScreenRow As Integer +Static Private $iScreenCol As Integer +Static Private $bLeft As Boolean +Static Private $bMiddle As Boolean +Static Private $bRight As Boolean +Static Private $iDelta As Integer + +Static Private Function Left_Read() As Boolean + + Return $bLeft + +End + +Static Private Function Right_Read() As Boolean + + Return $bRight + +End + +Static Private Function Middle_Read() As Boolean + + Return $bMiddle + +End + +Static Public Sub Hide() + + + +End + +Static Public Sub Show() + + + +End + + +Static Public Sub _SetState(hControl As TermControl, Col As Integer, Row As Integer, Optional btnLeft As Boolean, btnMiddle As Boolean, btnRight As Boolean, Delta As Integer) + + If Not IsMissing(btnLeft) Then $bLeft = btnLeft + If Not IsMissing(btnMiddle) Then $bMiddle = btnMiddle + If Not IsMissing(btnRight) Then $bRight = btnRight + $iDelta = Delta + $iScreenRow = Row + $iScreenCol = Col + If hControl = Null Then + $iRow = $iScreenRow + $iCol = $iScreenCol + Else + $irow = $iScreenRow - hControl.ScreenY + $iCol = $iScreenCol - hControl.ScreenX + Endif + + + + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse State: Row: " & $iRow & " Col: " & $iCol & " btnLeft: " & $bLeft & " btnMiddle: " & $bMiddle & " btnRight: " & $bRight & " Delta: " & $iDelta +End + + + +Static Private Function X_Read() As Integer + + Return $iCol + +End + +Static Private Function Y_Read() As Integer + + Return $iRow + +End + +Static Private Function ScreenX_Read() As Integer + + Return $iScreenCol + +End + + + +Static Private Function ScreenY_Read() As Integer + + Return $iScreenRow + +End + diff --git a/comp/src/gb.term.form/.src/TermButton.class b/comp/src/gb.term.form/.src/TermButton.class new file mode 100644 index 00000000..be3e3016 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermButton.class @@ -0,0 +1,150 @@ +' Gambas class file + +Export +Inherits TermControl + +Class Rect + +Public Const _Properties As String = "*,Text,Shadow" +Public Const _DrawWith As String = "Button" + +Property Text As String +Property Shadow As Boolean +Property Alignment As Integer +Property Read _DefaultBackground As Integer +Property Read _DefaultForeGround As Integer +Private $sText As String +Private $iOldLen As Integer +Private $iAlignment As Integer + + +Public Sub _new() + + Me._UseFocus = True + +End + + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + + +End + +Public Sub _Render() + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim sALign As String + Dim hRectC As Rect = Me._GetClientRect() + If Not Me._NeedRender Then Return + Super._Render + + Select Case $iAlignment + Case Align.Right + sALign = String(hRectC.Width - Len($sText), " ") & $sText + Case Align.Center + sALign = String(CInt(Ceil((hRectC.Width - Len($sText)) / 2)), " ") & $sText + Case Else + sALign = $sText + End Select + If Not Me.HaveFocus Then + hAttr.Background = Me._GetBackGround() + hAttr.Foreground = Me._GetForeground() + Else + hAttr.Background = TermColor.Focus + hAttr.Foreground = TermColor.FocusText + Endif + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len(sALign)) Then Break + If i <= String.Len(sALign) Then + Me._Content[0][i - 1] = Char(Mid(sALign, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False +End + + + + +Private Function Shadow_Read() As Boolean + + Return Super._Shadow + +End + +Private Sub Shadow_Write(Value As Boolean) + + Super._Shadow = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + + +End + +Public Sub _KeyPress() + + If Key.Code = Key.Return Then Raise Click + + Super._KeyPress + +End + + + + +Private Function _DefaultBackground_Read() As Integer + + Return TermColor.ButtonBackground + +End + +Private Function _DefaultForeGround_Read() As Integer + + Return TermColor.ButtonText + +End + +Public Sub _GetBackGround() As Integer + + If Me.Background = -1 Then + Return TermColor.ButtonBackground + Else + Return Me.Background + Endif + +End + + +Public Sub _GetForeGround() As Integer + + If Me.Foreground = -1 Then + Return TermColor.ButtonText + Else + Return Me.Foreground + Endif + +End diff --git a/comp/src/gb.term.form/.src/TermCheckBox.class b/comp/src/gb.term.form/.src/TermCheckBox.class new file mode 100644 index 00000000..9781d28a --- /dev/null +++ b/comp/src/gb.term.form/.src/TermCheckBox.class @@ -0,0 +1,114 @@ +' Gambas class file + +Export +Inherits TermControl +Class Rect +Property Text As String +Property Value As Integer +Property Tristate As Boolean + +Private $sText As String +Private $iOldLen As Integer +Private $iValue As Integer +Private $bTristate As Boolean + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim sALign As String + Dim hRectC As Rect = Me._GetClientRect() + Dim sValue As String + Dim iBC As Integer + + If Not Me._NeedRender Then Return + Super._Render + + sValue = Choose($iValue + 2, "x", " ", "/") + + sALign = "[" & sValue & "] " & $sText + + iBC = Me._GetBackGround() + If Me.HaveFocus Then iBC = TermColor.Focus + hAttr.Background = iBC + hAttr.Foreground = Me.Foreground + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len(sALign)) Then Break + If i <= String.Len(sALign) Then + Me._Content[0][i - 1] = Char(Mid(sALign, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False + +End + +Private Function Value_Read() As Integer + + Return $iValue + +End + +Private Sub Value_Write(Value As Integer) + + $iValue = Max(Min(Value, 1), -1) + +End + +Private Function Tristate_Read() As Boolean + + Return $bTristate + +End + +Private Sub Tristate_Write(Value As Boolean) + + $bTristate = Value + +End + +Public Sub _MouseUp() + + ChangeValue + Super._MouseUp + Super._Click + 'Me._NeedRender = True + 'Me.Refresh +End + +Private Sub ChangeValue() + + If Me.Value < IIf($bTristate, 1, 0) Then + Me.Value = $iValue + 1 + Else + Me.Value = -1 + Endif + Me.Refresh +End + + +Public Sub _KeyPress() + + If Key.Text = " " Then + ChangeValue + Endif + Super._Click + Super._KeyPress +End diff --git a/comp/src/gb.term.form/.src/TermColor.class b/comp/src/gb.term.form/.src/TermColor.class new file mode 100644 index 00000000..a5f30b6d --- /dev/null +++ b/comp/src/gb.term.form/.src/TermColor.class @@ -0,0 +1,21 @@ +' Gambas class file + +Export +Public Const Transparent As Integer = -1 +Public Enum Black, Red, Green, Yellow, Blue, Magenta, Cyan, White 'LightGray +'Public Enum DarkGray = 8, LightRed = 9, LightGreen = 10, LightYellow = 11, LightBlue = 12, LightMagenta = 13, LightCyan = 14, White = 15 + +Public Enum {Default}, Mode256, ModeRGB + +Static Public ForeGround As Integer = Black +Static Public Background As Integer = White +Static Public ActiveWindow As Integer = Red +Static Public InactiveWindow As Integer = Magenta +Static Public Focus As Integer = Yellow +Static Public FocusText As Integer = White +Static Public {Desktop} As Integer = Black +Static Public ButtonBackground As Integer = Red +Static Public ButtonText As Integer = White +Static Public Border As Integer = Black +Static Public TextBackground As Integer = Cyan +Static Public TextForeground As Integer = Black diff --git a/comp/src/gb.term.form/.src/TermContainer.class b/comp/src/gb.term.form/.src/TermContainer.class new file mode 100644 index 00000000..cc2706b3 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermContainer.class @@ -0,0 +1,243 @@ +' Gambas class file + +Export +Create Private +Inherits TermControl +Class Rect + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*" + +Property Read Children As TermControl[] +Property Read ClientWidth, CLientW As Integer +Property Read ClientHeight, ClientH As Integer +Property Spacing As Integer +Property Padding As Integer +Property Border As Integer +Property Shadow As Boolean +Property Invert As Boolean + +Private $iSpacing As Integer '= 1 +Private $bInvert As Boolean +Private $aChildren As New Object[] + +Public _Arrangement As Integer '= Arrange.Vertical + +Public Sub _new() + + Me._AllowFocus = False + +End + + +Public Sub _Add(hCtrl As TermControl) + + $aChildren.Add(hCtrl) + +End + +Private Function Children_Read() As TermControl[] + + If Not $aChildren Then $aChildren = New TermControl[] + Return $aChildren + +End + +Public Sub _Render() + + Dim hChild As TermControl + Dim i, j As Integer + Dim hAttr As New Attr + Dim iAttr As Integer + + If Me._NeedRender Then + Super._Render + + Endif + For Each hChild In Me.Children + + hChild._Render + + Next + Me._NeedRender = False +End + +Public Sub _Arrange() + + Dim hChild As Object + Dim H As Integer + Dim iExt As Integer '= Min(5, $yBorder + $iPadding) + Dim iNExp As Integer + Dim iHExp As Integer + Dim hRectClient As Rect = Me._GetClientRect() + Dim iPadding As Integer = Me.Padding + 'If Me.tag = "*" Then Stop + Select Case _Arrangement + Case Arrange.Vertical + + For Each hChild In $aChildren + If Not hChild.Expand Then + iHExp += hChild.Height + If $iSpacing > 0 Then iHExp += $iSpacing + Else + Inc iNExp + Endif + 'If Me Is TermVBox Then Stop + If iHExp >= (hrectClient.Height - iPadding * 2) Then Break + Next + + If iNExp > 0 Then + If iHExp > 0 And If $iSpacing > 0 Then + 'iHExp -= $iSpacing + Endif + iHExp = (hrectClient.Height - iHExp) / iNExp + Endif + + H = 0 'hrectClient.Top + For Each hChild In $aChildren + 'If hChild Is TermPictureBox Then Stop + If Not hChild.Ignore Then + hChild.X = 0 'hrectClient.Left + hChild.Y = H + hChild.Width = hrectClient.Width + If hChild.Expand Then hChild.H = iHExp + H += hChild.H + $iSpacing + Endif + hChild._Arrange() + 'If H >= Me.ClientHeight Then Break + Next + + Case Arrange.Horizontal + For Each hChild In $aChildren + If Not hChild.Expand Then + iHExp += hChild.Width + If $iSpacing > 0 Then iHExp += $iSpacing + Else + Inc iNExp + Endif + 'If Me Is TermVBox Then Stop + If iHExp >= (hrectClient.Width - iPadding * 2) Then Break + Next + + If iNExp > 0 Then + + If iHExp > 0 And If $iSpacing > 0 Then + 'iHExp -= $iSpacing + Endif + iHExp = Ceil((hrectClient.Width - iHExp) / iNExp) + Endif + + If $bInvert Then + H = hrectClient.Right - 1 + Else + H = hrectClient.Width - Me.Width + Endif + For Each hChild In $aChildren + If Not hChild.Ignore Then + If $bInvert Then H -= hChild.W + hChild.Y = hrectClient.Top + + hChild.X = H + hChild.Height = hrectClient.Height + If hChild.Expand Then hChild.W = iHExp + 'f hChild.Expand Then Stop + If $bInvert Then + H -= $iSpacing + Else + H += hChild.W + $iSpacing + Endif + Endif + hChild._Arrange() + 'If H >= Me.ClientHeight Then Break + Next + End Select + +End + +' Public Sub _GetClientRect() As Rect +' +' Return $rectClient.Copy() +' +' End + +Private Function ClientWidth_Read() As Integer + + Return Me._GetClientRect().Width + 'Return $rectClient.Width + 'Return $iCLientW + +End + +Private Function ClientHeight_Read() As Integer + + Return Me._GetClientRect().Height + +End + +Private Function Spacing_Read() As Integer + + Return $iSpacing + +End + +Private Sub Spacing_Write(Value As Integer) + + $iSpacing = Value + +End + +Private Function Padding_Read() As Integer + + Return Me._Padding + +End + +Private Sub Padding_Write(Value As Integer) + + Me._Padding = Value + Me._DefineRect + +End + +Private Function Border_Read() As Integer + + Return Super._Border + +End + +Private Sub Border_Write(Value As Integer) + + Super._Border = Value + + Me._DefineRect + Me.Refresh + + +End + +Private Function Shadow_Read() As Boolean + + Return Super._Shadow + +End + +Private Sub Shadow_Write(Value As Boolean) + + Super._Shadow = True + +End + +Private Function Invert_Read() As Boolean + + Return $bInvert + +End + +Private Sub Invert_Write(Value As Boolean) + + $bInvert = Value + +End + + diff --git a/comp/src/gb.term.form/.src/TermControl.class b/comp/src/gb.term.form/.src/TermControl.class new file mode 100644 index 00000000..d4195665 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermControl.class @@ -0,0 +1,783 @@ +' Gambas class file + +Export +Create Private +Class Rect + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +Public Const _Properties As String = "X{Position},Y{Position},Width{Dimension},Height{Dimension},Background{Color},Foreground{Color},Ignore" +'"Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Margin,Ignore,Expand,AutoResize,Tag,Range" +Public Const _Family As String = "TermForm" + +Static Public _IdToControl As New Collection +Static Private $iLastId As Integer = 1 + +Public _Content As Char[][] +Public _NeedRender As Boolean +Public _UseFocus As Boolean +Property Read id As Integer + +Property Read Parent As TermContainer +Property Read ScreenX As Integer +Property Read ScreenY As Integer +Property Read Window As TermWindow + +Property Left, X, Column As Integer +Property Top, Y, {Line} As Integer +Property Width, W As Integer +Property Height, H As Integer +Property Background As Integer +Property Foreground As Integer +Property Tag As Variant +Property Expand As Boolean +Property _Shadow As Boolean +Property _Border As Integer +Property _Padding As Integer +Property _ColorMode As Integer +Property Visible As Boolean +Property Ignore As Boolean + +Property Read Next As TermControl +Property Read Previous As TermControl +Property Read HaveFocus As Boolean +Property Read _DefaultBackground As Integer +Property Read _DefaultForeGround As Integer + +Property Read _ClientHeight As Integer +Property Read _ClientWidth As Integer + +Private $iColorMode As Integer +Private $iBorder As Integer + +Private $bShadow As Boolean +Private $bIgnore As Boolean +Private _RecalcPos As Boolean +Private $rectControl As New Rect +Private $iId As Integer +Private $iParent As Integer +Private $iScreenX As Integer +Private $iScreenY As Integer +Private $iWindow As Integer +Private $iBackGround As Integer = -1 +Private $iForeground As Integer = -1 +Private $vTag As Variant +Private $bExpand As Boolean +Private $yBorder As Byte +Private $iPadding As Integer +Private $rectClient As Rect +Private $iVisible As Boolean = True +Private $bLockRender As Boolean +Public _AllowFocus As Boolean = True +Private $bHaveFocus As Boolean +Event MouseDown +Event MouseUp +Event MouseWheel +Event MouseMove +Event Click +Event KeyPress +Event GotFocus +Event LostFocus + +Public Sub _new(Optional hParent As TermContainer) + + $iId = $iLastId + Inc $iLastId + If hParent Then + $iParent = hParent.Id + hParent._Add(Me) + GetWindow + Else + If Me Is TermWindow Then + TermWindows._Add(Me) + $iWindow = $iId + Endif + Endif + $rectControl.Width = 5 + $rectControl.Height = 1 + _DefineRect + _IdToControl[$iId] = Me + _RecalcPos = True + _NeedRender = True + _AllowFocus = True + If hParent Then + TermWindows.SetRefreshArea(hParent._GetScreenRect()) + 'TermWindows._Render + 'hParent.Refresh + Endif + ' TermWindows._RefreshScreen + 'Me.Window.Refresh + +End + +Private Sub ResizeBuffer(Width As Integer, Height As Integer) + + Dim i As Integer + Dim aLine As Char[] + + 'Debug "Resize", Object.Type(Me) + _Content = New Char[][] + For i = 0 To Height + aLine = New Char[Width] + _Content.Add(aLine) + Next + Me._NeedRender = True + +End + +Public Sub _Render() + + Dim i, j As Integer + Dim hAttr As New Attr + Dim iAttr As Integer + Dim hRectC As Rect + Dim iShadow As Integer = IIf($bShadow, 1, 0) + Dim iBC As Integer + + If Not Me._NeedRender Then Return + 'If Me Is TermContainer Then + hRectC = $rectControl + 'Else + ' hRectC = $rectClient + 'Endif + + If Not _Content Then ResizeBuffer(hRectC.Width, hRectC.Height) + + iBC = Me._GetBackGround() + If Me.HaveFocus Then iBC = TermColor.Focus + hAttr.Background = iBC + + iAttr = hAttr.GetInteger() + + For i = 0 To _Content.Max + hAttr.Foreground = TermColor.Border + For j = 0 To _Content[0].Max + _Content[i][j] = Char(" ", iAttr) + Next + Next + + Select Case $iBorder + Case Border.Simple + hAttr.Background = iBC + + iAttr = hAttr.GetInteger() + For j = 1 To hRectC.width - 2 '_Content[0].Max - 1 + _Content[0][j] = Char(String.Chr(&h2500), iAttr) + Next + For j = 1 To hRectC.width - 2 + _Content[hRectC.Height - 1 - iShadow][j] = Char(String.Chr(&h2500), iAttr) + Next + For i = 1 To hRectC.Height - 2 - iShadow + + _Content[i][0] = Char(String.chr(&H2502), iAttr) + _Content[i][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2502), iAttr) + Next + _Content[0][0] = Char(String.chr(&H250C), iAttr) + _Content[0][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2510), iAttr) + _Content[hRectC.Height - 1 - iShadow][0] = Char(String.chr(&H2514), iAttr) + _Content[hRectC.Height - 1 - iShadow][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2518), iAttr) + + Case Border.Double + hAttr.Background = iBC + + iAttr = hAttr.GetInteger() + For j = 1 To hRectC.width - 2 '_Content[0].Max - 1 + _Content[0][j] = Char(String.Chr(&h2550), iAttr) + Next + For j = 1 To hRectC.width - 2 + _Content[hRectC.Height - 1 - iShadow][j] = Char(String.Chr(&h2550), iAttr) + Next + For i = 1 To hRectC.Height - 2 - iShadow + + _Content[i][0] = Char(String.chr(&H2551), iAttr) + _Content[i][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2551), iAttr) + Next + _Content[0][0] = Char(String.chr(&H2554), iAttr) + _Content[0][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2557), iAttr) + _Content[hRectC.Height - 1 - iShadow][0] = Char(String.chr(&H255A), iAttr) + _Content[hRectC.Height - 1 - iShadow][hRectC.Width - 1 - iShadow] = Char(String.chr(&H255D), iAttr) + Case Else + + End Select + + If $bShadow Then + ' If Me Is TermButton Then Stop + hAttr.Foreground = TermColor.Black + If IsNull(Me.Parent) Then + hAttr.Background = TermColor.Desktop + Else + hAttr.Background = Me.Parent.Background + Endif + + iAttr = hAttr.GetInteger() + For i = 1 To _Content[0].Max + + _Content[_Content.Max - 1][i] = Char(String.chr(&H2580), iAttr) + + Next + For i = 1 To _Content.Max - 2 + _Content[i][_Content[0].Max] = Char(String.chr(&H2588), iAttr) + + Next + 'hAttr.Background = If(IsNull(Me.Parent), Desktop.BackGround, Me.Parent.Background) + _Content[0][_Content[0].Max] = Char(String.chr(&H2584), iAttr) + _Content[_Content.Max - 1][0] = Char(" ", iAttr) + + Endif + +End + +Private Function id_Read() As Integer + + Return $iId + +End + +Private Function Parent_Read() As TermContainer + + Return _IdToControl[$iParent] + +End + +Private Function Left_Read() As Integer + + Return $rectControl.X + +End + +Private Sub Left_Write(Value As Integer) + + If $rectControl.Left = Value Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.X = Value + Me._DefineRect + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + _RecalcPos = True + If Not $bLockRender Then TermWindows._Render + +End + +Private Function Top_Read() As Integer + + 'If Me.tag = "--" Then Stop + + Return $rectControl.Y + +End + +Private Sub Top_Write(Value As Integer) + + If $rectControl.Top = Value Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.Y = Value + + Me._DefineRect + _RecalcPos = True + + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + If Not $bLockRender Then TermWindows._Render + +End + +Private Function Width_Read() As Integer + + Return $rectControl.Width + +End + +Private Sub Width_Write(Value As Integer) + + If $rectControl.Width = Value Then Return + If Value < 1 Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.Width = Value + + '$rectDrawing.width = Value + IIf($bShadow, 1, 0) + Me._DefineRect + ResizeBuffer($rectControl.Width, $rectControl.Height) + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + If Not $bLockRender Then TermWindows._Render 'If Me Is TermContainer Then Me._SetClientRect() + +End + +Private Function Height_Read() As Integer + + Return $rectControl.Height + +End + +Private Sub Height_Write(Value As Integer) + + If $rectControl.Height = Value Then Return + If Value < 1 Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.Height = Value + '$rectDrawing.Height = Value + IIf($bShadow, 1, 0) + Me._DefineRect + ResizeBuffer($rectControl.Width, $rectControl.Height) + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + If Not $bLockRender Then TermWindows._Render 'If Me Is TermContainer Then Me._SetClientRect() + +End + +Private Function ScreenX_Read() As Integer + + Dim hParent As TermControl + Dim iBorder As Integer + + hParent = _IdToControl[$iParent] + If hParent._Border > 0 Then iBorder = 1 + Return _IdToControl[$iParent].ScreenX + iBorder + Me.Left + +End + +Private Function ScreenY_Read() As Integer + + Dim hParent As TermControl + Dim iBorder As Integer + + hParent = _IdToControl[$iParent] + If hParent._Border > 0 Then iBorder = 1 + Return hParent.ScreenY + iBorder + Me.Top + +End + +Private Sub GetWindow() + + Dim hCont As Object = Me.Parent + + Do + If hCont.Parent = Null Then + $iWindow = hCont.Id + Break + Endif + hCont = hCont.Parent + Loop + +End + +Private Sub ComputeScreenXY() + + Dim hC As TermControl = Me.Parent + + If Me Is TermLabel Then Stop + If hC Then + $iScreenX = hC.ScreenX + $rectControl.Left + $iScreenY = hC.ScreenY + $rectControl.Top + Else + $iScreenX = $rectControl.Left + $iScreenY = $rectControl.Top + Endif + _RecalcPos = False + +End + +Private Function Window_Read() As TermWindow + + Return _IdToControl[$iWindow] + +End + +Public Function _GetScreenClientRect() As Rect + + Dim iCorner As Integer = IIf($iBorder > 0, 1, 0) + $iPadding + Dim iSize As Integer = iCorner * 2 + IIf($bShadow, 1, 0) + + Return rect(Me.ScreenX + iCorner, Me.ScreenY + iCorner, $rectControl.Width - iSize, $rectControl.Height - iSize) + +End + +Public Function _GetScreenRect() As Rect + + Return Rect(Me.ScreenX, Me.ScreenY, $rectControl.width, $rectControl.Height) + +End + +Public Function _GetRect() As Rect + + Return $rectControl.Copy() + +End + +Private Function Background_Read() As Integer + + Return $iBackGround + +End + +Private Sub Background_Write(Value As Integer) + + $iBackGround = Value + Me.Refresh + +End + +Private Function Foreground_Read() As Integer + + Return $iForeGround + +End + +Private Sub Foreground_Write(Value As Integer) + + $iForeground = Value + Me.Refresh + +End + +Public Sub _GetChar(c As Integer, l As Integer) As Char + 'If Me.Tag = "#" Then Stop + + Return _Content[l - Me.ScreenY][c - Me.ScreenX] + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + +End + +Public Sub _Arrange() + +End + +Public Sub Move(Left As Integer, Top As Integer, Optional Width As Integer, Height As Integer) + + $bLockRender = True + Me.Left = Left + Me.Top = Top + If Not IsMissing(Width) Then Me.Width = Width + If Not IsMissing(Height) Then Me.Height = Height + $bLockRender = False + TermWindows._Render + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + Dim hParent As TermContainer + $bExpand = Value + hParent = Me.Parent + If hParent Then + TermWindows.SetRefreshArea(hParent._GetScreenRect()) + TermWindows._Render + + Endif + +End + +Public Sub _Initialize() + + 'Here generate the content Array to the right size + 'Define the true size for the Client content + +End + +Private Function _Shadow_Read() As Boolean + + Return $bShadow + +End + +Private Sub _Shadow_Write(Value As Boolean) + + $bShadow = Value + Me._NeedRender = True + +End + +Public Sub _DefineRect() + 'If Me Is TermPictureBox Then Stop + + $rectClient = Rect($iPadding + $yBorder, $iPadding + $yBorder, $rectControl.Width - ($iPadding + $yBorder) * 2 - IIf($bShadow, 1, 0), $rectControl.Height - ($iPadding + $yBorder) * 2 - IIf($bShadow, 1, 0)) + +End + +Private Function _Border_Read() As Integer + + Return $iBorder + +End + +Private Sub _Border_Write(Value As Integer) + + $iBorder = Value + $yBorder = IIf($iBorder > Border.None, 1, 0) + Me.Refresh + +End + +Public Sub _GetClientRect() As Rect + ' If Me Is TermFrame Then Stop + '_DefineRect + + Return $rectClient.Copy() + +End + +Private Function _Padding_Read() As Integer + + Return $iPadding + +End + +Private Sub _Padding_Write(Value As Integer) + + $iPadding = Value + +End + +Public Sub _GetBackGround() As Integer + + Dim i As Integer + + If $iBackGround = -1 Then + If $iParent > 0 Then + i = Me.Parent._GetBackGround() + Return i + Else + Return TermColor.Background + Endif + Else + Return $iBackGround + Endif + +End + +Public Sub _GetForeground() As Integer + + Dim i As Integer + + If $iForeground = -1 Then + If $iParent > 0 Then + i = Me.Parent._GetForeground() + Return i + Else + Return TermColor.ForeGround + Endif + Else + Return $iForeground + Endif + +End + +Private Function _ColorMode_Read() As Integer + + Return $iColorMode + +End + +Private Sub _ColorMode_Write(Value As Integer) + + $iColorMode = Value + +End + +Public Sub _MouseDown() + + Dim hwindow As TermWindow + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Down Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + + If Not Me.Window.Active Then + Me.Window.Activate + Endif + Me.SetFocus + Raise MouseDown + +End + +Public Sub _MouseUp() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Up Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise MouseUp + +End + +Public Sub _MouseMove() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Move Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise MouseMove + +End + +Public Sub _MouseWheel() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Wheel Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise MouseWheel + +End + +Public Sub _Click() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Click Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise Click + +End + +Private Function Visible_Read() As Boolean + + Return $iVisible + +End + +Private Sub Visible_Write(Value As Boolean) + + If $iVisible <> Value Then + $iVisible = Value + TermWindows.SetRefreshArea($rectControl) + TermWindows._Render + Endif + +End + +Public Sub Show() + ' If Me Is Termform Then Stop + Me.Visible = True + +End + +Public Sub Hide() + + Me.Visible = False + +End + +Public Sub Resize(Width As Integer, Height As Integer) + + $bLockRender = True + Me.Width = Width + Me.Height = Height + $bLockRender = False + TermWindows._Render + +End + +Public Sub Refresh(Optional hScreenRect As Rect) + + Me._NeedRender = True + If hScreenRect Then + TermWindows.SetRenderArea(hScreenRect) + Else + TermWindows.SetRenderArea(Me._GetScreenRect()) + Endif + TermWindows._Render + +End + +Public Sub _KeyPress() + + Raise KeyPress + +End + +Private Function Next_Read() As TermControl + + Dim iPos As Integer + Dim hChildren As TermControl[] + + hChildren = Me.Parent.Children + ipos = hChildren.Find(Me) + If ipos = hChildren.Max Then Return Null + Return hChildren[iPos + 1] + +End + +Private Function Previous_Read() As TermControl + + Dim iPos As Integer + Dim hChildren As TermControl[] + + hChildren = Me.Parent.Children + iPos = hChildren.Find(Me) + If iPos = 0 Then Return Null + Return hChildren[iPos - 1] + +End + +Public Sub SetFocus() + + Dim hPrev As TermControl + + If Me.HaveFocus Then Return + If _AllowFocus Then + hPrev = TermControl._IdToControl[Me.Window._GetCurrentFocusId()] + hPrev._LostFocus + hPrev.Refresh + Me.Window._SetFocus(Me) + Me._GotFocus + Me.Refresh + Endif + +End + +Private Sub HaveFocus_Read() As Boolean + + Return $bHaveFocus + +End + +Public Sub _LostFocus() + + $bHaveFocus = False + + Raise LostFocus + +End + +Public Sub _GotFocus() + + $bHaveFocus = True + + Raise GotFocus + +End + +Private Function _DefaultBackground_Read() As Integer + + Return TermColor.Background + +End + +Private Function _DefaultForeGround_Read() As Integer + + Return TermColor.ForeGround + +End + +Private Function _ClientHeight_Read() As Integer + + Return $rectClient.Height + +End + +Private Function _ClientWidth_Read() As Integer + + Return $rectClient.Width + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + $bIgnore = Value + +End diff --git a/comp/src/gb.term.form/.src/TermForm.class b/comp/src/gb.term.form/.src/TermForm.class new file mode 100644 index 00000000..d78296cf --- /dev/null +++ b/comp/src/gb.term.form/.src/TermForm.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits TermWindow +Public Const _Properties As String = "*,Border{Border.None;Simple;Double}=Simple,Arrangement{Arrange.None;Vertical;Horizontal}=None,Text,Resizable=True" +Public Const _HiddenControls As String = "TermControl,TermForm,TermWindow,TermContainer" +Public Const _IsForm As Boolean = True +Public Const _IsContainer As Boolean = True +Public Const _IsMultiContainer As Boolean = False +Public Const _DrawWith As String = "Form" +Public Const _DefaultEvent As String = "Open" + + +Static Public Sub Main() + + Dim hObject As TermForm + + hObject = Application.Startup.AutoCreate() + hObject.Show + + TermWindows._Render + +End diff --git a/comp/src/gb.term.form/.src/TermFrame.class b/comp/src/gb.term.form/.src/TermFrame.class new file mode 100644 index 00000000..0693b9d7 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermFrame.class @@ -0,0 +1,57 @@ +' Gambas class file + +Export +Inherits TermPanel +Class Rect + +Property Text As String +Private $sText As String +Private $iOldLen As Integer + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim iStart As Integer + Dim hRectC As Rect = Me._GetClientRect() + Dim sText As String = " " & $sText & " " + Super._Render + If Not Me._NeedRender Then Return + + ' Select Case $iAlignment + ' Case Align.Right + ' sALign = String(hRectC.Width - Len($sText), " ") & $sText + ' Case Align.Center + iStart = CInt(Ceil((hRectC.Width - Len(sText)) / 2)) + ' Case Else + ' sALign = $sText + ' End Select + + hAttr.Background = Me._GetBackGround() + hAttr.Foreground = Me.Foreground + iAttr = hAttr.GetInteger() + + For i = iStart To Me._Content[0].count + If i - iStart >= String.Len(sText) Then Break + + Me._Content[0][i - 1] = Char(Mid(sText, i - iStart + 1, 1), iAttr) + + + Next + + Me._NeedRender = False + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End diff --git a/comp/src/gb.term.form/.src/TermHBox.class b/comp/src/gb.term.form/.src/TermHBox.class new file mode 100644 index 00000000..e03dfd03 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermHBox.class @@ -0,0 +1,9 @@ +' Gambas class file + +Export +Inherits TermContainer +Public Sub _new() + + Super._Arrangement = Arrange.Horizontal + +End diff --git a/comp/src/gb.term.form/.src/TermLabel.class b/comp/src/gb.term.form/.src/TermLabel.class new file mode 100644 index 00000000..8bff882a --- /dev/null +++ b/comp/src/gb.term.form/.src/TermLabel.class @@ -0,0 +1,73 @@ +' Gambas class file + +Export +Inherits TermControl + +Class Rect + +Public Const _Properties As String = "*,Text" +Public Const _DrawWith As String = "Label" + +Property Text As String + +Private $sText As String +Private $iOldLen As Integer + +Public Sub _new() + + Me._AllowFocus = False + +End + + + +Public Sub _Render() + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + If Not Me._NeedRender Then Return + Super._Render + If Me.HaveFocus Then + hAttr.Background = TermColor.Yellow + Else + hAttr.Background = Me._GetBackGround() + Endif + hAttr.Foreground = Me._GetForeground() + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len($sText)) Then Break + If i <= String.Len($sText) Then + Me._Content[0][i - 1] = Char(Mid($sText, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + $iOldLen = String.len($sText) + $sText = Value + TermWindows.SetRenderArea(rect(Me.ScreenX, Me.ScreenY, Max($iOldLen, String.Len($sText)), Me.Height)) + Me._NeedRender = True + TermWindows._Render +End + + +Public Sub _GetForeground() As Integer + + If Me.Foreground = -1 Then + Return TermColor.ForeGround + Else + Return Me.Foreground + Endif + +End diff --git a/comp/src/gb.term.form/.src/TermListBox.class b/comp/src/gb.term.form/.src/TermListBox.class new file mode 100644 index 00000000..024fc06b --- /dev/null +++ b/comp/src/gb.term.form/.src/TermListBox.class @@ -0,0 +1,201 @@ +' Gambas class file + +Export +Inherits TermControl + +Property List As String[] +Property Index As Integer +Property Read Count As Integer +Property Read Text As String +Private $iIndex As Integer +Private $aList As New String[] +Private $iFirstVisible As Integer +Private $iPrevIndex As Integer +Event Activate +Event Change +Class Rect + +Private Function List_Read() As String[] + + Return $aList + +End + +Private Sub List_Write(Value As String[]) + + $aList = Value + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + Dim iNewIndex As Integer + iNewIndex = Min($aList.Max, Max(0, Value)) + If iNewIndex <> $iIndex Then + $iPrevIndex = $iIndex + $iIndex = iNewIndex + EnsureVisible + Endif + +End + +Private Function Count_Read() As Integer + + Return $aList.Count + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr, iAttr2 As Integer + Dim i, il, k, ilen As Integer + Dim s As String + Dim hRectC As Rect = Me._GetClientRect() + Dim hRect As Rect = Me._GetClientRect() + Dim iCursorPos As Integer + + If Not Me._NeedRender Then Return + Super._Render + + hAttr.FillFrom(Me._Content[0][0].Attr) + + Swap hAttr.Background, hAttr.Foreground + iAttr = hAttr.GetInteger() + k = $iFirstVisible + For i = 0 To Me._Content.Max + If k > $aList.Max Then Break + s = $aList[k] + ilen = String.Len(s) + For il = 0 To IIf(k = $iIndex, hRect.width - 2, Min(hRect.width - 2, ilen)) + 'iAttr = Me._Content[i][il]. + If k = $iIndex Then Me._Content[i][il].Attr = iAttr + If il < ilen Then Me._Content[i][il].c = String.Code(String.Mid(s, il + 1, 1)) + Next + + Inc k + Next + + ilen = Me._Content[0].Max + 'Render the scrollbar + For i = 1 To Me._Content.Max - 2 + Me._Content[i][ilen].c = &H2591 + Next + Me._Content[0][ilen].c = &H25B2 + Me._Content[Me._Content.Max - 1][ilen].c = &H25BC + + hAttr.Foreground = TermColor.Black + iAttr = hAttr.GetInteger() + iCursorPos = ($iFirstVisible + hRect.height) / $aList.Count * (hRect.Height - 3) + Try Me._Content[iCursorPos][hRect.width - 1] = Char(String.Chr(&H2588), iAttr) + + Me._NeedRender = False +Fin: + +End + +Public Sub _KeyPress() + + Select Key.Code + Case Key.Up + Dec Me.Index + Raise Change + Case Key.Down + Inc Me.Index + Raise Change + Case Key.Return, Key.Enter + Raise Activate + + Default + + End Select + Super._KeyPress + +End + +Public Sub _MouseDown() + + Super._MouseDown + Me.Index = $iFirstVisible + Mouse.Row + Raise Change + 'EnsureVisible + +End + +Public Sub _MouseUp() + + Super._MouseUp + Raise Activate + Raise Click + +End + +Public Sub _MouseMove() + + Super._MouseMove + _MouseDown + +End + +Public Sub EnsureVisible() + + Dim hRect As Rect + Dim hRectSc As Rect + + If IsVisible($iIndex) Then + hRectSC = Me._GetScreenClientRect() + Me.Refresh(Rect(hRectSc.X, GetRowByIndex($iPrevIndex), hRectSc.Width, 1)) + Me.Refresh(Rect(hRectSc.X, GetRowByIndex($iIndex), hRectSc.Width, 1)) + Return + Endif + + hRect = Me._GetClientRect() + If $iFirstVisible + (hRect.Height - 1) < $iIndex Then + $iFirstVisible = $iIndex - hRect.Height + 1 + Endif + If $iIndex < $iFirstVisible Then + $iFirstVisible = $iIndex + Endif + Me.Refresh + +End + +Public Sub _put(Value As String, Index As Integer) + + Dim hRect As Rect = Me._GetScreenClientRect() + $aList[Index] = Value + If IsVisible(Index) Then + Me.Refresh(Rect(hRect.X, hRect.Y + GetRowByIndex(Index), hRect.Width, 1)) + Endif + +End + +Public Function _get(Index As Integer) As String + + Return $aList[Index] + +End + +Private Sub IsVisible(Index As Integer) As Boolean + + If Index >= $iFirstVisible And If Index < $iFirstVisible + Me._ClientHeight Then Return True + +End + +Private Function Text_Read() As String + + Return $aList[$iIndex] + +End + +Private Function GetRowByIndex(Index As Integer) As Integer + + Return Index - $iFirstVisible + +End diff --git a/comp/src/gb.term.form/.src/TermPanel.class b/comp/src/gb.term.form/.src/TermPanel.class new file mode 100644 index 00000000..47a4b240 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermPanel.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits TermContainer + +Property Arrangement As Integer + + + + +Private Function Arrangement_Read() As Integer + + Return Super._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Super._Arrangement = Value + +End + + diff --git a/comp/src/gb.term.form/.src/TermPictureBox.class b/comp/src/gb.term.form/.src/TermPictureBox.class new file mode 100644 index 00000000..bcb2f890 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermPictureBox.class @@ -0,0 +1,222 @@ +' Gambas class file + +Export +Inherits TermControl +Class Rect +Class Image +Class Color +Private $hImage As Image '= Image.Load("all.png") +Property Image As Image +Property Stretch As Integer +Property Alignment As Integer +Private $iAlignment As Integer +Private $iStretch As Integer +'None: Do nothing +'Proportional: Fit to one near border and adapt the other side +'Cut/Zoom: Adapt to the far border and cut the rest +'Fit: use the PictureBox content size +'If Picture box.None is not set the the ALignment property is set to centered +Public Enum None, Proportional, Cut, Fit +Private $aTermColor As Integer[] = [ + &H000000, &H800000, &H008000, &H808000, &H000080, &H800080, &H008080, &Hc0c0c0, + &H808080, &Hff0000, &H00ff00, &Hffff00, &H0000ff, &Hff00ff, &H00ffff, &Hffffff, + &H000000, &H00005f, &H000087, &H0000af, &H0000d7, &H0000ff, + &H005f00, &H005f5f, &H005f87, &H005faf, &H005fd7, &H005fff, + &H008700, &H00875f, &H008787, &H0087af, &H0087d7, &H0087ff, + &H00af00, &H00af5f, &H00af87, &H00afaf, &H00afd7, &H00afff, + &H00d700, &H00d75f, &H00d787, &H00d7af, &H00d7d7, &H00d7ff, + &H00ff00, &H00ff5f, &H00ff87, &H00ffaf, &H00ffd7, &H00ffff, + &H5f0000, &H5f005f, &H5f0087, &H5f00af, &H5f00d7, &H5f00ff, + &H5f5f00, &H5f5f5f, &H5f5f87, &H5f5faf, &H5f5fd7, &H5f5fff, + &H5f8700, &H5f875f, &H5f8787, &H5f87af, &H5f87d7, &H5f87ff, + &H5faf00, &H5faf5f, &H5faf87, &H5fafaf, &H5fafd7, &H5fafff, + &H5fd700, &H5fd75f, &H5fd787, &H5fd7af, &H5fd7d7, &H5fd7ff, + &H5fff00, &H5fff5f, &H5fff87, &H5fffaf, &H5fffd7, &H5fffff, + &H870000, &H87005f, &H870087, &H8700af, &H8700d7, &H8700ff, + &H875f00, &H875f5f, &H875f87, &H875faf, &H875fd7, &H875fff, + &H878700, &H87875f, &H878787, &H8787af, &H8787d7, &H8787ff, + &H87af00, &H87af5f, &H87af87, &H87afaf, &H87afd7, &H87afff, + &H87d700, &H87d75f, &H87d787, &H87d7af, &H87d7d7, &H87d7ff, + &H87ff00, &H87ff5f, &H87ff87, &H87ffaf, &H87ffd7, &H87ffff, + &Haf0000, &Haf005f, &Haf0087, &Haf00af, &Haf00d7, &Haf00ff, + &Haf5f00, &Haf5f5f, &Haf5f87, &Haf5faf, &Haf5fd7, &Haf5fff, + &Haf8700, &Haf875f, &Haf8787, &Haf87af, &Haf87d7, &Haf87ff, + &Hafaf00, &Hafaf5f, &Hafaf87, &Hafafaf, &Hafafd7, &Hafafff, + &Hafd700, &Hafd75f, &Hafd787, &Hafd7af, &Hafd7d7, &Hafd7ff, + &Hafff00, &Hafff5f, &Hafff87, &Hafffaf, &Hafffd7, &Hafffff, + &Hd70000, &Hd7005f, &Hd70087, &Hd700af, &Hd700d7, &Hd700ff, + &Hd75f00, &Hd75f5f, &Hd75f87, &Hd75faf, &Hd75fd7, &Hd75fff, + &Hd78700, &Hd7875f, &Hd78787, &Hd787af, &Hd787d7, &Hd787ff, + &Hd7af00, &Hd7af5f, &Hd7af87, &Hd7afaf, &Hd7afd7, &Hd7afff, + &Hd7d700, &Hd7d75f, &Hd7d787, &Hd7d7af, &Hd7d7d7, &Hd7d7ff, + &Hd7ff00, &Hd7ff5f, &Hd7ff87, &Hd7ffaf, &Hd7ffd7, &Hd7ffff, + &Hff0000, &Hff005f, &Hff0087, &Hff00af, &Hff00d7, &Hff00ff, + &Hff5f00, &Hff5f5f, &Hff5f87, &Hff5faf, &Hff5fd7, &Hff5fff, + &Hff8700, &Hff875f, &Hff8787, &Hff87af, &Hff87d7, &Hff87ff, + &Hffaf00, &Hffaf5f, &Hffaf87, &Hffafaf, &Hffafd7, &Hffafff, + &Hffd700, &Hffd75f, &Hffd787, &Hffd7af, &Hffd7d7, &Hffd7ff, + &Hffff00, &Hffff5f, &Hffff87, &Hffffaf, &Hffffd7, &Hffffff, + &H080808, &H121212, &H1c1c1c, &H262626, &H303030, &H3a3a3a, + &H444444, &H4e4e4e, &H585858, &H606060, &H666666, &H767676, + &H808080, &H8a8a8a, &H949494, &H9e9e9e, &Ha8a8a8, &Hb2b2b2, + &Hbcbcbc, &Hc6c6c6, &Hd0d0d0, &Hdadada, &He4e4e4, &Heeeeee] +'Private $aaImage As New Byte[][] +Static Public Sub _init() + + ' Component.Load(Component.Path &/ "gb.Image") + ' Component.Load(Component.Path &/ "gb.Image.io") + + +End + + + + + +Public Sub _new() + + ' Dim aLine As Byte[] + ' Dim yPix As Byte + ' Dim y As Integer + ' Dim x As Integer + ' + ' $hImage = $hImage.Stretch(150, 100) + ' + ' For y = 0 To $hImage.Height + ' aline = New Byte[] + ' For x = 0 To $hImage.Width + ' aLine.Add(ConvTermColor(Color.SetAlpha($hImage[x, y], 0))) + ' Next + ' $aaImage.Add(aline) + ' Next + ' + ' + ' _Render + Me._ColorMode = TermColor.Mode256 + +End + +Private Function Image_Read() As Image + + Return $hImage + +End + +Private Sub Image_Write(Value As Image) + + $hImage = Value + +End + +Public Sub _Render() + + Dim img As Image + Dim x, y As Integer + Dim fg, bg As Integer + Dim hattr As New Attr + Dim hClientRect As Rect + Dim hChar As Char + Dim hRect As Rect = Me._GetRect() + + If Not Me._NeedRender Then Return + Super._Render + If $hImage = Null Then Return + 'Return + hClientRect = Me._GetClientRect() + 'img2 = $hImage.Copy() + 'hattr.ColorMode = TermColor.Mode256 + Select Case $iStretch + Case TermPictureBox.Cut + Case TermPictureBox.Fit + img = $hImage.Stretch(hClientRect.Width, (hClientRect.Height) * 2) + + Case TermPictureBox.Proportional + Case Else + 'AKA None + img = $hImage.Copy() + End Select + + Select Case $iAlignment + Case Align.Right + Case Align.Center + Case Else + 'AKA ALign.Left + End Select + + For y = 0 To img.Height - 1 Step 2 + For x = 0 To img.Width - 1 + hChar = New Char + + fg = ConvTermColor(Color.SetAlpha(img[x, y], 0)) + bg = ConvTermColor(Color.SetAlpha(img[x, y + 1], 0)) + hattr.ColorMode = TermColor.Mode256 + hattr.Foreground = fg + hattr.Background = bg + hChar.Attr = hattr.GetInteger() + hChar.c = &H2580 + 'Try + Me._Content[hClientRect.Top + y / 2][hClientRect.Left + x] = hChar + Next + Next + + 'Print + ' For y = 0 To $aaImage.Max Step 2 + ' For x = 0 To $aaImage[0].Max + ' fg = $aaImage[y][x] + ' Try bg = $aaImage[y + 1][x] + ' hattr.Foreground = fg + ' hattr.Background = bg + ' hattr.Send() + ' Print String.Chr(&H2580); + ' Next + ' Print + ' + ' + ' + ' + ' Next + Me._NeedRender = False + +End + +Private Function ConvTermColor(iTermColor As Integer) As Integer + + Dim i As Integer + Dim fMax As Float = 1 + Dim fCur As Float + Dim iCur As Integer + + For i = 0 To $aTermColor.Max + fcur = Color.Distance($aTermColor[i], iTermColor) + If fcur < fMax Then + fMax = fcur + iCur = i + Endif + Next + Return iCur + +End + +Private Function Stretch_Read() As Integer + + Return $iStretch + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretch = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End diff --git a/comp/src/gb.term.form/.src/TermRadioButton.class b/comp/src/gb.term.form/.src/TermRadioButton.class new file mode 100644 index 00000000..6b7e7a75 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermRadioButton.class @@ -0,0 +1,112 @@ +' Gambas class file + +Export +Inherits TermControl +Class Rect +Property Text As String +Property Value As Boolean + + +Private $sText As String +Private $iOldLen As Integer +Private $bValue As Integer + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim sALign As String + Dim hRectC As Rect = Me._GetClientRect() + Dim sValue As String + Dim iBC As Integer + + If Not Me._NeedRender Then Return + Super._Render + + sValue = IIf($bValue, "*", " ") + + sALign = "(" & sValue & ") " & $sText + + iBC = Me._GetBackGround() + If Me.HaveFocus Then iBC = TermColor.Focus + hAttr.Background = iBC + hAttr.Foreground = Me.Foreground + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len(sALign)) Then Break + If i <= String.Len(sALign) Then + Me._Content[0][i - 1] = Char(Mid(sALign, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False + +End + +Private Function Value_Read() As Boolean + + Return $bValue + +End + +Private Sub Value_Write(Value As Boolean) + If $bValue = Value Then Return + If Value Then SetChecked + $bValue = Value + Me.Refresh + '$iValue = Max(Min(Value, 1), -1) +End + +Private Sub SetChecked() + + Dim hParent As TermContainer = Me.Parent + Dim hCtrl As Object + + For Each hCtrl In hParent.Children + If hCtrl Is TermRadioButton Then + If hCtrl.Value Then hCtrl.Value = False + Endif + Next + +End + + + + + +Public Sub _MouseUp() + + Me.Value = True + Super._MouseUp + Super._Click + 'Me._NeedRender = True + 'Me.Refresh +End + + + +Public Sub _KeyPress() + + If Key.Text = " " Then + Me.Value = True + Endif + Super._Click + Super._KeyPress +End diff --git a/comp/src/gb.term.form/.src/TermScrollBar.class b/comp/src/gb.term.form/.src/TermScrollBar.class new file mode 100644 index 00000000..cd18cc77 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermScrollBar.class @@ -0,0 +1,173 @@ +' Gambas class file + +Export +Inherits TermControl + +Property MinValue As Integer +Property MaxValue As Integer +Property Value As Integer + +Private $iMaxValue As Integer = 5 +Private $iMinValue As Integer +Private $iValue As Integer +Private $bInCursor As Boolean +Private $iCursorPos As Integer + +Event Change + +Public Sub _new() + + Me.MaxValue = 5 + Me.Value = 5 + +End + +Private Function MinValue_Read() As Integer + + Return $iMinValue + +End + +Private Sub MinValue_Write(Value As Integer) + + $iMinValue = Max(Value, 0) + Me.Refresh + +End + +Private Function MaxValue_Read() As Integer + + Return $iMaxValue + +End + +Private Sub MaxValue_Write(Value As Integer) + + $iMaxValue = Value + Me.Refresh + +End + +Private Function Value_Read() As Integer + + Return $iValue + +End + +Private Sub Value_Write(Value As Integer) + + $iValue = Max(Min($iMaxValue, Value), $iMinValue) + Me.Refresh + Raise Change + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim iHeight As Integer + + If Not Me._NeedRender Then Return + + Super._Render + + If Me.HaveFocus Then + hAttr.Background = TermColor.Yellow + Else + hAttr.Background = Me._GetBackGround() + Endif + + hAttr.Foreground = Me._GetForeground() + iAttr = hAttr.GetInteger() + + If Me.Height >= Me.Width Then + iHeight = Me.Height - 3 + + For i = 1 To Me._Content.Count - 3 + Me._Content[i][0] = Char(String.Chr(&H2591), iAttr) + + Next + + Me._Content[0][0] = Char(String.Chr(&H25B2), iAttr) + Me._Content[Me._Content.Count - 2][0] = Char(String.Chr(&H25BC), iAttr) + + If Me.MaxValue > 0 Then + $iCursorPos = Ceil($iValue / ($iMaxValue - $iMinValue) * iHeight) + 1 + Me._Content[$iCursorPos][0] = Char(String.Chr(&H2588), iAttr) + Endif + + Else + iHeight = Me.Width - 3 + + For i = 1 To Me._Content[0].max - 1 + + Me._Content[0][i] = Char(String.Chr(&H2591), iAttr) + + Next + + Me._Content[0][0] = Char(String.Chr(&H25C0), iAttr) + Me._Content[0][Me._Content[0].Max] = Char(String.Chr(&H25BA), iAttr) + + If Me.MaxValue > 0 Then + $iCursorPos = Ceil($iValue / ($iMaxValue - $iMinValue) * iHeight) + 1 + Me._Content[0][$iCursorPos] = Char(String.Chr(&H2588), iAttr) + Endif + + Endif + Me._NeedRender = False + +End + +Public Sub _MouseDown() + + If Me.Height > Me.Width Then + If Mouse.Row = 0 Then + Dec Me.Value + Else + If Mouse.Row = Me.Height - 1 Then + + Inc Me.Value + Else + If Mouse.Row = $iCursorPos Then $bInCursor = True + + Endif + Endif + Else + If Mouse.Col = 0 Then + Dec Me.Value + Else + If Mouse.Col = Me.Width - 1 Then + Inc Me.Value + Else + If Mouse.Col = $iCursorPos Then $bInCursor = True + Endif + Endif + + Endif + Super._MouseDown + +End + +Public Sub _MouseUp() + + $bInCursor = False + Super._MouseUp + +End + +Public Sub _MouseMove() + + Dim iHeight As Integer + + If Me.Height > Me.Width Then + + Me.Value = (Mouse.Row - 1) / (Me.Height - 3) * ($iMaxValue - $iMinValue) + Else + Me.Value = (Mouse.Col - 1) / (Me.Width - 3) * ($iMaxValue - $iMinValue) + Endif + + Super._MouseMove + +End diff --git a/comp/src/gb.term.form/.src/TermTextBox.class b/comp/src/gb.term.form/.src/TermTextBox.class new file mode 100644 index 00000000..9df4c3e6 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermTextBox.class @@ -0,0 +1,187 @@ +' Gambas class file + +Export +Inherits TermControl + +Class Rect + +Public Const _DrawWith As String = "TextBox" + +Property Text As String + +Private $sText As String +Private $iOldLen As Integer +Property Height As Integer +Private $iCursor As Integer +Private $iLenText As Integer + +Event Change +Event Activate + +Public Sub _new() + + Me._AllowFocus = True + 'Me.Height = 1 + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + + If Not Me._NeedRender Then Return + Super._Render + If Me.HaveFocus Then + hAttr.Background = TermColor.Yellow + Else + hAttr.Background = Me._GetBackGround() + Endif + hAttr.Foreground = Me._GetForeground() + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, $iLenText) Then Break + If i <= $iLenText Then + Me._Content[0][i - 1] = Char(Mid($sText, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + If Me.HaveFocus Then + hAttr.FillFrom(iAttr) + hAttr.Blink = True + If $iCursor < Me.Width Then + If $iCursor >= $iLenText Then + Me._Content[0][$iLenText] = Char(String.Chr(&H258F), hAttr.GetInteger()) + hAttr.Blink = False + Me._Content[0][$iLenText + 1] = Char(" ", hAttr.GetInteger()) + Else + Swap hAttr.Background, hAttr.Foreground + Me._Content[0][$iCursor].Attr = hAttr.GetInteger() + Endif + Endif + 'Me._Content[0][string.Len($sText)].Attr = hAttr.GetInteger() + Endif + Me._NeedRender = False + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $iOldLen = String.len($sText) + $sText = Value + $iLenText = String.Len(Value) + RefreshText + ' TermWindows.SetRenderArea(rect(Me.ScreenX, Me.ScreenY, Max($iOldLen + 1, $iLenText), Me.Height)) + ' Me._NeedRender = True + ' Termwindows._Render() + +End + +Public Sub _KeyPress() + Dim sText As String = Me.Text + Select Case Key.Code + Case Key.BackSpace + '$iOldLen = $iLenText + If $iCursor >= $iLenText Then + sText = Left(sText, -1) + Else + If $iCursor = 0 Then + + Else + Mid(sText, $iCursor, 1) = "" + Endif + Endif + '$iLenText = String.Len($sText) + + $iCursor = Max(0, Min($iLenText, $iCursor - 1)) + Me.Text = sText + Raise Change + Case Key.Left + $iCursor = Max(0, Min($iLenText, $iCursor - 1)) + RefreshText + Case Key.Right + $iCursor = Max(0, Min($iLenText, $iCursor + 1)) + RefreshText + Case Key.Home + $iCursor = 0 + RefreshText + Case Key.End + $iCursor = $iLenText + RefreshText + Case Key.Return + Raise Activate + Case Else + If $iCursor >= $iLenText Then + sText &= Key.Text + Else + If $iCursor = 0 Then + sText = Key.Text & sText + Else + sText = Left(sText, $iCursor) & Key.Text & Right(sText, - $iCursor) + Endif + Endif + Inc $iCursor + Me.Text = sText + Raise Change + End Select + Super._KeyPress + +End + +Private Sub RefreshText() + + TermWindows.SetRenderArea(rect(Me.ScreenX, Me.ScreenY, Max($iOldLen + 1, $iLenText + 1), Me.Height)) + Me._NeedRender = True + Termwindows._Render() + +End + +Private Function Height_Read() As Integer + + Return Super.Height + +End + +Private Sub Height_Write(Value As Integer) + +End + +Public Sub _GetBackGround() As Integer + + If Me.Background = -1 Then + Return TermColor.TextBackground + Else + Return Me.Background + Endif + +End + +Public Sub _GetForeGround() As Integer + + If Me.Foreground = -1 Then + Return TermColor.TextForeground + Else + Return Me.Foreground + Endif + +End + +Public Sub _MouseDown() + Dim iPos As Integer = Min(Mouse.x, String.Len($sText)) + If iPos <> $iCursor Then + + $iCursor = iPos + RefreshText + Endif + Super._MouseDown + +End diff --git a/comp/src/gb.term.form/.src/TermVBox.class b/comp/src/gb.term.form/.src/TermVBox.class new file mode 100644 index 00000000..cd221cf1 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermVBox.class @@ -0,0 +1,11 @@ +' Gambas class file + +Export +Inherits TermContainer +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + 'Super.Border = 1 + 'Super._Shadow = True + 'Super.Padding = 1 +End diff --git a/comp/src/gb.term.form/.src/TermWindow.class b/comp/src/gb.term.form/.src/TermWindow.class new file mode 100644 index 00000000..dbd0ea7c --- /dev/null +++ b/comp/src/gb.term.form/.src/TermWindow.class @@ -0,0 +1,577 @@ +' Gambas class file + +Create Static +Export +Inherits TermContainer +Class Rect +Private $sText As String +Property Arrangement As Integer +Property Text, Title As String +Property Maximized As Boolean +Property Read Active As Boolean +Property Resizable As Boolean +Property Visible As Boolean +Property Left, X, Column As Integer +Property Top, Y, {Line} As Integer +Property Width, W As Integer +Property Height, H As Integer +Property Persistent As Boolean +Private $bOpen As Boolean +Private $rectOldPos As Rect +Private $bMaximized As Boolean +Private $iInMove As Integer +Private $tmrMove As Timer +Private $iTmpCol As Integer +Private $iTmpRow As Integer +Private $bResizable As Boolean +Private $bInResize As Boolean +Private $iFocusedId As Integer +Private $bPersistent As Boolean +Property Read ScreenY As Integer +Property Read ScreenX As Integer + +Public _IsModal As Boolean + +Event Close +Event Open + +Public Sub _new() + + Me.Visible = False + $iFocusedId = Me.id + Object.Attach(Me, Me, "TermForm") + +End + +' Static Public Sub Main() +' +' Dim hObject As Object +' +' hObject = Application.Startup.AutoCreate() +' 'If Not hObject Is Report Then Return +' 'FPreview.Run(hObject) +' TermWindows._Render +' +' End + +Public Sub _Render() + + Dim i As Integer + Dim hAttr As New Attr + Dim iAttr As Integer + Dim b As Boolean = Me._NeedRender + + Super._Render + If Not b Then Return + + 'Render TitleBar + If Me.Border > 0 Then + hAttr.Background = IIf(Me.Active, TermColor.ActiveWindow, TermColor.InactiveWindow) + hAttr.Foreground = TermColor.White + hAttr.Bold = True + iAttr = hAttr.GetInteger() + + For i = 0 To Me.Width - 1 '- 2 + Me._Content[0][i] = Char(" ", iAttr) + Next + + Me._Content[0][0].c = Asc("[") + Me._Content[0][1].c = Asc("]") + + For i = 1 To Min(Me._Content[0].Max, String.Len($sText)) - 2 + Me._Content[0][i + 2].c = Asc(Mid($sText, i, 1)) + Next + Me._Content[0][Me._Content[0].Max].c = Asc("X") + Endif + Me._NeedRender = False + + If Me.Resizable Then + hAttr.Background = Me.Background + hAttr.Foreground = Me.Foreground + hAttr.Bold = False + iAttr = hAttr.GetInteger() + + If Not $bMaximized Then Me._Content[Me._Content.Max - 1][Me._Content[0].Max].c = &h25E2 + Endif + 'Catch + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + Dim bNeedLayout As Boolean + + If Len($sText) > 0 And Value = "" Then bNeedLayout = True + If $sText = "" And Value <> "" Then bNeedLayout = True + $sText = Value + Me._NeedRender = True + If bNeedLayout Then + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, Me.Width, Me.Height)) + Else + TermWindows.SetRenderArea(Rect(Me.ScreenX, Me.ScreenY, Me.Width, 1)) + Endif + TermWindows._Render + +End + +Public Sub _Arrange() + + 'Me._GetClientRect().Move(0, 1, Me.W - 1, Me.Height - 1) + Super._Arrange + +End + +Private Function Maximized_Read() As Boolean + + Return $bMaximized + +End + +Private Sub Maximized_Write(Value As Boolean) + + If $bMaximized = Value And If $bMaximized Then + $bMaximized = False + Me.Move(0, 0, Desktop.Width, Desktop.Height) + $bMaximized = True + Return + Endif + + If Value Then + $rectOldPos = Me._GetRect() + Me.Move(0, 0, Desktop.Width, Desktop.Height) + $bMaximized = Value + Else + If $bMaximized <> value Then + $bMaximized = Value + Me.Move($rectOldPos.Left, $rectOldPos.Top, $rectOldPos.Width, $rectOldPos.Height) + Endif + Endif + +End + +Public Sub _ScreenResize() + + Me.Maximized = $bMaximized + ' If $bMaximized Then + ' Me.Move(0, 0, Desktop.Width, Desktop.Height) + ' + ' Endif + +End + +Public Sub Center() + + Me.Move((Desktop.Width - Me.Width) / 2, (Desktop.Height - Me.Height) / 2, Me.Width, Me.Height) + +End + +Public Sub Close() + + Dim hCont As TermControl + + Me.Visible = False + Raise Close + + If Not Me.Persistent Then + TermWindows.Delete(Me) + Endif + +End + +Public Sub _MouseUp() + + If Mouse.ScreenY = Me.ScreenY Then + If Mouse.ScreenX = Me.ScreenX + Me.Width - 1 Then + Me.Close + Return + Endif + If Mouse.X >= 0 And If Mouse.x <= 1 Then + Me.Maximized = Not Me.Maximized + Endif + Endif + $tmrMove = Null + $iInMove = 0 + $bInResize = False + Super._MouseUp + +End + +Public Sub _MouseDown() + + If Mouse.Left + If Not Me.Active Then Termwindows._RaiseWindow(Me) + If Mouse.y = 0 Then + $iInMove = Mouse.Col + $tmrMove = New Timer As "_tmrMove" + $tmrMove.Delay = 100 + Endif + If Mouse.y = Me.Height - 1 And Mouse.x = Me.Width - 1 Then + If Me.Resizable And If Not Me.Maximized Then + $tmrMove = New Timer As "_tmrMove" + $tmrMove.Delay = 100 + $bInResize = True + Endif + Endif + Endif + Super._MouseDown + +End + +Public Sub _MouseMove() + + If $iInMove > 0 Then + $iTmpCol = Mouse.ScreenCol - $iInMove + $iTmpRow = Mouse.ScreenRow + $tmrMove.Start + 'Me.Move(Mouse.ScreenX - $iInMove, Mouse.screeny) + Endif + + If $bInResize Then + $iTmpCol = Mouse.ScreenCol - Me.ScreenX + $iTmpRow = Mouse.ScreenRow - Me.ScreenY + $tmrMove.Start + Endif + Super._MouseMove + +End + +Private Function Active_Read() As Boolean + + Return TermWindows.GetActiveWindow() = Me + +End + +Public Sub _tmrMove_Timer() + + If $bInResize Then + Me.Move(Me.Left, Me.Top, $iTmpCol, $iTmpRow) + $tmrMove.Stop + Else + Me.Move($iTmpCol, $iTmpRow) + $tmrMove.Stop + Endif + +End + +Public Sub Activate() + + TermWindows._RaiseWindow(Me) + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Top_Read() As Integer + + Return Super.Top + +End + +Private Sub Top_Write(Value As Integer) + + If $bMaximized Then Return + Super.Top = Value + +End + +Private Function Left_Read() As Integer + + Return Super.Left + +End + +Private Sub Left_Write(Value As Integer) + + If $bMaximized Then Return + Super.Left = Value + +End + +Private Function Height_Read() As Integer + + Return Super.H + +End + +Private Sub Height_Write(Value As Integer) + + If $bMaximized Then Return + If value < 3 Then Return + Super.H = Value + +End + +Private Function Width_Read() As Integer + + Return Super.W + +End + +Private Sub Width_Write(Value As Integer) + + If $bMaximized Then Return + If value < 5 Then Return + Super.Width = Value + +End + +Public Sub Move(Left As Integer, Top As Integer, Optional Width As Integer, Height As Integer) + + If $bMaximized Then Return + Super.Move(Left, Top, Width, Height) + +End + +Public Sub Resize(Width As Integer, Height As Integer) + + If $bMaximized Then Return + Super.Resize(Width, Height) + +End + +Public Sub GetNextFocusedControl(hCtrl As TermControl, Optional bFirst As Boolean) As TermControl + + Dim hNext As Object + Dim hParent As TermContainer + Dim aChildren As TermControl[] + Dim i As Integer + 'On stock le control passé en argument + Dim hFirst As TermControl = hCtrl + Dim hCont As TermContainer + + hCont = hCtrl.Parent + If Not hCont Then Return Null + 'Trouver le parent du control + aChildren = hCont.Children + 'et la position du control + i = aChildren.Find(hCtrl) + + Do + 'Il n 'y a pas d'enfant a suivre + If i = aChildren.Max Then + 'le control a t'il un parent ? + hParent = aChildren[i].Parent.Parent + If hParent Then + hNext = Null + If hParent.Children.Count > 0 Then hNext = GetNextFocusedControl(hParent.Children[hParent.Children.Find(aChildren[i].Parent)]) + If hNext Then Return hNext + Endif + i = 0 + Else + 'On passe a l'enfant suivant + If Not bFirst Then Inc i + + Endif + + hNext = aChildren[i] + 'Si on reviens au point de départ c'est qu'on a rien trouve + If Not bFirst And If hNext = hFirst Then Return Null + bFirst = False + 'si c'est un conteneur + If hNext Is TermContainer Then + 'parcourir le conteneur + hCtrl = Null + 'si le conteneur a des enfants + If hNext.Children.Count > 0 Then hCtrl = GetNextFocusedControl(hNext.Children[0], True) + 'si on a trouve on Return l'enfant + If hCtrl Then Return hCtrl + 'sinon on passe a l'enfant suivant en repartant de l'enfant courant + Continue + 'si c'est pas un conteneur + Else + 'on verifie si le control accepte le focus + 'si oui on le Return + If hNext._AllowFocus Then Return hNext + 'sinon on passe au control suivant + Continue + Endif + + Loop + +End + +Public Sub GetPrevFocusedControl(hCtrl As TermControl, Optional bFirst As Boolean) As TermControl + + Dim hNext As Object + Dim hParent As TermContainer + Dim aChildren As TermControl[] + Dim i As Integer + 'On stock le control passé en argument + Dim hFirst As TermControl = hCtrl + 'Trouver le parent du control + aChildren = hCtrl.Parent.Children + 'et la position du control + i = aChildren.Find(hCtrl) + + Do + If i = 0 Then + 'le control a t'il un parent ? + hParent = aChildren[i].Parent.Parent + If hParent Then + hNext = Null + If hParent.Children.Count > 0 Then hNext = GetNextFocusedControl(hParent.Children[hParent.Children.Find(aChildren[i].Parent)]) + If hNext Then Return hNext + Endif + i = aChildren.Max + Else + 'On passe a l'enfant suivant + If Not bFirst Then Dec i + + Endif + + hNext = aChildren[i] + 'Si on reviens au point de départ c'est qu'on a rien trouve + If Not bFirst And If hNext = hFirst Then Return Null + + bFirst = False + 'si c'est un conteneur + If hNext Is TermContainer Then + 'parcourir le conteneur + hCtrl = Null + 'si le conteneur a des enfants + If hNext.Children.Count > 0 Then hCtrl = GetNextFocusedControl(hNext.Children[hNext.Children.Max], True) + 'si on a trouve on Return l'enfant + If hCtrl Then Return hCtrl + 'sinon on passe a l'enfant suivant en repartant de l'enfant courant + Continue + 'si c'est pas un conteneur + Else + 'on verifie si le control accepte le focus + 'si oui on le Return + If hNext._AllowFocus Then Return hNext + 'sinon on passe au control suivant + Continue + Endif + + Loop + +End + +Public Sub _SetFocus(hCtrl As TermControl) + + Dim hPrev As TermControl + + hPrev = TermControl._IdToControl[$iFocusedId] + If hPrev Then + hPrev._LostFocus + hPrev.Refresh + Endif + + $iFocusedId = hCtrl.Id + +End + +Public Function _GetCurrentFocusId() As Integer + + Return $iFocusedId + +End + +Public Sub _KeyPress() + + Dim hCtrl As TermControl + + If Key.Code = Key.Tab Then + hCtrl = GetNextFocusedControl(TermControl._IdToControl[$iFocusedId]) + If hCtrl Then hCtrl.SetFocus + Return + Endif + If Key.Code = Key.BackTab Then + GetPrevFocusedControl(TermControl._IdToControl[$iFocusedId]).SetFocus + Return + Endif + Super._KeyPress + hCtrl = TermControl._IdToControl[$iFocusedId] + If hCtrl <> Me Then hCtrl._KeyPress + +End + +Private Function Persistent_Read() As Boolean + + Return $bPersistent + +End + +Private Sub Persistent_Write(Value As Boolean) + + $bPersistent = Value + +End + +Public Sub _GetClientRect() As Rect + ' If Me Is TermFrame Then Stop + '_DefineRect + + Dim hRect As Rect = Super._GetClientRect() + + If Me.Border < 1 And Me.Title <> "" Then + hRect.y += 1 + hRect.Height -= 1 + Endif + Return hRect + +End + +Private Function Visible_Read() As Boolean + + Return Super.Visible + +End + +Private Sub Visible_Write(Value As Boolean) + + If Value And If Not $bOpen Then + Raise Open + $bOpen = True + Endif + + Super.Visible = Value + +End + +Private Function ScreenY_Read() As Integer + + Return Me.Top + +End + +Private Function ScreenX_Read() As Integer + + Return Me.Left + +End + +Public Sub ShowModal() + + Me._IsModal = True + Me.Show + +End + +Private Function Arrangement_Read() As Integer + + Return Me._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Me._Arrangement = Value + +End diff --git a/comp/src/gb.term.form/.src/TermWindows.class b/comp/src/gb.term.form/.src/TermWindows.class new file mode 100644 index 00000000..a60f1f1e --- /dev/null +++ b/comp/src/gb.term.form/.src/TermWindows.class @@ -0,0 +1,715 @@ +' Gambas class file + +Export +Create Static +Class Rect +Static Private Obs As Observer +Static Private $aScreen As New Integer[] +Static $hFile As File +Static Private $RectRefreshArea As Rect +Static Private $aChildren As New TermWindow[] +Static Private $hRect As Rect +Static Private $RectRenderArea As Rect +Static Public {Debug} As Integer +Static Private $Index As Integer +Static Private $bThisIsTheEnd As Boolean +Static Private $bInRender As Boolean +Static Private $hCurControl As TermControl +Static Private $iActiveWindow As Integer +Static Public bLock As Boolean +Static Private $hStream As File + +Public Enum DebugNone, DebugInput, DebugOutput + +Static Public Sub _init() + + Dim hRect As Rect + Dim hSetting As TerminalSettings + + Component.Load("gb.geom") + + hRect = New Rect(0, 0, 3, 3) + + $hFile = Open File.In.Term.Name For Read Watch + obs = New Observer(File.In) As "Terminal" + + hSetting = File.In.Term.GetAttr() + 'hSetting.ICANON = False + 'hSetting.ECHO = False + hSetting.MakeRaw + File.In.Term.SetAttr(Term.TCSANOW, hSetting) + 'saveSetting(hSetting) + Print "\e[?1002h\e[?1006h\e[?1049h\e[?25l\e[?2h"; + Print "\e[8;30;200"; + ResizeScreen(File.Out.Term.Width, File.Out.Term.Height) + +End + +Static Private Sub ResizeScreen(Width As Integer, Height As Integer) + + $aScreen = New Integer[Height, Width] + $hRect = Rect(0, 0, Width, Height) + $RectRefreshArea = $hRect.Copy() + +End + +Public Sub _Add(hWin As TermWindow) + + Dim i As Integer + Dim hCurACtive As TermWindow + + + hCurActive = TermControl._IdToControl[$iActiveWindow] + If hCurACtive Then + hCurACtive._NeedRender = True + SetRenderArea(hCurACtive._getrect()) + Endif + $aChildren.Add(hWIn) + $iActiveWindow = hWin.id + + ' For i = 0 To $aChildren.Max - 1 + ' SetRenderArea($aChildren[i]._GetRect()) + ' $aChildren[i]._NeedRender = True + ' Next +End + +Static Public Sub _RefreshScreen() + + $RectRefreshArea = $hRect.Copy() + _Render + +End + +'Recalculate the refresh zone +Static Public Sub DoRefreshArea() + + Dim hChild As TermWindow + Dim hRect As Rect + + If Not $RectRefreshArea Then Return + DoArrange() + 'refresh desktop + 'Try + RefreshDesktop($hRect.InterSection($RectRefreshArea)) + 'Resfresh windows + For Each hChild In $aChildren + hRect = $hRect.InterSection($RectRefreshArea) + 'Try + RefreshChild(hChild, hRect) + Next + + SetRenderArea($RectRefreshArea) + $RectRefreshArea = Null + +End + +Static Private Sub RefreshDesktop(rectUpdate As Rect) + + Dim L, C As Integer + + For L = rectUpdate.Top To rectUpdate.Bottom - 1 + For C = rectUpdate.Left To rectUpdate.Right - 1 + $aScreen[l, C] = 0 + Next + Next + +End + +Static Private Sub RefreshChild(hChild As Object, hRect As Rect) + + Dim rectUpdate As Rect + Dim L, C As Integer + Dim hObj As Object + 'On définit la zone de + rectUpdate = hRect.InterSection(hChild._GetScreenRect()) + 'L 'objet est invisible alors fin + If Not hChild.Visible Then Return + 'La zone n'existe pas alors fin + If Not rectUpdate Then Return + + 'Remplissage de la zone de calepinageavec l'id de l'objet + + For L = rectUpdate.Top To rectUpdate.Bottom - 1 + For C = rectUpdate.Left To rectUpdate.Right - 1 + $aScreen[l, C] = hChild.Id + Next + Next + + 'L'objet est un conteneur ? + If hChild Is TermContainer Then + 'On applique la Client Zone + rectUpdate = hRect.InterSection(hChild._GetScreenClientRect()) + If Not IsNull(rectUpdate) Then + For Each hObj In hChild.Children + + RefreshChild(hObj, rectUpdate) + Next + Endif + Endif + +End + +' Static Private Sub RefreshChild(hChild As Object, hRect As Rect) +' +' Dim iLeft, iRight, iTop, iBottom As Integer +' +' Dim L As Integer +' Dim C As Integer +' Dim hObj As Object +' Dim rectUpdate As Rect +' Dim iMove, iResize As Integer +' 'If hChild Is Window Then Stop +' +' rectUpdate = hRect.Intersection(hChild._GetScreenRect()) +' If hChild.Visible = False Then Return +' If Not rectUpdate Then Return +' +' For L = rectUpdate.Top To rectUpdate.Bottom - 1 +' For C = rectUpdate.Left To rectUpdate.Right - 1 +' $aScreen[l, C] = hChild.Id +' Next +' Next +' +' +' If hChild Is TermContainer Then +' imove = If(hChild.Border > 0, 1, 0) + hChild.Padding +' iResize = iMove * 2 + If(hChild._Shadow, 1, 0) +' rectUpdate.Move(rectUpdate.Left + iMove, rectUpdate.Top + imove, rectUpdate.Width - iResize, rectUpdate.Height - iResize) +' For Each hObj In hChild.Children +' +' RefreshChild(hObj, rectUpdate) +' Next +' +' Endif +' +' End + +Static Public Sub Terminal_Resize() + + Dim hWin As TermWindow + + ResizeScreen(File.Out.Term.Width, File.Out.Term.Height) + For Each hWin In $aChildren + hWin._ScreenResize + Next + 'Try + SetRefreshArea(Rect(0, 0, Desktop.Width, Desktop.Height)) + _Render + +End + +Static Public Sub File_Read() + + Dim s, ss, sss As String + Dim ai As Integer[] + Dim hControl As TermControl + Dim R, C As Integer + Dim i As Integer + Dim b As Byte + Dim ab As Byte[] + Dim bnoesc As Boolean + Dim iPos As Integer = 1 + Dim iPos2 As Integer + Dim bControl As Boolean + Dim bInEscapeSec As Boolean + Dim bFound As Boolean + Dim aa As String[] + Dim bAlt As Boolean + 'Dim bControl As Boolean + Dim bShift As Boolean + + s = Read #$hFile, Lof($hFile) + bnoesc = InStr(s, "\e", 0) = -1 + 'Inc $index + + If s = "" Then Return + Do + ss = Mid(s, iPos, 1) + If ss = "\e" Then + bInEscapeSec = True + Inc ipos + Endif + + If bInEscapeSec Then + 'Is a mouse sequence ? + If Mid(s, ipos, 2) = "[<" Then + 'find the end of the sequence + i = InStr(s, "m", iPos, gb.IgnoreCase) + If i > -1 Then + sss = Mid(s, ipos + 2, i - (ipos + 1)) + ai = Split(Left(sss, -1), ";") + iPos = i + 1 + 'Mouse._setstate(x,y,Left, middle, right,delta) + ai[2] -= 1 + ai[1] -= 1 + Try hControl = TermControl._IdToControl[$aScreen[ai[2], ai[1]]] + If Error Then hControl = Null + Select Case ai[0] + 'Mouse move with left button down + Case 32 + 'Mouse._SetState(hControl, ai[1], ai[2], True,,, 0) + RaiseMouseEvent(hControl, "_MouseMove", ai[1], ai[2], True,,, 0) + 'Mouse move with right button down + Case 34 + 'Mouse._SetState(hControl, ai[1], ai[2],,, True, 0) + RaiseMouseEvent(hControl, "_MouseMove", ai[1], ai[2],,, True, 0) + 'Button left down Or up + Case 0 + 'Mouse._SetState(hControl, ai[1], ai[2], Right(s, 1) = "M",,, 0) + RaiseMouseEvent(hControl, IIf(Right(s, 1) = "M", "_MouseDown", "_MouseUp"), ai[1], ai[2], Right(s, 1) = "M",,, 0) + 'Button middle down Or up + Case 1 + 'Mouse._SetState(hControl, ai[1], ai[2],, Right(s, 1) = "M",, 0) + RaiseMouseEvent(hControl, IIf(Right(s, 1) = "M", "_MouseDown", "_MouseUp"), ai[1], ai[2],, Right(s, 1) = "M",, 0) + 'Button right down or up + Case 2 + 'Mouse._SetState(hControl, ai[1], ai[2],,, Right(s, 1) = "M", 0) + RaiseMouseEvent(hControl, IIf(Right(s, 1) = "M", "_MouseDown", "_MouseUp"), ai[1], ai[2],,, Right(s, 1) = "M", 0) + 'Mouse wheel up or down + Case 64, 65 + 'Mouse._SetState(hControl, ai[1], ai[2],,,, IIf(ai[0] = 64, -1, 1)) + RaiseMouseEvent(hControl, "_MouseWheel", ai[1], ai[2],,,, IIf(ai[0] = 64, -1, 1)) + End Select + + Endif + + bInEscapeSec = False + Continue + Endif + i = 10 + ipos2 = 10 + ' If String.InStr(S, "B") Then Stop + For Each ss In ["~", "A", "B", "C", "D", "H", "F", "P", "Q", "R", "S", "M", "Z"] + i = InStr(s, ss, ipos) + If i = 0 Then Continue + ipos2 = Min(ipos2, i) + Next + Inc ipos2 + sss = Mid(s, ipos, ipos2 - ipos) + aa = Split(sss, ";") + bInEscapeSec = False + bShift = False + bControl = False + bAlt = False + If aa.Count > 1 Then + aa[0] &= Right(aa[1], 1) + aa[1] = Left(aa[1], 1) + If aa[1] = "2" Then bShift = True + If aa[1] = "3" Then bAlt = True + If aa[1] = "5" Then bControl = True + Endif + Select Case aa[0] + Case "OA", "[A" + 'Key.Up + Key._SetKey("", Key.Up, bAlt, bControl, bShift) + bFound = True + + Case "OB", "[B" + 'Key.Down + Key._SetKey("", Key.Down, bAlt, bControl, bShift) + bFound = True + + Case "OC", "[C" + 'Key.Right + Key._SetKey("", Key.Right, bAlt, bControl, bShift) + bFound = True + + Case "OD", "[D" + 'Key.Left + Key._SetKey("", Key.Left, bAlt, bControl, bShift) + bFound = True + + 'Case + 'Key.Enter, Key.Return + ' sText = If(hScreen.NewlineMode, "\r\n", "\n") + + Case "OH", "[H" + 'Key.Home + 'sText="\e[1~" + Key._SetKey("", Key.Home, bAlt, bControl, bShift) + bFound = True + + Case "OF", "[F" + 'Key.End + Key._SetKey("", Key.End, bAlt, bControl, bShift) + bFound = True + Case "OP", "[1P" + 'Key.F1 + Key._SetKey("", Key.F1, bAlt, bControl, bShift) + bFound = True + + Case "OQ", "[1Q" + 'Key.F2 + Key._SetKey("", Key.F2, bAlt, bControl, bShift) + bFound = True + + Case "OR", "[1R" + 'Key.F3 + Key._SetKey("", Key.F3, bAlt, bControl, bShift) + bFound = True + + Case "OS", "[1S" + 'Key.F4 + Key._SetKey("", Key.F4, bAlt, bControl, bShift) + bFound = True + Case "OM" + 'Key.Return, Key.Enter + Key._SetKey("", Key.Return, bAlt, bControl, bShift) + bFound = True + + Case "[Z" + 'Key.BackTab + Key._SetKey("", Key.BackTab, bAlt, bControl, bShift) + bFound = True + + Case "[2~" + 'Key.Insert + Key._SetKey("", Key.Insert, bAlt, bControl, bShift) + bFound = True + + Case "[3~" + 'Key.Delete + Key._SetKey("", Key.Delete, bAlt, bControl, bShift) + bFound = True + + Case "[6~" + 'Key.PageDown + Key._SetKey("", Key.PageDown, bAlt, bControl, bShift) + bFound = True + + Case "[5~" + 'Key.PageUp + Key._SetKey("", Key.PageUp, bAlt, bControl, bShift) + bFound = True + + Case "[15~" + 'Key.F5 + Key._SetKey("", Key.F5, bAlt, bControl, bShift) + bFound = True + + Case "[17~" + 'Key.F6 + Key._SetKey("", Key.F6, bAlt, bControl, bShift) + bFound = True + + Case "[18~" + 'Key.F7 + Key._SetKey("", Key.F7, bAlt, bControl, bShift) + bFound = True + + Case "[19~" + 'Key.F8 + Key._SetKey("", Key.F8, bAlt, bControl, bShift) + bFound = True + + Case "[20~" + 'Key.F9 + Key._SetKey("", Key.F9, bAlt, bControl, bShift) + bFound = True + + Case "[21~" + 'Key.F10 + Key._SetKey("", Key.F10, bAlt, bControl, bShift) + bFound = True + + Case "[23~" + 'Key.F11 + Key._SetKey("", Key.F11, bAlt, bControl, bShift) + bFound = True + + Case "[24~" + 'Key.F12 + Key._SetKey("", Key.F12, bAlt, bControl, bShift) + bFound = True + + Case "[29~" + 'menu + Key._SetKey("", Key.Menu, bAlt, bControl, bShift) + bFound = True + End Select + If bFound Then + ipos = ipos2 + Endif + + bFound = False + + Else + + 'Normal Characters + + ss = Mid(s, iPos, 1) + 'Key._SetKey(s, bControl) + Select Case ss + Case "\n" + Key._SetKey(ss, Key.Return, bAlt, bControl, bShift) + Case "\t" + Key._SetKey(ss, Key.Tab, bAlt, bControl, bShift) + Case Chr(8), Chr(127) + Key._SetKey(ss, Key.BackSpace, bAlt, bControl, bShift) + Case Else + Key._SetKey(ss, 0, bAlt, bControl, bShift) + End Select + + iPos += 1 + Endif + + RaiseKeyEvent() + Key._Reset + + Loop Until iPos > Len(s) + + 'This is the end + + If $bThisIsTheEnd Then + TermControl._IdToControl = Null + hControl = Null + $hFile.Close + + Endif + +End + +Static Private Sub RaiseMouseEvent(hControl As TermControl, sEvent As String, Col As Integer, Row As Integer, Optional btnLeft As Boolean, btnMiddle As Boolean, btnRight As Boolean, Delta As Integer) + + 'Dim hControl As TermControl + + 'hControl = TermControl._IdToControl[$aScreen[Mouse.Row, Mouse.Col]] + + If hControl = Null And If $hCurControl = Null Then Return + If sEvent = "_MouseDown" Then $hCurControl = hControl + + Mouse._SetState($hCurControl, Col, Row, btnLeft, btnMiddle, btnRight, Delta) + + If sEvent = "_MouseUp" Then + If hControl = $hCurControl Then + + Object.Call($hCurControl, "_Click") + Object.Call($hCurControl, sEvent) + $hCurControl = Null + Endif + Endif + If $hCurControl Then Object.Call($hCurControl, sEvent) + +End + +Static Private Sub RaiseKeyEvent() + + Select Case Key.Code + Case Key.Menu + If Key.Alt Then + _RaiseWindow($aChildren[$aChildren.max - 1]) + Endif + Case Else + TermControl._IdToControl[$iActiveWindow]._KeyPress() + End Select + +End + +Static Public Sub _RaiseWindow(hWin As TermWindow) + + Dim hLastActive As TermWindow + Dim i As Integer + Dim bAdded As Boolean + 'If $aChildren.Count = 1 Then Return + 'hWin = $aChildren[0] + hLastActive = $aChildren[$aChildren.Max] + hLastActive._NeedRender = True + hWin._NeedRender = True + + For i = $aChildren.Max DownTo 0 + + If $aChildren[i] <> hwin And If Not $aChildren[i]._IsModal Then + $aChildren.Remove($aChildren.Find(hWIn)) + $aChildren.add(hWin, i + 1) + bAdded = True + Endif + Next + + SetRefreshArea(hWin._GetScreenRect()) + SetRenderArea(hLastActive._GetScreenRect()) + $iActiveWindow = hWin.id + + _Render + +End + +Static Public Sub _Read(sValue As String) + + Write "ok" & sValue + +End + +Static Public Sub SetRenderArea(hRect As Rect) + + If Not $RectRenderArea Then + $RectRenderArea = hRect + Return + Endif + + $RectRenderArea = $RectRenderArea.Union(hRect) + +End + +Static Public Sub SetRefreshArea(hRect As Rect) + + If Not $RectRefreshArea Then $RectRefreshArea = hRect.Copy() 'New Rect + 'If hRect.Top = 3 Then Stop + $RectRefreshArea = $RectRefreshArea.Union(hRect) + 'Error "RefreshARea : " & RectToStr($RectRefreshArea) + +End + +Static Public Sub RectToStr(hRect As Rect) As String + + Error "Top: " & hRect.Top & " Left: " & hRect.Left & " Width: " & hRect.Width & " Height: " & hRect.Height + +End + +Static Public Sub _Render() + + Dim c As Integer + Dim l As Integer + Dim hRect As Rect + Dim hAttr As New Attr + Dim hChar As Char + Dim hCont As TermControl + Dim sAttr As String + Dim sDisplay As String + Dim hOldCont As Object + Dim iOldAttr As Integer + Dim iBgAttr As Integer + Dim iBgChar As Integer + Dim bWroteBg As Boolean + Dim iId As Integer + + If bLock Then Stop + If $bInRender Then Return + + $hStream = Open String For Write + + $bInRender = True + DoRefreshArea() + RenderAll + + If Not IsNull($RectRenderArea) Then hRect = $hRect.Intersection($RectRenderArea) + + If hRect = Null Then + $bInRender = False + Return + Endif + + With Desktop.Background + iBgAttr = .Attr + iBgChar = .c + End With + + sAttr = hAttr._GetString(True) + + Write #$hStream, sAttr + + For l = hRect.Y To hRect.Bottom - 1 + 'Debug "ligne " & l + Write #$hStream, "\e[" & (l + 1) & ";" & (hRect.X + 1) & "H" + iOldAttr = 0 + bWroteBg = False + + For c = hRect.X To hRect.Right - 1 + + iId = $aScreen[l, c] + + If iId Then + + hCont = TermControl._IdToControl[iId] + If hOldCont <> hCont Then + hAttr.Reset + hAttr.ColorMode = hCont._ColorMode + Endif + hOldCont = hCont + hChar = hCont._GetChar(c, l) + If hChar.Attr <> iOldAttr Then + hAttr.FillFrom(hChar.Attr) + hAttr.WriteToStream($hStream) + 'sAttr = hAttr._GetString() + iOldAttr = hChar.Attr + Endif + 'Write #$hStream, sAttr + + Write #$hStream, String.Chr(hChar.c) + bWroteBg = False + + Else + + If Not bWroteBg Then + hAttr.FillFrom(iBgAttr) + hAttr.WriteToStream($hStream) + Endif + + Write #$hStream, String.Chr(iBgChar) + bWroteBg = True + + Endif + + Next + Next + sDisplay = Close #$hStream + If TermWindows.Debug = Me.DebugOutput Then Error Replace(Replace(sDisplay, "\e[", "\n"), "\e", "!"); + Print sDisplay; + Flush + + $RectRenderArea = Null + + $bInRender = False + +End + +Static Private Sub RenderAll() + + Dim hWin As TermWindow + + For Each hWin In $aChildren + hWin._Render + Next + +End + +Static Private Sub DoArrange() + + Dim hChild As TermWindow + + For Each hChild In $aChildren + hChild._Arrange() + Next + +End + +Static Public Sub Delete(hControl As TermControl) + + Dim hObj As Object = hControl + + TermControl._IdToControl[hObj.Id] = Null + hObj._Content = Null + If hObj Is TermContainer Then + For Each hControl In hObj.Children + Delete(hControl) + Next + 'hObj.Children.Clear + Endif + + If hObj Is TermWindow Then + $aChildren.Delete($aChildren.Find(hObj)) + If $aChildren.Count > 0 Then _RaiseWindow($aChildren[$aChildren.Max]) + Endif + + If $aChildren.Count = 0 Then + + $bThisIsTheEnd = True + + Endif + +End + +Static Public Sub GetActiveWindow() As TermWindow + + Return TermControl._IdToControl[$iActiveWindow] + +End diff --git a/comp/src/gb.term.form/.src/Test/FTest2.class b/comp/src/gb.term.form/.src/Test/FTest2.class new file mode 100644 index 00000000..c49d89a7 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/FTest2.class @@ -0,0 +1,61 @@ +' Gambas class file + +Inherits TermForm +Private aList As New String[] +Private hTim As New Timer As "Tim" +Private hList As TermListBox +Private k As Integer + Private hLbl As TermLabel + +Public Sub _new() + Dim i As Integer +'aList = ["Chien", "Chat", "Eléphant", "Hiène", "Phoque", "Canard", "Hippoppo"] + Me.Spacing = 1 + + hList = New TermListBox(Me) As "ListBox1" + hLbl = New TermLabel(Me) + + For i = 1 To 200 + aList.Add("Item" & i) + Next + 'hList._Shadow = True + hList.List = aList + Me.Move(0, 0, 60, 20) + Me._Arrangement = Arrange.Vertical + hList.Move(1, 1, 3, 3) + 'Me._Arrangement = Arrange.Fill + hList.Expand = True + Me.Border = 0 + hList.Background = termColor.Blue + Me.Center + Me.Show + 'Me.Title = "Test 1" + Me.Maximized = True + hTim.Delay = 500 + hTim.Start + + hLbl.Background = TermColor.Green + hLbl.Foreground = TermColor.Black + TermWindows.Debug = TermWindows.DebugOutput +End + + +Public Sub tim_Timer() + + 'hList[5] = k + Inc k +End + + +Public Sub ListBox1_Change() + + hLbl.Text = Last.Text + +End + +Public Sub Form_close() + + hTim.Stop + +End + diff --git a/comp/src/gb.term.form/.src/Test/Main.module b/comp/src/gb.term.form/.src/Test/Main.module new file mode 100644 index 00000000..333fad3e --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Main.module @@ -0,0 +1,136 @@ +' Gambas module file + + +Private hTimer As New Timer As "Timer" +Private hWin2 As TermWindow +Private hTermLabel As TermLabel +Private $i As Integer +Private $iBal As Integer = 1 +Private $hCurFocued As Object +'Private hWin2 As TermForm + + Public Sub Main() + + Dim hWin As New TermForm + Dim hTerCheckBox As TermRadioButton + Dim i As Integer + Dim hCont As TermVBox + Dim hCont2 As TermHBox + Dim hTermButton As TermButton + 'TermWindows.Debug = True + + 'Dim hTermLabel As New TermLabel(hWin) + 'Dim hWin2 As New Window + hTermLabel = New TermLabel(hwin) + hTimer.delay = 5 + 'hTimer.Start + + hWin.Left = 3 + hWin.Top = 1 + hWin.Height = 20 + hWin.Width = 70 + hwin.Padding = 1 + hwin.Border = Border.Simple + hTermLabel.Left = 2 + hTermLabel.Top = 2 + hTermLabel.Width = 7 + hTermLabel.tag = "*" + hTermLabel.Background = TermColor.Yellow + hTermLabel.Foreground = TermColor.Green + hTermLabel.Text = "TermLabel 1" + hWin.Text = "Window 1" + hTermLabel = New TermLabel(hWin) + hTermLabel.Width = 15 + hTermLabel.Text = "TermLabel 2" + hTermLabel.Tag = "#" + hTermLabel.SetFocus + + + hWin2 = New TermWindow + hWin2.Background = TermColor.Cyan + hWin2.Border = Border.Simple + hWin2.Width = 50 + hWin2.Spacing = 1 + hWin2.Height = 14 + 'hWin2._Shadow = True + hWin2.top = 6 + hWin2.Left = 15 + 'hWin2.Padding = 1 + 'hWin2.Maximized = True + hWin2.Text = "Window 2" + + 'hCont.Top = 3 + hTermLabel = New TermLabel(hWin2) + hTermLabel.Width = 14 + hTermLabel.Top = 2 + hTermLabel.Left = 2 + hTermLabel.Text = "TermLabel on win 2" + + hCont = New TermVBox(hWin2) + hCont.Expand = True + hcont.Background = TermColor.Green + hcont.Border = Border.Simple + 'hcont.Padding = 1 + hcont.Shadow = True + For i = 0 To 10 + hTerCheckBox = New TermRadioButton(hcont) + hTerCheckBox.Background = TermColor.Green + hTerCheckBox.Text = "TermLabel " & i + + Next + hCont2 = New TermHBox(hWin2) + hCont2.Height = 2 + hCont2.Spacing = 1 + hCont2.Background = TermColor.Cyan + hCont2.Invert = True + 'hCont.Shadow = True + hTermButton = New TermButton(hCont2) As "btnOk" + 'hTermButton.Background = TermColor.Magenta + hTermButton.Text = "Ok" + hTermButton.W = 10 + hTermButton.H = 2 + hTermButton._Shadow = True + hTermButton.Alignment = Align.Center + hTermButton = New TermButton(hCont2) As "btnCancel" + 'hTermButton.Background = TermColor.Magenta + hTermButton.Text = "Cancel" + hTermButton.W = 10 + hTermButton.H = 2 + hTermButton._Shadow = True + hTermButton.Alignment = Align.Center + 'hWin2.Maximized = True + hWin2.Show + hwin.Show + 'TermWindows._Render + $hCurFocued = hTermLabel + hTermButton.SetFocus + End + + +Public Sub Timer_Timer() + + hTermLabel.Text = $i +$hCurFocued = hWin2.GetNextFocusedControl($hCurFocued) + Error Object.Type($hCurFocued) & " "; + Try Error $hCurFocued.Text + $hCurFocued.setFocus() + Inc $i + TermWindows._Render + +End + + +Public Sub btnCancel_Click() + + Last.Window.Visible = False + 'TermWindows._Render + +End + +Public Sub btnOk_Click() + Dim hattr As New Attr + hattr.Background = TermColor.Black + 'Message("Salut a tous !") + Desktop.BackGround = Char(" ", hattr.GetInteger()) + hTimer.Start +End diff --git a/comp/src/gb.term.form/.src/Test/Main2.module b/comp/src/gb.term.form/.src/Test/Main2.module new file mode 100644 index 00000000..13e15453 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Main2.module @@ -0,0 +1,61 @@ +' Gambas module file + +Private i As Integer + +Public Sub Main() + Dim hForm As New TermForm + + Dim hCont As TermFrame + Dim hCont2 As TermHBox + Dim hTermButton As TermButton + Dim hLabel As New TermLabel(hForm) + Dim hCheck As TermCheckBox + Dim i As Integer + Dim hPicBox As TermPictureBox + + hLabel.Text = "Hello this is My first terminal window" + + hForm.Padding = 1 + hForm.Height = 15 + hForm.Width = 50 + hForm.Text = "Hi Gambasians" + hForm.Background = TermColor.White + hForm.Border = Border.Double + + hcont = New TermFrame(hForm) + hCont.Expand = True + hCont.Text = "Choose an option" + hCont2 = New TermHBox(hForm) + hCont2.Invert = True + + hTermButton = New TermButton(hCont2) As "btnClose" + hTermButton.Width = 10 + hTermButton.Text = "< Close >" + + hcont.Background = TermColor.Cyan + hcont.Shadow = True + hcont.Border = Border.Simple + hcont.Padding = 1 + For i = 0 To 5 + hCheck = New TermCheckBox(hcont) + + hCheck.Text = "Check me " & i + hCheck.Value = Int(Rnd(-1, 2)) + Next + hPicBox = New TermPictureBox(hcont) + + hPicBox.Expand = True + hPicBox.Image = Image.Load("all.png") + + hForm.Spacing = 1 + hForm.Center + hForm.Show + hForm.Maximized = True + 'TermWindows._Render + +End +Public Sub btnClose_MouseUp() + + Last.Window.Close + +End diff --git a/comp/src/gb.term.form/.src/Test/Main3.module b/comp/src/gb.term.form/.src/Test/Main3.module new file mode 100644 index 00000000..ad16cab8 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Main3.module @@ -0,0 +1,7 @@ +' Gambas module file + +Public Sub Main() + + Dim hpicture As New TermPictureBox + Quit +End diff --git a/comp/src/gb.term.form/.src/Test/Module1.module b/comp/src/gb.term.form/.src/Test/Module1.module new file mode 100644 index 00000000..a53e0fc9 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Module1.module @@ -0,0 +1,7 @@ +' Gambas module file + +Public Sub Main() + + Print "toto" + +End diff --git a/comp/src/gb.term.form/.src/Test/Termform1.class b/comp/src/gb.term.form/.src/Test/Termform1.class new file mode 100644 index 00000000..64569144 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform1.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub TermForm_Open() + + Me.Title = "toto" + +End diff --git a/comp/src/gb.term.form/.src/Test/Termform1.termform b/comp/src/gb.term.form/.src/Test/Termform1.termform new file mode 100644 index 00000000..5b521aa3 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform1.termform @@ -0,0 +1,16 @@ +# Gambas Form File 3.0 + +{ TermForm TermForm + Move(0,0,52,22) + #Translate = False + { TermTextBox1 TermTextBox + Move(11,7,22,2) + } + { TermLabel2 TermLabel + Move(0,7,11,2) + Text = ("Bonjour") + } + { TermTextBox2 TermTextBox + Move(11,11,22,2) + } +} diff --git a/comp/src/gb.term.form/.src/Test/Termform2.class b/comp/src/gb.term.form/.src/Test/Termform2.class new file mode 100644 index 00000000..2bd7137a --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform2.class @@ -0,0 +1,9 @@ +' Gambas class file + + +Public Sub TermForm_Open() +'Me.Persistent = True + Me.Move(0, 0, 30, 10) + Me.Background = TermColor.Red + +End diff --git a/comp/src/gb.term.form/.src/Test/Termform2.termform b/comp/src/gb.term.form/.src/Test/Termform2.termform new file mode 100644 index 00000000..eee892e7 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform2.termform @@ -0,0 +1,5 @@ +# Gambas Form File 3.0 + +{ TermForm TermForm + #MoveScaled(0,0,68,37) +} diff --git a/comp/src/gb.term.form/.src/Test/trfTest.class b/comp/src/gb.term.form/.src/Test/trfTest.class new file mode 100644 index 00000000..9af0905b --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/trfTest.class @@ -0,0 +1,85 @@ +' Gambas class file + +Inherits TermForm +Private $hLab2 As TermLabel +'Private hTimer As New Timer As "Timer" + + + +Public Sub _New() + Dim hText As TermTextBox + Dim hBox As New TermHBox(Me) + Dim hLab As New TermLabel(hBox) + Dim hBar As TermScrollBar + Dim bar As TermScrollBar + Dim hbb As TermHBox + 'Dim hlab2 As TermLabel + + 'Me.Maximized = True + 'Me.Background = Color.Yellow + Me.Padding = 1 + Me.Move(3, 3, 50, 20) + Me.Text = "coucou" + Me.Border = Border.Simple + Me.Visible = True + Me.Center + Me._Arrangement = Arrange.Vertical + Me.Spacing = 1 + + hLab.Text = "Votre nom: " + hLab.W = Len(hLab.Text) + + + hText = New TermTextBox(hBox) As "TextBox" + hText.Text = "test" + hText.Expand = True + hText.SetFocus + + hbox = New TermHBox(Me) + hlab = New TermLabel(hbox) + hLab.Text = "Votre prenom: " + hLab.W = Len(hLab.Text) + + + hText = New TermTextBox(hBox) As "TextBox" + hText.Text = "test" + hText.Expand = True + + ' + $hlab2 = New TermLabel(Me) + + $hlab2.Background = TermColor.green + $hlab2.Show + + + hbb = New TermHBox(Me) + hbb.Height = 8 + hbb.Expand = True + hbox = New TermHBox(hbb) + 'hbox.Expand + bar = New TermScrollBar(hbb) As "Bar" + bar.Height = 5 + bar.Width = 1 + bar.Show + bar.Background = TermColor.red + + bar = New TermScrollBar(Me) As "bar" + + bar.Show + bar.MinValue = -20 + bar.MaxValue = 100 +End + + +Public Sub TextBox_Change() + + $hLab2.Text = Last.text + +End + +Public Sub Bar_Change() + + $hlab2.Text = Last.value + +End + diff --git a/comp/src/gb.term.form/all.png b/comp/src/gb.term.form/all.png new file mode 100644 index 0000000000000000000000000000000000000000..bd1c1ce075ebae381f9229434e02249d8096eae8 GIT binary patch literal 1507 zcmV<91swW`P)-)aEgYyjVC0N-l>-)aEgYXIMC0N-l>-)aEAfB@fX0N-l> z-)sWriZ<@BclFP$`Q_97_V51v`{R5b@VSTl@aF&j|KfKP^1+n;{QByhSN!wq;BE!- z!GOrR*G-khGKIW`x7XY1^P|Qq)}hDXDuB6IoXA0r!M4=rhq%{0jKDaFzBq`! zEP=Y*>ht~n{*S!cY^cxhxrXre`i;BV&f@RF+v_!lzKXfo=J5AkpURTH+f0?kxz^}E zjlj*}@66%uH;BDZm&W}4{;JL6Hi*46hQ0Lp`<1}lMUcYb?e*@jb~cB-h`HGH&#b)H z>9o@3a;wq#<q?Wv@%Q@g_WD4M!L`%p{q^qr{{BXh!?@Mx^Z5I>)aNdPyYl$^ zy4L7rq|JA((?^oSLXW{Qg}kWC;-kpnH;BG3gS(Er+DVhc&*Sf4q03U3#&4?7p2gqy z`uxAy>U6BpiMiNwtkE-uyyfurQJBW+^Y~(-%-ZSmJBq*6=JBo0}_SCg6<9{Ro001L&QchC<8d{RO+wl7kZO0E6k@W?4C(kBP z^JxBOc96zdy6^hV1oE$$(}@5815imsK~zY`l~(sd6EPIN0tE|{jqD*%Hi5DcaP$NO zK@c~J6(>Ux1wl{{6=f6$B5qM};vW2gzL!fcxwcpJ{g9^lzIRF9j3l{=!N=Fn=pPW^ zZ}ju^F-U)rb*8{{jRcy4ywHQqA?a=sVh+}!hlY8eQ8wz_Xt<*iS{Y=!Uql9Bf@FjFg#)z@hauWiDUjn ze-%5Cs*Tk(HPyA7H1y4c;LSr6pPSL1%syMT+GHxWZFQVq-vG8k>&DY~!wrfUf4i(o zW0RA=!`2LA3vPjtXhQ!of9;e7sb&1uHhC9%5Pfa}1&rr>c78lh`|dq^_w9$tfrGf# zaR{b|an=dM1jrYRHx@s&$UkD2<)dB4V0s*uPMpNePSLo+NHj=3Fk0B=rvd9GJ+j=} zcLt_sN&mS4db(~3TY#D<`NEjV6wU*70TpB!hnm45cAbk99tNse@`EvpDU47BJ5nx* z*B|Mi@F-A|BqNO3OraUTF;Zvm>9JoH6|N}oI8cp}Ka4rb!bBT@S0{0L?YgLNL&2wj znk)stm`nfPyhYLW>6w{X4S&wTatWv@g2KG4Be#jDz>joCPhp;jN8*>htHZz7PsB@e zvo7c;+;;|{Vpi@rblc(sPF!kieF#58YMd*M00i~_4sb(UXo(} zv!SRhtHr%#wRmh~x^a4qJ{s3r!fT>{L*Qo#i002ov JPDHLkV1kF=GHn0= literal 0 HcmV?d00001 diff --git a/comp/src/gb.util.web/.component b/comp/src/gb.util.web/.component new file mode 100644 index 00000000..5c80f5fd --- /dev/null +++ b/comp/src/gb.util.web/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.util.web +Version=3.17.90 +State=1 diff --git a/comp/src/gb.util.web/.directory b/comp/src/gb.util.web/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.util.web/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.util.web/.hidden/control/ccontainer.png b/comp/src/gb.util.web/.hidden/control/ccontainer.png new file mode 100644 index 0000000000000000000000000000000000000000..0c964749ebf7dbdb57d5191e0da2b88753fc8546 GIT binary patch literal 1606 zcmV-M2D$l(P)0zwsps4b}iQOhG0k04crPgN`N zsVXI3c&LP=0e?VYOQ=ZWQb+^oLv3juG^)tUbz)~@JC42U-SzA|4}iYHXZ5i zy?Zr#e*b&!xo6KsMEJbT*~J<^$9_MDjO^qQAO&c%vGKXU=9_f-IrdBUW#XN9O5F3m z68YeZlHbPjKL&5__u`l$}vUDNf!s`2Jm)yGii+T0i580xW%8Q8i ze+lq%>LFd@d97nZJIkMdmMky|6oDe1SpZsq5*QxF*T2OV)c*T^D81Jf%iO7y>K74r z)&ktO)EZr5yT0JI4sQ4*T(TG#iNRrv>V+Y)WI-xmn_!tFzkEHd6>It9YtQYq9qv?0 z^)(Y=Gy(t2$S(c{H*JSYZv_fdz(Ffv6$(HFoB)nxf^Cvn)`|GCY|HHf2Ud6TfKuvj zfGc%D7YjoFcWQ^0Y~RQ=KZPs53kV`*{PIaWdl=s>;d^DQk+ZmF5zjFh@6WSj-8CRU zbsVhmpbwjQ<~&13FO&|P;5$EfgTDfHR7Cs~?7LRz+zbov1_U$@(ur{O(N+jOQG&sf zFmO8fy>1}B0`XC@@rrhFbr7D zkKvJ@L*Zk%dGl1bKMr;k#!KNws-QzKk}8M?%?I6~B^}R2g#iHCLt_mD=mx039B1DH zUEsRgAz=mZ$Z?}oM2r;5il2aqvFl7R?+0lq0qBjlAeD)&iow5tc@kEx2ctX*uuc5Z z7)nzSconR$x$dNm=t--~i**7dB4FYJtRfulhO0V3*FzCi{Bi{&n+bq+H4x!g0XUhO z1d9Z=1$^HL#*GC4sq`c`D#96t6Gd3G0=xhwo;T@s+X~)Grena^7{M10@R|vTcmNd_ zP}`QF9>~nWFPG4i1Ylnvk*tG;_GA){2)m(TjY(sbxQ6dP_2=IoFW4QBCt*z6GP=J?!WM<~@+Ps=Z zz|c_XvAOT>d#UA)JJ9FNi)=+yL^$Ur!B5W)j(`>y5c}5g6x)yW+D{kvypPpC7@UYH zEgkfUWDo3XC&T zlTICc^piY2T?gqsIB@v)eQe#?!x4hwa!5qXhW7%clme1FzQ?Msb?|oEimzrDu75Bt z!f_zc$3(J0!bJt+(y1Yizw{wz|D8X%Z-A{^KjrTPl}uPlQ56xt>BXcb;(--({A3mH z%wKuyvdcI8I1Ee$P6ZPADe&sx2t7Oh%>TMS8TijAuRVK+-4SpZFafVtk~J+zL`0NQ zcGn?#Q;NGcS3i2!F51f$ZF&~HrH$b4ygC7P%N35lyof0cX7?0WPcyb=Oy<&??A zs^VskQp)(z3Ko5TIlo!3cLEk(RH-IUqT_R~YGXvDtQov`+N1_R|i;N`ox8~^|S07*qoM6N<$ Ef+WBOQvd(} literal 0 HcmV?d00001 diff --git a/comp/src/gb.util.web/.hidden/control/ccontrol.png b/comp/src/gb.util.web/.hidden/control/ccontrol.png new file mode 100644 index 0000000000000000000000000000000000000000..0c964749ebf7dbdb57d5191e0da2b88753fc8546 GIT binary patch literal 1606 zcmV-M2D$l(P)0zwsps4b}iQOhG0k04crPgN`N zsVXI3c&LP=0e?VYOQ=ZWQb+^oLv3juG^)tUbz)~@JC42U-SzA|4}iYHXZ5i zy?Zr#e*b&!xo6KsMEJbT*~J<^$9_MDjO^qQAO&c%vGKXU=9_f-IrdBUW#XN9O5F3m z68YeZlHbPjKL&5__u`l$}vUDNf!s`2Jm)yGii+T0i580xW%8Q8i ze+lq%>LFd@d97nZJIkMdmMky|6oDe1SpZsq5*QxF*T2OV)c*T^D81Jf%iO7y>K74r z)&ktO)EZr5yT0JI4sQ4*T(TG#iNRrv>V+Y)WI-xmn_!tFzkEHd6>It9YtQYq9qv?0 z^)(Y=Gy(t2$S(c{H*JSYZv_fdz(Ffv6$(HFoB)nxf^Cvn)`|GCY|HHf2Ud6TfKuvj zfGc%D7YjoFcWQ^0Y~RQ=KZPs53kV`*{PIaWdl=s>;d^DQk+ZmF5zjFh@6WSj-8CRU zbsVhmpbwjQ<~&13FO&|P;5$EfgTDfHR7Cs~?7LRz+zbov1_U$@(ur{O(N+jOQG&sf zFmO8fy>1}B0`XC@@rrhFbr7D zkKvJ@L*Zk%dGl1bKMr;k#!KNws-QzKk}8M?%?I6~B^}R2g#iHCLt_mD=mx039B1DH zUEsRgAz=mZ$Z?}oM2r;5il2aqvFl7R?+0lq0qBjlAeD)&iow5tc@kEx2ctX*uuc5Z z7)nzSconR$x$dNm=t--~i**7dB4FYJtRfulhO0V3*FzCi{Bi{&n+bq+H4x!g0XUhO z1d9Z=1$^HL#*GC4sq`c`D#96t6Gd3G0=xhwo;T@s+X~)Grena^7{M10@R|vTcmNd_ zP}`QF9>~nWFPG4i1Ylnvk*tG;_GA){2)m(TjY(sbxQ6dP_2=IoFW4QBCt*z6GP=J?!WM<~@+Ps=Z zz|c_XvAOT>d#UA)JJ9FNi)=+yL^$Ur!B5W)j(`>y5c}5g6x)yW+D{kvypPpC7@UYH zEgkfUWDo3XC&T zlTICc^piY2T?gqsIB@v)eQe#?!x4hwa!5qXhW7%clme1FzQ?Msb?|oEimzrDu75Bt z!f_zc$3(J0!bJt+(y1Yizw{wz|D8X%Z-A{^KjrTPl}uPlQ56xt>BXcb;(--({A3mH z%wKuyvdcI8I1Ee$P6ZPADe&sx2t7Oh%>TMS8TijAuRVK+-4SpZFafVtk~J+zL`0NQ zcGn?#Q;NGcS3i2!F51f$ZF&~HrH$b4ygC7P%N35lyof0cX7?0WPcyb=Oy<&??A zs^VskQp)(z3Ko5TIlo!3cLEk(RH-IUqT_R~YGXvDtQov`+N1_R|i;N`ox8~^|S07*qoM6N<$ Ef+WBOQvd(} literal 0 HcmV?d00001 diff --git a/comp/src/gb.util.web/.icon.png b/comp/src/gb.util.web/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eedc7d8863854cb8646b25127e14db92ddf35ed5 GIT binary patch literal 10933 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

    Len($sStr) Then Return + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return sCar + +End + + +Private Sub ReadChar() As String + + Dim sCar As String + + Do + sCar = GetChar() + If Not sCar Then Return + If sCar > " " Then Return sCar + Loop + +End + + +Private Sub ReadToken() As String + + Dim sToken As String + Dim sCar As String + + GoSub READ_CHAR + If Not IsLetter(sCar) Then Return sCar + + sToken = sCar + Do + GoSub GET_CHAR + If Not sCar Then Break + If Not IsLetter(sCar) Then + Dec $iPos + Break + Endif + sToken &= sCar + Loop + + Return sToken + +READ_CHAR: + + Do + GoSub GET_CHAR + If Not sCar Or If sCar > " " Then Return + Loop + Return + +GET_CHAR: + + If $iPos > Len($sStr) Then + sCar = "" + Return + Endif + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadString() As String + + Dim sCar As String + Dim sString As String + Dim sBuffer As String + Dim iPos As Integer + + Do + GoSub GET_CHAR + If sCar = Chr$(34) Then Return sString & sBuffer + If sCar = "\\" Then + GoSub GET_CHAR + iPos = InStr("bfrtn", sCar) + If iPos Then + sCar = Mid$("\b\f\r\t\n", iPos, 1) + Else If sCar = "u" Then + Try sCar = String.Chr$(Val("&H" & Mid$($sStr, $iPos, 4))) + If Not Error Then $iPos += 4 + Else + ' Keep character + Endif + Endif + sBuffer &= sCar + If Len(sBuffer) > 512 Then + sString &= sBuffer + sBuffer = "" + Endif + Loop + +GET_CHAR: + + If $iPos > Len($sStr) Then Error.Raise("Non terminated string") + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadObject() As Collection + + Dim sCar As String + Dim cObject As Collection + Dim sKey As String + + If $bUseNull Then + cObject = New JSONCollection + Else + cObject = New Collection + Endif + + Do + GoSub READ_CHAR + If sCar = "}" Then Return cObject + If sCar <> Chr$(34) Then Error.Raise("String expected") + sKey = ReadString() + GoSub READ_CHAR + If sCar <> ":" Then Error.Raise("Colon expected") + cObject[sKey] = ReadValue() + GoSub READ_CHAR + If sCar = "}" Then Return cObject + If sCar <> "," Then Error.Raise("Comma expected") + Loop + +READ_CHAR: + + Do + GoSub GET_CHAR + If Not sCar Or If sCar > " " Then Return + Loop + Return + +GET_CHAR: + + If $iPos > Len($sStr) Then + sCar = "" + Return + Endif + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadArray() As Variant[] + + Dim sCar As String + Dim aArray As New Variant[] + + Do + sCar = ReadChar() + If sCar = "]" Then Return aArray + Dec $iPos + aArray.Add(ReadValue()) + sCar = ReadChar() + If sCar = "]" Then Return aArray + If sCar <> "," Then Error.Raise("Comma expected") + Loop + +End + +Private Sub ReadNumber(sNumber As String) As Variant + + Dim sCar As String + Dim vNumber As Variant + Dim bFloat As Boolean + + Do + GoSub GET_CHAR + If Not sCar Then Break + If InStr(".eE", sCar) Then + bFloat = True + Else If InStr("-+0123456789", sCar) = 0 Then + Dec $iPos + Break + Endif + sNumber &= sCar + Loop + + If bFloat Then + Try vNumber = CFloat(sNumber) + If Not Error Then Return vNumber + Else + Try vNumber = CInt(sNumber) + If Not Error Then Return vNumber + Try vNumber = CLong(sNumber) + If Not Error Then Return vNumber + Endif + + Error.Raise("Incorrect number") + +GET_CHAR: + + If $iPos > Len($sStr) Then + sCar = "" + Return + Endif + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadValue() As Variant + + Dim sCar As String + + sCar = ReadToken() + + If sCar = "{" Then + Return ReadObject() + Else If sCar = "[" Then + Return ReadArray() + Else If sCar = Chr$(34) Then + Return ReadString() + Else If sCar = "-" Or If IsDigit(sCar) Then + Return ReadNumber(sCar) + Else If sCar = "null" Then + Return Null + Else If sCar = "true" Then + Return True + Else If sCar = "false" Then + Return False + Else If Not sCar Then + Return + Else + Error.Raise("Incorrect token: " & Quote(sCar)) + Endif + +End + +Private Sub WriteString(sStr As String) + + Dim iPos As Integer + + sStr = Quote(sStr) + Do + iPos = InStr(sStr, "\\", iPos + 1) + If iPos = 0 Then Break + If Mid$(sStr, iPos + 1, 1) = "x" Then + Mid$(sStr, iPos, 4) = "\\u00" & Mid$(sStr, iPos + 2, 2) + Else + Inc iPos + Endif + Loop + Write #$hStream, sStr + +End + + +Private Sub WriteValue(vVal As Variant) + + Dim iPos As Integer + Dim aArray As Array + Dim cCol As Collection + + Select Case TypeOf(vVal) + + Case gb.Null + Write #$hStream, "null" + + Case gb.Boolean + If vVal Then + Write #$hStream, "true" + Else + Write #$hStream, "false" + Endif + + Case gb.Byte, gb.Short, gb.Integer, gb.Long, gb.Float + Write #$hStream, CStr(vVal) + + Case gb.Date + Write #$hStream, Chr$(34) & CStr(vVal) & Chr$(34) + + Case gb.String + WriteString(vVal) + + Case gb.Pointer + If vVal = JSON.Null Then + Write #$hStream, "null" + Else + Write #$hStream, "undefined" + Endif + + Case Else + If vVal Is Array Then + aArray = vVal + Write #$hStream, "[" + For iPos = 0 To vVal.Max + If iPos Then Write #$hStream, "," + WriteValue(vVal[iPos]) + Next + Write #$hStream, "]" + Else If vVal Is Collection Then + cCol = vVal + Write #$hStream, "{" + For Each vVal In cCol + If iPos Then Write #$hStream, "," + WriteString(cCol.Key) + Write #$hStream, ":" + WriteValue(vVal) + Inc iPos + Next + Write #$hStream, "}" + Else If IsNull(vVal) Then + Write #$hStream, "null" + Else + Write #$hStream, "undefined" + Endif + + End Select + +End + + +Public Sub Decode(JSONString As String, Optional UseNull As Boolean) As Variant + + Dim vVal As Variant + $sStr = JSONString + $iPos = 1 + $bUseNull = UseNull + vVal = ReadValue() + $bUseNull = False + $sStr = "" + Return vVal + +End + +Public Sub FromString(JSONString As String, Optional UseNull As Boolean) As Variant + + Return Decode(JSONString, UseNull) + +End + + +Public Sub Encode(Value As Variant) As String + + Dim sStr As String + + $hStream = Open String For Write + WriteValue(Value) + sStr = Close #$hStream + Return sStr + +End + +Public Sub ToString(Value As Variant) As String + + Return Encode(Value) + +End + + +Private Function Null_Read() As Variant + + Return $vNull + +End diff --git a/comp/src/gb.util.web/.src/JSONCollection.class b/comp/src/gb.util.web/.src/JSONCollection.class new file mode 100644 index 00000000..2cac2d76 --- /dev/null +++ b/comp/src/gb.util.web/.src/JSONCollection.class @@ -0,0 +1,42 @@ +' Gambas class file + +Export + +Inherits Collection + +Public Sub _get(Key As String) As Variant + + Dim vVal As Variant + + vVal = Super[Key] + If TypeOf(vVal) = gb.Pointer And If vVal = JSON.Null Then vVal = Null + Return vVal + +End + +Public Sub _put(Value As Variant, Key As String) + + If IsNull(Value) Then Value = JSON.Null + Super[Key] = Value + +End + +Public Sub _next() As Variant + + Super._next() + If Not Enum.Stopped Then Return _get(Me.Key) + +End + +Public Sub Copy() As JSONCollection + + Dim hCopy As New JSONCollection + Dim vVal As Variant + + For Each vVal In Me + hCopy[Me.Key] = vVal + Next + + Return hCopy + +End diff --git a/comp/src/gb.util.web/.src/MMain.module b/comp/src/gb.util.web/.src/MMain.module new file mode 100644 index 00000000..188160bb --- /dev/null +++ b/comp/src/gb.util.web/.src/MMain.module @@ -0,0 +1,32 @@ +' Gambas module file + +Public Function GetNullVariant() As Variant + + Return Null + +End + +Public Function GetNullObject() As Object + + Return Null + +End + +Public Sub Main2() + + Dim hURL As URL + + hURL = URL.FromString("http://localhost/guygle/find?query=ordre+de+travail+avec+interventions+dont+la+date+planifiée+est+comprise+entre+le+01%2F03%2F2017+et+31%2F03%2F2017+et+type+est+Campagne+dont+agence+est+AG.CIG.GONESSE&format=json&test#f5") + + Print hURL.Query["query"] + + Print hURL.ToString() + +End + + +Public Sub Main() + + Print JSON.FromString("") + +End diff --git a/comp/src/gb.util.web/.src/URL.class b/comp/src/gb.util.web/.src/URL.class new file mode 100644 index 00000000..3879e38b --- /dev/null +++ b/comp/src/gb.util.web/.src/URL.class @@ -0,0 +1,185 @@ +' Gambas class file + +Export + +Public Protocol As String +Public Host As String +Public Port As String +Public (User) As String +Public Password As String +Public Path As String +Public Query As UrlQuery +Public Hash As String + +Static Public Sub Quote((Path) As String) As String + + Dim iInd As Integer + Dim sRes As String + Dim sCar As String + + For iInd = 1 To Len(Path) + sCar = Mid$(Path, iInd, 1) + If IsLetter(sCar) Or If IsDigit(sCar) Or If InStr("-._~,$!", sCar) Then + Else + sCar = "%" & Hex$(Asc(sCar), 2) + Endif + sRes &= sCar + Next + + Return sRes + +End + +Static Public Sub Unquote((Path) As String, Optional DoNotDecodePlus As Boolean) As String + + Dim iInd As Integer + Dim sRes As String + Dim sCar As String + + For iInd = 1 To Len(Path) + sCar = Mid$(Path, iInd, 1) + If sCar = "%" Then + sCar = Chr$(Val("&H" & Mid$(Path, iInd + 1, 2))) + iInd += 2 + Else If sCar = "+" And If Not DoNotDecodePlus Then + sCar = " " + Endif + sRes &= sCar + Next + + Return sRes + +End + +Static Public Sub Encode((Path) As String) As String + + Return {Quote}(Path) + +End + +Static Public Sub Decode((Path) As String) As String + + Return {UnQuote}(Path) + +End + +Private Sub CreateUrl(sUrl As String) As URL + + Dim iPos As Integer + Dim sTemp As String + Dim sIdent As String + + iPos = InStr(sURL, ":") + If iPos Then + Protocol = Left(sURL, iPos - 1) + sURL = Mid$(sURL, iPos + 1) + Endif + + If sURL Begins "//" Then + + sURL = Mid$(sURL, 3) + iPos = InStr(sURL, "/") + If iPos = 0 Then + sTemp = sURL + sURL = "" + Else + sTemp = Left(sURL, iPos - 1) + sURL = Mid$(sURL, iPos) + Endif + + iPos = InStr(sTemp, "@") + If iPos Then + + sIdent = Left(sTemp, iPos - 1) + sTemp = Mid$(sTemp, iPos + 1) + + iPos = InStr(sIdent, ":") + If iPos Then + User = Left(sIdent, iPos - 1) + Password = Mid$(sIdent, iPos + 1) + Else + User = sIdent + Endif + + Endif + + iPos = InStr(sTemp, ":") + If iPos Then + Host = Left(sTemp, iPos - 1) + Port = Mid(sTemp, iPos + 1) + Else + Host = sTemp + Endif + + Endif + + iPos = InStr(sURL, "#") + If iPos Then + Hash = Mid(sURL, iPos + 1) + sURL = Left(sURL, iPos - 1) + Endif + + iPos = InStr(sURL, "?") + If iPos Then + Path = Left(sURL, iPos - 1) + Query = New URLQuery(Mid$(sURL, iPos + 1)) + Else + Path = sURL + Endif + + User = FromUrl(User) + Password = FromUrl(Password) + Host = FromUrl(Host) + Port = FromUrl(Port) + Path = FromUrl(Path) + Hash = FromUrl(Hash) + +End + +Public Sub _new(URL As String) + + CreateUrl(URL) + +End + +Public Sub ToString() As String + + Dim sURL As String + Dim sQuery As String + + If Protocol Then sURL = Protocol & ":" + + If Host Then + + sUrl &= "//" + + If User Or If Password Then + + sUrl &= Url(User) + If Password Then sUrl &= ":" & Url(Password) + sUrl &= "@" + + Endif + + sUrl &= Url(Host) + + If Port Then sUrl &= ":" & Url(Port) + + Endif + + If Path Then sUrl &/= Url(Path) + + sQuery = Query.ToString() + If sQuery Then sUrl &= "?" & sQuery + + If Hash Then sUrl &= "#" & Url(Hash) + + Return sUrl + +End + +Static Public Sub FromString(URL As String) As URL + + Return New URL(URL) + +End diff --git a/comp/src/gb.util.web/.src/URLQuery.class b/comp/src/gb.util.web/.src/URLQuery.class new file mode 100644 index 00000000..53c8c142 --- /dev/null +++ b/comp/src/gb.util.web/.src/URLQuery.class @@ -0,0 +1,119 @@ +' Gambas class file + +Export + +Property Read Count As Integer +Property Read Keys As String[] + +Private $cNull As New Collection +Private $cVal As New Collection + +Public Sub _new(Query As String) + + Dim sElt As String + Dim iPos As Integer + Dim sKey As String + Dim sValue As String + Dim cKey As New Collection + + For Each sElt In Split(Query, "&") + + iPos = InStr(sElt, "=") + If iPos Then + sKey = URL.UnQuote(Left(sElt, iPos - 1), True) + sValue = URL.Unquote(Mid$(sElt, iPos + 1), True) + Else + sKey = URL.UnQuote(sElt) + sValue = "" + Endif + + If Not sKey Then Continue + If cKey.Exist(sKey) Then Continue + + If sValue Then + $cVal[sKey] = sValue + Else + $cNull[sKey] = True + Endif + + Next + +End + +Public Sub Exist(Key As String) As Boolean + + If $cNull.Exist(Key) Then Return True + If $cVal.Exist(Key) Then Return True + +End + +Public Sub Remove(Key As String) + + $cNull.Remove(Key) + $cVal.Remove(Key) + +End + + +Public Sub _get(Key As String) As String + + Return $cVal[Key] + +End + +Public Sub _put(Value As String, Key As String) + + Remove(Key) + + If Value Then + $cVal[Key] = Value + Else + $cNull[Key] = True + Endif + +End + +Public Sub ToString() As String + + Dim aQuery As New String[] + Dim sVal As String + + For Each sVal In $cVal + aQuery.Add(URL.Quote($cVal.Key) & "=" & URL.Quote(sVal)) + Next + + For Each $cNull + aQuery.Add(URL.Quote($cNull.Key)) + Next + + Return aQuery.Join("&") + +End + +Static Public Sub FromString(Query As String) As URLQuery + + Return New URLQuery(Query) + +End + + +Private Function Count_Read() As Integer + + Return $cVal.Count + $cNull.Count + +End + +Private Function Keys_Read() As String[] + + Dim aKeys As String[] + + aKeys = New String[] + For Each $cVal + aKeys.Add($cVal.Key) + Next + For Each $cNull + aKeys.Add($cNull.Key) + Next + Return aKeys + +End diff --git a/comp/src/gb.util.web/.src/XML.class b/comp/src/gb.util.web/.src/XML.class new file mode 100644 index 00000000..b22223d2 --- /dev/null +++ b/comp/src/gb.util.web/.src/XML.class @@ -0,0 +1,3 @@ +' Gambas class file + +Export diff --git a/comp/src/gb.util.web/.src/XMLMarkup.class b/comp/src/gb.util.web/.src/XMLMarkup.class new file mode 100644 index 00000000..f5baf4d7 --- /dev/null +++ b/comp/src/gb.util.web/.src/XMLMarkup.class @@ -0,0 +1 @@ +' Gambas class file diff --git a/comp/src/gb.util/.component b/comp/src/gb.util/.component new file mode 100644 index 00000000..2344cc2d --- /dev/null +++ b/comp/src/gb.util/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.util +Version=3.17.90 +State=1 diff --git a/comp/src/gb.util/.directory b/comp/src/gb.util/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.util/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.util/.icon.png b/comp/src/gb.util/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6047946e9bac76ad166efda16507af7fd9b76088 GIT binary patch literal 11424 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 c8=mNKM^f0zz`NVvuW$iX#m5iJ\n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "عربي (مصر)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "عربي (تونس)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr " البلغاريّ ( بلغاريا )" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr " اللّغة الدّنماركيّة ( الدّنمارك )" + +#: Language.class:40 +msgid "German (Germany)" +msgstr " الألمانيّ ( ألمانيا )" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr " الألمانيّ ( بلجيكا )" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr " اليونانيّ ( اليونان )" + +#: Language.class:47 +msgid "English (common)" +msgstr "" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr " الإنجليزيّة ( المملكة المتّحدة )" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr " الإنجليزيّة ( الولايات المتّحدة الأمريكيّة )" + +#: Language.class:50 +msgid "English (Australia)" +msgstr " الإنجليزيّة ( أستراليا )" + +#: Language.class:51 +msgid "English (Canada)" +msgstr " الإنجليزيّة ( كندا )" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr " الأسبانيّ ( أسبانيا )" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr " الأسبانيّ ( الأرجنتين )" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr " فرنسيّ ( فرنسا )" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr " اللّغة الفرنسيّة ( بلجيكا )" + +#: Language.class:76 +msgid "French (Canada)" +msgstr " فرنسيّ ( كندا )" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr " اللّغة الفرنسيّة ( سويسرا )" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr " المجريّ ( المجر )" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "كرواتى" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr " الإندونيسيّ ( أندونيسيا )" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr " الأيرلنديّ ( أيرلندا )" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr " الإيطاليّ ( إيطاليا )" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr " اللّغة الهولنديّة ( هولندا )" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr " اللّغة الهولنديّة ( بلجيكا )" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr " النّرويجيّ ( النّرويج )" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "بولندى ( بولندا )" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr " البرتغاليّ ( البرتغال )" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr " البرتغاليّ ( البرازيل )" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr " روسيّ ( روسيا )" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr " اللّغة السّويديّة ( السّويد )" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "تركى" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr " والون ( بلجيكا )" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "" + +#: Language.class:241 +msgid "Unknown" +msgstr "مجهول" diff --git a/comp/src/gb.util/.lang/ca.po b/comp/src/gb.util/.lang/ca.po new file mode 100644 index 00000000..20d42f54 --- /dev/null +++ b/comp/src/gb.util/.lang/ca.po @@ -0,0 +1,308 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:27 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Àrab (Egipte)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Àrab (Tunísia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Àzeri (Azerbaidjan)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Búlgar (Bulgària)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Català (Països Catalans)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Gaŀlès (Regne Unit)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Txec (República Txeca)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danès (Dinamarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Alemany (Alemanya)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Alemany (Bèlgica)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grec (Grècia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Anglès (comú)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Anglès (Regne Unit)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Anglès (Estats Units d'Amèrica)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Anglès (Austràlia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Anglès (Canadà)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (A qualsevol lloc!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Castellà (Espanya)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Castellà (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estonià (Estònia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Basc (País Basc)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Persa (Iran)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finès (Finlàndia)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francès (França)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francès (Bèlgica)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francès (Canadà)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francès (Suïssa)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Gallec (Espanya)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebreu (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (Índia)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Hongarès (Hongria)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croat (Croàcia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesi (Indonèsia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irlandès (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italià (Itàlia)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japonès (Japó)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Cambodja)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Coreà (Corea)" + +#: Language.class:116 +msgid "Latin" +msgstr "Llatí" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Lituà (Lituània)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (Inde)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macedònic (República de Macedònia)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Neerlandès (Països Baixos)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Neerlandès (Bèlgica)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Noruec (Noruega)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Panjabi (Índia)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polonès (Polònia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portuguès (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portuguès (Brasil)" + +# gb-ignore +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Romanès (Romania)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Rus (Rússia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Eslovè (Eslovènia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanès (Albània)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbi (Sèrbia i Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Suec (Suècia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turc (Turquia)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ucraïnès (Ucraïna)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamita (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Való (Bèlgica)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Xinès simplificat (Xina)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Xinès tradicional (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Desconegut" diff --git a/comp/src/gb.util/.lang/cs.po b/comp/src/gb.util/.lang/cs.po new file mode 100644 index 00000000..823e90fd --- /dev/null +++ b/comp/src/gb.util/.lang/cs.po @@ -0,0 +1,311 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:27 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +# gb-ignore +#: File.class:22 +msgid "&1 B" +msgstr "" + +# gb-ignore +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +# gb-ignore +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +# gb-ignore +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "AfrikánÅ¡tina (Jižní Afrika)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabsky (Egypt)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabsky (Tunisko)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Ázerbájdžánsky (Ázerbájdžán)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulharsky (Bulharsko)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalánsky (Katalánsko, Å panělsko)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "VelÅ¡sky (Velká Británie)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Česky (Česká republika)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Dánsky (Dánsko)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Německy (Německo)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Něměcky (Belgie)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Řecky (Řecko)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Anglicky (obecná anličtina)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Anglicky (Velká Británie)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Anglicky (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Anglicky (Austrálie)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Anglicky (Kanada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (kdekoli!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Å panělsky (Å panělsko)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Å panělsky (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estonsky (Estonsko)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Basque (Baskicko)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "PerÅ¡tina (Írán)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finsky (Finsko)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francouzsky (Francie)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francouzsky (Belgie)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francouzsky (Kanada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francouzsky (Å výcarsko)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galsky (Å panělsko)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "HebrejÅ¡tina (Izrael)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "HindÅ¡tina (Indie)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Maďarsky (Maďarsko)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Chorvatsky (Chorvatsko)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonésky (Indonézie)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irsky (Irsko)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Islandsky (Island)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italsky (Itálie)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japonsky (Japonsko)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Kambodža)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "KorejÅ¡tina (Korea)" + +#: Language.class:116 +msgid "Latin" +msgstr "Latina" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Litevsky (Litva)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (Indie)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Makedonsky (Republika Makedonie)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Holandsky (Nizozemí)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Holandsky (Belgie)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norsky (Norsko)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (Indie)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polsky (Polsko)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugalsky (Portugalsko)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugalsky (Brazílie)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valencian (Valencian Community, Spain)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Rumunsky (Rumunsko)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Rusky (Rusko)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovinsky (Slovinsko)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albánský (Albánie)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Srbská (Srbsko & Černá Hora)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Å védsky (Å védsko)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turecky (Turecko)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ukrajinsky (Ukrajina)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamsky (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Wallon (Belgie)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "ZjednoduÅ¡ená činÅ¡tina (Čína)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Tradiční čínÅ¡tina (Thajsko)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Neznámý" diff --git a/comp/src/gb.util/.lang/cy.po b/comp/src/gb.util/.lang/cy.po new file mode 100644 index 00000000..af6f5878 --- /dev/null +++ b/comp/src/gb.util/.lang/cy.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:28 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: cy\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabeg (yr Aifft)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabeg (Twnisia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bwlgaria" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Cymraeg (Teyrnas Unedig)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Daneg (Denmarc)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Almaeneg (Almaen)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Almaeneg (Gwlad Belg)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Groeg (Groeg)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Saesneg (cyffredin)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Saesneg (Teyrnas Unedig)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Saesneg (U.D.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Saesneg (Awstralia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Saesneg (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Sbaeneg (Sbaen)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Sbaeneg (Ariannin)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Ffrangeg (Ffrainc)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Ffrangeg (Gwlad Belg)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Ffrangeg (Canada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Ffrangeg (Swistir)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galiseg (Sbaen)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Hwngareg (Hwngari)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croateg (Croatia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indoneseg (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Gwyddeleg (Iwerddon)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Eidaleg (Eidal)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Iseldireg (Iseldiroedd)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Iseldireg (Gwlad Belg)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norwyeg (Norwy)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Pwyleg (Gwlad Pwyl)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portiwgaleg (Portiwgal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portiwgaleg (Brasil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Rwsieg (Rwsia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Swedeg (Sweden)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Twrceg (Twrci)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Walwn (Gwlad Belg)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "" + +#: Language.class:241 +msgid "Unknown" +msgstr "Anhysbys" diff --git a/comp/src/gb.util/.lang/de.po b/comp/src/gb.util/.lang/de.po new file mode 100644 index 00000000..22c9d5c7 --- /dev/null +++ b/comp/src/gb.util/.lang/de.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:28 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Afrikaans (Südafrika)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabisch (Ägypten)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabisch (Tunesien)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Aserbaidschanisch (Aserbaidschan)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgarisch (Bulgarien)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalanisch (Katalonien, Spanien)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Walisisch (UK)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Tschechisch (Tschechische Republik)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Dänisch (Dänemark)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Deutsch (Deutschland)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Deutsch (Belgien)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Griechisch (Griechenland)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Englisch (allgemein)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Englisch (UK)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Englisch (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Englisch (Australien)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Englisch (Kanada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (überall!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "Spanisch (Allgemein)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spanisch (Spanien)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spanisch (Argentinien)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estnisch (Estland)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Baskisch (Baskenland)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finnisch (Finnland)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Französisch (Frankreich)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Französisch (Belgien)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Französisch (Kanada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Französisch (Schweiz)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galizisch (Spanien)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebräisch (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (Indien)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Ungarisch (Ungarn)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Kroatisch (Kroatien)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesisch (Indonesien)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irisch (Irland)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Isländisch (Island)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italienisch (Italien)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japanisch (Japan)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Kambodscha)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Koreanisch (Korea)" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Litauisch (Litauen)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (Indien)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Mazedonisch (Republik von Mazedonien)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Niederländisch (Niederlande)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Niederländisch (Belgien)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norwegisch (Norwegen)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (Indien)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polnisch (Polen)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugiesisch (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugiesisch (Brasilien)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valencianisch (Valencia, Spanien)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Rumänisch (Rumänien)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russisch (Russland)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slowenisch (Slowenien)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanisch (Albanien)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbisch (Serbien & Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Schwedisch (Schweden)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Türkisch (Türkei)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ukrainisch (Ukraine)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamesisch (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Wallonisch (Belgien)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Vereinfachtes Chinesisch (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Traditionelles Chinesisch (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Unbekannt" diff --git a/comp/src/gb.util/.lang/el.po b/comp/src/gb.util/.lang/el.po new file mode 100644 index 00000000..fdb31e87 --- /dev/null +++ b/comp/src/gb.util/.lang/el.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:28 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: el\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Αραβικά (Αίγυπτος)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Αραβικά (Τυνησία)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Βουλγάρικα (Βουλγαρία)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Ουαλικά (Ηνωμένο Βασίλειο)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Τσέχικα (Δημοκρατία της Τσεχίας)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Δανέζικα (Δανία)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Γερμανικά (Γερμανία)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Ελληνικά (Ελλάδα)" + +#: Language.class:47 +msgid "English (common)" +msgstr "" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Αγγλικά (Ηνωμένο Βασίλειο)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Αγγλικά (Αυστραλία)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Αγγλικά (Καναδάς)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Ισπανικά (Ισπανία)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Ισπανικά (Αργεντινή)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Εσθονικά (Εσθονία)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Φινλανδικά (Φινλανδία)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Γαλλικά (Γαλλία)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Γαλλικά (Βέλγιο)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Γαλλικά (Καναδάς)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Γαλλικά (Ελβετία)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Εβραϊκά (Ισραήλ)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Ίντι (Ινδία)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Ουγγρικά (Ουγγαρία)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Κροατικά (Κροατία)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Ιρλανδός (Ιρλανδία)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Ιταλικά (Ιταλία)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Ιαπωνικά (Ιαπωνία)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Χμερ (Καμπότζη)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "Latin" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Λιθουανικά (Λιθουανία)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Μαλέιγιαλαμ (Ινδία)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "FYROM" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Ολλανδικά (Ολλανδία)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Ολλανδικά (Βέλγιο)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Πουντζάμπι (Ινδία)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Πολωνικά (Πολωνία)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Πορτογαλικά (Πορτογαλία)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Πορτογαλικά (Βραζιλία)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Ρουμάνικα (Ρουμανία)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Ρώσικα (Ρωσία)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Σλοβένικα (Σλοβενία)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Αλβανός (Αλβανία)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Σουηδικά (Σουηδία)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Τουρκικά (Τουρκία)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Βιετναμέζικα (Βιετνάμ)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "" + +#: Language.class:241 +msgid "Unknown" +msgstr "Άγνωστο" diff --git a/comp/src/gb.util/.lang/es.po b/comp/src/gb.util/.lang/es.po new file mode 100644 index 00000000..7313e484 --- /dev/null +++ b/comp/src/gb.util/.lang/es.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:29 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Africano (Sudáfrica)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arábico (Egipto)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arábico (Tunicia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbayano (Azerbayán)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Búlgaro (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalán (Cataluña, España)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Galeico (Reino Unido)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Checo (República Checa)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danés (Dinamarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Alemán (Alemania)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Alemán (Bélgica)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Griego (Grecia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Inglés (común)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Inglés (Reino Unido)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Inglés (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Inglés (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Inglés (Canadá)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (¡En todo el mundo!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "Español (común)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Español (España)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Español (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estonio (Estonia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Basco (País Basco)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (Irán)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finlandés (Finlandia)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francés (Francia)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francés (Bélgica)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francés (Canadá)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francés (Suiza)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Gallego (España)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebreo (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (India)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Húngaro (Hungría)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croata (Croacia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesio (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Galéico (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Islandés (Islandia)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiano (Italia)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japonés (Japón)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Jemer (Camboya)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Koreano (Korea)" + +#: Language.class:116 +msgid "Latin" +msgstr "Latín" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Lituano (Lituania)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malabar (India)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macedonio (Republica de Macedonia)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Flamenco (Holanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Flamenco (Bélgica)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Noruego (noruega)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (India)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polaco (Polonia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugués (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugués (Brasil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valenciano (Comunidad Valenciana, España)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Rumano (Rumania)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Ruso (Rusia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Esloveno (Eslovenia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanés (Albania)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbio (Serbia & Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Sueco (Suecia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turco (Turquía)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ucrania (Ucraniano)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamita (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Valón (Bélgica)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Chino simplificado (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Chino tradicional (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Desconocido" diff --git a/comp/src/gb.util/.lang/es_ES.po b/comp/src/gb.util/.lang/es_ES.po new file mode 100644 index 00000000..a288a210 --- /dev/null +++ b/comp/src/gb.util/.lang/es_ES.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:29 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Africano (Sudáfrica)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arábico (Egipto)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arábico (Tunicia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbayano (Azerbayán)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Búlgaro (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalán (Cataluña, España)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Galeico (Reino Unido)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Checo (República Checa)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danés (Dinamarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Alemán (Alemania)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Alemán (Bélgica)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Griego (Grecia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Inglés (común)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Inglés (Reino Unido)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Inglés (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Inglés (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Inglés (Canadá)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (¡En todo el mundo!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "Español (común)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Español (España)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Español (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estonio (Estonia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Basco (País Basco)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (Irán)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finlandés (Finlandia)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francés (Francia)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francés (Bélgica)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francés (Canadá)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francés (Suiza)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Gallego (España)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebreo (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (India)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Húngaro (Hungría)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croata (Croacia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesio (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Galéico (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Islandés (Islandia)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiano (Italia)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japonés (Japón)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Jemer (Camboya)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Koreano (Korea)" + +#: Language.class:116 +msgid "Latin" +msgstr "Latín" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Lituano (Lituania)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malabar (India)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macedonio (Republica de Macedonia)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Flamenco (Holanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Flamenco (Bélgica)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Noruego (noruega)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (India)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polaco (Polonia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugués (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugués (Brasil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valenciano (Comunidad Valenciana, España)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Rumano (Rumania)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Ruso (Rusia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Esloveno (Eslovenia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanés (Albania)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbio (Serbia & Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Sueco (Suecia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turco (Turquía)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ucrania (Ucraniano)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamita (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Valón (Bélgica)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Chino simplificado (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Chino tradicional (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Desconocido" diff --git a/comp/src/gb.util/.lang/fa.po b/comp/src/gb.util/.lang/fa.po new file mode 100644 index 00000000..34d6449b --- /dev/null +++ b/comp/src/gb.util/.lang/fa.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:29 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: fa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabic (مصر)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabic (تونس)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbaijani (آذربایجان)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgarian (بلغارستان)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalan (کاتالونیا - اسپانیا)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Welsh (انگلستان)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Czech (جمهوری چک)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danish (دانمارک)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "German (آلمان)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "German (بلژیک)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Greek (یونان)" + +#: Language.class:47 +msgid "English (common)" +msgstr "English (معمول)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "English (انگلستان)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "English (ایالات متحده آمریکا)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "English (استرالیا)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "English (کانادا)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (هر جا!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spanish (اسپانیا)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spanish (آرژانتین)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (ایران)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "French (فرانسه)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "French (بلژیک)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "French (کانادا)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "French (سوئیس)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galician (اسپانیا)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Hungarian (مجارستان)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croatian (کرواسی)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesian (اندونزی)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irish (ایرلند)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italian (ایتالیا)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japanese (ژاپن)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Korean (کره)" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Dutch (هلند)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Dutch (بلژیک)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norwegian (نروژ)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (هند)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polish (لهستان)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portuguese (پرتغال)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portuguese (برزیل)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russian (روسیه)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovenian (اسلوانی)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbian (صربستان و مونتنگرو)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Swedish (سوئد)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turkish (ترکیه ای)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamese (ویتنامی)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Wallon (بلژیکی)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Simplified chinese (چین)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Traditional chinese (تایوان)" + +#: Language.class:241 +msgid "Unknown" +msgstr "ناشناس" diff --git a/comp/src/gb.util/.lang/fr.po b/comp/src/gb.util/.lang/fr.po new file mode 100644 index 00000000..1e365f0f --- /dev/null +++ b/comp/src/gb.util/.lang/fr.po @@ -0,0 +1,335 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.17.90\n" +"POT-Creation-Date: 2022-03-26 19:21 UTC\n" +"PO-Revision-Date: 2022-03-26 19:17 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "Routines utilitaires" + +#: File.class:22 +msgid "&1 B" +msgstr "&1 o" + +#: File.class:24 +msgid "&1 KiB" +msgstr "&1 Kio" + +#: File.class:26 +msgid "&1 MiB" +msgstr "&1 Mio" + +#: File.class:28 +msgid "&1 GiB" +msgstr "&1 Gio" + +#: File.class:34 +msgid "&1 KB" +msgstr "&1 Ko" + +#: File.class:36 +msgid "&1 MB" +msgstr "&1 Mo" + +#: File.class:38 +msgid "&1 GB" +msgstr "&1 Go" + +#: Language.class:31 +msgid "Afrikaans (South Africa)" +msgstr "Afrikaans (Afrique du Sud)" + +#: Language.class:34 +msgid "Arabic (Egypt)" +msgstr "Arabe (Egypte)" + +#: Language.class:35 +msgid "Arabic (Tunisia)" +msgstr "Arabe (Tunisie)" + +#: Language.class:36 +msgid "Arabic (Algeria)" +msgstr "Arabe (Algérie)" + +#: Language.class:40 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azéri (Azerbaïdjan)" + +#: Language.class:43 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgare (Bulgarie)" + +#: Language.class:46 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalan (Catalogne, Espagne)" + +#: Language.class:51 +msgid "Welsh (United Kingdom)" +msgstr "Gallois (Royaume-Uni)" + +#: Language.class:54 +msgid "Czech (Czech Republic)" +msgstr "Tchèque (République Tchèque)" + +#: Language.class:58 +msgid "Danish (Denmark)" +msgstr "Danois (Danemark)" + +#: Language.class:61 +msgid "German (Germany)" +msgstr "Allemand (Allemagne)" + +#: Language.class:62 +msgid "German (Belgium)" +msgstr "Allemand (Belgique)" + +#: Language.class:66 +msgid "Greek (Greece)" +msgstr "Grec (Grèce)" + +#: Language.class:69 +msgid "English (common)" +msgstr "Anglais (commun)" + +#: Language.class:70 +msgid "English (United Kingdom)" +msgstr "Anglais (Royaume Uni)" + +#: Language.class:71 +msgid "English (U.S.A.)" +msgstr "Anglais (U.S.A.)" + +#: Language.class:72 +msgid "English (Australia)" +msgstr "Anglais (Australie)" + +#: Language.class:73 +msgid "English (Canada)" +msgstr "Anglais (Canada)" + +#: Language.class:74 +msgid "English (India)" +msgstr "Anglais (Inde)" + +#: Language.class:78 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (Partout!)" + +#: Language.class:81 +msgid "Spanish (common)" +msgstr "Espagnol (commun)" + +#: Language.class:82 +msgid "Spanish (Spain)" +msgstr "Espagnol (Espagne)" + +#: Language.class:83 +msgid "Spanish (Argentina)" +msgstr "Espagnol (Argentine)" + +#: Language.class:87 +msgid "Estonian (Estonia)" +msgstr "Estonien (Estonie)" + +#: Language.class:90 +msgid "Basque (Basque country)" +msgstr "Basque (Pays basque)" + +#: Language.class:93 +msgid "Farsi (Iran)" +msgstr "Farsi (Iran)" + +#: Language.class:96 +msgid "Finnish (Finland)" +msgstr "Finlandais (Finlande)" + +#: Language.class:100 +msgid "French (France)" +msgstr "Français (France)" + +#: Language.class:101 +msgid "French (Belgium)" +msgstr "Français (Belgique)" + +#: Language.class:102 +msgid "French (Canada)" +msgstr "Français (Canada)" + +#: Language.class:103 +msgid "French (Switzerland)" +msgstr "Français (Suisse)" + +#: Language.class:107 +msgid "Galician (Spain)" +msgstr "Galicien (Espagne)" + +#: Language.class:110 +msgid "Hebrew (Israel)" +msgstr "Hébreu (Israël)" + +#: Language.class:113 +msgid "Hindi (India)" +msgstr "Hindi (Inde)" + +#: Language.class:116 +msgid "Hungarian (Hungary)" +msgstr "Hongrois (Hongrie)" + +#: Language.class:119 +msgid "Croatian (Croatia)" +msgstr "Croate (Croatie)" + +#: Language.class:122 +msgid "Indonesian (Indonesia)" +msgstr "Indonésien (Indonésie)" + +#: Language.class:126 +msgid "Tamil (India)" +msgstr "Tamil (Inde)" + +#: Language.class:130 +msgid "Telugu (India)" +msgstr "Telugu (Inde)" + +#: Language.class:134 +msgid "Malayalam (India)" +msgstr "Malayalam (Inde)" + +#: Language.class:138 +msgid "Kannada (India)" +msgstr "Kannada (Inde)" + +#: Language.class:146 +msgid "Punjabi (India)" +msgstr "Punjabi (Inde)" + +#: Language.class:165 +msgid "Irish (Ireland)" +msgstr "Irlandais (Irlande)" + +#: Language.class:168 +msgid "Icelandic (Iceland)" +msgstr "Islandais (Islande)" + +#: Language.class:171 +msgid "Italian (Italy)" +msgstr "Italien (Italie)" + +#: Language.class:172 +msgid "Italian (Switzerland)" +msgstr "Italien (Suisse)" + +#: Language.class:176 +msgid "Japanese (Japan)" +msgstr "Japonais (Japon)" + +#: Language.class:179 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Cambodge)" + +#: Language.class:182 +msgid "Korean (Korea)" +msgstr "Coréen (Corée)" + +#: Language.class:186 +msgid "Latin" +msgstr "Latin" + +#: Language.class:189 +msgid "Lithuanian (Lithuania)" +msgstr "Lithuanien (Lithuanie)" + +#: Language.class:192 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macédonien (République de Macédoine)" + +#: Language.class:195 +msgid "Dutch (Netherlands)" +msgstr "Hollandais (Pays-bas)" + +#: Language.class:196 +msgid "Dutch (Belgium)" +msgstr "Hollandais (Belgique)" + +#: Language.class:200 +msgid "Norwegian (Norway)" +msgstr "Norvégien (Norvège)" + +#: Language.class:201 +msgid "BokmÃ¥l (Norway)" +msgstr "BokmÃ¥l (Norvège)" + +#: Language.class:205 +msgid "Polish (Poland)" +msgstr "Polonais (Pologne)" + +#: Language.class:208 +msgid "Portuguese (Portugal)" +msgstr "Portugais (Portugal)" + +#: Language.class:209 +msgid "Portuguese (Brazil)" +msgstr "Portugais (Brésil)" + +#: Language.class:212 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valencien (Communauté Valencienne, Espagne)" + +#: Language.class:215 +msgid "Romanian (Romania)" +msgstr "Roumain (Roumanie)" + +#: Language.class:218 +msgid "Russian (Russia)" +msgstr "Russe (Russie)" + +#: Language.class:222 +msgid "Slovenian (Slovenia)" +msgstr "Slovénien (Slovénie)" + +#: Language.class:225 +msgid "Albanian (Albania)" +msgstr "Albanais (Albanie)" + +#: Language.class:228 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbe (Serbie & Montenegro)" + +#: Language.class:231 +msgid "Swedish (Sweden)" +msgstr "Suèdois (Suède)" + +#: Language.class:234 +msgid "Turkish (Turkey)" +msgstr "Turque (Turquie)" + +#: Language.class:238 +msgid "Ukrainian (Ukrain)" +msgstr "Ukrainien (Ukraine)" + +#: Language.class:241 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamien (Viet-nâm)" + +#: Language.class:244 +msgid "Wallon (Belgium)" +msgstr "Wallon (Belgique)" + +#: Language.class:247 +msgid "Simplified chinese (China)" +msgstr "Chinois simplifié (Chine)" + +#: Language.class:248 +msgid "Traditional chinese (Taiwan)" +msgstr "Chinois traditionnel (Taiwan)" + +#: Language.class:324 +msgid "Unknown" +msgstr "Inconnu" diff --git a/comp/src/gb.util/.lang/gl_ES.po b/comp/src/gb.util/.lang/gl_ES.po new file mode 100644 index 00000000..d6d18f7d --- /dev/null +++ b/comp/src/gb.util/.lang/gl_ES.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:29 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: gl_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arábico (Exipto)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arábico (Tunicia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Búlgaro (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Galeico (Reino Unido)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danés (Dinamarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Alemán (Alemania)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Alemán (Bélxica)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grego (Grecia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Inglés (común)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Inglés (Reino Unido)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Inglés (U.S.A)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Inglés (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Inglés (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Español (España)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Español (Arxentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Frances (Francia)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francés (Bélxica)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francés (Canadá)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francés (Suiza)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galego (España)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Húngaro (Hungría)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croata (Croacia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesio (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Galéico (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Iataliano (Italia)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Flamenco (Holanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Alemán (Bélgica)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Noruego (noruega)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polaco (Polonia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugués (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugés (Brasil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Ruso (Rusia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Sueco (Suecia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turco (Turquía)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Valón (Belxica)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "" + +#: Language.class:241 +msgid "Unknown" +msgstr "Descoñecido" diff --git a/comp/src/gb.util/.lang/hr.po b/comp/src/gb.util/.lang/hr.po new file mode 100644 index 00000000..6b3785cf --- /dev/null +++ b/comp/src/gb.util/.lang/hr.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:29 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: hr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arapski (Egipt)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arapki (Tunis)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bugarski (Bugarska)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalonski (Katalonija, Å panjolska)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "VelÅ¡ki (UK)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danski (Danska)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Njemački (Njemačka)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Njemački (Belgija)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grčki (Grčka)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Engleski (zajednički)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Engleski (Velika Britanija)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Engleski (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Engleski (Australija)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Engleski (Kanada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Å panjolski (Å panjolska)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Å panjolski (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francuski (Francuska)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francuski (Belgija)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francuski (Kanada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francuska (Å vicarska)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galicijski (Å panjolska)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Mađarski (Mađarska)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Hrvatski (Hrvatska)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonezijski (Indonezija)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irski (Irska)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Talijanski (Italija)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Nizozemski (Nizozemska)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Nizozemski (Belgija)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "NorveÅ¡ki (NorveÅ¡ka)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Poljski (Poljska)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "portugalski (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugalski (Brazil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Ruski (Rusija)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovenski (Slovenija)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Å vedski (Å vedska)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turski (Turska)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Valonski (Belgija)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Pojednostavljeni kineski (Kina)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Tradicionalni kineski (Tajvan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Nepoznat" diff --git a/comp/src/gb.util/.lang/hu.po b/comp/src/gb.util/.lang/hu.po new file mode 100644 index 00000000..d15dae7b --- /dev/null +++ b/comp/src/gb.util/.lang/hu.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:30 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: hu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arab (Egyiptom)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arab (Tunézia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bolgár (Bulgária)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalán (Katalónia, Spanyolország)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Velszi (Egyesült Királyság)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Cseh (Cseh Köztársaság)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Dán (Dánia)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Német (Németország)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Német (Belgium)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Görög (Görögország)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Angol (közös)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Angol (Egyesült Királyság)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Angol (Amerikai Egyesült Államok)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Angol (Ausztrália)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Angol (Kanada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Eszperantó (mindenhol!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spanyol (Spanyolország)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spanyol (Argentína)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Észt (Észtország)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Fárszi (Irán)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finn (Finnország)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francia (Franciaország)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francia (Belgium)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francia (Kanada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francia (Svájc)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galíciai (Spanyolország)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Héber (Izrael)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (India)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Magyar (Magyarország)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Horvát (Horvátország)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonéz (Indonézia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Ír (Írország)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Olasz (Olaszország)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japán (Japán)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Kambodzsa)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "Latin" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Litván (Litvánia)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (India)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Holland (Hollandia)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Holland (Belgium)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norvég (Norvégia)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Pandzsábi (India)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Lengyel (Lengyelország)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugál (Portugália)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugál (Brazília)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Román (Románia)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Orosz (Oroszország)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Szlovén (Szlovénia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albán (Albánia)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Szerb (Szerbia és Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Svéd (Svédország)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Török (Törökország)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnami (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Vallon (Belgium)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Egyszerűsített kínai (Kína)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Hagyományos kínai (Tajvan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Ismeretlen" diff --git a/comp/src/gb.util/.lang/id.po b/comp/src/gb.util/.lang/id.po new file mode 100644 index 00000000..49dfde6c --- /dev/null +++ b/comp/src/gb.util/.lang/id.po @@ -0,0 +1,308 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:30 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: id\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arab (Mesir)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arab (Tunisia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgaria (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalan (Catalonia, Spain)" + +# gb-ignore +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danish (Denmark)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "German (Germany)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "German (Belgium)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Greek (Greece)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Inggris (umum)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Inggris (United Kingdom)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Inggris (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Inggris (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Inggris (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spanish (Spain)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spanish (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "French (France)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "French (Belgium)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "French (Canada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "French (Switzerland)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galicia (Spanyol)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Hungarian (Hungary)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Kroasia (Kroasia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesian (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irish (Irlandia)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italian (Itali)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Dutch (Netherlands)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Dutch (Belgium)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norwegian (Norway)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polish (Poland)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugis (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugis (Brazil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russian (Rusia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovenia (Slovenia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Swedish (Sweden)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turkish (Turkey)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Wallon (Belgia)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Cina" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Taiwan" + +#: Language.class:241 +msgid "Unknown" +msgstr "Tidak dikenal" diff --git a/comp/src/gb.util/.lang/it.po b/comp/src/gb.util/.lang/it.po new file mode 100644 index 00000000..19d231b6 --- /dev/null +++ b/comp/src/gb.util/.lang/it.po @@ -0,0 +1,314 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:30 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "Routine di utilità" + +# gb-ignore +#: File.class:22 +msgid "&1 B" +msgstr "" + +# gb-ignore +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +# gb-ignore +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +# gb-ignore +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +# gb-ignore +#: File.class:34 +msgid "&1 KB" +msgstr "" + +# gb-ignore +#: File.class:36 +msgid "&1 MB" +msgstr "" + +# gb-ignore +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Afrikaans (Sudafrica)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabo (Egitto)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabo (Tunisia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbaigiano (Azerbaijan)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgaro (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalano (Catalano, Spagna)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Gallese (UK)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Ceco (Repubblica Ceca)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danese (Danimarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Tedesco (Germania)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Tedesco (Belgio)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Greco (Grecia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Inglese (Internazionale)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Inglese (Regno Unito)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Inglese (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Inglese (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Inglese (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (Ovunque!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "Spagnolo (comune)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spagnolo (Spagna)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spagnolo (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estone (Estonia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Basco (Regione Basca)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (Iran)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finnico (Finlandia)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francese (Francia)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francese (Belgio)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francese (Canada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francese (Svizzera)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galiziano (Spagna)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Ebraico (Israele)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (India)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Ungherese (Ungheria)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croato (Croazia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesiano (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irlandese (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Islandese (Islanda)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiano (Italia)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Giapponese (Giappone)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Cambogia)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Coreano (Corea)" + +#: Language.class:116 +msgid "Latin" +msgstr "Latino" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Lituano (Lituania)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (India)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macedone (Repubblica di Macedonia)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Danese (Olanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Danese (Belgio)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norvegese (Norvegia)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (India)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polacco (Polonia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portoghese (Portogallo)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portoghese (Brasile)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valenzano (Comunità Valenzana, Spagna)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Rumeno (Romania)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russo (Russia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovacco (Slovenia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanese (Albania)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbo (Serbia e Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Svedese (Svezia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turco (Turchia)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ucraino (Ucraina)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamita (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Vallone (Belgio)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Cinese semplificato (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Cinese tradizionale (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Sconosciuto" diff --git a/comp/src/gb.util/.lang/ja.po b/comp/src/gb.util/.lang/ja.po new file mode 100644 index 00000000..12743e6f --- /dev/null +++ b/comp/src/gb.util/.lang/ja.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:30 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "アラビア語 (エジプト)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "アラビア語 (チュニジア)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "アゼルバイジャン語(アゼルバイジャン)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "ブルガリア (ブルガリア語)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "カタロニア語 (カタロニア、スペイン)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "ウェールズ (イギリス)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "チェコ語 (チェコ共和国)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "デンマーク語 (デンマーク)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "ドイツ語 (ドイツ)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "ドイツ語 (ベルギー)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "ギリシャ語 (ギリシャ)" + +#: Language.class:47 +msgid "English (common)" +msgstr "英語 (共通)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "英語 (イギリス)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "英語 (アメリカ)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "英語 (オーストラリア)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "英語 (カナダ)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "エスペラント語(どこでも!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "スペイン語 (スペイン)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "スペイン語 (アルゼンチン)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "エストニア語 (エストニア)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "近代ペルシャ語(イラン)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "フランス語 (フランス)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "フランス語 (ベルギー)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "フランス語 (カナダ)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "フランス語 (スイス)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "ガリシア語 (スペイン)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "ヘブライ語 (イスラエル)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "ヒンディー語 (インド)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "ハンガリー語 (ハンガリー)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "クロアチア (クロアチア語)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "インドネシア語 (インドネシア)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "アイルランド語 (アイルランド)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "イタリア語 (イタリア)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "日本語 (日本)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "クメール語 (カンボジア)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "韓国語(韓国)" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "マラヤーラム語 (インド)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "オランダ語 (オランダ)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "オランダ語 (ベルギー)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "ノルウェー語 (ノルウェー)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "パンジャブ語(インド)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "ポーランド語 (ポーランド)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "ポルトガル語 (ポルトガル)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "ポルトガル語 (ブラジル)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "ロシア語 (ロシア)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "スロベニア語 (スロベニア)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "アルバニア語 (アルバニア)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "セルビア語(セルビア・モンテネグロ)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "スウェーデン語 (スウェーデン)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "トルコ語 (トルコ)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "ベトナム語(ベトナム)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "ワロン語 (ベルギー)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "簡体字中国語 (中国)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "繁体字中国語 (台湾)" + +#: Language.class:241 +msgid "Unknown" +msgstr "不明" diff --git a/comp/src/gb.util/.lang/ko.po b/comp/src/gb.util/.lang/ko.po new file mode 100644 index 00000000..f16acf73 --- /dev/null +++ b/comp/src/gb.util/.lang/ko.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:30 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "아랍어 (이집트)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "아랍어 (튀니지)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "불가리아어 (불가리아)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "웨일즈어 (영국)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "체코어 (체코 공화국)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "덴마크어 (덴마크)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "독일어 (독일)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "그리스어 (그리스)" + +#: Language.class:47 +msgid "English (common)" +msgstr "" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "영어 (영국)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "영어 (오스트레일리아)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "영어 (캐나다)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "스페인어 (스페인)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "스페인어 (아르헨티나)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "에스토니아어 (에스토니아)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "프랑스어 (프랑스)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "프랑스어 (벨기에)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "프랑스어 (캐나다)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "프랑스어 (스위스)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "히브리어 (이스라엘)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "힌디어 (인도)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "헝가리어 (헝가리)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "크로아티아어 (크로아티아)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "아일랜드어 (아일랜드)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "이탈리아어 (이탈리아)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "일본어 (일본)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "크메르어 (캄보디아)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "말라얄람어 (인도)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "네덜란드어 (네덜란드)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "네덜란드어 (벨기에)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "펀자브어 (인도)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "폴란드어 (폴란드)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "포르투갈어 (포르투갈)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "포르투갈어 (브라질)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "러시아어 (러시아)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "슬로베니아어 (슬로베니아)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "알바니아어 (알바니아)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "스웨덴어 (스웨덴)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "터키어 (터키)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "베트남어 (베트남)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "" + +#: Language.class:241 +msgid "Unknown" +msgstr "알 수 없음" diff --git a/comp/src/gb.util/.lang/lt.po b/comp/src/gb.util/.lang/lt.po new file mode 100644 index 00000000..c81fdf23 --- /dev/null +++ b/comp/src/gb.util/.lang/lt.po @@ -0,0 +1,365 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:31 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: lt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +# gb-ignore +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "" + +# gb-ignore +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "" + +# gb-ignore +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +# gb-ignore +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "" + +# gb-ignore +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "" + +# gb-ignore +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "" + +# gb-ignore +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +# gb-ignore +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "" + +# gb-ignore +#: Language.class:40 +msgid "German (Germany)" +msgstr "" + +# gb-ignore +#: Language.class:41 +msgid "German (Belgium)" +msgstr "" + +# gb-ignore +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "" + +# gb-ignore +#: Language.class:47 +msgid "English (common)" +msgstr "" + +# gb-ignore +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "" + +# gb-ignore +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "" + +# gb-ignore +#: Language.class:50 +msgid "English (Australia)" +msgstr "" + +# gb-ignore +#: Language.class:51 +msgid "English (Canada)" +msgstr "" + +# gb-ignore +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +# gb-ignore +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "" + +# gb-ignore +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "" + +# gb-ignore +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Baskų (Baskų kraÅ¡tas)" + +# gb-ignore +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +# gb-ignore +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +# gb-ignore +#: Language.class:74 +msgid "French (France)" +msgstr "" + +# gb-ignore +#: Language.class:75 +msgid "French (Belgium)" +msgstr "" + +# gb-ignore +#: Language.class:76 +msgid "French (Canada)" +msgstr "" + +# gb-ignore +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "" + +# gb-ignore +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "" + +# gb-ignore +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +# gb-ignore +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +# gb-ignore +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "" + +# gb-ignore +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "" + +# gb-ignore +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +# gb-ignore +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "" + +# gb-ignore +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +# gb-ignore +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +# gb-ignore +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +# gb-ignore +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +# gb-ignore +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +# gb-ignore +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +# gb-ignore +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "" + +# gb-ignore +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "" + +# gb-ignore +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "" + +# gb-ignore +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +# gb-ignore +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "" + +# gb-ignore +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "" + +# gb-ignore +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "" + +# gb-ignore +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +# gb-ignore +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +# gb-ignore +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "" + +# gb-ignore +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanian (Albanų)" + +# gb-ignore +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +# gb-ignore +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "" + +# gb-ignore +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "" + +# gb-ignore +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +# gb-ignore +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +# gb-ignore +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "" + +# gb-ignore +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "" + +# gb-ignore +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "" + +#: Language.class:241 +msgid "Unknown" +msgstr "Nežinomas" diff --git a/comp/src/gb.util/.lang/nl.po b/comp/src/gb.util/.lang/nl.po new file mode 100644 index 00000000..bf323189 --- /dev/null +++ b/comp/src/gb.util/.lang/nl.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:31 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Afrikaans (Zuid Afrika)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabisch (Egypte)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabisch (Tunesië)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbaijaans (Azerbaijaan)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgaars (Bulgarije)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalaans (Catalonie, Spanje)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Welsh (Verinigd Koninkrijk)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Tsjechisch (Tsjechische Republiek)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Deens (Denemarken)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Duits (Duitsland)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Duits (België)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grieks (Griekenland)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Engels (algemeen)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Engels (Verenigd Koninkrijk)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Engels (V.S.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Engels (Australië)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Engels (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (Wereldwijd!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "Spanish (algemeen)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spaans (Spanje)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spaans (Argentinië)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estonees (Estonië)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Baskisch (Baskenland)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (Iran)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Fins (Finland)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Frans (Frankrijk)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Frans (België)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Frans (Canada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Frans (Zwitserland)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galiciaans (Spanje)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebreeuws (Israël)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (India)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Hongaars (Hongarije)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Kroatisch (Kroatie)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesies (Indonesië)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Iers (Ierland)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Ijslands (Ijsland)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiaans (Italië)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japans (Japan)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Cambodja)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Koreaans (Korea)" + +#: Language.class:116 +msgid "Latin" +msgstr "Latijn" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Litouws (Litouwen)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (India)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macedonisch (Republiek Macedonië)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Nederlands (Nederland)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Nederlands (België)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Noors (Noorwegen)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjab (Indië)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Pools (Polen)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugees (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugees (Brazilië)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valentiaans (Valentiaanse Gemeenschap, Spanje)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Roemeens (Roemenië)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russisch (Rusland)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slowaaks (Slowenie)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanees (Albanië)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Servisch (Servië & Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Zweeds (Zweden)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turks (Turkije)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Oekraïend (Oekraïne)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamees (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Wallonië (België)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Simpel chinees (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Traditioneel chinees (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Onbekend" diff --git a/comp/src/gb.util/.lang/no.po b/comp/src/gb.util/.lang/no.po new file mode 100644 index 00000000..4b1add04 --- /dev/null +++ b/comp/src/gb.util/.lang/no.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:31 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: no\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabisk (Egypt)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabisk (Tunis)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgarsk (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalansk (Katalonia, Spain)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Walisisk (Storbrittania)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Dansk (Danmark)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Tysk (Tyskland)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Tysk (Belgia)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Gresk (Hellas)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Engelsk (vanlig)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Engelsk (Storbrittania)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Engelsk (USA)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Engelsk (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Engelsk (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spansk (Spania)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spansk (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Fransk (Frankrike)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Fransk (Belgia)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Fransk (Canada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Fransk (Sveits)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Gallisk (Spania)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Ungarsk (Ungarn)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Kroatisk (Kroatia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesisk (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irsk (Irland)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiensk (Italia)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Hollandsk (Nederland)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Hollandsk (Belgia)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norsk (Norge)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polsk (Polen)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugisisk (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugisisk (Brasil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russisk (Russland)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovensk (Slovenia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Svensk (Sverige)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Tyrkisk (Tyrkia)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Walloonsk (Belgia)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Forenklet kinesisk (Kina)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Tradisjonell kinesisk (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Ukjent" diff --git a/comp/src/gb.util/.lang/pl.po b/comp/src/gb.util/.lang/pl.po new file mode 100644 index 00000000..74aa6cdb --- /dev/null +++ b/comp/src/gb.util/.lang/pl.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:31 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabski (Egypt)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabski (Tunisia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bułgarski (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Kataloński (Catalonia, Spain)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Walijski (United Kingdom)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Czeski (Czech Republic)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Duński (Denmark)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Niemiecki (Germany)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Niemiecki (Belgium)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grecki (Greece)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Angielski (ogólny)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Angielski (United Kingdom)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Angielski (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Angielski (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Angielski (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Hiszpański (Spain)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Hiszpański (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estoński (Estonia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francuski (France)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francuski (Belgium)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francuski (Canada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francuski (Switzerland)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galicyjski (Spain)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebrajski (Izrael)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (Indie)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Węgierski (Hungary)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Chorwacki (Croatia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonezyjski (Indonesia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irlandzki (Ireland)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Włoski (Italy)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japoński (Japonia)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Mon-Khmer (Kambodża)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malajalam (Indie)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Holenderski (Netherlands)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Holenderski (Belgium)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norweski (Norway)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Pendżabski (Indie)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polski (Poland)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugalski (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugalski (Brazil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Rosyjski (Russia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Słoweński (Slovenia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albański (Albania)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Szwedzki (Sweden)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turecki (Turkey)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Wietnamski (Wietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Waloński (Belgium)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Uproszczony chiński (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Tradycyjny chiński (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Nieznany" diff --git a/comp/src/gb.util/.lang/pt.po b/comp/src/gb.util/.lang/pt.po new file mode 100644 index 00000000..adedb6c5 --- /dev/null +++ b/comp/src/gb.util/.lang/pt.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:31 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Árabe (Egipto)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Árabe (Tunisia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Búlgaro (Bulgária)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalão (Catalúnia, Espanha)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Welsh (Reino Unido)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Checo (República Checa)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Dinamarquês (Dinamarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Alemão (Alemanha)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Alemão (Bélgica)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grego (Grécia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Inglês (comun)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Inglês (Reino Unido)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Inglês (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Inglês (Austrália)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Inglês (Canadá)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (Qualquer lado!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Espanhol (Espanha)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Espanhol (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estónio (Estónia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (Irão)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francês (França)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francês (Bélgica)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francês (Canadá)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francês (Suíça)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galês (Espanha)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebraico (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindu (Índia)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Húngaro (Hungria)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croata (Croácia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonésio (Indonésia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irlandês (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiano (Itália)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japonês (Japão)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Camboja)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (Índia)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Holandês (Holanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Holandês (Bélgica)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norueguês (Noruega)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (Índia)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Poláco (Polónia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Português (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Português (Brasil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russo (Rússia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Eslovaco (Eslovênia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanês (Albânia)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Sérvio (Servia & Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Sueco (Suécia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turco (Turquia)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamita (Vietname)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Wallon (Bélgica)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Chinês simplificado (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Chinês tradicional (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Desconhecido" diff --git a/comp/src/gb.util/.lang/pt_BR.po b/comp/src/gb.util/.lang/pt_BR.po new file mode 100644 index 00000000..bebd02dc --- /dev/null +++ b/comp/src/gb.util/.lang/pt_BR.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:31 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Africanês (Áfica do Sul)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Árabe (Egito)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Árabe (Tunisia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbaijano (Azerbaijão)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Búlgaro (Bulgária)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalão (Catalúnia, Espanha)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Welsh (Reino Unido)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Tcheco (República Tcheca)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Dinamarquês (Dinamarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Alemão (Alemanha)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Alemão (Bélgica)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grego (Grécia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Inglês (comun)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Inglês (Reino Unido)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Inglês (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Inglês (Austrália)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Inglês (Canadá)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (Qualquer lugar!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "Espanhol (comum)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Espanhol (Espanha)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Espanhol (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estoniano (Estônia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Basco (País Basco)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (Irã)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finlandês (Finlândia)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francês (França)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francês (Bélgica)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francês (Canadá)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francês (Suíça)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galês (Espanha)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebraico (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (Índia)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Húngaro (Hungria)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croata (Croácia)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonésio (Indonésia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irlandês (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Islândico (Islândia)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiano (Itália)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japonês (Japão)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Camboja)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Corano (Coréia)" + +#: Language.class:116 +msgid "Latin" +msgstr "Latin" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Lituano (Lituânia)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malaiala (Índia)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macedônio (República da Macedónia)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Holandês (Holanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Holandês (Bélgica)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norueguês (Noruega)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Panjabi (Índia)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polonês (Polônia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Português (Portugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Português (Brasil)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valencian (Comunicade Valenciana, Espanha)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Romeno (Romania)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Russo (Rússia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovaco (Eslovênia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanês (Albânia)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Sérvio (Sérvia & Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Sueco (Suécia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turco (Turquia)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ucraniano (Ucrânia)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamita (Vietnã)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Wallon (Bélgica)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Chinês simplificado (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Chinês tradicional (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Desconhecido" diff --git a/comp/src/gb.util/.lang/ro.po b/comp/src/gb.util/.lang/ro.po new file mode 100644 index 00000000..4a4608f6 --- /dev/null +++ b/comp/src/gb.util/.lang/ro.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:32 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: ro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabică (Egipt)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabică (Tunisia)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbaidjană (Azerbaidjan)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgară (Bulgaria)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Catalană (Catalonia, Spania)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Galeză (Marea Britanie)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Cehă (Cehia)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Daneză (Danemarca)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Germană (Germania)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Germană (Belgia)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Greacă (Grecia)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Engleză (general)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Engleză (Marea Britanie)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Engleză (Statele Unite)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Engleză (Australia)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Engleză (Canada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (Oriunde!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spaniolă (Spania)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spaniolă (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estonă (Estonia)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Bască (Țara Bascilor)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Persană (Iran)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finlandeză (Finlanda)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Franceză (Franța)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Franceză (Belgia)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Franceză (Canasa)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Franceză (Elveția)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galiciană (Spania)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Ebraică (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindusă (India)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Maghiară (Ungaria)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Croată (Croația)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indoneziană (Indonezia)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irlandeză (Irlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italiană (Italia)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japoneză (Japonia)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmeră (Cambogia)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Coreeană (Coreea)" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (India)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Macedoneană (Macedonia)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Olandeză (Olanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Olandeză (Belgia)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norvegiană (Norvegia)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (India)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Poloneză (Polonia)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugheză (Portugalia)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugheză (Brazilia)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valenciană (Comunitatea Valenciană, Spania)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Română (România)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Rusă (Rusia)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovenă (Slovenia)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albaneză (Albania)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Sârbă (Serbia & Muntenegru)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Suedeză (Suedia)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turcă (Turcia)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Ucraineană (Ucraina)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnameză (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Valonă (Belgia)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Chineză simplificată (China)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Chineză tradițională (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Necunoscut" diff --git a/comp/src/gb.util/.lang/ru.po b/comp/src/gb.util/.lang/ru.po new file mode 100644 index 00000000..ac23f9f1 --- /dev/null +++ b/comp/src/gb.util/.lang/ru.po @@ -0,0 +1,318 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:32 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "&1 Б" + +#: File.class:24 +msgid "&1 KiB" +msgstr "&1 кБ" + +#: File.class:26 +msgid "&1 MiB" +msgstr "&1 МБ" + +#: File.class:28 +msgid "&1 GiB" +msgstr "&1 ГБ" + +#: File.class:34 +msgid "&1 KB" +msgstr "&1 Кб" + +#: File.class:36 +msgid "&1 MB" +msgstr "&1 МБ" + +#: File.class:38 +msgid "&1 GB" +msgstr "&1 ГБ" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Африкаанс (Южная Африка)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Арабский (Египет)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Арабский (Тунис)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Азербайджанский (Азербайджан)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Болгарский (Болгария)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Каталонский (Каталония, Испания)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Валлийский (Великобритания)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Чешский (Чехия)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Датский (Дания)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Немецкий (Германия)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Немецкий (Бельгия)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Греческий (Греция)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Английский (общий)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Английский (Великобритания)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Английский (США)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Английский (Австралия)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Английский (Канада)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Эсперанто (Где угодно!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "Испанский (общий)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Испанский (Испания)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Испанский (Аргентина)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Эстонский (Эстония)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Баскский (Страна Басков)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Фарси (Иран)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Финский (Финляндия)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Французский (Франция)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Французский (Бельгия)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Французский (Канада)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Французский (Швейцария)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Галицийский (Испания)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Иврит (Израиль)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Хинди (Индия)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Венгерский (Венгрия)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Хорватский (Хорватия)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Индонезийский (Индонезия)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Ирландский (Ирландия)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "Исландский (Исландия)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Итальянский (Италия)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Японский (Япония)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Кхмерский (Камбоджа)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Корейский (Корея)" + +#: Language.class:116 +msgid "Latin" +msgstr "Латиница" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Литовский (Литва)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Малаялам (Индия)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Македонский (Македония)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Голландский (Нидерланды)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Голландский (Бельгия)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Норвежский (Норвегия)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Панджаби (Индия)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Польский (Польша)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Португальский (Португалия)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Португальский (Бразилия)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Валенсийский (Валенсия, Испания)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Румынский (Румыния)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Русский (Россия)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Словенский (Словения)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Албанский (Албания)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Сербский (Сербия и Черногория)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Швецкий (Швеция)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Турецкий (Турция)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Украинский (Украина)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Вьетнамский (Вьетнам)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Валлонский (Бельгия)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Упрощённый китайский (Китай)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Традиционный китайский (Тайвань)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Неизвестно" diff --git a/comp/src/gb.util/.lang/sl.po b/comp/src/gb.util/.lang/sl.po new file mode 100644 index 00000000..96e6aa20 --- /dev/null +++ b/comp/src/gb.util/.lang/sl.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:32 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabski (Egipt)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabski (Tunizija)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bolgarski (Bolgarija)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalonski (Katalonija, Å panija)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Valižanski (Združeno kraljestvo)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danski (Danska)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "NemÅ¡ki (Nemčija)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "NemÅ¡ki (Belgija)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "GrÅ¡ki (Grčija)" + +#: Language.class:47 +msgid "English (common)" +msgstr "AngleÅ¡ki (sploÅ¡no)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "AngleÅ¡ki (Velika Britanija)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "AngleÅ¡ki (ZDA)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "AngleÅ¡ki (Avstralija)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "AngleÅ¡ki (Kanada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Å panski (Å panija)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Å panski (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Francoski (Francija)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Francoski (Belgija)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Francoski (Kanada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Francoski (Å vica)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galicijski (Å panija)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Madžarski (Madžarska)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "HrvaÅ¡ki (HrvaÅ¡ka)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonezijski (Indonezija)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irski (Irska)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italijanski (Italija)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "" + +#: Language.class:116 +msgid "Latin" +msgstr "Latin" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Nizozemski (Nizozemska)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Nizozemski (Belgija)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "NorveÅ¡ki (NorveÅ¡ka)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Poljski (Poljska)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugalski (Portugalska)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugalski (Brazilija)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Ruski (Rusija)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovenski (Slovenija)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Å vedski (Å vedska)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "TurÅ¡ki (Turčija)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Valonski (Belgija)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Poenostavljena kitajščina (Kitajska)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Tradicionalna kitajščina (Tajvan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Neznano" diff --git a/comp/src/gb.util/.lang/sv.po b/comp/src/gb.util/.lang/sv.po new file mode 100644 index 00000000..924c1718 --- /dev/null +++ b/comp/src/gb.util/.lang/sv.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:32 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arabiska (Egypten)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arabiska (Tunisien)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerbajan (Azerbaijan)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgariska (Bulgarien)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalanska(Katalonien, Spanien)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Walesiska (Storbrittanien)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Tjeckiska (Tjeckien)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danska (Danmark)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Tyska (Tyskland)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Tyska (Belgien)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Grekiska (Grekland)" + +#: Language.class:47 +msgid "English (common)" +msgstr "Engelska (Allmän)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "Engelska (Storbrittanien)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "Engelska (U.S.A.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "Engelska (Austarlien)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "Engelska (Kananda)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto (Överallt!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "Spanska (Spanien)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "Spanska (Argentina)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estniska (Estland)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Baskiska (Baskien)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsi (Iran)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "Finska (Finland)" + +#: Language.class:74 +msgid "French (France)" +msgstr "Franska (Frankrike)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Franska (Belgien)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Franska (Kanada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Franska (Schweitz)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galisiska(Spanien)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "Hebreiska (Israel)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hindi (Indien)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Ungerska (Ungern)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Kroatiska (Kroatioen)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Indonesiska (Indonesien)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "Irländska (Irland)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "Italienska (Italien)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japanska (Japan)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmer (Kambodja)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Koeranska (Korea)" + +#: Language.class:116 +msgid "Latin" +msgstr "Latin" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "Litauiska (Litauen)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malaysiska (Indien)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Makedonska (Republiken Makedonien)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Dutch (Nederländerna)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Dutch (Belgien)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norska (Norge)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi (Indien)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polska (polen)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portugisiska (Porugal)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portugisiska (Brasilien)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valensiska (Spanien)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "Rumänska (Rumänien)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Ryska (Ryssland)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovenska (Slovenien)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Albanska (Albaien)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Serbiska (Serbien & Montenegro)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "Svenska (Sverige)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Turkiska (Turkiet)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "Uktainska (Ukraina)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnamesiska (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Vallonska (Belgien)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Förenklad kinesiska (Kina)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Traditionell kinesiska (Taiwan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Okänd" diff --git a/comp/src/gb.util/.lang/tr.po b/comp/src/gb.util/.lang/tr.po new file mode 100644 index 00000000..5baab0b4 --- /dev/null +++ b/comp/src/gb.util/.lang/tr.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:32 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "&1 KB" + +#: File.class:26 +msgid "&1 MiB" +msgstr "&1 MB" + +#: File.class:28 +msgid "&1 GiB" +msgstr "&1 GB" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "Afrika (Güney Afrika)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "Arapça (Mısır)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "Arapça (Tunus)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "Azerice (Azerbaycan)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "Bulgarca (Bulgaristan)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "Katalanca (Katalan, İspanya)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "Gal dili (Birleşik Krallık)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "Çek (Çek Cumhuriyeti)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "Danimarka dili (Danimarka)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "Almanca (Almanya)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "Almanca (Belçika)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "Yunanca (Yunanistan)" + +#: Language.class:47 +msgid "English (common)" +msgstr "İngilizce (genel)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "İngilizce (Birleşik Krallık)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "İngilizce (A.B.D.)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "İngilizce (Avustralya)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "İngilizce (Kanada)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "Esperanto dili (Her yerde!)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "İspanyolca (genel)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "İspanyolca (İspanya)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "İspanyolca (Arjantin)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "Estonyaca (Estonya)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "Baskça (Bask Ülkesi)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "Farsça (İran)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "" + +#: Language.class:74 +msgid "French (France)" +msgstr "Fransızca (Fransa)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "Fransızca (Belçika)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "Fransızca (Kanada)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "Fransızca (İsviçre)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "Galiçya dili (İspanya)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "İbranice (İsrail)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "Hintçe (Hİndistan)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "Macarca (Macaristan)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "Hırvatça (Hırvatistan)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "Endonezya dili (Endonezya)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "İrlanda dili (İrlanda)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "İtalyanca (İtalya)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "Japonca (Japonya)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "Khmerce (Kamboçya)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "Korece (Kore)" + +#: Language.class:116 +msgid "Latin" +msgstr "" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "Malayalam (Hindistan)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "Makedonca (Makedonya Cumhuriyeti)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "Hollanda dili (Hollanda)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "Hollanda dili (Belçika)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "Norveççe (Norveç)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "Punjabi dili (Hindistan)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "Polonya dili (Polonya)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "Portekizce (Portekiz)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "Portekizce (Brezilya)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "Valensiyaca (Valensiya Halkı, İspanya)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "Rusça (Rusya)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "Slovence (Slovenya)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "Arnavutça (Arnavutluk)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "Sırpça (Sırbistan & Karadağ)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "İsveççe (İsveç)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "Türkçe (Türkiye)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "Vietnam dili (Vietnam)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "Valonca (Belçika)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "Basitleştirilmiş Çince (Çin)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "Geleneksel Çince (Tayvan)" + +#: Language.class:241 +msgid "Unknown" +msgstr "Bilinmeyen" diff --git a/comp/src/gb.util/.lang/zh.po b/comp/src/gb.util/.lang/zh.po new file mode 100644 index 00000000..0ff12322 --- /dev/null +++ b/comp/src/gb.util/.lang/zh.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:32 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "实用程序例程" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "&1 KB" + +#: File.class:26 +msgid "&1 MiB" +msgstr "&1 MB" + +#: File.class:28 +msgid "&1 GiB" +msgstr "&1 GB" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "南非荷兰语(南非)" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "阿拉伯语(埃及) " + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "阿拉伯语(突尼斯)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "阿塞拜疆语(阿塞拜疆)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "保加利亚语(保加利亚)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "加泰罗尼亚语(加泰罗尼亚,西班牙)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "威尔士语(英国)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "捷克语(捷克共和国)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "丹麦语(丹麦)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "德语(德国)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "德语(比利时)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "希腊语(希腊)" + +#: Language.class:47 +msgid "English (common)" +msgstr "英语(通用)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "英语(英国)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "英语(美国)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "英语(澳大利亚)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "英语(加拿大)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "世界语(任何地方)" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "西班牙语(普通的)" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "西班牙语(西班牙)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "西班牙语(阿根廷)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "爱沙尼亚语(爱沙尼亚)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "巴斯克语(巴斯克地区)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "波斯语(伊朗)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "芬兰语(芬兰)" + +#: Language.class:74 +msgid "French (France)" +msgstr "法语(法国)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "法语(比利时)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "法语(加拿大)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "法语(瑞士)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "加利西亚语(西班牙)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "希伯来语 (以色列)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "印地语(印度)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "匈牙利语(匈牙利)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "克罗地亚语(克罗地亚)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "印度尼西亚语 (印度尼西亚)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "爱尔兰语(爱尔兰)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "冰岛语 (冰岛)" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "意大利语(意大利)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "日本语(日本)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "高棉语(柬埔寨)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "朝鲜语(朝鲜)" + +#: Language.class:116 +msgid "Latin" +msgstr "拉丁语" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "立陶宛语(立陶宛)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "马拉雅拉姆语(印度)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "马其顿语(马其顿共和国)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "荷兰语(荷兰)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "荷兰语(比利时)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "挪威语(挪威)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "旁遮普语(印度)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "波兰语(波兰)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "葡萄牙语(葡萄牙) " + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "葡萄牙语(巴西) " + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "巴伦西亚语(巴伦西亚社区,西班牙)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "罗马尼亚语(罗马尼亚)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "俄语(俄罗斯)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "斯洛文尼亚语(斯洛文尼亚)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "阿尔巴尼亚语(阿尔巴尼亚)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "塞尔维亚语(塞尔维亚和黑山)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "瑞士语(瑞士)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "土耳其语(土耳其)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "乌克兰语(乌克兰)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "越语(越南)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "沃伦(比利时)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "简体中文(中华人民共和国)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "繁体中文(中华人民共和国台湾省)" + +#: Language.class:241 +msgid "Unknown" +msgstr "未知的" diff --git a/comp/src/gb.util/.lang/zh_TW.po b/comp/src/gb.util/.lang/zh_TW.po new file mode 100644 index 00000000..c70dae50 --- /dev/null +++ b/comp/src/gb.util/.lang/zh_TW.po @@ -0,0 +1,307 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.util 3.16.90\n" +"POT-Creation-Date: 2022-02-04 01:26 UTC\n" +"PO-Revision-Date: 2022-02-04 01:33 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Utility routines" +msgstr "" + +#: File.class:22 +msgid "&1 B" +msgstr "" + +#: File.class:24 +msgid "&1 KiB" +msgstr "" + +#: File.class:26 +msgid "&1 MiB" +msgstr "" + +#: File.class:28 +msgid "&1 GiB" +msgstr "" + +#: File.class:34 +msgid "&1 KB" +msgstr "" + +#: File.class:36 +msgid "&1 MB" +msgstr "" + +#: File.class:38 +msgid "&1 GB" +msgstr "" + +#: Language.class:14 +msgid "Afrikaans (South Africa)" +msgstr "" + +#: Language.class:17 +msgid "Arabic (Egypt)" +msgstr "阿拉伯語 (埃及)" + +#: Language.class:18 +msgid "Arabic (Tunisia)" +msgstr "阿拉伯語 (突尼西亞)" + +#: Language.class:21 +msgid "Azerbaijani (Azerbaijan)" +msgstr "亞塞拜然語 (亞塞拜然)" + +#: Language.class:24 +msgid "Bulgarian (Bulgaria)" +msgstr "保加利亞語 (保加利亞)" + +#: Language.class:27 +msgid "Catalan (Catalonia, Spain)" +msgstr "加泰隆尼亞語 (加泰隆尼亞,西班牙)" + +#: Language.class:31 +msgid "Welsh (United Kingdom)" +msgstr "威爾斯 (大英國協)" + +#: Language.class:34 +msgid "Czech (Czech Republic)" +msgstr "捷克語 (捷克共和國)" + +#: Language.class:37 +msgid "Danish (Denmark)" +msgstr "丹麥語 (丹麥)" + +#: Language.class:40 +msgid "German (Germany)" +msgstr "德文 (德國)" + +#: Language.class:41 +msgid "German (Belgium)" +msgstr "德文 (比利時)" + +#: Language.class:44 +msgid "Greek (Greece)" +msgstr "希臘語 (希臘)" + +#: Language.class:47 +msgid "English (common)" +msgstr "英文 (一般)" + +#: Language.class:48 +msgid "English (United Kingdom)" +msgstr "英文 (大英國協)" + +#: Language.class:49 +msgid "English (U.S.A.)" +msgstr "英文 (美國)" + +#: Language.class:50 +msgid "English (Australia)" +msgstr "英文 (澳洲)" + +#: Language.class:51 +msgid "English (Canada)" +msgstr "英文 (加拿大)" + +#: Language.class:54 +msgid "Esperanto (Anywhere!)" +msgstr "世界語" + +#: Language.class:57 +msgid "Spanish (common)" +msgstr "" + +#: Language.class:58 +msgid "Spanish (Spain)" +msgstr "西班牙語 (西班牙)" + +#: Language.class:59 +msgid "Spanish (Argentina)" +msgstr "西班牙語( 阿根庭)" + +#: Language.class:62 +msgid "Estonian (Estonia)" +msgstr "愛沙尼亞語 (愛沙尼亞)" + +#: Language.class:65 +msgid "Basque (Basque country)" +msgstr "巴斯克語 (巴斯克)" + +#: Language.class:68 +msgid "Farsi (Iran)" +msgstr "波斯語 (伊朗)" + +#: Language.class:71 +msgid "Finnish (Finland)" +msgstr "芬蘭語 (芬蘭)" + +#: Language.class:74 +msgid "French (France)" +msgstr "法文 (法國)" + +#: Language.class:75 +msgid "French (Belgium)" +msgstr "法文 (比利時)" + +#: Language.class:76 +msgid "French (Canada)" +msgstr "法文 (加拿大)" + +#: Language.class:77 +msgid "French (Switzerland)" +msgstr "法文 (瑞士)" + +#: Language.class:80 +msgid "Galician (Spain)" +msgstr "加里西亞語 (西班牙)" + +#: Language.class:83 +msgid "Hebrew (Israel)" +msgstr "希伯來文 (以色列)" + +#: Language.class:86 +msgid "Hindi (India)" +msgstr "印地語 (印度)" + +#: Language.class:89 +msgid "Hungarian (Hungary)" +msgstr "匈牙利語 (匈牙利)" + +#: Language.class:92 +msgid "Croatian (Croatia)" +msgstr "克羅埃西亞語 (克羅埃西亞)" + +#: Language.class:95 +msgid "Indonesian (Indonesia)" +msgstr "印尼語 (印尼)" + +#: Language.class:98 +msgid "Irish (Ireland)" +msgstr "愛爾蘭語 (愛爾蘭)" + +#: Language.class:101 +msgid "Icelandic (Iceland)" +msgstr "" + +#: Language.class:104 +msgid "Italian (Italy)" +msgstr "義大利文 (義大利)" + +#: Language.class:107 +msgid "Japanese (Japan)" +msgstr "日語 (日本)" + +#: Language.class:110 +msgid "Khmer (Cambodia)" +msgstr "高棉語 (柬埔寨)" + +#: Language.class:113 +msgid "Korean (Korea)" +msgstr "韓語 (韓國)" + +#: Language.class:116 +msgid "Latin" +msgstr "拉丁語" + +#: Language.class:119 +msgid "Lithuanian (Lithuania)" +msgstr "立陶宛語 (立陶宛)" + +#: Language.class:122 +msgid "Malayalam (India)" +msgstr "馬拉雅拉姆語 (印度)" + +#: Language.class:125 +msgid "Macedonian (Republic of Macedonia)" +msgstr "馬其頓 (馬其頓共和國)" + +#: Language.class:128 +msgid "Dutch (Netherlands)" +msgstr "荷蘭語 (荷蘭)" + +#: Language.class:129 +msgid "Dutch (Belgium)" +msgstr "荷蘭語 (比利時)" + +#: Language.class:132 +msgid "Norwegian (Norway)" +msgstr "挪威語 (挪威)" + +#: Language.class:135 +msgid "Punjabi (India)" +msgstr "旁遮普語 (印度)" + +#: Language.class:138 +msgid "Polish (Poland)" +msgstr "波蘭語 (波蘭)" + +#: Language.class:141 +msgid "Portuguese (Portugal)" +msgstr "葡萄牙語 (葡萄牙)" + +#: Language.class:142 +msgid "Portuguese (Brazil)" +msgstr "葡萄牙語 (巴西)" + +#: Language.class:145 +msgid "Valencian (Valencian Community, Spain)" +msgstr "巴倫西亞語 (巴倫西亞自治區, 西班牙)" + +#: Language.class:148 +msgid "Romanian (Romania)" +msgstr "羅馬尼亞語 (羅馬尼亞)" + +#: Language.class:151 +msgid "Russian (Russia)" +msgstr "俄文 (俄國)" + +#: Language.class:154 +msgid "Slovenian (Slovenia)" +msgstr "斯洛法尼亞語 (斯洛法尼亞)" + +#: Language.class:157 +msgid "Albanian (Albania)" +msgstr "阿爾巴尼亞語 (阿爾巴尼亞)" + +#: Language.class:160 +msgid "Serbian (Serbia & Montenegro)" +msgstr "塞爾維亞語 (塞爾維亞 & 蒙特內哥羅)" + +#: Language.class:163 +msgid "Swedish (Sweden)" +msgstr "瑞典語 (瑞典)" + +#: Language.class:166 +msgid "Turkish (Turkey)" +msgstr "土耳其語 (土耳其)" + +#: Language.class:169 +msgid "Ukrainian (Ukrain)" +msgstr "烏克蘭語 (烏克蘭)" + +#: Language.class:172 +msgid "Vietnamese (Vietnam)" +msgstr "越南語 (越南)" + +#: Language.class:175 +msgid "Wallon (Belgium)" +msgstr "華隆語 (比利時)" + +#: Language.class:178 +msgid "Simplified chinese (China)" +msgstr "簡體中文 (中國)" + +#: Language.class:179 +msgid "Traditional chinese (Taiwan)" +msgstr "繁體中文 (台灣)" + +#: Language.class:241 +msgid "Unknown" +msgstr "未知" diff --git a/comp/src/gb.util/.project b/comp/src/gb.util/.project new file mode 100644 index 00000000..0fb7a94e --- /dev/null +++ b/comp/src/gb.util/.project @@ -0,0 +1,11 @@ +# Gambas Project File 3.0 +Title=Utility routines +Startup=MMain +Version=3.17.90 +VersionFile=1 +TabSize=2 +Translate=1 +Language=en_US +Type=Component +ForceBytecodeVersion=3.8 +Packager=1 diff --git a/comp/src/gb.util/.src/Class.class b/comp/src/gb.util/.src/Class.class new file mode 100644 index 00000000..9d1a02ca --- /dev/null +++ b/comp/src/gb.util/.src/Class.class @@ -0,0 +1,127 @@ +' Gambas class file + +Export + +Static Private $iSectionSize As Integer +Static Private $iSectionPos As Integer + +Static Private Sub GotoNextSection(hFile As File) + + $iSectionPos += $iSectionSize + Seek #hFile, $iSectionPos + $iSectionSize = Read #hFile As Integer + $iSectionPos += 4 + +End + +Static Private Sub ReadZeroString(hFile As File) As String + + Dim sStr As String + Dim iPos As Integer + + Do + sStr &= Read #hFile, Min(16, Lof(hFile) - Seek(hFile)) + iPos = InStr(sStr, Chr$(0)) + If iPos Then Return Left(sStr, iPos - 1) + Loop + +End + +'' Return information about a class from its name, by analyzing its bytecode file. +'' +'' By default the class file is searched in the calling component or project. +'' If ~Name~ is prefixed with `../`, then the class file is searched one component up. +'' If ~Name~ is prefixed with '.../', then the class file is searched in the project. + +Static Public Sub Stat(Name As String) As ClassStat + + Dim sPath As String + Dim hFile As File + Dim hStat As ClassStat + Dim iVal As Integer + Dim iParent As Integer + Dim iFlag As Short + Dim bDebug As Boolean + Dim iStringPos As Integer + Dim sDir As String + + If Name Begins ".../" Then + + Name = Mid$(Name, 5) + sPath = ".../.gambas" &/ UCase(Name) + + Else + + While Name Begins "../" + Name = Mid(Name, 4) + sDir &= "../" + Wend + + sPath = ".." &/ sDir &/ ".gambas" &/ UCase(Name) + + Endif + + If Not Exist(sPath) Then Error.Raise("Class not found") + + $iSectionPos = 0 + $iSectionSize = 0 + + hStat = New ClassStat + hStat.Name = Name + + hFile = Open sPath + + Seek #hFile, 8 + iVal = Read #hFile As Integer + If iVal <> &H12345678 Then hFile.ByteOrder = 1 - hFile.ByteOrder + Seek #hFile, 12 + iVal = Read #hFile As Integer + bDebug = iVal And 1 + + $iSectionSize = 16 + + GotoNextSection(hFile) ' info + Seek #hFile, $iSectionPos + iParent = Read #hFile As Short + iFlag = Read #hFile As Short + + hStat.Exported = BTst(iFlag, 0) + hStat.AutoCreate = BTst(iFlag, 1) + hStat.Optional = BTst(iFlag, 2) + hStat.NoCreate = BTst(iFlag, 3) + hStat.HasFast = BTst(iFlag, 4) + hStat.Test = BTst(iFlag, 5) + + GotoNextSection(hFile) ' description + GotoNextSection(hFile) ' constant + GotoNextSection(hFile) ' reference + + If iParent <> -1 Then + + Seek #hFile, $iSectionPos + iParent * 4 + iParent = Read #hFile As Integer + iParent = Abs(iParent) + + Endif + + Do + iStringPos = $iSectionPos + Try GotoNextSection(hFile) + If Error Then Break + Loop + + Seek #hFile, iStringPos + hStat.Name = ReadZeroString(hFile) + + If iParent > 0 Then + + Seek #hFile, iStringPos + iParent + hStat.Parent = ReadZeroString(hFile) + + Endif + + Close hFile + + Return hStat + +End diff --git a/comp/src/gb.util/.src/ClassStat.class b/comp/src/gb.util/.src/ClassStat.class new file mode 100644 index 00000000..f94bbdce --- /dev/null +++ b/comp/src/gb.util/.src/ClassStat.class @@ -0,0 +1,12 @@ +' Gambas class file + +Export + +Public Name As String +Public Parent As String +Public Exported As Boolean +Public HasFast As Boolean +Public AutoCreate As Boolean +Public NoCreate As Boolean +Public Optional As Boolean +Public Test As Boolean diff --git a/comp/src/gb.util/.src/CsvFile.class b/comp/src/gb.util/.src/CsvFile.class new file mode 100644 index 00000000..ba8abec4 --- /dev/null +++ b/comp/src/gb.util/.src/CsvFile.class @@ -0,0 +1,332 @@ +' Gambas class file + +Export + +'' Return if the end of file has been reached. +Property Read Eof As Boolean + +'' Return the name of the columns +Property Fields, Columns As String[] + +'' Return the current line +Property Read Line As Integer + +'' @{since 3.17} +'' +'' Return or set the CSV file separator + +Property Separator As String Use $sSeparator + +'' @{since 3.17} +'' +'' Return or set the CSV file escape character. + +Property Escape As String Use $sEscape + +'' @{since 3.17} +'' +'' Return or set the CSV file charset. + +Property Charset As String Use $sCharset + +'' @{since 3.17} +'' +'' Return or set if field names diacritics are kept or not. + +Property NoDiacritics As Boolean Use $bNoDiacritics + +'' @{since 3.17} +'' +'' Return or set if field names are kept unchanged or not. +'' +'' If this property is set, the [../NoDiacritics] property is ignored. + +Property KeepNames As Boolean Use $bKeepNames + +Static Private $bCreate As Boolean + +Private $hFile As File +Private $aField As String[] +Private $iLine As Integer +Private $bWrite As Boolean + +'' Open a CSV file for reading and return it as a CsvFile object. +'' +'' - ~Path~ is the path of the file. +'' - ~Separator~ is the column separator, ',' by default. +'' - ~Escape~ is the escape character, '"' by default. +'' - ~Charset~ is the file charset. If not specified, it will be guessed. +'' +'' Field names are automatically formatted: trailing and double white spaces are removed, and characters are converted to lower case. +'' +'' If you want to keep the original field names, set the [../KeepNames] property just after the creation of the CsvFile object. + +Static Public Sub Open(Path As String, Optional Separator As String = ",", Optional Escape As String = "\"", Optional Charset As String) As CsvFile + + Return New CsvFile(Path, Separator, Escape, Charset) + +End + +'' Create a CSV file for writing and return it as a CsvFile object. +'' +'' - ~Path~ is the path of the file. +'' - ~Separator~ is the column separator, ',' by default. +'' - ~Escape~ is the escape character, '"' by default. +'' - ~Charset~ is the file charset. If not specified, it will be guessed. +'' +'' [[ warning +'' If another file with the same name already exists, it is erased. +'' ]] +'' +'' Field names are automatically formatted: trailing and double white spaces are removed, and characters are converted to lower case. +'' +'' If you want to keep the original field names, set the [../KeepNames] property just after the creation of the CsvFile object. + +Static Public Sub Create(Path As String, Optional Separator As String = ",", Optional Escape As String = "\"", Optional Charset As String) As CsvFile + + $bCreate = True + Return New CsvFile(Path, Separator, Escape, Charset) + +End + +'' Create a new CsvFile for reading. +'' +'' - ~Path~ is the path of the file. +'' - ~Separator~ is the column separator, ',' by default. +'' - ~Escape~ is the escape character, '"' by default. +'' - ~Charset~ is the file charset. If not specified, it will be guessed. +'' - ~NoDiacritics~ remove all diacritics from field names. +'' +'' Field names are automatically formatted: trailing and double white spaces are removed, and characters are converted to lower case. +'' +'' If you want to keep the original field names, set the [../KeepNames] property just after the creation of the CsvFile object. + +Public Sub _new(Path As String, Optional Separator As String = ",", Optional Escape As String = "\"", Optional Charset As String, Optional NoDiacritics As Boolean) + + $sSeparator = Separator + $sEscape = Escape + $sCharset = Charset + + If $bCreate Then + $bCreate = False + $bWrite = True + $hFile = Open Path For Output Create + Return + Endif + + If File.IsRelative(Path) Then + If Path Begins "./" Then + Path = "." & Path + Else If Path Not Begins "../" Then + Path = ".." &/ Path + Endif + Endif + + $bNoDiacritics = NoDiacritics + + $hFile = Open Path For Input + +End + +Private Sub ReadFields() + + Dim sLine As String + Dim iInd As Integer + Dim sField As String + + sLine = $hFile.ReadLine($sEscape) + + If Not $sCharset And If Not String.IsValid(sLine) Then + $sCharset = "WINDOWS-1252" + Try sLine = Conv(sLine, $sCharset, "UTF-8") + If Error Then + $sCharset = "ISO-8859-15" + Try sLine = Conv(sLine, $sCharset, "UTF-8") + If Error Then Error.Raise("Cannot guess charset") + Endif + Endif + + ' For all these stupid Windows softwares that put useless BOM in UTF-8 files. + If sLine Begins "\xEF\xBB\xBF" Then sLine = Mid$(sLine, 4) + + $aField = Split(sLine, $sSeparator, $sEscape) + + For iInd = 0 To $aField.Max + + If Not $bKeepNames Then + + sField = Trim($aField[iInd]) + + sField = Replace(sField, String.Chr(160), " ") + sField = Replace(sField, "\n", " ") + sField = Replace(sField, "\t", " ") + + While InStr(sField, " ") + sField = Replace(sField, " ", " ") + Wend + + sField = String.LCase(sField) + + Endif + + If $bNoDiacritics Then sField = String.RemoveDiacritics(sField) + + If Not sField Then sField = "#" & CStr(iInd) + $aField[iInd] = sField + Next + + $iLine = 1 + +End + +'' Read a row from the CSV file. +'' +'' The row is returned as a collection of column values indexed by the column name. + +Public Sub Read() As Collection + + Dim sLine As String + Dim aLine As String[] + Dim cCol As Collection + Dim iInd As Integer + + If Eof($hFile) Then Return + If Not $aField Then ReadFields + + cCol = New Collection(gb.IgnoreCase) + + Inc $iLine + sLine = $hFile.ReadLine($sEscape) + If $sCharset Then sLine = Conv(sLine, $sCharset, "UTF-8") + aLine = Split(sLine, $sSeparator, $sEscape) + + For iInd = 0 To Min($aField.Max, aLine.Max) + cCol[$aField[iInd]] = Trim(Replace(aLine[iInd], String.Chr(160), " ")) + Next + + Return cCol + +End + +Private Function Eof_Read() As Boolean + + Return Eof($hFile) + +End + +Private Function Fields_Read() As String[] + + If Not $aField Then ReadFields + Return $aField.Copy() + +End + +Private Function Line_Read() As Integer + + Return $iLine + +End + +Private Sub Fields_Write(Value As String[]) + + If Not $bWrite Then Error.Raise("Read-only property") + If Not Value Or If Value.Count < 1 Then Error.Raise("Bad number of columns") + $aField = Value.Copy() + +End + +Public Sub Close() + + Close #$hFile + +End + +'' @{since 3.15} +'' +'' Quote a string value for a CSV file. +'' +'' - ~Value~ if the string value to quote. +'' - ~Separator~ is the CSV file field separator. By default a comma. +'' - ~Escape~ is the CSV file escape character. By default a double quote. +'' +'' [[ info +'' `"\r"` characters are automatically removed when the string value has to be escaped, because it makes Microsoft Excel™ fail. +'' ]] + +Static Public Sub Quote(Value As String, Optional Separator As String = ",", Optional Escape As String = "\"") As String + + If InStr(Value, Separator) Or If InStr(Value, "\n") Or If InStr(Value, " ") Or If InStr(Value, Escape) Then + Return Escape & Replace(Replace(Value, "\r", ""), Escape, Escape & Escape) & Escape + Else + Return Value + Endif + +End + +Private Sub Print(sVal As String, Optional bNoSep As Boolean) + + If Not bNoSep Then Print #$hFile, ","; + + If InStr(sVal, $sSeparator) Or If InStr(sVal, "\n") Or If InStr(sVal, " ") Or If InStr(sVal, $sEscape) Then + If $sCharset Then + Print #$hFile, Conv($sEscape & Replace(Replace(sVal, "\r", ""), $sEscape, $sEscape & $sEscape) & $sEscape, "UTF-8", $sCharset); + Else + Print #$hFile, $sEscape; Replace(Replace(sVal, "\r", ""), $sEscape, $sEscape & $sEscape); $sEscape; + Endif + Else + If $sCharset Then + Print #$hFile, Conv(sVal, "UTF-8", $sCharset); + Else + Print #$hFile, sVal; + Endif + Endif + +End + +'' Write a new row of columns in the CSV file. +'' +'' - ~Line~ is the contents of the columns. +'' +'' Line can be either a string array of column values (following the [../Columns] order), or a collection of column values indexed by column names. + +Public Sub Write({Line} As Variant) + + Dim I As Integer + Dim cLine As Collection + Dim aLine As String[] + + If $iLine = 0 Then + + If Not $aField Or If $aField.Count < 1 Then Error.Raise("The Field property must be set first") + + Print($aField[0], True) + For I = 1 To $aField.Max + Print($aField[I]) + Next + Print #$hFile + Inc $iLine + + Endif + + Try cLine = {Line} + If cLine Then + Print(cLine[$aField[0]], True) + For I = 1 To $aField.Max + Print(cLine[$aField[I]]) + Next + Else + Try aLine = {Line} + If aLine Then + Print(aLine[0], True) + For I = 1 To $aField.Max + Print(aLine[I]) + Next + Else + Error.Raise("Type mismatch. Collection or Array expected") + Endif + Endif + + Print #$hFile + Inc $iLine + +End diff --git a/comp/src/gb.util/.src/Date.module b/comp/src/gb.util/.src/Date.module new file mode 100644 index 00000000..6583208b --- /dev/null +++ b/comp/src/gb.util/.src/Date.module @@ -0,0 +1,215 @@ +' Gambas module file + +Export + +Private $aDay As String[] +Private $aMonth As String[] +Private $dEpoch As Date + +Public Sub ToUnixTime({Date} As Date) As Long + + If Not $dEpoch Then $dEpoch = DateAdd(Date(1970, 1, 1), gb.Second, -System.TimeZone) + Return DateDiff($dEpoch, {Date}, gb.Second) + +End + +Public Sub FromUnixTime(UnixTime As Long) As Date + + If Not $dEpoch Then $dEpoch = DateAdd(Date(1970, 1, 1), gb.Second, -System.TimeZone) + Return $dEpoch + UnixTime / 86400 + +End + +' Gregorian algorithm by Aloysius Lilius And Christophorus Clavius. +' Valid For any year since 1583. + +Public Sub EasterDay(Year As Integer) As Date + + Dim A, B, C, D, E, F, G As Integer + + If Year < 1583 Then Error.Raise("Year must be greater or equal than 1583") + + A = Year Mod 19 + 1 + B = Year Div 100 + 1 + C = (3 * B) Div 4 - 12 + D = (8 * B + 5) Div 25 - 5 + E = (Year * 5) Div 4 - 10 - C + F = ((11 * A + 20 + D - C) Mod 30 + 30) Mod 30 + If F = 24 Or (F = 25 And A > 11) Then F = F + 1 + G = 44 - F + If G < 21 Then G = G + 30 + Return DateAdd(Date(Year, 3, 1), gb.Day, G + 7 - (E + G) Mod 7 - 1) + +End + +Private Sub InitDaysMonths() + + If Not $aDay Then + $aDay = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + $aMonth = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + Endif + +End + +Public Sub ToUTC({Date} As Date) As Date + + Return {Date} + System.TimeZone / 86400 + +End + +Public Sub FromUTC({Date} As Date) As Date + + Return {Date} - System.TimeZone / 86400 + +End + +Private Sub GetRFC822Zone(sZone As String) As Float + + Dim fZone As Float + + Select Case sZone + Case "UT", "GMT", "Z" + fZone = 0 + Case "EDT" + fZone = -4 + Case "EST", "CDT" + fZone = -5 + Case "CST", "MDT" + fZone = -6 + Case "MST", "PDT" + fZone = -7 + Case "PST" + fZone = -8 + Case Like "[A-I]" + fZone = -(Asc(sZone) - 64) + Case Like "[J-M]" + fZone = -(Asc(sZone) - 65) + Case Like "[N-Y]" + fZone = Asc(sZone) - 77 + Case Like "[+-][0-1][0-9][0-5][0-9]" + fZone = CInt(Left(sZone, 3)) + CInt(Mid$(sZone, 4)) / 60 + Case Else + Error.Raise(Subst$("Unknown timezone '&1'", sZone)) + End Select + + Return fZone / 24 + +End + +Public Sub ToRFC822({Date} As Date, Optional TimeZone As String = "GMT") As String + + InitDaysMonths + {Date} = ToUTC({Date}) + GetRFC822Zone(TimeZone) + Return $aDay[WeekDay({Date})] & ", " & Format(Day({Date}), "00") & " " & $aMonth[Month({Date}) - 1] & " " & Year({Date}) & " " & Format(Hour({Date}), "00") & ":" & Format(Minute({Date}), "00") & ":" & Format(Second({Date}), "00") & " " & TimeZone + +End + +Public Sub FromRFC822(Value As String, Optional ByRef TimeZone As String) As Date + + Dim aDate As String[] + Dim dDate As Date + Dim iPos As Integer + Dim iWeekDay As Integer + Dim iYear As Integer + + InitDaysMonths + Value = Trim(Value) + + iPos = InStr(Value, ", ") + If iPos Then + iWeekDay = $aDay.Find(Trim(Left(Value, iPos - 1))) + If iWeekDay < 0 Then Error.Raise("Unknown week day") + Value = Trim(Mid$(Value, iPos + 2)) + Else + iWeekDay = -1 + Endif + + aDate = Scan(Value, "* * * *:* *") + If aDate.Count <> 6 Then Return + + iPos = InStr(aDate[4], ":") + If iPos Then + aDate.Add(Mid$(aDate[4], iPos + 1), 5) + aDate[4] = Left(aDate[4], iPos - 1) + Else + aDate.Add("0", 5) + Endif + + iYear = CInt(aDate[2]) + If iYear >= 0 And If iYear <= 99 Then iYear += 1900 + dDate = Date(iYear, $aMonth.Find(aDate[1]) + 1, CInt(aDate[0]), CInt(aDate[3]), CInt(aDate[4]), CInt(aDate[5])) + If iWeekDay >= 0 And If WeekDay(dDate) <> iWeekDay Then Error.Raise("Incorrect week day") + + dDate = FromUTC(dDate) - GetRFC822Zone(aDate[6]) + + TimeZone = aDate[6] + Return dDate + +Catch + + If Error.Class.Name = "Date" Then Error.Propagate + Error.Raise("Not a RFC822 date format") + +End + +Public Sub IsWeekEnd({Date} As Date) As Boolean + + Dim iDay As Integer = WeekDay({Date}) + If iDay = gb.Sunday Or If iDay = gb.Saturday Then Return True + +End + +'' @{since 3.15} +'' +'' Convert a date to the ISO-8601 format. +'' +'' - ~Date~ is the date to convert. +'' - ~Timezone~ is the timezone of the date in RFC822 format. By default, the local timezone is used. + +Public Sub ToISO8601({Date} As Date, Optional Timezone As String) As String + + Dim iYear As Integer + Dim sResult As String + Dim sMicroseconds As String + Dim fTimeZone As Float + + If Not IsMissing(TimeZone) Then + fTimeZone = GetRFC822Zone(TimeZone) + Else + fTimeZone = -System.TimeZone / 86400 + Endif + + {Date} = ToUTC({Date}) + fTimeZone + + If fTimeZone > 0 Then + TimeZone = "+" & Format(CDate(fTimeZone), "hhnn") + Else If fTimeZone < 0 Then + TimeZone = "-" & Format(CDate(-fTimeZone), "hhnn") + Else + TimeZone = "Z" + Endif + + iYear = Year({Date}) + + If iYear Then + + If iYear < 0 Then sResult = "B" + sResult &= Format(iYear, "0000") & "-" & Format(Month({Date}), "00") & "-" & Format(Day({Date}), "00") + + Endif + + sResult &= "T" & Format({Date}, "hhnnss") + + sMicroseconds = Format({Date}, "uuu") + If sMicroseconds <> "000" Then sResult &= "," & sMicroseconds + + Return sResult & TimeZone + +End + +Public Sub DaysInMonth(Month As Integer, Optional Year As Integer = Year(Now)) As Integer + + If Month = 12 Then Return 31 + Return Day(DateAdd(Date(Year, Month + 1, 1), gb.Day, -1)) + +End diff --git a/comp/src/gb.util/.src/File.class b/comp/src/gb.util/.src/File.class new file mode 100644 index 00000000..768731e9 --- /dev/null +++ b/comp/src/gb.util/.src/File.class @@ -0,0 +1,42 @@ +' Gambas class file + +Export + +Static Private Sub FormatNumber(fNum As Float) As String + + Dim sStr As String + + sStr = Format(fNum, "0.##") + If String.Len(sStr) > 4 Then + sStr = String.Left(sStr, 4) + If Not IsDigit(Right(sStr)) Then sStr = Left(sStr, -1) + Endif + Return sStr + +End + +Static Public Sub FormatSize(Size As Long, Optional {Binary} As Boolean) As String + + If {Binary} Then + If Size < 1000 Then + Return Subst(("&1 B"), CStr(Size)) + Else If Size < 1024000 Then + Return Subst(("&1 KiB"), FormatNumber(Size / 1024)) + Else If Size < 1048576000 Then + Return Subst(("&1 MiB"), FormatNumber(Size / 1048576)) + Else + Return Subst(("&1 GiB"), FormatNumber(Size / 1073741824)) + Endif + Else + If Size < 1000 Then + Return Subst(("&1 B"), CStr(Size)) + Else If Size < 1000000 Then + Return Subst(("&1 KB"), FormatNumber(Size / 1000)) + Else If Size < 1000000000 Then + Return Subst(("&1 MB"), FormatNumber(Size / 1000000)) + Else + Return Subst(("&1 GB"), FormatNumber(Size / 1000000000)) + Endif + Endif + +End diff --git a/comp/src/gb.util/.src/Language.class b/comp/src/gb.util/.src/Language.class new file mode 100644 index 00000000..5354be28 --- /dev/null +++ b/comp/src/gb.util/.src/Language.class @@ -0,0 +1,395 @@ +' Gambas class file + +Export + +''' This class allows to deal with the translation of the current project. + +'' Return all known languages codes. +Static Property Read All As String[] + +'' Return the language key. +Property Read Key As String Use $sKey +'' Return the language code, suitable to [System.Language](/comp/gb/system/language). +Property Read Code As String +'' Return the translated name. +Property Read Name As String +'' Return the english name. +Property Read EnglishName As String +'' Return if the language is unknown +Property Read Unknown As Boolean + +Static Private $cName As Collection +Static Private $cEnglishName As Collection +Static Private $cCode As Collection + +Static Public Sub _lang() + + $cName = New Collection + $cEnglishName = New Collection + $cCode = New Collection + + $cName["af"] = ("Afrikaans (South Africa)") + $cEnglishName["af"] = "Afrikaans" + + $cName["ar"] = ("Arabic (Egypt)") + $cName["ar_TN"] = ("Arabic (Tunisia)") + $cName["ar_DZ"] = ("Arabic (Algeria)") + $cEnglishName["ar"] = "Arabic" + $cCode["ar"] = "ar_EG" + + $cName["az"] = ("Azerbaijani (Azerbaijan)") + $cEnglishName["az"] = "Azerbaijani" + + $cName["bg"] = ("Bulgarian (Bulgaria)") + $cEnglishName["bg"] = "Bulgarian" + + $cName["ca"] = ("Catalan (Catalonia, Spain)") + 'Name["ca_ES"] = ("Catalan (Catalonia, Spain)") + $cEnglishName["ca"] = "Catalan" + $cCode["ca"] = "ca_ES" + + $cName["cy"] = ("Welsh (United Kingdom)") + $cEnglishName["cy"] = "Welsh" + + $cName["cs"] = ("Czech (Czech Republic)") + $cEnglishName["cs"] = "Czech" + $cCode["cs"] = "cs_CZ" + + $cName["da"] = ("Danish (Denmark)") + $cEnglishName["da"] = "Danish" + + $cName["de"] = ("German (Germany)") + $cName["de_BE"] = ("German (Belgium)") + $cEnglishName["de"] = "German" + $cCode["de"] = "de_DE" + + $cName["el"] = ("Greek (Greece)") + $cEnglishName["el"] = "Greek" + + $cName["en"] = ("English (common)") + $cName["en_GB"] = ("English (United Kingdom)") + $cName["en_US"] = ("English (U.S.A.)") + $cName["en_AU"] = ("English (Australia)") + $cName["en_CA"] = ("English (Canada)") + $cName["en_IN"] = ("English (India)") + $cEnglishName["en"] = "English" + $cCode["en"] = "en_GB" + + $cName["eo"] = ("Esperanto (Anywhere!)") + $cEnglishName["eo"] = "Esperanto" + + $cName["es"] = ("Spanish (common)") + $cName["es_ES"] = ("Spanish (Spain)") + $cName["es_AR"] = ("Spanish (Argentina)") + $cEnglishName["es"] = "Spanish" + $cCode["es"] = "es_ES" + + $cName["et_EE"] = ("Estonian (Estonia)") + $cEnglishName["et"] = "Estonian" + + $cName["eu"] = ("Basque (Basque country)") + $cEnglishName["eu"] = "Basque" + + $cName["fa"] = ("Farsi (Iran)") + $cEnglishName["fa"] = "Farsi" + + $cName["fi"] = ("Finnish (Finland)") + $cEnglishName["fi"] = "Finnish" + $cCode["fi"] = "fi_FI" + + $cName["fr"] = ("French (France)") + $cName["fr_BE"] = ("French (Belgium)") + $cName["fr_CA"] = ("French (Canada)") + $cName["fr_CH"] = ("French (Switzerland)") + $cEnglishName["fr"] = "French" + $cCode["fr"] = "fr_FR" + + $cName["gl_ES"] = ("Galician (Spain)") + $cEnglishName["gl"] = "Galician" + + $cName["he"] = ("Hebrew (Israel)") + $cEnglishName["he"] = "Hebrew" + + $cName["hi"] = ("Hindi (India)") + $cEnglishName["hi"] = "Hindi" + + $cName["hu"] = ("Hungarian (Hungary)") + $cEnglishName["hu"] = "Hungarian" + + $cName["hr"] = ("Croatian (Croatia)") + $cEnglishName["hr"] = "Croatian" + + $cName["id"] = ("Indonesian (Indonesia)") + $cEnglishName["id"] = "Indonesian" + $cCode["id"] = "id_ID" + + $cName["ta"] = ("Tamil (India)") + $cEnglishName["ta"] = "Tamil" + $cCode["ta"] = "ta_IN" + + $cName["te"] = ("Telugu (India)") + $cEnglishName["te"] = "Assamese" + $cCode["te"] = "te_IN" + + $cName["ml"] = ("Malayalam (India)") + $cEnglishName["ml"] = "Malayalam" + $cCode["ml"] = "ml_IN" + + $cName["kn"] = ("Kannada (India)") + $cEnglishName["kn"] = "Kannada" + $cCode["kn"] = "kn_IN" + + $cName["hi"] = ("Hindi (India)") + $cEnglishName["hi"] = "Hindi" + $cCode["hi"] = "hi_IN" + + $cName["pa"] = ("Punjabi (India)") + $cEnglishName["pa"] = "Punjabi" + $cCode["pa"] = "pa_IN" + +' IndiaBanglabn-INbn-IND1093 +' IndiaBodobrx-INbrx-IND4096 +' IndiaChakmaccp-INccp-IND4096 +' IndiaGujaratigu-INgu-IND1095 +' IndiaKashmiriks-INks-IND4096 +' IndiaKonkanikok-INkok-IND1111 +' IndiaManipurimni-INmni-IND4096 +' IndiaMarathimr-INmr-IND1102 +' IndiaNepaline-INne-IND2145 +' IndiaOdiaor-INor-IND1096 +' IndiaSanskritsa-INsa-IND1103 +' IndiaSantalisat-INsat-IND4096 +' IndiaTibetanbo-INbo-IND4096 +' IndiaUrduur-INur-IND2080 + + $cName["ir"] = ("Irish (Ireland)") + $cEnglishName["ir"] = "Irish" + + $cName["is"] = ("Icelandic (Iceland)") + $cEnglishName["is"] = "Icelandic" + + $cName["it"] = ("Italian (Italy)") + $cName["it_CH"] = ("Italian (Switzerland)") + $cEnglishName["it"] = "Italian" + $cCode["it"] = "it_IT" + + $cName["ja"] = ("Japanese (Japan)") + $cEnglishName["ja"] = "Japanase" + + $cName["km"] = ("Khmer (Cambodia)") + $cEnglishName["km"] = "Khmer" + + $cName["ko"] = ("Korean (Korea)") + $cEnglishName["ko"] = "Korean" + $cCode["ko"] = "ko_KR" + + $cName["la"] = ("Latin") + $cEnglishName["la"] = "Latin" + + $cName["lt"] = ("Lithuanian (Lithuania)") + $cEnglishName["lt"] = "Lithuanian" + + $cName["mk"] = ("Macedonian (Republic of Macedonia)") + $cEnglishName["mk"] = "Macedonian" + + $cName["nl"] = ("Dutch (Netherlands)") + $cName["nl_BE"] = ("Dutch (Belgium)") + $cEnglishName["nl"] = "Dutch" + $cCode["nl"] = "nl_NL" + + $cName["no"] = ("Norwegian (Norway)") + $cName["nb_NO"] = ("BokmÃ¥l (Norway)") + $cEnglishName["no"] = "Norwegian" + $cCode["no"] = "nb_NO" + + $cName["pl"] = ("Polish (Poland)") + $cEnglishName["pl"] = "Polish" + + $cName["pt"] = ("Portuguese (Portugal)") + $cName["pt_BR"] = ("Portuguese (Brazil)") + $cEnglishName["pt"] = "Portuguese" + + $cName["qcv_ES"] = ("Valencian (Valencian Community, Spain)") + $cEnglishName["qcv"] = "Valencian" + + $cName["ro"] = ("Romanian (Romania)") + $cEnglishName["ro"] = "Romanian" + + $cName["ru"] = ("Russian (Russia)") + $cEnglishName["ru"] = "Russian" + $cCode["ru"] = "ru_RU" + + $cName["sl"] = ("Slovenian (Slovenia)") + $cEnglishName["sl"] = "Slovenian" + + $cName["sq"] = ("Albanian (Albania)") + $cEnglishName["sq"] = "Albanian" + + $cName["sr"] = ("Serbian (Serbia & Montenegro)") + $cEnglishName["sr"] = "Serbian" + + $cName["sv"] = ("Swedish (Sweden)") + $cEnglishName["sv"] = "Swedish" + + $cName["tr"] = ("Turkish (Turkey)") + $cEnglishName["tr"] = "Turkish" + $cCode["tr"] = "tr_TR" + + $cName["uk"] = ("Ukrainian (Ukrain)") + $cEnglishName["uk"] = "Ukrainian" + + $cName["vi"] = ("Vietnamese (Vietnam)") + $cEnglishName["vi"] = "Vietnamese" + + $cName["wa"] = ("Wallon (Belgium)") + $cEnglishName["wa"] = "Wallon" + + $cName["zh"] = ("Simplified chinese (China)") + $cName["zh_TW"] = ("Traditional chinese (Taiwan)") + $cEnglishName["zh"] = "Chinese" + $cCode["zh"] = "zh_CN" + +End + +Static Public Sub _get(Code As String) As Language + + Return New Language(Code) + +End + + +'' Find a language from its code or its translated name and returns it. +'' +'' * ~Language~ is the code or name to search. If not specified, [System.Language](/comp/gb/system/language) is used. +'' +'' If the language is not found, NULL is returned. + +Static Public Sub Find(Optional Language As String) As Language + + Dim sName As String + Dim sStr As String + Dim sKey As String + Dim iPos As Integer + Dim sLang As String + + sLang = Language + If Not sLang Then sLang = System.Language + + sName = $cName[sLang] + If Not sName Then + iPos = InStr(sLang, ".") + If iPos Then + sLang = Left(sLang, iPos - 1) + sName = $cName[sLang] + If Not sName Then + iPos = InStr(sLang, "_") + If iPos Then + sLang = Left(sLang, iPos - 1) + sName = $cName[sLang] + Endif + Endif + Endif + Endif + + If sName Then + + sKey = Language + + Else + + For Each sStr In $cName + If String.Comp(sStr, Language, gb.IgnoreCase) = 0 Then + sKey = $cName.Key + Break + Endif + Next + + Endif + + If sKey Then Return New Language(sKey) + +End + +Public Sub _new(Language As String) + + $sKey = Language + +End + +Private Function Name_Read() As String + + Dim sName As String + + sName = $cName[$sKey] + If Not sName Then sName = ("Unknown") & " (" & $sKey & ")" + Return sName + +End + +Private Function EnglishName_Read() As String + + Dim sKey As String + Dim iPos As Integer + Dim sName As Variant + + sKey = $sKey + iPos = InStr(sKey, "_") + If iPos Then sKey = Left(sKey, iPos - 1) + + sName = $cEnglishName[sKey] + If Not sName Then sName = "Unknown (" & sKey & ")" + Return sName + +End + +'' Return the translations of the current projet or library as a list of language codes. +'' +'' * ~Dir~ : Allow to override the directory where the translation files are searched for. + +Static Public Sub GetTranslations(Optional Dir As String) As String[] + + Dim aLang As New String[] + Dim sStr As String + + If Dir Then + Dir &/= ".lang" + Else + Dir = "../.lang" + Endif + + For Each sStr In Dir(Dir, "*.mo") + aLang.Add(File.BaseName(sStr)) + Next + + Return aLang.Sort() + +End + +Static Private Function All_Read() As String[] + + Dim aLang As New String[] + Dim sStr As String + + For Each sStr In $cName + aLang.Add($cName.Key) + Next + + Return aLang.Sort() + +End + +Private Function Unknown_Read() As Boolean + + Return $cName.Exist($sKey) + +End + +Private Function Code_Read() As String + + Dim sCode As String + + sCode = $cCode[$sKey] + If Not sCode Then sCode = $sKey + Return sCode & ".utf-8" + +End diff --git a/comp/src/gb.util/.src/MMain.module b/comp/src/gb.util/.src/MMain.module new file mode 100644 index 00000000..e966fb78 --- /dev/null +++ b/comp/src/gb.util/.src/MMain.module @@ -0,0 +1,69 @@ +' Gambas module file + +' Public Sub Main() +' +' Dim I As Integer +' Dim fTimer As Float +' Dim sData As String +' +' sData = File.Load("~/.local/share/gambas3/wiki/data/en/doc/object-model/~page") +' +' fTimer = Timer +' For I = 1 To 1000 +' String.RemoveDiacritics(sData) +' Next +' Print Timer - fTimer +' +' End + +Public Sub Main() + + 'Print String.PadLeft("Gambas", 16, "×->") + 'Print String.PadRight("Gambas", 16, "×->") + 'Print Now;; Date.ToISO8601(Now);; Date.ToISO8601(Now, "-0200") + + 'Print DateAdd(Date.EasterDay(2020), gb.Day, 40) + + ' Print String.ToPhonetic("Le comte (de Monte-Cristo) ; le conte (du Petit Chaperon Rouge) ; le compte (en banque)", "fr_FR") + ' + ' Print String.ToPhonetic("Dans cet antre, lassés de gêner au palais", "fr_FR") + ' Print String.ToPhonetic("Dansait, entrelacés, deux généraux pas laids", "fr_FR") + ' + ' Print String.ToPhonetic("Elle sort là-bas des menthes, la belle Eve à l'âme hantée", "fr_FR") + ' Print String.ToPhonetic("Et le sort l'abat, démente, l'abbé laid va lamenter", "fr_FR") + +End + +Public Sub RFC822Test() + + Dim sTimeZone As String + + ' Must be correct in the local timezone + Print CStr(Now) + Print Format(Now) + Print CStr(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000")) + Print Format$(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000")) + Print Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100") + Print Date.FromRFC822("Tue, 1 Jan 2019 00:00:00 +0800") + Print Date.ToRFC822(Now) + Print Date.ToRFC822(Now, "+0100") + Print "---" + ' Timezone to-from conversion should be the identity mapping + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000"), "+0000") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000"), "+0100") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000"), "+0800") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100"), "+0000") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100"), "+0100") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100"), "+0800") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0800"), "+0000") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0800"), "+0100") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0800"), "+0800") + Print "---" + ' Ability to extract timezone + Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100", ByRef sTimeZone) + Print sTimeZone + ' 21 Apr 2019 is not a Wednesday + Try Date.FromRFC822("Wed, 21 Apr 2019 05:00:00 +0100") + If Error Then Print Error.Text + +End diff --git a/comp/src/gb.util/.src/MPhonetic_English.module b/comp/src/gb.util/.src/MPhonetic_English.module new file mode 100644 index 00000000..7d169be6 --- /dev/null +++ b/comp/src/gb.util/.src/MPhonetic_English.module @@ -0,0 +1,876 @@ +' Gambas module file + +Public Sub Before(sStr As String) As String + + Return sStr + +End + +Public Sub Run((sStr) As String) As String + ' //////////////////////////////////////////////////////////////////////////////// + ' // Double Metaphone (c) 1998, 1999 by Lawrence Philips + ' // + ' // Slightly modified by Kevin Atkinson to fix several bugs and + ' // to allow it to give back more than 4 characters. + ' // + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' #include "stdafx.h" + ' #include "dmetaph.h" + ' + ' #define AND && + ' #define OR || + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' MString::MString() + ' { + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' MString::MString(const char* in) : CString(in) + ' { + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' MString::MString(const CString& in) : CString(in) + ' { + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' bool MString::SlavoGermanic() + ' { + ' if((Find('W') > -1) OR (Find('K') > -1) OR (Find("CZ") > -1) OR (Find("WITZ") > -1)) + ' return TRUE; + ' + ' return FALSE; + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' inline void MString::MetaphAdd(const char* main) + ' { + ' if(*main) + ' { + ' primary += main; + ' secondary += main; + ' } + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' inline void MString::MetaphAdd(const char* main, const char* alt) + ' { + ' if(*main) + ' primary += main; + ' if(*alt) + ' { + ' alternate = TRUE; + ' if(alt[0] != ' ') + ' secondary += alt; + ' }else + ' if(*main AND (main[0] != ' ')) + ' secondary += main; + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' bool MString::IsVowel(int at) + ' { + ' + ' if((at < 0) OR (at >= length)) + ' return false; + ' + ' char it = GetAt(at); + ' + ' if((it == 'A') OR (it == 'E') OR (it == 'I') OR (it == 'O') OR (it == 'U') OR (it == 'Y') ) + ' return true; + ' + ' return false; + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // + ' //////////////////////////////////////////////////////////////////////////////// + ' bool MString::StringAt(int start, int length, ... ) + ' { + ' + ' if (start < 0) return FALSE; + ' + ' char buffer[64]; + ' char* test; + ' CString target; + ' + ' test = buffer; + ' target = Mid(start, length); + ' + ' va_list sstrings; + ' va_start(sstrings, length); + ' + ' do + ' { + ' test = va_arg(sstrings, char*); + ' if(*test AND (target == test)) + ' return true; + ' + ' }while(strcmp(test, "")); + ' + ' va_end(sstrings); + ' + ' return false; + ' } + ' + ' //////////////////////////////////////////////////////////////////////////////// + ' // main deal + ' //////////////////////////////////////////////////////////////////////////////// + ' void MString::DoubleMetaphone(CString &metaph, CString &metaph2) + ' { + ' + ' int current = 0; + ' + ' length = GetLength(); + ' if(length < 1) + ' return; + ' last = length - 1;//zero based index + ' + ' alternate = FALSE; + ' + ' MakeUpper(); + ' + ' //pad the original string so that we can index beyond the edge of the world + ' Insert(GetLength(), " "); + ' + ' //skip these when at start of word + ' if(StringAt(0, 2, "GN", "KN", "PN", "WR", "PS", "")) + ' current += 1; + ' + ' //Initial 'X' is pronounced 'Z' e.g. 'Xavier' + ' if(GetAt(0) == 'X') + ' { + ' MetaphAdd("S"); //'Z' maps to 'S' + ' current += 1; + ' } + ' + ' ///////////main loop////////////////////////// + ' while(TRUE OR (primary.GetLength() < 4) OR (secondary.GetLength() < 4)) + ' { + ' if(current >= length) + ' break; + ' + ' switch(GetAt(current)) + ' { + ' case 'A': + ' case 'E': + ' case 'I': + ' case 'O': + ' case 'U': + ' case 'Y': + ' if(current == 0) + ' //all init vowels now map to 'A' + ' MetaphAdd("A"); + ' current +=1; + ' break; + ' + ' case 'B': + ' + ' //"-mb", e.g", "dumb", already skipped over... + ' MetaphAdd("P"); + ' + ' if(GetAt(current + 1) == 'B') + ' current +=2; + ' else + ' current +=1; + ' break; + ' + ' case 'Ç': + ' MetaphAdd("S"); + ' current += 1; + ' break; + ' + ' case 'C': + ' //various germanic + ' if((current > 1) + ' AND !IsVowel(current - 2) + ' AND StringAt((current - 1), 3, "ACH", "") + ' AND ((GetAt(current + 2) != 'I') AND ((GetAt(current + 2) != 'E') + ' OR StringAt((current - 2), 6, "BACHER", "MACHER", "")) )) + ' { + ' MetaphAdd("K"); + ' current +=2; + ' break; + ' } + ' + ' //special case 'caesar' + ' if((current == 0) AND StringAt(current, 6, "CAESAR", "")) + ' { + ' MetaphAdd("S"); + ' current +=2; + ' break; + ' } + ' + ' //italian 'chianti' + ' if(StringAt(current, 4, "CHIA", "")) + ' { + ' MetaphAdd("K"); + ' current +=2; + ' break; + ' } + ' + ' if(StringAt(current, 2, "CH", "")) + ' { + ' //find 'michael' + ' if((current > 0) AND StringAt(current, 4, "CHAE", "")) + ' { + ' MetaphAdd("K", "X"); + ' current +=2; + ' break; + ' } + ' + ' //greek roots e.g. 'chemistry', 'chorus' + ' if((current == 0) + ' AND (StringAt((current + 1), 5, "HARAC", "HARIS", "") + ' OR StringAt((current + 1), 3, "HOR", "HYM", "HIA", "HEM", "")) + ' AND !StringAt(0, 5, "CHORE", "")) + ' { + ' MetaphAdd("K"); + ' current +=2; + ' break; + ' } + ' + ' //germanic, greek, or otherwise 'ch' for 'kh' sound + ' if((StringAt(0, 4, "VAN ", "VON ", "") OR StringAt(0, 3, "SCH", "")) + ' // 'architect but not 'arch', 'orchestra', 'orchid' + ' OR StringAt((current - 2), 6, "ORCHES", "ARCHIT", "ORCHID", "") + ' OR StringAt((current + 2), 1, "T", "S", "") + ' OR ((StringAt((current - 1), 1, "A", "O", "U", "E", "") OR (current == 0)) + ' //e.g., 'wachtler', 'wechsler', but not 'tichner' + ' AND StringAt((current + 2), 1, "L", "R", "N", "M", "B", "H", "F", "V", "W", " ", ""))) + ' { + ' MetaphAdd("K"); + ' }else{ + ' if(current > 0) + ' { + ' if(StringAt(0, 2, "MC", "")) + ' //e.g., "McHugh" + ' MetaphAdd("K"); + ' else + ' MetaphAdd("X", "K"); + ' }else + ' MetaphAdd("X"); + ' } + ' current +=2; + ' break; + ' } + ' //e.g, 'czerny' + ' if(StringAt(current, 2, "CZ", "") AND !StringAt((current - 2), 4, "WICZ", "")) + ' { + ' MetaphAdd("S", "X"); + ' current += 2; + ' break; + ' } + ' + ' //e.g., 'focaccia' + ' if(StringAt((current + 1), 3, "CIA", "")) + ' { + ' MetaphAdd("X"); + ' current += 3; + ' break; + ' } + ' + ' //double 'C', but not if e.g. 'McClellan' + ' if(StringAt(current, 2, "CC", "") AND !((current == 1) AND (GetAt(0) == 'M'))) + ' //'bellocchio' but not 'bacchus' + ' if(StringAt((current + 2), 1, "I", "E", "H", "") AND !StringAt((current + 2), 2, "HU", "")) + ' { + ' //'accident', 'accede' 'succeed' + ' if(((current == 1) AND (GetAt(current - 1) == 'A')) + ' OR StringAt((current - 1), 5, "UCCEE", "UCCES", "")) + ' MetaphAdd("KS"); + ' //'bacci', 'bertucci', other italian + ' else + ' MetaphAdd("X"); + ' current += 3; + ' break; + ' }else{//Pierce's rule + ' MetaphAdd("K"); + ' current += 2; + ' break; + ' } + ' + ' if(StringAt(current, 2, "CK", "CG", "CQ", "")) + ' { + ' MetaphAdd("K"); + ' current += 2; + ' break; + ' } + ' + ' if(StringAt(current, 2, "CI", "CE", "CY", "")) + ' { + ' //italian vs. english + ' if(StringAt(current, 3, "CIO", "CIE", "CIA", "")) + ' MetaphAdd("S", "X"); + ' else + ' MetaphAdd("S"); + ' current += 2; + ' break; + ' } + ' + ' //else + ' MetaphAdd("K"); + ' + ' //name sent in 'mac caffrey', 'mac gregor + ' if(StringAt((current + 1), 2, " C", " Q", " G", "")) + ' current += 3; + ' else + ' if(StringAt((current + 1), 1, "C", "K", "Q", "") + ' AND !StringAt((current + 1), 2, "CE", "CI", "")) + ' current += 2; + ' else + ' current += 1; + ' break; + ' + ' case 'D': + ' if(StringAt(current, 2, "DG", "")) + ' if(StringAt((current + 2), 1, "I", "E", "Y", "")) + ' { + ' //e.g. 'edge' + ' MetaphAdd("J"); + ' current += 3; + ' break; + ' }else{ + ' //e.g. 'edgar' + ' MetaphAdd("TK"); + ' current += 2; + ' break; + ' } + ' + ' if(StringAt(current, 2, "DT", "DD", "")) + ' { + ' MetaphAdd("T"); + ' current += 2; + ' break; + ' } + ' + ' //else + ' MetaphAdd("T"); + ' current += 1; + ' break; + ' + ' case 'F': + ' if(GetAt(current + 1) == 'F') + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("F"); + ' break; + ' + ' case 'G': + ' if(GetAt(current + 1) == 'H') + ' { + ' if((current > 0) AND !IsVowel(current - 1)) + ' { + ' MetaphAdd("K"); + ' current += 2; + ' break; + ' } + ' + ' if(current < 3) + ' { + ' //'ghislane', ghiradelli + ' if(current == 0) + ' { + ' if(GetAt(current + 2) == 'I') + ' MetaphAdd("J"); + ' else + ' MetaphAdd("K"); + ' current += 2; + ' break; + ' } + ' } + ' //Parker's rule (with some further refinements) - e.g., 'hugh' + ' if(((current > 1) AND StringAt((current - 2), 1, "B", "H", "D", "") ) + ' //e.g., 'bough' + ' OR ((current > 2) AND StringAt((current - 3), 1, "B", "H", "D", "") ) + ' //e.g., 'broughton' + ' OR ((current > 3) AND StringAt((current - 4), 1, "B", "H", "") ) ) + ' { + ' current += 2; + ' break; + ' }else{ + ' //e.g., 'laugh', 'McLaughlin', 'cough', 'gough', 'rough', 'tough' + ' if((current > 2) + ' AND (GetAt(current - 1) == 'U') + ' AND StringAt((current - 3), 1, "C", "G", "L", "R", "T", "") ) + ' { + ' MetaphAdd("F"); + ' }else + ' if((current > 0) AND GetAt(current - 1) != 'I') + ' MetaphAdd("K"); + ' + ' current += 2; + ' break; + ' } + ' } + ' + ' if(GetAt(current + 1) == 'N') + ' { + ' if((current == 1) AND IsVowel(0) AND !SlavoGermanic()) + ' { + ' MetaphAdd("KN", "N"); + ' }else + ' //not e.g. 'cagney' + ' if(!StringAt((current + 2), 2, "EY", "") + ' AND (GetAt(current + 1) != 'Y') AND !SlavoGermanic()) + ' { + ' MetaphAdd("N", "KN"); + ' }else + ' MetaphAdd("KN"); + ' current += 2; + ' break; + ' } + ' + ' //'tagliaro' + ' if(StringAt((current + 1), 2, "LI", "") AND !SlavoGermanic()) + ' { + ' MetaphAdd("KL", "L"); + ' current += 2; + ' break; + ' } + ' + ' //-ges-,-gep-,-gel-, -gie- at beginning + ' if((current == 0) + ' AND ((GetAt(current + 1) == 'Y') + ' OR StringAt((current + 1), 2, "ES", "EP", "EB", "EL", "EY", "IB", "IL", "IN", "IE", "EI", "ER", "")) ) + ' { + ' MetaphAdd("K", "J"); + ' current += 2; + ' break; + ' } + ' + ' // -ger-, -gy- + ' if((StringAt((current + 1), 2, "ER", "") OR (GetAt(current + 1) == 'Y')) + ' AND !StringAt(0, 6, "DANGER", "RANGER", "MANGER", "") + ' AND !StringAt((current - 1), 1, "E", "I", "") + ' AND !StringAt((current - 1), 3, "RGY", "OGY", "") ) + ' { + ' MetaphAdd("K", "J"); + ' current += 2; + ' break; + ' } + ' + ' // italian e.g, 'biaggi' + ' if(StringAt((current + 1), 1, "E", "I", "Y", "") OR StringAt((current - 1), 4, "AGGI", "OGGI", "")) + ' { + ' //obvious germanic + ' if((StringAt(0, 4, "VAN ", "VON ", "") OR StringAt(0, 3, "SCH", "")) + ' OR StringAt((current + 1), 2, "ET", "")) + ' MetaphAdd("K"); + ' else + ' //always soft if french ending + ' if(StringAt((current + 1), 4, "IER ", "")) + ' MetaphAdd("J"); + ' else + ' MetaphAdd("J", "K"); + ' current += 2; + ' break; + ' } + ' + ' if(GetAt(current + 1) == 'G') + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("K"); + ' break; + ' + ' case 'H': + ' //only keep if first & before vowel or btw. 2 vowels + ' if(((current == 0) OR IsVowel(current - 1)) + ' AND IsVowel(current + 1)) + ' { + ' MetaphAdd("H"); + ' current += 2; + ' }else//also takes care of 'HH' + ' current += 1; + ' break; + ' + ' case 'J': + ' //obvious spanish, 'jose', 'san jacinto' + ' if(StringAt(current, 4, "JOSE", "") OR StringAt(0, 4, "SAN ", "") ) + ' { + ' if(((current == 0) AND (GetAt(current + 4) == ' ')) OR StringAt(0, 4, "SAN ", "") ) + ' MetaphAdd("H"); + ' else + ' { + ' MetaphAdd("J", "H"); + ' } + ' current +=1; + ' break; + ' } + ' + ' if((current == 0) AND !StringAt(current, 4, "JOSE", "")) + ' MetaphAdd("J", "A");//Yankelovich/Jankelowicz + ' else + ' //spanish pron. of e.g. 'bajador' + ' if(IsVowel(current - 1) + ' AND !SlavoGermanic() + ' AND ((GetAt(current + 1) == 'A') OR (GetAt(current + 1) == 'O'))) + ' MetaphAdd("J", "H"); + ' else + ' if(current == last) + ' MetaphAdd("J", " "); + ' else + ' if(!StringAt((current + 1), 1, "L", "T", "K", "S", "N", "M", "B", "Z", "") + ' AND !StringAt((current - 1), 1, "S", "K", "L", "")) + ' MetaphAdd("J"); + ' + ' if(GetAt(current + 1) == 'J')//it could happen! + ' current += 2; + ' else + ' current += 1; + ' break; + ' + ' case 'K': + ' if(GetAt(current + 1) == 'K') + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("K"); + ' break; + ' + ' case 'L': + ' if(GetAt(current + 1) == 'L') + ' { + ' //spanish e.g. 'cabrillo', 'gallegos' + ' if(((current == (length - 3)) + ' AND StringAt((current - 1), 4, "ILLO", "ILLA", "ALLE", "")) + ' OR ((StringAt((last - 1), 2, "AS", "OS", "") OR StringAt(last, 1, "A", "O", "")) + ' AND StringAt((current - 1), 4, "ALLE", "")) ) + ' { + ' MetaphAdd("L", " "); + ' current += 2; + ' break; + ' } + ' current += 2; + ' }else + ' current += 1; + ' MetaphAdd("L"); + ' break; + ' + ' case 'M': + ' if((StringAt((current - 1), 3, "UMB", "") + ' AND (((current + 1) == last) OR StringAt((current + 2), 2, "ER", ""))) + ' //'dumb','thumb' + ' OR (GetAt(current + 1) == 'M') ) + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("M"); + ' break; + ' + ' case 'N': + ' if(GetAt(current + 1) == 'N') + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("N"); + ' break; + ' + ' case 'Ñ': + ' current += 1; + ' MetaphAdd("N"); + ' break; + ' + ' case 'P': + ' if(GetAt(current + 1) == 'H') + ' { + ' MetaphAdd("F"); + ' current += 2; + ' break; + ' } + ' + ' //also account for "campbell", "raspberry" + ' if(StringAt((current + 1), 1, "P", "B", "")) + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("P"); + ' break; + ' + ' case 'Q': + ' if(GetAt(current + 1) == 'Q') + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("K"); + ' break; + ' + ' case 'R': + ' //french e.g. 'rogier', but exclude 'hochmeier' + ' if((current == last) + ' AND !SlavoGermanic() + ' AND StringAt((current - 2), 2, "IE", "") + ' AND !StringAt((current - 4), 2, "ME", "MA", "")) + ' MetaphAdd("", "R"); + ' else + ' MetaphAdd("R"); + ' + ' if(GetAt(current + 1) == 'R') + ' current += 2; + ' else + ' current += 1; + ' break; + ' + ' case 'S': + ' //special cases 'island', 'isle', 'carlisle', 'carlysle' + ' if(StringAt((current - 1), 3, "ISL", "YSL", "")) + ' { + ' current += 1; + ' break; + ' } + ' + ' //special case 'sugar-' + ' if((current == 0) AND StringAt(current, 5, "SUGAR", "")) + ' { + ' MetaphAdd("X", "S"); + ' current += 1; + ' break; + ' } + ' + ' if(StringAt(current, 2, "SH", "")) + ' { + ' //germanic + ' if(StringAt((current + 1), 4, "HEIM", "HOEK", "HOLM", "HOLZ", "")) + ' MetaphAdd("S"); + ' else + ' MetaphAdd("X"); + ' current += 2; + ' break; + ' } + ' + ' //italian & armenian + ' if(StringAt(current, 3, "SIO", "SIA", "") OR StringAt(current, 4, "SIAN", "")) + ' { + ' if(!SlavoGermanic()) + ' MetaphAdd("S", "X"); + ' else + ' MetaphAdd("S"); + ' current += 3; + ' break; + ' } + ' + ' //german & anglicisations, e.g. 'smith' match 'schmidt', 'snider' match 'schneider' + ' //also, -sz- in slavic language altho in hungarian it is pronounced 's' + ' if(((current == 0) + ' AND StringAt((current + 1), 1, "M", "N", "L", "W", "")) + ' OR StringAt((current + 1), 1, "Z", "")) + ' { + ' MetaphAdd("S", "X"); + ' if(StringAt((current + 1), 1, "Z", "")) + ' current += 2; + ' else + ' current += 1; + ' break; + ' } + ' + ' if(StringAt(current, 2, "SC", "")) + ' { + ' //Schlesinger's rule + ' if(GetAt(current + 2) == 'H') + ' //dutch origin, e.g. 'school', 'schooner' + ' if(StringAt((current + 3), 2, "OO", "ER", "EN", "UY", "ED", "EM", "")) + ' { + ' //'schermerhorn', 'schenker' + ' if(StringAt((current + 3), 2, "ER", "EN", "")) + ' { + ' MetaphAdd("X", "SK"); + ' }else + ' MetaphAdd("SK"); + ' current += 3; + ' break; + ' }else{ + ' if((current == 0) AND !IsVowel(3) AND (GetAt(3) != 'W')) + ' MetaphAdd("X", "S"); + ' else + ' MetaphAdd("X"); + ' current += 3; + ' break; + ' } + ' + ' if(StringAt((current + 2), 1, "I", "E", "Y", "")) + ' { + ' MetaphAdd("S"); + ' current += 3; + ' break; + ' } + ' //else + ' MetaphAdd("SK"); + ' current += 3; + ' break; + ' } + ' + ' //french e.g. 'resnais', 'artois' + ' if((current == last) AND StringAt((current - 2), 2, "AI", "OI", "")) + ' MetaphAdd("", "S"); + ' else + ' MetaphAdd("S"); + ' + ' if(StringAt((current + 1), 1, "S", "Z", "")) + ' current += 2; + ' else + ' current += 1; + ' break; + ' + ' case 'T': + ' if(StringAt(current, 4, "TION", "")) + ' { + ' MetaphAdd("X"); + ' current += 3; + ' break; + ' } + ' + ' if(StringAt(current, 3, "TIA", "TCH", "")) + ' { + ' MetaphAdd("X"); + ' current += 3; + ' break; + ' } + ' + ' if(StringAt(current, 2, "TH", "") + ' OR StringAt(current, 3, "TTH", "")) + ' { + ' //special case 'thomas', 'thames' or germanic + ' if(StringAt((current + 2), 2, "OM", "AM", "") + ' OR StringAt(0, 4, "VAN ", "VON ", "") + ' OR StringAt(0, 3, "SCH", "")) + ' { + ' MetaphAdd("T"); + ' }else{ + ' MetaphAdd("0", "T"); + ' } + ' current += 2; + ' break; + ' } + ' + ' if(StringAt((current + 1), 1, "T", "D", "")) + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("T"); + ' break; + ' + ' case 'V': + ' if(GetAt(current + 1) == 'V') + ' current += 2; + ' else + ' current += 1; + ' MetaphAdd("F"); + ' break; + ' + ' case 'W': + ' //can also be in middle of word + ' if(StringAt(current, 2, "WR", "")) + ' { + ' MetaphAdd("R"); + ' current += 2; + ' break; + ' } + ' + ' if((current == 0) + ' AND (IsVowel(current + 1) OR StringAt(current, 2, "WH", ""))) + ' { + ' //Wasserman should match Vasserman + ' if(IsVowel(current + 1)) + ' MetaphAdd("A", "F"); + ' else + ' //need Uomo to match Womo + ' MetaphAdd("A"); + ' } + ' + ' //Arnow should match Arnoff + ' if(((current == last) AND IsVowel(current - 1)) + ' OR StringAt((current - 1), 5, "EWSKI", "EWSKY", "OWSKI", "OWSKY", "") + ' OR StringAt(0, 3, "SCH", "")) + ' { + ' MetaphAdd("", "F"); + ' current +=1; + ' break; + ' } + ' + ' //polish e.g. 'filipowicz' + ' if(StringAt(current, 4, "WICZ", "WITZ", "")) + ' { + ' MetaphAdd("TS", "FX"); + ' current +=4; + ' break; + ' } + ' + ' //else skip it + ' current +=1; + ' break; + ' + ' case 'X': + ' //french e.g. breaux + ' if(!((current == last) + ' AND (StringAt((current - 3), 3, "IAU", "EAU", "") + ' OR StringAt((current - 2), 2, "AU", "OU", ""))) ) + ' MetaphAdd("KS"); + ' + ' if(StringAt((current + 1), 1, "C", "X", "")) + ' current += 2; + ' else + ' current += 1; + ' break; + ' + ' case 'Z': + ' //chinese pinyin e.g. 'zhao' + ' if(GetAt(current + 1) == 'H') + ' { + ' MetaphAdd("J"); + ' current += 2; + ' break; + ' }else + ' if(StringAt((current + 1), 2, "ZO", "ZI", "ZA", "") + ' OR (SlavoGermanic() AND ((current > 0) AND GetAt(current - 1) != 'T'))) + ' { + ' MetaphAdd("S", "TS"); + ' } + ' else + ' MetaphAdd("S"); + ' + ' if(GetAt(current + 1) == 'Z') + ' current += 2; + ' else + ' current += 1; + ' break; + ' + ' default: + ' current += 1; + ' } + ' } + ' + ' metaph = primary; + ' //only give back 4 char metaph + ' //if(metaph.GetLength() > 4) + ' // metaph.SetAt(4,'\0'); + ' if(alternate) + ' { + ' metaph2 = secondary; + ' //if(metaph2.GetLength() > 4) + ' // metaph2.SetAt(4,'\0'); + ' } + ' + ' } + ' + +End + diff --git a/comp/src/gb.util/.src/MPhonetic_French.module b/comp/src/gb.util/.src/MPhonetic_French.module new file mode 100644 index 00000000..16889e75 --- /dev/null +++ b/comp/src/gb.util/.src/MPhonetic_French.module @@ -0,0 +1,283 @@ +' Gambas module file + +Public Sub Before(sStr As String) As String + + ' R0 substitution des caractères accentués par les voyelles "simples" + + sStr = Replace(sStr, "ç", "ss") + sStr = Replace(sStr, "Ç", "ss") + + Return sStr + +End + +Public Sub Run(sStr As String) As String + + Dim sSuff As String + Dim sPref As String + Dim I As Integer + Dim sPat As String + Dim sResult As String + + If IsDigit(sStr) Then Return "(" & sStr & ")" + + ' R1 remplacement du "y" par un "i" + + sStr = Replace(sStr, "y", "i") + + ' R2 remplacement du son "ph" par un simple "f" + + sStr = Replace(sStr, "ph", "f") + + ' R3 suppression des "h" muets, c'est à dire ceux qui ne sont ni précédés d'un "c" ni d'un "s" + + sStr = Replace(sStr, "ch", "cH") + sStr = Replace(sStr, "sh", "sH") + sStr = Replace(sStr, "h", "") + sStr = Replace(sStr, "H", "h") + + ' R4 remplacement du "g" par "k" devant "an/am/ain/aim" gamin -> kamin + + sStr = Replace(sStr, "gan", "kan") + sStr = Replace(sStr, "gam", "kam") + sStr = Replace(sStr, "gain", "kain") + sStr = Replace(sStr, "gaim", "kaim") + + ' R5 + ' + ' remplacement de aina,eina,aima,eima par yna + ' remplacement de aine,eine,aime,eime par yne + ' remplacement de aini,eini,aimi,eimi par yni + ' remplacement de aino,eino,aimo,eimo par yno + ' remplacement de ainu,einu,aimu,eimu par ynu + + For Each sSuff In ["na", "ne", "ni", "no", "nu"] + For Each sPref In ["a", "e", "i", "o", "u"] + sStr = Replace(sStr, sPref & "i" & sSuff, "y" & sSuff) + Next + Next +' + ' R6 + ' + ' remplacement de eau par o + ' remplacement de oua par 2 + ' remplacement de ein par 4 + ' remplacement de ain par 4 + + sStr = Replace(sStr, "eau", "o") + sStr = Replace(sStr, "oua", "2") + sStr = Replace(sStr, "ein", "4") + sStr = Replace(sStr, "ain", "4") + + ' R7 + ' + ' remplacement de ai par y + ' remplacement de ei par y + ' remplacement de ee par y + ' remplacement de er par yr + ' remplacement de ess par yss + ' remplacement de et par yt + ' remplacement de ez par yz + + sStr = Replace(sStr, "ai", "y") + sStr = Replace(sStr, "ei", "y") + sStr = Replace(sStr, "ee", "y") + sStr = Replace(sStr, "er", "yr") + sStr = Replace(sStr, "ess", "yss") + sStr = Replace(sStr, "et", "yt") + sStr = Replace(sStr, "ez", "yz") + + ' R8 suppression des lettres doublées pelle -> pele + + GoSub REMOVE_DOUBLE + + ' R9 + ' + ' remplacement de "an" par "1" + ' remplacement de "am" par "1" + ' remplacement de "en" par "1" + ' remplacement de "em" par "1" + ' remplacement de "in" par "4" + ' à condition que ces modèles ne soient ni suivis d'une voyelle ni d'un son "1" à "4" + ' patient - > pati1t + + For Each sPat In ["an", "am", "en", "em", "in"] + I = 0 + Do + I = InStr(sStr, sPat, I + 1) + If I = 0 Then Break + If InStr("aeiouy1234", Mid$(sStr, I + 2, 1)) Then + I += 2 + Else + sStr = Left(sStr, I - 1) & If(sPat = "in", "4", "1") & Mid$(sStr, I + 2) + Endif + Loop + Next + + ' R10 remplacement du "z" par "s" s'il est en tête de mot ou précédé et suivi d'une voyelle ou d'un son "1" à "4" + ' zebu -> zebu + ' azteque -> azteque + ' bizarre -> bisarre + + I = 0 + Do + I = InStr(sStr, "z", I + 1) + If I = 0 Then Break + If I = 1 + sStr = "s" & Mid$(sStr, 2) + Else If InStr("aeiouy1234", Mid$(sStr, I - 1, 1)) And If InStr("aeiouy1234", Mid$(sStr, I + 1, 1)) Then + sStr = Left(sStr, I - 1) & "s" & Mid(sStr, I + 1) + I += 1 + Endif + Loop + + ' R11 + ' + ' remplacement de "oe" par "e" + ' remplacement de "eu" par "e" + ' remplacement de "au" par "o" + ' remplacement de "oi" par "2" + ' remplacement de "ou" par "3" + ' + ' heureux -> erex + ' paul -> pol + ' roue -> r3e + + sStr = Replace(sStr, "oe", "e") + sStr = Replace(sStr, "eu", "e") + sStr = Replace(sStr, "au", "o") + sStr = Replace(sStr, "oi", "2") + sStr = Replace(sStr, "ou", "3") + + ' R12 + ' + ' remplacement de "ch" par "5" + ' remplacement de "sch" par "5" + ' remplacement de "sh" par "5" + ' remplacement de "ss" par "s" + ' remplacement de "sc" par "s" si suivi d'un "i" ou d'un "e" + ' + ' chat -> 5at + ' scaralatine -> scarlatine + ' scie -> sie + + sStr = Replace(sStr, "ch", "5") + sStr = Replace(sStr, "sch", "5") + sStr = Replace(sStr, "sh", "5") + sStr = Replace(sStr, "ss", "s") + sStr = Replace(sStr, "sci", "si") + sStr = Replace(sStr, "sce", "se") + + ' R13 remplacement du "c" par "s" s'il est suivi dun "e" ou d'un "i" + ' car -> car + ' innocence -> inosense + + sStr = Replace(sStr, "ce", "se") + sStr = Replace(sStr, "ci", "si") + + ' R14 + ' + ' remplacement de "c" par "k" + ' remplacement de "q" par "k" + ' remplacement de "qu" par "k" + ' remplacement de "gu" par "k" + ' remplacement de "ga" par "ka" + ' remplacement de "go" par "ko" + ' + ' car -> kar + ' gateau -> kato + + sStr = Replace(sStr, "c", "k") + sStr = Replace(sStr, "qu", "k") + sStr = Replace(sStr, "q", "k") + sStr = Replace(sStr, "ga", "ga") + sStr = Replace(sStr, "ge", "je") + sStr = Replace(sStr, "gi", "ji") + sStr = Replace(sStr, "go", "go") + sStr = Replace(sStr, "gu", "g") + + ' R15 + ' + ' remplacement de "a" par "o" + ' remplacement de "d" et "p" par "t" + ' remplacement de "j" par "g" + ' remplacement de "b" et "v" par "f" + ' remplacement de "m" par "n" + ' + ' depart -> tetord + ' homme -> one + + 'sStr = Replace(sStr, "a", "o") + 'sStr = Replace(sStr, "d", "t") + 'sStr = Replace(sStr, "p", "t") + 'sStr = Replace(sStr, "j", "g") + 'sStr = Replace(sStr, "b", "f") + 'sStr = Replace(sStr, "v", "f") + 'sStr = Replace(sStr, "m", "n") + 'sStr = Replace(sStr, "w", "v") + + ' R15.1 + + sStr = Replace(sStr, "w", "3") + + ' R15.2 + + sStr = Replace(sStr, "mpt", "nt") + sStr = Replace(sStr, "mt", "nt") + + ' R16 (optionnelle) remplacement des "y" (les sons "é") par "e" nez -> nyz -> nez + + 'sStr = Replace(sStr, "y", "e") + + ' R17 + ' + ' suppression des finales "t","x","s","z" + ' (optionnelle) suppression du "e" final après une consonne (fait en deux fois pour eliminer les finales "ez" par exemple) + ' + ' heureux -> ere + ' tetard -> tetor + + If Len(sStr) >= 2 And If InStr("rdptxsz", Right(sStr)) Then sStr = Left(sStr, -1) + + ' If Len(sStr) >= 2 Then + ' If Right(sStr) = "e" And InStr("aeiouy", Left(Right(sStr, 2))) = 0 Then + ' sStr = Left(sStr, -1) + ' Endif + ' Endif + ' + ' If Len(sStr) >= 2 And If InStr("rtxsz", Right(sStr)) Then sStr = Left(sStr, -1) + + ' R18 suppression des lettres doubles (pour la 2ème fois) arrivee -> arive + + GoSub REMOVE_DOUBLE + + ' R19 retourne une chaine d'une taille maximale de 16 caractères et ne contenant que les caractères autorisés. + ' Au vu des traductions décrites, les caractères obtenus font partie de l'alphabet réduit: + ' 12345efghiklnorstuwxyz + ' Les caractères qui n'appartiennent pas à cet ensemble sont supprimés + + sResult = "" + For I = 1 To Len(sStr) + sPat = Mid$(sStr, I, 1) + If InStr("12345abdefghijklmnoprstuvxyz", sPat) Then + sResult &= sPat + 'If Len(sResult) = 16 Then Break + Endif + Next + + Return sResult + +REMOVE_DOUBLE: + + I = 1 + While I < Len(sStr) + If Mid$(sStr, I, 1) = Mid$(sStr, I + 1, 1) Then + sStr = Left(sStr, I) & Mid$(sStr, I + 2) + Else + Inc I + Endif + Wend + Return + +End + diff --git a/comp/src/gb.util/.src/Process.class b/comp/src/gb.util/.src/Process.class new file mode 100644 index 00000000..f504f3b0 --- /dev/null +++ b/comp/src/gb.util/.src/Process.class @@ -0,0 +1,39 @@ +' Gambas class file + +Export + +'' @{since 3.15} +'' +'' This event is raised when a prompt specified by the [../expect] method has been detected on the standard output of the process. +'' +'' The ~Prompt~ and ~Answer~ arguments of the corresponding [../expect] method are provided to the event handler. + +Event Prompt(Prompt As String, Answer As String) + +Private $hExpect As ProcessExpect + +'' @{since 3.15} +'' +'' Tell which answer must be sent when a specific prompt is emitted by the process on its standard input. +'' +'' * ~Prompt~ : The prompt as a [/lang/like] regular expression that should match the last characters emitted by the process. +'' * ~Answer~ : The optional answer string to send back. +'' @{since 3.17} +'' * ~Keep~ tells if the prompt keeps being watched after having been detected, or if it is disabled. ~Keep~ is FALSE by default. +'' +'' The [../.prompt] event is emitted just after the answer has been sent, or just after the prompt has been detected if no answer was specified. + +Public Sub Expect(Prompt As String, Optional Answer As String, Optional Keep As Boolean) + + If Not Prompt Then Return + + If Not $hExpect Then $hExpect = New ProcessExpect + $hExpect.Expect(Me, Prompt, Answer, Keep) + +End + +' Public Sub Expect_Kill() +' +' $hExpect = Null +' +' End diff --git a/comp/src/gb.util/.src/ProcessExpect.class b/comp/src/gb.util/.src/ProcessExpect.class new file mode 100644 index 00000000..02ede27a --- /dev/null +++ b/comp/src/gb.util/.src/ProcessExpect.class @@ -0,0 +1,78 @@ +' Gambas class file + +Private $aExpect As New ProcessPrompt[] +Private $sBuffer As String +Private $hObs As Observer +Private $hObsAfter As Observer + +Public Sub _new() + + $aExpect = New String[] + +End + +Public Sub Expect(hProcess As Process, Prompt As String, Answer As String, Keep As Boolean) + + Dim hProcessPrompt As ProcessPrompt + + If Prompt Not Begins "*" Then Prompt = "*" & Prompt + + With hProcessPrompt = New ProcessPrompt + .Prompt = Prompt + .Answer = Answer + .Keep = Keep + End With + + $aExpect.Add(hProcessPrompt) + + If Not $hObs Then + $hObs = New Observer(hProcess) As "Expect" + $hObsAfter = New Observer(hProcess, True) As "ExpectAfter" + Endif + +End + +Public Sub Expect_Read() + + Dim sData As String + Dim hProcess As Process + + hProcess = $hObs.Object + + sData = Peek #hProcess, Lof(hProcess) + + If Len(sData) >= 1024 Then + $sBuffer = Right(sData, 1024) + Else + $sBuffer = Right($sBuffer & sData, 1024) + Endif + +End + +Public Sub ExpectAfter_Read() + + Dim hProcess As Process + Dim I As Integer + Dim hProcessPrompt As ProcessPrompt + + hProcess = $hObsAfter.Object + + For I = 0 To $aExpect.Max + With hProcessPrompt = $aExpect[I] + If RTrim($sBuffer) Like .Prompt Then + If .Answer Then Print #hProcess, .Answer + Object.Raise(hProcess, "Prompt", [.Prompt, .Answer]) + $sBuffer = "" + If Not .Keep Then $aExpect.Remove(I) + Break + Endif + End With + Next + + If $aExpect.Count = 0 Then + $hObs.Detach + $hObsAfter.Detach + Endif + +End + diff --git a/comp/src/gb.util/.src/ProcessPrompt.class b/comp/src/gb.util/.src/ProcessPrompt.class new file mode 100644 index 00000000..35f11553 --- /dev/null +++ b/comp/src/gb.util/.src/ProcessPrompt.class @@ -0,0 +1,5 @@ +' Gambas class file + +Public Prompt As String +Public Answer As String +Public Keep As Boolean \ No newline at end of file diff --git a/comp/src/gb.util/.src/Shell.module b/comp/src/gb.util/.src/Shell.module new file mode 100644 index 00000000..41b26b9e --- /dev/null +++ b/comp/src/gb.util/.src/Shell.module @@ -0,0 +1,59 @@ +' Gambas module file + +Export + +Public Sub MkDir(Path As String) + + Dim sElt As String + Dim sMake As String = "/" + + If Path Begins "~/" Then Path = User.Home &/ Mid$(Path, 3) + + For Each sElt In Split(Path, "/") + sMake &/= sElt + If IsDir(sMake) Then Continue + Try Mkdir sMake + Next + + If Not Exist(Path) Or If Not IsDir(Path) Then Error.Raise("Cannot create directory") + +End + +Public Sub RmDir(Path As String, Optional Force As Boolean) + + Dim sFile As String + + If Len(Path) > 1 And If Right(Path) = "/" Then Path = Left(Path, -1) + + If Not Force Then + If Path = "~" Or If Path = User.Home Or If Path = "/" Then Error.Raise("Removing this directory recursively is a bad idea: " & Path) + Endif + + For Each sFile In Dir(Path) + If IsDir(Path &/ sFile) Then + RmDir(Path &/ sFile) + Else + Kill Path &/ sFile + Endif + Next + + Rmdir Path + +End + +Public Sub Move(Source As String, Destination As String, Optional Force As Boolean) + + If IsDir(Destination) Then Destination &/= File.Name(Source) + + If Force Then + Try Move Source Kill Destination + Else + Try Move Source To Destination + Endif + If Error Then + If Force Then Try Kill Destination + Copy Source To Destination + Kill Source + Endif + +End diff --git a/comp/src/gb.util/.src/String.class b/comp/src/gb.util/.src/String.class new file mode 100644 index 00000000..7d7ea7fd --- /dev/null +++ b/comp/src/gb.util/.src/String.class @@ -0,0 +1,416 @@ +' Gambas class file + +Export + +Private Const REMOVE_ACC As String = "ÀÁÂÃÄÅĀĂĄǍǞǠȀȂȦȺǺẠẢẬẶḀẮẰẲẴẪẤẦẨ[A]" + "ÈÉÊËĒĖĘĚȄȨɆĔȆḘḜẸẺỆḚẼỄḔḖẾỀỂ[E]" + "ÌÍÎÏĨĪĬĮİƗǏȈȊḬỈỊḮ[I]" + "ŎÒÓÔÖŌŐƠǑǬȌȎȮỌỎỘỚỜỞỢỠȪȰȬÕṌṎṐṒỐỒỔỖ[O]" + "ǗǙǛȖŬŨŮǕÙÚÛÜŪŰŲƯǓȔɄṲṴṶỤỦỨỪỬỮỰƲṺṸ[U]" + "ÝŶŸƳȲɎẎỲỴỶỸỾ[Y]" + "ƁɃḂḄḆ[B]ÇĆĈĊČƇȻḈ[C]ĎÐĐƉḊḌḎḐḒ[D]ƑḞ[F]ĞĜĠĢƓǤǦǴɢʛḠ[G]ĤȞĦḢḤḦḪḨ[H]ĴɈɟʄʝ[J]ĶƘǨḰḲḴ[K]ĹĻĽĿʟŁȽḶḸḺḼ[L]ḾṀṂ[M]" + "ƝÑŃŅŇŊǸṄṆṈṊ[N]ƤṔṖ[P]ŔŖŘȐȒɌṘṚṜṞ[R]ŚŜŞŠȘṠṢṨṤṦ[S]ŢŤŦƮȚȾṪṬṮṰ[T]ṼṾ[V]ŴẀẂẄẆẈ[W]ẊẌ[X]ŹŻŽƵȤẐẒẔ[Z]" + "ÆǢǼ[AE]Œɶ[OE]ßẞ[ss]æǣǽ[ae]œ[oe]" + "àáâãäåāăąǎǟǡǻȁȃȧắằẳẵḁẚạảấầẩậặẫ[a]" + "èéêëēĕėęěȅȇȩɇḕḗḙḛḝẹẻẽếềểệễ[e]" + "īĭìíïîĩįıǐȉȋɨỉḭḯị[i]" + "òóôõöōŏőơǒǫǭȍȏȫȯȱȭṍṏṑṓọỏốồổỗộớờởỡợ[o]" + "ùúûüũūŭůűųưǔǖǘǚǜȕȗṹṳṷṻụủứừửữựṵ[u]" + "ýÿŷƴȳɏỵẏẙỳỷỹỿ[y]" + "ɓƀḃḅḇ[b]çćĉċčƈȼɕḉ[c]ďɖɗđḋḍḏḑḓ[d]ƒḟ[f]ĝğġģǥǧǵɠɡḡ[g]ĥȟɦɧħḣḥḧḫẖḩ[h]ĵǰȷɉ[j]ķƙǩḱḳḵ[k]ĺľŀłļḽḷḹḻ[l]ɱḿṁṃ[m]" + "ñńņňʼnŋǹṅṇṉṋ[n]ƥṕṗ[p]ʠɋ[q]ŕŗřȑȓṙṛṝṟ[r]śŝşšșʂȿṡṣṥṧṩ[s]ţťŧƫƭțṫṭṯṱẗ[t]ʋṽṿ[v]ŵẁẃẅẇẉẘ[w]ẋẍ[x]źżžƶȥɀʐʑẑẓẕ[z]" + +Static Public Sub RemoveDiacritics({String} As String) As String + + If Not {String} Then Return + If IsAscii({String}) Then Return {String} + + Return DoRemoveAccents({String}) + +End + +Static Private Sub DoRemoveAccents(sStr As String) As String + + Dim sCar As String + Dim iPos As Integer + Dim iPosL As Integer + Dim iPosR As Integer + Dim sNewStr As String + Dim I As Integer + Dim L As Integer + + L = 1 + For I = 1 To Len(sStr) + If IsAscii(Mid$(sStr, I, 1)) Then Continue + If I > L Then sNewStr &= Mid$(sStr, L, I - L) + sCar = String.Left(Mid$(sStr, I)) + L = I + Len(sCar) + I = L - 1 + iPos = InStr(REMOVE_ACC, sCar) + If iPos Then + iPosL = InStr(REMOVE_ACC, "[", iPos + 1) + iPosR = InStr(REMOVE_ACC, "]", iPos + 1) + If iPosL <> 0 And If iPosR <> 0 And If iPosL < iPosR Then + sCar = Mid$(REMOVE_ACC, iPosL + 1, iPosR - iPosL - 1) + Endif + Endif + sNewStr &= sCar + + Next + + If I > L Then sNewStr &= Mid$(sStr, L) + + Return sNewStr + +End + +Static Public Sub Distance(String1 As String, String2 As String) As Integer + + Dim L1 As Integer = String.Len(String1) + Dim L2 As Integer = String.Len(String2) + Dim D As New Integer[L1 + 1, L2 + 1] + Dim I, J, C As Integer + Dim C1, C1M, C2, C2M As String + + For I = 0 To L1 + D[I, 0] = I + Next + + For J = 0 To L2 + D[0, J] = J + Next + + For I = 1 To L1 + + C1M = C1 + C1 = String.Mid$(String1, I, 1) + + C2 = "" + + For J = 1 To L2 + + C2M = C2 + C2 = String.Mid$(String2, J, 1) + + If C1 = C2 Then + C = 0 + Else + C = 1 + Endif + + D[I, J] = Min(Min(D[I - 1, J] + 1, D[I, J - 1] + 1), D[I - 1, J - 1] + C) + + If I > 1 And If J > 1 Then + If C1 = C2M And If C1M = C2 Then + D[I, J] = Min(D[I, J], D[I - 2, J - 2] + C) + Endif + Endif + + Next + Next + + Return D[L1, L2] + +End + +Static Public Sub FromHTML(Html As String) As String + + Dim P As Integer + Dim iLen As Integer + Dim C As String + Dim sResult As String + Dim C2 As String + Dim sEntity As String + Dim sMarkup As String + Dim aMarkup As New String[] + + iLen = String.Len(Html) + +READ_TEXT: + + GoSub READ_CHAR + + If C = "<" Then Goto READ_MARKUP + + If C = "&" Then Goto READ_ENTITY + + If aMarkup.Count = 0 Then sResult &= C + Goto READ_TEXT + +READ_MARKUP: + +READ_MARKUP_NAME: + + GoSub READ_CHAR + + If C = " " Then Goto READ_MARKUP_REST + If C = ">" Then Goto READ_MARKUP_END + + sMarkup &= C + + Goto READ_MARKUP_NAME + +READ_MARKUP_REST: + + GoSub READ_CHAR + + If C = ">" Then Goto READ_MARKUP_END + + If C = Chr$(34) Or If C = "'" Then + C2 = C + Goto READ_STRING + Endif + + Goto READ_MARKUP_REST + +READ_MARKUP_END: + + sMarkup = LCase(sMarkup) + If sMarkup Begins "/" Then + sMarkup = Mid$(sMarkup, 2) + If sMarkup = "li" Then + sResult &= "\n" + Endif + If aMarkup.Count Then + If aMarkup[aMarkup.Max] = sMarkup Then aMarkup.Pop() + Endif + Else + If sMarkup = "script" Or If sMarkup = "style" Then + aMarkup.Push(sMarkup) + Else If sMarkup = "br" Then + sResult &= "\n" + Else If sMarkup = "ul" Or If sMarkup = "ol" Or If sMarkup = "p" Then + If Right(sResult) <> "\n" Then sResult &= "\n" + Else If sMarkup = "li" Then + sResult &= "- " + Endif + Endif + sMarkup = "" + Goto READ_TEXT + +READ_STRING: + + GoSub READ_CHAR + + If C = C2 Then Goto READ_MARKUP + + Goto READ_STRING + +READ_ENTITY: + + GoSub READ_CHAR + + If IsDigit(C) Or If IsLetter(C) Or If C = "#" Then + sEntity &= C + Goto READ_ENTITY + Endif + + Select Case sEntity + Case "lt" + C = "<" + Case "gt" + C = ">" + Case "amp" + C = "&" + Case "nbsp" + C = String.Chr$(160) + Case "quot" + C = Chr$(34) + Case "apos" + C = "'" + Case Else + If sEntity Begins "#x" Then + Try C = String.Chr(Val("&H" & Mid$(sEntity, 3))) + If Error Then C = "?" + Else If sEntity Begins "#" Then + Try C = String.Chr(CInt(Mid$(sEntity, 2))) + If Error Then C = "?" + Else + C = "&" & sEntity + Dec P + Endif + End Select + + If aMarkup.Count = 0 Then sResult &= C + sEntity = "" + Goto READ_TEXT + +READ_CHAR: + + Inc P + If P > iLen Then + Return sResult + Endif + C = String.Mid$(Html, P, 1) + Return + +End + +Static Public Sub ToPhonetic({String} As String, Optional Language As String = System.Language) As String + + Dim I As Integer + Dim sStr As String + Dim sCar As String + Dim J As Integer + Dim sWord As String + Dim sResult As String + + sStr = Trim({String}) + If Not sStr Then Return + + Try Language = LCase(Split(Language, "-._")[0]) + + Select Case Language + + Case "c", "en" + Error.Raise("Unsupported language") + + Case "fr" + sStr = MPhonetic_French.Before(sStr) + + Default + Error.Raise("Unsupported language") + + End Select + + sStr = LCase(RemoveDiacritics(sStr)) & " " + + For I = 1 To Len(sStr) + + sCar = Mid$(sStr, I, 1) + + If IsLetter(sCar) Or IsDigit(sCar) Then + If J = 0 Then J = I + Continue + Endif + + If J = 0 Then Continue + + sWord = Mid$(sStr, J, I - J) + J = 0 + + If sResult And If Right(sResult) <> " " Then sResult &= " " + Select Case Language + + Case "c", "en" + Error.Raise("Unsupported language") + + Case "fr" + sResult &= MPhonetic_French.Run(sWord) + + Default + Error.Raise("Unsupported language") + + End Select + + Next + + Return sResult + +End + +Static Private Sub PadString(sStr As String, iLength As Integer, sPad As String, bRight As Boolean) As String + + Dim iLen As Integer + + If iLength < 0 Then Error.Raise("Bad argument") + iLength -= String.Len(sStr) + + If Len(sPad) = 1 Then + If bRight Then + Return sStr & String$(iLength, sPad) + Else + Return String$(iLength, sPad) & sStr + Endif + Else + iLen = String.Len(sPad) + If bRight Then + Return sStr & String.Right(sPad, iLength Mod iLen) & String$(iLength \ iLen, sPad) + Else + Return String$(iLength \ iLen, sPad) & String.Left(sPad, iLength Mod iLen) & sStr + Endif + Endif + +End + +Static Public Sub PadLeft({String} As String, Length As Integer, Optional Pad As String = " ") As String + + Return PadString({String}, Length, Pad, False) + +End + +Static Public Sub PadRight({String} As String, Length As Integer, Optional Pad As String = " ") As String + + Return PadString({String}, Length, Pad, True) + +End + +Static Public Sub IsEmail({String} As String) As Boolean + + Dim sStr As String + Dim iPos As Integer + + sStr = Trim({String}) + If Not sStr Then Return + + If sStr Ends ">" Then + iPos = RInStr(sStr, " <") + If iPos = 0 Then Return + sStr = Mid$(sStr, iPos + 2, -1) + Endif + + iPos = InStr(sStr, "@") + If iPos = 0 Then Return + + sStr = Mid$(sStr, iPos + 1) + If InStr(sStr, ".") = 0 Then Return + + Return True + +End + +Static Public Sub IsIP({String} As String) As Boolean + + Dim aStr As String[] + Dim I As Integer + Dim iNum As Integer + + aStr = Split({String}, ".") + If aStr.Count <> 4 Then Return + + For I = 0 To 4 + If Not IsDigit(aStr[I]) Then Return + iNum = CInt(aStr[I]) + If iNum > 255 Then Return + Next + + Return True + +End + +Static Public Sub IsIPv6({String} As String) As Boolean + + Dim aStr As String[] + Dim I As Integer + Dim iNum As Integer + Dim bVoid As Boolean + + aStr = Split({String}, ":") + If aStr.Count < 3 Or If aStr.Count > 8 Then Return + + If String.IsIP(aStr.Last) Then aStr.Pop() + + For I = 0 To aStr.Max + If Not aStr[I] Then + If bVoid Then Return + bVoid = True + Continue + Endif + Try iNum = Val("&H" & aStr[I] & "&") + If Error Then Return + If iNum < 0 Or If iNum > 65535 Then Return + Next + + Return True + +End diff --git a/comp/src/gb.web.feed/.component b/comp/src/gb.web.feed/.component new file mode 100644 index 00000000..c5196136 --- /dev/null +++ b/comp/src/gb.web.feed/.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.web.feed +Version=3.12.90 +State=1 +Authors=(C) 2017-2019 Tobias Boege +Needs=XML +Requires=gb.xml,gb.util diff --git a/comp/src/gb.web.feed/.directory b/comp/src/gb.web.feed/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.web.feed/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.web.feed/.hidden/TODO b/comp/src/gb.web.feed/.hidden/TODO new file mode 100644 index 00000000..815805d0 --- /dev/null +++ b/comp/src/gb.web.feed/.hidden/TODO @@ -0,0 +1,10 @@ + o Should I Html$() every string? Or better use CDATA? How to detect when it is necessary? + ~> Adrien said gb.xml will take care of escaping in the future. + + o Rss.ParseDate() ignores the timezone. What should I do and what can I do? + ~> Gambas Dates are always UTC. Probably make an RssDate class which saves a Date and the timezone. + Give it conversion methods. Benoit said these should be provided by gb.(web.)util. + o Should Rss.FormatDate() put the timezone string instead of +nnnn. How? + + o Support Atom. Should there be a basis class which represents the common features of Rss and Atom + so that a user can offer both formats cheaply (without keeping a complete Rss and Atom object around)? diff --git a/comp/src/gb.web.feed/.icon.png b/comp/src/gb.web.feed/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..213a3598f68df49e301f9a304e27809f8f177da3 GIT binary patch literal 12094 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 Vv4Pby55WTfH%+Xs*Ijdu`#&{q5?cTO literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.feed/.lang/it.po b/comp/src/gb.web.feed/.lang/it.po new file mode 100644 index 00000000..edb1ac98 --- /dev/null +++ b/comp/src/gb.web.feed/.lang/it.po @@ -0,0 +1,119 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.feed 3.12.90\n" +"POT-Creation-Date: 2019-04-22 15:34 UTC\n" +"PO-Revision-Date: 2019-04-22 15:33 UTC\n" +"Last-Translator: gian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "Web feed parser and generator" +msgstr "Parser e generatore di feed Web" + +#: Rss.class:213 +msgid "SkipHours may only contain up to 24 elements" +msgstr "SkipHours può contenere solo fino a 24 elementi" + +#: Rss.class:217 +msgid "SkipHour element must be in the range [0, 23]" +msgstr "L'elemento SkipHour deve essere compreso nell'intervallo [0, 23]" + +#: Rss.class:228 +msgid "SkipDays may only contain up to 7 elements" +msgstr "SkipDays può contenere solo fino a 7 elementi" + +#: Rss.class:255 +msgid "Invalid SkipDays day '&1'" +msgstr "'&1' giorno SkipDays non valido" + +#: Rss.class:275 +msgid "Invalid day '&1'" +msgstr "'&1' giorno non valido" + +#: Rss.class:290 +msgid " expected" +msgstr "previsto " + +#: Rss.class:293 +msgid "End-of-file expected" +msgstr "End-of-file previsto" + +#: Rss.class:318 +msgid " expected" +msgstr "previsto " + +#: Rss.class:322 +msgid " expected" +msgstr "previsto " + +#: Rss.class:383 +msgid "Unexpected element '&1' in " +msgstr "'&1' elemento imprevisto in " + +#: Rss.class:404 +msgid " expected" +msgstr "previsto " + +#: Rss.class:406 +msgid "SkipHours element '&1' out of range [0, 23]" +msgstr "L'elemento SkipHours '&1' è fuori dall'intervallo [0, 23]" + +#: Rss.class:420 +msgid " expected" +msgstr "previsto " + +#: RssCloud.class:24 +msgid "Domain, Port, Path, RegisterProcedure and Protocol must be set in RssCloud" +msgstr "Domain, Port, Path, Procedura di registro e Protocollo devono essere impostati in RssCloud" + +#: RssCloud.class:40 +msgid "Invalid RssCloud protocol constant '&1'" +msgstr "'&1' costante del protocollo RssCloud non valida" + +#: RssCloud.class:52 +msgid "Invalid RssCloud protocol '&1'" +msgstr "'&1' protocollo RssCloud non valido" + +#: RssEnclosure.class:15 +msgid "Url, Length and Type must be set in RssEnclosure" +msgstr "Url, Lunghezza e Tipo devono essere impostati in RssEnclosure" + +#: RssImage.class:37 +msgid "Url, Title and Link must be set in RssImage" +msgstr "Url, Titolo e Link devono essere impostati in RssImage" + +#: RssImage.class:44 +msgid "Maximum width of RssImage is 144" +msgstr "La larghezza massima di RssImage è 144" + +#: RssImage.class:48 +msgid "Maximum height of RssImage is 400" +msgstr "L'altezza massima di RssImage è 400" + +#: RssImage.class:75 +msgid "Unexpected element '&1' in " +msgstr "'&1' elemento imprevisto in " + +#: RssItem.class:52 +msgid "Title or Description must be set in RssItem" +msgstr "Titolo o Descrizione devono essere impostati in RssItem" + +#: RssItem.class:107 +msgid "Unexpected element '&1' in " +msgstr "'&1' elemento imprevisto in " + +#: RssSource.class:16 +msgid "Url must be set in RssSource" +msgstr "L'url deve essere impostato in RssSource" + +#: RssTextInput.class:21 +msgid "Title, Description, Name and Link must be set in RssTextInput" +msgstr "Titolo, Descrizione, Nome e Link devono essere impostati in RssTextInput" + +#: RssTextInput.class:47 +msgid "Unexpected element '&1' in " +msgstr "'&1' elemento imprevisto in " diff --git a/comp/src/gb.web.feed/.lang/nl.po b/comp/src/gb.web.feed/.lang/nl.po new file mode 100644 index 00000000..54fbb95c --- /dev/null +++ b/comp/src/gb.web.feed/.lang/nl.po @@ -0,0 +1,141 @@ +# Willy Raets < gbWilly@openmailbox.org >, 2017 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.feed 3.10.90\n" +"POT-Creation-Date: 2019-04-06 18:47 UTC\n" +"PO-Revision-Date: 2017-08-26 19:52 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "Web feed parser and generator" +msgstr "Web feed parser en generator" + +#: Rss.class:213 +msgid "SkipHours may only contain up to 24 elements" +msgstr "SkipHours kan maximaal 24 elementen bevatten" + +#: Rss.class:217 +msgid "SkipHour element must be in the range [0, 23]" +msgstr "SkipHour element dient in het bereik [0, 23] te vallen" + +#: Rss.class:228 +msgid "SkipDays may only contain up to 7 elements" +msgstr "SkipDays mag niet meer dat 7 elementen bevatten" + +#: Rss.class:255 +msgid "Invalid SkipDays day '&1'" +msgstr "Ongeldige SkipDays dag '&1'" + +#: Rss.class:275 +msgid "Invalid day '&1'" +msgstr "Ongeldige dag '&1'" + +#: Rss.class:290 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:293 +msgid "End-of-file expected" +msgstr "End-of-file verwacht" + +#: Rss.class:318 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:322 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:383 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + +#: Rss.class:404 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:406 +msgid "SkipHours element '&1' out of range [0, 23]" +msgstr "SkipHours element '&1' buiten bereik [0, 23]" + +#: Rss.class:420 +msgid " expected" +msgstr " verwacht" + +#: RssCloud.class:24 +msgid "Domain, Port, Path, RegisterProcedure and Protocol must be set in RssCloud" +msgstr "Domain, Port, Path, RegisterProcedure en Protocol moeten ingesteld zijn in RssCloud" + +#: RssCloud.class:40 +msgid "Invalid RssCloud protocol constant '&1'" +msgstr "Ongeldige RssCloud protocol constante '&1'" + +#: RssCloud.class:52 +msgid "Invalid RssCloud protocol '&1'" +msgstr "Ongeldig RssCloud protocol '&1'" + +#: RssEnclosure.class:15 +msgid "Url, Length and Type must be set in RssEnclosure" +msgstr "Url, Length en Type dienen ingestelt in RssEnclosure" + +#: RssImage.class:37 +msgid "Url, Title and Link must be set in RssImage" +msgstr "Url, Title en Link dienen ingestelt in RssImage" + +#: RssImage.class:44 +msgid "Maximum width of RssImage is 144" +msgstr "Maximale breedte van RssImage is 144" + +#: RssImage.class:48 +msgid "Maximum height of RssImage is 400" +msgstr "Maximale hoogte van RssImage is 400" + +#: RssImage.class:75 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + +#: RssItem.class:52 +msgid "Title or Description must be set in RssItem" +msgstr "Title of Description dient ingestelt in RssItem" + +#: RssItem.class:107 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + +#: RssSource.class:16 +msgid "Url must be set in RssSource" +msgstr "Url dient ingestelt in RssSource" + +#: RssTextInput.class:21 +msgid "Title, Description, Name and Link must be set in RssTextInput" +msgstr "Title, Description, Name en Link dienen ingestelt in RssTextInput" + +#: RssTextInput.class:47 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + +#~ msgid "Weekday does not match date: '&1' vs. '&2'" +#~ msgstr "Weekdag komt niet overeen met datum: '&1' vs. '&2'" + +#~ msgid "Invalid second '&1'" +#~ msgstr "Ongeldige seconde '&1'" + +#~ msgid "Invalid minute '&1'" +#~ msgstr "Ongeldige minuut '&1'" + +#~ msgid "Invalid month '&1'" +#~ msgstr "Ongeldige maand '&1'" + +#~ msgid "Invalid year '&1'" +#~ msgstr "Ongeldig jaar '&1'" + +#~ msgid "Invalid weekday '&1'" +#~ msgstr "Ongeldige weekdag '&1'" + +#~ msgid "Date does not conform to RFC 822" +#~ msgstr "Datum conformeert niet met RFC 822" diff --git a/comp/src/gb.web.feed/.lang/ru.po b/comp/src/gb.web.feed/.lang/ru.po new file mode 100644 index 00000000..4720c869 --- /dev/null +++ b/comp/src/gb.web.feed/.lang/ru.po @@ -0,0 +1,106 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: comp/src/gb.web.feed/.project:16 +msgid "Web feed parser and generator" +msgstr "Веб анализатор каналов и генератор" + +#: comp/src/gb.web.feed/.src/Main.module:16 +msgid "diff -u <(sort &1 | sed 's/ *$//' | sed 's/^ *//') <(sort &2 | sed 's/^ *//')" +msgstr "diff -u <(sort &1 | sed 's/ *$//' | sed 's/^ *//') <(sort &2 | sed 's/^ *//')" + +#: comp/src/gb.web.feed/.src/Rss.class:213 comp/src/gb.web.feed/.src/Rss.class:410 +msgid "SkipHours may only contain up to 24 elements" +msgstr "Пропуск часов может содержать до 24 элементов" + +#: comp/src/gb.web.feed/.src/Rss.class:217 +msgid "SkipHour element must be in the range [0, 23]" +msgstr "Элемент пропуска часов должен быть в диапазоне [0, 23]" + +#: comp/src/gb.web.feed/.src/Rss.class:228 comp/src/gb.web.feed/.src/Rss.class:425 +msgid "SkipDays may only contain up to 7 elements" +msgstr "Пропуск дней может содержать до 7 элементов" + +#: comp/src/gb.web.feed/.src/Rss.class:255 +msgid "Invalid SkipDays day '&1'" +msgstr "Неверный день для пропуска дня '&1'" + +#: comp/src/gb.web.feed/.src/Rss.class:275 +msgid "Invalid day '&1'" +msgstr "Неверный день '&1'" + +#: comp/src/gb.web.feed/.src/Rss.class:290 +msgid " expected" +msgstr " ожидается" + +#: comp/src/gb.web.feed/.src/Rss.class:293 +msgid "End-of-file expected" +msgstr "Ожидается конец файла" + +#: comp/src/gb.web.feed/.src/Rss.class:318 +msgid " expected" +msgstr " ожидается" + +#: comp/src/gb.web.feed/.src/Rss.class:322 +msgid " expected" +msgstr " ожидается" + +#: comp/src/gb.web.feed/.src/Rss.class:383 +msgid "Unexpected element '&1' in " +msgstr "Неожиданный элемент '&1' в " + +#: comp/src/gb.web.feed/.src/Rss.class:404 +msgid " expected" +msgstr " ожидается" + +#: comp/src/gb.web.feed/.src/Rss.class:406 +msgid "SkipHours element '&1' out of range [0, 23]" +msgstr "Элемент пропуска часов '&1' вне диапазона [0, 23]" + +#: comp/src/gb.web.feed/.src/Rss.class:420 +msgid " expected" +msgstr " ожидается" + +#: comp/src/gb.web.feed/.src/RssCloud.class:24 +msgid "Domain, Port, Path, RegisterProcedure and Protocol must be set in RssCloud" +msgstr "Домен, порт, путь, процедура регистрации и протокол должны быть установлены в облаке Rss" + +#: comp/src/gb.web.feed/.src/RssCloud.class:40 +msgid "Invalid RssCloud protocol constant '&1'" +msgstr "Неверная константа протокола облака Rss '&1'" + +#: comp/src/gb.web.feed/.src/RssCloud.class:52 +msgid "Invalid RssCloud protocol '&1'" +msgstr "Неверный протокол облака Rss '&1'" + +#: comp/src/gb.web.feed/.src/RssItem.class:52 +msgid "Title or Description must be set in RssItem" +msgstr "Заголовок или описание должны быть установлены в пункте Rss" + +#: comp/src/gb.web.feed/.src/RssItem.class:107 +msgid "Unexpected element '&1' in " +msgstr "Неожиданный элемент '&1' в " + diff --git a/comp/src/gb.web.feed/.lang/zh.po b/comp/src/gb.web.feed/.lang/zh.po new file mode 100644 index 00000000..e80c3b70 --- /dev/null +++ b/comp/src/gb.web.feed/.lang/zh.po @@ -0,0 +1,119 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.feed 3.15.90\n" +"POT-Creation-Date: 2020-11-18 18:41 UTC\n" +"PO-Revision-Date: 2020-11-18 18:41 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "Web feed parser and generator" +msgstr "Web提要解析器和生成器" + +#: Rss.class:213 +msgid "SkipHours may only contain up to 24 elements" +msgstr "SkipHours最多只能含有24个元素" + +#: Rss.class:217 +msgid "SkipHour element must be in the range [0, 23]" +msgstr "SkipHour元素必须在范围[0,23]" + +#: Rss.class:228 +msgid "SkipDays may only contain up to 7 elements" +msgstr "Skipday最多只能包含7个元素" + +#: Rss.class:255 +msgid "Invalid SkipDays day '&1'" +msgstr "无效的SkipDays日'&1'" + +#: Rss.class:275 +msgid "Invalid day '&1'" +msgstr "日期'&1'无效 " + +#: Rss.class:290 +msgid " expected" +msgstr "需要" + +#: Rss.class:293 +msgid "End-of-file expected" +msgstr "应为文件结尾" + +#: Rss.class:318 +msgid " expected" +msgstr "需要" + +#: Rss.class:322 +msgid " expected" +msgstr "需要" + +#: Rss.class:383 +msgid "Unexpected element '&1' in " +msgstr "中的意外元素'&1'" + +#: Rss.class:404 +msgid " expected" +msgstr "需要" + +#: Rss.class:406 +msgid "SkipHours element '&1' out of range [0, 23]" +msgstr "SkipHours元素'&1'超出范围[0,23]" + +#: Rss.class:420 +msgid " expected" +msgstr "需要" + +#: RssCloud.class:24 +msgid "Domain, Port, Path, RegisterProcedure and Protocol must be set in RssCloud" +msgstr "必须在RssCloud中设置Domain、Port、Path、RegisterProcedure和Protocol" + +#: RssCloud.class:40 +msgid "Invalid RssCloud protocol constant '&1'" +msgstr "RssCloud协议常量'&1'无效" + +#: RssCloud.class:52 +msgid "Invalid RssCloud protocol '&1'" +msgstr "RssCloud协议'&1'无效" + +#: RssEnclosure.class:15 +msgid "Url, Length and Type must be set in RssEnclosure" +msgstr "必须在RssEnclosure中设置Url、Length和Type" + +#: RssImage.class:37 +msgid "Url, Title and Link must be set in RssImage" +msgstr "必须在RssImage中设置Url、Title和Link" + +#: RssImage.class:44 +msgid "Maximum width of RssImage is 144" +msgstr "RssImage的最大宽度为144" + +#: RssImage.class:48 +msgid "Maximum height of RssImage is 400" +msgstr "RssImage的最大高度为100" + +#: RssImage.class:75 +msgid "Unexpected element '&1' in " +msgstr "中的意外元素'&1'" + +#: RssItem.class:52 +msgid "Title or Description must be set in RssItem" +msgstr "必须在RssItem中设置Title或Description" + +#: RssItem.class:107 +msgid "Unexpected element '&1' in " +msgstr "中的意外元素'&1'" + +#: RssSource.class:16 +msgid "Url must be set in RssSource" +msgstr "必须在RssSource中设置Url" + +#: RssTextInput.class:21 +msgid "Title, Description, Name and Link must be set in RssTextInput" +msgstr "必须在RssTextInput中设置Title、Description、Name和Link" + +#: RssTextInput.class:47 +msgid "Unexpected element '&1' in " +msgstr "中的意外元素'&1'" diff --git a/comp/src/gb.web.feed/.project b/comp/src/gb.web.feed/.project new file mode 100644 index 00000000..84278822 --- /dev/null +++ b/comp/src/gb.web.feed/.project @@ -0,0 +1,14 @@ +# Gambas Project File 3.0 +Startup=Main +Icon=Feed-icon.svg +Version=3.12.90 +Component=gb.util +Component=gb.xml +Description="Web feed parser and generator" +Authors="(C) 2017-2019 Tobias Boege " +TabSize=2 +Translate=1 +Language=en_US +Type=Component +SourcePath=/tmp +Packager=1 diff --git a/comp/src/gb.web.feed/.src/Main.module b/comp/src/gb.web.feed/.src/Main.module new file mode 100644 index 00000000..93113731 --- /dev/null +++ b/comp/src/gb.web.feed/.src/Main.module @@ -0,0 +1,28 @@ +' Gambas module file + +Public Sub Main() + Dim sRealInFile As String = "test.xml" + Dim sInFile As String = Temp$() + Dim sOutFile As String = Temp$() + Dim sDiff As String + Dim hRss As New Rss + Dim hItem As New RssItem + + Copy sRealInFile To sInFile + hRss.FromString(File.Load(sInFile)) + File.Save(sOutFile, hRss.ToString()) + + System.Shell = "/bin/bash" + Shell Subst$("diff -u <(sort &1 | sed 's/ *$//' | sed 's/^ *//') <(sort &2 | sed 's/^ *//')", sOutFile, sInFile) To sDiff + Print sDiff + Print "---" + + hRss = New Rss + hRss.Title = "Date test" + hRss.Pub = New RssDate(DateAdd(Now, gb.Hour, -11)) + hRss.LastBuild = New RssDate(DateAdd(Now, gb.Hour, -11), "+0800") + hRss.Add(hItem) + hItem.Title = "Blank RssDate" + hItem.Pub = New RssDate + Print hRss.ToString() +End diff --git a/comp/src/gb.web.feed/.src/Rss.class b/comp/src/gb.web.feed/.src/Rss.class new file mode 100644 index 00000000..e8794b90 --- /dev/null +++ b/comp/src/gb.web.feed/.src/Rss.class @@ -0,0 +1,427 @@ +' Gambas class file + +''' This class represents an RSS document. Its properties are those of its single element. +''' It acts like an array of [RssItem](../rssitem) objects. Add your items in order and then call the +''' ToString() method to get the XML document string. +''' +''' The Title, Link and Description are necessary. All other properties are optional. +''' +''' Conforming to the [RSS 2.0 specification](https://cyber.harvard.edu/rss/rss.html). You have to +''' know the document structure and terminology of RSS to make use of this component. + +Export + +Static Private DAY_NAME As String[] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] ' conforms to how WeekDay() works in Gambas +Static Private MONTH_NAME As String[] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + +' Mandatory elements +'' The name or title of the feed. +Public Title As String +'' The URL to the website corresponding to this feed. +Public {Link} As String +'' A description of the feed's contents. +Public Description As String + +' Optional elements +'' The language the feed content is written in. Use one of the [defined](https://cyber.harvard.edu/rss/languages.html) values. +Public Language As String +'' Copyright notice for the feed content. +Public Copyright As String +'' EMail address of the managing editor. +Public ManagingEditor As String +'' EMail address of the web master. +Public WebMaster As String +'' Publication date/timezone of the feed. E.g. a newspaper with daily publication would change this once a day. If unset, defaults to the time the XML document is written in the local timezone. +Public Pub As RssDate +'' The date/timezone of the last change to the feed contents. If unset, defaults to the time the XML document is written in the local timezone. +Public LastBuild As RssDate +'' An array of categories for this feed. +'' +'' ## See also +'' [../../rsscategory] +Public Categories As RssCategory[] +'' The program which generated the feed. +Public Generator As String +'' A link to the RSS specification, to inform future generations of what an RSS file is, if they find one. +Public Docs As String +'' Description of an associated cloud service. +'' +'' ## See also +'' [../../rsscloud] +Public Cloud As RssCloud +'' "Time to live", indicates how long the feed contents may be cached. +Public Ttl As Integer +'' An image associated with the feed. +'' +'' ## See also +'' [../../rssimage] +Public Image As RssImage +'' [PICS](http://www.w3.org/PICS/) rating of the feed. +Public Rating As String +'' Describes a text input box to be displayed with the feed. +'' +'' ## See also +'' [../../rsstextinput] +Public TextInput As RssTextInput +'' A hint for news aggregators. It gives a number of hours in the range of 0 to 23 when no new content is to be expected in the feed. +'' A client may ignore this setting, but can also use it to save traffic. You may only specify up to 24 skip hours. +Public SkipHours As Integer[] +'' A hint for news aggregators. It gives a number of weekdays (gb.Monday, gb.Tuesday, etc.) when no new content is to be expected in +'' the feed. A client may ignore this setting, but can also use it to save traffic. You may only specify up to 7 skip days. +Public SkipDays As Integer[] + +'' Returns the number of RssItems in this document. +Property Read Count As Integer + +Private $aItems As New RssItem[] + +'' Add an RssItem to the document. If ~Index~ is given, the item is inserted at the given +'' position in the array of items. By default it is inserted at the end. +Public Sub Add(Item As RssItem, Optional Index As Integer) + If IsMissing(Index) Then + $aItems.Add(Item) + Else + $aItems.Add(Item, Index) + Endif +End + +Private Function Count_Read() As Integer + Return $aItems.Count +End + +'' Return the RssItem at position ~Index~. +Public Sub _get(Index As Integer) As RssItem + Return $aItems[Index] +End + +'' Replace the RssItem at position ~Index~. +Public Sub _put(Item As RssItem, Index As Integer) + $aItems[Index] = Item +End + +'' Iterate through all RssItems, in the order they are given in the document. +Public Sub _next() As RssItem + Dim iIndex As Integer + Dim hItem As RssItem + + iIndex = 0 + If Not IsNull(Enum.Index) Then iIndex = Enum.Index + If iIndex > $aItems.Max Then + Enum.Stop() + Return + Endif + hItem = $aItems[iIndex] + Enum.Index = iIndex + 1 + Return hItem +End + +'' Remove ~Length~-many RssItems from the item array, beginning at position ~Index~. By default ~Length~ is 1. +Public Sub Remove(Index As Integer, Optional Length As Integer = 1) + $aItems.Remove(Index, Length) +End + +'' Reverse the order of RssItems. +Public Sub Reverse() + $aItems.Reverse() +End + +'' Sort the items by their date. You can pick ~Mode~ as gb.Ascent or gb.Descent. +'' The default is descending sort so that the newest items are first in the document. +Public Sub Sort(Optional Mode As Integer = gb.Descent) + $aItems.Sort(Mode) +End + +'' Clear the Rss object. Sets all properties to Null and clears the item array. +Public Sub Clear() + Title = Null + {Link} = Null + Description = Null + Language = Null + Copyright = Null + ManagingEditor = Null + WebMaster = Null + Pub = Null + LastBuild = Null + Categories = Null + Generator = Null + Docs = Null + Cloud = Null + Ttl = 0 + Image = Null + Rating = Null + TextInput = Null + SkipHours = Null + SkipDays = Null + $aItems.Clear() +End + +'' Write this Rss object as an XML document and return its contents. +Public Function ToString() As String + Dim hWriter As New XmlWriter + + hWriter.Open(Null, True) + _Write(hWriter) + Return hWriter.EndDocument() +End + +Public Sub _Write(hWriter As XmlWriter) + Dim hItem As RssItem + Dim hCat As RssCategory + + hWriter.StartElement("rss", ["version", "2.0"]) + hWriter.StartElement("channel") + With hWriter + .Element("title", Title) + .Element("link", {Link}) + .Element("description", Description) + If Language Then .Element("language", Language) + If Copyright Then .Element("copyright", Copyright) + If ManagingEditor Then .Element("managingEditor", ManagingEditor) + If WebMaster Then .Element("webMaster", WebMaster) + If Pub Then Pub._Write(hWriter, "pubDate") + If LastBuild Then LastBuild._Write(hWriter, "lastBuildDate") + If Categories Then + For Each hCat In Categories + hCat._Write(hWriter) + Next + Endif + If Generator Then .Element("generator", Generator) + If Docs Then .Element("docs", Docs) + If Cloud Then Cloud._Write(hWriter) + If Ttl Then .Element("ttl", Str$(Ttl)) + If Image Then Image._Write(hWriter) + ' For the Rating, see Holzner: "Secrets of RSS", chapter 4 + If Rating Then .Element("rating", Rating) + If TextInput Then TextInput._Write(hWriter) + If SkipHours Then _WriteSkipHours(hWriter) + If SkipDays Then _WriteSkipDays(hWriter) + End With + + For Each hItem In $aItems + hItem._Write(hWriter) + Next + hWriter.EndElement() + hWriter.EndElement() +End + +'' https://cyber.harvard.edu/rss/skipHoursDays.html + +Private Sub _WriteSkipHours(hWriter As XmlWriter) + Dim iHour As Integer + + If Not SkipHours.Count Then Return + If SkipHours.Count > 24 Then Error.Raise(("SkipHours may only contain up to 24 elements")) + With hWriter + .StartElement("skipHours") + For Each iHour In SkipHours + If iHour < 0 Or If iHour > 23 Then Error.Raise(("SkipHour element must be in the range [0, 23]")) + .Element("hour", Str$(iHour)) + Next + .EndElement() + End With +End + +Private Sub _WriteSkipDays(hWriter As XmlWriter) + Dim iDay As Integer + + If Not SkipDays.Count Then Return + If SkipDays.Count > 7 Then Error.Raise(("SkipDays may only contain up to 7 elements")) + With hWriter + .StartElement("skipDays") + For Each iDay In SkipDays + .Element("day", GetSkipDay(iDay)) + Next + .EndElement() + End With +End + +Private Function GetSkipDay(iDay As Integer) As String + Select Case iDay + Case gb.Monday + Return "Monday" + Case gb.Tuesday + Return "Tuesday" + Case gb.Wednesday + Return "Wednesday" + Case gb.Thursday + Return "Thursday" + Case gb.Friday + Return "Friday" + Case gb.Saturday + Return "Saturday" + Case gb.Sunday + Return "Sunday" + End Select + Error.Raise(Subst$(("Invalid SkipDays day '&1'"), iDay)) +End + +Private Function GetDay(sDay As String) As Integer + Select Case sDay + Case "Monday" + Return gb.Monday + Case "Tuesday" + Return gb.Tuesday + Case "Wednesday" + Return gb.Wednesday + Case "Thursday" + Return gb.Thursday + Case "Friday" + Return gb.Friday + Case "Saturday" + Return gb.Saturday + Case "Sunday" + Return gb.Sunday + End Select + Error.Raise(Subst$(("Invalid day '&1'"), sDay)) +End + +'' Read the RSS document in XML format, given in ~Data~, and fill the properties of this Rss object +'' accordingly. +Public Sub FromString(Data As String) + Dim hReader As New XmlReader + + Clear() + hReader.FromString(Data) + _Read(hReader) +End + +Public Sub _Read(hReader As XmlReader) + hReader.Read() + If Not hReader.Node.Name = "rss" Then Error.Raise((" expected")) + _ReadRss(hReader) + hReader.Read() + If Not hReader.Eof Then Error.Raise(("End-of-file expected")) +End + +' This method is to be used as a While condition. In this case, the loop will continue until +' an end tag is found on the same depth as ~iDepth~. +Static Public Function _NotClosed(hReader As XmlReader, iDepth As Integer) As Boolean + ' The depth of the end tag is apparently one less than that of the start tag. + ' TODO: It would be nice if hReader.Node.Name contained the name of an element also when we encounter its closing tag. + Return (hReader.Node.Type <> XmlReaderNodeType.EndElement) Or (hReader.Depth <> iDepth - 1) +End + +Static Public Sub _GetText(hReader As XmlReader) As String + Dim sRes As String + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While _NotClosed(hReader, iDepth) + If hReader.Node.Type = XmlReaderNodeType.Text Or If hReader.Node.Type = XmlReaderNodeType.CDATA Then sRes &= hReader.Node.Value + hReader.Read() + Wend + Return sRes +End + +Private Sub _ReadRss(hReader As XmlReader) + hReader.Read() + If Not hReader.Node.Name = "channel" Then Error.Raise((" expected")) + _ReadChannel(hReader) + hReader.Read() + ' FIXME: Here, hReader.Depth is 0, instead of -1, as it should be if it was consistent with the observation in _NotClosed() + If hReader.Node.Type <> XmlReaderNodeType.EndElement Or If hReader.Depth <> 0 Then Error.Raise((" expected")) +End + +Private Sub _ReadChannel(hReader As XmlReader) + Dim hCat As RssCategory, aCategories As New RssCategory[] + Dim hItem As RssItem, aItems As New RssItem[] + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While _NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "title" + Title = _GetText(hReader) + Case "link" + {Link} = _GetText(hReader) + Case "description" + Description = _GetText(hReader) + Case "language" + Language = _GetText(hReader) + Case "copyright" + Copyright = _GetText(hReader) + Case "managingEditor" + ManagingEditor = _GetText(hReader) + Case "webMaster" + WebMaster = _GetText(hReader) + Case "pubDate" + Pub = New RssDate + Pub._Read(hReader) + Case "lastBuildDate" + LastBuild = New RssDate + LastBuild._Read(hReader) + Case "category" + hCat = New RssCategory + hCat._Read(hReader) + aCategories.Add(hCat) + Case "generator" + Generator = _GetText(hReader) + Case "docs" + Docs = _GetText(hReader) + Case "cloud" + Cloud = New RssCloud + Cloud._Read(hReader) + Case "ttl" + Ttl = CInt(_GetText(hReader)) + Case "image" + Image = New RssImage + Image._Read(hReader) + Case "rating" + Rating = _GetText(hReader) + Case "textInput" + TextInput = New RssTextInput + TextInput._Read(hReader) + Case "skipHours" + _ReadSkipHours(hReader) + Case "skipDays" + _ReadSkipDays(hReader) + Case "item" + hItem = New RssItem + hItem._Read(hReader) + aItems.Add(hItem) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + ' XXX: Empty tags can be or . If the last thing we read was of the + ' latter kind, we have to Read() one step further to consume its closing tag. If it was + ' a self-closing element, we must not read any further. + ' + ' This is how I check for this currently: we have to be at the end of an element now. + ' XmlReader sets hReader.Node.Type To XmlReaderNodeType.EndElement only if a real + ' ending tag was encountered. A self-closing element is an XmlReaderNodeType.Element. + If hReader.Node.Type = XmlReaderNodeType.EndElement Then hReader.Read() + Wend + If aCategories.Count Then Categories = aCategories + $aItems = aItems +End + +Private Sub _ReadSkipHours(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + Dim iHour As Integer, aHours As New Integer[] + + hReader.Read() + While _NotClosed(hReader, iDepth) + If hReader.Node.Name <> "hour" Then Error.Raise((" expected")) + iHour = CInt(_GetText(hReader)) + If iHour < 0 Or If iHour > 23 Then Error.Raise(Subst$(("SkipHours element '&1' out of range [0, 23]"), iHour)) + aHours.Add(iHour) + hReader.Read() + Wend + If aHours.Count > 24 Then Error.Raise(("SkipHours may only contain up to 24 elements")) + If aHours.Count Then SkipHours = aHours +End + +Private Sub _ReadSkipDays(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + Dim iDay As Integer, aDays As New Integer[] + + hReader.Read() + While _NotClosed(hReader, iDepth) + If hReader.Node.Name <> "day" Then Error.Raise((" expected")) + iDay = GetDay(_GetText(hReader)) + aDays.Add(iDay) + hReader.Read() + Wend + If aDays.Count > 7 Then Error.Raise(("SkipDays may only contain up to 7 elements")) + If aDays.Count Then SkipDays = aDays +End diff --git a/comp/src/gb.web.feed/.src/RssCategory.class b/comp/src/gb.web.feed/.src/RssCategory.class new file mode 100644 index 00000000..23c2f975 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssCategory.class @@ -0,0 +1,24 @@ +' Gambas class file + +''' This class represents an RSS category. The RSS specification does not provide a standard +''' set of categories. You can invent your own (site-specific) categories. + +Export + +'' The category description string. Use forward slashes as separators to structure categories hierarchically. +Public Category As String +'' The category domain is an ID for, or a URL to, a category taxonomy description. +Public Domain As String + +Public Sub _Write(hWriter As XmlWriter) + With hWriter + .StartElement("category", IIf(Domain, ["domain", Domain], Null)) + .Text(Category) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Domain = hReader.Node.Attributes["domain"] + Category = Rss._GetText(hReader) +End diff --git a/comp/src/gb.web.feed/.src/RssCloud.class b/comp/src/gb.web.feed/.src/RssCloud.class new file mode 100644 index 00000000..7b9d7da4 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssCloud.class @@ -0,0 +1,64 @@ +' Gambas class file + +''' This class can be used to specify a web application implementing the rssCloud interface. +''' An rssCloud application notifies its registrees of changes in subscribed RSS channels. +''' +''' See [https://cyber.harvard.edu/rss/soapMeetsRss.html#rsscloudInterface] for an explanation. + +Export + +Public Enum XmlRpc, Soap, HttpPost ' Protocol constants + +'' Domain name of the application server. +Public Domain As String +'' TCP port of the application. +Public Port As Integer +'' The script path on the server. +Public Path As String +'' The name of the registration procedure to be called on the server side. +Public RegisterProcedure As String +'' The rssCloud protocol to use. One of [../xmlrpc], [../soap] or [../httppost]. +Public Protocol As Integer + +Public Sub _Write(hWriter As XmlWriter) + If Not Domain Or If Not Port Or If Not Path Or If Not RegisterProcedure Or If Not Protocol Then Error.Raise(("Domain, Port, Path, RegisterProcedure and Protocol must be set in RssCloud")) + With hWriter + .StartElement("cloud", ["domain", Domain, "port", Str$(Port), "path", Path, "registerProcedure", RegisterProcedure, "protocol", GetProtocol(Protocol)]) + .EndElement() + End With +End + +Private Function GetProtocol(iProto As Integer) As String + Select Case iProto + Case XmlRpc + Return "xml-rpc" + Case Soap + Return "soap" + Case HttpPost + Return "http-post" + End Select + Error.Raise(Subst$(("Invalid RssCloud protocol constant '&1'"), iProto)) +End + +Private Function GetProtocolConst(sProto As String) As Integer + Select Case sProto + Case "xml-rpc" + Return XmlRpc + Case "soap" + Return Soap + Case "http-post" + Return HttpPost + End Select + Error.Raise(Subst$(("Invalid RssCloud protocol '&1'"), sProto)) +End + +Public Sub _Read(hReader As XmlReader) + With hReader.Node.Attributes + Domain = .["domain"] + Port = CInt(.["port"]) + Path = .["path"] + RegisterProcedure = .["registerProcedure"] + Protocol = GetProtocolConst(.["protocol"]) + End With + hReader.Read() +End diff --git a/comp/src/gb.web.feed/.src/RssDate.class b/comp/src/gb.web.feed/.src/RssDate.class new file mode 100644 index 00000000..00a72ee0 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssDate.class @@ -0,0 +1,38 @@ +' Gambas class file + +''' This class represents a date in RSS. It is comprised of a Gambas Date and +''' a Zone timezone string. Since dates are absolute in Gambas, the timezone is +''' only used during reading and writing the XML document backing an RSS feed. +''' It does not have to "applied" to the Date in any way by the user of this component. + +Export + +' We have a member named "Date", which overshadows the global class of the same name. +' Get a reference to it here. +Static Private DateClass As Object = Classes["Date"].New() + +'' The date represented by this object. +Public {Date} As Date +'' The timezone relative to which the Date was or should be printed. +Public Zone As String + +Public Sub _new(Optional {Date} As Date, Optional Zone As String) + ' Now in the local timezone by default + If IsMissing({Date}) Then {Date} = Now + If IsMissing(Zone) Then Zone = Format$(Now, "tt") + Me.Date = {Date} + Me.Zone = Zone +End + +Public Sub _Read(hReader As XmlReader) + {Date} = DateClass.FromRFC822(Rss._GetText(hReader), ByRef Zone) +End + +' The sTag parameter avoids subclassing RssDate to RssPubDate and RssLastBuildDate. +Public Sub _Write(hWriter As XmlWriter, sTag As String) + With hWriter + .StartElement(sTag) + .Text(DateClass.ToRFC822(Me.Date, Me.Zone)) + .EndElement() + End With +End diff --git a/comp/src/gb.web.feed/.src/RssEnclosure.class b/comp/src/gb.web.feed/.src/RssEnclosure.class new file mode 100644 index 00000000..ca6b0733 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssEnclosure.class @@ -0,0 +1,29 @@ +' Gambas class file + +''' This class represents a media attachment to an RSS item. + +Export + +'' The URL of the attached file. +Public Url As String +'' The length of the file in bytes. +Public Length As Long +'' The MIME type of the file. +Public Type As String + +Public Sub _Write(hWriter As XmlWriter) + If Not Url Or If Not Length Or If Not Type Then Error.Raise(("Url, Length and Type must be set in RssEnclosure")) + With hWriter + .StartElement("enclosure", ["url", Url, "type", Type, "length", Str$(Length)]) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + With hReader.Node.Attributes + Url = .["url"] + Length = CLong(.["length"]) + Type = .["type"] + End With + hReader.Read() +End diff --git a/comp/src/gb.web.feed/.src/RssGuid.class b/comp/src/gb.web.feed/.src/RssGuid.class new file mode 100644 index 00000000..6ae27913 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssGuid.class @@ -0,0 +1,37 @@ +' Gambas class file + +''' This class represents a GUID (**G**lobally **U**nique **ID**entifier) of an RSS item. +''' The RSS specification does not prescribe a particular format. A news aggregator may use +''' this property, if present, to determine whether the item is new. +''' +''' By default the [IsPermaLink] property is True, meaning permalinks are the default way +''' to assign the item a GUID. + +Export + +'' The GUID string. +Public Guid As String +'' Returns or sets if this GUID is a [permalink](https://en.wikipedia.org/wiki/Permalink). +'' The default value of this property is True. +Public IsPermaLink As Boolean + +Public Sub _new() + IsPermaLink = True +End + +Public Sub _Write(hWriter As XmlWriter) + With hWriter + .StartElement("guid", IIf(IsPermaLink, ["isPermaLink", "true"], Null)) + .Text(Guid) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + If hReader.Node.Attributes.Exist("isPermaLink") Then + IsPermaLink = (hReader.Node.Attributes["isPermaLink"] = "true") + Else + IsPermaLink = False + Endif + Guid = Rss._GetText(hReader) +End diff --git a/comp/src/gb.web.feed/.src/RssImage.class b/comp/src/gb.web.feed/.src/RssImage.class new file mode 100644 index 00000000..cebf51c7 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssImage.class @@ -0,0 +1,79 @@ +' Gambas class file + +''' This class represents an image which is associated with an RSS feed. +''' The Title and Link properties should be the same as the corresponding ones of the +''' parent Rss object. + +Export + +'' The URL of the image file. The specification requires this to be a GIF, JPEG or PNG file. +Public Url As String +'' An image title. It is to be used as the 'alt' attribute of the '' HTML tag if the feed is rendered as HTML. +'' +'' ## See also +'' [../link], [../description] +Public Title As String +'' The URL of the website the feed belongs to. When the feed is rendered as HTML the RssImage becomes a link to this website. +'' +'' ## See also +'' [../title], [../description] +Public {Link} As String +'' The width of the image. The maximum width is 144, the default is 88. +Public Width As Integer +'' The height of the image. The maximum height is 400, the default is 31. +Public Height As Integer +'' If the feed is rendered as HTML this string is to be become the 'title' attribute of the link rendered around the image. +'' +'' ## See also +'' [../title], [../link] +Public Description As String + +Public Sub _new() + Width = 88 + Height = 31 +End + +Public Sub _Write(hWriter As XmlWriter) + If Not Url Or If Not Title Or If Not {Link} Then Error.Raise(("Url, Title and Link must be set in RssImage")) + With hWriter + .StartElement("image") + .Element("url", Url) + .Element("title", Title) + .Element("link", {Link}) + If Width Then + If Width > 144 Then Error.Raise(("Maximum width of RssImage is 144")) + .Element("width", Str$(Width)) + Endif + If Height Then + If Height > 400 Then Error.Raise(("Maximum height of RssImage is 400")) + .Element("height", Str$(Height)) + Endif + If Description Then .Element("description", Description) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While Rss._NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "url" + Url = Rss._GetText(hReader) + Case "title" + Title = Rss._GetText(hReader) + Case "link" + {Link} = Rss._GetText(hReader) + Case "width" + Width = CInt(Rss._GetText(hReader)) + Case "height" + Height = CInt(Rss._GetText(hReader)) + Case "description" + Description = Rss._GetText(hReader) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + hReader.Read() + Wend +End diff --git a/comp/src/gb.web.feed/.src/RssItem.class b/comp/src/gb.web.feed/.src/RssItem.class new file mode 100644 index 00000000..806030c7 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssItem.class @@ -0,0 +1,113 @@ +' Gambas class file + +''' This class represents a single news item. Create an object of this class, fill its properties +''' and add it to an existing [Rss](../rss) object. +''' +''' All properties of this class are optional, but you have to fill at least Title *or* Description. + +Export + +'' The title of the news item. +Public Title As String +'' A link to the website containing the full content. +Public {Link} As String +'' Synopsis of the item. You can use entity-encoded HTML. +Public Description As String +'' EMail address of the author. +Public Author As String +'' An array of categories for this item. +'' +'' ## See also +'' [../../rsscategory] +Public Categories As RssCategory[] +'' A URL pointing to a comment page for the item. +Public Comments As String +'' Describes a media attachment to this item. +'' +'' ## See also +'' [../../rssenclosure] +Public Enclosure As RssEnclosure +'' A **G**lobally **U**nique **ID**entifier for this item, e.g. a [permalink](https://en.wikipedia.org/wiki/Permalink) +'' to the item's full content on your website. +'' +'' ## See also +'' [../../rssguid] +Public Guid As RssGuid +'' Publication date/timezone of this item. If unset, defaults to the time the XML document is written in the local timezone. +Public Pub As RssDate +'' If this item comes from another RSS feed, use this property to link to the original feed. +'' +'' ## See also +'' [../../rsssource] +Public Source As RssSource + +Public Sub _compare(Other As RssItem) As Integer + ' Only up to seconds. The time formatting in RSS is not any finer anyway. + Return Sgn(DateDiff(Other.Pub.Date, Pub.Date, gb.Second)) +End + +Public Sub _Write(hWriter As XmlWriter) + Dim hCat As RssCategory + + If Not Title And If Not Description Then Error.Raise(("Title or Description must be set in RssItem")) + hWriter.StartElement("item") + With hWriter + If Title Then .Element("title", Title) + If {Link} Then .Element("link", {Link}) + If Description Then .Element("description", Description) + If Author Then .Element("author", Author) + If Categories Then + For Each hCat In Categories + hCat._Write(hWriter) + Next + Endif + If Comments Then .Element("comments", Comments) + If Enclosure Then Enclosure._Write(hWriter) + If Guid Then Guid._Write(hWriter) + If Pub Then Pub._Write(hWriter, "pubDate") + If Source Then Source._Write(hWriter) + End With + hWriter.EndElement() +End + +Public Sub _Read(hReader As XmlReader) + Dim hCat As RssCategory, aCategories As New RssCategory[] + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While Rss._NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "title" + Title = Rss._GetText(hReader) + Case "link" + {Link} = Rss._GetText(hReader) + Case "description" + Description = Rss._GetText(hReader) + Case "author" + Author = Rss._GetText(hReader) + Case "category" + hCat = New RssCategory + hCat._Read(hReader) + aCategories.Add(hCat) + Case "comments" + Comments = Rss._GetText(hReader) + Case "enclosure" + Enclosure = New RssEnclosure + Enclosure._Read(hReader) + Case "guid" + Guid = New RssGuid + Guid._Read(hReader) + Case "pubDate" + Pub = New RssDate + Pub._Read(hReader) + Case "source" + Source = New RssSource + Source._Read(hReader) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + ' XXX: See Rss._ReadChannel() for this construction. + If hReader.Node.Type = XmlReaderNodeType.EndElement Then hReader.Read() + Wend + If aCategories.Count Then Categories = aCategories +End diff --git a/comp/src/gb.web.feed/.src/RssSource.class b/comp/src/gb.web.feed/.src/RssSource.class new file mode 100644 index 00000000..b7debc58 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssSource.class @@ -0,0 +1,27 @@ +' Gambas class file + +''' This class describes a foreign source of an RSS item which you included in your RSS feed. + +Export + +'' The feed title of the source RSS feed. +'' +'' ## See also +'' [Rss.Title](../../rss/title) +Public Source As String +'' Link to the foreign RSS feed's XML document. +Public Url As String + +Public Sub _Write(hWriter As XmlWriter) + If Not Url Then Error.Raise(("Url must be set in RssSource")) + With hWriter + .StartElement("source", ["url", Url]) + .Text(Source) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Url = hReader.Node.Attributes["url"] + Source = Rss._GetText(hReader) +End diff --git a/comp/src/gb.web.feed/.src/RssTextInput.class b/comp/src/gb.web.feed/.src/RssTextInput.class new file mode 100644 index 00000000..852d9553 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssTextInput.class @@ -0,0 +1,51 @@ +' Gambas class file + +''' This class represents a text input field which the news aggregator may display +''' with your feed. The [RSS specification](https://cyber.harvard.edu/rss/rss.html#lttextinputgtSubelementOfLtchannelgt) +''' admits that this element's purpose is unclear and that it may not be widely supported. +''' It can be used as a built-in mechanism to provide feedback to the feed owner's website +''' or as a search box. + +Export + +'' The label of the submit button next to the input box. +Public Title As String +'' Explanation of the input box's purpose. +Public Description As String +'' The name of the input box, for use by the CGI script the input is sent to. +Public Name As String +'' Link to the CGI script which processes the input. +Public {Link} As String + +Public Sub _Write(hWriter As XmlWriter) + If Not Title Or If Not Description Or If Not Name Or If Not {Link} Then Error.Raise(("Title, Description, Name and Link must be set in RssTextInput")) + With hWriter + .StartElement("textInput") + .Element("title", Title) + .Element("description", Description) + .Element("name", Name) + .Element("link", {Link}) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While Rss._NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "title" + Title = Rss._GetText(hReader) + Case "description" + Description = Rss._GetText(hReader) + Case "name" + Name = Rss._GetText(hReader) + Case "link" + {Link} = Rss._GetText(hReader) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + hReader.Read() + Wend +End diff --git a/comp/src/gb.web.feed/Feed-icon.svg b/comp/src/gb.web.feed/Feed-icon.svg new file mode 100644 index 00000000..11fd98a5 --- /dev/null +++ b/comp/src/gb.web.feed/Feed-icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/comp/src/gb.web.feed/test.xml b/comp/src/gb.web.feed/test.xml new file mode 100644 index 00000000..7f981ba3 --- /dev/null +++ b/comp/src/gb.web.feed/test.xml @@ -0,0 +1,41 @@ + + + + Liftoff News + http://liftoff.msfc.nasa.gov/ + Liftoff to Space Exploration. + en-us + Tue, 10 Jun 2003 04:00:00 GMT + Tue, 10 Jun 2003 09:41:01 GMT + http://blogs.law.harvard.edu/tech/rss + Weblog Editor 2.0 + editor@example.com + webmaster@example.com + + Star City + http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp + How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>. + Tue, 03 Jun 2003 09:39:21 GMT + http://liftoff.msfc.nasa.gov/2003/06/03.html#item573 + + + Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a <a href="http://science.nasa.gov/headlines/y2003/30may_solareclipse.htm">partial eclipse of the Sun</a> on Saturday, May 31st. + Fri, 30 May 2003 11:06:42 GMT + http://liftoff.msfc.nasa.gov/2003/05/30.html#item572 + + + The Engine That Does More + http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp + Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that. + Tue, 27 May 2003 08:37:32 GMT + http://liftoff.msfc.nasa.gov/2003/05/27.html#item571 + + + Astronauts' Dirty Laundry + http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp + Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options. + Tue, 20 May 2003 08:56:02 GMT + http://liftoff.msfc.nasa.gov/2003/05/20.html#item570 + + + \ No newline at end of file diff --git a/comp/src/gb.web.form/.component b/comp/src/gb.web.form/.component new file mode 100644 index 00000000..d81cc075 --- /dev/null +++ b/comp/src/gb.web.form/.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.web.form +Version=3.17.90 +State=3 +Authors=Benoît Minisini +Requires=gb.web,gb.util.web,gb.util +Excludes=gb.gui,gb.gui.qt,gb.gtk,gb.gtk3,gb.qt4,gb.qt5,gb.sdl2,gb.sdl,gb.web.form2 diff --git a/comp/src/gb.web.form/.directory b/comp/src/gb.web.form/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.web.form/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css b/comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css new file mode 100644 index 00000000..549123a6 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css @@ -0,0 +1,504 @@ +HTML, BODY { + margin: 0; + padding: 0; + height: 100%; + width: 100%; +} + +HTML, BODY, DIV { + box-sizing: border-box; +} + +SELECT { + min-height: 2rem; + font-size: inherit; +} + +INPUT { + font: inherit; +} + +H1,H2,H3,P { + margin-top: 0.5rem; + margin-bottom: 0.25rem; +} + +/*UL, OL { + padding-left: 2em; + margin-bottom: 0; +} + +P:first-child,UL:first-child,OL:first-child { + margin-top: 0; +}*/ + +.gw-button { + padding: 0; + font: inherit; + padding: 0 0.25em; + /*min-height: 1rem;*/ +} + +.gw-button.gw-noborder { + border: solid 1px transparent; + background: none; + padding: 0; +} + +.gw-button.gw-noborder:hover { + border: solid 1px #C0C0C0; +} + +.gw-button-image { + margin-right: 0.5em; +} + +.gw-button > IMG { + vertical-align: middle; +} + +.gw-button > DIV { + display: inline-block; + vertical-align: middle; +} + +.gw-tab-header { + margin-bottom: -1px; + z-index: 1; + line-height: 2em; +} + +.gw-tab-header.gw-noborder { + margin-bottom: 0; +} + +.gw-tab { + color: gray; + padding: 0 0.5em; + cursor: pointer; + border-top: solid transparent 1px; + border-right: solid transparent 1px; + border-left: solid transparent 1px; +} + +.gw-tab:hover { + color: black; +} + +.gw-tab-selected { + padding: 0 0.5em; + border-left: solid #C0C0C0 1px; + border-top: solid #C0C0C0 1px; + border-right: solid #C0C0C0 1px; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + background: white; +} + +.gw-tab-header.gw-noborder > div { + border: none; +} + +.gw-tab-contents { + border: solid #C0C0C0 1px; + flex-grow: 1; + display: flex; + flex-flow: column; +} + +.gw-tab-contents > DIV { + flex-grow: 1; +} + +.gw-tab-contents.gw-noborder { + border: none; +} + +.gw-checkbox-label,.gw-radiobutton-label { + vertical-align: middle; +} + +.gw-checkbox-toggle,.gw-radiobutton-toggle { + vertical-align: middle; + margin: 0; + margin-right: 0.5em; +} + +.gw-textbox { + min-height: 2rem; + box-sizing: border-box; + padding: 0 0.25em; +} +.gw-textbox.gw-noborder { + border: none; +} + +.gw-combobox { + display: inline-block; + position: relative; + min-height: 2rem; +} + +.gw-combobox SELECT { + height: 100%; + z-index: 1; + opacity: 0; + float: right; +} + +.gw-combobox INPUT { + width: 100%; + min-height: 2rem; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-combobox.gw-noborder INPUT { + border: none; +} + +.gw-combobox-arrow { + display: inline-block; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAI0lEQVQYlWNgoAZoYGBg+I8DN+BT1MCABhrwSSIrwilJHgAAbCcP9dwIQGIAAAAASUVORK5CYII='); + background-position: center; + background-repeat: no-repeat; + height: 2rem; + width: 2rem; + margin-left: -2rem; + /*pointer-events: none;*/ + vertical-align: middle; + border: none; + overflow: hidden; +} + +.gw-selectbox { + display: inline-block; + position: relative; + min-height: 2rem; +} + +.gw-selectbox SELECT { + width: 100%; +} + +.gw-selectbox.gw-noborder SELECT { + border: none; +} + +.gw-window { + position: fixed; + border: solid 1px white; + box-shadow: 0 0 0.5em black; + border-radius: 0.5em; + background: white; + z-index: 10; + overflow: hidden; +} + +.gw-window-titlebar { + display: flex; + flex-flow: row; + background: #2980B9; + font-weight: bold; + height: 2em; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; +} + +.gw-window-title { + flex-grow: 1; + color: white; + line-height: 2em; + padding-left: 0.5em; + pointer-events: none; +} + +.gw-window-button { + position: absolute; + padding: 0; + width: 1.5em; + height: 1.5em; + top: 0.25em; + right: 0.25em; +} + +/*@-moz-document url-prefix() { + .gw-window-button { + display: inline-flex; + }; +}*/ + +.gw-window-contents { + display: flex; + flex-flow: column; + flex-grow: 1; + padding: 0.5em; +} + +.gw-label { + display: flex; + align-items: center; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +#gw-modal { + display: none; + position: fixed; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: black; + opacity: 0.05; +} + +.gw-textarea { + font-family: inherit; + resize: none; +} + +.gw-textarea.gw-noborder { + border: none; +} + + +.gw-menu-title { + display: inline-table; + padding: 0.5em; + padding-top: 0.25em; + cursor: default; + border-top: solid 1px transparent; + border-left: solid 1px transparent; + border-right: solid 1px transparent; + /*margin-top: 0.25em; + margin-bottom: 0.25em;*/ +} + +.gw-menu:hover > .gw-menu-title { + border-top: solid 1px #C0C0C0; + border-left: solid 1px #C0C0C0; + border-right: solid 1px #C0C0C0; + box-shadow: 0 0 0.25em #C0C0C0; + background: white; + z-index: 1002; +} + +.gw-submenu { + display: none; + position: absolute; + border: solid 1px #C0C0C0; + background: white; + box-shadow: 0 0.125em 0.25em #C0C0C0; + margin-top: -0.25em; + z-index: 1003; +} + +.gw-menuitem > .gw-menu > .gw-submenu { + margin-top: 0; + top: 0; + left: 100%; +} + +.gw-menu:hover > .gw-submenu { + display: table; +} + +.gw-menu-tape { + display: none; + position: relative; + background: white; + z-index: 1004; + height: 4px; + margin-top: -4px; + margin-left: 1px; + margin-right: 1px; + top: -2px; +} + +.gw-menu:hover > .gw-menu-tape { + display: block; +} + +.gw-submenu > div:hover { + background: #E0E0E0; +} + +.gw-menuitem { + display: flex; + flex-flow: column; +} + +.gw-menuitem > div { + display: table-cell !important; + vertical-align: middle; +} + +.gw-menuitem-text { + flex-grow: 1; + padding: 0.125em 0.25em; + white-space: nowrap; +} + +.gw-menuitem-icon { + padding: 0.125em 0.25em; +} + +.gw-menuitem-icon > IMG { + vertical-align: middle; +} + +.gw-menuitem-shortcut { + text-align: right; + padding: 0.125em 1em; +} + +.gw-separator { + position: relative; + padding: 0 !important; + pointer-events: none; +} + +.gw-separator-hline { + position: absolute; + display: flex; + flex-flow: row; + align-items: center; + height: inherit; + width: 100%; +} + +.gw-separator-hline > div { + height: 1px; + width: 100%; + background: #C0C0C0; +} + +.gw-separator-vline { + position: absolute; + display: flex; + flex-flow: column; + align-items: center; + height: 100%; + width: inherit; +} + +.gw-separator-vline > div { + width: 1px; + height: 100%; + background: #C0C0C0; +} + +.gw-submenu > div { + display: flex !important; + cursor: default; + padding: 0.25em 1em; + margin: 0; +} + +.gw-submenu > .gw-menuitem { + display: table-row !important; + table-layout: fixed; + cursor: default; + padding: 0; + margin: 0; +} + +.gw-submenu > .gw-separator { + display: table-row !important; + cursor: default; + margin: 0; + width: auto; + height: 1em; +} + +.gw-menuitem > .gw-menu { + display: table-cell !important; + position: relative; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAJElEQVQYlWNgQIAGBgLgPyFF/wkp+o9NERMhe0m2AqckAy5JALuADHot/KmNAAAAAElFTkSuQmCC'); + background-position: center; + background-repeat: no-repeat; + width: 1em; +} + +.gw-spinbox.gw-noborder { + border: none; +} + +.gw-expander-header + DIV { + margin-top: 0.5em; +} + +.gw-expander-header > IMG { + vertical-align: middle; +} + +.gw-expander-header > DIV { + display: inline-table; + vertical-align: middle; +} + +.gw-expander-border { + border: solid 1px #C0C0C0; + padding: 0.5em; +} + +.gw-table { + position: relative; + overflow: auto; + min-height: 4em; + border: solid 1px #C0C0C0; +} + +.gw-table.gw-noborder { + border: none; +} + +/*.gw-table-header { + height: 1.5em; + background-color: yellow; +}*/ + +.gw-table-contents { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.gw-table > DIV > TABLE { + border-collapse: collapse; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH { + text-align: left; + background-color: #E0E0E0; + padding: 0.25em 0.5em; + vertical-align: top; + border-bottom: solid 1px #C0C0C0; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR > TD { + /*border-right: solid 1px #C0C0C0;*/ + padding: 0.25em 0.5em; + vertical-align: top; +} + +/*.gw-table > DIV > TABLE > TBODY > TR > TD:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR:last-child > TD { + border-bottom: solid 1px #C0C0C0; +}*/ + +.gw-table > DIV > TABLE > TBODY > TR:nth-child(even) { + background-color: #F0F0F0; +} diff --git a/comp/src/gb.web.form/.hidden/Uncompressed/lib.js b/comp/src/gb.web.form/.hidden/Uncompressed/lib.js new file mode 100644 index 00000000..164ffbd9 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/Uncompressed/lib.js @@ -0,0 +1,1321 @@ +function $(a) +{ + return document.getElementById(a); +} + +if (!String.prototype.endsWith) +{ + String.prototype.endsWith = function(searchString, position) + { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; +} + +Element.prototype.hasClass = function(klass) +{ + if (this.classList) + return this.classList.contains(klass); + else + return !!this.className.match(new RegExp('(\\s|^)' + klass + '(\\s|$)')); +}; + +Element.prototype.addClass = function(klass) +{ + if (this.classList) + this.classList.add(klass); + else if (!this.hasClass(klass)) + this.className += " " + klass; +}; + +Element.prototype.removeClass = function(klass) +{ + if (this.classList) + this.classList.remove(klass); + else if (this.hasClass(klass)) + { + var reg = new RegExp('(\\s|^)' + klass + '(\\s|$)'); + this.className = this.className.replace(reg, ' '); + } +}; + +/*Element.prototype.ensureVisible = function() +{ + var parent = this.offsetParent; + + while (parent && parent.clientHeight == parent.scrollHeight && parent.clientWidth == parent.scrollWidth) + parent = parent.offsetParent; + + if (parent) + gw.ensureVisible(this.offsetParent, this.offsetLeft, this.offsetTop, this.offsetWidth, this.offsetHeight); +};*/ + +gw = { + + version: '0', + commands: [], + timers: {}, + windows: [], + form: '', + debug: false, + loaded: {}, + uploads: {}, + autocompletion: [], + focus: false, + lock: 0, + + log: function(msg) + { + if (gw.debug) + { + if (gw.startTime == undefined) + gw.startTime = Date.now(); + console.log(((Date.now() - gw.startTime) / 1000).toFixed(3) + ': ' + msg); + } + }, + + load: function(lib) + { + var elt, src; + + if (gw.loaded[lib]) + return; + + if (lib.endsWith('.js')) + { + elt = document.createElement('script'); + elt.setAttribute("type", "text/javascript"); + src = $root + '/lib:' + lib.slice(0, -3) + ':' + gw.version + '.js'; + elt.setAttribute("src", src); + } + else if (lib.endsWith('.css')) + { + elt = document.createElement('link'); + elt.setAttribute("rel", "stylesheet"); + elt.setAttribute("type", "text/css"); + src = $root + '/style:' + lib.slice(0, -4) + ':' + gw.version + '.css'; + elt.setAttribute("href", src); + } + else + return; + + document.getElementsByTagName("head")[0].appendChild(elt); + gw.loaded[lib] = src; + console.log('load: ' + src); + }, + + setInnerHtml : function(id, html) + { + var oldDiv = $(id); + var newDiv = oldDiv.cloneNode(false); + newDiv.innerHTML = html; + oldDiv.parentNode.replaceChild(newDiv, oldDiv); + }, + + setOuterHtml : function(id, html) + { + if ($(id)) + $(id).outerHTML = html; + else + console.log('setOuterHtml: ' + id + '? ' + html); + }, + + removeElement : function(id) + { + var elt = $(id); + //for (i = 0; i < id_list.length; i++) + //{ + //elt = $(id_list[i]); + if (!elt) + return; + + //console.log(id + " removed"); + + elt.parentNode.removeChild(elt); + //} + }, + + insertElement : function(id, parent) + { + var elt = document.createElement('div'); + elt.id = id; + + $(parent).appendChild(elt); + }, + + setVisible : function(id, visible) + { + var elt = $(id); + if (elt) + { + if (visible) + elt.removeClass('gw-hidden'); + else + elt.addClass('gw-hidden'); + } + }, + + saveFocus: function() { + var active = document.activeElement.id; + var selection; + + if (active) + selection = gw.getSelection($(active)); + + return [active, selection]; + }, + + restoreFocus: function(save) { + var elt; + + if (save[0]) + { + elt = $(save[0]) + if (elt) + { + elt.focus(); + gw.setSelection(elt, save[1]); + } + } + //else + // gw.active = document.activeElement.id; + }, + + wait: function(lock) { + var elt; + + if (lock) + { + if (gw.lock == 0) + { + elt = $('gw-lock'); + elt.style.zIndex = 1000; + elt.style.display = 'block'; + + gw.lock_id = setTimeout(function() { + $('gw-lock').style.opacity = '1'; + }, 500); + } + + gw.lock++; + } + else + { + gw.lock--; + if (gw.lock == 0) + { + if (gw.lock_id) + { + clearTimeout(gw.lock_id); + gw.lock_id = undefined; + } + elt = $('gw-lock'); + elt.style.display = 'none'; + elt.style.opacity = '0'; + } + } + }, + + answer: function(xhr, after) + { + if (xhr.readyState == 4) + { + if (xhr.status == 200 && xhr.responseText) + { + xhr.gw_command && gw.log('==> ' + xhr.gw_command + '...'); + + gw.focus = false; + var save = gw.saveFocus(); + + /*if (gw.debug) + console.log('--> ' + xhr.responseText);*/ + + var r = xhr.responseText.split('\n'); + var i, expr; + + for (i = 0; i < r.length; i++) + { + expr = r[i].trim(); + if (expr.length == 0) + continue; + if (gw.debug) + { + if (expr.length > 1024) + gw.log('--> ' + expr.substr(0, 1024) + '...'); + else + gw.log('--> ' + expr); + } + eval(expr); + } + + //eval(xhr.responseText); + + if (!gw.focus) + gw.restoreFocus(save); + + } + + if (after) + after(); + + xhr.gw_command && gw.log('==> ' + xhr.gw_command + ' done.'); + + if (xhr.gw_command && (xhr.gw_command.length < 5 || xhr.gw_command[4] == undefined || xhr.gw_command[4] == false)) + gw.wait(false); + + gw.commands.splice(0, 2); + gw.sendNewCommand(); + } + }, + + sendNewCommand: function() + { + var command, after, len; + var xhr; + + for(;;) { + + len = gw.commands.length; + + if (len < 2) + return; + + command = gw.commands[0]; + after = gw.commands[1]; + + gw.log('[ ' + command + ' ]'); + + if (command) + { + if (command.length < 5 || command[4] == undefined || command[4] == false) + gw.wait(true); + + xhr = new XMLHttpRequest(); + xhr.gw_command = command; + xhr.open('GET', $root + '/x?c=' + encodeURIComponent(JSON.stringify(command)), true); + xhr.onreadystatechange = function() { gw.answer(xhr, after); }; + xhr.send(null); + return; + } + + after(); + gw.commands.splice(0, 2); + } + }, + + send: function(command, after) + { + gw.log('gw.send: ' + command + ' ' + JSON.stringify(gw.commands)); + + gw.commands.push(command); + gw.commands.push(after); + + if (gw.commands.length == 2) + gw.sendNewCommand(); + }, + + raise: function(id, event, args, no_wait) + { + gw.send(['raise', id, event, args, no_wait]); + }, + + update: function(id, prop, value, after) + { + gw.send(['update', id, prop, value, true], after); + }, + + command: function(action) + { + gw.send(null, action); + }, + + getSelection: function(o) + { + var start, end; + + try + { + if (o.createTextRange) + { + var r = document.selection.createRange().duplicate(); + r.moveEnd('character', o.value.length) + if (r.text == '') + start = o.value.length; + else + start = o.value.lastIndexOf(r.text); + r.moveStart('character', -o.value.length); + end = r.text.length; + return [start, end]; + } + + if (o.selectionStart && o.selectionEnd) + return [o.selectionStart, o.selectionEnd]; + } + catch(e) {}; + + return undefined; + }, + + setSelection: function(o, sel) + { + if (sel) + { + if (o.setSelectionRange) + try { o.setSelectionRange(sel[0], sel[1]) } catch(e) {}; + } + }, + + setFocus: function(id) + { + var elt = $(id + ':entry') || $(id); + + if (elt) + { + elt.focus(); + gw.active = document.activeElement.id; + gw.selection = undefined; + gw.focus = true; + } + }, + + resizeComboBox: function(id) + { + $(id + '-select').onmouseover = function() { $(id + '-select').style.width = $(id).offsetWidth + 'px'; } + }, + + highlightMandatory: function(id) + { + var elt = $(id); + var elt_br; + var div; + var div_br; + + if (elt == undefined || elt.gw_mandatory) + return; + + elt.gw_mandatory = div = document.createElement('div'); + div.className = 'gw-mandatory'; + elt.parentNode.insertBefore(div, elt); + + elt_br = gw.getPos(elt); + div_br = gw.getPos(div); + + div.style.top = (elt_br.top - div_br.top) + 'px'; + div.style.left = (elt_br.left - div_br.left) + 'px'; + div.style.width = elt_br.width + 'px'; + div.style.height = elt_br.height + 'px'; + }, + + addTimer: function(id, delay) + { + gw.removeTimer(id); + gw.timers[id] = setInterval(function() { gw.raise(id, 'timer', [], true); }, delay); + }, + + removeTimer: function(id) + { + var t = gw.timers[id]; + if (t) + { + clearInterval(gw.timers[id]); + gw.timers[id] = undefined; + } + }, + + getTargetId: function(elt) + { + for(;;) + { + if (elt.id) + return elt.id; + elt = elt.parentNode; + if (!elt) + return; + } + }, + + getPos: function(elt) + { + var found, left = 0, top = 0, width = 0, height = 0; + var offsetBase = gw.offsetBase; + + if (!offsetBase && document.body) + { + offsetBase = gw.offsetBase = document.createElement('div'); + offsetBase.style.cssText = 'position:absolute;left:0;top:0'; + document.body.appendChild(offsetBase); + } + + if (elt && elt.ownerDocument === document && 'getBoundingClientRect' in elt && offsetBase) + { + var boundingRect = elt.getBoundingClientRect(); + var baseRect = offsetBase.getBoundingClientRect(); + found = true; + left = boundingRect.left - baseRect.left; + top = boundingRect.top - baseRect.top; + width = boundingRect.right - boundingRect.left; + height = boundingRect.bottom - boundingRect.top; + } + + return { found: found, left: left, top: top, width: width, height: height, right: left + width, bottom: top + height }; + }, + + /*ensureVisible: function(id, x, y, w, h) + { + var elt = typeof(id) == 'string' ? $(id) : id; + var pw, ph,cx, cy, cw, ch; + var xx, yy, ww, hh; + + // WW = W / 2 + ww = w / 2; + //HH = H / 2 + hh = h / 2; + // XX = X + WW + xx = x + ww + // YY = Y + HH + yy = y + hh; + + // PW = Me.ClientW + // PH = Me.ClientH + pw = elt.clientWidth; + ph = elt.clientHeight; + + cx = - elt.scrollLeft; + cy = - elt.scrollTop; + cw = elt.scrollWidth; + ch = elt.scrollHeight; + + //If PW < (WW * 2) Then WW = PW / 2 + //If PH < (HH * 2) Then HH = PH / 2 + if (pw < (ww * 2)) ww = pw / 2; + if (ph < (hh * 2)) hh = ph / 2; + + //If CW <= PW Then + // WW = 0 + // CX = 0 + //Endif + if (cw <= pw) { ww = 0; cx = 0; } + + //If CH <= PH Then + // HH = 0 + // CY = 0 + //Endif + if (ch <= ph) { hh = 0; cy = 0 } + + //If XX < (- CX + WW) Then + // CX = Ceil(- XX + WW) + //Else If XX >= (- CX + PW - WW) Then + // CX = Floor(- XX + PW - WW) + //Endif + if (xx < (- cx + ww)) + cx = - xx + ww; + else if (xx >= (- cx + pw - ww)) + cx = - xx + pw - ww; + + //If YY < (- CY + HH) Then + // CY = Ceil(- YY + HH) + //Else If YY >= (- CY + PH - HH) Then + // CY = Floor(- YY + PH - HH) + //Endif + + if (yy < (- cy + hh)) + cy = - yy + hh; + else if (yy >= (- cy + ph - hh)) + cy = - yy + ph - hh; + + //If CX > 0 + // CX = 0 + //Else If CX < (PW - CW) And If CW > PW Then + // CX = PW - CW + //Endif + if (cx > 0) + cx = 0; + else if (cx < (pw - cw) && cw > pw) + cx = pw - cw; + + //If CY > 0 Then + // CY = 0 + //Else If CY < (PH - CH) And If CH > PH Then + // CY = PH - CH + //Endif + if (cy > 0) + cy = 0; + else if (cy < (ph - ch) && ch > ph) + cy = ph - ch; + + //If $hHBar.Value = - CX And If $hVBar.Value = - CY Then Return True + //Scroll(- CX, - CY) + elt.scrollLeft = - cx; + elt.scrollTop = - cy; + },*/ + + window: + { + zIndex: 0, + + open: function(id, resizable, modal, minw, minh) + { + gw.window.close(id); + + if (gw.windows.length == 0) + { + document.addEventListener('mousemove', gw.window.onMove); + document.addEventListener('mouseup', gw.window.onUp); + gw.log('document.addEventListener'); + } + + gw.windows.push(id); + + $(id).addEventListener('mousedown', gw.window.onMouseDown); + + $(id).gw_resizable = resizable; + $(id).gw_modal = modal; + + if (modal) + $(id).gw_focus = gw.saveFocus(); + + if (minw != undefined) + { + $(id).gw_minw = minw; + $(id).gw_minh = minh; + } + else + { + $(id).gw_minw = $(id).offsetWidth; + $(id).gw_minh = $(id).offsetHeight; + } + + //console.log('gw.window.open: minw = ' + $(id).gw_minw + ' minh = ' + $(id).gw_minh); + + // Touch events + //pane.addEventListener('touchstart', onTouchDown); + //document.addEventListener('touchmove', onTouchMove); + //document.addEventListener('touchend', onTouchEnd); + + gw.window.refresh(); + }, + + popup: function(id, resizable, control, alignment, minw, minh) + { + var pos; + + gw.window.close(id); + + if (gw.windows.length == 0) + { + document.addEventListener('mousemove', gw.window.onMove); + document.addEventListener('mouseup', gw.window.onUp); + gw.log('document.addEventListener'); + } + + gw.windows.push(id); + + $(id).addEventListener('mousedown', gw.window.onMouseDown); + + $(id).gw_resizable = resizable; + $(id).gw_modal = true; + $(id).gw_popup = true; + $(id).gw_focus = gw.saveFocus(); + + if (minw != undefined) + { + $(id).gw_minw = minw; + $(id).gw_minh = minh; + } + else + { + $(id).gw_minw = $(id).offsetWidth; + $(id).gw_minh = $(id).offsetHeight; + } + + pos = gw.getPos($(control)); + //console.log(pos); + + /*$(id).style.left = pos.left + 'px'; + $(id).style.top = pos.bottom + 'px';*/ + $(id).style.transform = 'translate(' + pos.left + 'px,' + pos.bottom + 'px)'; + + gw.window.refresh(); + }, + + close: function(id) + { + var i; + + $(id).removeEventListener('mousedown', gw.window.onMouseDown); + + i = gw.windows.indexOf(id); + if (i >= 0) + { + gw.windows.splice(i, 1); + gw.window.refresh(); + } + + if ($(id).gw_focus) + { + gw.restoreFocus($(id).gw_focus); + $(id).gw_focus = undefined; + } + }, + + refresh: function() + { + var i = 0; + var zi; + + while (i < gw.windows.length) + { + if ($(gw.windows[i])) + { + zi = 11 + i * 2; + if ($(gw.windows[i]).style.zIndex != zi) + $(gw.windows[i]).style.zIndex = zi; + i++; + } + else + gw.windows.splice(i, 1); + } + + gw.window.updateModal(); + + if (gw.windows.length == 0) + { + gw.log('document.removeEventListener'); + document.removeEventListener('mousemove', gw.window.onMove); + document.removeEventListener('mouseup', gw.window.onUp); + } + else + gw.window.updateTitleBars(); + }, + + updateTitleBars: function() + { + var i, win, last; + + for (i = 0; i < gw.windows.length - 1; i++) + { + win = gw.windows[i]; + if ($(win).gw_popup) + continue; + $(win).addClass('gw-deactivated'); + $(win + '-titlebar').addClass('gw-deactivated'); + last = win; + } + + if (last && !$(last).gw_popup) + { + $(last).removeClass('gw-deactivated'); + $(last + '-titlebar').removeClass('gw-deactivated'); + } + }, + + raise: function(id, send) + { + var i = gw.windows.indexOf(id); + if (i < 0) + return; + + gw.windows.splice(i, 1); + gw.windows.push(id); + + for (i = 0; i < gw.windows.length; i++) + $(gw.windows[i]).style.zIndex = 11 + i * 2; + + gw.window.updateTitleBars(); + + if (send) + gw.update('', '#windows', gw.windows); + }, + + updateModal: function() + { + var i, elt = $('gw-modal'); + + for (i = gw.windows.length - 1; i >= 0; i--) + { + if ($(gw.windows[i]).gw_modal) + { + gw.window.zIndex = 10 + i * 2; + elt.style.zIndex = 10 + i * 2; + elt.style.display = 'block'; + /*if ($(gw.windows[i]).gw_popup) + elt.style.opacity = '0'; + else + elt.style.opacity = '';*/ + return; + } + } + + gw.window.zIndex = 0; + elt.style.display = 'none'; + }, + + center: function(id) + { + $(id).style.transform = 'translate(' + ((window.innerWidth - $(id).offsetWidth) / 2 | 0) + 'px,' + ((window.innerHeight - $(id).offsetHeight) / 2 | 0) + 'px)'; + gw.window.updateGeometry(id); + }, + + maximize: function(id) + { + var geom = $(id).gw_save_geometry; + if (geom != undefined) + { + //$(id).style.left = geom[0]; + //$(id).style.top = geom[1]; + $(id).style.transform = geom[0] + $(id).style.width = geom[1]; + $(id).style.height = geom[2]; + $(id).gw_save_geometry = undefined; + } + else + { + $(id).gw_save_geometry = [$(id).style.transform, $(id).style.width, $(id).style.height]; + $(id).style.transform = ''; + $(id).style.width = '100%'; + $(id).style.height = '100%'; + } + //gw.window.updateGeometry(id); + }, + + onMouseDown: function(e) + { + gw.window.onDown(e); + }, + + onDown: function(e) + { + var c, win; + + gw.window.context = undefined; + + if (e.target.className == 'gw-window-button') + return; + + gw.window.onMove(e); + + c = gw.window.context; + if (c == undefined) + return; + + if ($(c.id).gw_save_geometry) + return; + + if (c.isMoving || c.isResizing) + { + gw.window.raise(c.id); + gw.window.downEvent = e; + e.preventDefault(); + } + }, + + onDownModal: function() + { + var win = gw.windows[gw.windows.length - 1]; + + if ($(win).gw_popup) + gw.update(win, '#close'); + }, + + onMove: function(e) + { + var i, id, elt, b, x, y, bx, by, bw, bh, th; + var onTopEdge, onLeftEdge, onRightEdge, onBottomEdge, isResizing; + var MARGINS = 6; + + if (gw.window.downEvent) + { + gw.window.context.cx = e.clientX; + gw.window.context.cy = e.clientY; + gw.window.animate(); + return; + } + + gw.window.context = undefined; + + for (i = 0; i < gw.windows.length; i++) + { + id = gw.windows[gw.windows.length - i - 1]; + elt = $(id); + + if (elt.style.zIndex < gw.window.zIndex) + continue; + + b = elt.getBoundingClientRect(); + + bx = b.left; // - MARGINS; + by = b.top; // - MARGINS; + bw = b.width; // + MARGINS * 2; + bh = b.height; // + MARGINS * 2; + + x = e.clientX - bx; + y = e.clientY - by; + + //console.log(x + ',' + y + ' : ' + bx + ',' + by + ',' + bw + ',' + bh); + + if (x >= 0 && x < bw && y >= 0 && y < bh) + { + if (elt.gw_resizable) + { + onTopEdge = y < MARGINS; + onLeftEdge = x < MARGINS; + onRightEdge = x >= (bw - MARGINS); + onBottomEdge = y >= (bh - MARGINS); + + isResizing = onTopEdge || onLeftEdge || onRightEdge || onBottomEdge; + } + else + onTopEdge = onLeftEdge = onRightEdge = onBottomEdge = isResizing = false; + + if ($(id).gw_popup) + th = 0; + else + th = $(id + '-titlebar').offsetHeight; + isMoving = !isResizing && y < (th + MARGINS); + + gw.window.context = { + id: id, + x: b.left + window.scrollX, + y: b.top + window.scrollY, + cx: e.clientX, + cy: e.clientY, + w: b.width, + h: b.height, + isResizing: isResizing, + isMoving: isMoving, + onTopEdge: onTopEdge, + onLeftEdge: onLeftEdge, + onRightEdge: onRightEdge, + onBottomEdge: onBottomEdge + }; + gw.window.animate(); + break; + } + } + }, + + updateGeometry: function(id) + { + var b = $(id).getBoundingClientRect(); + gw.update(id, '#geometry', [ b.left + 'px', b.top + 'px', b.width + 'px', b.height + 'px']); + }, + + onUp: function(e) + { + var c = gw.window.context; + + gw.window.downEvent = undefined; + + if (c && (c.isMoving || c.isResizing)) + { + var id = gw.window.context.id; + gw.window.context = undefined; + gw.window.raise(id, true); + gw.window.updateGeometry(id); + } + }, + + animate: function() + { + var id, elt, c, e, x, y, w, h; + var minWidth; + var minHeight; + + //requestAnimationFrame(gw.window.animate); + + c = gw.window.context; + if (!c) return; + + elt = $(c.id); + minWidth = elt.gw_minw; + minHeight = elt.gw_minh; //$(c.id + '-titlebar').offsetHeight + 2 + elt.gw_minh; + e = gw.window.downEvent; + + if (c && c.isResizing && e) + { + if (c.onRightEdge) + elt.style.width = Math.max(c.w + c.cx - e.clientX, minWidth) + 'px'; + + if (c.onBottomEdge) + elt.style.height = Math.max(c.h + c.cy - e.clientY, minHeight) + 'px'; + + x = c.x; + y = c.y; + + if (c.onLeftEdge) + { + x = c.x + c.cx - e.clientX; + w = c.x + c.w - x; + if (w >= minWidth) + { + elt.style.width = w + 'px'; + //elt.style.left = x + 'px'; + elt.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + //c.x = x; + } + } + + if (c.onTopEdge) + { + y = c.y + c.cy - e.clientY; + h = c.y + c.h - y; + if (h >= minHeight) + { + elt.style.height = h + 'px'; + //elt.style.top = y + 'px'; + elt.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + } + } + + return; + } + + if (c && c.isMoving && e) + { + /*elt.style.left = (Math.max(0, c.x + c.cx - e.clientX)) + 'px'; + elt.style.top = (Math.max(0, c.y + c.cy - e.clientY)) + 'px';*/ + elt.style.transform = 'translate(' + (Math.max(0, c.x + c.cx - e.clientX)) + 'px,' + (Math.max(0, c.y + c.cy - e.clientY)) + 'px)'; + return; + } + + // This code executes when mouse moves without clicking + + if (c.onRightEdge && c.onBottomEdge || c.onLeftEdge && c.onTopEdge) + elt.style.cursor = 'nwse-resize'; + else if (c.onRightEdge && c.onTopEdge || c.onBottomEdge && c.onLeftEdge) + elt.style.cursor = 'nesw-resize'; + else if (c.onRightEdge || c.onLeftEdge) + elt.style.cursor = 'ew-resize'; + else if (c.onBottomEdge || c.onTopEdge) + elt.style.cursor = 'ns-resize'; + else + elt.style.cursor = ''; + } + }, + + menu: + { + hide: function(elt) + { + elt.style.display = 'none'; + setTimeout(function() { elt.style.display = ''; }, 150); + }, + + click: function(name, event) + { + var id = gw.getTargetId(event.target); + gw.update(name, '#click', id); + event.stopPropagation(); + } + }, + + table: + { + select: function(id, row) + { + var elt = $(id); + var tr = $(id + ':' + row); + var current = elt.gw_current; + + if (tr.tagName == 'TR') + { + if (current !== undefined) + { + if (current >= 0) + $(id + ':' + current) && $(id + ':' + current).removeClass('gw-table-row-selected'); + tr.addClass('gw-table-row-selected'); + elt.gw_current = row; + } + else + { + if (tr.hasClass('gw-table-row-selected')) + tr.removeClass('gw-table-row-selected'); + else + tr.addClass('gw-table-row-selected'); + } + } + + gw.update(id, '$' + row, null); + }, + + check: function(id, row) + { + var elt = $(id + ':' + row); + if (event.target.tagName == 'TD') + elt.checked = !elt.checked; + gw.update(id, '!' + row, elt.checked); + event.stopPropagation(); + }, + + toggle: function(id, row) + { + gw.update(id, '?' + row, false); + }, + + onScroll: function(id, more, timeout) + { + var elt = $(id); + var sw = elt.firstChild; + var last = elt.gw_last_scroll; + + if (last && last[0] == sw.scrollLeft && last[1] == sw.scrollTop) + return; + + elt.gw_last_scroll = [sw.scrollLeft, sw.scrollTop]; + + //console.log('onScroll: ' + id + ' ' + sw.scrollLeft + ',' + sw.scrollTop); + + if (more) + { + //if ((sw.scrollHeight - sw.scrollTop) === (sw.clientHeight)) + if (sw.scrollTop >= (sw.scrollHeight - sw.clientHeight - 16)) + { + /*var wait = document.createElement('div'); + wait.className = 'gw-waiting'; + elt.appendChild(wait);*/ + if (elt.gw_scroll) + { + clearTimeout(elt.gw_scroll); + elt.gw_scroll = undefined; + } + + gw.update(id, '#more', [sw.scrollLeft, sw.scrollTop]); + return; + } + } + + if (elt.gw_headerh) + $(elt.gw_headerh).firstChild.scrollLeft = sw.scrollLeft; + + if (elt.gw_headerv) + $(elt.gw_headerv).firstChild.scrollTop = sw.scrollTop; + + if (elt.gw_noscroll) + { + elt.gw_noscroll = undefined; + return; + } + + if (elt.gw_scroll) + clearTimeout(elt.gw_scroll); + + elt.gw_scroll = setTimeout(function() + { + //console.log("gw.table.onScroll: " + id + ": " + sw.scrollLeft + " " + sw.scrollTop); + var pos = [sw.scrollLeft, sw.scrollTop]; + + clearTimeout(elt.gw_scroll); + + gw.update(elt.id, '#scroll', pos, function() + { + elt.gw_scroll = undefined; + if (pos[0] != sw.scrollLeft || pos[1] != sw.scrollTop) + gw.table.onScroll(id, more, timeout); + }); + + //elt.gw_scroll = undefined; + }, timeout || 250); + }, + + scroll: function(id, x, y) + { + var sw = $(id).firstChild + + //console.log("gw.table.scroll: " + id + ": " + x + " " + y); + + if (x != sw.scrollLeft) + { + $(id).gw_noscroll = true; + sw.scrollLeft = x; + } + if (y != sw.scrollTop) + { + $(id).gw_noscroll = true; + sw.scrollTop = y; + } + if (x != sw.scrollLeft || y != sw.scrollTop) + gw.update(id, '#scroll', [sw.scrollLeft, sw.scrollTop]); + }, + + ensureVisible: function(id, row) + { + var sw = $(id).firstChild; + gw.table.scroll(id, sw.scrollLeft, $(id + ':' + row).offsetTop - sw.clientHeight / 2); + } + }, + + scrollview: + { + setHeaders: function(id, hid, vid) + { + $(id).gw_headerh = hid; + $(id).gw_headerv = vid; + } + }, + + file: + { + select: function(id) + { + var elt = $(id + ':file'); + + if ($(id).gw_uploading) + return; + + elt.focus(); + elt.click(); + }, + + finish: function(xhr) + { + if (xhr.gw_progress) + { + setTimeout(function() { gw.file.finish(xhr); }, 250); + return; + } + + gw.update(xhr.gw_id, '#progress', 1, function() { + gw.answer(xhr); + gw.uploads[xhr.gw_id] = undefined; + gw.raise(xhr.gw_id, 'upload', [], true); + xhr.gw_id = undefined; + }); + }, + + upload: function(id) + { + var elt = $(id + ':file'); + var file = elt.files[0]; + var xhr = new XMLHttpRequest(); + var form = new FormData(); + + if (gw.uploads[id]) + return; + + gw.uploads[id] = xhr; + + //gw.log('gw.file.upload: ' + id + ': ' + file.name); + + xhr.gw_progress = 0; + + xhr.gw_progress++; + gw.update(id, '#progress', 0, function() { xhr.gw_progress--; }); + + form.append('file', file); + form.append('name', file.name); + form.append('id', id); + + //xhr.upload.addEventListener("loadstart", loadStartFunction, false); + //xhr.upload.addEventListener("load", transferCompleteFunction, false); + + xhr.upload.addEventListener("progress", + function(e) + { + //console.log('upload: progress ' + e.loaded + ' / ' + e.total); + + if (xhr.gw_id == undefined) + return; + + if (e.lengthComputable) + { + var t = (new Date()).getTime(); + + if ((xhr.gw_time == undefined || (t - xhr.gw_time) > 250) && xhr.gw_progress == 0) + { + xhr.gw_progress++; + gw.update(xhr.gw_id, '#progress', e.loaded / e.total, function() { xhr.gw_progress--; }); + xhr.gw_time = t; + } + } + }, + false); + + xhr.gw_command = ['upload', id]; + xhr.gw_id = id; + + xhr.open("POST", $root + '/u', true); + + xhr.onreadystatechange = function() + { + if (xhr.readyState == 4) + gw.file.finish(xhr); + }; + + xhr.send(form); + }, + + abort: function(id) + { + if (gw.uploads[id]) + gw.uploads[id].abort(); + } + }, + + autocomplete: function(id) + { + new AutoComplete({ + selector: $(id + ':entry'), + cache: false, + source: function(term, response) { + var xhr = $(id).gw_xhr; + if (xhr) + { + try { xhr.abort(); } catch(e) {} + } + + $(id).gw_xhr = xhr = new XMLHttpRequest(); + + xhr.open('GET', $root + '/x?c=' + encodeURIComponent(JSON.stringify(['raise', id, 'completion', [term]])), true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) + { + gw.autocompletion = []; + gw.answer(xhr); + response(gw.autocompletion); + } + }; + xhr.send(); + }, + onSelect: function(e, term, item) { + gw.textbox.setText(id, gw.textbox.getText(id)); + } + }); + }, + + textbox: + { + onactivate: function(id, e) + { + gw.log('textbox.onactivate'); + if (e.keyCode == 13) + setTimeout(function() { gw.raise(id, 'activate', [], false); }, 50); + }, + + getText: function(id) + { + return $(id + ':entry').value; + }, + + setText: function(id, text) + { + gw.command(function() { + $(id + ':entry').value = text; + gw.setSelection($(id + ':entry'), [text.length, text.length]); + gw.update(id, 'text', text); + }); + }, + + clear: function(id) + { + gw.textbox.setText(id, ''); + gw.setFocus(id); + gw.raise(id, 'activate', [], false); + } + } +} + diff --git a/comp/src/gb.web.form/.hidden/Uncompressed/style.css b/comp/src/gb.web.form/.hidden/Uncompressed/style.css new file mode 100644 index 00000000..0e8fbb86 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/Uncompressed/style.css @@ -0,0 +1,732 @@ +HTML, BODY { + margin: 0; + padding: 0; + height: 100%; + width: 100%; +} + +HTML, BODY, DIV, INPUT { + box-sizing: border-box; +} + +SELECT { + min-height: 2em; + font-size: inherit; +} + +INPUT { + font: inherit; +} + +H1,H2,H3,P { + margin-top: 0.5rem; + margin-bottom: 0.25rem; +} + +/*UL, OL { + padding-left: 2em; + margin-bottom: 0; +} + +P:first-child,UL:first-child,OL:first-child { + margin-top: 0; +}*/ + +.gw-button { + padding: 0; + font: inherit; + padding: 0.125em 0.25em; + min-height: 2em; +} + +.gw-button.gw-noborder { + border: solid 1px transparent; + background: none; + padding: 0; +} + +.gw-button.gw-noborder:hover { + border: solid 1px #C0C0C0; +} + +.gw-button-image { + margin-right: 0.5em; +} + +.gw-button > DIV { + vertical-align: middle; + position: relative; + display: inline-flex; + justify-content: center; + align-items: center; + flex-flow: row; +} + +.gw-button > DIV > SPAN { + display: inline-block; +} + +.gw-tab-header { + margin-bottom: -1px; + z-index: 1; + line-height: 2em; +} + +.gw-tab-header.gw-noborder { + margin-bottom: 0; +} + +.gw-tab { + color: gray; + padding: 0 0.5em; + cursor: pointer; + border-top: solid transparent 1px; + border-right: solid transparent 1px; + border-left: solid transparent 1px; +} + +.gw-tab:hover { + color: black; +} + +.gw-tab-selected { + padding: 0 0.5em; + border-left: solid #C0C0C0 1px; + border-top: solid #C0C0C0 1px; + border-right: solid #C0C0C0 1px; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + background: white; +} + +.gw-tab-header.gw-noborder > div { + border: none; +} + +.gw-tab-contents { + border: solid #C0C0C0 1px; + flex-grow: 1; + display: flex; + flex-flow: column; +} + +.gw-tab-contents > DIV { + flex-grow: 1; +} + +.gw-tab-contents.gw-noborder { + border: none; +} + +.gw-checkbox,.gw-radiobutton { + display: flex; + flex-flow: row; + min-height: 2em; +} + +.gw-checkbox-label,.gw-radiobutton-label { + display: flex; + flex-flow: row; + align-items: center; +} + +.gw-checkbox.gw-disabled > .gw-checkbox-label { + opacity: 0.5; +} + +.gw-radiobutton.gw-disabled > .gw-checkbox-label { + opacity: 0.5; +} + +.gw-checkbox-toggle,.gw-radiobutton-toggle { + margin: 0; + margin-right: 0.5em; +} + +.gw-textbox { + min-height: 2em; + /*box-sizing: border-box; firefox bug on refresh !*/ + padding: 0 0.25em; +} + +.gw-textbox.gw-noborder { + border: none; +} + +.gw-combobox { + display: inline-block; + position: relative; + min-height: 2em; +} + +.gw-combobox SELECT { + height: 100%; + z-index: 1; + opacity: 0; + float: right; +} + +.gw-combobox INPUT { + width: 100%; + min-height: 2em; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-combobox.gw-noborder INPUT { + border: none; +} + +.gw-combobox-arrow { + display: inline-block; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAI0lEQVQYlWNgoAZoYGBg+I8DN+BT1MCABhrwSSIrwilJHgAAbCcP9dwIQGIAAAAASUVORK5CYII='); + background-position: center; + background-repeat: no-repeat; + height: 2rem; + width: 2rem; + margin-left: -2rem; + /*pointer-events: none;*/ + vertical-align: middle; + border: none; + overflow: hidden; +} + +.gw-selectbox { + display: inline-block; + position: relative; + min-height: 2em; +} + +.gw-selectbox SELECT { + width: 100%; + height: 100%; +} + +.gw-selectbox.gw-noborder SELECT { + border: none; +} + +.gw-window-container { +} + +.gw-window { + position: fixed; + top: 0; + left: 0; + border: solid 1px white; + box-shadow: 0 0 0.5em black; + border-radius: 0.5em; + background: white; + z-index: 10; + overflow: hidden; +} + +.gw-popup { + border: solid 1px #C0C0C0; + border-radius: 0; + box-shadow: none; +} + +.gw-window-titlebar { + display: flex; + flex-flow: row; + background: #2980B9; + font-weight: bold; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + padding: 0.25em; +} + +.gw-window-titlebar.gw-deactivated { + background: white; +} + +.gw-window-title { + flex-grow: 1; + color: white; + padding: 0 1em; + pointer-events: none; +} + +.gw-window-titlebar.gw-deactivated > DIV.gw-window-title { + color: gray; +} + +.gw-window-button { + width: 1.5em; + height: 1.5em; + padding: 0; + display: inline-flex; + justify-content: center; +} + +.gw-window-button > IMG { + width: 0.8em; + height: 0.8em; + margin: auto; +} + +/*@-moz-document url-prefix() { + .gw-window-button { + display: inline-flex; + }; +}*/ + +.gw-window-contents { + display: flex; + flex-flow: column; + flex-grow: 1; + padding: 0.5em; +} + +.gw-popup > .gw-window-contents { + padding: 0; +} + + +.gw-label { + display: flex; + align-items: center; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.gw-label.gw-disabled { + opacity: 0.5; +} + +#gw-modal { + display: none; + position: fixed; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: black; + opacity: 0.05; +} + +.gw-textarea { + font: inherit; + resize: none; +} + +.gw-textarea.gw-noborder { + border: none; +} + + +.gw-menu-title { + display: inline-table; + padding: 0.5em; + padding-top: 0.25em; + cursor: default; + border-top: solid 1px transparent; + border-left: solid 1px transparent; + border-right: solid 1px transparent; + /*margin-top: 0.25em; + margin-bottom: 0.25em;*/ +} + +.gw-menu:hover > .gw-menu-title { + border-top: solid 1px #C0C0C0; + border-left: solid 1px #C0C0C0; + border-right: solid 1px #C0C0C0; + box-shadow: 0 0 0.25em #C0C0C0; + background: white; + z-index: 1002; +} + +.gw-submenu { + display: none; + position: absolute; + border: solid 1px #C0C0C0; + background: white; + box-shadow: 0 0.125em 0.25em #C0C0C0; + margin-top: -0.25em; + z-index: 1003; +} + +.gw-menuitem > .gw-menu > .gw-submenu { + margin-top: 0; + top: 0; + left: 100%; +} + +.gw-menu:hover > .gw-submenu { + display: table; +} + +.gw-menu-tape { + display: none; + position: relative; + background: white; + z-index: 1004; + height: 4px; + margin-top: -4px; + margin-left: 1px; + margin-right: 1px; + top: -2px; +} + +.gw-menu:hover > .gw-menu-tape { + display: block; +} + +.gw-submenu > div:hover { + background: #E0E0E0; +} + +.gw-menuitem { + display: flex; + flex-flow: column; +} + +.gw-menuitem > div { + display: table-cell !important; + vertical-align: middle; +} + +.gw-menuitem-text { + flex-grow: 1; + padding: 0.125em 0.25em; + white-space: nowrap; +} + +.gw-menuitem-icon { + padding: 0.125em 0.25em; +} + +.gw-menuitem-icon > IMG { + vertical-align: middle; +} + +.gw-menuitem-shortcut { + text-align: right; + padding: 0.125em 1em; +} + +.gw-separator { + position: relative; + padding: 0 !important; + pointer-events: none; +} + +.gw-separator-hline { + position: absolute; + display: flex; + flex-flow: row; + align-items: center; + height: inherit; + width: 100%; +} + +.gw-separator-hline > div { + height: 1px; + width: 100%; + background: #C0C0C0; +} + +.gw-separator-vline { + position: absolute; + display: flex; + flex-flow: column; + align-items: center; + height: 100%; + width: inherit; +} + +.gw-separator-vline > div { + width: 1px; + height: 100%; + background: #C0C0C0; +} + +.gw-submenu > div { + display: flex !important; + cursor: default; + padding: 0.25em 1em; + margin: 0; +} + +.gw-submenu > .gw-menuitem { + display: table-row !important; + table-layout: fixed; + cursor: default; + padding: 0; + margin: 0; +} + +.gw-submenu > .gw-separator { + display: table-row !important; + cursor: default; + margin: 0; + width: auto; + height: 1em; +} + +.gw-menuitem > .gw-menu { + display: table-cell !important; + position: relative; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAJElEQVQYlWNgQIAGBgLgPyFF/wkp+o9NERMhe0m2AqckAy5JALuADHot/KmNAAAAAElFTkSuQmCC'); + background-position: center; + background-repeat: no-repeat; + width: 1em; +} + +.gw-spinbox { + min-height: 2em; + padding: 0 0.25em; +} + +.gw-spinbox.gw-noborder { + border: none; +} + +.gw-expander-header + DIV { + margin-top: 0.5em; +} + +.gw-expander-header > IMG { + vertical-align: middle; +} + +.gw-expander-header > DIV { + display: inline-table; + vertical-align: middle; +} + +.gw-expander-border { + border: solid 1px #C0C0C0; + padding: 0.5em; +} + +.gw-table { + position: relative; + min-height: 4em; + border: solid 1px #C0C0C0; + cursor: default; +} + +.gw-table.gw-noborder { + border: none; +} + +/*.gw-table-header { + height: 1.5em; + background-color: yellow; +}*/ + +.gw-table-contents { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; +} + +.gw-table > DIV > TABLE { + border-collapse: collapse; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH { + text-align: left; + background-color: #E0E0E0; + padding: 0.25em 0.5em; + vertical-align: top; + border-bottom: solid 1px #C0C0C0; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR > TD { + /*border-right: solid 1px #C0C0C0;*/ + padding: 0.1em 0.5em; + vertical-align: top; +} + +/*.gw-table > DIV > TABLE > TBODY > TR > TD:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR:last-child > TD { + border-bottom: solid 1px #C0C0C0; +}*/ + +.gw-table > DIV > TABLE > TBODY > TR:nth-child(even) { + background-color: #F0F0F0; +} + +.gw-table-more { + position: absolute; + padding-left: 4px; +} + +.gw-waiting { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: url('//gw-waiting.gif'); + background-position: center; + background-repeat: no-repeat; +} + +.gw-scrollview { + position: relative; +} + +.gw-scrollview > DIV { + position: absolute !important; + overflow: auto; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.gw-datebox { + position: relative; + display: inline-block; + min-height: 2em; +} + +.gw-datebox INPUT { + width: 100%; + min-height: 2em; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-datebox.gw-noborder INPUT { + border: none; +} + +TABLE.gw-calendar { + width: 100%; +} + +TABLE.gw-calendar > TBODY > TR > TH { + padding: 0.25em 0.5em; + background: #E0E0E0; +} + +TABLE.gw-calendar > TBODY > TR > TD { + padding: 0.25em 0.5em; + text-align: center; + cursor: pointer; + border: solid 1px transparent; +} + +TABLE.gw-calendar > TBODY > TR > TD:hover { + outline: solid 1px #C0C0C0; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-disabled { + color: #C0C0C0; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-today { + font-weight: bold; + border: solid 1px black; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-current { + background-color: #2980B9; + color: white; +} + +.gw-file-input { + position: absolute; + left: -2000px; +} + +.gw-progressbar { + position: relative; + border: solid 1px #C0C0C0; + min-height: 0.5em; + border-radius: 0.25em; +} + +.gw-progressbar.gw-noborder { + border: solid 1px transparent; +} + +.gw-progressbar-bar { + position: absolute; + height: 100%; + background: #C0C0C0; + border: solid 1px white; + border-radius: 0.25em; + z-index: -1; +} + +.gw-progressbar-label { + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.gw-form { + flex-grow: 1; +} + +.gw-image { + border: solid 1px #C0C0C0; + box-sizing: border-box; +} + +.gw-image.gw-noborder { + border: none; +} + +.gw-table-row-selected { + background-color: #2980B9 !important; + color: white !important; +} + +.gw-ac-suggestions { + text-align: left; + cursor: default; + border: 1px solid #C0C0C0; + border-top: 0; + background: white; + /*box-shadow: -1px 1px 3px rgba(0,0,0,.1);*/ + + /* core styles should not be changed */ + position: absolute; + display: none; + z-index: 9999; + max-height: 12em; + overflow: hidden; + overflow-y: auto; + box-sizing: border-box; +} + +.gw-ac-suggestion { + position: relative; + padding: 0 0.5em; + line-height: 1.5em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.gw-ac-suggestion b { + font-weight: normal; + color: #2980B9; +} + +.gw-ac-suggestion.selected { + background: #E0E0E0; +} diff --git a/comp/src/gb.web.form/.hidden/calendar.js b/comp/src/gb.web.form/.hidden/calendar.js new file mode 100644 index 00000000..e07da523 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/calendar.js @@ -0,0 +1,1577 @@ + +function getAbsoluteParent(obj) +{ + for(;;) + { + obj = obj.offsetParent; + if (obj == null) + break; + if (obj.style.position == 'absolute') + break; + } + + return obj; +} + +function __getIEVersion() { + var rv = -1; // Return value assumes failure. + if (navigator.appName == 'Microsoft Internet Explorer') { + var ua = navigator.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat(RegExp.$1); + } + return rv; +} + +function __getOperaVersion() { + var rv = 0; // Default value + if (window.opera) { + var sver = window.opera.version(); + rv = parseFloat(sver); + } + return rv; +} + +var __userAgent = navigator.userAgent; +var __isIE = navigator.appVersion.match(/MSIE/) != null; +var __IEVersion = __getIEVersion(); +var __isIENew = __isIE && __IEVersion >= 8; +var __isIEOld = __isIE && !__isIENew; + +var __isFireFox = __userAgent.match(/firefox/i) != null; +var __isFireFoxOld = __isFireFox && ((__userAgent.match(/firefox\/2./i) != null) || + (__userAgent.match(/firefox\/1./i) != null)); +var __isFireFoxNew = __isFireFox && !__isFireFoxOld; + +var __isWebKit = navigator.appVersion.match(/WebKit/) != null; +var __isChrome = window.chrome != null; +var __isOpera = window.opera != null; +var __operaVersion = __getOperaVersion(); +var __isOperaOld = __isOpera && (__operaVersion < 10); + +function __parseBorderWidth(width) { + var res = 0; + if (typeof(width) == "string" && width != null && width != "" ) { + var p = width.indexOf("px"); + if (p >= 0) { + res = parseInt(width.substring(0, p)); + } + else { + //do not know how to calculate other values + //(such as 0.5em or 0.1cm) correctly now + //so just set the width to 1 pixel + res = 1; + } + } + return res; +} + +//returns border width for some element +function __getBorderWidth(element) { + var res = new Object(); + res.left = 0; res.top = 0; res.right = 0; res.bottom = 0; + if (window.getComputedStyle) { + //for Firefox + var elStyle = window.getComputedStyle(element, null); + res.left = parseInt(elStyle.borderLeftWidth.slice(0, -2)); + res.top = parseInt(elStyle.borderTopWidth.slice(0, -2)); + res.right = parseInt(elStyle.borderRightWidth.slice(0, -2)); + res.bottom = parseInt(elStyle.borderBottomWidth.slice(0, -2)); + } + else { + //for other browsers + res.left = __parseBorderWidth(element.style.borderLeftWidth); + res.top = __parseBorderWidth(element.style.borderTopWidth); + res.right = __parseBorderWidth(element.style.borderRightWidth); + res.bottom = __parseBorderWidth(element.style.borderBottomWidth); + } + + return res; +} + +//returns the absolute position of some element within document +function getElementAbsolutePos(element) { + var res = new Object(); + res.x = 0; res.y = 0; + + if (element !== null) { + if (element.getBoundingClientRect) { + var viewportElement = document.documentElement; + var box = element.getBoundingClientRect(); + var scrollLeft = viewportElement.scrollLeft; + var scrollTop = viewportElement.scrollTop; + + res.x = box.left + scrollLeft; + res.y = box.top + scrollTop; + + } + else { //for old browsers + res.x = element.offsetLeft; + res.y = element.offsetTop; + + var parentNode = element.parentNode; + var borderWidth = null; + var offsetParent = element.offsetParent; + + while (offsetParent != null) { + res.x += offsetParent.offsetLeft; + res.y += offsetParent.offsetTop; + + var parentTagName = + offsetParent.tagName.toLowerCase(); + + if ((__isIEOld && parentTagName != "table") || + ((__isFireFoxNew || __isChrome) && + parentTagName == "td")) { + borderWidth = kGetBorderWidth + (offsetParent); + res.x += borderWidth.left; + res.y += borderWidth.top; + } + + if (offsetParent != document.body && + offsetParent != document.documentElement) { + res.x -= offsetParent.scrollLeft; + res.y -= offsetParent.scrollTop; + } + + + //next lines are necessary to fix the problem + //with offsetParent + if (!__isIE && !__isOperaOld || __isIENew) { + while (offsetParent != parentNode && + parentNode !== null) { + res.x -= parentNode.scrollLeft; + res.y -= parentNode.scrollTop; + if (__isFireFoxOld || __isWebKit) + { + borderWidth = + kGetBorderWidth(parentNode); + res.x += borderWidth.left; + res.y += borderWidth.top; + } + parentNode = parentNode.parentNode; + } + } + + if (offsetParent.style.position == 'absolute') + break; + parentNode = offsetParent.parentNode; + offsetParent = offsetParent.offsetParent; + } + } + } + return res; +} + + +function getLeftPos(obj) +{ + return getElementAbsolutePos(obj).x +} + +function getTopPos(obj) +{ + return getElementAbsolutePos(obj).y +} + +function moveElementUnder(obj, under, dx, dy, right) +{ + var pos, opos, x, y; + var absUnder, absObj; + + pos = getElementAbsolutePos(under); + + obj.style.position = 'absolute'; + obj.style.left = '0'; + obj.style.top = '0'; + + opos = getElementAbsolutePos(obj); + + x = pos.x - opos.x + dx; //- getLeftPos(under.offsetParent); + y = pos.y - opos.y + under.offsetHeight + dy; //- getTopPos(under.offsetParent); + + if (right) + x -= obj.offsetWidth - under.offsetWidth; + + obj.style.left = x + 'px'; + obj.style.top = y + 'px'; +} + +/************************************************************************************************************ +JS Calendar +Copyright (C) September 2006 DTHMLGoodies.com, Alf Magne Kalleland + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Dhtmlgoodies.com., hereby disclaims all copyright interest in this script +written by Alf Magne Kalleland. + +Alf Magne Kalleland, 2006 +Owner of DHTMLgoodies.com + +************************************************************************************************************/ + +var turnOffYearSpan = false; // true = Only show This Year and Next, false = show +/- 5 years +var weekStartsOnSunday = false; // true = Start the week on Sunday, false = start the week on Monday +var showWeekNumber = true; // true = show week number, false = do not show week number + +var calendar_display_time = true; + +// Format of current day at the bottom of the calendar +// [todayString] = the value of todayString +// [dayString] = day of week (example: mon, tue, wed...) +// [UCFdayString] = day of week (example: Mon, Tue, Wed...) ( First letter in uppercase) +// [day] = Day of month, 1..31 +// [monthString] = Name of current month +// [year] = Current year +var todayStringFormat = '[todayString] [UCFdayString]. [day] [monthString] [year]'; +var pathToImages = $root; // Relative to your HTML file + +var speedOfSelectBoxSliding = 50; // Milliseconds between changing year and hour when holding mouse over "-" and "+" - lower value = faster +var intervalSelectBox_minutes = 5; // Minute select box - interval between each option (5 = default) + +var calendar_offsetTop = 3; // Offset - calendar placement - You probably have to modify this value if you're not using a strict doctype +var calendar_offsetLeft = 4; // Offset - calendar placement - You probably have to modify this value if you're not using a strict doctype +var calendarDiv; +var must_submit = false; + +var MSIE = false; +var Opera = false; +//if(navigator.userAgent.indexOf('MSIE')>=0 && navigator.userAgent.indexOf('Opera')<0)MSIE=true; +if(navigator.userAgent.indexOf('Opera')>=0)Opera=true; + +var monthArray = ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre']; +var monthArrayShort = ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc']; +var dayArray = ['Lun','Mar','Mer','Jeu','Ven','Sam','Dim']; +var weekString = 'Sem'; +var todayString = ""; + + +if (weekStartsOnSunday) { + var tempDayName = dayArray[6]; + for(var theIx = 6; theIx > 0; theIx--) { + dayArray[theIx] = dayArray[theIx-1]; + } + dayArray[0] = tempDayName; +} + +var daysInMonthArray = [31,28,31,30,31,30,31,31,30,31,30,31]; +var currentMonth; +var currentYear; +var currentHour; +var currentMinute; +var calendarContentDiv; +var returnDateTo; +var returnFormat; +var activeSelectBoxMonth; +var activeSelectBoxYear; +var activeSelectBoxHour; +var activeSelectBoxMinute; + +var iframeObj = false; +//// fix for EI frame problem on time dropdowns 09/30/2006 +var iframeObj2 =false; + +function EIS_FIX_EI1(where2fixit) +{ + if (!iframeObj2) + return; + iframeObj2.style.display = 'block'; + iframeObj2.style.height = $(where2fixit).offsetHeight+1; + iframeObj2.style.width= $(where2fixit).offsetWidth; + iframeObj2.style.left=getLeftPos($(where2fixit))+1-calendar_offsetLeft; + iframeObj2.style.top=getTopPos($(where2fixit))-$(where2fixit).offsetHeight-calendar_offsetTop; +} + +function EIS_Hide_Frame() +{ + if(iframeObj2) + iframeObj2.style.display = 'none'; +} + +//// fix for EI frame problem on time dropdowns 09/30/2006 +var returnDateToYear; +var returnDateToMonth; +var returnDateToDay; +var returnDateToHour; +var returnDateToMinute; + +var inputYear; +var inputMonth; +var inputDay; +var inputHour; +var inputMinute; +var calendarDisplayTime = false; + +var selectBoxHighlightColor = '#FF0000'; // Highlight color of select boxes +var selectBoxRolloverBgColor = '#F0F0F0'; // Background color on drop down lists(rollover) + +var selectBoxMovementInProgress = false; +var activeSelectBox = false; + +function cancelCalendarEvent() +{ + return false; +} + +function isLeapYear(inputYear) +{ + return inputYear % 400 == 0 || (inputYear % 4 == 0 && inputYear % 100 != 0); +} + +var activeSelectBoxMonth = false; +var activeSelectBoxDirection = false; + +function selectElt(elt, value) +{ + elt.style.fontWeight = value ? 'bold' : ''; +} + +function scrollMonthYear() +{ + activeSelectBox = this; + + if (this.id.indexOf('UpDiv') >= 0 || this.id.indexOf('DownDiv') >= 0) + { + //if (this.className=='monthYearActive') + selectBoxMovementInProgress = true; + //else + // selectBoxMovementInProgress = false; + + if (this.id.indexOf('UpDiv') >=0 ) + activeSelectBoxDirection = -1; + else + activeSelectBoxDirection = 1; + setTimeout(slideCalendarSelectBox, speedOfSelectBoxSliding); + } + else + selectBoxMovementInProgress = false; +} + +function showMonthDropDown() +{ + if($('monthDropDown').style.display=='block'){ + $('monthDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('monthDropDown').style.display='block'; + $('yearDropDown').style.display='none'; + $('hourDropDown').style.display='none'; + $('minuteDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('monthDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + } +} + +function showYearDropDown() +{ + if($('yearDropDown').style.display=='block'){ + $('yearDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('yearDropDown').style.display='block'; + $('monthDropDown').style.display='none'; + $('hourDropDown').style.display='none'; + $('minuteDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('yearDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + + } + +} +function showHourDropDown() +{ + if($('hourDropDown').style.display=='block'){ + $('hourDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('hourDropDown').style.display='block'; + $('monthDropDown').style.display='none'; + $('yearDropDown').style.display='none'; + $('minuteDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('hourDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + } + +} +function showMinuteDropDown() +{ + if($('minuteDropDown').style.display=='block'){ + $('minuteDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('minuteDropDown').style.display='block'; + $('monthDropDown').style.display='none'; + $('yearDropDown').style.display='none'; + $('hourDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('minuteDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + } + +} + +function selectMonth() +{ + var elt; + + $('calendar_month_txt').innerHTML = this.innerHTML + currentMonth = this.id.replace(/[^\d]/g,''); + + $('monthDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + for(var no=0;no=0){ + currentMonth=currentMonth-1;; + if(currentMonth<0){ + currentMonth=11; + currentYear=currentYear-1; + } + }else{ + currentMonth=currentMonth+1;; + if(currentMonth>11){ + currentMonth=0; + currentYear=currentYear/1+1; + } + } + + updateMonthDiv(); + writeCalendarContent(); +} + +function createMonthDiv(){ + var div = document.createElement('DIV'); + div.className='monthYearPicker'; + div.id = 'monthPicker'; + + for(var no=0;no=0){ + var startYear = yearItems[1].innerHTML/1 -1; + if(activeSelectBoxYear) + selectElt(activeSelectBoxYear, false); + }else{ + var startYear = yearItems[1].innerHTML/1 +1; + if(activeSelectBoxYear) + selectElt(activeSelectBoxYear, false); + } + + for(var no=1;no= 0) + { + var startHour = hourItems[1].innerHTML/1 - 1; + if(startHour < 0) startHour = 0; + } + else + { + var startHour = hourItems[1].innerHTML/1 + 1; + if(startHour >14 ) startHour = 14; + } + + if(activeSelectBoxHour) + selectElt(activeSelectBoxHour, false); + + for (var no = 1; no < (hourItems.length - 1); no++) + { + hourItems[no].innerHTML = padleft(startHour+no-1, '00'); + hourItems[no].id = 'hourDiv' + padleft(startHour+no-1, '00'); + } + + if (activeSelectBoxHour) + { + selectElt(activeSelectBoxHour, false); + if ($('hourDiv'+currentHour)) + { + activeSelectBoxHour = $('hourDiv' + currentHour); + selectElt(activeSelectBoxHour, true); + } + } +} + +function updateYearDiv() +{ + var yearSpan = 5; + if (turnOffYearSpan) { + yearSpan = 0; + } + var div = $('yearDropDown'); + var yearItems = div.getElementsByTagName('DIV'); + for(var no=1;no'; + subDiv.onclick = changeSelectBoxYear; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + } else { + startYear = d.getFullYear()/1 - 0; + yearSpan = 2; + } + + for(var no=startYear;no<(startYear+yearSpan);no++){ + var subDiv = document.createElement('DIV'); + subDiv.innerHTML = no; + //subDiv.onmouseover = scrollMonthYear; + //subDiv.onmouseout = scrollMonthYear; + subDiv.onclick = selectYear; + subDiv.id = 'yearDiv' + no; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + if(currentYear && currentYear==no){ + selectElt(subDiv, true); + activeSelectBoxYear = subDiv; + } + } + if (! turnOffYearSpan) { + var subDiv = document.createElement('DIV'); + subDiv.id = 'yearDownDiv'; + subDiv.style.height = "13px"; + subDiv.innerHTML = ''; + subDiv.onclick = changeSelectBoxYear; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + } + return div; +} + +/* This function creates the hour div at the bottom bar */ + +function slideCalendarSelectBox() +{ + if(selectBoxMovementInProgress){ + if(activeSelectBox.parentNode.id=='hourDropDown') + changeSelectBoxHour(false,activeSelectBox); + else if(activeSelectBox.parentNode.id=='yearDropDown') + changeSelectBoxYear(false,activeSelectBox); + setTimeout(slideCalendarSelectBox, speedOfSelectBoxSliding); + } +} + +function createHourDiv() +{ + if(!$('hourDropDown')){ + var div = document.createElement('DIV'); + div.className='monthYearPicker'; + }else{ + var div = $('hourDropDown'); + var subDivs = div.getElementsByTagName('DIV'); + for(var no=0;no14)startHour=14; + + var subDiv = document.createElement('DIV'); + //subDiv.innerHTML = '  - '; + subDiv.style.height = '13px'; + subDiv.id = 'hourUpDiv'; + subDiv.innerHTML = ''; + subDiv.onclick = changeSelectBoxHour; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + + for(var no=startHour;no'; + //subDiv.innerHTML = '  + '; + subDiv.onclick = changeSelectBoxHour; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + + return div; +} +/* This function creates the minute div at the bottom bar */ + +function createMinuteDiv() +{ + if(!$('minuteDropDown')){ + var div = document.createElement('DIV'); + div.className='monthYearPicker'; + }else{ + var div = $('minuteDropDown'); + var subDivs = div.getElementsByTagName('DIV'); + for(var no=0;no0){ + calendarContentDiv.removeChild(existingTable[0]); + } + + var calTable = document.createElement('TABLE'); + calTable.width = '100%'; + calTable.cellSpacing = '0'; + calendarContentDiv.appendChild(calTable); + + var calTBody = document.createElement('TBODY'); + calTable.appendChild(calTBody); + var row = calTBody.insertRow(-1); + row.className = 'calendar_week_row'; + if (showWeekNumber) + { + var cell = row.insertCell(-1); + cell.innerHTML = weekString; + cell.className = 'calendar_week_column'; + cell.style.backgroundColor = selectBoxRolloverBgColor; + } + + for(var no=0;no0 && colCounter%7==0){ + var row = calTBody.insertRow(-1); + if (showWeekNumber) { + var cell = row.insertCell(-1); + cell.className = 'calendar_week_column'; + var week = getWeek(currentYear,currentMonth,no); + cell.innerHTML = week; // Week + cell.style.backgroundColor = selectBoxRolloverBgColor; + } + } + var cell = row.insertCell(-1); + if(currentYear==inputYear && currentMonth == inputMonth && no==inputDay) + cell.className='activeDay'; + else + cell.className='normalDay'; + cell.innerHTML = no; + if (calendarDisplayTime) + cell.onclick = setDate; + else + cell.onclick = pickDate; + colCounter++; + } + + if(!document.all){ + if(calendarContentDiv.offsetHeight) + $('topBar').style.top = calendarContentDiv.offsetHeight + $('timeBar').offsetHeight + $('topBar').offsetHeight -1 + 'px'; + else{ + $('topBar').style.top = ''; + $('topBar').style.bottom = '0px'; + } + + } + + if(iframeObj){ + if(!calendarContentDivExists)setTimeout('resizeIframe()',350);else setTimeout('resizeIframe()',10); + } +} + +function resizeIframe() +{ + iframeObj.style.width = calendarDiv.offsetWidth + 'px'; + iframeObj.style.height = calendarDiv.offsetHeight + 'px' ; +} + +function pickTodaysDate(n) +{ + var d = new Date(); + if (n !== undefined) + d.setDate(d.getDate() + n); + + currentMonth = d.getMonth(); + currentYear = d.getFullYear(); + inputDay = d.getDate(); + pickDate(null, inputDay); +} + +function setDate(e, day) +{ + inputDay = this.innerHTML; + inputMonth = currentMonth; + inputYear = currentYear; + writeCalendarContent(); +} + +function pickDate(e, day) +{ + var month; + + month = currentMonth/1 + 1; + if (month < 10) + month = '0' + month; + + if (!day && this && this.innerHTML) + day = this.innerHTML; + else + day = inputDay/1; + + if (day/1 < 10) + day = '0' + day; + + if (returnFormat) + { + returnFormat = returnFormat.replace('dd',day); + returnFormat = returnFormat.replace('mm',month); + returnFormat = returnFormat.replace('yyyy',currentYear); + returnFormat = returnFormat.replace('hh',currentHour); + returnFormat = returnFormat.replace('nn',currentMinute); + returnFormat = returnFormat.replace('d',day/1); + returnFormat = returnFormat.replace('m',month/1); + + returnDateTo.value = returnFormat; + } + else + { + for(var no=0;no= 0 ? 1 : 0)) + 'px'; + //calendarDiv.style.top = (getTopPos(inputObj), calendarDiv) + 'px'; + moveElementUnder(calendarDiv, inputObj, 0, 0); + if(iframeObj){ + iframeObj.style.left = calendarDiv.style.left; + iframeObj.style.top = calendarDiv.style.top; + //// fix for EI frame problem on time dropdowns 09/30/2006 + iframeObj2.style.left = calendarDiv.style.left; + iframeObj2.style.top = calendarDiv.style.top; + } + +} + +function initCalendar() +{ + if(MSIE){ + iframeObj = document.createElement('IFRAME'); + iframeObj.style.filter = 'alpha(opacity=0)'; + iframeObj.style.position = 'absolute'; + iframeObj.border='0px'; + iframeObj.style.border = '0px'; + iframeObj.style.backgroundColor = '#FF0000'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + iframeObj2 = document.createElement('IFRAME'); + iframeObj2.style.position = 'absolute'; + iframeObj2.border='0px'; + iframeObj2.style.border = '0px'; + iframeObj2.style.height = '1px'; + iframeObj2.style.width = '1px'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + // Added fixed for HTTPS + iframeObj2.src = 'blank.html'; + iframeObj.src = 'blank.html'; + document.body.appendChild(iframeObj2); // gfb move this down AFTER the .src is set + document.body.appendChild(iframeObj); + } + + calendarDiv = $("calendarDiv"); + if (calendarDiv == null) + { + calendarDiv = document.createElement('div'); + calendarDiv.id = 'calendarDiv'; + document.getElementsByTagName("body")[0].appendChild(calendarDiv); + } + + writeBottomBar(); + writeTopBar(); + + /*if(!currentYear){ + var d = new Date(); + currentMonth = d.getMonth(); + currentYear = d.getFullYear(); + }*/ + + writeCalendarContent(); +} + +function setTimeProperties() +{ + if (!calendarDisplayTime) + { + $('timeBar').style.display='none'; + $('timeBar').style.visibility='hidden'; + //$('todaysDateString').style.width = '100%'; + $('dayPlus1').style.display = 'inline-table'; + $('dayPlus2').style.display = 'inline-table'; + } + else + { + $('timeBar').style.display='block'; + $('timeBar').style.visibility='visible'; + $('hourDropDown').style.top = $('calendar_minute_txt').parentNode.offsetHeight + calendarContentDiv.offsetHeight + $('topBar').offsetHeight + 'px'; + $('minuteDropDown').style.top = $('calendar_minute_txt').parentNode.offsetHeight + calendarContentDiv.offsetHeight + $('topBar').offsetHeight + 'px'; + $('minuteDropDown').style.right = '50px'; + $('hourDropDown').style.right = '50px'; + //$('todaysDateString').style.width = ''; + $('dayPlus1').style.display = 'none'; + $('dayPlus2').style.display = 'none'; + } +} + +function calendarSortItems(a,b) +{ + return a/1 - b/1; +} + +function trim(str) +{ + return str.replace(/^\s+|\s+$/g, ''); +} + +function padleft(str, fmt) +{ + str = str.toString(); + return fmt.substr(0, fmt.length - str.length) + str; +} + +function displayCalendar(inputField, format, buttonObj, displayTime, submit) +{ + var input, pos, date, time; + var items, d; + + calendarDisplayTime = displayTime == true; + must_submit = submit; + + d = new Date(); + d = new Date(d.getFullYear(), d.getMonth(), d.getDate()); + + input = trim(inputField.value); + + if (input != '') + { + pos = input.indexOf(' '); + if (pos > 0) + { + date = input.substr(0, pos); + time = trim(input.substr(pos)); + } + else + { + date = input; + time = '00:00'; + } + + try + { + items = date.split(/[\/]/gi); + + inputDay = items[0]/1; + currentMonth = items[1]/1; + currentYear = items[2]/1; + + items = time.split(/:/gi); + + currentHour = items[0]/1; + currentMinute = items[1]/1; + + if (isFinite(currentYear) && isFinite(currentMonth) && isFinite(inputDay) && isFinite(currentHour) && isFinite(currentMinute)) + d = new Date(currentYear, currentMonth - 1, inputDay, currentHour, currentMinute, 0); + } + catch(e) + { + } + } + + currentMonth = padleft(d.getMonth() + 1, '00'); + currentYear = padleft(d.getFullYear(), '0000'); + currentHour = padleft(d.getHours(), '00'); + currentMinute = padleft(d.getMinutes(), '00'); + + currentMonth--; + + inputDay = d.getDate()/1; + inputYear = currentYear; + inputMonth = currentMonth; + + if (!calendarDiv) + { + initCalendar(); + } + else + { + if (calendarDiv.style.display == 'block') + { + closeCalendar(); + return false; + } + + writeCalendarContent(); + } + + returnFormat = format; + returnDateTo = inputField; + + calendarDiv.style.visibility = 'visible'; + calendarDiv.style.display = 'block'; + positionCalendar(buttonObj); + + if(iframeObj){ + iframeObj.style.display = ''; + iframeObj.style.height = '140px'; + iframeObj.style.width = '195px'; + iframeObj2.style.display = ''; + iframeObj2.style.height = '140px'; + iframeObj2.style.width = '195px'; + } + + setTimeProperties(); + updateYearDiv(); + updateMonthDiv(); + updateMinuteDiv(); + updateHourDiv(); + + var hShadow = $('gw-modal'); + hShadow.style.visibility = 'visible'; + hShadow.onclick = closeCalendar; +} diff --git a/comp/src/gb.web.form/.hidden/control/webbutton.png b/comp/src/gb.web.form/.hidden/control/webbutton.png new file mode 120000 index 00000000..23d989b3 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webbutton.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/button.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webcheckbox.png b/comp/src/gb.web.form/.hidden/control/webcheckbox.png new file mode 120000 index 00000000..b8fe6fbd --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webcheckbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/checkbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webcombobox.png b/comp/src/gb.web.form/.hidden/control/webcombobox.png new file mode 120000 index 00000000..ccfd857b --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webcombobox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/combobox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webcontainer.png b/comp/src/gb.web.form/.hidden/control/webcontainer.png new file mode 100644 index 0000000000000000000000000000000000000000..0d4551280dc4bc6289f35fc30d164d41118dad3f GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;*aCb)T!HlehK938rg;Kcj3q&S z!3+-1ZlnP@qMj~}Asp9}fAF*L@bd8VH1{)D@-q0@h>HvOP4faOVDNPHb6Mw<&;$Sx Cf*G{{ literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webdatebox.png b/comp/src/gb.web.form/.hidden/control/webdatebox.png new file mode 120000 index 00000000..119ec46b --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webdatebox.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/datebox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webdatechooser.png b/comp/src/gb.web.form/.hidden/control/webdatechooser.png new file mode 120000 index 00000000..e9aed8c9 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webdatechooser.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/datechooser.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webexpander.png b/comp/src/gb.web.form/.hidden/control/webexpander.png new file mode 120000 index 00000000..fbdd33c7 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webexpander.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/expander.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webhbox.png b/comp/src/gb.web.form/.hidden/control/webhbox.png new file mode 120000 index 00000000..891ef5b1 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webhbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/hbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webhtml.png b/comp/src/gb.web.form/.hidden/control/webhtml.png new file mode 100644 index 0000000000000000000000000000000000000000..ca4ef5d505fce0007a065eeef76a2c232bd5a8ba GIT binary patch literal 403 zcmV;E0c`$>P)kdg00046Nkl1nmSBeE>@(eLy0bY-OP& zg$W^`*TT5&W`8nfvB+1=oSiwdbLMhk=!q$Ugb+b9OvFas5y z5sZIIK;;U3F5qvV`x@vdk>re5rtEpOo4_=0oc1$`u|or)M%t4=8VjMpc5v}6Q$lF7 zPr}yL5stg#1P8PcN-TLdPjbX3!X>6lKS5Y?%uTKkJ~(8;ZD1ihu;MYU`qV(43WDVp zTL|aO_AFiSt${f%5L#|A%{{_9yNo*24MzgsjIl=(;f_V`3v5wGsF1g{LQev#JR;QD z3@SjKB7$&@0cg}52^icU2nA*cB}J_Iro34)n{L;<$wK|IF{+lGOE-tdS3p1$CIs5f+0!3fhj28M3X5&M&Qx|0ne}y zu08851Q*N>Ug7B_F~RZ9gvn~{4e^J4codY6-uV#!V6LRtj~AZ~KEJ;GDD&Ocnulvo4n;lpMibhXYKbLh*2~7ZWni)#~ literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webmenubar.png b/comp/src/gb.web.form/.hidden/control/webmenubar.png new file mode 100644 index 0000000000000000000000000000000000000000..20648ca34d68a1e43598f122211048c09a8056fe GIT binary patch literal 472 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy+XH+;T!D02S{jgCzkdC@ckkZ5 zeG6oFc6M&rvgO5#7eIDHL&Ls(`<^^`5*iwM`0(M^uU{WIas;UI)TvV+KYqM$;lis| zuYf8SFJ65A{(YeO_V)J6moEbuB_$Y8F@OI2Ns}i1-d?g0sNbL@ z$S;_Il~LetFq70@A7+_<&dk3ZzT12;eQ)sKZyx)Q(p5$u^;Bn2qYW3LWVT(mKm)6sjAO7EedTQ=^{^TT^)oQkx zx1t;D6$}^}3!S(g{&^HT)9XhW`{kMP6CHQXKl{}4Rr13iovEyiTGuy3ZFXrqT-EH- zxHyg5wJ~&eawpUMUA983`@RQ?EIN4M8YlnK8Sale)p%x|dvqi4mdKI;Vst0RBSF!Tr<>lq=?d<>n0Dypi z|NsB?`ux1t>8Hx$yVvRU`TXni`1kw$=H}+z-Q9Gp(KLpQ&TlHHD6y}+uPe@WMoxURXRF4pP!%k`T6?%{foNU zin-a(;_rL0)GC0vd9c*^`~CCt^V!+i{{H@rjg4(>Z4VC*$H&Lz<>g05N2jN!>+|_X zlEX2DyR6OR>+9>UudjG`csDmUN=izmrlvfLzbt~fN|VG`oXAp`$A*T6Mn*=Rot>JR znp<02y1KeZNJ#4P_(PAvkGAsz?Ck88 zmX<9oEki>?Xl1eui!T>p$Vk5v|-{py2uJt_4Ex4jf_=HOwC{l%q@@=sOei;S=-p!**iF5 zSKwse?BeR??&0a>t)k-N>*pV!VjdV291;pu5Ekwk5gFy_=@}gptD+JYpOBcOVxF9m znwAb#kdf&L0a@7~LvwQT@>R?WR8$K6p$hD>yo!qTOG<6ZGJqoG6_r&$1=T7lHMLL$ zb@dI6P0cNOt!*F!+B;M_yAsU1dsKS+pbGjYOq?`%%G7Do!OosBbJpxRp>qr7)z0sL z`C>to!@@<2m&8ImwshI@!WAo*uL?j4NlnMqjceAz!k}kedcpb)J-Fins9;vX#!Wpi z1sGtna{8=UTeenhhD0|eU}OR+n6-_Wg%w8k4UU7jwEAs)w*b9~a){Z~HiAXjmA z#%Kuw?b%Q6VQoIIB_&)WBwS{mxW>z>!^6X^X}Unp_k}{U z?ZsQtk{aI*tzc|E$RNhn)-2J)?rD5L&e{4S=gR{M9L<5uZo)#LhZ3$Zu(7GNUH3nE t?a!3Pho;HN5B|^OS(GyC!z@EthL}F--%iTOCxNbF@O1TaS?83{1OP=SM_K>? literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webtabpanel.png b/comp/src/gb.web.form/.hidden/control/webtabpanel.png new file mode 120000 index 00000000..7be554f7 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtabpanel.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/tabpanel.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webtextarea.png b/comp/src/gb.web.form/.hidden/control/webtextarea.png new file mode 120000 index 00000000..dd1d94b9 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtextarea.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textarea.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webtextbox.png b/comp/src/gb.web.form/.hidden/control/webtextbox.png new file mode 120000 index 00000000..276853b4 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtextbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webtimer.png b/comp/src/gb.web.form/.hidden/control/webtimer.png new file mode 120000 index 00000000..0ae99c8a --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtimer.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/timer.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webuploadarea.png b/comp/src/gb.web.form/.hidden/control/webuploadarea.png new file mode 100644 index 0000000000000000000000000000000000000000..0fac5b2ec05670b2127600c217651c80383cc7e6 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv#Q>iWS0L>G2KM&$KpH|qME*B4 z02#>vasPpeI7@>3f*E{XybSppvtYj5oiz)9f>oX_jv*T7lV^0YNvIhtVDQW0;?P{g znW5_>9I>_Q9aGOSJ_SaxWX2T?pH~@7aCoJ~yyL*7meAEV7Cm6n30!?BY^_)9)+>x@ z8WZ+ri3W(|aR-zopr02`1^9{>OV literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webuploadbutton.png b/comp/src/gb.web.form/.hidden/control/webuploadbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..ebd4f966e9b493f33a72ca639fcde4a49bbaeef1 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4U37#&FAs)w*6C_xf`6Z;8|DR70 zP!0Z`eXOLdExiB#e`R62fA>FhwK|4OdMH1WZ$e6fKx$&b5C1@k4F(f({}&h~mB_4W1h^YilZ^6>ER?(XhnWMpAs zVcy=}?Ck7dU|?NcUEST?>+9>>+}!Eu>Dt=bTwGk|=jYhi*yZKr*4EbI;^NHA%;4bQ zfU4wxwg3PB|K#N4#hA#Y00003bW%=J08#2038Sq5006g1L_t(I%hl4$4uUWch2hfT z1&gS-JqtrSi75+Le(7c={{}KD(k4JhO2>4;S^H=>Gz8F7Vyy_to hjOIUr+0UH&QEyzKNGb36V`BgS002ovPDHLkV1h2Hy|MrR literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webvbox.png b/comp/src/gb.web.form/.hidden/control/webvbox.png new file mode 120000 index 00000000..50d7662d --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webvbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/vbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.icon.png b/comp/src/gb.web.form/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4eba83bf6cfe6b3f7a6f60eacfe4433c2d3d67ac GIT binary patch literal 11601 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 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.lang/fr.po b/comp/src/gb.web.form/.lang/fr.po new file mode 100644 index 00000000..d59dd0fb --- /dev/null +++ b/comp/src/gb.web.form/.lang/fr.po @@ -0,0 +1,306 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.form 3.14.90\n" +"POT-Creation-Date: 2022-06-30 09:46 UTC\n" +"PO-Revision-Date: 2020-03-03 22:26 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Web application development" +msgstr "" + +#: FHello.webform:14 +msgid "This is a very important message" +msgstr "" + +#: FHello.webform:18 +msgid "" +"

    Welcome to the gb.web.form component!

    \n" +"

    This component aims at making web application as easy as making desktop applications.

    \n" +"

    This goal is difficult to achieve, but I hope to succeed!

    " +msgstr "" + +#: FHello.webform:34 FMessage.webform:50 +msgid "OK" +msgstr "OK" + +#: Message.class:33 +msgid "Information" +msgstr "Information" + +#: Message.class:45 Webform2.webform:170 +msgid "Warning" +msgstr "Avertissement" + +#: Message.class:51 +msgid "Error" +msgstr "Erreur" + +#: Message.class:57 +msgid "Question" +msgstr "Question" + +#: WebUploader.class:36 +msgid "Abort" +msgstr "Annuler" + +#: WebUploader.class:59 +msgid "Upload file..." +msgstr "Envoyer un fichier..." + +#: Webform1.webform:39 +msgid "gb.web.form test" +msgstr "" + +#: Webform1.webform:42 +msgid "WebMenu1" +msgstr "" + +#: Webform1.webform:45 +msgid "WebMenu2" +msgstr "" + +#: Webform1.webform:49 +msgid "WebMenu3" +msgstr "" + +#: Webform1.webform:54 +msgid "WebMenu4" +msgstr "" + +#: Webform1.webform:64 +msgid "Header" +msgstr "" + +#: Webform1.webform:85 +msgid "Hello world!" +msgstr "" + +#: Webform1.webform:89 +msgid "Reset text" +msgstr "" + +#: Webform1.webform:93 +msgid "Change text" +msgstr "" + +#: Webform1.webform:97 +msgid "0" +msgstr "" + +#: Webform1.webform:101 +msgid "Border" +msgstr "" + +#: Webform1.webform:105 +msgid "Toggle enabled" +msgstr "" + +#: Webform1.webform:131 +msgid "Absolute !" +msgstr "" + +#: Webform1.webform:143 +msgid "B1" +msgstr "" + +#: Webform1.webform:148 +msgid "B5" +msgstr "" + +#: Webform1.webform:153 +msgid "B7" +msgstr "" + +#: Webform1.webform:158 +msgid "B6" +msgstr "" + +#: Webform1.webform:163 +msgid "B2" +msgstr "" + +#: Webform1.webform:168 +msgid "B3" +msgstr "" + +#: Webform1.webform:173 +msgid "B4" +msgstr "" + +#: Webform1.webform:178 +msgid "B8" +msgstr "" + +#: Webform2.webform:67 +msgid "This is webform2" +msgstr "" + +#: Webform2.webform:73 +msgid "File" +msgstr "" + +#: Webform2.webform:76 +msgid "New" +msgstr "" + +#: Webform2.webform:82 +msgid "Open" +msgstr "" + +#: Webform2.webform:88 +msgid "Project" +msgstr "" + +#: Webform2.webform:91 +msgid "Compile" +msgstr "" + +#: Webform2.webform:97 +msgid "Compile all" +msgstr "" + +#: Webform2.webform:105 +msgid "Translate" +msgstr "" + +#: Webform2.webform:113 +msgid "Teleport" +msgstr "" + +#: Webform2.webform:121 +msgid "Quit" +msgstr "" + +#: Webform2.webform:160 +msgid "A button" +msgstr "" + +#: Webform2.webform:165 +msgid "Add window" +msgstr "" + +#: Webform2.webform:175 +msgid "Toggle timer" +msgstr "" + +#: Webform2.webform:180 +msgid "Hidden" +msgstr "" + +#: Webform2.webform:203 +msgid "General" +msgstr "" + +#: Webform2.webform:206 +msgid "Hello !" +msgstr "" + +#: Webform2.webform:210 +msgid "Check and uncheck me!" +msgstr "" + +#: Webform2.webform:217 Webform6.webform:17 +msgid "Élément 1" +msgstr "" + +#: Webform2.webform:217 Webform6.webform:17 +msgid "Élément 2" +msgstr "" + +#: Webform2.webform:217 Webform6.webform:17 +msgid "Élément 3" +msgstr "" + +#: Webform2.webform:217 +msgid "Élément 4" +msgstr "" + +#: Webform2.webform:248 +msgid "New element" +msgstr "" + +#: Webform2.webform:252 +msgid "Clear" +msgstr "" + +#: Webform2.webform:269 +msgid "This is an expander container" +msgstr "" + +#: Webform2.webform:278 +msgid "Alpha" +msgstr "" + +#: Webform2.webform:282 +msgid "Beta" +msgstr "" + +#: Webform2.webform:287 +msgid "Gamma" +msgstr "" + +#: Webform2.webform:291 +msgid "Gambas Almost Means Basic!" +msgstr "" + +#: Webform2.webform:300 +msgid "Please enter some text:" +msgstr "" + +#: Webform2.webform:319 +msgid "Options" +msgstr "" + +#: Webform2.webform:322 +msgid "Test" +msgstr "" + +#: Webform2.webform:325 +msgid "Application state" +msgstr "" + +#: Webform3.webform:24 +msgid "Tab 1" +msgstr "" + +#: Webform3.webform:27 +msgid "Message" +msgstr "" + +#: Webform3.webform:36 +msgid "Tab 2" +msgstr "" + +#: Webform4.webform:28 +msgid "Select file" +msgstr "" + +#: Webform4.webform:45 +msgid "Show" +msgstr "" + +#: Webform4.webform:49 +msgid "Hide" +msgstr "" + +#: Webform6.webform:30 +msgid "Copy" +msgstr "" + +#: Webform6.webform:34 +msgid "Text" +msgstr "" + +#: Webform7.webform:13 +msgid "Name" +msgstr "" + +#: Webform8.webform:10 +msgid "Load Form" +msgstr "" diff --git a/comp/src/gb.web.form/.lang/it.po b/comp/src/gb.web.form/.lang/it.po new file mode 100644 index 00000000..eae9e675 --- /dev/null +++ b/comp/src/gb.web.form/.lang/it.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.form 3.16.90\n" +"PO-Revision-Date: 2021-12-06 16:12 UTC\n" +"Last-Translator: gian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: WebUploader.class:36 +msgid "Abort" +msgstr "Interruzione" + +#: Message.class:51 +msgid "Error" +msgstr "Errore" + +#: Message.class:33 +msgid "Information" +msgstr "Informazione" + +# gb-ignore +#: FMessage.webform:50 +msgid "OK" +msgstr "" + +#: Message.class:57 +msgid "Question" +msgstr "Domanda" + +#: WebUploader.class:59 +msgid "Upload file..." +msgstr "Carica file..." + +#: Message.class:45 +msgid "Warning" +msgstr "Attenzione" + +#: .project:1 +msgid "Web application development" +msgstr "Sviluppo di applicazioni web" + diff --git a/comp/src/gb.web.form/.lang/nl.po b/comp/src/gb.web.form/.lang/nl.po new file mode 100644 index 00000000..234fe515 --- /dev/null +++ b/comp/src/gb.web.form/.lang/nl.po @@ -0,0 +1,283 @@ +# Willy Raets < gbWilly@openmailbox.org >, 2017 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.form 3.10.90\n" +"POT-Creation-Date: 2019-04-06 18:47 UTC\n" +"PO-Revision-Date: 2017-08-26 19:52 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "Web application GUI controls" +msgstr "Web applicatie GUI controls" + +#: FHello.webform:14 +msgid "This is a very important message" +msgstr "Dit is een heel belangrijke boodschap" + +#: FHello.webform:18 +msgid "" +"

    Welcome to the gb.web.form component!

    \n" +"

    This component aims at making web application as easy as making desktop applications.

    \n" +"

    This goal is difficult to achieve, but I hope to succeed!

    " +msgstr "-" + +#: FHello.webform:34 FMessage.webform:50 +msgid "OK" +msgstr "-" + +#: Message.class:33 +msgid "Information" +msgstr "Informatie" + +#: Message.class:45 Webform2.webform:169 +msgid "Warning" +msgstr "Waarschuwing" + +#: Message.class:51 +msgid "Error" +msgstr "-" + +#: Message.class:57 +msgid "Question" +msgstr "Vraag" + +#: WebUploader.class:36 +msgid "Abort" +msgstr "Afbreken" + +#: WebUploader.class:59 +msgid "Upload file..." +msgstr "Upload bestand..." + +#: Webform1.webform:35 +msgid "gb.web.form test" +msgstr "-" + +#: Webform1.webform:44 +msgid "Header" +msgstr "Hoofding" + +#: Webform1.webform:65 +msgid "Hello world!" +msgstr "Hallo wereld !" + +#: Webform1.webform:69 +msgid "Reset text" +msgstr "Tekst resetten" + +#: Webform1.webform:73 +msgid "Change text" +msgstr "Wijzig tekst" + +#: Webform1.webform:77 +msgid "0" +msgstr "-" + +#: Webform1.webform:81 +msgid "Border" +msgstr "Rand" + +#: Webform1.webform:85 +msgid "Toggle enabled" +msgstr "-" + +#: Webform1.webform:111 +msgid "Absolute !" +msgstr "Absoluut !" + +#: Webform1.webform:123 +msgid "B1" +msgstr "-" + +#: Webform1.webform:128 +msgid "B5" +msgstr "-" + +#: Webform1.webform:133 +msgid "B7" +msgstr "-" + +#: Webform1.webform:138 +msgid "B6" +msgstr "-" + +#: Webform1.webform:143 +msgid "B2" +msgstr "-" + +#: Webform1.webform:148 +msgid "B3" +msgstr "-" + +#: Webform1.webform:153 +msgid "B4" +msgstr "-" + +#: Webform1.webform:158 +msgid "B8" +msgstr "-" + +#: Webform2.webform:67 +msgid "This is webform2" +msgstr "Dit is webformulier2" + +#: Webform2.webform:73 +msgid "File" +msgstr "Bestand" + +#: Webform2.webform:76 +msgid "New" +msgstr "Nieuw" + +#: Webform2.webform:82 +msgid "Open" +msgstr "Openen" + +#: Webform2.webform:88 +msgid "Project" +msgstr "-" + +#: Webform2.webform:91 +msgid "Compile" +msgstr "Compileer" + +#: Webform2.webform:97 +msgid "Compile all" +msgstr "Compileer alles" + +#: Webform2.webform:105 +msgid "Translate" +msgstr "Vertaal" + +#: Webform2.webform:113 +msgid "Teleport" +msgstr "Teleporteer" + +#: Webform2.webform:121 +msgid "Quit" +msgstr "Afsluiten" + +#: Webform2.webform:159 +msgid "A button" +msgstr "Een knop" + +#: Webform2.webform:164 +msgid "Add window" +msgstr "Venster toevoegen" + +#: Webform2.webform:174 +msgid "Toggle timer" +msgstr "-" + +#: Webform2.webform:179 +msgid "Hidden" +msgstr "Verborgen" + +#: Webform2.webform:202 +msgid "General" +msgstr "Algemeen" + +#: Webform2.webform:205 +msgid "Hello !" +msgstr "Hallo !" + +#: Webform2.webform:209 +msgid "Check and uncheck me!" +msgstr "Vink me aan, vink me uit!" + +#: Webform2.webform:216 Webform6.webform:17 +msgid "Élément 1" +msgstr "-" + +#: Webform2.webform:216 Webform6.webform:17 +msgid "Élément 2" +msgstr "-" + +#: Webform2.webform:216 Webform6.webform:17 +msgid "Élément 3" +msgstr "-" + +#: Webform2.webform:216 +msgid "Élément 4" +msgstr "-" + +#: Webform2.webform:247 +msgid "New element" +msgstr "Nieuw element" + +#: Webform2.webform:251 +msgid "Clear" +msgstr "Opschonen" + +#: Webform2.webform:268 +msgid "This is an expander container" +msgstr "Dit is een expander container" + +#: Webform2.webform:277 +msgid "Alpha" +msgstr "-" + +#: Webform2.webform:281 +msgid "Beta" +msgstr "-" + +#: Webform2.webform:286 +msgid "Gamma" +msgstr "-" + +#: Webform2.webform:290 +msgid "Gambas Almost Means Basic!" +msgstr "-" + +#: Webform2.webform:299 +msgid "Please enter some text:" +msgstr "Voer wat tekst in:" + +#: Webform2.webform:318 +msgid "Options" +msgstr "Opties" + +#: Webform2.webform:321 +msgid "Test" +msgstr "-" + +#: Webform2.webform:324 +msgid "Application state" +msgstr "Applicatie status" + +#: Webform3.webform:24 +msgid "Tab 1" +msgstr "-" + +#: Webform3.webform:27 +msgid "Message" +msgstr "Bericht" + +#: Webform3.webform:36 +msgid "Tab 2" +msgstr "-" + +#: Webform4.webform:28 +msgid "Select file" +msgstr "Selecteer bestand" + +#: Webform4.webform:45 +msgid "Show" +msgstr "Weergeven" + +#: Webform4.webform:49 +msgid "Hide" +msgstr "Verberg" + +#: Webform6.webform:30 +msgid "Copy" +msgstr "" + +#: Webform6.webform:34 +msgid "Text" +msgstr "" diff --git a/comp/src/gb.web.form/.lang/pt_BR.po b/comp/src/gb.web.form/.lang/pt_BR.po new file mode 100644 index 00000000..d72e29ee --- /dev/null +++ b/comp/src/gb.web.form/.lang/pt_BR.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.form 3.14.90\n" +"PO-Revision-Date: 2020-06-12 00:43 UTC\n" +"Last-Translator: Gen Braga \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Web application development" +msgstr "Desenvolvimento de aplicação web" + +# gb-ignore +#: FMessage.webform:50 +msgid "OK" +msgstr "" + +#: Message.class:33 +msgid "Information" +msgstr "Informação" + +#: Message.class:45 +msgid "Warning" +msgstr "Atenção" + +#: Message.class:51 +msgid "Error" +msgstr "Erro" + +#: Message.class:57 +msgid "Question" +msgstr "Questão" + +#: WebUploader.class:36 +msgid "Abort" +msgstr "Abortar" + +#: WebUploader.class:59 +msgid "Upload file..." +msgstr "Upload de arquivo..." + diff --git a/comp/src/gb.web.form/.lang/ru.po b/comp/src/gb.web.form/.lang/ru.po new file mode 100644 index 00000000..b18fd4ca --- /dev/null +++ b/comp/src/gb.web.form/.lang/ru.po @@ -0,0 +1,300 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: comp/src/gb.web.form/.project:19 +msgid "Web application GUI controls" +msgstr "Элементы управления графическим интерфейсом веб-приложения" + +#: comp/src/gb.web.form/.src/WebUploader.class:36 +msgid "Abort" +msgstr "Прервать" + +#: comp/src/gb.web.form/.src/WebUploader.class:59 +msgid "Upload file..." +msgstr "Выгрузить файл..." + +#: comp/src/gb.web.form/.src/Test/FHello.webform:8 +msgid "This is a very important message" +msgstr "Это очень важное сообщение" + +#: comp/src/gb.web.form/.src/Test/FHello.webform:12 +msgid "" +"

    Welcome to the gb.web.form component!

    \n" +"

    This component aims at making web application as easy as making desktop applications.

    \n" +"

    This goal is difficult to achieve, but I hope to succeed!

    " +msgstr "" +"

    Добро пожаловать в компонент gb.web.form!

    \n" +"

    тот компонент нацелен на то, чтобы сделать веб-приложение таким же простым, как и настольные приложения.

    \n" +"

    Эту цель трудно достичь, но я надеюсь добиться успеха!

    " + +#: comp/src/gb.web.form/.src/Test/FHello.webform:28 comp/src/gb.web.form/.src/Message/FMessage.webform:40 +msgid "OK" +msgstr "ОК" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:9 +msgid "gb.web.form test" +msgstr "тест gb.web.form" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:18 +msgid "Header" +msgstr "Заголовок" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:39 +msgid "Hello world!" +msgstr "Привет, мир!" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:43 +msgid "Reset text" +msgstr "Сбросить текст" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:47 +msgid "Change text" +msgstr "Изменить текст" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:51 +msgid "0" +msgstr "0" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:55 +msgid "Border" +msgstr "Граница" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:59 +msgid "Toggle enabled" +msgstr "Переключатель включен" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:85 +msgid "Absolute !" +msgstr "Абсолютно!" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:97 +msgid "B1" +msgstr "B1" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:102 +msgid "B5" +msgstr "B5" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:107 +msgid "B7" +msgstr "B7" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:112 +msgid "B6" +msgstr "B6" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:117 +msgid "B2" +msgstr "B2" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:122 +msgid "B3" +msgstr "B3" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:127 +msgid "B4" +msgstr "B4" + +#: comp/src/gb.web.form/.src/Test/Webform1.webform:132 +msgid "B8" +msgstr "B8" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:7 +msgid "This is webform2" +msgstr "Это веб_форма_2" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:13 +msgid "File" +msgstr "Файл" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:16 +msgid "New" +msgstr "Новый" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:22 +msgid "Open" +msgstr "Открыть" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:28 comp/src/gb.web.form/.src/Test/Webform2.webform:66 +msgid "Project" +msgstr "Проект" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:31 comp/src/gb.web.form/.src/Test/Webform2.webform:69 +msgid "Compile" +msgstr "Компилировать" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:37 comp/src/gb.web.form/.src/Test/Webform2.webform:75 +msgid "Compile all" +msgstr "Компилировать все" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:45 comp/src/gb.web.form/.src/Test/Webform2.webform:83 +msgid "Translate" +msgstr "Перевести" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:53 comp/src/gb.web.form/.src/Test/Webform2.webform:91 +msgid "Teleport" +msgstr "Телепорт" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:61 +msgid "Quit" +msgstr "Выход" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:99 +msgid "A button" +msgstr "Кнопка" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:104 +msgid "Add window" +msgstr "Добавить окно" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:109 comp/src/gb.web.form/.src/Message/Message.class:45 +msgid "Warning" +msgstr "Предупреждение" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:114 +msgid "Toggle timer" +msgstr "Переключить таймер" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:119 +msgid "Hidden" +msgstr "Скрытый" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:142 +msgid "General" +msgstr "Основное" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:145 +msgid "Hello !" +msgstr "Привет!" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:149 +msgid "Check and uncheck me!" +msgstr "Отметьте флажком и снимите его с меня!" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:156 comp/src/gb.web.form/.src/Test/Webform2.webform:182 comp/src/gb.web.form/.src/Test/Webform6.webform:10 +msgid "Élément 1" +msgstr "Элемент 1" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:156 comp/src/gb.web.form/.src/Test/Webform2.webform:182 comp/src/gb.web.form/.src/Test/Webform6.webform:10 +msgid "Élément 2" +msgstr "Элемент 2" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:156 comp/src/gb.web.form/.src/Test/Webform2.webform:182 comp/src/gb.web.form/.src/Test/Webform6.webform:10 +msgid "Élément 3" +msgstr "Элемент 3" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:156 comp/src/gb.web.form/.src/Test/Webform2.webform:182 +msgid "Élément 4" +msgstr "Элемент 4" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:187 +msgid "New element" +msgstr "Новый элемент" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:191 +msgid "Clear" +msgstr "Очистить" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:208 +msgid "This is an expander container" +msgstr "Это контейнер-расширитель" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:217 +msgid "Alpha" +msgstr "Альфа" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:221 +msgid "Beta" +msgstr "Бета" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:226 +msgid "Gamma" +msgstr "Гамма" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:230 +msgid "Gambas Almost Means Basic!" +msgstr "Gambas почти что Basic!" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:239 +msgid "Please enter some text:" +msgstr "Пожалуйста, введите текст:" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:258 +msgid "Options" +msgstr "Опции" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:261 +msgid "Test" +msgstr "Тест" + +#: comp/src/gb.web.form/.src/Test/Webform2.webform:264 +msgid "Application state" +msgstr "Состояние приложения" + +#: comp/src/gb.web.form/.src/Test/Webform3.webform:18 +msgid "Tab 1" +msgstr "Вкладка 1" + +#: comp/src/gb.web.form/.src/Test/Webform3.webform:21 +msgid "Message" +msgstr "Сообщение" + +#: comp/src/gb.web.form/.src/Test/Webform3.webform:30 +msgid "Tab 2" +msgstr "Вкладка 2" + +#: comp/src/gb.web.form/.src/Test/Webform4.webform:17 +msgid "Select file" +msgstr "Выбрать файл" + +#: comp/src/gb.web.form/.src/Test/Webform4.webform:34 +msgid "Show" +msgstr "Показать" + +#: comp/src/gb.web.form/.src/Test/Webform4.webform:38 +msgid "Hide" +msgstr "Скрыть" + +#: comp/src/gb.web.form/.src/Test/Webform6.webform:23 +msgid "Copy" +msgstr "Копировать" + +#: comp/src/gb.web.form/.src/Test/Webform6.webform:27 +msgid "Text" +msgstr "Текст" + +#: comp/src/gb.web.form/.src/Message/Message.class:33 +msgid "Information" +msgstr "Информация" + +#: comp/src/gb.web.form/.src/Message/Message.class:51 +msgid "Error" +msgstr "Ошибка" + +#: comp/src/gb.web.form/.src/Message/Message.class:57 +msgid "Question" +msgstr "Вопрос" + diff --git a/comp/src/gb.web.form/.lang/zh.po b/comp/src/gb.web.form/.lang/zh.po new file mode 100644 index 00000000..02f2f585 --- /dev/null +++ b/comp/src/gb.web.form/.lang/zh.po @@ -0,0 +1,306 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.form 3.15.90\n" +"POT-Creation-Date: 2020-11-18 18:42 UTC\n" +"PO-Revision-Date: 2020-11-18 18:43 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Web application development" +msgstr "Web应用程序开发" + +#: FHello.webform:14 +msgid "This is a very important message" +msgstr "" + +#: FHello.webform:18 +msgid "" +"

    Welcome to the gb.web.form component!

    \n" +"

    This component aims at making web application as easy as making desktop applications.

    \n" +"

    This goal is difficult to achieve, but I hope to succeed!

    " +msgstr "" + +#: FHello.webform:34 FMessage.webform:50 +msgid "OK" +msgstr "确定" + +#: Message.class:33 +msgid "Information" +msgstr "信息" + +#: Message.class:45 Webform2.webform:170 +msgid "Warning" +msgstr "警告" + +#: Message.class:51 +msgid "Error" +msgstr "错误" + +#: Message.class:57 +msgid "Question" +msgstr "问题" + +#: WebUploader.class:36 +msgid "Abort" +msgstr "中断" + +#: WebUploader.class:59 +msgid "Upload file..." +msgstr "上传文件..." + +#: Webform1.webform:39 +msgid "gb.web.form test" +msgstr "" + +#: Webform1.webform:42 +msgid "WebMenu1" +msgstr "" + +#: Webform1.webform:45 +msgid "WebMenu2" +msgstr "" + +#: Webform1.webform:49 +msgid "WebMenu3" +msgstr "" + +#: Webform1.webform:54 +msgid "WebMenu4" +msgstr "" + +#: Webform1.webform:64 +msgid "Header" +msgstr "" + +#: Webform1.webform:85 +msgid "Hello world!" +msgstr "" + +#: Webform1.webform:89 +msgid "Reset text" +msgstr "" + +#: Webform1.webform:93 +msgid "Change text" +msgstr "" + +#: Webform1.webform:97 +msgid "0" +msgstr "" + +#: Webform1.webform:101 +msgid "Border" +msgstr "" + +#: Webform1.webform:105 +msgid "Toggle enabled" +msgstr "" + +#: Webform1.webform:131 +msgid "Absolute !" +msgstr "" + +#: Webform1.webform:143 +msgid "B1" +msgstr "" + +#: Webform1.webform:148 +msgid "B5" +msgstr "" + +#: Webform1.webform:153 +msgid "B7" +msgstr "" + +#: Webform1.webform:158 +msgid "B6" +msgstr "" + +#: Webform1.webform:163 +msgid "B2" +msgstr "" + +#: Webform1.webform:168 +msgid "B3" +msgstr "" + +#: Webform1.webform:173 +msgid "B4" +msgstr "" + +#: Webform1.webform:178 +msgid "B8" +msgstr "" + +#: Webform2.webform:67 +msgid "This is webform2" +msgstr "" + +#: Webform2.webform:73 +msgid "File" +msgstr "" + +#: Webform2.webform:76 +msgid "New" +msgstr "" + +#: Webform2.webform:82 +msgid "Open" +msgstr "" + +#: Webform2.webform:88 +msgid "Project" +msgstr "" + +#: Webform2.webform:91 +msgid "Compile" +msgstr "" + +#: Webform2.webform:97 +msgid "Compile all" +msgstr "" + +#: Webform2.webform:105 +msgid "Translate" +msgstr "" + +#: Webform2.webform:113 +msgid "Teleport" +msgstr "" + +#: Webform2.webform:121 +msgid "Quit" +msgstr "" + +#: Webform2.webform:160 +msgid "A button" +msgstr "" + +#: Webform2.webform:165 +msgid "Add window" +msgstr "" + +#: Webform2.webform:175 +msgid "Toggle timer" +msgstr "" + +#: Webform2.webform:180 +msgid "Hidden" +msgstr "" + +#: Webform2.webform:203 +msgid "General" +msgstr "" + +#: Webform2.webform:206 +msgid "Hello !" +msgstr "" + +#: Webform2.webform:210 +msgid "Check and uncheck me!" +msgstr "" + +#: Webform2.webform:217 Webform6.webform:17 +msgid "Élément 1" +msgstr "" + +#: Webform2.webform:217 Webform6.webform:17 +msgid "Élément 2" +msgstr "" + +#: Webform2.webform:217 Webform6.webform:17 +msgid "Élément 3" +msgstr "" + +#: Webform2.webform:217 +msgid "Élément 4" +msgstr "" + +#: Webform2.webform:248 +msgid "New element" +msgstr "" + +#: Webform2.webform:252 +msgid "Clear" +msgstr "" + +#: Webform2.webform:269 +msgid "This is an expander container" +msgstr "" + +#: Webform2.webform:278 +msgid "Alpha" +msgstr "" + +#: Webform2.webform:282 +msgid "Beta" +msgstr "" + +#: Webform2.webform:287 +msgid "Gamma" +msgstr "" + +#: Webform2.webform:291 +msgid "Gambas Almost Means Basic!" +msgstr "" + +#: Webform2.webform:300 +msgid "Please enter some text:" +msgstr "" + +#: Webform2.webform:319 +msgid "Options" +msgstr "" + +#: Webform2.webform:322 +msgid "Test" +msgstr "" + +#: Webform2.webform:325 +msgid "Application state" +msgstr "" + +#: Webform3.webform:24 +msgid "Tab 1" +msgstr "" + +#: Webform3.webform:27 +msgid "Message" +msgstr "" + +#: Webform3.webform:36 +msgid "Tab 2" +msgstr "" + +#: Webform4.webform:28 +msgid "Select file" +msgstr "" + +#: Webform4.webform:45 +msgid "Show" +msgstr "" + +#: Webform4.webform:49 +msgid "Hide" +msgstr "" + +#: Webform6.webform:30 +msgid "Copy" +msgstr "" + +#: Webform6.webform:34 +msgid "Text" +msgstr "" + +#: Webform7.webform:13 +msgid "Name" +msgstr "" + +#: Webform8.webform:10 +msgid "Load Form" +msgstr "" diff --git a/comp/src/gb.web.form/.project b/comp/src/gb.web.form/.project new file mode 100644 index 00000000..41cd380d --- /dev/null +++ b/comp/src/gb.web.form/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=Web application development +Startup=Webform1 +Version=3.17.90 +VersionFile=1 +Component=gb.util +Component=gb.util.web +Component=gb.web +Authors="Benoît Minisini" +TabSize=2 +Translate=1 +Language=en_US +RunAfter=cp $(FILE) /home/guygle/guygle/cgi-bin/test +Type=Component +ForceBytecodeVersion=3.8 +Packager=1 +DoNotTranslate=".src/Test" +usehttpserver=1 diff --git a/comp/src/gb.web.form/.public/favicon.png b/comp/src/gb.web.form/.public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6acb4bfb851c591279232c229f84a5f70c129e GIT binary patch literal 16241 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 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-arrow-left.png b/comp/src/gb.web.form/.public/gw-arrow-left.png new file mode 100644 index 0000000000000000000000000000000000000000..0fd95f5830e050ecc0af12332d70f323a64601ba GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6Y&=~YLp07OCrB_T94qL1$YS^M zG|zv@a}THQJEPz>ZHvWW(YF6Q`WKeY^eDEE)Ak4|>T6`2FSLizm6?HI?@qDLIu6#` Qf#xuHy85}Sb4q9e0Ht;%YXATM literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-arrow-right.png b/comp/src/gb.web.form/.public/gw-arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..936584c0ac5b4e55c06f4f328d98cbabf8077723 GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6EInNuLp07OCoIsmI9Sl<$TPpG znCHKw-s51UV+Av{yy6W0r0+PSz$cubbXJ~ajlv72rh6(90VYfgPYuOBe<=)I2Q-Ai M)78&qol`;+0MJ%-j5Jq2Iv(>E>T*UG$$$>)dV|ff0B!yeO17v{}5EqyLX=88LbO7hfJh&P zi-?V)Fd?}?93&=c?=L8B?luS7(RZh#5 zHzvwkGv&>x^7dT0K%rcsQ7%%cm+6!Xm8Gz~8-i20KGfa?!KqviXLd9}01Z3N3-_JM z_2Kq*1c!2c$|DK^&m#F zfsqXh@lV(MK!eBhXWu=hGQ~}77x^${eV*gaJ^ON+JyM=6eEqvJ`hGpbbBBWGKeJx- Pg1q4A>gTe~DWM4f=O9|0 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-table-more.gif b/comp/src/gb.web.form/.public/gw-table-more.gif new file mode 100644 index 0000000000000000000000000000000000000000..cad57b9e6459100c1761c989a9e1bdf0a84ac520 GIT binary patch literal 723 zcmZ?wbhEHb6ky zFk#7(B}a}N*|>4zy?gg2O`6o$*tl%jvQM8ry?*`r=g*(BXU~57_U(@!KW^Q+b^iSM zCr_Sy`}U0i1{D8u`?-b$J39ur8tEA@GXljG|4BI)r6!i7rYMwWmSiX-W+hhS zY~S+8;RKIf@w#hVu4|_`VKYG`AyI-gVcv>Nnf8hM4%|Al^3A2?z0cq5eZ4zEUtZ=E6w0k7GR^Oh05+pcfkrLia1{(;>V0gIq~dw;l}*3!`O?bg*0Xq*GoNU&OWNT6 zOSJ#Y#9~Vpsmj&Yx_4>K4N}Brk`W|4u5F5V<`Ou0hsUL)pitM7TVfws#l4+&n=$Qh zeBcXZ#@-XpPOv7=^ICcJs}xqhnL~_GDU5Adu_Z`1Zf z!3WHG(|uL?XP7X>>oS;`onEl-K@bNvC+S1XdIEIMicRlU70IXuZQ*@jy8icenS$xu g-@1;>Dr}Yc6{pK!!sZ;gehJn9BH0XUP!a(H0Loeu2mk;8 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-waiting.gif b/comp/src/gb.web.form/.public/gw-waiting.gif new file mode 100644 index 0000000000000000000000000000000000000000..84d7b8cfdb97a27d75c6d4ca3d2be9b372d2b538 GIT binary patch literal 6968 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/comp/src/gb.web.form/.public/message/error.png b/comp/src/gb.web.form/.public/message/error.png new file mode 100644 index 0000000000000000000000000000000000000000..0b792721102820c1043bddcd132b06d37f60c9e5 GIT binary patch literal 1173 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBgK_U|bU56XFWwUJVU>Y#F-GBIux5 zz-`ay8`gd|T|#bqdfs7I4!Qr0k|k@NL)NJMN(nmA9QijN4%Ccbo!2f^LDgT>T)j_Z&j*I7I;2cbvm+ z0(Aiu0A*oTKo~cjgKoP3-2k*M^tN*#P}$SOv|FwrH(f#>=;+^sNI3&#LAGCVfVdMR z3*_DbIR?T8k|7{n&OjG}89>uOt__741U_5n~1o;I6X%wIyT(M;Hk;TjkQ>Vx?S$+KCsrKlh&Z32kQa2?;e*O{@ z{VmEUDam;M?t+CH+NYvi&N4;DG9GvQocf7LzOd|{{Pc|6qU1?safup5QE_TPi5Z!3 zMdf9gTgwlZGv#joC43XM8~B}YxmDqROyM>Aj3<1SuCr(O?K^jY&E4X=j?0fNX992h zJaU?8ug<4?96wr)oWCFycto|!Ue{u}V*H*}60Lo1F9n5b>wYNkJ>p{Z*HiXr+;LK) z&gaexb>s8t--ToHPc8|&A%CkhDmLzp_=h6pN&d-K{~l-e**UHDUbe}?6A9M88u!&* z&iSdoYu=8Fyl1D0HJ(kG8Mu|vbLLM!+pZ};nPs!I)kA-8oBL6>%JARImj6r5*L`AB zfBNsctaJO1VC~1u^*7n}DrZOT&N+RmZEvl0oNdC-)7z>u&gOIHem4qtU*-D4tZ3Fr z=KufNE3IT~+GI8au+5p%xAD)d54$#0$E8m!-Y?Xz{qOUMnJ@M-Xe=(+7LvLaRK75H My85}Sb4q9e07bkNwg3PC literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/message/info.png b/comp/src/gb.web.form/.public/message/info.png new file mode 100644 index 0000000000000000000000000000000000000000..1e7aef08541644e81596a62e2f1b81feca067ad7 GIT binary patch literal 828 zcmV-C1H=4@P)I_nqFpo%jFEyq(>B!ysr5 zL%32cF<1a4#DnI5OA*UsTMf9hM`+&TS!D+np>bDS}`Ly~12eS~5OU+Y@dptF2 zNDauXG>E;~%LA~Wt!iQBgC;=kpx(_;VdIdkLH|@nfZR&GSyetwozP&N^-m`T*6m^~ zAAwOLw~PM9yj^rp1G38N2PZcHq*6?yLUVHELqB6HEu$Zr>j)T6#&;B74?fnG&&n#- z9>(1mps5}$d6Nf#Yf?=TfOX~ga8r%Zg9LzVM|EE10Wh0f2Ozp@s9hw5`kNv#uTW_G z4U7Q{aO@6$$Bulj06^GW?powP_<}C-fH`~~Fal&3d%o+xun6n~10c7F1i)b6CB|YU? zi1VPHfVChu2e6`>{>sjI?o#kYH-O)zZ~zm!0mcCi(1>38%m;8l@nTsQ{bkcIzyWUv zZe0KauEqF2xknd34-f;mM=_q8RvLR7ZG_`p#a&Qd<%v3@P7E+K24`ON`#kTG0D#(o z*}Un`72J-y3BO~xntUk8oF&}+MsU{sJ`DhnTHr*kA8?}Up645vN#_IqC+iIG)`x zmip+j`}5`-8AYTT)=jm7@}qPozVC$r03g???W|d|b6v&&dfXZxnk~!FXfZZ0JK11O zYS30Ng9+waed7&j=L^@_VF*{K`&bb`OU7R(7VfKJdH(^L;^7cA+3XAe0000A^FLgyBSZm=#8cRPyVAd#I6T|fuyV3@Rxq3;+-FGO(<*cPAy zAOq;m{{0}+fX;ww0djXUG|UIdcI;;8gg6yq%T9*g-5_^%0gc)TRu5!@5lAo4K(Jsh z(B54PT{{?7tpck7vmsJIHNAVlvOvLI41GXjAzJo=-4ABBZUvdsvlrxmK1gsu>;mcq zGIoMo1+fko&cFbKuz}8)vWMY6Fq9x#z-mAN4py-0DnlnQNP!Lmx*zOus4k#_Js^?p zqd;>wB8<-i&74#c%_kH(`N)UElR^Y}llJ{d&$6Q z?~k7YQ~&*qdDQJ8$@Ho=!doioPH{)w95;r*N_@!z$U*B!GqUPeSL(Q2xC#UVT zy0n}5%WK={ulLm4RIPQBe_J^$Eix#SPf=FneH|_2C~-8tMZ?usK~!_i291kZdqqU! zoIG?-dt5P4)^*(@9O4v`m6bMg`ZN8qX#q2Dmfw6>bA$i+pL3hvpEEY*Ztw_SWG?X~ zy+>ie=I36geN@zgD_x)dbSjy8Gf8#MnrYluwJLnq_AXepIo{=iH=l6e)Rq5?&d2Zc z;yQKnz^Ci$gn4)7J^5@dy|u#W&a>csC(pW^-JW-UVpP=aTRV^4P5w8po%5ajiJ$zZ zZ?hQ?ElL1^G=_4oVE9X z(Cofhj#EP`JvS@f;|VO5cy6m~BK`DkIKxB3DfPB~Kh3$Uh1$ZV)LT3LJlSDX!T$bY zS=ULD9f_@xr_L|DeU#zzjJ*sSI`Y>`NZjq%%kpEzCyxuBT^EfSWIsO^(?4bI z^<|BsF)O28#Rey*dp@2^<6Yl6u9+}}`}C>p^+&d~t_dis*Vp*tKmV_`#Qaq!#V*CW zT-TUv9eF9-@Lp6=jacnwr`G)C-`#Fay!1@?{^#|}e(n%^KQBdp|JAKu54Fm7{kDkN znHc)q{PK;d@)5Uwb$;G^d)5A_@og8*y|Vx7R+sV+Zr4pu7koMye9xPwE^%lLoS!I$zOx)w}E Wy}$KR&;D@-l{cQQelF{r5}E*r%ox@H literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/message/warning.png b/comp/src/gb.web.form/.public/message/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..c67b58f0561b278ec3918e9ff812506b4d9a2617 GIT binary patch literal 588 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sa{_!qT!Hlc{SMbR``p~>duM0h zjjg^wbakuOjjjGSw))-L;tdhG4&-k0yRpUh<~E=Lum7t;A>#Y@JAlNu`rX(FBtd%b z?hd`N&G-6N-|O20AR@Q7c>)>NxA|WK>Gi$2J@Eg329VTtKZvrMTl}trEqb=u7pNDc zeH+kr-?yQ<;z0Kqmjw9*GjLQ^h<*4ZF=M*s??1Xb_IP?QU%tkYnZf(~6@N#&!uOxb zE0$aQvtc|EdLlC7{v)nqt9)ByTr(LM7%(nLAxEPE1X=xM z)^;$x{(JxIvI{0B{U*=lK3;k?@!Ne#y?vP?f3H8XFz`5c=$-Z5&v5}0n8X#oPY-wa zZ>4Y|;9Xn&{KdB#s_pmKvXy^7lhATmuHyUS<5m^3HZ*BInP>T3f4%RAZ|6AD0?fYO z*PoWPA?D}1>++A*3G}yC{EDe=;{C!9|L%81Hrsvsa|(@;2RIjBUfL+^#wE2QfrHJY z;J}Fl?pujZHn8U!WU-lKG%89?IKa0!(Scc;rY3GO9gwi(0ShnB$pgY35|R~;yvpp= z%*~DEqCJBD_cbjv`mnymkawrJ0iWvyzKiqUi(h)ZC(Z0K?{8qRGkCiCxvXU3O!^8jo|80>LVTlx2eG*P~5UZ=JKxP%k$H&RZ z$;HLR`T6gvnO%fG+B z_4W1a?CjLk)YjJ4!otG6y}k4E^Xu#DzP`S@ySwr6@#*R5xw*OT@9*d5=eM`F?d|R6 z=H`Z|BY>nBgQg(0wYB8rlew;bVyw&*E^^=XKWSbewbASY7WaYp>UO`cb6C|Llq6!9TYVztD zAOTG+Z5Ytf;Q|Wi>gmIPo-Rm*fuRu$7#e^Cj7>}-z|7p-0wiFmXaxb*Hnw&k0ec5W z5O8vKadmTd=4N2<@bm%!Zy#Sj|A0Uq28N*E5D*9r3y+A5isofth=~;ifw=gD#H8eS uJ_d#qNhnB7OV0oq%AW}ZS=reF3=9CqRVq%t%^+X^0000tB2$STtgNhGjw@V^B~yqWudlDEsi|O(Dvh9fuCA`EtgQe4|EQ>_si~>IzrTr` zbE>MUW{fkasHlH`e{zVPf0L?khoND6mQikwO>2rwYKp0;sYYRbNo0fo0000000000 zsHmuupoXl*W&Hg7|NsBj@jiIx?&*8q$;=j-0 zztG~p(c+1nvAfLQma)#3vd)*X&X=;!ma@;2u+5UM%!iw;x60g+t;&(D%a5$ejjPFv zsmF+?#)YNDgQLZSm#ehL+l;Bli>Stkrp1P&#Dt^6fuX^Ep1^yYzj&IycbL6%mb-qE zr>()(f1ba5oW6OQymy$pa+SJnlDKS;wrh^HYL2yPkF{!!v~Px;p|{U!j_vQvg5gL)iZHNwfX_=-tZ!`}+j_0~GuM?BLqc$-}+w{-P5lDtSJ};s5{u>q$gG zR2b7^V1NKdCT1v$fkg!fR9V&7*dcrlPIV0pO)YI5T`q1OUOol}emx*x-@wqw*u>OK zKoBSZ"; + Print "
    " + 'Print "displayCalendar($("; JS(Me.Name & ":entry"); "),'dd/mm/yyyy'"; '"; If(bTime And Not bChooseDate, "dd/mm/yyyy hh:nn", "dd/mm/yyyy"); + 'Print ",$("; JS(Me.Name); "),false,false);"; '; If(bTime And Not bChooseDate, "true", "false"); ","; If(bSubmit, "true", "false"); ")\""; + 'Print "\">" + 'WebForm._AddJavascript("gw.load('calendar.css');") + 'WebForm._AddJavascript("gw.load('calendar.js');") + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "text" Then + Try Me.Text = vValue + If Error Then Me.Refresh + Else If sProp = "#popup" Then + FCalendar.Value = Date_Read() + FCalendar.ShowPopup(Me, Align.Right) + Endif + +End + + +Public Sub Clear() + + Date_Write(Null) + +End + +Private Function ReadOnly_Read() As Boolean + + Return $bReadOnly + +End + +Private Sub ReadOnly_Write(Value As Boolean) + + $bReadOnly = Value + Me._SetProperty("ReadOnly", Value) + +End + +Private Function Date_Read() As Date + + Return $dDate + +End + +Private Sub Date_Write(Value As Date) + + $dDate = Value + Me._SetProperty("Date", Value) + Raise Change + +End + +Private Function Text_Read() As String + + If $bDateTime Then + Return Format($dDate, gb.GeneralDate) + Else + Return Format($dDate, gb.ShortDate) + Endif + +End + +Private Sub Text_Write(Value As String) + + If IsDate(Value) Or If IsNull(Value) Then + Date_Write(Val(Value)) + Else + Error.Raise("Invalid date") + Endif + +End + +Private Function DateTime_Read() As Boolean + + Return $bDateTime + +End + +Private Sub DateTime_Write(Value As Boolean) + + $bDateTime = Value + Me._SetProperty("DateTime", Value) + +End diff --git a/comp/src/gb.web.form/.src/Calendar/WebDateChooser.class b/comp/src/gb.web.form/.src/Calendar/WebDateChooser.class new file mode 100644 index 00000000..5b0d8eff --- /dev/null +++ b/comp/src/gb.web.form/.src/Calendar/WebDateChooser.class @@ -0,0 +1,244 @@ +' Gambas class file + +''' This class implements a calendar that allows to choose a date. + +Export +Inherits WebContainer + +Public Const _IsContainer As Boolean = False +Public Const _Group As String = "Chooser" +Public Const _Properties As String = "*,Value,Border" +Public Const _DrawWith As String = "DateChooser" + +'' This event is raised when the selected date has changed. +Event Change +'' This event is raised when a date has been selected by the user. +Event Click + +'' Return or set the selected date. +Property Value As Date + +Private $dDate As Date +Private $dMonth As Date +Private $dStart As Date +Private $bLock As Boolean + +Private btnPrev As WebButton +Private btnNext As WebButton +Private cmbMonth As WebComboBox +Private txtYear As WebSpinBox +Private panCalendar As WebContainer +Private $dOldDate As Date + +'' Create a new WebDateChooser control. +Public Sub _new() + + Dim hTop As WebHBox + Dim I As Integer + Dim aList As String[] + + Me.Arrangement = Arrange.Vertical + + hTop = New WebHBox(Me) + + btnPrev = New WebButton(hTop) As "btnPrev" + btnPrev.Image = "gw-arrow-left.png" + btnPrev.Border = False + + btnNext = New WebButton(hTop) As "btnNext" + btnNext.Image = "gw-arrow-right.png" + btnNext.Border = False + + cmbMonth = New WebComboBox(hTop) As "cmbMonth" + cmbMonth.ReadOnly = True + cmbMonth.Expand = True + + txtYear = New WebSpinBox(hTop) As "txtYear" + txtYear.MinValue = 1600 + txtYear.MaxValue = 9999 + txtYear.Width = "6em" + txtYear.Border = False + + panCalendar = New WebContainer(Me) As "panCalendar" + + aList = New String[] + For I = 1 To 12 + aList.Add(Format(Date(1972, I, 1), "mmmm")) + Next + cmbMonth.List = aList + + $dDate = Date(Now) + SetDate() + +End + +Private Sub SetDate(Optional iYear As Integer, Optional iMonth As Integer, Optional iDay As Integer) + + Dim dMonth As Date + Dim dDate As Date + + If $bLock Then Return + $bLock = True + + ' If iYear = 0 And If iMonth = 0 And If iDay = 0 Then + ' $dDate = CheckDate($dDate) + ' Endif + + If iYear <= 0 Then iYear = Year($dDate) + If iMonth <= 0 Then iMonth = Month($dDate) + If iDay <= 0 Then iDay = Day($dDate) + + Do + Try dDate = Date(iYear, iMonth, iDay) + If Not Error Then Break + Dec iDay + If iDay < 28 Then + dDate = Null + Break + Endif + Loop + + 'If dDate And If CheckDate(dDate) <> dDate Then dDate = Null + + If dDate Then + $dDate = dDate + dMonth = Date(Year(dDate), Month(dDate), 1) + Else + dMonth = $dMonth + $dMonth = Null + Endif + + If dMonth <> $dMonth Then + + $dMonth = dMonth + + cmbMonth.Index = Month($dMonth) - 1 + txtYear.Value = Year($dMonth) + + Endif + + iDay = WeekDay($dMonth) - System.FirstDayOfWeek + If iDay < 1 Then iDay += 7 + $dStart = $dMonth - iDay + + ' If $dDate <> $dLast Then + ' $dLast = $dDate + ' 'GetParent()._Change + ' Endif + + 'dwgMonth.SetFocus + panCalendar.Refresh + + Me._SetProperty("Value", $dDate) + + $bLock = False + + If $dDate <> $dOldDate Then + $dOldDate = $dDate + Raise Change + Endif + +End + +Public Sub panCalendar_Render() + + Dim Y As Integer + Dim X As Integer + Dim dDate As Date + Dim sClass As String + Dim dNow As Date + + Print "" + + Print "" + For X = 0 To 6 + Print "" + Next + Print "" + + dDate = Date($dStart) + dNow = Date(Now) + + For Y = 1 To 5 + Print "" + For X = 0 To 6 + Print " Month($dDate) Then sClass &= " gw-date-disabled" + sClass = Trim(sClass) + If sClass Then Print " class=\""; sClass; "\""; + If Month(dDate) = Month($dDate) Then + Print Me._GetUpdateJS("onclick", "#date", JS(CStr(dDate))); + Endif + Print ">"; Day(dDate); "" + Inc dDate + Next + Print "" + Next + + Print "
    "; Html(Format(CDate($dStart + X), "ddd")); "
    " + +End + +Public Sub cmbMonth_Click() + + SetDate(0, cmbMonth.Index + 1) + +End + +Public Sub txtYear_Change() + + SetDate(txtYear.Value, 0) + +End + +Public Sub btnPrev_Click() + + If Month($dDate) = 1 Then + SetDate(Year($dDate) - 1, 12) + Else + SetDate(0, Month($dDate) - 1) + Endif + +End + +Public Sub btnNext_Click() + + If Month($dDate) = 12 Then + SetDate(Year($dDate) + 1, 1) + Else + SetDate(0, Month($dDate) + 1) + Endif + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Select Case sProp + + Case "#date" + $dDate = CDate(vValue) + SetDate() + Raise Click + + Default + Super._UpdateProperty(sProp, vValue) + + End Select + +End + + +Private Function Value_Read() As Date + + Return $dDate + +End + +Private Sub Value_Write(Value As Date) + + SetDate(Year(Value), Month(Value), Day(Value)) + +End diff --git a/comp/src/gb.web.form/.src/Color.class b/comp/src/gb.web.form/.src/Color.class new file mode 100644 index 00000000..cb8dace9 --- /dev/null +++ b/comp/src/gb.web.form/.src/Color.class @@ -0,0 +1,44 @@ +' Gambas class file + +''' This class provides constant for pre-defined colors and color-related methods + +Export Optional + +Public Const Default As Integer = -1 +Public Const Black As Integer = &H000000& +Public Const White As Integer = &HFFFFFF& +Public Const Gray As Integer = &H808080& +Public Const LightGray As Integer = &HC0C0C0& +Public Const DarkGray As Integer = &H404040& +Public Const Blue As Integer = &HFF& +Public Const Green As Integer = &HFF00& +Public Const Red As Integer = &HFF0000& +Public Const Yellow As Integer = &HFFFF00& +Public Const Magenta As Integer = &HFF00FF& +Public Const Cyan As Integer = &H00FFFF& +Public Const Orange As Integer = &HFF7F00& +Public Const Violet As Integer = &H7F00FF& +Public Const RoyalBlue As Integer = &H007FFF& +Public Const Pink As Integer = &HFF80FF& + +'' Change the alpha component of a color and return it. + +Static Public Sub SetAlpha(Color As Integer, Alpha As Integer) As Integer + + Return Color Or Lsl(Alpha, 24) + +End + +'' Returns a color value from its red, green, blue and alpha components. +'' +'' If ~Alpha~ is not specified, then it is assumed to be zero, i.e. you get a fully opaque color. +'' +'' [[ info +'' In Gambas, the alpha value of fully opaque colors is zero, and the alpha value of fully transparent colors is 255. +'' ]] + +Static Public Sub RGB((Red) As Integer, (Green) As Integer, (Blue) As Integer, Optional Alpha As Integer) As Integer + + Return Blue Or Lsl(Green, 8) Or Lsl(Red, 16) Or Lsl(Alpha, 24) + +End diff --git a/comp/src/gb.web.form/.src/Header.class b/comp/src/gb.web.form/.src/Header.class new file mode 100644 index 00000000..7c6ebdb4 --- /dev/null +++ b/comp/src/gb.web.form/.src/Header.class @@ -0,0 +1,36 @@ +' Gambas class file + +Static Public Form As WebForm + +Private Sub GetTitle() As String + + If Form And If Form.Title Then + Print Html(Form.Title); + Else + Print Html(Application.Title); + Endif + +End + +Private Sub GetJavascript() As String + + Dim aFiles As String[] + + aFiles = Form._GetJavascriptFiles() + If aFiles And If aFiles.Count Then Return ":" & aFiles.Join(":") + +End + +Private Sub PrintJavascriptExternFiles() + + Dim aFiles As String[] + Dim sFile As String + + aFiles = Form._GetJavascriptExternFiles() + If aFiles Then + For Each sFile In aFiles + Print "" + Next + Endif + +End diff --git a/comp/src/gb.web.form/.src/Header.webpage b/comp/src/gb.web.form/.src/Header.webpage new file mode 100644 index 00000000..ea8157f6 --- /dev/null +++ b/comp/src/gb.web.form/.src/Header.webpage @@ -0,0 +1,12 @@ + + + + + + + "> + + + <%PrintJavascriptExternFiles()%> + <%=GetTitle()%> + diff --git a/comp/src/gb.web.form/.src/Main.module b/comp/src/gb.web.form/.src/Main.module new file mode 100644 index 00000000..0ef33860 --- /dev/null +++ b/comp/src/gb.web.form/.src/Main.module @@ -0,0 +1,30 @@ +' Gambas module file + +'Public CurrentForm As WebForm + +Public Sub Main() + + 'Debug Base64(File.Load(".public/waiting.gif")) + 'Debug WebForm._GetColor(Color.Gray) + Debug Base64$(File.Load("shadow.png")) + +End + +Public Sub DumpSession() + + Dim sKey As String + + Debug String$(80, "-") + + If Session.Id Then + + For Each sKey In Session.Keys + Error sKey; ": "; JSON.Encode(Session[sKey]) + Next + + Endif + + Debug String$(80, "-") + +End + diff --git a/comp/src/gb.web.form/.src/Message/FMessage.class b/comp/src/gb.web.form/.src/Message/FMessage.class new file mode 100644 index 00000000..cacaa6ab --- /dev/null +++ b/comp/src/gb.web.form/.src/Message/FMessage.class @@ -0,0 +1,94 @@ +' Gambas class file + +Public Sub SetIcon(sIcon As String) + + imgIcon.Image = "message" &/ sIcon & ".png" + +End + +Public Sub SetText(sText As String) + + txtMessage.Text = sText + +End + +Public Sub SetButton(sButton1 As String, Optional sButton2 As String, sButton3 As String) + + If sButton1 Then + btnButton1.Text = sButton1 + Endif + + If sButton2 Then + btnButton2.Text = sButton2 + btnButton2.Show + Endif + + If sButton3 Then + btnButton3.Text = sButton3 + btnButton3.Show + Endif + +End + +Public Sub _new() + + Dim hCtrl As WebButton + + Me.Style["min-width"] = "24em" + Me.Style["min-height"] = "12em" + + For Each hCtrl In [btnButton1, btnButton2, btnButton3] + hCtrl.Style["min-width"] = "8em" + Next + + imgIcon.Style["margin-right"] = "1em" + +End + +Private Sub HandleClick() + + Dim hForm As WebForm + Dim sName As String + Dim hCtrl As WebControl + Dim sText As String + + sText = Last.Text + sName = Me["#message"] + hCtrl = WebControl.FromName(sName) + + If hCtrl Then + If Object.CanRaise(hCtrl, "Message") Then + Object.Raise(hCtrl, "Message", [hCtrl, sText]) + Else + hForm = WebControl.FromId(Me._Window).Form + Object.Raise(hForm, "Message", [hCtrl, sText]) + Endif + Endif + + Me.Close + +End + +Public Sub btnButton1_Click() + + HandleClick() + +End + +Public Sub btnButton2_Click() + + HandleClick() + +End + +Public Sub btnButton3_Click() + + HandleClick() + +End + +Public Sub WebForm_Open() + + btnButton1.SetFocus + +End diff --git a/comp/src/gb.web.form/.src/Message/FMessage.webform b/comp/src/gb.web.form/.src/Message/FMessage.webform new file mode 100644 index 00000000..bceee02e --- /dev/null +++ b/comp/src/gb.web.form/.src/Message/FMessage.webform @@ -0,0 +1,43 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,77,36) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebContainer1 WebHBox + #MoveScaled(1,1,75,19) + Expand = True + Spacing = True + { WebContainer2 WebVBox + #MoveScaled(1,1,17,17) + { imgIcon WebImage + #MoveScaled(1,1,15,10) + } + } + { txtMessage WebHtml + #MoveScaled(19,1,33,17) + Expand = True + } + } + { panButton WebHBox + #MoveScaled(1,21,75,7) + Spacing = True + { WebContainer3 WebContainer + #MoveScaled(1,1,5,5) + Expand = True + } + { btnButton3 WebButton + #MoveScaled(7,1,17,5) + Visible = False + } + { btnButton2 WebButton + #MoveScaled(25,1,17,5) + Visible = False + } + { btnButton1 WebButton + #MoveScaled(43,1,17,5) + Text = ("OK") + } + } +} diff --git a/comp/src/gb.web.form/.src/Message/Message.class b/comp/src/gb.web.form/.src/Message/Message.class new file mode 100644 index 00000000..9c27c1ff --- /dev/null +++ b/comp/src/gb.web.form/.src/Message/Message.class @@ -0,0 +1,61 @@ +' Gambas class file + +Export + +Static Public Name As String + +Static Private Sub Open(sIcon As String, sTitle As String, sText As String, Optional sButton1 As String, sButton2 As String, sButton3 As String) + + Dim hMessage As FMessage + Dim sName As String + + hMessage = New FMessage + hMessage.Title = sTitle + hMessage.SetIcon(sIcon) + hMessage.SetText(sText) + hMessage.SetButton(sButton1, sButton2, sButton3) + + If Name Then + sName = Name + Else + Try sName = Last.Name + Endif + + hMessage["#message"] = sName + + hMessage.ShowModal(Null) + +End + + +Static Public Sub _call(Text As String, Optional Button As String) + + Open("info", ("Information"), Text, Button) + +End + +Static Public Sub Info(Text As String, Optional Button As String) + + _call(Text, Button) + +End + +Static Public Sub Warning(Text As String, Optional Button1 As String, Button2 As String, Button3 As String) + + Open("warning", ("Warning"), Text, Button1, Button2, Button3) + +End + +Static Public Sub Error(Text As String, Optional Button As String) + + Open("error", ("Error"), Text, Button) + +End + +Static Public Sub Question(Text As String, Optional Button1 As String, Button2 As String, Button3 As String) + + Open("question", ("Question"), Text, Button1, Button2, Button3) + +End + + diff --git a/comp/src/gb.web.form/.src/Scroll.class b/comp/src/gb.web.form/.src/Scroll.class new file mode 100644 index 00000000..6116dea0 --- /dev/null +++ b/comp/src/gb.web.form/.src/Scroll.class @@ -0,0 +1,14 @@ +' Gambas class file + +''' Constants used by the ScrollBar property of many controls. + +Export + +'' No scrollbar is visible. +Public Const None As Integer = 0 +'' The horizontal scrollbar is visible if needed. +Public Const Horizontal As Integer = 1 +'' The vertical scrollbar is visible if needed. +Public Const Vertical As Integer = 2 +'' Both horizontal and vertical scrollbars are visible if needed. +Public Const Both As Integer = 3 diff --git a/comp/src/gb.web.form/.src/Select.class b/comp/src/gb.web.form/.src/Select.class new file mode 100644 index 00000000..9fe86d76 --- /dev/null +++ b/comp/src/gb.web.form/.src/Select.class @@ -0,0 +1,12 @@ +' Gambas class file + +''' Constants used for the selection mode of WebTable control. + +Export + +'' No selection is possible. +Public Const None As Integer = 0 +'' Only one element can be selected. +Public Const Single As Integer = 1 +'' Many elements can be selected at the same time. +Public Const Multiple As Integer = 2 diff --git a/comp/src/gb.web.form/.src/Test/FHello.class b/comp/src/gb.web.form/.src/Test/FHello.class new file mode 100644 index 00000000..4e8e8cbe --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FHello.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub WebButton1_Click() + + Me.Close + +End diff --git a/comp/src/gb.web.form/.src/Test/FHello.webform b/comp/src/gb.web.form/.src/Test/FHello.webform new file mode 100644 index 00000000..84568cf8 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FHello.webform @@ -0,0 +1,31 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,82,45) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Title = ("This is a very important message") + Resizable = True + { WebHtml1 WebHtml + #MoveScaled(1,1,80,11) + Text = ("

    Welcome to the gb.web.form component!

    \n

    This component aims at making web application as easy as making desktop applications.

    \n

    This goal is difficult to achieve, but I hope to succeed!

    ") + } + { WebContainer1 WebContainer + #MoveScaled(1,12,80,20) + Expand = True + } + { WebContainer2 WebContainer + #MoveScaled(1,32,80,8) + Arrangement = Arrange.Horizontal + { WebContainer3 WebContainer + #MoveScaled(1,1,57,6) + Expand = True + } + { WebButton1 WebButton + #MoveScaled(58,1,16,6) + Width = "8em" + Text = ("OK") + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/FTestWebUploader.class b/comp/src/gb.web.form/.src/Test/FTestWebUploader.class new file mode 100644 index 00000000..5a74d550 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FTestWebUploader.class @@ -0,0 +1,16 @@ +' Gambas class file + +'Export + + +Public Sub WebUploader1_Upload() + + WebImage1.Image = WebUploader1.Path + +End + +Public Sub WebForm_Open() + + WebForm.Debug = True + +End diff --git a/comp/src/gb.web.form/.src/Test/FTestWebUploader.webform b/comp/src/gb.web.form/.src/Test/FTestWebUploader.webform new file mode 100644 index 00000000..8ad84e1c --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FTestWebUploader.webform @@ -0,0 +1,18 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,64,91) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebUploader1 WebUploader + #MoveScaled(1,1,62,8) + } + { WebContainer1 WebContainer + #MoveScaled(1,10,62,35) + { WebImage1 WebImage + #MoveScaled(0,0,25,24) + Border = True + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform1.class b/comp/src/gb.web.form/.src/Test/Webform1.class new file mode 100644 index 00000000..170123b2 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform1.class @@ -0,0 +1,83 @@ +' Gambas class file + +Public Sub _new() + + WebContainer2.Style["background-color"] = "#BFDFFF" + WebHTML2.Style["font-size"] = "200%" + WebHTML2.Style["font-weight"] = "bold" + +End + +Public Sub WebButton3_Click() + + Last.Text = "Gambas rules!" + +End + +Public Sub WebForm_Open() + + WebForm.Print("Form is opened") + +End + +Public Sub WebButton2_Click() + + WebButton2.Parent.Border = Not WebButton2.Parent.Border + +End + + +Public Sub WebButton1_Click() + + Randomize + WebHTML2.Style["font-size"] = "100%" + WebHTML2.Html &= "Hello world!" + +End + +Public Sub WebForm_Event() + + WebContainer2.Refresh + +End + +Public Sub WebButton4_Click() + + WebButton4.Text = CStr(CInt(WebButton4.Text) + 1) + +End + +Public Sub WebButton5_Click() + + WebHTML2.Style["font-size"] = "200%" + WebHTML2.Html = "Header" + +End + +Public Sub WebButton6_Click() + + WebButton4.Enabled = Not WebButton4.Enabled + +End + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub WebTextBox1_Change() + + Dim sText As String + + sText = Last.Text + + If sText Then + WebHtml1.Text = Html("You have entered: « " & sText & " ».") + WebHtml1.Visible = True + Else + WebHtml1.Text = Html("You have entered nothing.") + WebHtml1.Visible = False + Endif + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform1.webform b/comp/src/gb.web.form/.src/Test/Webform1.webform new file mode 100644 index 00000000..dc71ab5e --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform1.webform @@ -0,0 +1,147 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,98,109) + Height = "100%" + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Title = ("gb.web.form test") + { WebMenu1 WebMenu + Text = ("WebMenu1") + { WebMenu2 WebMenu + Text = ("WebMenu2") + } + { WebMenu3 WebMenu + Text = ("WebMenu3") + } + } + { WebMenu4 WebMenu + Text = ("WebMenu4") + } + { WebContainer4 WebContainer + #MoveScaled(1,1,96,6) + Arrangement = Arrange.Horizontal + Margin = True + Border = True + { WebHTML2 WebHTML + #MoveScaled(1,1,27,4) + Expand = True + Text = ("Header") + } + { btnClose WebButton + #MoveScaled(28,1,13,4) + Border = False + Image = "gw-close.png" + } + } + { WebContainer3 WebContainer + #MoveScaled(1,8,96,48) + Expand = True + Arrangement = Arrange.Horizontal + Spacing = True + { WebContainer1 WebContainer + #MoveScaled(1,1,23,46) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Border = True + { WebButton1 WebButton + #MoveScaled(1,1,21,5) + Text = ("Hello world!") + } + { WebButton5 WebButton + #MoveScaled(1,7,21,5) + Text = ("Reset text") + } + { WebButton3 WebButton + #MoveScaled(1,13,21,5) + Text = ("Change text") + } + { WebButton4 WebButton + #MoveScaled(1,19,21,5) + Text = ("0") + } + { WebButton2 WebButton + #MoveScaled(1,25,21,5) + Text = ("Border") + } + { WebButton6 WebButton + #MoveScaled(1,31,21,5) + Text = ("Toggle enabled") + } + } + { WebContainer5 WebContainer + #MoveScaled(25,1,65,46) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Border = True + { WebTextBox1 WebTextBox + #MoveScaled(1,1,63,5) + Height = "2em" + } + { WebHtml1 WebHtml + #MoveScaled(1,7,63,5) + Visible = False + } + { WebContainer2 WebContainer + #MoveScaled(1,13,63,13) + Expand = True + Margin = True + Border = True + } + { WebHtml3 WebHtml + #MoveScaled(1,27,63,8) + Text = ("Absolute !") + } + } + } + { WebContainer6 WebContainer + #MoveScaled(1,57,96,24) + Arrangement = Arrange.Row + Spacing = True + Border = True + { WebButton7 WebButton + #MoveScaled(1,1,10,10) + Width = "8em" + Text = ("B1") + } + { WebButton11 WebButton + #MoveScaled(12,1,10,10) + Width = "8em" + Text = ("B5") + } + { WebButton13 WebButton + #MoveScaled(23,1,10,10) + Width = "8em" + Text = ("B7") + } + { WebButton12 WebButton + #MoveScaled(34,1,10,10) + Width = "8em" + Text = ("B6") + } + { WebButton8 WebButton + #MoveScaled(45,1,10,10) + Width = "8em" + Text = ("B2") + } + { WebButton9 WebButton + #MoveScaled(56,1,10,10) + Width = "8em" + Text = ("B3") + } + { WebButton10 WebButton + #MoveScaled(67,1,10,10) + Width = "8em" + Text = ("B4") + } + { WebButton14 WebButton + #MoveScaled(78,1,10,10) + Width = "8em" + Text = ("B8") + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform2.class b/comp/src/gb.web.form/.src/Test/Webform2.class new file mode 100644 index 00000000..b347bdae --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform2.class @@ -0,0 +1,146 @@ +' Gambas class file + +Public Sub _new() + + WebLabel1.Style["font-size"] = "140%" + WebLabel1.Style["padding-right"] = "1em" + +End + + +Public Sub WebButton1_Click() + + WebTabPanel1.Index = 1 + +End + +Public Sub WebContainer2_Render() + + Dim sKey As String + Dim bTable As Boolean + Dim bDark As Boolean + + If Session.Id Then + + For Each sKey In Session.Keys + If Not bTable Then + Print "" + bTable = True + Endif + If bDark Then + Print "" + Else + Print "" + Endif + Print ""; + Print "" + Print "" + bDark = Not bDark + Next + + If bTable Then Print "
    "; Html(sKey); ":"; Html(JSON.Encode(Session[sKey])); "
    " + + Endif + +End + + +Public Sub WebForm_Event() + + WebContainer2.Refresh + +End + +Public Sub WebTabPanel1_Click() + + 'If WebTabPanel1.Index = 0 Then WebTextBox1.SetFocus + +End + +Public Sub WebButton4_Click() + + WebComboBox1.Add("Item #" & CStr(WebComboBox1.Count)) + +End + +Public Sub WebButton5_Click() + + WebComboBox1.Clear + +End + +Public Sub WebTimer1_Timer() + + WebLabel1.Text = Format(Now, gb.LongTime) + +End + +Public Sub WebButton2_Click() + + Dim hForm As WebForm + + Randomize + + hForm = New FHello + hForm.Show() + hForm.Move(Rand(16, 24), Rand(4, 16), 32, 32) + +Catch + + Me.Print(Error.Text & ": " & Error.Backtrace.Join(" ")) + +End + +Public Sub WebButton6_Click() + + WebTimer1.Enabled = Not WebTimer1.Enabled + +End + +Public Sub WebSlider1_Change() + + WebSpinBox1.Value = WebSlider1.Value + +End + +Public Sub WebSpinBox1_Change() + + WebSlider1.Value = WebSpinBox1.Value + +End + +Public Sub WebButton3_Click() + + Message("Hello world!") + +End + +Public Sub WebButton7_Click() + + Message.Warning("File has been modified.

    All changes will be lost!", "Cancel", "Delete") + +End + +Public Sub WebMenu3_Click(Target As WebControl) + + Message("You have clicked on '" & Target.Name & "' control.") + +End + +Public Sub WebForm_Message(Source As WebControl, Action As String) + + 'Debug "Action '"; Action; "' on '"; Source.Name; "'" + +End + +Public Sub WebMenu4_Click(Target As WebControl) + + Message("You have clicked on '" & Target.Name & "' control.") + +End + +Public Sub WebMenuItem6_Click() + + Message.Error("Quit !") + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform2.webform b/comp/src/gb.web.form/.src/Test/Webform2.webform new file mode 100644 index 00000000..4491d3ee --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform2.webform @@ -0,0 +1,275 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,138,122) + Height = "100%" + Arrangement = Arrange.Vertical + Title = ("This is webform2") + { WebMenuBar1 WebMenuBar + #MoveScaled(1,1,136,51) + Spacing = True + { WebMenu3 WebMenu + #MoveScaled(1,1,32,49) + Text = ("File") + { WebMenuItem7 WebMenuItem + #MoveScaled(1,1,30,4) + Text = ("New") & "..." + Image = "new.png" + Shortcut = "CTRL+N" + } + { WebMenuItem8 WebMenuItem + #MoveScaled(1,5,30,4) + Text = ("Open") & "..." + Image = "open.png" + Shortcut = "CTRL+O" + } + { WebMenu4 WebMenu + #MoveScaled(1,9,30,26) + Text = ("Project") + { WebMenuItem9 WebMenuItem + #MoveScaled(1,1,28,4) + Text = ("Compile") + Image = "new.png" + Shortcut = "F7" + } + { WebMenuItem10 WebMenuItem + #MoveScaled(1,5,28,4) + Text = ("Compile all") + Image = "new.png" + } + { WebSeparator4 WebSeparator + #MoveScaled(1,9,28,2) + } + { WebMenuItem11 WebMenuItem + #MoveScaled(1,11,28,4) + Text = ("Translate") + Shortcut = "CTRL+T" + } + { WebSeparator1 WebSeparator + #MoveScaled(1,15,28,2) + } + { WebMenuItem1 WebMenuItem + #MoveScaled(1,17,28,4) + Text = ("Teleport") + } + } + { WebSeparator5 WebSeparator + #MoveScaled(1,35,30,2) + } + { WebMenuItem6 WebMenuItem + #MoveScaled(1,37,30,4) + Text = ("Quit") + } + } + { WebMenu1 WebMenu + #MoveScaled(34,1,30,49) + Text = ("Project") + { WebMenuItem2 WebMenuItem + #MoveScaled(1,1,28,4) + Text = ("Compile") + Image = "new.png" + Shortcut = "F7" + } + { WebMenuItem3 WebMenuItem + #MoveScaled(1,5,28,4) + Text = ("Compile all") + Image = "new.png" + } + { WebSeparator2 WebSeparator + #MoveScaled(1,9,28,2) + } + { WebMenuItem4 WebMenuItem + #MoveScaled(1,11,28,4) + Text = ("Translate") + Shortcut = "CTRL+T" + } + { WebSeparator6 WebSeparator + #MoveScaled(1,15,28,2) + } + { WebMenuItem5 WebMenuItem + #MoveScaled(1,17,28,5) + Text = ("Teleport") + } + } + } + { WebContainer1 WebHBox + #MoveScaled(1,52,136,6) + Spacing = True + { WebButton1 WebButton + #MoveScaled(1,1,13,4) + Text = ("A button") + } + { WebButton2 WebButton + #MoveScaled(15,1,13,4) + Border = False + Text = ("Add window") + } + { WebButton7 WebButton + #MoveScaled(29,1,13,4) + Border = False + Text = ("Warning") + } + { WebButton6 WebButton + #MoveScaled(43,1,13,4) + Border = False + Text = ("Toggle timer") + } + { WebButton8 WebButton + #MoveScaled(57,1,13,4) + Visible = False + Text = ("Hidden") + } + { WebContainer6 WebContainer + #MoveScaled(71,1,42,4) + Expand = True + } + { WebLabel1 WebLabel + #MoveScaled(114,1,15,4) + } + } + { WebContainer9 WebContainer + #MoveScaled(1,58,136,88) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + { WebTabPanel1 WebTabPanel + #MoveScaled(1,1,134,82) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Count = 3 + Index = 0 + Text = ("General") + { WebTextBox1 WebTextBox + #MoveScaled(1,1,131.75,4) + Text = ("Hello !") + } + { WebCheckBox1 WebCheckBox + #MoveScaled(1,6,131.75,4) + Text = ("Check and uncheck me!") + } + { WebContainer5 WebHBox + #MoveScaled(1,11,131.75,6) + Spacing = True + { WebComboBox2 WebComboBox + #MoveScaled(1,1,22,4) + List = [("Élément 1"), ("Élément 2"), ("Élément 3"), ("Élément 4")] + } + { WebTextBox2 WebTextBox + #MoveScaled(24,1,15,4) + } + { WebDateBox2 WebDateBox + #MoveScaled(40,1,24,4) + } + { WebSpinBox1 WebSpinBox + #MoveScaled(65,1,13,4) + } + { WebSlider1 WebSlider + #MoveScaled(79,1,49,4) + Expand = True + Border = False + } + } + { WebContainer3 WebHBox + #MoveScaled(1,18,131.75,6) + Spacing = True + { WebDateBox1 WebDateBox + #MoveScaled(1,1,24,4) + } + { WebComboBox1 WebComboBox + #MoveScaled(26,1,65.75,4) + Width = "30em" + List = [("Élément 1"), ("Élément 2"), ("Élément 3"), ("Élément 4")] + ReadOnly = False + } + { WebButton4 WebButton + #MoveScaled(92.75,1,19,4) + Text = ("New element") + } + { WebButton5 WebButton + #MoveScaled(112.75,1,19,4) + Text = ("Clear") + } + { WebSeparator3 WebSeparator + #MoveScaled(132.75,1,11,4) + Ignore = True + } + } + { WebSeparator7 WebSeparator + #MoveScaled(1,25,131.75,2) + Height = "1em" + } + { WebContainer7 WebExpander + #MoveScaled(1,28,131.75,32) + Background = &HA0FF7F00& + Arrangement = Arrange.Horizontal + Spacing = True + Indent = True + Text = ("This is an expander container") + Hidden = True + { WebContainer4 WebVBox + #MoveScaled(1.125,1.125,64,25.75) + Expand = True + Background = &H80FFFF00& + Margin = True + { WebRadioButton1 WebRadioButton + #MoveScaled(1,1,62,4) + Text = ("Alpha") + } + { WebRadioButton2 WebRadioButton + #MoveScaled(1,5,62,4) + Text = ("Beta") + Value = True + } + { WebRadioButton3 WebRadioButton + #MoveScaled(1,9,62,4) + Text = ("Gamma") + } + { WebCheckBox2 WebCheckBox + #MoveScaled(1,13,62,4) + Text = ("Gambas Almost Means Basic!") + } + } + { WebContainer8 WebVBox + #MoveScaled(66.125,1.125,58.375,25.75) + Expand = True + Spacing = True + { WebLabel2 WebLabel + #MoveScaled(1,1,56.375,4) + Text = ("Please enter some text:") + } + { WebTextArea1 WebTextArea + #MoveScaled(1,6,56.375,24) + Height = "20em" + Expand = True + Wrap = True + } + } + } + { WebHBox2 WebHBox + #MoveScaled(1,61,131.75,11) + { WebImage1 WebImage + #MoveScaled(1,1,9,9) + Width = "8em" + Image = "message/close.svg" + } + } + Index = 1 + Text = ("Options") + { WebButton3 WebButton + #MoveScaled(1,1,131.75,8) + Text = ("Test") + } + Index = 2 + Text = ("Application state") + { WebContainer2 WebContainer + #MoveScaled(1,1,131.75,32) + } + Index = 0 + } + } + { WebTimer1 WebTimer + #MoveScaled(70,8) + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform3.class b/comp/src/gb.web.form/.src/Test/Webform3.class new file mode 100644 index 00000000..46ee490b --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform3.class @@ -0,0 +1,75 @@ +' Gambas class file + + +Public Sub _new() + + Dim I As Integer + + WebForm.Debug = True + + WebTable1.Columns.Count = 12 + For I = 0 To WebTable1.Columns.Count - 1 + WebTable1.Columns[I].Text = Chr$(65 + I) + WebTable1.Columns[I].Wrap = False + WebTable1.Columns[I].Sortable = I Mod 3 + Next + 'WebTable1.Columns[3].Expand = True + + WebTable1.Count = 1000 + 'WebTable1.Select(2, 3) + +End + +Public Sub WebForm_Open() + +End + + +Public Sub WebTable1_Data(Row As Integer, Column As Integer, Data As WebTableData) + + Data.Text = "This is the " & CStr(Row) & ":" & CStr(Column) & " cell" + 'Data.Text = 1 / 0 + +End + +Public Sub WebContainer1_Render() + + Dim sKey As String + Dim bTable As Boolean + Dim bDark As Boolean + + If Session.Id Then + + For Each sKey In Session.Keys + If Not bTable Then + Print "" + bTable = True + Endif + If bDark Then + Print "" + Else + Print "" + Endif + Print ""; + Print "" + Print "" + bDark = Not bDark + Next + + If bTable Then Print "
    "; Html(sKey); ":"; Html(JSON.Encode(Session[sKey])); "
    " + + Endif + +End + +Public Sub WebButton1_Click() + + Message.Warning("Ceci est une table avec " & WebTable1.Count & " éléments !") + +End + +Public Sub WebTable1_Select() + + WebForm.Print("Select: " & WebTable1.Selection.Count) + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform3.webform b/comp/src/gb.web.form/.src/Test/Webform3.webform new file mode 100644 index 00000000..738fed7a --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform3.webform @@ -0,0 +1,41 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,106,85) + Height = "100%" + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebTabPanel1 WebTabPanel + #MoveScaled(1,1,104,69) + Height = "32em" + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Count = 2 + Index = 0 + Text = ("Tab 1") + { WebButton1 WebButton + #MoveScaled(1,1,101.7143,4) + Text = ("Message") + } + { WebTable1 WebTable + #MoveScaled(1,6,101.7143,32) + Expand = True + Mode = Select.Multiple + Sortable = True + } + Index = 1 + Text = ("Tab 2") + { WebContainer1 WebContainer + #MoveScaled(1,1,101.7143,32) + } + Index = 0 + } + { WebHBox1 WebHBox + #MoveScaled(1,71,104,9) + Height = "6em" + Border = True + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform4.class b/comp/src/gb.web.form/.src/Test/Webform4.class new file mode 100644 index 00000000..02645880 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform4.class @@ -0,0 +1,41 @@ +' Gambas class file + +Public Sub _new() + + WebForm.Debug = True + +End + + +Public Sub WebUploadArea1_Progress() + + btnSelectFile.Enabled = False + WebProgressBar1.Value = WebUploadArea1.Progress + +End + +Public Sub WebUploadArea1_Upload() + + Dim sPath As String + + btnSelectFile.Enabled = True + + WebForm.Print("upload") + For Each sPath In Request.Files + Message(Request.Files.Key & ": " & sPath) + Next + +End + + +Public Sub WebButton1_Click() + + Webform5.Show + +End + +Public Sub WebButton2_Click() + + Webform5.Hide + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform4.webform b/comp/src/gb.web.form/.src/Test/Webform4.webform new file mode 100644 index 00000000..93e1d329 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform4.webform @@ -0,0 +1,45 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,107,26) + Arrangement = Arrange.Horizontal + Margin = True + Spacing = True + { WebContainer3 WebVBox + #MoveScaled(1,1,60,24) + Spacing = True + { WebContainer2 WebHBox + #MoveScaled(1,1,58,8) + { WebUploadArea1 WebUploadArea + #MoveScaled(1,1,21,6) + { btnSelectFile WebButton + #MoveScaled(1,1,16,4) + Text = ("Select file") & "..." + } + } + } + { WebProgressBar1 WebProgressBar + #MoveScaled(1,10,58,4) + } + } + { WebContainer1 WebContainer + #MoveScaled(62,1,27,24) + Width = "10em" + Arrangement = Arrange.Vertical + { WebDateBox1 WebDateBox + #MoveScaled(1,1,25,4) + } + { WebButton1 WebButton + #MoveScaled(1,5,25,4) + Text = ("Show") + } + { WebButton2 WebButton + #MoveScaled(1,9,25,4) + Text = ("Hide") + } + } + { WebTimer1 WebTimer + #MoveScaled(93,7) + Delay = 500 + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform5.class b/comp/src/gb.web.form/.src/Test/Webform5.class new file mode 100644 index 00000000..3dab0df7 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform5.class @@ -0,0 +1,16 @@ +' Gambas class file + + +Public Sub WebForm_Open() + + Dim M As Integer + Dim aMonth As String[] + + aMonth = New String[] + + For M = 1 To 12 + aMonth.Add(Format(Date(1972, M, 1), "mmmm")) + Next + cmbMonth.List = aMonth + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform5.webform b/comp/src/gb.web.form/.src/Test/Webform5.webform new file mode 100644 index 00000000..b634e4c6 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform5.webform @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,64,29) + Arrangement = Arrange.Vertical + Spacing = True + Persistent = True + { WebHBox1 WebHBox + #MoveScaled(1,1,62,7) + { WebButton1 WebButton + #MoveScaled(1,1,11,5) + Image = "gw-arrow-left.png" + } + { cmbMonth WebComboBox + #MoveScaled(12,1,24,5) + } + { WebButton2 WebButton + #MoveScaled(36,1,16,5) + Image = "gw-arrow-right.png" + } + } + { WebContainer1 WebContainer + #MoveScaled(1,9,62,18) + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform6.class b/comp/src/gb.web.form/.src/Test/Webform6.class new file mode 100644 index 00000000..0d4a779b --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform6.class @@ -0,0 +1,20 @@ +' Gambas class file + + +Public Sub WebForm_Open() + + WebForm.Debug = True + +End + +Public Sub WebComboBox1_Click() + + Message(WebComboBox1.Text) + +End + +Public Sub WebButton2_Click() + + Message(Html("TextBox: " & WebTextBox1.Text) & "

    " & Html("TextArea: " & WebTextArea1.Text)) + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform6.webform b/comp/src/gb.web.form/.src/Test/Webform6.webform new file mode 100644 index 00000000..1156f563 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform6.webform @@ -0,0 +1,30 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,74,91) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebComboBox1 WebComboBox + #MoveScaled(1,1,72,4) + List = [(""), ("Élément 1"), ("Élément 2"), ("Élément 3")] + } + { WebTextBox1 WebTextBox + #MoveScaled(1,6,72,4) + } + { WebTextArea1 WebTextArea + #MoveScaled(1,11,72,16) + } + { WebHBox1 WebHBox + #MoveScaled(1,28,72,7) + Spacing = True + { WebButton1 WebButton + #MoveScaled(1,1,16,5) + Text = ("Copy") + } + { WebButton2 WebButton + #MoveScaled(18,1,16,5) + Text = ("Text") + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform7.class b/comp/src/gb.web.form/.src/Test/Webform7.class new file mode 100644 index 00000000..1f6be946 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform7.class @@ -0,0 +1,4 @@ +' Gambas class file + +'Export + diff --git a/comp/src/gb.web.form/.src/Test/Webform7.webform b/comp/src/gb.web.form/.src/Test/Webform7.webform new file mode 100644 index 00000000..6f2158b1 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform7.webform @@ -0,0 +1,15 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,64,91) + { WebContainer4 WebContainer + #MoveScaled(1,1,62,5) + { WebLabel1 WebLabel + #MoveScaled(1,1,12,3) + Text = ("Name") + } + { WebTextBox1 WebTextBox + #MoveScaled(13,1,27,3) + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform8.class b/comp/src/gb.web.form/.src/Test/Webform8.class new file mode 100644 index 00000000..cf218917 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform8.class @@ -0,0 +1,13 @@ +' Gambas class file + +'Export + + +Public Sub WebButton1_Click() + + Dim hForm As Webform7 + + Debug + hForm = New Webform7(WebContainer1) + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform8.webform b/comp/src/gb.web.form/.src/Test/Webform8.webform new file mode 100644 index 00000000..b527caf4 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform8.webform @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,64,91) + { WebButton1 WebButton + #MoveScaled(2,1,18,4) + Text = ("Load Form") + } + { WebContainer1 WebContainer + #MoveScaled(2,6,59,43) + } +} diff --git a/comp/src/gb.web.form/.src/WebButton.class b/comp/src/gb.web.form/.src/WebButton.class new file mode 100644 index 00000000..92098d1d --- /dev/null +++ b/comp/src/gb.web.form/.src/WebButton.class @@ -0,0 +1,134 @@ +' Gambas class file + +''' This class implements a push button. + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,Text,Image{WebImage},Immediate" +'Public Const _DrawWith As String '= "Button" +Public Const _DefaultEvent As String = "Click" +Public Const _DefaultSize As String = "16,4" + +'' This event is raised when the button is pressed. +'' +'' ### See also +'' [../immediate] +Event Click + +'' Return or set the button text. +Property Text As String +'' Return or set the button image relative path. +Property Image As String +'' If the Immediate property is set, the [../click] event is raised to generate javascript code that will be executed on the client side when +'' the button is clicked. +'' +'' In that case, the Click event handler should only call methods that generate javascript on the client side: +'' - [../../webform/exec](WebForm.Exec) +'' - [../../webtextbox/copy](WebTextBox.Copy) +'' - ... +Property Immediate As Boolean + +Private $sText As String +Private $sImage As String +Private $bImmediate As Boolean + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + If $sText = Value Then Return + $sText = Value + Me._SetProperty("Text", Value) + +End + +Private Function Image_Read() As String + + Return $sImage + +End + +Private Sub Image_Write(Value As String) + + If $sImage = Value Then Return + $sImage = Value + Me._SetProperty("Image", Value) + +End + +Public Sub _BeforeRender() + + Dim vSave As Variant + + Print ""; + +End + + +Public Sub _Render() + + If $sImage Then + Print "

    "; + Endif + If $sText Then Print ""; Html($sText); ""; + +End + +Public Sub _AfterRender() + + Raise Render + Print "" + +End + +'' Click on the button. + +Public Sub Click() + + Object.Raise(Me, "Click") + +End + +Private Function Immediate_Read() As Boolean + + Return $bImmediate + +End + +Private Sub Immediate_Write(Value As Boolean) + + If $bImmediate = Value Then Return + $bImmediate = Value + Me._SetProperty("Immediate", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebCheckBox.class b/comp/src/gb.web.form/.src/WebCheckBox.class new file mode 100644 index 00000000..79ccd984 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebCheckBox.class @@ -0,0 +1,71 @@ +' Gambas class file + +''' This class implements a check-box control. + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Text,Value" +Public Const _DrawWith As String = "CheckBox" +Public Const _DefaultSize As String = "24,4" +Public Const _Similar As String = "WebButton" +Public Const _DefaultEvent As String = "Click" + +'' This event is raised when the check-box is toggled. +Event Click + +'' Return or set if the control is checked. +Property Value, Checked As Boolean +'' Return or set the text displayed beside the check mark. +Property Text As String + +Private $sText As String +Private $bChecked As Boolean + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + If $sText = Value Then Return + $sText = Value + Me._SetProperty("Text", Value) + +End + +Public Sub _Render() + + If $sText Then Print ""; + Print + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "value" Then Try Value_Write(vValue) + +End + + +Private Function Value_Read() As Boolean + + Return $bChecked + +End + +Private Sub Value_Write(Value As Boolean) + + If $bChecked = Value Then Return + $bChecked = Value + Me._SetProperty("Value", Value) + Raise Click + +End diff --git a/comp/src/gb.web.form/.src/WebComboBox.class b/comp/src/gb.web.form/.src/WebComboBox.class new file mode 100644 index 00000000..c6edcf2a --- /dev/null +++ b/comp/src/gb.web.form/.src/WebComboBox.class @@ -0,0 +1,269 @@ +' Gambas class file + +''' This class implements a combo-box control. + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,List,ReadOnly=True,PlaceHolder,Text" +Public Const _DrawWith As String = "ComboBox" +Public Const _DefaultSize As String = "24,4" +Public Const _Similar As String = "WebTextBox" +Public Const _DefaultEvent As String = "Click" + +'' This event is raised when an item is selected in the list box. +Event Click +'' This event is raised when the combo-box text has changed. Works only if the combo-box is editable. +Event Change +'' This event is raised when the ENTER key is hit. Works only if the combo-box is editable. +Event Activate + +'' Return or set the combo-box text. +Property Text As String +'' Return the number of items in the combo-box popup. +Property Read Count As Integer +'' Return or set the index of the current selected item. +Property Index As Integer +'' Return or set the contents of the combo-box popup as a string array. +Property List As String[] +'' Return or set if the combo-box is read-only, i.e. not editable. +Property ReadOnly As Boolean +'' Return or set the placeholder text displayed when the editable combo-box is empty. +Property PlaceHolder As String + +Private $iIndex As Integer +Private $sText As String +Private $aList As New String[] +Private $bReadOnly As Boolean = True +Private $sPlaceHolder As String + +'' Create a new WebComboBox control. + +Public Sub _new() + + Me._Proxy = ":entry" + +End + + +Public Sub _BeforeRender() + + Dim sClass As String + + If $bReadOnly Then + sClass = "gw-selectbox" + Else + sClass = "gw-combobox" + Endif + + Print "" + +End + +Public Sub _Render() + + Dim I As Integer + + If Not $bReadOnly Then + Print ""; + Print "
    "; + Endif + + Print ""; + + If Not $bReadOnly Then + Print "
    "; + WebForm._AddJavascript("gw.resizeComboBox(" & JS(Me.Name) & ")") + Endif + Print + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "index" Then + Try Me.Index = vValue + Else If sProp = "text" Then + Try Me.Text = vValue + Endif + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + If Value < -1 Or If Value >= $aList.Count Then Error.Raise("Out of bounds") + If $iIndex = Value Then Return + + $iIndex = Value + If Not $bReadOnly Then + If $iIndex < 0 Then + $sText = "" + Else + $sText = $aList[$iIndex] + Endif + Me._SetProperty("Text", $sText) + Endif + Me._SetProperty("Index", $iIndex) + Raise Click + +End + +Private Function Count_Read() As Integer + + Return $aList.Count + +End + +Private Function List_Read() As String[] + + Return $aList.Copy() + +End + +Private Sub CheckIndex() + + If $iIndex < 0 Or If $iIndex >= $aList.Count Then + If $aList.Count = 0 Then + Index_Write(-1) + Else + Index_Write(0) + Endif + Endif + +End + +Private Sub List_Write(Value As String[]) + + If Value Then + $aList = Value.Copy() + Else + $aList = New String[] + Endif + CheckIndex + Me._SetProperty("List", $aList) + +End + +'' Clear the contents of the combo-box and its popup. +Public Sub Clear() + + $aList.Clear + Index_Write(-1) + Me._SetProperty("List", $aList) + If Not $bReadOnly Then Text_Write("") + +End + +Private Function ReadOnly_Read() As Boolean + + Return $bReadOnly + +End + +Private Sub ReadOnly_Write(Value As Boolean) + + $bReadOnly = Value + If $bReadOnly Then + Me._Proxy = "" + Else + Me._Proxy = ":entry" + Endif + Me._SetProperty("ReadOnly", Value) + +End + +'' Add an item to the popup list. +'' +'' - ~Item~ is the text of the item to add. +'' - ~Index~ is the position the item will be inserted from. If not specified, the item is added at the end of the popup list. + +Public Sub Add(Item As String, Optional Index As Integer = -1) + + $aList.Add(Item, Index) + CheckIndex + Me._SetProperty("List", $aList) + +End + +'' Remove an item from the popup list. +'' +'' - ~Index~ is the index of the item to remove, between 0 and [../count] - 1. + +Public Sub Remove(Index As Integer) + + $aList.Remove(Index) + CheckIndex + Me._SetProperty("List", $aList) + +End + +Private Function Text_Read() As String + + If $bReadOnly Then + Try Return $aList[$iIndex] + Return -1 + Else + Return $sText + Endif + +End + +Private Sub Text_Write(Value As String) + + If $bReadOnly Then + + Index_Write($aList.Find(Value)) + + Else + + If $sText = Value Then Return + $sText = Value + + Inc Me._NoRefresh + Me._SetProperty("Text", Value) + Dec Me._NoRefresh + + If Me._CanRefresh() Then WebForm._AddJavascript("$(" & JS(Me.Name & ":entry") & ").value = " & JS($sText)) + + Raise Change + + Endif + +End + +Private Function PlaceHolder_Read() As String + + Return $sPlaceHolder + +End + +Private Sub PlaceHolder_Write(Value As String) + + $sPlaceHolder = Value + Me._SetProperty("PlaceHolder", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebContainer.class b/comp/src/gb.web.form/.src/WebContainer.class new file mode 100644 index 00000000..324e278f --- /dev/null +++ b/comp/src/gb.web.form/.src/WebContainer.class @@ -0,0 +1,438 @@ +' Gambas class file + +''' This class implements a generic container for other controls. +''' +''' It is also the parent class of every other container. + +Export + +Inherits WebControl + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*,Arrangement{Arrange.*},Margin,Spacing,Indent,Border" +Public Const _DefaultArrangement As String = "F" +Public Const _DefaultSize As String = "32,32" + +'' Return or set the how the container arrange its contents. +'' +'' The value of that property can be one of the value of the [../arrange] class. +Property Arrangement As Integer +'' Return or set if there is a margin between the container border and all its children. +'' +'' The margin size is 0.5em. +Property Margin As Boolean +'' Return or set if the children of the container are spaced out. +'' +'' The amount of space is 0.5em. +Property Spacing As Boolean +'' Return or set if the container has a border. +Property Border As Boolean +'' Return or set if the children controls of the container are indented. +'' +'' The direction of the indentation follows the direction of the container arrangement. +'' +'' The amount of indentation is 0.5em. +Property Indent As Boolean + +'' Return an array of all container's children. +Property Read Children As WebControl[] + +Public _Container As WebContainer + +Private $aChildren As New String[] +Private $aExtraChildren As New String[][] +Private $iArrangement As Integer +Private $bMargin As Boolean +Private $bSpacing As Boolean +Private $bBorder As Boolean +Private $bIndent As Boolean + +Public Sub _Add(hChild As WebControl) + + Dim aExtraChild As String[] + Dim hParent As WebControl + + $aChildren.Add(hChild.Name) + + Inc Me._NoRefresh + + If WebForm._InExec And If Not Object.IsLocked(Me) Then + 'If Not Object.IsLocked(Me) Then + + 'Debug "Add extra "; hChild.Name; " to "; Me.Name + hChild._Extra = True + aExtraChild = [hChild.Name, Object.Type(hChild)] + + If hChild._EventName Then + aExtraChild.Add(hChild._EventName) + Try hParent = Object.Parent(hChild) + If Not Error Then aExtraChild.Add(hParent.Name) + Endif + + $aExtraChildren.Add(aExtraChild) + + Me._SetProperty("#extra", $aExtraChildren) + + Endif + + Me._SetProperty("#children", $aChildren) + + Dec Me._NoRefresh + + If Me._CanRefresh() Then + WebForm._AddJavascriptBefore("gw.insertElement(" & JS(hChild.Name) & "," & JS(Me.Name) & ")") + hChild.Refresh + Endif + +End + +Public Sub _Remove(hChild As WebControl) + + Dim iPos As Integer + + $aChildren.Remove($aChildren.Find(hChild.Name)) + + Inc Me._NoRefresh + + If hChild._Extra Then + + For iPos = 0 To $aExtraChildren.Max + If $aExtraChildren[iPos][0] = hChild.Name Then + 'Debug "Remove extra "; hChild.Name; " from "; Me.Name + $aExtraChildren.Remove(iPos) + Me._SetProperty("#extra", $aExtraChildren) + Break + Endif + Next + + Endif + + Me._SetProperty("#children", $aChildren) + + Dec Me._NoRefresh + + If Me._CanRefresh() Then + WebForm._AddJavascript("gw.removeElement(" & JS(hChild.Name) & ")") + Endif + +End + +Public Sub _UpdateChildName(sOld As String, sNew As String) + + $aChildren[$aChildren.Find(sOld)] = sNew + +End + + +Public Sub _IsFirstVisibleChild(hChild As WebControl) As Boolean + + Dim I As Integer + Dim hCtrl As WebControl + + For I = 0 To $aChildren.Max + If $aChildren[I] = hChild.Name Then Return True + hCtrl = WebControl.FromName($aChildren[I]) + If hCtrl.Visible And If Not hCtrl.Ignore Then Return + Next + +End + +Public Sub _InitProperties() + + Dim I As Integer + + Super._InitProperties() + + For I = 0 To $aChildren.Max + WebControl.FromName($aChildren[I])._InitProperties() + Next + +End + +Public Sub _Render() + + Dim I As Integer + + For I = 0 To $aChildren.Max + With WebControl.FromName($aChildren[I]) + 'If Not .Visible Then Continue + ._BeforeRender() + ._Render() + ._AfterRender() + End With + Next + +End + +Public Sub _RenderStyleSheet() + + 'Dim I As Integer + + Me._StartStyleSheet + + Super._RenderStyleSheet() + + If Not _Container And If Me.Visible Then + + If $iArrangement Then + + Me._AddStyleSheet("display:flex;") + Select Case $iArrangement + Case Arrange.Horizontal + Me._AddStyleSheet("flex-flow:row;") + Me._AddStyleSheet("overflow-x:hidden;") + Case Arrange.Vertical + Me._AddStyleSheet("flex-flow:column;") + Case Arrange.Column + Me._AddStyleSheet("flex-flow:column wrap;") + 'If $bSpacing And If $aChildren.Count Then Me._AddStyleSheet("margin-right:-0.5em;margin-bottom:-0.5em;") + Case Arrange.Row + Me._AddStyleSheet("flex-flow:row wrap;") + 'If $bSpacing And If $aChildren.Count Then Me._AddStyleSheet("margin-right:-0.5em;margin-bottom:-0.5em;") + End Select + + Endif + + If $bMargin Then Me._AddStyleSheet("padding:0.5rem;") + + If $bIndent Then + If $bMargin Then + Me._AddStyleSheet("padding-left:1rem;") + Else + Me._AddStyleSheet("padding-left:0.5rem;") + Endif + Endif + + If $bSpacing Then + If $iArrangement = Arrange.Column Or If $iArrangement = Arrange.Row Then + Me._AddStyleSheet("padding-bottom: 0;") + Me._AddStyleSheet("padding-right: 0;") + Endif + Endif + + Endif + + If $bBorder Then Me._AddStyleSheet("border:solid #C0C0C0 1px;") + + Me._EndStyleSheet + + ' For I = 0 To $aChildren.Max + ' With WebControl.FromId($aChildren[I]) + ' ._RenderStyleSheet() + ' End With + ' Next + +End + +Private Function Arrangement_Read() As Integer + + Return $iArrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + $iArrangement = Value + Me._SetProperty("Arrangement", Value) + UpdateContainer() + +End + +Private Function Margin_Read() As Boolean + + Return $bMargin + +End + +Private Sub Margin_Write(Value As Boolean) + + $bMargin = Value + Me._SetProperty("Margin", Value) + UpdateContainer() + +End + +Private Function Spacing_Read() As Boolean + + Return $bSpacing + +End + +Private Sub Spacing_Write(Value As Boolean) + + $bSpacing = Value + Me._SetProperty("Spacing", Value) + UpdateContainer() + +End + +Private Function Border_Read() As Boolean + + Return $bBorder + +End + +Private Sub Border_Write(Value As Boolean) + + $bBorder = Value + Me._SetProperty("Border", Value) + +End + +Public Sub _RefreshReply() As Boolean + + Dim I As Integer + + If Super._RefreshReply() Then Return + + For I = 0 To $aChildren.Max + WebControl.FromName($aChildren[I])._RefreshReply() + Next + +End + +Public Sub _SetContainer(hCont As WebContainer) + + _Container = hCont + UpdateContainer() + +End + +Private Sub UpdateContainer() + + If Not _Container Then Return + + With _Container + .Arrangement = $iArrangement + .Spacing = $bSpacing + .Margin = $bMargin + End With + +End + +Private Function Children_Read() As WebControl[] + + Dim aChildren As WebControl[] + Dim I As Integer + + aChildren = New WebControl[$aChildren.Count] + For I = 0 To $aChildren.Max + aChildren[I] = WebControl.FromName($aChildren[I]) + Next + Return aChildren +End + +Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) + + Dim I As Integer + Dim sClass As String + Dim sName As String + Dim hCtrl As WebControl + Dim hForm As WebForm + Dim sEventName As String + Dim aExtraChildren As String[][] + Dim aExtraChild As String[] + Dim sParentName As String + + If sProp = "#extra" Then + + hForm = Me.Form + aExtraChildren = vVal + + For I = 0 To aExtraChildren.Max + + aExtraChild = aExtraChildren[I] + + sName = aExtraChild[0] + sClass = aExtraChild[1] + + WebControl._NextName = sName + hCtrl = Object.New(sClass, [Me]) + + If aExtraChild.Count = 4 Then + sEventName = aExtraChild[2] + sParentName = aExtraChild[3] + 'Debug sName;; sClass; sEventName;; WebControl.FromName(sParentName) + If sEventName Then Object.Attach(hCtrl, WebControl.FromName(sParentName), sEventName) + Endif + + hCtrl._Extra = True + + 'WebForm.Print(WebControl._GetNames()) + + ' TODO: attach to the real form + 'If Left(sName) <> "$" Then Object.Attach(hCtrl, hForm, sName) + + Next + + $aExtraChildren = aExtraChildren + + Else If sProp = "#children" Then + + $aChildren = vVal + Me.Refresh + + Endif + +End + +Public Sub _Raise(sName As String) + + Dim iPos As Integer + + iPos = $aChildren.Find(sName) + If iPos < 0 Then Return + + $aChildren.Remove(iPos) + $aChildren.Add(sName) + Me._SetProperty("#children", $aChildren) + +End + +Public Sub _Lower(sName As String) + + Dim iPos As Integer + + iPos = $aChildren.Find(sName) + If iPos < 0 Then Return + + $aChildren.Remove(iPos) + $aChildren.Add(sName, 0) + Me._SetProperty("#children", $aChildren) + +End + +'' Delete all container's children. + +Public Sub DeleteChildren() + + While $aChildren.Count + WebControl.FromName($aChildren[0]).Delete + Wend + +End + +'' Delete the container with all its children. + +Public Sub Delete() + + Inc WebForm._DisableRefresh + DeleteChildren + Dec WebForm._DisableRefresh + Super.Delete + +End + +Private Function Indent_Read() As Boolean + + Return $bIndent + +End + +Private Sub Indent_Write(Value As Boolean) + + $bIndent = Value + Me._SetProperty("Indent", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebControl.class b/comp/src/gb.web.form/.src/WebControl.class new file mode 100644 index 00000000..0bbfc1f6 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebControl.class @@ -0,0 +1,1102 @@ +' Gambas class file + +''' This class is the parent class of every web controls. + +Export +Create Private + +Public Const _IsControl As Boolean = True +Public Const _Properties As String = "Class,Width{WebCoord},Height{WebCoord},Visible=True,Enabled=True,Tag,Expand,Ignore,Background{Color},Foreground{Color},Tooltip,Font{WebFont}" +Public Const _Family As String = "WebForm" +Public Const _DefaultEvent As String = "Render" + +'' This event is raised just after the rendering of the control, so that you can add your own HTML contents. +Event Render +'' This event is raised after a message box has been closed. +'' - ~Source~ is the source control, i.e. the control owning the method that opened the message box. +'' - ~Action~ is the text of the message box button that has been clicked. +Event Message(Source As WebControl, Action As String) + +Event Dialog(Dialog As WebForm, Result As Variant) + +'' Return the server control identifier. +Property Read Id As Integer + +'' Return or set the name of the control, used as an identifier on the client side. +Property Name As String + +'' Returns or sets the control tag. +'' +'' This property is intended for the programmer and is never used by the component. It can contain any [/lang/type/variant] value. +Property Tag As Variant + +'' Return the parent container of the control. +Property Read Parent As WebContainer + +'' Return or set if the control should expand its size according to its parent arrangement mode. +Property Expand As Boolean + +'' Return or set if the control is visible. +Property Visible As Boolean + +'' Return a virtual object that allows to define specific style sheet elements of the control. +Property Read Style As WebControlStyle + +'' Return the web form that control belongs to. +Property Read Form As WebForm + +'' Return or set the explicit width of the control. +'' +'' This property can take any valid CSS size. If not set, the control will have its default width. +Property Width, W As String + +'' Return or set the explicit height of the control. +'' +'' This property can take any valid CSS size. If not set, the control will have its default height. +Property Height, H As String + +'' Return or set if the control must be ignored by its parent container in automatic arrangement mode. +Property Ignore As Boolean + +'' Return or set if the control is enabled. +Property Enabled As Boolean + +'' Return or set the background color of the control. +Property Background As Integer + +'' Return or set the foreground color of the control. +Property Foreground As Integer + +'' Return or set if the control has a border. +Property Border As Boolean + +'' Return or set the specific CSS classes added to the control. +Property Class As String + +'' Return or set the tooltip displayed when the mouse cursor hovers the control. +Property Tooltip As String + +'' Return or set the specific font of the control. +Property Font As String + +Static Private $iLastId As Integer +Static Private $iLastFormId As Integer +Static Private $cFromId As New Collection +Static Private $cFromName As New Collection + +Static Private $aStyleSheet As String[] + +Static Public _NextName As String + +Public _Refresh As Boolean +Public _EventName As String +Public _Extra As Boolean +Public _Proxy As String +Public _NoRefresh As Integer +Public _Naked As Boolean + +Private $iId As Integer +Private $iParentId As Integer +Private $iFormId As Integer +Private $sName As String +Private $vTag As Variant + +Private $iStyleSheet As Integer +Private $hStyle As WebControlStyle + +Private $iBackground As Integer = Color.Default +Private $iForeground As Integer = Color.Default + +Private $bExpand As Boolean +Private $bIgnore As Boolean +Private $bDisabled As Boolean +Private $bHidden As Boolean +Private $bNoBorder As Boolean +Private $sClass As String +Private $sTooltip As String + +'' Create a new [@{class}](.). + +Public Sub _new(Optional Parent As WebContainer) + + Dim sName As String + + Inc $iLastId + $iId = $iLastId + + If _NextName Then + sName = _NextName + _NextName = "" + Endif + + If Me Is WebForm Then + Object.Attach(Me, Me, "WebForm") + $iFormId = $iId + Inc $iLastFormId + If Not sName Then sName = "@" & CStr($iLastFormId) + Else + _EventName = Param.EventName + Try $iFormId = Parent.Form.Id + If Not sName Then sName = Param.EventName + If Not sName Then sName = "$" & CStr($iId) + Endif + + SetName(sName) + + $cFromId[$iId] = Me + + Parent_Write(Parent) + + 'Debug Object.Type(Me);; $sName;; $iId + +End + +Private Sub SetName(Optional sName As String) + + Dim iSuffix As Integer + Dim sNameSuffix As String + Dim hParent As WebContainer + + If Left(sName) <> "@" Then sName = Form_Read().Name & "." & sName + + If sName = $sName Then Return + + If $sName Then $cFromName[$sName] = Null + + Do + If iSuffix = 0 Then + sNameSuffix = sName + Else + sNameSuffix = sName & "-" & CStr(iSuffix + 1) + Endif + If Not $cFromName.Exist(sNameSuffix) Then + sName = sNameSuffix + Break + Endif + Inc iSuffix + Loop + + hParent = Parent_Read() + If hParent Then hParent._UpdateChildName($sName, sName) + + $cFromName[sName] = $iId + $sName = sName + +End + +Public Sub _SetForm(hForm As WebForm) + + $iFormId = hForm.Id + +End + +Private Function Id_Read() As Integer + + Return $iId + +End + +'' Return a WebControl from its server identifier. + +Static Public Sub FromId(Id As Integer) As WebControl + + Return $cFromId[Id] + +End + +'' Return a WebControl from its name. + +Static Public Sub FromName(Name As String) As WebControl + + Try Return $cFromId[$cFromName[Name]] + +End + +Private Function Parent_Read() As WebContainer + + Dim hParent As WebContainer + + If $iParentId = 0 Then Return + + hParent = $cFromId[$iParentId] +' If hParent.Parent And If hParent.Parent._Container = hParent Then hParent = hParent.Parent + + Return hParent + +End + +Private Sub Parent_Write(Value As WebContainer) + + Dim hParent As WebContainer + Dim sParent As String + + hParent = Parent_Read() + + If Value = hParent Then Return + + If hParent Then hParent._Remove(Me) + + If Value Then + If Value._Container Then Value = Value._Container + $iParentId = Value.Id + $iFormId = Value.Form.Id + Value._Add(Me) + sParent = Value.Name + Else + $iParentId = 0 + Endif + + '_SetProperty("#parent", sParent) + +End + +Private Function Name_Read() As String + + Return $sName + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + _SetProperty("Tag", Value) + +End + +Public Sub _GetClassId(Optional sClass As String) As String + + Dim sResult As String + + If Not sClass Then sClass = "gw-" & LCase(Mid$(Object.Type(Me), 4)) + If $bNoBorder Then sClass &= " gw-noborder" + If Not Me.Enabled Then sClass &= " gw-disabled" + If $sClass Then sClass &= " " & $sClass + If $bHidden Then sClass &= " gw-hidden" + sResult = " class=\"" & sClass & "\" id=\"" & $sName & "\"" + If $sTooltip Then sResult &= " title=\"" & Replace($sTooltip, Chr$(34), """) & "\"" + Return sResult + +End + + +Public Sub _BeforeRender() + + Print ""; + +End + +Public Sub _Render() + +End + +Public Sub _AfterRender() + + Raise Render + Print "
    " + +End + +Public Sub _RenderStyleSheet() + + Dim hParent As WebContainer + + _StartStyleSheet + + If $bIgnore Then + + _AddStyleSheet("position:absolute;") + + Else + + If Me Is WebContainer Then _AddStyleSheet("position:relative;") + + If $bExpand Then + _AddStyleSheet("flex-grow:1;") + Else + _AddStyleSheet("flex-shrink:0;") + Endif + + hParent = Me.Parent + If hParent Then + Select Case hParent.Arrangement + Case Arrange.Vertical + 'If bInline Then _AddStyleSheet("display:block;") + If hParent.Spacing And If Not hParent._IsFirstVisibleChild(Me) Then _AddStyleSheet("margin-top:0.5rem;") + Case Arrange.Horizontal + '_AddStyleSheet("display: block;") + If hParent.Spacing And If Not hParent._IsFirstVisibleChild(Me) Then _AddStyleSheet("margin-left:0.5rem;") + Case Arrange.Column, Arrange.Row + _AddStyleSheet("display:inline-block;") + If hParent.Spacing Then _AddStyleSheet("margin-right:0.5rem;margin-bottom:0.5rem;") + End Select + Endif + + Endif + + If $iBackground <> Color.Default Then _AddStyleSheet("background-color:" & _GetColor($iBackground) & ";") + If $iForeground <> Color.Default Then _AddStyleSheet("color:" & _GetColor($iForeground) & ";") + + If $hStyle And If Not $hStyle.IsVoid() Then $aStyleSheet.Insert($hStyle._GetStyleSheet()) + + _EndStyleSheet + +End + +Public Sub _StartStyleSheet() + + Inc $iStyleSheet + If $iStyleSheet = 1 Then $aStyleSheet = New String[] + +End + +Public Sub _AddStyleSheet(sStyle As String) + + $aStyleSheet.Add(sStyle) + +End + +Public Sub _RemoveStyleSheet(sStyle As String) + + Try $aStyleSheet.Remove($aStyleSheet.Find(sStyle)) + +End + + +Public Sub _EndStyleSheet() + + Dec $iStyleSheet + If $iStyleSheet Then Return + + If $aStyleSheet.Count Then + 'Print "#"; Me.Name; " {" + 'Print " "; $aStyleSheet.Join("\n ") + 'Print "}" + Print " style=\""; $aStyleSheet.Join(""); "\""; + $aStyleSheet.Clear + Endif + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + If $bExpand = Value Then Return + $bExpand = Value + _SetProperty("Expand", Value) + Try Me.Parent.Refresh + +End + +Private Function Style_Read() As WebControlStyle + + If Not $hStyle Then $hStyle = New WebControlStyle As "Style" + Return $hStyle + +End + +Private Function Form_Read() As WebForm + + Try Return $cFromId[$iFormId] + +End + +Public Sub _SetProperty(sProp As String, vValue As Variant) + + Dim cProp As Collection + + If Me.Form._Loaded Then + + If WebForm._InExec Then Me.Refresh + + If TypeOf(vValue) = gb.Object And If vValue Is WebControl Then + sProp = "@" & sProp + vValue = vValue.Name + Endif + + cProp = Session[$sName] + If Not cProp Then cProp = New Collection + cProp[sProp] = vValue + Session[$sName] = cProp + + Endif + +End + +Public Sub _InitSpecialProperty((sProp) As String, (vVal) As Variant) + +End + +Public Sub _InitProperties() + + Dim cProp As Collection + Dim vVal As Variant + Dim sProp As String + + If Not Session.Id Then Return + + Try cProp = Session[$sName] + If Not cProp Then Return + + Object.Lock(Me) + For Each vVal In cProp + sProp = cProp.Key + 'WebForm.Print("set " & sProp) + If Left(sProp) = "#" Then + If sProp = "#style" Then + $hStyle = New WebControlStyle As "Style" + $hStyle._Style = vVal + ' Else If sProp = "#parent" Then + ' Parent_Write(FromName(vVal)) + Else + Me._InitSpecialProperty(sProp, vVal) + Endif + Else If Left(sProp) = "@" Then + Object.SetProperty(Me, Mid$(sProp, 2), WebControl.FromName(vVal)) + Else + Try Object.SetProperty(Me, sProp, vVal) + Endif + Next + Object.Unlock(Me) + +End + +Public Sub Style_Change() + + _SetProperty("#style", $hStyle._Style) + +End + +'' Return if the control or one of its parent is hidden. + +Public Sub IsHidden() As Boolean + + Dim hCtrl As WebControl + + hCtrl = Me + While hCtrl + If Not hCtrl.Visible Then Return True + hCtrl = hCtrl.Parent + Wend + +End + +Public Sub _CanRefresh() As Boolean + + If Not Me.Form._Loaded Then Return + If WebForm._InExec = 0 Then Return + If WebForm._DisableRefresh Then Return + If _NoRefresh Then Return + If IsHidden() Then Return + Return True + +End + +'' Refresh the control. + +Public Sub Refresh() + + If WebForm._DisableRefresh Then Return + If _NoRefresh Then Return + If IsHidden() Then Return + If WebForm._InExec Then + _Refresh = True + WebForm._HasRefresh = True + Endif + +End + +Public Sub _RefreshReply() As Boolean + + Dim hFile As File + Dim sResult As String + + If _Refresh Then + + hFile = Open String For Write + Output To #hFile + + If Not _Naked Then Me._BeforeRender() + Me._Render() + If Not _Naked Then Me._AfterRender() + + sResult = Close #hFile + Output To Default + + 'WebForm._AddRefresh("$(" & JS(Me.Name) & ").outerHTML = " & JS(sResult) & ";") + + If _Naked Then + Print "gw.setInnerHtml("; JS(Me.Name); ","; JS(sResult); ");" + Else + Print "gw.setOuterHtml("; JS(Me.Name); ","; JS(sResult); ");" + 'Print "$("; JS(Me.Name); ").outerHTML = "; JS(sResult); ";" + Endif + + _Refresh = False + Return True + + Endif + +Catch + + Output To Default + Error.Propagate() + +End + +Public Sub _GetEventJS(sProp As String, sEvent As String, Optional sArg As String) As String + + Dim sStr As String + + If Not Object.CanRaise(Me, sEvent) Then Return + + sStr = " " & sProp & "=\"gw.raise(" '& JS(Me.Form.Name) & "," + If Me Is WebForm Then + sStr &= "null" + Else + sStr &= JS($sName) + Endif + sStr &= "," & JS(sEvent) + If sArg Then sStr &= "," & sArg + sStr &= ");" + sStr &= "\"" + + Return sStr + +End + +Public Sub _GetUpdateJS(sEvent As String, sProp As String, Optional sGetValue As String = "null", Optional sMore As String) As String + + Dim sStr As String + sStr = " " & sEvent & "=\"gw.update(" & JS($sName) & "," & JS(sProp) & "," & sGetValue & ");" + If sMore Then sStr &= sMore + Return sStr & "\"" + +End + +Public Sub _GetUpdateWaitJS(sEvent As String, sProp As String, Optional sGetValue As String = "null", Optional sMore As String) As String + + Dim sStr As String + sStr = " " & sEvent & "=\"gw.updateWait(" & JS($sName) & "," & JS(sProp) & "," & sGetValue & ");" + If sMore Then sStr &= sMore + Return sStr & "\"" + +End + +Public Sub _UpdateProperty((sProp) As String, (vValue) As Variant) + +End + +Private Function Width_Read() As String + + If $hStyle Then Return $hStyle["width"] + +End + +Private Sub Width_Write(Value As String) + + Style_Read()["width"] = Value + +End + +Private Function Height_Read() As String + + If $hStyle Then Return $hStyle["height"] + +End + +Private Sub Height_Write(Value As String) + + Style_Read()["height"] = Value + +End + +Private Function Visible_Read() As Boolean + + Return Not $bHidden + + ' If Not $hStyle Then Return True + ' Return $hStyle["display"] <> "none" + +End + +Private Sub Visible_Write(Value As Boolean) + + Dim bCanRefresh As Boolean + + If $bHidden <> Value Then Return + + $bHidden = False + bCanRefresh = _CanRefresh() + $bHidden = Not Value + + Inc _NoRefresh + _SetProperty("Visible", Value) + Dec _NoRefresh + If bCanRefresh Then + WebForm._AddJavascript("gw.setVisible(" & JS(Me.Name) & "," & JS(Not $bHidden) & ")") + If Me.Parent Then + Me.Parent.Refresh + Else If Me Is WebContainer Then + Refresh + Endif + Endif + 'Me.Parent.Refresh + + ' If Value Then + ' If $hStyle Then Style_Read()["display"] = "" + ' Else + ' Style_Read()["display"] = "none" + ' Endif + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + If $bIgnore = Value Then Return + $bIgnore = Value + _SetProperty("Ignore", Value) + Me.Parent.Refresh + +End + +'' Set the focus on the control. +'' +'' - ~Highlight~ is an optional argument that specify if the control contents must be highlighted. It usually works only for TextBox or TextArea controls. + +Public Sub SetFocus(Optional Highlight As Boolean) + + Dim sName As String + + sName = $sName + If _Proxy Then sName &= _Proxy + + WebForm._AddReply("gw.setFocus(" & JS(sName) & ")") + If Highlight Then WebForm._AddReply("gw.highlightMandatory(" & JS(sName) & ")") + +End + +Private Function Enabled_Read() As Boolean + + If $bDisabled Then Return + If $iParentId = 0 Then Return True + Return Parent_Read().Enabled + +End + +Private Sub Enabled_Write(Value As Boolean) + + If $bDisabled <> Value Then Return + $bDisabled = Not Value + Me._SetProperty("Enabled", Value) + +End + +Private Function Background_Read() As Integer + + Return $iBackground + +End + +Private Sub Background_Write(Value As Integer) + + If $iBackground = Value Then Return + $iBackground = Value + Me._SetProperty("Background", Value) + +End + +Private Function Foreground_Read() As Integer + + Return $iForeground + +End + +Private Sub Foreground_Write(Value As Integer) + + If $iForeground = Value Then Return + $iForeground = Value + Me._SetProperty("Foreground", Value) + + +End + +Static Public Sub _GetColor(iCol As Integer) As String + + Dim A As Integer + Dim R As Integer + Dim G As Integer + Dim B As Integer + + A = Lsr(iCol, 24) + R = Lsr(iCol, 16) And 255 + G = Lsr(iCol, 8) And 255 + B = iCol And 255 + + If A Then + Return "rgba(" & CStr(R) & "," & CStr(G) & "," & CStr(B) & "," & CStr(Round(A / 255, -2)) & ")" + Else + Return "#" & Hex$(R, 2) & Hex$(G, 2) & Hex$(B, 2) + Endif + +End + +'' Raise the control. + +Public Sub Raise() + + Dim hParent As WebContainer + + hParent = Parent_Read() + If hParent Then hParent._Raise($sName) + +End + +'' Lower the control. + +Public Sub Lower() + + Dim hParent As WebContainer + + hParent = Parent_Read() + If hParent Then hParent._Lower($sName) + +End + +Static Public Sub _GetNames() As String + + Dim aKey As New String[] + + For Each $cFromName + aKey.Add($cFromName.Key) + Next + + Return aKey.Join(",") + +End + +'' Delete the control + +Public Sub Delete() + + Dim hParent As WebContainer + + hParent = Parent_Read() + If hParent Then hParent._Remove(Me) + + Session[$sName] = Null + Session[$sName & ":data"] = Null + + $cFromName[$sName] = Null + $cFromId[$iId] = Null + 'Debug $sName;; $iId;; System.Backtrace.Join(" ") + +End + +'' Show the control. +'' +'' Setting the [../visible] property to TRUE has the same effect. + +Public Sub Show() + + Me.Visible = True + +End + +'' Hide the control. +'' +'' Setting the [../visible] property to FALSE has the same effect. + +Public Sub Hide() + + Me.Visible = False + +End + +'' Return the custom data associated with the string ~Key~. + +Public Sub _get(Key As String) As Variant + + Dim cData As Collection + + If Not Key Then Error.Raise("Void key") + + cData = Session[$sName & ":data"] + If cData Then Return cData[Key] + +End + +'' Associate the some custom data with the string ~Key~. + +Public Sub _put(Value As Variant, Key As String) + + Dim cData As Collection + + If Not Key Then Error.Raise("Void key") + cData = Session[$sName & ":data"] + If Not cData Then cData = New Collection + cData[Key] = Value + Session[$sName & ":data"] = cData + +End + +'' Clear all custom data associated with the control + +Public Sub Reset() + + Session[$sName & ":data"] = Null + +End + + +' Public Sub _GetBaseName() As String +' +' Dim iPos As Integer +' +' iPos = InStr($sName, ".") +' If iPos Then +' Return Mid$($sName, iPos + 1) +' Else +' Return $sName +' Endif +' +' End + +Private Function Border_Read() As Boolean + + Return $bNoBorder + +End + +Private Sub Border_Write(Value As Boolean) + + If $bNoBorder <> Value Then Return + $bNoBorder = Not Value + Me._SetProperty("Border", Value) + +End + +Private Function Class_Read() As String + + Return $sClass + +End + +Private Sub Class_Write(Value As String) + + If $sClass = Value Then Return + $sClass = Value + Me._SetProperty("Class", Value) + +End + +Private Function Tooltip_Read() As String + + Return $sTooltip + +End + +Private Sub Tooltip_Write(Value As String) + + If $sTooltip = Value Then Return + $sTooltip = Value + Me._SetProperty("Tooltip", Value) + +End + +Public Sub _GetImageLink(sImage As String) As String + + If Left(sImage) = "/" Then + Return Application.Root &/ "img:" & Me.Name & "?z=" & Base64(Format(Now, "hhnnssuuu")) + Else + Return Application.Root &/ sImage + Endif + +End + + +Private Sub Name_Write(Value As String) + + SetName(Value) + +End + +'' Add a specific class to the [../class] property. + +Public Sub AddClass((Class) As String) + + Dim aClass As String[] + Dim sClass As String + + sClass = Trim(Class) + If Not sClass Then Return + + aClass = Split($sClass, " ") + + For Each sClass In Split(sClass, " ", "", True) + + If aClass.Exist(sClass) Then Continue + aClass.Add(sClass) + + Next + + Class_Write(aClass.Join(" ")) + +End + +'' Remove a specific class from the [../class] property. + +Public Sub RemoveClass((Class) As String) + + Dim aClass As String[] + Dim iPos As Integer + Dim sClass As String + + sClass = Trim(Class) + If Not sClass Then Return + + aClass = Split($sClass, " ") + + For Each sClass In Split(sClass, " ", "", True) + + iPos = aClass.Find(sClass) + If iPos < 0 Then Continue + + aClass.Remove(iPos) + + Next + + Class_Write(aClass.Join(" ")) + +End + +Private Function Font_Read() As String + + Dim aFont As New String[] + Dim sFamily As String + Dim sWeight As String + Dim sStyle As String + Dim sSize As String + Dim sDecoration As String + + If Not $hStyle Then Return + + sFamily = $hStyle["font-family"] + sWeight = $hStyle["font-weight"] + sStyle = $hStyle["font-style"] + sDecoration = $hStyle["font-decoration"] + sSize = $hStyle["font-size"] + + If sFamily Then aFont.Add(sFamily) + + If sWeight = "normal" And If sStyle = "normal" And If sDecoration = "none" Then + + aFont.Add("normal") + + Else + + If sWeight Then + If sWeight <> "bold" Then sWeight = "weight:" & sWeight + aFont.Add(sWeight) + Endif + + If sStyle Then + If sStyle <> "italic" Then sStyle = "style:" & sStyle + aFont.Add(sStyle) + Endif + + If sDecoration Then + If sDecoration = "underline" Then + aFont.Add(sDecoration) + Else If sDecoration = "line-through" Then + aFont.Add("strikeout") + Else + aFont.Add("decoration:" & sDecoration) + Endif + Endif + + Endif + + If sSize Then + If Not IsDigit(Left(sSize)) Then sSize = "size:" & sSize + aFont.Add(sSize) + Endif + + Return aFont.Join() + +End + +Private Sub Font_Write(Value As String) + + Dim sElt As String + + For Each sElt In Split(Value) + + sElt = Trim(sElt) + If Not sElt Then Continue + + If IsDigit(Left(sElt)) Then + Style_Read()["font-size"] = sElt + Continue + Endif + + If sElt Begins "weight:" Then + Style_Read()["font-weight"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + If sElt Begins "style:" Then + Style_Read()["font-style"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + If sElt Begins "decoration:" Then + Style_Read()["text-decoration"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + If sElt Begins "size:" Then + Style_Read()["font-size"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + Select Case sElt + Case "normal" + Style_Read()["font-weight"] = "normal" + Style_Read()["font-style"] = "normal" + Style_Read()["text-decoration"] = "none" + Case "bold" + Style_Read()["font-weight"] = sElt + Case "italic" + Style_Read()["font-style"] = sElt + Case "italic" + Style_Read()["font-style"] = sElt + Case "underline" + Style_Read()["text-decoration"] = sElt + Case "strikeout" + Style_Read()["text-decoration"] = "line-through" + Case Else + Style_Read()["font-family"] = sElt + End Select + + Next + +End diff --git a/comp/src/gb.web.form/.src/WebControlStyle.class b/comp/src/gb.web.form/.src/WebControlStyle.class new file mode 100644 index 00000000..a459a416 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebControlStyle.class @@ -0,0 +1,46 @@ +' Gambas class file + +Export + +Event Change + +Public _Style As New Collection + +Public Sub _get({Property} As String) As String + + Return _Style[{Property}] + +End + +Public Sub _put(Value As String, {Property} As String) + + _Style[{Property}] = Value + Raise Change + +End + +Public Sub IsVoid() As Boolean + + Return _Style.Count = 0 + +End + +Public Sub Exist({Property} As String) As Boolean + + Return _Style.Exist({Property}) + +End + + +Public Sub _GetStyleSheet() As String[] + + Dim aStyle As New String[] + Dim sValue As String + + For Each sValue In _Style + aStyle.Add(_Style.Key & ":" & sValue & ";") + Next + + Return aStyle + +End diff --git a/comp/src/gb.web.form/.src/WebExpander.class b/comp/src/gb.web.form/.src/WebExpander.class new file mode 100644 index 00000000..241ba2a5 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebExpander.class @@ -0,0 +1,109 @@ +' Gambas class file + +''' This class implements an expander container. + +Export +Inherits WebContainer + +Public Const _Properties As String = "*,Text,Hidden" +Public Const _DefaultEvent As String = "Show" +Public Const _Similar As String = "WebContainer" +Public Const _DrawWith As String = "Expander" + +'' This event is raised when the expander is opened. +Event Show +'' This event is raised when the expander is closed. +Event Hide + +'' Return or set if the expander contents is hidden. +Property Hidden As Boolean +'' Return or set the expander title. +Property Text As String +'' Return or set if the expander has a border. +Property Border As Boolean + +Private $hCont As WebContainer +Private $bHidden As Boolean +Private $sText As String +Private $bBorder As Boolean + +'' Create a new WebExpander +Public Sub _new() + + $hCont = New WebContainer(Me) + Me._Container = $hCont + +End + +Private Function Hidden_Read() As Boolean + + Return $bHidden + +End + +Private Sub Hidden_Write(Value As Boolean) + + $bHidden = Value + Me._SetProperty("Hidden", Value) + If $bHidden Then + Raise Hide + Else + Raise Show + Endif + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + Me._SetProperty("Text", Value) + +End + + +Public Sub _Render() + + If $bBorder Then Print "
    " + + Print "
    "; + Print ""; + Print "
    "; $sText; "
    "; + Print "
    " + + If Not $bHidden Then + With $hCont + ._BeforeRender() + ._Render() + ._AfterRender() + End With + Endif + + If $bBorder Then Print "
    " + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "hidden" Then Try Me.Hidden = vValue + +End + + +Private Function Border_Read() As Boolean + + Return $bBorder + +End + +Private Sub Border_Write(Value As Boolean) + + $bBorder = Value + Me._SetProperty("Border", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebForm.class b/comp/src/gb.web.form/.src/WebForm.class new file mode 100644 index 00000000..5bd30be7 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebForm.class @@ -0,0 +1,1040 @@ +' Gambas class file + +''' This class represents the different pages and dialogs of your Web application. + +Export +Create Static + +Inherits WebContainer + +Public Const _IsForm As Boolean = True +Public Const _HiddenControls As String = "WebControl,WebForm,Timer" +Public Const _Properties As String = "*,Title,Resizable,Persistent" +Public Const _DefaultEvent As String = "Open" + +'' Return or set if debugging messages are printed in the javascript console of the browser. +Static Property Debug As Boolean +'' Return or set the name of the startup form. +'' +'' By default, this is the startup class of your project. +Static Property Startup As String + +Static Public _InExec As Integer +Static Public _Current As WebForm + +Static Private $aJavascriptBefore As New String[] +Static Private $aJavascript As New String[] +Static Private $aRefresh As New String[] + +Static Private $cLibrary As New Collection + +Static Private $iDownload As Integer +'Static Private $aPreload As String[] + +'' This event is raised when the form is opened. +Event Open +'' This event is raised each time an event is handled by the form. +Event Event +'' This event is raised when the form is closed. +Event Close + +'' Return or set the form title. +'' +'' This title is used as HTML page title if the form is the main form. +Property Title As String +'' Return or set if the form is resizable when used as a dialog. +Property Resizable As Boolean +'' Return or set the minimum width of the form. +'' +'' This property can take any valid CSS size. If not set, the control will have no minimum width. +Property Width, W As String +'' Return or set the minimum height of the form. +'' +'' This property can take any valid CSS size. If not set, the control will have no minimum height. +Property Height, H As String +'' Return if the form is a popup. +Property Read Popup As Boolean +'' Return the control that owns the popup. +Property Read PopupParent As WebControl +'' Return or set if the form is persistent. +'' +'' Closing a non-persistent form automatically destroys it. +Property Persistent As Boolean + +Static Public _DisableRefresh As Integer +Static Public _HasRefresh As Boolean + +Public _Loaded As Boolean +Public _Window As Integer + +Private $aJavascriptFiles As String[] +Private $aJavascriptExternfiles As String[] + +Private $sTitle As String +Private $bResizable As Boolean +Private $sX As String +Private $sY As String +Private $sWidth As String +Private $sHeight As String + +Private $hWindowContainer As WebContainer +Private $bPersistent As Boolean +'Private $bCentered As Boolean + +Static Public Sub _init() + + System.Language = Request.Language + +End + +'' This method is called when the form is used as startup class. +'' +'' It handles all HTTP requests received by your application. + +Static Public Sub Main() + + Dim sPath As String + Dim hForm As WebForm + Dim aLib As String[] + Dim sFile As String + Dim sKey As String + Dim sErr As String + Dim sVar As String + + 'System.Log(Format(Timer, "#0.000") & ": start request: [" & Session.Id & "] " & URL.Decode(Application.Request)) + + Try Object.Call(Application.Startup, "HandleRequest") + If Response.Done Then Return + + sPath = Mid$(Request.Path, 2) + + If sPath Then + + If sPath Begins "style:" And sPath Ends ".css" Then + + aLib = Split(Left(sPath, -4), ":") + If aLib.Count >= 3 Then sFile = aLib[1] & ".css" + RenderStyleSheet(sFile) + Goto END_REQUEST + + Else If sPath Begins "lib:" And sPath Ends ".js" Then + + aLib = Split(Left(sPath, -3), ":") + aLib.Remove(aLib.Max) + 'If aLib.Count >= 3 Then sFile = aLib[1] & ".js" + RenderJavascript(aLib) + Goto END_REQUEST + + Else If sPath Begins "img:" Then + + Try sPath = Session[Mid$(sPath, 5)]["Image"] + If Not Error Then Response.SendFile(sPath) + Goto END_REQUEST + + Else If sPath Begins "download:" Then + + If DownloadFile(Mid$(sPath, 10)) Then Goto NOT_FOUND + Goto END_REQUEST + + Else If sPath = "~dump" Then + + Response.ContentType = "text/plain;charset=utf-8" + Response.Begin + + For Each sVar In Env + Print sVar; " = "; Env[sVar] + Next + + If Session.Id Then + + Print + For Each sKey In Session.Keys + Print sKey; " : "; JSON.Encode(Session[sKey]) + Next + + Print + Print "size = "; Session.Size + + Endif + + Response.End + Return + + Else If sPath = "~logout" Then + + Session.Abandon + + Else If Exist("../.public/" &/ sPath) Then + + Response.SendFile("../.public/" &/ sPath) + Goto END_REQUEST + + Else If Exist(".public/" &/ sPath) Then + + Response.SendFile(".public/" &/ sPath) + Goto END_REQUEST + + ' Else + ' + ' iPos = InStr(sPath, "/") + ' If iPos = 0 Then + ' sClass = sPath + ' sPath = "" + ' Else + ' sClass = Left(sPath, iPos - 1) + ' sPath = Mid$(sPath, iPos + 1) + ' Endif + ' + ' Try hStat = Class.Stat(".." &/ sClass) + ' If Not hStat Then + ' Try hStat = Class.Stat(sClass) + ' If Not hStat Then Goto NOT_FOUND + ' Endif + ' + ' If LCase(hStat.Parent) <> "webform" Then Goto NOT_FOUND + ' + ' Try hClass = Class.Load(sClass) + ' If Not hClass Then + ' Debug "Class.Load: "; Error.Text + ' Goto NOT_FOUND + ' Endif + ' + ' 'Main.DumpSession + ' hForm = hClass.AutoCreate() + ' + Endif + + Endif + + If Session.Id And If Session["startup"] Then + Try hForm = Class.Load(Session["startup"]).AutoCreate() + Endif + If Not hForm Then + hForm = Application.Startup.AutoCreate() + Endif + _Current = hForm + + If sPath = "x" Then + hForm._Exec(JSON.Decode(Request["c"])) + Else If sPath = "u" And If Request.Method = "POST" Then + hForm._Upload(Request["id"]) + Else + hForm.Render + Endif + + Goto END_REQUEST + +NOT_FOUND: + + Response.Status = "404 NotFound" + Response.Begin + Print "

    404 NotFound

    " + Response.End + +END_REQUEST: + + 'System.Log(Format(Timer, "#0.000") & ": end request: [" & Session.Id & "] " & URL.Decode(Application.Request)) + Return + +Catch + + sErr = Error.Text & "\n" & Error.Backtrace.Join(" ") + + Response.Cancel + Response.Begin + If sPath <> "x" Then Print "" + Response.End + 'System.Log("End request") + +End + +Static Public Sub _AddRefresh(sStr As String) + + $aRefresh.Add(sStr) + +End + +Static Public Sub _AddReplyBefore(sStr As String) + + $aJavascriptBefore.Add(sStr) + +End + +Static Public Sub _AddReply(sStr As String) + + $aJavascript.Add(sStr) + +End + + +Static Public Sub _AddJavascript(sStr As String) + + _AddReply(sStr) + +End + +Static Public Sub _AddJavascriptBefore(sStr As String) + + _AddReplyBefore(sStr) + +End + +'' Create a new WebForm +'' +'' You can embed a webform into a container. In that case the webform acts like a WebContainer. + +Public Sub _new() + + If Not Me.Parent Then + $hWindowContainer = New WebContainer(Me) + $hWindowContainer.Ignore = True + $hWindowContainer._Naked = True + $hWindowContainer.Class = "gw-window-container" + Endif + +End + +Public Sub _ready() + + If Not Me.Parent Then + Me._InitProperties + $hWindowContainer.Raise + Endif + + _Loaded = True + + 'If Me.Parent And If Then Raise Open + +End + +Public Sub _BeforeRender() + + If Me.Parent Then + Super._BeforeRender() + Return + Endif + + Print "
    " + +End + +Static Private Sub PrintJavascript() + + If $aJavascriptBefore.Count Then + Print $aJavascriptBefore.Join(";\n"); ";" + $aJavascriptBefore.Clear + Endif + + If _HasRefresh Then + _HasRefresh = False + Me._RefreshReply() + Endif + + If $aJavascript.Count Then + Print $aJavascript.Join(";\n"); ";" + $aJavascript.Clear + Endif + +End + +Public Sub _BeginJavascript() As Variant + + Dim aJavascript As String[] + Dim aJavascriptBefore As String[] + + aJavascript = $aJavascript + aJavascriptBefore = $aJavascriptBefore + + $aJavascript = New String[] + $aJavascriptBefore = New String[] + + Return [aJavascript, aJavascriptBefore] + +End + +Public Sub _EndJavascript(vSave As Variant) + + Dim aJavascript As String[] + Dim aJavascriptBefore As String[] + + aJavascript = $aJavascript + aJavascriptBefore = $aJavascriptBefore + + $aJavascript = vSave[0] + $aJavascriptBefore = vSave[1] + + If aJavascriptBefore.Count Then Print aJavascriptBefore.Join(";"); ";"; + If aJavascript.Count Then Print aJavascript.Join(";"); ";"; + +End + +'' Render the form contents to the standard output as HTML. + +Public Sub Render() + + Dim sImage As String + + Response.Buffered = True + Response.Begin + + Header.Form = Me + Header.__Render() + 'Print Header.ToString(); + Header.Form = Null + + $aJavascript.Clear + + Print "" + + Print "
    "; + For Each sImage In ["error.png", "info.png", "question.png", "warning.png"] + Print ""; + Next + Print "
    " + Print "
    " + Print "
    " + "
    " + "
    " + "
    " + "
    " + "
    " + "
    " + + Me._BeforeRender() + Me._Render() + Me._AfterRender() + + Print "" + + Print "" + Print "" + + Response.End + +End + +Static Private Sub RenderStyleSheet(Optional sFile As String) + + If Not sFile Then sFile = "style.css" + + If Not Exist(sFile) Then + Response.Status = "404 NotFound" + Response.Begin + Response.End + Return + Endif + + Response.Buffered = True + Response.ContentType = "text/css;charset=utf-8" + Response.Begin + + Print Replace(File.Load(sFile), "//", Application.Root); + + If Exist("../.public" &/ sFile) Then Print File.Load("../.public/" &/ sFile); + + Response.End + +End + +Static Private Sub RenderJavascript(aLib As String[]) + + Dim sFile As String + + Response.ContentType = "text/javascript;charset=utf-8" + Response.Begin + + For Each sFile In aLib + + sFile &= ".js" + + If sFile = "lib.js" Then + Print "$root = "; JS(Application.Root); ";" + Print File.Load(sFile) + Print File.Load("ac.js") + Else + If Exist(sFile) Then + Print File.Load(sFile) + Else If Exist(".." &/ sFile) Then + Print File.Load(".." &/ sFile) + Endif + Endif + + Next + + Response.End + +End + + +Private Function Title_Read() As String + + Return $sTitle + +End + +Private Sub Title_Write(Value As String) + + $sTitle = Value + Me._SetProperty("Title", Value) + +End + +'' Print some text to the browser javascript console. + +Static Public Sub Print(Text As String) + + $aJavascript.Add("console.log(" & JS(Text) & ")") + +End + +'' Execute some javascript into the browser. + +Static Public Sub Exec(Javascript As String) + + $aJavascript.Add(Javascript) + +End + +Public Sub _Exec(aCmd As Variant[]) + + Dim hCtrl As WebControl + Dim iOldInExec As Integer = _InExec + Dim sErr As String + 'Dim fTimer As Float + + 'fTimer = Timer + + Inc _InExec + + Raise Event + + Select Case aCmd[0] + + Case "raise" + + If aCmd[1] Then + hCtrl = WebControl.FromName(aCmd[1]) + Else + hCtrl = Me + Endif + + If Not hCtrl Then Goto VOID_RESPONSE + + Object.Raise(hCtrl, aCmd[2], aCmd[3]) + + Case "update" + + 'Debug aCmd[1];; aCmd[2];; aCmd[3] + + If aCmd[1] Then + hCtrl = WebControl.FromName(aCmd[1]) + If Not hCtrl Then Goto VOID_RESPONSE + Else + hCtrl = Me + Endif + hCtrl._UpdateProperty(aCmd[2], aCmd[3]) + + End Select + + _InExec = iOldInExec + + Response.Buffered = True + Response.ContentType = "text/javascript;charset=utf-8" + Response.Begin + PrintJavascript + 'Session.Save + 'Print "// WebForm._Exec: " & Format(Timer - fTimer, "0.000 s") + Response.End + Return + +VOID_RESPONSE: + + Response.Begin + 'Print "console.log("; JS("unknown control " & aCmd[1]); ");" + Response.End + Return + +Catch + + sErr = Error.Text & "\n" & Error.Backtrace.Join(" ") + _InExec = iOldInExec + + Response.Cancel + Response.Begin + Print "console.log("; JS(aCmd); ");" + Print "console.log("; JS(sErr); ");" + Print "console.log("; JS($aJavascript.Join("\n")); ");" + Print "alert("; JS(sErr); ");" + Response.End + +End + +'' Close the form. +'' +'' If the form was a dialog, you can specify a return value with the optional ~ReturnValue~ argument. +'' +'' That return value will be passed as an argument to the [../control/.dialog] event of the control that owns the dialog. + +Public Sub Close(Optional ReturnValue As Variant) + + ' Dim bCancel As Boolean + Dim hWindow As WebWindow + + Raise Close + + If Me._Window Then + + hWindow = WebControl.FromId(Me._Window) + With hWindow + .Close(ReturnValue) + If $bPersistent Then + .Hide + Else + .Delete + Endif + End With + + Else + + If $bPersistent Then + Me.Hide + Else + Me.Delete + Endif + _AddReply("window.location.reload(true)") + + Endif + +End + +' Public Sub Open(Child As WebForm) +' +' Dim hWindow As WebWindow +' +' If $bOpened Then Return +' +' hWindow = New WebWindow(Me) +' hWindow.Child = Child +' Child._Window = hWindow.Id +' +' $bOpened = True +' +' End + +'' Hide the form. + +Public Sub Hide() + + Dim hWindow As WebControl + + If Me.Parent Then + Super.Hide() + Return + Endif + + If _Window = 0 Then Return + + hWindow = WebControl.FromId(_Window) + If hWindow Then hWindow.Hide + +End + +Private Sub DoShow() + + Dim hWindow As WebWindow + + If _Window Then + hWindow = WebControl.FromId(_Window) + If hWindow Then hWindow.Show + Return + Endif + + hWindow = New WebWindow(WebForm._Current._GetWindowContainer()) + hWindow.Child = Me + hWindow.Resizable = $bResizable + hWindow.Move($sX, $sY, $sWidth, $sHeight) + + _Window = hWindow.Id + +End + +'' Show the form. + +Public Sub Show() + + If Me.Parent Then Return + DoShow + Raise Open + +End + +'' Show the form as a modal dialog. +'' +'' ~Parent~ is the owner of the dialog. If not specified, then the value of [/lang/last] is used as owner. +'' +'' An error is raised if you try to call this method on an embedded form. + +Public Sub ShowModal(Optional Parent As WebControl) + + Dim hWindow As WebWindow + + If Me.Parent Then Error.Raise("Form is embedded") + If _Window = 0 Then DoShow() + + hWindow = WebControl.FromId(_Window) + If hWindow Then + hWindow.Modal = True + If IsMissing(Parent) Then Try Parent = Last + If Parent Then hWindow.ParentControl = Parent.Name + Endif + + Raise Open + +End + +'' Show the form as a popup. +'' +'' - ~Control~ is the owner of the popup. It is normally the control the popup is attached to. +'' - ~Alignment~ defines the position of the popup on the screen: +'' - [Align.Left](../../align/left) : the popup is aligned to the left of its owner. +'' - [Align.Right](../../align/eight) : the popup is aligned to the right of its owner. +'' +'' By default the popup is aligned to the left of its owner. + +Public Sub ShowPopup(Control As WebControl, Optional Alignment As Integer = Align.Left) + + Dim hWindow As WebWindow + + If Me.Parent Then Error.Raise("Form is embedded") + If _Window Then Return + + hWindow = New WebWindow(WebForm._Current._GetWindowContainer()) + hWindow.Child = Me + hWindow.Resizable = $bResizable + hWindow.PopupAlignment = Alignment + hWindow.PopupControl = Control.Name + hWindow.Modal = True + + _Window = hWindow.Id + + Raise Open + +End + +'' Move and/or resize the form. +'' [[ warning +'' A main form cannot be moved nor resized. +'' ]] +Public Sub Move(X As String, Y As String, Optional Width As String, Optional Height As String) + + Dim hWindow As WebWindow = WebControl.FromId(_Window) + + $sX = X + $sY = Y + If Not IsMissing(Width) Then $sWidth = Width + If Not IsMissing(Height) Then $sHeight = Height + '$bCentered = False + + If hWindow Then hWindow.Move(X, Y, Width, Height) + +End + +'' Resize the form. +'' [[ warning +'' A main form cannot be resized. +'' ]] +Public Sub Resize(Width As String, Height As String) + + Dim hWindow As WebWindow = WebControl.FromId(_Window) + + $sWidth = Width + $sHeight = Height + '$bCentered = False + + If hWindow Then hWindow.Resize(Width, Height) + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Dim aWindows As String[] + Dim sName As String + + Select Case sProp + + Case "#windows" + aWindows = vValue + Inc _DisableRefresh + For Each sName In aWindows + Try WebControl.FromName(sName).Raise() + Next + Dec _DisableRefresh + + End Select + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + Dim hWindow As WebWindow = WebControl.FromId(_Window) + $bResizable = Value + UpdateSize + If hWindow Then hWindow.Resizable = Value + +End + +' Public Sub Center() +' +' Dim hWindow As WebWindow = WebControl.FromId(_Window) +' +' $bCentered = True +' If hWindow Then hWindow.Centered = True +' +' End + +Static Private Function Debug_Read() As Boolean + + Try Return Session["debug"] + +End + +Static Private Sub Debug_Write(Value As Boolean) + + Dim bDebug As Boolean + + Try bDebug = Session["debug"] + If Value = bDebug Then Return + + Session["debug"] = Value + _AddJavascript("gw.debug = " & JS(Value)) + +End + +Private Sub UpdateSize() + + If $bResizable Then + Me.Style["width"] = "" + Me.Style["height"] = "" + Else + Me.Style["width"] = Me.Style["min-width"] + Me.Style["height"] = Me.Style["min-height"] + Endif + +End + +Private Function Width_Read() As String + + Return Me.Style["min-width"] + +End + +Private Sub Width_Write(Value As String) + + Me.Style["min-width"] = Value + UpdateSize + +End + +Private Function Height_Read() As String + + Return Me.Style["min-height"] + +End + +Private Sub Height_Write(Value As String) + + Me.Style["min-height"] = Value + UpdateSize + +End + +Public Sub _GetWindowContainer() As WebContainer + + 'If Me.Parent Then Return Me.Form._GetWindowContainer() + Return $hWindowContainer + +End + +' Public Sub _Preload(sUrl As String) +' +' If Not $aPreload Then $aPreload = New String[] +' If $aPreload.Exist(sUrl) Then Return +' $aPreload.Add(sUrl) +' +' End +' +Static Public Sub _AddLibrary(sLib As String) + + $cLibrary[sLib] = True + +End + +Private Function Popup_Read() As Boolean + + Dim hWindow As WebWindow = WebForm.FromId(Me._Window) + If hWindow Then Return hWindow.IsPopup() + +End + +Private Function PopupParent_Read() As WebControl + + Dim hWindow As WebWindow = WebForm.FromId(Me._Window) + If hWindow Then Return WebControl.FromName(hWindow.PopupControl) + +End + +Public Sub _Upload(Id As String) + + Dim hCtrl As WebUploadArea + Dim iOldInExec As Integer = _InExec + Dim sErr As String + + Inc _InExec + + Try hCtrl = WebControl.FromName(Id) + If hCtrl Then hCtrl._UploadFinish + + _InExec = iOldInExec + + Response.Begin + PrintJavascript + Response.End + +Catch + + sErr = Error.Text & "\n" & Error.Backtrace.Join(" ") + 'Debug sErr + _InExec = iOldInExec + + Response.Begin + Print "console.log("; JS(sErr); ");" + Print "console.log("; JS($aJavascript.Join("\n")); ");" + Response.End + +End + +'' Reload the form. +'' +'' It the form is not a main form, then it is refreshed. + +Public Sub Reload() + + If Me = _Current Then + _AddReply("location.reload(true)") + Else + Me.Refresh + Endif + +End + +Public Sub _GetJavascriptFiles() As String[] + + Return $aJavascriptFiles + +End + +Public Sub _GetJavascriptExternFiles() As String[] + + Return $aJavascriptExternFiles + +End + +'' Add a javascript file to the list of javascript ressources defined by the ` + +
    + +
    +
    +
    + +
    +
    + /readme +
    + +
    +
    + +
    +

    README

    +

    Gambas Almost Means BASIC

    +

    WELCOME TO GAMBAS!

    +

    GAMBAS is a free implementation of a graphical development environment +based on a BASIC interpreter and a full development platform. It is very +inspired by Visual Basic and Java.

    +

    Go to http://gambas.sourceforge.net to get more information: how to compile +and install it, where to find binary packages, how to report a bug...

    +

    The following pieces of code were borrowed and adapted:

    +
      +
    • +

      The natural string comparison algorithme was adapted from the algorithm +made by Martin Pol. See http://sourcefrog.net/projects/natsort/ for more +details.

    • +
    • +

      The hash table implementation was adapted from the glib one.

    • +
    • +

      The HTML entities parsing in gb.gtk comes from KHTML sources.

    • +
    • +

      The gb.image.effect sources are adapted from KDE 3 image effect routines.

    • +
    • +

      The gb.clipper library embeds the Clipper library. See +http://www.angusj.com/delphi/clipper.php for mode details.

    • +
    • +

      The function that computes the easter day of a specific year uses an +algorithm made by Aloysius Lilius And Christophorus Clavius.

    • +
    • +

      The blurring algoritm is based on the StackBlur algorithm made by Mario +Klingemann. See http://incubator.quasimondo.com/processing/fast_blur_deluxe.php +for more details.

    • +
    • +

      The javascript automatic completion is done with autoComplete from Simon +Steinberger / Pixabay, and is published under the MIT license. +See https://github.com/Pixabay/JavaScript-autoComplete for more details.

    • +
    +

    If I forget some borrowed code in the list above, just tell me.

    +

    Enjoy Gambas!

    +

    Benoît.

    + +
    +
    + +
    + + diff --git a/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test2.html b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test2.html new file mode 100644 index 00000000..5e0c27cf --- /dev/null +++ b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test2.html @@ -0,0 +1,1095 @@ + + + + + + + + + + Wiki de Guygle - /search + + + + + + + +
    + + +

    Documentation de la recherche avancée

    +

    Table des matières

    + + +

    Introduction

    +

    Pour effectuer une recherche avancée dans Omogen, il faut cliquer sur le menu Recherche puis sur +l'entrée Recherche avancée....

    +

    +

    Une boîte de dialogue permettant de saisir la description de la recherche à effectuer s'affiche.

    +

    +

    Cette description suit une syntaxe précise, proche du français. Cette syntaxe est décrite dans le document présent.

    +

    Attention : des exemples de recherche sont donnés dès le début de la documentation. Il est parfois nécessaire d'avoir une vue d'ensemble pour pouvoir les comprendre complètement.

    +

    Syntaxe générale

    +

    Une recherche est constituée :

    +
      +
    • +

      D'un type d'objet.

      +
    • +
    • +

      D'un ensemble de propositions constituées :

      +
        +
      • +

        D'un mot-clef décrivant le type de recherche à effectuer.

        +
      • +
      • +

        D'une proposition décrivant les critères de cette recherche.

        +
      • +
      • +

        Éventuellement d'une sous-requête imbriquée.

        +
      • +
      +
    • +
    +

    Chaque proposition filtre le résultat de la proposition précédente (autrement dit, elles sont reliées par un ET logique).

    +

    Si le type d'objet n'est pas suivi d'un mot-clef décrivant un type de recherche précis, une recherche simplifiée est lancée.

    +

    Types d'objets

    +

    Les types d'objets correspondent au contenu de la base de données : ouvrage, intervention, opérateur, etc.

    +
    +

    Seuls les administrateurs peuvent effectuer des recherches sur les tâches planifiées, les utilisateurs et les groupes.

    +
    +

    Il est possible d'utiliser objet comme type d'objet pour effectuer une recherche sur l'ensemble des objets de la base de données.

    +

    L'héritage entre types d'objets est pris en compte. Par exemple, si les types d'objets regard, tronçon, et branchement héritent tous les trois du type d'objet ouvrage, alors une recherche portants sur des ouvrages retournera les regards, les tronçons et les branchements.

    +

    Types de recherches

    +

    Les mot-clefs qui déterminent le type de recherche à effectuer sont les suivants :

    + + + + + + + + + + +
    +

    Mot-clef

    +
    +Description +
    +parmi + +Recherche les objets dont les identifiants font partie d'une liste explicite. +
    +dont + +Effectue une recherche en fonction de la valeur d'un ou plusieurs champs. +
    +avec + +Recherche les objets liés à d'autres objets. +
    +sans + +Recherche les objets qui ne sont pas liés à d'autres objets. +
    +situé dans + +Recherche les objets situés à l'intérieur d'une ou plusieurs zones géographiques. +
    +situé à + +Recherche les objets situés à proximité d'autres objets. +
    +avec tag + +Recherche les objets possédant des étiquettes de classification. +
    +sans tag + +Recherche les objets ne possédant pas d'étiquettes de classification. +
    +

    Tolérance sur la syntaxe

    +

    La syntaxe d'une recherche est insensible :

    +
      +
    • +

      À la différence entre majuscules et minuscules.

      +
    • +
    • +

      Aux accents.

      +
    • +
    • +

      Au genre.

      +
    • +
    • +

      Au nombre.

      +
    • +
    • +

      Aux articles définis ou indéfinis : le, la, les, un, des, au, de, du, etc.

      +
    • +
    +

    Cela signifie par exemple que les types d'objets peuvent être écrits indifféremment au singulier ou au pluriel.

    +

    Cela signifie aussi que vous pouvez librement accorder les mots-clefs, les articles, etc.

    +
    +
    Exemple
    +

    Les deux recherches suivantes sont équivalentes :

    +
    opérateurs dont l'état est connecté
    +
    +
    operateur dont etat est connecte
    +
    +
    +

    Par contre les noms de champs sont sensibles au nombre.

    +
    +
    Exemple
    +

    Les deux recherches suivantes sont différentes :

    +
    opérateurs dont les interventions sont renseignées
    +
    +
    opérateurs dont l'intervention est renseignée
    +
    +
    +

    Tolérance sur les valeurs

    +

    Les valeurs des champs sont sensibles à la casse et aux accents, sauf lorsqu'on utilise le mot-clef est ou n'est pas. +Dans ce cas, la comparaison est insensible à la casse et aux accents.

    +
    +
    Exemple
    +

    Les recherches suivantes sont identiques :

    +
    type d'ouvrage dont nom est fosse
    +type d'ouvrage dont nom est fossé
    +type d'ouvrage dont nom est Fosse
    +type d'ouvrage dont nom est FOSSÉ
    +
    +

    Les recherches suivantes sont différentes :

    +
    type d'ouvrage dont nom est égal à fosse
    +type d'ouvrage dont nom est égal à fossé
    +type d'ouvrage dont nom est égal à Fosse
    +type d'ouvrage dont nom est égal à FOSSÉ
    +
    +
    +

    Utiliser un mot-clef comme valeur

    +

    Pour empêcher Omogen d'interpréter un élément comme mot-clef, ou bien pour rechercher un élément contenant des espaces, il est nécessaire +d'entourer l'élément avec des guillemets.

    +

    Si l'élément dont contenir lui-même un guillemet, il faut alors le doubler.

    +
    +
    Exemple
    +
    client dont le nom est "EST ENSEMBLE"
    +
    +
    opérateur dont le nom est "John ""The Ripper"""
    +
    +
    +
    +

    Les guillemets délimitent les valeurs recherchées.

    +

    Autrement dit, si vous n'utilisez pas de guillemets, Omogen considérera que la valeur recherchée est constituée par l'ensemble des mots +suivant l'opérateur, jusqu'au mot-clef suivant.

    +

    Ainsi :

    +
    opérateur dont le nom est "John The Ripper"
    +
    +

    et

    +
    opérateur dont le nom est John The Ripper
    +
    +

    sont équivalents.

    +

    Par contre :

    +
    opérateur dont le nom est "John The" Ripper
    +
    +

    ne fonctionnera pas. L'élément recherché sera John The. Ripper sera ignoré et provoquera une erreur de syntaxe.

    +
    +

    Recherche simplifiée

    +

    Recherche d'un objet dont on connaît l'identifiant

    +

    On peut accéder à un objet rapidement dès l'instant où l'on connaît son identifiant, +en utilisant la syntaxe suivante :

    +
    <type d'objet> <identifiant>
    +
    +
    +
    Exemple
    +
    intervention I.000037
    +
    +

    Retourne l'intervention I.000037 immédiatement.

    +
    +

    Il est possible de demander plusieurs objets en séparant leur identifiant par une virgule :

    +
    <type d'objet> <identifiant 1>,<identifiant 2>,...,<identifiant n>
    +
    +
    +
    Exemple
    +
    intervention I.000001,I.000002,I.000037,I.000004
    +
    +

    Retourne les interventions I.000001, I.000002, I.000037 et I.000004 immédiatement.

    +
    +
    +

    Il est possible d'utiliser le mot-clef parmi pour effectuer le même genre de recherche (voir plus loin).

    +
    +
    +

    Dans le cas d'une recherche portant sur les utilisateurs, le mot-clef MOI correspond à l'identifiant de l'utilisateur courant.

    +
    +

    Recherche d'objet par mots-clefs

    +

    Cette recherche suit la syntaxe suivante:

    +
    <type d'objet> <mot 1> <mot 2> ... <mot n>
    +
    +

    Elle consiste à rechercher tous les objets du type spécifié dont la description contient un des mots spécifié après le type d'objet.

    +
    +

    La description d'un objet est ce que vous voyez apparaître dans les tableaux.

    +
    +
    +
    Exemple
    +

    Si on effectue ainsi la recherche suivante :

    +
    intervention robinetterie
    +
    +

    Les interventions suivantes seront retournées :

    +
    I.000006 I.000013 I.000018 I.000018 I.000024 I.000035
    +
    +

    Il s'agira de chaque intervention contenant le mot robinetterie n'importe où dans la ligne de tableau la décrivant.

    +
    +

    Recherche d'objets parmi une liste d'identifiants avec parmi

    +

    Le mot-clef parmi permet de rechercher les objets à partir d'une liste d'identifiants. +La syntaxe est la suivante :

    +
    <type d'objet> parmi <identifiant 1>,<identifiant 2>,...,<identifiant n>
    +
    +
    +
    Exemple
    +
    interventions parmi I.000001,I.000002,I.000037,I.000004
    +
    +

    Retourne les interventions I.000001, I.000002, I.000037 et I.000004 immédiatement.

    +
    +

    Si la liste d'identifiants est le mot-clef quelconque, la totalité des objets du type spécifié est retourné.

    +

    Si la liste d'identifiants est le mot-clef rien, aucun objet n'est retourné.

    +

    Les deux dernières syntaxes sont surtout utiles lorsqu'on construit des recherches au sein des fichiers de descriptions de classe d'Omogen.

    +

    Recherche en fonction de la valeur d'un champ

    +

    Le mot-clef dont introduit une recherche en fonction de la valeur d'un ou plusieurs champs.

    +

    La syntaxe est la suivante :

    +
    <type d'objet> dont <nom de champ> <critère de comparaison> [ et <nom de champ> <critère de comparaison> ... ]
    +
    +
    +
    Exemple
    +
    interventions dont l'état est en cours
    +
    +

    Retourne l'ensemble des interventions dont le champ "état" vaut "En cours".

    +
    +

    Le mot-clef et permet de cumuler plusieurs critères :

    +
    <type d'objet> dont <nom de champ> <critère de comparaison> [ et <nom de champ> <critère de comparaison> ... ]
    +
    +
    +
    Exemple
    +
    interventions dont l'état est en cours et la date planifiée est le 6/9/2011
    +
    +

    Retourne l'ensemble des interventions dont l'état est "En cours" et dont la date planifiée est le 6 septembre 2011.

    +
    +

    Champs

    +

    Une comparaison peut porter sur n'importe quel champ défini dans la fiche de l'objet. Le nom du champ est celui utilisé à l'affichage.

    +

    La comparaison peut en outre porter sur les champs spéciaux suivants:

    +
      +
    • +

      date de modification : la date à laquelle l'objet a été modifié pour la dernière fois.

      +
    • +
    • +

      date de création : la date à laquelle l'objet a été créé.

      +
    • +
    • +

      créateur : l'utilisateur ayant créé l'objet.

      +
    • +
    • +

      modificateur : l'utilisateur ayant modifié l'objet en dernier.

      +
    • +
    +
    +
    Exemple
    +
    ouvrages dont la date de modification est hier
    +
    +

    Retourne la liste des ouvrages modifiés hier uniquement, et pas aujourd'hui.

    +
    +

    Critères de comparaisons généraux

    +

    Les critères de comparaison généraux peuvent porter sur n'importe quel champ. Ils sont décrits dans le tableau suivant :

    + + + + + + + + + + + + +
    +

    Critère

    +
    +Syntaxe + +Signification + +Exemple +
    +est
    +sont +
    +
    est <valeur> [ ou <valeur> ... ]
    +
    +
    +Teste si un champ est égal à la valeur spécifiée, ou bien à l'une des valeurs spécifiée si le mot-clef ou est utilisé. +

    Attention : la comparaison est insensible à la casse et aux accents !

    +
    +
    interventions dont l'état est en cours ou en attente
    +
    +
    +n'est pas
    +ne sont pas
    +n'est ni
    +ne sont ni +
    +
    n'est pas <valeur> [ ou <valeur> ... ]
    +
    +
    +Teste si un champ est différent de la valeur spécifiée, ou bien n'est aucune des valeurs spécifiées si le mot-clef ou est utilisé. +

    Attention : la comparaison est insensible à la casse et aux accents !

    +
    +
    interventions dont l'état n'est pas clôturée
    +
    +
    +contient + +
    contient <valeur> [ ou <valeur> ... ]
    +
    +
    +Teste si un champ contient la valeur spécifiée, ou bien une des valeurs spécifiée si le mot-clef ou est utilisé. +

    Attention : la comparaison est insensible à la casse et aux accents !

    +
    +
    ouvrages dont l'adresse contient "de gaulle"
    +
    +
    +ne contient pas + +
    ne contient pas <valeur> [ ou <valeur> ... ]
    +
    +
    +Teste si un champ ne contient pas la valeur spécifiée, ou bien aucune des valeurs spécifiées si le mot-clef ou est utilisé. +

    Attention : la comparaison est insensible à la casse et aux accents !

    +
    +
    ouvrages dont l'adresse ne contient pas "général de gaulle" ou "gal de gaulle"
    +
    +
    +commence par + +
    commence par <valeur> [ ou <valeur> ... ]
    +
    +
    +Teste si un champ commence par la valeur spécifiée, ou bien par une des valeurs spécifiées si le mot-clef ou est utilisé. +

    Attention : la comparaison est insensible à la casse et aux accents !

    +
    +
    ouvrages dont l'adresse commence par avenue
    +
    +
    +ne commence pas par + +
    ne commence pas par <valeur> [ ou <valeur> ... ]
    +
    +
    +Teste si un champ ne commence pas par la valeur spécifiée, ou bien par aucune des valeurs spécifiées si le mot-clef ou est utilisé. +

    Attention : la comparaison est insensible à la casse et aux accents !

    +
    +
    ouvrages dont l'adresse ne commence par impasse
    +
    +
    +est renseigné
    +n'est pas vide +
    +
    est renseigné
    +
    +
    +Teste si un champ est renseigné. + +
    interventions dont la description est renseignée
    +
    +
    +n'est pas renseigné
    +est vide +
    +
    n'est pas renseigné
    +est vide
    +
    +
    +Teste si un champ n'est pas renseigné. + +
    ouvrages dont l'adresse n'est pas renseignée
    +
    +
    +existe + +
    existe
    +
    +
    +Teste si un champ pointe sur un objet existant. + +
    interventions dont le client existe
    +
    +
    +n'existe pas +est inexistant + +
    n'existe pas
    +
    +
    +Teste si un champ pointe sur un objet inexistant. + +
    interventions dont le client n'existe pas
    +
    +
    +

    Critères de comparaison textuels

    +

    Les critères de comparaison textuels portent sur les champs uniquement textuels. Ils sont décrits dans le tableau suivant :

    + + + +
    +ressemble à + +
    ressemble à <texte>
    +
    +
    +Teste si un champ est phonétiquement équivalent à la valeur spécifiée. + +
    ouvrage dont adresse ressemble à "général de gaulle"
    +
    +
    +ne ressemble pas à + +
    ne ressemble pas à <texte>
    +
    +
    +Teste si un champ n'est pas phonétiquement équivalent à la valeur spécifiée. + +
    ouvrage dont adresse ne ressemble pas à "général de gaulle"
    +
    +
    +
    +

    Le test de ressemblance phonétique ne fonctionne que sur des champs explicitement paramétrés comme tels dans l'application.

    +
    +

    Critères de comparaison numériques

    +

    Les critères de comparaison numériques portent sur les champs contenant des valeurs numériques ou bien des dates. +Ils sont décrits dans le tableau suivant :

    + + + + + + + + + + +
    +

    Critère

    +
    +Syntaxe + +Signification + +Exemple +
    +est égal(e) à
    +sont égaux(égales) à +
    +
    est égal(e) à <valeur>
    +
    +
    +Teste si un champ numérique ou une date est égal à la valeur spécifiée. + +
    interventions dont la date planifiée est le 06/09/2011
    +
    +
    +est différent(e) de
    +sont différent(e)s de +
    +
    est différent de <valeur>
    +
    +
    +Teste si un champ numérique ou une date est différente de la valeur spécifiée. + +
    dossiers dont le nombre d'appels est différent de 2
    +
    +
    +est supérieur(e) à
    +sont supérieur(e)s à +
    +
    est supérieur à <valeur>
    +
    +
    +Teste si un champ numérique ou une date est supérieur à la valeur spécifiée. + +
    dossiers dont le nombre d'appels est supérieur à 1
    +
    +
    +est supérieur(e) ou égal(e) à
    +sont supérieur(e)s ou égaux(égales) à +
    +
    est supérieur à <valeur>
    +
    +
    +Teste si un champ numérique ou une date est supérieur ou égal à la valeur spécifiée. + +
    dossiers dont le nombre d'appels est supérieur ou égal à 2
    +
    +
    +est inférieur(e) à
    +sont inférieur(e)s à +
    +
    est inférieur à <valeur>
    +
    +
    +Teste si un champ numérique ou une date est inférieur à la valeur spécifiée. + +
    interventions dont la date planifiée est inférieure au 06/09/2011
    +
    +
    +est inférieur(e) ou égal(e) à
    +sont inférieur(e)s ou égaux(égales) à +
    +
    est inférieur ou égal à <valeur>
    +
    +
    +Teste si un champ numérique ou une date est inférieur ou égal à la valeur spécifiée. + +
    interventions dont la date planifiée est inférieure ou égale au 05/09/2011
    +
    +
    +est compris entre + +
    est compris entre <valeur minimale> et <valeur maximale>
    +
    +
    +Teste si un champ numérique ou une date est compris entre les valeur spécifiées, bornes incluses. + +
    interventions dont la date planifiée est comprise entre le 01/01/2010 et le 31/12/2010
    +
    +
    +est quelconque + +
    est quelconque
    +
    +
    +Permet de rédiger une condition toujours vraie. Le mot-clef quelconque peut être ainsi utilisé comme +valeur par défaut dans un critère de recherche présenté à l'utilisateur sous forme de liste. + +
    interventions dont le client est quelconque
    +
    +
    +
    +

    Vous pouvez utiliser les critères est égal à et est différent de avec des champs de type chaîne de caractères. +Dans ce cas la comparaison est sensible à la casse et aux accents.

    +
    +

    Syntaxe des valeurs numériques

    +

    Les nombres peuvent être saisis :

    +
      +
    • +

      Sous forme numérique, avec une virgule ou un point comme séparateur décimal.

      +
    • +
    • +

      En français, s'ils sont compris entre zéro et vingt.

      +
    • +
    +
    +
    Exemple
    +
    dossiers dont le nombre d'appels est supérieur à cinq
    +regards dont la profondeur est supérieure ou égale à 3,5
    +
    +
    +

    Syntaxe des dates

    +

    Les dates doivent être saisies au format français : <jour>/<mois>/<année>.

    +

    Par exemple: 01/01/2010, 06/09/2011...

    +

    Il est possible d'utiliser les mots-clefs suivants qui représentent des périodes.

    +

    Dans ce cas, la recherche ne portera pas sur une date exacte, mais sur la plage temporelle associée au mot-clef, comme pour une recherche « est compris entre ... et ... ».

    + + + + +
    +

    Mot-clef

    +
    +Période correspondante +
    +le mois de XXXX + +Du premier jour du mois spécifié à son dernier jour. +
    +l'année XXXX + +Du 1er janvier de l'année spécifiée au 31 décembre de cette même année. +
    +

    Les mots-clefs suivants représentent des dates relatives :

    + + + + + + + +
    +

    Mot-clef

    +
    +Période correspondante +
    +aujourd'hui + +De 00h00 à l'heure courante. +
    +maintenant + +De l'heure courante jusqu'au lendemain même heure. +
    +hier + +Hier, de 00h00 à minuit. +
    +avant-hier + +Avant-hier, de 00h00 à minuit. +
    +demain + +Demain, de 00h00 à minuit. +
    +
    +
    Exemple
    +
    interventions dont la date planifiée est demain
    +
    +
    +

    Les mots-clefs suivant représentent des intervalles de dates relatifs:

    + + + + + + + + + + + + + + +
    +

    Mot-clef

    +
    +Période correspondante +
    +cette semaine + +Du lundi de la semaine en cours 00h00 jusqu'au dimanche soir minuit. +
    +la semaine dernière
    +la semaine précédente +
    +Du lundi de la semaine dernière 00h00 jusqu'au dimanche soir minuit. +
    +la semaine prochaine
    +la semaine suivante +
    +Du lundi de la semaine prochaine 00h00 jusqu'au dimanche soir minuit. +
    +ce mois
    +ce mois-ci +
    +Du premier jour du mois en cours 00h00 jusqu'au dernier jour du mois minuit. +
    +le mois dernier
    +le moins précédent +
    +Du premier jour du mois dernier 00h00 jusqu'au dernier jour minuit. +
    +le mois prochain
    +le mois suivant +
    +Du premier jour du mois prochain 00h00 jusqu'au dernier jour minuit. +
    +ce trimestre + +Du premier jour du trimestre en cours 00h00 jusqu'au dernier jour du trimestre minuit. +
    +le trimestre dernier
    +le trimestre précédent +
    +Du premier jour du trimestre dernier 00h00 jusqu'au dernier jour de ce trimestre minuit. +
    +le trimestre prochain
    +le trimestre suivant +
    +Du premier jour du trimestre suivant 00h00 jusqu'au dernier jour de ce trimestre minuit. +
    +cette année + +Du 1er janvier de cette année 00h00 jusqu'au 31 décembre minuit. +
    +l'année dernière
    +l'année précédente +
    +Du 1er janvier de l'année dernière 00h00 jusqu'au 31 décembre minuit. +
    +l'année prochaine
    +l'année suivante +
    +Du 1er janvier de l'année prochaine 00h00 jusqu'au 31 décembre minuit. +
    +
    +
    Exemple
    +
    évènements dont la date est ce mois
    +
    +
    +

    Il est possible aussi d'utiliser les syntaxes suivantes :

    + + + + +
    +

    Mot-clef

    +
    +Période correspondante +
    +il y a <valeur> <période> + +Période spécifiée dans le passé par rapport à maintenant. +
    +dans <valeur> <période> + +Période spécifiée dans le futur par rapport à maintenant. +
    +

    Les différentes périodes possibles sont : minute(s), heure(s), jour(s), semaine(s), mois(s), an(s) ou années(s).

    +
    +

    Pour les périodes minute et heure, le période commencera (ou terminera) à la date et heure courante. Pour les autres périodes, elle commencera (ou terminera) aujourd'hui à 00h00.

    +
    +
    +
    Exemple
    +
    évènements dont la date est il y a 6 mois
    +évènements dont la date est dans une semaine
    +
    +
    +

    Recherche d'objets liés

    +

    Le mot-clef avec

    +

    Le mot-clef avec introduit une recherche d'objets liés avec d'autres objets.

    +

    Elle permet de rechercher un ensemble d'objets à l'aide une requête normale, puis retourne les objets qui lui sont reliés par l'intermédiaire d'un champ donné.

    +

    Par exemple, les interventions associées aux opérateurs dont le métier est la plomberie.

    +

    La syntaxe est la suivante :

    +
    <type d'objet> avec <nom du champ contenant le lien> <sous-requête>
    +
    +
    +

    ATTENTION ! ce qui suit le mot-clef avec n'est pas un type d'objet, mais le nom du champ contenant les liens. +Il est donc sensible au genre et au nombre (mais pas aux accents ni à la casse). Heureusement, dans la plupart des cas, +le nom de champ est le nom du type d'objet lié au pluriel.

    +
    +

    La sous-requête peut être de n'importe quel type : requête portant sur un champ introduite par le mot-clef dont, +une autre requête d'objets liés introduite par le mot-clef avec, etc.

    +
    +
    Exemple
    +
    interventions avec opérateur dont le prénom est Jean-Claude et le nom est Dusse
    +
    +

    Cette requête recherche toutes les interventions affectées à l'opérateur Jean-Claude Dusse. Notez que opérateur est obligatoirement au singulier, car il s'agit d'un nom de champ.

    +
    ordre travail avec interventions dont l'état est annulée
    +
    +

    Cette recherche retourne toutes les interventions ayant au moins une intervention annulée.

    +
    campagnes avec objets RU.123
    +
    +

    Cette recherche retourne les campagnes qui ont eu lieu sur l'ouvrage d'identifiant RU.123.

    +
    intervention avec ordre de travail
    +
    +

    Cette recherche retourne les interventions qui ne sont pas orphelines (c'est-à-dire rattachées à un ordre de travail).

    +
    +

    Le mot-clef avec éventuellement

    +

    Il s'agit d'une recherche ayant la même syntaxe et retournant le même résultat qu'une recherche « avec », à ceci prêt que la recherche retourne en plus les objets n'ayant aucune association.

    +
    +
    Exemple
    +
    opérateur dont agence est AG.ASAP avec éventuellement activités ACT.INF
    +
    +

    Cette recherche retourne les opérateur de l'agence AG.ASAP associés à l'activité ACT.INF, ainsi que les opérateurss de l'agence AG.ASAP qui ne sont associés à aucune activité.

    +
    +

    Le mot-clef sans

    +

    Le mot-clef sans permet de rechercher les objets n'ayant aucune association avec d'autres objets.

    +

    Cette recherche utilise exactement la même syntaxe que celle introduite par le mot-clef avec.

    +
    +
    Exemple
    +
    interventions sans ordre de travail
    +
    +

    Cette recherche retourne les intervention orphelines (sans ordre de travail).

    +
    ordre de travail sans interventions
    +
    +

    Cette recherche retourne les ordres de travail n'ayant encore aucune intervention.

    +
    ordre de travail sans interventions dont état est non réalisable.
    +
    +

    Cette recherche retourne les ordres de travail n'ayant aucune intervention non réalisable.

    +
    +

    Recherches imbriquées

    +

    Les mots-clefs avec et sans introduise une recherche imbriquée.

    +

    Une question se pose alors si cette recherche imbriquée est suivie d'une autre partie de la recherche introduite par un nouveau mot-clef : cette partie de la recherche porte-t-elle sur la recherche imbriquée ou bien sur la recherche globale ?

    +

    Par défaut, tout ce qui suit une recherche imbriquée porte sur cette recherche imbriquée et pas sur la recherche globale.

    +
    +
    Exemple
    +
    client avec agences avec activités dont gestion des ressources est "Oui"
    +
    +

    retourne les clients associés aux agences associées aux activités dont la gestion des ressources est activée.

    +
    +

    Si l'on veut que ce qui suive la recherche imbriquée porte sur la recherche précédente, il faut entourer la recherche imbriquée avec des crochets, de la manière suivante :

    +
    +
    Exemple
    +
    client avec agences avec activités [ dont gestion des ressources est "Oui" ] dont service d'astreinte est "Oui"
    +
    +

    retourne les clients qui sont associés aux agences avec un service d'astreinte, qui sont associées aux activités dont la gestion des ressources est activée.

    +

    Si on n'avait pas mis les crochets, la partie dont service d'astreinte est "Oui" aurait porté sur les activités des agences.

    +
    +

    On peut imbriquer les crochets :

    +
    +
    Exemple
    +
    client avec agences [ avec activités [ dont gestion des ressources est "Oui" ] dont service d'astreinte est "Oui" ] dont la société mère est "VEOLIA"
    +
    +

    retourne les clients dont la société mère est Veolia, et qui sont associés aux agences avec un service d'astreinte associées aux activités dont la gestion des ressources est activée.

    +

    Si on n'avait pas mis les crochets, la partie dont la société mère est "VEOLIA" aurait porté soit sur les activités des agences, soit sur les agences (en fonction de quels crochets on supprime).

    +
    +

    Recherche géographique

    +

    Les recherches géographiques permettent de retrouver les objets :

    +
      +
    • +

      situés dans une zone donnée.

      +
    • +
    • +

      situés à proximité d'un ou plusieurs d'objets données.

      +
    • +
    +

    Recherche au sein d'une zone

    +

    Le mot-clef situé dans introduit la recherche au sein d'une zone.

    +

    La syntaxe est la suivante :

    +
    <type d'objet> situé dans <sous-requête>
    +
    +

    La sous-requête peut être n'importe quelle type de recherche. Elle doit retourner un ou plusieurs objets de type "secteur".

    +

    La requête principale retournera alors l'ensemble des objets situés dans l'ensemble des objets de type "secteur" retourné par la sous-requête.

    +

    Recherche de proximité

    +

    Le mot-clef situé à introduit une recherche de proximité.

    +

    La syntaxe est la suivante :

    +
    <type d'objet> situé à <distance> de <sous-requête>
    +
    +

    La sous-requête peut être n'importe quelle type de recherche.

    +

    La requête principale retournera l'ensemble des objets situés autour des objets retournés par la sous-requête, à une distance inférieure ou égale à la distance spécifiée.

    +

    La distance est constitué d'une valeur numérique suivie d'une unité : m ou km.

    +

    Recherches sur les étiquettes

    +

    Les mots-clefs avec tag et sans tag permettent d'effectuer des recherches d'objet associés à des étiquettes de classification.

    +

    La syntaxe est la suivante :

    +
    <type d'objet> avec tag <étiquette n°1> [ ou <étiquette n°2> ... ]
    +<type d'objet> sans tag <étiquette n°1> [ ou <étiquette n°2> ... ]
    +
    +

    avec tag recherche les objets qui possèdent les étiquettes spécifiées. sans tag les objets ne possédant pas les étiquettes spécifiées.

    +

    Les étiquettes sont indiquées en utilisant le format interne d'Omogen :

    +
    <Parent> | <Groupe> : <Nom de l'étiquette>
    +
    +
    +
    Exemple
    +
    intervention avec tag "Couleur:Bleu" ou "Couleur:Rouge"
    +intervention avec tag "Accident animal|Type d'animal:Grand gibier"
    +
    +
    +

    Recherches spéciales

    +

    Recherche sans critère

    +

    Le mot-clef quelconque peut être utilisé pour faire une recherche de tous les objets d'une classe.

    +
    +
    Exemple
    +
    ordre de travail quelconque
    +
    +

    Retournera tous les ordres de travail de la base.

    +

    C'est équivalent à utiliser

    +
    ordre de travail
    +
    +

    seul comme recherche.

    +
    +

    Recherche générique

    +

    La recherche générique permet d'effectuer une recherche textuelle sur l'ensemble de la base de données.

    +

    Elle est déclenchée dès lors que le texte complet de la recherche est écrit entre guillements :

    +
    "<recherche textuelle>"
    +
    +

    Le texte contenu entre guillemets est alors recherché dans la base de données. +Les objets dont la fiche contient au moins une occurence de ce texte sont retournés.

    +

    Il est possible d'utiliser le caractère spécial * dans le texte recherché. Il signifie alors "n'importe quel texte".

    +

    La recherche est insensible à la casse.

    +
    +

    Attention : ce type de recherche peut-être très lent, car il est toujours proportionnel à la taille de la base de données.

    +

    Donc évitez autant que possible ce type de recherche !

    +
    +

    Raccourcis de recherche

    +

    Certains mots-clefs servent de raccourci à certaines recherches.

    + + + + +
    +

    Mot-clef

    +
    +Exemple + +Signification + +Correspond à la recherche +
    +actif / active + +<type d'objet> actif + +Recherche d'objets actifs + +<type d'objet> dont activé n'est pas Non +
    +inactif / inactive + +<type d'objet> inactif + +Recherche d'objets désactivés + +<type d'objet> dont activé est Non +
    +

    Présentation du résultat

    +

    Le résultat d'une recherche est, par défaut, trié dans l'ordre croissant des identifiants internes.

    +
    +

    Le tri est dit "naturel". C'est-à-dire qu'il est insensible à la casse, et qu'en outre les éventuels nombres +présents à l'intérieur de l'identifiant sont triés par ordre numérique et non alphabétique.

    +
    +
    +
    Exemple
    +

    INT.8, INT.9, INT.10, INT.11

    +

    et non INT.10, INT.11, INT.8, INT.9 qui serait un tri alphabétique classique.

    +
    +

    Tri par ordre décroissant

    +

    Par défaut, le tri est toujours par ordre croissant.

    +

    Il est possible d'inverser l'ordre du résultat et d'obtenir un tri par ordre décroissant en ajoutant (en ordre inverse) ou (par ordre inverse) à la fin de la recherche.

    +

    Tri par rapport à un champ donné

    +

    Il est possible de trier le résultat de la recherche en fonction de la valeur d'un champ donné.

    +

    Pour cela, il faut rajouter à la fin de la recherche (par <champ>) ou <champ> est le nom du champ de tri.

    +

    Il est possible en outre de spécifier si le tri est croissant ou décroissant en ajoutant le terme croissant ou décroissant après le nom du champ et avant la parenthèse fermante.

    +

    Par défaut, le tri est par ordre croissant.

    + + + + + diff --git a/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test3.html b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test3.html new file mode 100644 index 00000000..99f66143 --- /dev/null +++ b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test3.html @@ -0,0 +1,19 @@ + + + + + + +

    Ceci est le titre

    +

    Sous-titre

    +

    Encore un titre

    +

    coucou

    +
    Y a du padding ou pas ? x2
    +
    + test +
    +
    +
    + + + diff --git a/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test4.html b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test4.html new file mode 100644 index 00000000..5e53be31 --- /dev/null +++ b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test4.html @@ -0,0 +1,86 @@ + + + + + + + +

    The cursor Property

    +

    Mouse over the words to change the mouse cursor.

    + +

    alias

    +

    all-scroll

    +

    auto

    +

    cell

    +

    context-menu

    +

    col-resize

    +

    copy

    +

    crosshair

    +

    default

    +

    e-resize

    +

    ew-resize

    +

    grab

    +

    grabbing

    +

    help

    +

    move

    +

    n-resize

    +

    ne-resize

    +

    nesw-resize

    +

    ns-resize

    +

    nw-resize

    +

    nwse-resize

    +

    no-drop

    +

    none

    +

    not-allowed

    +

    pointer

    +

    progress

    +

    row-resize

    +

    s-resize

    +

    se-resize

    +

    sw-resize

    +

    text

    +

    url

    +

    w-resize

    +

    wait

    +

    zoom-in

    +

    zoom-out

    + + + \ No newline at end of file diff --git a/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test5.html b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test5.html new file mode 100644 index 00000000..6f32caaf --- /dev/null +++ b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test5.html @@ -0,0 +1,154 @@ + + + + + + + + + / - Gambas Documentation + + + + + + +
    +
    + + + +
    + +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + + +
    + + + +
    + + + +

    Gambas Documentation

    + + +
    +README    + +Wiki License    + +Wiki syntax    + +To Do    + +Last Changes    + +Gambas Web Site    + +Bug Tracker +
    +

    Language Reference

    +

    +Language Index
    +Language Overviews
    +Native Classes
    +GUI Classes
    +Components
    +
    +Error Messages
    +Lexicon
    +The Gambas Object Model +

    +

    Documents

    +

    Here is a list of documents and topics that may help you.

    +

    +Release Notes
    +Compilation & Installation
    +Reporting a problem, a bug or a crash
    +Frequently Asked Questions
    +Development Environment Documentation
    +Some Little Benchmarks +
    +Differences From Visual Basic
    +Tutorials Not finished.
    +Application Repository
    +Code Snippets
    +How To's +

    +

    Developers

    +

    This is the documentation for component developers.

    +

    +How To Contribute
    +How To Deal With Git and Gitlab
    +Additional Gambas Components
    +
    +How To Program Components In Gambas
    +How To Program Components In C/C++
    +Gambas Programming Interface +

    +

    Translators

    +

    +How To Translate A Gambas Project +

    +

    Packagers

    +

    How To Package Gambas

    +

    +This documentation is based on a Wiki written in Gambas.
    +If you want to modify some pages, you must register on the bugtracker.
    +Once done, you must learn the Gambas Wiki Markup Syntax.

    +

    This documentation is under the Creative Commons Attribution-ShareAlike (CC-BY-SA) 3.0 license. +

    +

    + + +
    + +
    + +
    +
    + + + + diff --git a/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test6.html b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test6.html new file mode 100644 index 00000000..7e763870 --- /dev/null +++ b/gb.form.htmlview/src/gb.form.htmlview/.hidden/test/test6.html @@ -0,0 +1,1640 @@ + + + + + the extinction of pi by Miles Mathis + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    +


    +

    +
    +

    return + to homepage

    The + Extinction of π

    by + Miles Mathis


    bye-bye + π

    If you get lost at any point in this paper
    you may + go to the short version

    +


    First + posted September 9, 2008

    +


    Abstract: + I show that in all kinematic situations, π is 4. For all those + going ballistic over my title, I repeat and stress that this + paper applies to kinematic situations, not to static or geometric + situations. I am analyzing the equivalent of an orbit, which is + caused by motion and includes the time variable. In that + situation, π becomes 4. I will also remind you this is not just + a theory: it has been indicated by many mainstream experiments, + including rocketry tests and quantum experiments (see links + below). It has also now been proven by my own experiments (see + link below).
    +

    +



    +

    +

    [May + 1, 2018: + This paper has now been up for almost ten years. In that time, it + has become somewhat overgrown with addenda and other links, which + I have added as my argument has advanced. Readers soon found + confirmation wherever they looked, and I have added those links + to bolster my thesis. I have made some effort to clean up the + paper recently, though most will still find it to be—how shall + I put it—overrich. It is a long and complex question, and as + usual I have been exhaustive in researching it. One critic called + this paper “discursive”, though he doesn't seem to know what + the word means. Nothing I say below is off-subject or digressive. + The problem of π touches on many other questions, and can't be + discussed in any depth without hitting those questions at some + length. +

    +

    Whatever + these planted critics may be paid to say, the subject is now + pretty much settled. No one who studies my arguments can fail to + recognize that I discovered something very important here, so we + must assume the people trolling me on the web are agents of some + sort, wasting our time with low-level debating tricks. In the ten + years this paper has been up, I have yet to see a reply or + debunking worth addressing.** None of them even appear serious on + the surface, and none are from recognized people in any field. + Most are from anonymous sources. This is to be expected, I + suppose, since it is difficult to debunk something that is + obviously true. The standard method of debunking the truth—the + method we see at places like Snopes, for instance, or other + mainstream spook fronts—is with low-level character + assassination, couched lies, and other oily subterfuge. We have + certainly seen that in the mainstream response to me from the + beginning, but we have seen little else. I am only telling you + this because Google seems to be in on the game. Any search on me + is littered in the first pages with this misdirection, and + someone who didn't know any better would assume I really am + widely considered to be a crank. I'm not, as you can see here. + Probably more of my papers (on both sites) have gone viral than + the papers of anyone else in history, though you have to dig a + bit to figure that out. My science papers are being mobbed by + mainstream physicists as well as by interested amateurs, + including this paper. In fact, I have it from inside sources that + the mainstream has already begun correcting its kinematic + equations that include π, though they are of course not publicly + admitting it. Apparently they are trying to figure out how to + proceed without giving me any credit. What they don't understand + is that it doesn't matter: I know and they know and they can't + change that. They can't suck the information out of my head or + their own heads, and those are the heads that determine this. + Even if they somehow succeed in stealing this from me, they will + always know + they stole it. That won't make + things better for them, it will make things worse. The only hope + for these people is coming clean, and we can only hope they + eventually figure that out. As I have said many times, if they + start playing nice, so will I.] +

    +

    [September + 24, 2016: There + is now a simple experiment + posted on youtube by a Dutch engineer, one of my readers, + showing π=4 when motion is involved. To go with the arrival of + that experiment, I have published another short + paper on the subject, which stands as a further + simplification and may be easier to penetrate than the analysis + below for most people. You may also consult my + recent paper on the cycloid for further clarification and + simplification of the problem.]

    +

    I suppose I + shouldn't be surprised this paper has ruffled feathers. It was + meant to. I made my title provocative on purpose, so it is no + wonder it has succeeded in provoking. You don't promote a + revolution in science with meekness, and no one has ever accused + me of being meek. They have accused me of a lot of things, but + never that. I saw very quickly that, beyond my main thesis, I + could counter all the pi-dolatry + on the internet with this paper, and of course I embraced that + with my usual fervor. +

    +

    That said, + before we get started let me answer a couple of prejudices. Many + readers, especially those just coming to my papers, will hit a + wall at some point in this paper. No doubt many already hit that + wall when they read the title. Understandably, π as 4 is a big + pill to swallow. This is admittedly one of my most difficult + papers to absorb. It alone is a huge red pill, and will start you + on a journey far more fantastic and interesting than any Matrix + movie. Beyond that, this paper cannot stand alone. It is a + mistake to start with this paper. Those who do start with this + paper will very likely be led to believe I am simply doing the + calculus wrong. To these people, I say that it is not I who am + doing the calculus wrong. It is Newton and Leibniz and Cauchy and + everyone since who has been doing the calculus wrong. I have + earned the right to write this paper by first writing three + important papers on the foundations of the calculus. The + first shows that the derivative has been defined wrongly from + the beginning, and that the derivative is a constant differential + over a subinterval, not a diminishing differential as we approach + zero. There is no necessary approach to zero in the calculus, and + the interval of the derivative is a real interval. In any + particular problem, you can find the time that passes during the + derivative, so nothing in the calculus is instantaneous, either. + This revolutionizes QED by forbidding the point particle and + bypassing all need for renormalization. The + second paper proves that Newton's first eight lemmae or + assumptions in the Principia + are all false. Newton monitors + the wrong angle in his triangle as he goes to the limit, + achieving faulty conclusions about his angles, and about the + value of the tangent and arc at the limit. Finally, the + third paper rigorously analyzes all the historical proofs of + the orbital equation a=v2/r, + including the proofs of Newton and Feynman, showing they all + contain fundamental errors. The current equation is shown to be + false, and the equation for the orbital velocity v=2πr/t is also + shown to be false. Those who don't find enough rigor or math in + this paper should read those three papers before they decide this + is all too big a leap. I cannot rederive all my proofs in each + paper, or restate all my arguments, so I am afraid more reading + is due for those who really wish to be convinced. This paper + cannot stand without the historical rewrite contained in those + papers, and I would be the first to admit it.

    +


    [
    Added + December 10, 2012: + after an email from a reader concerning the + Taxicab geometry, it dawned on me that Hilbert's metric is + basically equivalent to my "metric" in this paper. In + Hilbert's metric, π also equals 4! And it equals 4 for the same + basic reason π equals 4 in this paper: my "limit" is + approached in the same way his is. See below, where I show the + approach to the limit using circle geometry. Well, Hilbert uses + the same sort of analysis. Only difference is, I dig a bit deeper + into the kinematics, showing the real cause of the problem. At + Wikipedia, they say the difference in metrics is caused by + single-axis motion:

    +


    This + is essentially a consequence of being forced to adhere to + single-axis movement: when following the Manhattan metric, one + cannot move diagonally (in more than one axis simultaneously).

    +


    But + that isn't the cause. The cause is the motion itself. The motion + brings the time variable into play, which adds another degree of + freedom to the equations. You may now consider the fact that + contemporary physicists often use the Manhattan distance or + metric when they get in a jam, especially at the quantum level. + The Manhattan metric is the same as Taxicab geometry. And you can + now understand why using this metric helps them: as I show in + this paper, standard geometry fails because it fails to include + the time variable explicitly, which fouls up the math and then + the physics. In kinematic situations like the orbit, the correct + math and physics includes the analysis I provide in this paper, + in which π=4. And this means that all the hotheads** on the + internet going ape over my paper now have to take on Hilbert as + well. I don't esteem Hilbert and never have, but in this case + having him as an ally is a considerable boost. The mainstream + esteems him, often more than Einstein or Newton, it would seem. + So if my proposing π=4 automatically qualifies me as a crank or + crackpot, these critics will have to explain why the same does + not apply to Hilbert. Is Hilbert a crackpot for proposing that + π=4?

    +

    Also of + considerable interest is the fact that in Taxicab geometry, the + circumference is 8r, as I show in this paper. Furthermore, all + this also connects to my + earlier corrections to a=v2/r, + where I show that the denominator should be 2r rather than r. The + same thing would be found in Taxicab geometry, by extending the + equations just beyond where Hilbert took them. To read more on + this, you may now consult my + latest paper, where I extend my comments of this section and + offer some more diagrams and animations, including a video on + youtube produced by Caltech.]


    +

    +

    +

    a cycloid as + created by a surveyor's wheel

    +

    [Addendum + April 2014. Another + reader has now helped me add to this proof, since she has + reminded me that the arc of a cycloid is also 8r. That is, in the + cycloid, π is replaced by 4, just as in the Manhattan metric. I + don't know why I didn't think to include this before, since it is + so obvious. We should have always asked more persistently why the + arc of the cycloid is 8r while the circumference is 2πr. As a + matter of kinematics, it makes no sense. The same point draws + both, so why the 21% miss? I will be told that it is because with + the circumference, the circle is not moving along an x-axis, but + with the cycloid, it is. It is the difference between a rolling + circle and a non-rolling circle. It is this lateral movement that + adds the 21%. But whoever is telling me this is missing a very + important point: in the kinematic circle I am talking about, the + circle is also rolling. If you are in an orbit, for instance, the + circle is not moving laterally, but a point on the circle is + moving. The + circle is rolling in place, + and it is moving exactly like the point in the cycloid. + Therefore, we see it is not the lateral motion that adds the 21%, + it is the rolling alone. A static circle and a circle drawn by + motion are not the same. The number π works only on the given + static circle, in which there is no motion, no time, and no + drawing. Any real-world circle drawn in time by a real object + cannot be described with π.

    +

    If we study + the generation of the cycloid closely, we find more evidence of + this, since the arc of the cycloid isn't some sort of integration + of the circumference with the distance rolled. It can't be, + because some point on the circle is always contiguous with the + flat surface. We would have to slide + the circle in order to add any + of the x-distance traveled. What is actually happening is that + with the cycloid, the x-to-y integration of distances is + explicitly including time, as you see here:

    +




    In + that integral, we have three variables or functions: x, y, and t. + Study the second and third lines of the math, where we are + explicitly following the value of t. It is not the sine or cosine + of x or y we are following, it is the cosine and then sine of t. + In that integration, we have three degrees of freedom or a + 3-vector. So it is not the lateral motion that is causing the + difference, it is the inclusion of time. To calculate the arc + length of the cycloid, we need the integral which includes dt. + But when we calculate the circumference, we don't include any dt. + So the methods of calculation don't match. Despite that, we use + the naive static circumference that includes π when we calculate + orbits. This is illogical, since all orbits include time. They + should be solved with integrals like the one above, not with the + static circumference calculated from π.

    +

    You may wish + to differentiate the two as I + do in my long calculus paper. There I differentiate between + length and distance. A length is a given parameter that does not + include motion or time. It is geometric only. But a distance is a + length traveled in some real time, so it requires motion. A + length is not kinematic, while a distance is. The circumference + 2πr is a length. The circumference 8r is a distance.]

    +

    +

    +

    +



    +

    +

    In this paper + I will show that if we define π as the relationship between the + diameter and the circumference and + include motion, + the correct value of π is 4.00. In other words, the current + numerical value of π is nothing more than a mathematical error: + it is the standard margin of error, caused by a fabulously false + postulate.

    +

    More + specifically, the π that I am correcting is the constant in the + orbital equation v = 2πr/t.

    +

    The + Pythagoreans had some inkling of this error. They were never + happy about the irrational number π , just above the number 3. + We have been taught that the Pythagoreans were unhappy about π + due to the fact it was not rational. But their unease was likely + caused by a more fundamental problem. They may have intuited the + problem I am showing you on some level. They seem to have smelled + that something was wrong here. Meaning, perhaps it was not the + value of π that bothered them, much less its status as + rational or irrational. No, they didn’t even spend much time + seeking a precise value for it, since they had no respect for π + to start with, regardless of how you categorized it. Had it been + rational, they would have had no more respect for it. They had no + respect for π because they suspected it was an outcome of bad or + incomplete math. They did not want any rational or + irrational number slightly above 3, no matter what it was, + because they felt the right answer must be 3. What bothered them + most is that they could not complete the math. I will do that + now. Unfortunately, although I will show that their unease was + justified, their intuition was faulty. The correct number is not + 3 but 4.

    +

    In previous + papers, I showed that classical geometers have sought solutions + by ignoring the time variable completely. The equations of + geometry happen at some imaginary instant. Not only are we at a + limit with regard to length (since all lines have no width, + etc.), we are at a limit with regard to time. We have reached the + limit where t=0, since time is not passing. We don’t take into + consideration how long it takes to draw the lines or curves, we + simply take them as given. We do not imagine traveling along + those lines, or imagine seeing them traveled by a point. The + circle is not an orbit, for instance, it is just a circle, + existing all at once. +

    +

    But, as I + also pointed out before, geometry cheats in this, since geometry + is supposed to represent the external world, but the external + world never exists in this way. Never once in the history of the + universe has a circle drawn itself or existed all at once. + Currently we assume that π exists in the real world, but I will + show that π exists only in abstract geometry, and that abstract + geometry is kinematically false. That is to say, π does not and + cannot exist in physics or applied mathematics, except as a fudge + factor. We require it so often in our equations only because our + equations are incomplete or misdefined. If our equations + contained all the proper logic and transforms, π would be + extinct. +

    +

    Let me first + explain what I mean by that in a bit more detail. The number π + is a relationship. We already know that. Currently we think it is + a relationship between the diameter and the circumference. The + problem is, we treat the diameter and the circumference as + equivalent mathematical entities, when they are not. One is a + line and one is a curve. If we study the line and the curve with + a bit more rigor, we discover they aren’t directly comparable. + To state it yet another way: we assume that we can straighten out + a curve like a piece of string, measure it as a straight line, + and then compare that new length to any line we like. Physically, + this turns out to be a false assumption. The only place we can do + that is in abstract geometry, where time does not exist, and + where lines and curves can be “given”, rather than drawn or + created in any physical sense. If we are given lines and curves, + and if we can ignore time, then we get π as the relationship + between the diameter and the circumference. The number π only + exists when we are given absolute pre-existing values, when the + circumference is treated as a simple length, and when we ignore + time. But since with any real circle both these assumptions are + necessarily false, π does not exist in any real circle. In any + real circle, the relationship between the diameter and the + circumference is not π , since the circumference may not + be thought of as a straight-line distance. Because the + circumference cannot be created with a single velocity vector + (and the diameter can), the two numbers cannot be compared + directly. +

    +
    +

    But let us + start at the beginning. By definition, a velocity vector cannot + curve. A velocity takes place in one dimension or direction only. + In a velocity, there is only one distance in the numerator and + one time in the denominator. These times and distances are also + vectors, and may not curve. But to create a curve, either + mathematically or physically, requires at least two velocities + happening over the same interval. Or, to put it another way, it + requires two distances measured over the same time interval. If + we sum these velocities over the same interval, we achieve an + acceleration, and thereby—assuming the two velocities are at an + angle—a curve. +

    +

    If we bring + time back into the problem of the circle, we find that every line + or distance becomes a velocity and every curve becomes an + acceleration. So the diameter becomes a velocity and the + circumference becomes an acceleration. All we have to do is + imagine the lines being drawn. The pencil must have some velocity + or acceleration as it moves along the line or curve. Likewise + with a planet drawing out an orbit, or any other possible + creation of a circle in the real world. +

    +

    Once we do + this, we see that in comparing the diameter to the circumference + in any real circle we are comparing a velocity to an + acceleration. But you cannot directly compare two numbers, when + one is a velocity and one is an acceleration. Or, you can if you + like, but the new number you get from the ratio is not going to + be a number that carries any real meaning in it. It is certainly + not the same as comparing one distance to another. For example, + if you compare one distance to another by putting them into a + fraction and achieving a new number, this new number will contain + useful information. It will tell you how long one line is + compared to the other, obviously. But if you compare a velocity + and an acceleration, what information do you get? Say you achieve + the number 5, which tells you the acceleration is five times the + velocity. Does that tell you anything about distances? Yes, it + might, if you develop a transform. But without a transform and a + bit of thinking, the number 5 isn’t telling you anything. It + certainly isn’t telling you that some distance is 5 times some + other distance. +

    +

    To give a + specific example, what if my acceleration is 3 and your velocity + is 1. Can we compare those two numbers directly? No, we cannot + put them into a fraction or any other equation without doing some + more work upon them. We cannot claim that I have done anything + three times as much as you. With an acceleration of 3, my + velocity could be anything at a given interval, and my distance + traveled likewise. What if my acceleration is π and your + velocity is 1? Is π the value of any real relationship between + us? No. You can’t compare an acceleration to a velocity. You + need more information.

    +

    To say it + another way, think of the calculus. In the calculus, velocities + and accelerations can't be put into the same equation—without + the proper notation. Velocity is the first derivative and + acceleration the second, so we have to notate a velocity variable + or function with a single quotation mark, and an acceleration + with a double (or something similar). If we don't, the equations + will soon implode. The functions indicate different rates of + change, so they can't be compared directly. The functions have to + be kept separate and handled in the right way, and everyone knows + that. But in the historical orbital equations, they haven't been + kept separate. Whenever we compare the circumference to the + diameter directly, finding π, we are breaking this fundamental + rule of the calculus. +

    +

    This is + important because this is precisely what we think π is telling + us. We think it is telling us that the circumference is + 3.14 times the diameter. But it isn’t. About real kinematic + circles, π is telling us nothing. About abstract circles, π is + telling us that if the circumference were a straight line, + it would be π times the diameter. But since the circumference is + not a straight line, π is telling us nothing useful there + either. +

    +

    To show this + more clearly, let me give you another example, with a diagram. +

    +

    +

    Here we have + a Cartesian graph of a velocity, with axes labeled x and t. As we + know, a velocity on such a graph is a straight line. The line + stands for or represents the velocity. But what does the length + of that line represent? In this example, x = 2 and t = 4, so v = + x/t = .5. But the length of line v is much longer than .5 units. + The length of line v can only be found by the Pythagorean + Theorem, and it turns out to be about 4.47. Now, we can ask what + is the ratio of line v to line x, and we will find it is about + 4.47/2 = 2.236, which is an irrational number. An esoteric ratio? + Of course not, since the length of v is not only a meaningless + length here physically, it is not even the real velocity. By the + definition of velocity, the velocity is the distance over the + time, so using the Pythagorean Theorem to find the length of v is + just foolish. I claim that, in a physical situation, comparing + the length of the circumference of a given circle to the length + of its diameter is just as foolish. +

    +

    +

    We can draw a + circle on a Cartesian graph, too. We can’t make one of our axes + a time axis, but we know we can draw a circle on an x/y graph, + since a well-known equation comes with it (x2 + y2 + = r2). Here we are taught that the circle represents + an acceleration, as all curves are accelerations on Cartesian + graphs. But what about the length of the circumference of this + circle: what does it represent? Just as with the line + representing the velocity in the first illustration, it + represents nothing here. You wouldn’t think of comparing that + “length” to the radius or the diameter in this illustration, + so why would you do it when you take away the graph?

    +

    Of course, + this analysis begs the question: what “graph” is a real + circle drawn on in the real world? When a planet “draws” an + orbit around a star, what mathematical background are we using? + Put that way, this problem starts to look very complex. We have + three dimensions, x, y, and t, and a complex motion. The orbit is + not just a velocity or an acceleration, it is a + simultaneous velocity and an acceleration. To draw out the + circle, the planet has to be expressing both a tangential + velocity and a centripetal acceleration over every dt. We are + told that the planet will have an “orbital velocity”, but + that wording is criminally reductive and misleading. Not only + does the planet NOT have an orbital velocity, it does not + precisely have an orbital acceleration either. It does have a + sort of acceleration, but that acceleration is not like any + first-degree acceleration we are accustomed to measuring. No, it + is a very odd beast altogether. It must be called an acceleration + for two reasons: 1) It curves. A velocity is a vector and cannot + curve. 2) It requires a constant force. A velocity is achieved + with a single force. An acceleration requires a continuous force. + A circle requires both a single force and a continuous force, + therefore it must be the expression of some sort of acceleration. + But it is a unique compound acceleration, compounded of both an + acceleration and a velocity.

    +

    And that begs + another question: is 2πr/t a velocity? No, since a velocity + cannot curve. Is it an acceleration? No, since you can’t + achieve an acceleration by dividing a distance by a time. What is + it? It is a floating heuristic device, a piece of fake math that + misdirects us. It causes us to think of the orbit as an abstract + geometric shape, where time can be stirred back in in a sloppy + manner at the end. But neither the orbit nor the circle should be + thought of that way. As I will show in more detail below, 2πr/t + is actually a variable or second-degree acceleration, of the form + x/t3. This is because π is already an acceleration + itself. This gives 2πr/t the dimensions of x/t2/t, + which reduces to x/t3. This is logical, since we need + the three t variables in order to express the simultaneous + tangential velocity and centripetal acceleration that makes up + the orbit. The velocity contributes one t variable and the + acceleration contributes the other two. The orbit is neither a + velocity nor a simple acceleration. It is a second-degree + acceleration.*

    +

    Now, + hopefully, you begin to see the problem. Historically, we have + been seeking a relationship between distances, and historically + we have thought that π has been an expression of that + relationship. But it isn’t. The number π is an expression of + the relationship between two lengths that exist only in abstract + geometry, and abstract geometry is physically false. In the real + world, if we want to know the relationship between the diameter + and the circumference, we have to look at the relationship + between a velocity and a second-degree acceleration.

    +

    You will say, + “But surely that acceleration will give us a distance, as will + the velocity. Are you saying that the distance given by that + acceleration is not 2πr?” Yes, that is precisely what I am + saying. If you use straight-line motion to measure everything + (which is what we do physically), then a curved string must be + longer than a straight string. And the very simple reason for + that is that it must take longer for any real body to travel + along the curve than along the straight line. +

    +

    One key term + in that last paragraph is this one: “straight-line motion.” + Notice that I do not say, “the straight line.” In reality, we + do not use a straight line to measure, we use straight-line + motion. We use a velocity vector to measure the real world. This + can be seen very clearly in my explanation above: The real world + includes time. In physics, you simply never have a distance + divorced from time, like you have in geometry. Every distance + comes with a time, and cannot be separated from it. When we + insert time back into the circle equations, all the lengths + become velocities. The diameter is not a length, it is a + velocity. Now, if we compare the diameter to the circumference, + what we are really doing is measuring the circumference + with the diameter. So, if we are measuring with a velocity, and + it takes longer to travel a curve than a straight line, the curve + must be longer. This must be true even when the curve and the + straight line have the same length in abstract geometry. +

    +

    Another key + term in the paragraph before the last one is this one: “it must + take longer.” When measuring a curve with a straight line or an + acceleration with a velocity, we are not measuring with x so much + as we are measuring with t. Look at it this way: if you have a + constant velocity, then the distance traveled is simply a + function of time. If you travel two lengths, the one that took + longer will be longer. Since a curve is not equivalent to a + straight line, we should measure them with time instead of + length. We can tell how long the curve is by seeing how long it + takes us to travel it. +

    +

    Finally, + remember that the distance traveled by an acceleration is never + just the velocity times the time. So neither an orbital velocity + nor an orbital acceleration could be expressed by the term 2πr/t. + If you are given a normal acceleration and seek the distance + traveled in some time, you must use the equation x = at2/2. + But we don’t have a normal acceleration; we have a variable + acceleration, one that is not linear, and we seek the distance + traveled in some time. What equation do we use? We are in new + territory here, since there is no standing equation to plug into. + We must create it from whole cloth.

    +

    +

    To arrive at + that equation, let us first look at some other thought problems. + These thought problems, with their accompanying illustrations, + may give us a clue how to proceed. As our first thought problem, + let us say you are in a spacecraft traveling at velocity v. You + are approaching a planet. You happen to know its gravity + precisely, so you arrange to intersect its field at precisely the + right angle and distance, so that your velocity creates a stable + orbit. In real life, this maneuver would require some + adjustments, but we will assume you can slip right in there. To + make this even more interesting, let us say that your orbit has a + radius of fifty miles and you are going fifty miles per hour. The + planet is very small and happens to have a gravity vector that + makes this stable. Both logic and Newtonian mechanics tell you + that it will take you eight hours to orbit (see the illustration + below). Let us imagine further that you are an alien from some + planet that knows nothing of π and that does physics a bit + differently, and you calculate the orbital distance like this: + your velocity was the same both before and after entering orbit; + your engines are in the same state as they were before, both as + regards direction and strength; you haven’t throttled up or + down or moved a tail fin. Since distance is velocity times time, + you calculate the orbital distance to be 400 miles. +

    +

    Are you + wrong?

    +

    According to + abstract geometry and π, of course you are. The circumference of + the orbit is about 314 miles, not 400. Your orbital velocity is + about 39 mph, not 50. And what is slowing you down is gravity, + which tends to pull you back slightly, working against your + engines. +

    +

    But there are + a lot of problems here. If we use general relativity, our + mechanics is immediately out the window, since there is no + centripetal force. The curvature pre-exists in the orbit: it is + not caused by a pull, so only your velocity exists. If your + velocity is the only motion that exists, and if a straight line + is equivalent to a curve, there is absolutely no reason you + should not calculate the orbital distance to be 400 miles. There + is nothing pulling you back or working against you engines, so if + you aren’t being slowed down, you must be going the same speed. + If you are going the same speed for the same time, you must go + the same distance. If you aren’t being slowed down, then your + “orbital velocity” cannot be less than your original + velocity.

    +

    But it is + even worse than that, from a physical viewpoint. Let us ask if it + is possible to apply a centripetal force without affecting a + tangential velocity. Let us assume there is some force + turning the spaceship, either Newton’s gravity or some real + force that is “warping” space. The problem with this is: the + spaceship’s engines are off! When glossing the standard model + response, I said that gravity was working against the spaceship’s + engines, slowing it down. But a constant velocity requires no + engines in space. Does the Moon have engines? No, all we require + is an initial force, to get the Moon or spaceship going. + After that it coasts at a constant velocity (this is all + according to the standard model and the historical + interpretation). +

    +

    To make this + problem even clearer, let us say we want to create the appearance + of an orbit using just the power of the spaceship. Meaning, we + want to draw a circle in space without a central body. + Theoretically, we should be able to do this with just two + engines: one to get us started in the forward direction, and one + to push us constantly toward some chosen center. This second + engine will run all the time with a constant thrust, and its + direction will change every moment. I hope you can see the + problem. Even if we could build a directional thrust to the + engine that could change smoothly and accurately, we could not + keep this thrust from interfering with the original velocity. You + can neither push nor pull on a real object at a true + perpendicular to the line of motion of that object, since a real + push or pull is never instantaneous. Our push here, like gravity, + must be continuous, which must create some real time interval, + which must create some non-tangential component, which must + interfere with the initial velocity of the spacecraft. Any real + pulling or pushing force, even one at a tangent, must tend to + dissipate an initial velocity. +

    +

    Circle + mechanics makes this even clearer, since the force of gravity is + not strictly perpendicular anyway. To create the circle or the + orbit given an initial straight line velocity requires some + backward component to the central force. You can see this + immediately just by comparing the tangential velocity and the + “orbital velocity”. As I showed above, and as every engineer + knows, the orbital velocity is less than the tangential + velocity. This alone proves that there is a component of force + backward along the line of the tangent, for if there weren’t, + the tangential velocity would have to be equal to the orbital + velocity. In other words, gravity must pull down and back. + If it just pulled down, no circle or orbit would be created. + Because the spaceship is moving forward, gravity must pull + down and back. Look at the illustration below. BC is the gravity + vector. To go to the limit, we make the triangle ABC as small as + we can. We cannot make it zero, since AB must have some length. + If AB has no length, then the spaceship has zero velocity. If the + spaceship is moving, then AB has some length. If AB has some + length, then BC must have some backward component. If BC has some + backward component, it must tend to diminish the velocity AB. + QED, gravity must tend to diminish the tangential velocity of the + spacecraft or the “innate motion” of the Moon.

    +

    The standard + model response here is that I am wrong: the centripetal force + must be completely perpendicular to the initial velocity at all + times, therefore there is and can be no “backward” component + to the force or the acceleration. I will be told that this is why + the Moon’s innate motion does not dissipate. But if this is + true, then why is the orbital distance less than the distance + that would have been traveled at the original velocity? As the + illustration shows clearly, a tangential velocity of r would + create a distance traveled of 4d, not πd. If the centripetal + force does not work against the tangential velocity, as a + vector, then how can πd be less than 4d? To put it another way: + if the centripetal force is completely perpendicular to the + tangential velocity—even while we sum the times involved in a + complete orbit—then the tangential velocity must be unchanged. + If the tangential velocity is unchanged over each dt and the sum + of all dts, then how is it that the orbital distance is not the + same as the expected straight-line distance? And how is it + possible that the orbital velocity is less than the tangential + velocity? +

    +

    I say that in + order for the compound or “orbital” velocity to be less than + the original or tangential velocity, the centripetal force must + be working against it, in a vector sense. This is the only + possible way the orbital distance (the circumference) could be + less than 4d. But if the centripetal force is diminishing the + total distance traveled, it must be diminishing the tangential + velocity. And if it is diminishing the tangential velocity, that + velocity must be dissipating, and the innate motion of the + orbiter must be dissipating as well. +

    +

    In fact, if + we accept the standard-model interpretation, the orbit would be a + sort of perpetual motion machine. Without perturbations and other + orbital imperfections, a planetary orbit would be perfectly + stable, according to both Newton and the current standard model. + It would not tend to change either inward or outward. The + standard model allows for such a possibility in the real world, + since neither the tangential velocity nor the centripetal + acceleration is a cause of orbital instability. Theoretically, a + perfectly round planet in a perfectly round orbit around a + perfectly round star, at a distance from all other planets and + stars, might orbit forever. And since in General Relativity, + curvature is given rather than created, this motion conserves + energy. Since it both conserves energy and creates potential + energy, it must be a perpetual motion machine. Remember, we have + a non-dissipating curve here! That is not only perpetual motion, + that is a perpetual machine: a perpetual source of power. +

    +

    Now, I have + not put the analysis in these terms in order to sell a perpetual + motion machine. I have put the analysis in these terms to show + that the standard model is contradictory and flawed. I need to + get back to π, so I will tell you the end of this story and move + on. The short answer to this problem is that circles and orbits + aren’t created this way physically or mechanically. We can + analyze the circle mathematically in this way if we do it right + (which historically we haven’t), but in the real world circles + and orbits are never created with simple tangential velocities or + “innate motions”. A protractor does not draw a circle using a + tangential velocity, a boy whirling a ball on a string does not + use a tangential velocity, and no other circle is ever created in + this way. The protractor and the boy and the tilt-a-whirl and so + on make use of constant tangential forces, not just centripetal + forces, and they would have to do so even if friction were not a + factor. Regarding celestial orbits, the mechanics fails there, + too. Velocities would dissipate, planets are not self-propelled, + and the balance could not be maintained. As I show in + a series of other papers, celestial orbits are more complex + than we have been taught. They include the E/M field, for a + start, which acts as both a balance and an engine. And we have to + reverse the gravity vector, which makes it neither a push nor a + pull. It is a real acceleration of the central body, so it causes + no forces and no dissipation in the Newtonian sense. +

    +

    That being + said, we can still analyze the orbit and the circle in much the + way Newton taught. We can use his equations, we just have to + clean them up a bit. To do this, lets us look again at the last + illustration. This drawing requires a more rigorous analysis, + both as it applies to orbits and as it applies to circles created + in any other ways. If you have studied this problem at all, you + have probably seen hundreds of drawings, with polygons inscribed + and superscribed on circles and so on and on. But all those + drawings are misdirection. The important drawing is this one.

    +

    +

    Here we + measure the circle with the radius directly. We treat the radius + as a velocity instead of a distance, and then begin our trip from + some point on the circle. I am starting at point A in this + drawing. In the drawing, the length of the vector stands for the + velocity, and I must draw it at a tangent. Meaning, the angle at + A must be 90 degrees. So, if you give me the same time to travel + as I took to draw the radius, then I will end up at point B. +

    +

    But if we + want to draw a curve, we must keep the pencil on the circle, and + here I am way off the circle. How do we solve this? Let us solve + the way Newton did, by inserting a second motion. To create any + real curve requires at least two simultaneous motions. As we know + from classical orbital mechanics, this second motion must be a + vector pointing at the center. This motion will take us from off + the circle to back on the circle. To get us back on the circle, + we can postulate a constant force over the interval of motion, + and this would give us an acceleration, just as in gravitational + mechanics. Or, to avoid that variation, which mathematically + would require the use of calculus, we can just postulate all our + force at the end. In that case, the final velocity is the + acceleration, since we have a change from zero. The acceleration + can be found just by drawing a line from B to O. The length of + the line BC then represents the centripetal acceleration of our + pencil over the interval AC. Those two motions or velocities + happen over the same interval, so the two times superimpose. + Whatever time it took us to draw the radius initially, is the + same time it now took us to go from A to C. Therefore we have + measured the circumference using the radius. +

    +

    But how does + this give us a circumference? Before I show you the full math, + let me show you the conceptual shortcut. Go back to the drawing + above. You can immediately see that we have carved out a piece of + pie that is 1/8 of the circle. AO = AB, so the angle at O is 45 + degrees. Eight pieces of pie means we use eight radii to measure + the whole circle. That is 4 diameters. Problem solved. It takes + us four times as long to travel the circumference as to travel + the diameter, given the same velocity. If the velocities are + equal, and the times are directly comparable, then the distances + are directly comparable. Velocity is d/t, so if it takes us four + times as long, the distance must be four times as much also. That + is the only real way to compare a velocity and an acceleration, + or a straight line and a curve. Every other analysis is + incomplete and faulty, since every other analysis ignores the + time variable.

    +

    +

    Now let us do + the full math. Let us go back to the illustration, to start. Let + us drop a perpendicular from point D so that it hits point C, as + above. Since the angle at B is 45o, DB must equal DC. + And now I will prove very quickly that AD + DC is equal to the + curve AC, in length. As I do that, I will have also proved that + AB is equal to the curve AC, in length, which will prove my + assertions above concerning the falsity of π . If I can prove + that, it will prove that the circumference is 8AB, not 2πAB. +

    +

    +

    The + historical way of finding the length of arc AC is by exhaustion + or by calculus. We look at smaller and smaller subarcs until we + reach the limit where the arc equals the chord. The chord is + simply the straight line from point to point: for example, the + straight line from A to C is the chord AC. If we can straighten + out the arc, we will have measured it, and we can use that length + to compare to other straight lines. So we let C approach A. At + the limit, it is assumed that the chord AC is the length + traveled. The Greeks used this analysis and these assumptions in + solving this problem over 2000 years ago, using the idea of + exhaustion (which I will return to in a moment). Later, when the + calculus was formalized, we went to an ultimate ratio, as Newton + called it; and then we went to a limit, with Cauchy. But in all + the historical solutions, the assumptions were as I have stated + them in this paragraph. The main assumption being that we were + taking the arc to the chord: approaching the chord in some way or + fashion.

    +

    But all this + is false. The very simple fact is that the distance traveled + never approaches the chord. The distance does not approach + anything, since it never diminishes. The distance is the same + whether we draw it large or draw it small. Since it doesn’t + change as we “exhaust” it, it cannot approach a limit. Watch + closely as I prove this in my diagrams, with simple logic. +

    +

    In my + analysis above, I showed that the centripetal force must pull + down and back in order to take any object—either a + pencil tip or an orbiting spacecraft—out of its original path + and into a circular path. The simplest way to think of this is to + think of the original velocity as AB. Then the centripetal force + creates two other velocities: a velocity of size DB, which pulls + the body back from B to D; and a velocity DC, which pulls the + body down to its final destination of C. This is how we break + down our curve into straight velocity vectors. The motion of the + body from A to C is a summation of these three vectors. All three + velocities happen over the same time interval, so we sum them. It + is that simple.

    +

    You will say, + “That makes some sense, until we look at the actual lines. Any + idiot can tell that AD + DC must be longer than the curve AC. The + curve AC never goes out to point D, for a start. If you want to + solve by exhaustion, you have to “push” the point D closer + and closer to the curve, by dividing that curve into lots of + little segments or steps. That is how the Greeks actually solved + it, you foolish person. You can’t just take any large arc like + this and run perpendiculars: you have to go to a limit. You have + to get small.”

    +

    +

    OK, well, let + us do that, then. In this diagram, I will push D closer to the + curve AC. I will begin both the exhaustion and the “approach” + to an infinitesimal or limit. We will start by dividing the curve + into 4 equal parts or steps. And yes, we are much closer to the + curve already, as you see, with just 4 divisions. We don’t find + ourselves anywhere near the distance from the curve of the + original point D. It already appears that if we continue to do + this, we will “approach” the curve AC very fast, so our + method appears to be the right one. It does what we want it to, + and it mirrors the method of the Greeks and Moderns.

    +

    The problem + is, it doesn’t change the distance traveled at all. If + you add up those eight little lines segments along the steps, you + find that they equal AD + DC. We have changed our path, but we + have not changed our distance! We can draw eight steps or 64 + steps or an infinity of steps, and it will not change a thing. + With our logical little method here, we are not “approaching” + any new distance, we are only approaching a new path. The + distance is the same at the limit as it was in the beginning: AD + + DC.

    +

    Therefore, at + the limit, the path AD + DC is equal to the path taken by + the curve AC, which does indeed solve our problem. But the + distance has not changed by going to this limit. So if AB + is the same length as the radius, and we have defined the radius + as .5, then AC must .5 also, and the circumference is 4. +

    +

    It turns out + the aliens were correct again. I said above that they had no idea + of π, and that they simply used their tangential or original + velocity to measure the circumference of the orbit. I have just + shown why they are correct and why we are and always have been + wrong. +

    +

    There + is one more thing to notice here before we move on. It is very + important. I have shown that AD + DC = arcAC. In my diagram, this + means that the tangent equals the arc. However, this works only + when the tangent is equal to the radius. The angle at O must be + 45o, + so that DC = DB. If the angle is not 45, then the tangent cannot + equal the arc, because AD + DC is not equal to AB.

    +


    This + is important because if we assign the tangent to the tangential + velocity and the arc to the orbital velocity, as Newton did, we + find they are equal not at the limit, but only when the tangent + equals the radius. In fact, as + I have shown, the tangent and the arc are NOT equal at the + limit. At the limit, the tangent remains longer than the arc. And + this means that the tangential velocity and orbital velocity are + equal only when the length of the tangent is equal to the radius, + or when the time is equal to 1/8th of the orbital period. An + orbital velocity found by any other method will get the wrong + answer.
    This + is why 2πr/t is wrong: 2πr/8 is not equal to r.

    +


    Ditching + π allows us to further correct the orbital equation. I have + already shown that the equation should be a = v
    2/2r + instead of v2/r. + If t is the time of one orbit, then the orbital velocity must be + 8r/t, not 2πr/t. These two corrections change the value of a + from 39.5r/t2 + to 32r/t2. + In a supplement to this paper, I will show how this solves the + famous Explorer + Anomaly.

    +


    After + we have made all these corrections, we are in a position to see + that we may assign the acceleration a to the line segment BC. + Furthermore, if a = v
    2/2r, + and AB = r, then r = v, and a = r/2. Consulting the diagram above + again, that is also 2BC = CO. What this means is that we have a + new way to find a centripetal acceleration, currently called + gravity. The equation a = r/2 gives us a total acceleration over + 1/8th of the orbit, so the total acceleration over the entire + orbit is 4r. Or, we can find the acceleration over any + subinterval. Say we want to find the acceleration of the Moon + over 1 second. We are given that the period for the Moon is + 2,360,534s. Since the Moon is orbiting at 384,400,000m, the total + acceleration over the orbit is 4 times that, or 1.538 x 109. + Dividing gives us a = .000276m/s2 + over 1 second.

    +


    For + more fun, we can even show that the centripetal acceleration is + not found at an instant. I + have shown that the Moon's centripetal acceleration due to + the Earth is not .002725, it is .002208. If the Moon's total + acceleration over 1 second is .000276, then an acceleration of + .002208 cannot happen over an instant. We can even find the time, + with simple math. If we plug that into our new equations, we find + that a = .002208 when t = 8s. That is not an instant or an + infinitesimal, since it is a calculable number. That would be + true even if I used the number .002725 for the Moon's + acceleration. You cannot have a real acceleration over an + instant, so we should have known simply from logic that the + centripetal acceleration we have been calculating was not over an + instant or infinitesimal. I have just calculated the real time of + orbit during the "instantaneous" acceleration, so I + have proved that Newton did not go to a limit or approach zero. + As I have said in my + paper on the derivative, the calculus does not work by going + to zero or a limit, it works by going to a subinterval, and I + have just shown you the subinterval in a specific problem.

    Some + will say this is just doing the calculus wrong, but I claim that + history has done the calculus wrong, not me. The common answer to + my demonstration above is that at the limit, defined as it + currently is, the total length of the steps never approaches the + arc, because even with a very large number of steps, the distance + between each step and the curve remains real. We can sum this + distance even at the limit, keeping the sum of the steps above + the sum of the curve. It is not the sum of the steps that + approaches the curve, it is the curve that approaches the chord. + In other words, it is the hypotenuses of the little triangles + that converges upon the curve, not the sums of the other legs of + the triangles.

    +


    While + I recognize that this is the common interpretation, I cannot + agree with it. In my paper on Newton's + lemmae, I proved that the tangent in his triangle must be + longer than the chord and arc at the limit. If we apply that to + this problem, it means that the arc cannot approach the chord at + the limit. The tangent is a component of the arc, by Newton's own + definition of it in the
    Principia, + so if the tangent is longer than the chord at the limit, the arc + must be also. This means that the curve does not approach the + hypotenuses of these steps, no matter how many there are. The + hypotenuses are the chords, and they cannot be approached by the + arc or tangent.

    +


    You + will now ask where in the
    Principia + Newton says that the arc is + composed of the tangent. It is where he tells us that the orbital + motion is composed of the centripetal acceleration and the innate + motion of the body. These two vectors compose the orbital motion. + They are the only two motions given us by Newton, and he + explicitly assigns the innate motion of the body to the tangent.

    +


    This + means that the historical and current interpretation cannot be + correct. The hypotenuses do not converge to the curve or the arc. + No, as I show above, it is the tangent that converges upon the + arc, but the convergence happens only when the tangent equals the + radius. This convergence can happen only at 1/8th of the circle, + and it happens just as I show in these papers. It happens because + the arc is never a continuous curve, even at the limit. The arc + is defined as a curve composed of linear or straight vectors, + therefore it can never be continuous, if we mean by continuous + that the time or length traveled goes to zero. Logically, the + time or length cannot go to zero, since there is no time or + length at zero. All numbers in math and physics imply a + differential, and since this is so, the curve cannot be thought + of as continuous in this way. The curve must be thought of as + composed of linear vectors, even at the limit. And since this + must be true, it must also be true that the path I have drawn + above must converge upon the arc, for precisely the reasons I + have stated. The path does converge upon the arc, in the sense + that the path gets nearer the arc. No one can deny that, at + least. But since the arc is not continuous even at the limit, the + path truly does converge upon the arc, as we draw more steps. + Since the distance doesn't change no matter how many steps we + draw, the tangent must converge upon the arc. Or, at 1/8 of the + circle, the tangent simply IS the arc.
    +

    +


    +

    +
    +

    Let me round + out this “full math” by answering a couple of questions. I + have shown the simplest full math, and it may not convince some + people. I will be told, “It appears that your method of + ‘exhaustion’ can be used to show that any curve from A to C + is of the same length. Beyond that, it would appear that even the + chord AC can be shown to be equal to AD + DC, by your method. + What makes the circle arc AC special, and why should your method + work upon it and upon no other arc or line between A and C?”

    +

    It is a good + question, admittedly, or I would not include it here. To make + this method—which I have called exhaustion but which might just + as easily be called approaching a limit—work, we have to push D + toward the curve in a rigorous manner. In short, all of our steps + have to be approaching the curve at the same rate, or the method + will not work. For instance, if we draw a different curve from A + to C, one that bulges out very near to D, and then we draw our + steps, we will not be able to make those steps even. Or, to put + it another way, we will not be able to push D toward the curve in + an even manner. Our exhaustion will not “go to infinity” at + the same rate all long the curve; therefore our method will not + work, mathematically or physically. But it will work with the + circle arc AC, and it works for the physical reason I have shown + above: both the tangential velocity and the centripetal force are + constant. The arc AC is created by a constant and unvarying + process, therefore that arc can be approached by the (right) + orthogonal vectors in a logical and rigorous manner. In fact, the + arc AC is the only curve from A to C that can be exhausted in + this manner, given AD and DC. All other curves are varying + curves, and cannot be approached as a limit or exhausted in this + direct way. +

    +

    To show why + the chord AC cannot be approached like this, we use much the same + analysis. At first look, it appears that you could draw steps + along the chord AC in the same way, “exhaust” them in the + same way by increasing the number of steps higher and higher, and + find by this magic that the straight line AC was the same length + as AD + DC. The reason you cannot do this is because once again + you cannot approach the chord AC in an even manner from the point + D. Therefore all your steps won’t go to the limit at the same + rate, and your “method” won’t work. You will say, “Gosh, + it seems like the straight line would be the easiest thing to + approach in an even manner, since it is even to start with!” + But try it and you will soon see this is not the case. The + straight line is actually the most difficult thing to approach, + and the impossibility of this approach is actually the easiest to + discover. For instance, draw four equal steps along AC, then look + at them from the point D. There isn’t any way you could have + approached those four equal steps from D. In the method, you + aren’t just drawing any steps you like. You are supposed to be + drawing steps that would occur if you pushed the line AD + DC + toward AC. Exhausting a process or going to a limit is not a + willy-nilly process, it is a defined and rigorous process. You + will find, if you try, that you can’t approach a line evenly + from any point, using this method. No matter where you + place D along the line AB, it will not approach AC at the same + rate, with steps logically defined in any possible way. And it + may eventually become clear to you why this is so: the distance + of a line cannot be approached from off the line, because the + line is already the distance itself. It is “even to start + with”; therefore, it cannot be approached evenly (except by a + parallel line of the same length). +

    +

    As one more + short demonstration of this, say we place D on the line AB so + that it is equidistant from A and C to begin with. You may think + we could make it approach AC in an even manner in this way. But + no. If we draw smaller steps in the middle and larger ones toward + A and C, we can force one set of steps to act right. But we + cannot make our next set of steps act right, both in regards to D + and in regards to our first set. To make our next set of steps + diminish evenly, we would have to vary the rate of change along + the steps, and this isn’t allowed. An approach to a limit must + progress in a defined way, else the approach won't happen. An + approach that progressed unevenly would create "bumps" + as the limit was approached, and the limit would not be the curve + we see. It is one thing to approach a limit that is a point, and + another to approach a limit that is a line or curve. In + approaching a line or curve, the approach has to be the same at + all places along the curve, and to achieve this the approach must + be monitored all along the curve, as I am showing you. +

    +

    There are + more abstract ways of stating this, in various mathematical + symbolisms, but because math is always shorthand for the + explanation, I prefer to give the explanation. If you don’t + comprehend the explanation, you won’t ever comprehend the math. + To ever understand the curve or calculus, you have to study + diagrams and do some real pushing and pulling of lines, just like + this. A limit is not an abstract thing. It is not a concept you + can use like a hammer, without finesse. You have to understand + that a limit is always approached in a rigorous and defined + manner; and if it is not approached in a rigorous manner, you + will get the wrong answer. The same thing applies to exhaustion. + Mathematically, exhaustion is a rigorous process. It is not just + drawing more and more steps; it is drawing steps that increase in + number and size in a defined manner. +

    +

    The very + shortest answer to this question is that you can approach the + circle arc AC from the line AD + DC because those vectors created + the arc AC to begin with. Those vectors physically created the + curved path. They are not just orthogonal vectors, chosen because + they were handy. They are THE orthogonal vectors that define the + path of the curve. The arc AC is approached smoothly from D + because it was in some sense created from D. D is the physical + balance of O, given the interval AC and motion from A to C. D was + guaranteed to approach the arc AC smoothly and evenly, which is + why I use the method without explanation in my gloss of this + paper. +

    +
    +

    Now, some may + ask what this has to do with my whole analysis of time, earlier + in this paper. I spent many pages telling you that time changed + the whole problem of the circumference and its measurement and + then I offered a "full math" that didn't once mention + time. Here we get into some difficulty I wanted to avoid. I have + been trying to avoid bringing calculus into the problem here, + since I have already been forced to redefine the calculus in + another paper. Bringing all that into this paper was + something I hoped to skirt, retaining a transparent explanation + to the end. For some readers I will have achieved that already. + For others (the ones asking about time now) I won't. For these I + will attempt a transparent explanation that still avoids a full + use of the calculus, either the historical treatment, or my own. + When I say that we must monitor the rate at which D approaches + AC, that rate is time. Whenever someone says "rate" you + should hear "time". In any physical situation, time is + always underneath all our distance measurements. We can draw a + circle and refuse to monitor the time involved in drawing it, but + time is there regardless. It is there when we remember that all + distances are velocities or accelerations; and it is also there + when we remember that limits must be approached in some real and + rigorous way. The curve AC cannot be straightened out like a + piece of string because that curve is a curve made up of both + distance and time. If you straighten it out and measure it like + string, you are measuring the distance but not the time, and so + you get the wrong answer. +

    +

    There are two + ways to think of this. One is to think of time as an actual + length itself. Say you have a curve and a line that are equal + lengths, according abstract geometry or the string method. The + curve will have more time "embedded" in it. It would + take you more time to travel it at the same velocity (as the + standard model already admits with its "orbital velocity"). + Therefore, when you straighten it out, you should monitor both + the distance and the time. If you do this, the time will add to + the distance, and your curve will be appreciably longer than you + expect. You can actually add the difference in time to the end of + the line and get the correct answer, so thinking of time as + embedded in the curve is not just a pretty visualization. It is + mathematically true. +

    +

    Another way + to think of it is to think of the line and curve as made up of + atoms. Let the atoms be the distance and the separation between + atoms be the time. If you straighten out a curve, you must + compress the atoms, losing somewhat of the separation. If you + have lost this separation, then you have lost part of the "time" + and therefore the distance. A curve cannot be straightened out + without affecting its real length. This is not just a + visualization either. It is physically true. The best way to see + it is again with a diagram:

    +

    +

    We want to + straighten the piece of curved string 1-4-5 out into the piece of + straight string 1-2-3. Although it is clear that the curve is + longer than the straight line, let us follow the standard model + and define both strings as equal length and see what happens. I + use this diagram because some will say that no real compression + is involved if we imagine a string with no width. They will say + that a real piece of string will be compressed only on the + outside, as you straighten it, but that the inside of the curve + will be stretched. They will say that a one-dimensional string + will not be compressed as you straighten it. This diagram shows + that is false. Even a piece of string composed of only one line + of atoms, with the distance between adjacent atoms always only + 2r, will be compressed. To see this, all we have to do is roll + ball 5 up to ball 3. If we do that, the mark that is the curve + must leak out the ends, showing that compression took place. This + is obviously because the line AB is equal to CDE, not to the + curve. The curve is longer than CDE, and must be compressed to + equal AB. +

    +

    Those who are + especially prickly will say, "Well, if the curve is longer + than AB, then the two strings were not the same length. You + should define the curve as equal to AB." But if we do that, + we have the opposite problem, in that we can't make the balls the + same size in the curved string as in the straight one. If we + define the curve as equal to AB, then we have to make our balls + bigger along the curve. So that when we roll ball 5 up to meet + ball 3, we again get compression. No matter how you look at it, + you cannot straighten out a curve without materially, and + therefore mathematically, affecting it. +

    +

    Some will say + that this analysis assumes non-continuity, but it doesn't. It + doesn't even require a quantum view of matter. Those balls don't + have to stand for atoms. They can stand for little number 1's if + we like. They can stand for anything at all, physical or + mathematical, real or abstract, except zero or its equivalent. No + matter what the balls represent, compression is the logical + result. +

    +

    But let us + return to time. Time is central to this last visualization as + well. A second variable always supplies us with the rate of + change, and thereby the curve, and in the real world, time is + always present: it determines this rate of change, no matter what + other variables are present. Time is the reason that the curve + does not equal AB in this example. The difference between the two + is the difference in time. And the difference is large. The + difference between π and 4 is not small. Historically, we have + mis-measured the length of a curve by a large margin. +

    +
    +

    Professional + mathematicians will not be happy with all this for several + reasons. One, they will not like to see π treated with so little + respect. Something with such a long history should not be looked + at this closely. It is like looking at your grandmother with a + magnifying glass: impolite and impolitic, if nothing else. Only a + monomaniac would even entertain the idea that all of history was + wrong, about any idea at all. Two, They will not like all this + talk. If I have some new math to relate, I should just plop down + the equations and see if they can remain standing under fire. + This is the expected route to take. Three, they will not like my + "misuse" of the calculus, even if I never claim to be + orthodox. I will appear to them to be taking things to limits in + very strange ways, and explaining myself in even stranger ways. + Given Newton and Cauchy and the rest, I will simply be seen to be + doing calculus wrong. In response, I point out that the + invention of the calculus historically went hand in hand with + analyzing curves, and it was invented to analyze geometric + curves, not kinematic curves. What I mean by that is that + calculus and trigonometry and the orthodox use of limits was used + in an analysis that ignored time. Newton assumed that a circular + orbit could be thought of just like a circle given in geometry, + with no monitoring of time or velocity. In this he was actually + even more reductive than the Greeks themselves, who at least + asked if the point on the curve had a velocity. Some of their + analyses toyed with this idea. But Newton never questioned that + the distance around the circle was a raw distance, like the + geometric circumference. This is why he wrongly called the + orbital velocity a velocity, and this is why we still do it. We + don't include time in the curve, we just try to add it back in at + the end. This is why we retain the ridiculous habit of writing + the orbital "velocity" as a length over a time, even + though, as I have shown in great detail, and as was already + understood long ago, the orbital motion is made up of three + separate velocity vectors. Historically, common wisdom may have + been that there were only two velocities over each dt, but in any + case it was known that the orbital motion is not a simple + velocity. +

    +

    The calculus + since Newton has been an algorithm that is able to describe a + curve, given x and y changes. What does it find? The differential + calculus finds the slope at the tangent, which we are told is the + velocity at that point. Problem is, I have shown that whatever is + at that point doesn't have a velocity. It has an acceleration, at + the least, and in the circle it has a second-degree acceleration + (three t's in the denominator). So Newton's algorithm must fail. + If it works at all, it can only work on a geometric circle, where + time is not passing. But a kinematic circle is not equivalent + physically or mathematically to a geometric circle, so the + calculus must be reworked. +

    +

    Now, I have + never claimed that the calculus is wrong, in toto. Newton + was right about most things, and the calculus is a true and + useful algorithm, used correctly. It works great on geometric + curves, and it can be used to find π in a geometric circle. It + solves one of the problems Newton wanted to solve. But as it is + used now, it does not solve the problem of physical circles, + because physical circles are not geometric circles. Their curves + are not equivalent mathematical entities. This being so, I must + show how they differ and how the foundations of circle analysis + must change. Clearly, I cannot do this with raw equations. + Reworking an entire algorithm takes a good deal of groundwork, + and that groundwork requires a good deal of explanation. You + cannot rewrite two and a half millennia of history with a half + page of raw equations. If I want to make a major correction, I + must first convince that the correction is necessary. I hope I + have done that, at the least. +

    +
    +

    The Greeks + got us off on the wrong track by assuming that the arc approached + the chord as we exhausted the series (Archimedes actually let + smaller and smaller chords approach the circle, in the form of + polygons, but the idea is much the same, in reverse). Newton + and others solidified this error by formalizing it with their + calculi, and Cauchy covered up the error with an even less + physical formalization. For the past 200 years, this error has + been unrecognized because it was unrecognizable. As the + calculus—and thereby the curve—is now taught, no one could + possibly uncover any of this. Both the calculus and circle + geometry are taught as a series of increasingly abstract + equations, not as logical steps, physically grounded. But when + you return to the diagrams, as I have here, it is shockingly easy + to see that circular motion is another big mess. +

    +

    Quantitatively, + this may be THE biggest error in all of math and physics, since + every single physical equation with π in it must now be thrown + out and redone. The transform π must be jettisoned from all of + kinematics and dynamics.

    +

    You may ask + how physics has existed with such errors for so long. Shouldn’t + all engineering be impossible with errors of this magnitude? + Shouldn’t all of our machines immediately break and crash? Not + necessarily. Because we make the same mistakes in all our + equations, the equations are correct relative to each other. Most + of engineering is concerned with relative numbers, not absolute + numbers. For example, it is more important in physics—at least + as a matter of engineering—that we know the how the gravity of + Venus compares to the gravity of Mars, than that we know the real + gravity of either one. If we are wrong about all of them in the + same amount, most of our machines will still work. Only rarely + will a mistake in absolute numbers affect engineering of any + kind. I could show this with specific examples, but I believe it + is clear enough regardless. +

    +

    Now to sum + up. You should take from this paper several things: 1) There is + no such thing as an orbital velocity. An orbital “velocity” + is actually the summation of three separate and separable + velocities. In the diagram above, the orbital motion is AB - BD + + DC, which is obviously the same as AD + DC. 2) A curve may not be + measured by straightening it out like a piece of string. In any + physical situation, the distance traveled along a curve is found + by running perpendiculars and in no other way. As I have shown, + you do not have to go to any limit: all you have to do is draw + the largest perpendiculars. In the example above, you do not need + to draw lots of little steps and go to a limit: just draw AD and + DC. As long as AB equals AO, AB will always equal AC. 3) The + circumference equation is now C = 8r = 4d. This means that π is + extinct. +

    +

    All the + thousands of mathematicians who have been chasing more and more + precise values for π have been chasing a phantom. For, although + their equations may be correct in most ways, their assumptions + are wrong. Their first assumption has always been that Newton + understood circle geometry and that he understood how to do + calculus. Mathematicians and physicists since Newton have simply + taken his postulates as true: they have taken his algorithm and + attempted to fine tune it. But the algorithm is faulty at the + foundational level, and has been for millennia. Newton's first + and fundamental lemmae—which are themselves based on the + assumptions of the Greeks—are all false. At the limit, the + tangent is not equal to the chord, as + I have shown elsewhere. The chord is never approached by + either the arc or the tangent, or by any other mathematical + entity in trigonometry or calculus. In creating or measuring the + circle, we are not exhausting polygons, either inscribed or + superscribed, so the chord is immaterial. To create or measure + the circle, we should do it as I have done it here: with + orthogonal vectors. If we choose the correct vectors, we do not + have to approach any limit. We can use a sort of exhaustion, but + we do this only to show that the largest vectors are correct to + begin with. I "exhausted" a process of measurement + above (by increasing the steps along the arc), but this + exhaustion did not take our distance to a limit. It only showed + that, as a + distance, + one path was equivalent to another. Since this was what we were + seeking with the circumference, it gave us a very simple and + quick method. By choosing precisely the right analysis and + interval, we were able to solve without calculus, without limits, + without infinitesimals, without ultimate ratios, and without + integration or differentiation. In a nutshell, I returned to the + pre-calculus analysis of the Greeks, which lay under all modern + analyses. By correcting that analysis in a simple but crucial + way, I have changed and corrected every piece of circular math + and analysis since then. This means that π is now a relic + (except perhaps in scalar equations, like area and so on). Some + will be distraught that so much earnest work has been wasted, + both in mathematics and physics; but rather than hone an error to + the end of time, surely it is better to discover the simple truth + at last.

    +



    +

    +



    +

    +

    For + experimental proof of this paper, you may go to Proof + from NASA that π is 4.

    +

    Indication I + am correct also comes from the quantum realm, where replacing π + with 4 in quantum equations + explains many mysteries at that level. For instance, in my + quantum spin equations I am able to explain why the proton is + 1822 times as large as the electron. But the math only works if + π=4. I have done the same thing in many other places, correcting + Bohr's equations and many other basic equations using π=4. + In fact, I consider this the best evidence in favor of my new + kinematics: it works where π didn't. +

    +

    Also of + interest to some will be how this impacts Relativity via the + Friedmann Metric. In those papers I show that because the + early equations of Relativity were written as 3D or 4D + Pythagorean theorems, they are falsified by my analysis of pi + like everything else. Einstein and Hilbert and Klein assumed + that objects were travelling on slants in the math when they were + not. In any vector math, including tensor math, all objects are + travelling upon orthogonal vectors, which stand for velocities. +

    +



    +

    +



    +

    +

    +

    [Addendum, + August 30, 2016: + One of my readers suggested an easy proof of π=4 using a + surveyor's wheel. That is one of those rolling distance + measurers, with a handle. He said that since it was a rolling + wheel, exactly like a cycloid, they must use cycloid math to give + you the number you read on the meter. Since I have never used + one, I checked, and they don't use cycloid math, they use pi. + That's very curious, wouldn't you say? It looks like a cover up + to me. You will say that in measuring a distance—as a surveyor + would need—there is no reason to use cycloid math and every + reason to use pi. In that case, you aren't interested in the + length of the cycloid, you are interested in the length of the + straight line on the ground. True, as far as it goes. But see if + you can follow me on this. What if you are measuring the length + of a curve + on the ground, using a + surveyor's wheel? By using π, you assume the curve is just like + the straight line, and indeed that is what the surveyor's wheel + tells you. But if instead of measuring the length of the curve on + the ground, what if you wished to measure the new curved cycloid + you have drawn in the air with your wheel? In other words, you + have taken a cycloid and wrapped it around a curve. Will the + length stay the same? I suppose the mainstream would say yes, but + the obvious answer is NO. You have just integrated an external + motion into the math, so there is no logical way the length could + stay the same. In other words, you have just imported another + variable of the x, y, z sort, and that variable cannot be zero. + Consult the integral above, which requires a variable for each + constituent motion. The length of the curved cycloid cannot + logically be the same as the original cycloid. +

    +

    Maybe some of + my readers can grasp that, given that the cycloid is planar + rather than linear. It may help them visualize this. Take the red + cycloid curve in the diagram above and imagine curving the entire + diagram. If you don't see what I mean, think of cutting the whole + curve out with scissors and pasting it onto a curved surface, + like a soup can. After you do that, answer me this question: is + that new curved curve you have created the same as the flat curve + above? No, of course not. If you were writing an equation for it, + you couldn't use the same equation. You would have to write a new + more complex equation, with an extra variable in it to express + the new curvature. Since that new variable could not be zero, it + would necessarily change the length of the curve. Well, + if that is true, then the same logic must apply to any + curve, whether is it wrapped + by a cycloid or wrapped by a simple line. The length of the curve + cannot be measured in the same way you measure a straight line, + since the curve will always have an additional non-zero variable + to integrate into the math. The + length of the curve will always be greater than the length of the + “equivalent” straight line. +

    +

    Some will + say, “That may be true, but using cycloid math in a surveyor's + wheel won't solve it.” True enough. I didn't mean to imply that + using cycloid math would give you more useful numbers for the + wheel. I meant that using pi and the naive math they do use was a + cover up, since it hid the problem I have just shown you. And I + meant that by using cycloid math, I could show why the surveyor's + wheel must fail to correctly measure any curve. +

    +

    No doubt many + will answer me, “The surveyor's wheel doesn't + fail, since what a surveyor is + interested in is a simple length, not some mystical kinematic + distance like you are inventing here.” But that is false as + well. Let us say a surveyor is measuring a running track for the + Olympics. Well, he has to measure both the straight legs of the + track and the curves. And what he wants to know is how far the + runners have run, right? Well, running is kinematic. It is like a + little orbit. It requires real bodies to move through the curves. + It is not just curves sitting on the ground, it is curves being + run in real time. Therefore, to calculate the correct distances + through the curves, the surveyor must integrate all the motions + involved. Treating the curves as equivalent to the straights will + fail to do that. And yes, I am telling you the inside lane of the + standard track is longer than 400 meters. Or, the runners are + running considerably farther than 400 meters. This should be easy + to prove by timing groups of top athletes through straights and + curves. I predict it will be found that the athletes appear + to move + through the curves much slower than can be accounted for by + stress on the inside leg, etc. But if you use pi=4 to measure the + length of the curve, this discrepancy will vanish. +

    +

    In fact, I + find that some tests have been run, confirming this. At this + link to Brigham Young University [p. 25], we find that + “Depending upon the track, athletes may spend up to 60% of the + race on the turn (P. R. Greene & Monheit, 1990).” Holy Cow! + 60%? And no one ever thought that was strange? Using π=4, we + would predict 56% of the time to be spent in the curves [4/7.14]. + The other 4% would then be given to tighter curves requiring more + leg adjustments. Using running dynamics, you would never predict + a slowdown that great [60/40] in the turns. That's a slowdown of + 33%. But if we give most of that slowdown to a mismeasurement of + the curve using pi [a “slowdown” of 21%], it makes more + sense.] +

    +



    +

    +


    *For + more on this, see my + paper on the calculus.

    +

    **For those + of you who have read Dan's critique of this paper at Ex Falso, + you might want to go here + for the rebuttal. I didn't write it, since I find that the + arguments at Ex Falso pretty much fall apart on a first reading, + without the need of any rebuttal from me. And I don't know who + did. I only know the moniker Sleestak from my (old, now defunct) + youtube page, where Sleestak was one of my subscribers. And I had + thought he was there to hear me sing! +

    +



    +

    +
    +

    If this paper + was useful to you in any way, please consider donating a dollar + (or more) to the SAVE THE ARTISTS FOUNDATION. This will allow me + to continue writing these "unpublishable" things. Don't + be confused by paying Melisa Smith--that is just one of my many + noms de plume. If you are a Paypal user, there is no fee; + so it might be worth your while to become one. Otherwise they + will rob us 33 cents for each transaction. +

    +
    +

    + +

    +
    +


    +

    +
    +
    +



    +

    + + \ No newline at end of file diff --git a/gb.form.htmlview/src/gb.form.htmlview/.icon.png b/gb.form.htmlview/src/gb.form.htmlview/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..43bd84cae108efee8d0a9eae75f1dafe6a51f644 GIT binary patch literal 8507 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 1 Then + $aForward.Add($aBack.Pop()) + Inc $iNoHistory + Url_Write($aBack.Last) + Dec $iNoHistory + Else + Return True + Endif + +End + +Public Sub Forward() + + If $aForward.Count Then + $aBack.Add($aForward.Pop()) + Inc $iNoHistory + Url_Write($aBack.Last) + Dec $iNoHistory + Endif + +End + + +Public Sub _LoadCss(sUrl As String, sBase As String) As String + + Dim sPath As String + + 'Return + sPath = ResolveUrl(sUrl, sBase) + If Not sPath Then Return + Try Return File.Load(sPath) + +End + +Private Function Zoom_Read() As Float + + Return $fZoom + +End + +Private Sub Zoom_Write(Value As Float) + + Dim fLog As Float + Dim fX As Float + Dim fY As Float + + fLog = Log2(Value) + If fLog < -10 Or If fLog > 10 Then Return + If $fZoom = Value Then Return + + Try fX = ($hView.ScrollX + $hView.ClientW / 2) / $hView.ScrollW + Try fY = ($hView.ScrollY + $hView.ClientH / 2) / $hView.ScrollH + + $fZoom = Value + + View_Arrange + $hView.Scroll(fX * $hView.ScrollW - $hView.ClientW / 2, fY * $hView.ScrollH - $hView.ClientH / 2) + $hView.Refresh + +End + +Public Sub Reload() + + $hDoc.Reload() + View_Arrange + $hView.Refresh + +End + +Private Function MonospaceFont_Read() As Font + + Return $hMonospaceFont + +End + +Private Sub MonospaceFont_Write(Value As Font) + + If Value Then Value = Value.Copy() + $hMonospaceFont = Value + $hDoc.SetMonospaceFont(Value.Name) + Reload() + +End + +Public Sub View_KeyPress() + + If Key.Code = Key.Up Then + + If Key.Control Then + $hView.Scroll($hView.ScrollX, 0) + Else If Key.Shift Then + $hView.Scroll($hView.ScrollX, $hView.ScrollY - 1) + Else + $hView.Scroll($hView.ScrollX, $hView.ScrollY - $hView.ClientH / 10) + Endif + + Else If Key.Code = Key.Down Then + + If Key.Control Then + $hView.Scroll($hView.ScrollX, $hView.ScrollH) + Else If Key.Shift Then + $hView.Scroll($hView.ScrollX, $hView.ScrollY + 1) + Else + $hView.Scroll($hView.ScrollX, $hView.ScrollY + $hView.ClientH / 10) + Endif + + Else If Key.Code = Key.Left Then + + If Key.Control Then + $hView.Scroll(0, $hView.ScrollY) + Else If Key.Shift Then + $hView.Scroll($hView.ScrollX - 1, $hView.ScrollY) + Else + $hView.Scroll($hView.ScrollX - $hView.ClientW / 10, $hView.ScrollY) + Endif + + Else If Key.Code = Key.Right Then + + If Key.Control Then + $hView.Scroll($hView.ScrollH, $hView.ScrollY) + Else If Key.Shift Then + $hView.Scroll($hView.ScrollX + 1, $hView.ScrollY) + Else + $hView.Scroll($hView.ScrollX + $hView.ClientW / 10, $hView.ScrollY) + Endif + + Else If Key.Code = Key.PageUp Then + + $hView.Scroll($hView.ScrollX, $hView.ScrollY - $hView.ClientH) + + Else If Key.Code = Key.PageDown Then + + $hView.Scroll($hView.ScrollX, $hView.ScrollY + $hView.ClientH) + + Else If Key.Code = Key.Home Then + + $hView.Scroll($hView.ScrollX, 0) + + Else If Key.Code = Key.End Then + + $hView.Scroll($hView.ScrollX, $hView.ScrollH) + + Endif + +End + +Private Function Url_Read() As String + + Return $sUrl + +End + +Private Sub Url_Write(Value As String) + + If GotoLink(Value) Then Return + + $sUrl = Value + + If $iNoHistory = 0 Then + $aBack.Add(Value) + $aForward.Clear + Endif + +End + +Public Sub Load(Path As String) + + Dim iPos As Integer + Dim sAnchor As String + Dim Y As Integer + Dim sSaveDir As String + + iPos = RInStr(Path, "#") + If iPos Then + sAnchor = Mid$(Path, iPos + 1) + Path = Left(Path, iPos - 1) + Endif + + sSaveDir = $sDir + $sDir = File.Dir(Path) + Try Me.Html = File.Load(Path) + If Error Then + $sDir = sSaveDir + Error.Raise(Error.Text) + Endif + + $bLoaded = True + + Y = Max(0, $hDoc.FindAnchor(sAnchor)) + $hView.Scroll(0, Y * $fZoom) + +End + +Public Sub View_Leave() + + $hDoc.OnLeave() + +End + +Public Sub Clear() + + Me.Html = "" + $sUrl = "" + $aBack.Clear + $aForward.Clear + +End diff --git a/gb.form.htmlview/src/gb.form.htmlview/.src/HtmlViewDocument.class b/gb.form.htmlview/src/gb.form.htmlview/.src/HtmlViewDocument.class new file mode 100644 index 00000000..93a6408d --- /dev/null +++ b/gb.form.htmlview/src/gb.form.htmlview/.src/HtmlViewDocument.class @@ -0,0 +1,36 @@ +' Gambas class file + +Inherits _HtmlDocument + +Event _Fake + +Private Sub GetView() As HtmlView + + Return Object.Parent(Me) + +End + +Public Sub _LoadCss(sUrl As String, sBase As String) As String + + Return GetView()._LoadCss(sUrl, sBase) + +End + +Public Sub _SetCursor(sCursor As String) + + GetView()._SetCursor(sCursor) + +End + +Public Sub _Refresh(X As Integer, Y As Integer, W As Integer, H As Integer) + + GetView()._RefreshView(X, Y, W, H) + +End + +Public Sub _LoadImage(sUrl As String, sBase As String) As Image + + Return GetView()._LoadImage(sUrl, sBase) + +End + diff --git a/gb.form.htmlview/src/gb.form.htmlview/.src/Main.module b/gb.form.htmlview/src/gb.form.htmlview/.src/Main.module new file mode 100644 index 00000000..00113993 --- /dev/null +++ b/gb.form.htmlview/src/gb.form.htmlview/.src/Main.module @@ -0,0 +1,7 @@ +' Gambas module file + +Public Sub Main() + + Print "Hello world" + +End diff --git a/gb.form.htmlview/src/gb.form.htmlview/fail.png b/gb.form.htmlview/src/gb.form.htmlview/fail.png new file mode 100644 index 0000000000000000000000000000000000000000..0289156a8c91552ae24365586cfd51adb44cc60c GIT binary patch literal 720 zcmV;>0x$iEP)C00009a7bBm000ie z000ie0hKEb8vpMMrQ<;>r}@#slHU3E;&5x9_+6_uSw&H~;_tZ{Kh9>!|+y z_51hRdJkOTHG;7oolpa3}Sh$x-{CjiAo=mek`Lni>mjmQZ=aVvJ)mJ_@fxdFhJ<02kl zz8jM1w`9gm>62+wYTA}R*|z2SgUN&OVDh>SF2keQh4pCmGmW?I;q>PB?}xolxTy}? z-(Md9fae?N@2&R- zAoC6Nch`FZu=xi2JL`QvFTmy-=`YK`J0UjTXn!C8n{T{741mqIMSl p { + margin-top: 0; +} + +dd { + display: block; + margin-left: 4em; +} + +dl { + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 0; + margin-right: 0; +} + +dt { + display: block; +} + +ol ul, ul ol, ul ul, ol ol { + margin-top: 0; + margin-bottom: 0 +} + +blockquote { + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 4em; + margin-right: 4em; +} + +/*********** FORM ELEMENTS ************/ + +form { + display: block; + margin-top: 0em; +} + +option { + display: none; +} + +input, textarea, keygen, select, button, isindex { + margin: 0em; + color: initial; + line-height: normal; + text-transform: none; + text-indent: 0; + text-shadow: none; + display: inline-block; +} +input[type="hidden"] { + display: none; +} + + +article, aside, footer, header, hgroup, nav, section +{ + display: block; +} + diff --git a/gb.form.htmlview/src/gumbo/LICENSE b/gb.form.htmlview/src/gumbo/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/gb.form.htmlview/src/gumbo/attribute.c b/gb.form.htmlview/src/gumbo/attribute.c new file mode 100644 index 00000000..234927a5 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/attribute.c @@ -0,0 +1,44 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "attribute.h" + +#include +#include +#include +#include + +#include "util.h" + +struct GumboInternalParser; + +GumboAttribute* gumbo_get_attribute( + const GumboVector* attributes, const char* name) { + for (unsigned int i = 0; i < attributes->length; ++i) { + GumboAttribute* attr = attributes->data[i]; + if (!strcasecmp(attr->name, name)) { + return attr; + } + } + return NULL; +} + +void gumbo_destroy_attribute( + struct GumboInternalParser* parser, GumboAttribute* attribute) { + gumbo_parser_deallocate(parser, (void*) attribute->name); + gumbo_parser_deallocate(parser, (void*) attribute->value); + gumbo_parser_deallocate(parser, (void*) attribute); +} diff --git a/gb.form.htmlview/src/gumbo/attribute.h b/gb.form.htmlview/src/gumbo/attribute.h new file mode 100644 index 00000000..f9b8aea5 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/attribute.h @@ -0,0 +1,37 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#ifndef GUMBO_ATTRIBUTE_H_ +#define GUMBO_ATTRIBUTE_H_ + +#include "gumbo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalParser; + +// Release the memory used for an GumboAttribute, including the attribute +// itself. +void gumbo_destroy_attribute( + struct GumboInternalParser* parser, GumboAttribute* attribute); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_ATTRIBUTE_H_ diff --git a/gb.form.htmlview/src/gumbo/char_ref.c b/gb.form.htmlview/src/gumbo/char_ref.c new file mode 100644 index 00000000..b3c5ecca --- /dev/null +++ b/gb.form.htmlview/src/gumbo/char_ref.c @@ -0,0 +1,23069 @@ + +#line 1 "char_ref.rl" +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// This is a Ragel state machine re-implementation of the original char_ref.c, +// rewritten to improve efficiency. To generate the .c file from it, +// +// $ ragel -F0 char_ref.rl +// +// The generated source is also checked into source control so that most people +// hacking on the parser do not need to install ragel. + +#include "char_ref.h" + +#include +#include +#include +#include +#include // Only for debug assertions at present. + +#include "error.h" +#include "string_piece.h" +#include "utf8.h" +#include "util.h" + +struct GumboInternalParser; + +const int kGumboNoChar = -1; + +// Table of replacement characters. The spec specifies that any occurrence of +// the first character should be replaced by the second character, and a parse +// error recorded. +typedef struct { + int from_char; + int to_char; +} CharReplacement; + +static const CharReplacement kCharReplacements[] = {{0x00, 0xfffd}, + {0x0d, 0x000d}, {0x80, 0x20ac}, {0x81, 0x0081}, {0x82, 0x201A}, + {0x83, 0x0192}, {0x84, 0x201E}, {0x85, 0x2026}, {0x86, 0x2020}, + {0x87, 0x2021}, {0x88, 0x02C6}, {0x89, 0x2030}, {0x8A, 0x0160}, + {0x8B, 0x2039}, {0x8C, 0x0152}, {0x8D, 0x008D}, {0x8E, 0x017D}, + {0x8F, 0x008F}, {0x90, 0x0090}, {0x91, 0x2018}, {0x92, 0x2019}, + {0x93, 0x201C}, {0x94, 0x201D}, {0x95, 0x2022}, {0x96, 0x2013}, + {0x97, 0x2014}, {0x98, 0x02DC}, {0x99, 0x2122}, {0x9A, 0x0161}, + {0x9B, 0x203A}, {0x9C, 0x0153}, {0x9D, 0x009D}, {0x9E, 0x017E}, + {0x9F, 0x0178}, + // Terminator. + {-1, -1}}; + +static int parse_digit(int c, bool allow_hex) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (allow_hex && c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (allow_hex && c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + +static void add_no_digit_error( + struct GumboInternalParser* parser, Utf8Iterator* input) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + utf8iterator_fill_error_at_mark(input, error); + error->type = GUMBO_ERR_NUMERIC_CHAR_REF_NO_DIGITS; +} + +static void add_codepoint_error(struct GumboInternalParser* parser, + Utf8Iterator* input, GumboErrorType type, int codepoint) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + utf8iterator_fill_error_at_mark(input, error); + error->type = type; + error->v.codepoint = codepoint; +} + +static void add_named_reference_error(struct GumboInternalParser* parser, + Utf8Iterator* input, GumboErrorType type, GumboStringPiece text) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + utf8iterator_fill_error_at_mark(input, error); + error->type = type; + error->v.text = text; +} + +static int maybe_replace_codepoint(int codepoint) { + for (int i = 0; kCharReplacements[i].from_char != -1; ++i) { + if (kCharReplacements[i].from_char == codepoint) { + return kCharReplacements[i].to_char; + } + } + return -1; +} + +static bool consume_numeric_ref( + struct GumboInternalParser* parser, Utf8Iterator* input, int* output) { + utf8iterator_next(input); + bool is_hex = false; + int c = utf8iterator_current(input); + if (c == 'x' || c == 'X') { + is_hex = true; + utf8iterator_next(input); + c = utf8iterator_current(input); + } + + int digit = parse_digit(c, is_hex); + if (digit == -1) { + // First digit was invalid; add a parse error and return. + add_no_digit_error(parser, input); + utf8iterator_reset(input); + *output = kGumboNoChar; + return false; + } + + int codepoint = 0; + bool status = true; + do { + codepoint = (codepoint * (is_hex ? 16 : 10)) + digit; + utf8iterator_next(input); + digit = parse_digit(utf8iterator_current(input), is_hex); + } while (digit != -1); + + if (utf8iterator_current(input) != ';') { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON, codepoint); + status = false; + } else { + utf8iterator_next(input); + } + + int replacement = maybe_replace_codepoint(codepoint); + if (replacement != -1) { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); + *output = replacement; + return false; + } + + if ((codepoint >= 0xd800 && codepoint <= 0xdfff) || codepoint > 0x10ffff) { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); + *output = 0xfffd; + return false; + } + + if (utf8_is_invalid_code_point(codepoint) || codepoint == 0xb) { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); + status = false; + // But return it anyway, per spec. + } + *output = codepoint; + return status; +} + +static bool maybe_add_invalid_named_reference( + struct GumboInternalParser* parser, Utf8Iterator* input) { + // The iterator will always be reset in this code path, so we don't need to + // worry about consuming characters. + const char* start = utf8iterator_get_char_pointer(input); + int c = utf8iterator_current(input); + while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')) { + utf8iterator_next(input); + c = utf8iterator_current(input); + } + if (c == ';') { + GumboStringPiece bad_ref; + bad_ref.data = start; + bad_ref.length = utf8iterator_get_char_pointer(input) - start; + add_named_reference_error( + parser, input, GUMBO_ERR_NAMED_CHAR_REF_INVALID, bad_ref); + return false; + } + return true; +} + +#line 2465 "char_ref.rl" + +// clang-format off + +#line 238 "char_ref.c" +static const short _char_ref_actions[] = { + 0, 1, 0, 1, 1, 1, 2, 1, + 3, 1, 4, 1, 5, 1, 6, 1, + 7, 1, 8, 1, 9, 1, 10, 1, + 11, 1, 12, 1, 13, 1, 14, 1, + 15, 1, 16, 1, 17, 1, 18, 1, + 19, 1, 20, 1, 21, 1, 22, 1, + 23, 1, 24, 1, 25, 1, 26, 1, + 27, 1, 28, 1, 29, 1, 30, 1, + 31, 1, 32, 1, 33, 1, 34, 1, + 35, 1, 36, 1, 37, 1, 38, 1, + 39, 1, 40, 1, 41, 1, 42, 1, + 43, 1, 44, 1, 45, 1, 46, 1, + 47, 1, 48, 1, 49, 1, 50, 1, + 51, 1, 52, 1, 53, 1, 54, 1, + 55, 1, 56, 1, 57, 1, 58, 1, + 59, 1, 60, 1, 61, 1, 62, 1, + 63, 1, 64, 1, 65, 1, 66, 1, + 67, 1, 68, 1, 69, 1, 70, 1, + 71, 1, 72, 1, 73, 1, 74, 1, + 75, 1, 76, 1, 77, 1, 78, 1, + 79, 1, 80, 1, 81, 1, 82, 1, + 83, 1, 84, 1, 85, 1, 86, 1, + 87, 1, 88, 1, 89, 1, 90, 1, + 91, 1, 92, 1, 93, 1, 94, 1, + 95, 1, 96, 1, 97, 1, 98, 1, + 99, 1, 100, 1, 101, 1, 102, 1, + 103, 1, 104, 1, 105, 1, 106, 1, + 107, 1, 108, 1, 109, 1, 110, 1, + 111, 1, 112, 1, 113, 1, 114, 1, + 115, 1, 116, 1, 117, 1, 118, 1, + 119, 1, 120, 1, 121, 1, 122, 1, + 123, 1, 124, 1, 125, 1, 126, 1, + 127, 1, 128, 1, 129, 1, 130, 1, + 131, 1, 132, 1, 133, 1, 134, 1, + 135, 1, 136, 1, 137, 1, 138, 1, + 139, 1, 140, 1, 141, 1, 142, 1, + 143, 1, 144, 1, 145, 1, 146, 1, + 147, 1, 148, 1, 149, 1, 150, 1, + 151, 1, 152, 1, 153, 1, 154, 1, + 155, 1, 156, 1, 157, 1, 158, 1, + 159, 1, 160, 1, 161, 1, 162, 1, + 163, 1, 164, 1, 165, 1, 166, 1, + 167, 1, 168, 1, 169, 1, 170, 1, + 171, 1, 172, 1, 173, 1, 174, 1, + 175, 1, 176, 1, 177, 1, 178, 1, + 179, 1, 180, 1, 181, 1, 182, 1, + 183, 1, 184, 1, 185, 1, 186, 1, + 187, 1, 188, 1, 189, 1, 190, 1, + 191, 1, 192, 1, 193, 1, 194, 1, + 195, 1, 196, 1, 197, 1, 198, 1, + 199, 1, 200, 1, 201, 1, 202, 1, + 203, 1, 204, 1, 205, 1, 206, 1, + 207, 1, 208, 1, 209, 1, 210, 1, + 211, 1, 212, 1, 213, 1, 214, 1, + 215, 1, 216, 1, 217, 1, 218, 1, + 219, 1, 220, 1, 221, 1, 222, 1, + 223, 1, 224, 1, 225, 1, 226, 1, + 227, 1, 228, 1, 229, 1, 230, 1, + 231, 1, 232, 1, 233, 1, 234, 1, + 235, 1, 236, 1, 237, 1, 238, 1, + 239, 1, 240, 1, 241, 1, 242, 1, + 243, 1, 244, 1, 245, 1, 246, 1, + 247, 1, 248, 1, 249, 1, 250, 1, + 251, 1, 252, 1, 253, 1, 254, 1, + 255, 1, 256, 1, 257, 1, 258, 1, + 259, 1, 260, 1, 261, 1, 262, 1, + 263, 1, 264, 1, 265, 1, 266, 1, + 267, 1, 268, 1, 269, 1, 270, 1, + 271, 1, 272, 1, 273, 1, 274, 1, + 275, 1, 276, 1, 277, 1, 278, 1, + 279, 1, 280, 1, 281, 1, 282, 1, + 283, 1, 284, 1, 285, 1, 286, 1, + 287, 1, 288, 1, 289, 1, 290, 1, + 291, 1, 292, 1, 293, 1, 294, 1, + 295, 1, 296, 1, 297, 1, 298, 1, + 299, 1, 300, 1, 301, 1, 302, 1, + 303, 1, 304, 1, 305, 1, 306, 1, + 307, 1, 308, 1, 309, 1, 310, 1, + 311, 1, 312, 1, 313, 1, 314, 1, + 315, 1, 316, 1, 317, 1, 318, 1, + 319, 1, 320, 1, 321, 1, 322, 1, + 323, 1, 324, 1, 325, 1, 326, 1, + 327, 1, 328, 1, 329, 1, 330, 1, + 331, 1, 332, 1, 333, 1, 334, 1, + 335, 1, 336, 1, 337, 1, 338, 1, + 339, 1, 340, 1, 341, 1, 342, 1, + 343, 1, 344, 1, 345, 1, 346, 1, + 347, 1, 348, 1, 349, 1, 350, 1, + 351, 1, 352, 1, 353, 1, 354, 1, + 355, 1, 356, 1, 357, 1, 358, 1, + 359, 1, 360, 1, 361, 1, 362, 1, + 363, 1, 364, 1, 365, 1, 366, 1, + 367, 1, 368, 1, 369, 1, 370, 1, + 371, 1, 372, 1, 373, 1, 374, 1, + 375, 1, 376, 1, 377, 1, 378, 1, + 379, 1, 380, 1, 381, 1, 382, 1, + 383, 1, 384, 1, 385, 1, 386, 1, + 387, 1, 388, 1, 389, 1, 390, 1, + 391, 1, 392, 1, 393, 1, 394, 1, + 395, 1, 396, 1, 397, 1, 398, 1, + 399, 1, 400, 1, 401, 1, 402, 1, + 403, 1, 404, 1, 405, 1, 406, 1, + 407, 1, 408, 1, 409, 1, 410, 1, + 411, 1, 412, 1, 413, 1, 414, 1, + 415, 1, 416, 1, 417, 1, 418, 1, + 419, 1, 420, 1, 421, 1, 422, 1, + 423, 1, 424, 1, 425, 1, 426, 1, + 427, 1, 428, 1, 429, 1, 430, 1, + 431, 1, 432, 1, 433, 1, 434, 1, + 435, 1, 436, 1, 437, 1, 438, 1, + 439, 1, 440, 1, 441, 1, 442, 1, + 443, 1, 444, 1, 445, 1, 446, 1, + 447, 1, 448, 1, 449, 1, 450, 1, + 451, 1, 452, 1, 453, 1, 454, 1, + 455, 1, 456, 1, 457, 1, 458, 1, + 459, 1, 460, 1, 461, 1, 462, 1, + 463, 1, 464, 1, 465, 1, 466, 1, + 467, 1, 468, 1, 469, 1, 470, 1, + 471, 1, 472, 1, 473, 1, 474, 1, + 475, 1, 476, 1, 477, 1, 478, 1, + 479, 1, 480, 1, 481, 1, 482, 1, + 483, 1, 484, 1, 485, 1, 486, 1, + 487, 1, 488, 1, 489, 1, 490, 1, + 491, 1, 492, 1, 493, 1, 494, 1, + 495, 1, 496, 1, 497, 1, 498, 1, + 499, 1, 500, 1, 501, 1, 502, 1, + 503, 1, 504, 1, 505, 1, 506, 1, + 507, 1, 508, 1, 509, 1, 510, 1, + 511, 1, 512, 1, 513, 1, 514, 1, + 515, 1, 516, 1, 517, 1, 518, 1, + 519, 1, 520, 1, 521, 1, 522, 1, + 523, 1, 524, 1, 525, 1, 526, 1, + 527, 1, 528, 1, 529, 1, 530, 1, + 531, 1, 532, 1, 533, 1, 534, 1, + 535, 1, 536, 1, 537, 1, 538, 1, + 539, 1, 540, 1, 541, 1, 542, 1, + 543, 1, 544, 1, 545, 1, 546, 1, + 547, 1, 548, 1, 549, 1, 550, 1, + 551, 1, 552, 1, 553, 1, 554, 1, + 555, 1, 556, 1, 557, 1, 558, 1, + 559, 1, 560, 1, 561, 1, 562, 1, + 563, 1, 564, 1, 565, 1, 566, 1, + 567, 1, 568, 1, 569, 1, 570, 1, + 571, 1, 572, 1, 573, 1, 574, 1, + 575, 1, 576, 1, 577, 1, 578, 1, + 579, 1, 580, 1, 581, 1, 582, 1, + 583, 1, 584, 1, 585, 1, 586, 1, + 587, 1, 588, 1, 589, 1, 590, 1, + 591, 1, 592, 1, 593, 1, 594, 1, + 595, 1, 596, 1, 597, 1, 598, 1, + 599, 1, 600, 1, 601, 1, 602, 1, + 603, 1, 604, 1, 605, 1, 606, 1, + 607, 1, 608, 1, 609, 1, 610, 1, + 611, 1, 612, 1, 613, 1, 614, 1, + 615, 1, 616, 1, 617, 1, 618, 1, + 619, 1, 620, 1, 621, 1, 622, 1, + 623, 1, 624, 1, 625, 1, 626, 1, + 627, 1, 628, 1, 629, 1, 630, 1, + 631, 1, 632, 1, 633, 1, 634, 1, + 635, 1, 636, 1, 637, 1, 638, 1, + 639, 1, 640, 1, 641, 1, 642, 1, + 643, 1, 644, 1, 645, 1, 646, 1, + 647, 1, 648, 1, 649, 1, 650, 1, + 651, 1, 652, 1, 653, 1, 654, 1, + 655, 1, 656, 1, 657, 1, 658, 1, + 659, 1, 660, 1, 661, 1, 662, 1, + 663, 1, 664, 1, 665, 1, 666, 1, + 667, 1, 668, 1, 669, 1, 670, 1, + 671, 1, 672, 1, 673, 1, 674, 1, + 675, 1, 676, 1, 677, 1, 678, 1, + 679, 1, 680, 1, 681, 1, 682, 1, + 683, 1, 684, 1, 685, 1, 686, 1, + 687, 1, 688, 1, 689, 1, 690, 1, + 691, 1, 692, 1, 693, 1, 694, 1, + 695, 1, 696, 1, 697, 1, 698, 1, + 699, 1, 700, 1, 701, 1, 702, 1, + 703, 1, 704, 1, 705, 1, 706, 1, + 707, 1, 708, 1, 709, 1, 710, 1, + 711, 1, 712, 1, 713, 1, 714, 1, + 715, 1, 716, 1, 717, 1, 718, 1, + 719, 1, 720, 1, 721, 1, 722, 1, + 723, 1, 724, 1, 725, 1, 726, 1, + 727, 1, 728, 1, 729, 1, 730, 1, + 731, 1, 732, 1, 733, 1, 734, 1, + 735, 1, 736, 1, 737, 1, 738, 1, + 739, 1, 740, 1, 741, 1, 742, 1, + 743, 1, 744, 1, 745, 1, 746, 1, + 747, 1, 748, 1, 749, 1, 750, 1, + 751, 1, 752, 1, 753, 1, 754, 1, + 755, 1, 756, 1, 757, 1, 758, 1, + 759, 1, 760, 1, 761, 1, 762, 1, + 763, 1, 764, 1, 765, 1, 766, 1, + 767, 1, 768, 1, 769, 1, 770, 1, + 771, 1, 772, 1, 773, 1, 774, 1, + 775, 1, 776, 1, 777, 1, 778, 1, + 779, 1, 780, 1, 781, 1, 782, 1, + 783, 1, 784, 1, 785, 1, 786, 1, + 787, 1, 788, 1, 789, 1, 790, 1, + 791, 1, 792, 1, 793, 1, 794, 1, + 795, 1, 796, 1, 797, 1, 798, 1, + 799, 1, 800, 1, 801, 1, 802, 1, + 803, 1, 804, 1, 805, 1, 806, 1, + 807, 1, 808, 1, 809, 1, 810, 1, + 811, 1, 812, 1, 813, 1, 814, 1, + 815, 1, 816, 1, 817, 1, 818, 1, + 819, 1, 820, 1, 821, 1, 822, 1, + 823, 1, 824, 1, 825, 1, 826, 1, + 827, 1, 828, 1, 829, 1, 830, 1, + 831, 1, 832, 1, 833, 1, 834, 1, + 835, 1, 836, 1, 837, 1, 838, 1, + 839, 1, 840, 1, 841, 1, 842, 1, + 843, 1, 844, 1, 845, 1, 846, 1, + 847, 1, 848, 1, 849, 1, 850, 1, + 851, 1, 852, 1, 853, 1, 854, 1, + 855, 1, 856, 1, 857, 1, 858, 1, + 859, 1, 860, 1, 861, 1, 862, 1, + 863, 1, 864, 1, 865, 1, 866, 1, + 867, 1, 868, 1, 869, 1, 870, 1, + 871, 1, 872, 1, 873, 1, 874, 1, + 875, 1, 876, 1, 877, 1, 878, 1, + 879, 1, 880, 1, 881, 1, 882, 1, + 883, 1, 884, 1, 885, 1, 886, 1, + 887, 1, 888, 1, 889, 1, 890, 1, + 891, 1, 892, 1, 893, 1, 894, 1, + 895, 1, 896, 1, 897, 1, 898, 1, + 899, 1, 900, 1, 901, 1, 902, 1, + 903, 1, 904, 1, 905, 1, 906, 1, + 907, 1, 908, 1, 909, 1, 910, 1, + 911, 1, 912, 1, 913, 1, 914, 1, + 915, 1, 916, 1, 917, 1, 918, 1, + 919, 1, 920, 1, 921, 1, 922, 1, + 923, 1, 924, 1, 925, 1, 926, 1, + 927, 1, 928, 1, 929, 1, 930, 1, + 931, 1, 932, 1, 933, 1, 934, 1, + 935, 1, 936, 1, 937, 1, 938, 1, + 939, 1, 940, 1, 941, 1, 942, 1, + 943, 1, 944, 1, 945, 1, 946, 1, + 947, 1, 948, 1, 949, 1, 950, 1, + 951, 1, 952, 1, 953, 1, 954, 1, + 955, 1, 956, 1, 957, 1, 958, 1, + 959, 1, 960, 1, 961, 1, 962, 1, + 963, 1, 964, 1, 965, 1, 966, 1, + 967, 1, 968, 1, 969, 1, 970, 1, + 971, 1, 972, 1, 973, 1, 974, 1, + 975, 1, 976, 1, 977, 1, 978, 1, + 979, 1, 980, 1, 981, 1, 982, 1, + 983, 1, 984, 1, 985, 1, 986, 1, + 987, 1, 988, 1, 989, 1, 990, 1, + 991, 1, 992, 1, 993, 1, 994, 1, + 995, 1, 996, 1, 997, 1, 998, 1, + 999, 1, 1000, 1, 1001, 1, 1002, 1, + 1003, 1, 1004, 1, 1005, 1, 1006, 1, + 1007, 1, 1008, 1, 1009, 1, 1010, 1, + 1011, 1, 1012, 1, 1013, 1, 1014, 1, + 1015, 1, 1016, 1, 1017, 1, 1018, 1, + 1019, 1, 1020, 1, 1021, 1, 1022, 1, + 1023, 1, 1024, 1, 1025, 1, 1026, 1, + 1027, 1, 1028, 1, 1029, 1, 1030, 1, + 1031, 1, 1032, 1, 1033, 1, 1034, 1, + 1035, 1, 1036, 1, 1037, 1, 1038, 1, + 1039, 1, 1040, 1, 1041, 1, 1042, 1, + 1043, 1, 1044, 1, 1045, 1, 1046, 1, + 1047, 1, 1048, 1, 1049, 1, 1050, 1, + 1051, 1, 1052, 1, 1053, 1, 1054, 1, + 1055, 1, 1056, 1, 1057, 1, 1058, 1, + 1059, 1, 1060, 1, 1061, 1, 1062, 1, + 1063, 1, 1064, 1, 1065, 1, 1066, 1, + 1067, 1, 1068, 1, 1069, 1, 1070, 1, + 1071, 1, 1072, 1, 1073, 1, 1074, 1, + 1075, 1, 1076, 1, 1077, 1, 1078, 1, + 1079, 1, 1080, 1, 1081, 1, 1082, 1, + 1083, 1, 1084, 1, 1085, 1, 1086, 1, + 1087, 1, 1088, 1, 1089, 1, 1090, 1, + 1091, 1, 1092, 1, 1093, 1, 1094, 1, + 1095, 1, 1096, 1, 1097, 1, 1098, 1, + 1099, 1, 1100, 1, 1101, 1, 1102, 1, + 1103, 1, 1104, 1, 1105, 1, 1106, 1, + 1107, 1, 1108, 1, 1109, 1, 1110, 1, + 1111, 1, 1112, 1, 1113, 1, 1114, 1, + 1115, 1, 1116, 1, 1117, 1, 1118, 1, + 1119, 1, 1120, 1, 1121, 1, 1122, 1, + 1123, 1, 1124, 1, 1125, 1, 1126, 1, + 1127, 1, 1128, 1, 1129, 1, 1130, 1, + 1131, 1, 1132, 1, 1133, 1, 1134, 1, + 1135, 1, 1136, 1, 1137, 1, 1138, 1, + 1139, 1, 1140, 1, 1141, 1, 1142, 1, + 1143, 1, 1144, 1, 1145, 1, 1146, 1, + 1147, 1, 1148, 1, 1149, 1, 1150, 1, + 1151, 1, 1152, 1, 1153, 1, 1154, 1, + 1155, 1, 1156, 1, 1157, 1, 1158, 1, + 1159, 1, 1160, 1, 1161, 1, 1162, 1, + 1163, 1, 1164, 1, 1165, 1, 1166, 1, + 1167, 1, 1168, 1, 1169, 1, 1170, 1, + 1171, 1, 1172, 1, 1173, 1, 1174, 1, + 1175, 1, 1176, 1, 1177, 1, 1178, 1, + 1179, 1, 1180, 1, 1181, 1, 1182, 1, + 1183, 1, 1184, 1, 1185, 1, 1186, 1, + 1187, 1, 1188, 1, 1189, 1, 1190, 1, + 1191, 1, 1192, 1, 1193, 1, 1194, 1, + 1195, 1, 1196, 1, 1197, 1, 1198, 1, + 1199, 1, 1200, 1, 1201, 1, 1202, 1, + 1203, 1, 1204, 1, 1205, 1, 1206, 1, + 1207, 1, 1208, 1, 1209, 1, 1210, 1, + 1211, 1, 1212, 1, 1213, 1, 1214, 1, + 1215, 1, 1216, 1, 1217, 1, 1218, 1, + 1219, 1, 1220, 1, 1221, 1, 1222, 1, + 1223, 1, 1224, 1, 1225, 1, 1226, 1, + 1227, 1, 1228, 1, 1229, 1, 1230, 1, + 1231, 1, 1232, 1, 1233, 1, 1234, 1, + 1235, 1, 1236, 1, 1237, 1, 1238, 1, + 1239, 1, 1240, 1, 1241, 1, 1242, 1, + 1243, 1, 1244, 1, 1245, 1, 1246, 1, + 1247, 1, 1248, 1, 1249, 1, 1250, 1, + 1251, 1, 1252, 1, 1253, 1, 1254, 1, + 1255, 1, 1256, 1, 1257, 1, 1258, 1, + 1259, 1, 1260, 1, 1261, 1, 1262, 1, + 1263, 1, 1264, 1, 1265, 1, 1266, 1, + 1267, 1, 1268, 1, 1269, 1, 1270, 1, + 1271, 1, 1272, 1, 1273, 1, 1274, 1, + 1275, 1, 1276, 1, 1277, 1, 1278, 1, + 1279, 1, 1280, 1, 1281, 1, 1282, 1, + 1283, 1, 1284, 1, 1285, 1, 1286, 1, + 1287, 1, 1288, 1, 1289, 1, 1290, 1, + 1291, 1, 1292, 1, 1293, 1, 1294, 1, + 1295, 1, 1296, 1, 1297, 1, 1298, 1, + 1299, 1, 1300, 1, 1301, 1, 1302, 1, + 1303, 1, 1304, 1, 1305, 1, 1306, 1, + 1307, 1, 1308, 1, 1309, 1, 1310, 1, + 1311, 1, 1312, 1, 1313, 1, 1314, 1, + 1315, 1, 1316, 1, 1317, 1, 1318, 1, + 1319, 1, 1320, 1, 1321, 1, 1322, 1, + 1323, 1, 1324, 1, 1325, 1, 1326, 1, + 1327, 1, 1328, 1, 1329, 1, 1330, 1, + 1331, 1, 1332, 1, 1333, 1, 1334, 1, + 1335, 1, 1336, 1, 1337, 1, 1338, 1, + 1339, 1, 1340, 1, 1341, 1, 1342, 1, + 1343, 1, 1344, 1, 1345, 1, 1346, 1, + 1347, 1, 1348, 1, 1349, 1, 1350, 1, + 1351, 1, 1352, 1, 1353, 1, 1354, 1, + 1355, 1, 1356, 1, 1357, 1, 1358, 1, + 1359, 1, 1360, 1, 1361, 1, 1362, 1, + 1363, 1, 1364, 1, 1365, 1, 1366, 1, + 1367, 1, 1368, 1, 1369, 1, 1370, 1, + 1371, 1, 1372, 1, 1373, 1, 1374, 1, + 1375, 1, 1376, 1, 1377, 1, 1378, 1, + 1379, 1, 1380, 1, 1381, 1, 1382, 1, + 1383, 1, 1384, 1, 1385, 1, 1386, 1, + 1387, 1, 1388, 1, 1389, 1, 1390, 1, + 1391, 1, 1392, 1, 1393, 1, 1394, 1, + 1395, 1, 1396, 1, 1397, 1, 1398, 1, + 1399, 1, 1400, 1, 1401, 1, 1402, 1, + 1403, 1, 1404, 1, 1405, 1, 1406, 1, + 1407, 1, 1408, 1, 1409, 1, 1410, 1, + 1411, 1, 1412, 1, 1413, 1, 1414, 1, + 1415, 1, 1416, 1, 1417, 1, 1418, 1, + 1419, 1, 1420, 1, 1421, 1, 1422, 1, + 1423, 1, 1424, 1, 1425, 1, 1426, 1, + 1427, 1, 1428, 1, 1429, 1, 1430, 1, + 1431, 1, 1432, 1, 1433, 1, 1434, 1, + 1435, 1, 1436, 1, 1437, 1, 1438, 1, + 1439, 1, 1440, 1, 1441, 1, 1442, 1, + 1443, 1, 1444, 1, 1445, 1, 1446, 1, + 1447, 1, 1448, 1, 1449, 1, 1450, 1, + 1451, 1, 1452, 1, 1453, 1, 1454, 1, + 1455, 1, 1456, 1, 1457, 1, 1458, 1, + 1459, 1, 1460, 1, 1461, 1, 1462, 1, + 1463, 1, 1464, 1, 1465, 1, 1466, 1, + 1467, 1, 1468, 1, 1469, 1, 1470, 1, + 1471, 1, 1472, 1, 1473, 1, 1474, 1, + 1475, 1, 1476, 1, 1477, 1, 1478, 1, + 1479, 1, 1480, 1, 1481, 1, 1482, 1, + 1483, 1, 1484, 1, 1485, 1, 1486, 1, + 1487, 1, 1488, 1, 1489, 1, 1490, 1, + 1491, 1, 1492, 1, 1493, 1, 1494, 1, + 1495, 1, 1496, 1, 1497, 1, 1498, 1, + 1499, 1, 1500, 1, 1501, 1, 1502, 1, + 1503, 1, 1504, 1, 1505, 1, 1506, 1, + 1507, 1, 1508, 1, 1509, 1, 1510, 1, + 1511, 1, 1512, 1, 1513, 1, 1514, 1, + 1515, 1, 1516, 1, 1517, 1, 1518, 1, + 1519, 1, 1520, 1, 1521, 1, 1522, 1, + 1523, 1, 1524, 1, 1525, 1, 1526, 1, + 1527, 1, 1528, 1, 1529, 1, 1530, 1, + 1531, 1, 1532, 1, 1533, 1, 1534, 1, + 1535, 1, 1536, 1, 1537, 1, 1538, 1, + 1539, 1, 1540, 1, 1541, 1, 1542, 1, + 1543, 1, 1544, 1, 1545, 1, 1546, 1, + 1547, 1, 1548, 1, 1549, 1, 1550, 1, + 1551, 1, 1552, 1, 1553, 1, 1554, 1, + 1555, 1, 1556, 1, 1557, 1, 1558, 1, + 1559, 1, 1560, 1, 1561, 1, 1562, 1, + 1563, 1, 1564, 1, 1565, 1, 1566, 1, + 1567, 1, 1568, 1, 1569, 1, 1570, 1, + 1571, 1, 1572, 1, 1573, 1, 1574, 1, + 1575, 1, 1576, 1, 1577, 1, 1578, 1, + 1579, 1, 1580, 1, 1581, 1, 1582, 1, + 1583, 1, 1584, 1, 1585, 1, 1586, 1, + 1587, 1, 1588, 1, 1589, 1, 1590, 1, + 1591, 1, 1592, 1, 1593, 1, 1594, 1, + 1595, 1, 1596, 1, 1597, 1, 1598, 1, + 1599, 1, 1600, 1, 1601, 1, 1602, 1, + 1603, 1, 1604, 1, 1605, 1, 1606, 1, + 1607, 1, 1608, 1, 1609, 1, 1610, 1, + 1611, 1, 1612, 1, 1613, 1, 1614, 1, + 1615, 1, 1616, 1, 1617, 1, 1618, 1, + 1619, 1, 1620, 1, 1621, 1, 1622, 1, + 1623, 1, 1624, 1, 1625, 1, 1626, 1, + 1627, 1, 1628, 1, 1629, 1, 1630, 1, + 1631, 1, 1632, 1, 1633, 1, 1634, 1, + 1635, 1, 1636, 1, 1637, 1, 1638, 1, + 1639, 1, 1640, 1, 1641, 1, 1642, 1, + 1643, 1, 1644, 1, 1645, 1, 1646, 1, + 1647, 1, 1648, 1, 1649, 1, 1650, 1, + 1651, 1, 1652, 1, 1653, 1, 1654, 1, + 1655, 1, 1656, 1, 1657, 1, 1658, 1, + 1659, 1, 1660, 1, 1661, 1, 1662, 1, + 1663, 1, 1664, 1, 1665, 1, 1666, 1, + 1667, 1, 1668, 1, 1669, 1, 1670, 1, + 1671, 1, 1672, 1, 1673, 1, 1674, 1, + 1675, 1, 1676, 1, 1677, 1, 1678, 1, + 1679, 1, 1680, 1, 1681, 1, 1682, 1, + 1683, 1, 1684, 1, 1685, 1, 1686, 1, + 1687, 1, 1688, 1, 1689, 1, 1690, 1, + 1691, 1, 1692, 1, 1693, 1, 1694, 1, + 1695, 1, 1696, 1, 1697, 1, 1698, 1, + 1699, 1, 1700, 1, 1701, 1, 1702, 1, + 1703, 1, 1704, 1, 1705, 1, 1706, 1, + 1707, 1, 1708, 1, 1709, 1, 1710, 1, + 1711, 1, 1712, 1, 1713, 1, 1714, 1, + 1715, 1, 1716, 1, 1717, 1, 1718, 1, + 1719, 1, 1720, 1, 1721, 1, 1722, 1, + 1723, 1, 1724, 1, 1725, 1, 1726, 1, + 1727, 1, 1728, 1, 1729, 1, 1730, 1, + 1731, 1, 1732, 1, 1733, 1, 1734, 1, + 1735, 1, 1736, 1, 1737, 1, 1738, 1, + 1739, 1, 1740, 1, 1741, 1, 1742, 1, + 1743, 1, 1744, 1, 1745, 1, 1746, 1, + 1747, 1, 1748, 1, 1749, 1, 1750, 1, + 1751, 1, 1752, 1, 1753, 1, 1754, 1, + 1755, 1, 1756, 1, 1757, 1, 1758, 1, + 1759, 1, 1760, 1, 1761, 1, 1762, 1, + 1763, 1, 1764, 1, 1765, 1, 1766, 1, + 1767, 1, 1768, 1, 1769, 1, 1770, 1, + 1771, 1, 1772, 1, 1773, 1, 1774, 1, + 1775, 1, 1776, 1, 1777, 1, 1778, 1, + 1779, 1, 1780, 1, 1781, 1, 1782, 1, + 1783, 1, 1784, 1, 1785, 1, 1786, 1, + 1787, 1, 1788, 1, 1789, 1, 1790, 1, + 1791, 1, 1792, 1, 1793, 1, 1794, 1, + 1795, 1, 1796, 1, 1797, 1, 1798, 1, + 1799, 1, 1800, 1, 1801, 1, 1802, 1, + 1803, 1, 1804, 1, 1805, 1, 1806, 1, + 1807, 1, 1808, 1, 1809, 1, 1810, 1, + 1811, 1, 1812, 1, 1813, 1, 1814, 1, + 1815, 1, 1816, 1, 1817, 1, 1818, 1, + 1819, 1, 1820, 1, 1821, 1, 1822, 1, + 1823, 1, 1824, 1, 1825, 1, 1826, 1, + 1827, 1, 1828, 1, 1829, 1, 1830, 1, + 1831, 1, 1832, 1, 1833, 1, 1834, 1, + 1835, 1, 1836, 1, 1837, 1, 1838, 1, + 1839, 1, 1840, 1, 1841, 1, 1842, 1, + 1843, 1, 1844, 1, 1845, 1, 1846, 1, + 1847, 1, 1848, 1, 1849, 1, 1850, 1, + 1851, 1, 1852, 1, 1853, 1, 1854, 1, + 1855, 1, 1856, 1, 1857, 1, 1858, 1, + 1859, 1, 1860, 1, 1861, 1, 1862, 1, + 1863, 1, 1864, 1, 1865, 1, 1866, 1, + 1867, 1, 1868, 1, 1869, 1, 1870, 1, + 1871, 1, 1872, 1, 1873, 1, 1874, 1, + 1875, 1, 1876, 1, 1877, 1, 1878, 1, + 1879, 1, 1880, 1, 1881, 1, 1882, 1, + 1883, 1, 1884, 1, 1885, 1, 1886, 1, + 1887, 1, 1888, 1, 1889, 1, 1890, 1, + 1891, 1, 1892, 1, 1893, 1, 1894, 1, + 1895, 1, 1896, 1, 1897, 1, 1898, 1, + 1899, 1, 1900, 1, 1901, 1, 1902, 1, + 1903, 1, 1904, 1, 1905, 1, 1906, 1, + 1907, 1, 1908, 1, 1909, 1, 1910, 1, + 1911, 1, 1912, 1, 1913, 1, 1914, 1, + 1915, 1, 1916, 1, 1917, 1, 1918, 1, + 1919, 1, 1920, 1, 1921, 1, 1922, 1, + 1923, 1, 1924, 1, 1925, 1, 1926, 1, + 1927, 1, 1928, 1, 1929, 1, 1930, 1, + 1931, 1, 1932, 1, 1933, 1, 1934, 1, + 1935, 1, 1936, 1, 1937, 1, 1938, 1, + 1939, 1, 1940, 1, 1941, 1, 1942, 1, + 1943, 1, 1944, 1, 1945, 1, 1946, 1, + 1947, 1, 1948, 1, 1949, 1, 1950, 1, + 1951, 1, 1952, 1, 1953, 1, 1954, 1, + 1955, 1, 1956, 1, 1957, 1, 1958, 1, + 1959, 1, 1960, 1, 1961, 1, 1962, 1, + 1963, 1, 1964, 1, 1965, 1, 1966, 1, + 1967, 1, 1968, 1, 1969, 1, 1970, 1, + 1971, 1, 1972, 1, 1973, 1, 1974, 1, + 1975, 1, 1976, 1, 1977, 1, 1978, 1, + 1979, 1, 1980, 1, 1981, 1, 1982, 1, + 1983, 1, 1984, 1, 1985, 1, 1986, 1, + 1987, 1, 1988, 1, 1989, 1, 1990, 1, + 1991, 1, 1992, 1, 1993, 1, 1994, 1, + 1995, 1, 1996, 1, 1997, 1, 1998, 1, + 1999, 1, 2000, 1, 2001, 1, 2002, 1, + 2003, 1, 2004, 1, 2005, 1, 2006, 1, + 2007, 1, 2008, 1, 2009, 1, 2010, 1, + 2011, 1, 2012, 1, 2013, 1, 2014, 1, + 2015, 1, 2016, 1, 2017, 1, 2018, 1, + 2019, 1, 2020, 1, 2021, 1, 2022, 1, + 2023, 1, 2024, 1, 2025, 1, 2026, 1, + 2027, 1, 2028, 1, 2029, 1, 2030, 1, + 2031, 1, 2032, 1, 2033, 1, 2034, 1, + 2035, 1, 2036, 1, 2037, 1, 2038, 1, + 2039, 1, 2040, 1, 2041, 1, 2042, 1, + 2043, 1, 2044, 1, 2045, 1, 2046, 1, + 2047, 1, 2048, 1, 2049, 1, 2050, 1, + 2051, 1, 2052, 1, 2053, 1, 2054, 1, + 2055, 1, 2056, 1, 2057, 1, 2058, 1, + 2059, 1, 2060, 1, 2061, 1, 2062, 1, + 2063, 1, 2064, 1, 2065, 1, 2066, 1, + 2067, 1, 2068, 1, 2069, 1, 2070, 1, + 2071, 1, 2072, 1, 2073, 1, 2074, 1, + 2075, 1, 2076, 1, 2077, 1, 2078, 1, + 2079, 1, 2080, 1, 2081, 1, 2082, 1, + 2083, 1, 2084, 1, 2085, 1, 2086, 1, + 2087, 1, 2088, 1, 2089, 1, 2090, 1, + 2091, 1, 2092, 1, 2093, 1, 2094, 1, + 2095, 1, 2096, 1, 2097, 1, 2098, 1, + 2099, 1, 2100, 1, 2101, 1, 2102, 1, + 2103, 1, 2104, 1, 2105, 1, 2106, 1, + 2107, 1, 2108, 1, 2109, 1, 2110, 1, + 2111, 1, 2112, 1, 2113, 1, 2114, 1, + 2115, 1, 2116, 1, 2117, 1, 2118, 1, + 2119, 1, 2120, 1, 2121, 1, 2122, 1, + 2123, 1, 2124, 1, 2125, 1, 2126, 1, + 2127, 1, 2128, 1, 2129, 1, 2130, 1, + 2131, 1, 2132, 1, 2133, 1, 2134, 1, + 2135, 1, 2136, 1, 2137, 1, 2138, 1, + 2139, 1, 2140, 1, 2141, 1, 2142, 1, + 2143, 1, 2144, 1, 2145, 1, 2146, 1, + 2147, 1, 2148, 1, 2149, 1, 2150, 1, + 2151, 1, 2152, 1, 2153, 1, 2154, 1, + 2155, 1, 2156, 1, 2157, 1, 2158, 1, + 2159, 1, 2160, 1, 2161, 1, 2162, 1, + 2163, 1, 2164, 1, 2165, 1, 2166, 1, + 2167, 1, 2168, 1, 2169, 1, 2170, 1, + 2171, 1, 2172, 1, 2173, 1, 2174, 1, + 2175, 1, 2176, 1, 2177, 1, 2178, 1, + 2179, 1, 2180, 1, 2181, 1, 2182, 1, + 2183, 1, 2184, 1, 2185, 1, 2186, 1, + 2187, 1, 2188, 1, 2189, 1, 2190, 1, + 2191, 1, 2192, 1, 2193, 1, 2194, 1, + 2195, 1, 2196, 1, 2197, 1, 2198, 1, + 2199, 1, 2200, 1, 2201, 1, 2202, 1, + 2203, 1, 2204, 1, 2205, 1, 2206, 1, + 2207, 1, 2208, 1, 2209, 1, 2210, 1, + 2211, 1, 2212, 1, 2213, 1, 2214, 1, + 2215, 1, 2216, 1, 2217, 1, 2218, 1, + 2219, 1, 2220, 1, 2221, 1, 2222, 1, + 2223, 1, 2224, 1, 2225, 1, 2226, 1, + 2227, 1, 2228, 1, 2229, 1, 2230, 1, + 2231, 1, 2232, 1, 2233, 1, 2234, 1, + 2235, 1, 2236, 1, 2237, 1, 2238, 1, + 2239, 1, 2240, 1, 2241 +}; + +static const char _char_ref_trans_keys[] = { + 0, 0, 69, 117, 108, 108, 105, 105, 103, 103, 80, 80, 99, 99, 117, 117, + 116, 116, 101, 101, 114, 114, 101, 101, 118, 118, 101, 101, 59, 59, 105, 121, + 114, 114, 99, 99, 59, 59, 114, 114, 59, 59, 114, 114, 97, 97, 118, 118, + 101, 101, 112, 112, 104, 104, 97, 97, 59, 59, 97, 97, 99, 99, 114, 114, + 59, 59, 100, 100, 59, 59, 103, 112, 111, 111, 110, 110, 59, 59, 102, 102, + 59, 59, 112, 112, 108, 108, 121, 121, 70, 70, 117, 117, 110, 110, 99, 99, + 116, 116, 105, 105, 111, 111, 110, 110, 59, 59, 105, 105, 110, 110, 103, 103, + 99, 115, 114, 114, 59, 59, 105, 105, 103, 103, 110, 110, 59, 59, 105, 105, + 108, 108, 100, 100, 101, 101, 109, 109, 108, 108, 97, 117, 99, 114, 107, 107, + 115, 115, 108, 108, 97, 97, 115, 115, 104, 104, 59, 59, 118, 119, 59, 59, + 101, 101, 100, 100, 59, 59, 121, 121, 59, 59, 99, 116, 97, 97, 117, 117, + 115, 115, 101, 101, 59, 59, 110, 110, 111, 111, 117, 117, 108, 108, 108, 108, + 105, 105, 115, 115, 59, 59, 97, 97, 59, 59, 114, 114, 59, 59, 112, 112, + 102, 102, 59, 59, 101, 101, 118, 118, 101, 101, 59, 59, 99, 99, 114, 114, + 59, 59, 109, 109, 112, 112, 101, 101, 113, 113, 59, 59, 72, 117, 99, 99, + 121, 121, 59, 59, 80, 80, 89, 89, 99, 121, 117, 117, 116, 116, 101, 101, + 59, 59, 59, 105, 116, 116, 97, 97, 108, 108, 68, 68, 105, 105, 102, 102, + 102, 102, 101, 101, 114, 114, 101, 101, 110, 110, 116, 116, 105, 105, 97, 97, + 108, 108, 68, 68, 59, 59, 108, 108, 101, 101, 121, 121, 115, 115, 59, 59, + 97, 111, 114, 114, 111, 111, 110, 110, 59, 59, 100, 100, 105, 105, 108, 108, + 114, 114, 99, 99, 59, 59, 110, 110, 105, 105, 110, 110, 116, 116, 59, 59, + 111, 111, 116, 116, 59, 59, 100, 110, 105, 105, 108, 108, 108, 108, 97, 97, + 59, 59, 116, 116, 101, 101, 114, 114, 68, 68, 111, 111, 116, 116, 59, 59, + 114, 114, 59, 59, 105, 105, 59, 59, 114, 114, 99, 99, 108, 108, 101, 101, + 68, 84, 111, 111, 116, 116, 59, 59, 105, 105, 110, 110, 117, 117, 115, 115, + 59, 59, 108, 108, 117, 117, 115, 115, 59, 59, 105, 105, 109, 109, 101, 101, + 115, 115, 59, 59, 111, 111, 99, 115, 107, 107, 119, 119, 105, 105, 115, 115, + 101, 101, 67, 67, 111, 111, 110, 110, 116, 116, 111, 111, 117, 117, 114, 114, + 73, 73, 110, 110, 116, 116, 101, 101, 103, 103, 114, 114, 97, 97, 108, 108, + 59, 59, 101, 101, 67, 67, 117, 117, 114, 114, 108, 108, 121, 121, 68, 81, + 111, 111, 117, 117, 98, 98, 108, 108, 101, 101, 81, 81, 117, 117, 111, 111, + 116, 116, 101, 101, 59, 59, 117, 117, 111, 111, 116, 116, 101, 101, 59, 59, + 108, 117, 111, 111, 110, 110, 59, 101, 59, 59, 103, 116, 114, 114, 117, 117, + 101, 101, 110, 110, 116, 116, 59, 59, 110, 110, 116, 116, 59, 59, 111, 111, + 117, 117, 114, 114, 73, 73, 110, 110, 116, 116, 101, 101, 103, 103, 114, 114, + 97, 97, 108, 108, 59, 59, 102, 114, 59, 59, 111, 111, 100, 100, 117, 117, + 99, 99, 116, 116, 59, 59, 110, 110, 116, 116, 101, 101, 114, 114, 67, 67, + 108, 108, 111, 111, 99, 99, 107, 107, 119, 119, 105, 105, 115, 115, 101, 101, + 67, 67, 111, 111, 110, 110, 116, 116, 111, 111, 117, 117, 114, 114, 73, 73, + 110, 110, 116, 116, 101, 101, 103, 103, 114, 114, 97, 97, 108, 108, 59, 59, + 111, 111, 115, 115, 115, 115, 59, 59, 99, 99, 114, 114, 59, 59, 112, 112, + 59, 67, 97, 97, 112, 112, 59, 59, 68, 115, 59, 111, 116, 116, 114, 114, + 97, 97, 104, 104, 100, 100, 59, 59, 99, 99, 121, 121, 59, 59, 99, 99, + 121, 121, 59, 59, 99, 99, 121, 121, 59, 59, 103, 115, 103, 103, 101, 101, + 114, 114, 59, 59, 114, 114, 59, 59, 104, 104, 118, 118, 59, 59, 97, 121, + 114, 114, 111, 111, 110, 110, 59, 59, 59, 59, 108, 108, 59, 116, 97, 97, + 59, 59, 114, 114, 59, 59, 97, 102, 99, 109, 114, 114, 105, 105, 116, 116, + 105, 105, 99, 99, 97, 97, 108, 108, 65, 84, 99, 99, 117, 117, 116, 116, + 101, 101, 59, 59, 111, 111, 116, 117, 59, 59, 98, 98, 108, 108, 101, 101, + 65, 65, 99, 99, 117, 117, 116, 116, 101, 101, 59, 59, 114, 114, 97, 97, + 118, 118, 101, 101, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, + 111, 111, 110, 110, 100, 100, 59, 59, 102, 102, 101, 101, 114, 114, 101, 101, + 110, 110, 116, 116, 105, 105, 97, 97, 108, 108, 68, 68, 59, 59, 112, 119, + 102, 102, 59, 59, 59, 69, 111, 111, 116, 116, 59, 59, 113, 113, 117, 117, + 97, 97, 108, 108, 59, 59, 98, 98, 108, 108, 101, 101, 67, 86, 111, 111, + 110, 110, 116, 116, 111, 111, 117, 117, 114, 114, 73, 73, 110, 110, 116, 116, + 101, 101, 103, 103, 114, 114, 97, 97, 108, 108, 59, 59, 111, 111, 116, 119, + 59, 59, 110, 110, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 101, 111, 102, 102, 116, 116, 65, 84, 114, 114, 114, 114, 111, 111, 119, 119, + 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 65, 65, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 59, 101, 101, 101, 101, 59, 59, 110, 110, 103, 103, + 76, 82, 101, 101, 102, 102, 116, 116, 65, 82, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 65, 65, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, + 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, + 104, 104, 116, 116, 65, 84, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 101, 101, 101, 101, 59, 59, 112, 112, 65, 68, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 111, 111, 119, 119, 110, 110, 65, 65, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 59, 101, 101, 114, 114, 116, 116, 105, 105, 99, 99, + 97, 97, 108, 108, 66, 66, 97, 97, 114, 114, 59, 59, 110, 110, 65, 97, + 114, 114, 114, 114, 111, 111, 119, 119, 59, 85, 97, 97, 114, 114, 59, 59, + 112, 112, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 114, 114, + 101, 101, 118, 118, 101, 101, 59, 59, 101, 101, 102, 102, 116, 116, 82, 86, + 105, 105, 103, 103, 104, 104, 116, 116, 86, 86, 101, 101, 99, 99, 116, 116, + 111, 111, 114, 114, 59, 59, 101, 101, 101, 101, 86, 86, 101, 101, 99, 99, + 116, 116, 111, 111, 114, 114, 59, 59, 101, 101, 99, 99, 116, 116, 111, 111, + 114, 114, 59, 66, 97, 97, 114, 114, 59, 59, 105, 105, 103, 103, 104, 104, + 116, 116, 84, 86, 101, 101, 101, 101, 86, 86, 101, 101, 99, 99, 116, 116, + 111, 111, 114, 114, 59, 59, 101, 101, 99, 99, 116, 116, 111, 111, 114, 114, + 59, 66, 97, 97, 114, 114, 59, 59, 101, 101, 101, 101, 59, 65, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 114, 114, 114, 114, 111, 111, 119, 119, + 59, 59, 99, 116, 114, 114, 59, 59, 114, 114, 111, 111, 107, 107, 59, 59, + 78, 120, 71, 71, 59, 59, 72, 72, 99, 99, 117, 117, 116, 116, 101, 101, + 97, 121, 114, 114, 111, 111, 110, 110, 59, 59, 114, 114, 99, 99, 59, 59, + 111, 111, 116, 116, 59, 59, 114, 114, 59, 59, 114, 114, 97, 97, 118, 118, + 101, 101, 101, 101, 109, 109, 101, 101, 110, 110, 116, 116, 59, 59, 97, 112, + 99, 99, 114, 114, 59, 59, 116, 116, 121, 121, 83, 86, 109, 109, 97, 97, + 108, 108, 108, 108, 83, 83, 113, 113, 117, 117, 97, 97, 114, 114, 101, 101, + 59, 59, 101, 101, 114, 114, 121, 121, 83, 83, 109, 109, 97, 97, 108, 108, + 108, 108, 83, 83, 113, 113, 117, 117, 97, 97, 114, 114, 101, 101, 59, 59, + 103, 112, 111, 111, 110, 110, 59, 59, 102, 102, 59, 59, 115, 115, 105, 105, + 108, 108, 111, 111, 110, 110, 59, 59, 117, 117, 97, 105, 108, 108, 59, 84, + 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, 108, 108, 105, 105, 98, 98, + 114, 114, 105, 105, 117, 117, 109, 109, 59, 59, 99, 105, 114, 114, 59, 59, + 109, 109, 59, 59, 97, 97, 59, 59, 109, 109, 108, 108, 105, 112, 115, 115, + 116, 116, 115, 115, 59, 59, 111, 111, 110, 110, 101, 101, 110, 110, 116, 116, + 105, 105, 97, 97, 108, 108, 69, 69, 59, 59, 99, 115, 121, 121, 59, 59, + 114, 114, 59, 59, 108, 108, 108, 108, 101, 101, 100, 100, 83, 86, 109, 109, + 97, 97, 108, 108, 108, 108, 83, 83, 113, 113, 117, 117, 97, 97, 114, 114, + 101, 101, 59, 59, 101, 101, 114, 114, 121, 121, 83, 83, 109, 109, 97, 97, + 108, 108, 108, 108, 83, 83, 113, 113, 117, 117, 97, 97, 114, 114, 101, 101, + 59, 59, 112, 117, 102, 102, 59, 59, 65, 65, 108, 108, 108, 108, 59, 59, + 114, 114, 105, 105, 101, 101, 114, 114, 116, 116, 114, 114, 102, 102, 59, 59, + 99, 99, 114, 114, 59, 59, 74, 116, 99, 99, 121, 121, 59, 59, 109, 109, + 109, 109, 97, 97, 59, 100, 59, 59, 114, 114, 101, 101, 118, 118, 101, 101, + 59, 59, 101, 121, 100, 100, 105, 105, 108, 108, 59, 59, 114, 114, 99, 99, + 59, 59, 59, 59, 111, 111, 116, 116, 59, 59, 114, 114, 59, 59, 59, 59, + 112, 112, 102, 102, 59, 59, 101, 101, 97, 97, 116, 116, 101, 101, 114, 114, + 69, 84, 113, 113, 117, 117, 97, 97, 108, 108, 59, 76, 101, 101, 115, 115, + 115, 115, 59, 59, 117, 117, 108, 108, 108, 108, 69, 69, 113, 113, 117, 117, + 97, 97, 108, 108, 59, 59, 114, 114, 101, 101, 97, 97, 116, 116, 101, 101, + 114, 114, 59, 59, 101, 101, 115, 115, 115, 115, 59, 59, 108, 108, 97, 97, + 110, 110, 116, 116, 69, 69, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, + 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, 99, 99, 114, 114, 59, 59, + 59, 59, 65, 117, 82, 82, 68, 68, 99, 99, 121, 121, 59, 59, 99, 116, + 101, 101, 107, 107, 59, 59, 59, 59, 105, 105, 114, 114, 99, 99, 59, 59, + 114, 114, 59, 59, 108, 108, 98, 98, 101, 101, 114, 114, 116, 116, 83, 83, + 112, 112, 97, 97, 99, 99, 101, 101, 59, 59, 112, 114, 102, 102, 59, 59, + 105, 105, 122, 122, 111, 111, 110, 110, 116, 116, 97, 97, 108, 108, 76, 76, + 105, 105, 110, 110, 101, 101, 59, 59, 99, 116, 114, 114, 59, 59, 114, 114, + 111, 111, 107, 107, 59, 59, 109, 109, 112, 112, 68, 69, 111, 111, 119, 119, + 110, 110, 72, 72, 117, 117, 109, 109, 112, 112, 59, 59, 113, 113, 117, 117, + 97, 97, 108, 108, 59, 59, 69, 117, 99, 99, 121, 121, 59, 59, 108, 108, + 105, 105, 103, 103, 59, 59, 99, 99, 121, 121, 59, 59, 99, 99, 117, 117, + 116, 116, 101, 101, 105, 121, 114, 114, 99, 99, 59, 59, 111, 111, 116, 116, + 59, 59, 114, 114, 59, 59, 114, 114, 97, 97, 118, 118, 101, 101, 59, 112, + 99, 103, 114, 114, 59, 59, 105, 105, 110, 110, 97, 97, 114, 114, 121, 121, + 73, 73, 59, 59, 108, 108, 105, 105, 101, 101, 115, 115, 59, 59, 116, 118, + 59, 101, 103, 114, 114, 114, 97, 97, 108, 108, 59, 59, 115, 115, 101, 101, + 99, 99, 116, 116, 105, 105, 111, 111, 110, 110, 59, 59, 105, 105, 115, 115, + 105, 105, 98, 98, 108, 108, 101, 101, 67, 84, 111, 111, 109, 109, 109, 109, + 97, 97, 59, 59, 105, 105, 109, 109, 101, 101, 115, 115, 59, 59, 103, 116, + 111, 111, 110, 110, 59, 59, 102, 102, 59, 59, 97, 97, 59, 59, 99, 99, + 114, 114, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, 107, 109, + 99, 99, 121, 121, 59, 59, 108, 108, 99, 117, 105, 121, 114, 114, 99, 99, + 59, 59, 59, 59, 114, 114, 59, 59, 112, 112, 102, 102, 59, 59, 99, 101, + 114, 114, 59, 59, 114, 114, 99, 99, 121, 121, 59, 59, 107, 107, 99, 99, + 121, 121, 59, 59, 72, 115, 99, 99, 121, 121, 59, 59, 99, 99, 121, 121, + 59, 59, 112, 112, 112, 112, 97, 97, 59, 59, 101, 121, 100, 100, 105, 105, + 108, 108, 59, 59, 59, 59, 114, 114, 59, 59, 112, 112, 102, 102, 59, 59, + 99, 99, 114, 114, 59, 59, 74, 116, 99, 99, 121, 121, 59, 59, 99, 114, + 117, 117, 116, 116, 101, 101, 59, 59, 98, 98, 100, 100, 97, 97, 59, 59, + 103, 103, 59, 59, 108, 108, 97, 97, 99, 99, 101, 101, 116, 116, 114, 114, + 102, 102, 59, 59, 114, 114, 59, 59, 97, 121, 114, 114, 111, 111, 110, 110, + 59, 59, 100, 100, 105, 105, 108, 108, 59, 59, 59, 59, 102, 115, 116, 116, + 65, 114, 110, 114, 103, 103, 108, 108, 101, 101, 66, 66, 114, 114, 97, 97, + 99, 99, 107, 107, 101, 101, 116, 116, 59, 59, 114, 114, 111, 111, 119, 119, + 59, 82, 97, 97, 114, 114, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, + 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 101, 101, 105, 105, + 108, 108, 105, 105, 110, 110, 103, 103, 59, 59, 111, 111, 117, 119, 98, 98, + 108, 108, 101, 101, 66, 66, 114, 114, 97, 97, 99, 99, 107, 107, 101, 101, + 116, 116, 59, 59, 110, 110, 84, 86, 101, 101, 101, 101, 86, 86, 101, 101, + 99, 99, 116, 116, 111, 111, 114, 114, 59, 59, 101, 101, 99, 99, 116, 116, + 111, 111, 114, 114, 59, 66, 97, 97, 114, 114, 59, 59, 108, 108, 111, 111, + 111, 111, 114, 114, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 65, 86, + 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 101, 101, 99, 99, 116, 116, + 111, 111, 114, 114, 59, 59, 101, 114, 101, 101, 59, 86, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 59, 101, 101, 99, 99, 116, 116, 111, 111, 114, 114, + 59, 59, 105, 105, 97, 97, 110, 110, 103, 103, 108, 108, 101, 101, 59, 69, + 97, 97, 114, 114, 59, 59, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, + 112, 112, 68, 86, 111, 111, 119, 119, 110, 110, 86, 86, 101, 101, 99, 99, + 116, 116, 111, 111, 114, 114, 59, 59, 101, 101, 101, 101, 86, 86, 101, 101, + 99, 99, 116, 116, 111, 111, 114, 114, 59, 59, 101, 101, 99, 99, 116, 116, + 111, 111, 114, 114, 59, 66, 97, 97, 114, 114, 59, 59, 101, 101, 99, 99, + 116, 116, 111, 111, 114, 114, 59, 66, 97, 97, 114, 114, 59, 59, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, + 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 115, 115, 69, 84, + 113, 113, 117, 117, 97, 97, 108, 108, 71, 71, 114, 114, 101, 101, 97, 97, + 116, 116, 101, 101, 114, 114, 59, 59, 117, 117, 108, 108, 108, 108, 69, 69, + 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 114, 114, 101, 101, 97, 97, + 116, 116, 101, 101, 114, 114, 59, 59, 101, 101, 115, 115, 115, 115, 59, 59, + 108, 108, 97, 97, 110, 110, 116, 116, 69, 69, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, 114, 114, + 59, 59, 59, 101, 102, 102, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 105, 105, 100, 100, 111, 111, 116, 116, 59, 59, 110, 119, + 103, 103, 76, 114, 101, 101, 102, 102, 116, 116, 65, 82, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 65, 65, + 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, + 116, 116, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 101, 101, + 102, 102, 116, 116, 97, 114, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 105, 105, 103, 103, 104, 104, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 97, 97, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 102, 102, 59, 59, 101, 101, 114, 114, + 76, 82, 101, 101, 102, 102, 116, 116, 65, 65, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 65, 65, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 99, 116, 114, 114, 59, 59, 59, 59, + 114, 114, 111, 111, 107, 107, 59, 59, 59, 59, 97, 117, 112, 112, 59, 59, + 121, 121, 59, 59, 100, 108, 105, 105, 117, 117, 109, 109, 83, 83, 112, 112, + 97, 97, 99, 99, 101, 101, 59, 59, 108, 108, 105, 105, 110, 110, 116, 116, + 114, 114, 102, 102, 59, 59, 114, 114, 59, 59, 110, 110, 117, 117, 115, 115, + 80, 80, 108, 108, 117, 117, 115, 115, 59, 59, 112, 112, 102, 102, 59, 59, + 99, 99, 114, 114, 59, 59, 59, 59, 74, 117, 99, 99, 121, 121, 59, 59, + 99, 99, 117, 117, 116, 116, 101, 101, 59, 59, 97, 121, 114, 114, 111, 111, + 110, 110, 59, 59, 100, 100, 105, 105, 108, 108, 59, 59, 59, 59, 103, 119, + 97, 97, 116, 116, 105, 105, 118, 118, 101, 101, 77, 86, 101, 101, 100, 100, + 105, 105, 117, 117, 109, 109, 83, 83, 112, 112, 97, 97, 99, 99, 101, 101, + 59, 59, 104, 104, 105, 105, 99, 110, 107, 107, 83, 83, 112, 112, 97, 97, + 99, 99, 101, 101, 59, 59, 83, 83, 112, 112, 97, 97, 99, 99, 101, 101, + 59, 59, 101, 101, 114, 114, 121, 121, 84, 84, 104, 104, 105, 105, 110, 110, + 83, 83, 112, 112, 97, 97, 99, 99, 101, 101, 59, 59, 116, 116, 101, 101, + 100, 100, 71, 76, 114, 114, 101, 101, 97, 97, 116, 116, 101, 101, 114, 114, + 71, 71, 114, 114, 101, 101, 97, 97, 116, 116, 101, 101, 114, 114, 59, 59, + 101, 101, 115, 115, 115, 115, 76, 76, 101, 101, 115, 115, 115, 115, 59, 59, + 76, 76, 105, 105, 110, 110, 101, 101, 59, 59, 114, 114, 59, 59, 66, 116, + 114, 114, 101, 101, 97, 97, 107, 107, 59, 59, 66, 66, 114, 114, 101, 101, + 97, 97, 107, 107, 105, 105, 110, 110, 103, 103, 83, 83, 112, 112, 97, 97, + 99, 99, 101, 101, 59, 59, 102, 102, 59, 59, 59, 86, 111, 117, 110, 110, + 103, 103, 114, 114, 117, 117, 101, 101, 110, 110, 116, 116, 59, 59, 112, 112, + 67, 67, 97, 97, 112, 112, 59, 59, 111, 111, 117, 117, 98, 98, 108, 108, + 101, 101, 86, 86, 101, 101, 114, 114, 116, 116, 105, 105, 99, 99, 97, 97, + 108, 108, 66, 66, 97, 97, 114, 114, 59, 59, 108, 120, 101, 101, 109, 109, + 101, 101, 110, 110, 116, 116, 59, 59, 117, 117, 97, 97, 108, 108, 59, 84, + 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, 105, 105, 115, 115, 116, 116, + 115, 115, 59, 59, 114, 114, 101, 101, 97, 97, 116, 116, 101, 101, 114, 114, + 59, 84, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 117, 117, 108, 108, + 108, 108, 69, 69, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 114, 114, + 101, 101, 97, 97, 116, 116, 101, 101, 114, 114, 59, 59, 101, 101, 115, 115, + 115, 115, 59, 59, 108, 108, 97, 97, 110, 110, 116, 116, 69, 69, 113, 113, + 117, 117, 97, 97, 108, 108, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, + 59, 59, 117, 117, 109, 109, 112, 112, 68, 69, 111, 111, 119, 119, 110, 110, + 72, 72, 117, 117, 109, 109, 112, 112, 59, 59, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 101, 101, 102, 115, 116, 116, 84, 84, 114, 114, 105, 105, + 97, 97, 110, 110, 103, 103, 108, 108, 101, 101, 59, 69, 97, 97, 114, 114, + 59, 59, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 115, 115, 59, 84, + 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 114, 114, 101, 101, 97, 97, + 116, 116, 101, 101, 114, 114, 59, 59, 101, 101, 115, 115, 115, 115, 59, 59, + 108, 108, 97, 97, 110, 110, 116, 116, 69, 69, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, 101, 101, + 115, 115, 116, 116, 101, 101, 100, 100, 71, 76, 114, 114, 101, 101, 97, 97, + 116, 116, 101, 101, 114, 114, 71, 71, 114, 114, 101, 101, 97, 97, 116, 116, + 101, 101, 114, 114, 59, 59, 101, 101, 115, 115, 115, 115, 76, 76, 101, 101, + 115, 115, 115, 115, 59, 59, 114, 114, 101, 101, 99, 99, 101, 101, 100, 100, + 101, 101, 115, 115, 59, 83, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, + 108, 108, 97, 97, 110, 110, 116, 116, 69, 69, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 101, 105, 118, 118, 101, 101, 114, 114, 115, 115, 101, 101, + 69, 69, 108, 108, 101, 101, 109, 109, 101, 101, 110, 110, 116, 116, 59, 59, + 103, 103, 104, 104, 116, 116, 84, 84, 114, 114, 105, 105, 97, 97, 110, 110, + 103, 103, 108, 108, 101, 101, 59, 69, 97, 97, 114, 114, 59, 59, 113, 113, + 117, 117, 97, 97, 108, 108, 59, 59, 113, 117, 117, 117, 97, 97, 114, 114, + 101, 101, 83, 83, 117, 117, 98, 112, 115, 115, 101, 101, 116, 116, 59, 69, + 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 101, 101, 114, 114, 115, 115, + 101, 101, 116, 116, 59, 69, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, + 98, 112, 115, 115, 101, 101, 116, 116, 59, 69, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 99, 99, 101, 101, 101, 101, 100, 100, 115, 115, 59, 84, + 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 108, 108, 97, 97, 110, 110, + 116, 116, 69, 69, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 105, 105, + 108, 108, 100, 100, 101, 101, 59, 59, 101, 101, 114, 114, 115, 115, 101, 101, + 116, 116, 59, 69, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 105, 105, + 108, 108, 100, 100, 101, 101, 59, 84, 113, 113, 117, 117, 97, 97, 108, 108, + 59, 59, 117, 117, 108, 108, 108, 108, 69, 69, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, 101, 101, + 114, 114, 116, 116, 105, 105, 99, 99, 97, 97, 108, 108, 66, 66, 97, 97, + 114, 114, 59, 59, 99, 99, 114, 114, 59, 59, 105, 105, 108, 108, 100, 100, + 101, 101, 59, 59, 69, 118, 108, 108, 105, 105, 103, 103, 59, 59, 99, 99, + 117, 117, 116, 116, 101, 101, 105, 121, 114, 114, 99, 99, 59, 59, 98, 98, + 108, 108, 97, 97, 99, 99, 59, 59, 114, 114, 59, 59, 114, 114, 97, 97, + 118, 118, 101, 101, 97, 105, 99, 99, 114, 114, 59, 59, 103, 103, 97, 97, + 59, 59, 99, 99, 114, 114, 111, 111, 110, 110, 59, 59, 112, 112, 102, 102, + 59, 59, 101, 101, 110, 110, 67, 67, 117, 117, 114, 114, 108, 108, 121, 121, + 68, 81, 111, 111, 117, 117, 98, 98, 108, 108, 101, 101, 81, 81, 117, 117, + 111, 111, 116, 116, 101, 101, 59, 59, 117, 117, 111, 111, 116, 116, 101, 101, + 59, 59, 59, 59, 99, 108, 114, 114, 59, 59, 97, 97, 115, 115, 104, 104, + 105, 105, 108, 109, 100, 100, 101, 101, 101, 101, 115, 115, 59, 59, 109, 109, + 108, 108, 101, 101, 114, 114, 66, 80, 97, 114, 114, 114, 59, 59, 97, 97, + 99, 99, 101, 107, 59, 59, 101, 101, 116, 116, 59, 59, 97, 97, 114, 114, + 101, 101, 110, 110, 116, 116, 104, 104, 101, 101, 115, 115, 105, 105, 115, 115, + 59, 59, 97, 115, 114, 114, 116, 116, 105, 105, 97, 97, 108, 108, 68, 68, + 59, 59, 121, 121, 59, 59, 114, 114, 59, 59, 105, 105, 59, 59, 59, 59, + 117, 117, 115, 115, 77, 77, 105, 105, 110, 110, 117, 117, 115, 115, 59, 59, + 105, 112, 110, 110, 99, 99, 97, 97, 114, 114, 101, 101, 112, 112, 108, 108, + 97, 97, 110, 110, 101, 101, 59, 59, 102, 102, 59, 59, 59, 111, 99, 99, + 101, 101, 100, 100, 101, 101, 115, 115, 59, 84, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 108, 108, 97, 97, 110, 110, 116, 116, 69, 69, 113, 113, + 117, 117, 97, 97, 108, 108, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, + 59, 59, 109, 109, 101, 101, 59, 59, 100, 112, 117, 117, 99, 99, 116, 116, + 59, 59, 111, 111, 114, 114, 116, 116, 105, 105, 111, 111, 110, 110, 59, 97, + 108, 108, 59, 59, 99, 105, 114, 114, 59, 59, 59, 59, 85, 115, 79, 79, + 84, 84, 114, 114, 59, 59, 112, 112, 102, 102, 59, 59, 99, 99, 114, 114, + 59, 59, 66, 117, 97, 97, 114, 114, 114, 114, 59, 59, 71, 71, 99, 114, + 117, 117, 116, 116, 101, 101, 59, 59, 103, 103, 59, 59, 114, 114, 59, 116, + 108, 108, 59, 59, 97, 121, 114, 114, 111, 111, 110, 110, 59, 59, 100, 100, + 105, 105, 108, 108, 59, 59, 59, 59, 59, 118, 101, 101, 114, 114, 115, 115, + 101, 101, 69, 85, 108, 113, 101, 101, 109, 109, 101, 101, 110, 110, 116, 116, + 59, 59, 117, 117, 105, 105, 108, 108, 105, 105, 98, 98, 114, 114, 105, 105, + 117, 117, 109, 109, 59, 59, 112, 112, 69, 69, 113, 113, 117, 117, 105, 105, + 108, 108, 105, 105, 98, 98, 114, 114, 105, 105, 117, 117, 109, 109, 59, 59, + 114, 114, 59, 59, 111, 111, 59, 59, 103, 103, 104, 104, 116, 116, 65, 97, + 110, 114, 103, 103, 108, 108, 101, 101, 66, 66, 114, 114, 97, 97, 99, 99, + 107, 107, 101, 101, 116, 116, 59, 59, 114, 114, 111, 111, 119, 119, 59, 76, + 97, 97, 114, 114, 59, 59, 101, 101, 102, 102, 116, 116, 65, 65, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 101, 101, 105, 105, 108, 108, 105, 105, + 110, 110, 103, 103, 59, 59, 111, 111, 117, 119, 98, 98, 108, 108, 101, 101, + 66, 66, 114, 114, 97, 97, 99, 99, 107, 107, 101, 101, 116, 116, 59, 59, + 110, 110, 84, 86, 101, 101, 101, 101, 86, 86, 101, 101, 99, 99, 116, 116, + 111, 111, 114, 114, 59, 59, 101, 101, 99, 99, 116, 116, 111, 111, 114, 114, + 59, 66, 97, 97, 114, 114, 59, 59, 108, 108, 111, 111, 111, 111, 114, 114, + 59, 59, 101, 114, 101, 101, 59, 86, 114, 114, 114, 114, 111, 111, 119, 119, + 59, 59, 101, 101, 99, 99, 116, 116, 111, 111, 114, 114, 59, 59, 105, 105, + 97, 97, 110, 110, 103, 103, 108, 108, 101, 101, 59, 69, 97, 97, 114, 114, + 59, 59, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 112, 112, 68, 86, + 111, 111, 119, 119, 110, 110, 86, 86, 101, 101, 99, 99, 116, 116, 111, 111, + 114, 114, 59, 59, 101, 101, 101, 101, 86, 86, 101, 101, 99, 99, 116, 116, + 111, 111, 114, 114, 59, 59, 101, 101, 99, 99, 116, 116, 111, 111, 114, 114, + 59, 66, 97, 97, 114, 114, 59, 59, 101, 101, 99, 99, 116, 116, 111, 111, + 114, 114, 59, 66, 97, 97, 114, 114, 59, 59, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 112, 117, 102, 102, 59, 59, 110, 110, 100, 100, 73, 73, + 109, 109, 112, 112, 108, 108, 105, 105, 101, 101, 115, 115, 59, 59, 105, 105, + 103, 103, 104, 104, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, + 59, 59, 99, 104, 114, 114, 59, 59, 59, 59, 108, 108, 101, 101, 68, 68, + 101, 101, 108, 108, 97, 97, 121, 121, 101, 101, 100, 100, 59, 59, 72, 117, + 67, 99, 72, 72, 99, 99, 121, 121, 59, 59, 121, 121, 59, 59, 70, 70, + 84, 84, 99, 99, 121, 121, 59, 59, 99, 99, 117, 117, 116, 116, 101, 101, + 59, 59, 59, 121, 114, 114, 111, 111, 110, 110, 59, 59, 100, 100, 105, 105, + 108, 108, 59, 59, 114, 114, 99, 99, 59, 59, 59, 59, 114, 114, 59, 59, + 111, 111, 114, 114, 116, 116, 68, 85, 111, 111, 119, 119, 110, 110, 65, 65, + 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 101, 101, 102, 102, 116, 116, + 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, + 104, 104, 116, 116, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 112, 112, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 103, 103, + 109, 109, 97, 97, 59, 59, 97, 97, 108, 108, 108, 108, 67, 67, 105, 105, + 114, 114, 99, 99, 108, 108, 101, 101, 59, 59, 112, 112, 102, 102, 59, 59, + 114, 117, 116, 116, 59, 59, 97, 97, 114, 114, 101, 101, 59, 85, 110, 110, + 116, 116, 101, 101, 114, 114, 115, 115, 101, 101, 99, 99, 116, 116, 105, 105, + 111, 111, 110, 110, 59, 59, 117, 117, 98, 112, 115, 115, 101, 101, 116, 116, + 59, 69, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 101, 101, 114, 114, + 115, 115, 101, 101, 116, 116, 59, 69, 113, 113, 117, 117, 97, 97, 108, 108, + 59, 59, 110, 110, 105, 105, 111, 111, 110, 110, 59, 59, 99, 99, 114, 114, + 59, 59, 97, 97, 114, 114, 59, 59, 98, 112, 59, 115, 101, 101, 116, 116, + 59, 69, 113, 113, 117, 117, 97, 97, 108, 108, 59, 59, 99, 104, 101, 101, + 101, 101, 100, 100, 115, 115, 59, 84, 113, 113, 117, 117, 97, 97, 108, 108, + 59, 59, 108, 108, 97, 97, 110, 110, 116, 116, 69, 69, 113, 113, 117, 117, + 97, 97, 108, 108, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, + 84, 84, 104, 104, 97, 97, 116, 116, 59, 59, 59, 59, 59, 115, 114, 114, + 115, 115, 101, 101, 116, 116, 59, 69, 113, 113, 117, 117, 97, 97, 108, 108, + 59, 59, 101, 101, 116, 116, 59, 59, 72, 115, 79, 79, 82, 82, 78, 78, + 65, 65, 68, 68, 69, 69, 59, 59, 72, 99, 99, 99, 121, 121, 59, 59, + 121, 121, 59, 59, 98, 117, 59, 59, 59, 59, 97, 121, 114, 114, 111, 111, + 110, 110, 59, 59, 100, 100, 105, 105, 108, 108, 59, 59, 59, 59, 114, 114, + 59, 59, 101, 105, 114, 116, 101, 101, 102, 102, 111, 111, 114, 114, 101, 101, + 59, 59, 97, 97, 59, 59, 99, 110, 107, 107, 83, 83, 112, 112, 97, 97, + 99, 99, 101, 101, 59, 59, 83, 83, 112, 112, 97, 97, 99, 99, 101, 101, + 59, 59, 108, 108, 100, 100, 101, 101, 59, 84, 113, 113, 117, 117, 97, 97, + 108, 108, 59, 59, 117, 117, 108, 108, 108, 108, 69, 69, 113, 113, 117, 117, + 97, 97, 108, 108, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, + 112, 112, 102, 102, 59, 59, 105, 105, 112, 112, 108, 108, 101, 101, 68, 68, + 111, 111, 116, 116, 59, 59, 99, 116, 114, 114, 59, 59, 114, 114, 111, 111, + 107, 107, 59, 59, 97, 117, 99, 114, 117, 117, 116, 116, 101, 101, 114, 114, + 59, 111, 99, 99, 105, 105, 114, 114, 59, 59, 114, 114, 99, 101, 121, 121, + 59, 59, 118, 118, 101, 101, 59, 59, 105, 121, 114, 114, 99, 99, 59, 59, + 98, 98, 108, 108, 97, 97, 99, 99, 59, 59, 114, 114, 59, 59, 114, 114, + 97, 97, 118, 118, 101, 101, 97, 97, 99, 99, 114, 114, 59, 59, 100, 105, + 101, 101, 114, 114, 66, 80, 97, 114, 114, 114, 59, 59, 97, 97, 99, 99, + 101, 107, 59, 59, 101, 101, 116, 116, 59, 59, 97, 97, 114, 114, 101, 101, + 110, 110, 116, 116, 104, 104, 101, 101, 115, 115, 105, 105, 115, 115, 59, 59, + 111, 111, 110, 110, 59, 80, 108, 108, 117, 117, 115, 115, 59, 59, 103, 112, + 111, 111, 110, 110, 59, 59, 102, 102, 59, 59, 65, 115, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 68, 97, 97, 114, 114, 59, 59, 111, 111, 119, 119, + 110, 110, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 111, 111, + 119, 119, 110, 110, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 113, 113, 117, 117, 105, 105, 108, 108, 105, 105, 98, 98, 114, 114, 105, 105, + 117, 117, 109, 109, 59, 59, 101, 101, 101, 101, 59, 65, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 59, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 111, 111, 119, 119, 110, 110, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, + 59, 59, 101, 101, 114, 114, 76, 82, 101, 101, 102, 102, 116, 116, 65, 65, + 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, + 116, 116, 65, 65, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, + 59, 108, 111, 111, 110, 110, 59, 59, 105, 105, 110, 110, 103, 103, 59, 59, + 99, 99, 114, 114, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 59, 59, + 109, 109, 108, 108, 68, 118, 97, 97, 115, 115, 104, 104, 59, 59, 97, 97, + 114, 114, 59, 59, 121, 121, 59, 59, 97, 97, 115, 115, 104, 104, 59, 108, + 59, 59, 101, 114, 59, 59, 98, 121, 97, 97, 114, 114, 59, 59, 59, 105, + 99, 99, 97, 97, 108, 108, 66, 84, 97, 97, 114, 114, 59, 59, 105, 105, + 110, 110, 101, 101, 59, 59, 101, 101, 112, 112, 97, 97, 114, 114, 97, 97, + 116, 116, 111, 111, 114, 114, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, + 59, 59, 84, 84, 104, 104, 105, 105, 110, 110, 83, 83, 112, 112, 97, 97, + 99, 99, 101, 101, 59, 59, 114, 114, 59, 59, 112, 112, 102, 102, 59, 59, + 99, 99, 114, 114, 59, 59, 100, 100, 97, 97, 115, 115, 104, 104, 59, 59, + 99, 115, 105, 105, 114, 114, 99, 99, 59, 59, 100, 100, 103, 103, 101, 101, + 59, 59, 114, 114, 59, 59, 112, 112, 102, 102, 59, 59, 99, 99, 114, 114, + 59, 59, 102, 115, 114, 114, 59, 59, 59, 59, 112, 112, 102, 102, 59, 59, + 99, 99, 114, 114, 59, 59, 65, 117, 99, 99, 121, 121, 59, 59, 99, 99, + 121, 121, 59, 59, 99, 99, 121, 121, 59, 59, 99, 99, 117, 117, 116, 116, + 101, 101, 105, 121, 114, 114, 99, 99, 59, 59, 59, 59, 114, 114, 59, 59, + 112, 112, 102, 102, 59, 59, 99, 99, 114, 114, 59, 59, 109, 109, 108, 108, + 59, 59, 72, 115, 99, 99, 121, 121, 59, 59, 99, 99, 117, 117, 116, 116, + 101, 101, 59, 59, 97, 121, 114, 114, 111, 111, 110, 110, 59, 59, 59, 59, + 111, 111, 116, 116, 59, 59, 114, 116, 111, 111, 87, 87, 105, 105, 100, 100, + 116, 116, 104, 104, 83, 83, 112, 112, 97, 97, 99, 99, 101, 101, 59, 59, + 97, 97, 59, 59, 114, 114, 59, 59, 112, 112, 102, 102, 59, 59, 99, 99, + 114, 114, 59, 59, 97, 119, 99, 99, 117, 117, 116, 116, 101, 101, 114, 114, + 101, 101, 118, 118, 101, 101, 59, 59, 59, 121, 59, 59, 59, 59, 114, 114, + 99, 99, 116, 116, 101, 101, 59, 59, 108, 108, 105, 105, 103, 103, 59, 114, + 59, 59, 114, 114, 97, 97, 118, 118, 101, 101, 101, 112, 102, 112, 115, 115, + 121, 121, 109, 109, 59, 59, 104, 104, 59, 59, 104, 104, 97, 97, 59, 59, + 97, 112, 99, 108, 114, 114, 59, 59, 103, 103, 59, 59, 100, 103, 59, 118, + 110, 110, 100, 100, 59, 59, 59, 59, 108, 108, 111, 111, 112, 112, 101, 101, + 59, 59, 59, 59, 59, 122, 59, 59, 101, 101, 59, 59, 115, 115, 100, 100, + 59, 97, 97, 104, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 116, 116, 59, 118, 98, 98, 59, 100, 59, 59, 112, 116, + 104, 104, 59, 59, 59, 59, 97, 97, 114, 114, 114, 114, 59, 59, 103, 112, + 111, 111, 110, 110, 59, 59, 102, 102, 59, 59, 59, 112, 59, 59, 99, 99, + 105, 105, 114, 114, 59, 59, 59, 59, 100, 100, 59, 59, 115, 115, 59, 59, + 114, 114, 111, 111, 120, 120, 59, 101, 113, 113, 59, 59, 105, 105, 110, 110, + 103, 103, 99, 121, 114, 114, 59, 59, 59, 59, 109, 109, 112, 112, 59, 101, + 113, 113, 59, 59, 105, 105, 108, 108, 100, 100, 101, 101, 109, 109, 108, 108, + 99, 105, 111, 111, 110, 110, 105, 105, 110, 110, 116, 116, 59, 59, 110, 110, + 116, 116, 59, 59, 78, 117, 111, 111, 116, 116, 59, 59, 99, 114, 107, 107, + 99, 115, 111, 111, 110, 110, 103, 103, 59, 59, 112, 112, 115, 115, 105, 105, + 108, 108, 111, 111, 110, 110, 59, 59, 114, 114, 105, 105, 109, 109, 101, 101, + 59, 59, 105, 105, 109, 109, 59, 101, 113, 113, 59, 59, 118, 119, 101, 101, + 101, 101, 59, 59, 101, 101, 100, 100, 59, 103, 101, 101, 59, 59, 114, 114, + 107, 107, 59, 116, 98, 98, 114, 114, 107, 107, 59, 59, 111, 121, 110, 110, + 103, 103, 59, 59, 59, 59, 113, 113, 117, 117, 111, 111, 59, 59, 99, 116, + 97, 97, 117, 117, 115, 115, 59, 101, 59, 59, 112, 112, 116, 116, 121, 121, + 118, 118, 59, 59, 115, 115, 105, 105, 59, 59, 110, 110, 111, 111, 117, 117, + 59, 59, 97, 119, 59, 59, 59, 59, 101, 101, 101, 101, 110, 110, 59, 59, + 114, 114, 59, 59, 103, 103, 99, 119, 97, 117, 112, 112, 59, 59, 114, 114, + 99, 99, 59, 59, 112, 112, 59, 59, 100, 116, 111, 111, 116, 116, 59, 59, + 108, 108, 117, 117, 115, 115, 59, 59, 105, 105, 109, 109, 101, 101, 115, 115, + 59, 59, 113, 116, 99, 99, 117, 117, 112, 112, 59, 59, 97, 97, 114, 114, + 59, 59, 114, 114, 105, 105, 97, 97, 110, 110, 103, 103, 108, 108, 101, 101, + 100, 117, 111, 111, 119, 119, 110, 110, 59, 59, 112, 112, 59, 59, 112, 112, + 108, 108, 117, 117, 115, 115, 59, 59, 101, 101, 101, 101, 59, 59, 101, 101, + 100, 100, 103, 103, 101, 101, 59, 59, 97, 97, 114, 114, 111, 111, 119, 119, + 59, 59, 97, 111, 99, 110, 107, 107, 108, 116, 111, 111, 122, 122, 101, 101, + 110, 110, 103, 103, 101, 101, 59, 59, 113, 113, 117, 117, 97, 97, 114, 114, + 101, 101, 59, 59, 114, 114, 105, 105, 97, 97, 110, 110, 103, 103, 108, 108, + 101, 101, 59, 114, 111, 111, 119, 119, 110, 110, 59, 59, 101, 101, 102, 102, + 116, 116, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 59, 59, 107, 107, + 59, 59, 49, 51, 50, 52, 59, 59, 59, 59, 52, 52, 59, 59, 99, 99, + 107, 107, 59, 59, 101, 111, 59, 113, 117, 117, 105, 105, 118, 118, 59, 59, + 116, 116, 59, 59, 112, 120, 102, 102, 59, 59, 59, 116, 111, 111, 109, 109, + 59, 59, 116, 116, 105, 105, 101, 101, 59, 59, 68, 118, 76, 114, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 117, 59, 59, 59, 59, 59, 59, 59, 59, + 76, 114, 59, 59, 59, 59, 59, 59, 59, 59, 59, 114, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 111, 111, 120, 120, 59, 59, 76, 114, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 117, 59, 59, 59, 59, 59, 59, + 59, 59, 105, 105, 110, 110, 117, 117, 115, 115, 59, 59, 108, 108, 117, 117, + 115, 115, 59, 59, 105, 105, 109, 109, 101, 101, 115, 115, 59, 59, 76, 114, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 114, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 114, 114, 105, 105, 109, 109, 101, 101, 59, 59, + 101, 118, 118, 118, 101, 101, 59, 59, 98, 98, 97, 97, 114, 114, 99, 111, + 114, 114, 59, 59, 109, 109, 105, 105, 59, 59, 109, 109, 59, 101, 59, 59, + 108, 108, 59, 104, 59, 59, 115, 115, 117, 117, 98, 98, 59, 59, 108, 109, + 108, 108, 59, 101, 116, 116, 59, 59, 112, 112, 59, 101, 59, 59, 59, 113, + 59, 59, 97, 121, 99, 114, 117, 117, 116, 116, 101, 101, 59, 59, 59, 115, + 110, 110, 100, 100, 59, 59, 114, 114, 99, 99, 117, 117, 112, 112, 59, 59, + 97, 117, 112, 112, 59, 59, 112, 112, 59, 59, 111, 111, 116, 116, 59, 59, + 59, 59, 101, 111, 116, 116, 59, 59, 110, 110, 59, 59, 97, 117, 112, 114, + 115, 115, 59, 59, 111, 111, 110, 110, 59, 59, 100, 100, 105, 105, 108, 108, + 114, 114, 99, 99, 59, 59, 112, 112, 115, 115, 59, 115, 109, 109, 59, 59, + 111, 111, 116, 116, 59, 59, 100, 110, 105, 105, 108, 108, 112, 112, 116, 116, + 121, 121, 118, 118, 59, 59, 116, 116, 114, 114, 100, 100, 111, 111, 116, 116, + 59, 59, 114, 114, 59, 59, 99, 105, 121, 121, 59, 59, 99, 99, 107, 107, + 59, 109, 97, 97, 114, 114, 107, 107, 59, 59, 59, 59, 114, 114, 59, 115, + 59, 59, 59, 108, 113, 113, 59, 59, 101, 101, 97, 100, 114, 114, 114, 114, + 111, 111, 119, 119, 108, 114, 101, 101, 102, 102, 116, 116, 59, 59, 105, 105, + 103, 103, 104, 104, 116, 116, 59, 59, 82, 100, 59, 59, 59, 59, 115, 115, + 116, 116, 59, 59, 105, 105, 114, 114, 99, 99, 59, 59, 97, 97, 115, 115, + 104, 104, 59, 59, 59, 59, 110, 110, 105, 105, 110, 110, 116, 116, 59, 59, + 105, 105, 100, 100, 59, 59, 99, 99, 105, 105, 114, 114, 59, 59, 117, 117, + 98, 98, 115, 115, 59, 117, 105, 105, 116, 116, 59, 59, 108, 112, 111, 111, + 110, 110, 59, 101, 59, 113, 59, 59, 109, 112, 97, 97, 59, 116, 59, 59, + 59, 108, 110, 110, 59, 59, 101, 101, 109, 120, 101, 101, 110, 110, 116, 116, + 59, 59, 101, 101, 115, 115, 59, 59, 103, 105, 59, 100, 111, 111, 116, 116, + 59, 59, 110, 110, 116, 116, 59, 59, 102, 121, 59, 59, 111, 111, 100, 100, + 59, 59, 114, 114, 59, 59, 97, 111, 114, 114, 114, 114, 59, 59, 115, 115, + 115, 115, 59, 59, 99, 117, 114, 114, 59, 59, 98, 112, 59, 101, 59, 59, + 59, 101, 59, 59, 100, 100, 111, 111, 116, 116, 59, 59, 100, 119, 97, 97, + 114, 114, 114, 114, 108, 114, 59, 59, 59, 59, 112, 115, 114, 114, 59, 59, + 99, 99, 59, 59, 97, 97, 114, 114, 114, 114, 59, 112, 59, 59, 59, 115, + 114, 114, 99, 99, 97, 97, 112, 112, 59, 59, 97, 117, 112, 112, 59, 59, + 112, 112, 59, 59, 111, 111, 116, 116, 59, 59, 114, 114, 59, 59, 59, 59, + 97, 118, 114, 114, 114, 114, 59, 109, 59, 59, 121, 121, 101, 119, 113, 113, + 112, 115, 114, 114, 101, 101, 99, 99, 59, 59, 117, 117, 99, 99, 99, 99, + 59, 59, 101, 101, 101, 101, 59, 59, 101, 101, 100, 100, 103, 103, 101, 101, + 59, 59, 101, 101, 110, 110, 101, 101, 97, 97, 114, 114, 114, 114, 111, 111, + 119, 119, 108, 114, 101, 101, 102, 102, 116, 116, 59, 59, 105, 105, 103, 103, + 104, 104, 116, 116, 59, 59, 101, 101, 101, 101, 59, 59, 101, 101, 100, 100, + 59, 59, 99, 105, 111, 111, 110, 110, 105, 105, 110, 110, 116, 116, 59, 59, + 110, 110, 116, 116, 59, 59, 108, 108, 99, 99, 116, 116, 121, 121, 59, 59, + 65, 122, 114, 114, 114, 114, 59, 59, 97, 97, 114, 114, 59, 59, 103, 115, + 103, 103, 101, 101, 114, 114, 59, 59, 101, 101, 116, 116, 104, 104, 59, 59, + 114, 114, 59, 59, 104, 104, 59, 118, 59, 59, 107, 108, 97, 97, 114, 114, + 111, 111, 119, 119, 59, 59, 97, 97, 99, 99, 59, 59, 97, 121, 114, 114, + 111, 111, 110, 110, 59, 59, 59, 59, 59, 111, 103, 114, 103, 103, 101, 101, + 114, 114, 59, 59, 114, 114, 59, 59, 116, 116, 115, 115, 101, 101, 113, 113, + 59, 59, 103, 109, 116, 116, 97, 97, 59, 59, 112, 112, 116, 116, 121, 121, + 118, 118, 59, 59, 105, 114, 115, 115, 104, 104, 116, 116, 59, 59, 59, 59, + 97, 97, 114, 114, 108, 114, 59, 59, 59, 59, 97, 118, 109, 109, 59, 115, + 110, 110, 100, 100, 59, 115, 117, 117, 105, 105, 116, 116, 59, 59, 59, 59, + 59, 59, 97, 97, 109, 109, 109, 109, 97, 97, 59, 59, 105, 105, 110, 110, + 59, 59, 59, 111, 100, 100, 101, 101, 110, 110, 116, 116, 105, 105, 109, 109, + 101, 101, 115, 115, 59, 59, 110, 110, 120, 120, 59, 59, 99, 99, 121, 121, + 59, 59, 99, 99, 111, 114, 114, 114, 110, 110, 59, 59, 111, 111, 112, 112, + 59, 59, 108, 119, 108, 108, 97, 97, 114, 114, 59, 59, 102, 102, 59, 59, + 59, 115, 113, 113, 59, 100, 111, 111, 116, 116, 59, 59, 105, 105, 110, 110, + 117, 117, 115, 115, 59, 59, 108, 108, 117, 117, 115, 115, 59, 59, 113, 113, + 117, 117, 97, 97, 114, 114, 101, 101, 59, 59, 98, 98, 108, 108, 101, 101, + 98, 98, 97, 97, 114, 114, 119, 119, 101, 101, 100, 100, 103, 103, 101, 101, + 59, 59, 110, 110, 97, 104, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 111, 111, 119, 119, 110, 110, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, + 115, 115, 59, 59, 97, 97, 114, 114, 112, 112, 111, 111, 111, 111, 110, 110, + 108, 114, 101, 101, 102, 102, 116, 116, 59, 59, 105, 105, 103, 103, 104, 104, + 116, 116, 59, 59, 98, 99, 107, 107, 97, 97, 114, 114, 111, 111, 119, 119, + 59, 59, 111, 114, 114, 114, 110, 110, 59, 59, 111, 111, 112, 112, 59, 59, + 99, 116, 114, 121, 59, 59, 59, 59, 108, 108, 59, 59, 114, 114, 111, 111, + 107, 107, 59, 59, 100, 114, 111, 111, 116, 116, 59, 59, 105, 105, 59, 102, + 59, 59, 97, 104, 114, 114, 114, 114, 59, 59, 97, 97, 114, 114, 59, 59, + 97, 97, 110, 110, 103, 103, 108, 108, 101, 101, 59, 59, 99, 105, 121, 121, + 59, 59, 103, 103, 114, 114, 97, 97, 114, 114, 114, 114, 59, 59, 68, 120, + 68, 111, 111, 111, 116, 116, 59, 59, 116, 116, 59, 59, 99, 115, 117, 117, + 116, 116, 101, 101, 116, 116, 101, 101, 114, 114, 59, 59, 97, 121, 114, 114, + 111, 111, 110, 110, 59, 59, 114, 114, 59, 99, 108, 108, 111, 111, 110, 110, + 59, 59, 59, 59, 111, 111, 116, 116, 59, 59, 59, 59, 68, 114, 111, 111, + 116, 116, 59, 59, 59, 59, 59, 115, 97, 97, 118, 118, 101, 101, 59, 100, + 111, 111, 116, 116, 59, 59, 59, 115, 110, 110, 116, 116, 101, 101, 114, 114, + 115, 115, 59, 59, 59, 59, 59, 100, 111, 111, 116, 116, 59, 59, 97, 115, + 99, 99, 114, 114, 59, 59, 116, 116, 121, 121, 59, 118, 101, 101, 116, 116, + 59, 59, 59, 59, 112, 112, 49, 59, 51, 52, 59, 59, 59, 59, 103, 115, + 59, 59, 112, 112, 59, 59, 103, 112, 111, 111, 110, 110, 59, 59, 102, 102, + 59, 59, 97, 115, 114, 114, 59, 115, 108, 108, 59, 59, 117, 117, 115, 115, + 59, 59, 105, 105, 59, 118, 111, 111, 110, 110, 59, 59, 59, 59, 99, 118, + 105, 111, 114, 114, 99, 99, 59, 59, 108, 108, 111, 111, 110, 110, 59, 59, + 105, 108, 109, 109, 59, 59, 97, 97, 110, 110, 116, 116, 103, 108, 116, 116, + 114, 114, 59, 59, 101, 101, 115, 115, 115, 115, 59, 59, 97, 105, 108, 108, + 115, 115, 59, 59, 115, 115, 116, 116, 59, 59, 118, 118, 59, 68, 68, 68, + 59, 59, 112, 112, 97, 97, 114, 114, 115, 115, 108, 108, 59, 59, 68, 97, + 111, 111, 116, 116, 59, 59, 114, 114, 114, 114, 59, 59, 99, 105, 114, 114, + 59, 59, 111, 111, 116, 116, 59, 59, 109, 109, 59, 59, 97, 104, 59, 59, + 109, 114, 108, 108, 111, 111, 59, 59, 99, 112, 108, 108, 59, 59, 115, 115, + 116, 116, 59, 59, 101, 111, 99, 99, 116, 116, 97, 97, 116, 116, 105, 105, + 111, 111, 110, 110, 59, 59, 110, 110, 101, 101, 110, 110, 116, 116, 105, 105, + 97, 97, 108, 108, 101, 101, 59, 59, 97, 115, 108, 108, 108, 108, 105, 105, + 110, 110, 103, 103, 100, 100, 111, 111, 116, 116, 115, 115, 101, 101, 113, 113, + 59, 59, 121, 121, 59, 59, 109, 109, 97, 97, 108, 108, 101, 101, 59, 59, + 105, 114, 108, 108, 105, 105, 103, 103, 59, 59, 105, 108, 103, 103, 59, 59, + 105, 105, 103, 103, 59, 59, 59, 59, 108, 108, 105, 105, 103, 103, 59, 59, + 108, 108, 105, 105, 103, 103, 59, 59, 97, 116, 116, 116, 59, 59, 105, 105, + 103, 103, 59, 59, 110, 110, 115, 115, 59, 59, 111, 111, 102, 102, 59, 59, + 112, 114, 102, 102, 59, 59, 97, 107, 108, 108, 108, 108, 59, 59, 59, 118, + 59, 59, 97, 97, 114, 114, 116, 116, 105, 105, 110, 110, 116, 116, 59, 59, + 97, 111, 99, 115, 49, 55, 50, 56, 59, 59, 59, 59, 59, 59, 59, 59, + 51, 53, 59, 59, 59, 59, 52, 56, 59, 59, 59, 59, 53, 53, 59, 59, + 54, 56, 59, 59, 59, 59, 56, 56, 59, 59, 108, 108, 59, 59, 119, 119, + 110, 110, 59, 59, 99, 99, 114, 114, 59, 59, 69, 118, 59, 108, 59, 59, + 99, 112, 117, 117, 116, 116, 101, 101, 59, 59, 109, 109, 97, 97, 59, 100, + 59, 59, 59, 59, 114, 114, 101, 101, 118, 118, 101, 101, 59, 59, 105, 121, + 114, 114, 99, 99, 59, 59, 59, 59, 111, 111, 116, 116, 59, 59, 59, 115, + 59, 59, 59, 115, 59, 59, 108, 108, 97, 97, 110, 110, 116, 116, 59, 59, + 59, 108, 99, 99, 59, 59, 111, 111, 116, 116, 59, 111, 59, 108, 59, 59, + 59, 101, 115, 115, 59, 59, 114, 114, 59, 59, 59, 103, 59, 59, 109, 109, + 101, 101, 108, 108, 59, 59, 99, 99, 121, 121, 59, 59, 59, 106, 59, 59, + 59, 59, 59, 59, 69, 115, 59, 59, 112, 112, 59, 112, 114, 114, 111, 111, + 120, 120, 59, 59, 59, 113, 59, 113, 59, 59, 105, 105, 109, 109, 59, 59, + 112, 112, 102, 102, 59, 59, 97, 97, 118, 118, 101, 101, 59, 59, 99, 105, + 114, 114, 59, 59, 109, 109, 59, 108, 59, 59, 59, 59, 99, 105, 59, 59, + 114, 114, 59, 59, 111, 111, 116, 116, 59, 59, 80, 80, 97, 97, 114, 114, + 59, 59, 117, 117, 101, 101, 115, 115, 116, 116, 59, 59, 97, 115, 112, 114, + 112, 112, 114, 114, 111, 111, 120, 120, 59, 59, 114, 114, 59, 59, 111, 111, + 116, 116, 59, 59, 113, 113, 108, 113, 101, 101, 115, 115, 115, 115, 59, 59, + 108, 108, 101, 101, 115, 115, 115, 115, 59, 59, 101, 101, 115, 115, 115, 115, + 59, 59, 105, 105, 109, 109, 59, 59, 101, 110, 114, 114, 116, 116, 110, 110, + 101, 101, 113, 113, 113, 113, 59, 59, 69, 69, 59, 59, 65, 121, 114, 114, + 114, 114, 59, 59, 105, 114, 114, 114, 115, 115, 112, 112, 59, 59, 102, 102, + 59, 59, 105, 105, 108, 108, 116, 116, 59, 59, 100, 114, 99, 99, 121, 121, + 59, 59, 59, 119, 105, 105, 114, 114, 59, 59, 59, 59, 97, 97, 114, 114, + 59, 59, 105, 105, 114, 114, 99, 99, 59, 59, 97, 114, 114, 114, 116, 116, + 115, 115, 59, 117, 105, 105, 116, 116, 59, 59, 108, 108, 105, 105, 112, 112, + 59, 59, 99, 99, 111, 111, 110, 110, 59, 59, 114, 114, 59, 59, 115, 115, + 101, 119, 97, 97, 114, 114, 111, 111, 119, 119, 59, 59, 97, 97, 114, 114, + 111, 111, 119, 119, 59, 59, 97, 114, 114, 114, 114, 114, 59, 59, 116, 116, + 104, 104, 116, 116, 59, 59, 107, 107, 108, 114, 101, 101, 102, 102, 116, 116, + 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, + 104, 104, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, + 102, 102, 59, 59, 98, 98, 97, 97, 114, 114, 59, 59, 99, 116, 114, 114, + 59, 59, 97, 97, 115, 115, 104, 104, 59, 59, 114, 114, 111, 111, 107, 107, + 59, 59, 98, 112, 117, 117, 108, 108, 108, 108, 59, 59, 104, 104, 101, 101, + 110, 110, 59, 59, 97, 117, 99, 99, 117, 117, 116, 116, 101, 101, 59, 121, + 114, 114, 99, 99, 59, 59, 99, 120, 121, 121, 59, 59, 99, 99, 108, 108, + 102, 114, 59, 59, 59, 59, 114, 114, 97, 97, 118, 118, 101, 101, 59, 111, + 105, 110, 110, 110, 116, 116, 59, 59, 116, 116, 59, 59, 102, 102, 105, 105, + 110, 110, 59, 59, 116, 116, 97, 97, 59, 59, 108, 108, 105, 105, 103, 103, + 59, 59, 97, 112, 99, 116, 114, 114, 59, 59, 101, 112, 59, 59, 105, 105, + 110, 110, 101, 101, 59, 59, 97, 97, 114, 114, 116, 116, 59, 59, 104, 104, + 59, 59, 102, 102, 59, 59, 101, 101, 100, 100, 59, 59, 59, 116, 97, 97, + 114, 114, 101, 101, 59, 59, 105, 105, 110, 110, 59, 116, 105, 105, 101, 101, + 59, 59, 100, 100, 111, 111, 116, 116, 59, 59, 59, 112, 97, 97, 108, 108, + 59, 59, 103, 114, 101, 101, 114, 114, 115, 115, 59, 59, 99, 99, 97, 97, + 108, 108, 59, 59, 97, 97, 114, 114, 104, 104, 107, 107, 59, 59, 114, 114, + 111, 111, 100, 100, 59, 59, 99, 116, 121, 121, 59, 59, 111, 111, 110, 110, + 59, 59, 102, 102, 59, 59, 97, 97, 59, 59, 114, 114, 111, 111, 100, 100, + 59, 59, 117, 117, 101, 101, 115, 115, 116, 116, 99, 105, 114, 114, 59, 59, + 110, 110, 59, 118, 59, 59, 111, 111, 116, 116, 59, 59, 59, 118, 59, 59, + 59, 59, 59, 105, 108, 108, 100, 100, 101, 101, 59, 59, 107, 109, 99, 99, + 121, 121, 59, 59, 108, 108, 99, 117, 105, 121, 114, 114, 99, 99, 59, 59, + 59, 59, 114, 114, 59, 59, 97, 97, 116, 116, 104, 104, 59, 59, 112, 112, + 102, 102, 59, 59, 99, 101, 114, 114, 59, 59, 114, 114, 99, 99, 121, 121, + 59, 59, 107, 107, 99, 99, 121, 121, 59, 59, 97, 115, 112, 112, 112, 112, + 97, 97, 59, 118, 59, 59, 101, 121, 100, 100, 105, 105, 108, 108, 59, 59, + 59, 59, 114, 114, 59, 59, 114, 114, 101, 101, 101, 101, 110, 110, 59, 59, + 99, 99, 121, 121, 59, 59, 99, 99, 121, 121, 59, 59, 112, 112, 102, 102, + 59, 59, 99, 99, 114, 114, 59, 59, 65, 118, 97, 116, 114, 114, 114, 114, + 59, 59, 114, 114, 59, 59, 97, 97, 105, 105, 108, 108, 59, 59, 97, 97, + 114, 114, 114, 114, 59, 59, 59, 103, 59, 59, 97, 97, 114, 114, 59, 59, + 99, 116, 117, 117, 116, 116, 101, 101, 59, 59, 109, 109, 112, 112, 116, 116, + 121, 121, 118, 118, 59, 59, 114, 114, 97, 97, 110, 110, 59, 59, 98, 98, + 100, 100, 97, 97, 59, 59, 103, 103, 59, 108, 59, 59, 101, 101, 59, 59, + 59, 59, 117, 117, 111, 111, 114, 114, 59, 116, 59, 102, 115, 115, 59, 59, + 115, 115, 59, 59, 107, 107, 59, 59, 112, 112, 59, 59, 108, 108, 59, 59, + 105, 105, 109, 109, 59, 59, 108, 108, 59, 59, 59, 101, 105, 105, 108, 108, + 59, 59, 59, 115, 59, 59, 97, 114, 114, 114, 114, 114, 59, 59, 114, 114, + 107, 107, 59, 59, 97, 107, 99, 99, 101, 107, 59, 59, 59, 59, 101, 115, + 59, 59, 108, 108, 100, 117, 59, 59, 59, 59, 97, 121, 114, 114, 111, 111, + 110, 110, 59, 59, 100, 105, 105, 105, 108, 108, 59, 59, 108, 108, 59, 59, + 98, 98, 59, 59, 59, 59, 99, 115, 97, 97, 59, 59, 117, 117, 111, 111, + 59, 114, 59, 59, 100, 117, 104, 104, 97, 97, 114, 114, 59, 59, 115, 115, + 104, 104, 97, 97, 114, 114, 59, 59, 104, 104, 59, 59, 59, 115, 116, 116, + 97, 116, 114, 114, 114, 114, 111, 111, 119, 119, 59, 116, 97, 97, 105, 105, + 108, 108, 59, 59, 97, 97, 114, 114, 112, 112, 111, 111, 111, 111, 110, 110, + 100, 117, 111, 111, 119, 119, 110, 110, 59, 59, 112, 112, 59, 59, 101, 101, + 102, 102, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 115, 115, + 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 97, 115, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 115, 59, 59, 97, 97, 114, 114, 112, 112, 111, 111, + 111, 111, 110, 110, 115, 115, 59, 59, 113, 113, 117, 117, 105, 105, 103, 103, + 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 104, 104, 114, 114, + 101, 101, 101, 101, 116, 116, 105, 105, 109, 109, 101, 101, 115, 115, 59, 59, + 59, 59, 59, 115, 59, 59, 108, 108, 97, 97, 110, 110, 116, 116, 59, 59, + 59, 115, 99, 99, 59, 59, 111, 111, 116, 116, 59, 111, 59, 114, 59, 59, + 59, 101, 115, 115, 59, 59, 97, 115, 112, 112, 112, 112, 114, 114, 111, 111, + 120, 120, 59, 59, 111, 111, 116, 116, 59, 59, 113, 113, 103, 113, 116, 116, + 114, 114, 59, 59, 103, 103, 116, 116, 114, 114, 59, 59, 116, 116, 114, 114, + 59, 59, 105, 105, 109, 109, 59, 59, 105, 114, 115, 115, 104, 104, 116, 116, + 59, 59, 111, 111, 111, 111, 114, 114, 59, 59, 59, 59, 59, 69, 59, 59, + 97, 98, 114, 114, 100, 117, 59, 59, 59, 108, 59, 59, 108, 108, 107, 107, + 59, 59, 99, 99, 121, 121, 59, 59, 59, 116, 114, 114, 114, 114, 59, 59, + 111, 111, 114, 114, 110, 110, 101, 101, 114, 114, 59, 59, 97, 97, 114, 114, + 100, 100, 59, 59, 114, 114, 105, 105, 59, 59, 105, 111, 100, 100, 111, 111, + 116, 116, 59, 59, 117, 117, 115, 115, 116, 116, 59, 97, 99, 99, 104, 104, + 101, 101, 59, 59, 69, 115, 59, 59, 112, 112, 59, 112, 114, 114, 111, 111, + 120, 120, 59, 59, 59, 113, 59, 113, 59, 59, 105, 105, 109, 109, 59, 59, + 97, 122, 110, 114, 103, 103, 59, 59, 114, 114, 59, 59, 114, 114, 107, 107, + 59, 59, 103, 103, 108, 114, 101, 101, 102, 102, 116, 116, 97, 114, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, + 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 97, 97, 112, 112, + 115, 115, 116, 116, 111, 111, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, + 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 112, 112, 97, 97, + 114, 114, 114, 114, 111, 111, 119, 119, 108, 114, 101, 101, 102, 102, 116, 116, + 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 59, 59, 97, 108, 114, 114, + 59, 59, 59, 59, 117, 117, 115, 115, 59, 59, 105, 105, 109, 109, 101, 101, + 115, 115, 59, 59, 97, 98, 115, 115, 116, 116, 59, 59, 97, 97, 114, 114, + 59, 59, 59, 102, 110, 110, 103, 103, 101, 101, 59, 59, 59, 59, 97, 97, + 114, 114, 59, 108, 116, 116, 59, 59, 97, 116, 114, 114, 114, 114, 59, 59, + 111, 111, 114, 114, 110, 110, 101, 101, 114, 114, 59, 59, 97, 97, 114, 114, + 59, 100, 59, 59, 59, 59, 114, 114, 105, 105, 59, 59, 97, 116, 113, 113, + 117, 117, 111, 111, 59, 59, 114, 114, 59, 59, 59, 59, 109, 109, 59, 103, + 59, 59, 59, 59, 98, 117, 59, 59, 111, 111, 59, 114, 59, 59, 114, 114, + 111, 111, 107, 107, 59, 59, 99, 105, 59, 59, 114, 114, 59, 59, 111, 111, + 116, 116, 59, 59, 114, 114, 101, 101, 101, 101, 59, 59, 109, 109, 101, 101, + 115, 115, 59, 59, 97, 97, 114, 114, 114, 114, 59, 59, 117, 117, 101, 101, + 115, 115, 116, 116, 59, 59, 80, 105, 97, 97, 114, 114, 59, 59, 59, 102, + 59, 59, 59, 59, 114, 114, 100, 117, 115, 115, 104, 104, 97, 97, 114, 114, + 59, 59, 104, 104, 97, 97, 114, 114, 59, 59, 101, 110, 114, 114, 116, 116, + 110, 110, 101, 101, 113, 113, 113, 113, 59, 59, 69, 69, 59, 59, 68, 117, + 68, 68, 111, 111, 116, 116, 59, 59, 99, 114, 114, 114, 101, 116, 59, 59, + 59, 101, 115, 115, 101, 101, 59, 59, 59, 115, 116, 116, 111, 111, 59, 117, + 111, 111, 119, 119, 110, 110, 59, 59, 101, 101, 102, 102, 116, 116, 59, 59, + 112, 112, 59, 59, 107, 107, 101, 101, 114, 114, 59, 59, 111, 121, 109, 109, + 109, 109, 97, 97, 59, 59, 59, 59, 97, 97, 115, 115, 104, 104, 59, 59, + 97, 97, 115, 115, 117, 117, 114, 114, 101, 101, 100, 100, 97, 97, 110, 110, + 103, 103, 108, 108, 101, 101, 59, 59, 114, 114, 59, 59, 111, 111, 59, 59, + 99, 110, 114, 114, 111, 111, 59, 100, 115, 115, 116, 116, 59, 59, 105, 105, + 114, 114, 59, 59, 111, 111, 116, 116, 117, 117, 115, 115, 59, 100, 59, 59, + 59, 117, 59, 59, 99, 100, 112, 112, 59, 59, 114, 114, 59, 59, 112, 112, + 108, 108, 117, 117, 115, 115, 59, 59, 100, 112, 101, 101, 108, 108, 115, 115, + 59, 59, 102, 102, 59, 59, 59, 59, 99, 116, 114, 114, 59, 59, 112, 112, + 111, 111, 115, 115, 59, 59, 59, 109, 116, 116, 105, 105, 109, 109, 97, 97, + 112, 112, 59, 59, 97, 97, 112, 112, 59, 59, 71, 119, 103, 116, 59, 59, + 59, 118, 59, 59, 101, 116, 102, 102, 116, 116, 97, 114, 114, 114, 114, 114, + 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 97, 97, + 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 59, 59, 59, 118, 59, 59, + 105, 105, 103, 103, 104, 104, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 68, 100, 97, 97, 115, 115, 104, 104, 59, 59, 97, 97, + 115, 115, 104, 104, 59, 59, 98, 116, 108, 108, 97, 97, 59, 59, 117, 117, + 116, 116, 101, 101, 59, 59, 103, 103, 59, 59, 59, 112, 59, 59, 100, 100, + 59, 59, 115, 115, 59, 59, 114, 114, 111, 111, 120, 120, 59, 59, 117, 117, + 114, 114, 59, 97, 108, 108, 59, 115, 59, 59, 115, 117, 112, 112, 109, 109, + 112, 112, 59, 101, 59, 59, 97, 121, 112, 114, 59, 59, 111, 111, 110, 110, + 59, 59, 100, 100, 105, 105, 108, 108, 59, 59, 110, 110, 103, 103, 59, 100, + 111, 111, 116, 116, 59, 59, 112, 112, 59, 59, 59, 59, 97, 97, 115, 115, + 104, 104, 59, 59, 59, 120, 114, 114, 114, 114, 59, 59, 114, 114, 104, 114, + 107, 107, 59, 59, 59, 111, 119, 119, 59, 59, 111, 111, 116, 116, 59, 59, + 117, 117, 105, 105, 118, 118, 59, 59, 101, 105, 97, 97, 114, 114, 59, 59, + 109, 109, 59, 59, 105, 105, 115, 115, 116, 116, 59, 115, 59, 59, 114, 114, + 59, 59, 69, 116, 59, 59, 59, 115, 59, 115, 59, 59, 108, 108, 97, 97, + 110, 110, 116, 116, 59, 59, 59, 59, 105, 105, 109, 109, 59, 59, 59, 114, + 59, 59, 65, 112, 114, 114, 114, 114, 59, 59, 114, 114, 114, 114, 59, 59, + 97, 97, 114, 114, 59, 59, 59, 118, 59, 100, 59, 59, 59, 59, 99, 99, + 121, 121, 59, 59, 65, 116, 114, 114, 114, 114, 59, 59, 59, 59, 114, 114, + 114, 114, 59, 59, 114, 114, 59, 59, 59, 115, 116, 116, 97, 114, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, + 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 59, 115, 59, 59, + 108, 108, 97, 97, 110, 110, 116, 116, 59, 59, 59, 115, 59, 59, 105, 105, + 109, 109, 59, 59, 59, 114, 105, 105, 59, 101, 59, 59, 105, 105, 100, 100, + 59, 59, 112, 116, 102, 102, 59, 59, 110, 110, 59, 118, 59, 59, 111, 111, + 116, 116, 59, 59, 97, 99, 59, 59, 59, 59, 59, 59, 105, 105, 59, 118, + 97, 99, 59, 59, 59, 59, 59, 59, 97, 114, 114, 114, 59, 116, 108, 108, + 108, 108, 101, 101, 108, 108, 59, 59, 108, 108, 59, 59, 59, 59, 108, 108, + 105, 105, 110, 110, 116, 116, 59, 59, 59, 101, 117, 117, 101, 101, 59, 59, + 59, 99, 59, 101, 113, 113, 59, 59, 65, 116, 114, 114, 114, 114, 59, 59, + 114, 114, 114, 114, 59, 119, 59, 59, 59, 59, 103, 103, 104, 104, 116, 116, + 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 114, 114, 105, 105, + 59, 101, 59, 59, 99, 117, 59, 114, 117, 117, 101, 101, 59, 59, 59, 59, + 59, 59, 111, 111, 114, 114, 116, 116, 109, 112, 105, 105, 100, 100, 59, 59, + 97, 97, 114, 114, 97, 97, 108, 108, 108, 108, 101, 101, 108, 108, 59, 59, + 109, 109, 59, 101, 59, 113, 59, 59, 105, 105, 100, 100, 59, 59, 97, 97, + 114, 114, 59, 59, 115, 115, 117, 117, 98, 112, 101, 101, 59, 59, 101, 101, + 59, 59, 98, 112, 59, 115, 59, 59, 59, 59, 101, 101, 116, 116, 59, 101, + 113, 113, 59, 113, 59, 59, 99, 99, 59, 101, 113, 113, 59, 59, 59, 115, + 59, 59, 59, 59, 101, 101, 116, 116, 59, 101, 113, 113, 59, 113, 59, 59, + 103, 114, 108, 108, 59, 59, 108, 108, 100, 100, 101, 101, 103, 103, 59, 59, + 105, 105, 97, 97, 110, 110, 103, 103, 108, 108, 101, 101, 108, 114, 101, 101, + 102, 102, 116, 116, 59, 101, 113, 113, 59, 59, 105, 105, 103, 103, 104, 104, + 116, 116, 59, 101, 113, 113, 59, 59, 59, 109, 59, 115, 114, 114, 111, 111, + 59, 59, 112, 112, 59, 59, 68, 115, 97, 97, 115, 115, 104, 104, 59, 59, + 97, 97, 114, 114, 114, 114, 59, 59, 112, 112, 59, 59, 97, 97, 115, 115, + 104, 104, 59, 59, 101, 116, 59, 59, 59, 59, 110, 110, 102, 102, 105, 105, + 110, 110, 59, 59, 65, 116, 114, 114, 114, 114, 59, 59, 59, 59, 59, 114, + 105, 105, 101, 101, 59, 59, 65, 116, 114, 114, 114, 114, 59, 59, 114, 114, + 105, 105, 101, 101, 59, 59, 105, 105, 109, 109, 59, 59, 65, 110, 114, 114, + 114, 114, 59, 59, 114, 114, 104, 114, 107, 107, 59, 59, 59, 111, 119, 119, + 59, 59, 101, 101, 97, 97, 114, 114, 59, 59, 83, 118, 59, 59, 99, 115, + 117, 117, 116, 116, 101, 101, 116, 116, 59, 59, 105, 121, 114, 114, 59, 99, + 59, 59, 97, 115, 115, 115, 104, 104, 59, 59, 108, 108, 97, 97, 99, 99, + 59, 59, 118, 118, 59, 59, 116, 116, 59, 59, 111, 111, 108, 108, 100, 100, + 59, 59, 108, 108, 105, 105, 103, 103, 59, 59, 99, 114, 105, 105, 114, 114, + 59, 59, 59, 59, 111, 116, 110, 110, 59, 59, 97, 97, 118, 118, 101, 101, + 59, 59, 98, 109, 97, 97, 114, 114, 59, 59, 59, 59, 110, 110, 116, 116, + 59, 59, 97, 116, 114, 114, 114, 114, 59, 59, 105, 114, 114, 114, 59, 59, + 111, 111, 115, 115, 115, 115, 59, 59, 110, 110, 101, 101, 59, 59, 59, 59, + 97, 105, 99, 99, 114, 114, 59, 59, 103, 103, 97, 97, 59, 59, 99, 110, + 114, 114, 111, 111, 110, 110, 59, 59, 59, 59, 117, 117, 115, 115, 59, 59, + 112, 112, 102, 102, 59, 59, 97, 108, 114, 114, 59, 59, 114, 114, 112, 112, + 59, 59, 117, 117, 115, 115, 59, 59, 59, 118, 114, 114, 114, 114, 59, 59, + 59, 109, 114, 114, 59, 111, 102, 102, 59, 59, 103, 103, 111, 111, 102, 102, + 59, 59, 114, 114, 59, 59, 108, 108, 111, 111, 112, 112, 101, 101, 59, 59, + 59, 59, 99, 111, 114, 114, 59, 59, 97, 97, 115, 115, 104, 104, 108, 108, + 59, 59, 105, 105, 108, 109, 100, 100, 101, 101, 101, 101, 115, 115, 59, 97, + 115, 115, 59, 59, 109, 109, 108, 108, 98, 98, 97, 97, 114, 114, 59, 59, + 97, 117, 114, 114, 59, 116, 108, 108, 101, 101, 108, 108, 59, 59, 105, 108, + 109, 109, 59, 59, 59, 59, 59, 59, 121, 121, 59, 59, 114, 114, 99, 116, + 110, 110, 116, 116, 59, 59, 111, 111, 100, 100, 59, 59, 105, 105, 108, 108, + 59, 59, 59, 59, 101, 101, 110, 110, 107, 107, 59, 59, 114, 114, 59, 59, + 105, 111, 59, 118, 59, 59, 109, 109, 97, 97, 116, 116, 59, 59, 110, 110, + 101, 101, 59, 59, 59, 118, 99, 99, 104, 104, 102, 102, 111, 111, 114, 114, + 107, 107, 59, 59, 59, 59, 97, 117, 110, 110, 99, 107, 107, 107, 59, 104, + 59, 59, 118, 118, 59, 59, 115, 115, 59, 116, 99, 99, 105, 105, 114, 114, + 59, 59, 59, 59, 105, 105, 114, 114, 59, 59, 111, 117, 59, 59, 59, 59, + 59, 59, 110, 110, 105, 105, 109, 109, 59, 59, 119, 119, 111, 111, 59, 59, + 59, 59, 105, 117, 110, 110, 116, 116, 105, 105, 110, 110, 116, 116, 59, 59, + 102, 102, 59, 59, 110, 110, 100, 100, 59, 117, 59, 59, 112, 112, 59, 59, + 117, 117, 101, 101, 59, 59, 59, 99, 59, 115, 112, 112, 112, 112, 114, 114, + 111, 111, 120, 120, 59, 59, 117, 117, 114, 114, 108, 108, 121, 121, 101, 101, + 113, 113, 59, 59, 113, 113, 59, 59, 97, 115, 112, 112, 112, 112, 114, 114, + 111, 111, 120, 120, 59, 59, 113, 113, 113, 113, 59, 59, 105, 105, 109, 109, + 59, 59, 105, 105, 109, 109, 59, 59, 109, 109, 101, 101, 59, 115, 59, 59, + 69, 115, 59, 59, 112, 112, 59, 59, 105, 105, 109, 109, 59, 59, 100, 112, + 59, 59, 97, 115, 108, 108, 97, 97, 114, 114, 59, 59, 105, 105, 110, 110, + 101, 101, 59, 59, 117, 117, 114, 114, 102, 102, 59, 59, 59, 116, 111, 111, + 59, 59, 105, 105, 109, 109, 59, 59, 114, 114, 101, 101, 108, 108, 59, 59, + 99, 105, 114, 114, 59, 59, 59, 59, 110, 110, 99, 99, 115, 115, 112, 112, + 59, 59, 102, 117, 114, 114, 59, 59, 110, 110, 116, 116, 59, 59, 112, 112, + 102, 102, 59, 59, 114, 114, 105, 105, 109, 109, 101, 101, 59, 59, 99, 99, + 114, 114, 59, 59, 97, 111, 116, 116, 101, 105, 114, 114, 110, 110, 105, 105, + 111, 111, 110, 110, 115, 115, 59, 59, 110, 110, 116, 116, 59, 59, 115, 115, + 116, 116, 59, 101, 113, 113, 59, 59, 116, 116, 65, 120, 97, 116, 114, 114, + 114, 114, 59, 59, 114, 114, 59, 59, 97, 97, 105, 105, 108, 108, 59, 59, + 97, 97, 114, 114, 114, 114, 59, 59, 97, 97, 114, 114, 59, 59, 99, 116, + 101, 117, 59, 59, 116, 116, 101, 101, 59, 59, 105, 105, 99, 99, 59, 59, + 109, 109, 112, 112, 116, 116, 121, 121, 118, 118, 59, 59, 103, 103, 59, 108, + 59, 59, 59, 59, 101, 101, 59, 59, 117, 117, 111, 111, 114, 114, 59, 119, + 112, 112, 59, 59, 59, 102, 115, 115, 59, 59, 59, 59, 115, 115, 59, 59, + 107, 107, 59, 59, 112, 112, 59, 59, 108, 108, 59, 59, 105, 105, 109, 109, + 59, 59, 108, 108, 59, 59, 59, 59, 97, 105, 105, 105, 108, 108, 59, 59, + 111, 111, 59, 110, 97, 97, 108, 108, 115, 115, 59, 59, 97, 114, 114, 114, + 114, 114, 59, 59, 114, 114, 107, 107, 59, 59, 97, 107, 99, 99, 101, 107, + 59, 59, 59, 59, 101, 115, 59, 59, 108, 108, 100, 117, 59, 59, 59, 59, + 97, 121, 114, 114, 111, 111, 110, 110, 59, 59, 100, 105, 105, 105, 108, 108, + 59, 59, 108, 108, 59, 59, 98, 98, 59, 59, 59, 59, 99, 115, 97, 97, + 59, 59, 100, 100, 104, 104, 97, 97, 114, 114, 59, 59, 117, 117, 111, 111, + 59, 114, 59, 59, 104, 104, 59, 59, 97, 103, 108, 108, 59, 115, 110, 110, + 101, 101, 59, 59, 97, 97, 114, 114, 116, 116, 59, 59, 59, 59, 116, 116, + 59, 59, 105, 114, 115, 115, 104, 104, 116, 116, 59, 59, 111, 111, 111, 111, + 114, 114, 59, 59, 59, 59, 97, 111, 114, 114, 100, 117, 59, 59, 59, 108, + 59, 59, 59, 118, 59, 59, 103, 115, 104, 104, 116, 116, 97, 116, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 116, 97, 97, 105, 105, 108, 108, 59, 59, + 97, 97, 114, 114, 112, 112, 111, 111, 111, 111, 110, 110, 100, 117, 111, 111, + 119, 119, 110, 110, 59, 59, 112, 112, 59, 59, 101, 101, 102, 102, 116, 116, + 97, 104, 114, 114, 114, 114, 111, 111, 119, 119, 115, 115, 59, 59, 97, 97, + 114, 114, 112, 112, 111, 111, 111, 111, 110, 110, 115, 115, 59, 59, 105, 105, + 103, 103, 104, 104, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, + 115, 115, 59, 59, 113, 113, 117, 117, 105, 105, 103, 103, 97, 97, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 104, 104, 114, 114, 101, 101, 101, 101, + 116, 116, 105, 105, 109, 109, 101, 101, 115, 115, 59, 59, 103, 103, 59, 59, + 105, 105, 110, 110, 103, 103, 100, 100, 111, 111, 116, 116, 115, 115, 101, 101, + 113, 113, 59, 59, 97, 109, 114, 114, 114, 114, 59, 59, 97, 97, 114, 114, + 59, 59, 59, 59, 111, 111, 117, 117, 115, 115, 116, 116, 59, 97, 99, 99, + 104, 104, 101, 101, 59, 59, 109, 109, 105, 105, 100, 100, 59, 59, 97, 116, + 110, 114, 103, 103, 59, 59, 114, 114, 59, 59, 114, 114, 107, 107, 59, 59, + 97, 108, 114, 114, 59, 59, 59, 59, 117, 117, 115, 115, 59, 59, 105, 105, + 109, 109, 101, 101, 115, 115, 59, 59, 97, 112, 114, 114, 59, 103, 116, 116, + 59, 59, 111, 111, 108, 108, 105, 105, 110, 110, 116, 116, 59, 59, 97, 97, + 114, 114, 114, 114, 59, 59, 97, 113, 113, 113, 117, 117, 111, 111, 59, 59, + 114, 114, 59, 59, 59, 59, 98, 117, 59, 59, 111, 111, 59, 114, 59, 59, + 104, 114, 114, 114, 101, 101, 101, 101, 59, 59, 109, 109, 101, 101, 115, 115, + 59, 59, 105, 105, 59, 108, 59, 59, 59, 59, 116, 116, 114, 114, 105, 105, + 59, 59, 108, 108, 117, 117, 104, 104, 97, 97, 114, 114, 59, 59, 59, 59, + 97, 122, 99, 99, 117, 117, 116, 116, 101, 101, 59, 59, 113, 113, 117, 117, + 111, 111, 59, 59, 59, 121, 59, 59, 112, 114, 59, 59, 111, 111, 110, 110, + 59, 59, 117, 117, 101, 101, 59, 59, 59, 100, 105, 105, 108, 108, 59, 59, + 114, 114, 99, 99, 59, 59, 69, 115, 59, 59, 112, 112, 59, 59, 105, 105, + 109, 109, 59, 59, 111, 111, 108, 108, 105, 105, 110, 110, 116, 116, 59, 59, + 105, 105, 109, 109, 59, 59, 59, 59, 111, 111, 116, 116, 59, 101, 59, 59, + 59, 59, 65, 120, 114, 114, 114, 114, 59, 59, 114, 114, 104, 114, 107, 107, + 59, 59, 59, 111, 119, 119, 59, 59, 116, 116, 105, 105, 59, 59, 119, 119, + 97, 97, 114, 114, 59, 59, 109, 109, 105, 110, 110, 110, 117, 117, 115, 115, + 59, 59, 59, 59, 116, 116, 59, 59, 114, 114, 59, 111, 119, 119, 110, 110, + 59, 59, 97, 121, 114, 114, 112, 112, 59, 59, 104, 121, 99, 99, 121, 121, + 59, 59, 59, 59, 114, 114, 116, 116, 109, 112, 105, 105, 100, 100, 59, 59, + 97, 97, 114, 114, 97, 97, 108, 108, 108, 108, 101, 101, 108, 108, 59, 59, + 103, 109, 109, 109, 97, 97, 59, 118, 59, 59, 59, 59, 59, 114, 111, 111, + 116, 116, 59, 59, 59, 113, 59, 59, 59, 69, 59, 59, 59, 69, 59, 59, + 101, 101, 59, 59, 108, 108, 117, 117, 115, 115, 59, 59, 97, 97, 114, 114, + 114, 114, 59, 59, 97, 97, 114, 114, 114, 114, 59, 59, 97, 116, 108, 115, + 108, 108, 115, 115, 101, 101, 116, 116, 109, 109, 105, 105, 110, 110, 117, 117, + 115, 115, 59, 59, 104, 104, 112, 112, 59, 59, 112, 112, 97, 97, 114, 114, + 115, 115, 108, 108, 59, 59, 100, 108, 59, 59, 101, 101, 59, 59, 59, 101, + 59, 115, 59, 59, 102, 112, 116, 116, 99, 99, 121, 121, 59, 59, 59, 98, + 59, 97, 114, 114, 59, 59, 102, 102, 59, 59, 97, 97, 100, 114, 101, 101, + 115, 115, 59, 117, 105, 105, 116, 116, 59, 59, 59, 59, 99, 117, 97, 117, + 112, 112, 59, 115, 59, 59, 112, 112, 59, 115, 59, 59, 117, 117, 98, 112, + 59, 115, 59, 59, 101, 101, 116, 116, 59, 101, 113, 113, 59, 59, 59, 115, + 59, 59, 101, 101, 116, 116, 59, 101, 113, 113, 59, 59, 59, 102, 114, 114, + 101, 102, 59, 59, 59, 59, 59, 59, 97, 97, 114, 114, 114, 114, 59, 59, + 99, 116, 114, 114, 59, 59, 116, 116, 109, 109, 110, 110, 59, 59, 105, 105, + 108, 108, 101, 101, 59, 59, 97, 97, 114, 114, 102, 102, 59, 59, 97, 114, + 114, 114, 59, 102, 59, 59, 97, 110, 105, 105, 103, 103, 104, 104, 116, 116, + 101, 112, 112, 112, 115, 115, 105, 105, 108, 108, 111, 111, 110, 110, 59, 59, + 104, 104, 105, 105, 59, 59, 115, 115, 59, 59, 98, 112, 59, 115, 59, 59, + 111, 111, 116, 116, 59, 59, 59, 100, 111, 111, 116, 116, 59, 59, 117, 117, + 108, 108, 116, 116, 59, 59, 69, 101, 59, 59, 59, 59, 108, 108, 117, 117, + 115, 115, 59, 59, 97, 97, 114, 114, 114, 114, 59, 59, 101, 117, 116, 116, + 59, 110, 113, 113, 59, 113, 59, 59, 101, 101, 113, 113, 59, 113, 59, 59, + 109, 109, 59, 59, 98, 112, 59, 59, 59, 59, 99, 99, 59, 115, 112, 112, + 112, 112, 114, 114, 111, 111, 120, 120, 59, 59, 117, 117, 114, 114, 108, 108, + 121, 121, 101, 101, 113, 113, 59, 59, 113, 113, 59, 59, 97, 115, 112, 112, + 112, 112, 114, 114, 111, 111, 120, 120, 59, 59, 113, 113, 113, 113, 59, 59, + 105, 105, 109, 109, 59, 59, 105, 105, 109, 109, 59, 59, 59, 59, 103, 103, + 59, 59, 49, 115, 59, 59, 111, 115, 116, 116, 59, 59, 117, 117, 98, 98, + 59, 59, 59, 100, 111, 111, 116, 116, 59, 59, 115, 115, 111, 117, 108, 108, + 59, 59, 98, 98, 59, 59, 97, 97, 114, 114, 114, 114, 59, 59, 117, 117, + 108, 108, 116, 116, 59, 59, 69, 101, 59, 59, 59, 59, 108, 108, 117, 117, + 115, 115, 59, 59, 101, 117, 116, 116, 59, 110, 113, 113, 59, 113, 59, 59, + 101, 101, 113, 113, 59, 113, 59, 59, 109, 109, 59, 59, 98, 112, 59, 59, + 59, 59, 65, 110, 114, 114, 114, 114, 59, 59, 114, 114, 104, 114, 107, 107, + 59, 59, 59, 111, 119, 119, 59, 59, 119, 119, 97, 97, 114, 114, 59, 59, + 108, 108, 105, 105, 103, 103, 97, 119, 114, 117, 103, 103, 101, 101, 116, 116, + 59, 59, 59, 59, 114, 114, 107, 107, 59, 59, 97, 121, 114, 114, 111, 111, + 110, 110, 59, 59, 100, 100, 105, 105, 108, 108, 59, 59, 59, 59, 111, 111, + 116, 116, 59, 59, 108, 108, 114, 114, 101, 101, 99, 99, 59, 59, 114, 114, + 59, 59, 101, 111, 114, 116, 101, 101, 52, 102, 59, 59, 111, 111, 114, 114, + 101, 101, 59, 59, 97, 97, 59, 118, 121, 121, 109, 109, 59, 59, 59, 59, + 99, 110, 107, 107, 97, 115, 112, 112, 112, 112, 114, 114, 111, 111, 120, 120, + 59, 59, 105, 105, 109, 109, 59, 59, 115, 115, 112, 112, 59, 59, 97, 115, + 112, 112, 59, 59, 105, 105, 109, 109, 59, 59, 114, 114, 110, 110, 108, 110, + 100, 100, 101, 101, 59, 59, 101, 101, 115, 115, 59, 97, 114, 114, 59, 59, + 59, 59, 116, 116, 59, 59, 101, 115, 97, 97, 59, 59, 59, 102, 111, 111, + 116, 116, 59, 59, 105, 105, 114, 114, 59, 59, 59, 111, 114, 114, 107, 107, + 59, 59, 97, 97, 59, 59, 114, 114, 105, 105, 109, 109, 101, 101, 59, 59, + 97, 112, 100, 100, 101, 101, 59, 59, 97, 116, 110, 110, 103, 103, 108, 108, + 101, 101, 59, 114, 111, 111, 119, 119, 110, 110, 59, 59, 101, 101, 102, 102, + 116, 116, 59, 101, 113, 113, 59, 59, 59, 59, 105, 105, 103, 103, 104, 104, + 116, 116, 59, 101, 113, 113, 59, 59, 111, 111, 116, 116, 59, 59, 59, 59, + 105, 105, 110, 110, 117, 117, 115, 115, 59, 59, 108, 108, 117, 117, 115, 115, + 59, 59, 98, 98, 59, 59, 105, 105, 109, 109, 101, 101, 59, 59, 101, 101, + 122, 122, 105, 105, 117, 117, 109, 109, 59, 59, 99, 116, 114, 121, 59, 59, + 59, 59, 99, 99, 121, 121, 59, 59, 114, 114, 111, 111, 107, 107, 59, 59, + 105, 111, 120, 120, 116, 116, 59, 59, 104, 104, 101, 101, 97, 97, 100, 100, + 108, 114, 101, 101, 102, 102, 116, 116, 97, 97, 114, 114, 114, 114, 111, 111, + 119, 119, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 97, 97, 114, 114, + 114, 114, 111, 111, 119, 119, 59, 59, 65, 119, 114, 114, 114, 114, 59, 59, + 97, 97, 114, 114, 59, 59, 99, 114, 117, 117, 116, 116, 101, 101, 114, 114, + 59, 59, 114, 114, 99, 101, 121, 121, 59, 59, 118, 118, 101, 101, 59, 59, + 105, 121, 114, 114, 99, 99, 59, 59, 97, 104, 114, 114, 114, 114, 59, 59, + 108, 108, 97, 97, 99, 99, 59, 59, 97, 97, 114, 114, 59, 59, 105, 114, + 115, 115, 104, 104, 116, 116, 59, 59, 59, 59, 114, 114, 97, 97, 118, 118, + 101, 101, 97, 98, 114, 114, 108, 114, 59, 59, 59, 59, 108, 108, 107, 107, + 59, 59, 99, 116, 111, 114, 114, 114, 110, 110, 59, 101, 114, 114, 59, 59, + 111, 111, 112, 112, 59, 59, 114, 114, 105, 105, 59, 59, 97, 108, 99, 99, + 114, 114, 59, 59, 103, 112, 111, 111, 110, 110, 59, 59, 102, 102, 59, 59, + 97, 117, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 111, 111, 119, 119, + 110, 110, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 59, 59, 97, 97, + 114, 114, 112, 112, 111, 111, 111, 111, 110, 110, 108, 114, 101, 101, 102, 102, + 116, 116, 59, 59, 105, 105, 103, 103, 104, 104, 116, 116, 59, 59, 117, 117, + 115, 115, 59, 59, 105, 105, 59, 108, 59, 59, 111, 111, 110, 110, 59, 59, + 112, 112, 97, 97, 114, 114, 114, 114, 111, 111, 119, 119, 115, 115, 59, 59, + 99, 116, 111, 114, 114, 114, 110, 110, 59, 101, 114, 114, 59, 59, 111, 111, + 112, 112, 59, 59, 110, 110, 103, 103, 59, 59, 114, 114, 105, 105, 59, 59, + 99, 99, 114, 114, 59, 59, 100, 114, 111, 111, 116, 116, 59, 59, 108, 108, + 100, 100, 101, 101, 59, 59, 105, 105, 59, 102, 59, 59, 97, 109, 114, 114, + 114, 114, 59, 59, 108, 108, 97, 97, 110, 110, 103, 103, 108, 108, 101, 101, + 59, 59, 65, 122, 114, 114, 114, 114, 59, 59, 97, 97, 114, 114, 59, 118, + 59, 59, 97, 97, 115, 115, 104, 104, 59, 59, 110, 114, 103, 103, 114, 114, + 116, 116, 59, 59, 101, 116, 112, 112, 115, 115, 105, 105, 108, 108, 111, 111, + 110, 110, 59, 59, 97, 97, 112, 112, 112, 112, 97, 97, 59, 59, 111, 111, + 116, 116, 104, 104, 105, 105, 110, 110, 103, 103, 59, 59, 104, 114, 105, 105, + 59, 59, 59, 59, 111, 111, 112, 112, 116, 116, 111, 111, 59, 59, 59, 104, + 111, 111, 59, 59, 105, 117, 103, 103, 109, 109, 97, 97, 59, 59, 98, 112, + 115, 115, 101, 101, 116, 116, 110, 110, 101, 101, 113, 113, 59, 113, 59, 59, + 115, 115, 101, 101, 116, 116, 110, 110, 101, 101, 113, 113, 59, 113, 59, 59, + 104, 114, 101, 101, 116, 116, 97, 97, 59, 59, 105, 105, 97, 97, 110, 110, + 103, 103, 108, 108, 101, 101, 108, 114, 101, 101, 102, 102, 116, 116, 59, 59, + 105, 105, 103, 103, 104, 104, 116, 116, 59, 59, 121, 121, 59, 59, 97, 97, + 115, 115, 104, 104, 59, 59, 101, 114, 59, 101, 97, 97, 114, 114, 59, 59, + 113, 113, 59, 59, 108, 108, 105, 105, 112, 112, 59, 59, 98, 116, 97, 97, + 114, 114, 59, 59, 59, 59, 114, 114, 59, 59, 116, 116, 114, 114, 105, 105, + 59, 59, 115, 115, 117, 117, 98, 112, 59, 59, 59, 59, 112, 112, 102, 102, + 59, 59, 114, 114, 111, 111, 112, 112, 59, 59, 116, 116, 114, 114, 105, 105, + 59, 59, 99, 117, 114, 114, 59, 59, 98, 112, 110, 110, 69, 101, 59, 59, + 59, 59, 110, 110, 69, 101, 59, 59, 59, 59, 105, 105, 103, 103, 122, 122, + 97, 97, 103, 103, 59, 59, 99, 115, 105, 105, 114, 114, 99, 99, 59, 59, + 100, 105, 98, 103, 97, 97, 114, 114, 59, 59, 101, 101, 59, 113, 59, 59, + 101, 101, 114, 114, 112, 112, 59, 59, 114, 114, 59, 59, 112, 112, 102, 102, + 59, 59, 59, 59, 59, 101, 97, 97, 116, 116, 104, 104, 59, 59, 99, 99, + 114, 114, 59, 59, 99, 119, 97, 117, 112, 112, 59, 59, 114, 114, 99, 99, + 59, 59, 112, 112, 59, 59, 116, 116, 114, 114, 105, 105, 59, 59, 114, 114, + 59, 59, 65, 97, 114, 114, 114, 114, 59, 59, 114, 114, 114, 114, 59, 59, + 59, 59, 65, 97, 114, 114, 114, 114, 59, 59, 114, 114, 114, 114, 59, 59, + 97, 97, 112, 112, 59, 59, 105, 105, 115, 115, 59, 59, 100, 116, 111, 111, + 116, 116, 59, 59, 102, 108, 59, 59, 117, 117, 115, 115, 59, 59, 105, 105, + 109, 109, 101, 101, 59, 59, 65, 97, 114, 114, 114, 114, 59, 59, 114, 114, + 114, 114, 59, 59, 99, 113, 114, 114, 59, 59, 99, 99, 117, 117, 112, 112, + 59, 59, 112, 116, 108, 108, 117, 117, 115, 115, 59, 59, 114, 114, 105, 105, + 59, 59, 101, 101, 101, 101, 59, 59, 101, 101, 100, 100, 103, 103, 101, 101, + 59, 59, 97, 117, 99, 99, 117, 121, 116, 116, 101, 101, 59, 59, 105, 121, + 114, 114, 99, 99, 59, 59, 59, 59, 110, 110, 114, 114, 59, 59, 99, 99, + 121, 121, 59, 59, 112, 112, 102, 102, 59, 59, 99, 99, 114, 114, 59, 59, + 99, 109, 121, 121, 59, 59, 108, 108, 97, 119, 99, 99, 117, 117, 116, 116, + 101, 101, 59, 59, 97, 121, 114, 114, 111, 111, 110, 110, 59, 59, 59, 59, + 111, 111, 116, 116, 59, 59, 101, 116, 116, 116, 114, 114, 102, 102, 59, 59, + 97, 97, 59, 59, 114, 114, 59, 59, 99, 99, 121, 121, 59, 59, 103, 103, + 114, 114, 97, 97, 114, 114, 114, 114, 59, 59, 112, 112, 102, 102, 59, 59, + 99, 99, 114, 114, 59, 59, 106, 110, 59, 59, 106, 106, 59, 59, 65, 122, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 101, 59, 115, 59, 59, 59, 59, 59, 111, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 114, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 114, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 110, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 108, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 100, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 0 +}; + +static const char _char_ref_key_spans[] = { + 0, 49, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 17, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 10, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 17, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 21, 16, 1, + 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 1, 1, 1, 23, 1, 1, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 15, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 17, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 17, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 14, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 10, 1, 1, 43, 1, 14, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 13, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9, 1, 1, 1, 48, 53, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 13, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 25, + 1, 1, 1, 1, 1, 1, 58, 1, + 1, 1, 1, 6, 11, 1, 1, 1, + 1, 1, 1, 1, 20, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8, + 1, 1, 11, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 20, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4, + 1, 1, 1, 1, 1, 1, 1, 1, + 11, 1, 1, 20, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 20, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 4, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 33, + 1, 1, 1, 1, 27, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 18, 1, 1, 1, 1, 1, 1, + 43, 1, 1, 1, 1, 1, 1, 1, + 25, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 16, + 1, 1, 1, 1, 1, 4, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 10, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 26, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7, 1, 1, + 1, 1, 1, 1, 1, 1, 8, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 17, 1, 1, + 1, 1, 1, 1, 1, 1, 4, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 43, 1, 1, 1, 1, + 1, 1, 42, 1, 1, 1, 1, 1, + 1, 21, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 16, 1, 1, 1, 1, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 53, 1, 1, 1, 1, 1, 18, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 49, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 17, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 54, + 5, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, + 43, 12, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 14, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, + 1, 1, 1, 1, 19, 17, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 44, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 21, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 43, 1, 1, 1, 16, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 25, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 14, 1, + 50, 5, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 24, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 22, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 14, 1, 28, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 11, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 19, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8, 1, 1, 1, 1, 1, + 1, 1, 1, 8, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 16, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 43, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 10, + 1, 39, 1, 1, 1, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 18, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 21, 1, 1, + 1, 1, 9, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 1, 1, + 1, 1, 1, 1, 1, 25, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 17, + 1, 1, 1, 1, 1, 10, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 12, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 51, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 28, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 13, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 26, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 26, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 14, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 11, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 26, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 25, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 5, 1, 1, 1, + 1, 1, 1, 15, 1, 1, 1, 11, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 11, 1, 1, 1, 1, 1, + 15, 1, 1, 1, 11, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 26, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 11, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 26, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 50, 1, 1, 1, 1, 1, + 1, 1, 1, 17, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 9, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 14, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 10, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 18, 1, 1, 1, + 1, 7, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 19, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 53, 1, + 1, 1, 1, 1, 26, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 7, 1, 1, 1, 31, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 52, 1, 1, 1, 1, 1, 16, + 1, 1, 1, 1, 1, 1, 1, 58, + 1, 1, 25, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 60, 1, 1, 1, + 1, 17, 6, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 33, + 5, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 18, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 1, 1, 1, + 1, 14, 1, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 11, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 19, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 1, 1, 1, + 1, 8, 1, 1, 1, 1, 1, 1, + 1, 1, 6, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 46, + 33, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 63, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 18, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4, 1, 1, 1, 1, 1, 27, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 15, 1, 1, 1, + 11, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 15, 57, 1, 1, + 11, 1, 1, 1, 1, 1, 6, 1, + 1, 1, 1, 26, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 1, 1, + 1, 1, 1, 1, 28, 1, 1, 1, + 1, 1, 20, 1, 1, 25, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 12, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 26, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 18, 1, 1, 1, 1, + 1, 1, 21, 16, 1, 1, 1, 1, + 53, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 17, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6, + 1, 1, 15, 18, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 22, 1, 1, 1, 1, 10, + 1, 1, 1, 1, 1, 51, 1, 1, + 1, 1, 10, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 50, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 51, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 50, + 1, 14, 1, 24, 1, 1, 1, 47, + 1, 1, 1, 19, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 17, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 14, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 44, 1, 1, 1, 1, 1, 1, + 1, 1, 25, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 23, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 63, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 56, + 1, 1, 1, 1, 1, 12, 11, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 16, 10, 1, 1, 1, 1, 4, 60, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 64, 1, 1, 1, 1, 1, + 39, 8, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 42, 1, 5, + 1, 1, 1, 1, 1, 1, 1, 10, + 1, 1, 1, 1, 1, 54, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 43, 1, 1, 1, 1, + 1, 23, 1, 1, 1, 1, 1, 43, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 1, 1, 16, 1, + 17, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 43, 1, 1, 2, 1, + 1, 1, 1, 1, 45, 1, 1, 1, + 1, 58, 1, 1, 1, 1, 11, 1, + 1, 1, 1, 1, 1, 1, 1, 18, + 1, 1, 1, 43, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 23, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 21, 21, 1, 1, 1, + 1, 1, 1, 1, 17, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 15, 12, 1, 9, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 56, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 3, 1, 1, 1, 1, 1, + 1, 1, 11, 55, 1, 1, 1, 1, + 1, 1, 9, 1, 1, 58, 1, 1, + 1, 1, 1, 1, 1, 51, 39, 1, + 1, 1, 1, 59, 1, 1, 1, 1, + 39, 1, 1, 1, 1, 56, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 59, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 56, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 13, + 1, 1, 1, 1, 1, 1, 43, 1, + 1, 46, 1, 1, 1, 1, 1, 2, + 1, 43, 1, 1, 1, 43, 1, 55, + 1, 25, 16, 1, 1, 1, 1, 57, + 1, 1, 1, 1, 1, 1, 1, 1, + 21, 1, 1, 1, 1, 1, 1, 1, + 1, 11, 1, 1, 1, 1, 21, 3, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 57, 1, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7, 1, 1, 1, 1, + 51, 1, 1, 1, 1, 1, 1, 57, + 1, 50, 1, 1, 1, 4, 1, 1, + 1, 1, 7, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 19, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 59, 1, 1, 1, 5, 1, + 1, 43, 55, 1, 4, 1, 58, 1, + 50, 1, 1, 1, 12, 1, 1, 1, + 1, 1, 1, 1, 3, 42, 1, 1, + 1, 1, 1, 1, 20, 1, 1, 1, + 1, 1, 1, 15, 1, 1, 1, 1, + 1, 1, 19, 1, 1, 15, 43, 1, + 43, 1, 1, 1, 1, 1, 20, 1, + 1, 1, 7, 1, 1, 4, 1, 1, + 1, 1, 1, 1, 1, 54, 1, 57, + 1, 1, 1, 1, 1, 21, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 22, 1, 1, 51, 1, 1, 19, 1, + 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 58, 1, 1, 1, 1, 1, 1, 13, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 25, 1, + 1, 1, 1, 1, 53, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7, 1, 1, 1, 1, 1, 1, + 1, 1, 10, 1, 1, 1, 1, 1, + 1, 1, 7, 1, 1, 22, 1, 57, + 1, 1, 57, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 53, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4, 1, 1, 1, 1, 1, + 1, 12, 1, 1, 1, 1, 1, 1, + 57, 1, 42, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, + 1, 4, 1, 1, 1, 1, 1, 1, + 18, 8, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 1, 1, 1, 1, 44, + 1, 8, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 53, + 44, 1, 1, 1, 1, 1, 17, 1, + 1, 1, 1, 1, 1, 1, 25, 1, + 1, 1, 1, 1, 41, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 47, 1, + 1, 1, 1, 57, 1, 1, 1, 42, + 1, 1, 1, 57, 1, 1, 1, 1, + 1, 1, 1, 42, 1, 1, 1, 19, + 1, 1, 1, 1, 1, 60, 1, 1, + 1, 1, 1, 11, 2, 1, 1, 13, + 1, 1, 1, 10, 1, 1, 1, 1, + 1, 19, 1, 57, 1, 1, 1, 1, + 1, 1, 60, 1, 1, 1, 1, 20, + 7, 1, 1, 1, 1, 1, 1, 1, + 4, 1, 1, 1, 1, 1, 6, 1, + 1, 1, 1, 1, 1, 1, 9, 1, + 1, 1, 1, 1, 1, 1, 10, 1, + 1, 1, 1, 1, 1, 1, 1, 30, + 1, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 8, 1, + 6, 1, 1, 1, 14, 1, 1, 1, + 1, 1, 11, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 19, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 10, 1, 1, 1, 1, 4, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 20, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3, 1, 1, 11, 1, 1, 1, 60, + 1, 1, 1, 1, 1, 1, 1, 1, + 15, 17, 7, 7, 1, 1, 1, 1, + 3, 1, 1, 5, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 50, 50, 1, + 14, 1, 1, 1, 1, 1, 1, 42, + 1, 1, 1, 1, 1, 1, 1, 17, + 1, 1, 1, 1, 1, 1, 1, 57, + 1, 57, 1, 1, 1, 1, 1, 1, + 50, 1, 1, 1, 1, 53, 50, 1, + 43, 1, 1, 1, 1, 45, 1, 1, + 1, 1, 1, 1, 1, 1, 48, 1, + 1, 1, 47, 1, 1, 54, 1, 1, + 1, 1, 55, 55, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7, + 1, 1, 1, 50, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 19, 3, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 10, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 1, 1, 10, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 15, 1, 1, + 1, 61, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 18, 1, 1, + 1, 59, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 19, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 18, 1, 1, 1, 1, + 1, 1, 1, 1, 7, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 18, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 15, 1, 1, 1, 1, 1, 1, + 1, 1, 21, 1, 1, 1, 1, 63, + 1, 1, 1, 22, 1, 1, 1, 1, + 13, 1, 1, 1, 1, 1, 1, 53, + 6, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 16, 18, 1, 1, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 58, 1, + 1, 1, 1, 1, 1, 58, 1, 1, + 1, 1, 1, 1, 1, 54, 1, 1, + 1, 12, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 18, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7, 1, 1, + 1, 60, 1, 1, 1, 1, 60, 1, + 1, 47, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 19, 17, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 19, 1, 1, + 1, 60, 1, 21, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 54, 20, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 45, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 1, + 1, 1, 1, 1, 58, 44, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 1, 1, + 1, 57, 1, 18, 1, 1, 1, 1, + 1, 1, 11, 1, 7, 1, 1, 15, + 1, 1, 18, 1, 1, 25, 1, 1, + 1, 1, 6, 1, 1, 1, 1, 1, + 1, 1, 1, 17, 1, 1, 1, 1, + 56, 1, 18, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 20, 1, 1, 1, 1, 58, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 19, 1, 1, + 1, 1, 57, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 57, 1, 1, 1, 1, 1, 1, + 57, 1, 1, 1, 1, 53, 56, 1, + 43, 1, 1, 19, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 10, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, + 2, 1, 18, 1, 50, 1, 1, 1, + 1, 1, 1, 1, 58, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7, 1, 1, + 1, 1, 1, 1, 1, 39, 1, 1, + 1, 1, 47, 1, 1, 54, 1, 1, + 1, 1, 55, 55, 1, 1, 1, 1, + 26, 5, 1, 1, 1, 1, 1, 1, + 1, 1, 7, 1, 1, 1, 18, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 12, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, + 1, 44, 1, 1, 1, 1, 1, 1, + 1, 50, 1, 1, 20, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 42, 1, 1, 1, 1, 1, 20, 1, + 1, 1, 1, 1, 1, 1, 1, 45, + 1, 1, 20, 1, 1, 56, 1, 1, + 1, 1, 1, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 26, 1, 1, 1, 44, + 1, 1, 1, 18, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 10, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 50, + 1, 1, 1, 1, 16, 1, 16, 1, + 43, 1, 1, 1, 57, 1, 1, 59, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 12, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 42, 1, + 59, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 51, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 49, 14, 1, + 60, 1, 16, 1, 1, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 60, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 33, 1, 1, 1, 1, 1, + 1, 1, 1, 19, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 54, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 57, 1, 3, 1, 1, + 1, 43, 1, 25, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 42, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 62, 1, 1, 1, 1, 11, + 1, 1, 53, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 5, 1, 1, 1, + 1, 1, 1, 1, 1, 57, 1, 1, + 1, 48, 1, 57, 57, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 56, + 1, 48, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 60, 42, 1, 1, 1, + 1, 1, 52, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 57, 1, 18, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 1, 1, 1, 1, 1, 57, 1, 1, + 1, 1, 56, 1, 43, 1, 1, 1, + 1, 5, 1, 1, 1, 60, 1, 1, + 1, 1, 3, 1, 1, 1, 1, 60, + 3, 1, 1, 1, 18, 1, 58, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 43, 1, 1, 1, + 41, 43, 1, 1, 52, 1, 1, 1, + 1, 1, 61, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 43, 1, 19, 56, 1, 1, 1, 1, + 1, 1, 1, 1, 4, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 43, 55, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 15, 1, 1, 1, + 1, 15, 57, 1, 1, 1, 1, 43, + 1, 55, 1, 1, 43, 1, 1, 57, + 1, 1, 1, 1, 43, 1, 55, 1, + 12, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 43, 1, 1, 1, 1, 1, + 1, 43, 1, 1, 51, 57, 1, 1, + 1, 1, 1, 48, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 16, 1, 1, 1, 1, 1, + 1, 1, 52, 1, 1, 1, 1, 56, + 1, 1, 1, 52, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 1, 1, 11, 1, 1, 53, 1, + 1, 1, 1, 1, 1, 36, 1, 17, + 1, 1, 1, 1, 1, 17, 1, 41, + 1, 19, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 16, 1, 1, + 1, 1, 6, 1, 1, 1, 1, 1, + 1, 12, 1, 1, 1, 1, 1, 1, + 1, 20, 1, 1, 1, 10, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9, 1, 1, 1, 1, 1, 1, 12, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 12, 1, 1, 1, 1, + 1, 1, 1, 1, 60, 1, 1, 1, + 51, 1, 53, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 13, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 1, 1, 1, 1, + 21, 1, 58, 1, 1, 1, 1, 4, + 1, 1, 1, 1, 1, 1, 1, 18, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 60, 1, 1, 1, 1, 1, 1, + 1, 1, 60, 1, 1, 1, 1, 1, + 1, 1, 1, 21, 1, 9, 1, 46, + 1, 1, 1, 1, 58, 1, 1, 1, + 1, 1, 1, 1, 1, 7, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 13, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 59, 1, 1, 1, + 1, 1, 1, 41, 57, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 19, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 47, 1, 1, 1, 1, 1, 1, 13, + 1, 19, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 58, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 1, 16, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 1, 5, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 43, 1, 1, 1, 56, 20, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 18, + 17, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 50, + 1, 1, 1, 1, 1, 1, 1, 61, + 1, 1, 44, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 9, 1, 1, 1, + 1, 52, 1, 1, 1, 1, 18, 1, + 1, 1, 1, 1, 1, 11, 1, 7, + 1, 1, 15, 1, 1, 18, 1, 1, + 25, 1, 1, 1, 1, 6, 1, 1, + 1, 1, 1, 1, 1, 1, 17, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 56, 1, 1, 1, 7, 1, 57, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 10, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 1, 18, 1, 50, + 1, 60, 1, 13, 1, 1, 20, 1, + 1, 1, 1, 58, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 18, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 13, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 39, 1, + 1, 1, 1, 1, 1, 1, 1, 20, + 5, 1, 1, 1, 1, 1, 1, 1, + 12, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 16, 1, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 17, 1, 1, 1, 1, + 1, 1, 1, 20, 1, 1, 56, 1, + 11, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 50, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 26, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 63, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 42, 1, 1, 1, + 1, 1, 1, 47, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 43, 1, + 1, 56, 1, 1, 1, 1, 11, 1, + 1, 53, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 1, 1, 1, + 1, 1, 1, 1, 1, 53, 1, 1, + 1, 25, 1, 1, 1, 18, 1, 1, + 1, 1, 1, 1, 4, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 60, 1, 1, 56, 1, + 1, 1, 55, 1, 11, 1, 11, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 20, 8, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9, 1, 1, 1, 43, + 57, 1, 11, 1, 1, 1, 1, 40, + 39, 1, 1, 1, 1, 1, 15, 1, + 1, 59, 1, 1, 1, 1, 19, 21, + 1, 57, 1, 1, 57, 1, 1, 15, + 57, 1, 1, 1, 43, 1, 1, 57, + 1, 1, 1, 43, 1, 1, 44, 1, + 2, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 18, + 1, 44, 1, 14, 1, 1, 1, 1, + 12, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 15, 57, 1, + 1, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 33, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 17, 1, + 52, 1, 55, 1, 1, 1, 55, 1, + 1, 1, 15, 1, 1, 1, 57, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 19, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 67, 1, 5, 1, 1, 1, 1, + 1, 42, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 33, 1, 1, 1, 1, + 1, 1, 17, 1, 52, 1, 55, 1, + 1, 1, 55, 1, 1, 1, 15, 1, + 1, 46, 1, 1, 1, 1, 11, 1, + 1, 53, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 23, 4, 1, 1, 1, + 1, 1, 1, 1, 1, 25, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 11, 3, 1, 51, 1, 1, 1, + 1, 1, 1, 60, 1, 1, 1, 1, + 12, 1, 19, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 19, + 1, 1, 1, 1, 1, 1, 1, 3, + 1, 1, 1, 1, 1, 39, 1, 1, + 1, 1, 1, 15, 1, 1, 44, 1, + 1, 1, 1, 1, 1, 53, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 16, 1, 1, 1, 20, 1, 1, 1, + 1, 56, 1, 1, 1, 1, 1, 1, + 1, 43, 1, 1, 1, 1, 1, 1, + 1, 43, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 18, 8, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 55, 1, 1, 1, + 1, 1, 1, 16, 1, 1, 1, 1, + 1, 1, 3, 1, 1, 1, 1, 1, + 17, 1, 1, 1, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 10, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 7, 1, 1, 1, 1, + 1, 18, 4, 1, 1, 43, 1, 1, + 1, 1, 1, 1, 1, 1, 12, 1, + 1, 1, 10, 1, 1, 1, 1, 1, + 21, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 50, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 4, 1, 1, 43, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 13, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 58, 1, 1, 1, 1, 1, 60, + 1, 1, 1, 1, 1, 5, 1, 1, + 1, 1, 16, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, + 1, 1, 1, 1, 1, 1, 1, 46, + 1, 1, 13, 1, 1, 1, 1, 15, + 1, 1, 1, 1, 1, 1, 55, 1, + 1, 1, 1, 1, 1, 1, 55, 1, + 11, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 14, 43, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 19, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 19, 1, 1, 15, 1, 33, 1, + 1, 1, 33, 1, 1, 1, 1, 1, + 1, 1, 1, 17, 1, 1, 1, 1, + 6, 6, 1, 1, 1, 1, 55, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 43, 1, 1, 1, 1, 1, + 1, 1, 21, 21, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 33, 1, 1, 1, 1, 1, 1, + 1, 33, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 17, 1, + 1, 1, 7, 1, 1, 1, 1, 1, + 1, 1, 1, 33, 1, 1, 1, 1, + 1, 1, 15, 1, 1, 1, 1, 1, + 1, 5, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 21, 1, 5, 1, 1, 1, 17, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 11, 1, 1, 1, 23, 1, 1, 1, + 1, 1, 25, 1, 1, 1, 1, 1, + 1, 1, 1, 16, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5, 1, 1, 1, 58, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 43, 57, 1, 1, 53, 1, 1, 1, + 1, 1, 1, 1, 1, 56, 1, 1, + 1, 1, 1, 1, 1, 56, 1, 1, + 1, 1, 52, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 42, 1, 1, 1, 1, 1, 1, + 1, 1 +}; + +static const unsigned short _char_ref_index_offsets[] = { + 0, 0, 50, 52, 54, 56, 58, 60, + 62, 64, 66, 68, 70, 72, 74, 76, + 94, 96, 98, 100, 102, 104, 106, 108, + 110, 112, 114, 116, 118, 120, 122, 124, + 126, 128, 130, 132, 143, 145, 147, 149, + 151, 153, 155, 157, 159, 161, 163, 165, + 167, 169, 171, 173, 175, 177, 179, 181, + 183, 201, 203, 205, 207, 209, 211, 213, + 215, 217, 219, 221, 223, 225, 247, 264, + 266, 268, 270, 272, 274, 276, 278, 281, + 283, 285, 287, 289, 291, 293, 312, 314, + 316, 318, 320, 322, 324, 326, 328, 330, + 332, 334, 336, 338, 340, 342, 344, 346, + 348, 350, 352, 354, 356, 358, 360, 362, + 364, 366, 368, 370, 372, 374, 376, 423, + 425, 427, 429, 431, 433, 457, 459, 461, + 463, 465, 513, 515, 517, 519, 521, 523, + 525, 527, 529, 531, 533, 535, 537, 539, + 541, 543, 545, 547, 549, 551, 553, 555, + 557, 573, 575, 577, 579, 581, 583, 585, + 587, 589, 591, 593, 595, 597, 599, 601, + 603, 605, 607, 609, 621, 623, 625, 627, + 629, 631, 633, 635, 637, 639, 641, 643, + 645, 647, 649, 651, 653, 655, 657, 659, + 661, 679, 681, 683, 685, 687, 689, 691, + 693, 695, 697, 699, 701, 703, 705, 707, + 709, 711, 713, 715, 733, 735, 737, 739, + 741, 743, 745, 747, 749, 751, 753, 755, + 757, 759, 761, 763, 765, 767, 769, 771, + 773, 775, 777, 779, 781, 783, 785, 787, + 802, 804, 806, 808, 810, 812, 814, 816, + 818, 820, 822, 824, 826, 828, 830, 832, + 834, 845, 847, 849, 893, 895, 910, 912, + 914, 916, 918, 920, 922, 924, 926, 928, + 930, 932, 934, 936, 938, 940, 942, 944, + 946, 948, 950, 952, 966, 968, 970, 972, + 974, 976, 978, 980, 982, 984, 986, 988, + 990, 992, 994, 996, 998, 1000, 1002, 1004, + 1006, 1008, 1010, 1012, 1014, 1016, 1018, 1020, + 1022, 1024, 1026, 1028, 1030, 1032, 1034, 1036, + 1038, 1040, 1042, 1044, 1046, 1048, 1050, 1052, + 1054, 1064, 1066, 1068, 1070, 1119, 1173, 1175, + 1177, 1179, 1181, 1183, 1185, 1187, 1189, 1191, + 1193, 1195, 1197, 1199, 1201, 1203, 1217, 1219, + 1221, 1223, 1225, 1227, 1229, 1231, 1233, 1235, + 1261, 1263, 1265, 1267, 1269, 1271, 1273, 1332, + 1334, 1336, 1338, 1340, 1347, 1359, 1361, 1363, + 1365, 1367, 1369, 1371, 1373, 1394, 1396, 1398, + 1400, 1402, 1404, 1406, 1409, 1411, 1413, 1415, + 1417, 1419, 1421, 1423, 1425, 1427, 1429, 1431, + 1433, 1435, 1437, 1439, 1441, 1443, 1445, 1447, + 1449, 1451, 1453, 1455, 1457, 1459, 1461, 1463, + 1465, 1467, 1469, 1471, 1473, 1475, 1477, 1479, + 1488, 1490, 1492, 1504, 1506, 1508, 1510, 1512, + 1514, 1516, 1518, 1520, 1522, 1524, 1526, 1547, + 1549, 1551, 1553, 1555, 1557, 1559, 1561, 1563, + 1565, 1567, 1569, 1571, 1573, 1575, 1577, 1579, + 1584, 1586, 1588, 1590, 1592, 1594, 1596, 1598, + 1600, 1612, 1614, 1616, 1637, 1639, 1641, 1643, + 1645, 1647, 1649, 1651, 1653, 1655, 1657, 1659, + 1661, 1663, 1665, 1667, 1669, 1671, 1673, 1675, + 1677, 1685, 1687, 1689, 1691, 1710, 1712, 1714, + 1716, 1718, 1720, 1722, 1724, 1726, 1728, 1730, + 1732, 1734, 1736, 1738, 1740, 1742, 1744, 1746, + 1748, 1750, 1752, 1754, 1756, 1758, 1760, 1762, + 1764, 1766, 1768, 1789, 1791, 1793, 1795, 1797, + 1799, 1801, 1803, 1805, 1807, 1812, 1814, 1816, + 1818, 1820, 1822, 1824, 1826, 1828, 1830, 1832, + 1834, 1836, 1838, 1840, 1842, 1844, 1846, 1848, + 1850, 1852, 1854, 1856, 1858, 1860, 1862, 1864, + 1898, 1900, 1902, 1904, 1906, 1934, 1936, 1938, + 1940, 1942, 1944, 1946, 1948, 1950, 1952, 1954, + 1956, 1958, 1960, 1962, 1964, 1966, 1968, 1970, + 1976, 1978, 1980, 1982, 1984, 1986, 1988, 1990, + 1992, 1994, 1996, 1998, 2000, 2002, 2004, 2006, + 2008, 2010, 2012, 2014, 2016, 2018, 2020, 2022, + 2024, 2026, 2035, 2037, 2039, 2041, 2043, 2045, + 2047, 2049, 2053, 2055, 2057, 2059, 2061, 2063, + 2065, 2067, 2069, 2071, 2073, 2075, 2077, 2079, + 2081, 2090, 2092, 2094, 2096, 2098, 2100, 2108, + 2110, 2112, 2114, 2116, 2118, 2120, 2122, 2124, + 2126, 2128, 2147, 2149, 2151, 2153, 2155, 2157, + 2159, 2203, 2205, 2207, 2209, 2211, 2213, 2215, + 2217, 2243, 2245, 2247, 2249, 2251, 2253, 2255, + 2257, 2259, 2261, 2263, 2265, 2267, 2269, 2271, + 2273, 2275, 2277, 2279, 2281, 2283, 2285, 2287, + 2304, 2306, 2308, 2310, 2312, 2314, 2319, 2321, + 2323, 2325, 2327, 2329, 2331, 2333, 2335, 2337, + 2339, 2341, 2343, 2345, 2347, 2349, 2351, 2353, + 2355, 2357, 2359, 2361, 2363, 2365, 2367, 2369, + 2371, 2382, 2384, 2386, 2388, 2390, 2392, 2394, + 2396, 2398, 2400, 2402, 2404, 2406, 2416, 2418, + 2445, 2447, 2449, 2451, 2453, 2455, 2457, 2459, + 2461, 2463, 2465, 2467, 2469, 2471, 2479, 2481, + 2483, 2485, 2487, 2489, 2491, 2493, 2495, 2504, + 2506, 2508, 2510, 2512, 2514, 2516, 2518, 2520, + 2522, 2524, 2526, 2528, 2530, 2532, 2550, 2552, + 2554, 2556, 2558, 2560, 2562, 2564, 2566, 2571, + 2573, 2575, 2577, 2579, 2581, 2583, 2585, 2587, + 2589, 2591, 2593, 2595, 2597, 2599, 2601, 2603, + 2605, 2607, 2609, 2611, 2613, 2615, 2617, 2619, + 2621, 2623, 2630, 2632, 2634, 2636, 2638, 2640, + 2642, 2644, 2646, 2648, 2650, 2652, 2654, 2656, + 2658, 2660, 2662, 2664, 2708, 2710, 2712, 2714, + 2716, 2718, 2720, 2763, 2765, 2767, 2769, 2771, + 2773, 2775, 2797, 2799, 2801, 2803, 2805, 2807, + 2809, 2811, 2813, 2815, 2817, 2819, 2821, 2823, + 2825, 2827, 2829, 2831, 2833, 2835, 2837, 2839, + 2841, 2858, 2860, 2862, 2864, 2866, 2885, 2887, + 2889, 2891, 2893, 2895, 2897, 2899, 2901, 2903, + 2905, 2907, 2909, 2911, 2913, 2915, 2917, 2919, + 2921, 2923, 2925, 2927, 2929, 2931, 2933, 2935, + 2937, 2939, 2941, 2943, 2945, 2947, 2949, 2951, + 2953, 2955, 2957, 2959, 2961, 2963, 2965, 2967, + 2969, 2971, 3025, 3027, 3029, 3031, 3033, 3035, + 3054, 3056, 3058, 3060, 3062, 3064, 3066, 3068, + 3070, 3072, 3074, 3076, 3078, 3080, 3082, 3084, + 3086, 3088, 3090, 3092, 3094, 3096, 3100, 3102, + 3104, 3106, 3108, 3110, 3112, 3114, 3116, 3118, + 3120, 3122, 3124, 3126, 3128, 3147, 3149, 3151, + 3153, 3155, 3157, 3159, 3161, 3163, 3166, 3168, + 3170, 3172, 3174, 3176, 3178, 3180, 3182, 3184, + 3186, 3188, 3190, 3192, 3242, 3244, 3246, 3248, + 3250, 3252, 3254, 3256, 3258, 3260, 3262, 3264, + 3266, 3268, 3270, 3288, 3290, 3292, 3294, 3296, + 3298, 3300, 3302, 3304, 3306, 3308, 3310, 3312, + 3367, 3373, 3375, 3377, 3379, 3381, 3383, 3385, + 3387, 3389, 3391, 3393, 3395, 3397, 3399, 3401, + 3405, 3449, 3462, 3464, 3466, 3468, 3470, 3472, + 3474, 3476, 3478, 3480, 3482, 3484, 3486, 3488, + 3490, 3492, 3494, 3496, 3498, 3517, 3519, 3521, + 3523, 3525, 3527, 3529, 3531, 3533, 3535, 3537, + 3552, 3554, 3556, 3558, 3560, 3562, 3564, 3566, + 3568, 3570, 3572, 3574, 3576, 3578, 3580, 3582, + 3586, 3588, 3590, 3592, 3594, 3614, 3632, 3634, + 3636, 3638, 3640, 3642, 3644, 3646, 3648, 3650, + 3654, 3656, 3658, 3660, 3662, 3664, 3666, 3668, + 3670, 3672, 3674, 3719, 3721, 3723, 3725, 3727, + 3729, 3731, 3733, 3735, 3737, 3739, 3761, 3763, + 3765, 3767, 3769, 3771, 3773, 3775, 3777, 3779, + 3781, 3783, 3785, 3787, 3831, 3833, 3835, 3837, + 3854, 3856, 3858, 3860, 3862, 3864, 3866, 3868, + 3870, 3872, 3874, 3876, 3878, 3880, 3882, 3884, + 3886, 3888, 3890, 3892, 3894, 3920, 3922, 3924, + 3926, 3928, 3930, 3932, 3934, 3936, 3938, 3953, + 3955, 4006, 4012, 4014, 4016, 4018, 4020, 4022, + 4024, 4026, 4028, 4030, 4032, 4034, 4036, 4038, + 4040, 4065, 4067, 4069, 4071, 4073, 4075, 4077, + 4079, 4081, 4083, 4085, 4087, 4089, 4091, 4093, + 4095, 4097, 4099, 4101, 4103, 4105, 4107, 4111, + 4113, 4115, 4117, 4119, 4121, 4123, 4125, 4127, + 4129, 4131, 4133, 4135, 4139, 4141, 4143, 4145, + 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, + 4163, 4165, 4167, 4176, 4178, 4180, 4182, 4184, + 4186, 4188, 4190, 4192, 4194, 4196, 4198, 4200, + 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, + 4239, 4241, 4243, 4245, 4260, 4262, 4291, 4293, + 4295, 4297, 4299, 4301, 4303, 4305, 4307, 4309, + 4311, 4313, 4315, 4317, 4319, 4321, 4323, 4325, + 4337, 4339, 4341, 4343, 4345, 4347, 4349, 4351, + 4353, 4355, 4375, 4377, 4379, 4381, 4383, 4385, + 4387, 4389, 4391, 4393, 4395, 4397, 4399, 4401, + 4403, 4405, 4407, 4409, 4411, 4413, 4415, 4417, + 4419, 4421, 4423, 4432, 4434, 4436, 4438, 4440, + 4442, 4444, 4446, 4448, 4457, 4459, 4461, 4463, + 4465, 4467, 4469, 4471, 4473, 4475, 4477, 4479, + 4481, 4483, 4485, 4487, 4489, 4491, 4493, 4495, + 4512, 4514, 4516, 4518, 4520, 4522, 4524, 4526, + 4528, 4530, 4532, 4534, 4536, 4538, 4540, 4542, + 4544, 4546, 4548, 4550, 4552, 4554, 4556, 4558, + 4560, 4562, 4564, 4566, 4568, 4570, 4572, 4574, + 4576, 4578, 4580, 4582, 4584, 4586, 4588, 4590, + 4592, 4594, 4596, 4598, 4600, 4602, 4604, 4606, + 4608, 4610, 4654, 4656, 4658, 4660, 4662, 4664, + 4666, 4668, 4670, 4672, 4674, 4676, 4678, 4680, + 4691, 4693, 4733, 4735, 4737, 4739, 4758, 4760, + 4762, 4764, 4766, 4768, 4770, 4772, 4774, 4776, + 4778, 4780, 4782, 4784, 4786, 4788, 4790, 4792, + 4794, 4796, 4798, 4800, 4802, 4804, 4806, 4808, + 4810, 4812, 4814, 4833, 4835, 4837, 4839, 4841, + 4843, 4845, 4847, 4849, 4851, 4853, 4855, 4857, + 4859, 4861, 4863, 4865, 4867, 4869, 4871, 4873, + 4875, 4877, 4879, 4881, 4883, 4885, 4887, 4889, + 4891, 4899, 4901, 4903, 4905, 4907, 4909, 4911, + 4913, 4915, 4917, 4919, 4921, 4923, 4925, 4927, + 4929, 4931, 4933, 4935, 4937, 4956, 4958, 4960, + 4962, 4964, 4966, 4968, 4970, 4972, 4994, 4996, + 4998, 5000, 5002, 5012, 5014, 5016, 5018, 5020, + 5022, 5024, 5026, 5028, 5030, 5032, 5034, 5036, + 5038, 5040, 5042, 5044, 5046, 5048, 5050, 5052, + 5054, 5056, 5058, 5060, 5062, 5064, 5066, 5068, + 5070, 5072, 5074, 5076, 5078, 5123, 5125, 5127, + 5129, 5131, 5133, 5135, 5137, 5139, 5165, 5167, + 5169, 5171, 5173, 5175, 5177, 5179, 5181, 5183, + 5201, 5203, 5205, 5207, 5209, 5211, 5222, 5224, + 5226, 5228, 5230, 5232, 5234, 5236, 5238, 5240, + 5242, 5244, 5246, 5248, 5261, 5263, 5265, 5267, + 5269, 5271, 5273, 5275, 5277, 5279, 5281, 5283, + 5285, 5287, 5289, 5291, 5293, 5295, 5297, 5299, + 5301, 5303, 5305, 5307, 5309, 5311, 5313, 5315, + 5317, 5319, 5326, 5328, 5330, 5332, 5334, 5336, + 5338, 5340, 5342, 5344, 5346, 5348, 5350, 5352, + 5354, 5356, 5358, 5360, 5362, 5364, 5366, 5368, + 5370, 5372, 5374, 5376, 5378, 5380, 5382, 5384, + 5436, 5438, 5440, 5442, 5444, 5446, 5448, 5450, + 5452, 5454, 5456, 5458, 5460, 5462, 5464, 5466, + 5468, 5470, 5472, 5474, 5476, 5478, 5507, 5515, + 5517, 5519, 5521, 5523, 5525, 5527, 5529, 5531, + 5533, 5535, 5537, 5539, 5541, 5543, 5545, 5547, + 5549, 5551, 5553, 5555, 5557, 5559, 5561, 5563, + 5565, 5567, 5569, 5571, 5573, 5575, 5589, 5591, + 5593, 5595, 5597, 5599, 5601, 5603, 5605, 5607, + 5634, 5636, 5638, 5640, 5642, 5644, 5646, 5648, + 5650, 5652, 5654, 5656, 5658, 5660, 5662, 5664, + 5666, 5693, 5695, 5697, 5699, 5701, 5703, 5705, + 5707, 5709, 5711, 5713, 5715, 5717, 5719, 5721, + 5723, 5725, 5727, 5729, 5731, 5733, 5735, 5737, + 5739, 5741, 5743, 5745, 5747, 5749, 5751, 5753, + 5755, 5757, 5759, 5761, 5763, 5765, 5767, 5769, + 5771, 5773, 5775, 5777, 5779, 5782, 5784, 5786, + 5788, 5790, 5792, 5794, 5796, 5798, 5800, 5802, + 5804, 5806, 5808, 5810, 5825, 5827, 5829, 5831, + 5833, 5835, 5837, 5839, 5841, 5843, 5855, 5857, + 5859, 5861, 5863, 5865, 5867, 5869, 5871, 5873, + 5900, 5902, 5904, 5906, 5908, 5910, 5912, 5914, + 5916, 5918, 5920, 5922, 5924, 5926, 5928, 5930, + 5932, 5934, 5936, 5938, 5940, 5942, 5944, 5946, + 5948, 5950, 5952, 5954, 5956, 5958, 5960, 5962, + 5964, 5966, 5968, 5970, 5972, 5979, 5981, 5983, + 5985, 5987, 5989, 5991, 5993, 5995, 5997, 5999, + 6001, 6003, 6005, 6007, 6009, 6011, 6013, 6015, + 6017, 6019, 6021, 6023, 6025, 6027, 6029, 6031, + 6033, 6035, 6037, 6063, 6065, 6067, 6069, 6071, + 6073, 6075, 6077, 6079, 6081, 6083, 6085, 6087, + 6089, 6091, 6093, 6099, 6101, 6103, 6105, 6107, + 6109, 6111, 6113, 6115, 6117, 6119, 6121, 6123, + 6125, 6127, 6129, 6131, 6133, 6135, 6137, 6139, + 6141, 6143, 6145, 6147, 6159, 6161, 6163, 6165, + 6167, 6169, 6171, 6173, 6175, 6181, 6183, 6185, + 6187, 6189, 6191, 6193, 6209, 6211, 6213, 6215, + 6227, 6229, 6231, 6233, 6235, 6237, 6239, 6241, + 6243, 6245, 6247, 6259, 6261, 6263, 6265, 6267, + 6269, 6285, 6287, 6289, 6291, 6303, 6305, 6307, + 6309, 6311, 6313, 6315, 6317, 6319, 6321, 6323, + 6350, 6352, 6354, 6356, 6358, 6360, 6362, 6364, + 6366, 6368, 6370, 6372, 6374, 6376, 6378, 6380, + 6382, 6384, 6386, 6388, 6390, 6392, 6394, 6396, + 6398, 6400, 6412, 6414, 6416, 6418, 6420, 6422, + 6424, 6426, 6428, 6430, 6457, 6459, 6461, 6463, + 6465, 6467, 6469, 6471, 6473, 6475, 6477, 6479, + 6481, 6483, 6485, 6487, 6489, 6491, 6493, 6495, + 6497, 6499, 6501, 6503, 6505, 6507, 6509, 6511, + 6513, 6515, 6517, 6519, 6521, 6523, 6525, 6527, + 6529, 6531, 6533, 6584, 6586, 6588, 6590, 6592, + 6594, 6596, 6598, 6600, 6618, 6620, 6622, 6624, + 6626, 6628, 6630, 6632, 6634, 6636, 6638, 6640, + 6642, 6644, 6646, 6656, 6658, 6660, 6662, 6664, + 6666, 6668, 6670, 6672, 6674, 6676, 6678, 6680, + 6682, 6684, 6686, 6688, 6690, 6692, 6694, 6696, + 6698, 6713, 6715, 6717, 6719, 6721, 6723, 6725, + 6727, 6729, 6731, 6733, 6735, 6737, 6739, 6741, + 6743, 6745, 6747, 6758, 6760, 6762, 6764, 6766, + 6768, 6770, 6773, 6775, 6777, 6779, 6781, 6783, + 6785, 6787, 6789, 6791, 6807, 6826, 6828, 6830, + 6832, 6834, 6842, 6844, 6846, 6848, 6850, 6852, + 6854, 6856, 6858, 6860, 6862, 6864, 6866, 6868, + 6870, 6872, 6892, 6894, 6896, 6898, 6900, 6902, + 6904, 6906, 6908, 6910, 6912, 6914, 6916, 6918, + 6920, 6922, 6924, 6926, 6928, 6930, 6932, 6934, + 6936, 6945, 6947, 6949, 6951, 6953, 6955, 6957, + 6959, 6961, 6963, 6965, 6967, 6969, 6971, 7025, + 7027, 7029, 7031, 7033, 7035, 7062, 7064, 7066, + 7068, 7070, 7072, 7074, 7076, 7078, 7080, 7082, + 7084, 7086, 7088, 7090, 7092, 7094, 7096, 7098, + 7100, 7102, 7104, 7106, 7108, 7122, 7124, 7126, + 7128, 7130, 7132, 7134, 7136, 7138, 7140, 7142, + 7182, 7184, 7186, 7194, 7196, 7198, 7200, 7232, + 7234, 7236, 7238, 7240, 7242, 7244, 7246, 7248, + 7250, 7252, 7305, 7307, 7309, 7311, 7313, 7315, + 7332, 7334, 7336, 7338, 7340, 7342, 7344, 7346, + 7405, 7407, 7409, 7435, 7437, 7439, 7441, 7443, + 7445, 7447, 7449, 7451, 7453, 7514, 7516, 7518, + 7520, 7522, 7540, 7547, 7549, 7551, 7553, 7555, + 7557, 7559, 7561, 7563, 7565, 7567, 7569, 7571, + 7573, 7575, 7577, 7579, 7581, 7583, 7585, 7587, + 7589, 7591, 7593, 7595, 7597, 7599, 7601, 7603, + 7605, 7607, 7609, 7611, 7613, 7615, 7617, 7619, + 7653, 7659, 7661, 7663, 7665, 7667, 7669, 7671, + 7673, 7675, 7677, 7679, 7681, 7683, 7685, 7687, + 7706, 7708, 7710, 7712, 7714, 7716, 7718, 7720, + 7722, 7724, 7726, 7728, 7730, 7732, 7734, 7736, + 7738, 7740, 7742, 7744, 7746, 7750, 7752, 7754, + 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, + 7772, 7774, 7778, 7780, 7782, 7784, 7786, 7788, + 7790, 7792, 7794, 7796, 7798, 7800, 7802, 7804, + 7806, 7815, 7817, 7819, 7821, 7823, 7825, 7827, + 7829, 7831, 7846, 7848, 7877, 7879, 7881, 7883, + 7885, 7887, 7889, 7891, 7893, 7895, 7897, 7899, + 7901, 7903, 7905, 7907, 7909, 7911, 7923, 7925, + 7927, 7929, 7931, 7933, 7935, 7937, 7939, 7941, + 7961, 7963, 7965, 7967, 7969, 7971, 7973, 7975, + 7977, 7979, 7981, 7983, 7985, 7987, 7989, 7991, + 7993, 7995, 7997, 7999, 8001, 8003, 8005, 8007, + 8009, 8018, 8020, 8022, 8024, 8026, 8028, 8030, + 8032, 8034, 8043, 8045, 8047, 8049, 8051, 8053, + 8055, 8057, 8059, 8066, 8068, 8070, 8072, 8074, + 8076, 8078, 8080, 8082, 8084, 8086, 8088, 8090, + 8092, 8094, 8096, 8098, 8100, 8102, 8104, 8106, + 8108, 8110, 8117, 8119, 8121, 8123, 8125, 8127, + 8129, 8131, 8133, 8135, 8137, 8139, 8141, 8143, + 8190, 8224, 8226, 8228, 8230, 8232, 8234, 8236, + 8238, 8240, 8242, 8244, 8246, 8248, 8250, 8252, + 8254, 8256, 8320, 8322, 8324, 8326, 8328, 8330, + 8332, 8334, 8336, 8338, 8340, 8342, 8344, 8346, + 8348, 8350, 8352, 8354, 8373, 8375, 8377, 8379, + 8381, 8383, 8385, 8387, 8389, 8391, 8393, 8395, + 8397, 8399, 8401, 8403, 8405, 8407, 8409, 8411, + 8413, 8415, 8417, 8419, 8421, 8423, 8425, 8427, + 8429, 8431, 8433, 8435, 8437, 8439, 8441, 8443, + 8445, 8447, 8449, 8451, 8453, 8455, 8457, 8459, + 8461, 8463, 8465, 8467, 8469, 8471, 8473, 8475, + 8477, 8482, 8484, 8486, 8488, 8490, 8492, 8520, + 8522, 8524, 8526, 8528, 8530, 8532, 8534, 8536, + 8538, 8540, 8542, 8544, 8546, 8562, 8564, 8566, + 8568, 8580, 8582, 8584, 8586, 8588, 8590, 8592, + 8594, 8596, 8598, 8600, 8612, 8614, 8616, 8618, + 8620, 8622, 8624, 8626, 8628, 8630, 8632, 8634, + 8636, 8638, 8640, 8642, 8644, 8660, 8718, 8720, + 8722, 8734, 8736, 8738, 8740, 8742, 8744, 8751, + 8753, 8755, 8757, 8759, 8786, 8788, 8790, 8792, + 8794, 8796, 8798, 8800, 8802, 8804, 8806, 8808, + 8810, 8812, 8814, 8816, 8818, 8820, 8822, 8824, + 8826, 8828, 8830, 8832, 8834, 8836, 8838, 8896, + 8898, 8900, 8902, 8904, 8916, 8918, 8920, 8922, + 8924, 8926, 8928, 8930, 8932, 8977, 8979, 8981, + 8983, 8985, 8987, 8989, 8991, 9020, 9022, 9024, + 9026, 9028, 9030, 9051, 9053, 9055, 9081, 9083, + 9085, 9087, 9089, 9091, 9093, 9095, 9097, 9099, + 9101, 9103, 9109, 9113, 9115, 9117, 9119, 9121, + 9123, 9125, 9127, 9129, 9142, 9144, 9146, 9148, + 9150, 9152, 9154, 9156, 9158, 9160, 9162, 9164, + 9166, 9168, 9170, 9172, 9174, 9201, 9203, 9205, + 9207, 9209, 9211, 9213, 9215, 9217, 9219, 9221, + 9223, 9225, 9227, 9229, 9231, 9233, 9235, 9237, + 9239, 9241, 9243, 9245, 9247, 9249, 9251, 9253, + 9255, 9257, 9259, 9261, 9280, 9282, 9284, 9286, + 9288, 9290, 9292, 9314, 9331, 9333, 9335, 9337, + 9339, 9393, 9395, 9397, 9399, 9401, 9403, 9407, + 9409, 9411, 9413, 9415, 9417, 9435, 9437, 9439, + 9441, 9443, 9445, 9447, 9449, 9451, 9453, 9455, + 9457, 9459, 9461, 9463, 9465, 9467, 9469, 9471, + 9478, 9480, 9482, 9498, 9517, 9519, 9521, 9523, + 9525, 9533, 9535, 9537, 9539, 9541, 9543, 9545, + 9547, 9549, 9551, 9553, 9555, 9557, 9559, 9561, + 9563, 9565, 9567, 9590, 9592, 9594, 9596, 9598, + 9609, 9611, 9613, 9615, 9617, 9619, 9671, 9673, + 9675, 9677, 9679, 9690, 9692, 9694, 9696, 9698, + 9700, 9702, 9704, 9706, 9708, 9710, 9712, 9714, + 9716, 9718, 9720, 9722, 9724, 9726, 9728, 9730, + 9732, 9734, 9736, 9738, 9740, 9742, 9744, 9746, + 9748, 9750, 9752, 9754, 9756, 9758, 9766, 9768, + 9770, 9772, 9774, 9776, 9778, 9780, 9782, 9784, + 9786, 9788, 9790, 9792, 9794, 9796, 9798, 9800, + 9802, 9804, 9806, 9808, 9816, 9818, 9820, 9822, + 9824, 9826, 9828, 9830, 9832, 9834, 9836, 9838, + 9840, 9842, 9844, 9846, 9848, 9850, 9852, 9854, + 9856, 9907, 9909, 9911, 9913, 9915, 9917, 9919, + 9921, 9923, 9925, 9927, 9929, 9931, 9933, 9935, + 9937, 9939, 9941, 9993, 9995, 9997, 9999, 10001, + 10003, 10005, 10007, 10009, 10011, 10013, 10015, 10017, + 10068, 10070, 10085, 10087, 10112, 10114, 10116, 10118, + 10166, 10168, 10170, 10172, 10192, 10194, 10196, 10198, + 10200, 10202, 10204, 10206, 10208, 10210, 10212, 10214, + 10216, 10218, 10220, 10222, 10224, 10226, 10228, 10230, + 10232, 10234, 10236, 10238, 10240, 10242, 10244, 10246, + 10248, 10250, 10252, 10254, 10256, 10258, 10260, 10262, + 10264, 10266, 10268, 10270, 10272, 10274, 10276, 10278, + 10280, 10298, 10300, 10302, 10304, 10306, 10308, 10310, + 10312, 10314, 10316, 10318, 10320, 10322, 10324, 10326, + 10328, 10330, 10345, 10347, 10349, 10351, 10353, 10355, + 10357, 10359, 10361, 10363, 10417, 10419, 10421, 10423, + 10425, 10427, 10429, 10431, 10433, 10435, 10437, 10439, + 10441, 10443, 10461, 10463, 10465, 10467, 10469, 10471, + 10473, 10475, 10477, 10479, 10481, 10483, 10485, 10487, + 10489, 10491, 10536, 10538, 10540, 10542, 10544, 10546, + 10548, 10550, 10552, 10578, 10580, 10582, 10584, 10586, + 10588, 10590, 10592, 10594, 10598, 10600, 10602, 10604, + 10606, 10608, 10610, 10612, 10614, 10616, 10618, 10620, + 10622, 10624, 10626, 10628, 10630, 10632, 10634, 10636, + 10638, 10640, 10642, 10666, 10668, 10670, 10672, 10674, + 10676, 10678, 10680, 10682, 10684, 10748, 10750, 10752, + 10754, 10756, 10758, 10760, 10762, 10764, 10766, 10768, + 10825, 10827, 10829, 10831, 10833, 10835, 10848, 10860, + 10862, 10864, 10866, 10868, 10870, 10872, 10874, 10876, + 10878, 10895, 10906, 10908, 10910, 10912, 10914, 10919, + 10980, 10982, 10984, 10986, 10988, 10990, 10992, 10994, + 10996, 10998, 11000, 11065, 11067, 11069, 11071, 11073, + 11075, 11115, 11124, 11126, 11128, 11130, 11132, 11134, + 11136, 11138, 11140, 11142, 11203, 11205, 11248, 11250, + 11256, 11258, 11260, 11262, 11264, 11266, 11268, 11270, + 11281, 11283, 11285, 11287, 11289, 11291, 11346, 11348, + 11350, 11352, 11354, 11356, 11358, 11360, 11362, 11364, + 11366, 11368, 11370, 11372, 11416, 11418, 11420, 11422, + 11424, 11426, 11450, 11452, 11454, 11456, 11458, 11460, + 11504, 11506, 11508, 11510, 11512, 11514, 11516, 11518, + 11520, 11528, 11530, 11532, 11534, 11536, 11538, 11540, + 11542, 11544, 11546, 11587, 11589, 11591, 11593, 11610, + 11612, 11630, 11632, 11634, 11636, 11638, 11640, 11642, + 11644, 11646, 11648, 11650, 11652, 11654, 11656, 11658, + 11660, 11662, 11664, 11666, 11710, 11712, 11714, 11717, + 11719, 11721, 11723, 11725, 11727, 11773, 11775, 11777, + 11779, 11781, 11840, 11842, 11844, 11846, 11848, 11860, + 11862, 11864, 11866, 11868, 11870, 11872, 11874, 11876, + 11895, 11897, 11899, 11901, 11945, 11947, 11949, 11951, + 11953, 11955, 11957, 11959, 11961, 11963, 11965, 11967, + 11969, 11971, 11995, 11997, 11999, 12001, 12003, 12005, + 12007, 12009, 12011, 12013, 12035, 12057, 12059, 12061, + 12063, 12065, 12067, 12069, 12071, 12089, 12091, 12093, + 12095, 12097, 12099, 12101, 12103, 12105, 12107, 12109, + 12111, 12113, 12118, 12120, 12122, 12124, 12126, 12128, + 12130, 12132, 12134, 12136, 12138, 12140, 12142, 12144, + 12146, 12165, 12167, 12169, 12171, 12173, 12175, 12177, + 12179, 12181, 12183, 12185, 12187, 12189, 12191, 12193, + 12195, 12197, 12199, 12201, 12203, 12205, 12207, 12209, + 12211, 12213, 12229, 12242, 12244, 12254, 12256, 12258, + 12260, 12262, 12264, 12266, 12268, 12270, 12272, 12274, + 12276, 12278, 12280, 12282, 12284, 12286, 12288, 12290, + 12292, 12294, 12351, 12353, 12355, 12357, 12359, 12361, + 12363, 12365, 12367, 12369, 12371, 12373, 12375, 12377, + 12379, 12381, 12385, 12389, 12391, 12393, 12395, 12397, + 12399, 12401, 12403, 12415, 12471, 12473, 12475, 12477, + 12479, 12481, 12483, 12493, 12495, 12497, 12556, 12558, + 12560, 12562, 12564, 12566, 12568, 12570, 12622, 12662, + 12664, 12666, 12668, 12670, 12730, 12732, 12734, 12736, + 12738, 12778, 12780, 12782, 12784, 12786, 12843, 12845, + 12847, 12849, 12851, 12853, 12855, 12857, 12859, 12861, + 12901, 12903, 12905, 12907, 12909, 12969, 12971, 12973, + 12975, 12977, 12979, 12981, 12983, 12985, 12987, 12989, + 12991, 12993, 12995, 12997, 12999, 13001, 13003, 13005, + 13045, 13047, 13049, 13051, 13053, 13110, 13112, 13114, + 13116, 13118, 13120, 13122, 13124, 13126, 13128, 13130, + 13132, 13151, 13153, 13155, 13157, 13159, 13161, 13163, + 13177, 13179, 13181, 13183, 13185, 13187, 13189, 13233, + 13235, 13237, 13284, 13286, 13288, 13290, 13292, 13294, + 13297, 13299, 13343, 13345, 13347, 13349, 13393, 13395, + 13451, 13453, 13479, 13496, 13498, 13500, 13502, 13504, + 13562, 13564, 13566, 13568, 13570, 13572, 13574, 13576, + 13578, 13600, 13602, 13604, 13606, 13608, 13610, 13612, + 13614, 13616, 13628, 13630, 13632, 13634, 13636, 13658, + 13662, 13664, 13666, 13668, 13670, 13672, 13674, 13676, + 13678, 13680, 13682, 13684, 13686, 13688, 13746, 13748, + 13750, 13752, 13754, 13756, 13768, 13770, 13772, 13774, + 13776, 13778, 13780, 13782, 13784, 13786, 13788, 13790, + 13792, 13794, 13796, 13798, 13806, 13808, 13810, 13812, + 13814, 13866, 13868, 13870, 13872, 13874, 13876, 13878, + 13936, 13938, 13989, 13991, 13993, 13995, 14000, 14002, + 14004, 14006, 14008, 14016, 14018, 14020, 14022, 14024, + 14026, 14028, 14030, 14032, 14034, 14054, 14056, 14058, + 14060, 14062, 14064, 14066, 14068, 14070, 14072, 14074, + 14076, 14078, 14080, 14082, 14084, 14086, 14088, 14090, + 14092, 14094, 14096, 14098, 14100, 14102, 14104, 14106, + 14108, 14110, 14112, 14172, 14174, 14176, 14178, 14184, + 14186, 14188, 14232, 14288, 14290, 14295, 14297, 14356, + 14358, 14409, 14411, 14413, 14415, 14428, 14430, 14432, + 14434, 14436, 14438, 14440, 14442, 14446, 14489, 14491, + 14493, 14495, 14497, 14499, 14501, 14522, 14524, 14526, + 14528, 14530, 14532, 14534, 14550, 14552, 14554, 14556, + 14558, 14560, 14562, 14582, 14584, 14586, 14602, 14646, + 14648, 14692, 14694, 14696, 14698, 14700, 14702, 14723, + 14725, 14727, 14729, 14737, 14739, 14741, 14746, 14748, + 14750, 14752, 14754, 14756, 14758, 14760, 14815, 14817, + 14875, 14877, 14879, 14881, 14883, 14885, 14907, 14909, + 14911, 14913, 14915, 14917, 14919, 14921, 14923, 14925, + 14927, 14950, 14952, 14954, 15006, 15008, 15010, 15030, + 15032, 15037, 15039, 15041, 15043, 15045, 15047, 15049, + 15051, 15053, 15055, 15057, 15059, 15061, 15063, 15065, + 15067, 15069, 15071, 15073, 15075, 15077, 15079, 15081, + 15083, 15085, 15093, 15095, 15097, 15099, 15101, 15103, + 15105, 15107, 15109, 15111, 15113, 15115, 15117, 15119, + 15121, 15123, 15131, 15133, 15135, 15137, 15139, 15141, + 15143, 15145, 15147, 15149, 15151, 15153, 15155, 15157, + 15159, 15218, 15220, 15222, 15224, 15226, 15228, 15230, + 15244, 15246, 15248, 15250, 15252, 15254, 15256, 15258, + 15260, 15262, 15264, 15266, 15327, 15329, 15332, 15334, + 15336, 15338, 15340, 15342, 15344, 15346, 15348, 15374, + 15376, 15378, 15380, 15382, 15384, 15438, 15451, 15453, + 15455, 15457, 15459, 15461, 15463, 15465, 15467, 15469, + 15471, 15473, 15481, 15483, 15485, 15487, 15489, 15491, + 15493, 15495, 15497, 15508, 15510, 15512, 15514, 15516, + 15518, 15520, 15522, 15530, 15532, 15534, 15557, 15559, + 15617, 15619, 15621, 15679, 15681, 15683, 15685, 15687, + 15689, 15691, 15693, 15695, 15697, 15699, 15701, 15703, + 15705, 15707, 15761, 15763, 15765, 15767, 15769, 15771, + 15773, 15775, 15777, 15779, 15781, 15783, 15785, 15787, + 15789, 15791, 15793, 15798, 15800, 15802, 15804, 15806, + 15808, 15810, 15823, 15825, 15827, 15829, 15831, 15833, + 15835, 15893, 15895, 15938, 15940, 15942, 15944, 15946, + 15948, 15950, 15952, 15954, 15956, 15958, 15960, 15962, + 15964, 15966, 15968, 15970, 15972, 15974, 15976, 15978, + 15980, 15982, 15984, 15986, 15988, 15990, 15992, 15994, + 15996, 15998, 16000, 16009, 16011, 16013, 16015, 16017, + 16019, 16021, 16023, 16025, 16027, 16029, 16031, 16033, + 16035, 16037, 16039, 16041, 16043, 16045, 16047, 16049, + 16051, 16059, 16061, 16063, 16065, 16067, 16069, 16071, + 16073, 16075, 16077, 16080, 16082, 16084, 16086, 16088, + 16090, 16092, 16097, 16099, 16101, 16103, 16105, 16107, + 16109, 16128, 16137, 16139, 16141, 16143, 16145, 16147, + 16149, 16151, 16153, 16169, 16171, 16173, 16175, 16177, + 16222, 16224, 16233, 16235, 16237, 16239, 16241, 16243, + 16245, 16247, 16249, 16251, 16253, 16255, 16257, 16265, + 16267, 16269, 16271, 16273, 16275, 16277, 16279, 16281, + 16335, 16380, 16382, 16384, 16386, 16388, 16390, 16408, + 16410, 16412, 16414, 16416, 16418, 16420, 16422, 16448, + 16450, 16452, 16454, 16456, 16458, 16500, 16502, 16504, + 16506, 16508, 16510, 16512, 16514, 16516, 16518, 16566, + 16568, 16570, 16572, 16574, 16632, 16634, 16636, 16638, + 16681, 16683, 16685, 16687, 16745, 16747, 16749, 16751, + 16753, 16755, 16757, 16759, 16802, 16804, 16806, 16808, + 16828, 16830, 16832, 16834, 16836, 16838, 16899, 16901, + 16903, 16905, 16907, 16909, 16921, 16924, 16926, 16928, + 16942, 16944, 16946, 16948, 16959, 16961, 16963, 16965, + 16967, 16969, 16989, 16991, 17049, 17051, 17053, 17055, + 17057, 17059, 17061, 17122, 17124, 17126, 17128, 17130, + 17151, 17159, 17161, 17163, 17165, 17167, 17169, 17171, + 17173, 17178, 17180, 17182, 17184, 17186, 17188, 17195, + 17197, 17199, 17201, 17203, 17205, 17207, 17209, 17219, + 17221, 17223, 17225, 17227, 17229, 17231, 17233, 17244, + 17246, 17248, 17250, 17252, 17254, 17256, 17258, 17260, + 17291, 17293, 17295, 17297, 17299, 17301, 17303, 17311, + 17313, 17315, 17317, 17319, 17321, 17323, 17325, 17334, + 17336, 17343, 17345, 17347, 17349, 17364, 17366, 17368, + 17370, 17372, 17374, 17386, 17388, 17390, 17392, 17394, + 17396, 17398, 17400, 17402, 17404, 17406, 17408, 17410, + 17412, 17414, 17416, 17418, 17420, 17440, 17442, 17444, + 17446, 17448, 17450, 17452, 17454, 17456, 17458, 17460, + 17462, 17464, 17466, 17468, 17470, 17472, 17474, 17476, + 17478, 17489, 17491, 17493, 17495, 17497, 17502, 17504, + 17506, 17508, 17510, 17512, 17514, 17516, 17518, 17520, + 17522, 17524, 17526, 17528, 17530, 17551, 17553, 17555, + 17557, 17559, 17561, 17563, 17565, 17567, 17569, 17571, + 17573, 17577, 17579, 17581, 17593, 17595, 17597, 17599, + 17660, 17662, 17664, 17666, 17668, 17670, 17672, 17674, + 17676, 17692, 17710, 17718, 17726, 17728, 17730, 17732, + 17734, 17738, 17740, 17742, 17748, 17750, 17752, 17754, + 17756, 17760, 17762, 17764, 17766, 17768, 17770, 17772, + 17774, 17776, 17778, 17780, 17782, 17784, 17835, 17886, + 17888, 17903, 17905, 17907, 17909, 17911, 17913, 17915, + 17958, 17960, 17962, 17964, 17966, 17968, 17970, 17972, + 17990, 17992, 17994, 17996, 17998, 18000, 18002, 18004, + 18062, 18064, 18122, 18124, 18126, 18128, 18130, 18132, + 18134, 18185, 18187, 18189, 18191, 18193, 18247, 18298, + 18300, 18344, 18346, 18348, 18350, 18352, 18398, 18400, + 18402, 18404, 18406, 18408, 18410, 18412, 18414, 18463, + 18465, 18467, 18469, 18517, 18519, 18521, 18576, 18578, + 18580, 18582, 18584, 18640, 18696, 18698, 18700, 18702, + 18704, 18706, 18708, 18710, 18712, 18714, 18716, 18718, + 18726, 18728, 18730, 18732, 18783, 18785, 18787, 18795, + 18797, 18799, 18801, 18803, 18805, 18807, 18809, 18811, + 18813, 18815, 18817, 18819, 18821, 18823, 18825, 18845, + 18849, 18851, 18853, 18855, 18857, 18859, 18861, 18863, + 18865, 18867, 18869, 18871, 18878, 18880, 18882, 18884, + 18886, 18888, 18890, 18892, 18894, 18896, 18898, 18900, + 18902, 18904, 18906, 18908, 18910, 18921, 18923, 18925, + 18927, 18929, 18931, 18933, 18935, 18937, 18939, 18997, + 18999, 19001, 19003, 19014, 19016, 19018, 19020, 19022, + 19024, 19026, 19028, 19030, 19032, 19034, 19050, 19052, + 19054, 19056, 19118, 19120, 19122, 19124, 19126, 19128, + 19130, 19132, 19134, 19136, 19138, 19140, 19159, 19161, + 19163, 19165, 19225, 19227, 19229, 19231, 19233, 19235, + 19237, 19239, 19241, 19243, 19245, 19247, 19249, 19251, + 19253, 19273, 19275, 19277, 19279, 19281, 19283, 19285, + 19287, 19289, 19291, 19293, 19312, 19314, 19316, 19318, + 19320, 19322, 19324, 19326, 19328, 19336, 19338, 19340, + 19342, 19344, 19346, 19348, 19350, 19352, 19354, 19356, + 19358, 19360, 19362, 19364, 19366, 19368, 19370, 19372, + 19374, 19376, 19378, 19380, 19382, 19384, 19386, 19405, + 19407, 19409, 19411, 19413, 19415, 19417, 19419, 19421, + 19423, 19425, 19441, 19443, 19445, 19447, 19449, 19451, + 19453, 19455, 19457, 19479, 19481, 19483, 19485, 19487, + 19551, 19553, 19555, 19557, 19580, 19582, 19584, 19586, + 19588, 19602, 19604, 19606, 19608, 19610, 19612, 19614, + 19668, 19675, 19677, 19679, 19681, 19683, 19685, 19687, + 19689, 19691, 19693, 19695, 19697, 19699, 19701, 19703, + 19705, 19707, 19724, 19743, 19745, 19747, 19760, 19762, + 19764, 19766, 19768, 19770, 19772, 19774, 19776, 19778, + 19780, 19782, 19784, 19786, 19788, 19790, 19792, 19851, + 19853, 19855, 19857, 19859, 19861, 19863, 19922, 19924, + 19926, 19928, 19930, 19932, 19934, 19936, 19991, 19993, + 19995, 19997, 20010, 20012, 20014, 20016, 20018, 20020, + 20022, 20024, 20026, 20028, 20030, 20032, 20034, 20036, + 20038, 20040, 20042, 20044, 20063, 20065, 20067, 20069, + 20071, 20073, 20075, 20077, 20079, 20081, 20083, 20085, + 20087, 20089, 20091, 20093, 20095, 20097, 20105, 20107, + 20109, 20111, 20172, 20174, 20176, 20178, 20180, 20241, + 20243, 20245, 20293, 20295, 20297, 20299, 20301, 20305, + 20307, 20309, 20311, 20313, 20333, 20351, 20353, 20355, + 20357, 20359, 20361, 20363, 20365, 20367, 20369, 20371, + 20373, 20375, 20377, 20381, 20383, 20385, 20387, 20389, + 20391, 20393, 20395, 20397, 20399, 20401, 20421, 20423, + 20425, 20427, 20488, 20490, 20512, 20514, 20516, 20518, + 20520, 20522, 20524, 20526, 20528, 20530, 20532, 20534, + 20536, 20538, 20540, 20542, 20544, 20546, 20548, 20550, + 20552, 20554, 20556, 20558, 20560, 20615, 20636, 20638, + 20640, 20642, 20644, 20646, 20648, 20650, 20652, 20654, + 20656, 20658, 20660, 20662, 20708, 20710, 20712, 20714, + 20716, 20735, 20737, 20739, 20741, 20743, 20745, 20747, + 20749, 20751, 20753, 20755, 20757, 20759, 20761, 20763, + 20765, 20767, 20769, 20771, 20773, 20824, 20826, 20828, + 20830, 20832, 20834, 20836, 20838, 20897, 20942, 20944, + 20946, 20948, 20950, 20952, 20954, 20956, 20958, 20960, + 20962, 20964, 20966, 20968, 20970, 20972, 21016, 21018, + 21020, 21022, 21080, 21082, 21101, 21103, 21105, 21107, + 21109, 21111, 21113, 21125, 21127, 21135, 21137, 21139, + 21155, 21157, 21159, 21178, 21180, 21182, 21208, 21210, + 21212, 21214, 21216, 21223, 21225, 21227, 21229, 21231, + 21233, 21235, 21237, 21239, 21257, 21259, 21261, 21263, + 21265, 21322, 21324, 21343, 21345, 21347, 21349, 21351, + 21353, 21355, 21357, 21359, 21361, 21363, 21365, 21423, + 21425, 21446, 21448, 21450, 21452, 21454, 21513, 21515, + 21517, 21519, 21521, 21523, 21525, 21527, 21529, 21531, + 21533, 21552, 21554, 21556, 21558, 21560, 21562, 21564, + 21566, 21568, 21570, 21572, 21574, 21576, 21578, 21580, + 21582, 21584, 21586, 21588, 21590, 21592, 21612, 21614, + 21616, 21618, 21620, 21678, 21680, 21682, 21684, 21686, + 21688, 21690, 21692, 21694, 21696, 21698, 21700, 21702, + 21704, 21706, 21708, 21710, 21712, 21714, 21716, 21718, + 21720, 21722, 21724, 21726, 21728, 21730, 21732, 21734, + 21736, 21738, 21796, 21798, 21800, 21802, 21804, 21806, + 21808, 21866, 21868, 21870, 21872, 21874, 21928, 21985, + 21987, 22031, 22033, 22035, 22055, 22057, 22059, 22061, + 22063, 22065, 22067, 22069, 22071, 22073, 22075, 22087, + 22089, 22091, 22093, 22095, 22097, 22099, 22101, 22103, + 22105, 22107, 22109, 22111, 22113, 22124, 22126, 22128, + 22130, 22132, 22134, 22136, 22138, 22140, 22142, 22154, + 22156, 22159, 22161, 22180, 22182, 22233, 22235, 22237, + 22239, 22241, 22243, 22245, 22247, 22306, 22308, 22310, + 22312, 22314, 22316, 22318, 22320, 22322, 22324, 22326, + 22328, 22330, 22332, 22334, 22336, 22338, 22346, 22348, + 22350, 22352, 22354, 22356, 22358, 22360, 22400, 22402, + 22404, 22406, 22408, 22456, 22458, 22460, 22515, 22517, + 22519, 22521, 22523, 22579, 22635, 22637, 22639, 22641, + 22643, 22670, 22676, 22678, 22680, 22682, 22684, 22686, + 22688, 22690, 22692, 22700, 22702, 22704, 22706, 22725, + 22727, 22729, 22731, 22733, 22735, 22737, 22739, 22741, + 22743, 22745, 22747, 22749, 22751, 22753, 22755, 22757, + 22759, 22761, 22763, 22765, 22767, 22769, 22771, 22773, + 22775, 22777, 22779, 22781, 22783, 22785, 22787, 22789, + 22791, 22793, 22795, 22797, 22799, 22807, 22809, 22811, + 22813, 22815, 22817, 22819, 22821, 22823, 22825, 22838, + 22840, 22842, 22844, 22846, 22848, 22850, 22852, 22854, + 22856, 22858, 22860, 22863, 22865, 22867, 22869, 22871, + 22873, 22875, 22920, 22922, 22924, 22926, 22928, 22930, + 22932, 22934, 22985, 22987, 22989, 23010, 23012, 23014, + 23016, 23018, 23020, 23022, 23024, 23026, 23028, 23030, + 23032, 23075, 23077, 23079, 23081, 23083, 23085, 23106, + 23108, 23110, 23112, 23114, 23116, 23118, 23120, 23122, + 23168, 23170, 23172, 23193, 23195, 23197, 23254, 23256, + 23258, 23260, 23262, 23264, 23272, 23274, 23276, 23278, + 23280, 23282, 23284, 23286, 23288, 23290, 23292, 23294, + 23296, 23298, 23300, 23302, 23304, 23306, 23308, 23310, + 23312, 23314, 23316, 23318, 23345, 23347, 23349, 23351, + 23396, 23398, 23400, 23402, 23421, 23423, 23425, 23427, + 23429, 23431, 23433, 23435, 23437, 23439, 23450, 23452, + 23454, 23456, 23458, 23460, 23462, 23464, 23466, 23468, + 23519, 23521, 23523, 23525, 23527, 23544, 23546, 23563, + 23565, 23609, 23611, 23613, 23615, 23673, 23675, 23677, + 23737, 23739, 23741, 23743, 23745, 23747, 23749, 23751, + 23753, 23755, 23757, 23759, 23761, 23763, 23765, 23777, + 23779, 23781, 23783, 23785, 23787, 23789, 23791, 23793, + 23795, 23797, 23799, 23801, 23803, 23805, 23807, 23809, + 23811, 23813, 23815, 23817, 23819, 23821, 23823, 23825, + 23827, 23840, 23842, 23844, 23887, 23889, 23891, 23893, + 23895, 23897, 23899, 23901, 23903, 23905, 23907, 23950, + 23952, 24012, 24014, 24017, 24019, 24021, 24023, 24025, + 24027, 24029, 24031, 24033, 24035, 24049, 24051, 24053, + 24055, 24057, 24059, 24061, 24063, 24082, 24084, 24086, + 24088, 24090, 24092, 24094, 24146, 24148, 24150, 24152, + 24154, 24156, 24158, 24160, 24162, 24164, 24214, 24229, + 24231, 24292, 24294, 24311, 24313, 24315, 24334, 24336, + 24338, 24340, 24342, 24344, 24346, 24348, 24350, 24352, + 24354, 24356, 24358, 24360, 24362, 24364, 24366, 24427, + 24429, 24431, 24433, 24435, 24437, 24439, 24441, 24443, + 24445, 24447, 24449, 24483, 24485, 24487, 24489, 24491, + 24493, 24495, 24497, 24499, 24519, 24521, 24523, 24525, + 24527, 24529, 24531, 24533, 24535, 24537, 24592, 24594, + 24596, 24598, 24600, 24602, 24604, 24606, 24608, 24610, + 24612, 24614, 24654, 24656, 24714, 24716, 24720, 24722, + 24724, 24726, 24770, 24772, 24798, 24802, 24804, 24806, + 24808, 24810, 24812, 24814, 24816, 24818, 24820, 24822, + 24865, 24867, 24869, 24871, 24873, 24875, 24877, 24879, + 24881, 24883, 24885, 24948, 24950, 24952, 24954, 24956, + 24968, 24970, 24972, 25026, 25028, 25030, 25032, 25034, + 25036, 25038, 25040, 25042, 25044, 25050, 25052, 25054, + 25056, 25058, 25060, 25062, 25064, 25066, 25124, 25126, + 25128, 25130, 25179, 25181, 25239, 25297, 25299, 25301, + 25303, 25305, 25307, 25309, 25311, 25313, 25315, 25317, + 25374, 25376, 25425, 25427, 25429, 25431, 25433, 25435, + 25437, 25439, 25441, 25443, 25504, 25547, 25549, 25551, + 25553, 25555, 25557, 25610, 25612, 25614, 25616, 25618, + 25620, 25622, 25624, 25626, 25628, 25686, 25688, 25707, + 25709, 25711, 25713, 25715, 25717, 25719, 25721, 25723, + 25725, 25727, 25729, 25731, 25733, 25735, 25737, 25795, + 25797, 25799, 25801, 25803, 25805, 25807, 25865, 25867, + 25869, 25871, 25873, 25930, 25932, 25976, 25978, 25980, + 25982, 25984, 25990, 25992, 25994, 25996, 26057, 26059, + 26061, 26063, 26065, 26069, 26071, 26073, 26075, 26077, + 26138, 26142, 26144, 26146, 26148, 26167, 26169, 26228, + 26230, 26232, 26234, 26236, 26238, 26240, 26242, 26244, + 26246, 26248, 26250, 26252, 26254, 26298, 26300, 26302, + 26304, 26346, 26390, 26392, 26394, 26447, 26449, 26451, + 26453, 26455, 26457, 26519, 26521, 26523, 26525, 26527, + 26529, 26531, 26533, 26535, 26537, 26539, 26541, 26543, + 26545, 26589, 26591, 26611, 26668, 26670, 26672, 26674, + 26676, 26678, 26680, 26682, 26684, 26689, 26691, 26693, + 26695, 26697, 26699, 26701, 26703, 26705, 26707, 26709, + 26711, 26713, 26757, 26813, 26815, 26817, 26819, 26821, + 26823, 26825, 26827, 26829, 26831, 26847, 26849, 26851, + 26853, 26855, 26871, 26929, 26931, 26933, 26935, 26937, + 26981, 26983, 27039, 27041, 27043, 27087, 27089, 27091, + 27149, 27151, 27153, 27155, 27157, 27201, 27203, 27259, + 27261, 27274, 27276, 27278, 27280, 27282, 27284, 27286, + 27288, 27290, 27292, 27294, 27296, 27298, 27300, 27308, + 27310, 27312, 27314, 27358, 27360, 27362, 27364, 27366, + 27368, 27370, 27414, 27416, 27418, 27470, 27528, 27530, + 27532, 27534, 27536, 27538, 27587, 27589, 27591, 27593, + 27595, 27597, 27599, 27601, 27603, 27605, 27607, 27609, + 27611, 27613, 27615, 27632, 27634, 27636, 27638, 27640, + 27642, 27644, 27646, 27699, 27701, 27703, 27705, 27707, + 27764, 27766, 27768, 27770, 27823, 27825, 27827, 27829, + 27831, 27833, 27835, 27837, 27839, 27841, 27843, 27890, + 27892, 27894, 27896, 27898, 27910, 27912, 27914, 27968, + 27970, 27972, 27974, 27976, 27978, 27980, 28017, 28019, + 28037, 28039, 28041, 28043, 28045, 28047, 28065, 28067, + 28109, 28111, 28131, 28133, 28135, 28137, 28139, 28141, + 28143, 28145, 28147, 28149, 28151, 28153, 28155, 28157, + 28159, 28161, 28163, 28165, 28167, 28169, 28186, 28188, + 28190, 28192, 28194, 28201, 28203, 28205, 28207, 28209, + 28211, 28213, 28226, 28228, 28230, 28232, 28234, 28236, + 28238, 28240, 28261, 28263, 28265, 28267, 28278, 28280, + 28282, 28284, 28286, 28288, 28290, 28292, 28294, 28296, + 28298, 28308, 28310, 28312, 28314, 28316, 28318, 28320, + 28333, 28335, 28337, 28339, 28341, 28343, 28345, 28347, + 28349, 28351, 28353, 28355, 28368, 28370, 28372, 28374, + 28376, 28378, 28380, 28382, 28384, 28445, 28447, 28449, + 28451, 28503, 28505, 28559, 28561, 28563, 28565, 28567, + 28569, 28571, 28573, 28575, 28577, 28579, 28581, 28583, + 28585, 28587, 28601, 28603, 28605, 28607, 28609, 28611, + 28613, 28615, 28617, 28620, 28622, 28624, 28626, 28628, + 28668, 28670, 28672, 28674, 28676, 28678, 28680, 28682, + 28684, 28706, 28708, 28767, 28769, 28771, 28773, 28775, + 28780, 28782, 28784, 28786, 28788, 28790, 28792, 28794, + 28813, 28815, 28817, 28819, 28821, 28823, 28825, 28827, + 28829, 28831, 28833, 28835, 28837, 28839, 28841, 28843, + 28845, 28853, 28914, 28916, 28918, 28920, 28922, 28924, + 28926, 28928, 28930, 28991, 28993, 28995, 28997, 28999, + 29001, 29003, 29005, 29007, 29029, 29031, 29041, 29043, + 29090, 29092, 29094, 29096, 29098, 29157, 29159, 29161, + 29163, 29165, 29167, 29169, 29171, 29173, 29181, 29183, + 29185, 29187, 29189, 29191, 29193, 29195, 29197, 29199, + 29201, 29203, 29217, 29219, 29221, 29223, 29225, 29227, + 29229, 29231, 29233, 29235, 29237, 29297, 29299, 29301, + 29303, 29305, 29307, 29309, 29351, 29409, 29411, 29413, + 29415, 29417, 29419, 29421, 29423, 29425, 29427, 29429, + 29431, 29433, 29435, 29437, 29439, 29459, 29461, 29463, + 29465, 29467, 29469, 29471, 29473, 29475, 29477, 29479, + 29481, 29483, 29485, 29487, 29489, 29491, 29493, 29551, + 29553, 29601, 29603, 29605, 29607, 29609, 29611, 29613, + 29627, 29629, 29649, 29651, 29653, 29655, 29657, 29659, + 29661, 29663, 29665, 29667, 29669, 29671, 29673, 29732, + 29734, 29736, 29738, 29740, 29742, 29744, 29746, 29748, + 29750, 29758, 29760, 29762, 29764, 29766, 29768, 29770, + 29772, 29774, 29791, 29793, 29795, 29797, 29799, 29801, + 29803, 29805, 29807, 29809, 29811, 29813, 29815, 29817, + 29819, 29821, 29823, 29839, 29841, 29847, 29849, 29851, + 29853, 29855, 29857, 29859, 29861, 29863, 29865, 29867, + 29869, 29871, 29915, 29917, 29919, 29921, 29978, 29999, + 30001, 30003, 30005, 30007, 30009, 30011, 30013, 30015, + 30017, 30019, 30021, 30023, 30025, 30027, 30029, 30031, + 30050, 30068, 30070, 30072, 30074, 30076, 30078, 30080, + 30082, 30084, 30086, 30088, 30090, 30092, 30094, 30096, + 30147, 30149, 30151, 30153, 30155, 30157, 30159, 30161, + 30223, 30225, 30227, 30272, 30274, 30276, 30278, 30280, + 30282, 30284, 30286, 30288, 30290, 30292, 30294, 30296, + 30298, 30300, 30302, 30304, 30306, 30316, 30318, 30320, + 30322, 30324, 30377, 30379, 30381, 30383, 30385, 30404, + 30406, 30408, 30410, 30412, 30414, 30416, 30428, 30430, + 30438, 30440, 30442, 30458, 30460, 30462, 30481, 30483, + 30485, 30511, 30513, 30515, 30517, 30519, 30526, 30528, + 30530, 30532, 30534, 30536, 30538, 30540, 30542, 30560, + 30562, 30564, 30566, 30568, 30570, 30572, 30574, 30576, + 30578, 30635, 30637, 30639, 30641, 30649, 30651, 30709, + 30711, 30713, 30715, 30717, 30719, 30721, 30723, 30725, + 30727, 30729, 30740, 30742, 30744, 30746, 30748, 30750, + 30752, 30754, 30756, 30758, 30774, 30776, 30795, 30797, + 30848, 30850, 30911, 30913, 30927, 30929, 30931, 30952, + 30954, 30956, 30958, 30960, 31019, 31021, 31023, 31025, + 31027, 31029, 31031, 31033, 31035, 31037, 31039, 31058, + 31060, 31062, 31064, 31066, 31068, 31070, 31072, 31074, + 31076, 31085, 31087, 31089, 31091, 31093, 31095, 31097, + 31099, 31101, 31103, 31105, 31107, 31109, 31111, 31113, + 31115, 31117, 31119, 31121, 31123, 31125, 31127, 31129, + 31131, 31133, 31135, 31137, 31139, 31141, 31143, 31145, + 31147, 31149, 31151, 31153, 31155, 31157, 31159, 31161, + 31163, 31165, 31167, 31169, 31171, 31173, 31175, 31177, + 31179, 31181, 31183, 31185, 31187, 31189, 31191, 31193, + 31195, 31197, 31199, 31213, 31215, 31217, 31219, 31221, + 31223, 31225, 31227, 31229, 31231, 31233, 31235, 31275, + 31277, 31279, 31281, 31283, 31285, 31287, 31289, 31291, + 31312, 31318, 31320, 31322, 31324, 31326, 31328, 31330, + 31332, 31345, 31347, 31349, 31351, 31353, 31355, 31357, + 31359, 31361, 31363, 31365, 31367, 31384, 31386, 31432, + 31434, 31436, 31438, 31440, 31442, 31444, 31446, 31448, + 31450, 31452, 31454, 31456, 31474, 31476, 31478, 31480, + 31482, 31484, 31486, 31488, 31509, 31511, 31513, 31570, + 31572, 31584, 31586, 31588, 31590, 31592, 31594, 31596, + 31598, 31600, 31602, 31653, 31655, 31657, 31659, 31661, + 31663, 31665, 31667, 31669, 31671, 31673, 31675, 31677, + 31679, 31706, 31708, 31710, 31712, 31714, 31716, 31718, + 31720, 31722, 31724, 31788, 31790, 31794, 31796, 31798, + 31800, 31802, 31804, 31806, 31808, 31851, 31853, 31855, + 31857, 31859, 31861, 31863, 31911, 31913, 31915, 31917, + 31919, 31921, 31923, 31925, 31927, 31929, 31931, 31933, + 31935, 31937, 31939, 31941, 31943, 31945, 31947, 31991, + 31993, 31995, 32052, 32054, 32056, 32058, 32060, 32072, + 32074, 32076, 32130, 32132, 32134, 32136, 32138, 32140, + 32142, 32144, 32146, 32148, 32150, 32157, 32159, 32161, + 32163, 32165, 32167, 32169, 32171, 32173, 32227, 32229, + 32231, 32233, 32259, 32261, 32263, 32265, 32284, 32286, + 32288, 32290, 32292, 32294, 32296, 32301, 32303, 32305, + 32307, 32309, 32311, 32313, 32315, 32317, 32319, 32321, + 32323, 32331, 32333, 32335, 32396, 32398, 32400, 32457, + 32459, 32461, 32463, 32519, 32521, 32533, 32535, 32547, + 32549, 32551, 32553, 32555, 32557, 32559, 32561, 32563, + 32565, 32567, 32569, 32571, 32573, 32575, 32577, 32598, + 32607, 32609, 32611, 32613, 32615, 32617, 32619, 32621, + 32623, 32625, 32627, 32629, 32631, 32633, 32635, 32637, + 32639, 32641, 32643, 32645, 32655, 32657, 32659, 32661, + 32705, 32763, 32765, 32777, 32779, 32781, 32783, 32785, + 32826, 32866, 32868, 32870, 32872, 32874, 32876, 32892, + 32894, 32896, 32956, 32958, 32960, 32962, 32964, 32984, + 33006, 33008, 33066, 33068, 33070, 33128, 33130, 33132, + 33148, 33206, 33208, 33210, 33212, 33256, 33258, 33260, + 33318, 33320, 33322, 33324, 33368, 33370, 33372, 33417, + 33419, 33422, 33424, 33426, 33428, 33430, 33432, 33434, + 33436, 33455, 33457, 33459, 33461, 33463, 33465, 33467, + 33469, 33471, 33473, 33475, 33477, 33479, 33481, 33483, + 33502, 33504, 33549, 33551, 33566, 33568, 33570, 33572, + 33574, 33587, 33589, 33591, 33593, 33595, 33597, 33599, + 33601, 33603, 33605, 33607, 33609, 33611, 33627, 33685, + 33687, 33689, 33691, 33693, 33736, 33738, 33740, 33742, + 33744, 33746, 33748, 33750, 33784, 33786, 33788, 33790, + 33792, 33794, 33796, 33798, 33800, 33802, 33804, 33822, + 33824, 33877, 33879, 33935, 33937, 33939, 33941, 33997, + 33999, 34001, 34003, 34019, 34021, 34023, 34025, 34083, + 34085, 34087, 34089, 34091, 34093, 34095, 34097, 34099, + 34101, 34103, 34105, 34107, 34109, 34111, 34113, 34133, + 34135, 34137, 34139, 34141, 34143, 34145, 34147, 34149, + 34151, 34153, 34155, 34157, 34159, 34161, 34163, 34165, + 34167, 34169, 34237, 34239, 34245, 34247, 34249, 34251, + 34253, 34255, 34298, 34300, 34302, 34304, 34306, 34314, + 34316, 34318, 34320, 34322, 34324, 34326, 34328, 34330, + 34332, 34334, 34336, 34338, 34372, 34374, 34376, 34378, + 34380, 34382, 34384, 34402, 34404, 34457, 34459, 34515, + 34517, 34519, 34521, 34577, 34579, 34581, 34583, 34599, + 34601, 34603, 34650, 34652, 34654, 34656, 34658, 34670, + 34672, 34674, 34728, 34730, 34732, 34734, 34736, 34738, + 34740, 34742, 34744, 34746, 34770, 34775, 34777, 34779, + 34781, 34783, 34785, 34787, 34789, 34791, 34817, 34819, + 34821, 34823, 34825, 34827, 34829, 34831, 34833, 34835, + 34837, 34839, 34841, 34843, 34845, 34847, 34849, 34851, + 34853, 34855, 34867, 34871, 34873, 34925, 34927, 34929, + 34931, 34933, 34935, 34937, 34998, 35000, 35002, 35004, + 35006, 35019, 35021, 35041, 35043, 35045, 35047, 35049, + 35051, 35053, 35055, 35057, 35059, 35061, 35063, 35065, + 35085, 35087, 35089, 35091, 35093, 35095, 35097, 35099, + 35103, 35105, 35107, 35109, 35111, 35113, 35153, 35155, + 35157, 35159, 35161, 35163, 35179, 35181, 35183, 35228, + 35230, 35232, 35234, 35236, 35238, 35240, 35294, 35296, + 35298, 35300, 35302, 35304, 35306, 35308, 35310, 35312, + 35314, 35331, 35333, 35335, 35337, 35358, 35360, 35362, + 35364, 35366, 35423, 35425, 35427, 35429, 35431, 35433, + 35435, 35437, 35481, 35483, 35485, 35487, 35489, 35491, + 35493, 35495, 35539, 35541, 35543, 35545, 35547, 35549, + 35551, 35553, 35555, 35557, 35559, 35561, 35563, 35565, + 35567, 35569, 35571, 35573, 35575, 35577, 35579, 35581, + 35583, 35585, 35587, 35589, 35591, 35593, 35612, 35621, + 35623, 35625, 35627, 35629, 35631, 35633, 35635, 35637, + 35639, 35647, 35649, 35651, 35653, 35655, 35657, 35659, + 35661, 35669, 35671, 35673, 35675, 35677, 35679, 35681, + 35683, 35685, 35687, 35689, 35691, 35693, 35695, 35697, + 35699, 35701, 35703, 35705, 35707, 35763, 35765, 35767, + 35769, 35771, 35773, 35775, 35792, 35794, 35796, 35798, + 35800, 35802, 35804, 35808, 35810, 35812, 35814, 35816, + 35818, 35836, 35838, 35840, 35842, 35851, 35853, 35855, + 35857, 35859, 35861, 35863, 35865, 35867, 35869, 35871, + 35882, 35884, 35886, 35888, 35890, 35892, 35894, 35896, + 35898, 35900, 35903, 35905, 35913, 35915, 35917, 35919, + 35921, 35923, 35942, 35947, 35949, 35951, 35995, 35997, + 35999, 36001, 36003, 36005, 36007, 36009, 36011, 36024, + 36026, 36028, 36030, 36041, 36043, 36045, 36047, 36049, + 36051, 36073, 36075, 36077, 36079, 36081, 36083, 36085, + 36087, 36089, 36091, 36093, 36095, 36097, 36099, 36101, + 36103, 36105, 36107, 36109, 36111, 36113, 36121, 36123, + 36125, 36127, 36129, 36131, 36133, 36135, 36137, 36139, + 36141, 36143, 36145, 36147, 36198, 36200, 36202, 36204, + 36206, 36208, 36210, 36212, 36214, 36216, 36218, 36220, + 36222, 36241, 36246, 36248, 36250, 36294, 36296, 36298, + 36300, 36302, 36304, 36306, 36308, 36310, 36312, 36314, + 36316, 36318, 36320, 36322, 36338, 36340, 36342, 36344, + 36346, 36348, 36350, 36352, 36354, 36399, 36401, 36415, + 36417, 36419, 36421, 36423, 36425, 36427, 36429, 36431, + 36433, 36435, 36494, 36496, 36498, 36500, 36502, 36504, + 36565, 36567, 36569, 36571, 36573, 36575, 36581, 36583, + 36585, 36587, 36589, 36606, 36608, 36610, 36612, 36614, + 36616, 36618, 36620, 36622, 36624, 36626, 36628, 36630, + 36632, 36634, 36636, 36638, 36640, 36642, 36644, 36656, + 36658, 36660, 36662, 36664, 36666, 36668, 36670, 36672, + 36719, 36721, 36723, 36737, 36739, 36741, 36743, 36745, + 36761, 36763, 36765, 36767, 36769, 36771, 36773, 36829, + 36831, 36833, 36835, 36837, 36839, 36841, 36843, 36899, + 36901, 36913, 36915, 36917, 36919, 36921, 36923, 36925, + 36927, 36929, 36931, 36933, 36941, 36943, 36945, 36947, + 36949, 36951, 36953, 36955, 36957, 36959, 36961, 36963, + 36965, 36967, 36969, 36971, 36986, 37030, 37032, 37034, + 37036, 37038, 37040, 37042, 37044, 37046, 37048, 37068, + 37070, 37072, 37074, 37076, 37078, 37080, 37082, 37084, + 37086, 37088, 37090, 37092, 37108, 37110, 37112, 37114, + 37116, 37118, 37120, 37122, 37124, 37126, 37128, 37130, + 37132, 37134, 37154, 37156, 37158, 37174, 37176, 37210, + 37212, 37214, 37216, 37250, 37252, 37254, 37256, 37258, + 37260, 37262, 37264, 37266, 37284, 37286, 37288, 37290, + 37292, 37299, 37306, 37308, 37310, 37312, 37314, 37370, + 37372, 37374, 37376, 37378, 37380, 37382, 37384, 37386, + 37388, 37390, 37392, 37436, 37438, 37440, 37442, 37444, + 37446, 37448, 37450, 37472, 37494, 37496, 37498, 37500, + 37502, 37504, 37506, 37508, 37510, 37512, 37514, 37516, + 37518, 37520, 37554, 37556, 37558, 37560, 37562, 37564, + 37566, 37568, 37602, 37604, 37606, 37608, 37610, 37612, + 37614, 37616, 37618, 37620, 37622, 37624, 37626, 37644, + 37646, 37648, 37650, 37658, 37660, 37662, 37664, 37666, + 37668, 37670, 37672, 37674, 37708, 37710, 37712, 37714, + 37716, 37718, 37720, 37736, 37738, 37740, 37742, 37744, + 37746, 37748, 37754, 37756, 37758, 37760, 37762, 37764, + 37766, 37768, 37770, 37772, 37774, 37776, 37778, 37780, + 37782, 37784, 37806, 37808, 37814, 37816, 37818, 37820, + 37838, 37840, 37842, 37844, 37846, 37848, 37850, 37852, + 37854, 37856, 37858, 37860, 37862, 37864, 37866, 37868, + 37870, 37882, 37884, 37886, 37888, 37912, 37914, 37916, + 37918, 37920, 37922, 37948, 37950, 37952, 37954, 37956, + 37958, 37960, 37962, 37964, 37981, 37983, 37985, 37987, + 37989, 37991, 37993, 37995, 37997, 37999, 38001, 38003, + 38005, 38007, 38009, 38011, 38013, 38015, 38017, 38019, + 38021, 38023, 38025, 38027, 38033, 38035, 38037, 38039, + 38098, 38100, 38102, 38104, 38106, 38108, 38110, 38112, + 38114, 38116, 38118, 38120, 38122, 38124, 38126, 38128, + 38130, 38132, 38134, 38136, 38138, 38140, 38142, 38144, + 38146, 38148, 38150, 38152, 38154, 38156, 38158, 38160, + 38162, 38164, 38166, 38168, 38170, 38172, 38174, 38176, + 38178, 38180, 38182, 38184, 38186, 38188, 38190, 38192, + 38194, 38238, 38296, 38298, 38300, 38354, 38356, 38358, + 38360, 38362, 38364, 38366, 38368, 38370, 38427, 38429, + 38431, 38433, 38435, 38437, 38439, 38441, 38498, 38500, + 38502, 38504, 38506, 38559, 38561, 38563, 38565, 38567, + 38569, 38571, 38573, 38575, 38577, 38628, 38630, 38632, + 38634, 38636, 38638, 38640, 38642, 38644, 38646, 38648, + 38650, 38652, 38695, 38697, 38699, 38701, 38703, 38705, + 38707, 38709 +}; + +static const short _char_ref_indicies[] = { + 0, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 4, 5, + 1, 1, 6, 7, 1, 1, 1, 1, + 8, 9, 10, 11, 12, 1, 13, 14, + 15, 16, 1, 17, 1, 18, 1, 19, + 1, 20, 1, 21, 1, 22, 1, 23, + 1, 24, 1, 25, 1, 26, 1, 27, + 1, 28, 1, 29, 1, 30, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 31, 1, 32, + 1, 33, 1, 34, 1, 35, 1, 36, + 1, 37, 1, 38, 1, 39, 1, 40, + 1, 41, 1, 42, 1, 43, 1, 44, + 1, 45, 1, 46, 1, 47, 1, 48, + 1, 49, 1, 50, 1, 51, 1, 1, + 1, 1, 1, 1, 1, 1, 52, 1, + 53, 1, 54, 1, 55, 1, 56, 1, + 57, 1, 58, 1, 59, 1, 60, 1, + 61, 1, 62, 1, 63, 1, 64, 1, + 65, 1, 66, 1, 67, 1, 68, 1, + 69, 1, 70, 1, 71, 1, 72, 1, + 73, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 74, 1, 75, 1, 76, 1, 77, 1, + 78, 1, 79, 1, 80, 1, 81, 1, + 82, 1, 83, 1, 84, 1, 85, 1, + 86, 1, 87, 1, 88, 1, 89, 90, + 1, 1, 1, 1, 1, 1, 1, 1, + 91, 1, 1, 92, 93, 1, 94, 1, + 95, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 96, + 1, 97, 1, 98, 1, 99, 1, 100, + 1, 101, 1, 102, 1, 103, 1, 104, + 105, 1, 106, 1, 107, 1, 108, 1, + 109, 1, 110, 1, 111, 1, 112, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 113, 1, 114, + 1, 115, 1, 116, 1, 117, 1, 118, + 1, 119, 1, 120, 1, 121, 1, 122, + 1, 123, 1, 124, 1, 125, 1, 126, + 1, 127, 1, 128, 1, 129, 1, 130, + 1, 131, 1, 132, 1, 133, 1, 134, + 1, 135, 1, 136, 1, 137, 1, 138, + 1, 139, 1, 140, 1, 141, 1, 142, + 1, 143, 1, 144, 1, 145, 1, 146, + 1, 147, 1, 1, 1, 1, 1, 1, + 148, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 149, 1, 150, 151, 152, 153, + 1, 154, 155, 1, 1, 156, 1, 1, + 157, 1, 1, 158, 159, 1, 160, 1, + 161, 1, 162, 1, 163, 1, 164, 1, + 165, 1, 166, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 167, + 1, 1, 1, 1, 1, 1, 1, 1, + 168, 1, 169, 1, 170, 1, 171, 1, + 172, 1, 173, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 174, 1, 175, 1, 176, 1, 177, 1, + 178, 1, 179, 1, 180, 1, 181, 1, + 182, 1, 183, 1, 184, 1, 185, 1, + 186, 1, 187, 1, 188, 1, 189, 1, + 190, 1, 191, 1, 192, 1, 193, 1, + 194, 1, 195, 1, 196, 1, 197, 1, + 1, 1, 198, 1, 1, 1, 199, 1, + 1, 1, 1, 1, 200, 1, 201, 1, + 202, 1, 203, 1, 204, 1, 205, 1, + 206, 1, 207, 1, 208, 1, 209, 1, + 210, 1, 211, 1, 212, 1, 213, 1, + 214, 1, 215, 1, 216, 1, 217, 1, + 218, 1, 219, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 220, 1, 221, 1, + 222, 1, 223, 1, 224, 1, 225, 1, + 226, 1, 227, 1, 228, 1, 229, 1, + 230, 1, 231, 1, 232, 1, 233, 1, + 234, 1, 235, 1, 236, 1, 237, 1, + 238, 1, 239, 1, 240, 1, 241, 1, + 1, 1, 1, 1, 1, 1, 1, 242, + 1, 1, 243, 1, 1, 1, 244, 1, + 245, 1, 246, 1, 247, 1, 248, 1, + 249, 1, 250, 1, 251, 1, 252, 1, + 253, 1, 254, 1, 255, 1, 256, 1, + 257, 1, 258, 1, 259, 1, 260, 1, + 261, 1, 262, 1, 263, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 264, 1, 265, 1, + 266, 1, 267, 1, 268, 1, 269, 1, + 270, 1, 271, 1, 272, 1, 273, 1, + 274, 1, 275, 1, 276, 1, 277, 1, + 278, 1, 279, 1, 280, 1, 281, 1, + 282, 1, 283, 1, 284, 1, 285, 1, + 286, 1, 287, 1, 288, 1, 289, 1, + 290, 1, 291, 1, 292, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 293, 1, 294, 1, 295, 1, 296, + 1, 297, 1, 298, 1, 299, 1, 300, + 1, 301, 1, 302, 1, 303, 1, 304, + 1, 305, 1, 306, 1, 307, 1, 308, + 1, 309, 1, 310, 1, 311, 1, 312, + 1, 1, 1, 1, 313, 1, 314, 1, + 315, 1, 316, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 317, 1, 318, 1, + 319, 1, 320, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 321, 1, 322, + 1, 323, 1, 324, 1, 325, 1, 326, + 1, 327, 1, 328, 1, 329, 1, 330, + 1, 331, 1, 332, 1, 333, 1, 334, + 1, 335, 1, 336, 1, 337, 1, 338, + 1, 339, 1, 340, 1, 341, 1, 342, + 1, 343, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 344, 1, 345, + 1, 346, 1, 347, 1, 348, 1, 349, + 1, 350, 1, 351, 1, 352, 1, 353, + 1, 354, 1, 355, 1, 356, 1, 357, + 1, 358, 1, 359, 1, 360, 1, 361, + 1, 362, 1, 363, 1, 364, 1, 365, + 1, 366, 1, 367, 1, 368, 1, 369, + 1, 370, 1, 371, 1, 372, 1, 373, + 1, 374, 1, 375, 1, 376, 1, 377, + 1, 378, 1, 379, 1, 380, 1, 381, + 1, 382, 1, 383, 1, 384, 1, 385, + 1, 386, 1, 387, 1, 388, 1, 389, + 1, 1, 1, 1, 1, 1, 1, 390, + 1, 391, 1, 392, 1, 393, 1, 394, + 1, 1, 1, 1, 1, 395, 1, 1, + 1, 1, 1, 1, 1, 1, 396, 1, + 1, 1, 1, 1, 1, 397, 1, 1, + 1, 1, 1, 1, 398, 1, 399, 1, + 400, 401, 1, 1, 402, 1, 1, 1, + 1, 1, 403, 1, 1, 1, 404, 1, + 405, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 406, 1, 407, 1, + 408, 1, 409, 1, 410, 1, 411, 1, + 412, 1, 413, 1, 414, 1, 415, 1, + 416, 1, 417, 1, 418, 1, 419, 1, + 420, 1, 421, 1, 422, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 423, + 424, 1, 425, 1, 426, 1, 427, 1, + 428, 1, 429, 1, 430, 1, 431, 1, + 432, 1, 433, 1, 434, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 435, 1, 436, 1, + 437, 1, 438, 1, 439, 1, 440, 1, + 441, 1, 442, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 443, 1, 444, 1, 445, + 1, 446, 1, 447, 1, 448, 1, 1, + 1, 1, 449, 1, 450, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 451, 1, + 452, 1, 453, 1, 454, 1, 455, 1, + 456, 1, 457, 1, 458, 1, 459, 1, + 1, 460, 1, 1, 461, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 462, 1, 463, 1, 464, 1, 465, + 1, 466, 1, 467, 1, 468, 1, 469, + 470, 1, 471, 1, 472, 1, 473, 1, + 474, 1, 475, 1, 476, 1, 477, 1, + 478, 1, 479, 1, 480, 1, 481, 1, + 482, 1, 483, 1, 484, 1, 485, 1, + 486, 1, 487, 1, 488, 1, 489, 1, + 490, 1, 491, 1, 492, 1, 493, 1, + 494, 1, 495, 1, 496, 1, 497, 1, + 498, 1, 499, 1, 500, 1, 501, 1, + 502, 1, 503, 1, 504, 1, 505, 1, + 506, 1, 1, 1, 507, 508, 1, 509, + 1, 510, 1, 511, 1, 512, 1, 1, + 1, 1, 1, 1, 1, 1, 513, 514, + 1, 515, 1, 516, 1, 517, 1, 518, + 1, 519, 1, 520, 1, 521, 1, 522, + 1, 523, 1, 524, 1, 525, 1, 526, + 527, 1, 1, 1, 1, 1, 1, 1, + 528, 1, 1, 1, 1, 1, 529, 1, + 1, 530, 531, 1, 532, 1, 533, 1, + 534, 1, 535, 1, 536, 1, 537, 1, + 538, 1, 539, 1, 540, 1, 541, 1, + 542, 1, 543, 1, 544, 1, 545, 1, + 546, 1, 547, 1, 548, 1, 1, 549, + 1, 550, 1, 551, 1, 552, 1, 553, + 1, 554, 1, 555, 1, 556, 1, 557, + 1, 558, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 559, 1, 560, 1, 561, + 1, 562, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 563, 1, 564, 1, 565, 1, + 566, 1, 567, 1, 568, 1, 569, 1, + 570, 1, 571, 1, 572, 1, 573, 1, + 574, 1, 575, 1, 576, 1, 577, 1, + 578, 1, 579, 1, 580, 1, 581, 1, + 582, 1, 583, 1, 584, 1, 585, 1, + 1, 1, 1, 1, 586, 1, 587, 1, + 588, 1, 589, 1, 590, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 591, 1, 592, + 1, 593, 1, 594, 1, 595, 1, 596, + 1, 597, 1, 598, 1, 599, 1, 600, + 1, 601, 1, 602, 1, 603, 1, 604, + 1, 605, 1, 606, 1, 607, 1, 608, + 1, 609, 1, 610, 1, 611, 1, 612, + 1, 613, 1, 614, 1, 615, 1, 616, + 1, 617, 1, 618, 1, 619, 1, 620, + 1, 621, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 622, 1, 623, 1, + 624, 1, 625, 1, 626, 1, 627, 1, + 628, 1, 629, 1, 630, 1, 631, 1, + 632, 1, 1, 633, 1, 634, 1, 635, + 1, 636, 1, 637, 1, 638, 1, 639, + 1, 640, 1, 641, 1, 642, 1, 643, + 1, 644, 1, 645, 1, 646, 1, 647, + 1, 648, 1, 649, 1, 650, 1, 651, + 1, 652, 1, 653, 1, 654, 1, 655, + 1, 656, 1, 657, 1, 658, 1, 659, + 1, 660, 661, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 662, 1, 1, 1, + 1, 1, 663, 1, 664, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 665, 1, 666, 1, 667, 1, 668, + 1, 669, 1, 670, 1, 1, 1, 1, + 1, 1, 671, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 672, 1, 673, + 1, 674, 1, 675, 1, 676, 1, 677, + 1, 678, 1, 679, 1, 680, 1, 681, + 1, 682, 1, 683, 1, 684, 1, 685, + 1, 686, 1, 687, 1, 688, 1, 689, + 1, 690, 1, 691, 1, 692, 1, 693, + 1, 694, 1, 695, 1, 696, 1, 697, + 1, 698, 1, 699, 1, 700, 1, 701, + 1, 702, 1, 703, 1, 704, 1, 705, + 1, 706, 1, 707, 1, 708, 1, 709, + 1, 710, 1, 711, 1, 712, 1, 713, + 1, 714, 1, 715, 1, 716, 1, 717, + 1, 718, 1, 719, 1, 1, 1, 1, + 1, 1, 720, 1, 721, 1, 722, 1, + 723, 1, 724, 1, 725, 1, 726, 1, + 727, 1, 728, 1, 729, 1, 730, 1, + 731, 1, 732, 1, 733, 1, 734, 1, + 735, 1, 736, 1, 737, 1, 738, 1, + 739, 1, 740, 1, 741, 1, 742, 1, + 743, 1, 744, 1, 1, 1, 1, 1, + 1, 745, 1, 746, 1, 747, 1, 748, + 1, 749, 1, 750, 1, 751, 1, 1, + 1, 1, 1, 752, 1, 753, 1, 754, + 1, 755, 1, 756, 1, 757, 1, 758, + 1, 759, 1, 760, 1, 761, 1, 762, + 1, 763, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 764, 1, 765, 1, 766, 1, + 767, 1, 768, 1, 769, 1, 770, 1, + 771, 1, 1, 1, 1, 1, 772, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 773, 1, 774, 775, 1, + 776, 777, 1, 1, 1, 1, 778, 779, + 1, 780, 781, 782, 1, 783, 784, 785, + 1, 1, 786, 1, 787, 1, 788, 1, + 789, 1, 790, 1, 791, 1, 792, 1, + 793, 1, 794, 1, 1, 1, 1, 1, + 1, 1, 795, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 796, 1, 797, 1, 798, 1, + 799, 1, 800, 1, 801, 1, 802, 1, + 803, 1, 804, 1, 805, 1, 806, 1, + 807, 1, 808, 1, 809, 1, 810, 1, + 811, 1, 812, 1, 813, 1, 814, 1, + 815, 1, 816, 1, 817, 1, 818, 1, + 819, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 820, + 1, 821, 1, 822, 1, 823, 1, 824, + 1, 825, 1, 826, 1, 1, 827, 1, + 828, 1, 829, 1, 830, 1, 831, 1, + 832, 1, 833, 1, 834, 1, 835, 1, + 836, 1, 837, 1, 838, 1, 839, 1, + 840, 1, 841, 1, 842, 1, 843, 1, + 844, 1, 845, 1, 846, 1, 847, 1, + 848, 1, 849, 1, 850, 1, 851, 1, + 852, 1, 853, 1, 854, 1, 1, 1, + 1, 1, 1, 1, 1, 855, 1, 856, + 1, 857, 1, 858, 1, 859, 1, 860, + 1, 861, 1, 862, 1, 863, 1, 864, + 1, 865, 1, 866, 1, 867, 1, 868, + 1, 1, 1, 1, 1, 1, 1, 869, + 1, 870, 1, 871, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 872, 1, 873, 1, + 874, 1, 875, 1, 876, 1, 877, 1, + 878, 1, 879, 1, 880, 1, 881, 1, + 882, 1, 883, 1, 884, 1, 885, 1, + 886, 1, 1, 1, 1, 1, 887, 1, + 888, 1, 889, 1, 890, 1, 891, 1, + 892, 1, 893, 1, 894, 1, 895, 1, + 896, 1, 1, 1, 1, 1, 1, 897, + 1, 898, 1, 899, 1, 900, 1, 901, + 1, 902, 1, 903, 1, 904, 1, 905, + 1, 906, 1, 907, 1, 908, 1, 909, + 1, 910, 1, 911, 1, 912, 1, 1, + 913, 1, 1, 914, 1, 1, 1, 1, + 1, 915, 1, 1, 1, 916, 1, 917, + 1, 918, 1, 919, 1, 920, 1, 921, + 1, 922, 1, 923, 1, 924, 1, 925, + 1, 1, 926, 1, 927, 1, 928, 1, + 929, 1, 930, 1, 931, 1, 932, 1, + 933, 1, 934, 1, 935, 1, 936, 1, + 937, 1, 938, 1, 939, 1, 940, 1, + 941, 1, 942, 1, 943, 1, 944, 1, + 945, 1, 946, 1, 947, 1, 948, 1, + 949, 1, 950, 1, 951, 1, 952, 1, + 953, 1, 954, 1, 1, 955, 1, 956, + 1, 957, 1, 958, 1, 959, 1, 960, + 1, 961, 1, 962, 1, 963, 1, 964, + 1, 965, 1, 966, 1, 967, 1, 968, + 1, 969, 1, 970, 1, 971, 1, 972, + 1, 973, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 974, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 975, 976, 977, 978, 1, 979, 980, 1, + 1, 1, 1, 1, 1, 1, 981, 1, + 1, 982, 983, 984, 1, 985, 1, 986, + 1, 987, 1, 988, 1, 989, 1, 990, + 1, 991, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 992, 1, 993, 1, 994, 1, + 995, 1, 996, 1, 997, 1, 998, 1, + 999, 1, 1, 1, 1000, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1001, 1, 1002, 1, + 1003, 1, 1004, 1, 1005, 1, 1006, 1, + 1007, 1, 1008, 1, 1009, 1, 1010, 1, + 1011, 1, 1012, 1, 1013, 1, 1014, 1, + 1015, 1, 1016, 1, 1017, 1, 1018, 1, + 1019, 1, 1020, 1, 1021, 1, 1022, 1, + 1023, 1, 1024, 1025, 1026, 1, 1, 1, + 1, 1027, 1, 1, 1, 1, 1, 1, + 1028, 1029, 1, 1030, 1, 1031, 1, 1032, + 1, 1033, 1, 1034, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1035, 1, 1036, 1, + 1037, 1, 1038, 1, 1039, 1, 1040, 1, + 1041, 1, 1042, 1, 1043, 1, 1044, 1, + 1045, 1, 1046, 1, 1047, 1, 1048, 1, + 1049, 1, 1050, 1, 1051, 1, 1052, 1, + 1053, 1, 1054, 1, 1055, 1, 1056, 1, + 1057, 1, 1058, 1, 1059, 1, 1060, 1, + 1061, 1, 1062, 1, 1063, 1, 1064, 1, + 1065, 1, 1066, 1, 1067, 1, 1068, 1, + 1069, 1, 1070, 1, 1071, 1, 1072, 1, + 1073, 1, 1074, 1, 1075, 1, 1076, 1, + 1077, 1, 1078, 1, 1079, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1080, 1, 1081, 1, + 1, 1082, 1, 1, 1083, 1, 1, 1, + 1, 1, 1084, 1, 1, 1, 1085, 1, + 1086, 1, 1087, 1, 1088, 1, 1089, 1, + 1090, 1, 1091, 1, 1092, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1093, 1, 1094, + 1, 1095, 1, 1096, 1, 1097, 1, 1098, + 1, 1099, 1, 1100, 1, 1101, 1, 1102, + 1, 1103, 1, 1104, 1, 1105, 1, 1106, + 1, 1107, 1, 1108, 1, 1109, 1, 1110, + 1, 1111, 1, 1112, 1, 1113, 1, 1114, + 1, 1115, 1, 1116, 1, 1117, 1, 1118, + 1, 1119, 1, 1120, 1, 1121, 1, 1122, + 1, 1123, 1, 1124, 1, 1125, 1, 1126, + 1, 1127, 1, 1128, 1, 1129, 1, 1130, + 1, 1131, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1132, 1, 1133, 1, 1134, 1, + 1135, 1, 1136, 1, 1137, 1, 1138, 1, + 1139, 1, 1140, 1, 1141, 1142, 1, 1143, + 1, 1144, 1, 1145, 1, 1146, 1, 1147, + 1, 1148, 1, 1149, 1, 1150, 1, 1151, + 1, 1152, 1, 1153, 1, 1154, 1, 1155, + 1, 1156, 1, 1, 1, 1, 1157, 1, + 1, 1, 1, 1158, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1159, 1, 1160, + 1161, 1, 1162, 1163, 1, 1, 1, 1, + 1, 1164, 1165, 1166, 1, 1, 1, 1167, + 1168, 1169, 1, 1170, 1, 1171, 1, 1172, + 1, 1173, 1, 1174, 1, 1175, 1, 1176, + 1, 1177, 1, 1178, 1, 1179, 1, 1180, + 1, 1181, 1, 1182, 1, 1183, 1, 1184, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1185, + 1, 1186, 1, 1187, 1, 1188, 1, 1189, + 1, 1190, 1, 1191, 1, 1192, 1, 1193, + 1, 1194, 1, 1195, 1, 1196, 1, 1197, + 1, 1198, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1199, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1200, 1, + 1201, 1, 1, 1, 1202, 1, 1203, 1, + 1204, 1, 1205, 1, 1206, 1, 1207, 1, + 1208, 1, 1209, 1, 1210, 1, 1211, 1, + 1212, 1, 1213, 1, 1214, 1, 1215, 1, + 1216, 1, 1217, 1, 1218, 1, 1219, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1220, 1, 1221, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1222, 1, 1223, + 1, 1224, 1, 1225, 1, 1226, 1, 1227, + 1, 1228, 1, 1229, 1, 1230, 1, 1231, + 1, 1232, 1, 1233, 1, 1234, 1, 1235, + 1, 1236, 1, 1237, 1, 1238, 1, 1239, + 1, 1240, 1, 1241, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1242, 1, 1243, 1, + 1244, 1, 1245, 1, 1246, 1, 1247, 1, + 1248, 1, 1249, 1, 1250, 1, 1251, 1, + 1252, 1, 1253, 1, 1, 1, 1, 1, + 1, 1, 1, 1254, 1, 1, 1, 1255, + 1, 1256, 1, 1257, 1, 1258, 1, 1259, + 1, 1260, 1, 1261, 1, 1262, 1, 1263, + 1, 1264, 1, 1265, 1, 1266, 1, 1267, + 1, 1268, 1, 1269, 1, 1270, 1, 1271, + 1, 1272, 1, 1273, 1, 1274, 1, 1275, + 1, 1276, 1, 1277, 1, 1, 1278, 1, + 1, 1, 1, 1, 1, 1, 1, 1279, + 1, 1, 1, 1280, 1, 1281, 1, 1282, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1283, + 1, 1284, 1, 1285, 1, 1286, 1, 1287, + 1, 1288, 1, 1289, 1, 1290, 1, 1291, + 1, 1292, 1, 1293, 1, 1294, 1, 1295, + 1, 1296, 1, 1297, 1, 1298, 1, 1299, + 1, 1300, 1, 1301, 1, 1302, 1, 1303, + 1, 1304, 1, 1305, 1, 1306, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1307, 1, 1308, 1, + 1, 1309, 1, 1, 1, 1, 1, 1, + 1, 1, 1310, 1, 1, 1, 1311, 1, + 1312, 1, 1313, 1, 1314, 1, 1315, 1, + 1316, 1, 1317, 1, 1318, 1, 1319, 1, + 1320, 1, 1321, 1, 1322, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1323, 1, 1324, 1, 1325, 1, 1326, 1, + 1327, 1, 1328, 1, 1329, 1, 1330, 1, + 1331, 1, 1332, 1, 1333, 1, 1334, 1, + 1335, 1, 1336, 1, 1337, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1338, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1339, 1, 1340, 1, 1341, + 1342, 1, 1, 1, 1, 1, 1343, 1344, + 1, 1345, 1, 1, 1, 1346, 1347, 1, + 1348, 1, 1349, 1, 1350, 1, 1351, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1352, 1353, 1, 1354, 1, 1355, 1, 1356, + 1, 1357, 1, 1358, 1, 1359, 1, 1360, + 1, 1361, 1, 1362, 1, 1363, 1, 1364, + 1, 1365, 1, 1366, 1, 1367, 1, 1368, + 1, 1369, 1, 1370, 1, 1371, 1, 1372, + 1, 1373, 1, 1374, 1, 1375, 1, 1376, + 1, 1, 1, 1377, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1378, + 1, 1379, 1, 1380, 1, 1381, 1, 1382, + 1, 1383, 1, 1384, 1, 1385, 1, 1386, + 1, 1387, 1, 1388, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1389, 1, 1390, 1, 1391, 1, 1392, 1393, + 1, 1394, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1395, 1, 1396, + 1397, 1398, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1399, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1400, 1, 1401, + 1, 1, 1, 1402, 1, 1403, 1, 1404, + 1, 1405, 1, 1406, 1, 1407, 1, 1408, + 1, 1409, 1, 1410, 1, 1411, 1, 1412, + 1, 1413, 1, 1414, 1, 1415, 1, 1416, + 1, 1417, 1, 1, 1, 1, 1, 1, + 1418, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1419, 1, 1420, 1, 1421, 1, 1422, 1, + 1423, 1, 1424, 1, 1425, 1, 1426, 1, + 1427, 1, 1428, 1, 1429, 1, 1430, 1, + 1431, 1, 1432, 1, 1433, 1, 1434, 1, + 1435, 1, 1436, 1, 1437, 1, 1438, 1, + 1439, 1, 1440, 1, 1441, 1, 1442, 1, + 1443, 1, 1444, 1, 1445, 1, 1446, 1, + 1447, 1, 1448, 1, 1449, 1, 1450, 1, + 1451, 1, 1452, 1, 1453, 1, 1454, 1, + 1455, 1, 1456, 1, 1457, 1, 1458, 1, + 1459, 1, 1460, 1, 1461, 1, 1462, 1, + 1463, 1, 1464, 1, 1465, 1, 1466, 1, + 1467, 1, 1468, 1, 1469, 1, 1470, 1, + 1471, 1, 1, 1, 1, 1, 1, 1472, + 1, 1473, 1, 1474, 1, 1475, 1, 1476, + 1, 1477, 1, 1478, 1, 1479, 1, 1480, + 1, 1481, 1, 1482, 1, 1483, 1, 1484, + 1, 1485, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1486, 1, + 1487, 1, 1488, 1, 1489, 1, 1490, 1, + 1491, 1, 1492, 1, 1493, 1, 1494, 1, + 1495, 1, 1496, 1, 1497, 1, 1498, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1499, 1, 1500, 1, 1501, + 1, 1, 1, 1, 1, 1502, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1503, 1, 1504, 1, 1505, 1, + 1506, 1, 1507, 1, 1508, 1, 1509, 1, + 1510, 1, 1511, 1, 1512, 1, 1513, 1, + 1514, 1, 1515, 1, 1516, 1, 1517, 1, + 1518, 1, 1519, 1, 1520, 1, 1521, 1, + 1, 1, 1, 1, 1, 1522, 1, 1, + 1523, 1, 1524, 1, 1525, 1, 1526, 1, + 1527, 1, 1528, 1, 1529, 1, 1530, 1, + 1531, 1, 1532, 1, 1533, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1534, 1, 1535, 1, + 1536, 1, 1537, 1, 1538, 1, 1539, 1, + 1540, 1, 1541, 1, 1542, 1, 1543, 1, + 1544, 1, 1545, 1, 1546, 1, 1547, 1, + 1548, 1, 1549, 1, 1550, 1, 1551, 1, + 1552, 1, 1553, 1, 1554, 1, 1555, 1, + 1556, 1, 1557, 1, 1558, 1, 1559, 1, + 1560, 1, 1, 1, 1, 1, 1, 1561, + 1, 1562, 1, 1563, 1, 1564, 1, 1565, + 1, 1566, 1, 1567, 1, 1568, 1, 1569, + 1, 1570, 1, 1, 1, 1, 1, 1, + 1571, 1, 1572, 1, 1573, 1, 1574, 1, + 1575, 1, 1576, 1, 1577, 1, 1578, 1, + 1579, 1, 1580, 1, 1581, 1, 1582, 1, + 1583, 1, 1584, 1, 1585, 1, 1586, 1, + 1587, 1, 1588, 1, 1589, 1, 1590, 1, + 1591, 1592, 1593, 1, 1, 1, 1, 1594, + 1, 1, 1, 1, 1, 1, 1595, 1596, + 1, 1597, 1, 1598, 1, 1599, 1, 1600, + 1, 1601, 1, 1602, 1, 1603, 1, 1604, + 1, 1605, 1, 1606, 1, 1607, 1, 1608, + 1, 1609, 1, 1610, 1, 1611, 1, 1612, + 1, 1613, 1, 1614, 1, 1615, 1, 1616, + 1, 1617, 1, 1618, 1, 1619, 1, 1620, + 1, 1621, 1, 1622, 1, 1623, 1, 1624, + 1, 1625, 1, 1626, 1, 1627, 1, 1628, + 1, 1629, 1, 1630, 1, 1631, 1, 1632, + 1, 1633, 1, 1634, 1, 1635, 1, 1636, + 1, 1637, 1, 1638, 1, 1639, 1, 1640, + 1, 1641, 1, 1642, 1, 1643, 1, 1644, + 1, 1645, 1, 1646, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1647, 1, 1648, + 1, 1649, 1, 1650, 1, 1651, 1, 1652, + 1, 1653, 1, 1654, 1, 1655, 1, 1656, + 1, 1657, 1, 1658, 1, 1659, 1, 1660, + 1, 1661, 1, 1662, 1, 1, 1, 1, + 1, 1, 1663, 1, 1664, 1, 1665, 1, + 1, 1, 1, 1, 1666, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1667, 1, + 1, 1, 1, 1, 1668, 1, 1669, 1, + 1670, 1, 1671, 1, 1672, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1673, 1, 1674, + 1, 1675, 1, 1676, 1, 1677, 1, 1678, + 1, 1679, 1, 1680, 1, 1681, 1, 1682, + 1, 1683, 1, 1684, 1, 1685, 1, 1686, + 1, 1687, 1, 1688, 1, 1689, 1, 1690, + 1, 1691, 1, 1692, 1, 1693, 1, 1694, + 1, 1695, 1, 1696, 1, 1697, 1, 1698, + 1, 1699, 1, 1700, 1, 1701, 1, 1702, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1703, 1, 1704, 1, 1705, 1, 1706, 1, + 1707, 1, 1708, 1, 1709, 1, 1710, 1, + 1711, 1, 1712, 1, 1713, 1, 1714, 1, + 1715, 1, 1716, 1, 1717, 1, 1718, 1, + 1719, 1, 1720, 1, 1721, 1, 1722, 1, + 1723, 1, 1724, 1, 1725, 1, 1726, 1, + 1727, 1, 1728, 1, 1729, 1, 1730, 1, + 1731, 1, 1732, 1, 1733, 1, 1, 1, + 1, 1, 1734, 1, 1735, 1, 1736, 1, + 1737, 1, 1738, 1, 1739, 1, 1740, 1, + 1741, 1, 1742, 1, 1743, 1, 1744, 1, + 1745, 1, 1746, 1, 1747, 1, 1748, 1, + 1749, 1, 1750, 1, 1751, 1, 1752, 1, + 1753, 1, 1754, 1, 1, 1, 1, 1755, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1756, 1, 1757, 1, 1758, + 1, 1759, 1, 1760, 1, 1761, 1, 1762, + 1, 1763, 1, 1764, 1, 1765, 1, 1766, + 1, 1767, 1768, 1, 1, 1769, 1, 1, + 1, 1, 1, 1770, 1, 1, 1, 1771, + 1, 1772, 1, 1773, 1, 1774, 1, 1775, + 1, 1776, 1, 1777, 1, 1, 1, 1, + 1, 1, 1, 1778, 1, 1779, 1, 1780, + 1, 1781, 1, 1782, 1, 1783, 1, 1784, + 1, 1785, 1, 1786, 1, 1787, 1, 1788, + 1, 1789, 1, 1790, 1, 1791, 1, 1792, + 1, 1793, 1, 1794, 1, 1795, 1, 1796, + 1, 1797, 1, 1798, 1, 1799, 1, 1800, + 1, 1801, 1, 1802, 1, 1803, 1, 1804, + 1, 1805, 1, 1806, 1, 1807, 1, 1808, + 1, 1809, 1, 1810, 1, 1811, 1, 1812, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1813, 1, + 1814, 1, 1815, 1816, 1, 1, 1, 1, + 1, 1, 1, 1, 1817, 1, 1, 1, + 1818, 1819, 1820, 1, 1821, 1, 1822, 1, + 1823, 1, 1824, 1, 1825, 1, 1826, 1, + 1827, 1, 1828, 1, 1829, 1, 1, 1, + 1830, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1831, 1, 1832, 1, + 1833, 1, 1834, 1, 1835, 1, 1836, 1, + 1837, 1, 1838, 1, 1839, 1, 1840, 1, + 1841, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1842, 1, 1, 1, + 1843, 1, 1844, 1, 1845, 1, 1846, 1, + 1847, 1, 1848, 1, 1849, 1, 1, 1, + 1, 1, 1, 1850, 1, 1851, 1, 1852, + 1, 1853, 1, 1854, 1, 1855, 1, 1856, + 1, 1857, 1, 1858, 1, 1859, 1, 1860, + 1, 1861, 1, 1862, 1, 1863, 1, 1864, + 1, 1865, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1866, 1, 1867, 1, + 1868, 1, 1869, 1, 1870, 1, 1871, 1, + 1872, 1, 1873, 1, 1874, 1, 1875, 1, + 1876, 1, 1877, 1, 1878, 1, 1879, 1, + 1880, 1, 1881, 1, 1882, 1, 1883, 1, + 1884, 1, 1885, 1, 1886, 1, 1887, 1, + 1888, 1, 1889, 1, 1890, 1, 1891, 1, + 1892, 1, 1893, 1, 1894, 1, 1895, 1, + 1896, 1, 1, 1, 1, 1897, 1, 1898, + 1, 1899, 1, 1900, 1, 1901, 1, 1902, + 1, 1903, 1, 1904, 1, 1905, 1, 1906, + 1, 1907, 1, 1908, 1, 1909, 1, 1910, + 1, 1911, 1, 1912, 1, 1913, 1, 1914, + 1, 1915, 1, 1916, 1, 1917, 1, 1918, + 1, 1919, 1, 1920, 1, 1921, 1, 1922, + 1, 1923, 1, 1924, 1, 1925, 1, 1926, + 1, 1927, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1928, 1, 1929, + 1, 1, 1, 1930, 1, 1931, 1, 1932, + 1, 1933, 1, 1934, 1, 1935, 1, 1936, + 1, 1937, 1, 1938, 1, 1939, 1, 1940, + 1, 1941, 1, 1942, 1, 1943, 1, 1944, + 1, 1945, 1, 1946, 1, 1947, 1, 1948, + 1, 1949, 1, 1950, 1, 1951, 1, 1952, + 1, 1, 1, 1, 1, 1, 1, 1953, + 1954, 1955, 1, 1956, 1957, 1, 1, 1, + 1958, 1, 1959, 1, 1960, 1, 1961, 1962, + 1963, 1, 1964, 1, 1965, 1, 1, 1, + 1, 1, 1966, 1, 1967, 1, 1968, 1, + 1969, 1, 1970, 1, 1971, 1, 1972, 1, + 1973, 1, 1974, 1, 1975, 1, 1976, 1, + 1977, 1, 1978, 1, 1979, 1, 1980, 1, + 1981, 1, 1982, 1, 1983, 1, 1984, 1, + 1985, 1, 1986, 1, 1987, 1, 1988, 1, + 1989, 1, 1990, 1, 1991, 1, 1992, 1, + 1993, 1, 1994, 1, 1995, 1, 1996, 1, + 1997, 1, 1, 1, 1, 1998, 1, 1, + 1, 1, 1, 1, 1999, 1, 2000, 1, + 2001, 1, 2002, 1, 2003, 1, 2004, 1, + 2005, 1, 2006, 1, 2007, 1, 2008, 1, + 2009, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2010, 1, 2011, 1, 2012, 1, 2013, + 1, 2014, 1, 2015, 1, 2016, 1, 2017, + 1, 2018, 1, 2019, 1, 2020, 1, 2021, + 1, 2022, 1, 2023, 1, 2024, 1, 2025, + 1, 2026, 1, 2027, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2028, 2029, 2030, + 1, 1, 1, 1, 2031, 1, 1, 1, + 1, 1, 1, 2032, 2033, 1, 2034, 1, + 2035, 1, 2036, 1, 2037, 1, 2038, 1, + 2039, 1, 2040, 1, 2041, 1, 2042, 1, + 2043, 1, 2044, 1, 2045, 1, 2046, 1, + 2047, 1, 2048, 1, 2049, 1, 2050, 1, + 2051, 1, 2052, 1, 2053, 1, 2054, 1, + 2055, 1, 2056, 1, 2057, 1, 2058, 1, + 2059, 1, 2060, 1, 2061, 1, 2062, 1, + 2063, 1, 2064, 1, 2065, 1, 2066, 1, + 2067, 1, 2068, 1, 2069, 1, 2070, 1, + 2071, 1, 2072, 1, 2073, 1, 2074, 1, + 2075, 1, 2076, 1, 2077, 2078, 1, 2079, + 1, 2080, 1, 2081, 1, 2082, 1, 2083, + 1, 2084, 1, 2085, 1, 2086, 1, 2087, + 1, 2088, 1, 2089, 1, 2090, 1, 2091, + 1, 2092, 1, 2093, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2094, 1, 2095, 1, 2096, 1, 2097, 1, + 2098, 1, 2099, 1, 2100, 1, 2101, 1, + 2102, 1, 2103, 1, 2104, 1, 1, 1, + 1, 1, 1, 2105, 1, 1, 2106, 1, + 2107, 1, 2108, 1, 2109, 1, 2110, 1, + 2111, 1, 2112, 1, 2113, 1, 2114, 1, + 2115, 1, 2116, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2117, 1, 2118, 1, + 1, 1, 1, 2119, 1, 1, 1, 1, + 1, 1, 2120, 2121, 1, 2122, 1, 2123, + 1, 2124, 1, 2125, 1, 2126, 1, 2127, + 1, 2128, 1, 2129, 1, 2130, 1, 2131, + 1, 2132, 1, 2133, 1, 2134, 1, 2135, + 1, 2136, 1, 2137, 1, 2138, 1, 2139, + 1, 2140, 1, 2141, 1, 2142, 1, 2143, + 1, 2144, 1, 2145, 1, 2146, 1, 2147, + 1, 2148, 1, 2149, 1, 2150, 1, 2151, + 1, 2152, 1, 2153, 1, 2154, 1, 2155, + 1, 2156, 1, 2157, 1, 2158, 1, 1, + 1, 1, 2159, 1, 2160, 1, 2161, 1, + 2162, 1, 2163, 1, 2164, 1, 2165, 1, + 2166, 1, 2167, 1, 2168, 1, 2169, 1, + 2170, 1, 2171, 1, 2172, 1, 2173, 1, + 2174, 1, 2175, 1, 2176, 1, 2177, 1, + 2178, 1, 2179, 1, 2180, 1, 2181, 1, + 2182, 1, 2183, 1, 2184, 1, 2185, 1, + 2186, 1, 2187, 1, 2188, 1, 2189, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2190, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2191, 1, + 2192, 1, 2193, 1, 2194, 1, 2195, 1, + 2196, 1, 2197, 1, 2198, 1, 2199, 1, + 2200, 1, 2201, 1, 2202, 1, 2203, 1, + 2204, 1, 2205, 1, 2206, 1, 2207, 1, + 1, 1, 2208, 1, 2209, 1, 2210, 1, + 2211, 1, 2212, 1, 2213, 1, 2214, 1, + 2215, 1, 2216, 1, 2217, 1, 2218, 1, + 2219, 1, 2220, 1, 2221, 1, 2222, 1, + 2223, 1, 2224, 1, 2225, 1, 2226, 1, + 2227, 1, 2228, 1, 2229, 1, 2230, 1, + 2231, 1, 2232, 1, 2233, 1, 1, 1, + 1, 1, 1, 2234, 1, 1, 2235, 1, + 2236, 1, 2237, 1, 2238, 1, 2239, 1, + 2240, 1, 2241, 1, 2242, 1, 2243, 1, + 2244, 1, 1, 1, 2245, 1, 2246, 1, + 2247, 1, 2248, 1, 2249, 1, 2250, 1, + 2251, 1, 2252, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2253, 1, 2254, 1, 2255, 1, 2256, 1, + 2257, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2258, 1, 2259, 1, 2260, 1, + 2261, 1, 2262, 1, 2263, 1, 2264, 1, + 2265, 1, 2266, 1, 2267, 1, 2268, 1, + 2269, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2270, 1, 2271, 1, 2272, 1, + 2273, 1, 2274, 1, 2275, 1, 2276, 2277, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2278, 1, 2279, 1, + 2280, 1, 2281, 1, 2282, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2283, 1, + 2284, 1, 2285, 1, 2286, 1, 2287, 1, + 2288, 1, 2289, 1, 2290, 1, 2291, 1, + 2292, 1, 2293, 1, 2294, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2295, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2296, 2297, 1, 2298, + 1, 2299, 1, 2300, 1, 2301, 1, 2302, + 1, 2303, 1, 2304, 1, 2305, 1, 2306, + 1, 2307, 1, 2308, 1, 2309, 1, 2310, + 1, 2311, 1, 2312, 1, 2313, 1, 2314, + 1, 2315, 1, 2316, 1, 2317, 1, 2318, + 1, 2319, 1, 2320, 1, 2321, 1, 2322, + 1, 2323, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2324, 1, 2325, 1, 2326, + 1, 2327, 1, 2328, 1, 2329, 1, 2330, + 1, 2331, 1, 2332, 1, 2333, 1, 2334, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2335, 2336, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2337, 1, 2338, 1, 2339, 1, 2340, 1, + 2341, 1, 2342, 1, 2343, 1, 2344, 1, + 2345, 1, 2346, 1, 2347, 1, 2348, 1, + 2349, 1, 2350, 1, 2351, 1, 2352, 1, + 2353, 1, 2354, 1, 2355, 1, 2356, 1, + 2357, 1, 2358, 1, 2359, 1, 2360, 1, + 2361, 1, 2362, 1, 2363, 1, 2364, 1, + 2365, 1, 2366, 1, 2367, 1, 2368, 1, + 2369, 1, 2370, 1, 2371, 1, 2372, 1, + 2373, 1, 2374, 1, 2375, 1, 2376, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2377, 1, 2378, 2379, 1, 2380, + 2381, 1, 1, 1, 1, 1, 2382, 1, + 2383, 2384, 1, 2385, 2386, 2387, 2388, 2389, + 1, 2390, 1, 2391, 1, 2392, 1, 2393, + 1, 2394, 1, 2395, 1, 2396, 1, 2397, + 1, 2398, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2399, 1, 2400, 1, 2401, 1, 2402, + 1, 2403, 1, 2404, 1, 2405, 1, 2406, + 1, 2407, 1, 2408, 1, 2409, 1, 2410, + 1, 2411, 1, 2412, 1, 2413, 1, 2414, + 1, 1, 1, 2415, 1, 1, 1, 2416, + 1, 2417, 1, 2418, 1, 2419, 1, 2420, + 1, 2421, 1, 2422, 1, 2423, 1, 2424, + 1, 2425, 1, 2426, 1, 2427, 1, 2428, + 1, 2429, 1, 2430, 1, 2431, 1, 2432, + 1, 2433, 1, 2434, 1, 2435, 1, 2436, + 1, 2437, 1, 2438, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2439, 1, 2440, 1, 2441, 1, 2442, 1, + 2443, 1, 2444, 1, 2445, 1, 2446, 1, + 2447, 1, 2448, 1, 2449, 1, 2450, 1, + 2451, 1, 2452, 1, 2453, 1, 2454, 1, + 2455, 1, 2456, 1, 2457, 1, 1, 1, + 1, 1, 1, 1, 1, 2458, 1, 2459, + 1, 2460, 1, 2461, 1, 2462, 1, 2463, + 1, 2464, 1, 2465, 2466, 1, 2467, 1, + 2468, 1, 2469, 1, 2470, 1, 2471, 1, + 2472, 1, 2473, 1, 2474, 1, 2475, 1, + 2476, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2477, 1, + 2478, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2479, 1, 2480, 1, 2481, 1, 2482, + 1, 2483, 1, 2484, 1, 1, 1, 1, + 1, 2485, 1, 2486, 1, 2487, 1, 2488, + 1, 2489, 1, 2490, 1, 2491, 1, 2492, + 1, 2493, 1, 2494, 1, 2495, 1, 2496, + 1, 2497, 1, 2498, 1, 2499, 1, 2500, + 1, 2501, 1, 2502, 1, 1, 2503, 1, + 2504, 2505, 1, 1, 2506, 1, 1, 2507, + 1, 1, 2508, 2509, 1, 2510, 1, 2511, + 1, 2512, 1, 2513, 1, 2514, 1, 2515, + 1, 2516, 1, 2517, 1, 2518, 1, 2519, + 1, 2520, 1, 2521, 1, 2522, 1, 2523, + 1, 2524, 1, 2525, 1, 2526, 1, 2527, + 1, 2528, 1, 2529, 1, 2530, 1, 2531, + 1, 2532, 1, 1, 1, 1, 1, 1, + 2533, 1, 2534, 1, 2535, 1, 2536, 1, + 2537, 1, 2538, 1, 2539, 1, 2540, 1, + 2541, 1, 2542, 1, 2543, 1, 2544, 1, + 2545, 1, 2546, 1, 2547, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2548, 1, + 1, 1, 2549, 1, 1, 1, 1, 1, + 2550, 1, 2551, 1, 2552, 1, 2553, 1, + 2554, 1, 2555, 1, 2556, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2557, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2558, 2559, 1, 2560, + 1, 2561, 1, 2562, 1, 2563, 1, 2564, + 1, 2565, 1, 2566, 1, 2567, 1, 2568, + 1, 2569, 1, 2570, 1, 2571, 1, 2572, + 1, 2573, 1, 2574, 1, 2575, 1, 2576, + 1, 2577, 1, 2578, 1, 2579, 1, 2580, + 1, 2581, 1, 2582, 1, 2583, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2584, 1, 2585, 1, 2586, 1, 2587, + 1, 2588, 1, 2589, 1, 2590, 1, 2591, + 1, 2592, 1, 2593, 1, 2594, 1, 2595, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2596, 1, 2597, + 1, 2598, 1, 2599, 1, 1, 1, 1, + 1, 2600, 1, 2601, 1, 2602, 1, 2603, + 1, 2604, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2605, 1, 1, 1, 1, 1, + 1, 1, 1, 2606, 1, 1, 1, 2607, + 1, 2608, 1, 2609, 1, 2610, 1, 2611, + 1, 2612, 1, 2613, 1, 2614, 1, 2615, + 1, 2616, 1, 2617, 1, 2618, 1, 1, + 2619, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2620, 1, 2621, 1, + 2622, 2623, 1, 2624, 2625, 1, 1, 1, + 1, 1, 2626, 1, 1, 2627, 2628, 1, + 2629, 1, 2630, 1, 2631, 1, 2632, 1, + 2633, 1, 2634, 1, 2635, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2636, + 1, 1, 1, 2637, 1, 2638, 1, 2639, + 1, 2640, 1, 2641, 1, 2642, 1, 2643, + 1, 2644, 1, 2645, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2646, 1, 2647, 1, + 2648, 1, 2649, 1, 1, 1, 2650, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2651, 1, 2652, 1, 2653, 1, + 2654, 1, 2655, 1, 2656, 1, 2657, 1, + 2658, 1, 2659, 1, 2660, 1, 2661, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2662, 1, 2663, 1, 2664, 1, 2665, + 1, 2666, 1, 2667, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2668, 1, 2669, 1, 1, + 1, 1, 2670, 1, 2671, 1, 2672, 1, + 2673, 1, 2674, 1, 2675, 1, 2676, 1, + 2677, 1, 2678, 1, 2679, 1, 2680, 1, + 2681, 1, 2682, 1, 2683, 1, 2684, 1, + 2685, 1, 2686, 1, 2687, 1, 2688, 1, + 2689, 1, 2690, 1, 2691, 1, 2692, 1, + 2693, 1, 2694, 1, 2695, 1, 2696, 1, + 2697, 1, 2698, 1, 2699, 1, 2700, 1, + 2701, 1, 2702, 1, 2703, 1, 2704, 1, + 2705, 1, 2706, 1, 2707, 1, 2708, 2709, + 1, 2710, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2711, + 2712, 2713, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2714, 1, 2715, 1, + 1, 1, 2716, 1, 2717, 1, 2718, 1, + 2719, 1, 2720, 1, 2721, 1, 2722, 1, + 2723, 1, 2724, 1, 2725, 1, 2726, 1, + 2727, 1, 2728, 1, 2729, 1, 2730, 1, + 2731, 1, 1, 1, 1, 1, 1, 2732, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2733, 1, 2734, 1, 2735, 1, 2736, + 1, 2737, 1, 2738, 1, 2739, 1, 2740, + 1, 2741, 1, 2742, 1, 2743, 1, 2744, + 1, 2745, 1, 2746, 1, 2747, 1, 2748, + 1, 2749, 1, 2750, 1, 2751, 1, 2752, + 1, 2753, 1, 2754, 1, 2755, 1, 2756, + 1, 2757, 1, 2758, 1, 2759, 1, 2760, + 1, 2761, 1, 2762, 1, 2763, 1, 2764, + 1, 2765, 1, 2766, 1, 2767, 1, 2768, + 1, 2769, 1, 2770, 1, 2771, 1, 2772, + 1, 2773, 1, 2774, 1, 2775, 1, 2776, + 1, 2777, 1, 2778, 1, 2779, 1, 2780, + 1, 2781, 1, 2782, 1, 2783, 1, 2784, + 1, 1, 1, 1, 1, 1, 2785, 1, + 2786, 1, 2787, 1, 2788, 1, 2789, 1, + 2790, 1, 2791, 1, 2792, 1, 2793, 1, + 2794, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2795, 1, 2796, + 1, 2797, 1, 1, 1, 1, 1, 2798, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2799, 1, 2800, 1, + 2801, 1, 2802, 1, 2803, 1, 2804, 1, + 2805, 1, 2806, 1, 2807, 1, 2808, 1, + 2809, 1, 2810, 1, 2811, 1, 2812, 1, + 2813, 1, 2814, 1, 2815, 1, 2816, 1, + 2817, 1, 1, 1, 1, 1, 1, 2818, + 1, 1, 2819, 1, 2820, 1, 2821, 1, + 2822, 1, 2823, 1, 2824, 1, 2825, 1, + 2826, 1, 2827, 1, 2828, 1, 2829, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2830, 1, + 2831, 1, 2832, 1, 2833, 1, 2834, 1, + 2835, 1, 2836, 1, 2837, 1, 2838, 1, + 2839, 1, 2840, 1, 2841, 1, 2842, 1, + 2843, 1, 2844, 1, 2845, 1, 2846, 1, + 2847, 1, 2848, 1, 2849, 1, 2850, 1, + 2851, 1, 2852, 1, 2853, 1, 2854, 1, + 2855, 1, 2856, 1, 1, 1, 1, 1, + 1, 2857, 1, 2858, 1, 2859, 1, 2860, + 1, 2861, 1, 2862, 1, 2863, 1, 2864, + 1, 2865, 1, 2866, 1, 1, 1, 1, + 1, 1, 2867, 1, 2868, 1, 2869, 1, + 2870, 1, 2871, 1, 2872, 1, 2873, 1, + 2874, 1, 2875, 1, 2876, 1, 1, 1, + 1, 2877, 1, 2878, 1, 2879, 1, 2880, + 1, 2881, 1, 2882, 1, 2883, 1, 2884, + 1, 2885, 1, 2886, 1, 2887, 1, 2888, + 1, 2889, 1, 2890, 1, 2891, 1, 2892, + 1, 2893, 1, 2894, 1, 2895, 1, 2896, + 1, 2897, 1, 2898, 1, 2899, 1, 2900, + 1, 1, 1, 1, 2901, 1, 2902, 1, + 2903, 1, 2904, 1, 2905, 1, 2906, 1, + 2907, 1, 2908, 1, 2909, 1, 2910, 1, + 2911, 1, 2912, 1, 2913, 1, 2914, 1, + 2915, 1, 1, 1, 1, 1, 1, 2916, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2917, 1, 2918, 1, 1, 2919, 1, + 2920, 2921, 1, 1, 1, 2922, 1, 2923, + 1, 2924, 1, 2925, 2926, 2927, 1, 2928, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2929, + 1, 2930, 1, 2931, 1, 2932, 1, 2933, + 1, 2934, 1, 2935, 1, 2936, 1, 2937, + 1, 2938, 1, 2939, 1, 2940, 1, 2941, + 1, 2942, 1, 2943, 1, 2944, 1, 2945, + 1, 2946, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2947, + 1, 1, 1, 2948, 1, 1, 1, 2949, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2950, + 1, 2951, 1, 2952, 1, 2953, 1, 2954, + 1, 2955, 1, 2956, 1, 2957, 1, 2958, + 1, 2959, 1, 2960, 1, 2961, 1, 2962, + 1, 2963, 1, 2964, 1, 2965, 1, 2966, + 1, 2967, 1, 2968, 1, 1, 1, 1, + 1, 1, 1, 2969, 1, 1, 1, 1, + 1, 2970, 1, 1, 2971, 1, 2972, 1, + 2973, 1, 2974, 1, 2975, 1, 2976, 1, + 2977, 1, 2978, 1, 2979, 1, 2980, 1, + 2981, 1, 2982, 1, 2983, 1, 2984, 1, + 2985, 1, 2986, 1, 2987, 1, 2988, 1, + 2989, 1, 2990, 1, 2991, 1, 2992, 1, + 2993, 1, 2994, 1, 2995, 1, 2996, 1, + 2997, 1, 2998, 1, 2999, 1, 3000, 1, + 3001, 1, 3002, 1, 3003, 1, 3004, 1, + 3005, 1, 3006, 1, 3007, 1, 3008, 1, + 3009, 1, 3010, 1, 3011, 1, 3012, 1, + 3013, 1, 3014, 1, 3015, 1, 3016, 1, + 3017, 1, 3018, 1, 3019, 1, 3020, 1, + 3021, 1, 3022, 1, 3023, 1, 3024, 1, + 1, 3025, 1, 3026, 1, 3027, 1, 3028, + 1, 3029, 1, 3030, 1, 3031, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3032, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3033, 1, 3034, + 1, 3035, 1, 3036, 1, 3037, 1, 3038, + 1, 3039, 1, 3040, 1, 3041, 1, 3042, + 1, 3043, 1, 3044, 1, 3045, 1, 3046, + 1, 3047, 1, 3048, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3049, 1, 3050, 1, 3051, 1, 3052, + 1, 3053, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3054, 1, 3055, 1, 3056, + 1, 3057, 1, 3058, 1, 3059, 1, 3060, + 1, 3061, 1, 3062, 1, 3063, 1, 3064, + 1, 3065, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3066, 1, 3067, 1, 3068, + 1, 3069, 1, 3070, 1, 3071, 1, 3072, + 1, 3073, 1, 3074, 1, 3075, 1, 3076, + 1, 3077, 1, 3078, 1, 3079, 1, 3080, + 1, 3081, 1, 3082, 1, 3083, 3084, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3085, 1, 1, 3086, 1, 3087, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3088, 1, 3089, + 1, 3090, 1, 3091, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3092, 1, 3093, + 1, 3094, 1, 3095, 1, 3096, 1, 3097, + 1, 3098, 1, 1, 1, 1, 3099, 1, + 3100, 1, 3101, 1, 3102, 1, 3103, 1, + 3104, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3105, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3106, 3107, 1, 3108, 1, 3109, 1, 3110, + 1, 3111, 1, 3112, 1, 3113, 1, 3114, + 1, 3115, 1, 3116, 1, 3117, 1, 3118, + 1, 3119, 1, 3120, 1, 3121, 1, 3122, + 1, 3123, 1, 3124, 1, 3125, 1, 3126, + 1, 3127, 1, 3128, 1, 3129, 1, 3130, + 1, 3131, 1, 3132, 1, 3133, 1, 3134, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3135, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3136, + 1, 3137, 1, 3138, 1, 3139, 1, 3140, + 1, 3141, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3142, 1, 3143, 1, 3144, + 1, 3145, 1, 3146, 1, 3147, 1, 3148, + 1, 3149, 1, 3150, 1, 3151, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3152, + 3153, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3154, 1, + 3155, 1, 1, 3156, 1, 3157, 3158, 1, + 1, 1, 1, 1, 3159, 1, 1, 3160, + 3161, 1, 3162, 1, 3163, 1, 3164, 1, + 3165, 1, 3166, 1, 3167, 1, 3168, 1, + 3169, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3170, 1, 3171, 1, 3172, + 1, 3173, 1, 3174, 1, 3175, 1, 3176, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3177, 1, 3178, 1, 3179, 1, + 3180, 1, 1, 1, 3181, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3182, 1, 3183, 1, 3184, 1, 3185, 1, + 3186, 1, 3187, 1, 3188, 1, 3189, 1, + 3190, 1, 3191, 1, 3192, 1, 3193, 1, + 3194, 1, 1, 1, 3195, 1, 3196, 1, + 3197, 1, 3198, 1, 3199, 1, 3200, 1, + 3201, 1, 3202, 1, 3203, 1, 3204, 1, + 3205, 1, 3206, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3207, 1, 3208, + 1, 3209, 1, 3210, 1, 3211, 1, 3212, + 1, 3213, 1, 3214, 1, 3215, 1, 3216, + 1, 3217, 1, 3218, 1, 3219, 1, 3220, + 1, 3221, 1, 3222, 1, 3223, 1, 3224, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3225, 3226, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3227, 1, 3228, 1, 3229, 1, 3230, 1, + 3231, 1, 3232, 1, 3233, 1, 3234, 1, + 3235, 1, 3236, 1, 3237, 1, 3238, 1, + 3239, 1, 3240, 1, 3241, 1, 3242, 1, + 3243, 1, 3244, 1, 3245, 1, 3246, 1, + 3247, 1, 3248, 1, 3249, 1, 3250, 1, + 3251, 1, 3252, 1, 3253, 1, 3254, 1, + 3255, 1, 3256, 1, 3257, 1, 3258, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3259, + 1, 3260, 1, 3261, 1, 3262, 1, 3263, + 1, 3264, 1, 3265, 1, 3266, 3267, 3268, + 3269, 1, 3270, 3271, 1, 1, 1, 1, + 1, 3272, 3273, 3274, 3275, 1, 3276, 3277, + 3278, 3279, 1, 3280, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3281, 1, 3282, 1, 3283, 1, + 3284, 1, 3285, 1, 3286, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3287, 1, 3288, 1, 3289, 1, 3290, 1, + 3291, 1, 3292, 1, 3293, 1, 3294, 1, + 3295, 1, 3296, 1, 3297, 1, 3298, 1, + 3299, 1, 3300, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3301, 1, 3302, 1, 3303, 1, + 3304, 1, 3305, 1, 3306, 1, 3307, 1, + 3308, 1, 3309, 1, 3310, 1, 3311, 1, + 3312, 1, 3313, 1, 3314, 1, 3315, 1, + 3316, 1, 3317, 1, 3318, 1, 3319, 1, + 3320, 1, 1, 1, 1, 3321, 1, 3322, + 1, 3323, 1, 3324, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3325, 1, 3326, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3327, 1, 3328, 1, + 3329, 1, 3330, 1, 3331, 1, 3332, 1, + 1, 1, 1, 1, 3333, 1, 3334, 1, + 3335, 1, 3336, 1, 3337, 1, 3338, 1, + 3339, 1, 3340, 1, 3341, 1, 3342, 1, + 3343, 1, 3344, 1, 3345, 1, 3346, 1, + 3347, 1, 3348, 1, 3349, 1, 3350, 1, + 3351, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3352, 1, 3353, + 1, 3354, 1, 3355, 1, 3356, 1, 3357, + 1, 1, 1, 1, 1, 1, 1, 1, + 3358, 1, 3359, 1, 3360, 1, 3361, 1, + 3362, 1, 3363, 1, 3364, 1, 1, 3365, + 3366, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3367, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3368, 1, 1, 3369, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3370, 1, 1, 3371, 1, + 3372, 1, 3373, 1, 3374, 1, 3375, 1, + 3376, 1, 1, 1, 1, 1, 1, 3377, + 1, 3378, 1, 3379, 1, 3380, 1, 3381, + 1, 3382, 1, 3383, 1, 3384, 1, 3385, + 1, 3386, 1, 3387, 1, 3388, 1, 3389, + 1, 3390, 1, 3391, 1, 3392, 1, 3393, + 1, 3394, 1, 3395, 1, 3396, 1, 3397, + 1, 3398, 1, 3399, 1, 3400, 1, 3401, + 1, 3402, 1, 3403, 1, 3404, 1, 3405, + 1, 3406, 1, 3407, 1, 3408, 1, 3409, + 1, 3410, 1, 3411, 1, 3412, 1, 3413, + 1, 1, 1, 1, 1, 3414, 1, 3415, + 1, 3416, 1, 3417, 1, 3418, 1, 3419, + 1, 3420, 1, 3421, 1, 3422, 1, 3423, + 1, 3424, 1, 3425, 1, 3426, 1, 3427, + 1, 3428, 1, 3429, 1, 3430, 1, 3431, + 1, 3432, 1, 3433, 1, 3434, 1, 3435, + 1, 3436, 1, 1, 1, 1, 1, 3437, + 1, 3438, 1, 3439, 1, 3440, 1, 3441, + 1, 3442, 1, 3443, 1, 3444, 1, 3445, + 1, 3446, 1, 3447, 1, 3448, 1, 3449, + 1, 3450, 1, 3451, 1, 3452, 1, 3453, + 1, 3454, 1, 3455, 1, 3456, 1, 3457, + 1, 3458, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3459, 1, 3460, 1, 3461, 1, + 3462, 1, 3463, 1, 3464, 1, 3465, 1, + 3466, 1, 3467, 1, 3468, 1, 3469, 1, + 3470, 1, 3471, 1, 3472, 1, 3473, 1, + 3474, 1, 3475, 1, 3476, 1, 3477, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3478, 3479, 3480, 3481, + 3482, 1, 1, 1, 1, 1, 1, 1, + 1, 3483, 1, 1, 1, 3484, 1, 1, + 3485, 1, 3486, 1, 3487, 1, 3488, 1, + 3489, 1, 3490, 1, 3491, 1, 3492, 1, + 3493, 1, 3494, 1, 3495, 1, 3496, 1, + 3497, 1, 3498, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3499, 1, 3500, 1, 3501, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3502, 1, 3503, 1, + 3504, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3505, 1, 1, 1, 1, 3506, + 1, 3507, 1, 3508, 1, 3509, 1, 3510, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3511, 1, 3512, + 1, 3513, 1, 3514, 1, 3515, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3516, + 1, 1, 1, 1, 1, 1, 3517, 3518, + 1, 3519, 1, 3520, 1, 3521, 1, 3522, + 1, 3523, 1, 3524, 1, 3525, 1, 3526, + 1, 3527, 1, 3528, 1, 3529, 1, 3530, + 1, 3531, 1, 3532, 1, 3533, 1, 3534, + 1, 3535, 1, 3536, 1, 3537, 1, 3538, + 1, 3539, 1, 3540, 1, 3541, 1, 3542, + 1, 3543, 1, 3544, 1, 3545, 1, 3546, + 1, 3547, 1, 3548, 1, 3549, 1, 3550, + 1, 3551, 1, 3552, 1, 3553, 1, 3554, + 1, 3555, 1, 3556, 1, 3557, 1, 3558, + 1, 3559, 1, 3560, 1, 3561, 1, 3562, + 1, 3563, 1, 3564, 3565, 1, 1, 1, + 1, 1, 1, 1, 1, 3566, 1, 1, + 1, 3567, 1, 3568, 1, 3569, 1, 3570, + 1, 3571, 1, 3572, 1, 3573, 1, 3574, + 1, 3575, 1, 3576, 1, 3577, 1, 3578, + 1, 3579, 1, 3580, 1, 3581, 1, 3582, + 1, 3583, 1, 3584, 1, 1, 3585, 1, + 1, 1, 1, 1, 3586, 1, 1, 1, + 3587, 1, 3588, 1, 3589, 1, 3590, 1, + 3591, 1, 3592, 1, 3593, 1, 3594, 1, + 3595, 1, 3596, 1, 3597, 1, 1, 1, + 1, 1, 1, 1, 3598, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3599, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3600, 1, 3601, 1, + 1, 3602, 1, 1, 1, 1, 1, 1, + 1, 1, 3603, 1, 1, 1, 3604, 1, + 3605, 1, 3606, 1, 3607, 1, 3608, 1, + 3609, 1, 3610, 1, 3611, 1, 3612, 1, + 3613, 1, 3614, 1, 3615, 1, 3616, 1, + 3617, 1, 3618, 1, 3619, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3620, 1, 3621, 1, + 3622, 1, 3623, 1, 3624, 1, 3625, 1, + 3626, 1, 3627, 1, 3628, 1, 3629, 1, + 3630, 1, 3631, 1, 3632, 1, 3633, 1, + 3634, 1, 3635, 1, 3636, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3637, 1, 3638, + 3639, 3640, 3641, 1, 1, 1, 1, 1, + 1, 1, 1, 3642, 1, 1, 1, 3643, + 1, 3644, 1, 3645, 1, 3646, 1, 3647, + 1, 3648, 1, 3649, 1, 3650, 1, 3651, + 1, 3652, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3653, 1, 3654, 1, 3655, 1, 3656, + 1, 3657, 1, 3658, 1, 3659, 1, 3660, + 1, 3661, 1, 3662, 1, 3663, 1, 3664, + 1, 3665, 1, 3666, 1, 3667, 1, 3668, + 1, 3669, 1, 3670, 1, 3671, 1, 3672, + 1, 3673, 1, 3674, 1, 3675, 1, 3676, + 1, 3677, 1, 3678, 1, 3679, 1, 3680, + 1, 3681, 1, 3682, 1, 3683, 1, 3684, + 1, 3685, 1, 3686, 3687, 3688, 1, 3689, + 3690, 3691, 1, 1, 1, 1, 3692, 3693, + 3694, 3695, 3696, 1, 3697, 3698, 3699, 3700, + 1, 3701, 1, 3702, 1, 3703, 1, 3704, + 1, 3705, 1, 3706, 1, 3707, 1, 3708, + 1, 3709, 1, 3710, 1, 3711, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3712, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3713, 1, + 1, 1, 1, 3714, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3715, + 1, 1, 1, 3716, 1, 3717, 1, 3718, + 1, 3719, 1, 3720, 1, 3721, 1, 3722, + 1, 3723, 1, 3724, 1, 3725, 1, 3726, + 1, 3727, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3728, 1, 3729, 1, 3730, 1, 3731, 1, + 3732, 1, 3733, 1, 3734, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3735, + 1, 3736, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3737, 1, 3738, 1, 3739, + 1, 3740, 1, 3741, 1, 3742, 1, 3743, + 1, 3744, 1, 3745, 1, 3746, 1, 3747, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3748, 1, + 3749, 1, 1, 1, 1, 1, 1, 1, + 1, 3750, 1, 3751, 1, 3752, 1, 3753, + 1, 3754, 1, 3755, 1, 1, 3756, 1, + 3757, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3758, 1, + 1, 3759, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3760, 1, 1, 3761, 1, 3762, 1, 3763, + 1, 3764, 1, 3765, 1, 3766, 1, 3767, + 1, 3768, 1, 3769, 1, 3770, 1, 3771, + 1, 3772, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3773, 1, 1, 1, 1, + 1, 1, 3774, 3775, 1, 1, 1, 1, + 3776, 3777, 1, 1, 1, 1, 1, 1, + 3778, 1, 3779, 1, 3780, 1, 3781, 1, + 3782, 1, 3783, 1, 3784, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3785, 1, 3786, 3787, 3788, 3789, + 3790, 3791, 3792, 3793, 1, 3794, 1, 3795, + 1, 3796, 1, 3797, 1, 3798, 1, 3799, + 1, 3800, 1, 3801, 1, 3802, 1, 3803, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3804, 1, 3805, 1, 3806, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3807, + 1, 3808, 1, 3809, 1, 1, 1, 3810, + 1, 3811, 1, 3812, 1, 3813, 1, 3814, + 1, 3815, 1, 3816, 1, 3817, 1, 3818, + 1, 1, 1, 1, 1, 1, 1, 1, + 3819, 1, 3820, 1, 3821, 1, 3822, 1, + 3823, 1, 3824, 1, 3825, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3826, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3827, 1, 1, 1, 3828, 1, + 1, 1, 3829, 1, 1, 1, 1, 1, + 3830, 3831, 1, 3832, 1, 3833, 1, 3834, + 1, 3835, 1, 3836, 1, 3837, 1, 3838, + 1, 3839, 1, 3840, 1, 3841, 1, 3842, + 1, 3843, 1, 3844, 1, 3845, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3846, + 1, 3847, 1, 3848, 1, 3849, 1, 3850, + 1, 3851, 1, 3852, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3853, 1, 1, 1, + 1, 3854, 1, 3855, 1, 3856, 1, 3857, + 1, 3858, 1, 3859, 1, 3860, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3861, + 1, 3862, 1, 3863, 1, 3864, 1, 3865, + 1, 3866, 1, 3867, 1, 3868, 1, 3869, + 1, 3870, 1, 1, 1, 1, 1, 3871, + 1, 3872, 1, 3873, 1, 3874, 1, 3875, + 1, 3876, 1, 3877, 1, 3878, 1, 3879, + 1, 3880, 1, 3881, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3882, 3883, + 3884, 3885, 3886, 3887, 1, 1, 3888, 1, + 3889, 3890, 1, 3891, 3892, 3893, 1, 3894, + 3895, 1, 3896, 1, 3897, 1, 3898, 1, + 3899, 1, 3900, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3901, 1, 3902, 1, 3903, 1, 3904, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3905, 1, 1, 3906, 1, 3907, + 1, 3908, 1, 3909, 1, 3910, 1, 3911, + 1, 3912, 1, 3913, 1, 3914, 1, 3915, + 1, 3916, 1, 3917, 1, 3918, 1, 3919, + 1, 3920, 1, 3921, 1, 3922, 1, 3923, + 1, 3924, 1, 3925, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3926, 1, 3927, + 1, 3928, 1, 3929, 3930, 1, 3931, 1, + 3932, 1, 3933, 1, 3934, 1, 3935, 1, + 3936, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3937, 1, 3938, 1, + 3939, 1, 3940, 1, 3941, 1, 3942, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3943, + 1, 3944, 1, 3945, 1, 3946, 1, 3947, + 1, 3948, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3949, 1, 3950, 1, 3951, + 1, 3952, 1, 3953, 1, 3954, 1, 3955, + 1, 3956, 1, 3957, 1, 3958, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3959, + 1, 1, 3960, 1, 3961, 1, 3962, 1, + 3963, 1, 3964, 1, 3965, 1, 3966, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 3967, 1, 3968, 1, 3969, 1, 3970, 1, + 3971, 1, 3972, 1, 3973, 1, 3974, 1, + 3975, 1, 3976, 1, 3977, 1, 3978, 1, + 3979, 1, 3980, 1, 3981, 1, 1, 1, + 1, 1, 1, 3982, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3983, 1, 3984, 1, 3985, 1, + 3986, 1, 3987, 1, 3988, 1, 3989, 1, + 3990, 1, 3991, 1, 3992, 1, 3993, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3994, 1, 1, 1, 3995, 3996, + 3997, 3998, 3999, 1, 4000, 1, 1, 1, + 1, 1, 1, 1, 4001, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4002, 1, 4003, 1, 4004, 1, 4005, 1, + 4006, 1, 4007, 1, 4008, 1, 4009, 1, + 4010, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 4011, 1, 1, 1, + 4012, 1, 4013, 1, 4014, 1, 4015, 1, + 4016, 1, 4017, 1, 4018, 1, 4019, 1, + 4020, 1, 4021, 1, 4022, 1, 4023, 1, + 4024, 1, 4025, 1, 1, 4026, 1, 4027, + 1, 4028, 1, 4029, 1, 4030, 1, 4031, + 1, 4032, 1, 4033, 1, 4034, 1, 4035, + 1, 4036, 1, 4037, 1, 4038, 1, 4039, + 1, 4040, 1, 4041, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 4042, 1, 4043, 1, + 4044, 1, 4045, 1, 4046, 1, 4047, 1, + 4048, 1, 4049, 1, 4050, 1, 4051, 1, + 4052, 1, 4053, 1, 4054, 1, 4055, 1, + 4056, 1, 4057, 1, 4058, 1, 4059, 1, + 4060, 1, 4061, 1, 4062, 1, 4063, 1, + 4064, 1, 4065, 1, 4066, 1, 4067, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4068, 1, 1, 1, 4069, 1, 4070, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4071, 1, 4072, 1, 4073, 1, 1, + 1, 1, 1, 1, 4074, 4075, 1, 4076, + 1, 4077, 1, 4078, 1, 4079, 1, 4080, + 1, 4081, 1, 4082, 1, 4083, 1, 4084, + 1, 4085, 1, 4086, 1, 4087, 1, 4088, + 1, 4089, 1, 4090, 1, 4091, 1, 4092, + 1, 4093, 1, 4094, 1, 4095, 1, 4096, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4097, 1, 1, 1, 1, 1, 1, 1, + 4098, 1, 1, 1, 1, 1, 4099, 1, + 4100, 1, 4101, 1, 4102, 1, 4103, 1, + 4104, 1, 4105, 1, 4106, 1, 4107, 1, + 4108, 1, 4109, 1, 4110, 1, 4111, 1, + 4112, 1, 4113, 1, 4114, 1, 4115, 1, + 4116, 1, 4117, 1, 4118, 1, 4119, 1, + 4120, 1, 4121, 1, 4122, 1, 4123, 1, + 4124, 1, 4125, 1, 4126, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4127, 1, + 4128, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4129, 1, + 4130, 1, 4131, 1, 4132, 1, 4133, 1, + 4134, 1, 4135, 1, 4136, 1, 1, 1, + 4137, 1, 1, 4138, 4139, 1, 4140, 1, + 4141, 1, 4142, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4143, 1, 4144, 1, 4145, + 1, 4146, 1, 4147, 1, 4148, 1, 4149, + 1, 4150, 1, 4151, 1, 1, 1, 4152, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 4153, 4154, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4155, 1, 4156, 1, 1, 1, 4157, + 1, 1, 1, 1, 4158, 1, 1, 4159, + 1, 1, 1, 4160, 4161, 4162, 1, 4163, + 1, 1, 1, 1, 1, 4164, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4165, + 1, 1, 1, 1, 1, 4166, 1, 4167, + 1, 4168, 1, 4169, 1, 4170, 1, 4171, + 1, 1, 1, 1, 1, 1, 1, 1, + 4172, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4173, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4174, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4175, 1, 4176, 1, 4177, 1, 4178, + 1, 4179, 1, 4180, 1, 1, 1, 1, + 1, 4181, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4182, 1, 1, 1, 1, + 1, 4183, 1, 4184, 1, 4185, 1, 4186, + 1, 4187, 1, 4188, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4189, 1, 1, 1, 4190, 1, 1, 1, + 1, 1, 4191, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4192, 1, 1, 1, 4193, 1, 1, 1, + 1, 1, 4194, 1, 4195, 1, 4196, 1, + 4197, 1, 4198, 1, 4199, 1, 4200, 1, + 4201, 1, 4202, 1, 4203, 1, 4204, 1, + 1, 1, 1, 1, 4205, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4206, 1, + 1, 1, 1, 1, 4207, 1, 4208, 1, + 4209, 1, 4210, 1, 4211, 1, 4212, 1, + 1, 1, 1, 1, 1, 1, 1, 4213, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4214, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4215, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4216, 1, 4217, 1, 4218, 1, 4219, 1, + 4220, 1, 4221, 1, 4222, 1, 4223, 1, + 4224, 1, 4225, 1, 4226, 1, 4227, 1, + 4228, 1, 4229, 1, 4230, 1, 4231, 1, + 4232, 1, 4233, 1, 4234, 1, 4235, 1, + 1, 1, 1, 1, 4236, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4237, 1, + 1, 1, 1, 1, 4238, 1, 4239, 1, + 4240, 1, 4241, 1, 4242, 1, 4243, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4244, 1, 1, 1, 4245, + 1, 1, 1, 1, 1, 4246, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4247, 1, 1, 1, 4248, + 1, 1, 1, 1, 1, 4249, 1, 4250, + 1, 4251, 1, 4252, 1, 4253, 1, 4254, + 1, 4255, 1, 4256, 1, 4257, 1, 4258, + 1, 4259, 1, 4260, 1, 4261, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4262, 1, + 4263, 1, 4264, 1, 4265, 1, 4266, 1, + 4267, 1, 4268, 1, 4269, 1, 4270, 1, + 1, 1, 4271, 1, 1, 1, 1, 1, + 4272, 1, 4273, 1, 4274, 1, 4275, 1, + 4276, 1, 4277, 1, 4278, 1, 4279, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4280, 1, 4281, 1, 4282, 1, 4283, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4284, 1, 1, + 1, 1, 1, 4285, 1, 4286, 1, 4287, + 1, 4288, 1, 4289, 1, 4290, 1, 4291, + 4292, 1, 4293, 1, 4294, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4295, 1, + 4296, 1, 4297, 1, 4298, 1, 4299, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4300, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4301, 1, 4302, 1, 4303, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4304, 1, 4305, 1, 4306, 1, + 4307, 4308, 4309, 4310, 1, 4311, 4312, 1, + 1, 4313, 1, 1, 4314, 1, 1, 4315, + 4316, 4317, 4318, 1, 4319, 1, 4320, 1, + 4321, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4322, 1, 4323, + 1, 4324, 1, 4325, 1, 4326, 1, 4327, + 1, 4328, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4329, + 4330, 4331, 4332, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4333, 1, 4334, 1, 4335, 1, 4336, + 1, 4337, 1, 4338, 1, 4339, 1, 4340, + 1, 4341, 1, 4342, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4343, + 1, 4344, 1, 4345, 1, 4346, 1, 4347, + 1, 4348, 1, 4349, 1, 4350, 1, 4351, + 1, 4352, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4353, 1, 4354, 1, 4355, + 1, 4356, 1, 4357, 1, 4358, 1, 1, + 1, 4359, 1, 1, 1, 4360, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4361, 1, 4362, 1, 4363, 1, 4364, + 1, 4365, 1, 4366, 1, 4367, 1, 4368, + 1, 4369, 1, 4370, 1, 4371, 1, 4372, + 1, 4373, 1, 4374, 1, 4375, 1, 4376, + 1, 4377, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4378, 1, 4379, 1, 4380, 1, 4381, + 1, 4382, 1, 4383, 1, 4384, 1, 1, + 1, 1, 1, 1, 1, 1, 4385, 4386, + 1, 4387, 1, 4388, 1, 4389, 1, 4390, + 1, 4391, 1, 4392, 1, 4393, 1, 4394, + 1, 4396, 4395, 4397, 4395, 4398, 4395, 4399, + 4395, 4400, 4395, 4401, 1, 4402, 1, 4403, + 1, 4404, 1, 1, 1, 4405, 1, 4406, + 1, 4407, 1, 4408, 1, 4409, 1, 4410, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4411, 1, 4412, 1, 4413, 1, 4414, + 1, 4415, 1, 4416, 1, 4417, 1, 4418, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4419, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4420, + 1, 4421, 4422, 1, 1, 1, 1, 1, + 1, 4423, 1, 1, 1, 1, 1, 4424, + 1, 4425, 1, 4426, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4427, 1, 1, + 1, 1, 1, 1, 4428, 1, 4429, 1, + 4430, 1, 4431, 1, 4432, 1, 1, 4433, + 1, 4434, 1, 4435, 1, 4436, 1, 4437, + 1, 4438, 1, 1, 1, 1, 1, 4439, + 1, 4440, 1, 4441, 1, 4442, 1, 4443, + 1, 4444, 1, 4445, 1, 4446, 1, 4447, + 1, 4448, 1, 4449, 4450, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4451, 1, 4452, 4453, 1, 4454, + 1, 4455, 1, 4456, 1, 4457, 1, 4458, + 1, 4459, 1, 4460, 1, 4461, 1, 4462, + 1, 4463, 1, 4464, 1, 4465, 1, 4466, + 1, 4467, 1, 4468, 1, 4469, 1, 4470, + 1, 4471, 1, 4472, 1, 4473, 1, 4474, + 1, 4475, 1, 4476, 1, 4477, 1, 4478, + 1, 4479, 1, 4480, 1, 4481, 1, 4482, + 1, 4483, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4484, 1, 4485, 1, 4486, + 1, 4487, 1, 4488, 4489, 4490, 1, 4491, + 1, 4492, 1, 4493, 1, 4494, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4495, + 1, 4496, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4497, + 1, 4498, 1, 4499, 1, 1, 4500, 1, + 4501, 1, 4502, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4503, 1, 4504, 1, 4505, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4506, 1, 1, 1, 1, 1, + 4507, 1, 4508, 1, 4509, 1, 4510, 1, + 4511, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4512, 1, 4513, 1, 4514, + 1, 4515, 1, 4516, 1, 4517, 1, 4518, + 1, 4519, 1, 4520, 1, 4521, 1, 4522, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4523, 1, 4524, 1, 4525, 1, 4526, 1, + 4527, 1, 4528, 1, 4529, 1, 4530, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4531, 1, 1, 1, 1, 1, + 1, 4532, 1, 4533, 1, 4534, 1, 4535, + 1, 4536, 1, 4538, 4537, 4539, 4537, 4540, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4541, 1, 4542, + 1, 4543, 1, 4544, 1, 4545, 1, 4546, + 1, 4547, 1, 4548, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4549, 1, 4550, + 1, 4551, 1, 4552, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4553, 1, 4554, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4555, 1, 4556, + 1, 4557, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4558, 1, 4559, 1, 4560, + 1, 4561, 1, 4562, 1, 4563, 1, 4564, + 4565, 1, 1, 1, 1, 1, 1, 4566, + 1, 1, 1, 4567, 1, 4568, 1, 1, + 1, 4569, 4570, 1, 4571, 1, 4572, 1, + 4573, 1, 4574, 1, 1, 1, 1, 1, + 4575, 1, 4576, 1, 4577, 1, 4578, 1, + 1, 4579, 1, 4580, 1, 4581, 1, 4582, + 1, 4583, 1, 4584, 1, 4585, 1, 4586, + 1, 4587, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4588, 1, + 4589, 1, 4590, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4591, 4592, 4593, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4594, 1, + 1, 1, 4595, 1, 4596, 1, 4597, 1, + 4598, 1, 4599, 1, 4600, 1, 4601, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4602, 1, 4603, 1, 4604, 1, + 4605, 1, 4606, 1, 4607, 1, 4608, 1, + 4609, 1, 4610, 1, 4611, 1, 4612, 1, + 4613, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4614, 1, 1, 1, 1, + 1, 4615, 1, 1, 1, 4616, 1, 4617, + 1, 4618, 1, 4619, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4620, 1, 4621, + 1, 4622, 1, 4623, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 4624, 4625, 1, 4626, + 1, 4627, 1, 1, 4628, 1, 4629, 1, + 4630, 1, 4631, 1, 4632, 1, 4633, 1, + 4634, 1, 4635, 1, 4636, 1, 4637, 1, + 4638, 1, 4639, 1, 4640, 1, 4641, 1, + 4642, 1, 4643, 1, 4644, 1, 4645, 1, + 4646, 1, 4647, 1, 4648, 1, 4649, 1, + 4650, 1, 4651, 1, 4652, 1, 4653, 1, + 1, 1, 1, 1, 4654, 1, 4655, 1, + 4656, 1, 4657, 1, 4658, 1, 4659, 1, + 4660, 1, 4661, 1, 4662, 1, 4663, 1, + 4664, 1, 4665, 1, 4666, 1, 4667, 1, + 4668, 1, 4669, 1, 4670, 1, 1, 1, + 1, 1, 4671, 1, 4672, 1, 4673, 1, + 4674, 1, 4675, 1, 4676, 1, 4677, 1, + 4678, 1, 4679, 1, 4680, 1, 4681, 1, + 4682, 1, 4683, 1, 4684, 1, 4685, 1, + 4686, 1, 1, 1, 1, 1, 1, 4687, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4688, 4689, 4690, 4691, 4692, 4693, 1, 4694, + 4695, 4696, 1, 4697, 1, 1, 4698, 1, + 1, 4699, 4700, 4701, 4702, 1, 4703, 1, + 1, 4704, 1, 4705, 1, 4706, 1, 4707, + 1, 4708, 1, 4709, 1, 4710, 1, 4711, + 1, 1, 1, 1, 4712, 1, 1, 1, + 1, 1, 4713, 4714, 1, 4715, 1, 4716, + 1, 4717, 1, 4718, 1, 4719, 1, 4720, + 1, 4721, 1, 4722, 1, 4723, 1, 4724, + 1, 4725, 1, 4726, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4727, 1, + 4728, 1, 4729, 4730, 1, 4731, 1, 4732, + 1, 4733, 1, 4734, 1, 4735, 1, 4736, + 1, 4737, 1, 4738, 1, 4739, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4740, 1, 4741, + 1, 4742, 1, 4743, 1, 4744, 1, 4745, + 1, 4746, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4747, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4748, 1, 4749, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4750, 1, 4751, 1, 4752, 1, + 4753, 1, 4754, 1, 4755, 1, 4756, 1, + 4757, 1, 4758, 1, 4759, 1, 4760, 1, + 4761, 1, 4762, 1, 1, 1, 1, 4763, + 4764, 1, 4765, 1, 4766, 1, 4767, 1, + 4768, 1, 4769, 1, 4770, 1, 4771, 1, + 4772, 1, 4773, 1, 1, 1, 1, 1, + 1, 1, 1, 4774, 1, 4775, 1, 4776, + 1, 4777, 1, 4778, 1, 4779, 1, 4780, + 1, 4781, 1, 4782, 1, 1, 1, 1, + 1, 4783, 1, 4784, 1, 4785, 1, 4786, + 1, 1, 1, 4787, 1, 4788, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4789, 1, 1, 4790, 1, 4791, 1, + 4792, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 4793, 1, 1, 1, + 4794, 1, 4795, 1, 4796, 1, 4797, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4798, 1, + 4799, 1, 4800, 1, 4801, 1, 4802, 1, + 4803, 1, 4804, 1, 4805, 1, 4806, 1, + 4807, 1, 4808, 1, 4809, 1, 4810, 1, + 4811, 1, 4812, 1, 4813, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4814, 1, 1, 1, 1, 1, + 4815, 1, 4816, 1, 4817, 1, 4819, 4818, + 4820, 4818, 4821, 4818, 4822, 4818, 4823, 4818, + 4824, 4818, 4825, 4818, 4826, 1, 4827, 1, + 4828, 1, 4829, 1, 4830, 1, 4831, 1, + 4832, 1, 4833, 1, 1, 4834, 1, 4835, + 1, 4836, 1, 4837, 1, 4838, 1, 4839, + 1, 4840, 1, 4841, 1, 1, 1, 4842, + 1, 1, 1, 4843, 4844, 1, 4845, 1, + 4846, 1, 4847, 1, 4848, 1, 4849, 1, + 4850, 1, 4851, 1, 4852, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4853, 1, + 1, 1, 1, 1, 1, 1, 4854, 1, + 1, 4855, 1, 1, 4856, 1, 4857, 1, + 4858, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4859, 1, 4860, 1, 4861, 1, 4862, + 1, 4863, 1, 4864, 1, 4865, 1, 4866, + 1, 4867, 1, 4868, 1, 4869, 1, 4870, + 1, 4871, 1, 4872, 1, 4873, 1, 4874, + 1, 4875, 1, 4876, 1, 4877, 1, 4878, + 1, 4879, 1, 4880, 1, 4881, 1, 4882, + 1, 4883, 1, 4884, 1, 4885, 1, 4886, + 1, 4887, 1, 4888, 1, 4889, 1, 4890, + 1, 4891, 1, 1, 4892, 1, 1, 1, + 4893, 1, 4894, 1, 4895, 1, 4896, 1, + 4897, 1, 4898, 1, 4899, 1, 4900, 1, + 4901, 1, 4902, 1, 4903, 1, 4904, 1, + 4905, 1, 4906, 1, 4907, 1, 4908, 1, + 4909, 1, 4910, 1, 4911, 1, 4912, 1, + 4913, 1, 4914, 1, 4915, 1, 1, 1, + 1, 1, 4916, 1, 4917, 1, 4918, 1, + 4919, 1, 4920, 1, 4921, 1, 4922, 1, + 4923, 1, 4924, 1, 4925, 1, 4926, 4927, + 1, 4928, 1, 4929, 1, 4930, 1, 4931, + 1, 4932, 1, 4933, 1, 4934, 1, 1, + 4935, 1, 4936, 1, 4937, 1, 4938, 1, + 4939, 1, 4940, 1, 4941, 1, 4942, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4943, 1, 1, 1, 1, 4944, + 1, 4945, 1, 1, 1, 1, 1, 1, + 4946, 1, 4947, 1, 4948, 1, 4949, 1, + 4950, 1, 4951, 1, 4952, 1, 4953, 1, + 4954, 1, 4955, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 4956, 1, 4957, 1, 4958, 1, 4959, 1, + 4960, 1, 4961, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4962, 1, 4963, + 1, 4964, 1, 1, 1, 1, 1, 1, + 4965, 1, 4966, 1, 4967, 1, 4968, 1, + 4969, 1, 4970, 1, 4971, 1, 4972, 1, + 4973, 1, 4974, 1, 4975, 1, 4976, 1, + 4977, 1, 4978, 1, 1, 1, 1, 1, + 4979, 1, 4980, 1, 4981, 1, 4982, 1, + 4983, 1, 4984, 1, 4985, 1, 4986, 1, + 4987, 1, 4988, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4989, + 1, 4990, 4991, 4992, 4993, 4994, 1, 1, + 1, 1, 4995, 4996, 4997, 4998, 4999, 5000, + 5001, 5002, 5003, 5004, 1, 1, 5005, 1, + 5006, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5007, 1, 5008, 1, 5009, + 1, 5010, 1, 5011, 1, 5012, 1, 5013, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5014, + 1, 5015, 1, 5016, 1, 5017, 1, 5018, + 1, 5019, 1, 5020, 1, 5021, 1, 5022, + 1, 1, 1, 1, 1, 1, 1, 5023, + 1, 1, 1, 1, 1, 5024, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5025, + 1, 5026, 1, 5027, 1, 5028, 1, 5029, + 1, 5030, 1, 5031, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5032, 1, 5033, 1, 5034, + 1, 5035, 1, 5036, 1, 5037, 1, 5038, + 1, 5039, 1, 5040, 1, 5041, 1, 5042, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5043, 1, 5044, + 1, 5045, 1, 5046, 1, 5047, 1, 5048, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5049, 5050, + 1, 5051, 1, 5052, 1, 5053, 1, 5054, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5055, 1, 5056, 1, 5057, 1, 5058, 1, + 5059, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5060, 1, + 1, 5061, 1, 1, 1, 1, 1, 1, + 5062, 1, 5063, 1, 5064, 1, 5065, 1, + 5066, 1, 5067, 1, 5068, 1, 5069, 1, + 5070, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5071, 1, 5072, 1, 5073, 1, 5074, + 1, 5075, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5076, 1, 1, 5077, 1, 5078, 1, 5079, + 1, 5080, 1, 5081, 1, 5082, 1, 5083, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5084, + 1, 1, 5085, 1, 5086, 1, 5087, 1, + 5088, 1, 5089, 1, 5090, 1, 5091, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5092, 1, 5093, 5094, 1, 5095, 1, 5096, + 1, 5097, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5098, 1, 5099, + 1, 5100, 1, 5101, 1, 5102, 1, 1, + 1, 1, 1, 1, 1, 1, 5103, 1, + 5104, 1, 5105, 1, 5106, 1, 5107, 1, + 5108, 1, 5109, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5110, 1, 1, + 1, 1, 1, 1, 5111, 1, 5112, 1, + 5113, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5114, 1, 5115, 1, 5116, 1, 5117, 1, + 5118, 1, 5119, 1, 5120, 1, 5121, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5122, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5123, 1, 5124, 1, 5125, 1, 5126, + 1, 5127, 1, 5128, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5129, 1, 5130, 5131, 1, + 5132, 1, 1, 1, 1, 1, 5133, 1, + 5134, 1, 5135, 1, 5136, 1, 5137, 1, + 5138, 1, 5139, 1, 5140, 1, 5141, 1, + 1, 5142, 1, 5143, 1, 5144, 1, 5145, + 1, 5146, 1, 5147, 1, 5148, 1, 1, + 1, 1, 5149, 1, 5150, 1, 5151, 1, + 5152, 1, 5153, 1, 5154, 1, 5155, 1, + 5156, 1, 5157, 1, 1, 1, 5158, 1, + 1, 1, 5159, 1, 5160, 1, 5161, 1, + 5162, 1, 5163, 1, 5164, 1, 5165, 1, + 5166, 1, 5167, 1, 1, 1, 1, 1, + 1, 1, 1, 5168, 1, 5169, 1, 5170, + 1, 5171, 1, 5172, 1, 5173, 1, 5174, + 1, 5175, 1, 5176, 1, 5177, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5178, 1, 5179, 1, 5180, 1, + 5181, 1, 5182, 1, 5183, 1, 5184, 1, + 5185, 5186, 1, 1, 1, 1, 5187, 1, + 5188, 1, 5189, 1, 5190, 1, 5191, 1, + 5192, 1, 5193, 1, 5194, 1, 5195, 1, + 1, 1, 1, 1, 1, 5196, 1, 5197, + 1, 5198, 1, 1, 1, 1, 5199, 1, + 5200, 1, 5201, 1, 5202, 1, 5203, 1, + 1, 1, 1, 1, 5204, 1, 1, 1, + 1, 1, 1, 5205, 1, 5206, 1, 5207, + 1, 5208, 1, 5209, 1, 5210, 1, 5211, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5212, 1, 5213, 1, 5214, 1, 5215, + 1, 5216, 1, 5217, 1, 5218, 1, 5219, + 1, 5220, 1, 5221, 1, 5222, 1, 5223, + 1, 5224, 1, 5225, 1, 5226, 1, 5227, + 1, 5228, 1, 5229, 1, 5230, 1, 5231, + 1, 5232, 5233, 1, 1, 5234, 5235, 1, + 5236, 1, 5237, 5238, 5239, 1, 5240, 5241, + 1, 5242, 1, 5243, 1, 5244, 1, 5245, + 1, 5246, 1, 5247, 1, 5248, 1, 5249, + 1, 5250, 1, 5251, 1, 5252, 1, 5253, + 1, 5254, 1, 5255, 1, 5256, 1, 5257, + 1, 5258, 1, 5259, 1, 5260, 1, 5261, + 1, 1, 5262, 1, 1, 1, 1, 1, + 5263, 1, 5264, 1, 5265, 1, 5266, 1, + 5267, 1, 5268, 1, 1, 5269, 1, 5270, + 1, 5271, 1, 5272, 1, 5273, 1, 5274, + 1, 5275, 1, 5276, 1, 5277, 1, 5278, + 1, 5279, 1, 5280, 1, 5281, 1, 5282, + 1, 5283, 1, 5284, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5285, 1, + 1, 1, 1, 1, 1, 1, 5286, 1, + 5287, 1, 5288, 1, 5289, 1, 5290, 1, + 5291, 1, 5292, 1, 5293, 1, 5294, 1, + 5295, 1, 5296, 1, 5297, 1, 5298, 1, + 5299, 1, 5300, 1, 5301, 1, 5302, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5303, 1, 5304, 1, 5305, 1, 5306, 1, + 5307, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5308, 1, 5309, 1, 5310, + 1, 5311, 1, 5312, 1, 5313, 1, 5314, + 1, 5315, 1, 5316, 1, 5317, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5318, 1, 5319, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5320, 1, 5321, + 5322, 5323, 5324, 5325, 1, 5326, 1, 5327, + 5328, 5329, 5330, 5331, 1, 5332, 1, 5333, + 1, 5334, 1, 5335, 1, 5336, 1, 5337, + 1, 5338, 1, 5339, 1, 5340, 1, 5341, + 5342, 1, 1, 5343, 1, 5344, 1, 5345, + 1, 5346, 1, 5347, 1, 5348, 1, 5349, + 1, 5350, 1, 5351, 1, 5352, 1, 5353, + 1, 5354, 1, 5355, 1, 5356, 1, 5357, + 1, 5358, 1, 5359, 1, 5360, 1, 5361, + 1, 5362, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5363, 5364, 5365, + 5366, 5367, 5368, 5369, 1, 5370, 5371, 1, + 5372, 1, 5373, 5374, 1, 1, 5375, 5376, + 5377, 1, 5378, 1, 5379, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5380, 1, 5381, + 1, 5382, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5383, 1, 1, 5384, 1, + 5385, 1, 5386, 1, 5387, 1, 5388, 1, + 5389, 1, 5390, 1, 5391, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5392, 1, 5393, + 1, 5394, 1, 5395, 1, 5396, 1, 5397, + 1, 5398, 1, 5399, 1, 5400, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5401, 1, 5402, + 1, 5403, 1, 5404, 1, 5405, 1, 5406, + 1, 5407, 1, 5408, 1, 5409, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5410, 1, + 1, 1, 1, 5411, 1, 5412, 1, 5413, + 1, 5414, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5415, + 1, 5416, 1, 5417, 1, 5418, 1, 5419, + 1, 5420, 1, 5421, 1, 5422, 1, 5423, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5424, + 5425, 1, 1, 1, 1, 1, 1, 1, + 5426, 1, 5427, 1, 5428, 1, 5429, 1, + 5430, 1, 5431, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5432, 1, + 5433, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5434, 1, 5435, 1, 5436, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5437, + 1, 5438, 1, 5439, 1, 5440, 1, 5441, + 1, 5442, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5443, 1, 5444, + 1, 5445, 1, 5446, 1, 5447, 1, 5448, + 1, 5449, 1, 5450, 1, 5451, 1, 5452, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5453, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5454, 1, 1, + 1, 1, 1, 1, 1, 1, 5455, 1, + 5456, 1, 5457, 1, 5458, 1, 5459, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5460, 1, 1, 1, 5461, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 5462, 1, 5463, 1, + 5464, 1, 5465, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5466, + 1, 5467, 1, 5468, 1, 5469, 1, 5470, + 1, 5471, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5472, + 1, 5473, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5474, + 1, 5475, 1, 5476, 1, 5477, 1, 5478, + 1, 5479, 1, 5480, 1, 5481, 1, 5482, + 1, 5483, 1, 5484, 1, 5485, 1, 5486, + 1, 1, 1, 1, 1, 5487, 1, 5488, + 1, 5489, 1, 5490, 1, 5491, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5492, + 1, 1, 1, 1, 1, 1, 5493, 1, + 5494, 1, 5495, 1, 5497, 5496, 5496, 5496, + 5496, 5496, 5498, 5496, 5499, 5496, 5500, 5496, + 5501, 5496, 5502, 5496, 5503, 5496, 5504, 5496, + 5505, 5496, 5506, 5496, 5507, 5496, 5508, 5496, + 5509, 5496, 5510, 5496, 5511, 5496, 5512, 5496, + 5513, 5496, 5514, 5496, 5496, 5515, 5516, 5496, + 5496, 5496, 5496, 5496, 5496, 5517, 5496, 5496, + 5496, 5496, 5496, 5496, 5518, 5496, 5519, 5496, + 5520, 5496, 5521, 5496, 5522, 5496, 5523, 5496, + 5524, 5496, 5525, 5496, 5526, 5496, 5527, 5496, + 5528, 5496, 5529, 5496, 5530, 5496, 5531, 5496, + 5532, 5496, 5496, 5496, 5496, 5533, 5496, 5534, + 5496, 5535, 5496, 5536, 5496, 5537, 5496, 5538, + 5496, 5539, 5496, 5540, 5496, 5541, 5496, 5542, + 5496, 5543, 5496, 5544, 5496, 5545, 5496, 5546, + 5496, 5547, 5496, 5548, 5496, 5549, 5496, 5550, + 1, 1, 1, 1, 1, 1, 1, 1, + 5551, 1, 5552, 1, 5553, 1, 5554, 1, + 5555, 1, 5556, 1, 5557, 1, 5558, 1, + 5559, 1, 5560, 1, 5561, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 5562, 5563, 5564, 1, + 5565, 5566, 1, 1, 1, 1, 5567, 1, + 1, 1, 5568, 1, 1, 1, 5569, 1, + 1, 1, 1, 1, 5570, 1, 5571, 1, + 5572, 1, 5573, 1, 5574, 1, 1, 5575, + 5576, 1, 1, 1, 1, 5577, 1, 5578, + 1, 5579, 1, 5580, 1, 5581, 1, 5582, + 1, 5583, 1, 5584, 1, 5585, 1, 5586, + 1, 5587, 1, 5588, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5589, 1, 5590, 1, 5591, 1, 5592, + 1, 5593, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5594, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5595, 1, 5596, + 1, 5597, 1, 5598, 1, 5599, 1, 5600, + 1, 5601, 1, 5602, 1, 5603, 1, 5604, + 1, 5605, 1, 5606, 1, 5607, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5608, 1, 1, 1, 1, 1, 5609, 1, + 5610, 1, 5611, 1, 5612, 1, 5613, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5614, 1, 5615, 1, 5616, 1, 5617, 1, + 5618, 1, 5619, 1, 5620, 1, 5621, 1, + 5622, 1, 5623, 1, 5624, 1, 5625, 1, + 5626, 1, 5627, 1, 5628, 1, 5629, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5630, 1, 5631, 1, 5632, 1, 5633, 1, + 5634, 1, 5635, 1, 5636, 1, 5637, 1, + 5638, 1, 5639, 1, 5640, 1, 5641, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5642, 1, 5643, 5644, 1, 5645, + 1, 5646, 1, 5647, 1, 5648, 1, 5649, + 1, 5650, 1, 5651, 1, 5652, 1, 5653, + 1, 5654, 1, 1, 1, 1, 1, 5655, + 1, 5656, 1, 5657, 1, 5658, 1, 5659, + 1, 5660, 1, 5661, 1, 5662, 1, 5663, + 1, 5664, 1, 5665, 1, 5666, 1, 5667, + 1, 5668, 1, 5669, 1, 5670, 1, 5671, + 1, 5672, 1, 5673, 1, 5674, 1, 5675, + 1, 5676, 1, 5677, 1, 5678, 1, 5679, + 1, 5680, 1, 5681, 1, 1, 1, 1, + 1, 1, 1, 1, 5682, 1, 1, 1, + 1, 1, 1, 1, 5683, 1, 5684, 1, + 5685, 1, 5686, 1, 5687, 1, 5688, 1, + 5689, 1, 5690, 1, 5691, 1, 5692, 1, + 5693, 1, 5694, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5695, 1, 5696, 1, 5697, 1, 5698, 1, + 5699, 1, 5700, 1, 5701, 1, 5702, 1, + 5703, 1, 5704, 1, 5705, 1, 5706, 5707, + 5708, 1, 5709, 5710, 1, 1, 5711, 5712, + 5713, 5714, 5715, 1, 5716, 5717, 5718, 1, + 5719, 1, 5720, 1, 5721, 1, 5722, 1, + 5723, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5724, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5725, 1, + 5726, 1, 5727, 1, 5728, 1, 5729, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5730, 1, 5731, 1, 5732, + 1, 5733, 1, 5734, 1, 5735, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5736, 1, 5737, 1, 5738, 1, 5739, + 1, 5740, 1, 5741, 1, 5742, 1, 5743, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5744, 1, 1, + 1, 1, 5745, 5746, 1, 5747, 1, 1, + 1, 1, 5748, 1, 5749, 1, 5750, 1, + 5751, 1, 5752, 1, 5753, 1, 5754, 1, + 5755, 1, 5756, 1, 5757, 1, 5758, 1, + 5759, 1, 5760, 1, 5761, 1, 5762, 1, + 5763, 1, 5764, 1, 5765, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5766, 5767, 1, 5768, 1, 1, + 1, 5769, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5770, 1, + 5771, 1, 5772, 1, 5773, 1, 1, 1, + 1, 1, 1, 5774, 1, 1, 1, 5775, + 1, 5776, 1, 5777, 1, 5778, 1, 5779, + 1, 5780, 1, 5781, 1, 5782, 1, 5783, + 1, 5784, 1, 5785, 1, 5786, 1, 5787, + 1, 5788, 1, 5789, 1, 5790, 1, 5791, + 1, 5792, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5793, 1, 1, 5794, 1, 1, 1, + 1, 1, 1, 1, 1, 5795, 1, 1, + 1, 1, 5796, 1, 5797, 1, 5798, 1, + 5799, 1, 5800, 1, 5801, 1, 5802, 1, + 5803, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5804, 1, 5805, 1, 5806, 1, 5807, + 1, 5808, 1, 5809, 1, 5810, 1, 5811, + 1, 5812, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5813, 1, 5814, 1, 1, 1, 1, + 1, 1, 5815, 1, 1, 1, 5816, 1, + 5817, 1, 5818, 1, 5819, 1, 5820, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5821, 1, 5822, 1, 5823, 1, 5824, + 1, 5825, 1, 5826, 1, 5827, 1, 5828, + 1, 5829, 1, 5830, 1, 5831, 1, 5832, + 1, 5833, 1, 5834, 1, 5835, 1, 5836, + 1, 5837, 1, 5838, 1, 5839, 1, 1, + 1, 5840, 1, 1, 1, 1, 1, 1, + 1, 1, 5841, 1, 1, 1, 5842, 1, + 5843, 1, 5844, 1, 5845, 1, 5846, 1, + 5847, 1, 5848, 1, 5849, 1, 5850, 1, + 5851, 1, 5852, 1, 5853, 1, 5854, 1, + 5855, 1, 5856, 1, 5857, 1, 5858, 1, + 5859, 1, 5860, 1, 1, 1, 1, 1, + 5861, 1, 5862, 1, 5863, 1, 5864, 1, + 5865, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5866, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5867, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5868, 1, 1, 5869, 1, 5870, 1, 5871, + 1, 5872, 1, 5873, 1, 5874, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 5875, 1, 5876, 1, 5877, 1, 5878, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 5879, 1, 5880, 1, + 5881, 1, 5882, 1, 5883, 1, 5884, 1, + 5885, 1, 5886, 1, 5887, 1, 5888, 1, + 5889, 1, 5890, 1, 1, 5891, 1, 1, + 1, 1, 1, 1, 5892, 1, 5893, 1, + 1, 1, 5894, 1, 5895, 1, 5896, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5897, 1, + 5898, 1, 5899, 1, 5900, 1, 5901, 1, + 5902, 1, 5903, 1, 5904, 1, 5905, 1, + 5906, 1, 5907, 1, 5908, 1, 5909, 1, + 5910, 1, 5911, 1, 5912, 1, 5913, 1, + 5914, 1, 5915, 1, 5916, 1, 5917, 1, + 5918, 1, 5919, 1, 5920, 1, 5921, 1, + 5922, 1, 5923, 1, 5924, 1, 1, 5925, + 5926, 5927, 1, 5928, 1, 1, 1, 1, + 5929, 1, 1, 1, 5930, 1, 5931, 1, + 5932, 1, 5933, 1, 5934, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5935, + 1, 5936, 1, 5937, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5938, + 1, 5939, 1, 5940, 1, 5941, 1, 5942, + 1, 5943, 1, 5944, 1, 5945, 1, 5946, + 1, 5947, 1, 5948, 1, 5949, 1, 5950, + 1, 5951, 1, 5952, 1, 5953, 1, 5954, + 1, 5955, 1, 5956, 1, 5957, 1, 5958, + 1, 5959, 1, 5960, 1, 5961, 1, 5962, + 1, 5963, 5964, 1, 1, 5965, 1, 1, + 5966, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5967, 5968, 5969, 5970, 5971, 5972, 5973, + 5974, 1, 5975, 1, 5976, 5977, 5978, 5979, + 5980, 1, 5981, 5982, 5983, 5984, 5985, 1, + 5986, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5987, 1, 5988, 1, 5989, 1, 5990, + 1, 5991, 1, 5992, 1, 5993, 1, 5994, + 1, 5995, 1, 5996, 1, 5997, 1, 5998, + 1, 5999, 1, 6000, 1, 6001, 1, 6002, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6003, 1, 6004, 1, 6005, + 1, 6006, 1, 6007, 1, 6008, 1, 6009, + 1, 6010, 1, 1, 1, 1, 1, 6011, + 6012, 1, 6013, 6014, 6015, 1, 6016, 1, + 6017, 1, 6018, 1, 6019, 1, 6020, 1, + 6021, 1, 6022, 1, 6023, 1, 6024, 1, + 6025, 1, 6026, 1, 6027, 1, 6028, 1, + 6029, 1, 6030, 1, 6031, 1, 6032, 1, + 6033, 1, 6034, 1, 6035, 1, 6036, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6037, + 1, 1, 1, 1, 1, 1, 1, 6038, + 1, 6039, 1, 6040, 1, 6041, 1, 6042, + 1, 6043, 1, 6044, 1, 6045, 1, 6046, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6047, 1, + 1, 1, 6048, 1, 6049, 1, 1, 1, + 6050, 1, 1, 1, 6051, 1, 1, 6052, + 6053, 1, 6054, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6055, 1, 6056, + 1, 6057, 1, 6058, 1, 6059, 1, 6060, + 1, 6061, 1, 6062, 1, 6063, 1, 6064, + 1, 6065, 1, 6066, 1, 6067, 1, 6068, + 1, 6069, 1, 6070, 1, 6071, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6072, 1, 1, 1, 6073, + 1, 6074, 1, 6075, 1, 6076, 1, 6077, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6078, + 1, 6079, 1, 6080, 6081, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6082, 1, 6083, 1, + 6084, 1, 6085, 1, 6086, 1, 6087, 1, + 6088, 1, 6089, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6090, 1, 6091, 1, + 6092, 1, 1, 1, 1, 1, 6093, 1, + 6094, 1, 6095, 1, 6096, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6097, 1, 6098, 1, 6099, 1, + 6100, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6101, 1, 6102, 1, 6103, 1, 6104, + 1, 1, 1, 6105, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6106, 1, 1, 1, 6107, + 1, 6108, 1, 6109, 1, 6110, 1, 6111, + 1, 6112, 1, 1, 1, 1, 6113, 1, + 6114, 1, 6115, 1, 6116, 1, 6117, 1, + 6118, 1, 6119, 1, 6120, 1, 6121, 1, + 6122, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6123, 6124, + 6125, 1, 6126, 1, 6127, 1, 6128, 1, + 6129, 1, 6130, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6131, 1, 6132, 1, 6133, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6134, 1, + 6135, 1, 6136, 1, 6137, 1, 6138, 1, + 6139, 1, 6140, 1, 6141, 1, 6142, 1, + 6143, 1, 6144, 1, 6145, 1, 6146, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6147, 6148, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6149, 1, 6150, 1, + 6151, 1, 6152, 1, 1, 1, 1, 1, + 1, 6153, 1, 1, 1, 6154, 1, 1, + 1, 1, 1, 6155, 1, 6156, 1, 6157, + 1, 6158, 1, 6159, 1, 6160, 1, 6161, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6162, 1, 6163, 1, 6164, 1, 6165, 1, + 6166, 1, 6167, 1, 6168, 1, 6169, 1, + 6170, 1, 6171, 1, 6172, 1, 6173, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6174, + 1, 6175, 1, 6176, 1, 6177, 1, 6178, + 1, 6179, 1, 6180, 1, 6181, 1, 6182, + 1, 6183, 1, 6184, 1, 6185, 1, 6186, + 1, 6187, 1, 6188, 1, 6189, 1, 6190, + 1, 6191, 1, 6192, 1, 6193, 1, 6194, + 1, 6195, 1, 1, 1, 1, 1, 1, + 6196, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6197, 1, 6198, 1, 6199, + 1, 6200, 1, 6201, 1, 6202, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6203, 1, 6204, + 1, 6205, 1, 6206, 1, 6207, 1, 6208, + 1, 6209, 1, 6210, 1, 6211, 1, 6212, + 1, 6213, 1, 6214, 1, 6215, 1, 6216, + 1, 6217, 1, 6218, 1, 6219, 1, 6220, + 1, 6221, 1, 6222, 1, 6223, 1, 6224, + 1, 6225, 1, 6226, 1, 6227, 1, 6228, + 1, 6229, 1, 6230, 1, 6231, 1, 6232, + 1, 6233, 1, 6234, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6235, 1, 6236, 1, 6237, 1, 6238, + 1, 6239, 1, 6240, 1, 6241, 1, 6242, + 1, 6243, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6244, 6245, 1, 1, 6246, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6247, 1, 6248, 1, 6249, 1, 6250, + 1, 6251, 1, 6252, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6253, + 1, 6254, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6255, 1, 6256, 1, 6257, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6258, 1, + 6259, 1, 6260, 1, 6261, 1, 1, 6262, + 6263, 1, 6264, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6265, 1, + 6266, 1, 6267, 1, 6268, 1, 6269, 1, + 6270, 1, 6271, 1, 6272, 1, 6273, 1, + 6274, 1, 6275, 1, 6276, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6277, 1, + 6278, 1, 6279, 1, 6280, 1, 6281, 1, + 6282, 1, 6283, 1, 6284, 1, 6285, 1, + 6286, 1, 6287, 1, 6288, 1, 6289, 1, + 6290, 1, 6291, 1, 1, 6292, 1, 1, + 1, 1, 1, 6293, 1, 6294, 1, 6295, + 1, 6296, 1, 6297, 1, 6298, 1, 6299, + 1, 6300, 1, 6301, 1, 6302, 1, 6303, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6304, 1, 6305, 1, 6306, 6307, 1, + 6308, 1, 6309, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6310, 1, 6311, 1, 6312, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6313, 1, 6314, 1, 6315, 1, 6316, 1, + 6317, 1, 6318, 1, 6319, 1, 6320, 1, + 6321, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6322, 1, + 6323, 1, 1, 1, 1, 6324, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6325, 1, 6326, 1, 6327, 1, 6328, + 1, 6329, 1, 6330, 1, 6331, 1, 6332, + 1, 6333, 1, 6334, 1, 6335, 1, 6336, + 1, 6337, 1, 6338, 1, 6339, 1, 6340, + 1, 6341, 1, 6342, 1, 1, 1, 1, + 1, 6343, 1, 6344, 1, 6345, 1, 6346, + 1, 6347, 1, 6348, 1, 6349, 1, 6350, + 1, 6351, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6352, + 1, 6353, 1, 6354, 1, 6355, 1, 6356, + 1, 6357, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6358, 1, 1, + 1, 6359, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6360, + 1, 6361, 1, 6362, 1, 6363, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6364, 1, 6365, 1, 6366, 1, + 6367, 1, 6368, 1, 6369, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6370, 1, 6371, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6372, 1, 6373, 1, 6374, 1, + 6375, 1, 6376, 1, 6377, 6378, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6379, 6380, 6381, 1, 1, 1, 6382, + 1, 1, 6383, 1, 1, 6384, 1, 6385, + 1, 1, 1, 6386, 1, 6387, 1, 6388, + 1, 6389, 1, 6390, 1, 6391, 1, 6392, + 1, 6393, 1, 6394, 1, 6395, 6396, 1, + 1, 1, 1, 6397, 1, 6398, 1, 6399, + 1, 6400, 1, 6401, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6402, 1, 6403, 1, + 6404, 1, 6405, 1, 6406, 1, 6407, 1, + 6408, 1, 6409, 1, 6410, 1, 6411, 1, + 6412, 1, 6413, 1, 6414, 1, 6415, 1, + 6416, 1, 6417, 1, 6418, 1, 6419, 1, + 6420, 1, 6421, 1, 6422, 1, 6423, 1, + 6424, 1, 6425, 1, 6426, 1, 6427, 1, + 6428, 1, 6429, 1, 6430, 1, 6431, 1, + 6432, 1, 6433, 1, 6434, 1, 6435, 1, + 6436, 1, 6437, 1, 6438, 1, 6439, 1, + 6440, 1, 1, 1, 1, 1, 6441, 1, + 6442, 1, 6443, 1, 6444, 1, 6445, 1, + 6446, 1, 6447, 1, 6448, 1, 6449, 1, + 6450, 1, 6451, 1, 1, 1, 1, 6452, + 1, 1, 1, 1, 1, 6453, 1, 6454, + 1, 6455, 1, 6456, 1, 6457, 1, 6458, + 1, 6459, 1, 6460, 1, 6461, 1, 6462, + 1, 6463, 1, 6464, 1, 6465, 6466, 1, + 6467, 1, 6468, 1, 6469, 1, 6470, 1, + 6471, 1, 6472, 1, 6473, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6474, 6475, + 1, 6476, 1, 6477, 1, 6478, 1, 6479, + 1, 6480, 1, 6481, 1, 6482, 1, 6483, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6484, 1, 6485, 1, 6486, 1, 6487, 1, + 6488, 1, 1, 1, 1, 6489, 1, 1, + 1, 1, 6490, 1, 1, 1, 1, 1, + 1, 6491, 1, 6492, 1, 6493, 1, 6494, + 1, 6495, 1, 6496, 1, 6497, 1, 6498, + 1, 6499, 1, 6500, 1, 6501, 1, 6502, + 1, 6503, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6504, 1, 6505, 1, 6506, 1, + 6507, 1, 6508, 1, 6509, 1, 6510, 1, + 6511, 1, 1, 1, 1, 6512, 6513, 1, + 1, 1, 1, 1, 1, 1, 6514, 1, + 1, 6515, 1, 6516, 1, 6517, 1, 6518, + 1, 6519, 1, 6520, 1, 6521, 1, 6522, + 1, 6523, 1, 6524, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6525, 1, 6526, + 1, 6527, 1, 6528, 1, 6529, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6530, 1, 6531, 1, 6532, 1, 6533, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6534, 1, 6535, + 1, 6536, 1, 6537, 1, 6538, 1, 6539, + 1, 6541, 6540, 6540, 6540, 6540, 6540, 6542, + 6540, 6543, 6540, 6544, 6540, 6545, 6540, 6546, + 6540, 6547, 6540, 6548, 6540, 6549, 6540, 6550, + 6540, 6551, 6540, 6552, 6540, 6553, 6540, 6554, + 6540, 6555, 6540, 6556, 6540, 6557, 6540, 6558, + 6540, 6559, 6540, 6560, 6540, 6561, 6540, 6562, + 6540, 6563, 6540, 6564, 6540, 6565, 6540, 6566, + 6540, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6540, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6540, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6567, 6540, 6568, 6540, 6569, 6540, 6570, 6540, + 6571, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6540, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6540, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6540, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6540, 6540, 6540, 6540, 6540, 6540, 6540, 6540, + 6540, 6540, 6572, 6573, 6540, 6574, 6540, 6575, + 6540, 6576, 1, 6577, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6578, 1, 6579, 1, + 6580, 1, 6581, 1, 6582, 1, 6583, 1, + 6584, 1, 6585, 1, 6586, 1, 6587, 1, + 6588, 1, 1, 1, 1, 1, 1, 1, + 1, 6589, 1, 6590, 1, 6591, 1, 6592, + 1, 6593, 1, 6594, 1, 6595, 1, 6596, + 1, 6597, 1, 6598, 1, 6599, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6600, 1, 6601, 6602, 6603, 6604, + 1, 6605, 6606, 1, 1, 6607, 1, 6608, + 6609, 6610, 1, 1, 6611, 1, 6612, 1, + 6613, 1, 6614, 1, 6615, 1, 6616, 1, + 6617, 1, 1, 1, 1, 1, 1, 1, + 1, 6618, 1, 1, 1, 6619, 1, 6620, + 1, 6621, 1, 6622, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6623, 1, 6624, 1, 6625, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6626, 1, 6627, 1, 6628, 1, 6629, 1, + 6630, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6631, 1, 6632, 1, 6633, 1, 6634, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6635, + 1, 1, 1, 1, 1, 1, 1, 6636, + 1, 1, 1, 1, 1, 1, 1, 1, + 6637, 1, 6638, 1, 6639, 1, 6640, 1, + 6641, 1, 6642, 1, 6643, 1, 6644, 1, + 6645, 1, 6646, 1, 6647, 1, 6648, 1, + 6649, 1, 6650, 1, 6651, 1, 6652, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6653, 1, 6654, 1, 6655, 1, 6656, 1, + 6657, 1, 6658, 1, 6659, 1, 6660, 1, + 6661, 1, 6662, 1, 6663, 1, 6664, 1, + 6665, 1, 6666, 1, 6667, 1, 6668, 1, + 6669, 1, 6670, 1, 6671, 1, 6672, 1, + 6673, 1, 6674, 1, 6675, 1, 6676, 1, + 6677, 1, 6678, 1, 6679, 6680, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6681, + 1, 6682, 1, 6683, 1, 6684, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6685, 1, 6686, 6687, 1, + 6688, 1, 6689, 1, 6690, 1, 6691, 1, + 6692, 1, 6693, 1, 6694, 1, 6695, 1, + 6696, 1, 6697, 1, 6698, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6699, 1, 6700, 1, 6701, + 1, 6702, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6703, 1, 6704, 1, 6705, + 6706, 1, 6707, 1, 6708, 1, 6709, 1, + 6710, 1, 6711, 1, 6712, 1, 6713, 1, + 6714, 1, 6715, 1, 6716, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6717, 1, 6718, 1, 6719, 1, 6720, 1, + 6721, 1, 6722, 1, 6723, 1, 6724, 1, + 6725, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6726, 1, 6727, 1, 6728, 1, 6729, + 1, 6730, 1, 6731, 1, 6732, 1, 6733, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6734, 6735, 1, 6736, 1, 6737, 1, 6738, + 1, 6739, 1, 6740, 1, 6741, 1, 6742, + 1, 6743, 1, 6744, 1, 6745, 1, 1, + 1, 1, 6746, 1, 1, 1, 1, 1, + 6747, 1, 1, 1, 6748, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6749, + 6750, 6751, 6752, 6753, 6754, 6755, 6756, 6757, + 6758, 1, 6759, 6760, 1, 6761, 6762, 1, + 6763, 6764, 6765, 6766, 6767, 6768, 1, 6769, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6770, 1, 6771, 1, + 6772, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6773, 1, 6774, 1, 6775, + 1, 1, 1, 1, 1, 1, 6776, 1, + 1, 1, 1, 1, 1, 1, 6777, 1, + 6778, 1, 6779, 1, 6780, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6781, 1, 6782, + 1, 6783, 1, 6784, 1, 6785, 1, 6786, + 1, 6787, 1, 6788, 1, 6789, 1, 6790, + 1, 6791, 1, 6792, 1, 6793, 1, 6794, + 1, 6795, 1, 6796, 1, 6797, 1, 6798, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6799, 1, 6800, 1, 6801, 1, + 6802, 1, 6803, 1, 6804, 1, 6805, 1, + 6806, 1, 6807, 1, 6808, 1, 6809, 1, + 6810, 1, 6811, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6812, 1, 6813, 1, 6814, 1, + 6815, 1, 6816, 1, 6817, 1, 6818, 1, + 6819, 1, 6820, 1, 6821, 6822, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6823, 1, 6824, 1, 1, 1, 6825, 1, + 6826, 1, 6827, 1, 6828, 1, 6829, 1, + 6830, 1, 6831, 1, 6832, 1, 6833, 1, + 6834, 1, 6835, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6836, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6837, 1, 1, 1, 1, 1, 6838, 6839, + 1, 6840, 1, 6841, 1, 6842, 1, 6843, + 1, 6844, 1, 6845, 1, 6846, 1, 6847, + 1, 6848, 1, 6849, 1, 6850, 1, 6851, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6852, 1, 6853, + 1, 6854, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6855, 1, 6856, 1, 6857, 1, 6858, + 1, 6859, 1, 6860, 1, 6861, 1, 6862, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6863, 1, 6864, 1, 6865, 1, 1, + 1, 6866, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6867, 1, 1, 1, 1, + 1, 6868, 1, 1, 1, 6869, 1, 6870, + 1, 6871, 1, 6872, 1, 6873, 1, 6874, + 1, 6875, 1, 6876, 1, 6877, 1, 6878, + 1, 6879, 1, 6880, 1, 6881, 1, 6882, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6883, 1, 6884, 1, 6885, 1, 6886, 1, + 6887, 1, 6888, 1, 6889, 1, 6890, 1, + 6891, 1, 6892, 1, 6893, 1, 6894, 1, + 1, 1, 1, 1, 6895, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6896, 1, 1, 6897, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6898, 1, 6899, 1, + 1, 1, 1, 6900, 1, 6901, 1, 6902, + 1, 6903, 1, 6904, 1, 6905, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6906, + 1, 6907, 1, 6908, 1, 6909, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6910, 1, 6911, 1, 6912, 1, 6913, + 1, 6914, 1, 6915, 1, 6916, 1, 6917, + 1, 6918, 1, 6919, 1, 6920, 1, 1, + 1, 6921, 1, 6922, 1, 6923, 1, 6924, + 1, 6925, 1, 6926, 1, 6927, 1, 6928, + 1, 6929, 1, 6930, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6931, 1, 6932, 1, 6933, + 1, 6934, 1, 6935, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6936, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6937, 6938, 1, 6939, 1, 6940, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6941, 1, 6942, 1, + 6943, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6944, 1, + 6945, 1, 6946, 1, 6947, 1, 6948, 1, + 6949, 1, 6950, 1, 6951, 1, 6952, 1, + 6953, 1, 6954, 1, 6955, 1, 6956, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6957, 1, 6958, + 1, 6959, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6960, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6961, 1, 6962, 1, 6963, 1, 6964, 1, + 6965, 1, 6966, 1, 6967, 1, 6968, 1, + 6969, 1, 6970, 1, 6971, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6972, 1, 1, 6973, + 1, 6974, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6975, 1, 6976, 1, 6977, 1, + 6978, 1, 6979, 1, 6980, 1, 6981, 1, + 1, 1, 6982, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6983, 1, + 1, 6984, 6985, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6986, 6987, 1, 6988, 1, 6989, 1, 6990, + 1, 6991, 1, 6992, 1, 6993, 1, 6994, + 1, 6995, 1, 6996, 1, 6997, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 6998, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6999, 1, 7000, 1, 7001, + 1, 7002, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7003, 1, 7004, 1, 7005, 1, + 7006, 1, 7007, 1, 7008, 1, 7009, 1, + 7010, 1, 7011, 1, 7012, 1, 7013, 1, + 7014, 1, 7015, 1, 7016, 1, 7017, 1, + 7018, 1, 7019, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7020, 1, 7021, 1, 7022, 1, 7023, 1, + 7024, 1, 7025, 1, 7026, 1, 7027, 1, + 7028, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7029, 1, 7030, 1, 7031, 1, 7032, 1, + 7033, 1, 7034, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7035, 1, 7036, 1, 7037, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7038, + 1, 7039, 1, 7040, 1, 7041, 1, 7042, + 1, 7043, 1, 1, 1, 7044, 1, 7045, + 1, 7046, 1, 7048, 7047, 7049, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7050, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7051, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7052, 7047, 7053, 7047, 7054, 7047, 7055, 7047, + 7056, 7047, 7057, 7058, 7059, 7047, 7060, 7047, + 7061, 7047, 7062, 7047, 7063, 7047, 7064, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7047, 7047, 7047, 7047, 7047, 7047, 7047, + 7047, 7065, 7047, 7066, 7067, 7068, 7047, 7069, + 7047, 7070, 7047, 7071, 7047, 7072, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7073, 1, 1, 7074, 1, + 7075, 1, 7076, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7077, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7078, 7079, 1, 7080, 1, 7081, + 1, 7082, 1, 7083, 1, 7084, 1, 7085, + 1, 7086, 1, 7087, 1, 7088, 1, 7089, + 1, 7090, 1, 7091, 1, 7092, 1, 7093, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7094, + 1, 7095, 1, 7096, 1, 7097, 1, 7098, + 1, 7099, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7100, 1, 7101, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7102, 1, 7103, + 1, 7104, 1, 7105, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7106, 1, 1, 1, 1, + 1, 1, 1, 7107, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7108, 1, + 7109, 1, 7110, 1, 7111, 1, 7112, 1, + 7113, 1, 7114, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7115, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7116, 1, + 7117, 1, 7118, 1, 7119, 1, 7120, 1, + 7121, 1, 7122, 1, 7123, 1, 7124, 1, + 7125, 1, 7126, 1, 7127, 1, 7128, 1, + 7129, 1, 7130, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7131, 1, 7132, 1, + 7133, 1, 1, 1, 1, 7134, 7135, 1, + 1, 1, 7136, 1, 1, 7137, 7138, 1, + 1, 1, 7139, 1, 7140, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7141, 1, 7142, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7143, 1, 7144, 1, 7145, + 1, 7146, 1, 7147, 1, 7148, 1, 7149, + 1, 7150, 1, 7151, 1, 7152, 1, 1, + 7153, 1, 7154, 1, 7155, 1, 7156, 1, + 7157, 1, 7158, 1, 7159, 1, 7160, 1, + 7161, 1, 7162, 1, 7163, 1, 7164, 1, + 7165, 1, 7166, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7167, 1, 7168, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7169, 1, 7170, 1, + 7171, 1, 7172, 1, 7173, 1, 7174, 1, + 7175, 1, 7176, 1, 7177, 1, 7178, 1, + 7179, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7180, 1, + 7181, 1, 7182, 1, 7183, 1, 7184, 1, + 7185, 7186, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7187, 1, + 7188, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7189, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7190, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7191, 1, 7192, 1, 7193, 1, 7194, 1, + 7195, 1, 7196, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7197, 1, 7198, 1, + 7199, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7200, 1, + 7201, 1, 7202, 1, 7203, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7204, 1, + 7205, 1, 7206, 1, 7207, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7208, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7209, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7210, 1, 7211, 1, + 7212, 1, 7213, 1, 7214, 1, 7215, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7216, 1, 7217, 1, 7218, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7219, 1, 7220, 1, 7221, 1, + 7222, 1, 1, 7223, 1, 1, 1, 1, + 1, 7224, 1, 7225, 1, 7226, 1, 7227, + 1, 7228, 1, 7229, 1, 7230, 1, 7231, + 1, 7232, 1, 7233, 1, 7234, 1, 7235, + 1, 7236, 1, 7237, 1, 7238, 1, 1, + 1, 1, 1, 7239, 1, 7240, 1, 7241, + 1, 7242, 1, 7243, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7244, 1, 7245, + 1, 7246, 1, 7247, 1, 7248, 1, 7249, + 1, 7250, 1, 7251, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7252, 1, 7253, + 1, 7254, 1, 7255, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7256, 1, 7257, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7258, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7259, + 1, 7260, 1, 7261, 1, 7262, 1, 7263, + 1, 7264, 1, 7265, 1, 1, 1, 7266, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7267, 1, 1, 7268, 1, 1, 7269, 1, + 7270, 1, 1, 7271, 1, 1, 1, 1, + 1, 7272, 7273, 1, 7274, 1, 7275, 1, + 7276, 1, 7277, 1, 7278, 1, 7279, 1, + 7280, 1, 7281, 1, 7282, 1, 7283, 1, + 7284, 1, 7285, 1, 7286, 1, 7287, 1, + 7288, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7289, + 1, 7290, 1, 7291, 1, 7292, 1, 7293, + 1, 7294, 1, 7295, 1, 7296, 1, 7297, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7298, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7299, 1, 7300, 1, 7301, 1, + 7302, 1, 7303, 1, 7304, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7305, 1, 7306, 1, 7307, + 1, 7308, 1, 7309, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7310, 1, + 7311, 1, 7312, 1, 7313, 1, 7314, 1, + 7315, 1, 7316, 1, 7317, 1, 7318, 1, + 7319, 1, 7320, 1, 7321, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7322, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7323, 1, 7324, 1, 7325, 1, 7326, + 1, 7327, 1, 7328, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7329, 1, 7330, + 1, 7331, 1, 7332, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7333, + 1, 7334, 1, 7335, 1, 7336, 1, 7337, + 1, 7338, 1, 7339, 1, 7340, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7341, 1, 7342, 7343, 7344, + 7345, 7346, 7347, 7348, 1, 1, 7349, 7350, + 1, 7351, 7352, 1, 7353, 7354, 7355, 7356, + 7357, 1, 7358, 1, 7359, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7360, 1, 7361, 1, + 7362, 1, 7363, 1, 7364, 1, 7365, 1, + 7366, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7367, 1, 7368, 1, 7369, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7370, 1, 7371, 1, + 7372, 7373, 1, 1, 1, 1, 1, 1, + 7374, 1, 1, 1, 1, 1, 7375, 1, + 1, 1, 7376, 1, 7377, 1, 7378, 1, + 7379, 1, 7380, 1, 7381, 1, 7382, 1, + 7383, 1, 7384, 1, 7385, 1, 7386, 1, + 7387, 1, 7388, 1, 7389, 1, 7390, 1, + 7391, 1, 7392, 1, 7393, 1, 7394, 1, + 7395, 1, 7396, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7397, 1, 7398, 1, 7399, 1, 7400, + 1, 7401, 1, 7402, 1, 1, 7403, 1, + 7404, 1, 7405, 1, 7406, 1, 7407, 1, + 7408, 1, 7409, 1, 7410, 1, 7411, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7412, 1, 7413, 1, 7414, 1, 7415, + 1, 7416, 1, 7417, 1, 7418, 1, 7419, + 1, 7420, 1, 7421, 1, 1, 1, 1, + 1, 7422, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7423, 1, 7424, 1, + 7425, 1, 7426, 1, 7427, 1, 1, 1, + 1, 1, 1, 1, 1, 7428, 1, 7429, + 1, 7430, 1, 7431, 1, 7432, 1, 7433, + 1, 7434, 1, 7435, 1, 7436, 1, 7437, + 1, 7438, 1, 7439, 1, 1, 1, 7440, + 1, 1, 1, 7441, 1, 7442, 1, 7443, + 1, 7444, 1, 7445, 1, 7446, 1, 7447, + 1, 7448, 7449, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7450, 1, 7451, 1, + 7452, 1, 7453, 1, 7454, 1, 7455, 1, + 7456, 1, 7457, 1, 7458, 1, 7459, 1, + 7460, 1, 7461, 1, 7462, 1, 1, 1, + 7463, 1, 1, 1, 1, 1, 1, 7464, + 1, 7465, 1, 7466, 1, 7467, 1, 7468, + 1, 7469, 1, 7470, 1, 7471, 1, 7472, + 1, 7473, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7474, + 1, 1, 7475, 1, 1, 1, 1, 7476, + 1, 1, 1, 1, 1, 7477, 1, 1, + 1, 7478, 1, 1, 7479, 1, 7480, 1, + 7481, 1, 7482, 1, 7483, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7484, 7485, + 1, 1, 1, 1, 1, 1, 7486, 1, + 7487, 1, 7488, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7489, 1, + 7490, 1, 7491, 1, 7492, 1, 7493, 1, + 7494, 1, 7495, 1, 7496, 1, 7497, 1, + 7498, 1, 7499, 1, 7500, 1, 7501, 1, + 7502, 1, 7503, 1, 7504, 1, 1, 1, + 1, 1, 1, 1, 1, 7505, 1, 1, + 7506, 1, 7507, 1, 7508, 1, 7509, 1, + 7510, 1, 7511, 1, 7512, 1, 7513, 1, + 7514, 1, 7515, 7516, 1, 7517, 1, 7518, + 1, 7519, 1, 7520, 1, 7521, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7522, 1, 7523, 1, 7524, + 1, 7525, 1, 7526, 1, 7527, 1, 7528, + 1, 7529, 1, 7530, 1, 7531, 1, 7532, + 1, 7533, 7534, 1, 7535, 7536, 1, 1, + 7537, 7538, 1, 7539, 1, 1, 7540, 7541, + 1, 7542, 1, 7543, 1, 7544, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7545, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 7546, 7547, 1, + 7549, 7548, 7550, 7548, 7551, 7548, 7552, 7548, + 7553, 1, 1, 7554, 1, 7555, 1, 7556, + 1, 7557, 1, 7558, 1, 7559, 1, 7560, + 1, 7561, 1, 7562, 1, 1, 1, 1, + 1, 7563, 1, 1, 1, 7564, 1, 1, + 7565, 1, 1, 1, 7566, 1, 7567, 1, + 7568, 1, 7569, 1, 7570, 1, 7571, 1, + 7572, 1, 7573, 1, 7574, 1, 7575, 1, + 7576, 1, 7577, 1, 7578, 1, 7579, 1, + 7580, 1, 7581, 1, 7582, 1, 7583, 1, + 1, 1, 7584, 1, 7585, 1, 7586, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7587, 1, 7588, 1, 7589, 1, 7590, + 1, 7591, 1, 7592, 1, 7593, 1, 7594, + 1, 7595, 1, 7596, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7597, 1, 7598, 1, + 7599, 1, 7600, 1, 7601, 1, 7602, 1, + 7603, 1, 7604, 1, 7605, 1, 7606, 1, + 7607, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7608, 1, 7609, 1, + 7610, 1, 1, 1, 1, 1, 1, 1, + 7611, 1, 7612, 1, 7613, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7614, 1, 7615, 1, 7616, 1, 7617, + 1, 7618, 1, 7619, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7620, 7621, 7622, 7623, 7624, 1, 1, + 1, 1, 1, 1, 1, 7625, 1, 1, + 1, 1, 1, 7626, 7627, 1, 7628, 1, + 7629, 1, 7630, 1, 7631, 1, 7632, 1, + 7633, 1, 7634, 1, 7635, 1, 7636, 1, + 1, 1, 1, 1, 7637, 1, 7638, 1, + 7639, 1, 7640, 1, 7641, 1, 7642, 1, + 7643, 1, 7644, 1, 7645, 1, 7646, 1, + 7647, 1, 7648, 1, 7649, 1, 1, 1, + 1, 1, 1, 7650, 1, 1, 1, 1, + 7651, 1, 7652, 1, 7653, 1, 7654, 1, + 7655, 1, 7656, 1, 7657, 1, 7658, 1, + 7659, 1, 7660, 1, 7661, 1, 7662, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7663, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7664, 1, 7665, 1, + 7666, 1, 1, 1, 7667, 1, 1, 1, + 1, 7668, 7669, 1, 1, 1, 7670, 1, + 7671, 1, 7672, 1, 7673, 1, 7674, 1, + 7675, 1, 7676, 1, 7677, 1, 7678, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7679, 1, + 7680, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7681, 1, + 7682, 1, 7683, 1, 1, 1, 1, 1, + 1, 1, 1, 7684, 1, 1, 1, 1, + 7685, 1, 7686, 1, 7687, 1, 7688, 1, + 7689, 1, 7690, 1, 7691, 1, 7692, 1, + 7693, 1, 7694, 1, 7695, 1, 7696, 1, + 7697, 1, 7698, 1, 7699, 1, 7700, 1, + 7701, 1, 1, 1, 7702, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7703, 1, 7704, 1, 7705, 1, + 7706, 1, 7707, 1, 7708, 1, 7709, 1, + 7710, 1, 7711, 1, 7712, 1, 7713, 1, + 7714, 1, 7715, 1, 7716, 1, 7717, 1, + 7718, 1, 7719, 1, 7720, 1, 7721, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7722, 1, + 7723, 1, 7724, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7725, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7726, 1, 7727, 1, 7728, 1, 7729, 1, + 7730, 1, 7731, 1, 7732, 1, 7733, 1, + 7734, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7735, 1, 7736, 1, 7737, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7738, 1, 1, 1, 1, 1, 1, + 7739, 1, 7740, 1, 7741, 1, 7742, 1, + 7743, 1, 7744, 1, 7745, 1, 7746, 1, + 7747, 1, 7748, 1, 7749, 1, 7750, 1, + 7751, 1, 7752, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7753, 1, 7754, 1, 7755, + 1, 7756, 1, 7757, 1, 7758, 1, 7759, + 1, 7760, 1, 7761, 1, 7762, 1, 7763, + 1, 1, 1, 1, 1, 7764, 1, 7765, + 1, 7766, 1, 7767, 1, 7768, 1, 7769, + 1, 7770, 1, 7771, 1, 7772, 1, 7773, + 1, 1, 7774, 1, 1, 1, 1, 1, + 7775, 7776, 1, 1, 7777, 1, 7778, 1, + 7779, 1, 7780, 1, 7781, 1, 7782, 1, + 7783, 1, 7784, 1, 7785, 1, 7786, 1, + 7787, 1, 7788, 1, 7789, 1, 7790, 1, + 7791, 1, 7792, 1, 7793, 1, 7794, 1, + 7795, 1, 1, 1, 7796, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7797, 1, + 7798, 1, 7799, 1, 1, 1, 7800, 1, + 7801, 1, 7802, 1, 7803, 1, 7804, 1, + 7805, 1, 7806, 1, 7807, 1, 7808, 1, + 7809, 1, 7810, 1, 7811, 1, 7812, 1, + 7813, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7814, 1, 7815, 1, 7816, 1, + 7817, 1, 7818, 7819, 1, 1, 1, 1, + 1, 7820, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7821, 7822, 7823, 7824, 7825, 7826, + 1, 7827, 7828, 1, 1, 7829, 7830, 7831, + 7832, 7833, 1, 7834, 7835, 7836, 7837, 1, + 1, 7838, 1, 7839, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7840, 1, 7841, 1, + 7842, 1, 7843, 1, 7844, 1, 7845, 1, + 7846, 1, 7847, 1, 7848, 1, 7849, 1, + 7850, 1, 7851, 1, 7852, 1, 7853, 1, + 7854, 1, 7855, 1, 7856, 1, 7857, 1, + 7858, 7859, 7860, 1, 1, 1, 1, 1, + 1, 1, 1, 7861, 1, 1, 7862, 7863, + 1, 7864, 1, 7865, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7866, 1, 7867, 1, 7868, + 1, 7869, 1, 7870, 1, 7871, 1, 7872, + 1, 7873, 1, 7874, 1, 7875, 1, 7876, + 1, 7877, 1, 7878, 1, 7879, 1, 7880, + 1, 7881, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7882, 7883, 1, 1, 1, 1, + 1, 1, 7884, 1, 7885, 1, 7886, 1, + 7887, 1, 7888, 1, 7889, 1, 7890, 1, + 7891, 1, 7892, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7893, 7894, 7895, 1, 1, 7896, 1, 7897, + 1, 1, 1, 7898, 1, 1, 1, 7899, + 1, 1, 7900, 7901, 1, 1, 7902, 1, + 7903, 1, 7904, 1, 7905, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7906, + 1, 7907, 1, 7908, 1, 7909, 1, 7910, + 1, 7911, 1, 7912, 1, 7913, 1, 7914, + 1, 7915, 1, 7916, 1, 7917, 1, 7918, + 1, 7919, 1, 7920, 1, 7921, 1, 7922, + 1, 7923, 1, 7924, 1, 1, 1, 1, + 1, 1, 1, 7925, 1, 7926, 1, 7927, + 1, 7928, 1, 7929, 1, 7930, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7931, 1, 7932, 1, 7933, 1, 7934, 1, + 7935, 1, 7936, 7937, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7938, 1, 7939, 1, 7940, + 1, 7941, 1, 7942, 1, 7943, 1, 7944, + 1, 7945, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 7946, 1, 7947, 1, 7948, + 1, 1, 1, 1, 1, 7949, 1, 7950, + 1, 7951, 1, 7952, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 7953, 1, 7954, 1, 7955, 1, 7956, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7957, 1, 7958, 1, 7959, 1, 7960, 1, + 1, 1, 7961, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7962, 1, 1, 1, 7963, 1, + 7964, 1, 7965, 1, 7966, 1, 7967, 1, + 7968, 1, 1, 1, 1, 7969, 1, 7970, + 1, 7971, 1, 7972, 1, 7973, 1, 7974, + 1, 7975, 1, 7976, 1, 7977, 1, 7978, + 1, 1, 1, 1, 1, 1, 1, 1, + 7979, 1, 1, 1, 1, 7980, 1, 7981, + 1, 7982, 1, 7983, 1, 7984, 1, 7985, + 1, 7986, 1, 7987, 1, 7988, 1, 7989, + 1, 7990, 1, 7991, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7992, 1, 7993, 1, 7994, 1, + 7995, 1, 7996, 1, 7997, 1, 1, 1, + 7998, 1, 7999, 1, 8000, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8001, 1, 1, 1, 1, 1, + 1, 8002, 1, 1, 8003, 1, 8004, 1, + 8005, 1, 8006, 1, 8007, 1, 8008, 1, + 8009, 1, 8010, 1, 8011, 1, 8012, 1, + 8013, 1, 8014, 1, 1, 8015, 1, 1, + 1, 1, 1, 8016, 1, 8017, 1, 8018, + 1, 8019, 1, 8020, 1, 8021, 1, 8022, + 1, 8023, 1, 8024, 1, 8025, 1, 8026, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8027, 1, 8028, + 1, 8029, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8030, 1, 8031, 1, 8032, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8033, + 1, 8034, 1, 8035, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8036, 1, + 8037, 1, 8038, 1, 1, 1, 1, 1, + 1, 8039, 1, 1, 1, 1, 8040, 1, + 8041, 1, 8042, 1, 8043, 1, 1, 1, + 1, 1, 1, 8044, 1, 1, 1, 8045, + 1, 1, 1, 1, 1, 8046, 8047, 8048, + 1, 8049, 1, 8050, 1, 8051, 1, 8052, + 1, 8053, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8054, 1, 8055, 1, 8056, 1, + 8057, 1, 8058, 1, 8059, 1, 8060, 1, + 8061, 1, 8062, 1, 8063, 1, 8064, 1, + 8065, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8066, 1, 8067, 1, 8068, 1, 8069, + 1, 8070, 1, 8071, 1, 8072, 1, 8073, + 1, 8074, 1, 8075, 1, 8076, 1, 1, + 1, 1, 1, 1, 8077, 1, 8078, 1, + 8079, 1, 8080, 1, 8081, 1, 8082, 1, + 8083, 1, 8084, 1, 8085, 1, 8086, 1, + 8087, 1, 8088, 1, 8089, 1, 8090, 1, + 8091, 1, 8092, 1, 8093, 1, 8094, 1, + 8095, 1, 8096, 1, 8097, 1, 8098, 1, + 8099, 1, 8100, 1, 8101, 1, 8102, 1, + 8103, 1, 8104, 1, 8105, 1, 8106, 1, + 8107, 1, 8108, 1, 8109, 1, 8110, 1, + 8111, 1, 8112, 1, 8113, 1, 8114, 1, + 8115, 1, 8116, 1, 8117, 1, 8118, 1, + 8119, 1, 8120, 1, 8121, 1, 8122, 1, + 8123, 1, 8124, 1, 8125, 1, 8126, 1, + 8127, 1, 8128, 1, 8129, 1, 8130, 1, + 8131, 1, 8132, 1, 8133, 1, 8134, 1, + 8135, 1, 1, 1, 1, 1, 1, 8136, + 1, 1, 1, 1, 8137, 1, 8138, 1, + 8139, 1, 8140, 1, 8141, 1, 8142, 1, + 8143, 1, 8144, 1, 8145, 1, 8146, 1, + 8147, 1, 8148, 1, 8149, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8150, 1, 8151, 1, 8152, 1, + 8153, 1, 8154, 1, 8155, 1, 8156, 1, + 8157, 1, 8158, 1, 8159, 8160, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8161, 1, 1, 1, 8162, + 1, 8163, 1, 1, 1, 8164, 1, 8165, + 1, 8166, 1, 8167, 1, 8168, 1, 8169, + 1, 8170, 1, 8171, 1, 8172, 1, 1, + 1, 1, 8173, 1, 1, 1, 1, 1, + 8174, 1, 8175, 1, 8176, 1, 8177, 1, + 8178, 1, 8179, 1, 8180, 1, 8181, 1, + 8182, 1, 8183, 1, 8184, 1, 8185, 1, + 8186, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8187, + 1, 8188, 1, 8189, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8190, + 1, 8191, 1, 8192, 1, 8193, 1, 8194, + 1, 8195, 1, 8196, 1, 8197, 1, 8198, + 1, 8199, 1, 8200, 1, 8201, 1, 8202, + 1, 8203, 1, 8204, 1, 1, 1, 1, + 8205, 1, 1, 1, 1, 1, 1, 1, + 1, 8206, 1, 8207, 1, 8208, 1, 8209, + 1, 8210, 1, 8211, 1, 8212, 1, 8213, + 1, 8214, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8215, 1, 8216, 1, + 8217, 1, 8218, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8219, 1, 8220, 1, 8221, 8222, 1, + 1, 1, 1, 1, 1, 1, 1, 8223, + 1, 8224, 1, 8225, 1, 8226, 1, 8227, + 1, 8228, 1, 8229, 1, 8230, 1, 8231, + 1, 8232, 1, 8233, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8234, 8235, 1, + 1, 1, 1, 1, 8236, 1, 8237, 1, + 8238, 1, 8239, 1, 8240, 1, 8241, 1, + 8242, 1, 8243, 1, 8244, 1, 8245, 1, + 8246, 1, 8247, 1, 8248, 1, 8249, 1, + 8250, 8251, 8252, 8253, 8254, 8255, 1, 8256, + 8257, 1, 1, 8258, 8259, 1, 8260, 8261, + 8262, 8263, 8264, 8265, 8266, 1, 8267, 1, + 1, 8268, 1, 8269, 1, 8270, 1, 8271, + 1, 8272, 1, 8273, 1, 8274, 1, 8275, + 1, 8276, 1, 8277, 1, 8278, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8279, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8280, 1, 8281, 1, 8282, + 1, 1, 1, 8283, 1, 1, 1, 1, + 8284, 1, 8285, 1, 1, 8286, 1, 1, + 1, 1, 1, 8287, 1, 8288, 1, 8289, + 1, 8290, 1, 8291, 1, 8292, 1, 8293, + 1, 8294, 1, 8295, 1, 8296, 1, 8297, + 1, 8298, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8299, 1, 8300, 1, 8301, 1, + 8302, 1, 8303, 1, 8304, 1, 8305, 1, + 8306, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8307, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8308, 1, + 8309, 1, 8310, 1, 8311, 1, 8312, 1, + 8313, 1, 8314, 1, 8315, 1, 8316, 1, + 8317, 1, 8318, 1, 8319, 1, 8320, 1, + 8321, 1, 8322, 1, 8323, 1, 8324, 1, + 8325, 1, 8326, 1, 8327, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8328, 1, 1, 8329, 1, + 8330, 1, 8331, 1, 8332, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8333, 1, 8334, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8335, 1, 1, 1, 1, 1, 8336, 8337, + 1, 1, 1, 8338, 1, 8339, 1, 8340, + 1, 8341, 1, 8342, 1, 8343, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8344, + 1, 8345, 1, 8346, 1, 8347, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8348, 1, 8349, 1, 8350, 1, 8351, + 1, 8352, 1, 8353, 1, 8354, 1, 8355, + 1, 8356, 1, 8357, 1, 8358, 1, 8359, + 1, 1, 1, 1, 8360, 1, 8361, 1, + 8362, 1, 8363, 1, 8364, 1, 8365, 1, + 8366, 1, 8367, 1, 8368, 1, 8369, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8370, 1, 8371, 1, 8372, 1, + 8373, 1, 8374, 1, 8375, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8376, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8377, 1, 8378, 1, 8379, 1, + 8380, 1, 8381, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8382, 1, 8383, 1, 8384, + 1, 8385, 1, 8386, 1, 8387, 1, 8388, + 1, 8389, 1, 1, 8390, 1, 8391, 1, + 8392, 1, 8393, 1, 8394, 1, 8395, 1, + 8396, 1, 8397, 1, 8398, 1, 8399, 1, + 8400, 1, 8401, 1, 8402, 1, 1, 1, + 1, 1, 8403, 1, 8404, 1, 8405, 1, + 8406, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8407, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8408, 1, 8409, 1, 8410, + 1, 8411, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8412, 8413, 1, 8414, 1, 1, + 1, 1, 8415, 1, 8416, 1, 8417, 1, + 8418, 1, 8419, 1, 8420, 1, 8421, 1, + 8422, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8423, 1, + 8424, 1, 8425, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8426, 1, 8427, 1, + 8428, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8429, 1, 8430, 1, 8431, 1, + 8432, 1, 8433, 1, 8434, 1, 8435, 1, + 8436, 1, 8437, 1, 8438, 1, 8439, 1, + 8440, 1, 8441, 1, 8442, 1, 8443, 1, + 8444, 1, 8445, 1, 1, 1, 8446, 1, + 1, 1, 8447, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8448, 1, 8449, + 1, 1, 1, 1, 1, 1, 8450, 1, + 8451, 1, 8452, 1, 8453, 1, 8454, 1, + 8455, 1, 8456, 1, 8457, 1, 8458, 1, + 8459, 1, 8460, 1, 8461, 1, 8462, 1, + 8463, 1, 8464, 1, 8465, 1, 8466, 1, + 8467, 1, 8468, 1, 8469, 1, 8470, 1, + 1, 1, 1, 1, 1, 1, 8471, 1, + 8472, 1, 8473, 1, 8474, 1, 8475, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8476, 1, 8477, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8478, 1, 8479, 1, 8480, 1, + 1, 1, 1, 1, 8481, 1, 1, 1, + 8482, 1, 8483, 1, 8484, 1, 8485, 1, + 8486, 1, 8487, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8488, 1, 8489, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8490, 1, 8491, 1, 8492, 1, 8493, + 1, 8494, 1, 8495, 1, 8496, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8497, 1, 8498, 1, 8499, + 1, 8500, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8501, 1, 8502, 1, 8503, + 1, 8504, 1, 8505, 1, 8506, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8507, 1, 8508, + 1, 8509, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8510, 1, 8511, + 1, 8512, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8513, 1, 8514, 1, 8515, 1, 8516, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8517, + 1, 8518, 1, 8519, 1, 8520, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8521, 1, 8522, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8523, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8524, 1, 8525, + 1, 8526, 1, 8527, 1, 8528, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8529, + 1, 8530, 1, 8531, 1, 8532, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8533, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8534, 1, 8535, + 1, 8536, 1, 8537, 1, 8538, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8539, + 1, 8540, 1, 8541, 1, 8542, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8543, 1, 1, 1, 1, + 8544, 1, 8545, 1, 8546, 8547, 1, 8548, + 1, 8549, 1, 8550, 1, 8551, 1, 8552, + 1, 8553, 1, 8554, 1, 8555, 1, 8556, + 1, 1, 1, 1, 1, 1, 1, 8557, + 1, 1, 1, 1, 1, 1, 8558, 1, + 8559, 1, 8560, 1, 8561, 1, 8562, 1, + 8563, 1, 8564, 1, 8565, 1, 8566, 1, + 8567, 1, 8568, 1, 8569, 1, 8570, 1, + 8571, 1, 8572, 1, 8573, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8574, 1, 8575, + 1, 8576, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8577, 1, 8578, 1, + 8579, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8580, 1, 8581, + 1, 8582, 1, 8583, 1, 8584, 1, 8585, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8586, 1, 8587, 1, 8588, 1, + 8589, 1, 8590, 1, 8591, 1, 8592, 1, + 8593, 1, 8594, 1, 8595, 1, 8596, 1, + 8597, 1, 8598, 1, 8599, 8600, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8601, + 8602, 1, 8603, 1, 8604, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8605, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8606, 8607, 1, + 1, 1, 1, 1, 1, 1, 8608, 8609, + 1, 8610, 1, 8611, 8612, 1, 8613, 1, + 8614, 1, 8615, 1, 8616, 1, 8617, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8618, + 1, 8619, 1, 8620, 1, 8621, 1, 8622, + 1, 8623, 1, 8624, 1, 8625, 1, 8626, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8627, + 1, 8628, 1, 8629, 1, 8630, 1, 8631, + 1, 8632, 1, 8633, 1, 8634, 1, 8635, + 1, 8636, 1, 8637, 1, 8638, 1, 1, + 1, 8639, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8640, 1, 8641, + 1, 8642, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8643, 1, 1, 1, 1, + 1, 1, 1, 1, 8644, 1, 8645, 1, + 8646, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8647, 1, + 8648, 1, 8649, 1, 8650, 1, 8651, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8652, 1, 8653, 1, + 8654, 1, 8655, 1, 8656, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8657, 1, 8658, 1, 8659, 1, + 8660, 1, 8661, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8662, 1, 8663, 1, 8664, 1, 1, 1, + 1, 1, 1, 1, 1, 8665, 1, 1, + 1, 1, 8666, 1, 8667, 1, 8668, 1, + 8669, 1, 8670, 1, 8671, 1, 8672, 1, + 8673, 1, 8674, 1, 8675, 1, 8676, 1, + 8677, 1, 8678, 1, 8679, 1, 8680, 1, + 8681, 1, 8682, 1, 1, 1, 8683, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8684, 1, 8685, 1, + 8686, 1, 8687, 1, 8688, 1, 8689, 1, + 8690, 1, 8691, 1, 8692, 1, 8693, 1, + 8694, 1, 8695, 1, 8696, 1, 8697, 1, + 8698, 1, 8699, 1, 8700, 1, 8701, 1, + 8702, 1, 8703, 8704, 8705, 1, 1, 1, + 1, 1, 1, 1, 8706, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8707, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8708, 8709, 1, + 1, 8710, 1, 1, 1, 8711, 8712, 8713, + 1, 8714, 1, 1, 8715, 1, 8716, 1, + 8717, 1, 1, 1, 8718, 1, 8719, 1, + 8720, 1, 8721, 1, 8722, 1, 8723, 1, + 8724, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8725, 1, 8726, 1, 8727, 1, 8728, + 1, 8729, 1, 8730, 1, 1, 1, 1, + 1, 8731, 1, 8732, 1, 8733, 1, 8734, + 1, 8735, 1, 8736, 1, 8737, 1, 8738, + 1, 8739, 1, 8740, 1, 8741, 1, 8742, + 1, 8743, 1, 8744, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8745, 1, 8746, 1, 8747, + 1, 8748, 1, 8749, 1, 8750, 1, 8751, + 1, 8752, 1, 1, 1, 8753, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8754, 1, 8755, 1, 8756, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8757, + 1, 1, 1, 1, 1, 1, 1, 1, + 8758, 1, 8759, 1, 8760, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8761, 1, 8762, 1, 8763, 1, + 8764, 1, 8765, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8766, 1, 8767, 1, 8768, 1, 8769, 1, + 8770, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8771, 1, + 8772, 1, 8773, 1, 8774, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8775, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 8776, 1, 8777, 1, 8778, 1, 8779, + 1, 8780, 1, 8781, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8782, 1, 8783, + 1, 8784, 1, 8785, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8786, + 1, 8787, 1, 8788, 1, 8789, 1, 8790, + 1, 8791, 1, 8792, 1, 8793, 1, 8794, + 1, 8795, 1, 8796, 8797, 8798, 8799, 8800, + 8801, 1, 8802, 8803, 1, 1, 1, 1, + 1, 8804, 8805, 1, 8806, 8807, 1, 1, + 1, 8808, 1, 8809, 1, 1, 8810, 1, + 8811, 1, 8812, 1, 8813, 1, 8814, 1, + 8815, 1, 8816, 1, 8817, 1, 8818, 1, + 8819, 1, 1, 1, 8820, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8821, 1, 8822, 1, 8823, 1, 8824, 1, + 8825, 1, 8826, 1, 8827, 1, 8828, 1, + 8829, 1, 8830, 1, 8831, 1, 8832, 1, + 8833, 1, 8834, 1, 8835, 1, 8836, 1, + 8837, 1, 8838, 1, 8839, 1, 8840, 1, + 8841, 1, 1, 1, 8842, 1, 8843, 1, + 1, 1, 8844, 1, 8845, 1, 8846, 1, + 8847, 1, 8848, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8849, 1, 8850, 1, + 8851, 1, 8852, 1, 8853, 1, 8854, 1, + 8855, 1, 8856, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8857, 1, 1, 8858, 1, 8859, + 1, 8860, 1, 8861, 1, 8862, 1, 8863, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8864, 1, 8865, 1, 8866, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8867, 1, 8868, 1, 8869, 1, 8870, 1, + 8871, 1, 8872, 1, 8873, 1, 8874, 1, + 8875, 1, 8876, 1, 8877, 1, 8878, 1, + 8879, 1, 8880, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8881, 1, 8882, 1, + 8883, 1, 8884, 1, 8885, 1, 8886, 1, + 8887, 1, 8888, 1, 8889, 8890, 8891, 1, + 8892, 1, 8893, 1, 8894, 1, 8895, 1, + 8896, 1, 8898, 8897, 8897, 8897, 8897, 8897, + 8897, 8897, 8897, 8897, 8897, 8897, 8897, 8897, + 8897, 8897, 8897, 8897, 8897, 8897, 8897, 8897, + 8897, 8897, 8897, 8897, 8897, 8897, 8897, 8897, + 8897, 8897, 8897, 8897, 8897, 8897, 8897, 8897, + 8899, 8897, 8900, 8897, 8901, 8897, 8902, 8897, + 8903, 1, 8904, 1, 8905, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8906, + 1, 1, 8907, 1, 8908, 1, 8909, 1, + 8910, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8911, + 8912, 1, 1, 8913, 1, 8914, 1, 8915, + 1, 8916, 1, 8917, 1, 8918, 1, 8919, + 1, 8920, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8921, 1, 8922, + 1, 8923, 1, 8924, 1, 8925, 1, 8926, + 1, 8927, 1, 8928, 1, 8929, 1, 8930, + 1, 8931, 1, 8932, 1, 1, 1, 1, + 1, 1, 1, 8933, 1, 1, 1, 1, + 1, 1, 8934, 1, 8935, 1, 8936, 1, + 8937, 1, 8938, 1, 1, 8939, 8940, 1, + 1, 1, 1, 1, 1, 1, 8941, 1, + 1, 8942, 1, 1, 8943, 8944, 1, 8945, + 1, 8946, 1, 8947, 1, 8948, 1, 8949, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8950, 1, 1, 1, 1, 1, 1, 1, + 8951, 1, 1, 1, 1, 8952, 8953, 1, + 8954, 1, 8955, 1, 8956, 1, 8957, 1, + 8958, 1, 8959, 1, 8960, 1, 8961, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 8962, 1, 8963, 1, 8964, 1, 8965, 1, + 8966, 1, 8967, 1, 8968, 1, 8969, 1, + 8970, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8971, 1, 8972, 1, 8973, 1, + 8974, 1, 8975, 1, 8976, 1, 8977, 1, + 8978, 1, 8979, 1, 8980, 1, 8981, 1, + 8982, 1, 8983, 1, 8984, 1, 8985, 1, + 8986, 1, 8987, 1, 8988, 1, 8989, 1, + 8990, 1, 8991, 1, 8992, 1, 8993, 1, + 8994, 1, 8995, 1, 8996, 1, 8997, 1, + 8998, 1, 8999, 1, 1, 1, 1, 9000, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9001, 1, 9002, 1, 1, + 1, 1, 1, 1, 9003, 1, 9004, 1, + 9005, 1, 9006, 1, 9007, 1, 9008, 1, + 9009, 1, 9010, 1, 9011, 1, 9012, 1, + 9013, 1, 1, 1, 1, 1, 9014, 1, + 9015, 1, 9016, 1, 9017, 1, 9018, 1, + 9019, 1, 9020, 1, 9021, 1, 9022, 1, + 1, 1, 1, 1, 9023, 1, 9024, 1, + 9025, 1, 9026, 1, 9027, 1, 9028, 1, + 9029, 1, 9030, 1, 9031, 1, 9032, 1, + 9033, 1, 9034, 1, 9035, 1, 9036, 1, + 9037, 1, 9038, 1, 9039, 1, 9040, 1, + 9041, 1, 9042, 1, 9043, 1, 1, 1, + 1, 1, 1, 9044, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 9045, 9046, 9047, 9048, + 1, 9049, 9050, 9051, 1, 1, 1, 9052, + 9053, 1, 9054, 9055, 1, 9056, 9057, 9058, + 9059, 1, 9060, 1, 9061, 1, 9062, 1, + 9063, 1, 9064, 1, 9065, 1, 9066, 1, + 9067, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 9068, + 1, 9069, 1, 9070, 1, 9071, 1, 9072, + 1, 9073, 1, 9074, 1, 9075, 1, 9076, + 1, 9077, 1, 9078, 1, 9079, 1, 9080, + 1, 9081, 1, 9082, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9083, 1, 9084, 1, 9085, + 1, 9086, 1, 9087, 9088, 1, 1, 1, + 1, 1, 9089, 1, 9090, 1, 9091, 1, + 9092, 1, 9093, 1, 9094, 1, 9095, 1, + 9096, 1, 9097, 1, 9098, 1, 9099, 1, + 9100, 1, 1, 1, 1, 1, 1, 1, + 1, 9101, 1, 9102, 1, 9103, 1, 9104, + 1, 9105, 1, 9106, 1, 9107, 1, 9108, + 1, 9109, 1, 9110, 1, 9111, 9112, 1, + 9113, 1, 9114, 1, 1, 1, 1, 1, + 9115, 1, 9116, 1, 9117, 1, 9118, 1, + 9119, 1, 9120, 1, 9121, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9122, 1, 9123, + 1, 1, 9124, 1, 9125, 1, 9126, 1, + 9127, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 9128, 1, 9129, 1, 9130, 1, + 9131, 1, 9132, 1, 9133, 1, 9134, 1, + 9135, 1, 9136, 1, 9137, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 9138, + 1, 9139, 1, 9140, 1, 9141, 1, 9142, + 1, 1, 1, 1, 1, 1, 1, 1, + 9143, 1, 9144, 1, 9145, 1, 9146, 1, + 9147, 1, 9148, 1, 9149, 1, 1, 9150, + 1, 1, 1, 9151, 1, 1, 1, 9152, + 1, 1, 1, 1, 1, 1, 9153, 1, + 9154, 1, 9155, 1, 9156, 1, 9157, 1, + 9158, 1, 9159, 1, 9160, 1, 9161, 1, + 9162, 1, 9163, 1, 9164, 1, 9165, 1, + 9166, 1, 9167, 1, 9168, 1, 9169, 1, + 9170, 1, 9171, 1, 9172, 1, 9173, 1, + 9174, 1, 9175, 1, 1, 1, 1, 1, + 9176, 1, 9177, 1, 9178, 1, 9179, 1, + 9180, 1, 9181, 1, 9182, 1, 9183, 1, + 9184, 1, 9185, 1, 9186, 1, 9187, 1, + 9188, 1, 9189, 1, 9190, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9191, 1, 1, 1, 9192, 1, 9193, + 1, 9194, 1, 9195, 1, 9196, 1, 9197, + 1, 9198, 1, 9199, 1, 9200, 1, 9201, + 1, 9202, 1, 9203, 1, 9204, 1, 9205, + 1, 1, 1, 1, 1, 9206, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9207, 1, 9208, 1, 1, 9209, 1, 9210, + 1, 9211, 1, 9212, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9213, 1, 9214, + 1, 9215, 1, 9216, 1, 9217, 1, 9218, + 1, 9219, 1, 9220, 1, 9221, 1, 9222, + 1, 9223, 1, 9224, 1, 9225, 1, 9226, + 1, 9227, 1, 9228, 1, 1, 1, 1, + 9229, 1, 1, 1, 1, 1, 1, 1, + 1, 9230, 1, 9231, 1, 9232, 1, 9233, + 1, 9234, 1, 9235, 1, 9236, 1, 9237, + 1, 9238, 1, 9239, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 9240, 1, + 9241, 1, 9242, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 9243, 1, + 9244, 1, 9245, 1, 9246, 1, 9247, 1, + 9248, 1, 9249, 1, 9250, 1, 9251, 1, + 9252, 1, 9253, 1, 9254, 9255, 1, 9256, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 9257, 1, 9258, 9259, + 9260, 9261, 1, 1, 1, 1, 1, 9262, + 1, 9263, 9264, 9265, 1, 9266, 9267, 1, + 1, 1, 1, 1, 1, 9268, 1, 9269, + 1, 9270, 1, 9271, 1, 9272, 1, 9273, + 1, 9274, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 9275, 1, 9276, 1, + 9277, 1, 9278, 1, 9279, 1, 9280, 1, + 9281, 1, 1, 1, 9282, 1, 9283, 1, + 9284, 1, 9285, 1, 9286, 1, 9287, 1, + 1, 1, 1, 1, 9288, 1, 1, 9289, + 1, 9290, 1, 9291, 9292, 9293, 1, 9294, + 1, 9295, 1, 9296, 1, 9297, 1, 9298, + 1, 9299, 1, 9300, 1, 9301, 1, 9302, + 1, 9303, 1, 9304, 1, 9305, 1, 9306, + 1, 9307, 1, 9308, 1, 9309, 1, 9310, + 1, 9311, 1, 9312, 1, 9313, 9314, 1, + 1, 1, 1, 1, 1, 1, 1, 9315, + 1, 9316, 1, 9317, 1, 9318, 1, 9319, + 1, 9320, 1, 9321, 1, 9322, 1, 9323, + 1, 9324, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 9325, 1, + 9326, 1, 9327, 1, 9328, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9329, 1, 9330, 1, 9331, 1, 9332, 1, + 9333, 1, 9334, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9335, 1, 9336, 1, 9337, 1, 9338, 1, + 9339, 1, 9340, 1, 9341, 1, 9342, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 9343, 1, 9344, 1, + 9345, 1, 9346, 1, 9347, 1, 9348, 1, + 9349, 1, 9350, 1, 9351, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 9352, 1, 9353, 1, 9354, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9355, 1, 9356, 1, 9357, 1, 9358, 1, + 9359, 1, 9360, 1, 9361, 1, 9362, 1, + 9363, 1, 9364, 1, 9365, 1, 9366, 1, + 1, 1, 1, 1, 9367, 1, 9368, 1, + 9369, 1, 9370, 1, 9371, 1, 9372, 1, + 9373, 1, 9374, 1, 9375, 1, 9376, 1, + 9377, 1, 9378, 1, 9379, 1, 9380, 1, + 9381, 1, 9382, 1, 9383, 1, 1, 1, + 1, 1, 1, 9384, 1, 1, 1, 1, + 1, 9385, 1, 9386, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 9387, 1, 1, 9388, 1, 9389, + 1, 9390, 1, 9391, 1, 9392, 1, 9393, + 1, 9394, 1, 9395, 1, 9396, 1, 9397, + 1, 9398, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9399, 1, 9400, 1, 9401, + 1, 9402, 1, 9403, 1, 9404, 1, 9405, + 1, 9406, 1, 9407, 1, 9408, 1, 9409, + 1, 9410, 1, 9411, 1, 9412, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9413, 1, 9414, 1, 9415, + 1, 9416, 1, 9417, 1, 9418, 1, 9419, + 1, 9420, 1, 9421, 1, 9422, 1, 9423, + 1, 9424, 1, 9425, 1, 9426, 1, 9427, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9428, 1, 9429, 1, 9430, 1, 9431, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9432, 1, 9433, + 1, 9434, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9435, 1, 9436, 1, 9437, 1, 9438, + 1, 9439, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9440, 1, 9441, 1, 9442, 1, 9443, + 1, 9444, 1, 9445, 1, 9446, 1, 9447, + 1, 9448, 1, 9449, 1, 9450, 9451, 1, + 1, 1, 1, 1, 1, 1, 1, 9452, + 9453, 1, 9454, 9455, 1, 9456, 1, 9457, + 1, 9458, 1, 9459, 1, 9460, 1, 1, + 1, 1, 9461, 1, 9462, 1, 1, 1, + 1, 9463, 1, 9464, 1, 9465, 1, 9466, + 1, 9467, 1, 9468, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9469, 1, 9470, 1, 9471, 1, 9472, + 1, 9473, 1, 9474, 1, 9475, 1, 9476, + 1, 9477, 1, 9478, 1, 9479, 1, 9480, + 1, 9481, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9482, 1, 9483, 1, 9484, + 1, 9485, 1, 9486, 1, 9487, 1, 9488, + 1, 9489, 1, 9490, 9491, 1, 9492, 1, + 9493, 9494, 1, 1, 9495, 9496, 9497, 9498, + 1, 1, 9499, 9500, 1, 9501, 9502, 9503, + 1, 9504, 1, 1, 1, 1, 1, 1, + 1, 9505, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9506, 1, 9507, + 1, 9508, 1, 9509, 1, 9510, 1, 9511, + 1, 9512, 1, 9513, 1, 9514, 1, 9515, + 1, 9516, 1, 9517, 1, 9518, 1, 9519, + 1, 9520, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9521, 1, 9522, 1, 9523, 1, 9524, + 1, 9525, 1, 9526, 1, 9527, 1, 9528, + 1, 9529, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9530, 1, 9531, 1, 9532, 1, 9533, + 1, 9534, 1, 9535, 1, 9536, 1, 9537, + 1, 9538, 1, 9539, 1, 9540, 1, 9541, + 1, 9542, 1, 9543, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 9544, + 1, 1, 1, 9545, 1, 9546, 1, 9547, + 1, 9548, 1, 9549, 1, 1, 1, 1, + 1, 9550, 1, 9551, 1, 9552, 1, 9553, + 1, 9554, 1, 9555, 1, 9556, 1, 9557, + 1, 9558, 1, 9559, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9560, 1, 9561, 1, 9562, + 1, 9563, 1, 9564, 1, 9565, 1, 9566, + 1, 9567, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 9568, + 1, 9569, 1, 9570, 1, 9571, 1, 9572, + 1, 9573, 1, 9574, 1, 9575, 1, 1, + 1, 9576, 1, 9577, 1, 9578, 1, 9579, + 1, 9580, 1, 9581, 1, 9582, 1, 9583, + 1, 9584, 1, 9585, 1, 9586, 1, 9587, + 1, 9588, 1, 9589, 1, 9590, 1, 9591, + 1, 9592, 1, 9593, 1, 9594, 9595, 1, + 1, 9596, 1, 1, 1, 1, 1, 9597, + 1, 1, 1, 9598, 1, 9599, 1, 9600, + 1, 9601, 1, 1, 1, 9602, 1, 9603, + 1, 9604, 1, 9605, 1, 9606, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9607, 1, 9608, + 1, 9609, 1, 9610, 1, 9611, 1, 9612, + 1, 9613, 1, 9614, 1, 9615, 1, 9616, + 1, 9617, 1, 9618, 1, 9619, 1, 9620, + 1, 9621, 1, 9622, 1, 9623, 1, 9624, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9625, 1, 9626, 1, 9627, 1, 9628, + 1, 9629, 1, 9630, 9631, 9632, 9633, 1, + 9634, 9635, 1, 1, 1, 1, 1, 9636, + 1, 1, 1, 9637, 1, 1, 1, 9638, + 1, 9639, 1, 9640, 1, 9641, 1, 9642, + 1, 9643, 1, 9644, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9645, 1, 9646, 1, 9647, + 1, 9648, 1, 9649, 1, 9650, 1, 9651, + 1, 9652, 1, 9653, 1, 9654, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 9655, 1, 9656, 1, + 9657, 1, 9658, 1, 9659, 1, 9660, 1, + 9661, 1, 9662, 1, 9663, 1, 9664, 1, + 9665, 1, 9666, 1, 9667, 1, 9668, 1, + 9669, 1, 9670, 1, 9671, 1, 9672, 1, + 9673, 1, 9674, 1, 9675, 1, 9676, 1, + 9677, 1, 9678, 1, 9679, 1, 1, 1, + 9680, 1, 9681, 1, 9682, 1, 9683, 1, + 9684, 9685, 9686, 9687, 9688, 9689, 9690, 9691, + 9692, 9693, 9694, 9695, 9696, 9697, 9698, 9699, + 9700, 9701, 9702, 9703, 9704, 9705, 9706, 9707, + 9708, 9709, 1, 1, 1, 1, 1, 1, + 9710, 9711, 9712, 9713, 9714, 9715, 9716, 9717, + 9718, 9719, 9720, 9721, 9722, 9723, 9724, 9725, + 9726, 9727, 9728, 9729, 9730, 9731, 9732, 9733, + 9734, 9735, 1, 9737, 9736, 9739, 9738, 9741, + 9740, 9743, 9742, 9745, 9744, 9747, 9746, 9749, + 9748, 9751, 9750, 9753, 9752, 9755, 9754, 9757, + 9756, 9759, 9758, 9761, 9760, 9763, 9762, 9765, + 9764, 9767, 9766, 9769, 9768, 9771, 9770, 9773, + 9772, 9775, 9774, 9777, 9776, 9779, 9778, 9781, + 9780, 9783, 9782, 9785, 9784, 9787, 9786, 9789, + 9788, 9791, 9790, 9793, 9792, 9795, 9794, 9797, + 9796, 9799, 9798, 9801, 9800, 9803, 9802, 9805, + 9804, 9807, 9806, 9809, 9808, 9811, 9810, 9813, + 9812, 9815, 9814, 9817, 9816, 9819, 9818, 9821, + 9820, 9823, 9822, 9825, 9824, 9827, 9826, 9829, + 9828, 9831, 9830, 9833, 9832, 9832, 9832, 9832, + 9832, 9832, 9832, 9832, 9832, 9832, 9832, 9832, + 9832, 9832, 9832, 9832, 9832, 9832, 9832, 9832, + 9832, 9832, 9832, 9832, 9832, 9832, 9832, 9832, + 9832, 9832, 9832, 9832, 9832, 9832, 9832, 9832, + 9832, 9832, 9832, 9832, 9832, 9834, 9832, 9836, + 9835, 9835, 9835, 9835, 9835, 9835, 9835, 9835, + 9835, 9835, 9835, 9835, 9835, 9835, 9835, 9835, + 9835, 9835, 9835, 9835, 9835, 9835, 9835, 9835, + 9835, 9835, 9835, 9835, 9835, 9835, 9835, 9835, + 9835, 9835, 9835, 9835, 9835, 9835, 9835, 9835, + 9835, 9835, 9835, 9835, 9835, 9835, 9835, 9835, + 9835, 9835, 9835, 9835, 9835, 9835, 9835, 9837, + 9835, 9839, 9838, 9841, 9840, 9843, 9842, 9842, + 9842, 9842, 9842, 9842, 9842, 9842, 9842, 9842, + 9842, 9842, 9842, 9842, 9842, 9842, 9842, 9842, + 9842, 9842, 9842, 9842, 9842, 9842, 9842, 9842, + 9842, 9842, 9842, 9842, 9842, 9842, 9842, 9842, + 9842, 9842, 9842, 9842, 9842, 9842, 9842, 9842, + 9842, 9842, 9842, 9842, 9842, 9842, 9842, 9842, + 9842, 9844, 9842, 9846, 9845, 9848, 9847, 9850, + 9849, 9852, 9851, 9854, 9853, 9856, 9855, 9858, + 9857, 9860, 9859, 9862, 9861, 9861, 9861, 9861, + 9861, 9861, 9861, 9861, 9861, 9861, 9861, 9861, + 9861, 9861, 9861, 9861, 9861, 9861, 9861, 9861, + 9861, 9861, 9861, 9861, 9861, 9861, 9861, 9861, + 9861, 9861, 9861, 9861, 9861, 9861, 9861, 9861, + 9861, 9861, 9861, 9863, 9864, 9861, 9861, 9861, + 9861, 9861, 9861, 9861, 9865, 9861, 9861, 9861, + 9861, 9866, 9867, 9861, 9869, 9868, 9871, 9870, + 9873, 9872, 9875, 9874, 9877, 9876, 9879, 9878, + 9881, 9880, 9883, 9882, 9882, 9882, 9882, 9882, + 9882, 9882, 9882, 9882, 9882, 9882, 9882, 9882, + 9882, 9882, 9882, 9882, 9882, 9882, 9882, 9882, + 9882, 9882, 9882, 9882, 9882, 9882, 9882, 9882, + 9882, 9882, 9882, 9882, 9882, 9882, 9882, 9882, + 9882, 9882, 9884, 9885, 9882, 9882, 9882, 9886, + 9887, 9882, 9882, 9888, 9882, 9882, 9882, 9882, + 9889, 9890, 9882, 9892, 9891, 9894, 9893, 9896, + 9895, 9898, 9897, 9900, 9899, 9899, 9899, 9899, + 9899, 9899, 9899, 9899, 9899, 9899, 9899, 9899, + 9899, 9899, 9899, 9899, 9899, 9899, 9899, 9899, + 9899, 9899, 9899, 9899, 9899, 9899, 9899, 9899, + 9899, 9899, 9899, 9899, 9899, 9899, 9899, 9899, + 9899, 9899, 9899, 9899, 9899, 9899, 9899, 9899, + 9899, 9901, 9899, 9899, 9899, 9899, 9902, 9899, + 9904, 9903, 9906, 9905, 9908, 9907, 9910, 9909, + 9912, 9911, 9914, 9913, 9916, 9915, 9918, 9917, + 9920, 9919, 9922, 9921, 9921, 9921, 9921, 9921, + 9921, 9921, 9921, 9921, 9921, 9921, 9921, 9921, + 9921, 9921, 9921, 9921, 9921, 9921, 9921, 9921, + 9921, 9921, 9921, 9921, 9921, 9921, 9921, 9921, + 9921, 9921, 9921, 9921, 9921, 9921, 9921, 9921, + 9921, 9921, 9921, 9921, 9921, 9921, 9921, 9921, + 9921, 9921, 9921, 9923, 9921, 9925, 9924, 9927, + 9926, 9929, 9928, 9931, 9930, 9933, 9932, 9935, + 9934, 9937, 9936, 9939, 9938, 9941, 9940, 9943, + 9942, 9945, 9944, 9947, 9946, 9949, 9948, 9948, + 9948, 9948, 9948, 9948, 9948, 9948, 9948, 9948, + 9948, 9948, 9948, 9948, 9948, 9948, 9948, 9948, + 9948, 9948, 9948, 9948, 9948, 9948, 9948, 9948, + 9948, 9948, 9948, 9948, 9948, 9948, 9948, 9948, + 9948, 9948, 9948, 9948, 9950, 9948, 9951, 9948, + 9953, 9952, 9955, 9954, 9957, 9956, 9959, 9958, + 9961, 9960, 9963, 9962, 9965, 9964, 9967, 9966, + 0 +}; + +static const short _char_ref_trans_targs[] = { + 2, 0, 5, 6, 10, 15, 19, 21, + 25, 29, 33, 35, 41, 53, 56, 63, + 67, 3, 4, 7624, 7625, 7, 8, 9, + 7626, 11, 12, 13, 14, 7623, 16, 18, + 17, 7627, 7623, 20, 7623, 22, 23, 24, + 7628, 26, 27, 28, 7623, 30, 31, 32, + 7623, 34, 7623, 36, 39, 37, 38, 7623, + 40, 7623, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 7623, 54, 55, + 7629, 57, 59, 58, 7623, 60, 61, 62, + 7623, 64, 65, 66, 7630, 68, 7631, 70, + 83, 85, 101, 103, 106, 110, 113, 71, + 78, 72, 73, 74, 75, 76, 77, 7623, + 79, 80, 7623, 81, 82, 7623, 84, 7623, + 86, 91, 99, 87, 88, 89, 90, 7623, + 92, 93, 94, 95, 96, 97, 98, 7623, + 100, 7623, 102, 7623, 104, 105, 7623, 107, + 108, 109, 7623, 111, 112, 7623, 114, 115, + 116, 117, 7623, 119, 122, 124, 152, 168, + 171, 184, 186, 188, 210, 256, 320, 324, + 327, 120, 121, 7623, 123, 7632, 125, 129, + 147, 126, 127, 128, 7623, 7623, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 7623, + 148, 149, 150, 151, 7623, 153, 157, 160, + 163, 154, 155, 156, 7623, 158, 159, 7633, + 161, 162, 7623, 164, 165, 166, 167, 7623, + 169, 170, 7623, 172, 177, 173, 174, 175, + 176, 7623, 178, 179, 180, 181, 182, 183, + 7623, 185, 7623, 187, 7623, 189, 190, 191, + 192, 193, 196, 201, 205, 194, 195, 7623, + 197, 198, 199, 200, 7623, 202, 203, 204, + 7623, 206, 207, 208, 209, 7623, 211, 212, + 233, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 7623, 234, 235, + 236, 237, 238, 239, 240, 251, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, + 7623, 252, 253, 254, 255, 7623, 257, 261, + 283, 291, 258, 259, 7623, 260, 7623, 262, + 268, 271, 263, 264, 265, 266, 267, 7623, + 269, 270, 7623, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 7623, 284, + 285, 7623, 286, 287, 288, 289, 290, 7623, + 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 7623, 321, 322, 323, + 7623, 325, 326, 7623, 328, 7623, 329, 330, + 331, 7623, 333, 340, 343, 346, 349, 359, + 365, 369, 371, 423, 649, 7623, 334, 335, + 336, 337, 338, 339, 7623, 341, 342, 7623, + 344, 345, 7623, 347, 348, 7623, 350, 354, + 356, 351, 352, 353, 7623, 355, 7623, 357, + 358, 7623, 360, 364, 361, 362, 363, 7623, + 7623, 366, 7623, 367, 368, 7623, 370, 7623, + 372, 412, 373, 408, 374, 375, 376, 377, + 378, 379, 380, 381, 386, 398, 403, 382, + 383, 384, 385, 7623, 387, 388, 389, 7623, + 390, 391, 392, 393, 394, 395, 396, 397, + 7623, 399, 400, 401, 402, 7623, 404, 405, + 406, 407, 7623, 409, 410, 411, 7623, 413, + 414, 415, 416, 417, 418, 419, 420, 421, + 422, 7623, 424, 426, 435, 558, 425, 7623, + 7623, 427, 430, 428, 429, 7623, 431, 432, + 433, 434, 7623, 436, 437, 438, 439, 454, + 464, 518, 531, 547, 440, 441, 442, 443, + 444, 445, 446, 447, 448, 449, 450, 451, + 452, 453, 7623, 455, 456, 457, 7623, 458, + 459, 460, 461, 462, 463, 7623, 465, 486, + 466, 467, 468, 473, 483, 469, 470, 471, + 472, 7623, 474, 475, 476, 477, 478, 479, + 480, 481, 482, 7623, 484, 485, 7623, 487, + 488, 489, 508, 490, 491, 492, 493, 498, + 494, 495, 496, 497, 7623, 499, 500, 501, + 502, 503, 504, 505, 506, 507, 7623, 509, + 510, 511, 512, 513, 514, 515, 516, 517, + 7623, 519, 520, 521, 522, 523, 528, 524, + 525, 526, 527, 7623, 529, 530, 7623, 532, + 533, 538, 534, 535, 536, 537, 7623, 539, + 540, 541, 542, 543, 544, 545, 546, 7623, + 548, 549, 550, 551, 552, 553, 554, 555, + 556, 557, 7623, 559, 560, 575, 580, 613, + 636, 644, 561, 562, 563, 564, 7623, 565, + 568, 566, 567, 7623, 569, 570, 571, 572, + 573, 574, 7623, 576, 577, 578, 579, 7623, + 581, 582, 583, 584, 595, 604, 585, 586, + 587, 588, 589, 590, 591, 592, 593, 594, + 7623, 596, 597, 598, 599, 600, 601, 602, + 603, 7623, 605, 606, 607, 608, 609, 7623, + 610, 611, 612, 7623, 614, 615, 616, 617, + 618, 627, 619, 620, 621, 622, 623, 624, + 625, 626, 7623, 628, 629, 630, 631, 632, + 7623, 633, 634, 635, 7623, 637, 638, 7623, + 639, 640, 641, 642, 643, 7623, 645, 646, + 647, 648, 7623, 650, 652, 651, 7623, 653, + 654, 655, 7623, 657, 659, 660, 664, 672, + 675, 677, 681, 687, 720, 726, 732, 749, + 754, 756, 758, 658, 7623, 7634, 661, 662, + 663, 7635, 665, 669, 671, 666, 667, 668, + 7623, 670, 7636, 7623, 673, 674, 7623, 676, + 7623, 678, 679, 680, 7637, 682, 683, 684, + 685, 686, 7623, 688, 691, 689, 690, 7623, + 692, 693, 694, 705, 695, 696, 697, 698, + 699, 700, 701, 702, 703, 704, 7623, 706, + 707, 708, 709, 710, 711, 712, 713, 714, + 715, 716, 717, 718, 719, 7623, 721, 724, + 722, 723, 7623, 725, 7623, 727, 728, 729, + 730, 731, 7623, 733, 734, 741, 735, 7623, + 736, 737, 738, 739, 740, 7623, 742, 743, + 744, 745, 746, 747, 748, 7623, 750, 752, + 751, 7623, 753, 7623, 755, 7623, 757, 7638, + 759, 763, 760, 761, 762, 7623, 764, 765, + 766, 767, 768, 769, 770, 771, 772, 7623, + 774, 776, 778, 809, 824, 775, 7623, 777, + 7623, 779, 780, 781, 782, 783, 794, 784, + 785, 786, 787, 788, 789, 790, 791, 792, + 793, 7623, 795, 796, 797, 798, 799, 800, + 801, 802, 803, 804, 805, 806, 807, 808, + 7623, 810, 812, 816, 811, 7623, 813, 814, + 815, 7623, 817, 818, 819, 820, 821, 822, + 823, 7623, 825, 826, 7623, 828, 7639, 831, + 836, 841, 850, 853, 855, 856, 859, 909, + 912, 829, 830, 7623, 832, 833, 834, 7623, + 835, 7623, 837, 838, 839, 840, 7623, 842, + 846, 849, 843, 844, 845, 7623, 847, 848, + 7623, 7623, 851, 852, 7623, 854, 7623, 7623, + 857, 858, 7623, 860, 861, 862, 863, 864, + 865, 874, 883, 890, 894, 904, 866, 867, + 868, 869, 7623, 870, 871, 872, 873, 7623, + 875, 876, 877, 878, 879, 880, 881, 882, + 7623, 884, 885, 886, 887, 888, 889, 7623, + 891, 892, 893, 7623, 895, 896, 897, 898, + 899, 900, 901, 902, 903, 7623, 905, 906, + 907, 908, 7623, 910, 911, 7623, 7623, 914, + 919, 924, 928, 930, 941, 956, 963, 915, + 916, 917, 918, 7623, 920, 923, 921, 922, + 7623, 7623, 925, 926, 927, 7623, 929, 7623, + 931, 932, 933, 934, 935, 936, 937, 938, + 939, 940, 7623, 942, 944, 943, 7623, 945, + 946, 947, 948, 949, 950, 951, 952, 953, + 954, 955, 7623, 957, 959, 958, 7623, 960, + 961, 962, 7623, 964, 965, 966, 974, 967, + 968, 969, 970, 971, 972, 973, 7623, 975, + 976, 977, 978, 7623, 980, 983, 987, 990, + 994, 998, 1001, 1003, 1007, 1023, 1055, 1063, + 1066, 1071, 981, 982, 7623, 984, 985, 986, + 7623, 988, 989, 7623, 991, 992, 993, 7640, + 995, 997, 996, 7641, 7623, 999, 1000, 7623, + 1002, 7623, 1004, 1005, 1006, 7642, 7623, 1008, + 1018, 1009, 1011, 1010, 7623, 1012, 1013, 1014, + 1015, 1016, 1017, 7623, 1019, 1020, 1021, 1022, + 7623, 1024, 1038, 7623, 1025, 1026, 1030, 1027, + 1028, 1029, 7623, 1031, 1032, 1033, 1034, 1035, + 1036, 1037, 7623, 1039, 1040, 1041, 1042, 1043, + 1044, 1045, 1050, 1046, 1047, 1048, 1049, 7623, + 1051, 1052, 1053, 1054, 7623, 1056, 1059, 1061, + 1057, 1058, 7623, 1060, 7623, 1062, 7623, 1064, + 1065, 7623, 1067, 1068, 1069, 1070, 7623, 1072, + 1075, 1073, 1074, 7623, 7643, 1077, 1082, 1084, + 1087, 1094, 1078, 1081, 1079, 1080, 7623, 7623, + 1083, 7623, 1085, 1086, 7623, 1088, 1090, 1089, + 7623, 1091, 1092, 1093, 7623, 1095, 1096, 1097, + 7623, 1099, 1102, 1105, 1109, 1115, 1117, 1120, + 1100, 1101, 7623, 1103, 1104, 7623, 1106, 1107, + 1108, 7623, 1110, 1114, 1111, 1112, 1113, 7623, + 7623, 1116, 7623, 1118, 1119, 7623, 1121, 1122, + 7623, 1124, 7644, 1127, 1148, 1158, 1383, 1385, + 1394, 1399, 1484, 1492, 1125, 1126, 7623, 1128, + 1132, 1136, 1138, 1146, 1129, 1130, 1131, 7623, + 1133, 1134, 1135, 7623, 1137, 7623, 1139, 1140, + 1141, 1142, 1143, 1144, 1145, 7623, 1147, 7623, + 1149, 1153, 1157, 1150, 1151, 1152, 7623, 1154, + 1155, 1156, 7623, 7623, 1159, 1334, 1160, 1161, + 1190, 1197, 1230, 1235, 1251, 1280, 1310, 1319, + 1324, 1162, 1173, 1163, 1164, 1165, 1166, 1167, + 1168, 1169, 1170, 1171, 1172, 7623, 1174, 1175, + 1176, 7623, 1177, 1180, 1178, 1179, 7623, 1181, + 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, + 7623, 1191, 1192, 1193, 1194, 1195, 1196, 7623, + 1198, 1199, 1210, 1200, 1201, 1202, 1203, 1204, + 1205, 1206, 1207, 1208, 1209, 7623, 1211, 1212, + 1221, 1213, 1214, 1215, 1216, 1217, 1218, 1219, + 1220, 7623, 1222, 1223, 1224, 1225, 1226, 7623, + 1227, 1228, 1229, 7623, 1231, 1232, 1233, 1234, + 7623, 1236, 1237, 1238, 1239, 1240, 1245, 1241, + 1242, 1243, 1244, 7623, 1246, 1247, 1248, 1249, + 1250, 7623, 1252, 1265, 1253, 7623, 1254, 1259, + 1255, 1256, 1257, 1258, 7623, 1260, 1261, 1262, + 1263, 1264, 7623, 1266, 1267, 1268, 1269, 1270, + 1271, 7623, 1272, 1275, 1273, 1274, 7623, 1276, + 1277, 1278, 1279, 7623, 1281, 1282, 1292, 1301, + 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, + 1291, 7623, 1293, 1294, 1295, 1296, 1297, 1298, + 1299, 1300, 7623, 1302, 1303, 1304, 1305, 1306, + 7623, 1307, 1308, 1309, 7623, 1311, 1312, 1313, + 1314, 1315, 7623, 1316, 1317, 1318, 7623, 1320, + 1321, 1322, 1323, 7623, 1325, 1326, 1327, 1328, + 1329, 1330, 1331, 1332, 1333, 7623, 1335, 1336, + 1348, 1357, 1364, 1368, 1378, 1337, 1338, 1339, + 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, + 7623, 1349, 1350, 1351, 1352, 1353, 1354, 1355, + 1356, 7623, 1358, 1359, 1360, 1361, 1362, 1363, + 7623, 1365, 1366, 1367, 7623, 1369, 1370, 1371, + 1372, 1373, 1374, 1375, 1376, 1377, 7623, 1379, + 1380, 1381, 1382, 7623, 1384, 7623, 7623, 1386, + 1387, 1388, 1389, 1390, 1391, 1392, 1393, 7623, + 1395, 1396, 1397, 1398, 7623, 1400, 1460, 1462, + 1401, 1402, 1421, 1431, 1450, 1403, 1404, 1405, + 1406, 1411, 1407, 1408, 1409, 1410, 7623, 1412, + 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, + 7623, 1422, 1423, 1424, 1425, 1426, 1427, 1428, + 1429, 1430, 7623, 1432, 1433, 1434, 1435, 1440, + 1436, 1437, 1438, 1439, 7623, 1441, 1442, 1443, + 1444, 1445, 1446, 1447, 1448, 1449, 7623, 1451, + 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, + 7623, 1461, 7623, 1463, 1464, 1465, 1474, 1466, + 1467, 1468, 1469, 1470, 1471, 1472, 1473, 7623, + 1475, 1476, 1477, 1478, 1479, 1480, 1481, 1482, + 1483, 7623, 1485, 1487, 1488, 1486, 7623, 7623, + 1489, 1490, 1491, 7623, 7623, 1494, 1496, 1498, + 1515, 1517, 1525, 1528, 1531, 1495, 7623, 1497, + 7623, 1499, 1508, 1500, 1501, 1502, 1503, 1504, + 1505, 1506, 1507, 7623, 1509, 1510, 1511, 1512, + 1513, 1514, 7623, 1516, 7623, 1518, 1519, 1520, + 1521, 1522, 1523, 1524, 7623, 1526, 1527, 7623, + 1529, 1530, 7623, 7623, 1533, 1536, 1541, 1551, + 1629, 1631, 2018, 2021, 2025, 1534, 1535, 7623, + 1537, 1538, 1539, 1540, 7623, 1542, 1546, 1550, + 1543, 1544, 1545, 7623, 1547, 1548, 1549, 7623, + 7623, 1552, 1598, 1624, 1553, 1554, 1555, 1556, + 1557, 1558, 1569, 1585, 1559, 1560, 1561, 1562, + 1563, 1564, 1565, 1566, 1567, 1568, 7623, 1570, + 1571, 1572, 1579, 1573, 1574, 1575, 1576, 1577, + 1578, 7623, 1580, 1581, 1582, 1583, 1584, 7623, + 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, + 1594, 1595, 1596, 1597, 7623, 1599, 1600, 1601, + 1602, 1616, 1603, 1604, 1605, 1606, 1607, 1608, + 1609, 1610, 1611, 1612, 1613, 1614, 1615, 7623, + 1617, 1618, 1619, 1620, 1621, 1622, 1623, 7623, + 1625, 1626, 1627, 1628, 7623, 1630, 7623, 1632, + 1637, 1651, 1653, 1633, 1634, 1635, 1636, 7623, + 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, + 1646, 1647, 1648, 1649, 1650, 7623, 1652, 7623, + 7623, 1654, 1668, 1685, 1706, 1753, 1770, 1823, + 1851, 1874, 1908, 1983, 2007, 1655, 1663, 1656, + 1657, 1658, 1659, 1660, 1661, 1662, 7623, 1664, + 1665, 1666, 1667, 7623, 1669, 1670, 1671, 1672, + 1673, 1674, 1675, 1676, 1677, 1678, 1679, 1680, + 1681, 1682, 1683, 1684, 7623, 1686, 1692, 1701, + 1687, 1688, 1689, 1690, 1691, 7623, 1693, 1694, + 1695, 7623, 1696, 1697, 1698, 1699, 1700, 7623, + 1702, 1703, 1704, 1705, 7623, 1707, 1708, 1709, + 1710, 1711, 1712, 7623, 1713, 1718, 1727, 1734, + 1738, 1748, 1714, 1715, 1716, 1717, 7623, 1719, + 1720, 1721, 1722, 1723, 1724, 1725, 1726, 7623, + 1728, 1729, 1730, 1731, 1732, 1733, 7623, 1735, + 1736, 1737, 7623, 1739, 1740, 1741, 1742, 1743, + 1744, 1745, 1746, 1747, 7623, 1749, 1750, 1751, + 1752, 7623, 1754, 1755, 1756, 1757, 1765, 1758, + 1759, 1760, 1761, 1762, 1763, 1764, 7623, 1766, + 1767, 1768, 1769, 7623, 1771, 1772, 1790, 1773, + 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, + 7623, 1782, 1785, 1783, 1784, 7623, 1786, 1787, + 1788, 1789, 7623, 1791, 7623, 1792, 1797, 1804, + 1808, 1818, 1793, 1794, 1795, 1796, 7623, 1798, + 1799, 1800, 1801, 1802, 1803, 7623, 1805, 1806, + 1807, 7623, 1809, 1810, 1811, 1812, 1813, 1814, + 1815, 1816, 1817, 7623, 1819, 1820, 1821, 1822, + 7623, 1824, 1825, 1826, 1827, 1828, 1829, 1843, + 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, + 1838, 1839, 1840, 1841, 1842, 7623, 1844, 1845, + 1846, 1847, 1848, 1849, 1850, 7623, 1852, 1853, + 1854, 1855, 1856, 1857, 1858, 7623, 1859, 1864, + 1860, 1861, 1862, 1863, 7623, 1865, 1866, 1867, + 1868, 1869, 1870, 1871, 1872, 1873, 7623, 1875, + 1888, 1876, 1877, 1878, 1879, 1880, 1881, 1882, + 1883, 1884, 1885, 1886, 1887, 7623, 1889, 1890, + 1891, 1892, 1893, 1894, 1895, 1896, 1897, 1898, + 1899, 7623, 1900, 1903, 1901, 1902, 7623, 1904, + 1905, 1906, 1907, 7623, 1909, 1936, 1910, 1911, + 1912, 1913, 1914, 1915, 1916, 1925, 1917, 1918, + 1919, 7623, 1920, 1921, 1922, 1923, 1924, 7623, + 1926, 1927, 1928, 1929, 1930, 7623, 1931, 1932, + 1933, 1934, 1935, 7623, 1937, 1946, 1972, 1938, + 1939, 1940, 7623, 1941, 1942, 1943, 1944, 1945, + 7623, 1947, 1948, 1949, 1950, 1951, 7623, 1952, + 1957, 1967, 1953, 1954, 1955, 1956, 7623, 1958, + 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, + 7623, 1968, 1969, 1970, 1971, 7623, 1973, 1974, + 1975, 1976, 1977, 7623, 1978, 1979, 1980, 1981, + 1982, 7623, 1984, 1985, 1986, 1987, 7623, 1988, + 1993, 2002, 1989, 1990, 1991, 1992, 7623, 1994, + 1995, 1996, 1997, 1998, 1999, 2000, 2001, 7623, + 2003, 2004, 2005, 2006, 7623, 2008, 2009, 2010, + 2011, 2012, 2013, 2014, 2015, 2016, 2017, 7623, + 2019, 2020, 7623, 2022, 2023, 2024, 7645, 7623, + 2027, 2031, 2035, 2039, 2044, 2046, 2050, 2062, + 2065, 2089, 2090, 2096, 2103, 2105, 2028, 2029, + 2030, 7623, 2032, 2033, 2034, 7646, 2036, 2038, + 2037, 7647, 7623, 2040, 2041, 2042, 2043, 7623, + 2045, 7623, 2047, 2048, 2049, 7648, 2051, 2054, + 2057, 2052, 2053, 7623, 2055, 2056, 7623, 2058, + 2059, 2060, 2061, 7623, 2063, 2064, 7623, 2066, + 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2084, + 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, + 2082, 2083, 7623, 2085, 2086, 2087, 2088, 7623, + 7623, 2091, 2093, 2092, 7623, 2094, 2095, 7649, + 2097, 2098, 2100, 2099, 7650, 2101, 2102, 7623, + 2104, 7651, 2106, 2107, 2108, 2118, 2109, 2111, + 2110, 7623, 2112, 2113, 2114, 2115, 7623, 2116, + 2117, 7623, 2119, 2120, 2121, 2122, 2123, 2124, + 2125, 2126, 2127, 2128, 7623, 2130, 2137, 2139, + 2141, 2143, 2144, 2152, 2166, 2210, 2131, 2132, + 2133, 2134, 2135, 2136, 7623, 2138, 7623, 2140, + 7623, 2142, 7623, 7623, 2145, 2146, 2147, 2148, + 2149, 2150, 2151, 7623, 2153, 2164, 2154, 2155, + 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, + 7623, 2165, 7623, 7623, 2167, 2193, 2196, 2168, + 2169, 2170, 2171, 2172, 7623, 2173, 2178, 2188, + 2174, 2175, 2176, 2177, 7623, 2179, 2180, 2181, + 2182, 2183, 2184, 2185, 2186, 2187, 7623, 2189, + 2190, 2191, 2192, 7623, 2194, 2195, 7623, 2197, + 2201, 2198, 2199, 2200, 7623, 2202, 2203, 2204, + 2205, 2206, 2207, 7623, 2208, 2209, 7623, 2211, + 2213, 2212, 7623, 7623, 2215, 2217, 2219, 2222, + 2216, 7652, 2218, 7623, 2220, 2221, 7623, 2223, + 2224, 7623, 2226, 2230, 2231, 2242, 2252, 2288, + 2290, 2292, 2442, 2455, 2465, 2469, 2227, 2228, + 2229, 7623, 7653, 2232, 2236, 2238, 2233, 2234, + 2235, 7623, 2237, 7623, 2239, 7623, 2240, 2241, + 7623, 2243, 2247, 2251, 2244, 2245, 2246, 7623, + 2248, 2249, 2250, 7623, 7623, 7623, 2253, 2254, + 2255, 2256, 2257, 2258, 2275, 2259, 2265, 2260, + 2261, 2262, 2263, 2264, 7623, 2266, 2267, 2268, + 2269, 2270, 2271, 2272, 2273, 2274, 7623, 2276, + 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, + 2285, 2286, 2287, 7623, 2289, 7623, 2291, 7623, + 2293, 2294, 2295, 2296, 2324, 2331, 2364, 2369, + 2398, 2428, 2437, 2297, 2308, 2298, 2299, 2300, + 2301, 2302, 2303, 2304, 2305, 2306, 2307, 7623, + 2309, 2310, 2311, 7623, 2312, 2315, 2313, 2314, + 7623, 2316, 2317, 2318, 2319, 2320, 2321, 2322, + 2323, 7623, 2325, 2326, 2327, 2328, 2329, 2330, + 7623, 2332, 2333, 2344, 2334, 2335, 2336, 2337, + 2338, 2339, 2340, 2341, 2342, 2343, 7623, 2345, + 2346, 2355, 2347, 2348, 2349, 2350, 2351, 2352, + 2353, 2354, 7623, 2356, 2357, 2358, 2359, 2360, + 7623, 2361, 2362, 2363, 7623, 2365, 2366, 2367, + 2368, 7623, 2370, 2383, 2371, 7623, 2372, 2377, + 2373, 2374, 2375, 2376, 7623, 2378, 2379, 2380, + 2381, 2382, 7623, 2384, 2385, 2386, 2387, 2388, + 2389, 7623, 2390, 2393, 2391, 2392, 7623, 2394, + 2395, 2396, 2397, 7623, 2399, 2400, 2410, 2419, + 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, + 2409, 7623, 2411, 2412, 2413, 2414, 2415, 2416, + 2417, 2418, 7623, 2420, 2421, 2422, 2423, 2424, + 7623, 2425, 2426, 2427, 7623, 2429, 2430, 2431, + 2432, 2433, 7623, 2434, 2435, 2436, 7623, 2438, + 2439, 2440, 2441, 7623, 2443, 2445, 2444, 7623, + 2446, 2447, 2448, 2449, 2450, 2451, 2452, 2453, + 2454, 7623, 2456, 2457, 2458, 2459, 2460, 2461, + 2462, 2463, 2464, 7623, 2466, 2468, 2467, 7623, + 7623, 2470, 2471, 2472, 2473, 2474, 2475, 2476, + 2477, 2478, 7623, 2480, 2487, 2492, 2497, 2510, + 2512, 2551, 2555, 2565, 2568, 2614, 2617, 2620, + 2481, 2485, 2482, 2483, 2484, 7623, 2486, 7623, + 2488, 2489, 2490, 2491, 7623, 2493, 2494, 2495, + 2496, 7623, 7623, 2498, 2502, 2506, 2509, 2499, + 2500, 2501, 7623, 2503, 2504, 2505, 7623, 2507, + 2508, 7623, 7623, 2511, 7623, 2513, 2514, 2515, + 2516, 2525, 2534, 2544, 2517, 2518, 2519, 2520, + 2521, 2522, 2523, 2524, 7623, 2526, 2527, 2528, + 2529, 2530, 2531, 2532, 2533, 7623, 2535, 2536, + 2537, 2538, 2539, 2540, 2541, 2542, 2543, 7623, + 2545, 2546, 2547, 2548, 2549, 2550, 7623, 2552, + 2553, 2554, 7623, 2556, 2557, 2558, 2559, 2560, + 2561, 2562, 2563, 2564, 7623, 2566, 2567, 7623, + 2569, 2571, 2570, 7623, 2572, 2573, 2574, 7623, + 2575, 2587, 2609, 2576, 2577, 2578, 2579, 2580, + 2581, 2582, 2583, 2584, 2585, 2586, 7623, 2588, + 2589, 2598, 2590, 2591, 2592, 7623, 2593, 2594, + 2595, 2596, 2597, 7623, 2599, 2600, 2601, 2602, + 2603, 7623, 2604, 2605, 2606, 2607, 2608, 7623, + 2610, 2611, 2612, 2613, 7623, 2615, 2616, 7623, + 2618, 2619, 7623, 2621, 2630, 2661, 2662, 7623, + 2622, 2623, 2624, 7623, 2625, 2626, 2627, 2628, + 2629, 7623, 2631, 2656, 2632, 2633, 2634, 2635, + 7623, 2636, 2641, 2651, 2637, 2638, 2639, 2640, + 7623, 2642, 2643, 2644, 2645, 2646, 2647, 2648, + 2649, 2650, 7623, 2652, 2653, 2654, 2655, 7623, + 2657, 2658, 2659, 2660, 7623, 7623, 7623, 2663, + 2673, 2664, 2665, 2666, 2667, 7623, 2668, 2669, + 2670, 2671, 2672, 7623, 2674, 2675, 7623, 2677, + 2680, 2684, 2690, 2693, 2703, 2705, 2729, 2752, + 2755, 2763, 2678, 2679, 7654, 2681, 2682, 2683, + 7623, 2685, 2688, 2686, 2687, 7623, 2689, 7623, + 2691, 2692, 7623, 7623, 2694, 2698, 2702, 2695, + 2696, 2697, 7623, 2699, 2700, 2701, 7623, 7623, + 2704, 7623, 2706, 2715, 2707, 2713, 2708, 2709, + 2710, 2711, 2712, 7623, 2714, 7623, 2716, 2723, + 2717, 2718, 2719, 2720, 2721, 2722, 7623, 2724, + 2725, 2726, 2727, 2728, 7623, 2730, 2731, 2732, + 7623, 2733, 2738, 2747, 2734, 2735, 2736, 2737, + 7623, 2739, 2740, 2741, 2742, 2743, 2744, 2745, + 2746, 7623, 2748, 2749, 2750, 2751, 7623, 2753, + 2754, 7623, 2756, 2757, 2758, 2759, 2760, 2761, + 2762, 7623, 2764, 2766, 2765, 7623, 2767, 2768, + 2769, 7623, 2771, 2781, 2788, 2792, 2797, 2799, + 2803, 2807, 2839, 2845, 2932, 2936, 2939, 2944, + 2772, 2775, 2773, 2774, 7655, 2776, 7623, 2777, + 2778, 2779, 2780, 7623, 2782, 2783, 2785, 2784, + 7623, 2786, 2787, 7623, 2789, 2791, 2790, 7656, + 7623, 2793, 2794, 2795, 2796, 7623, 2798, 7623, + 2800, 2801, 2802, 7657, 2804, 2805, 2806, 7623, + 2808, 2832, 2809, 2810, 2811, 2821, 2812, 2814, + 2813, 7623, 2815, 2816, 2817, 2818, 7623, 2819, + 2820, 7623, 2822, 2823, 2824, 2825, 2826, 2827, + 2828, 2829, 2830, 2831, 7623, 2833, 2834, 7623, + 2835, 2836, 2837, 2838, 7623, 2840, 2843, 2841, + 2842, 7623, 2844, 7623, 2846, 2863, 2872, 2883, + 2891, 2896, 2905, 2927, 2847, 2848, 2849, 2850, + 7623, 2851, 2854, 2852, 2853, 7623, 2855, 2856, + 2857, 2858, 2859, 2860, 2861, 2862, 7623, 2864, + 2865, 2866, 2867, 2868, 2869, 2870, 2871, 7623, + 2873, 2874, 2875, 2876, 2877, 2878, 2879, 2880, + 2881, 2882, 7623, 2884, 2885, 7623, 2886, 2887, + 2888, 2889, 2890, 7623, 2892, 2893, 2894, 2895, + 7623, 2897, 2898, 2899, 2900, 2901, 2902, 2903, + 2904, 7623, 2906, 2907, 2908, 2917, 2909, 2910, + 2911, 2912, 2913, 2914, 2915, 2916, 7623, 2918, + 2919, 2920, 2921, 2922, 2923, 2924, 2925, 2926, + 7623, 2928, 7623, 2929, 2930, 2931, 7623, 2933, + 2934, 2935, 7623, 2937, 2938, 7623, 2940, 2941, + 2942, 2943, 7623, 2945, 7658, 2947, 2951, 2954, + 2956, 2961, 3003, 3005, 3008, 3011, 2948, 2949, + 2950, 7623, 2952, 2953, 7623, 2955, 7623, 2957, + 2958, 2959, 7623, 2960, 7623, 2962, 2963, 7623, + 2964, 2967, 2993, 2965, 2966, 7623, 7623, 2968, + 2969, 2970, 2971, 2972, 2975, 2979, 2988, 2973, + 2974, 7623, 2976, 2977, 2978, 7623, 2980, 2981, + 2982, 2983, 2984, 2985, 2986, 2987, 7623, 2989, + 2990, 2991, 2992, 7623, 2994, 2995, 2996, 2997, + 2998, 2999, 3000, 3001, 3002, 7623, 3004, 7623, + 3006, 3007, 7623, 3009, 3010, 7623, 3012, 3013, + 3014, 3015, 7623, 3017, 3021, 3025, 3027, 3030, + 3018, 3019, 3020, 7623, 3022, 3023, 3024, 7623, + 3026, 7623, 3028, 3029, 7623, 3031, 3032, 7623, + 3034, 3036, 3037, 3040, 3035, 7623, 7623, 3038, + 3039, 7623, 3041, 3042, 7623, 3044, 3047, 3050, + 3053, 3057, 3062, 3064, 3067, 3070, 3045, 3046, + 7623, 3048, 3049, 7623, 3051, 3052, 7623, 3054, + 3055, 3056, 7659, 3058, 3061, 3059, 3060, 7623, + 7623, 3063, 7623, 3065, 3066, 7623, 3068, 3069, + 7623, 3071, 3072, 7623, 3074, 3077, 3082, 3088, + 3091, 3106, 3108, 3111, 3075, 3076, 7623, 3078, + 3079, 3080, 3081, 7623, 3083, 3087, 3084, 3085, + 3086, 7623, 7623, 3089, 3090, 7623, 3092, 3104, + 3093, 3094, 3095, 3096, 3097, 3098, 3099, 3100, + 3101, 3102, 3103, 7623, 3105, 7623, 3107, 7623, + 3109, 3110, 7623, 3112, 3113, 7623, 3115, 3119, + 3124, 3132, 3135, 3137, 3141, 3152, 3158, 3199, + 3205, 3222, 3225, 3234, 3238, 3240, 3116, 3117, + 3118, 7660, 3120, 3121, 3122, 3123, 7623, 7623, + 3125, 3126, 3127, 3129, 3131, 7623, 7623, 3128, + 7661, 3130, 7662, 7623, 3133, 3134, 7663, 7623, + 3136, 7623, 3138, 3139, 3140, 7664, 3142, 3149, + 3143, 3147, 3144, 3145, 3146, 7623, 3148, 7623, + 3150, 3151, 7623, 3153, 7665, 3154, 3156, 3155, + 7623, 3157, 7623, 3159, 3170, 7623, 3160, 3163, + 3164, 3169, 3161, 3162, 7623, 7623, 3165, 3166, + 3167, 3168, 7623, 7623, 7623, 3171, 3172, 3174, + 3186, 3191, 3195, 7623, 3173, 7623, 3175, 3176, + 7623, 3177, 3178, 3179, 3180, 3181, 3182, 3183, + 3184, 3185, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 3187, 7623, 3188, 3189, 7623, 3190, + 7623, 3192, 3194, 3193, 7623, 7623, 3196, 3197, + 3198, 7623, 3200, 3203, 3201, 3202, 7623, 3204, + 7623, 7623, 3206, 3207, 3211, 3212, 3214, 3216, + 7623, 3208, 3209, 3210, 7623, 7623, 3213, 7623, + 3215, 7623, 3217, 3218, 3219, 7623, 3220, 3221, + 7623, 3223, 3224, 7666, 3226, 3228, 3229, 3227, + 7623, 7623, 3230, 3231, 7623, 3232, 3233, 7623, + 3235, 3236, 3237, 7667, 3239, 7668, 3241, 3247, + 3242, 3243, 3244, 3245, 3246, 7623, 3248, 3249, + 7623, 3251, 3254, 3287, 3294, 3299, 3303, 3328, + 3330, 3388, 3393, 3442, 3450, 3523, 3528, 3535, + 3551, 3252, 3253, 7623, 3255, 3278, 3256, 3257, + 3261, 3268, 3273, 3258, 3259, 3260, 7623, 3262, + 3263, 3264, 3265, 3266, 3267, 7623, 3269, 3270, + 3271, 3272, 7623, 3274, 3275, 7623, 3276, 3277, + 7623, 3279, 3282, 3280, 3281, 7623, 3283, 3284, + 7623, 3285, 3286, 7623, 3288, 3289, 7623, 3290, + 3291, 3292, 3293, 7623, 3295, 3298, 3296, 3297, + 7623, 7623, 3300, 3301, 3302, 7623, 3304, 3309, + 3314, 3317, 3321, 3305, 3306, 3307, 7623, 3308, + 7623, 3310, 3311, 3312, 3313, 7623, 3315, 3316, + 7623, 3318, 3319, 3320, 7623, 3322, 3323, 3324, + 7623, 7623, 3325, 3326, 3327, 7623, 3329, 7623, + 3331, 3332, 3340, 3353, 3361, 3375, 3380, 3383, + 3333, 3335, 3338, 3334, 7623, 3336, 3337, 7623, + 3339, 7623, 3341, 3344, 3348, 3342, 3343, 7623, + 3345, 3346, 3347, 7623, 3349, 3350, 3351, 3352, + 7623, 3354, 3358, 3355, 3356, 3357, 7623, 3359, + 3360, 7623, 3362, 3363, 3364, 3365, 3366, 3367, + 3368, 3369, 3373, 3370, 3371, 3372, 7623, 3374, + 7623, 3376, 3377, 3378, 3379, 7623, 3381, 3382, + 7623, 3384, 3385, 3386, 3387, 7623, 3389, 3390, + 3391, 3392, 7623, 3394, 3433, 3439, 3395, 3431, + 3396, 3397, 3404, 3410, 3398, 3399, 3400, 3401, + 3402, 3403, 7623, 3405, 3406, 3407, 3408, 3409, + 7623, 3411, 3412, 3413, 3414, 3415, 3416, 3417, + 7623, 3418, 3422, 3426, 3419, 3420, 3421, 7623, + 3423, 3424, 3425, 7623, 3427, 3428, 3429, 3430, + 7623, 3432, 7623, 3434, 3437, 3435, 3436, 7623, + 7623, 3438, 7623, 3440, 3441, 7623, 3443, 3448, + 7623, 3444, 3445, 3446, 3447, 7623, 3449, 7623, + 3451, 3453, 3457, 3461, 3452, 7623, 7623, 3454, + 3455, 3456, 7623, 3458, 3459, 3460, 7623, 3462, + 3467, 3472, 3477, 3484, 3487, 3492, 3497, 3502, + 3506, 3511, 3516, 3463, 3464, 3465, 3466, 7623, + 7623, 7623, 7623, 7623, 3468, 3469, 3470, 3471, + 7623, 7623, 7623, 7623, 3473, 3474, 3475, 3476, + 7623, 7623, 7623, 7623, 7623, 3478, 3479, 3480, + 3481, 3482, 3483, 7623, 7623, 7623, 7623, 7623, + 7623, 3485, 3486, 7623, 3488, 3489, 3490, 3491, + 7623, 7623, 7623, 7623, 7623, 3493, 3494, 3495, + 3496, 7623, 7623, 7623, 7623, 3498, 3499, 3500, + 3501, 7623, 3503, 3504, 3505, 7623, 3507, 3508, + 3509, 3510, 7623, 3512, 3513, 3514, 3515, 7623, + 7623, 7623, 7623, 7623, 3517, 3518, 3519, 3520, + 3521, 3522, 7623, 7623, 7623, 7623, 7623, 7623, + 3524, 3525, 3526, 3527, 7623, 3529, 3532, 3530, + 3531, 7623, 3533, 3534, 7669, 3536, 3538, 3541, + 3544, 3537, 7623, 3539, 3540, 7623, 3542, 7623, + 3543, 7623, 3545, 7623, 3546, 3547, 7623, 3548, + 3549, 3550, 7623, 3552, 3556, 3553, 7623, 3554, + 3555, 7623, 3557, 7623, 3558, 3559, 7623, 7623, + 3560, 7623, 3562, 3590, 3608, 3611, 3625, 3627, + 3638, 3687, 3694, 3731, 3738, 3746, 3750, 3833, + 3843, 3563, 3567, 3585, 3564, 3565, 3566, 7623, + 7623, 3568, 3571, 3576, 3581, 3584, 3569, 3570, + 7623, 3572, 3573, 3574, 3575, 7623, 3577, 3579, + 3578, 7623, 3580, 7623, 3582, 3583, 7623, 7623, + 3586, 3588, 3587, 7623, 3589, 7623, 3591, 3597, + 3600, 3603, 3592, 3594, 3593, 7623, 3595, 3596, + 7623, 3598, 3599, 7670, 3601, 3602, 7623, 3604, + 3605, 7623, 3606, 3607, 7623, 3609, 3610, 7623, + 3612, 3614, 3619, 3613, 7671, 3615, 3616, 3617, + 3618, 7623, 7672, 7623, 3621, 3622, 3623, 3624, + 7623, 3626, 7623, 3628, 3630, 3637, 3629, 7623, + 3631, 3632, 7623, 3633, 3634, 3635, 3636, 7623, + 7623, 3639, 7623, 3640, 3641, 3674, 3675, 3680, + 3683, 7623, 7623, 3642, 3644, 3643, 7623, 3645, + 3646, 3660, 3647, 3648, 3649, 3650, 3651, 3655, + 3652, 3653, 3654, 7623, 3656, 3657, 3658, 3659, + 7623, 3661, 3662, 3663, 3666, 3670, 7623, 7623, + 3664, 3665, 7623, 3667, 3668, 3669, 7623, 3671, + 3672, 3673, 7623, 7623, 3676, 3677, 3678, 3679, + 7623, 3681, 3682, 7623, 3684, 3685, 3686, 7623, + 3688, 3689, 3690, 7623, 3691, 3692, 3693, 7623, + 3695, 3700, 3716, 3724, 3696, 3697, 7623, 3698, + 7623, 3699, 7623, 3701, 3704, 3702, 7623, 3703, + 7623, 7623, 3705, 3707, 3706, 7623, 3708, 3709, + 3713, 3710, 3711, 3712, 7623, 3714, 3715, 7623, + 3717, 3721, 7623, 3718, 3719, 3720, 7623, 3722, + 3723, 7623, 3725, 3726, 7673, 7623, 3727, 3728, + 7623, 7623, 3730, 7623, 3732, 3735, 3733, 3734, + 7623, 3736, 3737, 7623, 3739, 3741, 3740, 7623, + 3742, 3744, 7623, 3743, 7623, 7623, 3745, 7623, + 3747, 3748, 3749, 7623, 3751, 3757, 3762, 3767, + 3784, 3827, 3830, 3752, 3753, 3754, 3755, 3756, + 7623, 7623, 3758, 3760, 3759, 7623, 3761, 7623, + 3763, 3764, 3765, 7623, 3766, 7623, 7623, 3768, + 3773, 3778, 3781, 3783, 3769, 3770, 3771, 3772, + 7623, 3774, 3776, 3775, 7623, 3777, 7623, 3779, + 3780, 7623, 3782, 7623, 7623, 3785, 3789, 3809, + 3811, 3786, 3787, 7623, 3788, 7623, 3790, 3791, + 3801, 3804, 3792, 3793, 3797, 3794, 3795, 3796, + 7623, 3798, 3799, 3800, 7623, 3802, 3803, 7623, + 3805, 3806, 3807, 3808, 7623, 3810, 7674, 3812, + 3813, 3814, 3815, 3816, 3817, 3818, 3822, 3819, + 3820, 3821, 7623, 3823, 3824, 3825, 3826, 7623, + 3828, 3829, 7623, 3831, 3832, 7623, 3834, 3840, + 3835, 3836, 3837, 3838, 3839, 7623, 3841, 3842, + 7623, 3844, 3845, 3846, 3847, 7623, 3849, 3852, + 3855, 3869, 3878, 3884, 3897, 3906, 3912, 3917, + 3950, 3953, 3961, 4034, 4048, 4058, 4065, 4072, + 4078, 3850, 3851, 7623, 3853, 3854, 7623, 3856, + 3860, 3864, 3866, 3857, 3858, 3859, 7623, 3861, + 3862, 3863, 7623, 3865, 7623, 3867, 7623, 3868, + 7623, 3870, 3875, 3871, 3872, 3873, 3874, 7623, + 3876, 3877, 7623, 3879, 3883, 3880, 3881, 3882, + 7623, 7623, 7623, 3885, 3892, 3886, 3890, 3887, + 3888, 3889, 7623, 3891, 7623, 3893, 3894, 3895, + 3896, 7623, 7675, 3898, 3901, 3899, 3900, 7623, + 3902, 3903, 3904, 3905, 7623, 3907, 3911, 3908, + 3909, 3910, 7623, 7623, 3913, 3914, 3915, 3916, + 7623, 7623, 3918, 3928, 3929, 3934, 3937, 3919, + 7623, 3920, 3927, 3921, 3922, 7623, 3923, 3924, + 3925, 3926, 7623, 7623, 7623, 3930, 3931, 3932, + 3933, 7623, 3935, 3936, 7623, 7623, 3938, 3947, + 3939, 7676, 7623, 3941, 3942, 3943, 3944, 3945, + 3946, 7623, 3948, 3949, 7623, 3951, 3952, 7623, + 3954, 3955, 3958, 3956, 3957, 7623, 3959, 3960, + 7623, 3962, 3966, 3968, 3989, 4001, 3963, 3964, + 3965, 7623, 3967, 7623, 7623, 3969, 3974, 3979, + 3983, 3970, 7623, 3971, 3972, 3973, 7623, 3975, + 3976, 3977, 3978, 7623, 3980, 3981, 3982, 7623, + 3984, 3985, 3986, 3987, 3988, 7623, 3990, 3991, + 3992, 3993, 3994, 3995, 3996, 3997, 3998, 3999, + 4000, 7623, 4002, 4003, 4008, 4018, 4004, 4005, + 4006, 4007, 7623, 4009, 4010, 4011, 4012, 4013, + 4014, 4015, 4016, 4017, 7623, 4019, 4020, 4021, + 4022, 4023, 4024, 4025, 4029, 4026, 4027, 4028, + 7623, 4030, 4031, 4032, 4033, 7623, 4035, 4041, + 4036, 4037, 4038, 4039, 4040, 7623, 4042, 4045, + 4043, 4044, 7623, 4046, 4047, 7623, 4049, 4052, + 4054, 4050, 4051, 7623, 7623, 4053, 7623, 4055, + 4056, 4057, 7623, 4059, 4062, 4060, 4061, 7623, + 4063, 7623, 4064, 7623, 4066, 4069, 4067, 4068, + 7623, 4070, 4071, 7623, 4073, 4074, 4075, 4076, + 4077, 7623, 4079, 4081, 4080, 7623, 4082, 4083, + 4084, 4085, 4086, 7623, 4088, 4094, 4102, 4114, + 4117, 4118, 4123, 4131, 4143, 4159, 4163, 4169, + 4183, 4223, 4230, 4238, 4240, 4244, 4089, 4092, + 4090, 4091, 7623, 4093, 7623, 4095, 4098, 4096, + 4097, 7677, 4099, 4100, 4101, 7623, 4103, 4107, + 4109, 4113, 4104, 4105, 4106, 7623, 4108, 7623, + 7678, 4110, 4111, 4112, 7623, 7623, 4115, 4116, + 7623, 7623, 4119, 4122, 4120, 4121, 7623, 7623, + 7623, 4124, 4127, 4125, 4126, 7679, 7623, 4128, + 4129, 4130, 7623, 7623, 4132, 4138, 4139, 4133, + 4134, 4135, 4136, 4137, 7623, 7623, 7623, 4140, + 4141, 4142, 7623, 4144, 4147, 4154, 4145, 4146, + 7623, 4148, 4149, 7623, 4150, 4153, 4151, 4152, + 7623, 7623, 4155, 4156, 7623, 4157, 4158, 7623, + 7623, 4160, 4161, 7623, 4162, 7623, 4164, 4167, + 4165, 4166, 7623, 4168, 7623, 4170, 4174, 4177, + 4171, 7623, 4172, 4173, 7623, 4175, 4176, 7623, + 4178, 7623, 4179, 4182, 4180, 4181, 7623, 7623, + 4184, 4192, 4206, 4217, 4185, 4188, 4186, 4187, + 7623, 4189, 4190, 4191, 7623, 4193, 4195, 4194, + 7623, 4196, 4197, 4198, 4199, 4202, 4200, 4201, + 7623, 4203, 4204, 4205, 7623, 4207, 4210, 4213, + 4208, 4209, 7623, 4211, 4212, 7623, 4214, 7623, + 4215, 4216, 7623, 4218, 4219, 4220, 4221, 4222, + 7623, 4224, 4227, 4225, 4226, 7623, 4228, 4229, + 7623, 4231, 4233, 4236, 4232, 7623, 4234, 4235, + 7623, 4237, 7623, 4239, 7680, 7623, 4241, 4242, + 7681, 4243, 7623, 4245, 4247, 4250, 4246, 7623, + 4248, 4249, 7623, 4251, 4259, 4252, 4253, 4254, + 4255, 4256, 4257, 4258, 7623, 4260, 4261, 4262, + 4263, 4264, 4265, 4266, 4267, 7623, 4269, 4281, + 4283, 4288, 4300, 4304, 4308, 4317, 4320, 4329, + 4336, 4362, 4270, 4271, 4272, 4273, 4274, 4275, + 4276, 4277, 4278, 4279, 4280, 7623, 4282, 7623, + 4284, 4285, 4286, 4287, 7623, 4289, 4293, 4299, + 4290, 4291, 4292, 7623, 4294, 4296, 4295, 7623, + 4297, 4298, 7623, 7623, 4301, 4302, 4303, 7623, + 4305, 4306, 4307, 7623, 4309, 4311, 4314, 4310, + 7623, 4312, 4313, 7623, 4315, 4316, 7623, 4318, + 4319, 7623, 4321, 4323, 4322, 7623, 4324, 4327, + 4325, 4326, 7623, 7623, 4328, 7623, 4330, 4331, + 4332, 4333, 4334, 4335, 7623, 4337, 4359, 4338, + 4357, 4339, 4344, 4347, 4350, 4352, 4355, 7682, + 4340, 7683, 4341, 4342, 4343, 7623, 7623, 7623, + 7623, 4345, 4346, 7623, 7623, 7684, 4348, 4349, + 7623, 7623, 4351, 7623, 4353, 4354, 7623, 7623, + 4356, 7623, 4358, 7623, 4360, 4361, 7623, 4363, + 4364, 7623, 4366, 4368, 4378, 4383, 4388, 4391, + 4411, 4413, 4415, 4419, 4422, 4426, 4440, 4443, + 4447, 7685, 4500, 7623, 4367, 7623, 4369, 4373, + 4377, 4370, 4371, 4372, 7623, 4374, 4375, 7623, + 4376, 7623, 7623, 4379, 4380, 4381, 4382, 7623, + 4384, 4387, 4385, 4386, 7623, 7623, 4389, 4390, + 7623, 7623, 4392, 4393, 4400, 7623, 7623, 4394, + 4395, 7623, 4396, 4397, 4398, 4399, 7623, 7623, + 4401, 4403, 4408, 4402, 7623, 4404, 4405, 7623, + 4406, 7623, 4407, 7623, 7623, 4409, 4410, 7623, + 4412, 7623, 7623, 4414, 7623, 4416, 4417, 4418, + 7623, 4420, 4421, 7623, 7623, 4423, 4424, 4425, + 7623, 7623, 7623, 4427, 4428, 4434, 4437, 7623, + 4429, 7623, 4430, 4431, 4432, 4433, 7623, 7623, + 4435, 7623, 4436, 7623, 4438, 4439, 7623, 4441, + 4442, 7623, 4444, 4445, 4446, 7623, 4448, 4450, + 4449, 7623, 4451, 7623, 4452, 4453, 7623, 7623, + 7623, 4455, 4456, 7623, 4457, 7623, 4459, 4460, + 7623, 4462, 4463, 4464, 7623, 4466, 4467, 4468, + 4469, 7623, 4471, 4479, 4482, 4493, 4497, 4472, + 4477, 4473, 4474, 4475, 4476, 7623, 4478, 7623, + 4480, 4481, 7623, 4483, 4484, 4488, 4485, 4486, + 4487, 7623, 4489, 4490, 4491, 4492, 7623, 4494, + 4495, 4496, 7623, 4498, 4499, 7623, 4501, 4508, + 4502, 4503, 4504, 4505, 4506, 4507, 7623, 4509, + 7623, 4511, 4514, 4534, 4537, 4541, 4557, 4559, + 4571, 4606, 4617, 4512, 4513, 7623, 4515, 4519, + 4521, 4525, 4516, 4517, 4518, 7623, 4520, 7623, + 4522, 4523, 4524, 7623, 4526, 4529, 4527, 4528, + 7623, 7623, 4530, 4533, 4531, 4532, 7623, 7623, + 4535, 4536, 7623, 4538, 4539, 4540, 7623, 4542, + 4549, 4553, 4543, 4544, 4545, 7623, 4546, 4547, + 4548, 7623, 4550, 4551, 4552, 7623, 4554, 4555, + 4556, 7623, 4558, 7623, 4560, 4561, 4566, 4562, + 4563, 4564, 4565, 7623, 4567, 4568, 4569, 4570, + 7623, 4572, 4575, 4579, 4600, 4602, 4573, 4574, + 7623, 4576, 4577, 4578, 7623, 4580, 4581, 4590, + 4582, 4583, 4584, 4585, 4586, 4587, 4588, 4589, + 7623, 4591, 4592, 4593, 4594, 4595, 4596, 4597, + 4598, 4599, 7623, 4601, 7623, 4603, 4604, 4605, + 7623, 4607, 4609, 4613, 4608, 7623, 4610, 4611, + 4612, 7623, 4614, 4615, 4616, 7623, 4618, 4622, + 4619, 4620, 4621, 7623, 4623, 4624, 4625, 7623, + 4627, 4631, 4635, 4640, 4643, 4647, 4661, 4665, + 4686, 4723, 4733, 4737, 4741, 4753, 4758, 4628, + 4629, 4630, 7686, 7623, 4632, 4634, 4633, 7687, + 7623, 4636, 4638, 4637, 7623, 4639, 7688, 4641, + 4642, 7623, 7623, 4644, 4645, 4646, 7689, 7623, + 4648, 4654, 4658, 4649, 4652, 4650, 4651, 7623, + 4653, 7623, 4655, 4656, 4657, 7623, 4659, 4660, + 7623, 4662, 4663, 4664, 7623, 4666, 4681, 4683, + 4667, 4669, 4679, 4668, 7623, 4670, 4671, 4675, + 7623, 4672, 4673, 4674, 7623, 4676, 4677, 4678, + 7623, 4680, 7623, 4682, 7623, 4684, 4685, 7623, + 7623, 4687, 4691, 4697, 4701, 4688, 4689, 4690, + 7623, 4692, 4693, 7623, 4694, 4695, 4696, 7623, + 4698, 4699, 4700, 7623, 7623, 4702, 4705, 4714, + 4719, 4703, 4704, 7623, 4706, 4710, 4707, 4708, + 4709, 7623, 4711, 4712, 4713, 7623, 4715, 4716, + 4717, 4718, 7623, 4720, 4721, 4722, 7623, 4724, + 4726, 4729, 4731, 4725, 7623, 4727, 4728, 7623, + 4730, 7623, 4732, 7623, 4734, 4735, 4736, 7623, + 4738, 4739, 4740, 7690, 4742, 4744, 4743, 7623, + 4745, 7623, 4746, 4747, 4750, 4752, 7623, 4748, + 4749, 7623, 7623, 4751, 7623, 7623, 7623, 4754, + 4755, 4756, 4757, 7623, 4759, 4762, 4760, 4761, + 7623, 7691, 4764, 4769, 4771, 4775, 4778, 4785, + 4765, 4768, 4766, 4767, 7623, 7623, 4770, 7623, + 4772, 4773, 4774, 7623, 4776, 4777, 7623, 4779, + 4781, 4780, 7623, 4782, 4783, 4784, 7623, 4786, + 4787, 4788, 7623, 4790, 4795, 4801, 4803, 4808, + 4811, 4814, 4817, 4791, 4792, 4793, 7623, 4794, + 7623, 4796, 4800, 4797, 4798, 4799, 7623, 7623, + 4802, 7623, 4804, 4805, 4806, 4807, 7623, 4809, + 4810, 7623, 4812, 4813, 7623, 4815, 4816, 7623, + 4818, 4819, 7623, 4821, 4831, 4835, 4837, 4840, + 4891, 4909, 4923, 4942, 5060, 5070, 5072, 5081, + 5084, 5101, 5114, 5128, 5215, 5220, 5238, 7693, + 5290, 5301, 4822, 4825, 4827, 4823, 4824, 7623, + 4826, 7623, 4828, 4829, 4830, 7623, 4832, 4833, + 4834, 7623, 7623, 4836, 7623, 4838, 4839, 7623, + 4841, 4845, 4851, 4855, 4859, 4864, 4865, 4867, + 4885, 4842, 4843, 4844, 7623, 4846, 4847, 4848, + 4849, 4850, 7623, 4852, 4853, 4854, 7623, 4856, + 4857, 4858, 7623, 4860, 7623, 4861, 4862, 7623, + 4863, 7623, 7623, 4866, 7692, 4868, 7623, 4869, + 4872, 4874, 4876, 4878, 4880, 4883, 7623, 4870, + 4871, 7623, 4873, 7623, 4875, 7623, 4877, 7623, + 4879, 7623, 4881, 4882, 7623, 4884, 7623, 7623, + 4886, 4889, 4887, 4888, 7623, 7623, 4890, 7623, + 4892, 4895, 4898, 4893, 4894, 7623, 4896, 4897, + 7623, 4899, 4903, 4900, 4901, 4902, 7623, 7623, + 4904, 4905, 7623, 4906, 4907, 4908, 7623, 7623, + 4910, 4914, 4920, 4922, 4911, 4912, 4913, 7623, + 4915, 4918, 4916, 4917, 7623, 4919, 7623, 4921, + 7623, 7623, 4924, 4926, 4930, 4940, 4925, 7623, + 4927, 4928, 7623, 4929, 7623, 4931, 4935, 4932, + 4933, 4934, 7623, 4936, 4937, 4938, 4939, 7623, + 4941, 7623, 7623, 4943, 5016, 5017, 5024, 4944, + 4945, 4954, 4967, 4977, 5006, 4946, 4947, 4948, + 4949, 7623, 4950, 4951, 4952, 4953, 7623, 4955, + 4956, 4957, 4958, 4959, 4960, 4961, 4965, 4962, + 4963, 4964, 7623, 4966, 7623, 4968, 4969, 4970, + 4971, 4972, 4973, 4974, 4975, 4976, 7623, 4978, + 4979, 4980, 4981, 4982, 4988, 4996, 4983, 4984, + 4985, 4986, 7623, 4987, 7623, 4989, 4990, 4991, + 4992, 4993, 4994, 4995, 7623, 4997, 4998, 4999, + 5000, 5001, 5002, 5003, 5004, 5005, 7623, 5007, + 5008, 5009, 5010, 5011, 5012, 5013, 5014, 5015, + 7623, 7623, 7623, 5018, 5019, 7623, 5020, 5021, + 5022, 5023, 7623, 7623, 5025, 5027, 5032, 5035, + 5026, 7623, 5028, 5029, 7623, 5030, 7623, 5031, + 7623, 7623, 5033, 5034, 7623, 5036, 5042, 5045, + 5054, 5057, 5037, 5038, 5039, 5040, 5041, 7623, + 5043, 5044, 7623, 5046, 5047, 5050, 5048, 5049, + 7623, 5051, 5052, 5053, 7623, 5055, 5056, 7623, + 5058, 5059, 7623, 5061, 5065, 5069, 5062, 5063, + 5064, 7623, 5066, 5067, 5068, 7623, 7623, 7623, + 5071, 7623, 5073, 5078, 5074, 5075, 5076, 7623, + 7623, 5077, 7623, 5079, 5080, 7623, 5082, 5083, + 7623, 7623, 5085, 5088, 5094, 5098, 5086, 5087, + 7623, 5089, 5090, 5091, 5092, 5093, 7623, 5095, + 5096, 5097, 7623, 5099, 5100, 7623, 5102, 5106, + 5103, 5104, 5105, 7623, 5107, 5108, 5109, 7623, + 5110, 5111, 5112, 5113, 7623, 5115, 5116, 5122, + 5125, 7623, 5117, 7623, 5118, 5119, 5120, 5121, + 7623, 7623, 5123, 7623, 5124, 7623, 5126, 5127, + 7623, 5129, 5134, 5137, 5174, 5190, 5197, 5202, + 5209, 5130, 5132, 5131, 7623, 5133, 7623, 5135, + 5136, 7623, 5138, 5139, 5158, 5164, 5140, 5141, + 5142, 5143, 5148, 5144, 5145, 5146, 5147, 7623, + 5149, 5150, 5151, 5152, 5153, 5154, 5155, 5156, + 5157, 7623, 5159, 5160, 5161, 5162, 5163, 7623, + 5165, 5166, 5167, 5168, 5169, 5170, 5171, 5172, + 5173, 7623, 5175, 5176, 5177, 5178, 5179, 5180, + 5181, 5185, 5182, 5183, 5184, 7623, 5186, 5187, + 5188, 5189, 7623, 5191, 5193, 5194, 5192, 7623, + 7623, 5195, 5196, 7623, 5198, 5199, 5200, 5201, + 7623, 5203, 5206, 5204, 5205, 7623, 5207, 5208, + 7623, 7623, 5210, 5214, 5211, 5212, 5213, 7623, + 7623, 5216, 5217, 7623, 5218, 5219, 7623, 5221, + 5224, 5230, 5234, 5235, 5222, 5223, 7623, 5225, + 5226, 5227, 5228, 5229, 7623, 5231, 5232, 7623, + 5233, 7623, 7623, 5236, 5237, 7623, 5239, 5243, + 5245, 5246, 5250, 5255, 5240, 5241, 5242, 7623, + 5244, 7623, 7623, 5247, 7623, 5248, 5249, 7623, + 7623, 5251, 5252, 7623, 5253, 7623, 5254, 7623, + 5256, 5257, 5258, 7623, 7623, 5260, 5261, 7623, + 5262, 7623, 5264, 5265, 7623, 5267, 5268, 5269, + 7623, 5271, 5272, 5273, 7623, 5275, 5276, 5277, + 7623, 5279, 5280, 5281, 5282, 7623, 5284, 5287, + 5285, 5286, 7623, 7623, 5288, 5289, 7623, 7623, + 5291, 5292, 5297, 5293, 5294, 5295, 5296, 7623, + 5298, 5299, 5300, 7623, 5302, 5309, 5303, 5304, + 5305, 5306, 5307, 5308, 7623, 5310, 7623, 5312, + 5316, 5342, 5348, 5352, 5364, 5366, 5368, 5386, + 5391, 5396, 5403, 5404, 5411, 5313, 5314, 5315, + 7623, 5317, 5318, 5324, 5338, 7694, 5319, 5320, + 7623, 7623, 5321, 5322, 5323, 7623, 7623, 5325, + 5326, 5327, 7623, 5328, 5332, 5336, 5329, 5330, + 5331, 7623, 5333, 5334, 5335, 7623, 5337, 7623, + 5339, 5340, 5341, 7623, 5343, 5347, 5344, 5345, + 5346, 7623, 7623, 5349, 5350, 5351, 7623, 5353, + 5354, 5355, 5356, 5357, 5358, 5359, 5360, 5361, + 5362, 5363, 7623, 5365, 7623, 5367, 7623, 5369, + 5371, 5380, 5370, 7695, 7623, 5372, 5375, 5378, + 5373, 5374, 7623, 5376, 5377, 7623, 5379, 7696, + 5381, 5382, 7623, 5383, 5384, 7623, 7623, 5385, + 7623, 5387, 5389, 5388, 7623, 5390, 7623, 5392, + 5393, 5394, 5395, 7623, 5397, 5401, 5398, 5399, + 5400, 7623, 5402, 7623, 7623, 5405, 5407, 5406, + 7623, 5408, 5409, 5410, 7623, 7623, 5412, 5418, + 5413, 5414, 5415, 5416, 5417, 7623, 5419, 5420, + 7623, 5422, 5426, 5448, 5458, 5467, 5493, 5499, + 5518, 5522, 5551, 5553, 5569, 5579, 5583, 5586, + 5630, 5633, 5652, 5676, 5698, 5760, 5788, 5795, + 5838, 5423, 5424, 7623, 7623, 5425, 7623, 5427, + 5445, 5446, 5428, 5429, 5430, 5435, 5431, 5432, + 5433, 5434, 7623, 5436, 5437, 5438, 5439, 5440, + 5441, 5442, 5443, 5444, 7623, 7623, 7623, 5447, + 7623, 5449, 5450, 5451, 5452, 5453, 5454, 5455, + 5456, 5457, 7623, 5459, 5463, 5460, 5461, 5462, + 7623, 5464, 5465, 5466, 7623, 5468, 5471, 5475, + 5477, 5487, 5469, 5470, 7623, 5472, 5473, 5474, + 7623, 5476, 7623, 7623, 5478, 5479, 5481, 5483, + 7623, 5480, 7623, 5482, 7623, 5484, 5485, 5486, + 7623, 5488, 5489, 7623, 5490, 5491, 7623, 5492, + 7623, 5494, 5495, 7697, 5496, 5497, 7623, 5498, + 7623, 5500, 5505, 5509, 5515, 5517, 5501, 5502, + 7623, 5503, 5504, 7623, 5506, 5507, 5508, 7623, + 5510, 5511, 7623, 5512, 5513, 5514, 7623, 5516, + 7623, 7623, 5519, 5520, 5521, 7623, 7623, 5523, + 5526, 5533, 5536, 5540, 5546, 5524, 5525, 7623, + 5527, 5528, 5530, 5529, 7623, 7623, 5531, 5532, + 7623, 5534, 5535, 7623, 5537, 5538, 5539, 7623, + 5541, 5544, 5542, 5543, 7623, 5545, 7623, 5547, + 5548, 5549, 7623, 5550, 7623, 5552, 7623, 5554, + 5555, 5564, 5567, 7623, 7623, 5556, 5563, 7623, + 5557, 5558, 7623, 5559, 5560, 5561, 5562, 7623, + 7623, 5565, 5566, 7623, 7623, 5568, 7623, 5570, + 5573, 5576, 5571, 5572, 7623, 5574, 5575, 7623, + 5577, 5578, 7623, 7623, 5580, 5582, 7623, 5581, + 7623, 7623, 5584, 5585, 7623, 5587, 5590, 5591, + 5594, 5596, 5623, 5626, 5588, 5589, 7623, 7623, + 5592, 5593, 7623, 5595, 7623, 7623, 5597, 5614, + 5621, 5598, 5599, 5604, 5600, 5601, 5602, 5603, + 7623, 5605, 5606, 5607, 5608, 5609, 5610, 5611, + 5612, 5613, 7623, 7623, 5615, 5616, 7623, 5617, + 5618, 5619, 5620, 7623, 7623, 5622, 7623, 5624, + 5625, 7623, 7623, 5627, 5628, 7623, 5629, 7623, + 5631, 5632, 7623, 5634, 7698, 5635, 7623, 7623, + 5637, 7623, 5638, 5639, 5642, 7623, 5640, 5641, + 7623, 5643, 5644, 5645, 7623, 7623, 7623, 5647, + 7623, 5648, 5649, 5650, 5651, 7623, 7623, 7623, + 5653, 5663, 5668, 5654, 7623, 5655, 5660, 5662, + 5656, 5657, 5658, 5659, 7623, 5661, 7623, 7623, + 5664, 5665, 5666, 5667, 7623, 7623, 5669, 5672, + 5670, 5671, 7623, 7623, 5673, 7623, 5674, 5675, + 7623, 5677, 5680, 5685, 5694, 5678, 5679, 7623, + 5681, 5682, 7623, 5683, 5684, 7623, 7623, 5686, + 5687, 5688, 5689, 5690, 5691, 5692, 5693, 7623, + 5695, 5696, 7623, 5697, 7623, 5699, 5705, 5720, + 5724, 5727, 5730, 5737, 7623, 5700, 5703, 5704, + 5701, 5702, 7623, 7623, 7623, 5706, 5707, 5708, + 5709, 5712, 5710, 5711, 7623, 5713, 5714, 5715, + 5716, 5717, 5718, 5719, 7623, 5721, 7623, 5722, + 7623, 5723, 7623, 5725, 5726, 7623, 5728, 5729, + 7623, 5731, 5732, 5733, 5735, 5734, 7623, 5736, + 7623, 5738, 5747, 5751, 7623, 5739, 5740, 5741, + 7623, 7623, 5742, 5743, 7623, 5744, 5745, 7623, + 5746, 7623, 5748, 7623, 5749, 5750, 7623, 7623, + 5752, 5753, 5754, 7623, 7623, 5755, 5756, 7623, + 5757, 5758, 7623, 5759, 7623, 5761, 5763, 5766, + 5768, 5762, 7623, 5764, 5765, 7699, 5767, 7623, + 5769, 5770, 5771, 5772, 5773, 5774, 5775, 5781, + 5776, 5777, 5778, 7623, 5779, 5780, 7623, 5782, + 5783, 5784, 5785, 7623, 5786, 5787, 7623, 7623, + 5789, 7623, 5790, 5793, 5791, 5792, 7623, 5794, + 7623, 5796, 5800, 5804, 5806, 5810, 5813, 5818, + 5827, 5835, 5797, 5798, 5799, 7623, 5801, 5802, + 5803, 7623, 5805, 7623, 5807, 5808, 5809, 7623, + 5811, 5812, 7623, 7623, 5814, 5815, 5816, 5817, + 7623, 5819, 5822, 5823, 5820, 5821, 7623, 7623, + 7623, 5824, 5825, 5826, 7623, 5828, 5831, 5829, + 5830, 7623, 5832, 5833, 5834, 7623, 5836, 5837, + 7623, 5839, 5842, 5849, 5840, 5841, 7623, 5843, + 5844, 5846, 5845, 7623, 7623, 5847, 5848, 7623, + 5850, 5851, 5852, 7623, 5854, 5855, 5861, 5865, + 5881, 5885, 5890, 5897, 5902, 5905, 5920, 5936, + 5939, 5948, 5969, 5977, 5986, 5988, 7623, 5856, + 5859, 5857, 5858, 7700, 5860, 7623, 5862, 5864, + 5863, 7623, 7701, 7623, 5866, 5869, 5873, 5875, + 5877, 5867, 5868, 7623, 5870, 5871, 5872, 7623, + 5874, 7623, 5876, 7623, 5878, 5879, 5880, 7623, + 5882, 5883, 5884, 7623, 5886, 5889, 5887, 5888, + 7623, 7623, 5891, 5893, 5896, 5892, 7623, 5894, + 5895, 7702, 7623, 5898, 5901, 5899, 5900, 7623, + 7623, 5903, 5904, 7623, 5906, 5909, 5916, 5919, + 5907, 5908, 7623, 5910, 5912, 5911, 7623, 5913, + 5914, 5915, 7623, 5917, 5918, 7623, 7623, 5921, + 5924, 5927, 5922, 5923, 7623, 5925, 5926, 7623, + 5928, 5932, 5933, 5929, 5930, 5931, 7623, 7623, + 5934, 5935, 7623, 5937, 5938, 7623, 5940, 5942, + 5945, 5941, 7623, 5943, 5944, 7623, 5946, 5947, + 7623, 7623, 5949, 5952, 5957, 5961, 5963, 5968, + 5950, 5951, 7623, 7623, 5953, 7703, 7704, 5954, + 7623, 5955, 5956, 7623, 5958, 5959, 5960, 7623, + 5962, 7623, 5964, 5965, 5966, 5967, 7623, 7623, + 5970, 5972, 5975, 5971, 7623, 5973, 5974, 7705, + 5976, 7623, 5978, 5979, 5981, 5980, 7706, 5982, + 5983, 7623, 5984, 5985, 7623, 5987, 7707, 5989, + 5990, 5991, 7623, 5993, 6004, 6006, 6022, 6024, + 6034, 6043, 6072, 6073, 6084, 6160, 6164, 5994, + 7623, 7708, 5999, 6003, 7623, 5996, 5997, 5998, + 7623, 6000, 6002, 6001, 7623, 7623, 7623, 6005, + 7623, 6007, 6008, 6011, 6014, 6017, 6018, 6009, + 6010, 7623, 6012, 6013, 7623, 6015, 6016, 7623, + 7623, 6019, 6020, 6021, 7623, 6023, 7623, 6025, + 6027, 6031, 7623, 6026, 7623, 6028, 6029, 6030, + 7623, 6032, 6033, 7623, 7623, 6035, 6042, 6036, + 6037, 6038, 6039, 6040, 6041, 7623, 7623, 6044, + 6051, 6045, 6046, 6049, 6047, 7623, 6048, 7623, + 6050, 7623, 6052, 7623, 6053, 6057, 6058, 6061, + 6064, 6065, 6066, 6069, 6054, 6055, 6056, 7623, + 7623, 6059, 6060, 7623, 6062, 6063, 7623, 7623, + 7623, 7709, 6067, 6068, 7623, 6070, 6071, 7623, + 7623, 6074, 6080, 6082, 6075, 6076, 6077, 6078, + 6079, 7623, 6081, 7623, 6083, 7710, 7623, 6085, + 6086, 6088, 6091, 6124, 6128, 6135, 6153, 6156, + 7623, 6087, 7623, 6089, 6090, 7623, 7623, 6092, + 7623, 6093, 6099, 6106, 6108, 6121, 6094, 6095, + 6096, 6097, 6098, 7623, 6100, 6101, 6102, 6103, + 6104, 6105, 7623, 6107, 7623, 6109, 6115, 6118, + 6110, 6111, 6112, 6113, 6114, 7623, 6116, 6117, + 7623, 6119, 6120, 7623, 6122, 6123, 7623, 6125, + 6126, 7623, 6127, 7623, 6129, 6130, 6132, 7623, + 6131, 7623, 6133, 6134, 7623, 6136, 6137, 6150, + 7623, 6138, 6142, 6146, 6139, 6140, 6141, 7623, + 6143, 6144, 6145, 7623, 6147, 6148, 6149, 7623, + 7623, 6151, 6152, 7623, 6154, 6155, 7623, 6157, + 6158, 6159, 7623, 6161, 6163, 6162, 7623, 7623, + 6165, 6166, 6167, 6168, 7623, 6170, 6172, 6175, + 6178, 6183, 6186, 6171, 7623, 6173, 6174, 7623, + 6176, 6177, 7623, 6179, 6180, 6181, 6182, 7623, + 6184, 6185, 7623, 6187, 6199, 6204, 6188, 6189, + 6196, 6190, 6191, 6192, 6193, 6194, 6195, 7623, + 6197, 6198, 7623, 6200, 6201, 7623, 6202, 6203, + 7623, 7711, 6206, 6216, 6220, 6223, 6278, 6296, + 6310, 6324, 6337, 6347, 6355, 6442, 6450, 6459, + 6463, 6484, 6495, 6499, 6512, 6529, 6535, 6207, + 6210, 6212, 6208, 6209, 7623, 6211, 7623, 6213, + 6214, 6215, 7623, 6217, 6218, 6219, 7623, 6221, + 6222, 7623, 6224, 6229, 6232, 6238, 6244, 6246, + 6268, 6225, 6226, 7623, 6227, 6228, 7623, 6230, + 6231, 7623, 6233, 6234, 6235, 6236, 6237, 7623, + 6239, 7623, 6240, 6241, 6242, 7623, 7623, 6243, + 7623, 6245, 7712, 6247, 7623, 6248, 6250, 6253, + 6254, 6256, 6258, 6260, 6262, 6265, 6267, 6249, + 7623, 7623, 6251, 6252, 7623, 7623, 6255, 7623, + 6257, 7623, 6259, 7623, 6261, 7623, 6263, 6264, + 7623, 6266, 7623, 7623, 6269, 6272, 6270, 6271, + 7623, 6273, 7623, 6274, 6275, 6276, 6277, 7623, + 6279, 6282, 6285, 6280, 6281, 7623, 6283, 6284, + 7623, 6286, 6290, 6287, 6288, 6289, 7623, 7623, + 6291, 6292, 7623, 6293, 6294, 6295, 7623, 7623, + 6297, 6301, 6307, 6309, 6298, 6299, 6300, 7623, + 6302, 6305, 6303, 6304, 7623, 6306, 7623, 6308, + 7623, 7623, 6311, 6313, 6318, 6322, 6312, 7623, + 6314, 6315, 6316, 6317, 7623, 6319, 6320, 7623, + 6321, 7623, 6323, 7623, 6325, 6335, 7713, 6326, + 7623, 6327, 6330, 6334, 6328, 6329, 7623, 6331, + 6332, 6333, 7623, 7623, 6336, 7623, 6338, 6342, + 6346, 6339, 6340, 6341, 7623, 6343, 6344, 6345, + 7623, 7623, 6348, 6353, 6349, 6350, 6351, 7623, + 7623, 6352, 7623, 7623, 6354, 7623, 6356, 6430, + 6432, 6357, 6358, 6359, 6368, 6381, 6399, 6410, + 6420, 6360, 6361, 6362, 6363, 7623, 6364, 6365, + 6366, 6367, 7623, 6369, 6370, 6371, 6372, 6373, + 6374, 6375, 6379, 6376, 6377, 6378, 7623, 6380, + 7623, 6382, 6383, 6384, 6385, 6391, 6386, 6387, + 6388, 6389, 6390, 7623, 6392, 6393, 6394, 6395, + 6396, 6397, 6398, 7623, 6400, 6401, 6402, 6403, + 6404, 6405, 6406, 6407, 6408, 6409, 7623, 6411, + 6412, 6413, 6414, 6415, 6416, 6417, 6418, 6419, + 7623, 6421, 6422, 6423, 6424, 6425, 6426, 6427, + 6428, 6429, 7623, 6431, 7623, 6433, 6434, 6435, + 6436, 6437, 6438, 6439, 6440, 6441, 7623, 6443, + 6446, 6449, 6444, 6445, 7623, 6447, 6448, 7623, + 7623, 6451, 6452, 6453, 6454, 7623, 6455, 6456, + 6457, 6458, 7623, 6460, 6461, 6462, 7623, 6464, + 6469, 6472, 6479, 6465, 6467, 6466, 7623, 6468, + 7623, 6470, 6471, 7623, 6473, 6475, 6476, 6474, + 7623, 7623, 6477, 6478, 7623, 6480, 6481, 6482, + 6483, 7623, 6485, 6489, 6486, 7623, 6487, 6488, + 7623, 6490, 6491, 6492, 6493, 6494, 7623, 6496, + 6497, 6498, 7623, 6500, 6504, 6506, 6507, 6501, + 6502, 6503, 7623, 6505, 7623, 7623, 6508, 6509, + 7623, 6510, 7623, 6511, 7623, 6513, 6517, 6521, + 6514, 6515, 6516, 7623, 6518, 6519, 6520, 7623, + 6522, 7623, 6523, 6524, 6525, 7623, 7623, 6526, + 6527, 6528, 7623, 6530, 6531, 6532, 6533, 6534, + 7623, 7623, 6537, 6542, 6546, 6580, 6585, 6612, + 6617, 6640, 6666, 6670, 6698, 6709, 6718, 6748, + 6752, 6767, 6789, 6913, 6928, 6538, 6539, 6540, + 6541, 7623, 6543, 6544, 6545, 7623, 7623, 6547, + 6548, 6553, 6556, 6560, 6563, 6570, 6576, 6579, + 7623, 6549, 6550, 7623, 6551, 6552, 7623, 6554, + 6555, 7623, 7623, 6557, 6558, 6559, 7623, 6561, + 6562, 7623, 6564, 6565, 6567, 7623, 6566, 7623, + 6568, 6569, 7623, 6571, 6572, 6573, 6574, 6575, + 7623, 6577, 6578, 7623, 7623, 6581, 6582, 7623, + 6583, 6584, 7623, 7623, 6586, 6589, 6596, 6597, + 6599, 6603, 6610, 6587, 6588, 7623, 6590, 6591, + 6593, 6592, 7623, 7623, 6594, 6595, 7623, 7714, + 6598, 7623, 6600, 6601, 6602, 7623, 6604, 6605, + 6609, 6606, 6607, 6608, 7623, 7623, 6611, 7623, + 6613, 7623, 6614, 6615, 6616, 7623, 6618, 6621, + 6626, 7715, 6619, 6620, 7623, 6622, 6625, 6623, + 6624, 7623, 7623, 6627, 6628, 6629, 6632, 6630, + 6631, 7623, 6633, 6634, 6635, 6636, 6637, 6638, + 6639, 7623, 6641, 6646, 6642, 6643, 7623, 6644, + 6645, 7623, 7623, 7623, 6647, 6650, 6652, 6654, + 6656, 6658, 6662, 6648, 6649, 7623, 7623, 6651, + 7623, 7623, 6653, 7623, 7623, 6655, 7623, 6657, + 7623, 6659, 6660, 6661, 7623, 6663, 6664, 6665, + 7623, 6667, 6668, 6669, 7623, 6671, 6685, 6691, + 6695, 6672, 6682, 6673, 6674, 6675, 6676, 6677, + 6678, 6679, 6680, 6681, 7623, 6683, 6684, 7623, + 6686, 6687, 6688, 6689, 6690, 7623, 6692, 6693, + 7623, 6694, 7623, 7623, 6696, 7623, 6697, 7623, + 6699, 6703, 6707, 6700, 6701, 6702, 7623, 7623, + 6704, 7623, 6705, 6706, 7623, 6708, 7623, 6710, + 6711, 6717, 6712, 6713, 7623, 6714, 6715, 6716, + 7623, 7623, 6719, 6726, 6742, 6720, 6723, 6721, + 7623, 6722, 7623, 6724, 7623, 6725, 7623, 6727, + 6728, 6735, 7623, 6729, 6730, 7623, 6731, 6732, + 7623, 6733, 6734, 7623, 7623, 6736, 6737, 7623, + 6738, 6739, 7623, 6740, 6741, 7623, 7623, 6743, + 6747, 6744, 6745, 6746, 7623, 7623, 7623, 6749, + 6750, 6751, 7623, 6753, 6755, 6759, 6763, 6754, + 7623, 6756, 6757, 6758, 7623, 6760, 6761, 6762, + 7623, 6764, 6765, 6766, 7623, 6768, 6771, 6769, + 7623, 6770, 7623, 6772, 6787, 6773, 6774, 6775, + 6776, 6777, 6784, 6778, 6779, 6780, 6781, 6782, + 6783, 7623, 6785, 6786, 7623, 6788, 7623, 6790, + 6829, 6862, 6863, 6865, 7623, 6791, 6792, 6795, + 6799, 6803, 6806, 6810, 6814, 7623, 6793, 6794, + 7623, 7623, 6796, 6797, 6798, 7623, 6800, 6801, + 6802, 7623, 6804, 6805, 7623, 7623, 6807, 6808, + 6809, 7623, 6811, 6812, 6813, 7623, 6815, 6824, + 6826, 6816, 7623, 6817, 6820, 6818, 7623, 6819, + 7623, 6821, 6822, 7623, 6823, 7623, 6825, 7623, + 6827, 6828, 7623, 7623, 6830, 7623, 6831, 6837, + 6844, 6846, 6859, 6832, 6833, 6834, 6835, 6836, + 7623, 6838, 6839, 6840, 6841, 6842, 6843, 7623, + 6845, 7623, 6847, 6853, 6856, 6848, 6849, 6850, + 6851, 6852, 7623, 6854, 6855, 7623, 6857, 6858, + 7623, 6860, 6861, 7623, 7623, 6864, 7623, 7716, + 7717, 7718, 7623, 6866, 6867, 6873, 6877, 6883, + 6887, 6891, 6894, 6898, 7623, 6868, 6870, 6869, + 7623, 6871, 6872, 7623, 7623, 6874, 6875, 6876, + 7623, 6878, 6879, 6881, 6880, 7623, 6882, 7623, + 6884, 6885, 6886, 7623, 6888, 6889, 6890, 7623, + 6892, 6893, 7623, 7623, 6895, 6896, 6897, 7623, + 6899, 6908, 6910, 6900, 7623, 6901, 6904, 6902, + 7623, 6903, 7623, 6905, 6906, 7623, 6907, 7623, + 6909, 7623, 6911, 6912, 7623, 7623, 6914, 6917, + 6924, 6915, 6916, 7623, 6918, 6919, 6921, 6920, + 7623, 7623, 6922, 6923, 7623, 6925, 6926, 6927, + 7623, 6929, 6930, 7719, 6932, 6938, 6941, 6951, + 6954, 6959, 6961, 6999, 7011, 7027, 7032, 7085, + 7096, 6933, 6937, 6934, 6935, 6936, 7623, 7623, + 6939, 6940, 7623, 6942, 6946, 6950, 6943, 6944, + 6945, 7623, 6947, 6948, 6949, 7623, 7623, 6952, + 6953, 7623, 6955, 6956, 6957, 6958, 7623, 6960, + 7623, 6962, 6976, 6991, 6997, 6963, 6970, 6964, + 6965, 6966, 7623, 6967, 6968, 6969, 7623, 6971, + 7623, 6972, 6975, 6973, 6974, 7623, 7623, 6977, + 6988, 6978, 6979, 6985, 6980, 6981, 6982, 6983, + 6984, 7623, 6986, 6987, 7623, 6989, 6990, 7623, + 6992, 6994, 6993, 7623, 6995, 6996, 7623, 6998, + 7720, 7000, 7003, 7009, 7001, 7002, 7623, 7004, + 7721, 7623, 7623, 7006, 7007, 7623, 7623, 7010, + 7623, 7012, 7014, 7025, 7013, 7623, 7623, 7015, + 7018, 7021, 7016, 7017, 7623, 7019, 7020, 7623, + 7623, 7022, 7023, 7024, 7623, 7026, 7623, 7028, + 7029, 7030, 7031, 7623, 7033, 7036, 7079, 7034, + 7035, 7623, 7037, 7060, 7063, 7064, 7069, 7073, + 7075, 7038, 7039, 7040, 7041, 7623, 7042, 7046, + 7052, 7053, 7043, 7044, 7045, 7623, 7047, 7048, + 7049, 7623, 7050, 7051, 7623, 7623, 7054, 7055, + 7056, 7057, 7623, 7058, 7059, 7623, 7061, 7062, + 7623, 7623, 7065, 7066, 7067, 7068, 7623, 7070, + 7071, 7072, 7623, 7074, 7623, 7076, 7077, 7078, + 7623, 7080, 7081, 7082, 7083, 7084, 7623, 7086, + 7089, 7092, 7087, 7088, 7623, 7623, 7090, 7091, + 7623, 7093, 7094, 7095, 7623, 7097, 7100, 7098, + 7099, 7623, 7101, 7102, 7103, 7104, 7105, 7114, + 7106, 7107, 7108, 7109, 7110, 7111, 7112, 7113, + 7623, 7115, 7116, 7117, 7118, 7119, 7120, 7121, + 7122, 7123, 7623, 7125, 7128, 7131, 7137, 7144, + 7148, 7159, 7165, 7169, 7177, 7190, 7194, 7200, + 7248, 7264, 7267, 7278, 7283, 7126, 7127, 7623, + 7129, 7130, 7623, 7132, 7135, 7133, 7134, 7722, + 7136, 7623, 7138, 7139, 7141, 7140, 7623, 7142, + 7143, 7623, 7145, 7147, 7146, 7723, 7623, 7149, + 7152, 7156, 7150, 7151, 7623, 7153, 7154, 7155, + 7623, 7157, 7158, 7623, 7160, 7164, 7161, 7162, + 7163, 7623, 7623, 7166, 7167, 7168, 7724, 7170, + 7174, 7171, 7172, 7173, 7623, 7623, 7175, 7176, + 7623, 7178, 7187, 7179, 7184, 7180, 7181, 7623, + 7182, 7183, 7623, 7185, 7186, 7623, 7188, 7189, + 7623, 7191, 7725, 7192, 7193, 7623, 7195, 7198, + 7196, 7197, 7623, 7199, 7623, 7201, 7206, 7215, + 7231, 7234, 7240, 7202, 7203, 7204, 7205, 7623, + 7207, 7208, 7209, 7210, 7211, 7212, 7213, 7214, + 7623, 7216, 7217, 7218, 7219, 7220, 7221, 7222, + 7226, 7223, 7224, 7225, 7623, 7227, 7228, 7229, + 7230, 7623, 7232, 7233, 7623, 7235, 7623, 7236, + 7237, 7623, 7238, 7239, 7623, 7241, 7242, 7243, + 7244, 7245, 7246, 7247, 7623, 7249, 7258, 7261, + 7250, 7255, 7251, 7252, 7623, 7253, 7254, 7623, + 7256, 7257, 7623, 7259, 7260, 7623, 7262, 7263, + 7623, 7265, 7266, 7623, 7268, 7271, 7275, 7269, + 7270, 7623, 7272, 7273, 7274, 7623, 7276, 7623, + 7277, 7623, 7279, 7282, 7280, 7281, 7623, 7726, + 7284, 7285, 7286, 7287, 7288, 7623, 7290, 7293, + 7297, 7301, 7381, 7383, 7387, 7403, 7405, 7409, + 7414, 7417, 7421, 7425, 7437, 7291, 7292, 7623, + 7294, 7295, 7623, 7296, 7623, 7298, 7299, 7300, + 7623, 7302, 7306, 7303, 7304, 7305, 7623, 7307, + 7314, 7319, 7326, 7335, 7338, 7360, 7308, 7309, + 7310, 7311, 7312, 7313, 7623, 7315, 7316, 7317, + 7318, 7623, 7320, 7321, 7322, 7323, 7324, 7325, + 7623, 7327, 7329, 7330, 7328, 7623, 7623, 7331, + 7332, 7333, 7334, 7623, 7623, 7336, 7337, 7623, + 7339, 7343, 7340, 7341, 7342, 7623, 7344, 7352, + 7345, 7346, 7347, 7348, 7349, 7350, 7623, 7351, + 7623, 7353, 7354, 7355, 7356, 7357, 7358, 7623, + 7359, 7623, 7361, 7365, 7362, 7363, 7364, 7623, + 7366, 7367, 7368, 7369, 7370, 7371, 7372, 7376, + 7373, 7374, 7375, 7623, 7377, 7378, 7379, 7380, + 7623, 7382, 7623, 7384, 7385, 7386, 7623, 7388, + 7394, 7398, 7623, 7389, 7392, 7390, 7391, 7623, + 7393, 7623, 7395, 7396, 7397, 7623, 7399, 7402, + 7400, 7401, 7623, 7623, 7404, 7623, 7406, 7407, + 7408, 7623, 7410, 7411, 7412, 7413, 7623, 7623, + 7415, 7416, 7623, 7418, 7419, 7420, 7623, 7422, + 7423, 7424, 7623, 7426, 7428, 7427, 7623, 7429, + 7433, 7430, 7431, 7432, 7623, 7623, 7434, 7435, + 7436, 7623, 7623, 7438, 7439, 7440, 7441, 7442, + 7623, 7444, 7448, 7460, 7462, 7465, 7466, 7471, + 7445, 7446, 7447, 7623, 7449, 7456, 7450, 7453, + 7451, 7452, 7623, 7454, 7623, 7455, 7623, 7457, + 7458, 7459, 7623, 7461, 7623, 7463, 7464, 7623, + 7623, 7623, 7467, 7468, 7469, 7470, 7623, 7472, + 7473, 7623, 7475, 7483, 7487, 7489, 7496, 7497, + 7504, 7507, 7510, 7523, 7530, 7537, 7545, 7548, + 7476, 7478, 7481, 7477, 7623, 7479, 7480, 7623, + 7482, 7623, 7484, 7485, 7486, 7623, 7488, 7623, + 7490, 7493, 7491, 7492, 7623, 7494, 7495, 7623, + 7623, 7498, 7501, 7499, 7500, 7623, 7502, 7503, + 7623, 7505, 7506, 7623, 7508, 7509, 7623, 7511, + 7514, 7519, 7512, 7513, 7623, 7515, 7516, 7623, + 7517, 7518, 7623, 7520, 7521, 7522, 7623, 7524, + 7527, 7525, 7526, 7623, 7528, 7529, 7623, 7531, + 7533, 7532, 7623, 7534, 7535, 7536, 7623, 7538, + 7542, 7539, 7540, 7541, 7623, 7543, 7544, 7623, + 7546, 7547, 7623, 7549, 7550, 7551, 7552, 7623, + 7554, 7559, 7564, 7565, 7567, 7570, 7573, 7576, + 7555, 7556, 7558, 7557, 7727, 7623, 7560, 7563, + 7561, 7562, 7623, 7623, 7728, 7566, 7623, 7568, + 7569, 7623, 7571, 7572, 7623, 7574, 7575, 7623, + 7577, 7579, 7578, 7623, 7729, 7581, 7586, 7592, + 7595, 7602, 7604, 7607, 7613, 7616, 7619, 7582, + 7583, 7584, 7585, 7623, 7587, 7591, 7588, 7589, + 7590, 7623, 7623, 7593, 7594, 7623, 7596, 7600, + 7597, 7598, 7599, 7623, 7601, 7623, 7603, 7623, + 7605, 7606, 7623, 7608, 7609, 7610, 7611, 7612, + 7623, 7614, 7615, 7623, 7617, 7618, 7623, 7620, + 7621, 7623, 7622, 7623, 1, 69, 118, 332, + 656, 773, 827, 913, 979, 1076, 1098, 1123, + 1493, 1532, 2026, 2129, 2214, 2225, 2479, 2676, + 2770, 2946, 3016, 3033, 3043, 3073, 3114, 3250, + 3561, 3848, 4087, 4268, 4365, 4510, 4626, 4763, + 4789, 4820, 5311, 5421, 5853, 5992, 6169, 6205, + 6536, 6931, 7124, 7289, 7443, 7474, 7553, 7580, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 3620, 7623, 7623, 3729, 7623, 7623, + 7623, 7623, 7623, 7623, 3940, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 4454, + 4458, 4461, 4465, 4470, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 5259, 5263, 5266, 5270, + 5274, 5278, 5283, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 5636, 5646, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 5995, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7005, 7008, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623, + 7623, 7623, 7623, 7623, 7623, 7623, 7623, 7623 +}; + +static const short _char_ref_trans_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 0, 0, + 0, 0, 17, 0, 19, 0, 0, 0, + 0, 0, 0, 0, 23, 0, 0, 0, + 25, 0, 27, 0, 0, 0, 0, 29, + 0, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 37, 0, 0, 0, + 39, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 45, + 0, 0, 47, 0, 0, 49, 0, 51, + 0, 0, 0, 0, 0, 0, 0, 53, + 0, 0, 0, 0, 0, 0, 0, 55, + 0, 57, 0, 59, 0, 0, 61, 0, + 0, 0, 63, 0, 0, 65, 0, 0, + 0, 0, 67, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 69, 0, 0, 0, 0, + 0, 0, 0, 0, 73, 75, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 77, + 0, 0, 0, 0, 79, 0, 0, 0, + 0, 0, 0, 0, 81, 0, 0, 0, + 0, 0, 85, 0, 0, 0, 0, 87, + 0, 0, 89, 0, 0, 0, 0, 0, + 0, 91, 0, 0, 0, 0, 0, 0, + 93, 0, 95, 0, 97, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 99, + 0, 0, 0, 0, 101, 0, 0, 0, + 103, 0, 0, 0, 0, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 107, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 109, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 0, 113, 0, 115, 0, + 0, 0, 0, 0, 0, 0, 0, 117, + 0, 0, 119, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 121, 0, + 0, 123, 0, 0, 0, 0, 0, 125, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 127, 0, 0, 0, + 129, 0, 0, 131, 0, 133, 0, 0, + 0, 135, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 137, 0, 0, + 0, 0, 0, 0, 139, 0, 0, 141, + 0, 0, 143, 0, 0, 145, 0, 0, + 0, 0, 0, 0, 147, 0, 149, 0, + 0, 151, 0, 0, 0, 0, 0, 153, + 155, 0, 157, 0, 0, 159, 0, 161, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 163, 0, 0, 0, 165, + 0, 0, 0, 0, 0, 0, 0, 0, + 167, 0, 0, 0, 0, 169, 0, 0, + 0, 0, 171, 0, 0, 0, 173, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 175, 0, 0, 0, 0, 0, 177, + 179, 0, 0, 0, 0, 181, 0, 0, + 0, 0, 183, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 185, 0, 0, 0, 187, 0, + 0, 0, 0, 0, 0, 189, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 191, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 193, 0, 0, 195, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 197, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 199, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 201, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 203, 0, 0, 205, 0, + 0, 0, 0, 0, 0, 0, 207, 0, + 0, 0, 0, 0, 0, 0, 0, 209, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 211, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 213, 0, + 0, 0, 0, 215, 0, 0, 0, 0, + 0, 0, 217, 0, 0, 0, 0, 219, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 221, 0, 0, 0, 0, 0, 0, 0, + 0, 223, 0, 0, 0, 0, 0, 225, + 0, 0, 0, 227, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 229, 0, 0, 0, 0, 0, + 231, 0, 0, 0, 233, 0, 0, 235, + 0, 0, 0, 0, 0, 237, 0, 0, + 0, 0, 239, 0, 0, 0, 241, 0, + 0, 0, 243, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 245, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 251, 0, 0, 255, 0, 0, 257, 0, + 259, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 263, 0, 0, 0, 0, 265, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 267, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 269, 0, 0, + 0, 0, 271, 0, 273, 0, 0, 0, + 0, 0, 275, 0, 0, 0, 0, 277, + 0, 0, 0, 0, 0, 279, 0, 0, + 0, 0, 0, 0, 0, 281, 0, 0, + 0, 283, 0, 285, 0, 287, 0, 0, + 0, 0, 0, 0, 0, 291, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 293, + 0, 0, 0, 0, 0, 0, 295, 0, + 297, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 299, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 301, 0, 0, 0, 0, 303, 0, 0, + 0, 305, 0, 0, 0, 0, 0, 0, + 0, 307, 0, 0, 309, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 311, 0, 0, 0, 315, + 0, 317, 0, 0, 0, 0, 319, 0, + 0, 0, 0, 0, 0, 321, 0, 0, + 323, 325, 0, 0, 327, 0, 329, 331, + 0, 0, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 335, 0, 0, 0, 0, 337, + 0, 0, 0, 0, 0, 0, 0, 0, + 339, 0, 0, 0, 0, 0, 0, 341, + 0, 0, 0, 343, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 345, 0, 0, + 0, 0, 347, 0, 0, 349, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 353, 0, 0, 0, 0, + 355, 357, 0, 0, 0, 359, 0, 361, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 363, 0, 0, 0, 365, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 367, 0, 0, 0, 369, 0, + 0, 0, 371, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 373, 0, + 0, 0, 0, 375, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 377, 0, 0, 0, + 379, 0, 0, 381, 0, 0, 0, 0, + 0, 0, 0, 0, 387, 0, 0, 389, + 0, 391, 0, 0, 0, 0, 395, 0, + 0, 0, 0, 0, 397, 0, 0, 0, + 0, 0, 0, 399, 0, 0, 0, 0, + 401, 0, 0, 403, 0, 0, 0, 0, + 0, 0, 405, 0, 0, 0, 0, 0, + 0, 0, 407, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 409, + 0, 0, 0, 0, 411, 0, 0, 0, + 0, 0, 413, 0, 415, 0, 417, 0, + 0, 419, 0, 0, 0, 0, 421, 0, + 0, 0, 0, 423, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 427, 429, + 0, 431, 0, 0, 433, 0, 0, 0, + 435, 0, 0, 0, 437, 0, 0, 0, + 439, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 441, 0, 0, 443, 0, 0, + 0, 445, 0, 0, 0, 0, 0, 447, + 449, 0, 451, 0, 0, 453, 0, 0, + 455, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 457, 0, + 0, 0, 0, 0, 0, 0, 0, 461, + 0, 0, 0, 463, 0, 465, 0, 0, + 0, 0, 0, 0, 0, 467, 0, 469, + 0, 0, 0, 0, 0, 0, 471, 0, + 0, 0, 473, 475, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 477, 0, 0, + 0, 479, 0, 0, 0, 0, 481, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 483, 0, 0, 0, 0, 0, 0, 485, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 487, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 489, 0, 0, 0, 0, 0, 491, + 0, 0, 0, 493, 0, 0, 0, 0, + 495, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 497, 0, 0, 0, 0, + 0, 499, 0, 0, 0, 501, 0, 0, + 0, 0, 0, 0, 503, 0, 0, 0, + 0, 0, 505, 0, 0, 0, 0, 0, + 0, 507, 0, 0, 0, 0, 509, 0, + 0, 0, 0, 511, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 513, 0, 0, 0, 0, 0, 0, + 0, 0, 515, 0, 0, 0, 0, 0, + 517, 0, 0, 0, 519, 0, 0, 0, + 0, 0, 521, 0, 0, 0, 523, 0, + 0, 0, 0, 525, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 527, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 529, 0, 0, 0, 0, 0, 0, 0, + 0, 531, 0, 0, 0, 0, 0, 0, + 533, 0, 0, 0, 535, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 537, 0, + 0, 0, 0, 539, 0, 541, 543, 0, + 0, 0, 0, 0, 0, 0, 0, 545, + 0, 0, 0, 0, 547, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 549, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 551, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 553, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 555, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 557, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 559, 0, 561, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 563, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 565, 0, 0, 0, 0, 567, 569, + 0, 0, 0, 571, 573, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 575, 0, + 577, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 579, 0, 0, 0, 0, + 0, 0, 581, 0, 583, 0, 0, 0, + 0, 0, 0, 0, 585, 0, 0, 587, + 0, 0, 589, 591, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 593, + 0, 0, 0, 0, 595, 0, 0, 0, + 0, 0, 0, 597, 0, 0, 0, 599, + 601, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 603, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 605, 0, 0, 0, 0, 0, 607, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 609, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 611, + 0, 0, 0, 0, 0, 0, 0, 613, + 0, 0, 0, 0, 615, 0, 617, 0, + 0, 0, 0, 0, 0, 0, 0, 619, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 621, 0, 623, + 625, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 627, 0, + 0, 0, 0, 629, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 631, 0, 0, 0, + 0, 0, 0, 0, 0, 633, 0, 0, + 0, 635, 0, 0, 0, 0, 0, 637, + 0, 0, 0, 0, 639, 0, 0, 0, + 0, 0, 0, 641, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 643, 0, + 0, 0, 0, 0, 0, 0, 0, 645, + 0, 0, 0, 0, 0, 0, 647, 0, + 0, 0, 649, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 651, 0, 0, 0, + 0, 653, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 655, 0, + 0, 0, 0, 657, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 659, 0, 0, 0, 0, 661, 0, 0, + 0, 0, 663, 0, 665, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 667, 0, + 0, 0, 0, 0, 0, 669, 0, 0, + 0, 671, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 673, 0, 0, 0, 0, + 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 677, 0, 0, + 0, 0, 0, 0, 0, 679, 0, 0, + 0, 0, 0, 0, 0, 681, 0, 0, + 0, 0, 0, 0, 683, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 685, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 687, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 689, 0, 0, 0, 0, 691, 0, + 0, 0, 0, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 695, 0, 0, 0, 0, 0, 697, + 0, 0, 0, 0, 0, 699, 0, 0, + 0, 0, 0, 701, 0, 0, 0, 0, + 0, 0, 703, 0, 0, 0, 0, 0, + 705, 0, 0, 0, 0, 0, 707, 0, + 0, 0, 0, 0, 0, 0, 709, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 711, 0, 0, 0, 0, 713, 0, 0, + 0, 0, 0, 715, 0, 0, 0, 0, + 0, 717, 0, 0, 0, 0, 719, 0, + 0, 0, 0, 0, 0, 0, 721, 0, + 0, 0, 0, 0, 0, 0, 0, 723, + 0, 0, 0, 0, 725, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, + 0, 0, 729, 0, 0, 0, 0, 733, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 0, 0, 0, 0, 0, + 0, 0, 741, 0, 0, 0, 0, 743, + 0, 745, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 749, 0, 0, 751, 0, + 0, 0, 0, 753, 0, 0, 755, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 757, 0, 0, 0, 0, 759, + 761, 0, 0, 0, 763, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 769, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 773, 0, 0, 0, 0, 775, 0, + 0, 777, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 779, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 781, 0, 783, 0, + 785, 0, 787, 789, 0, 0, 0, 0, + 0, 0, 0, 791, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 793, 0, 795, 797, 0, 0, 0, 0, + 0, 0, 0, 0, 799, 0, 0, 0, + 0, 0, 0, 0, 801, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 803, 0, + 0, 0, 0, 805, 0, 0, 807, 0, + 0, 0, 0, 0, 809, 0, 0, 0, + 0, 0, 0, 811, 0, 0, 813, 0, + 0, 0, 815, 817, 0, 0, 0, 0, + 0, 0, 0, 821, 0, 0, 823, 0, + 0, 825, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 827, 0, 0, 0, 0, 0, 0, + 0, 831, 0, 833, 0, 835, 0, 0, + 837, 0, 0, 0, 0, 0, 0, 839, + 0, 0, 0, 841, 843, 845, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 847, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 849, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 851, 0, 853, 0, 855, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 857, + 0, 0, 0, 859, 0, 0, 0, 0, + 861, 0, 0, 0, 0, 0, 0, 0, + 0, 863, 0, 0, 0, 0, 0, 0, + 865, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 867, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 869, 0, 0, 0, 0, 0, + 871, 0, 0, 0, 873, 0, 0, 0, + 0, 875, 0, 0, 0, 877, 0, 0, + 0, 0, 0, 0, 879, 0, 0, 0, + 0, 0, 881, 0, 0, 0, 0, 0, + 0, 883, 0, 0, 0, 0, 885, 0, + 0, 0, 0, 887, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 889, 0, 0, 0, 0, 0, 0, + 0, 0, 891, 0, 0, 0, 0, 0, + 893, 0, 0, 0, 895, 0, 0, 0, + 0, 0, 897, 0, 0, 0, 899, 0, + 0, 0, 0, 901, 0, 0, 0, 903, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 905, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 907, 0, 0, 0, 909, + 911, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 913, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 915, 0, 917, + 0, 0, 0, 0, 919, 0, 0, 0, + 0, 921, 923, 0, 0, 0, 0, 0, + 0, 0, 925, 0, 0, 0, 927, 0, + 0, 929, 931, 0, 933, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 935, 0, 0, 0, + 0, 0, 0, 0, 0, 937, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 939, + 0, 0, 0, 0, 0, 0, 941, 0, + 0, 0, 943, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 945, 0, 0, 947, + 0, 0, 0, 949, 0, 0, 0, 951, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 953, 0, + 0, 0, 0, 0, 0, 955, 0, 0, + 0, 0, 0, 957, 0, 0, 0, 0, + 0, 959, 0, 0, 0, 0, 0, 961, + 0, 0, 0, 0, 963, 0, 0, 965, + 0, 0, 967, 0, 0, 0, 0, 969, + 0, 0, 0, 971, 0, 0, 0, 0, + 0, 973, 0, 0, 0, 0, 0, 0, + 975, 0, 0, 0, 0, 0, 0, 0, + 977, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 979, 0, 0, 0, 0, 981, + 0, 0, 0, 0, 983, 985, 987, 0, + 0, 0, 0, 0, 0, 989, 0, 0, + 0, 0, 0, 991, 0, 0, 993, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 997, 0, 0, 0, 0, 999, 0, 1001, + 0, 0, 1003, 1005, 0, 0, 0, 0, + 0, 0, 1007, 0, 0, 0, 1009, 1011, + 0, 1013, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1015, 0, 1017, 0, 0, + 0, 0, 0, 0, 0, 0, 1019, 0, + 0, 0, 0, 0, 1021, 0, 0, 0, + 1023, 0, 0, 0, 0, 0, 0, 0, + 1025, 0, 0, 0, 0, 0, 0, 0, + 0, 1027, 0, 0, 0, 0, 1029, 0, + 0, 1031, 0, 0, 0, 0, 0, 0, + 0, 1033, 0, 0, 0, 1035, 0, 0, + 0, 1037, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1041, 0, + 0, 0, 0, 1043, 0, 0, 0, 0, + 1045, 0, 0, 1047, 0, 0, 0, 0, + 1051, 0, 0, 0, 0, 1053, 0, 1055, + 0, 0, 0, 0, 0, 0, 0, 1059, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1061, 0, 0, 0, 0, 1063, 0, + 0, 1065, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1067, 0, 0, 1069, + 0, 0, 0, 0, 1071, 0, 0, 0, + 0, 1073, 0, 1075, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1077, 0, 0, 0, 0, 1079, 0, 0, + 0, 0, 0, 0, 0, 0, 1081, 0, + 0, 0, 0, 0, 0, 0, 0, 1083, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1085, 0, 0, 1087, 0, 0, + 0, 0, 0, 1089, 0, 0, 0, 0, + 1091, 0, 0, 0, 0, 0, 0, 0, + 0, 1093, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1095, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1097, 0, 1099, 0, 0, 0, 1101, 0, + 0, 0, 1103, 0, 0, 1105, 0, 0, + 0, 0, 1107, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1111, 0, 0, 1113, 0, 1115, 0, + 0, 0, 1117, 0, 1119, 0, 0, 1121, + 0, 0, 0, 0, 0, 1123, 1125, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1127, 0, 0, 0, 1129, 0, 0, + 0, 0, 0, 0, 0, 0, 1131, 0, + 0, 0, 0, 1133, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1135, 0, 1137, + 0, 0, 1139, 0, 0, 1141, 0, 0, + 0, 0, 1143, 0, 0, 0, 0, 0, + 0, 0, 0, 1145, 0, 0, 0, 1147, + 0, 1149, 0, 0, 1151, 0, 0, 1153, + 0, 0, 0, 0, 0, 1155, 1157, 0, + 0, 1159, 0, 0, 1161, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1163, 0, 0, 1165, 0, 0, 1167, 0, + 0, 0, 0, 0, 0, 0, 0, 1171, + 1173, 0, 1175, 0, 0, 1177, 0, 0, + 1179, 0, 0, 1181, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1183, 0, + 0, 0, 0, 1185, 0, 0, 0, 0, + 0, 1187, 1189, 0, 0, 1191, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1193, 0, 1195, 0, 1197, + 0, 0, 1199, 0, 0, 1201, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1205, 1207, + 0, 0, 0, 0, 0, 1209, 1211, 0, + 0, 0, 0, 1217, 0, 0, 0, 1221, + 0, 1223, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1227, 0, 1229, + 0, 0, 1231, 0, 0, 0, 0, 0, + 1233, 0, 1235, 0, 0, 1239, 0, 0, + 0, 0, 0, 0, 1241, 1243, 0, 0, + 0, 0, 1245, 1247, 1249, 0, 0, 0, + 0, 0, 0, 1251, 0, 1253, 0, 0, + 1255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1257, 1259, 1261, 1263, 1265, 1267, + 1269, 1271, 0, 1273, 0, 0, 1275, 0, + 1277, 0, 0, 0, 1279, 1281, 0, 0, + 0, 1283, 0, 0, 0, 0, 1285, 0, + 1287, 1289, 0, 0, 0, 0, 0, 0, + 1291, 0, 0, 0, 1293, 1295, 0, 1297, + 0, 1299, 0, 0, 0, 1301, 0, 0, + 1303, 0, 0, 0, 0, 0, 0, 0, + 1307, 1309, 0, 0, 1311, 0, 0, 1313, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1319, 0, 0, + 1321, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1323, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1325, 0, + 0, 0, 0, 0, 0, 1327, 0, 0, + 0, 0, 1329, 0, 0, 1331, 0, 0, + 1333, 0, 0, 0, 0, 1335, 0, 0, + 1337, 0, 0, 1339, 0, 0, 1341, 0, + 0, 0, 0, 1343, 0, 0, 0, 0, + 1345, 1347, 0, 0, 0, 1349, 0, 0, + 0, 0, 0, 0, 0, 0, 1351, 0, + 1353, 0, 0, 0, 0, 1355, 0, 0, + 1357, 0, 0, 0, 1359, 0, 0, 0, + 1361, 1363, 0, 0, 0, 1365, 0, 1367, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1369, 0, 0, 1371, + 0, 1373, 0, 0, 0, 0, 0, 1375, + 0, 0, 0, 1377, 0, 0, 0, 0, + 1379, 0, 0, 0, 0, 0, 1381, 0, + 0, 1383, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1385, 0, + 1387, 0, 0, 0, 0, 1389, 0, 0, + 1391, 0, 0, 0, 0, 1393, 0, 0, + 0, 0, 1395, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1397, 0, 0, 0, 0, 0, + 1399, 0, 0, 0, 0, 0, 0, 0, + 1401, 0, 0, 0, 0, 0, 0, 1403, + 0, 0, 0, 1405, 0, 0, 0, 0, + 1407, 0, 1409, 0, 0, 0, 0, 1411, + 1413, 0, 1415, 0, 0, 1417, 0, 0, + 1419, 0, 0, 0, 0, 1421, 0, 1423, + 0, 0, 0, 0, 0, 1425, 1427, 0, + 0, 0, 1429, 0, 0, 0, 1431, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1433, + 1435, 1437, 1439, 1441, 0, 0, 0, 0, + 1443, 1445, 1447, 1449, 0, 0, 0, 0, + 1451, 1453, 1455, 1457, 1459, 0, 0, 0, + 0, 0, 0, 1461, 1463, 1465, 1467, 1469, + 1471, 0, 0, 1473, 0, 0, 0, 0, + 1475, 1477, 1479, 1481, 1483, 0, 0, 0, + 0, 1485, 1487, 1489, 1491, 0, 0, 0, + 0, 1493, 0, 0, 0, 1495, 0, 0, + 0, 0, 1497, 0, 0, 0, 0, 1499, + 1501, 1503, 1505, 1507, 0, 0, 0, 0, + 0, 0, 1509, 1511, 1513, 1515, 1517, 1519, + 0, 0, 0, 0, 1521, 0, 0, 0, + 0, 1523, 0, 0, 0, 0, 0, 0, + 0, 0, 1527, 0, 0, 1529, 0, 1531, + 0, 1533, 0, 1535, 0, 0, 1537, 0, + 0, 0, 1539, 0, 0, 0, 1541, 0, + 0, 1543, 0, 1545, 0, 0, 1547, 1549, + 0, 1551, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1553, + 1555, 0, 0, 0, 0, 0, 0, 0, + 1557, 0, 0, 0, 0, 1559, 0, 0, + 0, 1561, 0, 1563, 0, 0, 1565, 1567, + 0, 0, 0, 1569, 0, 1571, 0, 0, + 0, 0, 0, 0, 0, 1573, 0, 0, + 1575, 0, 0, 0, 0, 0, 1579, 0, + 0, 1581, 0, 0, 1583, 0, 0, 1585, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1589, 5, 4469, 0, 0, 0, 0, + 1593, 0, 1595, 0, 0, 0, 0, 1597, + 0, 0, 1599, 0, 0, 0, 0, 1601, + 1603, 0, 1605, 0, 0, 0, 0, 0, + 0, 1607, 1609, 0, 0, 0, 1611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1613, 0, 0, 0, 0, + 1615, 0, 0, 0, 0, 0, 1617, 1619, + 0, 0, 1621, 0, 0, 0, 1623, 0, + 0, 0, 1625, 1627, 0, 0, 0, 0, + 1629, 0, 0, 1631, 0, 0, 0, 1633, + 0, 0, 0, 1635, 0, 0, 0, 1637, + 0, 0, 0, 0, 0, 0, 1639, 0, + 1641, 0, 1643, 0, 0, 0, 1645, 0, + 1647, 1649, 0, 0, 0, 1651, 0, 0, + 0, 0, 0, 0, 1653, 0, 0, 1655, + 0, 0, 1657, 0, 0, 0, 1659, 0, + 0, 1661, 0, 0, 5, 1663, 0, 0, + 1665, 4471, 0, 1669, 0, 0, 0, 0, + 1671, 0, 0, 1673, 0, 0, 0, 1675, + 0, 0, 1677, 0, 1679, 1681, 0, 1683, + 0, 0, 0, 1685, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1687, 1689, 0, 0, 0, 1691, 0, 1693, + 0, 0, 0, 1695, 0, 1697, 1699, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1701, 0, 0, 0, 1703, 0, 1705, 0, + 0, 1707, 0, 1709, 1711, 0, 0, 0, + 0, 0, 0, 1713, 0, 1715, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1717, 0, 0, 0, 1719, 0, 0, 1721, + 0, 0, 0, 0, 1723, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1727, 0, 0, 0, 0, 1729, + 0, 0, 1731, 0, 0, 1733, 0, 0, + 0, 0, 0, 0, 0, 1735, 0, 0, + 1737, 0, 0, 0, 0, 1739, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1741, 0, 0, 1743, 0, + 0, 0, 0, 0, 0, 0, 1745, 0, + 0, 0, 1747, 0, 1749, 0, 1751, 0, + 1753, 0, 0, 0, 0, 0, 0, 1755, + 0, 0, 1757, 0, 0, 0, 0, 0, + 1759, 1761, 1763, 0, 0, 0, 0, 0, + 0, 0, 1765, 0, 1767, 0, 0, 0, + 0, 1769, 0, 0, 0, 0, 0, 1773, + 0, 0, 0, 0, 1775, 0, 0, 0, + 0, 0, 1777, 1779, 0, 0, 0, 0, + 1781, 1783, 0, 0, 0, 0, 0, 0, + 1785, 0, 0, 0, 0, 1787, 0, 0, + 0, 0, 1789, 1791, 1793, 0, 0, 0, + 0, 1795, 0, 0, 1797, 1799, 0, 0, + 0, 5, 4473, 0, 0, 0, 0, 0, + 0, 1803, 0, 0, 1805, 0, 0, 1807, + 0, 0, 0, 0, 0, 1809, 0, 0, + 1811, 0, 0, 0, 0, 0, 0, 0, + 0, 1813, 0, 1815, 1817, 0, 0, 0, + 0, 0, 1819, 0, 0, 0, 1821, 0, + 0, 0, 0, 1823, 0, 0, 0, 1825, + 0, 0, 0, 0, 0, 1827, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1829, 0, 0, 0, 0, 0, 0, + 0, 0, 1831, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1833, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1835, 0, 0, 0, 0, 1837, 0, 0, + 0, 0, 0, 0, 0, 1839, 0, 0, + 0, 0, 1841, 0, 0, 1843, 0, 0, + 0, 0, 0, 1845, 1847, 0, 1849, 0, + 0, 0, 1851, 0, 0, 0, 0, 1853, + 0, 1855, 0, 1857, 0, 0, 0, 0, + 1859, 0, 0, 1861, 0, 0, 0, 0, + 0, 1863, 0, 0, 0, 1865, 0, 0, + 0, 0, 0, 1867, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1869, 0, 1871, 0, 0, 0, + 0, 0, 0, 0, 0, 1875, 0, 0, + 0, 0, 0, 0, 0, 1877, 0, 1879, + 0, 0, 0, 0, 1883, 1885, 0, 0, + 1887, 1889, 0, 0, 0, 0, 1891, 1893, + 1895, 0, 0, 0, 0, 0, 1899, 0, + 0, 0, 1901, 1903, 0, 0, 0, 0, + 0, 0, 0, 0, 1905, 1907, 1909, 0, + 0, 0, 1911, 0, 0, 0, 0, 0, + 1913, 0, 0, 1915, 0, 0, 0, 0, + 1917, 1919, 0, 0, 1925, 0, 0, 1921, + 1923, 0, 0, 1927, 0, 1929, 0, 0, + 0, 0, 1931, 0, 1933, 0, 0, 0, + 0, 1935, 0, 0, 1937, 0, 0, 1939, + 0, 1941, 0, 0, 0, 0, 1943, 1945, + 0, 0, 0, 0, 0, 0, 0, 0, + 1947, 0, 0, 0, 1949, 0, 0, 0, + 1951, 0, 0, 0, 0, 0, 0, 0, + 1953, 0, 0, 0, 1955, 0, 0, 0, + 0, 0, 1957, 0, 0, 1959, 0, 1961, + 0, 0, 1963, 0, 0, 0, 0, 0, + 1965, 0, 0, 0, 0, 1967, 0, 0, + 1969, 0, 0, 0, 0, 1971, 0, 0, + 1973, 0, 1975, 0, 0, 1977, 0, 0, + 0, 0, 1983, 0, 0, 0, 0, 1985, + 0, 0, 1987, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1989, 0, 0, 0, + 0, 0, 0, 0, 0, 1991, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1993, 0, 1995, + 0, 0, 0, 0, 1997, 0, 0, 0, + 0, 0, 0, 1999, 0, 0, 0, 2001, + 0, 0, 2003, 2005, 0, 0, 0, 2007, + 0, 0, 0, 2009, 0, 0, 0, 0, + 2011, 0, 0, 2013, 0, 0, 2015, 0, + 0, 2017, 0, 0, 0, 2019, 0, 0, + 0, 0, 2021, 2023, 0, 2025, 0, 0, + 0, 0, 0, 0, 2027, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2031, 2035, 2037, + 2039, 0, 0, 2041, 2043, 0, 0, 0, + 2047, 2049, 0, 2051, 0, 0, 2053, 2055, + 0, 2057, 0, 2059, 0, 0, 2061, 0, + 0, 2063, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 0, 2065, 0, 2067, 0, 0, + 0, 0, 0, 0, 2069, 0, 0, 2071, + 0, 2073, 2075, 0, 0, 0, 0, 2077, + 0, 0, 0, 0, 2079, 2081, 0, 0, + 2083, 2085, 0, 0, 0, 2087, 2089, 0, + 0, 2091, 0, 0, 0, 0, 2093, 2095, + 0, 0, 0, 0, 2097, 0, 0, 2099, + 0, 2101, 0, 2103, 2105, 0, 0, 2107, + 0, 2109, 2111, 0, 2113, 0, 0, 0, + 2115, 0, 0, 2117, 2119, 0, 0, 0, + 2121, 2123, 2125, 0, 0, 0, 0, 2127, + 0, 2129, 0, 0, 0, 0, 2131, 2133, + 0, 2135, 0, 2137, 0, 0, 2139, 0, + 0, 2141, 0, 0, 0, 2143, 0, 0, + 0, 2145, 0, 2147, 0, 0, 2149, 2151, + 4475, 0, 0, 2155, 0, 2157, 0, 0, + 2159, 0, 0, 0, 2161, 0, 0, 0, + 0, 2163, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2165, 0, 2167, + 0, 0, 2169, 0, 0, 0, 0, 0, + 0, 2171, 0, 0, 0, 0, 2173, 0, + 0, 0, 2175, 0, 0, 2177, 0, 0, + 0, 0, 0, 0, 0, 0, 2179, 0, + 2181, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2183, 0, 0, + 0, 0, 0, 0, 0, 2185, 0, 2187, + 0, 0, 0, 2189, 0, 0, 0, 0, + 2191, 2193, 0, 0, 0, 0, 2195, 2197, + 0, 0, 2199, 0, 0, 0, 2201, 0, + 0, 0, 0, 0, 0, 2203, 0, 0, + 0, 2205, 0, 0, 0, 2207, 0, 0, + 0, 2209, 0, 2211, 0, 0, 0, 0, + 0, 0, 0, 2213, 0, 0, 0, 0, + 2215, 0, 0, 0, 0, 0, 0, 0, + 2217, 0, 0, 0, 2219, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2221, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2223, 0, 2225, 0, 0, 0, + 2227, 0, 0, 0, 0, 2229, 0, 0, + 0, 2231, 0, 0, 0, 2233, 0, 0, + 0, 0, 0, 2235, 0, 0, 0, 2237, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2241, 0, 0, 0, 0, + 2245, 0, 0, 0, 2247, 0, 0, 0, + 0, 2251, 2253, 0, 0, 0, 0, 2257, + 0, 0, 0, 0, 0, 0, 0, 2259, + 0, 2261, 0, 0, 0, 2263, 0, 0, + 2265, 0, 0, 0, 2267, 0, 0, 0, + 0, 0, 0, 0, 2269, 0, 0, 0, + 2271, 0, 0, 0, 2273, 0, 0, 0, + 2275, 0, 2277, 0, 2279, 0, 0, 2281, + 2283, 0, 0, 0, 0, 0, 0, 0, + 2285, 0, 0, 2287, 0, 0, 0, 2289, + 0, 0, 0, 2291, 2293, 0, 0, 0, + 0, 0, 0, 2295, 0, 0, 0, 0, + 0, 2297, 0, 0, 0, 2299, 0, 0, + 0, 0, 2301, 0, 0, 0, 2303, 0, + 0, 0, 0, 0, 2305, 0, 0, 2307, + 0, 2309, 0, 2311, 0, 0, 0, 2313, + 0, 0, 0, 0, 0, 0, 0, 2317, + 0, 2319, 0, 0, 0, 0, 2321, 0, + 0, 2323, 2325, 0, 2327, 2329, 2331, 0, + 0, 0, 0, 2333, 0, 0, 0, 0, + 2335, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2339, 2341, 0, 2343, + 0, 0, 0, 2345, 0, 0, 2347, 0, + 0, 0, 2349, 0, 0, 0, 2351, 0, + 0, 0, 2353, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2355, 0, + 2357, 0, 0, 0, 0, 0, 2359, 2361, + 0, 2363, 0, 0, 0, 0, 2365, 0, + 0, 2367, 0, 0, 2369, 0, 0, 2371, + 0, 0, 2373, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 2375, + 0, 2377, 0, 0, 0, 2379, 0, 0, + 0, 2381, 2383, 0, 2385, 0, 0, 2387, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2389, 0, 0, 0, + 0, 0, 2391, 0, 0, 0, 2393, 0, + 0, 0, 2395, 0, 2397, 0, 0, 2399, + 0, 2401, 2403, 0, 0, 0, 2407, 0, + 0, 0, 0, 0, 0, 0, 2409, 0, + 0, 2411, 0, 2413, 0, 2415, 0, 2417, + 0, 2419, 0, 0, 2421, 0, 2423, 2425, + 0, 0, 0, 0, 2427, 2429, 0, 2431, + 0, 0, 0, 0, 0, 2433, 0, 0, + 2435, 0, 0, 0, 0, 0, 2437, 2439, + 0, 0, 2441, 0, 0, 0, 2443, 2445, + 0, 0, 0, 0, 0, 0, 0, 2447, + 0, 0, 0, 0, 2449, 0, 2451, 0, + 2453, 2455, 0, 0, 0, 0, 0, 2457, + 0, 0, 2459, 0, 2461, 0, 0, 0, + 0, 0, 2463, 0, 0, 0, 0, 2465, + 0, 2467, 2469, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2471, 0, 0, 0, 0, 2473, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2475, 0, 2477, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2479, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2481, 0, 2483, 0, 0, 0, + 0, 0, 0, 0, 2485, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2487, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2489, 2491, 2493, 0, 0, 2495, 0, 0, + 0, 0, 2497, 2499, 0, 0, 0, 0, + 0, 2501, 0, 0, 2503, 0, 2505, 0, + 2507, 2509, 0, 0, 2511, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2513, + 0, 0, 2515, 0, 0, 0, 0, 0, + 2517, 0, 0, 0, 2519, 0, 0, 2521, + 0, 0, 2523, 0, 0, 0, 0, 0, + 0, 2525, 0, 0, 0, 2527, 2529, 2531, + 0, 2533, 0, 0, 0, 0, 0, 2535, + 2537, 0, 2539, 0, 0, 2541, 0, 0, + 2543, 2545, 0, 0, 0, 0, 0, 0, + 2547, 0, 0, 0, 0, 0, 2549, 0, + 0, 0, 2551, 0, 0, 2553, 0, 0, + 0, 0, 0, 2555, 0, 0, 0, 2557, + 0, 0, 0, 0, 2559, 0, 0, 0, + 0, 2561, 0, 2563, 0, 0, 0, 0, + 2565, 2567, 0, 2569, 0, 2571, 0, 0, + 2573, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2575, 0, 2577, 0, + 0, 2579, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2581, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2583, 0, 0, 0, 0, 0, 2585, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2587, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2589, 0, 0, + 0, 0, 2591, 0, 0, 0, 0, 2593, + 2595, 0, 0, 2597, 0, 0, 0, 0, + 2599, 0, 0, 0, 0, 2601, 0, 0, + 2603, 2605, 0, 0, 0, 0, 0, 2607, + 2609, 0, 0, 2611, 0, 0, 2613, 0, + 0, 0, 0, 0, 0, 0, 2615, 0, + 0, 0, 0, 0, 2617, 0, 0, 2619, + 0, 2621, 2623, 0, 0, 2625, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2627, + 0, 2629, 2631, 0, 2633, 0, 0, 2635, + 2637, 0, 0, 2639, 0, 2641, 0, 2643, + 0, 0, 0, 2645, 4477, 0, 0, 2649, + 0, 2651, 0, 0, 2653, 0, 0, 0, + 2655, 0, 0, 0, 2657, 0, 0, 0, + 2659, 0, 0, 0, 0, 2661, 0, 0, + 0, 0, 2663, 2665, 0, 0, 2667, 2669, + 0, 0, 0, 0, 0, 0, 0, 2671, + 0, 0, 0, 2673, 0, 0, 0, 0, + 0, 0, 0, 0, 2675, 0, 2677, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2679, 0, 0, 0, 0, 0, 0, 0, + 2683, 2685, 0, 0, 0, 2687, 2689, 0, + 0, 0, 2691, 0, 0, 0, 0, 0, + 0, 2693, 0, 0, 0, 2695, 0, 2697, + 0, 0, 0, 2699, 0, 0, 0, 0, + 0, 2701, 2703, 0, 0, 0, 2705, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2707, 0, 2709, 0, 2711, 0, + 0, 0, 0, 0, 2715, 0, 0, 0, + 0, 0, 2717, 0, 0, 2719, 0, 0, + 0, 0, 2723, 0, 0, 2725, 2727, 0, + 2729, 0, 0, 0, 2731, 0, 2733, 0, + 0, 0, 0, 2735, 0, 0, 0, 0, + 0, 2737, 0, 2739, 2741, 0, 0, 0, + 2743, 0, 0, 0, 2745, 2747, 0, 0, + 0, 0, 0, 0, 0, 2749, 0, 0, + 2751, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2753, 2755, 0, 2757, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2759, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2761, 2763, 2765, 0, + 2767, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2769, 0, 0, 0, 0, 0, + 2771, 0, 0, 0, 2773, 0, 0, 0, + 0, 0, 0, 0, 2775, 0, 0, 0, + 2777, 0, 2779, 2781, 0, 0, 0, 0, + 2783, 0, 2785, 0, 2787, 0, 0, 0, + 2789, 0, 0, 2791, 0, 0, 2793, 0, + 2795, 0, 0, 0, 0, 0, 2799, 0, + 2801, 0, 0, 0, 0, 0, 0, 0, + 2803, 0, 0, 2805, 0, 0, 0, 2807, + 0, 0, 2809, 0, 0, 0, 2811, 0, + 2813, 2815, 0, 0, 0, 2817, 2819, 0, + 0, 0, 0, 0, 0, 0, 0, 2821, + 0, 0, 0, 0, 2823, 2825, 0, 0, + 2827, 0, 0, 2829, 0, 0, 0, 2831, + 0, 0, 0, 0, 2833, 0, 2835, 0, + 0, 0, 2837, 0, 2839, 0, 2841, 0, + 0, 0, 0, 2843, 2845, 0, 0, 2847, + 0, 0, 2849, 0, 0, 0, 0, 2851, + 2853, 0, 0, 2855, 2857, 0, 2859, 0, + 0, 0, 0, 0, 2861, 0, 0, 2863, + 0, 0, 2865, 2867, 0, 0, 2869, 0, + 2871, 2873, 0, 0, 2875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2877, 2879, + 0, 0, 2881, 0, 2883, 2885, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2887, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2889, 2891, 0, 0, 2893, 0, + 0, 0, 0, 2895, 2897, 0, 2899, 0, + 0, 2901, 2903, 0, 0, 2905, 0, 2907, + 0, 0, 2909, 0, 5, 0, 2911, 4479, + 0, 2915, 0, 0, 0, 2917, 0, 0, + 2919, 0, 0, 0, 2921, 2923, 2925, 0, + 2927, 0, 0, 0, 0, 2929, 2931, 2933, + 0, 0, 0, 0, 2935, 0, 0, 0, + 0, 0, 0, 0, 2937, 0, 2939, 2941, + 0, 0, 0, 0, 2943, 2945, 0, 0, + 0, 0, 2947, 2949, 0, 2951, 0, 0, + 2953, 0, 0, 0, 0, 0, 0, 2955, + 0, 0, 2957, 0, 0, 2959, 2961, 0, + 0, 0, 0, 0, 0, 0, 0, 2963, + 0, 0, 2965, 0, 2967, 0, 0, 0, + 0, 0, 0, 0, 2969, 0, 0, 0, + 0, 0, 2971, 2973, 2975, 0, 0, 0, + 0, 0, 0, 0, 2977, 0, 0, 0, + 0, 0, 0, 0, 2979, 0, 2981, 0, + 2983, 0, 2985, 0, 0, 2987, 0, 0, + 2989, 0, 0, 0, 0, 0, 2991, 0, + 2993, 0, 0, 0, 2995, 0, 0, 0, + 2997, 2999, 0, 0, 3001, 0, 0, 3003, + 0, 3005, 0, 3007, 0, 0, 3009, 3011, + 0, 0, 0, 3013, 3015, 0, 0, 3017, + 0, 0, 3019, 0, 3021, 0, 0, 0, + 0, 0, 3023, 0, 0, 0, 0, 3027, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3029, 0, 0, 3031, 0, + 0, 0, 0, 3033, 0, 0, 3035, 3037, + 0, 3039, 0, 0, 0, 0, 3041, 0, + 3043, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3045, 0, 0, + 0, 3047, 0, 3049, 0, 0, 0, 3051, + 0, 0, 3053, 3055, 0, 0, 0, 0, + 3057, 0, 0, 0, 0, 0, 3059, 3061, + 3063, 0, 0, 0, 3065, 0, 0, 0, + 0, 3067, 0, 0, 0, 3069, 0, 0, + 3071, 0, 0, 0, 0, 0, 3073, 0, + 0, 0, 0, 3075, 3077, 0, 0, 3079, + 0, 0, 0, 3081, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3083, 0, + 0, 0, 0, 0, 0, 3087, 0, 0, + 0, 3089, 0, 3093, 0, 0, 0, 0, + 0, 0, 0, 3095, 0, 0, 0, 3097, + 0, 3099, 0, 3101, 0, 0, 0, 3103, + 0, 0, 0, 3105, 0, 0, 0, 0, + 3107, 3109, 0, 0, 0, 0, 3111, 0, + 0, 0, 3115, 0, 0, 0, 0, 3117, + 3119, 0, 0, 3121, 0, 0, 0, 0, + 0, 0, 3123, 0, 0, 0, 3125, 0, + 0, 0, 3127, 0, 0, 3129, 3131, 0, + 0, 0, 0, 0, 3133, 0, 0, 3135, + 0, 0, 0, 0, 0, 0, 3137, 3139, + 0, 0, 3141, 0, 0, 3143, 0, 0, + 0, 0, 3145, 0, 0, 3147, 0, 0, + 3149, 3151, 0, 0, 0, 0, 0, 0, + 0, 0, 3153, 3155, 0, 0, 0, 0, + 3157, 0, 0, 3159, 0, 0, 0, 3165, + 0, 3167, 0, 0, 0, 0, 3169, 3171, + 0, 0, 0, 0, 3173, 0, 0, 0, + 0, 3177, 0, 0, 0, 0, 0, 0, + 0, 3181, 0, 0, 3183, 0, 0, 0, + 0, 0, 3187, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3189, 5, 0, 0, 4481, 0, 0, 0, + 3193, 0, 0, 0, 3195, 3197, 3199, 0, + 3201, 0, 0, 0, 0, 0, 0, 0, + 0, 3203, 0, 0, 3205, 0, 0, 3207, + 3209, 0, 0, 0, 3211, 0, 3213, 0, + 0, 0, 3215, 0, 3217, 0, 0, 0, + 3219, 0, 0, 3221, 3223, 0, 0, 0, + 0, 0, 0, 0, 0, 3225, 3227, 0, + 0, 0, 0, 0, 0, 3229, 0, 3231, + 0, 3233, 0, 3235, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3237, + 3239, 0, 0, 3241, 0, 0, 3243, 3245, + 3247, 0, 0, 0, 3251, 0, 0, 3253, + 3255, 0, 0, 0, 0, 0, 0, 0, + 0, 3257, 0, 3259, 0, 0, 3263, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3265, 0, 3267, 0, 0, 3269, 3271, 0, + 3273, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3275, 0, 0, 0, 0, + 0, 0, 3277, 0, 3279, 0, 0, 0, + 0, 0, 0, 0, 0, 3281, 0, 0, + 3283, 0, 0, 3285, 0, 0, 3287, 0, + 0, 3289, 0, 3291, 0, 0, 0, 3293, + 0, 3295, 0, 0, 3297, 0, 0, 0, + 3299, 0, 0, 0, 0, 0, 0, 3301, + 0, 0, 0, 3303, 0, 0, 0, 3305, + 3307, 0, 0, 3309, 0, 0, 3311, 0, + 0, 0, 3313, 0, 0, 0, 3315, 3317, + 0, 0, 0, 0, 3319, 0, 0, 0, + 0, 0, 0, 0, 3321, 0, 0, 3323, + 0, 0, 3325, 0, 0, 0, 0, 3327, + 0, 0, 3329, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3331, + 0, 0, 3333, 0, 0, 3335, 0, 0, + 3337, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3341, 0, 3343, 0, + 0, 0, 3345, 0, 0, 0, 3347, 0, + 0, 3349, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3351, 0, 0, 3353, 0, + 0, 3355, 0, 0, 0, 0, 0, 3357, + 0, 3359, 0, 0, 0, 3361, 3363, 0, + 3365, 0, 0, 0, 3369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3371, 3373, 0, 0, 3375, 3377, 0, 3379, + 0, 3381, 0, 3383, 0, 3385, 0, 0, + 3387, 0, 3389, 3391, 0, 0, 0, 0, + 3393, 0, 3395, 0, 0, 0, 0, 3397, + 0, 0, 0, 0, 0, 3399, 0, 0, + 3401, 0, 0, 0, 0, 0, 3403, 3405, + 0, 0, 3407, 0, 0, 0, 3409, 3411, + 0, 0, 0, 0, 0, 0, 0, 3413, + 0, 0, 0, 0, 3415, 0, 3417, 0, + 3419, 3421, 0, 0, 0, 0, 0, 3423, + 0, 0, 0, 0, 3425, 0, 0, 3427, + 0, 3429, 0, 3431, 0, 0, 0, 0, + 3433, 0, 0, 0, 0, 0, 3435, 0, + 0, 0, 3437, 3439, 0, 3441, 0, 0, + 0, 0, 0, 0, 3445, 0, 0, 0, + 3447, 3449, 0, 0, 0, 0, 0, 3451, + 3453, 0, 3455, 3457, 0, 3459, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3461, 0, 0, + 0, 0, 3463, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3465, 0, + 3467, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3469, 0, 0, 0, 0, + 0, 0, 0, 3471, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3473, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3475, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3477, 0, 3479, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3481, 0, + 0, 0, 0, 0, 3483, 0, 0, 3485, + 3487, 0, 0, 0, 0, 3489, 0, 0, + 0, 0, 3491, 0, 0, 0, 3493, 0, + 0, 0, 0, 0, 0, 0, 3495, 0, + 3497, 0, 0, 3499, 0, 0, 0, 0, + 3501, 3503, 0, 0, 3505, 0, 0, 0, + 0, 3507, 0, 0, 0, 3509, 0, 0, + 3511, 0, 0, 0, 0, 0, 3513, 0, + 0, 0, 3515, 0, 0, 0, 0, 0, + 0, 0, 3517, 0, 3519, 3521, 0, 0, + 3523, 0, 3525, 0, 3527, 0, 0, 0, + 0, 0, 0, 3529, 0, 0, 0, 3531, + 0, 3533, 0, 0, 0, 3535, 3537, 0, + 0, 0, 3539, 0, 0, 0, 0, 0, + 3541, 3543, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3545, 0, 0, 0, 3547, 3549, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3551, 0, 0, 3553, 0, 0, 3555, 0, + 0, 3557, 3559, 0, 0, 0, 3561, 0, + 0, 3563, 0, 0, 0, 3565, 0, 3567, + 0, 0, 3569, 0, 0, 0, 0, 0, + 3571, 0, 0, 3573, 3575, 0, 0, 3577, + 0, 0, 3579, 3581, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3583, 0, 0, + 0, 0, 3585, 3587, 0, 0, 3589, 0, + 0, 3593, 0, 0, 0, 3595, 0, 0, + 0, 0, 0, 0, 3597, 3599, 0, 3601, + 0, 3603, 0, 0, 0, 3605, 0, 0, + 0, 0, 0, 0, 3607, 0, 0, 0, + 0, 3609, 3611, 0, 0, 0, 0, 0, + 0, 3613, 0, 0, 0, 0, 0, 0, + 0, 3615, 0, 0, 0, 0, 3619, 0, + 0, 3621, 3623, 3625, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3627, 3629, 0, + 3631, 3633, 0, 3635, 3637, 0, 3639, 0, + 3641, 0, 0, 0, 3643, 0, 0, 0, + 3645, 0, 0, 0, 3647, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3649, 0, 0, 3651, + 0, 0, 0, 0, 0, 3653, 0, 0, + 3655, 0, 3657, 3659, 0, 3661, 0, 3663, + 0, 0, 0, 0, 0, 0, 3665, 3667, + 0, 3669, 0, 0, 3671, 0, 3673, 0, + 0, 0, 0, 0, 3675, 0, 0, 0, + 3677, 3679, 0, 0, 0, 0, 0, 0, + 3681, 0, 3683, 0, 3685, 0, 3687, 0, + 0, 0, 3689, 0, 0, 3691, 0, 0, + 3693, 0, 0, 3695, 3697, 0, 0, 3699, + 0, 0, 3701, 0, 0, 3703, 3705, 0, + 0, 0, 0, 0, 3707, 3709, 3711, 0, + 0, 0, 3713, 0, 0, 0, 0, 0, + 3715, 0, 0, 0, 3717, 0, 0, 0, + 3719, 0, 0, 0, 3721, 0, 0, 0, + 3723, 0, 3725, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3727, 0, 0, 3729, 0, 3731, 0, + 0, 0, 0, 0, 3733, 0, 0, 0, + 0, 0, 0, 0, 0, 3735, 0, 0, + 3737, 3739, 0, 0, 0, 3741, 0, 0, + 0, 3743, 0, 0, 3745, 3747, 0, 0, + 0, 3749, 0, 0, 0, 3751, 0, 0, + 0, 0, 3753, 0, 0, 0, 3755, 0, + 3757, 0, 0, 3759, 0, 3761, 0, 3763, + 0, 0, 3765, 3767, 0, 3769, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3771, 0, 0, 0, 0, 0, 0, 3773, + 0, 3775, 0, 0, 0, 0, 0, 0, + 0, 0, 3777, 0, 0, 3779, 0, 0, + 3781, 0, 0, 3783, 3785, 0, 3787, 0, + 0, 0, 3795, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3797, 0, 0, 0, + 3799, 0, 0, 3801, 3803, 0, 0, 0, + 3805, 0, 0, 0, 0, 3807, 0, 3809, + 0, 0, 0, 3811, 0, 0, 0, 3813, + 0, 0, 3815, 3817, 0, 0, 0, 3819, + 0, 0, 0, 0, 3821, 0, 0, 0, + 3823, 0, 3825, 0, 0, 3827, 0, 3829, + 0, 3831, 0, 0, 3833, 3835, 0, 0, + 0, 0, 0, 3837, 0, 0, 0, 0, + 3839, 3841, 0, 0, 3843, 0, 0, 0, + 3845, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3849, 3851, + 0, 0, 3853, 0, 0, 0, 0, 0, + 0, 3855, 0, 0, 0, 3857, 3859, 0, + 0, 3861, 0, 0, 0, 0, 3863, 0, + 3865, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3867, 0, 0, 0, 3869, 0, + 3871, 0, 0, 0, 0, 3873, 3875, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3877, 0, 0, 3879, 0, 0, 3881, + 0, 0, 0, 3883, 0, 0, 3885, 0, + 0, 0, 0, 0, 0, 0, 3889, 0, + 5, 4483, 3893, 0, 0, 3895, 3897, 0, + 3899, 0, 0, 0, 0, 3901, 3903, 0, + 0, 0, 0, 0, 3905, 0, 0, 3907, + 3909, 0, 0, 0, 3911, 0, 3913, 0, + 0, 0, 0, 3915, 0, 0, 0, 0, + 0, 3917, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3919, 0, 0, + 0, 0, 0, 0, 0, 3921, 0, 0, + 0, 3923, 0, 0, 3925, 3927, 0, 0, + 0, 0, 3929, 0, 0, 3931, 0, 0, + 3933, 3935, 0, 0, 0, 0, 3937, 0, + 0, 0, 3939, 0, 3941, 0, 0, 0, + 3943, 0, 0, 0, 0, 0, 3945, 0, + 0, 0, 0, 0, 3947, 3949, 0, 0, + 3951, 0, 0, 0, 3953, 0, 0, 0, + 0, 3955, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3957, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3959, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3961, + 0, 0, 3963, 0, 0, 0, 0, 0, + 0, 3967, 0, 0, 0, 0, 3969, 0, + 0, 3971, 0, 0, 0, 0, 3975, 0, + 0, 0, 0, 0, 3977, 0, 0, 0, + 3979, 0, 0, 3981, 0, 0, 0, 0, + 0, 3983, 3985, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3989, 3991, 0, 0, + 3993, 0, 0, 0, 0, 0, 0, 3995, + 0, 0, 3997, 0, 0, 3999, 0, 0, + 4001, 0, 0, 0, 0, 4003, 0, 0, + 0, 0, 4007, 0, 4009, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4011, + 0, 0, 0, 0, 0, 0, 0, 0, + 4013, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4015, 0, 0, 0, + 0, 4017, 0, 0, 4019, 0, 4021, 0, + 0, 4023, 0, 0, 4025, 0, 0, 0, + 0, 0, 0, 0, 4027, 0, 0, 0, + 0, 0, 0, 0, 4029, 0, 0, 4031, + 0, 0, 4033, 0, 0, 4035, 0, 0, + 4037, 0, 0, 4039, 0, 0, 0, 0, + 0, 4041, 0, 0, 0, 4043, 0, 4045, + 0, 4047, 0, 0, 0, 0, 4049, 0, + 0, 0, 0, 0, 0, 4053, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4055, + 0, 0, 4057, 0, 4059, 0, 0, 0, + 4061, 0, 0, 0, 0, 0, 4063, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4065, 0, 0, 0, + 0, 4067, 0, 0, 0, 0, 0, 0, + 4069, 0, 0, 0, 0, 4071, 4073, 0, + 0, 0, 0, 4075, 4077, 0, 0, 4079, + 0, 0, 0, 0, 0, 4081, 0, 0, + 0, 0, 0, 0, 0, 0, 4083, 0, + 4085, 0, 0, 0, 0, 0, 0, 4087, + 0, 4089, 0, 0, 0, 0, 0, 4091, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4093, 0, 0, 0, 0, + 4095, 0, 4097, 0, 0, 0, 4099, 0, + 0, 0, 4101, 0, 0, 0, 0, 4103, + 0, 4105, 0, 0, 0, 4107, 0, 0, + 0, 0, 4109, 4111, 0, 4113, 0, 0, + 0, 4115, 0, 0, 0, 0, 4117, 4119, + 0, 0, 4121, 0, 0, 0, 4123, 0, + 0, 0, 4125, 0, 0, 0, 4127, 0, + 0, 0, 0, 0, 4129, 4131, 0, 0, + 0, 4133, 4135, 0, 0, 0, 0, 0, + 4137, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4139, 0, 0, 0, 0, + 0, 0, 4141, 0, 4143, 0, 4145, 0, + 0, 0, 4147, 0, 4149, 0, 0, 4151, + 4153, 4155, 0, 0, 0, 0, 4157, 0, + 0, 4159, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4161, 0, 0, 4163, + 0, 4165, 0, 0, 0, 4167, 0, 4169, + 0, 0, 0, 0, 4171, 0, 0, 4173, + 4175, 0, 0, 0, 0, 4177, 0, 0, + 4179, 0, 0, 4181, 0, 0, 4183, 0, + 0, 0, 0, 0, 4185, 0, 0, 4187, + 0, 0, 4189, 0, 0, 0, 4191, 0, + 0, 0, 0, 4193, 0, 0, 4195, 0, + 0, 0, 4197, 0, 0, 0, 4199, 0, + 0, 0, 0, 0, 4201, 0, 0, 4203, + 0, 0, 4205, 0, 0, 0, 0, 4207, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4211, 0, 0, + 0, 0, 4213, 4215, 0, 0, 4219, 0, + 0, 4221, 0, 0, 4223, 0, 0, 4225, + 0, 0, 0, 4227, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4231, 0, 0, 0, 0, + 0, 4233, 4235, 0, 0, 4237, 0, 0, + 0, 0, 0, 4239, 0, 4241, 0, 4243, + 0, 0, 4245, 0, 0, 0, 0, 0, + 4247, 0, 0, 4249, 0, 0, 4251, 0, + 0, 4253, 0, 4255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4257, 7, 4259, 9, 4261, 11, 4263, 15, + 4265, 21, 4267, 35, 4269, 41, 4271, 43, + 4273, 71, 4275, 83, 4277, 247, 4279, 249, + 4281, 253, 4283, 261, 4285, 289, 4287, 313, + 4289, 383, 4291, 385, 4293, 393, 4295, 425, + 4297, 459, 4299, 731, 4301, 737, 4303, 739, + 4305, 747, 4307, 765, 4309, 767, 4311, 771, + 4313, 819, 4315, 829, 4317, 995, 4319, 1039, + 4321, 1049, 4323, 1057, 4325, 1109, 4327, 1169, + 4329, 1203, 4331, 1213, 4333, 1215, 4335, 1219, + 4337, 1225, 4339, 1237, 4341, 1305, 4343, 1315, + 4345, 1317, 4347, 1525, 4349, 1577, 4351, 1587, + 4353, 1591, 0, 4355, 1667, 0, 4357, 1725, + 4359, 1771, 4361, 1801, 0, 4363, 1873, 4365, + 1881, 4367, 1897, 4369, 1979, 4371, 1981, 4373, + 2029, 4375, 2033, 4377, 2045, 4379, 2153, 0, + 0, 0, 0, 0, 4381, 2239, 4383, 2243, + 4385, 2249, 4387, 2255, 4389, 2315, 4391, 2337, + 4393, 2405, 4395, 2647, 0, 0, 0, 0, + 0, 0, 0, 4397, 2681, 4399, 2713, 4401, + 2721, 4403, 2797, 4405, 2913, 0, 0, 4407, + 3025, 4409, 3085, 4411, 3091, 4413, 3113, 4415, + 3161, 4417, 3163, 4419, 3175, 4421, 3179, 4423, + 3185, 4425, 3191, 0, 4427, 3249, 4429, 3261, + 4431, 3339, 4433, 3367, 4435, 3443, 4437, 3591, + 4439, 3617, 4441, 3789, 4443, 3791, 4445, 3793, + 4447, 3847, 4449, 3887, 4451, 3891, 0, 0, + 4453, 3965, 4455, 3973, 4457, 3987, 4459, 4005, + 4461, 4051, 4463, 4209, 4465, 4217, 4467, 4229 +}; + +static const short _char_ref_to_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 +}; + +static const short _char_ref_from_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 +}; + +static const short _char_ref_eof_trans[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4396, 4396, 4396, 4396, + 4396, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4538, 4538, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4819, 4819, 4819, 4819, + 4819, 4819, 4819, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5497, 5497, + 5497, 5497, 5497, 5497, 5497, 5497, 5497, 5497, + 5497, 5497, 5497, 5497, 5497, 5497, 5497, 5497, + 5497, 5497, 5497, 5497, 5497, 5497, 5497, 5497, + 5497, 5497, 5497, 5497, 5497, 5497, 5497, 5497, + 5497, 5497, 5497, 5497, 5497, 5497, 5497, 5497, + 5497, 5497, 5497, 5497, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6541, 6541, 6541, 6541, 6541, + 6541, 6541, 6541, 6541, 6541, 6541, 6541, 6541, + 6541, 6541, 6541, 6541, 6541, 6541, 6541, 6541, + 6541, 6541, 6541, 6541, 6541, 6541, 6541, 6541, + 6541, 6541, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7048, 7048, 7048, 7048, + 7048, 7048, 7048, 7048, 7048, 7048, 7048, 7048, + 7048, 7048, 7048, 7048, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7549, 7549, 7549, 7549, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8898, 8898, 8898, + 8898, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 9737, 9739, 9741, 9743, 9745, 9747, 9749, 9751, + 9753, 9755, 9757, 9759, 9761, 9763, 9765, 9767, + 9769, 9771, 9773, 9775, 9777, 9779, 9781, 9783, + 9785, 9787, 9789, 9791, 9793, 9795, 9797, 9799, + 9801, 9803, 9805, 9807, 9809, 9811, 9813, 9815, + 9817, 9819, 9821, 9823, 9825, 9827, 9829, 9831, + 9833, 9836, 9839, 9841, 9843, 9846, 9848, 9850, + 9852, 9854, 9856, 9858, 9860, 9862, 9869, 9871, + 9873, 9875, 9877, 9879, 9881, 9883, 9892, 9894, + 9896, 9898, 9900, 9904, 9906, 9908, 9910, 9912, + 9914, 9916, 9918, 9920, 9922, 9925, 9927, 9929, + 9931, 9933, 9935, 9937, 9939, 9941, 9943, 9945, + 9947, 9949, 9953, 9955, 9957, 9959, 9961, 9963, + 9965, 9967 +}; + +static const int char_ref_start = 7623; + +static const int char_ref_en_valid_named_ref = 7623; + + +#line 2469 "char_ref.rl" +// clang-format on + +static bool consume_named_ref(struct GumboInternalParser* parser, + Utf8Iterator* input, bool is_in_attribute, OneOrTwoCodepoints* output) { + assert(output->first == kGumboNoChar); + const char* p = utf8iterator_get_char_pointer(input); + const char* pe = utf8iterator_get_end_pointer(input); + const char* eof = pe; + const char* te = 0; + const char *ts, *start; + int cs, act; + +// clang-format off + +#line 13985 "char_ref.c" + { + cs = char_ref_start; + ts = 0; + te = 0; + act = 0; + } + +#line 2484 "char_ref.rl" + // Avoid unused variable warnings. + (void) act; + (void) ts; + (void) char_ref_en_valid_named_ref; + + start = p; + +#line 14001 "char_ref.c" + { + int _slen; + int _trans; + const short *_acts; + unsigned int _nacts; + const char *_keys; + const short *_inds; + + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _acts = _char_ref_actions + _char_ref_from_state_actions[cs]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) { + switch ( *_acts++ ) { + case 1: +#line 1 "NONE" + {ts = p;} + break; +#line 14023 "char_ref.c" + } + } + + _keys = _char_ref_trans_keys + (cs<<1); + _inds = _char_ref_indicies + _char_ref_index_offsets[cs]; + + _slen = _char_ref_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + +_eof_trans: + cs = _char_ref_trans_targs[_trans]; + + if ( _char_ref_trans_actions[_trans] == 0 ) + goto _again; + + _acts = _char_ref_actions + _char_ref_trans_actions[_trans]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) { + switch ( *(_acts++) ) + { + case 2: +#line 1 "NONE" + {te = p+1;} + break; + case 3: +#line 233 "char_ref.rl" + {te = p+1;{ output->first = 0xc6; {p++; goto _out; } }} + break; + case 4: +#line 235 "char_ref.rl" + {te = p+1;{ output->first = 0x26; {p++; goto _out; } }} + break; + case 5: +#line 237 "char_ref.rl" + {te = p+1;{ output->first = 0xc1; {p++; goto _out; } }} + break; + case 6: +#line 239 "char_ref.rl" + {te = p+1;{ output->first = 0x0102; {p++; goto _out; } }} + break; + case 7: +#line 240 "char_ref.rl" + {te = p+1;{ output->first = 0xc2; {p++; goto _out; } }} + break; + case 8: +#line 242 "char_ref.rl" + {te = p+1;{ output->first = 0x0410; {p++; goto _out; } }} + break; + case 9: +#line 243 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d504; {p++; goto _out; } }} + break; + case 10: +#line 244 "char_ref.rl" + {te = p+1;{ output->first = 0xc0; {p++; goto _out; } }} + break; + case 11: +#line 246 "char_ref.rl" + {te = p+1;{ output->first = 0x0391; {p++; goto _out; } }} + break; + case 12: +#line 247 "char_ref.rl" + {te = p+1;{ output->first = 0x0100; {p++; goto _out; } }} + break; + case 13: +#line 248 "char_ref.rl" + {te = p+1;{ output->first = 0x2a53; {p++; goto _out; } }} + break; + case 14: +#line 249 "char_ref.rl" + {te = p+1;{ output->first = 0x0104; {p++; goto _out; } }} + break; + case 15: +#line 250 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d538; {p++; goto _out; } }} + break; + case 16: +#line 251 "char_ref.rl" + {te = p+1;{ output->first = 0x2061; {p++; goto _out; } }} + break; + case 17: +#line 252 "char_ref.rl" + {te = p+1;{ output->first = 0xc5; {p++; goto _out; } }} + break; + case 18: +#line 254 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d49c; {p++; goto _out; } }} + break; + case 19: +#line 255 "char_ref.rl" + {te = p+1;{ output->first = 0x2254; {p++; goto _out; } }} + break; + case 20: +#line 256 "char_ref.rl" + {te = p+1;{ output->first = 0xc3; {p++; goto _out; } }} + break; + case 21: +#line 258 "char_ref.rl" + {te = p+1;{ output->first = 0xc4; {p++; goto _out; } }} + break; + case 22: +#line 260 "char_ref.rl" + {te = p+1;{ output->first = 0x2216; {p++; goto _out; } }} + break; + case 23: +#line 261 "char_ref.rl" + {te = p+1;{ output->first = 0x2ae7; {p++; goto _out; } }} + break; + case 24: +#line 262 "char_ref.rl" + {te = p+1;{ output->first = 0x2306; {p++; goto _out; } }} + break; + case 25: +#line 263 "char_ref.rl" + {te = p+1;{ output->first = 0x0411; {p++; goto _out; } }} + break; + case 26: +#line 264 "char_ref.rl" + {te = p+1;{ output->first = 0x2235; {p++; goto _out; } }} + break; + case 27: +#line 265 "char_ref.rl" + {te = p+1;{ output->first = 0x212c; {p++; goto _out; } }} + break; + case 28: +#line 266 "char_ref.rl" + {te = p+1;{ output->first = 0x0392; {p++; goto _out; } }} + break; + case 29: +#line 267 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d505; {p++; goto _out; } }} + break; + case 30: +#line 268 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d539; {p++; goto _out; } }} + break; + case 31: +#line 269 "char_ref.rl" + {te = p+1;{ output->first = 0x02d8; {p++; goto _out; } }} + break; + case 32: +#line 270 "char_ref.rl" + {te = p+1;{ output->first = 0x212c; {p++; goto _out; } }} + break; + case 33: +#line 271 "char_ref.rl" + {te = p+1;{ output->first = 0x224e; {p++; goto _out; } }} + break; + case 34: +#line 272 "char_ref.rl" + {te = p+1;{ output->first = 0x0427; {p++; goto _out; } }} + break; + case 35: +#line 273 "char_ref.rl" + {te = p+1;{ output->first = 0xa9; {p++; goto _out; } }} + break; + case 36: +#line 275 "char_ref.rl" + {te = p+1;{ output->first = 0x0106; {p++; goto _out; } }} + break; + case 37: +#line 276 "char_ref.rl" + {te = p+1;{ output->first = 0x22d2; {p++; goto _out; } }} + break; + case 38: +#line 277 "char_ref.rl" + {te = p+1;{ output->first = 0x2145; {p++; goto _out; } }} + break; + case 39: +#line 278 "char_ref.rl" + {te = p+1;{ output->first = 0x212d; {p++; goto _out; } }} + break; + case 40: +#line 279 "char_ref.rl" + {te = p+1;{ output->first = 0x010c; {p++; goto _out; } }} + break; + case 41: +#line 280 "char_ref.rl" + {te = p+1;{ output->first = 0xc7; {p++; goto _out; } }} + break; + case 42: +#line 282 "char_ref.rl" + {te = p+1;{ output->first = 0x0108; {p++; goto _out; } }} + break; + case 43: +#line 283 "char_ref.rl" + {te = p+1;{ output->first = 0x2230; {p++; goto _out; } }} + break; + case 44: +#line 284 "char_ref.rl" + {te = p+1;{ output->first = 0x010a; {p++; goto _out; } }} + break; + case 45: +#line 285 "char_ref.rl" + {te = p+1;{ output->first = 0xb8; {p++; goto _out; } }} + break; + case 46: +#line 286 "char_ref.rl" + {te = p+1;{ output->first = 0xb7; {p++; goto _out; } }} + break; + case 47: +#line 287 "char_ref.rl" + {te = p+1;{ output->first = 0x212d; {p++; goto _out; } }} + break; + case 48: +#line 288 "char_ref.rl" + {te = p+1;{ output->first = 0x03a7; {p++; goto _out; } }} + break; + case 49: +#line 289 "char_ref.rl" + {te = p+1;{ output->first = 0x2299; {p++; goto _out; } }} + break; + case 50: +#line 290 "char_ref.rl" + {te = p+1;{ output->first = 0x2296; {p++; goto _out; } }} + break; + case 51: +#line 291 "char_ref.rl" + {te = p+1;{ output->first = 0x2295; {p++; goto _out; } }} + break; + case 52: +#line 292 "char_ref.rl" + {te = p+1;{ output->first = 0x2297; {p++; goto _out; } }} + break; + case 53: +#line 293 "char_ref.rl" + {te = p+1;{ output->first = 0x2232; {p++; goto _out; } }} + break; + case 54: +#line 294 "char_ref.rl" + {te = p+1;{ output->first = 0x201d; {p++; goto _out; } }} + break; + case 55: +#line 295 "char_ref.rl" + {te = p+1;{ output->first = 0x2019; {p++; goto _out; } }} + break; + case 56: +#line 296 "char_ref.rl" + {te = p+1;{ output->first = 0x2237; {p++; goto _out; } }} + break; + case 57: +#line 297 "char_ref.rl" + {te = p+1;{ output->first = 0x2a74; {p++; goto _out; } }} + break; + case 58: +#line 298 "char_ref.rl" + {te = p+1;{ output->first = 0x2261; {p++; goto _out; } }} + break; + case 59: +#line 299 "char_ref.rl" + {te = p+1;{ output->first = 0x222f; {p++; goto _out; } }} + break; + case 60: +#line 300 "char_ref.rl" + {te = p+1;{ output->first = 0x222e; {p++; goto _out; } }} + break; + case 61: +#line 301 "char_ref.rl" + {te = p+1;{ output->first = 0x2102; {p++; goto _out; } }} + break; + case 62: +#line 302 "char_ref.rl" + {te = p+1;{ output->first = 0x2210; {p++; goto _out; } }} + break; + case 63: +#line 303 "char_ref.rl" + {te = p+1;{ output->first = 0x2233; {p++; goto _out; } }} + break; + case 64: +#line 304 "char_ref.rl" + {te = p+1;{ output->first = 0x2a2f; {p++; goto _out; } }} + break; + case 65: +#line 305 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d49e; {p++; goto _out; } }} + break; + case 66: +#line 306 "char_ref.rl" + {te = p+1;{ output->first = 0x22d3; {p++; goto _out; } }} + break; + case 67: +#line 307 "char_ref.rl" + {te = p+1;{ output->first = 0x224d; {p++; goto _out; } }} + break; + case 68: +#line 308 "char_ref.rl" + {te = p+1;{ output->first = 0x2145; {p++; goto _out; } }} + break; + case 69: +#line 309 "char_ref.rl" + {te = p+1;{ output->first = 0x2911; {p++; goto _out; } }} + break; + case 70: +#line 310 "char_ref.rl" + {te = p+1;{ output->first = 0x0402; {p++; goto _out; } }} + break; + case 71: +#line 311 "char_ref.rl" + {te = p+1;{ output->first = 0x0405; {p++; goto _out; } }} + break; + case 72: +#line 312 "char_ref.rl" + {te = p+1;{ output->first = 0x040f; {p++; goto _out; } }} + break; + case 73: +#line 313 "char_ref.rl" + {te = p+1;{ output->first = 0x2021; {p++; goto _out; } }} + break; + case 74: +#line 314 "char_ref.rl" + {te = p+1;{ output->first = 0x21a1; {p++; goto _out; } }} + break; + case 75: +#line 315 "char_ref.rl" + {te = p+1;{ output->first = 0x2ae4; {p++; goto _out; } }} + break; + case 76: +#line 316 "char_ref.rl" + {te = p+1;{ output->first = 0x010e; {p++; goto _out; } }} + break; + case 77: +#line 317 "char_ref.rl" + {te = p+1;{ output->first = 0x0414; {p++; goto _out; } }} + break; + case 78: +#line 318 "char_ref.rl" + {te = p+1;{ output->first = 0x2207; {p++; goto _out; } }} + break; + case 79: +#line 319 "char_ref.rl" + {te = p+1;{ output->first = 0x0394; {p++; goto _out; } }} + break; + case 80: +#line 320 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d507; {p++; goto _out; } }} + break; + case 81: +#line 321 "char_ref.rl" + {te = p+1;{ output->first = 0xb4; {p++; goto _out; } }} + break; + case 82: +#line 322 "char_ref.rl" + {te = p+1;{ output->first = 0x02d9; {p++; goto _out; } }} + break; + case 83: +#line 323 "char_ref.rl" + {te = p+1;{ output->first = 0x02dd; {p++; goto _out; } }} + break; + case 84: +#line 324 "char_ref.rl" + {te = p+1;{ output->first = 0x60; {p++; goto _out; } }} + break; + case 85: +#line 325 "char_ref.rl" + {te = p+1;{ output->first = 0x02dc; {p++; goto _out; } }} + break; + case 86: +#line 326 "char_ref.rl" + {te = p+1;{ output->first = 0x22c4; {p++; goto _out; } }} + break; + case 87: +#line 327 "char_ref.rl" + {te = p+1;{ output->first = 0x2146; {p++; goto _out; } }} + break; + case 88: +#line 328 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d53b; {p++; goto _out; } }} + break; + case 89: +#line 329 "char_ref.rl" + {te = p+1;{ output->first = 0xa8; {p++; goto _out; } }} + break; + case 90: +#line 330 "char_ref.rl" + {te = p+1;{ output->first = 0x20dc; {p++; goto _out; } }} + break; + case 91: +#line 331 "char_ref.rl" + {te = p+1;{ output->first = 0x2250; {p++; goto _out; } }} + break; + case 92: +#line 332 "char_ref.rl" + {te = p+1;{ output->first = 0x222f; {p++; goto _out; } }} + break; + case 93: +#line 333 "char_ref.rl" + {te = p+1;{ output->first = 0xa8; {p++; goto _out; } }} + break; + case 94: +#line 334 "char_ref.rl" + {te = p+1;{ output->first = 0x21d3; {p++; goto _out; } }} + break; + case 95: +#line 335 "char_ref.rl" + {te = p+1;{ output->first = 0x21d0; {p++; goto _out; } }} + break; + case 96: +#line 336 "char_ref.rl" + {te = p+1;{ output->first = 0x21d4; {p++; goto _out; } }} + break; + case 97: +#line 337 "char_ref.rl" + {te = p+1;{ output->first = 0x2ae4; {p++; goto _out; } }} + break; + case 98: +#line 338 "char_ref.rl" + {te = p+1;{ output->first = 0x27f8; {p++; goto _out; } }} + break; + case 99: +#line 339 "char_ref.rl" + {te = p+1;{ output->first = 0x27fa; {p++; goto _out; } }} + break; + case 100: +#line 340 "char_ref.rl" + {te = p+1;{ output->first = 0x27f9; {p++; goto _out; } }} + break; + case 101: +#line 341 "char_ref.rl" + {te = p+1;{ output->first = 0x21d2; {p++; goto _out; } }} + break; + case 102: +#line 342 "char_ref.rl" + {te = p+1;{ output->first = 0x22a8; {p++; goto _out; } }} + break; + case 103: +#line 343 "char_ref.rl" + {te = p+1;{ output->first = 0x21d1; {p++; goto _out; } }} + break; + case 104: +#line 344 "char_ref.rl" + {te = p+1;{ output->first = 0x21d5; {p++; goto _out; } }} + break; + case 105: +#line 345 "char_ref.rl" + {te = p+1;{ output->first = 0x2225; {p++; goto _out; } }} + break; + case 106: +#line 346 "char_ref.rl" + {te = p+1;{ output->first = 0x2193; {p++; goto _out; } }} + break; + case 107: +#line 347 "char_ref.rl" + {te = p+1;{ output->first = 0x2913; {p++; goto _out; } }} + break; + case 108: +#line 348 "char_ref.rl" + {te = p+1;{ output->first = 0x21f5; {p++; goto _out; } }} + break; + case 109: +#line 349 "char_ref.rl" + {te = p+1;{ output->first = 0x0311; {p++; goto _out; } }} + break; + case 110: +#line 350 "char_ref.rl" + {te = p+1;{ output->first = 0x2950; {p++; goto _out; } }} + break; + case 111: +#line 351 "char_ref.rl" + {te = p+1;{ output->first = 0x295e; {p++; goto _out; } }} + break; + case 112: +#line 352 "char_ref.rl" + {te = p+1;{ output->first = 0x21bd; {p++; goto _out; } }} + break; + case 113: +#line 353 "char_ref.rl" + {te = p+1;{ output->first = 0x2956; {p++; goto _out; } }} + break; + case 114: +#line 354 "char_ref.rl" + {te = p+1;{ output->first = 0x295f; {p++; goto _out; } }} + break; + case 115: +#line 355 "char_ref.rl" + {te = p+1;{ output->first = 0x21c1; {p++; goto _out; } }} + break; + case 116: +#line 356 "char_ref.rl" + {te = p+1;{ output->first = 0x2957; {p++; goto _out; } }} + break; + case 117: +#line 357 "char_ref.rl" + {te = p+1;{ output->first = 0x22a4; {p++; goto _out; } }} + break; + case 118: +#line 358 "char_ref.rl" + {te = p+1;{ output->first = 0x21a7; {p++; goto _out; } }} + break; + case 119: +#line 359 "char_ref.rl" + {te = p+1;{ output->first = 0x21d3; {p++; goto _out; } }} + break; + case 120: +#line 360 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d49f; {p++; goto _out; } }} + break; + case 121: +#line 361 "char_ref.rl" + {te = p+1;{ output->first = 0x0110; {p++; goto _out; } }} + break; + case 122: +#line 362 "char_ref.rl" + {te = p+1;{ output->first = 0x014a; {p++; goto _out; } }} + break; + case 123: +#line 363 "char_ref.rl" + {te = p+1;{ output->first = 0xd0; {p++; goto _out; } }} + break; + case 124: +#line 365 "char_ref.rl" + {te = p+1;{ output->first = 0xc9; {p++; goto _out; } }} + break; + case 125: +#line 367 "char_ref.rl" + {te = p+1;{ output->first = 0x011a; {p++; goto _out; } }} + break; + case 126: +#line 368 "char_ref.rl" + {te = p+1;{ output->first = 0xca; {p++; goto _out; } }} + break; + case 127: +#line 370 "char_ref.rl" + {te = p+1;{ output->first = 0x042d; {p++; goto _out; } }} + break; + case 128: +#line 371 "char_ref.rl" + {te = p+1;{ output->first = 0x0116; {p++; goto _out; } }} + break; + case 129: +#line 372 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d508; {p++; goto _out; } }} + break; + case 130: +#line 373 "char_ref.rl" + {te = p+1;{ output->first = 0xc8; {p++; goto _out; } }} + break; + case 131: +#line 375 "char_ref.rl" + {te = p+1;{ output->first = 0x2208; {p++; goto _out; } }} + break; + case 132: +#line 376 "char_ref.rl" + {te = p+1;{ output->first = 0x0112; {p++; goto _out; } }} + break; + case 133: +#line 377 "char_ref.rl" + {te = p+1;{ output->first = 0x25fb; {p++; goto _out; } }} + break; + case 134: +#line 378 "char_ref.rl" + {te = p+1;{ output->first = 0x25ab; {p++; goto _out; } }} + break; + case 135: +#line 379 "char_ref.rl" + {te = p+1;{ output->first = 0x0118; {p++; goto _out; } }} + break; + case 136: +#line 380 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d53c; {p++; goto _out; } }} + break; + case 137: +#line 381 "char_ref.rl" + {te = p+1;{ output->first = 0x0395; {p++; goto _out; } }} + break; + case 138: +#line 382 "char_ref.rl" + {te = p+1;{ output->first = 0x2a75; {p++; goto _out; } }} + break; + case 139: +#line 383 "char_ref.rl" + {te = p+1;{ output->first = 0x2242; {p++; goto _out; } }} + break; + case 140: +#line 384 "char_ref.rl" + {te = p+1;{ output->first = 0x21cc; {p++; goto _out; } }} + break; + case 141: +#line 385 "char_ref.rl" + {te = p+1;{ output->first = 0x2130; {p++; goto _out; } }} + break; + case 142: +#line 386 "char_ref.rl" + {te = p+1;{ output->first = 0x2a73; {p++; goto _out; } }} + break; + case 143: +#line 387 "char_ref.rl" + {te = p+1;{ output->first = 0x0397; {p++; goto _out; } }} + break; + case 144: +#line 388 "char_ref.rl" + {te = p+1;{ output->first = 0xcb; {p++; goto _out; } }} + break; + case 145: +#line 390 "char_ref.rl" + {te = p+1;{ output->first = 0x2203; {p++; goto _out; } }} + break; + case 146: +#line 391 "char_ref.rl" + {te = p+1;{ output->first = 0x2147; {p++; goto _out; } }} + break; + case 147: +#line 392 "char_ref.rl" + {te = p+1;{ output->first = 0x0424; {p++; goto _out; } }} + break; + case 148: +#line 393 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d509; {p++; goto _out; } }} + break; + case 149: +#line 394 "char_ref.rl" + {te = p+1;{ output->first = 0x25fc; {p++; goto _out; } }} + break; + case 150: +#line 395 "char_ref.rl" + {te = p+1;{ output->first = 0x25aa; {p++; goto _out; } }} + break; + case 151: +#line 396 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d53d; {p++; goto _out; } }} + break; + case 152: +#line 397 "char_ref.rl" + {te = p+1;{ output->first = 0x2200; {p++; goto _out; } }} + break; + case 153: +#line 398 "char_ref.rl" + {te = p+1;{ output->first = 0x2131; {p++; goto _out; } }} + break; + case 154: +#line 399 "char_ref.rl" + {te = p+1;{ output->first = 0x2131; {p++; goto _out; } }} + break; + case 155: +#line 400 "char_ref.rl" + {te = p+1;{ output->first = 0x0403; {p++; goto _out; } }} + break; + case 156: +#line 401 "char_ref.rl" + {te = p+1;{ output->first = 0x3e; {p++; goto _out; } }} + break; + case 157: +#line 403 "char_ref.rl" + {te = p+1;{ output->first = 0x0393; {p++; goto _out; } }} + break; + case 158: +#line 404 "char_ref.rl" + {te = p+1;{ output->first = 0x03dc; {p++; goto _out; } }} + break; + case 159: +#line 405 "char_ref.rl" + {te = p+1;{ output->first = 0x011e; {p++; goto _out; } }} + break; + case 160: +#line 406 "char_ref.rl" + {te = p+1;{ output->first = 0x0122; {p++; goto _out; } }} + break; + case 161: +#line 407 "char_ref.rl" + {te = p+1;{ output->first = 0x011c; {p++; goto _out; } }} + break; + case 162: +#line 408 "char_ref.rl" + {te = p+1;{ output->first = 0x0413; {p++; goto _out; } }} + break; + case 163: +#line 409 "char_ref.rl" + {te = p+1;{ output->first = 0x0120; {p++; goto _out; } }} + break; + case 164: +#line 410 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d50a; {p++; goto _out; } }} + break; + case 165: +#line 411 "char_ref.rl" + {te = p+1;{ output->first = 0x22d9; {p++; goto _out; } }} + break; + case 166: +#line 412 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d53e; {p++; goto _out; } }} + break; + case 167: +#line 413 "char_ref.rl" + {te = p+1;{ output->first = 0x2265; {p++; goto _out; } }} + break; + case 168: +#line 414 "char_ref.rl" + {te = p+1;{ output->first = 0x22db; {p++; goto _out; } }} + break; + case 169: +#line 415 "char_ref.rl" + {te = p+1;{ output->first = 0x2267; {p++; goto _out; } }} + break; + case 170: +#line 416 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa2; {p++; goto _out; } }} + break; + case 171: +#line 417 "char_ref.rl" + {te = p+1;{ output->first = 0x2277; {p++; goto _out; } }} + break; + case 172: +#line 418 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7e; {p++; goto _out; } }} + break; + case 173: +#line 419 "char_ref.rl" + {te = p+1;{ output->first = 0x2273; {p++; goto _out; } }} + break; + case 174: +#line 420 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4a2; {p++; goto _out; } }} + break; + case 175: +#line 421 "char_ref.rl" + {te = p+1;{ output->first = 0x226b; {p++; goto _out; } }} + break; + case 176: +#line 422 "char_ref.rl" + {te = p+1;{ output->first = 0x042a; {p++; goto _out; } }} + break; + case 177: +#line 423 "char_ref.rl" + {te = p+1;{ output->first = 0x02c7; {p++; goto _out; } }} + break; + case 178: +#line 424 "char_ref.rl" + {te = p+1;{ output->first = 0x5e; {p++; goto _out; } }} + break; + case 179: +#line 425 "char_ref.rl" + {te = p+1;{ output->first = 0x0124; {p++; goto _out; } }} + break; + case 180: +#line 426 "char_ref.rl" + {te = p+1;{ output->first = 0x210c; {p++; goto _out; } }} + break; + case 181: +#line 427 "char_ref.rl" + {te = p+1;{ output->first = 0x210b; {p++; goto _out; } }} + break; + case 182: +#line 428 "char_ref.rl" + {te = p+1;{ output->first = 0x210d; {p++; goto _out; } }} + break; + case 183: +#line 429 "char_ref.rl" + {te = p+1;{ output->first = 0x2500; {p++; goto _out; } }} + break; + case 184: +#line 430 "char_ref.rl" + {te = p+1;{ output->first = 0x210b; {p++; goto _out; } }} + break; + case 185: +#line 431 "char_ref.rl" + {te = p+1;{ output->first = 0x0126; {p++; goto _out; } }} + break; + case 186: +#line 432 "char_ref.rl" + {te = p+1;{ output->first = 0x224e; {p++; goto _out; } }} + break; + case 187: +#line 433 "char_ref.rl" + {te = p+1;{ output->first = 0x224f; {p++; goto _out; } }} + break; + case 188: +#line 434 "char_ref.rl" + {te = p+1;{ output->first = 0x0415; {p++; goto _out; } }} + break; + case 189: +#line 435 "char_ref.rl" + {te = p+1;{ output->first = 0x0132; {p++; goto _out; } }} + break; + case 190: +#line 436 "char_ref.rl" + {te = p+1;{ output->first = 0x0401; {p++; goto _out; } }} + break; + case 191: +#line 437 "char_ref.rl" + {te = p+1;{ output->first = 0xcd; {p++; goto _out; } }} + break; + case 192: +#line 439 "char_ref.rl" + {te = p+1;{ output->first = 0xce; {p++; goto _out; } }} + break; + case 193: +#line 441 "char_ref.rl" + {te = p+1;{ output->first = 0x0418; {p++; goto _out; } }} + break; + case 194: +#line 442 "char_ref.rl" + {te = p+1;{ output->first = 0x0130; {p++; goto _out; } }} + break; + case 195: +#line 443 "char_ref.rl" + {te = p+1;{ output->first = 0x2111; {p++; goto _out; } }} + break; + case 196: +#line 444 "char_ref.rl" + {te = p+1;{ output->first = 0xcc; {p++; goto _out; } }} + break; + case 197: +#line 446 "char_ref.rl" + {te = p+1;{ output->first = 0x2111; {p++; goto _out; } }} + break; + case 198: +#line 447 "char_ref.rl" + {te = p+1;{ output->first = 0x012a; {p++; goto _out; } }} + break; + case 199: +#line 448 "char_ref.rl" + {te = p+1;{ output->first = 0x2148; {p++; goto _out; } }} + break; + case 200: +#line 449 "char_ref.rl" + {te = p+1;{ output->first = 0x21d2; {p++; goto _out; } }} + break; + case 201: +#line 450 "char_ref.rl" + {te = p+1;{ output->first = 0x222c; {p++; goto _out; } }} + break; + case 202: +#line 451 "char_ref.rl" + {te = p+1;{ output->first = 0x222b; {p++; goto _out; } }} + break; + case 203: +#line 452 "char_ref.rl" + {te = p+1;{ output->first = 0x22c2; {p++; goto _out; } }} + break; + case 204: +#line 453 "char_ref.rl" + {te = p+1;{ output->first = 0x2063; {p++; goto _out; } }} + break; + case 205: +#line 454 "char_ref.rl" + {te = p+1;{ output->first = 0x2062; {p++; goto _out; } }} + break; + case 206: +#line 455 "char_ref.rl" + {te = p+1;{ output->first = 0x012e; {p++; goto _out; } }} + break; + case 207: +#line 456 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d540; {p++; goto _out; } }} + break; + case 208: +#line 457 "char_ref.rl" + {te = p+1;{ output->first = 0x0399; {p++; goto _out; } }} + break; + case 209: +#line 458 "char_ref.rl" + {te = p+1;{ output->first = 0x2110; {p++; goto _out; } }} + break; + case 210: +#line 459 "char_ref.rl" + {te = p+1;{ output->first = 0x0128; {p++; goto _out; } }} + break; + case 211: +#line 460 "char_ref.rl" + {te = p+1;{ output->first = 0x0406; {p++; goto _out; } }} + break; + case 212: +#line 461 "char_ref.rl" + {te = p+1;{ output->first = 0xcf; {p++; goto _out; } }} + break; + case 213: +#line 463 "char_ref.rl" + {te = p+1;{ output->first = 0x0134; {p++; goto _out; } }} + break; + case 214: +#line 464 "char_ref.rl" + {te = p+1;{ output->first = 0x0419; {p++; goto _out; } }} + break; + case 215: +#line 465 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d50d; {p++; goto _out; } }} + break; + case 216: +#line 466 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d541; {p++; goto _out; } }} + break; + case 217: +#line 467 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4a5; {p++; goto _out; } }} + break; + case 218: +#line 468 "char_ref.rl" + {te = p+1;{ output->first = 0x0408; {p++; goto _out; } }} + break; + case 219: +#line 469 "char_ref.rl" + {te = p+1;{ output->first = 0x0404; {p++; goto _out; } }} + break; + case 220: +#line 470 "char_ref.rl" + {te = p+1;{ output->first = 0x0425; {p++; goto _out; } }} + break; + case 221: +#line 471 "char_ref.rl" + {te = p+1;{ output->first = 0x040c; {p++; goto _out; } }} + break; + case 222: +#line 472 "char_ref.rl" + {te = p+1;{ output->first = 0x039a; {p++; goto _out; } }} + break; + case 223: +#line 473 "char_ref.rl" + {te = p+1;{ output->first = 0x0136; {p++; goto _out; } }} + break; + case 224: +#line 474 "char_ref.rl" + {te = p+1;{ output->first = 0x041a; {p++; goto _out; } }} + break; + case 225: +#line 475 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d50e; {p++; goto _out; } }} + break; + case 226: +#line 476 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d542; {p++; goto _out; } }} + break; + case 227: +#line 477 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4a6; {p++; goto _out; } }} + break; + case 228: +#line 478 "char_ref.rl" + {te = p+1;{ output->first = 0x0409; {p++; goto _out; } }} + break; + case 229: +#line 479 "char_ref.rl" + {te = p+1;{ output->first = 0x3c; {p++; goto _out; } }} + break; + case 230: +#line 481 "char_ref.rl" + {te = p+1;{ output->first = 0x0139; {p++; goto _out; } }} + break; + case 231: +#line 482 "char_ref.rl" + {te = p+1;{ output->first = 0x039b; {p++; goto _out; } }} + break; + case 232: +#line 483 "char_ref.rl" + {te = p+1;{ output->first = 0x27ea; {p++; goto _out; } }} + break; + case 233: +#line 484 "char_ref.rl" + {te = p+1;{ output->first = 0x2112; {p++; goto _out; } }} + break; + case 234: +#line 485 "char_ref.rl" + {te = p+1;{ output->first = 0x219e; {p++; goto _out; } }} + break; + case 235: +#line 486 "char_ref.rl" + {te = p+1;{ output->first = 0x013d; {p++; goto _out; } }} + break; + case 236: +#line 487 "char_ref.rl" + {te = p+1;{ output->first = 0x013b; {p++; goto _out; } }} + break; + case 237: +#line 488 "char_ref.rl" + {te = p+1;{ output->first = 0x041b; {p++; goto _out; } }} + break; + case 238: +#line 489 "char_ref.rl" + {te = p+1;{ output->first = 0x27e8; {p++; goto _out; } }} + break; + case 239: +#line 490 "char_ref.rl" + {te = p+1;{ output->first = 0x2190; {p++; goto _out; } }} + break; + case 240: +#line 491 "char_ref.rl" + {te = p+1;{ output->first = 0x21e4; {p++; goto _out; } }} + break; + case 241: +#line 492 "char_ref.rl" + {te = p+1;{ output->first = 0x21c6; {p++; goto _out; } }} + break; + case 242: +#line 493 "char_ref.rl" + {te = p+1;{ output->first = 0x2308; {p++; goto _out; } }} + break; + case 243: +#line 494 "char_ref.rl" + {te = p+1;{ output->first = 0x27e6; {p++; goto _out; } }} + break; + case 244: +#line 495 "char_ref.rl" + {te = p+1;{ output->first = 0x2961; {p++; goto _out; } }} + break; + case 245: +#line 496 "char_ref.rl" + {te = p+1;{ output->first = 0x21c3; {p++; goto _out; } }} + break; + case 246: +#line 497 "char_ref.rl" + {te = p+1;{ output->first = 0x2959; {p++; goto _out; } }} + break; + case 247: +#line 498 "char_ref.rl" + {te = p+1;{ output->first = 0x230a; {p++; goto _out; } }} + break; + case 248: +#line 499 "char_ref.rl" + {te = p+1;{ output->first = 0x2194; {p++; goto _out; } }} + break; + case 249: +#line 500 "char_ref.rl" + {te = p+1;{ output->first = 0x294e; {p++; goto _out; } }} + break; + case 250: +#line 501 "char_ref.rl" + {te = p+1;{ output->first = 0x22a3; {p++; goto _out; } }} + break; + case 251: +#line 502 "char_ref.rl" + {te = p+1;{ output->first = 0x21a4; {p++; goto _out; } }} + break; + case 252: +#line 503 "char_ref.rl" + {te = p+1;{ output->first = 0x295a; {p++; goto _out; } }} + break; + case 253: +#line 504 "char_ref.rl" + {te = p+1;{ output->first = 0x22b2; {p++; goto _out; } }} + break; + case 254: +#line 505 "char_ref.rl" + {te = p+1;{ output->first = 0x29cf; {p++; goto _out; } }} + break; + case 255: +#line 506 "char_ref.rl" + {te = p+1;{ output->first = 0x22b4; {p++; goto _out; } }} + break; + case 256: +#line 507 "char_ref.rl" + {te = p+1;{ output->first = 0x2951; {p++; goto _out; } }} + break; + case 257: +#line 508 "char_ref.rl" + {te = p+1;{ output->first = 0x2960; {p++; goto _out; } }} + break; + case 258: +#line 509 "char_ref.rl" + {te = p+1;{ output->first = 0x21bf; {p++; goto _out; } }} + break; + case 259: +#line 510 "char_ref.rl" + {te = p+1;{ output->first = 0x2958; {p++; goto _out; } }} + break; + case 260: +#line 511 "char_ref.rl" + {te = p+1;{ output->first = 0x21bc; {p++; goto _out; } }} + break; + case 261: +#line 512 "char_ref.rl" + {te = p+1;{ output->first = 0x2952; {p++; goto _out; } }} + break; + case 262: +#line 513 "char_ref.rl" + {te = p+1;{ output->first = 0x21d0; {p++; goto _out; } }} + break; + case 263: +#line 514 "char_ref.rl" + {te = p+1;{ output->first = 0x21d4; {p++; goto _out; } }} + break; + case 264: +#line 515 "char_ref.rl" + {te = p+1;{ output->first = 0x22da; {p++; goto _out; } }} + break; + case 265: +#line 516 "char_ref.rl" + {te = p+1;{ output->first = 0x2266; {p++; goto _out; } }} + break; + case 266: +#line 517 "char_ref.rl" + {te = p+1;{ output->first = 0x2276; {p++; goto _out; } }} + break; + case 267: +#line 518 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa1; {p++; goto _out; } }} + break; + case 268: +#line 519 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7d; {p++; goto _out; } }} + break; + case 269: +#line 520 "char_ref.rl" + {te = p+1;{ output->first = 0x2272; {p++; goto _out; } }} + break; + case 270: +#line 521 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d50f; {p++; goto _out; } }} + break; + case 271: +#line 522 "char_ref.rl" + {te = p+1;{ output->first = 0x22d8; {p++; goto _out; } }} + break; + case 272: +#line 523 "char_ref.rl" + {te = p+1;{ output->first = 0x21da; {p++; goto _out; } }} + break; + case 273: +#line 524 "char_ref.rl" + {te = p+1;{ output->first = 0x013f; {p++; goto _out; } }} + break; + case 274: +#line 525 "char_ref.rl" + {te = p+1;{ output->first = 0x27f5; {p++; goto _out; } }} + break; + case 275: +#line 526 "char_ref.rl" + {te = p+1;{ output->first = 0x27f7; {p++; goto _out; } }} + break; + case 276: +#line 527 "char_ref.rl" + {te = p+1;{ output->first = 0x27f6; {p++; goto _out; } }} + break; + case 277: +#line 528 "char_ref.rl" + {te = p+1;{ output->first = 0x27f8; {p++; goto _out; } }} + break; + case 278: +#line 529 "char_ref.rl" + {te = p+1;{ output->first = 0x27fa; {p++; goto _out; } }} + break; + case 279: +#line 530 "char_ref.rl" + {te = p+1;{ output->first = 0x27f9; {p++; goto _out; } }} + break; + case 280: +#line 531 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d543; {p++; goto _out; } }} + break; + case 281: +#line 532 "char_ref.rl" + {te = p+1;{ output->first = 0x2199; {p++; goto _out; } }} + break; + case 282: +#line 533 "char_ref.rl" + {te = p+1;{ output->first = 0x2198; {p++; goto _out; } }} + break; + case 283: +#line 534 "char_ref.rl" + {te = p+1;{ output->first = 0x2112; {p++; goto _out; } }} + break; + case 284: +#line 535 "char_ref.rl" + {te = p+1;{ output->first = 0x21b0; {p++; goto _out; } }} + break; + case 285: +#line 536 "char_ref.rl" + {te = p+1;{ output->first = 0x0141; {p++; goto _out; } }} + break; + case 286: +#line 537 "char_ref.rl" + {te = p+1;{ output->first = 0x226a; {p++; goto _out; } }} + break; + case 287: +#line 538 "char_ref.rl" + {te = p+1;{ output->first = 0x2905; {p++; goto _out; } }} + break; + case 288: +#line 539 "char_ref.rl" + {te = p+1;{ output->first = 0x041c; {p++; goto _out; } }} + break; + case 289: +#line 540 "char_ref.rl" + {te = p+1;{ output->first = 0x205f; {p++; goto _out; } }} + break; + case 290: +#line 541 "char_ref.rl" + {te = p+1;{ output->first = 0x2133; {p++; goto _out; } }} + break; + case 291: +#line 542 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d510; {p++; goto _out; } }} + break; + case 292: +#line 543 "char_ref.rl" + {te = p+1;{ output->first = 0x2213; {p++; goto _out; } }} + break; + case 293: +#line 544 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d544; {p++; goto _out; } }} + break; + case 294: +#line 545 "char_ref.rl" + {te = p+1;{ output->first = 0x2133; {p++; goto _out; } }} + break; + case 295: +#line 546 "char_ref.rl" + {te = p+1;{ output->first = 0x039c; {p++; goto _out; } }} + break; + case 296: +#line 547 "char_ref.rl" + {te = p+1;{ output->first = 0x040a; {p++; goto _out; } }} + break; + case 297: +#line 548 "char_ref.rl" + {te = p+1;{ output->first = 0x0143; {p++; goto _out; } }} + break; + case 298: +#line 549 "char_ref.rl" + {te = p+1;{ output->first = 0x0147; {p++; goto _out; } }} + break; + case 299: +#line 550 "char_ref.rl" + {te = p+1;{ output->first = 0x0145; {p++; goto _out; } }} + break; + case 300: +#line 551 "char_ref.rl" + {te = p+1;{ output->first = 0x041d; {p++; goto _out; } }} + break; + case 301: +#line 552 "char_ref.rl" + {te = p+1;{ output->first = 0x200b; {p++; goto _out; } }} + break; + case 302: +#line 553 "char_ref.rl" + {te = p+1;{ output->first = 0x200b; {p++; goto _out; } }} + break; + case 303: +#line 554 "char_ref.rl" + {te = p+1;{ output->first = 0x200b; {p++; goto _out; } }} + break; + case 304: +#line 555 "char_ref.rl" + {te = p+1;{ output->first = 0x200b; {p++; goto _out; } }} + break; + case 305: +#line 556 "char_ref.rl" + {te = p+1;{ output->first = 0x226b; {p++; goto _out; } }} + break; + case 306: +#line 557 "char_ref.rl" + {te = p+1;{ output->first = 0x226a; {p++; goto _out; } }} + break; + case 307: +#line 558 "char_ref.rl" + {te = p+1;{ output->first = 0x0a; {p++; goto _out; } }} + break; + case 308: +#line 559 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d511; {p++; goto _out; } }} + break; + case 309: +#line 560 "char_ref.rl" + {te = p+1;{ output->first = 0x2060; {p++; goto _out; } }} + break; + case 310: +#line 561 "char_ref.rl" + {te = p+1;{ output->first = 0xa0; {p++; goto _out; } }} + break; + case 311: +#line 562 "char_ref.rl" + {te = p+1;{ output->first = 0x2115; {p++; goto _out; } }} + break; + case 312: +#line 563 "char_ref.rl" + {te = p+1;{ output->first = 0x2aec; {p++; goto _out; } }} + break; + case 313: +#line 564 "char_ref.rl" + {te = p+1;{ output->first = 0x2262; {p++; goto _out; } }} + break; + case 314: +#line 565 "char_ref.rl" + {te = p+1;{ output->first = 0x226d; {p++; goto _out; } }} + break; + case 315: +#line 566 "char_ref.rl" + {te = p+1;{ output->first = 0x2226; {p++; goto _out; } }} + break; + case 316: +#line 567 "char_ref.rl" + {te = p+1;{ output->first = 0x2209; {p++; goto _out; } }} + break; + case 317: +#line 568 "char_ref.rl" + {te = p+1;{ output->first = 0x2260; {p++; goto _out; } }} + break; + case 318: +#line 569 "char_ref.rl" + {te = p+1;{ output->first = 0x2242; output->second = 0x0338; {p++; goto _out; } }} + break; + case 319: +#line 570 "char_ref.rl" + {te = p+1;{ output->first = 0x2204; {p++; goto _out; } }} + break; + case 320: +#line 571 "char_ref.rl" + {te = p+1;{ output->first = 0x226f; {p++; goto _out; } }} + break; + case 321: +#line 572 "char_ref.rl" + {te = p+1;{ output->first = 0x2271; {p++; goto _out; } }} + break; + case 322: +#line 573 "char_ref.rl" + {te = p+1;{ output->first = 0x2267; output->second = 0x0338; {p++; goto _out; } }} + break; + case 323: +#line 574 "char_ref.rl" + {te = p+1;{ output->first = 0x226b; output->second = 0x0338; {p++; goto _out; } }} + break; + case 324: +#line 575 "char_ref.rl" + {te = p+1;{ output->first = 0x2279; {p++; goto _out; } }} + break; + case 325: +#line 576 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7e; output->second = 0x0338; {p++; goto _out; } }} + break; + case 326: +#line 577 "char_ref.rl" + {te = p+1;{ output->first = 0x2275; {p++; goto _out; } }} + break; + case 327: +#line 578 "char_ref.rl" + {te = p+1;{ output->first = 0x224e; output->second = 0x0338; {p++; goto _out; } }} + break; + case 328: +#line 579 "char_ref.rl" + {te = p+1;{ output->first = 0x224f; output->second = 0x0338; {p++; goto _out; } }} + break; + case 329: +#line 580 "char_ref.rl" + {te = p+1;{ output->first = 0x22ea; {p++; goto _out; } }} + break; + case 330: +#line 581 "char_ref.rl" + {te = p+1;{ output->first = 0x29cf; output->second = 0x0338; {p++; goto _out; } }} + break; + case 331: +#line 582 "char_ref.rl" + {te = p+1;{ output->first = 0x22ec; {p++; goto _out; } }} + break; + case 332: +#line 583 "char_ref.rl" + {te = p+1;{ output->first = 0x226e; {p++; goto _out; } }} + break; + case 333: +#line 584 "char_ref.rl" + {te = p+1;{ output->first = 0x2270; {p++; goto _out; } }} + break; + case 334: +#line 585 "char_ref.rl" + {te = p+1;{ output->first = 0x2278; {p++; goto _out; } }} + break; + case 335: +#line 586 "char_ref.rl" + {te = p+1;{ output->first = 0x226a; output->second = 0x0338; {p++; goto _out; } }} + break; + case 336: +#line 587 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7d; output->second = 0x0338; {p++; goto _out; } }} + break; + case 337: +#line 588 "char_ref.rl" + {te = p+1;{ output->first = 0x2274; {p++; goto _out; } }} + break; + case 338: +#line 589 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa2; output->second = 0x0338; {p++; goto _out; } }} + break; + case 339: +#line 590 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa1; output->second = 0x0338; {p++; goto _out; } }} + break; + case 340: +#line 591 "char_ref.rl" + {te = p+1;{ output->first = 0x2280; {p++; goto _out; } }} + break; + case 341: +#line 592 "char_ref.rl" + {te = p+1;{ output->first = 0x2aaf; output->second = 0x0338; {p++; goto _out; } }} + break; + case 342: +#line 593 "char_ref.rl" + {te = p+1;{ output->first = 0x22e0; {p++; goto _out; } }} + break; + case 343: +#line 594 "char_ref.rl" + {te = p+1;{ output->first = 0x220c; {p++; goto _out; } }} + break; + case 344: +#line 595 "char_ref.rl" + {te = p+1;{ output->first = 0x22eb; {p++; goto _out; } }} + break; + case 345: +#line 596 "char_ref.rl" + {te = p+1;{ output->first = 0x29d0; output->second = 0x0338; {p++; goto _out; } }} + break; + case 346: +#line 597 "char_ref.rl" + {te = p+1;{ output->first = 0x22ed; {p++; goto _out; } }} + break; + case 347: +#line 598 "char_ref.rl" + {te = p+1;{ output->first = 0x228f; output->second = 0x0338; {p++; goto _out; } }} + break; + case 348: +#line 599 "char_ref.rl" + {te = p+1;{ output->first = 0x22e2; {p++; goto _out; } }} + break; + case 349: +#line 600 "char_ref.rl" + {te = p+1;{ output->first = 0x2290; output->second = 0x0338; {p++; goto _out; } }} + break; + case 350: +#line 601 "char_ref.rl" + {te = p+1;{ output->first = 0x22e3; {p++; goto _out; } }} + break; + case 351: +#line 602 "char_ref.rl" + {te = p+1;{ output->first = 0x2282; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 352: +#line 603 "char_ref.rl" + {te = p+1;{ output->first = 0x2288; {p++; goto _out; } }} + break; + case 353: +#line 604 "char_ref.rl" + {te = p+1;{ output->first = 0x2281; {p++; goto _out; } }} + break; + case 354: +#line 605 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab0; output->second = 0x0338; {p++; goto _out; } }} + break; + case 355: +#line 606 "char_ref.rl" + {te = p+1;{ output->first = 0x22e1; {p++; goto _out; } }} + break; + case 356: +#line 607 "char_ref.rl" + {te = p+1;{ output->first = 0x227f; output->second = 0x0338; {p++; goto _out; } }} + break; + case 357: +#line 608 "char_ref.rl" + {te = p+1;{ output->first = 0x2283; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 358: +#line 609 "char_ref.rl" + {te = p+1;{ output->first = 0x2289; {p++; goto _out; } }} + break; + case 359: +#line 610 "char_ref.rl" + {te = p+1;{ output->first = 0x2241; {p++; goto _out; } }} + break; + case 360: +#line 611 "char_ref.rl" + {te = p+1;{ output->first = 0x2244; {p++; goto _out; } }} + break; + case 361: +#line 612 "char_ref.rl" + {te = p+1;{ output->first = 0x2247; {p++; goto _out; } }} + break; + case 362: +#line 613 "char_ref.rl" + {te = p+1;{ output->first = 0x2249; {p++; goto _out; } }} + break; + case 363: +#line 614 "char_ref.rl" + {te = p+1;{ output->first = 0x2224; {p++; goto _out; } }} + break; + case 364: +#line 615 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4a9; {p++; goto _out; } }} + break; + case 365: +#line 616 "char_ref.rl" + {te = p+1;{ output->first = 0xd1; {p++; goto _out; } }} + break; + case 366: +#line 618 "char_ref.rl" + {te = p+1;{ output->first = 0x039d; {p++; goto _out; } }} + break; + case 367: +#line 619 "char_ref.rl" + {te = p+1;{ output->first = 0x0152; {p++; goto _out; } }} + break; + case 368: +#line 620 "char_ref.rl" + {te = p+1;{ output->first = 0xd3; {p++; goto _out; } }} + break; + case 369: +#line 622 "char_ref.rl" + {te = p+1;{ output->first = 0xd4; {p++; goto _out; } }} + break; + case 370: +#line 624 "char_ref.rl" + {te = p+1;{ output->first = 0x041e; {p++; goto _out; } }} + break; + case 371: +#line 625 "char_ref.rl" + {te = p+1;{ output->first = 0x0150; {p++; goto _out; } }} + break; + case 372: +#line 626 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d512; {p++; goto _out; } }} + break; + case 373: +#line 627 "char_ref.rl" + {te = p+1;{ output->first = 0xd2; {p++; goto _out; } }} + break; + case 374: +#line 629 "char_ref.rl" + {te = p+1;{ output->first = 0x014c; {p++; goto _out; } }} + break; + case 375: +#line 630 "char_ref.rl" + {te = p+1;{ output->first = 0x03a9; {p++; goto _out; } }} + break; + case 376: +#line 631 "char_ref.rl" + {te = p+1;{ output->first = 0x039f; {p++; goto _out; } }} + break; + case 377: +#line 632 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d546; {p++; goto _out; } }} + break; + case 378: +#line 633 "char_ref.rl" + {te = p+1;{ output->first = 0x201c; {p++; goto _out; } }} + break; + case 379: +#line 634 "char_ref.rl" + {te = p+1;{ output->first = 0x2018; {p++; goto _out; } }} + break; + case 380: +#line 635 "char_ref.rl" + {te = p+1;{ output->first = 0x2a54; {p++; goto _out; } }} + break; + case 381: +#line 636 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4aa; {p++; goto _out; } }} + break; + case 382: +#line 637 "char_ref.rl" + {te = p+1;{ output->first = 0xd8; {p++; goto _out; } }} + break; + case 383: +#line 639 "char_ref.rl" + {te = p+1;{ output->first = 0xd5; {p++; goto _out; } }} + break; + case 384: +#line 641 "char_ref.rl" + {te = p+1;{ output->first = 0x2a37; {p++; goto _out; } }} + break; + case 385: +#line 642 "char_ref.rl" + {te = p+1;{ output->first = 0xd6; {p++; goto _out; } }} + break; + case 386: +#line 644 "char_ref.rl" + {te = p+1;{ output->first = 0x203e; {p++; goto _out; } }} + break; + case 387: +#line 645 "char_ref.rl" + {te = p+1;{ output->first = 0x23de; {p++; goto _out; } }} + break; + case 388: +#line 646 "char_ref.rl" + {te = p+1;{ output->first = 0x23b4; {p++; goto _out; } }} + break; + case 389: +#line 647 "char_ref.rl" + {te = p+1;{ output->first = 0x23dc; {p++; goto _out; } }} + break; + case 390: +#line 648 "char_ref.rl" + {te = p+1;{ output->first = 0x2202; {p++; goto _out; } }} + break; + case 391: +#line 649 "char_ref.rl" + {te = p+1;{ output->first = 0x041f; {p++; goto _out; } }} + break; + case 392: +#line 650 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d513; {p++; goto _out; } }} + break; + case 393: +#line 651 "char_ref.rl" + {te = p+1;{ output->first = 0x03a6; {p++; goto _out; } }} + break; + case 394: +#line 652 "char_ref.rl" + {te = p+1;{ output->first = 0x03a0; {p++; goto _out; } }} + break; + case 395: +#line 653 "char_ref.rl" + {te = p+1;{ output->first = 0xb1; {p++; goto _out; } }} + break; + case 396: +#line 654 "char_ref.rl" + {te = p+1;{ output->first = 0x210c; {p++; goto _out; } }} + break; + case 397: +#line 655 "char_ref.rl" + {te = p+1;{ output->first = 0x2119; {p++; goto _out; } }} + break; + case 398: +#line 656 "char_ref.rl" + {te = p+1;{ output->first = 0x2abb; {p++; goto _out; } }} + break; + case 399: +#line 657 "char_ref.rl" + {te = p+1;{ output->first = 0x227a; {p++; goto _out; } }} + break; + case 400: +#line 658 "char_ref.rl" + {te = p+1;{ output->first = 0x2aaf; {p++; goto _out; } }} + break; + case 401: +#line 659 "char_ref.rl" + {te = p+1;{ output->first = 0x227c; {p++; goto _out; } }} + break; + case 402: +#line 660 "char_ref.rl" + {te = p+1;{ output->first = 0x227e; {p++; goto _out; } }} + break; + case 403: +#line 661 "char_ref.rl" + {te = p+1;{ output->first = 0x2033; {p++; goto _out; } }} + break; + case 404: +#line 662 "char_ref.rl" + {te = p+1;{ output->first = 0x220f; {p++; goto _out; } }} + break; + case 405: +#line 663 "char_ref.rl" + {te = p+1;{ output->first = 0x2237; {p++; goto _out; } }} + break; + case 406: +#line 664 "char_ref.rl" + {te = p+1;{ output->first = 0x221d; {p++; goto _out; } }} + break; + case 407: +#line 665 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4ab; {p++; goto _out; } }} + break; + case 408: +#line 666 "char_ref.rl" + {te = p+1;{ output->first = 0x03a8; {p++; goto _out; } }} + break; + case 409: +#line 667 "char_ref.rl" + {te = p+1;{ output->first = 0x22; {p++; goto _out; } }} + break; + case 410: +#line 669 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d514; {p++; goto _out; } }} + break; + case 411: +#line 670 "char_ref.rl" + {te = p+1;{ output->first = 0x211a; {p++; goto _out; } }} + break; + case 412: +#line 671 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4ac; {p++; goto _out; } }} + break; + case 413: +#line 672 "char_ref.rl" + {te = p+1;{ output->first = 0x2910; {p++; goto _out; } }} + break; + case 414: +#line 673 "char_ref.rl" + {te = p+1;{ output->first = 0xae; {p++; goto _out; } }} + break; + case 415: +#line 675 "char_ref.rl" + {te = p+1;{ output->first = 0x0154; {p++; goto _out; } }} + break; + case 416: +#line 676 "char_ref.rl" + {te = p+1;{ output->first = 0x27eb; {p++; goto _out; } }} + break; + case 417: +#line 677 "char_ref.rl" + {te = p+1;{ output->first = 0x21a0; {p++; goto _out; } }} + break; + case 418: +#line 678 "char_ref.rl" + {te = p+1;{ output->first = 0x2916; {p++; goto _out; } }} + break; + case 419: +#line 679 "char_ref.rl" + {te = p+1;{ output->first = 0x0158; {p++; goto _out; } }} + break; + case 420: +#line 680 "char_ref.rl" + {te = p+1;{ output->first = 0x0156; {p++; goto _out; } }} + break; + case 421: +#line 681 "char_ref.rl" + {te = p+1;{ output->first = 0x0420; {p++; goto _out; } }} + break; + case 422: +#line 682 "char_ref.rl" + {te = p+1;{ output->first = 0x211c; {p++; goto _out; } }} + break; + case 423: +#line 683 "char_ref.rl" + {te = p+1;{ output->first = 0x220b; {p++; goto _out; } }} + break; + case 424: +#line 684 "char_ref.rl" + {te = p+1;{ output->first = 0x21cb; {p++; goto _out; } }} + break; + case 425: +#line 685 "char_ref.rl" + {te = p+1;{ output->first = 0x296f; {p++; goto _out; } }} + break; + case 426: +#line 686 "char_ref.rl" + {te = p+1;{ output->first = 0x211c; {p++; goto _out; } }} + break; + case 427: +#line 687 "char_ref.rl" + {te = p+1;{ output->first = 0x03a1; {p++; goto _out; } }} + break; + case 428: +#line 688 "char_ref.rl" + {te = p+1;{ output->first = 0x27e9; {p++; goto _out; } }} + break; + case 429: +#line 689 "char_ref.rl" + {te = p+1;{ output->first = 0x2192; {p++; goto _out; } }} + break; + case 430: +#line 690 "char_ref.rl" + {te = p+1;{ output->first = 0x21e5; {p++; goto _out; } }} + break; + case 431: +#line 691 "char_ref.rl" + {te = p+1;{ output->first = 0x21c4; {p++; goto _out; } }} + break; + case 432: +#line 692 "char_ref.rl" + {te = p+1;{ output->first = 0x2309; {p++; goto _out; } }} + break; + case 433: +#line 693 "char_ref.rl" + {te = p+1;{ output->first = 0x27e7; {p++; goto _out; } }} + break; + case 434: +#line 694 "char_ref.rl" + {te = p+1;{ output->first = 0x295d; {p++; goto _out; } }} + break; + case 435: +#line 695 "char_ref.rl" + {te = p+1;{ output->first = 0x21c2; {p++; goto _out; } }} + break; + case 436: +#line 696 "char_ref.rl" + {te = p+1;{ output->first = 0x2955; {p++; goto _out; } }} + break; + case 437: +#line 697 "char_ref.rl" + {te = p+1;{ output->first = 0x230b; {p++; goto _out; } }} + break; + case 438: +#line 698 "char_ref.rl" + {te = p+1;{ output->first = 0x22a2; {p++; goto _out; } }} + break; + case 439: +#line 699 "char_ref.rl" + {te = p+1;{ output->first = 0x21a6; {p++; goto _out; } }} + break; + case 440: +#line 700 "char_ref.rl" + {te = p+1;{ output->first = 0x295b; {p++; goto _out; } }} + break; + case 441: +#line 701 "char_ref.rl" + {te = p+1;{ output->first = 0x22b3; {p++; goto _out; } }} + break; + case 442: +#line 702 "char_ref.rl" + {te = p+1;{ output->first = 0x29d0; {p++; goto _out; } }} + break; + case 443: +#line 703 "char_ref.rl" + {te = p+1;{ output->first = 0x22b5; {p++; goto _out; } }} + break; + case 444: +#line 704 "char_ref.rl" + {te = p+1;{ output->first = 0x294f; {p++; goto _out; } }} + break; + case 445: +#line 705 "char_ref.rl" + {te = p+1;{ output->first = 0x295c; {p++; goto _out; } }} + break; + case 446: +#line 706 "char_ref.rl" + {te = p+1;{ output->first = 0x21be; {p++; goto _out; } }} + break; + case 447: +#line 707 "char_ref.rl" + {te = p+1;{ output->first = 0x2954; {p++; goto _out; } }} + break; + case 448: +#line 708 "char_ref.rl" + {te = p+1;{ output->first = 0x21c0; {p++; goto _out; } }} + break; + case 449: +#line 709 "char_ref.rl" + {te = p+1;{ output->first = 0x2953; {p++; goto _out; } }} + break; + case 450: +#line 710 "char_ref.rl" + {te = p+1;{ output->first = 0x21d2; {p++; goto _out; } }} + break; + case 451: +#line 711 "char_ref.rl" + {te = p+1;{ output->first = 0x211d; {p++; goto _out; } }} + break; + case 452: +#line 712 "char_ref.rl" + {te = p+1;{ output->first = 0x2970; {p++; goto _out; } }} + break; + case 453: +#line 713 "char_ref.rl" + {te = p+1;{ output->first = 0x21db; {p++; goto _out; } }} + break; + case 454: +#line 714 "char_ref.rl" + {te = p+1;{ output->first = 0x211b; {p++; goto _out; } }} + break; + case 455: +#line 715 "char_ref.rl" + {te = p+1;{ output->first = 0x21b1; {p++; goto _out; } }} + break; + case 456: +#line 716 "char_ref.rl" + {te = p+1;{ output->first = 0x29f4; {p++; goto _out; } }} + break; + case 457: +#line 717 "char_ref.rl" + {te = p+1;{ output->first = 0x0429; {p++; goto _out; } }} + break; + case 458: +#line 718 "char_ref.rl" + {te = p+1;{ output->first = 0x0428; {p++; goto _out; } }} + break; + case 459: +#line 719 "char_ref.rl" + {te = p+1;{ output->first = 0x042c; {p++; goto _out; } }} + break; + case 460: +#line 720 "char_ref.rl" + {te = p+1;{ output->first = 0x015a; {p++; goto _out; } }} + break; + case 461: +#line 721 "char_ref.rl" + {te = p+1;{ output->first = 0x2abc; {p++; goto _out; } }} + break; + case 462: +#line 722 "char_ref.rl" + {te = p+1;{ output->first = 0x0160; {p++; goto _out; } }} + break; + case 463: +#line 723 "char_ref.rl" + {te = p+1;{ output->first = 0x015e; {p++; goto _out; } }} + break; + case 464: +#line 724 "char_ref.rl" + {te = p+1;{ output->first = 0x015c; {p++; goto _out; } }} + break; + case 465: +#line 725 "char_ref.rl" + {te = p+1;{ output->first = 0x0421; {p++; goto _out; } }} + break; + case 466: +#line 726 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d516; {p++; goto _out; } }} + break; + case 467: +#line 727 "char_ref.rl" + {te = p+1;{ output->first = 0x2193; {p++; goto _out; } }} + break; + case 468: +#line 728 "char_ref.rl" + {te = p+1;{ output->first = 0x2190; {p++; goto _out; } }} + break; + case 469: +#line 729 "char_ref.rl" + {te = p+1;{ output->first = 0x2192; {p++; goto _out; } }} + break; + case 470: +#line 730 "char_ref.rl" + {te = p+1;{ output->first = 0x2191; {p++; goto _out; } }} + break; + case 471: +#line 731 "char_ref.rl" + {te = p+1;{ output->first = 0x03a3; {p++; goto _out; } }} + break; + case 472: +#line 732 "char_ref.rl" + {te = p+1;{ output->first = 0x2218; {p++; goto _out; } }} + break; + case 473: +#line 733 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d54a; {p++; goto _out; } }} + break; + case 474: +#line 734 "char_ref.rl" + {te = p+1;{ output->first = 0x221a; {p++; goto _out; } }} + break; + case 475: +#line 735 "char_ref.rl" + {te = p+1;{ output->first = 0x25a1; {p++; goto _out; } }} + break; + case 476: +#line 736 "char_ref.rl" + {te = p+1;{ output->first = 0x2293; {p++; goto _out; } }} + break; + case 477: +#line 737 "char_ref.rl" + {te = p+1;{ output->first = 0x228f; {p++; goto _out; } }} + break; + case 478: +#line 738 "char_ref.rl" + {te = p+1;{ output->first = 0x2291; {p++; goto _out; } }} + break; + case 479: +#line 739 "char_ref.rl" + {te = p+1;{ output->first = 0x2290; {p++; goto _out; } }} + break; + case 480: +#line 740 "char_ref.rl" + {te = p+1;{ output->first = 0x2292; {p++; goto _out; } }} + break; + case 481: +#line 741 "char_ref.rl" + {te = p+1;{ output->first = 0x2294; {p++; goto _out; } }} + break; + case 482: +#line 742 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4ae; {p++; goto _out; } }} + break; + case 483: +#line 743 "char_ref.rl" + {te = p+1;{ output->first = 0x22c6; {p++; goto _out; } }} + break; + case 484: +#line 744 "char_ref.rl" + {te = p+1;{ output->first = 0x22d0; {p++; goto _out; } }} + break; + case 485: +#line 745 "char_ref.rl" + {te = p+1;{ output->first = 0x22d0; {p++; goto _out; } }} + break; + case 486: +#line 746 "char_ref.rl" + {te = p+1;{ output->first = 0x2286; {p++; goto _out; } }} + break; + case 487: +#line 747 "char_ref.rl" + {te = p+1;{ output->first = 0x227b; {p++; goto _out; } }} + break; + case 488: +#line 748 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab0; {p++; goto _out; } }} + break; + case 489: +#line 749 "char_ref.rl" + {te = p+1;{ output->first = 0x227d; {p++; goto _out; } }} + break; + case 490: +#line 750 "char_ref.rl" + {te = p+1;{ output->first = 0x227f; {p++; goto _out; } }} + break; + case 491: +#line 751 "char_ref.rl" + {te = p+1;{ output->first = 0x220b; {p++; goto _out; } }} + break; + case 492: +#line 752 "char_ref.rl" + {te = p+1;{ output->first = 0x2211; {p++; goto _out; } }} + break; + case 493: +#line 753 "char_ref.rl" + {te = p+1;{ output->first = 0x22d1; {p++; goto _out; } }} + break; + case 494: +#line 754 "char_ref.rl" + {te = p+1;{ output->first = 0x2283; {p++; goto _out; } }} + break; + case 495: +#line 755 "char_ref.rl" + {te = p+1;{ output->first = 0x2287; {p++; goto _out; } }} + break; + case 496: +#line 756 "char_ref.rl" + {te = p+1;{ output->first = 0x22d1; {p++; goto _out; } }} + break; + case 497: +#line 757 "char_ref.rl" + {te = p+1;{ output->first = 0xde; {p++; goto _out; } }} + break; + case 498: +#line 759 "char_ref.rl" + {te = p+1;{ output->first = 0x2122; {p++; goto _out; } }} + break; + case 499: +#line 760 "char_ref.rl" + {te = p+1;{ output->first = 0x040b; {p++; goto _out; } }} + break; + case 500: +#line 761 "char_ref.rl" + {te = p+1;{ output->first = 0x0426; {p++; goto _out; } }} + break; + case 501: +#line 762 "char_ref.rl" + {te = p+1;{ output->first = 0x09; {p++; goto _out; } }} + break; + case 502: +#line 763 "char_ref.rl" + {te = p+1;{ output->first = 0x03a4; {p++; goto _out; } }} + break; + case 503: +#line 764 "char_ref.rl" + {te = p+1;{ output->first = 0x0164; {p++; goto _out; } }} + break; + case 504: +#line 765 "char_ref.rl" + {te = p+1;{ output->first = 0x0162; {p++; goto _out; } }} + break; + case 505: +#line 766 "char_ref.rl" + {te = p+1;{ output->first = 0x0422; {p++; goto _out; } }} + break; + case 506: +#line 767 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d517; {p++; goto _out; } }} + break; + case 507: +#line 768 "char_ref.rl" + {te = p+1;{ output->first = 0x2234; {p++; goto _out; } }} + break; + case 508: +#line 769 "char_ref.rl" + {te = p+1;{ output->first = 0x0398; {p++; goto _out; } }} + break; + case 509: +#line 770 "char_ref.rl" + {te = p+1;{ output->first = 0x205f; output->second = 0x200a; {p++; goto _out; } }} + break; + case 510: +#line 771 "char_ref.rl" + {te = p+1;{ output->first = 0x2009; {p++; goto _out; } }} + break; + case 511: +#line 772 "char_ref.rl" + {te = p+1;{ output->first = 0x223c; {p++; goto _out; } }} + break; + case 512: +#line 773 "char_ref.rl" + {te = p+1;{ output->first = 0x2243; {p++; goto _out; } }} + break; + case 513: +#line 774 "char_ref.rl" + {te = p+1;{ output->first = 0x2245; {p++; goto _out; } }} + break; + case 514: +#line 775 "char_ref.rl" + {te = p+1;{ output->first = 0x2248; {p++; goto _out; } }} + break; + case 515: +#line 776 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d54b; {p++; goto _out; } }} + break; + case 516: +#line 777 "char_ref.rl" + {te = p+1;{ output->first = 0x20db; {p++; goto _out; } }} + break; + case 517: +#line 778 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4af; {p++; goto _out; } }} + break; + case 518: +#line 779 "char_ref.rl" + {te = p+1;{ output->first = 0x0166; {p++; goto _out; } }} + break; + case 519: +#line 780 "char_ref.rl" + {te = p+1;{ output->first = 0xda; {p++; goto _out; } }} + break; + case 520: +#line 782 "char_ref.rl" + {te = p+1;{ output->first = 0x219f; {p++; goto _out; } }} + break; + case 521: +#line 783 "char_ref.rl" + {te = p+1;{ output->first = 0x2949; {p++; goto _out; } }} + break; + case 522: +#line 784 "char_ref.rl" + {te = p+1;{ output->first = 0x040e; {p++; goto _out; } }} + break; + case 523: +#line 785 "char_ref.rl" + {te = p+1;{ output->first = 0x016c; {p++; goto _out; } }} + break; + case 524: +#line 786 "char_ref.rl" + {te = p+1;{ output->first = 0xdb; {p++; goto _out; } }} + break; + case 525: +#line 788 "char_ref.rl" + {te = p+1;{ output->first = 0x0423; {p++; goto _out; } }} + break; + case 526: +#line 789 "char_ref.rl" + {te = p+1;{ output->first = 0x0170; {p++; goto _out; } }} + break; + case 527: +#line 790 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d518; {p++; goto _out; } }} + break; + case 528: +#line 791 "char_ref.rl" + {te = p+1;{ output->first = 0xd9; {p++; goto _out; } }} + break; + case 529: +#line 793 "char_ref.rl" + {te = p+1;{ output->first = 0x016a; {p++; goto _out; } }} + break; + case 530: +#line 794 "char_ref.rl" + {te = p+1;{ output->first = 0x5f; {p++; goto _out; } }} + break; + case 531: +#line 795 "char_ref.rl" + {te = p+1;{ output->first = 0x23df; {p++; goto _out; } }} + break; + case 532: +#line 796 "char_ref.rl" + {te = p+1;{ output->first = 0x23b5; {p++; goto _out; } }} + break; + case 533: +#line 797 "char_ref.rl" + {te = p+1;{ output->first = 0x23dd; {p++; goto _out; } }} + break; + case 534: +#line 798 "char_ref.rl" + {te = p+1;{ output->first = 0x22c3; {p++; goto _out; } }} + break; + case 535: +#line 799 "char_ref.rl" + {te = p+1;{ output->first = 0x228e; {p++; goto _out; } }} + break; + case 536: +#line 800 "char_ref.rl" + {te = p+1;{ output->first = 0x0172; {p++; goto _out; } }} + break; + case 537: +#line 801 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d54c; {p++; goto _out; } }} + break; + case 538: +#line 802 "char_ref.rl" + {te = p+1;{ output->first = 0x2191; {p++; goto _out; } }} + break; + case 539: +#line 803 "char_ref.rl" + {te = p+1;{ output->first = 0x2912; {p++; goto _out; } }} + break; + case 540: +#line 804 "char_ref.rl" + {te = p+1;{ output->first = 0x21c5; {p++; goto _out; } }} + break; + case 541: +#line 805 "char_ref.rl" + {te = p+1;{ output->first = 0x2195; {p++; goto _out; } }} + break; + case 542: +#line 806 "char_ref.rl" + {te = p+1;{ output->first = 0x296e; {p++; goto _out; } }} + break; + case 543: +#line 807 "char_ref.rl" + {te = p+1;{ output->first = 0x22a5; {p++; goto _out; } }} + break; + case 544: +#line 808 "char_ref.rl" + {te = p+1;{ output->first = 0x21a5; {p++; goto _out; } }} + break; + case 545: +#line 809 "char_ref.rl" + {te = p+1;{ output->first = 0x21d1; {p++; goto _out; } }} + break; + case 546: +#line 810 "char_ref.rl" + {te = p+1;{ output->first = 0x21d5; {p++; goto _out; } }} + break; + case 547: +#line 811 "char_ref.rl" + {te = p+1;{ output->first = 0x2196; {p++; goto _out; } }} + break; + case 548: +#line 812 "char_ref.rl" + {te = p+1;{ output->first = 0x2197; {p++; goto _out; } }} + break; + case 549: +#line 813 "char_ref.rl" + {te = p+1;{ output->first = 0x03d2; {p++; goto _out; } }} + break; + case 550: +#line 814 "char_ref.rl" + {te = p+1;{ output->first = 0x03a5; {p++; goto _out; } }} + break; + case 551: +#line 815 "char_ref.rl" + {te = p+1;{ output->first = 0x016e; {p++; goto _out; } }} + break; + case 552: +#line 816 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b0; {p++; goto _out; } }} + break; + case 553: +#line 817 "char_ref.rl" + {te = p+1;{ output->first = 0x0168; {p++; goto _out; } }} + break; + case 554: +#line 818 "char_ref.rl" + {te = p+1;{ output->first = 0xdc; {p++; goto _out; } }} + break; + case 555: +#line 820 "char_ref.rl" + {te = p+1;{ output->first = 0x22ab; {p++; goto _out; } }} + break; + case 556: +#line 821 "char_ref.rl" + {te = p+1;{ output->first = 0x2aeb; {p++; goto _out; } }} + break; + case 557: +#line 822 "char_ref.rl" + {te = p+1;{ output->first = 0x0412; {p++; goto _out; } }} + break; + case 558: +#line 823 "char_ref.rl" + {te = p+1;{ output->first = 0x22a9; {p++; goto _out; } }} + break; + case 559: +#line 824 "char_ref.rl" + {te = p+1;{ output->first = 0x2ae6; {p++; goto _out; } }} + break; + case 560: +#line 825 "char_ref.rl" + {te = p+1;{ output->first = 0x22c1; {p++; goto _out; } }} + break; + case 561: +#line 826 "char_ref.rl" + {te = p+1;{ output->first = 0x2016; {p++; goto _out; } }} + break; + case 562: +#line 827 "char_ref.rl" + {te = p+1;{ output->first = 0x2016; {p++; goto _out; } }} + break; + case 563: +#line 828 "char_ref.rl" + {te = p+1;{ output->first = 0x2223; {p++; goto _out; } }} + break; + case 564: +#line 829 "char_ref.rl" + {te = p+1;{ output->first = 0x7c; {p++; goto _out; } }} + break; + case 565: +#line 830 "char_ref.rl" + {te = p+1;{ output->first = 0x2758; {p++; goto _out; } }} + break; + case 566: +#line 831 "char_ref.rl" + {te = p+1;{ output->first = 0x2240; {p++; goto _out; } }} + break; + case 567: +#line 832 "char_ref.rl" + {te = p+1;{ output->first = 0x200a; {p++; goto _out; } }} + break; + case 568: +#line 833 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d519; {p++; goto _out; } }} + break; + case 569: +#line 834 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d54d; {p++; goto _out; } }} + break; + case 570: +#line 835 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b1; {p++; goto _out; } }} + break; + case 571: +#line 836 "char_ref.rl" + {te = p+1;{ output->first = 0x22aa; {p++; goto _out; } }} + break; + case 572: +#line 837 "char_ref.rl" + {te = p+1;{ output->first = 0x0174; {p++; goto _out; } }} + break; + case 573: +#line 838 "char_ref.rl" + {te = p+1;{ output->first = 0x22c0; {p++; goto _out; } }} + break; + case 574: +#line 839 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d51a; {p++; goto _out; } }} + break; + case 575: +#line 840 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d54e; {p++; goto _out; } }} + break; + case 576: +#line 841 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b2; {p++; goto _out; } }} + break; + case 577: +#line 842 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d51b; {p++; goto _out; } }} + break; + case 578: +#line 843 "char_ref.rl" + {te = p+1;{ output->first = 0x039e; {p++; goto _out; } }} + break; + case 579: +#line 844 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d54f; {p++; goto _out; } }} + break; + case 580: +#line 845 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b3; {p++; goto _out; } }} + break; + case 581: +#line 846 "char_ref.rl" + {te = p+1;{ output->first = 0x042f; {p++; goto _out; } }} + break; + case 582: +#line 847 "char_ref.rl" + {te = p+1;{ output->first = 0x0407; {p++; goto _out; } }} + break; + case 583: +#line 848 "char_ref.rl" + {te = p+1;{ output->first = 0x042e; {p++; goto _out; } }} + break; + case 584: +#line 849 "char_ref.rl" + {te = p+1;{ output->first = 0xdd; {p++; goto _out; } }} + break; + case 585: +#line 851 "char_ref.rl" + {te = p+1;{ output->first = 0x0176; {p++; goto _out; } }} + break; + case 586: +#line 852 "char_ref.rl" + {te = p+1;{ output->first = 0x042b; {p++; goto _out; } }} + break; + case 587: +#line 853 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d51c; {p++; goto _out; } }} + break; + case 588: +#line 854 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d550; {p++; goto _out; } }} + break; + case 589: +#line 855 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b4; {p++; goto _out; } }} + break; + case 590: +#line 856 "char_ref.rl" + {te = p+1;{ output->first = 0x0178; {p++; goto _out; } }} + break; + case 591: +#line 857 "char_ref.rl" + {te = p+1;{ output->first = 0x0416; {p++; goto _out; } }} + break; + case 592: +#line 858 "char_ref.rl" + {te = p+1;{ output->first = 0x0179; {p++; goto _out; } }} + break; + case 593: +#line 859 "char_ref.rl" + {te = p+1;{ output->first = 0x017d; {p++; goto _out; } }} + break; + case 594: +#line 860 "char_ref.rl" + {te = p+1;{ output->first = 0x0417; {p++; goto _out; } }} + break; + case 595: +#line 861 "char_ref.rl" + {te = p+1;{ output->first = 0x017b; {p++; goto _out; } }} + break; + case 596: +#line 862 "char_ref.rl" + {te = p+1;{ output->first = 0x200b; {p++; goto _out; } }} + break; + case 597: +#line 863 "char_ref.rl" + {te = p+1;{ output->first = 0x0396; {p++; goto _out; } }} + break; + case 598: +#line 864 "char_ref.rl" + {te = p+1;{ output->first = 0x2128; {p++; goto _out; } }} + break; + case 599: +#line 865 "char_ref.rl" + {te = p+1;{ output->first = 0x2124; {p++; goto _out; } }} + break; + case 600: +#line 866 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b5; {p++; goto _out; } }} + break; + case 601: +#line 867 "char_ref.rl" + {te = p+1;{ output->first = 0xe1; {p++; goto _out; } }} + break; + case 602: +#line 869 "char_ref.rl" + {te = p+1;{ output->first = 0x0103; {p++; goto _out; } }} + break; + case 603: +#line 870 "char_ref.rl" + {te = p+1;{ output->first = 0x223e; {p++; goto _out; } }} + break; + case 604: +#line 871 "char_ref.rl" + {te = p+1;{ output->first = 0x223e; output->second = 0x0333; {p++; goto _out; } }} + break; + case 605: +#line 872 "char_ref.rl" + {te = p+1;{ output->first = 0x223f; {p++; goto _out; } }} + break; + case 606: +#line 873 "char_ref.rl" + {te = p+1;{ output->first = 0xe2; {p++; goto _out; } }} + break; + case 607: +#line 875 "char_ref.rl" + {te = p+1;{ output->first = 0xb4; {p++; goto _out; } }} + break; + case 608: +#line 877 "char_ref.rl" + {te = p+1;{ output->first = 0x0430; {p++; goto _out; } }} + break; + case 609: +#line 878 "char_ref.rl" + {te = p+1;{ output->first = 0xe6; {p++; goto _out; } }} + break; + case 610: +#line 880 "char_ref.rl" + {te = p+1;{ output->first = 0x2061; {p++; goto _out; } }} + break; + case 611: +#line 881 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d51e; {p++; goto _out; } }} + break; + case 612: +#line 882 "char_ref.rl" + {te = p+1;{ output->first = 0xe0; {p++; goto _out; } }} + break; + case 613: +#line 884 "char_ref.rl" + {te = p+1;{ output->first = 0x2135; {p++; goto _out; } }} + break; + case 614: +#line 885 "char_ref.rl" + {te = p+1;{ output->first = 0x2135; {p++; goto _out; } }} + break; + case 615: +#line 886 "char_ref.rl" + {te = p+1;{ output->first = 0x03b1; {p++; goto _out; } }} + break; + case 616: +#line 887 "char_ref.rl" + {te = p+1;{ output->first = 0x0101; {p++; goto _out; } }} + break; + case 617: +#line 888 "char_ref.rl" + {te = p+1;{ output->first = 0x2a3f; {p++; goto _out; } }} + break; + case 618: +#line 889 "char_ref.rl" + {te = p+1;{ output->first = 0x26; {p++; goto _out; } }} + break; + case 619: +#line 891 "char_ref.rl" + {te = p+1;{ output->first = 0x2227; {p++; goto _out; } }} + break; + case 620: +#line 892 "char_ref.rl" + {te = p+1;{ output->first = 0x2a55; {p++; goto _out; } }} + break; + case 621: +#line 893 "char_ref.rl" + {te = p+1;{ output->first = 0x2a5c; {p++; goto _out; } }} + break; + case 622: +#line 894 "char_ref.rl" + {te = p+1;{ output->first = 0x2a58; {p++; goto _out; } }} + break; + case 623: +#line 895 "char_ref.rl" + {te = p+1;{ output->first = 0x2a5a; {p++; goto _out; } }} + break; + case 624: +#line 896 "char_ref.rl" + {te = p+1;{ output->first = 0x2220; {p++; goto _out; } }} + break; + case 625: +#line 897 "char_ref.rl" + {te = p+1;{ output->first = 0x29a4; {p++; goto _out; } }} + break; + case 626: +#line 898 "char_ref.rl" + {te = p+1;{ output->first = 0x2220; {p++; goto _out; } }} + break; + case 627: +#line 899 "char_ref.rl" + {te = p+1;{ output->first = 0x2221; {p++; goto _out; } }} + break; + case 628: +#line 900 "char_ref.rl" + {te = p+1;{ output->first = 0x29a8; {p++; goto _out; } }} + break; + case 629: +#line 901 "char_ref.rl" + {te = p+1;{ output->first = 0x29a9; {p++; goto _out; } }} + break; + case 630: +#line 902 "char_ref.rl" + {te = p+1;{ output->first = 0x29aa; {p++; goto _out; } }} + break; + case 631: +#line 903 "char_ref.rl" + {te = p+1;{ output->first = 0x29ab; {p++; goto _out; } }} + break; + case 632: +#line 904 "char_ref.rl" + {te = p+1;{ output->first = 0x29ac; {p++; goto _out; } }} + break; + case 633: +#line 905 "char_ref.rl" + {te = p+1;{ output->first = 0x29ad; {p++; goto _out; } }} + break; + case 634: +#line 906 "char_ref.rl" + {te = p+1;{ output->first = 0x29ae; {p++; goto _out; } }} + break; + case 635: +#line 907 "char_ref.rl" + {te = p+1;{ output->first = 0x29af; {p++; goto _out; } }} + break; + case 636: +#line 908 "char_ref.rl" + {te = p+1;{ output->first = 0x221f; {p++; goto _out; } }} + break; + case 637: +#line 909 "char_ref.rl" + {te = p+1;{ output->first = 0x22be; {p++; goto _out; } }} + break; + case 638: +#line 910 "char_ref.rl" + {te = p+1;{ output->first = 0x299d; {p++; goto _out; } }} + break; + case 639: +#line 911 "char_ref.rl" + {te = p+1;{ output->first = 0x2222; {p++; goto _out; } }} + break; + case 640: +#line 912 "char_ref.rl" + {te = p+1;{ output->first = 0xc5; {p++; goto _out; } }} + break; + case 641: +#line 913 "char_ref.rl" + {te = p+1;{ output->first = 0x237c; {p++; goto _out; } }} + break; + case 642: +#line 914 "char_ref.rl" + {te = p+1;{ output->first = 0x0105; {p++; goto _out; } }} + break; + case 643: +#line 915 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d552; {p++; goto _out; } }} + break; + case 644: +#line 916 "char_ref.rl" + {te = p+1;{ output->first = 0x2248; {p++; goto _out; } }} + break; + case 645: +#line 917 "char_ref.rl" + {te = p+1;{ output->first = 0x2a70; {p++; goto _out; } }} + break; + case 646: +#line 918 "char_ref.rl" + {te = p+1;{ output->first = 0x2a6f; {p++; goto _out; } }} + break; + case 647: +#line 919 "char_ref.rl" + {te = p+1;{ output->first = 0x224a; {p++; goto _out; } }} + break; + case 648: +#line 920 "char_ref.rl" + {te = p+1;{ output->first = 0x224b; {p++; goto _out; } }} + break; + case 649: +#line 921 "char_ref.rl" + {te = p+1;{ output->first = 0x27; {p++; goto _out; } }} + break; + case 650: +#line 922 "char_ref.rl" + {te = p+1;{ output->first = 0x2248; {p++; goto _out; } }} + break; + case 651: +#line 923 "char_ref.rl" + {te = p+1;{ output->first = 0x224a; {p++; goto _out; } }} + break; + case 652: +#line 924 "char_ref.rl" + {te = p+1;{ output->first = 0xe5; {p++; goto _out; } }} + break; + case 653: +#line 926 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b6; {p++; goto _out; } }} + break; + case 654: +#line 927 "char_ref.rl" + {te = p+1;{ output->first = 0x2a; {p++; goto _out; } }} + break; + case 655: +#line 928 "char_ref.rl" + {te = p+1;{ output->first = 0x2248; {p++; goto _out; } }} + break; + case 656: +#line 929 "char_ref.rl" + {te = p+1;{ output->first = 0x224d; {p++; goto _out; } }} + break; + case 657: +#line 930 "char_ref.rl" + {te = p+1;{ output->first = 0xe3; {p++; goto _out; } }} + break; + case 658: +#line 932 "char_ref.rl" + {te = p+1;{ output->first = 0xe4; {p++; goto _out; } }} + break; + case 659: +#line 934 "char_ref.rl" + {te = p+1;{ output->first = 0x2233; {p++; goto _out; } }} + break; + case 660: +#line 935 "char_ref.rl" + {te = p+1;{ output->first = 0x2a11; {p++; goto _out; } }} + break; + case 661: +#line 936 "char_ref.rl" + {te = p+1;{ output->first = 0x2aed; {p++; goto _out; } }} + break; + case 662: +#line 937 "char_ref.rl" + {te = p+1;{ output->first = 0x224c; {p++; goto _out; } }} + break; + case 663: +#line 938 "char_ref.rl" + {te = p+1;{ output->first = 0x03f6; {p++; goto _out; } }} + break; + case 664: +#line 939 "char_ref.rl" + {te = p+1;{ output->first = 0x2035; {p++; goto _out; } }} + break; + case 665: +#line 940 "char_ref.rl" + {te = p+1;{ output->first = 0x223d; {p++; goto _out; } }} + break; + case 666: +#line 941 "char_ref.rl" + {te = p+1;{ output->first = 0x22cd; {p++; goto _out; } }} + break; + case 667: +#line 942 "char_ref.rl" + {te = p+1;{ output->first = 0x22bd; {p++; goto _out; } }} + break; + case 668: +#line 943 "char_ref.rl" + {te = p+1;{ output->first = 0x2305; {p++; goto _out; } }} + break; + case 669: +#line 944 "char_ref.rl" + {te = p+1;{ output->first = 0x2305; {p++; goto _out; } }} + break; + case 670: +#line 945 "char_ref.rl" + {te = p+1;{ output->first = 0x23b5; {p++; goto _out; } }} + break; + case 671: +#line 946 "char_ref.rl" + {te = p+1;{ output->first = 0x23b6; {p++; goto _out; } }} + break; + case 672: +#line 947 "char_ref.rl" + {te = p+1;{ output->first = 0x224c; {p++; goto _out; } }} + break; + case 673: +#line 948 "char_ref.rl" + {te = p+1;{ output->first = 0x0431; {p++; goto _out; } }} + break; + case 674: +#line 949 "char_ref.rl" + {te = p+1;{ output->first = 0x201e; {p++; goto _out; } }} + break; + case 675: +#line 950 "char_ref.rl" + {te = p+1;{ output->first = 0x2235; {p++; goto _out; } }} + break; + case 676: +#line 951 "char_ref.rl" + {te = p+1;{ output->first = 0x2235; {p++; goto _out; } }} + break; + case 677: +#line 952 "char_ref.rl" + {te = p+1;{ output->first = 0x29b0; {p++; goto _out; } }} + break; + case 678: +#line 953 "char_ref.rl" + {te = p+1;{ output->first = 0x03f6; {p++; goto _out; } }} + break; + case 679: +#line 954 "char_ref.rl" + {te = p+1;{ output->first = 0x212c; {p++; goto _out; } }} + break; + case 680: +#line 955 "char_ref.rl" + {te = p+1;{ output->first = 0x03b2; {p++; goto _out; } }} + break; + case 681: +#line 956 "char_ref.rl" + {te = p+1;{ output->first = 0x2136; {p++; goto _out; } }} + break; + case 682: +#line 957 "char_ref.rl" + {te = p+1;{ output->first = 0x226c; {p++; goto _out; } }} + break; + case 683: +#line 958 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d51f; {p++; goto _out; } }} + break; + case 684: +#line 959 "char_ref.rl" + {te = p+1;{ output->first = 0x22c2; {p++; goto _out; } }} + break; + case 685: +#line 960 "char_ref.rl" + {te = p+1;{ output->first = 0x25ef; {p++; goto _out; } }} + break; + case 686: +#line 961 "char_ref.rl" + {te = p+1;{ output->first = 0x22c3; {p++; goto _out; } }} + break; + case 687: +#line 962 "char_ref.rl" + {te = p+1;{ output->first = 0x2a00; {p++; goto _out; } }} + break; + case 688: +#line 963 "char_ref.rl" + {te = p+1;{ output->first = 0x2a01; {p++; goto _out; } }} + break; + case 689: +#line 964 "char_ref.rl" + {te = p+1;{ output->first = 0x2a02; {p++; goto _out; } }} + break; + case 690: +#line 965 "char_ref.rl" + {te = p+1;{ output->first = 0x2a06; {p++; goto _out; } }} + break; + case 691: +#line 966 "char_ref.rl" + {te = p+1;{ output->first = 0x2605; {p++; goto _out; } }} + break; + case 692: +#line 967 "char_ref.rl" + {te = p+1;{ output->first = 0x25bd; {p++; goto _out; } }} + break; + case 693: +#line 968 "char_ref.rl" + {te = p+1;{ output->first = 0x25b3; {p++; goto _out; } }} + break; + case 694: +#line 969 "char_ref.rl" + {te = p+1;{ output->first = 0x2a04; {p++; goto _out; } }} + break; + case 695: +#line 970 "char_ref.rl" + {te = p+1;{ output->first = 0x22c1; {p++; goto _out; } }} + break; + case 696: +#line 971 "char_ref.rl" + {te = p+1;{ output->first = 0x22c0; {p++; goto _out; } }} + break; + case 697: +#line 972 "char_ref.rl" + {te = p+1;{ output->first = 0x290d; {p++; goto _out; } }} + break; + case 698: +#line 973 "char_ref.rl" + {te = p+1;{ output->first = 0x29eb; {p++; goto _out; } }} + break; + case 699: +#line 974 "char_ref.rl" + {te = p+1;{ output->first = 0x25aa; {p++; goto _out; } }} + break; + case 700: +#line 975 "char_ref.rl" + {te = p+1;{ output->first = 0x25b4; {p++; goto _out; } }} + break; + case 701: +#line 976 "char_ref.rl" + {te = p+1;{ output->first = 0x25be; {p++; goto _out; } }} + break; + case 702: +#line 977 "char_ref.rl" + {te = p+1;{ output->first = 0x25c2; {p++; goto _out; } }} + break; + case 703: +#line 978 "char_ref.rl" + {te = p+1;{ output->first = 0x25b8; {p++; goto _out; } }} + break; + case 704: +#line 979 "char_ref.rl" + {te = p+1;{ output->first = 0x2423; {p++; goto _out; } }} + break; + case 705: +#line 980 "char_ref.rl" + {te = p+1;{ output->first = 0x2592; {p++; goto _out; } }} + break; + case 706: +#line 981 "char_ref.rl" + {te = p+1;{ output->first = 0x2591; {p++; goto _out; } }} + break; + case 707: +#line 982 "char_ref.rl" + {te = p+1;{ output->first = 0x2593; {p++; goto _out; } }} + break; + case 708: +#line 983 "char_ref.rl" + {te = p+1;{ output->first = 0x2588; {p++; goto _out; } }} + break; + case 709: +#line 984 "char_ref.rl" + {te = p+1;{ output->first = 0x3d; output->second = 0x20e5; {p++; goto _out; } }} + break; + case 710: +#line 985 "char_ref.rl" + {te = p+1;{ output->first = 0x2261; output->second = 0x20e5; {p++; goto _out; } }} + break; + case 711: +#line 986 "char_ref.rl" + {te = p+1;{ output->first = 0x2310; {p++; goto _out; } }} + break; + case 712: +#line 987 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d553; {p++; goto _out; } }} + break; + case 713: +#line 988 "char_ref.rl" + {te = p+1;{ output->first = 0x22a5; {p++; goto _out; } }} + break; + case 714: +#line 989 "char_ref.rl" + {te = p+1;{ output->first = 0x22a5; {p++; goto _out; } }} + break; + case 715: +#line 990 "char_ref.rl" + {te = p+1;{ output->first = 0x22c8; {p++; goto _out; } }} + break; + case 716: +#line 991 "char_ref.rl" + {te = p+1;{ output->first = 0x2557; {p++; goto _out; } }} + break; + case 717: +#line 992 "char_ref.rl" + {te = p+1;{ output->first = 0x2554; {p++; goto _out; } }} + break; + case 718: +#line 993 "char_ref.rl" + {te = p+1;{ output->first = 0x2556; {p++; goto _out; } }} + break; + case 719: +#line 994 "char_ref.rl" + {te = p+1;{ output->first = 0x2553; {p++; goto _out; } }} + break; + case 720: +#line 995 "char_ref.rl" + {te = p+1;{ output->first = 0x2550; {p++; goto _out; } }} + break; + case 721: +#line 996 "char_ref.rl" + {te = p+1;{ output->first = 0x2566; {p++; goto _out; } }} + break; + case 722: +#line 997 "char_ref.rl" + {te = p+1;{ output->first = 0x2569; {p++; goto _out; } }} + break; + case 723: +#line 998 "char_ref.rl" + {te = p+1;{ output->first = 0x2564; {p++; goto _out; } }} + break; + case 724: +#line 999 "char_ref.rl" + {te = p+1;{ output->first = 0x2567; {p++; goto _out; } }} + break; + case 725: +#line 1000 "char_ref.rl" + {te = p+1;{ output->first = 0x255d; {p++; goto _out; } }} + break; + case 726: +#line 1001 "char_ref.rl" + {te = p+1;{ output->first = 0x255a; {p++; goto _out; } }} + break; + case 727: +#line 1002 "char_ref.rl" + {te = p+1;{ output->first = 0x255c; {p++; goto _out; } }} + break; + case 728: +#line 1003 "char_ref.rl" + {te = p+1;{ output->first = 0x2559; {p++; goto _out; } }} + break; + case 729: +#line 1004 "char_ref.rl" + {te = p+1;{ output->first = 0x2551; {p++; goto _out; } }} + break; + case 730: +#line 1005 "char_ref.rl" + {te = p+1;{ output->first = 0x256c; {p++; goto _out; } }} + break; + case 731: +#line 1006 "char_ref.rl" + {te = p+1;{ output->first = 0x2563; {p++; goto _out; } }} + break; + case 732: +#line 1007 "char_ref.rl" + {te = p+1;{ output->first = 0x2560; {p++; goto _out; } }} + break; + case 733: +#line 1008 "char_ref.rl" + {te = p+1;{ output->first = 0x256b; {p++; goto _out; } }} + break; + case 734: +#line 1009 "char_ref.rl" + {te = p+1;{ output->first = 0x2562; {p++; goto _out; } }} + break; + case 735: +#line 1010 "char_ref.rl" + {te = p+1;{ output->first = 0x255f; {p++; goto _out; } }} + break; + case 736: +#line 1011 "char_ref.rl" + {te = p+1;{ output->first = 0x29c9; {p++; goto _out; } }} + break; + case 737: +#line 1012 "char_ref.rl" + {te = p+1;{ output->first = 0x2555; {p++; goto _out; } }} + break; + case 738: +#line 1013 "char_ref.rl" + {te = p+1;{ output->first = 0x2552; {p++; goto _out; } }} + break; + case 739: +#line 1014 "char_ref.rl" + {te = p+1;{ output->first = 0x2510; {p++; goto _out; } }} + break; + case 740: +#line 1015 "char_ref.rl" + {te = p+1;{ output->first = 0x250c; {p++; goto _out; } }} + break; + case 741: +#line 1016 "char_ref.rl" + {te = p+1;{ output->first = 0x2500; {p++; goto _out; } }} + break; + case 742: +#line 1017 "char_ref.rl" + {te = p+1;{ output->first = 0x2565; {p++; goto _out; } }} + break; + case 743: +#line 1018 "char_ref.rl" + {te = p+1;{ output->first = 0x2568; {p++; goto _out; } }} + break; + case 744: +#line 1019 "char_ref.rl" + {te = p+1;{ output->first = 0x252c; {p++; goto _out; } }} + break; + case 745: +#line 1020 "char_ref.rl" + {te = p+1;{ output->first = 0x2534; {p++; goto _out; } }} + break; + case 746: +#line 1021 "char_ref.rl" + {te = p+1;{ output->first = 0x229f; {p++; goto _out; } }} + break; + case 747: +#line 1022 "char_ref.rl" + {te = p+1;{ output->first = 0x229e; {p++; goto _out; } }} + break; + case 748: +#line 1023 "char_ref.rl" + {te = p+1;{ output->first = 0x22a0; {p++; goto _out; } }} + break; + case 749: +#line 1024 "char_ref.rl" + {te = p+1;{ output->first = 0x255b; {p++; goto _out; } }} + break; + case 750: +#line 1025 "char_ref.rl" + {te = p+1;{ output->first = 0x2558; {p++; goto _out; } }} + break; + case 751: +#line 1026 "char_ref.rl" + {te = p+1;{ output->first = 0x2518; {p++; goto _out; } }} + break; + case 752: +#line 1027 "char_ref.rl" + {te = p+1;{ output->first = 0x2514; {p++; goto _out; } }} + break; + case 753: +#line 1028 "char_ref.rl" + {te = p+1;{ output->first = 0x2502; {p++; goto _out; } }} + break; + case 754: +#line 1029 "char_ref.rl" + {te = p+1;{ output->first = 0x256a; {p++; goto _out; } }} + break; + case 755: +#line 1030 "char_ref.rl" + {te = p+1;{ output->first = 0x2561; {p++; goto _out; } }} + break; + case 756: +#line 1031 "char_ref.rl" + {te = p+1;{ output->first = 0x255e; {p++; goto _out; } }} + break; + case 757: +#line 1032 "char_ref.rl" + {te = p+1;{ output->first = 0x253c; {p++; goto _out; } }} + break; + case 758: +#line 1033 "char_ref.rl" + {te = p+1;{ output->first = 0x2524; {p++; goto _out; } }} + break; + case 759: +#line 1034 "char_ref.rl" + {te = p+1;{ output->first = 0x251c; {p++; goto _out; } }} + break; + case 760: +#line 1035 "char_ref.rl" + {te = p+1;{ output->first = 0x2035; {p++; goto _out; } }} + break; + case 761: +#line 1036 "char_ref.rl" + {te = p+1;{ output->first = 0x02d8; {p++; goto _out; } }} + break; + case 762: +#line 1037 "char_ref.rl" + {te = p+1;{ output->first = 0xa6; {p++; goto _out; } }} + break; + case 763: +#line 1039 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b7; {p++; goto _out; } }} + break; + case 764: +#line 1040 "char_ref.rl" + {te = p+1;{ output->first = 0x204f; {p++; goto _out; } }} + break; + case 765: +#line 1041 "char_ref.rl" + {te = p+1;{ output->first = 0x223d; {p++; goto _out; } }} + break; + case 766: +#line 1042 "char_ref.rl" + {te = p+1;{ output->first = 0x22cd; {p++; goto _out; } }} + break; + case 767: +#line 1043 "char_ref.rl" + {te = p+1;{ output->first = 0x5c; {p++; goto _out; } }} + break; + case 768: +#line 1044 "char_ref.rl" + {te = p+1;{ output->first = 0x29c5; {p++; goto _out; } }} + break; + case 769: +#line 1045 "char_ref.rl" + {te = p+1;{ output->first = 0x27c8; {p++; goto _out; } }} + break; + case 770: +#line 1046 "char_ref.rl" + {te = p+1;{ output->first = 0x2022; {p++; goto _out; } }} + break; + case 771: +#line 1047 "char_ref.rl" + {te = p+1;{ output->first = 0x2022; {p++; goto _out; } }} + break; + case 772: +#line 1048 "char_ref.rl" + {te = p+1;{ output->first = 0x224e; {p++; goto _out; } }} + break; + case 773: +#line 1049 "char_ref.rl" + {te = p+1;{ output->first = 0x2aae; {p++; goto _out; } }} + break; + case 774: +#line 1050 "char_ref.rl" + {te = p+1;{ output->first = 0x224f; {p++; goto _out; } }} + break; + case 775: +#line 1051 "char_ref.rl" + {te = p+1;{ output->first = 0x224f; {p++; goto _out; } }} + break; + case 776: +#line 1052 "char_ref.rl" + {te = p+1;{ output->first = 0x0107; {p++; goto _out; } }} + break; + case 777: +#line 1053 "char_ref.rl" + {te = p+1;{ output->first = 0x2229; {p++; goto _out; } }} + break; + case 778: +#line 1054 "char_ref.rl" + {te = p+1;{ output->first = 0x2a44; {p++; goto _out; } }} + break; + case 779: +#line 1055 "char_ref.rl" + {te = p+1;{ output->first = 0x2a49; {p++; goto _out; } }} + break; + case 780: +#line 1056 "char_ref.rl" + {te = p+1;{ output->first = 0x2a4b; {p++; goto _out; } }} + break; + case 781: +#line 1057 "char_ref.rl" + {te = p+1;{ output->first = 0x2a47; {p++; goto _out; } }} + break; + case 782: +#line 1058 "char_ref.rl" + {te = p+1;{ output->first = 0x2a40; {p++; goto _out; } }} + break; + case 783: +#line 1059 "char_ref.rl" + {te = p+1;{ output->first = 0x2229; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 784: +#line 1060 "char_ref.rl" + {te = p+1;{ output->first = 0x2041; {p++; goto _out; } }} + break; + case 785: +#line 1061 "char_ref.rl" + {te = p+1;{ output->first = 0x02c7; {p++; goto _out; } }} + break; + case 786: +#line 1062 "char_ref.rl" + {te = p+1;{ output->first = 0x2a4d; {p++; goto _out; } }} + break; + case 787: +#line 1063 "char_ref.rl" + {te = p+1;{ output->first = 0x010d; {p++; goto _out; } }} + break; + case 788: +#line 1064 "char_ref.rl" + {te = p+1;{ output->first = 0xe7; {p++; goto _out; } }} + break; + case 789: +#line 1066 "char_ref.rl" + {te = p+1;{ output->first = 0x0109; {p++; goto _out; } }} + break; + case 790: +#line 1067 "char_ref.rl" + {te = p+1;{ output->first = 0x2a4c; {p++; goto _out; } }} + break; + case 791: +#line 1068 "char_ref.rl" + {te = p+1;{ output->first = 0x2a50; {p++; goto _out; } }} + break; + case 792: +#line 1069 "char_ref.rl" + {te = p+1;{ output->first = 0x010b; {p++; goto _out; } }} + break; + case 793: +#line 1070 "char_ref.rl" + {te = p+1;{ output->first = 0xb8; {p++; goto _out; } }} + break; + case 794: +#line 1072 "char_ref.rl" + {te = p+1;{ output->first = 0x29b2; {p++; goto _out; } }} + break; + case 795: +#line 1073 "char_ref.rl" + {te = p+1;{ output->first = 0xa2; {p++; goto _out; } }} + break; + case 796: +#line 1075 "char_ref.rl" + {te = p+1;{ output->first = 0xb7; {p++; goto _out; } }} + break; + case 797: +#line 1076 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d520; {p++; goto _out; } }} + break; + case 798: +#line 1077 "char_ref.rl" + {te = p+1;{ output->first = 0x0447; {p++; goto _out; } }} + break; + case 799: +#line 1078 "char_ref.rl" + {te = p+1;{ output->first = 0x2713; {p++; goto _out; } }} + break; + case 800: +#line 1079 "char_ref.rl" + {te = p+1;{ output->first = 0x2713; {p++; goto _out; } }} + break; + case 801: +#line 1080 "char_ref.rl" + {te = p+1;{ output->first = 0x03c7; {p++; goto _out; } }} + break; + case 802: +#line 1081 "char_ref.rl" + {te = p+1;{ output->first = 0x25cb; {p++; goto _out; } }} + break; + case 803: +#line 1082 "char_ref.rl" + {te = p+1;{ output->first = 0x29c3; {p++; goto _out; } }} + break; + case 804: +#line 1083 "char_ref.rl" + {te = p+1;{ output->first = 0x02c6; {p++; goto _out; } }} + break; + case 805: +#line 1084 "char_ref.rl" + {te = p+1;{ output->first = 0x2257; {p++; goto _out; } }} + break; + case 806: +#line 1085 "char_ref.rl" + {te = p+1;{ output->first = 0x21ba; {p++; goto _out; } }} + break; + case 807: +#line 1086 "char_ref.rl" + {te = p+1;{ output->first = 0x21bb; {p++; goto _out; } }} + break; + case 808: +#line 1087 "char_ref.rl" + {te = p+1;{ output->first = 0xae; {p++; goto _out; } }} + break; + case 809: +#line 1088 "char_ref.rl" + {te = p+1;{ output->first = 0x24c8; {p++; goto _out; } }} + break; + case 810: +#line 1089 "char_ref.rl" + {te = p+1;{ output->first = 0x229b; {p++; goto _out; } }} + break; + case 811: +#line 1090 "char_ref.rl" + {te = p+1;{ output->first = 0x229a; {p++; goto _out; } }} + break; + case 812: +#line 1091 "char_ref.rl" + {te = p+1;{ output->first = 0x229d; {p++; goto _out; } }} + break; + case 813: +#line 1092 "char_ref.rl" + {te = p+1;{ output->first = 0x2257; {p++; goto _out; } }} + break; + case 814: +#line 1093 "char_ref.rl" + {te = p+1;{ output->first = 0x2a10; {p++; goto _out; } }} + break; + case 815: +#line 1094 "char_ref.rl" + {te = p+1;{ output->first = 0x2aef; {p++; goto _out; } }} + break; + case 816: +#line 1095 "char_ref.rl" + {te = p+1;{ output->first = 0x29c2; {p++; goto _out; } }} + break; + case 817: +#line 1096 "char_ref.rl" + {te = p+1;{ output->first = 0x2663; {p++; goto _out; } }} + break; + case 818: +#line 1097 "char_ref.rl" + {te = p+1;{ output->first = 0x2663; {p++; goto _out; } }} + break; + case 819: +#line 1098 "char_ref.rl" + {te = p+1;{ output->first = 0x3a; {p++; goto _out; } }} + break; + case 820: +#line 1099 "char_ref.rl" + {te = p+1;{ output->first = 0x2254; {p++; goto _out; } }} + break; + case 821: +#line 1100 "char_ref.rl" + {te = p+1;{ output->first = 0x2254; {p++; goto _out; } }} + break; + case 822: +#line 1101 "char_ref.rl" + {te = p+1;{ output->first = 0x2c; {p++; goto _out; } }} + break; + case 823: +#line 1102 "char_ref.rl" + {te = p+1;{ output->first = 0x40; {p++; goto _out; } }} + break; + case 824: +#line 1103 "char_ref.rl" + {te = p+1;{ output->first = 0x2201; {p++; goto _out; } }} + break; + case 825: +#line 1104 "char_ref.rl" + {te = p+1;{ output->first = 0x2218; {p++; goto _out; } }} + break; + case 826: +#line 1105 "char_ref.rl" + {te = p+1;{ output->first = 0x2201; {p++; goto _out; } }} + break; + case 827: +#line 1106 "char_ref.rl" + {te = p+1;{ output->first = 0x2102; {p++; goto _out; } }} + break; + case 828: +#line 1107 "char_ref.rl" + {te = p+1;{ output->first = 0x2245; {p++; goto _out; } }} + break; + case 829: +#line 1108 "char_ref.rl" + {te = p+1;{ output->first = 0x2a6d; {p++; goto _out; } }} + break; + case 830: +#line 1109 "char_ref.rl" + {te = p+1;{ output->first = 0x222e; {p++; goto _out; } }} + break; + case 831: +#line 1110 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d554; {p++; goto _out; } }} + break; + case 832: +#line 1111 "char_ref.rl" + {te = p+1;{ output->first = 0x2210; {p++; goto _out; } }} + break; + case 833: +#line 1112 "char_ref.rl" + {te = p+1;{ output->first = 0xa9; {p++; goto _out; } }} + break; + case 834: +#line 1114 "char_ref.rl" + {te = p+1;{ output->first = 0x2117; {p++; goto _out; } }} + break; + case 835: +#line 1115 "char_ref.rl" + {te = p+1;{ output->first = 0x21b5; {p++; goto _out; } }} + break; + case 836: +#line 1116 "char_ref.rl" + {te = p+1;{ output->first = 0x2717; {p++; goto _out; } }} + break; + case 837: +#line 1117 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b8; {p++; goto _out; } }} + break; + case 838: +#line 1118 "char_ref.rl" + {te = p+1;{ output->first = 0x2acf; {p++; goto _out; } }} + break; + case 839: +#line 1119 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad1; {p++; goto _out; } }} + break; + case 840: +#line 1120 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad0; {p++; goto _out; } }} + break; + case 841: +#line 1121 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad2; {p++; goto _out; } }} + break; + case 842: +#line 1122 "char_ref.rl" + {te = p+1;{ output->first = 0x22ef; {p++; goto _out; } }} + break; + case 843: +#line 1123 "char_ref.rl" + {te = p+1;{ output->first = 0x2938; {p++; goto _out; } }} + break; + case 844: +#line 1124 "char_ref.rl" + {te = p+1;{ output->first = 0x2935; {p++; goto _out; } }} + break; + case 845: +#line 1125 "char_ref.rl" + {te = p+1;{ output->first = 0x22de; {p++; goto _out; } }} + break; + case 846: +#line 1126 "char_ref.rl" + {te = p+1;{ output->first = 0x22df; {p++; goto _out; } }} + break; + case 847: +#line 1127 "char_ref.rl" + {te = p+1;{ output->first = 0x21b6; {p++; goto _out; } }} + break; + case 848: +#line 1128 "char_ref.rl" + {te = p+1;{ output->first = 0x293d; {p++; goto _out; } }} + break; + case 849: +#line 1129 "char_ref.rl" + {te = p+1;{ output->first = 0x222a; {p++; goto _out; } }} + break; + case 850: +#line 1130 "char_ref.rl" + {te = p+1;{ output->first = 0x2a48; {p++; goto _out; } }} + break; + case 851: +#line 1131 "char_ref.rl" + {te = p+1;{ output->first = 0x2a46; {p++; goto _out; } }} + break; + case 852: +#line 1132 "char_ref.rl" + {te = p+1;{ output->first = 0x2a4a; {p++; goto _out; } }} + break; + case 853: +#line 1133 "char_ref.rl" + {te = p+1;{ output->first = 0x228d; {p++; goto _out; } }} + break; + case 854: +#line 1134 "char_ref.rl" + {te = p+1;{ output->first = 0x2a45; {p++; goto _out; } }} + break; + case 855: +#line 1135 "char_ref.rl" + {te = p+1;{ output->first = 0x222a; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 856: +#line 1136 "char_ref.rl" + {te = p+1;{ output->first = 0x21b7; {p++; goto _out; } }} + break; + case 857: +#line 1137 "char_ref.rl" + {te = p+1;{ output->first = 0x293c; {p++; goto _out; } }} + break; + case 858: +#line 1138 "char_ref.rl" + {te = p+1;{ output->first = 0x22de; {p++; goto _out; } }} + break; + case 859: +#line 1139 "char_ref.rl" + {te = p+1;{ output->first = 0x22df; {p++; goto _out; } }} + break; + case 860: +#line 1140 "char_ref.rl" + {te = p+1;{ output->first = 0x22ce; {p++; goto _out; } }} + break; + case 861: +#line 1141 "char_ref.rl" + {te = p+1;{ output->first = 0x22cf; {p++; goto _out; } }} + break; + case 862: +#line 1142 "char_ref.rl" + {te = p+1;{ output->first = 0xa4; {p++; goto _out; } }} + break; + case 863: +#line 1144 "char_ref.rl" + {te = p+1;{ output->first = 0x21b6; {p++; goto _out; } }} + break; + case 864: +#line 1145 "char_ref.rl" + {te = p+1;{ output->first = 0x21b7; {p++; goto _out; } }} + break; + case 865: +#line 1146 "char_ref.rl" + {te = p+1;{ output->first = 0x22ce; {p++; goto _out; } }} + break; + case 866: +#line 1147 "char_ref.rl" + {te = p+1;{ output->first = 0x22cf; {p++; goto _out; } }} + break; + case 867: +#line 1148 "char_ref.rl" + {te = p+1;{ output->first = 0x2232; {p++; goto _out; } }} + break; + case 868: +#line 1149 "char_ref.rl" + {te = p+1;{ output->first = 0x2231; {p++; goto _out; } }} + break; + case 869: +#line 1150 "char_ref.rl" + {te = p+1;{ output->first = 0x232d; {p++; goto _out; } }} + break; + case 870: +#line 1151 "char_ref.rl" + {te = p+1;{ output->first = 0x21d3; {p++; goto _out; } }} + break; + case 871: +#line 1152 "char_ref.rl" + {te = p+1;{ output->first = 0x2965; {p++; goto _out; } }} + break; + case 872: +#line 1153 "char_ref.rl" + {te = p+1;{ output->first = 0x2020; {p++; goto _out; } }} + break; + case 873: +#line 1154 "char_ref.rl" + {te = p+1;{ output->first = 0x2138; {p++; goto _out; } }} + break; + case 874: +#line 1155 "char_ref.rl" + {te = p+1;{ output->first = 0x2193; {p++; goto _out; } }} + break; + case 875: +#line 1156 "char_ref.rl" + {te = p+1;{ output->first = 0x2010; {p++; goto _out; } }} + break; + case 876: +#line 1157 "char_ref.rl" + {te = p+1;{ output->first = 0x22a3; {p++; goto _out; } }} + break; + case 877: +#line 1158 "char_ref.rl" + {te = p+1;{ output->first = 0x290f; {p++; goto _out; } }} + break; + case 878: +#line 1159 "char_ref.rl" + {te = p+1;{ output->first = 0x02dd; {p++; goto _out; } }} + break; + case 879: +#line 1160 "char_ref.rl" + {te = p+1;{ output->first = 0x010f; {p++; goto _out; } }} + break; + case 880: +#line 1161 "char_ref.rl" + {te = p+1;{ output->first = 0x0434; {p++; goto _out; } }} + break; + case 881: +#line 1162 "char_ref.rl" + {te = p+1;{ output->first = 0x2146; {p++; goto _out; } }} + break; + case 882: +#line 1163 "char_ref.rl" + {te = p+1;{ output->first = 0x2021; {p++; goto _out; } }} + break; + case 883: +#line 1164 "char_ref.rl" + {te = p+1;{ output->first = 0x21ca; {p++; goto _out; } }} + break; + case 884: +#line 1165 "char_ref.rl" + {te = p+1;{ output->first = 0x2a77; {p++; goto _out; } }} + break; + case 885: +#line 1166 "char_ref.rl" + {te = p+1;{ output->first = 0xb0; {p++; goto _out; } }} + break; + case 886: +#line 1168 "char_ref.rl" + {te = p+1;{ output->first = 0x03b4; {p++; goto _out; } }} + break; + case 887: +#line 1169 "char_ref.rl" + {te = p+1;{ output->first = 0x29b1; {p++; goto _out; } }} + break; + case 888: +#line 1170 "char_ref.rl" + {te = p+1;{ output->first = 0x297f; {p++; goto _out; } }} + break; + case 889: +#line 1171 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d521; {p++; goto _out; } }} + break; + case 890: +#line 1172 "char_ref.rl" + {te = p+1;{ output->first = 0x21c3; {p++; goto _out; } }} + break; + case 891: +#line 1173 "char_ref.rl" + {te = p+1;{ output->first = 0x21c2; {p++; goto _out; } }} + break; + case 892: +#line 1174 "char_ref.rl" + {te = p+1;{ output->first = 0x22c4; {p++; goto _out; } }} + break; + case 893: +#line 1175 "char_ref.rl" + {te = p+1;{ output->first = 0x22c4; {p++; goto _out; } }} + break; + case 894: +#line 1176 "char_ref.rl" + {te = p+1;{ output->first = 0x2666; {p++; goto _out; } }} + break; + case 895: +#line 1177 "char_ref.rl" + {te = p+1;{ output->first = 0x2666; {p++; goto _out; } }} + break; + case 896: +#line 1178 "char_ref.rl" + {te = p+1;{ output->first = 0xa8; {p++; goto _out; } }} + break; + case 897: +#line 1179 "char_ref.rl" + {te = p+1;{ output->first = 0x03dd; {p++; goto _out; } }} + break; + case 898: +#line 1180 "char_ref.rl" + {te = p+1;{ output->first = 0x22f2; {p++; goto _out; } }} + break; + case 899: +#line 1181 "char_ref.rl" + {te = p+1;{ output->first = 0xf7; {p++; goto _out; } }} + break; + case 900: +#line 1182 "char_ref.rl" + {te = p+1;{ output->first = 0xf7; {p++; goto _out; } }} + break; + case 901: +#line 1184 "char_ref.rl" + {te = p+1;{ output->first = 0x22c7; {p++; goto _out; } }} + break; + case 902: +#line 1185 "char_ref.rl" + {te = p+1;{ output->first = 0x22c7; {p++; goto _out; } }} + break; + case 903: +#line 1186 "char_ref.rl" + {te = p+1;{ output->first = 0x0452; {p++; goto _out; } }} + break; + case 904: +#line 1187 "char_ref.rl" + {te = p+1;{ output->first = 0x231e; {p++; goto _out; } }} + break; + case 905: +#line 1188 "char_ref.rl" + {te = p+1;{ output->first = 0x230d; {p++; goto _out; } }} + break; + case 906: +#line 1189 "char_ref.rl" + {te = p+1;{ output->first = 0x24; {p++; goto _out; } }} + break; + case 907: +#line 1190 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d555; {p++; goto _out; } }} + break; + case 908: +#line 1191 "char_ref.rl" + {te = p+1;{ output->first = 0x02d9; {p++; goto _out; } }} + break; + case 909: +#line 1192 "char_ref.rl" + {te = p+1;{ output->first = 0x2250; {p++; goto _out; } }} + break; + case 910: +#line 1193 "char_ref.rl" + {te = p+1;{ output->first = 0x2251; {p++; goto _out; } }} + break; + case 911: +#line 1194 "char_ref.rl" + {te = p+1;{ output->first = 0x2238; {p++; goto _out; } }} + break; + case 912: +#line 1195 "char_ref.rl" + {te = p+1;{ output->first = 0x2214; {p++; goto _out; } }} + break; + case 913: +#line 1196 "char_ref.rl" + {te = p+1;{ output->first = 0x22a1; {p++; goto _out; } }} + break; + case 914: +#line 1197 "char_ref.rl" + {te = p+1;{ output->first = 0x2306; {p++; goto _out; } }} + break; + case 915: +#line 1198 "char_ref.rl" + {te = p+1;{ output->first = 0x2193; {p++; goto _out; } }} + break; + case 916: +#line 1199 "char_ref.rl" + {te = p+1;{ output->first = 0x21ca; {p++; goto _out; } }} + break; + case 917: +#line 1200 "char_ref.rl" + {te = p+1;{ output->first = 0x21c3; {p++; goto _out; } }} + break; + case 918: +#line 1201 "char_ref.rl" + {te = p+1;{ output->first = 0x21c2; {p++; goto _out; } }} + break; + case 919: +#line 1202 "char_ref.rl" + {te = p+1;{ output->first = 0x2910; {p++; goto _out; } }} + break; + case 920: +#line 1203 "char_ref.rl" + {te = p+1;{ output->first = 0x231f; {p++; goto _out; } }} + break; + case 921: +#line 1204 "char_ref.rl" + {te = p+1;{ output->first = 0x230c; {p++; goto _out; } }} + break; + case 922: +#line 1205 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4b9; {p++; goto _out; } }} + break; + case 923: +#line 1206 "char_ref.rl" + {te = p+1;{ output->first = 0x0455; {p++; goto _out; } }} + break; + case 924: +#line 1207 "char_ref.rl" + {te = p+1;{ output->first = 0x29f6; {p++; goto _out; } }} + break; + case 925: +#line 1208 "char_ref.rl" + {te = p+1;{ output->first = 0x0111; {p++; goto _out; } }} + break; + case 926: +#line 1209 "char_ref.rl" + {te = p+1;{ output->first = 0x22f1; {p++; goto _out; } }} + break; + case 927: +#line 1210 "char_ref.rl" + {te = p+1;{ output->first = 0x25bf; {p++; goto _out; } }} + break; + case 928: +#line 1211 "char_ref.rl" + {te = p+1;{ output->first = 0x25be; {p++; goto _out; } }} + break; + case 929: +#line 1212 "char_ref.rl" + {te = p+1;{ output->first = 0x21f5; {p++; goto _out; } }} + break; + case 930: +#line 1213 "char_ref.rl" + {te = p+1;{ output->first = 0x296f; {p++; goto _out; } }} + break; + case 931: +#line 1214 "char_ref.rl" + {te = p+1;{ output->first = 0x29a6; {p++; goto _out; } }} + break; + case 932: +#line 1215 "char_ref.rl" + {te = p+1;{ output->first = 0x045f; {p++; goto _out; } }} + break; + case 933: +#line 1216 "char_ref.rl" + {te = p+1;{ output->first = 0x27ff; {p++; goto _out; } }} + break; + case 934: +#line 1217 "char_ref.rl" + {te = p+1;{ output->first = 0x2a77; {p++; goto _out; } }} + break; + case 935: +#line 1218 "char_ref.rl" + {te = p+1;{ output->first = 0x2251; {p++; goto _out; } }} + break; + case 936: +#line 1219 "char_ref.rl" + {te = p+1;{ output->first = 0xe9; {p++; goto _out; } }} + break; + case 937: +#line 1221 "char_ref.rl" + {te = p+1;{ output->first = 0x2a6e; {p++; goto _out; } }} + break; + case 938: +#line 1222 "char_ref.rl" + {te = p+1;{ output->first = 0x011b; {p++; goto _out; } }} + break; + case 939: +#line 1223 "char_ref.rl" + {te = p+1;{ output->first = 0x2256; {p++; goto _out; } }} + break; + case 940: +#line 1224 "char_ref.rl" + {te = p+1;{ output->first = 0xea; {p++; goto _out; } }} + break; + case 941: +#line 1226 "char_ref.rl" + {te = p+1;{ output->first = 0x2255; {p++; goto _out; } }} + break; + case 942: +#line 1227 "char_ref.rl" + {te = p+1;{ output->first = 0x044d; {p++; goto _out; } }} + break; + case 943: +#line 1228 "char_ref.rl" + {te = p+1;{ output->first = 0x0117; {p++; goto _out; } }} + break; + case 944: +#line 1229 "char_ref.rl" + {te = p+1;{ output->first = 0x2147; {p++; goto _out; } }} + break; + case 945: +#line 1230 "char_ref.rl" + {te = p+1;{ output->first = 0x2252; {p++; goto _out; } }} + break; + case 946: +#line 1231 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d522; {p++; goto _out; } }} + break; + case 947: +#line 1232 "char_ref.rl" + {te = p+1;{ output->first = 0x2a9a; {p++; goto _out; } }} + break; + case 948: +#line 1233 "char_ref.rl" + {te = p+1;{ output->first = 0xe8; {p++; goto _out; } }} + break; + case 949: +#line 1235 "char_ref.rl" + {te = p+1;{ output->first = 0x2a96; {p++; goto _out; } }} + break; + case 950: +#line 1236 "char_ref.rl" + {te = p+1;{ output->first = 0x2a98; {p++; goto _out; } }} + break; + case 951: +#line 1237 "char_ref.rl" + {te = p+1;{ output->first = 0x2a99; {p++; goto _out; } }} + break; + case 952: +#line 1238 "char_ref.rl" + {te = p+1;{ output->first = 0x23e7; {p++; goto _out; } }} + break; + case 953: +#line 1239 "char_ref.rl" + {te = p+1;{ output->first = 0x2113; {p++; goto _out; } }} + break; + case 954: +#line 1240 "char_ref.rl" + {te = p+1;{ output->first = 0x2a95; {p++; goto _out; } }} + break; + case 955: +#line 1241 "char_ref.rl" + {te = p+1;{ output->first = 0x2a97; {p++; goto _out; } }} + break; + case 956: +#line 1242 "char_ref.rl" + {te = p+1;{ output->first = 0x0113; {p++; goto _out; } }} + break; + case 957: +#line 1243 "char_ref.rl" + {te = p+1;{ output->first = 0x2205; {p++; goto _out; } }} + break; + case 958: +#line 1244 "char_ref.rl" + {te = p+1;{ output->first = 0x2205; {p++; goto _out; } }} + break; + case 959: +#line 1245 "char_ref.rl" + {te = p+1;{ output->first = 0x2205; {p++; goto _out; } }} + break; + case 960: +#line 1246 "char_ref.rl" + {te = p+1;{ output->first = 0x2004; {p++; goto _out; } }} + break; + case 961: +#line 1247 "char_ref.rl" + {te = p+1;{ output->first = 0x2005; {p++; goto _out; } }} + break; + case 962: +#line 1248 "char_ref.rl" + {te = p+1;{ output->first = 0x2003; {p++; goto _out; } }} + break; + case 963: +#line 1249 "char_ref.rl" + {te = p+1;{ output->first = 0x014b; {p++; goto _out; } }} + break; + case 964: +#line 1250 "char_ref.rl" + {te = p+1;{ output->first = 0x2002; {p++; goto _out; } }} + break; + case 965: +#line 1251 "char_ref.rl" + {te = p+1;{ output->first = 0x0119; {p++; goto _out; } }} + break; + case 966: +#line 1252 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d556; {p++; goto _out; } }} + break; + case 967: +#line 1253 "char_ref.rl" + {te = p+1;{ output->first = 0x22d5; {p++; goto _out; } }} + break; + case 968: +#line 1254 "char_ref.rl" + {te = p+1;{ output->first = 0x29e3; {p++; goto _out; } }} + break; + case 969: +#line 1255 "char_ref.rl" + {te = p+1;{ output->first = 0x2a71; {p++; goto _out; } }} + break; + case 970: +#line 1256 "char_ref.rl" + {te = p+1;{ output->first = 0x03b5; {p++; goto _out; } }} + break; + case 971: +#line 1257 "char_ref.rl" + {te = p+1;{ output->first = 0x03b5; {p++; goto _out; } }} + break; + case 972: +#line 1258 "char_ref.rl" + {te = p+1;{ output->first = 0x03f5; {p++; goto _out; } }} + break; + case 973: +#line 1259 "char_ref.rl" + {te = p+1;{ output->first = 0x2256; {p++; goto _out; } }} + break; + case 974: +#line 1260 "char_ref.rl" + {te = p+1;{ output->first = 0x2255; {p++; goto _out; } }} + break; + case 975: +#line 1261 "char_ref.rl" + {te = p+1;{ output->first = 0x2242; {p++; goto _out; } }} + break; + case 976: +#line 1262 "char_ref.rl" + {te = p+1;{ output->first = 0x2a96; {p++; goto _out; } }} + break; + case 977: +#line 1263 "char_ref.rl" + {te = p+1;{ output->first = 0x2a95; {p++; goto _out; } }} + break; + case 978: +#line 1264 "char_ref.rl" + {te = p+1;{ output->first = 0x3d; {p++; goto _out; } }} + break; + case 979: +#line 1265 "char_ref.rl" + {te = p+1;{ output->first = 0x225f; {p++; goto _out; } }} + break; + case 980: +#line 1266 "char_ref.rl" + {te = p+1;{ output->first = 0x2261; {p++; goto _out; } }} + break; + case 981: +#line 1267 "char_ref.rl" + {te = p+1;{ output->first = 0x2a78; {p++; goto _out; } }} + break; + case 982: +#line 1268 "char_ref.rl" + {te = p+1;{ output->first = 0x29e5; {p++; goto _out; } }} + break; + case 983: +#line 1269 "char_ref.rl" + {te = p+1;{ output->first = 0x2253; {p++; goto _out; } }} + break; + case 984: +#line 1270 "char_ref.rl" + {te = p+1;{ output->first = 0x2971; {p++; goto _out; } }} + break; + case 985: +#line 1271 "char_ref.rl" + {te = p+1;{ output->first = 0x212f; {p++; goto _out; } }} + break; + case 986: +#line 1272 "char_ref.rl" + {te = p+1;{ output->first = 0x2250; {p++; goto _out; } }} + break; + case 987: +#line 1273 "char_ref.rl" + {te = p+1;{ output->first = 0x2242; {p++; goto _out; } }} + break; + case 988: +#line 1274 "char_ref.rl" + {te = p+1;{ output->first = 0x03b7; {p++; goto _out; } }} + break; + case 989: +#line 1275 "char_ref.rl" + {te = p+1;{ output->first = 0xf0; {p++; goto _out; } }} + break; + case 990: +#line 1277 "char_ref.rl" + {te = p+1;{ output->first = 0xeb; {p++; goto _out; } }} + break; + case 991: +#line 1279 "char_ref.rl" + {te = p+1;{ output->first = 0x20ac; {p++; goto _out; } }} + break; + case 992: +#line 1280 "char_ref.rl" + {te = p+1;{ output->first = 0x21; {p++; goto _out; } }} + break; + case 993: +#line 1281 "char_ref.rl" + {te = p+1;{ output->first = 0x2203; {p++; goto _out; } }} + break; + case 994: +#line 1282 "char_ref.rl" + {te = p+1;{ output->first = 0x2130; {p++; goto _out; } }} + break; + case 995: +#line 1283 "char_ref.rl" + {te = p+1;{ output->first = 0x2147; {p++; goto _out; } }} + break; + case 996: +#line 1284 "char_ref.rl" + {te = p+1;{ output->first = 0x2252; {p++; goto _out; } }} + break; + case 997: +#line 1285 "char_ref.rl" + {te = p+1;{ output->first = 0x0444; {p++; goto _out; } }} + break; + case 998: +#line 1286 "char_ref.rl" + {te = p+1;{ output->first = 0x2640; {p++; goto _out; } }} + break; + case 999: +#line 1287 "char_ref.rl" + {te = p+1;{ output->first = 0xfb03; {p++; goto _out; } }} + break; + case 1000: +#line 1288 "char_ref.rl" + {te = p+1;{ output->first = 0xfb00; {p++; goto _out; } }} + break; + case 1001: +#line 1289 "char_ref.rl" + {te = p+1;{ output->first = 0xfb04; {p++; goto _out; } }} + break; + case 1002: +#line 1290 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d523; {p++; goto _out; } }} + break; + case 1003: +#line 1291 "char_ref.rl" + {te = p+1;{ output->first = 0xfb01; {p++; goto _out; } }} + break; + case 1004: +#line 1292 "char_ref.rl" + {te = p+1;{ output->first = 0x66; output->second = 0x6a; {p++; goto _out; } }} + break; + case 1005: +#line 1293 "char_ref.rl" + {te = p+1;{ output->first = 0x266d; {p++; goto _out; } }} + break; + case 1006: +#line 1294 "char_ref.rl" + {te = p+1;{ output->first = 0xfb02; {p++; goto _out; } }} + break; + case 1007: +#line 1295 "char_ref.rl" + {te = p+1;{ output->first = 0x25b1; {p++; goto _out; } }} + break; + case 1008: +#line 1296 "char_ref.rl" + {te = p+1;{ output->first = 0x0192; {p++; goto _out; } }} + break; + case 1009: +#line 1297 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d557; {p++; goto _out; } }} + break; + case 1010: +#line 1298 "char_ref.rl" + {te = p+1;{ output->first = 0x2200; {p++; goto _out; } }} + break; + case 1011: +#line 1299 "char_ref.rl" + {te = p+1;{ output->first = 0x22d4; {p++; goto _out; } }} + break; + case 1012: +#line 1300 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad9; {p++; goto _out; } }} + break; + case 1013: +#line 1301 "char_ref.rl" + {te = p+1;{ output->first = 0x2a0d; {p++; goto _out; } }} + break; + case 1014: +#line 1302 "char_ref.rl" + {te = p+1;{ output->first = 0xbd; {p++; goto _out; } }} + break; + case 1015: +#line 1304 "char_ref.rl" + {te = p+1;{ output->first = 0x2153; {p++; goto _out; } }} + break; + case 1016: +#line 1305 "char_ref.rl" + {te = p+1;{ output->first = 0xbc; {p++; goto _out; } }} + break; + case 1017: +#line 1307 "char_ref.rl" + {te = p+1;{ output->first = 0x2155; {p++; goto _out; } }} + break; + case 1018: +#line 1308 "char_ref.rl" + {te = p+1;{ output->first = 0x2159; {p++; goto _out; } }} + break; + case 1019: +#line 1309 "char_ref.rl" + {te = p+1;{ output->first = 0x215b; {p++; goto _out; } }} + break; + case 1020: +#line 1310 "char_ref.rl" + {te = p+1;{ output->first = 0x2154; {p++; goto _out; } }} + break; + case 1021: +#line 1311 "char_ref.rl" + {te = p+1;{ output->first = 0x2156; {p++; goto _out; } }} + break; + case 1022: +#line 1312 "char_ref.rl" + {te = p+1;{ output->first = 0xbe; {p++; goto _out; } }} + break; + case 1023: +#line 1314 "char_ref.rl" + {te = p+1;{ output->first = 0x2157; {p++; goto _out; } }} + break; + case 1024: +#line 1315 "char_ref.rl" + {te = p+1;{ output->first = 0x215c; {p++; goto _out; } }} + break; + case 1025: +#line 1316 "char_ref.rl" + {te = p+1;{ output->first = 0x2158; {p++; goto _out; } }} + break; + case 1026: +#line 1317 "char_ref.rl" + {te = p+1;{ output->first = 0x215a; {p++; goto _out; } }} + break; + case 1027: +#line 1318 "char_ref.rl" + {te = p+1;{ output->first = 0x215d; {p++; goto _out; } }} + break; + case 1028: +#line 1319 "char_ref.rl" + {te = p+1;{ output->first = 0x215e; {p++; goto _out; } }} + break; + case 1029: +#line 1320 "char_ref.rl" + {te = p+1;{ output->first = 0x2044; {p++; goto _out; } }} + break; + case 1030: +#line 1321 "char_ref.rl" + {te = p+1;{ output->first = 0x2322; {p++; goto _out; } }} + break; + case 1031: +#line 1322 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4bb; {p++; goto _out; } }} + break; + case 1032: +#line 1323 "char_ref.rl" + {te = p+1;{ output->first = 0x2267; {p++; goto _out; } }} + break; + case 1033: +#line 1324 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8c; {p++; goto _out; } }} + break; + case 1034: +#line 1325 "char_ref.rl" + {te = p+1;{ output->first = 0x01f5; {p++; goto _out; } }} + break; + case 1035: +#line 1326 "char_ref.rl" + {te = p+1;{ output->first = 0x03b3; {p++; goto _out; } }} + break; + case 1036: +#line 1327 "char_ref.rl" + {te = p+1;{ output->first = 0x03dd; {p++; goto _out; } }} + break; + case 1037: +#line 1328 "char_ref.rl" + {te = p+1;{ output->first = 0x2a86; {p++; goto _out; } }} + break; + case 1038: +#line 1329 "char_ref.rl" + {te = p+1;{ output->first = 0x011f; {p++; goto _out; } }} + break; + case 1039: +#line 1330 "char_ref.rl" + {te = p+1;{ output->first = 0x011d; {p++; goto _out; } }} + break; + case 1040: +#line 1331 "char_ref.rl" + {te = p+1;{ output->first = 0x0433; {p++; goto _out; } }} + break; + case 1041: +#line 1332 "char_ref.rl" + {te = p+1;{ output->first = 0x0121; {p++; goto _out; } }} + break; + case 1042: +#line 1333 "char_ref.rl" + {te = p+1;{ output->first = 0x2265; {p++; goto _out; } }} + break; + case 1043: +#line 1334 "char_ref.rl" + {te = p+1;{ output->first = 0x22db; {p++; goto _out; } }} + break; + case 1044: +#line 1335 "char_ref.rl" + {te = p+1;{ output->first = 0x2265; {p++; goto _out; } }} + break; + case 1045: +#line 1336 "char_ref.rl" + {te = p+1;{ output->first = 0x2267; {p++; goto _out; } }} + break; + case 1046: +#line 1337 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7e; {p++; goto _out; } }} + break; + case 1047: +#line 1338 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7e; {p++; goto _out; } }} + break; + case 1048: +#line 1339 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa9; {p++; goto _out; } }} + break; + case 1049: +#line 1340 "char_ref.rl" + {te = p+1;{ output->first = 0x2a80; {p++; goto _out; } }} + break; + case 1050: +#line 1341 "char_ref.rl" + {te = p+1;{ output->first = 0x2a82; {p++; goto _out; } }} + break; + case 1051: +#line 1342 "char_ref.rl" + {te = p+1;{ output->first = 0x2a84; {p++; goto _out; } }} + break; + case 1052: +#line 1343 "char_ref.rl" + {te = p+1;{ output->first = 0x22db; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1053: +#line 1344 "char_ref.rl" + {te = p+1;{ output->first = 0x2a94; {p++; goto _out; } }} + break; + case 1054: +#line 1345 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d524; {p++; goto _out; } }} + break; + case 1055: +#line 1346 "char_ref.rl" + {te = p+1;{ output->first = 0x226b; {p++; goto _out; } }} + break; + case 1056: +#line 1347 "char_ref.rl" + {te = p+1;{ output->first = 0x22d9; {p++; goto _out; } }} + break; + case 1057: +#line 1348 "char_ref.rl" + {te = p+1;{ output->first = 0x2137; {p++; goto _out; } }} + break; + case 1058: +#line 1349 "char_ref.rl" + {te = p+1;{ output->first = 0x0453; {p++; goto _out; } }} + break; + case 1059: +#line 1350 "char_ref.rl" + {te = p+1;{ output->first = 0x2277; {p++; goto _out; } }} + break; + case 1060: +#line 1351 "char_ref.rl" + {te = p+1;{ output->first = 0x2a92; {p++; goto _out; } }} + break; + case 1061: +#line 1352 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa5; {p++; goto _out; } }} + break; + case 1062: +#line 1353 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa4; {p++; goto _out; } }} + break; + case 1063: +#line 1354 "char_ref.rl" + {te = p+1;{ output->first = 0x2269; {p++; goto _out; } }} + break; + case 1064: +#line 1355 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8a; {p++; goto _out; } }} + break; + case 1065: +#line 1356 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8a; {p++; goto _out; } }} + break; + case 1066: +#line 1357 "char_ref.rl" + {te = p+1;{ output->first = 0x2a88; {p++; goto _out; } }} + break; + case 1067: +#line 1358 "char_ref.rl" + {te = p+1;{ output->first = 0x2a88; {p++; goto _out; } }} + break; + case 1068: +#line 1359 "char_ref.rl" + {te = p+1;{ output->first = 0x2269; {p++; goto _out; } }} + break; + case 1069: +#line 1360 "char_ref.rl" + {te = p+1;{ output->first = 0x22e7; {p++; goto _out; } }} + break; + case 1070: +#line 1361 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d558; {p++; goto _out; } }} + break; + case 1071: +#line 1362 "char_ref.rl" + {te = p+1;{ output->first = 0x60; {p++; goto _out; } }} + break; + case 1072: +#line 1363 "char_ref.rl" + {te = p+1;{ output->first = 0x210a; {p++; goto _out; } }} + break; + case 1073: +#line 1364 "char_ref.rl" + {te = p+1;{ output->first = 0x2273; {p++; goto _out; } }} + break; + case 1074: +#line 1365 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8e; {p++; goto _out; } }} + break; + case 1075: +#line 1366 "char_ref.rl" + {te = p+1;{ output->first = 0x2a90; {p++; goto _out; } }} + break; + case 1076: +#line 1367 "char_ref.rl" + {te = p+1;{ output->first = 0x3e; {p++; goto _out; } }} + break; + case 1077: +#line 1369 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa7; {p++; goto _out; } }} + break; + case 1078: +#line 1370 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7a; {p++; goto _out; } }} + break; + case 1079: +#line 1371 "char_ref.rl" + {te = p+1;{ output->first = 0x22d7; {p++; goto _out; } }} + break; + case 1080: +#line 1372 "char_ref.rl" + {te = p+1;{ output->first = 0x2995; {p++; goto _out; } }} + break; + case 1081: +#line 1373 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7c; {p++; goto _out; } }} + break; + case 1082: +#line 1374 "char_ref.rl" + {te = p+1;{ output->first = 0x2a86; {p++; goto _out; } }} + break; + case 1083: +#line 1375 "char_ref.rl" + {te = p+1;{ output->first = 0x2978; {p++; goto _out; } }} + break; + case 1084: +#line 1376 "char_ref.rl" + {te = p+1;{ output->first = 0x22d7; {p++; goto _out; } }} + break; + case 1085: +#line 1377 "char_ref.rl" + {te = p+1;{ output->first = 0x22db; {p++; goto _out; } }} + break; + case 1086: +#line 1378 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8c; {p++; goto _out; } }} + break; + case 1087: +#line 1379 "char_ref.rl" + {te = p+1;{ output->first = 0x2277; {p++; goto _out; } }} + break; + case 1088: +#line 1380 "char_ref.rl" + {te = p+1;{ output->first = 0x2273; {p++; goto _out; } }} + break; + case 1089: +#line 1381 "char_ref.rl" + {te = p+1;{ output->first = 0x2269; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1090: +#line 1382 "char_ref.rl" + {te = p+1;{ output->first = 0x2269; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1091: +#line 1383 "char_ref.rl" + {te = p+1;{ output->first = 0x21d4; {p++; goto _out; } }} + break; + case 1092: +#line 1384 "char_ref.rl" + {te = p+1;{ output->first = 0x200a; {p++; goto _out; } }} + break; + case 1093: +#line 1385 "char_ref.rl" + {te = p+1;{ output->first = 0xbd; {p++; goto _out; } }} + break; + case 1094: +#line 1386 "char_ref.rl" + {te = p+1;{ output->first = 0x210b; {p++; goto _out; } }} + break; + case 1095: +#line 1387 "char_ref.rl" + {te = p+1;{ output->first = 0x044a; {p++; goto _out; } }} + break; + case 1096: +#line 1388 "char_ref.rl" + {te = p+1;{ output->first = 0x2194; {p++; goto _out; } }} + break; + case 1097: +#line 1389 "char_ref.rl" + {te = p+1;{ output->first = 0x2948; {p++; goto _out; } }} + break; + case 1098: +#line 1390 "char_ref.rl" + {te = p+1;{ output->first = 0x21ad; {p++; goto _out; } }} + break; + case 1099: +#line 1391 "char_ref.rl" + {te = p+1;{ output->first = 0x210f; {p++; goto _out; } }} + break; + case 1100: +#line 1392 "char_ref.rl" + {te = p+1;{ output->first = 0x0125; {p++; goto _out; } }} + break; + case 1101: +#line 1393 "char_ref.rl" + {te = p+1;{ output->first = 0x2665; {p++; goto _out; } }} + break; + case 1102: +#line 1394 "char_ref.rl" + {te = p+1;{ output->first = 0x2665; {p++; goto _out; } }} + break; + case 1103: +#line 1395 "char_ref.rl" + {te = p+1;{ output->first = 0x2026; {p++; goto _out; } }} + break; + case 1104: +#line 1396 "char_ref.rl" + {te = p+1;{ output->first = 0x22b9; {p++; goto _out; } }} + break; + case 1105: +#line 1397 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d525; {p++; goto _out; } }} + break; + case 1106: +#line 1398 "char_ref.rl" + {te = p+1;{ output->first = 0x2925; {p++; goto _out; } }} + break; + case 1107: +#line 1399 "char_ref.rl" + {te = p+1;{ output->first = 0x2926; {p++; goto _out; } }} + break; + case 1108: +#line 1400 "char_ref.rl" + {te = p+1;{ output->first = 0x21ff; {p++; goto _out; } }} + break; + case 1109: +#line 1401 "char_ref.rl" + {te = p+1;{ output->first = 0x223b; {p++; goto _out; } }} + break; + case 1110: +#line 1402 "char_ref.rl" + {te = p+1;{ output->first = 0x21a9; {p++; goto _out; } }} + break; + case 1111: +#line 1403 "char_ref.rl" + {te = p+1;{ output->first = 0x21aa; {p++; goto _out; } }} + break; + case 1112: +#line 1404 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d559; {p++; goto _out; } }} + break; + case 1113: +#line 1405 "char_ref.rl" + {te = p+1;{ output->first = 0x2015; {p++; goto _out; } }} + break; + case 1114: +#line 1406 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4bd; {p++; goto _out; } }} + break; + case 1115: +#line 1407 "char_ref.rl" + {te = p+1;{ output->first = 0x210f; {p++; goto _out; } }} + break; + case 1116: +#line 1408 "char_ref.rl" + {te = p+1;{ output->first = 0x0127; {p++; goto _out; } }} + break; + case 1117: +#line 1409 "char_ref.rl" + {te = p+1;{ output->first = 0x2043; {p++; goto _out; } }} + break; + case 1118: +#line 1410 "char_ref.rl" + {te = p+1;{ output->first = 0x2010; {p++; goto _out; } }} + break; + case 1119: +#line 1411 "char_ref.rl" + {te = p+1;{ output->first = 0xed; {p++; goto _out; } }} + break; + case 1120: +#line 1413 "char_ref.rl" + {te = p+1;{ output->first = 0x2063; {p++; goto _out; } }} + break; + case 1121: +#line 1414 "char_ref.rl" + {te = p+1;{ output->first = 0xee; {p++; goto _out; } }} + break; + case 1122: +#line 1416 "char_ref.rl" + {te = p+1;{ output->first = 0x0438; {p++; goto _out; } }} + break; + case 1123: +#line 1417 "char_ref.rl" + {te = p+1;{ output->first = 0x0435; {p++; goto _out; } }} + break; + case 1124: +#line 1418 "char_ref.rl" + {te = p+1;{ output->first = 0xa1; {p++; goto _out; } }} + break; + case 1125: +#line 1420 "char_ref.rl" + {te = p+1;{ output->first = 0x21d4; {p++; goto _out; } }} + break; + case 1126: +#line 1421 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d526; {p++; goto _out; } }} + break; + case 1127: +#line 1422 "char_ref.rl" + {te = p+1;{ output->first = 0xec; {p++; goto _out; } }} + break; + case 1128: +#line 1424 "char_ref.rl" + {te = p+1;{ output->first = 0x2148; {p++; goto _out; } }} + break; + case 1129: +#line 1425 "char_ref.rl" + {te = p+1;{ output->first = 0x2a0c; {p++; goto _out; } }} + break; + case 1130: +#line 1426 "char_ref.rl" + {te = p+1;{ output->first = 0x222d; {p++; goto _out; } }} + break; + case 1131: +#line 1427 "char_ref.rl" + {te = p+1;{ output->first = 0x29dc; {p++; goto _out; } }} + break; + case 1132: +#line 1428 "char_ref.rl" + {te = p+1;{ output->first = 0x2129; {p++; goto _out; } }} + break; + case 1133: +#line 1429 "char_ref.rl" + {te = p+1;{ output->first = 0x0133; {p++; goto _out; } }} + break; + case 1134: +#line 1430 "char_ref.rl" + {te = p+1;{ output->first = 0x012b; {p++; goto _out; } }} + break; + case 1135: +#line 1431 "char_ref.rl" + {te = p+1;{ output->first = 0x2111; {p++; goto _out; } }} + break; + case 1136: +#line 1432 "char_ref.rl" + {te = p+1;{ output->first = 0x2110; {p++; goto _out; } }} + break; + case 1137: +#line 1433 "char_ref.rl" + {te = p+1;{ output->first = 0x2111; {p++; goto _out; } }} + break; + case 1138: +#line 1434 "char_ref.rl" + {te = p+1;{ output->first = 0x0131; {p++; goto _out; } }} + break; + case 1139: +#line 1435 "char_ref.rl" + {te = p+1;{ output->first = 0x22b7; {p++; goto _out; } }} + break; + case 1140: +#line 1436 "char_ref.rl" + {te = p+1;{ output->first = 0x01b5; {p++; goto _out; } }} + break; + case 1141: +#line 1437 "char_ref.rl" + {te = p+1;{ output->first = 0x2208; {p++; goto _out; } }} + break; + case 1142: +#line 1438 "char_ref.rl" + {te = p+1;{ output->first = 0x2105; {p++; goto _out; } }} + break; + case 1143: +#line 1439 "char_ref.rl" + {te = p+1;{ output->first = 0x221e; {p++; goto _out; } }} + break; + case 1144: +#line 1440 "char_ref.rl" + {te = p+1;{ output->first = 0x29dd; {p++; goto _out; } }} + break; + case 1145: +#line 1441 "char_ref.rl" + {te = p+1;{ output->first = 0x0131; {p++; goto _out; } }} + break; + case 1146: +#line 1442 "char_ref.rl" + {te = p+1;{ output->first = 0x222b; {p++; goto _out; } }} + break; + case 1147: +#line 1443 "char_ref.rl" + {te = p+1;{ output->first = 0x22ba; {p++; goto _out; } }} + break; + case 1148: +#line 1444 "char_ref.rl" + {te = p+1;{ output->first = 0x2124; {p++; goto _out; } }} + break; + case 1149: +#line 1445 "char_ref.rl" + {te = p+1;{ output->first = 0x22ba; {p++; goto _out; } }} + break; + case 1150: +#line 1446 "char_ref.rl" + {te = p+1;{ output->first = 0x2a17; {p++; goto _out; } }} + break; + case 1151: +#line 1447 "char_ref.rl" + {te = p+1;{ output->first = 0x2a3c; {p++; goto _out; } }} + break; + case 1152: +#line 1448 "char_ref.rl" + {te = p+1;{ output->first = 0x0451; {p++; goto _out; } }} + break; + case 1153: +#line 1449 "char_ref.rl" + {te = p+1;{ output->first = 0x012f; {p++; goto _out; } }} + break; + case 1154: +#line 1450 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d55a; {p++; goto _out; } }} + break; + case 1155: +#line 1451 "char_ref.rl" + {te = p+1;{ output->first = 0x03b9; {p++; goto _out; } }} + break; + case 1156: +#line 1452 "char_ref.rl" + {te = p+1;{ output->first = 0x2a3c; {p++; goto _out; } }} + break; + case 1157: +#line 1453 "char_ref.rl" + {te = p+1;{ output->first = 0xbf; {p++; goto _out; } }} + break; + case 1158: +#line 1455 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4be; {p++; goto _out; } }} + break; + case 1159: +#line 1456 "char_ref.rl" + {te = p+1;{ output->first = 0x2208; {p++; goto _out; } }} + break; + case 1160: +#line 1457 "char_ref.rl" + {te = p+1;{ output->first = 0x22f9; {p++; goto _out; } }} + break; + case 1161: +#line 1458 "char_ref.rl" + {te = p+1;{ output->first = 0x22f5; {p++; goto _out; } }} + break; + case 1162: +#line 1459 "char_ref.rl" + {te = p+1;{ output->first = 0x22f4; {p++; goto _out; } }} + break; + case 1163: +#line 1460 "char_ref.rl" + {te = p+1;{ output->first = 0x22f3; {p++; goto _out; } }} + break; + case 1164: +#line 1461 "char_ref.rl" + {te = p+1;{ output->first = 0x2208; {p++; goto _out; } }} + break; + case 1165: +#line 1462 "char_ref.rl" + {te = p+1;{ output->first = 0x2062; {p++; goto _out; } }} + break; + case 1166: +#line 1463 "char_ref.rl" + {te = p+1;{ output->first = 0x0129; {p++; goto _out; } }} + break; + case 1167: +#line 1464 "char_ref.rl" + {te = p+1;{ output->first = 0x0456; {p++; goto _out; } }} + break; + case 1168: +#line 1465 "char_ref.rl" + {te = p+1;{ output->first = 0xef; {p++; goto _out; } }} + break; + case 1169: +#line 1467 "char_ref.rl" + {te = p+1;{ output->first = 0x0135; {p++; goto _out; } }} + break; + case 1170: +#line 1468 "char_ref.rl" + {te = p+1;{ output->first = 0x0439; {p++; goto _out; } }} + break; + case 1171: +#line 1469 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d527; {p++; goto _out; } }} + break; + case 1172: +#line 1470 "char_ref.rl" + {te = p+1;{ output->first = 0x0237; {p++; goto _out; } }} + break; + case 1173: +#line 1471 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d55b; {p++; goto _out; } }} + break; + case 1174: +#line 1472 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4bf; {p++; goto _out; } }} + break; + case 1175: +#line 1473 "char_ref.rl" + {te = p+1;{ output->first = 0x0458; {p++; goto _out; } }} + break; + case 1176: +#line 1474 "char_ref.rl" + {te = p+1;{ output->first = 0x0454; {p++; goto _out; } }} + break; + case 1177: +#line 1475 "char_ref.rl" + {te = p+1;{ output->first = 0x03ba; {p++; goto _out; } }} + break; + case 1178: +#line 1476 "char_ref.rl" + {te = p+1;{ output->first = 0x03f0; {p++; goto _out; } }} + break; + case 1179: +#line 1477 "char_ref.rl" + {te = p+1;{ output->first = 0x0137; {p++; goto _out; } }} + break; + case 1180: +#line 1478 "char_ref.rl" + {te = p+1;{ output->first = 0x043a; {p++; goto _out; } }} + break; + case 1181: +#line 1479 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d528; {p++; goto _out; } }} + break; + case 1182: +#line 1480 "char_ref.rl" + {te = p+1;{ output->first = 0x0138; {p++; goto _out; } }} + break; + case 1183: +#line 1481 "char_ref.rl" + {te = p+1;{ output->first = 0x0445; {p++; goto _out; } }} + break; + case 1184: +#line 1482 "char_ref.rl" + {te = p+1;{ output->first = 0x045c; {p++; goto _out; } }} + break; + case 1185: +#line 1483 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d55c; {p++; goto _out; } }} + break; + case 1186: +#line 1484 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c0; {p++; goto _out; } }} + break; + case 1187: +#line 1485 "char_ref.rl" + {te = p+1;{ output->first = 0x21da; {p++; goto _out; } }} + break; + case 1188: +#line 1486 "char_ref.rl" + {te = p+1;{ output->first = 0x21d0; {p++; goto _out; } }} + break; + case 1189: +#line 1487 "char_ref.rl" + {te = p+1;{ output->first = 0x291b; {p++; goto _out; } }} + break; + case 1190: +#line 1488 "char_ref.rl" + {te = p+1;{ output->first = 0x290e; {p++; goto _out; } }} + break; + case 1191: +#line 1489 "char_ref.rl" + {te = p+1;{ output->first = 0x2266; {p++; goto _out; } }} + break; + case 1192: +#line 1490 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8b; {p++; goto _out; } }} + break; + case 1193: +#line 1491 "char_ref.rl" + {te = p+1;{ output->first = 0x2962; {p++; goto _out; } }} + break; + case 1194: +#line 1492 "char_ref.rl" + {te = p+1;{ output->first = 0x013a; {p++; goto _out; } }} + break; + case 1195: +#line 1493 "char_ref.rl" + {te = p+1;{ output->first = 0x29b4; {p++; goto _out; } }} + break; + case 1196: +#line 1494 "char_ref.rl" + {te = p+1;{ output->first = 0x2112; {p++; goto _out; } }} + break; + case 1197: +#line 1495 "char_ref.rl" + {te = p+1;{ output->first = 0x03bb; {p++; goto _out; } }} + break; + case 1198: +#line 1496 "char_ref.rl" + {te = p+1;{ output->first = 0x27e8; {p++; goto _out; } }} + break; + case 1199: +#line 1497 "char_ref.rl" + {te = p+1;{ output->first = 0x2991; {p++; goto _out; } }} + break; + case 1200: +#line 1498 "char_ref.rl" + {te = p+1;{ output->first = 0x27e8; {p++; goto _out; } }} + break; + case 1201: +#line 1499 "char_ref.rl" + {te = p+1;{ output->first = 0x2a85; {p++; goto _out; } }} + break; + case 1202: +#line 1500 "char_ref.rl" + {te = p+1;{ output->first = 0xab; {p++; goto _out; } }} + break; + case 1203: +#line 1502 "char_ref.rl" + {te = p+1;{ output->first = 0x2190; {p++; goto _out; } }} + break; + case 1204: +#line 1503 "char_ref.rl" + {te = p+1;{ output->first = 0x21e4; {p++; goto _out; } }} + break; + case 1205: +#line 1504 "char_ref.rl" + {te = p+1;{ output->first = 0x291f; {p++; goto _out; } }} + break; + case 1206: +#line 1505 "char_ref.rl" + {te = p+1;{ output->first = 0x291d; {p++; goto _out; } }} + break; + case 1207: +#line 1506 "char_ref.rl" + {te = p+1;{ output->first = 0x21a9; {p++; goto _out; } }} + break; + case 1208: +#line 1507 "char_ref.rl" + {te = p+1;{ output->first = 0x21ab; {p++; goto _out; } }} + break; + case 1209: +#line 1508 "char_ref.rl" + {te = p+1;{ output->first = 0x2939; {p++; goto _out; } }} + break; + case 1210: +#line 1509 "char_ref.rl" + {te = p+1;{ output->first = 0x2973; {p++; goto _out; } }} + break; + case 1211: +#line 1510 "char_ref.rl" + {te = p+1;{ output->first = 0x21a2; {p++; goto _out; } }} + break; + case 1212: +#line 1511 "char_ref.rl" + {te = p+1;{ output->first = 0x2aab; {p++; goto _out; } }} + break; + case 1213: +#line 1512 "char_ref.rl" + {te = p+1;{ output->first = 0x2919; {p++; goto _out; } }} + break; + case 1214: +#line 1513 "char_ref.rl" + {te = p+1;{ output->first = 0x2aad; {p++; goto _out; } }} + break; + case 1215: +#line 1514 "char_ref.rl" + {te = p+1;{ output->first = 0x2aad; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1216: +#line 1515 "char_ref.rl" + {te = p+1;{ output->first = 0x290c; {p++; goto _out; } }} + break; + case 1217: +#line 1516 "char_ref.rl" + {te = p+1;{ output->first = 0x2772; {p++; goto _out; } }} + break; + case 1218: +#line 1517 "char_ref.rl" + {te = p+1;{ output->first = 0x7b; {p++; goto _out; } }} + break; + case 1219: +#line 1518 "char_ref.rl" + {te = p+1;{ output->first = 0x5b; {p++; goto _out; } }} + break; + case 1220: +#line 1519 "char_ref.rl" + {te = p+1;{ output->first = 0x298b; {p++; goto _out; } }} + break; + case 1221: +#line 1520 "char_ref.rl" + {te = p+1;{ output->first = 0x298f; {p++; goto _out; } }} + break; + case 1222: +#line 1521 "char_ref.rl" + {te = p+1;{ output->first = 0x298d; {p++; goto _out; } }} + break; + case 1223: +#line 1522 "char_ref.rl" + {te = p+1;{ output->first = 0x013e; {p++; goto _out; } }} + break; + case 1224: +#line 1523 "char_ref.rl" + {te = p+1;{ output->first = 0x013c; {p++; goto _out; } }} + break; + case 1225: +#line 1524 "char_ref.rl" + {te = p+1;{ output->first = 0x2308; {p++; goto _out; } }} + break; + case 1226: +#line 1525 "char_ref.rl" + {te = p+1;{ output->first = 0x7b; {p++; goto _out; } }} + break; + case 1227: +#line 1526 "char_ref.rl" + {te = p+1;{ output->first = 0x043b; {p++; goto _out; } }} + break; + case 1228: +#line 1527 "char_ref.rl" + {te = p+1;{ output->first = 0x2936; {p++; goto _out; } }} + break; + case 1229: +#line 1528 "char_ref.rl" + {te = p+1;{ output->first = 0x201c; {p++; goto _out; } }} + break; + case 1230: +#line 1529 "char_ref.rl" + {te = p+1;{ output->first = 0x201e; {p++; goto _out; } }} + break; + case 1231: +#line 1530 "char_ref.rl" + {te = p+1;{ output->first = 0x2967; {p++; goto _out; } }} + break; + case 1232: +#line 1531 "char_ref.rl" + {te = p+1;{ output->first = 0x294b; {p++; goto _out; } }} + break; + case 1233: +#line 1532 "char_ref.rl" + {te = p+1;{ output->first = 0x21b2; {p++; goto _out; } }} + break; + case 1234: +#line 1533 "char_ref.rl" + {te = p+1;{ output->first = 0x2264; {p++; goto _out; } }} + break; + case 1235: +#line 1534 "char_ref.rl" + {te = p+1;{ output->first = 0x2190; {p++; goto _out; } }} + break; + case 1236: +#line 1535 "char_ref.rl" + {te = p+1;{ output->first = 0x21a2; {p++; goto _out; } }} + break; + case 1237: +#line 1536 "char_ref.rl" + {te = p+1;{ output->first = 0x21bd; {p++; goto _out; } }} + break; + case 1238: +#line 1537 "char_ref.rl" + {te = p+1;{ output->first = 0x21bc; {p++; goto _out; } }} + break; + case 1239: +#line 1538 "char_ref.rl" + {te = p+1;{ output->first = 0x21c7; {p++; goto _out; } }} + break; + case 1240: +#line 1539 "char_ref.rl" + {te = p+1;{ output->first = 0x2194; {p++; goto _out; } }} + break; + case 1241: +#line 1540 "char_ref.rl" + {te = p+1;{ output->first = 0x21c6; {p++; goto _out; } }} + break; + case 1242: +#line 1541 "char_ref.rl" + {te = p+1;{ output->first = 0x21cb; {p++; goto _out; } }} + break; + case 1243: +#line 1542 "char_ref.rl" + {te = p+1;{ output->first = 0x21ad; {p++; goto _out; } }} + break; + case 1244: +#line 1543 "char_ref.rl" + {te = p+1;{ output->first = 0x22cb; {p++; goto _out; } }} + break; + case 1245: +#line 1544 "char_ref.rl" + {te = p+1;{ output->first = 0x22da; {p++; goto _out; } }} + break; + case 1246: +#line 1545 "char_ref.rl" + {te = p+1;{ output->first = 0x2264; {p++; goto _out; } }} + break; + case 1247: +#line 1546 "char_ref.rl" + {te = p+1;{ output->first = 0x2266; {p++; goto _out; } }} + break; + case 1248: +#line 1547 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7d; {p++; goto _out; } }} + break; + case 1249: +#line 1548 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7d; {p++; goto _out; } }} + break; + case 1250: +#line 1549 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa8; {p++; goto _out; } }} + break; + case 1251: +#line 1550 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7f; {p++; goto _out; } }} + break; + case 1252: +#line 1551 "char_ref.rl" + {te = p+1;{ output->first = 0x2a81; {p++; goto _out; } }} + break; + case 1253: +#line 1552 "char_ref.rl" + {te = p+1;{ output->first = 0x2a83; {p++; goto _out; } }} + break; + case 1254: +#line 1553 "char_ref.rl" + {te = p+1;{ output->first = 0x22da; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1255: +#line 1554 "char_ref.rl" + {te = p+1;{ output->first = 0x2a93; {p++; goto _out; } }} + break; + case 1256: +#line 1555 "char_ref.rl" + {te = p+1;{ output->first = 0x2a85; {p++; goto _out; } }} + break; + case 1257: +#line 1556 "char_ref.rl" + {te = p+1;{ output->first = 0x22d6; {p++; goto _out; } }} + break; + case 1258: +#line 1557 "char_ref.rl" + {te = p+1;{ output->first = 0x22da; {p++; goto _out; } }} + break; + case 1259: +#line 1558 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8b; {p++; goto _out; } }} + break; + case 1260: +#line 1559 "char_ref.rl" + {te = p+1;{ output->first = 0x2276; {p++; goto _out; } }} + break; + case 1261: +#line 1560 "char_ref.rl" + {te = p+1;{ output->first = 0x2272; {p++; goto _out; } }} + break; + case 1262: +#line 1561 "char_ref.rl" + {te = p+1;{ output->first = 0x297c; {p++; goto _out; } }} + break; + case 1263: +#line 1562 "char_ref.rl" + {te = p+1;{ output->first = 0x230a; {p++; goto _out; } }} + break; + case 1264: +#line 1563 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d529; {p++; goto _out; } }} + break; + case 1265: +#line 1564 "char_ref.rl" + {te = p+1;{ output->first = 0x2276; {p++; goto _out; } }} + break; + case 1266: +#line 1565 "char_ref.rl" + {te = p+1;{ output->first = 0x2a91; {p++; goto _out; } }} + break; + case 1267: +#line 1566 "char_ref.rl" + {te = p+1;{ output->first = 0x21bd; {p++; goto _out; } }} + break; + case 1268: +#line 1567 "char_ref.rl" + {te = p+1;{ output->first = 0x21bc; {p++; goto _out; } }} + break; + case 1269: +#line 1568 "char_ref.rl" + {te = p+1;{ output->first = 0x296a; {p++; goto _out; } }} + break; + case 1270: +#line 1569 "char_ref.rl" + {te = p+1;{ output->first = 0x2584; {p++; goto _out; } }} + break; + case 1271: +#line 1570 "char_ref.rl" + {te = p+1;{ output->first = 0x0459; {p++; goto _out; } }} + break; + case 1272: +#line 1571 "char_ref.rl" + {te = p+1;{ output->first = 0x226a; {p++; goto _out; } }} + break; + case 1273: +#line 1572 "char_ref.rl" + {te = p+1;{ output->first = 0x21c7; {p++; goto _out; } }} + break; + case 1274: +#line 1573 "char_ref.rl" + {te = p+1;{ output->first = 0x231e; {p++; goto _out; } }} + break; + case 1275: +#line 1574 "char_ref.rl" + {te = p+1;{ output->first = 0x296b; {p++; goto _out; } }} + break; + case 1276: +#line 1575 "char_ref.rl" + {te = p+1;{ output->first = 0x25fa; {p++; goto _out; } }} + break; + case 1277: +#line 1576 "char_ref.rl" + {te = p+1;{ output->first = 0x0140; {p++; goto _out; } }} + break; + case 1278: +#line 1577 "char_ref.rl" + {te = p+1;{ output->first = 0x23b0; {p++; goto _out; } }} + break; + case 1279: +#line 1578 "char_ref.rl" + {te = p+1;{ output->first = 0x23b0; {p++; goto _out; } }} + break; + case 1280: +#line 1579 "char_ref.rl" + {te = p+1;{ output->first = 0x2268; {p++; goto _out; } }} + break; + case 1281: +#line 1580 "char_ref.rl" + {te = p+1;{ output->first = 0x2a89; {p++; goto _out; } }} + break; + case 1282: +#line 1581 "char_ref.rl" + {te = p+1;{ output->first = 0x2a89; {p++; goto _out; } }} + break; + case 1283: +#line 1582 "char_ref.rl" + {te = p+1;{ output->first = 0x2a87; {p++; goto _out; } }} + break; + case 1284: +#line 1583 "char_ref.rl" + {te = p+1;{ output->first = 0x2a87; {p++; goto _out; } }} + break; + case 1285: +#line 1584 "char_ref.rl" + {te = p+1;{ output->first = 0x2268; {p++; goto _out; } }} + break; + case 1286: +#line 1585 "char_ref.rl" + {te = p+1;{ output->first = 0x22e6; {p++; goto _out; } }} + break; + case 1287: +#line 1586 "char_ref.rl" + {te = p+1;{ output->first = 0x27ec; {p++; goto _out; } }} + break; + case 1288: +#line 1587 "char_ref.rl" + {te = p+1;{ output->first = 0x21fd; {p++; goto _out; } }} + break; + case 1289: +#line 1588 "char_ref.rl" + {te = p+1;{ output->first = 0x27e6; {p++; goto _out; } }} + break; + case 1290: +#line 1589 "char_ref.rl" + {te = p+1;{ output->first = 0x27f5; {p++; goto _out; } }} + break; + case 1291: +#line 1590 "char_ref.rl" + {te = p+1;{ output->first = 0x27f7; {p++; goto _out; } }} + break; + case 1292: +#line 1591 "char_ref.rl" + {te = p+1;{ output->first = 0x27fc; {p++; goto _out; } }} + break; + case 1293: +#line 1592 "char_ref.rl" + {te = p+1;{ output->first = 0x27f6; {p++; goto _out; } }} + break; + case 1294: +#line 1593 "char_ref.rl" + {te = p+1;{ output->first = 0x21ab; {p++; goto _out; } }} + break; + case 1295: +#line 1594 "char_ref.rl" + {te = p+1;{ output->first = 0x21ac; {p++; goto _out; } }} + break; + case 1296: +#line 1595 "char_ref.rl" + {te = p+1;{ output->first = 0x2985; {p++; goto _out; } }} + break; + case 1297: +#line 1596 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d55d; {p++; goto _out; } }} + break; + case 1298: +#line 1597 "char_ref.rl" + {te = p+1;{ output->first = 0x2a2d; {p++; goto _out; } }} + break; + case 1299: +#line 1598 "char_ref.rl" + {te = p+1;{ output->first = 0x2a34; {p++; goto _out; } }} + break; + case 1300: +#line 1599 "char_ref.rl" + {te = p+1;{ output->first = 0x2217; {p++; goto _out; } }} + break; + case 1301: +#line 1600 "char_ref.rl" + {te = p+1;{ output->first = 0x5f; {p++; goto _out; } }} + break; + case 1302: +#line 1601 "char_ref.rl" + {te = p+1;{ output->first = 0x25ca; {p++; goto _out; } }} + break; + case 1303: +#line 1602 "char_ref.rl" + {te = p+1;{ output->first = 0x25ca; {p++; goto _out; } }} + break; + case 1304: +#line 1603 "char_ref.rl" + {te = p+1;{ output->first = 0x29eb; {p++; goto _out; } }} + break; + case 1305: +#line 1604 "char_ref.rl" + {te = p+1;{ output->first = 0x28; {p++; goto _out; } }} + break; + case 1306: +#line 1605 "char_ref.rl" + {te = p+1;{ output->first = 0x2993; {p++; goto _out; } }} + break; + case 1307: +#line 1606 "char_ref.rl" + {te = p+1;{ output->first = 0x21c6; {p++; goto _out; } }} + break; + case 1308: +#line 1607 "char_ref.rl" + {te = p+1;{ output->first = 0x231f; {p++; goto _out; } }} + break; + case 1309: +#line 1608 "char_ref.rl" + {te = p+1;{ output->first = 0x21cb; {p++; goto _out; } }} + break; + case 1310: +#line 1609 "char_ref.rl" + {te = p+1;{ output->first = 0x296d; {p++; goto _out; } }} + break; + case 1311: +#line 1610 "char_ref.rl" + {te = p+1;{ output->first = 0x200e; {p++; goto _out; } }} + break; + case 1312: +#line 1611 "char_ref.rl" + {te = p+1;{ output->first = 0x22bf; {p++; goto _out; } }} + break; + case 1313: +#line 1612 "char_ref.rl" + {te = p+1;{ output->first = 0x2039; {p++; goto _out; } }} + break; + case 1314: +#line 1613 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c1; {p++; goto _out; } }} + break; + case 1315: +#line 1614 "char_ref.rl" + {te = p+1;{ output->first = 0x21b0; {p++; goto _out; } }} + break; + case 1316: +#line 1615 "char_ref.rl" + {te = p+1;{ output->first = 0x2272; {p++; goto _out; } }} + break; + case 1317: +#line 1616 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8d; {p++; goto _out; } }} + break; + case 1318: +#line 1617 "char_ref.rl" + {te = p+1;{ output->first = 0x2a8f; {p++; goto _out; } }} + break; + case 1319: +#line 1618 "char_ref.rl" + {te = p+1;{ output->first = 0x5b; {p++; goto _out; } }} + break; + case 1320: +#line 1619 "char_ref.rl" + {te = p+1;{ output->first = 0x2018; {p++; goto _out; } }} + break; + case 1321: +#line 1620 "char_ref.rl" + {te = p+1;{ output->first = 0x201a; {p++; goto _out; } }} + break; + case 1322: +#line 1621 "char_ref.rl" + {te = p+1;{ output->first = 0x0142; {p++; goto _out; } }} + break; + case 1323: +#line 1622 "char_ref.rl" + {te = p+1;{ output->first = 0x3c; {p++; goto _out; } }} + break; + case 1324: +#line 1624 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa6; {p++; goto _out; } }} + break; + case 1325: +#line 1625 "char_ref.rl" + {te = p+1;{ output->first = 0x2a79; {p++; goto _out; } }} + break; + case 1326: +#line 1626 "char_ref.rl" + {te = p+1;{ output->first = 0x22d6; {p++; goto _out; } }} + break; + case 1327: +#line 1627 "char_ref.rl" + {te = p+1;{ output->first = 0x22cb; {p++; goto _out; } }} + break; + case 1328: +#line 1628 "char_ref.rl" + {te = p+1;{ output->first = 0x22c9; {p++; goto _out; } }} + break; + case 1329: +#line 1629 "char_ref.rl" + {te = p+1;{ output->first = 0x2976; {p++; goto _out; } }} + break; + case 1330: +#line 1630 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7b; {p++; goto _out; } }} + break; + case 1331: +#line 1631 "char_ref.rl" + {te = p+1;{ output->first = 0x2996; {p++; goto _out; } }} + break; + case 1332: +#line 1632 "char_ref.rl" + {te = p+1;{ output->first = 0x25c3; {p++; goto _out; } }} + break; + case 1333: +#line 1633 "char_ref.rl" + {te = p+1;{ output->first = 0x22b4; {p++; goto _out; } }} + break; + case 1334: +#line 1634 "char_ref.rl" + {te = p+1;{ output->first = 0x25c2; {p++; goto _out; } }} + break; + case 1335: +#line 1635 "char_ref.rl" + {te = p+1;{ output->first = 0x294a; {p++; goto _out; } }} + break; + case 1336: +#line 1636 "char_ref.rl" + {te = p+1;{ output->first = 0x2966; {p++; goto _out; } }} + break; + case 1337: +#line 1637 "char_ref.rl" + {te = p+1;{ output->first = 0x2268; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1338: +#line 1638 "char_ref.rl" + {te = p+1;{ output->first = 0x2268; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1339: +#line 1639 "char_ref.rl" + {te = p+1;{ output->first = 0x223a; {p++; goto _out; } }} + break; + case 1340: +#line 1640 "char_ref.rl" + {te = p+1;{ output->first = 0xaf; {p++; goto _out; } }} + break; + case 1341: +#line 1642 "char_ref.rl" + {te = p+1;{ output->first = 0x2642; {p++; goto _out; } }} + break; + case 1342: +#line 1643 "char_ref.rl" + {te = p+1;{ output->first = 0x2720; {p++; goto _out; } }} + break; + case 1343: +#line 1644 "char_ref.rl" + {te = p+1;{ output->first = 0x2720; {p++; goto _out; } }} + break; + case 1344: +#line 1645 "char_ref.rl" + {te = p+1;{ output->first = 0x21a6; {p++; goto _out; } }} + break; + case 1345: +#line 1646 "char_ref.rl" + {te = p+1;{ output->first = 0x21a6; {p++; goto _out; } }} + break; + case 1346: +#line 1647 "char_ref.rl" + {te = p+1;{ output->first = 0x21a7; {p++; goto _out; } }} + break; + case 1347: +#line 1648 "char_ref.rl" + {te = p+1;{ output->first = 0x21a4; {p++; goto _out; } }} + break; + case 1348: +#line 1649 "char_ref.rl" + {te = p+1;{ output->first = 0x21a5; {p++; goto _out; } }} + break; + case 1349: +#line 1650 "char_ref.rl" + {te = p+1;{ output->first = 0x25ae; {p++; goto _out; } }} + break; + case 1350: +#line 1651 "char_ref.rl" + {te = p+1;{ output->first = 0x2a29; {p++; goto _out; } }} + break; + case 1351: +#line 1652 "char_ref.rl" + {te = p+1;{ output->first = 0x043c; {p++; goto _out; } }} + break; + case 1352: +#line 1653 "char_ref.rl" + {te = p+1;{ output->first = 0x2014; {p++; goto _out; } }} + break; + case 1353: +#line 1654 "char_ref.rl" + {te = p+1;{ output->first = 0x2221; {p++; goto _out; } }} + break; + case 1354: +#line 1655 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d52a; {p++; goto _out; } }} + break; + case 1355: +#line 1656 "char_ref.rl" + {te = p+1;{ output->first = 0x2127; {p++; goto _out; } }} + break; + case 1356: +#line 1657 "char_ref.rl" + {te = p+1;{ output->first = 0xb5; {p++; goto _out; } }} + break; + case 1357: +#line 1659 "char_ref.rl" + {te = p+1;{ output->first = 0x2223; {p++; goto _out; } }} + break; + case 1358: +#line 1660 "char_ref.rl" + {te = p+1;{ output->first = 0x2a; {p++; goto _out; } }} + break; + case 1359: +#line 1661 "char_ref.rl" + {te = p+1;{ output->first = 0x2af0; {p++; goto _out; } }} + break; + case 1360: +#line 1662 "char_ref.rl" + {te = p+1;{ output->first = 0xb7; {p++; goto _out; } }} + break; + case 1361: +#line 1664 "char_ref.rl" + {te = p+1;{ output->first = 0x2212; {p++; goto _out; } }} + break; + case 1362: +#line 1665 "char_ref.rl" + {te = p+1;{ output->first = 0x229f; {p++; goto _out; } }} + break; + case 1363: +#line 1666 "char_ref.rl" + {te = p+1;{ output->first = 0x2238; {p++; goto _out; } }} + break; + case 1364: +#line 1667 "char_ref.rl" + {te = p+1;{ output->first = 0x2a2a; {p++; goto _out; } }} + break; + case 1365: +#line 1668 "char_ref.rl" + {te = p+1;{ output->first = 0x2adb; {p++; goto _out; } }} + break; + case 1366: +#line 1669 "char_ref.rl" + {te = p+1;{ output->first = 0x2026; {p++; goto _out; } }} + break; + case 1367: +#line 1670 "char_ref.rl" + {te = p+1;{ output->first = 0x2213; {p++; goto _out; } }} + break; + case 1368: +#line 1671 "char_ref.rl" + {te = p+1;{ output->first = 0x22a7; {p++; goto _out; } }} + break; + case 1369: +#line 1672 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d55e; {p++; goto _out; } }} + break; + case 1370: +#line 1673 "char_ref.rl" + {te = p+1;{ output->first = 0x2213; {p++; goto _out; } }} + break; + case 1371: +#line 1674 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c2; {p++; goto _out; } }} + break; + case 1372: +#line 1675 "char_ref.rl" + {te = p+1;{ output->first = 0x223e; {p++; goto _out; } }} + break; + case 1373: +#line 1676 "char_ref.rl" + {te = p+1;{ output->first = 0x03bc; {p++; goto _out; } }} + break; + case 1374: +#line 1677 "char_ref.rl" + {te = p+1;{ output->first = 0x22b8; {p++; goto _out; } }} + break; + case 1375: +#line 1678 "char_ref.rl" + {te = p+1;{ output->first = 0x22b8; {p++; goto _out; } }} + break; + case 1376: +#line 1679 "char_ref.rl" + {te = p+1;{ output->first = 0x22d9; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1377: +#line 1680 "char_ref.rl" + {te = p+1;{ output->first = 0x226b; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1378: +#line 1681 "char_ref.rl" + {te = p+1;{ output->first = 0x226b; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1379: +#line 1682 "char_ref.rl" + {te = p+1;{ output->first = 0x21cd; {p++; goto _out; } }} + break; + case 1380: +#line 1683 "char_ref.rl" + {te = p+1;{ output->first = 0x21ce; {p++; goto _out; } }} + break; + case 1381: +#line 1684 "char_ref.rl" + {te = p+1;{ output->first = 0x22d8; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1382: +#line 1685 "char_ref.rl" + {te = p+1;{ output->first = 0x226a; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1383: +#line 1686 "char_ref.rl" + {te = p+1;{ output->first = 0x226a; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1384: +#line 1687 "char_ref.rl" + {te = p+1;{ output->first = 0x21cf; {p++; goto _out; } }} + break; + case 1385: +#line 1688 "char_ref.rl" + {te = p+1;{ output->first = 0x22af; {p++; goto _out; } }} + break; + case 1386: +#line 1689 "char_ref.rl" + {te = p+1;{ output->first = 0x22ae; {p++; goto _out; } }} + break; + case 1387: +#line 1690 "char_ref.rl" + {te = p+1;{ output->first = 0x2207; {p++; goto _out; } }} + break; + case 1388: +#line 1691 "char_ref.rl" + {te = p+1;{ output->first = 0x0144; {p++; goto _out; } }} + break; + case 1389: +#line 1692 "char_ref.rl" + {te = p+1;{ output->first = 0x2220; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1390: +#line 1693 "char_ref.rl" + {te = p+1;{ output->first = 0x2249; {p++; goto _out; } }} + break; + case 1391: +#line 1694 "char_ref.rl" + {te = p+1;{ output->first = 0x2a70; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1392: +#line 1695 "char_ref.rl" + {te = p+1;{ output->first = 0x224b; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1393: +#line 1696 "char_ref.rl" + {te = p+1;{ output->first = 0x0149; {p++; goto _out; } }} + break; + case 1394: +#line 1697 "char_ref.rl" + {te = p+1;{ output->first = 0x2249; {p++; goto _out; } }} + break; + case 1395: +#line 1698 "char_ref.rl" + {te = p+1;{ output->first = 0x266e; {p++; goto _out; } }} + break; + case 1396: +#line 1699 "char_ref.rl" + {te = p+1;{ output->first = 0x266e; {p++; goto _out; } }} + break; + case 1397: +#line 1700 "char_ref.rl" + {te = p+1;{ output->first = 0x2115; {p++; goto _out; } }} + break; + case 1398: +#line 1701 "char_ref.rl" + {te = p+1;{ output->first = 0xa0; {p++; goto _out; } }} + break; + case 1399: +#line 1703 "char_ref.rl" + {te = p+1;{ output->first = 0x224e; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1400: +#line 1704 "char_ref.rl" + {te = p+1;{ output->first = 0x224f; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1401: +#line 1705 "char_ref.rl" + {te = p+1;{ output->first = 0x2a43; {p++; goto _out; } }} + break; + case 1402: +#line 1706 "char_ref.rl" + {te = p+1;{ output->first = 0x0148; {p++; goto _out; } }} + break; + case 1403: +#line 1707 "char_ref.rl" + {te = p+1;{ output->first = 0x0146; {p++; goto _out; } }} + break; + case 1404: +#line 1708 "char_ref.rl" + {te = p+1;{ output->first = 0x2247; {p++; goto _out; } }} + break; + case 1405: +#line 1709 "char_ref.rl" + {te = p+1;{ output->first = 0x2a6d; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1406: +#line 1710 "char_ref.rl" + {te = p+1;{ output->first = 0x2a42; {p++; goto _out; } }} + break; + case 1407: +#line 1711 "char_ref.rl" + {te = p+1;{ output->first = 0x043d; {p++; goto _out; } }} + break; + case 1408: +#line 1712 "char_ref.rl" + {te = p+1;{ output->first = 0x2013; {p++; goto _out; } }} + break; + case 1409: +#line 1713 "char_ref.rl" + {te = p+1;{ output->first = 0x2260; {p++; goto _out; } }} + break; + case 1410: +#line 1714 "char_ref.rl" + {te = p+1;{ output->first = 0x21d7; {p++; goto _out; } }} + break; + case 1411: +#line 1715 "char_ref.rl" + {te = p+1;{ output->first = 0x2924; {p++; goto _out; } }} + break; + case 1412: +#line 1716 "char_ref.rl" + {te = p+1;{ output->first = 0x2197; {p++; goto _out; } }} + break; + case 1413: +#line 1717 "char_ref.rl" + {te = p+1;{ output->first = 0x2197; {p++; goto _out; } }} + break; + case 1414: +#line 1718 "char_ref.rl" + {te = p+1;{ output->first = 0x2250; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1415: +#line 1719 "char_ref.rl" + {te = p+1;{ output->first = 0x2262; {p++; goto _out; } }} + break; + case 1416: +#line 1720 "char_ref.rl" + {te = p+1;{ output->first = 0x2928; {p++; goto _out; } }} + break; + case 1417: +#line 1721 "char_ref.rl" + {te = p+1;{ output->first = 0x2242; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1418: +#line 1722 "char_ref.rl" + {te = p+1;{ output->first = 0x2204; {p++; goto _out; } }} + break; + case 1419: +#line 1723 "char_ref.rl" + {te = p+1;{ output->first = 0x2204; {p++; goto _out; } }} + break; + case 1420: +#line 1724 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d52b; {p++; goto _out; } }} + break; + case 1421: +#line 1725 "char_ref.rl" + {te = p+1;{ output->first = 0x2267; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1422: +#line 1726 "char_ref.rl" + {te = p+1;{ output->first = 0x2271; {p++; goto _out; } }} + break; + case 1423: +#line 1727 "char_ref.rl" + {te = p+1;{ output->first = 0x2271; {p++; goto _out; } }} + break; + case 1424: +#line 1728 "char_ref.rl" + {te = p+1;{ output->first = 0x2267; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1425: +#line 1729 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7e; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1426: +#line 1730 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7e; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1427: +#line 1731 "char_ref.rl" + {te = p+1;{ output->first = 0x2275; {p++; goto _out; } }} + break; + case 1428: +#line 1732 "char_ref.rl" + {te = p+1;{ output->first = 0x226f; {p++; goto _out; } }} + break; + case 1429: +#line 1733 "char_ref.rl" + {te = p+1;{ output->first = 0x226f; {p++; goto _out; } }} + break; + case 1430: +#line 1734 "char_ref.rl" + {te = p+1;{ output->first = 0x21ce; {p++; goto _out; } }} + break; + case 1431: +#line 1735 "char_ref.rl" + {te = p+1;{ output->first = 0x21ae; {p++; goto _out; } }} + break; + case 1432: +#line 1736 "char_ref.rl" + {te = p+1;{ output->first = 0x2af2; {p++; goto _out; } }} + break; + case 1433: +#line 1737 "char_ref.rl" + {te = p+1;{ output->first = 0x220b; {p++; goto _out; } }} + break; + case 1434: +#line 1738 "char_ref.rl" + {te = p+1;{ output->first = 0x22fc; {p++; goto _out; } }} + break; + case 1435: +#line 1739 "char_ref.rl" + {te = p+1;{ output->first = 0x22fa; {p++; goto _out; } }} + break; + case 1436: +#line 1740 "char_ref.rl" + {te = p+1;{ output->first = 0x220b; {p++; goto _out; } }} + break; + case 1437: +#line 1741 "char_ref.rl" + {te = p+1;{ output->first = 0x045a; {p++; goto _out; } }} + break; + case 1438: +#line 1742 "char_ref.rl" + {te = p+1;{ output->first = 0x21cd; {p++; goto _out; } }} + break; + case 1439: +#line 1743 "char_ref.rl" + {te = p+1;{ output->first = 0x2266; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1440: +#line 1744 "char_ref.rl" + {te = p+1;{ output->first = 0x219a; {p++; goto _out; } }} + break; + case 1441: +#line 1745 "char_ref.rl" + {te = p+1;{ output->first = 0x2025; {p++; goto _out; } }} + break; + case 1442: +#line 1746 "char_ref.rl" + {te = p+1;{ output->first = 0x2270; {p++; goto _out; } }} + break; + case 1443: +#line 1747 "char_ref.rl" + {te = p+1;{ output->first = 0x219a; {p++; goto _out; } }} + break; + case 1444: +#line 1748 "char_ref.rl" + {te = p+1;{ output->first = 0x21ae; {p++; goto _out; } }} + break; + case 1445: +#line 1749 "char_ref.rl" + {te = p+1;{ output->first = 0x2270; {p++; goto _out; } }} + break; + case 1446: +#line 1750 "char_ref.rl" + {te = p+1;{ output->first = 0x2266; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1447: +#line 1751 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7d; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1448: +#line 1752 "char_ref.rl" + {te = p+1;{ output->first = 0x2a7d; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1449: +#line 1753 "char_ref.rl" + {te = p+1;{ output->first = 0x226e; {p++; goto _out; } }} + break; + case 1450: +#line 1754 "char_ref.rl" + {te = p+1;{ output->first = 0x2274; {p++; goto _out; } }} + break; + case 1451: +#line 1755 "char_ref.rl" + {te = p+1;{ output->first = 0x226e; {p++; goto _out; } }} + break; + case 1452: +#line 1756 "char_ref.rl" + {te = p+1;{ output->first = 0x22ea; {p++; goto _out; } }} + break; + case 1453: +#line 1757 "char_ref.rl" + {te = p+1;{ output->first = 0x22ec; {p++; goto _out; } }} + break; + case 1454: +#line 1758 "char_ref.rl" + {te = p+1;{ output->first = 0x2224; {p++; goto _out; } }} + break; + case 1455: +#line 1759 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d55f; {p++; goto _out; } }} + break; + case 1456: +#line 1760 "char_ref.rl" + {te = p+1;{ output->first = 0xac; {p++; goto _out; } }} + break; + case 1457: +#line 1761 "char_ref.rl" + {te = p+1;{ output->first = 0x2209; {p++; goto _out; } }} + break; + case 1458: +#line 1762 "char_ref.rl" + {te = p+1;{ output->first = 0x22f9; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1459: +#line 1763 "char_ref.rl" + {te = p+1;{ output->first = 0x22f5; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1460: +#line 1764 "char_ref.rl" + {te = p+1;{ output->first = 0x2209; {p++; goto _out; } }} + break; + case 1461: +#line 1765 "char_ref.rl" + {te = p+1;{ output->first = 0x22f7; {p++; goto _out; } }} + break; + case 1462: +#line 1766 "char_ref.rl" + {te = p+1;{ output->first = 0x22f6; {p++; goto _out; } }} + break; + case 1463: +#line 1767 "char_ref.rl" + {te = p+1;{ output->first = 0x220c; {p++; goto _out; } }} + break; + case 1464: +#line 1768 "char_ref.rl" + {te = p+1;{ output->first = 0x220c; {p++; goto _out; } }} + break; + case 1465: +#line 1769 "char_ref.rl" + {te = p+1;{ output->first = 0x22fe; {p++; goto _out; } }} + break; + case 1466: +#line 1770 "char_ref.rl" + {te = p+1;{ output->first = 0x22fd; {p++; goto _out; } }} + break; + case 1467: +#line 1772 "char_ref.rl" + {te = p+1;{ output->first = 0x2226; {p++; goto _out; } }} + break; + case 1468: +#line 1773 "char_ref.rl" + {te = p+1;{ output->first = 0x2226; {p++; goto _out; } }} + break; + case 1469: +#line 1774 "char_ref.rl" + {te = p+1;{ output->first = 0x2afd; output->second = 0x20e5; {p++; goto _out; } }} + break; + case 1470: +#line 1775 "char_ref.rl" + {te = p+1;{ output->first = 0x2202; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1471: +#line 1776 "char_ref.rl" + {te = p+1;{ output->first = 0x2a14; {p++; goto _out; } }} + break; + case 1472: +#line 1777 "char_ref.rl" + {te = p+1;{ output->first = 0x2280; {p++; goto _out; } }} + break; + case 1473: +#line 1778 "char_ref.rl" + {te = p+1;{ output->first = 0x22e0; {p++; goto _out; } }} + break; + case 1474: +#line 1779 "char_ref.rl" + {te = p+1;{ output->first = 0x2aaf; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1475: +#line 1780 "char_ref.rl" + {te = p+1;{ output->first = 0x2280; {p++; goto _out; } }} + break; + case 1476: +#line 1781 "char_ref.rl" + {te = p+1;{ output->first = 0x2aaf; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1477: +#line 1782 "char_ref.rl" + {te = p+1;{ output->first = 0x21cf; {p++; goto _out; } }} + break; + case 1478: +#line 1783 "char_ref.rl" + {te = p+1;{ output->first = 0x219b; {p++; goto _out; } }} + break; + case 1479: +#line 1784 "char_ref.rl" + {te = p+1;{ output->first = 0x2933; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1480: +#line 1785 "char_ref.rl" + {te = p+1;{ output->first = 0x219d; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1481: +#line 1786 "char_ref.rl" + {te = p+1;{ output->first = 0x219b; {p++; goto _out; } }} + break; + case 1482: +#line 1787 "char_ref.rl" + {te = p+1;{ output->first = 0x22eb; {p++; goto _out; } }} + break; + case 1483: +#line 1788 "char_ref.rl" + {te = p+1;{ output->first = 0x22ed; {p++; goto _out; } }} + break; + case 1484: +#line 1789 "char_ref.rl" + {te = p+1;{ output->first = 0x2281; {p++; goto _out; } }} + break; + case 1485: +#line 1790 "char_ref.rl" + {te = p+1;{ output->first = 0x22e1; {p++; goto _out; } }} + break; + case 1486: +#line 1791 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab0; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1487: +#line 1792 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c3; {p++; goto _out; } }} + break; + case 1488: +#line 1793 "char_ref.rl" + {te = p+1;{ output->first = 0x2224; {p++; goto _out; } }} + break; + case 1489: +#line 1794 "char_ref.rl" + {te = p+1;{ output->first = 0x2226; {p++; goto _out; } }} + break; + case 1490: +#line 1795 "char_ref.rl" + {te = p+1;{ output->first = 0x2241; {p++; goto _out; } }} + break; + case 1491: +#line 1796 "char_ref.rl" + {te = p+1;{ output->first = 0x2244; {p++; goto _out; } }} + break; + case 1492: +#line 1797 "char_ref.rl" + {te = p+1;{ output->first = 0x2244; {p++; goto _out; } }} + break; + case 1493: +#line 1798 "char_ref.rl" + {te = p+1;{ output->first = 0x2224; {p++; goto _out; } }} + break; + case 1494: +#line 1799 "char_ref.rl" + {te = p+1;{ output->first = 0x2226; {p++; goto _out; } }} + break; + case 1495: +#line 1800 "char_ref.rl" + {te = p+1;{ output->first = 0x22e2; {p++; goto _out; } }} + break; + case 1496: +#line 1801 "char_ref.rl" + {te = p+1;{ output->first = 0x22e3; {p++; goto _out; } }} + break; + case 1497: +#line 1802 "char_ref.rl" + {te = p+1;{ output->first = 0x2284; {p++; goto _out; } }} + break; + case 1498: +#line 1803 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac5; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1499: +#line 1804 "char_ref.rl" + {te = p+1;{ output->first = 0x2288; {p++; goto _out; } }} + break; + case 1500: +#line 1805 "char_ref.rl" + {te = p+1;{ output->first = 0x2282; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1501: +#line 1806 "char_ref.rl" + {te = p+1;{ output->first = 0x2288; {p++; goto _out; } }} + break; + case 1502: +#line 1807 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac5; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1503: +#line 1808 "char_ref.rl" + {te = p+1;{ output->first = 0x2281; {p++; goto _out; } }} + break; + case 1504: +#line 1809 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab0; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1505: +#line 1810 "char_ref.rl" + {te = p+1;{ output->first = 0x2285; {p++; goto _out; } }} + break; + case 1506: +#line 1811 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac6; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1507: +#line 1812 "char_ref.rl" + {te = p+1;{ output->first = 0x2289; {p++; goto _out; } }} + break; + case 1508: +#line 1813 "char_ref.rl" + {te = p+1;{ output->first = 0x2283; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1509: +#line 1814 "char_ref.rl" + {te = p+1;{ output->first = 0x2289; {p++; goto _out; } }} + break; + case 1510: +#line 1815 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac6; output->second = 0x0338; {p++; goto _out; } }} + break; + case 1511: +#line 1816 "char_ref.rl" + {te = p+1;{ output->first = 0x2279; {p++; goto _out; } }} + break; + case 1512: +#line 1817 "char_ref.rl" + {te = p+1;{ output->first = 0xf1; {p++; goto _out; } }} + break; + case 1513: +#line 1819 "char_ref.rl" + {te = p+1;{ output->first = 0x2278; {p++; goto _out; } }} + break; + case 1514: +#line 1820 "char_ref.rl" + {te = p+1;{ output->first = 0x22ea; {p++; goto _out; } }} + break; + case 1515: +#line 1821 "char_ref.rl" + {te = p+1;{ output->first = 0x22ec; {p++; goto _out; } }} + break; + case 1516: +#line 1822 "char_ref.rl" + {te = p+1;{ output->first = 0x22eb; {p++; goto _out; } }} + break; + case 1517: +#line 1823 "char_ref.rl" + {te = p+1;{ output->first = 0x22ed; {p++; goto _out; } }} + break; + case 1518: +#line 1824 "char_ref.rl" + {te = p+1;{ output->first = 0x03bd; {p++; goto _out; } }} + break; + case 1519: +#line 1825 "char_ref.rl" + {te = p+1;{ output->first = 0x23; {p++; goto _out; } }} + break; + case 1520: +#line 1826 "char_ref.rl" + {te = p+1;{ output->first = 0x2116; {p++; goto _out; } }} + break; + case 1521: +#line 1827 "char_ref.rl" + {te = p+1;{ output->first = 0x2007; {p++; goto _out; } }} + break; + case 1522: +#line 1828 "char_ref.rl" + {te = p+1;{ output->first = 0x22ad; {p++; goto _out; } }} + break; + case 1523: +#line 1829 "char_ref.rl" + {te = p+1;{ output->first = 0x2904; {p++; goto _out; } }} + break; + case 1524: +#line 1830 "char_ref.rl" + {te = p+1;{ output->first = 0x224d; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1525: +#line 1831 "char_ref.rl" + {te = p+1;{ output->first = 0x22ac; {p++; goto _out; } }} + break; + case 1526: +#line 1832 "char_ref.rl" + {te = p+1;{ output->first = 0x2265; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1527: +#line 1833 "char_ref.rl" + {te = p+1;{ output->first = 0x3e; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1528: +#line 1834 "char_ref.rl" + {te = p+1;{ output->first = 0x29de; {p++; goto _out; } }} + break; + case 1529: +#line 1835 "char_ref.rl" + {te = p+1;{ output->first = 0x2902; {p++; goto _out; } }} + break; + case 1530: +#line 1836 "char_ref.rl" + {te = p+1;{ output->first = 0x2264; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1531: +#line 1837 "char_ref.rl" + {te = p+1;{ output->first = 0x3c; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1532: +#line 1838 "char_ref.rl" + {te = p+1;{ output->first = 0x22b4; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1533: +#line 1839 "char_ref.rl" + {te = p+1;{ output->first = 0x2903; {p++; goto _out; } }} + break; + case 1534: +#line 1840 "char_ref.rl" + {te = p+1;{ output->first = 0x22b5; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1535: +#line 1841 "char_ref.rl" + {te = p+1;{ output->first = 0x223c; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 1536: +#line 1842 "char_ref.rl" + {te = p+1;{ output->first = 0x21d6; {p++; goto _out; } }} + break; + case 1537: +#line 1843 "char_ref.rl" + {te = p+1;{ output->first = 0x2923; {p++; goto _out; } }} + break; + case 1538: +#line 1844 "char_ref.rl" + {te = p+1;{ output->first = 0x2196; {p++; goto _out; } }} + break; + case 1539: +#line 1845 "char_ref.rl" + {te = p+1;{ output->first = 0x2196; {p++; goto _out; } }} + break; + case 1540: +#line 1846 "char_ref.rl" + {te = p+1;{ output->first = 0x2927; {p++; goto _out; } }} + break; + case 1541: +#line 1847 "char_ref.rl" + {te = p+1;{ output->first = 0x24c8; {p++; goto _out; } }} + break; + case 1542: +#line 1848 "char_ref.rl" + {te = p+1;{ output->first = 0xf3; {p++; goto _out; } }} + break; + case 1543: +#line 1850 "char_ref.rl" + {te = p+1;{ output->first = 0x229b; {p++; goto _out; } }} + break; + case 1544: +#line 1851 "char_ref.rl" + {te = p+1;{ output->first = 0x229a; {p++; goto _out; } }} + break; + case 1545: +#line 1852 "char_ref.rl" + {te = p+1;{ output->first = 0xf4; {p++; goto _out; } }} + break; + case 1546: +#line 1854 "char_ref.rl" + {te = p+1;{ output->first = 0x043e; {p++; goto _out; } }} + break; + case 1547: +#line 1855 "char_ref.rl" + {te = p+1;{ output->first = 0x229d; {p++; goto _out; } }} + break; + case 1548: +#line 1856 "char_ref.rl" + {te = p+1;{ output->first = 0x0151; {p++; goto _out; } }} + break; + case 1549: +#line 1857 "char_ref.rl" + {te = p+1;{ output->first = 0x2a38; {p++; goto _out; } }} + break; + case 1550: +#line 1858 "char_ref.rl" + {te = p+1;{ output->first = 0x2299; {p++; goto _out; } }} + break; + case 1551: +#line 1859 "char_ref.rl" + {te = p+1;{ output->first = 0x29bc; {p++; goto _out; } }} + break; + case 1552: +#line 1860 "char_ref.rl" + {te = p+1;{ output->first = 0x0153; {p++; goto _out; } }} + break; + case 1553: +#line 1861 "char_ref.rl" + {te = p+1;{ output->first = 0x29bf; {p++; goto _out; } }} + break; + case 1554: +#line 1862 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d52c; {p++; goto _out; } }} + break; + case 1555: +#line 1863 "char_ref.rl" + {te = p+1;{ output->first = 0x02db; {p++; goto _out; } }} + break; + case 1556: +#line 1864 "char_ref.rl" + {te = p+1;{ output->first = 0xf2; {p++; goto _out; } }} + break; + case 1557: +#line 1866 "char_ref.rl" + {te = p+1;{ output->first = 0x29c1; {p++; goto _out; } }} + break; + case 1558: +#line 1867 "char_ref.rl" + {te = p+1;{ output->first = 0x29b5; {p++; goto _out; } }} + break; + case 1559: +#line 1868 "char_ref.rl" + {te = p+1;{ output->first = 0x03a9; {p++; goto _out; } }} + break; + case 1560: +#line 1869 "char_ref.rl" + {te = p+1;{ output->first = 0x222e; {p++; goto _out; } }} + break; + case 1561: +#line 1870 "char_ref.rl" + {te = p+1;{ output->first = 0x21ba; {p++; goto _out; } }} + break; + case 1562: +#line 1871 "char_ref.rl" + {te = p+1;{ output->first = 0x29be; {p++; goto _out; } }} + break; + case 1563: +#line 1872 "char_ref.rl" + {te = p+1;{ output->first = 0x29bb; {p++; goto _out; } }} + break; + case 1564: +#line 1873 "char_ref.rl" + {te = p+1;{ output->first = 0x203e; {p++; goto _out; } }} + break; + case 1565: +#line 1874 "char_ref.rl" + {te = p+1;{ output->first = 0x29c0; {p++; goto _out; } }} + break; + case 1566: +#line 1875 "char_ref.rl" + {te = p+1;{ output->first = 0x014d; {p++; goto _out; } }} + break; + case 1567: +#line 1876 "char_ref.rl" + {te = p+1;{ output->first = 0x03c9; {p++; goto _out; } }} + break; + case 1568: +#line 1877 "char_ref.rl" + {te = p+1;{ output->first = 0x03bf; {p++; goto _out; } }} + break; + case 1569: +#line 1878 "char_ref.rl" + {te = p+1;{ output->first = 0x29b6; {p++; goto _out; } }} + break; + case 1570: +#line 1879 "char_ref.rl" + {te = p+1;{ output->first = 0x2296; {p++; goto _out; } }} + break; + case 1571: +#line 1880 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d560; {p++; goto _out; } }} + break; + case 1572: +#line 1881 "char_ref.rl" + {te = p+1;{ output->first = 0x29b7; {p++; goto _out; } }} + break; + case 1573: +#line 1882 "char_ref.rl" + {te = p+1;{ output->first = 0x29b9; {p++; goto _out; } }} + break; + case 1574: +#line 1883 "char_ref.rl" + {te = p+1;{ output->first = 0x2295; {p++; goto _out; } }} + break; + case 1575: +#line 1884 "char_ref.rl" + {te = p+1;{ output->first = 0x2228; {p++; goto _out; } }} + break; + case 1576: +#line 1885 "char_ref.rl" + {te = p+1;{ output->first = 0x21bb; {p++; goto _out; } }} + break; + case 1577: +#line 1886 "char_ref.rl" + {te = p+1;{ output->first = 0x2a5d; {p++; goto _out; } }} + break; + case 1578: +#line 1887 "char_ref.rl" + {te = p+1;{ output->first = 0x2134; {p++; goto _out; } }} + break; + case 1579: +#line 1888 "char_ref.rl" + {te = p+1;{ output->first = 0x2134; {p++; goto _out; } }} + break; + case 1580: +#line 1889 "char_ref.rl" + {te = p+1;{ output->first = 0xaa; {p++; goto _out; } }} + break; + case 1581: +#line 1891 "char_ref.rl" + {te = p+1;{ output->first = 0xba; {p++; goto _out; } }} + break; + case 1582: +#line 1893 "char_ref.rl" + {te = p+1;{ output->first = 0x22b6; {p++; goto _out; } }} + break; + case 1583: +#line 1894 "char_ref.rl" + {te = p+1;{ output->first = 0x2a56; {p++; goto _out; } }} + break; + case 1584: +#line 1895 "char_ref.rl" + {te = p+1;{ output->first = 0x2a57; {p++; goto _out; } }} + break; + case 1585: +#line 1896 "char_ref.rl" + {te = p+1;{ output->first = 0x2a5b; {p++; goto _out; } }} + break; + case 1586: +#line 1897 "char_ref.rl" + {te = p+1;{ output->first = 0x2134; {p++; goto _out; } }} + break; + case 1587: +#line 1898 "char_ref.rl" + {te = p+1;{ output->first = 0xf8; {p++; goto _out; } }} + break; + case 1588: +#line 1900 "char_ref.rl" + {te = p+1;{ output->first = 0x2298; {p++; goto _out; } }} + break; + case 1589: +#line 1901 "char_ref.rl" + {te = p+1;{ output->first = 0xf5; {p++; goto _out; } }} + break; + case 1590: +#line 1903 "char_ref.rl" + {te = p+1;{ output->first = 0x2297; {p++; goto _out; } }} + break; + case 1591: +#line 1904 "char_ref.rl" + {te = p+1;{ output->first = 0x2a36; {p++; goto _out; } }} + break; + case 1592: +#line 1905 "char_ref.rl" + {te = p+1;{ output->first = 0xf6; {p++; goto _out; } }} + break; + case 1593: +#line 1907 "char_ref.rl" + {te = p+1;{ output->first = 0x233d; {p++; goto _out; } }} + break; + case 1594: +#line 1908 "char_ref.rl" + {te = p+1;{ output->first = 0x2225; {p++; goto _out; } }} + break; + case 1595: +#line 1909 "char_ref.rl" + {te = p+1;{ output->first = 0xb6; {p++; goto _out; } }} + break; + case 1596: +#line 1911 "char_ref.rl" + {te = p+1;{ output->first = 0x2225; {p++; goto _out; } }} + break; + case 1597: +#line 1912 "char_ref.rl" + {te = p+1;{ output->first = 0x2af3; {p++; goto _out; } }} + break; + case 1598: +#line 1913 "char_ref.rl" + {te = p+1;{ output->first = 0x2afd; {p++; goto _out; } }} + break; + case 1599: +#line 1914 "char_ref.rl" + {te = p+1;{ output->first = 0x2202; {p++; goto _out; } }} + break; + case 1600: +#line 1915 "char_ref.rl" + {te = p+1;{ output->first = 0x043f; {p++; goto _out; } }} + break; + case 1601: +#line 1916 "char_ref.rl" + {te = p+1;{ output->first = 0x25; {p++; goto _out; } }} + break; + case 1602: +#line 1917 "char_ref.rl" + {te = p+1;{ output->first = 0x2e; {p++; goto _out; } }} + break; + case 1603: +#line 1918 "char_ref.rl" + {te = p+1;{ output->first = 0x2030; {p++; goto _out; } }} + break; + case 1604: +#line 1919 "char_ref.rl" + {te = p+1;{ output->first = 0x22a5; {p++; goto _out; } }} + break; + case 1605: +#line 1920 "char_ref.rl" + {te = p+1;{ output->first = 0x2031; {p++; goto _out; } }} + break; + case 1606: +#line 1921 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d52d; {p++; goto _out; } }} + break; + case 1607: +#line 1922 "char_ref.rl" + {te = p+1;{ output->first = 0x03c6; {p++; goto _out; } }} + break; + case 1608: +#line 1923 "char_ref.rl" + {te = p+1;{ output->first = 0x03d5; {p++; goto _out; } }} + break; + case 1609: +#line 1924 "char_ref.rl" + {te = p+1;{ output->first = 0x2133; {p++; goto _out; } }} + break; + case 1610: +#line 1925 "char_ref.rl" + {te = p+1;{ output->first = 0x260e; {p++; goto _out; } }} + break; + case 1611: +#line 1926 "char_ref.rl" + {te = p+1;{ output->first = 0x03c0; {p++; goto _out; } }} + break; + case 1612: +#line 1927 "char_ref.rl" + {te = p+1;{ output->first = 0x22d4; {p++; goto _out; } }} + break; + case 1613: +#line 1928 "char_ref.rl" + {te = p+1;{ output->first = 0x03d6; {p++; goto _out; } }} + break; + case 1614: +#line 1929 "char_ref.rl" + {te = p+1;{ output->first = 0x210f; {p++; goto _out; } }} + break; + case 1615: +#line 1930 "char_ref.rl" + {te = p+1;{ output->first = 0x210e; {p++; goto _out; } }} + break; + case 1616: +#line 1931 "char_ref.rl" + {te = p+1;{ output->first = 0x210f; {p++; goto _out; } }} + break; + case 1617: +#line 1932 "char_ref.rl" + {te = p+1;{ output->first = 0x2b; {p++; goto _out; } }} + break; + case 1618: +#line 1933 "char_ref.rl" + {te = p+1;{ output->first = 0x2a23; {p++; goto _out; } }} + break; + case 1619: +#line 1934 "char_ref.rl" + {te = p+1;{ output->first = 0x229e; {p++; goto _out; } }} + break; + case 1620: +#line 1935 "char_ref.rl" + {te = p+1;{ output->first = 0x2a22; {p++; goto _out; } }} + break; + case 1621: +#line 1936 "char_ref.rl" + {te = p+1;{ output->first = 0x2214; {p++; goto _out; } }} + break; + case 1622: +#line 1937 "char_ref.rl" + {te = p+1;{ output->first = 0x2a25; {p++; goto _out; } }} + break; + case 1623: +#line 1938 "char_ref.rl" + {te = p+1;{ output->first = 0x2a72; {p++; goto _out; } }} + break; + case 1624: +#line 1939 "char_ref.rl" + {te = p+1;{ output->first = 0xb1; {p++; goto _out; } }} + break; + case 1625: +#line 1941 "char_ref.rl" + {te = p+1;{ output->first = 0x2a26; {p++; goto _out; } }} + break; + case 1626: +#line 1942 "char_ref.rl" + {te = p+1;{ output->first = 0x2a27; {p++; goto _out; } }} + break; + case 1627: +#line 1943 "char_ref.rl" + {te = p+1;{ output->first = 0xb1; {p++; goto _out; } }} + break; + case 1628: +#line 1944 "char_ref.rl" + {te = p+1;{ output->first = 0x2a15; {p++; goto _out; } }} + break; + case 1629: +#line 1945 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d561; {p++; goto _out; } }} + break; + case 1630: +#line 1946 "char_ref.rl" + {te = p+1;{ output->first = 0xa3; {p++; goto _out; } }} + break; + case 1631: +#line 1948 "char_ref.rl" + {te = p+1;{ output->first = 0x227a; {p++; goto _out; } }} + break; + case 1632: +#line 1949 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab3; {p++; goto _out; } }} + break; + case 1633: +#line 1950 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab7; {p++; goto _out; } }} + break; + case 1634: +#line 1951 "char_ref.rl" + {te = p+1;{ output->first = 0x227c; {p++; goto _out; } }} + break; + case 1635: +#line 1952 "char_ref.rl" + {te = p+1;{ output->first = 0x2aaf; {p++; goto _out; } }} + break; + case 1636: +#line 1953 "char_ref.rl" + {te = p+1;{ output->first = 0x227a; {p++; goto _out; } }} + break; + case 1637: +#line 1954 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab7; {p++; goto _out; } }} + break; + case 1638: +#line 1955 "char_ref.rl" + {te = p+1;{ output->first = 0x227c; {p++; goto _out; } }} + break; + case 1639: +#line 1956 "char_ref.rl" + {te = p+1;{ output->first = 0x2aaf; {p++; goto _out; } }} + break; + case 1640: +#line 1957 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab9; {p++; goto _out; } }} + break; + case 1641: +#line 1958 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab5; {p++; goto _out; } }} + break; + case 1642: +#line 1959 "char_ref.rl" + {te = p+1;{ output->first = 0x22e8; {p++; goto _out; } }} + break; + case 1643: +#line 1960 "char_ref.rl" + {te = p+1;{ output->first = 0x227e; {p++; goto _out; } }} + break; + case 1644: +#line 1961 "char_ref.rl" + {te = p+1;{ output->first = 0x2032; {p++; goto _out; } }} + break; + case 1645: +#line 1962 "char_ref.rl" + {te = p+1;{ output->first = 0x2119; {p++; goto _out; } }} + break; + case 1646: +#line 1963 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab5; {p++; goto _out; } }} + break; + case 1647: +#line 1964 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab9; {p++; goto _out; } }} + break; + case 1648: +#line 1965 "char_ref.rl" + {te = p+1;{ output->first = 0x22e8; {p++; goto _out; } }} + break; + case 1649: +#line 1966 "char_ref.rl" + {te = p+1;{ output->first = 0x220f; {p++; goto _out; } }} + break; + case 1650: +#line 1967 "char_ref.rl" + {te = p+1;{ output->first = 0x232e; {p++; goto _out; } }} + break; + case 1651: +#line 1968 "char_ref.rl" + {te = p+1;{ output->first = 0x2312; {p++; goto _out; } }} + break; + case 1652: +#line 1969 "char_ref.rl" + {te = p+1;{ output->first = 0x2313; {p++; goto _out; } }} + break; + case 1653: +#line 1970 "char_ref.rl" + {te = p+1;{ output->first = 0x221d; {p++; goto _out; } }} + break; + case 1654: +#line 1971 "char_ref.rl" + {te = p+1;{ output->first = 0x221d; {p++; goto _out; } }} + break; + case 1655: +#line 1972 "char_ref.rl" + {te = p+1;{ output->first = 0x227e; {p++; goto _out; } }} + break; + case 1656: +#line 1973 "char_ref.rl" + {te = p+1;{ output->first = 0x22b0; {p++; goto _out; } }} + break; + case 1657: +#line 1974 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c5; {p++; goto _out; } }} + break; + case 1658: +#line 1975 "char_ref.rl" + {te = p+1;{ output->first = 0x03c8; {p++; goto _out; } }} + break; + case 1659: +#line 1976 "char_ref.rl" + {te = p+1;{ output->first = 0x2008; {p++; goto _out; } }} + break; + case 1660: +#line 1977 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d52e; {p++; goto _out; } }} + break; + case 1661: +#line 1978 "char_ref.rl" + {te = p+1;{ output->first = 0x2a0c; {p++; goto _out; } }} + break; + case 1662: +#line 1979 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d562; {p++; goto _out; } }} + break; + case 1663: +#line 1980 "char_ref.rl" + {te = p+1;{ output->first = 0x2057; {p++; goto _out; } }} + break; + case 1664: +#line 1981 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c6; {p++; goto _out; } }} + break; + case 1665: +#line 1982 "char_ref.rl" + {te = p+1;{ output->first = 0x210d; {p++; goto _out; } }} + break; + case 1666: +#line 1983 "char_ref.rl" + {te = p+1;{ output->first = 0x2a16; {p++; goto _out; } }} + break; + case 1667: +#line 1984 "char_ref.rl" + {te = p+1;{ output->first = 0x3f; {p++; goto _out; } }} + break; + case 1668: +#line 1985 "char_ref.rl" + {te = p+1;{ output->first = 0x225f; {p++; goto _out; } }} + break; + case 1669: +#line 1986 "char_ref.rl" + {te = p+1;{ output->first = 0x22; {p++; goto _out; } }} + break; + case 1670: +#line 1988 "char_ref.rl" + {te = p+1;{ output->first = 0x21db; {p++; goto _out; } }} + break; + case 1671: +#line 1989 "char_ref.rl" + {te = p+1;{ output->first = 0x21d2; {p++; goto _out; } }} + break; + case 1672: +#line 1990 "char_ref.rl" + {te = p+1;{ output->first = 0x291c; {p++; goto _out; } }} + break; + case 1673: +#line 1991 "char_ref.rl" + {te = p+1;{ output->first = 0x290f; {p++; goto _out; } }} + break; + case 1674: +#line 1992 "char_ref.rl" + {te = p+1;{ output->first = 0x2964; {p++; goto _out; } }} + break; + case 1675: +#line 1993 "char_ref.rl" + {te = p+1;{ output->first = 0x223d; output->second = 0x0331; {p++; goto _out; } }} + break; + case 1676: +#line 1994 "char_ref.rl" + {te = p+1;{ output->first = 0x0155; {p++; goto _out; } }} + break; + case 1677: +#line 1995 "char_ref.rl" + {te = p+1;{ output->first = 0x221a; {p++; goto _out; } }} + break; + case 1678: +#line 1996 "char_ref.rl" + {te = p+1;{ output->first = 0x29b3; {p++; goto _out; } }} + break; + case 1679: +#line 1997 "char_ref.rl" + {te = p+1;{ output->first = 0x27e9; {p++; goto _out; } }} + break; + case 1680: +#line 1998 "char_ref.rl" + {te = p+1;{ output->first = 0x2992; {p++; goto _out; } }} + break; + case 1681: +#line 1999 "char_ref.rl" + {te = p+1;{ output->first = 0x29a5; {p++; goto _out; } }} + break; + case 1682: +#line 2000 "char_ref.rl" + {te = p+1;{ output->first = 0x27e9; {p++; goto _out; } }} + break; + case 1683: +#line 2001 "char_ref.rl" + {te = p+1;{ output->first = 0xbb; {p++; goto _out; } }} + break; + case 1684: +#line 2003 "char_ref.rl" + {te = p+1;{ output->first = 0x2192; {p++; goto _out; } }} + break; + case 1685: +#line 2004 "char_ref.rl" + {te = p+1;{ output->first = 0x2975; {p++; goto _out; } }} + break; + case 1686: +#line 2005 "char_ref.rl" + {te = p+1;{ output->first = 0x21e5; {p++; goto _out; } }} + break; + case 1687: +#line 2006 "char_ref.rl" + {te = p+1;{ output->first = 0x2920; {p++; goto _out; } }} + break; + case 1688: +#line 2007 "char_ref.rl" + {te = p+1;{ output->first = 0x2933; {p++; goto _out; } }} + break; + case 1689: +#line 2008 "char_ref.rl" + {te = p+1;{ output->first = 0x291e; {p++; goto _out; } }} + break; + case 1690: +#line 2009 "char_ref.rl" + {te = p+1;{ output->first = 0x21aa; {p++; goto _out; } }} + break; + case 1691: +#line 2010 "char_ref.rl" + {te = p+1;{ output->first = 0x21ac; {p++; goto _out; } }} + break; + case 1692: +#line 2011 "char_ref.rl" + {te = p+1;{ output->first = 0x2945; {p++; goto _out; } }} + break; + case 1693: +#line 2012 "char_ref.rl" + {te = p+1;{ output->first = 0x2974; {p++; goto _out; } }} + break; + case 1694: +#line 2013 "char_ref.rl" + {te = p+1;{ output->first = 0x21a3; {p++; goto _out; } }} + break; + case 1695: +#line 2014 "char_ref.rl" + {te = p+1;{ output->first = 0x219d; {p++; goto _out; } }} + break; + case 1696: +#line 2015 "char_ref.rl" + {te = p+1;{ output->first = 0x291a; {p++; goto _out; } }} + break; + case 1697: +#line 2016 "char_ref.rl" + {te = p+1;{ output->first = 0x2236; {p++; goto _out; } }} + break; + case 1698: +#line 2017 "char_ref.rl" + {te = p+1;{ output->first = 0x211a; {p++; goto _out; } }} + break; + case 1699: +#line 2018 "char_ref.rl" + {te = p+1;{ output->first = 0x290d; {p++; goto _out; } }} + break; + case 1700: +#line 2019 "char_ref.rl" + {te = p+1;{ output->first = 0x2773; {p++; goto _out; } }} + break; + case 1701: +#line 2020 "char_ref.rl" + {te = p+1;{ output->first = 0x7d; {p++; goto _out; } }} + break; + case 1702: +#line 2021 "char_ref.rl" + {te = p+1;{ output->first = 0x5d; {p++; goto _out; } }} + break; + case 1703: +#line 2022 "char_ref.rl" + {te = p+1;{ output->first = 0x298c; {p++; goto _out; } }} + break; + case 1704: +#line 2023 "char_ref.rl" + {te = p+1;{ output->first = 0x298e; {p++; goto _out; } }} + break; + case 1705: +#line 2024 "char_ref.rl" + {te = p+1;{ output->first = 0x2990; {p++; goto _out; } }} + break; + case 1706: +#line 2025 "char_ref.rl" + {te = p+1;{ output->first = 0x0159; {p++; goto _out; } }} + break; + case 1707: +#line 2026 "char_ref.rl" + {te = p+1;{ output->first = 0x0157; {p++; goto _out; } }} + break; + case 1708: +#line 2027 "char_ref.rl" + {te = p+1;{ output->first = 0x2309; {p++; goto _out; } }} + break; + case 1709: +#line 2028 "char_ref.rl" + {te = p+1;{ output->first = 0x7d; {p++; goto _out; } }} + break; + case 1710: +#line 2029 "char_ref.rl" + {te = p+1;{ output->first = 0x0440; {p++; goto _out; } }} + break; + case 1711: +#line 2030 "char_ref.rl" + {te = p+1;{ output->first = 0x2937; {p++; goto _out; } }} + break; + case 1712: +#line 2031 "char_ref.rl" + {te = p+1;{ output->first = 0x2969; {p++; goto _out; } }} + break; + case 1713: +#line 2032 "char_ref.rl" + {te = p+1;{ output->first = 0x201d; {p++; goto _out; } }} + break; + case 1714: +#line 2033 "char_ref.rl" + {te = p+1;{ output->first = 0x201d; {p++; goto _out; } }} + break; + case 1715: +#line 2034 "char_ref.rl" + {te = p+1;{ output->first = 0x21b3; {p++; goto _out; } }} + break; + case 1716: +#line 2035 "char_ref.rl" + {te = p+1;{ output->first = 0x211c; {p++; goto _out; } }} + break; + case 1717: +#line 2036 "char_ref.rl" + {te = p+1;{ output->first = 0x211b; {p++; goto _out; } }} + break; + case 1718: +#line 2037 "char_ref.rl" + {te = p+1;{ output->first = 0x211c; {p++; goto _out; } }} + break; + case 1719: +#line 2038 "char_ref.rl" + {te = p+1;{ output->first = 0x211d; {p++; goto _out; } }} + break; + case 1720: +#line 2039 "char_ref.rl" + {te = p+1;{ output->first = 0x25ad; {p++; goto _out; } }} + break; + case 1721: +#line 2040 "char_ref.rl" + {te = p+1;{ output->first = 0xae; {p++; goto _out; } }} + break; + case 1722: +#line 2042 "char_ref.rl" + {te = p+1;{ output->first = 0x297d; {p++; goto _out; } }} + break; + case 1723: +#line 2043 "char_ref.rl" + {te = p+1;{ output->first = 0x230b; {p++; goto _out; } }} + break; + case 1724: +#line 2044 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d52f; {p++; goto _out; } }} + break; + case 1725: +#line 2045 "char_ref.rl" + {te = p+1;{ output->first = 0x21c1; {p++; goto _out; } }} + break; + case 1726: +#line 2046 "char_ref.rl" + {te = p+1;{ output->first = 0x21c0; {p++; goto _out; } }} + break; + case 1727: +#line 2047 "char_ref.rl" + {te = p+1;{ output->first = 0x296c; {p++; goto _out; } }} + break; + case 1728: +#line 2048 "char_ref.rl" + {te = p+1;{ output->first = 0x03c1; {p++; goto _out; } }} + break; + case 1729: +#line 2049 "char_ref.rl" + {te = p+1;{ output->first = 0x03f1; {p++; goto _out; } }} + break; + case 1730: +#line 2050 "char_ref.rl" + {te = p+1;{ output->first = 0x2192; {p++; goto _out; } }} + break; + case 1731: +#line 2051 "char_ref.rl" + {te = p+1;{ output->first = 0x21a3; {p++; goto _out; } }} + break; + case 1732: +#line 2052 "char_ref.rl" + {te = p+1;{ output->first = 0x21c1; {p++; goto _out; } }} + break; + case 1733: +#line 2053 "char_ref.rl" + {te = p+1;{ output->first = 0x21c0; {p++; goto _out; } }} + break; + case 1734: +#line 2054 "char_ref.rl" + {te = p+1;{ output->first = 0x21c4; {p++; goto _out; } }} + break; + case 1735: +#line 2055 "char_ref.rl" + {te = p+1;{ output->first = 0x21cc; {p++; goto _out; } }} + break; + case 1736: +#line 2056 "char_ref.rl" + {te = p+1;{ output->first = 0x21c9; {p++; goto _out; } }} + break; + case 1737: +#line 2057 "char_ref.rl" + {te = p+1;{ output->first = 0x219d; {p++; goto _out; } }} + break; + case 1738: +#line 2058 "char_ref.rl" + {te = p+1;{ output->first = 0x22cc; {p++; goto _out; } }} + break; + case 1739: +#line 2059 "char_ref.rl" + {te = p+1;{ output->first = 0x02da; {p++; goto _out; } }} + break; + case 1740: +#line 2060 "char_ref.rl" + {te = p+1;{ output->first = 0x2253; {p++; goto _out; } }} + break; + case 1741: +#line 2061 "char_ref.rl" + {te = p+1;{ output->first = 0x21c4; {p++; goto _out; } }} + break; + case 1742: +#line 2062 "char_ref.rl" + {te = p+1;{ output->first = 0x21cc; {p++; goto _out; } }} + break; + case 1743: +#line 2063 "char_ref.rl" + {te = p+1;{ output->first = 0x200f; {p++; goto _out; } }} + break; + case 1744: +#line 2064 "char_ref.rl" + {te = p+1;{ output->first = 0x23b1; {p++; goto _out; } }} + break; + case 1745: +#line 2065 "char_ref.rl" + {te = p+1;{ output->first = 0x23b1; {p++; goto _out; } }} + break; + case 1746: +#line 2066 "char_ref.rl" + {te = p+1;{ output->first = 0x2aee; {p++; goto _out; } }} + break; + case 1747: +#line 2067 "char_ref.rl" + {te = p+1;{ output->first = 0x27ed; {p++; goto _out; } }} + break; + case 1748: +#line 2068 "char_ref.rl" + {te = p+1;{ output->first = 0x21fe; {p++; goto _out; } }} + break; + case 1749: +#line 2069 "char_ref.rl" + {te = p+1;{ output->first = 0x27e7; {p++; goto _out; } }} + break; + case 1750: +#line 2070 "char_ref.rl" + {te = p+1;{ output->first = 0x2986; {p++; goto _out; } }} + break; + case 1751: +#line 2071 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d563; {p++; goto _out; } }} + break; + case 1752: +#line 2072 "char_ref.rl" + {te = p+1;{ output->first = 0x2a2e; {p++; goto _out; } }} + break; + case 1753: +#line 2073 "char_ref.rl" + {te = p+1;{ output->first = 0x2a35; {p++; goto _out; } }} + break; + case 1754: +#line 2074 "char_ref.rl" + {te = p+1;{ output->first = 0x29; {p++; goto _out; } }} + break; + case 1755: +#line 2075 "char_ref.rl" + {te = p+1;{ output->first = 0x2994; {p++; goto _out; } }} + break; + case 1756: +#line 2076 "char_ref.rl" + {te = p+1;{ output->first = 0x2a12; {p++; goto _out; } }} + break; + case 1757: +#line 2077 "char_ref.rl" + {te = p+1;{ output->first = 0x21c9; {p++; goto _out; } }} + break; + case 1758: +#line 2078 "char_ref.rl" + {te = p+1;{ output->first = 0x203a; {p++; goto _out; } }} + break; + case 1759: +#line 2079 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c7; {p++; goto _out; } }} + break; + case 1760: +#line 2080 "char_ref.rl" + {te = p+1;{ output->first = 0x21b1; {p++; goto _out; } }} + break; + case 1761: +#line 2081 "char_ref.rl" + {te = p+1;{ output->first = 0x5d; {p++; goto _out; } }} + break; + case 1762: +#line 2082 "char_ref.rl" + {te = p+1;{ output->first = 0x2019; {p++; goto _out; } }} + break; + case 1763: +#line 2083 "char_ref.rl" + {te = p+1;{ output->first = 0x2019; {p++; goto _out; } }} + break; + case 1764: +#line 2084 "char_ref.rl" + {te = p+1;{ output->first = 0x22cc; {p++; goto _out; } }} + break; + case 1765: +#line 2085 "char_ref.rl" + {te = p+1;{ output->first = 0x22ca; {p++; goto _out; } }} + break; + case 1766: +#line 2086 "char_ref.rl" + {te = p+1;{ output->first = 0x25b9; {p++; goto _out; } }} + break; + case 1767: +#line 2087 "char_ref.rl" + {te = p+1;{ output->first = 0x22b5; {p++; goto _out; } }} + break; + case 1768: +#line 2088 "char_ref.rl" + {te = p+1;{ output->first = 0x25b8; {p++; goto _out; } }} + break; + case 1769: +#line 2089 "char_ref.rl" + {te = p+1;{ output->first = 0x29ce; {p++; goto _out; } }} + break; + case 1770: +#line 2090 "char_ref.rl" + {te = p+1;{ output->first = 0x2968; {p++; goto _out; } }} + break; + case 1771: +#line 2091 "char_ref.rl" + {te = p+1;{ output->first = 0x211e; {p++; goto _out; } }} + break; + case 1772: +#line 2092 "char_ref.rl" + {te = p+1;{ output->first = 0x015b; {p++; goto _out; } }} + break; + case 1773: +#line 2093 "char_ref.rl" + {te = p+1;{ output->first = 0x201a; {p++; goto _out; } }} + break; + case 1774: +#line 2094 "char_ref.rl" + {te = p+1;{ output->first = 0x227b; {p++; goto _out; } }} + break; + case 1775: +#line 2095 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab4; {p++; goto _out; } }} + break; + case 1776: +#line 2096 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab8; {p++; goto _out; } }} + break; + case 1777: +#line 2097 "char_ref.rl" + {te = p+1;{ output->first = 0x0161; {p++; goto _out; } }} + break; + case 1778: +#line 2098 "char_ref.rl" + {te = p+1;{ output->first = 0x227d; {p++; goto _out; } }} + break; + case 1779: +#line 2099 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab0; {p++; goto _out; } }} + break; + case 1780: +#line 2100 "char_ref.rl" + {te = p+1;{ output->first = 0x015f; {p++; goto _out; } }} + break; + case 1781: +#line 2101 "char_ref.rl" + {te = p+1;{ output->first = 0x015d; {p++; goto _out; } }} + break; + case 1782: +#line 2102 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab6; {p++; goto _out; } }} + break; + case 1783: +#line 2103 "char_ref.rl" + {te = p+1;{ output->first = 0x2aba; {p++; goto _out; } }} + break; + case 1784: +#line 2104 "char_ref.rl" + {te = p+1;{ output->first = 0x22e9; {p++; goto _out; } }} + break; + case 1785: +#line 2105 "char_ref.rl" + {te = p+1;{ output->first = 0x2a13; {p++; goto _out; } }} + break; + case 1786: +#line 2106 "char_ref.rl" + {te = p+1;{ output->first = 0x227f; {p++; goto _out; } }} + break; + case 1787: +#line 2107 "char_ref.rl" + {te = p+1;{ output->first = 0x0441; {p++; goto _out; } }} + break; + case 1788: +#line 2108 "char_ref.rl" + {te = p+1;{ output->first = 0x22c5; {p++; goto _out; } }} + break; + case 1789: +#line 2109 "char_ref.rl" + {te = p+1;{ output->first = 0x22a1; {p++; goto _out; } }} + break; + case 1790: +#line 2110 "char_ref.rl" + {te = p+1;{ output->first = 0x2a66; {p++; goto _out; } }} + break; + case 1791: +#line 2111 "char_ref.rl" + {te = p+1;{ output->first = 0x21d8; {p++; goto _out; } }} + break; + case 1792: +#line 2112 "char_ref.rl" + {te = p+1;{ output->first = 0x2925; {p++; goto _out; } }} + break; + case 1793: +#line 2113 "char_ref.rl" + {te = p+1;{ output->first = 0x2198; {p++; goto _out; } }} + break; + case 1794: +#line 2114 "char_ref.rl" + {te = p+1;{ output->first = 0x2198; {p++; goto _out; } }} + break; + case 1795: +#line 2115 "char_ref.rl" + {te = p+1;{ output->first = 0xa7; {p++; goto _out; } }} + break; + case 1796: +#line 2117 "char_ref.rl" + {te = p+1;{ output->first = 0x3b; {p++; goto _out; } }} + break; + case 1797: +#line 2118 "char_ref.rl" + {te = p+1;{ output->first = 0x2929; {p++; goto _out; } }} + break; + case 1798: +#line 2119 "char_ref.rl" + {te = p+1;{ output->first = 0x2216; {p++; goto _out; } }} + break; + case 1799: +#line 2120 "char_ref.rl" + {te = p+1;{ output->first = 0x2216; {p++; goto _out; } }} + break; + case 1800: +#line 2121 "char_ref.rl" + {te = p+1;{ output->first = 0x2736; {p++; goto _out; } }} + break; + case 1801: +#line 2122 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d530; {p++; goto _out; } }} + break; + case 1802: +#line 2123 "char_ref.rl" + {te = p+1;{ output->first = 0x2322; {p++; goto _out; } }} + break; + case 1803: +#line 2124 "char_ref.rl" + {te = p+1;{ output->first = 0x266f; {p++; goto _out; } }} + break; + case 1804: +#line 2125 "char_ref.rl" + {te = p+1;{ output->first = 0x0449; {p++; goto _out; } }} + break; + case 1805: +#line 2126 "char_ref.rl" + {te = p+1;{ output->first = 0x0448; {p++; goto _out; } }} + break; + case 1806: +#line 2127 "char_ref.rl" + {te = p+1;{ output->first = 0x2223; {p++; goto _out; } }} + break; + case 1807: +#line 2128 "char_ref.rl" + {te = p+1;{ output->first = 0x2225; {p++; goto _out; } }} + break; + case 1808: +#line 2129 "char_ref.rl" + {te = p+1;{ output->first = 0xad; {p++; goto _out; } }} + break; + case 1809: +#line 2131 "char_ref.rl" + {te = p+1;{ output->first = 0x03c3; {p++; goto _out; } }} + break; + case 1810: +#line 2132 "char_ref.rl" + {te = p+1;{ output->first = 0x03c2; {p++; goto _out; } }} + break; + case 1811: +#line 2133 "char_ref.rl" + {te = p+1;{ output->first = 0x03c2; {p++; goto _out; } }} + break; + case 1812: +#line 2134 "char_ref.rl" + {te = p+1;{ output->first = 0x223c; {p++; goto _out; } }} + break; + case 1813: +#line 2135 "char_ref.rl" + {te = p+1;{ output->first = 0x2a6a; {p++; goto _out; } }} + break; + case 1814: +#line 2136 "char_ref.rl" + {te = p+1;{ output->first = 0x2243; {p++; goto _out; } }} + break; + case 1815: +#line 2137 "char_ref.rl" + {te = p+1;{ output->first = 0x2243; {p++; goto _out; } }} + break; + case 1816: +#line 2138 "char_ref.rl" + {te = p+1;{ output->first = 0x2a9e; {p++; goto _out; } }} + break; + case 1817: +#line 2139 "char_ref.rl" + {te = p+1;{ output->first = 0x2aa0; {p++; goto _out; } }} + break; + case 1818: +#line 2140 "char_ref.rl" + {te = p+1;{ output->first = 0x2a9d; {p++; goto _out; } }} + break; + case 1819: +#line 2141 "char_ref.rl" + {te = p+1;{ output->first = 0x2a9f; {p++; goto _out; } }} + break; + case 1820: +#line 2142 "char_ref.rl" + {te = p+1;{ output->first = 0x2246; {p++; goto _out; } }} + break; + case 1821: +#line 2143 "char_ref.rl" + {te = p+1;{ output->first = 0x2a24; {p++; goto _out; } }} + break; + case 1822: +#line 2144 "char_ref.rl" + {te = p+1;{ output->first = 0x2972; {p++; goto _out; } }} + break; + case 1823: +#line 2145 "char_ref.rl" + {te = p+1;{ output->first = 0x2190; {p++; goto _out; } }} + break; + case 1824: +#line 2146 "char_ref.rl" + {te = p+1;{ output->first = 0x2216; {p++; goto _out; } }} + break; + case 1825: +#line 2147 "char_ref.rl" + {te = p+1;{ output->first = 0x2a33; {p++; goto _out; } }} + break; + case 1826: +#line 2148 "char_ref.rl" + {te = p+1;{ output->first = 0x29e4; {p++; goto _out; } }} + break; + case 1827: +#line 2149 "char_ref.rl" + {te = p+1;{ output->first = 0x2223; {p++; goto _out; } }} + break; + case 1828: +#line 2150 "char_ref.rl" + {te = p+1;{ output->first = 0x2323; {p++; goto _out; } }} + break; + case 1829: +#line 2151 "char_ref.rl" + {te = p+1;{ output->first = 0x2aaa; {p++; goto _out; } }} + break; + case 1830: +#line 2152 "char_ref.rl" + {te = p+1;{ output->first = 0x2aac; {p++; goto _out; } }} + break; + case 1831: +#line 2153 "char_ref.rl" + {te = p+1;{ output->first = 0x2aac; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1832: +#line 2154 "char_ref.rl" + {te = p+1;{ output->first = 0x044c; {p++; goto _out; } }} + break; + case 1833: +#line 2155 "char_ref.rl" + {te = p+1;{ output->first = 0x2f; {p++; goto _out; } }} + break; + case 1834: +#line 2156 "char_ref.rl" + {te = p+1;{ output->first = 0x29c4; {p++; goto _out; } }} + break; + case 1835: +#line 2157 "char_ref.rl" + {te = p+1;{ output->first = 0x233f; {p++; goto _out; } }} + break; + case 1836: +#line 2158 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d564; {p++; goto _out; } }} + break; + case 1837: +#line 2159 "char_ref.rl" + {te = p+1;{ output->first = 0x2660; {p++; goto _out; } }} + break; + case 1838: +#line 2160 "char_ref.rl" + {te = p+1;{ output->first = 0x2660; {p++; goto _out; } }} + break; + case 1839: +#line 2161 "char_ref.rl" + {te = p+1;{ output->first = 0x2225; {p++; goto _out; } }} + break; + case 1840: +#line 2162 "char_ref.rl" + {te = p+1;{ output->first = 0x2293; {p++; goto _out; } }} + break; + case 1841: +#line 2163 "char_ref.rl" + {te = p+1;{ output->first = 0x2293; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1842: +#line 2164 "char_ref.rl" + {te = p+1;{ output->first = 0x2294; {p++; goto _out; } }} + break; + case 1843: +#line 2165 "char_ref.rl" + {te = p+1;{ output->first = 0x2294; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 1844: +#line 2166 "char_ref.rl" + {te = p+1;{ output->first = 0x228f; {p++; goto _out; } }} + break; + case 1845: +#line 2167 "char_ref.rl" + {te = p+1;{ output->first = 0x2291; {p++; goto _out; } }} + break; + case 1846: +#line 2168 "char_ref.rl" + {te = p+1;{ output->first = 0x228f; {p++; goto _out; } }} + break; + case 1847: +#line 2169 "char_ref.rl" + {te = p+1;{ output->first = 0x2291; {p++; goto _out; } }} + break; + case 1848: +#line 2170 "char_ref.rl" + {te = p+1;{ output->first = 0x2290; {p++; goto _out; } }} + break; + case 1849: +#line 2171 "char_ref.rl" + {te = p+1;{ output->first = 0x2292; {p++; goto _out; } }} + break; + case 1850: +#line 2172 "char_ref.rl" + {te = p+1;{ output->first = 0x2290; {p++; goto _out; } }} + break; + case 1851: +#line 2173 "char_ref.rl" + {te = p+1;{ output->first = 0x2292; {p++; goto _out; } }} + break; + case 1852: +#line 2174 "char_ref.rl" + {te = p+1;{ output->first = 0x25a1; {p++; goto _out; } }} + break; + case 1853: +#line 2175 "char_ref.rl" + {te = p+1;{ output->first = 0x25a1; {p++; goto _out; } }} + break; + case 1854: +#line 2176 "char_ref.rl" + {te = p+1;{ output->first = 0x25aa; {p++; goto _out; } }} + break; + case 1855: +#line 2177 "char_ref.rl" + {te = p+1;{ output->first = 0x25aa; {p++; goto _out; } }} + break; + case 1856: +#line 2178 "char_ref.rl" + {te = p+1;{ output->first = 0x2192; {p++; goto _out; } }} + break; + case 1857: +#line 2179 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c8; {p++; goto _out; } }} + break; + case 1858: +#line 2180 "char_ref.rl" + {te = p+1;{ output->first = 0x2216; {p++; goto _out; } }} + break; + case 1859: +#line 2181 "char_ref.rl" + {te = p+1;{ output->first = 0x2323; {p++; goto _out; } }} + break; + case 1860: +#line 2182 "char_ref.rl" + {te = p+1;{ output->first = 0x22c6; {p++; goto _out; } }} + break; + case 1861: +#line 2183 "char_ref.rl" + {te = p+1;{ output->first = 0x2606; {p++; goto _out; } }} + break; + case 1862: +#line 2184 "char_ref.rl" + {te = p+1;{ output->first = 0x2605; {p++; goto _out; } }} + break; + case 1863: +#line 2185 "char_ref.rl" + {te = p+1;{ output->first = 0x03f5; {p++; goto _out; } }} + break; + case 1864: +#line 2186 "char_ref.rl" + {te = p+1;{ output->first = 0x03d5; {p++; goto _out; } }} + break; + case 1865: +#line 2187 "char_ref.rl" + {te = p+1;{ output->first = 0xaf; {p++; goto _out; } }} + break; + case 1866: +#line 2188 "char_ref.rl" + {te = p+1;{ output->first = 0x2282; {p++; goto _out; } }} + break; + case 1867: +#line 2189 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac5; {p++; goto _out; } }} + break; + case 1868: +#line 2190 "char_ref.rl" + {te = p+1;{ output->first = 0x2abd; {p++; goto _out; } }} + break; + case 1869: +#line 2191 "char_ref.rl" + {te = p+1;{ output->first = 0x2286; {p++; goto _out; } }} + break; + case 1870: +#line 2192 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac3; {p++; goto _out; } }} + break; + case 1871: +#line 2193 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac1; {p++; goto _out; } }} + break; + case 1872: +#line 2194 "char_ref.rl" + {te = p+1;{ output->first = 0x2acb; {p++; goto _out; } }} + break; + case 1873: +#line 2195 "char_ref.rl" + {te = p+1;{ output->first = 0x228a; {p++; goto _out; } }} + break; + case 1874: +#line 2196 "char_ref.rl" + {te = p+1;{ output->first = 0x2abf; {p++; goto _out; } }} + break; + case 1875: +#line 2197 "char_ref.rl" + {te = p+1;{ output->first = 0x2979; {p++; goto _out; } }} + break; + case 1876: +#line 2198 "char_ref.rl" + {te = p+1;{ output->first = 0x2282; {p++; goto _out; } }} + break; + case 1877: +#line 2199 "char_ref.rl" + {te = p+1;{ output->first = 0x2286; {p++; goto _out; } }} + break; + case 1878: +#line 2200 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac5; {p++; goto _out; } }} + break; + case 1879: +#line 2201 "char_ref.rl" + {te = p+1;{ output->first = 0x228a; {p++; goto _out; } }} + break; + case 1880: +#line 2202 "char_ref.rl" + {te = p+1;{ output->first = 0x2acb; {p++; goto _out; } }} + break; + case 1881: +#line 2203 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac7; {p++; goto _out; } }} + break; + case 1882: +#line 2204 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad5; {p++; goto _out; } }} + break; + case 1883: +#line 2205 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad3; {p++; goto _out; } }} + break; + case 1884: +#line 2206 "char_ref.rl" + {te = p+1;{ output->first = 0x227b; {p++; goto _out; } }} + break; + case 1885: +#line 2207 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab8; {p++; goto _out; } }} + break; + case 1886: +#line 2208 "char_ref.rl" + {te = p+1;{ output->first = 0x227d; {p++; goto _out; } }} + break; + case 1887: +#line 2209 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab0; {p++; goto _out; } }} + break; + case 1888: +#line 2210 "char_ref.rl" + {te = p+1;{ output->first = 0x2aba; {p++; goto _out; } }} + break; + case 1889: +#line 2211 "char_ref.rl" + {te = p+1;{ output->first = 0x2ab6; {p++; goto _out; } }} + break; + case 1890: +#line 2212 "char_ref.rl" + {te = p+1;{ output->first = 0x22e9; {p++; goto _out; } }} + break; + case 1891: +#line 2213 "char_ref.rl" + {te = p+1;{ output->first = 0x227f; {p++; goto _out; } }} + break; + case 1892: +#line 2214 "char_ref.rl" + {te = p+1;{ output->first = 0x2211; {p++; goto _out; } }} + break; + case 1893: +#line 2215 "char_ref.rl" + {te = p+1;{ output->first = 0x266a; {p++; goto _out; } }} + break; + case 1894: +#line 2216 "char_ref.rl" + {te = p+1;{ output->first = 0xb9; {p++; goto _out; } }} + break; + case 1895: +#line 2218 "char_ref.rl" + {te = p+1;{ output->first = 0xb2; {p++; goto _out; } }} + break; + case 1896: +#line 2220 "char_ref.rl" + {te = p+1;{ output->first = 0xb3; {p++; goto _out; } }} + break; + case 1897: +#line 2222 "char_ref.rl" + {te = p+1;{ output->first = 0x2283; {p++; goto _out; } }} + break; + case 1898: +#line 2223 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac6; {p++; goto _out; } }} + break; + case 1899: +#line 2224 "char_ref.rl" + {te = p+1;{ output->first = 0x2abe; {p++; goto _out; } }} + break; + case 1900: +#line 2225 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad8; {p++; goto _out; } }} + break; + case 1901: +#line 2226 "char_ref.rl" + {te = p+1;{ output->first = 0x2287; {p++; goto _out; } }} + break; + case 1902: +#line 2227 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac4; {p++; goto _out; } }} + break; + case 1903: +#line 2228 "char_ref.rl" + {te = p+1;{ output->first = 0x27c9; {p++; goto _out; } }} + break; + case 1904: +#line 2229 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad7; {p++; goto _out; } }} + break; + case 1905: +#line 2230 "char_ref.rl" + {te = p+1;{ output->first = 0x297b; {p++; goto _out; } }} + break; + case 1906: +#line 2231 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac2; {p++; goto _out; } }} + break; + case 1907: +#line 2232 "char_ref.rl" + {te = p+1;{ output->first = 0x2acc; {p++; goto _out; } }} + break; + case 1908: +#line 2233 "char_ref.rl" + {te = p+1;{ output->first = 0x228b; {p++; goto _out; } }} + break; + case 1909: +#line 2234 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac0; {p++; goto _out; } }} + break; + case 1910: +#line 2235 "char_ref.rl" + {te = p+1;{ output->first = 0x2283; {p++; goto _out; } }} + break; + case 1911: +#line 2236 "char_ref.rl" + {te = p+1;{ output->first = 0x2287; {p++; goto _out; } }} + break; + case 1912: +#line 2237 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac6; {p++; goto _out; } }} + break; + case 1913: +#line 2238 "char_ref.rl" + {te = p+1;{ output->first = 0x228b; {p++; goto _out; } }} + break; + case 1914: +#line 2239 "char_ref.rl" + {te = p+1;{ output->first = 0x2acc; {p++; goto _out; } }} + break; + case 1915: +#line 2240 "char_ref.rl" + {te = p+1;{ output->first = 0x2ac8; {p++; goto _out; } }} + break; + case 1916: +#line 2241 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad4; {p++; goto _out; } }} + break; + case 1917: +#line 2242 "char_ref.rl" + {te = p+1;{ output->first = 0x2ad6; {p++; goto _out; } }} + break; + case 1918: +#line 2243 "char_ref.rl" + {te = p+1;{ output->first = 0x21d9; {p++; goto _out; } }} + break; + case 1919: +#line 2244 "char_ref.rl" + {te = p+1;{ output->first = 0x2926; {p++; goto _out; } }} + break; + case 1920: +#line 2245 "char_ref.rl" + {te = p+1;{ output->first = 0x2199; {p++; goto _out; } }} + break; + case 1921: +#line 2246 "char_ref.rl" + {te = p+1;{ output->first = 0x2199; {p++; goto _out; } }} + break; + case 1922: +#line 2247 "char_ref.rl" + {te = p+1;{ output->first = 0x292a; {p++; goto _out; } }} + break; + case 1923: +#line 2248 "char_ref.rl" + {te = p+1;{ output->first = 0xdf; {p++; goto _out; } }} + break; + case 1924: +#line 2250 "char_ref.rl" + {te = p+1;{ output->first = 0x2316; {p++; goto _out; } }} + break; + case 1925: +#line 2251 "char_ref.rl" + {te = p+1;{ output->first = 0x03c4; {p++; goto _out; } }} + break; + case 1926: +#line 2252 "char_ref.rl" + {te = p+1;{ output->first = 0x23b4; {p++; goto _out; } }} + break; + case 1927: +#line 2253 "char_ref.rl" + {te = p+1;{ output->first = 0x0165; {p++; goto _out; } }} + break; + case 1928: +#line 2254 "char_ref.rl" + {te = p+1;{ output->first = 0x0163; {p++; goto _out; } }} + break; + case 1929: +#line 2255 "char_ref.rl" + {te = p+1;{ output->first = 0x0442; {p++; goto _out; } }} + break; + case 1930: +#line 2256 "char_ref.rl" + {te = p+1;{ output->first = 0x20db; {p++; goto _out; } }} + break; + case 1931: +#line 2257 "char_ref.rl" + {te = p+1;{ output->first = 0x2315; {p++; goto _out; } }} + break; + case 1932: +#line 2258 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d531; {p++; goto _out; } }} + break; + case 1933: +#line 2259 "char_ref.rl" + {te = p+1;{ output->first = 0x2234; {p++; goto _out; } }} + break; + case 1934: +#line 2260 "char_ref.rl" + {te = p+1;{ output->first = 0x2234; {p++; goto _out; } }} + break; + case 1935: +#line 2261 "char_ref.rl" + {te = p+1;{ output->first = 0x03b8; {p++; goto _out; } }} + break; + case 1936: +#line 2262 "char_ref.rl" + {te = p+1;{ output->first = 0x03d1; {p++; goto _out; } }} + break; + case 1937: +#line 2263 "char_ref.rl" + {te = p+1;{ output->first = 0x03d1; {p++; goto _out; } }} + break; + case 1938: +#line 2264 "char_ref.rl" + {te = p+1;{ output->first = 0x2248; {p++; goto _out; } }} + break; + case 1939: +#line 2265 "char_ref.rl" + {te = p+1;{ output->first = 0x223c; {p++; goto _out; } }} + break; + case 1940: +#line 2266 "char_ref.rl" + {te = p+1;{ output->first = 0x2009; {p++; goto _out; } }} + break; + case 1941: +#line 2267 "char_ref.rl" + {te = p+1;{ output->first = 0x2248; {p++; goto _out; } }} + break; + case 1942: +#line 2268 "char_ref.rl" + {te = p+1;{ output->first = 0x223c; {p++; goto _out; } }} + break; + case 1943: +#line 2269 "char_ref.rl" + {te = p+1;{ output->first = 0xfe; {p++; goto _out; } }} + break; + case 1944: +#line 2271 "char_ref.rl" + {te = p+1;{ output->first = 0x02dc; {p++; goto _out; } }} + break; + case 1945: +#line 2272 "char_ref.rl" + {te = p+1;{ output->first = 0xd7; {p++; goto _out; } }} + break; + case 1946: +#line 2274 "char_ref.rl" + {te = p+1;{ output->first = 0x22a0; {p++; goto _out; } }} + break; + case 1947: +#line 2275 "char_ref.rl" + {te = p+1;{ output->first = 0x2a31; {p++; goto _out; } }} + break; + case 1948: +#line 2276 "char_ref.rl" + {te = p+1;{ output->first = 0x2a30; {p++; goto _out; } }} + break; + case 1949: +#line 2277 "char_ref.rl" + {te = p+1;{ output->first = 0x222d; {p++; goto _out; } }} + break; + case 1950: +#line 2278 "char_ref.rl" + {te = p+1;{ output->first = 0x2928; {p++; goto _out; } }} + break; + case 1951: +#line 2279 "char_ref.rl" + {te = p+1;{ output->first = 0x22a4; {p++; goto _out; } }} + break; + case 1952: +#line 2280 "char_ref.rl" + {te = p+1;{ output->first = 0x2336; {p++; goto _out; } }} + break; + case 1953: +#line 2281 "char_ref.rl" + {te = p+1;{ output->first = 0x2af1; {p++; goto _out; } }} + break; + case 1954: +#line 2282 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d565; {p++; goto _out; } }} + break; + case 1955: +#line 2283 "char_ref.rl" + {te = p+1;{ output->first = 0x2ada; {p++; goto _out; } }} + break; + case 1956: +#line 2284 "char_ref.rl" + {te = p+1;{ output->first = 0x2929; {p++; goto _out; } }} + break; + case 1957: +#line 2285 "char_ref.rl" + {te = p+1;{ output->first = 0x2034; {p++; goto _out; } }} + break; + case 1958: +#line 2286 "char_ref.rl" + {te = p+1;{ output->first = 0x2122; {p++; goto _out; } }} + break; + case 1959: +#line 2287 "char_ref.rl" + {te = p+1;{ output->first = 0x25b5; {p++; goto _out; } }} + break; + case 1960: +#line 2288 "char_ref.rl" + {te = p+1;{ output->first = 0x25bf; {p++; goto _out; } }} + break; + case 1961: +#line 2289 "char_ref.rl" + {te = p+1;{ output->first = 0x25c3; {p++; goto _out; } }} + break; + case 1962: +#line 2290 "char_ref.rl" + {te = p+1;{ output->first = 0x22b4; {p++; goto _out; } }} + break; + case 1963: +#line 2291 "char_ref.rl" + {te = p+1;{ output->first = 0x225c; {p++; goto _out; } }} + break; + case 1964: +#line 2292 "char_ref.rl" + {te = p+1;{ output->first = 0x25b9; {p++; goto _out; } }} + break; + case 1965: +#line 2293 "char_ref.rl" + {te = p+1;{ output->first = 0x22b5; {p++; goto _out; } }} + break; + case 1966: +#line 2294 "char_ref.rl" + {te = p+1;{ output->first = 0x25ec; {p++; goto _out; } }} + break; + case 1967: +#line 2295 "char_ref.rl" + {te = p+1;{ output->first = 0x225c; {p++; goto _out; } }} + break; + case 1968: +#line 2296 "char_ref.rl" + {te = p+1;{ output->first = 0x2a3a; {p++; goto _out; } }} + break; + case 1969: +#line 2297 "char_ref.rl" + {te = p+1;{ output->first = 0x2a39; {p++; goto _out; } }} + break; + case 1970: +#line 2298 "char_ref.rl" + {te = p+1;{ output->first = 0x29cd; {p++; goto _out; } }} + break; + case 1971: +#line 2299 "char_ref.rl" + {te = p+1;{ output->first = 0x2a3b; {p++; goto _out; } }} + break; + case 1972: +#line 2300 "char_ref.rl" + {te = p+1;{ output->first = 0x23e2; {p++; goto _out; } }} + break; + case 1973: +#line 2301 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4c9; {p++; goto _out; } }} + break; + case 1974: +#line 2302 "char_ref.rl" + {te = p+1;{ output->first = 0x0446; {p++; goto _out; } }} + break; + case 1975: +#line 2303 "char_ref.rl" + {te = p+1;{ output->first = 0x045b; {p++; goto _out; } }} + break; + case 1976: +#line 2304 "char_ref.rl" + {te = p+1;{ output->first = 0x0167; {p++; goto _out; } }} + break; + case 1977: +#line 2305 "char_ref.rl" + {te = p+1;{ output->first = 0x226c; {p++; goto _out; } }} + break; + case 1978: +#line 2306 "char_ref.rl" + {te = p+1;{ output->first = 0x219e; {p++; goto _out; } }} + break; + case 1979: +#line 2307 "char_ref.rl" + {te = p+1;{ output->first = 0x21a0; {p++; goto _out; } }} + break; + case 1980: +#line 2308 "char_ref.rl" + {te = p+1;{ output->first = 0x21d1; {p++; goto _out; } }} + break; + case 1981: +#line 2309 "char_ref.rl" + {te = p+1;{ output->first = 0x2963; {p++; goto _out; } }} + break; + case 1982: +#line 2310 "char_ref.rl" + {te = p+1;{ output->first = 0xfa; {p++; goto _out; } }} + break; + case 1983: +#line 2312 "char_ref.rl" + {te = p+1;{ output->first = 0x2191; {p++; goto _out; } }} + break; + case 1984: +#line 2313 "char_ref.rl" + {te = p+1;{ output->first = 0x045e; {p++; goto _out; } }} + break; + case 1985: +#line 2314 "char_ref.rl" + {te = p+1;{ output->first = 0x016d; {p++; goto _out; } }} + break; + case 1986: +#line 2315 "char_ref.rl" + {te = p+1;{ output->first = 0xfb; {p++; goto _out; } }} + break; + case 1987: +#line 2317 "char_ref.rl" + {te = p+1;{ output->first = 0x0443; {p++; goto _out; } }} + break; + case 1988: +#line 2318 "char_ref.rl" + {te = p+1;{ output->first = 0x21c5; {p++; goto _out; } }} + break; + case 1989: +#line 2319 "char_ref.rl" + {te = p+1;{ output->first = 0x0171; {p++; goto _out; } }} + break; + case 1990: +#line 2320 "char_ref.rl" + {te = p+1;{ output->first = 0x296e; {p++; goto _out; } }} + break; + case 1991: +#line 2321 "char_ref.rl" + {te = p+1;{ output->first = 0x297e; {p++; goto _out; } }} + break; + case 1992: +#line 2322 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d532; {p++; goto _out; } }} + break; + case 1993: +#line 2323 "char_ref.rl" + {te = p+1;{ output->first = 0xf9; {p++; goto _out; } }} + break; + case 1994: +#line 2325 "char_ref.rl" + {te = p+1;{ output->first = 0x21bf; {p++; goto _out; } }} + break; + case 1995: +#line 2326 "char_ref.rl" + {te = p+1;{ output->first = 0x21be; {p++; goto _out; } }} + break; + case 1996: +#line 2327 "char_ref.rl" + {te = p+1;{ output->first = 0x2580; {p++; goto _out; } }} + break; + case 1997: +#line 2328 "char_ref.rl" + {te = p+1;{ output->first = 0x231c; {p++; goto _out; } }} + break; + case 1998: +#line 2329 "char_ref.rl" + {te = p+1;{ output->first = 0x231c; {p++; goto _out; } }} + break; + case 1999: +#line 2330 "char_ref.rl" + {te = p+1;{ output->first = 0x230f; {p++; goto _out; } }} + break; + case 2000: +#line 2331 "char_ref.rl" + {te = p+1;{ output->first = 0x25f8; {p++; goto _out; } }} + break; + case 2001: +#line 2332 "char_ref.rl" + {te = p+1;{ output->first = 0x016b; {p++; goto _out; } }} + break; + case 2002: +#line 2333 "char_ref.rl" + {te = p+1;{ output->first = 0xa8; {p++; goto _out; } }} + break; + case 2003: +#line 2335 "char_ref.rl" + {te = p+1;{ output->first = 0x0173; {p++; goto _out; } }} + break; + case 2004: +#line 2336 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d566; {p++; goto _out; } }} + break; + case 2005: +#line 2337 "char_ref.rl" + {te = p+1;{ output->first = 0x2191; {p++; goto _out; } }} + break; + case 2006: +#line 2338 "char_ref.rl" + {te = p+1;{ output->first = 0x2195; {p++; goto _out; } }} + break; + case 2007: +#line 2339 "char_ref.rl" + {te = p+1;{ output->first = 0x21bf; {p++; goto _out; } }} + break; + case 2008: +#line 2340 "char_ref.rl" + {te = p+1;{ output->first = 0x21be; {p++; goto _out; } }} + break; + case 2009: +#line 2341 "char_ref.rl" + {te = p+1;{ output->first = 0x228e; {p++; goto _out; } }} + break; + case 2010: +#line 2342 "char_ref.rl" + {te = p+1;{ output->first = 0x03c5; {p++; goto _out; } }} + break; + case 2011: +#line 2343 "char_ref.rl" + {te = p+1;{ output->first = 0x03d2; {p++; goto _out; } }} + break; + case 2012: +#line 2344 "char_ref.rl" + {te = p+1;{ output->first = 0x03c5; {p++; goto _out; } }} + break; + case 2013: +#line 2345 "char_ref.rl" + {te = p+1;{ output->first = 0x21c8; {p++; goto _out; } }} + break; + case 2014: +#line 2346 "char_ref.rl" + {te = p+1;{ output->first = 0x231d; {p++; goto _out; } }} + break; + case 2015: +#line 2347 "char_ref.rl" + {te = p+1;{ output->first = 0x231d; {p++; goto _out; } }} + break; + case 2016: +#line 2348 "char_ref.rl" + {te = p+1;{ output->first = 0x230e; {p++; goto _out; } }} + break; + case 2017: +#line 2349 "char_ref.rl" + {te = p+1;{ output->first = 0x016f; {p++; goto _out; } }} + break; + case 2018: +#line 2350 "char_ref.rl" + {te = p+1;{ output->first = 0x25f9; {p++; goto _out; } }} + break; + case 2019: +#line 2351 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4ca; {p++; goto _out; } }} + break; + case 2020: +#line 2352 "char_ref.rl" + {te = p+1;{ output->first = 0x22f0; {p++; goto _out; } }} + break; + case 2021: +#line 2353 "char_ref.rl" + {te = p+1;{ output->first = 0x0169; {p++; goto _out; } }} + break; + case 2022: +#line 2354 "char_ref.rl" + {te = p+1;{ output->first = 0x25b5; {p++; goto _out; } }} + break; + case 2023: +#line 2355 "char_ref.rl" + {te = p+1;{ output->first = 0x25b4; {p++; goto _out; } }} + break; + case 2024: +#line 2356 "char_ref.rl" + {te = p+1;{ output->first = 0x21c8; {p++; goto _out; } }} + break; + case 2025: +#line 2357 "char_ref.rl" + {te = p+1;{ output->first = 0xfc; {p++; goto _out; } }} + break; + case 2026: +#line 2359 "char_ref.rl" + {te = p+1;{ output->first = 0x29a7; {p++; goto _out; } }} + break; + case 2027: +#line 2360 "char_ref.rl" + {te = p+1;{ output->first = 0x21d5; {p++; goto _out; } }} + break; + case 2028: +#line 2361 "char_ref.rl" + {te = p+1;{ output->first = 0x2ae8; {p++; goto _out; } }} + break; + case 2029: +#line 2362 "char_ref.rl" + {te = p+1;{ output->first = 0x2ae9; {p++; goto _out; } }} + break; + case 2030: +#line 2363 "char_ref.rl" + {te = p+1;{ output->first = 0x22a8; {p++; goto _out; } }} + break; + case 2031: +#line 2364 "char_ref.rl" + {te = p+1;{ output->first = 0x299c; {p++; goto _out; } }} + break; + case 2032: +#line 2365 "char_ref.rl" + {te = p+1;{ output->first = 0x03f5; {p++; goto _out; } }} + break; + case 2033: +#line 2366 "char_ref.rl" + {te = p+1;{ output->first = 0x03f0; {p++; goto _out; } }} + break; + case 2034: +#line 2367 "char_ref.rl" + {te = p+1;{ output->first = 0x2205; {p++; goto _out; } }} + break; + case 2035: +#line 2368 "char_ref.rl" + {te = p+1;{ output->first = 0x03d5; {p++; goto _out; } }} + break; + case 2036: +#line 2369 "char_ref.rl" + {te = p+1;{ output->first = 0x03d6; {p++; goto _out; } }} + break; + case 2037: +#line 2370 "char_ref.rl" + {te = p+1;{ output->first = 0x221d; {p++; goto _out; } }} + break; + case 2038: +#line 2371 "char_ref.rl" + {te = p+1;{ output->first = 0x2195; {p++; goto _out; } }} + break; + case 2039: +#line 2372 "char_ref.rl" + {te = p+1;{ output->first = 0x03f1; {p++; goto _out; } }} + break; + case 2040: +#line 2373 "char_ref.rl" + {te = p+1;{ output->first = 0x03c2; {p++; goto _out; } }} + break; + case 2041: +#line 2374 "char_ref.rl" + {te = p+1;{ output->first = 0x228a; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2042: +#line 2375 "char_ref.rl" + {te = p+1;{ output->first = 0x2acb; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2043: +#line 2376 "char_ref.rl" + {te = p+1;{ output->first = 0x228b; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2044: +#line 2377 "char_ref.rl" + {te = p+1;{ output->first = 0x2acc; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2045: +#line 2378 "char_ref.rl" + {te = p+1;{ output->first = 0x03d1; {p++; goto _out; } }} + break; + case 2046: +#line 2379 "char_ref.rl" + {te = p+1;{ output->first = 0x22b2; {p++; goto _out; } }} + break; + case 2047: +#line 2380 "char_ref.rl" + {te = p+1;{ output->first = 0x22b3; {p++; goto _out; } }} + break; + case 2048: +#line 2381 "char_ref.rl" + {te = p+1;{ output->first = 0x0432; {p++; goto _out; } }} + break; + case 2049: +#line 2382 "char_ref.rl" + {te = p+1;{ output->first = 0x22a2; {p++; goto _out; } }} + break; + case 2050: +#line 2383 "char_ref.rl" + {te = p+1;{ output->first = 0x2228; {p++; goto _out; } }} + break; + case 2051: +#line 2384 "char_ref.rl" + {te = p+1;{ output->first = 0x22bb; {p++; goto _out; } }} + break; + case 2052: +#line 2385 "char_ref.rl" + {te = p+1;{ output->first = 0x225a; {p++; goto _out; } }} + break; + case 2053: +#line 2386 "char_ref.rl" + {te = p+1;{ output->first = 0x22ee; {p++; goto _out; } }} + break; + case 2054: +#line 2387 "char_ref.rl" + {te = p+1;{ output->first = 0x7c; {p++; goto _out; } }} + break; + case 2055: +#line 2388 "char_ref.rl" + {te = p+1;{ output->first = 0x7c; {p++; goto _out; } }} + break; + case 2056: +#line 2389 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d533; {p++; goto _out; } }} + break; + case 2057: +#line 2390 "char_ref.rl" + {te = p+1;{ output->first = 0x22b2; {p++; goto _out; } }} + break; + case 2058: +#line 2391 "char_ref.rl" + {te = p+1;{ output->first = 0x2282; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 2059: +#line 2392 "char_ref.rl" + {te = p+1;{ output->first = 0x2283; output->second = 0x20d2; {p++; goto _out; } }} + break; + case 2060: +#line 2393 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d567; {p++; goto _out; } }} + break; + case 2061: +#line 2394 "char_ref.rl" + {te = p+1;{ output->first = 0x221d; {p++; goto _out; } }} + break; + case 2062: +#line 2395 "char_ref.rl" + {te = p+1;{ output->first = 0x22b3; {p++; goto _out; } }} + break; + case 2063: +#line 2396 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4cb; {p++; goto _out; } }} + break; + case 2064: +#line 2397 "char_ref.rl" + {te = p+1;{ output->first = 0x2acb; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2065: +#line 2398 "char_ref.rl" + {te = p+1;{ output->first = 0x228a; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2066: +#line 2399 "char_ref.rl" + {te = p+1;{ output->first = 0x2acc; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2067: +#line 2400 "char_ref.rl" + {te = p+1;{ output->first = 0x228b; output->second = 0xfe00; {p++; goto _out; } }} + break; + case 2068: +#line 2401 "char_ref.rl" + {te = p+1;{ output->first = 0x299a; {p++; goto _out; } }} + break; + case 2069: +#line 2402 "char_ref.rl" + {te = p+1;{ output->first = 0x0175; {p++; goto _out; } }} + break; + case 2070: +#line 2403 "char_ref.rl" + {te = p+1;{ output->first = 0x2a5f; {p++; goto _out; } }} + break; + case 2071: +#line 2404 "char_ref.rl" + {te = p+1;{ output->first = 0x2227; {p++; goto _out; } }} + break; + case 2072: +#line 2405 "char_ref.rl" + {te = p+1;{ output->first = 0x2259; {p++; goto _out; } }} + break; + case 2073: +#line 2406 "char_ref.rl" + {te = p+1;{ output->first = 0x2118; {p++; goto _out; } }} + break; + case 2074: +#line 2407 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d534; {p++; goto _out; } }} + break; + case 2075: +#line 2408 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d568; {p++; goto _out; } }} + break; + case 2076: +#line 2409 "char_ref.rl" + {te = p+1;{ output->first = 0x2118; {p++; goto _out; } }} + break; + case 2077: +#line 2410 "char_ref.rl" + {te = p+1;{ output->first = 0x2240; {p++; goto _out; } }} + break; + case 2078: +#line 2411 "char_ref.rl" + {te = p+1;{ output->first = 0x2240; {p++; goto _out; } }} + break; + case 2079: +#line 2412 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4cc; {p++; goto _out; } }} + break; + case 2080: +#line 2413 "char_ref.rl" + {te = p+1;{ output->first = 0x22c2; {p++; goto _out; } }} + break; + case 2081: +#line 2414 "char_ref.rl" + {te = p+1;{ output->first = 0x25ef; {p++; goto _out; } }} + break; + case 2082: +#line 2415 "char_ref.rl" + {te = p+1;{ output->first = 0x22c3; {p++; goto _out; } }} + break; + case 2083: +#line 2416 "char_ref.rl" + {te = p+1;{ output->first = 0x25bd; {p++; goto _out; } }} + break; + case 2084: +#line 2417 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d535; {p++; goto _out; } }} + break; + case 2085: +#line 2418 "char_ref.rl" + {te = p+1;{ output->first = 0x27fa; {p++; goto _out; } }} + break; + case 2086: +#line 2419 "char_ref.rl" + {te = p+1;{ output->first = 0x27f7; {p++; goto _out; } }} + break; + case 2087: +#line 2420 "char_ref.rl" + {te = p+1;{ output->first = 0x03be; {p++; goto _out; } }} + break; + case 2088: +#line 2421 "char_ref.rl" + {te = p+1;{ output->first = 0x27f8; {p++; goto _out; } }} + break; + case 2089: +#line 2422 "char_ref.rl" + {te = p+1;{ output->first = 0x27f5; {p++; goto _out; } }} + break; + case 2090: +#line 2423 "char_ref.rl" + {te = p+1;{ output->first = 0x27fc; {p++; goto _out; } }} + break; + case 2091: +#line 2424 "char_ref.rl" + {te = p+1;{ output->first = 0x22fb; {p++; goto _out; } }} + break; + case 2092: +#line 2425 "char_ref.rl" + {te = p+1;{ output->first = 0x2a00; {p++; goto _out; } }} + break; + case 2093: +#line 2426 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d569; {p++; goto _out; } }} + break; + case 2094: +#line 2427 "char_ref.rl" + {te = p+1;{ output->first = 0x2a01; {p++; goto _out; } }} + break; + case 2095: +#line 2428 "char_ref.rl" + {te = p+1;{ output->first = 0x2a02; {p++; goto _out; } }} + break; + case 2096: +#line 2429 "char_ref.rl" + {te = p+1;{ output->first = 0x27f9; {p++; goto _out; } }} + break; + case 2097: +#line 2430 "char_ref.rl" + {te = p+1;{ output->first = 0x27f6; {p++; goto _out; } }} + break; + case 2098: +#line 2431 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4cd; {p++; goto _out; } }} + break; + case 2099: +#line 2432 "char_ref.rl" + {te = p+1;{ output->first = 0x2a06; {p++; goto _out; } }} + break; + case 2100: +#line 2433 "char_ref.rl" + {te = p+1;{ output->first = 0x2a04; {p++; goto _out; } }} + break; + case 2101: +#line 2434 "char_ref.rl" + {te = p+1;{ output->first = 0x25b3; {p++; goto _out; } }} + break; + case 2102: +#line 2435 "char_ref.rl" + {te = p+1;{ output->first = 0x22c1; {p++; goto _out; } }} + break; + case 2103: +#line 2436 "char_ref.rl" + {te = p+1;{ output->first = 0x22c0; {p++; goto _out; } }} + break; + case 2104: +#line 2437 "char_ref.rl" + {te = p+1;{ output->first = 0xfd; {p++; goto _out; } }} + break; + case 2105: +#line 2439 "char_ref.rl" + {te = p+1;{ output->first = 0x044f; {p++; goto _out; } }} + break; + case 2106: +#line 2440 "char_ref.rl" + {te = p+1;{ output->first = 0x0177; {p++; goto _out; } }} + break; + case 2107: +#line 2441 "char_ref.rl" + {te = p+1;{ output->first = 0x044b; {p++; goto _out; } }} + break; + case 2108: +#line 2442 "char_ref.rl" + {te = p+1;{ output->first = 0xa5; {p++; goto _out; } }} + break; + case 2109: +#line 2444 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d536; {p++; goto _out; } }} + break; + case 2110: +#line 2445 "char_ref.rl" + {te = p+1;{ output->first = 0x0457; {p++; goto _out; } }} + break; + case 2111: +#line 2446 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d56a; {p++; goto _out; } }} + break; + case 2112: +#line 2447 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4ce; {p++; goto _out; } }} + break; + case 2113: +#line 2448 "char_ref.rl" + {te = p+1;{ output->first = 0x044e; {p++; goto _out; } }} + break; + case 2114: +#line 2449 "char_ref.rl" + {te = p+1;{ output->first = 0xff; {p++; goto _out; } }} + break; + case 2115: +#line 2451 "char_ref.rl" + {te = p+1;{ output->first = 0x017a; {p++; goto _out; } }} + break; + case 2116: +#line 2452 "char_ref.rl" + {te = p+1;{ output->first = 0x017e; {p++; goto _out; } }} + break; + case 2117: +#line 2453 "char_ref.rl" + {te = p+1;{ output->first = 0x0437; {p++; goto _out; } }} + break; + case 2118: +#line 2454 "char_ref.rl" + {te = p+1;{ output->first = 0x017c; {p++; goto _out; } }} + break; + case 2119: +#line 2455 "char_ref.rl" + {te = p+1;{ output->first = 0x2128; {p++; goto _out; } }} + break; + case 2120: +#line 2456 "char_ref.rl" + {te = p+1;{ output->first = 0x03b6; {p++; goto _out; } }} + break; + case 2121: +#line 2457 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d537; {p++; goto _out; } }} + break; + case 2122: +#line 2458 "char_ref.rl" + {te = p+1;{ output->first = 0x0436; {p++; goto _out; } }} + break; + case 2123: +#line 2459 "char_ref.rl" + {te = p+1;{ output->first = 0x21dd; {p++; goto _out; } }} + break; + case 2124: +#line 2460 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d56b; {p++; goto _out; } }} + break; + case 2125: +#line 2461 "char_ref.rl" + {te = p+1;{ output->first = 0x0001d4cf; {p++; goto _out; } }} + break; + case 2126: +#line 2462 "char_ref.rl" + {te = p+1;{ output->first = 0x200d; {p++; goto _out; } }} + break; + case 2127: +#line 2463 "char_ref.rl" + {te = p+1;{ output->first = 0x200c; {p++; goto _out; } }} + break; + case 2128: +#line 234 "char_ref.rl" + {te = p;p--;{ output->first = 0xc6; {p++; goto _out; } }} + break; + case 2129: +#line 236 "char_ref.rl" + {te = p;p--;{ output->first = 0x26; {p++; goto _out; } }} + break; + case 2130: +#line 238 "char_ref.rl" + {te = p;p--;{ output->first = 0xc1; {p++; goto _out; } }} + break; + case 2131: +#line 241 "char_ref.rl" + {te = p;p--;{ output->first = 0xc2; {p++; goto _out; } }} + break; + case 2132: +#line 245 "char_ref.rl" + {te = p;p--;{ output->first = 0xc0; {p++; goto _out; } }} + break; + case 2133: +#line 253 "char_ref.rl" + {te = p;p--;{ output->first = 0xc5; {p++; goto _out; } }} + break; + case 2134: +#line 257 "char_ref.rl" + {te = p;p--;{ output->first = 0xc3; {p++; goto _out; } }} + break; + case 2135: +#line 259 "char_ref.rl" + {te = p;p--;{ output->first = 0xc4; {p++; goto _out; } }} + break; + case 2136: +#line 274 "char_ref.rl" + {te = p;p--;{ output->first = 0xa9; {p++; goto _out; } }} + break; + case 2137: +#line 281 "char_ref.rl" + {te = p;p--;{ output->first = 0xc7; {p++; goto _out; } }} + break; + case 2138: +#line 364 "char_ref.rl" + {te = p;p--;{ output->first = 0xd0; {p++; goto _out; } }} + break; + case 2139: +#line 366 "char_ref.rl" + {te = p;p--;{ output->first = 0xc9; {p++; goto _out; } }} + break; + case 2140: +#line 369 "char_ref.rl" + {te = p;p--;{ output->first = 0xca; {p++; goto _out; } }} + break; + case 2141: +#line 374 "char_ref.rl" + {te = p;p--;{ output->first = 0xc8; {p++; goto _out; } }} + break; + case 2142: +#line 389 "char_ref.rl" + {te = p;p--;{ output->first = 0xcb; {p++; goto _out; } }} + break; + case 2143: +#line 402 "char_ref.rl" + {te = p;p--;{ output->first = 0x3e; {p++; goto _out; } }} + break; + case 2144: +#line 438 "char_ref.rl" + {te = p;p--;{ output->first = 0xcd; {p++; goto _out; } }} + break; + case 2145: +#line 440 "char_ref.rl" + {te = p;p--;{ output->first = 0xce; {p++; goto _out; } }} + break; + case 2146: +#line 445 "char_ref.rl" + {te = p;p--;{ output->first = 0xcc; {p++; goto _out; } }} + break; + case 2147: +#line 462 "char_ref.rl" + {te = p;p--;{ output->first = 0xcf; {p++; goto _out; } }} + break; + case 2148: +#line 480 "char_ref.rl" + {te = p;p--;{ output->first = 0x3c; {p++; goto _out; } }} + break; + case 2149: +#line 617 "char_ref.rl" + {te = p;p--;{ output->first = 0xd1; {p++; goto _out; } }} + break; + case 2150: +#line 621 "char_ref.rl" + {te = p;p--;{ output->first = 0xd3; {p++; goto _out; } }} + break; + case 2151: +#line 623 "char_ref.rl" + {te = p;p--;{ output->first = 0xd4; {p++; goto _out; } }} + break; + case 2152: +#line 628 "char_ref.rl" + {te = p;p--;{ output->first = 0xd2; {p++; goto _out; } }} + break; + case 2153: +#line 638 "char_ref.rl" + {te = p;p--;{ output->first = 0xd8; {p++; goto _out; } }} + break; + case 2154: +#line 640 "char_ref.rl" + {te = p;p--;{ output->first = 0xd5; {p++; goto _out; } }} + break; + case 2155: +#line 643 "char_ref.rl" + {te = p;p--;{ output->first = 0xd6; {p++; goto _out; } }} + break; + case 2156: +#line 668 "char_ref.rl" + {te = p;p--;{ output->first = 0x22; {p++; goto _out; } }} + break; + case 2157: +#line 674 "char_ref.rl" + {te = p;p--;{ output->first = 0xae; {p++; goto _out; } }} + break; + case 2158: +#line 758 "char_ref.rl" + {te = p;p--;{ output->first = 0xde; {p++; goto _out; } }} + break; + case 2159: +#line 781 "char_ref.rl" + {te = p;p--;{ output->first = 0xda; {p++; goto _out; } }} + break; + case 2160: +#line 787 "char_ref.rl" + {te = p;p--;{ output->first = 0xdb; {p++; goto _out; } }} + break; + case 2161: +#line 792 "char_ref.rl" + {te = p;p--;{ output->first = 0xd9; {p++; goto _out; } }} + break; + case 2162: +#line 819 "char_ref.rl" + {te = p;p--;{ output->first = 0xdc; {p++; goto _out; } }} + break; + case 2163: +#line 850 "char_ref.rl" + {te = p;p--;{ output->first = 0xdd; {p++; goto _out; } }} + break; + case 2164: +#line 868 "char_ref.rl" + {te = p;p--;{ output->first = 0xe1; {p++; goto _out; } }} + break; + case 2165: +#line 874 "char_ref.rl" + {te = p;p--;{ output->first = 0xe2; {p++; goto _out; } }} + break; + case 2166: +#line 876 "char_ref.rl" + {te = p;p--;{ output->first = 0xb4; {p++; goto _out; } }} + break; + case 2167: +#line 879 "char_ref.rl" + {te = p;p--;{ output->first = 0xe6; {p++; goto _out; } }} + break; + case 2168: +#line 883 "char_ref.rl" + {te = p;p--;{ output->first = 0xe0; {p++; goto _out; } }} + break; + case 2169: +#line 890 "char_ref.rl" + {te = p;p--;{ output->first = 0x26; {p++; goto _out; } }} + break; + case 2170: +#line 925 "char_ref.rl" + {te = p;p--;{ output->first = 0xe5; {p++; goto _out; } }} + break; + case 2171: +#line 931 "char_ref.rl" + {te = p;p--;{ output->first = 0xe3; {p++; goto _out; } }} + break; + case 2172: +#line 933 "char_ref.rl" + {te = p;p--;{ output->first = 0xe4; {p++; goto _out; } }} + break; + case 2173: +#line 1038 "char_ref.rl" + {te = p;p--;{ output->first = 0xa6; {p++; goto _out; } }} + break; + case 2174: +#line 1065 "char_ref.rl" + {te = p;p--;{ output->first = 0xe7; {p++; goto _out; } }} + break; + case 2175: +#line 1071 "char_ref.rl" + {te = p;p--;{ output->first = 0xb8; {p++; goto _out; } }} + break; + case 2176: +#line 1074 "char_ref.rl" + {te = p;p--;{ output->first = 0xa2; {p++; goto _out; } }} + break; + case 2177: +#line 1113 "char_ref.rl" + {te = p;p--;{ output->first = 0xa9; {p++; goto _out; } }} + break; + case 2178: +#line 1143 "char_ref.rl" + {te = p;p--;{ output->first = 0xa4; {p++; goto _out; } }} + break; + case 2179: +#line 1167 "char_ref.rl" + {te = p;p--;{ output->first = 0xb0; {p++; goto _out; } }} + break; + case 2180: +#line 1183 "char_ref.rl" + {te = p;p--;{ output->first = 0xf7; {p++; goto _out; } }} + break; + case 2181: +#line 1220 "char_ref.rl" + {te = p;p--;{ output->first = 0xe9; {p++; goto _out; } }} + break; + case 2182: +#line 1225 "char_ref.rl" + {te = p;p--;{ output->first = 0xea; {p++; goto _out; } }} + break; + case 2183: +#line 1234 "char_ref.rl" + {te = p;p--;{ output->first = 0xe8; {p++; goto _out; } }} + break; + case 2184: +#line 1276 "char_ref.rl" + {te = p;p--;{ output->first = 0xf0; {p++; goto _out; } }} + break; + case 2185: +#line 1278 "char_ref.rl" + {te = p;p--;{ output->first = 0xeb; {p++; goto _out; } }} + break; + case 2186: +#line 1303 "char_ref.rl" + {te = p;p--;{ output->first = 0xbd; {p++; goto _out; } }} + break; + case 2187: +#line 1306 "char_ref.rl" + {te = p;p--;{ output->first = 0xbc; {p++; goto _out; } }} + break; + case 2188: +#line 1313 "char_ref.rl" + {te = p;p--;{ output->first = 0xbe; {p++; goto _out; } }} + break; + case 2189: +#line 1368 "char_ref.rl" + {te = p;p--;{ output->first = 0x3e; {p++; goto _out; } }} + break; + case 2190: +#line 1412 "char_ref.rl" + {te = p;p--;{ output->first = 0xed; {p++; goto _out; } }} + break; + case 2191: +#line 1415 "char_ref.rl" + {te = p;p--;{ output->first = 0xee; {p++; goto _out; } }} + break; + case 2192: +#line 1419 "char_ref.rl" + {te = p;p--;{ output->first = 0xa1; {p++; goto _out; } }} + break; + case 2193: +#line 1423 "char_ref.rl" + {te = p;p--;{ output->first = 0xec; {p++; goto _out; } }} + break; + case 2194: +#line 1454 "char_ref.rl" + {te = p;p--;{ output->first = 0xbf; {p++; goto _out; } }} + break; + case 2195: +#line 1466 "char_ref.rl" + {te = p;p--;{ output->first = 0xef; {p++; goto _out; } }} + break; + case 2196: +#line 1501 "char_ref.rl" + {te = p;p--;{ output->first = 0xab; {p++; goto _out; } }} + break; + case 2197: +#line 1623 "char_ref.rl" + {te = p;p--;{ output->first = 0x3c; {p++; goto _out; } }} + break; + case 2198: +#line 1641 "char_ref.rl" + {te = p;p--;{ output->first = 0xaf; {p++; goto _out; } }} + break; + case 2199: +#line 1658 "char_ref.rl" + {te = p;p--;{ output->first = 0xb5; {p++; goto _out; } }} + break; + case 2200: +#line 1663 "char_ref.rl" + {te = p;p--;{ output->first = 0xb7; {p++; goto _out; } }} + break; + case 2201: +#line 1702 "char_ref.rl" + {te = p;p--;{ output->first = 0xa0; {p++; goto _out; } }} + break; + case 2202: +#line 1771 "char_ref.rl" + {te = p;p--;{ output->first = 0xac; {p++; goto _out; } }} + break; + case 2203: +#line 1818 "char_ref.rl" + {te = p;p--;{ output->first = 0xf1; {p++; goto _out; } }} + break; + case 2204: +#line 1849 "char_ref.rl" + {te = p;p--;{ output->first = 0xf3; {p++; goto _out; } }} + break; + case 2205: +#line 1853 "char_ref.rl" + {te = p;p--;{ output->first = 0xf4; {p++; goto _out; } }} + break; + case 2206: +#line 1865 "char_ref.rl" + {te = p;p--;{ output->first = 0xf2; {p++; goto _out; } }} + break; + case 2207: +#line 1890 "char_ref.rl" + {te = p;p--;{ output->first = 0xaa; {p++; goto _out; } }} + break; + case 2208: +#line 1892 "char_ref.rl" + {te = p;p--;{ output->first = 0xba; {p++; goto _out; } }} + break; + case 2209: +#line 1899 "char_ref.rl" + {te = p;p--;{ output->first = 0xf8; {p++; goto _out; } }} + break; + case 2210: +#line 1902 "char_ref.rl" + {te = p;p--;{ output->first = 0xf5; {p++; goto _out; } }} + break; + case 2211: +#line 1906 "char_ref.rl" + {te = p;p--;{ output->first = 0xf6; {p++; goto _out; } }} + break; + case 2212: +#line 1910 "char_ref.rl" + {te = p;p--;{ output->first = 0xb6; {p++; goto _out; } }} + break; + case 2213: +#line 1940 "char_ref.rl" + {te = p;p--;{ output->first = 0xb1; {p++; goto _out; } }} + break; + case 2214: +#line 1947 "char_ref.rl" + {te = p;p--;{ output->first = 0xa3; {p++; goto _out; } }} + break; + case 2215: +#line 1987 "char_ref.rl" + {te = p;p--;{ output->first = 0x22; {p++; goto _out; } }} + break; + case 2216: +#line 2002 "char_ref.rl" + {te = p;p--;{ output->first = 0xbb; {p++; goto _out; } }} + break; + case 2217: +#line 2041 "char_ref.rl" + {te = p;p--;{ output->first = 0xae; {p++; goto _out; } }} + break; + case 2218: +#line 2116 "char_ref.rl" + {te = p;p--;{ output->first = 0xa7; {p++; goto _out; } }} + break; + case 2219: +#line 2130 "char_ref.rl" + {te = p;p--;{ output->first = 0xad; {p++; goto _out; } }} + break; + case 2220: +#line 2217 "char_ref.rl" + {te = p;p--;{ output->first = 0xb9; {p++; goto _out; } }} + break; + case 2221: +#line 2219 "char_ref.rl" + {te = p;p--;{ output->first = 0xb2; {p++; goto _out; } }} + break; + case 2222: +#line 2221 "char_ref.rl" + {te = p;p--;{ output->first = 0xb3; {p++; goto _out; } }} + break; + case 2223: +#line 2249 "char_ref.rl" + {te = p;p--;{ output->first = 0xdf; {p++; goto _out; } }} + break; + case 2224: +#line 2270 "char_ref.rl" + {te = p;p--;{ output->first = 0xfe; {p++; goto _out; } }} + break; + case 2225: +#line 2273 "char_ref.rl" + {te = p;p--;{ output->first = 0xd7; {p++; goto _out; } }} + break; + case 2226: +#line 2311 "char_ref.rl" + {te = p;p--;{ output->first = 0xfa; {p++; goto _out; } }} + break; + case 2227: +#line 2316 "char_ref.rl" + {te = p;p--;{ output->first = 0xfb; {p++; goto _out; } }} + break; + case 2228: +#line 2324 "char_ref.rl" + {te = p;p--;{ output->first = 0xf9; {p++; goto _out; } }} + break; + case 2229: +#line 2334 "char_ref.rl" + {te = p;p--;{ output->first = 0xa8; {p++; goto _out; } }} + break; + case 2230: +#line 2358 "char_ref.rl" + {te = p;p--;{ output->first = 0xfc; {p++; goto _out; } }} + break; + case 2231: +#line 2438 "char_ref.rl" + {te = p;p--;{ output->first = 0xfd; {p++; goto _out; } }} + break; + case 2232: +#line 2443 "char_ref.rl" + {te = p;p--;{ output->first = 0xa5; {p++; goto _out; } }} + break; + case 2233: +#line 2450 "char_ref.rl" + {te = p;p--;{ output->first = 0xff; {p++; goto _out; } }} + break; + case 2234: +#line 1074 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0xa2; {p++; goto _out; } }} + break; + case 2235: +#line 1113 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0xa9; {p++; goto _out; } }} + break; + case 2236: +#line 1183 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0xf7; {p++; goto _out; } }} + break; + case 2237: +#line 1368 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0x3e; {p++; goto _out; } }} + break; + case 2238: +#line 1623 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0x3c; {p++; goto _out; } }} + break; + case 2239: +#line 1771 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0xac; {p++; goto _out; } }} + break; + case 2240: +#line 1910 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0xb6; {p++; goto _out; } }} + break; + case 2241: +#line 2273 "char_ref.rl" + {{p = ((te))-1;}{ output->first = 0xd7; {p++; goto _out; } }} + break; +#line 23006 "char_ref.c" + } + } + +_again: + _acts = _char_ref_actions + _char_ref_to_state_actions[cs]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) { + switch ( *_acts++ ) { + case 0: +#line 1 "NONE" + {ts = 0;} + break; +#line 23019 "char_ref.c" + } + } + + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _char_ref_eof_trans[cs] > 0 ) { + _trans = _char_ref_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + _out: {} + } + +#line 2491 "char_ref.rl" + // clang-format on + + if (cs >= 7623) { + assert(output->first != kGumboNoChar); + char last_char = *(te - 1); + ptrdiff_t len = te - start; + if (last_char == ';') { + bool matched = utf8iterator_maybe_consume_match(input, start, len, true); + assert(matched); + return true; + } else if (is_in_attribute && (*te == '=' || isalnum(*te))) { + output->first = kGumboNoChar; + output->second = kGumboNoChar; + utf8iterator_reset(input); + return true; + } else { + GumboStringPiece bad_ref; + bad_ref.length = te - start; + bad_ref.data = start; + add_named_reference_error( + parser, input, GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON, bad_ref); + bool matched = utf8iterator_maybe_consume_match(input, start, len, true); + assert(matched); + return false; + } + } else { + output->first = kGumboNoChar; + output->second = kGumboNoChar; + bool status = maybe_add_invalid_named_reference(parser, input); + utf8iterator_reset(input); + return status; + } +} + +bool consume_char_ref(struct GumboInternalParser* parser, + struct GumboInternalUtf8Iterator* input, int additional_allowed_char, + bool is_in_attribute, OneOrTwoCodepoints* output) { + utf8iterator_mark(input); + utf8iterator_next(input); + int c = utf8iterator_current(input); + output->first = kGumboNoChar; + output->second = kGumboNoChar; + if (c == additional_allowed_char) { + utf8iterator_reset(input); + output->first = kGumboNoChar; + return true; + } + switch (utf8iterator_current(input)) { + case '\t': + case '\n': + case '\f': + case ' ': + case '<': + case '&': + case -1: + utf8iterator_reset(input); + return true; + case '#': + return consume_numeric_ref(parser, input, &output->first); + default: + return consume_named_ref(parser, input, is_in_attribute, output); + } +} diff --git a/gb.form.htmlview/src/gumbo/char_ref.h b/gb.form.htmlview/src/gumbo/char_ref.h new file mode 100644 index 00000000..09d2598f --- /dev/null +++ b/gb.form.htmlview/src/gumbo/char_ref.h @@ -0,0 +1,60 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// Internal header for character reference handling; this should not be exposed +// transitively by any public API header. This is why the functions aren't +// namespaced. + +#ifndef GUMBO_CHAR_REF_H_ +#define GUMBO_CHAR_REF_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalParser; +struct GumboInternalUtf8Iterator; + +// Value that indicates no character was produced. +extern const int kGumboNoChar; + +// Certain named character references generate two codepoints, not one, and so +// the consume_char_ref subroutine needs to return this instead of an int. The +// first field will be kGumboNoChar if no character reference was found; the +// second field will be kGumboNoChar if that is the case or if the character +// reference returns only a single codepoint. +typedef struct { + int first; + int second; +} OneOrTwoCodepoints; + +// Implements the "consume a character reference" section of the spec. +// This reads in characters from the input as necessary, and fills in a +// OneOrTwoCodepoints struct containing the characters read. It may add parse +// errors to the GumboParser's errors vector, if the spec calls for it. Pass a +// space for the "additional allowed char" when the spec says "with no +// additional allowed char". Returns false on parse error, true otherwise. +bool consume_char_ref(struct GumboInternalParser* parser, + struct GumboInternalUtf8Iterator* input, int additional_allowed_char, + bool is_in_attribute, OneOrTwoCodepoints* output); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_CHAR_REF_H_ diff --git a/gb.form.htmlview/src/gumbo/char_ref.rl b/gb.form.htmlview/src/gumbo/char_ref.rl new file mode 100644 index 00000000..7c1c410d --- /dev/null +++ b/gb.form.htmlview/src/gumbo/char_ref.rl @@ -0,0 +1,2554 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// This is a Ragel state machine re-implementation of the original char_ref.c, +// rewritten to improve efficiency. To generate the .c file from it, +// +// $ ragel -F0 char_ref.rl +// +// The generated source is also checked into source control so that most people +// hacking on the parser do not need to install ragel. + +#include "char_ref.h" + +#include +#include +#include +#include +#include // Only for debug assertions at present. + +#include "error.h" +#include "string_piece.h" +#include "utf8.h" +#include "util.h" + +struct GumboInternalParser; + +const int kGumboNoChar = -1; + +// Table of replacement characters. The spec specifies that any occurrence of +// the first character should be replaced by the second character, and a parse +// error recorded. +typedef struct { + int from_char; + int to_char; +} CharReplacement; + +static const CharReplacement kCharReplacements[] = { + { 0x00, 0xfffd }, + { 0x0d, 0x000d }, + { 0x80, 0x20ac }, + { 0x81, 0x0081 }, + { 0x82, 0x201A }, + { 0x83, 0x0192 }, + { 0x84, 0x201E }, + { 0x85, 0x2026 }, + { 0x86, 0x2020 }, + { 0x87, 0x2021 }, + { 0x88, 0x02C6 }, + { 0x89, 0x2030 }, + { 0x8A, 0x0160 }, + { 0x8B, 0x2039 }, + { 0x8C, 0x0152 }, + { 0x8D, 0x008D }, + { 0x8E, 0x017D }, + { 0x8F, 0x008F }, + { 0x90, 0x0090 }, + { 0x91, 0x2018 }, + { 0x92, 0x2019 }, + { 0x93, 0x201C }, + { 0x94, 0x201D }, + { 0x95, 0x2022 }, + { 0x96, 0x2013 }, + { 0x97, 0x2014 }, + { 0x98, 0x02DC }, + { 0x99, 0x2122 }, + { 0x9A, 0x0161 }, + { 0x9B, 0x203A }, + { 0x9C, 0x0153 }, + { 0x9D, 0x009D }, + { 0x9E, 0x017E }, + { 0x9F, 0x0178 }, + // Terminator. + { -1, -1 } +}; + +static int parse_digit(int c, bool allow_hex) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (allow_hex && c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (allow_hex && c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + +static void add_no_digit_error( + struct GumboInternalParser* parser, Utf8Iterator* input) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + utf8iterator_fill_error_at_mark(input, error); + error->type = GUMBO_ERR_NUMERIC_CHAR_REF_NO_DIGITS; +} + +static void add_codepoint_error( + struct GumboInternalParser* parser, Utf8Iterator* input, + GumboErrorType type, int codepoint) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + utf8iterator_fill_error_at_mark(input, error); + error->type = type; + error->v.codepoint = codepoint; +} + +static void add_named_reference_error( + struct GumboInternalParser* parser, Utf8Iterator* input, + GumboErrorType type, GumboStringPiece text) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + utf8iterator_fill_error_at_mark(input, error); + error->type = type; + error->v.text = text; +} + +static int maybe_replace_codepoint(int codepoint) { + for (int i = 0; kCharReplacements[i].from_char != -1; ++i) { + if (kCharReplacements[i].from_char == codepoint) { + return kCharReplacements[i].to_char; + } + } + return -1; +} + +static bool consume_numeric_ref( + struct GumboInternalParser* parser, Utf8Iterator* input, int* output) { + utf8iterator_next(input); + bool is_hex = false; + int c = utf8iterator_current(input); + if (c == 'x' || c == 'X') { + is_hex = true; + utf8iterator_next(input); + c = utf8iterator_current(input); + } + + int digit = parse_digit(c, is_hex); + if (digit == -1) { + // First digit was invalid; add a parse error and return. + add_no_digit_error(parser, input); + utf8iterator_reset(input); + *output = kGumboNoChar; + return false; + } + + int codepoint = 0; + bool status = true; + do { + codepoint = (codepoint * (is_hex ? 16 : 10)) + digit; + utf8iterator_next(input); + digit = parse_digit(utf8iterator_current(input), is_hex); + } while (digit != -1); + + if (utf8iterator_current(input) != ';') { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON, codepoint); + status = false; + } else { + utf8iterator_next(input); + } + + int replacement = maybe_replace_codepoint(codepoint); + if (replacement != -1) { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); + *output = replacement; + return false; + } + + if ((codepoint >= 0xd800 && codepoint <= 0xdfff) || codepoint > 0x10ffff) { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); + *output = 0xfffd; + return false; + } + + if (utf8_is_invalid_code_point(codepoint) || codepoint == 0xb) { + add_codepoint_error( + parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); + status = false; + // But return it anyway, per spec. + } + *output = codepoint; + return status; +} + +static bool maybe_add_invalid_named_reference( + struct GumboInternalParser* parser, Utf8Iterator* input) { + // The iterator will always be reset in this code path, so we don't need to + // worry about consuming characters. + const char* start = utf8iterator_get_char_pointer(input); + int c = utf8iterator_current(input); + while ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')) { + utf8iterator_next(input); + c = utf8iterator_current(input); + } + if (c == ';') { + GumboStringPiece bad_ref; + bad_ref.data = start; + bad_ref.length = utf8iterator_get_char_pointer(input) - start; + add_named_reference_error( + parser, input, GUMBO_ERR_NAMED_CHAR_REF_INVALID, bad_ref); + return false; + } + return true; +} + +%%{ +machine char_ref; + +valid_named_ref := |* + 'AElig;' => { output->first = 0xc6; fbreak; }; + 'AElig' => { output->first = 0xc6; fbreak; }; + 'AMP;' => { output->first = 0x26; fbreak; }; + 'AMP' => { output->first = 0x26; fbreak; }; + 'Aacute;' => { output->first = 0xc1; fbreak; }; + 'Aacute' => { output->first = 0xc1; fbreak; }; + 'Abreve;' => { output->first = 0x0102; fbreak; }; + 'Acirc;' => { output->first = 0xc2; fbreak; }; + 'Acirc' => { output->first = 0xc2; fbreak; }; + 'Acy;' => { output->first = 0x0410; fbreak; }; + 'Afr;' => { output->first = 0x0001d504; fbreak; }; + 'Agrave;' => { output->first = 0xc0; fbreak; }; + 'Agrave' => { output->first = 0xc0; fbreak; }; + 'Alpha;' => { output->first = 0x0391; fbreak; }; + 'Amacr;' => { output->first = 0x0100; fbreak; }; + 'And;' => { output->first = 0x2a53; fbreak; }; + 'Aogon;' => { output->first = 0x0104; fbreak; }; + 'Aopf;' => { output->first = 0x0001d538; fbreak; }; + 'ApplyFunction;' => { output->first = 0x2061; fbreak; }; + 'Aring;' => { output->first = 0xc5; fbreak; }; + 'Aring' => { output->first = 0xc5; fbreak; }; + 'Ascr;' => { output->first = 0x0001d49c; fbreak; }; + 'Assign;' => { output->first = 0x2254; fbreak; }; + 'Atilde;' => { output->first = 0xc3; fbreak; }; + 'Atilde' => { output->first = 0xc3; fbreak; }; + 'Auml;' => { output->first = 0xc4; fbreak; }; + 'Auml' => { output->first = 0xc4; fbreak; }; + 'Backslash;' => { output->first = 0x2216; fbreak; }; + 'Barv;' => { output->first = 0x2ae7; fbreak; }; + 'Barwed;' => { output->first = 0x2306; fbreak; }; + 'Bcy;' => { output->first = 0x0411; fbreak; }; + 'Because;' => { output->first = 0x2235; fbreak; }; + 'Bernoullis;' => { output->first = 0x212c; fbreak; }; + 'Beta;' => { output->first = 0x0392; fbreak; }; + 'Bfr;' => { output->first = 0x0001d505; fbreak; }; + 'Bopf;' => { output->first = 0x0001d539; fbreak; }; + 'Breve;' => { output->first = 0x02d8; fbreak; }; + 'Bscr;' => { output->first = 0x212c; fbreak; }; + 'Bumpeq;' => { output->first = 0x224e; fbreak; }; + 'CHcy;' => { output->first = 0x0427; fbreak; }; + 'COPY;' => { output->first = 0xa9; fbreak; }; + 'COPY' => { output->first = 0xa9; fbreak; }; + 'Cacute;' => { output->first = 0x0106; fbreak; }; + 'Cap;' => { output->first = 0x22d2; fbreak; }; + 'CapitalDifferentialD;' => { output->first = 0x2145; fbreak; }; + 'Cayleys;' => { output->first = 0x212d; fbreak; }; + 'Ccaron;' => { output->first = 0x010c; fbreak; }; + 'Ccedil;' => { output->first = 0xc7; fbreak; }; + 'Ccedil' => { output->first = 0xc7; fbreak; }; + 'Ccirc;' => { output->first = 0x0108; fbreak; }; + 'Cconint;' => { output->first = 0x2230; fbreak; }; + 'Cdot;' => { output->first = 0x010a; fbreak; }; + 'Cedilla;' => { output->first = 0xb8; fbreak; }; + 'CenterDot;' => { output->first = 0xb7; fbreak; }; + 'Cfr;' => { output->first = 0x212d; fbreak; }; + 'Chi;' => { output->first = 0x03a7; fbreak; }; + 'CircleDot;' => { output->first = 0x2299; fbreak; }; + 'CircleMinus;' => { output->first = 0x2296; fbreak; }; + 'CirclePlus;' => { output->first = 0x2295; fbreak; }; + 'CircleTimes;' => { output->first = 0x2297; fbreak; }; + 'ClockwiseContourIntegral;' => { output->first = 0x2232; fbreak; }; + 'CloseCurlyDoubleQuote;' => { output->first = 0x201d; fbreak; }; + 'CloseCurlyQuote;' => { output->first = 0x2019; fbreak; }; + 'Colon;' => { output->first = 0x2237; fbreak; }; + 'Colone;' => { output->first = 0x2a74; fbreak; }; + 'Congruent;' => { output->first = 0x2261; fbreak; }; + 'Conint;' => { output->first = 0x222f; fbreak; }; + 'ContourIntegral;' => { output->first = 0x222e; fbreak; }; + 'Copf;' => { output->first = 0x2102; fbreak; }; + 'Coproduct;' => { output->first = 0x2210; fbreak; }; + 'CounterClockwiseContourIntegral;' => { output->first = 0x2233; fbreak; }; + 'Cross;' => { output->first = 0x2a2f; fbreak; }; + 'Cscr;' => { output->first = 0x0001d49e; fbreak; }; + 'Cup;' => { output->first = 0x22d3; fbreak; }; + 'CupCap;' => { output->first = 0x224d; fbreak; }; + 'DD;' => { output->first = 0x2145; fbreak; }; + 'DDotrahd;' => { output->first = 0x2911; fbreak; }; + 'DJcy;' => { output->first = 0x0402; fbreak; }; + 'DScy;' => { output->first = 0x0405; fbreak; }; + 'DZcy;' => { output->first = 0x040f; fbreak; }; + 'Dagger;' => { output->first = 0x2021; fbreak; }; + 'Darr;' => { output->first = 0x21a1; fbreak; }; + 'Dashv;' => { output->first = 0x2ae4; fbreak; }; + 'Dcaron;' => { output->first = 0x010e; fbreak; }; + 'Dcy;' => { output->first = 0x0414; fbreak; }; + 'Del;' => { output->first = 0x2207; fbreak; }; + 'Delta;' => { output->first = 0x0394; fbreak; }; + 'Dfr;' => { output->first = 0x0001d507; fbreak; }; + 'DiacriticalAcute;' => { output->first = 0xb4; fbreak; }; + 'DiacriticalDot;' => { output->first = 0x02d9; fbreak; }; + 'DiacriticalDoubleAcute;' => { output->first = 0x02dd; fbreak; }; + 'DiacriticalGrave;' => { output->first = 0x60; fbreak; }; + 'DiacriticalTilde;' => { output->first = 0x02dc; fbreak; }; + 'Diamond;' => { output->first = 0x22c4; fbreak; }; + 'DifferentialD;' => { output->first = 0x2146; fbreak; }; + 'Dopf;' => { output->first = 0x0001d53b; fbreak; }; + 'Dot;' => { output->first = 0xa8; fbreak; }; + 'DotDot;' => { output->first = 0x20dc; fbreak; }; + 'DotEqual;' => { output->first = 0x2250; fbreak; }; + 'DoubleContourIntegral;' => { output->first = 0x222f; fbreak; }; + 'DoubleDot;' => { output->first = 0xa8; fbreak; }; + 'DoubleDownArrow;' => { output->first = 0x21d3; fbreak; }; + 'DoubleLeftArrow;' => { output->first = 0x21d0; fbreak; }; + 'DoubleLeftRightArrow;' => { output->first = 0x21d4; fbreak; }; + 'DoubleLeftTee;' => { output->first = 0x2ae4; fbreak; }; + 'DoubleLongLeftArrow;' => { output->first = 0x27f8; fbreak; }; + 'DoubleLongLeftRightArrow;' => { output->first = 0x27fa; fbreak; }; + 'DoubleLongRightArrow;' => { output->first = 0x27f9; fbreak; }; + 'DoubleRightArrow;' => { output->first = 0x21d2; fbreak; }; + 'DoubleRightTee;' => { output->first = 0x22a8; fbreak; }; + 'DoubleUpArrow;' => { output->first = 0x21d1; fbreak; }; + 'DoubleUpDownArrow;' => { output->first = 0x21d5; fbreak; }; + 'DoubleVerticalBar;' => { output->first = 0x2225; fbreak; }; + 'DownArrow;' => { output->first = 0x2193; fbreak; }; + 'DownArrowBar;' => { output->first = 0x2913; fbreak; }; + 'DownArrowUpArrow;' => { output->first = 0x21f5; fbreak; }; + 'DownBreve;' => { output->first = 0x0311; fbreak; }; + 'DownLeftRightVector;' => { output->first = 0x2950; fbreak; }; + 'DownLeftTeeVector;' => { output->first = 0x295e; fbreak; }; + 'DownLeftVector;' => { output->first = 0x21bd; fbreak; }; + 'DownLeftVectorBar;' => { output->first = 0x2956; fbreak; }; + 'DownRightTeeVector;' => { output->first = 0x295f; fbreak; }; + 'DownRightVector;' => { output->first = 0x21c1; fbreak; }; + 'DownRightVectorBar;' => { output->first = 0x2957; fbreak; }; + 'DownTee;' => { output->first = 0x22a4; fbreak; }; + 'DownTeeArrow;' => { output->first = 0x21a7; fbreak; }; + 'Downarrow;' => { output->first = 0x21d3; fbreak; }; + 'Dscr;' => { output->first = 0x0001d49f; fbreak; }; + 'Dstrok;' => { output->first = 0x0110; fbreak; }; + 'ENG;' => { output->first = 0x014a; fbreak; }; + 'ETH;' => { output->first = 0xd0; fbreak; }; + 'ETH' => { output->first = 0xd0; fbreak; }; + 'Eacute;' => { output->first = 0xc9; fbreak; }; + 'Eacute' => { output->first = 0xc9; fbreak; }; + 'Ecaron;' => { output->first = 0x011a; fbreak; }; + 'Ecirc;' => { output->first = 0xca; fbreak; }; + 'Ecirc' => { output->first = 0xca; fbreak; }; + 'Ecy;' => { output->first = 0x042d; fbreak; }; + 'Edot;' => { output->first = 0x0116; fbreak; }; + 'Efr;' => { output->first = 0x0001d508; fbreak; }; + 'Egrave;' => { output->first = 0xc8; fbreak; }; + 'Egrave' => { output->first = 0xc8; fbreak; }; + 'Element;' => { output->first = 0x2208; fbreak; }; + 'Emacr;' => { output->first = 0x0112; fbreak; }; + 'EmptySmallSquare;' => { output->first = 0x25fb; fbreak; }; + 'EmptyVerySmallSquare;' => { output->first = 0x25ab; fbreak; }; + 'Eogon;' => { output->first = 0x0118; fbreak; }; + 'Eopf;' => { output->first = 0x0001d53c; fbreak; }; + 'Epsilon;' => { output->first = 0x0395; fbreak; }; + 'Equal;' => { output->first = 0x2a75; fbreak; }; + 'EqualTilde;' => { output->first = 0x2242; fbreak; }; + 'Equilibrium;' => { output->first = 0x21cc; fbreak; }; + 'Escr;' => { output->first = 0x2130; fbreak; }; + 'Esim;' => { output->first = 0x2a73; fbreak; }; + 'Eta;' => { output->first = 0x0397; fbreak; }; + 'Euml;' => { output->first = 0xcb; fbreak; }; + 'Euml' => { output->first = 0xcb; fbreak; }; + 'Exists;' => { output->first = 0x2203; fbreak; }; + 'ExponentialE;' => { output->first = 0x2147; fbreak; }; + 'Fcy;' => { output->first = 0x0424; fbreak; }; + 'Ffr;' => { output->first = 0x0001d509; fbreak; }; + 'FilledSmallSquare;' => { output->first = 0x25fc; fbreak; }; + 'FilledVerySmallSquare;' => { output->first = 0x25aa; fbreak; }; + 'Fopf;' => { output->first = 0x0001d53d; fbreak; }; + 'ForAll;' => { output->first = 0x2200; fbreak; }; + 'Fouriertrf;' => { output->first = 0x2131; fbreak; }; + 'Fscr;' => { output->first = 0x2131; fbreak; }; + 'GJcy;' => { output->first = 0x0403; fbreak; }; + 'GT;' => { output->first = 0x3e; fbreak; }; + 'GT' => { output->first = 0x3e; fbreak; }; + 'Gamma;' => { output->first = 0x0393; fbreak; }; + 'Gammad;' => { output->first = 0x03dc; fbreak; }; + 'Gbreve;' => { output->first = 0x011e; fbreak; }; + 'Gcedil;' => { output->first = 0x0122; fbreak; }; + 'Gcirc;' => { output->first = 0x011c; fbreak; }; + 'Gcy;' => { output->first = 0x0413; fbreak; }; + 'Gdot;' => { output->first = 0x0120; fbreak; }; + 'Gfr;' => { output->first = 0x0001d50a; fbreak; }; + 'Gg;' => { output->first = 0x22d9; fbreak; }; + 'Gopf;' => { output->first = 0x0001d53e; fbreak; }; + 'GreaterEqual;' => { output->first = 0x2265; fbreak; }; + 'GreaterEqualLess;' => { output->first = 0x22db; fbreak; }; + 'GreaterFullEqual;' => { output->first = 0x2267; fbreak; }; + 'GreaterGreater;' => { output->first = 0x2aa2; fbreak; }; + 'GreaterLess;' => { output->first = 0x2277; fbreak; }; + 'GreaterSlantEqual;' => { output->first = 0x2a7e; fbreak; }; + 'GreaterTilde;' => { output->first = 0x2273; fbreak; }; + 'Gscr;' => { output->first = 0x0001d4a2; fbreak; }; + 'Gt;' => { output->first = 0x226b; fbreak; }; + 'HARDcy;' => { output->first = 0x042a; fbreak; }; + 'Hacek;' => { output->first = 0x02c7; fbreak; }; + 'Hat;' => { output->first = 0x5e; fbreak; }; + 'Hcirc;' => { output->first = 0x0124; fbreak; }; + 'Hfr;' => { output->first = 0x210c; fbreak; }; + 'HilbertSpace;' => { output->first = 0x210b; fbreak; }; + 'Hopf;' => { output->first = 0x210d; fbreak; }; + 'HorizontalLine;' => { output->first = 0x2500; fbreak; }; + 'Hscr;' => { output->first = 0x210b; fbreak; }; + 'Hstrok;' => { output->first = 0x0126; fbreak; }; + 'HumpDownHump;' => { output->first = 0x224e; fbreak; }; + 'HumpEqual;' => { output->first = 0x224f; fbreak; }; + 'IEcy;' => { output->first = 0x0415; fbreak; }; + 'IJlig;' => { output->first = 0x0132; fbreak; }; + 'IOcy;' => { output->first = 0x0401; fbreak; }; + 'Iacute;' => { output->first = 0xcd; fbreak; }; + 'Iacute' => { output->first = 0xcd; fbreak; }; + 'Icirc;' => { output->first = 0xce; fbreak; }; + 'Icirc' => { output->first = 0xce; fbreak; }; + 'Icy;' => { output->first = 0x0418; fbreak; }; + 'Idot;' => { output->first = 0x0130; fbreak; }; + 'Ifr;' => { output->first = 0x2111; fbreak; }; + 'Igrave;' => { output->first = 0xcc; fbreak; }; + 'Igrave' => { output->first = 0xcc; fbreak; }; + 'Im;' => { output->first = 0x2111; fbreak; }; + 'Imacr;' => { output->first = 0x012a; fbreak; }; + 'ImaginaryI;' => { output->first = 0x2148; fbreak; }; + 'Implies;' => { output->first = 0x21d2; fbreak; }; + 'Int;' => { output->first = 0x222c; fbreak; }; + 'Integral;' => { output->first = 0x222b; fbreak; }; + 'Intersection;' => { output->first = 0x22c2; fbreak; }; + 'InvisibleComma;' => { output->first = 0x2063; fbreak; }; + 'InvisibleTimes;' => { output->first = 0x2062; fbreak; }; + 'Iogon;' => { output->first = 0x012e; fbreak; }; + 'Iopf;' => { output->first = 0x0001d540; fbreak; }; + 'Iota;' => { output->first = 0x0399; fbreak; }; + 'Iscr;' => { output->first = 0x2110; fbreak; }; + 'Itilde;' => { output->first = 0x0128; fbreak; }; + 'Iukcy;' => { output->first = 0x0406; fbreak; }; + 'Iuml;' => { output->first = 0xcf; fbreak; }; + 'Iuml' => { output->first = 0xcf; fbreak; }; + 'Jcirc;' => { output->first = 0x0134; fbreak; }; + 'Jcy;' => { output->first = 0x0419; fbreak; }; + 'Jfr;' => { output->first = 0x0001d50d; fbreak; }; + 'Jopf;' => { output->first = 0x0001d541; fbreak; }; + 'Jscr;' => { output->first = 0x0001d4a5; fbreak; }; + 'Jsercy;' => { output->first = 0x0408; fbreak; }; + 'Jukcy;' => { output->first = 0x0404; fbreak; }; + 'KHcy;' => { output->first = 0x0425; fbreak; }; + 'KJcy;' => { output->first = 0x040c; fbreak; }; + 'Kappa;' => { output->first = 0x039a; fbreak; }; + 'Kcedil;' => { output->first = 0x0136; fbreak; }; + 'Kcy;' => { output->first = 0x041a; fbreak; }; + 'Kfr;' => { output->first = 0x0001d50e; fbreak; }; + 'Kopf;' => { output->first = 0x0001d542; fbreak; }; + 'Kscr;' => { output->first = 0x0001d4a6; fbreak; }; + 'LJcy;' => { output->first = 0x0409; fbreak; }; + 'LT;' => { output->first = 0x3c; fbreak; }; + 'LT' => { output->first = 0x3c; fbreak; }; + 'Lacute;' => { output->first = 0x0139; fbreak; }; + 'Lambda;' => { output->first = 0x039b; fbreak; }; + 'Lang;' => { output->first = 0x27ea; fbreak; }; + 'Laplacetrf;' => { output->first = 0x2112; fbreak; }; + 'Larr;' => { output->first = 0x219e; fbreak; }; + 'Lcaron;' => { output->first = 0x013d; fbreak; }; + 'Lcedil;' => { output->first = 0x013b; fbreak; }; + 'Lcy;' => { output->first = 0x041b; fbreak; }; + 'LeftAngleBracket;' => { output->first = 0x27e8; fbreak; }; + 'LeftArrow;' => { output->first = 0x2190; fbreak; }; + 'LeftArrowBar;' => { output->first = 0x21e4; fbreak; }; + 'LeftArrowRightArrow;' => { output->first = 0x21c6; fbreak; }; + 'LeftCeiling;' => { output->first = 0x2308; fbreak; }; + 'LeftDoubleBracket;' => { output->first = 0x27e6; fbreak; }; + 'LeftDownTeeVector;' => { output->first = 0x2961; fbreak; }; + 'LeftDownVector;' => { output->first = 0x21c3; fbreak; }; + 'LeftDownVectorBar;' => { output->first = 0x2959; fbreak; }; + 'LeftFloor;' => { output->first = 0x230a; fbreak; }; + 'LeftRightArrow;' => { output->first = 0x2194; fbreak; }; + 'LeftRightVector;' => { output->first = 0x294e; fbreak; }; + 'LeftTee;' => { output->first = 0x22a3; fbreak; }; + 'LeftTeeArrow;' => { output->first = 0x21a4; fbreak; }; + 'LeftTeeVector;' => { output->first = 0x295a; fbreak; }; + 'LeftTriangle;' => { output->first = 0x22b2; fbreak; }; + 'LeftTriangleBar;' => { output->first = 0x29cf; fbreak; }; + 'LeftTriangleEqual;' => { output->first = 0x22b4; fbreak; }; + 'LeftUpDownVector;' => { output->first = 0x2951; fbreak; }; + 'LeftUpTeeVector;' => { output->first = 0x2960; fbreak; }; + 'LeftUpVector;' => { output->first = 0x21bf; fbreak; }; + 'LeftUpVectorBar;' => { output->first = 0x2958; fbreak; }; + 'LeftVector;' => { output->first = 0x21bc; fbreak; }; + 'LeftVectorBar;' => { output->first = 0x2952; fbreak; }; + 'Leftarrow;' => { output->first = 0x21d0; fbreak; }; + 'Leftrightarrow;' => { output->first = 0x21d4; fbreak; }; + 'LessEqualGreater;' => { output->first = 0x22da; fbreak; }; + 'LessFullEqual;' => { output->first = 0x2266; fbreak; }; + 'LessGreater;' => { output->first = 0x2276; fbreak; }; + 'LessLess;' => { output->first = 0x2aa1; fbreak; }; + 'LessSlantEqual;' => { output->first = 0x2a7d; fbreak; }; + 'LessTilde;' => { output->first = 0x2272; fbreak; }; + 'Lfr;' => { output->first = 0x0001d50f; fbreak; }; + 'Ll;' => { output->first = 0x22d8; fbreak; }; + 'Lleftarrow;' => { output->first = 0x21da; fbreak; }; + 'Lmidot;' => { output->first = 0x013f; fbreak; }; + 'LongLeftArrow;' => { output->first = 0x27f5; fbreak; }; + 'LongLeftRightArrow;' => { output->first = 0x27f7; fbreak; }; + 'LongRightArrow;' => { output->first = 0x27f6; fbreak; }; + 'Longleftarrow;' => { output->first = 0x27f8; fbreak; }; + 'Longleftrightarrow;' => { output->first = 0x27fa; fbreak; }; + 'Longrightarrow;' => { output->first = 0x27f9; fbreak; }; + 'Lopf;' => { output->first = 0x0001d543; fbreak; }; + 'LowerLeftArrow;' => { output->first = 0x2199; fbreak; }; + 'LowerRightArrow;' => { output->first = 0x2198; fbreak; }; + 'Lscr;' => { output->first = 0x2112; fbreak; }; + 'Lsh;' => { output->first = 0x21b0; fbreak; }; + 'Lstrok;' => { output->first = 0x0141; fbreak; }; + 'Lt;' => { output->first = 0x226a; fbreak; }; + 'Map;' => { output->first = 0x2905; fbreak; }; + 'Mcy;' => { output->first = 0x041c; fbreak; }; + 'MediumSpace;' => { output->first = 0x205f; fbreak; }; + 'Mellintrf;' => { output->first = 0x2133; fbreak; }; + 'Mfr;' => { output->first = 0x0001d510; fbreak; }; + 'MinusPlus;' => { output->first = 0x2213; fbreak; }; + 'Mopf;' => { output->first = 0x0001d544; fbreak; }; + 'Mscr;' => { output->first = 0x2133; fbreak; }; + 'Mu;' => { output->first = 0x039c; fbreak; }; + 'NJcy;' => { output->first = 0x040a; fbreak; }; + 'Nacute;' => { output->first = 0x0143; fbreak; }; + 'Ncaron;' => { output->first = 0x0147; fbreak; }; + 'Ncedil;' => { output->first = 0x0145; fbreak; }; + 'Ncy;' => { output->first = 0x041d; fbreak; }; + 'NegativeMediumSpace;' => { output->first = 0x200b; fbreak; }; + 'NegativeThickSpace;' => { output->first = 0x200b; fbreak; }; + 'NegativeThinSpace;' => { output->first = 0x200b; fbreak; }; + 'NegativeVeryThinSpace;' => { output->first = 0x200b; fbreak; }; + 'NestedGreaterGreater;' => { output->first = 0x226b; fbreak; }; + 'NestedLessLess;' => { output->first = 0x226a; fbreak; }; + 'NewLine;' => { output->first = 0x0a; fbreak; }; + 'Nfr;' => { output->first = 0x0001d511; fbreak; }; + 'NoBreak;' => { output->first = 0x2060; fbreak; }; + 'NonBreakingSpace;' => { output->first = 0xa0; fbreak; }; + 'Nopf;' => { output->first = 0x2115; fbreak; }; + 'Not;' => { output->first = 0x2aec; fbreak; }; + 'NotCongruent;' => { output->first = 0x2262; fbreak; }; + 'NotCupCap;' => { output->first = 0x226d; fbreak; }; + 'NotDoubleVerticalBar;' => { output->first = 0x2226; fbreak; }; + 'NotElement;' => { output->first = 0x2209; fbreak; }; + 'NotEqual;' => { output->first = 0x2260; fbreak; }; + 'NotEqualTilde;' => { output->first = 0x2242; output->second = 0x0338; fbreak; }; + 'NotExists;' => { output->first = 0x2204; fbreak; }; + 'NotGreater;' => { output->first = 0x226f; fbreak; }; + 'NotGreaterEqual;' => { output->first = 0x2271; fbreak; }; + 'NotGreaterFullEqual;' => { output->first = 0x2267; output->second = 0x0338; fbreak; }; + 'NotGreaterGreater;' => { output->first = 0x226b; output->second = 0x0338; fbreak; }; + 'NotGreaterLess;' => { output->first = 0x2279; fbreak; }; + 'NotGreaterSlantEqual;' => { output->first = 0x2a7e; output->second = 0x0338; fbreak; }; + 'NotGreaterTilde;' => { output->first = 0x2275; fbreak; }; + 'NotHumpDownHump;' => { output->first = 0x224e; output->second = 0x0338; fbreak; }; + 'NotHumpEqual;' => { output->first = 0x224f; output->second = 0x0338; fbreak; }; + 'NotLeftTriangle;' => { output->first = 0x22ea; fbreak; }; + 'NotLeftTriangleBar;' => { output->first = 0x29cf; output->second = 0x0338; fbreak; }; + 'NotLeftTriangleEqual;' => { output->first = 0x22ec; fbreak; }; + 'NotLess;' => { output->first = 0x226e; fbreak; }; + 'NotLessEqual;' => { output->first = 0x2270; fbreak; }; + 'NotLessGreater;' => { output->first = 0x2278; fbreak; }; + 'NotLessLess;' => { output->first = 0x226a; output->second = 0x0338; fbreak; }; + 'NotLessSlantEqual;' => { output->first = 0x2a7d; output->second = 0x0338; fbreak; }; + 'NotLessTilde;' => { output->first = 0x2274; fbreak; }; + 'NotNestedGreaterGreater;' => { output->first = 0x2aa2; output->second = 0x0338; fbreak; }; + 'NotNestedLessLess;' => { output->first = 0x2aa1; output->second = 0x0338; fbreak; }; + 'NotPrecedes;' => { output->first = 0x2280; fbreak; }; + 'NotPrecedesEqual;' => { output->first = 0x2aaf; output->second = 0x0338; fbreak; }; + 'NotPrecedesSlantEqual;' => { output->first = 0x22e0; fbreak; }; + 'NotReverseElement;' => { output->first = 0x220c; fbreak; }; + 'NotRightTriangle;' => { output->first = 0x22eb; fbreak; }; + 'NotRightTriangleBar;' => { output->first = 0x29d0; output->second = 0x0338; fbreak; }; + 'NotRightTriangleEqual;' => { output->first = 0x22ed; fbreak; }; + 'NotSquareSubset;' => { output->first = 0x228f; output->second = 0x0338; fbreak; }; + 'NotSquareSubsetEqual;' => { output->first = 0x22e2; fbreak; }; + 'NotSquareSuperset;' => { output->first = 0x2290; output->second = 0x0338; fbreak; }; + 'NotSquareSupersetEqual;' => { output->first = 0x22e3; fbreak; }; + 'NotSubset;' => { output->first = 0x2282; output->second = 0x20d2; fbreak; }; + 'NotSubsetEqual;' => { output->first = 0x2288; fbreak; }; + 'NotSucceeds;' => { output->first = 0x2281; fbreak; }; + 'NotSucceedsEqual;' => { output->first = 0x2ab0; output->second = 0x0338; fbreak; }; + 'NotSucceedsSlantEqual;' => { output->first = 0x22e1; fbreak; }; + 'NotSucceedsTilde;' => { output->first = 0x227f; output->second = 0x0338; fbreak; }; + 'NotSuperset;' => { output->first = 0x2283; output->second = 0x20d2; fbreak; }; + 'NotSupersetEqual;' => { output->first = 0x2289; fbreak; }; + 'NotTilde;' => { output->first = 0x2241; fbreak; }; + 'NotTildeEqual;' => { output->first = 0x2244; fbreak; }; + 'NotTildeFullEqual;' => { output->first = 0x2247; fbreak; }; + 'NotTildeTilde;' => { output->first = 0x2249; fbreak; }; + 'NotVerticalBar;' => { output->first = 0x2224; fbreak; }; + 'Nscr;' => { output->first = 0x0001d4a9; fbreak; }; + 'Ntilde;' => { output->first = 0xd1; fbreak; }; + 'Ntilde' => { output->first = 0xd1; fbreak; }; + 'Nu;' => { output->first = 0x039d; fbreak; }; + 'OElig;' => { output->first = 0x0152; fbreak; }; + 'Oacute;' => { output->first = 0xd3; fbreak; }; + 'Oacute' => { output->first = 0xd3; fbreak; }; + 'Ocirc;' => { output->first = 0xd4; fbreak; }; + 'Ocirc' => { output->first = 0xd4; fbreak; }; + 'Ocy;' => { output->first = 0x041e; fbreak; }; + 'Odblac;' => { output->first = 0x0150; fbreak; }; + 'Ofr;' => { output->first = 0x0001d512; fbreak; }; + 'Ograve;' => { output->first = 0xd2; fbreak; }; + 'Ograve' => { output->first = 0xd2; fbreak; }; + 'Omacr;' => { output->first = 0x014c; fbreak; }; + 'Omega;' => { output->first = 0x03a9; fbreak; }; + 'Omicron;' => { output->first = 0x039f; fbreak; }; + 'Oopf;' => { output->first = 0x0001d546; fbreak; }; + 'OpenCurlyDoubleQuote;' => { output->first = 0x201c; fbreak; }; + 'OpenCurlyQuote;' => { output->first = 0x2018; fbreak; }; + 'Or;' => { output->first = 0x2a54; fbreak; }; + 'Oscr;' => { output->first = 0x0001d4aa; fbreak; }; + 'Oslash;' => { output->first = 0xd8; fbreak; }; + 'Oslash' => { output->first = 0xd8; fbreak; }; + 'Otilde;' => { output->first = 0xd5; fbreak; }; + 'Otilde' => { output->first = 0xd5; fbreak; }; + 'Otimes;' => { output->first = 0x2a37; fbreak; }; + 'Ouml;' => { output->first = 0xd6; fbreak; }; + 'Ouml' => { output->first = 0xd6; fbreak; }; + 'OverBar;' => { output->first = 0x203e; fbreak; }; + 'OverBrace;' => { output->first = 0x23de; fbreak; }; + 'OverBracket;' => { output->first = 0x23b4; fbreak; }; + 'OverParenthesis;' => { output->first = 0x23dc; fbreak; }; + 'PartialD;' => { output->first = 0x2202; fbreak; }; + 'Pcy;' => { output->first = 0x041f; fbreak; }; + 'Pfr;' => { output->first = 0x0001d513; fbreak; }; + 'Phi;' => { output->first = 0x03a6; fbreak; }; + 'Pi;' => { output->first = 0x03a0; fbreak; }; + 'PlusMinus;' => { output->first = 0xb1; fbreak; }; + 'Poincareplane;' => { output->first = 0x210c; fbreak; }; + 'Popf;' => { output->first = 0x2119; fbreak; }; + 'Pr;' => { output->first = 0x2abb; fbreak; }; + 'Precedes;' => { output->first = 0x227a; fbreak; }; + 'PrecedesEqual;' => { output->first = 0x2aaf; fbreak; }; + 'PrecedesSlantEqual;' => { output->first = 0x227c; fbreak; }; + 'PrecedesTilde;' => { output->first = 0x227e; fbreak; }; + 'Prime;' => { output->first = 0x2033; fbreak; }; + 'Product;' => { output->first = 0x220f; fbreak; }; + 'Proportion;' => { output->first = 0x2237; fbreak; }; + 'Proportional;' => { output->first = 0x221d; fbreak; }; + 'Pscr;' => { output->first = 0x0001d4ab; fbreak; }; + 'Psi;' => { output->first = 0x03a8; fbreak; }; + 'QUOT;' => { output->first = 0x22; fbreak; }; + 'QUOT' => { output->first = 0x22; fbreak; }; + 'Qfr;' => { output->first = 0x0001d514; fbreak; }; + 'Qopf;' => { output->first = 0x211a; fbreak; }; + 'Qscr;' => { output->first = 0x0001d4ac; fbreak; }; + 'RBarr;' => { output->first = 0x2910; fbreak; }; + 'REG;' => { output->first = 0xae; fbreak; }; + 'REG' => { output->first = 0xae; fbreak; }; + 'Racute;' => { output->first = 0x0154; fbreak; }; + 'Rang;' => { output->first = 0x27eb; fbreak; }; + 'Rarr;' => { output->first = 0x21a0; fbreak; }; + 'Rarrtl;' => { output->first = 0x2916; fbreak; }; + 'Rcaron;' => { output->first = 0x0158; fbreak; }; + 'Rcedil;' => { output->first = 0x0156; fbreak; }; + 'Rcy;' => { output->first = 0x0420; fbreak; }; + 'Re;' => { output->first = 0x211c; fbreak; }; + 'ReverseElement;' => { output->first = 0x220b; fbreak; }; + 'ReverseEquilibrium;' => { output->first = 0x21cb; fbreak; }; + 'ReverseUpEquilibrium;' => { output->first = 0x296f; fbreak; }; + 'Rfr;' => { output->first = 0x211c; fbreak; }; + 'Rho;' => { output->first = 0x03a1; fbreak; }; + 'RightAngleBracket;' => { output->first = 0x27e9; fbreak; }; + 'RightArrow;' => { output->first = 0x2192; fbreak; }; + 'RightArrowBar;' => { output->first = 0x21e5; fbreak; }; + 'RightArrowLeftArrow;' => { output->first = 0x21c4; fbreak; }; + 'RightCeiling;' => { output->first = 0x2309; fbreak; }; + 'RightDoubleBracket;' => { output->first = 0x27e7; fbreak; }; + 'RightDownTeeVector;' => { output->first = 0x295d; fbreak; }; + 'RightDownVector;' => { output->first = 0x21c2; fbreak; }; + 'RightDownVectorBar;' => { output->first = 0x2955; fbreak; }; + 'RightFloor;' => { output->first = 0x230b; fbreak; }; + 'RightTee;' => { output->first = 0x22a2; fbreak; }; + 'RightTeeArrow;' => { output->first = 0x21a6; fbreak; }; + 'RightTeeVector;' => { output->first = 0x295b; fbreak; }; + 'RightTriangle;' => { output->first = 0x22b3; fbreak; }; + 'RightTriangleBar;' => { output->first = 0x29d0; fbreak; }; + 'RightTriangleEqual;' => { output->first = 0x22b5; fbreak; }; + 'RightUpDownVector;' => { output->first = 0x294f; fbreak; }; + 'RightUpTeeVector;' => { output->first = 0x295c; fbreak; }; + 'RightUpVector;' => { output->first = 0x21be; fbreak; }; + 'RightUpVectorBar;' => { output->first = 0x2954; fbreak; }; + 'RightVector;' => { output->first = 0x21c0; fbreak; }; + 'RightVectorBar;' => { output->first = 0x2953; fbreak; }; + 'Rightarrow;' => { output->first = 0x21d2; fbreak; }; + 'Ropf;' => { output->first = 0x211d; fbreak; }; + 'RoundImplies;' => { output->first = 0x2970; fbreak; }; + 'Rrightarrow;' => { output->first = 0x21db; fbreak; }; + 'Rscr;' => { output->first = 0x211b; fbreak; }; + 'Rsh;' => { output->first = 0x21b1; fbreak; }; + 'RuleDelayed;' => { output->first = 0x29f4; fbreak; }; + 'SHCHcy;' => { output->first = 0x0429; fbreak; }; + 'SHcy;' => { output->first = 0x0428; fbreak; }; + 'SOFTcy;' => { output->first = 0x042c; fbreak; }; + 'Sacute;' => { output->first = 0x015a; fbreak; }; + 'Sc;' => { output->first = 0x2abc; fbreak; }; + 'Scaron;' => { output->first = 0x0160; fbreak; }; + 'Scedil;' => { output->first = 0x015e; fbreak; }; + 'Scirc;' => { output->first = 0x015c; fbreak; }; + 'Scy;' => { output->first = 0x0421; fbreak; }; + 'Sfr;' => { output->first = 0x0001d516; fbreak; }; + 'ShortDownArrow;' => { output->first = 0x2193; fbreak; }; + 'ShortLeftArrow;' => { output->first = 0x2190; fbreak; }; + 'ShortRightArrow;' => { output->first = 0x2192; fbreak; }; + 'ShortUpArrow;' => { output->first = 0x2191; fbreak; }; + 'Sigma;' => { output->first = 0x03a3; fbreak; }; + 'SmallCircle;' => { output->first = 0x2218; fbreak; }; + 'Sopf;' => { output->first = 0x0001d54a; fbreak; }; + 'Sqrt;' => { output->first = 0x221a; fbreak; }; + 'Square;' => { output->first = 0x25a1; fbreak; }; + 'SquareIntersection;' => { output->first = 0x2293; fbreak; }; + 'SquareSubset;' => { output->first = 0x228f; fbreak; }; + 'SquareSubsetEqual;' => { output->first = 0x2291; fbreak; }; + 'SquareSuperset;' => { output->first = 0x2290; fbreak; }; + 'SquareSupersetEqual;' => { output->first = 0x2292; fbreak; }; + 'SquareUnion;' => { output->first = 0x2294; fbreak; }; + 'Sscr;' => { output->first = 0x0001d4ae; fbreak; }; + 'Star;' => { output->first = 0x22c6; fbreak; }; + 'Sub;' => { output->first = 0x22d0; fbreak; }; + 'Subset;' => { output->first = 0x22d0; fbreak; }; + 'SubsetEqual;' => { output->first = 0x2286; fbreak; }; + 'Succeeds;' => { output->first = 0x227b; fbreak; }; + 'SucceedsEqual;' => { output->first = 0x2ab0; fbreak; }; + 'SucceedsSlantEqual;' => { output->first = 0x227d; fbreak; }; + 'SucceedsTilde;' => { output->first = 0x227f; fbreak; }; + 'SuchThat;' => { output->first = 0x220b; fbreak; }; + 'Sum;' => { output->first = 0x2211; fbreak; }; + 'Sup;' => { output->first = 0x22d1; fbreak; }; + 'Superset;' => { output->first = 0x2283; fbreak; }; + 'SupersetEqual;' => { output->first = 0x2287; fbreak; }; + 'Supset;' => { output->first = 0x22d1; fbreak; }; + 'THORN;' => { output->first = 0xde; fbreak; }; + 'THORN' => { output->first = 0xde; fbreak; }; + 'TRADE;' => { output->first = 0x2122; fbreak; }; + 'TSHcy;' => { output->first = 0x040b; fbreak; }; + 'TScy;' => { output->first = 0x0426; fbreak; }; + 'Tab;' => { output->first = 0x09; fbreak; }; + 'Tau;' => { output->first = 0x03a4; fbreak; }; + 'Tcaron;' => { output->first = 0x0164; fbreak; }; + 'Tcedil;' => { output->first = 0x0162; fbreak; }; + 'Tcy;' => { output->first = 0x0422; fbreak; }; + 'Tfr;' => { output->first = 0x0001d517; fbreak; }; + 'Therefore;' => { output->first = 0x2234; fbreak; }; + 'Theta;' => { output->first = 0x0398; fbreak; }; + 'ThickSpace;' => { output->first = 0x205f; output->second = 0x200a; fbreak; }; + 'ThinSpace;' => { output->first = 0x2009; fbreak; }; + 'Tilde;' => { output->first = 0x223c; fbreak; }; + 'TildeEqual;' => { output->first = 0x2243; fbreak; }; + 'TildeFullEqual;' => { output->first = 0x2245; fbreak; }; + 'TildeTilde;' => { output->first = 0x2248; fbreak; }; + 'Topf;' => { output->first = 0x0001d54b; fbreak; }; + 'TripleDot;' => { output->first = 0x20db; fbreak; }; + 'Tscr;' => { output->first = 0x0001d4af; fbreak; }; + 'Tstrok;' => { output->first = 0x0166; fbreak; }; + 'Uacute;' => { output->first = 0xda; fbreak; }; + 'Uacute' => { output->first = 0xda; fbreak; }; + 'Uarr;' => { output->first = 0x219f; fbreak; }; + 'Uarrocir;' => { output->first = 0x2949; fbreak; }; + 'Ubrcy;' => { output->first = 0x040e; fbreak; }; + 'Ubreve;' => { output->first = 0x016c; fbreak; }; + 'Ucirc;' => { output->first = 0xdb; fbreak; }; + 'Ucirc' => { output->first = 0xdb; fbreak; }; + 'Ucy;' => { output->first = 0x0423; fbreak; }; + 'Udblac;' => { output->first = 0x0170; fbreak; }; + 'Ufr;' => { output->first = 0x0001d518; fbreak; }; + 'Ugrave;' => { output->first = 0xd9; fbreak; }; + 'Ugrave' => { output->first = 0xd9; fbreak; }; + 'Umacr;' => { output->first = 0x016a; fbreak; }; + 'UnderBar;' => { output->first = 0x5f; fbreak; }; + 'UnderBrace;' => { output->first = 0x23df; fbreak; }; + 'UnderBracket;' => { output->first = 0x23b5; fbreak; }; + 'UnderParenthesis;' => { output->first = 0x23dd; fbreak; }; + 'Union;' => { output->first = 0x22c3; fbreak; }; + 'UnionPlus;' => { output->first = 0x228e; fbreak; }; + 'Uogon;' => { output->first = 0x0172; fbreak; }; + 'Uopf;' => { output->first = 0x0001d54c; fbreak; }; + 'UpArrow;' => { output->first = 0x2191; fbreak; }; + 'UpArrowBar;' => { output->first = 0x2912; fbreak; }; + 'UpArrowDownArrow;' => { output->first = 0x21c5; fbreak; }; + 'UpDownArrow;' => { output->first = 0x2195; fbreak; }; + 'UpEquilibrium;' => { output->first = 0x296e; fbreak; }; + 'UpTee;' => { output->first = 0x22a5; fbreak; }; + 'UpTeeArrow;' => { output->first = 0x21a5; fbreak; }; + 'Uparrow;' => { output->first = 0x21d1; fbreak; }; + 'Updownarrow;' => { output->first = 0x21d5; fbreak; }; + 'UpperLeftArrow;' => { output->first = 0x2196; fbreak; }; + 'UpperRightArrow;' => { output->first = 0x2197; fbreak; }; + 'Upsi;' => { output->first = 0x03d2; fbreak; }; + 'Upsilon;' => { output->first = 0x03a5; fbreak; }; + 'Uring;' => { output->first = 0x016e; fbreak; }; + 'Uscr;' => { output->first = 0x0001d4b0; fbreak; }; + 'Utilde;' => { output->first = 0x0168; fbreak; }; + 'Uuml;' => { output->first = 0xdc; fbreak; }; + 'Uuml' => { output->first = 0xdc; fbreak; }; + 'VDash;' => { output->first = 0x22ab; fbreak; }; + 'Vbar;' => { output->first = 0x2aeb; fbreak; }; + 'Vcy;' => { output->first = 0x0412; fbreak; }; + 'Vdash;' => { output->first = 0x22a9; fbreak; }; + 'Vdashl;' => { output->first = 0x2ae6; fbreak; }; + 'Vee;' => { output->first = 0x22c1; fbreak; }; + 'Verbar;' => { output->first = 0x2016; fbreak; }; + 'Vert;' => { output->first = 0x2016; fbreak; }; + 'VerticalBar;' => { output->first = 0x2223; fbreak; }; + 'VerticalLine;' => { output->first = 0x7c; fbreak; }; + 'VerticalSeparator;' => { output->first = 0x2758; fbreak; }; + 'VerticalTilde;' => { output->first = 0x2240; fbreak; }; + 'VeryThinSpace;' => { output->first = 0x200a; fbreak; }; + 'Vfr;' => { output->first = 0x0001d519; fbreak; }; + 'Vopf;' => { output->first = 0x0001d54d; fbreak; }; + 'Vscr;' => { output->first = 0x0001d4b1; fbreak; }; + 'Vvdash;' => { output->first = 0x22aa; fbreak; }; + 'Wcirc;' => { output->first = 0x0174; fbreak; }; + 'Wedge;' => { output->first = 0x22c0; fbreak; }; + 'Wfr;' => { output->first = 0x0001d51a; fbreak; }; + 'Wopf;' => { output->first = 0x0001d54e; fbreak; }; + 'Wscr;' => { output->first = 0x0001d4b2; fbreak; }; + 'Xfr;' => { output->first = 0x0001d51b; fbreak; }; + 'Xi;' => { output->first = 0x039e; fbreak; }; + 'Xopf;' => { output->first = 0x0001d54f; fbreak; }; + 'Xscr;' => { output->first = 0x0001d4b3; fbreak; }; + 'YAcy;' => { output->first = 0x042f; fbreak; }; + 'YIcy;' => { output->first = 0x0407; fbreak; }; + 'YUcy;' => { output->first = 0x042e; fbreak; }; + 'Yacute;' => { output->first = 0xdd; fbreak; }; + 'Yacute' => { output->first = 0xdd; fbreak; }; + 'Ycirc;' => { output->first = 0x0176; fbreak; }; + 'Ycy;' => { output->first = 0x042b; fbreak; }; + 'Yfr;' => { output->first = 0x0001d51c; fbreak; }; + 'Yopf;' => { output->first = 0x0001d550; fbreak; }; + 'Yscr;' => { output->first = 0x0001d4b4; fbreak; }; + 'Yuml;' => { output->first = 0x0178; fbreak; }; + 'ZHcy;' => { output->first = 0x0416; fbreak; }; + 'Zacute;' => { output->first = 0x0179; fbreak; }; + 'Zcaron;' => { output->first = 0x017d; fbreak; }; + 'Zcy;' => { output->first = 0x0417; fbreak; }; + 'Zdot;' => { output->first = 0x017b; fbreak; }; + 'ZeroWidthSpace;' => { output->first = 0x200b; fbreak; }; + 'Zeta;' => { output->first = 0x0396; fbreak; }; + 'Zfr;' => { output->first = 0x2128; fbreak; }; + 'Zopf;' => { output->first = 0x2124; fbreak; }; + 'Zscr;' => { output->first = 0x0001d4b5; fbreak; }; + 'aacute;' => { output->first = 0xe1; fbreak; }; + 'aacute' => { output->first = 0xe1; fbreak; }; + 'abreve;' => { output->first = 0x0103; fbreak; }; + 'ac;' => { output->first = 0x223e; fbreak; }; + 'acE;' => { output->first = 0x223e; output->second = 0x0333; fbreak; }; + 'acd;' => { output->first = 0x223f; fbreak; }; + 'acirc;' => { output->first = 0xe2; fbreak; }; + 'acirc' => { output->first = 0xe2; fbreak; }; + 'acute;' => { output->first = 0xb4; fbreak; }; + 'acute' => { output->first = 0xb4; fbreak; }; + 'acy;' => { output->first = 0x0430; fbreak; }; + 'aelig;' => { output->first = 0xe6; fbreak; }; + 'aelig' => { output->first = 0xe6; fbreak; }; + 'af;' => { output->first = 0x2061; fbreak; }; + 'afr;' => { output->first = 0x0001d51e; fbreak; }; + 'agrave;' => { output->first = 0xe0; fbreak; }; + 'agrave' => { output->first = 0xe0; fbreak; }; + 'alefsym;' => { output->first = 0x2135; fbreak; }; + 'aleph;' => { output->first = 0x2135; fbreak; }; + 'alpha;' => { output->first = 0x03b1; fbreak; }; + 'amacr;' => { output->first = 0x0101; fbreak; }; + 'amalg;' => { output->first = 0x2a3f; fbreak; }; + 'amp;' => { output->first = 0x26; fbreak; }; + 'amp' => { output->first = 0x26; fbreak; }; + 'and;' => { output->first = 0x2227; fbreak; }; + 'andand;' => { output->first = 0x2a55; fbreak; }; + 'andd;' => { output->first = 0x2a5c; fbreak; }; + 'andslope;' => { output->first = 0x2a58; fbreak; }; + 'andv;' => { output->first = 0x2a5a; fbreak; }; + 'ang;' => { output->first = 0x2220; fbreak; }; + 'ange;' => { output->first = 0x29a4; fbreak; }; + 'angle;' => { output->first = 0x2220; fbreak; }; + 'angmsd;' => { output->first = 0x2221; fbreak; }; + 'angmsdaa;' => { output->first = 0x29a8; fbreak; }; + 'angmsdab;' => { output->first = 0x29a9; fbreak; }; + 'angmsdac;' => { output->first = 0x29aa; fbreak; }; + 'angmsdad;' => { output->first = 0x29ab; fbreak; }; + 'angmsdae;' => { output->first = 0x29ac; fbreak; }; + 'angmsdaf;' => { output->first = 0x29ad; fbreak; }; + 'angmsdag;' => { output->first = 0x29ae; fbreak; }; + 'angmsdah;' => { output->first = 0x29af; fbreak; }; + 'angrt;' => { output->first = 0x221f; fbreak; }; + 'angrtvb;' => { output->first = 0x22be; fbreak; }; + 'angrtvbd;' => { output->first = 0x299d; fbreak; }; + 'angsph;' => { output->first = 0x2222; fbreak; }; + 'angst;' => { output->first = 0xc5; fbreak; }; + 'angzarr;' => { output->first = 0x237c; fbreak; }; + 'aogon;' => { output->first = 0x0105; fbreak; }; + 'aopf;' => { output->first = 0x0001d552; fbreak; }; + 'ap;' => { output->first = 0x2248; fbreak; }; + 'apE;' => { output->first = 0x2a70; fbreak; }; + 'apacir;' => { output->first = 0x2a6f; fbreak; }; + 'ape;' => { output->first = 0x224a; fbreak; }; + 'apid;' => { output->first = 0x224b; fbreak; }; + 'apos;' => { output->first = 0x27; fbreak; }; + 'approx;' => { output->first = 0x2248; fbreak; }; + 'approxeq;' => { output->first = 0x224a; fbreak; }; + 'aring;' => { output->first = 0xe5; fbreak; }; + 'aring' => { output->first = 0xe5; fbreak; }; + 'ascr;' => { output->first = 0x0001d4b6; fbreak; }; + 'ast;' => { output->first = 0x2a; fbreak; }; + 'asymp;' => { output->first = 0x2248; fbreak; }; + 'asympeq;' => { output->first = 0x224d; fbreak; }; + 'atilde;' => { output->first = 0xe3; fbreak; }; + 'atilde' => { output->first = 0xe3; fbreak; }; + 'auml;' => { output->first = 0xe4; fbreak; }; + 'auml' => { output->first = 0xe4; fbreak; }; + 'awconint;' => { output->first = 0x2233; fbreak; }; + 'awint;' => { output->first = 0x2a11; fbreak; }; + 'bNot;' => { output->first = 0x2aed; fbreak; }; + 'backcong;' => { output->first = 0x224c; fbreak; }; + 'backepsilon;' => { output->first = 0x03f6; fbreak; }; + 'backprime;' => { output->first = 0x2035; fbreak; }; + 'backsim;' => { output->first = 0x223d; fbreak; }; + 'backsimeq;' => { output->first = 0x22cd; fbreak; }; + 'barvee;' => { output->first = 0x22bd; fbreak; }; + 'barwed;' => { output->first = 0x2305; fbreak; }; + 'barwedge;' => { output->first = 0x2305; fbreak; }; + 'bbrk;' => { output->first = 0x23b5; fbreak; }; + 'bbrktbrk;' => { output->first = 0x23b6; fbreak; }; + 'bcong;' => { output->first = 0x224c; fbreak; }; + 'bcy;' => { output->first = 0x0431; fbreak; }; + 'bdquo;' => { output->first = 0x201e; fbreak; }; + 'becaus;' => { output->first = 0x2235; fbreak; }; + 'because;' => { output->first = 0x2235; fbreak; }; + 'bemptyv;' => { output->first = 0x29b0; fbreak; }; + 'bepsi;' => { output->first = 0x03f6; fbreak; }; + 'bernou;' => { output->first = 0x212c; fbreak; }; + 'beta;' => { output->first = 0x03b2; fbreak; }; + 'beth;' => { output->first = 0x2136; fbreak; }; + 'between;' => { output->first = 0x226c; fbreak; }; + 'bfr;' => { output->first = 0x0001d51f; fbreak; }; + 'bigcap;' => { output->first = 0x22c2; fbreak; }; + 'bigcirc;' => { output->first = 0x25ef; fbreak; }; + 'bigcup;' => { output->first = 0x22c3; fbreak; }; + 'bigodot;' => { output->first = 0x2a00; fbreak; }; + 'bigoplus;' => { output->first = 0x2a01; fbreak; }; + 'bigotimes;' => { output->first = 0x2a02; fbreak; }; + 'bigsqcup;' => { output->first = 0x2a06; fbreak; }; + 'bigstar;' => { output->first = 0x2605; fbreak; }; + 'bigtriangledown;' => { output->first = 0x25bd; fbreak; }; + 'bigtriangleup;' => { output->first = 0x25b3; fbreak; }; + 'biguplus;' => { output->first = 0x2a04; fbreak; }; + 'bigvee;' => { output->first = 0x22c1; fbreak; }; + 'bigwedge;' => { output->first = 0x22c0; fbreak; }; + 'bkarow;' => { output->first = 0x290d; fbreak; }; + 'blacklozenge;' => { output->first = 0x29eb; fbreak; }; + 'blacksquare;' => { output->first = 0x25aa; fbreak; }; + 'blacktriangle;' => { output->first = 0x25b4; fbreak; }; + 'blacktriangledown;' => { output->first = 0x25be; fbreak; }; + 'blacktriangleleft;' => { output->first = 0x25c2; fbreak; }; + 'blacktriangleright;' => { output->first = 0x25b8; fbreak; }; + 'blank;' => { output->first = 0x2423; fbreak; }; + 'blk12;' => { output->first = 0x2592; fbreak; }; + 'blk14;' => { output->first = 0x2591; fbreak; }; + 'blk34;' => { output->first = 0x2593; fbreak; }; + 'block;' => { output->first = 0x2588; fbreak; }; + 'bne;' => { output->first = 0x3d; output->second = 0x20e5; fbreak; }; + 'bnequiv;' => { output->first = 0x2261; output->second = 0x20e5; fbreak; }; + 'bnot;' => { output->first = 0x2310; fbreak; }; + 'bopf;' => { output->first = 0x0001d553; fbreak; }; + 'bot;' => { output->first = 0x22a5; fbreak; }; + 'bottom;' => { output->first = 0x22a5; fbreak; }; + 'bowtie;' => { output->first = 0x22c8; fbreak; }; + 'boxDL;' => { output->first = 0x2557; fbreak; }; + 'boxDR;' => { output->first = 0x2554; fbreak; }; + 'boxDl;' => { output->first = 0x2556; fbreak; }; + 'boxDr;' => { output->first = 0x2553; fbreak; }; + 'boxH;' => { output->first = 0x2550; fbreak; }; + 'boxHD;' => { output->first = 0x2566; fbreak; }; + 'boxHU;' => { output->first = 0x2569; fbreak; }; + 'boxHd;' => { output->first = 0x2564; fbreak; }; + 'boxHu;' => { output->first = 0x2567; fbreak; }; + 'boxUL;' => { output->first = 0x255d; fbreak; }; + 'boxUR;' => { output->first = 0x255a; fbreak; }; + 'boxUl;' => { output->first = 0x255c; fbreak; }; + 'boxUr;' => { output->first = 0x2559; fbreak; }; + 'boxV;' => { output->first = 0x2551; fbreak; }; + 'boxVH;' => { output->first = 0x256c; fbreak; }; + 'boxVL;' => { output->first = 0x2563; fbreak; }; + 'boxVR;' => { output->first = 0x2560; fbreak; }; + 'boxVh;' => { output->first = 0x256b; fbreak; }; + 'boxVl;' => { output->first = 0x2562; fbreak; }; + 'boxVr;' => { output->first = 0x255f; fbreak; }; + 'boxbox;' => { output->first = 0x29c9; fbreak; }; + 'boxdL;' => { output->first = 0x2555; fbreak; }; + 'boxdR;' => { output->first = 0x2552; fbreak; }; + 'boxdl;' => { output->first = 0x2510; fbreak; }; + 'boxdr;' => { output->first = 0x250c; fbreak; }; + 'boxh;' => { output->first = 0x2500; fbreak; }; + 'boxhD;' => { output->first = 0x2565; fbreak; }; + 'boxhU;' => { output->first = 0x2568; fbreak; }; + 'boxhd;' => { output->first = 0x252c; fbreak; }; + 'boxhu;' => { output->first = 0x2534; fbreak; }; + 'boxminus;' => { output->first = 0x229f; fbreak; }; + 'boxplus;' => { output->first = 0x229e; fbreak; }; + 'boxtimes;' => { output->first = 0x22a0; fbreak; }; + 'boxuL;' => { output->first = 0x255b; fbreak; }; + 'boxuR;' => { output->first = 0x2558; fbreak; }; + 'boxul;' => { output->first = 0x2518; fbreak; }; + 'boxur;' => { output->first = 0x2514; fbreak; }; + 'boxv;' => { output->first = 0x2502; fbreak; }; + 'boxvH;' => { output->first = 0x256a; fbreak; }; + 'boxvL;' => { output->first = 0x2561; fbreak; }; + 'boxvR;' => { output->first = 0x255e; fbreak; }; + 'boxvh;' => { output->first = 0x253c; fbreak; }; + 'boxvl;' => { output->first = 0x2524; fbreak; }; + 'boxvr;' => { output->first = 0x251c; fbreak; }; + 'bprime;' => { output->first = 0x2035; fbreak; }; + 'breve;' => { output->first = 0x02d8; fbreak; }; + 'brvbar;' => { output->first = 0xa6; fbreak; }; + 'brvbar' => { output->first = 0xa6; fbreak; }; + 'bscr;' => { output->first = 0x0001d4b7; fbreak; }; + 'bsemi;' => { output->first = 0x204f; fbreak; }; + 'bsim;' => { output->first = 0x223d; fbreak; }; + 'bsime;' => { output->first = 0x22cd; fbreak; }; + 'bsol;' => { output->first = 0x5c; fbreak; }; + 'bsolb;' => { output->first = 0x29c5; fbreak; }; + 'bsolhsub;' => { output->first = 0x27c8; fbreak; }; + 'bull;' => { output->first = 0x2022; fbreak; }; + 'bullet;' => { output->first = 0x2022; fbreak; }; + 'bump;' => { output->first = 0x224e; fbreak; }; + 'bumpE;' => { output->first = 0x2aae; fbreak; }; + 'bumpe;' => { output->first = 0x224f; fbreak; }; + 'bumpeq;' => { output->first = 0x224f; fbreak; }; + 'cacute;' => { output->first = 0x0107; fbreak; }; + 'cap;' => { output->first = 0x2229; fbreak; }; + 'capand;' => { output->first = 0x2a44; fbreak; }; + 'capbrcup;' => { output->first = 0x2a49; fbreak; }; + 'capcap;' => { output->first = 0x2a4b; fbreak; }; + 'capcup;' => { output->first = 0x2a47; fbreak; }; + 'capdot;' => { output->first = 0x2a40; fbreak; }; + 'caps;' => { output->first = 0x2229; output->second = 0xfe00; fbreak; }; + 'caret;' => { output->first = 0x2041; fbreak; }; + 'caron;' => { output->first = 0x02c7; fbreak; }; + 'ccaps;' => { output->first = 0x2a4d; fbreak; }; + 'ccaron;' => { output->first = 0x010d; fbreak; }; + 'ccedil;' => { output->first = 0xe7; fbreak; }; + 'ccedil' => { output->first = 0xe7; fbreak; }; + 'ccirc;' => { output->first = 0x0109; fbreak; }; + 'ccups;' => { output->first = 0x2a4c; fbreak; }; + 'ccupssm;' => { output->first = 0x2a50; fbreak; }; + 'cdot;' => { output->first = 0x010b; fbreak; }; + 'cedil;' => { output->first = 0xb8; fbreak; }; + 'cedil' => { output->first = 0xb8; fbreak; }; + 'cemptyv;' => { output->first = 0x29b2; fbreak; }; + 'cent;' => { output->first = 0xa2; fbreak; }; + 'cent' => { output->first = 0xa2; fbreak; }; + 'centerdot;' => { output->first = 0xb7; fbreak; }; + 'cfr;' => { output->first = 0x0001d520; fbreak; }; + 'chcy;' => { output->first = 0x0447; fbreak; }; + 'check;' => { output->first = 0x2713; fbreak; }; + 'checkmark;' => { output->first = 0x2713; fbreak; }; + 'chi;' => { output->first = 0x03c7; fbreak; }; + 'cir;' => { output->first = 0x25cb; fbreak; }; + 'cirE;' => { output->first = 0x29c3; fbreak; }; + 'circ;' => { output->first = 0x02c6; fbreak; }; + 'circeq;' => { output->first = 0x2257; fbreak; }; + 'circlearrowleft;' => { output->first = 0x21ba; fbreak; }; + 'circlearrowright;' => { output->first = 0x21bb; fbreak; }; + 'circledR;' => { output->first = 0xae; fbreak; }; + 'circledS;' => { output->first = 0x24c8; fbreak; }; + 'circledast;' => { output->first = 0x229b; fbreak; }; + 'circledcirc;' => { output->first = 0x229a; fbreak; }; + 'circleddash;' => { output->first = 0x229d; fbreak; }; + 'cire;' => { output->first = 0x2257; fbreak; }; + 'cirfnint;' => { output->first = 0x2a10; fbreak; }; + 'cirmid;' => { output->first = 0x2aef; fbreak; }; + 'cirscir;' => { output->first = 0x29c2; fbreak; }; + 'clubs;' => { output->first = 0x2663; fbreak; }; + 'clubsuit;' => { output->first = 0x2663; fbreak; }; + 'colon;' => { output->first = 0x3a; fbreak; }; + 'colone;' => { output->first = 0x2254; fbreak; }; + 'coloneq;' => { output->first = 0x2254; fbreak; }; + 'comma;' => { output->first = 0x2c; fbreak; }; + 'commat;' => { output->first = 0x40; fbreak; }; + 'comp;' => { output->first = 0x2201; fbreak; }; + 'compfn;' => { output->first = 0x2218; fbreak; }; + 'complement;' => { output->first = 0x2201; fbreak; }; + 'complexes;' => { output->first = 0x2102; fbreak; }; + 'cong;' => { output->first = 0x2245; fbreak; }; + 'congdot;' => { output->first = 0x2a6d; fbreak; }; + 'conint;' => { output->first = 0x222e; fbreak; }; + 'copf;' => { output->first = 0x0001d554; fbreak; }; + 'coprod;' => { output->first = 0x2210; fbreak; }; + 'copy;' => { output->first = 0xa9; fbreak; }; + 'copy' => { output->first = 0xa9; fbreak; }; + 'copysr;' => { output->first = 0x2117; fbreak; }; + 'crarr;' => { output->first = 0x21b5; fbreak; }; + 'cross;' => { output->first = 0x2717; fbreak; }; + 'cscr;' => { output->first = 0x0001d4b8; fbreak; }; + 'csub;' => { output->first = 0x2acf; fbreak; }; + 'csube;' => { output->first = 0x2ad1; fbreak; }; + 'csup;' => { output->first = 0x2ad0; fbreak; }; + 'csupe;' => { output->first = 0x2ad2; fbreak; }; + 'ctdot;' => { output->first = 0x22ef; fbreak; }; + 'cudarrl;' => { output->first = 0x2938; fbreak; }; + 'cudarrr;' => { output->first = 0x2935; fbreak; }; + 'cuepr;' => { output->first = 0x22de; fbreak; }; + 'cuesc;' => { output->first = 0x22df; fbreak; }; + 'cularr;' => { output->first = 0x21b6; fbreak; }; + 'cularrp;' => { output->first = 0x293d; fbreak; }; + 'cup;' => { output->first = 0x222a; fbreak; }; + 'cupbrcap;' => { output->first = 0x2a48; fbreak; }; + 'cupcap;' => { output->first = 0x2a46; fbreak; }; + 'cupcup;' => { output->first = 0x2a4a; fbreak; }; + 'cupdot;' => { output->first = 0x228d; fbreak; }; + 'cupor;' => { output->first = 0x2a45; fbreak; }; + 'cups;' => { output->first = 0x222a; output->second = 0xfe00; fbreak; }; + 'curarr;' => { output->first = 0x21b7; fbreak; }; + 'curarrm;' => { output->first = 0x293c; fbreak; }; + 'curlyeqprec;' => { output->first = 0x22de; fbreak; }; + 'curlyeqsucc;' => { output->first = 0x22df; fbreak; }; + 'curlyvee;' => { output->first = 0x22ce; fbreak; }; + 'curlywedge;' => { output->first = 0x22cf; fbreak; }; + 'curren;' => { output->first = 0xa4; fbreak; }; + 'curren' => { output->first = 0xa4; fbreak; }; + 'curvearrowleft;' => { output->first = 0x21b6; fbreak; }; + 'curvearrowright;' => { output->first = 0x21b7; fbreak; }; + 'cuvee;' => { output->first = 0x22ce; fbreak; }; + 'cuwed;' => { output->first = 0x22cf; fbreak; }; + 'cwconint;' => { output->first = 0x2232; fbreak; }; + 'cwint;' => { output->first = 0x2231; fbreak; }; + 'cylcty;' => { output->first = 0x232d; fbreak; }; + 'dArr;' => { output->first = 0x21d3; fbreak; }; + 'dHar;' => { output->first = 0x2965; fbreak; }; + 'dagger;' => { output->first = 0x2020; fbreak; }; + 'daleth;' => { output->first = 0x2138; fbreak; }; + 'darr;' => { output->first = 0x2193; fbreak; }; + 'dash;' => { output->first = 0x2010; fbreak; }; + 'dashv;' => { output->first = 0x22a3; fbreak; }; + 'dbkarow;' => { output->first = 0x290f; fbreak; }; + 'dblac;' => { output->first = 0x02dd; fbreak; }; + 'dcaron;' => { output->first = 0x010f; fbreak; }; + 'dcy;' => { output->first = 0x0434; fbreak; }; + 'dd;' => { output->first = 0x2146; fbreak; }; + 'ddagger;' => { output->first = 0x2021; fbreak; }; + 'ddarr;' => { output->first = 0x21ca; fbreak; }; + 'ddotseq;' => { output->first = 0x2a77; fbreak; }; + 'deg;' => { output->first = 0xb0; fbreak; }; + 'deg' => { output->first = 0xb0; fbreak; }; + 'delta;' => { output->first = 0x03b4; fbreak; }; + 'demptyv;' => { output->first = 0x29b1; fbreak; }; + 'dfisht;' => { output->first = 0x297f; fbreak; }; + 'dfr;' => { output->first = 0x0001d521; fbreak; }; + 'dharl;' => { output->first = 0x21c3; fbreak; }; + 'dharr;' => { output->first = 0x21c2; fbreak; }; + 'diam;' => { output->first = 0x22c4; fbreak; }; + 'diamond;' => { output->first = 0x22c4; fbreak; }; + 'diamondsuit;' => { output->first = 0x2666; fbreak; }; + 'diams;' => { output->first = 0x2666; fbreak; }; + 'die;' => { output->first = 0xa8; fbreak; }; + 'digamma;' => { output->first = 0x03dd; fbreak; }; + 'disin;' => { output->first = 0x22f2; fbreak; }; + 'div;' => { output->first = 0xf7; fbreak; }; + 'divide;' => { output->first = 0xf7; fbreak; }; + 'divide' => { output->first = 0xf7; fbreak; }; + 'divideontimes;' => { output->first = 0x22c7; fbreak; }; + 'divonx;' => { output->first = 0x22c7; fbreak; }; + 'djcy;' => { output->first = 0x0452; fbreak; }; + 'dlcorn;' => { output->first = 0x231e; fbreak; }; + 'dlcrop;' => { output->first = 0x230d; fbreak; }; + 'dollar;' => { output->first = 0x24; fbreak; }; + 'dopf;' => { output->first = 0x0001d555; fbreak; }; + 'dot;' => { output->first = 0x02d9; fbreak; }; + 'doteq;' => { output->first = 0x2250; fbreak; }; + 'doteqdot;' => { output->first = 0x2251; fbreak; }; + 'dotminus;' => { output->first = 0x2238; fbreak; }; + 'dotplus;' => { output->first = 0x2214; fbreak; }; + 'dotsquare;' => { output->first = 0x22a1; fbreak; }; + 'doublebarwedge;' => { output->first = 0x2306; fbreak; }; + 'downarrow;' => { output->first = 0x2193; fbreak; }; + 'downdownarrows;' => { output->first = 0x21ca; fbreak; }; + 'downharpoonleft;' => { output->first = 0x21c3; fbreak; }; + 'downharpoonright;' => { output->first = 0x21c2; fbreak; }; + 'drbkarow;' => { output->first = 0x2910; fbreak; }; + 'drcorn;' => { output->first = 0x231f; fbreak; }; + 'drcrop;' => { output->first = 0x230c; fbreak; }; + 'dscr;' => { output->first = 0x0001d4b9; fbreak; }; + 'dscy;' => { output->first = 0x0455; fbreak; }; + 'dsol;' => { output->first = 0x29f6; fbreak; }; + 'dstrok;' => { output->first = 0x0111; fbreak; }; + 'dtdot;' => { output->first = 0x22f1; fbreak; }; + 'dtri;' => { output->first = 0x25bf; fbreak; }; + 'dtrif;' => { output->first = 0x25be; fbreak; }; + 'duarr;' => { output->first = 0x21f5; fbreak; }; + 'duhar;' => { output->first = 0x296f; fbreak; }; + 'dwangle;' => { output->first = 0x29a6; fbreak; }; + 'dzcy;' => { output->first = 0x045f; fbreak; }; + 'dzigrarr;' => { output->first = 0x27ff; fbreak; }; + 'eDDot;' => { output->first = 0x2a77; fbreak; }; + 'eDot;' => { output->first = 0x2251; fbreak; }; + 'eacute;' => { output->first = 0xe9; fbreak; }; + 'eacute' => { output->first = 0xe9; fbreak; }; + 'easter;' => { output->first = 0x2a6e; fbreak; }; + 'ecaron;' => { output->first = 0x011b; fbreak; }; + 'ecir;' => { output->first = 0x2256; fbreak; }; + 'ecirc;' => { output->first = 0xea; fbreak; }; + 'ecirc' => { output->first = 0xea; fbreak; }; + 'ecolon;' => { output->first = 0x2255; fbreak; }; + 'ecy;' => { output->first = 0x044d; fbreak; }; + 'edot;' => { output->first = 0x0117; fbreak; }; + 'ee;' => { output->first = 0x2147; fbreak; }; + 'efDot;' => { output->first = 0x2252; fbreak; }; + 'efr;' => { output->first = 0x0001d522; fbreak; }; + 'eg;' => { output->first = 0x2a9a; fbreak; }; + 'egrave;' => { output->first = 0xe8; fbreak; }; + 'egrave' => { output->first = 0xe8; fbreak; }; + 'egs;' => { output->first = 0x2a96; fbreak; }; + 'egsdot;' => { output->first = 0x2a98; fbreak; }; + 'el;' => { output->first = 0x2a99; fbreak; }; + 'elinters;' => { output->first = 0x23e7; fbreak; }; + 'ell;' => { output->first = 0x2113; fbreak; }; + 'els;' => { output->first = 0x2a95; fbreak; }; + 'elsdot;' => { output->first = 0x2a97; fbreak; }; + 'emacr;' => { output->first = 0x0113; fbreak; }; + 'empty;' => { output->first = 0x2205; fbreak; }; + 'emptyset;' => { output->first = 0x2205; fbreak; }; + 'emptyv;' => { output->first = 0x2205; fbreak; }; + 'emsp13;' => { output->first = 0x2004; fbreak; }; + 'emsp14;' => { output->first = 0x2005; fbreak; }; + 'emsp;' => { output->first = 0x2003; fbreak; }; + 'eng;' => { output->first = 0x014b; fbreak; }; + 'ensp;' => { output->first = 0x2002; fbreak; }; + 'eogon;' => { output->first = 0x0119; fbreak; }; + 'eopf;' => { output->first = 0x0001d556; fbreak; }; + 'epar;' => { output->first = 0x22d5; fbreak; }; + 'eparsl;' => { output->first = 0x29e3; fbreak; }; + 'eplus;' => { output->first = 0x2a71; fbreak; }; + 'epsi;' => { output->first = 0x03b5; fbreak; }; + 'epsilon;' => { output->first = 0x03b5; fbreak; }; + 'epsiv;' => { output->first = 0x03f5; fbreak; }; + 'eqcirc;' => { output->first = 0x2256; fbreak; }; + 'eqcolon;' => { output->first = 0x2255; fbreak; }; + 'eqsim;' => { output->first = 0x2242; fbreak; }; + 'eqslantgtr;' => { output->first = 0x2a96; fbreak; }; + 'eqslantless;' => { output->first = 0x2a95; fbreak; }; + 'equals;' => { output->first = 0x3d; fbreak; }; + 'equest;' => { output->first = 0x225f; fbreak; }; + 'equiv;' => { output->first = 0x2261; fbreak; }; + 'equivDD;' => { output->first = 0x2a78; fbreak; }; + 'eqvparsl;' => { output->first = 0x29e5; fbreak; }; + 'erDot;' => { output->first = 0x2253; fbreak; }; + 'erarr;' => { output->first = 0x2971; fbreak; }; + 'escr;' => { output->first = 0x212f; fbreak; }; + 'esdot;' => { output->first = 0x2250; fbreak; }; + 'esim;' => { output->first = 0x2242; fbreak; }; + 'eta;' => { output->first = 0x03b7; fbreak; }; + 'eth;' => { output->first = 0xf0; fbreak; }; + 'eth' => { output->first = 0xf0; fbreak; }; + 'euml;' => { output->first = 0xeb; fbreak; }; + 'euml' => { output->first = 0xeb; fbreak; }; + 'euro;' => { output->first = 0x20ac; fbreak; }; + 'excl;' => { output->first = 0x21; fbreak; }; + 'exist;' => { output->first = 0x2203; fbreak; }; + 'expectation;' => { output->first = 0x2130; fbreak; }; + 'exponentiale;' => { output->first = 0x2147; fbreak; }; + 'fallingdotseq;' => { output->first = 0x2252; fbreak; }; + 'fcy;' => { output->first = 0x0444; fbreak; }; + 'female;' => { output->first = 0x2640; fbreak; }; + 'ffilig;' => { output->first = 0xfb03; fbreak; }; + 'fflig;' => { output->first = 0xfb00; fbreak; }; + 'ffllig;' => { output->first = 0xfb04; fbreak; }; + 'ffr;' => { output->first = 0x0001d523; fbreak; }; + 'filig;' => { output->first = 0xfb01; fbreak; }; + 'fjlig;' => { output->first = 0x66; output->second = 0x6a; fbreak; }; + 'flat;' => { output->first = 0x266d; fbreak; }; + 'fllig;' => { output->first = 0xfb02; fbreak; }; + 'fltns;' => { output->first = 0x25b1; fbreak; }; + 'fnof;' => { output->first = 0x0192; fbreak; }; + 'fopf;' => { output->first = 0x0001d557; fbreak; }; + 'forall;' => { output->first = 0x2200; fbreak; }; + 'fork;' => { output->first = 0x22d4; fbreak; }; + 'forkv;' => { output->first = 0x2ad9; fbreak; }; + 'fpartint;' => { output->first = 0x2a0d; fbreak; }; + 'frac12;' => { output->first = 0xbd; fbreak; }; + 'frac12' => { output->first = 0xbd; fbreak; }; + 'frac13;' => { output->first = 0x2153; fbreak; }; + 'frac14;' => { output->first = 0xbc; fbreak; }; + 'frac14' => { output->first = 0xbc; fbreak; }; + 'frac15;' => { output->first = 0x2155; fbreak; }; + 'frac16;' => { output->first = 0x2159; fbreak; }; + 'frac18;' => { output->first = 0x215b; fbreak; }; + 'frac23;' => { output->first = 0x2154; fbreak; }; + 'frac25;' => { output->first = 0x2156; fbreak; }; + 'frac34;' => { output->first = 0xbe; fbreak; }; + 'frac34' => { output->first = 0xbe; fbreak; }; + 'frac35;' => { output->first = 0x2157; fbreak; }; + 'frac38;' => { output->first = 0x215c; fbreak; }; + 'frac45;' => { output->first = 0x2158; fbreak; }; + 'frac56;' => { output->first = 0x215a; fbreak; }; + 'frac58;' => { output->first = 0x215d; fbreak; }; + 'frac78;' => { output->first = 0x215e; fbreak; }; + 'frasl;' => { output->first = 0x2044; fbreak; }; + 'frown;' => { output->first = 0x2322; fbreak; }; + 'fscr;' => { output->first = 0x0001d4bb; fbreak; }; + 'gE;' => { output->first = 0x2267; fbreak; }; + 'gEl;' => { output->first = 0x2a8c; fbreak; }; + 'gacute;' => { output->first = 0x01f5; fbreak; }; + 'gamma;' => { output->first = 0x03b3; fbreak; }; + 'gammad;' => { output->first = 0x03dd; fbreak; }; + 'gap;' => { output->first = 0x2a86; fbreak; }; + 'gbreve;' => { output->first = 0x011f; fbreak; }; + 'gcirc;' => { output->first = 0x011d; fbreak; }; + 'gcy;' => { output->first = 0x0433; fbreak; }; + 'gdot;' => { output->first = 0x0121; fbreak; }; + 'ge;' => { output->first = 0x2265; fbreak; }; + 'gel;' => { output->first = 0x22db; fbreak; }; + 'geq;' => { output->first = 0x2265; fbreak; }; + 'geqq;' => { output->first = 0x2267; fbreak; }; + 'geqslant;' => { output->first = 0x2a7e; fbreak; }; + 'ges;' => { output->first = 0x2a7e; fbreak; }; + 'gescc;' => { output->first = 0x2aa9; fbreak; }; + 'gesdot;' => { output->first = 0x2a80; fbreak; }; + 'gesdoto;' => { output->first = 0x2a82; fbreak; }; + 'gesdotol;' => { output->first = 0x2a84; fbreak; }; + 'gesl;' => { output->first = 0x22db; output->second = 0xfe00; fbreak; }; + 'gesles;' => { output->first = 0x2a94; fbreak; }; + 'gfr;' => { output->first = 0x0001d524; fbreak; }; + 'gg;' => { output->first = 0x226b; fbreak; }; + 'ggg;' => { output->first = 0x22d9; fbreak; }; + 'gimel;' => { output->first = 0x2137; fbreak; }; + 'gjcy;' => { output->first = 0x0453; fbreak; }; + 'gl;' => { output->first = 0x2277; fbreak; }; + 'glE;' => { output->first = 0x2a92; fbreak; }; + 'gla;' => { output->first = 0x2aa5; fbreak; }; + 'glj;' => { output->first = 0x2aa4; fbreak; }; + 'gnE;' => { output->first = 0x2269; fbreak; }; + 'gnap;' => { output->first = 0x2a8a; fbreak; }; + 'gnapprox;' => { output->first = 0x2a8a; fbreak; }; + 'gne;' => { output->first = 0x2a88; fbreak; }; + 'gneq;' => { output->first = 0x2a88; fbreak; }; + 'gneqq;' => { output->first = 0x2269; fbreak; }; + 'gnsim;' => { output->first = 0x22e7; fbreak; }; + 'gopf;' => { output->first = 0x0001d558; fbreak; }; + 'grave;' => { output->first = 0x60; fbreak; }; + 'gscr;' => { output->first = 0x210a; fbreak; }; + 'gsim;' => { output->first = 0x2273; fbreak; }; + 'gsime;' => { output->first = 0x2a8e; fbreak; }; + 'gsiml;' => { output->first = 0x2a90; fbreak; }; + 'gt;' => { output->first = 0x3e; fbreak; }; + 'gt' => { output->first = 0x3e; fbreak; }; + 'gtcc;' => { output->first = 0x2aa7; fbreak; }; + 'gtcir;' => { output->first = 0x2a7a; fbreak; }; + 'gtdot;' => { output->first = 0x22d7; fbreak; }; + 'gtlPar;' => { output->first = 0x2995; fbreak; }; + 'gtquest;' => { output->first = 0x2a7c; fbreak; }; + 'gtrapprox;' => { output->first = 0x2a86; fbreak; }; + 'gtrarr;' => { output->first = 0x2978; fbreak; }; + 'gtrdot;' => { output->first = 0x22d7; fbreak; }; + 'gtreqless;' => { output->first = 0x22db; fbreak; }; + 'gtreqqless;' => { output->first = 0x2a8c; fbreak; }; + 'gtrless;' => { output->first = 0x2277; fbreak; }; + 'gtrsim;' => { output->first = 0x2273; fbreak; }; + 'gvertneqq;' => { output->first = 0x2269; output->second = 0xfe00; fbreak; }; + 'gvnE;' => { output->first = 0x2269; output->second = 0xfe00; fbreak; }; + 'hArr;' => { output->first = 0x21d4; fbreak; }; + 'hairsp;' => { output->first = 0x200a; fbreak; }; + 'half;' => { output->first = 0xbd; fbreak; }; + 'hamilt;' => { output->first = 0x210b; fbreak; }; + 'hardcy;' => { output->first = 0x044a; fbreak; }; + 'harr;' => { output->first = 0x2194; fbreak; }; + 'harrcir;' => { output->first = 0x2948; fbreak; }; + 'harrw;' => { output->first = 0x21ad; fbreak; }; + 'hbar;' => { output->first = 0x210f; fbreak; }; + 'hcirc;' => { output->first = 0x0125; fbreak; }; + 'hearts;' => { output->first = 0x2665; fbreak; }; + 'heartsuit;' => { output->first = 0x2665; fbreak; }; + 'hellip;' => { output->first = 0x2026; fbreak; }; + 'hercon;' => { output->first = 0x22b9; fbreak; }; + 'hfr;' => { output->first = 0x0001d525; fbreak; }; + 'hksearow;' => { output->first = 0x2925; fbreak; }; + 'hkswarow;' => { output->first = 0x2926; fbreak; }; + 'hoarr;' => { output->first = 0x21ff; fbreak; }; + 'homtht;' => { output->first = 0x223b; fbreak; }; + 'hookleftarrow;' => { output->first = 0x21a9; fbreak; }; + 'hookrightarrow;' => { output->first = 0x21aa; fbreak; }; + 'hopf;' => { output->first = 0x0001d559; fbreak; }; + 'horbar;' => { output->first = 0x2015; fbreak; }; + 'hscr;' => { output->first = 0x0001d4bd; fbreak; }; + 'hslash;' => { output->first = 0x210f; fbreak; }; + 'hstrok;' => { output->first = 0x0127; fbreak; }; + 'hybull;' => { output->first = 0x2043; fbreak; }; + 'hyphen;' => { output->first = 0x2010; fbreak; }; + 'iacute;' => { output->first = 0xed; fbreak; }; + 'iacute' => { output->first = 0xed; fbreak; }; + 'ic;' => { output->first = 0x2063; fbreak; }; + 'icirc;' => { output->first = 0xee; fbreak; }; + 'icirc' => { output->first = 0xee; fbreak; }; + 'icy;' => { output->first = 0x0438; fbreak; }; + 'iecy;' => { output->first = 0x0435; fbreak; }; + 'iexcl;' => { output->first = 0xa1; fbreak; }; + 'iexcl' => { output->first = 0xa1; fbreak; }; + 'iff;' => { output->first = 0x21d4; fbreak; }; + 'ifr;' => { output->first = 0x0001d526; fbreak; }; + 'igrave;' => { output->first = 0xec; fbreak; }; + 'igrave' => { output->first = 0xec; fbreak; }; + 'ii;' => { output->first = 0x2148; fbreak; }; + 'iiiint;' => { output->first = 0x2a0c; fbreak; }; + 'iiint;' => { output->first = 0x222d; fbreak; }; + 'iinfin;' => { output->first = 0x29dc; fbreak; }; + 'iiota;' => { output->first = 0x2129; fbreak; }; + 'ijlig;' => { output->first = 0x0133; fbreak; }; + 'imacr;' => { output->first = 0x012b; fbreak; }; + 'image;' => { output->first = 0x2111; fbreak; }; + 'imagline;' => { output->first = 0x2110; fbreak; }; + 'imagpart;' => { output->first = 0x2111; fbreak; }; + 'imath;' => { output->first = 0x0131; fbreak; }; + 'imof;' => { output->first = 0x22b7; fbreak; }; + 'imped;' => { output->first = 0x01b5; fbreak; }; + 'in;' => { output->first = 0x2208; fbreak; }; + 'incare;' => { output->first = 0x2105; fbreak; }; + 'infin;' => { output->first = 0x221e; fbreak; }; + 'infintie;' => { output->first = 0x29dd; fbreak; }; + 'inodot;' => { output->first = 0x0131; fbreak; }; + 'int;' => { output->first = 0x222b; fbreak; }; + 'intcal;' => { output->first = 0x22ba; fbreak; }; + 'integers;' => { output->first = 0x2124; fbreak; }; + 'intercal;' => { output->first = 0x22ba; fbreak; }; + 'intlarhk;' => { output->first = 0x2a17; fbreak; }; + 'intprod;' => { output->first = 0x2a3c; fbreak; }; + 'iocy;' => { output->first = 0x0451; fbreak; }; + 'iogon;' => { output->first = 0x012f; fbreak; }; + 'iopf;' => { output->first = 0x0001d55a; fbreak; }; + 'iota;' => { output->first = 0x03b9; fbreak; }; + 'iprod;' => { output->first = 0x2a3c; fbreak; }; + 'iquest;' => { output->first = 0xbf; fbreak; }; + 'iquest' => { output->first = 0xbf; fbreak; }; + 'iscr;' => { output->first = 0x0001d4be; fbreak; }; + 'isin;' => { output->first = 0x2208; fbreak; }; + 'isinE;' => { output->first = 0x22f9; fbreak; }; + 'isindot;' => { output->first = 0x22f5; fbreak; }; + 'isins;' => { output->first = 0x22f4; fbreak; }; + 'isinsv;' => { output->first = 0x22f3; fbreak; }; + 'isinv;' => { output->first = 0x2208; fbreak; }; + 'it;' => { output->first = 0x2062; fbreak; }; + 'itilde;' => { output->first = 0x0129; fbreak; }; + 'iukcy;' => { output->first = 0x0456; fbreak; }; + 'iuml;' => { output->first = 0xef; fbreak; }; + 'iuml' => { output->first = 0xef; fbreak; }; + 'jcirc;' => { output->first = 0x0135; fbreak; }; + 'jcy;' => { output->first = 0x0439; fbreak; }; + 'jfr;' => { output->first = 0x0001d527; fbreak; }; + 'jmath;' => { output->first = 0x0237; fbreak; }; + 'jopf;' => { output->first = 0x0001d55b; fbreak; }; + 'jscr;' => { output->first = 0x0001d4bf; fbreak; }; + 'jsercy;' => { output->first = 0x0458; fbreak; }; + 'jukcy;' => { output->first = 0x0454; fbreak; }; + 'kappa;' => { output->first = 0x03ba; fbreak; }; + 'kappav;' => { output->first = 0x03f0; fbreak; }; + 'kcedil;' => { output->first = 0x0137; fbreak; }; + 'kcy;' => { output->first = 0x043a; fbreak; }; + 'kfr;' => { output->first = 0x0001d528; fbreak; }; + 'kgreen;' => { output->first = 0x0138; fbreak; }; + 'khcy;' => { output->first = 0x0445; fbreak; }; + 'kjcy;' => { output->first = 0x045c; fbreak; }; + 'kopf;' => { output->first = 0x0001d55c; fbreak; }; + 'kscr;' => { output->first = 0x0001d4c0; fbreak; }; + 'lAarr;' => { output->first = 0x21da; fbreak; }; + 'lArr;' => { output->first = 0x21d0; fbreak; }; + 'lAtail;' => { output->first = 0x291b; fbreak; }; + 'lBarr;' => { output->first = 0x290e; fbreak; }; + 'lE;' => { output->first = 0x2266; fbreak; }; + 'lEg;' => { output->first = 0x2a8b; fbreak; }; + 'lHar;' => { output->first = 0x2962; fbreak; }; + 'lacute;' => { output->first = 0x013a; fbreak; }; + 'laemptyv;' => { output->first = 0x29b4; fbreak; }; + 'lagran;' => { output->first = 0x2112; fbreak; }; + 'lambda;' => { output->first = 0x03bb; fbreak; }; + 'lang;' => { output->first = 0x27e8; fbreak; }; + 'langd;' => { output->first = 0x2991; fbreak; }; + 'langle;' => { output->first = 0x27e8; fbreak; }; + 'lap;' => { output->first = 0x2a85; fbreak; }; + 'laquo;' => { output->first = 0xab; fbreak; }; + 'laquo' => { output->first = 0xab; fbreak; }; + 'larr;' => { output->first = 0x2190; fbreak; }; + 'larrb;' => { output->first = 0x21e4; fbreak; }; + 'larrbfs;' => { output->first = 0x291f; fbreak; }; + 'larrfs;' => { output->first = 0x291d; fbreak; }; + 'larrhk;' => { output->first = 0x21a9; fbreak; }; + 'larrlp;' => { output->first = 0x21ab; fbreak; }; + 'larrpl;' => { output->first = 0x2939; fbreak; }; + 'larrsim;' => { output->first = 0x2973; fbreak; }; + 'larrtl;' => { output->first = 0x21a2; fbreak; }; + 'lat;' => { output->first = 0x2aab; fbreak; }; + 'latail;' => { output->first = 0x2919; fbreak; }; + 'late;' => { output->first = 0x2aad; fbreak; }; + 'lates;' => { output->first = 0x2aad; output->second = 0xfe00; fbreak; }; + 'lbarr;' => { output->first = 0x290c; fbreak; }; + 'lbbrk;' => { output->first = 0x2772; fbreak; }; + 'lbrace;' => { output->first = 0x7b; fbreak; }; + 'lbrack;' => { output->first = 0x5b; fbreak; }; + 'lbrke;' => { output->first = 0x298b; fbreak; }; + 'lbrksld;' => { output->first = 0x298f; fbreak; }; + 'lbrkslu;' => { output->first = 0x298d; fbreak; }; + 'lcaron;' => { output->first = 0x013e; fbreak; }; + 'lcedil;' => { output->first = 0x013c; fbreak; }; + 'lceil;' => { output->first = 0x2308; fbreak; }; + 'lcub;' => { output->first = 0x7b; fbreak; }; + 'lcy;' => { output->first = 0x043b; fbreak; }; + 'ldca;' => { output->first = 0x2936; fbreak; }; + 'ldquo;' => { output->first = 0x201c; fbreak; }; + 'ldquor;' => { output->first = 0x201e; fbreak; }; + 'ldrdhar;' => { output->first = 0x2967; fbreak; }; + 'ldrushar;' => { output->first = 0x294b; fbreak; }; + 'ldsh;' => { output->first = 0x21b2; fbreak; }; + 'le;' => { output->first = 0x2264; fbreak; }; + 'leftarrow;' => { output->first = 0x2190; fbreak; }; + 'leftarrowtail;' => { output->first = 0x21a2; fbreak; }; + 'leftharpoondown;' => { output->first = 0x21bd; fbreak; }; + 'leftharpoonup;' => { output->first = 0x21bc; fbreak; }; + 'leftleftarrows;' => { output->first = 0x21c7; fbreak; }; + 'leftrightarrow;' => { output->first = 0x2194; fbreak; }; + 'leftrightarrows;' => { output->first = 0x21c6; fbreak; }; + 'leftrightharpoons;' => { output->first = 0x21cb; fbreak; }; + 'leftrightsquigarrow;' => { output->first = 0x21ad; fbreak; }; + 'leftthreetimes;' => { output->first = 0x22cb; fbreak; }; + 'leg;' => { output->first = 0x22da; fbreak; }; + 'leq;' => { output->first = 0x2264; fbreak; }; + 'leqq;' => { output->first = 0x2266; fbreak; }; + 'leqslant;' => { output->first = 0x2a7d; fbreak; }; + 'les;' => { output->first = 0x2a7d; fbreak; }; + 'lescc;' => { output->first = 0x2aa8; fbreak; }; + 'lesdot;' => { output->first = 0x2a7f; fbreak; }; + 'lesdoto;' => { output->first = 0x2a81; fbreak; }; + 'lesdotor;' => { output->first = 0x2a83; fbreak; }; + 'lesg;' => { output->first = 0x22da; output->second = 0xfe00; fbreak; }; + 'lesges;' => { output->first = 0x2a93; fbreak; }; + 'lessapprox;' => { output->first = 0x2a85; fbreak; }; + 'lessdot;' => { output->first = 0x22d6; fbreak; }; + 'lesseqgtr;' => { output->first = 0x22da; fbreak; }; + 'lesseqqgtr;' => { output->first = 0x2a8b; fbreak; }; + 'lessgtr;' => { output->first = 0x2276; fbreak; }; + 'lesssim;' => { output->first = 0x2272; fbreak; }; + 'lfisht;' => { output->first = 0x297c; fbreak; }; + 'lfloor;' => { output->first = 0x230a; fbreak; }; + 'lfr;' => { output->first = 0x0001d529; fbreak; }; + 'lg;' => { output->first = 0x2276; fbreak; }; + 'lgE;' => { output->first = 0x2a91; fbreak; }; + 'lhard;' => { output->first = 0x21bd; fbreak; }; + 'lharu;' => { output->first = 0x21bc; fbreak; }; + 'lharul;' => { output->first = 0x296a; fbreak; }; + 'lhblk;' => { output->first = 0x2584; fbreak; }; + 'ljcy;' => { output->first = 0x0459; fbreak; }; + 'll;' => { output->first = 0x226a; fbreak; }; + 'llarr;' => { output->first = 0x21c7; fbreak; }; + 'llcorner;' => { output->first = 0x231e; fbreak; }; + 'llhard;' => { output->first = 0x296b; fbreak; }; + 'lltri;' => { output->first = 0x25fa; fbreak; }; + 'lmidot;' => { output->first = 0x0140; fbreak; }; + 'lmoust;' => { output->first = 0x23b0; fbreak; }; + 'lmoustache;' => { output->first = 0x23b0; fbreak; }; + 'lnE;' => { output->first = 0x2268; fbreak; }; + 'lnap;' => { output->first = 0x2a89; fbreak; }; + 'lnapprox;' => { output->first = 0x2a89; fbreak; }; + 'lne;' => { output->first = 0x2a87; fbreak; }; + 'lneq;' => { output->first = 0x2a87; fbreak; }; + 'lneqq;' => { output->first = 0x2268; fbreak; }; + 'lnsim;' => { output->first = 0x22e6; fbreak; }; + 'loang;' => { output->first = 0x27ec; fbreak; }; + 'loarr;' => { output->first = 0x21fd; fbreak; }; + 'lobrk;' => { output->first = 0x27e6; fbreak; }; + 'longleftarrow;' => { output->first = 0x27f5; fbreak; }; + 'longleftrightarrow;' => { output->first = 0x27f7; fbreak; }; + 'longmapsto;' => { output->first = 0x27fc; fbreak; }; + 'longrightarrow;' => { output->first = 0x27f6; fbreak; }; + 'looparrowleft;' => { output->first = 0x21ab; fbreak; }; + 'looparrowright;' => { output->first = 0x21ac; fbreak; }; + 'lopar;' => { output->first = 0x2985; fbreak; }; + 'lopf;' => { output->first = 0x0001d55d; fbreak; }; + 'loplus;' => { output->first = 0x2a2d; fbreak; }; + 'lotimes;' => { output->first = 0x2a34; fbreak; }; + 'lowast;' => { output->first = 0x2217; fbreak; }; + 'lowbar;' => { output->first = 0x5f; fbreak; }; + 'loz;' => { output->first = 0x25ca; fbreak; }; + 'lozenge;' => { output->first = 0x25ca; fbreak; }; + 'lozf;' => { output->first = 0x29eb; fbreak; }; + 'lpar;' => { output->first = 0x28; fbreak; }; + 'lparlt;' => { output->first = 0x2993; fbreak; }; + 'lrarr;' => { output->first = 0x21c6; fbreak; }; + 'lrcorner;' => { output->first = 0x231f; fbreak; }; + 'lrhar;' => { output->first = 0x21cb; fbreak; }; + 'lrhard;' => { output->first = 0x296d; fbreak; }; + 'lrm;' => { output->first = 0x200e; fbreak; }; + 'lrtri;' => { output->first = 0x22bf; fbreak; }; + 'lsaquo;' => { output->first = 0x2039; fbreak; }; + 'lscr;' => { output->first = 0x0001d4c1; fbreak; }; + 'lsh;' => { output->first = 0x21b0; fbreak; }; + 'lsim;' => { output->first = 0x2272; fbreak; }; + 'lsime;' => { output->first = 0x2a8d; fbreak; }; + 'lsimg;' => { output->first = 0x2a8f; fbreak; }; + 'lsqb;' => { output->first = 0x5b; fbreak; }; + 'lsquo;' => { output->first = 0x2018; fbreak; }; + 'lsquor;' => { output->first = 0x201a; fbreak; }; + 'lstrok;' => { output->first = 0x0142; fbreak; }; + 'lt;' => { output->first = 0x3c; fbreak; }; + 'lt' => { output->first = 0x3c; fbreak; }; + 'ltcc;' => { output->first = 0x2aa6; fbreak; }; + 'ltcir;' => { output->first = 0x2a79; fbreak; }; + 'ltdot;' => { output->first = 0x22d6; fbreak; }; + 'lthree;' => { output->first = 0x22cb; fbreak; }; + 'ltimes;' => { output->first = 0x22c9; fbreak; }; + 'ltlarr;' => { output->first = 0x2976; fbreak; }; + 'ltquest;' => { output->first = 0x2a7b; fbreak; }; + 'ltrPar;' => { output->first = 0x2996; fbreak; }; + 'ltri;' => { output->first = 0x25c3; fbreak; }; + 'ltrie;' => { output->first = 0x22b4; fbreak; }; + 'ltrif;' => { output->first = 0x25c2; fbreak; }; + 'lurdshar;' => { output->first = 0x294a; fbreak; }; + 'luruhar;' => { output->first = 0x2966; fbreak; }; + 'lvertneqq;' => { output->first = 0x2268; output->second = 0xfe00; fbreak; }; + 'lvnE;' => { output->first = 0x2268; output->second = 0xfe00; fbreak; }; + 'mDDot;' => { output->first = 0x223a; fbreak; }; + 'macr;' => { output->first = 0xaf; fbreak; }; + 'macr' => { output->first = 0xaf; fbreak; }; + 'male;' => { output->first = 0x2642; fbreak; }; + 'malt;' => { output->first = 0x2720; fbreak; }; + 'maltese;' => { output->first = 0x2720; fbreak; }; + 'map;' => { output->first = 0x21a6; fbreak; }; + 'mapsto;' => { output->first = 0x21a6; fbreak; }; + 'mapstodown;' => { output->first = 0x21a7; fbreak; }; + 'mapstoleft;' => { output->first = 0x21a4; fbreak; }; + 'mapstoup;' => { output->first = 0x21a5; fbreak; }; + 'marker;' => { output->first = 0x25ae; fbreak; }; + 'mcomma;' => { output->first = 0x2a29; fbreak; }; + 'mcy;' => { output->first = 0x043c; fbreak; }; + 'mdash;' => { output->first = 0x2014; fbreak; }; + 'measuredangle;' => { output->first = 0x2221; fbreak; }; + 'mfr;' => { output->first = 0x0001d52a; fbreak; }; + 'mho;' => { output->first = 0x2127; fbreak; }; + 'micro;' => { output->first = 0xb5; fbreak; }; + 'micro' => { output->first = 0xb5; fbreak; }; + 'mid;' => { output->first = 0x2223; fbreak; }; + 'midast;' => { output->first = 0x2a; fbreak; }; + 'midcir;' => { output->first = 0x2af0; fbreak; }; + 'middot;' => { output->first = 0xb7; fbreak; }; + 'middot' => { output->first = 0xb7; fbreak; }; + 'minus;' => { output->first = 0x2212; fbreak; }; + 'minusb;' => { output->first = 0x229f; fbreak; }; + 'minusd;' => { output->first = 0x2238; fbreak; }; + 'minusdu;' => { output->first = 0x2a2a; fbreak; }; + 'mlcp;' => { output->first = 0x2adb; fbreak; }; + 'mldr;' => { output->first = 0x2026; fbreak; }; + 'mnplus;' => { output->first = 0x2213; fbreak; }; + 'models;' => { output->first = 0x22a7; fbreak; }; + 'mopf;' => { output->first = 0x0001d55e; fbreak; }; + 'mp;' => { output->first = 0x2213; fbreak; }; + 'mscr;' => { output->first = 0x0001d4c2; fbreak; }; + 'mstpos;' => { output->first = 0x223e; fbreak; }; + 'mu;' => { output->first = 0x03bc; fbreak; }; + 'multimap;' => { output->first = 0x22b8; fbreak; }; + 'mumap;' => { output->first = 0x22b8; fbreak; }; + 'nGg;' => { output->first = 0x22d9; output->second = 0x0338; fbreak; }; + 'nGt;' => { output->first = 0x226b; output->second = 0x20d2; fbreak; }; + 'nGtv;' => { output->first = 0x226b; output->second = 0x0338; fbreak; }; + 'nLeftarrow;' => { output->first = 0x21cd; fbreak; }; + 'nLeftrightarrow;' => { output->first = 0x21ce; fbreak; }; + 'nLl;' => { output->first = 0x22d8; output->second = 0x0338; fbreak; }; + 'nLt;' => { output->first = 0x226a; output->second = 0x20d2; fbreak; }; + 'nLtv;' => { output->first = 0x226a; output->second = 0x0338; fbreak; }; + 'nRightarrow;' => { output->first = 0x21cf; fbreak; }; + 'nVDash;' => { output->first = 0x22af; fbreak; }; + 'nVdash;' => { output->first = 0x22ae; fbreak; }; + 'nabla;' => { output->first = 0x2207; fbreak; }; + 'nacute;' => { output->first = 0x0144; fbreak; }; + 'nang;' => { output->first = 0x2220; output->second = 0x20d2; fbreak; }; + 'nap;' => { output->first = 0x2249; fbreak; }; + 'napE;' => { output->first = 0x2a70; output->second = 0x0338; fbreak; }; + 'napid;' => { output->first = 0x224b; output->second = 0x0338; fbreak; }; + 'napos;' => { output->first = 0x0149; fbreak; }; + 'napprox;' => { output->first = 0x2249; fbreak; }; + 'natur;' => { output->first = 0x266e; fbreak; }; + 'natural;' => { output->first = 0x266e; fbreak; }; + 'naturals;' => { output->first = 0x2115; fbreak; }; + 'nbsp;' => { output->first = 0xa0; fbreak; }; + 'nbsp' => { output->first = 0xa0; fbreak; }; + 'nbump;' => { output->first = 0x224e; output->second = 0x0338; fbreak; }; + 'nbumpe;' => { output->first = 0x224f; output->second = 0x0338; fbreak; }; + 'ncap;' => { output->first = 0x2a43; fbreak; }; + 'ncaron;' => { output->first = 0x0148; fbreak; }; + 'ncedil;' => { output->first = 0x0146; fbreak; }; + 'ncong;' => { output->first = 0x2247; fbreak; }; + 'ncongdot;' => { output->first = 0x2a6d; output->second = 0x0338; fbreak; }; + 'ncup;' => { output->first = 0x2a42; fbreak; }; + 'ncy;' => { output->first = 0x043d; fbreak; }; + 'ndash;' => { output->first = 0x2013; fbreak; }; + 'ne;' => { output->first = 0x2260; fbreak; }; + 'neArr;' => { output->first = 0x21d7; fbreak; }; + 'nearhk;' => { output->first = 0x2924; fbreak; }; + 'nearr;' => { output->first = 0x2197; fbreak; }; + 'nearrow;' => { output->first = 0x2197; fbreak; }; + 'nedot;' => { output->first = 0x2250; output->second = 0x0338; fbreak; }; + 'nequiv;' => { output->first = 0x2262; fbreak; }; + 'nesear;' => { output->first = 0x2928; fbreak; }; + 'nesim;' => { output->first = 0x2242; output->second = 0x0338; fbreak; }; + 'nexist;' => { output->first = 0x2204; fbreak; }; + 'nexists;' => { output->first = 0x2204; fbreak; }; + 'nfr;' => { output->first = 0x0001d52b; fbreak; }; + 'ngE;' => { output->first = 0x2267; output->second = 0x0338; fbreak; }; + 'nge;' => { output->first = 0x2271; fbreak; }; + 'ngeq;' => { output->first = 0x2271; fbreak; }; + 'ngeqq;' => { output->first = 0x2267; output->second = 0x0338; fbreak; }; + 'ngeqslant;' => { output->first = 0x2a7e; output->second = 0x0338; fbreak; }; + 'nges;' => { output->first = 0x2a7e; output->second = 0x0338; fbreak; }; + 'ngsim;' => { output->first = 0x2275; fbreak; }; + 'ngt;' => { output->first = 0x226f; fbreak; }; + 'ngtr;' => { output->first = 0x226f; fbreak; }; + 'nhArr;' => { output->first = 0x21ce; fbreak; }; + 'nharr;' => { output->first = 0x21ae; fbreak; }; + 'nhpar;' => { output->first = 0x2af2; fbreak; }; + 'ni;' => { output->first = 0x220b; fbreak; }; + 'nis;' => { output->first = 0x22fc; fbreak; }; + 'nisd;' => { output->first = 0x22fa; fbreak; }; + 'niv;' => { output->first = 0x220b; fbreak; }; + 'njcy;' => { output->first = 0x045a; fbreak; }; + 'nlArr;' => { output->first = 0x21cd; fbreak; }; + 'nlE;' => { output->first = 0x2266; output->second = 0x0338; fbreak; }; + 'nlarr;' => { output->first = 0x219a; fbreak; }; + 'nldr;' => { output->first = 0x2025; fbreak; }; + 'nle;' => { output->first = 0x2270; fbreak; }; + 'nleftarrow;' => { output->first = 0x219a; fbreak; }; + 'nleftrightarrow;' => { output->first = 0x21ae; fbreak; }; + 'nleq;' => { output->first = 0x2270; fbreak; }; + 'nleqq;' => { output->first = 0x2266; output->second = 0x0338; fbreak; }; + 'nleqslant;' => { output->first = 0x2a7d; output->second = 0x0338; fbreak; }; + 'nles;' => { output->first = 0x2a7d; output->second = 0x0338; fbreak; }; + 'nless;' => { output->first = 0x226e; fbreak; }; + 'nlsim;' => { output->first = 0x2274; fbreak; }; + 'nlt;' => { output->first = 0x226e; fbreak; }; + 'nltri;' => { output->first = 0x22ea; fbreak; }; + 'nltrie;' => { output->first = 0x22ec; fbreak; }; + 'nmid;' => { output->first = 0x2224; fbreak; }; + 'nopf;' => { output->first = 0x0001d55f; fbreak; }; + 'not;' => { output->first = 0xac; fbreak; }; + 'notin;' => { output->first = 0x2209; fbreak; }; + 'notinE;' => { output->first = 0x22f9; output->second = 0x0338; fbreak; }; + 'notindot;' => { output->first = 0x22f5; output->second = 0x0338; fbreak; }; + 'notinva;' => { output->first = 0x2209; fbreak; }; + 'notinvb;' => { output->first = 0x22f7; fbreak; }; + 'notinvc;' => { output->first = 0x22f6; fbreak; }; + 'notni;' => { output->first = 0x220c; fbreak; }; + 'notniva;' => { output->first = 0x220c; fbreak; }; + 'notnivb;' => { output->first = 0x22fe; fbreak; }; + 'notnivc;' => { output->first = 0x22fd; fbreak; }; + 'not' => { output->first = 0xac; fbreak; }; + 'npar;' => { output->first = 0x2226; fbreak; }; + 'nparallel;' => { output->first = 0x2226; fbreak; }; + 'nparsl;' => { output->first = 0x2afd; output->second = 0x20e5; fbreak; }; + 'npart;' => { output->first = 0x2202; output->second = 0x0338; fbreak; }; + 'npolint;' => { output->first = 0x2a14; fbreak; }; + 'npr;' => { output->first = 0x2280; fbreak; }; + 'nprcue;' => { output->first = 0x22e0; fbreak; }; + 'npre;' => { output->first = 0x2aaf; output->second = 0x0338; fbreak; }; + 'nprec;' => { output->first = 0x2280; fbreak; }; + 'npreceq;' => { output->first = 0x2aaf; output->second = 0x0338; fbreak; }; + 'nrArr;' => { output->first = 0x21cf; fbreak; }; + 'nrarr;' => { output->first = 0x219b; fbreak; }; + 'nrarrc;' => { output->first = 0x2933; output->second = 0x0338; fbreak; }; + 'nrarrw;' => { output->first = 0x219d; output->second = 0x0338; fbreak; }; + 'nrightarrow;' => { output->first = 0x219b; fbreak; }; + 'nrtri;' => { output->first = 0x22eb; fbreak; }; + 'nrtrie;' => { output->first = 0x22ed; fbreak; }; + 'nsc;' => { output->first = 0x2281; fbreak; }; + 'nsccue;' => { output->first = 0x22e1; fbreak; }; + 'nsce;' => { output->first = 0x2ab0; output->second = 0x0338; fbreak; }; + 'nscr;' => { output->first = 0x0001d4c3; fbreak; }; + 'nshortmid;' => { output->first = 0x2224; fbreak; }; + 'nshortparallel;' => { output->first = 0x2226; fbreak; }; + 'nsim;' => { output->first = 0x2241; fbreak; }; + 'nsime;' => { output->first = 0x2244; fbreak; }; + 'nsimeq;' => { output->first = 0x2244; fbreak; }; + 'nsmid;' => { output->first = 0x2224; fbreak; }; + 'nspar;' => { output->first = 0x2226; fbreak; }; + 'nsqsube;' => { output->first = 0x22e2; fbreak; }; + 'nsqsupe;' => { output->first = 0x22e3; fbreak; }; + 'nsub;' => { output->first = 0x2284; fbreak; }; + 'nsubE;' => { output->first = 0x2ac5; output->second = 0x0338; fbreak; }; + 'nsube;' => { output->first = 0x2288; fbreak; }; + 'nsubset;' => { output->first = 0x2282; output->second = 0x20d2; fbreak; }; + 'nsubseteq;' => { output->first = 0x2288; fbreak; }; + 'nsubseteqq;' => { output->first = 0x2ac5; output->second = 0x0338; fbreak; }; + 'nsucc;' => { output->first = 0x2281; fbreak; }; + 'nsucceq;' => { output->first = 0x2ab0; output->second = 0x0338; fbreak; }; + 'nsup;' => { output->first = 0x2285; fbreak; }; + 'nsupE;' => { output->first = 0x2ac6; output->second = 0x0338; fbreak; }; + 'nsupe;' => { output->first = 0x2289; fbreak; }; + 'nsupset;' => { output->first = 0x2283; output->second = 0x20d2; fbreak; }; + 'nsupseteq;' => { output->first = 0x2289; fbreak; }; + 'nsupseteqq;' => { output->first = 0x2ac6; output->second = 0x0338; fbreak; }; + 'ntgl;' => { output->first = 0x2279; fbreak; }; + 'ntilde;' => { output->first = 0xf1; fbreak; }; + 'ntilde' => { output->first = 0xf1; fbreak; }; + 'ntlg;' => { output->first = 0x2278; fbreak; }; + 'ntriangleleft;' => { output->first = 0x22ea; fbreak; }; + 'ntrianglelefteq;' => { output->first = 0x22ec; fbreak; }; + 'ntriangleright;' => { output->first = 0x22eb; fbreak; }; + 'ntrianglerighteq;' => { output->first = 0x22ed; fbreak; }; + 'nu;' => { output->first = 0x03bd; fbreak; }; + 'num;' => { output->first = 0x23; fbreak; }; + 'numero;' => { output->first = 0x2116; fbreak; }; + 'numsp;' => { output->first = 0x2007; fbreak; }; + 'nvDash;' => { output->first = 0x22ad; fbreak; }; + 'nvHarr;' => { output->first = 0x2904; fbreak; }; + 'nvap;' => { output->first = 0x224d; output->second = 0x20d2; fbreak; }; + 'nvdash;' => { output->first = 0x22ac; fbreak; }; + 'nvge;' => { output->first = 0x2265; output->second = 0x20d2; fbreak; }; + 'nvgt;' => { output->first = 0x3e; output->second = 0x20d2; fbreak; }; + 'nvinfin;' => { output->first = 0x29de; fbreak; }; + 'nvlArr;' => { output->first = 0x2902; fbreak; }; + 'nvle;' => { output->first = 0x2264; output->second = 0x20d2; fbreak; }; + 'nvlt;' => { output->first = 0x3c; output->second = 0x20d2; fbreak; }; + 'nvltrie;' => { output->first = 0x22b4; output->second = 0x20d2; fbreak; }; + 'nvrArr;' => { output->first = 0x2903; fbreak; }; + 'nvrtrie;' => { output->first = 0x22b5; output->second = 0x20d2; fbreak; }; + 'nvsim;' => { output->first = 0x223c; output->second = 0x20d2; fbreak; }; + 'nwArr;' => { output->first = 0x21d6; fbreak; }; + 'nwarhk;' => { output->first = 0x2923; fbreak; }; + 'nwarr;' => { output->first = 0x2196; fbreak; }; + 'nwarrow;' => { output->first = 0x2196; fbreak; }; + 'nwnear;' => { output->first = 0x2927; fbreak; }; + 'oS;' => { output->first = 0x24c8; fbreak; }; + 'oacute;' => { output->first = 0xf3; fbreak; }; + 'oacute' => { output->first = 0xf3; fbreak; }; + 'oast;' => { output->first = 0x229b; fbreak; }; + 'ocir;' => { output->first = 0x229a; fbreak; }; + 'ocirc;' => { output->first = 0xf4; fbreak; }; + 'ocirc' => { output->first = 0xf4; fbreak; }; + 'ocy;' => { output->first = 0x043e; fbreak; }; + 'odash;' => { output->first = 0x229d; fbreak; }; + 'odblac;' => { output->first = 0x0151; fbreak; }; + 'odiv;' => { output->first = 0x2a38; fbreak; }; + 'odot;' => { output->first = 0x2299; fbreak; }; + 'odsold;' => { output->first = 0x29bc; fbreak; }; + 'oelig;' => { output->first = 0x0153; fbreak; }; + 'ofcir;' => { output->first = 0x29bf; fbreak; }; + 'ofr;' => { output->first = 0x0001d52c; fbreak; }; + 'ogon;' => { output->first = 0x02db; fbreak; }; + 'ograve;' => { output->first = 0xf2; fbreak; }; + 'ograve' => { output->first = 0xf2; fbreak; }; + 'ogt;' => { output->first = 0x29c1; fbreak; }; + 'ohbar;' => { output->first = 0x29b5; fbreak; }; + 'ohm;' => { output->first = 0x03a9; fbreak; }; + 'oint;' => { output->first = 0x222e; fbreak; }; + 'olarr;' => { output->first = 0x21ba; fbreak; }; + 'olcir;' => { output->first = 0x29be; fbreak; }; + 'olcross;' => { output->first = 0x29bb; fbreak; }; + 'oline;' => { output->first = 0x203e; fbreak; }; + 'olt;' => { output->first = 0x29c0; fbreak; }; + 'omacr;' => { output->first = 0x014d; fbreak; }; + 'omega;' => { output->first = 0x03c9; fbreak; }; + 'omicron;' => { output->first = 0x03bf; fbreak; }; + 'omid;' => { output->first = 0x29b6; fbreak; }; + 'ominus;' => { output->first = 0x2296; fbreak; }; + 'oopf;' => { output->first = 0x0001d560; fbreak; }; + 'opar;' => { output->first = 0x29b7; fbreak; }; + 'operp;' => { output->first = 0x29b9; fbreak; }; + 'oplus;' => { output->first = 0x2295; fbreak; }; + 'or;' => { output->first = 0x2228; fbreak; }; + 'orarr;' => { output->first = 0x21bb; fbreak; }; + 'ord;' => { output->first = 0x2a5d; fbreak; }; + 'order;' => { output->first = 0x2134; fbreak; }; + 'orderof;' => { output->first = 0x2134; fbreak; }; + 'ordf;' => { output->first = 0xaa; fbreak; }; + 'ordf' => { output->first = 0xaa; fbreak; }; + 'ordm;' => { output->first = 0xba; fbreak; }; + 'ordm' => { output->first = 0xba; fbreak; }; + 'origof;' => { output->first = 0x22b6; fbreak; }; + 'oror;' => { output->first = 0x2a56; fbreak; }; + 'orslope;' => { output->first = 0x2a57; fbreak; }; + 'orv;' => { output->first = 0x2a5b; fbreak; }; + 'oscr;' => { output->first = 0x2134; fbreak; }; + 'oslash;' => { output->first = 0xf8; fbreak; }; + 'oslash' => { output->first = 0xf8; fbreak; }; + 'osol;' => { output->first = 0x2298; fbreak; }; + 'otilde;' => { output->first = 0xf5; fbreak; }; + 'otilde' => { output->first = 0xf5; fbreak; }; + 'otimes;' => { output->first = 0x2297; fbreak; }; + 'otimesas;' => { output->first = 0x2a36; fbreak; }; + 'ouml;' => { output->first = 0xf6; fbreak; }; + 'ouml' => { output->first = 0xf6; fbreak; }; + 'ovbar;' => { output->first = 0x233d; fbreak; }; + 'par;' => { output->first = 0x2225; fbreak; }; + 'para;' => { output->first = 0xb6; fbreak; }; + 'para' => { output->first = 0xb6; fbreak; }; + 'parallel;' => { output->first = 0x2225; fbreak; }; + 'parsim;' => { output->first = 0x2af3; fbreak; }; + 'parsl;' => { output->first = 0x2afd; fbreak; }; + 'part;' => { output->first = 0x2202; fbreak; }; + 'pcy;' => { output->first = 0x043f; fbreak; }; + 'percnt;' => { output->first = 0x25; fbreak; }; + 'period;' => { output->first = 0x2e; fbreak; }; + 'permil;' => { output->first = 0x2030; fbreak; }; + 'perp;' => { output->first = 0x22a5; fbreak; }; + 'pertenk;' => { output->first = 0x2031; fbreak; }; + 'pfr;' => { output->first = 0x0001d52d; fbreak; }; + 'phi;' => { output->first = 0x03c6; fbreak; }; + 'phiv;' => { output->first = 0x03d5; fbreak; }; + 'phmmat;' => { output->first = 0x2133; fbreak; }; + 'phone;' => { output->first = 0x260e; fbreak; }; + 'pi;' => { output->first = 0x03c0; fbreak; }; + 'pitchfork;' => { output->first = 0x22d4; fbreak; }; + 'piv;' => { output->first = 0x03d6; fbreak; }; + 'planck;' => { output->first = 0x210f; fbreak; }; + 'planckh;' => { output->first = 0x210e; fbreak; }; + 'plankv;' => { output->first = 0x210f; fbreak; }; + 'plus;' => { output->first = 0x2b; fbreak; }; + 'plusacir;' => { output->first = 0x2a23; fbreak; }; + 'plusb;' => { output->first = 0x229e; fbreak; }; + 'pluscir;' => { output->first = 0x2a22; fbreak; }; + 'plusdo;' => { output->first = 0x2214; fbreak; }; + 'plusdu;' => { output->first = 0x2a25; fbreak; }; + 'pluse;' => { output->first = 0x2a72; fbreak; }; + 'plusmn;' => { output->first = 0xb1; fbreak; }; + 'plusmn' => { output->first = 0xb1; fbreak; }; + 'plussim;' => { output->first = 0x2a26; fbreak; }; + 'plustwo;' => { output->first = 0x2a27; fbreak; }; + 'pm;' => { output->first = 0xb1; fbreak; }; + 'pointint;' => { output->first = 0x2a15; fbreak; }; + 'popf;' => { output->first = 0x0001d561; fbreak; }; + 'pound;' => { output->first = 0xa3; fbreak; }; + 'pound' => { output->first = 0xa3; fbreak; }; + 'pr;' => { output->first = 0x227a; fbreak; }; + 'prE;' => { output->first = 0x2ab3; fbreak; }; + 'prap;' => { output->first = 0x2ab7; fbreak; }; + 'prcue;' => { output->first = 0x227c; fbreak; }; + 'pre;' => { output->first = 0x2aaf; fbreak; }; + 'prec;' => { output->first = 0x227a; fbreak; }; + 'precapprox;' => { output->first = 0x2ab7; fbreak; }; + 'preccurlyeq;' => { output->first = 0x227c; fbreak; }; + 'preceq;' => { output->first = 0x2aaf; fbreak; }; + 'precnapprox;' => { output->first = 0x2ab9; fbreak; }; + 'precneqq;' => { output->first = 0x2ab5; fbreak; }; + 'precnsim;' => { output->first = 0x22e8; fbreak; }; + 'precsim;' => { output->first = 0x227e; fbreak; }; + 'prime;' => { output->first = 0x2032; fbreak; }; + 'primes;' => { output->first = 0x2119; fbreak; }; + 'prnE;' => { output->first = 0x2ab5; fbreak; }; + 'prnap;' => { output->first = 0x2ab9; fbreak; }; + 'prnsim;' => { output->first = 0x22e8; fbreak; }; + 'prod;' => { output->first = 0x220f; fbreak; }; + 'profalar;' => { output->first = 0x232e; fbreak; }; + 'profline;' => { output->first = 0x2312; fbreak; }; + 'profsurf;' => { output->first = 0x2313; fbreak; }; + 'prop;' => { output->first = 0x221d; fbreak; }; + 'propto;' => { output->first = 0x221d; fbreak; }; + 'prsim;' => { output->first = 0x227e; fbreak; }; + 'prurel;' => { output->first = 0x22b0; fbreak; }; + 'pscr;' => { output->first = 0x0001d4c5; fbreak; }; + 'psi;' => { output->first = 0x03c8; fbreak; }; + 'puncsp;' => { output->first = 0x2008; fbreak; }; + 'qfr;' => { output->first = 0x0001d52e; fbreak; }; + 'qint;' => { output->first = 0x2a0c; fbreak; }; + 'qopf;' => { output->first = 0x0001d562; fbreak; }; + 'qprime;' => { output->first = 0x2057; fbreak; }; + 'qscr;' => { output->first = 0x0001d4c6; fbreak; }; + 'quaternions;' => { output->first = 0x210d; fbreak; }; + 'quatint;' => { output->first = 0x2a16; fbreak; }; + 'quest;' => { output->first = 0x3f; fbreak; }; + 'questeq;' => { output->first = 0x225f; fbreak; }; + 'quot;' => { output->first = 0x22; fbreak; }; + 'quot' => { output->first = 0x22; fbreak; }; + 'rAarr;' => { output->first = 0x21db; fbreak; }; + 'rArr;' => { output->first = 0x21d2; fbreak; }; + 'rAtail;' => { output->first = 0x291c; fbreak; }; + 'rBarr;' => { output->first = 0x290f; fbreak; }; + 'rHar;' => { output->first = 0x2964; fbreak; }; + 'race;' => { output->first = 0x223d; output->second = 0x0331; fbreak; }; + 'racute;' => { output->first = 0x0155; fbreak; }; + 'radic;' => { output->first = 0x221a; fbreak; }; + 'raemptyv;' => { output->first = 0x29b3; fbreak; }; + 'rang;' => { output->first = 0x27e9; fbreak; }; + 'rangd;' => { output->first = 0x2992; fbreak; }; + 'range;' => { output->first = 0x29a5; fbreak; }; + 'rangle;' => { output->first = 0x27e9; fbreak; }; + 'raquo;' => { output->first = 0xbb; fbreak; }; + 'raquo' => { output->first = 0xbb; fbreak; }; + 'rarr;' => { output->first = 0x2192; fbreak; }; + 'rarrap;' => { output->first = 0x2975; fbreak; }; + 'rarrb;' => { output->first = 0x21e5; fbreak; }; + 'rarrbfs;' => { output->first = 0x2920; fbreak; }; + 'rarrc;' => { output->first = 0x2933; fbreak; }; + 'rarrfs;' => { output->first = 0x291e; fbreak; }; + 'rarrhk;' => { output->first = 0x21aa; fbreak; }; + 'rarrlp;' => { output->first = 0x21ac; fbreak; }; + 'rarrpl;' => { output->first = 0x2945; fbreak; }; + 'rarrsim;' => { output->first = 0x2974; fbreak; }; + 'rarrtl;' => { output->first = 0x21a3; fbreak; }; + 'rarrw;' => { output->first = 0x219d; fbreak; }; + 'ratail;' => { output->first = 0x291a; fbreak; }; + 'ratio;' => { output->first = 0x2236; fbreak; }; + 'rationals;' => { output->first = 0x211a; fbreak; }; + 'rbarr;' => { output->first = 0x290d; fbreak; }; + 'rbbrk;' => { output->first = 0x2773; fbreak; }; + 'rbrace;' => { output->first = 0x7d; fbreak; }; + 'rbrack;' => { output->first = 0x5d; fbreak; }; + 'rbrke;' => { output->first = 0x298c; fbreak; }; + 'rbrksld;' => { output->first = 0x298e; fbreak; }; + 'rbrkslu;' => { output->first = 0x2990; fbreak; }; + 'rcaron;' => { output->first = 0x0159; fbreak; }; + 'rcedil;' => { output->first = 0x0157; fbreak; }; + 'rceil;' => { output->first = 0x2309; fbreak; }; + 'rcub;' => { output->first = 0x7d; fbreak; }; + 'rcy;' => { output->first = 0x0440; fbreak; }; + 'rdca;' => { output->first = 0x2937; fbreak; }; + 'rdldhar;' => { output->first = 0x2969; fbreak; }; + 'rdquo;' => { output->first = 0x201d; fbreak; }; + 'rdquor;' => { output->first = 0x201d; fbreak; }; + 'rdsh;' => { output->first = 0x21b3; fbreak; }; + 'real;' => { output->first = 0x211c; fbreak; }; + 'realine;' => { output->first = 0x211b; fbreak; }; + 'realpart;' => { output->first = 0x211c; fbreak; }; + 'reals;' => { output->first = 0x211d; fbreak; }; + 'rect;' => { output->first = 0x25ad; fbreak; }; + 'reg;' => { output->first = 0xae; fbreak; }; + 'reg' => { output->first = 0xae; fbreak; }; + 'rfisht;' => { output->first = 0x297d; fbreak; }; + 'rfloor;' => { output->first = 0x230b; fbreak; }; + 'rfr;' => { output->first = 0x0001d52f; fbreak; }; + 'rhard;' => { output->first = 0x21c1; fbreak; }; + 'rharu;' => { output->first = 0x21c0; fbreak; }; + 'rharul;' => { output->first = 0x296c; fbreak; }; + 'rho;' => { output->first = 0x03c1; fbreak; }; + 'rhov;' => { output->first = 0x03f1; fbreak; }; + 'rightarrow;' => { output->first = 0x2192; fbreak; }; + 'rightarrowtail;' => { output->first = 0x21a3; fbreak; }; + 'rightharpoondown;' => { output->first = 0x21c1; fbreak; }; + 'rightharpoonup;' => { output->first = 0x21c0; fbreak; }; + 'rightleftarrows;' => { output->first = 0x21c4; fbreak; }; + 'rightleftharpoons;' => { output->first = 0x21cc; fbreak; }; + 'rightrightarrows;' => { output->first = 0x21c9; fbreak; }; + 'rightsquigarrow;' => { output->first = 0x219d; fbreak; }; + 'rightthreetimes;' => { output->first = 0x22cc; fbreak; }; + 'ring;' => { output->first = 0x02da; fbreak; }; + 'risingdotseq;' => { output->first = 0x2253; fbreak; }; + 'rlarr;' => { output->first = 0x21c4; fbreak; }; + 'rlhar;' => { output->first = 0x21cc; fbreak; }; + 'rlm;' => { output->first = 0x200f; fbreak; }; + 'rmoust;' => { output->first = 0x23b1; fbreak; }; + 'rmoustache;' => { output->first = 0x23b1; fbreak; }; + 'rnmid;' => { output->first = 0x2aee; fbreak; }; + 'roang;' => { output->first = 0x27ed; fbreak; }; + 'roarr;' => { output->first = 0x21fe; fbreak; }; + 'robrk;' => { output->first = 0x27e7; fbreak; }; + 'ropar;' => { output->first = 0x2986; fbreak; }; + 'ropf;' => { output->first = 0x0001d563; fbreak; }; + 'roplus;' => { output->first = 0x2a2e; fbreak; }; + 'rotimes;' => { output->first = 0x2a35; fbreak; }; + 'rpar;' => { output->first = 0x29; fbreak; }; + 'rpargt;' => { output->first = 0x2994; fbreak; }; + 'rppolint;' => { output->first = 0x2a12; fbreak; }; + 'rrarr;' => { output->first = 0x21c9; fbreak; }; + 'rsaquo;' => { output->first = 0x203a; fbreak; }; + 'rscr;' => { output->first = 0x0001d4c7; fbreak; }; + 'rsh;' => { output->first = 0x21b1; fbreak; }; + 'rsqb;' => { output->first = 0x5d; fbreak; }; + 'rsquo;' => { output->first = 0x2019; fbreak; }; + 'rsquor;' => { output->first = 0x2019; fbreak; }; + 'rthree;' => { output->first = 0x22cc; fbreak; }; + 'rtimes;' => { output->first = 0x22ca; fbreak; }; + 'rtri;' => { output->first = 0x25b9; fbreak; }; + 'rtrie;' => { output->first = 0x22b5; fbreak; }; + 'rtrif;' => { output->first = 0x25b8; fbreak; }; + 'rtriltri;' => { output->first = 0x29ce; fbreak; }; + 'ruluhar;' => { output->first = 0x2968; fbreak; }; + 'rx;' => { output->first = 0x211e; fbreak; }; + 'sacute;' => { output->first = 0x015b; fbreak; }; + 'sbquo;' => { output->first = 0x201a; fbreak; }; + 'sc;' => { output->first = 0x227b; fbreak; }; + 'scE;' => { output->first = 0x2ab4; fbreak; }; + 'scap;' => { output->first = 0x2ab8; fbreak; }; + 'scaron;' => { output->first = 0x0161; fbreak; }; + 'sccue;' => { output->first = 0x227d; fbreak; }; + 'sce;' => { output->first = 0x2ab0; fbreak; }; + 'scedil;' => { output->first = 0x015f; fbreak; }; + 'scirc;' => { output->first = 0x015d; fbreak; }; + 'scnE;' => { output->first = 0x2ab6; fbreak; }; + 'scnap;' => { output->first = 0x2aba; fbreak; }; + 'scnsim;' => { output->first = 0x22e9; fbreak; }; + 'scpolint;' => { output->first = 0x2a13; fbreak; }; + 'scsim;' => { output->first = 0x227f; fbreak; }; + 'scy;' => { output->first = 0x0441; fbreak; }; + 'sdot;' => { output->first = 0x22c5; fbreak; }; + 'sdotb;' => { output->first = 0x22a1; fbreak; }; + 'sdote;' => { output->first = 0x2a66; fbreak; }; + 'seArr;' => { output->first = 0x21d8; fbreak; }; + 'searhk;' => { output->first = 0x2925; fbreak; }; + 'searr;' => { output->first = 0x2198; fbreak; }; + 'searrow;' => { output->first = 0x2198; fbreak; }; + 'sect;' => { output->first = 0xa7; fbreak; }; + 'sect' => { output->first = 0xa7; fbreak; }; + 'semi;' => { output->first = 0x3b; fbreak; }; + 'seswar;' => { output->first = 0x2929; fbreak; }; + 'setminus;' => { output->first = 0x2216; fbreak; }; + 'setmn;' => { output->first = 0x2216; fbreak; }; + 'sext;' => { output->first = 0x2736; fbreak; }; + 'sfr;' => { output->first = 0x0001d530; fbreak; }; + 'sfrown;' => { output->first = 0x2322; fbreak; }; + 'sharp;' => { output->first = 0x266f; fbreak; }; + 'shchcy;' => { output->first = 0x0449; fbreak; }; + 'shcy;' => { output->first = 0x0448; fbreak; }; + 'shortmid;' => { output->first = 0x2223; fbreak; }; + 'shortparallel;' => { output->first = 0x2225; fbreak; }; + 'shy;' => { output->first = 0xad; fbreak; }; + 'shy' => { output->first = 0xad; fbreak; }; + 'sigma;' => { output->first = 0x03c3; fbreak; }; + 'sigmaf;' => { output->first = 0x03c2; fbreak; }; + 'sigmav;' => { output->first = 0x03c2; fbreak; }; + 'sim;' => { output->first = 0x223c; fbreak; }; + 'simdot;' => { output->first = 0x2a6a; fbreak; }; + 'sime;' => { output->first = 0x2243; fbreak; }; + 'simeq;' => { output->first = 0x2243; fbreak; }; + 'simg;' => { output->first = 0x2a9e; fbreak; }; + 'simgE;' => { output->first = 0x2aa0; fbreak; }; + 'siml;' => { output->first = 0x2a9d; fbreak; }; + 'simlE;' => { output->first = 0x2a9f; fbreak; }; + 'simne;' => { output->first = 0x2246; fbreak; }; + 'simplus;' => { output->first = 0x2a24; fbreak; }; + 'simrarr;' => { output->first = 0x2972; fbreak; }; + 'slarr;' => { output->first = 0x2190; fbreak; }; + 'smallsetminus;' => { output->first = 0x2216; fbreak; }; + 'smashp;' => { output->first = 0x2a33; fbreak; }; + 'smeparsl;' => { output->first = 0x29e4; fbreak; }; + 'smid;' => { output->first = 0x2223; fbreak; }; + 'smile;' => { output->first = 0x2323; fbreak; }; + 'smt;' => { output->first = 0x2aaa; fbreak; }; + 'smte;' => { output->first = 0x2aac; fbreak; }; + 'smtes;' => { output->first = 0x2aac; output->second = 0xfe00; fbreak; }; + 'softcy;' => { output->first = 0x044c; fbreak; }; + 'sol;' => { output->first = 0x2f; fbreak; }; + 'solb;' => { output->first = 0x29c4; fbreak; }; + 'solbar;' => { output->first = 0x233f; fbreak; }; + 'sopf;' => { output->first = 0x0001d564; fbreak; }; + 'spades;' => { output->first = 0x2660; fbreak; }; + 'spadesuit;' => { output->first = 0x2660; fbreak; }; + 'spar;' => { output->first = 0x2225; fbreak; }; + 'sqcap;' => { output->first = 0x2293; fbreak; }; + 'sqcaps;' => { output->first = 0x2293; output->second = 0xfe00; fbreak; }; + 'sqcup;' => { output->first = 0x2294; fbreak; }; + 'sqcups;' => { output->first = 0x2294; output->second = 0xfe00; fbreak; }; + 'sqsub;' => { output->first = 0x228f; fbreak; }; + 'sqsube;' => { output->first = 0x2291; fbreak; }; + 'sqsubset;' => { output->first = 0x228f; fbreak; }; + 'sqsubseteq;' => { output->first = 0x2291; fbreak; }; + 'sqsup;' => { output->first = 0x2290; fbreak; }; + 'sqsupe;' => { output->first = 0x2292; fbreak; }; + 'sqsupset;' => { output->first = 0x2290; fbreak; }; + 'sqsupseteq;' => { output->first = 0x2292; fbreak; }; + 'squ;' => { output->first = 0x25a1; fbreak; }; + 'square;' => { output->first = 0x25a1; fbreak; }; + 'squarf;' => { output->first = 0x25aa; fbreak; }; + 'squf;' => { output->first = 0x25aa; fbreak; }; + 'srarr;' => { output->first = 0x2192; fbreak; }; + 'sscr;' => { output->first = 0x0001d4c8; fbreak; }; + 'ssetmn;' => { output->first = 0x2216; fbreak; }; + 'ssmile;' => { output->first = 0x2323; fbreak; }; + 'sstarf;' => { output->first = 0x22c6; fbreak; }; + 'star;' => { output->first = 0x2606; fbreak; }; + 'starf;' => { output->first = 0x2605; fbreak; }; + 'straightepsilon;' => { output->first = 0x03f5; fbreak; }; + 'straightphi;' => { output->first = 0x03d5; fbreak; }; + 'strns;' => { output->first = 0xaf; fbreak; }; + 'sub;' => { output->first = 0x2282; fbreak; }; + 'subE;' => { output->first = 0x2ac5; fbreak; }; + 'subdot;' => { output->first = 0x2abd; fbreak; }; + 'sube;' => { output->first = 0x2286; fbreak; }; + 'subedot;' => { output->first = 0x2ac3; fbreak; }; + 'submult;' => { output->first = 0x2ac1; fbreak; }; + 'subnE;' => { output->first = 0x2acb; fbreak; }; + 'subne;' => { output->first = 0x228a; fbreak; }; + 'subplus;' => { output->first = 0x2abf; fbreak; }; + 'subrarr;' => { output->first = 0x2979; fbreak; }; + 'subset;' => { output->first = 0x2282; fbreak; }; + 'subseteq;' => { output->first = 0x2286; fbreak; }; + 'subseteqq;' => { output->first = 0x2ac5; fbreak; }; + 'subsetneq;' => { output->first = 0x228a; fbreak; }; + 'subsetneqq;' => { output->first = 0x2acb; fbreak; }; + 'subsim;' => { output->first = 0x2ac7; fbreak; }; + 'subsub;' => { output->first = 0x2ad5; fbreak; }; + 'subsup;' => { output->first = 0x2ad3; fbreak; }; + 'succ;' => { output->first = 0x227b; fbreak; }; + 'succapprox;' => { output->first = 0x2ab8; fbreak; }; + 'succcurlyeq;' => { output->first = 0x227d; fbreak; }; + 'succeq;' => { output->first = 0x2ab0; fbreak; }; + 'succnapprox;' => { output->first = 0x2aba; fbreak; }; + 'succneqq;' => { output->first = 0x2ab6; fbreak; }; + 'succnsim;' => { output->first = 0x22e9; fbreak; }; + 'succsim;' => { output->first = 0x227f; fbreak; }; + 'sum;' => { output->first = 0x2211; fbreak; }; + 'sung;' => { output->first = 0x266a; fbreak; }; + 'sup1;' => { output->first = 0xb9; fbreak; }; + 'sup1' => { output->first = 0xb9; fbreak; }; + 'sup2;' => { output->first = 0xb2; fbreak; }; + 'sup2' => { output->first = 0xb2; fbreak; }; + 'sup3;' => { output->first = 0xb3; fbreak; }; + 'sup3' => { output->first = 0xb3; fbreak; }; + 'sup;' => { output->first = 0x2283; fbreak; }; + 'supE;' => { output->first = 0x2ac6; fbreak; }; + 'supdot;' => { output->first = 0x2abe; fbreak; }; + 'supdsub;' => { output->first = 0x2ad8; fbreak; }; + 'supe;' => { output->first = 0x2287; fbreak; }; + 'supedot;' => { output->first = 0x2ac4; fbreak; }; + 'suphsol;' => { output->first = 0x27c9; fbreak; }; + 'suphsub;' => { output->first = 0x2ad7; fbreak; }; + 'suplarr;' => { output->first = 0x297b; fbreak; }; + 'supmult;' => { output->first = 0x2ac2; fbreak; }; + 'supnE;' => { output->first = 0x2acc; fbreak; }; + 'supne;' => { output->first = 0x228b; fbreak; }; + 'supplus;' => { output->first = 0x2ac0; fbreak; }; + 'supset;' => { output->first = 0x2283; fbreak; }; + 'supseteq;' => { output->first = 0x2287; fbreak; }; + 'supseteqq;' => { output->first = 0x2ac6; fbreak; }; + 'supsetneq;' => { output->first = 0x228b; fbreak; }; + 'supsetneqq;' => { output->first = 0x2acc; fbreak; }; + 'supsim;' => { output->first = 0x2ac8; fbreak; }; + 'supsub;' => { output->first = 0x2ad4; fbreak; }; + 'supsup;' => { output->first = 0x2ad6; fbreak; }; + 'swArr;' => { output->first = 0x21d9; fbreak; }; + 'swarhk;' => { output->first = 0x2926; fbreak; }; + 'swarr;' => { output->first = 0x2199; fbreak; }; + 'swarrow;' => { output->first = 0x2199; fbreak; }; + 'swnwar;' => { output->first = 0x292a; fbreak; }; + 'szlig;' => { output->first = 0xdf; fbreak; }; + 'szlig' => { output->first = 0xdf; fbreak; }; + 'target;' => { output->first = 0x2316; fbreak; }; + 'tau;' => { output->first = 0x03c4; fbreak; }; + 'tbrk;' => { output->first = 0x23b4; fbreak; }; + 'tcaron;' => { output->first = 0x0165; fbreak; }; + 'tcedil;' => { output->first = 0x0163; fbreak; }; + 'tcy;' => { output->first = 0x0442; fbreak; }; + 'tdot;' => { output->first = 0x20db; fbreak; }; + 'telrec;' => { output->first = 0x2315; fbreak; }; + 'tfr;' => { output->first = 0x0001d531; fbreak; }; + 'there4;' => { output->first = 0x2234; fbreak; }; + 'therefore;' => { output->first = 0x2234; fbreak; }; + 'theta;' => { output->first = 0x03b8; fbreak; }; + 'thetasym;' => { output->first = 0x03d1; fbreak; }; + 'thetav;' => { output->first = 0x03d1; fbreak; }; + 'thickapprox;' => { output->first = 0x2248; fbreak; }; + 'thicksim;' => { output->first = 0x223c; fbreak; }; + 'thinsp;' => { output->first = 0x2009; fbreak; }; + 'thkap;' => { output->first = 0x2248; fbreak; }; + 'thksim;' => { output->first = 0x223c; fbreak; }; + 'thorn;' => { output->first = 0xfe; fbreak; }; + 'thorn' => { output->first = 0xfe; fbreak; }; + 'tilde;' => { output->first = 0x02dc; fbreak; }; + 'times;' => { output->first = 0xd7; fbreak; }; + 'times' => { output->first = 0xd7; fbreak; }; + 'timesb;' => { output->first = 0x22a0; fbreak; }; + 'timesbar;' => { output->first = 0x2a31; fbreak; }; + 'timesd;' => { output->first = 0x2a30; fbreak; }; + 'tint;' => { output->first = 0x222d; fbreak; }; + 'toea;' => { output->first = 0x2928; fbreak; }; + 'top;' => { output->first = 0x22a4; fbreak; }; + 'topbot;' => { output->first = 0x2336; fbreak; }; + 'topcir;' => { output->first = 0x2af1; fbreak; }; + 'topf;' => { output->first = 0x0001d565; fbreak; }; + 'topfork;' => { output->first = 0x2ada; fbreak; }; + 'tosa;' => { output->first = 0x2929; fbreak; }; + 'tprime;' => { output->first = 0x2034; fbreak; }; + 'trade;' => { output->first = 0x2122; fbreak; }; + 'triangle;' => { output->first = 0x25b5; fbreak; }; + 'triangledown;' => { output->first = 0x25bf; fbreak; }; + 'triangleleft;' => { output->first = 0x25c3; fbreak; }; + 'trianglelefteq;' => { output->first = 0x22b4; fbreak; }; + 'triangleq;' => { output->first = 0x225c; fbreak; }; + 'triangleright;' => { output->first = 0x25b9; fbreak; }; + 'trianglerighteq;' => { output->first = 0x22b5; fbreak; }; + 'tridot;' => { output->first = 0x25ec; fbreak; }; + 'trie;' => { output->first = 0x225c; fbreak; }; + 'triminus;' => { output->first = 0x2a3a; fbreak; }; + 'triplus;' => { output->first = 0x2a39; fbreak; }; + 'trisb;' => { output->first = 0x29cd; fbreak; }; + 'tritime;' => { output->first = 0x2a3b; fbreak; }; + 'trpezium;' => { output->first = 0x23e2; fbreak; }; + 'tscr;' => { output->first = 0x0001d4c9; fbreak; }; + 'tscy;' => { output->first = 0x0446; fbreak; }; + 'tshcy;' => { output->first = 0x045b; fbreak; }; + 'tstrok;' => { output->first = 0x0167; fbreak; }; + 'twixt;' => { output->first = 0x226c; fbreak; }; + 'twoheadleftarrow;' => { output->first = 0x219e; fbreak; }; + 'twoheadrightarrow;' => { output->first = 0x21a0; fbreak; }; + 'uArr;' => { output->first = 0x21d1; fbreak; }; + 'uHar;' => { output->first = 0x2963; fbreak; }; + 'uacute;' => { output->first = 0xfa; fbreak; }; + 'uacute' => { output->first = 0xfa; fbreak; }; + 'uarr;' => { output->first = 0x2191; fbreak; }; + 'ubrcy;' => { output->first = 0x045e; fbreak; }; + 'ubreve;' => { output->first = 0x016d; fbreak; }; + 'ucirc;' => { output->first = 0xfb; fbreak; }; + 'ucirc' => { output->first = 0xfb; fbreak; }; + 'ucy;' => { output->first = 0x0443; fbreak; }; + 'udarr;' => { output->first = 0x21c5; fbreak; }; + 'udblac;' => { output->first = 0x0171; fbreak; }; + 'udhar;' => { output->first = 0x296e; fbreak; }; + 'ufisht;' => { output->first = 0x297e; fbreak; }; + 'ufr;' => { output->first = 0x0001d532; fbreak; }; + 'ugrave;' => { output->first = 0xf9; fbreak; }; + 'ugrave' => { output->first = 0xf9; fbreak; }; + 'uharl;' => { output->first = 0x21bf; fbreak; }; + 'uharr;' => { output->first = 0x21be; fbreak; }; + 'uhblk;' => { output->first = 0x2580; fbreak; }; + 'ulcorn;' => { output->first = 0x231c; fbreak; }; + 'ulcorner;' => { output->first = 0x231c; fbreak; }; + 'ulcrop;' => { output->first = 0x230f; fbreak; }; + 'ultri;' => { output->first = 0x25f8; fbreak; }; + 'umacr;' => { output->first = 0x016b; fbreak; }; + 'uml;' => { output->first = 0xa8; fbreak; }; + 'uml' => { output->first = 0xa8; fbreak; }; + 'uogon;' => { output->first = 0x0173; fbreak; }; + 'uopf;' => { output->first = 0x0001d566; fbreak; }; + 'uparrow;' => { output->first = 0x2191; fbreak; }; + 'updownarrow;' => { output->first = 0x2195; fbreak; }; + 'upharpoonleft;' => { output->first = 0x21bf; fbreak; }; + 'upharpoonright;' => { output->first = 0x21be; fbreak; }; + 'uplus;' => { output->first = 0x228e; fbreak; }; + 'upsi;' => { output->first = 0x03c5; fbreak; }; + 'upsih;' => { output->first = 0x03d2; fbreak; }; + 'upsilon;' => { output->first = 0x03c5; fbreak; }; + 'upuparrows;' => { output->first = 0x21c8; fbreak; }; + 'urcorn;' => { output->first = 0x231d; fbreak; }; + 'urcorner;' => { output->first = 0x231d; fbreak; }; + 'urcrop;' => { output->first = 0x230e; fbreak; }; + 'uring;' => { output->first = 0x016f; fbreak; }; + 'urtri;' => { output->first = 0x25f9; fbreak; }; + 'uscr;' => { output->first = 0x0001d4ca; fbreak; }; + 'utdot;' => { output->first = 0x22f0; fbreak; }; + 'utilde;' => { output->first = 0x0169; fbreak; }; + 'utri;' => { output->first = 0x25b5; fbreak; }; + 'utrif;' => { output->first = 0x25b4; fbreak; }; + 'uuarr;' => { output->first = 0x21c8; fbreak; }; + 'uuml;' => { output->first = 0xfc; fbreak; }; + 'uuml' => { output->first = 0xfc; fbreak; }; + 'uwangle;' => { output->first = 0x29a7; fbreak; }; + 'vArr;' => { output->first = 0x21d5; fbreak; }; + 'vBar;' => { output->first = 0x2ae8; fbreak; }; + 'vBarv;' => { output->first = 0x2ae9; fbreak; }; + 'vDash;' => { output->first = 0x22a8; fbreak; }; + 'vangrt;' => { output->first = 0x299c; fbreak; }; + 'varepsilon;' => { output->first = 0x03f5; fbreak; }; + 'varkappa;' => { output->first = 0x03f0; fbreak; }; + 'varnothing;' => { output->first = 0x2205; fbreak; }; + 'varphi;' => { output->first = 0x03d5; fbreak; }; + 'varpi;' => { output->first = 0x03d6; fbreak; }; + 'varpropto;' => { output->first = 0x221d; fbreak; }; + 'varr;' => { output->first = 0x2195; fbreak; }; + 'varrho;' => { output->first = 0x03f1; fbreak; }; + 'varsigma;' => { output->first = 0x03c2; fbreak; }; + 'varsubsetneq;' => { output->first = 0x228a; output->second = 0xfe00; fbreak; }; + 'varsubsetneqq;' => { output->first = 0x2acb; output->second = 0xfe00; fbreak; }; + 'varsupsetneq;' => { output->first = 0x228b; output->second = 0xfe00; fbreak; }; + 'varsupsetneqq;' => { output->first = 0x2acc; output->second = 0xfe00; fbreak; }; + 'vartheta;' => { output->first = 0x03d1; fbreak; }; + 'vartriangleleft;' => { output->first = 0x22b2; fbreak; }; + 'vartriangleright;' => { output->first = 0x22b3; fbreak; }; + 'vcy;' => { output->first = 0x0432; fbreak; }; + 'vdash;' => { output->first = 0x22a2; fbreak; }; + 'vee;' => { output->first = 0x2228; fbreak; }; + 'veebar;' => { output->first = 0x22bb; fbreak; }; + 'veeeq;' => { output->first = 0x225a; fbreak; }; + 'vellip;' => { output->first = 0x22ee; fbreak; }; + 'verbar;' => { output->first = 0x7c; fbreak; }; + 'vert;' => { output->first = 0x7c; fbreak; }; + 'vfr;' => { output->first = 0x0001d533; fbreak; }; + 'vltri;' => { output->first = 0x22b2; fbreak; }; + 'vnsub;' => { output->first = 0x2282; output->second = 0x20d2; fbreak; }; + 'vnsup;' => { output->first = 0x2283; output->second = 0x20d2; fbreak; }; + 'vopf;' => { output->first = 0x0001d567; fbreak; }; + 'vprop;' => { output->first = 0x221d; fbreak; }; + 'vrtri;' => { output->first = 0x22b3; fbreak; }; + 'vscr;' => { output->first = 0x0001d4cb; fbreak; }; + 'vsubnE;' => { output->first = 0x2acb; output->second = 0xfe00; fbreak; }; + 'vsubne;' => { output->first = 0x228a; output->second = 0xfe00; fbreak; }; + 'vsupnE;' => { output->first = 0x2acc; output->second = 0xfe00; fbreak; }; + 'vsupne;' => { output->first = 0x228b; output->second = 0xfe00; fbreak; }; + 'vzigzag;' => { output->first = 0x299a; fbreak; }; + 'wcirc;' => { output->first = 0x0175; fbreak; }; + 'wedbar;' => { output->first = 0x2a5f; fbreak; }; + 'wedge;' => { output->first = 0x2227; fbreak; }; + 'wedgeq;' => { output->first = 0x2259; fbreak; }; + 'weierp;' => { output->first = 0x2118; fbreak; }; + 'wfr;' => { output->first = 0x0001d534; fbreak; }; + 'wopf;' => { output->first = 0x0001d568; fbreak; }; + 'wp;' => { output->first = 0x2118; fbreak; }; + 'wr;' => { output->first = 0x2240; fbreak; }; + 'wreath;' => { output->first = 0x2240; fbreak; }; + 'wscr;' => { output->first = 0x0001d4cc; fbreak; }; + 'xcap;' => { output->first = 0x22c2; fbreak; }; + 'xcirc;' => { output->first = 0x25ef; fbreak; }; + 'xcup;' => { output->first = 0x22c3; fbreak; }; + 'xdtri;' => { output->first = 0x25bd; fbreak; }; + 'xfr;' => { output->first = 0x0001d535; fbreak; }; + 'xhArr;' => { output->first = 0x27fa; fbreak; }; + 'xharr;' => { output->first = 0x27f7; fbreak; }; + 'xi;' => { output->first = 0x03be; fbreak; }; + 'xlArr;' => { output->first = 0x27f8; fbreak; }; + 'xlarr;' => { output->first = 0x27f5; fbreak; }; + 'xmap;' => { output->first = 0x27fc; fbreak; }; + 'xnis;' => { output->first = 0x22fb; fbreak; }; + 'xodot;' => { output->first = 0x2a00; fbreak; }; + 'xopf;' => { output->first = 0x0001d569; fbreak; }; + 'xoplus;' => { output->first = 0x2a01; fbreak; }; + 'xotime;' => { output->first = 0x2a02; fbreak; }; + 'xrArr;' => { output->first = 0x27f9; fbreak; }; + 'xrarr;' => { output->first = 0x27f6; fbreak; }; + 'xscr;' => { output->first = 0x0001d4cd; fbreak; }; + 'xsqcup;' => { output->first = 0x2a06; fbreak; }; + 'xuplus;' => { output->first = 0x2a04; fbreak; }; + 'xutri;' => { output->first = 0x25b3; fbreak; }; + 'xvee;' => { output->first = 0x22c1; fbreak; }; + 'xwedge;' => { output->first = 0x22c0; fbreak; }; + 'yacute;' => { output->first = 0xfd; fbreak; }; + 'yacute' => { output->first = 0xfd; fbreak; }; + 'yacy;' => { output->first = 0x044f; fbreak; }; + 'ycirc;' => { output->first = 0x0177; fbreak; }; + 'ycy;' => { output->first = 0x044b; fbreak; }; + 'yen;' => { output->first = 0xa5; fbreak; }; + 'yen' => { output->first = 0xa5; fbreak; }; + 'yfr;' => { output->first = 0x0001d536; fbreak; }; + 'yicy;' => { output->first = 0x0457; fbreak; }; + 'yopf;' => { output->first = 0x0001d56a; fbreak; }; + 'yscr;' => { output->first = 0x0001d4ce; fbreak; }; + 'yucy;' => { output->first = 0x044e; fbreak; }; + 'yuml;' => { output->first = 0xff; fbreak; }; + 'yuml' => { output->first = 0xff; fbreak; }; + 'zacute;' => { output->first = 0x017a; fbreak; }; + 'zcaron;' => { output->first = 0x017e; fbreak; }; + 'zcy;' => { output->first = 0x0437; fbreak; }; + 'zdot;' => { output->first = 0x017c; fbreak; }; + 'zeetrf;' => { output->first = 0x2128; fbreak; }; + 'zeta;' => { output->first = 0x03b6; fbreak; }; + 'zfr;' => { output->first = 0x0001d537; fbreak; }; + 'zhcy;' => { output->first = 0x0436; fbreak; }; + 'zigrarr;' => { output->first = 0x21dd; fbreak; }; + 'zopf;' => { output->first = 0x0001d56b; fbreak; }; + 'zscr;' => { output->first = 0x0001d4cf; fbreak; }; + 'zwj;' => { output->first = 0x200d; fbreak; }; + 'zwnj;' => { output->first = 0x200c; fbreak; }; +*|; +}%% + +// clang-format off +%% write data noerror nofinal; +// clang-format on + +static bool consume_named_ref( + struct GumboInternalParser* parser, Utf8Iterator* input, bool is_in_attribute, + OneOrTwoCodepoints* output) { + assert(output->first == kGumboNoChar); + const char* p = utf8iterator_get_char_pointer(input); + const char* pe = utf8iterator_get_end_pointer(input); + const char* eof = pe; + const char* te = 0; + const char *ts, *start; + int cs, act; + + // clang-format off + %% write init; + // Avoid unused variable warnings. + (void) act; + (void) ts; + (void) char_ref_en_valid_named_ref; + + start = p; + %% write exec; + // clang-format on + + if (cs >= %%{ write first_final; }%%) { + assert(output->first != kGumboNoChar); + char last_char = *(te - 1); + ptrdiff_t len = te - start; + if (last_char == ';') { + bool matched = utf8iterator_maybe_consume_match(input, start, len, true); + assert(matched); + return true; + } else if (is_in_attribute && (*te == '=' || isalnum(*te))) { + output->first = kGumboNoChar; + output->second = kGumboNoChar; + utf8iterator_reset(input); + return true; + } else { + GumboStringPiece bad_ref; + bad_ref.length = te - start; + bad_ref.data = start; + add_named_reference_error( + parser, input, GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON, bad_ref); + bool matched = utf8iterator_maybe_consume_match(input, start, len, true); + assert(matched); + return false; + } + } else { + output->first = kGumboNoChar; + output->second = kGumboNoChar; + bool status = maybe_add_invalid_named_reference(parser, input); + utf8iterator_reset(input); + return status; + } +} + +bool consume_char_ref( + struct GumboInternalParser* parser, struct GumboInternalUtf8Iterator* input, + int additional_allowed_char, bool is_in_attribute, + OneOrTwoCodepoints* output) { + utf8iterator_mark(input); + utf8iterator_next(input); + int c = utf8iterator_current(input); + output->first = kGumboNoChar; + output->second = kGumboNoChar; + if (c == additional_allowed_char) { + utf8iterator_reset(input); + output->first = kGumboNoChar; + return true; + } + switch (utf8iterator_current(input)) { + case '\t': + case '\n': + case '\f': + case ' ': + case '<': + case '&': + case -1: + utf8iterator_reset(input); + return true; + case '#': + return consume_numeric_ref(parser, input, &output->first); + default: + return consume_named_ref(parser, input, is_in_attribute, output); + } +} diff --git a/gb.form.htmlview/src/gumbo/error.c b/gb.form.htmlview/src/gumbo/error.c new file mode 100644 index 00000000..5e89cd76 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/error.c @@ -0,0 +1,279 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "error.h" + +#include +#include +#include +#include + +#include "gumbo.h" +#include "parser.h" +#include "string_buffer.h" +#include "util.h" +#include "vector.h" + +// Prints a formatted message to a StringBuffer. This automatically resizes the +// StringBuffer as necessary to fit the message. Returns the number of bytes +// written. +static int print_message( + GumboParser* parser, GumboStringBuffer* output, const char* format, ...) { + va_list args; + size_t remaining_capacity = output->capacity - output->length; + va_start(args, format); + int bytes_written = vsnprintf( + output->data + output->length, remaining_capacity, format, args); + va_end(args); +#ifdef _MSC_VER + if (bytes_written == -1) { + // vsnprintf returns -1 on MSVC++ if there's not enough capacity, instead of + // returning the number of bytes that would've been written had there been + // enough. In this case, we'll double the buffer size and hope it fits when + // we retry (letting it fail and returning 0 if it doesn't), since there's + // no way to smartly resize the buffer. + gumbo_string_buffer_reserve(parser, output->capacity * 2, output); + va_start(args, format); + int result = vsnprintf( + output->data + output->length, remaining_capacity, format, args); + va_end(args); + return result == -1 ? 0 : result; + } +#else + // -1 in standard C99 indicates an encoding error. Return 0 and do nothing. + if (bytes_written == -1) { + return 0; + } +#endif + + if (bytes_written > remaining_capacity) { + gumbo_string_buffer_reserve( + parser, output->capacity + bytes_written, output); + remaining_capacity = output->capacity - output->length; + va_start(args, format); + bytes_written = vsnprintf( + output->data + output->length, remaining_capacity, format, args); + va_end(args); + } + output->length += bytes_written; + return bytes_written; +} + +static void print_tag_stack(GumboParser* parser, const GumboParserError* error, + GumboStringBuffer* output) { + print_message(parser, output, " Currently open tags: "); + for (unsigned int i = 0; i < error->tag_stack.length; ++i) { + if (i) { + print_message(parser, output, ", "); + } + GumboTag tag = (GumboTag) error->tag_stack.data[i]; + print_message(parser, output, gumbo_normalized_tagname(tag)); + } + gumbo_string_buffer_append_codepoint(parser, '.', output); +} + +static void handle_parser_error(GumboParser* parser, + const GumboParserError* error, GumboStringBuffer* output) { + if (error->parser_state == GUMBO_INSERTION_MODE_INITIAL && + error->input_type != GUMBO_TOKEN_DOCTYPE) { + print_message( + parser, output, "The doctype must be the first token in the document"); + return; + } + + switch (error->input_type) { + case GUMBO_TOKEN_DOCTYPE: + print_message(parser, output, "This is not a legal doctype"); + return; + case GUMBO_TOKEN_COMMENT: + // Should never happen; comments are always legal. + assert(0); + // But just in case... + print_message(parser, output, "Comments aren't legal here"); + return; + case GUMBO_TOKEN_CDATA: + case GUMBO_TOKEN_WHITESPACE: + case GUMBO_TOKEN_CHARACTER: + print_message(parser, output, "Character tokens aren't legal here"); + return; + case GUMBO_TOKEN_NULL: + print_message(parser, output, "Null bytes are not allowed in HTML5"); + return; + case GUMBO_TOKEN_EOF: + if (error->parser_state == GUMBO_INSERTION_MODE_INITIAL) { + print_message(parser, output, "You must provide a doctype"); + } else { + print_message(parser, output, "Premature end of file"); + print_tag_stack(parser, error, output); + } + return; + case GUMBO_TOKEN_START_TAG: + case GUMBO_TOKEN_END_TAG: + print_message(parser, output, "That tag isn't allowed here"); + print_tag_stack(parser, error, output); + // TODO(jdtang): Give more specific messaging. + return; + } +} + +// Finds the preceding newline in an original source buffer from a given byte +// location. Returns a character pointer to the character after that, or a +// pointer to the beginning of the string if this is the first line. +static const char* find_last_newline( + const char* original_text, const char* error_location) { + assert(error_location >= original_text); + const char* c = error_location; + for (; c != original_text && *c != '\n'; --c) { + // There may be an error at EOF, which would be a nul byte. + assert(*c || c == error_location); + } + return c == original_text ? c : c + 1; +} + +// Finds the next newline in the original source buffer from a given byte +// location. Returns a character pointer to that newline, or a pointer to the +// terminating null byte if this is the last line. +static const char* find_next_newline( + const char* original_text, const char* error_location) { + const char* c = error_location; + for (; *c && *c != '\n'; ++c) + ; + return c; +} + +GumboError* gumbo_add_error(GumboParser* parser) { + int max_errors = parser->_options->max_errors; + if (max_errors >= 0 && parser->_output->errors.length >= (unsigned int) max_errors) { + return NULL; + } + GumboError* error = gumbo_parser_allocate(parser, sizeof(GumboError)); + gumbo_vector_add(parser, error, &parser->_output->errors); + return error; +} + +void gumbo_error_to_string( + GumboParser* parser, const GumboError* error, GumboStringBuffer* output) { + print_message( + parser, output, "@%d:%d: ", error->position.line, error->position.column); + switch (error->type) { + case GUMBO_ERR_UTF8_INVALID: + print_message( + parser, output, "Invalid UTF8 character 0x%x", error->v.codepoint); + break; + case GUMBO_ERR_UTF8_TRUNCATED: + print_message(parser, output, + "Input stream ends with a truncated UTF8 character 0x%x", + error->v.codepoint); + break; + case GUMBO_ERR_NUMERIC_CHAR_REF_NO_DIGITS: + print_message( + parser, output, "No digits after &# in numeric character reference"); + break; + case GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON: + print_message(parser, output, + "The numeric character reference &#%d should be followed " + "by a semicolon", + error->v.codepoint); + break; + case GUMBO_ERR_NUMERIC_CHAR_REF_INVALID: + print_message(parser, output, + "The numeric character reference &#%d; encodes an invalid " + "unicode codepoint", + error->v.codepoint); + break; + case GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON: + // The textual data came from one of the literal strings in the table, and + // so it'll be null-terminated. + print_message(parser, output, + "The named character reference &%.*s should be followed by a " + "semicolon", + (int) error->v.text.length, error->v.text.data); + break; + case GUMBO_ERR_NAMED_CHAR_REF_INVALID: + print_message(parser, output, + "The named character reference &%.*s; is not a valid entity name", + (int) error->v.text.length, error->v.text.data); + break; + case GUMBO_ERR_DUPLICATE_ATTR: + print_message(parser, output, + "Attribute %s occurs multiple times, at positions %d and %d", + error->v.duplicate_attr.name, error->v.duplicate_attr.original_index, + error->v.duplicate_attr.new_index); + break; + case GUMBO_ERR_PARSER: + case GUMBO_ERR_UNACKNOWLEDGED_SELF_CLOSING_TAG: + handle_parser_error(parser, &error->v.parser, output); + break; + default: + print_message(parser, output, + "Tokenizer error with an unimplemented error message"); + break; + } + gumbo_string_buffer_append_codepoint(parser, '.', output); +} + +void gumbo_caret_diagnostic_to_string(GumboParser* parser, + const GumboError* error, const char* source_text, + GumboStringBuffer* output) { + gumbo_error_to_string(parser, error, output); + + const char* line_start = find_last_newline(source_text, error->original_text); + const char* line_end = find_next_newline(source_text, error->original_text); + GumboStringPiece original_line; + original_line.data = line_start; + original_line.length = line_end - line_start; + + gumbo_string_buffer_append_codepoint(parser, '\n', output); + gumbo_string_buffer_append_string(parser, &original_line, output); + gumbo_string_buffer_append_codepoint(parser, '\n', output); + gumbo_string_buffer_reserve( + parser, output->length + error->position.column, output); + int num_spaces = error->position.column - 1; + memset(output->data + output->length, ' ', num_spaces); + output->length += num_spaces; + gumbo_string_buffer_append_codepoint(parser, '^', output); + gumbo_string_buffer_append_codepoint(parser, '\n', output); +} + +void gumbo_print_caret_diagnostic( + GumboParser* parser, const GumboError* error, const char* source_text) { + GumboStringBuffer text; + gumbo_string_buffer_init(parser, &text); + gumbo_caret_diagnostic_to_string(parser, error, source_text, &text); + printf("%.*s", (int) text.length, text.data); + gumbo_string_buffer_destroy(parser, &text); +} + +void gumbo_error_destroy(GumboParser* parser, GumboError* error) { + if (error->type == GUMBO_ERR_PARSER || + error->type == GUMBO_ERR_UNACKNOWLEDGED_SELF_CLOSING_TAG) { + gumbo_vector_destroy(parser, &error->v.parser.tag_stack); + } else if (error->type == GUMBO_ERR_DUPLICATE_ATTR) { + gumbo_parser_deallocate(parser, (void*) error->v.duplicate_attr.name); + } + gumbo_parser_deallocate(parser, error); +} + +void gumbo_init_errors(GumboParser* parser) { + gumbo_vector_init(parser, 5, &parser->_output->errors); +} + +void gumbo_destroy_errors(GumboParser* parser) { + for (unsigned int i = 0; i < parser->_output->errors.length; ++i) { + gumbo_error_destroy(parser, parser->_output->errors.data[i]); + } + gumbo_vector_destroy(parser, &parser->_output->errors); +} diff --git a/gb.form.htmlview/src/gumbo/error.h b/gb.form.htmlview/src/gumbo/error.h new file mode 100644 index 00000000..c1b4b39f --- /dev/null +++ b/gb.form.htmlview/src/gumbo/error.h @@ -0,0 +1,225 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// Error types, enums, and handling functions. + +#ifndef GUMBO_ERROR_H_ +#define GUMBO_ERROR_H_ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif +#include + +#include "gumbo.h" +#include "insertion_mode.h" +#include "string_buffer.h" +#include "token_type.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalParser; + +typedef enum { + GUMBO_ERR_UTF8_INVALID, + GUMBO_ERR_UTF8_TRUNCATED, + GUMBO_ERR_UTF8_NULL, + GUMBO_ERR_NUMERIC_CHAR_REF_NO_DIGITS, + GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON, + GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, + GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON, + GUMBO_ERR_NAMED_CHAR_REF_INVALID, + GUMBO_ERR_TAG_STARTS_WITH_QUESTION, + GUMBO_ERR_TAG_EOF, + GUMBO_ERR_TAG_INVALID, + GUMBO_ERR_CLOSE_TAG_EMPTY, + GUMBO_ERR_CLOSE_TAG_EOF, + GUMBO_ERR_CLOSE_TAG_INVALID, + GUMBO_ERR_SCRIPT_EOF, + GUMBO_ERR_ATTR_NAME_EOF, + GUMBO_ERR_ATTR_NAME_INVALID, + GUMBO_ERR_ATTR_DOUBLE_QUOTE_EOF, + GUMBO_ERR_ATTR_SINGLE_QUOTE_EOF, + GUMBO_ERR_ATTR_UNQUOTED_EOF, + GUMBO_ERR_ATTR_UNQUOTED_RIGHT_BRACKET, + GUMBO_ERR_ATTR_UNQUOTED_EQUALS, + GUMBO_ERR_ATTR_AFTER_EOF, + GUMBO_ERR_ATTR_AFTER_INVALID, + GUMBO_ERR_DUPLICATE_ATTR, + GUMBO_ERR_SOLIDUS_EOF, + GUMBO_ERR_SOLIDUS_INVALID, + GUMBO_ERR_DASHES_OR_DOCTYPE, + GUMBO_ERR_COMMENT_EOF, + GUMBO_ERR_COMMENT_INVALID, + GUMBO_ERR_COMMENT_BANG_AFTER_DOUBLE_DASH, + GUMBO_ERR_COMMENT_DASH_AFTER_DOUBLE_DASH, + GUMBO_ERR_COMMENT_SPACE_AFTER_DOUBLE_DASH, + GUMBO_ERR_COMMENT_END_BANG_EOF, + GUMBO_ERR_DOCTYPE_EOF, + GUMBO_ERR_DOCTYPE_INVALID, + GUMBO_ERR_DOCTYPE_SPACE, + GUMBO_ERR_DOCTYPE_RIGHT_BRACKET, + GUMBO_ERR_DOCTYPE_SPACE_OR_RIGHT_BRACKET, + GUMBO_ERR_DOCTYPE_END, + GUMBO_ERR_PARSER, + GUMBO_ERR_UNACKNOWLEDGED_SELF_CLOSING_TAG, +} GumboErrorType; + +// Additional data for duplicated attributes. +typedef struct GumboInternalDuplicateAttrError { + // The name of the attribute. Owned by this struct. + const char* name; + + // The (0-based) index within the attributes vector of the original + // occurrence. + unsigned int original_index; + + // The (0-based) index where the new occurrence would be. + unsigned int new_index; +} GumboDuplicateAttrError; + +// A simplified representation of the tokenizer state, designed to be more +// useful to clients of this library than the internal representation. This +// condenses the actual states used in the tokenizer state machine into a few +// values that will be familiar to users of HTML. +typedef enum { + GUMBO_ERR_TOKENIZER_DATA, + GUMBO_ERR_TOKENIZER_CHAR_REF, + GUMBO_ERR_TOKENIZER_RCDATA, + GUMBO_ERR_TOKENIZER_RAWTEXT, + GUMBO_ERR_TOKENIZER_PLAINTEXT, + GUMBO_ERR_TOKENIZER_SCRIPT, + GUMBO_ERR_TOKENIZER_TAG, + GUMBO_ERR_TOKENIZER_SELF_CLOSING_TAG, + GUMBO_ERR_TOKENIZER_ATTR_NAME, + GUMBO_ERR_TOKENIZER_ATTR_VALUE, + GUMBO_ERR_TOKENIZER_MARKUP_DECLARATION, + GUMBO_ERR_TOKENIZER_COMMENT, + GUMBO_ERR_TOKENIZER_DOCTYPE, + GUMBO_ERR_TOKENIZER_CDATA, +} GumboTokenizerErrorState; + +// Additional data for tokenizer errors. +// This records the current state and codepoint encountered - this is usually +// enough to reconstruct what went wrong and provide a friendly error message. +typedef struct GumboInternalTokenizerError { + // The bad codepoint encountered. + int codepoint; + + // The state that the tokenizer was in at the time. + GumboTokenizerErrorState state; +} GumboTokenizerError; + +// Additional data for parse errors. +typedef struct GumboInternalParserError { + // The type of input token that resulted in this error. + GumboTokenType input_type; + + // The HTML tag of the input token. TAG_UNKNOWN if this was not a tag token. + GumboTag input_tag; + + // The insertion mode that the parser was in at the time. + GumboInsertionMode parser_state; + + // The tag stack at the point of the error. Note that this is an GumboVector + // of GumboTag's *stored by value* - cast the void* to an GumboTag directly to + // get at the tag. + GumboVector /* GumboTag */ tag_stack; +} GumboParserError; + +// The overall error struct representing an error in decoding/tokenizing/parsing +// the HTML. This contains an enumerated type flag, a source position, and then +// a union of fields containing data specific to the error. +typedef struct GumboInternalError { + // The type of error. + GumboErrorType type; + + // The position within the source file where the error occurred. + GumboSourcePosition position; + + // A pointer to the byte within the original source file text where the error + // occurred (note that this is not the same as position.offset, as that gives + // character-based instead of byte-based offsets). + const char* original_text; + + // Type-specific error information. + union { + // The code point we encountered, for: + // * GUMBO_ERR_UTF8_INVALID + // * GUMBO_ERR_UTF8_TRUNCATED + // * GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON + // * GUMBO_ERR_NUMERIC_CHAR_REF_INVALID + uint64_t codepoint; + + // Tokenizer errors. + GumboTokenizerError tokenizer; + + // Short textual data, for: + // * GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON + // * GUMBO_ERR_NAMED_CHAR_REF_INVALID + GumboStringPiece text; + + // Duplicate attribute data, for GUMBO_ERR_DUPLICATE_ATTR. + GumboDuplicateAttrError duplicate_attr; + + // Parser state, for GUMBO_ERR_PARSER and + // GUMBO_ERR_UNACKNOWLEDGE_SELF_CLOSING_TAG. + struct GumboInternalParserError parser; + } v; +} GumboError; + +// Adds a new error to the parser's error list, and returns a pointer to it so +// that clients can fill out the rest of its fields. May return NULL if we're +// already over the max_errors field specified in GumboOptions. +GumboError* gumbo_add_error(struct GumboInternalParser* parser); + +// Initializes the errors vector in the parser. +void gumbo_init_errors(struct GumboInternalParser* errors); + +// Frees all the errors in the 'errors_' field of the parser. +void gumbo_destroy_errors(struct GumboInternalParser* errors); + +// Frees the memory used for a single GumboError. +void gumbo_error_destroy(struct GumboInternalParser* parser, GumboError* error); + +// Prints an error to a string. This fills an empty GumboStringBuffer with a +// freshly-allocated buffer containing the error message text. The caller is +// responsible for deleting the buffer. (Note that the buffer is allocated with +// the allocator specified in the GumboParser ~config and hence should be freed +// by gumbo_parser_deallocate().) +void gumbo_error_to_string(struct GumboInternalParser* parser, + const GumboError* error, GumboStringBuffer* output); + +// Prints a caret diagnostic to a string. This fills an empty GumboStringBuffer +// with a freshly-allocated buffer containing the error message text. The +// caller is responsible for deleting the buffer. (Note that the buffer is +// allocated with the allocator specified in the GumboParser ~config and hence +// should be freed by gumbo_parser_deallocate().) +void gumbo_caret_diagnostic_to_string(struct GumboInternalParser* parser, + const GumboError* error, const char* source_text, + GumboStringBuffer* output); + +// Like gumbo_caret_diagnostic_to_string, but prints the text to stdout instead +// of writing to a string. +void gumbo_print_caret_diagnostic(struct GumboInternalParser* parser, + const GumboError* error, const char* source_text); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_ERROR_H_ diff --git a/gb.form.htmlview/src/gumbo/gumbo.h b/gb.form.htmlview/src/gumbo/gumbo.h new file mode 100644 index 00000000..f8137cf0 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/gumbo.h @@ -0,0 +1,671 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// We use Gumbo as a prefix for types, gumbo_ as a prefix for functions, and +// GUMBO_ as a prefix for enum constants (static constants get the Google-style +// kGumbo prefix). + +/** + * @file + * @mainpage Gumbo HTML Parser + * + * This provides a conformant, no-dependencies implementation of the HTML5 + * parsing algorithm. It supports only UTF8; if you need to parse a different + * encoding, run a preprocessing step to convert to UTF8. It returns a parse + * tree made of the structs in this file. + * + * Example: + * @code + * GumboOutput* output = gumbo_parse(input); + * do_something_with_doctype(output->document); + * do_something_with_html_tree(output->root); + * gumbo_destroy_output(&options, output); + * @endcode + * HTML5 Spec: + * + * http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html + */ + +#ifndef GUMBO_GUMBO_H_ +#define GUMBO_GUMBO_H_ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#define fileno _fileno +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A struct representing a character position within the original text buffer. + * Line and column numbers are 1-based and offsets are 0-based, which matches + * how most editors and command-line tools work. Also, columns measure + * positions in terms of characters while offsets measure by bytes; this is + * because the offset field is often used to pull out a particular region of + * text (which in most languages that bind to C implies pointer arithmetic on a + * buffer of bytes), while the column field is often used to reference a + * particular column on a printable display, which nowadays is usually UTF-8. + */ +typedef struct { + unsigned int line; + unsigned int column; + unsigned int offset; +} GumboSourcePosition; + +/** + * A SourcePosition used for elements that have no source position, i.e. + * parser-inserted elements. + */ +extern const GumboSourcePosition kGumboEmptySourcePosition; + +/** + * A struct representing a string or part of a string. Strings within the + * parser are represented by a char* and a length; the char* points into + * an existing data buffer owned by some other code (often the original input). + * GumboStringPieces are assumed (by convention) to be immutable, because they + * may share data. Use GumboStringBuffer if you need to construct a string. + * Clients should assume that it is not NUL-terminated, and should always use + * explicit lengths when manipulating them. + */ +typedef struct { + /** A pointer to the beginning of the string. NULL iff length == 0. */ + const char* data; + + /** The length of the string fragment, in bytes. May be zero. */ + size_t length; +} GumboStringPiece; + +/** A constant to represent a 0-length null string. */ +extern const GumboStringPiece kGumboEmptyString; + +/** + * Compares two GumboStringPieces, and returns true if they're equal or false + * otherwise. + */ +bool gumbo_string_equals( + const GumboStringPiece* str1, const GumboStringPiece* str2); + +/** + * Compares two GumboStringPieces ignoring case, and returns true if they're + * equal or false otherwise. + */ +bool gumbo_string_equals_ignore_case( + const GumboStringPiece* str1, const GumboStringPiece* str2); + +/** + * A simple vector implementation. This stores a pointer to a data array and a + * length. All elements are stored as void*; client code must cast to the + * appropriate type. Overflows upon addition result in reallocation of the data + * array, with the size doubling to maintain O(1) amortized cost. There is no + * removal function, as this isn't needed for any of the operations within this + * library. Iteration can be done through inspecting the structure directly in + * a for-loop. + */ +typedef struct { + /** Data elements. This points to a dynamically-allocated array of capacity + * elements, each a void* to the element itself. + */ + void** data; + + /** Number of elements currently in the vector. */ + unsigned int length; + + /** Current array capacity. */ + unsigned int capacity; +} GumboVector; + +/** An empty (0-length, 0-capacity) GumboVector. */ +extern const GumboVector kGumboEmptyVector; + +/** + * Returns the first index at which an element appears in this vector (testing + * by pointer equality), or -1 if it never does. + */ +int gumbo_vector_index_of(GumboVector* vector, const void* element); + +/** + * An enum for all the tags defined in the HTML5 standard. These correspond to + * the tag names themselves. Enum constants exist only for tags which appear in + * the spec itself (or for tags with special handling in the SVG and MathML + * namespaces); any other tags appear as GUMBO_TAG_UNKNOWN and the actual tag + * name can be obtained through original_tag. + * + * This is mostly for API convenience, so that clients of this library don't + * need to perform a strcasecmp to find the normalized tag name. It also has + * efficiency benefits, by letting the parser work with enums instead of + * strings. + */ +typedef enum { +// Load all the tags from an external source, generated from tag.in. +#include "gumbo/tag_enum.h" + // Used for all tags that don't have special handling in HTML. Add new tags + // to the end of tag.in so as to preserve backwards-compatibility. + GUMBO_TAG_UNKNOWN, + // A marker value to indicate the end of the enum, for iterating over it. + // Also used as the terminator for varargs functions that take tags. + GUMBO_TAG_LAST, +} GumboTag; + +/** + * Returns the normalized (usually all-lowercased, except for foreign content) + * tag name for an GumboTag enum. Return value is static data owned by the + * library. + */ +const char* gumbo_normalized_tagname(GumboTag tag); + +/** + * Extracts the tag name from the original_text field of an element or token by + * stripping off characters and attributes and adjusting the passed-in + * GumboStringPiece appropriately. The tag name is in the original case and + * shares a buffer with the original text, to simplify memory management. + * Behavior is undefined if a string-piece that doesn't represent an HTML tag + * ( or ) is passed in. If the string piece is completely + * empty (NULL data pointer), then this function will exit successfully as a + * no-op. + */ +void gumbo_tag_from_original_text(GumboStringPiece* text); + +/** + * Fixes the case of SVG elements that are not all lowercase. + * http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inforeign + * This is not done at parse time because there's no place to store a mutated + * tag name. tag_name is an enum (which will be TAG_UNKNOWN for most SVG tags + * without special handling), while original_tag_name is a pointer into the + * original buffer. Instead, we provide this helper function that clients can + * use to rename SVG tags as appropriate. + * Returns the case-normalized SVG tagname if a replacement is found, or NULL if + * no normalization is called for. The return value is static data and owned by + * the library. + */ +const char* gumbo_normalize_svg_tagname(const GumboStringPiece* tagname); + +/** + * Converts a tag name string (which may be in upper or mixed case) to a tag + * enum. The `tag` version expects `tagname` to be NULL-terminated + */ +GumboTag gumbo_tag_enum(const char* tagname); +GumboTag gumbo_tagn_enum(const char* tagname, unsigned int length); + +/** + * Attribute namespaces. + * HTML includes special handling for XLink, XML, and XMLNS namespaces on + * attributes. Everything else goes in the generic "NONE" namespace. + */ +typedef enum { + GUMBO_ATTR_NAMESPACE_NONE, + GUMBO_ATTR_NAMESPACE_XLINK, + GUMBO_ATTR_NAMESPACE_XML, + GUMBO_ATTR_NAMESPACE_XMLNS, +} GumboAttributeNamespaceEnum; + +/** + * A struct representing a single attribute on an HTML tag. This is a + * name-value pair, but also includes information about source locations and + * original source text. + */ +typedef struct { + /** + * The namespace for the attribute. This will usually be + * GUMBO_ATTR_NAMESPACE_NONE, but some XLink/XMLNS/XML attributes take special + * values, per: + * http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adjust-foreign-attributes + */ + GumboAttributeNamespaceEnum attr_namespace; + + /** + * The name of the attribute. This is in a freshly-allocated buffer to deal + * with case-normalization, and is null-terminated. + */ + const char* name; + + /** + * The original text of the attribute name, as a pointer into the original + * source buffer. + */ + GumboStringPiece original_name; + + /** + * The value of the attribute. This is in a freshly-allocated buffer to deal + * with unescaping, and is null-terminated. It does not include any quotes + * that surround the attribute. If the attribute has no value (for example, + * 'selected' on a checkbox), this will be an empty string. + */ + const char* value; + + /** + * The original text of the value of the attribute. This points into the + * original source buffer. It includes any quotes that surround the + * attribute, and you can look at original_value.data[0] and + * original_value.data[original_value.length - 1] to determine what the quote + * characters were. If the attribute has no value, this will be a 0-length + * string. + */ + GumboStringPiece original_value; + + /** The starting position of the attribute name. */ + GumboSourcePosition name_start; + + /** + * The ending position of the attribute name. This is not always derivable + * from the starting position of the value because of the possibility of + * whitespace around the = sign. + */ + GumboSourcePosition name_end; + + /** The starting position of the attribute value. */ + GumboSourcePosition value_start; + + /** The ending position of the attribute value. */ + GumboSourcePosition value_end; +} GumboAttribute; + +/** + * Given a vector of GumboAttributes, look up the one with the specified name + * and return it, or NULL if no such attribute exists. This uses a + * case-insensitive match, as HTML is case-insensitive. + */ +GumboAttribute* gumbo_get_attribute(const GumboVector* attrs, const char* name); + +/** + * Enum denoting the type of node. This determines the type of the node.v + * union. + */ +typedef enum { + /** Document node. v will be a GumboDocument. */ + GUMBO_NODE_DOCUMENT, + /** Element node. v will be a GumboElement. */ + GUMBO_NODE_ELEMENT, + /** Text node. v will be a GumboText. */ + GUMBO_NODE_TEXT, + /** CDATA node. v will be a GumboText. */ + GUMBO_NODE_CDATA, + /** Comment node. v will be a GumboText, excluding comment delimiters. */ + GUMBO_NODE_COMMENT, + /** Text node, where all contents is whitespace. v will be a GumboText. */ + GUMBO_NODE_WHITESPACE, + /** Template node. This is separate from GUMBO_NODE_ELEMENT because many + * client libraries will want to ignore the contents of template nodes, as + * the spec suggests. Recursing on GUMBO_NODE_ELEMENT will do the right thing + * here, while clients that want to include template contents should also + * check for GUMBO_NODE_TEMPLATE. v will be a GumboElement. */ + GUMBO_NODE_TEMPLATE +} GumboNodeType; + +/** + * Forward declaration of GumboNode so it can be used recursively in + * GumboNode.parent. + */ +typedef struct GumboInternalNode GumboNode; + +/** + * http://www.whatwg.org/specs/web-apps/current-work/complete/dom.html#quirks-mode + */ +typedef enum { + GUMBO_DOCTYPE_NO_QUIRKS, + GUMBO_DOCTYPE_QUIRKS, + GUMBO_DOCTYPE_LIMITED_QUIRKS +} GumboQuirksModeEnum; + +/** + * Namespaces. + * Unlike in X(HT)ML, namespaces in HTML5 are not denoted by a prefix. Rather, + * anything inside an tag is in the SVG namespace, anything inside the + * tag is in the MathML namespace, and anything else is inside the HTML + * namespace. No other namespaces are supported, so this can be an enum only. + */ +typedef enum { + GUMBO_NAMESPACE_HTML, + GUMBO_NAMESPACE_SVG, + GUMBO_NAMESPACE_MATHML +} GumboNamespaceEnum; + +/** + * Parse flags. + * We track the reasons for parser insertion of nodes and store them in a + * bitvector in the node itself. This lets client code optimize out nodes that + * are implied by the HTML structure of the document, or flag constructs that + * may not be allowed by a style guide, or track the prevalence of incorrect or + * tricky HTML code. + */ +typedef enum { + /** + * A normal node - both start and end tags appear in the source, nothing has + * been reparented. + */ + GUMBO_INSERTION_NORMAL = 0, + + /** + * A node inserted by the parser to fulfill some implicit insertion rule. + * This is usually set in addition to some other flag giving a more specific + * insertion reason; it's a generic catch-all term meaning "The start tag for + * this node did not appear in the document source". + */ + GUMBO_INSERTION_BY_PARSER = 1 << 0, + + /** + * A flag indicating that the end tag for this node did not appear in the + * document source. Note that in some cases, you can still have + * parser-inserted nodes with an explicit end tag: for example, "Text" + * has GUMBO_INSERTED_BY_PARSER set on the node, but + * GUMBO_INSERTED_END_TAG_IMPLICITLY is unset, as the tag actually + * exists. This flag will be set only if the end tag is completely missing; + * in some cases, the end tag may be misplaced (eg. a tag with text + * afterwards), which will leave this flag unset and require clients to + * inspect the parse errors for that case. + */ + GUMBO_INSERTION_IMPLICIT_END_TAG = 1 << 1, + + // Value 1 << 2 was for a flag that has since been removed. + + /** + * A flag for nodes that are inserted because their presence is implied by + * other tags, eg. , , , , etc. + */ + GUMBO_INSERTION_IMPLIED = 1 << 3, + + /** + * A flag for nodes that are converted from their end tag equivalents. For + * example,

    when no paragraph is open implies that the parser should + * create a

    tag and immediately close it, while
    means the same thing + * as
    . + */ + GUMBO_INSERTION_CONVERTED_FROM_END_TAG = 1 << 4, + + /** A flag for nodes that are converted from the parse of an tag. */ + GUMBO_INSERTION_FROM_ISINDEX = 1 << 5, + + /** A flag for tags that are rewritten as . */ + GUMBO_INSERTION_FROM_IMAGE = 1 << 6, + + /** + * A flag for nodes that are cloned as a result of the reconstruction of + * active formatting elements. This is set only on the clone; the initial + * portion of the formatting run is a NORMAL node with an IMPLICIT_END_TAG. + */ + GUMBO_INSERTION_RECONSTRUCTED_FORMATTING_ELEMENT = 1 << 7, + + /** A flag for nodes that are cloned by the adoption agency algorithm. */ + GUMBO_INSERTION_ADOPTION_AGENCY_CLONED = 1 << 8, + + /** A flag for nodes that are moved by the adoption agency algorithm. */ + GUMBO_INSERTION_ADOPTION_AGENCY_MOVED = 1 << 9, + + /** + * A flag for nodes that have been foster-parented out of a table (or + * should've been foster-parented, if verbatim mode is set). + */ + GUMBO_INSERTION_FOSTER_PARENTED = 1 << 10, +} GumboParseFlags; + +/** + * Information specific to document nodes. + */ +typedef struct { + /** + * An array of GumboNodes, containing the children of this element. This will + * normally consist of the element and any comment nodes found. + * Pointers are owned. + */ + GumboVector /* GumboNode* */ children; + + // True if there was an explicit doctype token as opposed to it being omitted. + bool has_doctype; + + // Fields from the doctype token, copied verbatim. + const char* name; + const char* public_identifier; + const char* system_identifier; + + /** + * Whether or not the document is in QuirksMode, as determined by the values + * in the GumboTokenDocType template. + */ + GumboQuirksModeEnum doc_type_quirks_mode; +} GumboDocument; + +/** + * The struct used to represent TEXT, CDATA, COMMENT, and WHITESPACE elements. + * This contains just a block of text and its position. + */ +typedef struct { + /** + * The text of this node, after entities have been parsed and decoded. For + * comment/cdata nodes, this does not include the comment delimiters. + */ + const char* text; + + /** + * The original text of this node, as a pointer into the original buffer. For + * comment/cdata nodes, this includes the comment delimiters. + */ + GumboStringPiece original_text; + + /** + * The starting position of this node. This corresponds to the position of + * original_text, before entities are decoded. + * */ + GumboSourcePosition start_pos; +} GumboText; + +/** + * The struct used to represent all HTML elements. This contains information + * about the tag, attributes, and child nodes. + */ +typedef struct { + /** + * An array of GumboNodes, containing the children of this element. Pointers + * are owned. + */ + GumboVector /* GumboNode* */ children; + + /** The GumboTag enum for this element. */ + GumboTag tag; + + /** The GumboNamespaceEnum for this element. */ + GumboNamespaceEnum tag_namespace; + + /** + * A GumboStringPiece pointing to the original tag text for this element, + * pointing directly into the source buffer. If the tag was inserted + * algorithmically (for example, or insertion), this will be a + * zero-length string. + */ + GumboStringPiece original_tag; + + /** + * A GumboStringPiece pointing to the original end tag text for this element. + * If the end tag was inserted algorithmically, (for example, closing a + * self-closing tag), this will be a zero-length string. + */ + GumboStringPiece original_end_tag; + + /** The source position for the start of the start tag. */ + GumboSourcePosition start_pos; + + /** The source position for the start of the end tag. */ + GumboSourcePosition end_pos; + + /** + * An array of GumboAttributes, containing the attributes for this tag in the + * order that they were parsed. Pointers are owned. + */ + GumboVector /* GumboAttribute* */ attributes; +} GumboElement; + +/** + * A supertype for GumboElement and GumboText, so that we can include one + * generic type in lists of children and cast as necessary to subtypes. + */ +struct GumboInternalNode { + /** The type of node that this is. */ + GumboNodeType type; + + /** Pointer back to parent node. Not owned. */ + GumboNode* parent; + + /** The index within the parent's children vector of this node. */ + size_t index_within_parent; + + /** + * A bitvector of flags containing information about why this element was + * inserted into the parse tree, including a variety of special parse + * situations. + */ + GumboParseFlags parse_flags; + + /** The actual node data. */ + union { + GumboDocument document; // For GUMBO_NODE_DOCUMENT. + GumboElement element; // For GUMBO_NODE_ELEMENT. + GumboText text; // For everything else. + } v; +}; + +/** + * The type for an allocator function. Takes the 'userdata' member of the + * GumboParser struct as its first argument. Semantics should be the same as + * malloc, i.e. return a block of size_t bytes on success or NULL on failure. + * Allocating a block of 0 bytes behaves as per malloc. + */ +// TODO(jdtang): Add checks throughout the codebase for out-of-memory condition. +typedef void* (*GumboAllocatorFunction)(void* userdata, size_t size); + +/** + * The type for a deallocator function. Takes the 'userdata' member of the + * GumboParser struct as its first argument. + */ +typedef void (*GumboDeallocatorFunction)(void* userdata, void* ptr); + +/** + * Input struct containing configuration options for the parser. + * These let you specify alternate memory managers, provide different error + * handling, etc. + * Use kGumboDefaultOptions for sensible defaults, and only set what you need. + */ +typedef struct GumboInternalOptions { + /** A memory allocator function. Default: malloc. */ + GumboAllocatorFunction allocator; + + /** A memory deallocator function. Default: free. */ + GumboDeallocatorFunction deallocator; + + /** + * An opaque object that's passed in as the first argument to all callbacks + * used by this library. Default: NULL. + */ + void* userdata; + + /** + * The tab-stop size, for computing positions in source code that uses tabs. + * Default: 8. + */ + int tab_stop; + + /** + * Whether or not to stop parsing when the first error is encountered. + * Default: false. + */ + bool stop_on_first_error; + + /** + * The maximum number of errors before the parser stops recording them. This + * is provided so that if the page is totally borked, we don't completely fill + * up the errors vector and exhaust memory with useless redundant errors. Set + * to -1 to disable the limit. + * Default: -1 + */ + int max_errors; + + /** + * The fragment context for parsing: + * https://html.spec.whatwg.org/multipage/syntax.html#parsing-html-fragments + * + * If GUMBO_TAG_LAST is passed here, it is assumed to be "no fragment", i.e. + * the regular parsing algorithm. Otherwise, pass the tag enum for the + * intended parent of the parsed fragment. We use just the tag enum rather + * than a full node because that's enough to set all the parsing context we + * need, and it provides some additional flexibility for client code to act as + * if parsing a fragment even when a full HTML tree isn't available. + * + * Default: GUMBO_TAG_LAST + */ + GumboTag fragment_context; + + /** + * The namespace for the fragment context. This lets client code + * differentiate between, say, parsing a tag in SVG vs. parsing it in + * HTML. + * Default: GUMBO_NAMESPACE_HTML + */ + GumboNamespaceEnum fragment_namespace; +} GumboOptions; + +/** Default options struct; use this with gumbo_parse_with_options. */ +extern const GumboOptions kGumboDefaultOptions; + +/** The output struct containing the results of the parse. */ +typedef struct GumboInternalOutput { + /** + * Pointer to the document node. This is a GumboNode of type NODE_DOCUMENT + * that contains the entire document as its child. + */ + GumboNode* document; + + /** + * Pointer to the root node. This the <html> tag that forms the root of the + * document. + */ + GumboNode* root; + + /** + * A list of errors that occurred during the parse. + * NOTE: In version 1.0 of this library, the API for errors hasn't been fully + * fleshed out and may change in the future. For this reason, the GumboError + * header isn't part of the public API. Contact us if you need errors + * reported so we can work out something appropriate for your use-case. + */ + GumboVector /* GumboError */ errors; +} GumboOutput; + +/** + * Parses a buffer of UTF8 text into an GumboNode parse tree. The buffer must + * live at least as long as the parse tree, as some fields (eg. original_text) + * point directly into the original buffer. + * + * This doesn't support buffers longer than 4 gigabytes. + */ +GumboOutput* gumbo_parse(const char* buffer); + +/** + * Extended version of gumbo_parse that takes an explicit options structure, + * buffer, and length. + */ +GumboOutput* gumbo_parse_with_options( + const GumboOptions* options, const char* buffer, size_t buffer_length); + +/** Release the memory used for the parse tree & parse errors. */ +void gumbo_destroy_output(const GumboOptions* options, GumboOutput* output); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_GUMBO_H_ diff --git a/gb.form.htmlview/src/gumbo/insertion_mode.h b/gb.form.htmlview/src/gumbo/insertion_mode.h new file mode 100644 index 00000000..45134c13 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/insertion_mode.h @@ -0,0 +1,57 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#ifndef GUMBO_INSERTION_MODE_H_ +#define GUMBO_INSERTION_MODE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#insertion-mode +// If new enum values are added, be sure to update the kTokenHandlers dispatch +// table in parser.c. +typedef enum { + GUMBO_INSERTION_MODE_INITIAL, + GUMBO_INSERTION_MODE_BEFORE_HTML, + GUMBO_INSERTION_MODE_BEFORE_HEAD, + GUMBO_INSERTION_MODE_IN_HEAD, + GUMBO_INSERTION_MODE_IN_HEAD_NOSCRIPT, + GUMBO_INSERTION_MODE_AFTER_HEAD, + GUMBO_INSERTION_MODE_IN_BODY, + GUMBO_INSERTION_MODE_TEXT, + GUMBO_INSERTION_MODE_IN_TABLE, + GUMBO_INSERTION_MODE_IN_TABLE_TEXT, + GUMBO_INSERTION_MODE_IN_CAPTION, + GUMBO_INSERTION_MODE_IN_COLUMN_GROUP, + GUMBO_INSERTION_MODE_IN_TABLE_BODY, + GUMBO_INSERTION_MODE_IN_ROW, + GUMBO_INSERTION_MODE_IN_CELL, + GUMBO_INSERTION_MODE_IN_SELECT, + GUMBO_INSERTION_MODE_IN_SELECT_IN_TABLE, + GUMBO_INSERTION_MODE_IN_TEMPLATE, + GUMBO_INSERTION_MODE_AFTER_BODY, + GUMBO_INSERTION_MODE_IN_FRAMESET, + GUMBO_INSERTION_MODE_AFTER_FRAMESET, + GUMBO_INSERTION_MODE_AFTER_AFTER_BODY, + GUMBO_INSERTION_MODE_AFTER_AFTER_FRAMESET +} GumboInsertionMode; + +#ifdef __cplusplus +} // extern C +#endif + +#endif // GUMBO_INSERTION_MODE_H_ diff --git a/gb.form.htmlview/src/gumbo/parser.c b/gb.form.htmlview/src/gumbo/parser.c new file mode 100644 index 00000000..653fd85a --- /dev/null +++ b/gb.form.htmlview/src/gumbo/parser.c @@ -0,0 +1,4188 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +#include "attribute.h" +#include "error.h" +#include "gumbo.h" +#include "insertion_mode.h" +#include "parser.h" +#include "tokenizer.h" +#include "tokenizer_states.h" +#include "utf8.h" +#include "util.h" +#include "vector.h" + +#define AVOID_UNUSED_VARIABLE_WARNING(i) (void)(i) + +#define GUMBO_STRING(literal) \ + { literal, sizeof(literal) - 1 } +#define TERMINATOR \ + { "", 0 } + +typedef char gumbo_tagset[GUMBO_TAG_LAST]; +#define TAG(tag) [GUMBO_TAG_##tag] = (1 << GUMBO_NAMESPACE_HTML) +#define TAG_SVG(tag) [GUMBO_TAG_##tag] = (1 << GUMBO_NAMESPACE_SVG) +#define TAG_MATHML(tag) [GUMBO_TAG_##tag] = (1 << GUMBO_NAMESPACE_MATHML) + +#define TAGSET_INCLUDES(tagset, namespace, tag) \ + (tag < GUMBO_TAG_LAST && tagset[(int) tag] == (1 << (int) namespace)) + +// selected forward declarations as it is getting hard to find +// an appropriate order +static bool node_html_tag_is(const GumboNode*, GumboTag); +static GumboInsertionMode get_current_template_insertion_mode( + const GumboParser*); +static bool handle_in_template(GumboParser*, GumboToken*); +static void destroy_node(GumboParser*, GumboNode*); + +static void* malloc_wrapper(void* unused, size_t size) { return malloc(size); } + +static void free_wrapper(void* unused, void* ptr) { free(ptr); } + +const GumboOptions kGumboDefaultOptions = {&malloc_wrapper, &free_wrapper, NULL, + 8, false, -1, GUMBO_TAG_LAST, GUMBO_NAMESPACE_HTML}; + +static const GumboStringPiece kDoctypeHtml = GUMBO_STRING("html"); +static const GumboStringPiece kPublicIdHtml4_0 = + GUMBO_STRING("-//W3C//DTD HTML 4.0//EN"); +static const GumboStringPiece kPublicIdHtml4_01 = + GUMBO_STRING("-//W3C//DTD HTML 4.01//EN"); +static const GumboStringPiece kPublicIdXhtml1_0 = + GUMBO_STRING("-//W3C//DTD XHTML 1.0 Strict//EN"); +static const GumboStringPiece kPublicIdXhtml1_1 = + GUMBO_STRING("-//W3C//DTD XHTML 1.1//EN"); +static const GumboStringPiece kSystemIdRecHtml4_0 = + GUMBO_STRING("http://www.w3.org/TR/REC-html40/strict.dtd"); +static const GumboStringPiece kSystemIdHtml4 = + GUMBO_STRING("http://www.w3.org/TR/html4/strict.dtd"); +static const GumboStringPiece kSystemIdXhtmlStrict1_1 = + GUMBO_STRING("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"); +static const GumboStringPiece kSystemIdXhtml1_1 = + GUMBO_STRING("http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"); +static const GumboStringPiece kSystemIdLegacyCompat = + GUMBO_STRING("about:legacy-compat"); + +// The doctype arrays have an explicit terminator because we want to pass them +// to a helper function, and passing them as a pointer discards sizeof +// information. The SVG arrays are used only by one-off functions, and so loops +// over them use sizeof directly instead of a terminator. + +static const GumboStringPiece kQuirksModePublicIdPrefixes[] = { + GUMBO_STRING("+//Silmaril//dtd html Pro v0r11 19970101//"), + GUMBO_STRING("-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//"), + GUMBO_STRING("-//AS//DTD HTML 3.0 asWedit + extensions//"), + GUMBO_STRING("-//IETF//DTD HTML 2.0 Level 1//"), + GUMBO_STRING("-//IETF//DTD HTML 2.0 Level 2//"), + GUMBO_STRING("-//IETF//DTD HTML 2.0 Strict Level 1//"), + GUMBO_STRING("-//IETF//DTD HTML 2.0 Strict Level 2//"), + GUMBO_STRING("-//IETF//DTD HTML 2.0 Strict//"), + GUMBO_STRING("-//IETF//DTD HTML 2.0//"), + GUMBO_STRING("-//IETF//DTD HTML 2.1E//"), + GUMBO_STRING("-//IETF//DTD HTML 3.0//"), + GUMBO_STRING("-//IETF//DTD HTML 3.2 Final//"), + GUMBO_STRING("-//IETF//DTD HTML 3.2//"), + GUMBO_STRING("-//IETF//DTD HTML 3//"), + GUMBO_STRING("-//IETF//DTD HTML Level 0//"), + GUMBO_STRING("-//IETF//DTD HTML Level 1//"), + GUMBO_STRING("-//IETF//DTD HTML Level 2//"), + GUMBO_STRING("-//IETF//DTD HTML Level 3//"), + GUMBO_STRING("-//IETF//DTD HTML Strict Level 0//"), + GUMBO_STRING("-//IETF//DTD HTML Strict Level 1//"), + GUMBO_STRING("-//IETF//DTD HTML Strict Level 2//"), + GUMBO_STRING("-//IETF//DTD HTML Strict Level 3//"), + GUMBO_STRING("-//IETF//DTD HTML Strict//"), + GUMBO_STRING("-//IETF//DTD HTML//"), + GUMBO_STRING("-//Metrius//DTD Metrius Presentational//"), + GUMBO_STRING("-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//"), + GUMBO_STRING("-//Microsoft//DTD Internet Explorer 2.0 HTML//"), + GUMBO_STRING("-//Microsoft//DTD Internet Explorer 2.0 Tables//"), + GUMBO_STRING("-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//"), + GUMBO_STRING("-//Microsoft//DTD Internet Explorer 3.0 HTML//"), + GUMBO_STRING("-//Microsoft//DTD Internet Explorer 3.0 Tables//"), + GUMBO_STRING("-//Netscape Comm. Corp.//DTD HTML//"), + GUMBO_STRING("-//Netscape Comm. Corp.//DTD Strict HTML//"), + GUMBO_STRING("-//O'Reilly and Associates//DTD HTML 2.0//"), + GUMBO_STRING("-//O'Reilly and Associates//DTD HTML Extended 1.0//"), + GUMBO_STRING("-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//"), + GUMBO_STRING( + "-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::)" + "extensions to HTML 4.0//"), + GUMBO_STRING( + "-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::" + "extensions to HTML 4.0//"), + GUMBO_STRING("-//Spyglass//DTD HTML 2.0 Extended//"), + GUMBO_STRING("-//SQ//DTD HTML 2.0 HoTMetaL + extensions//"), + GUMBO_STRING("-//Sun Microsystems Corp.//DTD HotJava HTML//"), + GUMBO_STRING("-//Sun Microsystems Corp.//DTD HotJava Strict HTML//"), + GUMBO_STRING("-//W3C//DTD HTML 3 1995-03-24//"), + GUMBO_STRING("-//W3C//DTD HTML 3.2 Draft//"), + GUMBO_STRING("-//W3C//DTD HTML 3.2 Final//"), + GUMBO_STRING("-//W3C//DTD HTML 3.2//"), + GUMBO_STRING("-//W3C//DTD HTML 3.2S Draft//"), + GUMBO_STRING("-//W3C//DTD HTML 4.0 Frameset//"), + GUMBO_STRING("-//W3C//DTD HTML 4.0 Transitional//"), + GUMBO_STRING("-//W3C//DTD HTML Experimental 19960712//"), + GUMBO_STRING("-//W3C//DTD HTML Experimental 970421//"), + GUMBO_STRING("-//W3C//DTD W3 HTML//"), + GUMBO_STRING("-//W3O//DTD W3 HTML 3.0//"), + GUMBO_STRING("-//WebTechs//DTD Mozilla HTML 2.0//"), + GUMBO_STRING("-//WebTechs//DTD Mozilla HTML//"), TERMINATOR}; + +static const GumboStringPiece kQuirksModePublicIdExactMatches[] = { + GUMBO_STRING("-//W3O//DTD W3 HTML Strict 3.0//EN//"), + GUMBO_STRING("-/W3C/DTD HTML 4.0 Transitional/EN"), GUMBO_STRING("HTML"), + TERMINATOR}; + +static const GumboStringPiece kQuirksModeSystemIdExactMatches[] = { + GUMBO_STRING("http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"), + TERMINATOR}; + +static const GumboStringPiece kLimitedQuirksPublicIdPrefixes[] = { + GUMBO_STRING("-//W3C//DTD XHTML 1.0 Frameset//"), + GUMBO_STRING("-//W3C//DTD XHTML 1.0 Transitional//"), TERMINATOR}; + +static const GumboStringPiece kLimitedQuirksRequiresSystemIdPublicIdPrefixes[] = + {GUMBO_STRING("-//W3C//DTD HTML 4.01 Frameset//"), + GUMBO_STRING("-//W3C//DTD HTML 4.01 Transitional//"), TERMINATOR}; + +// Indexed by GumboNamespaceEnum; keep in sync with that. +static const char* kLegalXmlns[] = {"http://www.w3.org/1999/xhtml", + "http://www.w3.org/2000/svg", "http://www.w3.org/1998/Math/MathML"}; + +typedef struct _ReplacementEntry { + const GumboStringPiece from; + const GumboStringPiece to; +} ReplacementEntry; + +#define REPLACEMENT_ENTRY(from, to) \ + { GUMBO_STRING(from), GUMBO_STRING(to) } + +// Static data for SVG attribute replacements. +// https://html.spec.whatwg.org/multipage/syntax.html#creating-and-inserting-nodes +static const ReplacementEntry kSvgAttributeReplacements[] = { + REPLACEMENT_ENTRY("attributename", "attributeName"), + REPLACEMENT_ENTRY("attributetype", "attributeType"), + REPLACEMENT_ENTRY("basefrequency", "baseFrequency"), + REPLACEMENT_ENTRY("baseprofile", "baseProfile"), + REPLACEMENT_ENTRY("calcmode", "calcMode"), + REPLACEMENT_ENTRY("clippathunits", "clipPathUnits"), + // REPLACEMENT_ENTRY("contentscripttype", "contentScriptType"), + // REPLACEMENT_ENTRY("contentstyletype", "contentStyleType"), + REPLACEMENT_ENTRY("diffuseconstant", "diffuseConstant"), + REPLACEMENT_ENTRY("edgemode", "edgeMode"), + // REPLACEMENT_ENTRY("externalresourcesrequired", + // "externalResourcesRequired"), + // REPLACEMENT_ENTRY("filterres", "filterRes"), + REPLACEMENT_ENTRY("filterunits", "filterUnits"), + REPLACEMENT_ENTRY("glyphref", "glyphRef"), + REPLACEMENT_ENTRY("gradienttransform", "gradientTransform"), + REPLACEMENT_ENTRY("gradientunits", "gradientUnits"), + REPLACEMENT_ENTRY("kernelmatrix", "kernelMatrix"), + REPLACEMENT_ENTRY("kernelunitlength", "kernelUnitLength"), + REPLACEMENT_ENTRY("keypoints", "keyPoints"), + REPLACEMENT_ENTRY("keysplines", "keySplines"), + REPLACEMENT_ENTRY("keytimes", "keyTimes"), + REPLACEMENT_ENTRY("lengthadjust", "lengthAdjust"), + REPLACEMENT_ENTRY("limitingconeangle", "limitingConeAngle"), + REPLACEMENT_ENTRY("markerheight", "markerHeight"), + REPLACEMENT_ENTRY("markerunits", "markerUnits"), + REPLACEMENT_ENTRY("markerwidth", "markerWidth"), + REPLACEMENT_ENTRY("maskcontentunits", "maskContentUnits"), + REPLACEMENT_ENTRY("maskunits", "maskUnits"), + REPLACEMENT_ENTRY("numoctaves", "numOctaves"), + REPLACEMENT_ENTRY("pathlength", "pathLength"), + REPLACEMENT_ENTRY("patterncontentunits", "patternContentUnits"), + REPLACEMENT_ENTRY("patterntransform", "patternTransform"), + REPLACEMENT_ENTRY("patternunits", "patternUnits"), + REPLACEMENT_ENTRY("pointsatx", "pointsAtX"), + REPLACEMENT_ENTRY("pointsaty", "pointsAtY"), + REPLACEMENT_ENTRY("pointsatz", "pointsAtZ"), + REPLACEMENT_ENTRY("preservealpha", "preserveAlpha"), + REPLACEMENT_ENTRY("preserveaspectratio", "preserveAspectRatio"), + REPLACEMENT_ENTRY("primitiveunits", "primitiveUnits"), + REPLACEMENT_ENTRY("refx", "refX"), REPLACEMENT_ENTRY("refy", "refY"), + REPLACEMENT_ENTRY("repeatcount", "repeatCount"), + REPLACEMENT_ENTRY("repeatdur", "repeatDur"), + REPLACEMENT_ENTRY("requiredextensions", "requiredExtensions"), + REPLACEMENT_ENTRY("requiredfeatures", "requiredFeatures"), + REPLACEMENT_ENTRY("specularconstant", "specularConstant"), + REPLACEMENT_ENTRY("specularexponent", "specularExponent"), + REPLACEMENT_ENTRY("spreadmethod", "spreadMethod"), + REPLACEMENT_ENTRY("startoffset", "startOffset"), + REPLACEMENT_ENTRY("stddeviation", "stdDeviation"), + REPLACEMENT_ENTRY("stitchtiles", "stitchTiles"), + REPLACEMENT_ENTRY("surfacescale", "surfaceScale"), + REPLACEMENT_ENTRY("systemlanguage", "systemLanguage"), + REPLACEMENT_ENTRY("tablevalues", "tableValues"), + REPLACEMENT_ENTRY("targetx", "targetX"), + REPLACEMENT_ENTRY("targety", "targetY"), + REPLACEMENT_ENTRY("textlength", "textLength"), + REPLACEMENT_ENTRY("viewbox", "viewBox"), + REPLACEMENT_ENTRY("viewtarget", "viewTarget"), + REPLACEMENT_ENTRY("xchannelselector", "xChannelSelector"), + REPLACEMENT_ENTRY("ychannelselector", "yChannelSelector"), + REPLACEMENT_ENTRY("zoomandpan", "zoomAndPan"), +}; + +static const ReplacementEntry kSvgTagReplacements[] = { + REPLACEMENT_ENTRY("altglyph", "altGlyph"), + REPLACEMENT_ENTRY("altglyphdef", "altGlyphDef"), + REPLACEMENT_ENTRY("altglyphitem", "altGlyphItem"), + REPLACEMENT_ENTRY("animatecolor", "animateColor"), + REPLACEMENT_ENTRY("animatemotion", "animateMotion"), + REPLACEMENT_ENTRY("animatetransform", "animateTransform"), + REPLACEMENT_ENTRY("clippath", "clipPath"), + REPLACEMENT_ENTRY("feblend", "feBlend"), + REPLACEMENT_ENTRY("fecolormatrix", "feColorMatrix"), + REPLACEMENT_ENTRY("fecomponenttransfer", "feComponentTransfer"), + REPLACEMENT_ENTRY("fecomposite", "feComposite"), + REPLACEMENT_ENTRY("feconvolvematrix", "feConvolveMatrix"), + REPLACEMENT_ENTRY("fediffuselighting", "feDiffuseLighting"), + REPLACEMENT_ENTRY("fedisplacementmap", "feDisplacementMap"), + REPLACEMENT_ENTRY("fedistantlight", "feDistantLight"), + REPLACEMENT_ENTRY("feflood", "feFlood"), + REPLACEMENT_ENTRY("fefunca", "feFuncA"), + REPLACEMENT_ENTRY("fefuncb", "feFuncB"), + REPLACEMENT_ENTRY("fefuncg", "feFuncG"), + REPLACEMENT_ENTRY("fefuncr", "feFuncR"), + REPLACEMENT_ENTRY("fegaussianblur", "feGaussianBlur"), + REPLACEMENT_ENTRY("feimage", "feImage"), + REPLACEMENT_ENTRY("femerge", "feMerge"), + REPLACEMENT_ENTRY("femergenode", "feMergeNode"), + REPLACEMENT_ENTRY("femorphology", "feMorphology"), + REPLACEMENT_ENTRY("feoffset", "feOffset"), + REPLACEMENT_ENTRY("fepointlight", "fePointLight"), + REPLACEMENT_ENTRY("fespecularlighting", "feSpecularLighting"), + REPLACEMENT_ENTRY("fespotlight", "feSpotLight"), + REPLACEMENT_ENTRY("fetile", "feTile"), + REPLACEMENT_ENTRY("feturbulence", "feTurbulence"), + REPLACEMENT_ENTRY("foreignobject", "foreignObject"), + REPLACEMENT_ENTRY("glyphref", "glyphRef"), + REPLACEMENT_ENTRY("lineargradient", "linearGradient"), + REPLACEMENT_ENTRY("radialgradient", "radialGradient"), + REPLACEMENT_ENTRY("textpath", "textPath"), +}; + +typedef struct _NamespacedAttributeReplacement { + const char* from; + const char* local_name; + const GumboAttributeNamespaceEnum attr_namespace; +} NamespacedAttributeReplacement; + +static const NamespacedAttributeReplacement kForeignAttributeReplacements[] = { + {"xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:href", "href", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:role", "role", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:show", "show", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:title", "title", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:type", "type", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xml:base", "base", GUMBO_ATTR_NAMESPACE_XML}, + {"xml:lang", "lang", GUMBO_ATTR_NAMESPACE_XML}, + {"xml:space", "space", GUMBO_ATTR_NAMESPACE_XML}, + {"xmlns", "xmlns", GUMBO_ATTR_NAMESPACE_XMLNS}, + {"xmlns:xlink", "xlink", GUMBO_ATTR_NAMESPACE_XMLNS}, +}; + +// The "scope marker" for the list of active formatting elements. We use a +// pointer to this as a generic marker element, since the particular element +// scope doesn't matter. +static const GumboNode kActiveFormattingScopeMarker; + +// The tag_is and tag_in function use true & false to denote start & end tags, +// but for readability, we define constants for them here. +static const bool kStartTag = true; +static const bool kEndTag = false; + +// Because GumboStringPieces are immutable, we can't insert a character directly +// into a text node. Instead, we accumulate all pending characters here and +// flush them out to a text node whenever a new element is inserted. +// +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#insert-a-character +typedef struct _TextNodeBufferState { + // The accumulated text to be inserted into the current text node. + GumboStringBuffer _buffer; + + // A pointer to the original text represented by this text node. Note that + // because of foster parenting and other strange DOM manipulations, this may + // include other non-text HTML tags in it; it is defined as the span of + // original text from the first character in this text node to the last + // character in this text node. + const char* _start_original_text; + + // The source position of the start of this text node. + GumboSourcePosition _start_position; + + // The type of node that will be inserted (TEXT, CDATA, or WHITESPACE). + GumboNodeType _type; +} TextNodeBufferState; + +typedef struct GumboInternalParserState { + // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#insertion-mode + GumboInsertionMode _insertion_mode; + + // Used for run_generic_parsing_algorithm, which needs to switch back to the + // original insertion mode at its conclusion. + GumboInsertionMode _original_insertion_mode; + + // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#the-stack-of-open-elements + GumboVector /*GumboNode*/ _open_elements; + + // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#the-list-of-active-formatting-elements + GumboVector /*GumboNode*/ _active_formatting_elements; + + // The stack of template insertion modes. + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-insertion-mode + GumboVector /*InsertionMode*/ _template_insertion_modes; + + // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#the-element-pointers + GumboNode* _head_element; + GumboNode* _form_element; + + // The element used as fragment context when parsing in fragment mode + GumboNode* _fragment_ctx; + + // The flag for when the spec says "Reprocess the current token in..." + bool _reprocess_current_token; + + // The flag for "acknowledge the token's self-closing flag". + bool _self_closing_flag_acknowledged; + + // The "frameset-ok" flag from the spec. + bool _frameset_ok; + + // The flag for "If the next token is a LINE FEED, ignore that token...". + bool _ignore_next_linefeed; + + // The flag for "whenever a node would be inserted into the current node, it + // must instead be foster parented". This is used for misnested table + // content, which needs to be handled according to "in body" rules yet foster + // parented outside of the table. + // It would perhaps be more explicit to have this as a parameter to + // handle_in_body and insert_element, but given how special-purpose this is + // and the number of call-sites that would need to take the extra parameter, + // it's easier just to have a state flag. + bool _foster_parent_insertions; + + // The accumulated text node buffer state. + TextNodeBufferState _text_node; + + // The current token. + GumboToken* _current_token; + + // The way that the spec is written, the </body> and </html> tags are *always* + // implicit, because encountering one of those tokens merely switches the + // insertion mode out of "in body". So we have individual state flags for + // those end tags that are then inspected by pop_current_node when the <body> + // and <html> nodes are popped to set the GUMBO_INSERTION_IMPLICIT_END_TAG + // flag appropriately. + bool _closed_body_tag; + bool _closed_html_tag; +} GumboParserState; + +static bool token_has_attribute(const GumboToken* token, const char* name) { + assert(token->type == GUMBO_TOKEN_START_TAG); + return gumbo_get_attribute(&token->v.start_tag.attributes, name) != NULL; +} + +// Checks if the value of the specified attribute is a case-insensitive match +// for the specified string. +static bool attribute_matches( + const GumboVector* attributes, const char* name, const char* value) { + const GumboAttribute* attr = gumbo_get_attribute(attributes, name); + return attr ? strcasecmp(value, attr->value) == 0 : false; +} + +// Checks if the value of the specified attribute is a case-sensitive match +// for the specified string. +static bool attribute_matches_case_sensitive( + const GumboVector* attributes, const char* name, const char* value) { + const GumboAttribute* attr = gumbo_get_attribute(attributes, name); + return attr ? strcmp(value, attr->value) == 0 : false; +} + +// Checks if the specified attribute vectors are identical. +static bool all_attributes_match( + const GumboVector* attr1, const GumboVector* attr2) { + unsigned int num_unmatched_attr2_elements = attr2->length; + for (unsigned int i = 0; i < attr1->length; ++i) { + const GumboAttribute* attr = attr1->data[i]; + if (attribute_matches_case_sensitive(attr2, attr->name, attr->value)) { + --num_unmatched_attr2_elements; + } else { + return false; + } + } + return num_unmatched_attr2_elements == 0; +} + +static void set_frameset_not_ok(GumboParser* parser) { + gumbo_debug("Setting frameset_ok to false.\n"); + parser->_parser_state->_frameset_ok = false; +} + +static GumboNode* create_node(GumboParser* parser, GumboNodeType type) { + GumboNode* node = gumbo_parser_allocate(parser, sizeof(GumboNode)); + node->parent = NULL; + node->index_within_parent = -1; + node->type = type; + node->parse_flags = GUMBO_INSERTION_NORMAL; + return node; +} + +static GumboNode* new_document_node(GumboParser* parser) { + GumboNode* document_node = create_node(parser, GUMBO_NODE_DOCUMENT); + document_node->parse_flags = GUMBO_INSERTION_BY_PARSER; + gumbo_vector_init(parser, 1, &document_node->v.document.children); + + // Must be initialized explicitly, as there's no guarantee that we'll see a + // doc type token. + GumboDocument* document = &document_node->v.document; + document->has_doctype = false; + document->name = NULL; + document->public_identifier = NULL; + document->system_identifier = NULL; + return document_node; +} + +static void output_init(GumboParser* parser) { + GumboOutput* output = gumbo_parser_allocate(parser, sizeof(GumboOutput)); + output->root = NULL; + output->document = new_document_node(parser); + parser->_output = output; + gumbo_init_errors(parser); +} + +static void parser_state_init(GumboParser* parser) { + GumboParserState* parser_state = + gumbo_parser_allocate(parser, sizeof(GumboParserState)); + parser_state->_insertion_mode = GUMBO_INSERTION_MODE_INITIAL; + parser_state->_reprocess_current_token = false; + parser_state->_frameset_ok = true; + parser_state->_ignore_next_linefeed = false; + parser_state->_foster_parent_insertions = false; + parser_state->_text_node._type = GUMBO_NODE_WHITESPACE; + gumbo_string_buffer_init(parser, &parser_state->_text_node._buffer); + gumbo_vector_init(parser, 10, &parser_state->_open_elements); + gumbo_vector_init(parser, 5, &parser_state->_active_formatting_elements); + gumbo_vector_init(parser, 5, &parser_state->_template_insertion_modes); + parser_state->_head_element = NULL; + parser_state->_form_element = NULL; + parser_state->_fragment_ctx = NULL; + parser_state->_current_token = NULL; + parser_state->_closed_body_tag = false; + parser_state->_closed_html_tag = false; + parser->_parser_state = parser_state; +} + +static void parser_state_destroy(GumboParser* parser) { + GumboParserState* state = parser->_parser_state; + if (state->_fragment_ctx) { + destroy_node(parser, state->_fragment_ctx); + } + gumbo_vector_destroy(parser, &state->_active_formatting_elements); + gumbo_vector_destroy(parser, &state->_open_elements); + gumbo_vector_destroy(parser, &state->_template_insertion_modes); + gumbo_string_buffer_destroy(parser, &state->_text_node._buffer); + gumbo_parser_deallocate(parser, state); +} + +static GumboNode* get_document_node(GumboParser* parser) { + return parser->_output->document; +} + +static bool is_fragment_parser(const GumboParser* parser) { + return !!parser->_parser_state->_fragment_ctx; +} + +// Returns the node at the bottom of the stack of open elements, or NULL if no +// elements have been added yet. +static GumboNode* get_current_node(GumboParser* parser) { + GumboVector* open_elements = &parser->_parser_state->_open_elements; + if (open_elements->length == 0) { + assert(!parser->_output->root); + return NULL; + } + assert(open_elements->length > 0); + assert(open_elements->data != NULL); + return open_elements->data[open_elements->length - 1]; +} + +static GumboNode* get_adjusted_current_node(GumboParser* parser) { + GumboParserState* state = parser->_parser_state; + if (state->_open_elements.length == 1 && state->_fragment_ctx) { + return state->_fragment_ctx; + } + return get_current_node(parser); +} + +// Returns true if the given needle is in the given array of literal +// GumboStringPieces. If exact_match is true, this requires that they match +// exactly; otherwise, this performs a prefix match to check if any of the +// elements in haystack start with needle. This always performs a +// case-insensitive match. +static bool is_in_static_list( + const char* needle, const GumboStringPiece* haystack, bool exact_match) { + for (unsigned int i = 0; haystack[i].length > 0; ++i) { + if ((exact_match && !strcmp(needle, haystack[i].data)) || + (!exact_match && !strcasecmp(needle, haystack[i].data))) { + return true; + } + } + return false; +} + +static void set_insertion_mode(GumboParser* parser, GumboInsertionMode mode) { + parser->_parser_state->_insertion_mode = mode; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#reset-the-insertion-mode-appropriately +// This is a helper function that returns the appropriate insertion mode instead +// of setting it. Returns GUMBO_INSERTION_MODE_INITIAL as a sentinel value to +// indicate that there is no appropriate insertion mode, and the loop should +// continue. +static GumboInsertionMode get_appropriate_insertion_mode( + const GumboParser* parser, int index) { + const GumboVector* open_elements = &parser->_parser_state->_open_elements; + const GumboNode* node = open_elements->data[index]; + const bool is_last = index == 0; + + if (is_last && is_fragment_parser(parser)) { + node = parser->_parser_state->_fragment_ctx; + } + + assert(node->type == GUMBO_NODE_ELEMENT || node->type == GUMBO_NODE_TEMPLATE); + switch (node->v.element.tag) { + case GUMBO_TAG_SELECT: { + if (is_last) { + return GUMBO_INSERTION_MODE_IN_SELECT; + } + for (int i = index; i > 0; --i) { + const GumboNode* ancestor = open_elements->data[i]; + if (node_html_tag_is(ancestor, GUMBO_TAG_TEMPLATE)) { + return GUMBO_INSERTION_MODE_IN_SELECT; + } + if (node_html_tag_is(ancestor, GUMBO_TAG_TABLE)) { + return GUMBO_INSERTION_MODE_IN_SELECT_IN_TABLE; + } + } + return GUMBO_INSERTION_MODE_IN_SELECT; + } + case GUMBO_TAG_TD: + case GUMBO_TAG_TH: + if (!is_last) return GUMBO_INSERTION_MODE_IN_CELL; + break; + case GUMBO_TAG_TR: + return GUMBO_INSERTION_MODE_IN_ROW; + case GUMBO_TAG_TBODY: + case GUMBO_TAG_THEAD: + case GUMBO_TAG_TFOOT: + return GUMBO_INSERTION_MODE_IN_TABLE_BODY; + case GUMBO_TAG_CAPTION: + return GUMBO_INSERTION_MODE_IN_CAPTION; + case GUMBO_TAG_COLGROUP: + return GUMBO_INSERTION_MODE_IN_COLUMN_GROUP; + case GUMBO_TAG_TABLE: + return GUMBO_INSERTION_MODE_IN_TABLE; + case GUMBO_TAG_TEMPLATE: + return get_current_template_insertion_mode(parser); + case GUMBO_TAG_HEAD: + if (!is_last) return GUMBO_INSERTION_MODE_IN_HEAD; + break; + case GUMBO_TAG_BODY: + return GUMBO_INSERTION_MODE_IN_BODY; + case GUMBO_TAG_FRAMESET: + return GUMBO_INSERTION_MODE_IN_FRAMESET; + case GUMBO_TAG_HTML: + return parser->_parser_state->_head_element + ? GUMBO_INSERTION_MODE_AFTER_HEAD + : GUMBO_INSERTION_MODE_BEFORE_HEAD; + default: + break; + } + return is_last ? GUMBO_INSERTION_MODE_IN_BODY : GUMBO_INSERTION_MODE_INITIAL; +} + +// This performs the actual "reset the insertion mode" loop. +static void reset_insertion_mode_appropriately(GumboParser* parser) { + const GumboVector* open_elements = &parser->_parser_state->_open_elements; + for (int i = open_elements->length; --i >= 0;) { + GumboInsertionMode mode = get_appropriate_insertion_mode(parser, i); + if (mode != GUMBO_INSERTION_MODE_INITIAL) { + set_insertion_mode(parser, mode); + return; + } + } + // Should never get here, because is_last will be set on the last iteration + // and will force GUMBO_INSERTION_MODE_IN_BODY. + assert(0); +} + +static GumboError* parser_add_parse_error( + GumboParser* parser, const GumboToken* token) { + gumbo_debug("Adding parse error.\n"); + GumboError* error = gumbo_add_error(parser); + if (!error) { + return NULL; + } + error->type = GUMBO_ERR_PARSER; + error->position = token->position; + error->original_text = token->original_text.data; + GumboParserError* extra_data = &error->v.parser; + extra_data->input_type = token->type; + extra_data->input_tag = GUMBO_TAG_UNKNOWN; + if (token->type == GUMBO_TOKEN_START_TAG) { + extra_data->input_tag = token->v.start_tag.tag; + } else if (token->type == GUMBO_TOKEN_END_TAG) { + extra_data->input_tag = token->v.end_tag; + } + GumboParserState* state = parser->_parser_state; + extra_data->parser_state = state->_insertion_mode; + gumbo_vector_init( + parser, state->_open_elements.length, &extra_data->tag_stack); + for (unsigned int i = 0; i < state->_open_elements.length; ++i) { + const GumboNode* node = state->_open_elements.data[i]; + assert( + node->type == GUMBO_NODE_ELEMENT || node->type == GUMBO_NODE_TEMPLATE); + gumbo_vector_add( + parser, (void*) node->v.element.tag, &extra_data->tag_stack); + } + return error; +} + +// Returns true if the specified token is either a start or end tag (specified +// by is_start) with one of the tag types in the varargs list. Terminate the +// list with GUMBO_TAG_LAST; this functions as a sentinel since no portion of +// the spec references tags that are not in the spec. +static bool tag_in( + const GumboToken* token, bool is_start, const gumbo_tagset tags) { + GumboTag token_tag; + if (is_start && token->type == GUMBO_TOKEN_START_TAG) { + token_tag = token->v.start_tag.tag; + } else if (!is_start && token->type == GUMBO_TOKEN_END_TAG) { + token_tag = token->v.end_tag; + } else { + return false; + } + return (token_tag < GUMBO_TAG_LAST && tags[(int) token_tag] != 0); +} + +// Like tag_in, but for the single-tag case. +static bool tag_is(const GumboToken* token, bool is_start, GumboTag tag) { + if (is_start && token->type == GUMBO_TOKEN_START_TAG) { + return token->v.start_tag.tag == tag; + } else if (!is_start && token->type == GUMBO_TOKEN_END_TAG) { + return token->v.end_tag == tag; + } else { + return false; + } +} + +// Like tag_in, but checks for the tag of a node, rather than a token. +static bool node_tag_in_set(const GumboNode* node, const gumbo_tagset tags) { + assert(node != NULL); + if (node->type != GUMBO_NODE_ELEMENT && node->type != GUMBO_NODE_TEMPLATE) { + return false; + } + return TAGSET_INCLUDES( + tags, node->v.element.tag_namespace, node->v.element.tag); +} + +// Like node_tag_in, but for the single-tag case. +static bool node_qualified_tag_is( + const GumboNode* node, GumboNamespaceEnum ns, GumboTag tag) { + assert(node); + return (node->type == GUMBO_NODE_ELEMENT || + node->type == GUMBO_NODE_TEMPLATE) && + node->v.element.tag == tag && node->v.element.tag_namespace == ns; +} + +// Like node_tag_in, but for the single-tag case in the HTML namespace +static bool node_html_tag_is(const GumboNode* node, GumboTag tag) { + return node_qualified_tag_is(node, GUMBO_NAMESPACE_HTML, tag); +} + +static void push_template_insertion_mode( + GumboParser* parser, GumboInsertionMode mode) { + gumbo_vector_add( + parser, (void*) mode, &parser->_parser_state->_template_insertion_modes); +} + +static void pop_template_insertion_mode(GumboParser* parser) { + gumbo_vector_pop(parser, &parser->_parser_state->_template_insertion_modes); +} + +// Returns the current template insertion mode. If the stack of template +// insertion modes is empty, this returns GUMBO_INSERTION_MODE_INITIAL. +static GumboInsertionMode get_current_template_insertion_mode( + const GumboParser* parser) { + GumboVector* template_insertion_modes = + &parser->_parser_state->_template_insertion_modes; + if (template_insertion_modes->length == 0) { + return GUMBO_INSERTION_MODE_INITIAL; + } + return (GumboInsertionMode) + template_insertion_modes->data[(template_insertion_modes->length - 1)]; +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#mathml-text-integration-point +static bool is_mathml_integration_point(const GumboNode* node) { + return node_tag_in_set( + node, (gumbo_tagset){TAG_MATHML(MI), TAG_MATHML(MO), TAG_MATHML(MN), + TAG_MATHML(MS), TAG_MATHML(MTEXT)}); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#html-integration-point +static bool is_html_integration_point(const GumboNode* node) { + return node_tag_in_set(node, (gumbo_tagset){TAG_SVG(FOREIGNOBJECT), + TAG_SVG(DESC), TAG_SVG(TITLE)}) || + (node_qualified_tag_is( + node, GUMBO_NAMESPACE_MATHML, GUMBO_TAG_ANNOTATION_XML) && + (attribute_matches( + &node->v.element.attributes, "encoding", "text/html") || + attribute_matches(&node->v.element.attributes, "encoding", + "application/xhtml+xml"))); +} + +// This represents a place to insert a node, consisting of a target parent and a +// child index within that parent. If the node should be inserted at the end of +// the parent's child, index will be -1. +typedef struct { + GumboNode* target; + int index; +} InsertionLocation; + +InsertionLocation get_appropriate_insertion_location( + GumboParser* parser, GumboNode* override_target) { + InsertionLocation retval = {override_target, -1}; + if (retval.target == NULL) { + // No override target; default to the current node, but special-case the + // root node since get_current_node() assumes the stack of open elements is + // non-empty. + retval.target = parser->_output->root != NULL ? get_current_node(parser) + : get_document_node(parser); + } + if (!parser->_parser_state->_foster_parent_insertions || + !node_tag_in_set(retval.target, (gumbo_tagset){TAG(TABLE), TAG(TBODY), + TAG(TFOOT), TAG(THEAD), TAG(TR)})) { + return retval; + } + + // Foster-parenting case. + int last_template_index = -1; + int last_table_index = -1; + GumboVector* open_elements = &parser->_parser_state->_open_elements; + for (unsigned int i = 0; i < open_elements->length; ++i) { + if (node_html_tag_is(open_elements->data[i], GUMBO_TAG_TEMPLATE)) { + last_template_index = i; + } + if (node_html_tag_is(open_elements->data[i], GUMBO_TAG_TABLE)) { + last_table_index = i; + } + } + if (last_template_index != -1 && + (last_table_index == -1 || last_template_index > last_table_index)) { + retval.target = open_elements->data[last_template_index]; + return retval; + } + if (last_table_index == -1) { + retval.target = open_elements->data[0]; + return retval; + } + GumboNode* last_table = open_elements->data[last_table_index]; + if (last_table->parent != NULL) { + retval.target = last_table->parent; + retval.index = (int)last_table->index_within_parent; + return retval; + } + + retval.target = open_elements->data[last_table_index - 1]; + return retval; +} + +// Appends a node to the end of its parent, setting the "parent" and +// "index_within_parent" fields appropriately. +static void append_node( + GumboParser* parser, GumboNode* parent, GumboNode* node) { + assert(node->parent == NULL); + assert(node->index_within_parent == -1); + GumboVector* children; + if (parent->type == GUMBO_NODE_ELEMENT || + parent->type == GUMBO_NODE_TEMPLATE) { + children = &parent->v.element.children; + } else { + assert(parent->type == GUMBO_NODE_DOCUMENT); + children = &parent->v.document.children; + } + node->parent = parent; + node->index_within_parent = children->length; + gumbo_vector_add(parser, (void*) node, children); + assert(node->index_within_parent < children->length); +} + +// Inserts a node at the specified InsertionLocation, updating the +// "parent" and "index_within_parent" fields of it and all its siblings. +// If the index of the location is -1, this calls append_node. +static void insert_node( + GumboParser* parser, GumboNode* node, InsertionLocation location) { + assert(node->parent == NULL); + assert(node->index_within_parent == -1); + GumboNode* parent = location.target; + int index = location.index; + if (index != -1) { + GumboVector* children = NULL; + if (parent->type == GUMBO_NODE_ELEMENT || + parent->type == GUMBO_NODE_TEMPLATE) { + children = &parent->v.element.children; + } else if (parent->type == GUMBO_NODE_DOCUMENT) { + children = &parent->v.document.children; + assert(children->length == 0); + } else { + assert(0); + } + + assert(index >= 0); + assert((unsigned int) index < children->length); + node->parent = parent; + node->index_within_parent = index; + gumbo_vector_insert_at(parser, (void*) node, index, children); + assert(node->index_within_parent < children->length); + for (unsigned int i = index + 1; i < children->length; ++i) { + GumboNode* sibling = children->data[i]; + sibling->index_within_parent = i; + assert(sibling->index_within_parent < children->length); + } + } else { + append_node(parser, parent, node); + } +} + +static void maybe_flush_text_node_buffer(GumboParser* parser) { + GumboParserState* state = parser->_parser_state; + TextNodeBufferState* buffer_state = &state->_text_node; + if (buffer_state->_buffer.length == 0) { + return; + } + + assert(buffer_state->_type == GUMBO_NODE_WHITESPACE || + buffer_state->_type == GUMBO_NODE_TEXT || + buffer_state->_type == GUMBO_NODE_CDATA); + GumboNode* text_node = create_node(parser, buffer_state->_type); + GumboText* text_node_data = &text_node->v.text; + text_node_data->text = + gumbo_string_buffer_to_string(parser, &buffer_state->_buffer); + text_node_data->original_text.data = buffer_state->_start_original_text; + text_node_data->original_text.length = + state->_current_token->original_text.data - + buffer_state->_start_original_text; + text_node_data->start_pos = buffer_state->_start_position; + + gumbo_debug("Flushing text node buffer of %.*s.\n", + (int) buffer_state->_buffer.length, buffer_state->_buffer.data); + + InsertionLocation location = get_appropriate_insertion_location(parser, NULL); + if (location.target->type == GUMBO_NODE_DOCUMENT) { + // The DOM does not allow Document nodes to have Text children, so per the + // spec, they are dropped on the floor. + destroy_node(parser, text_node); + } else { + insert_node(parser, text_node, location); + } + + gumbo_string_buffer_clear(parser, &buffer_state->_buffer); + buffer_state->_type = GUMBO_NODE_WHITESPACE; + assert(buffer_state->_buffer.length == 0); +} + +static void record_end_of_element( + GumboToken* current_token, GumboElement* element) { + element->end_pos = current_token->position; + element->original_end_tag = current_token->type == GUMBO_TOKEN_END_TAG + ? current_token->original_text + : kGumboEmptyString; +} + +static GumboNode* pop_current_node(GumboParser* parser) { + GumboParserState* state = parser->_parser_state; + maybe_flush_text_node_buffer(parser); + if (state->_open_elements.length > 0) { + assert(node_html_tag_is(state->_open_elements.data[0], GUMBO_TAG_HTML)); + gumbo_debug("Popping %s node.\n", + gumbo_normalized_tagname(get_current_node(parser)->v.element.tag)); + } + GumboNode* current_node = gumbo_vector_pop(parser, &state->_open_elements); + if (!current_node) { + assert(state->_open_elements.length == 0); + return NULL; + } + assert(current_node->type == GUMBO_NODE_ELEMENT || + current_node->type == GUMBO_NODE_TEMPLATE); + bool is_closed_body_or_html_tag = + (node_html_tag_is(current_node, GUMBO_TAG_BODY) && + state->_closed_body_tag) || + (node_html_tag_is(current_node, GUMBO_TAG_HTML) && + state->_closed_html_tag); + if ((state->_current_token->type != GUMBO_TOKEN_END_TAG || + !node_html_tag_is(current_node, state->_current_token->v.end_tag)) && + !is_closed_body_or_html_tag) { + current_node->parse_flags |= GUMBO_INSERTION_IMPLICIT_END_TAG; + } + if (!is_closed_body_or_html_tag) { + record_end_of_element(state->_current_token, ¤t_node->v.element); + } + return current_node; +} + +static void append_comment_node( + GumboParser* parser, GumboNode* node, const GumboToken* token) { + maybe_flush_text_node_buffer(parser); + GumboNode* comment = create_node(parser, GUMBO_NODE_COMMENT); + comment->type = GUMBO_NODE_COMMENT; + comment->parse_flags = GUMBO_INSERTION_NORMAL; + comment->v.text.text = token->v.text; + comment->v.text.original_text = token->original_text; + comment->v.text.start_pos = token->position; + append_node(parser, node, comment); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#clear-the-stack-back-to-a-table-row-context +static void clear_stack_to_table_row_context(GumboParser* parser) { + while (!node_tag_in_set(get_current_node(parser), + (gumbo_tagset){TAG(HTML), TAG(TR), TAG(TEMPLATE)})) { + pop_current_node(parser); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#clear-the-stack-back-to-a-table-context +static void clear_stack_to_table_context(GumboParser* parser) { + while (!node_tag_in_set(get_current_node(parser), + (gumbo_tagset){TAG(HTML), TAG(TABLE), TAG(TEMPLATE)})) { + pop_current_node(parser); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#clear-the-stack-back-to-a-table-body-context +void clear_stack_to_table_body_context(GumboParser* parser) { + while (!node_tag_in_set(get_current_node(parser), + (gumbo_tagset){TAG(HTML), TAG(TBODY), TAG(TFOOT), TAG(THEAD), + TAG(TEMPLATE)})) { + pop_current_node(parser); + } +} + +// Creates a parser-inserted element in the HTML namespace and returns it. +static GumboNode* create_element(GumboParser* parser, GumboTag tag) { + GumboNode* node = create_node(parser, GUMBO_NODE_ELEMENT); + GumboElement* element = &node->v.element; + gumbo_vector_init(parser, 1, &element->children); + gumbo_vector_init(parser, 0, &element->attributes); + element->tag = tag; + element->tag_namespace = GUMBO_NAMESPACE_HTML; + element->original_tag = kGumboEmptyString; + element->original_end_tag = kGumboEmptyString; + element->start_pos = (parser->_parser_state->_current_token) + ? parser->_parser_state->_current_token->position + : kGumboEmptySourcePosition; + element->end_pos = kGumboEmptySourcePosition; + return node; +} + +// Constructs an element from the given start tag token. +static GumboNode* create_element_from_token( + GumboParser* parser, GumboToken* token, GumboNamespaceEnum tag_namespace) { + assert(token->type == GUMBO_TOKEN_START_TAG); + GumboTokenStartTag* start_tag = &token->v.start_tag; + + GumboNodeType type = (tag_namespace == GUMBO_NAMESPACE_HTML && + start_tag->tag == GUMBO_TAG_TEMPLATE) + ? GUMBO_NODE_TEMPLATE + : GUMBO_NODE_ELEMENT; + + GumboNode* node = create_node(parser, type); + GumboElement* element = &node->v.element; + gumbo_vector_init(parser, 1, &element->children); + element->attributes = start_tag->attributes; + element->tag = start_tag->tag; + element->tag_namespace = tag_namespace; + + assert(token->original_text.length >= 2); + assert(token->original_text.data[0] == '<'); + assert(token->original_text.data[token->original_text.length - 1] == '>'); + element->original_tag = token->original_text; + element->start_pos = token->position; + element->original_end_tag = kGumboEmptyString; + element->end_pos = kGumboEmptySourcePosition; + + // The element takes ownership of the attributes from the token, so any + // allocated-memory fields should be nulled out. + start_tag->attributes = kGumboEmptyVector; + return node; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#insert-an-html-element +static void insert_element(GumboParser* parser, GumboNode* node, + bool is_reconstructing_formatting_elements) { + GumboParserState* state = parser->_parser_state; + // NOTE(jdtang): The text node buffer must always be flushed before inserting + // a node, otherwise we're handling nodes in a different order than the spec + // mandated. However, one clause of the spec (character tokens in the body) + // requires that we reconstruct the active formatting elements *before* adding + // the character, and reconstructing the active formatting elements may itself + // result in the insertion of new elements (which should be pushed onto the + // stack of open elements before the buffer is flushed). We solve this (for + // the time being, the spec has been rewritten for <template> and the new + // version may be simpler here) with a boolean flag to this method. + if (!is_reconstructing_formatting_elements) { + maybe_flush_text_node_buffer(parser); + } + InsertionLocation location = get_appropriate_insertion_location(parser, NULL); + insert_node(parser, node, location); + gumbo_vector_add(parser, (void*) node, &state->_open_elements); +} + +// Convenience method that combines create_element_from_token and +// insert_element, inserting the generated element directly into the current +// node. Returns the node inserted. +static GumboNode* insert_element_from_token( + GumboParser* parser, GumboToken* token) { + GumboNode* element = + create_element_from_token(parser, token, GUMBO_NAMESPACE_HTML); + insert_element(parser, element, false); + gumbo_debug("Inserting <%s> element (@%x) from token.\n", + gumbo_normalized_tagname(element->v.element.tag), element); + return element; +} + +// Convenience method that combines create_element and insert_element, inserting +// a parser-generated element of a specific tag type. Returns the node +// inserted. +static GumboNode* insert_element_of_tag_type( + GumboParser* parser, GumboTag tag, GumboParseFlags reason) { + GumboNode* element = create_element(parser, tag); + element->parse_flags |= GUMBO_INSERTION_BY_PARSER | reason; + insert_element(parser, element, false); + gumbo_debug("Inserting %s element (@%x) from tag type.\n", + gumbo_normalized_tagname(tag), element); + return element; +} + +// Convenience method for creating foreign namespaced element. Returns the node +// inserted. +static GumboNode* insert_foreign_element( + GumboParser* parser, GumboToken* token, GumboNamespaceEnum tag_namespace) { + assert(token->type == GUMBO_TOKEN_START_TAG); + GumboNode* element = create_element_from_token(parser, token, tag_namespace); + insert_element(parser, element, false); + if (token_has_attribute(token, "xmlns") && + !attribute_matches_case_sensitive(&token->v.start_tag.attributes, "xmlns", + kLegalXmlns[tag_namespace])) { + // TODO(jdtang): Since there're multiple possible error codes here, we + // eventually need reason codes to differentiate them. + parser_add_parse_error(parser, token); + } + if (token_has_attribute(token, "xmlns:xlink") && + !attribute_matches_case_sensitive(&token->v.start_tag.attributes, + "xmlns:xlink", "http://www.w3.org/1999/xlink")) { + parser_add_parse_error(parser, token); + } + return element; +} + +static void insert_text_token(GumboParser* parser, GumboToken* token) { + assert(token->type == GUMBO_TOKEN_WHITESPACE || + token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_NULL || token->type == GUMBO_TOKEN_CDATA); + TextNodeBufferState* buffer_state = &parser->_parser_state->_text_node; + if (buffer_state->_buffer.length == 0) { + // Initialize position fields. + buffer_state->_start_original_text = token->original_text.data; + buffer_state->_start_position = token->position; + } + gumbo_string_buffer_append_codepoint( + parser, token->v.character, &buffer_state->_buffer); + if (token->type == GUMBO_TOKEN_CHARACTER) { + buffer_state->_type = GUMBO_NODE_TEXT; + } else if (token->type == GUMBO_TOKEN_CDATA) { + buffer_state->_type = GUMBO_NODE_CDATA; + } + gumbo_debug("Inserting text token '%c'.\n", token->v.character); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#generic-rcdata-element-parsing-algorithm +static void run_generic_parsing_algorithm( + GumboParser* parser, GumboToken* token, GumboTokenizerEnum lexer_state) { + insert_element_from_token(parser, token); + gumbo_tokenizer_set_state(parser, lexer_state); + parser->_parser_state->_original_insertion_mode = + parser->_parser_state->_insertion_mode; + parser->_parser_state->_insertion_mode = GUMBO_INSERTION_MODE_TEXT; +} + +static void acknowledge_self_closing_tag(GumboParser* parser) { + parser->_parser_state->_self_closing_flag_acknowledged = true; +} + +// Returns true if there's an anchor tag in the list of active formatting +// elements, and fills in its index if so. +static bool find_last_anchor_index(GumboParser* parser, int* anchor_index) { + GumboVector* elements = &parser->_parser_state->_active_formatting_elements; + for (int i = elements->length; --i >= 0;) { + GumboNode* node = elements->data[i]; + if (node == &kActiveFormattingScopeMarker) { + return false; + } + if (node_html_tag_is(node, GUMBO_TAG_A)) { + *anchor_index = i; + return true; + } + } + return false; +} + +// Counts the number of open formatting elements in the list of active +// formatting elements (after the last active scope marker) that have a specific +// tag. If this is > 0, then earliest_matching_index will be filled in with the +// index of the first such element. +static int count_formatting_elements_of_tag(GumboParser* parser, + const GumboNode* desired_node, int* earliest_matching_index) { + const GumboElement* desired_element = &desired_node->v.element; + GumboVector* elements = &parser->_parser_state->_active_formatting_elements; + int num_identical_elements = 0; + for (int i = elements->length; --i >= 0;) { + GumboNode* node = elements->data[i]; + if (node == &kActiveFormattingScopeMarker) { + break; + } + assert(node->type == GUMBO_NODE_ELEMENT); + if (node_qualified_tag_is( + node, desired_element->tag_namespace, desired_element->tag) && + all_attributes_match( + &node->v.element.attributes, &desired_element->attributes)) { + num_identical_elements++; + *earliest_matching_index = i; + } + } + return num_identical_elements; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#reconstruct-the-active-formatting-elements +static void add_formatting_element(GumboParser* parser, const GumboNode* node) { + assert(node == &kActiveFormattingScopeMarker || + node->type == GUMBO_NODE_ELEMENT); + GumboVector* elements = &parser->_parser_state->_active_formatting_elements; + if (node == &kActiveFormattingScopeMarker) { + gumbo_debug("Adding a scope marker.\n"); + } else { + gumbo_debug("Adding a formatting element.\n"); + } + + // Hunt for identical elements. + int earliest_identical_element = elements->length; + int num_identical_elements = count_formatting_elements_of_tag( + parser, node, &earliest_identical_element); + + // Noah's Ark clause: if there're at least 3, remove the earliest. + if (num_identical_elements >= 3) { + gumbo_debug("Noah's ark clause: removing element at %d.\n", + earliest_identical_element); + gumbo_vector_remove_at(parser, earliest_identical_element, elements); + } + + gumbo_vector_add(parser, (void*) node, elements); +} + +static bool is_open_element(GumboParser* parser, const GumboNode* node) { + GumboVector* open_elements = &parser->_parser_state->_open_elements; + for (unsigned int i = 0; i < open_elements->length; ++i) { + if (open_elements->data[i] == node) { + return true; + } + } + return false; +} + +// Clones attributes, tags, etc. of a node, but does not copy the content. The +// clone shares no structure with the original node: all owned strings and +// values are fresh copies. +GumboNode* clone_node( + GumboParser* parser, GumboNode* node, GumboParseFlags reason) { + assert(node->type == GUMBO_NODE_ELEMENT || node->type == GUMBO_NODE_TEMPLATE); + GumboNode* new_node = gumbo_parser_allocate(parser, sizeof(GumboNode)); + *new_node = *node; + new_node->parent = NULL; + new_node->index_within_parent = -1; + // Clear the GUMBO_INSERTION_IMPLICIT_END_TAG flag, as the cloned node may + // have a separate end tag. + new_node->parse_flags &= ~GUMBO_INSERTION_IMPLICIT_END_TAG; + new_node->parse_flags |= reason | GUMBO_INSERTION_BY_PARSER; + GumboElement* element = &new_node->v.element; + gumbo_vector_init(parser, 1, &element->children); + + const GumboVector* old_attributes = &node->v.element.attributes; + gumbo_vector_init(parser, old_attributes->length, &element->attributes); + for (unsigned int i = 0; i < old_attributes->length; ++i) { + const GumboAttribute* old_attr = old_attributes->data[i]; + GumboAttribute* attr = + gumbo_parser_allocate(parser, sizeof(GumboAttribute)); + *attr = *old_attr; + attr->name = gumbo_copy_stringz(parser, old_attr->name); + attr->value = gumbo_copy_stringz(parser, old_attr->value); + gumbo_vector_add(parser, attr, &element->attributes); + } + return new_node; +} + +// "Reconstruct active formatting elements" part of the spec. +// This implementation is based on the html5lib translation from the mess of +// GOTOs in the spec to reasonably structured programming. +// http://code.google.com/p/html5lib/source/browse/python/html5lib/treebuilders/_base.py +static void reconstruct_active_formatting_elements(GumboParser* parser) { + GumboVector* elements = &parser->_parser_state->_active_formatting_elements; + // Step 1 + if (elements->length == 0) { + return; + } + + // Step 2 & 3 + unsigned int i = elements->length - 1; + GumboNode* element = elements->data[i]; + if (element == &kActiveFormattingScopeMarker || + is_open_element(parser, element)) { + return; + } + + // Step 6 + do { + if (i == 0) { + // Step 4 + i = -1; // Incremented to 0 below. + break; + } + // Step 5 + element = elements->data[--i]; + } while (element != &kActiveFormattingScopeMarker && + !is_open_element(parser, element)); + + ++i; + gumbo_debug("Reconstructing elements from %d on %s parent.\n", i, + gumbo_normalized_tagname(get_current_node(parser)->v.element.tag)); + for (; i < elements->length; ++i) { + // Step 7 & 8. + assert(elements->length > 0); + assert(i < elements->length); + element = elements->data[i]; + assert(element != &kActiveFormattingScopeMarker); + GumboNode* clone = clone_node( + parser, element, GUMBO_INSERTION_RECONSTRUCTED_FORMATTING_ELEMENT); + // Step 9. + InsertionLocation location = + get_appropriate_insertion_location(parser, NULL); + insert_node(parser, clone, location); + gumbo_vector_add( + parser, (void*) clone, &parser->_parser_state->_open_elements); + + // Step 10. + elements->data[i] = clone; + gumbo_debug("Reconstructed %s element at %d.\n", + gumbo_normalized_tagname(clone->v.element.tag), i); + } +} + +static void clear_active_formatting_elements(GumboParser* parser) { + GumboVector* elements = &parser->_parser_state->_active_formatting_elements; + int num_elements_cleared = 0; + const GumboNode* node; + do { + node = gumbo_vector_pop(parser, elements); + ++num_elements_cleared; + } while (node && node != &kActiveFormattingScopeMarker); + gumbo_debug("Cleared %d elements from active formatting list.\n", + num_elements_cleared); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#the-initial-insertion-mode +static GumboQuirksModeEnum compute_quirks_mode( + const GumboTokenDocType* doctype) { + if (doctype->force_quirks || strcmp(doctype->name, kDoctypeHtml.data) || + is_in_static_list( + doctype->public_identifier, kQuirksModePublicIdPrefixes, false) || + is_in_static_list( + doctype->public_identifier, kQuirksModePublicIdExactMatches, true) || + is_in_static_list( + doctype->system_identifier, kQuirksModeSystemIdExactMatches, true) || + (is_in_static_list(doctype->public_identifier, + kLimitedQuirksRequiresSystemIdPublicIdPrefixes, false) && + !doctype->has_system_identifier)) { + return GUMBO_DOCTYPE_QUIRKS; + } else if (is_in_static_list(doctype->public_identifier, + kLimitedQuirksPublicIdPrefixes, false) || + (is_in_static_list(doctype->public_identifier, + kLimitedQuirksRequiresSystemIdPublicIdPrefixes, false) && + doctype->has_system_identifier)) { + return GUMBO_DOCTYPE_LIMITED_QUIRKS; + } + return GUMBO_DOCTYPE_NO_QUIRKS; +} + +// The following functions are all defined by the "has an element in __ scope" +// sections of the HTML5 spec: +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#has-an-element-in-the-specific-scope +// The basic idea behind them is that they check for an element of the given +// qualified name, contained within a scope formed by a set of other qualified +// names. For example, "has an element in list scope" looks for an element of +// the given qualified name within the nearest enclosing <ol> or <ul>, along +// with a bunch of generic element types that serve to "firewall" their content +// from the rest of the document. Note that because of the way the spec is +// written, +// all elements are expected to be in the HTML namespace +static bool has_an_element_in_specific_scope(GumboParser* parser, + int expected_size, const GumboTag* expected, bool negate, + const gumbo_tagset tags) { + GumboVector* open_elements = &parser->_parser_state->_open_elements; + for (int i = open_elements->length; --i >= 0;) { + const GumboNode* node = open_elements->data[i]; + if (node->type != GUMBO_NODE_ELEMENT && node->type != GUMBO_NODE_TEMPLATE) + continue; + + GumboTag node_tag = node->v.element.tag; + GumboNamespaceEnum node_ns = node->v.element.tag_namespace; + for (int j = 0; j < expected_size; ++j) { + if (node_tag == expected[j] && node_ns == GUMBO_NAMESPACE_HTML) + return true; + } + + bool found = TAGSET_INCLUDES(tags, node_ns, node_tag); + if (negate != found) return false; + } + return false; +} + +// Checks for the presence of an open element of the specified tag type. +static bool has_open_element(GumboParser* parser, GumboTag tag) { + return has_an_element_in_specific_scope( + parser, 1, &tag, false, (gumbo_tagset){TAG(HTML)}); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#has-an-element-in-scope +static bool has_an_element_in_scope(GumboParser* parser, GumboTag tag) { + return has_an_element_in_specific_scope(parser, 1, &tag, false, + (gumbo_tagset){TAG(APPLET), TAG(CAPTION), TAG(HTML), TAG(TABLE), TAG(TD), + TAG(TH), TAG(MARQUEE), TAG(OBJECT), TAG(TEMPLATE), TAG_MATHML(MI), + TAG_MATHML(MO), TAG_MATHML(MN), TAG_MATHML(MS), TAG_MATHML(MTEXT), + TAG_MATHML(ANNOTATION_XML), TAG_SVG(FOREIGNOBJECT), TAG_SVG(DESC), + TAG_SVG(TITLE)}); +} + +// Like "has an element in scope", but for the specific case of looking for a +// unique target node, not for any node with a given tag name. This duplicates +// much of the algorithm from has_an_element_in_specific_scope because the +// predicate is different when checking for an exact node, and it's easier & +// faster just to duplicate the code for this one case than to try and +// parameterize it. +static bool has_node_in_scope(GumboParser* parser, const GumboNode* node) { + GumboVector* open_elements = &parser->_parser_state->_open_elements; + for (int i = open_elements->length; --i >= 0;) { + const GumboNode* current = open_elements->data[i]; + if (current == node) { + return true; + } + if (current->type != GUMBO_NODE_ELEMENT && + current->type != GUMBO_NODE_TEMPLATE) { + continue; + } + if (node_tag_in_set(current, + (gumbo_tagset){TAG(APPLET), TAG(CAPTION), TAG(HTML), TAG(TABLE), + TAG(TD), TAG(TH), TAG(MARQUEE), TAG(OBJECT), TAG(TEMPLATE), + TAG_MATHML(MI), TAG_MATHML(MO), TAG_MATHML(MN), TAG_MATHML(MS), + TAG_MATHML(MTEXT), TAG_MATHML(ANNOTATION_XML), + TAG_SVG(FOREIGNOBJECT), TAG_SVG(DESC), TAG_SVG(TITLE)})) { + return false; + } + } + assert(false); + return false; +} + +// Like has_an_element_in_scope, but restricts the expected qualified name to a +// range of possible qualified names instead of just a single one. +static bool has_an_element_in_scope_with_tagname( + GumboParser* parser, int expected_len, const GumboTag expected[]) { + return has_an_element_in_specific_scope(parser, expected_len, expected, false, + (gumbo_tagset){TAG(APPLET), TAG(CAPTION), TAG(HTML), TAG(TABLE), TAG(TD), + TAG(TH), TAG(MARQUEE), TAG(OBJECT), TAG(TEMPLATE), TAG_MATHML(MI), + TAG_MATHML(MO), TAG_MATHML(MN), TAG_MATHML(MS), TAG_MATHML(MTEXT), + TAG_MATHML(ANNOTATION_XML), TAG_SVG(FOREIGNOBJECT), TAG_SVG(DESC), + TAG_SVG(TITLE)}); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#has-an-element-in-list-item-scope +static bool has_an_element_in_list_scope(GumboParser* parser, GumboTag tag) { + return has_an_element_in_specific_scope(parser, 1, &tag, false, + (gumbo_tagset){TAG(APPLET), TAG(CAPTION), TAG(HTML), TAG(TABLE), TAG(TD), + TAG(TH), TAG(MARQUEE), TAG(OBJECT), TAG(TEMPLATE), TAG_MATHML(MI), + TAG_MATHML(MO), TAG_MATHML(MN), TAG_MATHML(MS), TAG_MATHML(MTEXT), + TAG_MATHML(ANNOTATION_XML), TAG_SVG(FOREIGNOBJECT), TAG_SVG(DESC), + TAG_SVG(TITLE), TAG(OL), TAG(UL)}); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#has-an-element-in-button-scope +static bool has_an_element_in_button_scope(GumboParser* parser, GumboTag tag) { + return has_an_element_in_specific_scope(parser, 1, &tag, false, + (gumbo_tagset){TAG(APPLET), TAG(CAPTION), TAG(HTML), TAG(TABLE), TAG(TD), + TAG(TH), TAG(MARQUEE), TAG(OBJECT), TAG(TEMPLATE), TAG_MATHML(MI), + TAG_MATHML(MO), TAG_MATHML(MN), TAG_MATHML(MS), TAG_MATHML(MTEXT), + TAG_MATHML(ANNOTATION_XML), TAG_SVG(FOREIGNOBJECT), TAG_SVG(DESC), + TAG_SVG(TITLE), TAG(BUTTON)}); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#has-an-element-in-table-scope +static bool has_an_element_in_table_scope(GumboParser* parser, GumboTag tag) { + return has_an_element_in_specific_scope(parser, 1, &tag, false, + (gumbo_tagset){TAG(HTML), TAG(TABLE), TAG(TEMPLATE)}); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#has-an-element-in-select-scope +static bool has_an_element_in_select_scope(GumboParser* parser, GumboTag tag) { + return has_an_element_in_specific_scope( + parser, 1, &tag, true, (gumbo_tagset){TAG(OPTGROUP), TAG(OPTION)}); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#generate-implied-end-tags +// "exception" is the "element to exclude from the process" listed in the spec. +// Pass GUMBO_TAG_LAST to not exclude any of them. +static void generate_implied_end_tags(GumboParser* parser, GumboTag exception) { + for (; node_tag_in_set(get_current_node(parser), + (gumbo_tagset){TAG(DD), TAG(DT), TAG(LI), TAG(OPTION), + TAG(OPTGROUP), TAG(P), TAG(RP), TAG(RB), TAG(RT), TAG(RTC)}) && + !node_html_tag_is(get_current_node(parser), exception); + pop_current_node(parser)) + ; +} + +// This is the "generate all implied end tags thoroughly" clause of the spec. +// https://html.spec.whatwg.org/multipage/syntax.html#closing-elements-that-have-implied-end-tags +static void generate_all_implied_end_tags_thoroughly(GumboParser* parser) { + for ( + ; node_tag_in_set(get_current_node(parser), + (gumbo_tagset){TAG(CAPTION), TAG(COLGROUP), TAG(DD), TAG(DT), TAG(LI), + TAG(OPTION), TAG(OPTGROUP), TAG(P), TAG(RP), TAG(RT), TAG(RTC), + TAG(TBODY), TAG(TD), TAG(TFOOT), TAG(TH), TAG(HEAD), TAG(TR)}); + pop_current_node(parser)) + ; +} + +// This factors out the clauses relating to "act as if an end tag token with tag +// name "table" had been seen. Returns true if there's a table element in table +// scope which was successfully closed, false if not and the token should be +// ignored. Does not add parse errors; callers should handle that. +static bool close_table(GumboParser* parser) { + if (!has_an_element_in_table_scope(parser, GUMBO_TAG_TABLE)) { + return false; + } + + GumboNode* node = pop_current_node(parser); + while (!node_html_tag_is(node, GUMBO_TAG_TABLE)) { + node = pop_current_node(parser); + } + reset_insertion_mode_appropriately(parser); + return true; +} + +// This factors out the clauses relating to "act as if an end tag token with tag +// name `cell_tag` had been seen". +static bool close_table_cell( + GumboParser* parser, const GumboToken* token, GumboTag cell_tag) { + bool result = true; + generate_implied_end_tags(parser, GUMBO_TAG_LAST); + const GumboNode* node = get_current_node(parser); + if (!node_html_tag_is(node, cell_tag)) { + parser_add_parse_error(parser, token); + result = false; + } + do { + node = pop_current_node(parser); + } while (!node_html_tag_is(node, cell_tag)); + + clear_active_formatting_elements(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_ROW); + return result; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#close-the-cell +// This holds the logic to determine whether we should close a <td> or a <th>. +static bool close_current_cell(GumboParser* parser, const GumboToken* token) { + if (has_an_element_in_table_scope(parser, GUMBO_TAG_TD)) { + assert(!has_an_element_in_table_scope(parser, GUMBO_TAG_TH)); + return close_table_cell(parser, token, GUMBO_TAG_TD); + } else { + assert(has_an_element_in_table_scope(parser, GUMBO_TAG_TH)); + return close_table_cell(parser, token, GUMBO_TAG_TH); + } +} + +// This factors out the "act as if an end tag of tag name 'select' had been +// seen" clause of the spec, since it's referenced in several places. It pops +// all nodes from the stack until the current <select> has been closed, then +// resets the insertion mode appropriately. +static void close_current_select(GumboParser* parser) { + GumboNode* node = pop_current_node(parser); + while (!node_html_tag_is(node, GUMBO_TAG_SELECT)) { + node = pop_current_node(parser); + } + reset_insertion_mode_appropriately(parser); +} + +// The list of nodes in the "special" category: +// http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#special +static bool is_special_node(const GumboNode* node) { + assert(node->type == GUMBO_NODE_ELEMENT || node->type == GUMBO_NODE_TEMPLATE); + return node_tag_in_set(node, + (gumbo_tagset){TAG(ADDRESS), TAG(APPLET), TAG(AREA), TAG(ARTICLE), + TAG(ASIDE), TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), TAG(BLOCKQUOTE), + TAG(BODY), TAG(BR), TAG(BUTTON), TAG(CAPTION), TAG(CENTER), TAG(COL), + TAG(COLGROUP), TAG(MENUITEM), TAG(DD), TAG(DETAILS), TAG(DIR), + TAG(DIV), TAG(DL), TAG(DT), TAG(EMBED), TAG(FIELDSET), + TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(FORM), TAG(FRAME), + TAG(FRAMESET), TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5), TAG(H6), + TAG(HEAD), TAG(HEADER), TAG(HGROUP), TAG(HR), TAG(HTML), TAG(IFRAME), + TAG(IMG), TAG(INPUT), TAG(ISINDEX), TAG(LI), TAG(LINK), TAG(LISTING), + TAG(MARQUEE), TAG(MENU), TAG(META), TAG(NAV), TAG(NOEMBED), + TAG(NOFRAMES), TAG(NOSCRIPT), TAG(OBJECT), TAG(OL), TAG(P), + TAG(PARAM), TAG(PLAINTEXT), TAG(PRE), TAG(SCRIPT), TAG(SECTION), + TAG(SELECT), TAG(STYLE), TAG(SUMMARY), TAG(TABLE), TAG(TBODY), + TAG(TD), TAG(TEMPLATE), TAG(TEXTAREA), TAG(TFOOT), TAG(TH), + TAG(THEAD), TAG(TITLE), TAG(TR), TAG(UL), TAG(WBR), TAG(XMP), + + TAG_MATHML(MI), TAG_MATHML(MO), TAG_MATHML(MN), TAG_MATHML(MS), + TAG_MATHML(MTEXT), TAG_MATHML(ANNOTATION_XML), + + TAG_SVG(FOREIGNOBJECT), TAG_SVG(DESC)}); +} + +// Implicitly closes currently open elements until it reaches an element with +// the +// specified qualified name. If the elements closed are in the set handled by +// generate_implied_end_tags, this is normal operation and this function returns +// true. Otherwise, a parse error is recorded and this function returns false. +static bool implicitly_close_tags(GumboParser* parser, GumboToken* token, + GumboNamespaceEnum target_ns, GumboTag target) { + bool result = true; + generate_implied_end_tags(parser, target); + if (!node_qualified_tag_is(get_current_node(parser), target_ns, target)) { + parser_add_parse_error(parser, token); + while ( + !node_qualified_tag_is(get_current_node(parser), target_ns, target)) { + pop_current_node(parser); + } + result = false; + } + assert(node_qualified_tag_is(get_current_node(parser), target_ns, target)); + pop_current_node(parser); + return result; +} + +// If the stack of open elements has a <p> tag in button scope, this acts as if +// a </p> tag was encountered, implicitly closing tags. Returns false if a +// parse error occurs. This is a convenience function because this particular +// clause appears several times in the spec. +static bool maybe_implicitly_close_p_tag( + GumboParser* parser, GumboToken* token) { + if (has_an_element_in_button_scope(parser, GUMBO_TAG_P)) { + return implicitly_close_tags( + parser, token, GUMBO_NAMESPACE_HTML, GUMBO_TAG_P); + } + return true; +} + +// Convenience function to encapsulate the logic for closing <li> or <dd>/<dt> +// tags. Pass true to is_li for handling <li> tags, false for <dd> and <dt>. +static void maybe_implicitly_close_list_tag( + GumboParser* parser, GumboToken* token, bool is_li) { + GumboParserState* state = parser->_parser_state; + state->_frameset_ok = false; + for (int i = state->_open_elements.length; --i >= 0;) { + const GumboNode* node = state->_open_elements.data[i]; + bool is_list_tag = + is_li ? node_html_tag_is(node, GUMBO_TAG_LI) + : node_tag_in_set(node, (gumbo_tagset){TAG(DD), TAG(DT)}); + if (is_list_tag) { + implicitly_close_tags( + parser, token, node->v.element.tag_namespace, node->v.element.tag); + return; + } + if (is_special_node(node) && + !node_tag_in_set( + node, (gumbo_tagset){TAG(ADDRESS), TAG(DIV), TAG(P)})) { + return; + } + } +} + +static void merge_attributes( + GumboParser* parser, GumboToken* token, GumboNode* node) { + assert(token->type == GUMBO_TOKEN_START_TAG); + assert(node->type == GUMBO_NODE_ELEMENT); + const GumboVector* token_attr = &token->v.start_tag.attributes; + GumboVector* node_attr = &node->v.element.attributes; + + for (unsigned int i = 0; i < token_attr->length; ++i) { + GumboAttribute* attr = token_attr->data[i]; + if (!gumbo_get_attribute(node_attr, attr->name)) { + // Ownership of the attribute is transferred by this gumbo_vector_add, + // so it has to be nulled out of the original token so it doesn't get + // double-deleted. + gumbo_vector_add(parser, attr, node_attr); + token_attr->data[i] = NULL; + } + } + // When attributes are merged, it means the token has been ignored and merged + // with another token, so we need to free its memory. The attributes that are + // transferred need to be nulled-out in the vector above so that they aren't + // double-deleted. + gumbo_token_destroy(parser, token); + +#ifndef NDEBUG + // Mark this sentinel so the assertion in the main loop knows it's been + // destroyed. + token->v.start_tag.attributes = kGumboEmptyVector; +#endif +} + +const char* gumbo_normalize_svg_tagname(const GumboStringPiece* tag) { + for (size_t i = 0; i < sizeof(kSvgTagReplacements) / sizeof(ReplacementEntry); + ++i) { + const ReplacementEntry* entry = &kSvgTagReplacements[i]; + if (gumbo_string_equals_ignore_case(tag, &entry->from)) { + return entry->to.data; + } + } + return NULL; +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adjust-foreign-attributes +// This destructively modifies any matching attributes on the token and sets the +// namespace appropriately. +static void adjust_foreign_attributes(GumboParser* parser, GumboToken* token) { + assert(token->type == GUMBO_TOKEN_START_TAG); + const GumboVector* attributes = &token->v.start_tag.attributes; + for (size_t i = 0; i < sizeof(kForeignAttributeReplacements) / + sizeof(NamespacedAttributeReplacement); + ++i) { + const NamespacedAttributeReplacement* entry = + &kForeignAttributeReplacements[i]; + GumboAttribute* attr = gumbo_get_attribute(attributes, entry->from); + if (!attr) { + continue; + } + gumbo_parser_deallocate(parser, (void*) attr->name); + attr->attr_namespace = entry->attr_namespace; + attr->name = gumbo_copy_stringz(parser, entry->local_name); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#adjust-svg-attributes +// This destructively modifies any matching attributes on the token. +static void adjust_svg_attributes(GumboParser* parser, GumboToken* token) { + assert(token->type == GUMBO_TOKEN_START_TAG); + const GumboVector* attributes = &token->v.start_tag.attributes; + for (size_t i = 0; + i < sizeof(kSvgAttributeReplacements) / sizeof(ReplacementEntry); ++i) { + const ReplacementEntry* entry = &kSvgAttributeReplacements[i]; + GumboAttribute* attr = gumbo_get_attribute(attributes, entry->from.data); + if (!attr) { + continue; + } + gumbo_parser_deallocate(parser, (void*) attr->name); + attr->name = gumbo_copy_stringz(parser, entry->to.data); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#adjust-mathml-attributes +// Note that this may destructively modify the token with the new attribute +// value. +static void adjust_mathml_attributes(GumboParser* parser, GumboToken* token) { + assert(token->type == GUMBO_TOKEN_START_TAG); + GumboAttribute* attr = + gumbo_get_attribute(&token->v.start_tag.attributes, "definitionurl"); + if (!attr) { + return; + } + gumbo_parser_deallocate(parser, (void*) attr->name); + attr->name = gumbo_copy_stringz(parser, "definitionURL"); +} + +static bool doctype_matches(const GumboTokenDocType* doctype, + const GumboStringPiece* public_id, const GumboStringPiece* system_id, + bool allow_missing_system_id) { + return !strcmp(doctype->public_identifier, public_id->data) && + (allow_missing_system_id || doctype->has_system_identifier) && + !strcmp(doctype->system_identifier, system_id->data); +} + +static bool maybe_add_doctype_error( + GumboParser* parser, const GumboToken* token) { + const GumboTokenDocType* doctype = &token->v.doc_type; + bool html_doctype = !strcmp(doctype->name, kDoctypeHtml.data); + if ((!html_doctype || doctype->has_public_identifier || + (doctype->has_system_identifier && + !strcmp( + doctype->system_identifier, kSystemIdLegacyCompat.data))) && + !(html_doctype && (doctype_matches(doctype, &kPublicIdHtml4_0, + &kSystemIdRecHtml4_0, true) || + doctype_matches(doctype, &kPublicIdHtml4_01, + &kSystemIdHtml4, true) || + doctype_matches(doctype, &kPublicIdXhtml1_0, + &kSystemIdXhtmlStrict1_1, false) || + doctype_matches(doctype, &kPublicIdXhtml1_1, + &kSystemIdXhtml1_1, false)))) { + parser_add_parse_error(parser, token); + return false; + } + return true; +} + +static void remove_from_parent(GumboParser* parser, GumboNode* node) { + if (!node->parent) { + // The node may not have a parent if, for example, it is a newly-cloned copy + // of an active formatting element. DOM manipulations continue with the + // orphaned fragment of the DOM tree until it's appended/foster-parented to + // the common ancestor at the end of the adoption agency algorithm. + return; + } + assert(node->parent->type == GUMBO_NODE_ELEMENT); + GumboVector* children = &node->parent->v.element.children; + int index = gumbo_vector_index_of(children, node); + assert(index != -1); + + gumbo_vector_remove_at(parser, index, children); + node->parent = NULL; + node->index_within_parent = -1; + for (unsigned int i = index; i < children->length; ++i) { + GumboNode* child = children->data[i]; + child->index_within_parent = i; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#an-introduction-to-error-handling-and-strange-cases-in-the-parser +// Also described in the "in body" handling for end formatting tags. +static bool adoption_agency_algorithm( + GumboParser* parser, GumboToken* token, GumboTag subject) { + GumboParserState* state = parser->_parser_state; + gumbo_debug("Entering adoption agency algorithm.\n"); + // Step 1. + GumboNode* current_node = get_current_node(parser); + if (current_node->v.element.tag_namespace == GUMBO_NAMESPACE_HTML && + current_node->v.element.tag == subject && + gumbo_vector_index_of( + &state->_active_formatting_elements, current_node) == -1) { + pop_current_node(parser); + return false; + } + // Steps 2-4 & 20: + for (unsigned int i = 0; i < 8; ++i) { + // Step 5. + GumboNode* formatting_node = NULL; + int formatting_node_in_open_elements = -1; + for (int j = state->_active_formatting_elements.length; --j >= 0;) { + GumboNode* current_node = state->_active_formatting_elements.data[j]; + if (current_node == &kActiveFormattingScopeMarker) { + gumbo_debug("Broke on scope marker; aborting.\n"); + // Last scope marker; abort the algorithm. + return false; + } + if (node_html_tag_is(current_node, subject)) { + // Found it. + formatting_node = current_node; + formatting_node_in_open_elements = + gumbo_vector_index_of(&state->_open_elements, formatting_node); + gumbo_debug("Formatting element of tag %s at %d.\n", + gumbo_normalized_tagname(subject), + formatting_node_in_open_elements); + break; + } + } + if (!formatting_node) { + // No matching tag; not a parse error outright, but fall through to the + // "any other end tag" clause (which may potentially add a parse error, + // but not always). + gumbo_debug("No active formatting elements; aborting.\n"); + return false; + } + + // Step 6 + if (formatting_node_in_open_elements == -1) { + gumbo_debug("Formatting node not on stack of open elements.\n"); + parser_add_parse_error(parser, token); + gumbo_vector_remove( + parser, formatting_node, &state->_active_formatting_elements); + return false; + } + + // Step 7 + if (!has_an_element_in_scope(parser, formatting_node->v.element.tag)) { + parser_add_parse_error(parser, token); + gumbo_debug("Element not in scope.\n"); + return false; + } + + // Step 8 + if (formatting_node != get_current_node(parser)) { + parser_add_parse_error(parser, token); // But continue onwards. + } + assert(formatting_node); + assert(!node_html_tag_is(formatting_node, GUMBO_TAG_HTML)); + assert(!node_html_tag_is(formatting_node, GUMBO_TAG_BODY)); + + // Step 9 & 10 + GumboNode* furthest_block = NULL; + for (unsigned int j = formatting_node_in_open_elements; + j < state->_open_elements.length; ++j) { + assert(j > 0); + GumboNode* current = state->_open_elements.data[j]; + if (is_special_node(current)) { + // Step 9. + furthest_block = current; + break; + } + } + if (!furthest_block) { + // Step 10. + while (get_current_node(parser) != formatting_node) { + pop_current_node(parser); + } + // And the formatting element itself. + pop_current_node(parser); + gumbo_vector_remove( + parser, formatting_node, &state->_active_formatting_elements); + return false; + } + assert(!node_html_tag_is(furthest_block, GUMBO_TAG_HTML)); + assert(furthest_block); + + // Step 11. + // Elements may be moved and reparented by this algorithm, so + // common_ancestor is not necessarily the same as formatting_node->parent. + GumboNode* common_ancestor = + state->_open_elements.data[gumbo_vector_index_of(&state->_open_elements, + formatting_node) - + 1]; + gumbo_debug("Common ancestor tag = %s, furthest block tag = %s.\n", + gumbo_normalized_tagname(common_ancestor->v.element.tag), + gumbo_normalized_tagname(furthest_block->v.element.tag)); + + // Step 12. + int bookmark = gumbo_vector_index_of( + &state->_active_formatting_elements, formatting_node) + + 1; + gumbo_debug("Bookmark at %d.\n", bookmark); + // Step 13. + GumboNode* node = furthest_block; + GumboNode* last_node = furthest_block; + // Must be stored explicitly, in case node is removed from the stack of open + // elements, to handle step 9.4. + int saved_node_index = gumbo_vector_index_of(&state->_open_elements, node); + assert(saved_node_index > 0); + // Step 13.1. + for (int j = 0;;) { + // Step 13.2. + ++j; + // Step 13.3. + int node_index = gumbo_vector_index_of(&state->_open_elements, node); + gumbo_debug( + "Current index: %d, last index: %d.\n", node_index, saved_node_index); + if (node_index == -1) { + node_index = saved_node_index; + } + saved_node_index = --node_index; + assert(node_index > 0); + assert((unsigned int) node_index < state->_open_elements.capacity); + node = state->_open_elements.data[node_index]; + assert(node->parent); + if (node == formatting_node) { + // Step 13.4. + break; + } + int formatting_index = + gumbo_vector_index_of(&state->_active_formatting_elements, node); + if (j > 3 && formatting_index != -1) { + // Step 13.5. + gumbo_debug("Removing formatting element at %d.\n", formatting_index); + gumbo_vector_remove_at( + parser, formatting_index, &state->_active_formatting_elements); + // Removing the element shifts all indices over by one, so we may need + // to move the bookmark. + if (formatting_index < bookmark) { + --bookmark; + gumbo_debug("Moving bookmark to %d.\n", bookmark); + } + continue; + } + if (formatting_index == -1) { + // Step 13.6. + gumbo_vector_remove_at(parser, node_index, &state->_open_elements); + continue; + } + // Step 13.7. + // "common ancestor as the intended parent" doesn't actually mean insert + // it into the common ancestor; that happens below. + node = clone_node(parser, node, GUMBO_INSERTION_ADOPTION_AGENCY_CLONED); + assert(formatting_index >= 0); + state->_active_formatting_elements.data[formatting_index] = node; + assert(node_index >= 0); + state->_open_elements.data[node_index] = node; + // Step 13.8. + if (last_node == furthest_block) { + bookmark = formatting_index + 1; + gumbo_debug("Bookmark moved to %d.\n", bookmark); + assert((unsigned int) bookmark <= state->_active_formatting_elements.length); + } + // Step 13.9. + last_node->parse_flags |= GUMBO_INSERTION_ADOPTION_AGENCY_MOVED; + remove_from_parent(parser, last_node); + append_node(parser, node, last_node); + // Step 13.10. + last_node = node; + } // Step 13.11. + + // Step 14. + gumbo_debug("Removing %s node from parent ", + gumbo_normalized_tagname(last_node->v.element.tag)); + remove_from_parent(parser, last_node); + last_node->parse_flags |= GUMBO_INSERTION_ADOPTION_AGENCY_MOVED; + InsertionLocation location = + get_appropriate_insertion_location(parser, common_ancestor); + gumbo_debug("and inserting it into %s.\n", + gumbo_normalized_tagname(location.target->v.element.tag)); + insert_node(parser, last_node, location); + + // Step 15. + GumboNode* new_formatting_node = clone_node( + parser, formatting_node, GUMBO_INSERTION_ADOPTION_AGENCY_CLONED); + formatting_node->parse_flags |= GUMBO_INSERTION_IMPLICIT_END_TAG; + + // Step 16. Instead of appending nodes one-by-one, we swap the children + // vector of furthest_block with the empty children of new_formatting_node, + // reducing memory traffic and allocations. We still have to reset their + // parent pointers, though. + GumboVector temp = new_formatting_node->v.element.children; + new_formatting_node->v.element.children = + furthest_block->v.element.children; + furthest_block->v.element.children = temp; + + temp = new_formatting_node->v.element.children; + for (unsigned int i = 0; i < temp.length; ++i) { + GumboNode* child = temp.data[i]; + child->parent = new_formatting_node; + } + + // Step 17. + append_node(parser, furthest_block, new_formatting_node); + + // Step 18. + // If the formatting node was before the bookmark, it may shift over all + // indices after it, so we need to explicitly find the index and possibly + // adjust the bookmark. + int formatting_node_index = gumbo_vector_index_of( + &state->_active_formatting_elements, formatting_node); + assert(formatting_node_index != -1); + if (formatting_node_index < bookmark) { + gumbo_debug( + "Formatting node at %d is before bookmark at %d; decrementing.\n", + formatting_node_index, bookmark); + --bookmark; + } + gumbo_vector_remove_at( + parser, formatting_node_index, &state->_active_formatting_elements); + assert(bookmark >= 0); + assert((unsigned int) bookmark <= state->_active_formatting_elements.length); + gumbo_vector_insert_at(parser, new_formatting_node, bookmark, + &state->_active_formatting_elements); + + // Step 19. + gumbo_vector_remove(parser, formatting_node, &state->_open_elements); + int insert_at = + gumbo_vector_index_of(&state->_open_elements, furthest_block) + 1; + assert(insert_at >= 0); + assert((unsigned int) insert_at <= state->_open_elements.length); + gumbo_vector_insert_at( + parser, new_formatting_node, insert_at, &state->_open_elements); + } // Step 20. + return true; +} + +// This is here to clean up memory when the spec says "Ignore current token." +static void ignore_token(GumboParser* parser) { + GumboToken* token = parser->_parser_state->_current_token; + // Ownership of the token's internal buffers are normally transferred to the + // element, but if no element is emitted (as happens in non-verbatim-mode + // when a token is ignored), we need to free it here to prevent a memory + // leak. + gumbo_token_destroy(parser, token); +#ifndef NDEBUG + if (token->type == GUMBO_TOKEN_START_TAG) { + // Mark this sentinel so the assertion in the main loop knows it's been + // destroyed. + token->v.start_tag.attributes = kGumboEmptyVector; + } +#endif +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/the-end.html +static void finish_parsing(GumboParser* parser) { + gumbo_debug("Finishing parsing"); + maybe_flush_text_node_buffer(parser); + GumboParserState* state = parser->_parser_state; + for (GumboNode* node = pop_current_node(parser); node; + node = pop_current_node(parser)) { + if ((node_html_tag_is(node, GUMBO_TAG_BODY) && state->_closed_body_tag) || + (node_html_tag_is(node, GUMBO_TAG_HTML) && state->_closed_html_tag)) { + continue; + } + node->parse_flags |= GUMBO_INSERTION_IMPLICIT_END_TAG; + } + while (pop_current_node(parser)) + ; // Pop them all. +} + +static bool handle_initial(GumboParser* parser, GumboToken* token) { + GumboDocument* document = &get_document_node(parser)->v.document; + if (token->type == GUMBO_TOKEN_WHITESPACE) { + ignore_token(parser); + return true; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_document_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + document->has_doctype = true; + document->name = token->v.doc_type.name; + document->public_identifier = token->v.doc_type.public_identifier; + document->system_identifier = token->v.doc_type.system_identifier; + document->doc_type_quirks_mode = compute_quirks_mode(&token->v.doc_type); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_BEFORE_HTML); + return maybe_add_doctype_error(parser, token); + } + parser_add_parse_error(parser, token); + document->doc_type_quirks_mode = GUMBO_DOCTYPE_QUIRKS; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_BEFORE_HTML); + parser->_parser_state->_reprocess_current_token = true; + return true; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#the-before-html-insertion-mode +static bool handle_before_html(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_document_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_WHITESPACE) { + ignore_token(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + GumboNode* html_node = insert_element_from_token(parser, token); + parser->_output->root = html_node; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_BEFORE_HEAD); + return true; + } else if (token->type == GUMBO_TOKEN_END_TAG && + !tag_in(token, false, + (gumbo_tagset){TAG(HEAD), TAG(BODY), TAG(HTML), TAG(BR)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + GumboNode* html_node = insert_element_of_tag_type( + parser, GUMBO_TAG_HTML, GUMBO_INSERTION_IMPLIED); + assert(html_node); + parser->_output->root = html_node; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_BEFORE_HEAD); + parser->_parser_state->_reprocess_current_token = true; + return true; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#the-before-head-insertion-mode +static bool handle_before_head(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_WHITESPACE) { + ignore_token(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HEAD)) { + GumboNode* node = insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_HEAD); + parser->_parser_state->_head_element = node; + return true; + } else if (token->type == GUMBO_TOKEN_END_TAG && + !tag_in(token, false, + (gumbo_tagset){TAG(HEAD), TAG(BODY), TAG(HTML), TAG(BR)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + GumboNode* node = insert_element_of_tag_type( + parser, GUMBO_TAG_HEAD, GUMBO_INSERTION_IMPLIED); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_HEAD); + parser->_parser_state->_head_element = node; + parser->_parser_state->_reprocess_current_token = true; + return true; + } +} + +// Forward declarations because of mutual dependencies. +static bool handle_token(GumboParser* parser, GumboToken* token); +static bool handle_in_body(GumboParser* parser, GumboToken* token); + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-inhead +static bool handle_in_head(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), + TAG(MENUITEM), TAG(LINK)})) { + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_META)) { + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + // NOTE(jdtang): Gumbo handles only UTF-8, so the encoding clause of the + // spec doesn't apply. If clients want to handle meta-tag re-encoding, they + // should specifically look for that string in the document and re-encode it + // before passing to Gumbo. + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_TITLE)) { + run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RCDATA); + return true; + } else if (tag_in( + token, kStartTag, (gumbo_tagset){TAG(NOFRAMES), TAG(STYLE)})) { + run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RAWTEXT); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_NOSCRIPT)) { + insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_HEAD_NOSCRIPT); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_SCRIPT)) { + run_generic_parsing_algorithm(parser, token, GUMBO_LEX_SCRIPT); + return true; + } else if (tag_is(token, kEndTag, GUMBO_TAG_HEAD)) { + GumboNode* head = pop_current_node(parser); + AVOID_UNUSED_VARIABLE_WARNING(head); + assert(node_html_tag_is(head, GUMBO_TAG_HEAD)); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_AFTER_HEAD); + return true; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(BODY), TAG(HTML), TAG(BR)})) { + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_AFTER_HEAD); + parser->_parser_state->_reprocess_current_token = true; + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_TEMPLATE)) { + insert_element_from_token(parser, token); + add_formatting_element(parser, &kActiveFormattingScopeMarker); + parser->_parser_state->_frameset_ok = false; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TEMPLATE); + push_template_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TEMPLATE); + return true; + } else if (tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) { + if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + generate_all_implied_end_tags_thoroughly(parser); + bool success = true; + if (!node_html_tag_is(get_current_node(parser), GUMBO_TAG_TEMPLATE)) { + parser_add_parse_error(parser, token); + success = false; + } + while (!node_html_tag_is(pop_current_node(parser), GUMBO_TAG_TEMPLATE)) + ; + clear_active_formatting_elements(parser); + pop_template_insertion_mode(parser); + reset_insertion_mode_appropriately(parser); + return success; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HEAD) || + (token->type == GUMBO_TOKEN_END_TAG)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_AFTER_HEAD); + parser->_parser_state->_reprocess_current_token = true; + return true; + } + return true; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-inheadnoscript +static bool handle_in_head_noscript(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (tag_is(token, kEndTag, GUMBO_TAG_NOSCRIPT)) { + const GumboNode* node = pop_current_node(parser); + assert(node_html_tag_is(node, GUMBO_TAG_NOSCRIPT)); + AVOID_UNUSED_VARIABLE_WARNING(node); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_HEAD); + return true; + } else if (token->type == GUMBO_TOKEN_WHITESPACE || + token->type == GUMBO_TOKEN_COMMENT || + tag_in(token, kStartTag, + (gumbo_tagset){TAG(BASEFONT), TAG(BGSOUND), TAG(LINK), + TAG(META), TAG(NOFRAMES), TAG(STYLE)})) { + return handle_in_head(parser, token); + } else if (tag_in( + token, kStartTag, (gumbo_tagset){TAG(HEAD), TAG(NOSCRIPT)}) || + (token->type == GUMBO_TOKEN_END_TAG && + !tag_is(token, kEndTag, GUMBO_TAG_BR))) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + parser_add_parse_error(parser, token); + const GumboNode* node = pop_current_node(parser); + assert(node_html_tag_is(node, GUMBO_TAG_NOSCRIPT)); + AVOID_UNUSED_VARIABLE_WARNING(node); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_HEAD); + parser->_parser_state->_reprocess_current_token = true; + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#the-after-head-insertion-mode +static bool handle_after_head(GumboParser* parser, GumboToken* token) { + GumboParserState* state = parser->_parser_state; + if (token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (tag_is(token, kStartTag, GUMBO_TAG_BODY)) { + insert_element_from_token(parser, token); + state->_frameset_ok = false; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_BODY); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_FRAMESET)) { + insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_FRAMESET); + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), + TAG(LINK), TAG(META), TAG(NOFRAMES), TAG(SCRIPT), + TAG(STYLE), TAG(TEMPLATE), TAG(TITLE)})) { + parser_add_parse_error(parser, token); + assert(state->_head_element != NULL); + // This must be flushed before we push the head element on, as there may be + // pending character tokens that should be attached to the root. + maybe_flush_text_node_buffer(parser); + gumbo_vector_add(parser, state->_head_element, &state->_open_elements); + bool result = handle_in_head(parser, token); + gumbo_vector_remove(parser, state->_head_element, &state->_open_elements); + return result; + } else if (tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) { + return handle_in_head(parser, token); + } else if (tag_is(token, kStartTag, GUMBO_TAG_HEAD) || + (token->type == GUMBO_TOKEN_END_TAG && + !tag_in(token, kEndTag, + (gumbo_tagset){TAG(BODY), TAG(HTML), TAG(BR)}))) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + insert_element_of_tag_type(parser, GUMBO_TAG_BODY, GUMBO_INSERTION_IMPLIED); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_BODY); + state->_reprocess_current_token = true; + return true; + } +} + +static void destroy_node(GumboParser* parser, GumboNode* node) { + switch (node->type) { + case GUMBO_NODE_DOCUMENT: { + GumboDocument* doc = &node->v.document; + for (unsigned int i = 0; i < doc->children.length; ++i) { + destroy_node(parser, doc->children.data[i]); + } + gumbo_parser_deallocate(parser, (void*) doc->children.data); + gumbo_parser_deallocate(parser, (void*) doc->name); + gumbo_parser_deallocate(parser, (void*) doc->public_identifier); + gumbo_parser_deallocate(parser, (void*) doc->system_identifier); + } break; + case GUMBO_NODE_TEMPLATE: + case GUMBO_NODE_ELEMENT: + for (unsigned int i = 0; i < node->v.element.attributes.length; ++i) { + gumbo_destroy_attribute(parser, node->v.element.attributes.data[i]); + } + gumbo_parser_deallocate(parser, node->v.element.attributes.data); + for (unsigned int i = 0; i < node->v.element.children.length; ++i) { + destroy_node(parser, node->v.element.children.data[i]); + } + gumbo_parser_deallocate(parser, node->v.element.children.data); + break; + case GUMBO_NODE_TEXT: + case GUMBO_NODE_CDATA: + case GUMBO_NODE_COMMENT: + case GUMBO_NODE_WHITESPACE: + gumbo_parser_deallocate(parser, (void*) node->v.text.text); + break; + } + gumbo_parser_deallocate(parser, node); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-inbody +static bool handle_in_body(GumboParser* parser, GumboToken* token) { + GumboParserState* state = parser->_parser_state; + assert(state->_open_elements.length > 0); + if (token->type == GUMBO_TOKEN_NULL) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_WHITESPACE) { + reconstruct_active_formatting_elements(parser); + insert_text_token(parser, token); + return true; + } else if (token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_CDATA) { + reconstruct_active_formatting_elements(parser); + insert_text_token(parser, token); + set_frameset_not_ok(parser); + return true; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + parser_add_parse_error(parser, token); + if (has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + ignore_token(parser); + return false; + } + assert(parser->_output->root != NULL); + assert(parser->_output->root->type == GUMBO_NODE_ELEMENT); + merge_attributes(parser, token, parser->_output->root); + return false; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), + TAG(MENUITEM), TAG(LINK), TAG(META), TAG(NOFRAMES), + TAG(SCRIPT), TAG(STYLE), TAG(TEMPLATE), TAG(TITLE)}) || + tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) { + return handle_in_head(parser, token); + } else if (tag_is(token, kStartTag, GUMBO_TAG_BODY)) { + parser_add_parse_error(parser, token); + if (state->_open_elements.length < 2 || + !node_html_tag_is(state->_open_elements.data[1], GUMBO_TAG_BODY) || + has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + ignore_token(parser); + return false; + } + state->_frameset_ok = false; + merge_attributes(parser, token, state->_open_elements.data[1]); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_FRAMESET)) { + parser_add_parse_error(parser, token); + if (state->_open_elements.length < 2 || + !node_html_tag_is(state->_open_elements.data[1], GUMBO_TAG_BODY) || + !state->_frameset_ok) { + ignore_token(parser); + return false; + } + // Save the body node for later removal. + GumboNode* body_node = state->_open_elements.data[1]; + + // Pop all nodes except root HTML element. + GumboNode* node; + do { + node = pop_current_node(parser); + } while (node != state->_open_elements.data[1]); + + // Removing & destroying the body node is going to kill any nodes that have + // been added to the list of active formatting elements, and so we should + // clear it to prevent a use-after-free if the list of active formatting + // elements is reconstructed afterwards. This may happen if whitespace + // follows the </frameset>. + clear_active_formatting_elements(parser); + + // Remove the body node. We may want to factor this out into a generic + // helper, but right now this is the only code that needs to do this. + GumboVector* children = &parser->_output->root->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { + if (children->data[i] == body_node) { + gumbo_vector_remove_at(parser, i, children); + break; + } + } + destroy_node(parser, body_node); + + // Insert the <frameset>, and switch the insertion mode. + insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_FRAMESET); + return true; + } else if (token->type == GUMBO_TOKEN_EOF) { + for (unsigned int i = 0; i < state->_open_elements.length; ++i) { + if (!node_tag_in_set(state->_open_elements.data[i], + (gumbo_tagset){TAG(DD), TAG(DT), TAG(LI), TAG(P), TAG(TBODY), + TAG(TD), TAG(TFOOT), TAG(TH), TAG(THEAD), TAG(TR), TAG(BODY), + TAG(HTML)})) { + parser_add_parse_error(parser, token); + } + } + if (get_current_template_insertion_mode(parser) != + GUMBO_INSERTION_MODE_INITIAL) { + return handle_in_template(parser, token); + } + return true; + } else if (tag_in(token, kEndTag, (gumbo_tagset){TAG(BODY), TAG(HTML)})) { + if (!has_an_element_in_scope(parser, GUMBO_TAG_BODY)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + bool success = true; + for (unsigned int i = 0; i < state->_open_elements.length; ++i) { + if (!node_tag_in_set(state->_open_elements.data[i], + (gumbo_tagset){TAG(DD), TAG(DT), TAG(LI), TAG(OPTGROUP), + TAG(OPTION), TAG(P), TAG(RB), TAG(RP), TAG(RT), TAG(RTC), + TAG(TBODY), TAG(TD), TAG(TFOOT), TAG(TH), TAG(THEAD), TAG(TR), + TAG(BODY), TAG(HTML)})) { + parser_add_parse_error(parser, token); + success = false; + break; + } + } + set_insertion_mode(parser, GUMBO_INSERTION_MODE_AFTER_BODY); + if (tag_is(token, kEndTag, GUMBO_TAG_HTML)) { + parser->_parser_state->_reprocess_current_token = true; + } else { + GumboNode* body = state->_open_elements.data[1]; + assert(node_html_tag_is(body, GUMBO_TAG_BODY)); + record_end_of_element(state->_current_token, &body->v.element); + } + return success; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), + TAG(BLOCKQUOTE), TAG(CENTER), TAG(DETAILS), TAG(DIR), + TAG(DIV), TAG(DL), TAG(FIELDSET), TAG(FIGCAPTION), + TAG(FIGURE), TAG(FOOTER), TAG(HEADER), TAG(HGROUP), + TAG(MENU), TAG(MAIN), TAG(NAV), TAG(OL), TAG(P), + TAG(SECTION), TAG(SUMMARY), TAG(UL)})) { + bool result = maybe_implicitly_close_p_tag(parser, token); + insert_element_from_token(parser, token); + return result; + } else if (tag_in(token, kStartTag, (gumbo_tagset){TAG(H1), TAG(H2), TAG(H3), + TAG(H4), TAG(H5), TAG(H6)})) { + bool result = maybe_implicitly_close_p_tag(parser, token); + if (node_tag_in_set( + get_current_node(parser), (gumbo_tagset){TAG(H1), TAG(H2), TAG(H3), + TAG(H4), TAG(H5), TAG(H6)})) { + parser_add_parse_error(parser, token); + pop_current_node(parser); + result = false; + } + insert_element_from_token(parser, token); + return result; + } else if (tag_in(token, kStartTag, (gumbo_tagset){TAG(PRE), TAG(LISTING)})) { + bool result = maybe_implicitly_close_p_tag(parser, token); + insert_element_from_token(parser, token); + state->_ignore_next_linefeed = true; + state->_frameset_ok = false; + return result; + } else if (tag_is(token, kStartTag, GUMBO_TAG_FORM)) { + if (state->_form_element != NULL && + !has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + gumbo_debug("Ignoring nested form.\n"); + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + bool result = maybe_implicitly_close_p_tag(parser, token); + GumboNode* form_element = insert_element_from_token(parser, token); + if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + state->_form_element = form_element; + } + return result; + } else if (tag_is(token, kStartTag, GUMBO_TAG_LI)) { + maybe_implicitly_close_list_tag(parser, token, true); + bool result = maybe_implicitly_close_p_tag(parser, token); + insert_element_from_token(parser, token); + return result; + } else if (tag_in(token, kStartTag, (gumbo_tagset){TAG(DD), TAG(DT)})) { + maybe_implicitly_close_list_tag(parser, token, false); + bool result = maybe_implicitly_close_p_tag(parser, token); + insert_element_from_token(parser, token); + return result; + } else if (tag_is(token, kStartTag, GUMBO_TAG_PLAINTEXT)) { + bool result = maybe_implicitly_close_p_tag(parser, token); + insert_element_from_token(parser, token); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_PLAINTEXT); + return result; + } else if (tag_is(token, kStartTag, GUMBO_TAG_BUTTON)) { + if (has_an_element_in_scope(parser, GUMBO_TAG_BUTTON)) { + parser_add_parse_error(parser, token); + implicitly_close_tags( + parser, token, GUMBO_NAMESPACE_HTML, GUMBO_TAG_BUTTON); + state->_reprocess_current_token = true; + return false; + } + reconstruct_active_formatting_elements(parser); + insert_element_from_token(parser, token); + state->_frameset_ok = false; + return true; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), + TAG(BLOCKQUOTE), TAG(BUTTON), TAG(CENTER), TAG(DETAILS), + TAG(DIR), TAG(DIV), TAG(DL), TAG(FIELDSET), + TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(HEADER), + TAG(HGROUP), TAG(LISTING), TAG(MAIN), TAG(MENU), TAG(NAV), + TAG(OL), TAG(PRE), TAG(SECTION), TAG(SUMMARY), TAG(UL)})) { + GumboTag tag = token->v.end_tag; + if (!has_an_element_in_scope(parser, tag)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + implicitly_close_tags( + parser, token, GUMBO_NAMESPACE_HTML, token->v.end_tag); + return true; + } else if (tag_is(token, kEndTag, GUMBO_TAG_FORM)) { + if (has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + if (!has_an_element_in_scope(parser, GUMBO_TAG_FORM)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + bool success = true; + generate_implied_end_tags(parser, GUMBO_TAG_LAST); + if (!node_html_tag_is(get_current_node(parser), GUMBO_TAG_FORM)) { + parser_add_parse_error(parser, token); + return false; + } + while (!node_html_tag_is(pop_current_node(parser), GUMBO_TAG_FORM)) + ; + return success; + } else { + bool result = true; + const GumboNode* node = state->_form_element; + assert(!node || node->type == GUMBO_NODE_ELEMENT); + state->_form_element = NULL; + if (!node || !has_node_in_scope(parser, node)) { + gumbo_debug("Closing an unopened form.\n"); + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + // This differs from implicitly_close_tags because we remove *only* the + // <form> element; other nodes are left in scope. + generate_implied_end_tags(parser, GUMBO_TAG_LAST); + if (get_current_node(parser) != node) { + parser_add_parse_error(parser, token); + result = false; + } + + GumboVector* open_elements = &state->_open_elements; + int index = gumbo_vector_index_of(open_elements, node); + assert(index >= 0); + gumbo_vector_remove_at(parser, index, open_elements); + return result; + } + } else if (tag_is(token, kEndTag, GUMBO_TAG_P)) { + if (!has_an_element_in_button_scope(parser, GUMBO_TAG_P)) { + parser_add_parse_error(parser, token); + // reconstruct_active_formatting_elements(parser); + insert_element_of_tag_type( + parser, GUMBO_TAG_P, GUMBO_INSERTION_CONVERTED_FROM_END_TAG); + state->_reprocess_current_token = true; + return false; + } + return implicitly_close_tags( + parser, token, GUMBO_NAMESPACE_HTML, GUMBO_TAG_P); + } else if (tag_is(token, kEndTag, GUMBO_TAG_LI)) { + if (!has_an_element_in_list_scope(parser, GUMBO_TAG_LI)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + return implicitly_close_tags( + parser, token, GUMBO_NAMESPACE_HTML, GUMBO_TAG_LI); + } else if (tag_in(token, kEndTag, (gumbo_tagset){TAG(DD), TAG(DT)})) { + assert(token->type == GUMBO_TOKEN_END_TAG); + GumboTag token_tag = token->v.end_tag; + if (!has_an_element_in_scope(parser, token_tag)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + return implicitly_close_tags( + parser, token, GUMBO_NAMESPACE_HTML, token_tag); + } else if (tag_in(token, kEndTag, (gumbo_tagset){TAG(H1), TAG(H2), TAG(H3), + TAG(H4), TAG(H5), TAG(H6)})) { + if (!has_an_element_in_scope_with_tagname( + parser, 6, (GumboTag[]){GUMBO_TAG_H1, GUMBO_TAG_H2, GUMBO_TAG_H3, + GUMBO_TAG_H4, GUMBO_TAG_H5, GUMBO_TAG_H6})) { + // No heading open; ignore the token entirely. + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + generate_implied_end_tags(parser, GUMBO_TAG_LAST); + const GumboNode* current_node = get_current_node(parser); + bool success = node_html_tag_is(current_node, token->v.end_tag); + if (!success) { + // There're children of the heading currently open; close them below and + // record a parse error. + // TODO(jdtang): Add a way to distinguish this error case from the one + // above. + parser_add_parse_error(parser, token); + } + do { + current_node = pop_current_node(parser); + } while (!node_tag_in_set( + current_node, (gumbo_tagset){TAG(H1), TAG(H2), TAG(H3), + TAG(H4), TAG(H5), TAG(H6)})); + return success; + } + } else if (tag_is(token, kStartTag, GUMBO_TAG_A)) { + bool success = true; + int last_a; + int has_matching_a = find_last_anchor_index(parser, &last_a); + if (has_matching_a) { + assert(has_matching_a == 1); + parser_add_parse_error(parser, token); + adoption_agency_algorithm(parser, token, GUMBO_TAG_A); + // The adoption agency algorithm usually removes all instances of <a> + // from the list of active formatting elements, but in case it doesn't, + // we're supposed to do this. (The conditions where it might not are + // listed in the spec.) + if (find_last_anchor_index(parser, &last_a)) { + void* last_element = gumbo_vector_remove_at( + parser, last_a, &state->_active_formatting_elements); + gumbo_vector_remove(parser, last_element, &state->_open_elements); + } + success = false; + } + reconstruct_active_formatting_elements(parser); + add_formatting_element(parser, insert_element_from_token(parser, token)); + return success; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(B), TAG(BIG), TAG(CODE), TAG(EM), TAG(FONT), + TAG(I), TAG(S), TAG(SMALL), TAG(STRIKE), TAG(STRONG), + TAG(TT), TAG(U)})) { + reconstruct_active_formatting_elements(parser); + add_formatting_element(parser, insert_element_from_token(parser, token)); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_NOBR)) { + bool result = true; + reconstruct_active_formatting_elements(parser); + if (has_an_element_in_scope(parser, GUMBO_TAG_NOBR)) { + result = false; + parser_add_parse_error(parser, token); + adoption_agency_algorithm(parser, token, GUMBO_TAG_NOBR); + reconstruct_active_formatting_elements(parser); + } + insert_element_from_token(parser, token); + add_formatting_element(parser, get_current_node(parser)); + return result; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(A), TAG(B), TAG(BIG), TAG(CODE), TAG(EM), + TAG(FONT), TAG(I), TAG(NOBR), TAG(S), TAG(SMALL), + TAG(STRIKE), TAG(STRONG), TAG(TT), TAG(U)})) { + return adoption_agency_algorithm(parser, token, token->v.end_tag); + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(APPLET), TAG(MARQUEE), TAG(OBJECT)})) { + reconstruct_active_formatting_elements(parser); + insert_element_from_token(parser, token); + add_formatting_element(parser, &kActiveFormattingScopeMarker); + set_frameset_not_ok(parser); + return true; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(APPLET), TAG(MARQUEE), TAG(OBJECT)})) { + GumboTag token_tag = token->v.end_tag; + if (!has_an_element_in_table_scope(parser, token_tag)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + implicitly_close_tags(parser, token, GUMBO_NAMESPACE_HTML, token_tag); + clear_active_formatting_elements(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_TABLE)) { + if (get_document_node(parser)->v.document.doc_type_quirks_mode != + GUMBO_DOCTYPE_QUIRKS) { + maybe_implicitly_close_p_tag(parser, token); + } + insert_element_from_token(parser, token); + set_frameset_not_ok(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(AREA), TAG(BR), TAG(EMBED), TAG(IMG), + TAG(IMAGE), TAG(KEYGEN), TAG(WBR)})) { + bool success = true; + if (tag_is(token, kStartTag, GUMBO_TAG_IMAGE)) { + success = false; + parser_add_parse_error(parser, token); + token->v.start_tag.tag = GUMBO_TAG_IMG; + } + reconstruct_active_formatting_elements(parser); + GumboNode* node = insert_element_from_token(parser, token); + if (tag_is(token, kStartTag, GUMBO_TAG_IMAGE)) { + success = false; + parser_add_parse_error(parser, token); + node->v.element.tag = GUMBO_TAG_IMG; + node->parse_flags |= GUMBO_INSERTION_FROM_IMAGE; + } + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + set_frameset_not_ok(parser); + return success; + } else if (tag_is(token, kStartTag, GUMBO_TAG_INPUT)) { + if (!attribute_matches(&token->v.start_tag.attributes, "type", "hidden")) { + // Must be before the element is inserted, as that takes ownership of the + // token's attribute vector. + set_frameset_not_ok(parser); + } + reconstruct_active_formatting_elements(parser); + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(PARAM), TAG(SOURCE), TAG(TRACK)})) { + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HR)) { + bool result = maybe_implicitly_close_p_tag(parser, token); + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + set_frameset_not_ok(parser); + return result; + } else if (tag_is(token, kStartTag, GUMBO_TAG_ISINDEX)) { + parser_add_parse_error(parser, token); + if (parser->_parser_state->_form_element != NULL && + !has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + ignore_token(parser); + return false; + } + acknowledge_self_closing_tag(parser); + maybe_implicitly_close_p_tag(parser, token); + set_frameset_not_ok(parser); + + GumboVector* token_attrs = &token->v.start_tag.attributes; + GumboAttribute* prompt_attr = gumbo_get_attribute(token_attrs, "prompt"); + GumboAttribute* action_attr = gumbo_get_attribute(token_attrs, "action"); + GumboAttribute* name_attr = gumbo_get_attribute(token_attrs, "name"); + + GumboNode* form = insert_element_of_tag_type( + parser, GUMBO_TAG_FORM, GUMBO_INSERTION_FROM_ISINDEX); + if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + parser->_parser_state->_form_element = form; + } + if (action_attr) { + gumbo_vector_add(parser, action_attr, &form->v.element.attributes); + } + insert_element_of_tag_type( + parser, GUMBO_TAG_HR, GUMBO_INSERTION_FROM_ISINDEX); + pop_current_node(parser); // <hr> + + insert_element_of_tag_type( + parser, GUMBO_TAG_LABEL, GUMBO_INSERTION_FROM_ISINDEX); + TextNodeBufferState* text_state = &parser->_parser_state->_text_node; + text_state->_start_original_text = token->original_text.data; + text_state->_start_position = token->position; + text_state->_type = GUMBO_NODE_TEXT; + if (prompt_attr) { + size_t prompt_attr_length = strlen(prompt_attr->value); + gumbo_string_buffer_destroy(parser, &text_state->_buffer); + text_state->_buffer.data = gumbo_copy_stringz(parser, prompt_attr->value); + text_state->_buffer.length = prompt_attr_length; + text_state->_buffer.capacity = prompt_attr_length + 1; + gumbo_destroy_attribute(parser, prompt_attr); + } else { + GumboStringPiece prompt_text = + GUMBO_STRING("This is a searchable index. Enter search keywords: "); + gumbo_string_buffer_append_string( + parser, &prompt_text, &text_state->_buffer); + } + + GumboNode* input = insert_element_of_tag_type( + parser, GUMBO_TAG_INPUT, GUMBO_INSERTION_FROM_ISINDEX); + for (unsigned int i = 0; i < token_attrs->length; ++i) { + GumboAttribute* attr = token_attrs->data[i]; + if (attr != prompt_attr && attr != action_attr && attr != name_attr) { + gumbo_vector_add(parser, attr, &input->v.element.attributes); + } + token_attrs->data[i] = NULL; + } + + // All attributes have been successfully transferred and nulled out at this + // point, so the call to ignore_token will free the memory for it without + // touching the attributes. + ignore_token(parser); + + // The name attribute, if present, should be destroyed since it's ignored + // when copying over. The action attribute should be kept since it's moved + // to the form. + if (name_attr) { + gumbo_destroy_attribute(parser, name_attr); + } + + GumboAttribute* name = + gumbo_parser_allocate(parser, sizeof(GumboAttribute)); + GumboStringPiece name_str = GUMBO_STRING("name"); + GumboStringPiece isindex_str = GUMBO_STRING("isindex"); + name->attr_namespace = GUMBO_ATTR_NAMESPACE_NONE; + name->name = gumbo_copy_stringz(parser, "name"); + name->value = gumbo_copy_stringz(parser, "isindex"); + name->original_name = name_str; + name->original_value = isindex_str; + name->name_start = kGumboEmptySourcePosition; + name->name_end = kGumboEmptySourcePosition; + name->value_start = kGumboEmptySourcePosition; + name->value_end = kGumboEmptySourcePosition; + gumbo_vector_add(parser, name, &input->v.element.attributes); + + pop_current_node(parser); // <input> + pop_current_node(parser); // <label> + insert_element_of_tag_type( + parser, GUMBO_TAG_HR, GUMBO_INSERTION_FROM_ISINDEX); + pop_current_node(parser); // <hr> + pop_current_node(parser); // <form> + if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + parser->_parser_state->_form_element = NULL; + } + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_TEXTAREA)) { + run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RCDATA); + parser->_parser_state->_ignore_next_linefeed = true; + set_frameset_not_ok(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_XMP)) { + bool result = maybe_implicitly_close_p_tag(parser, token); + reconstruct_active_formatting_elements(parser); + set_frameset_not_ok(parser); + run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RAWTEXT); + return result; + } else if (tag_is(token, kStartTag, GUMBO_TAG_IFRAME)) { + set_frameset_not_ok(parser); + run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RAWTEXT); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_NOEMBED)) { + run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RAWTEXT); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_SELECT)) { + reconstruct_active_formatting_elements(parser); + insert_element_from_token(parser, token); + set_frameset_not_ok(parser); + GumboInsertionMode state = parser->_parser_state->_insertion_mode; + if (state == GUMBO_INSERTION_MODE_IN_TABLE || + state == GUMBO_INSERTION_MODE_IN_CAPTION || + state == GUMBO_INSERTION_MODE_IN_TABLE_BODY || + state == GUMBO_INSERTION_MODE_IN_ROW || + state == GUMBO_INSERTION_MODE_IN_CELL) { + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_SELECT_IN_TABLE); + } else { + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_SELECT); + } + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(OPTION), TAG(OPTGROUP)})) { + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION)) { + pop_current_node(parser); + } + reconstruct_active_formatting_elements(parser); + insert_element_from_token(parser, token); + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(RB), TAG(RP), TAG(RT), TAG(RTC)})) { + bool success = true; + GumboTag exception = + tag_in(token, kStartTag, (gumbo_tagset){TAG(RT), TAG(RP)}) + ? GUMBO_TAG_RTC + : GUMBO_TAG_LAST; + if (has_an_element_in_scope(parser, GUMBO_TAG_RUBY)) { + generate_implied_end_tags(parser, exception); + } + if (!node_html_tag_is(get_current_node(parser), GUMBO_TAG_RUBY) && + !(exception == GUMBO_TAG_LAST || + node_html_tag_is(get_current_node(parser), GUMBO_TAG_RTC))) { + parser_add_parse_error(parser, token); + success = false; + } + insert_element_from_token(parser, token); + return success; + } else if (tag_is(token, kEndTag, GUMBO_TAG_BR)) { + parser_add_parse_error(parser, token); + reconstruct_active_formatting_elements(parser); + insert_element_of_tag_type( + parser, GUMBO_TAG_BR, GUMBO_INSERTION_CONVERTED_FROM_END_TAG); + pop_current_node(parser); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_MATH)) { + reconstruct_active_formatting_elements(parser); + adjust_mathml_attributes(parser, token); + adjust_foreign_attributes(parser, token); + insert_foreign_element(parser, token, GUMBO_NAMESPACE_MATHML); + if (token->v.start_tag.is_self_closing) { + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + } + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_SVG)) { + reconstruct_active_formatting_elements(parser); + adjust_svg_attributes(parser, token); + adjust_foreign_attributes(parser, token); + insert_foreign_element(parser, token, GUMBO_NAMESPACE_SVG); + if (token->v.start_tag.is_self_closing) { + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + } + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(CAPTION), TAG(COL), TAG(COLGROUP), + TAG(FRAME), TAG(HEAD), TAG(TBODY), TAG(TD), TAG(TFOOT), + TAG(TH), TAG(THEAD), TAG(TR)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_START_TAG) { + reconstruct_active_formatting_elements(parser); + insert_element_from_token(parser, token); + return true; + } else { + assert(token->type == GUMBO_TOKEN_END_TAG); + GumboTag end_tag = token->v.end_tag; + assert(state->_open_elements.length > 0); + assert(node_html_tag_is(state->_open_elements.data[0], GUMBO_TAG_HTML)); + // Walk up the stack of open elements until we find one that either: + // a) Matches the tag name we saw + // b) Is in the "special" category. + // If we see a), implicitly close everything up to and including it. If we + // see b), then record a parse error, don't close anything (except the + // implied end tags) and ignore the end tag token. + for (int i = state->_open_elements.length; --i >= 0;) { + const GumboNode* node = state->_open_elements.data[i]; + if (node_html_tag_is(node, end_tag)) { + generate_implied_end_tags(parser, end_tag); + // TODO(jdtang): Do I need to add a parse error here? The condition in + // the spec seems like it's the inverse of the loop condition above, and + // so would never fire. + while (node != pop_current_node(parser)) + ; // Pop everything. + return true; + } else if (is_special_node(node)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + } + // <html> is in the special category, so we should never get here. + assert(0); + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-incdata +static bool handle_text(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + } else { + // We provide only bare-bones script handling that doesn't involve any of + // the parser-pause/already-started/script-nesting flags or re-entrant + // invocations of the tokenizer. Because the intended usage of this library + // is mostly for templating, refactoring, and static-analysis libraries, we + // provide the script body as a text-node child of the <script> element. + // This behavior doesn't support document.write of partial HTML elements, + // but should be adequate for almost all other scripting support. + if (token->type == GUMBO_TOKEN_EOF) { + parser_add_parse_error(parser, token); + parser->_parser_state->_reprocess_current_token = true; + } + pop_current_node(parser); + set_insertion_mode(parser, parser->_parser_state->_original_insertion_mode); + } + return true; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-intable +static bool handle_in_table(GumboParser* parser, GumboToken* token) { + GumboParserState* state = parser->_parser_state; + if (token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_WHITESPACE) { + // The "pending table character tokens" list described in the spec is + // nothing more than the TextNodeBufferState. We accumulate text tokens as + // normal, except that when we go to flush them in the handle_in_table_text, + // we set _foster_parent_insertions if there're non-whitespace characters in + // the buffer. + assert(state->_text_node._buffer.length == 0); + state->_original_insertion_mode = state->_insertion_mode; + state->_reprocess_current_token = true; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE_TEXT); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_CAPTION)) { + clear_stack_to_table_context(parser); + add_formatting_element(parser, &kActiveFormattingScopeMarker); + insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_CAPTION); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_COLGROUP)) { + clear_stack_to_table_context(parser); + insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_COLUMN_GROUP); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_COL)) { + clear_stack_to_table_context(parser); + insert_element_of_tag_type( + parser, GUMBO_TAG_COLGROUP, GUMBO_INSERTION_IMPLIED); + parser->_parser_state->_reprocess_current_token = true; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_COLUMN_GROUP); + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(TBODY), TAG(TFOOT), TAG(THEAD), TAG(TD), + TAG(TH), TAG(TR)})) { + clear_stack_to_table_context(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE_BODY); + if (tag_in(token, kStartTag, (gumbo_tagset){TAG(TD), TAG(TH), TAG(TR)})) { + insert_element_of_tag_type( + parser, GUMBO_TAG_TBODY, GUMBO_INSERTION_IMPLIED); + state->_reprocess_current_token = true; + } else { + insert_element_from_token(parser, token); + } + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_TABLE)) { + parser_add_parse_error(parser, token); + if (close_table(parser)) { + parser->_parser_state->_reprocess_current_token = true; + } else { + ignore_token(parser); + } + return false; + } else if (tag_is(token, kEndTag, GUMBO_TAG_TABLE)) { + if (!close_table(parser)) { + parser_add_parse_error(parser, token); + return false; + } + return true; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(BODY), TAG(CAPTION), TAG(COL), + TAG(COLGROUP), TAG(HTML), TAG(TBODY), TAG(TD), TAG(TFOOT), + TAG(TH), TAG(THEAD), TAG(TR)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(STYLE), TAG(SCRIPT), TAG(TEMPLATE)}) || + (tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE))) { + return handle_in_head(parser, token); + } else if (tag_is(token, kStartTag, GUMBO_TAG_INPUT) && + attribute_matches( + &token->v.start_tag.attributes, "type", "hidden")) { + parser_add_parse_error(parser, token); + insert_element_from_token(parser, token); + pop_current_node(parser); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_FORM)) { + parser_add_parse_error(parser, token); + if (state->_form_element || has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + ignore_token(parser); + return false; + } + state->_form_element = insert_element_from_token(parser, token); + pop_current_node(parser); + return false; + } else if (token->type == GUMBO_TOKEN_EOF) { + return handle_in_body(parser, token); + } else { + parser_add_parse_error(parser, token); + state->_foster_parent_insertions = true; + bool result = handle_in_body(parser, token); + state->_foster_parent_insertions = false; + return result; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-intabletext +static bool handle_in_table_text(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_NULL) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + return true; + } else { + GumboParserState* state = parser->_parser_state; + GumboStringBuffer* buffer = &state->_text_node._buffer; + // Can't use strspn for this because GumboStringBuffers are not + // null-terminated. + // Note that TextNodeBuffer may contain UTF-8 characters, but the presence + // of any one byte that is not whitespace means we flip the flag, so this + // loop is still valid. + for (unsigned int i = 0; i < buffer->length; ++i) { + if (!isspace((unsigned char) buffer->data[i]) || + buffer->data[i] == '\v') { + state->_foster_parent_insertions = true; + reconstruct_active_formatting_elements(parser); + break; + } + } + maybe_flush_text_node_buffer(parser); + state->_foster_parent_insertions = false; + state->_reprocess_current_token = true; + state->_insertion_mode = state->_original_insertion_mode; + return true; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-incaption +static bool handle_in_caption(GumboParser* parser, GumboToken* token) { + if (tag_is(token, kEndTag, GUMBO_TAG_CAPTION)) { + if (!has_an_element_in_table_scope(parser, GUMBO_TAG_CAPTION)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + generate_implied_end_tags(parser, GUMBO_TAG_LAST); + bool result = true; + if (!node_html_tag_is(get_current_node(parser), GUMBO_TAG_CAPTION)) { + parser_add_parse_error(parser, token); + } + while (!node_html_tag_is(pop_current_node(parser), GUMBO_TAG_CAPTION)) + ; + clear_active_formatting_elements(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + return result; + } + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(CAPTION), TAG(COL), TAG(COLGROUP), + TAG(TBODY), TAG(TD), TAG(TFOOT), TAG(TH), TAG(THEAD), + TAG(TR)}) || + (tag_is(token, kEndTag, GUMBO_TAG_TABLE))) { + if (!has_an_element_in_table_scope(parser, GUMBO_TAG_CAPTION)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + while (!node_html_tag_is(pop_current_node(parser), GUMBO_TAG_CAPTION)) + ; + clear_active_formatting_elements(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + parser->_parser_state->_reprocess_current_token = true; + return true; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(BODY), TAG(COL), TAG(COLGROUP), TAG(HTML), + TAG(TBODY), TAG(TD), TAG(TFOOT), TAG(TH), TAG(THEAD), + TAG(TR)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + return handle_in_body(parser, token); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-incolgroup +static bool handle_in_column_group(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (tag_is(token, kStartTag, GUMBO_TAG_COL)) { + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + return true; + } else if (tag_is(token, kEndTag, GUMBO_TAG_COLGROUP)) { + if (!node_html_tag_is(get_current_node(parser), GUMBO_TAG_COLGROUP)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + return false; + } else if (tag_is(token, kEndTag, GUMBO_TAG_COL)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_TEMPLATE) || + tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) { + return handle_in_head(parser, token); + } else if (token->type == GUMBO_TOKEN_EOF) { + return handle_in_body(parser, token); + } else { + if (!node_html_tag_is(get_current_node(parser), GUMBO_TAG_COLGROUP)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + parser->_parser_state->_reprocess_current_token = true; + return true; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-intbody +static bool handle_in_table_body(GumboParser* parser, GumboToken* token) { + if (tag_is(token, kStartTag, GUMBO_TAG_TR)) { + clear_stack_to_table_body_context(parser); + insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_ROW); + return true; + } else if (tag_in(token, kStartTag, (gumbo_tagset){TAG(TD), TAG(TH)})) { + parser_add_parse_error(parser, token); + clear_stack_to_table_body_context(parser); + insert_element_of_tag_type(parser, GUMBO_TAG_TR, GUMBO_INSERTION_IMPLIED); + parser->_parser_state->_reprocess_current_token = true; + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_ROW); + return false; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(TBODY), TAG(TFOOT), TAG(THEAD)})) { + if (!has_an_element_in_table_scope(parser, token->v.end_tag)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + clear_stack_to_table_body_context(parser); + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + return true; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(CAPTION), TAG(COL), TAG(COLGROUP), + TAG(TBODY), TAG(TFOOT), TAG(THEAD)}) || + tag_is(token, kEndTag, GUMBO_TAG_TABLE)) { + if (!(has_an_element_in_table_scope(parser, GUMBO_TAG_TBODY) || + has_an_element_in_table_scope(parser, GUMBO_TAG_THEAD) || + has_an_element_in_table_scope(parser, GUMBO_TAG_TFOOT))) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + clear_stack_to_table_body_context(parser); + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + parser->_parser_state->_reprocess_current_token = true; + return true; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(BODY), TAG(CAPTION), TAG(COL), TAG(TR), + TAG(COLGROUP), TAG(HTML), TAG(TD), TAG(TH)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + return handle_in_table(parser, token); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-intr +static bool handle_in_row(GumboParser* parser, GumboToken* token) { + if (tag_in(token, kStartTag, (gumbo_tagset){TAG(TH), TAG(TD)})) { + clear_stack_to_table_row_context(parser); + insert_element_from_token(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_CELL); + add_formatting_element(parser, &kActiveFormattingScopeMarker); + return true; + } else if (tag_is(token, kEndTag, GUMBO_TAG_TR)) { + if (!has_an_element_in_table_scope(parser, GUMBO_TAG_TR)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + clear_stack_to_table_row_context(parser); + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE_BODY); + return true; + } + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(CAPTION), TAG(COL), TAG(COLGROUP), + TAG(TBODY), TAG(TFOOT), TAG(THEAD), TAG(TR)}) || + tag_is(token, kEndTag, GUMBO_TAG_TABLE)) { + if (!has_an_element_in_table_scope(parser, GUMBO_TAG_TR)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + clear_stack_to_table_row_context(parser); + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE_BODY); + parser->_parser_state->_reprocess_current_token = true; + return true; + } + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(TBODY), TAG(TFOOT), TAG(THEAD)})) { + if (!has_an_element_in_table_scope(parser, token->v.end_tag) || + (!has_an_element_in_table_scope(parser, GUMBO_TAG_TR))) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + clear_stack_to_table_row_context(parser); + pop_current_node(parser); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE_BODY); + parser->_parser_state->_reprocess_current_token = true; + return true; + } + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(BODY), TAG(CAPTION), TAG(COL), + TAG(COLGROUP), TAG(HTML), TAG(TD), TAG(TH)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else { + return handle_in_table(parser, token); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-intd +static bool handle_in_cell(GumboParser* parser, GumboToken* token) { + if (tag_in(token, kEndTag, (gumbo_tagset){TAG(TD), TAG(TH)})) { + GumboTag token_tag = token->v.end_tag; + if (!has_an_element_in_table_scope(parser, token_tag)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + return close_table_cell(parser, token, token_tag); + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(CAPTION), TAG(COL), TAG(COLGROUP), + TAG(TBODY), TAG(TD), TAG(TFOOT), TAG(TH), TAG(THEAD), + TAG(TR)})) { + gumbo_debug("Handling <td> in cell.\n"); + if (!has_an_element_in_table_scope(parser, GUMBO_TAG_TH) && + !has_an_element_in_table_scope(parser, GUMBO_TAG_TD)) { + gumbo_debug("Bailing out because there's no <td> or <th> in scope.\n"); + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + parser->_parser_state->_reprocess_current_token = true; + return close_current_cell(parser, token); + } else if (tag_in(token, kEndTag, (gumbo_tagset){TAG(BODY), TAG(CAPTION), + TAG(COL), TAG(COLGROUP), TAG(HTML)})) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (tag_in(token, kEndTag, (gumbo_tagset){TAG(TABLE), TAG(TBODY), + TAG(TFOOT), TAG(THEAD), TAG(TR)})) { + if (!has_an_element_in_table_scope(parser, token->v.end_tag)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + parser->_parser_state->_reprocess_current_token = true; + return close_current_cell(parser, token); + } else { + return handle_in_body(parser, token); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-inselect +static bool handle_in_select(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_NULL) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (tag_is(token, kStartTag, GUMBO_TAG_OPTION)) { + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION)) { + pop_current_node(parser); + } + insert_element_from_token(parser, token); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_OPTGROUP)) { + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION)) { + pop_current_node(parser); + } + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTGROUP)) { + pop_current_node(parser); + } + insert_element_from_token(parser, token); + return true; + } else if (tag_is(token, kEndTag, GUMBO_TAG_OPTGROUP)) { + GumboVector* open_elements = &parser->_parser_state->_open_elements; + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION) && + node_html_tag_is(open_elements->data[open_elements->length - 2], + GUMBO_TAG_OPTGROUP)) { + pop_current_node(parser); + } + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTGROUP)) { + pop_current_node(parser); + return true; + } else { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + } else if (tag_is(token, kEndTag, GUMBO_TAG_OPTION)) { + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION)) { + pop_current_node(parser); + return true; + } else { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + } else if (tag_is(token, kEndTag, GUMBO_TAG_SELECT)) { + if (!has_an_element_in_select_scope(parser, GUMBO_TAG_SELECT)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + close_current_select(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_SELECT)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + if (has_an_element_in_select_scope(parser, GUMBO_TAG_SELECT)) { + close_current_select(parser); + } + return false; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(INPUT), TAG(KEYGEN), TAG(TEXTAREA)})) { + parser_add_parse_error(parser, token); + if (!has_an_element_in_select_scope(parser, GUMBO_TAG_SELECT)) { + ignore_token(parser); + } else { + close_current_select(parser); + parser->_parser_state->_reprocess_current_token = true; + } + return false; + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(SCRIPT), TAG(TEMPLATE)}) || + tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) { + return handle_in_head(parser, token); + } else if (token->type == GUMBO_TOKEN_EOF) { + return handle_in_body(parser, token); + } else { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-inselectintable +static bool handle_in_select_in_table(GumboParser* parser, GumboToken* token) { + if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(CAPTION), TAG(TABLE), TAG(TBODY), TAG(TFOOT), + TAG(THEAD), TAG(TR), TAG(TD), TAG(TH)})) { + parser_add_parse_error(parser, token); + close_current_select(parser); + parser->_parser_state->_reprocess_current_token = true; + return false; + } else if (tag_in(token, kEndTag, + (gumbo_tagset){TAG(CAPTION), TAG(TABLE), TAG(TBODY), + TAG(TFOOT), TAG(THEAD), TAG(TR), TAG(TD), TAG(TH)})) { + parser_add_parse_error(parser, token); + if (!has_an_element_in_table_scope(parser, token->v.end_tag)) { + ignore_token(parser); + return false; + } else { + close_current_select(parser); + // close_current_select already does the + // reset_insertion_mode_appropriately + // reset_insertion_mode_appropriately(parser); + parser->_parser_state->_reprocess_current_token = true; + return false; + } + } else { + return handle_in_select(parser, token); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-intemplate +static bool handle_in_template(GumboParser* parser, GumboToken* token) { + GumboParserState* state = parser->_parser_state; + if (token->type == GUMBO_TOKEN_WHITESPACE || + token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_COMMENT || token->type == GUMBO_TOKEN_NULL || + token->type == GUMBO_TOKEN_DOCTYPE) { + return handle_in_body(parser, token); + } else if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), + TAG(LINK), TAG(META), TAG(NOFRAMES), TAG(SCRIPT), + TAG(STYLE), TAG(TEMPLATE), TAG(TITLE)}) || + tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) { + return handle_in_head(parser, token); + } else if (tag_in( + token, kStartTag, (gumbo_tagset){TAG(CAPTION), TAG(COLGROUP), + TAG(TBODY), TAG(TFOOT), TAG(THEAD)})) { + pop_template_insertion_mode(parser); + push_template_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE); + state->_reprocess_current_token = true; + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_COL)) { + pop_template_insertion_mode(parser); + push_template_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_COLUMN_GROUP); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_COLUMN_GROUP); + state->_reprocess_current_token = true; + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_TR)) { + pop_template_insertion_mode(parser); + push_template_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE_BODY); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TABLE_BODY); + state->_reprocess_current_token = true; + return true; + } else if (tag_in(token, kStartTag, (gumbo_tagset){TAG(TD), TAG(TH)})) { + pop_template_insertion_mode(parser); + push_template_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_ROW); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_ROW); + state->_reprocess_current_token = true; + return true; + } else if (token->type == GUMBO_TOKEN_START_TAG) { + pop_template_insertion_mode(parser); + push_template_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_BODY); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_BODY); + state->_reprocess_current_token = true; + return true; + } else if (token->type == GUMBO_TOKEN_END_TAG) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (token->type == GUMBO_TOKEN_EOF) { + if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) { + // Stop parsing. + return true; + } + parser_add_parse_error(parser, token); + while (!node_html_tag_is(pop_current_node(parser), GUMBO_TAG_TEMPLATE)) + ; + clear_active_formatting_elements(parser); + pop_template_insertion_mode(parser); + reset_insertion_mode_appropriately(parser); + state->_reprocess_current_token = true; + return false; + } else { + assert(0); + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-afterbody +static bool handle_after_body(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_WHITESPACE || + tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (token->type == GUMBO_TOKEN_COMMENT) { + GumboNode* html_node = parser->_output->root; + assert(html_node != NULL); + append_comment_node(parser, html_node, token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (tag_is(token, kEndTag, GUMBO_TAG_HTML)) { + /* fragment case: ignore the closing HTML token */ + if (is_fragment_parser(parser)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + set_insertion_mode(parser, GUMBO_INSERTION_MODE_AFTER_AFTER_BODY); + GumboNode* html = parser->_parser_state->_open_elements.data[0]; + assert(node_html_tag_is(html, GUMBO_TAG_HTML)); + record_end_of_element( + parser->_parser_state->_current_token, &html->v.element); + return true; + } else if (token->type == GUMBO_TOKEN_EOF) { + return true; + } else { + parser_add_parse_error(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_BODY); + parser->_parser_state->_reprocess_current_token = true; + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-inframeset +static bool handle_in_frameset(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + return true; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (tag_is(token, kStartTag, GUMBO_TAG_FRAMESET)) { + insert_element_from_token(parser, token); + return true; + } else if (tag_is(token, kEndTag, GUMBO_TAG_FRAMESET)) { + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_HTML)) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } + pop_current_node(parser); + if (!is_fragment_parser(parser) && + !node_html_tag_is(get_current_node(parser), GUMBO_TAG_FRAMESET)) { + set_insertion_mode(parser, GUMBO_INSERTION_MODE_AFTER_FRAMESET); + } + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_FRAME)) { + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_NOFRAMES)) { + return handle_in_head(parser, token); + } else if (token->type == GUMBO_TOKEN_EOF) { + if (!node_html_tag_is(get_current_node(parser), GUMBO_TAG_HTML)) { + parser_add_parse_error(parser, token); + return false; + } + return true; + } else { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-afterframeset +static bool handle_after_frameset(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_WHITESPACE) { + insert_text_token(parser, token); + return true; + } else if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_current_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE) { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (tag_is(token, kEndTag, GUMBO_TAG_HTML)) { + GumboNode* html = parser->_parser_state->_open_elements.data[0]; + assert(node_html_tag_is(html, GUMBO_TAG_HTML)); + record_end_of_element( + parser->_parser_state->_current_token, &html->v.element); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_AFTER_AFTER_FRAMESET); + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_NOFRAMES)) { + return handle_in_head(parser, token); + } else if (token->type == GUMBO_TOKEN_EOF) { + return true; + } else { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#the-after-after-body-insertion-mode +static bool handle_after_after_body(GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_document_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE || + token->type == GUMBO_TOKEN_WHITESPACE || + tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (token->type == GUMBO_TOKEN_EOF) { + return true; + } else { + parser_add_parse_error(parser, token); + set_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_BODY); + parser->_parser_state->_reprocess_current_token = true; + return false; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#the-after-after-frameset-insertion-mode +static bool handle_after_after_frameset( + GumboParser* parser, GumboToken* token) { + if (token->type == GUMBO_TOKEN_COMMENT) { + append_comment_node(parser, get_document_node(parser), token); + return true; + } else if (token->type == GUMBO_TOKEN_DOCTYPE || + token->type == GUMBO_TOKEN_WHITESPACE || + tag_is(token, kStartTag, GUMBO_TAG_HTML)) { + return handle_in_body(parser, token); + } else if (token->type == GUMBO_TOKEN_EOF) { + return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_NOFRAMES)) { + return handle_in_head(parser, token); + } else { + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + } +} + +// Function pointers for each insertion mode. Keep in sync with +// insertion_mode.h. +typedef bool (*TokenHandler)(GumboParser* parser, GumboToken* token); +static const TokenHandler kTokenHandlers[] = {handle_initial, + handle_before_html, handle_before_head, handle_in_head, + handle_in_head_noscript, handle_after_head, handle_in_body, handle_text, + handle_in_table, handle_in_table_text, handle_in_caption, + handle_in_column_group, handle_in_table_body, handle_in_row, handle_in_cell, + handle_in_select, handle_in_select_in_table, handle_in_template, + handle_after_body, handle_in_frameset, handle_after_frameset, + handle_after_after_body, handle_after_after_frameset}; + +static bool handle_html_content(GumboParser* parser, GumboToken* token) { + return kTokenHandlers[(unsigned int) parser->_parser_state->_insertion_mode]( + parser, token); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#parsing-main-inforeign +static bool handle_in_foreign_content(GumboParser* parser, GumboToken* token) { + gumbo_debug("Handling foreign content"); + switch (token->type) { + case GUMBO_TOKEN_NULL: + parser_add_parse_error(parser, token); + token->v.character = kUtf8ReplacementChar; + insert_text_token(parser, token); + return false; + case GUMBO_TOKEN_WHITESPACE: + insert_text_token(parser, token); + return true; + case GUMBO_TOKEN_CDATA: + case GUMBO_TOKEN_CHARACTER: + insert_text_token(parser, token); + set_frameset_not_ok(parser); + return true; + case GUMBO_TOKEN_COMMENT: + append_comment_node(parser, get_current_node(parser), token); + return true; + case GUMBO_TOKEN_DOCTYPE: + parser_add_parse_error(parser, token); + ignore_token(parser); + return false; + default: + // Fall through to the if-statements below. + break; + } + // Order matters for these clauses. + if (tag_in(token, kStartTag, + (gumbo_tagset){TAG(B), TAG(BIG), TAG(BLOCKQUOTE), TAG(BODY), TAG(BR), + TAG(CENTER), TAG(CODE), TAG(DD), TAG(DIV), TAG(DL), TAG(DT), + TAG(EM), TAG(EMBED), TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5), + TAG(H6), TAG(HEAD), TAG(HR), TAG(I), TAG(IMG), TAG(LI), + TAG(LISTING), TAG(MENU), TAG(META), TAG(NOBR), TAG(OL), TAG(P), + TAG(PRE), TAG(RUBY), TAG(S), TAG(SMALL), TAG(SPAN), TAG(STRONG), + TAG(STRIKE), TAG(SUB), TAG(SUP), TAG(TABLE), TAG(TT), TAG(U), + TAG(UL), TAG(VAR)}) || + (tag_is(token, kStartTag, GUMBO_TAG_FONT) && + (token_has_attribute(token, "color") || + token_has_attribute(token, "face") || + token_has_attribute(token, "size")))) { + /* Parse error */ + parser_add_parse_error(parser, token); + + /* + * Fragment case: If the parser was originally created for the HTML + * fragment parsing algorithm, then act as described in the "any other + * start tag" entry below. + */ + if (!is_fragment_parser(parser)) { + do { + pop_current_node(parser); + } while (!(is_mathml_integration_point(get_current_node(parser)) || + is_html_integration_point(get_current_node(parser)) || + get_current_node(parser)->v.element.tag_namespace == + GUMBO_NAMESPACE_HTML)); + parser->_parser_state->_reprocess_current_token = true; + return false; + } + + assert(token->type == GUMBO_TOKEN_START_TAG); + } + + if (token->type == GUMBO_TOKEN_START_TAG) { + const GumboNamespaceEnum current_namespace = + get_adjusted_current_node(parser)->v.element.tag_namespace; + if (current_namespace == GUMBO_NAMESPACE_MATHML) { + adjust_mathml_attributes(parser, token); + } + if (current_namespace == GUMBO_NAMESPACE_SVG) { + // Tag adjustment is left to the gumbo_normalize_svg_tagname helper + // function. + adjust_svg_attributes(parser, token); + } + adjust_foreign_attributes(parser, token); + insert_foreign_element(parser, token, current_namespace); + if (token->v.start_tag.is_self_closing) { + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + } + return true; + // </script> tags are handled like any other end tag, putting the script's + // text into a text node child and closing the current node. + } else { + assert(token->type == GUMBO_TOKEN_END_TAG); + GumboNode* node = get_current_node(parser); + assert(node != NULL); + GumboStringPiece token_tagname = token->original_text; + GumboStringPiece node_tagname = node->v.element.original_tag; + gumbo_tag_from_original_text(&token_tagname); + gumbo_tag_from_original_text(&node_tagname); + + bool is_success = true; + if (!gumbo_string_equals_ignore_case(&node_tagname, &token_tagname)) { + parser_add_parse_error(parser, token); + is_success = false; + } + int i = parser->_parser_state->_open_elements.length; + for (--i; i > 0;) { + // Here we move up the stack until we find an HTML element (in which + // case we do nothing) or we find the element that we're about to + // close (in which case we pop everything we've seen until that + // point.) + gumbo_debug("Foreign %.*s node at %d.\n", node_tagname.length, + node_tagname.data, i); + if (gumbo_string_equals_ignore_case(&node_tagname, &token_tagname)) { + gumbo_debug("Matches.\n"); + while (pop_current_node(parser) != node) { + // Pop all the nodes below the current one. Node is guaranteed to + // be an element on the stack of open elements (set below), so + // this loop is guaranteed to terminate. + } + return is_success; + } + --i; + node = parser->_parser_state->_open_elements.data[i]; + if (node->v.element.tag_namespace == GUMBO_NAMESPACE_HTML) { + // Must break before gumbo_tag_from_original_text to avoid passing + // parser-inserted nodes through. + break; + } + node_tagname = node->v.element.original_tag; + gumbo_tag_from_original_text(&node_tagname); + } + assert(node->v.element.tag_namespace == GUMBO_NAMESPACE_HTML); + // We can't call handle_token directly because the current node is still in + // the SVG namespace, so it would re-enter this and result in infinite + // recursion. + return handle_html_content(parser, token) && is_success; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#tree-construction +static bool handle_token(GumboParser* parser, GumboToken* token) { + if (parser->_parser_state->_ignore_next_linefeed && + token->type == GUMBO_TOKEN_WHITESPACE && token->v.character == '\n') { + parser->_parser_state->_ignore_next_linefeed = false; + ignore_token(parser); + return true; + } + // This needs to be reset both here and in the conditional above to catch both + // the case where the next token is not whitespace (so we don't ignore + // whitespace in the middle of <pre> tags) and where there are multiple + // whitespace tokens (so we don't ignore the second one). + parser->_parser_state->_ignore_next_linefeed = false; + + if (tag_is(token, kEndTag, GUMBO_TAG_BODY)) { + parser->_parser_state->_closed_body_tag = true; + } + if (tag_is(token, kEndTag, GUMBO_TAG_HTML)) { + parser->_parser_state->_closed_html_tag = true; + } + + const GumboNode* current_node = get_adjusted_current_node(parser); + assert(!current_node || current_node->type == GUMBO_NODE_ELEMENT || + current_node->type == GUMBO_NODE_TEMPLATE); + if (current_node) { + gumbo_debug("Current node: <%s>.\n", + gumbo_normalized_tagname(current_node->v.element.tag)); + } + if (!current_node || + current_node->v.element.tag_namespace == GUMBO_NAMESPACE_HTML || + (is_mathml_integration_point(current_node) && + (token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_WHITESPACE || + token->type == GUMBO_TOKEN_NULL || + (token->type == GUMBO_TOKEN_START_TAG && + !tag_in(token, kStartTag, + (gumbo_tagset){TAG(MGLYPH), TAG(MALIGNMARK)})))) || + (current_node->v.element.tag_namespace == GUMBO_NAMESPACE_MATHML && + node_qualified_tag_is( + current_node, GUMBO_NAMESPACE_MATHML, GUMBO_TAG_ANNOTATION_XML) && + tag_is(token, kStartTag, GUMBO_TAG_SVG)) || + (is_html_integration_point(current_node) && + (token->type == GUMBO_TOKEN_START_TAG || + token->type == GUMBO_TOKEN_CHARACTER || + token->type == GUMBO_TOKEN_NULL || + token->type == GUMBO_TOKEN_WHITESPACE)) || + token->type == GUMBO_TOKEN_EOF) { + return handle_html_content(parser, token); + } else { + return handle_in_foreign_content(parser, token); + } +} + +static void fragment_parser_init(GumboParser* parser, GumboTag fragment_ctx, + GumboNamespaceEnum fragment_namespace) { + GumboNode* root; + assert(fragment_ctx != GUMBO_TAG_LAST); + + // 3 + parser->_parser_state->_fragment_ctx = create_element(parser, fragment_ctx); + parser->_parser_state->_fragment_ctx->v.element.tag_namespace = + fragment_namespace; + + // 4 + if (fragment_namespace == GUMBO_NAMESPACE_HTML) { + // Non-HTML namespaces always start in the DATA state. + switch (fragment_ctx) { + case GUMBO_TAG_TITLE: + case GUMBO_TAG_TEXTAREA: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA); + break; + + case GUMBO_TAG_STYLE: + case GUMBO_TAG_XMP: + case GUMBO_TAG_IFRAME: + case GUMBO_TAG_NOEMBED: + case GUMBO_TAG_NOFRAMES: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT); + break; + + case GUMBO_TAG_SCRIPT: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + break; + + case GUMBO_TAG_NOSCRIPT: + /* scripting is disabled in Gumbo, so leave the tokenizer + * in the default data state */ + break; + + case GUMBO_TAG_PLAINTEXT: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_PLAINTEXT); + break; + + default: + /* default data state */ + break; + } + } + + // 5. 6. 7. + root = insert_element_of_tag_type( + parser, GUMBO_TAG_HTML, GUMBO_INSERTION_IMPLIED); + parser->_output->root = root; + + // 8. + if (fragment_ctx == GUMBO_TAG_TEMPLATE) { + push_template_insertion_mode(parser, GUMBO_INSERTION_MODE_IN_TEMPLATE); + } + + // 10. + reset_insertion_mode_appropriately(parser); +} + +GumboOutput* gumbo_parse(const char* buffer) { + return gumbo_parse_with_options( + &kGumboDefaultOptions, buffer, strlen(buffer)); +} + +GumboOutput* gumbo_parse_with_options( + const GumboOptions* options, const char* buffer, size_t length) { + GumboParser parser; + parser._options = options; + output_init(&parser); + gumbo_tokenizer_state_init(&parser, buffer, length); + parser_state_init(&parser); + + if (options->fragment_context != GUMBO_TAG_LAST) { + fragment_parser_init( + &parser, options->fragment_context, options->fragment_namespace); + } + + GumboParserState* state = parser._parser_state; + gumbo_debug("Parsing %.*s.\n", length, buffer); + + // Sanity check so that infinite loops die with an assertion failure instead + // of hanging the process before we ever get an error. + int loop_count = 0; + + GumboToken token; + bool has_error = false; + + do { + if (state->_reprocess_current_token) { + state->_reprocess_current_token = false; + } else { + GumboNode* current_node = get_current_node(&parser); + gumbo_tokenizer_set_is_current_node_foreign(&parser, + current_node && + current_node->v.element.tag_namespace != GUMBO_NAMESPACE_HTML); + has_error = !gumbo_lex(&parser, &token) || has_error; + } + const char* token_type = "text"; + switch (token.type) { + case GUMBO_TOKEN_DOCTYPE: + token_type = "doctype"; + break; + case GUMBO_TOKEN_START_TAG: + token_type = gumbo_normalized_tagname(token.v.start_tag.tag); + break; + case GUMBO_TOKEN_END_TAG: + token_type = gumbo_normalized_tagname(token.v.end_tag); + break; + case GUMBO_TOKEN_COMMENT: + token_type = "comment"; + break; + default: + break; + } + gumbo_debug("Handling %s token @%d:%d in state %d.\n", (char*) token_type, + token.position.line, token.position.column, state->_insertion_mode); + + state->_current_token = &token; + state->_self_closing_flag_acknowledged = + !(token.type == GUMBO_TOKEN_START_TAG && + token.v.start_tag.is_self_closing); + + has_error = !handle_token(&parser, &token) || has_error; + + // Check for memory leaks when ownership is transferred from start tag + // tokens to nodes. + assert(state->_reprocess_current_token || + token.type != GUMBO_TOKEN_START_TAG || + token.v.start_tag.attributes.data == NULL); + + if (!state->_self_closing_flag_acknowledged) { + GumboError* error = parser_add_parse_error(&parser, &token); + if (error) { + error->type = GUMBO_ERR_UNACKNOWLEDGED_SELF_CLOSING_TAG; + } + } + + ++loop_count; + assert(loop_count < 1000000000); + + } while ((token.type != GUMBO_TOKEN_EOF || state->_reprocess_current_token) && + !(options->stop_on_first_error && has_error)); + + finish_parsing(&parser); + // For API uniformity reasons, if the doctype still has nulls, convert them to + // empty strings. + GumboDocument* doc_type = &parser._output->document->v.document; + if (doc_type->name == NULL) { + doc_type->name = gumbo_copy_stringz(&parser, ""); + } + if (doc_type->public_identifier == NULL) { + doc_type->public_identifier = gumbo_copy_stringz(&parser, ""); + } + if (doc_type->system_identifier == NULL) { + doc_type->system_identifier = gumbo_copy_stringz(&parser, ""); + } + + parser_state_destroy(&parser); + gumbo_tokenizer_state_destroy(&parser); + return parser._output; +} + +void gumbo_destroy_node(GumboOptions* options, GumboNode* node) { + // Need a dummy GumboParser because the allocator comes along with the + // options object. + GumboParser parser; + parser._options = options; + destroy_node(&parser, node); +} + +void gumbo_destroy_output(const GumboOptions* options, GumboOutput* output) { + // Need a dummy GumboParser because the allocator comes along with the + // options object. + GumboParser parser; + parser._options = options; + destroy_node(&parser, output->document); + for (unsigned int i = 0; i < output->errors.length; ++i) { + gumbo_error_destroy(&parser, output->errors.data[i]); + } + gumbo_vector_destroy(&parser, &output->errors); + gumbo_parser_deallocate(&parser, output); +} diff --git a/gb.form.htmlview/src/gumbo/parser.h b/gb.form.htmlview/src/gumbo/parser.h new file mode 100644 index 00000000..95019e3e --- /dev/null +++ b/gb.form.htmlview/src/gumbo/parser.h @@ -0,0 +1,57 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// Contains the definition of the top-level GumboParser structure that's +// threaded through basically every internal function in the library. + +#ifndef GUMBO_PARSER_H_ +#define GUMBO_PARSER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalParserState; +struct GumboInternalOutput; +struct GumboInternalOptions; +struct GumboInternalTokenizerState; + +// An overarching struct that's threaded through (nearly) all functions in the +// library, OOP-style. This gives each function access to the options and +// output, along with any internal state needed for the parse. +typedef struct GumboInternalParser { + // Settings for this parse run. + const struct GumboInternalOptions* _options; + + // Output for the parse. + struct GumboInternalOutput* _output; + + // The internal tokenizer state, defined as a pointer to avoid a cyclic + // dependency on html5tokenizer.h. The main parse routine is responsible for + // initializing this on parse start, and destroying it on parse end. + // End-users will never see a non-garbage value in this pointer. + struct GumboInternalTokenizerState* _tokenizer_state; + + // The internal parser state. Initialized on parse start and destroyed on + // parse end; end-users will never see a non-garbage value in this pointer. + struct GumboInternalParserState* _parser_state; +} GumboParser; + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_PARSER_H_ diff --git a/gb.form.htmlview/src/gumbo/string_buffer.c b/gb.form.htmlview/src/gumbo/string_buffer.c new file mode 100644 index 00000000..d9be2f6b --- /dev/null +++ b/gb.form.htmlview/src/gumbo/string_buffer.c @@ -0,0 +1,110 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "string_buffer.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +#include "string_piece.h" +#include "util.h" + +struct GumboInternalParser; + +// Size chosen via statistical analysis of ~60K websites. +// 99% of text nodes and 98% of attribute names/values fit in this initial size. +static const size_t kDefaultStringBufferSize = 5; + +static void maybe_resize_string_buffer(struct GumboInternalParser* parser, + size_t additional_chars, GumboStringBuffer* buffer) { + size_t new_length = buffer->length + additional_chars; + size_t new_capacity = buffer->capacity; + while (new_capacity < new_length) { + new_capacity *= 2; + } + if (new_capacity != buffer->capacity) { + char* new_data = gumbo_parser_allocate(parser, new_capacity); + memcpy(new_data, buffer->data, buffer->length); + gumbo_parser_deallocate(parser, buffer->data); + buffer->data = new_data; + buffer->capacity = new_capacity; + } +} + +void gumbo_string_buffer_init( + struct GumboInternalParser* parser, GumboStringBuffer* output) { + output->data = gumbo_parser_allocate(parser, kDefaultStringBufferSize); + output->length = 0; + output->capacity = kDefaultStringBufferSize; +} + +void gumbo_string_buffer_reserve(struct GumboInternalParser* parser, + size_t min_capacity, GumboStringBuffer* output) { + maybe_resize_string_buffer(parser, min_capacity - output->length, output); +} + +void gumbo_string_buffer_append_codepoint( + struct GumboInternalParser* parser, int c, GumboStringBuffer* output) { + // num_bytes is actually the number of continuation bytes, 1 less than the + // total number of bytes. This is done to keep the loop below simple and + // should probably change if we unroll it. + int num_bytes, prefix; + if (c <= 0x7f) { + num_bytes = 0; + prefix = 0; + } else if (c <= 0x7ff) { + num_bytes = 1; + prefix = 0xc0; + } else if (c <= 0xffff) { + num_bytes = 2; + prefix = 0xe0; + } else { + num_bytes = 3; + prefix = 0xf0; + } + maybe_resize_string_buffer(parser, num_bytes + 1, output); + output->data[output->length++] = prefix | (c >> (num_bytes * 6)); + for (int i = num_bytes - 1; i >= 0; --i) { + output->data[output->length++] = 0x80 | (0x3f & (c >> (i * 6))); + } +} + +void gumbo_string_buffer_append_string(struct GumboInternalParser* parser, + GumboStringPiece* str, GumboStringBuffer* output) { + maybe_resize_string_buffer(parser, str->length, output); + memcpy(output->data + output->length, str->data, str->length); + output->length += str->length; +} + +char* gumbo_string_buffer_to_string( + struct GumboInternalParser* parser, GumboStringBuffer* input) { + char* buffer = gumbo_parser_allocate(parser, input->length + 1); + memcpy(buffer, input->data, input->length); + buffer[input->length] = '\0'; + return buffer; +} + +void gumbo_string_buffer_clear( + struct GumboInternalParser* parser, GumboStringBuffer* input) { + input->length = 0; +} + +void gumbo_string_buffer_destroy( + struct GumboInternalParser* parser, GumboStringBuffer* buffer) { + gumbo_parser_deallocate(parser, buffer->data); +} diff --git a/gb.form.htmlview/src/gumbo/string_buffer.h b/gb.form.htmlview/src/gumbo/string_buffer.h new file mode 100644 index 00000000..ee7956ac --- /dev/null +++ b/gb.form.htmlview/src/gumbo/string_buffer.h @@ -0,0 +1,84 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +#ifndef GUMBO_STRING_BUFFER_H_ +#define GUMBO_STRING_BUFFER_H_ + +#include <stdbool.h> +#include <stddef.h> + +#include "gumbo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalParser; + +// A struct representing a mutable, growable string. This consists of a +// heap-allocated buffer that may grow (by doubling) as necessary. When +// converting to a string, this allocates a new buffer that is only as long as +// it needs to be. Note that the internal buffer here is *not* nul-terminated, +// so be sure not to use ordinary string manipulation functions on it. +typedef struct { + // A pointer to the beginning of the string. NULL iff length == 0. + char* data; + + // The length of the string fragment, in bytes. May be zero. + size_t length; + + // The capacity of the buffer, in bytes. + size_t capacity; +} GumboStringBuffer; + +// Initializes a new GumboStringBuffer. +void gumbo_string_buffer_init( + struct GumboInternalParser* parser, GumboStringBuffer* output); + +// Ensures that the buffer contains at least a certain amount of space. Most +// useful with snprintf and the other length-delimited string functions, which +// may want to write directly into the buffer. +void gumbo_string_buffer_reserve(struct GumboInternalParser* parser, + size_t min_capacity, GumboStringBuffer* output); + +// Appends a single Unicode codepoint onto the end of the GumboStringBuffer. +// This is essentially a UTF-8 encoder, and may add 1-4 bytes depending on the +// value of the codepoint. +void gumbo_string_buffer_append_codepoint( + struct GumboInternalParser* parser, int c, GumboStringBuffer* output); + +// Appends a string onto the end of the GumboStringBuffer. +void gumbo_string_buffer_append_string(struct GumboInternalParser* parser, + GumboStringPiece* str, GumboStringBuffer* output); + +// Converts this string buffer to const char*, alloctaing a new buffer for it. +char* gumbo_string_buffer_to_string( + struct GumboInternalParser* parser, GumboStringBuffer* input); + +// Reinitialize this string buffer. This clears it by setting length=0. It +// does not zero out the buffer itself. +void gumbo_string_buffer_clear( + struct GumboInternalParser* parser, GumboStringBuffer* input); + +// Deallocates this GumboStringBuffer. +void gumbo_string_buffer_destroy( + struct GumboInternalParser* parser, GumboStringBuffer* buffer); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_STRING_BUFFER_H_ diff --git a/gb.form.htmlview/src/gumbo/string_piece.c b/gb.form.htmlview/src/gumbo/string_piece.c new file mode 100644 index 00000000..8ad5b846 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/string_piece.c @@ -0,0 +1,48 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "string_piece.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +#include "util.h" + +struct GumboInternalParser; + +const GumboStringPiece kGumboEmptyString = {NULL, 0}; + +bool gumbo_string_equals( + const GumboStringPiece* str1, const GumboStringPiece* str2) { + return str1->length == str2->length && + !memcmp(str1->data, str2->data, str1->length); +} + +bool gumbo_string_equals_ignore_case( + const GumboStringPiece* str1, const GumboStringPiece* str2) { + return str1->length == str2->length && + !strncasecmp(str1->data, str2->data, str1->length); +} + +void gumbo_string_copy(struct GumboInternalParser* parser, + GumboStringPiece* dest, const GumboStringPiece* source) { + dest->length = source->length; + char* buffer = gumbo_parser_allocate(parser, source->length); + memcpy(buffer, source->data, source->length); + dest->data = buffer; +} diff --git a/gb.form.htmlview/src/gumbo/string_piece.h b/gb.form.htmlview/src/gumbo/string_piece.h new file mode 100644 index 00000000..8c8188c5 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/string_piece.h @@ -0,0 +1,38 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#ifndef GUMBO_STRING_PIECE_H_ +#define GUMBO_STRING_PIECE_H_ + +#include "gumbo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalParser; + +// Performs a deep-copy of an GumboStringPiece, allocating a fresh buffer in the +// destination and copying over the characters from source. Dest should be +// empty, with no buffer allocated; otherwise, this leaks it. +void gumbo_string_copy(struct GumboInternalParser* parser, + GumboStringPiece* dest, const GumboStringPiece* source); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_STRING_PIECE_H_ diff --git a/gb.form.htmlview/src/gumbo/tag.c b/gb.form.htmlview/src/gumbo/tag.c new file mode 100644 index 00000000..a394c0a6 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tag.c @@ -0,0 +1,95 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "gumbo.h" + +#include <assert.h> +#include <ctype.h> +#include <string.h> + +const char* kGumboTagNames[] = { +#include "tag_strings.h" + "", // TAG_UNKNOWN + "", // TAG_LAST +}; + +static const unsigned char kGumboTagSizes[] = { +#include "tag_sizes.h" + 0, // TAG_UNKNOWN + 0, // TAG_LAST +}; + +const char* gumbo_normalized_tagname(GumboTag tag) { + assert(tag <= GUMBO_TAG_LAST); + return kGumboTagNames[tag]; +} + +void gumbo_tag_from_original_text(GumboStringPiece* text) { + if (text->data == NULL) { + return; + } + + assert(text->length >= 2); + assert(text->data[0] == '<'); + assert(text->data[text->length - 1] == '>'); + if (text->data[1] == '/') { + // End tag. + assert(text->length >= 3); + text->data += 2; // Move past </ + text->length -= 3; + } else { + // Start tag. + text->data += 1; // Move past < + text->length -= 2; + // strnchr is apparently not a standard C library function, so I loop + // explicitly looking for whitespace or other illegal tag characters. + for (const char* c = text->data; c != text->data + text->length; ++c) { + if (isspace(*c) || *c == '/') { + text->length = c - text->data; + break; + } + } + } +} + +static int case_memcmp(const char* s1, const char* s2, unsigned int n) { + while (n--) { + unsigned char c1 = tolower(*s1++); + unsigned char c2 = tolower(*s2++); + if (c1 != c2) return (int) c1 - (int) c2; + } + return 0; +} + +#include "tag_gperf.h" +#define TAG_MAP_SIZE (sizeof(kGumboTagMap) / sizeof(kGumboTagMap[0])) + +GumboTag gumbo_tagn_enum(const char* tagname, unsigned int length) { + if (length) { + unsigned int key = tag_hash(tagname, length); + if (key < TAG_MAP_SIZE) { + GumboTag tag = kGumboTagMap[key]; + if (length == kGumboTagSizes[(int) tag] && + !case_memcmp(tagname, kGumboTagNames[(int) tag], length)) + return tag; + } + } + return GUMBO_TAG_UNKNOWN; +} + +GumboTag gumbo_tag_enum(const char* tagname) { + return gumbo_tagn_enum(tagname, (unsigned)strlen(tagname)); +} diff --git a/gb.form.htmlview/src/gumbo/tag_enum.h b/gb.form.htmlview/src/gumbo/tag_enum.h new file mode 100644 index 00000000..6d7aeb3d --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tag_enum.h @@ -0,0 +1,153 @@ +// Generated via `gentags.py src/tag.in`. +// Do not edit; edit src/tag.in instead. +// clang-format off +GUMBO_TAG_HTML, +GUMBO_TAG_HEAD, +GUMBO_TAG_TITLE, +GUMBO_TAG_BASE, +GUMBO_TAG_LINK, +GUMBO_TAG_META, +GUMBO_TAG_STYLE, +GUMBO_TAG_SCRIPT, +GUMBO_TAG_NOSCRIPT, +GUMBO_TAG_TEMPLATE, +GUMBO_TAG_BODY, +GUMBO_TAG_ARTICLE, +GUMBO_TAG_SECTION, +GUMBO_TAG_NAV, +GUMBO_TAG_ASIDE, +GUMBO_TAG_H1, +GUMBO_TAG_H2, +GUMBO_TAG_H3, +GUMBO_TAG_H4, +GUMBO_TAG_H5, +GUMBO_TAG_H6, +GUMBO_TAG_HGROUP, +GUMBO_TAG_HEADER, +GUMBO_TAG_FOOTER, +GUMBO_TAG_ADDRESS, +GUMBO_TAG_P, +GUMBO_TAG_HR, +GUMBO_TAG_PRE, +GUMBO_TAG_BLOCKQUOTE, +GUMBO_TAG_OL, +GUMBO_TAG_UL, +GUMBO_TAG_LI, +GUMBO_TAG_DL, +GUMBO_TAG_DT, +GUMBO_TAG_DD, +GUMBO_TAG_FIGURE, +GUMBO_TAG_FIGCAPTION, +GUMBO_TAG_MAIN, +GUMBO_TAG_DIV, +GUMBO_TAG_A, +GUMBO_TAG_EM, +GUMBO_TAG_STRONG, +GUMBO_TAG_SMALL, +GUMBO_TAG_S, +GUMBO_TAG_CITE, +GUMBO_TAG_Q, +GUMBO_TAG_DFN, +GUMBO_TAG_ABBR, +GUMBO_TAG_DATA, +GUMBO_TAG_TIME, +GUMBO_TAG_CODE, +GUMBO_TAG_VAR, +GUMBO_TAG_SAMP, +GUMBO_TAG_KBD, +GUMBO_TAG_SUB, +GUMBO_TAG_SUP, +GUMBO_TAG_I, +GUMBO_TAG_B, +GUMBO_TAG_U, +GUMBO_TAG_MARK, +GUMBO_TAG_RUBY, +GUMBO_TAG_RT, +GUMBO_TAG_RP, +GUMBO_TAG_BDI, +GUMBO_TAG_BDO, +GUMBO_TAG_SPAN, +GUMBO_TAG_BR, +GUMBO_TAG_WBR, +GUMBO_TAG_INS, +GUMBO_TAG_DEL, +GUMBO_TAG_IMAGE, +GUMBO_TAG_IMG, +GUMBO_TAG_IFRAME, +GUMBO_TAG_EMBED, +GUMBO_TAG_OBJECT, +GUMBO_TAG_PARAM, +GUMBO_TAG_VIDEO, +GUMBO_TAG_AUDIO, +GUMBO_TAG_SOURCE, +GUMBO_TAG_TRACK, +GUMBO_TAG_CANVAS, +GUMBO_TAG_MAP, +GUMBO_TAG_AREA, +GUMBO_TAG_MATH, +GUMBO_TAG_MI, +GUMBO_TAG_MO, +GUMBO_TAG_MN, +GUMBO_TAG_MS, +GUMBO_TAG_MTEXT, +GUMBO_TAG_MGLYPH, +GUMBO_TAG_MALIGNMARK, +GUMBO_TAG_ANNOTATION_XML, +GUMBO_TAG_SVG, +GUMBO_TAG_FOREIGNOBJECT, +GUMBO_TAG_DESC, +GUMBO_TAG_TABLE, +GUMBO_TAG_CAPTION, +GUMBO_TAG_COLGROUP, +GUMBO_TAG_COL, +GUMBO_TAG_TBODY, +GUMBO_TAG_THEAD, +GUMBO_TAG_TFOOT, +GUMBO_TAG_TR, +GUMBO_TAG_TD, +GUMBO_TAG_TH, +GUMBO_TAG_FORM, +GUMBO_TAG_FIELDSET, +GUMBO_TAG_LEGEND, +GUMBO_TAG_LABEL, +GUMBO_TAG_INPUT, +GUMBO_TAG_BUTTON, +GUMBO_TAG_SELECT, +GUMBO_TAG_DATALIST, +GUMBO_TAG_OPTGROUP, +GUMBO_TAG_OPTION, +GUMBO_TAG_TEXTAREA, +GUMBO_TAG_KEYGEN, +GUMBO_TAG_OUTPUT, +GUMBO_TAG_PROGRESS, +GUMBO_TAG_METER, +GUMBO_TAG_DETAILS, +GUMBO_TAG_SUMMARY, +GUMBO_TAG_MENU, +GUMBO_TAG_MENUITEM, +GUMBO_TAG_APPLET, +GUMBO_TAG_ACRONYM, +GUMBO_TAG_BGSOUND, +GUMBO_TAG_DIR, +GUMBO_TAG_FRAME, +GUMBO_TAG_FRAMESET, +GUMBO_TAG_NOFRAMES, +GUMBO_TAG_ISINDEX, +GUMBO_TAG_LISTING, +GUMBO_TAG_XMP, +GUMBO_TAG_NEXTID, +GUMBO_TAG_NOEMBED, +GUMBO_TAG_PLAINTEXT, +GUMBO_TAG_RB, +GUMBO_TAG_STRIKE, +GUMBO_TAG_BASEFONT, +GUMBO_TAG_BIG, +GUMBO_TAG_BLINK, +GUMBO_TAG_CENTER, +GUMBO_TAG_FONT, +GUMBO_TAG_MARQUEE, +GUMBO_TAG_MULTICOL, +GUMBO_TAG_NOBR, +GUMBO_TAG_SPACER, +GUMBO_TAG_TT, +GUMBO_TAG_RTC, diff --git a/gb.form.htmlview/src/gumbo/tag_gperf.h b/gb.form.htmlview/src/gumbo/tag_gperf.h new file mode 100644 index 00000000..378eaf95 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tag_gperf.h @@ -0,0 +1,105 @@ +static unsigned int tag_hash( + register const char *str, register unsigned int len) { + static unsigned short asso_values[] = {296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 6, 4, 3, 1, 1, 0, + 1, 0, 0, 296, 296, 296, 296, 296, 296, 296, 22, 73, 151, 4, 13, 59, 65, 2, + 69, 0, 134, 9, 16, 52, 55, 28, 101, 0, 1, 6, 63, 126, 104, 93, 124, 296, + 296, 296, 296, 296, 296, 296, 22, 73, 151, 4, 13, 59, 65, 2, 69, 0, 134, + 9, 16, 52, 55, 28, 101, 0, 1, 6, 63, 126, 104, 93, 124, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296}; + register unsigned int hval = len; + + switch (hval) { + default: + hval += asso_values[(unsigned char) str[1] + 3]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char) str[0]]; + break; + } + return hval + asso_values[(unsigned char) str[len - 1]]; +} + +static const unsigned char kGumboTagMap[] = {GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_S, GUMBO_TAG_H6, GUMBO_TAG_H5, GUMBO_TAG_H4, + GUMBO_TAG_H3, GUMBO_TAG_SPACER, GUMBO_TAG_H2, GUMBO_TAG_HEADER, + GUMBO_TAG_H1, GUMBO_TAG_HEAD, GUMBO_TAG_LAST, GUMBO_TAG_DETAILS, + GUMBO_TAG_SELECT, GUMBO_TAG_DIR, GUMBO_TAG_LAST, GUMBO_TAG_DEL, + GUMBO_TAG_LAST, GUMBO_TAG_SOURCE, GUMBO_TAG_LEGEND, GUMBO_TAG_DATALIST, + GUMBO_TAG_METER, GUMBO_TAG_MGLYPH, GUMBO_TAG_LAST, GUMBO_TAG_MATH, + GUMBO_TAG_LABEL, GUMBO_TAG_TABLE, GUMBO_TAG_TEMPLATE, GUMBO_TAG_LAST, + GUMBO_TAG_RP, GUMBO_TAG_TIME, GUMBO_TAG_TITLE, GUMBO_TAG_DATA, + GUMBO_TAG_APPLET, GUMBO_TAG_HGROUP, GUMBO_TAG_SAMP, GUMBO_TAG_TEXTAREA, + GUMBO_TAG_ABBR, GUMBO_TAG_MARQUEE, GUMBO_TAG_LAST, GUMBO_TAG_MENUITEM, + GUMBO_TAG_SMALL, GUMBO_TAG_META, GUMBO_TAG_A, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_EMBED, + GUMBO_TAG_MAP, GUMBO_TAG_LAST, GUMBO_TAG_PARAM, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_NOBR, GUMBO_TAG_P, GUMBO_TAG_SPAN, GUMBO_TAG_EM, + GUMBO_TAG_LAST, GUMBO_TAG_NOFRAMES, GUMBO_TAG_SECTION, GUMBO_TAG_NOEMBED, + GUMBO_TAG_NEXTID, GUMBO_TAG_FOOTER, GUMBO_TAG_NOSCRIPT, GUMBO_TAG_HR, + GUMBO_TAG_LAST, GUMBO_TAG_FONT, GUMBO_TAG_DL, GUMBO_TAG_TR, + GUMBO_TAG_SCRIPT, GUMBO_TAG_MO, GUMBO_TAG_LAST, GUMBO_TAG_DD, + GUMBO_TAG_MAIN, GUMBO_TAG_TD, GUMBO_TAG_FOREIGNOBJECT, GUMBO_TAG_FORM, + GUMBO_TAG_OBJECT, GUMBO_TAG_LAST, GUMBO_TAG_FIELDSET, GUMBO_TAG_LAST, + GUMBO_TAG_BGSOUND, GUMBO_TAG_MENU, GUMBO_TAG_TFOOT, GUMBO_TAG_FIGURE, + GUMBO_TAG_RB, GUMBO_TAG_LI, GUMBO_TAG_LISTING, GUMBO_TAG_BASEFONT, + GUMBO_TAG_OPTGROUP, GUMBO_TAG_LAST, GUMBO_TAG_BASE, GUMBO_TAG_ADDRESS, + GUMBO_TAG_MI, GUMBO_TAG_LAST, GUMBO_TAG_PLAINTEXT, GUMBO_TAG_LAST, + GUMBO_TAG_PROGRESS, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_ACRONYM, GUMBO_TAG_ARTICLE, GUMBO_TAG_LAST, GUMBO_TAG_PRE, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_AREA, + GUMBO_TAG_RT, GUMBO_TAG_LAST, GUMBO_TAG_OPTION, GUMBO_TAG_IMAGE, + GUMBO_TAG_DT, GUMBO_TAG_LAST, GUMBO_TAG_TT, GUMBO_TAG_HTML, GUMBO_TAG_WBR, + GUMBO_TAG_OL, GUMBO_TAG_LAST, GUMBO_TAG_STYLE, GUMBO_TAG_STRIKE, + GUMBO_TAG_SUP, GUMBO_TAG_MULTICOL, GUMBO_TAG_U, GUMBO_TAG_DFN, GUMBO_TAG_UL, + GUMBO_TAG_FIGCAPTION, GUMBO_TAG_MTEXT, GUMBO_TAG_LAST, GUMBO_TAG_VAR, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_FRAMESET, GUMBO_TAG_LAST, + GUMBO_TAG_BR, GUMBO_TAG_I, GUMBO_TAG_FRAME, GUMBO_TAG_LAST, GUMBO_TAG_DIV, + GUMBO_TAG_LAST, GUMBO_TAG_TH, GUMBO_TAG_MS, GUMBO_TAG_ANNOTATION_XML, + GUMBO_TAG_B, GUMBO_TAG_TBODY, GUMBO_TAG_THEAD, GUMBO_TAG_BIG, + GUMBO_TAG_BLOCKQUOTE, GUMBO_TAG_XMP, GUMBO_TAG_LAST, GUMBO_TAG_KBD, + GUMBO_TAG_LAST, GUMBO_TAG_LINK, GUMBO_TAG_IFRAME, GUMBO_TAG_MARK, + GUMBO_TAG_CENTER, GUMBO_TAG_OUTPUT, GUMBO_TAG_DESC, GUMBO_TAG_CANVAS, + GUMBO_TAG_COL, GUMBO_TAG_MALIGNMARK, GUMBO_TAG_IMG, GUMBO_TAG_ASIDE, + GUMBO_TAG_LAST, GUMBO_TAG_CODE, GUMBO_TAG_LAST, GUMBO_TAG_SUB, GUMBO_TAG_MN, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_INS, GUMBO_TAG_AUDIO, + GUMBO_TAG_STRONG, GUMBO_TAG_CITE, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_INPUT, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_NAV, GUMBO_TAG_LAST, GUMBO_TAG_COLGROUP, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_SVG, GUMBO_TAG_KEYGEN, GUMBO_TAG_VIDEO, + GUMBO_TAG_BDO, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_BODY, GUMBO_TAG_LAST, GUMBO_TAG_Q, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_TRACK, + GUMBO_TAG_LAST, GUMBO_TAG_BDI, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_CAPTION, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_RUBY, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_BUTTON, + GUMBO_TAG_SUMMARY, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_RTC, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_BLINK, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, + GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_ISINDEX}; diff --git a/gb.form.htmlview/src/gumbo/tag_sizes.h b/gb.form.htmlview/src/gumbo/tag_sizes.h new file mode 100644 index 00000000..7c92de07 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tag_sizes.h @@ -0,0 +1,4 @@ +// Generated via `gentags.py src/tag.in`. +// Do not edit; edit src/tag.in instead. +// clang-format off +4, 4, 5, 4, 4, 4, 5, 6, 8, 8, 4, 7, 7, 3, 5, 2, 2, 2, 2, 2, 2, 6, 6, 6, 7, 1, 2, 3, 10, 2, 2, 2, 2, 2, 2, 6, 10, 4, 3, 1, 2, 6, 5, 1, 4, 1, 3, 4, 4, 4, 4, 3, 4, 3, 3, 3, 1, 1, 1, 4, 4, 2, 2, 3, 3, 4, 2, 3, 3, 3, 5, 3, 6, 5, 6, 5, 5, 5, 6, 5, 6, 3, 4, 4, 2, 2, 2, 2, 5, 6, 10, 14, 3, 13, 4, 5, 7, 8, 3, 5, 5, 5, 2, 2, 2, 4, 8, 6, 5, 5, 6, 6, 8, 8, 6, 8, 6, 6, 8, 5, 7, 7, 4, 8, 6, 7, 7, 3, 5, 8, 8, 7, 7, 3, 6, 7, 9, 2, 6, 8, 3, 5, 6, 4, 7, 8, 4, 6, 2, 3, \ No newline at end of file diff --git a/gb.form.htmlview/src/gumbo/tag_strings.h b/gb.form.htmlview/src/gumbo/tag_strings.h new file mode 100644 index 00000000..6540e2e6 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tag_strings.h @@ -0,0 +1,153 @@ +// Generated via `gentags.py src/tag.in`. +// Do not edit; edit src/tag.in instead. +// clang-format off +"html", +"head", +"title", +"base", +"link", +"meta", +"style", +"script", +"noscript", +"template", +"body", +"article", +"section", +"nav", +"aside", +"h1", +"h2", +"h3", +"h4", +"h5", +"h6", +"hgroup", +"header", +"footer", +"address", +"p", +"hr", +"pre", +"blockquote", +"ol", +"ul", +"li", +"dl", +"dt", +"dd", +"figure", +"figcaption", +"main", +"div", +"a", +"em", +"strong", +"small", +"s", +"cite", +"q", +"dfn", +"abbr", +"data", +"time", +"code", +"var", +"samp", +"kbd", +"sub", +"sup", +"i", +"b", +"u", +"mark", +"ruby", +"rt", +"rp", +"bdi", +"bdo", +"span", +"br", +"wbr", +"ins", +"del", +"image", +"img", +"iframe", +"embed", +"object", +"param", +"video", +"audio", +"source", +"track", +"canvas", +"map", +"area", +"math", +"mi", +"mo", +"mn", +"ms", +"mtext", +"mglyph", +"malignmark", +"annotation-xml", +"svg", +"foreignobject", +"desc", +"table", +"caption", +"colgroup", +"col", +"tbody", +"thead", +"tfoot", +"tr", +"td", +"th", +"form", +"fieldset", +"legend", +"label", +"input", +"button", +"select", +"datalist", +"optgroup", +"option", +"textarea", +"keygen", +"output", +"progress", +"meter", +"details", +"summary", +"menu", +"menuitem", +"applet", +"acronym", +"bgsound", +"dir", +"frame", +"frameset", +"noframes", +"isindex", +"listing", +"xmp", +"nextid", +"noembed", +"plaintext", +"rb", +"strike", +"basefont", +"big", +"blink", +"center", +"font", +"marquee", +"multicol", +"nobr", +"spacer", +"tt", +"rtc", diff --git a/gb.form.htmlview/src/gumbo/token_type.h b/gb.form.htmlview/src/gumbo/token_type.h new file mode 100644 index 00000000..eeab5078 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/token_type.h @@ -0,0 +1,41 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#ifndef GUMBO_TOKEN_TYPE_H_ +#define GUMBO_TOKEN_TYPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// An enum representing the type of token. +typedef enum { + GUMBO_TOKEN_DOCTYPE, + GUMBO_TOKEN_START_TAG, + GUMBO_TOKEN_END_TAG, + GUMBO_TOKEN_COMMENT, + GUMBO_TOKEN_WHITESPACE, + GUMBO_TOKEN_CHARACTER, + GUMBO_TOKEN_CDATA, + GUMBO_TOKEN_NULL, + GUMBO_TOKEN_EOF +} GumboTokenType; + +#ifdef __cplusplus +} // extern C +#endif + +#endif // GUMBO_TOKEN_TYPE_H_ diff --git a/gb.form.htmlview/src/gumbo/tokenizer.c b/gb.form.htmlview/src/gumbo/tokenizer.c new file mode 100644 index 00000000..0d0ea0f2 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tokenizer.c @@ -0,0 +1,2897 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// Coding conventions specific to this file: +// +// 1. Functions that fill in a token should be named emit_*, and should be +// followed immediately by a return from the tokenizer (true if no error +// occurred, false if an error occurred). Sometimes the emit functions +// themselves return a boolean so that they can be combined with the return +// statement; in this case, they should match this convention. +// 2. Functions that shuffle data from temporaries to final API structures +// should be named finish_*, and be called just before the tokenizer exits the +// state that accumulates the temporary. +// 3. All internal data structures should be kept in an initialized state from +// tokenizer creation onwards, ready to accept input. When a buffer's flushed +// and reset, it should be deallocated and immediately reinitialized. +// 4. Make sure there are appropriate break statements following each state. +// 5. Assertions on the state of the temporary and tag buffers are usually a +// good idea, and should go at the entry point of each state when added. +// 6. Statement order within states goes: +// 1. Add parse errors, if appropriate. +// 2. Call finish_* functions to build up tag state. +// 2. Switch to new state. Set _reconsume flag if appropriate. +// 3. Perform any other temporary buffer manipulation. +// 4. Emit tokens +// 5. Return/break. +// This order ensures that we can verify that every emit is followed by a +// return, ensures that the correct state is recorded with any parse errors, and +// prevents parse error position from being messed up by possible mark/resets in +// temporary buffer manipulation. + +#include "tokenizer.h" + +#include <assert.h> +#include <stdbool.h> +#include <string.h> + +#include "attribute.h" +#include "char_ref.h" +#include "error.h" +#include "gumbo.h" +#include "parser.h" +#include "string_buffer.h" +#include "string_piece.h" +#include "token_type.h" +#include "tokenizer_states.h" +#include "utf8.h" +#include "util.h" +#include "vector.h" + +// Compared against _script_data_buffer to determine if we're in double-escaped +// script mode. +const GumboStringPiece kScriptTag = {"script", 6}; + +// An enum for the return value of each individual state. +typedef enum { + RETURN_ERROR, // Return false (error) from the tokenizer. + RETURN_SUCCESS, // Return true (success) from the tokenizer. + NEXT_CHAR // Proceed to the next character and continue lexing. +} StateResult; + +// This is a struct containing state necessary to build up a tag token, +// character by character. +typedef struct GumboInternalTagState { + // A buffer to accumulate characters for various GumboStringPiece fields. + GumboStringBuffer _buffer; + + // A pointer to the start of the original text corresponding to the contents + // of the buffer. + const char* _original_text; + + // The current tag enum, computed once the tag name state has finished so that + // the buffer can be re-used for building up attributes. + GumboTag _tag; + + // The starting location of the text in the buffer. + GumboSourcePosition _start_pos; + + // The current list of attributes. This is copied (and ownership of its data + // transferred) to the GumboStartTag token upon completion of the tag. New + // attributes are added as soon as their attribute name state is complete, and + // values are filled in by operating on _attributes.data[attributes.length-1]. + GumboVector /* GumboAttribute */ _attributes; + + // If true, the next attribute value to be finished should be dropped. This + // happens if a duplicate attribute name is encountered - we want to consume + // the attribute value, but shouldn't overwrite the existing value. + bool _drop_next_attr_value; + + // The state that caused the tokenizer to switch into a character reference in + // attribute value state. This is used to set the additional allowed + // character, and is switched back to on completion. Initialized as the + // tokenizer enters the character reference state. + GumboTokenizerEnum _attr_value_state; + + // The last start tag to have been emitted by the tokenizer. This is + // necessary to check for appropriate end tags. + GumboTag _last_start_tag; + + // If true, then this is a start tag. If false, it's an end tag. This is + // necessary to generate the appropriate token type at tag-closing time. + bool _is_start_tag; + + // If true, then this tag is "self-closing" and doesn't have an end tag. + bool _is_self_closing; +} GumboTagState; + +// This is the main tokenizer state struct, containing all state used by in +// tokenizing the input stream. +typedef struct GumboInternalTokenizerState { + // The current lexer state. Starts in GUMBO_LEX_DATA. + GumboTokenizerEnum _state; + + // A flag indicating whether the current input character needs to reconsumed + // in another state, or whether the next input character should be read for + // the next iteration of the state loop. This is set when the spec reads + // "Reconsume the current input character in..." + bool _reconsume_current_input; + + // A flag indicating whether the current node is a foreign element. This is + // set by gumbo_tokenizer_set_is_current_node_foreign and checked in the + // markup declaration state. + bool _is_current_node_foreign; + + // A flag indicating whether the tokenizer is in a CDATA section. If so, then + // text tokens emitted will be GUMBO_TOKEN_CDATA. + bool _is_in_cdata; + + // Certain states (notably character references) may emit two character tokens + // at once, but the contract for lex() fills in only one token at a time. The + // extra character is buffered here, and then this is checked on entry to + // lex(). If a character is stored here, it's immediately emitted and control + // returns from the lexer. kGumboNoChar is used to represent 'no character + // stored.' + // + // Note that characters emitted through this mechanism will have their source + // position marked as the character under the mark, i.e. multiple characters + // may be emitted with the same position. This is desirable for character + // references, but unsuitable for many other cases. Use the _temporary_buffer + // mechanism if the buffered characters must have their original positions in + // the document. + int _buffered_emit_char; + + // A temporary buffer to accumulate characters, as described by the "temporary + // buffer" phrase in the tokenizer spec. We use this in a somewhat unorthodox + // way: we record the specific character to go into the buffer, which may + // sometimes be a lowercased version of the actual input character. However, + // we *also* use utf8iterator_mark() to record the position at tag start. + // When we start flushing the temporary buffer, we set _temporary_buffer_emit + // to the start of it, and then increment it for each call to the tokenizer. + // We also call utf8iterator_reset(), and utf8iterator_next() through the + // input stream, so that tokens emitted by emit_char have the correct position + // and original text. + GumboStringBuffer _temporary_buffer; + + // The current cursor position we're emitting from within + // _temporary_buffer.data. NULL whenever we're not flushing the buffer. + const char* _temporary_buffer_emit; + + // The temporary buffer is also used by the spec to check whether we should + // enter the script data double escaped state, but we can't use the same + // buffer for both because we have to flush out "<s" as emits while still + // maintaining the context that will eventually become "script". This is a + // separate buffer that's used in place of the temporary buffer for states + // that may enter the script data double escape start state. + GumboStringBuffer _script_data_buffer; + + // Pointer to the beginning of the current token in the original buffer; used + // to record the original text. + const char* _token_start; + + // GumboSourcePosition recording the source location of the start of the + // current token. + GumboSourcePosition _token_start_pos; + + // Current tag state. + GumboTagState _tag_state; + + // Doctype state. We use the temporary buffer to accumulate characters (it's + // not used for anything else in the doctype states), and then freshly + // allocate the strings in the doctype token, then copy it over on emit. + GumboTokenDocType _doc_type_state; + + // The UTF8Iterator over the tokenizer input. + Utf8Iterator _input; +} GumboTokenizerState; + +// Adds an ERR_UNEXPECTED_CODE_POINT parse error to the parser's error struct. +static void tokenizer_add_parse_error( + GumboParser* parser, GumboErrorType type) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + utf8iterator_get_position(&tokenizer->_input, &error->position); + error->original_text = utf8iterator_get_char_pointer(&tokenizer->_input); + error->type = type; + error->v.tokenizer.codepoint = utf8iterator_current(&tokenizer->_input); + switch (tokenizer->_state) { + case GUMBO_LEX_DATA: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_DATA; + break; + case GUMBO_LEX_CHAR_REF_IN_DATA: + case GUMBO_LEX_CHAR_REF_IN_RCDATA: + case GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_CHAR_REF; + break; + case GUMBO_LEX_RCDATA: + case GUMBO_LEX_RCDATA_LT: + case GUMBO_LEX_RCDATA_END_TAG_OPEN: + case GUMBO_LEX_RCDATA_END_TAG_NAME: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_RCDATA; + break; + case GUMBO_LEX_RAWTEXT: + case GUMBO_LEX_RAWTEXT_LT: + case GUMBO_LEX_RAWTEXT_END_TAG_OPEN: + case GUMBO_LEX_RAWTEXT_END_TAG_NAME: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_RAWTEXT; + break; + case GUMBO_LEX_PLAINTEXT: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_PLAINTEXT; + break; + case GUMBO_LEX_SCRIPT: + case GUMBO_LEX_SCRIPT_LT: + case GUMBO_LEX_SCRIPT_END_TAG_OPEN: + case GUMBO_LEX_SCRIPT_END_TAG_NAME: + case GUMBO_LEX_SCRIPT_ESCAPED_START: + case GUMBO_LEX_SCRIPT_ESCAPED_START_DASH: + case GUMBO_LEX_SCRIPT_ESCAPED: + case GUMBO_LEX_SCRIPT_ESCAPED_DASH: + case GUMBO_LEX_SCRIPT_ESCAPED_DASH_DASH: + case GUMBO_LEX_SCRIPT_ESCAPED_LT: + case GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_OPEN: + case GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_NAME: + case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_START: + case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED: + case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH: + case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH_DASH: + case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT: + case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_END: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_SCRIPT; + break; + case GUMBO_LEX_TAG_OPEN: + case GUMBO_LEX_END_TAG_OPEN: + case GUMBO_LEX_TAG_NAME: + case GUMBO_LEX_BEFORE_ATTR_NAME: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_TAG; + break; + case GUMBO_LEX_SELF_CLOSING_START_TAG: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_SELF_CLOSING_TAG; + break; + case GUMBO_LEX_ATTR_NAME: + case GUMBO_LEX_AFTER_ATTR_NAME: + case GUMBO_LEX_BEFORE_ATTR_VALUE: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_ATTR_NAME; + break; + case GUMBO_LEX_ATTR_VALUE_DOUBLE_QUOTED: + case GUMBO_LEX_ATTR_VALUE_SINGLE_QUOTED: + case GUMBO_LEX_ATTR_VALUE_UNQUOTED: + case GUMBO_LEX_AFTER_ATTR_VALUE_QUOTED: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_ATTR_VALUE; + break; + case GUMBO_LEX_BOGUS_COMMENT: + case GUMBO_LEX_COMMENT_START: + case GUMBO_LEX_COMMENT_START_DASH: + case GUMBO_LEX_COMMENT: + case GUMBO_LEX_COMMENT_END_DASH: + case GUMBO_LEX_COMMENT_END: + case GUMBO_LEX_COMMENT_END_BANG: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_COMMENT; + break; + case GUMBO_LEX_MARKUP_DECLARATION: + case GUMBO_LEX_DOCTYPE: + case GUMBO_LEX_BEFORE_DOCTYPE_NAME: + case GUMBO_LEX_DOCTYPE_NAME: + case GUMBO_LEX_AFTER_DOCTYPE_NAME: + case GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_KEYWORD: + case GUMBO_LEX_BEFORE_DOCTYPE_PUBLIC_ID: + case GUMBO_LEX_DOCTYPE_PUBLIC_ID_DOUBLE_QUOTED: + case GUMBO_LEX_DOCTYPE_PUBLIC_ID_SINGLE_QUOTED: + case GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_ID: + case GUMBO_LEX_BETWEEN_DOCTYPE_PUBLIC_SYSTEM_ID: + case GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_KEYWORD: + case GUMBO_LEX_BEFORE_DOCTYPE_SYSTEM_ID: + case GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED: + case GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED: + case GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_ID: + case GUMBO_LEX_BOGUS_DOCTYPE: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_DOCTYPE; + break; + case GUMBO_LEX_CDATA: + error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_CDATA; + break; + } +} + +static bool is_alpha(int c) { + // We don't use ISO C isupper/islower functions here because they + // depend upon the program's locale, while the behavior of the HTML5 spec is + // independent of which locale the program is run in. + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +static int ensure_lowercase(int c) { + return c >= 'A' && c <= 'Z' ? c + 0x20 : c; +} + +static GumboTokenType get_char_token_type(bool is_in_cdata, int c) { + if (is_in_cdata && c > 0) { + return GUMBO_TOKEN_CDATA; + } + + switch (c) { + case '\t': + case '\n': + case '\r': + case '\f': + case ' ': + return GUMBO_TOKEN_WHITESPACE; + case 0: + gumbo_debug("Emitted null byte.\n"); + return GUMBO_TOKEN_NULL; + case -1: + return GUMBO_TOKEN_EOF; + default: + return GUMBO_TOKEN_CHARACTER; + } +} + +// Starts recording characters in the temporary buffer. +// Because this needs to reset the utf8iterator_mark to the beginning of the +// text that will eventually be emitted, it needs to be called a couple of +// states before the spec says "Set the temporary buffer to the empty string". +// In general, this should be called whenever there's a transition to a +// "less-than sign state". The initial < and possibly / then need to be +// appended to the temporary buffer, their presence needs to be accounted for in +// states that compare the temporary buffer against a literal value, and +// spec stanzas that say "emit a < and / character token along with a character +// token for each character in the temporary buffer" need to be adjusted to +// account for the presence of the < and / inside the temporary buffer. +static void clear_temporary_buffer(GumboParser* parser) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + assert(!tokenizer->_temporary_buffer_emit); + utf8iterator_mark(&tokenizer->_input); + gumbo_string_buffer_clear(parser, &tokenizer->_temporary_buffer); + // The temporary buffer and script data buffer are the same object in the + // spec, so the script data buffer should be cleared as well. + gumbo_string_buffer_clear(parser, &tokenizer->_script_data_buffer); +} + +// Appends a codepoint to the temporary buffer. +static void append_char_to_temporary_buffer( + GumboParser* parser, int codepoint) { + gumbo_string_buffer_append_codepoint( + parser, codepoint, &parser->_tokenizer_state->_temporary_buffer); +} + +// Checks to see if the temporary buffer equals a certain string. +// Make sure this remains side-effect free; it's used in assertions. +#ifndef NDEBUG +static bool temporary_buffer_equals(GumboParser* parser, const char* text) { + GumboStringBuffer* buffer = &parser->_tokenizer_state->_temporary_buffer; + // TODO(jdtang): See if the extra strlen is a performance problem, and replace + // it with an explicit sizeof(literal) if necessary. I don't think it will + // be, as this is only used in a couple of rare states. + size_t text_len = strlen(text); + return text_len == buffer->length && + memcmp(buffer->data, text, text_len) == 0; +} +#endif + +static void doc_type_state_init(GumboParser* parser) { + GumboTokenDocType* doc_type_state = + &parser->_tokenizer_state->_doc_type_state; + // We initialize these to NULL here so that we don't end up leaking memory if + // we never see a doctype token. When we do see a doctype token, we reset + // them to a freshly-allocated empty string so that we can present a uniform + // interface to client code and not make them check for null. Ownership is + // transferred to the doctype token when it's emitted. + doc_type_state->name = NULL; + doc_type_state->public_identifier = NULL; + doc_type_state->system_identifier = NULL; + doc_type_state->force_quirks = false; + doc_type_state->has_public_identifier = false; + doc_type_state->has_system_identifier = false; +} + +// Sets the token original_text and position to the current iterator position. +// This is necessary because [CDATA[ sections may include text that is ignored +// by the tokenizer. +static void reset_token_start_point(GumboTokenizerState* tokenizer) { + tokenizer->_token_start = utf8iterator_get_char_pointer(&tokenizer->_input); + utf8iterator_get_position(&tokenizer->_input, &tokenizer->_token_start_pos); +} + +// Sets the tag buffer original text and start point to the current iterator +// position. This is necessary because attribute names & values may have +// whitespace preceeding them, and so we can't assume that the actual token +// starting point was the end of the last tag buffer usage. +static void reset_tag_buffer_start_point(GumboParser* parser) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + GumboTagState* tag_state = &tokenizer->_tag_state; + + utf8iterator_get_position(&tokenizer->_input, &tag_state->_start_pos); + tag_state->_original_text = utf8iterator_get_char_pointer(&tokenizer->_input); +} + +// Moves the temporary buffer contents over to the specified output string, +// and clears the temporary buffer. +static void finish_temporary_buffer(GumboParser* parser, const char** output) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + *output = + gumbo_string_buffer_to_string(parser, &tokenizer->_temporary_buffer); + clear_temporary_buffer(parser); +} + +// Advances the iterator past the end of the token, and then fills in the +// relevant position fields. It's assumed that after every emit, the tokenizer +// will immediately return (letting the tree-construction stage read the filled +// in Token). Thus, it's safe to advance the input stream here, since it will +// bypass the advance at the bottom of the state machine loop. +// +// Since this advances the iterator and resets the current input, make sure to +// call it after you've recorded any other data you need for the token. +static void finish_token(GumboParser* parser, GumboToken* token) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + if (!tokenizer->_reconsume_current_input) { + utf8iterator_next(&tokenizer->_input); + } + + token->position = tokenizer->_token_start_pos; + token->original_text.data = tokenizer->_token_start; + reset_token_start_point(tokenizer); + token->original_text.length = + tokenizer->_token_start - token->original_text.data; + if (token->original_text.length > 0 && + token->original_text.data[token->original_text.length - 1] == '\r') { + // The UTF8 iterator will ignore carriage returns in the input stream, which + // means that the next token may start one past a \r character. The pointer + // arithmetic above results in that \r being appended to the original text + // of the preceding token, so we have to adjust its length here to chop the + // \r off. + --token->original_text.length; + } +} + +// Records the doctype public ID, assumed to be in the temporary buffer. +// Convenience method that also sets has_public_identifier to true. +static void finish_doctype_public_id(GumboParser* parser) { + GumboTokenDocType* doc_type_state = + &parser->_tokenizer_state->_doc_type_state; + gumbo_parser_deallocate(parser, (void*) doc_type_state->public_identifier); + finish_temporary_buffer(parser, &doc_type_state->public_identifier); + doc_type_state->has_public_identifier = true; +} + +// Records the doctype system ID, assumed to be in the temporary buffer. +// Convenience method that also sets has_system_identifier to true. +static void finish_doctype_system_id(GumboParser* parser) { + GumboTokenDocType* doc_type_state = + &parser->_tokenizer_state->_doc_type_state; + gumbo_parser_deallocate(parser, (void*) doc_type_state->system_identifier); + finish_temporary_buffer(parser, &doc_type_state->system_identifier); + doc_type_state->has_system_identifier = true; +} + +// Writes a single specified character to the output token. +static void emit_char(GumboParser* parser, int c, GumboToken* output) { + output->type = get_char_token_type(parser->_tokenizer_state->_is_in_cdata, c); + output->v.character = c; + finish_token(parser, output); +} + +// Writes a replacement character token and records a parse error. +// Always returns RETURN_ERROR, per gumbo_lex return value. +static StateResult emit_replacement_char( + GumboParser* parser, GumboToken* output) { + // In all cases, this is because of a null byte in the input stream. + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + emit_char(parser, kUtf8ReplacementChar, output); + return RETURN_ERROR; +} + +// Writes an EOF character token. Always returns RETURN_SUCCESS. +static StateResult emit_eof(GumboParser* parser, GumboToken* output) { + emit_char(parser, -1, output); + return RETURN_SUCCESS; +} + +// Writes the current input character out as a character token. +// Always returns RETURN_SUCCESS. +static bool emit_current_char(GumboParser* parser, GumboToken* output) { + emit_char( + parser, utf8iterator_current(&parser->_tokenizer_state->_input), output); + return RETURN_SUCCESS; +} + +// Writes out a doctype token, copying it from the tokenizer state. +static void emit_doctype(GumboParser* parser, GumboToken* output) { + output->type = GUMBO_TOKEN_DOCTYPE; + output->v.doc_type = parser->_tokenizer_state->_doc_type_state; + finish_token(parser, output); + doc_type_state_init(parser); +} + +// Debug-only function that explicitly sets the attribute vector data to NULL so +// it can be asserted on tag creation, verifying that there are no memory leaks. +static void mark_tag_state_as_empty(GumboTagState* tag_state) { +#ifndef NDEBUG + tag_state->_attributes = kGumboEmptyVector; +#endif +} + +// Writes out the current tag as a start or end tag token. +// Always returns RETURN_SUCCESS. +static StateResult emit_current_tag(GumboParser* parser, GumboToken* output) { + GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state; + if (tag_state->_is_start_tag) { + output->type = GUMBO_TOKEN_START_TAG; + output->v.start_tag.tag = tag_state->_tag; + output->v.start_tag.attributes = tag_state->_attributes; + output->v.start_tag.is_self_closing = tag_state->_is_self_closing; + tag_state->_last_start_tag = tag_state->_tag; + mark_tag_state_as_empty(tag_state); + gumbo_debug( + "Emitted start tag %s.\n", gumbo_normalized_tagname(tag_state->_tag)); + } else { + output->type = GUMBO_TOKEN_END_TAG; + output->v.end_tag = tag_state->_tag; + // In end tags, ownership of the attributes vector is not transferred to the + // token, but it's still initialized as normal, so it must be manually + // deallocated. There may also be attributes to destroy, in certain broken + // cases like </div</th> (the "th" is an attribute there). + for (unsigned int i = 0; i < tag_state->_attributes.length; ++i) { + gumbo_destroy_attribute(parser, tag_state->_attributes.data[i]); + } + gumbo_parser_deallocate(parser, tag_state->_attributes.data); + mark_tag_state_as_empty(tag_state); + gumbo_debug( + "Emitted end tag %s.\n", gumbo_normalized_tagname(tag_state->_tag)); + } + gumbo_string_buffer_destroy(parser, &tag_state->_buffer); + finish_token(parser, output); + gumbo_debug("Original text = %.*s.\n", output->original_text.length, + output->original_text.data); + assert(output->original_text.length >= 2); + assert(output->original_text.data[0] == '<'); + assert(output->original_text.data[output->original_text.length - 1] == '>'); + return RETURN_SUCCESS; +} + +// In some states, we speculatively start a tag, but don't know whether it'll be +// emitted as tag token or as a series of character tokens until we finish it. +// We need to abandon the tag we'd started & free its memory in that case to +// avoid a memory leak. +static void abandon_current_tag(GumboParser* parser) { + GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state; + for (unsigned int i = 0; i < tag_state->_attributes.length; ++i) { + gumbo_destroy_attribute(parser, tag_state->_attributes.data[i]); + } + gumbo_parser_deallocate(parser, tag_state->_attributes.data); + mark_tag_state_as_empty(tag_state); + gumbo_string_buffer_destroy(parser, &tag_state->_buffer); + gumbo_debug("Abandoning current tag.\n"); +} + +// Wraps the consume_char_ref function to handle its output and make the +// appropriate TokenizerState modifications. Returns RETURN_ERROR if a parse +// error occurred, RETURN_SUCCESS otherwise. +static StateResult emit_char_ref(GumboParser* parser, + int additional_allowed_char, bool is_in_attribute, GumboToken* output) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + OneOrTwoCodepoints char_ref; + bool status = consume_char_ref( + parser, &tokenizer->_input, additional_allowed_char, false, &char_ref); + if (char_ref.first != kGumboNoChar) { + // consume_char_ref ends with the iterator pointing at the next character, + // so we need to be sure not advance it again before reading the next token. + tokenizer->_reconsume_current_input = true; + emit_char(parser, char_ref.first, output); + tokenizer->_buffered_emit_char = char_ref.second; + } else { + emit_char(parser, '&', output); + } + return status ? RETURN_SUCCESS : RETURN_ERROR; +} + +// Emits a comment token. Comments use the temporary buffer to accumulate their +// data, and then it's copied over and released to the 'text' field of the +// GumboToken union. Always returns RETURN_SUCCESS. +static StateResult emit_comment(GumboParser* parser, GumboToken* output) { + output->type = GUMBO_TOKEN_COMMENT; + finish_temporary_buffer(parser, &output->v.text); + finish_token(parser, output); + return RETURN_SUCCESS; +} + +// Checks to see we should be flushing accumulated characters in the temporary +// buffer, and fills the output token with the next output character if so. +// Returns true if a character has been emitted and the tokenizer should +// immediately return, false if we're at the end of the temporary buffer and +// should resume normal operation. +static bool maybe_emit_from_temporary_buffer( + GumboParser* parser, GumboToken* output) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + const char* c = tokenizer->_temporary_buffer_emit; + GumboStringBuffer* buffer = &tokenizer->_temporary_buffer; + + if (!c || c >= buffer->data + buffer->length) { + tokenizer->_temporary_buffer_emit = NULL; + return false; + } + + assert(*c == utf8iterator_current(&tokenizer->_input)); + // emit_char also advances the input stream. We need to do some juggling of + // the _reconsume_current_input flag to get the proper behavior when emitting + // previous tokens. Basically, _reconsume_current_input should *never* be set + // when emitting anything from the temporary buffer, since those characters + // have already been advanced past. However, it should be preserved so that + // when the *next* character is encountered again, the tokenizer knows not to + // advance past it. + bool saved_reconsume_state = tokenizer->_reconsume_current_input; + tokenizer->_reconsume_current_input = false; + emit_char(parser, *c, output); + ++tokenizer->_temporary_buffer_emit; + tokenizer->_reconsume_current_input = saved_reconsume_state; + return true; +} + +// Sets up the tokenizer to begin flushing the temporary buffer. +// This resets the input iterator stream to the start of the last tag, sets up +// _temporary_buffer_emit, and then (if the temporary buffer is non-empty) emits +// the first character in it. It returns true if a character was emitted, false +// otherwise. +static bool emit_temporary_buffer(GumboParser* parser, GumboToken* output) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + assert(tokenizer->_temporary_buffer.data); + utf8iterator_reset(&tokenizer->_input); + tokenizer->_temporary_buffer_emit = tokenizer->_temporary_buffer.data; + return maybe_emit_from_temporary_buffer(parser, output); +} + +// Appends a codepoint to the current tag buffer. If +// reinitilize_position_on_first is set, this also initializes the tag buffer +// start point; the only time you would *not* want to pass true for this +// parameter is if you want the original_text to include character (like an +// opening quote) that doesn't appear in the value. +static void append_char_to_tag_buffer( + GumboParser* parser, int codepoint, bool reinitilize_position_on_first) { + GumboStringBuffer* buffer = &parser->_tokenizer_state->_tag_state._buffer; + if (buffer->length == 0 && reinitilize_position_on_first) { + reset_tag_buffer_start_point(parser); + } + gumbo_string_buffer_append_codepoint(parser, codepoint, buffer); +} + +// (Re-)initialize the tag buffer. This also resets the original_text pointer +// and _start_pos field to point to the current position. +static void initialize_tag_buffer(GumboParser* parser) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + GumboTagState* tag_state = &tokenizer->_tag_state; + + gumbo_string_buffer_init(parser, &tag_state->_buffer); + reset_tag_buffer_start_point(parser); +} + +// Initializes the tag_state to start a new tag, keeping track of the opening +// positions and original text. Takes a boolean indicating whether this is a +// start or end tag. +static void start_new_tag(GumboParser* parser, bool is_start_tag) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + GumboTagState* tag_state = &tokenizer->_tag_state; + int c = utf8iterator_current(&tokenizer->_input); + assert(is_alpha(c)); + c = ensure_lowercase(c); + assert(is_alpha(c)); + + initialize_tag_buffer(parser); + gumbo_string_buffer_append_codepoint(parser, c, &tag_state->_buffer); + + assert(tag_state->_attributes.data == NULL); + // Initial size chosen by statistical analysis of a corpus of 60k webpages. + // 99.5% of elements have 0 attributes, 93% of the remainder have 1. These + // numbers are a bit higher for more modern websites (eg. ~45% = 0, ~40% = 1 + // for the HTML5 Spec), but still have basically 99% of nodes with <= 2 attrs. + gumbo_vector_init(parser, 1, &tag_state->_attributes); + tag_state->_drop_next_attr_value = false; + tag_state->_is_start_tag = is_start_tag; + tag_state->_is_self_closing = false; + gumbo_debug("Starting new tag.\n"); +} + +// Fills in the specified char* with the contents of the tag buffer. +static void copy_over_tag_buffer(GumboParser* parser, const char** output) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + GumboTagState* tag_state = &tokenizer->_tag_state; + *output = gumbo_string_buffer_to_string(parser, &tag_state->_buffer); +} + +// Fills in: +// * The original_text GumboStringPiece with the portion of the original +// buffer that corresponds to the tag buffer. +// * The start_pos GumboSourcePosition with the start position of the tag +// buffer. +// * The end_pos GumboSourcePosition with the current source position. +static void copy_over_original_tag_text(GumboParser* parser, + GumboStringPiece* original_text, GumboSourcePosition* start_pos, + GumboSourcePosition* end_pos) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + GumboTagState* tag_state = &tokenizer->_tag_state; + + original_text->data = tag_state->_original_text; + original_text->length = utf8iterator_get_char_pointer(&tokenizer->_input) - + tag_state->_original_text; + if (original_text->data[original_text->length - 1] == '\r') { + // Since \r is skipped by the UTF-8 iterator, it can sometimes end up + // appended to the end of original text even when it's really the first part + // of the next character. If we detect this situation, shrink the length of + // the original text by 1 to remove the carriage return. + --original_text->length; + } + *start_pos = tag_state->_start_pos; + utf8iterator_get_position(&tokenizer->_input, end_pos); +} + +// Releases and then re-initializes the tag buffer. +static void reinitialize_tag_buffer(GumboParser* parser) { + gumbo_parser_deallocate( + parser, parser->_tokenizer_state->_tag_state._buffer.data); + initialize_tag_buffer(parser); +} + +// Moves some data from the temporary buffer over the the tag-based fields in +// TagState. +static void finish_tag_name(GumboParser* parser) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + GumboTagState* tag_state = &tokenizer->_tag_state; + + tag_state->_tag = + gumbo_tagn_enum(tag_state->_buffer.data, (unsigned)tag_state->_buffer.length); + reinitialize_tag_buffer(parser); +} + +// Adds an ERR_DUPLICATE_ATTR parse error to the parser's error struct. +static void add_duplicate_attr_error(GumboParser* parser, const char* attr_name, + int original_index, int new_index) { + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state; + error->type = GUMBO_ERR_DUPLICATE_ATTR; + error->position = tag_state->_start_pos; + error->original_text = tag_state->_original_text; + error->v.duplicate_attr.original_index = original_index; + error->v.duplicate_attr.new_index = new_index; + copy_over_tag_buffer(parser, &error->v.duplicate_attr.name); + reinitialize_tag_buffer(parser); +} + +// Creates a new attribute in the current tag, copying the current tag buffer to +// the attribute's name. The attribute's value starts out as the empty string +// (following the "Boolean attributes" section of the spec) and is only +// overwritten on finish_attribute_value(). If the attribute has already been +// specified, the new attribute is dropped, a parse error is added, and the +// function returns false. Otherwise, this returns true. +static bool finish_attribute_name(GumboParser* parser) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + GumboTagState* tag_state = &tokenizer->_tag_state; + // May've been set by a previous attribute without a value; reset it here. + tag_state->_drop_next_attr_value = false; + assert(tag_state->_attributes.data); + assert(tag_state->_attributes.capacity); + + GumboVector* /* GumboAttribute* */ attributes = &tag_state->_attributes; + for (unsigned int i = 0; i < attributes->length; ++i) { + GumboAttribute* attr = attributes->data[i]; + if (strlen(attr->name) == tag_state->_buffer.length && + memcmp(attr->name, tag_state->_buffer.data, + tag_state->_buffer.length) == 0) { + // Identical attribute; bail. + add_duplicate_attr_error(parser, attr->name, i, attributes->length); + tag_state->_drop_next_attr_value = true; + return false; + } + } + + GumboAttribute* attr = gumbo_parser_allocate(parser, sizeof(GumboAttribute)); + attr->attr_namespace = GUMBO_ATTR_NAMESPACE_NONE; + copy_over_tag_buffer(parser, &attr->name); + copy_over_original_tag_text( + parser, &attr->original_name, &attr->name_start, &attr->name_end); + attr->value = gumbo_copy_stringz(parser, ""); + copy_over_original_tag_text( + parser, &attr->original_value, &attr->name_start, &attr->name_end); + gumbo_vector_add(parser, attr, attributes); + reinitialize_tag_buffer(parser); + return true; +} + +// Finishes an attribute value. This sets the value of the most recently added +// attribute to the current contents of the tag buffer. +static void finish_attribute_value(GumboParser* parser) { + GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state; + if (tag_state->_drop_next_attr_value) { + // Duplicate attribute name detected in an earlier state, so we have to + // ignore the value. + tag_state->_drop_next_attr_value = false; + reinitialize_tag_buffer(parser); + return; + } + + GumboAttribute* attr = + tag_state->_attributes.data[tag_state->_attributes.length - 1]; + gumbo_parser_deallocate(parser, (void*) attr->value); + copy_over_tag_buffer(parser, &attr->value); + copy_over_original_tag_text( + parser, &attr->original_value, &attr->value_start, &attr->value_end); + reinitialize_tag_buffer(parser); +} + +// Returns true if the current end tag matches the last start tag emitted. +static bool is_appropriate_end_tag(GumboParser* parser) { + GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state; + assert(!tag_state->_is_start_tag); + return tag_state->_last_start_tag != GUMBO_TAG_LAST && + tag_state->_last_start_tag == gumbo_tagn_enum(tag_state->_buffer.data, + (unsigned)tag_state->_buffer.length); +} + +void gumbo_tokenizer_state_init( + GumboParser* parser, const char* text, size_t text_length) { + GumboTokenizerState* tokenizer = + gumbo_parser_allocate(parser, sizeof(GumboTokenizerState)); + parser->_tokenizer_state = tokenizer; + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_reconsume_current_input = false; + tokenizer->_is_current_node_foreign = false; + tokenizer->_is_in_cdata = false; + tokenizer->_tag_state._last_start_tag = GUMBO_TAG_LAST; + + tokenizer->_buffered_emit_char = kGumboNoChar; + gumbo_string_buffer_init(parser, &tokenizer->_temporary_buffer); + tokenizer->_temporary_buffer_emit = NULL; + + mark_tag_state_as_empty(&tokenizer->_tag_state); + + gumbo_string_buffer_init(parser, &tokenizer->_script_data_buffer); + tokenizer->_token_start = text; + utf8iterator_init(parser, text, text_length, &tokenizer->_input); + utf8iterator_get_position(&tokenizer->_input, &tokenizer->_token_start_pos); + doc_type_state_init(parser); +} + +void gumbo_tokenizer_state_destroy(GumboParser* parser) { + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + assert(tokenizer->_doc_type_state.name == NULL); + assert(tokenizer->_doc_type_state.public_identifier == NULL); + assert(tokenizer->_doc_type_state.system_identifier == NULL); + gumbo_string_buffer_destroy(parser, &tokenizer->_temporary_buffer); + gumbo_string_buffer_destroy(parser, &tokenizer->_script_data_buffer); + gumbo_parser_deallocate(parser, tokenizer); +} + +void gumbo_tokenizer_set_state(GumboParser* parser, GumboTokenizerEnum state) { + parser->_tokenizer_state->_state = state; +} + +void gumbo_tokenizer_set_is_current_node_foreign( + GumboParser* parser, bool is_foreign) { + if (is_foreign != parser->_tokenizer_state->_is_current_node_foreign) { + gumbo_debug("Toggling is_current_node_foreign to %s.\n", + is_foreign ? "true" : "false"); + } + parser->_tokenizer_state->_is_current_node_foreign = is_foreign; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#data-state +static StateResult handle_data_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '&': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_DATA); + // The char_ref machinery expects to be on the & so it can mark that + // and return to it if the text isn't a char ref, so we need to + // reconsume it. + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_TAG_OPEN); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, '<'); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + emit_char(parser, c, output); + return RETURN_ERROR; + default: + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#character-reference-in-data-state +static StateResult handle_char_ref_in_data_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_char_ref(parser, ' ', false, output); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rcdata-state +static StateResult handle_rcdata_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '&': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_RCDATA); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA_LT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, '<'); + return NEXT_CHAR; + case '\0': + return emit_replacement_char(parser, output); + case -1: + return emit_eof(parser, output); + default: + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#character-reference-in-rcdata-state +static StateResult handle_char_ref_in_rcdata_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA); + return emit_char_ref(parser, ' ', false, output); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-state +static StateResult handle_rawtext_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT_LT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, '<'); + return NEXT_CHAR; + case '\0': + return emit_replacement_char(parser, output); + case -1: + return emit_eof(parser, output); + default: + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-state +static StateResult handle_script_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_LT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, '<'); + return NEXT_CHAR; + case '\0': + return emit_replacement_char(parser, output); + case -1: + return emit_eof(parser, output); + default: + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#plaintext-state +static StateResult handle_plaintext_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\0': + return emit_replacement_char(parser, output); + case -1: + return emit_eof(parser, output); + default: + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#tag-open-state +static StateResult handle_tag_open_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "<")); + switch (c) { + case '!': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_MARKUP_DECLARATION); + clear_temporary_buffer(parser); + return NEXT_CHAR; + case '/': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_END_TAG_OPEN); + append_char_to_temporary_buffer(parser, '/'); + return NEXT_CHAR; + case '?': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_COMMENT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, '?'); + tokenizer_add_parse_error(parser, GUMBO_ERR_TAG_STARTS_WITH_QUESTION); + return NEXT_CHAR; + default: + if (is_alpha(c)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_TAG_NAME); + start_new_tag(parser, true); + return NEXT_CHAR; + } else { + tokenizer_add_parse_error(parser, GUMBO_ERR_TAG_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_temporary_buffer(parser, output); + return RETURN_ERROR; + } + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#end-tag-open-state +static StateResult handle_end_tag_open_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "</")); + switch (c) { + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_CLOSE_TAG_EMPTY); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_CLOSE_TAG_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_temporary_buffer(parser, output); + default: + if (is_alpha(c)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_TAG_NAME); + start_new_tag(parser, false); + } else { + tokenizer_add_parse_error(parser, GUMBO_ERR_CLOSE_TAG_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_COMMENT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, c); + } + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#tag-name-state +static StateResult handle_tag_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + return NEXT_CHAR; + case '/': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '>': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_TAG_EOF); + abandon_current_tag(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return NEXT_CHAR; + default: + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#rcdata-less-than-sign-state +static StateResult handle_rcdata_lt_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "<")); + if (c == '/') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA_END_TAG_OPEN); + append_char_to_temporary_buffer(parser, '/'); + return NEXT_CHAR; + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA); + tokenizer->_reconsume_current_input = true; + return emit_temporary_buffer(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#rcdata-end-tag-open-state +static StateResult handle_rcdata_end_tag_open_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "</")); + if (is_alpha(c)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA_END_TAG_NAME); + start_new_tag(parser, false); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA); + return emit_temporary_buffer(parser, output); + } + return true; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#rcdata-end-tag-name-state +static StateResult handle_rcdata_end_tag_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(tokenizer->_temporary_buffer.length >= 2); + if (is_alpha(c)) { + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else if (is_appropriate_end_tag(parser)) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + return NEXT_CHAR; + case '/': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '>': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + } + } + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA); + abandon_current_tag(parser); + return emit_temporary_buffer(parser, output); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-less-than-sign-state +static StateResult handle_rawtext_lt_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "<")); + if (c == '/') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT_END_TAG_OPEN); + append_char_to_temporary_buffer(parser, '/'); + return NEXT_CHAR; + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT); + tokenizer->_reconsume_current_input = true; + return emit_temporary_buffer(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-end-tag-open-state +static StateResult handle_rawtext_end_tag_open_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "</")); + if (is_alpha(c)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT_END_TAG_NAME); + start_new_tag(parser, false); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT); + return emit_temporary_buffer(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-end-tag-name-state +static StateResult handle_rawtext_end_tag_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(tokenizer->_temporary_buffer.length >= 2); + gumbo_debug("Last end tag: %*s\n", (int) tokenizer->_tag_state._buffer.length, + tokenizer->_tag_state._buffer.data); + if (is_alpha(c)) { + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else if (is_appropriate_end_tag(parser)) { + gumbo_debug("Is an appropriate end tag.\n"); + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + return NEXT_CHAR; + case '/': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '>': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + } + } + gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT); + abandon_current_tag(parser); + return emit_temporary_buffer(parser, output); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-less-than-sign-state +static StateResult handle_script_lt_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "<")); + if (c == '/') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_END_TAG_OPEN); + append_char_to_temporary_buffer(parser, '/'); + return NEXT_CHAR; + } else if (c == '!') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_START); + append_char_to_temporary_buffer(parser, '!'); + return emit_temporary_buffer(parser, output); + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + tokenizer->_reconsume_current_input = true; + return emit_temporary_buffer(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-end-tag-open-state +static StateResult handle_script_end_tag_open_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "</")); + if (is_alpha(c)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_END_TAG_NAME); + start_new_tag(parser, false); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + return emit_temporary_buffer(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-end-tag-name-state +static StateResult handle_script_end_tag_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(tokenizer->_temporary_buffer.length >= 2); + if (is_alpha(c)) { + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else if (is_appropriate_end_tag(parser)) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + return NEXT_CHAR; + case '/': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '>': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + } + } + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + abandon_current_tag(parser); + return emit_temporary_buffer(parser, output); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escape-start-state +static StateResult handle_script_escaped_start_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + if (c == '-') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_START_DASH); + return emit_current_char(parser, output); + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escape-start-dash-state +static StateResult handle_script_escaped_start_dash_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + if (c == '-') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_DASH_DASH); + return emit_current_char(parser, output); + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-state +static StateResult handle_script_escaped_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_DASH); + return emit_current_char(parser, output); + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_LT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + case '\0': + return emit_replacement_char(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF); + return emit_eof(parser, output); + default: + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-dash-state +static StateResult handle_script_escaped_dash_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_DASH_DASH); + return emit_current_char(parser, output); + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_LT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + case '\0': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + return emit_replacement_char(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return NEXT_CHAR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-dash-dash-state +static StateResult handle_script_escaped_dash_dash_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + return emit_current_char(parser, output); + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_LT); + clear_temporary_buffer(parser); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + return emit_current_char(parser, output); + case '\0': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + return emit_replacement_char(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return NEXT_CHAR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-less-than-sign-state +static StateResult handle_script_escaped_lt_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "<")); + assert(!tokenizer->_script_data_buffer.length); + if (c == '/') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_OPEN); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else if (is_alpha(c)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_START); + append_char_to_temporary_buffer(parser, c); + gumbo_string_buffer_append_codepoint( + parser, ensure_lowercase(c), &tokenizer->_script_data_buffer); + return emit_temporary_buffer(parser, output); + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + return emit_temporary_buffer(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-end-tag-open-state +static StateResult handle_script_escaped_end_tag_open_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(temporary_buffer_equals(parser, "</")); + if (is_alpha(c)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_NAME); + start_new_tag(parser, false); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + return emit_temporary_buffer(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-end-tag-name-state +static StateResult handle_script_escaped_end_tag_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(tokenizer->_temporary_buffer.length >= 2); + if (is_alpha(c)) { + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } else if (is_appropriate_end_tag(parser)) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + return NEXT_CHAR; + case '/': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '>': + finish_tag_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + } + } + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + abandon_current_tag(parser); + return emit_temporary_buffer(parser, output); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escape-start-state +static StateResult handle_script_double_escaped_start_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + case '/': + case '>': + gumbo_tokenizer_set_state( + parser, gumbo_string_equals(&kScriptTag, + (GumboStringPiece*) &tokenizer->_script_data_buffer) + ? GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED + : GUMBO_LEX_SCRIPT_ESCAPED); + return emit_current_char(parser, output); + default: + if (is_alpha(c)) { + gumbo_string_buffer_append_codepoint( + parser, ensure_lowercase(c), &tokenizer->_script_data_buffer); + return emit_current_char(parser, output); + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + } + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-state +static StateResult handle_script_double_escaped_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH); + return emit_current_char(parser, output); + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT); + return emit_current_char(parser, output); + case '\0': + return emit_replacement_char(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return NEXT_CHAR; + default: + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-dash-state +static StateResult handle_script_double_escaped_dash_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH_DASH); + return emit_current_char(parser, output); + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT); + return emit_current_char(parser, output); + case '\0': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED); + return emit_replacement_char(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return NEXT_CHAR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED); + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-dash-dash-state +static StateResult handle_script_double_escaped_dash_dash_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '-': + return emit_current_char(parser, output); + case '<': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT); + return emit_current_char(parser, output); + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT); + return emit_current_char(parser, output); + case '\0': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED); + return emit_replacement_char(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return NEXT_CHAR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED); + return emit_current_char(parser, output); + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-less-than-sign-state +static StateResult handle_script_double_escaped_lt_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + if (c == '/') { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_END); + gumbo_string_buffer_clear(parser, &tokenizer->_script_data_buffer); + return emit_current_char(parser, output); + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escape-end-state +static StateResult handle_script_double_escaped_end_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + case '/': + case '>': + gumbo_tokenizer_set_state( + parser, gumbo_string_equals(&kScriptTag, + (GumboStringPiece*) &tokenizer->_script_data_buffer) + ? GUMBO_LEX_SCRIPT_ESCAPED + : GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED); + return emit_current_char(parser, output); + default: + if (is_alpha(c)) { + gumbo_string_buffer_append_codepoint( + parser, ensure_lowercase(c), &tokenizer->_script_data_buffer); + return emit_current_char(parser, output); + } else { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + } + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#before-attribute-name-state +static StateResult handle_before_attr_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '/': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME); + append_char_to_temporary_buffer(parser, 0xfffd); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + return NEXT_CHAR; + case '"': + case '\'': + case '<': + case '=': + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_INVALID); + // Fall through. + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME); + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-name-state +static StateResult handle_attr_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + finish_attribute_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_ATTR_NAME); + return NEXT_CHAR; + case '/': + finish_attribute_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '=': + finish_attribute_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_VALUE); + return NEXT_CHAR; + case '>': + finish_attribute_name(parser); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true); + return NEXT_CHAR; + case -1: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_EOF); + return NEXT_CHAR; + case '"': + case '\'': + case '<': + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_INVALID); + // Fall through. + default: + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#after-attribute-name-state +static StateResult handle_after_attr_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '/': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '=': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_VALUE); + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME); + append_char_to_temporary_buffer(parser, 0xfffd); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + return NEXT_CHAR; + case '"': + case '\'': + case '<': + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_INVALID); + // Fall through. + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME); + append_char_to_tag_buffer(parser, ensure_lowercase(c), true); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#before-attribute-value-state +static StateResult handle_before_attr_value_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '"': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_DOUBLE_QUOTED); + reset_tag_buffer_start_point(parser); + return NEXT_CHAR; + case '&': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_UNQUOTED); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + case '\'': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_SINGLE_QUOTED); + reset_tag_buffer_start_point(parser); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_UNQUOTED); + append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_RIGHT_BRACKET); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_current_tag(parser, output); + return RETURN_ERROR; + case '<': + case '=': + case '`': + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EQUALS); + // Fall through. + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_UNQUOTED); + append_char_to_tag_buffer(parser, c, true); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-value-double-quoted-state +static StateResult handle_attr_value_double_quoted_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '"': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_ATTR_VALUE_QUOTED); + return NEXT_CHAR; + case '&': + tokenizer->_tag_state._attr_value_state = tokenizer->_state; + gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_tag_buffer(parser, kUtf8ReplacementChar, false); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_DOUBLE_QUOTE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + default: + append_char_to_tag_buffer(parser, c, false); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-value-single-quoted-state +static StateResult handle_attr_value_single_quoted_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\'': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_ATTR_VALUE_QUOTED); + return NEXT_CHAR; + case '&': + tokenizer->_tag_state._attr_value_state = tokenizer->_state; + gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_tag_buffer(parser, kUtf8ReplacementChar, false); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_SINGLE_QUOTE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + default: + append_char_to_tag_buffer(parser, c, false); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-value-unquoted-state +static StateResult handle_attr_value_unquoted_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + finish_attribute_value(parser); + return NEXT_CHAR; + case '&': + tokenizer->_tag_state._attr_value_state = tokenizer->_state; + gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + finish_attribute_value(parser); + return emit_current_tag(parser, output); + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_reconsume_current_input = true; + abandon_current_tag(parser); + return NEXT_CHAR; + case '<': + case '=': + case '"': + case '\'': + case '`': + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EQUALS); + // Fall through. + default: + append_char_to_tag_buffer(parser, c, true); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#character-reference-in-attribute-value-state +static StateResult handle_char_ref_in_attr_value_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + OneOrTwoCodepoints char_ref; + int allowed_char; + bool is_unquoted = false; + switch (tokenizer->_tag_state._attr_value_state) { + case GUMBO_LEX_ATTR_VALUE_DOUBLE_QUOTED: + allowed_char = '"'; + break; + case GUMBO_LEX_ATTR_VALUE_SINGLE_QUOTED: + allowed_char = '\''; + break; + case GUMBO_LEX_ATTR_VALUE_UNQUOTED: + allowed_char = '>'; + is_unquoted = true; + break; + default: + // -Wmaybe-uninitialized is a little overzealous here, and doesn't + // get that the assert(0) means this codepath will never happen. + allowed_char = ' '; + assert(0); + } + + // Ignore the status, since we don't have a convenient way of signalling that + // a parser error has occurred when the error occurs in the middle of a + // multi-state token. We'd need a flag inside the TokenizerState to do this, + // but that's a low priority fix. + consume_char_ref(parser, &tokenizer->_input, allowed_char, true, &char_ref); + if (char_ref.first != kGumboNoChar) { + tokenizer->_reconsume_current_input = true; + append_char_to_tag_buffer(parser, char_ref.first, is_unquoted); + if (char_ref.second != kGumboNoChar) { + append_char_to_tag_buffer(parser, char_ref.second, is_unquoted); + } + } else { + append_char_to_tag_buffer(parser, '&', is_unquoted); + } + gumbo_tokenizer_set_state(parser, tokenizer->_tag_state._attr_value_state); + return NEXT_CHAR; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#after-attribute-value-quoted-state +static StateResult handle_after_attr_value_quoted_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + finish_attribute_value(parser); + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + return NEXT_CHAR; + case '/': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG); + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_current_tag(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_AFTER_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_AFTER_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#self-closing-start-tag-state +static StateResult handle_self_closing_start_tag_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_tag_state._is_self_closing = true; + return emit_current_tag(parser, output); + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_SOLIDUS_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + abandon_current_tag(parser); + return NEXT_CHAR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_SOLIDUS_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME); + tokenizer->_reconsume_current_input = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#bogus-comment-state +static StateResult handle_bogus_comment_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + while (c != '>' && c != -1) { + if (c == '\0') { + c = 0xFFFD; + } + append_char_to_temporary_buffer(parser, c); + utf8iterator_next(&tokenizer->_input); + c = utf8iterator_current(&tokenizer->_input); + } + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_comment(parser, output); +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#markup-declaration-open-state +static StateResult handle_markup_declaration_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + if (utf8iterator_maybe_consume_match( + &tokenizer->_input, "--", sizeof("--") - 1, true)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_START); + tokenizer->_reconsume_current_input = true; + } else if (utf8iterator_maybe_consume_match( + &tokenizer->_input, "DOCTYPE", sizeof("DOCTYPE") - 1, false)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE); + tokenizer->_reconsume_current_input = true; + // If we get here, we know we'll eventually emit a doctype token, so now is + // the time to initialize the doctype strings. (Not in doctype_state_init, + // since then they'll leak if ownership never gets transferred to the + // doctype token. + tokenizer->_doc_type_state.name = gumbo_copy_stringz(parser, ""); + tokenizer->_doc_type_state.public_identifier = + gumbo_copy_stringz(parser, ""); + tokenizer->_doc_type_state.system_identifier = + gumbo_copy_stringz(parser, ""); + } else if (tokenizer->_is_current_node_foreign && + utf8iterator_maybe_consume_match( + &tokenizer->_input, "[CDATA[", sizeof("[CDATA[") - 1, true)) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_CDATA); + tokenizer->_is_in_cdata = true; + tokenizer->_reconsume_current_input = true; + } else { + tokenizer_add_parse_error(parser, GUMBO_ERR_DASHES_OR_DOCTYPE); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_COMMENT); + tokenizer->_reconsume_current_input = true; + clear_temporary_buffer(parser); + } + return NEXT_CHAR; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-start-state +static StateResult handle_comment_start_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_START_DASH); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-start-dash-state +static StateResult handle_comment_start_dash_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-state +static StateResult handle_comment_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END_DASH); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + default: + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-end-dash-state +static StateResult handle_comment_end_dash_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-end-state +static StateResult handle_comment_end_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_comment(parser, output); + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '!': + tokenizer_add_parse_error( + parser, GUMBO_ERR_COMMENT_BANG_AFTER_DOUBLE_DASH); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END_BANG); + return NEXT_CHAR; + case '-': + tokenizer_add_parse_error( + parser, GUMBO_ERR_COMMENT_DASH_AFTER_DOUBLE_DASH); + append_char_to_temporary_buffer(parser, '-'); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-end-bang-state +static StateResult handle_comment_end_bang_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '-': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END_DASH); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '!'); + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + return emit_comment(parser, output); + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '!'); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_END_BANG_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_comment(parser, output); + return RETURN_ERROR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '-'); + append_char_to_temporary_buffer(parser, '!'); + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#doctype-state +static StateResult handle_doctype_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + assert(!tokenizer->_temporary_buffer.length); + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_NAME); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_SPACE); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_NAME); + tokenizer->_reconsume_current_input = true; + tokenizer->_doc_type_state.force_quirks = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#before-doctype-name-state +static StateResult handle_before_doctype_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE_NAME); + tokenizer->_doc_type_state.force_quirks = true; + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_RIGHT_BRACKET); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE_NAME); + tokenizer->_doc_type_state.force_quirks = false; + append_char_to_temporary_buffer(parser, ensure_lowercase(c)); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#doctype-name-state +static StateResult handle_doctype_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_NAME); + gumbo_parser_deallocate(parser, (void*) tokenizer->_doc_type_state.name); + finish_temporary_buffer(parser, &tokenizer->_doc_type_state.name); + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + gumbo_parser_deallocate(parser, (void*) tokenizer->_doc_type_state.name); + finish_temporary_buffer(parser, &tokenizer->_doc_type_state.name); + emit_doctype(parser, output); + return RETURN_SUCCESS; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + gumbo_parser_deallocate(parser, (void*) tokenizer->_doc_type_state.name); + finish_temporary_buffer(parser, &tokenizer->_doc_type_state.name); + emit_doctype(parser, output); + return RETURN_ERROR; + default: + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE_NAME); + tokenizer->_doc_type_state.force_quirks = false; + append_char_to_temporary_buffer(parser, ensure_lowercase(c)); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-name-state +static StateResult handle_after_doctype_name_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_doctype(parser, output); + return RETURN_SUCCESS; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + if (utf8iterator_maybe_consume_match( + &tokenizer->_input, "PUBLIC", sizeof("PUBLIC") - 1, false)) { + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_KEYWORD); + tokenizer->_reconsume_current_input = true; + } else if (utf8iterator_maybe_consume_match(&tokenizer->_input, "SYSTEM", + sizeof("SYSTEM") - 1, false)) { + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_KEYWORD); + tokenizer->_reconsume_current_input = true; + } else { + tokenizer_add_parse_error( + parser, GUMBO_ERR_DOCTYPE_SPACE_OR_RIGHT_BRACKET); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + tokenizer->_doc_type_state.force_quirks = true; + } + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-public-keyword-state +static StateResult handle_after_doctype_public_keyword_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_PUBLIC_ID); + return NEXT_CHAR; + case '"': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_DOUBLE_QUOTED); + return NEXT_CHAR; + case '\'': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_SINGLE_QUOTED); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_RIGHT_BRACKET); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#before-doctype-public-identifier-state +static StateResult handle_before_doctype_public_id_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '"': + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_DOUBLE_QUOTED); + return NEXT_CHAR; + case '\'': + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_SINGLE_QUOTED); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-public-identifier-(double-quoted)-state +static StateResult handle_doctype_public_id_double_quoted_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '"': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_ID); + finish_doctype_public_id(parser); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_public_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_public_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + default: + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-public-identifier-(single-quoted)-state +static StateResult handle_doctype_public_id_single_quoted_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '\'': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_ID); + finish_doctype_public_id(parser); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_public_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_public_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + default: + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-public-identifier-state +static StateResult handle_after_doctype_public_id_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_BETWEEN_DOCTYPE_PUBLIC_SYSTEM_ID); + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_doctype(parser, output); + return RETURN_SUCCESS; + case '"': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED); + return NEXT_CHAR; + case '\'': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_reconsume_current_input = true; + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + tokenizer->_doc_type_state.force_quirks = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#between-doctype-public-and-system-identifiers-state +static StateResult handle_between_doctype_public_system_id_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_doctype(parser, output); + return RETURN_SUCCESS; + case '"': + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED); + return NEXT_CHAR; + case '\'': + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED); + return NEXT_CHAR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-system-keyword-state +static StateResult handle_after_doctype_system_keyword_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_SYSTEM_ID); + return NEXT_CHAR; + case '"': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED); + return NEXT_CHAR; + case '\'': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + tokenizer->_doc_type_state.force_quirks = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#before-doctype-system-identifier-state +static StateResult handle_before_doctype_system_id_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '"': + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED); + return NEXT_CHAR; + case '\'': + assert(temporary_buffer_equals(parser, "")); + gumbo_tokenizer_set_state( + parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + tokenizer->_doc_type_state.force_quirks = true; + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-system-identifier-(double-quoted)-state +static StateResult handle_doctype_system_id_double_quoted_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '"': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_ID); + finish_doctype_system_id(parser); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_system_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_system_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + default: + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-system-identifier-(single-quoted)-state +static StateResult handle_doctype_system_id_single_quoted_state( + GumboParser* parser, GumboTokenizerState* tokenizer, int c, + GumboToken* output) { + switch (c) { + case '\'': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_ID); + finish_doctype_system_id(parser); + return NEXT_CHAR; + case '\0': + tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL); + append_char_to_temporary_buffer(parser, kUtf8ReplacementChar); + return NEXT_CHAR; + case '>': + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_system_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + finish_doctype_system_id(parser); + emit_doctype(parser, output); + return RETURN_ERROR; + default: + append_char_to_temporary_buffer(parser, c); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-system-identifier-state +static StateResult handle_after_doctype_system_id_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + switch (c) { + case '\t': + case '\n': + case '\f': + case ' ': + return NEXT_CHAR; + case '>': + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_doctype(parser, output); + return RETURN_SUCCESS; + case -1: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_doc_type_state.force_quirks = true; + emit_doctype(parser, output); + return RETURN_ERROR; + default: + tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE); + return NEXT_CHAR; + } +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#bogus-doctype-state +static StateResult handle_bogus_doctype_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + if (c == '>' || c == -1) { + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + emit_doctype(parser, output); + return RETURN_ERROR; + } + return NEXT_CHAR; +} + +// http://www.whatwg.org/specs/web-apps/current-work/complete.html#cdata-section-state +static StateResult handle_cdata_state(GumboParser* parser, + GumboTokenizerState* tokenizer, int c, GumboToken* output) { + if (c == -1 || utf8iterator_maybe_consume_match( + &tokenizer->_input, "]]>", sizeof("]]>") - 1, true)) { + tokenizer->_reconsume_current_input = true; + reset_token_start_point(tokenizer); + gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA); + tokenizer->_is_in_cdata = false; + return NEXT_CHAR; + } else { + return emit_current_char(parser, output); + } +} + +typedef StateResult (*GumboLexerStateFunction)( + GumboParser*, GumboTokenizerState*, int, GumboToken*); + +static GumboLexerStateFunction dispatch_table[] = {handle_data_state, + handle_char_ref_in_data_state, handle_rcdata_state, + handle_char_ref_in_rcdata_state, handle_rawtext_state, handle_script_state, + handle_plaintext_state, handle_tag_open_state, handle_end_tag_open_state, + handle_tag_name_state, handle_rcdata_lt_state, + handle_rcdata_end_tag_open_state, handle_rcdata_end_tag_name_state, + handle_rawtext_lt_state, handle_rawtext_end_tag_open_state, + handle_rawtext_end_tag_name_state, handle_script_lt_state, + handle_script_end_tag_open_state, handle_script_end_tag_name_state, + handle_script_escaped_start_state, handle_script_escaped_start_dash_state, + handle_script_escaped_state, handle_script_escaped_dash_state, + handle_script_escaped_dash_dash_state, handle_script_escaped_lt_state, + handle_script_escaped_end_tag_open_state, + handle_script_escaped_end_tag_name_state, + handle_script_double_escaped_start_state, + handle_script_double_escaped_state, handle_script_double_escaped_dash_state, + handle_script_double_escaped_dash_dash_state, + handle_script_double_escaped_lt_state, + handle_script_double_escaped_end_state, handle_before_attr_name_state, + handle_attr_name_state, handle_after_attr_name_state, + handle_before_attr_value_state, handle_attr_value_double_quoted_state, + handle_attr_value_single_quoted_state, handle_attr_value_unquoted_state, + handle_char_ref_in_attr_value_state, handle_after_attr_value_quoted_state, + handle_self_closing_start_tag_state, handle_bogus_comment_state, + handle_markup_declaration_state, handle_comment_start_state, + handle_comment_start_dash_state, handle_comment_state, + handle_comment_end_dash_state, handle_comment_end_state, + handle_comment_end_bang_state, handle_doctype_state, + handle_before_doctype_name_state, handle_doctype_name_state, + handle_after_doctype_name_state, handle_after_doctype_public_keyword_state, + handle_before_doctype_public_id_state, + handle_doctype_public_id_double_quoted_state, + handle_doctype_public_id_single_quoted_state, + handle_after_doctype_public_id_state, + handle_between_doctype_public_system_id_state, + handle_after_doctype_system_keyword_state, + handle_before_doctype_system_id_state, + handle_doctype_system_id_double_quoted_state, + handle_doctype_system_id_single_quoted_state, + handle_after_doctype_system_id_state, handle_bogus_doctype_state, + handle_cdata_state}; + +bool gumbo_lex(GumboParser* parser, GumboToken* output) { + // Because of the spec requirements that... + // + // 1. Tokens be handled immediately by the parser upon emission. + // 2. Some states (eg. CDATA, or various error conditions) require the + // emission of multiple tokens in the same states. + // 3. The tokenizer often has to reconsume the same character in a different + // state. + // + // ...all state must be held in the GumboTokenizer struct instead of in local + // variables in this function. That allows us to return from this method with + // a token, and then immediately jump back to the same state with the same + // input if we need to return a different token. The various emit_* functions + // are responsible for changing state (eg. flushing the chardata buffer, + // reading the next input character) to avoid an infinite loop. + GumboTokenizerState* tokenizer = parser->_tokenizer_state; + + if (tokenizer->_buffered_emit_char != kGumboNoChar) { + tokenizer->_reconsume_current_input = true; + emit_char(parser, tokenizer->_buffered_emit_char, output); + // And now that we've avoided advancing the input, make sure we set + // _reconsume_current_input back to false to make sure the *next* character + // isn't consumed twice. + tokenizer->_reconsume_current_input = false; + tokenizer->_buffered_emit_char = kGumboNoChar; + return true; + } + + if (maybe_emit_from_temporary_buffer(parser, output)) { + return true; + } + + while (1) { + assert(!tokenizer->_temporary_buffer_emit); + assert(tokenizer->_buffered_emit_char == kGumboNoChar); + int c = utf8iterator_current(&tokenizer->_input); + gumbo_debug( + "Lexing character '%c' (%d) in state %d.\n", c, c, tokenizer->_state); + StateResult result = + dispatch_table[tokenizer->_state](parser, tokenizer, c, output); + // We need to clear reconsume_current_input before returning to prevent + // certain infinite loop states. + bool should_advance = !tokenizer->_reconsume_current_input; + tokenizer->_reconsume_current_input = false; + + if (result == RETURN_SUCCESS) { + return true; + } else if (result == RETURN_ERROR) { + return false; + } + + if (should_advance) { + utf8iterator_next(&tokenizer->_input); + } + } +} + +void gumbo_token_destroy(GumboParser* parser, GumboToken* token) { + if (!token) return; + + switch (token->type) { + case GUMBO_TOKEN_DOCTYPE: + gumbo_parser_deallocate(parser, (void*) token->v.doc_type.name); + gumbo_parser_deallocate( + parser, (void*) token->v.doc_type.public_identifier); + gumbo_parser_deallocate( + parser, (void*) token->v.doc_type.system_identifier); + return; + case GUMBO_TOKEN_START_TAG: + for (unsigned int i = 0; i < token->v.start_tag.attributes.length; ++i) { + GumboAttribute* attr = token->v.start_tag.attributes.data[i]; + if (attr) { + // May have been nulled out if this token was merged with another. + gumbo_destroy_attribute(parser, attr); + } + } + gumbo_parser_deallocate( + parser, (void*) token->v.start_tag.attributes.data); + return; + case GUMBO_TOKEN_COMMENT: + gumbo_parser_deallocate(parser, (void*) token->v.text); + return; + default: + return; + } +} diff --git a/gb.form.htmlview/src/gumbo/tokenizer.h b/gb.form.htmlview/src/gumbo/tokenizer.h new file mode 100644 index 00000000..1e2a2ca7 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tokenizer.h @@ -0,0 +1,123 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// This contains an implementation of a tokenizer for HTML5. It consumes a +// buffer of UTF-8 characters, and then emits a stream of tokens. + +#ifndef GUMBO_TOKENIZER_H_ +#define GUMBO_TOKENIZER_H_ + +#include <stdbool.h> +#include <stddef.h> + +#include "gumbo.h" +#include "token_type.h" +#include "tokenizer_states.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalParser; + +// Struct containing all information pertaining to doctype tokens. +typedef struct GumboInternalTokenDocType { + const char* name; + const char* public_identifier; + const char* system_identifier; + bool force_quirks; + // There's no way to tell a 0-length public or system ID apart from the + // absence of a public or system ID, but they're handled different by the + // spec, so we need bool flags for them. + bool has_public_identifier; + bool has_system_identifier; +} GumboTokenDocType; + +// Struct containing all information pertaining to start tag tokens. +typedef struct GumboInternalTokenStartTag { + GumboTag tag; + GumboVector /* GumboAttribute */ attributes; + bool is_self_closing; +} GumboTokenStartTag; + +// A data structure representing a single token in the input stream. This +// contains an enum for the type, the source position, a GumboStringPiece +// pointing to the original text, and then a union for any parsed data. +typedef struct GumboInternalToken { + GumboTokenType type; + GumboSourcePosition position; + GumboStringPiece original_text; + union { + GumboTokenDocType doc_type; + GumboTokenStartTag start_tag; + GumboTag end_tag; + const char* text; // For comments. + int character; // For character, whitespace, null, and EOF tokens. + } v; +} GumboToken; + +// Initializes the tokenizer state within the GumboParser object, setting up a +// parse of the specified text. +void gumbo_tokenizer_state_init( + struct GumboInternalParser* parser, const char* text, size_t text_length); + +// Destroys the tokenizer state within the GumboParser object, freeing any +// dynamically-allocated structures within it. +void gumbo_tokenizer_state_destroy(struct GumboInternalParser* parser); + +// Sets the tokenizer state to the specified value. This is needed by some +// parser states, which alter the state of the tokenizer in response to tags +// seen. +void gumbo_tokenizer_set_state( + struct GumboInternalParser* parser, GumboTokenizerEnum state); + +// Flags whether the current node is a foreign content element. This is +// necessary for the markup declaration open state, where the tokenizer must be +// aware of the state of the parser to properly tokenize bad comment tags. +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#markup-declaration-open-state +void gumbo_tokenizer_set_is_current_node_foreign( + struct GumboInternalParser* parser, bool is_foreign); + +// Lexes a single token from the specified buffer, filling the output with the +// parsed GumboToken data structure. Returns true for a successful +// tokenization, false if a parse error occurs. +// +// Example: +// struct GumboInternalParser parser; +// GumboToken output; +// gumbo_tokenizer_state_init(&parser, text, strlen(text)); +// while (gumbo_lex(&parser, &output)) { +// ...do stuff with output. +// gumbo_token_destroy(&parser, &token); +// } +// gumbo_tokenizer_state_destroy(&parser); +bool gumbo_lex(struct GumboInternalParser* parser, GumboToken* output); + +// Frees the internally-allocated pointers within an GumboToken. Note that this +// doesn't free the token itself, since oftentimes it will be allocated on the +// stack. A simple call to free() (or GumboParser->deallocator, if +// appropriate) can handle that. +// +// Note that if you are handing over ownership of the internal strings to some +// other data structure - for example, a parse tree - these do not need to be +// freed. +void gumbo_token_destroy(struct GumboInternalParser* parser, GumboToken* token); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_TOKENIZER_H_ diff --git a/gb.form.htmlview/src/gumbo/tokenizer_states.h b/gb.form.htmlview/src/gumbo/tokenizer_states.h new file mode 100644 index 00000000..80659f5f --- /dev/null +++ b/gb.form.htmlview/src/gumbo/tokenizer_states.h @@ -0,0 +1,103 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// This contains the list of states used in the tokenizer. Although at first +// glance it seems like these could be kept internal to the tokenizer, several +// of the actions in the parser require that it reach into the tokenizer and +// reset the tokenizer state. For that to work, it needs to have the +// definitions of individual states available. +// +// This may also be useful for providing more detailed error messages for parse +// errors, as we can match up states and inputs in a table without having to +// clutter the tokenizer code with lots of precise error messages. + +#ifndef GUMBO_TOKENIZER_STATES_H_ +#define GUMBO_TOKENIZER_STATES_H_ + +// The ordering of this enum is also used to build the dispatch table for the +// tokenizer state machine, so if it is changed, be sure to update that too. +typedef enum { + GUMBO_LEX_DATA, + GUMBO_LEX_CHAR_REF_IN_DATA, + GUMBO_LEX_RCDATA, + GUMBO_LEX_CHAR_REF_IN_RCDATA, + GUMBO_LEX_RAWTEXT, + GUMBO_LEX_SCRIPT, + GUMBO_LEX_PLAINTEXT, + GUMBO_LEX_TAG_OPEN, + GUMBO_LEX_END_TAG_OPEN, + GUMBO_LEX_TAG_NAME, + GUMBO_LEX_RCDATA_LT, + GUMBO_LEX_RCDATA_END_TAG_OPEN, + GUMBO_LEX_RCDATA_END_TAG_NAME, + GUMBO_LEX_RAWTEXT_LT, + GUMBO_LEX_RAWTEXT_END_TAG_OPEN, + GUMBO_LEX_RAWTEXT_END_TAG_NAME, + GUMBO_LEX_SCRIPT_LT, + GUMBO_LEX_SCRIPT_END_TAG_OPEN, + GUMBO_LEX_SCRIPT_END_TAG_NAME, + GUMBO_LEX_SCRIPT_ESCAPED_START, + GUMBO_LEX_SCRIPT_ESCAPED_START_DASH, + GUMBO_LEX_SCRIPT_ESCAPED, + GUMBO_LEX_SCRIPT_ESCAPED_DASH, + GUMBO_LEX_SCRIPT_ESCAPED_DASH_DASH, + GUMBO_LEX_SCRIPT_ESCAPED_LT, + GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_OPEN, + GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_NAME, + GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_START, + GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED, + GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH, + GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH_DASH, + GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT, + GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_END, + GUMBO_LEX_BEFORE_ATTR_NAME, + GUMBO_LEX_ATTR_NAME, + GUMBO_LEX_AFTER_ATTR_NAME, + GUMBO_LEX_BEFORE_ATTR_VALUE, + GUMBO_LEX_ATTR_VALUE_DOUBLE_QUOTED, + GUMBO_LEX_ATTR_VALUE_SINGLE_QUOTED, + GUMBO_LEX_ATTR_VALUE_UNQUOTED, + GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE, + GUMBO_LEX_AFTER_ATTR_VALUE_QUOTED, + GUMBO_LEX_SELF_CLOSING_START_TAG, + GUMBO_LEX_BOGUS_COMMENT, + GUMBO_LEX_MARKUP_DECLARATION, + GUMBO_LEX_COMMENT_START, + GUMBO_LEX_COMMENT_START_DASH, + GUMBO_LEX_COMMENT, + GUMBO_LEX_COMMENT_END_DASH, + GUMBO_LEX_COMMENT_END, + GUMBO_LEX_COMMENT_END_BANG, + GUMBO_LEX_DOCTYPE, + GUMBO_LEX_BEFORE_DOCTYPE_NAME, + GUMBO_LEX_DOCTYPE_NAME, + GUMBO_LEX_AFTER_DOCTYPE_NAME, + GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_KEYWORD, + GUMBO_LEX_BEFORE_DOCTYPE_PUBLIC_ID, + GUMBO_LEX_DOCTYPE_PUBLIC_ID_DOUBLE_QUOTED, + GUMBO_LEX_DOCTYPE_PUBLIC_ID_SINGLE_QUOTED, + GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_ID, + GUMBO_LEX_BETWEEN_DOCTYPE_PUBLIC_SYSTEM_ID, + GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_KEYWORD, + GUMBO_LEX_BEFORE_DOCTYPE_SYSTEM_ID, + GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED, + GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED, + GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_ID, + GUMBO_LEX_BOGUS_DOCTYPE, + GUMBO_LEX_CDATA +} GumboTokenizerEnum; + +#endif // GUMBO_TOKENIZER_STATES_H_ diff --git a/gb.form.htmlview/src/gumbo/utf8.c b/gb.form.htmlview/src/gumbo/utf8.c new file mode 100644 index 00000000..ad73cefa --- /dev/null +++ b/gb.form.htmlview/src/gumbo/utf8.c @@ -0,0 +1,270 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "utf8.h" + +#include <assert.h> +#include <stdint.h> +#include <string.h> +#include <strings.h> // For strncasecmp. + +#include "error.h" +#include "gumbo.h" +#include "parser.h" +#include "util.h" +#include "vector.h" + +const int kUtf8ReplacementChar = 0xFFFD; + +// Reference material: +// Wikipedia: http://en.wikipedia.org/wiki/UTF-8#Description +// RFC 3629: http://tools.ietf.org/html/rfc3629 +// HTML5 Unicode handling: +// http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#preprocessing-the-input-stream +// +// This implementation is based on a DFA-based decoder by Bjoern Hoehrmann +// <bjoern@hoehrmann.de>. We wrap the inner table-based decoder routine in our +// own handling for newlines, tabs, invalid continuation bytes, and other +// conditions that the HTML5 spec fully specifies but normal UTF8 decoders do +// not handle. +// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. Full text of +// the license agreement and code follows. + +// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do +// so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +#define UTF8_ACCEPT 0 +#define UTF8_REJECT 12 + +static const uint8_t utf8d[] = { + // The first part of the table maps bytes to character classes that + // to reduce the size of the transition table and create bitmasks. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, + + // The second part is a transition table that maps a combination + // of a state of the automaton and a character class to a state. + 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, + 12, 12, 12, 12, 24, 12, 24, 12, 12, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, + 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, + 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +}; + +uint32_t static inline decode(uint32_t* state, uint32_t* codep, uint32_t byte) { + uint32_t type = utf8d[byte]; + + *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) + : (0xff >> type) & (byte); + + *state = utf8d[256 + *state + type]; + return *state; +} + +// END COPIED CODE. + +// Adds a decoding error to the parser's error list, based on the current state +// of the Utf8Iterator. +static void add_error(Utf8Iterator* iter, GumboErrorType type) { + GumboParser* parser = iter->_parser; + + GumboError* error = gumbo_add_error(parser); + if (!error) { + return; + } + error->type = type; + error->position = iter->_pos; + error->original_text = iter->_start; + + // At the point the error is recorded, the code point hasn't been computed + // yet (and can't be, because it's invalid), so we need to build up the raw + // hex value from the bytes under the cursor. + uint64_t code_point = 0; + for (int i = 0; i < iter->_width; ++i) { + code_point = (code_point << 8) | (unsigned char) iter->_start[i]; + } + error->v.codepoint = code_point; +} + +// Reads the next UTF-8 character in the iter. +// This assumes that iter->_start points to the beginning of the character. +// When this method returns, iter->_width and iter->_current will be set +// appropriately, as well as any error flags. +static void read_char(Utf8Iterator* iter) { + if (iter->_start >= iter->_end) { + // No input left to consume; emit an EOF and set width = 0. + iter->_current = -1; + iter->_width = 0; + return; + } + + uint32_t code_point = 0; + uint32_t state = UTF8_ACCEPT; + for (const char* c = iter->_start; c < iter->_end; ++c) { + decode(&state, &code_point, (uint32_t)(unsigned char) (*c)); + if (state == UTF8_ACCEPT) { + iter->_width = (int)(c - iter->_start + 1); + // This is the special handling for carriage returns that is mandated by + // the HTML5 spec. Since we're looking for particular 7-bit literal + // characters, we operate in terms of chars and only need a check for iter + // overrun, instead of having to read in a full next code point. + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#preprocessing-the-input-stream + if (code_point == '\r') { + assert(iter->_width == 1); + const char* next = c + 1; + if (next < iter->_end && *next == '\n') { + // Advance the iter, as if the carriage return didn't exist. + ++iter->_start; + // Preserve the true offset, since other tools that look at it may be + // unaware of HTML5's rules for converting \r into \n. + ++iter->_pos.offset; + } + code_point = '\n'; + } + if (utf8_is_invalid_code_point(code_point)) { + add_error(iter, GUMBO_ERR_UTF8_INVALID); + code_point = kUtf8ReplacementChar; + } + iter->_current = code_point; + return; + } else if (state == UTF8_REJECT) { + // We don't want to consume the invalid continuation byte of a multi-byte + // run, but we do want to skip past an invalid first byte. + iter->_width = c - iter->_start + (c == iter->_start); + iter->_current = kUtf8ReplacementChar; + add_error(iter, GUMBO_ERR_UTF8_INVALID); + return; + } + } + // If we got here without exiting early, then we've reached the end of the + // iterator. Add an error for truncated input, set the width to consume the + // rest of the iterator, and emit a replacement character. The next time we + // enter this method, it will detect that there's no input to consume and + // output an EOF. + iter->_current = kUtf8ReplacementChar; + iter->_width = iter->_end - iter->_start; + add_error(iter, GUMBO_ERR_UTF8_TRUNCATED); +} + +static void update_position(Utf8Iterator* iter) { + iter->_pos.offset += (int)iter->_width; + if (iter->_current == '\n') { + ++iter->_pos.line; + iter->_pos.column = 1; + } else if (iter->_current == '\t') { + int tab_stop = iter->_parser->_options->tab_stop; + iter->_pos.column = ((iter->_pos.column / tab_stop) + 1) * tab_stop; + } else if (iter->_current != -1) { + ++iter->_pos.column; + } +} + +// Returns true if this Unicode code point is in the list of characters +// forbidden by the HTML5 spec, such as undefined control chars. +bool utf8_is_invalid_code_point(int c) { + return (c >= 0x1 && c <= 0x8) || c == 0xB || (c >= 0xE && c <= 0x1F) || + (c >= 0x7F && c <= 0x9F) || (c >= 0xFDD0 && c <= 0xFDEF) || + ((c & 0xFFFF) == 0xFFFE) || ((c & 0xFFFF) == 0xFFFF); +} + +void utf8iterator_init(GumboParser* parser, const char* source, + size_t source_length, Utf8Iterator* iter) { + iter->_start = source; + iter->_end = source + source_length; + iter->_pos.line = 1; + iter->_pos.column = 1; + iter->_pos.offset = 0; + iter->_parser = parser; + read_char(iter); +} + +void utf8iterator_next(Utf8Iterator* iter) { + // We update positions based on the *last* character read, so that the first + // character following a newline is at column 1 in the next line. + update_position(iter); + iter->_start += iter->_width; + read_char(iter); +} + +int utf8iterator_current(const Utf8Iterator* iter) { return iter->_current; } + +void utf8iterator_get_position( + const Utf8Iterator* iter, GumboSourcePosition* output) { + *output = iter->_pos; +} + +const char* utf8iterator_get_char_pointer(const Utf8Iterator* iter) { + return iter->_start; +} + +const char* utf8iterator_get_end_pointer(const Utf8Iterator* iter) { + return iter->_end; +} + +bool utf8iterator_maybe_consume_match(Utf8Iterator* iter, const char* prefix, + size_t length, bool case_sensitive) { + bool matched = (iter->_start + length <= iter->_end) && + (case_sensitive ? !strncmp(iter->_start, prefix, length) + : !strncasecmp(iter->_start, prefix, length)); + if (matched) { + for (unsigned int i = 0; i < length; ++i) { + utf8iterator_next(iter); + } + return true; + } else { + return false; + } +} + +void utf8iterator_mark(Utf8Iterator* iter) { + iter->_mark = iter->_start; + iter->_mark_pos = iter->_pos; +} + +// Returns the current input stream position to the mark. +void utf8iterator_reset(Utf8Iterator* iter) { + iter->_start = iter->_mark; + iter->_pos = iter->_mark_pos; + read_char(iter); +} + +// Sets the position and original text fields of an error to the value at the +// mark. +void utf8iterator_fill_error_at_mark(Utf8Iterator* iter, GumboError* error) { + error->position = iter->_mark_pos; + error->original_text = iter->_mark; +} diff --git a/gb.form.htmlview/src/gumbo/utf8.h b/gb.form.htmlview/src/gumbo/utf8.h new file mode 100644 index 00000000..ee852abf --- /dev/null +++ b/gb.form.htmlview/src/gumbo/utf8.h @@ -0,0 +1,132 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// This contains an implementation of a UTF8 iterator and decoder suitable for +// an HTML5 parser. This does a bit more than straight UTF-8 decoding. The +// HTML5 spec specifies that: +// 1. Decoding errors are parse errors. +// 2. Certain other codepoints (eg. control characters) are parse errors. +// 3. Carriage returns and CR/LF groups are converted to line feeds. +// http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#decoded-as-utf-8,-with-error-handling +// +// Also, we want to keep track of source positions for error handling. As a +// result, we fold all that functionality into this decoder, and can't use an +// off-the-shelf library. +// +// This header is internal-only, which is why we prefix functions with only +// utf8_ or utf8_iterator_ instead of gumbo_utf8_. + +#ifndef GUMBO_UTF8_H_ +#define GUMBO_UTF8_H_ + +#include <stdbool.h> +#include <stddef.h> + +#include "gumbo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct GumboInternalError; +struct GumboInternalParser; + +// Unicode replacement char. +extern const int kUtf8ReplacementChar; + +typedef struct GumboInternalUtf8Iterator { + // Points at the start of the code point most recently read into 'current'. + const char* _start; + + // Points at the mark. The mark is initially set to the beginning of the + // input. + const char* _mark; + + // Points past the end of the iter, like a past-the-end iterator in the STL. + const char* _end; + + // The code point under the cursor. + int _current; + + // The width in bytes of the current code point. + ptrdiff_t _width; + + // The SourcePosition for the current location. + GumboSourcePosition _pos; + + // The SourcePosition for the mark. + GumboSourcePosition _mark_pos; + + // Pointer back to the GumboParser instance, for configuration options and + // error recording. + struct GumboInternalParser* _parser; +} Utf8Iterator; + +// Returns true if this Unicode code point is in the list of characters +// forbidden by the HTML5 spec, such as NUL bytes and undefined control chars. +bool utf8_is_invalid_code_point(int c); + +// Initializes a new Utf8Iterator from the given byte buffer. The source does +// not have to be NUL-terminated, but the length must be passed in explicitly. +void utf8iterator_init(struct GumboInternalParser* parser, const char* source, + size_t source_length, Utf8Iterator* iter); + +// Advances the current position by one code point. +void utf8iterator_next(Utf8Iterator* iter); + +// Returns the current code point as an integer. +int utf8iterator_current(const Utf8Iterator* iter); + +// Retrieves and fills the output parameter with the current source position. +void utf8iterator_get_position( + const Utf8Iterator* iter, GumboSourcePosition* output); + +// Retrieves a character pointer to the start of the current character. +const char* utf8iterator_get_char_pointer(const Utf8Iterator* iter); + +// Retrieves a character pointer to 1 past the end of the buffer. This is +// necessary for certain state machines and string comparisons that would like +// to look directly for ASCII text in the buffer without going through the +// decoder. +const char* utf8iterator_get_end_pointer(const Utf8Iterator* iter); + +// If the upcoming text in the buffer matches the specified prefix (which has +// length 'length'), consume it and return true. Otherwise, return false with +// no other effects. If the length of the string would overflow the buffer, +// this returns false. Note that prefix should not contain null bytes because +// of the use of strncmp/strncasecmp internally. All existing use-cases adhere +// to this. +bool utf8iterator_maybe_consume_match( + Utf8Iterator* iter, const char* prefix, size_t length, bool case_sensitive); + +// "Marks" a particular location of interest in the input stream, so that it can +// later be reset() to. There's also the ability to record an error at the +// point that was marked, as oftentimes that's more useful than the last +// character before the error was detected. +void utf8iterator_mark(Utf8Iterator* iter); + +// Returns the current input stream position to the mark. +void utf8iterator_reset(Utf8Iterator* iter); + +// Sets the position and original text fields of an error to the value at the +// mark. +void utf8iterator_fill_error_at_mark( + Utf8Iterator* iter, struct GumboInternalError* error); + +#ifdef __cplusplus +} +#endif +#endif // GUMBO_UTF8_H_ diff --git a/gb.form.htmlview/src/gumbo/util.c b/gb.form.htmlview/src/gumbo/util.c new file mode 100644 index 00000000..5a24c115 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/util.c @@ -0,0 +1,58 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "util.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdarg.h> +#include <stdio.h> + +#include "gumbo.h" +#include "parser.h" + +// TODO(jdtang): This should be elsewhere, but there's no .c file for +// SourcePositions and yet the constant needs some linkage, so this is as good +// as any. +const GumboSourcePosition kGumboEmptySourcePosition = {0, 0, 0}; + +void* gumbo_parser_allocate(GumboParser* parser, size_t num_bytes) { + return parser->_options->allocator(parser->_options->userdata, num_bytes); +} + +void gumbo_parser_deallocate(GumboParser* parser, void* ptr) { + parser->_options->deallocator(parser->_options->userdata, ptr); +} + +char* gumbo_copy_stringz(GumboParser* parser, const char* str) { + char* buffer = gumbo_parser_allocate(parser, strlen(str) + 1); + strcpy(buffer, str); + return buffer; +} + +// Debug function to trace operation of the parser. Pass --copts=-DGUMBO_DEBUG +// to use. +void gumbo_debug(const char* format, ...) { +#ifdef GUMBO_DEBUG + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + fflush(stdout); +#endif +} diff --git a/gb.form.htmlview/src/gumbo/util.h b/gb.form.htmlview/src/gumbo/util.h new file mode 100644 index 00000000..dcde9e6c --- /dev/null +++ b/gb.form.htmlview/src/gumbo/util.h @@ -0,0 +1,60 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) +// +// This contains some utility functions that didn't fit into any of the other +// headers. + +#ifndef GUMBO_UTIL_H_ +#define GUMBO_UTIL_H_ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// Forward declaration since it's passed into some of the functions in this +// header. +struct GumboInternalParser; + +// Utility function for allocating & copying a null-terminated string into a +// freshly-allocated buffer. This is necessary for proper memory management; we +// have the convention that all const char* in parse tree structures are +// freshly-allocated, so if we didn't copy, we'd try to delete a literal string +// when the parse tree is destroyed. +char* gumbo_copy_stringz(struct GumboInternalParser* parser, const char* str); + +// Allocate a chunk of memory, using the allocator specified in the Parser's +// ~config options. +void* gumbo_parser_allocate( + struct GumboInternalParser* parser, size_t num_bytes); + +// Deallocate a chunk of memory, using the deallocator specified in the Parser's +// ~config options. +void gumbo_parser_deallocate(struct GumboInternalParser* parser, void* ptr); + +// Debug wrapper for printf, to make it easier to turn off debugging info when +// required. +void gumbo_debug(const char* format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_UTIL_H_ diff --git a/gb.form.htmlview/src/gumbo/vector.c b/gb.form.htmlview/src/gumbo/vector.c new file mode 100644 index 00000000..b9aa474e --- /dev/null +++ b/gb.form.htmlview/src/gumbo/vector.c @@ -0,0 +1,123 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#include "vector.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +#include "util.h" + +struct GumboInternalParser; + +const GumboVector kGumboEmptyVector = {NULL, 0, 0}; + +void gumbo_vector_init(struct GumboInternalParser* parser, + size_t initial_capacity, GumboVector* vector) { + vector->length = 0; + vector->capacity = (unsigned)initial_capacity; + if (initial_capacity > 0) { + vector->data = + gumbo_parser_allocate(parser, sizeof(void*) * initial_capacity); + } else { + vector->data = NULL; + } +} + +void gumbo_vector_destroy( + struct GumboInternalParser* parser, GumboVector* vector) { + if (vector->capacity > 0) { + gumbo_parser_deallocate(parser, vector->data); + } +} + +static void enlarge_vector_if_full( + struct GumboInternalParser* parser, GumboVector* vector) { + if (vector->length >= vector->capacity) { + if (vector->capacity) { + size_t old_num_bytes = sizeof(void*) * vector->capacity; + vector->capacity *= 2; + size_t num_bytes = sizeof(void*) * vector->capacity; + void** temp = gumbo_parser_allocate(parser, num_bytes); + memcpy(temp, vector->data, old_num_bytes); + gumbo_parser_deallocate(parser, vector->data); + vector->data = temp; + } else { + // 0-capacity vector; no previous array to deallocate. + vector->capacity = 2; + vector->data = + gumbo_parser_allocate(parser, sizeof(void*) * vector->capacity); + } + } +} + +void gumbo_vector_add( + struct GumboInternalParser* parser, void* element, GumboVector* vector) { + enlarge_vector_if_full(parser, vector); + assert(vector->data); + assert(vector->length < vector->capacity); + vector->data[vector->length++] = element; +} + +void* gumbo_vector_pop( + struct GumboInternalParser* parser, GumboVector* vector) { + if (vector->length == 0) { + return NULL; + } + return vector->data[--vector->length]; +} + +int gumbo_vector_index_of(GumboVector* vector, const void* element) { + for (unsigned int i = 0; i < vector->length; ++i) { + if (vector->data[i] == element) { + return i; + } + } + return -1; +} + +void gumbo_vector_insert_at(struct GumboInternalParser* parser, void* element, + unsigned int index, GumboVector* vector) { + assert(index >= 0); + assert(index <= vector->length); + enlarge_vector_if_full(parser, vector); + ++vector->length; + memmove(&vector->data[index + 1], &vector->data[index], + sizeof(void*) * (vector->length - index - 1)); + vector->data[index] = element; +} + +void gumbo_vector_remove( + struct GumboInternalParser* parser, void* node, GumboVector* vector) { + int index = gumbo_vector_index_of(vector, node); + if (index == -1) { + return; + } + gumbo_vector_remove_at(parser, index, vector); +} + +void* gumbo_vector_remove_at(struct GumboInternalParser* parser, + unsigned int index, GumboVector* vector) { + assert(index >= 0); + assert(index < vector->length); + void* result = vector->data[index]; + memmove(&vector->data[index], &vector->data[index + 1], + sizeof(void*) * (vector->length - index - 1)); + --vector->length; + return result; +} diff --git a/gb.form.htmlview/src/gumbo/vector.h b/gb.form.htmlview/src/gumbo/vector.h new file mode 100644 index 00000000..70fe6fa6 --- /dev/null +++ b/gb.form.htmlview/src/gumbo/vector.h @@ -0,0 +1,67 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: jdtang@google.com (Jonathan Tang) + +#ifndef GUMBO_VECTOR_H_ +#define GUMBO_VECTOR_H_ + +#include "gumbo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Forward declaration since it's passed into some of the functions in this +// header. +struct GumboInternalParser; + +// Initializes a new GumboVector with the specified initial capacity. +void gumbo_vector_init(struct GumboInternalParser* parser, + size_t initial_capacity, GumboVector* vector); + +// Frees the memory used by an GumboVector. Does not free the contained +// pointers. +void gumbo_vector_destroy( + struct GumboInternalParser* parser, GumboVector* vector); + +// Adds a new element to an GumboVector. +void gumbo_vector_add( + struct GumboInternalParser* parser, void* element, GumboVector* vector); + +// Removes and returns the element most recently added to the GumboVector. +// Ownership is transferred to caller. Capacity is unchanged. If the vector is +// empty, NULL is returned. +void* gumbo_vector_pop(struct GumboInternalParser* parser, GumboVector* vector); + +// Inserts an element at a specific index. This is potentially O(N) time, but +// is necessary for some of the spec's behavior. +void gumbo_vector_insert_at(struct GumboInternalParser* parser, void* element, + unsigned int index, GumboVector* vector); + +// Removes an element from the vector, or does nothing if the element is not in +// the vector. +void gumbo_vector_remove( + struct GumboInternalParser* parser, void* element, GumboVector* vector); + +// Removes and returns an element at a specific index. Note that this is +// potentially O(N) time and should be used sparingly. +void* gumbo_vector_remove_at(struct GumboInternalParser* parser, + unsigned int index, GumboVector* vector); + +#ifdef __cplusplus +} +#endif + +#endif // GUMBO_VECTOR_H_ diff --git a/gb.form.htmlview/src/litehtml/LICENSE b/gb.form.htmlview/src/litehtml/LICENSE new file mode 100644 index 00000000..601e1c71 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2013, Yuri Kobets (tordex) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gb.form.htmlview/src/litehtml/README.md b/gb.form.htmlview/src/litehtml/README.md new file mode 100644 index 00000000..c5df6b2d --- /dev/null +++ b/gb.form.htmlview/src/litehtml/README.md @@ -0,0 +1,38 @@ +# What is litehtml? + +**litehtml** is the lightweight HTML rendering engine with CSS2/CSS3 support. Note that **litehtml** itself does not draw any text, pictures or other graphics and that **litehtml** does not depend on any image/draw/font library. You are free to use any library to draw images, fonts and any other graphics. **litehtml** just parses HTML/CSS and places the HTML elements into the correct positions (renders HTML). To draw the HTML elements you have to implement the simple callback interface [document_container](https://github.com/litehtml/litehtml/wiki/document_container). This interface is really simple, check it out! The [document_container](https://github.com/litehtml/litehtml/wiki/document_container) implementation is required to render HTML correctly. + +# Where litehtml can be used + +**litehtml** can be used when you need to show HTML formatted text or even to create a mini-browser, but using it as a full-featured HTML engine is not recommended. Usually you don't need something like WebKit to show simple HTML tooltips or HTML-formatted text, **litehtml** is much better for these as it's more lightweight and easier to integrate into your application. + +## HTML Parser + +**litehtml** uses the [gumbo-parser](https://github.com/google/gumbo-parser) to parse HTML. Gumbo is an implementation of the HTML5 parsing algorithm implemented as a pure C99 library with no outside dependencies. It's designed to serve as a building block for other tools and libraries such as linters, validators, templating languages, and refactoring and analysis tools. + +## Compatibility + +**litehtml** is compatible with any platform suported by C++ and STL. For Windows MS Visual Studio 2013 is recommended. **litehtml** supports both UTF-8 and Unicode strings on Windows and UTF-8 strings on Linux and Haiku. + +## Support for HTML and CSS standards + +Unfortunately **litehtml** is not fully compatible with HTML/CSS standards. There is lots of work to do to make **litehtml** work as well as modern browsers. But **litehtml** supports most HTML tags and CSS properties. You can find the list of supported CSS properties in [this table](https://docs.google.com/spreadsheet/ccc?key=0AvHXl5n24PuhdHdELUdhaUl4OGlncXhDcDJuM1JpMnc&usp=sharing). For most simple usecases the HTML/CSS features supported by **litehtml** are enough. Right now **litehtml** supports even some pages with very complex HTML/CSS designs. As an example the pages created with [bootstrap framework](http://getbootstrap.com/) are usually well formatted by **litehtml**. + +## Testing litehtml + +You can [download the simple browser](http://www.litehtml.com/download.html) (**litebrowser**) to test the **litehtml** rendering engine. + +The litebrowser source codes are available on GitHub: + * [For Windows](https://github.com/litehtml/litebrowser) + * [For Linux](https://github.com/litehtml/litebrowser-linux) + * [For Haiku](https://github.com/adamfowleruk/litebrowser-haiku) + +## License + +**litehtml** is distributed under [New BSD License](https://opensource.org/licenses/BSD-3-Clause). +The **gumbo-parser** is disributed under [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + +## Links + + * [source code](https://github.com/litehtml/litehtml) + * [website](http://www.litehtml.com/) diff --git a/gb.form.htmlview/src/litehtml/attributes.h b/gb.form.htmlview/src/litehtml/attributes.h new file mode 100644 index 00000000..98487f02 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/attributes.h @@ -0,0 +1,35 @@ +#ifndef LH_ATTRIBUTES_H +#define LH_ATTRIBUTES_H + +namespace litehtml +{ + struct attr_color + { + unsigned char rgbBlue; + unsigned char rgbGreen; + unsigned char rgbRed; + unsigned char rgbAlpha; + attr_color() + { + rgbAlpha = 255; + rgbBlue = 0; + rgbGreen = 0; + rgbRed = 0; + } + }; + + struct attr_border + { + style_border border; + int width; + attr_color color; + + attr_border() + { + border = borderNone; + width = 0; + } + }; +} + +#endif // LH_ATTRIBUTES_H diff --git a/gb.form.htmlview/src/litehtml/background.cpp b/gb.form.htmlview/src/litehtml/background.cpp new file mode 100644 index 00000000..f83f36f1 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/background.cpp @@ -0,0 +1,76 @@ +#include "html.h" +#include "background.h" + +litehtml::background::background() +{ + m_attachment = background_attachment_scroll; + m_repeat = background_repeat_repeat; + m_clip = background_box_border; + m_origin = background_box_padding; + m_color.alpha = 0; + m_color.red = 0; + m_color.green = 0; + m_color.blue = 0; +} + +litehtml::background::background( const background& val ) +{ + m_image = val.m_image; + m_baseurl = val.m_baseurl; + m_color = val.m_color; + m_attachment = val.m_attachment; + m_position = val.m_position; + m_repeat = val.m_repeat; + m_clip = val.m_clip; + m_origin = val.m_origin; +} + +litehtml::background& litehtml::background::operator=( const background& val ) +{ + m_image = val.m_image; + m_baseurl = val.m_baseurl; + m_color = val.m_color; + m_attachment = val.m_attachment; + m_position = val.m_position; + m_repeat = val.m_repeat; + m_clip = val.m_clip; + m_origin = val.m_origin; + return *this; +} + + +litehtml::background_paint::background_paint() : color(0, 0, 0, 0) +{ + position_x = 0; + position_y = 0; + attachment = background_attachment_scroll; + repeat = background_repeat_repeat; + is_root = false; +} + +litehtml::background_paint::background_paint( const background_paint& val ) +{ + image = val.image; + baseurl = val.baseurl; + attachment = val.attachment; + repeat = val.repeat; + color = val.color; + clip_box = val.clip_box; + origin_box = val.origin_box; + border_box = val.border_box; + border_radius = val.border_radius; + image_size = val.image_size; + position_x = val.position_x; + position_y = val.position_y; + is_root = val.is_root; +} + +litehtml::background_paint& litehtml::background_paint::operator=( const background& val ) +{ + attachment = val.m_attachment; + baseurl = val.m_baseurl; + image = val.m_image; + repeat = val.m_repeat; + color = val.m_color; + return *this; +} diff --git a/gb.form.htmlview/src/litehtml/background.h b/gb.form.htmlview/src/litehtml/background.h new file mode 100644 index 00000000..35b022b3 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/background.h @@ -0,0 +1,58 @@ +#ifndef LH_BACKGROUND_H +#define LH_BACKGROUND_H + +#include "types.h" +#include "attributes.h" +#include "css_length.h" +#include "css_position.h" +#include "web_color.h" +#include "borders.h" + +namespace litehtml +{ + class background + { + public: + tstring m_image; + tstring m_baseurl; + web_color m_color; + background_attachment m_attachment; + css_position m_position; + background_repeat m_repeat; + background_box m_clip; + background_box m_origin; + css_border_radius m_radius; + + public: + background(); + background(const background& val); + ~background() = default; + + background& operator=(const background& val); + }; + + class background_paint + { + public: + tstring image; + tstring baseurl; + background_attachment attachment; + background_repeat repeat; + web_color color; + position clip_box; + position origin_box; + position border_box; + border_radiuses border_radius; + size image_size; + int position_x; + int position_y; + bool is_root; + public: + background_paint(); + background_paint(const background_paint& val); + background_paint& operator=(const background& val); + }; + +} + +#endif // LH_BACKGROUND_H diff --git a/gb.form.htmlview/src/litehtml/borders.h b/gb.form.htmlview/src/litehtml/borders.h new file mode 100644 index 00000000..72618670 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/borders.h @@ -0,0 +1,305 @@ +#ifndef LH_BORDERS_H +#define LH_BORDERS_H + +#include "css_length.h" +#include "types.h" + +namespace litehtml +{ + struct css_border + { + css_length width; + border_style style; + web_color color; + + css_border() + { + style = border_style_none; + } + + css_border(const css_border& val) + { + width = val.width; + style = val.style; + color = val.color; + } + + css_border& operator=(const css_border& val) + { + width = val.width; + style = val.style; + color = val.color; + return *this; + } + }; + + struct border + { + int width; + border_style style; + web_color color; + + border() + { + width = 0; + } + border(const border& val) + { + width = val.width; + style = val.style; + color = val.color; + } + border(const css_border& val) + { + width = (int) val.width.val(); + style = val.style; + color = val.color; + } + border& operator=(const border& val) + { + width = val.width; + style = val.style; + color = val.color; + return *this; + } + border& operator=(const css_border& val) + { + width = (int) val.width.val(); + style = val.style; + color = val.color; + return *this; + } + }; + + struct border_radiuses + { + int top_left_x; + int top_left_y; + + int top_right_x; + int top_right_y; + + int bottom_right_x; + int bottom_right_y; + + int bottom_left_x; + int bottom_left_y; + + border_radiuses() + { + top_left_x = 0; + top_left_y = 0; + top_right_x = 0; + top_right_y = 0; + bottom_right_x = 0; + bottom_right_y = 0; + bottom_left_x = 0; + bottom_left_y = 0; + } + border_radiuses(const border_radiuses& val) + { + top_left_x = val.top_left_x; + top_left_y = val.top_left_y; + top_right_x = val.top_right_x; + top_right_y = val.top_right_y; + bottom_right_x = val.bottom_right_x; + bottom_right_y = val.bottom_right_y; + bottom_left_x = val.bottom_left_x; + bottom_left_y = val.bottom_left_y; + } + border_radiuses& operator = (const border_radiuses& val) + { + top_left_x = val.top_left_x; + top_left_y = val.top_left_y; + top_right_x = val.top_right_x; + top_right_y = val.top_right_y; + bottom_right_x = val.bottom_right_x; + bottom_right_y = val.bottom_right_y; + bottom_left_x = val.bottom_left_x; + bottom_left_y = val.bottom_left_y; + return *this; + } + void operator += (const margins& mg) + { + top_left_x += mg.left; + top_left_y += mg.top; + top_right_x += mg.right; + top_right_y += mg.top; + bottom_right_x += mg.right; + bottom_right_y += mg.bottom; + bottom_left_x += mg.left; + bottom_left_y += mg.bottom; + fix_values(); + } + void operator -= (const margins& mg) + { + top_left_x -= mg.left; + top_left_y -= mg.top; + top_right_x -= mg.right; + top_right_y -= mg.top; + bottom_right_x -= mg.right; + bottom_right_y -= mg.bottom; + bottom_left_x -= mg.left; + bottom_left_y -= mg.bottom; + fix_values(); + } + void fix_values() + { + if (top_left_x < 0) top_left_x = 0; + if (top_left_y < 0) top_left_y = 0; + if (top_right_x < 0) top_right_x = 0; + if (top_right_y < 0) top_right_y = 0; + if (bottom_right_x < 0) bottom_right_x = 0; + if (bottom_right_y < 0) bottom_right_y = 0; + if (bottom_left_x < 0) bottom_left_x = 0; + if (bottom_left_y < 0) bottom_left_y = 0; + } + }; + + struct css_border_radius + { + css_length top_left_x; + css_length top_left_y; + + css_length top_right_x; + css_length top_right_y; + + css_length bottom_right_x; + css_length bottom_right_y; + + css_length bottom_left_x; + css_length bottom_left_y; + + css_border_radius() + { + + } + + css_border_radius(const css_border_radius& val) + { + top_left_x = val.top_left_x; + top_left_y = val.top_left_y; + top_right_x = val.top_right_x; + top_right_y = val.top_right_y; + bottom_left_x = val.bottom_left_x; + bottom_left_y = val.bottom_left_y; + bottom_right_x = val.bottom_right_x; + bottom_right_y = val.bottom_right_y; + } + + css_border_radius& operator=(const css_border_radius& val) + { + top_left_x = val.top_left_x; + top_left_y = val.top_left_y; + top_right_x = val.top_right_x; + top_right_y = val.top_right_y; + bottom_left_x = val.bottom_left_x; + bottom_left_y = val.bottom_left_y; + bottom_right_x = val.bottom_right_x; + bottom_right_y = val.bottom_right_y; + return *this; + } + border_radiuses calc_percents(int width, int height) + { + border_radiuses ret; + ret.bottom_left_x = bottom_left_x.calc_percent(width); + ret.bottom_left_y = bottom_left_y.calc_percent(height); + ret.top_left_x = top_left_x.calc_percent(width); + ret.top_left_y = top_left_y.calc_percent(height); + ret.top_right_x = top_right_x.calc_percent(width); + ret.top_right_y = top_right_y.calc_percent(height); + ret.bottom_right_x = bottom_right_x.calc_percent(width); + ret.bottom_right_y = bottom_right_y.calc_percent(height); + return ret; + } + }; + + struct css_borders + { + css_border left; + css_border top; + css_border right; + css_border bottom; + css_border_radius radius; + + css_borders() = default; + + bool is_visible() const + { + return left.width.val() != 0 || right.width.val() != 0 || top.width.val() != 0 || bottom.width.val() != 0; + } + + css_borders(const css_borders& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + radius = val.radius; + } + + css_borders& operator=(const css_borders& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + radius = val.radius; + return *this; + } + }; + + struct borders + { + border left; + border top; + border right; + border bottom; + border_radiuses radius; + + borders() = default; + + borders(const borders& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + radius = val.radius; + } + + borders(const css_borders& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + } + + bool is_visible() const + { + return left.width != 0 || right.width != 0 || top.width != 0 || bottom.width != 0; + } + + borders& operator=(const borders& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + radius = val.radius; + return *this; + } + + borders& operator=(const css_borders& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + return *this; + } + }; +} + +#endif // LH_BORDERS_H diff --git a/gb.form.htmlview/src/litehtml/box.cpp b/gb.form.htmlview/src/litehtml/box.cpp new file mode 100644 index 00000000..c0af4dce --- /dev/null +++ b/gb.form.htmlview/src/litehtml/box.cpp @@ -0,0 +1,432 @@ +#include "html.h" +#include "box.h" +#include "html_tag.h" + + +litehtml::box_type litehtml::block_box::get_type() const +{ + return box_block; +} + +int litehtml::block_box::height() const +{ + return m_element->height(); +} + +int litehtml::block_box::width() const +{ + return m_element->width(); +} + +void litehtml::block_box::add_element(const element::ptr &el) +{ + m_element = el; + el->m_box = this; +} + +void litehtml::block_box::finish(bool last_box) +{ + if(!m_element) return; + m_element->apply_relative_shift(m_box_right - m_box_left); +} + +bool litehtml::block_box::can_hold(const element::ptr &el, white_space ws) const +{ + if(m_element || el->is_inline_box()) + { + return false; + } + return true; +} + +bool litehtml::block_box::is_empty() const +{ + if(m_element) + { + return false; + } + return true; +} + +int litehtml::block_box::baseline() const +{ + if(m_element) + { + return m_element->get_base_line(); + } + return 0; +} + +void litehtml::block_box::get_elements( elements_vector& els ) +{ + els.push_back(m_element); +} + +int litehtml::block_box::top_margin() const +{ + if(m_element && m_element->collapse_top_margin()) + { + return m_element->m_margins.top; + } + return 0; +} + +int litehtml::block_box::bottom_margin() const +{ + if(m_element && m_element->collapse_bottom_margin()) + { + return m_element->m_margins.bottom; + } + return 0; +} + +void litehtml::block_box::y_shift( int shift ) +{ + m_box_top += shift; + if(m_element) + { + m_element->m_pos.y += shift; + } +} + +void litehtml::block_box::new_width( int left, int right, elements_vector& els ) +{ + +} + +////////////////////////////////////////////////////////////////////////// + +litehtml::box_type litehtml::line_box::get_type() const +{ + return box_line; +} + +int litehtml::line_box::height() const +{ + return m_height; +} + +int litehtml::line_box::width() const +{ + return m_width; +} + +void litehtml::line_box::add_element(const element::ptr &el) +{ + el->m_skip = false; + el->m_box = nullptr; + bool add = true; + if( (m_items.empty() && el->is_white_space()) || el->is_break() ) + { + el->m_skip = true; + } else if(el->is_white_space()) + { + if (have_last_space()) + { + add = false; + el->m_skip = true; + } + } + + if(add) + { + el->m_box = this; + m_items.push_back(el); + + if(!el->m_skip) + { + int el_shift_left = el->get_inline_shift_left(); + int el_shift_right = el->get_inline_shift_right(); + + el->m_pos.x = m_box_left + m_width + el_shift_left + el->content_margins_left(); + el->m_pos.y = m_box_top + el->content_margins_top(); + m_width += el->width() + el_shift_left + el_shift_right; + } + } +} + +void litehtml::line_box::finish(bool last_box) +{ + if( is_empty() || (!is_empty() && last_box && is_break_only()) ) + { + m_height = 0; + return; + } + + for(auto i = m_items.rbegin(); i != m_items.rend(); i++) + { + if((*i)->is_white_space() || (*i)->is_break()) + { + if(!(*i)->m_skip) + { + (*i)->m_skip = true; + m_width -= (*i)->width(); + } + } else + { + break; + } + } + + int base_line = m_font_metrics.base_line(); + int line_height = m_line_height; + + int add_x = 0; + switch(m_text_align) + { + case text_align_right: + if(m_width < (m_box_right - m_box_left)) + { + add_x = (m_box_right - m_box_left) - m_width; + } + break; + case text_align_center: + if(m_width < (m_box_right - m_box_left)) + { + add_x = ((m_box_right - m_box_left) - m_width) / 2; + } + break; + default: + add_x = 0; + } + + m_height = 0; + // find line box baseline and line-height + for(const auto& el : m_items) + { + if(el->get_display() == display_inline_text) + { + font_metrics fm; + el->get_font(&fm); + base_line = std::max(base_line, fm.base_line()); + line_height = std::max(line_height, el->line_height()); + m_height = std::max(m_height, fm.height); + } + el->m_pos.x += add_x; + } + + if(m_height) + { + base_line += (line_height - m_height) / 2; + } + + m_height = line_height; + + int y1 = 0; + int y2 = m_height; + + for (const auto& el : m_items) + { + if(el->get_display() == display_inline_text) + { + font_metrics fm; + el->get_font(&fm); + el->m_pos.y = m_height - base_line - fm.ascent; + } else + { + switch(el->get_vertical_align()) + { + case va_super: + case va_sub: + case va_baseline: + el->m_pos.y = m_height - base_line - el->height() + el->get_base_line() + el->content_margins_top(); + break; + case va_top: + el->m_pos.y = y1 + el->content_margins_top(); + break; + case va_text_top: + el->m_pos.y = m_height - base_line - m_font_metrics.ascent + el->content_margins_top(); + break; + case va_middle: + el->m_pos.y = m_height - base_line - m_font_metrics.x_height / 2 - el->height() / 2 + el->content_margins_top(); + break; + case va_bottom: + el->m_pos.y = y2 - el->height() + el->content_margins_top(); + break; + case va_text_bottom: + el->m_pos.y = m_height - base_line + m_font_metrics.descent - el->height() + el->content_margins_top(); + break; + } + y1 = std::min(y1, el->top()); + y2 = std::max(y2, el->bottom()); + } + } + + for (const auto& el : m_items) + { + el->m_pos.y -= y1; + el->m_pos.y += m_box_top; + if(el->get_display() != display_inline_text) + { + switch(el->get_vertical_align()) + { + case va_top: + el->m_pos.y = m_box_top + el->content_margins_top(); + break; + case va_bottom: + el->m_pos.y = m_box_top + (y2 - y1) - el->height() + el->content_margins_top(); + break; + case va_baseline: + //TODO: process vertical align "baseline" + break; + case va_middle: + //TODO: process vertical align "middle" + break; + case va_sub: + //TODO: process vertical align "sub" + break; + case va_super: + //TODO: process vertical align "super" + break; + case va_text_bottom: + //TODO: process vertical align "text-bottom" + break; + case va_text_top: + //TODO: process vertical align "text-top" + break; + } + } + + el->apply_relative_shift(m_box_right - m_box_left); + } + m_height = y2 - y1; + m_baseline = (base_line - y1) - (m_height - line_height); +} + +bool litehtml::line_box::can_hold(const element::ptr &el, white_space ws) const +{ + if(!el->is_inline_box()) return false; + + if(el->is_break()) + { + return false; + } + + if(ws == white_space_nowrap || ws == white_space_pre || (ws == white_space_pre_wrap && el->is_space())) + { + return true; + } + + if(m_box_left + m_width + el->width() + el->get_inline_shift_left() + el->get_inline_shift_right() > m_box_right) + { + return false; + } + + return true; +} + +bool litehtml::line_box::have_last_space() const +{ + bool ret = false; + for (auto i = m_items.rbegin(); i != m_items.rend() && !ret; i++) + { + if((*i)->is_white_space() || (*i)->is_break()) + { + ret = true; + } else + { + break; + } + } + return ret; +} + +bool litehtml::line_box::is_empty() const +{ + if(m_items.empty()) return true; + for (auto i = m_items.rbegin(); i != m_items.rend(); i++) + { + if(!(*i)->m_skip || (*i)->is_break()) + { + return false; + } + } + return true; +} + +int litehtml::line_box::baseline() const +{ + return m_baseline; +} + +void litehtml::line_box::get_elements( elements_vector& els ) +{ + els.insert(els.begin(), m_items.begin(), m_items.end()); +} + +int litehtml::line_box::top_margin() const +{ + return 0; +} + +int litehtml::line_box::bottom_margin() const +{ + return 0; +} + +void litehtml::line_box::y_shift( int shift ) +{ + m_box_top += shift; + for (auto& el : m_items) + { + el->m_pos.y += shift; + } +} + +bool litehtml::line_box::is_break_only() const +{ + if(m_items.empty()) return true; + + if(m_items.front()->is_break()) + { + for (auto& el : m_items) + { + if(!el->m_skip) + { + return false; + } + } + return true; + } + return false; +} + +void litehtml::line_box::new_width( int left, int right, elements_vector& els ) +{ + int add = left - m_box_left; + if(add) + { + m_box_left = left; + m_box_right = right; + m_width = 0; + auto remove_begin = m_items.end(); + for (auto i = m_items.begin() + 1; i != m_items.end(); i++) + { + element::ptr el = (*i); + + if(!el->m_skip) + { + if(m_box_left + m_width + el->width() + el->get_inline_shift_right() + el->get_inline_shift_left() > m_box_right) + { + remove_begin = i; + break; + } else + { + el->m_pos.x += add; + m_width += el->width() + el->get_inline_shift_right() + el->get_inline_shift_left(); + } + } + } + if(remove_begin != m_items.end()) + { + els.insert(els.begin(), remove_begin, m_items.end()); + m_items.erase(remove_begin, m_items.end()); + + for(const auto& el : els) + { + el->m_box = nullptr; + } + } + } +} + diff --git a/gb.form.htmlview/src/litehtml/box.h b/gb.form.htmlview/src/litehtml/box.h new file mode 100644 index 00000000..32c674e7 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/box.h @@ -0,0 +1,120 @@ +#ifndef LH_BOX_H +#define LH_BOX_H + +namespace litehtml +{ + class html_tag; + + enum box_type + { + box_block, + box_line + }; + + class box + { + public: + typedef std::unique_ptr<litehtml::box> ptr; + typedef std::vector< box::ptr > vector; + protected: + int m_box_top; + int m_box_left; + int m_box_right; + public: + box(int top, int left, int right) + { + m_box_top = top; + m_box_left = left; + m_box_right = right; + } + virtual ~box() = default; + + int bottom() const { return m_box_top + height(); } + int top() const { return m_box_top; } + int right() const { return m_box_left + width(); } + int left() const { return m_box_left; } + + virtual litehtml::box_type get_type() const = 0; + virtual int height() const = 0; + virtual int width() const = 0; + virtual void add_element(const element::ptr &el) = 0; + virtual bool can_hold(const element::ptr &el, white_space ws) const = 0; + virtual void finish(bool last_box = false) = 0; + virtual bool is_empty() const = 0; + virtual int baseline() const = 0; + virtual void get_elements(elements_vector& els) = 0; + virtual int top_margin() const = 0; + virtual int bottom_margin() const = 0; + virtual void y_shift(int shift) = 0; + virtual void new_width(int left, int right, elements_vector& els) = 0; + }; + + ////////////////////////////////////////////////////////////////////////// + + class block_box : public box + { + element::ptr m_element; + public: + block_box(int top, int left, int right) : box(top, left, right) + { + m_element = nullptr; + } + + litehtml::box_type get_type() const override; + int height() const override; + int width() const override; + void add_element(const element::ptr &el) override; + bool can_hold(const element::ptr &el, white_space ws) const override; + void finish(bool last_box = false) override; + bool is_empty() const override; + int baseline() const override; + void get_elements(elements_vector& els) override; + int top_margin() const override; + int bottom_margin() const override; + void y_shift(int shift) override; + void new_width(int left, int right, elements_vector& els) override; + }; + + ////////////////////////////////////////////////////////////////////////// + + class line_box : public box + { + elements_vector m_items; + int m_height; + int m_width; + int m_line_height; + font_metrics m_font_metrics; + int m_baseline; + text_align m_text_align; + public: + line_box(int top, int left, int right, int line_height, font_metrics& fm, text_align align) : box(top, left, right) + { + m_height = 0; + m_width = 0; + m_font_metrics = fm; + m_line_height = line_height; + m_baseline = 0; + m_text_align = align; + } + + litehtml::box_type get_type() const override; + int height() const override; + int width() const override; + void add_element(const element::ptr &el) override; + bool can_hold(const element::ptr &el, white_space ws) const override; + void finish(bool last_box = false) override; + bool is_empty() const override; + int baseline() const override; + void get_elements(elements_vector& els) override; + int top_margin() const override; + int bottom_margin() const override; + void y_shift(int shift) override; + void new_width(int left, int right, elements_vector& els) override; + + private: + bool have_last_space() const; + bool is_break_only() const; + }; +} + +#endif // LH_BOX_H diff --git a/gb.form.htmlview/src/litehtml/codepoint.cpp b/gb.form.htmlview/src/litehtml/codepoint.cpp new file mode 100644 index 00000000..b0ed36f2 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/codepoint.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "litehtml/codepoint.h" + +#include <iostream> + +namespace { + +bool lookup(const uint32_t* table, litehtml::tchar_t c) +{ + return table[c >> 5] & (1 << (c & 0x1f)); +} + +} // namespace + +namespace litehtml { + +bool is_ascii_codepoint(litehtml::tchar_t c) +{ + return (c < 128); +} + +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.2 +bool is_url_reserved_codepoint(litehtml::tchar_t c) +{ + static const uint32_t reserved_lookup[] = { + 0x00000000, + 0xac009fda, + 0x28000001, + 0x00000000 + }; + + if (!is_ascii_codepoint(c)) { + return false; + } + return lookup(reserved_lookup, c); +} + +// https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 +bool is_url_scheme_codepoint(litehtml::tchar_t c) +{ + static const uint32_t scheme_lookup[] = { + 0x00000000, + 0x03ff6800, + 0x07fffffe, + 0x07fffffe, + }; + + if (!is_ascii_codepoint(c)) { + return false; + } + return lookup(scheme_lookup, c); +} + +} // namespace litehtml diff --git a/gb.form.htmlview/src/litehtml/codepoint.h b/gb.form.htmlview/src/litehtml/codepoint.h new file mode 100644 index 00000000..0bd7906b --- /dev/null +++ b/gb.form.htmlview/src/litehtml/codepoint.h @@ -0,0 +1,51 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_CODEPOINT_H__ +#define LITEHTML_CODEPOINT_H__ + +#include <string> + +#include "litehtml/os_types.h" + +namespace litehtml { + +bool is_ascii_codepoint(litehtml::tchar_t c); + +// Returns true if the codepoint is a reserved codepoint for URLs. +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.2 +bool is_url_reserved_codepoint(litehtml::tchar_t c); + +// Returns true if the codepoint is a scheme codepoint for URLs. +// https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 +bool is_url_scheme_codepoint(litehtml::tchar_t c); + +} // namespace litehtml + +#endif // LITEHTML_CODEPOINT_H__ diff --git a/gb.form.htmlview/src/litehtml/context.cpp b/gb.form.htmlview/src/litehtml/context.cpp new file mode 100644 index 00000000..88f39e71 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/context.cpp @@ -0,0 +1,12 @@ +#include "html.h" +#include "context.h" +#include "stylesheet.h" + + +void litehtml::context::load_master_stylesheet( const tchar_t* str ) +{ + media_query_list::ptr media; + + m_master_css.parse_stylesheet(str, nullptr, std::shared_ptr<litehtml::document>(), media_query_list::ptr()); + m_master_css.sort_selectors(); +} diff --git a/gb.form.htmlview/src/litehtml/context.h b/gb.form.htmlview/src/litehtml/context.h new file mode 100644 index 00000000..b6450f8f --- /dev/null +++ b/gb.form.htmlview/src/litehtml/context.h @@ -0,0 +1,20 @@ +#ifndef LH_CONTEXT_H +#define LH_CONTEXT_H + +#include "stylesheet.h" + +namespace litehtml +{ + class context + { + litehtml::css m_master_css; + public: + void load_master_stylesheet(const tchar_t* str); + litehtml::css& master_css() + { + return m_master_css; + } + }; +} + +#endif // LH_CONTEXT_H diff --git a/gb.form.htmlview/src/litehtml/css_length.cpp b/gb.form.htmlview/src/litehtml/css_length.cpp new file mode 100644 index 00000000..c61c2989 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/css_length.cpp @@ -0,0 +1,54 @@ +#include "html.h" +#include "css_length.h" + +void litehtml::css_length::fromString( const tstring& str, const tstring& predefs, int defValue ) +{ + // TODO: Make support for calc + if(str.substr(0, 4) == _t("calc")) + { + m_is_predefined = true; + m_predef = 0; + return; + } + + int predef = value_index(str, predefs, -1); + if(predef >= 0) + { + m_is_predefined = true; + m_predef = predef; + } else + { + m_is_predefined = false; + + tstring num; + tstring un; + bool is_unit = false; + for(tchar_t chr : str) + { + if(!is_unit) + { + if(t_isdigit(chr) || chr == _t('.') || chr == _t('+') || chr == _t('-')) + { + num += chr; + } else + { + is_unit = true; + } + } + if(is_unit) + { + un += chr; + } + } + if(!num.empty()) + { + m_value = (float) t_strtod(num.c_str(), nullptr); + m_units = (css_units) value_index(un, css_units_strings, css_units_none); + } else + { + // not a number so it is predefined + m_is_predefined = true; + m_predef = defValue; + } + } +} diff --git a/gb.form.htmlview/src/litehtml/css_length.h b/gb.form.htmlview/src/litehtml/css_length.h new file mode 100644 index 00000000..13a3d77b --- /dev/null +++ b/gb.form.htmlview/src/litehtml/css_length.h @@ -0,0 +1,135 @@ +#ifndef LH_CSS_LENGTH_H +#define LH_CSS_LENGTH_H + +#include "types.h" + +namespace litehtml +{ + class css_length + { + union + { + float m_value; + int m_predef; + }; + css_units m_units; + bool m_is_predefined; + public: + css_length(); + css_length(const css_length& val); + + css_length& operator=(const css_length& val); + css_length& operator=(float val); + bool is_predefined() const; + void predef(int val); + int predef() const; + void set_value(float val, css_units units); + float val() const; + css_units units() const; + int calc_percent(int width) const; + void fromString(const tstring& str, const tstring& predefs = _t(""), int defValue = 0); + }; + + // css_length inlines + + inline css_length::css_length() + { + m_value = 0; + m_predef = 0; + m_units = css_units_none; + m_is_predefined = false; + } + + inline css_length::css_length(const css_length& val) + { + if(val.is_predefined()) + { + m_predef = val.m_predef; + } else + { + m_value = val.m_value; + } + m_units = val.m_units; + m_is_predefined = val.m_is_predefined; + } + + inline css_length& css_length::operator=(const css_length& val) + { + if(val.is_predefined()) + { + m_predef = val.m_predef; + } else + { + m_value = val.m_value; + } + m_units = val.m_units; + m_is_predefined = val.m_is_predefined; + return *this; + } + + inline css_length& css_length::operator=(float val) + { + m_value = val; + m_units = css_units_px; + m_is_predefined = false; + return *this; + } + + inline bool css_length::is_predefined() const + { + return m_is_predefined; + } + + inline void css_length::predef(int val) + { + m_predef = val; + m_is_predefined = true; + } + + inline int css_length::predef() const + { + if(m_is_predefined) + { + return m_predef; + } + return 0; + } + + inline void css_length::set_value(float val, css_units units) + { + m_value = val; + m_is_predefined = false; + m_units = units; + } + + inline float css_length::val() const + { + if(!m_is_predefined) + { + return m_value; + } + return 0; + } + + inline css_units css_length::units() const + { + return m_units; + } + + inline int css_length::calc_percent(int width) const + { + if(!is_predefined()) + { + if(units() == css_units_percentage) + { + return (int) ((double) width * (double) m_value / 100.0); + } else + { + return (int) val(); + } + } + return 0; + } +} + +#endif // LH_CSS_LENGTH_H diff --git a/gb.form.htmlview/src/litehtml/css_margins.h b/gb.form.htmlview/src/litehtml/css_margins.h new file mode 100644 index 00000000..def378a5 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/css_margins.h @@ -0,0 +1,36 @@ +#ifndef LH_CSS_MARGINS_H +#define LH_CSS_MARGINS_H + +#include "css_length.h" + +namespace litehtml +{ + struct css_margins + { + css_length left; + css_length right; + css_length top; + css_length bottom; + + css_margins() = default; + + css_margins(const css_margins& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + } + + css_margins& operator=(const css_margins& val) + { + left = val.left; + right = val.right; + top = val.top; + bottom = val.bottom; + return *this; + } + }; +} + +#endif // LH_CSS_MARGINS_H diff --git a/gb.form.htmlview/src/litehtml/css_offsets.h b/gb.form.htmlview/src/litehtml/css_offsets.h new file mode 100644 index 00000000..abeddfb6 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/css_offsets.h @@ -0,0 +1,36 @@ +#ifndef LH_CSS_OFFSETS_H +#define LH_CSS_OFFSETS_H + +#include "css_length.h" + +namespace litehtml +{ + struct css_offsets + { + css_length left; + css_length top; + css_length right; + css_length bottom; + + css_offsets() = default; + + css_offsets(const css_offsets& val) + { + left = val.left; + top = val.top; + right = val.right; + bottom = val.bottom; + } + + css_offsets& operator=(const css_offsets& val) + { + left = val.left; + top = val.top; + right = val.right; + bottom = val.bottom; + return *this; + } + }; +} + +#endif // LH_CSS_OFFSETS_H diff --git a/gb.form.htmlview/src/litehtml/css_position.h b/gb.form.htmlview/src/litehtml/css_position.h new file mode 100644 index 00000000..1f884121 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/css_position.h @@ -0,0 +1,36 @@ +#ifndef LH_CSS_POSITION_H +#define LH_CSS_POSITION_H + +#include "css_length.h" + +namespace litehtml +{ + struct css_position + { + css_length x; + css_length y; + css_length width; + css_length height; + + css_position() = default; + + css_position(const css_position& val) + { + x = val.x; + y = val.y; + width = val.width; + height = val.height; + } + + css_position& operator=(const css_position& val) + { + x = val.x; + y = val.y; + width = val.width; + height = val.height; + return *this; + } + }; +} + +#endif // LH_CSS_POSITION_H diff --git a/gb.form.htmlview/src/litehtml/css_selector.cpp b/gb.form.htmlview/src/litehtml/css_selector.cpp new file mode 100644 index 00000000..39113c3f --- /dev/null +++ b/gb.form.htmlview/src/litehtml/css_selector.cpp @@ -0,0 +1,263 @@ +#include "html.h" +#include "css_selector.h" +#include "document.h" + +void litehtml::css_element_selector::parse( const tstring& txt ) +{ + tstring::size_type el_end = txt.find_first_of(_t(".#[:")); + m_tag = txt.substr(0, el_end); + litehtml::lcase(m_tag); + m_attrs.clear(); + while(el_end != tstring::npos) + { + if(txt[el_end] == _t('.')) + { + css_attribute_selector attribute; + + tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 1); + attribute.val = txt.substr(el_end + 1, pos - el_end - 1); + split_string( attribute.val, attribute.class_val, _t(" ") ); + attribute.condition = select_equal; + attribute.attribute = _t("class"); + m_attrs.push_back(attribute); + el_end = pos; + } else if(txt[el_end] == _t(':')) + { + css_attribute_selector attribute; + + if(txt[el_end + 1] == _t(':')) + { + tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 2); + attribute.val = txt.substr(el_end + 2, pos - el_end - 2); + attribute.condition = select_pseudo_element; + litehtml::lcase(attribute.val); + attribute.attribute = _t("pseudo-el"); + m_attrs.push_back(attribute); + el_end = pos; + } else + { + tstring::size_type pos = txt.find_first_of(_t(".#[:("), el_end + 1); + if(pos != tstring::npos && txt.at(pos) == _t('(')) + { + pos = find_close_bracket(txt, pos); + if(pos != tstring::npos) + { + pos++; + } + } + if(pos != tstring::npos) + { + attribute.val = txt.substr(el_end + 1, pos - el_end - 1); + } else + { + attribute.val = txt.substr(el_end + 1); + } + litehtml::lcase(attribute.val); + if(attribute.val == _t("after") || attribute.val == _t("before")) + { + attribute.condition = select_pseudo_element; + } else + { + attribute.condition = select_pseudo_class; + } + attribute.attribute = _t("pseudo"); + m_attrs.push_back(attribute); + el_end = pos; + } + } else if(txt[el_end] == _t('#')) + { + css_attribute_selector attribute; + + tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 1); + attribute.val = txt.substr(el_end + 1, pos - el_end - 1); + attribute.condition = select_equal; + attribute.attribute = _t("id"); + m_attrs.push_back(attribute); + el_end = pos; + } else if(txt[el_end] == _t('[')) + { + css_attribute_selector attribute; + + tstring::size_type pos = txt.find_first_of(_t("]~=|$*^"), el_end + 1); + tstring attr = txt.substr(el_end + 1, pos - el_end - 1); + trim(attr); + litehtml::lcase(attr); + if(pos != tstring::npos) + { + if(txt[pos] == _t(']')) + { + attribute.condition = select_exists; + } else if(txt[pos] == _t('=')) + { + attribute.condition = select_equal; + pos++; + } else if(txt.substr(pos, 2) == _t("~=")) + { + attribute.condition = select_contain_str; + pos += 2; + } else if(txt.substr(pos, 2) == _t("|=")) + { + attribute.condition = select_start_str; + pos += 2; + } else if(txt.substr(pos, 2) == _t("^=")) + { + attribute.condition = select_start_str; + pos += 2; + } else if(txt.substr(pos, 2) == _t("$=")) + { + attribute.condition = select_end_str; + pos += 2; + } else if(txt.substr(pos, 2) == _t("*=")) + { + attribute.condition = select_contain_str; + pos += 2; + } else + { + attribute.condition = select_exists; + pos += 1; + } + pos = txt.find_first_not_of(_t(" \t"), pos); + if(pos != tstring::npos) + { + if(txt[pos] == _t('"')) + { + tstring::size_type pos2 = txt.find_first_of(_t('\"'), pos + 1); + attribute.val = txt.substr(pos + 1, pos2 == tstring::npos ? pos2 : (pos2 - pos - 1)); + pos = pos2 == tstring::npos ? pos2 : (pos2 + 1); + } else if(txt[pos] == _t(']')) + { + pos ++; + } else + { + tstring::size_type pos2 = txt.find_first_of(_t(']'), pos + 1); + attribute.val = txt.substr(pos, pos2 == tstring::npos ? pos2 : (pos2 - pos)); + trim(attribute.val); + pos = pos2 == tstring::npos ? pos2 : (pos2 + 1); + } + } + } else + { + attribute.condition = select_exists; + } + attribute.attribute = attr; + m_attrs.push_back(attribute); + el_end = pos; + } else + { + el_end++; + } + el_end = txt.find_first_of(_t(".#[:"), el_end); + } +} + + +bool litehtml::css_selector::parse( const tstring& text ) +{ + if(text.empty()) + { + return false; + } + string_vector tokens; + split_string(text, tokens, _t(""), _t(" \t>+~"), _t("([")); + + if(tokens.empty()) + { + return false; + } + + tstring left; + tstring right = tokens.back(); + tchar_t combinator = 0; + + tokens.pop_back(); + while(!tokens.empty() && (tokens.back() == _t(" ") || tokens.back() == _t("\t") || tokens.back() == _t("+") || tokens.back() == _t("~") || tokens.back() == _t(">"))) + { + if(combinator == _t(' ') || combinator == 0) + { + combinator = tokens.back()[0]; + } + tokens.pop_back(); + } + + for(const auto & token : tokens) + { + left += token; + } + + trim(left); + trim(right); + + if(right.empty()) + { + return false; + } + + m_right.parse(right); + + switch(combinator) + { + case _t('>'): + m_combinator = combinator_child; + break; + case _t('+'): + m_combinator = combinator_adjacent_sibling; + break; + case _t('~'): + m_combinator = combinator_general_sibling; + break; + default: + m_combinator = combinator_descendant; + break; + } + + m_left = nullptr; + + if(!left.empty()) + { + m_left = std::make_shared<css_selector>(media_query_list::ptr(nullptr), _t("")); + if(!m_left->parse(left)) + { + return false; + } + } + + return true; +} + +void litehtml::css_selector::calc_specificity() +{ + if(!m_right.m_tag.empty() && m_right.m_tag != _t("*")) + { + m_specificity.d = 1; + } + for(const auto& attr : m_right.m_attrs) + { + if(attr.attribute == _t("id")) + { + m_specificity.b++; + } else + { + if(attr.attribute == _t("class")) + { + m_specificity.c += (int) attr.class_val.size(); + } else + { + m_specificity.c++; + } + } + } + if(m_left) + { + m_left->calc_specificity(); + m_specificity += m_left->m_specificity; + } +} + +void litehtml::css_selector::add_media_to_doc( document* doc ) const +{ + if(m_media_query && doc) + { + doc->add_media_list(m_media_query); + } +} + diff --git a/gb.form.htmlview/src/litehtml/css_selector.h b/gb.form.htmlview/src/litehtml/css_selector.h new file mode 100644 index 00000000..da64df09 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/css_selector.h @@ -0,0 +1,278 @@ +#ifndef LH_CSS_SELECTOR_H +#define LH_CSS_SELECTOR_H + +#include "style.h" +#include "media_query.h" + +namespace litehtml +{ + ////////////////////////////////////////////////////////////////////////// + + struct selector_specificity + { + int a; + int b; + int c; + int d; + + explicit selector_specificity(int va = 0, int vb = 0, int vc = 0, int vd = 0) + { + a = va; + b = vb; + c = vc; + d = vd; + } + + void operator += (const selector_specificity& val) + { + a += val.a; + b += val.b; + c += val.c; + d += val.d; + } + + bool operator==(const selector_specificity& val) const + { + if(a == val.a && b == val.b && c == val.c && d == val.d) + { + return true; + } + return false; + } + + bool operator!=(const selector_specificity& val) const + { + if(a != val.a || b != val.b || c != val.c || d != val.d) + { + return true; + } + return false; + } + + bool operator > (const selector_specificity& val) const + { + if(a > val.a) + { + return true; + } else if(a < val.a) + { + return false; + } else + { + if(b > val.b) + { + return true; + } else if(b < val.b) + { + return false; + } else + { + if(c > val.c) + { + return true; + } else if(c < val.c) + { + return false; + } else + { + if(d > val.d) + { + return true; + } else if(d < val.d) + { + return false; + } + } + } + } + return false; + } + + bool operator >= (const selector_specificity& val) const + { + if((*this) == val) return true; + if((*this) > val) return true; + return false; + } + + bool operator <= (const selector_specificity& val) const + { + if((*this) > val) + { + return false; + } + return true; + } + + bool operator < (const selector_specificity& val) const + { + if((*this) <= val && (*this) != val) + { + return true; + } + return false; + } + + }; + + ////////////////////////////////////////////////////////////////////////// + + enum attr_select_condition + { + select_exists, + select_equal, + select_contain_str, + select_start_str, + select_end_str, + select_pseudo_class, + select_pseudo_element, + }; + + ////////////////////////////////////////////////////////////////////////// + + struct css_attribute_selector + { + typedef std::vector<css_attribute_selector> vector; + + tstring attribute; + tstring val; + string_vector class_val; + attr_select_condition condition; + + css_attribute_selector() + { + condition = select_exists; + } + }; + + ////////////////////////////////////////////////////////////////////////// + + class css_element_selector + { + public: + tstring m_tag; + css_attribute_selector::vector m_attrs; + public: + + void parse(const tstring& txt); + }; + + ////////////////////////////////////////////////////////////////////////// + + enum css_combinator + { + combinator_descendant, + combinator_child, + combinator_adjacent_sibling, + combinator_general_sibling + }; + + ////////////////////////////////////////////////////////////////////////// + + class css_selector + { + public: + typedef std::shared_ptr<css_selector> ptr; + typedef std::vector<css_selector::ptr> vector; + public: + selector_specificity m_specificity; + css_element_selector m_right; + css_selector::ptr m_left; + css_combinator m_combinator; + tstring m_style; + int m_order; + media_query_list::ptr m_media_query; + tstring m_baseurl; + public: + explicit css_selector(const media_query_list::ptr& media, const tstring& baseurl) + { + m_media_query = media; + m_baseurl = baseurl; + m_combinator = combinator_descendant; + m_order = 0; + } + + ~css_selector() = default; + + css_selector(const css_selector& val) + { + m_right = val.m_right; + if(val.m_left) + { + m_left = std::make_shared<css_selector>(*val.m_left); + } else + { + m_left = nullptr; + } + m_combinator = val.m_combinator; + m_specificity = val.m_specificity; + m_order = val.m_order; + m_media_query = val.m_media_query; + } + + bool parse(const tstring& text); + void calc_specificity(); + bool is_media_valid() const; + void add_media_to_doc(document* doc) const; + }; + + inline bool css_selector::is_media_valid() const + { + if(!m_media_query) + { + return true; + } + return m_media_query->is_used(); + } + + + ////////////////////////////////////////////////////////////////////////// + + inline bool operator > (const css_selector& v1, const css_selector& v2) + { + if(v1.m_specificity == v2.m_specificity) + { + return (v1.m_order > v2.m_order); + } + return (v1.m_specificity > v2.m_specificity); + } + + inline bool operator < (const css_selector& v1, const css_selector& v2) + { + if(v1.m_specificity == v2.m_specificity) + { + return (v1.m_order < v2.m_order); + } + return (v1.m_specificity < v2.m_specificity); + } + + inline bool operator >(const css_selector::ptr& v1, const css_selector::ptr& v2) + { + return (*v1 > *v2); + } + + inline bool operator < (const css_selector::ptr& v1, const css_selector::ptr& v2) + { + return (*v1 < *v2); + } + + ////////////////////////////////////////////////////////////////////////// + + class used_selector + { + public: + typedef std::unique_ptr<used_selector> ptr; + typedef std::vector<used_selector::ptr> vector; + + css_selector::ptr m_selector; + bool m_used; + + used_selector(const css_selector::ptr& selector, bool used) + { + m_used = used; + m_selector = selector; + } + }; +} + +#endif // LH_CSS_SELECTOR_H diff --git a/gb.form.htmlview/src/litehtml/document.cpp b/gb.form.htmlview/src/litehtml/document.cpp new file mode 100644 index 00000000..c151877e --- /dev/null +++ b/gb.form.htmlview/src/litehtml/document.cpp @@ -0,0 +1,974 @@ +#include "html.h" +#include "document.h" +#include "stylesheet.h" +#include "html_tag.h" +#include "el_text.h" +#include "el_para.h" +#include "el_space.h" +#include "el_body.h" +#include "el_image.h" +#include "el_table.h" +#include "el_td.h" +#include "el_link.h" +#include "el_title.h" +#include "el_style.h" +#include "el_script.h" +#include "el_comment.h" +#include "el_cdata.h" +#include "el_base.h" +#include "el_anchor.h" +#include "el_break.h" +#include "el_div.h" +#include "el_font.h" +#include "el_tr.h" +#include "el_li.h" +#include <cmath> +#include <cstdio> +#include <algorithm> +#include <functional> +#include "gumbo.h" +#include "utf8_strings.h" + +litehtml::document::document(litehtml::document_container* objContainer, litehtml::context* ctx) +{ + m_container = objContainer; + m_context = ctx; +} + +litehtml::document::~document() +{ + m_over_element = nullptr; + if(m_container) + { + for(auto & m_font : m_fonts) + { + m_container->delete_font(m_font.second.font); + } + } +} + +litehtml::document::ptr litehtml::document::createFromString( const tchar_t* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles) +{ + return createFromUTF8(litehtml_to_utf8(str), objPainter, ctx, user_styles); +} + +litehtml::document::ptr litehtml::document::createFromUTF8(const char* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles) +{ + // parse document into GumboOutput + GumboOutput* output = gumbo_parse((const char*) str); + + // Create litehtml::document + litehtml::document::ptr doc = std::make_shared<litehtml::document>(objPainter, ctx); + + // Create litehtml::elements. + elements_vector root_elements; + doc->create_node(output->root, root_elements, true); + if (!root_elements.empty()) + { + doc->m_root = root_elements.back(); + } + // Destroy GumboOutput + gumbo_destroy_output(&kGumboDefaultOptions, output); + + // Let's process created elements tree + if (doc->m_root) + { + doc->container()->get_media_features(doc->m_media); + + doc->m_root->set_pseudo_class(_t("root"), true); + + // apply master CSS + doc->m_root->apply_stylesheet(ctx->master_css()); + + // parse elements attributes + doc->m_root->parse_attributes(); + + // parse style sheets linked in document + media_query_list::ptr media; + for (const auto& css : doc->m_css) + { + if (!css.media.empty()) + { + media = media_query_list::create_from_string(css.media, doc); + } + else + { + media = nullptr; + } + doc->m_styles.parse_stylesheet(css.text.c_str(), css.baseurl.c_str(), doc, media); + } + // Sort css selectors using CSS rules. + doc->m_styles.sort_selectors(); + + // get current media features + if (!doc->m_media_lists.empty()) + { + doc->update_media_lists(doc->m_media); + } + + // Apply parsed styles. + doc->m_root->apply_stylesheet(doc->m_styles); + + // Apply user styles if any + if (user_styles) + { + doc->m_root->apply_stylesheet(*user_styles); + } + + // Parse applied styles in the elements + doc->m_root->parse_styles(); + + // Now the m_tabular_elements is filled with tabular elements. + // We have to check the tabular elements for missing table elements + // and create the anonymous boxes in visual table layout + doc->fix_tables_layout(); + + // Fanaly initialize elements + doc->m_root->init(); + } + + return doc; +} + +litehtml::uint_ptr litehtml::document::add_font( const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm ) +{ + uint_ptr ret = 0; + + if(!name || !t_strcasecmp(name, _t("inherit"))) + { + name = m_container->get_default_font_name(); + } + + if(!size) + { + size = container()->get_default_font_size(); + } + + tchar_t strSize[20]; + t_itoa(size, strSize, 20, 10); + + tstring key = name; + key += _t(":"); + key += strSize; + key += _t(":"); + key += weight; + key += _t(":"); + key += style; + key += _t(":"); + key += decoration; + + if(m_fonts.find(key) == m_fonts.end()) + { + font_style fs = (font_style) value_index(style, font_style_strings, fontStyleNormal); + int fw = value_index(weight, font_weight_strings, -1); + if(fw >= 0) + { + switch(fw) + { + case litehtml::fontWeightBold: + fw = 700; + break; + case litehtml::fontWeightBolder: + fw = 600; + break; + case litehtml::fontWeightLighter: + fw = 300; + break; + default: + fw = 400; + break; + } + } else + { + fw = t_atoi(weight); + if(fw < 100) + { + fw = 400; + } + } + + unsigned int decor = 0; + + if(decoration) + { + std::vector<tstring> tokens; + split_string(decoration, tokens, _t(" ")); + for(auto & token : tokens) + { + if(!t_strcasecmp(token.c_str(), _t("underline"))) + { + decor |= font_decoration_underline; + } else if(!t_strcasecmp(token.c_str(), _t("line-through"))) + { + decor |= font_decoration_linethrough; + } else if(!t_strcasecmp(token.c_str(), _t("overline"))) + { + decor |= font_decoration_overline; + } + } + } + + font_item fi= {0}; + + fi.font = m_container->create_font(name, size, fw, fs, decor, &fi.metrics); + m_fonts[key] = fi; + ret = fi.font; + if(fm) + { + *fm = fi.metrics; + } + } + return ret; +} + +litehtml::uint_ptr litehtml::document::get_font( const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm ) +{ + if(!name || !t_strcasecmp(name, _t("inherit"))) + { + name = m_container->get_default_font_name(); + } + + if(!size) + { + size = m_container->get_default_font_size(); + } + + tchar_t strSize[20]; + t_itoa(size, strSize, 20, 10); + + tstring key = name; + key += _t(":"); + key += strSize; + key += _t(":"); + key += weight; + key += _t(":"); + key += style; + key += _t(":"); + key += decoration; + + auto el = m_fonts.find(key); + + if(el != m_fonts.end()) + { + if(fm) + { + *fm = el->second.metrics; + } + return el->second.font; + } + return add_font(name, size, weight, style, decoration, fm); +} + +int litehtml::document::render( int max_width, render_type rt ) +{ + int ret = 0; + if(m_root) + { + if(rt == render_fixed_only) + { + m_fixed_boxes.clear(); + m_root->render_positioned(rt); + } else + { + ret = m_root->render(0, 0, max_width); + if(m_root->fetch_positioned()) + { + m_fixed_boxes.clear(); + m_root->render_positioned(rt); + } + m_size.width = 0; + m_size.height = 0; + m_root->calc_document_size(m_size); + } + } + return ret; +} + +void litehtml::document::draw( uint_ptr hdc, int x, int y, const position* clip ) +{ + if(m_root) + { + m_root->draw(hdc, x, y, clip); + m_root->draw_stacking_context(hdc, x, y, clip, true); + } +} + +int litehtml::document::cvt_units( const tchar_t* str, int fontSize, bool* is_percent/*= 0*/ ) const +{ + if(!str) return 0; + + css_length val; + val.fromString(str); + if(is_percent && val.units() == css_units_percentage && !val.is_predefined()) + { + *is_percent = true; + } + return cvt_units(val, fontSize); +} + +int litehtml::document::cvt_units( css_length& val, int fontSize, int size ) const +{ + if(val.is_predefined()) + { + return 0; + } + int ret; + switch(val.units()) + { + case css_units_percentage: + ret = val.calc_percent(size); + break; + case css_units_em: + ret = round_f(val.val() * (float) fontSize); + val.set_value((float) ret, css_units_px); + break; + case css_units_pt: + ret = m_container->pt_to_px((int) val.val()); + val.set_value((float) ret, css_units_px); + break; + case css_units_in: + ret = m_container->pt_to_px((int) (val.val() * 72)); + val.set_value((float) ret, css_units_px); + break; + case css_units_cm: + ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72)); + val.set_value((float) ret, css_units_px); + break; + case css_units_mm: + ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72) / 10); + val.set_value((float) ret, css_units_px); + break; + case css_units_vw: + ret = (int)((double)m_media.width * (double)val.val() / 100.0); + break; + case css_units_vh: + ret = (int)((double)m_media.height * (double)val.val() / 100.0); + break; + case css_units_vmin: + ret = (int)((double)std::min(m_media.height, m_media.width) * (double)val.val() / 100.0); + break; + case css_units_vmax: + ret = (int)((double)std::max(m_media.height, m_media.width) * (double)val.val() / 100.0); + break; + case css_units_rem: + ret = (int) ((double) m_root->get_font_size() * (double) val.val()); + val.set_value((float) ret, css_units_px); + break; + default: + ret = (int) val.val(); + break; + } + return ret; +} + +int litehtml::document::width() const +{ + return m_size.width; +} + +int litehtml::document::height() const +{ + return m_size.height; +} + +void litehtml::document::add_stylesheet( const tchar_t* str, const tchar_t* baseurl, const tchar_t* media ) +{ + if(str && str[0]) + { + m_css.push_back(css_text(str, baseurl, media)); + } +} + +bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) +{ + if(!m_root) + { + return false; + } + + element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y); + + bool state_was_changed = false; + + if(over_el != m_over_element) + { + if(m_over_element) + { + if(m_over_element->on_mouse_leave()) + { + state_was_changed = true; + } + } + m_over_element = over_el; + } + + const tchar_t* cursor = nullptr; + + if(m_over_element) + { + if(m_over_element->on_mouse_over()) + { + state_was_changed = true; + } + cursor = m_over_element->get_cursor(); + } + + m_container->set_cursor(cursor ? cursor : _t("auto")); + + if(state_was_changed) + { + return m_root->find_styles_changes(redraw_boxes, 0, 0); + } + return false; +} + +bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes ) +{ + if(!m_root) + { + return false; + } + if(m_over_element) + { + if(m_over_element->on_mouse_leave()) + { + return m_root->find_styles_changes(redraw_boxes, 0, 0); + } + } + return false; +} + +bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) +{ + if(!m_root) + { + return false; + } + + element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y); + + bool state_was_changed = false; + + if(over_el != m_over_element) + { + if(m_over_element) + { + if(m_over_element->on_mouse_leave()) + { + state_was_changed = true; + } + } + m_over_element = over_el; + if(m_over_element) + { + if(m_over_element->on_mouse_over()) + { + state_was_changed = true; + } + } + } + + const tchar_t* cursor = nullptr; + + if(m_over_element) + { + if(m_over_element->on_lbutton_down()) + { + state_was_changed = true; + } + cursor = m_over_element->get_cursor(); + } + + m_container->set_cursor(cursor ? cursor : _t("auto")); + + if(state_was_changed) + { + return m_root->find_styles_changes(redraw_boxes, 0, 0); + } + + return false; +} + +bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) +{ + if(!m_root) + { + return false; + } + if(m_over_element) + { + if(m_over_element->on_lbutton_up()) + { + return m_root->find_styles_changes(redraw_boxes, 0, 0); + } + } + return false; +} + +litehtml::element::ptr litehtml::document::create_element(const tchar_t* tag_name, const string_map& attributes) +{ + element::ptr newTag; + document::ptr this_doc = shared_from_this(); + if(m_container) + { + newTag = m_container->create_element(tag_name, attributes, this_doc); + } + if(!newTag) + { + if(!t_strcmp(tag_name, _t("br"))) + { + newTag = std::make_shared<litehtml::el_break>(this_doc); + } else if(!t_strcmp(tag_name, _t("p"))) + { + newTag = std::make_shared<litehtml::el_para>(this_doc); + } else if(!t_strcmp(tag_name, _t("img"))) + { + newTag = std::make_shared<litehtml::el_image>(this_doc); + } else if(!t_strcmp(tag_name, _t("table"))) + { + newTag = std::make_shared<litehtml::el_table>(this_doc); + } else if(!t_strcmp(tag_name, _t("td")) || !t_strcmp(tag_name, _t("th"))) + { + newTag = std::make_shared<litehtml::el_td>(this_doc); + } else if(!t_strcmp(tag_name, _t("link"))) + { + newTag = std::make_shared<litehtml::el_link>(this_doc); + } else if(!t_strcmp(tag_name, _t("title"))) + { + newTag = std::make_shared<litehtml::el_title>(this_doc); + } else if(!t_strcmp(tag_name, _t("a"))) + { + newTag = std::make_shared<litehtml::el_anchor>(this_doc); + } else if(!t_strcmp(tag_name, _t("tr"))) + { + newTag = std::make_shared<litehtml::el_tr>(this_doc); + } else if(!t_strcmp(tag_name, _t("style"))) + { + newTag = std::make_shared<litehtml::el_style>(this_doc); + } else if(!t_strcmp(tag_name, _t("base"))) + { + newTag = std::make_shared<litehtml::el_base>(this_doc); + } else if(!t_strcmp(tag_name, _t("body"))) + { + newTag = std::make_shared<litehtml::el_body>(this_doc); + } else if(!t_strcmp(tag_name, _t("div"))) + { + newTag = std::make_shared<litehtml::el_div>(this_doc); + } else if(!t_strcmp(tag_name, _t("script"))) + { + newTag = std::make_shared<litehtml::el_script>(this_doc); + } else if(!t_strcmp(tag_name, _t("font"))) + { + newTag = std::make_shared<litehtml::el_font>(this_doc); + } else if(!t_strcmp(tag_name, _t("li"))) + { + newTag = std::make_shared<litehtml::el_li>(this_doc); + } else + { + newTag = std::make_shared<litehtml::html_tag>(this_doc); + } + } + + if(newTag) + { + newTag->set_tagName(tag_name); + for (const auto & attribute : attributes) + { + newTag->set_attr(attribute.first.c_str(), attribute.second.c_str()); + } + } + + return newTag; +} + +void litehtml::document::get_fixed_boxes( position::vector& fixed_boxes ) +{ + fixed_boxes = m_fixed_boxes; +} + +void litehtml::document::add_fixed_box( const position& pos ) +{ + m_fixed_boxes.push_back(pos); +} + +bool litehtml::document::media_changed() +{ + container()->get_media_features(m_media); + if (update_media_lists(m_media)) + { + m_root->refresh_styles(); + m_root->parse_styles(); + return true; + } + return false; +} + +bool litehtml::document::lang_changed() +{ + if(!m_media_lists.empty()) + { + tstring culture; + container()->get_language(m_lang, culture); + if(!culture.empty()) + { + m_culture = m_lang + _t('-') + culture; + } + else + { + m_culture.clear(); + } + m_root->refresh_styles(); + m_root->parse_styles(); + return true; + } + return false; +} + +bool litehtml::document::update_media_lists(const media_features& features) +{ + bool update_styles = false; + for(auto & m_media_list : m_media_lists) + { + if(m_media_list->apply_media_features(features)) + { + update_styles = true; + } + } + return update_styles; +} + +void litehtml::document::add_media_list( const media_query_list::ptr& list ) +{ + if(list) + { + if(std::find(m_media_lists.begin(), m_media_lists.end(), list) == m_media_lists.end()) + { + m_media_lists.push_back(list); + } + } +} + +void litehtml::document::create_node(void* gnode, elements_vector& elements, bool parseTextNode) +{ + auto* node = (GumboNode*)gnode; + switch (node->type) + { + case GUMBO_NODE_ELEMENT: + { + string_map attrs; + GumboAttribute* attr; + for (unsigned int i = 0; i < node->v.element.attributes.length; i++) + { + attr = (GumboAttribute*)node->v.element.attributes.data[i]; + attrs[tstring(litehtml_from_utf8(attr->name))] = litehtml_from_utf8(attr->value); + } + + + element::ptr ret; + const char* tag = gumbo_normalized_tagname(node->v.element.tag); + if (tag[0]) + { + ret = create_element(litehtml_from_utf8(tag), attrs); + } + else + { + if (node->v.element.original_tag.data && node->v.element.original_tag.length) + { + std::string strA; + gumbo_tag_from_original_text(&node->v.element.original_tag); + strA.append(node->v.element.original_tag.data, node->v.element.original_tag.length); + ret = create_element(litehtml_from_utf8(strA.c_str()), attrs); + } + } + if (!strcmp(tag, "script")) + { + parseTextNode = false; + } + if (ret) + { + elements_vector child; + for (unsigned int i = 0; i < node->v.element.children.length; i++) + { + child.clear(); + create_node(static_cast<GumboNode*> (node->v.element.children.data[i]), child, parseTextNode); + std::for_each(child.begin(), child.end(), + [&ret](element::ptr& el) + { + ret->appendChild(el); + } + ); + } + elements.push_back(ret); + } + } + break; + case GUMBO_NODE_TEXT: + { + std::wstring str; + std::wstring str_in = (const wchar_t*) (utf8_to_wchar(node->v.text.text)); + if (!parseTextNode) + { + elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str_in).c_str(), shared_from_this())); + } + else + { + m_container->split_text(node->v.text.text, + [this, &elements](const tchar_t* text) { elements.push_back(std::make_shared<el_text>(text, shared_from_this())); }, + [this, &elements](const tchar_t* text) { elements.push_back(std::make_shared<el_space>(text, shared_from_this())); }); + } + } + break; + case GUMBO_NODE_CDATA: + { + element::ptr ret = std::make_shared<el_cdata>(shared_from_this()); + ret->set_data(litehtml_from_utf8(node->v.text.text)); + elements.push_back(ret); + } + break; + case GUMBO_NODE_COMMENT: + { + element::ptr ret = std::make_shared<el_comment>(shared_from_this()); + ret->set_data(litehtml_from_utf8(node->v.text.text)); + elements.push_back(ret); + } + break; + case GUMBO_NODE_WHITESPACE: + { + tstring str = litehtml_from_utf8(node->v.text.text); + for (size_t i = 0; i < str.length(); i++) + { + elements.push_back(std::make_shared<el_space>(str.substr(i, 1).c_str(), shared_from_this())); + } + } + break; + default: + break; + } +} + +void litehtml::document::fix_tables_layout() +{ + size_t i = 0; + while (i < m_tabular_elements.size()) + { + element::ptr el_ptr = m_tabular_elements[i]; + + switch (el_ptr->get_display()) + { + case display_inline_table: + case display_table: + fix_table_children(el_ptr, display_table_row_group, _t("table-row-group")); + break; + case display_table_footer_group: + case display_table_row_group: + case display_table_header_group: + { + element::ptr parent = el_ptr->parent(); + if (parent) + { + if (parent->get_display() != display_inline_table) + fix_table_parent(el_ptr, display_table, _t("table")); + } + fix_table_children(el_ptr, display_table_row, _t("table-row")); + } + break; + case display_table_row: + fix_table_parent(el_ptr, display_table_row_group, _t("table-row-group")); + fix_table_children(el_ptr, display_table_cell, _t("table-cell")); + break; + case display_table_cell: + fix_table_parent(el_ptr, display_table_row, _t("table-row")); + break; + // TODO: make table layout fix for table-caption, table-column etc. elements + case display_table_caption: + case display_table_column: + case display_table_column_group: + default: + break; + } + i++; + } +} + +void litehtml::document::fix_table_children(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str) +{ + elements_vector tmp; + auto first_iter = el_ptr->m_children.begin(); + auto cur_iter = el_ptr->m_children.begin(); + + auto flush_elements = [&]() + { + element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this()); + annon_tag->add_style(tstring(_t("display:")) + disp_str, _t("")); + annon_tag->parent(el_ptr); + annon_tag->parse_styles(); + std::for_each(tmp.begin(), tmp.end(), + [&annon_tag](element::ptr& el) + { + annon_tag->appendChild(el); + } + ); + first_iter = el_ptr->m_children.insert(first_iter, annon_tag); + cur_iter = first_iter + 1; + while (cur_iter != el_ptr->m_children.end() && (*cur_iter)->parent() != el_ptr) + { + cur_iter = el_ptr->m_children.erase(cur_iter); + } + first_iter = cur_iter; + tmp.clear(); + }; + + while (cur_iter != el_ptr->m_children.end()) + { + if ((*cur_iter)->get_display() != disp) + { + if (!(*cur_iter)->is_table_skip() || ((*cur_iter)->is_table_skip() && !tmp.empty())) + { + if (disp != display_table_row_group || (*cur_iter)->get_display() != display_table_caption) + { + if (tmp.empty()) + { + first_iter = cur_iter; + } + tmp.push_back((*cur_iter)); + } + } + cur_iter++; + } + else if (!tmp.empty()) + { + flush_elements(); + } + else + { + cur_iter++; + } + } + if (!tmp.empty()) + { + flush_elements(); + } +} + +void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str) +{ + element::ptr parent = el_ptr->parent(); + + if (parent->get_display() != disp) + { + auto this_element = std::find_if(parent->m_children.begin(), parent->m_children.end(), + [&](element::ptr& el) + { + if (el == el_ptr) + { + return true; + } + return false; + } + ); + if (this_element != parent->m_children.end()) + { + style_display el_disp = el_ptr->get_display(); + auto first = this_element; + auto last = this_element; + auto cur = this_element; + + // find first element with same display + while (true) + { + if (cur == parent->m_children.begin()) break; + cur--; + if ((*cur)->is_table_skip() || (*cur)->get_display() == el_disp) + { + first = cur; + } + else + { + break; + } + } + + // find last element with same display + cur = this_element; + while (true) + { + cur++; + if (cur == parent->m_children.end()) break; + + if ((*cur)->is_table_skip() || (*cur)->get_display() == el_disp) + { + last = cur; + } + else + { + break; + } + } + + // extract elements with the same display and wrap them with anonymous object + element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this()); + annon_tag->add_style(tstring(_t("display:")) + disp_str, _t("")); + annon_tag->parent(parent); + annon_tag->parse_styles(); + std::for_each(first, last + 1, + [&annon_tag](element::ptr& el) + { + annon_tag->appendChild(el); + } + ); + first = parent->m_children.erase(first, last + 1); + parent->m_children.insert(first, annon_tag); + } + } +} + +void litehtml::document::append_children_from_string(element& parent, const tchar_t* str) +{ + append_children_from_utf8(parent, litehtml_to_utf8(str)); +} + +void litehtml::document::append_children_from_utf8(element& parent, const char* str) +{ + // parent must belong to this document + if (parent.get_document().get() != this) + { + return; + } + + // parse document into GumboOutput + GumboOutput* output = gumbo_parse((const char*) str); + + // Create litehtml::elements. + elements_vector child_elements; + create_node(output->root, child_elements, true); + + // Destroy GumboOutput + gumbo_destroy_output(&kGumboDefaultOptions, output); + + // Let's process created elements tree + for (const auto& child : child_elements) + { + // Add the child element to parent + parent.appendChild(child); + + // apply master CSS + child->apply_stylesheet(m_context->master_css()); + + // parse elements attributes + child->parse_attributes(); + + // Apply parsed styles. + child->apply_stylesheet(m_styles); + + // Parse applied styles in the elements + child->parse_styles(); + + // Now the m_tabular_elements is filled with tabular elements. + // We have to check the tabular elements for missing table elements + // and create the anonymous boxes in visual table layout + fix_tables_layout(); + + // Fanaly initialize elements + child->init(); + } +} diff --git a/gb.form.htmlview/src/litehtml/document.h b/gb.form.htmlview/src/litehtml/document.h new file mode 100644 index 00000000..1fd8dab8 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/document.h @@ -0,0 +1,117 @@ +#ifndef LH_DOCUMENT_H +#define LH_DOCUMENT_H + +#include "style.h" +#include "types.h" +#include "context.h" + +namespace litehtml +{ + struct css_text + { + typedef std::vector<css_text> vector; + + tstring text; + tstring baseurl; + tstring media; + + css_text() = default; + + css_text(const tchar_t* txt, const tchar_t* url, const tchar_t* media_str) + { + text = txt ? txt : _t(""); + baseurl = url ? url : _t(""); + media = media_str ? media_str : _t(""); + } + + css_text(const css_text& val) + { + text = val.text; + baseurl = val.baseurl; + media = val.media; + } + }; + + class html_tag; + + class document : public std::enable_shared_from_this<document> + { + public: + typedef std::shared_ptr<document> ptr; + typedef std::weak_ptr<document> weak_ptr; + private: + std::shared_ptr<element> m_root; + document_container* m_container; + fonts_map m_fonts; + css_text::vector m_css; + litehtml::css m_styles; + litehtml::web_color m_def_color; + litehtml::context* m_context; + litehtml::size m_size; + position::vector m_fixed_boxes; + media_query_list::vector m_media_lists; + element::ptr m_over_element; + elements_vector m_tabular_elements; + media_features m_media; + tstring m_lang; + tstring m_culture; + public: + document(litehtml::document_container* objContainer, litehtml::context* ctx); + virtual ~document(); + + litehtml::document_container* container() { return m_container; } + uint_ptr get_font(const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm); + int render(int max_width, render_type rt = render_all); + void draw(uint_ptr hdc, int x, int y, const position* clip); + web_color get_def_color() { return m_def_color; } + int cvt_units(const tchar_t* str, int fontSize, bool* is_percent = nullptr) const; + int cvt_units(css_length& val, int fontSize, int size = 0) const; + int width() const; + int height() const; + void add_stylesheet(const tchar_t* str, const tchar_t* baseurl, const tchar_t* media); + bool on_mouse_over(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); + bool on_lbutton_down(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); + bool on_lbutton_up(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); + bool on_mouse_leave(position::vector& redraw_boxes); + litehtml::element::ptr create_element(const tchar_t* tag_name, const string_map& attributes); + element::ptr root(); + void get_fixed_boxes(position::vector& fixed_boxes); + void add_fixed_box(const position& pos); + void add_media_list(const media_query_list::ptr& list); + bool media_changed(); + bool lang_changed(); + bool match_lang(const tstring & lang); + void add_tabular(const element::ptr& el); + element::const_ptr get_over_element() const { return m_over_element; } + + void append_children_from_string(element& parent, const tchar_t* str); + void append_children_from_utf8(element& parent, const char* str); + + static litehtml::document::ptr createFromString(const tchar_t* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles = nullptr); + static litehtml::document::ptr createFromUTF8(const char* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles = nullptr); + + private: + litehtml::uint_ptr add_font(const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm); + + void create_node(void* gnode, elements_vector& elements, bool parseTextNode); + bool update_media_lists(const media_features& features); + void fix_tables_layout(); + void fix_table_children(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str); + void fix_table_parent(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str); + }; + + inline element::ptr document::root() + { + return m_root; + } + inline void document::add_tabular(const element::ptr& el) + { + m_tabular_elements.push_back(el); + } + inline bool document::match_lang(const tstring & lang) + { + return lang == m_lang || lang == m_culture; + } +} + +#endif // LH_DOCUMENT_H diff --git a/gb.form.htmlview/src/litehtml/el_anchor.cpp b/gb.form.htmlview/src/litehtml/el_anchor.cpp new file mode 100644 index 00000000..178b39b9 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_anchor.cpp @@ -0,0 +1,26 @@ +#include "html.h" +#include "el_anchor.h" +#include "document.h" + +litehtml::el_anchor::el_anchor(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ +} + +void litehtml::el_anchor::on_click() +{ + const tchar_t* href = get_attr(_t("href")); + + if(href) + { + get_document()->container()->on_anchor_click(href, shared_from_this()); + } +} + +void litehtml::el_anchor::apply_stylesheet( const litehtml::css& stylesheet ) +{ + if( get_attr(_t("href")) ) + { + m_pseudo_classes.push_back(_t("link")); + } + html_tag::apply_stylesheet(stylesheet); +} diff --git a/gb.form.htmlview/src/litehtml/el_anchor.h b/gb.form.htmlview/src/litehtml/el_anchor.h new file mode 100644 index 00000000..b33794c5 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_anchor.h @@ -0,0 +1,18 @@ +#ifndef LH_EL_ANCHOR_H +#define LH_EL_ANCHOR_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_anchor : public html_tag + { + public: + explicit el_anchor(const std::shared_ptr<litehtml::document>& doc); + + void on_click() override; + void apply_stylesheet(const litehtml::css& stylesheet) override; + }; +} + +#endif // LH_EL_ANCHOR_H diff --git a/gb.form.htmlview/src/litehtml/el_base.cpp b/gb.form.htmlview/src/litehtml/el_base.cpp new file mode 100644 index 00000000..37245996 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_base.cpp @@ -0,0 +1,13 @@ +#include "html.h" +#include "el_base.h" +#include "document.h" + +litehtml::el_base::el_base(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + +} + +void litehtml::el_base::parse_attributes() +{ + get_document()->container()->set_base_url(get_attr(_t("href"))); +} diff --git a/gb.form.htmlview/src/litehtml/el_base.h b/gb.form.htmlview/src/litehtml/el_base.h new file mode 100644 index 00000000..d7efb806 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_base.h @@ -0,0 +1,17 @@ +#ifndef LH_EL_BASE_H +#define LH_EL_BASE_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_base : public html_tag + { + public: + explicit el_base(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + }; +} + +#endif // LH_EL_BASE_H diff --git a/gb.form.htmlview/src/litehtml/el_before_after.cpp b/gb.form.htmlview/src/litehtml/el_before_after.cpp new file mode 100644 index 00000000..9e2d8f45 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_before_after.cpp @@ -0,0 +1,207 @@ +#include "html.h" +#include "el_before_after.h" +#include "el_text.h" +#include "el_space.h" +#include "el_image.h" +#include "utf8_strings.h" + +litehtml::el_before_after_base::el_before_after_base(const std::shared_ptr<litehtml::document>& doc, bool before) : html_tag(doc) +{ + if(before) + { + m_tag = _t("::before"); + } else + { + m_tag = _t("::after"); + } +} + +void litehtml::el_before_after_base::add_style(const tstring& style, const tstring& baseurl) +{ + html_tag::add_style(style, baseurl); + + auto children = m_children; + m_children.clear(); + + tstring content = get_style_property(_t("content"), false, _t("")); + if(!content.empty()) + { + int idx = value_index(content, content_property_string); + if(idx < 0) + { + tstring fnc; + tstring::size_type i = 0; + while(i < content.length() && i != tstring::npos) + { + if(content.at(i) == _t('"')) + { + fnc.clear(); + i++; + tstring::size_type pos = content.find(_t('"'), i); + tstring txt; + if(pos == tstring::npos) + { + txt = content.substr(i); + i = tstring::npos; + } else + { + txt = content.substr(i, pos - i); + i = pos + 1; + } + add_text(txt); + } else if(content.at(i) == _t('(')) + { + i++; + litehtml::trim(fnc); + litehtml::lcase(fnc); + tstring::size_type pos = content.find(_t(')'), i); + tstring params; + if(pos == tstring::npos) + { + params = content.substr(i); + i = tstring::npos; + } else + { + params = content.substr(i, pos - i); + i = pos + 1; + } + add_function(fnc, params); + fnc.clear(); + } else + { + fnc += content.at(i); + i++; + } + } + } + } + + if(m_children.empty()) + { + m_children = children; + } +} + +void litehtml::el_before_after_base::add_text( const tstring& txt ) +{ + tstring word; + tstring esc; + for(tstring::size_type i = 0; i < txt.length(); i++) + { + if( (txt.at(i) == _t(' ')) || (txt.at(i) == _t('\t')) || (txt.at(i) == _t('\\') && !esc.empty()) ) + { + if(esc.empty()) + { + if(!word.empty()) + { + element::ptr el = std::make_shared<el_text>(word.c_str(), get_document()); + appendChild(el); + word.clear(); + } + + element::ptr el = std::make_shared<el_space>(txt.substr(i, 1).c_str(), get_document()); + appendChild(el); + } else + { + word += convert_escape(esc.c_str() + 1); + esc.clear(); + if(txt.at(i) == _t('\\')) + { + esc += txt.at(i); + } + } + } else + { + if(!esc.empty() || txt.at(i) == _t('\\')) + { + esc += txt.at(i); + } else + { + word += txt.at(i); + } + } + } + + if(!esc.empty()) + { + word += convert_escape(esc.c_str() + 1); + } + if(!word.empty()) + { + element::ptr el = std::make_shared<el_text>(word.c_str(), get_document()); + appendChild(el); + word.clear(); + } +} + +void litehtml::el_before_after_base::add_function( const tstring& fnc, const tstring& params ) +{ + int idx = value_index(fnc, _t("attr;counter;url")); + switch(idx) + { + // attr + case 0: + { + tstring p_name = params; + trim(p_name); + lcase(p_name); + element::ptr el_parent = parent(); + if (el_parent) + { + const tchar_t* attr_value = el_parent->get_attr(p_name.c_str()); + if (attr_value) + { + add_text(attr_value); + } + } + } + break; + // counter + case 1: + break; + // url + case 2: + { + tstring p_url = params; + trim(p_url); + if(!p_url.empty()) + { + if(p_url.at(0) == _t('\'') || p_url.at(0) == _t('\"')) + { + p_url.erase(0, 1); + } + } + if(!p_url.empty()) + { + if(p_url.at(p_url.length() - 1) == _t('\'') || p_url.at(p_url.length() - 1) == _t('\"')) + { + p_url.erase(p_url.length() - 1, 1); + } + } + if(!p_url.empty()) + { + element::ptr el = std::make_shared<el_image>(get_document()); + el->set_attr(_t("src"), p_url.c_str()); + el->set_attr(_t("style"), _t("display:inline-block")); + el->set_tagName(_t("img")); + appendChild(el); + el->parse_attributes(); + } + } + break; + } +} + +litehtml::tstring litehtml::el_before_after_base::convert_escape( const tchar_t* txt ) +{ + tchar_t* str_end; + wchar_t u_str[2]; + u_str[0] = (wchar_t) t_strtol(txt, &str_end, 16); + u_str[1] = 0; + return litehtml::tstring(litehtml_from_wchar(u_str)); +} + +void litehtml::el_before_after_base::apply_stylesheet( const litehtml::css& stylesheet ) +{ + +} diff --git a/gb.form.htmlview/src/litehtml/el_before_after.h b/gb.form.htmlview/src/litehtml/el_before_after.h new file mode 100644 index 00000000..34554c5b --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_before_after.h @@ -0,0 +1,40 @@ +#ifndef LH_EL_BEFORE_AFTER_H +#define LH_EL_BEFORE_AFTER_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_before_after_base : public html_tag + { + public: + el_before_after_base(const std::shared_ptr<litehtml::document>& doc, bool before); + + void add_style(const tstring& style, const tstring& baseurl) override; + void apply_stylesheet(const litehtml::css& stylesheet) override; + private: + void add_text(const tstring& txt); + void add_function(const tstring& fnc, const tstring& params); + static tstring convert_escape(const tchar_t* txt); + }; + + class el_before : public el_before_after_base + { + public: + explicit el_before(const std::shared_ptr<litehtml::document>& doc) : el_before_after_base(doc, true) + { + + } + }; + + class el_after : public el_before_after_base + { + public: + explicit el_after(const std::shared_ptr<litehtml::document>& doc) : el_before_after_base(doc, false) + { + + } + }; +} + +#endif // LH_EL_BEFORE_AFTER_H diff --git a/gb.form.htmlview/src/litehtml/el_body.cpp b/gb.form.htmlview/src/litehtml/el_body.cpp new file mode 100644 index 00000000..3d35f8f7 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_body.cpp @@ -0,0 +1,12 @@ +#include "html.h" +#include "el_body.h" +#include "document.h" + +litehtml::el_body::el_body(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) +{ +} + +bool litehtml::el_body::is_body() const +{ + return true; +} diff --git a/gb.form.htmlview/src/litehtml/el_body.h b/gb.form.htmlview/src/litehtml/el_body.h new file mode 100644 index 00000000..fb30e0c5 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_body.h @@ -0,0 +1,17 @@ +#ifndef LH_EL_BODY_H +#define LH_EL_BODY_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_body : public html_tag + { + public: + explicit el_body(const std::shared_ptr<litehtml::document>& doc); + + bool is_body() const override; + }; +} + +#endif // LH_EL_BODY_H diff --git a/gb.form.htmlview/src/litehtml/el_break.cpp b/gb.form.htmlview/src/litehtml/el_break.cpp new file mode 100644 index 00000000..f13b2e3c --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_break.cpp @@ -0,0 +1,13 @@ +#include "html.h" +#include "el_break.h" + +litehtml::el_break::el_break(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + +} + +bool litehtml::el_break::is_break() const +{ + return true; +} + diff --git a/gb.form.htmlview/src/litehtml/el_break.h b/gb.form.htmlview/src/litehtml/el_break.h new file mode 100644 index 00000000..81f38fec --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_break.h @@ -0,0 +1,17 @@ +#ifndef LH_EL_BREAK_H +#define LH_EL_BREAK_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_break : public html_tag + { + public: + explicit el_break(const std::shared_ptr<litehtml::document>& doc); + + bool is_break() const override; + }; +} + +#endif // LH_EL_BREAK_H diff --git a/gb.form.htmlview/src/litehtml/el_cdata.cpp b/gb.form.htmlview/src/litehtml/el_cdata.cpp new file mode 100644 index 00000000..b25f4985 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_cdata.cpp @@ -0,0 +1,20 @@ +#include "html.h" +#include "el_cdata.h" + +litehtml::el_cdata::el_cdata(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +{ + m_skip = true; +} + +void litehtml::el_cdata::get_text( tstring& text ) +{ + text += m_text; +} + +void litehtml::el_cdata::set_data( const tchar_t* data ) +{ + if(data) + { + m_text += data; + } +} diff --git a/gb.form.htmlview/src/litehtml/el_cdata.h b/gb.form.htmlview/src/litehtml/el_cdata.h new file mode 100644 index 00000000..a948c620 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_cdata.h @@ -0,0 +1,19 @@ +#ifndef LH_EL_CDATA_H +#define LH_EL_CDATA_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_cdata : public element + { + tstring m_text; + public: + explicit el_cdata(const std::shared_ptr<litehtml::document>& doc); + + void get_text(tstring& text) override; + void set_data(const tchar_t* data) override; + }; +} + +#endif // LH_EL_CDATA_H diff --git a/gb.form.htmlview/src/litehtml/el_comment.cpp b/gb.form.htmlview/src/litehtml/el_comment.cpp new file mode 100644 index 00000000..a4353563 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_comment.cpp @@ -0,0 +1,25 @@ +#include "html.h" +#include "el_comment.h" + +litehtml::el_comment::el_comment(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +{ + m_skip = true; +} + +bool litehtml::el_comment::is_comment() const +{ + return true; +} + +void litehtml::el_comment::get_text( tstring& text ) +{ + text += m_text; +} + +void litehtml::el_comment::set_data( const tchar_t* data ) +{ + if(data) + { + m_text += data; + } +} diff --git a/gb.form.htmlview/src/litehtml/el_comment.h b/gb.form.htmlview/src/litehtml/el_comment.h new file mode 100644 index 00000000..543b3b80 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_comment.h @@ -0,0 +1,20 @@ +#ifndef LH_EL_COMMENT_H +#define LH_EL_COMMENT_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_comment : public element + { + tstring m_text; + public: + explicit el_comment(const std::shared_ptr<litehtml::document>& doc); + + bool is_comment() const override; + void get_text(tstring& text) override; + void set_data(const tchar_t* data) override; + }; +} + +#endif // LH_EL_COMMENT_H diff --git a/gb.form.htmlview/src/litehtml/el_div.cpp b/gb.form.htmlview/src/litehtml/el_div.cpp new file mode 100644 index 00000000..47904b0a --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_div.cpp @@ -0,0 +1,18 @@ +#include "html.h" +#include "el_div.h" + + +litehtml::el_div::el_div(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + +} + +void litehtml::el_div::parse_attributes() +{ + const tchar_t* str = get_attr(_t("align")); + if(str) + { + m_style.add_property(_t("text-align"), str, 0, false, this); + } + html_tag::parse_attributes(); +} diff --git a/gb.form.htmlview/src/litehtml/el_div.h b/gb.form.htmlview/src/litehtml/el_div.h new file mode 100644 index 00000000..a2d031a3 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_div.h @@ -0,0 +1,17 @@ +#ifndef LH_EL_DIV_H +#define LH_EL_DIV_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_div : public html_tag + { + public: + explicit el_div(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + }; +} + +#endif // LH_EL_DIV_H diff --git a/gb.form.htmlview/src/litehtml/el_font.cpp b/gb.form.htmlview/src/litehtml/el_font.cpp new file mode 100644 index 00000000..a75de1c5 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_font.cpp @@ -0,0 +1,55 @@ +#include "html.h" +#include "el_font.h" + + +litehtml::el_font::el_font(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + +} + +void litehtml::el_font::parse_attributes() +{ + const tchar_t* str = get_attr(_t("color")); + if(str) + { + m_style.add_property(_t("color"), str, nullptr, false, this); + } + + str = get_attr(_t("face")); + if(str) + { + m_style.add_property(_t("font-face"), str, nullptr, false, this); + } + + str = get_attr(_t("size")); + if(str) + { + int sz = t_atoi(str); + if(sz <= 1) + { + m_style.add_property(_t("font-size"), _t("x-small"), nullptr, false, this); + } else if(sz >= 6) + { + m_style.add_property(_t("font-size"), _t("xx-large"), nullptr, false, this); + } else + { + switch(sz) + { + case 2: + m_style.add_property(_t("font-size"), _t("small"), nullptr, false, this); + break; + case 3: + m_style.add_property(_t("font-size"), _t("medium"), nullptr, false, this); + break; + case 4: + m_style.add_property(_t("font-size"), _t("large"), nullptr, false, this); + break; + case 5: + m_style.add_property(_t("font-size"), _t("x-large"), nullptr, false, this); + break; + } + } + } + + html_tag::parse_attributes(); +} diff --git a/gb.form.htmlview/src/litehtml/el_font.h b/gb.form.htmlview/src/litehtml/el_font.h new file mode 100644 index 00000000..ccb89451 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_font.h @@ -0,0 +1,17 @@ +#ifndef LH_EL_FONT_H +#define LH_EL_FONT_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_font : public html_tag + { + public: + explicit el_font(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + }; +} + +#endif // LH_EL_FONT_H diff --git a/gb.form.htmlview/src/litehtml/el_image.cpp b/gb.form.htmlview/src/litehtml/el_image.cpp new file mode 100644 index 00000000..be71ba15 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_image.cpp @@ -0,0 +1,273 @@ +#include "html.h" +#include "el_image.h" +#include "document.h" + +litehtml::el_image::el_image(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + m_display = display_inline_block; +} + +litehtml::el_image::~el_image( void ) +{ + +} + +void litehtml::el_image::get_content_size( size& sz, int max_width ) +{ + get_document()->container()->get_image_size(m_src.c_str(), 0, sz); +} + +int litehtml::el_image::calc_max_height(int image_height) +{ + document::ptr doc = get_document(); + int percentSize = 0; + if (m_css_max_height.units() == css_units_percentage) + { + auto el_parent = parent(); + if (el_parent) + { + if (!el_parent->get_predefined_height(percentSize)) + { + return image_height; + } + } + } + return doc->cvt_units(m_css_max_height, m_font_size, percentSize); +} + +int litehtml::el_image::line_height() const +{ + return height(); +} + +bool litehtml::el_image::is_replaced() const +{ + return true; +} + +int litehtml::el_image::render( int x, int y, int max_width, bool second_pass ) +{ + int parent_width = max_width; + + calc_outlines(parent_width); + + m_pos.move_to(x, y); + + document::ptr doc = get_document(); + + litehtml::size sz; + doc->container()->get_image_size(m_src.c_str(), 0, sz); + + m_pos.width = sz.width; + m_pos.height = sz.height; + + if(m_css_height.is_predefined() && m_css_width.is_predefined()) + { + m_pos.height = sz.height; + m_pos.width = sz.width; + + // check for max-width + if(!m_css_max_width.is_predefined()) + { + int max_width = doc->cvt_units(m_css_max_width, m_font_size, parent_width); + if(m_pos.width > max_width) + { + m_pos.width = max_width; + } + if(sz.width) + { + m_pos.height = (int) ((float) m_pos.width * (float) sz.height / (float)sz.width); + } else + { + m_pos.height = sz.height; + } + } + + // check for max-height + if(!m_css_max_height.is_predefined()) + { + int max_height = calc_max_height(sz.height); + if(m_pos.height > max_height) + { + m_pos.height = max_height; + } + if(sz.height) + { + m_pos.width = (int) (m_pos.height * (float)sz.width / (float)sz.height); + } else + { + m_pos.width = sz.width; + } + } + } else if(!m_css_height.is_predefined() && m_css_width.is_predefined()) + { + if (!get_predefined_height(m_pos.height)) + { + m_pos.height = (int)m_css_height.val(); + } + + // check for max-height + if(!m_css_max_height.is_predefined()) + { + int max_height = calc_max_height(sz.height); + if(m_pos.height > max_height) + { + m_pos.height = max_height; + } + } + + if(sz.height) + { + m_pos.width = (int) (m_pos.height * (float)sz.width / (float)sz.height); + } else + { + m_pos.width = sz.width; + } + } else if(m_css_height.is_predefined() && !m_css_width.is_predefined()) + { + m_pos.width = (int) m_css_width.calc_percent(parent_width); + + // check for max-width + if(!m_css_max_width.is_predefined()) + { + int max_width = doc->cvt_units(m_css_max_width, m_font_size, parent_width); + if(m_pos.width > max_width) + { + m_pos.width = max_width; + } + } + + if(sz.width) + { + m_pos.height = (int) ((float) m_pos.width * (float) sz.height / (float)sz.width); + } else + { + m_pos.height = sz.height; + } + } else + { + m_pos.width = (int) m_css_width.calc_percent(parent_width); + m_pos.height = 0; + if (!get_predefined_height(m_pos.height)) + { + m_pos.height = (int)m_css_height.val(); + } + + // check for max-height + if(!m_css_max_height.is_predefined()) + { + int max_height = calc_max_height(sz.height); + if(m_pos.height > max_height) + { + m_pos.height = max_height; + } + } + + // check for max-height + if(!m_css_max_width.is_predefined()) + { + int max_width = doc->cvt_units(m_css_max_width, m_font_size, parent_width); + if(m_pos.width > max_width) + { + m_pos.width = max_width; + } + } + } + + calc_auto_margins(parent_width); + + m_pos.x += content_margins_left(); + m_pos.y += content_margins_top(); + + return m_pos.width + content_margins_left() + content_margins_right(); +} + +void litehtml::el_image::parse_attributes() +{ + m_src = get_attr(_t("src"), _t("")); + + const tchar_t* attr_height = get_attr(_t("height")); + if(attr_height) + { + m_style.add_property(_t("height"), attr_height, 0, false, this); + } + const tchar_t* attr_width = get_attr(_t("width")); + if(attr_width) + { + m_style.add_property(_t("width"), attr_width, 0, false, this); + } +} + +void litehtml::el_image::draw( uint_ptr hdc, int x, int y, const position* clip ) +{ + position pos = m_pos; + pos.x += x; + pos.y += y; + + position el_pos = pos; + el_pos += m_padding; + el_pos += m_borders; + + // draw standard background here + if (el_pos.does_intersect(clip)) + { + const background* bg = get_background(); + if (bg) + { + background_paint bg_paint; + init_background_paint(pos, bg_paint, bg); + + get_document()->container()->draw_background(hdc, bg_paint); + } + } + + // draw image as background + if(pos.does_intersect(clip)) + { + if (pos.width > 0 && pos.height > 0) { + background_paint bg; + bg.image = m_src; + bg.clip_box = pos; + bg.origin_box = pos; + bg.border_box = pos; + bg.border_box += m_padding; + bg.border_box += m_borders; + bg.repeat = background_repeat_no_repeat; + bg.image_size.width = pos.width; + bg.image_size.height = pos.height; + bg.border_radius = m_css_borders.radius.calc_percents(bg.border_box.width, bg.border_box.height); + bg.position_x = pos.x; + bg.position_y = pos.y; + get_document()->container()->draw_background(hdc, bg); + } + } + + // draw borders + if (el_pos.does_intersect(clip)) + { + position border_box = pos; + border_box += m_padding; + border_box += m_borders; + + borders bdr = m_css_borders; + bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); + + get_document()->container()->draw_borders(hdc, bdr, border_box, !have_parent()); + } +} + +void litehtml::el_image::parse_styles( bool is_reparse /*= false*/ ) +{ + html_tag::parse_styles(is_reparse); + + if(!m_src.empty()) + { + if(!m_css_height.is_predefined() && !m_css_width.is_predefined()) + { + get_document()->container()->load_image(m_src.c_str(), nullptr, true); + } else + { + get_document()->container()->load_image(m_src.c_str(), nullptr, false); + } + } +} diff --git a/gb.form.htmlview/src/litehtml/el_image.h b/gb.form.htmlview/src/litehtml/el_image.h new file mode 100644 index 00000000..fa13d3c1 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_image.h @@ -0,0 +1,28 @@ +#ifndef LH_EL_IMAGE_H +#define LH_EL_IMAGE_H + +#include "html_tag.h" + +namespace litehtml +{ + + class el_image : public html_tag + { + tstring m_src; + public: + el_image(const std::shared_ptr<litehtml::document>& doc); + virtual ~el_image(void); + + virtual int line_height() const override; + virtual bool is_replaced() const override; + virtual int render(int x, int y, int max_width, bool second_pass = false) override; + virtual void parse_attributes() override; + virtual void parse_styles(bool is_reparse = false) override; + virtual void draw(uint_ptr hdc, int x, int y, const position* clip) override; + virtual void get_content_size(size& sz, int max_width) override; + private: + int calc_max_height(int image_height); + }; +} + +#endif // LH_EL_IMAGE_H diff --git a/gb.form.htmlview/src/litehtml/el_li.cpp b/gb.form.htmlview/src/litehtml/el_li.cpp new file mode 100644 index 00000000..02b7d4e7 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_li.cpp @@ -0,0 +1,35 @@ +#include "html.h" +#include "el_li.h" +#include "document.h" + +litehtml::el_li::el_li(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) +{ +} + +int litehtml::el_li::render(int x, int y, int max_width, bool second_pass) +{ + if (m_list_style_type >= list_style_type_armenian && !m_index_initialized) + { + if (auto p = parent()) + { + const auto hasStart = p->get_attr(_t("start")); + const int start = hasStart ? t_atoi(hasStart) : 1; + tchar_t val[2] = { (tchar_t)start, 0 }; + for (int i = 0, n = (int)p->get_children_count(); i < n; ++i) + { + auto child = p->get_child(i); + if (child.get() == this) + { + set_attr(_t("list_index"), val); + break; + } + else if (!t_strcmp(child->get_tagName(), _t("li"))) + ++val[0]; + } + } + + m_index_initialized = true; + } + + return html_tag::render(x, y, max_width, second_pass); +} diff --git a/gb.form.htmlview/src/litehtml/el_li.h b/gb.form.htmlview/src/litehtml/el_li.h new file mode 100644 index 00000000..4500465b --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_li.h @@ -0,0 +1,20 @@ +#ifndef LH_EL_LI_H +#define LH_EL_LI_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_li : public html_tag + { + public: + explicit el_li(const std::shared_ptr<litehtml::document>& doc); + + int render(int x, int y, int max_width, bool second_pass = false) override; + + private: + bool m_index_initialized = false; + }; +} + +#endif // LH_EL_LI_H diff --git a/gb.form.htmlview/src/litehtml/el_link.cpp b/gb.form.htmlview/src/litehtml/el_link.cpp new file mode 100644 index 00000000..39ee5498 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_link.cpp @@ -0,0 +1,39 @@ +#include "html.h" +#include "el_link.h" +#include "document.h" + + +litehtml::el_link::el_link(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) +{ + +} + +void litehtml::el_link::parse_attributes() +{ + bool processed = false; + + document::ptr doc = get_document(); + + const tchar_t* rel = get_attr(_t("rel")); + if(rel && !t_strcmp(rel, _t("stylesheet"))) + { + const tchar_t* media = get_attr(_t("media")); + const tchar_t* href = get_attr(_t("href")); + if(href && href[0]) + { + tstring css_text; + tstring css_baseurl; + doc->container()->import_css(css_text, href, css_baseurl); + if(!css_text.empty()) + { + doc->add_stylesheet(css_text.c_str(), css_baseurl.c_str(), media); + processed = true; + } + } + } + + if(!processed) + { + doc->container()->link(doc, shared_from_this()); + } +} diff --git a/gb.form.htmlview/src/litehtml/el_link.h b/gb.form.htmlview/src/litehtml/el_link.h new file mode 100644 index 00000000..0da3513d --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_link.h @@ -0,0 +1,18 @@ +#ifndef LH_EL_LINK_H +#define LH_EL_LINK_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_link : public html_tag + { + public: + explicit el_link(const std::shared_ptr<litehtml::document>& doc); + + protected: + void parse_attributes() override; + }; +} + +#endif // LH_EL_LINK_H diff --git a/gb.form.htmlview/src/litehtml/el_para.cpp b/gb.form.htmlview/src/litehtml/el_para.cpp new file mode 100644 index 00000000..2789f387 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_para.cpp @@ -0,0 +1,18 @@ +#include "html.h" +#include "el_para.h" +#include "document.h" + +litehtml::el_para::el_para(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) +{ +} + +void litehtml::el_para::parse_attributes() +{ + const tchar_t* str = get_attr(_t("align")); + if(str) + { + m_style.add_property(_t("text-align"), str, nullptr, false, this); + } + + html_tag::parse_attributes(); +} diff --git a/gb.form.htmlview/src/litehtml/el_para.h b/gb.form.htmlview/src/litehtml/el_para.h new file mode 100644 index 00000000..32ad5375 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_para.h @@ -0,0 +1,18 @@ +#ifndef LH_EL_PARA_H +#define LH_EL_PARA_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_para : public html_tag + { + public: + explicit el_para(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + + }; +} + +#endif // LH_EL_PARA_H diff --git a/gb.form.htmlview/src/litehtml/el_script.cpp b/gb.form.htmlview/src/litehtml/el_script.cpp new file mode 100644 index 00000000..608b48da --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_script.cpp @@ -0,0 +1,25 @@ +#include "html.h" +#include "el_script.h" +#include "document.h" + + +litehtml::el_script::el_script(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +{ + +} + +void litehtml::el_script::parse_attributes() +{ + //TODO: pass script text to document container +} + +bool litehtml::el_script::appendChild(const ptr &el) +{ + el->get_text(m_text); + return true; +} + +const litehtml::tchar_t* litehtml::el_script::get_tagName() const +{ + return _t("script"); +} diff --git a/gb.form.htmlview/src/litehtml/el_script.h b/gb.form.htmlview/src/litehtml/el_script.h new file mode 100644 index 00000000..66767957 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_script.h @@ -0,0 +1,20 @@ +#ifndef LH_EL_SCRIPT_H +#define LH_EL_SCRIPT_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_script : public element + { + tstring m_text; + public: + explicit el_script(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + bool appendChild(const ptr &el) override; + const tchar_t* get_tagName() const override; + }; +} + +#endif // LH_EL_SCRIPT_H diff --git a/gb.form.htmlview/src/litehtml/el_space.cpp b/gb.form.htmlview/src/litehtml/el_space.cpp new file mode 100644 index 00000000..3344b421 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_space.cpp @@ -0,0 +1,40 @@ +#include "html.h" +#include "document.h" +#include "el_space.h" + +litehtml::el_space::el_space(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc) : el_text(text, doc) +{ +} + +bool litehtml::el_space::is_white_space() const +{ + white_space ws = get_white_space(); + if( ws == white_space_normal || + ws == white_space_nowrap || + ws == white_space_pre_line ) + { + return true; + } + return false; +} + +bool litehtml::el_space::is_break() const +{ + white_space ws = get_white_space(); + if( ws == white_space_pre || + ws == white_space_pre_line || + ws == white_space_pre_wrap) + { + if(m_text == _t("\n")) + { + return true; + } + } + return false; +} + +bool litehtml::el_space::is_space() const +{ + return true; +} + diff --git a/gb.form.htmlview/src/litehtml/el_space.h b/gb.form.htmlview/src/litehtml/el_space.h new file mode 100644 index 00000000..e1c81b4f --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_space.h @@ -0,0 +1,20 @@ +#ifndef LH_EL_SPACE_H +#define LH_EL_SPACE_H + +#include "html_tag.h" +#include "el_text.h" + +namespace litehtml +{ + class el_space : public el_text + { + public: + el_space(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc); + + bool is_white_space() const override; + bool is_break() const override; + bool is_space() const override; + }; +} + +#endif // LH_EL_SPACE_H diff --git a/gb.form.htmlview/src/litehtml/el_style.cpp b/gb.form.htmlview/src/litehtml/el_style.cpp new file mode 100644 index 00000000..372c12eb --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_style.cpp @@ -0,0 +1,31 @@ +#include "html.h" +#include "el_style.h" +#include "document.h" + + +litehtml::el_style::el_style(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +{ + +} + +void litehtml::el_style::parse_attributes() +{ + tstring text; + + for(auto& el : m_children) + { + el->get_text(text); + } + get_document()->add_stylesheet( text.c_str(), nullptr, get_attr(_t("media")) ); +} + +bool litehtml::el_style::appendChild(const ptr &el) +{ + m_children.push_back(el); + return true; +} + +const litehtml::tchar_t* litehtml::el_style::get_tagName() const +{ + return _t("style"); +} diff --git a/gb.form.htmlview/src/litehtml/el_style.h b/gb.form.htmlview/src/litehtml/el_style.h new file mode 100644 index 00000000..63026880 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_style.h @@ -0,0 +1,20 @@ +#ifndef LH_EL_STYLE_H +#define LH_EL_STYLE_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_style : public element + { + elements_vector m_children; + public: + explicit el_style(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + bool appendChild(const ptr &el) override; + const tchar_t* get_tagName() const override; + }; +} + +#endif // LH_EL_STYLE_H diff --git a/gb.form.htmlview/src/litehtml/el_table.cpp b/gb.form.htmlview/src/litehtml/el_table.cpp new file mode 100644 index 00000000..fef46ed4 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_table.cpp @@ -0,0 +1,105 @@ +#include "html.h" +#include "el_table.h" +#include "document.h" +#include "iterators.h" + + +litehtml::el_table::el_table(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + m_border_spacing_x = 0; + m_border_spacing_y = 0; + m_border_collapse = border_collapse_separate; +} + + +bool litehtml::el_table::appendChild(const litehtml::element::ptr& el) +{ + if(!el) return false; + if( !t_strcmp(el->get_tagName(), _t("tbody")) || + !t_strcmp(el->get_tagName(), _t("thead")) || + !t_strcmp(el->get_tagName(), _t("tfoot")) || + !t_strcmp(el->get_tagName(), _t("caption"))) + { + return html_tag::appendChild(el); + } + return false; +} + +void litehtml::el_table::parse_styles(bool is_reparse) +{ + html_tag::parse_styles(is_reparse); + + m_border_collapse = (border_collapse) value_index(get_style_property(_t("border-collapse"), true, _t("separate")), border_collapse_strings, border_collapse_separate); + + if(m_border_collapse == border_collapse_separate) + { + m_css_border_spacing_x.fromString(get_style_property(_t("-litehtml-border-spacing-x"), true, _t("0px"))); + m_css_border_spacing_y.fromString(get_style_property(_t("-litehtml-border-spacing-y"), true, _t("0px"))); + + int fntsz = get_font_size(); + document::ptr doc = get_document(); + m_border_spacing_x = doc->cvt_units(m_css_border_spacing_x, fntsz); + m_border_spacing_y = doc->cvt_units(m_css_border_spacing_y, fntsz); + } else + { + m_border_spacing_x = 0; + m_border_spacing_y = 0; + m_padding.bottom = 0; + m_padding.top = 0; + m_padding.left = 0; + m_padding.right = 0; + m_css_padding.bottom.set_value(0, css_units_px); + m_css_padding.top.set_value(0, css_units_px); + m_css_padding.left.set_value(0, css_units_px); + m_css_padding.right.set_value(0, css_units_px); + } +} + +void litehtml::el_table::parse_attributes() +{ + const tchar_t* str = get_attr(_t("width")); + if(str) + { + m_style.add_property(_t("width"), str, nullptr, false, this); + } + + str = get_attr(_t("align")); + if(str) + { + int align = value_index(str, _t("left;center;right")); + switch(align) + { + case 1: + m_style.add_property(_t("margin-left"), _t("auto"), nullptr, false, this); + m_style.add_property(_t("margin-right"), _t("auto"), nullptr, false, this); + break; + case 2: + m_style.add_property(_t("margin-left"), _t("auto"), nullptr, false, this); + m_style.add_property(_t("margin-right"), _t("0"), nullptr, false, this); + break; + } + } + + str = get_attr(_t("cellspacing")); + if(str) + { + tstring val = str; + val += _t(" "); + val += str; + m_style.add_property(_t("border-spacing"), val.c_str(), nullptr, false, this); + } + + str = get_attr(_t("border")); + if(str) + { + m_style.add_property(_t("border-width"), str, nullptr, false, this); + } + + str = get_attr(_t("bgcolor")); + if (str) + { + m_style.add_property(_t("background-color"), str, nullptr, false, this); + } + + html_tag::parse_attributes(); +} diff --git a/gb.form.htmlview/src/litehtml/el_table.h b/gb.form.htmlview/src/litehtml/el_table.h new file mode 100644 index 00000000..ea975214 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_table.h @@ -0,0 +1,26 @@ +#ifndef LH_EL_TABLE_H +#define LH_EL_TABLE_H + +#include "html_tag.h" + +namespace litehtml +{ + struct col_info + { + int width; + bool is_auto; + }; + + + class el_table : public html_tag + { + public: + explicit el_table(const std::shared_ptr<litehtml::document>& doc); + + bool appendChild(const litehtml::element::ptr& el) override; + void parse_styles(bool is_reparse = false) override; + void parse_attributes() override; + }; +} + +#endif // LH_EL_TABLE_H diff --git a/gb.form.htmlview/src/litehtml/el_td.cpp b/gb.form.htmlview/src/litehtml/el_td.cpp new file mode 100644 index 00000000..4a92bd0a --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_td.cpp @@ -0,0 +1,44 @@ +#include "html.h" +#include "el_td.h" + + +litehtml::el_td::el_td(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + +} + +void litehtml::el_td::parse_attributes() +{ + const tchar_t* str = get_attr(_t("width")); + if(str) + { + m_style.add_property(_t("width"), str, nullptr, false, this); + } + str = get_attr(_t("background")); + if(str) + { + tstring url = _t("url('"); + url += str; + url += _t("')"); + m_style.add_property(_t("background-image"), url.c_str(), nullptr, false, this); + } + str = get_attr(_t("align")); + if(str) + { + m_style.add_property(_t("text-align"), str, nullptr, false, this); + } + + str = get_attr(_t("bgcolor")); + if (str) + { + m_style.add_property(_t("background-color"), str, nullptr, false, this); + } + + str = get_attr(_t("valign")); + if(str) + { + m_style.add_property(_t("vertical-align"), str, nullptr, false, this); + } + html_tag::parse_attributes(); +} + diff --git a/gb.form.htmlview/src/litehtml/el_td.h b/gb.form.htmlview/src/litehtml/el_td.h new file mode 100644 index 00000000..03d21c1c --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_td.h @@ -0,0 +1,17 @@ +#ifndef LH_EL_TD_H +#define LH_EL_TD_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_td : public html_tag + { + public: + explicit el_td(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + }; +} + +#endif // LH_EL_TD_H diff --git a/gb.form.htmlview/src/litehtml/el_text.cpp b/gb.form.htmlview/src/litehtml/el_text.cpp new file mode 100644 index 00000000..07680434 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_text.cpp @@ -0,0 +1,183 @@ +#include "html.h" +#include "el_text.h" +#include "document.h" + +litehtml::el_text::el_text(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc) : element(doc) +{ + if(text) + { + m_text = text; + } + m_text_transform = text_transform_none; + m_use_transformed = false; + m_draw_spaces = true; +} + +void litehtml::el_text::get_content_size( size& sz, int max_width ) +{ + sz = m_size; +} + +void litehtml::el_text::get_text( tstring& text ) +{ + text += m_text; +} + +const litehtml::tchar_t* litehtml::el_text::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ ) const +{ + if(inherited) + { + element::ptr el_parent = parent(); + if (el_parent) + { + return el_parent->get_style_property(name, inherited, def); + } + } + return def; +} + +void litehtml::el_text::parse_styles(bool is_reparse) +{ + m_text_transform = (text_transform) value_index(get_style_property(_t("text-transform"), true, _t("none")), text_transform_strings, text_transform_none); + if(m_text_transform != text_transform_none) + { + m_transformed_text = m_text; + m_use_transformed = true; + get_document()->container()->transform_text(m_transformed_text, m_text_transform); + } + + if(is_white_space()) + { + m_transformed_text = _t(" "); + m_use_transformed = true; + } else + { + if(m_text == _t("\t")) + { + m_transformed_text = _t(" "); + m_use_transformed = true; + } + if(m_text == _t("\n") || m_text == _t("\r")) + { + m_transformed_text = _t(""); + m_use_transformed = true; + } + } + + font_metrics fm; + uint_ptr font = 0; + element::ptr el_parent = parent(); + if (el_parent) + { + font = el_parent->get_font(&fm); + } + if(is_break()) + { + m_size.height = 0; + m_size.width = 0; + } else + { + m_size.height = fm.height; + m_size.width = get_document()->container()->text_width(m_use_transformed ? m_transformed_text.c_str() : m_text.c_str(), font); + } + m_draw_spaces = fm.draw_spaces; +} + +int litehtml::el_text::get_base_line() +{ + element::ptr el_parent = parent(); + if (el_parent) + { + return el_parent->get_base_line(); + } + return 0; +} + +void litehtml::el_text::draw( uint_ptr hdc, int x, int y, const position* clip ) +{ + if(is_white_space() && !m_draw_spaces) + { + return; + } + + position pos = m_pos; + pos.x += x; + pos.y += y; + + if(pos.does_intersect(clip)) + { + element::ptr el_parent = parent(); + if (el_parent) + { + document::ptr doc = get_document(); + + uint_ptr font = el_parent->get_font(); + litehtml::web_color color = el_parent->get_color(_t("color"), true, doc->get_def_color()); + doc->container()->draw_text(hdc, m_use_transformed ? m_transformed_text.c_str() : m_text.c_str(), font, color, pos); + } + } +} + +int litehtml::el_text::line_height() const +{ + element::ptr el_parent = parent(); + if (el_parent) + { + return el_parent->line_height(); + } + return 0; +} + +litehtml::uint_ptr litehtml::el_text::get_font( font_metrics* fm /*= 0*/ ) +{ + element::ptr el_parent = parent(); + if (el_parent) + { + return el_parent->get_font(fm); + } + return 0; +} + +litehtml::style_display litehtml::el_text::get_display() const +{ + return display_inline_text; +} + +litehtml::white_space litehtml::el_text::get_white_space() const +{ + element::ptr el_parent = parent(); + if (el_parent) return el_parent->get_white_space(); + return white_space_normal; +} + +litehtml::element_position litehtml::el_text::get_element_position(css_offsets* offsets) const +{ + element::ptr p = parent(); + while(p && p->get_display() == display_inline) + { + if(p->get_element_position() == element_position_relative) + { + if(offsets) + { + *offsets = p->get_css_offsets(); + } + return element_position_relative; + } + p = p->parent(); + } + return element_position_static; +} + +litehtml::css_offsets litehtml::el_text::get_css_offsets() const +{ + element::ptr p = parent(); + while(p && p->get_display() == display_inline) + { + if(p->get_element_position() == element_position_relative) + { + return p->get_css_offsets(); + } + p = p->parent(); + } + return {}; +} diff --git a/gb.form.htmlview/src/litehtml/el_text.h b/gb.form.htmlview/src/litehtml/el_text.h new file mode 100644 index 00000000..c53db7f7 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_text.h @@ -0,0 +1,37 @@ +#ifndef LH_EL_TEXT_H +#define LH_EL_TEXT_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_text : public element + { + protected: + tstring m_text; + tstring m_transformed_text; + size m_size; + text_transform m_text_transform; + bool m_use_transformed; + bool m_draw_spaces; + public: + el_text(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc); + + void get_text(tstring& text) override; + const tchar_t* get_style_property(const tchar_t* name, bool inherited, const tchar_t* def = nullptr) const override; + void parse_styles(bool is_reparse) override; + int get_base_line() override; + void draw(uint_ptr hdc, int x, int y, const position* clip) override; + int line_height() const override; + uint_ptr get_font(font_metrics* fm = nullptr) override; + style_display get_display() const override; + white_space get_white_space() const override; + element_position get_element_position(css_offsets* offsets = nullptr) const override; + css_offsets get_css_offsets() const override; + + protected: + void get_content_size(size& sz, int max_width) override; + }; +} + +#endif // LH_EL_TEXT_H diff --git a/gb.form.htmlview/src/litehtml/el_title.cpp b/gb.form.htmlview/src/litehtml/el_title.cpp new file mode 100644 index 00000000..ac7af863 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_title.cpp @@ -0,0 +1,15 @@ +#include "html.h" +#include "el_title.h" +#include "document.h" + +litehtml::el_title::el_title(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) +{ + +} + +void litehtml::el_title::parse_attributes() +{ + tstring text; + get_text(text); + get_document()->container()->set_caption(text.c_str()); +} diff --git a/gb.form.htmlview/src/litehtml/el_title.h b/gb.form.htmlview/src/litehtml/el_title.h new file mode 100644 index 00000000..201186a4 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_title.h @@ -0,0 +1,18 @@ +#ifndef LH_EL_TITLE_H +#define LH_EL_TITLE_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_title : public html_tag + { + public: + explicit el_title(const std::shared_ptr<litehtml::document>& doc); + + protected: + void parse_attributes() override; + }; +} + +#endif // LH_EL_TITLE_H diff --git a/gb.form.htmlview/src/litehtml/el_tr.cpp b/gb.form.htmlview/src/litehtml/el_tr.cpp new file mode 100644 index 00000000..6ea62719 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_tr.cpp @@ -0,0 +1,46 @@ +#include "html.h" +#include "el_tr.h" + + +litehtml::el_tr::el_tr(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +{ + +} + +void litehtml::el_tr::parse_attributes() +{ + const tchar_t* str = get_attr(_t("align")); + if(str) + { + m_style.add_property(_t("text-align"), str, nullptr, false, this); + } + str = get_attr(_t("valign")); + if(str) + { + m_style.add_property(_t("vertical-align"), str, nullptr, false, this); + } + str = get_attr(_t("bgcolor")); + if (str) + { + m_style.add_property(_t("background-color"), str, nullptr, false, this); + } + html_tag::parse_attributes(); +} + +void litehtml::el_tr::get_inline_boxes( position::vector& boxes ) +{ + position pos; + for(auto& el : m_children) + { + if(el->get_display() == display_table_cell) + { + pos.x = el->left() + el->margin_left(); + pos.y = el->top() - m_padding.top - m_borders.top; + + pos.width = el->right() - pos.x - el->margin_right() - el->margin_left(); + pos.height = el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom; + + boxes.push_back(pos); + } + } +} diff --git a/gb.form.htmlview/src/litehtml/el_tr.h b/gb.form.htmlview/src/litehtml/el_tr.h new file mode 100644 index 00000000..40a2f703 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/el_tr.h @@ -0,0 +1,18 @@ +#ifndef LH_EL_TR_H +#define LH_EL_TR_H + +#include "html_tag.h" + +namespace litehtml +{ + class el_tr : public html_tag + { + public: + explicit el_tr(const std::shared_ptr<litehtml::document>& doc); + + void parse_attributes() override; + void get_inline_boxes(position::vector& boxes) override; + }; +} + +#endif // LH_EL_TR_H diff --git a/gb.form.htmlview/src/litehtml/element.cpp b/gb.form.htmlview/src/litehtml/element.cpp new file mode 100644 index 00000000..4c2d11f4 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/element.cpp @@ -0,0 +1,411 @@ +#include "html.h" +#include "element.h" +#include "document.h" + +#define LITEHTML_EMPTY_FUNC {} +#define LITEHTML_RETURN_FUNC(ret) {return ret;} + +litehtml::element::element(const std::shared_ptr<litehtml::document>& doc) : m_doc(doc) +{ + m_box = nullptr; + m_skip = false; +} + +bool litehtml::element::is_point_inside( int x, int y ) +{ + if(get_display() != display_inline && get_display() != display_table_row) + { + position pos = m_pos; + pos += m_padding; + pos += m_borders; + if(pos.is_point_inside(x, y)) + { + return true; + } else + { + return false; + } + } else + { + position::vector boxes; + get_inline_boxes(boxes); + for(auto & box : boxes) + { + if(box.is_point_inside(x, y)) + { + return true; + } + } + } + return false; +} + +litehtml::web_color litehtml::element::get_color( const tchar_t* prop_name, bool inherited, const litehtml::web_color& def_color ) +{ + const tchar_t* clrstr = get_style_property(prop_name, inherited, nullptr); + if(!clrstr) + { + return def_color; + } + return web_color::from_string(clrstr, get_document()->container()); +} + +litehtml::position litehtml::element::get_placement() const +{ + litehtml::position pos = m_pos; + element::ptr cur_el = parent(); + while(cur_el) + { + pos.x += cur_el->m_pos.x; + pos.y += cur_el->m_pos.y; + cur_el = cur_el->parent(); + } + return pos; +} + +bool litehtml::element::is_inline_box() const +{ + style_display d = get_display(); + if( d == display_inline || + d == display_inline_table || + d == display_inline_block || + d == display_inline_text) + { + return true; + } + return false; +} + +bool litehtml::element::collapse_top_margin() const +{ + if(!m_borders.top && !m_padding.top && in_normal_flow() && get_float() == float_none && m_margins.top >= 0 && have_parent()) + { + return true; + } + return false; +} + +bool litehtml::element::collapse_bottom_margin() const +{ + if(!m_borders.bottom && !m_padding.bottom && in_normal_flow() && get_float() == float_none && m_margins.bottom >= 0 && have_parent()) + { + return true; + } + return false; +} + +bool litehtml::element::get_predefined_height(int& p_height) const +{ + css_length h = get_css_height(); + if(h.is_predefined()) + { + p_height = m_pos.height; + return false; + } + if(h.units() == css_units_percentage) + { + element::ptr el_parent = parent(); + if (!el_parent) + { + position client_pos; + get_document()->container()->get_client_rect(client_pos); + p_height = h.calc_percent(client_pos.height); + return true; + } else + { + int ph = 0; + if (el_parent->get_predefined_height(ph)) + { + p_height = h.calc_percent(ph); + if (is_body()) + { + p_height -= content_margins_height(); + } + return true; + } else + { + p_height = m_pos.height; + return false; + } + } + } + p_height = get_document()->cvt_units(h, get_font_size()); + return true; +} + +void litehtml::element::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ ) +{ + if(is_visible()) + { + sz.width = std::max(sz.width, x + right()); + sz.height = std::max(sz.height, y + bottom()); + } +} + +void litehtml::element::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/) +{ + if(is_visible()) + { + int p_left = std::min(pos.left(), x + m_pos.left() - m_padding.left - m_borders.left); + int p_right = std::max(pos.right(), x + m_pos.right() + m_padding.left + m_borders.left); + int p_top = std::min(pos.top(), y + m_pos.top() - m_padding.top - m_borders.top); + int p_bottom = std::max(pos.bottom(), y + m_pos.bottom() + m_padding.bottom + m_borders.bottom); + + pos.x = p_left; + pos.y = p_top; + pos.width = p_right - p_left; + pos.height = p_bottom - p_top; + } +} + +int litehtml::element::calc_width(int defVal) const +{ + css_length w = get_css_width(); + if(w.is_predefined() || get_display() == display_table_cell) + { + return defVal; + } + if(w.units() == css_units_percentage) + { + element::ptr el_parent = parent(); + if (!el_parent) + { + position client_pos; + get_document()->container()->get_client_rect(client_pos); + return w.calc_percent(client_pos.width) - content_margins_width(); + } else + { + int pw = el_parent->calc_width(defVal); + if (is_body()) + { + pw -= content_margins_width(); + } + return w.calc_percent(pw); + } + } + return get_document()->cvt_units(w, get_font_size()); +} + +bool litehtml::element::is_ancestor(const ptr &el) const +{ + element::ptr el_parent = parent(); + while(el_parent && el_parent != el) + { + el_parent = el_parent->parent(); + } + if(el_parent) + { + return true; + } + return false; +} + +int litehtml::element::get_inline_shift_left() +{ + int ret = 0; + element::ptr el_parent = parent(); + if (el_parent) + { + if (el_parent->get_display() == display_inline) + { + style_display disp = get_display(); + + if (disp == display_inline_text || disp == display_inline_block) + { + element::ptr el = shared_from_this(); + while (el_parent && el_parent->get_display() == display_inline) + { + if (el_parent->is_first_child_inline(el)) + { + ret += el_parent->padding_left() + el_parent->border_left() + el_parent->margin_left(); + } + el = el_parent; + el_parent = el_parent->parent(); + } + } + } + } + + return ret; +} + +int litehtml::element::get_inline_shift_right() +{ + int ret = 0; + element::ptr el_parent = parent(); + if (el_parent) + { + if (el_parent->get_display() == display_inline) + { + style_display disp = get_display(); + + if (disp == display_inline_text || disp == display_inline_block) + { + element::ptr el = shared_from_this(); + while (el_parent && el_parent->get_display() == display_inline) + { + if (el_parent->is_last_child_inline(el)) + { + ret += el_parent->padding_right() + el_parent->border_right() + el_parent->margin_right(); + } + el = el_parent; + el_parent = el_parent->parent(); + } + } + } + } + + return ret; +} + +void litehtml::element::apply_relative_shift(int parent_width) +{ + css_offsets offsets; + if (get_element_position(&offsets) == element_position_relative) + { + element::ptr parent_ptr = parent(); + if (!offsets.left.is_predefined()) + { + m_pos.x += offsets.left.calc_percent(parent_width); + } + else if (!offsets.right.is_predefined()) + { + m_pos.x -= offsets.right.calc_percent(parent_width); + } + if (!offsets.top.is_predefined()) + { + int h = 0; + + if (offsets.top.units() == css_units_percentage) + { + element::ptr el_parent = parent(); + if (el_parent) + { + el_parent->get_predefined_height(h); + } + } + + m_pos.y += offsets.top.calc_percent(h); + } + else if (!offsets.bottom.is_predefined()) + { + int h = 0; + + if (offsets.top.units() == css_units_percentage) + { + element::ptr el_parent = parent(); + if (el_parent) + { + el_parent->get_predefined_height(h); + } + } + + m_pos.y -= offsets.bottom.calc_percent(h); + } + } +} + +bool litehtml::element::is_table_skip() const +{ + return is_space() || is_comment() || get_display() == display_none; +} + +void litehtml::element::calc_auto_margins(int parent_width) LITEHTML_EMPTY_FUNC +const litehtml::background* litehtml::element::get_background(bool own_only) LITEHTML_RETURN_FUNC(nullptr) +litehtml::element::ptr litehtml::element::get_element_by_point(int x, int y, int client_x, int client_y) LITEHTML_RETURN_FUNC(nullptr) +litehtml::element::ptr litehtml::element::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex) LITEHTML_RETURN_FUNC(nullptr) +void litehtml::element::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right ) LITEHTML_EMPTY_FUNC +void litehtml::element::add_style( const tstring& style, const tstring& baseurl ) LITEHTML_EMPTY_FUNC +void litehtml::element::select_all(const css_selector& selector, litehtml::elements_vector& res) LITEHTML_EMPTY_FUNC +litehtml::elements_vector litehtml::element::select_all(const litehtml::css_selector& selector) LITEHTML_RETURN_FUNC(litehtml::elements_vector()) +litehtml::elements_vector litehtml::element::select_all(const litehtml::tstring& selector) LITEHTML_RETURN_FUNC(litehtml::elements_vector()) +litehtml::element::ptr litehtml::element::select_one( const css_selector& selector ) LITEHTML_RETURN_FUNC(nullptr) +litehtml::element::ptr litehtml::element::select_one( const tstring& selector ) LITEHTML_RETURN_FUNC(nullptr) +litehtml::element::ptr litehtml::element::find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) +litehtml::element::ptr litehtml::element::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) +bool litehtml::element::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) +bool litehtml::element::is_nth_child(const element::ptr&, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) +bool litehtml::element::is_only_child(const element::ptr& el, bool of_type) const LITEHTML_RETURN_FUNC(false) +litehtml::overflow litehtml::element::get_overflow() const LITEHTML_RETURN_FUNC(overflow_visible) +void litehtml::element::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ) LITEHTML_EMPTY_FUNC +void litehtml::element::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ) LITEHTML_EMPTY_FUNC +void litehtml::element::render_positioned(render_type rt) LITEHTML_EMPTY_FUNC +int litehtml::element::get_zindex() const LITEHTML_RETURN_FUNC(0) +bool litehtml::element::fetch_positioned() LITEHTML_RETURN_FUNC(false) +litehtml::visibility litehtml::element::get_visibility() const LITEHTML_RETURN_FUNC(visibility_visible) +void litehtml::element::apply_vertical_align() LITEHTML_EMPTY_FUNC +void litehtml::element::set_css_width( css_length& w ) LITEHTML_EMPTY_FUNC +litehtml::element::ptr litehtml::element::get_child( int idx ) const LITEHTML_RETURN_FUNC(nullptr) +size_t litehtml::element::get_children_count() const LITEHTML_RETURN_FUNC(0) +void litehtml::element::calc_outlines( int parent_width ) LITEHTML_EMPTY_FUNC +litehtml::css_length litehtml::element::get_css_width() const LITEHTML_RETURN_FUNC(css_length()) +litehtml::css_length litehtml::element::get_css_height() const LITEHTML_RETURN_FUNC(css_length()) +litehtml::element_clear litehtml::element::get_clear() const LITEHTML_RETURN_FUNC(clear_none) +litehtml::css_length litehtml::element::get_css_left() const LITEHTML_RETURN_FUNC(css_length()) +litehtml::css_length litehtml::element::get_css_right() const LITEHTML_RETURN_FUNC(css_length()) +litehtml::css_length litehtml::element::get_css_top() const LITEHTML_RETURN_FUNC(css_length()) +litehtml::css_length litehtml::element::get_css_bottom() const LITEHTML_RETURN_FUNC(css_length()) +litehtml::css_offsets litehtml::element::get_css_offsets() const LITEHTML_RETURN_FUNC(css_offsets()) +litehtml::vertical_align litehtml::element::get_vertical_align() const LITEHTML_RETURN_FUNC(va_baseline) +int litehtml::element::place_element(const ptr &el, int max_width) LITEHTML_RETURN_FUNC(0) +int litehtml::element::render_inline(const ptr &container, int max_width) LITEHTML_RETURN_FUNC(0) +void litehtml::element::add_positioned(const ptr &el) LITEHTML_EMPTY_FUNC +int litehtml::element::find_next_line_top( int top, int width, int def_right ) LITEHTML_RETURN_FUNC(0) +litehtml::element_float litehtml::element::get_float() const LITEHTML_RETURN_FUNC(float_none) +void litehtml::element::add_float(const ptr &el, int x, int y) LITEHTML_EMPTY_FUNC +void litehtml::element::update_floats(int dy, const ptr &parent) LITEHTML_EMPTY_FUNC +int litehtml::element::get_line_left( int y ) LITEHTML_RETURN_FUNC(0) +int litehtml::element::get_line_right( int y, int def_right ) LITEHTML_RETURN_FUNC(def_right) +int litehtml::element::get_left_floats_height() const LITEHTML_RETURN_FUNC(0) +int litehtml::element::get_right_floats_height() const LITEHTML_RETURN_FUNC(0) +int litehtml::element::get_floats_height(element_float el_float) const LITEHTML_RETURN_FUNC(0) +bool litehtml::element::is_floats_holder() const LITEHTML_RETURN_FUNC(false) +void litehtml::element::get_content_size( size& sz, int max_width ) LITEHTML_EMPTY_FUNC +void litehtml::element::init() LITEHTML_EMPTY_FUNC +int litehtml::element::render( int x, int y, int max_width, bool second_pass ) LITEHTML_RETURN_FUNC(0) +bool litehtml::element::appendChild(const ptr &el) LITEHTML_RETURN_FUNC(false) +bool litehtml::element::removeChild(const ptr &el) LITEHTML_RETURN_FUNC(false) +void litehtml::element::clearRecursive() LITEHTML_EMPTY_FUNC +const litehtml::tchar_t* litehtml::element::get_tagName() const LITEHTML_RETURN_FUNC(_t("")) +void litehtml::element::set_tagName( const tchar_t* tag ) LITEHTML_EMPTY_FUNC +void litehtml::element::set_data( const tchar_t* data ) LITEHTML_EMPTY_FUNC +void litehtml::element::set_attr( const tchar_t* name, const tchar_t* val ) LITEHTML_EMPTY_FUNC +void litehtml::element::apply_stylesheet( const litehtml::css& stylesheet ) LITEHTML_EMPTY_FUNC +void litehtml::element::refresh_styles() LITEHTML_EMPTY_FUNC +void litehtml::element::on_click() LITEHTML_EMPTY_FUNC +void litehtml::element::init_font() LITEHTML_EMPTY_FUNC +void litehtml::element::get_inline_boxes( position::vector& boxes ) LITEHTML_EMPTY_FUNC +void litehtml::element::parse_styles( bool is_reparse /*= false*/ ) LITEHTML_EMPTY_FUNC +const litehtml::tchar_t* litehtml::element::get_attr( const tchar_t* name, const tchar_t* def /*= 0*/ ) const LITEHTML_RETURN_FUNC(def) +bool litehtml::element::is_white_space() const LITEHTML_RETURN_FUNC(false) +bool litehtml::element::is_space() const LITEHTML_RETURN_FUNC(false) +bool litehtml::element::is_comment() const LITEHTML_RETURN_FUNC(false) +bool litehtml::element::is_body() const LITEHTML_RETURN_FUNC(false) +bool litehtml::element::is_break() const LITEHTML_RETURN_FUNC(false) +int litehtml::element::get_base_line() LITEHTML_RETURN_FUNC(0) +bool litehtml::element::on_mouse_over() LITEHTML_RETURN_FUNC(false) +bool litehtml::element::on_mouse_leave() LITEHTML_RETURN_FUNC(false) +bool litehtml::element::on_lbutton_down() LITEHTML_RETURN_FUNC(false) +bool litehtml::element::on_lbutton_up() LITEHTML_RETURN_FUNC(false) +bool litehtml::element::find_styles_changes( position::vector& redraw_boxes, int x, int y ) LITEHTML_RETURN_FUNC(false) +const litehtml::tchar_t* litehtml::element::get_cursor() LITEHTML_RETURN_FUNC(nullptr) +litehtml::white_space litehtml::element::get_white_space() const LITEHTML_RETURN_FUNC(white_space_normal) +litehtml::style_display litehtml::element::get_display() const LITEHTML_RETURN_FUNC(display_none) +bool litehtml::element::set_pseudo_class( const tchar_t* pclass, bool add ) LITEHTML_RETURN_FUNC(false) +bool litehtml::element::set_class( const tchar_t* pclass, bool add ) LITEHTML_RETURN_FUNC(false) +litehtml::element_position litehtml::element::get_element_position(css_offsets* offsets) const LITEHTML_RETURN_FUNC(element_position_static) +bool litehtml::element::is_replaced() const LITEHTML_RETURN_FUNC(false) +int litehtml::element::line_height() const LITEHTML_RETURN_FUNC(0) +void litehtml::element::draw( uint_ptr hdc, int x, int y, const position* clip ) LITEHTML_EMPTY_FUNC +void litehtml::element::draw_background( uint_ptr hdc, int x, int y, const position* clip ) LITEHTML_EMPTY_FUNC +const litehtml::tchar_t* litehtml::element::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ ) const LITEHTML_RETURN_FUNC(nullptr) +litehtml::uint_ptr litehtml::element::get_font( font_metrics* fm /*= 0*/ ) LITEHTML_RETURN_FUNC(0) +int litehtml::element::get_font_size() const LITEHTML_RETURN_FUNC(0) +void litehtml::element::get_text( tstring& text ) LITEHTML_EMPTY_FUNC +void litehtml::element::parse_attributes() LITEHTML_EMPTY_FUNC +int litehtml::element::select( const css_selector& selector, bool apply_pseudo) LITEHTML_RETURN_FUNC(select_no_match) +int litehtml::element::select( const css_element_selector& selector, bool apply_pseudo /*= true*/ ) LITEHTML_RETURN_FUNC(select_no_match) +litehtml::element::ptr litehtml::element::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) LITEHTML_RETURN_FUNC(nullptr) +bool litehtml::element::is_first_child_inline(const element::ptr& el) const LITEHTML_RETURN_FUNC(false) +bool litehtml::element::is_last_child_inline(const element::ptr& el) LITEHTML_RETURN_FUNC(false) +bool litehtml::element::have_inline_child() const LITEHTML_RETURN_FUNC(false) diff --git a/gb.form.htmlview/src/litehtml/element.h b/gb.form.htmlview/src/litehtml/element.h new file mode 100644 index 00000000..82942439 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/element.h @@ -0,0 +1,408 @@ +#ifndef LH_ELEMENT_H +#define LH_ELEMENT_H + +#include <memory> +#include "stylesheet.h" +#include "css_offsets.h" + +namespace litehtml +{ + class box; + + class element : public std::enable_shared_from_this<element> + { + friend class block_box; + friend class line_box; + friend class html_tag; + friend class el_table; + friend class document; + public: + typedef std::shared_ptr<litehtml::element> ptr; + typedef std::shared_ptr<const litehtml::element> const_ptr; + typedef std::weak_ptr<litehtml::element> weak_ptr; + protected: + std::weak_ptr<element> m_parent; + std::weak_ptr<litehtml::document> m_doc; + litehtml::box* m_box; + elements_vector m_children; + position m_pos; + margins m_margins; + margins m_padding; + margins m_borders; + bool m_skip; + + virtual void select_all(const css_selector& selector, elements_vector& res); + public: + explicit element(const std::shared_ptr<litehtml::document>& doc); + virtual ~element() = default; + + // returns refer to m_pos member; + position& get_position(); + + int left() const; + int right() const; + int top() const; + int bottom() const; + int height() const; + int width() const; + + int content_margins_top() const; + int content_margins_bottom() const; + int content_margins_left() const; + int content_margins_right() const; + int content_margins_width() const; + int content_margins_height() const; + + int margin_top() const; + int margin_bottom() const; + int margin_left() const; + int margin_right() const; + margins get_margins() const; + + int padding_top() const; + int padding_bottom() const; + int padding_left() const; + int padding_right() const; + margins get_paddings() const; + + int border_top() const; + int border_bottom() const; + int border_left() const; + int border_right() const; + margins get_borders() const; + + bool in_normal_flow() const; + litehtml::web_color get_color(const tchar_t* prop_name, bool inherited, const litehtml::web_color& def_color = litehtml::web_color()); + bool is_inline_box() const; + position get_placement() const; + bool collapse_top_margin() const; + bool collapse_bottom_margin() const; + bool is_positioned() const; + + bool skip() const; + void skip(bool val); + bool have_parent() const; + element::ptr parent() const; + void parent(const element::ptr& par); + bool is_visible() const; + int calc_width(int defVal) const; + int get_inline_shift_left(); + int get_inline_shift_right(); + void apply_relative_shift(int parent_width); + // returns true for elements inside a table (but outside cells) that don't participate in table rendering + bool is_table_skip() const; + + std::shared_ptr<document> get_document() const; + + virtual elements_vector select_all(const tstring& selector); + virtual elements_vector select_all(const css_selector& selector); + + virtual element::ptr select_one(const tstring& selector); + virtual element::ptr select_one(const css_selector& selector); + + virtual int render(int x, int y, int max_width, bool second_pass = false); + virtual int render_inline(const ptr &container, int max_width); + virtual int place_element(const ptr &el, int max_width); + virtual void calc_outlines( int parent_width ); + virtual void calc_auto_margins(int parent_width); + virtual void apply_vertical_align(); + virtual bool fetch_positioned(); + virtual void render_positioned(render_type rt = render_all); + + virtual bool appendChild(const ptr &el); + virtual bool removeChild(const ptr &el); + virtual void clearRecursive(); + + virtual const tchar_t* get_tagName() const; + virtual void set_tagName(const tchar_t* tag); + virtual void set_data(const tchar_t* data); + virtual element_float get_float() const; + virtual vertical_align get_vertical_align() const; + virtual element_clear get_clear() const; + virtual size_t get_children_count() const; + virtual element::ptr get_child(int idx) const; + virtual overflow get_overflow() const; + + virtual css_length get_css_left() const; + virtual css_length get_css_right() const; + virtual css_length get_css_top() const; + virtual css_length get_css_bottom() const; + virtual css_offsets get_css_offsets() const; + virtual css_length get_css_width() const; + virtual void set_css_width(css_length& w); + virtual css_length get_css_height() const; + + virtual void set_attr(const tchar_t* name, const tchar_t* val); + virtual const tchar_t* get_attr(const tchar_t* name, const tchar_t* def = nullptr) const; + virtual void apply_stylesheet(const litehtml::css& stylesheet); + virtual void refresh_styles(); + virtual bool is_white_space() const; + virtual bool is_space() const; + virtual bool is_comment() const; + virtual bool is_body() const; + virtual bool is_break() const; + virtual int get_base_line(); + virtual bool on_mouse_over(); + virtual bool on_mouse_leave(); + virtual bool on_lbutton_down(); + virtual bool on_lbutton_up(); + virtual void on_click(); + virtual bool find_styles_changes(position::vector& redraw_boxes, int x, int y); + virtual const tchar_t* get_cursor(); + virtual void init_font(); + virtual bool is_point_inside(int x, int y); + virtual bool set_pseudo_class(const tchar_t* pclass, bool add); + virtual bool set_class(const tchar_t* pclass, bool add); + virtual bool is_replaced() const; + virtual int line_height() const; + virtual white_space get_white_space() const; + virtual style_display get_display() const; + virtual visibility get_visibility() const; + virtual element_position get_element_position(css_offsets* offsets = nullptr) const; + virtual void get_inline_boxes(position::vector& boxes); + virtual void parse_styles(bool is_reparse = false); + virtual void draw(uint_ptr hdc, int x, int y, const position* clip); + virtual void draw_background( uint_ptr hdc, int x, int y, const position* clip ); + virtual const tchar_t* get_style_property(const tchar_t* name, bool inherited, const tchar_t* def = nullptr) const; + virtual uint_ptr get_font(font_metrics* fm = nullptr); + virtual int get_font_size() const; + virtual void get_text(tstring& text); + virtual void parse_attributes(); + virtual int select(const css_selector& selector, bool apply_pseudo = true); + virtual int select(const css_element_selector& selector, bool apply_pseudo = true); + virtual element::ptr find_ancestor(const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); + virtual bool is_ancestor(const ptr &el) const; + virtual element::ptr find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); + virtual element::ptr find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); + virtual bool is_first_child_inline(const element::ptr& el) const; + virtual bool is_last_child_inline(const element::ptr& el); + virtual bool have_inline_child() const; + virtual void get_content_size(size& sz, int max_width); + virtual void init(); + virtual bool is_floats_holder() const; + virtual int get_floats_height(element_float el_float = float_none) const; + virtual int get_left_floats_height() const; + virtual int get_right_floats_height() const; + virtual int get_line_left(int y); + virtual int get_line_right(int y, int def_right); + virtual void get_line_left_right(int y, int def_right, int& ln_left, int& ln_right); + virtual void add_float(const ptr &el, int x, int y); + virtual void update_floats(int dy, const ptr &parent); + virtual void add_positioned(const ptr &el); + virtual int find_next_line_top(int top, int width, int def_right); + virtual int get_zindex() const; + virtual void draw_stacking_context(uint_ptr hdc, int x, int y, const position* clip, bool with_positioned); + virtual void draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ); + virtual bool is_nth_child(const element::ptr& el, int num, int off, bool of_type) const; + virtual bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const; + virtual bool is_only_child(const element::ptr& el, bool of_type) const; + virtual bool get_predefined_height(int& p_height) const; + virtual void calc_document_size(litehtml::size& sz, int x = 0, int y = 0); + virtual void get_redraw_box(litehtml::position& pos, int x = 0, int y = 0); + virtual void add_style(const tstring& style, const tstring& baseurl); + virtual element::ptr get_element_by_point(int x, int y, int client_x, int client_y); + virtual element::ptr get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex); + virtual const background* get_background(bool own_only = false); + }; + + ////////////////////////////////////////////////////////////////////////// + // INLINE FUNCTIONS // + ////////////////////////////////////////////////////////////////////////// + + inline int litehtml::element::right() const + { + return left() + width(); + } + + inline int litehtml::element::left() const + { + return m_pos.left() - margin_left() - m_padding.left - m_borders.left; + } + + inline int litehtml::element::top() const + { + return m_pos.top() - margin_top() - m_padding.top - m_borders.top; + } + + inline int litehtml::element::bottom() const + { + return top() + height(); + } + + inline int litehtml::element::height() const + { + return m_pos.height + margin_top() + margin_bottom() + m_padding.height() + m_borders.height(); + } + + inline int litehtml::element::width() const + { + return m_pos.width + margin_left() + margin_right() + m_padding.width() + m_borders.width(); + } + + inline int litehtml::element::content_margins_top() const + { + return margin_top() + m_padding.top + m_borders.top; + } + + inline int litehtml::element::content_margins_bottom() const + { + return margin_bottom() + m_padding.bottom + m_borders.bottom; + } + + inline int litehtml::element::content_margins_left() const + { + return margin_left() + m_padding.left + m_borders.left; + } + + inline int litehtml::element::content_margins_right() const + { + return margin_right() + m_padding.right + m_borders.right; + } + + inline int litehtml::element::content_margins_width() const + { + return content_margins_left() + content_margins_right(); + } + + inline int litehtml::element::content_margins_height() const + { + return content_margins_top() + content_margins_bottom(); + } + + inline litehtml::margins litehtml::element::get_paddings() const + { + return m_padding; + } + + inline litehtml::margins litehtml::element::get_borders() const + { + return m_borders; + } + + inline int litehtml::element::padding_top() const + { + return m_padding.top; + } + + inline int litehtml::element::padding_bottom() const + { + return m_padding.bottom; + } + + inline int litehtml::element::padding_left() const + { + return m_padding.left; + } + + inline int litehtml::element::padding_right() const + { + return m_padding.right; + } + + inline bool litehtml::element::in_normal_flow() const + { + if(get_element_position() != element_position_absolute && get_display() != display_none) + { + return true; + } + return false; + } + + inline int litehtml::element::border_top() const + { + return m_borders.top; + } + + inline int litehtml::element::border_bottom() const + { + return m_borders.bottom; + } + + inline int litehtml::element::border_left() const + { + return m_borders.left; + } + + inline int litehtml::element::border_right() const + { + return m_borders.right; + } + + inline bool litehtml::element::skip() const + { + return m_skip; + } + + inline void litehtml::element::skip(bool val) + { + m_skip = val; + } + + inline bool litehtml::element::have_parent() const + { + return !m_parent.expired(); + } + + inline element::ptr litehtml::element::parent() const + { + return m_parent.lock(); + } + + inline void litehtml::element::parent(const element::ptr& par) + { + m_parent = par; + } + + inline int litehtml::element::margin_top() const + { + return m_margins.top; + } + + inline int litehtml::element::margin_bottom() const + { + return m_margins.bottom; + } + + inline int litehtml::element::margin_left() const + { + return m_margins.left; + } + + inline int litehtml::element::margin_right() const + { + return m_margins.right; + } + + inline litehtml::margins litehtml::element::get_margins() const + { + margins ret; + ret.left = margin_left(); + ret.right = margin_right(); + ret.top = margin_top(); + ret.bottom = margin_bottom(); + + return ret; + } + + inline bool litehtml::element::is_positioned() const + { + return (get_element_position() > element_position_static); + } + + inline bool litehtml::element::is_visible() const + { + return !(m_skip || get_display() == display_none || get_visibility() != visibility_visible); + } + + inline position& litehtml::element::get_position() + { + return m_pos; + } + + inline std::shared_ptr<document> element::get_document() const + { + return m_doc.lock(); + } +} + +#endif // LH_ELEMENT_H diff --git a/gb.form.htmlview/src/litehtml/html.cpp b/gb.form.htmlview/src/litehtml/html.cpp new file mode 100644 index 00000000..4a4e69a8 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/html.cpp @@ -0,0 +1,245 @@ +#include "html.h" +#include "types.h" +#include "utf8_strings.h" + +void litehtml::trim(tstring &s) +{ + tstring::size_type pos = s.find_first_not_of(_t(" \n\r\t")); + if(pos != tstring::npos) + { + s.erase(s.begin(), s.begin() + pos); + } + pos = s.find_last_not_of(_t(" \n\r\t")); + if(pos != tstring::npos) + { + s.erase(s.begin() + pos + 1, s.end()); + } +} + +void litehtml::lcase(tstring &s) +{ + for(tchar_t & i : s) + { + i = t_tolower(i); + } +} + +litehtml::tstring::size_type litehtml::find_close_bracket(const tstring &s, tstring::size_type off, tchar_t open_b, tchar_t close_b) +{ + int cnt = 0; + for(tstring::size_type i = off; i < s.length(); i++) + { + if(s[i] == open_b) + { + cnt++; + } else if(s[i] == close_b) + { + cnt--; + if(!cnt) + { + return i; + } + } + } + return tstring::npos; +} + +int litehtml::value_index( const tstring& val, const tstring& strings, int defValue, tchar_t delim ) +{ + if(val.empty() || strings.empty() || !delim) + { + return defValue; + } + + int idx = 0; + tstring::size_type delim_start = 0; + tstring::size_type delim_end = strings.find(delim, delim_start); + tstring::size_type item_len; + while(true) + { + if(delim_end == tstring::npos) + { + item_len = strings.length() - delim_start; + } else + { + item_len = delim_end - delim_start; + } + if(item_len == val.length()) + { + if(val == strings.substr(delim_start, item_len)) + { + return idx; + } + } + idx++; + delim_start = delim_end; + if(delim_start == tstring::npos) break; + delim_start++; + if(delim_start == strings.length()) break; + delim_end = strings.find(delim, delim_start); + } + return defValue; +} + +bool litehtml::value_in_list( const tstring& val, const tstring& strings, tchar_t delim ) +{ + int idx = value_index(val, strings, -1, delim); + if(idx >= 0) + { + return true; + } + return false; +} + +void litehtml::split_string(const tstring& str, string_vector& tokens, const tstring& delims, const tstring& delims_preserve, const tstring& quote) +{ + if(str.empty() || (delims.empty() && delims_preserve.empty())) + { + return; + } + + tstring all_delims = delims + delims_preserve + quote; + + tstring::size_type token_start = 0; + tstring::size_type token_end = str.find_first_of(all_delims, token_start); + tstring::size_type token_len; + tstring token; + while(true) + { + while( token_end != tstring::npos && quote.find_first_of(str[token_end]) != tstring::npos ) + { + if(str[token_end] == _t('(')) + { + token_end = find_close_bracket(str, token_end, _t('('), _t(')')); + } else if(str[token_end] == _t('[')) + { + token_end = find_close_bracket(str, token_end, _t('['), _t(']')); + } else if(str[token_end] == _t('{')) + { + token_end = find_close_bracket(str, token_end, _t('{'), _t('}')); + } else + { + token_end = str.find_first_of(str[token_end], token_end + 1); + } + if(token_end != tstring::npos) + { + token_end = str.find_first_of(all_delims, token_end + 1); + } + } + + if(token_end == tstring::npos) + { + token_len = tstring::npos; + } else + { + token_len = token_end - token_start; + } + + token = str.substr(token_start, token_len); + if(!token.empty()) + { + tokens.push_back( token ); + } + if(token_end != tstring::npos && !delims_preserve.empty() && delims_preserve.find_first_of(str[token_end]) != tstring::npos) + { + tokens.push_back( str.substr(token_end, 1) ); + } + + token_start = token_end; + if(token_start == tstring::npos) break; + token_start++; + if(token_start == str.length()) break; + token_end = str.find_first_of(all_delims, token_start); + } +} + +void litehtml::join_string(tstring& str, const string_vector& tokens, const tstring& delims) +{ + tstringstream ss; + for(size_t i=0; i<tokens.size(); ++i) + { + if(i != 0) + { + ss << delims; + } + ss << tokens[i]; + } + + str = ss.str(); +} + +int litehtml::t_strcasecmp(const litehtml::tchar_t *s1, const litehtml::tchar_t *s2) +{ + int i, d, c; + + for (i = 0;; i++) + { + c = t_tolower((unsigned char)s1[i]); + d = c - t_tolower((unsigned char)s2[i]); + if (d < 0) + return -1; + else if (d > 0) + return 1; + else if (c == 0) + return 0; + } +} + +int litehtml::t_strncasecmp(const litehtml::tchar_t *s1, const litehtml::tchar_t *s2, size_t n) +{ + int i, d, c; + + for (i = 0; i < n; i++) + { + c = t_tolower((unsigned char)s1[i]); + d = c - t_tolower((unsigned char)s2[i]); + if (d < 0) + return -1; + else if (d > 0) + return 1; + } + + return 0; +} + +void litehtml::document_container::split_text(const char* text, const std::function<void(const tchar_t*)>& on_word, const std::function<void(const tchar_t*)>& on_space) +{ + std::wstring str; + std::wstring str_in = (const wchar_t*)(utf8_to_wchar(text)); + ucode_t c; + for (size_t i = 0; i < str_in.length(); i++) + { + c = (ucode_t)str_in[i]; + if (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')) + { + if (!str.empty()) + { + on_word(litehtml_from_wchar(str.c_str())); + str.clear(); + } + str += c; + on_space(litehtml_from_wchar(str.c_str())); + str.clear(); + } + // CJK character range + else if (c >= 0x4E00 && c <= 0x9FCC) + { + if (!str.empty()) + { + on_word(litehtml_from_wchar(str.c_str())); + str.clear(); + } + str += c; + on_word(litehtml_from_wchar(str.c_str())); + str.clear(); + } + else + { + str += c; + } + } + if (!str.empty()) + { + on_word(litehtml_from_wchar(str.c_str())); + } +} diff --git a/gb.form.htmlview/src/litehtml/html.h b/gb.form.htmlview/src/litehtml/html.h new file mode 100644 index 00000000..8bf9bedb --- /dev/null +++ b/gb.form.htmlview/src/litehtml/html.h @@ -0,0 +1,117 @@ +#ifndef LH_HTML_H +#define LH_HTML_H + +#include <stdlib.h> +#include <string> +#include <ctype.h> +#include <vector> +#include <map> +#include <cstring> +#include <algorithm> +#include <sstream> +#include <functional> +#include "os_types.h" +#include "types.h" +#include "background.h" +#include "borders.h" +#include "html_tag.h" +#include "web_color.h" +#include "media_query.h" + +namespace litehtml +{ + struct list_marker + { + tstring image; + const tchar_t* baseurl; + list_style_type marker_type; + web_color color; + position pos; + int index; + uint_ptr font; + }; + + // call back interface to draw text, images and other elements + class document_container + { + public: + virtual litehtml::uint_ptr create_font(const litehtml::tchar_t* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) = 0; + virtual void delete_font(litehtml::uint_ptr hFont) = 0; + virtual int text_width(const litehtml::tchar_t* text, litehtml::uint_ptr hFont) = 0; + virtual void draw_text(litehtml::uint_ptr hdc, const litehtml::tchar_t* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) = 0; + virtual int pt_to_px(int pt) const = 0; + virtual int get_default_font_size() const = 0; + virtual const litehtml::tchar_t* get_default_font_name() const = 0; + virtual void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) = 0; + virtual void load_image(const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready) = 0; + virtual void get_image_size(const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz) = 0; + virtual void draw_background(litehtml::uint_ptr hdc, const litehtml::background_paint& bg) = 0; + virtual void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) = 0; + + virtual void set_caption(const litehtml::tchar_t* caption) = 0; + virtual void set_base_url(const litehtml::tchar_t* base_url) = 0; + virtual void link(const std::shared_ptr<litehtml::document>& doc, const litehtml::element::ptr& el) = 0; + virtual void on_anchor_click(const litehtml::tchar_t* url, const litehtml::element::ptr& el) = 0; + virtual void set_cursor(const litehtml::tchar_t* cursor) = 0; + virtual void transform_text(litehtml::tstring& text, litehtml::text_transform tt) = 0; + virtual void import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl) = 0; + virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y) = 0; + virtual void del_clip() = 0; + virtual void get_client_rect(litehtml::position& client) const = 0; + virtual std::shared_ptr<litehtml::element> create_element(const litehtml::tchar_t *tag_name, + const litehtml::string_map &attributes, + const std::shared_ptr<litehtml::document> &doc) = 0; + + virtual void get_media_features(litehtml::media_features& media) const = 0; + virtual void get_language(litehtml::tstring& language, litehtml::tstring & culture) const = 0; + virtual litehtml::tstring resolve_color(const litehtml::tstring& /*color*/) const { return litehtml::tstring(); } + virtual void split_text(const char* text, const std::function<void(const tchar_t*)>& on_word, const std::function<void(const tchar_t*)>& on_space); + + protected: + ~document_container() = default; + }; + + void trim(tstring &s); + void lcase(tstring &s); + int value_index(const tstring& val, const tstring& strings, int defValue = -1, tchar_t delim = _t(';')); + bool value_in_list(const tstring& val, const tstring& strings, tchar_t delim = _t(';')); + tstring::size_type find_close_bracket(const tstring &s, tstring::size_type off, tchar_t open_b = _t('('), tchar_t close_b = _t(')')); + void split_string(const tstring& str, string_vector& tokens, const tstring& delims, const tstring& delims_preserve = _t(""), const tstring& quote = _t("\"")); + void join_string(tstring& str, const string_vector& tokens, const tstring& delims); + double t_strtod(const tchar_t* string, tchar_t** endPtr); + + int t_strcasecmp(const tchar_t *s1, const tchar_t *s2); + int t_strncasecmp(const tchar_t *s1, const tchar_t *s2, size_t n); + + inline int t_isdigit(int c) + { + return (c >= '0' && c <= '9'); + } + + inline int t_tolower(int c) + { + return (c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c); + } + + inline int round_f(float val) + { + int int_val = (int) val; + if(val - int_val >= 0.5) + { + int_val++; + } + return int_val; + } + + inline int round_d(double val) + { + int int_val = (int) val; + if(val - int_val >= 0.5) + { + int_val++; + } + return int_val; + } +} + +#endif // LH_HTML_H diff --git a/gb.form.htmlview/src/litehtml/html_tag.cpp b/gb.form.htmlview/src/litehtml/html_tag.cpp new file mode 100644 index 00000000..cfd6bac6 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/html_tag.cpp @@ -0,0 +1,4808 @@ +#include "html.h" +#include "html_tag.h" +#include "document.h" +#include "iterators.h" +#include "stylesheet.h" +#include "table.h" +#include <algorithm> +#include <locale> +#include "el_before_after.h" +#include "num_cvt.h" + +litehtml::html_tag::html_tag(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +{ + m_box_sizing = box_sizing_content_box; + m_z_index = 0; + m_overflow = overflow_visible; + m_box = nullptr; + m_text_align = text_align_left; + m_el_position = element_position_static; + m_display = display_inline; + m_vertical_align = va_baseline; + m_list_style_type = list_style_type_none; + m_list_style_position = list_style_position_outside; + m_float = float_none; + m_clear = clear_none; + m_font = 0; + m_font_size = 0; + m_white_space = white_space_normal; + m_lh_predefined = false; + m_line_height = 0; + m_visibility = visibility_visible; + m_border_spacing_x = 0; + m_border_spacing_y = 0; + m_border_collapse = border_collapse_separate; +} + +bool litehtml::html_tag::appendChild(const element::ptr &el) +{ + if(el) + { + el->parent(shared_from_this()); + m_children.push_back(el); + return true; + } + return false; +} + +bool litehtml::html_tag::removeChild(const element::ptr &el) +{ + if(el && el->parent() == shared_from_this()) + { + el->parent(nullptr); + m_children.erase(std::remove(m_children.begin(), m_children.end(), el), m_children.end()); + return true; + } + return false; +} + +void litehtml::html_tag::clearRecursive() +{ + for(auto& el : m_children) + { + el->clearRecursive(); + el->parent(nullptr); + } + m_children.clear(); +} + + +const litehtml::tchar_t* litehtml::html_tag::get_tagName() const +{ + return m_tag.c_str(); +} + +void litehtml::html_tag::set_attr( const tchar_t* name, const tchar_t* val ) +{ + if(name && val) + { + tstring s_val = name; + for(tchar_t& i : s_val) + { + i = std::tolower(i, std::locale::classic()); + } + m_attrs[s_val] = val; + + if( t_strcasecmp( name, _t("class") ) == 0 ) + { + m_class_values.resize( 0 ); + split_string( val, m_class_values, _t(" ") ); + } + } +} + +const litehtml::tchar_t* litehtml::html_tag::get_attr( const tchar_t* name, const tchar_t* def ) const +{ + auto attr = m_attrs.find(name); + if(attr != m_attrs.end()) + { + return attr->second.c_str(); + } + return def; +} + +litehtml::elements_vector litehtml::html_tag::select_all( const tstring& selector ) +{ + css_selector sel(media_query_list::ptr(nullptr), _t("")); + sel.parse(selector); + + return select_all(sel); +} + +litehtml::elements_vector litehtml::html_tag::select_all( const css_selector& selector ) +{ + litehtml::elements_vector res; + select_all(selector, res); + return res; +} + +void litehtml::html_tag::select_all(const css_selector& selector, elements_vector& res) +{ + if(select(selector)) + { + res.push_back(shared_from_this()); + } + + for(auto& el : m_children) + { + el->select_all(selector, res); + } +} + + +litehtml::element::ptr litehtml::html_tag::select_one( const tstring& selector ) +{ + css_selector sel(media_query_list::ptr(nullptr), _t("")); + sel.parse(selector); + + return select_one(sel); +} + +litehtml::element::ptr litehtml::html_tag::select_one( const css_selector& selector ) +{ + if(select(selector)) + { + return shared_from_this(); + } + + for(auto& el : m_children) + { + element::ptr res = el->select_one(selector); + if(res) + { + return res; + } + } + return nullptr; +} + +void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) +{ + remove_before_after(); + + for(const auto& sel : stylesheet.selectors()) + { + int apply = select(*sel, false); + + if(apply != select_no_match) + { + used_selector::ptr us = std::unique_ptr<used_selector>(new used_selector(sel, false)); + + if(sel->is_media_valid()) + { + if(apply & select_match_pseudo_class) + { + if(select(*sel, true)) + { + if(apply & select_match_with_after) + { + element::ptr el = get_element_after(); + if(el) + { + el->add_style(sel->m_style, sel->m_baseurl); + } + } else if(apply & select_match_with_before) + { + element::ptr el = get_element_before(); + if(el) + { + el->add_style(sel->m_style, sel->m_baseurl); + } + } + else + { + add_style(sel->m_style, sel->m_baseurl); + us->m_used = true; + } + } + } else if(apply & select_match_with_after) + { + element::ptr el = get_element_after(); + if(el) + { + el->add_style(sel->m_style, sel->m_baseurl); + } + } else if(apply & select_match_with_before) + { + element::ptr el = get_element_before(); + if(el) + { + el->add_style(sel->m_style, sel->m_baseurl); + } + } else + { + add_style(sel->m_style, sel->m_baseurl); + us->m_used = true; + } + } + m_used_styles.push_back(std::move(us)); + } + } + + for(auto& el : m_children) + { + if(el->get_display() != display_inline_text) + { + el->apply_stylesheet(stylesheet); + } + } +} + +void litehtml::html_tag::get_content_size( size& sz, int max_width ) +{ + sz.height = 0; + if(m_display == display_block) + { + sz.width = max_width; + } else + { + sz.width = 0; + } +} + +void litehtml::html_tag::draw( uint_ptr hdc, int x, int y, const position* clip ) +{ + position pos = m_pos; + pos.x += x; + pos.y += y; + + draw_background(hdc, x, y, clip); + + if(m_display == display_list_item && m_list_style_type != list_style_type_none) + { + if(m_overflow > overflow_visible) + { + position border_box = pos; + border_box += m_padding; + border_box += m_borders; + + border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); + + bdr_radius -= m_borders; + bdr_radius -= m_padding; + + get_document()->container()->set_clip(pos, bdr_radius, true, true); + } + + draw_list_marker(hdc, pos); + + if(m_overflow > overflow_visible) + { + get_document()->container()->del_clip(); + } + } +} + +litehtml::uint_ptr litehtml::html_tag::get_font(font_metrics* fm) +{ + if(fm) + { + *fm = m_font_metrics; + } + return m_font; +} + +const litehtml::tchar_t* litehtml::html_tag::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ ) const +{ + const tchar_t* ret = m_style.get_property(name); + element::ptr el_parent = parent(); + if (el_parent) + { + if ( ( ret && !t_strcasecmp(ret, _t("inherit")) ) || (!ret && inherited) ) + { + ret = el_parent->get_style_property(name, inherited, def); + } + } + + if(!ret) + { + ret = def; + } + + return ret; +} + +void litehtml::html_tag::parse_styles(bool is_reparse) +{ + const tchar_t* style = get_attr(_t("style")); + + if(style) + { + m_style.add(style, nullptr, this); + } + + init_font(); + document::ptr doc = get_document(); + + m_el_position = (element_position) value_index(get_style_property(_t("position"), false, _t("static")), element_position_strings, element_position_fixed); + m_text_align = (text_align) value_index(get_style_property(_t("text-align"), true, _t("left")), text_align_strings, text_align_left); + m_overflow = (overflow) value_index(get_style_property(_t("overflow"), false, _t("visible")), overflow_strings, overflow_visible); + m_white_space = (white_space) value_index(get_style_property(_t("white-space"), true, _t("normal")), white_space_strings, white_space_normal); + m_display = (style_display) value_index(get_style_property(_t("display"), false, _t("inline")), style_display_strings, display_inline); + m_visibility = (visibility) value_index(get_style_property(_t("visibility"), true, _t("visible")), visibility_strings, visibility_visible); + m_box_sizing = (box_sizing) value_index(get_style_property(_t("box-sizing"), false, _t("content-box")), box_sizing_strings, box_sizing_content_box); + + if(m_el_position != element_position_static) + { + const tchar_t* val = get_style_property(_t("z-index"), false, nullptr); + if(val) + { + m_z_index = t_atoi(val); + } + } + + const tchar_t* va = get_style_property(_t("vertical-align"), true, _t("baseline")); + m_vertical_align = (vertical_align) value_index(va, vertical_align_strings, va_baseline); + + const tchar_t* fl = get_style_property(_t("float"), false, _t("none")); + m_float = (element_float) value_index(fl, element_float_strings, float_none); + + m_clear = (element_clear) value_index(get_style_property(_t("clear"), false, _t("none")), element_clear_strings, clear_none); + + if (m_display != display_none && + m_display != display_table && + m_display != display_inline_table) + { + // reset display in to block for floating elements + if (m_float != float_none) + { + m_display = display_block; + } + // fix elements with absolute/fixed positions + else if (m_el_position == element_position_absolute || m_el_position == element_position_fixed) + { + m_display = display_block; + } + } + + if (m_display == display_table || + m_display == display_inline_table || + m_display == display_table_caption || + m_display == display_table_cell || + m_display == display_table_column || + m_display == display_table_column_group || + m_display == display_table_footer_group || + m_display == display_table_header_group || + m_display == display_table_row || + m_display == display_table_row_group) + { + doc->add_tabular(shared_from_this()); + } + + m_css_text_indent.fromString( get_style_property(_t("text-indent"), true, _t("0")), _t("0")); + + m_css_width.fromString( get_style_property(_t("width"), false, _t("auto")), _t("auto")); + m_css_height.fromString( get_style_property(_t("height"), false, _t("auto")), _t("auto")); + + doc->cvt_units(m_css_width, m_font_size); + doc->cvt_units(m_css_height, m_font_size); + + m_css_min_width.fromString( get_style_property(_t("min-width"), false, _t("0"))); + m_css_min_height.fromString( get_style_property(_t("min-height"), false, _t("0"))); + + m_css_max_width.fromString( get_style_property(_t("max-width"), false, _t("none")), _t("none")); + m_css_max_height.fromString( get_style_property(_t("max-height"), false, _t("none")), _t("none")); + + doc->cvt_units(m_css_min_width, m_font_size); + doc->cvt_units(m_css_min_height, m_font_size); + + m_css_offsets.left.fromString( get_style_property(_t("left"), false, _t("auto")), _t("auto")); + m_css_offsets.right.fromString( get_style_property(_t("right"), false, _t("auto")), _t("auto")); + m_css_offsets.top.fromString( get_style_property(_t("top"), false, _t("auto")), _t("auto")); + m_css_offsets.bottom.fromString( get_style_property(_t("bottom"), false, _t("auto")), _t("auto")); + + doc->cvt_units(m_css_offsets.left, m_font_size); + doc->cvt_units(m_css_offsets.right, m_font_size); + doc->cvt_units(m_css_offsets.top, m_font_size); + doc->cvt_units(m_css_offsets.bottom, m_font_size); + + m_css_margins.left.fromString( get_style_property(_t("margin-left"), false, _t("0")), _t("auto")); + m_css_margins.right.fromString( get_style_property(_t("margin-right"), false, _t("0")), _t("auto")); + m_css_margins.top.fromString( get_style_property(_t("margin-top"), false, _t("0")), _t("auto")); + m_css_margins.bottom.fromString( get_style_property(_t("margin-bottom"), false, _t("0")), _t("auto")); + + m_css_padding.left.fromString( get_style_property(_t("padding-left"), false, _t("0")), _t("")); + m_css_padding.right.fromString( get_style_property(_t("padding-right"), false, _t("0")), _t("")); + m_css_padding.top.fromString( get_style_property(_t("padding-top"), false, _t("0")), _t("")); + m_css_padding.bottom.fromString( get_style_property(_t("padding-bottom"), false, _t("0")), _t("")); + + m_css_borders.left.width.fromString( get_style_property(_t("border-left-width"), false, _t("medium")), border_width_strings); + m_css_borders.right.width.fromString( get_style_property(_t("border-right-width"), false, _t("medium")), border_width_strings); + m_css_borders.top.width.fromString( get_style_property(_t("border-top-width"), false, _t("medium")), border_width_strings); + m_css_borders.bottom.width.fromString( get_style_property(_t("border-bottom-width"), false, _t("medium")), border_width_strings); + + m_css_borders.left.color = web_color::from_string(get_style_property(_t("border-left-color"), false, _t("")), doc->container()); + m_css_borders.left.style = (border_style) value_index(get_style_property(_t("border-left-style"), false, _t("none")), border_style_strings, border_style_none); + + m_css_borders.right.color = web_color::from_string(get_style_property(_t("border-right-color"), false, _t("")), doc->container()); + m_css_borders.right.style = (border_style) value_index(get_style_property(_t("border-right-style"), false, _t("none")), border_style_strings, border_style_none); + + m_css_borders.top.color = web_color::from_string(get_style_property(_t("border-top-color"), false, _t("")), doc->container()); + m_css_borders.top.style = (border_style) value_index(get_style_property(_t("border-top-style"), false, _t("none")), border_style_strings, border_style_none); + + m_css_borders.bottom.color = web_color::from_string(get_style_property(_t("border-bottom-color"), false, _t("")), doc->container()); + m_css_borders.bottom.style = (border_style) value_index(get_style_property(_t("border-bottom-style"), false, _t("none")), border_style_strings, border_style_none); + + m_css_borders.radius.top_left_x.fromString(get_style_property(_t("border-top-left-radius-x"), false, _t("0"))); + m_css_borders.radius.top_left_y.fromString(get_style_property(_t("border-top-left-radius-y"), false, _t("0"))); + + m_css_borders.radius.top_right_x.fromString(get_style_property(_t("border-top-right-radius-x"), false, _t("0"))); + m_css_borders.radius.top_right_y.fromString(get_style_property(_t("border-top-right-radius-y"), false, _t("0"))); + + m_css_borders.radius.bottom_right_x.fromString(get_style_property(_t("border-bottom-right-radius-x"), false, _t("0"))); + m_css_borders.radius.bottom_right_y.fromString(get_style_property(_t("border-bottom-right-radius-y"), false, _t("0"))); + + m_css_borders.radius.bottom_left_x.fromString(get_style_property(_t("border-bottom-left-radius-x"), false, _t("0"))); + m_css_borders.radius.bottom_left_y.fromString(get_style_property(_t("border-bottom-left-radius-y"), false, _t("0"))); + + doc->cvt_units(m_css_borders.radius.bottom_left_x, m_font_size); + doc->cvt_units(m_css_borders.radius.bottom_left_y, m_font_size); + doc->cvt_units(m_css_borders.radius.bottom_right_x, m_font_size); + doc->cvt_units(m_css_borders.radius.bottom_right_y, m_font_size); + doc->cvt_units(m_css_borders.radius.top_left_x, m_font_size); + doc->cvt_units(m_css_borders.radius.top_left_y, m_font_size); + doc->cvt_units(m_css_borders.radius.top_right_x, m_font_size); + doc->cvt_units(m_css_borders.radius.top_right_y, m_font_size); + + doc->cvt_units(m_css_text_indent, m_font_size); + + m_margins.left = doc->cvt_units(m_css_margins.left, m_font_size); + m_margins.right = doc->cvt_units(m_css_margins.right, m_font_size); + m_margins.top = doc->cvt_units(m_css_margins.top, m_font_size); + m_margins.bottom = doc->cvt_units(m_css_margins.bottom, m_font_size); + + m_padding.left = doc->cvt_units(m_css_padding.left, m_font_size); + m_padding.right = doc->cvt_units(m_css_padding.right, m_font_size); + m_padding.top = doc->cvt_units(m_css_padding.top, m_font_size); + m_padding.bottom = doc->cvt_units(m_css_padding.bottom, m_font_size); + + m_borders.left = doc->cvt_units(m_css_borders.left.width, m_font_size); + m_borders.right = doc->cvt_units(m_css_borders.right.width, m_font_size); + m_borders.top = doc->cvt_units(m_css_borders.top.width, m_font_size); + m_borders.bottom = doc->cvt_units(m_css_borders.bottom.width, m_font_size); + + css_length line_height; + line_height.fromString(get_style_property(_t("line-height"), true, _t("normal")), _t("normal")); + if(line_height.is_predefined()) + { + m_line_height = m_font_metrics.height; + m_lh_predefined = true; + } else if(line_height.units() == css_units_none) + { + m_line_height = (int) (line_height.val() * (float) m_font_size); + m_lh_predefined = false; + } else + { + m_line_height = doc->cvt_units(line_height, m_font_size, m_font_size); + m_lh_predefined = false; + } + + + if(m_display == display_list_item) + { + const tchar_t* list_type = get_style_property(_t("list-style-type"), true, _t("disc")); + m_list_style_type = (list_style_type) value_index(list_type, list_style_type_strings, list_style_type_disc); + + const tchar_t* list_pos = get_style_property(_t("list-style-position"), true, _t("outside")); + m_list_style_position = (list_style_position) value_index(list_pos, list_style_position_strings, list_style_position_outside); + + const tchar_t* list_image = get_style_property(_t("list-style-image"), true, nullptr); + if(list_image && list_image[0]) + { + tstring url; + css::parse_css_url(list_image, url); + + const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, nullptr); + doc->container()->load_image(url.c_str(), list_image_baseurl, true); + } + + } + + parse_background(); + + if(!is_reparse) + { + for(auto& el : m_children) + { + el->parse_styles(); + } + } +} + +int litehtml::html_tag::render( int x, int y, int max_width, bool second_pass ) +{ + if (m_display == display_table || m_display == display_inline_table) + { + return render_table(x, y, max_width, second_pass); + } + + return render_box(x, y, max_width, second_pass); +} + +bool litehtml::html_tag::is_white_space() const +{ + return false; +} + +int litehtml::html_tag::get_font_size() const +{ + return m_font_size; +} + +int litehtml::html_tag::get_base_line() +{ + if(is_replaced()) + { + return 0; + } + int bl = 0; + if(!m_boxes.empty()) + { + bl = m_boxes.back()->baseline() + content_margins_bottom(); + } + return bl; +} + +void litehtml::html_tag::init() +{ + if (m_display == display_table || m_display == display_inline_table) + { + if (m_grid) + { + m_grid->clear(); + } + else + { + m_grid = std::unique_ptr<table_grid>(new table_grid()); + } + + go_inside_table table_selector; + table_rows_selector row_selector; + table_cells_selector cell_selector; + + elements_iterator row_iter(shared_from_this(), &table_selector, &row_selector); + + element::ptr row = row_iter.next(false); + while (row) + { + m_grid->begin_row(row); + + elements_iterator cell_iter(row, &table_selector, &cell_selector); + element::ptr cell = cell_iter.next(); + while (cell) + { + m_grid->add_cell(cell); + + cell = cell_iter.next(false); + } + row = row_iter.next(false); + } + + for (auto& el : m_children) + { + if (el->get_display() == display_table_caption) + { + m_grid->captions().push_back(el); + } + } + + m_grid->finish(); + } + + for (auto& el : m_children) + { + el->init(); + } +} + +int litehtml::html_tag::select(const css_selector& selector, bool apply_pseudo) +{ + int right_res = select(selector.m_right, apply_pseudo); + if(right_res == select_no_match) + { + return select_no_match; + } + element::ptr el_parent = parent(); + if(selector.m_left) + { + if (!el_parent) + { + return select_no_match; + } + switch(selector.m_combinator) + { + case combinator_descendant: + { + bool is_pseudo = false; + element::ptr res = find_ancestor(*selector.m_left, apply_pseudo, &is_pseudo); + if(!res) + { + return select_no_match; + } else + { + if(is_pseudo) + { + right_res |= select_match_pseudo_class; + } + } + } + break; + case combinator_child: + { + int res = el_parent->select(*selector.m_left, apply_pseudo); + if(res == select_no_match) + { + return select_no_match; + } else + { + if(right_res != select_match_pseudo_class) + { + right_res |= res; + } + } + } + break; + case combinator_adjacent_sibling: + { + bool is_pseudo = false; + element::ptr res = el_parent->find_adjacent_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo); + if(!res) + { + return select_no_match; + } else + { + if(is_pseudo) + { + right_res |= select_match_pseudo_class; + } + } + } + break; + case combinator_general_sibling: + { + bool is_pseudo = false; + element::ptr res = el_parent->find_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo); + if(!res) + { + return select_no_match; + } else + { + if(is_pseudo) + { + right_res |= select_match_pseudo_class; + } + } + } + break; + default: + right_res = select_no_match; + } + } + return right_res; +} + +int litehtml::html_tag::select(const css_element_selector& selector, bool apply_pseudo) +{ + if(!selector.m_tag.empty() && selector.m_tag != _t("*")) + { + if(selector.m_tag != m_tag) + { + return select_no_match; + } + } + + int res = select_match; + element::ptr el_parent = parent(); + + for(const auto& attr : selector.m_attrs) + { + const tchar_t* attr_value = get_attr(attr.attribute.c_str()); + switch(attr.condition) + { + case select_exists: + if(!attr_value) + { + return select_no_match; + } + break; + case select_equal: + if(!attr_value) + { + return select_no_match; + } else + { + if(attr.attribute == _t("class")) + { + const string_vector & tokens1 = m_class_values; + const string_vector & tokens2 = attr.class_val; + bool found = true; + for(const auto& str1 : tokens2) + { + bool f = false; + for(const auto& str2 : tokens1) + { + if( !t_strcasecmp(str1.c_str(), str2.c_str()) ) + { + f = true; + } + } + if(!f) + { + found = false; + } + } + if(!found) + { + return select_no_match; + } + } else + { + if( t_strcasecmp(attr.val.c_str(), attr_value) ) + { + return select_no_match; + } + } + } + break; + case select_contain_str: + if(!attr_value || !t_strstr(attr_value, attr.val.c_str())) + { + return select_no_match; + } + break; + case select_start_str: + if(!attr_value || t_strncmp(attr_value, attr.val.c_str(), attr.val.length())) + { + return select_no_match; + } + break; + case select_end_str: + if(!attr_value) + { + return select_no_match; + } else if(t_strncmp(attr_value, attr.val.c_str(), attr.val.length())) + { + const tchar_t* s = attr_value + t_strlen(attr_value) - attr.val.length() - 1; + if(s < attr_value) + { + return select_no_match; + } + if(attr.val != s) + { + return select_no_match; + } + } + break; + case select_pseudo_element: + if(attr.val == _t("after")) + { + res |= select_match_with_after; + } else if(attr.val == _t("before")) + { + res |= select_match_with_before; + } else + { + return select_no_match; + } + break; + case select_pseudo_class: + if(apply_pseudo) + { + tstring selector_param; + tstring selector_name; + + tstring::size_type begin = attr.val.find_first_of(_t('(')); + tstring::size_type end = (begin == tstring::npos) ? tstring::npos : find_close_bracket(attr.val, begin); + if(begin != tstring::npos && end != tstring::npos) + { + selector_param = attr.val.substr(begin + 1, end - begin - 1); + } + if(begin != tstring::npos) + { + selector_name = attr.val.substr(0, begin); + litehtml::trim(selector_name); + } else + { + selector_name = attr.val; + } + + int pseudo_selector = value_index(selector_name, pseudo_class_strings); + + switch(pseudo_selector) + { + case pseudo_class_only_child: + if (!el_parent || !el_parent->is_only_child(shared_from_this(), false)) + { + return select_no_match; + } + break; + case pseudo_class_only_of_type: + if (!el_parent || !el_parent->is_only_child(shared_from_this(), true)) + { + return select_no_match; + } + break; + case pseudo_class_first_child: + if (!el_parent || !el_parent->is_nth_child(shared_from_this(), 0, 1, false)) + { + return select_no_match; + } + break; + case pseudo_class_first_of_type: + if (!el_parent || !el_parent->is_nth_child(shared_from_this(), 0, 1, true)) + { + return select_no_match; + } + break; + case pseudo_class_last_child: + if (!el_parent || !el_parent->is_nth_last_child(shared_from_this(), 0, 1, false)) + { + return select_no_match; + } + break; + case pseudo_class_last_of_type: + if (!el_parent || !el_parent->is_nth_last_child(shared_from_this(), 0, 1, true)) + { + return select_no_match; + } + break; + case pseudo_class_nth_child: + case pseudo_class_nth_of_type: + case pseudo_class_nth_last_child: + case pseudo_class_nth_last_of_type: + { + if(!el_parent || selector_param.empty()) return select_no_match; + + int num = 0; + int off = 0; + + parse_nth_child_params(selector_param, num, off); + if(!num && !off) return select_no_match; + switch(pseudo_selector) + { + case pseudo_class_nth_child: + if (!el_parent->is_nth_child(shared_from_this(), num, off, false)) + { + return select_no_match; + } + break; + case pseudo_class_nth_of_type: + if (!el_parent->is_nth_child(shared_from_this(), num, off, true)) + { + return select_no_match; + } + break; + case pseudo_class_nth_last_child: + if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false)) + { + return select_no_match; + } + break; + case pseudo_class_nth_last_of_type: + if (!el_parent->is_nth_last_child(shared_from_this(), num, off, true)) + { + return select_no_match; + } + break; + } + + } + break; + case pseudo_class_not: + { + css_element_selector sel; + sel.parse(selector_param); + if(select(sel, apply_pseudo)) + { + return select_no_match; + } + } + break; + case pseudo_class_lang: + { + trim( selector_param ); + + if( !get_document()->match_lang( selector_param ) ) + { + return select_no_match; + } + } + break; + default: + if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), attr.val) == m_pseudo_classes.end()) + { + return select_no_match; + } + break; + } + } else + { + res |= select_match_pseudo_class; + } + break; + } + } + return res; +} + +litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) +{ + element::ptr el_parent = parent(); + if (!el_parent) + { + return nullptr; + } + int res = el_parent->select(selector, apply_pseudo); + if(res != select_no_match) + { + if(is_pseudo) + { + if(res & select_match_pseudo_class) + { + *is_pseudo = true; + } else + { + *is_pseudo = false; + } + } + return el_parent; + } + return el_parent->find_ancestor(selector, apply_pseudo, is_pseudo); +} + +int litehtml::html_tag::get_floats_height(element_float el_float) const +{ + if(is_floats_holder()) + { + int h = 0; + + bool process = false; + + for(const auto& fb : m_floats_left) + { + process = false; + switch(el_float) + { + case float_none: + process = true; + break; + case float_left: + if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) + { + process = true; + } + break; + case float_right: + if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) + { + process = true; + } + break; + } + if(process) + { + if(el_float == float_none) + { + h = std::max(h, fb.pos.bottom()); + } else + { + h = std::max(h, fb.pos.top()); + } + } + } + + + for(const auto& fb : m_floats_right) + { + process = false; + switch(el_float) + { + case float_none: + process = true; + break; + case float_left: + if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) + { + process = true; + } + break; + case float_right: + if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) + { + process = true; + } + break; + } + if(process) + { + if(el_float == float_none) + { + h = std::max(h, fb.pos.bottom()); + } else + { + h = std::max(h, fb.pos.top()); + } + } + } + + return h; + } + element::ptr el_parent = parent(); + if (el_parent) + { + int h = el_parent->get_floats_height(el_float); + return h - m_pos.y; + } + return 0; +} + +int litehtml::html_tag::get_left_floats_height() const +{ + if(is_floats_holder()) + { + int h = 0; + if(!m_floats_left.empty()) + { + for (const auto& fb : m_floats_left) + { + h = std::max(h, fb.pos.bottom()); + } + } + return h; + } + element::ptr el_parent = parent(); + if (el_parent) + { + int h = el_parent->get_left_floats_height(); + return h - m_pos.y; + } + return 0; +} + +int litehtml::html_tag::get_right_floats_height() const +{ + if(is_floats_holder()) + { + int h = 0; + if(!m_floats_right.empty()) + { + for(const auto& fb : m_floats_right) + { + h = std::max(h, fb.pos.bottom()); + } + } + return h; + } + element::ptr el_parent = parent(); + if (el_parent) + { + int h = el_parent->get_right_floats_height(); + return h - m_pos.y; + } + return 0; +} + +int litehtml::html_tag::get_line_left( int y ) +{ + if(is_floats_holder()) + { + if(m_cahe_line_left.is_valid && m_cahe_line_left.hash == y) + { + return m_cahe_line_left.val; + } + + int w = 0; + for(const auto& fb : m_floats_left) + { + if (y >= fb.pos.top() && y < fb.pos.bottom()) + { + w = std::max(w, fb.pos.right()); + if (w < fb.pos.right()) + { + break; + } + } + } + m_cahe_line_left.set_value(y, w); + return w; + } + element::ptr el_parent = parent(); + if (el_parent) + { + int w = el_parent->get_line_left(y + m_pos.y); + if (w < 0) + { + w = 0; + } + return w - (w ? m_pos.x : 0); + } + return 0; +} + +int litehtml::html_tag::get_line_right( int y, int def_right ) +{ + if(is_floats_holder()) + { + if(m_cahe_line_right.is_valid && m_cahe_line_right.hash == y) + { + if(m_cahe_line_right.is_default) + { + return def_right; + } else + { + return std::min(m_cahe_line_right.val, def_right); + } + } + + int w = def_right; + m_cahe_line_right.is_default = true; + for(const auto& fb : m_floats_right) + { + if(y >= fb.pos.top() && y < fb.pos.bottom()) + { + w = std::min(w, fb.pos.left()); + m_cahe_line_right.is_default = false; + if(w > fb.pos.left()) + { + break; + } + } + } + m_cahe_line_right.set_value(y, w); + return w; + } + element::ptr el_parent = parent(); + if (el_parent) + { + int w = el_parent->get_line_right(y + m_pos.y, def_right + m_pos.x); + return w - m_pos.x; + } + return 0; +} + + +void litehtml::html_tag::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right ) +{ + if(is_floats_holder()) + { + ln_left = get_line_left(y); + ln_right = get_line_right(y, def_right); + } else + { + element::ptr el_parent = parent(); + if (el_parent) + { + el_parent->get_line_left_right(y + m_pos.y, def_right + m_pos.x, ln_left, ln_right); + } + ln_right -= m_pos.x; + + if(ln_left < 0) + { + ln_left = 0; + } else if (ln_left > 0) + { + ln_left -= m_pos.x; + if (ln_left < 0) + { + ln_left = 0; + } + } + } +} + +int litehtml::html_tag::fix_line_width( int max_width, element_float flt ) +{ + int ret_width = 0; + if(!m_boxes.empty()) + { + elements_vector els; + m_boxes.back()->get_elements(els); + bool was_cleared = false; + if(!els.empty() && els.front()->get_clear() != clear_none) + { + if(els.front()->get_clear() == clear_both) + { + was_cleared = true; + } else + { + if( (flt == float_left && els.front()->get_clear() == clear_left) || + (flt == float_right && els.front()->get_clear() == clear_right) ) + { + was_cleared = true; + } + } + } + + if(!was_cleared) + { + m_boxes.pop_back(); + + for(const auto& el : els) + { + int rw = place_element(el, max_width); + if(rw > ret_width) + { + ret_width = rw; + } + } + } else + { + int line_top = 0; + if(m_boxes.back()->get_type() == box_line) + { + line_top = m_boxes.back()->top(); + } else + { + line_top = m_boxes.back()->bottom(); + } + + int line_left = 0; + int line_right = max_width; + get_line_left_right(line_top, max_width, line_left, line_right); + + if(m_boxes.back()->get_type() == box_line) + { + if(m_boxes.size() == 1 && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside) + { + int sz_font = get_font_size(); + line_left += sz_font; + } + + if(m_css_text_indent.val() != 0) + { + bool line_box_found = false; + for(const auto& box : m_boxes) + { + if(box->get_type() == box_line) + { + line_box_found = true; + break; + } + } + if(!line_box_found) + { + line_left += m_css_text_indent.calc_percent(max_width); + } + } + + } + + els.clear(); + m_boxes.back()->new_width(line_left, line_right, els); + for(auto& el : els) + { + int rw = place_element(el, max_width); + if(rw > ret_width) + { + ret_width = rw; + } + } + } + } + + return ret_width; +} + +void litehtml::html_tag::add_float(const element::ptr &el, int x, int y) +{ + if(is_floats_holder()) + { + floated_box fb; + fb.pos.x = el->left() + x; + fb.pos.y = el->top() + y; + fb.pos.width = el->width(); + fb.pos.height = el->height(); + fb.float_side = el->get_float(); + fb.clear_floats = el->get_clear(); + fb.el = el; + + if(fb.float_side == float_left) + { + if(m_floats_left.empty()) + { + m_floats_left.push_back(fb); + } else + { + bool inserted = false; + for(auto i = m_floats_left.begin(); i != m_floats_left.end(); i++) + { + if(fb.pos.right() > i->pos.right()) + { + m_floats_left.insert(i, std::move(fb)); + inserted = true; + break; + } + } + if(!inserted) + { + m_floats_left.push_back(std::move(fb)); + } + } + m_cahe_line_left.invalidate(); + } else if(fb.float_side == float_right) + { + if(m_floats_right.empty()) + { + m_floats_right.push_back(std::move(fb)); + } else + { + bool inserted = false; + for(auto i = m_floats_right.begin(); i != m_floats_right.end(); i++) + { + if(fb.pos.left() < i->pos.left()) + { + m_floats_right.insert(i, std::move(fb)); + inserted = true; + break; + } + } + if(!inserted) + { + m_floats_right.push_back(fb); + } + } + m_cahe_line_right.invalidate(); + } + } else + { + element::ptr el_parent = parent(); + if (el_parent) + { + el_parent->add_float(el, x + m_pos.x, y + m_pos.y); + } + } +} + +int litehtml::html_tag::find_next_line_top( int top, int width, int def_right ) +{ + if(is_floats_holder()) + { + int new_top = top; + int_vector points; + + for(const auto& fb : m_floats_left) + { + if(fb.pos.top() >= top) + { + if(find(points.begin(), points.end(), fb.pos.top()) == points.end()) + { + points.push_back(fb.pos.top()); + } + } + if (fb.pos.bottom() >= top) + { + if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) + { + points.push_back(fb.pos.bottom()); + } + } + } + + for (const auto& fb : m_floats_right) + { + if (fb.pos.top() >= top) + { + if (find(points.begin(), points.end(), fb.pos.top()) == points.end()) + { + points.push_back(fb.pos.top()); + } + } + if (fb.pos.bottom() >= top) + { + if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) + { + points.push_back(fb.pos.bottom()); + } + } + } + + if(!points.empty()) + { + sort(points.begin(), points.end(), std::less<int>( )); + new_top = points.back(); + + for(auto pt : points) + { + int pos_left = 0; + int pos_right = def_right; + get_line_left_right(pt, def_right, pos_left, pos_right); + + if(pos_right - pos_left >= width) + { + new_top = pt; + break; + } + } + } + return new_top; + } + element::ptr el_parent = parent(); + if (el_parent) + { + int new_top = el_parent->find_next_line_top(top + m_pos.y, width, def_right + m_pos.x); + return new_top - m_pos.y; + } + return 0; +} + +void litehtml::html_tag::parse_background() +{ + // parse background-color + m_bg.m_color = get_color(_t("background-color"), false, web_color(0, 0, 0, 0)); + + // parse background-position + const tchar_t* str = get_style_property(_t("background-position"), false, _t("0% 0%")); + if(str) + { + string_vector res; + split_string(str, res, _t(" \t")); + if(!res.empty()) + { + if(res.size() == 1) + { + if( value_in_list(res[0], _t("left;right;center")) ) + { + m_bg.m_position.x.fromString(res[0], _t("left;right;center")); + m_bg.m_position.y.set_value(50, css_units_percentage); + } else if( value_in_list(res[0], _t("top;bottom;center")) ) + { + m_bg.m_position.y.fromString(res[0], _t("top;bottom;center")); + m_bg.m_position.x.set_value(50, css_units_percentage); + } else + { + m_bg.m_position.x.fromString(res[0], _t("left;right;center")); + m_bg.m_position.y.set_value(50, css_units_percentage); + } + } else + { + if(value_in_list(res[0], _t("left;right"))) + { + m_bg.m_position.x.fromString(res[0], _t("left;right;center")); + m_bg.m_position.y.fromString(res[1], _t("top;bottom;center")); + } else if(value_in_list(res[0], _t("top;bottom"))) + { + m_bg.m_position.x.fromString(res[1], _t("left;right;center")); + m_bg.m_position.y.fromString(res[0], _t("top;bottom;center")); + } else if(value_in_list(res[1], _t("left;right"))) + { + m_bg.m_position.x.fromString(res[1], _t("left;right;center")); + m_bg.m_position.y.fromString(res[0], _t("top;bottom;center")); + }else if(value_in_list(res[1], _t("top;bottom"))) + { + m_bg.m_position.x.fromString(res[0], _t("left;right;center")); + m_bg.m_position.y.fromString(res[1], _t("top;bottom;center")); + } else + { + m_bg.m_position.x.fromString(res[0], _t("left;right;center")); + m_bg.m_position.y.fromString(res[1], _t("top;bottom;center")); + } + } + + if(m_bg.m_position.x.is_predefined()) + { + switch(m_bg.m_position.x.predef()) + { + case 0: + m_bg.m_position.x.set_value(0, css_units_percentage); + break; + case 1: + m_bg.m_position.x.set_value(100, css_units_percentage); + break; + case 2: + m_bg.m_position.x.set_value(50, css_units_percentage); + break; + } + } + if(m_bg.m_position.y.is_predefined()) + { + switch(m_bg.m_position.y.predef()) + { + case 0: + m_bg.m_position.y.set_value(0, css_units_percentage); + break; + case 1: + m_bg.m_position.y.set_value(100, css_units_percentage); + break; + case 2: + m_bg.m_position.y.set_value(50, css_units_percentage); + break; + } + } + } else + { + m_bg.m_position.x.set_value(0, css_units_percentage); + m_bg.m_position.y.set_value(0, css_units_percentage); + } + } else + { + m_bg.m_position.y.set_value(0, css_units_percentage); + m_bg.m_position.x.set_value(0, css_units_percentage); + } + + str = get_style_property(_t("background-size"), false, _t("auto")); + if(str) + { + string_vector res; + split_string(str, res, _t(" \t")); + if(!res.empty()) + { + m_bg.m_position.width.fromString(res[0], background_size_strings); + if(res.size() > 1) + { + m_bg.m_position.height.fromString(res[1], background_size_strings); + } else + { + m_bg.m_position.height.predef(background_size_auto); + } + } else + { + m_bg.m_position.width.predef(background_size_auto); + m_bg.m_position.height.predef(background_size_auto); + } + } + + document::ptr doc = get_document(); + + doc->cvt_units(m_bg.m_position.x, m_font_size); + doc->cvt_units(m_bg.m_position.y, m_font_size); + doc->cvt_units(m_bg.m_position.width, m_font_size); + doc->cvt_units(m_bg.m_position.height, m_font_size); + + // parse background_attachment + m_bg.m_attachment = (background_attachment) value_index( + get_style_property(_t("background-attachment"), false, _t("scroll")), + background_attachment_strings, + background_attachment_scroll); + + // parse background_attachment + m_bg.m_repeat = (background_repeat) value_index( + get_style_property(_t("background-repeat"), false, _t("repeat")), + background_repeat_strings, + background_repeat_repeat); + + // parse background_clip + m_bg.m_clip = (background_box) value_index( + get_style_property(_t("background-clip"), false, _t("border-box")), + background_box_strings, + background_box_border); + + // parse background_origin + m_bg.m_origin = (background_box) value_index( + get_style_property(_t("background-origin"), false, _t("padding-box")), + background_box_strings, + background_box_content); + + // parse background-image + css::parse_css_url(get_style_property(_t("background-image"), false, _t("")), m_bg.m_image); + m_bg.m_baseurl = get_style_property(_t("background-image-baseurl"), false, _t("")); + + if(!m_bg.m_image.empty()) + { + doc->container()->load_image(m_bg.m_image.c_str(), m_bg.m_baseurl.empty() ? nullptr : m_bg.m_baseurl.c_str(), true); + } +} + +void litehtml::html_tag::add_positioned(const element::ptr &el) +{ + if (m_el_position != element_position_static || (!have_parent())) + { + m_positioned.push_back(el); + } else + { + element::ptr el_parent = parent(); + if (el_parent) + { + el_parent->add_positioned(el); + } + } +} + +void litehtml::html_tag::calc_outlines( int parent_width ) +{ + m_padding.left = m_css_padding.left.calc_percent(parent_width); + m_padding.right = m_css_padding.right.calc_percent(parent_width); + + m_borders.left = m_css_borders.left.width.calc_percent(parent_width); + m_borders.right = m_css_borders.right.width.calc_percent(parent_width); + + m_margins.left = m_css_margins.left.calc_percent(parent_width); + m_margins.right = m_css_margins.right.calc_percent(parent_width); + + m_margins.top = m_css_margins.top.calc_percent(parent_width); + m_margins.bottom = m_css_margins.bottom.calc_percent(parent_width); + + m_padding.top = m_css_padding.top.calc_percent(parent_width); + m_padding.bottom = m_css_padding.bottom.calc_percent(parent_width); +} + +void litehtml::html_tag::calc_auto_margins(int parent_width) +{ + if ((m_display == display_block || m_display == display_table) && + get_element_position() != element_position_absolute && + m_float == float_none) + { + if (m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined()) + { + int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right; + if (el_width <= parent_width) + { + m_margins.left = (parent_width - el_width) / 2; + m_margins.right = (parent_width - el_width) - m_margins.left; + } + else + { + m_margins.left = 0; + m_margins.right = 0; + } + } + else if (m_css_margins.left.is_predefined() && !m_css_margins.right.is_predefined()) + { + int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right; + m_margins.left = parent_width - el_width; + if (m_margins.left < 0) m_margins.left = 0; + } + else if (!m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined()) + { + int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left; + m_margins.right = parent_width - el_width; + if (m_margins.right < 0) m_margins.right = 0; + } + } +} + +void litehtml::html_tag::parse_attributes() +{ + for(auto& el : m_children) + { + el->parse_attributes(); + } +} + +void litehtml::html_tag::get_text( tstring& text ) +{ + for (auto& el : m_children) + { + el->get_text(text); + } +} + +bool litehtml::html_tag::is_body() const +{ + return false; +} + +void litehtml::html_tag::set_data( const tchar_t* /*data*/ ) +{ + +} + +void litehtml::html_tag::get_inline_boxes( position::vector& boxes ) +{ + litehtml::box* old_box = nullptr; + position pos; + for(auto& el : m_children) + { + if(!el->skip()) + { + if(el->m_box) + { + if(el->m_box != old_box) + { + if(old_box) + { + if(boxes.empty()) + { + pos.x -= m_padding.left + m_borders.left; + pos.width += m_padding.left + m_borders.left; + } + boxes.push_back(pos); + } + old_box = el->m_box; + pos.x = el->left() + el->margin_left(); + pos.y = el->top() - m_padding.top - m_borders.top; + pos.width = 0; + pos.height = 0; + } + pos.width = el->right() - pos.x - el->margin_right() - el->margin_left(); + pos.height = std::max(pos.height, el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom); + } else if(el->get_display() == display_inline) + { + position::vector sub_boxes; + el->get_inline_boxes(sub_boxes); + if(!sub_boxes.empty()) + { + sub_boxes.rbegin()->width += el->margin_right(); + if(boxes.empty()) + { + if(m_padding.left + m_borders.left > 0) + { + position padding_box = (*sub_boxes.begin()); + padding_box.x -= m_padding.left + m_borders.left + el->margin_left(); + padding_box.width = m_padding.left + m_borders.left + el->margin_left(); + boxes.push_back(padding_box); + } + } + + sub_boxes.rbegin()->width += el->margin_right(); + + boxes.insert(boxes.end(), sub_boxes.begin(), sub_boxes.end()); + } + } + } + } + if(pos.width || pos.height) + { + if(boxes.empty()) + { + pos.x -= m_padding.left + m_borders.left; + pos.width += m_padding.left + m_borders.left; + } + boxes.push_back(pos); + } + if(!boxes.empty()) + { + if(m_padding.right + m_borders.right > 0) + { + boxes.back().width += m_padding.right + m_borders.right; + } + } +} + +bool litehtml::html_tag::on_mouse_over() +{ + bool ret = false; + + element::ptr el = shared_from_this(); + while(el) + { + if(el->set_pseudo_class(_t("hover"), true)) + { + ret = true; + } + el = el->parent(); + } + + return ret; +} + +bool litehtml::html_tag::find_styles_changes( position::vector& redraw_boxes, int x, int y ) +{ + if(m_display == display_inline_text) + { + return false; + } + + bool ret = false; + bool apply = false; + for (const auto& used_style : m_used_styles) + { + if(used_style->m_selector->is_media_valid()) + { + int res = select(*(used_style->m_selector), true); + if( (res == select_no_match && used_style->m_used) || (res == select_match && !used_style->m_used) ) + { + apply = true; + break; + } + } + } + + if(apply) + { + if(m_display == display_inline || m_display == display_table_row) + { + position::vector boxes; + get_inline_boxes(boxes); + for(auto & box : boxes) + { + box.x += x; + box.y += y; + redraw_boxes.push_back(box); + } + } else + { + position pos = m_pos; + if(m_el_position != element_position_fixed) + { + pos.x += x; + pos.y += y; + } + pos += m_padding; + pos += m_borders; + redraw_boxes.push_back(pos); + } + + ret = true; + refresh_styles(); + parse_styles(); + } + for (auto& el : m_children) + { + if(!el->skip()) + { + if(m_el_position != element_position_fixed) + { + if(el->find_styles_changes(redraw_boxes, x + m_pos.x, y + m_pos.y)) + { + ret = true; + } + } else + { + if(el->find_styles_changes(redraw_boxes, m_pos.x, m_pos.y)) + { + ret = true; + } + } + } + } + return ret; +} + +bool litehtml::html_tag::on_mouse_leave() +{ + bool ret = false; + + element::ptr el = shared_from_this(); + while(el) + { + if(el->set_pseudo_class(_t("hover"), false)) + { + ret = true; + } + if(el->set_pseudo_class(_t("active"), false)) + { + ret = true; + } + el = el->parent(); + } + + return ret; +} + +bool litehtml::html_tag::on_lbutton_down() +{ + bool ret = false; + + element::ptr el = shared_from_this(); + while (el) + { + if (el->set_pseudo_class(_t("active"), true)) + { + ret = true; + } + el = el->parent(); + } + + return ret; +} + +bool litehtml::html_tag::on_lbutton_up() +{ + bool ret = false; + + element::ptr el = shared_from_this(); + while (el) + { + if (el->set_pseudo_class(_t("active"), false)) + { + ret = true; + } + el = el->parent(); + } + + on_click(); + + return ret; +} + +void litehtml::html_tag::on_click() +{ + if (have_parent()) + { + element::ptr el_parent = parent(); + if (el_parent) + { + el_parent->on_click(); + } + } +} + +const litehtml::tchar_t* litehtml::html_tag::get_cursor() +{ + return get_style_property(_t("cursor"), true, nullptr); +} + +static const int font_size_table[8][7] = +{ + { 9, 9, 9, 9, 11, 14, 18}, + { 9, 9, 9, 10, 12, 15, 20}, + { 9, 9, 9, 11, 13, 17, 22}, + { 9, 9, 10, 12, 14, 18, 24}, + { 9, 9, 10, 13, 16, 20, 26}, + { 9, 9, 11, 14, 17, 21, 28}, + { 9, 10, 12, 15, 17, 23, 30}, + { 9, 10, 13, 16, 18, 24, 32} +}; + + +void litehtml::html_tag::init_font() +{ + // initialize font size + const tchar_t* str = get_style_property(_t("font-size"), false, nullptr); + + int parent_sz = 0; + int doc_font_size = get_document()->container()->get_default_font_size(); + element::ptr el_parent = parent(); + if (el_parent) + { + parent_sz = el_parent->get_font_size(); + } else + { + parent_sz = doc_font_size; + } + + + if(!str) + { + m_font_size = parent_sz; + } else + { + m_font_size = parent_sz; + + css_length sz; + sz.fromString(str, font_size_strings); + if(sz.is_predefined()) + { + int idx_in_table = doc_font_size - 9; + if(idx_in_table >= 0 && idx_in_table <= 7) + { + if(sz.predef() >= fontSize_xx_small && sz.predef() <= fontSize_xx_large) + { + m_font_size = font_size_table[idx_in_table][sz.predef()]; + } else + { + m_font_size = doc_font_size; + } + } else + { + switch(sz.predef()) + { + case fontSize_xx_small: + m_font_size = doc_font_size * 3 / 5; + break; + case fontSize_x_small: + m_font_size = doc_font_size * 3 / 4; + break; + case fontSize_small: + m_font_size = doc_font_size * 8 / 9; + break; + case fontSize_large: + m_font_size = doc_font_size * 6 / 5; + break; + case fontSize_x_large: + m_font_size = doc_font_size * 3 / 2; + break; + case fontSize_xx_large: + m_font_size = doc_font_size * 2; + break; + default: + m_font_size = doc_font_size; + break; + } + } + } else + { + if(sz.units() == css_units_percentage) + { + m_font_size = sz.calc_percent(parent_sz); + } else if(sz.units() == css_units_none) + { + m_font_size = parent_sz; + } else + { + m_font_size = get_document()->cvt_units(sz, parent_sz); + } + } + } + + // initialize font + const tchar_t* name = get_style_property(_t("font-family"), true, _t("inherit")); + const tchar_t* weight = get_style_property(_t("font-weight"), true, _t("normal")); + const tchar_t* style = get_style_property(_t("font-style"), true, _t("normal")); + const tchar_t* decoration = get_style_property(_t("text-decoration"), true, _t("none")); + + m_font = get_document()->get_font(name, m_font_size, weight, style, decoration, &m_font_metrics); +} + +bool litehtml::html_tag::is_break() const +{ + return false; +} + +void litehtml::html_tag::set_tagName( const tchar_t* tag ) +{ + m_tag = tag; + for (tchar_t& i : m_tag) + { + i = std::tolower(i, std::locale::classic()); + } +} + +void litehtml::html_tag::draw_background( uint_ptr hdc, int x, int y, const position* clip ) +{ + position pos = m_pos; + pos.x += x; + pos.y += y; + + position el_pos = pos; + el_pos += m_padding; + el_pos += m_borders; + + if(m_display != display_inline && m_display != display_table_row) + { + if(el_pos.does_intersect(clip)) + { + if (m_grid) + { + int captions_height = m_grid->captions_height(); + pos.y += captions_height; + pos.height -= captions_height; + } + + const background* bg = get_background(); + if(bg) + { + background_paint bg_paint; + init_background_paint(pos, bg_paint, bg); + + get_document()->container()->draw_background(hdc, bg_paint); + } + position border_box = pos; + border_box += m_padding; + border_box += m_borders; + + borders bdr = m_css_borders; + if(bdr.is_visible()) + { + bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); + get_document()->container()->draw_borders(hdc, bdr, border_box, !have_parent()); + } + } + } else + { + const background* bg = get_background(); + + position::vector boxes; + get_inline_boxes(boxes); + + background_paint bg_paint; + position content_box; + + for(auto box = boxes.begin(); box != boxes.end(); box++) + { + box->x += x; + box->y += y; + + if(box->does_intersect(clip)) + { + content_box = *box; + content_box -= m_borders; + content_box -= m_padding; + + if(bg) + { + init_background_paint(content_box, bg_paint, bg); + } + + css_borders bdr; + + // set left borders radius for the first box + if(box == boxes.begin()) + { + bdr.radius.bottom_left_x = m_css_borders.radius.bottom_left_x; + bdr.radius.bottom_left_y = m_css_borders.radius.bottom_left_y; + bdr.radius.top_left_x = m_css_borders.radius.top_left_x; + bdr.radius.top_left_y = m_css_borders.radius.top_left_y; + } + + // set right borders radius for the last box + if(box == boxes.end() - 1) + { + bdr.radius.bottom_right_x = m_css_borders.radius.bottom_right_x; + bdr.radius.bottom_right_y = m_css_borders.radius.bottom_right_y; + bdr.radius.top_right_x = m_css_borders.radius.top_right_x; + bdr.radius.top_right_y = m_css_borders.radius.top_right_y; + } + + + bdr.top = m_css_borders.top; + bdr.bottom = m_css_borders.bottom; + if(box == boxes.begin()) + { + bdr.left = m_css_borders.left; + } + if(box == boxes.end() - 1) + { + bdr.right = m_css_borders.right; + } + + if(bg) + { + bg_paint.border_radius = bdr.radius.calc_percents(bg_paint.border_box.width, bg_paint.border_box.width); + get_document()->container()->draw_background(hdc, bg_paint); + } + if(bdr.is_visible()) + { + borders b = bdr; + b.radius = bdr.radius.calc_percents(box->width, box->height); + get_document()->container()->draw_borders(hdc, b, *box, false); + } + } + } + } +} + +int litehtml::html_tag::render_inline(const element::ptr &container, int max_width) +{ + int ret_width = 0; + int rw = 0; + + white_space ws = get_white_space(); + bool skip_spaces = false; + if (ws == white_space_normal || + ws == white_space_nowrap || + ws == white_space_pre_line) + { + skip_spaces = true; + } + bool was_space = false; + + for (auto& el : m_children) + { + // skip spaces to make rendering a bit faster + if (skip_spaces) + { + if (el->is_white_space()) + { + if (was_space) + { + el->skip(true); + continue; + } + else + { + was_space = true; + } + } + else + { + was_space = false; + } + } + + rw = container->place_element( el, max_width ); + if(rw > ret_width) + { + ret_width = rw; + } + } + return ret_width; +} + +int litehtml::html_tag::place_element(const element::ptr &el, int max_width) +{ + if(el->get_display() == display_none) return 0; + + if(el->get_display() == display_inline) + { + return el->render_inline(shared_from_this(), max_width); + } + + element_position el_position = el->get_element_position(); + + if(el_position == element_position_absolute || el_position == element_position_fixed) + { + int line_top = 0; + if(!m_boxes.empty()) + { + if(m_boxes.back()->get_type() == box_line) + { + line_top = m_boxes.back()->top(); + if(!m_boxes.back()->is_empty()) + { + line_top += line_height(); + } + } else + { + line_top = m_boxes.back()->bottom(); + } + } + + el->render(0, line_top, max_width); + el->m_pos.x += el->content_margins_left(); + el->m_pos.y += el->content_margins_top(); + + return 0; + } + + int ret_width = 0; + + switch(el->get_float()) + { + case float_left: + { + int line_top = 0; + if(!m_boxes.empty()) + { + if(m_boxes.back()->get_type() == box_line) + { + line_top = m_boxes.back()->top(); + } else + { + line_top = m_boxes.back()->bottom(); + } + } + line_top = get_cleared_top(el, line_top); + int line_left = 0; + int line_right = max_width; + get_line_left_right(line_top, max_width, line_left, line_right); + + el->render(line_left, line_top, line_right); + if(el->right() > line_right) + { + int new_top = find_next_line_top(el->top(), el->width(), max_width); + el->m_pos.x = get_line_left(new_top) + el->content_margins_left(); + el->m_pos.y = new_top + el->content_margins_top(); + } + add_float(el, 0, 0); + ret_width = fix_line_width(max_width, float_left); + if(!ret_width) + { + ret_width = el->right(); + } + } + break; + case float_right: + { + int line_top = 0; + if(!m_boxes.empty()) + { + if(m_boxes.back()->get_type() == box_line) + { + line_top = m_boxes.back()->top(); + } else + { + line_top = m_boxes.back()->bottom(); + } + } + line_top = get_cleared_top(el, line_top); + int line_left = 0; + int line_right = max_width; + get_line_left_right(line_top, max_width, line_left, line_right); + + el->render(0, line_top, line_right); + + if(line_left + el->width() > line_right) + { + int new_top = find_next_line_top(el->top(), el->width(), max_width); + el->m_pos.x = get_line_right(new_top, max_width) - el->width() + el->content_margins_left(); + el->m_pos.y = new_top + el->content_margins_top(); + } else + { + el->m_pos.x = line_right - el->width() + el->content_margins_left(); + } + add_float(el, 0, 0); + ret_width = fix_line_width(max_width, float_right); + + if(!ret_width) + { + line_left = 0; + line_right = max_width; + get_line_left_right(line_top, max_width, line_left, line_right); + + ret_width = ret_width + (max_width - line_right); + } + } + break; + default: + { + line_context line_ctx = {0}; + line_ctx.top = 0; + if (!m_boxes.empty()) + { + line_ctx.top = m_boxes.back()->top(); + } + line_ctx.left = 0; + line_ctx.right = max_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); + + switch(el->get_display()) + { + case display_inline_block: + case display_inline_table: + ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.right); + break; + case display_block: + if(el->is_replaced() || el->is_floats_holder()) + { + element::ptr el_parent = el->parent(); + el->m_pos.width = el->get_css_width().calc_percent(line_ctx.right - line_ctx.left); + el->m_pos.height = el->get_css_height().calc_percent(el_parent ? el_parent->m_pos.height : 0); + } + el->calc_outlines(line_ctx.right - line_ctx.left); + break; + case display_inline_text: + { + litehtml::size sz; + el->get_content_size(sz, line_ctx.right); + el->m_pos = sz; + } + break; + default: + ret_width = 0; + break; + } + + bool add_box = true; + if(!m_boxes.empty()) + { + if(m_boxes.back()->can_hold(el, m_white_space)) + { + add_box = false; + } + } + if(add_box) + { + new_box(el, max_width, line_ctx); + } else if(!m_boxes.empty()) + { + line_ctx.top = m_boxes.back()->top(); + } + + if (line_ctx.top != line_ctx.calculatedTop) + { + line_ctx.left = 0; + line_ctx.right = max_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); + } + + if(!el->is_inline_box()) + { + if(m_boxes.size() == 1) + { + if(collapse_top_margin()) + { + int shift = el->margin_top(); + if(shift >= 0) + { + line_ctx.top -= shift; + m_boxes.back()->y_shift(-shift); + } + } + } else + { + int shift = 0; + int prev_margin = m_boxes[m_boxes.size() - 2]->bottom_margin(); + + if(prev_margin > el->margin_top()) + { + shift = el->margin_top(); + } else + { + shift = prev_margin; + } + if(shift >= 0) + { + line_ctx.top -= shift; + m_boxes.back()->y_shift(-shift); + } + } + } + + switch(el->get_display()) + { + case display_table: + case display_list_item: + ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()); + break; + case display_block: + case display_table_cell: + case display_table_caption: + case display_table_row: + if(el->is_replaced() || el->is_floats_holder()) + { + ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()) + line_ctx.left + (max_width - line_ctx.right); + } else + { + ret_width = el->render(0, line_ctx.top, max_width); + } + break; + default: + ret_width = 0; + break; + } + + m_boxes.back()->add_element(el); + + if(el->is_inline_box() && !el->skip()) + { + ret_width = el->right() + (max_width - line_ctx.right); + } + } + break; + } + + return ret_width; +} + +bool litehtml::html_tag::set_pseudo_class( const tchar_t* pclass, bool add ) +{ + bool ret = false; + if(add) + { + if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass) == m_pseudo_classes.end()) + { + m_pseudo_classes.push_back(pclass); + ret = true; + } + } else + { + auto pi = std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass); + if(pi != m_pseudo_classes.end()) + { + m_pseudo_classes.erase(pi); + ret = true; + } + } + return ret; +} + +bool litehtml::html_tag::set_class( const tchar_t* pclass, bool add ) +{ + string_vector classes; + bool changed = false; + + split_string( pclass, classes, _t(" ") ); + + if(add) + { + for( auto & _class : classes ) + { + if(std::find(m_class_values.begin(), m_class_values.end(), _class) == m_class_values.end()) + { + m_class_values.push_back( std::move( _class ) ); + changed = true; + } + } + } else + { + for( const auto & _class : classes ) + { + auto end = std::remove(m_class_values.begin(), m_class_values.end(), _class); + + if(end != m_class_values.end()) + { + m_class_values.erase(end, m_class_values.end()); + changed = true; + } + } + } + + if( changed ) + { + tstring class_string; + join_string(class_string, m_class_values, _t(" ")); + set_attr(_t("class"), class_string.c_str()); + + return true; + } + else + { + return false; + } + +} + +int litehtml::html_tag::line_height() const +{ + return m_line_height; +} + +bool litehtml::html_tag::is_replaced() const +{ + return false; +} + +int litehtml::html_tag::finish_last_box(bool end_of_render) +{ + int line_top = 0; + + if(!m_boxes.empty()) + { + m_boxes.back()->finish(end_of_render); + + if(m_boxes.back()->is_empty()) + { + line_top = m_boxes.back()->top(); + m_boxes.pop_back(); + } + + if(!m_boxes.empty()) + { + line_top = m_boxes.back()->bottom(); + } + } + return line_top; +} + +int litehtml::html_tag::new_box(const element::ptr &el, int max_width, line_context& line_ctx) +{ + line_ctx.top = get_cleared_top(el, finish_last_box()); + + line_ctx.left = 0; + line_ctx.right = max_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); + + if(el->is_inline_box() || el->is_floats_holder()) + { + if (el->width() > line_ctx.right - line_ctx.left) + { + line_ctx.top = find_next_line_top(line_ctx.top, el->width(), max_width); + line_ctx.left = 0; + line_ctx.right = max_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); + } + } + + int first_line_margin = 0; + if(m_boxes.empty() && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside) + { + int sz_font = get_font_size(); + first_line_margin = sz_font; + } + + if(el->is_inline_box()) + { + int text_indent = 0; + if(m_css_text_indent.val() != 0) + { + bool line_box_found = false; + for(auto & box : m_boxes) + { + if(box->get_type() == box_line) + { + line_box_found = true; + break; + } + } + if(!line_box_found) + { + text_indent = m_css_text_indent.calc_percent(max_width); + } + } + + font_metrics fm; + get_font(&fm); + m_boxes.emplace_back(std::unique_ptr<line_box>(new line_box(line_ctx.top, line_ctx.left + first_line_margin + text_indent, line_ctx.right, line_height(), fm, m_text_align))); + } else + { + m_boxes.emplace_back(std::unique_ptr<block_box>(new block_box(line_ctx.top, line_ctx.left, line_ctx.right))); + } + + return line_ctx.top; +} + +int litehtml::html_tag::get_cleared_top(const element::ptr &el, int line_top) const +{ + switch(el->get_clear()) + { + case clear_left: + { + int fh = get_left_floats_height(); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + case clear_right: + { + int fh = get_right_floats_height(); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + case clear_both: + { + int fh = get_floats_height(); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + default: + if(el->get_float() != float_none) + { + int fh = get_floats_height(el->get_float()); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + } + return line_top; +} + +litehtml::style_display litehtml::html_tag::get_display() const +{ + return m_display; +} + +litehtml::element_float litehtml::html_tag::get_float() const +{ + return m_float; +} + +bool litehtml::html_tag::is_floats_holder() const +{ + if( m_display == display_inline_block || + m_display == display_table_cell || + !have_parent() || + is_body() || + m_float != float_none || + m_el_position == element_position_absolute || + m_el_position == element_position_fixed || + m_overflow > overflow_visible) + { + return true; + } + return false; +} + +bool litehtml::html_tag::is_first_child_inline(const element::ptr& el) const +{ + if(!m_children.empty()) + { + for (const auto& this_el : m_children) + { + if (!this_el->is_white_space()) + { + if (el == this_el) + { + return true; + } + if (this_el->get_display() == display_inline) + { + if (this_el->have_inline_child()) + { + return false; + } + } else + { + return false; + } + } + } + } + return false; +} + +bool litehtml::html_tag::is_last_child_inline(const element::ptr& el) +{ + if(!m_children.empty()) + { + for (auto this_el = m_children.rbegin(); this_el < m_children.rend(); ++this_el) + { + if (!(*this_el)->is_white_space()) + { + if (el == (*this_el)) + { + return true; + } + if ((*this_el)->get_display() == display_inline) + { + if ((*this_el)->have_inline_child()) + { + return false; + } + } else + { + return false; + } + } + } + } + return false; +} + +litehtml::white_space litehtml::html_tag::get_white_space() const +{ + return m_white_space; +} + +litehtml::vertical_align litehtml::html_tag::get_vertical_align() const +{ + return m_vertical_align; +} + +litehtml::css_length litehtml::html_tag::get_css_left() const +{ + return m_css_offsets.left; +} + +litehtml::css_length litehtml::html_tag::get_css_right() const +{ + return m_css_offsets.right; +} + +litehtml::css_length litehtml::html_tag::get_css_top() const +{ + return m_css_offsets.top; +} + +litehtml::css_length litehtml::html_tag::get_css_bottom() const +{ + return m_css_offsets.bottom; +} + + +litehtml::css_offsets litehtml::html_tag::get_css_offsets() const +{ + return m_css_offsets; +} + +litehtml::element_clear litehtml::html_tag::get_clear() const +{ + return m_clear; +} + +litehtml::css_length litehtml::html_tag::get_css_width() const +{ + return m_css_width; +} + +litehtml::css_length litehtml::html_tag::get_css_height() const +{ + return m_css_height; +} + +size_t litehtml::html_tag::get_children_count() const +{ + return m_children.size(); +} + +litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const +{ + return m_children[idx]; +} + +void litehtml::html_tag::set_css_width( css_length& w ) +{ + m_css_width = w; +} + +void litehtml::html_tag::apply_vertical_align() +{ + if(!m_boxes.empty()) + { + int add = 0; + int content_height = m_boxes.back()->bottom(); + + if(m_pos.height > content_height) + { + switch(m_vertical_align) + { + case va_middle: + add = (m_pos.height - content_height) / 2; + break; + case va_bottom: + add = m_pos.height - content_height; + break; + default: + add = 0; + break; + } + } + + if(add) + { + for(auto & box : m_boxes) + { + box->y_shift(add); + } + } + } +} + +litehtml::element_position litehtml::html_tag::get_element_position(css_offsets* offsets) const +{ + if(offsets && m_el_position != element_position_static) + { + *offsets = m_css_offsets; + } + return m_el_position; +} + +void litehtml::html_tag::init_background_paint(position pos, background_paint &bg_paint, const background* bg) +{ + if(!bg) return; + + bg_paint = *bg; + position content_box = pos; + position padding_box = pos; + padding_box += m_padding; + position border_box = padding_box; + border_box += m_borders; + + switch(bg->m_clip) + { + case litehtml::background_box_padding: + bg_paint.clip_box = padding_box; + break; + case litehtml::background_box_content: + bg_paint.clip_box = content_box; + break; + default: + bg_paint.clip_box = border_box; + break; + } + + switch(bg->m_origin) + { + case litehtml::background_box_border: + bg_paint.origin_box = border_box; + break; + case litehtml::background_box_content: + bg_paint.origin_box = content_box; + break; + default: + bg_paint.origin_box = padding_box; + break; + } + + if(!bg_paint.image.empty()) + { + get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size); + if(bg_paint.image_size.width && bg_paint.image_size.height) + { + litehtml::size img_new_sz = bg_paint.image_size; + double img_ar_width = (double) bg_paint.image_size.width / (double) bg_paint.image_size.height; + double img_ar_height = (double) bg_paint.image_size.height / (double) bg_paint.image_size.width; + + + if(bg->m_position.width.is_predefined()) + { + switch(bg->m_position.width.predef()) + { + case litehtml::background_size_contain: + if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height ) + { + img_new_sz.width = bg_paint.origin_box.width; + img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); + } else + { + img_new_sz.height = bg_paint.origin_box.height; + img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); + } + break; + case litehtml::background_size_cover: + if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height ) + { + img_new_sz.width = bg_paint.origin_box.width; + img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); + } else + { + img_new_sz.height = bg_paint.origin_box.height; + img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); + } + break; + break; + case litehtml::background_size_auto: + if(!bg->m_position.height.is_predefined()) + { + img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height); + img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width); + } + break; + } + } else + { + img_new_sz.width = bg->m_position.width.calc_percent(bg_paint.origin_box.width); + if(bg->m_position.height.is_predefined()) + { + img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height); + } else + { + img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height); + } + } + + bg_paint.image_size = img_new_sz; + bg_paint.position_x = bg_paint.origin_box.x + (int) bg->m_position.x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width); + bg_paint.position_y = bg_paint.origin_box.y + (int) bg->m_position.y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height); + } + + } + bg_paint.border_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); + bg_paint.border_box = border_box; + bg_paint.is_root = !have_parent(); +} + +litehtml::visibility litehtml::html_tag::get_visibility() const +{ + return m_visibility; +} + +void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position &pos ) +{ + list_marker lm; + + const tchar_t* list_image = get_style_property(_t("list-style-image"), true, nullptr); + size img_size; + if(list_image) + { + css::parse_css_url(list_image, lm.image); + lm.baseurl = get_style_property(_t("list-style-image-baseurl"), true, nullptr); + get_document()->container()->get_image_size(lm.image.c_str(), lm.baseurl, img_size); + } else + { + lm.baseurl = nullptr; + } + + int ln_height = line_height(); + int sz_font = get_font_size(); + lm.pos.x = pos.x; + lm.pos.width = sz_font - sz_font * 2 / 3; + lm.color = get_color(_t("color"), true, web_color(0, 0, 0)); + lm.marker_type = m_list_style_type; + lm.font = get_font(); + + if (m_list_style_type >= list_style_type_armenian) + { + lm.pos.y = pos.y; + lm.pos.height = pos.height; + lm.index = (unsigned char) get_attr(_t("list_index"), _t(""))[0]; + } + else + { + lm.pos.height = sz_font - sz_font * 2 / 3; + lm.pos.y = pos.y + ln_height / 2 - lm.pos.height / 2; + lm.index = -1; + } + + if(img_size.width && img_size.height) + { + if(lm.pos.y + img_size.height > pos.y + pos.height) + { + lm.pos.y = pos.y + pos.height - img_size.height; + } + if(img_size.width > lm.pos.width) + { + lm.pos.x -= img_size.width - lm.pos.width; + } + + lm.pos.width = img_size.width; + lm.pos.height = img_size.height; + } + + if (m_list_style_position == list_style_position_outside) + { + if (m_list_style_type >= list_style_type_armenian) + { + auto tw_space = get_document()->container()->text_width(_t(" "), lm.font); + lm.pos.x = pos.x - tw_space * 2; + lm.pos.width = tw_space; + } + else + { + lm.pos.x -= sz_font; + } + } + + if (m_list_style_type >= list_style_type_armenian) + { + auto marker_text = get_list_marker_text(lm.index); + lm.pos.height = ln_height; + if (marker_text.empty()) + { + get_document()->container()->draw_list_marker(hdc, lm); + } + else + { + marker_text += _t("."); + auto tw = get_document()->container()->text_width(marker_text.c_str(), lm.font); + auto text_pos = lm.pos; + text_pos.move_to(text_pos.right() - tw, text_pos.y); + text_pos.width = tw; + get_document()->container()->draw_text(hdc, marker_text.c_str(), lm.font, lm.color, text_pos); + } + } + else + { + get_document()->container()->draw_list_marker(hdc, lm); + } +} + +litehtml::tstring litehtml::html_tag::get_list_marker_text(int index) +{ + switch (m_list_style_type) + { + case litehtml::list_style_type_decimal: + return t_to_string(index); + case litehtml::list_style_type_decimal_leading_zero: + { + auto txt = t_to_string(index); + if (txt.length() == 1) + { + txt = _t("0") + txt; + } + return txt; + } + case litehtml::list_style_type_lower_latin: + case litehtml::list_style_type_lower_alpha: + return num_cvt::to_latin_lower(index); + case litehtml::list_style_type_lower_greek: + return num_cvt::to_greek_lower(index); + case litehtml::list_style_type_upper_alpha: + case litehtml::list_style_type_upper_latin: + return num_cvt::to_latin_upper(index); + case litehtml::list_style_type_lower_roman: + return num_cvt::to_roman_lower(index); + case litehtml::list_style_type_upper_roman: + return num_cvt::to_roman_upper(index); + default: + return _t(""); +// case litehtml::list_style_type_armenian: +// case litehtml::list_style_type_georgian: +// case litehtml::list_style_type_hebrew: +// case litehtml::list_style_type_hiragana: +// case litehtml::list_style_type_hiragana_iroha: +// case litehtml::list_style_type_katakana: +// case litehtml::list_style_type_katakana_iroha: +// case litehtml::list_style_type_none: +// case litehtml::list_style_type_circle: +// case litehtml::list_style_type_disc: +// case litehtml::list_style_type_square: +// case litehtml::list_style_type_cjk_ideographic: +// break; + } +} + +void litehtml::html_tag::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ) +{ + if (m_display == display_table || m_display == display_inline_table) + { + draw_children_table(hdc, x, y, clip, flag, zindex); + } + else + { + draw_children_box(hdc, x, y, clip, flag, zindex); + } +} + +bool litehtml::html_tag::fetch_positioned() +{ + bool ret = false; + + m_positioned.clear(); + + litehtml::element_position el_pos; + + for(auto& el : m_children) + { + el_pos = el->get_element_position(); + if (el_pos != element_position_static) + { + add_positioned(el); + } + if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed)) + { + ret = true; + } + if(el->fetch_positioned()) + { + ret = true; + } + } + return ret; +} + +int litehtml::html_tag::get_zindex() const +{ + return m_z_index; +} + +void litehtml::html_tag::render_positioned(render_type rt) +{ + position wnd_position; + get_document()->container()->get_client_rect(wnd_position); + + element_position el_position; + bool process; + for (auto& el : m_positioned) + { + el_position = el->get_element_position(); + + process = false; + if(el->get_display() != display_none) + { + if(el_position == element_position_absolute) + { + if(rt != render_fixed_only) + { + process = true; + } + } else if(el_position == element_position_fixed) + { + if(rt != render_no_fixed) + { + process = true; + } + } + } + + if(process) + { + int parent_height = 0; + int parent_width = 0; + int client_x = 0; + int client_y = 0; + if(el_position == element_position_fixed) + { + parent_height = wnd_position.height; + parent_width = wnd_position.width; + client_x = wnd_position.left(); + client_y = wnd_position.top(); + } else + { + element::ptr el_parent = el->parent(); + if(el_parent) + { + parent_height = el_parent->height(); + parent_width = el_parent->width(); + } + } + + css_length css_left = el->get_css_left(); + css_length css_right = el->get_css_right(); + css_length css_top = el->get_css_top(); + css_length css_bottom = el->get_css_bottom(); + + bool need_render = false; + + css_length el_w = el->get_css_width(); + css_length el_h = el->get_css_height(); + + int new_width = -1; + int new_height = -1; + if(el_w.units() == css_units_percentage && parent_width) + { + new_width = el_w.calc_percent(parent_width); + if(el->m_pos.width != new_width) + { + need_render = true; + el->m_pos.width = new_width; + } + } + + if(el_h.units() == css_units_percentage && parent_height) + { + new_height = el_h.calc_percent(parent_height); + if(el->m_pos.height != new_height) + { + need_render = true; + el->m_pos.height = new_height; + } + } + + bool cvt_x = false; + bool cvt_y = false; + + if(el_position == element_position_fixed) + { + if(!css_left.is_predefined() || !css_right.is_predefined()) + { + if(!css_left.is_predefined() && css_right.is_predefined()) + { + el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left(); + } else if(css_left.is_predefined() && !css_right.is_predefined()) + { + el->m_pos.x = parent_width - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right(); + } else + { + el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left(); + el->m_pos.width = parent_width - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right()); + need_render = true; + } + } + + if(!css_top.is_predefined() || !css_bottom.is_predefined()) + { + if(!css_top.is_predefined() && css_bottom.is_predefined()) + { + el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top(); + } else if(css_top.is_predefined() && !css_bottom.is_predefined()) + { + el->m_pos.y = parent_height - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom(); + } else + { + el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top(); + el->m_pos.height = parent_height - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom()); + need_render = true; + } + } + } else + { + if(!css_left.is_predefined() || !css_right.is_predefined()) + { + if(!css_left.is_predefined() && css_right.is_predefined()) + { + el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left; + } else if(css_left.is_predefined() && !css_right.is_predefined()) + { + el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right(); + } else + { + el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left; + el->m_pos.width = m_pos.width + m_padding.left + m_padding.right - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right()); + if (new_width != -1) + { + el->m_pos.x += (el->m_pos.width - new_width) / 2; + el->m_pos.width = new_width; + } + need_render = true; + } + cvt_x = true; + } + + if(!css_top.is_predefined() || !css_bottom.is_predefined()) + { + if(!css_top.is_predefined() && css_bottom.is_predefined()) + { + el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top; + } else if(css_top.is_predefined() && !css_bottom.is_predefined()) + { + el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom(); + } else + { + el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top; + el->m_pos.height = m_pos.height + m_padding.top + m_padding.bottom - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom()); + if (new_height != -1) + { + el->m_pos.y += (el->m_pos.height - new_height) / 2; + el->m_pos.height = new_height; + } + need_render = true; + } + cvt_y = true; + } + } + + if(cvt_x || cvt_y) + { + int offset_x = 0; + int offset_y = 0; + element::ptr cur_el = el->parent(); + element::ptr this_el = shared_from_this(); + while(cur_el && cur_el != this_el) + { + offset_x += cur_el->m_pos.x; + offset_y += cur_el->m_pos.y; + cur_el = cur_el->parent(); + } + if(cvt_x) el->m_pos.x -= offset_x; + if(cvt_y) el->m_pos.y -= offset_y; + } + + if(need_render) + { + position pos = el->m_pos; + el->render(el->left(), el->top(), el->width(), true); + el->m_pos = pos; + } + + if(el_position == element_position_fixed) + { + position fixed_pos; + el->get_redraw_box(fixed_pos); + get_document()->add_fixed_box(fixed_pos); + } + } + + el->render_positioned(); + } + + if(!m_positioned.empty()) + { + std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const litehtml::element::ptr& Left, const litehtml::element::ptr& Right) + { + return (Left->get_zindex() < Right->get_zindex()); + }); + } +} + +void litehtml::html_tag::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ) +{ + if(!is_visible()) return; + + std::map<int, bool> z_indexes; + if(with_positioned) + { + for(const auto& idx : m_positioned) + { + z_indexes[idx->get_zindex()]; + } + + for(const auto& idx : z_indexes) + { + if(idx.first < 0) + { + draw_children(hdc, x, y, clip, draw_positioned, idx.first); + } + } + } + draw_children(hdc, x, y, clip, draw_block, 0); + draw_children(hdc, x, y, clip, draw_floats, 0); + draw_children(hdc, x, y, clip, draw_inlines, 0); + if(with_positioned) + { + for(auto& z_index : z_indexes) + { + if(z_index.first == 0) + { + draw_children(hdc, x, y, clip, draw_positioned, z_index.first); + } + } + + for(auto& z_index : z_indexes) + { + if(z_index.first > 0) + { + draw_children(hdc, x, y, clip, draw_positioned, z_index.first); + } + } + } +} + +litehtml::overflow litehtml::html_tag::get_overflow() const +{ + return m_overflow; +} + +bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const +{ + int idx = 1; + for(const auto& child : m_children) + { + if(child->get_display() != display_inline_text) + { + if( (!of_type) || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) ) + { + if(el == child) + { + if(num != 0) + { + if((idx - off) >= 0 && (idx - off) % num == 0) + { + return true; + } + + } else if(idx == off) + { + return true; + } + return false; + } + idx++; + } + if(el == child) break; + } + } + return false; +} + +bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const +{ + int idx = 1; + for(auto child = m_children.rbegin(); child != m_children.rend(); child++) + { + if((*child)->get_display() != display_inline_text) + { + if( !of_type || (of_type && !t_strcmp(el->get_tagName(), (*child)->get_tagName())) ) + { + if(el == (*child)) + { + if(num != 0) + { + if((idx - off) >= 0 && (idx - off) % num == 0) + { + return true; + } + + } else if(idx == off) + { + return true; + } + return false; + } + idx++; + } + if(el == (*child)) break; + } + } + return false; +} + +void litehtml::html_tag::parse_nth_child_params( const tstring& param, int &num, int &off ) +{ + if(param == _t("odd")) + { + num = 2; + off = 1; + } else if(param == _t("even")) + { + num = 2; + off = 0; + } else + { + string_vector tokens; + split_string(param, tokens, _t(" n"), _t("n")); + + tstring s_num; + tstring s_off; + + tstring s_int; + for(const auto& token : tokens) + { + if(token == _t("n")) + { + s_num = s_int; + s_int.clear(); + } else + { + s_int += token; + } + } + s_off = s_int; + + num = t_atoi(s_num.c_str()); + off = t_atoi(s_off.c_str()); + } +} + +void litehtml::html_tag::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ ) +{ + if(is_visible() && m_el_position != element_position_fixed) + { + element::calc_document_size(sz, x, y); + + if(m_overflow == overflow_visible) + { + for(auto& el : m_children) + { + el->calc_document_size(sz, x + m_pos.x, y + m_pos.y); + } + } + + // root element (<html>) must to cover entire window + if(!have_parent()) + { + position client_pos; + get_document()->container()->get_client_rect(client_pos); + m_pos.height = std::max(sz.height, client_pos.height) - content_margins_top() - content_margins_bottom(); + m_pos.width = std::max(sz.width, client_pos.width) - content_margins_left() - content_margins_right(); + } + } +} + + +void litehtml::html_tag::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/) +{ + if(is_visible()) + { + element::get_redraw_box(pos, x, y); + + if(m_overflow == overflow_visible) + { + for(auto& el : m_children) + { + if(el->get_element_position() != element_position_fixed) + { + el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y); + } + } + } + } +} + +litehtml::element::ptr litehtml::html_tag::find_adjacent_sibling( const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/ ) +{ + element::ptr ret; + for(auto& e : m_children) + { + if(e->get_display() != display_inline_text) + { + if(e == el) + { + if(ret) + { + int res = ret->select(selector, apply_pseudo); + if(res != select_no_match) + { + if(is_pseudo) + { + if(res & select_match_pseudo_class) + { + *is_pseudo = true; + } else + { + *is_pseudo = false; + } + } + return ret; + } + } + return nullptr; + } else + { + ret = e; + } + } + } + return nullptr; +} + +litehtml::element::ptr litehtml::html_tag::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) +{ + element::ptr ret = nullptr; + for(auto& e : m_children) + { + if(e->get_display() != display_inline_text) + { + if(e == el) + { + return ret; + } else if(!ret) + { + int res = e->select(selector, apply_pseudo); + if(res != select_no_match) + { + if(is_pseudo) + { + if(res & select_match_pseudo_class) + { + *is_pseudo = true; + } else + { + *is_pseudo = false; + } + } + ret = e; + } + } + } + } + return nullptr; +} + +bool litehtml::html_tag::is_only_child(const element::ptr& el, bool of_type) const +{ + int child_count = 0; + for(const auto& child : m_children) + { + if(child->get_display() != display_inline_text) + { + if( !of_type || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) ) + { + child_count++; + } + if(child_count > 1) break; + } + } + if(child_count > 1) + { + return false; + } + return true; +} + +void litehtml::html_tag::update_floats(int dy, const element::ptr &parent) +{ + if(is_floats_holder()) + { + bool reset_cache = false; + for(auto fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++) + { + if(fb->el->is_ancestor(parent)) + { + reset_cache = true; + fb->pos.y += dy; + } + } + if(reset_cache) + { + m_cahe_line_left.invalidate(); + } + reset_cache = false; + for(auto fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++) + { + if(fb->el->is_ancestor(parent)) + { + reset_cache = true; + fb->pos.y += dy; + } + } + if(reset_cache) + { + m_cahe_line_right.invalidate(); + } + } else + { + element::ptr el_parent = this->parent(); + if (el_parent) + { + el_parent->update_floats(dy, parent); + } + } +} + +void litehtml::html_tag::remove_before_after() +{ + if(!m_children.empty()) + { + if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) ) + { + m_children.erase(m_children.begin()); + } + } + if(!m_children.empty()) + { + if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) ) + { + m_children.erase(m_children.end() - 1); + } + } +} + +litehtml::element::ptr litehtml::html_tag::get_element_before() +{ + if(!m_children.empty()) + { + if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) ) + { + return m_children.front(); + } + } + element::ptr el = std::make_shared<el_before>(get_document()); + el->parent(shared_from_this()); + m_children.insert(m_children.begin(), el); + return el; +} + +litehtml::element::ptr litehtml::html_tag::get_element_after() +{ + if(!m_children.empty()) + { + if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) ) + { + return m_children.back(); + } + } + element::ptr el = std::make_shared<el_after>(get_document()); + appendChild(el); + return el; +} + +void litehtml::html_tag::add_style(const tstring& style, const tstring& baseurl) +{ + m_style.add(style.c_str(), baseurl.c_str(), this); +} + +bool litehtml::html_tag::have_inline_child() const +{ + if(!m_children.empty()) + { + for(const auto& el : m_children) + { + if(!el->is_white_space()) + { + return true; + } + } + } + return false; +} + +void litehtml::html_tag::refresh_styles() +{ + remove_before_after(); + + for (auto& el : m_children) + { + if(el->get_display() != display_inline_text) + { + el->refresh_styles(); + } + } + + m_style.clear(); + + for (auto& usel : m_used_styles) + { + usel->m_used = false; + + if(usel->m_selector->is_media_valid()) + { + int apply = select(*usel->m_selector, false); + + if(apply != select_no_match) + { + if(apply & select_match_pseudo_class) + { + if(select(*usel->m_selector, true)) + { + if(apply & select_match_with_after) + { + element::ptr el = get_element_after(); + if(el) + { + el->add_style(usel->m_selector->m_style, usel->m_selector->m_baseurl); + } + } else if(apply & select_match_with_before) + { + element::ptr el = get_element_before(); + if(el) + { + el->add_style(usel->m_selector->m_style, usel->m_selector->m_baseurl); + } + } + else + { + add_style(usel->m_selector->m_style, usel->m_selector->m_baseurl); + usel->m_used = true; + } + } + } else if(apply & select_match_with_after) + { + element::ptr el = get_element_after(); + if(el) + { + el->add_style(usel->m_selector->m_style, usel->m_selector->m_baseurl); + } + } else if(apply & select_match_with_before) + { + element::ptr el = get_element_before(); + if(el) + { + el->add_style(usel->m_selector->m_style, usel->m_selector->m_baseurl); + } + } else + { + add_style(usel->m_selector->m_style, usel->m_selector->m_baseurl); + usel->m_used = true; + } + } + } + } +} + +litehtml::element::ptr litehtml::html_tag::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex) +{ + element::ptr ret = nullptr; + + if(m_overflow > overflow_visible) + { + if(!m_pos.is_point_inside(x, y)) + { + return ret; + } + } + + position pos = m_pos; + pos.x = x - pos.x; + pos.y = y - pos.y; + + for(auto i = m_children.rbegin(); i != m_children.rend() && !ret; i++) + { + element::ptr el = (*i); + + if(el->is_visible() && el->get_display() != display_inline_text) + { + switch(flag) + { + case draw_positioned: + if(el->is_positioned() && el->get_zindex() == zindex) + { + if(el->get_element_position() == element_position_fixed) + { + ret = el->get_element_by_point(client_x, client_y, client_x, client_y); + if(!ret && (*i)->is_point_inside(client_x, client_y)) + { + ret = (*i); + } + } else + { + ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y); + if(!ret && (*i)->is_point_inside(pos.x, pos.y)) + { + ret = (*i); + } + } + el = nullptr; + } + break; + case draw_block: + if(!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) + { + if(el->is_point_inside(pos.x, pos.y)) + { + ret = el; + } + } + break; + case draw_floats: + if(el->get_float() != float_none && !el->is_positioned()) + { + ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y); + + if(!ret && (*i)->is_point_inside(pos.x, pos.y)) + { + ret = (*i); + } + el = nullptr; + } + break; + case draw_inlines: + if(el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) + { + if(el->get_display() == display_inline_block) + { + ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y); + el = nullptr; + } + if(!ret && (*i)->is_point_inside(pos.x, pos.y)) + { + ret = (*i); + } + } + break; + default: + break; + } + + if(el && !el->is_positioned()) + { + if(flag == draw_positioned) + { + element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex); + if(child) + { + ret = child; + } + } else + { + if( el->get_float() == float_none && + el->get_display() != display_inline_block) + { + element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex); + if(child) + { + ret = child; + } + } + } + } + } + } + + return ret; +} + +litehtml::element::ptr litehtml::html_tag::get_element_by_point(int x, int y, int client_x, int client_y) +{ + if(!is_visible()) return nullptr; + + element::ptr ret; + + std::map<int, bool> z_indexes; + + for(const auto& i : m_positioned) + { + z_indexes[i->get_zindex()]; + } + + for(const auto& zindex : z_indexes) + { + if(zindex.first > 0) + { + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, zindex.first); + break; + } + } + if(ret) return ret; + + for(const auto& z_index : z_indexes) + { + if(z_index.first == 0) + { + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, z_index.first); + break; + } + } + if(ret) return ret; + + ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0); + if(ret) return ret; + + ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0); + if(ret) return ret; + + ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0); + if(ret) return ret; + + + for(const auto& z_index : z_indexes) + { + if(z_index.first < 0) + { + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, z_index.first); + break; + } + } + if(ret) return ret; + + if(m_el_position == element_position_fixed) + { + if(is_point_inside(client_x, client_y)) + { + ret = shared_from_this(); + } + } else + { + if(is_point_inside(x, y)) + { + ret = shared_from_this(); + } + } + + return ret; +} + +const litehtml::background* litehtml::html_tag::get_background(bool own_only) +{ + if(own_only) + { + // return own background with check for empty one + if(m_bg.m_image.empty() && !m_bg.m_color.alpha) + { + return nullptr; + } + return &m_bg; + } + + if(m_bg.m_image.empty() && !m_bg.m_color.alpha) + { + // if this is root element (<html>) try to get background from body + if (!have_parent()) + { + for (const auto& el : m_children) + { + if( el->is_body() ) + { + // return own body background + return el->get_background(true); + } + } + } + return nullptr; + } + + if(is_body()) + { + element::ptr el_parent = parent(); + if (el_parent) + { + if (!el_parent->get_background(true)) + { + // parent of body will draw background for body + return nullptr; + } + } + } + + return &m_bg; +} + +int litehtml::html_tag::render_box(int x, int y, int max_width, bool second_pass /*= false*/) +{ + int parent_width = max_width; + + calc_outlines(parent_width); + + m_pos.clear(); + m_pos.move_to(x, y); + + m_pos.x += content_margins_left(); + m_pos.y += content_margins_top(); + + int ret_width = 0; + + def_value<int> block_width(0); + + if (m_display != display_table_cell && !m_css_width.is_predefined()) + { + int w = calc_width(parent_width); + + if (m_box_sizing == box_sizing_border_box) + { + w -= m_padding.width() + m_borders.width(); + } + ret_width = max_width = block_width = w; + } + else + { + if (max_width) + { + max_width -= content_margins_left() + content_margins_right(); + } + } + + // check for max-width (on the first pass only) + if (!m_css_max_width.is_predefined() && !second_pass) + { + int mw = get_document()->cvt_units(m_css_max_width, m_font_size, parent_width); + if (m_box_sizing == box_sizing_border_box) + { + mw -= m_padding.left + m_borders.left + m_padding.right + m_borders.right; + } + if (max_width > mw) + { + max_width = mw; + } + } + + m_floats_left.clear(); + m_floats_right.clear(); + m_boxes.clear(); + m_cahe_line_left.invalidate(); + m_cahe_line_right.invalidate(); + + element_position el_position; + + int block_height = 0; + + m_pos.height = 0; + + if (get_predefined_height(block_height)) + { + m_pos.height = block_height; + } + + white_space ws = get_white_space(); + bool skip_spaces = false; + if (ws == white_space_normal || + ws == white_space_nowrap || + ws == white_space_pre_line) + { + skip_spaces = true; + } + + bool was_space = false; + + for (const auto& el : m_children) + { + // we don't need to process absolute and fixed positioned element on the second pass + if (second_pass) + { + el_position = el->get_element_position(); + if ((el_position == element_position_absolute || el_position == element_position_fixed)) continue; + } + + // skip spaces to make rendering a bit faster + if (skip_spaces) + { + if (el->is_white_space()) + { + if (was_space) + { + el->skip(true); + continue; + } + else + { + was_space = true; + } + } + else + { + was_space = false; + } + } + + // place element into rendering flow + int rw = place_element(el, max_width); + if (rw > ret_width) + { + ret_width = rw; + } + } + + finish_last_box(true); + + if (block_width.is_default() && is_inline_box()) + { + m_pos.width = ret_width; + } + else + { + m_pos.width = max_width; + } + calc_auto_margins(parent_width); + + if (!m_boxes.empty()) + { + if (collapse_top_margin()) + { + int old_top = m_margins.top; + m_margins.top = std::max(m_boxes.front()->top_margin(), m_margins.top); + if (m_margins.top != old_top) + { + update_floats(m_margins.top - old_top, shared_from_this()); + } + } + if (collapse_bottom_margin()) + { + m_margins.bottom = std::max(m_boxes.back()->bottom_margin(), m_margins.bottom); + m_pos.height = m_boxes.back()->bottom() - m_boxes.back()->bottom_margin(); + } + else + { + m_pos.height = m_boxes.back()->bottom(); + } + } + + // add the floats height to the block height + if (is_floats_holder()) + { + int floats_height = get_floats_height(); + if (floats_height > m_pos.height) + { + m_pos.height = floats_height; + } + } + + // calculate the final position + + m_pos.move_to(x, y); + m_pos.x += content_margins_left(); + m_pos.y += content_margins_top(); + + if (get_predefined_height(block_height)) + { + m_pos.height = block_height; + } + + int min_height = 0; + if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage) + { + element::ptr el_parent = parent(); + if (el_parent) + { + if (el_parent->get_predefined_height(block_height)) + { + min_height = m_css_min_height.calc_percent(block_height); + } + } + } + else + { + min_height = (int)m_css_min_height.val(); + } + if (min_height != 0 && m_box_sizing == box_sizing_border_box) + { + min_height -= m_padding.top + m_borders.top + m_padding.bottom + m_borders.bottom; + if (min_height < 0) min_height = 0; + } + + if (m_display == display_list_item) + { + const tchar_t* list_image = get_style_property(_t("list-style-image"), true, nullptr); + if (list_image) + { + tstring url; + css::parse_css_url(list_image, url); + + size sz; + const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, nullptr); + get_document()->container()->get_image_size(url.c_str(), list_image_baseurl, sz); + if (min_height < sz.height) + { + min_height = sz.height; + } + } + + } + + if (min_height > m_pos.height) + { + m_pos.height = min_height; + } + + int min_width = m_css_min_width.calc_percent(parent_width); + + if (min_width != 0 && m_box_sizing == box_sizing_border_box) + { + min_width -= m_padding.left + m_borders.left + m_padding.right + m_borders.right; + if (min_width < 0) min_width = 0; + } + + if (min_width != 0) + { + if (min_width > m_pos.width) + { + m_pos.width = min_width; + } + if (min_width > ret_width) + { + ret_width = min_width; + } + } + + ret_width += content_margins_left() + content_margins_right(); + + // re-render with new width + if (ret_width < max_width && !second_pass && have_parent()) + { + if (m_display == display_inline_block || + (m_css_width.is_predefined() && + (m_float != float_none || + m_display == display_table || + m_el_position == element_position_absolute || + m_el_position == element_position_fixed + )) + ) + { + render(x, y, ret_width, true); + m_pos.width = ret_width - (content_margins_left() + content_margins_right()); + } + } + + if (is_floats_holder() && !second_pass) + { + for (const auto& fb : m_floats_left) + { + fb.el->apply_relative_shift(fb.el->parent()->calc_width(m_pos.width)); + } + } + + + return ret_width; +} + +int litehtml::html_tag::render_table(int x, int y, int max_width, bool /*second_pass = false*/) +{ + if (!m_grid) return 0; + + int parent_width = max_width; + + calc_outlines(parent_width); + + m_pos.clear(); + m_pos.move_to(x, y); + + m_pos.x += content_margins_left(); + m_pos.y += content_margins_top(); + + def_value<int> block_width(0); + + if (!m_css_width.is_predefined()) + { + max_width = block_width = calc_width(parent_width) - m_padding.width() - m_borders.width(); + } + else + { + if (max_width) + { + max_width -= content_margins_left() + content_margins_right(); + } + } + + // Calculate table spacing + int table_width_spacing = 0; + if (m_border_collapse == border_collapse_separate) + { + table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1); + } + else + { + table_width_spacing = 0; + + if (m_grid->cols_count()) + { + table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left); + table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right); + } + + for (int col = 1; col < m_grid->cols_count(); col++) + { + table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right); + } + } + + + // Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box. + // If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum + // cell width. + // + // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur. + + if (m_grid->cols_count() == 1 && !block_width.is_default()) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + table_cell* cell = m_grid->cell(0, row); + if (cell && cell->el) + { + cell->min_width = cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing); + cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right(); + } + } + } + else + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell && cell->el) + { + if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage) + { + int css_w = m_grid->column(col).css_width.calc_percent(block_width); + int el_w = cell->el->render(0, 0, css_w); + cell->min_width = cell->max_width = std::max(css_w, el_w); + cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right(); + } + else + { + // calculate minimum content width + cell->min_width = cell->el->render(0, 0, 1); + // calculate maximum content width + cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing); + } + } + } + } + } + + // For each column, determine a maximum and minimum column width from the cells that span only that column. + // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger). + // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger). + + for (int col = 0; col < m_grid->cols_count(); col++) + { + m_grid->column(col).max_width = 0; + m_grid->column(col).min_width = 0; + for (int row = 0; row < m_grid->rows_count(); row++) + { + if (m_grid->cell(col, row)->colspan <= 1) + { + m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width); + m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width); + } + } + } + + // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together, + // they are at least as wide as the cell. Do the same for the maximum widths. + // If possible, widen all spanned columns by approximately the same amount. + + for (int col = 0; col < m_grid->cols_count(); col++) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + if (m_grid->cell(col, row)->colspan > 1) + { + int max_total_width = m_grid->column(col).max_width; + int min_total_width = m_grid->column(col).min_width; + for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++) + { + max_total_width += m_grid->column(col2).max_width; + min_total_width += m_grid->column(col2).min_width; + } + if (min_total_width < m_grid->cell(col, row)->min_width) + { + m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1); + } + if (max_total_width < m_grid->cell(col, row)->max_width) + { + m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1); + } + } + } + } + + // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the + // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN). + // If the used width is greater than MIN, the extra width should be distributed over the columns. + // + // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width, + // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is + // less than that of the containing block, use max(MAX, CAPMIN). + + + int table_width = 0; + int min_table_width = 0; + int max_table_width = 0; + + if (!block_width.is_default()) + { + table_width = m_grid->calc_table_width(block_width - table_width_spacing, false, min_table_width, max_table_width); + } + else + { + table_width = m_grid->calc_table_width(max_width - table_width_spacing, true, min_table_width, max_table_width); + } + + min_table_width += table_width_spacing; + max_table_width += table_width_spacing; + table_width += table_width_spacing; + m_grid->calc_horizontal_positions(m_borders, m_border_collapse, m_border_spacing_x); + + bool row_span_found = false; + + // render cells with computed width + for (int row = 0; row < m_grid->rows_count(); row++) + { + m_grid->row(row).height = 0; + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + int span_col = col + cell->colspan - 1; + if (span_col >= m_grid->cols_count()) + { + span_col = m_grid->cols_count() - 1; + } + int cell_width = m_grid->column(span_col).right - m_grid->column(col).left; + + if (cell->el->m_pos.width != cell_width - cell->el->content_margins_left() - cell->el->content_margins_right()) + { + cell->el->render(m_grid->column(col).left, 0, cell_width); + cell->el->m_pos.width = cell_width - cell->el->content_margins_left() - cell->el->content_margins_right(); + } + else + { + cell->el->m_pos.x = m_grid->column(col).left + cell->el->content_margins_left(); + } + + if (cell->rowspan <= 1) + { + m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height()); + } + else + { + row_span_found = true; + } + + } + } + } + + if (row_span_found) + { + for (int col = 0; col < m_grid->cols_count(); col++) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + int span_row = row + cell->rowspan - 1; + if (span_row >= m_grid->rows_count()) + { + span_row = m_grid->rows_count() - 1; + } + if (span_row != row) + { + int h = 0; + for (int i = row; i <= span_row; i++) + { + h += m_grid->row(i).height; + } + if (h < cell->el->height()) + { + m_grid->row(span_row).height += cell->el->height() - h; + } + } + } + } + } + } + + // Calculate vertical table spacing + int table_height_spacing = 0; + if (m_border_collapse == border_collapse_separate) + { + table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1); + } + else + { + table_height_spacing = 0; + + if (m_grid->rows_count()) + { + table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top); + table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom); + } + + for (int row = 1; row < m_grid->rows_count(); row++) + { + table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom); + } + } + + + // calculate block height + int block_height = 0; + if (get_predefined_height(block_height)) + { + block_height -= m_padding.height() + m_borders.height(); + } + + // calculate minimum height from m_css_min_height + int min_height = 0; + if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage) + { + element::ptr el_parent = parent(); + if (el_parent) + { + int parent_height = 0; + if (el_parent->get_predefined_height(parent_height)) + { + min_height = m_css_min_height.calc_percent(parent_height); + } + } + } + else + { + min_height = (int)m_css_min_height.val(); + } + + int minimum_table_height = std::max(block_height, min_height); + + m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y); + m_grid->calc_vertical_positions(m_borders, m_border_collapse, m_border_spacing_y); + + int table_height = 0; + + // place cells vertically + for (int col = 0; col < m_grid->cols_count(); col++) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + int span_row = row + cell->rowspan - 1; + if (span_row >= m_grid->rows_count()) + { + span_row = m_grid->rows_count() - 1; + } + cell->el->m_pos.y = m_grid->row(row).top + cell->el->content_margins_top(); + cell->el->m_pos.height = m_grid->row(span_row).bottom - m_grid->row(row).top - cell->el->content_margins_top() - cell->el->content_margins_bottom(); + table_height = std::max(table_height, m_grid->row(span_row).bottom); + cell->el->apply_vertical_align(); + } + } + } + + if (m_border_collapse == border_collapse_collapse) + { + if (m_grid->rows_count()) + { + table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom); + } + } + else + { + table_height += m_border_spacing_y; + } + + m_pos.width = table_width; + + // Render table captions + // Table border doesn't round the caption so we have to start caption in the border position + int captions_height = -border_top(); + + for (auto& caption : m_grid->captions()) + { + caption->render(-border_left(), captions_height, table_width + border_left() + border_right()); + captions_height += caption->height(); + } + + if (captions_height) + { + // Add border height to get the top of cells + captions_height += border_top(); + + // Save caption height for draw_background + m_grid->captions_height(captions_height); + + // Move table cells to the bottom side + for (int row = 0; row < m_grid->rows_count(); row++) + { + m_grid->row(row).el_row->m_pos.y += captions_height; + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + cell->el->m_pos.y += captions_height; + } + } + } + } + + calc_auto_margins(parent_width); + + m_pos.move_to(x, y); + m_pos.x += content_margins_left(); + m_pos.y += content_margins_top(); + m_pos.width = table_width; + m_pos.height = table_height + captions_height; + + return max_table_width; +} + +void litehtml::html_tag::draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +{ + position pos = m_pos; + pos.x += x; + pos.y += y; + + document::ptr doc = get_document(); + + if (m_overflow > overflow_visible) + { + position border_box = pos; + border_box += m_padding; + border_box += m_borders; + + border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); + + bdr_radius -= m_borders; + bdr_radius -= m_padding; + + doc->container()->set_clip(pos, bdr_radius, true, true); + } + + element::ptr el; + for (auto& item : m_children) + { + el = item; + if (el->is_visible()) + { + switch (flag) + { + case draw_positioned: + if (el->is_positioned() && el->get_zindex() == zindex) + { + if (el->get_element_position() == element_position_fixed) + { + position browser_wnd; + doc->container()->get_client_rect(browser_wnd); + + el->draw(hdc, browser_wnd.x, browser_wnd.y, clip); + el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true); + } + else + { + el->draw(hdc, pos.x, pos.y, clip); + el->draw_stacking_context(hdc, pos.x, pos.y, clip, true); + } + el = nullptr; + } + break; + case draw_block: + if (!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) + { + el->draw(hdc, pos.x, pos.y, clip); + } + break; + case draw_floats: + if (el->get_float() != float_none && !el->is_positioned()) + { + el->draw(hdc, pos.x, pos.y, clip); + el->draw_stacking_context(hdc, pos.x, pos.y, clip, false); + el = nullptr; + } + break; + case draw_inlines: + if (el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) + { + el->draw(hdc, pos.x, pos.y, clip); + if (el->get_display() == display_inline_block) + { + el->draw_stacking_context(hdc, pos.x, pos.y, clip, false); + el = nullptr; + } + } + break; + default: + break; + } + + if (el) + { + if (flag == draw_positioned) + { + if (!el->is_positioned()) + { + el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + } + else + { + if (el->get_float() == float_none && + el->get_display() != display_inline_block && + !el->is_positioned()) + { + el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + } + } + } + } + + if (m_overflow > overflow_visible) + { + doc->container()->del_clip(); + } +} + +void litehtml::html_tag::draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +{ + if (!m_grid) return; + + position pos = m_pos; + pos.x += x; + pos.y += y; + for (auto& caption : m_grid->captions()) + { + if (flag == draw_block) + { + caption->draw(hdc, pos.x, pos.y, clip); + } + caption->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + for (int row = 0; row < m_grid->rows_count(); row++) + { + if (flag == draw_block) + { + m_grid->row(row).el_row->draw_background(hdc, pos.x, pos.y, clip); + } + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + if (flag == draw_block) + { + cell->el->draw(hdc, pos.x, pos.y, clip); + } + cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + } + } +} diff --git a/gb.form.htmlview/src/litehtml/html_tag.h b/gb.form.htmlview/src/litehtml/html_tag.h new file mode 100644 index 00000000..4dbe3e18 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/html_tag.h @@ -0,0 +1,248 @@ +#ifndef LH_HTML_TAG_H +#define LH_HTML_TAG_H + +#include "element.h" +#include "style.h" +#include "background.h" +#include "css_margins.h" +#include "borders.h" +#include "css_selector.h" +#include "stylesheet.h" +#include "box.h" +#include "table.h" + +namespace litehtml +{ + struct line_context + { + int calculatedTop; + int top; + int left; + int right; + + int width() const + { + return right - left; + } + void fix_top() + { + calculatedTop = top; + } + }; + + class html_tag : public element + { + friend class elements_iterator; + friend class el_table; + friend class table_grid; + friend class block_box; + friend class line_box; + public: + typedef std::shared_ptr<litehtml::html_tag> ptr; + protected: + box::vector m_boxes; + string_vector m_class_values; + tstring m_tag; + litehtml::style m_style; + string_map m_attrs; + vertical_align m_vertical_align; + text_align m_text_align; + style_display m_display; + list_style_type m_list_style_type; + list_style_position m_list_style_position; + white_space m_white_space; + element_float m_float; + element_clear m_clear; + floated_box::vector m_floats_left; + floated_box::vector m_floats_right; + elements_vector m_positioned; + background m_bg; + element_position m_el_position; + int m_line_height; + bool m_lh_predefined; + string_vector m_pseudo_classes; + used_selector::vector m_used_styles; + + uint_ptr m_font; + int m_font_size; + font_metrics m_font_metrics; + + css_margins m_css_margins; + css_margins m_css_padding; + css_borders m_css_borders; + css_length m_css_width; + css_length m_css_height; + css_length m_css_min_width; + css_length m_css_min_height; + css_length m_css_max_width; + css_length m_css_max_height; + css_offsets m_css_offsets; + css_length m_css_text_indent; + + overflow m_overflow; + visibility m_visibility; + int m_z_index; + box_sizing m_box_sizing; + + int_int_cache m_cahe_line_left; + int_int_cache m_cahe_line_right; + + // data for table rendering + std::unique_ptr<table_grid> m_grid; + css_length m_css_border_spacing_x; + css_length m_css_border_spacing_y; + int m_border_spacing_x; + int m_border_spacing_y; + border_collapse m_border_collapse; + + void select_all(const css_selector& selector, elements_vector& res) override; + + public: + explicit html_tag(const std::shared_ptr<litehtml::document>& doc); + + /* render functions */ + + int render(int x, int y, int max_width, bool second_pass = false) override; + + int render_inline(const element::ptr &container, int max_width) override; + int place_element(const element::ptr &el, int max_width) override; + bool fetch_positioned() override; + void render_positioned(render_type rt = render_all) override; + + int new_box(const element::ptr &el, int max_width, line_context& line_ctx); + + int get_cleared_top(const element::ptr &el, int line_top) const; + int finish_last_box(bool end_of_render = false); + + bool appendChild(const element::ptr &el) override; + bool removeChild(const element::ptr &el) override; + void clearRecursive() override; + const tchar_t* get_tagName() const override; + void set_tagName(const tchar_t* tag) override; + void set_data(const tchar_t* data) override; + element_float get_float() const override; + vertical_align get_vertical_align() const override; + css_length get_css_left() const override; + css_length get_css_right() const override; + css_length get_css_top() const override; + css_length get_css_bottom() const override; + css_length get_css_width() const override; + css_offsets get_css_offsets() const override; + void set_css_width(css_length& w) override; + css_length get_css_height() const override; + element_clear get_clear() const override; + size_t get_children_count() const override; + element::ptr get_child(int idx) const override; + element_position get_element_position(css_offsets* offsets = nullptr) const override; + overflow get_overflow() const override; + + void set_attr(const tchar_t* name, const tchar_t* val) override; + const tchar_t* get_attr(const tchar_t* name, const tchar_t* def = nullptr) const override; + void apply_stylesheet(const litehtml::css& stylesheet) override; + void refresh_styles() override; + + bool is_white_space() const override; + bool is_body() const override; + bool is_break() const override; + int get_base_line() override; + bool on_mouse_over() override; + bool on_mouse_leave() override; + bool on_lbutton_down() override; + bool on_lbutton_up() override; + void on_click() override; + bool find_styles_changes(position::vector& redraw_boxes, int x, int y) override; + const tchar_t* get_cursor() override; + void init_font() override; + bool set_pseudo_class(const tchar_t* pclass, bool add) override; + bool set_class(const tchar_t* pclass, bool add) override; + bool is_replaced() const override; + int line_height() const override; + white_space get_white_space() const override; + style_display get_display() const override; + visibility get_visibility() const override; + void parse_styles(bool is_reparse = false) override; + void draw(uint_ptr hdc, int x, int y, const position* clip) override; + void draw_background(uint_ptr hdc, int x, int y, const position* clip) override; + + const tchar_t* get_style_property(const tchar_t* name, bool inherited, const tchar_t* def = nullptr) const override; + uint_ptr get_font(font_metrics* fm = nullptr) override; + int get_font_size() const override; + + elements_vector& children(); + void calc_outlines(int parent_width) override; + void calc_auto_margins(int parent_width) override; + + int select(const css_selector& selector, bool apply_pseudo = true) override; + int select(const css_element_selector& selector, bool apply_pseudo = true) override; + + elements_vector select_all(const tstring& selector) override; + elements_vector select_all(const css_selector& selector) override; + + element::ptr select_one(const tstring& selector) override; + element::ptr select_one(const css_selector& selector) override; + + element::ptr find_ancestor(const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr) override; + element::ptr find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr) override; + element::ptr find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr) override; + void get_text(tstring& text) override; + void parse_attributes() override; + + bool is_first_child_inline(const element::ptr& el) const override; + bool is_last_child_inline(const element::ptr& el) override; + bool have_inline_child() const override; + void get_content_size(size& sz, int max_width) override; + void init() override; + void get_inline_boxes(position::vector& boxes) override; + bool is_floats_holder() const override; + int get_floats_height(element_float el_float = float_none) const override; + int get_left_floats_height() const override; + int get_right_floats_height() const override; + int get_line_left(int y) override; + int get_line_right(int y, int def_right) override; + void get_line_left_right(int y, int def_right, int& ln_left, int& ln_right) override; + void add_float(const element::ptr &el, int x, int y) override; + void update_floats(int dy, const element::ptr &parent) override; + void add_positioned(const element::ptr &el) override; + int find_next_line_top(int top, int width, int def_right) override; + void apply_vertical_align() override; + void draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) override; + int get_zindex() const override; + void draw_stacking_context(uint_ptr hdc, int x, int y, const position* clip, bool with_positioned) override; + void calc_document_size(litehtml::size& sz, int x = 0, int y = 0) override; + void get_redraw_box(litehtml::position& pos, int x = 0, int y = 0) override; + void add_style(const tstring& style, const tstring& baseurl) override; + element::ptr get_element_by_point(int x, int y, int client_x, int client_y) override; + element::ptr get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex) override; + + bool is_nth_child(const element::ptr& el, int num, int off, bool of_type) const override; + bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const override; + bool is_only_child(const element::ptr& el, bool of_type) const override; + const background* get_background(bool own_only = false) override; + + protected: + void draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex); + void draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex); + int render_box(int x, int y, int max_width, bool second_pass = false); + int render_table(int x, int y, int max_width, bool second_pass = false); + int fix_line_width(int max_width, element_float flt); + void parse_background(); + void init_background_paint( position pos, background_paint &bg_paint, const background* bg ); + void draw_list_marker( uint_ptr hdc, const position &pos ); + tstring get_list_marker_text(int index); + static void parse_nth_child_params( const tstring& param, int &num, int &off ); + void remove_before_after(); + litehtml::element::ptr get_element_before(); + litehtml::element::ptr get_element_after(); + }; + + /************************************************************************/ + /* Inline Functions */ + /************************************************************************/ + + inline elements_vector& litehtml::html_tag::children() + { + return m_children; + } +} + +#endif // LH_HTML_TAG_H diff --git a/gb.form.htmlview/src/litehtml/iterators.cpp b/gb.form.htmlview/src/litehtml/iterators.cpp new file mode 100644 index 00000000..ece2cb51 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/iterators.cpp @@ -0,0 +1,93 @@ +#include "html.h" +#include "iterators.h" +#include "html_tag.h" + +litehtml::element::ptr litehtml::elements_iterator::next(bool ret_parent) +{ + next_idx(); + + while(m_idx < (int) m_el->get_children_count()) + { + element::ptr el = m_el->get_child(m_idx); + if( el->get_children_count() && m_go_inside && m_go_inside->select(el) ) + { + stack_item si; + si.idx = m_idx; + si.el = m_el; + m_stack.push_back(si); + m_el = el; + m_idx = -1; + if(ret_parent) + { + return el; + } + next_idx(); + } else + { + if(!m_select || m_select->select(m_el->get_child(m_idx))) + { + return m_el->get_child(m_idx); + } else + { + next_idx(); + } + } + } + + return nullptr; +} + +void litehtml::elements_iterator::next_idx() +{ + m_idx++; + while(m_idx >= (int) m_el->get_children_count() && !m_stack.empty()) + { + stack_item si = m_stack.back(); + m_stack.pop_back(); + m_idx = si.idx; + m_el = si.el; + m_idx++; + } +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + + +bool litehtml::go_inside_inline::select(const element::ptr& el) +{ + if(el->get_display() == display_inline || el->get_display() == display_inline_text) + { + return true; + } + return false; +} + +bool litehtml::go_inside_table::select(const element::ptr& el) +{ + if( el->get_display() == display_table_row_group || + el->get_display() == display_table_header_group || + el->get_display() == display_table_footer_group) + { + return true; + } + return false; +} + +bool litehtml::table_rows_selector::select(const element::ptr& el) +{ + if( el->get_display() == display_table_row) + { + return true; + } + return false; +} + +bool litehtml::table_cells_selector::select(const element::ptr& el) +{ + if( el->get_display() == display_table_cell) + { + return true; + } + return false; +} diff --git a/gb.form.htmlview/src/litehtml/iterators.h b/gb.form.htmlview/src/litehtml/iterators.h new file mode 100644 index 00000000..4f4ae0f8 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/iterators.h @@ -0,0 +1,89 @@ +#ifndef LH_ITERATORS_H +#define LH_ITERATORS_H + +#include "types.h" + +namespace litehtml +{ + class element; + + class iterator_selector + { + public: + virtual bool select(const element::ptr& el) = 0; + + protected: + ~iterator_selector() = default; + }; + + class elements_iterator + { + private: + struct stack_item + { + int idx; + element::ptr el; + stack_item() : idx(0) + { + } + stack_item(const stack_item& val) + { + idx = val.idx; + el = val.el; + } + stack_item(stack_item&& val) + { + idx = val.idx; + el = std::move(val.el); + } + }; + + std::vector<stack_item> m_stack; + element::ptr m_el; + int m_idx; + iterator_selector* m_go_inside; + iterator_selector* m_select; + public: + + elements_iterator(const element::ptr& el, iterator_selector* go_inside, iterator_selector* select) + { + m_el = el; + m_idx = -1; + m_go_inside = go_inside; + m_select = select; + } + + ~elements_iterator() = default; + + element::ptr next(bool ret_parent = true); + + private: + void next_idx(); + }; + + class go_inside_inline final : public iterator_selector + { + public: + bool select(const element::ptr& el) override; + }; + + class go_inside_table final : public iterator_selector + { + public: + bool select(const element::ptr& el) override; + }; + + class table_rows_selector final : public iterator_selector + { + public: + bool select(const element::ptr& el) override; + }; + + class table_cells_selector final : public iterator_selector + { + public: + bool select(const element::ptr& el) override; + }; +} + +#endif // LH_ITERATORS_H diff --git a/gb.form.htmlview/src/litehtml/media_query.cpp b/gb.form.htmlview/src/litehtml/media_query.cpp new file mode 100644 index 00000000..2714b8c2 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/media_query.cpp @@ -0,0 +1,431 @@ +#include "html.h" +#include "media_query.h" +#include "document.h" + + +litehtml::media_query::media_query() +{ + m_media_type = media_type_all; + m_not = false; +} + +litehtml::media_query::media_query( const media_query& val ) +{ + m_not = val.m_not; + m_expressions = val.m_expressions; + m_media_type = val.m_media_type; +} + +litehtml::media_query::ptr litehtml::media_query::create_from_string(const tstring& str, const std::shared_ptr<document>& doc) +{ + media_query::ptr query = std::make_shared<media_query>(); + + string_vector tokens; + split_string(str, tokens, _t(" \t\r\n"), _t(""), _t("(")); + + for(auto & token : tokens) + { + if(token == _t("not")) + { + query->m_not = true; + } else if(token.at(0) == _t('(')) + { + token.erase(0, 1); + if(token.at(token.length() - 1) == _t(')')) + { + token.erase(token.length() - 1, 1); + } + media_query_expression expr; + string_vector expr_tokens; + split_string(token, expr_tokens, _t(":")); + if(!expr_tokens.empty()) + { + trim(expr_tokens[0]); + expr.feature = (media_feature) value_index(expr_tokens[0], media_feature_strings, media_feature_none); + if(expr.feature != media_feature_none) + { + if(expr_tokens.size() == 1) + { + expr.check_as_bool = true; + } else + { + trim(expr_tokens[1]); + expr.check_as_bool = false; + if(expr.feature == media_feature_orientation) + { + expr.val = value_index(expr_tokens[1], media_orientation_strings, media_orientation_landscape); + } else + { + tstring::size_type slash_pos = expr_tokens[1].find(_t('/')); + if( slash_pos != tstring::npos ) + { + tstring val1 = expr_tokens[1].substr(0, slash_pos); + tstring val2 = expr_tokens[1].substr(slash_pos + 1); + trim(val1); + trim(val2); + expr.val = t_atoi(val1.c_str()); + expr.val2 = t_atoi(val2.c_str()); + } else + { + css_length length; + length.fromString(expr_tokens[1]); + if(length.units() == css_units_dpcm || length.units() == css_units_dpi) + { + expr.val = (int) (length.val() * 2.54); + } else + { + if(doc) + { + doc->cvt_units(length, doc->container()->get_default_font_size()); + } + expr.val = (int) length.val(); + } + } + } + } + query->m_expressions.push_back(expr); + } + } + } else + { + query->m_media_type = (media_type) value_index(token, media_type_strings, media_type_all); + + } + } + + return query; +} + +bool litehtml::media_query::check( const media_features& features ) const +{ + bool res = false; + if(m_media_type == media_type_all || m_media_type == features.type) + { + res = true; + for(auto m_expression : m_expressions) + { + if(!m_expression.check(features)) + { + res = false; + break; + } + } + } + + if(m_not) + { + res = !res; + } + + return res; +} + +////////////////////////////////////////////////////////////////////////// + +litehtml::media_query_list::ptr litehtml::media_query_list::create_from_string(const tstring& str, const std::shared_ptr<document>& doc) +{ + media_query_list::ptr list = std::make_shared<media_query_list>(); + + string_vector tokens; + split_string(str, tokens, _t(",")); + + for(auto & token : tokens) + { + trim(token); + lcase(token); + + litehtml::media_query::ptr query = media_query::create_from_string(token, doc); + if(query) + { + list->m_queries.push_back(query); + } + } + if(list->m_queries.empty()) + { + list = nullptr; + } + + return list; +} + +bool litehtml::media_query_list::apply_media_features( const media_features& features ) +{ + bool apply = false; + + for(auto & query : m_queries) + { + if(query->check(features)) + { + apply = true; + break; + } + } + + bool ret = (apply != m_is_used); + m_is_used = apply; + return ret; +} + +bool litehtml::media_query_expression::check( const media_features& features ) const +{ + switch(feature) + { + case media_feature_width: + if(check_as_bool) + { + return (features.width != 0); + } else if(features.width == val) + { + return true; + } + break; + case media_feature_min_width: + if(features.width >= val) + { + return true; + } + break; + case media_feature_max_width: + if(features.width <= val) + { + return true; + } + break; + case media_feature_height: + if(check_as_bool) + { + return (features.height != 0); + } else if(features.height == val) + { + return true; + } + break; + case media_feature_min_height: + if(features.height >= val) + { + return true; + } + break; + case media_feature_max_height: + if(features.height <= val) + { + return true; + } + break; + + case media_feature_device_width: + if(check_as_bool) + { + return (features.device_width != 0); + } else if(features.device_width == val) + { + return true; + } + break; + case media_feature_min_device_width: + if(features.device_width >= val) + { + return true; + } + break; + case media_feature_max_device_width: + if(features.device_width <= val) + { + return true; + } + break; + case media_feature_device_height: + if(check_as_bool) + { + return (features.device_height != 0); + } else if(features.device_height == val) + { + return true; + } + break; + case media_feature_min_device_height: + if(features.device_height >= val) + { + return true; + } + break; + case media_feature_max_device_height: + if(features.device_height <= val) + { + return true; + } + break; + + case media_feature_orientation: + if(features.height >= features.width) + { + if(val == media_orientation_portrait) + { + return true; + } + } else + { + if(val == media_orientation_landscape) + { + return true; + } + } + break; + case media_feature_aspect_ratio: + if(features.height && val2) + { + int ratio_this = round_d( (double) val / (double) val2 * 100 ); + int ratio_feat = round_d( (double) features.width / (double) features.height * 100.0 ); + if(ratio_this == ratio_feat) + { + return true; + } + } + break; + case media_feature_min_aspect_ratio: + if(features.height && val2) + { + int ratio_this = round_d( (double) val / (double) val2 * 100 ); + int ratio_feat = round_d( (double) features.width / (double) features.height * 100.0 ); + if(ratio_feat >= ratio_this) + { + return true; + } + } + break; + case media_feature_max_aspect_ratio: + if(features.height && val2) + { + int ratio_this = round_d( (double) val / (double) val2 * 100 ); + int ratio_feat = round_d( (double) features.width / (double) features.height * 100.0 ); + if(ratio_feat <= ratio_this) + { + return true; + } + } + break; + + case media_feature_device_aspect_ratio: + if(features.device_height && val2) + { + int ratio_this = round_d( (double) val / (double) val2 * 100 ); + int ratio_feat = round_d( (double) features.device_width / (double) features.device_height * 100.0 ); + if(ratio_feat == ratio_this) + { + return true; + } + } + break; + case media_feature_min_device_aspect_ratio: + if(features.device_height && val2) + { + int ratio_this = round_d( (double) val / (double) val2 * 100 ); + int ratio_feat = round_d( (double) features.device_width / (double) features.device_height * 100.0 ); + if(ratio_feat >= ratio_this) + { + return true; + } + } + break; + case media_feature_max_device_aspect_ratio: + if(features.device_height && val2) + { + int ratio_this = round_d( (double) val / (double) val2 * 100 ); + int ratio_feat = round_d( (double) features.device_width / (double) features.device_height * 100.0 ); + if(ratio_feat <= ratio_this) + { + return true; + } + } + break; + + case media_feature_color: + if(check_as_bool) + { + return (features.color != 0); + } else if(features.color == val) + { + return true; + } + break; + case media_feature_min_color: + if(features.color >= val) + { + return true; + } + break; + case media_feature_max_color: + if(features.color <= val) + { + return true; + } + break; + + case media_feature_color_index: + if(check_as_bool) + { + return (features.color_index != 0); + } else if(features.color_index == val) + { + return true; + } + break; + case media_feature_min_color_index: + if(features.color_index >= val) + { + return true; + } + break; + case media_feature_max_color_index: + if(features.color_index <= val) + { + return true; + } + break; + + case media_feature_monochrome: + if(check_as_bool) + { + return (features.monochrome != 0); + } else if(features.monochrome == val) + { + return true; + } + break; + case media_feature_min_monochrome: + if(features.monochrome >= val) + { + return true; + } + break; + case media_feature_max_monochrome: + if(features.monochrome <= val) + { + return true; + } + break; + + case media_feature_resolution: + if(features.resolution == val) + { + return true; + } + break; + case media_feature_min_resolution: + if(features.resolution >= val) + { + return true; + } + break; + case media_feature_max_resolution: + if(features.resolution <= val) + { + return true; + } + break; + default: + return false; + } + + return false; +} diff --git a/gb.form.htmlview/src/litehtml/media_query.h b/gb.form.htmlview/src/litehtml/media_query.h new file mode 100644 index 00000000..6a81bcb3 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/media_query.h @@ -0,0 +1,77 @@ +#ifndef LH_MEDIA_QUERY_H +#define LH_MEDIA_QUERY_H + +namespace litehtml +{ + struct media_query_expression + { + typedef std::vector<media_query_expression> vector; + media_feature feature; + int val; + int val2; + bool check_as_bool; + + media_query_expression() + { + check_as_bool = false; + feature = media_feature_none; + val = 0; + val2 = 0; + } + + bool check(const media_features& features) const; + }; + + class media_query + { + public: + typedef std::shared_ptr<media_query> ptr; + typedef std::vector<media_query::ptr> vector; + private: + media_query_expression::vector m_expressions; + bool m_not; + media_type m_media_type; + public: + media_query(); + media_query(const media_query& val); + + static media_query::ptr create_from_string(const tstring& str, const std::shared_ptr<document>& doc); + bool check(const media_features& features) const; + }; + + class media_query_list + { + public: + typedef std::shared_ptr<media_query_list> ptr; + typedef std::vector<media_query_list::ptr> vector; + private: + media_query::vector m_queries; + bool m_is_used; + public: + media_query_list(); + media_query_list(const media_query_list& val); + + static media_query_list::ptr create_from_string(const tstring& str, const std::shared_ptr<document>& doc); + bool is_used() const; + bool apply_media_features(const media_features& features); // returns true if the m_is_used changed + }; + + inline media_query_list::media_query_list(const media_query_list& val) + { + m_is_used = val.m_is_used; + m_queries = val.m_queries; + } + + inline media_query_list::media_query_list() + { + m_is_used = false; + } + + inline bool media_query_list::is_used() const + { + return m_is_used; + } + +} + +#endif // LH_MEDIA_QUERY_H diff --git a/gb.form.htmlview/src/litehtml/num_cvt.cpp b/gb.form.htmlview/src/litehtml/num_cvt.cpp new file mode 100644 index 00000000..e1f38dab --- /dev/null +++ b/gb.form.htmlview/src/litehtml/num_cvt.cpp @@ -0,0 +1,108 @@ +#include "num_cvt.h" +#include "utf8_strings.h" +#include <vector> + +static std::vector<litehtml::tchar_t> latin_lower = { _t('a'), _t('b'), _t('c'), _t('d'), _t('e'), _t('f'), _t('g'), _t('h'), _t('i'), _t('j'), _t('k'), _t('l'), _t('m'), _t('n'), _t('o'), _t('p'), _t('q'), _t('r'), _t('s'), _t('t'), _t('u'), _t('v'), _t('w'), _t('x'), _t('y'), _t('z') }; +static std::vector<litehtml::tchar_t> latin_upper = { _t('A'), _t('B'), _t('C'), _t('D'), _t('E'), _t('F'), _t('G'), _t('H'), _t('I'), _t('J'), _t('K'), _t('L'), _t('M'), _t('N'), _t('O'), _t('P'), _t('Q'), _t('R'), _t('S'), _t('T'), _t('U'), _t('V'), _t('W'), _t('X'), _t('Y'), _t('Z') }; +static std::vector<std::wstring> greek_lower = { L"α", L"β", L"γ", L"δ", L"ε", L"ζ", L"η", L"θ", L"ι", L"κ", L"λ", L"μ", L"ν", L"ξ", L"ο", L"π", L"ρ", L"σ", L"τ", L"υ", L"φ", L"χ", L"ψ", L"ω" }; + +static litehtml::tstring to_mapped_alpha(int num, const std::vector<litehtml::tchar_t>& map) +{ + int dividend = num; + litehtml::tstring out; + int modulo; + + while (dividend > 0) + { + modulo = (dividend - 1) % map.size(); + out = map[modulo] + out; + dividend = (int)((dividend - modulo) / map.size()); + } + + return out; +} + +static litehtml::tstring to_mapped_alpha(int num, const std::vector<std::wstring>& map) +{ + int dividend = num; + litehtml::tstring out; + int modulo; + + while (dividend > 0) + { + modulo = (dividend - 1) % map.size(); + out = litehtml_from_wchar(map[modulo]).c_str() + out; + dividend = (int)((dividend - modulo) / map.size()); + } + + return out; +} + +litehtml::tstring litehtml::num_cvt::to_latin_lower(int val) +{ + return to_mapped_alpha(val, latin_lower); +} + +litehtml::tstring litehtml::num_cvt::to_latin_upper(int val) +{ + return to_mapped_alpha(val, latin_upper); +} + +litehtml::tstring litehtml::num_cvt::to_greek_lower(int val) +{ + return to_mapped_alpha(val, greek_lower); +} + +litehtml::tstring litehtml::num_cvt::to_roman_lower(int value) +{ + struct romandata_t { int value; const litehtml::tchar_t* numeral; }; + const struct romandata_t romandata[] = + { + { 1000, _t("m") }, { 900, _t("cm" )}, + { 500, _t("d") }, { 400, _t("cd") }, + { 100, _t("c") }, { 90, _t("xc") }, + { 50, _t("l") }, { 40, _t("xl") }, + { 10, _t("x") }, { 9, _t("ix") }, + { 5, _t("v") }, { 4, _t("iv") }, + { 1, _t("i") }, + { 0, nullptr } // end marker + }; + + litehtml::tstring result; + for (const romandata_t* current = romandata; current->value > 0; ++current) + { + while (value >= current->value) + { + result += current->numeral; + value -= current->value; + } + } + return result; +} + +litehtml::tstring litehtml::num_cvt::to_roman_upper(int value) +{ + struct romandata_t { int value; const litehtml::tchar_t* numeral; }; + const struct romandata_t romandata[] = + { + { 1000, _t("M") }, { 900, _t("CM") }, + { 500, _t("D") }, { 400, _t("CD") }, + { 100, _t("C") }, { 90, _t("XC") }, + { 50, _t("L") }, { 40, _t("XL") }, + { 10, _t("X") }, { 9, _t("IX") }, + { 5, _t("V") }, { 4, _t("IV") }, + { 1, _t("I") }, + { 0, nullptr } // end marker + }; + + litehtml::tstring result; + for (const romandata_t* current = romandata; current->value > 0; ++current) + { + while (value >= current->value) + { + result += current->numeral; + value -= current->value; + } + } + return result; +} diff --git a/gb.form.htmlview/src/litehtml/num_cvt.h b/gb.form.htmlview/src/litehtml/num_cvt.h new file mode 100644 index 00000000..515e46d6 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/num_cvt.h @@ -0,0 +1,19 @@ +#ifndef NUM_CVT_H +#define NUM_CVT_H + +#include <string> +#include "os_types.h" + +namespace litehtml +{ + namespace num_cvt + { + litehtml::tstring to_latin_lower(int val); + litehtml::tstring to_latin_upper(int val); + litehtml::tstring to_greek_lower(int val); + litehtml::tstring to_roman_lower(int value); + litehtml::tstring to_roman_upper(int value); + } +} + +#endif // NUM_CVT_H \ No newline at end of file diff --git a/gb.form.htmlview/src/litehtml/os_types.h b/gb.form.htmlview/src/litehtml/os_types.h new file mode 100644 index 00000000..6d1e5dc6 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/os_types.h @@ -0,0 +1,85 @@ +#ifndef LH_OS_TYPES_H +#define LH_OS_TYPES_H + +#include <string> +#include <cstdint> + +namespace litehtml +{ +#if defined( WIN32 ) || defined( _WIN32 ) || defined( WINCE ) + +// noexcept appeared since Visual Studio 2013 +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define noexcept +#endif + +#ifndef LITEHTML_UTF8 + + typedef std::wstring tstring; + typedef wchar_t tchar_t; + typedef std::wstringstream tstringstream; + + #define _t(quote) L##quote + + #define t_strlen wcslen + #define t_strcmp wcscmp + #define t_strncmp wcsncmp + #define t_strtol wcstol + #define t_atoi _wtoi + #define t_itoa(value, buffer, size, radix) _itow_s(value, buffer, size, radix) + #define t_strstr wcsstr + #define t_isspace iswspace + #define t_to_string(val) std::to_wstring(val) + +#else + + typedef std::string tstring; + typedef char tchar_t; + typedef std::stringstream tstringstream; + + #define _t(quote) quote + + #define t_strlen strlen + #define t_strcmp strcmp + #define t_strncmp strncmp + #define t_strtol strtol + #define t_atoi atoi + #define t_itoa(value, buffer, size, radix) _itoa_s(value, buffer, size, radix) + #define t_strstr strstr + #define t_isspace isspace + #define t_to_string(val) std::to_string(val) + +#endif + + #ifdef _WIN64 + typedef unsigned __int64 uint_ptr; + #else + typedef unsigned int uint_ptr; + #endif + +#else + #define LITEHTML_UTF8 + + typedef std::string tstring; + typedef char tchar_t; + typedef std::uintptr_t uint_ptr; + typedef std::stringstream tstringstream; + + #define _t(quote) quote + + #define t_strlen strlen + #define t_strcmp strcmp + #define t_strncmp strncmp + + #define t_itoa(value, buffer, size, radix) snprintf(buffer, size, "%d", value) + + #define t_strtol strtol + #define t_atoi atoi + #define t_strstr strstr + #define t_isspace isspace + #define t_to_string(val) std::to_string(val) + +#endif +} + +#endif // LH_OS_TYPES_H diff --git a/gb.form.htmlview/src/litehtml/strtod.cpp b/gb.form.htmlview/src/litehtml/strtod.cpp new file mode 100644 index 00000000..64940fb3 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/strtod.cpp @@ -0,0 +1,276 @@ +/* + * strtod.c -- + * + * Source code for the "strtod" library procedure. + * + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + * + * RCS: @(#) $Id$ + */ + +#include "html.h" +#include <cstdlib> +#include <cctype> +#include <cerrno> +extern int errno; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#ifndef NULL +#define NULL 0 +#endif + +static int maxExponent = 511; /* Largest possible base 10 exponent. Any + * exponent larger than this will already + * produce underflow or overflow, so there's + * no need to worry about additional digits. + */ +static double powersOf10[] = { /* Table giving binary powers of 10. Entry */ + 10., /* is 10^2^i. Used to convert decimal */ + 100., /* exponents into floating-point numbers. */ + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 +}; + +/* + *---------------------------------------------------------------------- + * + * strtod -- + * + * This procedure converts a floating-point number from an ASCII + * decimal representation to internal double-precision format. + * + * Results: + * The return value is the double-precision floating-point + * representation of the characters in string. If endPtr isn't + * NULL, then *endPtr is filled in with the address of the + * next character after the last one that was part of the + * floating-point number. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +double litehtml::t_strtod(const litehtml::tchar_t* string, litehtml::tchar_t** endPtr) +{ + int sign, expSign = FALSE; + double fraction, dblExp, *d; + const litehtml::tchar_t *p; + int c; + int exp = 0; /* Exponent read from "EX" field. */ + int fracExp = 0; /* Exponent that derives from the fractional + * part. Under normal circumstatnces, it is + * the negative of the number of digits in F. + * However, if I is very long, the last digits + * of I get dropped (otherwise a long I with a + * large negative exponent could cause an + * unnecessary overflow on I alone). In this + * case, fracExp is incremented one for each + * dropped digit. */ + int mantSize; /* Number of digits in mantissa. */ + int decPt; /* Number of mantissa digits BEFORE decimal + * point. */ + const litehtml::tchar_t *pExp; /* Temporarily holds location of exponent + * in string. */ + + /* + * Strip off leading blanks and check for a sign. + */ + + p = string; + while (t_isspace(*p)) + { + p += 1; + } + if (*p == _t('-')) + { + sign = TRUE; + p += 1; + } else + { + if (*p == _t('+')) + { + p += 1; + } + sign = FALSE; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + + decPt = -1; + for (mantSize = 0; ; mantSize += 1) + { + c = *p; + if (!t_isdigit(c)) + { + if ((c != _t('.')) || (decPt >= 0)) + { + break; + } + decPt = mantSize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to + * collect 9 digits each (this is faster than using floating-point). + * If the mantissa has more than 18 digits, ignore the extras, since + * they can't affect the value anyway. + */ + + pExp = p; + p -= mantSize; + if (decPt < 0) + { + decPt = mantSize; + } else + { + mantSize -= 1; /* One of the digits was the point. */ + } + if (mantSize > 18) + { + fracExp = decPt - 18; + mantSize = 18; + } else + { + fracExp = decPt - mantSize; + } + if (mantSize == 0) + { + fraction = 0.0; + p = string; + goto done; + } else + { + int frac1, frac2; + frac1 = 0; + for ( ; mantSize > 9; mantSize -= 1) + { + c = *p; + p += 1; + if (c == _t('.')) + { + c = *p; + p += 1; + } + frac1 = 10*frac1 + (c - _t('0')); + } + frac2 = 0; + for (; mantSize > 0; mantSize -= 1) + { + c = *p; + p += 1; + if (c == _t('.')) + { + c = *p; + p += 1; + } + frac2 = 10*frac2 + (c - _t('0')); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* + * Skim off the exponent. + */ + + p = pExp; + if ((*p == _t('E')) || (*p == _t('e'))) + { + p += 1; + if (*p == _t('-')) + { + expSign = TRUE; + p += 1; + } else + { + if (*p == _t('+')) + { + p += 1; + } + expSign = FALSE; + } + while (isdigit(*p)) + { + exp = exp * 10 + (*p - _t('0')); + p += 1; + } + } + if (expSign) + { + exp = fracExp - exp; + } else + { + exp = fracExp + exp; + } + + /* + * Generate a floating-point number that represents the exponent. + * Do this by processing the exponent one bit at a time to combine + * many powers of 2 of 10. Then combine the exponent with the + * fraction. + */ + + if (exp < 0) + { + expSign = TRUE; + exp = -exp; + } else + { + expSign = FALSE; + } + if (exp > maxExponent) + { + exp = maxExponent; + errno = ERANGE; + } + dblExp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, d += 1) + { + if (exp & 01) + { + dblExp *= *d; + } + } + if (expSign) + { + fraction /= dblExp; + } else + { + fraction *= dblExp; + } + +done: + if (endPtr != nullptr) + { + *endPtr = (litehtml::tchar_t *) p; + } + + if (sign) + { + return -fraction; + } + return fraction; +} diff --git a/gb.form.htmlview/src/litehtml/style.cpp b/gb.form.htmlview/src/litehtml/style.cpp new file mode 100644 index 00000000..1568ff1d --- /dev/null +++ b/gb.form.htmlview/src/litehtml/style.cpp @@ -0,0 +1,658 @@ +#include "html.h" +#include "style.h" +#ifndef WINCE +#include <locale> +#endif + +litehtml::string_map litehtml::style::m_valid_values = +{ + { _t("white-space"), white_space_strings } +}; + +litehtml::style::style( const style& val ) +{ + m_properties = val.m_properties; +} + +void litehtml::style::parse( const tchar_t* txt, const tchar_t* baseurl, const element* el ) +{ + std::vector<tstring> properties; + split_string(txt, properties, _t(";"), _t(""), _t("\"'")); + + for(const auto & property : properties) + { + parse_property(property, baseurl, el); + } +} + +void litehtml::style::parse_property( const tstring& txt, const tchar_t* baseurl, const element* el ) +{ + tstring::size_type pos = txt.find_first_of(_t(':')); + if(pos != tstring::npos) + { + tstring name = txt.substr(0, pos); + tstring val = txt.substr(pos + 1); + + trim(name); lcase(name); + trim(val); + + if(!name.empty() && !val.empty()) + { + string_vector vals; + split_string(val, vals, _t("!")); + if(vals.size() == 1) + { + add_property(name.c_str(), val.c_str(), baseurl, false, el); + } else if(vals.size() > 1) + { + trim(vals[0]); + lcase(vals[1]); + add_property(name.c_str(), vals[0].c_str(), baseurl, vals[1] == _t("important"), el); + } + } + } +} + +void litehtml::style::combine( const litehtml::style& src ) +{ + for(const auto& property : src.m_properties) + { + add_parsed_property(property.first, property.second.m_value, property.second.m_important); + } +} + +void litehtml::style::subst_vars( tstring& str, const element* el ) +{ + if (!el) return; + + while (1) + { + auto start = str.find(_t("var(")); + if (start == -1) break; + if (start > 0 && isalnum(str[start - 1])) break; + auto end = str.find(_t(")"), start + 4); + if (end == -1) break; + auto name = str.substr(start + 4, end - start - 4); + trim(name); + auto val = el->get_style_property(name.c_str(), true); + if (!val) break; + str.replace(start, end - start + 1, val); + } +} + +void litehtml::style::add_property( const tchar_t* name, const tchar_t* _val, const tchar_t* baseurl, bool important, const element* el ) +{ + if(!name || !_val) + { + return; + } + + tstring val = _val; + subst_vars(val, el); + + // Add baseurl for background image + if( !t_strcmp(name, _t("background-image"))) + { + add_parsed_property(name, val, important); + if(baseurl) + { + add_parsed_property(_t("background-image-baseurl"), baseurl, important); + } + } else + + // Parse border spacing properties + if( !t_strcmp(name, _t("border-spacing"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() == 1) + { + add_parsed_property(_t("-litehtml-border-spacing-x"), tokens[0], important); + add_parsed_property(_t("-litehtml-border-spacing-y"), tokens[0], important); + } else if(tokens.size() == 2) + { + add_parsed_property(_t("-litehtml-border-spacing-x"), tokens[0], important); + add_parsed_property(_t("-litehtml-border-spacing-y"), tokens[1], important); + } + } else + + // Parse borders shorthand properties + + if( !t_strcmp(name, _t("border"))) + { + string_vector tokens; + split_string(val, tokens, _t(" "), _t(""), _t("(")); + int idx; + tstring str; + for(const auto& token : tokens) + { + idx = value_index(token, border_style_strings, -1); + if(idx >= 0) + { + add_property(_t("border-left-style"), token.c_str(), baseurl, important, el); + add_property(_t("border-right-style"), token.c_str(), baseurl, important, el); + add_property(_t("border-top-style"), token.c_str(), baseurl, important, el); + add_property(_t("border-bottom-style"), token.c_str(), baseurl, important, el); + } else + { + if (t_isdigit(token[0]) || token[0] == _t('.') || + value_in_list(token, _t("thin;medium;thick"))) + { + add_property(_t("border-left-width"), token.c_str(), baseurl, important, el); + add_property(_t("border-right-width"), token.c_str(), baseurl, important, el); + add_property(_t("border-top-width"), token.c_str(), baseurl, important, el); + add_property(_t("border-bottom-width"), token.c_str(), baseurl, important, el); + } + else + { + add_property(_t("border-left-color"), token.c_str(), baseurl, important, el); + add_property(_t("border-right-color"), token.c_str(), baseurl, important, el); + add_property(_t("border-top-color"), token.c_str(), baseurl, important, el); + add_property(_t("border-bottom-color"), token.c_str(), baseurl, important, el); + } + } + } + } else if( !t_strcmp(name, _t("border-left")) || + !t_strcmp(name, _t("border-right")) || + !t_strcmp(name, _t("border-top")) || + !t_strcmp(name, _t("border-bottom")) ) + { + string_vector tokens; + split_string(val, tokens, _t(" "), _t(""), _t("(")); + int idx; + tstring str; + for(const auto& token : tokens) + { + idx = value_index(token, border_style_strings, -1); + if(idx >= 0) + { + str = name; + str += _t("-style"); + add_property(str.c_str(), token.c_str(), baseurl, important, el); + } else + { + if(web_color::is_color(token.c_str())) + { + str = name; + str += _t("-color"); + add_property(str.c_str(), token.c_str(), baseurl, important, el); + } else + { + str = name; + str += _t("-width"); + add_property(str.c_str(), token.c_str(), baseurl, important, el); + } + } + } + } else + + // Parse border radius shorthand properties + if(!t_strcmp(name, _t("border-bottom-left-radius"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() >= 2) + { + add_property(_t("border-bottom-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-y"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 1) + { + add_property(_t("border-bottom-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-y"), tokens[0].c_str(), baseurl, important, el); + } + + } else if(!t_strcmp(name, _t("border-bottom-right-radius"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() >= 2) + { + add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-y"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 1) + { + add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-y"), tokens[0].c_str(), baseurl, important, el); + } + + } else if(!t_strcmp(name, _t("border-top-right-radius"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() >= 2) + { + add_property(_t("border-top-right-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 1) + { + add_property(_t("border-top-right-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-y"), tokens[0].c_str(), baseurl, important, el); + } + + } else if(!t_strcmp(name, _t("border-top-left-radius"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() >= 2) + { + add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-left-radius-y"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 1) + { + add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important, el); + } + + } else + + // Parse border-radius shorthand properties + if(!t_strcmp(name, _t("border-radius"))) + { + string_vector tokens; + split_string(val, tokens, _t("/")); + if(tokens.size() == 1) + { + add_property(_t("border-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-radius-y"), tokens[0].c_str(), baseurl, important, el); + } else if(tokens.size() >= 2) + { + add_property(_t("border-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-radius-y"), tokens[1].c_str(), baseurl, important, el); + } + } else if(!t_strcmp(name, _t("border-radius-x"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() == 1) + { + add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + } else if(tokens.size() == 2) + { + add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-x"), tokens[1].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-x"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 3) + { + add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-x"), tokens[1].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-x"), tokens[2].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-x"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 4) + { + add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-x"), tokens[1].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-x"), tokens[2].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-x"), tokens[3].c_str(), baseurl, important, el); + } + } else if(!t_strcmp(name, _t("border-radius-y"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() == 1) + { + add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-y"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-y"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-y"), tokens[0].c_str(), baseurl, important, el); + } else if(tokens.size() == 2) + { + add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-y"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-y"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 3) + { + add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-y"), tokens[2].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-y"), tokens[1].c_str(), baseurl, important, el); + } else if(tokens.size() == 4) + { + add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important, el); + add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important, el); + add_property(_t("border-bottom-right-radius-y"), tokens[2].c_str(), baseurl, important, el); + add_property(_t("border-bottom-left-radius-y"), tokens[3].c_str(), baseurl, important, el); + } + } + else + + // Parse list-style shorthand properties + if(!t_strcmp(name, _t("list-style"))) + { + add_parsed_property(_t("list-style-type"), _t("disc"), important); + add_parsed_property(_t("list-style-position"), _t("outside"), important); + add_parsed_property(_t("list-style-image"), _t(""), important); + add_parsed_property(_t("list-style-image-baseurl"), _t(""), important); + + string_vector tokens; + split_string(val, tokens, _t(" "), _t(""), _t("(")); + for(const auto& token : tokens) + { + int idx = value_index(token, list_style_type_strings, -1); + if(idx >= 0) + { + add_parsed_property(_t("list-style-type"), token, important); + } else + { + idx = value_index(token, list_style_position_strings, -1); + if(idx >= 0) + { + add_parsed_property(_t("list-style-position"), token, important); + } else if(!t_strncmp(val.c_str(), _t("url"), 3)) + { + add_parsed_property(_t("list-style-image"), token, important); + if(baseurl) + { + add_parsed_property(_t("list-style-image-baseurl"), baseurl, important); + } + } + } + } + } else + + // Add baseurl for background image + if( !t_strcmp(name, _t("list-style-image"))) + { + add_parsed_property(name, val, important); + if(baseurl) + { + add_parsed_property(_t("list-style-image-baseurl"), baseurl, important); + } + } else + + // Parse background shorthand properties + if(!t_strcmp(name, _t("background"))) + { + parse_short_background(val, baseurl, important); + + } else + + // Parse margin and padding shorthand properties + if(!t_strcmp(name, _t("margin")) || !t_strcmp(name, _t("padding"))) + { + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() >= 4) + { + add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); + add_parsed_property(tstring(name) + _t("-right"), tokens[1], important); + add_parsed_property(tstring(name) + _t("-bottom"), tokens[2], important); + add_parsed_property(tstring(name) + _t("-left"), tokens[3], important); + } else if(tokens.size() == 3) + { + add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); + add_parsed_property(tstring(name) + _t("-right"), tokens[1], important); + add_parsed_property(tstring(name) + _t("-left"), tokens[1], important); + add_parsed_property(tstring(name) + _t("-bottom"), tokens[2], important); + } else if(tokens.size() == 2) + { + add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); + add_parsed_property(tstring(name) + _t("-bottom"), tokens[0], important); + add_parsed_property(tstring(name) + _t("-right"), tokens[1], important); + add_parsed_property(tstring(name) + _t("-left"), tokens[1], important); + } else if(tokens.size() == 1) + { + add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); + add_parsed_property(tstring(name) + _t("-bottom"), tokens[0], important); + add_parsed_property(tstring(name) + _t("-right"), tokens[0], important); + add_parsed_property(tstring(name) + _t("-left"), tokens[0], important); + } + } else + + + // Parse border-* shorthand properties + if( !t_strcmp(name, _t("border-left")) || + !t_strcmp(name, _t("border-right")) || + !t_strcmp(name, _t("border-top")) || + !t_strcmp(name, _t("border-bottom"))) + { + parse_short_border(name, val, important); + } else + + // Parse border-width/style/color shorthand properties + if( !t_strcmp(name, _t("border-width")) || + !t_strcmp(name, _t("border-style")) || + !t_strcmp(name, _t("border-color")) ) + { + string_vector nametokens; + split_string(name, nametokens, _t("-")); + + string_vector tokens; + split_string(val, tokens, _t(" ")); + if(tokens.size() >= 4) + { + add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); + add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[1], important); + add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[2], important); + add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[3], important); + } else if(tokens.size() == 3) + { + add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); + add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[1], important); + add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[1], important); + add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[2], important); + } else if(tokens.size() == 2) + { + add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); + add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[0], important); + add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[1], important); + add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[1], important); + } else if(tokens.size() == 1) + { + add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); + add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[0], important); + add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[0], important); + add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[0], important); + } + } else + + // Parse font shorthand properties + if(!t_strcmp(name, _t("font"))) + { + parse_short_font(val, important); + } else + { + add_parsed_property(name, val, important); + } +} + +void litehtml::style::parse_short_border( const tstring& prefix, const tstring& val, bool important ) +{ + string_vector tokens; + split_string(val, tokens, _t(" "), _t(""), _t("(")); + if(tokens.size() >= 3) + { + add_parsed_property(prefix + _t("-width"), tokens[0], important); + add_parsed_property(prefix + _t("-style"), tokens[1], important); + add_parsed_property(prefix + _t("-color"), tokens[2], important); + } else if(tokens.size() == 2) + { + if(iswdigit(tokens[0][0]) || value_index(val, border_width_strings) >= 0) + { + add_parsed_property(prefix + _t("-width"), tokens[0], important); + add_parsed_property(prefix + _t("-style"), tokens[1], important); + } else + { + add_parsed_property(prefix + _t("-style"), tokens[0], important); + add_parsed_property(prefix + _t("-color"), tokens[1], important); + } + } +} + +void litehtml::style::parse_short_background( const tstring& val, const tchar_t* baseurl, bool important ) +{ + add_parsed_property(_t("background-color"), _t("transparent"), important); + add_parsed_property(_t("background-image"), _t(""), important); + add_parsed_property(_t("background-image-baseurl"), _t(""), important); + add_parsed_property(_t("background-repeat"), _t("repeat"), important); + add_parsed_property(_t("background-origin"), _t("padding-box"), important); + add_parsed_property(_t("background-clip"), _t("border-box"), important); + add_parsed_property(_t("background-attachment"), _t("scroll"), important); + + if(val == _t("none")) + { + return; + } + + string_vector tokens; + split_string(val, tokens, _t(" "), _t(""), _t("(")); + bool origin_found = false; + for(const auto& token : tokens) + { + if(token.substr(0, 3) == _t("url")) + { + add_parsed_property(_t("background-image"), token, important); + if(baseurl) + { + add_parsed_property(_t("background-image-baseurl"), baseurl, important); + } + + } else if( value_in_list(token, background_repeat_strings) ) + { + add_parsed_property(_t("background-repeat"), token, important); + } else if( value_in_list(token, background_attachment_strings) ) + { + add_parsed_property(_t("background-attachment"), token, important); + } else if( value_in_list(token, background_box_strings) ) + { + if(!origin_found) + { + add_parsed_property(_t("background-origin"), token, important); + origin_found = true; + } else + { + add_parsed_property(_t("background-clip"), token, important); + } + } else if( value_in_list(token, _t("left;right;top;bottom;center")) || + iswdigit(token[0]) || + token[0] == _t('-') || + token[0] == _t('.') || + token[0] == _t('+')) + { + if(m_properties.find(_t("background-position")) != m_properties.end()) + { + m_properties[_t("background-position")].m_value = m_properties[_t("background-position")].m_value + _t(" ") + token; + } else + { + add_parsed_property(_t("background-position"), token, important); + } + } else if (web_color::is_color(token.c_str())) + { + add_parsed_property(_t("background-color"), token, important); + } + } +} + +void litehtml::style::parse_short_font( const tstring& val, bool important ) +{ + add_parsed_property(_t("font-style"), _t("normal"), important); + add_parsed_property(_t("font-variant"), _t("normal"), important); + add_parsed_property(_t("font-weight"), _t("normal"), important); + add_parsed_property(_t("font-size"), _t("medium"), important); + add_parsed_property(_t("line-height"), _t("normal"), important); + + string_vector tokens; + split_string(val, tokens, _t(" "), _t(""), _t("\"")); + + int idx; + bool is_family = false; + tstring font_family; + for(const auto& token : tokens) + { + idx = value_index(token, font_style_strings); + if(!is_family) + { + if(idx >= 0) + { + if(idx == 0) + { + add_parsed_property(_t("font-weight"), token, important); + add_parsed_property(_t("font-variant"), token, important); + add_parsed_property(_t("font-style"), token, important); + } else + { + add_parsed_property(_t("font-style"), token, important); + } + } else + { + if(value_in_list(token, font_weight_strings)) + { + add_parsed_property(_t("font-weight"), token, important); + } else + { + if(value_in_list(token, font_variant_strings)) + { + add_parsed_property(_t("font-variant"), token, important); + } else if( iswdigit(token[0]) ) + { + string_vector szlh; + split_string(token, szlh, _t("/")); + + if(szlh.size() == 1) + { + add_parsed_property(_t("font-size"), szlh[0], important); + } else if(szlh.size() >= 2) + { + add_parsed_property(_t("font-size"), szlh[0], important); + add_parsed_property(_t("line-height"), szlh[1], important); + } + } else + { + is_family = true; + font_family += token; + } + } + } + } else + { + font_family += token; + } + } + add_parsed_property(_t("font-family"), font_family, important); +} + +void litehtml::style::add_parsed_property( const tstring& name, const tstring& val, bool important ) +{ + bool is_valid = true; + auto vals = m_valid_values.find(name); + if (vals != m_valid_values.end()) + { + if (!value_in_list(val, vals->second)) + { + is_valid = false; + } + } + + if (is_valid) + { + auto prop = m_properties.find(name); + if (prop != m_properties.end()) + { + if (!prop->second.m_important || (important && prop->second.m_important)) + { + prop->second.m_value = val; + prop->second.m_important = important; + } + } + else + { + m_properties[name] = property_value(val.c_str(), important); + } + } +} + +void litehtml::style::remove_property( const tstring& name, bool important ) +{ + auto prop = m_properties.find(name); + if(prop != m_properties.end()) + { + if( !prop->second.m_important || (important && prop->second.m_important) ) + { + m_properties.erase(prop); + } + } +} diff --git a/gb.form.htmlview/src/litehtml/style.h b/gb.form.htmlview/src/litehtml/style.h new file mode 100644 index 00000000..20461450 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/style.h @@ -0,0 +1,96 @@ +#ifndef LH_STYLE_H +#define LH_STYLE_H + +#include "attributes.h" +#include <string> + +namespace litehtml +{ + class property_value + { + public: + tstring m_value; + bool m_important; + + property_value() + { + m_important = false; + } + property_value(const tchar_t* val, bool imp) + { + m_important = imp; + m_value = val; + } + property_value(const property_value& val) + { + m_value = val.m_value; + m_important = val.m_important; + } + + property_value& operator=(const property_value& val) + { + m_value = val.m_value; + m_important = val.m_important; + return *this; + } + }; + + typedef std::map<tstring, property_value> props_map; + + class style + { + public: + typedef std::shared_ptr<style> ptr; + typedef std::vector<style::ptr> vector; + private: + props_map m_properties; + static string_map m_valid_values; + public: + style() = default; + style(const style& val); + + style& operator=(const style& val) + { + m_properties = val.m_properties; + return *this; + } + + void add(const tchar_t* txt, const tchar_t* baseurl, const element* el) + { + parse(txt, baseurl, el); + } + + void add_property(const tchar_t* name, const tchar_t* val, const tchar_t* baseurl, bool important, const element* el); + + const tchar_t* get_property(const tchar_t* name) const + { + if(name) + { + auto f = m_properties.find(name); + if(f != m_properties.end()) + { + return f->second.m_value.c_str(); + } + } + return nullptr; + } + + void combine(const litehtml::style& src); + void clear() + { + m_properties.clear(); + } + + private: + void parse_property(const tstring& txt, const tchar_t* baseurl, const element* el); + void parse(const tchar_t* txt, const tchar_t* baseurl, const element* el); + void parse_short_border(const tstring& prefix, const tstring& val, bool important); + void parse_short_background(const tstring& val, const tchar_t* baseurl, bool important); + void parse_short_font(const tstring& val, bool important); + static void subst_vars(tstring& str, const element* el); + void add_parsed_property(const tstring& name, const tstring& val, bool important); + void remove_property(const tstring& name, bool important); + }; +} + +#endif // LH_STYLE_H diff --git a/gb.form.htmlview/src/litehtml/stylesheet.cpp b/gb.form.htmlview/src/litehtml/stylesheet.cpp new file mode 100644 index 00000000..45fc2266 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/stylesheet.cpp @@ -0,0 +1,218 @@ +#include "html.h" +#include "stylesheet.h" +#include <algorithm> +#include "document.h" + + +void litehtml::css::parse_stylesheet(const tchar_t* str, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) +{ + tstring text = str; + + // remove comments + tstring::size_type c_start = text.find(_t("/*")); + while(c_start != tstring::npos) + { + tstring::size_type c_end = text.find(_t("*/"), c_start + 2); + text.erase(c_start, c_end - c_start + 2); + c_start = text.find(_t("/*")); + } + + tstring::size_type pos = text.find_first_not_of(_t(" \n\r\t")); + while(pos != tstring::npos) + { + while(pos != tstring::npos && text[pos] == _t('@')) + { + tstring::size_type sPos = pos; + pos = text.find_first_of(_t("{;"), pos); + if(pos != tstring::npos && text[pos] == _t('{')) + { + pos = find_close_bracket(text, pos, _t('{'), _t('}')); + } + if(pos != tstring::npos) + { + parse_atrule(text.substr(sPos, pos - sPos + 1), baseurl, doc, media); + } else + { + parse_atrule(text.substr(sPos), baseurl, doc, media); + } + + if(pos != tstring::npos) + { + pos = text.find_first_not_of(_t(" \n\r\t"), pos + 1); + } + } + + if(pos == tstring::npos) + { + break; + } + + tstring::size_type style_start = text.find(_t('{'), pos); + tstring::size_type style_end = text.find(_t('}'), pos); + if(style_start != tstring::npos && style_end != tstring::npos) + { + auto style = text.substr(style_start + 1, style_end - style_start - 1); + + parse_selectors(text.substr(pos, style_start - pos), style, media, baseurl ? baseurl : _t("")); + + if(media && doc) + { + doc->add_media_list(media); + } + + pos = style_end + 1; + } else + { + pos = tstring::npos; + } + + if(pos != tstring::npos) + { + pos = text.find_first_not_of(_t(" \n\r\t"), pos); + } + } +} + +void litehtml::css::parse_css_url( const tstring& str, tstring& url ) +{ + url = _t(""); + size_t pos1 = str.find(_t('(')); + size_t pos2 = str.find(_t(')')); + if(pos1 != tstring::npos && pos2 != tstring::npos) + { + url = str.substr(pos1 + 1, pos2 - pos1 - 1); + if(url.length()) + { + if(url[0] == _t('\'') || url[0] == _t('"')) + { + url.erase(0, 1); + } + } + if(url.length()) + { + if(url[url.length() - 1] == _t('\'') || url[url.length() - 1] == _t('"')) + { + url.erase(url.length() - 1, 1); + } + } + } +} + +bool litehtml::css::parse_selectors( const tstring& txt, const tstring& styles, const media_query_list::ptr& media, const tstring& baseurl ) +{ + tstring selector = txt; + trim(selector); + string_vector tokens; + split_string(selector, tokens, _t(",")); + + bool added_something = false; + + for(auto & token : tokens) + { + css_selector::ptr new_selector = std::make_shared<css_selector>(media, baseurl); + new_selector->m_style = styles; + trim(token); + if(new_selector->parse(token)) + { + new_selector->calc_specificity(); + add_selector(new_selector); + added_something = true; + } + } + + return added_something; +} + +void litehtml::css::sort_selectors() +{ + std::sort(m_selectors.begin(), m_selectors.end(), + [](const css_selector::ptr& v1, const css_selector::ptr& v2) + { + return (*v1) < (*v2); + } + ); +} + +void litehtml::css::parse_atrule(const tstring& text, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) +{ + if(text.substr(0, 7) == _t("@import")) + { + int sPos = 7; + tstring iStr; + iStr = text.substr(sPos); + if(iStr[iStr.length() - 1] == _t(';')) + { + iStr.erase(iStr.length() - 1); + } + trim(iStr); + string_vector tokens; + split_string(iStr, tokens, _t(" "), _t(""), _t("(\"")); + if(!tokens.empty()) + { + tstring url; + parse_css_url(tokens.front(), url); + if(url.empty()) + { + url = tokens.front(); + } + tokens.erase(tokens.begin()); + if(doc) + { + document_container* doc_cont = doc->container(); + if(doc_cont) + { + tstring css_text; + tstring css_baseurl; + if(baseurl) + { + css_baseurl = baseurl; + } + doc_cont->import_css(css_text, url, css_baseurl); + if(!css_text.empty()) + { + media_query_list::ptr new_media = media; + if(!tokens.empty()) + { + tstring media_str; + for(auto iter = tokens.begin(); iter != tokens.end(); iter++) + { + if(iter != tokens.begin()) + { + media_str += _t(" "); + } + media_str += (*iter); + } + new_media = media_query_list::create_from_string(media_str, doc); + if(!new_media) + { + new_media = media; + } + } + parse_stylesheet(css_text.c_str(), css_baseurl.c_str(), doc, new_media); + } + } + } + } + } else if(text.substr(0, 6) == _t("@media")) + { + tstring::size_type b1 = text.find_first_of(_t('{')); + tstring::size_type b2 = text.find_last_of(_t('}')); + if(b1 != tstring::npos) + { + tstring media_type = text.substr(6, b1 - 6); + trim(media_type); + media_query_list::ptr new_media = media_query_list::create_from_string(media_type, doc); + + tstring media_style; + if(b2 != tstring::npos) + { + media_style = text.substr(b1 + 1, b2 - b1 - 1); + } else + { + media_style = text.substr(b1 + 1); + } + + parse_stylesheet(media_style.c_str(), baseurl, doc, new_media); + } + } +} diff --git a/gb.form.htmlview/src/litehtml/stylesheet.h b/gb.form.htmlview/src/litehtml/stylesheet.h new file mode 100644 index 00000000..e0d06ad0 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/stylesheet.h @@ -0,0 +1,47 @@ +#ifndef LH_STYLESHEET_H +#define LH_STYLESHEET_H + +#include "style.h" +#include "css_selector.h" + +namespace litehtml +{ + class document_container; + + class css + { + css_selector::vector m_selectors; + public: + css() = default; + ~css() = default; + + const css_selector::vector& selectors() const + { + return m_selectors; + } + + void clear() + { + m_selectors.clear(); + } + + void parse_stylesheet(const tchar_t* str, const tchar_t* baseurl, const std::shared_ptr <document>& doc, const media_query_list::ptr& media); + void sort_selectors(); + static void parse_css_url(const tstring& str, tstring& url); + + private: + void parse_atrule(const tstring& text, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media); + void add_selector(const css_selector::ptr& selector); + bool parse_selectors(const tstring& txt, const tstring& styles, const media_query_list::ptr& media, const tstring& baseurl); + + }; + + inline void litehtml::css::add_selector( const css_selector::ptr& selector ) + { + selector->m_order = (int) m_selectors.size(); + m_selectors.push_back(selector); + } + +} + +#endif // LH_STYLESHEET_H diff --git a/gb.form.htmlview/src/litehtml/table.cpp b/gb.form.htmlview/src/litehtml/table.cpp new file mode 100644 index 00000000..89342d38 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/table.cpp @@ -0,0 +1,594 @@ +#include "html.h" +#include "table.h" +#include "html_tag.h" + +void litehtml::table_grid::add_cell(element::ptr& el) +{ + table_cell cell; + cell.el = el; + cell.colspan = t_atoi(el->get_attr(_t("colspan"), _t("1"))); + cell.rowspan = t_atoi(el->get_attr(_t("rowspan"), _t("1"))); + cell.borders = el->get_borders(); + + while( is_rowspanned( (int) m_cells.size() - 1, (int) m_cells.back().size() ) ) + { + m_cells.back().push_back(table_cell()); + } + + m_cells.back().push_back(cell); + for(int i = 1; i < cell.colspan; i++) + { + table_cell empty_cell; + m_cells.back().push_back(empty_cell); + } +} + + +void litehtml::table_grid::begin_row(element::ptr& row) +{ + std::vector<table_cell> r; + m_cells.push_back(r); + + m_rows.push_back(table_row(0, row)); + +} + + +bool litehtml::table_grid::is_rowspanned( int r, int c ) +{ + for(int row = r - 1; row >= 0; row--) + { + if(c < (int) m_cells[row].size()) + { + if(m_cells[row][c].rowspan > 1) + { + if(m_cells[row][c].rowspan >= r - row + 1) + { + return true; + } + } + } + } + return false; +} + +void litehtml::table_grid::finish() +{ + m_rows_count = (int) m_cells.size(); + m_cols_count = 0; + for(auto& cell : m_cells) + { + m_cols_count = std::max(m_cols_count, (int) cell.size()); + } + for(auto& cell : m_cells) + { + for(int j = (int) cell.size(); j < m_cols_count; j++) + { + table_cell empty_cell; + cell.push_back(empty_cell); + } + } + + m_columns.clear(); + for(int i = 0; i < m_cols_count; i++) + { + m_columns.push_back(table_column(0, 0)); + } + + for(int col = 0; col < m_cols_count; col++) + { + for(int row = 0; row < m_rows_count; row++) + { + if(cell(col, row)->el) + { + // find minimum left border width + if(m_columns[col].border_left) + { + m_columns[col].border_left = std::min(m_columns[col].border_left, cell(col, row)->borders.left); + } else + { + m_columns[col].border_left = cell(col, row)->borders.left; + } + // find minimum right border width + if(m_columns[col].border_right) + { + m_columns[col].border_right = std::min(m_columns[col].border_right, cell(col, row)->borders.right); + } else + { + m_columns[col].border_right = cell(col, row)->borders.right; + } + // find minimum top border width + if(m_rows[row].border_top) + { + m_rows[row].border_top = std::min(m_rows[row].border_top, cell(col, row)->borders.top); + } else + { + m_rows[row].border_top = cell(col, row)->borders.top; + } + // find minimum bottom border width + if(m_rows[row].border_bottom) + { + m_rows[row].border_bottom = std::min(m_rows[row].border_bottom, cell(col, row)->borders.bottom); + } else + { + m_rows[row].border_bottom = cell(col, row)->borders.bottom; + } + } + + if(cell(col, row)->el && cell(col, row)->colspan <= 1) + { + if (!cell(col, row)->el->get_css_width().is_predefined() && m_columns[col].css_width.is_predefined()) + { + m_columns[col].css_width = cell(col, row)->el->get_css_width(); + } + } + } + } + + for(int col = 0; col < m_cols_count; col++) + { + for(int row = 0; row < m_rows_count; row++) + { + if(cell(col, row)->el && cell(col, row)->colspan == 1) + { + cell(col, row)->el->set_css_width(m_columns[col].css_width); + } + } + } +} + +litehtml::table_cell* litehtml::table_grid::cell( int t_col, int t_row ) +{ + if(t_col >= 0 && t_col < m_cols_count && t_row >= 0 && t_row < m_rows_count) + { + return &m_cells[t_row][t_col]; + } + return nullptr; +} + +void litehtml::table_grid::distribute_max_width( int width, int start, int end ) +{ + table_column_accessor_max_width selector; + distribute_width(width, start, end, &selector); +} + +void litehtml::table_grid::distribute_min_width( int width, int start, int end ) +{ + table_column_accessor_min_width selector; + distribute_width(width, start, end, &selector); +} + +void litehtml::table_grid::distribute_width( int width, int start, int end, table_column_accessor* acc ) +{ + if(!(start >= 0 && start < m_cols_count && end >= 0 && end < m_cols_count)) + { + return; + } + + int cols_width = 0; + for(int col = start; col <= end; col++) + { + cols_width += m_columns[col].max_width; + } + + int add = width / (end - start + 1); + int added_width = 0; + for(int col = start; col <= end; col++) + { + if(cols_width) + { + add = round_f( (float) width * ((float) m_columns[col].max_width / (float) cols_width) ); + } + added_width += add; + acc->get(m_columns[col]) += add; + } + if(added_width < width) + { + acc->get(m_columns[start]) += width - added_width; + } +} + +void litehtml::table_grid::distribute_width( int width, int start, int end ) +{ + if(!(start >= 0 && start < m_cols_count && end >= 0 && end < m_cols_count)) + { + return; + } + + std::vector<table_column*> distribute_columns; + + for(int step = 0; step < 3; step++) + { + distribute_columns.clear(); + + switch(step) + { + case 0: + { + // distribute between the columns with width == auto + for(int col = start; col <= end; col++) + { + if(m_columns[col].css_width.is_predefined()) + { + distribute_columns.push_back(&m_columns[col]); + } + } + } + break; + case 1: + { + // distribute between the columns with percents + for(int col = start; col <= end; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + distribute_columns.push_back(&m_columns[col]); + } + } + } + break; + case 2: + { + //well distribute between all columns + for(int col = start; col <= end; col++) + { + distribute_columns.push_back(&m_columns[col]); + } + } + break; + } + + int added_width = 0; + + if(!distribute_columns.empty() || step == 2) + { + int cols_width = 0; + for(const auto& column : distribute_columns) + { + cols_width += column->max_width - column->min_width; + } + + if(cols_width) + { + int add = width / (int) distribute_columns.size(); + for(const auto& column : distribute_columns) + { + add = round_f( (float) width * ((float) (column->max_width - column->min_width) / (float) cols_width) ); + if(column->width + add >= column->min_width) + { + column->width += add; + added_width += add; + } else + { + added_width += (column->width - column->min_width) * (add / abs(add)); + column->width = column->min_width; + } + } + if(added_width < width && step) + { + distribute_columns.front()->width += width - added_width; + added_width = width; + } + } else + { + distribute_columns.back()->width += width; + added_width = width; + } + } + + if(added_width == width) + { + break; + } else + { + width -= added_width; + } + } +} + +int litehtml::table_grid::calc_table_width(int block_width, bool is_auto, int& min_table_width, int& max_table_width) +{ + //int table_width = 0; + + min_table_width = 0; // MIN + max_table_width = 0; // MAX + + int cur_width = 0; + int max_w = 0; + int min_w = 0; + + for(int col = 0; col < m_cols_count; col++) + { + min_table_width += m_columns[col].min_width; + max_table_width += m_columns[col].max_width; + + if(!m_columns[col].css_width.is_predefined()) + { + m_columns[col].width = m_columns[col].css_width.calc_percent(block_width); + m_columns[col].width = std::max(m_columns[col].width, m_columns[col].min_width); + } else + { + m_columns[col].width = m_columns[col].min_width; + max_w += m_columns[col].max_width; + min_w += m_columns[col].min_width; + } + + cur_width += m_columns[col].width; + } + + if(cur_width == block_width) + { + return cur_width; + } + + if(cur_width < block_width) + { + if(cur_width - min_w + max_w <= block_width) + { + cur_width = 0; + for(int col = 0; col < m_cols_count; col++) + { + if(m_columns[col].css_width.is_predefined()) + { + m_columns[col].width = m_columns[col].max_width; + } + cur_width += m_columns[col].width; + } + if(cur_width == block_width || is_auto) + { + return cur_width; + } + } + distribute_width(block_width - cur_width, 0, m_cols_count - 1); + cur_width = 0; + for(int col = 0; col < m_cols_count; col++) + { + cur_width += m_columns[col].width; + } + } else + { + int fixed_width = 0; + float percent = 0; + for(int col = 0; col < m_cols_count; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + percent += m_columns[col].css_width.val(); + } else + { + fixed_width += m_columns[col].width; + } + } + auto scale = (float) (100.0 / percent); + cur_width = 0; + for(int col = 0; col < m_cols_count; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + css_length w; + w.set_value(m_columns[col].css_width.val() * scale, css_units_percentage); + m_columns[col].width = w.calc_percent(block_width - fixed_width); + if(m_columns[col].width < m_columns[col].min_width) + { + m_columns[col].width = m_columns[col].min_width; + } + } + cur_width += m_columns[col].width; + } + // If the table is still too wide shrink columns with % widths + if(cur_width > block_width) + { + while(true) + { + bool shrunk = false; + for(int col = 0; col < m_cols_count; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + if(m_columns[col].width > m_columns[col].min_width) + { + m_columns[col].width--; + cur_width--; + shrunk = true; + if(cur_width == block_width) + { + break; + } + } + } + } + if(cur_width == block_width || !shrunk) + { + break; + } + } + } + } + return cur_width; +} + +void litehtml::table_grid::clear() +{ + m_rows_count = 0; + m_cols_count = 0; + m_cells.clear(); + m_columns.clear(); + m_rows.clear(); +} + +void litehtml::table_grid::calc_horizontal_positions( margins& table_borders, border_collapse bc, int bdr_space_x) +{ + if(bc == border_collapse_separate) + { + int left = bdr_space_x; + for(int i = 0; i < m_cols_count; i++) + { + m_columns[i].left = left; + m_columns[i].right = m_columns[i].left + m_columns[i].width; + left = m_columns[i].right + bdr_space_x; + } + } else + { + int left = 0; + if(m_cols_count) + { + left -= std::min(table_borders.left, m_columns[0].border_left); + } + for(int i = 0; i < m_cols_count; i++) + { + if(i > 0) + { + left -= std::min(m_columns[i - 1].border_right, m_columns[i].border_left); + } + + m_columns[i].left = left; + m_columns[i].right = m_columns[i].left + m_columns[i].width; + left = m_columns[i].right; + } + } +} + +void litehtml::table_grid::calc_vertical_positions( margins& table_borders, border_collapse bc, int bdr_space_y ) +{ + if(bc == border_collapse_separate) + { + int top = bdr_space_y; + for(int i = 0; i < m_rows_count; i++) + { + m_rows[i].top = top; + m_rows[i].bottom = m_rows[i].top + m_rows[i].height; + top = m_rows[i].bottom + bdr_space_y; + } + } else + { + int top = 0; + if(m_rows_count) + { + top -= std::min(table_borders.top, m_rows[0].border_top); + } + for(int i = 0; i < m_rows_count; i++) + { + if(i > 0) + { + top -= std::min(m_rows[i - 1].border_bottom, m_rows[i].border_top); + } + + m_rows[i].top = top; + m_rows[i].bottom = m_rows[i].top + m_rows[i].height; + top = m_rows[i].bottom; + } + } +} + +void litehtml::table_grid::calc_rows_height(int blockHeight, int borderSpacingY) +{ + int min_table_height = 0; + + // compute vertical size inferred by cells + for (auto& row : m_rows) + { + if (!row.css_height.is_predefined()) + { + if (row.css_height.units() != css_units_percentage) + { + if (row.height < (int)row.css_height.val()) + { + row.height = (int)row.css_height.val(); + } + } + } + row.min_height = row.height; + min_table_height += row.height; + } + + //min_table_height += borderSpacingY * ((int) m_rows.size() + 1); + + if (blockHeight > min_table_height) + { + int extra_height = blockHeight - min_table_height; + int auto_count = 0; // number of rows with height=auto + for (auto& row : m_rows) + { + if (!row.css_height.is_predefined() && row.css_height.units() == css_units_percentage) + { + row.height = row.css_height.calc_percent(blockHeight); + if (row.height < row.min_height) + { + row.height = row.min_height; + } + + extra_height -= row.height - row.min_height; + + if (extra_height <= 0) break; + } + else if (row.css_height.is_predefined()) + { + auto_count++; + } + } + if (extra_height > 0) + { + if (auto_count) + { + // distribute height to the rows with height=auto + int extra_row_height = (int)(extra_height / auto_count); + for (auto& row : m_rows) + { + if (row.css_height.is_predefined()) + { + row.height += extra_row_height; + } + } + } + else + { + // We don't have rows with height=auto, so distribute height to all rows + if (!m_rows.empty()) + { + int extra_row_height = (int)(extra_height / m_rows.size()); + for (auto& row : m_rows) + { + row.height += extra_row_height; + } + } + } + } + else if (extra_height < 0) + { + extra_height = -extra_height; + for (auto row = m_rows.rbegin(); row < m_rows.rend() && extra_height > 0; row++) + { + if (row->height > row->min_height) + { + if (row->height - extra_height >= row->min_height) + { + row->height -= extra_height; + extra_height = 0; + } + else + { + extra_height -= row->height - row->min_height; + row->height = row->min_height; + } + } + } + } + } +} + +////////////////////////////////////////////////////////////////////////// + +int& litehtml::table_column_accessor_max_width::get( table_column& col ) +{ + return col.max_width; +} + +int& litehtml::table_column_accessor_min_width::get( table_column& col ) +{ + return col.min_width; +} + +int& litehtml::table_column_accessor_width::get( table_column& col ) +{ + return col.width; +} diff --git a/gb.form.htmlview/src/litehtml/table.h b/gb.form.htmlview/src/litehtml/table.h new file mode 100644 index 00000000..e71dcb6d --- /dev/null +++ b/gb.form.htmlview/src/litehtml/table.h @@ -0,0 +1,251 @@ +#ifndef LH_TABLE_H +#define LH_TABLE_H + +namespace litehtml +{ + struct table_row + { + typedef std::vector<table_row> vector; + + int height; + int border_top; + int border_bottom; + element::ptr el_row; + int top; + int bottom; + css_length css_height; + int min_height; + + table_row() + { + min_height = 0; + top = 0; + bottom = 0; + border_bottom = 0; + border_top = 0; + height = 0; + el_row = nullptr; + css_height.predef(0); + } + + table_row(int h, element::ptr& row) + { + min_height = 0; + height = h; + el_row = row; + border_bottom = 0; + border_top = 0; + top = 0; + bottom = 0; + if (row) + { + css_height = row->get_css_height(); + } + } + + table_row(const table_row& val) + { + min_height = val.min_height; + top = val.top; + bottom = val.bottom; + border_bottom = val.border_bottom; + border_top = val.border_top; + height = val.height; + css_height = val.css_height; + el_row = val.el_row; + } + + table_row(table_row&& val) noexcept + { + min_height = val.min_height; + top = val.top; + bottom = val.bottom; + border_bottom = val.border_bottom; + border_top = val.border_top; + height = val.height; + css_height = val.css_height; + el_row = std::move(val.el_row); + } + }; + + struct table_column + { + typedef std::vector<table_column> vector; + + int min_width; + int max_width; + int width; + css_length css_width; + int border_left; + int border_right; + int left; + int right; + + table_column() + { + left = 0; + right = 0; + border_left = 0; + border_right = 0; + min_width = 0; + max_width = 0; + width = 0; + css_width.predef(0); + } + + table_column(int min_w, int max_w) + { + left = 0; + right = 0; + border_left = 0; + border_right = 0; + max_width = max_w; + min_width = min_w; + width = 0; + css_width.predef(0); + } + + table_column(const table_column& val) + { + left = val.left; + right = val.right; + border_left = val.border_left; + border_right = val.border_right; + max_width = val.max_width; + min_width = val.min_width; + width = val.width; + css_width = val.css_width; + } + }; + + class table_column_accessor + { + public: + virtual int& get(table_column& col) = 0; + + protected: + ~table_column_accessor() = default; + }; + + class table_column_accessor_max_width final : public table_column_accessor + { + public: + int& get(table_column& col) override; + }; + + class table_column_accessor_min_width final : public table_column_accessor + { + public: + int& get(table_column& col) override; + }; + + class table_column_accessor_width final : public table_column_accessor + { + public: + int& get(table_column& col) override; + }; + + struct table_cell + { + element::ptr el; + int colspan; + int rowspan; + int min_width; + int min_height; + int max_width; + int max_height; + int width; + int height; + margins borders; + + table_cell() + { + min_width = 0; + min_height = 0; + max_width = 0; + max_height = 0; + width = 0; + height = 0; + colspan = 1; + rowspan = 1; + el = nullptr; + } + + table_cell(const table_cell& val) + { + el = val.el; + colspan = val.colspan; + rowspan = val.rowspan; + width = val.width; + height = val.height; + min_width = val.min_width; + min_height = val.min_height; + max_width = val.max_width; + max_height = val.max_height; + borders = val.borders; + } + + table_cell(table_cell&& val) noexcept + { + el = std::move(val.el); + colspan = val.colspan; + rowspan = val.rowspan; + width = val.width; + height = val.height; + min_width = val.min_width; + min_height = val.min_height; + max_width = val.max_width; + max_height = val.max_height; + borders = val.borders; + } + }; + + class table_grid + { + public: + typedef std::vector< std::vector<table_cell> > rows; + private: + int m_rows_count; + int m_cols_count; + rows m_cells; + table_column::vector m_columns; + table_row::vector m_rows; + elements_vector m_captions; + int m_captions_height; + public: + + table_grid() + { + m_rows_count = 0; + m_cols_count = 0; + m_captions_height = 0; + } + + void clear(); + void begin_row(element::ptr& row); + void add_cell(element::ptr& el); + bool is_rowspanned(int r, int c); + void finish(); + table_cell* cell(int t_col, int t_row); + table_column& column(int c) { return m_columns[c]; } + table_row& row(int r) { return m_rows[r]; } + elements_vector& captions() { return m_captions; } + + int rows_count() const { return m_rows_count; } + int cols_count() const { return m_cols_count; } + + void captions_height(int height) { m_captions_height = height; } + int captions_height() const { return m_captions_height; } + + void distribute_max_width(int width, int start, int end); + void distribute_min_width(int width, int start, int end); + void distribute_width(int width, int start, int end); + void distribute_width(int width, int start, int end, table_column_accessor* acc); + int calc_table_width(int block_width, bool is_auto, int& min_table_width, int& max_table_width); + void calc_horizontal_positions(margins& table_borders, border_collapse bc, int bdr_space_x); + void calc_vertical_positions(margins& table_borders, border_collapse bc, int bdr_space_y); + void calc_rows_height(int blockHeight, int borderSpacingY); + }; +} + +#endif // LH_TABLE_H diff --git a/gb.form.htmlview/src/litehtml/tstring_view.cpp b/gb.form.htmlview/src/litehtml/tstring_view.cpp new file mode 100644 index 00000000..76f8b45a --- /dev/null +++ b/gb.form.htmlview/src/litehtml/tstring_view.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "litehtml/tstring_view.h" + +namespace litehtml { + + +std::basic_ostream<tstring_view::value_type>& operator<<( + std::basic_ostream<tstring_view::value_type>& os, + tstring_view str) +{ + if (os.good()) { + os.write(str.data(), str.size()); + } + + return os; +} + +} // namespace litehtml \ No newline at end of file diff --git a/gb.form.htmlview/src/litehtml/tstring_view.h b/gb.form.htmlview/src/litehtml/tstring_view.h new file mode 100644 index 00000000..b4e6d13c --- /dev/null +++ b/gb.form.htmlview/src/litehtml/tstring_view.h @@ -0,0 +1,136 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_TSTRING_VIEW_H__ +#define LITEHTML_TSTRING_VIEW_H__ + +#include <cstddef> +#include <ostream> + +#include "litehtml/os_types.h" + +namespace litehtml { + +// tstring_view is a string reference type that provides a view into a string +// that is owned elsewhere (e.g., by a std::string object). + +// tstring_view implements the same interface as std::base_string_view in the +// standard library. When litehtml moves to C++17 consider replacing the +// tstring_view implementation with the standard library implementations +// (e.g., via a using statement). + +class tstring_view { +public: + using value_type = tchar_t; + + using pointer = tchar_t*; + + using const_pointer = const tchar_t*; + + using reference = tchar_t&; + + using const_reference = const tchar_t&; + + using iterator = const_pointer; + + using const_iterator = const_pointer; + + using size_type = size_t; + + using difference_type = std::ptrdiff_t; + +public: + tstring_view() = default; + + tstring_view(const tstring_view& other) = default; + + tstring_view(const_pointer s, size_type size) + : data_(s) + , size_(size) + { + } + + constexpr const_iterator begin() const + { + return data_; + } + + constexpr const_iterator cbegin() const + { + return data_; + } + + constexpr const_iterator end() const + { + return data_ + size_; + } + + constexpr const_iterator cend() const + { + return data_ + size_; + } + + constexpr const_reference operator[](size_type offset) const + { + return *(data_ + offset); + } + + constexpr const_pointer data() const + { + return data_; + } + + size_type size() const + { + return size_; + } + + size_type length() const + { + return size_; + } + + bool empty() const + { + return (size_ == 0); + } + +private: + const_pointer data_ = nullptr; + + size_type size_ = 0; +}; + +std::basic_ostream<tstring_view::value_type>& operator<<( + std::basic_ostream<tstring_view::value_type>&, + tstring_view str); + +} // namespace litehtml + +#endif // LITEHTML_TSTRING_VIEW_H__ diff --git a/gb.form.htmlview/src/litehtml/types.h b/gb.form.htmlview/src/litehtml/types.h new file mode 100644 index 00000000..0ba9979b --- /dev/null +++ b/gb.form.htmlview/src/litehtml/types.h @@ -0,0 +1,750 @@ +#ifndef LH_TYPES_H +#define LH_TYPES_H + +#include <stdlib.h> +#include <memory> +#include <map> +#include <vector> + +namespace litehtml +{ + class document; + class element; + + typedef std::map<litehtml::tstring, litehtml::tstring> string_map; + typedef std::vector< std::shared_ptr<litehtml::element> > elements_vector; + typedef std::vector<int> int_vector; + typedef std::vector<litehtml::tstring> string_vector; + + const unsigned int font_decoration_none = 0x00; + const unsigned int font_decoration_underline = 0x01; + const unsigned int font_decoration_linethrough = 0x02; + const unsigned int font_decoration_overline = 0x04; + + typedef unsigned char byte; + typedef unsigned int ucode_t; + + struct margins + { + int left; + int right; + int top; + int bottom; + + margins() + { + left = right = top = bottom = 0; + } + + int width() const { return left + right; } + int height() const { return top + bottom; } + }; + + struct size + { + int width; + int height; + + size() + { + width = 0; + height = 0; + } + }; + + struct position + { + typedef std::vector<position> vector; + + int x; + int y; + int width; + int height; + + position() + { + x = y = width = height = 0; + } + + position(int x, int y, int width, int height) + { + this->x = x; + this->y = y; + this->width = width; + this->height = height; + } + + int right() const { return x + width; } + int bottom() const { return y + height; } + int left() const { return x; } + int top() const { return y; } + + void operator+=(const margins& mg) + { + x -= mg.left; + y -= mg.top; + width += mg.left + mg.right; + height += mg.top + mg.bottom; + } + void operator-=(const margins& mg) + { + x += mg.left; + y += mg.top; + width -= mg.left + mg.right; + height -= mg.top + mg.bottom; + } + + void clear() + { + x = y = width = height = 0; + } + + void operator=(const size& sz) + { + width = sz.width; + height = sz.height; + } + + void move_to(int x, int y) + { + this->x = x; + this->y = y; + } + + bool does_intersect(const position* val) const + { + if(!val) return true; + + return ( + left() <= val->right() && + right() >= val->left() && + bottom() >= val->top() && + top() <= val->bottom() ) + || ( + val->left() <= right() && + val->right() >= left() && + val->bottom() >= top() && + val->top() <= bottom() ); + } + + bool empty() const + { + if(!width && !height) + { + return true; + } + return false; + } + + bool is_point_inside(int x, int y) const + { + if(x >= left() && x <= right() && y >= top() && y <= bottom()) + { + return true; + } + return false; + } + }; + + struct font_metrics + { + int height; + int ascent; + int descent; + int x_height; + bool draw_spaces; + + font_metrics() + { + height = 0; + ascent = 0; + descent = 0; + x_height = 0; + draw_spaces = true; + } + int base_line() { return descent; } + }; + + struct font_item + { + uint_ptr font; + font_metrics metrics; + }; + + typedef std::map<tstring, font_item> fonts_map; + + enum draw_flag + { + draw_root, + draw_block, + draw_floats, + draw_inlines, + draw_positioned, + }; + +#define style_display_strings _t("none;block;inline;inline-block;inline-table;list-item;table;table-caption;table-cell;table-column;table-column-group;table-footer-group;table-header-group;table-row;table-row-group;inline-text") + + enum style_display + { + display_none, + display_block, + display_inline, + display_inline_block, + display_inline_table, + display_list_item, + display_table, + display_table_caption, + display_table_cell, + display_table_column, + display_table_column_group, + display_table_footer_group, + display_table_header_group, + display_table_row, + display_table_row_group, + display_inline_text, + }; + + enum style_border + { + borderNope, + borderNone, + borderHidden, + borderDotted, + borderDashed, + borderSolid, + borderDouble + }; + +#define font_size_strings _t("xx-small;x-small;small;medium;large;x-large;xx-large;smaller;larger") + + enum font_size + { + fontSize_xx_small, + fontSize_x_small, + fontSize_small, + fontSize_medium, + fontSize_large, + fontSize_x_large, + fontSize_xx_large, + fontSize_smaller, + fontSize_larger, + }; + +#define font_style_strings _t("normal;italic") + + enum font_style + { + fontStyleNormal, + fontStyleItalic + }; + +#define font_variant_strings _t("normal;small-caps") + + enum font_variant + { + font_variant_normal, + font_variant_italic + }; + +#define font_weight_strings _t("normal;bold;bolder;lighter;100;200;300;400;500;600;700") + + enum font_weight + { + fontWeightNormal, + fontWeightBold, + fontWeightBolder, + fontWeightLighter, + fontWeight100, + fontWeight200, + fontWeight300, + fontWeight400, + fontWeight500, + fontWeight600, + fontWeight700 + }; + +#define list_style_type_strings _t("none;circle;disc;square;armenian;cjk-ideographic;decimal;decimal-leading-zero;georgian;hebrew;hiragana;hiragana-iroha;katakana;katakana-iroha;lower-alpha;lower-greek;lower-latin;lower-roman;upper-alpha;upper-latin;upper-roman") + + enum list_style_type + { + list_style_type_none, + list_style_type_circle, + list_style_type_disc, + list_style_type_square, + list_style_type_armenian, + list_style_type_cjk_ideographic, + list_style_type_decimal, + list_style_type_decimal_leading_zero, + list_style_type_georgian, + list_style_type_hebrew, + list_style_type_hiragana, + list_style_type_hiragana_iroha, + list_style_type_katakana, + list_style_type_katakana_iroha, + list_style_type_lower_alpha, + list_style_type_lower_greek, + list_style_type_lower_latin, + list_style_type_lower_roman, + list_style_type_upper_alpha, + list_style_type_upper_latin, + list_style_type_upper_roman, + }; + +#define list_style_position_strings _t("inside;outside") + + enum list_style_position + { + list_style_position_inside, + list_style_position_outside + }; + +#define vertical_align_strings _t("baseline;sub;super;top;text-top;middle;bottom;text-bottom") + + enum vertical_align + { + va_baseline, + va_sub, + va_super, + va_top, + va_text_top, + va_middle, + va_bottom, + va_text_bottom + }; + +#define border_width_strings _t("thin;medium;thick") + + enum border_width + { + border_width_thin, + border_width_medium, + border_width_thick + }; + +#define border_style_strings _t("none;hidden;dotted;dashed;solid;double;groove;ridge;inset;outset") + + enum border_style + { + border_style_none, + border_style_hidden, + border_style_dotted, + border_style_dashed, + border_style_solid, + border_style_double, + border_style_groove, + border_style_ridge, + border_style_inset, + border_style_outset + }; + +#define element_float_strings _t("none;left;right") + + enum element_float + { + float_none, + float_left, + float_right + }; + +#define element_clear_strings _t("none;left;right;both") + + enum element_clear + { + clear_none, + clear_left, + clear_right, + clear_both + }; + +#define css_units_strings _t("none;%;in;cm;mm;em;ex;pt;pc;px;dpi;dpcm;vw;vh;vmin;vmax;rem") + + enum css_units + { + css_units_none, + css_units_percentage, + css_units_in, + css_units_cm, + css_units_mm, + css_units_em, + css_units_ex, + css_units_pt, + css_units_pc, + css_units_px, + css_units_dpi, + css_units_dpcm, + css_units_vw, + css_units_vh, + css_units_vmin, + css_units_vmax, + css_units_rem, + }; + +#define background_attachment_strings _t("scroll;fixed") + + enum background_attachment + { + background_attachment_scroll, + background_attachment_fixed + }; + +#define background_repeat_strings _t("repeat;repeat-x;repeat-y;no-repeat") + + enum background_repeat + { + background_repeat_repeat, + background_repeat_repeat_x, + background_repeat_repeat_y, + background_repeat_no_repeat + }; + +#define background_box_strings _t("border-box;padding-box;content-box") + + enum background_box + { + background_box_border, + background_box_padding, + background_box_content + }; + +#define element_position_strings _t("static;relative;absolute;fixed") + + enum element_position + { + element_position_static, + element_position_relative, + element_position_absolute, + element_position_fixed, + }; + +#define text_align_strings _t("left;right;center;justify") + + enum text_align + { + text_align_left, + text_align_right, + text_align_center, + text_align_justify + }; + +#define text_transform_strings _t("none;capitalize;uppercase;lowercase") + + enum text_transform + { + text_transform_none, + text_transform_capitalize, + text_transform_uppercase, + text_transform_lowercase + }; + +#define white_space_strings _t("normal;nowrap;pre;pre-line;pre-wrap") + + enum white_space + { + white_space_normal, + white_space_nowrap, + white_space_pre, + white_space_pre_line, + white_space_pre_wrap + }; + +#define overflow_strings _t("visible;hidden;scroll;auto;no-display;no-content") + + enum overflow + { + overflow_visible, + overflow_hidden, + overflow_scroll, + overflow_auto, + overflow_no_display, + overflow_no_content + }; + +#define background_size_strings _t("auto;cover;contain") + + enum background_size + { + background_size_auto, + background_size_cover, + background_size_contain, + }; + +#define visibility_strings _t("visible;hidden;collapse") + + enum visibility + { + visibility_visible, + visibility_hidden, + visibility_collapse, + }; + +#define border_collapse_strings _t("collapse;separate") + + enum border_collapse + { + border_collapse_collapse, + border_collapse_separate, + }; + + +#define pseudo_class_strings _t("only-child;only-of-type;first-child;first-of-type;last-child;last-of-type;nth-child;nth-of-type;nth-last-child;nth-last-of-type;not;lang") + + enum pseudo_class + { + pseudo_class_only_child, + pseudo_class_only_of_type, + pseudo_class_first_child, + pseudo_class_first_of_type, + pseudo_class_last_child, + pseudo_class_last_of_type, + pseudo_class_nth_child, + pseudo_class_nth_of_type, + pseudo_class_nth_last_child, + pseudo_class_nth_last_of_type, + pseudo_class_not, + pseudo_class_lang, + }; + +#define content_property_string _t("none;normal;open-quote;close-quote;no-open-quote;no-close-quote") + + enum content_property + { + content_property_none, + content_property_normal, + content_property_open_quote, + content_property_close_quote, + content_property_no_open_quote, + content_property_no_close_quote, + }; + + + struct floated_box + { + typedef std::vector<floated_box> vector; + + position pos; + element_float float_side; + element_clear clear_floats; + std::shared_ptr<element> el; + + floated_box() = default; + floated_box(const floated_box& val) + { + pos = val.pos; + float_side = val.float_side; + clear_floats = val.clear_floats; + el = val.el; + } + floated_box& operator=(const floated_box& val) + { + pos = val.pos; + float_side = val.float_side; + clear_floats = val.clear_floats; + el = val.el; + return *this; + } + floated_box(floated_box&& val) + { + pos = val.pos; + float_side = val.float_side; + clear_floats = val.clear_floats; + el = std::move(val.el); + } + void operator=(floated_box&& val) + { + pos = val.pos; + float_side = val.float_side; + clear_floats = val.clear_floats; + el = std::move(val.el); + } + }; + + struct int_int_cache + { + int hash; + int val; + bool is_valid; + bool is_default; + + int_int_cache() + { + hash = 0; + val = 0; + is_valid = false; + is_default = false; + } + void invalidate() + { + is_valid = false; + is_default = false; + } + void set_value(int vHash, int vVal) + { + hash = vHash; + val = vVal; + is_valid = true; + } + }; + + enum select_result + { + select_no_match = 0x00, + select_match = 0x01, + select_match_pseudo_class = 0x02, + select_match_with_before = 0x10, + select_match_with_after = 0x20, + }; + + template<class T> + class def_value + { + T m_val; + bool m_is_default; + public: + def_value(T def_val) + { + m_is_default = true; + m_val = def_val; + } + void reset(T def_val) + { + m_is_default = true; + m_val = def_val; + } + bool is_default() + { + return m_is_default; + } + T operator=(T new_val) + { + m_val = new_val; + m_is_default = false; + return m_val; + } + operator T() + { + return m_val; + } + }; + + +#define media_orientation_strings _t("portrait;landscape") + + enum media_orientation + { + media_orientation_portrait, + media_orientation_landscape, + }; + +#define media_feature_strings _t("none;width;min-width;max-width;height;min-height;max-height;device-width;min-device-width;max-device-width;device-height;min-device-height;max-device-height;orientation;aspect-ratio;min-aspect-ratio;max-aspect-ratio;device-aspect-ratio;min-device-aspect-ratio;max-device-aspect-ratio;color;min-color;max-color;color-index;min-color-index;max-color-index;monochrome;min-monochrome;max-monochrome;resolution;min-resolution;max-resolution") + + enum media_feature + { + media_feature_none, + + media_feature_width, + media_feature_min_width, + media_feature_max_width, + + media_feature_height, + media_feature_min_height, + media_feature_max_height, + + media_feature_device_width, + media_feature_min_device_width, + media_feature_max_device_width, + + media_feature_device_height, + media_feature_min_device_height, + media_feature_max_device_height, + + media_feature_orientation, + + media_feature_aspect_ratio, + media_feature_min_aspect_ratio, + media_feature_max_aspect_ratio, + + media_feature_device_aspect_ratio, + media_feature_min_device_aspect_ratio, + media_feature_max_device_aspect_ratio, + + media_feature_color, + media_feature_min_color, + media_feature_max_color, + + media_feature_color_index, + media_feature_min_color_index, + media_feature_max_color_index, + + media_feature_monochrome, + media_feature_min_monochrome, + media_feature_max_monochrome, + + media_feature_resolution, + media_feature_min_resolution, + media_feature_max_resolution, + }; + +#define box_sizing_strings _t("content-box;border-box") + + enum box_sizing + { + box_sizing_content_box, + box_sizing_border_box, + }; + + +#define media_type_strings _t("none;all;screen;print;braille;embossed;handheld;projection;speech;tty;tv") + + enum media_type + { + media_type_none, + media_type_all, + media_type_screen, + media_type_print, + media_type_braille, + media_type_embossed, + media_type_handheld, + media_type_projection, + media_type_speech, + media_type_tty, + media_type_tv, + }; + + struct media_features + { + media_type type; + int width; // (pixels) For continuous media, this is the width of the viewport including the size of a rendered scroll bar (if any). For paged media, this is the width of the page box. + int height; // (pixels) The height of the targeted display area of the output device. For continuous media, this is the height of the viewport including the size of a rendered scroll bar (if any). For paged media, this is the height of the page box. + int device_width; // (pixels) The width of the rendering surface of the output device. For continuous media, this is the width of the screen. For paged media, this is the width of the page sheet size. + int device_height; // (pixels) The height of the rendering surface of the output device. For continuous media, this is the height of the screen. For paged media, this is the height of the page sheet size. + int color; // The number of bits per color component of the output device. If the device is not a color device, the value is zero. + int color_index; // The number of entries in the color lookup table of the output device. If the device does not use a color lookup table, the value is zero. + int monochrome; // The number of bits per pixel in a monochrome frame buffer. If the device is not a monochrome device, the output device value will be 0. + int resolution; // The resolution of the output device (in DPI) + + media_features() + { + type = media_type::media_type_none, + width =0; + height = 0; + device_width = 0; + device_height = 0; + color = 0; + color_index = 0; + monochrome = 0; + resolution = 0; + } + }; + + enum render_type + { + render_all, + render_no_fixed, + render_fixed_only, + }; + + // List of the Void Elements (can't have any contents) + const litehtml::tchar_t* const void_elements = _t("area;base;br;col;command;embed;hr;img;input;keygen;link;meta;param;source;track;wbr"); +} + +#endif // LH_TYPES_H diff --git a/gb.form.htmlview/src/litehtml/url.cpp b/gb.form.htmlview/src/litehtml/url.cpp new file mode 100644 index 00000000..7670ce7e --- /dev/null +++ b/gb.form.htmlview/src/litehtml/url.cpp @@ -0,0 +1,163 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "litehtml/url.h" + +#include <iostream> +#include <sstream> +#include <algorithm> + +#include "litehtml/codepoint.h" +#include "litehtml/url_path.h" + +namespace litehtml { + +url::url(const tstring& str) +: str_(str) +{ + // TODO: Rewrite using tstring_view to avoid unnecessary allocations. + tstring tmp = str_; + + // Does the URL include a scheme? + size_t offset = tmp.find(_t(':')); + if (offset != tstring::npos) { + bool valid_scheme = true; + for (size_t i = 0; i < offset; i++) { + if (!is_url_scheme_codepoint(tmp[i])) { + valid_scheme = false; + break; + } + } + if (valid_scheme) { + scheme_ = tmp.substr(0, offset); + tmp = tmp.substr(offset + 1); + } + } + + // Does the URL include an authority? An authority component is preceded + // by a double slash ("//") and is terminated by the next slash ("/"), + // question mark ("?"), number sign ("#"), or the end of the URL. + + if (tmp.size() >= 2 && tmp[0] == _t('/') && tmp[1] == _t('/')) { + tmp = tmp.substr(2); + offset = tmp.size(); + offset = std::min(offset, tmp.find(_t('/'))); + offset = std::min(offset, tmp.find(_t('?'))); + offset = std::min(offset, tmp.find(_t('#'))); + authority_ = tmp.substr(0, offset); + tmp = tmp.substr(offset); + + // TODO: Parse the network location into host and port? + } + + // Does the URL include a fragment? + offset = tmp.find(_t('#')); + if (offset != tstring::npos) { + fragment_ = tmp.substr(offset + 1); + tmp = tmp.substr(0, offset); + } + + // Does the URL include a query? + offset = tmp.find(_t('?')); + if (offset != tstring::npos) { + query_ = tmp.substr(offset + 1); + tmp = tmp.substr(0, offset); + } + + // Whatever remains of the URL after removing the scheme, the network + // location, the query, and the fragment is the path. + path_ = tmp; +} + +url::url(const tstring& scheme, + const tstring& authority, + const tstring& path, + const tstring& query, + const tstring& fragment) +: scheme_(scheme) +, authority_(authority) +, path_(path) +, query_(query) +, fragment_(fragment) +{ + tstringstream tss; + + if (!scheme_.empty()) { + tss << scheme_ << ":"; + } + if (!authority_.empty()) { + tss << "//" << authority_; + } + if (!path_.empty()) { + tss << path_; + } + if (!query_.empty()) { + tss << "?" << query_; + } + if (!fragment_.empty()) { + tss << "#" << fragment_; + } + str_ = tss.str(); +} + +url resolve(const url& b, const url& r) +{ + // The resolution algorithm roughly follows the resolution algorithm + // outlined in Section 5.2 (in particular Section 5.2.2) of RFC 3986. The + // major difference between the resolution algorithm and resolve() is that + // resolve() does not attempt to normalize the path components. + + if (r.has_scheme()) { + return r; + } else if (r.has_authority()) { + return url(b.scheme(), r.authority(), r.path(), r.query(), r.fragment()); + } else if (r.has_path()) { + + // The relative URL path is either an absolute path or a relative + // path. If it is an absolute path, build the URL using only the + // relative path. If it is a relative path, resolve the relative path + // against the base path and build the URL using the resolved path. + + if (is_url_path_absolute(r.path())) { + return url(b.scheme(), b.authority(), r.path(), r.query(), r.fragment()); + } else { + tstring path = url_path_resolve(b.path(), r.path()); + return url(b.scheme(), b.authority(), path, r.query(), r.fragment()); + } + + } else if (r.has_query()) { + return url(b.scheme(), b.authority(), b.path(), r.query(), r.fragment()); + } else { + // The resolved URL never includes the base URL fragment (i.e., it + // always includes the reference URL fragment). + return url(b.scheme(), b.authority(), b.path(), b.query(), r.fragment()); + } +} + +} // namespace litehtml diff --git a/gb.form.htmlview/src/litehtml/url.h b/gb.form.htmlview/src/litehtml/url.h new file mode 100644 index 00000000..57a61670 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/url.h @@ -0,0 +1,139 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_URL_H__ +#define LITEHTML_URL_H__ + +#include <ostream> + +#include "litehtml/os_types.h" + +// https://datatracker.ietf.org/doc/html/rfc3986 + +namespace litehtml { + +class url { +public: + url() = default; + + explicit url(const tstring& str); + + url(const tstring& scheme, + const tstring& authority, + const tstring& path, + const tstring& query, + const tstring& fragment); + + const tstring& string() const + { + return str_; + } + + const tstring& scheme() const + { + return scheme_; + } + + bool has_scheme() const + { + return !scheme_.empty(); + } + + const tstring& authority() const + { + return authority_; + } + + bool has_authority() const + { + return !authority_.empty(); + } + + const tstring& path() const + { + return path_; + } + + bool has_path() const + { + return !path_.empty(); + } + + const tstring& query() const + { + return query_; + } + + bool has_query() const + { + return !query_.empty(); + } + + const tstring& fragment() const + { + return fragment_; + } + + bool has_fragment() const + { + return !fragment_.empty(); + } + +protected: + tstring str_; + + // Assume URLs are relative by default. See RFC 3986 Section 4.3 for + // information on which URLs are considered relative and which URLs are + // considered absolute: + // + // https://datatracker.ietf.org/doc/html/rfc3986#section-4.3 + + bool absolute_ = false; + + tstring scheme_; + + tstring authority_; + + tstring path_; + + tstring query_; + + tstring fragment_; +}; + +// Returns a URL that is resolved from the reference URL that might be +// relative to the base URL. For example, given <https://www.twitter.com/> as +// the base URL and </foo> as the relative URL, resolve() will return the URL +// <https://www.twitter.com/foo>. + +url resolve(const url& base, const url& reference); + +} // namespace litehtml + +#endif // LITEHTML_URL_H__ diff --git a/gb.form.htmlview/src/litehtml/url_path.cpp b/gb.form.htmlview/src/litehtml/url_path.cpp new file mode 100644 index 00000000..03e64842 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/url_path.cpp @@ -0,0 +1,86 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "litehtml/url_path.h" + +namespace litehtml { + +bool is_url_path_absolute(const tstring& path) +{ + return path.length() > 0 && path[0] == _t('/'); +} + +tstring url_path_directory_name(const tstring& path) +{ + size_t offset = path.find_last_of(_t('/')); + if (offset == tstring::npos) { + return _t("."); + } else { + return path.substr(0, offset + 1); + } +} + +tstring url_path_base_name(const tstring& path) +{ + size_t offset = path.find_last_of(_t('/')); + if (offset == tstring::npos) { + return path; + } else { + return path.substr(offset + 1); + } +} + +tstring url_path_append(const tstring& base, const tstring& path) +{ + tstring result(base); + + // Only append a separator if both base and path are not empty and if the + // last character of base is not already a separator. + if (!result.empty() && !path.empty() && result.back() != _t('/')) { + result.append(1, _t('/')); + } + + result.append(path); + + return result; +} + +tstring url_path_resolve(const tstring& base, const tstring& path) +{ + + // If the possibly relative path is an absolute path then it is not + // relative and the base path is irrelevant. + if (is_url_path_absolute(path)) { + return path; + } + + return url_path_append(url_path_directory_name(base), path); +} + +} // namespace litehtml diff --git a/gb.form.htmlview/src/litehtml/url_path.h b/gb.form.htmlview/src/litehtml/url_path.h new file mode 100644 index 00000000..cea1f01a --- /dev/null +++ b/gb.form.htmlview/src/litehtml/url_path.h @@ -0,0 +1,51 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_URL_PATH_H__ +#define LITEHTML_URL_PATH_H__ + +#include <ostream> + +#include "litehtml/os_types.h" + +namespace litehtml { + +bool is_url_path_absolute(const tstring& path); + +tstring url_path_directory_name(const tstring& path); + +tstring url_path_base_name(const tstring& path); + +tstring url_path_append(const tstring& base, const tstring& path); + +tstring url_path_resolve(const tstring& base, const tstring& path); + +} // namespace litehtml + +#endif // LITEHTML_URL_PATH_H__ diff --git a/gb.form.htmlview/src/litehtml/utf8_strings.cpp b/gb.form.htmlview/src/litehtml/utf8_strings.cpp new file mode 100644 index 00000000..3cebc73d --- /dev/null +++ b/gb.form.htmlview/src/litehtml/utf8_strings.cpp @@ -0,0 +1,97 @@ +#include "html.h" +#include "utf8_strings.h" + + +litehtml::utf8_to_wchar::utf8_to_wchar(const char* val) +{ + m_utf8 = (const byte*) val; + while (true) + { + ucode_t wch = get_char(); + if (!wch) break; + m_str += wch; + } +} + +litehtml::ucode_t litehtml::utf8_to_wchar::get_char() +{ + ucode_t b1 = getb(); + + if (!b1) + { + return 0; + } + + // Determine whether we are dealing + // with a one-, two-, three-, or four- + // byte sequence. + if ((b1 & 0x80) == 0) + { + // 1-byte sequence: 000000000xxxxxxx = 0xxxxxxx + return b1; + } + else if ((b1 & 0xe0) == 0xc0) + { + // 2-byte sequence: 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx + ucode_t r = (b1 & 0x1f) << 6; + r |= get_next_utf8(getb()); + return r; + } + else if ((b1 & 0xf0) == 0xe0) + { + // 3-byte sequence: zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx + ucode_t r = (b1 & 0x0f) << 12; + r |= get_next_utf8(getb()) << 6; + r |= get_next_utf8(getb()); + return r; + } + else if ((b1 & 0xf8) == 0xf0) + { + // 4-byte sequence: 11101110wwwwzzzzyy + 110111yyyyxxxxxx + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + // (uuuuu = wwww + 1) + int b2 = get_next_utf8(getb()); + int b3 = get_next_utf8(getb()); + int b4 = get_next_utf8(getb()); + return ((b1 & 7) << 18) | ((b2 & 0x3f) << 12) | + ((b3 & 0x3f) << 6) | (b4 & 0x3f); + } + + //bad start for UTF-8 multi-byte sequence + return '?'; +} + +litehtml::wchar_to_utf8::wchar_to_utf8(const std::wstring& val) +{ + unsigned int code; + for (int i = 0; val[i]; i++) + { + code = val[i]; + if (code <= 0x7F) + { + m_str += (char)code; + } + else if (code <= 0x7FF) + { + m_str += (code >> 6) + 192; + m_str += (code & 63) + 128; + } + else if (0xd800 <= code && code <= 0xdfff) + { + //invalid block of utf8 + } + else if (code <= 0xFFFF) + { + m_str += (code >> 12) + 224; + m_str += ((code >> 6) & 63) + 128; + m_str += (code & 63) + 128; + } + else if (code <= 0x10FFFF) + { + m_str += (code >> 18) + 240; + m_str += ((code >> 12) & 63) + 128; + m_str += ((code >> 6) & 63) + 128; + m_str += (code & 63) + 128; + } + } +} diff --git a/gb.form.htmlview/src/litehtml/utf8_strings.h b/gb.form.htmlview/src/litehtml/utf8_strings.h new file mode 100644 index 00000000..35d474fd --- /dev/null +++ b/gb.form.htmlview/src/litehtml/utf8_strings.h @@ -0,0 +1,59 @@ +#ifndef LH_UTF8_STRINGS_H +#define LH_UTF8_STRINGS_H + +#include "os_types.h" +#include "types.h" + +namespace litehtml +{ + class utf8_to_wchar + { + const byte* m_utf8; + std::wstring m_str; + public: + utf8_to_wchar(const char* val); + operator const wchar_t*() const + { + return m_str.c_str(); + } + private: + ucode_t getb() + { + if (!(*m_utf8)) return 0; + return *m_utf8++; + } + ucode_t get_next_utf8(ucode_t val) + { + return (val & 0x3f); + } + ucode_t get_char(); + }; + + class wchar_to_utf8 + { + std::string m_str; + public: + wchar_to_utf8(const std::wstring& val); + operator const char*() const + { + return m_str.c_str(); + } + + const char* c_str() const + { + return m_str.c_str(); + } + }; + +#ifdef LITEHTML_UTF8 +#define litehtml_from_utf8(str) str +#define litehtml_to_utf8(str) str +#define litehtml_from_wchar(str) litehtml::wchar_to_utf8(str) +#else +#define litehtml_from_utf8(str) litehtml::utf8_to_wchar(str) +#define litehtml_from_wchar(str) str +#define litehtml_to_utf8(str) litehtml::wchar_to_utf8(str) +#endif +} + +#endif // LH_UTF8_STRINGS_H diff --git a/gb.form.htmlview/src/litehtml/web_color.cpp b/gb.form.htmlview/src/litehtml/web_color.cpp new file mode 100644 index 00000000..b0629f99 --- /dev/null +++ b/gb.form.htmlview/src/litehtml/web_color.cpp @@ -0,0 +1,256 @@ +#include "html.h" +#include "web_color.h" +#include <cstring> + +litehtml::def_color litehtml::g_def_colors[] = +{ + {_t("transparent"),_t("rgba(0, 0, 0, 0)")}, + {_t("AliceBlue"),_t("#F0F8FF")}, + {_t("AntiqueWhite"),_t("#FAEBD7")}, + {_t("Aqua"),_t("#00FFFF")}, + {_t("Aquamarine"),_t("#7FFFD4")}, + {_t("Azure"),_t("#F0FFFF")}, + {_t("Beige"),_t("#F5F5DC")}, + {_t("Bisque"),_t("#FFE4C4")}, + {_t("Black"),_t("#000000")}, + {_t("BlanchedAlmond"),_t("#FFEBCD")}, + {_t("Blue"),_t("#0000FF")}, + {_t("BlueViolet"),_t("#8A2BE2")}, + {_t("Brown"),_t("#A52A2A")}, + {_t("BurlyWood"),_t("#DEB887")}, + {_t("CadetBlue"),_t("#5F9EA0")}, + {_t("Chartreuse"),_t("#7FFF00")}, + {_t("Chocolate"),_t("#D2691E")}, + {_t("Coral"),_t("#FF7F50")}, + {_t("CornflowerBlue"),_t("#6495ED")}, + {_t("Cornsilk"),_t("#FFF8DC")}, + {_t("Crimson"),_t("#DC143C")}, + {_t("Cyan"),_t("#00FFFF")}, + {_t("DarkBlue"),_t("#00008B")}, + {_t("DarkCyan"),_t("#008B8B")}, + {_t("DarkGoldenRod"),_t("#B8860B")}, + {_t("DarkGray"),_t("#A9A9A9")}, + {_t("DarkGrey"),_t("#A9A9A9")}, + {_t("DarkGreen"),_t("#006400")}, + {_t("DarkKhaki"),_t("#BDB76B")}, + {_t("DarkMagenta"),_t("#8B008B")}, + {_t("DarkOliveGreen"),_t("#556B2F")}, + {_t("Darkorange"),_t("#FF8C00")}, + {_t("DarkOrchid"),_t("#9932CC")}, + {_t("DarkRed"),_t("#8B0000")}, + {_t("DarkSalmon"),_t("#E9967A")}, + {_t("DarkSeaGreen"),_t("#8FBC8F")}, + {_t("DarkSlateBlue"),_t("#483D8B")}, + {_t("DarkSlateGray"),_t("#2F4F4F")}, + {_t("DarkSlateGrey"),_t("#2F4F4F")}, + {_t("DarkTurquoise"),_t("#00CED1")}, + {_t("DarkViolet"),_t("#9400D3")}, + {_t("DeepPink"),_t("#FF1493")}, + {_t("DeepSkyBlue"),_t("#00BFFF")}, + {_t("DimGray"),_t("#696969")}, + {_t("DimGrey"),_t("#696969")}, + {_t("DodgerBlue"),_t("#1E90FF")}, + {_t("FireBrick"),_t("#B22222")}, + {_t("FloralWhite"),_t("#FFFAF0")}, + {_t("ForestGreen"),_t("#228B22")}, + {_t("Fuchsia"),_t("#FF00FF")}, + {_t("Gainsboro"),_t("#DCDCDC")}, + {_t("GhostWhite"),_t("#F8F8FF")}, + {_t("Gold"),_t("#FFD700")}, + {_t("GoldenRod"),_t("#DAA520")}, + {_t("Gray"),_t("#808080")}, + {_t("Grey"),_t("#808080")}, + {_t("Green"),_t("#008000")}, + {_t("GreenYellow"),_t("#ADFF2F")}, + {_t("HoneyDew"),_t("#F0FFF0")}, + {_t("HotPink"),_t("#FF69B4")}, + {_t("Ivory"),_t("#FFFFF0")}, + {_t("Khaki"),_t("#F0E68C")}, + {_t("Lavender"),_t("#E6E6FA")}, + {_t("LavenderBlush"),_t("#FFF0F5")}, + {_t("LawnGreen"),_t("#7CFC00")}, + {_t("LemonChiffon"),_t("#FFFACD")}, + {_t("LightBlue"),_t("#ADD8E6")}, + {_t("LightCoral"),_t("#F08080")}, + {_t("LightCyan"),_t("#E0FFFF")}, + {_t("LightGoldenRodYellow"),_t("#FAFAD2")}, + {_t("LightGray"),_t("#D3D3D3")}, + {_t("LightGrey"),_t("#D3D3D3")}, + {_t("LightGreen"),_t("#90EE90")}, + {_t("LightPink"),_t("#FFB6C1")}, + {_t("LightSalmon"),_t("#FFA07A")}, + {_t("LightSeaGreen"),_t("#20B2AA")}, + {_t("LightSkyBlue"),_t("#87CEFA")}, + {_t("LightSlateGray"),_t("#778899")}, + {_t("LightSlateGrey"),_t("#778899")}, + {_t("LightSteelBlue"),_t("#B0C4DE")}, + {_t("LightYellow"),_t("#FFFFE0")}, + {_t("Lime"),_t("#00FF00")}, + {_t("LimeGreen"),_t("#32CD32")}, + {_t("Linen"),_t("#FAF0E6")}, + {_t("Magenta"),_t("#FF00FF")}, + {_t("Maroon"),_t("#800000")}, + {_t("MediumAquaMarine"),_t("#66CDAA")}, + {_t("MediumBlue"),_t("#0000CD")}, + {_t("MediumOrchid"),_t("#BA55D3")}, + {_t("MediumPurple"),_t("#9370D8")}, + {_t("MediumSeaGreen"),_t("#3CB371")}, + {_t("MediumSlateBlue"),_t("#7B68EE")}, + {_t("MediumSpringGreen"),_t("#00FA9A")}, + {_t("MediumTurquoise"),_t("#48D1CC")}, + {_t("MediumVioletRed"),_t("#C71585")}, + {_t("MidnightBlue"),_t("#191970")}, + {_t("MintCream"),_t("#F5FFFA")}, + {_t("MistyRose"),_t("#FFE4E1")}, + {_t("Moccasin"),_t("#FFE4B5")}, + {_t("NavajoWhite"),_t("#FFDEAD")}, + {_t("Navy"),_t("#000080")}, + {_t("OldLace"),_t("#FDF5E6")}, + {_t("Olive"),_t("#808000")}, + {_t("OliveDrab"),_t("#6B8E23")}, + {_t("Orange"),_t("#FFA500")}, + {_t("OrangeRed"),_t("#FF4500")}, + {_t("Orchid"),_t("#DA70D6")}, + {_t("PaleGoldenRod"),_t("#EEE8AA")}, + {_t("PaleGreen"),_t("#98FB98")}, + {_t("PaleTurquoise"),_t("#AFEEEE")}, + {_t("PaleVioletRed"),_t("#D87093")}, + {_t("PapayaWhip"),_t("#FFEFD5")}, + {_t("PeachPuff"),_t("#FFDAB9")}, + {_t("Peru"),_t("#CD853F")}, + {_t("Pink"),_t("#FFC0CB")}, + {_t("Plum"),_t("#DDA0DD")}, + {_t("PowderBlue"),_t("#B0E0E6")}, + {_t("Purple"),_t("#800080")}, + {_t("Red"),_t("#FF0000")}, + {_t("RosyBrown"),_t("#BC8F8F")}, + {_t("RoyalBlue"),_t("#4169E1")}, + {_t("SaddleBrown"),_t("#8B4513")}, + {_t("Salmon"),_t("#FA8072")}, + {_t("SandyBrown"),_t("#F4A460")}, + {_t("SeaGreen"),_t("#2E8B57")}, + {_t("SeaShell"),_t("#FFF5EE")}, + {_t("Sienna"),_t("#A0522D")}, + {_t("Silver"),_t("#C0C0C0")}, + {_t("SkyBlue"),_t("#87CEEB")}, + {_t("SlateBlue"),_t("#6A5ACD")}, + {_t("SlateGray"),_t("#708090")}, + {_t("SlateGrey"),_t("#708090")}, + {_t("Snow"),_t("#FFFAFA")}, + {_t("SpringGreen"),_t("#00FF7F")}, + {_t("SteelBlue"),_t("#4682B4")}, + {_t("Tan"),_t("#D2B48C")}, + {_t("Teal"),_t("#008080")}, + {_t("Thistle"),_t("#D8BFD8")}, + {_t("Tomato"),_t("#FF6347")}, + {_t("Turquoise"),_t("#40E0D0")}, + {_t("Violet"),_t("#EE82EE")}, + {_t("Wheat"),_t("#F5DEB3")}, + {_t("White"),_t("#FFFFFF")}, + {_t("WhiteSmoke"),_t("#F5F5F5")}, + {_t("Yellow"),_t("#FFFF00")}, + {_t("YellowGreen"),_t("#9ACD32")}, + {nullptr,nullptr} +}; + + +litehtml::web_color litehtml::web_color::from_string(const tchar_t* str, litehtml::document_container* callback) +{ + if(!str || !str[0]) + { + return web_color(0, 0, 0); + } + if(str[0] == _t('#')) + { + tstring red; + tstring green; + tstring blue; + if(t_strlen(str + 1) == 3) + { + red += str[1]; + red += str[1]; + green += str[2]; + green += str[2]; + blue += str[3]; + blue += str[3]; + } else if(t_strlen(str + 1) == 6) + { + red += str[1]; + red += str[2]; + green += str[3]; + green += str[4]; + blue += str[5]; + blue += str[6]; + } + tchar_t* sss = nullptr; + web_color clr; + clr.red = (byte) t_strtol(red.c_str(), &sss, 16); + clr.green = (byte) t_strtol(green.c_str(), &sss, 16); + clr.blue = (byte) t_strtol(blue.c_str(), &sss, 16); + return clr; + } else if(!t_strncmp(str, _t("rgb"), 3)) + { + tstring s = str; + + tstring::size_type pos = s.find_first_of(_t('(')); + if(pos != tstring::npos) + { + s.erase(s.begin(), s.begin() + pos + 1); + } + pos = s.find_last_of(_t(')')); + if(pos != tstring::npos) + { + s.erase(s.begin() + pos, s.end()); + } + + std::vector<tstring> tokens; + split_string(s, tokens, _t(", \t")); + + web_color clr; + + if(tokens.size() >= 1) clr.red = (byte) t_atoi(tokens[0].c_str()); + if(tokens.size() >= 2) clr.green = (byte) t_atoi(tokens[1].c_str()); + if(tokens.size() >= 3) clr.blue = (byte) t_atoi(tokens[2].c_str()); + if(tokens.size() >= 4) clr.alpha = (byte) (t_strtod(tokens[3].c_str(), nullptr) * 255.0); + + return clr; + } else + { + tstring rgb = resolve_name(str, callback); + if(!rgb.empty()) + { + return from_string(rgb.c_str(), callback); + } + } + return web_color(0, 0, 0); +} + +litehtml::tstring litehtml::web_color::resolve_name(const tchar_t* name, litehtml::document_container* callback) +{ + for(int i=0; g_def_colors[i].name; i++) + { + if(!t_strcasecmp(name, g_def_colors[i].name)) + { + return litehtml::tstring(g_def_colors[i].rgb); + } + } + if (callback) + { + litehtml::tstring clr = callback->resolve_color(name); + return clr; + } + return litehtml::tstring(); +} + +bool litehtml::web_color::is_color(const tchar_t* str) +{ + if(!t_strncasecmp(str, _t("rgb"), 3) || str[0] == _t('#')) + { + return true; + } + if (!t_isdigit(str[0]) && str[0] != _t('.')) + { + return true; + } + return false; +} diff --git a/gb.form.htmlview/src/litehtml/web_color.h b/gb.form.htmlview/src/litehtml/web_color.h new file mode 100644 index 00000000..fa3b370b --- /dev/null +++ b/gb.form.htmlview/src/litehtml/web_color.h @@ -0,0 +1,61 @@ +#ifndef LH_WEB_COLOR_H +#define LH_WEB_COLOR_H + +namespace litehtml +{ + struct def_color + { + const tchar_t* name; + const tchar_t* rgb; + }; + + extern def_color g_def_colors[]; + + class document_container; + + struct web_color + { + byte blue; + byte green; + byte red; + byte alpha; + + web_color(byte r, byte g, byte b, byte a = 255) + { + blue = b; + green = g; + red = r; + alpha = a; + } + + web_color() + { + blue = 0; + green = 0; + red = 0; + alpha = 0xFF; + } + + web_color(const web_color& val) + { + blue = val.blue; + green = val.green; + red = val.red; + alpha = val.alpha; + } + + web_color& operator=(const web_color& val) + { + blue = val.blue; + green = val.green; + red = val.red; + alpha = val.alpha; + return *this; + } + static web_color from_string(const tchar_t* str, litehtml::document_container* callback); + static litehtml::tstring resolve_name(const tchar_t* name, litehtml::document_container* callback); + static bool is_color(const tchar_t* str); + }; +} + +#endif // LH_WEB_COLOR_H diff --git a/gb.form.htmlview/src/main.cpp b/gb.form.htmlview/src/main.cpp new file mode 100644 index 00000000..128bb7a7 --- /dev/null +++ b/gb.form.htmlview/src/main.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + + main.cpp + + gb.form.htmlview component + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "c_htmldocument.h" +#include "main.h" + +extern "C" { + +const GB_INTERFACE *GB_PTR EXPORT; +DRAW_INTERFACE DRAW; +GEOM_INTERFACE GEOM; +IMAGE_INTERFACE IMAGE; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + HtmlDocumentDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + //GB.Component.Load("gb.draw"); + GB.GetInterface("gb.draw", DRAW_INTERFACE_VERSION, &DRAW); + //GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + //GB.Component.Load("gb.image"); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/gb.form.htmlview/src/main.h b/gb.form.htmlview/src/main.h new file mode 100644 index 00000000..94c4a126 --- /dev/null +++ b/gb.form.htmlview/src/main.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + main.h + + gb.form.htmlview component + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.draw.h" +#include "gb.geom.h" +#include "gb.image.h" +#include "gb.paint.h" + +#ifndef __MAIN_CPP +extern "C" { +extern const GB_INTERFACE *GB_PTR; +extern DRAW_INTERFACE DRAW; +extern GEOM_INTERFACE GEOM; +extern IMAGE_INTERFACE IMAGE; +} +#endif + +#define GB (*GB_PTR) + +#endif /* __MAIN_H */ diff --git a/gb.gmp/AUTHORS b/gb.gmp/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/COPYING b/gb.gmp/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gmp/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gmp/ChangeLog b/gb.gmp/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/INSTALL b/gb.gmp/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gmp/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gmp/Makefile.am b/gb.gmp/Makefile.am new file mode 100644 index 00000000..28e8c41a --- /dev/null +++ b/gb.gmp/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GMP_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.gmp/NEWS b/gb.gmp/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/README b/gb.gmp/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/acinclude.m4 b/gb.gmp/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gmp/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gmp/component.am b/gb.gmp/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gmp/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gmp/configure.ac b/gb.gmp/configure.ac new file mode 100644 index 00000000..96203084 --- /dev/null +++ b/gb.gmp/configure.ac @@ -0,0 +1,21 @@ +dnl ---- configure.ac for gb.gmp + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-gmp],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gmp) +LT_INIT + +GB_COMPONENT( + gmp, + GMP, + gb.gmp, + [src], + [GB_FIND(gmp.h, $prefix /usr/local /usr /opt/local, include)], + [GB_FIND(libgmp.$SHLIBEXT, $prefix /usr/local /usr /opt/local, lib)], + [$C_LIB -lgmp], + [ ]) +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT +GB_PRINT_MESSAGES diff --git a/gb.gmp/gambas.h b/gb.gmp/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gmp/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gmp/gb_common.h b/gb.gmp/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gmp/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gmp/m4 b/gb.gmp/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gmp/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gmp/reconf b/gb.gmp/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gmp/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gmp/src/Makefile.am b/gb.gmp/src/Makefile.am new file mode 100644 index 00000000..a25326a6 --- /dev/null +++ b/gb.gmp/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.gmp +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gmp.la + +gb_gmp_la_LIBADD = @GMP_LIB@ +gb_gmp_la_LDFLAGS = -module @LD_FLAGS@ @GMP_LDFLAGS@ +gb_gmp_la_CPPFLAGS = @GMP_INC@ + +gb_gmp_la_SOURCES = \ + main.c main.h \ + c_bigint.c c_bigint.h \ + c_rational.c c_rational.h diff --git a/gb.gmp/src/c_bigint.c b/gb.gmp/src/c_bigint.c new file mode 100644 index 00000000..6c08ecfe --- /dev/null +++ b/gb.gmp/src/c_bigint.c @@ -0,0 +1,682 @@ +/*************************************************************************** + + c_bigint.c + + gb.gmp component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_BIGINT_C + +#include "main.h" +#include "c_bigint.h" + +#define THIS ((CBIGINT *)_object) +#define NUMBER (THIS->n) + +//---- BigInt number creation ---------------------------------------------- + +CBIGINT *BIGINT_create(mpz_t number) +{ + CBIGINT *c; + + c = (CBIGINT *)GB.New(CLASS_BigInt, NULL, NULL); + mpz_set(c->n, number); + mpz_clear(number); + + return c; +} + +#define BIGINT_make(_a, _b, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n, (_b)->n); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, (_a)->n, (_b)->n); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_unary(_a, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, (_a)->n); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_int(__a, _f, _op) \ +({ \ + CBIGINT *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (ulong)(_f)); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, _a->n, (ulong)(_f)); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_int_invert(_a, _f, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (ulong)(_f), (_a)->n); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, (ulong)(_f), (_a)->n); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_bit(__a, _n, _op) \ +({ \ + CBIGINT *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (_n)); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, _a->n, (_n)); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +//---- Arithmetic operators ------------------------------------------------- + +static CBIGINT *_addf(CBIGINT *a, double f, bool invert) +{ + if (f < 0) + return BIGINT_make_int(a, (-f), mpz_sub_ui); + else + return BIGINT_make_int(a, f, mpz_add_ui); +} + +static CBIGINT *_add(CBIGINT *a, CBIGINT *b, bool invert) +{ + return BIGINT_make(a, b, mpz_add); +} + +static CBIGINT *_subf(CBIGINT *a, double f, bool invert) +{ + if (invert) + { + if (f < 0) + return BIGINT_make_int(a, (-f), mpz_add_ui); + else + return BIGINT_make_int_invert(a, f, mpz_ui_sub); + } + else + { + if (f < 0) + return BIGINT_make_int(a, (-f), mpz_add_ui); + else + return BIGINT_make_int(a, f, mpz_sub_ui); + } +} + +static CBIGINT *_sub(CBIGINT *a, CBIGINT *b, bool invert) +{ + return BIGINT_make(a, b, mpz_sub); +} + +static CBIGINT *_mulf(CBIGINT *a, double f, bool invert) +{ + return BIGINT_make_int(a, f, mpz_mul_si); +} + +static CBIGINT *_mul(CBIGINT *a, CBIGINT *b, bool invert) +{ + return BIGINT_make(a, b, mpz_mul); +} + +static CBIGINT *_div(CBIGINT *a, CBIGINT *b, bool invert) +{ + if (mpz_cmp_si(b->n, 0) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + else + return BIGINT_make(a, b, mpz_tdiv_q); +} + +static CBIGINT *_divf(CBIGINT *a, double f, bool invert) +{ + if (invert) + { + CBIGINT *b; + mpz_t n; + + mpz_init_set_d(n, f); + b = BIGINT_create(n); + + return _div(b, a, FALSE); + } + else + { + if (f > 0) + { + return BIGINT_make_int(a, f, mpz_tdiv_q_ui); + } + else if (f < 0) + { + a = BIGINT_make_int(a, (-f), mpz_tdiv_q_ui); + mpz_neg(a->n, a->n); + return a; + } + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + } +} + +static int _equal(CBIGINT *a, CBIGINT *b, bool invert) +{ + return mpz_cmp(a->n, b->n) == 0; +} + +static int _equalf(CBIGINT *a, double f, bool invert) +{ + return mpz_cmp_d(a->n, f) == 0; +} + +static int _comp(CBIGINT *a, CBIGINT *b, bool invert) +{ + return mpz_cmp(a->n, b->n); +} + +static int _compf(CBIGINT *a, double f, bool invert) +{ + return mpz_cmp_d(a->n, f); +} + +static CBIGINT *_neg(CBIGINT *a) +{ + return BIGINT_make_unary(a, mpz_neg); +} + +static CBIGINT *_abs(CBIGINT *a) +{ + return BIGINT_make_unary(a, mpz_abs); +} + +static int _sgn(CBIGINT *a) +{ + return mpz_sgn(a->n); +} + +static CBIGINT *_pow(CBIGINT *a, CBIGINT *b, bool invert) +{ + if (!mpz_fits_slong_p(b->n)) + { + GB.Error(GB_ERR_OVERFLOW); + return NULL; + } + + return BIGINT_make_int(a, mpz_get_si(b->n), mpz_pow_ui); +} + +static CBIGINT *_powf(CBIGINT *a, double f, bool invert) +{ + if (invert) + { + mpz_t b; + + if (!mpz_fits_slong_p(a->n)) + return NULL; + + mpz_init_set_si(b, (long)f); + mpz_pow_ui(b, b, mpz_get_si(a->n)); + + return BIGINT_create(b); + } + else + return BIGINT_make_int(a, f, mpz_pow_ui); +} + + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .comp = (void *)_comp, + .compf = (void *)_compf, + .add = (void *)_add, + .addf = (void *)_addf, + .sub = (void *)_sub, + .subf = (void *)_subf, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .div = (void *)_div, + .divf = (void *)_divf, + .pow = (void *)_pow, + .powf = (void *)_powf, + .abs = (void *)_abs, + .neg = (void *)_neg, + .sgn = (void *)_sgn +}; + +//---- Conversions ---------------------------------------------------------- + +char *BIGINT_to_string(mpz_t n, int base) +{ + char *str; + int len; + + //if (mpz_cmp_si(n, 0) == 0) + // return GB.NewZeroString("0"); + + len = mpz_sizeinbase (n, base); + if (mpz_sgn(n) < 0) + len++; + + str = GB.NewString(NULL, len); + mpz_get_str(str, -base, n); + + if (str[len - 1] == 0) + str = GB.ExtendString(str, len - 1); + + return str; +} + +CBIGINT *BIGINT_from_string(char *str, int base) +{ + mpz_t n; + + if (mpz_init_set_str(n, str, base)) + return NULL; + else + return BIGINT_create(n); +} + +static bool _convert(CBIGINT *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = mpz_get_d(a->n); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = mpz_get_d(a->n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = (int)mpz_get_si(a->n); + return FALSE; + + case GB_T_LONG: + conv->_long.value = (int64_t)mpz_get_si(a->n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = BIGINT_to_string(a->n, 10); //, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + mpz_t n; + + switch(type) + { + case GB_T_FLOAT: + mpz_init_set_d(n, conv->_float.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_SINGLE: + mpz_init_set_d(n, conv->_single.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + mpz_init_set_si(n, (long)conv->_integer.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_LONG: + mpz_init_set_si(n, (long)conv->_long.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_object.value = BIGINT_from_string(GB.ToZeroString(&conv->_string), 10); + return conv->_object.value == NULL; + + default: + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(BigInt_new) + + mpz_init_set_si(THIS->n, 0); + +END_METHOD + +BEGIN_METHOD_VOID(BigInt_free) + + mpz_clear(THIS->n); + +END_METHOD + +BEGIN_METHOD(BigInt_compare, GB_OBJECT other) + + CBIGINT *other = VARG(other); + + if (GB.CheckObject(other)) + return; + + GB.ReturnInteger(mpz_cmp(NUMBER, other->n)); + +END_METHOD + +BEGIN_METHOD(BigInt_ToString, GB_INTEGER base) + + char *str; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + str = BIGINT_to_string(NUMBER, base); + GB.FreeStringLater(str); + GB.ReturnString(str); + +END_METHOD + +BEGIN_METHOD(BigInt_Shl, GB_INTEGER bits) + + GB.ReturnObject(BIGINT_make_int(THIS, VARG(bits), mpz_mul_2exp)); + +END_METHOD + +BEGIN_METHOD(BigInt_PowM, GB_OBJECT exp; GB_OBJECT mod) + + mpz_t n; + CBIGINT *exp = VARG(exp); + CBIGINT *mod = VARG(mod); + + if (GB.CheckObject(exp) || GB.CheckObject(mod)) + return; + + mpz_init(n); + mpz_powm(n, NUMBER, exp->n, mod->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_InvM, GB_OBJECT mod) + + mpz_t n; + CBIGINT *mod = VARG(mod); + + if (GB.CheckObject(mod)) + return; + + mpz_init(n); + mpz_invert(n, NUMBER, mod->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_Fact, GB_INTEGER n) + + mpz_t fact; + int n = VARG(n); + + if (n < 0) + { + GB.Error(GB_ERR_ARG); + return; + } + + mpz_init(fact); + mpz_fac_ui(fact, n); + GB.ReturnObject(BIGINT_create(fact)); + +END_METHOD + +BEGIN_METHOD(BigInt_Fibonacci, GB_INTEGER n) + + mpz_t r; + int n = VARG(n); + + if (n < 0) + { + GB.Error(GB_ERR_ARG); + return; + } + + mpz_init(r); + mpz_fib_ui(r, n); + GB.ReturnObject(BIGINT_create(r)); + +END_METHOD + +BEGIN_METHOD(BigInt_GCD, GB_OBJECT a; GB_OBJECT b) + + mpz_t n; + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + mpz_init(n); + mpz_gcd(n, a->n, b->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_LCM, GB_OBJECT a; GB_OBJECT b) + + mpz_t n; + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + mpz_init(n); + mpz_lcm(n, a->n, b->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_FromString, GB_STRING str; GB_INTEGER base) + + CBIGINT *n; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + n = BIGINT_from_string(GB.ToZeroString(ARG(str)), base); + if (!n) + { + GB.Error(GB_ERR_ARG); + return; + } + + GB.ReturnObject(n); + +END_METHOD + +BEGIN_PROPERTY(BigInt_Odd) + + GB.ReturnBoolean(mpz_odd_p(NUMBER)); + +END_PROPERTY + +BEGIN_PROPERTY(BigInt_Even) + + GB.ReturnBoolean(mpz_even_p(NUMBER)); + +END_PROPERTY + +BEGIN_METHOD(BigInt_And, GB_OBJECT a; GB_OBJECT b) + + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + GB.ReturnObject(BIGINT_make(a, b, mpz_and)); + +END_METHOD + +BEGIN_METHOD(BigInt_Or, GB_OBJECT a; GB_OBJECT b) + + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + GB.ReturnObject(BIGINT_make(a, b, mpz_ior)); + +END_METHOD + +BEGIN_METHOD(BigInt_Xor, GB_OBJECT a; GB_OBJECT b) + + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + GB.ReturnObject(BIGINT_make(a, b, mpz_xor)); + +END_METHOD + +BEGIN_METHOD(BigInt_Not, GB_OBJECT a) + + CBIGINT *a = VARG(a); + + if (GB.CheckObject(a)) + return; + + GB.ReturnObject(BIGINT_make_unary(a, mpz_com)); + +END_METHOD + +#define IMPLEMENT_BIT(_name, _func) \ +BEGIN_METHOD(BigInt_##_name, GB_INTEGER bit) \ + \ + _func(NUMBER, VARG(bit)); \ + RETURN_SELF(); \ + \ +END_METHOD + +IMPLEMENT_BIT(BSet, mpz_setbit) +IMPLEMENT_BIT(BClr, mpz_clrbit) +IMPLEMENT_BIT(BChg, mpz_combit) + +BEGIN_METHOD(BigInt_BTst, GB_INTEGER bit) + + GB.ReturnBoolean(mpz_tstbit(NUMBER, VARG(bit))); + +END_METHOD + +//--------------------------------------------------------------------------- + + +GB_DESC BigIntDesc[] = +{ + GB_DECLARE("BigInt", sizeof(CBIGINT)), + + GB_METHOD("_new", NULL, BigInt_new, NULL), + GB_METHOD("_free", NULL, BigInt_free, NULL), + GB_METHOD("_compare", "i", BigInt_compare, "(Other)BigInt;"), + + GB_STATIC_METHOD("Fact", "BigInt", BigInt_Fact, "(N)i"), + GB_STATIC_METHOD("Fibonacci", "BigInt", BigInt_Fibonacci, "(N)i"), + GB_STATIC_METHOD("GCD", "BigInt", BigInt_GCD, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("LCM", "BigInt", BigInt_LCM, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("FromString", "BigInt", BigInt_FromString, "(String)s[(Base)i]"), + + GB_STATIC_METHOD("And", "BigInt", BigInt_And, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("Or", "BigInt", BigInt_Or, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("Xor", "BigInt", BigInt_Xor, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("Not", "BigInt", BigInt_Not, "(A)BigInt;"), + + GB_METHOD("BSet", "BigInt", BigInt_BSet, "(Bit)i"), + GB_METHOD("BClr", "BigInt", BigInt_BClr, "(Bit)i"), + GB_METHOD("BTst", "b", BigInt_BTst, "(Bit)i"), + GB_METHOD("BChg", "BigInt", BigInt_BChg, "(Bit)i"), + + GB_PROPERTY_READ("Odd", "b", BigInt_Odd), + GB_PROPERTY_READ("Even", "b", BigInt_Even), + + GB_METHOD("ToString", "s", BigInt_ToString, "[(Base)i]"), + GB_METHOD("Shl", "BigInt", BigInt_Shl, "(Bits)i"), + GB_METHOD("PowM", "BigInt", BigInt_PowM, "(Exp)BigInt;(Mod)BigInt;"), + GB_METHOD("InvM", "BigInt", BigInt_InvM, "(Mod)BigInt;"), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/gb.gmp/src/c_bigint.h b/gb.gmp/src/c_bigint.h new file mode 100644 index 00000000..614e3e79 --- /dev/null +++ b/gb.gmp/src/c_bigint.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + c_bigint.h + + gb.gmp component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_BIGINT_H +#define __C_BIGINT_H + +#include <gmp.h> + +#ifndef __C_BIGINT_C +extern GB_DESC BigIntDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + mpz_t n; + } + CBIGINT; + +CBIGINT *BIGINT_create(mpz_t number); + +#endif /* __C_BIGINT_H */ diff --git a/gb.gmp/src/c_rational.c b/gb.gmp/src/c_rational.c new file mode 100644 index 00000000..95a56a6b --- /dev/null +++ b/gb.gmp/src/c_rational.c @@ -0,0 +1,730 @@ +/*************************************************************************** + + c_rational.c + + gb.gmp component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_RATIONAL_C + +#include "main.h" +#include "c_bigint.h" +#include "c_rational.h" + +#define THIS ((CRATIONAL *)_object) +#define NUMBER (THIS->n) + +//---- Utility functions & macros -------------------------------------------- + +static CRATIONAL _tmp; + +static void from_double(mpq_t n, double f, int level) +{ + double fa; + int nfa; + mpq_t ni, nn; + bool neg; + + //fprintf(stderr, "from_double: %.14g\n", f); + + if (level >= 10) + goto __DEFAULT; + + fa = fabs(f); + if (fa >= 1E8 || fa <= 1E-8) + goto __DEFAULT; + + neg = (f < 0); + + nfa = (int)fa; + if (nfa >= 1) + fa -= nfa; + + //fprintf(stderr, "fa = %.14g %.14g\n", fa, (fa*1E8) - (int)(fa*1E8)); + + if (nfa && fa < 1E-8) + { + mpq_set_si(n, 0, 1); + } + else if (((fa*1E8) - (int)(fa*1E8)) < 1E-8) + { + mpq_set_si(n, (int)(fa*1E8), 100000000); + } + else + { + mpq_init(ni); + from_double(ni, 1 / fa, level + 1); + mpq_inv(n, ni); + mpq_clear(ni); + } + + mpq_init(nn); + mpq_set_si(nn, nfa, 1); + mpq_add(n, n, nn); + mpq_clear(nn); + + if (neg) + mpq_neg(n, n); + + mpq_canonicalize(n); + + return; + +__DEFAULT: + + mpq_set_d(n, f); +} + +static void my_mpq_set_d(mpq_t n, double f) +{ + from_double(n, f, 0); +} + +#define mpq_init_set_d(_n, _f) (mpq_init(_n), my_mpq_set_d(_n, _f)) +#define mpq_init_set_si(_n, _i) (mpq_init(_n), mpq_set_si(_n, _i, 1)) + +//---- Rational number creation ---------------------------------------------- + +CRATIONAL *RATIONAL_create(mpq_t number) +{ + CRATIONAL *c; + + c = (CRATIONAL *)GB.New(CLASS_Rational, NULL, NULL); + mpq_set(c->n, number); + //mpq_canonicalize(c->n); + mpq_clear(number); + + return c; +} + +#define RATIONAL_make(__a, _b, _op) \ +({ \ + CRATIONAL *_a = __a; \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n, (_b)->n); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, (_a)->n, (_b)->n); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_unary(_a, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, (_a)->n); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_int(__a, _f, _op) \ +({ \ + CRATIONAL *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (ulong)(_f)); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, _a->n, (ulong)(_f)); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_int_invert(_a, _f, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (ulong)(_f), (_a)->n); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, (ulong)(_f), (_a)->n); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_bit(__a, _n, _op) \ +({ \ + CRATIONAL *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (_n)); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, _a->n, (_n)); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +//---- Arithmetic operators ------------------------------------------------- + +static CRATIONAL *_add(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return RATIONAL_make(a, b, mpq_add); +} + +static CRATIONAL *_addf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return RATIONAL_make(a, &_tmp, mpq_add); +} + +static CRATIONAL *_addo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return RATIONAL_make(a, &_tmp, mpq_add); + } + else + return NULL; +} + +static CRATIONAL *_sub(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return RATIONAL_make(a, b, mpq_sub); +} + +static CRATIONAL *_subf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + + if (invert) + return RATIONAL_make(&_tmp, a, mpq_sub); + else + return RATIONAL_make(a, &_tmp, mpq_sub); +} + +static CRATIONAL *_subo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + if (invert) + return RATIONAL_make(&_tmp, a, mpq_sub); + else + return RATIONAL_make(a, &_tmp, mpq_sub); + } + else + return NULL; +} + +static CRATIONAL *_mul(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return RATIONAL_make(a, b, mpq_mul); +} + +static CRATIONAL *_mulf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return RATIONAL_make(a, &_tmp, mpq_mul); +} + +static CRATIONAL *_mulo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return RATIONAL_make(a, &_tmp, mpq_mul); + } + else + return NULL; +} + +static CRATIONAL *_div(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + if (mpq_cmp_si(b->n, 0, 1) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + else + return RATIONAL_make(a, b, mpq_div); +} + +static CRATIONAL *_divf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + if (invert) + return _div(&_tmp, a, FALSE); + else + return _div(a, &_tmp, FALSE); +} + +static CRATIONAL *_divo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + if (invert) + return _div(&_tmp, a, FALSE); + else + return _div(a, &_tmp, FALSE); + } + else + return NULL; +} + +static int _equal(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return mpq_equal(a->n, b->n); +} + +static int _equalf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return mpq_equal(a->n, _tmp.n); +} + +static int _equalo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return mpq_equal(a->n, _tmp.n); + } + else + return -1; +} + +static int _comp(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return mpq_cmp(a->n, b->n); +} + +static int _compf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return mpq_cmp(a->n, _tmp.n); +} + +static int _compo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return mpq_cmp(a->n, _tmp.n); + } + else + return -2; +} + +static CRATIONAL *_neg(CRATIONAL *a) +{ + return RATIONAL_make_unary(a, mpq_neg); +} + +static CRATIONAL *_abs(CRATIONAL *a) +{ + return RATIONAL_make_unary(a, mpq_abs); +} + +static int _sgn(CRATIONAL *a) +{ + return mpq_sgn(a->n); +} + +static CRATIONAL *_powf(CRATIONAL *a, double f, bool invert) +{ + ulong p; + mpz_t num, den; + mpq_t n; + + if (invert || (double)(int)f != f) + return NULL; + + if (f < 0) + { + f = (-f); + invert = TRUE; + } + + p = (ulong)f; + + mpz_init(num); + mpz_pow_ui(num, mpq_numref(a->n), p); + + mpz_init(den); + mpz_pow_ui(den, mpq_denref(a->n), p); + + mpq_init(n); + if (invert) + mpz_swap(num, den); + + if (mpz_cmp_si(den, 0) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + mpq_set_num(n, num); + mpq_set_den(n, den); + + mpz_clear(num); + mpz_clear(den); + + mpq_canonicalize(n); + + return RATIONAL_create(n); +} + +static CRATIONAL *_powo(CRATIONAL *a, void *o, bool invert) +{ + CBIGINT *b; + + if (invert || !GB.Is(o, CLASS_BigInt)) + return NULL; + + b = (CBIGINT *)o; + + if (!mpz_fits_slong_p(b->n)) + { + GB.Error(GB_ERR_OVERFLOW); + return NULL; + } + + return _powf(a, (double)mpz_get_si(b->n), invert); +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .equalo = (void *)_equalo, + .comp = (void *)_comp, + .compf = (void *)_compf, + .compo = (void *)_compo, + .add = (void *)_add, + .addf = (void *)_addf, + .addo = (void *)_addo, + .sub = (void *)_sub, + .subf = (void *)_subf, + .subo = (void *)_subo, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .mulo = (void *)_mulo, + .div = (void *)_div, + .divf = (void *)_divf, + .divo = (void *)_divo, + .powo = (void *)_powo, + .powf = (void *)_powf, + .abs = (void *)_abs, + .neg = (void *)_neg, + .sgn = (void *)_sgn +}; + +//---- Conversions ---------------------------------------------------------- + +char *RATIONAL_to_string(mpq_t n, int base) +{ + char *str; + int len; + + len = mpz_sizeinbase (mpq_numref(n), base) + mpz_sizeinbase(mpq_denref(n), base) + 2; + if (mpq_sgn(n) < 0) + len++; + + str = GB.NewString(NULL, len); + memset(str, 0, len); + mpq_get_str(str, -base, n); + + while (len > 0 && str[len - 1] == 0) + { + str = GB.ExtendString(str, len - 1); + len--; + } + + return str; +} + +CRATIONAL *RATIONAL_from_string(char *str, int base) +{ + mpq_t n; + + mpq_init(n); + if (mpq_set_str(n, str, base)) + { + mpq_clear(n); + return NULL; + } + else + { + mpq_canonicalize(n); + return RATIONAL_create(n); + } +} + +static bool _convert(CRATIONAL *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = mpq_get_d(a->n); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = mpq_get_d(a->n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = (int)mpq_get_d(a->n); + return FALSE; + + case GB_T_LONG: + conv->_long.value = (int64_t)mpq_get_d(a->n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = RATIONAL_to_string(a->n, 10); //, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + + if (type == CLASS_BigInt) + { + mpz_t n; + + mpz_init(n); + mpz_tdiv_q(n, mpq_numref(a->n), mpq_denref(a->n)); + + conv->_object.value = BIGINT_create(n); + + return FALSE; + } + + return TRUE; + } + } + else + { + mpq_t n; + + switch(type) + { + case GB_T_FLOAT: + mpq_init_set_d(n, conv->_float.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_SINGLE: + mpq_init_set_d(n, conv->_single.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + mpq_init_set_si(n, (long)conv->_integer.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_LONG: + mpq_init_set_si(n, (long)conv->_long.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_object.value = RATIONAL_from_string(GB.ToZeroString(&conv->_string), 10); + return conv->_object.value == NULL; + + default: + + if (type == CLASS_BigInt) + { + mpq_init(n); + mpq_set_z(n, ((CBIGINT *)conv->_object.value)->n); + conv->_object.value = RATIONAL_create(n); + return FALSE; + } + + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Rational_init) + + _tmp.ob.ref = 2; + mpq_init(_tmp.n); + +END_METHOD + +BEGIN_METHOD_VOID(Rational_exit) + + mpq_clear(_tmp.n); + +END_METHOD + +BEGIN_METHOD_VOID(Rational_new) + + mpq_init(THIS->n); + +END_METHOD + +BEGIN_METHOD_VOID(Rational_free) + + mpq_clear(THIS->n); + +END_METHOD + +BEGIN_METHOD(Rational_compare, GB_OBJECT other) + + CRATIONAL *other = VARG(other); + + if (GB.CheckObject(other)) + return; + + GB.ReturnInteger(mpq_cmp(NUMBER, other->n)); + +END_METHOD + +BEGIN_METHOD(Rational_ToString, GB_INTEGER base) + + char *str; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + str = RATIONAL_to_string(NUMBER, base); + GB.FreeStringLater(str); + GB.ReturnString(str); + +END_METHOD + +BEGIN_METHOD(Rational_FromString, GB_STRING str; GB_INTEGER base) + + CRATIONAL *n; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + n = RATIONAL_from_string(GB.ToZeroString(ARG(str)), base); + if (!n) + { + GB.Error(GB_ERR_TYPE); + return; + } + + GB.ReturnObject(n); + +END_METHOD + +BEGIN_PROPERTY(Rational_Num) + + if (READ_PROPERTY) + { + mpz_t n; + mpz_init(n); + mpq_get_num(n, NUMBER); + GB.ReturnObject(BIGINT_create(n)); + } + else + { + CBIGINT *num = VPROP(GB_OBJECT); + + if (GB.CheckObject(num)) + return; + + mpq_set_num(NUMBER, num->n); + mpq_canonicalize(NUMBER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Rational_Den) + + if (READ_PROPERTY) + { + mpz_t n; + mpz_init(n); + mpq_get_den(n, NUMBER); + GB.ReturnObject(BIGINT_create(n)); + } + else + { + CBIGINT *den = VPROP(GB_OBJECT); + + if (GB.CheckObject(den)) + return; + + mpq_set_den(NUMBER, den->n); + mpq_canonicalize(NUMBER); + } + +END_PROPERTY + +//--------------------------------------------------------------------------- + + +GB_DESC RationalDesc[] = +{ + GB_DECLARE("Rational", sizeof(CRATIONAL)), + + GB_STATIC_METHOD("_init", NULL, Rational_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Rational_exit, NULL), + GB_METHOD("_new", NULL, Rational_new, NULL), + GB_METHOD("_free", NULL, Rational_free, NULL), + GB_METHOD("_compare", "i", Rational_compare, "(Other)Rational;"), + + GB_PROPERTY("Num", "BigInt", Rational_Num), + GB_PROPERTY("Den", "BigInt", Rational_Den), + + GB_STATIC_METHOD("FromString", "Rational", Rational_FromString, "(String)s[(Base)i]"), + + GB_METHOD("ToString", "s", Rational_ToString, "[(Base)i]"), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/gb.gmp/src/c_rational.h b/gb.gmp/src/c_rational.h new file mode 100644 index 00000000..9db89a85 --- /dev/null +++ b/gb.gmp/src/c_rational.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + c_rational.h + + gb.gmp component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_RATIONAL_H +#define __C_RATIONAL_H + +#include <gmp.h> + +#ifndef __C_RATIONAL_C +extern GB_DESC RationalDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + mpq_t n; + } + CRATIONAL; + +#endif /* __C_RATIONAL_H */ diff --git a/gb.gmp/src/gb.gmp.component b/gb.gmp/src/gb.gmp.component new file mode 100644 index 00000000..2ed93b42 --- /dev/null +++ b/gb.gmp/src/gb.gmp.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Stable diff --git a/gb.gmp/src/main.c b/gb.gmp/src/main.c new file mode 100644 index 00000000..fc39f493 --- /dev/null +++ b/gb.gmp/src/main.c @@ -0,0 +1,84 @@ +/*************************************************************************** + + main.c + + gb.gmp component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_bigint.h" +#include "c_rational.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + BigIntDesc, + RationalDesc, + NULL // Must have a null entry for the end of the structure +}; + +GB_CLASS CLASS_BigInt; +GB_CLASS CLASS_Rational; + +/*static void error_handler(const char *reason, const char *file, int line, int gsl_errno) +{ + //fprintf(stderr, "gb.gsl: error: %s: %s\n", gsl_strerror(gsl_errno), reason); + GB.Error("&1: &2", gsl_strerror(gsl_errno), reason); +}*/ + +static void *my_malloc(size_t n) +{ + void *p; + GB.Alloc(&p, n); + return p; +} + +static void my_free(void *p, size_t n) +{ + GB.Free(&p); +} + +static void *my_realloc(void *p, size_t old, size_t n) +{ + GB.Realloc(&p, n); + return p; +} + +int EXPORT GB_INIT(void) +{ + CLASS_BigInt = GB.FindClass("BigInt"); + CLASS_Rational = GB.FindClass("Rational"); + + mp_set_memory_functions(my_malloc, my_realloc, my_free); + + //gsl_set_error_handler(error_handler); + + return 0; +} + +void EXPORT GB_EXIT() +{ + +} diff --git a/gb.gmp/src/main.h b/gb.gmp/src/main.h new file mode 100644 index 00000000..532cf0bc --- /dev/null +++ b/gb.gmp/src/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + gb.gmp component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include <gmp.h> + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern GB_CLASS CLASS_BigInt; +extern GB_CLASS CLASS_Rational; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.gsl/AUTHORS b/gb.gsl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/COPYING b/gb.gsl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gsl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gsl/ChangeLog b/gb.gsl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/INSTALL b/gb.gsl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gsl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gsl/Makefile.am b/gb.gsl/Makefile.am new file mode 100644 index 00000000..67e464d3 --- /dev/null +++ b/gb.gsl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GSL_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.gsl/NEWS b/gb.gsl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/README b/gb.gsl/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/Test/test/.directory b/gb.gsl/Test/test/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.gsl/Test/test/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.gsl/Test/test/.icon.png b/gb.gsl/Test/test/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7c7a1e742729196ac371b2e78c72463876bbacd1 GIT binary patch literal 3682 zcmV-o4xRCdP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF000gtNkl<Zc-q~Y zd5~RIoyR|CdGGb>eNXR6cb4w#8w4;wVnU1^Hc=viD5K6e8FfYm!O<zGf}$*(8O==9 zh~+;JKoBL-VJT4)lCaq%A>DNLrPKS8^qzj{e)~OV{^-54n6;baTXm}5eRp~H`?<e! ze&?Kfp5Q7|*4k}YW58I#C#&h+zV4E)Cnws_g(&;J?EoajxtX4yfQ-qw<ZJ4QHg!JA zdtf`p2$T?fb4@NkSX)Tli2y4e_TkE0LZodM0GW6H353Qt5&VZ=#$WZDs~bSU*WSli zum*0wF@q<*T8N`PyjTf^DRCslYC;92MA~+dwRS7kv1BB<2?P|8>}7DZ0+{^Jdsv~c z1{M@0@WlOvOe;v>t;6tMHAJJZvj(o8mCokQVzSq6K`F&|?kV7V_e|znKi$vfmSgAj zTc0WbMgOo9YaFbF>|_r=ytjZgOVZg^2d}&ffza@=p@`vRSmI63WyOLto_c61$2%h| z|KSm8YI{L?t}Xymzr6!%Au-O)Kd#Q<?^kD2*#Xad1YLa)b%u{U(cDSR;aYN%1uy;E zT#EDJ`0<7&HvP2^l!rLsC8E3+58&~QUF)n6k85Rf&-4C+{xB6W%K7fo_hwXHi1BIb zc7l*d0bgF4!NYgvandS&wGSG5`on!tC%gR|*;hw2(92_A%;)~kX7ls^^Yf*jG!Sy4 zpu8Xi%6N##n2QHsg?N0?<nuNU5MwN#*;T9FePUzhs;Aecef+mhDE{YitOaW=%VsC@ zqc7%>l<Z-1103#vh#5gQ5arPRW?Je`u=bWresIqW%BnS`>!G*9A!<E<0U;1ppo|-9 zC9dq-h#Y_C!T~60avj43Yw=`~cGrn5=Yml3FS|gxum)!3$MT~u<T9@)jt^Skll>4L z@x&bms*bdC>~Irv^J97T@p)M7<-4!Ifo3oU;$z$(6j%pPXd}T92n#BC?!^Hx7NU;5 za00-Be4F%1gV^%pjh(BW83SR~qkFN&k{<8kvAeRleQ`3Mbijs#;E(nj8dOWuDfU!0 z5i1>j`n4(CSdzlC+u@Ch(Z{*H9<Ty|##)PZz#0q2h47^R&HEK+`tsj5b=a9%7YASv z;IVeW#YEASkTHbA4sWc#KmE&N_nkcg%*6u$C`!My^Dnd9M_c7xXWDUQ08W4XyOty@ zXY3f982}dN5+mT#rn5C^rvrHMNg0Y5dIF!43VfO54+BqH$&+G~u~7h4JlD>)KW`WH zi9PrO*H#_q=#_BbJq?6MelJ!$z4zI~SbZ-D*ANvYQv}ym6#$4hwtINXMtuJ0Ch=cy zHTTRdh*Kl0ys^Yh^vzxgNkQL;&+KdHbM(ixf!^}^@TmTPONna|K*X`Wk=w>NR!1D+ z_i8oDun-q^;j2Y*%miR4V*FZ38BkKjo$J7BMQZG}lmV@j@++mr?3n0Vy+Rrj*-9C| zmi-YhYKIh;F9R4SN`3W5^wb|>!K!a#oG4OF&^(MBO+ZQ+&@QQhdQ1d}%LKp}hh1ea zVyBldt>h+J>nrJKIn2D9zdAAg?PvflX@Xj58BlTzfXmPaFvel;u8oM2*+i=L(<4*p z%}6B1t58B+^7Su9c1-K8v=h);D8D+b(qDQvxVg51Q>pQYXc*gAM?-;OdhvDGkIRut z5cd1fF>y#ao-juH)I#~SQZhI;0<f2s2czL2Cr;MmDkuPJaoxIxo|a?Wkm{mt%8e+Y zI9C2PMe|n?my|hv{>5kjN;yF-l?<pcjf1^N0DHG?;KZ?V91GD!OGrvc#zexzrlu3D zuHew_1~Lk+W9OScr?%q=g^NCil;hXw5M!!<R5GBI6oIidz+OZX@cMs#h3|dib{_cE z2Dbd}H~i%H+X+u8M#aRD;euWfWyj7p(9L^s<!2F>;zcVtt|@nH2Tw^6&|1lWQls~n z3v2@Xz7Agg<x@PKo5QOQeVr{Yy-2~-5*~i;bx!#F_|r0poxhTd8)66UM52x!#I5zX zcDlw!n^rQQr66!APr>oG!;wF*_3s{G%ff}s?CBw1NEXa4X2F`Zly^l@*1?2>{bYq8 z)X_{~VkX|$_;Cd=)~6H%q*f{*rC?MMXD`-?(%soiOIs~92evUQwVA0aR*(q9WoFXc z+Q!rWy`D%+Jdvn_3G`yZ!J+!+goD_|8nRRK(Ass*-@5E&j8CmpKr3l|N(uqP$^UF^ zKuc9QK34?4332#%6L-zXz+~j{QbjZC4jkv#&p$`SAKyk4%;bgL4b=7okz%;<vDOkk z(a7w)*-XltK7Nn>%n=}zv_7r0Y?A^ZM)ZE{8QOpymG4rJ;G@T#Mqf-iX+ko0Y8HDt zgBT0XzOad}-0^v0mOV(t(MDSKHWD(RW8-o9P9c;g)Y?E!FiP>Fn@|%l;v5OUT7=TF zO}mtBS5l755W<XUEH=OUTkc)GkhhK~x&sbIgz*ZC#1M!?G0`Y$`eOd~$;WVIPeDwb zk0-kj6ATdE@h1{yEJR>QXl>!<(sjrQ8g7n^bZZeVOuLpww@PiK6bw!R3NzyVc5U0l zeYZ|!PtZfBb<x*&fW$dB(dX-?HykB3HW95PLI`qhSk3dVJw>?Ub!vqs-J^Ksru%6M zhskO`&dNK#Ga<%@0Wbu>Sfq^VR&70^RSCpkim(<j_<-uFDw&+G`KY>?{RDBvB@*>@ z;`H_qb;VG2w1Jt^U3lFt`uu%#wKVcSDbx7nfie~e*p!$~o7GIJuVU4Gk4~t004c># zqzhw3#3Nb-!1}GhmSF%^N1{$fG#sL<{U9?I7O`<_8I3)DV$yTaaS3!+yi3xoCHOkq z`BH(0oxyl&%ibY#<y!owe8~4UQ4kJr1j*fZu47U`$wYexz@=525W)d4#`4I@m>dnD zr3eTqy!`;c5rR<2k2lheF-3GH<srHb5^8TI((NXpcrmA{50H>PiEYQ)NDLl9C#2AG z_!C^-SP~}B!ILtHZ_U3Im*@NzDvXDe6hk2(ApisK1(YNpBwheBPz3~0PBhoh?v5eQ z>ms3e5m8?kzN4RDO_a>s0^)kRIpuQGbqeC#ipkzET0v5J4)d<RZKD02CMgkv?YkWX zK#AZW8DS7g3Y5!DLS`<m#6)^`?;sTRBZR<{T}W1-3#m1&5rb2`2QzIUNmGikaUQ(U zUb0n`!w1TkJ$LCudmbe<&=~@-g5U@M_y-f8pr4XkDs1c|@}?D2)CE1k5JE~?H@`~G zb+?k>SYp$2sR{UqnUqT?9HuE0MoGb`#5giyJ2+lbLGIK=6YYB#fE;e%E%=83Xi0z( z1eAe<q(lxL^ilQvFHnhDbT=J`Xc#SF`I6OqP<NQQ8ji;%5w-nYO(iVWS{hFVNX{vs z^5`C-LNTRi4pL3bEYL_vg&67#*%4JhDiIu<GPTwYrHXlTm+&D`)-IjRADhEe?JmO= zGmD0OWps77QEPe#G#*E#W+7cJ2Ct!{pt7lh1tkl4<Mmg$KGH>f<!<i!{DUN>WltOc zDMpS6gD@~hqz6WXO7=GtgP#+Ul9`iT$V)H&7QJUPDe37LA$aqhO*n~Z$i=HzymB?1 zY{goEwRUJi)vXnKnmTaJ`V4&?&3ymSf2Fc!7xgv!7`*;?7b)zJH!Vg)fL6k9h9W?S zk>lpc(`QktC1uU^Ow)cotg0pLrc#VMmYSM+gg2g^hGTg8dWoApAFLe!A#f>)!lAOR zm4mPTg(+EC_}lA<7{knx1>+t8N(wPJ`y~h3fc^-O1cxbs8dLI%N+_MP2zSIwZc;Kb z#*2xBNouR-7O&!t+wNgeVgjK9JF&(f2Il>RauMEN#=PuYmbk%lq6HCA96q?4aG&qA z-?`F7is9(iazqx8N(KhUf{sL-X`U`QEt6%-OUXM_!5<nMacAV=6PC92<9u%A7huM0 ziVl|Z*CYFAQ!#k+r(>~rW-Z}Vu#XfeSvq4DIfXN+uiB5&YFy_7Pm2H{hO2-}5*S>b zCk^UcgS9C$=8#nWA;FA1Y(gqkLUQPZ^?d#I`zV>SnB4qA$}2x&>xXX=TQ~<#S{A;9 z6b@NSbzL3BNB8pC()-5M_@IQeL&-=pDhuEjX6R{1agNu+ZnuZKSKh_*zu$ms)(u2` zC&_Soh(y8&AxKP4WBIb%xo+W7YHAKqzGnvw?X853Atx<^f?G=w6ZZO940)5%`e7h% zy?xX?aw;qy1qg(qsZqzA-%{(VOj^m1*Q2`t65RG@Yi7vx8o*f77l<gVwZb2Cl(B|u zZiS2hDXg+W3L%7zbD2IEr~*A*-PKW7vfCeu%3v_8thHQ=1cVUE!Fpmn+VpgttRCQk z0McLnpz(coeCkxk4lb6u_5c7VVI$$*uIdMG$zKMv4*>v5fnp#QaC7>CimP1?5CQst zBfy^r0RVV_Twpqo4Ws~GuCl+?okNEHwAT$B2aW+vKxo7m5H(=hLw^KF&a#^PDwG|7 z5FRK2Aj%ougI_C#&j-lmGo1gxW<1HM%%7IbYu}$rdrz3Be%HWXDmw8u?WW`Xr-}Bp zUNJX!JoGaTR{7BGc(gkfw>O!RLRh|hF<J>SQoP)K^W-z0ds$X11)9nbY6S-xH2?8> zGn>jf=xT4JtNb~92VT2k#s@VyFQ2;BHnck#PkbgOa*~V`ix3dw)@L;jmoelgt=T|q z))b^x1ez;2@y-*N(5WkKeo#yA--uC3xZPd|Rx!6I%%t4OJn>JZ=REJStygP(`y1nC z_Dsr5kH@1!I8Km_&wrD(uWTnh{nGzjea-9t0Zzk&3RC?negFUf07*qoM6N<$g0~_7 A2mk;8 literal 0 HcmV?d00001 diff --git a/gb.gsl/Test/test/.project b/gb.gsl/Test/test/.project new file mode 100644 index 00000000..fa45a305 --- /dev/null +++ b/gb.gsl/Test/test/.project @@ -0,0 +1,9 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.90 +Title=test +Startup=MMain +Version=3.6.90 +Component=gb.gsl +Component=gb.option +Component=gb.xml +TabSize=2 diff --git a/gb.gsl/Test/test/.src/MMain.module b/gb.gsl/Test/test/.src/MMain.module new file mode 100644 index 00000000..0a0df525 --- /dev/null +++ b/gb.gsl/Test/test/.src/MMain.module @@ -0,0 +1,247 @@ +' Gambas module file + +Private ts As New TestSuite + +'------------------------------------------------------------------ +'Creates a new test object and adds it to the test suite +'------------------------------------------------------------------ +Public Function AddTest(name As String, result As Variant, expected As Variant, Optional note As String) As Test + + Dim t As New Test + + ts.AddTest(t) + t.Run(name, result, expected, note) + +End + + +'----------------------------------------------------------------- +'Test All GSL Class Methods +'----------------------------------------------------------------- +Public Sub testGslMethods() + Dim result As Variant + + AddTest("GSL.Acosh(45)", GSL.Acosh(45), ACosh(45)) + AddTest("GSL.Asinh(45)", GSL.Asinh(45), ASinh(45)) + AddTest("GSL.Atanh(0.45)", GSL.Atanh(0.45), ATanh(0.45)) + AddTest("GSL.Expm1(45)", GSL.Expm1(45), Expm(45)) + AddTest("GSL.Fcmpb(-0.3, 0.2, 0.1)", GSL.Fcmpb(-0.3, 0.2, 0.1), GSL.Fcmpb(-0.3, 0.2, 0.1)) + AddTest("GSL.Fcmpi(-0.3, 0.2, 0.1)", GSL.Fcmpi(1.3, 2.2, -1.1), 1) + result = GSL.Frexp(0.45) + AddTest("GSL.Frexp(0.45)", result, result) + AddTest("GSL.Hypot(10.27, 7.45)", GSL.Hypot(10.27, 7.45), GSL.Hypot(10.27, 7.45)) + AddTest("GSL.Hypot3(10.27, 7.45, 6.78125)", GSL.Hypot3(10.27, 7.45, 6.78125), GSL.Hypot3(10.27, 7.45, 6.78125)) + AddTest("GSL.IntPow(2.0, 8)", GSL.IntPow(2.0, 8), 256.00) + AddTest("GSL.IntPow2(8)", GSL.IntPow2(8), GSL.IntPow2(8)) + AddTest("GSL.IntPow3(8)", GSL.IntPow3(8), GSL.IntPow3(8)) + AddTest("GSL.IntPow4(8)", GSL.IntPow4(8), GSL.IntPow4(8)) + AddTest("GSL.IntPow5(8)", GSL.IntPow5(8), GSL.IntPow5(8)) + AddTest("GSL.IntPow6(8)", GSL.IntPow6(8), GSL.IntPow6(8)) + AddTest("GSL.IntPow7(8)", GSL.IntPow7(8), GSL.IntPow7(8)) + AddTest("GSL.IntPow8(8)", GSL.IntPow8(8), GSL.IntPow8(8)) + AddTest("GSL.IntPow9(8)", GSL.IntPow9(8), GSL.IntPow9(8)) + AddTest("GSL.IsEven(2)", GSL.IsEven(2), True) + AddTest("GSL.IsFinite(25)", GSL.IsFinite(25), True) + AddTest("GSL.IsInf(0)", GSL.IsInf(0), False) + AddTest("GSL.IsNan(0)", GSL.IsNan(0), False) + AddTest("GSL.IsOdd(3)", GSL.IsOdd(3), True) + AddTest("GSL.IsPos(0)", GSL.IsPos(0), True) + AddTest("GSL.Ldexp(17.63, 3)", GSL.Ldexp(17.63, 3), 141.04) + AddTest("GSL.Log1p(12.36)", GSL.Log1p(12.36), GSL.Log1p(12.36)) + AddTest("GSL.MaxFloat(2.0123, 2.01234)", GSL.MaxFloat(2.0123, 2.01234), 2.01234) + AddTest("GSL.MaxInt(1,3)", GSL.MaxInt(1, 3), 3) + AddTest("GSL.MinFLoat(0.001, 0.0011)", GSL.MinFLoat(0.001, 0.0011), 0.001) + AddTest("GSL.MinInt(1,3)", GSL.MinInt(1, 3), 1) + + End + + +'---------------------------------------------------------------- +'Test GSL Class constance +'---------------------------------------------------------------- +Public Sub testGslConstance() + ' Constance + AddTest("GSL.M_1_PI", GSL.M_1_PI, 0.31830988618379067153776752675) + AddTest("GSL.M_2_PI", GSL.M_2_PI, 0.63661977236758134307553505349) + AddTest("GSL.M_2_SQRTPI", GSL.M_2_SQRTPI, 1.12837916709551257389615890312) + AddTest("GSL.M_E", GSL.M_E, 2.71828182845904523536028747135) + AddTest("GSL.M_EULER", GSL.M_EULER, 0.57721566490153286060651209008) + AddTest("GSL.M_LN2", GSL.M_LN2, 0.69314718055994530941723212146) + AddTest("GSL.M_LN10", GSL.M_LN10, 2.30258509299404568401799145468) + AddTest("GSL.M_LNPI", GSL.M_LNPI, 1.14472988584940017414342735135) + AddTest("GSL.M_LOG10E", GSL.M_LOG10E, 0.43429448190325182765112891892) + AddTest("GSL.M_LOGE", GSL.M_LOGE, 1.44269504088896340735992468100) + AddTest("GSL.M_PI", GSL.M_PI, 3.14159265358979323846264338328) + AddTest("GSL.M_PI_2", GSL.M_PI_2, 1.57079632679489661923132169164) + AddTest("GSL.M_PI_4", GSL.M_PI_4, 0.78539816339744830961566084582) + AddTest("GSL.M_SQRT1_2", GSL.M_SQRT1_2, 0.70710678118654752440084436210) + AddTest("GSL.M_SQRT2", GSL.M_SQRT2, 1.41421356237309504880168872421) + AddTest("GSL.M_SQRT3", GSL.M_SQRT3, 1.73205080756887729352744634151) + AddTest("GSL.M_SQRTPI", GSL.M_SQRTPI, 1.77245385090551602729816748334) + +End + + + +'------------------------------------------------------------------ +'Test Complex Class Methods +'------------------------------------------------------------------ +Public Sub AddComplexTest(name As String, result As Variant, expected As Variant, Optional note As String) + + Dim t As New TestComplex + + ts.AddTest(t) + t.Run(name, result, expected, note) + +End + + +'------------------------------------------------------------------- +'Test Complex Class Methods +'------------------------------------------------------------------- +Public Sub testComplexMethods() + Dim c As New Complex + Dim z1 As New Complex + Dim z2 As New Complex + Dim z3 As Complex + + z1.Set(1, 1) + z2.Set(2, 2) + + AddComplexTest("z1.Abs()", z1.Abs(), z1.Abs(), ("z1 = " & z1.ToString())) + AddComplexTest("z1.Abs2()", z1.Abs2(), z1.Abs2(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Add(z2)", z1.Add(z2), z1.Add(z2), "z1 = " & z1.ToString()) + AddComplexTest("z1.AddImag(1)", z1.AddImag(1), z1.AddImag(1), "z1 = " & z1.ToString()) + AddComplexTest("z1.AddReal(1)", z1.AddReal(1), z1.AddReal(1), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccos()", z1.Arccos(), z1.Arccos(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccosh()", z1.Arccosh(), z1.Arccosh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArccoshReal(5.45)", z1.ArccoshReal(5.45), z1.ArccoshReal(5.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArccosReal(3.25)", z1.ArccosReal(3.25), z1.ArccosReal(3.25), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccot()", z1.Arccot(), z1.Arccot(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccoth()", z1.Arccoth(), z1.Arccoth(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccsc()", z1.Arccsc(), z1.Arccsc(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccsch()", z1.Arccsch(), z1.Arccsch(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArccscReal(3.45)", z1.ArccscReal(3.45), z1.ArccscReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsec()", z1.Arcsec(), z1.Arcsec(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsech()", z1.Arcsech(), z1.Arcsech(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArcsecReal(3.45)", z1.ArcsecReal(3.45), z1.ArcsecReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsin()", z1.Arcsin(), z1.Arcsin(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsinh()", z1.Arcsinh(), z1.Arcsinh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArcsinReal(3.45)", z1.ArcsinReal(3.45), z1.ArcsinReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arctan()", z1.Arctan(), z1.Arctan(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arctanh()", z1.Arctanh(), z1.Arctanh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArctanhReal(3.45)", z1.ArctanhReal(3.45), z1.ArctanhReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arg()", z1.Arg(), z1.Arg(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Copy()", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Cos()", z1.Cos(), z1.Cos(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Cosh()", z1.Cosh(), z1.Cosh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Cot()", z1.Cot(), z1.Cot(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Coth()", z1.Coth(), z1.Coth(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Csc()", z1.Csc(), z1.Csc(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Csch()", z1.Csch(), z1.Csch(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Csch()", z1.Csch(), z1.Csch(), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.DivImag(3.45)", z1.DivImag(3.45), z1.DivImag(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.DivReal(3.45)", z1.DivReal(3.45), z1.DivReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Exp()", z1.Exp(), z1.Exp(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Log()", z1.Log(), z1.Log(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Log10()", z1.Log10(), z1.Log10(), "z1 = " & z1.ToString()) + AddComplexTest("z1.LogAbs()", z1.LogAbs(), z1.LogAbs(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Logb(z2)", z1.Logb(z2), z1.Logb(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.Mul(z2)", z1.Mul(z2), z1.Mul(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.MulImag(3.45)", z1.MulImag(3.45), z1.MulImag(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.MulReal(3.45)", z1.MulReal(3.45), z1.MulReal(3.45), "z1 = " & z1.ToString()) + z1.Polar(5, 2) + AddComplexTest("z1.Polar(5,2)", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Pow(z2)", z1.Pow(z2), z1.Pow(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.PowReal(2) ", z1.PowReal(2), z1.PowReal(2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + z1.Rect(1, 1) + AddComplexTest("z1.Rect(1,1)", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sec()", z1.Sec(), z1.Sec(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sech()", z1.Sech(), z1.Sech(), "z1 = " & z1.ToString()) + z1.Set(2, 2) + AddComplexTest("z1.Set(2,2)", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sin()", z1.Sin(), z1.Sin(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sinh()", z1.Sinh(), z1.Sinh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sqrt()", z1.Sqrt(), z1.Sqrt(), "z1 = " & z1.ToString()) + AddComplexTest("z1.SqrtReal(3.45)", z1.SqrtReal(3.45), z1.SqrtReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sub(z2)", z1.Sub(z2), z1.Sub(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.SubImag(1.321)", z1.SubImag(1.321), z1.SubImag(1.321), "z1 = " & z1.ToString()) + AddComplexTest("z1.SubReal(2.125)", z1.SubReal(2.125), z1.SubReal(2.125), "z1 = " & z1.ToString()) + AddComplexTest("z1.Tan()", z1.Tan(), z1.Tan(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Tanh()", z1.Tanh(), z1.Tanh(), "z1 = " & z1.ToString()) + +End + + +'------------------------------------------------------------------- +' Add Polynomial Tests +'------------------------------------------------------------------- +Public Sub AddPolynomialTest(name As String, result As Variant, expected As Variant, Optional note As String) + + Dim t As New TestPolynomial + + ts.AddTest(t) + t.Run(name, result, expected, note) + +End + + +'------------------------------------------------------------------- +' Polynomial Tests +'------------------------------------------------------------------- +Public Sub testPolynomialMethods() + Dim c As New Complex + Dim p1 As New Polynomial + Dim p2 As New Polynomial + Dim p3 As Polynomial + Dim f1 As Float + Dim f2 As Float + + p1.AddCoef(0.565) + p1.AddCoef(1.738) + p1.AddCoef(3.1465) + + AddPolynomialTest("p1.AddCoef(0.565)", p1.AddCoef(0.565), 1, p1.ToString()) + AddPolynomialTest("p1.AddCoef(1.738)", p1.AddCoef(1.738), 2, p1.ToString()) + AddPolynomialTest("p1.AddCoef(3.1465)", p1.AddCoef(3.1465), 3, p1.ToString()) + + +End Sub + + + + + + +'------------------------------------------------------------------- +'Run all tests +'------------------------------------------------------------------- +Public Sub Main() + + ' Setup test suite + ts.Name = "GSL Component" + ts.HeaderChar = "=" + ts.HeaderWidth = 40 + ts.TestHeaderChar = "-" + ts.TestHeaderWidth = 40 + ts.Note = "This test suite was developed for use\n" + "with GSL Component development. However,\n" + "it may be useful in other projects." + ts.ShowTestNotes = True + + 'Run our GSL Class Tests + testGslMethods() + testGslConstance + + 'Run Complex Class Test + testComplexMethods() + + 'Run Polynomial Class Test + testPolynomialMethods() + + + ' Finnish by showing test results + ts.ShowTests() +End + + diff --git a/gb.gsl/Test/test/.src/Test.class b/gb.gsl/Test/test/.src/Test.class new file mode 100644 index 00000000..65440363 --- /dev/null +++ b/gb.gsl/Test/test/.src/Test.class @@ -0,0 +1,199 @@ +' Gambas class file + +' ================================================================== +' @Class: TestSuite +' @Author: R Morgan <rmorgan62@gmail.com> +' @Date: 03/01/2012 +' @Ver: 0.01 +' @Desc: A framework for running unit and regression tests. +' ================================================================== +Public Name As String ' Name of test, usually the function or method name. +Public msgError As String ' Error Message if any +Public hasError As Boolean = False ' True is we find an error +Public Expected As Variant ' Expected Value +Public Result As Variant ' Result value +Public ExpType As String ' Expected Datatype +Public ResType As String ' Result Datatype +Public Note As String ' Note on test + +'------------------------------------------------------------------- +'@Sub: AddError +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: Void +'@Param msg - A string containing the error message +'------------------------------------------------------------------- +Public Sub AddError(msg As String) + + Me.msgError = msg + Me.hasError = True + +End + + +'------------------------------------------------------------------- +'@Func; TypeError +'@Desc: This method creates an error message for a type error +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: A string containing the type error message. +'@Param msg - A string containing the type as a string. +'------------------------------------------------------------------- +Public Function TypeError(gotType As String, expectedType As String) As String + + Dim msg As String + + msg = "Type error :<<< Expected type: " & expectedType & " Got type: " & gotType & " >>>" + + Return msg + +End + + +'------------------------------------------------------------------- +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: Void +'@Param: msg - A string containing the error message +'------------------------------------------------------------------- +Public Function ValueError(gotValue As Variant, expectedValue As Variant) As String + + Dim msg As String + + msg = "Value error: <<< Expected: " & Str(expectedValue) & " Got: " & Str(gotValue) & " >>>" + Return msg + +End + + +'------------------------------------------------------------------- +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/17/2012 +'@Returns: Void +'@Params: msg - A string contianing the error message. +'------------------------------------------------------------------- +Public Function LengthError(result As Integer, expected As Integer) As String + Dim msg As String + + msg = "Length error: <<< Expected a length of " & Str(expected) & " Got: " & Str(expected) & " >>>" + Return msg + +End + + +'------------------------------------------------------------------- +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: Void +'@Param msg - A string containing the error message +'------------------------------------------------------------------- +Public Function getTypeString(p As Variant) As String + + Select Case TypeOf(p) + Case gb.NULL + Return "NULL" + Case gb.Boolean + Return "Boolean" + Case gb.Byte + Return "Byte" + Case gb.Class + Return "Class" + Case gb.Date + Return "Date" + Case gb.Float + Return "Float" + Case gb.Integer + Return "Integer" + Case gb.Long + Return "Long" + Case gb.Object + Return "Object" + Case gb.Pointer + Return "Pointer" + Case gb.Short + Return "Short" + Case gb.Single + Return "Single" + Case gb.String + Return "String" + Case gb.Variant + Return "Variant" + Default + Return "Unknown" + End Select + +End + + +'------------------------------------------------------------------- +'Compare two value for equality +'------------------------------------------------------------------- +Public Function IsEqual(result As Variant, expected As Variant) As Boolean + Dim err As Boolean = False + Dim i As Integer = 0 + + If TypeOf(result) <> TypeOf(expected) Then + AddError(TypeError(Me.ResType, Me.ExpType)) + Me.hasError = True + Else + If IsObject(result) And result Is Array And expected Is Array Then + If result.len <> expected.len Then + Me.hasError(LengthError(result.len, expected.len)) + Else + For i = 0 To result.len - 1 + If result[i] <> expected[i] Then + Me.hasError = True + Endif + Next + Endif + + Else + If result <> expected Then + AddError(ValueError(Me.Result, Me.Expected)) + Me.hasError = True + Endif + Endif + Endif + + Return Me.hasError + +End + + + +'------------------------------------------------------------------- +'@Desc: This method tests the given values for equality in both +' type and value. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: +'@Param: func - A string containing the function that was tested. +'@Param: result - A variant value containing the actual result of +' the test. +'@Param: expected - A variant value containing the expected result + 'value for the test. +'------------------------------------------------------------------- +Public Sub Run(func As String, result As Variant, expected As Variant, Optional note As String) As Boolean + + Dim err As Boolean = False + + Me.Name = func + Me.Note = note + + Me.Expected = expected + Me.ExpType = getTypeString(expected) + + Me.Result = result + Me.ResType = Me.getTypeString(result) + + Return IsEqual(result, expected) + +End diff --git a/gb.gsl/Test/test/.src/TestComplex.class b/gb.gsl/Test/test/.src/TestComplex.class new file mode 100644 index 00000000..64cb208f --- /dev/null +++ b/gb.gsl/Test/test/.src/TestComplex.class @@ -0,0 +1,94 @@ +' Gambas class file + +Inherits Test + +Public Function getComplexTypeString(p As Variant) As String + Dim result As String + + result = Me.getTypeString(p) + + If result = "Unknown" Then + If result Is Complex Then + result = "Complex" + Endif + Endif + + Return result + +End + + +Public Sub IsComplexEqual(result As Variant, expected As Variant) As Boolean + + If result Is Complex Or expected Is Complex Then + 'We have objects that need special processing + If Me.IsEqual(result, expected) Then + Me.Result = result.ToString() + Me.ResType = "Complext" + Me.Expected = expected.ToString() + Me.ExpType = "Complex" + Else + Me.AddError(Me.ValueError(result.ToString, expected.ToString)) + Me.hasError = True + Endif + Else + Me.hasError = Me.IsEqual(result, expected) + Endif + + Return Me.hasError + +End + + +Public Sub Run(func As String, result As Variant, expected As Variant, Optional note As String) As Boolean + Dim err As Boolean = False + + Me.Name = func + Me.Note = note + + Me.Expected = expected + Me.ExpType = Me.getTypeString(expected) + + + Me.Result = result + Me.ResType = Me.getTypeString(result) + + If TypeOf(result) <> gb.Object And TypeOf(expected) <> gb.Object Then + ' Not an object so run parent code + If TypeOf(result) <> TypeOf(expected) Then + Me.AddError(Me.TypeError(Me.ResType, Me.ExpType)) + err = True + Else + If result <> expected Then + Me.AddError(Me.ValueError(Me.Result, Me.Expected)) + err = True + Endif + Endif + Else + 'We have objects that need special processing + If TypeOf(result) <> TypeOf(expected) Then + Me.AddError(Me.TypeError(Me.ResType, Me.ExpType)) + err = True + Else + If result Is Complex And expected Is Complex Then + If Me.IsEqual(result, expected) Then + Me.Result = result.ToString() + Me.ResType = "Complex Object" + Me.Expected = expected.ToString() + Me.ExpType = "Complex Object" + Else + Me.AddError(Me.ValueError(result.ToString, expected.ToString)) + err = True + Endif + Endif + Endif + + Endif + + Return Me.hasError + + + +End + + diff --git a/gb.gsl/Test/test/.src/TestSuite.class b/gb.gsl/Test/test/.src/TestSuite.class new file mode 100644 index 00000000..99fcb0d1 --- /dev/null +++ b/gb.gsl/Test/test/.src/TestSuite.class @@ -0,0 +1,141 @@ +' Gambas class file + +' ================================================================== +' @Class: TestSuite +' @Author: R Morgan <rmorgan62@gmail.com> +' @Date: 03/01/2012 +' @Ver: 0.01 +' @Desc: A framework for running unit and regression tests. +' ================================================================== + +Public numErrors As Integer +Public msgErrors As New String[] +Private numTests As Integer +Public Tests As New Test[] +Public hasErrors As Boolean +Public HeaderChar As String = "=" +Public HeaderWidth As Integer = 40 +Public TestHeaderChar As String = "-" +Public TestHeaderWidth As Integer = 40 +Public Name As String + +Public Note As String +Public NoteHeaderChar As String = "*" +Public NoteHeaderWidth As Integer = 40 +Public ShowTestNotes As Boolean = False + + +Public Sub AddTest(t As Test) + + Tests.Add(t) + numTests += 1 + +End + + + +Public Procedure ErrorCount() As Integer + Dim i As Integer + Dim t As Test + + For i = 0 To Tests.Length - 1 + t = Tests[i] + If t.hasError Then + Me.numErrors += 1 + Me.hasErrors = True + Endif + Next + + Return Me.numErrors +End + + + + +Public Sub ShowHeader() + Dim Header As String + + Me.ErrorCount() + + Header = String$(Me.HeaderWidth, Me.HeaderChar) + Header &= "\n " & Name & " test\n" + Header &= " Date: " & Date() & "\n" + Header &= " Time: " & Time() & "\n" + + If Me.hasErrors Then + Header &= " Failure:" & Me.numErrors & " errors occurred.\n" + Else + Header &= " Success: All tests passed.\n" + Endif + + Header &= " There are " & numTests & " test in this run.\n" + Header &= String$(Me.HeaderWidth, Me.HeaderChar) + Header &= "\n\n" + + Print Header + +End + +Public Sub ShowNotes() + Dim Header As String + + If Len(Me.Note) > 0 Then + Header = String$(Me.NoteHeaderWidth, Me.NoteHeaderChar) + Header &= "\n" + Header &= Me.Note & "\n" + Header &= String$(Me.NoteHeaderWidth, Me.NoteHeaderChar) + Header &= "\n\n" + + Print Header + + Endif + +End + + + +Public Sub ShowTest(idx As Integer) + Dim t As New Test + Dim header As String + Dim cnt As Integer = idx + 1 + t = Tests[idx] + + header = String$(Me.TestHeaderWidth, Me.TestHeaderChar) + header &= "\n #" & cnt & " " & t.Name & "\n" + + If t.hasError Then + header &= " Status: <<<<< Failure >>>>> \n" + header &= " Error: " & t.msgError & "\n" + Else + header &= " Status: Passed \n" + Endif + + header &= " Expected result: " & Str(t.Expected) & "\n of type: " & t.ExpType & "\n" + header &= " Recieved result: " & Str(t.Result) & "\n of type: " & t.ResType & "\n" + + If ShowTestNotes And Len(t.Note) <> 0 Then + header &= " Notes: " & t.Note & "\n" + Endif + header &= String$(Me.TestHeaderWidth, Me.TestHeaderChar) + + Print header + +End + + +Public Sub ShowTests() + Dim t As Test + Dim i As Integer + + Me.ShowHeader() + Me.ShowNotes() + + i = 0 + For i = 0 To Tests.Length - 1 + ShowTest(i) + Next + +End + + + diff --git a/gb.gsl/acinclude.m4 b/gb.gsl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gsl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gsl/component.am b/gb.gsl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gsl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gsl/configure.ac b/gb.gsl/configure.ac new file mode 100644 index 00000000..79973044 --- /dev/null +++ b/gb.gsl/configure.ac @@ -0,0 +1,19 @@ +dnl ---- configure.ac for gb.gsl + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-gsl],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gsl) +LT_INIT + +GB_COMPONENT_SEARCH( + gsl, GSL, gb.gsl, [src], + gsl, + [GB_FIND(gsl/gsl_cblas.h gsl/gsl_math.h, /usr /usr/local `gsl-config --prefix`, include include/gsl)], + [GB_FIND(libgslcblas.$SHLIBEXT libgsl.$SHLIBEXT, /usr/lib /usr/local /usr/local/lib `gsl-config --prefix`, lib)], + [$X_LIBS -llibgsl -llibgslcblas]) + +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT +GB_PRINT_MESSAGES diff --git a/gb.gsl/gambas.h b/gb.gsl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gsl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gsl/gb_common.h b/gb.gsl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gsl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gsl/m4 b/gb.gsl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gsl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gsl/reconf b/gb.gsl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gsl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gsl/src/Makefile.am b/gb.gsl/src/Makefile.am new file mode 100644 index 00000000..74a5b471 --- /dev/null +++ b/gb.gsl/src/Makefile.am @@ -0,0 +1,17 @@ +COMPONENT = gb.gsl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gsl.la + +gb_gsl_la_LIBADD = @GSL_LIB@ +gb_gsl_la_LDFLAGS = -module @LD_FLAGS@ @GSL_LDFLAGS@ +gb_gsl_la_CPPFLAGS = @GSL_INC@ + +gb_gsl_la_SOURCES = \ + main.c main.h \ + c_gsl.c c_gsl.h \ + c_complex.c c_complex.h \ + c_vector.c c_vector.h \ + c_matrix.c c_matrix.h \ + c_polynomial.c c_polynomial.h \ + c_float_array.c c_float_array.h diff --git a/gb.gsl/src/c_complex.c b/gb.gsl/src/c_complex.c new file mode 100644 index 00000000..d424af53 --- /dev/null +++ b/gb.gsl/src/c_complex.c @@ -0,0 +1,668 @@ +/*************************************************************************** + + c_complex.c + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_COMPLEX_C + +#include "c_complex.h" +#include "c_gsl.h" +#include <stdio.h> + +#define THIS ((CCOMPLEX *)_object) + +gsl_complex COMPLEX_zero = {{ 0.0, 0.0 }}; +gsl_complex COMPLEX_one = {{ 1.0, 0.0 }}; + +//---- Complex number creation ---------------------------------------------- + +CCOMPLEX *COMPLEX_create(gsl_complex number) +{ + CCOMPLEX *c; + + c = (CCOMPLEX *)GB.New(CLASS_Complex, NULL, NULL); + c->number = number; + + return c; +} + +#define COMPLEX_make(_a, _number) (((_a)->ob.ref <= 1) ? ((_a)->number = (_number), (_a)) : COMPLEX_create(_number)) + +CCOMPLEX *COMPLEX_push_complex(double value) +{ + return COMPLEX_create(gsl_complex_rect(0, value)); +} + +//---- Utility functions ---------------------------------------------------- + +int COMPLEX_get_value(GB_VALUE *value, COMPLEX_VALUE *v) +{ + GB.Conv(value, value->_variant.value.type); + + if (value->type >= GB_T_OBJECT && GB.Is(value->_object.value, CLASS_Complex)) + { + CCOMPLEX *c = (CCOMPLEX *)(value->_object.value); + if (GB.CheckObject(c)) + return CGV_ERR; + v->z = c->number; + if (GSL_IMAG(v->z) == 0.0) + return CGV_FLOAT; + else + return CGV_COMPLEX; + } + else + { + if (GB.Conv(value, GB_T_FLOAT)) + return CGV_ERR; + + v->z.dat[0] = value->_float.value; + v->z.dat[1] = 0.0; + return CGV_FLOAT; + } +} + +//---- Arithmetic operators ------------------------------------------------- + +static CCOMPLEX *_addf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, gsl_complex_add_real(a->number, f)); +} + +static CCOMPLEX *_add(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_add(a->number, b->number)); +} + +static CCOMPLEX *_subf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + return COMPLEX_make(a, gsl_complex_add_real(gsl_complex_negative(a->number), f)); + else + return COMPLEX_make(a, gsl_complex_sub_real(a->number, f)); +} + +static CCOMPLEX *_sub(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_sub(a->number, b->number)); +} + +static CCOMPLEX *_mulf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, gsl_complex_mul_real(a->number, f)); +} + +static CCOMPLEX *_mul(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_mul(a->number, b->number)); +} + +static CCOMPLEX *_divf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + { + gsl_complex c = gsl_complex_inverse(a->number); + + if (isfinite(GSL_REAL(c)) && isfinite(GSL_IMAG(c))) + return COMPLEX_make(a, gsl_complex_mul_real(c, f)); + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + } + else + { + gsl_complex c = gsl_complex_div_real(a->number, f); + + if (isfinite(GSL_REAL(c)) && isfinite(GSL_IMAG(c))) + return COMPLEX_make(a, c); + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + } +} + +static CCOMPLEX *_div(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + gsl_complex c = gsl_complex_div(a->number, b->number); + + if (isfinite(GSL_REAL(c)) && isfinite(GSL_IMAG(c))) + return COMPLEX_make(a, c); + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } +} + +static int _equal(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return GSL_REAL(a->number) == GSL_REAL(b->number) && GSL_IMAG(a->number) == GSL_IMAG(b->number); +} + +static int _equalf(CCOMPLEX *a, double f, bool invert) +{ + return GSL_REAL(a->number) == f && GSL_IMAG(a->number) == 0.0; +} + +static CCOMPLEX *_neg(CCOMPLEX *a) +{ + return COMPLEX_create(gsl_complex_negative(a->number)); +} + +static double _fabs(CCOMPLEX *a) +{ + return gsl_complex_abs(a->number); +} + +/*static CCOMPLEX *_powi(CCOMPLEX *a, int i) +{ + CCOMPLEX *r; + bool inv; + + inv = i < 0; + i = abs(i); + + if (i == 2) + r = _mul(a, a); + else if (i == 3) + { + r = COMPLEX_create(RE(a), IM(a)); + r = _mul(r, a); + r = _mul(r, a); + } + else if (i == 4) + { + a = _mul(a, a); + r = _mul(a, a); + } + else + r = COMPLEX_make(a, RE(a), IM(a)); + + if (inv) + return _idivf(r, 1); + else + return r; +}*/ + +static CCOMPLEX *_pow(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_pow(a->number, b->number)); +} + +static CCOMPLEX *_powf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, gsl_complex_pow_real(a->number, f)); +} + + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .add = (void *)_add, + .addf = (void *)_addf, + .sub = (void *)_sub, + .subf = (void *)_subf, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .div = (void *)_div, + .divf = (void *)_divf, + .pow = (void *)_pow, + .powf = (void *)_powf, + .fabs = (void *)_fabs, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +char *COMPLEX_to_string(gsl_complex number, bool local) +{ + char buffer[64]; + char *p; + char *str; + int len; + double real, imag; + + real = number.dat[0]; + imag = number.dat[1]; + + if (real == 0.0 && imag == 0.0) + return GB.NewString("0", 1); + + p = buffer; + + if (real != 0.0) + { + GB.NumberToString(local, real, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + + if (imag != 0.0) + { + if (imag < 0.0) + { + *p++ = '-'; + imag = (-imag); + } + else if (p != buffer) + *p++ = '+'; + + if (imag != 1.0 || !local) + { + GB.NumberToString(local, imag, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + *p++ = 'i'; + } + + return GB.NewString(buffer, p - buffer); +} + +static bool _convert(CCOMPLEX *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_float.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_SINGLE: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_single.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_integer.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_LONG: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_long.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = COMPLEX_to_string(a->number, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + switch(type) + { + case GB_T_FLOAT: + conv->_object.value = COMPLEX_create(gsl_complex_rect(conv->_float.value, 0)); + return FALSE; + + case GB_T_SINGLE: + conv->_object.value = COMPLEX_create(gsl_complex_rect(conv->_single.value, 0)); + return FALSE; + + case GB_T_LONG: + conv->_object.value = COMPLEX_create(gsl_complex_rect((double)conv->_long.value, 0)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_object.value = COMPLEX_create(gsl_complex_rect(conv->_integer.value, 0)); + return FALSE; + + default: + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Complex_new, GB_FLOAT real; GB_FLOAT imag) + + THIS->number.dat[0] = VARGOPT(real, 0.0); + THIS->number.dat[1] = VARGOPT(imag, 0.0); + +END_METHOD + + +BEGIN_METHOD(Complex_call, GB_FLOAT real; GB_FLOAT imag) + + GB.ReturnObject(COMPLEX_create(gsl_complex_rect(VARG(real), VARG(imag)))); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Copy) + + GB.ReturnObject(COMPLEX_create(THIS->number)); + +END_METHOD + + +BEGIN_METHOD(Complex_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(COMPLEX_to_string(THIS->number, VARGOPT(local, FALSE)))); + +END_METHOD + + +BEGIN_METHOD(Complex_Polar, GB_FLOAT real; GB_FLOAT imag) + + GB.ReturnObject(COMPLEX_create(gsl_complex_polar(VARGOPT(real, 0.0), VARGOPT(imag, 0.0)))); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Arg) + + GB.ReturnFloat(gsl_complex_arg(THIS->number)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs) + + GB.ReturnFloat(gsl_complex_abs(THIS->number)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs2) + + GB.ReturnFloat(gsl_complex_abs2(THIS->number)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_LogAbs) + + GB.ReturnFloat(gsl_complex_logabs(THIS->number)); + +END_METHOD + + + +/****************************** + Property Methods +******************************/ + +BEGIN_PROPERTY(Complex_Real) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->number.dat[0]); + else + THIS->number.dat[0] = (VPROP(GB_FLOAT)); + +END_PROPERTY + + +BEGIN_PROPERTY(Complex_Imagined) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->number.dat[1]); + else + THIS->number.dat[1] = (VPROP(GB_FLOAT)); + +END_PROPERTY + + +BEGIN_PROPERTY(Complex_Handle) + + GB.ReturnPointer(&THIS->number); + +END_PROPERTY + + +/************************************************** + Complex arithmetic operators +**************************************************/ + +#define IMPLEMENT_OP(_name, _func) \ +BEGIN_METHOD(Complex_##_name, GB_OBJECT x) \ + \ + CCOMPLEX *x = VARG(x); \ + \ + if (GB.CheckObject(x)) \ + return; \ + \ + GB.ReturnObject(COMPLEX_create(_func(THIS->number, x->number))); \ + \ +END_METHOD + +IMPLEMENT_OP(Add, gsl_complex_add) +IMPLEMENT_OP(Sub, gsl_complex_sub) +IMPLEMENT_OP(Mul, gsl_complex_mul) +IMPLEMENT_OP(Div, gsl_complex_div) + + +/********************************************* + Operations On Real +*********************************************/ + +#define IMPLEMENT_OP_REAL(_name, _func) \ +BEGIN_METHOD(Complex_##_name, GB_FLOAT x) \ + \ + GB.ReturnObject(COMPLEX_create(_func(THIS->number, VARG(x)))); \ + \ +END_METHOD + +IMPLEMENT_OP_REAL(AddReal, gsl_complex_add_real) +IMPLEMENT_OP_REAL(SubReal, gsl_complex_sub_real) +IMPLEMENT_OP_REAL(MulReal, gsl_complex_mul_real) +IMPLEMENT_OP_REAL(DivReal, gsl_complex_div_real) + + +/********************************************* + Operations On Imaginary +*********************************************/ + +IMPLEMENT_OP_REAL(AddImag, gsl_complex_add_imag) +IMPLEMENT_OP_REAL(SubImag, gsl_complex_sub_imag) +IMPLEMENT_OP_REAL(MulImag, gsl_complex_mul_imag) +IMPLEMENT_OP_REAL(DivImag, gsl_complex_div_imag) + +#define IMPLEMENT_FUNC(_name, _func) \ +BEGIN_METHOD_VOID(Complex_##_name) \ + \ + GB.ReturnObject(COMPLEX_create(_func(THIS->number))); \ + \ +END_METHOD + +IMPLEMENT_FUNC(Conjugate, gsl_complex_conjugate) +IMPLEMENT_FUNC(Inverse, gsl_complex_inverse) +IMPLEMENT_FUNC(Negative, gsl_complex_negative) + +/************************************************** + Elementary Complex Functions +**************************************************/ + +IMPLEMENT_FUNC(Sqrt, gsl_complex_sqrt) + +#define IMPLEMENT_FUNC_REAL(_name, _func) \ +BEGIN_METHOD(Complex_##_name, GB_FLOAT x) \ + \ + GB.ReturnObject(COMPLEX_create(_func(VARG(x)))); \ + \ +END_METHOD + +IMPLEMENT_FUNC_REAL(SqrtReal, gsl_complex_sqrt_real) + +IMPLEMENT_OP(Pow, gsl_complex_pow) +IMPLEMENT_OP_REAL(PowReal, gsl_complex_pow_real) + +IMPLEMENT_FUNC(Exp, gsl_complex_exp) +IMPLEMENT_FUNC(Log, gsl_complex_log) +IMPLEMENT_FUNC(Log10, gsl_complex_log10) +IMPLEMENT_OP(LogB, gsl_complex_log_b) + +/************************************************** + Complex Trigonometric Functions +**************************************************/ + +IMPLEMENT_FUNC(Sin, gsl_complex_sin) +IMPLEMENT_FUNC(Cos, gsl_complex_cos) +IMPLEMENT_FUNC(Tan, gsl_complex_tan) +IMPLEMENT_FUNC(Sec, gsl_complex_sec) +IMPLEMENT_FUNC(Csc, gsl_complex_csc) +IMPLEMENT_FUNC(Cot, gsl_complex_cot) + +/************************************************** + Inverse Complex Trigonometric Functions +**************************************************/ + +IMPLEMENT_FUNC(Arcsin, gsl_complex_arcsin) +IMPLEMENT_FUNC_REAL(ArcsinReal, gsl_complex_arcsin_real) +IMPLEMENT_FUNC(Arccos, gsl_complex_arccos) +IMPLEMENT_FUNC_REAL(ArccosReal, gsl_complex_arccos_real) +IMPLEMENT_FUNC(Arctan, gsl_complex_arctan) +IMPLEMENT_FUNC(Arcsec, gsl_complex_arcsec) +IMPLEMENT_FUNC_REAL(ArcsecReal, gsl_complex_arcsec_real) +IMPLEMENT_FUNC(Arccsc, gsl_complex_arccsc) +IMPLEMENT_FUNC_REAL(ArccscReal, gsl_complex_arccsc_real) +IMPLEMENT_FUNC(Arccot, gsl_complex_arccot) + +/************************************************** + Complex Hyperbolic Functions +**************************************************/ + +IMPLEMENT_FUNC(Sinh, gsl_complex_sinh) +IMPLEMENT_FUNC(Cosh, gsl_complex_cosh) +IMPLEMENT_FUNC(Tanh, gsl_complex_tanh) +IMPLEMENT_FUNC(Sech, gsl_complex_sech) +IMPLEMENT_FUNC(Csch, gsl_complex_csch) +IMPLEMENT_FUNC(Coth, gsl_complex_coth) + +/************************************************** + Inverse Complex Hyperbolic Functions +**************************************************/ + +IMPLEMENT_FUNC(Arcsinh, gsl_complex_arcsinh) +IMPLEMENT_FUNC(Arccosh, gsl_complex_arccosh) +IMPLEMENT_FUNC_REAL(ArccoshReal, gsl_complex_arccosh_real) +IMPLEMENT_FUNC(Arctanh, gsl_complex_arctanh) +IMPLEMENT_FUNC_REAL(ArctanhReal, gsl_complex_arctanh_real) +IMPLEMENT_FUNC(Arcsech, gsl_complex_arcsech) +IMPLEMENT_FUNC(Arccsch, gsl_complex_arccsch) +IMPLEMENT_FUNC(Arccoth, gsl_complex_arccoth) + +/************************************************** + Describe Class properties and methods to Gambas +**************************************************/ + +GB_DESC ComplexDesc[] = +{ + GB_DECLARE("Complex", sizeof(CCOMPLEX)), + + // Utility Methods + GB_METHOD("_new", NULL, Complex_new, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("_call", "Complex", Complex_call, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("Polar", "Complex", Complex_Polar, "[(Abs)f(Arg)f]"), + + GB_METHOD("Copy", "Complex", Complex_Copy, NULL), + GB_METHOD("ToString", "s", Complex_ToString, "[(Local)b]"), + + GB_METHOD("Conj", "Complex", Complex_Conjugate, NULL), + GB_METHOD("Inv", "Complex", Complex_Inverse, NULL), + + // Properties + GB_PROPERTY("Real", "f", Complex_Real), + GB_PROPERTY("Imag", "f", Complex_Imagined), + GB_PROPERTY("Handle", "p", Complex_Handle), + + GB_METHOD("Abs", "f", Complex_Abs, NULL), + GB_METHOD("Abs2", "f", Complex_Abs2, NULL), + GB_METHOD("LogAbs", "f", Complex_LogAbs, NULL), + GB_METHOD("Arg", "f", Complex_Arg, NULL), + + // Elementary Complex Functions + GB_METHOD("Sqrt", "Complex", Complex_Sqrt, NULL), + GB_STATIC_METHOD("SqrtReal", "Complex", Complex_SqrtReal, "(X)f"), + //GB_METHOD("Pow", "Complex", Complex_Pow, "(X)Complex"), + //GB_METHOD("PowReal", "Complex", Complex_PowReal, "(X)f"), + GB_METHOD("Exp", "Complex", Complex_Exp, NULL), + GB_METHOD("Log", "Complex", Complex_Log, NULL), + GB_METHOD("Log10", "Complex", Complex_Log10, NULL), + GB_METHOD("LogB", "Complex", Complex_LogB, "(X)Complex"), + + // Complex Trigonometric Functions + GB_METHOD("Sin", "Complex", Complex_Sin, NULL), + GB_METHOD("Cos", "Complex", Complex_Cos, NULL), + GB_METHOD("Tan", "Complex", Complex_Tan, NULL), + GB_METHOD("Sec", "Complex", Complex_Sec, NULL), + GB_METHOD("Csc", "Complex", Complex_Csc, NULL), + GB_METHOD("Cot", "Complex", Complex_Cot, NULL), + + // Inverse Complex Trigonometric Functions + GB_METHOD("ASin", "Complex", Complex_Arcsin, NULL), + GB_STATIC_METHOD("ASinReal", "Complex", Complex_ArcsinReal, "(X)f"), + GB_METHOD("ACos", "Complex", Complex_Arccos, NULL), + GB_STATIC_METHOD("ACosReal", "Complex", Complex_ArccosReal, "(X)f"), + GB_METHOD("ATan", "Complex", Complex_Arctan, NULL), + GB_METHOD("ASec", "Complex", Complex_Arcsec, NULL), + GB_STATIC_METHOD("ASecReal", "Complex", Complex_ArcsecReal, "(X)f"), + GB_METHOD("ACsc", "Complex", Complex_Arccsc, NULL), + GB_STATIC_METHOD("ACscReal", "Complex", Complex_ArccscReal, "(X)f"), + GB_METHOD("ACot", "Complex", Complex_Arccot, NULL), + + // Complex Hyperbolic Functions + GB_METHOD("Sinh", "Complex", Complex_Sinh, NULL), + GB_METHOD("Cosh", "Complex", Complex_Cosh, NULL), + GB_METHOD("Tanh", "Complex", Complex_Tanh, NULL), + GB_METHOD("Sech", "Complex", Complex_Sech, NULL), + GB_METHOD("Csch", "Complex", Complex_Csch, NULL), + GB_METHOD("Coth", "Complex", Complex_Coth, NULL), + + // Inverse Complex Hyperbolic Functions + GB_METHOD("ASinh", "Complex", Complex_Arcsinh, NULL), + GB_METHOD("ACosh", "Complex", Complex_Arccosh, NULL), + GB_STATIC_METHOD("ACoshReal", "Complex", Complex_ArccoshReal, "(X)f"), + GB_METHOD("ATanh", "Complex", Complex_Arctanh, NULL), + GB_STATIC_METHOD("ATanhReal", "Complex", Complex_ArctanhReal, "(X)f"), + GB_METHOD("ASech", "Complex", Complex_Arcsech, NULL), + GB_METHOD("ACsch", "Complex", Complex_Arccsch, NULL), + GB_METHOD("ACoth", "Complex", Complex_Arccoth, NULL), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_complex.h b/gb.gsl/src/c_complex.h new file mode 100644 index 00000000..6b6db0cb --- /dev/null +++ b/gb.gsl/src/c_complex.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + c_complex.h + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_COMPLEX_H +#define __C_COMPLEX_H + +#include "main.h" + +#ifndef _C_COMPLEX_C +extern GB_DESC ComplexDesc[]; +extern gsl_complex COMPLEX_zero; +extern gsl_complex COMPLEX_one; +//extern GB_DESC ComplexArrayDesc[]; +#endif + +typedef + struct + { + GB_BASE ob; + gsl_complex number; + } + CCOMPLEX; + +typedef + union + { + gsl_complex z; + double x; + } + COMPLEX_VALUE; + +enum +{ + CGV_ERR, + CGV_FLOAT, + CGV_COMPLEX +}; + +CCOMPLEX *COMPLEX_create(gsl_complex number); +CCOMPLEX *COMPLEX_push_complex(double value); +char *COMPLEX_to_string(gsl_complex number, bool local); + +#define COMPLEX_get(_c) ((_c) ? (_c)->number : COMPLEX_zero) + +int COMPLEX_get_value(GB_VALUE *value, COMPLEX_VALUE *v); +int COMPLEX_comp(gsl_complex a, gsl_complex b); + +#endif /* __C_COMPLEX_H */ diff --git a/gb.gsl/src/c_float_array.c b/gb.gsl/src/c_float_array.c new file mode 100644 index 00000000..7bd0a48a --- /dev/null +++ b/gb.gsl/src/c_float_array.c @@ -0,0 +1,421 @@ +/*************************************************************************** + + c_float_array.c + + gb.gsl component + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_FLOAT_ARRAY_C + +#include "c_float_array.h" +#include <gsl/gsl_sort.h> +#include <gsl/gsl_statistics.h> + +#define THIS ((CARRAY *)_object) +#define DATA ((double *)(THIS->array.data)) +#define COUNT (THIS->array.count) + +//------------------------------------------------------------------------- + +static bool check_array(void *array, int count, void **data) +{ + if (!array) + { + *data = NULL; + return FALSE; + } + + if (GB.CheckObject(array)) + return TRUE; + + if (((CARRAY *)array)->array.count != count) + { + GB.Error("Incorrect array size"); + return TRUE; + } + + *data = ((CARRAY *)array)->array.data; + return FALSE; +} + +BEGIN_METHOD(FloatArrayStat_Mean, GB_OBJECT weight) + + double *weight; + + if (check_array(VARGOPT(weight, NULL), COUNT, POINTER(&weight))) + return; + + if (weight) + GB.ReturnFloat(gsl_stats_wmean(weight, 1, DATA, 1, COUNT)); + else + GB.ReturnFloat(gsl_stats_mean(DATA, 1, COUNT)); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Variance, GB_OBJECT weight; GB_FLOAT mean; GB_BOOLEAN fixed) + + double *weight; + double mean; + + if (check_array(VARGOPT(weight, NULL), COUNT, POINTER(&weight))) + return; + + if (weight) + { + mean = VARGOPT(mean, gsl_stats_wmean(weight, 1, DATA, 1, COUNT)); + + if (VARGOPT(fixed, FALSE)) + GB.ReturnFloat(gsl_stats_wvariance_m(weight, 1, DATA, 1, COUNT, mean)); + else + GB.ReturnFloat(gsl_stats_wvariance_with_fixed_mean(weight, 1, DATA, 1, COUNT, mean)); + } + else + { + mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + + if (VARGOPT(fixed, FALSE)) + GB.ReturnFloat(gsl_stats_variance_m(DATA, 1, COUNT, mean)); + else + GB.ReturnFloat(gsl_stats_variance_with_fixed_mean(DATA, 1, COUNT, mean)); + } + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_StdDev, GB_OBJECT weight; GB_FLOAT mean; GB_BOOLEAN fixed) + + double *weight; + double mean; + + if (check_array(VARGOPT(weight, NULL), COUNT, POINTER(&weight))) + return; + + if (weight) + { + mean = VARGOPT(mean, gsl_stats_wmean(weight, 1, DATA, 1, COUNT)); + + if (VARGOPT(fixed, FALSE)) + GB.ReturnFloat(gsl_stats_wsd_m(weight, 1, DATA, 1, COUNT, mean)); + else + GB.ReturnFloat(gsl_stats_wsd_with_fixed_mean(weight, 1, DATA, 1, COUNT, mean)); + } + else + { + mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + + if (VARGOPT(fixed, FALSE)) + GB.ReturnFloat(gsl_stats_sd_m(DATA, 1, COUNT, mean)); + else + GB.ReturnFloat(gsl_stats_sd_with_fixed_mean(DATA, 1, COUNT, mean)); + } + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Tss, GB_OBJECT weight; GB_FLOAT mean) + + double *weight; + double mean; + + if (check_array(VARGOPT(weight, NULL), COUNT, POINTER(&weight))) + return; + + if (weight) + { + mean = VARGOPT(mean, gsl_stats_wmean(weight, 1, DATA, 1, COUNT)); + GB.ReturnFloat(gsl_stats_wtss_m(weight, 1, DATA, 1, COUNT, mean)); + } + else + { + mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + GB.ReturnFloat(gsl_stats_tss_m(DATA, 1, COUNT, mean)); + } + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_AbsDev,GB_OBJECT weight; GB_FLOAT mean) + + double *weight; + double mean; + + if (check_array(VARGOPT(weight, NULL), COUNT, POINTER(&weight))) + return; + + if (weight) + { + mean = VARGOPT(mean, gsl_stats_wmean(weight, 1, DATA, 1, COUNT)); + GB.ReturnFloat(gsl_stats_wabsdev_m(weight, 1, DATA, 1, COUNT, mean)); + } + else + { + mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + GB.ReturnFloat(gsl_stats_absdev_m(DATA, 1, COUNT, mean)); + } + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Skew, GB_OBJECT weight; GB_FLOAT mean; GB_FLOAT sd) + + double *weight; + double mean; + double sd; + + if (check_array(VARGOPT(weight, NULL), COUNT, POINTER(&weight))) + return; + + if (weight) + { + mean = VARGOPT(mean, gsl_stats_wmean(weight, 1, DATA, 1, COUNT)); + sd = VARGOPT(sd, gsl_stats_wsd_m(weight, 1, DATA, 1, COUNT, mean)); + GB.ReturnFloat(gsl_stats_wskew_m_sd(weight, 1, DATA, 1, COUNT, mean, sd)); + } + else + { + mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + sd = VARGOPT(sd, gsl_stats_sd_m(DATA, 1, COUNT, mean)); + GB.ReturnFloat(gsl_stats_skew_m_sd(DATA, 1, COUNT, mean, sd)); + } + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Kurtosis, GB_OBJECT weight; GB_FLOAT mean; GB_FLOAT sd) + + double *weight; + double mean; + double sd; + + if (check_array(VARGOPT(weight, NULL), COUNT, POINTER(&weight))) + return; + + if (weight) + { + mean = VARGOPT(mean, gsl_stats_wmean(weight, 1, DATA, 1, COUNT)); + sd = VARGOPT(sd, gsl_stats_wsd_m(weight, 1, DATA, 1, COUNT, mean)); + GB.ReturnFloat(gsl_stats_wkurtosis_m_sd(weight, 1, DATA, 1, COUNT, mean, sd)); + } + else + { + mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + sd = VARGOPT(sd, gsl_stats_sd_m(DATA, 1, COUNT, mean)); + GB.ReturnFloat(gsl_stats_kurtosis_m_sd(DATA, 1, COUNT, mean, sd)); + } + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_AutoCorrelation, GB_FLOAT mean) + + double mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + GB.ReturnFloat(gsl_stats_lag1_autocorrelation_m(DATA, 1, COUNT, mean)); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Covariance, GB_OBJECT other; GB_FLOAT mean; GB_FLOAT mean_other) + + double mean = VARGOPT(mean, gsl_stats_mean(DATA, 1, COUNT)); + double mean_other; + double *other_data; + + if (check_array(VARGOPT(other, NULL), COUNT, POINTER(&other_data))) + return; + + mean_other = VARGOPT(mean_other, gsl_stats_mean(other_data, 1, COUNT)); + + GB.ReturnFloat(gsl_stats_covariance_m(DATA, 1, other_data, 1, COUNT, mean, mean_other)); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Correlation, GB_OBJECT other) + + double *other_data; + + if (check_array(VARGOPT(other, NULL), COUNT, POINTER(&other_data))) + return; + + GB.ReturnFloat(gsl_stats_correlation(DATA, 1, other_data, 1, COUNT)); + +END_METHOD + +static double *get_sorted(CARRAY *_object, bool sorted) +{ + double *data; + int count = THIS->array.count; + + if (!sorted && count) + { + GB.Alloc(POINTER(&data), sizeof(double) * count); + memcpy(data, THIS->array.data, sizeof(double) * count); + gsl_sort(data, 1, count); + } + else + data = THIS->array.data; + + return data; +} + +static void free_sorted(CARRAY *_object, double *data) +{ + if (data != THIS->array.data) + GB.Free(POINTER(&data)); +} + +BEGIN_METHOD(FloatArrayStat_Median, GB_BOOLEAN sorted) + + double *data = get_sorted(THIS, VARGOPT(sorted, FALSE)); + GB.ReturnFloat(gsl_stats_median_from_sorted_data(data, 1, COUNT)); + free_sorted(THIS, data); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Quantile, GB_FLOAT quantile; GB_BOOLEAN sorted) + + double *data = get_sorted(THIS, VARGOPT(sorted, FALSE)); + GB.ReturnFloat(gsl_stats_quantile_from_sorted_data(data, 1, COUNT, VARG(quantile))); + free_sorted(THIS, data); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_TrimmedMean, GB_FLOAT trim; GB_BOOLEAN sorted) + + double *data = get_sorted(THIS, VARGOPT(sorted, FALSE)); + GB.ReturnFloat(gsl_stats_trmean_from_sorted_data(VARG(trim), data, 1, COUNT)); + free_sorted(THIS, data); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Gastwirth, GB_FLOAT sorted) + + double *data = get_sorted(THIS, VARGOPT(sorted, FALSE)); + GB.ReturnFloat(gsl_stats_gastwirth_from_sorted_data(data, 1, COUNT)); + free_sorted(THIS, data); + +END_METHOD + +BEGIN_METHOD_VOID(FloatArrayStat_MinValue) + + GB.ReturnFloat(gsl_stats_min(DATA, 1, COUNT)); + +END_METHOD + +BEGIN_METHOD_VOID(FloatArrayStat_MaxValue) + + GB.ReturnFloat(gsl_stats_max(DATA, 1, COUNT)); + +END_METHOD + +static void *get_buffer(CARRAY *_object, void *array, size_t size, int count) +{ + void *buffer; + + if (check_array(array, count, POINTER(&buffer))) + return NULL; + + if (!buffer) + GB.Alloc(POINTER(&buffer), size * count); + + return buffer; +} + +static void free_buffer(void *buffer, void *array) +{ + if (!array) + GB.Free(POINTER(&buffer)); +} + +BEGIN_METHOD(FloatArrayStat_Mad, GB_BOOLEAN bias; GB_OBJECT buffer) + + double *buffer = get_buffer(THIS, VARGOPT(buffer, NULL), sizeof(double), COUNT); + + if (VARGOPT(bias, FALSE)) + GB.ReturnFloat(gsl_stats_mad0(DATA, 1, COUNT, buffer)); + else + GB.ReturnFloat(gsl_stats_mad(DATA, 1, COUNT, buffer)); + + free_buffer(buffer, VARGOPT(buffer, NULL)); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Sn, GB_BOOLEAN sorted; GB_BOOLEAN bias; GB_OBJECT buffer) + + double *data = get_sorted(THIS, VARGOPT(sorted, FALSE)); + double *buffer = get_buffer(THIS, VARGOPT(buffer, NULL), sizeof(double), COUNT); + + if (VARGOPT(bias, FALSE)) + GB.ReturnFloat(gsl_stats_Sn0_from_sorted_data(data, 1, COUNT, buffer)); + else + GB.ReturnFloat(gsl_stats_Sn_from_sorted_data(data, 1, COUNT, buffer)); + + free_buffer(buffer, VARGOPT(buffer, NULL)); + free_sorted(THIS, data); + +END_METHOD + +BEGIN_METHOD(FloatArrayStat_Qn, GB_BOOLEAN sorted; GB_BOOLEAN bias; GB_OBJECT buffer; GB_OBJECT buffer_int) + + double *data = get_sorted(THIS, VARGOPT(sorted, FALSE)); + double *buffer = get_buffer(THIS, VARGOPT(buffer, NULL), sizeof(double), 3 * COUNT); + int *buffer_int = get_buffer(THIS, VARGOPT(buffer_int, NULL), sizeof(int), 5 * COUNT); + + if (VARGOPT(bias, FALSE)) + GB.ReturnFloat(gsl_stats_Qn0_from_sorted_data(data, 1, COUNT, buffer, buffer_int)); + else + GB.ReturnFloat(gsl_stats_Qn_from_sorted_data(data, 1, COUNT, buffer, buffer_int)); + + free_buffer(buffer, VARGOPT(buffer, NULL)); + free_buffer(buffer_int, VARGOPT(buffer_int, NULL)); + free_sorted(THIS, data); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC FloatArrayStatDesc[] = +{ + GB_DECLARE_VIRTUAL(".FloatArrayStat"), + + GB_METHOD("Mean", "f", FloatArrayStat_Mean, "[(Weight)Float[];]"), + GB_METHOD("Variance", "f", FloatArrayStat_Variance, "[(Weight)Float[];(Mean)f(Fixed)b]"), + GB_METHOD("StdDev", "f", FloatArrayStat_StdDev, "[(Weight)Float[];(Mean)f(Fixed)b]"), + GB_METHOD("Tss", "f", FloatArrayStat_Tss, "[(Weight)Float[];(Mean)f]"), + GB_METHOD("AbsDev", "f", FloatArrayStat_AbsDev, "[(Weight)Float[];(Mean)f]"), + GB_METHOD("Skew", "f", FloatArrayStat_Skew, "[(Weight)Float[];(Mean)f(StdDev)f]"), + GB_METHOD("Kurtosis", "f", FloatArrayStat_Kurtosis, "[(Weight)Float[];(Mean)f(StdDev)f]"), + GB_METHOD("AutoCorrelation", "f", FloatArrayStat_AutoCorrelation, "[(Mean)f]"), + GB_METHOD("Covariance", "f", FloatArrayStat_Covariance, "(Other)Float[];[(Mean)f(MeanOther)f]"), + GB_METHOD("Correlation", "f", FloatArrayStat_Correlation, "(Other)Float[];"), + GB_METHOD("Median", "f", FloatArrayStat_Median, "[(Sorted)b]"), + GB_METHOD("Quantile", "f", FloatArrayStat_Quantile, "(Quantile)f[(Sorted)b]"), + GB_METHOD("TrimmedMean", "f", FloatArrayStat_TrimmedMean, "(Trim)f[(Sorted)b]"), + GB_METHOD("Gastwirth", "f", FloatArrayStat_Gastwirth, "[(Sorted)b]"), + GB_METHOD("Min", "f", FloatArrayStat_MinValue, NULL), + GB_METHOD("Max", "f", FloatArrayStat_MaxValue, NULL), + GB_METHOD("Mad", "f", FloatArrayStat_Mad, "[(Bias)b(Buffer)Float[];]"), + GB_METHOD("Sn", "f", FloatArrayStat_Sn, "[(Sorted)b(Bias)b(Buffer)Float[];]"), + GB_METHOD("Qn", "f", FloatArrayStat_Qn, "[(Sorted)b(Bias)b(Buffer)Float[];(BufferInt)Integer[];]"), + + GB_END_DECLARE +}; + +GB_DESC FloatArrayDesc[] = +{ + GB_DECLARE("Float[]", sizeof(CARRAY)), + + GB_PROPERTY_SELF("Stat", ".FloatArrayStat"), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_float_array.h b/gb.gsl/src/c_float_array.h new file mode 100644 index 00000000..f8320933 --- /dev/null +++ b/gb.gsl/src/c_float_array.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + c_float_array.h + + gb.gsl component + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_FLOAT_ARRAY_H +#define __C_FLOAT_ARRAY_H + +#include "main.h" + +#ifndef __C_FLOAT_ARRAY_C +extern GB_DESC FloatArrayStatDesc[]; +extern GB_DESC FloatArrayDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + GB_ARRAY_BASE array; + } + CARRAY; + +#endif /* __C_COMPLEX_H */ diff --git a/gb.gsl/src/c_gsl.c b/gb.gsl/src/c_gsl.c new file mode 100644 index 00000000..7c447e26 --- /dev/null +++ b/gb.gsl/src/c_gsl.c @@ -0,0 +1,327 @@ +/*************************************************************************** + + c_gsl.c + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_GSL_C + +#include "c_gsl.h" +#include <gsl/gsl_math.h> +#include <gsl/gsl_sf.h> + + +/*-------------------------------- + Number testing functions +--------------------------------*/ + +BEGIN_METHOD(Gsl_IsNan, GB_FLOAT x;) + // This function returns 1 if x is not-a-number. + // Call GSL Function int gsl_isnan(const double x) + int c; + + c = gsl_isnan(VARG(x)); + + GB.ReturnBoolean(c); + +END_METHOD + + +BEGIN_METHOD(Gsl_IsInf, GB_FLOAT x;) + // This function returns +1 if x is positive infinity, + // -1 if x is negative infinity and 0 otherwise. + // Call GSL Function int gsl_isinf(const double x) + int c; + + c= gsl_isinf(VARG(x)); + + GB.ReturnInteger(c); + +END_METHOD + + +BEGIN_METHOD(Gsl_IsFinite, GB_FLOAT x;) + // This function returns 1 if x is a real number, + // and -1 if it is infinite or not-a-number. + // Call GSL Function int gsl_isfinite(const double x) + int c; + + c = gsl_finite(VARG(x)); + + GB.ReturnBoolean(c); + +END_METHOD + + +BEGIN_METHOD(Gsl_Fcmp, GB_FLOAT x; GB_FLOAT y; GB_FLOAT e;) + // Function: int gsl_fcmp (double x, double y, double epsilon) + // This function determines whether x and y are approximately + // equal to a relative accuracy epsilon. + // The relative accuracy is measured using an interval of size 2 \delta, + // where \delta = 2^k \epsilon and k is the maximum base-2 exponent of x + // and y as computed by the function frexp. + // If x and y lie within this interval, they are considered approximately + // equal and the function returns 0. Otherwise if x < y, the function returns + // -1, or if x > y, the function returns +1. + // Note that x and y are compared to relative accuracy, so this function is + // not suitable for testing whether a value is approximately zero. + + GB.ReturnInteger(gsl_fcmp (VARG(x), VARG(y), VARG(e))); + +END_METHOD + + + +/*----------------------------------------------- +Elementary Functions +-----------------------------------------------*/ + +BEGIN_METHOD(Gsl_Log1p, GB_FLOAT x;) + // This function computes the value of \log(1+x) + // in a way that is accurate for small x. + // Call GSL Function int gsl_isnan(const double x) + GB.ReturnFloat(gsl_log1p (VARG(x))); + +END_METHOD + + +BEGIN_METHOD(Gsl_Expm1, GB_FLOAT x;) + // This function computes the value of \exp(x)-1 + // in a way that is accurate for small x. + GB.ReturnFloat(gsl_expm1 (VARG(x))); + +END_METHOD + + +BEGIN_METHOD(Gsl_Hypot, GB_FLOAT x; GB_FLOAT y;) + // This function computes the value of + // \sqrt{x^2 + y^2} in a way that avoids overflow. + // Call GSL function double gsl_hypot (const double x, const double y) + GB.ReturnFloat(gsl_hypot(VARG(x), VARG(y))); + +END_METHOD + + +BEGIN_METHOD(Gsl_Hypot3, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z;) + // This function computes the value of \sqrt{x^2 + y^2 + z^2} + // in a way that avoids overflow. + // Call GSL function double gsl_hypot3 (const double x, const double y, const double z) + + GB.ReturnFloat(gsl_hypot3(VARG(x), VARG(y), VARG(z))); + +END_METHOD + +/*static void Gsl_Hypot3(ushort code, GB_VALUE *sp) +{ + int nparam = code & 0x3F; + GB_VALUE *param = (sp - nparam); + + if (param[0].type != GB_T_FLOAT) GB.Conv(¶m[0], GB_T_FLOAT); + if (param[1].type != GB_T_FLOAT) GB.Conv(¶m[1], GB_T_FLOAT); + if (param[2].type != GB_T_FLOAT) GB.Conv(¶m[2], GB_T_FLOAT); + + param[0]._float.value = gsl_hypot3(param[0]._float.value, param[1]._float.value, param[2]._float.value); +}*/ + + +BEGIN_METHOD(Gsl_Acosh, GB_FLOAT x;) + // This function computes the value of \arccosh(x). + // It provides an alternative to the standard math function acosh(x). + GB.ReturnFloat(gsl_acosh(VARG(x))); + +END_METHOD + +BEGIN_METHOD(Gsl_Asinh, GB_FLOAT x;) + // Function: double gsl_asinh (const double x) + // This function computes the value of arcsinh(x). + // It provides an alternative to the standard math function asinh(x). + GB.ReturnFloat(gsl_asinh(VARG(x))); +END_METHOD + +BEGIN_METHOD(Gsl_Atanh, GB_FLOAT x;) + // Function: double gsl_atanh (const double x) + // This function computes the value of \arctanh(x). + // It provides an alternative to the standard math function atanh(x). + GB.ReturnFloat(gsl_atanh(VARG(x))); +END_METHOD + +BEGIN_METHOD(Gsl_Ldexp, GB_FLOAT x; GB_INTEGER e;) + // Function: double gsl_ldexp (double x, int e) + // This function computes the value of x * 2^e. + // It provides an alternative to the standard math function ldexp(x,e). + GB.ReturnFloat(gsl_ldexp(VARG(x), VARG(e))); +END_METHOD + + +BEGIN_METHOD(Gsl_Frexp, GB_FLOAT x;) + // Function: double gsl_frexp (double x, int * e) + // This function splits the number x into its normalized + // fraction f and exponent e, such that x = f * 2^e and + // 0.5 <= f < 1. The function returns f and stores the + // exponent in e. If x is zero, both f and e are set to + // zero. This function provides an alternative to the + // standard math function frexp(x, e). + int b; + double r; + GB_ARRAY arr; + + b = 0.0; + r = gsl_frexp(VARG(x), &b); + //printf("r: %f \te: %i\n", r, b); + + GB.Array.New(&arr, GB_T_FLOAT, 2); + *((double *)GB.Array.Get(arr, 0)) = r; + *((double *)GB.Array.Get(arr, 1)) = b; + GB.ReturnObject(arr); + +END_METHOD + +// BM they are useless, as they are already implemented that way in the interpreter. + +#if 0 +/*----------------------------------------------- +Small Integer Power Functions +-----------------------------------------------*/ +BEGIN_METHOD(GSL_INTPOW, GB_FLOAT x; GB_INTEGER i;) + // A common complaint about the standard C library is its lack + // of a function for calculating (small) integer powers. GSL + // provides some simple functions to fill this gap. For reasons + // of efficiency, these functions do not check for overflow or + // underflow conditions. + GB.ReturnFloat(gsl_pow_int(VARG(x), VARG(i))); + +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW2, GB_FLOAT x;) + // Return x^2 using a small int safe method + // call gsl native function double gsl_pow_2(double x) + GB.ReturnFloat(gsl_pow_2(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW3, GB_FLOAT x;) + // Return x^3 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_3(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW4, GB_FLOAT x;) + // Return x^4 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_4(VARG(x))); +END_METHOD + +BEGIN_METHOD(GSL_INTPOW5, GB_FLOAT x;) + // Return x^5 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_5(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW6, GB_FLOAT x;) + // Return x^6 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_6(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW7, GB_FLOAT x;) + // Return x^7 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_7(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW8, GB_FLOAT x;) + // Return x^8 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_8(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW9, GB_FLOAT x;) + // Return x^9 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_9(VARG(x))); +END_METHOD +#endif + +/************************************************** + Describe Class properties and methods to Gambas +**************************************************/ +GB_DESC GslDesc[] = +{ + GB_DECLARE("Gsl",0), GB_NOT_CREATABLE(), + + // Number testing functions + GB_STATIC_METHOD("IsNan", "b", Gsl_IsNan, "(X)f"), + GB_STATIC_METHOD("IsInf", "i", Gsl_IsInf, "(X)f"), + GB_STATIC_METHOD("IsFinite", "b", Gsl_IsFinite, "(X)f"), + GB_STATIC_METHOD("Fcmp", "i", Gsl_Fcmp, "(X)f(Y)f(E)f"), + + // Elementary Functions + GB_STATIC_METHOD("Log1p", "f", Gsl_Log1p, "(X)f"), + GB_STATIC_METHOD("Expm1", "f", Gsl_Expm1, "(X)f"), + GB_STATIC_METHOD("Hypot", "f", Gsl_Hypot, "(X)f(Y)f"), + GB_STATIC_METHOD("Hypot3", "f", Gsl_Hypot3, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Acosh", "f", Gsl_Acosh, "(X)f"), + GB_STATIC_METHOD("Asinh", "f", Gsl_Asinh, "(X)f"), + GB_STATIC_METHOD("Atanh", "f", Gsl_Atanh, "(X)f"), + GB_STATIC_METHOD("Ldexp", "f", Gsl_Ldexp, "(X)f(E)i"), + GB_STATIC_METHOD("Frexp", "Float[]", Gsl_Frexp, "(X)f"), + + // Return x^y using a small int safe method + /*GB_STATIC_METHOD("IntPow", "f", GSL_INTPOW, "(X)f(I)i"), + GB_STATIC_METHOD("IntPow2", "f", GSL_INTPOW2, "(X)f"), + GB_STATIC_METHOD("IntPow3", "f", GSL_INTPOW3, "(X)f"), + GB_STATIC_METHOD("IntPow4", "f", GSL_INTPOW4, "(X)f"), + GB_STATIC_METHOD("IntPow5", "f", GSL_INTPOW5, "(X)f"), + GB_STATIC_METHOD("IntPow6", "f", GSL_INTPOW6, "(X)f"), + GB_STATIC_METHOD("IntPow7", "f", GSL_INTPOW7, "(X)f"), + GB_STATIC_METHOD("IntPow8", "f", GSL_INTPOW8, "(X)f"), + GB_STATIC_METHOD("IntPow9", "f", GSL_INTPOW9, "(X)f"),*/ + + // Class Constants + GB_FLOAT_CONSTANT("E", M_E), + GB_FLOAT_CONSTANT("LOG2E", M_LOG2E), + GB_FLOAT_CONSTANT("LOG10E", M_LOG10E), + GB_FLOAT_CONSTANT("SQRT2", M_SQRT2), + GB_FLOAT_CONSTANT("SQRT1_2", M_SQRT1_2), + GB_FLOAT_CONSTANT("SQRT3", M_SQRT3), + GB_FLOAT_CONSTANT("PI", M_PI), + GB_FLOAT_CONSTANT("PI_2", M_PI_2), + GB_FLOAT_CONSTANT("PI_4", M_PI_4), + GB_FLOAT_CONSTANT("SQRTPI", M_SQRTPI), + GB_FLOAT_CONSTANT("INV2_SQRTPI", M_2_SQRTPI), + GB_FLOAT_CONSTANT("INV_PI", M_1_PI), + GB_FLOAT_CONSTANT("INV2_PI", M_2_PI), + GB_FLOAT_CONSTANT("LN10", M_LN10), + GB_FLOAT_CONSTANT("LN2", M_LN2), + GB_FLOAT_CONSTANT("LNPI", M_LNPI), + GB_FLOAT_CONSTANT("EULER", M_EULER), + + GB_END_DECLARE +}; + + diff --git a/gb.gsl/src/c_gsl.h b/gb.gsl/src/c_gsl.h new file mode 100644 index 00000000..f1dcbc63 --- /dev/null +++ b/gb.gsl/src/c_gsl.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + c_gsl.h + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_GSL_H +#define __C_GSL_H + +#include "gambas.h" + +extern GB_INTERFACE GB EXPORT; + +#ifndef __C_GSL_C +extern GB_DESC GslDesc[]; +#endif + +#endif /* __C_GSL_H */ diff --git a/gb.gsl/src/c_matrix.c b/gb.gsl/src/c_matrix.c new file mode 100644 index 00000000..b7c82bfd --- /dev/null +++ b/gb.gsl/src/c_matrix.c @@ -0,0 +1,1300 @@ +/*************************************************************************** + + c_vector.c + + gb.gsl component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MATRIX_C + +#include "c_complex.h" +#include "c_vector.h" +#include "c_matrix.h" + +#define THIS ((CMATRIX *)_object) +#define MAT(_m) ((gsl_matrix *)(_m)->matrix) +#define CMAT(_m) ((gsl_matrix_complex *)(_m)->matrix) +#define HEIGHT(_m) ((int)(MAT(_m)->size1)) +#define WIDTH(_m) ((int)(MAT(_m)->size2)) +#define COMPLEX(_m) ((_m)->complex) + +//---- Matrix creation ------------------------------------------------------ + +static CMATRIX *MATRIX_create(int width, int height, bool complex, bool init) +{ + CMATRIX *m = GB.Create(CLASS_Matrix, NULL,NULL); + + if (complex) + m->matrix = init ? gsl_matrix_complex_calloc(height, width) : gsl_matrix_complex_alloc(height, width); + else + m->matrix = init ? gsl_matrix_calloc(height, width) : gsl_matrix_alloc(height, width); + + m->complex = complex; + return m; +} + +static CMATRIX *MATRIX_identity(int width, int height, bool complex) +{ + CMATRIX *m = MATRIX_create(width, height, complex, FALSE); + + if (complex) + gsl_matrix_complex_set_identity(CMAT(m)); + else + gsl_matrix_set_identity(MAT(m)); + + return m; +} + +static CMATRIX *MATRIX_create_from(void *matrix, bool complex) +{ + CMATRIX *m = GB.Create(CLASS_Matrix, NULL,NULL); + + m->matrix = matrix; + m->complex = complex; + return m; +} + +static CMATRIX *MATRIX_copy(CMATRIX *_object) +{ + CMATRIX *copy = MATRIX_create(WIDTH(THIS), HEIGHT(THIS), COMPLEX(THIS), FALSE); + if (COMPLEX(THIS)) + gsl_matrix_complex_memcpy(CMAT(copy), CMAT(THIS)); + else + gsl_matrix_memcpy(MAT(copy), MAT(THIS)); + + return copy; +} + +#define MATRIX_make(_ma) (((_ma)->ob.ref <= 1) ? (_ma) : MATRIX_copy(_ma)) + +/*static CMATRIX *MATRIX_convert_to_complex(CMATRIX *_object) +{ + CMATRIX *m = MATRIX_create(WIDTH(THIS), HEIGHT(THIS), TRUE, FALSE); + int i, j; + + for (i = 0; i < HEIGHT(THIS); i++) + for (j = 0; j < WIDTH(THIS); j++) + gsl_matrix_complex_set(CMAT(m), i, j, gsl_complex_rect(gsl_matrix_get(MAT(THIS), i, j), 0)); + + return m; +}*/ + +static void MATRIX_ensure_complex(CMATRIX *_object) +{ + gsl_matrix_complex *v; + int w = WIDTH(THIS); + int h = HEIGHT(THIS); + int i, j; + + if (COMPLEX(THIS)) + return; + + v = gsl_matrix_complex_alloc(h, w); + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + gsl_matrix_complex_set(v, i, j, gsl_complex_rect(gsl_matrix_get(MAT(THIS), i, j), 0)); + + gsl_matrix_free(MAT(THIS)); + THIS->matrix = v; + THIS->complex = TRUE; +} + + +/*static bool MATRIX_ensure_not_complex(CMATRIX *_object) +{ + gsl_matrix *m; + int w = WIDTH(THIS); + int h = HEIGHT(THIS); + int i, j; + gsl_complex c; + + if (!COMPLEX(THIS)) + return FALSE; + + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + { + c = gsl_matrix_complex_get(CMAT(THIS), i, j); + if (GSL_IMAG(c) != 0.0) + return TRUE; + } + + m = gsl_matrix_alloc(h, w); + + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + gsl_matrix_set(m, i, j, GSL_REAL(gsl_matrix_complex_get(CMAT(THIS), i, j))); + + gsl_matrix_complex_free(CMAT(THIS)); + THIS->matrix = m; + THIS->complex = FALSE; + return FALSE; +}*/ + +static void matrix_negative(void *m, bool complex) +{ + uint i; + gsl_matrix *mm = (gsl_matrix *)m; + double *d = mm->data; + uint n = (uint)(mm->size1 * mm->size2); + + if (complex) + n *= 2; + + for (i = 0; i < n; i++) + d[i] = -d[i]; +} + +static bool matrix_determinant(CMATRIX *m, COMPLEX_VALUE *det) +{ + int sign = 0; + int size = WIDTH(m); + + if (size != HEIGHT(m)) + return TRUE; + + gsl_permutation *p = gsl_permutation_calloc(size); + + if (COMPLEX(m)) + { + gsl_matrix_complex *tmp = gsl_matrix_complex_alloc(size, size); + gsl_matrix_complex_memcpy(tmp, CMAT(m)); + gsl_linalg_complex_LU_decomp(tmp, p, &sign); + det->z = gsl_linalg_complex_LU_det(tmp, sign); + gsl_matrix_complex_free(tmp); + } + else + { + gsl_matrix *tmp = gsl_matrix_alloc(size, size); + gsl_matrix_memcpy(tmp, MAT(m)); + gsl_linalg_LU_decomp(tmp, p, &sign); + det->x = gsl_linalg_LU_det(tmp, sign); + det->z.dat[1] = 0; + gsl_matrix_free(tmp); + } + + gsl_permutation_free(p); + return FALSE; +} + +static void *matrix_invert(void *m, bool complex) +{ + int sign = 0; + int size = ((gsl_matrix *)m)->size1; + void *result; + + if (size != ((gsl_matrix *)m)->size2) + return NULL; + + gsl_permutation *p = gsl_permutation_calloc(size); + + if (!complex) + { + gsl_matrix *tmp = gsl_matrix_alloc(size, size); + result = gsl_matrix_alloc(size, size); + gsl_matrix_memcpy(tmp, (gsl_matrix *)m); + gsl_linalg_LU_decomp(tmp, p, &sign); + if (gsl_linalg_LU_invert(tmp, p, (gsl_matrix *)result) != GSL_SUCCESS) + { + gsl_matrix_free(result); + return NULL; + } + gsl_matrix_free(tmp); + } + else + { + gsl_matrix_complex *tmp = gsl_matrix_complex_alloc(size, size); + result = gsl_matrix_complex_alloc(size, size); + gsl_matrix_complex_memcpy(tmp, (gsl_matrix_complex *)m); + gsl_linalg_complex_LU_decomp(tmp, p, &sign); + if (gsl_linalg_complex_LU_invert(tmp, p, (gsl_matrix_complex *)result) != GSL_SUCCESS) + { + gsl_matrix_complex_free(result); + return NULL; + } + gsl_matrix_complex_free(tmp); + } + + gsl_permutation_free(p); + return result; +} + +static void matrix_add_identity(gsl_matrix *m, double f) +{ + gsl_matrix *id = gsl_matrix_alloc(m->size1, m->size2); + gsl_matrix_set_identity(id); + gsl_matrix_scale(id, f); + gsl_matrix_add(m, id); + gsl_matrix_free(id); +} + +static void matrix_complex_add_identity(gsl_matrix_complex *m, gsl_complex c) +{ + gsl_matrix_complex *id = gsl_matrix_complex_alloc(m->size1, m->size2); + gsl_matrix_complex_set_identity(id); + gsl_matrix_complex_scale(id, c); + gsl_matrix_complex_add(m, id); + gsl_matrix_complex_free(id); +} + + + +//---- Arithmetic operators ------------------------------------------------- + +#define IMPLEMENT_OP(_name) \ +static CMATRIX *_name(CMATRIX *a, CMATRIX *b, bool invert) \ +{ \ + CMATRIX *m; \ + \ + if (COMPLEX(a) || COMPLEX(b)) \ + { \ + MATRIX_ensure_complex(a); \ + MATRIX_ensure_complex(b); \ + m = MAKE_MATRIX(a); \ + CFUNC(CMAT(m), CMAT(a), CMAT(b)); \ + } \ + else \ + { \ + m = MAKE_MATRIX(a); \ + FUNC(MAT(m), MAT(a), MAT(b)); \ + } \ + \ + return m; \ +} + +#define IMPLEMENT_OP_FLOAT(_name) \ +static CMATRIX *_name(CMATRIX *a, double f, bool invert) \ +{ \ + CMATRIX *m = MAKE_MATRIX(a); \ + \ + if (COMPLEX(a)) \ + { \ + CFUNC(CMAT(m), CMAT(a), f); \ + } \ + else \ + { \ + FUNC(MAT(m), MAT(a), f); \ + } \ + \ + return m; \ +} + +#define IMPLEMENT_OP_OTHER(_name) \ +static CMATRIX *_name(CMATRIX *a, void *b, bool invert) \ +{ \ + CMATRIX *m = MAKE_MATRIX(a); \ + \ + if (GB.Is(b, CLASS_Complex)) \ + { \ + MATRIX_ensure_complex(m); \ + CFUNC(CMAT(m), CMAT(a), ((CCOMPLEX *)b)->number); \ + return m; \ + } \ + else \ + return NULL; \ +} + +#define MAKE_MATRIX(_a) MATRIX_make(_a) + +#define FUNC(_m, _a, _b) gsl_matrix_add(_m, _b) +#define CFUNC(_m, _a, _b) gsl_matrix_complex_add(_m, _b) +IMPLEMENT_OP(_add) +#undef FUNC +#undef CFUNC + +#define FUNC(_m, _a, _f) matrix_add_identity(_m, _f) +#define CFUNC(_m, _a, _f) matrix_complex_add_identity(_m, gsl_complex_rect(_f, 0)) +IMPLEMENT_OP_FLOAT(_addf) +#undef FUNC +#undef CFUNC + +#define CFUNC(_m, _a, _c) matrix_complex_add_identity(_m, _c) +IMPLEMENT_OP_OTHER(_addo) +#undef CFUNC + +#define FUNC(_m, _a, _b) gsl_matrix_sub(_m, _b) +#define CFUNC(_m, _a, _b) gsl_matrix_complex_sub(_m, _b) +IMPLEMENT_OP(_sub) +#undef FUNC +#undef CFUNC + +#define FUNC(_m, _a, _f) \ + if (invert) \ + { \ + matrix_negative(_m, FALSE); \ + matrix_add_identity(_m, _f); \ + } \ + else \ + matrix_add_identity(_m, -(_f)); + +#define CFUNC(_m, _a, _f) \ + if (invert) \ + { \ + matrix_negative(_m, TRUE); \ + matrix_complex_add_identity(_m, gsl_complex_rect(_f, 0)); \ + } \ + else \ + matrix_complex_add_identity(_m, gsl_complex_rect(-(_f), 0)); + +IMPLEMENT_OP_FLOAT(_subf) +#undef FUNC +#undef CFUNC + +#define CFUNC(_m, _a, _c) \ + if (invert) \ + matrix_negative(_m, TRUE); \ + else \ + gsl_complex_negative(_c); \ + matrix_complex_add_identity(_m, _c); + +IMPLEMENT_OP_OTHER(_subo) +#undef CFUNC + +#define FUNC(_m, _a, _f) gsl_matrix_scale(_m, _f) +#define CFUNC(_m, _a, _f) gsl_matrix_complex_scale(_m, gsl_complex_rect(_f, 0)) +IMPLEMENT_OP_FLOAT(_mulf) +#undef FUNC +#undef CFUNC + +#define CFUNC(_m, _a, _c) gsl_matrix_complex_scale(_m, _c) +IMPLEMENT_OP_OTHER(_mulo) +#undef CFUNC + +#undef MAKE_MATRIX +#define MAKE_MATRIX(_a) MATRIX_copy(_a) + +#define FUNC(_m, _a, _b) gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, _a, _b, 0.0, _m); +#define CFUNC(_m, _a, _b) gsl_blas_zgemm(CblasNoTrans, CblasNoTrans, COMPLEX_one, _a, _b, COMPLEX_zero, _m); +IMPLEMENT_OP(_mul) +#undef FUNC +#undef CFUNC + +#define FUNC(_m, _a, _b) \ +{ \ + gsl_matrix *inv = matrix_invert(_b, FALSE); \ + if (!inv) \ + return NULL; \ + gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, _a, inv, 0.0, _m); \ + gsl_matrix_free(inv); \ +} + +#define CFUNC(_m, _a, _b) \ +{ \ + gsl_matrix_complex *inv = matrix_invert(_b, TRUE); \ + if (!inv) \ + { \ + GB.Error(GB_ERR_ZERO); \ + return NULL; \ + } \ + gsl_blas_zgemm(CblasNoTrans, CblasNoTrans, COMPLEX_one, _a, inv, COMPLEX_zero, _m); \ + gsl_matrix_complex_free(inv); \ +} + +IMPLEMENT_OP(_div) +#undef FUNC +#undef CFUNC + +static CMATRIX *_divf(CMATRIX *a, double f, bool invert) +{ + bool complex = COMPLEX(a); + CMATRIX *m; + + if (invert) + { + void *inv = matrix_invert(MAT(a), complex); + + if (!inv) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + m = MATRIX_create_from(inv, complex); + } + else + { + if (f == 0.0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + f = 1 / f; + m = MATRIX_make(a); + } + + if (complex) + gsl_matrix_complex_scale(CMAT(m), gsl_complex_rect(f, 0)); + else + gsl_matrix_scale(MAT(m), f); + + return m; +} + +static CMATRIX *_divo(CMATRIX *a, void *b, bool invert) +{ + bool complex = COMPLEX(a); + CMATRIX *m; + gsl_complex c; + + if (!GB.Is(b, CLASS_Complex)) + return NULL; + + c = ((CCOMPLEX *)b)->number; + + if (invert) + { + void *inv = matrix_invert(MAT(a), complex); + + if (!inv) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + m = MATRIX_create_from(inv, complex); + } + else + { + if (GSL_REAL(c) == 0 && GSL_IMAG(c) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + c = gsl_complex_inverse(c); + m = MATRIX_make(a); + } + + MATRIX_ensure_complex(m); + gsl_matrix_complex_scale(CMAT(m), c); + + return m; +} + +static int _equal(CMATRIX *a, CMATRIX *b) +{ + if (WIDTH(a) != WIDTH(b) || HEIGHT(a) != HEIGHT(b)) + return FALSE; + + if (COMPLEX(a) || COMPLEX(b)) + { + MATRIX_ensure_complex(a); + MATRIX_ensure_complex(b); + return gsl_matrix_complex_equal(CMAT(a), CMAT(b)); + } + else + return gsl_matrix_equal(MAT(a), MAT(b)); +} + +static int _equalf(CMATRIX *a, double f) +{ + bool result; + + if (COMPLEX(a)) + { + if (f == 0.0) + return gsl_matrix_complex_isnull(CMAT(a)); + + gsl_matrix_complex *m = gsl_matrix_complex_alloc(WIDTH(a), HEIGHT(a)); + gsl_matrix_complex_set_identity(m); + gsl_matrix_complex_scale(m, gsl_complex_rect(f, 0)); + result = gsl_matrix_complex_equal(CMAT(a), m); + gsl_matrix_complex_free(m); + } + else + { + if (f == 0.0) + return gsl_matrix_isnull(MAT(a)); + + gsl_matrix *m = gsl_matrix_alloc(WIDTH(a), HEIGHT(a)); + gsl_matrix_set_identity(m); + gsl_matrix_scale(m, f); + result = gsl_matrix_equal(MAT(a), m); + gsl_matrix_free(m); + } + + return result; +} + +static int _equalo(CMATRIX *a, void *b) +{ + bool result; + CCOMPLEX *c; + + if (!GB.Is(b, CLASS_Complex)) + return -1; + + c = (CCOMPLEX *)b; + + if (GSL_IMAG(c->number) == 0.0) + return _equalf(a, GSL_REAL(c->number)); + + if (!COMPLEX(a)) + return FALSE; + + gsl_matrix_complex *m = gsl_matrix_complex_alloc(WIDTH(a), HEIGHT(a)); + gsl_matrix_complex_set_identity(m); + gsl_matrix_complex_scale(m, c->number); + result = gsl_matrix_complex_equal(CMAT(a), m); + gsl_matrix_complex_free(m); + return result; +} + +static CMATRIX *_neg(CMATRIX *a) +{ + CMATRIX *m = MATRIX_make(a); + matrix_negative(m->matrix, m->complex); + return m; +} + +static CMATRIX *_powi(CMATRIX *m, int n) +{ + if (n == 1) + return m; + + CMATRIX *m2 = _mul(m, m, FALSE); + CMATRIX *r; + + if ((n & 1) == 0) + { + n /= 2; + if (n > 1) + r = _powi(m2, n); + else + r = m2; + } + else + { + n /= 2; + if (n > 1) + r = _powi(m2, n); + else + r = m2; + + m2 = _mul(r, m, FALSE); + GB.Unref(POINTER(&r)); + r = m2; + } + + GB.Unref(POINTER(&m)); + return r; +} + +static CMATRIX *_powf(CMATRIX *a, double f, bool invert) +{ + if (invert || f != (double)(int)f) + return NULL; + + CMATRIX *m = NULL; + int n = (int)f; + + if (n == 0) + { + m = MATRIX_make(a); + if (COMPLEX(m)) + gsl_matrix_complex_set_identity(CMAT(m)); + else + gsl_matrix_set_identity(MAT(m)); + } + else if (n == 1) + { + m = a; + } + else if (n > 1) + { + m = _powi(MATRIX_copy(a), n); + } + else if (n < 0) + { + void *inv = matrix_invert(a->matrix, COMPLEX(a)); + if (inv == NULL) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + m = _powi(MATRIX_create_from(inv, COMPLEX(a)), (-n)); + } + + return m; +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .equalo = (void *)_equalo, + .add = (void *)_add, + .addf = (void *)_addf, + .addo = (void *)_addo, + .sub = (void *)_sub, + .subf = (void *)_subf, + .subo = (void *)_subo, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .mulo = (void *)_mulo, + .div = (void *)_div, + .divf = (void *)_divf, + .divo = (void *)_divo, + .powf = (void *)_powf, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +static char *_to_string(CMATRIX *_object, bool local) +{ + char *result = NULL; + int i, j; + int w = WIDTH(THIS); + int h = HEIGHT(THIS); + char *str; + int len; + + result = GB.AddChar(result, '['); + + for (i = 0; i < h; i++) + { + if (i) + { + if (!local) + result = GB.AddChar(result, ','); + } + + result = GB.AddChar(result, '['); + + for (j = 0; j < w; j++) + { + if (j) + result = GB.AddChar(result, local ? ' ' : ','); + + if (!COMPLEX(THIS)) + { + GB.NumberToString(local, gsl_matrix_get(MAT(THIS), i, j), NULL, &str, &len); + result = GB.AddString(result, str, len); + } + else + { + str = COMPLEX_to_string(gsl_matrix_complex_get(CMAT(THIS), i, j), local); + result = GB.AddString(result, str, GB.StringLength(str)); + GB.FreeString(&str); + } + } + + result = GB.AddChar(result, ']'); + } + + result = GB.AddChar(result, ']'); + + return result; +} + +static bool _convert(CMATRIX *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (THIS) + { + if (!COMPLEX(THIS)) + { + switch (type) + { + /*case GB_T_FLOAT: + conv->_float.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE;*/ + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + switch (type) + { + /*case GB_T_FLOAT: + conv->_float.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE;*/ + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + } + else if (type >= GB_T_OBJECT) + { + /*if (type == CLASS_Complex) + { + CCOMPLEX *c = (CCOMPLEX *)conv->_object.value; + CMATRIX *m = MATRIX_create(2, 2, FALSE, FALSE); + + gsl_matrix_set(MAT(m), 0, 0, GSL_REAL(c->number)); + gsl_matrix_set(MAT(m), 1, 1, GSL_REAL(c->number)); + gsl_matrix_set(MAT(m), 0, 1, -GSL_IMAG(c->number)); + gsl_matrix_set(MAT(m), 1, 0, GSL_IMAG(c->number)); + + conv->_object.value = m; + return FALSE; + } + else*/ if (GB.Is(conv->_object.value, CLASS_Array)) + { + GB_ARRAY array = (GB_ARRAY)conv->_object.value; + GB_ARRAY array2; + int height = GB.Array.Count(array); + int width = 0; + GB_TYPE atype = GB.Array.Type(array); + GB_TYPE atype2; + int i, j, w; + GB_VALUE temp; + void *data; + CMATRIX *m; + CCOMPLEX *c; + bool complex; + + if (atype >= GB_T_OBJECT) + { + complex = FALSE; + + for (i = 0; i < height; i++) + { + data = GB.Array.Get(array, i); + array2 = *((void **)data); + if (!array2 || !GB.Is(array2, CLASS_Array)) + return TRUE; + + w = GB.Array.Count(array2); + if (w > width) + width = w; + + atype2 = GB.Array.Type(array2); + + if (atype2 == GB_T_VARIANT || atype2 == CLASS_Complex) + complex = TRUE; + else if (!(atype2 > GB_T_BOOLEAN && atype2 <= GB_T_FLOAT)) + return TRUE; + } + + //fprintf(stderr, "create: %d %d %d\n", width, height, complex); + m = MATRIX_create(width, height, complex, TRUE); + + for (i = 0; i < height; i++) + { + array2 = *((void **)GB.Array.Get(array, i)); + atype2 = GB.Array.Type(array2); + w = GB.Array.Count(array2); + + if (atype2 > GB_T_BOOLEAN && atype2 <= GB_T_FLOAT) + { + for (j = 0; j < w; j++) + { + data = GB.Array.Get(array2, j); + GB.ReadValue(&temp, data, atype2); + GB.Conv(&temp, GB_T_FLOAT); + if (complex) + gsl_matrix_complex_set(CMAT(m), i, j, gsl_complex_rect(temp._float.value, 0)); + else + gsl_matrix_set(MAT(m), i, j, temp._float.value); + } + } + else if (atype2 == GB_T_VARIANT) + { + for (j = 0; j < w; j++) + { + GB.ReadValue(&temp, GB.Array.Get(array2, j), atype2); + GB.BorrowValue(&temp); + GB.Conv(&temp, CLASS_Complex); + c = temp._object.value; + if (c) + gsl_matrix_complex_set(CMAT(m), i, j, c->number); + else + gsl_matrix_complex_set(CMAT(m), i, j, COMPLEX_zero); + GB.ReleaseValue(&temp); + } + } + else if (atype2 == CLASS_Complex) + { + for (j = 0; j < w; j++) + { + c = *((CCOMPLEX **)GB.Array.Get(array2, j)); + if (c) + gsl_matrix_complex_set(CMAT(m), i, j, c->number); + else + gsl_matrix_complex_set(CMAT(m), i, j, COMPLEX_zero); + } + } + } + + conv->_object.value = m; + return FALSE; + } + } + } + /*else if (type > GB_T_BOOLEAN && type <= GB_T_FLOAT) + { + CMATRIX *m = MATRIX_create(2, 2, FALSE, TRUE); + double value; + + if (type == GB_T_FLOAT) + value = conv->_float.value; + else if (type == GB_T_SINGLE) + value = conv->_single.value; + else + value = conv->_integer.value; + + gsl_matrix_set(MAT(m), 0, 0, value); + gsl_matrix_set(MAT(m), 1, 1, value); + + conv->_object.value = m; + return FALSE; + }*/ + + return TRUE; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Matrix_new, GB_INTEGER height; GB_INTEGER width; GB_BOOLEAN complex) + + bool complex = VARGOPT(complex, FALSE); + int h = VARGOPT(height, 2); + int w = VARGOPT(width, 2); + + if (h < 1) h = 1; + if (w < 1) w = 1; + + THIS->complex = complex; + + if (!complex) + THIS->matrix = gsl_matrix_calloc(h, w); + else + THIS->matrix = gsl_matrix_complex_calloc(h, w); + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_free) + + if (!COMPLEX(THIS)) + gsl_matrix_free(MAT(THIS)); + else + gsl_matrix_complex_free(CMAT(THIS)); + +END_METHOD + + +BEGIN_PROPERTY(Matrix_Width) + + GB.ReturnInteger(WIDTH(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Matrix_Height) + + GB.ReturnInteger(HEIGHT(THIS)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Matrix_Copy) + + GB.ReturnObject(MATRIX_copy(THIS)); + +END_METHOD + + +BEGIN_METHOD(Matrix_get, GB_INTEGER i; GB_INTEGER j) + + int w = WIDTH(THIS), h = HEIGHT(THIS); + int i = VARG(i), j = VARG(j); + + if (i < 0 || i >= h || j < 0 || j >= w) + { + GB.Error(GB_ERR_BOUND); + return; + } + + if (!COMPLEX(THIS)) + GB.ReturnFloat(gsl_matrix_get(MAT(THIS), i, j)); + else + GB.ReturnObject(COMPLEX_create(gsl_matrix_complex_get(CMAT(THIS), i, j))); + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Matrix_put, GB_VARIANT value; GB_INTEGER i; GB_INTEGER j) + + int w = WIDTH(THIS), h = HEIGHT(THIS); + int i = VARG(i), j = VARG(j); + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + if (i < 0 || i >= h || j < 0 || j >= w) + { + GB.Error(GB_ERR_BOUND); + return; + } + + type = COMPLEX_get_value(value, &cv); + + if (type == CGV_ERR) + return; + + if (type == CGV_COMPLEX) + { + MATRIX_ensure_complex(THIS); + gsl_matrix_complex_set(CMAT(THIS), i, j, cv.z); + } + else + { + if (COMPLEX(THIS)) + gsl_matrix_complex_set(CMAT(THIS), i, j, cv.z); + else + gsl_matrix_set(MAT(THIS), i, j, cv.x); + } + +END_METHOD + + +BEGIN_PROPERTY(Matrix_Handle) + + GB.ReturnPointer(THIS->matrix); + +END_PROPERTY + + +BEGIN_METHOD(Matrix_Identity, GB_INTEGER width; GB_INTEGER height; GB_BOOLEAN complex) + + GB.ReturnObject(MATRIX_identity(VARGOPT(width, 2), VARGOPT(height, 2), VARGOPT(complex, FALSE))); + +END_METHOD + + +BEGIN_METHOD(Matrix_Row, GB_INTEGER row) + + int row = VARG(row); + + if (row < 0 || row >= HEIGHT(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + bool complex = COMPLEX(THIS); + CVECTOR *v = VECTOR_create(WIDTH(THIS), complex, FALSE); + + if (complex) + gsl_matrix_complex_get_row(CVEC(v), CMAT(THIS), row); + else + gsl_matrix_get_row(VEC(v), MAT(THIS), row); + + GB.ReturnObject(v); + +END_METHOD + +BEGIN_METHOD(Matrix_Column, GB_INTEGER column) + + int column = VARG(column); + + if (column < 0 || column >= WIDTH(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + bool complex = COMPLEX(THIS); + CVECTOR *v = VECTOR_create(HEIGHT(THIS), complex, FALSE); + + if (complex) + gsl_matrix_complex_get_col(CVEC(v), CMAT(THIS), column); + else + gsl_matrix_get_col(VEC(v), MAT(THIS), column); + + GB.ReturnObject(v); + +END_METHOD + +BEGIN_METHOD(Matrix_SetRow, GB_INTEGER row; GB_OBJECT vector) + + int row = VARG(row); + + if (row < 0 || row >= HEIGHT(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + CVECTOR *v = (CVECTOR *)VARG(vector); + + if (GB.CheckObject(v)) + return; + + if (SIZE(v) != WIDTH(THIS)) + { + GB.Error("Vector size does not match matrix width"); + return; + } + + bool complex = COMPLEX(THIS); + + if (complex) + { + VECTOR_ensure_complex(v); + gsl_matrix_complex_set_row(CMAT(THIS), row, CVEC(v)); + } + else + { + if (VECTOR_ensure_not_complex(v)) + { + GB.Error(GB_ERR_TYPE, "Float", "Complex"); + return; + } + gsl_matrix_set_row(MAT(THIS), row, VEC(v)); + } + +END_METHOD + +BEGIN_METHOD(Matrix_SetColumn, GB_INTEGER column; GB_OBJECT vector) + + int column = VARG(column); + + if (column < 0 || column >= WIDTH(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + CVECTOR *v = (CVECTOR *)VARG(vector); + + if (GB.CheckObject(v)) + return; + + if (SIZE(v) != HEIGHT(THIS)) + { + GB.Error("Vector size does not match matrix height"); + return; + } + + bool complex = COMPLEX(THIS); + + if (complex) + { + VECTOR_ensure_complex(v); + gsl_matrix_complex_set_col(CMAT(THIS), column, CVEC(v)); + } + else + { + if (VECTOR_ensure_not_complex(v)) + { + GB.Error(GB_ERR_TYPE, "Float", "Complex"); + return; + } + gsl_matrix_set_col(MAT(THIS), column, VEC(v)); + } + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_Determinant) + + COMPLEX_VALUE cv; + + if (matrix_determinant(THIS, &cv)) + { + GB.Error("Matrix is not square"); + return; + } + + if (COMPLEX(THIS)) + GB.ReturnObject(COMPLEX_create(cv.z)); + else + GB.ReturnFloat(cv.x); + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Matrix_call, GB_OBJECT vector) + + CVECTOR *v = VARG(vector); + CVECTOR *result; + + if (GB.CheckObject(v)) + return; + + if (COMPLEX(THIS) || v->complex) + { + MATRIX_ensure_complex(THIS); + VECTOR_ensure_complex(v); + result = VECTOR_create(SIZE(v), TRUE, FALSE); + gsl_blas_zgemv(CblasNoTrans, COMPLEX_one, CMAT(THIS), CVEC(v), COMPLEX_zero, CVEC(result)); + GB.ReturnObject(result); + } + else + { + result = VECTOR_create(SIZE(v), FALSE, FALSE); + gsl_blas_dgemv(CblasNoTrans, 1.0, MAT(THIS), VEC(v), 0.0, VEC(result)); + GB.ReturnObject(result); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_Transpose) + + if (!COMPLEX(THIS)) + { + gsl_matrix *m = gsl_matrix_alloc(WIDTH(THIS), HEIGHT(THIS)); + gsl_matrix_transpose_memcpy(m, MAT(THIS)); + GB.ReturnObject(MATRIX_create_from(m, FALSE)); + } + else + { + gsl_matrix_complex *m = gsl_matrix_complex_alloc(WIDTH(THIS), HEIGHT(THIS)); + gsl_matrix_complex_transpose_memcpy(m, CMAT(THIS)); + GB.ReturnObject(MATRIX_create_from(m, TRUE)); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_Conjugate) + + CMATRIX *m = MATRIX_copy(THIS); + + if (COMPLEX(THIS)) + { + int i, j; + + for (i = 0; i < HEIGHT(m); i++) + for (j = 0; j < WIDTH(m); j++) + gsl_matrix_complex_set(CMAT(m), i, j, gsl_complex_conjugate(gsl_matrix_complex_get(CMAT(m), i, j))); + } + + GB.ReturnObject(m); + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_Invert) + + void *m = matrix_invert(THIS->matrix, COMPLEX(THIS)); + + if (!m) + GB.ReturnNull(); + else + GB.ReturnObject(MATRIX_create_from(m, COMPLEX(THIS))); + +END_METHOD + + +BEGIN_METHOD(Matrix_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(_to_string(THIS, VARGOPT(local, FALSE)))); + +END_METHOD + + +//--------------------------------------------------------------------------- + +GB_DESC MatrixDesc[] = +{ + GB_DECLARE("Matrix", sizeof(CMATRIX)), + + GB_METHOD("_new", NULL, Matrix_new, "[(Width)i(Height)i(Complex)b]"), + GB_METHOD("_free", NULL, Matrix_free, NULL), + //GB_STATIC_METHOD("_call", "Vector", Matrix_call, "(Value)f."), + GB_METHOD("Copy", "Matrix", Matrix_Copy, NULL), + GB_METHOD("ToString", "s", Matrix_ToString, "[(Local)b]"), + + GB_STATIC_METHOD("Identity", "Matrix", Matrix_Identity, "[(Width)i(Height)i(Complex)b]"), + + GB_PROPERTY_READ("Width", "i", Matrix_Width), + GB_PROPERTY_READ("Height", "i", Matrix_Height), + GB_PROPERTY_READ("Handle", "p", Matrix_Handle), + + GB_METHOD("Det", "v", Matrix_Determinant, NULL), + + GB_METHOD("_get", "v", Matrix_get, "(I)i(J)i"), + GB_METHOD("_put", NULL, Matrix_put, "(Value)v(I)i(J)i"), + + GB_METHOD("_call", "Vector", Matrix_call, "(Vector)Vector"), + + //GB_METHOD("Equal", "b", Matrix_Equal, "(Matrix)Matrix;"), + + GB_METHOD("Row", "Vector", Matrix_Row, "(Row)i"), + GB_METHOD("Column", "Vector", Matrix_Column, "(Column)i"), + GB_METHOD("SetRow", NULL, Matrix_SetRow, "(Row)i(Vector)Vector;"), + GB_METHOD("SetColumn", NULL, Matrix_SetColumn, "(Column)i(Vector)Vector;"), + + //GB_METHOD("Scale", "Matrix", Matrix_Scale, "(Value)v"), + GB_METHOD("Trans", "Matrix", Matrix_Transpose, NULL), + GB_METHOD("Conj", "Matrix", Matrix_Conjugate, NULL), + GB_METHOD("Inv", "Matrix", Matrix_Invert, NULL), + + GB_INTERFACE("_convert", &_convert), + GB_INTERFACE("_operator", &_operator), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_matrix.h b/gb.gsl/src/c_matrix.h new file mode 100644 index 00000000..d66b8938 --- /dev/null +++ b/gb.gsl/src/c_matrix.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + c_matrix.h + + gb.gsl component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MATRIX_H +#define __C_MATRIX_H + +#include "main.h" + +#ifndef __C_MATRIX_C +extern GB_DESC MatrixDesc[]; +#endif + +typedef + struct + { + GB_BASE ob; + void *matrix; + bool complex; + } + CMATRIX; + +#endif /* __C_MATRIX_H */ diff --git a/gb.gsl/src/c_newtonpolynomial.c b/gb.gsl/src/c_newtonpolynomial.c new file mode 100644 index 00000000..fc8ceed8 --- /dev/null +++ b/gb.gsl/src/c_newtonpolynomial.c @@ -0,0 +1,53 @@ +/*************************************************************************** + + c_newtonpolynomial.c + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ +/*========================================================================= + === NOTE THIS IS SIMPLE LEARNING CODE === + AND NOTHING USEFUL EXISTS HERE YET +==========================================================================*/ + +#define __C_GSL_NEWTONPOLYNOMIAL_C + +#include "c_newtonpolynomial.h" + + +#define THIS ((CNEWTONPOLYNOMIAL *)_object) + +static CNEWTONPOLYNOMIAL *create_newtonplynomial() +{ + return (CNEWTONPOLYNOMIAL *)GB.New(GB.FindClass("NewtonPolynomial"), NULL, NULL); +} + + + +/************************************************** + Describe Class properties and methods to Gambas +**************************************************/ +GB_DESC CNetwonPolynomial[] = +{ + GB_DECLARE("NewtonPolynomial", sizeof(CNEWTONPOLYNOMIAL)), + + GB_END_DECLARE +}; + diff --git a/gb.gsl/src/c_newtonpolynomial.h b/gb.gsl/src/c_newtonpolynomial.h new file mode 100644 index 00000000..cfc4055f --- /dev/null +++ b/gb.gsl/src/c_newtonpolynomial.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_newtonpolynomial.h + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_GSL_NEWTONPOLYNOMIAL_H +#define __C_GSL_NEWTONPOLYNOMIAL_H + +#include "gambas.h" +#include <gsl/gsl_poly.h> +#include <stdio.h> + + +GB_INTERFACE GB EXPORT; + +extern GB_DESC CNewtonPolynomialDesc[]; + +typedef + struct { + GB_BASE ob; + double *dd; + double *xa; + int len; + } + CNEWTONPOLYNOMIAL; + + +#endif /* __C_GSL_NEWTONPOLYNOMIAL_H */ diff --git a/gb.gsl/src/c_polynomial.c b/gb.gsl/src/c_polynomial.c new file mode 100644 index 00000000..78379788 --- /dev/null +++ b/gb.gsl/src/c_polynomial.c @@ -0,0 +1,914 @@ +/*************************************************************************** + + c_polynomial.c + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_POLYNOMIAL_C + +#include "c_complex.h" +#include "c_vector.h" +#include "c_polynomial.h" + +#define THIS ((CPOLYNOMIAL *)_object) +#define DATA(_p) ((double *)(_p)->data) +#define CDATA(_p) ((gsl_complex *)(_p)->data) +#define COUNT(_p) ((_p)->size) +#define COMPLEX(_p) ((_p)->complex) + +//---- Utility methods ------------------------------------------------------ + +static CPOLYNOMIAL *POLYNOMIAL_create(int size, bool complex) +{ + CPOLYNOMIAL *p = (CPOLYNOMIAL *)GB.Create(CLASS_Polynomial, NULL, NULL); + + GB.NewArray(POINTER(&p->data), complex ? sizeof(gsl_complex) : sizeof(double), size); + + p->size = size; + p->complex = complex; + + return p; +} + +static CPOLYNOMIAL *POLYNOMIAL_copy(CPOLYNOMIAL *_object) +{ + CPOLYNOMIAL *p = POLYNOMIAL_create(COUNT(THIS), COMPLEX(THIS)); + memcpy(p->data, THIS->data, THIS->size * (COMPLEX(THIS) ? sizeof(gsl_complex) : sizeof(double))); + return p; +} + +#define POLYNOMIAL_make(_a) (((_a)->ob.ref <= 1) ? (_a) : POLYNOMIAL_copy(_a)) + +static int get_degree(CPOLYNOMIAL *_object) +{ + int i; + + if (COMPLEX(THIS)) + { + gsl_complex *d = CDATA(THIS); + + for (i = COUNT(THIS) - 1; i >= 0; i--) + { + if (GSL_REAL(d[i]) != 0.0 || GSL_IMAG(d[i]) != 0.0) + return i; + } + } + else + { + double *d = DATA(THIS); + + for (i = COUNT(THIS) - 1; i >= 0; i--) + { + if (d[i] != 0.0) + return i; + } + } + + return 0; +} + +static void ensure_size(CPOLYNOMIAL *_object, int size) +{ + if (size > COUNT(THIS)) + { + GB.Insert(POINTER(&THIS->data), -1, size - COUNT(THIS)); + THIS->size = size; + } +} + +static CPOLYNOMIAL *POLYNOMIAL_make_size(CPOLYNOMIAL *a, int min_size) +{ + if (a->size >= min_size) + return POLYNOMIAL_make(a); + + a = POLYNOMIAL_copy(a); + ensure_size(a, min_size); + return a; +} + +static void ensure_complex(CPOLYNOMIAL *_object) +{ + gsl_complex *d; + int size, i; + + if (COMPLEX(THIS)) + return; + + if (THIS->data) + { + size = COUNT(THIS); + GB.NewArray(POINTER(&d), sizeof(gsl_complex), size); + for (i = 0; i < size; i++) + d[i].dat[0] = DATA(THIS)[i]; + GB.FreeArray(POINTER(&THIS->data)); + THIS->data = d; + } + + THIS->complex = TRUE; +} + +static bool ensure_not_complex(CPOLYNOMIAL *_object) +{ + gsl_complex *cd; + double *d; + int size, i; + + if (!COMPLEX(THIS)) + return FALSE; + + if (THIS->data) + { + size = COUNT(THIS); + cd = CDATA(THIS); + + for (i = 0; i < size; i++) + { + if (GSL_IMAG(cd[i]) != 0.0) + return TRUE; + } + + GB.NewArray(POINTER(&d), sizeof(double), size); + + for (i = 0; i < size; i++) + d[i] = GSL_REAL(cd[i]); + + GB.FreeArray(POINTER(&THIS->data)); + THIS->data = d; + } + + THIS->complex = FALSE; + return FALSE; +} + +static void poly_negative(CPOLYNOMIAL *_object) +{ + int i; + + if (COMPLEX(THIS)) + { + for (i = 0; i < COUNT(THIS); i++) + DATA(THIS)[i] = (- DATA(THIS)[i]); + } + else + { + for (i = 0; i < COUNT(THIS); i++) + CDATA(THIS)[i] = gsl_complex_negative(CDATA(THIS)[i]); + } +} + +//---- Arithmetic operators ------------------------------------------------- + +static CPOLYNOMIAL *_addf(CPOLYNOMIAL *a, double f, bool invert) +{ + CPOLYNOMIAL *p = POLYNOMIAL_make(a); + + DATA(p)[0] += f; + return p; +} + +static CPOLYNOMIAL *_add(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert) +{ + int da = get_degree(a); + int db = get_degree(b); + int d = Max(da, db); + int dm = Min(d, db); + int i; + + CPOLYNOMIAL *p = POLYNOMIAL_make_size(a, d + 1); + + if (COMPLEX(a) || COMPLEX(b)) + { + ensure_complex(p); + ensure_complex(b); + + for (i = 0; i <= dm; i++) + CDATA(p)[i] = gsl_complex_add(CDATA(p)[i], CDATA(b)[i]); + } + else + { + for (i = 0; i <= dm; i++) + DATA(p)[i] += DATA(b)[i]; + } + + return p; +} + +static CPOLYNOMIAL *op_array(CPOLYNOMIAL *a, void *b, bool invert, CPOLYNOMIAL *(*func)(CPOLYNOMIAL *, CPOLYNOMIAL *, bool)) +{ + GB_VALUE conv; + bool err; + CPOLYNOMIAL *p; + + conv._object.type = (GB_TYPE)GB.GetClass(b); + conv._object.value = b; + + GB.Ref(b); + err = GB.Conv(&conv, CLASS_Polynomial); + + if (!err) + { + if (invert) + { + GB.Ref(conv._object.value); + p = (*func)(conv._object.value, a, FALSE); + GB.Unref(&conv._object.value); + } + else + { + p = (*func)(a, conv._object.value, FALSE); + } + + GB.Unref(&conv._object.value); + + return p; + } + else + return NULL; +} + +static CPOLYNOMIAL *_addo(CPOLYNOMIAL *a, void *b, bool invert) +{ + CPOLYNOMIAL *p; + if (GB.Is(b, CLASS_Complex)) + { + p = POLYNOMIAL_make(a); + + ensure_complex(p); + CDATA(p)[0] = gsl_complex_add(CDATA(p)[0], ((CCOMPLEX *)b)->number); + return p; + } + else if (GB.Is(b, CLASS_Array)) + { + return op_array(a, b, invert, _add); + } + + return NULL; +} + +static CPOLYNOMIAL *_subf(CPOLYNOMIAL *a, double f, bool invert) +{ + CPOLYNOMIAL *p = POLYNOMIAL_make(a); + + if (invert) + poly_negative(p); + else + f = -f; + + DATA(p)[0] += f; + return p; +} + +static CPOLYNOMIAL *_sub(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert) +{ + int da = get_degree(a); + int db = get_degree(b); + int d = Max(da, db); + int dm = Min(d, db); + int i; + + CPOLYNOMIAL *p = POLYNOMIAL_make_size(a, d + 1); + + if (COMPLEX(a) || COMPLEX(b)) + { + ensure_complex(p); + ensure_complex(b); + + for (i = 0; i <= dm; i++) + CDATA(p)[i] = gsl_complex_sub(CDATA(p)[i], CDATA(b)[i]); + } + else + { + for (i = 0; i <= dm; i++) + DATA(p)[i] -= DATA(b)[i]; + } + + return p; +} + +static CPOLYNOMIAL *_subo(CPOLYNOMIAL *a, void *b, bool invert) +{ + CPOLYNOMIAL *p; + + if (GB.Is(b, CLASS_Complex)) + { + p = POLYNOMIAL_make(a); + + if (invert) + { + poly_negative(p); + ensure_complex(p); + CDATA(p)[0] = gsl_complex_add(CDATA(p)[0], ((CCOMPLEX *)b)->number); + } + else + { + ensure_complex(p); + CDATA(p)[0] = gsl_complex_sub(CDATA(p)[0], ((CCOMPLEX *)b)->number); + } + + return p; + } + else if (GB.Is(b, CLASS_Array)) + { + return op_array(a, b, invert, _sub); + } + + return NULL; +} + +static CPOLYNOMIAL *_neg(CPOLYNOMIAL *a) +{ + CPOLYNOMIAL *p = POLYNOMIAL_make(a); + poly_negative(p); + return p; +} + +static int _equal(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert) +{ + int da = get_degree(a); + int db = get_degree(b); + int i; + + if (da != db) + return FALSE; + + if (COMPLEX(a) || COMPLEX(b)) + { + ensure_complex(a); + ensure_complex(b); + + for (i = 0; i <= da; i++) + if (GSL_REAL(CDATA(a)[i]) != GSL_REAL(CDATA(b)[i]) || GSL_IMAG(CDATA(a)[i]) != GSL_IMAG(CDATA(b)[i])) + return FALSE; + } + else + { + for (i = 0; i <= da; i++) + if (DATA(a)[i] != DATA(b)[i]) + return FALSE; + } + + return TRUE; +} + + +static GB_OPERATOR_DESC _operator = +{ + .add = (void *)_add, + .addf = (void *)_addf, + .addo = (void *)_addo, + .sub = (void *)_sub, + .subf = (void *)_subf, + .subo = (void *)_subo, + /*.*mul = (void *)_mul, + .mulf = (void *)_mulf, + .div = (void *)_div, + .divf = (void *)_divf, + .idivf = (void *)_idivf,*/ + .equal = (void *)_equal, + /*.*equalf = (void *)_equalf, + .abs = (void *)_abs,*/ + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +char *POLYNOMIAL_to_string(CPOLYNOMIAL *p, bool local) +{ + int i; + int size = COUNT(p); + char *result = NULL; + char *str; + int len; + char buffer[16]; + double re, im; + bool add = FALSE; + bool complex = COMPLEX(p); + gsl_complex c; + bool par; + + i = size; + while (i > 0) + { + i--; + + if (complex) + { + c = CDATA(p)[i]; + re = GSL_REAL(c); + im = GSL_IMAG(c); + } + else + { + re = DATA(p)[i]; + im = 0.0; + } + + if (re == 0.0 && im == 0.0) + continue; + + par = i > 0 && re != 0.0 && im != 0.0; + + if (!add) + add = TRUE; + else if (re > 0.0 || (re == 0.0 && im > 0.0) || par) + result = GB.AddChar(result, '+'); + + //l = GB.StringLength(result); + + if (par) + result = GB.AddChar(result, '('); + + if (i == 0 || re != 1.0 || im != 0.0) + { + if (re != 0.0) + { + if (re == -1.0 && i > 0) + result = GB.AddChar(result, '-'); + else + { + GB.NumberToString(local, re, NULL, &str, &len); + result = GB.AddString(result, str, len); + } + } + if (im != 0.0) + { + if (re != 0.0 && im > 0.0) + result = GB.AddChar(result, '+'); + + if (local && im == -1.0) + result = GB.AddChar(result, '-'); + else if (!local || im != 1.0) + { + GB.NumberToString(local, im, NULL, &str, &len); + result = GB.AddString(result, str, len); + } + result = GB.AddChar(result, 'i'); + } + } + + if (par) + result = GB.AddChar(result, ')'); + + if (i > 0) + { + if (!local && ((im == 0 && re != 0 && re != 1 && re != -1) || (im != 0))) + result = GB.AddChar(result, '*'); + result = GB.AddChar(result, 'x'); + if (i > 1) + { + result = GB.AddChar(result, '^'); + len = sprintf(buffer, "%d", i); + result = GB.AddString(result, buffer, len); + } + } + } + + if (!result) + result = GB.NewString("0", 1); + + return result; +} + +bool POLYNOMIAL_convert(CPOLYNOMIAL *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = POLYNOMIAL_to_string(a, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + double coef; + + switch(type) + { + case GB_T_FLOAT: coef = conv->_float.value; break; + case GB_T_SINGLE: coef = conv->_single.value; break; + case GB_T_INTEGER: case GB_T_SHORT: case GB_T_BYTE: coef = conv->_integer.value; + return FALSE; + + default: + if (type >= GB_T_OBJECT) + { + if (GB.Is(conv->_object.value, CLASS_Array)) + { + CPOLYNOMIAL *p; + CCOMPLEX *c; + GB_ARRAY array = (GB_ARRAY)conv->_object.value; + int size = GB.Array.Count(array); + int i; + GB_VALUE temp; + void *data; + GB_TYPE atype = GB.Array.Type(array); + + if (atype > GB_T_BOOLEAN && atype <= GB_T_FLOAT) + { + p = POLYNOMIAL_create(size, FALSE); + + for (i = 0; i < size; i++) + { + data = GB.Array.Get(array, i); + GB.ReadValue(&temp, data, atype); + GB.Conv(&temp, GB_T_FLOAT); + DATA(p)[i] = temp._float.value; + } + + conv->_object.value = p; + return FALSE; + } + else if (atype == GB_T_VARIANT) + { + p = POLYNOMIAL_create(size, TRUE); + + for (i = 0; i < size; i++) + { + GB.ReadValue(&temp, GB.Array.Get(array, i), atype); + GB.BorrowValue(&temp); + GB.Conv(&temp, CLASS_Complex); + c = temp._object.value; + CDATA(p)[i] = c->number; + GB.ReleaseValue(&temp); + } + + conv->_object.value = p; + return FALSE; + } + else if (atype == CLASS_Complex) + { + p = POLYNOMIAL_create(size, TRUE); + + for (i = 0; i < size; i++) + { + c = *(CCOMPLEX **)GB.Array.Get(array, i); + if (c) + CDATA(p)[i] = c->number; + else + CDATA(p)[i] = COMPLEX_zero; + } + + conv->_object.value = p; + return FALSE; + } + } + } + + return TRUE; + } + + a = POLYNOMIAL_create(1, FALSE); + *DATA(a) = coef; + conv->_object.value = a; + return FALSE; + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Polynomial_new, GB_INTEGER size; GB_BOOLEAN complex) + + bool complex = VARGOPT(complex, FALSE); + int size = VARGOPT(size, 0); + + GB.NewArray(POINTER(&THIS->data), complex ? sizeof(gsl_complex) : sizeof(double), size); + + THIS->size = size; + THIS->complex = complex; + +END_METHOD + + +BEGIN_METHOD_VOID(Polynomial_free) + + GB.FreeArray(POINTER(&THIS->data)); + +END_METHOD + + +BEGIN_PROPERTY(Polynomial_Count) + + GB.ReturnInteger(COUNT(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Polynomial_Degree) + + GB.ReturnInteger(get_degree(THIS)); + +END_PROPERTY + + +BEGIN_METHOD(Polynomial_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(POLYNOMIAL_to_string(THIS, VARGOPT(local, FALSE)))); + +END_METHOD + + +BEGIN_METHOD(Polynomial_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= COUNT(THIS)) + { + if (COMPLEX(THIS)) + GB.ReturnObject(COMPLEX_create(COMPLEX_zero)); + else + GB.ReturnFloat(0.0); + } + else + { + if (COMPLEX(THIS)) + GB.ReturnObject(COMPLEX_create(CDATA(THIS)[index])); + else + GB.ReturnFloat(DATA(THIS)[index]); + } + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Polynomial_put, GB_VARIANT value; GB_INTEGER index) + + int index = VARG(index); + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + if (index < 0 || index > 65535) + { + GB.Error(GB_ERR_ARG); + return; + } + + type = COMPLEX_get_value(value, &cv); + + if (type == CGV_ERR) + return; + + ensure_size(THIS, index + 1); + + if (type == CGV_COMPLEX) + { + ensure_complex(THIS); + CDATA(THIS)[index] = cv.z; + } + else + { + if (COMPLEX(THIS)) + CDATA(THIS)[index] = cv.z; + else + DATA(THIS)[index] = cv.x; + } + +END_METHOD + + +BEGIN_METHOD(Polynomial_Eval, GB_VARIANT value) + + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + type = COMPLEX_get_value(value, &cv); + if (type == CGV_ERR) + return; + + if (COMPLEX(THIS)) + { + GB.ReturnObject(COMPLEX_create(gsl_complex_poly_complex_eval(CDATA(THIS), COUNT(THIS), cv.z))); + } + else + { + if (type == CGV_COMPLEX) + GB.ReturnObject(COMPLEX_create(gsl_poly_complex_eval(DATA(THIS), COUNT(THIS), cv.z))); + else + GB.ReturnFloat(gsl_poly_eval(DATA(THIS), COUNT(THIS), cv.x)); + } + +END_METHOD + + +BEGIN_METHOD(Polynomial_Solve, GB_BOOLEAN want_croot) + + bool want_croot = VARGOPT(want_croot, FALSE); + bool complex = COMPLEX(THIS); + + double *data = DATA(THIS); + gsl_complex *cdata = CDATA(THIS); + + int nr = 0, i, dg, ret; + + GB_ARRAY result; + + double r[3]; + gsl_complex cr[3]; + + double *z = NULL; + gsl_poly_complex_workspace *work; + + double *root; + CCOMPLEX **croot; + + dg = get_degree(THIS) + 1; + + if (dg > 2) + { + if (complex && ensure_not_complex(THIS)) + { + GB.Error("Cannot solve polynomial with complex coefficients"); + return; + } + + data = DATA(THIS); + } + + switch(dg) + { + case 1: + GB.ReturnNull(); + return; + + case 2: + nr = 1; + if (complex) + { + cr[0] = gsl_complex_div(gsl_complex_negative(cdata[0]), cdata[1]); + if (!want_croot) + { + if (GSL_IMAG(cr[0]) == 0.0) + r[0] = GSL_REAL(cr[0]); + else + nr = 0; + } + } + else + { + r[0] = -data[0] / data[1]; + if (want_croot) + cr[0] = gsl_complex_rect(r[0], 0); + } + + break; + + case 3: + if (want_croot) + nr = gsl_poly_complex_solve_quadratic(data[2], data[1], data[0], &cr[0], &cr[1]); + else + nr = gsl_poly_solve_quadratic(data[2], data[1], data[0], &r[0], &r[1]); + + break; + + case 4: + if (data[3] == 1.0) + { + if (want_croot) + nr = gsl_poly_complex_solve_cubic(data[2], data[1], data[0], &cr[0], &cr[1], &cr[2]); + else + nr = gsl_poly_solve_cubic(data[2], data[1], data[0], &r[0], &r[1], &r[2]); + break; + } + + default: + work = gsl_poly_complex_workspace_alloc(dg); + GB.Alloc(POINTER(&z), sizeof(double) * (dg - 1) * 2); + + ret = gsl_poly_complex_solve(data, dg, work, z); + + gsl_poly_complex_workspace_free(work); + + if (ret != GSL_SUCCESS) + { + GB.Free(POINTER(&z)); + return; + } + + if (!want_croot) + { + nr = 0; + for (i = 0; i < (dg - 1); i++) + { + if (z[i * 2 + 1] == 0.0) + nr++; + } + } + else + nr = dg - 1; + } + + if (!want_croot) + { + GB.Array.New(&result, GB_T_FLOAT, nr); + + if (nr > 0) + root = (double *)GB.Array.Get(result, 0); + + if (!z) + { + if (nr >= 1) root[0] = r[0]; + if (nr >= 2) root[1] = r[1]; + if (nr >= 3) root[2] = r[2]; + } + else + { + if (nr > 0) + { + nr = 0; + + for (i = 0; i < (dg - 1); i++) + { + if (z[i * 2 + 1] == 0.0) + root[nr++] = z[i * 2]; + } + } + + GB.Free(POINTER(&z)); + } + } + else + { + GB.Array.New(&result, CLASS_Complex, nr); + + if (nr > 0) + croot = (CCOMPLEX **)GB.Array.Get(result, 0); + else + croot = NULL; + + if (!z) + { + if (nr >= 1) croot[0] = COMPLEX_create(cr[0]); + if (nr >= 2) croot[1] = COMPLEX_create(cr[1]); + if (nr >= 3) croot[2] = COMPLEX_create(cr[2]); + } + else + { + for (i = 0; i < nr; i++) + croot[i] = COMPLEX_create(gsl_complex_rect(z[i * 2], z[i * 2 + 1])); + + GB.Free(POINTER(&z)); + } + + for (i = 0; i < nr; i++) + GB.Ref(croot[i]); + } + + GB.ReturnObject(result); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC PolynomialDesc[] = +{ + GB_DECLARE("Polynomial", sizeof(CPOLYNOMIAL)), + + GB_METHOD("_new", NULL, Polynomial_new, "[(Size)i(Complex)b]"), + GB_METHOD("_free", NULL, Polynomial_free, NULL), + + GB_METHOD("ToString", "s", Polynomial_ToString, "[(Local)b]"), + + GB_PROPERTY_READ("Degree", "i", Polynomial_Degree), + GB_PROPERTY_READ("Count", "i", Polynomial_Count), + + GB_METHOD("_get", "v", Polynomial_get, "(Index)i"), + GB_METHOD("_put", NULL, Polynomial_put, "(Value)v(Index)i"), + + GB_METHOD("_call", "v", Polynomial_Eval, "(X)v"), + GB_METHOD("Eval", "v", Polynomial_Eval, "(X)v"), + GB_METHOD("Solve", "Array", Polynomial_Solve, "[(Complex)b]"), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &POLYNOMIAL_convert), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_polynomial.h b/gb.gsl/src/c_polynomial.h new file mode 100644 index 00000000..e2105bdb --- /dev/null +++ b/gb.gsl/src/c_polynomial.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_polynomial.h + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_POLYNOMIAL_H +#define __C_POLYNOMIAL_H + +#include "main.h" +#include <gsl/gsl_poly.h> +#include <gsl/gsl_sf_result.h> + +extern GB_INTERFACE GB EXPORT; + +extern GB_DESC PolynomialDesc[]; + +typedef + struct { + GB_BASE ob; + int size; + void *data; + bool complex; + } + CPOLYNOMIAL; + +bool POLYNOMIAL_convert(CPOLYNOMIAL *a, GB_TYPE type, GB_VALUE *conv); + +#endif /* __C_POLYNOMIAL_H */ diff --git a/gb.gsl/src/c_vector.c b/gb.gsl/src/c_vector.c new file mode 100644 index 00000000..28a9ad59 --- /dev/null +++ b/gb.gsl/src/c_vector.c @@ -0,0 +1,817 @@ +/*************************************************************************** + + c_vector.c + + gb.gsl component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_VECTOR_C + +#include "c_complex.h" +#include "c_polynomial.h" +#include "c_vector.h" + +#define THIS ((CVECTOR *)_object) +#define COMPLEX(_v) ((_v)->complex) + +//---- Utility functions ---------------------------------------------- + +/*int gsl_vector_has_zero(gsl_vector *a) +{ + int i; + int size = (int)a->size; + + for (i = 0; i < size; i++) + { + if (gsl_vector_get(a, i) == 0.0) + return TRUE; + } + + return FALSE; +} + +int gsl_vector_complex_has_zero(gsl_vector *a) +{ + int i; + int size = (int)a->size; + gsl_complex *p: + + for (i = 0; i < size; i++) + { + p = gsl_vector_complex_ptr((gsl_vector_complext *)a, i); + if (p->dat[0] == 0.0 && p->dat[1] == 0.0) + return TRUE; + } + + return FALSE; +} + +void gsl_vector_inverse(gsl_vector *a) +{ + int i; + int size = (int)a->size; + double *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_ptr(a, i); + *p = 1.0 / *p; + } +} + +void gsl_vector_complex_inverse(gsl_vector *a) +{ + int i; + int size = (int)a->size; + gsl_complex *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_complex_ptr((gsl_vector_complex *)a, i); + *p = gsl_complex_inverse(*p); + } +} + +void gsl_vector_negative(gsl_vector *a) +{ + int i; + int size = (int)a->size; + double *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_ptr(a, i); + *p = (- *p); + } +} + +void gsl_vector_complex_negative(gsl_vector *a) +{ + int i; + int size = (int)a->size; + gsl_complex *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_complex_ptr((gsl_vector_complext *)a, i); + *p = gsl_complex_negative(*p); + } +}*/ + +//---- Vector creation ------------------------------------------------------ + +//static bool _do_not_init = FALSE; + +CVECTOR *VECTOR_create(int size, bool complex, bool init) +{ + CVECTOR *v = (CVECTOR *)GB.Create(CLASS_Vector, NULL, NULL); + + v->complex = complex; + + if (!complex) + v->vector = init ? gsl_vector_calloc(size) : gsl_vector_alloc(size); + else + v->vector = init ? gsl_vector_complex_calloc(size) : gsl_vector_complex_alloc(size); + + //GB.Push(2, GB_T_INTEGER, size, GB_T_BOOLEAN, complex); + //return (CVECTOR *)GB.New(CLASS_Vector, NULL, (void *)(intptr_t)2); + + return v; +} + +/*CVECTOR *VECTOR_create_from(void *vector, bool complex) +{ + int size = ((gsl_vector *)vector)->size; + CVECTOR *v = VECTOR_create(size, complex, FALSE); + + if (complex) + gsl_vector_complex_free(CVEC(v)); + else + gsl_vector_free(VEC(v)); + + v->vector = vector; + return v; +}*/ + +static CVECTOR *VECTOR_copy(CVECTOR *_object) +{ + CVECTOR *copy = VECTOR_create(SIZE(THIS), COMPLEX(THIS), FALSE); + if (!COMPLEX(THIS)) + gsl_vector_memcpy(VEC(copy), VEC(THIS)); + else + gsl_vector_complex_memcpy(CVEC(copy), CVEC(THIS)); + + return copy; +} + +#define VECTOR_make(_a) (((_a)->ob.ref <= 1) ? (_a) : VECTOR_copy(_a)) + +static CVECTOR *VECTOR_convert_to_complex(CVECTOR *_object) +{ + CVECTOR *v = VECTOR_create(SIZE(THIS), TRUE, FALSE); + int i; + + for (i = 0; i < SIZE(THIS); i++) + gsl_vector_complex_set((gsl_vector_complex *)v->vector, i, gsl_complex_rect(gsl_vector_get(VEC(THIS), i), 0)); + + return v; +} + +void VECTOR_ensure_complex(CVECTOR *_object) +{ + gsl_vector_complex *v; + int size = SIZE(THIS); + int i; + + if (COMPLEX(THIS)) + return; + + v = gsl_vector_complex_alloc(size); + for (i = 0; i < size; i++) + gsl_vector_complex_set(v, i, gsl_complex_rect(gsl_vector_get(VEC(THIS), i), 0)); + + gsl_vector_free(VEC(THIS)); + THIS->vector = v; + THIS->complex = TRUE; +} + + +bool VECTOR_ensure_not_complex(CVECTOR *_object) +{ + gsl_vector *v; + int size = SIZE(THIS); + int i; + gsl_complex c; + + if (!COMPLEX(THIS)) + return FALSE; + + for (i = 0; i < size; i++) + { + c = gsl_vector_complex_get(CVEC(THIS), i); + if (GSL_IMAG(c) != 0.0) + return TRUE; + } + + v = gsl_vector_alloc(size); + + for (i = 0; i < size; i++) + gsl_vector_set(v, i, GSL_REAL(gsl_vector_complex_get(CVEC(THIS), i))); + + gsl_vector_complex_free(CVEC(THIS)); + THIS->vector = v; + THIS->complex = FALSE; + return FALSE; +} + +//---- Arithmetic operators ------------------------------------------------- + +static CVECTOR *_add(CVECTOR *a, CVECTOR *b, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (COMPLEX(v) || COMPLEX(b)) + { + VECTOR_ensure_complex(v); + VECTOR_ensure_complex(b); + gsl_vector_complex_add(CVEC(v), CVEC(b)); + } + else + gsl_vector_add(VEC(v), VEC(b)); + + return v; +} + +static CVECTOR *_sub(CVECTOR *a, CVECTOR *b, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (COMPLEX(v) || COMPLEX(b)) + { + VECTOR_ensure_complex(v); + VECTOR_ensure_complex(b); + gsl_vector_complex_sub(CVEC(v), CVEC(b)); + } + else + gsl_vector_sub(VEC(v), VEC(b)); + + return v; +} + +static CVECTOR *_mulf(CVECTOR *a, double f, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (COMPLEX(v)) + gsl_vector_complex_scale(CVEC(v), gsl_complex_rect(f, 0)); + else + gsl_vector_scale(VEC(v), f); + + return v; +} + +static CVECTOR *_mulo(CVECTOR *a, void *b, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (!GB.Is(b, CLASS_Complex)) + return NULL; + + VECTOR_ensure_complex(v); + gsl_vector_complex_scale(CVEC(v), ((CCOMPLEX *)b)->number); + + return v; +} + +static CVECTOR *_divf(CVECTOR *a, double f, bool invert) +{ + if (invert) + return NULL; + + if (f == 0.0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + return _mulf(a, 1 / f, FALSE); +} + +static CVECTOR *_divo(CVECTOR *a, void *b, bool invert) +{ + if (!GB.Is(b, CLASS_Complex)) + return NULL; + + CCOMPLEX *c = (CCOMPLEX *)b; + + if (invert) + return NULL; + + if (GSL_REAL(c->number) == 0 && GSL_IMAG(c->number) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + CVECTOR *v = VECTOR_make(a); + + VECTOR_ensure_complex(v); + gsl_vector_complex_scale(CVEC(v), gsl_complex_inverse(c->number)); + + return v; +} + +static int _equal(CVECTOR *a, CVECTOR *b, bool invert) +{ + if (COMPLEX(a) || COMPLEX(b)) + { + VECTOR_ensure_complex(a); + VECTOR_ensure_complex(b); + return gsl_vector_complex_equal(CVEC(a), CVEC(b)); + } + else + return gsl_vector_equal(VEC(a), VEC(b)); +} + +static CVECTOR *_neg(CVECTOR *a) +{ + return _mulf(a, -1, FALSE); +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .add = (void *)_add, + .sub = (void *)_sub, + .mulf = (void *)_mulf, + .mulo = (void *)_mulo, + .divf = (void *)_divf, + .divo = (void *)_divo, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +static char *_to_string(CVECTOR *_object, bool local) +{ + char *result = NULL; + int i; + int size = SIZE(THIS); + char *str; + int len; + + result = GB.AddChar(result, '['); + + for (i = 0; i < size; i++) + { + if (i) + result = GB.AddChar(result, local ? ' ' : ','); + + if (!COMPLEX(THIS)) + { + GB.NumberToString(local, gsl_vector_get(VEC(THIS), i), NULL, &str, &len); + result = GB.AddString(result, str, len); + } + else + { + str = COMPLEX_to_string(gsl_vector_complex_get(CVEC(THIS), i), local); + result = GB.AddString(result, str, GB.StringLength(str)); + GB.FreeString(&str); + } + } + + result = GB.AddChar(result, ']'); + + return result; +} + +static bool _convert(CVECTOR *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (THIS) + { + if (!COMPLEX(THIS)) + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + break; + } + } + else + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + break; + } + } + + // Vector ---> Float[] + if ((type == GB.FindClass("Float[]") || type == CLASS_Polynomial) && !COMPLEX(THIS)) + { + GB_ARRAY a; + int i; + double *data; + + GB.Array.New(&a, GB_T_FLOAT, SIZE(THIS)); + data = (double *)GB.Array.Get(a, 0); + for(i = 0; i < SIZE(THIS); i++) + data[i] = gsl_vector_get(VEC(THIS), i); + + conv->_object.value = a; + if (type != CLASS_Polynomial) + return FALSE; + } + // Vector ---> Complex[] + else if (type == GB.FindClass("Complex[]") || type == CLASS_Polynomial) + { + GB_ARRAY a; + int i; + void **data; + CCOMPLEX *c; + + GB.Array.New(&a, CLASS_Complex, SIZE(THIS)); + data = (void **)GB.Array.Get(a, 0); + for(i = 0; i < SIZE(THIS); i++) + { + c = COMPLEX_create(COMPLEX(THIS) ? gsl_vector_complex_get(CVEC(THIS), i) : gsl_complex_rect(gsl_vector_get(VEC(THIS), i), 0)); + data[i] = c; + GB.Ref(c); + } + + conv->_object.value = a; + if (type != CLASS_Polynomial) + return FALSE; + } + else + return TRUE; + + // Vector ---> Polynomial + if (type == CLASS_Polynomial) + { + void *unref = conv->_object.value; + GB.Ref(unref); // Will be unref by the next GB.Conv() + POLYNOMIAL_convert(FALSE, type, conv); + GB.Unref(&unref); // Will be unref by the next GB.Conv() + //GB.Conv(conv, type); + //GB.UnrefKeep(&conv->_object.value, FALSE); // Will be ref again after the current GB.Conv() + return FALSE; + } + + } + else if (type >= GB_T_OBJECT) + { + if (GB.Is(conv->_object.value, CLASS_Array)) + { + GB_ARRAY array = (GB_ARRAY)conv->_object.value; + int size = GB.Array.Count(array); + CVECTOR *v; + int i; + GB_VALUE temp; + void *data; + GB_TYPE atype = GB.Array.Type(array); + + // Float[] Integer[] ... ---> Vector + if (atype > GB_T_BOOLEAN && atype <= GB_T_FLOAT) + { + v = VECTOR_create(size, FALSE, FALSE); + + for (i = 0; i < size; i++) + { + data = GB.Array.Get(array, i); + GB.ReadValue(&temp, data, atype); + GB.Conv(&temp, GB_T_FLOAT); + gsl_vector_set(VEC(v), i, temp._float.value); + } + + conv->_object.value = v; + return FALSE; + } + // Variant[] ---> Vector + else if (atype == GB_T_VARIANT) + { + CCOMPLEX *c; + v = VECTOR_create(size, TRUE, FALSE); + + for (i = 0; i < size; i++) + { + GB.ReadValue(&temp, GB.Array.Get(array, i), atype); + GB.BorrowValue(&temp); + GB.Conv(&temp, CLASS_Complex); + c = temp._object.value; + if (c) + gsl_vector_complex_set(CVEC(v), i, c->number); + else + gsl_vector_complex_set(CVEC(v), i, COMPLEX_zero); + GB.ReleaseValue(&temp); + } + + conv->_object.value = v; + return FALSE; + } + // Complex[] ---> Vector + else if (atype == CLASS_Complex) + { + CCOMPLEX *c; + v = VECTOR_create(size, TRUE, FALSE); + + for (i = 0; i < size; i++) + { + c = *((CCOMPLEX **)GB.Array.Get(array, i)); + if (c) + gsl_vector_complex_set(CVEC(v), i, c->number); + else + gsl_vector_complex_set(CVEC(v), i, COMPLEX_zero); + } + + conv->_object.value = v; + return FALSE; + } + } + // Float Integer... ---> Vector + else if (type > GB_T_BOOLEAN && type <= GB_T_FLOAT) + { + CVECTOR *v = VECTOR_create(1, FALSE, FALSE); + if (type == GB_T_FLOAT) + gsl_vector_set(VEC(v), 0, conv->_float.value); + else if (type == GB_T_SINGLE) + gsl_vector_set(VEC(v), 0, conv->_single.value); + else + gsl_vector_set(VEC(v), 0, conv->_integer.value); + conv->_object.value = v; + return FALSE; + } + // Complex ---> Vector + else if (type == CLASS_Complex) + { + CCOMPLEX *c = (CCOMPLEX *)conv->_object.value; + CVECTOR *v = VECTOR_create(1, TRUE, FALSE); + gsl_vector_complex_set(CVEC(v), 0, c->number); + conv->_object.value = v; + return FALSE; + } + } + + return TRUE; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Vector_new, GB_INTEGER size; GB_BOOLEAN complex) + + bool complex = VARGOPT(complex, FALSE); + int size = VARGOPT(size, 1); + + if (size < 1) size = 1; + + THIS->complex = complex; + + if (!complex) + THIS->vector = gsl_vector_calloc(size); + else + THIS->vector = gsl_vector_complex_calloc(size); + +END_METHOD + + +BEGIN_METHOD_VOID(Vector_free) + + if (!COMPLEX(THIS)) + gsl_vector_free(VEC(THIS)); + else + gsl_vector_complex_free(CVEC(THIS)); + +END_METHOD + + +BEGIN_PROPERTY(Vector_Count) + + GB.ReturnInteger(SIZE(THIS)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Vector_Copy) + + GB.ReturnObject(VECTOR_copy(THIS)); + +END_METHOD + + +BEGIN_METHOD(Vector_get, GB_INTEGER index) + + int size = SIZE(THIS); + int index = VARG(index); + + if (index < 0 || index >= size) + { + GB.Error(GB_ERR_BOUND); + return; + } + + if (!COMPLEX(THIS)) + GB.ReturnFloat(gsl_vector_get(VEC(THIS), index)); + else + GB.ReturnObject(COMPLEX_create(gsl_vector_complex_get(CVEC(THIS), index))); + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Vector_put, GB_VARIANT value; GB_INTEGER index) + + int index = VARG(index); + int size = SIZE(THIS); + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + if (index < 0 || index > size) + { + GB.Error(GB_ERR_BOUND); + return; + } + + type = COMPLEX_get_value(value, &cv); + + if (type == CGV_ERR) + return; + + if (type == CGV_COMPLEX) + { + VECTOR_ensure_complex(THIS); + gsl_vector_complex_set(CVEC(THIS), index, cv.z); + } + else + { + if (COMPLEX(THIS)) + gsl_vector_complex_set(CVEC(THIS), index, cv.z); + else + gsl_vector_set(VEC(THIS), index, cv.x); + } + +END_METHOD + + +static void do_dot(CVECTOR *_object, CVECTOR *v, bool conj) +{ + bool ca, cb; + + if (GB.CheckObject(v)) + return; + + ca = !COMPLEX(THIS); + cb = !COMPLEX(v); + + if (ca && cb) + { + double result; + gsl_blas_ddot(VEC(THIS), v->vector, &result); + GB.ReturnFloat(result); + } + else + { + CVECTOR *a, *b; + gsl_complex result; + + if (ca) + a = VECTOR_convert_to_complex(THIS); + else + a = THIS; + + if (cb) + b = VECTOR_convert_to_complex(v); + else + b = v; + + if (conj) + gsl_blas_zdotc(CVEC(a), CVEC(b), &result); + else + gsl_blas_zdotu(CVEC(a), CVEC(b), &result); + + GB.ReturnObject(COMPLEX_create(result)); + + if (ca) GB.Unref(POINTER(&a)); + if (cb) GB.Unref(POINTER(&b)); + } + + GB.ReturnConvVariant(); +} + + +BEGIN_METHOD(Vector_Dot, GB_OBJECT vector) + + do_dot(THIS, VARG(vector), FALSE); + +END_METHOD + + +BEGIN_METHOD(Vector_ConjDot, GB_OBJECT vector) + + do_dot(THIS, VARG(vector), TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Vector_Norm) + + if (!COMPLEX(THIS)) + GB.ReturnFloat(gsl_blas_dnrm2(VEC(THIS))); + else + GB.ReturnFloat(gsl_blas_dznrm2(CVEC(THIS))); + +END_METHOD + + +BEGIN_PROPERTY(Vector_Handle) + + GB.ReturnPointer(THIS->vector); + +END_PROPERTY + + +BEGIN_METHOD(Vector_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(_to_string(THIS, VARGOPT(local, FALSE)))); + +END_METHOD + + +//--------------------------------------------------------------------- + +GB_DESC VectorDesc[] = +{ + GB_DECLARE("Vector", sizeof(CVECTOR)), + + GB_METHOD("_new", NULL, Vector_new, "[(Size)i(Complex)b]"), + GB_METHOD("_free", NULL, Vector_free, NULL), + //GB_STATIC_METHOD("_call", "Vector", Vector_call, "(Value)f."), + GB_METHOD("Copy", "Vector", Vector_Copy, NULL), + GB_METHOD("ToString", "s", Vector_ToString, "[(Local)b]"), + + GB_PROPERTY_READ("Count", "i", Vector_Count), + GB_PROPERTY_READ("Handle", "p", Vector_Handle), + + GB_METHOD("_get", "v", Vector_get, "(Index)i"), + GB_METHOD("_put", NULL, Vector_put, "(Value)v(Index)i"), + + //GB_METHOD("Scale", "Vector", Vector_Scale, "(Value)v"), + GB_METHOD("Dot", "v", Vector_Dot, "(Vector)Vector"), + GB_METHOD("ConjDot", "v", Vector_ConjDot, "(Vector)Vector"), + GB_METHOD("Norm", "f", Vector_Norm, NULL), + //GB_METHOD("Equal", "b", Vector_Equal, "(Vector)Vector;"), + + GB_INTERFACE("_convert", &_convert), + GB_INTERFACE("_operator", &_operator), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_vector.h b/gb.gsl/src/c_vector.h new file mode 100644 index 00000000..6a787a5a --- /dev/null +++ b/gb.gsl/src/c_vector.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + c_vector.h + + gb.gsl component + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_VECTOR_H +#define __C_VECTOR_H + +#include "main.h" + +#ifndef __C_VECTOR_C +extern GB_DESC VectorDesc[]; +#endif + +typedef + struct + { + GB_BASE ob; + void *vector; + bool complex; + } + CVECTOR; + +#define VEC(_v) ((gsl_vector *)(_v)->vector) +#define CVEC(_v) ((gsl_vector_complex *)(_v)->vector) +#define SIZE(_v) ((int)(VEC(_v)->size)) + +CVECTOR *VECTOR_create(int size, bool complex, bool init); +void VECTOR_ensure_complex(CVECTOR *_object); +bool VECTOR_ensure_not_complex(CVECTOR *_object); + +#endif /* __C_VECTOR_H */ diff --git a/gb.gsl/src/gb.gsl.component b/gb.gsl/src/gb.gsl.component new file mode 100644 index 00000000..1d60d9da --- /dev/null +++ b/gb.gsl/src/gb.gsl.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.gsl +Author=Randall Morgan,Benoît Minisini +State=NotFinished +Implements=Complex diff --git a/gb.gsl/src/main.c b/gb.gsl/src/main.c new file mode 100644 index 00000000..e91215b6 --- /dev/null +++ b/gb.gsl/src/main.c @@ -0,0 +1,90 @@ +/*************************************************************************** + + main.c + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_gsl.h" +#include "c_complex.h" +#include "c_vector.h" +#include "c_matrix.h" +#include "c_polynomial.h" +#include "c_float_array.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + GslDesc, /* The Elementary math functions */ + ComplexDesc, + VectorDesc, + PolynomialDesc, + MatrixDesc, + FloatArrayStatDesc, + FloatArrayDesc, + NULL // Must have a null entry for the end of the structure +}; + +GB_CLASS CLASS_Array; +GB_CLASS CLASS_Complex; +GB_CLASS CLASS_Matrix; +GB_CLASS CLASS_Vector; +GB_CLASS CLASS_Polynomial; + +static void error_handler(const char *reason, const char *file, int line, int gsl_errno) +{ + //fprintf(stderr, "gb.gsl: error: %s: %s\n", gsl_strerror(gsl_errno), reason); + GB.Error("&1: &2", gsl_strerror(gsl_errno), reason); +} + +int EXPORT GB_INIT(void) +{ + CLASS_Array = GB.FindClass("Array"); + CLASS_Complex = GB.FindClass("Complex"); + CLASS_Vector = GB.FindClass("Vector"); + CLASS_Matrix = GB.FindClass("Matrix"); + CLASS_Polynomial = GB.FindClass("Polynomial"); + + gsl_set_error_handler(error_handler); + + return 0; +} + +void EXPORT GB_EXIT() +{ + +} + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "PUSH_COMPLEX")) + { + *value = (void *)COMPLEX_push_complex; + return TRUE; + } + else + return FALSE; +} diff --git a/gb.gsl/src/main.h b/gb.gsl/src/main.h new file mode 100644 index 00000000..484c9ce2 --- /dev/null +++ b/gb.gsl/src/main.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + main.h + + gb.gsl component + + (c) 2012 Randall Morgan <rmorgan62@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#include <gsl/gsl_math.h> +#include <gsl/gsl_blas.h> +#include <gsl/gsl_cblas.h> +#include <gsl/gsl_complex.h> +#include <gsl/gsl_vector.h> +#include <gsl/gsl_complex_math.h> +#include <gsl/gsl_linalg.h> +#include <gsl/gsl_matrix.h> + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern GB_CLASS CLASS_Array; +extern GB_CLASS CLASS_Complex; +extern GB_CLASS CLASS_Vector; +extern GB_CLASS CLASS_Matrix; +extern GB_CLASS CLASS_Polynomial; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.gtk/AUTHORS b/gb.gtk/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk/COPYING b/gb.gtk/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gtk/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gtk/ChangeLog b/gb.gtk/ChangeLog new file mode 100644 index 00000000..7de33bdf --- /dev/null +++ b/gb.gtk/ChangeLog @@ -0,0 +1,118 @@ +* FIXED: A serious bug making the program crash when deleting controls. +* FIXED: Some "Arrangement" bugs in some containers. +* NEW: "Menu" Enabled property implemented. +* FIXED: "Button" enabled/disabled aspect with some themes. +* FIXED: "PictureBox" can have focus now. +* NEW: Added an "Scroll" event to "ScrollView" container to detect user scroll changes. + + +* NEW: "Lang","Timer", and "Error" hooks implemented. +* NEW: "Label.Autoresize" property implemented. +* FIXED: "ScrollBar" is aligned at top or left of the control when there's extra space, as gb.qt does. +* FIXED: Some "Container" arrangement problems. +* FIXED: "HSplit" and "VSplit" arrangement fixed. +* NEW: "Separator" widget added. A simple widget which shows an horizontal or vertical separator line. +* FIXED: "TextLabel" horizontal alignment was wrong. +* FIXED: "Stock" icons take the current theme in account. +* FIXED: "ToolButton" click event is working now. +* NEW: "Button" and "ToggleButton" have a new boolean property "Vertical". If set, the + picture is show in the Top side instead of the Left side of the button. +* NEW: "TextLabel" and "Message" are able handle some HTML tags: <i>,<b>,<u>,<s>,<tt>, + <sup>,<sub>,<small>,<big>,<h1>...<h6>,<p>,<br>, and are able to ignore unrecognized + tags and parse not XML formatted strings. +* NEW: "Label" and "TextLabel" controls have a "Transparent" property now. +* FIXED: "Label" and "TextLabel" controls redesigned. +* FIXED: Segmentation fault in the "Stock" class. +* FIXED: "Window" size is working in transitions from border resizable to fixed. +* FIXED: "Picture" cache redesigned. +* FIXED: "ComboBox" background and foreground colors. + +050808 - Alpha + +* NEW: "UserContainer" class implemented. +* FIXED: Some problems in menu representation. +* NEW: "Stock" class implemented. + +050804 - Alpha + +* NEW: Added "HSplit" and "VSplit" containers. +* FIXED: Picture cache now is implemented using a hash table (faster). +* FIXED: some speed problems in DrawingArea. + +050731 - Alpha + +* NEW: ScrollView.EnsureVisible implemented. +* NEW: "UserControl" class implemented. +* NEW: TrayIcon.Tag, W and H properties. +* NEW: Font.Scalable and Font.Grade properties implemented. +* NEW: System colors added to "Color" class. +* NEW: TextArea.Scrollbar property added. +* NEW: Control.Reparent method added to reparent widgets (but not top-level windows) +* FIXED: Mouse, Focus, and Key events handling redesigned: 28 bytes less per widget. +* NEW: Application.Font implemented. + +050730 - Alpha + +* FIXED: Controls now inherit parent font. +* FIXED: Tooltips management improved: best performance and less memory wasted. +* NEW: Application.Tooltip: Enabled and Font properties implemented +* NEW: MouseWheel event, Mouse.Orientation and Mouse.Delta properties implemented. +* NEW: Fonts.Count and Fonts._next implemented. +* FIXED: Containers only arrange visible children. +* FIXED: Segmentation fault on some containers when arrangement is not "None". +* FIXED: Some Window.Picture representation problems. +* NEW: "ListView" Find implemented. +* NEW: "Application.ActiveWindow" implemented. +* NEW: "TrayIcon": ScreenX, ScreenY, Width and Height properties added. +* NEW: "TrayIcon" adapted to the gb.qt new interface. +* NEW: "Mouse.ScreenX" and "Mouse.ScreenY" implemented. +* NEW: "Desktop.Scale", "Control.MoveScaled" and "Control.ResizeScaled" implemented. + +050725 - Alpha + +* FIXED: Restored compatibility with GTK+-2.4 +* FIXED: "ComboBox" Length and Text properties are working now. +* FIXED: "TextBox" Length property is working now. +* NEW: "ColumnView" Click and Activate events implemented. + +050724 - Alpha + +* NEW: "Picture.Copy" implemented. +* FIXED: Pictures now display correctly PNG transparences. +* FIXED: "Picture" class redesigned for better performance and less memory usage. +* NEW: "Image" _get, _put, copy and replace methods implemented. +* FIXED: 4 bytes less wasted per "PictureBox". +* FIXED: 4 bytes less wasted per "ComboBox". +* FIXED: Internal code reorganization and cleaning. + +050722 - Alpha + +* FIXED: "Show" calls to "ShowModal" if current Window is modal. +* NEW: 8 bytes less wasted per container. +* FIXED: "Frame" children position fixed. +* FIXED: "TabStrip" can display accelerators in labels. BackGround and ForeGround properties + fixed. Some less memory wasted per TabStrip widget. +* NEW: "Window" Maximized, Minimized and FullScreen properties are working. State removed. +* FIXED: "Dialog" class now implements Dialog.paths and OpenFile(Multi) to be compatible with + the gb.qt component. +* FIXED: "Window" now has two related properties "Mask" and "Picture" in order to be + compatible with the gb.qt implementation. Masking works correctly now. Using a Picture + with Mask=FALSE just uses the Picture as background for the Window. +* FIXED: "Plugger" control now is called "Embedder" in order to be compatible with + the gb.qt implementation. Methods, Events and Properties names changed too. +* NEW: "Application.Embedder" added to be compatible with gb.qt implementation. + + +050702 - Alpha + +* NEW: ScrollView properties and methods fully implemented. +* FIXED: Button font handling improved: more compatibility with gb.qt +* FIXED: "fg" and "bg" widgets pointers removed from gControl (8 bytes less used per + widget) +* NEW: Image.Save, Picture.Save, Picture.Flush, Picture._put, Picture._get methods + implemented. +* FIXED: ListView.BackGround now works correctly. +* NEW: Mouse.Move method implemented. +* NEW: Picture.Clear method implemented. +* NEW: Window and Form can have a parent now. +* NEW: ScrollBar and Slider properties and methods are fully implemented now. \ No newline at end of file diff --git a/gb.gtk/INSTALL b/gb.gtk/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gtk/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gtk/Makefile.am b/gb.gtk/Makefile.am new file mode 100644 index 00000000..65461878 --- /dev/null +++ b/gb.gtk/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GTK_DIR@ +EXTRA_DIST = reconf spec share gb*.h gambas.h diff --git a/gb.gtk/NEWS b/gb.gtk/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk/README b/gb.gtk/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk/TODO b/gb.gtk/TODO new file mode 100644 index 00000000..8866cc08 --- /dev/null +++ b/gb.gtk/TODO @@ -0,0 +1,16 @@ +TODO: + +* HTML support in the TextEdit control. + +* The Settings properties of some controls. + +* The MovieBox source code was not reviewed. + +* The Control.Design property. + +* Solve underline and strikethrough problems. + +* TextEdit text size does not work. + +* Events ignored during a WAIT. + diff --git a/gb.gtk/acinclude.m4 b/gb.gtk/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gtk/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gtk/component.am b/gb.gtk/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gtk/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gtk/configure.ac b/gb.gtk/configure.ac new file mode 100644 index 00000000..5983ed30 --- /dev/null +++ b/gb.gtk/configure.ac @@ -0,0 +1,29 @@ +dnl ---- configure.ac for gb.gtk component + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-gtk],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gtk) +LT_INIT + +GB_CHECK_XWINDOW() + +GB_COMPONENT_PKG_CONFIG( + gtk, GTK, gb.gtk, [src], + 'gtk+-2.0 >= 2.16' 'librsvg-2.0 >= 2.14.3' 'cairo >= 1.6.0' 'cairo-ft >= 1.6.0' 'gtk+-unix-print-2.0 >= 2.10' sm ice x11 +) + +GB_COMPONENT_PKG_CONFIG( + gtkopengl, GTKOPENGL, gb.gtk.opengl, [opengl], + gtkglext-1.0 gl x11 +) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +src/opengl/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.gtk/gambas.h b/gb.gtk/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gtk/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gtk/gb.draw.h b/gb.gtk/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.gtk/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.gtk/gb.geom.h b/gb.gtk/gb.geom.h new file mode 120000 index 00000000..be9b8748 --- /dev/null +++ b/gb.gtk/gb.geom.h @@ -0,0 +1 @@ +../main/lib/draw/gb.geom.h \ No newline at end of file diff --git a/gb.gtk/gb.gl.h b/gb.gtk/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.gtk/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.gtk/gb.image.h b/gb.gtk/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.gtk/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.gtk/gb.paint.h b/gb.gtk/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.gtk/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.gtk/gb_common.h b/gb.gtk/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gtk/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gtk/m4 b/gb.gtk/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gtk/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gtk/reconf b/gb.gtk/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gtk/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gtk/share b/gb.gtk/share new file mode 120000 index 00000000..638490a4 --- /dev/null +++ b/gb.gtk/share @@ -0,0 +1 @@ +../gb.qt4/share \ No newline at end of file diff --git a/gb.gtk/src/CButton.cpp b/gb.gtk/src/CButton.cpp new file mode 100644 index 00000000..1bfa2b50 --- /dev/null +++ b/gb.gtk/src/CButton.cpp @@ -0,0 +1,312 @@ +/*************************************************************************** + + CButton.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CBUTTON_CPP + +#include "CButton.h" +#include "CContainer.h" +#include "CPicture.h" + +DECLARE_EVENT(EVENT_Click); + +void CB_button_click(gControl *sender) +{ + CWIDGET *ob = GetObject(sender); + + GB.Ref(ob); + GB.Raise((void *)ob, EVENT_Click, 0); + CACTION_raise(ob); + GB.Unref(POINTER(&ob)); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(CBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Button), (CWIDGET*)THIS); + +END_METHOD + + +BEGIN_METHOD(CTOGGLEBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Toggle), (CWIDGET*)THIS); + +END_METHOD + +BEGIN_METHOD(CCHECKBOX_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Check), (CWIDGET*)THIS); + +END_METHOD + +BEGIN_METHOD(CRADIOBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Radio), (CWIDGET*)THIS); + +END_METHOD + +BEGIN_METHOD(CTOOLBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Tool), (CWIDGET*)THIS); + +END_METHOD + + +BEGIN_PROPERTY(CBUTTON_text) + + if (READ_PROPERTY) { GB.ReturnNewZeroString(BUTTON->text()); return; } + BUTTON->setText((const char*)GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_picture) + + if (READ_PROPERTY) + { + gPicture *pic = BUTTON->picture(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + BUTTON->setPicture(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_border) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->getBorder()); return; } + BUTTON->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_value) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->value()); return; } + BUTTON->setValue(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_default) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->isDefault()); return; } + BUTTON->setDefault(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_cancel) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->isCancel()); return; } + BUTTON->setCancel(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isToggle()); + else + BUTTON->setToggle(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CCHECKBOX_tristate) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isTristate()); + else + BUTTON->setTristate(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CCHECKBOX_value) + + if (READ_PROPERTY) + { + if (BUTTON->isTristate() && BUTTON->inconsistent()) + GB.ReturnInteger(1); + else + GB.ReturnInteger(BUTTON->value() ? -1 : 0); + } + else + { + if (BUTTON->isTristate() && VPROP(GB_INTEGER) == 1) + BUTTON->setInconsistent(true); + else + { + BUTTON->setInconsistent(false); + BUTTON->setValue(VPROP(GB_INTEGER)); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isRadio()); + else + BUTTON->setRadio(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_autoresize) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isAutoResize()); + else + BUTTON->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CheckBox_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isInverted()); + else + BUTTON->setInverted(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CButtonDesc[] = +{ + GB_DECLARE("Button", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Default", "b", CBUTTON_default), + GB_PROPERTY("Cancel", "b", CBUTTON_cancel), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + BUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CToggleButtonDesc[] = +{ + GB_DECLARE("ToggleButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTOGGLEBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Radio", "b", CBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + TOGGLEBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CRadioButtonDesc[] = +{ + GB_DECLARE("RadioButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CRADIOBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + GB_PROPERTY("Invert", "b", CheckBox_Invert), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + RADIOBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CCheckBoxDesc[] = +{ + GB_DECLARE("CheckBox", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_CONSTANT("False", "i", 0), + GB_CONSTANT("True", "i", -1), + GB_CONSTANT("None", "i", 1), + + GB_METHOD("_new", 0, CCHECKBOX_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Tristate", "b", CCHECKBOX_tristate), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + GB_PROPERTY("Invert", "b", CheckBox_Invert), + + GB_PROPERTY("Value", "i", CCHECKBOX_value), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + CHECKBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CToolButtonDesc[] = +{ + GB_DECLARE("ToolButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTOOLBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("Toggle", "b", CTOOLBUTTON_toggle), + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Radio", "b", CBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + TOOLBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.gtk/src/CButton.h b/gb.gtk/src/CButton.h new file mode 100644 index 00000000..3a50a8e1 --- /dev/null +++ b/gb.gtk/src/CButton.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CButton.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CBUTTON_H +#define __CBUTTON_H + +#include "main.h" +#include "gbutton.h" +#include "CWidget.h" +#include "CPicture.h" + + +#ifndef __CBUTTON_CPP +extern GB_DESC CButtonDesc[]; +extern GB_DESC CToggleButtonDesc[]; +extern GB_DESC CCheckBoxDesc[]; +extern GB_DESC CRadioButtonDesc[]; +extern GB_DESC CToolButtonDesc[]; +#else + +#define THIS ((CBUTTON *)_object) +#define BUTTON ((gButton*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CBUTTON; + +#endif diff --git a/gb.gtk/src/CClipboard.cpp b/gb.gtk/src/CClipboard.cpp new file mode 100644 index 00000000..894d9e98 --- /dev/null +++ b/gb.gtk/src/CClipboard.cpp @@ -0,0 +1,609 @@ +/*************************************************************************** + + CClipboard.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCLIPBOARD_CPP + +#include "gclipboard.h" + +#include "CWidget.h" +#include "CClipboard.h" +#include "CImage.h" +#include "CPicture.h" + +/*************************************************************************** + + Clipboard + +***************************************************************************/ + +static CIMAGE *_clipboard_image = NULL; + +BEGIN_METHOD_VOID(Clipboard_Clear) + + gClipboard::clear(); + GB.StoreObject(NULL, POINTER(&_clipboard_image)); + +END_METHOD + +static char *get_format(int i = 0, bool charset = false, bool drag = false) +{ + char *format = drag ? gDrag::getFormat(i) : gClipboard::getFormat(i); + char *p; + + if (format && !charset) + { + p = index(format, ';'); + if (p) + { + format = gt_free_later(g_strndup(format, p - format)); + } + } + + return format; +} + +static void get_formats(GB_ARRAY array, bool drag = false) +{ + int i, j; + char *fmt; + + for (i = 0;; i++) + { + fmt = get_format(i, true, drag); + if (!fmt) + break; + + if (*fmt < 'a' || *fmt > 'z') + continue; + + for (j = 0; j < GB.Array.Count(array); j++) + { + if (strcasecmp(fmt, *((char **)GB.Array.Get(array, j))) == 0) + break; + } + + if (j < GB.Array.Count(array)) + continue; + + *((char **)GB.Array.Add(array)) = GB.NewZeroString(fmt); + } +} + +static bool exist_format(char *format, bool drag = false) +{ + int i; + char *fmt; + bool ret = false; + + for (i = 0;; i++) + { + fmt = get_format(i, true, drag); + if (!fmt) + break; + + if (*fmt < 'a' || *fmt > 'z') + continue; + + if (!strcasecmp(format, fmt)) + { + ret = true; + break; + } + } + + return ret; +} + +BEGIN_PROPERTY(Clipboard_Format) + + GB.ReturnNewZeroString(get_format()); + +END_PROPERTY + +BEGIN_PROPERTY(Clipboard_Formats) + + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + get_formats(array); + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(Clipboard_Type) + + GB.ReturnInteger(gClipboard::getType()); + +END_PROPERTY + +BEGIN_METHOD(Clipboard_Copy, GB_VARIANT data; GB_STRING format) + + char *format; + + if (VARG(data).type == GB_T_STRING) + { + if (MISSING(format)) + format = NULL; + else + { + format = GB.ToZeroString(ARG(format)); + if (strlen(format) < 6 || strncmp(format, "text/", 5) ) + goto _BAD_FORMAT; + } + + gClipboard::setText(VARG(data).value._string, -1, format); + return; + } + + if (VARG(data).type >= GB_T_OBJECT && GB.Is(VARG(data).value._object, GB.FindClass("Image"))) + { + CIMAGE *img; + + if (!MISSING(format)) + goto _BAD_FORMAT; + + img = (CIMAGE *)VARG(data).value._object; + GB.Unref(POINTER(&_clipboard_image)); + GB.Ref(img); + _clipboard_image = img; + gClipboard::setImage(CIMAGE_get(img)); + return; + } + +_BAD_FORMAT: + + GB.Error("Bad clipboard format"); + +END_METHOD + + +BEGIN_METHOD(Clipboard_Paste, GB_STRING format) + + CIMAGE *img; + char *format = NULL; + char *text; + int len; + int type; + + type = gClipboard::getType(); + + if (!MISSING(format)) + { + format = GB.ToZeroString(ARG(format)); + if (!exist_format(format)) + { + GB.ReturnVariant(NULL); + return; + } + if (strncasecmp(format, "text/", 5) == 0) + type = gClipboard::Text; + } + + switch(type) + { + case gClipboard::Text: + text = gClipboard::getText(&len, format); + if (text) + GB.ReturnNewString(text, len); + else + GB.ReturnNull(); + break; + + case gClipboard::Image: + img = CIMAGE_create(gClipboard::getImage()); + GB.ReturnObject((void *)img); + break; + + case gClipboard::Nothing: + default: + GB.ReturnNull(); + } + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_PROPERTY(Clipboard_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(gClipboard::getCurrent()); + else + gClipboard::setCurrent(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_HasChanged) + + GB.ReturnBoolean(gClipboard::hasChanged()); + +END_PROPERTY + +GB_DESC CClipboardDesc[] = +{ + GB_DECLARE_STATIC("Clipboard"), + + GB_CONSTANT("None", "i", 0), + GB_CONSTANT("Text", "i", 1), + GB_CONSTANT("Image", "i", 2), + + GB_CONSTANT("Default", "i", 0), + GB_CONSTANT("Selection", "i", 1), + + GB_STATIC_METHOD("_exit", 0, Clipboard_Clear, 0), + GB_STATIC_METHOD("Clear", 0, Clipboard_Clear, 0), + + GB_STATIC_PROPERTY("Current", "i", Clipboard_Current), + + GB_STATIC_PROPERTY_READ("Format", "s", Clipboard_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Clipboard_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", Clipboard_Type), + GB_STATIC_PROPERTY_READ("HasChanged", "b", Clipboard_HasChanged), + + GB_STATIC_METHOD("Copy", 0, Clipboard_Copy, "(Data)v[(Format)s]"), + GB_STATIC_METHOD("Paste", "v", Clipboard_Paste, "[(Format)s]"), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + Drag + +***************************************************************************/ + +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, char *format) +{ + gControl *dest; + + if (GB.CheckObject(source)) + return NULL; + + if (gDrag::isActive()) + { + GB.Error("Undergoing drag"); + return NULL; + } + + if (data->type == GB_T_STRING) + { + if (format) + { + if (strlen(format) < 5) + goto _BAD_FORMAT; + if (strncasecmp(format, "text/", 5)) + goto _BAD_FORMAT; + } + + dest = gDrag::dragText(source->widget, data->value._string, format); + } + else if (data->type >= GB_T_OBJECT && GB.Is(data->value._object, GB.FindClass("Image"))) + { + if (format && *format) + goto _BAD_FORMAT; + + dest = gDrag::dragImage(source->widget, CIMAGE_get((CIMAGE *)data->value._object)); + } + else + goto _BAD_FORMAT; + + //hide_frame(NULL); + //GB.Post((GB_POST_FUNC)post_exit_drag, 0); + + return GetObject(dest); + +_BAD_FORMAT: + + GB.Error("Bad drag format"); + return NULL; +} + +BEGIN_METHOD(Drag_call, GB_OBJECT source; GB_VARIANT data; GB_STRING format) + + GB.ReturnObject(CDRAG_drag((CWIDGET *)VARG(source), &VARG(data), MISSING(format) ? NULL : GB.ToZeroString(ARG(format)))); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Target) + + if (READ_PROPERTY) + GB.ReturnObject(GetObject(gDrag::getDestination())); + else + { + CWIDGET *dest = (CWIDGET *)VPROP(GB_OBJECT); + gDrag::setDestination(dest ? dest->widget : NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Icon) + + if (READ_PROPERTY) + { + gPicture *pic = gDrag::getIcon(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + gDrag::setIcon(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_IconX) + + int x, y; + + gDrag::getIconPos(&x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(x); + else + gDrag::setIconPos(VPROP(GB_INTEGER), y); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_IconY) + + int x, y; + + gDrag::getIconPos(&x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(y); + else + gDrag::setIconPos(x, VPROP(GB_INTEGER)); + +END_PROPERTY + + +#define CHECK_VALID() \ + if (!gDrag::isEnabled()) \ + { \ + GB.Error("No drag data"); \ + return; \ + } + + +BEGIN_PROPERTY(Drag_Type) + + CHECK_VALID(); + GB.ReturnInteger(gDrag::getType()); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Format) + + CHECK_VALID(); + + GB.ReturnNewZeroString(get_format(0, false, true)); + +END_PROPERTY + +BEGIN_PROPERTY(Drag_Formats) + + GB_ARRAY array; + + CHECK_VALID(); + + GB.Array.New(&array, GB_T_STRING, 0); + get_formats(array, true); + GB.ReturnObject(array); + +END_PROPERTY + + + +static void paste_drag(char *format) +{ + CIMAGE *image; + char *text; + int len; + + //if (format) + // g_debug("format: %s drag: %s\n", format, gDrag::getFormat()); + + //fprintf(stderr, "paste_drag: %s\n", format); + + if (format) + { + if (!exist_format(format, true)) + { + GB.ReturnVariant(NULL); + return; + } + } + + switch(gDrag::getType()) + { + case gDrag::Text: + text = gDrag::getText(&len, format); + if (text) + GB.ReturnNewString(text, len); + else + GB.ReturnNull(); + break; + + case gDrag::Image: + image = CIMAGE_create(gDrag::getImage()->copy()); + GB.ReturnObject((void *)image); + break; + + default: + GB.ReturnNull(); + } + + GB.ReturnConvVariant(); +} + +BEGIN_PROPERTY(Drag_Data) + + CHECK_VALID(); + + if (!gDrag::isActive()) + { + GB.ReturnNull(); + return; + } + + paste_drag(NULL); + +END_PROPERTY + + +BEGIN_METHOD(Drag_Paste, GB_STRING format) + + CHECK_VALID(); + + if (!gDrag::isActive()) + { + GB.ReturnNull(); + return; + } + + paste_drag(MISSING(format) ? NULL : GB.ToZeroString(ARG(format))); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Action) + + CHECK_VALID(); + GB.ReturnInteger(gDrag::getAction()); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Source) + + CHECK_VALID(); + GB.ReturnObject(GetObject(gDrag::getSource())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_X) + + CHECK_VALID(); + if (READ_PROPERTY) + GB.ReturnInteger(gDrag::getDropX()); + else + gDrag::setDropX(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Y) + + CHECK_VALID(); + if (READ_PROPERTY) + GB.ReturnInteger(gDrag::getDropY()); + else + gDrag::setDropY(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Drag_exit) + + gDrag::exit(); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Pending) + + GB.ReturnBoolean(gDrag::isActive()); + +END_PROPERTY + + +BEGIN_METHOD(Drag_Show, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + if (GB.CheckObject(VARG(control))) + return; + + /*if (!gDrag::isActive()) + { + GB.Error("No undergoing drag"); + return; + }*/ + + if (MISSING(x) || MISSING(y) || MISSING(w) || MISSING(h)) + gDrag::show(((CWIDGET *)VARG(control))->widget); + else + gDrag::show(((CWIDGET *)VARG(control))->widget, VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + + +BEGIN_METHOD_VOID(Drag_Hide) + + gDrag::hide(); + +END_METHOD + + +GB_DESC CDragDesc[] = +{ + GB_DECLARE("Drag", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", gDrag::Nothing), + GB_CONSTANT("Text", "i", gDrag::Text), + GB_CONSTANT("Image", "i", gDrag::Image), + + GB_CONSTANT("Copy", "i", DRAG_COPY), + GB_CONSTANT("Link", "i", DRAG_LINK), + GB_CONSTANT("Move", "i", DRAG_MOVE), + + GB_STATIC_PROPERTY("Icon", "Picture", Drag_Icon), + GB_STATIC_PROPERTY("IconX", "i", Drag_IconX), + GB_STATIC_PROPERTY("IconY", "i", Drag_IconY), + + GB_STATIC_PROPERTY_READ("Data", "v", Drag_Data), + GB_STATIC_PROPERTY_READ("Format", "s", Drag_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Drag_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", Drag_Type), + GB_STATIC_PROPERTY_READ("Action", "i", Drag_Action), + GB_STATIC_PROPERTY_READ("Source", "Control", Drag_Source), + GB_STATIC_PROPERTY("X", "i", Drag_X), + GB_STATIC_PROPERTY("Y", "i", Drag_Y), + GB_STATIC_PROPERTY_READ("Pending", "b", Drag_Pending), + GB_STATIC_PROPERTY("_Target", "Control", Drag_Target), + + GB_STATIC_METHOD("_call", "Control", Drag_call, "(Source)Control;(Data)v[(Format)s]"), + GB_STATIC_METHOD("_exit", 0, Drag_exit, 0), + GB_STATIC_METHOD("Show", 0, Drag_Show, "(Control)Control;[(X)i(Y)i(Width)i(Height)i]"), + GB_STATIC_METHOD("Hide", 0, Drag_Hide, 0), + GB_STATIC_METHOD("Paste", "v", Drag_Paste, "[(Format)s]"), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CClipboard.h b/gb.gtk/src/CClipboard.h new file mode 100644 index 00000000..17e69f99 --- /dev/null +++ b/gb.gtk/src/CClipboard.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + CClipboard.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCLIPBOARD_H +#define __CCLIPBOARD_H + +#include "main.h" + +#ifndef __CCLIPBOARD_CPP +extern GB_DESC CClipboardDesc[]; +extern GB_DESC CDragDesc[]; +#endif + +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, char *format); + +#endif diff --git a/gb.gtk/src/CColor.cpp b/gb.gtk/src/CColor.cpp new file mode 100644 index 00000000..23536437 --- /dev/null +++ b/gb.gtk/src/CColor.cpp @@ -0,0 +1,154 @@ +/*************************************************************************** + + CColor.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOLOR_CPP + +#include <math.h> + +#include "CColor.h" +#include "gdesktop.h" +#include "gcolor.h" + +GB_COLOR _link_foreground = COLOR_DEFAULT; +GB_COLOR _visited_foreground = COLOR_DEFAULT; +GB_COLOR _tooltip_foreground = COLOR_DEFAULT; +GB_COLOR _tooltip_background = COLOR_DEFAULT; + + +static void handle_color(void *_param, GB_COLOR color, GB_COLOR *var) +{ + if (READ_PROPERTY) + GB.ReturnInteger(*var == COLOR_DEFAULT ? color : *var); + else + *var = VPROP(GB_INTEGER); +} + +BEGIN_PROPERTY(Color_Background) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::BACKGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_Foreground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::FOREGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextBackground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::TEXT_BACKGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextForeground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::TEXT_FOREGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedBackground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::SELECTED_BACKGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedForeground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::SELECTED_FOREGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonBackground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::BUTTON_BACKGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonForeground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::BUTTON_FOREGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightBackground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::LIGHT_BACKGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightForeground) + + GB.ReturnInteger(gDesktop::getColor(gDesktop::LIGHT_FOREGROUND)); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TooltipBackground) + + handle_color(_param, gDesktop::getColor(gDesktop::TOOLTIP_BACKGROUND), &_tooltip_background); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TooltipForeground) + + handle_color(_param, gDesktop::getColor(gDesktop::TOOLTIP_FOREGROUND), &_tooltip_foreground); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LinkForeground) + + handle_color(_param, gDesktop::getColor(gDesktop::LINK_FOREGROUND), &_link_foreground); + +END_PROPERTY + +BEGIN_PROPERTY(Color_VisitedForeground) + + handle_color(_param, gDesktop::getColor(gDesktop::VISITED_FOREGROUND), &_visited_foreground); + +END_PROPERTY + +GB_DESC CColorDesc[] = +{ + GB_DECLARE_STATIC("Color"), + + GB_STATIC_PROPERTY_READ("Background", "i", Color_Background), + GB_STATIC_PROPERTY_READ("SelectedBackground", "i", Color_SelectedBackground), + GB_STATIC_PROPERTY_READ("LightBackground", "i", Color_LightBackground), + GB_STATIC_PROPERTY_READ("TextBackground", "i", Color_TextBackground), + GB_STATIC_PROPERTY_READ("ButtonBackground", "i", Color_ButtonBackground), + + GB_STATIC_PROPERTY_READ("Foreground", "i", Color_Foreground), + GB_STATIC_PROPERTY_READ("SelectedForeground", "i", Color_SelectedForeground), + GB_STATIC_PROPERTY_READ("LightForeground", "i", Color_LightForeground), + GB_STATIC_PROPERTY_READ("TextForeground", "i", Color_TextForeground), + GB_STATIC_PROPERTY_READ("ButtonForeground", "i", Color_ButtonForeground), + + GB_STATIC_PROPERTY("TooltipBackground", "i", Color_TooltipBackground), + GB_STATIC_PROPERTY("TooltipForeground", "i", Color_TooltipForeground), + GB_STATIC_PROPERTY("LinkForeground", "i", Color_LinkForeground), + GB_STATIC_PROPERTY("VisitedForeground", "i", Color_VisitedForeground), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CColor.h b/gb.gtk/src/CColor.h new file mode 100644 index 00000000..f33e6de2 --- /dev/null +++ b/gb.gtk/src/CColor.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + CColor.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOLOR_H +#define __CCOLOR_H + +#include "main.h" + +#ifndef __CCOLOR_CPP +extern GB_DESC CColorDesc[]; +#endif + +#endif diff --git a/gb.gtk/src/CConst.cpp b/gb.gtk/src/CConst.cpp new file mode 100644 index 00000000..e379b07b --- /dev/null +++ b/gb.gtk/src/CConst.cpp @@ -0,0 +1,154 @@ +/*************************************************************************** + + CConst.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONST_CPP + + +#include "gambas.h" +#include "main.h" +#include "widgets.h" +#include "CConst.h" + + +#define IMPLEMENT_ALIGN(_method, _code) \ +BEGIN_METHOD(_method, GB_INTEGER align) \ + int a = VARG(align); \ + GB.ReturnBoolean(_code); \ +END_METHOD + +IMPLEMENT_ALIGN(Align_IsTop, ALIGN_IS_TOP(a)) +IMPLEMENT_ALIGN(Align_IsBottom, ALIGN_IS_BOTTOM(a)) +IMPLEMENT_ALIGN(Align_IsMiddle, ALIGN_IS_MIDDLE(a)) +IMPLEMENT_ALIGN(Align_IsLeft, ALIGN_IS_LEFT(a)) +IMPLEMENT_ALIGN(Align_IsRight, ALIGN_IS_RIGHT(a)) +IMPLEMENT_ALIGN(Align_IsCenter, ALIGN_IS_CENTER(a)) + +BEGIN_METHOD(Align_Make, GB_INTEGER halign; GB_INTEGER valign) + + GB.ReturnInteger(ALIGN_MAKE(VARG(halign), VARG(valign))); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC AlignDesc[] = +{ + GB_DECLARE("Align", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Normal", "i", ALIGN_NORMAL), + GB_CONSTANT("Left", "i", ALIGN_LEFT), + GB_CONSTANT("Right", "i", ALIGN_RIGHT), + GB_CONSTANT("Center", "i", ALIGN_CENTER), + + GB_CONSTANT("TopNormal", "i", ALIGN_TOP_NORMAL), + GB_CONSTANT("TopLeft", "i", ALIGN_TOP_LEFT), + GB_CONSTANT("TopRight", "i", ALIGN_TOP_RIGHT), + GB_CONSTANT("Top", "i", ALIGN_TOP), + + GB_CONSTANT("BottomNormal", "i", ALIGN_BOTTOM_NORMAL), + GB_CONSTANT("BottomLeft", "i", ALIGN_BOTTOM_LEFT), + GB_CONSTANT("BottomRight", "i", ALIGN_BOTTOM_RIGHT), + GB_CONSTANT("Bottom", "i", ALIGN_BOTTOM), + + GB_CONSTANT("Justify", "i", ALIGN_JUSTIFY), + + GB_STATIC_METHOD("IsTop", "b", Align_IsTop, "(Alignment)i"), + GB_STATIC_METHOD("IsBottom", "b", Align_IsBottom, "(Alignment)i"), + GB_STATIC_METHOD("IsMiddle", "b", Align_IsMiddle, "(Alignment)i"), + GB_STATIC_METHOD("IsLeft", "b", Align_IsLeft, "(Alignment)i"), + GB_STATIC_METHOD("IsCenter", "b", Align_IsCenter, "(Alignment)i"), + GB_STATIC_METHOD("IsRight", "b", Align_IsRight, "(Alignment)i"), + + GB_STATIC_METHOD("Make", "i", Align_Make, "(Horizontal)i(Vertical)i"), + + GB_END_DECLARE +}; + + +GB_DESC ArrangeDesc[] = +{ + GB_DECLARE("Arrange", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", ARRANGE_NONE), + GB_CONSTANT("Horizontal", "i", ARRANGE_HORIZONTAL), + GB_CONSTANT("Vertical", "i", ARRANGE_VERTICAL), + GB_CONSTANT("LeftRight", "i", ARRANGE_ROW), + GB_CONSTANT("TopBottom", "i", ARRANGE_COLUMN), + GB_CONSTANT("Row", "i", ARRANGE_ROW), + GB_CONSTANT("Column", "i", ARRANGE_COLUMN), + GB_CONSTANT("Fill", "i", ARRANGE_FILL), + + GB_END_DECLARE +}; + + +GB_DESC BorderDesc[] = +{ + GB_DECLARE("Border", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", BORDER_NONE), + GB_CONSTANT("Plain", "i", BORDER_PLAIN), + GB_CONSTANT("Sunken", "i", BORDER_SUNKEN), + GB_CONSTANT("Raised", "i", BORDER_RAISED), + GB_CONSTANT("Etched", "i", BORDER_ETCHED), + + GB_END_DECLARE +}; + + +GB_DESC ScrollDesc[] = +{ + GB_DECLARE("Scroll", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SCROLL_NONE), + GB_CONSTANT("Horizontal", "i", SCROLL_HORIZONTAL), + GB_CONSTANT("Vertical", "i", SCROLL_VERTICAL), + GB_CONSTANT("Both", "i", SCROLL_BOTH), + + GB_END_DECLARE +}; + + +GB_DESC SelectDesc[] = +{ + GB_DECLARE("Select", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SELECT_NONE), + GB_CONSTANT("Single", "i", SELECT_SINGLE), + GB_CONSTANT("Multiple", "i", SELECT_MULTIPLE), + + GB_END_DECLARE +}; + + +GB_DESC DirectionDesc[] = +{ + GB_DECLARE_STATIC("Direction"), + + GB_CONSTANT("Default", "i", DIRECTION_DEFAULT), + GB_CONSTANT("LeftToRight", "i", DIRECTION_LTR), + GB_CONSTANT("RightToLeft", "i", DIRECTION_RTL), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CConst.h b/gb.gtk/src/CConst.h new file mode 100644 index 00000000..6bce27d7 --- /dev/null +++ b/gb.gtk/src/CConst.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + CConst.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONST_H +#define __CCONST_H + +#include "gambas.h" + +#ifndef __CCONST_CPP +extern GB_DESC AlignDesc[]; +extern GB_DESC ArrangeDesc[]; +extern GB_DESC BorderDesc[]; +extern GB_DESC ScrollDesc[]; +extern GB_DESC SelectDesc[]; +extern GB_DESC DirectionDesc[]; +#endif + + + + +#endif diff --git a/gb.gtk/src/CContainer.cpp b/gb.gtk/src/CContainer.cpp new file mode 100644 index 00000000..af43d2a5 --- /dev/null +++ b/gb.gtk/src/CContainer.cpp @@ -0,0 +1,753 @@ +/*************************************************************************** + + CContainer.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONTAINER_CPP + +#include "gambas.h" +#include "CContainer.h" +#include "gpanel.h" +#include "gmainwindow.h" +#include "gapplication.h" +#include "cpaint_impl.h" + +#define CALL_FUNCTION(_this, _func) \ +{ \ + if ((_this) && (_this)->_func) \ + { \ + GB_FUNCTION func; \ + func.object = (_this); \ + func.index = (_this)->_func; \ + GB.Call(&func, 0, TRUE); \ + } \ +} + + +/*************************************************************************** + + Container + +***************************************************************************/ + +DECLARE_EVENT(EVENT_BeforeArrange); +DECLARE_EVENT(EVENT_Arrange); +DECLARE_EVENT(EVENT_Insert); + + +void CB_container_before_arrange(gContainer *sender) +{ + GB.Raise(sender->hFree, EVENT_BeforeArrange, 0); +} + +void CB_container_arrange(gContainer *sender) +{ + GB.Raise(sender->hFree, EVENT_Arrange, 0); +} + +void CCONTAINER_raise_insert(CCONTAINER *_object, CWIDGET *child) +{ + GB.Raise(THIS, EVENT_Insert, 1, GB_T_OBJECT, child); +} + +#ifdef GTK3 + +static void cleanup_drawing(intptr_t arg1, intptr_t arg2) +{ + PAINT_end(); +} + +void CUSERCONTROL_cb_draw(gContainer *sender, cairo_t *cr) +{ + CWIDGET *_object = GetObject(sender); + GB_ERROR_HANDLER handler; + + cairo_t *save = THIS_USERCONTROL->context; + THIS_USERCONTROL->context = cr; + + PAINT_begin(THIS); + + handler.handler = (GB_CALLBACK)cleanup_drawing; + GB.OnErrorBegin(&handler); + CALL_FUNCTION(THIS_USERCONTROL, paint_func); + GB.OnErrorEnd(&handler); + + PAINT_end(); + + THIS_USERCONTROL->context = save; +} + +#else + +static void cleanup_drawing(cairo_t *cr, intptr_t arg2) +{ + cairo_restore(cr); + PAINT_end(); +} + +void CUSERCONTROL_cb_draw(gContainer *sender, GdkRegion *region, int dx, int dy) +{ + CWIDGET *_object = GetObject(sender); + GB_ERROR_HANDLER handler; + cairo_t *cr; + + PAINT_begin(THIS); + + cr = PAINT_get_current_context(); + cairo_save(cr); + PAINT_clip(0, 0, sender->width(), sender->height()); + + handler.handler = (GB_CALLBACK)cleanup_drawing; + handler.arg1 = (intptr_t)cr; + GB.OnErrorBegin(&handler); + CALL_FUNCTION(THIS_USERCONTROL, paint_func); + GB.OnErrorEnd(&handler); + + cairo_restore(cr); + PAINT_end(); +} + +#endif + +void CUSERCONTROL_cb_font(gContainer *sender) +{ + CWIDGET *_object = GetObject(sender); + CALL_FUNCTION(THIS_USERCONTROL, font_func); +} + +static bool cb_change_filter(gControl *control) +{ + return control->isContainer() && ((gContainer *)control)->isPaint(); +} + +static void cb_change(gControl *control) +{ + CWIDGET *_object = GetObject(control); + CALL_FUNCTION(THIS_USERCONTROL, change_func); +} + +void CUSERCONTROL_send_change_event(void) +{ + gApplication::forEachControl(cb_change, cb_change_filter); +} + +static void get_client_area(gContainer *cont, int *x, int *y, int *w, int *h) +{ + gContainer *proxy = cont->proxyContainer(); + + if (x) *x = proxy->clientX(); + if (y) *y = proxy->clientY(); + if (w) *w = proxy->clientWidth(); + if (h) *h = proxy->clientHeight(); + + if (x || y) + { + while (proxy && proxy != cont) + { + if (x) *x += proxy->x(); + if (y) *y += proxy->y(); + proxy = proxy->parent(); + } + } +} + +void CUSERCONTROL_cb_resize(gContainer *sender) +{ + CWIDGET *_object = GetObject(sender); + CALL_FUNCTION(THIS_USERCONTROL, resize_func); +} + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Container_ClientX) + + int x; + get_client_area(WIDGET, &x, NULL, NULL, NULL); + GB.ReturnInteger(x); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientY) + + int y; + get_client_area(WIDGET, NULL, &y, NULL, NULL); + GB.ReturnInteger(y); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientWidth) + + int w; + get_client_area(WIDGET, NULL, NULL, &w, NULL); + GB.ReturnInteger(w); + + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientHeight) + + int h; + get_client_area(WIDGET, NULL, NULL, NULL, &h); + GB.ReturnInteger(h); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Arrangement) + + if (READ_PROPERTY) { GB.ReturnInteger(WIDGET->arrange()); return; } + WIDGET->setArrange(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->autoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Padding) + + if (READ_PROPERTY) { GB.ReturnInteger(WIDGET->padding()); return; } + WIDGET->setPadding(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Spacing) + + if (READ_PROPERTY) { GB.ReturnBoolean(WIDGET->spacing()); return; } + WIDGET->setSpacing(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Margin) + + if (READ_PROPERTY) { GB.ReturnBoolean(WIDGET->margin()); return; } + WIDGET->setMargin(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Indent) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->indent()); + else + WIDGET->setIndent(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Centered) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->centered()); + else + WIDGET->setCentered(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->invert()); + else + WIDGET->setInvert(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD(Container_FindChild, GB_INTEGER x; GB_INTEGER y) + + gControl *child = WIDGET->proxyContainer()->find(VARG(x), VARG(y)); + + if (child) + GB.ReturnObject(child->hFree); + else + GB.ReturnNull(); + +END_METHOD + + +BEGIN_METHOD(Container_unknown, GB_VALUE x; GB_VALUE y) + + char *name = GB.GetUnknown(); + int nparam = GB.NParam(); + + if (strcasecmp(name, "Find")) + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + return; + } + + if (nparam < 2) + { + GB.Error("Not enough argument"); + return; + } + else if (nparam > 2) + { + GB.Error("Too many argument"); + return; + } + + GB.Deprecated(GTK_NAME, "Container.Find", "Container.FindChild"); + + if (GB.Conv(ARG(x), GB_T_INTEGER) || GB.Conv(ARG(y), GB_T_INTEGER)) + return; + + Container_FindChild(_object, _param); + + GB.ReturnConvVariant(); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +BEGIN_PROPERTY(Container_Children) + + CCONTAINERCHILDREN *children = (CCONTAINERCHILDREN *)GB.New(CLASS_ContainerChildren, NULL, NULL); + gContainer *cont = WIDGET->proxyContainer(); + gControl *child; + int i; + + children->container = THIS; + GB.Ref(THIS); + + GB.NewArray(POINTER(&children->children), sizeof(void *), 0); + + for (i = 0; i < cont->childCount(); i++) + { + child = cont->child(i); + if (!child->hFree || child->isDestroyed()) + continue; + GB.Ref(child->hFree); + *(void **)GB.Add(&children->children) = child->hFree; + } + + GB.ReturnObject(children); + +END_PROPERTY + + +BEGIN_METHOD_VOID(ContainerChildren_free) + + int i; + CWIDGET **array = THIS_CHILDREN->children; + + for (i = 0; i < GB.Count(array); i++) + GB.Unref(POINTER(&array[i])); + + GB.FreeArray(&THIS_CHILDREN->children); + GB.Unref(POINTER(&THIS_CHILDREN->container)); + +END_METHOD + + +BEGIN_PROPERTY(ContainerChildren_Count) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children)); + +END_PROPERTY + + +BEGIN_PROPERTY(ContainerChildren_Max) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children) - 1); + +END_PROPERTY + + +BEGIN_METHOD(ContainerChildren_get, GB_INTEGER index) + + CWIDGET **array = THIS_CHILDREN->children; + int index = VARG(index); + + if (index < 0 || index >= GB.Count(array)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(array[index]); + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_next) + + CWIDGET **array = THIS_CHILDREN->children; + int index; + + index = ENUM(int); + + if (index >= GB.Count(array)) + GB.StopEnum(); + else + { + ENUM(int) = index + 1; + GB.ReturnObject(array[index]); + } + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_Clear) + + ((gContainer *)(THIS_CHILDREN->container->ob.widget))->clear(); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +GB_DESC ContainerChildrenDesc[] = +{ + GB_DECLARE("ContainerChildren", sizeof(CCONTAINER)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, ContainerChildren_free, NULL), + GB_METHOD("_next", "Control", ContainerChildren_next, NULL), + GB_METHOD("_get", "Control", ContainerChildren_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", ContainerChildren_Count), + GB_PROPERTY_READ("Max", "i", ContainerChildren_Max), + GB_METHOD("Clear", NULL, ContainerChildren_Clear, NULL), + + GB_END_DECLARE +}; + + +GB_DESC ContainerDesc[] = +{ + GB_DECLARE("Container", sizeof(CCONTAINER)), GB_INHERITS("Control"), + + GB_NOT_CREATABLE(), + + GB_PROPERTY_READ("Children", "ContainerChildren", Container_Children), + + GB_PROPERTY_READ("ClientX", "i", Container_ClientX), + GB_PROPERTY_READ("ClientY", "i", Container_ClientY), + GB_PROPERTY_READ("ClientW", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientWidth", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientH", "i", Container_ClientHeight), + GB_PROPERTY_READ("ClientHeight", "i", Container_ClientHeight), + + GB_METHOD("FindChild", "Control", Container_FindChild, "(X)i(Y)i"), + GB_METHOD("_unknown", "v", Container_unknown, "."), + + CONTAINER_DESCRIPTION, + + GB_EVENT("BeforeArrange", NULL, NULL, &EVENT_BeforeArrange), + GB_EVENT("Arrange", NULL, NULL, &EVENT_Arrange), + GB_EVENT("NewChild", NULL, "(Child)Control", &EVENT_Insert), + + GB_END_DECLARE +}; + +/**************************************************************************** + + UserControl & UserContainer + +****************************************************************************/ + +BEGIN_METHOD(UserControl_new, GB_OBJECT parent) + + GB_FUNCTION func; + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + + PANEL->setArrange(ARRANGE_FILL); + PANEL->setUser(); + + if (GB.Is(THIS, CLASS_UserContainer)) + PANEL->setUserContainer(); + + if (!GB.GetFunction(&func, THIS, "UserControl_Draw", NULL, NULL)) + { + PANEL->setPaint(); + THIS_USERCONTROL->paint_func = func.index; + if (!GB.GetFunction(&func, THIS, "UserControl_Font", NULL, NULL)) + THIS_USERCONTROL->font_func = func.index; + if (!GB.GetFunction(&func, THIS, "UserControl_Change", NULL, NULL)) + THIS_USERCONTROL->change_func = func.index; + if (!GB.GetFunction(&func, THIS, "UserControl_Resize", NULL, NULL)) + THIS_USERCONTROL->resize_func = func.index; + } + + GB.Error(NULL); + +END_METHOD + + +BEGIN_PROPERTY(UserControl_Container) + + CCONTAINER *cont; + gControl *p; + gContainer *w; + + if (READ_PROPERTY) + { + GB.ReturnObject(GetObject(WIDGET->proxyContainer())); + return; + } + + cont = (CCONTAINER*)VPROP(GB_OBJECT); + + if (!cont) + { + if (WIDGET->proxyContainer() != WIDGET) + WIDGET->proxyContainer()->setProxyContainerFor(NULL); + WIDGET->setProxyContainer(NULL); + + WIDGET->setProxy(NULL); + return; + } + + if (GB.CheckObject(cont)) + return; + + w = ((gContainer *)cont->ob.widget)->proxyContainer(); + /*while (w->proxyContainer() != w) + w = w->proxyContainer();*/ + + if (w == WIDGET->proxyContainer()) + return; + + for (p = cont->ob.widget; p; p = p->parent()) + { + if (p == WIDGET) + break; + } + + if (!p) + { + GB.Error("Container must be a child control"); + return; + } + + gColor bg = WIDGET->proxyContainer()->background(); + gColor fg = WIDGET->proxyContainer()->foreground(); + + if (WIDGET->proxyContainer() != WIDGET) + WIDGET->proxyContainer()->setProxyContainerFor(NULL); + + WIDGET->setProxyContainer(w); + w->setProxyContainerFor(WIDGET); + + w->setBackground(bg); + w->setForeground(fg); + + WIDGET->performArrange(); + WIDGET->setProxy((gContainer *)cont->ob.widget); + +END_PROPERTY + + +BEGIN_PROPERTY(UserControl_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->canFocus()); + else + WIDGET->setCanFocus(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Container) + + if (READ_PROPERTY) + UserControl_Container(_object, _param); + else + { + UserControl_Container(_object, _param); + + WIDGET->proxyContainer()->setFullArrangement(&THIS_USERCONTAINER->save); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Arrangement) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->proxyContainer()->arrange()); + else + { + WIDGET->proxyContainer()->setArrange(VPROP(GB_INTEGER)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->proxyContainer()->autoResize()); + else + { + WIDGET->proxyContainer()->setAutoResize(VPROP(GB_BOOLEAN)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Padding) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->proxyContainer()->padding()); + else + { + WIDGET->proxyContainer()->setPadding(VPROP(GB_INTEGER)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Spacing) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->proxyContainer()->spacing()); + else + { + WIDGET->proxyContainer()->setSpacing(VPROP(GB_BOOLEAN)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Margin) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->proxyContainer()->margin()); + else + { + WIDGET->proxyContainer()->setMargin(VPROP(GB_BOOLEAN)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Indent) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->proxyContainer()->indent()); + else + { + WIDGET->proxyContainer()->setIndent(VPROP(GB_BOOLEAN)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Centered) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->proxyContainer()->centered()); + else + { + WIDGET->proxyContainer()->setCentered(VPROP(GB_BOOLEAN)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->proxyContainer()->invert()); + else + { + WIDGET->proxyContainer()->setInvert(VPROP(GB_BOOLEAN)); + THIS_USERCONTAINER->save = WIDGET->proxyContainer()->fullArrangement(); + } + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC UserControlDesc[] = +{ + GB_DECLARE("UserControl", sizeof(CUSERCONTROL)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + GB_PROPERTY("_Container", "Container", UserControl_Container), + GB_PROPERTY("_AutoResize", "b", Container_AutoResize), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + GB_PROPERTY("_Padding", "i", Container_Padding), + GB_PROPERTY("_Spacing", "b", Container_Spacing), + GB_PROPERTY("_Margin", "b", Container_Margin), + GB_PROPERTY("_Indent", "b", Container_Indent), + GB_PROPERTY("_Invert", "b", Container_Invert), + GB_PROPERTY("_Centered", "b", Container_Centered), + GB_PROPERTY("_Focus", "b", UserControl_Focus), + + USERCONTROL_DESCRIPTION, + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + +GB_DESC UserContainerDesc[] = +{ + GB_DECLARE("UserContainer", sizeof(CUSERCONTAINER)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + + GB_PROPERTY("_Container", "Container", UserContainer_Container), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + + GB_PROPERTY("Arrangement", "i", UserContainer_Arrangement), + GB_PROPERTY("AutoResize", "b", UserContainer_AutoResize), + GB_PROPERTY("Padding", "i", UserContainer_Padding), + GB_PROPERTY("Spacing", "b", UserContainer_Spacing), + GB_PROPERTY("Margin", "b", UserContainer_Margin), + GB_PROPERTY("Indent", "b", UserContainer_Indent), + GB_PROPERTY("Invert", "b", UserContainer_Invert), + GB_PROPERTY("Centered", "b", UserContainer_Centered), + + USERCONTAINER_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CContainer.h b/gb.gtk/src/CContainer.h new file mode 100644 index 00000000..47f8994e --- /dev/null +++ b/gb.gtk/src/CContainer.h @@ -0,0 +1,121 @@ +/*************************************************************************** + + CContainer.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONTAINER_H +#define __CCONTAINER_H + + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "CWidget.h" + +#ifndef __CCONTAINER_CPP + +extern GB_DESC ContainerChildrenDesc[]; +extern GB_DESC ContainerDesc[]; +extern GB_DESC UserControlDesc[]; +extern GB_DESC UserContainerDesc[]; + +#else + +#define THIS ((CCONTAINER *)_object) +#define THIS_CHILDREN ((CCONTAINERCHILDREN *)_object) +#define THIS_USERCONTAINER ((CUSERCONTAINER *)_object) +#define THIS_USERCONTROL ((CUSERCONTROL *)_object) +#define WIDGET ((gContainer*)THIS->ob.widget) +#define PANEL ((gPanel *)(THIS->ob.widget)) + +#endif + +#define ARRANGEMENT_FLAG_PROPERTIES \ + GB_PROPERTY("AutoResize", "b", Container_AutoResize), \ + GB_PROPERTY("Padding", "i", Container_Padding), \ + GB_PROPERTY("Spacing", "b", Container_Spacing), \ + GB_PROPERTY("Margin", "b", Container_Margin), \ + GB_PROPERTY("Indent", "b", Container_Indent), \ + GB_PROPERTY("Invert", "b", Container_Invert), \ + GB_PROPERTY("Centered", "b", Container_Centered) + +#define ARRANGEMENT_PROPERTIES \ + GB_PROPERTY("Arrangement", "i", Container_Arrangement), \ + ARRANGEMENT_FLAG_PROPERTIES + +typedef + struct + { + CWIDGET ob; + } + CCONTAINER; + +typedef + struct + { + GB_BASE ob; + CCONTAINER *container; + CWIDGET **children; + } + CCONTAINERCHILDREN; + +typedef + struct + { + CWIDGET widget; + gContainerArrangement save; + } + CUSERCONTAINER; + +typedef + struct + { + CWIDGET widget; + #ifdef GTK3 + cairo_t *context; + #endif + ushort paint_func; + ushort font_func; + ushort change_func; + ushort resize_func; + } + CUSERCONTROL; + +DECLARE_PROPERTY(Container_ClientX); +DECLARE_PROPERTY(Container_ClientY); +DECLARE_PROPERTY(Container_ClientWidth); +DECLARE_PROPERTY(Container_ClientHeight); +DECLARE_PROPERTY(Container_Arrangement); +DECLARE_PROPERTY(Container_AutoResize); +DECLARE_PROPERTY(Container_Padding); +DECLARE_PROPERTY(Container_Spacing); +DECLARE_PROPERTY(Container_Margin); +DECLARE_PROPERTY(Container_Indent); +DECLARE_PROPERTY(Container_Invert); +DECLARE_PROPERTY(Container_Centered); + +void CCONTAINER_cb_arrange(gContainer *sender); +void CCONTAINER_cb_before_arrange(gContainer *sender); +void CCONTAINER_raise_insert(CCONTAINER *_object, CWIDGET *child); +void CUSERCONTROL_send_change_event(); + + +#endif diff --git a/gb.gtk/src/CDialog.cpp b/gb.gtk/src/CDialog.cpp new file mode 100644 index 00000000..be4f919e --- /dev/null +++ b/gb.gtk/src/CDialog.cpp @@ -0,0 +1,228 @@ +/*************************************************************************** + + CDialog.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDIALOG_CPP + +#include "CDialog.h" +#include "CFont.h" + +static GB_ARRAY dialog_filter = NULL; + + +BEGIN_METHOD_VOID(CDIALOG_exit) + + GB.StoreObject(NULL, POINTER(&dialog_filter)); + +END_METHOD + + +BEGIN_PROPERTY(Dialog_Title) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(gDialog::title()); + else + gDialog::setTitle(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Filter) + + char **filters; + char *filter; + int i; + + if (READ_PROPERTY) + GB.ReturnObject(dialog_filter); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&dialog_filter)); + GB.NewArray(&filters, sizeof(char *), 0); + if (dialog_filter) + { + for (i = 0; i < (GB.Array.Count(dialog_filter) - 1); i += 2) + { + filter = *((char **)GB.Array.Get(dialog_filter, i)); + *((char **)GB.Add(&filters)) = filter; + filter = *((char **)GB.Array.Get(dialog_filter, i + 1)); + *((char **)GB.Add(&filters)) = filter; + } + } + + gDialog::setFilter(filters, GB.Count(filters)); + GB.FreeArray(&filters); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Paths) + + GB_ARRAY Array=NULL; + char **buf=NULL; + long b=0; + + buf=gDialog::paths(); + if (buf) + { + while (buf[b++]); + GB.Array.New(&Array,GB_T_STRING,b-1); + + b=0; + while (buf[b]) + { + *((char **)GB.Array.Get(Array, b)) = GB.NewZeroString(buf[b]); + b++; + } + GB.ReturnObject(Array); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Path) + + if (READ_PROPERTY) + GB.ReturnNewZeroString( gDialog::path()); + else + gDialog::setPath(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_ShowHidden) + + if (READ_PROPERTY) + GB.ReturnBoolean(gDialog::showHidden()); + else + gDialog::setShowHidden(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Font) + + CFONT *ft; + + if (READ_PROPERTY) + { + gFont *font = gDialog::font(); + if (font) + ft = CFONT_create(font->copy()); + else + ft = NULL; + + GB.ReturnObject(ft); + return; + } + + ft=(CFONT*)VPROP(GB_OBJECT); + if (ft && ft->font) + gDialog::setFont(ft->font); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Color) + + if (READ_PROPERTY) { GB.ReturnInteger( gDialog::color() ); return; } + gDialog::setColor(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD(Dialog_OpenFile,GB_BOOLEAN Multi;) + + bool Multi=false; + + if (!MISSING(Multi)) Multi=VARG(Multi); + + GB.ReturnBoolean( gDialog::openFile(Multi) ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SaveFile) + + GB.ReturnBoolean( gDialog::saveFile() ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectDirectory) + + GB.ReturnBoolean( gDialog::selectFolder() ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectColor) + + GB.ReturnBoolean ( gDialog::selectColor() ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectFont) + + GB.ReturnBoolean ( gDialog::selectFont() ); + +END_METHOD + + +BEGIN_PROPERTY(Dialog_FilterIndex) + + if (READ_PROPERTY) + GB.ReturnInteger(gDialog::filterIndex()); + else + gDialog::setFilterIndex(VPROP(GB_INTEGER)); + +END_PROPERTY + + +GB_DESC CDialogDesc[] = +{ + GB_DECLARE("Dialog", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", 0, CDIALOG_exit, 0), + + GB_STATIC_METHOD("OpenFile", "b", Dialog_OpenFile, "[(Multi)b]"), + GB_STATIC_METHOD("SaveFile", "b", Dialog_SaveFile, 0), + GB_STATIC_METHOD("SelectDirectory", "b", Dialog_SelectDirectory, 0), + GB_STATIC_METHOD("SelectColor", "b", Dialog_SelectColor, 0), + GB_STATIC_METHOD("SelectFont", "b", Dialog_SelectFont, 0), + + GB_STATIC_PROPERTY_READ("Paths", "String[]", Dialog_Paths), + + GB_STATIC_PROPERTY("Title", "s", Dialog_Title), + GB_STATIC_PROPERTY("Path", "s", Dialog_Path), + GB_STATIC_PROPERTY("Filter", "String[]", Dialog_Filter), + GB_STATIC_PROPERTY("FilterIndex", "i", Dialog_FilterIndex), + GB_STATIC_PROPERTY("Color", "i", Dialog_Color), + GB_STATIC_PROPERTY("Font", "Font", Dialog_Font), + GB_STATIC_PROPERTY("ShowHidden", "b", Dialog_ShowHidden), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CDialog.h b/gb.gtk/src/CDialog.h new file mode 100644 index 00000000..b7272bc1 --- /dev/null +++ b/gb.gtk/src/CDialog.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + CDialog.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDIALOG_H +#define __CDIALOG_H + +#include "main.h" +#include "gdialog.h" + +#ifndef __CDIALOG_CPP +extern GB_DESC CDialogDesc[]; +#endif + +#endif diff --git a/gb.gtk/src/CDraw.cpp b/gb.gtk/src/CDraw.cpp new file mode 100644 index 00000000..51a79eb2 --- /dev/null +++ b/gb.gtk/src/CDraw.cpp @@ -0,0 +1,39 @@ +/*************************************************************************** + + CDraw.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_CPP + +#include "CWindow.h" +#include "CDrawingArea.h" +#include "CPicture.h" +#include "CImage.h" +#include "CFont.h" +#include "CDraw.h" +#include "gdesktop.h" + +DRAW_INTERFACE DRAW EXPORT; + +void DRAW_init() +{ + GB.GetInterface("gb.draw", DRAW_INTERFACE_VERSION, &DRAW); +} diff --git a/gb.gtk/src/CDraw.h b/gb.gtk/src/CDraw.h new file mode 100644 index 00000000..5aad01ec --- /dev/null +++ b/gb.gtk/src/CDraw.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + CDraw.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "main.h" +#include "gb.draw.h" + +#ifndef __CDRAW_C + +extern DRAW_INTERFACE DRAW; + +#endif + +void DRAW_init(); + +#endif + diff --git a/gb.gtk/src/CDrawingArea.cpp b/gb.gtk/src/CDrawingArea.cpp new file mode 100644 index 00000000..3980ebb8 --- /dev/null +++ b/gb.gtk/src/CDrawingArea.cpp @@ -0,0 +1,280 @@ +/*************************************************************************** + + CDrawingArea.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAWINGAREA_CPP + +#include <stdio.h> + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "gapplication.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CDrawingArea.h" +#include "CWidget.h" +#include "CContainer.h" + +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Font); +DECLARE_EVENT(EVENT_Change); + + +/*************************************************************************** + + DrawingArea + +***************************************************************************/ + +static bool cb_change_filter(gControl *control) +{ + return control->isDrawingArea(); +} + +static void cb_change(gControl *control) +{ + GB.Raise(control->hFree, EVENT_Change, 0); +} + +void CDRAWINGAREA_send_change_event(void) +{ + gApplication::forEachControl(cb_change, cb_change_filter); +} + +#ifdef GTK3 + +typedef + struct { + CDRAWINGAREA *control; + cairo_t *save; + } + HANDLER_INFO; + +static void cleanup_drawing(HANDLER_INFO *info) +{ + PAINT_end(); + info->control->context = info->save; +} + +void CB_drawingarea_expose(gDrawingArea *sender, cairo_t *cr) +{ + CWIDGET *_object = GetObject(sender); + GB_RAISE_HANDLER handler; + HANDLER_INFO info; + int fw; + + if (GB.CanRaise(THIS, EVENT_Draw)) + { + handler.callback = (void (*)(intptr_t))cleanup_drawing; + handler.data = (intptr_t)&info; + + info.control = THIS; + info.save = THIS->context; + + GB.RaiseBegin(&handler); + + THIS->context = cr; + PAINT_begin(THIS); + + fw = sender->getFrameWidth(); + cairo_save(cr); + //cairo_reset_clip(cr); + PAINT_clip(fw, fw, sender->width() - fw * 2, sender->height() - fw * 2); + + GB.Raise(THIS, EVENT_Draw, 0); + + cairo_restore(cr); + + PAINT_end(); + THIS->context = info.save; + + GB.RaiseEnd(&handler); + } +} +#else +static void cleanup_drawing(intptr_t _unused) +{ + PAINT_end(); +} + +void CB_drawingarea_expose(gDrawingArea *sender, GdkRegion *region, int dx, int dy) +{ + CWIDGET *_object = GetObject(sender); + GB_RAISE_HANDLER handler; + cairo_t *cr; + int fw; + + if (GB.CanRaise(THIS, EVENT_Draw)) + { + handler.callback = cleanup_drawing; + handler.data = (intptr_t)THIS; + + GB.RaiseBegin(&handler); + + PAINT_begin(THIS); + cr = PAINT_get_current_context(); + fw = sender->getFrameWidth(); + cairo_save(cr); + PAINT_clip(fw, fw, sender->width() - fw * 2, sender->height() - fw * 2); + + GB.Raise(THIS, EVENT_Draw, 0); + + cairo_restore(cr); + PAINT_end(); + + GB.RaiseEnd(&handler); + } +} +#endif + +void CB_drawingarea_font(gDrawingArea *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Font, 0); +} + + +BEGIN_METHOD(CDRAWINGAREA_new, GB_OBJECT parent) + + InitControl(new gDrawingArea(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Border) + + if (READ_PROPERTY) { GB.ReturnInteger(WIDGET->getBorder()); return; } + WIDGET->setBorder(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(DrawingArea_Cached) + + if (READ_PROPERTY) { GB.ReturnBoolean(WIDGET->cached()); return; } + WIDGET->setCached(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->canFocus()); + else + WIDGET->setCanFocus(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(DrawingArea_Clear) + + if (DRAW.Paint.IsPainted(THIS)) + { + GB.Error("DrawingArea is being painted"); + return; + } + + WIDGET->clear(); + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Painted) + + static bool deprecated = false; + + if (!deprecated) + { + deprecated = true; + GB.Deprecated(GTK_NAME, "DrawingArea.Painted", NULL); + } + + if (READ_PROPERTY) + GB.ReturnBoolean(true); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_NoBackground) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasNoBackground()); + else + WIDGET->setNoBackground(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(DrawingArea_Refresh, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + if (MISSING(x) && MISSING(y) && MISSING(w) && MISSING(h)) + WIDGET->refresh(); + else + { + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, WIDGET->width()); + h = VARGOPT(h, WIDGET->height()); + + WIDGET->refresh(x,y,w,h); + } + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Tablet) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->useTablet()); + else + WIDGET->setUseTablet(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +GB_DESC CDrawingAreaDesc[] = +{ + GB_DECLARE("DrawingArea", sizeof(CDRAWINGAREA)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CDRAWINGAREA_new, "(Parent)Container;"), + + ARRANGEMENT_PROPERTIES, + + GB_PROPERTY("Cached", "b", DrawingArea_Cached), + GB_PROPERTY("Border", "i", DrawingArea_Border), + GB_PROPERTY("Focus","b",DrawingArea_Focus), + GB_PROPERTY("Painted", "b", DrawingArea_Painted), + GB_PROPERTY("NoBackground", "b", DrawingArea_NoBackground), + + GB_PROPERTY("Tablet", "b", DrawingArea_Tablet), + + GB_METHOD("Clear", NULL, DrawingArea_Clear, NULL), + GB_METHOD("Refresh", NULL, DrawingArea_Refresh, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + GB_INTERFACE("Paint", &PAINT_Interface), + + DRAWINGAREA_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CDrawingArea.h b/gb.gtk/src/CDrawingArea.h new file mode 100644 index 00000000..920c1129 --- /dev/null +++ b/gb.gtk/src/CDrawingArea.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CDrawingArea.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAWINGAREA_H +#define __CDRAWINGAREA_H + +#include "main.h" +#include "CWidget.h" +#include "gdrawingarea.h" + +#ifndef __CDRAWINGAREA_CPP +extern GB_DESC CDrawingAreaDesc[]; +#else + +#define THIS ((CDRAWINGAREA *)_object) +#define WIDGET ((gDrawingArea*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; +#ifdef GTK3 + cairo_t *context; +#endif + bool merge; + } + CDRAWINGAREA; + +void CDRAWINGAREA_send_change_event(void); + +#endif diff --git a/gb.gtk/src/CFont.cpp b/gb.gtk/src/CFont.cpp new file mode 100644 index 00000000..d0742b9b --- /dev/null +++ b/gb.gtk/src/CFont.cpp @@ -0,0 +1,447 @@ +/*************************************************************************** + + CFont.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFONT_CPP + +#include <math.h> +#include "CFont.h" +#include "gdesktop.h" +#include "ggambastag.h" + +#include "gb.form.font.h" + +CFONT *CFONT_create(gFont *font, FONT_FUNC func, void *object) +{ + CFONT *fnt; + + if (font && font->getTag()) + return (CFONT *)font->getTagValue(); + + fnt = (CFONT *)GB.New(GB.FindClass("Font"), 0, 0); + + if (font) + { + fnt->font->unref(); + fnt->font = font; + //font->ref(); + font->setTag(new gGambasTag((void *)fnt)); + } + + fnt->func = func; + fnt->object = object; + if (object) + GB.Ref(object); + + return fnt; +} + +BEGIN_METHOD(Font_new, GB_STRING font) + + FONT = new gFont(); + if (!MISSING(font)) + FONT->setFromString(GB.ToZeroString(ARG(font))); + +END_METHOD + + +BEGIN_METHOD_VOID(Font_free) + + GB.Unref(POINTER(&THIS->object)); + gFont::assign(&THIS->font); + +END_METHOD + +static void CFONT_manage(int prop, CFONT *_object, void *_param) +{ + gFont *f = FONT; + double size; + + if (!f) + { + THIS->font = ((CWIDGET *)THIS->object)->widget->font()->copy(); + f = FONT; + } + + if (READ_PROPERTY) + { + switch(prop) + { + case CFONT::Name: GB.ReturnNewZeroString(f->name()); break; + case CFONT::Size: GB.ReturnFloat(f->size()); break; + case CFONT::Grade: GB.ReturnInteger(f->grade()); break; + case CFONT::Bold: GB.ReturnBoolean(f->bold()); break; + case CFONT::Italic: GB.ReturnBoolean(f->italic()); break; + case CFONT::Underline: GB.ReturnBoolean(f->underline()); break; + case CFONT::Strikeout: GB.ReturnBoolean(f->strikeout()); break; + } + } + else + { + switch (prop) + { + case CFONT::Name: f->setName(GB.ToZeroString(PROP(GB_STRING))); break; + case CFONT::Size: + size = VPROP(GB_FLOAT); + if (size <= 0) + { + GB.Error("Bad font size"); + return; + } + f->setSize(VPROP(GB_FLOAT)); + break; + case CFONT::Grade: f->setGrade(VPROP(GB_INTEGER)); break; + case CFONT::Bold: f->setBold(VPROP(GB_BOOLEAN)); break; + case CFONT::Italic: f->setItalic(VPROP(GB_BOOLEAN)); break; + case CFONT::Underline: f->setUnderline(VPROP(GB_BOOLEAN)); break; + case CFONT::Strikeout: f->setStrikeout(VPROP(GB_BOOLEAN)); break; + } + + if (THIS->func) + (*(THIS->func))(f, THIS->object); + else if (THIS->object) + { + // THIS->control->widget->setFont(*f); - Not needed anymore + // TODO Make a Gambas API to call SetProperty faster + + //fprintf(stderr, "applying font to (%s %p)\n", GB.GetClassName(THIS->object), THIS->object); + + GB_FUNCTION func; + + GB.GetFunction(&func, (void *)GB.FindClass("Object"), "SetProperty", NULL, NULL); + GB.Push(3, + GB_T_OBJECT, THIS->object, + GB_T_STRING, "Font", 4, + GB_T_OBJECT, THIS + ); + GB.Call(&func, 3, TRUE); + } + + THIS->modified = TRUE; + } +} + + +BEGIN_PROPERTY(Font_Name) + + CFONT_manage(CFONT::Name, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Size) + + CFONT_manage(CFONT::Size, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Grade) + + CFONT_manage(CFONT::Grade, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Bold) + + CFONT_manage(CFONT::Bold, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Italic) + + CFONT_manage(CFONT::Italic, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Underline) + + CFONT_manage(CFONT::Underline, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Strikeout) + + CFONT_manage(CFONT::Strikeout, OBJECT(CFONT), _param); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Font_ToString) + + GB.ReturnNewZeroString(FONT->toString()); + +END_METHOD + + +BEGIN_METHOD(Font_get, GB_STRING str) + + CFONT *font; + gFont *fnt; + + fnt = new gFont(); + fnt->setFromString(GB.ToZeroString(ARG(str))); + font = CFONT_create(fnt); + + GB.ReturnObject(font); + +END_METHOD + +BEGIN_METHOD_VOID(Font_Copy) + + GB.ReturnObject(CFONT_create(FONT->copy())); + +END_METHOD + + +BEGIN_PROPERTY(Font_Ascent) + + GB.ReturnInteger(FONT->ascent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Descent) + + GB.ReturnInteger(FONT->descent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Height) + + GB.ReturnInteger(FONT->height()); + +END_PROPERTY + + +BEGIN_METHOD(Font_TextWidth, GB_STRING text) + + GB.ReturnInteger(FONT->width(STRING(text), LENGTH(text))); + +END_METHOD + + +BEGIN_METHOD(Font_TextHeight, GB_STRING text) + + GB.ReturnInteger(FONT->height(STRING(text), LENGTH(text))); + +END_METHOD + + +BEGIN_METHOD(Font_TextSize, GB_STRING text) + + GEOM_RECT *rect = GEOM.CreateRect(); + rect->w = FONT->width(STRING(text), LENGTH(text)); + rect->h = FONT->height(STRING(text), LENGTH(text)); + GB.ReturnObject(rect); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextWidth, GB_STRING text; GB_INTEGER width) + + float w; + + FONT->richTextSize(STRING(text), LENGTH(text), VARGOPT(width, -1), &w, NULL); + GB.ReturnInteger(ceil(w)); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextHeight, GB_STRING text; GB_INTEGER width) + + float h; + + FONT->richTextSize(STRING(text), LENGTH(text), VARGOPT(width, -1), NULL, &h); + GB.ReturnInteger(ceil(h)); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextSize, GB_STRING text; GB_INTEGER width) + + float w, h; + GEOM_RECT *rect = GEOM.CreateRect(); + + FONT->richTextSize(STRING(text), LENGTH(text), VARGOPT(width, -1), &w, &h); + + rect->w = ceil(w); + rect->h = ceil(h); + + GB.ReturnObject(rect); + +END_METHOD + + +BEGIN_PROPERTY(Font_Fixed) + + GB.ReturnBoolean(FONT->fixed()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Fonts_next) + + int *pos; + + pos = (int *)GB.GetEnum(); + + if (pos[0] >= gFont::count()) + { + GB.StopEnum (); + return; + } + + GB.ReturnNewZeroString(gFont::familyItem(pos[0]++)); + +END_METHOD + + +BEGIN_METHOD(Fonts_Exist, GB_STRING family) + + int i; + char *family = GB.ToZeroString(ARG(family)); + + for (i = 0; i < gFont::count(); i++) + { + if (strcmp(gFont::familyItem(i), family) == 0) + { + GB.ReturnBoolean(TRUE); + return; + } + } + + GB.ReturnBoolean(FALSE); + +END_METHOD + + +BEGIN_PROPERTY(Fonts_Count) + + GB.ReturnInteger(gFont::count()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Styles) + + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Regular"); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Italic"); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Bold"); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Bold Italic"); + + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Scalable) + + GB.ReturnBoolean(FONT->scalable()); + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(Font_Underline) + + if (READ_PROPERTY) { GB.ReturnBoolean(FONT->underline()); return; } + FONT->setUnderline(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Strikeout) + + if (READ_PROPERTY) { GB.ReturnBoolean(FONT->strikeOut()); return; } + FONT->setStrikeOut(VPROP(GB_BOOLEAN)); + +END_PROPERTY +#endif + +BEGIN_PROPERTY(Font_Modified) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->modified); + else + THIS->modified = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +GB_DESC CFontsDesc[] = +{ + GB_DECLARE_STATIC("Fonts"), + + GB_STATIC_METHOD("_next", "s", Fonts_next, NULL), + GB_STATIC_METHOD("Exist", "b", Fonts_Exist, "(Family)s"), + GB_STATIC_PROPERTY_READ("Count", "i", Fonts_Count), + + GB_END_DECLARE +}; + +GB_DESC CFontDesc[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), + + GB_METHOD("_new", 0, Font_new, "[(Font)s]"), + GB_METHOD("_free", 0, Font_free, 0), + GB_METHOD("Copy", "Font", Font_Copy, NULL), + + GB_PROPERTY("Name", "s", Font_Name), + GB_PROPERTY("Size", "f", Font_Size), + GB_PROPERTY("Bold", "b", Font_Bold), + GB_PROPERTY("Italic", "b", Font_Italic), + GB_PROPERTY("Underline", "b", Font_Underline), + GB_PROPERTY("Strikeout", "b", Font_Strikeout), + GB_PROPERTY("Grade", "i", Font_Grade), + GB_PROPERTY("Modified", "b", Font_Modified), + + GB_METHOD("ToString", "s", Font_ToString, 0), + + GB_METHOD("TextWidth", "i", Font_TextWidth, "(Text)s"), + GB_METHOD("TextHeight", "i", Font_TextHeight, "(Text)s"), + GB_METHOD("TextSize", "Rect", Font_TextSize, "(Text)s"), + + GB_METHOD("RichTextWidth", "i", Font_RichTextWidth, "(Text)s[(Width)i]"), + GB_METHOD("RichTextHeight", "i", Font_RichTextHeight, "(Text)s[(Width)i]"), + GB_METHOD("RichTextSize", "Rect", Font_RichTextSize, "(Text)s[(Width)i]"), + + GB_STATIC_METHOD("_get", "Font", Font_get, "(Font)s"), + #if 0 + GB_STATIC_PROPERTY("Resolution", "i", CFONT_resolution), + #endif + GB_PROPERTY_READ("Ascent", "i", Font_Ascent), + GB_PROPERTY_READ("Descent", "i", Font_Descent), + GB_PROPERTY_READ("Height", "i", Font_Height), + GB_PROPERTY_READ("H", "i", Font_Height), + + GB_PROPERTY_READ("Fixed", "b", Font_Fixed), + GB_PROPERTY_READ("Scalable", "b", Font_Scalable), + GB_PROPERTY_READ("Styles", "String[]", Font_Styles), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CFont.h b/gb.gtk/src/CFont.h new file mode 100644 index 00000000..34996da0 --- /dev/null +++ b/gb.gtk/src/CFont.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + CFont.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFONT_H +#define __CFONT_H + +#include "main.h" +#include "gfont.h" + +#ifndef __CFONT_CPP +extern GB_DESC CFontDesc[]; +extern GB_DESC CFontsDesc[]; + +#else + +#define THIS ((CFONT *)_object) +#define FONT (THIS->font) + +#endif + +typedef + void (*FONT_FUNC)(gFont *, void *); + +typedef struct + { + GB_BASE ob; + gFont *font; + FONT_FUNC func; + void *object; + unsigned modified : 1; + enum { Name, Size, Grade, Bold, Italic, Underline, Strikeout }; + } + CFONT; + +CFONT *CFONT_create(gFont *font, FONT_FUNC func = 0, void *object = 0); + +#endif diff --git a/gb.gtk/src/CImage.cpp b/gb.gtk/src/CImage.cpp new file mode 100644 index 00000000..dff1b139 --- /dev/null +++ b/gb.gtk/src/CImage.cpp @@ -0,0 +1,291 @@ +/*************************************************************************** + + CImage.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "ggambastag.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CScreen.h" +#include "CPicture.h" +#include "CImage.h" + +static void free_image(GB_IMG *img, void *image) +{ + ((gPicture *)image)->unref(); +} + +static void *temp_image(GB_IMG *img) +{ + gPicture *image; + + if (!img->data) + image = new gPicture(); + else + image = gPicture::fromData((const char *)img->data, img->width, img->height); + + image->setTag(new gGambasTag((void *)img)); + + return image; +} + +static GB_IMG_OWNER _image_owner = { + "gb.gtk", + GB_IMAGE_RGBA, + free_image, + free_image, + temp_image, + NULL + }; + +gPicture *CIMAGE_get(CIMAGE *_object) +{ + return (gPicture *)IMAGE.Check(&THIS->img, &_image_owner); +} + +#define check_image CIMAGE_get + +static void take_image(CIMAGE *_object, gPicture *image) +{ + IMAGE.Take(&THIS->img, &_image_owner, image, image->width(), image->height(), image->data()); + + if (!image->getTag()) + image->setTag(new gGambasTag(THIS)); +} + +CIMAGE *CIMAGE_create(gPicture *image) +{ + CIMAGE *img; + static GB_CLASS class_id = 0; + + if (!class_id) + class_id = GB.FindClass("Image"); + + img = (CIMAGE *)GB.New(class_id, NULL, NULL); + + if (image) + take_image(img, image); + else + take_image(img, new gPicture()); + + return img; +} + +/*CIMAGE *CIMAGE_create(gPicture *picture) +{ + CIMAGE *pic; + GB.New((void **)POINTER(&pic), GB.FindClass("Image"), 0, 0); + if (picture) + { + pic->picture->unref(); + pic->picture = picture; + picture->setTag(new gGambasTag((void *)pic)); + } + return pic; +}*/ + +void* GTK_GetImage(GdkPixbuf *buf) +{ + CIMAGE *pic = CIMAGE_create(new gPicture(buf)); + g_object_ref(buf); + return (void*)pic; +} + +/******************************************************************************* + + Image + +*******************************************************************************/ + +#if 0 +BEGIN_METHOD(CIMAGE_new, GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN trans) + + int w = VARGOPT(w, 0); + int h = VARGOPT(h, 0); + bool trans = VARGOPT(trans, false); + + IMAGE = new gPicture(gPicture::MEMORY, w, h, trans); + PICTURE->setTag(new gGambasTag((void *)THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_free) + + PICTURE->unref(); + +END_METHOD +#endif + +BEGIN_PROPERTY(Image_Picture) + + check_image(THIS); + + //GB.ReturnObject(THIS); + + CPICTURE *pic = CPICTURE_create(PICTURE->copy()); + GB.ReturnObject(pic); + +END_PROPERTY + + +BEGIN_METHOD(Image_Load, GB_STRING path) + + CIMAGE *image; + char *addr; + int len; + + if (!GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + //fprintf(stderr, "Image_Load: %.*s\n", LENGTH(path), STRING(path)); + gPicture *pic = gPicture::fromMemory(addr, len); + GB.ReleaseFile(addr, len); + + if (pic) + { + image = CIMAGE_create(pic); + pic->getPixbuf(); + GB.ReturnObject(image); + return; + } + } + + GB.Error("Unable to load image"); + +END_METHOD + + +BEGIN_METHOD(Image_FromString, GB_STRING data) + + CIMAGE *image; + + gPicture *pic = gPicture::fromMemory(STRING(data), LENGTH(data)); + + if (pic) + { + image = CIMAGE_create(pic); + pic->getPixbuf(); + GB.ReturnObject(image); + return; + } + + GB.Error("Unable to load image"); + +END_METHOD + + +BEGIN_METHOD(Image_Save, GB_STRING path; GB_INTEGER quality) + + check_image(THIS); + + switch (PICTURE->save(GB.FileName(STRING(path), LENGTH(path)), VARGOPT(quality, -1))) + { + case 0: break; + case -1: GB.Error("Unknown format"); break; + case -2: GB.Error("Unable to save picture"); break; + } + +END_METHOD + + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height; GB_BOOLEAN fast) + + CIMAGE *img; + + check_image(THIS); + img = CIMAGE_create(PICTURE->stretch(VARG(width), VARG(height), !VARGOPT(fast, FALSE))); + GB.ReturnObject((void*)img); + +END_METHOD + + +BEGIN_METHOD(Image_Rotate, GB_FLOAT angle) + + CIMAGE *img; + gPicture *pic, *pic2; + + check_image(THIS); + pic = PICTURE->stretch(PICTURE->width() * 2, PICTURE->height() * 2, false); + pic2 = pic->rotate(VARG(angle)); + pic->unref(); + pic = pic2->stretch(pic2->width() / 2, pic2->height() / 2, true); + pic2->unref(); + img = CIMAGE_create(pic); + GB.ReturnObject((void*)img); + +END_METHOD + + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT img; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + int x, y, w, h, sx, sy, sw, sh; + CIMAGE *image = (CIMAGE *)VARG(img); + gPicture *src; + + if (GB.CheckObject(image)) + return; + + src = check_image(image); + check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, -1); + sh = VARGOPT(sh, -1); + + //DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, pic->width(), pic->height()); + + PICTURE->draw(src, x, y, w, h, sx, sy, sw, sh); + +END_METHOD + + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Image", Image_FromString, "(Data)s"), + GB_METHOD("Save", 0, Image_Save, "(Path)s[(Quality)i]"), + + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i[(Fast)b]"), + GB_METHOD("Rotate", "Image", Image_Rotate, "(Angle)f"), + + GB_METHOD("PaintImage", 0, Image_PaintImage, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_PROPERTY_READ("Picture", "Picture", Image_Picture), + + GB_INTERFACE("Paint", &PAINT_Interface), + GB_INTERFACE("PaintMatrix", &PAINT_MATRIX_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CImage.h b/gb.gtk/src/CImage.h new file mode 100644 index 00000000..84a34c1d --- /dev/null +++ b/gb.gtk/src/CImage.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "gambas.h" +#include "gb.image.h" +#include "widgets.h" + +typedef + struct + { + GB_IMG img; + } + CIMAGE; + +#ifndef __CIMAGE_CPP + +extern GB_DESC CImageDesc[]; + +#else + +#define THIS ((CIMAGE *)_object) +#define PICTURE ((gPicture *)THIS->img.temp_handle) +#define GET_PICTURE(_image) ((gPicture *)(_image->img.temp_handle)) + +#endif + +void *GTK_GetImage(GdkPixbuf *buf); + +CIMAGE *CIMAGE_create(gPicture *picture); +gPicture *CIMAGE_get(CIMAGE *); + +#endif diff --git a/gb.gtk/src/CKey.cpp b/gb.gtk/src/CKey.cpp new file mode 100644 index 00000000..bd0caf68 --- /dev/null +++ b/gb.gtk/src/CKey.cpp @@ -0,0 +1,229 @@ +/*************************************************************************** + + CKey.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CKEY_CPP + +#include "CKey.h" +#include <gdk/gdkkeysyms.h> +#include "gkey.h" + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Key_get, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + int val = KEY_get_keyval_from_name(key); + + if (!val) + val = gKey::fromString(key); + + GB.ReturnInteger(val); + +END_METHOD + +#define CHECK_VALID() \ + if (!gKey::isValid()) \ + { \ + GB.Error("No keyboard event data"); \ + return; \ + } + +BEGIN_PROPERTY(Key_Text) + + CHECK_VALID(); + GB.ReturnNewZeroString(gKey::text()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Code) + + CHECK_VALID(); + GB.ReturnInteger(gKey::code()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_State) + + CHECK_VALID(); + GB.ReturnInteger(gKey::state()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shift) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::shift()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Control) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::control()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Alt) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::alt()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Meta) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::meta()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Normal) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::normal()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shortcut) + + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + init = TRUE; + GB.GetFunction(&func, (void *)GB.FindClass("Shortcut"), "FromKey", NULL, "s"); + } + + if (GB_FUNCTION_IS_VALID(&func)) + GB.Call(&func, 0, FALSE); + else + GB.ReturnNull(); + +END_PROPERTY + +GB_DESC CKeyDesc[] = +{ + GB_DECLARE("Key", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", "i", Key_get, "(Key)s"), + + GB_CONSTANT("Esc", "i", GDK_Escape), + GB_CONSTANT("Escape", "i", GDK_Escape), + GB_CONSTANT("Tab", "i", GDK_Tab), + GB_CONSTANT("BackTab", "i", GDK_ISO_Left_Tab), + GB_CONSTANT("Backspace", "i", GDK_BackSpace), + GB_CONSTANT("Return", "i", GDK_Return), + GB_CONSTANT("Enter", "i", GDK_KP_Enter), + GB_CONSTANT("Ins", "i", GDK_Insert), + GB_CONSTANT("Del", "i", GDK_Delete), + GB_CONSTANT("Insert", "i", GDK_Insert), + GB_CONSTANT("Delete", "i", GDK_Delete), + GB_CONSTANT("Pause", "i", GDK_Pause), + GB_CONSTANT("Print", "i", GDK_Print), + GB_CONSTANT("SysReq", "i", GDK_Sys_Req), + GB_CONSTANT("Home", "i", GDK_Home), + GB_CONSTANT("End", "i", GDK_End), + GB_CONSTANT("Left", "i", GDK_Left), + GB_CONSTANT("Up", "i", GDK_Up), + GB_CONSTANT("Right", "i", GDK_Right), + GB_CONSTANT("Down", "i", GDK_Down), + GB_CONSTANT("PgUp", "i", GDK_Page_Up), + GB_CONSTANT("PgDown", "i", GDK_Page_Down), + GB_CONSTANT("PageUp", "i", GDK_Page_Up), + GB_CONSTANT("PageDown", "i", GDK_Page_Down), + GB_CONSTANT("ShiftKey", "i", GDK_Shift_L), + GB_CONSTANT("ControlKey", "i", GDK_Control_L), + GB_CONSTANT("MetaKey", "i", GDK_Meta_L), + GB_CONSTANT("AltKey", "i", GDK_Alt_L), + GB_CONSTANT("AltGrKey", "i", GDK_ISO_Level3_Shift), + GB_CONSTANT("CapsLock", "i", GDK_Caps_Lock), + GB_CONSTANT("NumLock", "i", GDK_Num_Lock), + GB_CONSTANT("ScrollLock", "i", GDK_Scroll_Lock), + GB_CONSTANT("F1", "i", GDK_F1), + GB_CONSTANT("F2", "i", GDK_F2), + GB_CONSTANT("F3", "i", GDK_F3), + GB_CONSTANT("F4", "i", GDK_F4), + GB_CONSTANT("F5", "i", GDK_F5), + GB_CONSTANT("F6", "i", GDK_F6), + GB_CONSTANT("F7", "i", GDK_F7), + GB_CONSTANT("F8", "i", GDK_F8), + GB_CONSTANT("F9", "i", GDK_F9), + GB_CONSTANT("F10", "i", GDK_F10), + GB_CONSTANT("F11", "i", GDK_F11), + GB_CONSTANT("F12", "i", GDK_F12), + GB_CONSTANT("F13", "i", GDK_F13), + GB_CONSTANT("F14", "i", GDK_F14), + GB_CONSTANT("F15", "i", GDK_F15), + GB_CONSTANT("F16", "i", GDK_F16), + GB_CONSTANT("F17", "i", GDK_F17), + GB_CONSTANT("F18", "i", GDK_F18), + GB_CONSTANT("F19", "i", GDK_F19), + GB_CONSTANT("F20", "i", GDK_F20), + GB_CONSTANT("F21", "i", GDK_F21), + GB_CONSTANT("F22", "i", GDK_F22), + GB_CONSTANT("F23", "i", GDK_F23), + GB_CONSTANT("F24", "i", GDK_F24), + GB_CONSTANT("Menu", "i", GDK_Menu), + GB_CONSTANT("Help", "i", GDK_Help), + GB_CONSTANT("Space", "i", GDK_space), + + GB_STATIC_PROPERTY_READ("Text", "s", Key_Text), + GB_STATIC_PROPERTY_READ("Code", "i", Key_Code), + GB_STATIC_PROPERTY_READ("State", "i", Key_State), + GB_STATIC_PROPERTY_READ("Shift", "b", Key_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Key_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Key_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Key_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Key_Normal), + + GB_STATIC_PROPERTY_READ("Shortcut", "s", Key_Shortcut), + + GB_END_DECLARE +}; + + +//------------------------------------------------------------------------- + +int KEY_get_keyval_from_name(const char *name) +{ + const GB_DESC *p; + const char *pname; + + if (!name || !*name) + return 0; + + if (!name[1]) + return gKey::fromString(name); + + for(p = &CKeyDesc[3]; (pname = p->name); p++) + { + if (*pname != GB_CONSTANT_ID) + continue; + if (strcasecmp(name, &pname[1]) == 0) + return (int)p->val2; + } + + return gKey::fromString(name); +} + diff --git a/gb.gtk/src/CKey.h b/gb.gtk/src/CKey.h new file mode 100644 index 00000000..fae0d51d --- /dev/null +++ b/gb.gtk/src/CKey.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + CKey.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CKEY_H +#define __CKEY_H + +#include "main.h" + +#ifndef __CKEY_CPP +extern GB_DESC CKeyDesc[]; +#endif + +int KEY_get_keyval_from_name(const char *name); + +#endif diff --git a/gb.gtk/src/CMenu.cpp b/gb.gtk/src/CMenu.cpp new file mode 100644 index 00000000..e5177af0 --- /dev/null +++ b/gb.gtk/src/CMenu.cpp @@ -0,0 +1,566 @@ +/*************************************************************************** + + CMenu.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMENU_CPP + +#include "main.h" +#include "gambas.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CPicture.h" +#include "CMenu.h" + + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); + +static CMENU *_popup_menu_clicked = NULL; +static GB_FUNCTION _init_shortcut_func; + +#define HANDLE_PROXY(_ob) \ + while (((CMENU *)(_ob))->widget->proxy()) \ + _ob = (__typeof__ _ob)(((CMENU *)(_ob))->widget->proxy()->hFree); + + +static void send_click_event(void *_object) +{ + GB.Raise(THIS, EVENT_Click, 0); + CACTION_raise(THIS); + GB.Unref(POINTER(&_object)); +} + +static int CMENU_check(void *_object) +{ + return (MENU == NULL || MENU->isDestroyed()); +} + +static void delete_menu(gMenu *menu) +{ + void *_object = menu->hFree; + + if (!MENU) + return; + + menu->destroy(); + THIS->widget = NULL; +} + +void CB_menu_finish(gMenu *sender) +{ + CMENU *_object = (CMENU*)sender->hFree; + if (_object) + { + CACTION_register(THIS, THIS->action, NULL); + GB.FreeString(&THIS->action); + THIS->widget = NULL; + GB.StoreVariant(NULL, POINTER(&THIS->tag)); + GB.Unref(POINTER(&_object)); + } +} + +void CB_menu_click(gMenu *sender) +{ + void *_object = sender->hFree; + + GB.Ref(THIS); + + if (gMenu::insidePopup()) + { + GB.Unref(POINTER(&_popup_menu_clicked)); + _popup_menu_clicked = THIS; + } + else + GB.Post((GB_CALLBACK)send_click_event, (intptr_t)THIS); +} + +void CB_menu_show(gMenu *sender) +{ + static bool init = FALSE; + + void *_object = sender->hFree; + + GB.Ref(THIS); + + GB.Raise(THIS, EVENT_Show, 0); + + if (!THIS->init_shortcut) + { + if (!init) + { + GB.GetFunction(&_init_shortcut_func, (void *)GB.FindClass("_Gui"), "_DefineShortcut", NULL, NULL); + init = TRUE; + } + + THIS->init_shortcut = TRUE; + GB.Push(1, GB_T_OBJECT, THIS); + GB.Call(&_init_shortcut_func, 1, FALSE); + } + + GB.Unref(POINTER(&_object)); +} + +void CB_menu_hide(gMenu *sender) +{ + void *_object = sender->hFree; + GB.Raise(THIS, EVENT_Hide, 0); +} + +void CMENU_check_popup_click(void) +{ + if (_popup_menu_clicked) + { + CMENU *menu = _popup_menu_clicked; + _popup_menu_clicked = NULL; + send_click_event(menu); + } +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Menu_new, GB_OBJECT parent; GB_BOOLEAN hidden) + + void *parent = VARG(parent); + bool hidden; + char *name; + + hidden = VARGOPT(hidden, false); + + if (GB.Is(parent, CLASS_Window)) + { + if (!((CWINDOW*)parent)->ob.widget) + { + GB.Error("Invalid window"); + return; + } + + THIS->widget = new gMenu((gMainWindow*)((CWINDOW*)parent)->ob.widget, hidden); + goto __OK; + } + + if (GB.Is(parent, CLASS_Menu)) + { + if ( !((CMENU*)parent)->widget ) + { + GB.Error("Invalid menu"); + return; + } + + THIS->widget = new gMenu((gMenu*)((CMENU*)parent)->widget, hidden); + goto __OK; + } + + GB.Error("Type mismatch. The parent control of a Menu must be a Window or another Menu."); + return; + +__OK: + + MENU->hFree = (void*)THIS; + + name = GB.GetLastEventName(); + if (!name) + name = GB.GetClassName((void *)THIS); + + MENU->setName(name); + + GB.Ref((void*)THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_free) + + GB.FreeString(&THIS->save_text); + if (MENU) MENU->destroy(); + +END_METHOD + + +BEGIN_PROPERTY(Menu_Text) + + if (READ_PROPERTY) + { + if (THIS->save_text) + GB.ReturnString(THIS->save_text); + else + GB.ReturnNewZeroString(MENU->text()); + return; + } + else + { + MENU->setText(GB.ToZeroString(PROP(GB_STRING))); + + if (!MENU->isTopLevel()) + ((CMENU *)GetObject((gMenu *)MENU->parent()))->init_shortcut = FALSE; + + GB.FreeString(&THIS->save_text); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Picture) + + if (READ_PROPERTY) + { + gPicture *pic = MENU->picture(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + MENU->setPicture(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->isEnabled()); + else + MENU->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Checked) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->checked()); + else + MENU->setChecked(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Value) + + if (MENU->toggle() || MENU->radio()) + { + Menu_Checked(_object, _param); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(0); + } + else if (!MENU->isTopLevel()) + { + GB.Ref(THIS); + send_click_event(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Shortcut) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(MENU->shortcut()); + else + MENU->setShortcut(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->isVisible()); + else + MENU->setVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Menu_Show) + + MENU->setVisible(true); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Hide) + + MENU->setVisible(false); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Delete) + + delete_menu(MENU); + +END_METHOD + + +BEGIN_PROPERTY(MenuChildren_Count) + + GB.ReturnInteger(MENU->childCount()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(MenuChildren_next) + + gMenu *mn; + int *ct; + + ct = (int*)GB.GetEnum(); + + if (*ct >= MENU->childCount()) + { + GB.StopEnum(); + return; + } + + mn = MENU->child(*ct); + (*ct)++; + + GB.ReturnObject(mn->hFree); + +END_PROPERTY + + +BEGIN_METHOD(MenuChildren_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= MENU->childCount()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(MENU->child(index)->hFree); + +END_METHOD + + +BEGIN_METHOD_VOID(MenuChildren_Clear) + + while (MENU->childCount()) + delete_menu(MENU->child(0)); + + THIS->init_shortcut = FALSE; + +END_PROPERTY + + +BEGIN_METHOD(Menu_Popup, GB_INTEGER x; GB_INTEGER y) + + HANDLE_PROXY(_object); + + if (!MISSING(x) && !MISSING(y)) + MENU->popup(VARG(x), VARG(y)); + else + MENU->popup(); + + CMENU_check_popup_click(); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Close) + + HANDLE_PROXY(_object); + MENU->close(); + +END_METHOD + + +BEGIN_PROPERTY(Menu_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->tag); + +END_METHOD + + +BEGIN_PROPERTY(Menu_Toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->toggle()); + else + MENU->setToggle(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->radio()); + else + MENU->setRadio(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Window) + + GB.ReturnObject(GetObject(MENU->window())); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Parent) + + if (MENU->isTopLevel()) + GB.ReturnNull(); + else + GB.ReturnObject(GetObject(MENU->parentMenu())); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(MENU->name()); + else + MENU->setName(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Action) + + if (READ_PROPERTY) + GB.ReturnString(THIS->action); + else + { + CACTION_register(THIS, THIS->action, GB.ToZeroString(PROP(GB_STRING))); + GB.StoreString(PROP(GB_STRING), &THIS->action); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_SaveText) + + if (READ_PROPERTY) + GB.ReturnString(THIS->save_text); + else + GB.StoreString(PROP(GB_STRING), &THIS->save_text); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Proxy) + + if (READ_PROPERTY) + { + gMenu *proxy = MENU->proxy(); + GB.ReturnObject(proxy ? proxy->hFree : NULL); + } + else + { + CMENU *menu = (CMENU *)VPROP(GB_OBJECT); + + if (menu && GB.CheckObject(menu)) + return; + + if (MENU->setProxy(menu ? menu->widget : NULL)) + GB.Error("Circular proxy chain"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Closed) + + HANDLE_PROXY(_object); + GB.ReturnBoolean(MENU->isClosed()); + +END_PROPERTY + + +//--------------------------------------------------------------------------- + +GB_DESC CMenuChildrenDesc[] = +{ + GB_DECLARE(".Menu.Children", sizeof(CMENU)), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", MenuChildren_next, 0), + GB_METHOD("_get", "Menu", MenuChildren_get, "(Index)i"), + GB_METHOD("Clear", 0, MenuChildren_Clear, 0), + GB_PROPERTY_READ("Count", "i", MenuChildren_Count), + + GB_END_DECLARE +}; + + +GB_DESC CMenuDesc[] = +{ + GB_DECLARE("Menu", sizeof(CMENU)), + GB_HOOK_CHECK(CMENU_check), + + //GB_STATIC_METHOD("_init", 0, CMENU_init, 0), + GB_METHOD("_new", 0, Menu_new, "(Parent)o[(Hidden)b]"), + GB_METHOD("_free", 0, Menu_free, 0), + + GB_PROPERTY("Name", "s", Menu_Name), + GB_PROPERTY("Caption", "s", Menu_Text), + GB_PROPERTY("Text", "s", Menu_Text), + GB_PROPERTY("_Text", "s", Menu_SaveText), + GB_PROPERTY("Enabled", "b", Menu_Enabled), + GB_PROPERTY("Checked", "b", Menu_Checked), + GB_PROPERTY("Tag", "v", Menu_Tag), + GB_PROPERTY("Picture", "Picture", Menu_Picture), + GB_PROPERTY("Shortcut", "s", Menu_Shortcut), + GB_PROPERTY("Visible", "b", Menu_Visible), + GB_PROPERTY("Toggle", "b", Menu_Toggle), + GB_PROPERTY("Radio", "b", Menu_Radio), + GB_PROPERTY("Value", "b", Menu_Value), + //GB_PROPERTY("TearOff", "b", CMENU_tear_off), + GB_PROPERTY("Action", "s", Menu_Action), + GB_PROPERTY_READ("Parent", "Menu", Menu_Parent), + GB_PROPERTY_READ("Window", "Window", Menu_Window), + GB_PROPERTY("Proxy", "Menu", Menu_Proxy), + + GB_PROPERTY_SELF("Children", ".Menu.Children"), + + MENU_DESCRIPTION, + + GB_METHOD("Popup", 0, Menu_Popup, "[(X)i(Y)i]"), + GB_METHOD("Close", NULL, Menu_Close, NULL), + GB_METHOD("Delete", 0, Menu_Delete, 0), + GB_METHOD("Show", 0, Menu_Show, 0), + GB_METHOD("Hide", 0, Menu_Hide, 0), + + GB_PROPERTY_READ("Closed", "b", Menu_Closed), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + GB_EVENT("Show", 0, 0, &EVENT_Show), + GB_EVENT("Hide", 0, 0, &EVENT_Hide), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CMenu.h b/gb.gtk/src/CMenu.h new file mode 100644 index 00000000..c514c241 --- /dev/null +++ b/gb.gtk/src/CMenu.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + CMenu.h + + (c) 2004-2005 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMENU_H +#define __CMENU_H + +#include "main.h" +#include "gmenu.h" +#include "CPicture.h" + +#ifndef __CMENU_CPP +extern GB_DESC CMenuDesc[]; +extern GB_DESC CMenuChildrenDesc[]; +#else + +#define THIS ((CMENU *)_object) +#define MENU ((gMenu *)THIS->widget) + +#define GET_MENU(_widget) ((gMenu *)_widget)->hFree ? ((gMenu *)_widget)->hFree : NULL + +#endif + +typedef + struct + { + GB_BASE ob; + gMenu *widget; + GB_VARIANT_VALUE tag; + char *action; + char *save_text; + unsigned init_shortcut : 1; + } + CMENU; + +void CMENU_check_popup_click(void); +void CMENU_update_menubar(void *window); + +#endif diff --git a/gb.gtk/src/CMouse.cpp b/gb.gtk/src/CMouse.cpp new file mode 100644 index 00000000..efe20d28 --- /dev/null +++ b/gb.gtk/src/CMouse.cpp @@ -0,0 +1,472 @@ +/*************************************************************************** + + CMouse.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOUSE_CPP + +#include "CMouse.h" + +//------------------------------------------------------------------------- + +BEGIN_METHOD(CCURSOR_new, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y) + + CPICTURE *Pic=(CPICTURE*)VARG(picture); + gPicture *pic=NULL; + long X=0,Y=0; + + if (!MISSING(x)) X=VARG(x); + if (!MISSING(y)) Y=VARG(y); + if (Pic) pic=Pic->picture; + + THIS->cur=new gCursor(pic,X,Y); + +END_METHOD + + +BEGIN_METHOD_VOID(CCURSOR_delete) + + delete THIS->cur; + +END_METHOD + + +BEGIN_PROPERTY(CCURSOR_x) + + GB.ReturnInteger(THIS->cur->left()); + +END_PROPERTY + + +BEGIN_PROPERTY(CCURSOR_y) + + GB.ReturnInteger(THIS->cur->top()); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Mouse_ScreenX) + + GB.ReturnInteger(gMouse::screenX()); + +END_PROPERTY + + +BEGIN_PROPERTY(Mouse_ScreenY) + + GB.ReturnInteger(gMouse::screenY()); + +END_PROPERTY + + +BEGIN_METHOD(Mouse_Move, GB_INTEGER x; GB_INTEGER y) + + gMouse::move(VARG(x),VARG(y)); + +END_PROPERTY + +#define CHECK_VALID() \ + if (!gMouse::isValid()) \ + { \ + GB.Error("No mouse event data"); \ + return; \ + } + +BEGIN_PROPERTY(Mouse_X) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::x()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Y) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::y()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartX) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::startX()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartY) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::startY()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Button) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::button()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_State) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::state()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Left) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::left()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Right) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::right()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Middle) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::middle()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Shift) + + //CHECK_VALID(); + GB.ReturnBoolean(gMouse::shift()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Control) + + //CHECK_VALID(); + GB.ReturnBoolean(gMouse::control()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Alt) + + //CHECK_VALID(); + GB.ReturnBoolean(gMouse::alt()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Meta) + + //CHECK_VALID(); + GB.ReturnBoolean(gMouse::meta()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Normal) + + //CHECK_VALID(); + GB.ReturnBoolean(gMouse::normal()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Delta) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::delta()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Orientation) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::orientation()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Forward) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::delta() > 0); + +END_PROPERTY + +BEGIN_METHOD(Mouse_Inside, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + gControl *widget; + int x, y, xw, yw; + + if (GB.CheckObject(control)) + return; + + widget = control->widget; + + if (!widget->isVisible()) + { + GB.ReturnBoolean(false); + return; + } + + gMouse::getScreenPos(&x, &y); + widget->getScreenPos(&xw, &yw); + x -= xw; + y -= yw; + GB.ReturnBoolean(x >= 0 && x < widget->width() && y >= 0 && y < widget->height()); + +END_METHOD + +BEGIN_METHOD(Mouse_Translate, GB_INTEGER dx; GB_INTEGER dy) + + CHECK_VALID(); + gMouse::translate(VARG(dx), VARG(dy)); + +END_METHOD + +BEGIN_PROPERTY(Mouse_Click) + + GB.ReturnInteger(gMouse::clickCount()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Pointer_ScreenX) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerScreenX()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_ScreenY) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerScreenY()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_X) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerX()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Y) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerY()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_XTilt) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_XTILT)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_YTilt) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_YTILT)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Pressure) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_PRESSURE)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Rotation) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_WHEEL)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Type) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::getType()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CCursorDesc[] = +{ + GB_DECLARE("Cursor", sizeof(CCURSOR)), + + GB_METHOD("_new", 0, CCURSOR_new, "(Picture)Picture;[(X)i(Y)i]"), + GB_METHOD("_free", 0, CCURSOR_delete, NULL), + + GB_CONSTANT("Custom", "i", CURSOR_CUSTOM), + GB_CONSTANT("Default", "i", CURSOR_DEFAULT), + + GB_CONSTANT("Blank", "i", CURSOR_NONE), + GB_CONSTANT("Arrow", "i", CURSOR_ARROW), + GB_CONSTANT("Cross", "i", CURSOR_CROSSHAIR), + GB_CONSTANT("Wait", "i", CURSOR_WAIT), + GB_CONSTANT("Text", "i", CURSOR_TEXT), + GB_CONSTANT("SizeAll", "i", CURSOR_MOVE), + GB_CONSTANT("SizeH", "i", CURSOR_EW_RESIZE), + GB_CONSTANT("SizeV", "i", CURSOR_NS_RESIZE), + GB_CONSTANT("SizeN", "i", CURSOR_N_RESIZE), + GB_CONSTANT("SizeS", "i", CURSOR_S_RESIZE), + GB_CONSTANT("SizeW", "i", CURSOR_W_RESIZE), + GB_CONSTANT("SizeE", "i", CURSOR_E_RESIZE), + GB_CONSTANT("SizeNW", "i", CURSOR_NW_RESIZE), //FDiag + GB_CONSTANT("SizeSE", "i", CURSOR_SE_RESIZE), + GB_CONSTANT("SizeNE", "i", CURSOR_NE_RESIZE), //BDiag + GB_CONSTANT("SizeSW", "i", CURSOR_SW_RESIZE), + GB_CONSTANT("SizeNWSE", "i", CURSOR_NWSE_RESIZE), + GB_CONSTANT("SizeNESW", "i", CURSOR_NESW_RESIZE), + GB_CONSTANT("SplitH", "i", CURSOR_COL_RESIZE), // SplitH + GB_CONSTANT("SplitV", "i", CURSOR_ROW_RESIZE), // SplitV + GB_CONSTANT("Pointing", "i", CURSOR_POINTER), + + GB_CONSTANT("None", "i", CURSOR_NONE), + GB_CONSTANT("Help", "i", CURSOR_HELP), + GB_CONSTANT("Pointer", "i", CURSOR_POINTER), + GB_CONSTANT("ContextMenu", "i", CURSOR_CONTEXT_MENU), + GB_CONSTANT("Progress", "i", CURSOR_PROGRESS), + GB_CONSTANT("Cell", "i", CURSOR_CELL), + GB_CONSTANT("CrossHair", "i", CURSOR_CROSSHAIR), + GB_CONSTANT("VerticalText", "i", CURSOR_VERTICAL_TEXT), + GB_CONSTANT("Alias", "i", CURSOR_ALIAS), + GB_CONSTANT("Copy", "i", CURSOR_COPY), + GB_CONSTANT("NoDrop", "i", CURSOR_NO_DROP), + GB_CONSTANT("Move", "i", CURSOR_MOVE), + GB_CONSTANT("NotAllowed", "i", CURSOR_NOT_ALLOWED), + GB_CONSTANT("Grab", "i", CURSOR_GRAB), + GB_CONSTANT("Grabbing", "i", CURSOR_GRABBING), + GB_CONSTANT("AllScroll", "i", CURSOR_ALL_SCROLL), + GB_CONSTANT("ColResize", "i", CURSOR_COL_RESIZE), + GB_CONSTANT("RowResize", "i", CURSOR_ROW_RESIZE), + GB_CONSTANT("NResize", "i", CURSOR_N_RESIZE), + GB_CONSTANT("EResize", "i", CURSOR_E_RESIZE), + GB_CONSTANT("SResize", "i", CURSOR_S_RESIZE), + GB_CONSTANT("WResize", "i", CURSOR_W_RESIZE), + GB_CONSTANT("NEResize", "i", CURSOR_NE_RESIZE), + GB_CONSTANT("NWResize", "i", CURSOR_NW_RESIZE), + GB_CONSTANT("SWResize", "i", CURSOR_SW_RESIZE), + GB_CONSTANT("SEResize", "i", CURSOR_SE_RESIZE), + GB_CONSTANT("EWResize", "i", CURSOR_EW_RESIZE), + GB_CONSTANT("NSResize", "i", CURSOR_NS_RESIZE), + GB_CONSTANT("NESWResize", "i", CURSOR_NESW_RESIZE), + GB_CONSTANT("NWSEResize", "i", CURSOR_NWSE_RESIZE), + GB_CONSTANT("ZoomIn", "i", CURSOR_ZOOM_IN), + GB_CONSTANT("ZoomOut", "i", CURSOR_ZOOM_OUT), + + GB_PROPERTY_READ("X", "i", CCURSOR_x), + GB_PROPERTY_READ("Y", "i", CCURSOR_y), + + GB_END_DECLARE +}; + + +GB_DESC CMouseDesc[] = +{ + GB_DECLARE_VIRTUAL("Mouse"), + + GB_STATIC_PROPERTY_READ("ScreenX", "i", Mouse_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "i", Mouse_ScreenY), + GB_STATIC_METHOD("Move", 0, Mouse_Move, "(X)i(Y)i"), + GB_STATIC_METHOD("Inside", "b", Mouse_Inside, "(Control)Control"), + + GB_CONSTANT("Custom", "i", CURSOR_CUSTOM), + GB_CONSTANT("Default", "i", CURSOR_DEFAULT), + GB_CONSTANT("Blank", "i", CURSOR_NONE), + GB_CONSTANT("Arrow", "i", CURSOR_ARROW), + GB_CONSTANT("Cross", "i", CURSOR_CROSSHAIR), + GB_CONSTANT("Wait", "i", CURSOR_WAIT), + GB_CONSTANT("Text", "i", CURSOR_TEXT), + GB_CONSTANT("SizeAll", "i", CURSOR_MOVE), + GB_CONSTANT("SizeH", "i", CURSOR_EW_RESIZE), + GB_CONSTANT("SizeV", "i", CURSOR_NS_RESIZE), + GB_CONSTANT("SizeN", "i", CURSOR_N_RESIZE), + GB_CONSTANT("SizeS", "i", CURSOR_S_RESIZE), + GB_CONSTANT("SizeW", "i", CURSOR_W_RESIZE), + GB_CONSTANT("SizeE", "i", CURSOR_E_RESIZE), + GB_CONSTANT("SizeNW", "i", CURSOR_NW_RESIZE), //FDiag + GB_CONSTANT("SizeSE", "i", CURSOR_SE_RESIZE), + GB_CONSTANT("SizeNE", "i", CURSOR_NE_RESIZE), //BDiag + GB_CONSTANT("SizeSW", "i", CURSOR_SW_RESIZE), + GB_CONSTANT("SizeNWSE", "i", CURSOR_NWSE_RESIZE), + GB_CONSTANT("SizeNESW", "i", CURSOR_NESW_RESIZE), + GB_CONSTANT("SplitH", "i", CURSOR_COL_RESIZE), // SplitH + GB_CONSTANT("SplitV", "i", CURSOR_ROW_RESIZE), // SplitV + GB_CONSTANT("Pointing", "i", CURSOR_POINTER), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_STATIC_PROPERTY_READ("X", "i", Mouse_X), + GB_STATIC_PROPERTY_READ("Y", "i", Mouse_Y), + GB_STATIC_PROPERTY_READ("StartX", "i", Mouse_StartX), + GB_STATIC_PROPERTY_READ("StartY", "i", Mouse_StartY), + GB_STATIC_PROPERTY_READ("Left", "b", Mouse_Left), + GB_STATIC_PROPERTY_READ("Right", "b", Mouse_Right), + GB_STATIC_PROPERTY_READ("Middle", "b", Mouse_Middle), + GB_STATIC_PROPERTY_READ("Button", "i", Mouse_Button), + GB_STATIC_PROPERTY_READ("State", "i", Mouse_State), + GB_STATIC_PROPERTY_READ("Shift", "b", Mouse_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Mouse_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Mouse_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Mouse_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Mouse_Normal), + GB_STATIC_PROPERTY_READ("Orientation", "i", Mouse_Orientation), + GB_STATIC_PROPERTY_READ("Delta", "f", Mouse_Delta), + GB_STATIC_PROPERTY_READ("Forward", "b", Mouse_Forward), + GB_STATIC_PROPERTY_READ("Click", "i", Mouse_Click), + + GB_STATIC_METHOD("Translate", NULL, Mouse_Translate, "(DX)i(DY)i"), + + GB_END_DECLARE +}; + +GB_DESC CPointerDesc[] = +{ + GB_DECLARE_VIRTUAL("Pointer"), + + GB_CONSTANT("Mouse", "i", POINTER_MOUSE), + GB_CONSTANT("Pen", "i", POINTER_PEN), + GB_CONSTANT("Eraser", "i", POINTER_ERASER), + GB_CONSTANT("Cursor", "i", POINTER_CURSOR), + + GB_STATIC_PROPERTY_READ("Type", "i", Pointer_Type), + GB_STATIC_PROPERTY_READ("X", "f", Pointer_X), + GB_STATIC_PROPERTY_READ("Y", "f", Pointer_Y), + GB_STATIC_PROPERTY_READ("ScreenX", "f", Pointer_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "f", Pointer_ScreenY), + GB_STATIC_PROPERTY_READ("XTilt", "f", Pointer_XTilt), + GB_STATIC_PROPERTY_READ("YTilt", "f", Pointer_YTilt), + GB_STATIC_PROPERTY_READ("Pressure", "f", Pointer_Pressure), + GB_STATIC_PROPERTY_READ("Rotation", "f", Pointer_Rotation), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CMouse.h b/gb.gtk/src/CMouse.h new file mode 100644 index 00000000..305392ca --- /dev/null +++ b/gb.gtk/src/CMouse.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CMouse.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOUSE_H +#define __CMOUSE_H + +#include "main.h" +#include "CPicture.h" +#include "gmouse.h" + +#ifndef __CMOUSE_CPP + +extern GB_DESC CMouseDesc[]; +extern GB_DESC CCursorDesc[]; +extern GB_DESC CPointerDesc[]; + +#else + +#define THIS ((CCURSOR *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + gCursor *cur; + } + CCURSOR; + +#define MOUSE_CONSTANTS \ + "<Mouse,Default,Blank,Arrow,Cross,Wait,Text,SizeAll,SizeH,SizeV,SizeN,SizeS,SizeW,SizeE,SizeNWSE," \ + "SizeNESW,SplitH,SplitV,Pointing>" +#endif diff --git a/gb.gtk/src/CPanel.cpp b/gb.gtk/src/CPanel.cpp new file mode 100644 index 00000000..9729bdc1 --- /dev/null +++ b/gb.gtk/src/CPanel.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + + CPanel.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPANEL_CPP + +#include "CPanel.h" + + +BEGIN_METHOD(CPANEL_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + +END_METHOD + + +BEGIN_METHOD(CHBOX_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_HORIZONTAL); + //WIDGET->setAutoSize(true); + +END_METHOD + + +BEGIN_METHOD(CVBOX_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_VERTICAL); + //WIDGET->setAutoSize(true); + +END_METHOD + + +BEGIN_METHOD(CHPANEL_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_LEFT_RIGHT); + //WIDGET->setAutoSize(true); + +END_METHOD + + +BEGIN_METHOD(CVPANEL_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_TOP_BOTTOM); + //WIDGET->setAutoSize(true); + +END_METHOD + +BEGIN_PROPERTY(CPANEL_border) + + if (READ_PROPERTY) { GB.ReturnInteger(PANEL->getBorder()); return; } + PANEL->setBorder(VPROP(GB_INTEGER)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CPanelDesc[] = +{ + GB_DECLARE("Panel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("Border", "i", CPANEL_border), + + ARRANGEMENT_PROPERTIES, + + PANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHBoxDesc[] = +{ + GB_DECLARE("HBox", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CHBOX_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + HBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVBoxDesc[] = +{ + GB_DECLARE("VBox", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CVBOX_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + VBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHPanelDesc[] = +{ + GB_DECLARE("HPanel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CHPANEL_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + HPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVPanelDesc[] = +{ + GB_DECLARE("VPanel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CVPANEL_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + VPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.gtk/src/CPanel.h b/gb.gtk/src/CPanel.h new file mode 100644 index 00000000..b526331f --- /dev/null +++ b/gb.gtk/src/CPanel.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CPanel.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPANEL_H +#define __CPANEL_H + +#include "main.h" +#include "gpanel.h" +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CPANEL_CPP +extern GB_DESC CPanelDesc[]; +extern GB_DESC CHBoxDesc[]; +extern GB_DESC CVBoxDesc[]; +extern GB_DESC CHPanelDesc[]; +extern GB_DESC CVPanelDesc[]; +#else + +#define THIS ((CPANEL *)_object) + +#define PANEL ((gPanel *)(THIS->widget.widget)) + +#endif + +typedef + struct + { + CWIDGET widget; + } + CPANEL; + +#endif diff --git a/gb.gtk/src/CPicture.cpp b/gb.gtk/src/CPicture.cpp new file mode 100644 index 00000000..d9a7958f --- /dev/null +++ b/gb.gtk/src/CPicture.cpp @@ -0,0 +1,276 @@ +/*************************************************************************** + + CPicture.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPICTURE_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "ggambastag.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CScreen.h" +#include "CImage.h" +#include "CPicture.h" + +CPICTURE *CPICTURE_create(gPicture *picture) +{ + CPICTURE *pic; + + pic = (CPICTURE *)GB.New(GB.FindClass("Picture"), 0, 0); + if (picture) + { + pic->picture->unref(); + pic->picture = picture; + picture->setTag(new gGambasTag((void *)pic)); + } + return pic; +} + +void* GTK_GetPicture(GdkPixbuf *buf) +{ + CPICTURE *pic = CPICTURE_create(new gPicture(buf)); + g_object_ref(buf); + return (void*)pic; +} + + +bool CPICTURE_load_image(gPicture **p, const char *path, int lenp) +{ + char *addr; + int len; + + *p = NULL; + + if (GB.LoadFile(path, lenp, &addr, &len)) + { + GB.Error(NULL); + return FALSE; + } + + *p = gPicture::fromMemory(addr, len); + GB.ReleaseFile(addr, len); + return *p != NULL; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Picture_new, GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN trans) + + int w = VARGOPT(w, 0); + int h = VARGOPT(h, 0); + bool trans = VARGOPT(trans, false); + +#ifdef GTK3 + PICTURE = new gPicture(gPicture::SURFACE, w, h, trans); +#else + PICTURE = new gPicture(gPicture::PIXMAP, w, h, trans); +#endif + PICTURE->setTag(new gGambasTag(THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_free) + + if (PICTURE) PICTURE->unref(); + +END_METHOD + + +BEGIN_METHOD(Picture_Resize, GB_INTEGER width; GB_INTEGER height) + + PICTURE->resize(VARG(width),VARG(height)); + +END_METHOD + + +BEGIN_PROPERTY(Picture_Width) + + GB.ReturnInteger(PICTURE->width()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Height) + + GB.ReturnInteger(PICTURE->height()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Depth) + + GB.ReturnInteger(PICTURE->depth()); + +END_PROPERTY + + +BEGIN_METHOD(Picture_Load, GB_STRING path) + + CPICTURE *picture; + char *addr; + int len; + + if (!GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + gPicture *pic = gPicture::fromMemory(addr, len); + GB.ReleaseFile(addr, len); + + if (pic) + { + picture = CPICTURE_create(pic); + GB.ReturnObject(picture); + return; + } + } + + GB.Error("Unable to load picture"); + +END_METHOD + +BEGIN_METHOD(Picture_FromString, GB_STRING data) + + CPICTURE *picture; + + gPicture *pic = gPicture::fromMemory(STRING(data), LENGTH(data)); + + if (pic) + { + picture = CPICTURE_create(pic); + GB.ReturnObject(picture); + return; + } + + GB.Error("Unable to load picture"); + +END_METHOD + +/* +BEGIN_METHOD(CPICTURE_fromMemory,GB_STRING data;) + + CPICTURE *pic=NULL; + + if (!LENGTH(data)) return; + + GB.New((void **)&pic, GB.FindClass("Picture"), 0, 0); + pic->picture->fromMemory(STRING(data),LENGTH(data)); + GB.ReturnObject(pic); + + +END_METHOD +*/ + +BEGIN_METHOD(Picture_Save, GB_STRING path; GB_INTEGER quality) + + switch (PICTURE->save(GB.FileName(STRING(path), LENGTH(path)), VARGOPT(quality, -1))) + { + case 0: break; + case -1: GB.Error("Unknown format"); break; + case -2: GB.Error("Unable to save picture"); break; + } + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_Clear) + + PICTURE->clear(); + +END_METHOD + + +BEGIN_METHOD(Picture_Fill, GB_INTEGER col) + + PICTURE->fill(VARG(col)); + +END_METHOD + + +BEGIN_METHOD(Picture_Copy, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CPICTURE *pic=NULL; + int x=0; + int y=0; + int w=PICTURE->width(); + int h=PICTURE->height(); + + if (!MISSING(x)) x=VARG(x); + if (!MISSING(y)) y=VARG(y); + if (!MISSING(w)) w=VARG(w); + if (!MISSING(h)) h=VARG(h); + + pic = CPICTURE_create(PICTURE->copy(x, y, w, h)); + GB.ReturnObject(pic); + +END_METHOD + + +BEGIN_PROPERTY(Picture_Image) + + CIMAGE *img = CIMAGE_create(PICTURE->copy()); + //CIMAGE_get(img)->getPixbuf(); + GB.ReturnObject((void*)img); + +END_PROPERTY + +BEGIN_PROPERTY(Picture_Transparent) + + if (READ_PROPERTY) { GB.ReturnBoolean(PICTURE->isTransparent()); return; } + PICTURE->setTransparent(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +GB_DESC CPictureDesc[] = +{ + GB_DECLARE("Picture", sizeof(CPICTURE)), + + //GB_STATIC_METHOD("_exit", NULL, CPICTURE_flush, NULL), + + GB_METHOD("_new", NULL, Picture_new, "[(Width)i(Height)i(Transparent)b]"), + GB_METHOD("_free", NULL, Picture_free, NULL), + + GB_PROPERTY_READ("Width", "i", Picture_Width), + GB_PROPERTY_READ("Height", "i", Picture_Height), + GB_PROPERTY_READ("W", "i", Picture_Width), + GB_PROPERTY_READ("H", "i", Picture_Height), + GB_PROPERTY_READ("Depth", "i", Picture_Depth), + GB_PROPERTY("Transparent", "b", Picture_Transparent), + + GB_STATIC_METHOD("Load", "Picture", Picture_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Picture", Picture_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Picture_Save, "(Path)s[(Quality)i]"), + GB_METHOD("Resize", NULL, Picture_Resize, "(Width)i(Height)i"), + + GB_METHOD("Clear", NULL, Picture_Clear, NULL), + GB_METHOD("Fill", NULL, Picture_Fill, "(Color)i"), + + GB_METHOD("Copy", "Picture", Picture_Copy, "[(X)i(Y)i(Width)i(Height)i]"), + GB_PROPERTY_READ("Image", "Image", Picture_Image), + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CPicture.h b/gb.gtk/src/CPicture.h new file mode 100644 index 00000000..ada01c0c --- /dev/null +++ b/gb.gtk/src/CPicture.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + CPicture.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPICTURE_H +#define __CPICTURE_H + +#include "gambas.h" +#include "widgets.h" + +typedef + struct + { + GB_BASE ob; + gPicture *picture; + } + CPICTURE; + +#ifndef __CPICTURE_CPP + +extern GB_DESC CPictureDesc[]; + +#else + +#define MAX_KEY 255 + +#define STOCK_PREFIX "icon:/" +#define STOCK_PREFIX_LEN 6 + +#define THIS OBJECT(CPICTURE) +#define PICTURE (((CPICTURE*)(_object))->picture) + +#endif + +void *GTK_GetPicture(GdkPixbuf *buf); +CPICTURE *CPICTURE_create(gPicture *picture); + +#endif diff --git a/gb.gtk/src/CScreen.cpp b/gb.gtk/src/CScreen.cpp new file mode 100644 index 00000000..718665d2 --- /dev/null +++ b/gb.gtk/src/CScreen.cpp @@ -0,0 +1,558 @@ +/*************************************************************************** + + CScreen.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSCREEN_CPP + +#include "CWindow.h" +#include "CPicture.h" +#include "CFont.h" +#include "CContainer.h" +#include "CDrawingArea.h" +#include "CScreen.h" + +#include "gtrayicon.h" +#include "gapplication.h" +#include "gmainwindow.h" +#include "cpaint_impl.h" + +extern int CWINDOW_Embedder; +extern bool CWINDOW_Embedded; + +extern int MAIN_scale; + +char *CAPPLICATION_Theme = 0; +GB_ARRAY CAPPLICATION_Restart = NULL; +bool CAPPLICATION_MiddleClickPaste = TRUE; + +static int _busy = 0; + +#define MAX_SCREEN 16 +static CSCREEN *_screens[MAX_SCREEN] = { NULL }; + +static bool _animations = FALSE; +static bool _shadows = FALSE; + +//------------------------------------------------------------------------- + +static void send_change_event() +{ + CDRAWINGAREA_send_change_event(); + CUSERCONTROL_send_change_event(); +} + + +static CSCREEN *get_screen(int num) +{ + if (num < 0 || num >= MAX_SCREEN || num >= gDesktop::count()) + return NULL; + + if (!_screens[num]) + { + _screens[num] = (CSCREEN *)GB.New(GB.FindClass("Screen"), NULL, 0); + _screens[num]->index = num; + GB.Ref(_screens[num]); + } + + return _screens[num]; +} + +static void free_screens(void) +{ + int i; + + for (i = 0; i < MAX_SCREEN; i++) + { + if (_screens[i]) + GB.Unref(POINTER(&_screens[i])); + } +} + +static GdkRectangle *geometry(int num) +{ + static GdkRectangle rect; + gDesktop::geometry(num, &rect); + return ▭ +} + +static GdkRectangle *available_geometry(int num) +{ + static GdkRectangle rect; + gDesktop::availableGeometry(num, &rect); + return ▭ +} + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Desktop_X) + + GB.ReturnInteger(available_geometry(0)->x); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Y) + + GB.ReturnInteger(available_geometry(0)->y); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Width) + + GB.ReturnInteger(available_geometry(0)->width); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Height) + + GB.ReturnInteger(available_geometry(0)->height); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Resolution) + + GB.ReturnInteger(gDesktop::resolution()); + +END_PROPERTY + +BEGIN_METHOD(Desktop_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + CPICTURE *pic; + gPicture *buf = gDesktop::screenshot(VARGOPT(x,0), VARGOPT(y, 0), VARGOPT(width, 0), VARGOPT(height, 0)); + + pic = (CPICTURE *)GB.New(GB.FindClass("Picture"), 0, 0); + if (pic->picture) pic->picture->unref(); + pic->picture = buf; + GB.ReturnObject(pic); + +END_METHOD + +BEGIN_PROPERTY(Desktop_HasSystemTray) + + #ifdef NO_X_WINDOW + GB.Return(FALSE); + #else + GB.ReturnBoolean(gTrayIcon::hasSystemTray()); + #endif + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Scale) + + GB.ReturnInteger(MAIN_scale); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Platform) + + #ifdef GTK3 + GB.ReturnConstZeroString(MAIN_platform); + #else + GB.ReturnConstZeroString("x11"); + #endif + +END_PROPERTY + + +//------------------------------------------------------------------------- + +static void set_font(gFont *font, void *object = 0) +{ + gDesktop::setFont(font); + MAIN_scale = gDesktop::scale(); +} + +BEGIN_PROPERTY(Application_Font) + + if (READ_PROPERTY) + GB.ReturnObject(CFONT_create(gDesktop::font()->copy(), set_font)); + else if (VPROP(GB_OBJECT)) + { + CFONT *font = (CFONT*)VPROP(GB_OBJECT); + set_font(font ? font->font : NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveWindow) + + GB.ReturnObject(CWINDOW_Active); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveControl) + + GB.ReturnObject(GetObject(gApplication::activeControl())); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_PreviousControl) + + GB.ReturnObject(GetObject(gApplication::previousControl())); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Busy) + + int busy; + + if (READ_PROPERTY) + GB.ReturnInteger(_busy); + else + { + busy = VPROP(GB_INTEGER); + if (busy < 0) + busy = 0; + + if (_busy == 0 && busy != 0) + gApplication::setBusy(true); + else if (_busy > 0 && busy == 0) + gApplication::setBusy(false); + + _busy = busy; + if (MAIN_debug_busy) + fprintf(stderr, "%s: Application.Busy = %d\n", GB.Debug.GetCurrentPosition(), busy); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ShowTooltips) + + if (READ_PROPERTY) + GB.ReturnBoolean(gApplication::areTooltipsEnabled()); + else + gApplication::enableTooltips(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Animations) + + if (READ_PROPERTY) + GB.ReturnBoolean(_animations); + else if (_animations != VPROP(GB_BOOLEAN)) + { + _animations = VPROP(GB_BOOLEAN); + send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_MiddleClickPaste) + + if (READ_PROPERTY) + GB.ReturnBoolean(CAPPLICATION_MiddleClickPaste); + else + CAPPLICATION_MiddleClickPaste = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Shadows) + + if (READ_PROPERTY) + GB.ReturnBoolean(_shadows); + else if (_shadows != VPROP(GB_BOOLEAN)) + { + _shadows = VPROP(GB_BOOLEAN); + send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_MainWindow) + + if (READ_PROPERTY) + GB.ReturnObject(CWINDOW_Main); + else + { + CWINDOW_Main = (CWINDOW *)VPROP(GB_OBJECT); + gApplication::setMainWindow(CWINDOW_Main ? (gMainWindow *)CWINDOW_Main->ob.widget : NULL); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Application_exit) + + GB.FreeString(&CAPPLICATION_Theme); + GB.StoreObject(NULL, POINTER(&CAPPLICATION_Restart)); + free_screens(); + +END_METHOD + + +BEGIN_PROPERTY(Application_Embedder) + + if (READ_PROPERTY) + GB.ReturnInteger(CWINDOW_Embedder); + else + { + if (CWINDOW_Embedded) + { + GB.Error("Application is already embedded"); + return; + } + + CWINDOW_Embedder = VPROP(GB_INTEGER); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Theme) + + if (READ_PROPERTY) { GB.ReturnString(CAPPLICATION_Theme); return; } + GB.StoreString(PROP(GB_STRING), &CAPPLICATION_Theme); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_DarkTheme) + + static bool _init = FALSE; + static bool _dark = FALSE; + + uint bg; + char *env; + + if (!_init) + { + _init = TRUE; + bg = gDesktop::getColor(gDesktop::BACKGROUND); + if (IMAGE.GetLuminance(bg) >= 128) + { + env = getenv("GB_GUI_DARK_THEME"); + if (env && atoi(env)) + _dark = TRUE; + } + else + _dark = TRUE; + } + + GB.ReturnBoolean(_dark); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Restart) + + if (READ_PROPERTY) + GB.ReturnObject(CAPPLICATION_Restart); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&CAPPLICATION_Restart)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_DblClickTime) + + GB.ReturnInteger(gApplication::dblClickTime()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Screens_Count) + + GB.ReturnInteger(gDesktop::count()); + +END_PROPERTY + + +BEGIN_METHOD(Screens_get, GB_INTEGER screen) + + GB.ReturnObject(get_screen(VARG(screen))); + +END_METHOD + + +BEGIN_METHOD_VOID(Screens_next) + + int *index = (int *)GB.GetEnum(); + + if (*index >= gDesktop::count()) + GB.StopEnum(); + else + { + GB.ReturnObject(get_screen(*index)); + (*index)++; + } + +END_METHOD + + +BEGIN_PROPERTY(Screen_X) + + GB.ReturnInteger(geometry(SCREEN->index)->x); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Y) + + GB.ReturnInteger(geometry(SCREEN->index)->y); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Width) + + GB.ReturnInteger(geometry(SCREEN->index)->width); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Height) + + GB.ReturnInteger(geometry(SCREEN->index)->height); + +END_PROPERTY + + +BEGIN_PROPERTY(Screen_AvailableX) + + GB.ReturnInteger(available_geometry(SCREEN->index)->x); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableY) + + GB.ReturnInteger(available_geometry(SCREEN->index)->y); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableWidth) + + GB.ReturnInteger(available_geometry(SCREEN->index)->width); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableHeight) + + GB.ReturnInteger(available_geometry(SCREEN->index)->height); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_ResolutionX) + + double r; + gDesktop::screenResolution(SCREEN->index, &r, NULL); + GB.ReturnFloat(r); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_ResolutionY) + + double r; + gDesktop::screenResolution(SCREEN->index, NULL, &r); + GB.ReturnFloat(r); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC ScreenDesc[] = +{ + GB_DECLARE("Screen", sizeof(CSCREEN)), GB_NOT_CREATABLE(), GB_AUTO_CREATABLE(), + + GB_PROPERTY_READ("X", "i", Screen_X), + GB_PROPERTY_READ("Y", "i", Screen_Y), + GB_PROPERTY_READ("W", "i", Screen_Width), + GB_PROPERTY_READ("H", "i", Screen_Height), + GB_PROPERTY_READ("Width", "i", Screen_Width), + GB_PROPERTY_READ("Height", "i", Screen_Height), + + GB_PROPERTY_READ("AvailableX", "i", Screen_AvailableX), + GB_PROPERTY_READ("AvailableY", "i", Screen_AvailableY), + GB_PROPERTY_READ("AvailableWidth", "i", Screen_AvailableWidth), + GB_PROPERTY_READ("AvailableHeight", "i", Screen_AvailableHeight), + + GB_PROPERTY_READ("ResolutionX", "f", Screen_ResolutionX), + GB_PROPERTY_READ("ResolutionY", "f", Screen_ResolutionY), + + GB_END_DECLARE +}; + +GB_DESC ScreensDesc[] = +{ + GB_DECLARE("Screens", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("Count", "i", Screens_Count), + GB_STATIC_METHOD("_get", "Screen", Screens_get, "(Screen)i"), + GB_STATIC_METHOD("_next", "Screen", Screens_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC DesktopDesc[] = +{ + GB_DECLARE("Desktop", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("X", "i", Desktop_X), + GB_STATIC_PROPERTY_READ("Y", "i", Desktop_Y), + GB_STATIC_PROPERTY_READ("W", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("H", "i", Desktop_Height), + GB_STATIC_PROPERTY_READ("Width", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("Height", "i", Desktop_Height), + + GB_CONSTANT("Charset", "s", "UTF-8"), + GB_STATIC_PROPERTY_READ("Resolution", "i", Desktop_Resolution), + GB_STATIC_PROPERTY_READ("Scale","i",Desktop_Scale), + GB_STATIC_PROPERTY_READ("HasSystemTray", "b", Desktop_HasSystemTray), + + GB_STATIC_METHOD("Screenshot", "Picture", Desktop_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_STATIC_PROPERTY_READ("Platform", "s", Desktop_Platform), + + GB_END_DECLARE +}; + +GB_DESC ApplicationDesc[] = +{ + GB_DECLARE("Application", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Application_exit, 0), + + GB_STATIC_PROPERTY("Font", "Font", Application_Font), + GB_STATIC_PROPERTY_READ("ActiveControl", "Control", Application_ActiveControl), + GB_STATIC_PROPERTY_READ("PreviousControl", "Control", Application_PreviousControl), + GB_STATIC_PROPERTY_READ("ActiveWindow", "Window", Application_ActiveWindow), + GB_STATIC_PROPERTY("MainWindow", "Window", Application_MainWindow), + GB_STATIC_PROPERTY("Busy", "i", Application_Busy), + GB_STATIC_PROPERTY("ShowTooltips", "b", Application_ShowTooltips), + GB_STATIC_PROPERTY("Animations", "b", Application_Animations), + GB_STATIC_PROPERTY("Shadows", "b", Application_Shadows), + GB_STATIC_PROPERTY("MiddleClickPaste", "b", Application_MiddleClickPaste), + GB_STATIC_PROPERTY("Embedder", "i", Application_Embedder), + GB_STATIC_PROPERTY("Theme", "s", Application_Theme), + GB_STATIC_PROPERTY_READ("DarkTheme", "s", Application_DarkTheme), + GB_STATIC_PROPERTY("Restart", "String[]", Application_Restart), + GB_STATIC_PROPERTY_READ("DblClickTime", "i", Application_DblClickTime), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CScreen.h b/gb.gtk/src/CScreen.h new file mode 100644 index 00000000..e39b9479 --- /dev/null +++ b/gb.gtk/src/CScreen.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + CScreen.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSCREEN_H +#define __CSCREEN_H + +#include "main.h" +#include "gdesktop.h" +#include "gapplication.h" + +#ifndef __CSCREEN_CPP +extern GB_DESC ScreenDesc[]; +extern GB_DESC ScreensDesc[]; +extern GB_DESC DesktopDesc[]; +extern GB_DESC ApplicationDesc[]; + +extern char *CAPPLICATION_Theme; +extern GB_ARRAY CAPPLICATION_Restart; +extern bool CAPPLICATION_MiddleClickPaste; + +#else + +#define SCREEN ((CSCREEN *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + int index; + } + CSCREEN; + +#endif diff --git a/gb.gtk/src/CSlider.cpp b/gb.gtk/src/CSlider.cpp new file mode 100644 index 00000000..24c6f40d --- /dev/null +++ b/gb.gtk/src/CSlider.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + + CSlider.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSLIDER_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" + +#include "CSlider.h" +#include "CContainer.h" +#include "CWidget.h" + +DECLARE_EVENT(EVENT_Change); + +void CB_slider_change(gSlider *sender) +{ + CWIDGET *_ob = GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob, EVENT_Change, 0); +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Slider_new, GB_OBJECT parent) + + InitControl(new gSlider(CONTAINER(VARG(parent))),(CWIDGET*)THIS); + +END_METHOD + +BEGIN_METHOD(ScrollBar_new, GB_OBJECT parent) + + InitControl(new gScrollBar(CONTAINER(VARG(parent))),(CWIDGET*)THIS); + +END_METHOD + +BEGIN_PROPERTY(Slider_Tracking) + + if (READ_PROPERTY) { GB.ReturnBoolean(SLIDER->tracking()); return; } + SLIDER->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_Value) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->value()); return; } + SLIDER->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_MinValue) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->min()); return; } + SLIDER->setMin(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_MaxValue) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->max()); return; } + SLIDER->setMax(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_LineStep) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->step()); return; } + SLIDER->setStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_PageStep) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->pageStep()); return; } + SLIDER->setPageStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_mark) + + if (READ_PROPERTY){ GB.ReturnBoolean(SLIDER->mark()); return; } + SLIDER->setMark(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_DefaultSize) + + GB.ReturnInteger(SLIDER->getDefaultSize()); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_Orientation) + + if (READ_PROPERTY) + GB.ReturnInteger(SLIDER->orientation()); + else + SLIDER->setOrientation(VPROP(GB_INTEGER)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC SliderDesc[] = +{ + GB_DECLARE("Slider", sizeof(CSLIDER)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, Slider_new, "(Parent)Container;"), + + GB_PROPERTY("Tracking", "b", Slider_Tracking), + GB_PROPERTY("Value", "i", Slider_Value), + GB_PROPERTY("Mark", "b", CSLIDER_mark), + GB_PROPERTY("MinValue", "i", Slider_MinValue), + GB_PROPERTY("MaxValue", "i", Slider_MaxValue), + GB_PROPERTY("Step", "i", Slider_LineStep), + GB_PROPERTY("PageStep", "i", Slider_PageStep), + GB_PROPERTY("Orientation", "i", Slider_Orientation), + + GB_EVENT("Change", 0, 0, &EVENT_Change), + + SLIDER_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC ScrollBarDesc[] = +{ + GB_DECLARE("ScrollBar", sizeof(CSCROLLBAR)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, ScrollBar_new, "(Parent)Container;"), + + GB_PROPERTY_READ("DefaultSize", "i", Slider_DefaultSize), + + GB_PROPERTY("Tracking", "b", Slider_Tracking), + GB_PROPERTY("Value", "i", Slider_Value), + GB_PROPERTY("MinValue", "i", Slider_MinValue), + GB_PROPERTY("MaxValue", "i", Slider_MaxValue), + GB_PROPERTY("Step", "i", Slider_LineStep), + GB_PROPERTY("PageStep", "i", Slider_PageStep), + GB_PROPERTY("Orientation", "i", Slider_Orientation), + + GB_EVENT("Change", 0, 0, &EVENT_Change), + + SCROLLBAR_DESCRIPTION, + + GB_CONSTANT("Auto", "i", ORIENTATION_AUTO), + GB_CONSTANT("Horizontal", "i", ORIENTATION_HORIZONTAL), + GB_CONSTANT("Vertical", "i", ORIENTATION_VERTICAL), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CSlider.h b/gb.gtk/src/CSlider.h new file mode 100644 index 00000000..f68b66cc --- /dev/null +++ b/gb.gtk/src/CSlider.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + CSlider.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSLIDER_H +#define __CSLIDER_H + +#include "main.h" +#include "gslider.h" +#include "gscrollbar.h" + +#ifndef __CSLIDER_CPP + +extern GB_DESC SliderDesc[]; +extern GB_DESC ScrollBarDesc[]; + +#else + +#define THIS ((CSLIDER *)_object) +#define SLIDER ((gSlider*)THIS->ob.widget) +#define SBAR ((gScrollBar *)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CSLIDER; + +typedef + struct + { + CWIDGET ob; + } + CSCROLLBAR; + + +#endif diff --git a/gb.gtk/src/CStyle.cpp b/gb.gtk/src/CStyle.cpp new file mode 100644 index 00000000..5b63de28 --- /dev/null +++ b/gb.gtk/src/CStyle.cpp @@ -0,0 +1,1031 @@ +/*************************************************************************** + + CStyle.cpp + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSTYLE_CPP + +#include "CDrawingArea.h" +#include "CPicture.h" +#include "CStyle.h" +#include "cpaint_impl.h" + +#ifdef GTK3 + +static cairo_t *_cr = NULL; +static GtkWidget *_button = NULL; +static GtkWidget *_check_button = NULL; +static GtkWidget *_entry = NULL; +static GtkWidget *_radio_button = NULL; +static GtkStyleProvider *_css = NULL; + +#else + +static GdkDrawable *_dr = NULL; +static int _dr_x = 0; +static int _dr_y = 0; +static GtkWidget *_widget = NULL; + +#endif + +static STYLE_T *_stl = NULL; + +static bool _internal = false; + +#ifdef GTK3 +static GtkStyleContext *get_style(GType type = G_TYPE_NONE) +{ + GtkWidget *widget = NULL; + + if (type == GTK_TYPE_BUTTON) + { + if (!_button) + { + _button = gtk_button_new(); + gtk_widget_set_name(_button, "se"); + } + widget = _button; + } + else if (type == GTK_TYPE_CHECK_BUTTON) + { + if (!_check_button) _check_button = gtk_check_button_new(); + widget = _check_button; + } + else if (type == GTK_TYPE_ENTRY) + { + if (!_entry) + { + _entry = gtk_entry_new(); + gtk_widget_set_name(_entry, "se"); + } + widget = _entry; + } + else if (type == GTK_TYPE_RADIO_BUTTON) + { + if (!_radio_button) _radio_button = gtk_radio_button_new(NULL); + widget = _radio_button; + } + + if (!_css) + _css = GTK_STYLE_PROVIDER(gtk_css_provider_new()); + + return widget ? gtk_widget_get_style_context(widget) : NULL; +} +#else +static GtkStyle *attach_style(GtkStyle *style) +{ + if (_widget) + return gtk_style_attach(style, gtk_widget_get_window(_widget)); + else + return gtk_style_attach(style, (GdkWindow*)_dr); +} + +static GtkStyle *get_style(GType type = G_TYPE_NONE) +{ + if (type == G_TYPE_NONE && _widget) + { + _stl = gtk_style_copy(gtk_widget_get_style(_widget)); + //_stl = gtk_style_attach(_stl, gtk_widget_get_window(_widget)); + } + else + { + if (type != G_TYPE_NONE) + _stl = gtk_style_copy(gt_get_style(type)); + else + _stl = gtk_style_copy(gtk_widget_get_default_style()); + + //_stl = gtk_style_attach(_stl, (GdkWindow*)_dr); + } + + _stl = attach_style(_stl); + + return _stl; +} +#endif + +#ifdef GTK3 +static bool begin_draw(int *x, int *y) +{ + if (_internal) + return FALSE; + + void *device = PAINT_get_current_device(); + if (!device) + return TRUE; + + _cr = PAINT_get_current_context(); + cairo_save(_cr); + + if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CDRAWINGAREA *)device)->ob.widget; + + if (!(wid->cached() || wid->inDrawEvent())) + { + GB.Error("Cannot draw outside of 'Draw' event handler"); + return TRUE; + } + + //_widget = wid->widget; + } + else + { + //_widget = NULL; + } + + return FALSE; +} +#else +static bool begin_draw(int *x, int *y) +{ + void *device = PAINT_get_current_device(); + if (!device) + return TRUE; + + cairo_t *context = PAINT_get_current_context(); + cairo_surface_flush(cairo_get_target(context)); + cairo_save(context); + + if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CDRAWINGAREA *)device)->ob.widget; + + if (wid->cached() || wid->inDrawEvent()) + { + if (wid->cached()) + { + wid->resizeCache(); + _dr = wid->buffer; + } + else + { + _dr = wid->widget->window; + GtkAllocation *a = &wid->widget->allocation; + _dr_x = a->x; + _dr_y = a->y; + *x += _dr_x; + *y += _dr_y; + } + } + else + { + GB.Error("Cannot draw outside of 'Draw' event handler"); + return TRUE; + } + + _widget = wid->widget; + } + else if (GB.Is(device, CLASS_UserControl)) + { + gContainer *wid; + GtkAllocation *a; + + if (PAINT_is_internal()) + { + GB.Error("Cannot draw outside of 'Draw' event handler"); + return TRUE; + } + + wid = (gContainer *)((CDRAWINGAREA *)device)->ob.widget; + + _dr = wid->widget->window; + a = &wid->widget->allocation; + _dr_x = a->x; + _dr_y = a->y; + *x += _dr_x; + *y += _dr_y; + + _widget = wid->widget; + } + else if (GB.Is(device, CLASS_Picture)) + { + gPicture *pic = ((CPICTURE *)device)->picture; + if (pic->isVoid()) + { + GB.Error("Bad picture"); + return TRUE; + } + _dr = pic->getPixmap(); + //pic->invalidate(); + _widget = NULL; + } + else + { + GB.Error("Device not supported"); + } + + return FALSE; +} +#endif + +static void end_draw() +{ +#ifdef GTK3 + if (!_internal) + cairo_restore(_cr); + _cr = NULL; + if (_stl) + { + gtk_style_context_restore(_stl); + _stl = NULL; + } +#else + _dr = NULL; + if (_stl) + { + gtk_style_detach(_stl); + g_object_unref(G_OBJECT(_stl)); + _stl = NULL; + } + _widget = NULL; +#endif + +#ifndef GTK3 + if (!_internal) + { + cairo_t *context = PAINT_get_current_context(); + cairo_restore(context); + cairo_surface_mark_dirty(cairo_get_target(context)); + } +#endif +} + +#ifdef GTK3 + +static STATE_T get_state(int state) +{ + int gstate = STATE_NORMAL; + + if (state & GB_DRAW_STATE_DISABLED) + gstate |= STATE_INSENSITIVE; + if (state & GB_DRAW_STATE_ACTIVE) + gstate |= STATE_ACTIVE; + if (state & GB_DRAW_STATE_HOVER) + gstate |= STATE_PRELIGHT; + if (state & GB_DRAW_STATE_FOCUS) + gstate |= STATE_FOCUSED; + + return (STATE_T)gstate; +} + +static GtkCellRendererState get_cell_state(int state) +{ + int cstate = 0; + + if (state & GB_DRAW_STATE_DISABLED) + cstate |= GTK_CELL_RENDERER_INSENSITIVE; + if (state & GB_DRAW_STATE_ACTIVE) + cstate |= GTK_CELL_RENDERER_SELECTED; + if (state & GB_DRAW_STATE_HOVER) + cstate |= GTK_CELL_RENDERER_PRELIT; + if (state & GB_DRAW_STATE_FOCUS) + cstate |= GTK_CELL_RENDERER_FOCUSED; + + return (GtkCellRendererState)cstate; +} + +static void set_state(GtkStyleContext *style, int state) +{ + gtk_style_context_set_state(style, get_state(state)); +} + +#else + +static STATE_T get_state(int state) +{ + if (state & GB_DRAW_STATE_DISABLED) + return STATE_INSENSITIVE; + if (state & GB_DRAW_STATE_FOCUS) + return STATE_ACTIVE; + if (state & GB_DRAW_STATE_HOVER) + return STATE_PRELIGHT; + if (state & GB_DRAW_STATE_ACTIVE) + return STATE_ACTIVE; + + return STATE_NORMAL; +} + +#endif + +#ifndef GTK3 +static GdkRectangle *get_area() +{ + static GdkRectangle area; + + if (PAINT_get_clip(&area.x, &area.y, &area.width, &area.height)) + return NULL; + else + { + area.x += _dr_x; + area.y += _dr_y; + //fprintf(stderr, "clip: %d %d %d %d\n", area.x, area.y, area.width, area.height); + return &area; + } +} +#endif + +#ifdef GTK3 +static void paint_focus(STYLE_T *style, int x, int y, int w, int h) +{ + gtk_render_focus(style, _cr, x, y, w, h); +} +#else +static void paint_focus(STYLE_T *style, int x, int y, int w, int h, STATE_T state, const char *kind) +{ + gtk_paint_focus(style, _dr, state, get_area(), _widget, kind, x, y, w, h); +} +#endif + +static void style_arrow(int x, int y, int w, int h, int type, int state) +{ + GtkArrowType arrow; + STYLE_T *style = get_style(GTK_TYPE_BUTTON); + + switch (type) + { + case ALIGN_NORMAL: arrow = GB.System.IsRightToLeft() ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT; break; + case ALIGN_LEFT: arrow = GTK_ARROW_LEFT; break; + case ALIGN_RIGHT: arrow = GTK_ARROW_RIGHT; break; + case ALIGN_TOP: arrow = GTK_ARROW_UP; break; + case ALIGN_BOTTOM: arrow = GTK_ARROW_DOWN; break; + default: + return; + } + +#ifdef GTK3 + double angle; + + switch(arrow) + { + case GTK_ARROW_LEFT: angle = M_PI * 1.5; break; + case GTK_ARROW_RIGHT: angle = M_PI / 2; break; + case GTK_ARROW_UP: angle = 0; break; + case GTK_ARROW_DOWN: angle = M_PI; break; + default: return; + } + + if (w > h) + { + x += (w - h) / 2; + w = h; + } + else if (h > w) + { + y += (h - w) / 2; + } + + set_state(style, state); + gtk_render_arrow(style, _cr, angle, x, y, w); +#else + gtk_paint_arrow(style, _dr, get_state(state), + GTK_SHADOW_NONE, get_area(), _widget, NULL, + arrow, TRUE, x, y, w, h); +#endif +} + +#ifdef GTK3 +static void render_toggle(int x, int y, int w, int h, int value, int state, bool radio) +{ + static GtkCellRenderer *cell = NULL; + GdkRectangle area; + + if (!cell) + cell = gtk_cell_renderer_toggle_new(); + + gtk_cell_renderer_toggle_set_radio(GTK_CELL_RENDERER_TOGGLE(cell), radio); + + g_object_set(G_OBJECT(cell), "active", value < 0, NULL); + g_object_set(G_OBJECT(cell), "inconsistent", value > 0, NULL); + + area.x = x; + area.y = y; + area.width = w; + area.height = h; + + gtk_cell_renderer_render(cell, _cr, radio ? _radio_button : _check_button, &area, &area, get_cell_state(state)); +} +#endif + +static void style_check(int x, int y, int w, int h, int value, int state) +{ +#ifdef GTK3 + + get_style(GTK_TYPE_CHECK_BUTTON); + render_toggle(x, y, w, h, value, state, false); + +#else + + STYLE_T *style = get_style(GTK_TYPE_CHECK_BUTTON); + GtkShadowType shadow; + GtkStateType st = get_state(state); + + if (value) + state |= GB_DRAW_STATE_ACTIVE; + + //_dr->offset(&x, &y); + + switch (value) + { + case -1: shadow = GTK_SHADOW_IN; break; + case 1: shadow = GTK_SHADOW_ETCHED_IN; break; + default: shadow = GTK_SHADOW_OUT; break; + } + + gtk_paint_check(style, _dr, + st, shadow, get_area(), NULL, "checkbutton", + x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "checkbutton"); + +#endif +} + +static void style_option(int x, int y, int w, int h, int value, int state) +{ +#ifdef GTK3 + + get_style(GTK_TYPE_RADIO_BUTTON); + render_toggle(x, y, w, h, value, state, true); + +#else + + STYLE_T *style = get_style(GTK_TYPE_RADIO_BUTTON); + + if (value) + state |= GB_DRAW_STATE_ACTIVE; + + GtkShadowType shadow; + GtkStateType st = get_state(state | (value ? GB_DRAW_STATE_ACTIVE : 0)); + + shadow = value ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + + gtk_paint_option(style, _dr, + st, shadow, get_area(), NULL, "radiobutton", + x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "radiobutton"); + +#endif +} + +static void style_separator(int x, int y, int w, int h, int vertical, int state) +{ + STYLE_T *style = get_style(); + + if (vertical) + { +#ifdef GTK3 + set_state(style, state); + gtk_render_line(style, _cr, x + (w / 2), y, x + (w / 2), y + h - 1); +#else + gtk_paint_vline(style, _dr, + get_state(state), get_area(), NULL, NULL, + y, y + h - 1, x + (w / 2)); +#endif + } + else + { +#ifdef GTK3 + set_state(style, state); + gtk_render_line(style, _cr, x, y + (h / 2), x + w - 1, y + (h / 2)); +#else + gtk_paint_hline(style, _dr, + get_state(state), get_area(), NULL, NULL, + x, x + w - 1, y + (h / 2)); +#endif + } +} + + +#ifdef GTK3 +static void paint_background(STYLE_T *style, int state, GB_COLOR color, int x, int y, int w, int h) +{ + set_state(style, state); + + if (color != GB_COLOR_DEFAULT) + { + char *css = NULL; + g_stradd(&css, "#se:not(:selected) { background-color:"); + gt_add_css_color(&css, color); + g_stradd(&css, "; background-image:none; }\n"); + gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(_css), css, -1, NULL); + g_free(css); + gtk_style_context_add_provider(style, _css, GTK_STYLE_PROVIDER_PRIORITY_USER); + +#if GTK_CHECK_VERSION(3, 12, 0) +#else + gtk_style_context_invalidate(style); +#endif + gtk_render_background(style, _cr, x, y, w, h); + gtk_style_context_remove_provider(style, GTK_STYLE_PROVIDER(_css)); +#if GTK_CHECK_VERSION(3, 12, 0) +#else + gtk_style_context_invalidate(style); +#endif + } + else + gtk_render_background(style, _cr, x, y, w, h); + + gtk_render_frame(style, _cr, x, y, w, h); + + /*if (color != GB_COLOR_DEFAULT) + gtk_style_context_remove_provider(style, _css);*/ +} +#endif + + +static void style_button(int x, int y, int w, int h, int value, int state, int flat, GB_COLOR color) +{ + STYLE_T *style = get_style(GTK_TYPE_BUTTON); + + if (value) + state |= GB_DRAW_STATE_ACTIVE; + +#ifndef GTK3 + int xf, yf, wf, hf; + GtkBorder *default_border, *default_outside_border, *inner_border; + int focus_width, focus_pad, df; + gboolean interior_focus; + + gtk_style_get(style, GTK_TYPE_BUTTON, + "default-border", &default_border, + "default-outside-border", &default_outside_border, + "inner-border", &inner_border, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + "interior-focus", &interior_focus, + (char *)NULL); + + /*if (default_outside_border) + { + x += default_outside_border->left; + y += default_outside_border->top; + w -= default_outside_border->left + default_outside_border->right; + h -= default_outside_border->top + default_outside_border->bottom; + }*/ + + if (default_border) + { + x += default_border->left; + y += default_border->top; + w -= default_border->left + default_border->right; + h -= default_border->top + default_border->bottom; + } + + if (inner_border) gtk_border_free(inner_border); + if (default_outside_border) gtk_border_free(default_outside_border); + if (default_border) gtk_border_free(default_border); + + xf = x; + yf = y; + wf = w; + hf = h; + + if (interior_focus) + { + df = focus_pad + style->xthickness; + xf += df; + wf -= df * 2; + df = focus_pad + style->ythickness; + yf += df; + hf -= df * 2; + } + else if (state & GB_DRAW_STATE_FOCUS) + { + df = focus_pad + focus_width; + + x += df; + w -= df * 2; + y += df; + h -= df * 2; + } +#endif + + if (flat && (state & GB_DRAW_STATE_HOVER) == 0) + return; + + { + /*gtk_paint_flat_box(style, _dr, + st, value ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + get_area(d), _widget, "button", + x, y, w, h); + if (_dr->mask()) + gtk_paint_flat_box(style, _dr->mask(), + st, value ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + get_area(d), _widget, "button", + x, y, w, h);*/ + } + +#ifdef GTK3 + + paint_background(style, state, color, x, y, w, h); + + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h); + +#else + + GtkStateType st = get_state(state); + + if (color == GB_COLOR_DEFAULT) + { + gtk_paint_box(style, _dr, + st, value ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + get_area(), _widget, "button", + x, y, w, h); + } + else + { + GtkStyle *style2 = gtk_style_copy(style); + for (int i = 0; i < 5; i++) + { + fill_gdk_color(&style2->bg[i], color); + fill_gdk_color(&style2->base[i], color); + } + style2 = attach_style(style2); + + gtk_paint_box(style2, _dr, + st, value ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + get_area(), _widget, "button", + x, y, w, h); + + g_object_unref(G_OBJECT(style2)); + } + + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, xf, yf, wf, hf, st, "button"); + +#endif +} + + +static void style_box(int x, int y, int w, int h, int state, GB_COLOR color) +{ + STYLE_T *style = get_style(GTK_TYPE_ENTRY); + + if (gApplication::_fix_oxygen) + { + x -= 3; + w += 6; + } + +#ifdef GTK3 + + paint_background(style, state, color, x, y, w, h); + +#else + + if (gApplication::_fix_breeze) + state &= ~GB_DRAW_STATE_HOVER; + + GtkStateType st = get_state(state); + + if (color == GB_COLOR_DEFAULT) + { + gtk_paint_box (style, _dr, st, GTK_SHADOW_NONE, get_area(), _widget, "entry", x, y, w, h); + gtk_paint_shadow(style, _dr, st, GTK_SHADOW_NONE, get_area(), NULL, "entry", x, y, w, h); + } + else + { + GtkStyle *style2 = gtk_style_copy(style); + for (int i = 0; i < 5; i++) + { + fill_gdk_color(&style2->bg[i], color); + fill_gdk_color(&style2->base[i], color); + } + style2 = attach_style(style2); + + gtk_paint_box (style2, _dr, st, + GTK_SHADOW_IN, get_area(), _widget, "entry", x, y, w, h); + + g_object_unref(G_OBJECT(style2)); + } + + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "entry"); + +#endif +} + + +static void style_panel(int x, int y, int w, int h, int border, int state) +{ + STYLE_T *style = get_style(); + +#ifdef GTK3 + gColor col = 0; + + if (border == BORDER_PLAIN) + col = gDesktop::getColor(gDesktop::LIGHT_FOREGROUND); + /*{ + col = IMAGE.MergeColor(gDesktop::bgColor(), gDesktop::fgColor(), 0.5); + col = IMAGE.LighterColor(col); + }*/ + + gt_draw_border(_cr, style, get_state(state), border, col, x, y, w, h); + +#else + GtkShadowType shadow; + GtkStateType st = get_state(state); + + switch (border) + { + case BORDER_SUNKEN: shadow = GTK_SHADOW_IN; break; + case BORDER_RAISED: shadow = GTK_SHADOW_OUT; break; + case BORDER_ETCHED: shadow = GTK_SHADOW_ETCHED_IN; break; + default: shadow = GTK_SHADOW_NONE; + } + + gtk_paint_shadow(style, _dr, st, shadow, get_area(), NULL, NULL, x, y, w, h); + + if (border == BORDER_PLAIN) + { + GdkGC *gc; + GdkGCValues values; + uint col; + + col = gDesktop::getColor(gDesktop::LIGHT_FOREGROUND); + fill_gdk_color(&values.foreground, col, gdk_drawable_get_colormap(_dr)); + gc = gtk_gc_get(gdk_drawable_get_depth(_dr), gdk_drawable_get_colormap(_dr), &values, GDK_GC_FOREGROUND); + gdk_draw_rectangle(_dr, gc, FALSE, x, y, w - 1, h - 1); + gtk_gc_release(gc); + } + + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "button"); +#endif + +} + +static void style_handle(int x, int y, int w, int h, int vertical, int state) +{ + STYLE_T *style = get_style(); + +#ifdef GTK3 + set_state(style, state); + gtk_render_handle(style, _cr, x, y, w, h); +#else + gtk_paint_handle(style, _dr, get_state(state), + GTK_SHADOW_NONE, get_area(), NULL, NULL, + x, y, w, h, + (!vertical) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); +#endif +} + + +//------------------------------------------------------------------------- + +#ifdef GTK3 +void CSTYLE_paint_check(cairo_t *cr, int x, int y, int w, int h, int value, int state) +{ + _cr =cr; +#else +void CSTYLE_paint_check(GdkDrawable *dr, int x, int y, int w, int h, int value, int state) +{ + _dr = dr; +#endif + _internal = true; + begin_draw(&x, &y); + style_check(x, y, w, h, value, state); + end_draw(); + _internal = false; +} + +#ifdef GTK3 +void CSTYLE_paint_option(cairo_t *cr, int x, int y, int w, int h, int value, int state) +{ + _cr =cr; +#else +void CSTYLE_paint_option(GdkDrawable *dr, int x, int y, int w, int h, int value, int state) +{ + _dr = dr; +#endif + _internal = true; + begin_draw(&x, &y); + style_option(x, y, w, h, value, state); + end_draw(); + _internal = false; +} + +//------------------------------------------------------------------------- + + +BEGIN_PROPERTY(Style_ScrollbarSize) + + GB.ReturnInteger(gApplication::getScrollbarSize()); + +END_PROPERTY + +BEGIN_PROPERTY(Style_ScrollbarSpacing) + + GB.ReturnInteger(gApplication::getScrollbarSpacing()); + +END_PROPERTY + +BEGIN_PROPERTY(Style_FrameWidth) + + GB.ReturnInteger(gApplication::getFrameWidth()); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameWidth) + + int w, h; + gApplication::getBoxFrame(&w, &h); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameHeight) + + int w, h; + gApplication::getBoxFrame(&w, &h); + GB.ReturnInteger(h); + +END_PROPERTY + +BEGIN_PROPERTY(Style_Name) + + GB.ReturnNewZeroString(gApplication::getStyleName()); + +END_PROPERTY + +#define BEGIN_DRAW() \ + int x, y, w, h; \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (w < 1 || h < 1) \ + return; \ + \ + if (begin_draw(&x, &y)) \ + return; + +#define END_DRAW() end_draw() + +BEGIN_METHOD(Style_PaintArrow, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER type; GB_INTEGER state) + + BEGIN_DRAW(); + style_arrow(x, y, w, h, VARG(type), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintCheck, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER value; GB_INTEGER state) + + BEGIN_DRAW(); + style_check(x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintOption, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state) + + BEGIN_DRAW(); + style_option(x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintSeparator, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + BEGIN_DRAW(); + style_separator(x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintButton, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state; GB_BOOLEAN flat; GB_INTEGER color) + + BEGIN_DRAW(); + style_button(x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(flat, FALSE), VARGOPT(color, GB_COLOR_DEFAULT)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintPanel, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER border; GB_INTEGER state) + + BEGIN_DRAW(); + style_panel(x, y, w, h, VARG(border), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintHandle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + BEGIN_DRAW(); + style_handle(x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintBox, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER state; GB_INTEGER color) + + BEGIN_DRAW(); + style_box(x, y, w, h, VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(color, GB_COLOR_DEFAULT)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_StateOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + gControl *widget; + int state; + bool design; + + if (GB.CheckObject(control)) + return; + + widget = control->widget; + state = GB_DRAW_STATE_NORMAL; + design = widget->isDesign(); + + if (!widget->isEnabled()) + state |= GB_DRAW_STATE_DISABLED; + if (widget->hasVisibleFocus() && !design) + state |= GB_DRAW_STATE_FOCUS; + if (widget->hovered() && !design) + state |= GB_DRAW_STATE_HOVER; + + GB.ReturnInteger(state); + +END_METHOD + +BEGIN_METHOD(Style_BackgroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(control->widget->realBackground(true)); + +END_METHOD + +BEGIN_METHOD(Style_ForegroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(control->widget->realForeground(true)); + +END_METHOD + + +GB_DESC StyleDesc[] = +{ + GB_DECLARE("Style", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("ScrollbarSize", "i", Style_ScrollbarSize), + GB_STATIC_PROPERTY_READ("ScrollbarSpacing", "i", Style_ScrollbarSpacing), + GB_STATIC_PROPERTY_READ("FrameWidth", "i", Style_FrameWidth), + GB_STATIC_PROPERTY_READ("TextBoxFrameWidth", "i", Style_BoxFrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameWidth", "i", Style_BoxFrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameHeight", "i", Style_BoxFrameHeight), + GB_STATIC_PROPERTY_READ("Name", "s", Style_Name), + + GB_STATIC_METHOD("PaintArrow", NULL, Style_PaintArrow, "(X)i(Y)i(Width)i(Height)i(Type)i[(Flag)i]"), + GB_STATIC_METHOD("PaintCheck", NULL, Style_PaintCheck, "(X)i(Y)i(Width)i(Height)i(Value)i[(Flag)i]"), + GB_STATIC_METHOD("PaintOption", NULL, Style_PaintOption, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i]"), + GB_STATIC_METHOD("PaintSeparator", NULL, Style_PaintSeparator, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintButton", NULL, Style_PaintButton, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i(Flat)b(Color)i]"), + GB_STATIC_METHOD("PaintPanel", NULL, Style_PaintPanel, "(X)i(Y)i(Width)i(Height)i(Border)i[(Flag)i]"), + GB_STATIC_METHOD("PaintHandle", NULL, Style_PaintHandle, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintBox", NULL, Style_PaintBox, "(X)i(Y)i(Width)i(Height)i[(Flag)i(Color)i]"), + + GB_CONSTANT("Normal", "i", GB_DRAW_STATE_NORMAL), + GB_CONSTANT("Disabled", "i", GB_DRAW_STATE_DISABLED), + GB_CONSTANT("HasFocus", "i", GB_DRAW_STATE_FOCUS), + GB_CONSTANT("Hovered", "i", GB_DRAW_STATE_HOVER), + GB_CONSTANT("Active", "i", GB_DRAW_STATE_ACTIVE), + + GB_STATIC_METHOD("StateOf", "i", Style_StateOf, "(Control)Control;"), + GB_STATIC_METHOD("BackgroundOf", "i", Style_BackgroundOf, "(Control)Control;"), + GB_STATIC_METHOD("ForegroundOf", "i", Style_ForegroundOf, "(Control)Control;"), + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/CStyle.h b/gb.gtk/src/CStyle.h new file mode 100644 index 00000000..a136de1f --- /dev/null +++ b/gb.gtk/src/CStyle.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + CStyle.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSTYLE_H +#define __CSTYLE_H + +#include "main.h" +#include "gdesktop.h" +#include "gapplication.h" + +#ifndef __CSTYLE_CPP +extern GB_DESC StyleDesc[]; +#endif + +#ifdef GTK3 +void CSTYLE_paint_check(cairo_t *cr, int x, int y, int w, int h, int value, int state); +void CSTYLE_paint_option(cairo_t *cr, int x, int y, int w, int h, int value, int state); +#else +void CSTYLE_paint_check(GdkDrawable *dr, int x, int y, int w, int h, int value, int state); +void CSTYLE_paint_option(GdkDrawable *dr, int x, int y, int w, int h, int value, int state); +#endif + +#endif diff --git a/gb.gtk/src/CTabStrip.cpp b/gb.gtk/src/CTabStrip.cpp new file mode 100644 index 00000000..0a77d0b9 --- /dev/null +++ b/gb.gtk/src/CTabStrip.cpp @@ -0,0 +1,388 @@ +/*************************************************************************** + + CTabStrip.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTABSTRIP_CPP + +#include "CPicture.h" +#include "CContainer.h" +#include "CFont.h" +#include "CTabStrip.h" + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Close); + +static void raise_click(CTABSTRIP *_object) +{ + GB.Raise(THIS, EVENT_Click, 0); + GB.Unref(POINTER(&_object)); +} + +void CB_tabstrip_click(gTabStrip *sender) +{ + CWIDGET *_object = GetObject(sender); + + if (GB.IsRaiseLocked(_object)) + return; + + GB.Ref(THIS); + GB.Post((GB_CALLBACK)raise_click, (long)THIS); +} + +void CB_tabstrip_close(gTabStrip *sender, int index) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Close, 1, GB_T_INTEGER, index); +} + +/*************************************************************************** + + TabStrip + +***************************************************************************/ + +BEGIN_METHOD(TabStrip_new, GB_OBJECT parent) + + InitControl(new gTabStrip(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + CB_tabstrip_click(TABSTRIP); + +END_METHOD + + +BEGIN_METHOD_VOID(TabStrip_free) + + GB.Unref(POINTER(&THIS->textFont)); + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Count) + + if (READ_PROPERTY) { GB.ReturnInteger(TABSTRIP->count()); return; } + + if (VPROP(GB_INTEGER) < 1 || VPROP(GB_INTEGER) > 255) + { + GB.Error("Bad argument"); + return; + } + + if (TABSTRIP->setCount(VPROP(GB_INTEGER))) + GB.Error("Tab is not empty"); + +END_PROPERTY + + + +BEGIN_PROPERTY(TabStrip_Index) + + if (READ_PROPERTY) { GB.ReturnInteger(TABSTRIP->index()); return; } + if ( (VPROP(GB_INTEGER)<0) || (VPROP(GB_INTEGER)>=TABSTRIP->count()) ) + { + GB.Error("Bad index"); + return; + } + TABSTRIP->setIndex(VPROP(GB_INTEGER)); + + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Current) + + THIS->index = TABSTRIP->index(); + RETURN_SELF(); + +END_PROPERTY + + +BEGIN_METHOD(TabStrip_get, GB_INTEGER index) + + if ( (VARG(index)<0) || (VARG(index)>=TABSTRIP->count()) ) + { + GB.Error("Bad index"); + return; + } + + THIS->index=VARG(index); + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Orientation) + + if (READ_PROPERTY) + switch (TABSTRIP->orientation()) + { + case GTK_POS_TOP: GB.ReturnInteger(ALIGN_TOP); break; + case GTK_POS_BOTTOM: GB.ReturnInteger(ALIGN_BOTTOM); break; + case GTK_POS_LEFT: GB.ReturnInteger(ALIGN_LEFT); break; + case GTK_POS_RIGHT: GB.ReturnInteger(ALIGN_RIGHT); break; + default: GB.ReturnInteger(ALIGN_NORMAL); break; + } + + else + switch (VPROP(GB_INTEGER)) + { + case ALIGN_TOP: TABSTRIP->setOrientation(GTK_POS_TOP); break; + case ALIGN_BOTTOM: TABSTRIP->setOrientation(GTK_POS_BOTTOM); break; + case ALIGN_LEFT: TABSTRIP->setOrientation(GTK_POS_LEFT); break; + case ALIGN_RIGHT: TABSTRIP->setOrientation(GTK_POS_RIGHT); break; + + } + +END_PROPERTY + + + +/*************************************************************************** + + .Tab + +***************************************************************************/ + +BEGIN_PROPERTY(TabStripContainer_Text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TABSTRIP->tabText(THIS->index)); + else + TABSTRIP->setTabText(THIS->index, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_TextFont) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->textFont); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->textFont)); + CFONT *font = (CFONT *)THIS->textFont; + if (font) + TABSTRIP->setTextFont(font->font); + else + TABSTRIP->setTextFont(0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStripContainer_Picture) + + if (READ_PROPERTY) + { + gPicture *pic = TABSTRIP->tabPicture(THIS->index);; + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + TABSTRIP->setTabPicture(THIS->index, pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStripContainer_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(TABSTRIP->tabEnabled(THIS->index)); + else + TABSTRIP->setTabEnabled(THIS->index,VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TabStripContainer_next) + + int *ct = (int *)GB.GetEnum(); + + if (*ct >= TABSTRIP->tabCount(THIS->index)) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(GetObject(TABSTRIP->tabChild(THIS->index, *ct))); + (*ct)++; + +END_METHOD + +BEGIN_METHOD(TabStripContainer_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= TABSTRIP->tabCount(THIS->index)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(GetObject(TABSTRIP->tabChild(THIS->index, index))); + +END_METHOD + +BEGIN_PROPERTY(TabStripContainer_Count) + + GB.ReturnInteger(TABSTRIP->tabCount(THIS->index)); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TABSTRIP->tabText(TABSTRIP->index())); + else + TABSTRIP->setTabText(TABSTRIP->index(), GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Picture) + + int index = TABSTRIP->index(); + + if (READ_PROPERTY) + { + gPicture *pic = TABSTRIP->tabPicture(index); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + TABSTRIP->setTabPicture(index, pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Closable) + + if (READ_PROPERTY) + GB.ReturnBoolean(TABSTRIP->isClosable()); + else + TABSTRIP->setClosable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TabStripContainer_Delete) + + if (TABSTRIP->removeTab(THIS->index)) + GB.Error("Tab is not empty"); + +END_METHOD + +BEGIN_PROPERTY(TabStripContainer_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(TABSTRIP->tabVisible(THIS->index)); + else + TABSTRIP->setTabVisible(THIS->index, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(TabStrip_FindIndex, GB_OBJECT child) + + CWIDGET *child = (CWIDGET *)VARG(child); + + if (GB.CheckObject(child)) + return; + + GB.ReturnInteger(TABSTRIP->findIndex(child->widget)); + +END_METHOD + + +/*************************************************************************** + + Descriptions + +***************************************************************************/ + +GB_DESC CTabStripContainerChildrenDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer.Children"), + + GB_METHOD("_next", "Control", TabStripContainer_next, NULL), + GB_PROPERTY_READ("Count", "i", TabStripContainer_Count), + GB_METHOD("_get", "Control", TabStripContainer_get, "(Index)i"), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripContainerDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer"), + + GB_PROPERTY("Text", "s", TabStripContainer_Text), + GB_PROPERTY("Picture", "Picture", TabStripContainer_Picture), + GB_PROPERTY("Caption", "s", TabStripContainer_Text), + GB_PROPERTY("Enabled", "b", TabStripContainer_Enabled), + GB_PROPERTY("Visible", "b", TabStripContainer_Visible), + GB_PROPERTY_SELF("Children", ".TabStripContainer.Children"), + GB_METHOD("Delete", NULL, TabStripContainer_Delete, 0), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripDesc[] = +{ + GB_DECLARE("TabStrip", sizeof(CTABSTRIP)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, TabStrip_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, TabStrip_free, NULL), + + GB_PROPERTY("Count", "i", TabStrip_Count), + GB_PROPERTY("Text", "s", TabStrip_Text), + GB_PROPERTY("TextFont", "Font", TabStrip_TextFont), + GB_PROPERTY("Picture", "Picture", TabStrip_Picture), + GB_PROPERTY("Closable", "b", TabStrip_Closable), + GB_PROPERTY("Caption", "s", TabStrip_Text), + GB_PROPERTY_READ("Current", ".TabStripContainer", TabStrip_Current), + GB_PROPERTY("Index", "i", TabStrip_Index), + GB_PROPERTY("Orientation", "i", TabStrip_Orientation), + + GB_PROPERTY_READ("ClientX", "i", Container_ClientX), + GB_PROPERTY_READ("ClientY", "i", Container_ClientY), + GB_PROPERTY_READ("ClientW", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientWidth", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientH", "i", Container_ClientHeight), + GB_PROPERTY_READ("ClientHeight", "i", Container_ClientHeight), + + ARRANGEMENT_PROPERTIES, + + GB_METHOD("_get", ".TabStripContainer", TabStrip_get, "(Index)i"), + GB_METHOD("FindIndex", "i", TabStrip_FindIndex, "(Child)Control;"), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("Close", NULL, "(Index)i", &EVENT_Close), + + TABSTRIP_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CTabStrip.h b/gb.gtk/src/CTabStrip.h new file mode 100644 index 00000000..b052631c --- /dev/null +++ b/gb.gtk/src/CTabStrip.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CTabStrip.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTABSTRIP_H +#define __CTABSTRIP_H + +#include "main.h" +#include "gtabstrip.h" +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CTABSTRIP_CPP +extern GB_DESC CTabStripDesc[]; +extern GB_DESC CTabStripContainerDesc[]; +extern GB_DESC CTabStripContainerChildrenDesc[]; +#else + + +#define THIS ((CTABSTRIP*)_object) +#define TABSTRIP ((gTabStrip*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + int index; + void *textFont; + } + CTABSTRIP; + +#endif diff --git a/gb.gtk/src/CTextArea.cpp b/gb.gtk/src/CTextArea.cpp new file mode 100644 index 00000000..374d3238 --- /dev/null +++ b/gb.gtk/src/CTextArea.cpp @@ -0,0 +1,499 @@ +/*************************************************************************** + + CTextArea.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTAREA_CPP + +#include "gambas.h" +#include "main.h" +#include "widgets.h" + +#include "CTextArea.h" +#include "CWidget.h" +#include "CContainer.h" + +#include <stdlib.h> + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); +//DECLARE_EVENT(EVENT_Link); //TODO + +void CB_textarea_change(gTextArea *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Change, 0); +} + +void CB_textarea_cursor(gTextArea *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Cursor, 0); +} + +BEGIN_METHOD(CTEXTAREA_new, GB_OBJECT parent) + + InitControl(new gTextArea(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + +END_METHOD + + +BEGIN_PROPERTY(CTEXTAREA_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TEXTAREA->text()); + else + TEXTAREA->setText(PSTRING(), PLENGTH()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_length) + + GB.ReturnInteger(TEXTAREA->length()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_read_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTAREA->readOnly()); + else + TEXTAREA->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTAREA->wrap()); + else + TEXTAREA->setWrap(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_line) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->line()); + else + TEXTAREA->setLine(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_column) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->column()); + else + TEXTAREA->setColumn(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_pos) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->position()); + else + TEXTAREA->setPosition(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTEXTAREA_clear) + + TEXTAREA->clear(); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_insert, GB_STRING text) + + TEXTAREA->insert(GB.ToZeroString(ARG(text))); + +END_METHOD + +BEGIN_PROPERTY(CTEXTAREA_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTAREA->hasBorder()); + else + TEXTAREA->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + + +/*************************************************************************** + + .TextArea.Selection + +***************************************************************************/ + +BEGIN_PROPERTY(CTEXTAREA_sel_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TEXTAREA->selText()); + else + TEXTAREA->setSelText(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_sel_length) + + GB.ReturnInteger(TEXTAREA->selEnd()-TEXTAREA->selStart()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_sel_start) + + GB.ReturnInteger(TEXTAREA->selStart()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTEXTAREA_sel_clear) + + TEXTAREA->selDelete(); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_sel_select, GB_INTEGER start; GB_INTEGER length) + + int start = VARGOPT(start, 0); + int length = VARGOPT(length, TEXTAREA->length()); + + TEXTAREA->selSelect(start, length); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_sel_all) + + TEXTAREA->selectAll(); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_pos, GB_INTEGER line; GB_INTEGER col) + + GB.ReturnInteger(TEXTAREA->toPosition(VARG(line),VARG(col))); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_line, GB_INTEGER pos) + + GB.ReturnInteger(TEXTAREA->toLine(VARG(pos))); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_col, GB_INTEGER pos) + + GB.ReturnInteger(TEXTAREA->toColumn(VARG(pos))); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_copy) + + TEXTAREA->copy(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_cut) + + TEXTAREA->cut(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_paste) + + TEXTAREA->paste(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_ensure_visible) + + TEXTAREA->ensureVisible(); + +END_METHOD + + +BEGIN_PROPERTY(CTEXTAREA_scrollbar) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->scrollBar()); + else + TEXTAREA->setScrollBar(VPROP(GB_INTEGER)); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_undo) + + TEXTAREA->undo(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_redo) + + TEXTAREA->redo(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_selected) + + GB.ReturnBoolean(TEXTAREA->isSelected()); + +END_METHOD + + +BEGIN_PROPERTY(TextArea_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->alignment()); + else + WIDGET->setAlignment(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD(TextArea_CursorAt, GB_INTEGER pos) + + int x, y; + WIDGET->getCursorPos(&x, &y, VARGOPT(pos, -1)); + GB.ReturnObject(GEOM.CreatePoint(x, y)); + +END_PROPERTY + +GB_DESC CTextAreaSelectionDesc[] = +{ + GB_DECLARE(".TextArea.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTAREA_sel_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTAREA_sel_start), + GB_PROPERTY_READ("Pos", "i", CTEXTAREA_sel_start), + + GB_METHOD("Hide", 0, CTEXTAREA_sel_clear, 0), + + GB_END_DECLARE +}; + +GB_DESC CTextAreaDesc[] = +{ + GB_DECLARE("TextArea", sizeof(CTEXTAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTEXTAREA_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CTEXTAREA_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_length), + GB_PROPERTY("ReadOnly", "b", CTEXTAREA_read_only), + + GB_PROPERTY("ScrollBar", "i", CTEXTAREA_scrollbar), + GB_PROPERTY("Wrap", "b", CTEXTAREA_wrap), + GB_PROPERTY("Border", "b", CTEXTAREA_border), + GB_PROPERTY("Alignment", "i", TextArea_Alignment), + + GB_PROPERTY("Line", "i", CTEXTAREA_line), + GB_PROPERTY("Column", "i", CTEXTAREA_column), + GB_PROPERTY("Pos", "i", CTEXTAREA_pos), + + GB_PROPERTY_SELF("Selection", ".TextArea.Selection"), + GB_METHOD("Select", NULL, CTEXTAREA_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, CTEXTAREA_sel_all, NULL), + GB_METHOD("Unselect", NULL, CTEXTAREA_sel_clear, NULL), + GB_PROPERTY_READ("Selected", "b", CTEXTAREA_selected), + + GB_METHOD("Clear", NULL, CTEXTAREA_clear, NULL), + GB_METHOD("Insert", NULL, CTEXTAREA_insert, "(Text)s"), + + GB_METHOD("Copy", NULL, CTEXTAREA_copy, NULL), + GB_METHOD("Cut", NULL, CTEXTAREA_cut, NULL), + GB_METHOD("Paste", NULL, CTEXTAREA_paste, NULL), + GB_METHOD("Undo", NULL, CTEXTAREA_undo, NULL), + GB_METHOD("Redo", NULL, CTEXTAREA_redo, NULL), + + GB_METHOD("ToPos", "i", CTEXTAREA_to_pos, "(Line)i(Column)i"), + GB_METHOD("ToLine", "i", CTEXTAREA_to_line, "(Pos)i"), + GB_METHOD("ToColumn", "i", CTEXTAREA_to_col, "(Pos)i"), + + GB_METHOD("EnsureVisible", NULL, CTEXTAREA_ensure_visible, NULL), + + GB_METHOD("CursorAt", "Point", TextArea_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + + TEXTAREA_DESCRIPTION, + + GB_END_DECLARE +}; + +#if 0 +/** TextEdit ***************************************************************/ + +BEGIN_METHOD(CTEXTEDIT_new, GB_OBJECT parent) + + InitControl(new gTextArea(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + WIDGET->setWrap(true); + +END_METHOD + +BEGIN_PROPERTY(CTEXTEDIT_scroll_x) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->scrollX()); + else + WIDGET->setScrollX(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_scroll_y) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->scrollY()); + else + WIDGET->setScrollY(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_width) + + GB.ReturnInteger(WIDGET->textWidth()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_height) + + GB.ReturnInteger(WIDGET->textHeight()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_alignment) + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_font) + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_color) + +END_PROPERTY + + +GB_DESC CTextEditFormatDesc[] = +{ + GB_DECLARE(".TextEditFormat", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Alignment", "i", CTEXTEDIT_format_alignment), + //GB_PROPERTY("Position", "i", CTEXTEDIT_format_position), + GB_PROPERTY("Font", "Font", CTEXTEDIT_format_font), + GB_PROPERTY("Color", "i", CTEXTEDIT_format_color), + + GB_END_DECLARE +}; + +GB_DESC CTextEditSelectionDesc[] = +{ + GB_DECLARE(".TextEditSelection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTAREA_sel_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTAREA_sel_start), + GB_METHOD("Hide", NULL, CTEXTAREA_sel_clear, NULL), + + GB_END_DECLARE +}; + + +GB_DESC CTextEditDesc[] = +{ + GB_DECLARE("TextEdit", sizeof(CTEXTAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTEXTEDIT_new, "(Parent)Container;"), + + GB_PROPERTY("ReadOnly", "b", CTEXTAREA_read_only), + + GB_METHOD("Clear", NULL, CTEXTAREA_clear, NULL), + + GB_PROPERTY("Text", "s", CTEXTAREA_text), + GB_METHOD("Insert", NULL, CTEXTAREA_insert, "(Text)s"), + + GB_PROPERTY("Paragraph", "i", CTEXTAREA_line), + GB_PROPERTY("Index", "i", CTEXTAREA_column), + GB_PROPERTY("Pos", "i", CTEXTAREA_pos), + + GB_METHOD("ToPos", "i", CTEXTAREA_to_pos, "(Paragraph)i(Index)i"), + GB_METHOD("ToParagraph", "i", CTEXTAREA_to_line, "(Pos)i"), + GB_METHOD("ToIndex", "i", CTEXTAREA_to_col, "(Pos)i"), + + GB_METHOD("EnsureVisible", NULL, CTEXTAREA_ensure_visible, NULL), + + GB_PROPERTY_SELF("Selection", ".TextEditSelection"), + GB_METHOD("Select", NULL, CTEXTAREA_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, CTEXTAREA_sel_all, NULL), + GB_METHOD("Unselect", NULL, CTEXTAREA_sel_clear, NULL), + GB_PROPERTY_READ("Selected", "b", CTEXTAREA_selected), + + GB_METHOD("Copy", NULL, CTEXTAREA_copy, NULL), + GB_METHOD("Cut", NULL, CTEXTAREA_cut, NULL), + GB_METHOD("Paste", NULL, CTEXTAREA_paste, NULL), + GB_METHOD("Undo", NULL, CTEXTAREA_undo, NULL), + GB_METHOD("Redo", NULL, CTEXTAREA_redo, NULL), + + GB_PROPERTY("Border", "b", CTEXTAREA_border), + GB_PROPERTY("ScrollBar", "i", CTEXTAREA_scrollbar), + + GB_PROPERTY("ScrollX", "i", CTEXTEDIT_scroll_x), + GB_PROPERTY("ScrollY", "i", CTEXTEDIT_scroll_y), + + GB_PROPERTY("TextWidth", "i", CTEXTEDIT_text_width), + GB_PROPERTY("TextHeight", "i", CTEXTEDIT_text_height), + + GB_PROPERTY_SELF("Format", ".TextEditFormat"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + GB_EVENT("Link", NULL, "(Path)s", &EVENT_Link), + + TEXTEDIT_DESCRIPTION, + + GB_END_DECLARE +}; + +#endif diff --git a/gb.gtk/src/CTextArea.h b/gb.gtk/src/CTextArea.h new file mode 100644 index 00000000..f33e9dc0 --- /dev/null +++ b/gb.gtk/src/CTextArea.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CTextArea.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTAREA_H +#define __CTEXTAREA_H + +#include "main.h" +#include "gtextarea.h" + + +#ifndef __CTEXTAREA_CPP +extern GB_DESC CTextAreaDesc[]; +extern GB_DESC CTextAreaSelectionDesc[]; +/*extern GB_DESC CTextEditDesc[]; +extern GB_DESC CTextEditSelectionDesc[]; +extern GB_DESC CTextEditFormatDesc[];*/ +#else + +#define THIS ((CTEXTAREA *)_object) +#define TEXTAREA ((gTextArea *)THIS->ob.widget) +#define WIDGET ((gTextArea *)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CTEXTAREA; + +#endif diff --git a/gb.gtk/src/CTextBox.cpp b/gb.gtk/src/CTextBox.cpp new file mode 100644 index 00000000..110b7c94 --- /dev/null +++ b/gb.gtk/src/CTextBox.cpp @@ -0,0 +1,287 @@ +/*************************************************************************** + + CTextBox.cpp + + (c) 2004-2005 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTBOX_CPP + +#include <stdlib.h> +#include <string.h> +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "CTextBox.h" +#include "CWidget.h" +#include "CContainer.h" + + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Cursor); + + +void CB_textbox_change(gTextBox *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Change, 0); +} + +void CB_textbox_activate(gTextBox *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Activate, 0); +} + +void CB_textbox_cursor(gTextBox *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Cursor, 0); +} + +/*************************************************************************** + + TextBox + +***************************************************************************/ + + +BEGIN_METHOD(TextBox_new, GB_OBJECT parent) + + InitControl(new gTextBox(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(TextBox_Clear) + + TEXTBOX->clear(); + +END_METHOD + + +BEGIN_METHOD(TextBox_Insert, GB_STRING text) + + TEXTBOX->insert(STRING(text),LENGTH(text)); + +END_METHOD + + +BEGIN_PROPERTY(TextBox_Text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TEXTBOX->text()); + else + TEXTBOX->setText(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Placeholder) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TEXTBOX->placeholder()); + else + TEXTBOX->setPlaceholder(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Length) + + GB.ReturnInteger(TEXTBOX->length()); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Alignment) + + if (READ_PROPERTY) { GB.ReturnInteger(TEXTBOX->alignment()); return; } + TEXTBOX->setAlignment(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Pos) + + if (READ_PROPERTY) { GB.ReturnInteger(TEXTBOX->position()); return; } + TEXTBOX->setPosition(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_ReadOnly) + + if (READ_PROPERTY) { GB.ReturnBoolean(TEXTBOX->isReadOnly()); return; } + TEXTBOX->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Border) + + if (READ_PROPERTY) { GB.ReturnBoolean(TEXTBOX->hasBorder()); return; } + TEXTBOX->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Password) + + if (READ_PROPERTY) { GB.ReturnBoolean(TEXTBOX->password()); return; } + TEXTBOX->setPassword(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_MaxLength) + + if (READ_PROPERTY) { GB.ReturnInteger(TEXTBOX->maxLength()); return; } + TEXTBOX->setMaxLength(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TextBox_Selected) + + GB.ReturnBoolean(TEXTBOX->isSelected()); + +END_METHOD + + +BEGIN_METHOD(TextBox_CursorAt, GB_INTEGER pos) + + int x, y; + + TEXTBOX->getCursorPos(&x, &y, VARGOPT(pos, -1)); + GB.ReturnObject(GEOM.CreatePoint(x, y)); + +END_PROPERTY + +/*************************************************************************** + + .TextBox.Selection + +***************************************************************************/ + +BEGIN_PROPERTY(TextBox_Selection_Text) + + char *buf; + + if (READ_PROPERTY) + { + buf=TEXTBOX->selText(); + GB.ReturnNewZeroString(buf); + g_free(buf); + return; + } + + buf=GB.ToZeroString(PROP(GB_STRING)); + TEXTBOX->setSelText(buf,strlen(buf)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Selection_Length) + + GB.ReturnInteger(TEXTBOX->selLength()); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Selection_Start) + + GB.ReturnInteger(TEXTBOX->selStart()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TextBox_Unselect) + + TEXTBOX->selClear(); + +END_METHOD + +BEGIN_METHOD_VOID(TextBox_SelectAll) + + TEXTBOX->selectAll(); + +END_METHOD + +BEGIN_METHOD(TextBox_Select, GB_INTEGER start; GB_INTEGER length) + + TEXTBOX->select(VARG(start),VARG(length)); + +END_METHOD + + +//------------------------------------------------------------------------- + +GB_DESC CTextBoxSelectionDesc[] = +{ + GB_DECLARE(".TextBox.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", TextBox_Selection_Text), + GB_PROPERTY_READ("Length", "i", TextBox_Selection_Length), + GB_PROPERTY_READ("Start", "i", TextBox_Selection_Start), + GB_PROPERTY_READ("Pos", "i", TextBox_Selection_Start), + + GB_METHOD("Hide", 0, TextBox_Unselect, 0), + + GB_END_DECLARE +}; + +GB_DESC CTextBoxDesc[] = +{ + GB_DECLARE("TextBox", sizeof(CTEXTBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, TextBox_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", TextBox_Text), + GB_PROPERTY("Alignment", "i", TextBox_Alignment), + GB_PROPERTY_READ("Length", "i", TextBox_Length), + GB_PROPERTY("Pos", "i", TextBox_Pos), + GB_PROPERTY("ReadOnly", "b", TextBox_ReadOnly), + GB_PROPERTY("Border", "b", TextBox_Border), + GB_PROPERTY("Password", "b", TextBox_Password), + GB_PROPERTY("MaxLength", "i", TextBox_MaxLength), + GB_PROPERTY("Placeholder", "s", TextBox_Placeholder), + + GB_PROPERTY_SELF("Selection", ".TextBox.Selection"), + GB_METHOD("Select", 0, TextBox_Select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", 0, TextBox_SelectAll, 0), + GB_METHOD("Unselect", 0, TextBox_Unselect, 0), + GB_PROPERTY_READ("Selected", "b", TextBox_Selected), + + GB_METHOD("Clear", 0, TextBox_Clear, 0), + GB_METHOD("Insert", 0, TextBox_Insert, "(Text)s"), + + GB_METHOD("CursorAt", "Point", TextBox_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", 0, 0, &EVENT_Change), + GB_EVENT("Activate", 0, 0, &EVENT_Activate), + GB_EVENT("Cursor", 0, 0, &EVENT_Cursor), + + TEXTBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CTextBox.h b/gb.gtk/src/CTextBox.h new file mode 100644 index 00000000..515c63f4 --- /dev/null +++ b/gb.gtk/src/CTextBox.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + CTextBox.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTBOX_H +#define __CTEXTBOX_H + +#include "main.h" +#include "gtextbox.h" + +#ifndef __CTEXTBOX_CPP + +extern GB_DESC CTextBoxSelectionDesc[]; +extern GB_DESC CTextBoxDesc[]; + +#else + +#define THIS ((CTEXTBOX *)_object) +#define TEXTBOX ((gTextBox *)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CTEXTBOX; + +#endif diff --git a/gb.gtk/src/CTrayIcon.cpp b/gb.gtk/src/CTrayIcon.cpp new file mode 100644 index 00000000..1f28e09c --- /dev/null +++ b/gb.gtk/src/CTrayIcon.cpp @@ -0,0 +1,331 @@ +/*************************************************************************** + + CTrayIcon.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTRAYICON_CPP + +#include <stdio.h> + +#include "gambas.h" +#include "widgets.h" +#include "CTrayIcon.h" +#include "CPicture.h" +#include "CContainer.h" +#include "CMenu.h" +#include "gmouse.h" + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_MiddleClick); +DECLARE_EVENT(EVENT_Scroll); + +void CB_trayicon_destroy(gTrayIcon *sender) +{ + CTRAYICON *_object = (CTRAYICON*)sender->hFree; + THIS->base.widget = NULL; + GB.Unref(POINTER(&_object)); +} + +void CB_trayicon_click(gTrayIcon *sender, int button) +{ + if (button == 1) + GB.Raise(sender->hFree, EVENT_Click, 0); + else if (button == 2) + GB.Raise(sender->hFree, EVENT_MiddleClick, 0); +} + +void CB_trayicon_menu(gTrayIcon *sender) +{ + CTRAYICON *_object = (CTRAYICON *)sender->hFree; + + if (THIS->popup) + { + void *parent = GB.Parent(THIS); + if (parent && !CWIDGET_check(parent) && GB.Is(parent, CLASS_Control)) + { + gMainWindow *window = ((CWIDGET *)parent)->widget->window(); + gMenu *menu = gMenu::findFromName(window, THIS->popup); + if (menu) + { + menu->popup(); + CMENU_check_popup_click(); + } + return; + } + } + + //GB.Raise(sender->hFree, EVENT_Menu, 0); +} + +void CB_trayicon_scroll(gTrayIcon *sender) +{ + GB.Raise(sender->hFree, EVENT_Scroll, 2, GB_T_FLOAT, (float)gMouse::delta(), GB_T_INTEGER, gMouse::orientation()); +} + +static int CTRAYICON_check(void *_object) +{ + return TRAYICON == NULL; +} + +BEGIN_METHOD_VOID(TrayIcon_new) + + THIS->base.widget = new gTrayIcon(); + TRAYICON->hFree = (void*)THIS; + + THIS->base.tag.type = GB_T_NULL; + + GB.Ref(THIS); + //Add_Tray(_object); + +END_METHOD + +static void destroy_tray_icon(CTRAYICON *_object) +{ + if (TRAYICON) + { + delete TRAYICON; + THIS->base.widget = NULL; + MAIN_check_quit(); + } +} + +BEGIN_METHOD_VOID(TrayIcon_free) + + GB.StoreObject(NULL, POINTER(&THIS->picture)); + GB.StoreVariant(NULL, &THIS->base.tag); + GB.FreeString(&THIS->popup); + + destroy_tray_icon(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcon_Delete) + + destroy_tray_icon(THIS); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcon_Picture) + + if (READ_PROPERTY) + { + GB.ReturnObject(THIS->picture); + return; + } + + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->picture)); + if (THIS->picture) + TRAYICON->setPicture(THIS->picture->picture); + else + TRAYICON->setPicture(0); + +END_PROPERTY + +BEGIN_PROPERTY(TrayIcon_Text) + + if (READ_PROPERTY) + { + GB.ReturnNewZeroString(TRAYICON->tooltip()); + return; + } + + TRAYICON->setTooltip(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TrayIcon_Show) + + TRAYICON->show(); + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcon_Hide) + + TRAYICON->hide(); + MAIN_check_quit(); + +END_METHOD + +BEGIN_PROPERTY(TrayIcon_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(TRAYICON->isVisible()); + else + { + TRAYICON->setVisible(VPROP(GB_BOOLEAN)); + if (!VPROP(GB_BOOLEAN)) + MAIN_check_quit(); + } + +END_PROPERTY + +BEGIN_PROPERTY(TrayIcon_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->base.tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->base.tag); + +END_METHOD + +BEGIN_PROPERTY(TrayIcons_Count) + + GB.ReturnInteger(gTrayIcon::count()); + +END_PROPERTY + +BEGIN_METHOD(TrayIcons_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= gTrayIcon::count()) + { + GB.Error("Bad index"); + return; + } + + GB.ReturnObject(gTrayIcon::get(index)->hFree); + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcons_next) + + int *vl; + + vl = (int *)GB.GetEnum(); + if (*vl >= gTrayIcon::count()) + { + GB.StopEnum(); + } + else + { + GB.ReturnObject (gTrayIcon::get(*vl)->hFree); + (*vl)++; + } + +END_METHOD + +BEGIN_PROPERTY(TrayIcon_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS->popup); + else + GB.StoreString(PROP(GB_STRING), &(THIS->popup)); + +END_PROPERTY + +BEGIN_METHOD_VOID(TrayIcon_unknown) + + static char prop[32]; + char *name = GB.GetUnknown(); + + if (strcasecmp(name, "ScreenX") == 0 || strcasecmp(name, "ScreenY") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(GTK_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(0); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else if (strcasecmp(name, "W") == 0 || strcasecmp(name, "Width") == 0 || strcasecmp(name, "H") == 0 || strcasecmp(name, "Height") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(GTK_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(24); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + } + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcons_DeleteAll) + + gTrayIcon::exit(); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC TrayIconsDesc[] = +{ + GB_DECLARE("TrayIcons", 0), GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("Count", "i", TrayIcons_Count), + GB_STATIC_METHOD("_get","TrayIcon", TrayIcons_get,"(Index)i"), + GB_STATIC_METHOD("_next", "TrayIcon", TrayIcons_next, NULL), + GB_STATIC_METHOD("DeleteAll", NULL, TrayIcons_DeleteAll, NULL), + + GB_END_DECLARE +}; + +GB_DESC TrayIconDesc[] = +{ + GB_DECLARE("TrayIcon", sizeof(CTRAYICON)), + GB_HOOK_CHECK(CTRAYICON_check), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_METHOD("_new", NULL, TrayIcon_new, NULL), + GB_METHOD("_free", NULL, TrayIcon_free, NULL), + + GB_METHOD("Show", NULL, TrayIcon_Show, NULL), + GB_METHOD("Hide", NULL, TrayIcon_Hide, NULL), + GB_METHOD("Delete", NULL, TrayIcon_Hide, NULL), + + GB_PROPERTY("Picture", "Picture", TrayIcon_Picture), + GB_PROPERTY("Icon", "Picture", TrayIcon_Picture), + GB_PROPERTY("Visible", "b", TrayIcon_Visible), + + GB_PROPERTY("Text", "s", TrayIcon_Text), + GB_PROPERTY("PopupMenu", "s", TrayIcon_PopupMenu), + GB_PROPERTY("Tooltip", "s", TrayIcon_Text), + GB_PROPERTY("Tag", "v", TrayIcon_Tag), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("MiddleClick", NULL, NULL, &EVENT_MiddleClick), + GB_EVENT("Scroll", NULL, "(Delta)f(Orientation)i", &EVENT_Scroll), + + GB_METHOD("_unknown", "v", TrayIcon_unknown, "."), + + TRAYICON_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/CTrayIcon.h b/gb.gtk/src/CTrayIcon.h new file mode 100644 index 00000000..9ca31f94 --- /dev/null +++ b/gb.gtk/src/CTrayIcon.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CTrayIcon.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTRAYICON_H +#define __CTRAYICON_H + +#include "main.h" +#include "gtrayicon.h" +#include "CPicture.h" + +#ifndef __CTRAYICON_CPP + +extern GB_DESC TrayIconDesc[]; +extern GB_DESC TrayIconsDesc[]; + +#else + +#define THIS ((CTRAYICON *)_object) +#define TRAYICON ((gTrayIcon *)(THIS->base.widget)) + +#endif + +typedef + struct + { + GTK_CONTROL base; + CPICTURE *picture; + char *popup; + } + CTRAYICON; + +#endif diff --git a/gb.gtk/src/CWatcher.cpp b/gb.gtk/src/CWatcher.cpp new file mode 100644 index 00000000..53400a4e --- /dev/null +++ b/gb.gtk/src/CWatcher.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + + CWatcher.cpp + + (c) 2004-2005 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWATCHER_CPP + +#include "main.h" +#include "gapplication.h" +#include "CWatcher.h" + +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +//DECLARE_EVENT(EVENT_Remove); + +static void connect_signals(GtkWidget *wid, void *_object); + +static void raise_show(GtkWidget *widget, CWATCHER *_object) +{ + //fprintf(stderr, "raise_show: %p %s [%d]\n", THIS->wid, THIS->wid->widget->name(), gApplication::_disable_mapping_events); + if (!gApplication::_disable_mapping_events && !THIS->visible) + { + THIS->visible = true; + GB.Raise(THIS, EVENT_Show, 0); + } +} + +static void raise_hide(GtkWidget *widget, CWATCHER *_object) +{ + //fprintf(stderr, "raise_hide: %p %s [%d]\n", THIS->wid, THIS->wid->widget->name(), gApplication::_disable_mapping_events); + if (!gApplication::_disable_mapping_events && THIS->visible) + { + THIS->visible = false; + GB.Raise(THIS, EVENT_Hide, 0); + } +} + +static void cb_init_later(void *_object) +{ + if (THIS->wid->widget->isReallyVisible()) + raise_show(NULL, THIS); + else + raise_hide(NULL, THIS); + GB.Unref(&_object); +} + +static void raise_configure(GtkWidget *widget, GdkEventConfigure *e, CWATCHER *_object) +{ + GB.Ref(_object); + + if (THIS->x != e->x || THIS->y != e->y) + { + THIS->x = e->x; + THIS->y = e->y; + GB.Raise(THIS, EVENT_Move, 0); + } + + if (THIS->w != e->width || THIS->h != e->height) + { + THIS->w = e->width; + THIS->h = e->height; + GB.Raise(THIS, EVENT_Resize, 0); + } + + GB.Unref(POINTER(&_object)); +} + +static void cb_destroy(GtkWidget *widget, CWATCHER *_object) +{ + gControl *ctrl = THIS->wid->widget; + + //fprintf(stderr, "cb_destroy: %p %s\n", THIS->wid, ctrl->name()); + if (ctrl->_no_delete) + { + connect_signals(ctrl->border, _object); + } + else + { + GB.Unref(POINTER(&THIS->wid)); + THIS->wid = 0; + } +} + +static void connect_signals(GtkWidget *wid, void *_object) +{ + g_signal_connect(G_OBJECT(wid), "map", G_CALLBACK(raise_show), _object); + g_signal_connect(G_OBJECT(wid), "unmap", G_CALLBACK(raise_hide), _object); + g_signal_connect(G_OBJECT(wid), "configure-event", G_CALLBACK(raise_configure), _object); + g_signal_connect(G_OBJECT(wid), "destroy", G_CALLBACK(cb_destroy), _object); +} + +/** Watcher class *********************************************************/ + +BEGIN_METHOD(CWATCHER_new, GB_OBJECT control) + + gControl *control; + + THIS->wid = (CWIDGET*)VARG(control); + + if (GB.CheckObject(THIS->wid)) + return; + + //fprintf(stderr, "Watcher %p: Ref %p (%s %p)\n", _object, THIS->wid, GB.GetClassName(THIS->wid), THIS->wid); + GB.Ref((void *)THIS->wid); + + control = THIS->wid->widget; + + THIS->x = control->left() - 1; + THIS->y = control->top() - 1; + THIS->w = control->width() - 1; + THIS->h = control->height() - 1; + + //fprintf(stderr, "new watcher %s %d\n", control->name(), control->isReallyVisible()); + + connect_signals(control->border, THIS); + + GB.Ref(THIS); + GB.Post((GB_CALLBACK)cb_init_later, (intptr_t)THIS); + +END_METHOD + +BEGIN_METHOD_VOID(CWATCHER_free) + + //fprintf(stderr, "Watcher %p: UnRef %p %p ?\n", THIS, THIS->wid, THIS->wid ? THIS->wid->widget : 0); + + if (THIS->wid) + { + if (THIS->wid->widget) + g_signal_handlers_disconnect_matched(G_OBJECT(THIS->wid->widget->border), G_SIGNAL_MATCH_DATA, 0, (GQuark)0, NULL, NULL, _object); + GB.Unref(POINTER(&THIS->wid)); + } + +END_METHOD + +BEGIN_PROPERTY(CWATCHER_control) + + GB.ReturnObject((void*)THIS->wid); + +END_PROPERTY + +GB_DESC CWatcherDesc[] = +{ + GB_DECLARE("Watcher", sizeof(CWATCHER)), + + GB_METHOD("_new", 0, CWATCHER_new, "(Control)Control;"), + GB_METHOD("_free", 0, CWATCHER_free, 0), + + GB_PROPERTY("Control", "Control", CWATCHER_control), + + GB_EVENT("Move", 0, 0, &EVENT_Move), + GB_EVENT("Resize", 0, 0, &EVENT_Resize), + GB_EVENT("Show", 0, 0, &EVENT_Show), + GB_EVENT("Hide", 0, 0, &EVENT_Hide), + + GB_CONSTANT("_DefaultEvent", "s", "Resize"), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CWatcher.h b/gb.gtk/src/CWatcher.h new file mode 100644 index 00000000..5e6fe76a --- /dev/null +++ b/gb.gtk/src/CWatcher.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + CWatcher.h + + (c) 2004-2005 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWATCHER_H +#define __CWATCHER_H + +#include "gambas.h" +#include "CWidget.h" + +typedef + struct + { + GB_BASE ob; + CWIDGET *wid; + int x, y, w, h; + unsigned visible : 1; + } + CWATCHER; + +#ifndef __CWATCHER_CPP +extern GB_DESC CWatcherDesc[]; +#else +#define THIS ((CWATCHER*)_object) +#endif +#endif diff --git a/gb.gtk/src/CWidget.cpp b/gb.gtk/src/CWidget.cpp new file mode 100644 index 00000000..6e5fc15f --- /dev/null +++ b/gb.gtk/src/CWidget.cpp @@ -0,0 +1,1108 @@ +/*************************************************************************** + + CWidget.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWIDGET_CPP + +#include "widgets.h" +#include "gapplication.h" + +#include "CWidget.h" +#include "CWindow.h" +#include "CMenu.h" +#include "CFont.h" +#include "CMouse.h" +#include "CPicture.h" +#include "CContainer.h" +#include "CClipboard.h" +#include "CPanel.h" + +extern int MAIN_scale; + +DECLARE_EVENT(EVENT_Enter); +DECLARE_EVENT(EVENT_GotFocus); +DECLARE_EVENT(EVENT_LostFocus); +DECLARE_EVENT(EVENT_KeyPress); +DECLARE_EVENT(EVENT_KeyRelease); +DECLARE_EVENT(EVENT_Leave); +DECLARE_EVENT(EVENT_MouseDown); +DECLARE_EVENT(EVENT_MouseMove); +DECLARE_EVENT(EVENT_MouseDrag); +DECLARE_EVENT(EVENT_MouseUp); +DECLARE_EVENT(EVENT_MouseWheel); +DECLARE_EVENT(EVENT_DblClick); +DECLARE_EVENT(EVENT_Menu); +DECLARE_EVENT(EVENT_Drag); +DECLARE_EVENT(EVENT_DragMove); +DECLARE_EVENT(EVENT_Drop); +DECLARE_EVENT(EVENT_DragLeave); + +//Plug +DECLARE_EVENT(EVENT_Plugged); +DECLARE_EVENT(EVENT_Unplugged); +DECLARE_EVENT(EVENT_PlugError); + +//static void *CLASS_Image = NULL; + +/** Action *****************************************************************/ + +static bool has_action(void *control) +{ + if (GB.Is(control, GB.FindClass("Menu"))) + { + gMenu *menu = ((CMENU *)(control))->widget; + return menu ? menu->action() : false; + } + else + { + gControl *ctrl = ((CWIDGET *)(control))->widget; + return ctrl ? ctrl->action() : false; + } +} + +static void set_action(void *control, bool v) +{ + if (GB.Is(control, GB.FindClass("Menu"))) + { + gMenu *menu = ((CMENU *)(control))->widget; + if (menu) + menu->setAction(v); + } + else + { + gControl *ctrl = ((CWIDGET *)(control))->widget; + if (ctrl) + ctrl->setAction(v); + } +} + +#define HAS_ACTION(_control) has_action(_control) +#define SET_ACTION(_control, _flag) set_action(_control, _flag) + +#include "gb.form.action.h" + + +/** Control ****************************************************************/ + +void gb_plug_raise_plugged(gControl *sender) +{ + CWIDGET *_ob=GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob,EVENT_Plugged,0); +} + +void gb_plug_raise_unplugged(gControl *sender) +{ + CWIDGET *_ob=GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob,EVENT_Unplugged,0); +} + +void gb_plug_raise_error(gControl *sender) +{ + CWIDGET *_ob=GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob,EVENT_PlugError,0); +} + +// static void raise_menu(void *_object) +// { +// GB.Raise(THIS, EVENT_Menu, 0); +// GB.Unref(POINTER(&_object)); +// } + +static int to_gambas_event(int type) +{ + switch (type) + { + case gEvent_MousePress: return EVENT_MouseDown; + case gEvent_MouseRelease: return EVENT_MouseUp; + case gEvent_MouseMove: return EVENT_MouseMove; + case gEvent_MouseDrag: return EVENT_MouseDrag; + case gEvent_MouseWheel: return EVENT_MouseWheel; + case gEvent_MouseMenu: return EVENT_Menu; + case gEvent_MouseDblClick: return EVENT_DblClick; + case gEvent_KeyPress: return EVENT_KeyPress; + case gEvent_KeyRelease: return EVENT_KeyRelease; + case gEvent_FocusIn: return EVENT_GotFocus; + case gEvent_FocusOut: return EVENT_LostFocus; + case gEvent_Enter: return EVENT_Enter; + case gEvent_Leave: return EVENT_Leave; + case gEvent_DragMove: return EVENT_DragMove; + case gEvent_Drop: return EVENT_Drop; + default: fprintf(stderr, "warning: to_gambas_event: unhandled event: %d\n", type); return -1; + } +} + +bool CB_control_can_raise(gControl *sender, int type) +{ + CWIDGET *ob = GetObject(sender); + if (!ob) + return false; + + type = to_gambas_event(type); + if (type < 0) + return false; + + return GB.CanRaise(ob, type); +} + +bool CB_control_mouse(gControl *sender, int type) +{ + CWIDGET *ob = GetObject(sender); + bool ret = false; + + if (!ob) return false; // possible, for MouseDrag for example + + switch(type) + { + case gEvent_MouseDrag: + ret = GB.Raise(ob, EVENT_MouseDrag, 0); + break; + + case gEvent_MouseMenu: + + for(;;) + { + if (GB.CanRaise(ob, EVENT_Menu)) + { + int old = gMenu::popupCount(); + if (GB.Raise(ob, EVENT_Menu, 0) || old != gMenu::popupCount()) + return true; + } + + if (ob->popup) + { + gMainWindow *window = sender->window(); + gMenu *menu = gMenu::findFromName(window, ob->popup); + if (menu) + { + menu->popup(); + CMENU_check_popup_click(); + } + return true; + } + + if (sender->hasNativePopup()) + return false; + + if (sender->isTopLevel()) + break; + + sender = sender->parent(); + ob = GetObject(sender); + } + + break; + + default: + ret = GB.Raise(ob, to_gambas_event(type), 0); + + } + + return ret; +} + +bool CB_control_key(gControl *sender, int type) +{ + return GB.Raise(GetObject(sender), to_gambas_event(type), 0); +} + +void CB_control_enter_leave(gControl *sender, int type) +{ + GB.Raise(GetObject(sender), to_gambas_event(type), 0); +} + +void CB_control_focus(gControl *sender, int type) +{ + GB.Raise(GetObject(sender), to_gambas_event(type), 0); +} + +bool CB_control_drag(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + /*if (!THIS) + return true;*/ + + if (!GB.CanRaise(THIS, EVENT_Drag)) + { + if (GB.CanRaise(THIS, EVENT_DragMove) || GB.CanRaise(THIS, EVENT_Drop)) + return false; + else + return true; + } + + return GB.Raise(THIS, EVENT_Drag, 0); +} + +void CB_control_drag_leave(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + GB.Raise(THIS, EVENT_DragLeave, 0); +} + +bool CB_control_drag_move(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + if (!THIS) + return true; + + if (!GB.CanRaise(THIS, EVENT_DragMove)) + return !GB.CanRaise(THIS, EVENT_Drag); + + return GB.Raise(THIS, EVENT_DragMove, 0); +} + +bool CB_control_drop(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + if (!THIS) + return false; + + if (!GB.CanRaise(THIS, EVENT_Drop)) + return false; + + GB.Raise(THIS, EVENT_Drop, 0); + return true; +} + +void CB_control_finish(gControl *control) +{ + CWIDGET *widget = (CWIDGET*)control->hFree; + + if (!widget) + return; + + GB.Detach(widget); + + GB.StoreVariant(NULL, POINTER(&widget->tag)); + GB.StoreObject(NULL, POINTER(&widget->cursor)); + + CACTION_register(widget, widget->action, NULL); + GB.FreeString(&widget->action); + + if (control->isTopLevel()) + CWINDOW_check_main_window((CWINDOW*)widget); + + GB.Unref(POINTER(&widget->font)); + GB.FreeString(&widget->popup); + + widget->font = NULL; + widget->widget = NULL; + GB.Unref(POINTER(&widget)); + + control->hFree = NULL; +} + + +void InitControl(gControl *control, CWIDGET *widget) +{ + static int n = 0; + char *name; + + if (control->hFree) + return; + + GB.Ref((void*)widget); + + widget->widget = control; + control->hFree = (void*)widget; + //fprintf(stderr, "InitControl: %p %p\n", control, widget); + + name = GB.GetLastEventName(); + if (!name) + { + char buffer[16]; + n++; + sprintf(buffer, "#%d", n); + control->setName(buffer); + } + else + control->setName(name); + + if (control->parent()) + CCONTAINER_raise_insert((CCONTAINER *)control->parent()->hFree, widget); +} + +CWIDGET *GetContainer(CWIDGET *control) +{ + gContainer *cont; + + if (GB.CheckObject(control)) + GB.Propagate(); + + cont = ((gContainer *)control->widget)->proxyContainer(); + //fprintf(stderr, "container: (%p %s)\n", control->widget, control->widget->name()); + + return GetObject(cont); +} + +int CWIDGET_get_handle(void *_object) +{ + return CONTROL->handle(); +} + +int CWIDGET_check(void *_object) +{ + return (!CONTROL || CONTROL->isDestroyed()); +} + +/************************************************************************************* + +Embedder + +**************************************************************************************/ +#ifndef GTK3 + +BEGIN_METHOD(CPLUGIN_new, GB_OBJECT parent) + + InitControl(new gPlugin(CONTAINER(VARG(parent))), (CWIDGET*)_object); + PLUGIN->onPlug = gb_plug_raise_plugged; + PLUGIN->onUnplug = gb_plug_raise_unplugged; + PLUGIN->onError = gb_plug_raise_error; + +END_METHOD + +BEGIN_PROPERTY(CPLUGIN_client) + + GB.ReturnInteger(PLUGIN->client()); + +END_PROPERTY + +BEGIN_METHOD(CPLUGIN_embed, GB_INTEGER id) + + PLUGIN->plug(VARG(id)); + +END_METHOD + +BEGIN_METHOD_VOID(CPLUGIN_discard) + + PLUGIN->discard(); + +END_METHOD + +#endif + +/************************************************************************************** + +Control + +**************************************************************************************/ + +BEGIN_PROPERTY(Control_X) + + if (READ_PROPERTY) { GB.ReturnInteger(CONTROL->left()); return; } + CONTROL->setLeft(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_ScreenX) + + GB.ReturnInteger(CONTROL->screenX()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Y) + + if (READ_PROPERTY) { GB.ReturnInteger(CONTROL->top()); return; } + CONTROL->setTop(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_ScreenY) + + GB.ReturnInteger(CONTROL->screenY()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Width) + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->width()); + else + CONTROL->setWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Height) + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->height()); + else + CONTROL->setHeight(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Font) + + if (!THIS->font) + { + THIS->font = CFONT_create(new gFont(), 0, THIS); + GB.Ref(THIS->font); + } + + if (READ_PROPERTY) + { + CONTROL->actualFontTo(((CFONT *)THIS->font)->font); + GB.ReturnObject(THIS->font); + } + else + { + CFONT *font = (CFONT *)VPROP(GB_OBJECT); + if (font) + CONTROL->setFont(font->font->copy()); + else + CONTROL->setFont(NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Design) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isDesign()); + else + { + bool v = VPROP(GB_BOOLEAN); + if (v == CONTROL->isDesign()) + return; + if (v) + CONTROL->setDesign(); + else + GB.Error("Design property cannot be reset"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isVisible()); + else + CONTROL->setVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isEnabled()); + else + CONTROL->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_HasFocus) + + GB.ReturnBoolean(CONTROL->hasFocus()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Hovered) + + GB.ReturnBoolean(CONTROL->hovered()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Expand) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isExpand()); + else + CONTROL->setExpand(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Ignore) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isIgnore()); + else + CONTROL->setIgnore(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD(Control_Move, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + if (MISSING(w) && MISSING(h)) + CONTROL->move(VARG(x), VARG(y)); + else + CONTROL->moveResize(VARG(x), VARG(y), VARGOPT(w, CONTROL->width()), VARGOPT(h, CONTROL->height())); + +END_METHOD + + +BEGIN_METHOD(Control_Resize, GB_INTEGER w; GB_INTEGER h) + + CONTROL->resize(VARG(w),VARG(h)); + +END_METHOD + +BEGIN_METHOD(Control_MoveScaled, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + int x, y, w, h; + + x = (int)(VARG(x) * MAIN_scale + 0.5); + y = (int)(VARG(y) * MAIN_scale + 0.5); + w = (MISSING(w) ? -1 : (int)(VARG(w) * MAIN_scale + 0.5)); + h = (MISSING(h) ? -1 : (int)(VARG(h) * MAIN_scale + 0.5)); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + if (w > 0 && h > 0) + CONTROL->moveResize(x, y, w, h); + else + CONTROL->move(x, y); + +END_METHOD + + +BEGIN_METHOD(Control_ResizeScaled, GB_FLOAT w; GB_FLOAT h) + + int w, h; + + w = (int)(VARG(w) * MAIN_scale + 0.5); + h = (int)(VARG(h) * MAIN_scale + 0.5); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + CONTROL->resize(w, h); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Delete) + + if (CONTROL) + { + if (CONTROL->isDragging()) + GB.Error("Control is being dragged"); + else + CONTROL->destroy(); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Show) + + CONTROL->show(); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Hide) + + CONTROL->hide(); + +END_METHOD + +BEGIN_METHOD(Control_Reparent, GB_OBJECT parent; GB_INTEGER x; GB_INTEGER y) + + CCONTAINER *parent = (CCONTAINER*)VARG(parent); + int x, y; + + if (parent || !GB.Is(THIS, CLASS_Window)) + { + if (GB.CheckObject(parent)) + return; + } + + x = CONTROL->x(); + y = CONTROL->y(); + + if (!MISSING(x) && !MISSING(y)) + { + x = VARG(x); + y = VARG(y); + } + + CONTROL->reparent(parent ? CONTAINER(parent) : NULL, x, y); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Raise) + + CONTROL->raise(); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Lower) + + CONTROL->lower(); + +END_METHOD + + +BEGIN_PROPERTY(Control_Next) + + if (READ_PROPERTY) + GB.ReturnObject(GetObject(CONTROL->next())); + else + { + CWIDGET *next = (CWIDGET *)VPROP(GB_OBJECT); + CONTROL->setNext(next ? next->widget : NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Previous) + + if (READ_PROPERTY) + GB.ReturnObject(GetObject(CONTROL->previous())); + else + { + CWIDGET *previous = (CWIDGET *)VPROP(GB_OBJECT); + CONTROL->setPrevious(previous ? previous->widget : NULL); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Control_Refresh) //, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CONTROL->refresh(); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_SetFocus) + + CONTROL->setFocus(); + +END_METHOD + + +BEGIN_PROPERTY(Control_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->tag); + +END_METHOD + + +BEGIN_PROPERTY(Control_Mouse) + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->mouse()); + else + CONTROL->setMouse(VPROP(GB_INTEGER)); + +END_METHOD + + +BEGIN_PROPERTY(Control_Cursor) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->cursor); + else + { + GB.StoreObject(PROP(GB_OBJECT), &THIS->cursor); + CCURSOR *c = (CCURSOR *)THIS->cursor; + CONTROL->setCursor(c ? c->cur : NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Background) + + if (CONTROL->proxy()) + { + if (READ_PROPERTY) + GB.GetProperty(GetObject(CONTROL->proxy()), "Background"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(GetObject(CONTROL->proxy()), "Background", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->background()); + else + CONTROL->setBackground(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Foreground) + + if (CONTROL->proxy()) + { + if (READ_PROPERTY) + GB.GetProperty(GetObject(CONTROL->proxy()), "Foreground"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(GetObject(CONTROL->proxy()), "Foreground", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->foreground()); + else + CONTROL->setForeground(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Parent) + + gContainer *parent = CONTROL->parent(); + + if (parent) + { + while (parent->proxyContainerFor()) + parent = parent->proxyContainerFor(); + } + + GB.ReturnObject(GetObject(parent)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control__Parent) + + GB.ReturnObject(GetObject(CONTROL->parent())); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Window) + + GB.ReturnObject(GetObject(CONTROL->window())); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Id) + + GB.ReturnInteger(CONTROL->handle()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Tooltip) + + if (READ_PROPERTY) + { + GB.ReturnNewZeroString(CONTROL->tooltip()); + return; + } + + CONTROL->setTooltip(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +/*BEGIN_METHOD_VOID(CWIDGET_screenshot) + + CPICTURE *img; + + GB.New(POINTER(&img), GB.FindClass("Picture"), 0, 0); + if (img->picture) delete img->picture; + img->picture=CONTROL->screenshot(); + GB.ReturnObject((void*)img); + +END_METHOD*/ + +BEGIN_METHOD_VOID(Control_Grab) + + CONTROL->grab(); + +END_METHOD + + +BEGIN_METHOD(Control_Drag, GB_VARIANT data; GB_STRING format) + + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + GB.GetFunction(&func, (void *)GB.FindClass("Drag"), "_call", NULL, NULL); + init = TRUE; + } + + GB.Push(2, GB_T_OBJECT, THIS, GB_T_VARIANT, &VARG(data)); + if (MISSING(format)) + { + GB.Call(&func, 2, FALSE); + } + else + { + GB.Push(1, GB_T_STRING, STRING(format), LENGTH(format)); + GB.Call(&func, 3, FALSE); + } + +END_METHOD + + +BEGIN_PROPERTY(Control_Drop) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->acceptDrops()); + else + CONTROL->setAcceptDrops(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Control_Tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isTracking()); + else + CONTROL->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(CONTROL->name()); + else + CONTROL->setName(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Action) + + if (READ_PROPERTY) + GB.ReturnString(THIS->action); + else + { + CACTION_register(THIS, THIS->action, GB.ToZeroString(PROP(GB_STRING))); + GB.StoreString(PROP(GB_STRING), &THIS->action); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Proxy) + + if (READ_PROPERTY) + GB.ReturnObject(GetObject(CONTROL->proxy())); + else + { + CWIDGET *proxy = (CWIDGET *)VPROP(GB_OBJECT); + if (CONTROL->setProxy(proxy ? proxy->widget : NULL)) + GB.Error("Circular proxy chain"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS->popup); + else + GB.StoreString(PROP(GB_STRING), &(THIS->popup)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_NoTabFocus) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isNoTabFocus()); + else + CONTROL->setNoTabFocus(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Direction) + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->direction()); + else + CONTROL->setDirection(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_RightToLeft) + + GB.ReturnBoolean(CONTROL->isRightToLeft()); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CWidgetDesc[] = +{ + GB_DECLARE("Control", sizeof(CWIDGET)), + + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(CWIDGET_check), + + //GB_METHOD("_free", 0, Control_Delete, 0), + + GB_METHOD("Move", NULL, Control_Move, "(X)i(Y)i[(Width)i(Height)i]"), + GB_METHOD("Resize", NULL, Control_Resize, "(Width)i(Height)i"), + GB_METHOD("MoveScaled", NULL, Control_MoveScaled, "(X)f(Y)f[(Width)f(Height)f]"), + GB_METHOD("ResizeScaled", NULL, Control_ResizeScaled, "(Width)f(Height)f"), + GB_METHOD("Delete", NULL, Control_Delete, NULL), + GB_METHOD("Show", NULL, Control_Show, NULL), + GB_METHOD("Hide", NULL, Control_Hide, NULL), + GB_METHOD("Reparent", NULL, Control_Reparent, "(Parent)Container;[(X)i(Y)i]"), + + GB_METHOD("Raise", NULL, Control_Raise, NULL), + GB_METHOD("Lower", NULL, Control_Lower, NULL), + + GB_PROPERTY("Next", "Control", Control_Next), + GB_PROPERTY("Previous", "Control", Control_Previous), + + GB_METHOD("SetFocus", NULL, Control_SetFocus, NULL), + GB_METHOD("Refresh", NULL, Control_Refresh, NULL), + //GB_METHOD("Screenshot", "Picture", CWIDGET_screenshot, 0), + GB_METHOD("Grab", NULL, Control_Grab, NULL), + GB_METHOD("Drag", "Control", Control_Drag, "(Data)v[(Format)s]"), + + GB_PROPERTY("X", "i", Control_X), + GB_PROPERTY("Y", "i", Control_Y), + GB_PROPERTY_READ("ScreenX", "i", Control_ScreenX), + GB_PROPERTY_READ("ScreenY", "i", Control_ScreenY), + GB_PROPERTY("W", "i", Control_Width), + GB_PROPERTY("H", "i", Control_Height), + GB_PROPERTY("Left", "i", Control_X), + GB_PROPERTY("Top", "i", Control_Y), + GB_PROPERTY("Width", "i", Control_Width), + GB_PROPERTY("Height", "i", Control_Height), + + GB_PROPERTY("Visible", "b", Control_Visible), + GB_PROPERTY("Enabled", "b", Control_Enabled), + GB_PROPERTY_READ("HasFocus", "b", Control_HasFocus), + GB_PROPERTY_READ("Hovered", "b", Control_Hovered), + + GB_PROPERTY("Expand", "b", Control_Expand), + GB_PROPERTY("Ignore", "b", Control_Ignore), + + GB_PROPERTY("Font", "Font", Control_Font), + GB_PROPERTY("Background", "i", Control_Background), + GB_PROPERTY("Foreground", "i", Control_Foreground), + + GB_PROPERTY("Design", "b", Control_Design), + GB_PROPERTY("Name", "s", Control_Name), + GB_PROPERTY("Tag", "v", Control_Tag), + GB_PROPERTY("Tracking", "b", Control_Tracking), + GB_PROPERTY("Mouse", "i", Control_Mouse), + GB_PROPERTY("Cursor", "Cursor", Control_Cursor), + GB_PROPERTY("Tooltip", "s", Control_Tooltip), + GB_PROPERTY("Drop", "b", Control_Drop), + GB_PROPERTY("Action", "s", Control_Action), + GB_PROPERTY("PopupMenu", "s", Control_PopupMenu), + GB_PROPERTY("Proxy", "Control", Control_Proxy), + GB_PROPERTY("NoTabFocus", "b", Control_NoTabFocus), + GB_PROPERTY("Direction", "i", Control_Direction), + GB_PROPERTY_READ("RightToLeft", "b", Control_RightToLeft), + + GB_PROPERTY_READ("Parent", "Container", Control_Parent), + GB_PROPERTY_READ("_Parent", "Container", Control__Parent), + GB_PROPERTY_READ("Window", "Window", Control_Window), + GB_PROPERTY_READ("Id", "i", Control_Id), + GB_PROPERTY_READ("Handle", "i", Control_Id), + + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("GotFocus", NULL, NULL, &EVENT_GotFocus), + GB_EVENT("LostFocus", NULL, NULL, &EVENT_LostFocus), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPress), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyRelease), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDrag", NULL, NULL, &EVENT_MouseDrag), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("MouseWheel", NULL, NULL, &EVENT_MouseWheel), + GB_EVENT("DblClick", NULL, NULL, &EVENT_DblClick), + GB_EVENT("Menu", NULL, NULL, &EVENT_Menu), + GB_EVENT("Drag", NULL, NULL, &EVENT_Drag), + GB_EVENT("DragMove", NULL, NULL, &EVENT_DragMove), + GB_EVENT("Drop", NULL, NULL, &EVENT_Drop), + GB_EVENT("DragLeave", NULL, NULL, &EVENT_DragLeave), + + CONTROL_DESCRIPTION, + + GB_END_DECLARE +}; + +#ifndef GTK3 +GB_DESC CPluginDesc[] = +{ + GB_DECLARE("Embedder", sizeof(CPLUGIN)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CPLUGIN_new, "(Parent)Container;"), + GB_METHOD("Embed", 0, CPLUGIN_embed, "(Client)i"), + GB_METHOD("Discard", 0, CPLUGIN_discard, 0), + + GB_PROPERTY_READ("Client", "i", CPLUGIN_client), + //GB_PROPERTY("Border", "i<Border>", CPLUGIN_border), + + GB_EVENT("Embed", NULL, NULL, &EVENT_Plugged), + GB_EVENT("Close", NULL, NULL, &EVENT_Unplugged), + GB_EVENT("Error", NULL, NULL, &EVENT_PlugError), /* TODO */ + + EMBEDDER_DESCRIPTION, + + GB_END_DECLARE +}; +#endif diff --git a/gb.gtk/src/CWidget.h b/gb.gtk/src/CWidget.h new file mode 100644 index 00000000..7288a39b --- /dev/null +++ b/gb.gtk/src/CWidget.h @@ -0,0 +1,94 @@ +/*************************************************************************** + + CWidget.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWIDGET_H +#define __CWIDGET_H + +#include "main.h" +#include "gcontrol.h" + +#ifdef GTK3 +#else +#include "gplugin.h" +#endif + +#ifndef __CWIDGET_CPP + +extern GB_DESC CWidgetDesc[]; +extern GB_DESC CPluginDesc[]; + +#else + +#define THIS ((CWIDGET *)_object) +#define CONTROL (THIS->widget) +#define PLUGIN (((CPLUGIN*)_object)->widget) +#define CPLUGIN_PROPERTIES "*" + +#endif + +typedef + struct + { + GB_BASE ob; + gControl *widget; + GB_VARIANT_VALUE tag; + void *font; + void *cursor; + char *popup; + char *action; + } + CWIDGET; + +#ifdef GTK3 +#else +typedef + struct + { + GB_BASE ob; + gPlugin *widget; + GB_VARIANT_VALUE tag; + void *font; + char *popup; + } + CPLUGIN; +#endif + +void InitControl(gControl *control, CWIDGET *widget); +void DeleteControl(gControl *control); +CWIDGET *GetContainer(CWIDGET *control); +#define GetObject(_control) ((CWIDGET *)((_control) ? (_control)->hFree : NULL)) + +#define CWIDGET_PROPERTIES CCONTROL_PROPERTIES + +#define CONTAINER(_parent) ((gContainer *)GetContainer((CWIDGET *)_parent)->widget) + +DECLARE_PROPERTY(CCONTROL_action); + +int CWIDGET_check(void *_object); +int CWIDGET_get_handle(void *_object); + +void CACTION_register(void *control, const char *old, const char *key); +void CACTION_raise(void *control); + +#endif + diff --git a/gb.gtk/src/CWindow.cpp b/gb.gtk/src/CWindow.cpp new file mode 100644 index 00000000..54d2415d --- /dev/null +++ b/gb.gtk/src/CWindow.cpp @@ -0,0 +1,983 @@ +/*************************************************************************** + + CWindow.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWINDOW_CPP + +#include "main.h" +#include "CWindow.h" +#include "CWidget.h" +#include "CContainer.h" +#include "CPicture.h" +#include "CMenu.h" +#include "CDraw.h" +#include "gapplication.h" + +typedef + struct { + uint index; + GPtrArray *list; + } + WINDOW_ENUM; + +int CWINDOW_Embedder = 0; +bool CWINDOW_Embedded = false; + +CWINDOW *CWINDOW_Active = NULL; +CWINDOW *CWINDOW_Main = NULL; +static int MODAL_windows = 0; + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Deactivate); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Title); +DECLARE_EVENT(EVENT_Icon); +DECLARE_EVENT(EVENT_Font); +DECLARE_EVENT(EVENT_State); + +void CWINDOW_check_main_window(CWINDOW *win) +{ + if (CWINDOW_Main == win) + { + //fprintf(stderr, "CWINDOW_Main = NULL\n"); + CWINDOW_Main = NULL; + } +} + + +void CB_window_open(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Open, 0); +} + +void CB_window_font(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Font, 0); +} + +void CB_window_state(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_State, 0); +} + +void CB_window_show(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + + GB.Ref(THIS); + GB.Raise(THIS, EVENT_Show, 0); + if (!sender->spontaneous()) + CACTION_raise(THIS); + GB.Unref(POINTER(&_object)); +} + +void CB_window_hide(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + + GB.Ref(THIS); + GB.Raise(THIS, EVENT_Hide, 0); + if (!sender->spontaneous()) + CACTION_raise(THIS); + GB.Unref(POINTER(&_object)); +} + +void CB_window_move(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Move, 0); +} + +void CB_window_resize(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Resize, 0); +} + +static bool close_window(CWINDOW *_object, int ret = 0) +{ + THIS->ret = ret; + + return WINDOW->close(); +} + +void CWINDOW_delete_all(bool main) +{ + int i; + gMainWindow *win; + CWINDOW *window; + + for(i = 0;; i++) + { + win = gMainWindow::get(i); + if (!win) + break; + + window = (CWINDOW *)GetObject(win); + if (window == CWINDOW_Main) + continue; + + win->destroy(); + } + + if (main && CWINDOW_Main) + ((gMainWindow *)(CWINDOW_Main->ob.widget))->destroy(); +} + +bool CWINDOW_must_quit() +{ + int i; + gMainWindow *win; + + for(i = 0; i < gMainWindow::count(); i++) + { + win = gMainWindow::get(i); + if (win->isTopLevel() && win->isOpened()) + return false; + } + + return true; +} + +bool CB_window_close(gMainWindow *sender) +{ + CWINDOW *_object = (CWINDOW*)GetObject(sender); + + if (!THIS) + return false; + + if (GB.Raise(THIS, EVENT_Close, 0)) + return true; + + if (CWINDOW_Main && sender == CWINDOW_Main->ob.widget) + { + if (gMainWindow::closeAll()) + return true; + + if (!sender->isPersistent()) + { + CWINDOW_delete_all(false); + CWINDOW_Main = NULL; + } + } + + if (sender->isEmbedded()) + { + CWINDOW_Embedder = 0; + CWINDOW_Embedded = false; + } + + MAIN_check_quit(); + + return false; +} + + +gMainWindow *CB_window_activate(gControl *control) +{ + gMainWindow *active; + CWINDOW *active_ob; + + /*if (!control) + fprintf(stderr, "CB_window_activate: NULL\n"); + else + fprintf(stderr, "CB_window_activate: %s.%s\n", control->window()->name(), control->name());*/ + + if (control) + { + active = control->window(); + for(;;) + { + active_ob = (CWINDOW *)GetObject(active); + if (active->isTopLevel()) + break; + if (GB.CanRaise(active_ob, EVENT_Activate)) + break; + active = active->parent()->window(); + } + } + else + { + active = NULL; + active_ob = NULL; + } + + if (active_ob != CWINDOW_Active) + { + if (CWINDOW_Active) + { + //fprintf(stderr, "deactivate: %p %s\n", CWINDOW_Active, CWINDOW_Active->ob.widget->name()); + GB.Raise(CWINDOW_Active, EVENT_Deactivate, 0); + CWINDOW_Active = NULL; + } + + if (active) + { + //fprintf(stderr, "activate: %p %s\n", active, active->name()); + GB.Raise(active_ob, EVENT_Activate, 0); + } + + CWINDOW_Active = active_ob; + } + + return active; +} + +/*static void cb_state(gMainWindow *sender) +{ + CWINDOW *_object = (CWINDOW*)GetObject(sender); + GB.Raise(THIS, EVENT_State, 0); +}*/ + +static void show_later(CWINDOW *_object) +{ + /* If the user has explicitely hidden the window since the posting of this routines + then do nothing + */ + + if (WINDOW && !WINDOW->isHidden()) + { + //fprintf(stderr, "show_later: %p %s\n", WINDOW, WINDOW->name()); + if (!WINDOW->emitOpen()) + WINDOW->show(); + } + GB.Unref(POINTER(&_object)); +} + +/*************************************************************************** + + Window + +***************************************************************************/ + +BEGIN_METHOD(CWINDOW_new, GB_OBJECT parent) + + gMainWindow *win; + GB_CLASS CLASS_container; + gContainer *parent = NULL; + int plug = 0; + + if (!gApplication::isInit()) + { + GB.Error("GUI is not initialized"); + return; + } + + if (!MISSING(parent) && VARG(parent)) + { + CLASS_container = GB.FindClass("Container"); + if (GB.Conv((GB_VALUE *)(void *)ARG(parent), (GB_TYPE)CLASS_container)) + return; + + parent = CONTAINER(VARG(parent)); + } + + if (CWINDOW_Embedder && (!CWINDOW_Embedded) && (!parent)) + plug = CWINDOW_Embedder; + + if (parent) + win = new gMainWindow(parent); + else if (!plug) + win = new gMainWindow(); + else + { + win = new gMainWindow(plug); + if (!win->border) + { + delete win; + GB.Error("Embedder control is not supported on this platform"); + return; + } + } + + THIS->ob.widget = win; + InitControl(THIS->ob.widget, (CWIDGET*)THIS); + + if (parent) + { + GB.Ref(THIS); + GB.Post((void (*)())show_later, (intptr_t)THIS); + } + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_free) + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_next) + + int *vl; + + vl = (int *)GB.GetEnum(); + + if (gMainWindow::count() <= *vl) + GB.StopEnum(); + else + { + GB.ReturnObject(gMainWindow::get(*vl)->hFree); + (*vl)++; + } + +END_METHOD + + +BEGIN_PROPERTY(CWINDOW_count) + + GB.ReturnInteger(gMainWindow::count()); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_get_from_id, GB_INTEGER index) + + gMainWindow *ob = gMainWindow::get(VARG(index)); + + GB.ReturnObject(ob ? ob->hFree : NULL); + +END_METHOD + + +BEGIN_METHOD(CWINDOW_close, GB_INTEGER ret) + + GB.ReturnBoolean(close_window(THIS, VARGOPT(ret, 0))); + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_raise) + + WINDOW->raise(); + +END_METHOD + +static bool check_closed(CWINDOW *_object, bool modal) +{ + if (WINDOW->isOpened()) + { + if (modal || WINDOW->isModal()) + { + GB.Error("Window is already opened"); + return TRUE; + } + } + + return FALSE; +} + +BEGIN_METHOD_VOID(CWINDOW_show_modal) + + if (check_closed(THIS, TRUE)) + return; + + THIS->ret = 0; + MODAL_windows++; + WINDOW->showModal(); + MODAL_windows--; + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_METHOD(Window_ShowPopup, GB_INTEGER x; GB_INTEGER y) + + if (check_closed(THIS, TRUE)) + return; + + THIS->ret = 0; + MODAL_windows++; + if (!MISSING(x) && !MISSING(y)) + WINDOW->showPopup(VARG(x), VARG(y)); + else + WINDOW->showPopup(); + MODAL_windows--; + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_show) + + if (check_closed(THIS, FALSE)) + return; + + WINDOW->showActivate(); + +END_METHOD + + +BEGIN_PROPERTY(CWINDOW_modal) + + GB.ReturnBoolean(WINDOW->isModal()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_top_level) + + GB.ReturnBoolean(WINDOW->isTopLevel()); + +END_PROPERTY + +// + + +BEGIN_PROPERTY(CWINDOW_persistent) + + if (READ_PROPERTY) + GB.ReturnBoolean (WINDOW->isPersistent()); + else + WINDOW->setPersistent(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_text) + + if (READ_PROPERTY) { GB.ReturnNewZeroString(WINDOW->text()); return; } + WINDOW->setText((const char*)GB.ToZeroString(PROP(GB_STRING))); + GB.Raise(THIS, EVENT_Title, 0); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->hasBorder()); + else + WINDOW->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isResizable()); + else + WINDOW->setResizable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Utility) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isUtility()); + else + WINDOW->setUtility(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_icon) + + if (READ_PROPERTY) + { + gPicture *pic = WINDOW->icon(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + WINDOW->setIcon(pic ? pic->picture : 0); + GB.Raise(THIS, EVENT_Icon, 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_top_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isTopOnly()); + else + WINDOW->setTopOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_skip_taskbar) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isSkipTaskBar()); + else + WINDOW->setSkipTaskBar(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_mask) + + if (READ_PROPERTY){ GB.ReturnBoolean(WINDOW->mask()); return; } + WINDOW->setMask(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_picture) + + if (READ_PROPERTY) + { + gPicture *pic = WINDOW->picture(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + WINDOW->setPicture(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_minimized) + + if (READ_PROPERTY) { GB.ReturnBoolean ( WINDOW->minimized() ); return; } + WINDOW->setMinimized(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_maximized) + + if (READ_PROPERTY) { GB.ReturnBoolean ( WINDOW->maximized() ); return; } + WINDOW->setMaximized(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_fullscreen) + + if (READ_PROPERTY) { GB.ReturnBoolean ( WINDOW->fullscreen() ); return; } + WINDOW->setFullscreen(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_center) + + WINDOW->center(); + +END_METHOD + +BEGIN_PROPERTY(CWINDOW_menu_count) + + GB.ReturnInteger(WINDOW->menuCount()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_stacking) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOW->getStacking()); + else + WINDOW->setStacking(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_sticky) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isSticky()); + else + WINDOW->setSticky(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_menu_next) + + gMenu *mn; + int pos; + + pos = ENUM(int); + + if (pos >= gMenu::winChildCount(WINDOW)) + { + GB.StopEnum(); + return; + } + + mn = gMenu::winChildMenu(WINDOW, pos); + + ENUM(int) = pos + 1; + + GB.ReturnObject(mn->hFree); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_menu_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= gMenu::winChildCount(WINDOW)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(gMenu::winChildMenu(WINDOW, index)->hFree); + +END_METHOD + +BEGIN_PROPERTY(CWINDOW_control_count) + + GPtrArray *list = WINDOW->getControlList(); + GB.ReturnInteger(list->len); + g_object_unref(list); + +END_PROPERTY + + +static void cb_free_enum(WINDOW_ENUM *iter) +{ + g_ptr_array_unref(iter->list); + iter->list = NULL; +} + +BEGIN_METHOD_VOID(CWINDOW_control_next) + + WINDOW_ENUM *iter; + gControl *control; + + iter = (WINDOW_ENUM *)GB.GetEnum(); + + if (!iter->list) + { + iter->index = 0; + iter->list = WINDOW->getControlList(); + GB.OnFreeEnum((GB_CALLBACK)cb_free_enum); + } + + if (iter->index >= iter->list->len) + { + GB.StopEnum(); + return; + } + + control = (gControl *)g_ptr_array_index(iter->list, iter->index); + iter->index = iter->index + 1; + GB.ReturnObject(GetObject(control)); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_get, GB_STRING name) + + gControl *control = WINDOW->getControl(GB.ToZeroString(ARG(name))); + + if (!control) + GB.ReturnNull(); + else + GB.ReturnObject(GetObject(control)); + +END_METHOD + +BEGIN_PROPERTY(CWINDOW_closed) + + GB.ReturnBoolean(!WINDOW->isOpened()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_menu_visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isMenuBarVisible()); + else + WINDOW->setMenuBarVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_menu_show) + + WINDOW->setMenuBarVisible(true); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_menu_hide) + + WINDOW->setMenuBarVisible(false); + +END_METHOD + +BEGIN_PROPERTY(Window_Opacity) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOW->opacity() * 100); + else + { + double opacity = VPROP(GB_INTEGER) / 100.0; + + if (opacity < 0.0) + opacity = 0.0; + else if (opacity > 1.0) + opacity = 1.0; + + WINDOW->setOpacity(opacity); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Screen) + + GB.ReturnInteger(WINDOW->screen()); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Transparent) + + bool trans = WINDOW->isTransparent(); + + if (READ_PROPERTY) + GB.ReturnBoolean(trans); + else + { + bool new_trans = VPROP(GB_BOOLEAN); + if (trans == new_trans) + return; + + if (!new_trans) + { + GB.Error("Transparent property cannot be reset"); + return; + } + + WINDOW->setTransparent(true); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_TakeFocus) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WINDOW->isNoTakeFocus()); + else + WINDOW->setNoTakeFocus(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(Window_Activate) + + WINDOW->activate(); + +END_METHOD + +BEGIN_PROPERTY(Window_MinWidth) + + int w, h; + WINDOW->getCustomMinimumSize(&w, &h); + + if (READ_PROPERTY) + GB.ReturnInteger(w); + else + WINDOW->setCustomMinimumSize(VPROP(GB_INTEGER), h); + +END_PROPERTY + +BEGIN_PROPERTY(Window_MinHeight) + + int w, h; + WINDOW->getCustomMinimumSize(&w, &h); + + if (READ_PROPERTY) + GB.ReturnInteger(h); + else + WINDOW->setCustomMinimumSize(w, VPROP(GB_INTEGER)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Form_new) + + if (!GB.Parent(_object)) + GB.Attach(_object, _object, "Form"); + + WINDOW->setName(GB.GetClassName((void *)THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Form_Main) + + CWINDOW *form; + + form = (CWINDOW *)GB.AutoCreate(GB.GetClass(NULL), 0); + if (!((gMainWindow *)form->ob.widget)->isHidden()) + CWINDOW_show(form, NULL); + +END_METHOD + + +BEGIN_METHOD(Form_Load, GB_OBJECT parent) + + gMainWindow *window = (gMainWindow *)((CWIDGET *)GB.AutoCreate(GB.GetClass(NULL), 0))->widget; + CCONTAINER *parent = (CCONTAINER *)VARGOPT(parent, 0); + + window->reparent(parent ? CONTAINER(parent) : NULL, window->x(), window->y()); + +END_METHOD + +/*************************************************************************** + + Declarations + +***************************************************************************/ + +GB_DESC CWindowMenusDesc[] = +{ + GB_DECLARE(".Window.Menus", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", CWINDOW_menu_next, 0), + GB_METHOD("_get", "Menu", CWINDOW_menu_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", CWINDOW_menu_count), + GB_METHOD("Show", NULL, CWINDOW_menu_show, NULL), + GB_METHOD("Hide", NULL, CWINDOW_menu_hide, NULL), + GB_PROPERTY("Visible", "b", CWINDOW_menu_visible), + + GB_END_DECLARE +}; + +GB_DESC CWindowControlsDesc[] = +{ + GB_DECLARE(".Window.Controls", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Control", CWINDOW_control_next, 0), + GB_METHOD("_get", "Control", CWINDOW_get, "(Name)s"), + GB_PROPERTY_READ("Count", "i", CWINDOW_control_count), + + GB_END_DECLARE +}; + +GB_DESC CWindowDesc[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), GB_INHERITS("Container"), + + GB_CONSTANT("Normal", "i", 0), + GB_CONSTANT("Above", "i", 1), + GB_CONSTANT("Below", "i", 2), + + GB_METHOD("_new", NULL, CWINDOW_new, "[(Parent)Control;]"), + GB_METHOD("_free", NULL, CWINDOW_free, NULL), + GB_METHOD("_get", "Control", CWINDOW_get, "(Name)s"), + + GB_METHOD("Close", "b", CWINDOW_close, "[(Return)i]"), + GB_METHOD("Raise", NULL, CWINDOW_raise, NULL), + GB_METHOD("Show", NULL, CWINDOW_show, NULL), + //GB_METHOD("Hide", NULL, CWINDOW_hide, NULL), + //GB_PROPERTY("Visible", "b", CWINDOW_visible), + GB_METHOD("ShowModal", "i", CWINDOW_show_modal, NULL), + GB_METHOD("ShowDialog", "i", CWINDOW_show_modal, NULL), + GB_METHOD("ShowPopup", "i", Window_ShowPopup, "[(X)i(Y)i]"), + GB_METHOD("Center", NULL, CWINDOW_center, NULL), + GB_METHOD("Activate", NULL, Window_Activate, NULL), + + GB_PROPERTY_READ("Modal", "b", CWINDOW_modal), + GB_PROPERTY_READ("TopLevel", "b", CWINDOW_top_level), + GB_PROPERTY_READ("Closed", "b", CWINDOW_closed), + + GB_PROPERTY("Stacking","i",CWINDOW_stacking), + GB_PROPERTY("Sticky","b",CWINDOW_sticky), + GB_PROPERTY("Persistent", "b", CWINDOW_persistent), + GB_PROPERTY("Text", "s", CWINDOW_text), + GB_PROPERTY("Title", "s", CWINDOW_text), + GB_PROPERTY("Caption", "s", CWINDOW_text), + GB_PROPERTY("Icon", "Picture", CWINDOW_icon), + + GB_PROPERTY("Minimized", "b", CWINDOW_minimized), + GB_PROPERTY("Maximized", "b", CWINDOW_maximized), + GB_PROPERTY("FullScreen", "b", CWINDOW_fullscreen), + + GB_PROPERTY("TopOnly", "b", CWINDOW_top_only), + GB_PROPERTY("SkipTaskbar", "b", CWINDOW_skip_taskbar), + GB_PROPERTY("Opacity", "i", Window_Opacity), + GB_PROPERTY("Transparent", "b", Window_Transparent), + GB_PROPERTY("TakeFocus", "b", Window_TakeFocus), + + GB_PROPERTY("MinWidth", "i", Window_MinWidth), + GB_PROPERTY("MinHeight", "i", Window_MinHeight), + GB_PROPERTY("MinW", "i", Window_MinWidth), + GB_PROPERTY("MinH", "i", Window_MinHeight), + + ARRANGEMENT_PROPERTIES, + + GB_PROPERTY("Utility", "b", Window_Utility), + GB_PROPERTY("Border", "b", CWINDOW_border), + GB_PROPERTY("Resizable", "b", CWINDOW_resizable), + + GB_PROPERTY("Mask","b",CWINDOW_mask), + GB_PROPERTY("Picture", "Picture", CWINDOW_picture), + + GB_PROPERTY_READ("Screen", "i", Window_Screen), + + GB_PROPERTY_SELF("Menus", ".Window.Menus"), + GB_PROPERTY_SELF("Controls", ".Window.Controls"), + + WINDOW_DESCRIPTION, + + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + GB_EVENT("Title", NULL, NULL, &EVENT_Title), + GB_EVENT("Icon", NULL, NULL, &EVENT_Icon), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + GB_EVENT("State", NULL, NULL, &EVENT_State), + + GB_END_DECLARE +}; + + +GB_DESC CWindowsDesc[] = +{ + GB_DECLARE("Windows", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_next", "Window", CWINDOW_next, 0), + GB_STATIC_METHOD("_get", "Window", CWINDOW_get_from_id, "(Id)i"), + GB_STATIC_PROPERTY_READ("Count", "i", CWINDOW_count), + + GB_END_DECLARE +}; + + +GB_DESC CFormDesc[] = +{ + GB_DECLARE("Form", sizeof(CFORM)), GB_INHERITS("Window"), + GB_AUTO_CREATABLE(), + + GB_STATIC_METHOD("Main", 0, Form_Main, 0), + GB_STATIC_METHOD("Load", 0, Form_Load, "[(Parent)Container;]"), + GB_METHOD("_new", 0, Form_new, 0), + + FORM_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/CWindow.h b/gb.gtk/src/CWindow.h new file mode 100644 index 00000000..f3036a12 --- /dev/null +++ b/gb.gtk/src/CWindow.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + CWindow.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWINDOW_H +#define __CWINDOW_H + +#include "main.h" +#include "gmainwindow.h" +#include "CWidget.h" +#include "CPicture.h" + +typedef + struct + { + CWIDGET ob; + int ret; + } + CWINDOW; + +typedef + CWINDOW CFORM; + +#ifndef __CWINDOW_CPP + +extern GB_DESC CWindowMenusDesc[]; +extern GB_DESC CWindowControlsDesc[]; +extern GB_DESC CWindowDesc[]; +//extern GB_DESC CWindowTypeDesc[]; +extern GB_DESC CWindowsDesc[]; +extern GB_DESC CFormDesc[]; + +extern CWINDOW *CWINDOW_Active; +extern CWINDOW *CWINDOW_Main; + +#else + +#define THIS ((CWINDOW *)_object) +#define WINDOW ((gMainWindow*)THIS->ob.widget) + +#endif + +void CWINDOW_check_main_window(CWINDOW *win); +bool CWINDOW_must_quit(); +void CWINDOW_close_all(); +void CWINDOW_delete_all(bool main); + +#endif + diff --git a/gb.gtk/src/Makefile.am b/gb.gtk/src/Makefile.am new file mode 100644 index 00000000..fce8e2ed --- /dev/null +++ b/gb.gtk/src/Makefile.am @@ -0,0 +1,92 @@ +COMPONENT = gb.gtk +include $(top_srcdir)/component.am + +SUBDIRS = . @GTKOPENGL_DIR@ + +gblib_LTLIBRARIES = gb.gtk.la + +gb_gtk_la_LIBADD = @THREAD_LIB@ @GTK_LIB@ +gb_gtk_la_LDFLAGS = -module @LD_FLAGS@ @GTK_LDFLAGS@ +gb_gtk_la_CPPFLAGS = @THREAD_INC@ @GTK_INC@ -I$(top_srcdir)/share + +gb_gtk_la_SOURCES = \ + gtag.h ggambastag.h gshare.h \ + gb.gtk.h widgets.h font-parser.h font-parser.cpp \ + gtools.cpp gcolor.h \ + gfont.cpp gpicture.cpp \ + gdesktop.cpp gdialog.cpp \ + gcontrol.cpp gcontainer.cpp \ + gbutton.cpp gslider.cpp gsignals.cpp \ + gtextbox.cpp gtextarea.cpp \ + gpanel.cpp gtabstrip.cpp \ + gmenu.cpp gtrayicon.cpp gmainwindow.cpp \ + gtree.h gtree.cpp \ + watcher.h watcher.cpp \ + CConst.h CConst.cpp CColor.h CColor.cpp \ + CFont.h CFont.cpp \ + CKey.h CKey.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CDialog.h CDialog.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CImage.h CImage.cpp \ + CPicture.h CPicture.cpp \ + canimation.h canimation.cpp \ + CClipboard.h CClipboard.cpp \ + CMouse.h CMouse.cpp \ + CWatcher.h CWatcher.cpp \ + CWidget.h CWidget.cpp CContainer.h CContainer.cpp \ + CDrawingArea.h CDrawingArea.cpp \ + CSlider.h CSlider.cpp \ + CButton.h CButton.cpp \ + CTextBox.h CTextBox.cpp \ + CTextArea.h CTextArea.cpp \ + CTabStrip.h CTabStrip.cpp \ + CPanel.h CPanel.cpp \ + CMenu.h CMenu.cpp CTrayIcon.h CTrayIcon.cpp CWindow.h CWindow.cpp \ + cprinter.h cprinter.cpp \ + csvgimage.h csvgimage.cpp \ + main.h main.cpp \ + gkey.h gkey.cpp \ + gcursor.h gcursor.cpp \ + gmouse.h gmouse.cpp \ + gdesktop.h \ + gpicture.h \ + gfont.h \ + gdialog.h \ + gcontrol.h \ + gseparator.h \ + gtrayicon.h \ + gplugin.h \ + gbutton.h \ + gtextbox.h \ + gtextarea.h \ + gslider.h \ + gscrollbar.h \ + gcontainer.h \ + gdrawingarea.h gdrawingarea.cpp \ + gpanel.h gtabstrip.h \ + gmenu.h \ + gmainwindow.h \ + gapplication.h gapplication.cpp \ + gclipboard.h \ + gdrag.h gdrag.cpp \ + gtools.h kentities.h \ + gprinter.h gprinter.cpp \ + gglarea.h gglarea.cpp \ + x11.h x11.c \ + sm/bonobo-macros.h \ + sm/gnome-macros.h \ + sm/gnome-uidefs.h \ + sm/libgnomeui.h \ + sm/libgnomeuiP.h \ + sm/gnome-client.h \ + sm/gnome-client.c \ + sm/gnome-ice.h \ + sm/gnome-ice.c \ + sm/gnome-marshal.h \ + sm/gnome-marshal.c \ + sm/gnometypebuiltins.h \ + sm/gnometypebuiltins.c \ + sm/sm.h diff --git a/gb.gtk/src/canimation.cpp b/gb.gtk/src/canimation.cpp new file mode 100644 index 00000000..9b880308 --- /dev/null +++ b/gb.gtk/src/canimation.cpp @@ -0,0 +1,226 @@ +/*************************************************************************** + + canimation.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + (c) 2018 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CANIMATION_CPP + +#include "CImage.h" +#include "canimation.h" + +DECLARE_EVENT(EVENT_CHANGE); + +static void raise_change(void *_object) +{ + GB.Raise(THIS, EVENT_CHANGE, 0); +} + +static gboolean advance_movie(void *_object) +{ + if (gdk_pixbuf_animation_iter_advance(THIS->iter, NULL)) + raise_change(THIS); + return true; +} + +static void pause_movie(void *_object) +{ + if (!THIS->animation || !THIS->playing) + return; + + if (THIS->timeout) + { + g_source_remove(THIS->timeout); + THIS->timeout = 0; + } + + THIS->playing = FALSE; +} + +static void stop_movie(void *_object) +{ + pause_movie(THIS); + + if (THIS->iter) + { + g_object_unref(THIS->iter); + THIS->iter = NULL; + } +} + +static void play_movie(void *_object) +{ + GTimeVal now; + int interval; + + if (!THIS->animation || THIS->playing) + return; + + if (!THIS->iter) + { + g_get_current_time(&now); + THIS->iter = gdk_pixbuf_animation_get_iter(THIS->animation, &now); + raise_change(THIS); + } + + if (!THIS->timeout) + { + interval = gdk_pixbuf_animation_iter_get_delay_time(THIS->iter); + if (interval > 0) + { + THIS->timeout = g_timeout_add(interval, (GSourceFunc)advance_movie, THIS); + THIS->playing = TRUE; + } + } + + /*buf = gdk_pixbuf_animation_iter_get_pixbuf(iter); + gtk_image_set_from_pixbuf(GTK_IMAGE(widget),buf);*/ +} + +static void free_movie(void *_object) +{ + if (!THIS->animation) + return; + + stop_movie(THIS); + + g_object_unref(G_OBJECT(THIS->animation)); + THIS->animation = NULL; + + GB.ReleaseFile(THIS->addr, THIS->len); +} + +static void *load_movie(char *path, int len_path) +{ + void *_object; + char *addr; + int len; + GdkPixbufLoader *loader; + + if (GB.LoadFile(path, len_path, &addr, &len)) + return NULL; + + loader = gdk_pixbuf_loader_new(); + if (!gdk_pixbuf_loader_write(loader, (guchar*)addr, (gsize)len, NULL)) + { + g_object_unref(G_OBJECT(loader)); + GB.Error("Unable to load animation"); + return NULL; + } + + gdk_pixbuf_loader_close(loader, NULL); + + _object = GB.Create(GB.FindClass("Animation"), NULL, NULL); + + THIS->addr = addr; + THIS->len = len; + + THIS->animation = gdk_pixbuf_loader_get_animation(loader); + g_object_ref(G_OBJECT(THIS->animation)); + + g_object_unref(G_OBJECT(loader)); + + return THIS; +} + + +/*BEGIN_METHOD_(Animation_new, GB_STRING path) + + if (load_movie(THIS, STRING(path), LENGTH(path))) + return; + + QObject::connect(MOVIE, SIGNAL(frameChanged(int)), &CAnimationManager::manager, SLOT(change())); + +END_METHOD*/ + +BEGIN_METHOD_VOID(Animation_free) + + free_movie(THIS); + +END_METHOD + +BEGIN_METHOD(Animation_Load, GB_STRING path) + + GB.ReturnObject(load_movie(STRING(path), LENGTH(path))); + +END_METHOD + +BEGIN_PROPERTY(Animation_Playing) + + GB.ReturnBoolean(THIS->playing); + +END_PROPERTY + +BEGIN_METHOD_VOID(Animation_Play) + + play_movie(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Animation_Pause) + + pause_movie(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Animation_Stop) + + stop_movie(THIS); + +END_METHOD + +BEGIN_PROPERTY(Animation_Image) + + GdkPixbuf *buf; + + if (!THIS->animation || !THIS->iter) + { + GB.ReturnNull(); + return; + } + + buf = gdk_pixbuf_copy(gdk_pixbuf_animation_iter_get_pixbuf(THIS->iter)); + GB.ReturnObject(CIMAGE_create(new gPicture(buf))); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC AnimationDesc[] = +{ + GB_DECLARE("Animation", sizeof(CANIMATION)), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, Animation_new, "(Path)s"), + GB_METHOD("_free", NULL, Animation_free, NULL), + + GB_STATIC_METHOD("Load", "Animation", Animation_Load, "(Path)s"), + + GB_PROPERTY_READ("Playing", "b", Animation_Playing), + GB_PROPERTY_READ("Image", "Image", Animation_Image), + + GB_METHOD("Play", NULL, Animation_Play, NULL), + GB_METHOD("Stop", NULL, Animation_Stop, NULL), + GB_METHOD("Pause", NULL, Animation_Pause, NULL), + + GB_EVENT("Change", NULL, EVENT_CHANGE, NULL), + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/canimation.h b/gb.gtk/src/canimation.h new file mode 100644 index 00000000..fc40656a --- /dev/null +++ b/gb.gtk/src/canimation.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + canimation.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CANIMATION_H +#define __CANIMATION_H + +#include "main.h" + +#ifndef __CANIMATION_CPP +extern GB_DESC AnimationDesc[]; +#else + +#define THIS ((CANIMATION *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + GdkPixbufAnimation *animation; + GdkPixbufAnimationIter *iter; + guint timeout; + char *addr; + int len; + unsigned playing : 1; + } + CANIMATION; + +#endif diff --git a/gb.gtk/src/cpaint_impl.cpp b/gb.gtk/src/cpaint_impl.cpp new file mode 100644 index 00000000..d704b657 --- /dev/null +++ b/gb.gtk/src/cpaint_impl.cpp @@ -0,0 +1,1753 @@ +/*************************************************************************** + + cpaint_impl.cpp + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPAINT_IMPL_CPP + +#include <cairo.h> +#ifndef GTK3 +#include <cairo-xlib.h> +#endif + +#include "gambas.h" +#include "gb_common.h" +#include "widgets.h" +#include "gdesktop.h" + +#include "CWindow.h" +#include "CDrawingArea.h" +#include "CContainer.h" +#include "CPicture.h" +#include "CImage.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "CFont.h" +#include "CDraw.h" +#include "cpaint_impl.h" + +/**** Cairo image management *********************************************/ + +static void free_image(GB_IMG *img, void *image) +{ + cairo_surface_destroy((cairo_surface_t *)image); +} + +static void *temp_image(GB_IMG *img) +{ + cairo_surface_t *image; + + if (!img->data) + image = NULL; // TODO: use a static small image surface + else + image = cairo_image_surface_create_for_data(img->data, CAIRO_FORMAT_ARGB32, img->width, img->height, + cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, img->width)); + return image; +} + +static GB_IMG_OWNER _image_owner = { + "gb.gtk.cairo", + GB_IMAGE_BGRP, + free_image, + free_image, + temp_image + }; + +static cairo_surface_t *check_image(void *img) +{ + // TODO: format is endian-dependent + return (cairo_surface_t *)IMAGE.Check((GB_IMG *)img, &_image_owner); +} + +static GB_COLOR get_color(GB_PAINT *d, GB_COLOR col) +{ + if (col == GB_COLOR_DEFAULT) + { + if (GB.Is(d->device, CLASS_Control)) + col = (((CWIDGET *)d->device)->widget)->realBackground(true); + else + col = 0xFFFFFF; + } + + return col; +} + + +/**** Paint implementation ***********************************************/ + +typedef + struct { + cairo_t *context; + GtkPrintContext *print_context; + gFont *font; + gFont **font_stack; + PangoLayout *layout; + float ascent; + cairo_matrix_t init; + double dx; + double dy; + double bx; + double by; + bool invert; + } + GB_PAINT_EXTRA; + +#define EXTRA(d) ((GB_PAINT_EXTRA *)d->extra) +#define CONTEXT(d) EXTRA(d)->context +//#define DX(d) EXTRA(d)->dx +//#define DY(d) EXTRA(d)->dy +#define DX(d) 0 +#define DY(d) 0 + +static bool _internal_paint = false; + +static gFont *get_default_font(GB_PAINT *d) +{ + if (GB.Is(d->device, CLASS_DrawingArea) || GB.Is(d->device, CLASS_UserControl)) + { + gControl *wid = (gControl *)((CWIDGET *)d->device)->widget; + return wid->font()->copy(); + } + else + { + return new gFont(); + } +} + +//static void _Font(GB_PAINT *d, int set, GB_FONT *font); + +static void update_layout(GB_PAINT *d) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + + if (dx->layout) + { + gt_add_layout_from_font(dx->layout, dx->font, d->resolutionY); + dx->ascent = dx->font->ascentF(); + + pango_cairo_context_set_font_options(pango_layout_get_context(dx->layout), gdk_screen_get_font_options (gdk_screen_get_default())); + + /*cairo_font_options_t *options = cairo_font_options_create(); //cairo_font_options_copy(pango_cairo_context_get_font_options(pango_layout_get_context(dx->layout))); + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_MEDIUM); + cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_ON); + cairo_font_options_set_subpixel_order(options, CAIRO_SUBPIXEL_ORDER_RGB); + pango_cairo_context_set_font_options(pango_layout_get_context(dx->layout), options); + cairo_font_options_destroy(options);*/ + + pango_layout_context_changed(dx->layout); + } +} + + +static bool init_painting(GB_PAINT *d, cairo_surface_t *target, double w, double h, int rx, int ry) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + gColor col; + int r, g, b, a; + + d->area.width = w; + d->area.height = h; + d->resolutionX = rx; //device->physicalDpiX(); + d->resolutionY = ry; //device->physicalDpiY(); + + /*if (device->paintingActive()) + { + GB.Error("Device already being painted"); + return TRUE; + }*/ + + if (target) + { + dx->context = cairo_create(target); + cairo_surface_destroy(target); + } + + if (GB.Is(d->device, CLASS_Control)) + col = (((CWIDGET *)d->device)->widget)->realForeground(true); + else + col = 0; + + GB_COLOR_SPLIT(col, r, g, b, a); + cairo_set_source_rgba(CONTEXT(d), r / 255.0, g / 255.0, b / 255.0, a / 255.0); + + cairo_set_line_width(CONTEXT(d), 1.0); + /*cairo_set_line_join(CONTEXT(d), CAIRO_LINE_JOIN_MITER); + cairo_set_miter_limit(CONTEXT(d), 10.0); + cairo_set_line_cap(CONTEXT(d), CAIRO_LINE_CAP_BUTT);*/ + + dx->font = get_default_font(d); + dx->font_stack = NULL; + + cairo_get_matrix(CONTEXT(d), &EXTRA(d)->init); + + return FALSE; +} + +#if 0 +static void _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context, cairo_t *cr) +{ + cairo_matrix_t matrix; + gdouble width, height; + GtkPageSetup *page = gtk_print_context_get_page_setup(context); + + /*width = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH); + width = width * context->surface_dpi_x / context->pixels_per_unit_x; + height = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH); + height = height * context->surface_dpi_y / context->pixels_per_unit_y;*/ + + width = gtk_print_context_get_width(context); + height = gtk_print_context_get_height(context); + + switch (gtk_page_setup_get_orientation (page)) + { + default: + case GTK_PAGE_ORIENTATION_PORTRAIT: + break; + case GTK_PAGE_ORIENTATION_LANDSCAPE: + fprintf(stderr, "rotate landscape\n"); + cairo_translate (cr, 0, height); + cairo_matrix_init (&matrix, + 0, -1, + 1, 0, + 0, 0); + cairo_transform (cr, &matrix); + break; + case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT: + cairo_translate (cr, width, height); + cairo_matrix_init (&matrix, + -1, 0, + 0, -1, + 0, 0); + cairo_transform (cr, &matrix); + break; + case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE: + cairo_translate (cr, width, 0); + cairo_matrix_init (&matrix, + 0, 1, + -1, 0, + 0, 0); + cairo_transform (cr, &matrix); + break; + } +} +#endif + +static int Begin(GB_PAINT *d) +{ + void *device = d->device; + cairo_surface_t *target = NULL; + double w, h; + int rx = 96, ry = 96; + + EXTRA(d)->print_context = NULL; + EXTRA(d)->dx = EXTRA(d)->dy = 0; + + if (GB.Is(device, CLASS_Picture)) + { + gPicture *picture = ((CPICTURE *)device)->picture; + + if (picture->isVoid()) + { + GB.Error("Bad picture"); + return TRUE; + } + + w = picture->width(); + h = picture->height(); + +#ifdef GTK3 + target = picture->getSurface(); + cairo_surface_reference(target); +#else + GdkDrawable *pixmap = (GdkDrawable *)picture->getPixmap(); + + target = + cairo_xlib_surface_create(gdk_x11_drawable_get_xdisplay(pixmap), gdk_x11_drawable_get_xid(pixmap), + gdk_x11_visual_get_xvisual(gdk_drawable_get_visual(pixmap)), w, h); +#endif + } + else if (GB.Is(device, CLASS_Image)) + { + target = check_image(device); + if (!target) + { + GB.Error("Bad image"); + return TRUE; + } + + cairo_surface_reference(target); + w = ((GB_IMG *)device)->width; + h = ((GB_IMG *)device)->height; + } + else if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CWIDGET *)device)->widget; + double dx = 0, dy = 0; + + w = wid->width(); + h = wid->height(); + +#ifdef GTK3 + if (wid->cached()) + { + EXTRA(d)->context = cairo_create(wid->buffer); + } + else + { + if (!wid->inDrawEvent()) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + + EXTRA(d)->context = ((CDRAWINGAREA *)device)->context; + cairo_reference(CONTEXT(d)); + + /*GtkAllocation a; + gtk_widget_get_allocation(wid->border, &a); + dx = a.x; + dy = a.y;*/ + + } + + rx = gDesktop::resolution(); //device->physicalDpiX(); + ry = gDesktop::resolution(); //device->physicalDpiY(); + +#else + GdkDrawable *dr; + + if (wid->cached()) + { + wid->resizeCache(); // Why is it needed? + dr = wid->buffer; + } + else + { + if (!wid->inDrawEvent()) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + + GtkAllocation *a = &wid->widget->allocation; + dx = a->x; + dy = a->y; + dr = gtk_widget_get_window(wid->widget); + } + + rx = gDesktop::resolution(); //device->physicalDpiX(); + ry = gDesktop::resolution(); //device->physicalDpiY(); + + EXTRA(d)->context = gdk_cairo_create(dr); +#endif + + EXTRA(d)->dx = dx; + EXTRA(d)->dy = dy; + + cairo_translate(CONTEXT(d), dx, dy); + } + else if (GB.Is(device, CLASS_UserControl)) + { + gContainer *wid = (gDrawingArea *)((CWIDGET *)device)->widget; + double dx = 0, dy = 0; + + w = wid->width(); + h = wid->height(); + + if (!_internal_paint) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + +#ifdef GTK3 + EXTRA(d)->context = ((CUSERCONTROL *)device)->context; + cairo_reference(CONTEXT(d)); +#else + GdkDrawable *dr; + + GtkAllocation *a = &wid->widget->allocation; + dx = a->x; + dy = a->y; + dr = gtk_widget_get_window(wid->widget); + + EXTRA(d)->context = gdk_cairo_create(dr); +#endif + + rx = gDesktop::resolution(); + ry = gDesktop::resolution(); + + EXTRA(d)->dx = dx; + EXTRA(d)->dy = dy; + + cairo_translate(CONTEXT(d), dx, dy); + } + else if (GB.Is(device, CLASS_Printer)) + { + CPRINTER *printer = (CPRINTER *)device; + GtkPrintContext *context = printer->context; + double pw, ph; + + if (!context) + { + GB.Error("Printer is not printing"); + return TRUE; + } + + EXTRA(d)->print_context = context; + EXTRA(d)->context = gtk_print_context_get_cairo_context(context); + + cairo_reference(CONTEXT(d)); + + cairo_surface_set_fallback_resolution(cairo_get_target(CONTEXT(d)), 1200, 1200); + + w = gtk_print_context_get_width(context); + h = gtk_print_context_get_height(context); + + rx = (int)gtk_print_context_get_dpi_x(context); + ry = (int)gtk_print_context_get_dpi_y(context); + + printer->printer->getPaperSize(&pw, &ph); + d->fontScale = 25.4 * d->area.width / pw / printer->printer->resolution(); + } + else if (GB.Is(device, CLASS_SvgImage)) + { + CSVGIMAGE *svgimage = ((CSVGIMAGE *)device); + target = SVGIMAGE_begin(svgimage); + if (!target) + return TRUE; + + cairo_surface_reference(target); + w = svgimage->width; + h = svgimage->height; + rx = ry = 72; + } + else + return TRUE; + + return init_painting(d, target, w, h, rx, ry); +} + +static void End(GB_PAINT *d) +{ + int i; + void *device = d->device; + GB_PAINT_EXTRA *dx = EXTRA(d); + + if (dx->layout) + g_object_unref(dx->layout); + + if (dx->font_stack) + { + for (i = 0; i < GB.Count(dx->font_stack); i++) + delete dx->font_stack[i]; + + GB.FreeArray(POINTER(&dx->font_stack)); + } + + delete dx->font; + + if (GB.Is(device, CLASS_Picture)) + { + gPicture *picture = ((CPICTURE *)device)->picture; + picture->invalidate(); + } + else if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CWIDGET *)device)->widget; + if (wid && wid->cached()) + wid->setCache(); + } + else if (GB.Is(device, CLASS_SvgImage)) + { + CSVGIMAGE *svgimage = ((CSVGIMAGE *)device); + SVGIMAGE_end(svgimage); + } + + cairo_destroy(dx->context); +} + +static void Save(GB_PAINT *d) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + gFont **pfont; + + cairo_save(dx->context); + + if (!dx->font_stack) + GB.NewArray(POINTER(&dx->font_stack), sizeof(void *), 0); + + pfont = (gFont **)GB.Add(POINTER(&dx->font_stack)); + *pfont = dx->font->copy(); +} + +static void Restore(GB_PAINT *d) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + + cairo_restore(dx->context); + + if (dx->font_stack && GB.Count(dx->font_stack) > 0) + { + delete dx->font; + dx->font = dx->font_stack[GB.Count(dx->font_stack) - 1]; + GB.Remove(POINTER(&dx->font_stack), GB.Count(dx->font_stack) - 1, 1); + update_layout(d); + } +} + +static void Antialias(GB_PAINT *d, int set, int *antialias) +{ + if (set) + cairo_set_antialias(CONTEXT(d), *antialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); + else + *antialias = (cairo_get_antialias(CONTEXT(d)) == CAIRO_ANTIALIAS_NONE) ? 0 : 1; +} + +static void apply_font(gFont *font, void *object = 0) +{ + double scale; + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + GB_PAINT_EXTRA *dx = EXTRA(d); + + font = font->copy(); + + scale = d->fontScale; + if (dx->print_context) + scale *= ((CPRINTER *)d->device)->printer->resolution() / 96.0; + + if (scale != 1) + font->setSize(font->size() * scale); + + delete dx->font; + dx->font = font; + + update_layout(d); +} + +// Font is used by X11! +static void _Font(GB_PAINT *d, int set, GB_FONT *font) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + gFont *f; + double scale; + + scale = d->fontScale; + if (dx->print_context) + scale *= ((CPRINTER *)d->device)->printer->resolution() / 96.0; + + if (set) + { + delete dx->font; + if (*font) + f = ((CFONT *)(*font))->font->copy(); + else + f = get_default_font(d); + + if (scale != 1) + f->setSize(f->size() * scale); + + dx->font = f; + + update_layout(d); + } + else + { + f = dx->font->copy(); + + if (scale != 1) + f->setSize(f->size() / scale); + + *font = CFONT_create(f, apply_font); + } +} + + +static void Background(GB_PAINT *d, int set, GB_COLOR *color) +{ + if (set) + { + int r, g, b, a; + int col = get_color(d, *color); + GB_COLOR_SPLIT(col, r, g, b, a); + cairo_set_source_rgba(CONTEXT(d), r / 255.0, g / 255.0, b / 255.0, a / 255.0); + } + else + { + double r, g, b, a; + if (cairo_pattern_get_rgba(cairo_get_source(CONTEXT(d)), &r, &g, &b, &a) != CAIRO_STATUS_SUCCESS) + *color = 0; + else + *color = GB_COLOR_MAKE((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0)); + } +} + + +static void Invert(GB_PAINT *d, int set, int *invert) +{ + #if CAIRO_MAJOR >= 2 || (CAIRO_MAJOR == 1 && CAIRO_MINOR >= 10) + if (set) + cairo_set_operator(CONTEXT(d), *invert ? CAIRO_OPERATOR_DIFFERENCE : CAIRO_OPERATOR_OVER); + else + *invert = cairo_get_operator(CONTEXT(d)) == CAIRO_OPERATOR_DIFFERENCE; + #else + if (set) + EXTRA(d)->invert = *invert; + else + *invert = EXTRA(d)->invert; + #endif +} + + +static void Clip(GB_PAINT *d, int preserve) +{ + if (preserve) + cairo_clip_preserve(CONTEXT(d)); + else + cairo_clip(CONTEXT(d)); +} + +static void ResetClip(GB_PAINT *d) +{ + cairo_reset_clip(CONTEXT(d)); +} + +static void ClipExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + double x1, y1, x2, y2; + cairo_clip_extents(CONTEXT(d), &x1, &y1, &x2, &y2); + + ext->x1 = (float)x1 - DX(d); + ext->y1 = (float)y1 - DY(d); + ext->x2 = (float)x2; + ext->y2 = (float)y2; +} + +static void Fill(GB_PAINT *d, int preserve) +{ + if (preserve) + cairo_fill_preserve(CONTEXT(d)); + else + cairo_fill(CONTEXT(d)); +} + +static void Stroke(GB_PAINT *d, int preserve) +{ + if (preserve) + cairo_stroke_preserve(CONTEXT(d)); + else + cairo_stroke(CONTEXT(d)); +} + +static void PathExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + double x1, y1, x2, y2; + cairo_path_extents(CONTEXT(d), &x1, &y1, &x2, &y2); + + ext->x1 = (float)x1 - DX(d); + ext->y1 = (float)y1 - DY(d); + ext->x2 = (float)x2; + ext->y2 = (float)y2; +} + +static int PathContains(GB_PAINT *d, float x, float y) +{ + return cairo_in_fill(CONTEXT(d), (double)x + DX(d), (double)y + DY(d)); +} + +static void PathOutline(GB_PAINT *d, GB_PAINT_OUTLINE_CB cb) +{ + cairo_path_t *path; + cairo_path_data_t *data; + int i; + + path = cairo_copy_path_flat(CONTEXT(d)); + + for (i = 0; i < path->num_data; i += path->data[i].header.length) + { + data = &path->data[i]; + switch (data->header.type) + { + case CAIRO_PATH_MOVE_TO: + (*cb)(GB_PAINT_PATH_MOVE, data[1].point.x, data[1].point.y); + break; + + case CAIRO_PATH_LINE_TO: + (*cb)(GB_PAINT_PATH_LINE, data[1].point.x, data[1].point.y); + break; + + case CAIRO_PATH_CURVE_TO: + fprintf(stderr, "gb.gtk: warning: CAIRO_PATH_CURVE_TO not supported\n"); + break; + + case CAIRO_PATH_CLOSE_PATH: + fprintf(stderr, "gb.gtk: warning: CAIRO_PATH_CLOSE_PATH not supported\n"); + break; + } + } + + cairo_path_destroy(path); +} + +static void Dash(GB_PAINT *d, int set, float **dashes, int *count) +{ + int i; + double lw; + + lw = cairo_get_line_width(CONTEXT(d)); + if (lw == 0) lw = 1; + + if (set) + { + if (!*count) + cairo_set_dash(CONTEXT(d), NULL, 0, 0.0); + else + { + double dd[*count]; + + for (i = 0; i < *count; i++) + dd[i] = (*dashes)[i] * lw; + + cairo_set_dash(CONTEXT(d), dd, *count, 0.0); + } + } + else + { + *count = cairo_get_dash_count(CONTEXT(d)); + + if (*count) + { + double dd[*count]; + cairo_get_dash(CONTEXT(d), dd, NULL); + + GB.Alloc(POINTER(dashes), sizeof(float) * *count); + for (int i = 0; i < *count; i++) + (*dashes)[i] = (float)dd[i] / lw; + } + else + { + *dashes = NULL; + } + } +} + +static void DashOffset(GB_PAINT *d, int set, float *offset) +{ + double lw; + + lw = cairo_get_line_width(CONTEXT(d)); + if (lw == 0) lw = 1; + + if (set) + { + int count = cairo_get_dash_count(CONTEXT(d)); + double dashes[count]; + cairo_get_dash(CONTEXT(d), dashes, NULL); + cairo_set_dash(CONTEXT(d), dashes, count, (double)*offset * lw); + } + else + { + double v; + cairo_get_dash(CONTEXT(d), NULL, &v); + *offset = (float)v / lw; + } +} + + +static void FillRule(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_fill_rule_t v; + + switch (*value) + { + case GB_PAINT_FILL_RULE_EVEN_ODD: v = CAIRO_FILL_RULE_EVEN_ODD; break; + case GB_PAINT_FILL_RULE_WINDING: default: v = CAIRO_FILL_RULE_WINDING; + } + + cairo_set_fill_rule(CONTEXT(d), v); + } + else + { + switch (cairo_get_fill_rule(CONTEXT(d))) + { + case CAIRO_FILL_RULE_EVEN_ODD: *value = GB_PAINT_FILL_RULE_EVEN_ODD; break; + case CAIRO_FILL_RULE_WINDING: default: *value = GB_PAINT_FILL_RULE_WINDING; + } + } +} + + +static void FillStyle(GB_PAINT *d, int set, int *style) +{ + /*if (set) + { + EXTRA(d)->fillRule = *value; + } + else + *value = EXTRA(d)->fillRule;*/ +} + + +static void LineCap(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_line_cap_t v; + + switch (*value) + { + case GB_PAINT_LINE_CAP_ROUND: v = CAIRO_LINE_CAP_ROUND; break; + case GB_PAINT_LINE_CAP_SQUARE: v = CAIRO_LINE_CAP_SQUARE; break; + case GB_PAINT_LINE_CAP_BUTT: default: v = CAIRO_LINE_CAP_BUTT; + } + + cairo_set_line_cap(CONTEXT(d), v); + } + else + { + switch (cairo_get_line_cap(CONTEXT(d))) + { + case CAIRO_LINE_CAP_ROUND: *value = GB_PAINT_LINE_CAP_ROUND; break; + case CAIRO_LINE_CAP_SQUARE: *value = GB_PAINT_LINE_CAP_SQUARE; break; + case CAIRO_LINE_CAP_BUTT: default: *value = GB_PAINT_LINE_CAP_BUTT; + } + } +} + +static void LineJoin(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_line_join_t v; + + switch (*value) + { + case GB_PAINT_LINE_JOIN_ROUND: v = CAIRO_LINE_JOIN_ROUND; break; + case GB_PAINT_LINE_JOIN_BEVEL: v = CAIRO_LINE_JOIN_BEVEL; break; + case GB_PAINT_LINE_JOIN_MITER: default: v = CAIRO_LINE_JOIN_MITER; + } + + cairo_set_line_join(CONTEXT(d), v); + } + else + { + switch (cairo_get_line_join(CONTEXT(d))) + { + case CAIRO_LINE_JOIN_ROUND: *value = GB_PAINT_LINE_JOIN_ROUND; break; + case CAIRO_LINE_JOIN_BEVEL: *value = GB_PAINT_LINE_JOIN_BEVEL; break; + case CAIRO_LINE_JOIN_MITER: default: *value = GB_PAINT_LINE_JOIN_MITER; + } + } +} + +static void LineWidth(GB_PAINT *d, int set, float *value) +{ + if (set) + { + float *dashes; + int count; + float offset; + + Dash(d, FALSE, &dashes, &count); + DashOffset(d, FALSE, &offset); + + cairo_set_line_width(CONTEXT(d), (double)*value); + + Dash(d, TRUE, &dashes, &count); + DashOffset(d, TRUE, &offset); + GB.Free(POINTER(&dashes)); + } + else + *value = (float)cairo_get_line_width(CONTEXT(d)); +} + +static void MiterLimit(GB_PAINT *d, int set, float *value) +{ + if (set) + cairo_set_miter_limit(CONTEXT(d), (double)*value); + else + *value = (float)cairo_get_miter_limit(CONTEXT(d)); +} + + +static void Operator(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_operator_t v; + + switch (*value) + { + case GB_PAINT_OPERATOR_CLEAR: v = CAIRO_OPERATOR_CLEAR; break; + case GB_PAINT_OPERATOR_SOURCE: v = CAIRO_OPERATOR_SOURCE; break; + case GB_PAINT_OPERATOR_IN: v = CAIRO_OPERATOR_IN; break; + case GB_PAINT_OPERATOR_OUT: v = CAIRO_OPERATOR_OUT; break; + case GB_PAINT_OPERATOR_ATOP: v = CAIRO_OPERATOR_ATOP; break; + case GB_PAINT_OPERATOR_DEST: v = CAIRO_OPERATOR_DEST; break; + case GB_PAINT_OPERATOR_DEST_OVER: v = CAIRO_OPERATOR_DEST_OVER; break; + case GB_PAINT_OPERATOR_DEST_IN: v = CAIRO_OPERATOR_DEST_IN; break; + case GB_PAINT_OPERATOR_DEST_OUT: v = CAIRO_OPERATOR_DEST_OUT; break; + case GB_PAINT_OPERATOR_DEST_ATOP: v = CAIRO_OPERATOR_DEST_ATOP; break; + case GB_PAINT_OPERATOR_XOR: v = CAIRO_OPERATOR_XOR; break; + case GB_PAINT_OPERATOR_ADD: v = CAIRO_OPERATOR_ADD; break; + case GB_PAINT_OPERATOR_SATURATE: v = CAIRO_OPERATOR_SATURATE; break; + case GB_PAINT_OPERATOR_OVER: default: v = CAIRO_OPERATOR_OVER; break; + } + + cairo_set_operator(CONTEXT(d), v); + } + else + { + switch (cairo_get_operator(CONTEXT(d))) + { + case CAIRO_OPERATOR_CLEAR: *value = GB_PAINT_OPERATOR_CLEAR; break; + case CAIRO_OPERATOR_SOURCE: *value = GB_PAINT_OPERATOR_SOURCE; break; + case CAIRO_OPERATOR_IN: *value = GB_PAINT_OPERATOR_IN; break; + case CAIRO_OPERATOR_OUT: *value = GB_PAINT_OPERATOR_OUT; break; + case CAIRO_OPERATOR_ATOP: *value = GB_PAINT_OPERATOR_ATOP; break; + case CAIRO_OPERATOR_DEST: *value = GB_PAINT_OPERATOR_DEST; break; + case CAIRO_OPERATOR_DEST_OVER: *value = GB_PAINT_OPERATOR_DEST_OVER; break; + case CAIRO_OPERATOR_DEST_IN: *value = GB_PAINT_OPERATOR_DEST_IN; break; + case CAIRO_OPERATOR_DEST_OUT: *value = GB_PAINT_OPERATOR_DEST_OUT; break; + case CAIRO_OPERATOR_DEST_ATOP: *value = GB_PAINT_OPERATOR_DEST_ATOP; break; + case CAIRO_OPERATOR_XOR: *value = GB_PAINT_OPERATOR_XOR; break; + case CAIRO_OPERATOR_ADD: *value = GB_PAINT_OPERATOR_ADD; break; + case CAIRO_OPERATOR_SATURATE: *value = GB_PAINT_OPERATOR_SATURATE; break; + case CAIRO_OPERATOR_OVER: default: *value = GB_PAINT_OPERATOR_OVER; + } + } +} + + +static void NewPath(GB_PAINT *d) +{ + cairo_new_path(CONTEXT(d)); +} + +static void ClosePath(GB_PAINT *d) +{ + cairo_close_path(CONTEXT(d)); +} + + +static void Arc(GB_PAINT *d, float xc, float yc, float radius, float angle, float length, bool pie) +{ + xc += DX(d); + yc += DY(d); + + cairo_new_sub_path(CONTEXT(d)); + + if (pie) + cairo_move_to(CONTEXT(d), 0, 0); + + if (length < 0.0) + cairo_arc_negative(CONTEXT(d), xc, yc, radius, angle, angle + length); + else + cairo_arc(CONTEXT(d), xc, yc, radius, angle, angle + length); + + if (pie) + cairo_close_path(CONTEXT(d)); +} + +static void Ellipse(GB_PAINT *d, float x, float y, float width, float height, float angle, float length, bool pie) +{ + x += DX(d); + y += DY(d); + + cairo_new_sub_path(CONTEXT(d)); + + cairo_save(CONTEXT(d)); + + cairo_translate(CONTEXT(d), x + width / 2, y + height / 2); + cairo_scale(CONTEXT(d), width / 2, height / 2); + + if (pie) + cairo_move_to(CONTEXT(d), 0, 0); + + if (length < 0.0) + cairo_arc_negative(CONTEXT(d), 0, 0, 1, angle, angle + length); + else + cairo_arc(CONTEXT(d), 0, 0, 1, angle, angle + length); + + if (pie) + cairo_close_path(CONTEXT(d)); + + cairo_restore(CONTEXT(d)); +} + +static void Rectangle(GB_PAINT *d, float x, float y, float width, float height) +{ + x += DX(d); + y += DY(d); + cairo_rectangle(CONTEXT(d), x, y, width, height); +} + +static void ClipRect(GB_PAINT *d, int x, int y, int w, int h) +{ + ResetClip(d); + Rectangle(d, x, y, w, h); + Clip(d, FALSE); +} + +static void GetCurrentPoint(GB_PAINT *d, float *x, float *y) +{ + double cx, cy; + + cairo_get_current_point(CONTEXT(d), &cx, &cy); + *x = (float)cx - DX(d); + *y = (float)cy - DY(d); +} + +static void MoveTo(GB_PAINT *d, float x, float y) +{ + cairo_move_to(CONTEXT(d), x + DX(d), y + DY(d)); +} + +static void LineTo(GB_PAINT *d, float x, float y) +{ + cairo_line_to(CONTEXT(d), x + DX(d), y + DY(d)); +} + +static void CurveTo(GB_PAINT *d, float x1, float y1, float x2, float y2, float x3, float y3) +{ + cairo_curve_to(CONTEXT(d), x1 + DX(d), y1 + DY(d), x2 + DX(d), y2 + DY(d), x3 + DX(d), y3 + DY(d)); +} + +static PangoLayout *create_pango_layout(GB_PAINT *d) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + + if (!dx->layout) + dx->layout = pango_cairo_create_layout(dx->context); + + return dx->layout; +} + +static void draw_text(GB_PAINT *d, bool rich, const char *text, int len, float w, float h, int align, bool draw) +{ + char *html = NULL; + PangoLayout *layout; + float tw, th, offx, offy; + + if (len == 0) + return; + + layout = create_pango_layout(d); + + if (rich) + { + pango_layout_set_text(layout, "", 0); + if (w > 0) + { + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_width(layout, (int)ceilf(w * PANGO_SCALE)); + } + html = gt_html_to_pango_string(text, len, false); + pango_layout_set_markup(layout, html, -1); + } + else + { + pango_layout_set_markup(layout, "", 0); + pango_layout_set_text(layout, text, len); + pango_layout_set_width(layout, -1); + } + + update_layout(d); + + if (align == GB_DRAW_ALIGN_DEFAULT) + align = ALIGN_TOP_NORMAL; + + if (w > 0 || h > 0) + { + gt_layout_alignment(layout, text, len, w, h, &tw, &th, align, &offx, &offy); + if (rich) + offx = 0; + } + else + { + offx = 0; + offy = -(EXTRA(d)->ascent); + } + + cairo_rel_move_to(CONTEXT(d), offx, offy); + + if (draw) + pango_cairo_show_layout(CONTEXT(d), layout); + else + pango_cairo_layout_path(CONTEXT(d), layout); + + cairo_rel_move_to(CONTEXT(d), -offx, -offy); + + if (html) g_free(html); +} + +static void Text(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, FALSE, text, len, w, h, align, draw); +} + +static void RichText(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, TRUE, text, len, w, h, align, draw); +} + + +static void get_text_extents(GB_PAINT *d, bool rich, const char *text, int len, GB_EXTENTS *ext, float width) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + char *html = NULL; + PangoLayout *layout; + PangoRectangle rect; + float x, y; + + layout = create_pango_layout(d); + + if (rich) + { + pango_layout_set_text(layout, "", 0); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + html = gt_html_to_pango_string(text, len, false); + pango_layout_set_markup(layout, html, -1); + } + else + { + pango_layout_set_markup(layout, "", 0); + pango_layout_set_text(layout, text, len); + } + + if (width > 0) + pango_layout_set_width(layout, width * PANGO_SCALE); + else + pango_layout_set_width(layout, -1); + + update_layout(d); + //gt_add_layout_from_font(layout, dx->font, d->resolutionY); + + pango_layout_get_extents(layout, &rect, NULL); + + GetCurrentPoint(d, &x, &y); + + ext->x1 = (float)rect.x / PANGO_SCALE + x; + ext->y1 = (float)rect.y / PANGO_SCALE + y - dx->ascent; + ext->x2 = ext->x1 + (float)rect.width / PANGO_SCALE; + ext->y2 = ext->y1 + (float)rect.height / PANGO_SCALE; + + if (html) g_free(html); +} + +static void TextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext) +{ + get_text_extents(d, FALSE, text, len, ext, -1); +} + +static void RichTextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext, float width) +{ + get_text_extents(d, TRUE, text, len, ext, width); +} + +static void TextSize(GB_PAINT *d, const char *text, int len, float *w, float *h) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + float scale; + + scale = (float)d->resolutionY / gDesktop::resolution(); + + dx->font->textSize(text, len, w, h); + + if (w) *w *= scale; + if (h) *h *= scale; +} + +static void RichTextSize(GB_PAINT *d, const char *text, int len, float sw, float *w, float *h) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + float scale; + + scale = (float)d->resolutionY / gDesktop::resolution(); + + if (sw > 0) + sw /= scale; + + dx->font->richTextSize(text, len, sw, w, h); + *w *= scale; + *h *= scale; +} + + +static void Matrix(GB_PAINT *d, int set, GB_TRANSFORM matrix) +{ + cairo_matrix_t *t = (cairo_matrix_t *)matrix; + + if (set) + { + if (t) + cairo_set_matrix(CONTEXT(d), t); + else + { + //if (EXTRA(d)->print_context) + cairo_set_matrix(CONTEXT(d), &EXTRA(d)->init); + /*else + { + cairo_ + cairo_identity_matrix(CONTEXT(d)); + cairo_translate(CONTEXT(d), EXTRA(d)->dx, EXTRA(d)->dy); + }*/ + } + } + else + cairo_get_matrix(CONTEXT(d), t); +} + + +static void SetBrush(GB_PAINT *d, GB_BRUSH brush) +{ + cairo_set_source(CONTEXT(d), (cairo_pattern_t *)brush); +} + +static void BrushOrigin(GB_PAINT *d, int set, float *x, float *y) +{ + if (set) + { + cairo_pattern_t *brush; + cairo_matrix_t matrix; + + brush = cairo_get_source(CONTEXT(d)); + cairo_pattern_get_matrix(brush, &matrix); + cairo_matrix_translate(&matrix, EXTRA(d)->bx, EXTRA(d)->by); + cairo_matrix_translate(&matrix, (- *x), (- *y)); + cairo_pattern_set_matrix(brush, &matrix); + + EXTRA(d)->bx = *x; + EXTRA(d)->by = *y; + } + else + { + *x = EXTRA(d)->bx; + *y = EXTRA(d)->by; + } +} + +static void BrushFree(GB_BRUSH brush) +{ + // Should I release the surface associated with an image brush? Apparently no. + cairo_pattern_destroy((cairo_pattern_t *)brush); +} + +static void BrushColor(GB_BRUSH *brush, GB_COLOR color) +{ + int r, g, b, a; + GB_COLOR_SPLIT(color, r, g, b, a); + *brush = (GB_BRUSH)cairo_pattern_create_rgba(r / 255.0, g / 255.0, b / 255.0, a / 255.0); +} + +static void BrushImage(GB_BRUSH *brush, GB_IMAGE image) +{ + gPicture *picture = CIMAGE_get((CIMAGE *)image); + cairo_surface_t *surface; + cairo_pattern_t *pattern; + + surface = gt_cairo_create_surface_from_pixbuf(picture->getPixbuf()); + + pattern = cairo_pattern_create_for_surface(surface); + cairo_surface_destroy(surface); + + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + *brush = (GB_BRUSH)pattern; +} + +static void handle_color_stop(cairo_pattern_t *pattern, int nstop, const double *positions, const GB_COLOR *colors) +{ + int i, r, g, b, a; + + for (i = 0; i < nstop; i++) + { + GB_COLOR_SPLIT(colors[i], r, g, b, a); + cairo_pattern_add_color_stop_rgba(pattern, positions[i], r / 255.0, g / 255.0, b / 255.0, a / 255.0); + } +} + +static void set_pattern_extend(cairo_pattern_t *pattern, int extend) +{ + cairo_extend_t cext; + + switch (extend) + { + case GB_PAINT_EXTEND_REPEAT: cext = CAIRO_EXTEND_REPEAT; break; + case GB_PAINT_EXTEND_REFLECT: cext = CAIRO_EXTEND_REFLECT; break; + case GB_PAINT_EXTEND_PAD: default: cext = CAIRO_EXTEND_PAD; + } + + cairo_pattern_set_extend(pattern, cext); +} + +static void BrushLinearGradient(GB_BRUSH *brush, float x0, float y0, float x1, float y1, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + cairo_pattern_t *pattern; + + pattern = cairo_pattern_create_linear(x0, y0, x1, y1); + + handle_color_stop(pattern, nstop, positions, colors); + set_pattern_extend(pattern, extend); + + *brush = (GB_BRUSH)pattern; +} + +static void BrushRadialGradient(GB_BRUSH *brush, float cx, float cy, float r, float fx, float fy, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + cairo_pattern_t *pattern; + + // I know that from librsvg sources + pattern = cairo_pattern_create_radial(fx, fy, 0.0, cx, cy, r); + + handle_color_stop(pattern, nstop, positions, colors); + set_pattern_extend(pattern, extend); + + *brush = (GB_BRUSH)pattern; +} + +// Matrix must be inverted, so that it behaves the same way as in Qt4 + +static void BrushMatrix(GB_BRUSH brush, int set, GB_TRANSFORM matrix) +{ + cairo_matrix_t *t = (cairo_matrix_t *)matrix; + cairo_pattern_t *pattern = (cairo_pattern_t *)brush; + cairo_matrix_t actual; + + if (set) + { + if (t) + { + actual = *t; + cairo_matrix_invert(&actual); + } + else + cairo_matrix_init_identity(&actual); + + cairo_pattern_set_matrix(pattern, &actual); + } + else + { + cairo_pattern_get_matrix(pattern, t); + cairo_matrix_invert(t); + } +} + +static void TransformCreate(GB_TRANSFORM *matrix) +{ + GB.Alloc(POINTER(matrix), sizeof(cairo_matrix_t)); + cairo_matrix_init_identity((cairo_matrix_t *)*matrix); +} + +static void TransformCopy(GB_TRANSFORM *matrix, GB_TRANSFORM copy) +{ + GB.Alloc(POINTER(matrix), sizeof(cairo_matrix_t)); + *(cairo_matrix_t *)*matrix = *(cairo_matrix_t *)copy; +} + +static void TransformDelete(GB_TRANSFORM *matrix) +{ + GB.Free(POINTER(matrix)); +} + +static void TransformInit(GB_TRANSFORM matrix, float xx, float yx, float xy, float yy, float x0, float y0) +{ + cairo_matrix_init((cairo_matrix_t *)matrix, xx, yx, xy, yy, x0, y0); +} + +static void TransformTranslate(GB_TRANSFORM matrix, float tx, float ty) +{ + cairo_matrix_translate((cairo_matrix_t *)matrix, tx, ty); +} + +static void TransformScale(GB_TRANSFORM matrix, float sx, float sy) +{ + cairo_matrix_scale((cairo_matrix_t *)matrix, sx, sy); +} + +static void TransformRotate(GB_TRANSFORM matrix, float angle) +{ + cairo_matrix_rotate((cairo_matrix_t *)matrix, -angle); +} + +static int TransformInvert(GB_TRANSFORM matrix) +{ + cairo_status_t status = cairo_matrix_invert((cairo_matrix_t *)matrix); + return status != CAIRO_STATUS_SUCCESS; +} + +static void TransformMultiply(GB_TRANSFORM matrix, GB_TRANSFORM matrix2) +{ + cairo_matrix_multiply((cairo_matrix_t *)matrix, (cairo_matrix_t *)matrix, (cairo_matrix_t *)matrix2); +} + +static void TransformMap(GB_TRANSFORM matrix, double *x, double *y) +{ + cairo_matrix_transform_point((cairo_matrix_t *)matrix, x, y); +} + +static void my_cairo_fill(cairo_t *cr) +{ + if (cairo_get_operator(cr) == CAIRO_OPERATOR_OVER) + cairo_fill(cr); + else + { + cairo_save(cr); + cairo_clip(cr); + cairo_paint(cr); + cairo_restore(cr); + } +} + +static void DrawImage(GB_PAINT *d, GB_IMAGE image, float x, float y, float w, float h, float opacity, GB_RECT *source) +{ + cairo_t *cr = CONTEXT(d); + cairo_surface_t *surface; + cairo_pattern_t *pattern = NULL; + cairo_pattern_t *save; + cairo_matrix_t matrix; + + save = cairo_get_source(cr); + cairo_pattern_reference(save); + cairo_save(cr); + + surface = check_image(image); //picture->getSurface(); + + pattern = cairo_pattern_create_for_surface(surface); + + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + if (source) + { + cairo_matrix_scale(&matrix, w / source->w, h / source->h); + cairo_matrix_translate(&matrix, -source->x, -source->y); + } + else if (w > 0 && h > 0) + cairo_matrix_scale(&matrix, w / cairo_image_surface_get_width(surface), h / cairo_image_surface_get_height(surface)); + + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + + cairo_rectangle(cr, x, y, w, h); + + if (opacity == 1.0) + { + my_cairo_fill(cr); + } + else + { + cairo_clip(cr); + cairo_paint_with_alpha(cr, opacity); + } + + cairo_restore(cr); + cairo_set_source(cr, save); + cairo_pattern_destroy(save); + + cairo_pattern_destroy(pattern); +} + +#ifdef GTK3 +static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source) +{ + cairo_t *cr = CONTEXT(d); + gPicture *pic = ((CPICTURE *)picture)->picture; + cairo_surface_t *surface; + cairo_pattern_t *pattern = NULL; + cairo_pattern_t *save; + cairo_matrix_t matrix; + + cairo_save(cr); + + save = cairo_get_source(cr); + cairo_pattern_reference(save); + + surface = pic->getSurface(); + + pattern = cairo_pattern_create_for_surface(surface); + + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + if (source) + { + cairo_matrix_scale(&matrix, w / source->w, h / source->h); + cairo_matrix_translate(&matrix, -source->x, -source->y); + } + else if (w > 0 && h > 0) + cairo_matrix_scale(&matrix, w / cairo_image_surface_get_width(surface), h / cairo_image_surface_get_height(surface)); + + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + + cairo_rectangle(cr, x, y, w, h); + my_cairo_fill(cr); + + cairo_set_source(cr, save); + cairo_pattern_destroy(save); + + cairo_restore(cr); + + cairo_pattern_destroy(pattern); +} +#else +static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source) +{ + cairo_pattern_t *pattern, *save; + cairo_matrix_t matrix; + gPicture *pic = ((CPICTURE *)picture)->picture; + + if (pic->type() != gPicture::PIXMAP || source) + { + gt_cairo_draw_pixbuf(CONTEXT(d), pic->getPixbuf(), x, y, w, h, 1.0, source); + return; + } + + x += DX(d); + y += DY(d); + + cairo_save(CONTEXT(d)); + save = cairo_get_source(CONTEXT(d)); + cairo_pattern_reference(save); + + gdk_cairo_set_source_pixmap(CONTEXT(d), pic->getPixmap(), 0, 0); + + pattern = cairo_get_source(CONTEXT(d)); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + /*if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);*/ + + //gdk_cairo_set_source_pixbuf(CONTEXT(d), picture->getPixbuf(), x, y); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + cairo_matrix_scale(&matrix, w / pic->width(), h / pic->height()); + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + + cairo_rectangle(CONTEXT(d), x, y, w, h); + my_cairo_fill(CONTEXT(d)); + + cairo_set_source(CONTEXT(d), save); + cairo_pattern_destroy(save); + + cairo_restore(CONTEXT(d)); +} +#endif + +static void GetPictureInfo(GB_PAINT *d, GB_PICTURE picture, GB_PICTURE_INFO *info) +{ + gPicture *pic = ((CPICTURE *)picture)->picture; + + info->width = pic->width(); + info->height = pic->height(); +} + +static void FillRect(GB_PAINT *d, float x, float y, float w, float h, GB_COLOR color) +{ + cairo_pattern_t *save; + + x += DX(d); + y += DY(d); + + save = cairo_get_source(CONTEXT(d)); + cairo_pattern_reference(save); + + Background(d, TRUE, &color); + cairo_rectangle(CONTEXT(d), x, y, w, h); + my_cairo_fill(CONTEXT(d)); + + cairo_set_source(CONTEXT(d), save); + cairo_pattern_destroy(save); +} + + +GB_PAINT_DESC PAINT_Interface = +{ + sizeof(GB_PAINT_EXTRA), + Begin, + End, + Save, + Restore, + Antialias, + _Font, + Background, + Invert, + Clip, + ResetClip, + ClipExtents, + ClipRect, + Fill, + Stroke, + PathExtents, + PathContains, + PathOutline, + Dash, + DashOffset, + FillRule, + FillStyle, + LineCap, + LineJoin, + LineWidth, + MiterLimit, + Operator, + NewPath, + ClosePath, + Arc, + Ellipse, + Rectangle, + GetCurrentPoint, + MoveTo, + LineTo, + CurveTo, + Text, + TextExtents, + TextSize, + RichText, + RichTextExtents, + RichTextSize, + Matrix, + SetBrush, + BrushOrigin, + DrawImage, + DrawPicture, + GetPictureInfo, + FillRect, + { + BrushFree, + BrushColor, + BrushImage, + BrushLinearGradient, + BrushRadialGradient, + BrushMatrix, + } +}; + +GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface = +{ + TransformCreate, + TransformCopy, + TransformDelete, + TransformInit, + TransformTranslate, + TransformScale, + TransformRotate, + TransformInvert, + TransformMultiply, + TransformMap +}; + +void PAINT_begin(void *device) +{ + _internal_paint = true; + DRAW.Paint.Begin(device); + _internal_paint = false; +} + +bool PAINT_is_internal() +{ + return _internal_paint; +} + +void PAINT_end() +{ + DRAW.Paint.End(); +} + +void PAINT_clip(int x, int y, int w, int h) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + { + cairo_rectangle(CONTEXT(d), (double)x, (double)y, (double)w, (double)h); + cairo_clip(CONTEXT(d)); + } +} + +#ifdef GTK3 +#else +void PAINT_clip_region(GdkRegion *region) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + { + gdk_cairo_region(CONTEXT(d), region); + cairo_clip(CONTEXT(d)); + } +} +#endif + +cairo_t *PAINT_get_current_context() +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + return CONTEXT(d); + + GB.Error("No current device"); + return NULL; +} + +void *PAINT_get_current_device() +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + return d->device; + + GB.Error("No current device"); + return NULL; +} + +bool PAINT_get_clip(int *x, int *y, int *w, int *h) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + GB_EXTENTS ext; + + ClipExtents(d, &ext); + + *x = ceilf(ext.x1); + *y = ceilf(ext.y1); + *w = floorf(ext.x2) - *x; + *h = floorf(ext.y2) - *y; + + return *w <= 0 || *h <= 0; +} + +void PAINT_apply_offset(int *x, int *y) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + *x += EXTRA(d)->dx; + *y += EXTRA(d)->dy; +} diff --git a/gb.gtk/src/cpaint_impl.h b/gb.gtk/src/cpaint_impl.h new file mode 100644 index 00000000..4c38e58f --- /dev/null +++ b/gb.gtk/src/cpaint_impl.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + cpaint_impl.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPAINT_IMPL_H +#define __CPAINT_IMPL_H + +#include "gambas.h" +#include "gb.paint.h" +#include <gdk/gdk.h> + +#ifndef __CPAINT_IMPL_C + +extern GB_PAINT_DESC PAINT_Interface; +extern GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface; + +#endif + +void PAINT_begin(void *device); +void PAINT_end(void); +bool PAINT_is_internal(void); +void PAINT_clip(int x, int y, int w, int h); +#ifndef GTK3 +void PAINT_clip_region(GdkRegion *region); +#endif +cairo_t *PAINT_get_current_context(); +void *PAINT_get_current_device(); +bool PAINT_get_clip(int *x, int *y, int *w, int *h); +void PAINT_apply_offset(int *x, int *y); + +#endif diff --git a/gb.gtk/src/cprinter.cpp b/gb.gtk/src/cprinter.cpp new file mode 100644 index 00000000..3675cea5 --- /dev/null +++ b/gb.gtk/src/cprinter.cpp @@ -0,0 +1,390 @@ +/*************************************************************************** + + cprinter.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPRINTER_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "cpaint_impl.h" +#include "gb.form.print.h" +#include "cprinter.h" + +DECLARE_EVENT(EVENT_Begin); +DECLARE_EVENT(EVENT_End); +DECLARE_EVENT(EVENT_Paginate); +DECLARE_EVENT(EVENT_Draw); + +void CB_printer_begin(gPrinter *printer, GtkPrintContext *context) +{ + void *_object = printer->tag; + THIS->current = 0; + THIS->context = context; + PAINT_begin(THIS); + GB.Raise(THIS, EVENT_Begin, 0); +} + +void CB_printer_end(gPrinter *printer) +{ + void *_object = printer->tag; + THIS->current = 0; + GB.Raise(THIS, EVENT_End, 0); + PAINT_end(); +} + +void CB_printer_paginate(gPrinter *printer) +{ + void *_object = printer->tag; + + if (GB.CanRaise(THIS, EVENT_Paginate)) + GB.Raise(THIS, EVENT_Paginate, 0); + else + printer->setPageCount(printer->pageCount()); +} + +void CB_printer_draw(gPrinter *printer, GtkPrintContext *context, int page) +{ + void *_object = printer->tag; + THIS->current = page + 1; + THIS->context = context; + //PAINT_begin(THIS); + GB.Raise(THIS, EVENT_Draw, 0); + //PAINT_end(); +} + +BEGIN_METHOD_VOID(Printer_new) + + THIS->printer = new gPrinter(); + PRINTER->tag = THIS; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_free) + + delete THIS->printer; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Configure) + + GB.ReturnBoolean(PRINTER->configure()); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Print) + + GB.ReturnBoolean(PRINTER->print()); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Cancel) + + PRINTER->cancel(); + +END_METHOD + +BEGIN_PROPERTY(Printer_Count) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->pageCount()); + else + PRINTER->setPageCount(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Page) + + GB.ReturnInteger(THIS->current); + +END_PROPERTY + +BEGIN_METHOD(Printer_SetPage, GB_INTEGER page) + + THIS->current = VARG(page); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_IsCountSet) + + GB.ReturnBoolean(PRINTER->isPageCountSet()); + PRINTER->clearPageCountSet(); + +END_METHOD + +BEGIN_PROPERTY(Printer_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(PRINTER->name()); + else + PRINTER->setName(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Orientation) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->orientation()); + else + PRINTER->setOrientation(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Paper) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->paperModel()); + else + PRINTER->setPaperModel(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperWidth) + + double w, h; + + PRINTER->getPaperSize(&w, &h); + + if (READ_PROPERTY) + GB.ReturnFloat(w); + else + PRINTER->setPaperSize(VPROP(GB_FLOAT), h); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperHeight) + + double w, h; + + PRINTER->getPaperSize(&w, &h); + + if (READ_PROPERTY) + GB.ReturnFloat(h); + else + PRINTER->setPaperSize(w, VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_CollateCopies) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->collateCopies()); + else + PRINTER->setCollateCopies(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_ReverseOrder) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->reverserOrder()); + else + PRINTER->setReverseOrder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Duplex) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->duplex()); + else + PRINTER->setDuplex(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_GrayScale) + + if (READ_PROPERTY) + GB.ReturnBoolean(!PRINTER->useColor()); + else + PRINTER->setUseColor(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_NumCopies) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->numCopies()); + else + PRINTER->setNumCopies(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Resolution) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->resolution()); + else + PRINTER->setResolution(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FirstPage) + + int from, to; + + PRINTER->getPrintPages(&from, &to); + + if (READ_PROPERTY) + GB.ReturnInteger(from + 1); + else + PRINTER->setPrintPages(VPROP(GB_INTEGER) - 1, to); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_LastPage) + + int from, to; + + PRINTER->getPrintPages(&from, &to); + + if (READ_PROPERTY) + GB.ReturnInteger(to + 1); + else + PRINTER->setPrintPages(from, VPROP(GB_INTEGER) - 1); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FullPage) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->useFullPage()); + else + PRINTER->setUseFullPage(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_OutputFile) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(PRINTER->outputFileName()); + else + PRINTER->setOutputFileName(GB.FileName(PSTRING(), PLENGTH())); + +END_PROPERTY + +static bool find_default_printer(const char *name, bool def) +{ + if (def) + { + GB.ReturnNewZeroString(name); + return true; + } + else + return false; +} + +BEGIN_PROPERTY(Printer_Default) + + GB.ReturnNull(); + gPrinter::enumeratePrinters(find_default_printer); + +END_PROPERTY + +static GB_ARRAY _list = NULL; + +static bool add_printer(const char *name, bool def) +{ + *((char **)GB.Array.Add(_list)) = GB.NewZeroString(name); + return FALSE; +} + +BEGIN_PROPERTY(Printer_List) + + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + _list = array; + gPrinter::enumeratePrinters(add_printer); + _list = NULL; + GB.ReturnObject(array); + +END_PROPERTY + + + +GB_DESC PrinterDesc[] = +{ + GB_DECLARE("Printer", sizeof(CPRINTER)), + + GB_STATIC_PROPERTY_READ("Default", "s", Printer_Default), + GB_STATIC_PROPERTY_READ("List", "String[]", Printer_List), + + GB_CONSTANT("Portrait", "i", GB_PRINT_PORTRAIT), + GB_CONSTANT("Landscape", "i", GB_PRINT_LANDSCAPE), + //GB_CONSTANT("ReversePortrait", "i", GB_PRINT_REVERSE_PORTRAIT), + //GB_CONSTANT("ReverseLandscape", "i", GB_PRINT_REVERSE_LANDSCAPE), + + GB_CONSTANT("Custom", "i", GB_PRINT_CUSTOM), + GB_CONSTANT("A3", "i", GB_PRINT_A3), + GB_CONSTANT("A4", "i", GB_PRINT_A4), + GB_CONSTANT("A5", "i", GB_PRINT_A5), + GB_CONSTANT("B5", "i", GB_PRINT_B5), + GB_CONSTANT("Letter", "i", GB_PRINT_LETTER), + GB_CONSTANT("Executive", "i", GB_PRINT_EXECUTIVE), + GB_CONSTANT("Legal", "i", GB_PRINT_LEGAL), + + GB_CONSTANT("Simplex", "i", GB_PRINT_SIMPLEX), + GB_CONSTANT("Horizontal", "i", GB_PRINT_DUPLEX_HORIZONTAL), + GB_CONSTANT("Vertical", "i", GB_PRINT_DUPLEX_VERTICAL), + + GB_METHOD("_new", NULL, Printer_new, NULL), + GB_METHOD("_free", NULL, Printer_free, NULL), + + GB_METHOD("Configure", "b", Printer_Configure, NULL), + GB_METHOD("Print", "b", Printer_Print, NULL), + GB_METHOD("_SetPage", NULL, Printer_SetPage, "(Page)i"), + GB_METHOD("_IsCountSet", "b", Printer_IsCountSet, NULL), + + GB_METHOD("Cancel", NULL, Printer_Cancel, NULL), + + GB_PROPERTY("Count", "i", Printer_Count), + GB_PROPERTY_READ("Page", "i", Printer_Page), + + GB_PROPERTY("Name", "s", Printer_Name), + GB_PROPERTY("Orientation", "i", Printer_Orientation), + GB_PROPERTY("Paper", "i", Printer_Paper), + GB_PROPERTY("PaperWidth", "f", Printer_PaperWidth), + GB_PROPERTY("PaperHeight", "f", Printer_PaperHeight), + GB_PROPERTY("CollateCopies", "b", Printer_CollateCopies), + GB_PROPERTY("ReverseOrder", "b", Printer_ReverseOrder), + GB_PROPERTY("Duplex", "i", Printer_Duplex), + GB_PROPERTY("GrayScale", "b", Printer_GrayScale), + GB_PROPERTY("NumCopies", "i", Printer_NumCopies), + GB_PROPERTY("Resolution", "i", Printer_Resolution), + GB_PROPERTY("FirstPage", "i", Printer_FirstPage), + GB_PROPERTY("LastPage", "i", Printer_LastPage), + GB_PROPERTY("FullPage", "b", Printer_FullPage), + GB_PROPERTY("OutputFile", "s", Printer_OutputFile), + + GB_EVENT("Begin", NULL, NULL, &EVENT_Begin), + GB_EVENT("End", NULL, NULL, &EVENT_End), + GB_EVENT("Paginate", NULL, NULL, &EVENT_Paginate), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + + GB_INTERFACE("Paint", &PAINT_Interface), + + PRINTER_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/cprinter.h b/gb.gtk/src/cprinter.h new file mode 100644 index 00000000..57a445f3 --- /dev/null +++ b/gb.gtk/src/cprinter.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + cprinter.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPRINTER_H +#define __CPRINTER_H + +#include "gambas.h" +#include "widgets.h" +#include "gprinter.h" + +typedef + struct + { + GB_BASE ob; + gPrinter *printer; + int current; + GtkPrintContext *context; + } + CPRINTER; + +#ifndef __CPRINTER_CPP + +extern GB_DESC PrinterDesc[]; + +#else + +#define THIS ((CPRINTER *)_object) +#define PRINTER THIS->printer + +#endif + +#endif diff --git a/gb.gtk/src/csvgimage.cpp b/gb.gtk/src/csvgimage.cpp new file mode 100644 index 00000000..0890f037 --- /dev/null +++ b/gb.gtk/src/csvgimage.cpp @@ -0,0 +1,387 @@ +/*************************************************************************** + + csvgimage.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSVGIMAGE_CPP + +#include <cairo.h> +#include <cairo-svg.h> + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "cpaint_impl.h" +#include "csvgimage.h" + +#define MM_TO_PT(_mm) ((_mm) * 72 / 25.4) +#define PT_TO_MM(_pt) ((_pt) / 72 * 25.4) + +static void release(CSVGIMAGE *_object) +{ + if (HANDLE) + { + g_object_unref(G_OBJECT(HANDLE)); + HANDLE = NULL; + } + + if (SURFACE) + { + cairo_surface_destroy(SURFACE); + THIS->surface = NULL; + } + + THIS->width = THIS->height = 0; +} + + +static const char *load_file(CSVGIMAGE *_object, const char *path, int len_path) +{ + RsvgHandle *handle = NULL; + char *addr; + int len; + const char *err = NULL; + + if (GB.LoadFile(path, len_path, &addr, &len)) + return "Unable to load SVG file"; + + handle = rsvg_handle_new_from_data((const guint8 *)addr, len / sizeof(guint8), NULL); + if (!handle) + { + err = "Unable to load SVG file: invalid format"; + g_object_unref(G_OBJECT(handle)); + goto __RETURN; + } + + rsvg_handle_set_dpi(handle, 72); + + if (HANDLE) + g_object_unref(G_OBJECT(HANDLE)); + + THIS->handle = handle; + +#if LIBRSVG_CHECK_VERSION(2,46,0) + gboolean has_width, has_height, has_viewbox; + RsvgLength width; + RsvgLength height; + RsvgRectangle viewbox; + rsvg_handle_get_intrinsic_dimensions(handle, &has_width, &width, &has_height, &height, &has_viewbox, &viewbox); + if (has_viewbox) + { + THIS->width = viewbox.width; + THIS->height = viewbox.height; + } + else if (has_width && has_height && width.unit == height.unit) + { + THIS->width = width.length; + THIS->height = height.length; + } +#else + RsvgDimensionData dim; + rsvg_handle_get_dimensions(handle, &dim); + THIS->width = dim.width; + THIS->height = dim.height; +#endif + +__RETURN: + + GB.ReleaseFile(addr, len); + return err; +} + + +static void paint_svg(CSVGIMAGE *_object, cairo_t *context, double x, double y, double w, double h) +{ + cairo_matrix_t matrix; + double sx, sy, ss; + + if (!context) + return; + + if (THIS->width <= 0 || THIS->height <= 0) + return; + + if (!HANDLE && !SURFACE) + return; + + cairo_get_matrix(context, &matrix); + + if (HANDLE) + { + if (w <= 0) + { + w = THIS->width; + sx = 1; + } + else + sx = w / THIS->width; + + if (h <= 0) + { + h = THIS->height; + sy = 1; + } + else + sy = h / THIS->height; + + #if LIBRSVG_CHECK_VERSION(2,46,0) + + RsvgRectangle view; + view.x = x; + view.y = y; + view.width = w; + view.height = h; + + if ((h/w) != (THIS->height/THIS->width)) + { + //fprintf(stderr, "%g / %g\n", h/w, THIS->height/THIS->width); + + if ((h/w) < (THIS->height/THIS->width)) + { + ss = w/h/(THIS->width/THIS->height); + cairo_translate(context, -((x + w / 2) * (ss - 1)), 0); + cairo_scale(context, ss, 1); + } + else + { + ss = h/w/(THIS->height/THIS->width); + cairo_translate(context, 0, -((y + h / 2) * (ss - 1))); + cairo_scale(context, 1, ss); + } + } + + rsvg_handle_render_document(HANDLE, context, &view, NULL); + + cairo_set_matrix(context, &matrix); + + #endif + + cairo_scale(context, sx, sy); + cairo_translate(context, x, y); + + #if !LIBRSVG_CHECK_VERSION(2,46,0) + + rsvg_handle_render_cairo(HANDLE, context); + + #endif + } + + if (SURFACE) + { + //cairo_surface_finish(SURFACE); + + cairo_save(context); + cairo_set_source_surface(context, SURFACE, 0, 0); + cairo_paint(context); + cairo_restore(context); + } + + cairo_set_matrix(context, &matrix); +} + + +cairo_surface_t *SVGIMAGE_begin(CSVGIMAGE *_object) +{ + if (!SURFACE) + { + if (THIS->width <= 0 || THIS->height <= 0) + { + GB.Error("SvgImage size is not defined"); + return NULL; + } + + /*THIS->file = GB.NewZeroString(GB.TempFile(NULL)); + SURFACE = cairo_svg_surface_create(THIS->file, THIS->width, THIS->height);*/ + + SURFACE = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + +#if 0 + if (HANDLE) + { + cairo_t *context = cairo_create(SURFACE); + +#if LIBRSVG_CHECK_VERSION(2,46,0) + RsvgRectangle view = { 0, 0, THIS->width, THIS->height }; + rsvg_handle_render_document(HANDLE, context, &view, NULL); +#else + rsvg_handle_render_cairo(HANDLE, context); +#endif + cairo_destroy(context); + } +#endif + } + + return SURFACE; +} + + +void SVGIMAGE_end(CSVGIMAGE *_object) +{ + //const char *err; + + //cairo_surface_finish(SURFACE); + + /*if ((err = load_file(THIS, THIS->file, GB.StringLength(THIS->file)))) + { + GB.Error(err); + return; + }*/ +} + +//------------------------------------------------------------------------- + + +BEGIN_METHOD(SvgImage_new, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARGOPT(width, 0); + THIS->height = VARGOPT(height, 0); + +END_METHOD + + +BEGIN_METHOD_VOID(SvgImage_free) + + release(THIS); + +END_METHOD + + +BEGIN_PROPERTY(SvgImage_Width) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->width); + else + THIS->width = VPROP(GB_FLOAT); + +END_PROPERTY + + +BEGIN_PROPERTY(SvgImage_Height) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->height); + else + THIS->height = VPROP(GB_FLOAT); + +END_PROPERTY + + +BEGIN_METHOD(SvgImage_Resize, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARG(width); + THIS->height = VARG(height); + +END_METHOD + + +BEGIN_METHOD(SvgImage_Load, GB_STRING path) + + CSVGIMAGE *svgimage; + const char *err; + + svgimage = (CSVGIMAGE *)GB.New(CLASS_SvgImage, NULL, NULL); + + if ((err = load_file(svgimage, STRING(path), LENGTH(path)))) + { + GB.Unref(POINTER(&svgimage)); + GB.Error(err); + return; + } + + GB.ReturnObject(svgimage); + +END_METHOD + + +BEGIN_METHOD(SvgImage_Paint, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + cairo_t *context = PAINT_get_current_context(); + double tx, ty; + + if (!context) + return; + + if (THIS->width <= 0 || THIS->height <= 0) + return; + + cairo_get_current_point(context, &tx, &ty); + paint_svg(THIS, context, VARGOPT(x, tx), VARGOPT(y, ty), VARGOPT(w, -1), VARGOPT(h, -1)); + +END_METHOD + + +BEGIN_METHOD(SvgImage_Save, GB_STRING file) + + cairo_surface_t *svg; + cairo_t *context; + + if (THIS->width <= 0 || THIS->height <= 0) + { + GB.Error("SvgImage size is not defined"); + return; + } + + svg = cairo_svg_surface_create(GB.FileName(STRING(file), LENGTH(file)), THIS->width, THIS->height); + + context = cairo_create(svg); + + paint_svg(THIS, context, 0, 0, -1, -1); + + cairo_destroy(context); + + cairo_surface_destroy(svg); + +END_METHOD + + +BEGIN_METHOD_VOID(SvgImage_Clear) + + release(THIS); + +END_METHOD + + +//------------------------------------------------------------------------- + +GB_DESC SvgImageDesc[] = +{ + GB_DECLARE("SvgImage", sizeof(CSVGIMAGE)), + + GB_METHOD("_new", NULL, SvgImage_new, "[(Width)f(Height)f]"), + GB_METHOD("_free", NULL, SvgImage_free, NULL), + + GB_PROPERTY("Width", "f", SvgImage_Width), + GB_PROPERTY("W", "f", SvgImage_Width), + GB_PROPERTY("Height", "f", SvgImage_Height), + GB_PROPERTY("H", "f", SvgImage_Height), + GB_METHOD("Resize", NULL, SvgImage_Resize, "(Width)f(Height)f"), + + GB_STATIC_METHOD("Load", "SvgImage", SvgImage_Load, "(Path)s"), + GB_METHOD("Save", NULL, SvgImage_Save, "(Path)s"), + GB_METHOD("Paint", NULL, SvgImage_Paint, "[(X)f(Y)f(Width)f(Height)f]"), + + GB_METHOD("Clear", NULL, SvgImage_Clear, NULL), + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/csvgimage.h b/gb.gtk/src/csvgimage.h new file mode 100644 index 00000000..8cfd5f1d --- /dev/null +++ b/gb.gtk/src/csvgimage.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + csvgimage.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSVGIMAGE_H +#define __CSVGIMAGE_H + +#include "gambas.h" +#include "widgets.h" +#include <cairo.h> +#include <librsvg/rsvg.h> +#ifndef RSVG_CAIRO_H +#include <librsvg/rsvg-cairo.h> +#endif + +typedef + struct + { + GB_BASE ob; + cairo_surface_t *surface; + RsvgHandle *handle; + double width; + double height; + } + CSVGIMAGE; + +#ifndef __CSVGIMAGE_CPP + +extern GB_DESC SvgImageDesc[]; + +#else + +#define THIS OBJECT(CSVGIMAGE) +#define SURFACE THIS->surface +#define HANDLE THIS->handle + +#endif + +cairo_surface_t *SVGIMAGE_begin(CSVGIMAGE *_object); +void SVGIMAGE_end(CSVGIMAGE *_object); + +#endif diff --git a/gb.gtk/src/font-parser.cpp b/gb.gtk/src/font-parser.cpp new file mode 100644 index 00000000..dc978655 --- /dev/null +++ b/gb.gtk/src/font-parser.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + + font-parser.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +static char *gb_font_pointers[8]; +static char *gb_font_name; +static int gb_font_strikeout; +static int gb_font_underline; +static int gb_font_italic; +static int gb_font_bold; +static int gb_font_size; +static int gb_font_relative; + +int gb_fontparser_italic() +{ + return gb_font_italic; +} + +int gb_fontparser_bold() +{ + return gb_font_bold; +} + +int gb_fontparser_underline() +{ + return gb_font_underline; +} + +int gb_fontparser_strikeout() +{ + return gb_font_strikeout; +} + +int gb_fontparser_relative() +{ + return gb_font_relative; +} + +int gb_fontparser_size() +{ + return gb_font_size; +} + +char* gb_fontparser_name() +{ + return gb_font_name; +} + +void gb_font_trim() +{ + int bc,lenptr; + char *ptr; + + for (bc=0;bc<8;bc++) + { + if (gb_font_pointers[bc]==NULL) return; + ptr=gb_font_pointers[bc]; + while (ptr[0]==' ') + { + if (ptr[0]==0) break; + if (ptr[0]==' ') gb_font_pointers[bc]++; + ptr++; + } + lenptr=strlen(gb_font_pointers[bc])-1; + ptr=gb_font_pointers[bc]; + while (lenptr>=0) + { + if (ptr[lenptr]==' ') { ptr[lenptr]=0; } else { lenptr=0; } + lenptr--; + } + } + + +} + +int gb_font_is_size(char *str) +{ + long bc,max; + int rel=false; + int vl=0; + int fact=1; + + if (!str) return true; + + max=strlen(str); + for(bc=0;bc<max;bc++) + { + switch (str[bc]) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + vl*=10; vl+=(str[bc]-48); break; + + case '+': + if (!bc) rel=true; + else return true; + break; + + case '-': + if (!bc) { rel=true; fact=-1; } + else return true; + break; + + default: return true; + } + } + + gb_font_size=vl; + gb_font_relative=rel; + if (gb_font_relative) gb_font_size*=fact; + + return false; +} + +void gb_fontparser_parse(char *str) +{ + long max,bc; + long ptr=0; + long curr=0; + + for (bc=0;bc<8;bc++) gb_font_pointers[bc]=NULL; + gb_font_name=NULL; + gb_font_strikeout=false; + gb_font_underline=false; + gb_font_italic=false; + gb_font_bold=false; + gb_font_relative=false; + gb_font_size=0; + + max=strlen(str); + for (bc=0;bc<max;bc++) + if (str[bc]==',') { str[bc]=0; gb_font_pointers[ptr++]=(str+curr); curr=bc+1; } + + if (curr<(max-1)) gb_font_pointers[ptr]=(str+curr); + + gb_font_trim(); + + for (bc=0; bc<8; bc++) + { + if ( gb_font_pointers[bc]==NULL ) break; + + if (!strcasecmp(gb_font_pointers[bc],"bold")) gb_font_bold=true; + else if (!strcasecmp(gb_font_pointers[bc],"italic")) gb_font_italic=true; + else if (!strcasecmp(gb_font_pointers[bc],"underline")) gb_font_underline=true; + else if (!strcasecmp(gb_font_pointers[bc],"strikeout")) gb_font_strikeout=true; + else + { + if (gb_font_is_size(gb_font_pointers[bc])) gb_font_name=gb_font_pointers[bc]; + } + + } +} diff --git a/gb.gtk/src/font-parser.h b/gb.gtk/src/font-parser.h new file mode 100644 index 00000000..cc184537 --- /dev/null +++ b/gb.gtk/src/font-parser.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + font-parser.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +int gb_fontparser_italic(); +int gb_fontparser_bold(); +int gb_fontparser_underline(); +int gb_fontparser_strikeout(); +int gb_fontparser_relative(); +int gb_fontparser_size(); +char* gb_fontparser_name(); +void gb_fontparser_parse(char *str); + diff --git a/gb.gtk/src/gapplication.cpp b/gb.gtk/src/gapplication.cpp new file mode 100644 index 00000000..96790e5c --- /dev/null +++ b/gb.gtk/src/gapplication.cpp @@ -0,0 +1,1823 @@ +/*************************************************************************** + + gapplication.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include <ctype.h> +#include <time.h> +#include <unistd.h> + +#include "widgets.h" + +#ifndef GTK3 +#include "x11.h" +#include "sm/sm.h" +#endif + +#include "gapplication.h" +#include "gtrayicon.h" +#include "gdesktop.h" +#include "gkey.h" +#include "gmenu.h" +#include "gdialog.h" +#include "gclipboard.h" +#include "gmouse.h" +#include "gprinter.h" +#include "gmainwindow.h" + +//#define DEBUG_ENTER_LEAVE 1 +//#define DEBUG_FIND_CONTROL 1 +//#define DEBUG_FOCUS 1 + +#ifdef GTK3 +static GtkApplication *_app; +GtkStyleProvider *_tooltip_css = NULL; +#endif + +static bool _debug_keypress = false; + +/************************************************************************** + + Global event handler + +**************************************************************************/ + +static bool _focus_change = false; +static bool _doing_focus_change = false; + +static GtkWindowGroup *get_window_group(GtkWidget *widget) +{ + GtkWidget *toplevel = NULL; + + if (widget) + toplevel = gtk_widget_get_toplevel(widget); + + if (GTK_IS_WINDOW(toplevel)) + return gtk_window_get_group(GTK_WINDOW(toplevel)); + else + return gtk_window_get_group(NULL); +} + +static gControl *find_child(gControl *control, int rx, int ry, gControl *button_grab = NULL) +{ + gContainer *cont; + gControl *child; + gMainWindow *window; + int x, y; + int cx, cy, cw, ch; + #ifdef GTK3 + GtkAllocation a; + #endif + + if (gApplication::_control_grab) + return gApplication::_control_grab; + + if (button_grab) + { + #if DEBUG_FIND_CONTROL + fprintf(stderr, "find_child -> %s (button grab)\n", button_grab->name()); + #endif + return button_grab; + } + + window = control->topLevel(); + control = window; + + #ifdef GTK3 + gtk_widget_get_allocation(window->frame, &a); + //fprintf(stderr, "find_child: %d %d window: %d %d %d %d\n", rx, ry, a.x, a.y, a.width, a.height); + rx -= a.x; + ry -= a.y; + #endif + + #if DEBUG_FIND_CONTROL + fprintf(stderr, "find_child: [%s %p] %s (%d %d)\n", window->name(), window, control->name(), rx, ry); + #endif + + while (control->isContainer()) + { + control->getScreenPos(&x, &y); + #ifdef GTK3 + if (!control->isTopLevel()) + { + x -= a.x; + y -= a.y; + } + #endif + + #if DEBUG_FIND_CONTROL + fprintf(stderr, " screen pos %s = %d %d\n", control->name(), x ,y); + #endif + + cont = (gContainer *)control; + + cx = cont->clientX(); + cy = cont->clientY(); + cw = cont->clientWidth(); + ch = cont->clientHeight(); + + #if DEBUG_FIND_CONTROL + fprintf(stderr, " client area of %s: %d %d %d %d\n", control->name(), cx, cy, cw, ch); + #endif + + x = rx - x; + y = ry - y; + if (x < cx || y < cy || x >= (cx + cw) || y >= (cy + ch)) + { + #if DEBUG_FIND_CONTROL + fprintf(stderr, " outside of client area of %s\n", control->name()); + #endif + control = NULL; + break; + } + + #if DEBUG_FIND_CONTROL + fprintf(stderr, " find coord %d %d\n", x, y); + #endif + child = cont->find(x, y); + if (!child) + break; + + control = child; + } + + #if DEBUG_FIND_CONTROL + fprintf(stderr, "find_child -> %s\n", control ? control->name() : "NULL"); + #endif + + return control; +} + +void gApplication::checkHoveredControl(gControl *control) +{ + if (gApplication::_enter != control) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: %s\n", control->name()); + #endif + + gControl *leave = gApplication::_enter; + + while (leave && leave != control && !leave->isAncestorOf(control)) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: leave: %s\n", leave->name()); + #endif + leave->emitLeaveEvent(); + leave = leave->parent(); + } + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: _enter <- %s\n", control ? control->name() : "ø"); + #endif + + if (control) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: enter: %s\n", control->name()); + #endif + control->emitEnterEvent(); + } + } +} + +static void gambas_handle_event(GdkEvent *event) +{ + GtkWidget *widget; + GtkWidget *current_grab; + GtkWidget *grab; + GtkWindowGroup *group; +#ifdef GTK3 + GdkDevice *device; +#endif + gControl *control = NULL, *save_control; + gControl *button_grab; + int x, y, xs, ys, xc, yc; + bool cancel; + int type; + bool handle_event = false; + bool send_to_window = false; + + if (gApplication::_fix_printer_dialog) + { + widget = gtk_get_event_widget(event); + if (widget) + { + //fprintf(stderr, "type: %s\n", G_OBJECT_TYPE_NAME(widget)); + if (!strcmp(G_OBJECT_TYPE_NAME(gtk_widget_get_toplevel(widget)), "GtkPrintUnixDialog")) + { + if (event->type == GDK_WINDOW_STATE) + { + //fprintf(stderr, "event: GDK_WINDOW_STATE!\n"); + widget = gtk_window_get_default_widget(GTK_WINDOW(gtk_widget_get_toplevel(widget))); + if (widget && GTK_IS_BUTTON(widget)) + { + GtkPrintUnixDialog *dialog = GTK_PRINT_UNIX_DIALOG(gtk_widget_get_toplevel(widget)); + gPrinter::fixPrintDialog(dialog); + gApplication::_fix_printer_dialog = false; + //fprintf(stderr, "gtk_button_clicked: %s\n", gtk_button_get_label(GTK_BUTTON(widget))); + if (gApplication::_close_next_window) + gtk_button_clicked(GTK_BUTTON(widget)); + gApplication::_close_next_window = false; + //return; + //g_timeout_add(0, (GSourceFunc)close_dialog, GTK_BUTTON(widget)); + goto __HANDLE_EVENT; + } + //fprintf(stderr, "event: MAP! <<< end\n"); + } + } + } + } + + /*if (event->type == GDK_GRAB_BROKEN) + { + if (gApplication::_in_popup) + fprintf(stderr, "**** GDK_GRAB_BROKEN inside popup: %s %swindow = %p grab_window = %p popup_window = %p\n", event->grab_broken.keyboard ? "keyboard" : "pointer", + event->grab_broken.implicit ? "implicit " : "", event->grab_broken.window, event->grab_broken.grab_window, gApplication::_popup_grab_window); + }*/ + + if (!((event->type >= GDK_MOTION_NOTIFY && event->type <= GDK_FOCUS_CHANGE) || event->type == GDK_SCROLL)) + goto __HANDLE_EVENT; + + if (gApplication::_disable_input_events) + { + switch(event->type) + { + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_MOTION_NOTIFY: + case GDK_SCROLL: + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + //gApplication::pushInputEvent(event); + return; + default: + ; + } + } + + widget = gtk_get_event_widget(event); + + if (!widget) + goto __HANDLE_EVENT; + + if (_debug_keypress && (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)) + { + fprintf(stderr, "[%p] %s: keyval = %d state = %08X (%08X) is_modifier = %d hardware = %d send_event = %d for %p\n", event, event->type == GDK_KEY_PRESS ? "GDK_KEY_PRESS" : "GDK_KEY_RELEASE", + event->key.keyval, event->key.state, event->key.state & ~GDK_MODIFIER_MASK, event->key.is_modifier, event->key.hardware_keycode, event->key.send_event, widget); + } + + /*if ((event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)) + { + if (event->key.state & ~GDK_MODIFIER_MASK) // == 0) + { + if (_debug_keypress) + fprintf(stderr, "ignore key event\n"); + goto __HANDLE_EVENT; + } + }*/ + +#ifdef GTK3 + device = gdk_event_get_device (event); + group = get_window_group(widget); + current_grab = gtk_window_group_get_current_device_grab(group, device); + if (!current_grab) + current_grab = gtk_window_group_get_current_grab(group); //gtk_grab_get_current(); +#else + group = get_window_group(widget); + current_grab = gtk_window_group_get_current_grab(group); //gtk_grab_get_current(); +#endif + + button_grab = gApplication::_button_grab; + + if (gApplication::_control_grab) + { + control = gApplication::_control_grab; + widget = control->border; + //fprintf(stderr, "[1] _control_grab: %s -> widget = %p\n", control->name(), widget); + goto __FOUND_WIDGET; + } + + if (gMenu::currentPopup()) + { + grab = GTK_WIDGET(gMenu::currentPopup()->_popup); + //fprintf(stderr, "[2] popup menu: grab = %p\n", grab); + if (get_window_group(grab) != get_window_group(widget) && (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY)) + goto __RETURN; + } + else + { + grab = current_grab; //gtk_window_group_get_current_grab(get_window_group(widget)); + //fprintf(stderr, "[3] popup: grab = %p / %p / %p\n", gApplication::_popup_grab, grab, gtk_grab_get_current()); + if (!grab) + grab = gApplication::_popup_grab; + //fprintf(stderr, "[4] grab = %p\n", grab); + //fprintf(stderr, "search grab for widget %p -> group = %p -> grab = %p WINDOW = %d\n", widget, get_window_group(widget), grab, GTK_IS_WINDOW(grab)); + //if (grab && grab != widget && !GTK_IS_WINDOW(grab)) + // goto __HANDLE_EVENT; + + //if (!grab && gApplication::_popup_grab) + // grab = gApplication::_popup_grab; + } + //gdk_window_get_user_data(gApplication::_popup_grab_window, (gpointer *)&grab); + + if (grab) + { + control = gt_get_control(grab); + //fprintf(stderr, "grab = %p -> %p %s\n", grab, control, control ? control->name() : ""); + + if (!control) + goto __HANDLE_EVENT; + } + + if (event->type == GDK_FOCUS_CHANGE) + { + control = NULL; + //if (GTK_IS_WINDOW(widget)) + control = gt_get_control(widget); + + if (!control || control->isDesign()) + goto __HANDLE_EVENT; + //fprintf(stderr, "GDK_FOCUS_CHANGE: widget = %p %d : %s %d\n", widget, GTK_IS_WINDOW(widget), control ? control->name() : NULL, event->focus_change.in); + + //if (GTK_IS_WINDOW(widget)) + { + //control = gt_get_control(widget); + if (control) + gApplication::setActiveControl(control, event->focus_change.in); + else if (event->focus_change.in) + { + //fprintf(stderr, "GDK_FOCUS_CHANGE: setActiveWindow(NULL)\n"); + gMainWindow::setActiveWindow(NULL); + } + } + + if (event->focus_change.in && grab && widget != grab && !gtk_widget_is_ancestor(widget, grab)) + { + //fprintf(stderr, "Check popup grab\n"); + gApplication::grabPopup(); + // Must continue, otherwise things are broken by some styles + //return; + } + + goto __HANDLE_EVENT; + } + + if (grab && widget != grab && !gtk_widget_is_ancestor(widget, grab)) + { + //fprintf(stderr, "-> widget = grab\n"); + widget = grab; + } + + //fprintf(stderr, "grab = %p widget = %p %d\n", grab, widget, grab && !gtk_widget_is_ancestor(widget, grab)); + + while (widget) + { + control = gt_get_control(widget); + if (control || grab) + break; + widget = gtk_widget_get_parent(widget); + } + + /*if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE || event->type == GDK_MOTION_NOTIFY) + { + fprintf(stderr, "[%s] widget = %p grab = %p _popup_grab = %p _button_grab = %p\n", + event->type == GDK_BUTTON_PRESS ? "down" : event->type == GDK_BUTTON_RELEASE ? "up" : "move", + widget, grab, gApplication::_popup_grab, gApplication::_button_grab); + //fprintf(stderr, "widget = %p (%p) grab = %p (%p)\n", widget, widget ? g_object_get_data(G_OBJECT(widget), "gambas-control") : 0, + // grab, grab ? g_object_get_data(G_OBJECT(grab), "gambas-control") : 0); + }*/ + + /*if (event->type == GDK_BUTTON_PRESS || event->type == GDK_KEY_PRESS) + { + fprintf(stderr, "[GDK_BUTTON_PRESS] widget = %p control = %p grab = %p _popup_grab = %p _button_grab = %p\n", + widget, control, grab, gApplication::_popup_grab, gApplication::_button_grab); + }*/ + + if (!widget || !control) + goto __HANDLE_EVENT; + +__FOUND_WIDGET: + + //fprintf(stderr, "control = %p %s\n", control, control->name()); + + /*switch ((int)event->type) + { + case GDK_ENTER_NOTIFY: + fprintf(stderr, "ENTER: %p %s\n", control, control ? control->name() : 0); + break; + + case GDK_LEAVE_NOTIFY: + fprintf(stderr, "LEAVE: %p %s\n", control, control ? control->name() : 0); + break; + }*/ + + //group = get_window_group(widget); + //if (group != gApplication::currentGroup()) + // goto __HANDLE_EVENT; + + cancel = false; + + gApplication::updateLastEvent(event); + + switch ((int)event->type) + { + case GDK_ENTER_NOTIFY: + + if (event->crossing.detail == GDK_NOTIFY_INFERIOR) + break; + + control = find_child(control, (int)event->crossing.x_root, (int)event->crossing.y_root); + if (!control) + goto __HANDLE_EVENT; + +#if DEBUG_ENTER_LEAVE + fprintf(stderr, "GDK_ENTER_NOTIFY: %s (%s) %d %d %p [%d] %p\n", control->name(), gApplication::_enter ? gApplication::_enter->name() : "ø", (int)event->crossing.x_root, (int)event->crossing.y_root, event->crossing.window, gdk_window_is_input_only(event->crossing.window), event->crossing.subwindow); +#endif + + if (button_grab) + { + gApplication::_enter_after_button_grab = control; + break; + } + + if (gApplication::_leave) + { + if (gApplication::_leave == control || gApplication::_leave->isAncestorOf(control)) + gApplication::_leave = NULL; + } + + gApplication::checkHoveredControl(control); + + /* + if (gApplication::_leave == control) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "enter ignored: %s\n", control->name()); + #endif + gApplication::_leave = NULL; + } + else if (gApplication::_enter != control) + { + if (check_crossing_event(event)) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "enter: %s\n", control->name()); + #endif + gApplication::checkHoveredControl(control); + } + }*/ + + break; + + case GDK_LEAVE_NOTIFY: + +#if DEBUG_ENTER_LEAVE + fprintf(stderr, "GDK_LEAVE_NOTIFY: %s (%d %d) %p %p\n", control->name(), event->crossing.mode, event->crossing.detail, event->crossing.window, event->crossing.subwindow); +#endif + + if (button_grab) + break; + + if (event->crossing.detail == GDK_NOTIFY_INFERIOR) + break; + + //control = find_child(control, (int)event->button.x_root, (int)event->button.y_root); + + gApplication::_leave = control; + /* + if (gdk_events_pending() && gApplication::_leave == NULL) + { + if (check_crossing_event(event)) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "leave later: %s\n", control->name()); + #endif + gApplication::_leave = control; + } + } + else if (gApplication::_leave != control) + { + if (check_crossing_event(event)) + { + if (gApplication::_leave == control) + gApplication::_leave = NULL; + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "leave: %s\n", control->name()); + #endif + control->emitLeaveEvent(); + } + } + */ + + //if (widget != control->border && widget != control->widget) + // goto __RETURN; + + break; + + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + { + /*if (event->type == GDK_BUTTON_PRESS) + fprintf(stderr, "GDK_BUTTON_PRESS: %p / %p / %p\n", control, button_grab, gApplication::_control_grab);*/ + /*else if (event->type == GDK_BUTTON_RELEASE) + fprintf(stderr, "GDK_BUTTON_RELEASE: %p / %p\n", control, button_grab);*/ + + switch ((int)event->type) + { + case GDK_BUTTON_PRESS: type = gEvent_MousePress; break; + case GDK_2BUTTON_PRESS: type = gEvent_MouseDblClick; break; + default: type = gEvent_MouseRelease; break; + } + + save_control = find_child(control, (int)event->button.x_root, (int)event->button.y_root, button_grab); + + /*if (type == gEvent_MousePress) + fprintf(stderr, "save_control = %p %s\n", save_control, save_control ? save_control->name() : "");*/ + + if (save_control) + save_control = save_control->ignoreDesign(); + + if (!save_control) + { + if (type == gEvent_MousePress && control->isTopLevel()) + { + gMainWindow *win = ((gMainWindow *)control); + if (win->isPopup()) + win->close(); + } + + //fprintf(stderr, "handle event %s\n", type == gEvent_MousePress ? "press" : type == gEvent_MouseRelease ? "release" : "other"); + + goto __HANDLE_EVENT; + } + + control = save_control; + +#if GTK_CHECK_VERSION(3, 4, 0) + bool menu = gdk_event_triggers_context_menu(event); +#else + bool menu = (event->button.button == 3) && (event->type == GDK_BUTTON_PRESS); +#endif + + if (event->type != GDK_BUTTON_RELEASE) + { + #if DEBUG_FOCUS + fprintf(stderr, "GDK_BUTTON_PRESS: %s canFocus = %d design = %d\n", control->name(), control->canFocus(), control->isDesign()); + #endif + if (control->canFocusOnClick()) + control->setFocus(); + if (!control->_no_auto_grab) + gApplication::setButtonGrab(control); + } + + if (event->type == GDK_BUTTON_PRESS) + gMouse::handleClickCount(event); + + __BUTTON_TRY_PROXY: + + cancel = false; + + if (control->isDesign() || control->isEnabled()) + { + if (event->type == GDK_BUTTON_PRESS || CB_control_can_raise(control, type)) + { + control->getScreenPos(&xc, &yc); + xs = (int)event->button.x_root; + ys = (int)event->button.y_root; + x = xs - xc; + y = ys - yc; + + gMouse::validate(); + gMouse::setEvent(event); + //gMouse::setValid(1,(int)event->x,(int)event->y,event->button,event->state,data->screenX(),data->screenY()); + gMouse::setMouse(x, y, xs, ys, event->button.button, event->button.state); + switch ((int)event->type) + { + case GDK_BUTTON_PRESS: + gMouse::setControl(control); + gMouse::setStart(x, y); + cancel = CB_control_mouse(control, gEvent_MousePress); + break; + + case GDK_2BUTTON_PRESS: + cancel = CB_control_mouse(control, gEvent_MouseDblClick); + break; + + case GDK_BUTTON_RELEASE: + gMouse::setControl(NULL); + cancel = CB_control_mouse(control, gEvent_MouseRelease); + break; + } + + gMouse::invalidate(); + } + } + + if (type == gEvent_MouseRelease && control->_grab) + { + gApplication::exitLoop(control); + } + + if (!cancel) + { + if (control->_proxy_for) + { + control = control->_proxy_for; + //fprintf(stderr, "PRESS: try %s\n", control->name()); + goto __BUTTON_TRY_PROXY; + } + } + + if (menu) + { + control = save_control; + while (control) + { + //fprintf(stderr, "menu %s D = %d DI = %d\n", control->name(), control->isDesign(), control->isDesignIgnore()); + if (CB_control_mouse(control, gEvent_MouseMenu)) + { + cancel = true; + break; + } + if (control->hasNativePopup()) + goto __HANDLE_EVENT; + + control = control->_proxy_for; + } + } + + if (cancel) + { + gMouse::resetTranslate(); + goto __RETURN; + } + + if (widget != save_control->border && widget != save_control->widget) + { + //fprintf(stderr, "widget = %p, control = %p %p %s\n", widget, save_control->border, save_control->widget, save_control->name()); + gMouse::resetTranslate(); + goto __RETURN; + } + + break; + } + + case GDK_MOTION_NOTIFY: + + gdk_event_request_motions(&event->motion); + + save_control = control = find_child(control, (int)event->motion.x_root, (int)event->motion.y_root, button_grab); + if (!control) + goto __HANDLE_EVENT; + + control = control->ignoreDesign(); + /*while (control->isDesignIgnore()) + control = control->parent();*/ + //fprintf(stderr, "GDK_MOTION_NOTIFY: (%p %s) grab = %p state = %d tracking = %d\n", control, control->name(), button_grab, event->motion.state, control->isTracking()); + + gApplication::checkHoveredControl(control); + + __MOTION_TRY_PROXY: + + //fprintf(stderr, "--> try (%p %s) / %s\n", control, control->name(), control->_proxy_for ? control->_proxy_for->name() : "-"); + + if (!control->isDesign() && !control->isEnabled()) + goto __HANDLE_EVENT; + + if ((CB_control_can_raise(control, gEvent_MouseMove) || CB_control_can_raise(control, gEvent_MouseDrag)) + && (control->isTracking() || (event->motion.state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)))) + { + control->getScreenPos(&xc, &yc); + xs = (int)event->motion.x_root; + ys = (int)event->motion.y_root; + x = xs - xc; + y = ys - yc; + + gMouse::validate(); + gMouse::setEvent(event); + gMouse::setMouse(x, y, xs, ys, 0, event->motion.state); + + //fprintf(stderr, "pressure = %g\n", gMouse::getAxis(GDK_AXIS_PRESSURE)); + + cancel = CB_control_mouse(control, gEvent_MouseMove); + + //if (data->acceptDrops() && gDrag::checkThreshold(data, gMouse::x(), gMouse::y(), gMouse::startX(), gMouse::startY())) + if (!cancel && (event->motion.state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) + //&& (abs(gMouse::x() - gMouse::y()) + abs(gMouse::startX() - gMouse::startY())) > 8) + && gDrag::checkThreshold(control, gMouse::x(), gMouse::y(), gMouse::startX(), gMouse::startY())) + { + //fprintf(stderr, "gEvent_MouseDrag: event = %p\n", gApplication::lastEvent()); + cancel = CB_control_mouse(control, gEvent_MouseDrag); + } + gMouse::invalidate(); + + if (cancel) + goto __RETURN; + } + + if (control->_proxy_for) + { + control = control->_proxy_for; + //fprintf(stderr, "MOVE: try %s\n", control->name()); + goto __MOTION_TRY_PROXY; + } + + gMouse::resetTranslate(); + //if (widget != save_control->border && widget != save_control->widget) + // goto __RETURN; + + break; + + case GDK_SCROLL: + + save_control = control = find_child(control, (int)event->scroll.x_root, (int)event->scroll.y_root); + if (!control) + goto __HANDLE_EVENT; + + control = control->ignoreDesign(); + /*while (control->isDesignIgnore()) + control = control->parent();*/ + + __SCROLL_TRY_PROXY: + + if (!control->isDesign() && !control->isEnabled()) + goto __HANDLE_EVENT; + + if (CB_control_can_raise(control, gEvent_MouseWheel)) + { + int dir, dt, ort; + + control->setFocus(); + + control->getScreenPos(&xc, &yc); + xs = (int)event->scroll.x_root; + ys = (int)event->scroll.y_root; + x = xs - xc; + y = ys - yc; + + dir = event->scroll.direction; + +#ifdef GTK3 + if (dir == GDK_SCROLL_SMOOTH) + { + /*gdouble dx = 0, dy = 0; + gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy); + if (fabs(dy) > fabs(dx)) + dir = (dy < 0) ? GDK_SCROLL_UP : GDK_SCROLL_DOWN; + else + dir = (dx < 0) ? GDK_SCROLL_LEFT : GDK_SCROLL_RIGHT;*/ + goto __HANDLE_EVENT; + } +#endif + + switch (dir) + { + case GDK_SCROLL_DOWN: dt = -1; ort = 1; break; + case GDK_SCROLL_LEFT: dt = -1; ort = 0; break; + case GDK_SCROLL_RIGHT: dt = 1; ort = 0; break; + case GDK_SCROLL_UP: default: dt = 1; ort = 1; break; + } + + gMouse::validate(); + gMouse::setEvent(event); + gMouse::setMouse(x, y, xs, ys, 0, event->scroll.state); + gMouse::setWheel(dt, ort); + cancel = CB_control_mouse(control, gEvent_MouseWheel); + gMouse::invalidate(); + } + + if (cancel) + { + gMouse::resetTranslate(); + goto __RETURN; + } + + if (control->_proxy_for) + { + control = control->_proxy_for; + goto __SCROLL_TRY_PROXY; + } + + if (!control->_use_wheel) + { + control = control->parent(); + if (control) + goto __SCROLL_TRY_PROXY; + } + + if (widget != save_control->border && widget != save_control->widget) + { + gMouse::resetTranslate(); + goto __RETURN; + } + + break; + + case GDK_KEY_PRESS: + + if (event->key.keyval) + gKey::_last_key_press = event->key.keyval; + send_to_window = control->isTopLevel(); + goto __HANDLE_EVENT; + + case GDK_KEY_RELEASE: + + if (event->key.keyval) + gKey::_last_key_release = event->key.keyval; + send_to_window = control->isTopLevel(); + goto __HANDLE_EVENT; + + default: + + handle_event = true; + goto __RETURN; + } + +__HANDLE_EVENT: + + handle_event = !control || !control->isDesign(); + +__RETURN: + + if (event->type == GDK_BUTTON_RELEASE && gApplication::_button_grab) + { + if (gApplication::_enter_after_button_grab) + { + gApplication::checkHoveredControl(gApplication::_enter_after_button_grab); + gApplication::_enter_after_button_grab = NULL; + } + gApplication::setButtonGrab(NULL); + } + + if (handle_event) + gtk_main_do_event(event); + + if (send_to_window) + gcb_key_event(widget, event, control); + + if (!gdk_events_pending()) // && event->type != GDK_ENTER_NOTIFY && event->type != GDK_LEAVE_NOTIFY) + { + if (gApplication::_leave) + { + //if () // || (gApplication::_leave != control && check_crossing_event(event))) + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "post leave: %s\n", gApplication::_leave->name()); + #endif + + if (gApplication::_enter == gApplication::_leave) + gApplication::_enter = NULL; + + gApplication::_leave->emitLeaveEvent(); + + gApplication::_leave = NULL; + } + } + + gApplication::_event = NULL; +} + + + +/************************************************************************** + +gApplication + +**************************************************************************/ + +int appEvents; + +bool gApplication::_init = false; +bool gApplication::_busy = false; +char *gApplication::_title = NULL; +char *gApplication::_theme = NULL; +int gApplication::_in_popup = 0; +GtkWidget *gApplication::_popup_grab = NULL; +int gApplication::_loopLevel = 0; +void *gApplication::_loop_owner = 0; +GtkWindowGroup *gApplication::_group = NULL; +gControl *gApplication::_enter = NULL; +gControl *gApplication::_leave = NULL; +gControl *gApplication::_ignore_until_next_enter = NULL; +gControl *gApplication::_button_grab = NULL; +gControl *gApplication::_enter_after_button_grab = NULL; +gControl *gApplication::_control_grab = NULL; +gControl *gApplication::_active_control = NULL; +gControl *gApplication::_previous_control = NULL; +gControl *gApplication::_old_active_control = NULL; +bool (*gApplication::onKeyEvent)(int) = NULL; +guint32 gApplication::_event_time = 0; +bool gApplication::_close_next_window = false; +bool gApplication::_fix_printer_dialog = false; +gMainWindow *gApplication::_main_window = NULL; +void (*gApplication::onEnterEventLoop)(); +void (*gApplication::onLeaveEventLoop)(); +bool gApplication::_must_quit = false; +GdkEvent *gApplication::_event = NULL; +bool gApplication::_keep_focus = false; +bool gApplication::_disable_mapping_events = false; + +bool gApplication::_disable_input_events = false; +//GQueue *gApplication::_input_events = NULL; + +bool gApplication::_fix_breeze = false; +bool gApplication::_fix_oxygen = false; +int gApplication::_scrollbar_size = 0; +int gApplication::_scrollbar_big_size = 0; + +void gApplication::grabPopup() +{ + //fprintf(stderr, "grabPopup: %p\n", _popup_grab); + + if (!_popup_grab) + return; + + gt_grab(_popup_grab, TRUE, _event_time); //GDK_CURRENT_TIME); +} + +void gApplication::ungrabPopup() +{ + //fprintf(stderr, "ungrabPopup: %p\n", _popup_grab); + //gtk_grab_remove(_popup_grab); + + if (_popup_grab) + { + _popup_grab = NULL; + gt_ungrab(); + } +} + +#ifdef GTK3 + +bool gApplication::areTooltipsEnabled() +{ + return _tooltip_css == NULL; +} + +void gApplication::enableTooltips(bool vl) +{ + if (vl == areTooltipsEnabled()) + return; + + gt_define_style_sheet(&_tooltip_css, NULL); + + if (!vl) + gt_define_style_sheet(&_tooltip_css, g_string_new("tooltip { opacity: 0; }")); +} + +#else + +bool gApplication::areTooltipsEnabled() +{ + gboolean enabled; + GtkSettings *settings; + + settings = gtk_settings_get_default(); + + g_object_get (settings, "gtk-enable-tooltips", &enabled, (char *)NULL); + + return enabled; +} + +void gApplication::enableTooltips(bool vl) +{ + GtkSettings *settings; + gboolean enabled = vl; + settings = gtk_settings_get_default(); + g_object_set (settings, "gtk-enable-tooltips", enabled, (char *)NULL); +} + +#endif + +static void do_nothing() +{ +} + +#ifndef GTK3 +static gboolean master_client_save_yourself(GnomeClient *client, gint phase, GnomeSaveStyle save_style, gboolean is_shutting_down, GnomeInteractStyle interact_style, gboolean fast, gpointer user_data) +{ + if (gApplication::mainWindow()) + { + //fprintf(stderr, "master_client_save_yourself: %d\n", X11_window_get_desktop((Window)(gApplication::mainWindow()->handle()))); + session_manager_set_desktop(X11_window_get_desktop((Window)(gApplication::mainWindow()->handle()))); + } + return true; +} + +static void master_client_die(GnomeClient *client, gpointer user_data) +{ + if (gApplication::mainWindow()) + gApplication::mainWindow()->close(); + else + gMainWindow::closeAll(); + + gApplication::quit(); + MAIN_check_quit(); +} +#endif + +static void cb_theme_changed(GtkSettings *settings, GParamSpec *param, gpointer data) +{ + gApplication::onThemeChange(); + gDesktop::onThemeChange(); +} + +void gApplication::init(int *argc, char ***argv) +{ + GtkSettings *settings; + + appEvents = 0; + + #ifdef GTK3 + _app = gtk_application_new(NULL, G_APPLICATION_FLAGS_NONE); + g_object_set(G_OBJECT(_app), "register-session", TRUE, NULL); + #else + session_manager_init(argc, argv); + g_signal_connect(gnome_master_client(), "save-yourself", G_CALLBACK(master_client_save_yourself), NULL); + g_signal_connect(gnome_master_client(), "die", G_CALLBACK(master_client_die), NULL); + #endif + + getStyleName(); + + settings = gtk_settings_get_default(); + g_signal_connect(G_OBJECT(settings), "notify::gtk-theme-name", G_CALLBACK(cb_theme_changed), 0); + + gdk_event_handler_set((GdkEventFunc)gambas_handle_event, NULL, NULL); + + gKey::init(); + + onEnterEventLoop = do_nothing; + onLeaveEventLoop = do_nothing; + + _group = gtk_window_group_new(); + + _loop_owner = 0; + + char *env = getenv("GB_GTK_DEBUG_KEYPRESS"); + if (env && strcmp(env, "0")) + _debug_keypress = true; + +#ifdef GTK3 + // Override theme + GtkCssProvider *css = gtk_css_provider_new(); + gtk_css_provider_load_from_data(css, "button { min-width:0;min-height:0; } button.combo { padding-top:0;padding-bottom:0; }", -1, NULL); + gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +#endif + + gApplication::_init = true; +} + +void gApplication::exit() +{ + #ifdef GTK3 + g_object_unref(_app); + #else + session_manager_exit(); + #endif + + if (_title) + g_free(_title); + if (_theme) + g_free(_theme); + + gKey::exit(); + gTrayIcon::exit(); + gDialog::exit(); + gFont::exit(); + gt_exit(); +} + +gControl* gApplication::controlItem(GtkWidget *wid) +{ + gControl *control; + + while (wid) + { + control = gt_get_control(wid); + if (control) + return control; + wid = gtk_widget_get_parent(wid); + } + + return NULL; +} + +static void cb_update_busy(gControl *control) +{ + if (control->mustUpdateCursor()) + control->setMouse(control->mouse()); +} + +void gApplication::setBusy(bool b) +{ + if (b == _busy) + return; + + _busy = b; + + forEachControl(cb_update_busy); + + gdk_display_flush(gdk_display_get_default()); +} + +#if 0 +static bool _dirty = false; + +static gboolean update_geometry(void *data) +{ + GList *iter; + gControl *control; + + if (gContainer::_arrangement_level) + return true; + + _dirty = false; + //g_debug(">>>> update_geometry"); + iter = g_list_first(gControl::controlList()); + while (iter) + { + control = (gControl *)iter->data; + control->updateGeometry(); + iter = iter->next; + } + //g_debug("<<<<"); + + return false; +} + +void gApplication::setDirty() +{ + if (_dirty) + return; + + _dirty = true; + g_timeout_add(0, (GSourceFunc)update_geometry, NULL); +} +#endif + +void gApplication::setDefaultTitle(const char *title) +{ + if (_title) + g_free(_title); + _title = g_strdup(title); +} + +GtkWindowGroup *gApplication::enterGroup() +{ + gControl *control = _enter; + GtkWindowGroup *oldGroup = _group; + _group = gtk_window_group_new(); + + _enter = _leave = NULL; + + while (control) + { + CB_control_enter_leave(control, gEvent_Leave); + control = control->parent(); + } + + return oldGroup; +} + +void gApplication::exitGroup(GtkWindowGroup *oldGroup) +{ + g_object_unref(_group); + _group = oldGroup; +} + +void gApplication::enterLoop(void *owner, bool showIt, GtkWindow *modal) +{ + void *old_owner = _loop_owner; + int l = _loopLevel; + bool d; + + if (showIt) ((gControl *)owner)->show(); + + _loopLevel++; + _loop_owner = owner; + setButtonGrab(NULL); + + d = gApplication::disableInputEvents(false); + (*onEnterEventLoop)(); + do + { + MAIN_do_iteration(false); + } + while (_loopLevel > l); + (*onLeaveEventLoop)(); + gApplication::disableInputEvents(d); + + _loop_owner = old_owner; +} + +void gApplication::enterPopup(gMainWindow *owner) +{ + void *old_owner; + int l; + bool d; + GtkWindow *window = GTK_WINDOW(owner->border); + GtkWidget *old_popup_grab; + + _in_popup++; + + // Remove possible current button grab + gApplication::setButtonGrab(NULL); +// + gtk_window_set_modal(window, true); + owner->show(); + gdk_window_set_override_redirect(gtk_widget_get_window(owner->border), true); + + if (!owner->isDestroyed()) + { + old_popup_grab = _popup_grab; + _popup_grab = owner->border; + + if (_in_popup == 1) + owner->_grab_on_show = TRUE; + + l = _loopLevel; + old_owner = _loop_owner; + + _loopLevel++; + _loop_owner = owner; + + d = gApplication::disableInputEvents(false); + (*onEnterEventLoop)(); + //fprintf(stderr, "event loop <----\n"); + do + { + MAIN_do_iteration(false); + } + while (_loopLevel > l); + //fprintf(stderr, "event loop ---->\n"); + (*onLeaveEventLoop)(); + gApplication::disableInputEvents(d); + + if (_in_popup == 1) + { + if (owner->_grab_on_show) + owner->_grab_on_show = FALSE; + else + gApplication::ungrabPopup(); + } + + _popup_grab = old_popup_grab; + + _loop_owner = old_owner; + + if (owner->border) + { + gdk_window_set_override_redirect(gtk_widget_get_window(owner->border), false); + gtk_window_set_modal(window, false); + } + } + + _in_popup--; +} + +void gApplication::exitLoop(void *owner) +{ + if (!hasLoop(owner)) + return; + + if (_loopLevel > 0) + _loopLevel--; +} + +GtkWindowGroup *gApplication::currentGroup() +{ + if (_group) + return _group; + else + return gtk_window_get_group(NULL); +} + +void gApplication::updateLastEvent(GdkEvent *e) +{ + _event = e; + _event_time = gdk_event_get_time(e); +} + +void gApplication::updateLastEventTime() +{ + _event_time = gtk_get_current_event_time(); +} + +static void post_focus_change(void *) +{ + gControl *current, *control, *next; + + #if DEBUG_FOCUS + fprintf(stderr, "post_focus_change: %d %d\n", !_focus_change, _doing_focus_change); + #endif + + + if (!_focus_change || _doing_focus_change) + return; + + #if DEBUG_FOCUS + fprintf(stderr, "post_focus_change: %s -> %s\n", gApplication::_old_active_control ? gApplication::_old_active_control->name() : "nil", gApplication::_active_control ? gApplication::_active_control->name() : "nil"); + #endif + + _doing_focus_change = true; + + for(;;) + { + current = gApplication::activeControl(); + if (current == gApplication::_old_active_control) + break; + + control = gApplication::_old_active_control; + //if (control) fprintf(stderr, "check focus out %s\n", control->name()); + while (control) + { + next = control->_proxy_for; + #if DEBUG_FOCUS + fprintf(stderr, "focus out %s\n", control->name()); + #endif + CB_control_focus(control, gEvent_FocusOut); + control = next; + } + + gApplication::_old_active_control = current; + gMainWindow::setActiveWindow(current); + + control = current; //gApplication::activeControl(); + //if (control) fprintf(stderr, "check focus in %s\n", control->name()); + while (control) + { + next = control->_proxy_for; + #if DEBUG_FOCUS + fprintf(stderr, "focus in %s\n", control->name()); + #endif + CB_control_focus(control, gEvent_FocusIn); + control = next; + } + } + + _focus_change = false; + _doing_focus_change = false; + + #if DEBUG_FOCUS + fprintf(stderr, "post_focus_change: END\n"); + #endif +} + +void gApplication::finishFocus() +{ + post_focus_change(NULL); +} + +static void handle_focus_change() +{ + if (_focus_change) + return; + + _focus_change = true; + GB.Post((void (*)())post_focus_change, (intptr_t)NULL); +} + +void gApplication::setActiveControl(gControl *control, bool on) +{ + if (control->isWindow() && on) + { + gControl *focus = ((gMainWindow *)control)->getInitialFocus(); + if (focus != control) + { + focus->setFocus(); + control = focus; + } + } + + while (!control->canFocus()) + { + control = control->parent(); + if (!control) + return; + } + + if (on == (_active_control == control)) + return; + + #if DEBUG_FOCUS + fprintf(stderr, "setActiveControl: %s %s %d / %d\n", GB.GetClassName(control->hFree), control->name(), on, _focus_change); + #endif + + /*if (!::strcmp(GB.GetClassName(control->hFree), "FEditor") && !on) + BREAKPOINT();*/ + + if (_active_control && !_focus_change) + _previous_control = _active_control; + + _active_control = on ? control : NULL; + gKey::setActiveControl(_active_control); + handle_focus_change(); +} + +int gApplication::getScrollbarSize() +{ + if (g_type_from_name("OsBar")) + { + char *env = getenv("LIBOVERLAY_SCROLLBAR"); + if (!env || *env != '0') + return 1; + } + +#ifdef GTK3 + + if (_scrollbar_size == 0) + { + GtkWidget *widget = + #ifdef GTK3 + gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL); + #else + gtk_hscrollbar_new(NULL); + #endif + gtk_widget_show(widget); + gtk_widget_get_preferred_width(widget, NULL, &_scrollbar_size); //, &minimum_size, &natural_size); + gtk_widget_get_preferred_height(widget, NULL, &_scrollbar_big_size); //, &minimum_size, &natural_size); + gtk_widget_destroy(widget); + + if (_fix_breeze) + _scrollbar_size += 3; + //fprintf(stderr, "getScrollbarSize = %d\n", size); + } + + return _scrollbar_size; + +#else + + gint trough_border; + gint slider_width; + + gt_get_style_property(GTK_TYPE_SCROLLBAR, "slider-width", &slider_width); + gt_get_style_property(GTK_TYPE_SCROLLBAR, "trough-border", &trough_border); + + return (trough_border) * 2 + slider_width; + +#endif +} + +int gApplication::getScrollbarBigSize() +{ +#ifdef GTK3 + getScrollbarSize(); + return _scrollbar_big_size; +#else + return getScrollbarSize(); +#endif +} + +int gApplication::getScrollbarSpacing() +{ + gint v; + + gt_get_style_property(GTK_TYPE_SCROLLED_WINDOW, "scrollbar-spacing", &v); + + return v; +} + +int gApplication::getInnerWidth() +{ + if (_fix_oxygen) + return 1; + else + return 0; +} + +int gApplication::getFrameWidth() +{ + int w; +#ifdef GTK3 + int h; + + getBoxFrame(&w, &h); + w = h; + +#else + GtkStyle *style; + gint focus_width; + gboolean interior_focus; + //int inner; + + style = gt_get_style(GTK_TYPE_ENTRY); + + gt_get_style_property(GTK_TYPE_ENTRY, "focus-line-width", &focus_width); + gt_get_style_property(GTK_TYPE_ENTRY, "interior-focus", &interior_focus); + + w = MIN(style->xthickness, style->ythickness); + + if (!interior_focus) + w += focus_width; + + w += getInnerWidth(); +#endif + + return w; +} + +void gApplication::getBoxFrame(int *pw, int *ph) +{ + int w, h; + +#ifdef GTK3 + + GtkStyleContext *context = gt_get_style(GTK_TYPE_ENTRY); + GtkBorder border; + GtkBorder padding; + int radius; + + gtk_style_context_get_padding(context, STATE_FOCUSED, &padding); + //fprintf(stderr, "padding: %d %d %d %d\n", padding.top, padding.right, padding.bottom, padding.left); + gtk_style_context_get_border(context, STATE_FOCUSED, &border); + //fprintf(stderr, "border: %d %d %d %d\n", border.top, border.right, border.bottom, border.left); + + gtk_style_context_get(context, STATE_FOCUSED, GTK_STYLE_PROPERTY_BORDER_RADIUS, &radius, NULL); + //fprintf(stderr, "border-radius: %d\n", radius); + radius /= 2; + + w = MAX(border.left + padding.left, border.right + padding.right); + w = MAX(w, radius); + + h = MAX(border.top + padding.top, border.bottom + padding.bottom);//, MAX(padding.top, padding.bottom)); + h = MAX(h, radius); + + w = MAX(2, w); + h = MAX(2, h); + +#else + + GtkStyle *style; + gint focus_width; + gboolean interior_focus; + int inner; + + style = gt_get_style(GTK_TYPE_ENTRY); + + gt_get_style_property(GTK_TYPE_ENTRY, "focus-line-width", &focus_width); + gt_get_style_property(GTK_TYPE_ENTRY, "interior-focus", &interior_focus); + + w = style->xthickness; + h = style->ythickness; + + if (!interior_focus) + { + w += focus_width; + h += focus_width; + } + + inner = getInnerWidth(); + w += inner; + h += inner; + +#endif + + *pw = w; + *ph = h; +} + +char *gApplication::getStyleName() +{ + if (!_theme) + { + char *p; + GtkSettings *settings = gtk_settings_get_default(); + g_object_get(settings, "gtk-theme-name", &_theme, (char *)NULL); + + p = _theme = g_strdup(_theme); + while (*p) + { + *p = tolower(*p); + p++; + } + + _fix_breeze = false; + _fix_oxygen = false; + if (strcasecmp(_theme, "breeze") == 0 || strcasecmp(_theme, "breeze dark") == 0) + _fix_breeze = true; + else if (strcasecmp(_theme, "oxygen-gtk") == 0) + _fix_oxygen = true; + } + + return _theme; +} + +#ifndef GTK3 +static GdkFilterReturn x11_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) +{ + ((X11_EVENT_FILTER)data)((XEvent *)xevent); + return GDK_FILTER_CONTINUE; +} + +void gApplication::setEventFilter(X11_EVENT_FILTER filter) +{ + static X11_EVENT_FILTER save_filter = NULL; + static GdkEventMask save_mask = (GdkEventMask)0; + + if (save_filter) + { + gdk_window_remove_filter(NULL, (GdkFilterFunc)x11_event_filter, (gpointer)save_filter); + gdk_window_set_events(gdk_get_default_root_window(), save_mask); + } + + if (filter) + { + save_mask = gdk_window_get_events(gdk_get_default_root_window()); + gdk_window_set_events(gdk_get_default_root_window(), (GdkEventMask)(save_mask | GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK)); + gdk_window_add_filter(NULL, (GdkFilterFunc)x11_event_filter, (gpointer)filter); + } + + save_filter = filter; +} +#endif + +void gApplication::setMainWindow(gMainWindow *win) +{ + _main_window = win; +} + +void gApplication::quit() +{ + _must_quit = true; +} + +int gApplication::dblClickTime() +{ + gint value; + g_object_get (gtk_settings_get_default(), "gtk-double-click-time", &value, (char *)NULL); + return value; +} + +void gApplication::onThemeChange() +{ + if (_theme) + { + g_free(_theme); + _theme = NULL; + } + + getStyleName(); + _scrollbar_size = 0; +} + +static void for_each_filter(gContainer *cont, GPtrArray *list, bool (*filter)(gControl *)) +{ + int i; + gControl *control; + + if ((*filter)(cont)) + g_ptr_array_add(list, cont); + + for (i = 0; i < cont->childCount(); i++) + { + control = cont->child(i); + if (control->isContainer()) + for_each_filter((gContainer *)control, list, filter); + else + { + if ((*filter)(control)) + g_ptr_array_add(list, control); + } + } +} + +static void for_each_control(gContainer *cont, void (*cb)(gControl *)) +{ + //GPtrArray *children; + int i; + gControl *control; + + (*cb)(cont); + + //children = cont->childrenCopy(); + //for (i = 0; i < children->len; i++) + //{ + // control = (gControl *)g_ptr_array_index(children, i); + for (i = 0; i < cont->childCount(); i++) + { + control = cont->child(i); + + if (control->isContainer()) + for_each_control((gContainer *)control, cb); + else + (*cb)(control); + } + //g_ptr_array_unref(children); +} + +// Of the callback may destroy controls, the filter must be specified! + +void gApplication::forEachControl(void (*cb)(gControl *), bool (*filter)(gControl *)) +{ + GList *iter_win; + gMainWindow *win; + + iter_win = g_list_first(gMainWindow::windows); + while (iter_win) + { + win = (gMainWindow *)iter_win->data; + iter_win = g_list_next(iter_win); + + if (filter) + { + uint i; + gControl *control; + GPtrArray *list = g_ptr_array_new(); + + for_each_filter(win, list, filter); + + for (i = 0; i < list->len; i++) + { + control = (gControl *)g_ptr_array_index(list, i); + if (control->isDestroyed()) + continue; + //fprintf(stderr, "[%d] %s\n", i, control->name()); + (*cb)(control); + } + + g_ptr_array_unref(list); + } + else + { + for_each_control(win, cb); + } + } +} + +#if 0 +void gApplication::pushInputEvent(GdkEvent *event) +{ + if (!_input_events) + _input_events = g_queue_new(); + + g_queue_push_tail(_input_events, gdk_event_copy(event)); +} + +bool gApplication::processInputEvent() +{ + GdkEvent *event; + + if (!areInputEventsEnabled()) + return true; + + if (!_input_events || g_queue_is_empty(_input_events)) + return true; + + event = (GdkEvent *)g_queue_pop_head(_input_events); + gambas_handle_event(event); + gdk_event_free(event); + return false; +} +#endif + +bool gApplication::disableInputEvents(bool disable) +{ + bool d = _disable_input_events; + _disable_input_events = disable; + return d; +} + +bool gApplication::eventsPending() +{ + /*if (areInputEventsEnabled() && _input_events && !g_queue_is_empty(_input_events)) + return true;*/ + + return gtk_events_pending(); + +} + +bool gApplication::hasMiddleClickPaste() +{ + gboolean enabled; + GtkSettings *settings; + + settings = gtk_settings_get_default(); + + g_object_get(settings, "gtk-enable-primary-paste", &enabled, (char *)NULL); + + return enabled; +} + diff --git a/gb.gtk/src/gapplication.h b/gb.gtk/src/gapplication.h new file mode 100644 index 00000000..61153fe6 --- /dev/null +++ b/gb.gtk/src/gapplication.h @@ -0,0 +1,153 @@ +/*************************************************************************** + + gapplication.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GAPPLICATION_H +#define __GAPPLICATION_H + +#ifndef GTK3 +#include <X11/Xlib.h> +typedef + void (*X11_EVENT_FILTER)(XEvent *); +#endif + +class gControl; +class gMainWindow; + +class gApplication +{ +public: + static void init(int *argc, char ***argv); + static bool isInit() { return _init; } + static void quit(); + static void exit(); + static bool mustQuit() { return _must_quit; } + + static gControl* controlItem(GtkWidget *wid); + + static void setBusy(bool b); + static bool isBusy() { return _busy; } + + static gControl* activeControl() { return _active_control; } + static gControl* previousControl() { return _previous_control; } + static void setActiveControl(gControl *control, bool on); + static void finishFocus(); + + static bool areTooltipsEnabled(); + static void enableTooltips(bool vl); + + static bool hasMiddleClickPaste(); + + static int dblClickTime(); + + static void setDefaultTitle(const char *title); + static char *defaultTitle() { return _title; } + + //static void setDirty(); + static int loopLevel() { return _loopLevel; } + static void enterLoop(void *owner, bool showIt = false, GtkWindow *modal = NULL); + static void enterPopup(gMainWindow *owner); + static void exitLoop(void *owner); + static bool hasLoop(void *owner) { return _loop_owner == owner; } + static GtkWindowGroup *enterGroup(); + static void exitGroup(GtkWindowGroup *oldGroup); + static guint32 lastEventTime() { return _event_time; } + static GdkEvent *lastEvent() { return _event; } + static void updateLastEvent(GdkEvent *e); + static void updateLastEventTime(); + + static bool (*onKeyEvent)(int type); + + static int getScrollbarSize(); + static int getScrollbarBigSize(); + static int getScrollbarSpacing(); + static int getFrameWidth(); + static int getInnerWidth(); + static void getBoxFrame(int *w, int *h); + static char *getStyleName(); + + static void grabPopup(); + static void ungrabPopup(); + + static void setMainWindow(gMainWindow *win); + static gMainWindow *mainWindow() { return _main_window; } + + static void checkHoveredControl(gControl *control); + + #ifndef GTK3 + static void setEventFilter(X11_EVENT_FILTER filter); + #endif + + static void setButtonGrab(gControl *grab) { _button_grab = grab; } + + static void onThemeChange(); + + static void forEachControl(void (*cb)(gControl *), bool (*filter)(gControl *) = NULL); + + static bool disableInputEvents(bool disable); + static bool areInputEventsEnabled() { return !_disable_input_events; } + static bool eventsPending(); + static bool processInputEvent() { return true; }; +#if 0 + static void pushInputEvent(GdkEvent *); +#endif + + static bool _fix_breeze; + static bool _fix_oxygen; + static int _scrollbar_size; + static int _scrollbar_big_size; + + //"Private" + static bool _init; + static bool _busy; + static bool _must_quit; + static char *_title; + static char *_theme; + static int _loopLevel; + static int _in_popup; + static GtkWidget *_popup_grab; + static void *_loop_owner; + static GtkWindowGroup *_group; + static GtkWindowGroup *currentGroup(); + static gControl *_enter; + static gControl *_leave; + static gControl *_ignore_until_next_enter; + static gControl *_active_control; + static gControl *_previous_control; + static gControl *_old_active_control; + static gControl *_button_grab; + static gControl *_enter_after_button_grab; + static gControl *_control_grab; + static guint32 _event_time; + static GdkEvent *_event; + static gMainWindow *_main_window; + static bool _close_next_window; + static bool _fix_printer_dialog; + static void (*onEnterEventLoop)(); + static void (*onLeaveEventLoop)(); + static bool _keep_focus; + static bool _disable_mapping_events; + static bool _disable_input_events; + static GQueue *_input_events; +}; + +#endif diff --git a/gb.gtk/src/gb.gtk.component b/gb.gtk/src/gb.gtk.component new file mode 100644 index 00000000..0349774f --- /dev/null +++ b/gb.gtk/src/gb.gtk.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.gtk +Author=Daniel Campos Fernández,Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form diff --git a/gb.gtk/src/gb.gtk.h b/gb.gtk/src/gb.gtk.h new file mode 100644 index 00000000..ca29f301 --- /dev/null +++ b/gb.gtk/src/gb.gtk.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + gb.gtk.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef GB_GTK_H +#define GB_GTK_H + +#include "gambas.h" +#include <cairo.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#ifndef GDK_WINDOWING_X11 +#define NO_X_WINDOW 1 +#endif + +#define GTK_INTERFACE_VERSION 1 + +#ifdef GTK3 +#define GTK_NAME "gb.gtk3" +#else +#define GTK_NAME "gb.gtk" +#endif + +typedef + void *GTK_PICTURE; + +#define CCF_NONE 0 +#define CCF_HAS_INPUT_METHOD 1 + +typedef + struct + { + intptr_t version; + void (*CreateControl)(void *control, void *parent, GtkWidget *widget, uint flags); + GtkWidget *(*CreateGLArea)(void *control, void *parent, void (*init)(GtkWidget *)); + GTK_PICTURE *(*CreatePicture)(cairo_surface_t *surf, int w, int h); + int (*GetDesktopScale)(void); + void *_null; + } + GTK_INTERFACE; + +typedef + struct { + GB_BASE ob; + void *widget; + GB_VARIANT_VALUE tag; + void *font; + void *cursor; + char *popup; + char *action; + } + GTK_CONTROL; + +#endif diff --git a/gb.gtk/src/gbutton.cpp b/gb.gtk/src/gbutton.cpp new file mode 100644 index 00000000..9f31e590 --- /dev/null +++ b/gb.gtk/src/gbutton.cpp @@ -0,0 +1,754 @@ +/*************************************************************************** + + gbutton.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gmainwindow.h" +#include "gdesktop.h" +#include "gbutton.h" + +#include <unistd.h> + +static void cb_click(GtkButton *object, gButton *data) +{ + if (data->disable) + { + data->disable=false; + return; + } + + data->unsetOtherRadioButtons(); + + if (data->type == gButton::Tool) + { + if (!data->isToggle()) + { + data->disable = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->widget), false); + } + } + + CB_button_click(data); +} + +static void cb_click_radio(GtkButton *object, gControl *data) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object))) + CB_button_click(data); + return; +} + +static void cb_click_check(GtkButton *object, gButton *data) +{ + if (data->isTristate() && !data->locked()) + { + data->lock(); + if (data->inconsistent()) + { + data->setInconsistent(false); + data->setValue(false); + } + else if (!data->value()) + data->setInconsistent(true); + data->unlock(); + } + + CB_button_click(data); +} + +#ifdef GTK3 +static gboolean button_draw(GtkWidget *wid, cairo_t *cr, gButton *data) +#else +static gboolean button_expose(GtkWidget *wid, GdkEventExpose *e, gButton *data) +#endif +{ + GdkPixbuf *img; + GdkRectangle rect; + GtkCellRendererState state; + bool rtl; + int x, w, wt, wp, hp; + int d = gDesktop::scale() / 2; + + rtl = gtk_widget_get_direction(wid) == GTK_TEXT_DIR_RTL; + + #ifdef GTK3 + GtkStateFlags f = gtk_widget_get_state_flags(data->widget); + rect.x = rect.y = 0; + rect.width = data->width(); + rect.height = data->height(); + #else + GtkStateType f = (GtkStateType)GTK_WIDGET_STATE(data->widget); + rect = wid->allocation; + #endif + +#ifdef GTK3 + if (data->getBorder() && (f & GTK_STATE_FLAG_ACTIVE)) +#else + if (data->getBorder() && (f == GTK_STATE_ACTIVE)) +#endif + { + int d = 1 + gDesktop::scale() / 16; + rect.x += d; + rect.y += d; + } + + wp = 0; + wt = 0; + + if (data->hasText()) + { + gt_set_cell_renderer_text_from_font((GtkCellRendererText *)data->rendtxt, data->font()); + wt = data->font()->width(data->text(), strlen(data->text())) + 4; + } + + if (data->rendpix) + { + #ifdef GTK3 + if (f & GTK_STATE_FLAG_INSENSITIVE) + #else + if (f == GTK_STATE_INSENSITIVE) + #endif + { + if (!data->rendinc) + data->rendinc = gt_pixbuf_create_disabled(data->rendpix); + img = data->rendinc; + } + else + img = data->rendpix; + + wp = gdk_pixbuf_get_width(img); + hp = gdk_pixbuf_get_height(img); + } + + w = wt + wp; + if (wt && wp) + w += d; + + if (wp) + { + x = rect.x + (rect.width - w) / 2; + if (rtl && wt) + x += wt + d; + + #ifndef GTK3 + cairo_t *cr = gdk_cairo_create(wid->window); + gdk_cairo_region(cr, e->region); + cairo_clip(cr); + #endif + + gt_cairo_draw_pixbuf(cr, img, x, rect.y + (rect.height - hp) / 2, -1, -1, 1.0, NULL); + + #ifndef GTK3 + cairo_destroy(cr); + #endif + } + + if (wt) + { + x = rect.x + (rect.width - w) / 2; + if (!rtl && wp) + x += wp + d; + + #ifdef GTK3 + + g_object_set(G_OBJECT(data->rendtxt), "sensitive", !(f & GTK_STATE_INSENSITIVE), (void *)NULL); + + if (f & GTK_STATE_SELECTED) + state = GTK_CELL_RENDERER_SELECTED; + else if (f & GTK_STATE_INSENSITIVE) + state = GTK_CELL_RENDERER_INSENSITIVE; + else + state = (GtkCellRendererState)0; + + #else + + g_object_set(G_OBJECT(data->rendtxt), "sensitive", true, (void *)NULL); + + switch (f) + { + case GTK_STATE_PRELIGHT: + state = GTK_CELL_RENDERER_PRELIT; + break; + //case GTK_STATE_PRELIGHT: state=GTK_CELL_RENDERER_PRELIT; break; + case GTK_STATE_SELECTED: + state = GTK_CELL_RENDERER_SELECTED; + break; + + case GTK_STATE_INSENSITIVE: + state = GTK_CELL_RENDERER_INSENSITIVE; + g_object_set(G_OBJECT(data->rendtxt), "sensitive", false, (void *)NULL); + break; + + default: + state = (GtkCellRendererState)0; + break; + } + + #endif + + rect.x = x; + rect.width = wt; + gtk_cell_renderer_set_fixed_size(data->rendtxt, rect.width, rect.height); + #ifdef GTK3 + gtk_cell_renderer_render(data->rendtxt, cr, wid, &rect, &rect, state); + #else + gtk_cell_renderer_render(data->rendtxt, wid->window, wid, &rect, &rect, &e->area, state); + #endif + } + + return FALSE; +} + +#ifdef GTK3 +static void cb_state(GtkWidget *widget, GtkStateFlags state, gButton *data) +#else +static void cb_state(GtkWidget *widget, GtkStateType state, gButton *data) +#endif +{ + data->refresh(); +} + + +gButton::gButton(gContainer *par, Type typ) : gControl(par) +{ + gContainer *ct; + + disable = false; + _toggle = false; + _radio = false; + _animated = false; + _stretch = true; + _tristate = false; + _autoresize = false; + bufText = NULL; + rendtxt = NULL; + rendpix = NULL; + rendinc = NULL; + _label = NULL; + pic = NULL; + shortcut = 0; + _is_button = TRUE; + + switch(typ) + { + case Check: + border = gtk_check_button_new(); + break; + + case Radio: + ct = parent(); + if (!ct->radiogroup) + { + ct->radiogroup = gtk_radio_button_new(NULL); + g_object_ref(ct->radiogroup); + border = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ct->radiogroup)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(border),true); + } + else + { + border = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ct->radiogroup)); + } + break; + + case Toggle: + _no_background = true; + rendtxt = gtk_cell_renderer_text_new(); + border = gtk_toggle_button_new(); + break; + + case Tool: + _no_background = true; + rendtxt = gtk_cell_renderer_text_new(); + border = gtk_toggle_button_new(); + gt_set_focus_on_click(border, false); + break; + + default: + _no_background = true; + border = gtk_button_new(); + rendtxt = gtk_cell_renderer_text_new(); + typ = Button; + break; + } + + widget = border; + + type = typ; + + if (rendtxt) + { + g_object_set(G_OBJECT(rendtxt), "xalign", 0.5, "yalign", 0.5, "xpad", 0, "ypad", 0, NULL); + ON_DRAW(widget, this, button_expose, button_draw); + } + + realize(); + + gtk_widget_add_events(widget, GDK_POINTER_MOTION_MASK); + + if (type == Radio) + g_signal_connect(G_OBJECT(widget),"clicked",G_CALLBACK(cb_click_radio),(gpointer)this); + else if (type == Check) + g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(cb_click_check), (gpointer)this); + else + { + g_signal_connect(G_OBJECT(widget),"clicked",G_CALLBACK(cb_click),(gpointer)this); + setColorButton(); + } + + #ifdef GTK3 + g_signal_connect(G_OBJECT(widget), "state-flags-changed", G_CALLBACK(cb_state), (gpointer)this); + #else + g_signal_connect(G_OBJECT(widget), "state-changed", G_CALLBACK(cb_state), (gpointer)this); + #endif + + setText(NULL); + + if (type == Tool) + setBorder(false); +} + +gButton::~gButton() +{ + setDefault(false); + setCancel(false); + setPicture(0); + g_free(bufText); +} + +void gButton::setInconsistent(bool vl) +{ + if (type != Check) return; + + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON(widget),vl); +} + +bool gButton::inconsistent() const +{ + gboolean vl=false; + + if (type != Check) return false; + g_object_get (G_OBJECT(widget),"inconsistent",&vl,(void *)NULL); + return vl; +} + +void gButton::setText(const char *st) +{ + GtkAccelGroup *accel; + char *buf; + + accel = window()->accel; + + if (bufText) + { + if (shortcut) + gtk_widget_remove_accelerator(widget, accel, (guint)shortcut, GDK_MOD1_MASK); + g_free(bufText); + } + + bufText = st ? g_strdup(st) : NULL; + + if (rendtxt) + { + if (hasText()) + { + shortcut = (int)gMnemonic_correctMarkup(bufText, &buf); + + if (shortcut) + gtk_widget_add_accelerator(widget, "clicked", accel, (guint)shortcut, GDK_MOD1_MASK, (GtkAccelFlags)0); + + if (rendtxt) + g_object_set(G_OBJECT(rendtxt), "markup", buf, (void *)NULL); + + g_free(buf); + } + else + { + g_object_set(G_OBJECT(rendtxt), "markup", "", (void *)NULL); + } + + refresh(); + } + else + { + if (hasText()) + { + gMnemonic_correctText((char*)st, &buf); + gtk_button_set_use_underline(GTK_BUTTON(widget), TRUE); + gtk_button_set_label(GTK_BUTTON(widget), buf); + g_free(buf); + } + else + gtk_button_set_label(GTK_BUTTON(widget), ""); + + _label = gtk_bin_get_child(GTK_BIN(widget)); + updateDirection(); + #ifndef GTK3 + set_gdk_fg_color(_label, foreground()); + #endif + } + + updateFont(); +} + + +gPicture* gButton::picture() const +{ + if ( (type == Check) || (type == Radio) ) + return NULL; + + return pic; +} + +void gButton::setPicture(gPicture *npic) +{ + GdkPixbuf *new_rendpix = NULL; + + if ((type == Check) || (type == Radio)) return; + + gPicture::assign(&pic, npic); + + if (pic) + { + new_rendpix = pic->getPixbuf(); + if (new_rendpix) + g_object_ref(new_rendpix); + } + + if (rendpix) { g_object_unref(G_OBJECT(rendpix)); rendpix = NULL; } + if (rendinc) { g_object_unref(G_OBJECT(rendinc)); rendinc = NULL; } + + rendpix = new_rendpix; + + updateSize(); + refresh(); +} + +bool gButton::getBorder() const +{ + switch(gtk_button_get_relief(GTK_BUTTON(widget))) + { + case GTK_RELIEF_NORMAL: + case GTK_RELIEF_HALF: + return true; + default: + return false; + } +} + +void gButton::setBorder(bool vl) +{ + gtk_button_set_relief (GTK_BUTTON(widget), vl ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE); +} + +bool gButton::isDefault() const +{ + gMainWindow *win = window(); + return win ? win->_default == this : false; +} + +void gButton::setDefault(bool vl) +{ + gMainWindow *win = window(); + + if (type != Button || !win) + return; + + if (vl) + { + win->_default = this; +#if GTK_CHECK_VERSION(2, 18, 0) + gtk_widget_set_can_default(widget, true); +#else + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT); +#endif + //gtk_widget_grab_default (widget); + } + else + { +#if GTK_CHECK_VERSION(2, 18, 0) + gtk_widget_set_can_default(widget, false); +#else + GTK_WIDGET_UNSET_FLAGS(widget, GTK_CAN_DEFAULT); +#endif + if (win->_default == this) + win->_default = NULL; + } +} + +bool gButton::isCancel() const +{ + gMainWindow *win = window(); + return win ? win->_cancel == this : false; +} + +void gButton::setCancel(bool vl) +{ + gMainWindow *win = window(); + + if (type != Button || !win) + return; + + if (vl) + win->_cancel = this; + else if (win->_cancel == this) + win->_cancel = NULL; +} + +bool gButton::value() const +{ + if (type == Button) + return false; + else + return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)); +} + +void gButton::setValue(bool vl) +{ + if (type == Button) + { + if (vl) gtk_button_clicked(GTK_BUTTON(widget)); + } + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), vl); +} + +void gButton::setToggle(bool vl) +{ + if (type != Tool) return; + _toggle = vl; +} + +bool gButton::isToggle() const +{ + return type == Toggle || type == Check || type == Radio || _toggle; +} + +void gButton::animateClick(bool on) +{ + if (type != Button) return; + + if (!on && !_animated) + { +#ifdef GTK3 + gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_ACTIVE, FALSE); +#else + gtk_widget_set_state(widget, GTK_STATE_ACTIVE); +#endif + refresh(); + _animated = true; + } + else if (on && _animated) + { + _animated = false; +#ifdef GTK3 + gtk_widget_unset_state_flags(widget, GTK_STATE_FLAG_ACTIVE); +#else + gtk_widget_set_state(widget, GTK_STATE_NORMAL); +#endif + refresh(); + gtk_button_clicked(GTK_BUTTON(widget)); + } +} + +int gButton::autoHeight() const +{ + int mh = 0; + + if (hasText()) + { + if (type == Button || type == Toggle || type == Tool) + mh = font()->height() + gDesktop::scale(); + else + mh = font()->height() + 2; + } + + if (pic && (pic->height() > mh)) + mh = pic->height(); + + return mh; +} + +void gButton::setRadio(bool vl) +{ + _radio = vl; + if (value()) + unsetOtherRadioButtons(); +} + +bool gButton::isRadio() const +{ + return type == Radio || _radio; +} + +void gButton::unsetOtherRadioButtons() +{ + gContainer *pr = parent(); + gControl *child; + gButton *button; + int i; + + if (type == Radio || type == Button || !isRadio() || !isToggle()) + return; + + for (i = 0; i < pr->childCount(); i++) + { + child = pr->child(i); + if (!child->isButton()) + continue; + + button = (gButton *)child; + + if (button == this) + { + if (!value()) + { + button->disable = true; + button->setValue(true); + } + } + else if (button->type == type && button->isRadio() && button->isToggle() && button->value()) + { + button->disable = true; + button->setValue(false); + } + } +} + +bool gButton::hasShortcut() const +{ + return isDefault() || isCancel() || shortcut; +} + + +void gButton::setStretch(bool vl) +{ + _stretch = vl; +} + +void gButton::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + +#ifndef GTK3 + if (_label) + set_gdk_fg_color(_label, color); +#endif + + if (rendtxt) + { + if (color == COLOR_DEFAULT) + { + g_object_set(G_OBJECT(rendtxt), + "foreground-set", FALSE, + (void *)NULL); + } + else + { + GdkColor col; + fill_gdk_color(&col, color); + g_object_set(G_OBJECT(rendtxt), + "foreground-set", TRUE, + "foreground-gdk", &col, + (void *)NULL); + } + } +} + +void gButton::setTristate(bool vl) +{ + _tristate = vl; + if (!_tristate) + setInconsistent(false); +} + +void gButton::setAutoResize(bool vl) +{ + _autoresize = vl; + updateSize(); +} + +void gButton::updateSize() +{ + int mw, mh; + + if (!_autoresize) + return; + + mh = autoHeight(); + mw = 0; + + if (hasText()) + { + gint m; + + if (type == Check || type == Radio) + { +#ifdef GTK3 + int indicator_size, indicator_spacing, focus_width, focus_pad; + gtk_widget_style_get(widget, + "indicator-size", &indicator_size, + "indicator-spacing", &indicator_spacing, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + (char *)NULL); + m = (indicator_spacing + indicator_size + indicator_spacing + 2 * (focus_width + focus_pad)) + font()->width(bufText, strlen(bufText)); +#else + GtkRequisition req; + g_signal_emit_by_name(border, "size-request", &req); + m = req.width; +#endif + } + else + m = font()->width(bufText, strlen(bufText)); + + mw += m; + } + + if (pic) + { + if (mw) mw += gDesktop::scale() / 2; + mw += pic->width(); + } + + mw += gDesktop::scale() * 2; + + if (mh < height()) + mh = height(); + + resize(mw, mh); +} + +void gButton::updateDirection() +{ + gControl::updateDirection(); + if (_label) + gtk_widget_set_direction(_label, gtk_widget_get_direction(widget)); +} + +gColor gButton::defaultBackground() const +{ + if (type == Check || type == Radio) + return gControl::defaultBackground(); + else + return gDesktop::getColor(gDesktop::BUTTON_BACKGROUND, !isEnabled()); +} diff --git a/gb.gtk/src/gbutton.h b/gb.gtk/src/gbutton.h new file mode 100644 index 00000000..a6c9dcde --- /dev/null +++ b/gb.gtk/src/gbutton.h @@ -0,0 +1,103 @@ +/*************************************************************************** + + gbutton.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBUTTON_H +#define __GBUTTON_H + +class gButton : public gControl +{ +public: + + enum Type + { + Button, Toggle, Check, Radio, Tool + }; + + gButton(gContainer *parent, Type type); + ~gButton(); + + bool getBorder() const; + bool isCancel() const; + bool isDefault() const; + const char *text() const { return bufText; } + bool hasText() const { return bufText && *bufText; } + gPicture *picture() const; + bool value() const; + bool isToggle() const; + bool isRadio() const; + bool inconsistent() const; + bool isStretch() { return _stretch; } + bool isTristate() const { return _tristate; } + bool isAutoResize() const { return _autoresize; } + + //void setEnabled(bool vl); + void setBorder(bool vl); + void setCancel(bool vl); + void setDefault(bool vl); + void setText(const char *st); + void setPicture(gPicture *pic); + void setValue(bool vl); + void setToggle(bool vl); + void setRadio(bool vl); + void setInconsistent(bool vl); + void setStretch(bool vl); + void setTristate(bool vl); + void setAutoResize(bool vl); + + virtual void setRealForeground(gColor color); + gColor defaultBackground() const; + + virtual void updateDirection(); + + //virtual void setRealBackground(gColor color); + +//"Method" + void animateClick(bool on); + +//"Private" + char type; + char *bufText; + GtkWidget *_label; + GtkCellRenderer *rendtxt; + GdkPixbuf *rendpix,*rendinc; + gPicture *pic; + int shortcut; + unsigned disable : 1; + unsigned _toggle : 1; + unsigned _animated : 1; + unsigned _radio : 1; + unsigned _stretch : 1; + unsigned _tristate : 1; + unsigned _autoresize : 1; + + bool hasShortcut() const; + void unsetOtherRadioButtons(); + int autoHeight() const; + virtual void updateSize(); +}; + +// Callbacks +void CB_button_click(gControl *sender); + + +#endif diff --git a/gb.gtk/src/gclipboard.h b/gb.gtk/src/gclipboard.h new file mode 100644 index 00000000..0c1d94c4 --- /dev/null +++ b/gb.gtk/src/gclipboard.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + gclipboard.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCLIPBOARD_H +#define __GCLIPBOARD_H + +class gPicture; + +class gClipboard +{ +public: + enum + { + Nothing = 0, + Text = 1, + Image = 2 + }; + enum { + Clipboard = 0, + Selection = 1 + }; + + static void setCurrent(int clipboard); + static int getCurrent(); + static void clear(); + static char *getFormat(int n = 0); + static int getType(); + static void setText(char *text, int len, char *format = 0); + static char *getText(int *len, const char *format); + static void setImage(gPicture *image); + static gPicture *getImage(); + static bool hasChanged(); +}; + +#endif diff --git a/gb.gtk/src/gcolor.h b/gb.gtk/src/gcolor.h new file mode 100644 index 00000000..28dbc586 --- /dev/null +++ b/gb.gtk/src/gcolor.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + gcolor.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCOLOR_H +#define __GCOLOR_H + +#include "gb.image.h" + +typedef + uint gColor; + +#endif diff --git a/gb.gtk/src/gcontainer.cpp b/gb.gtk/src/gcontainer.cpp new file mode 100644 index 00000000..f83c8d23 --- /dev/null +++ b/gb.gtk/src/gcontainer.cpp @@ -0,0 +1,1018 @@ +/*************************************************************************** + + gcontainer.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" + +#include <stdio.h> +#include <unistd.h> + +#include "gapplication.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gcontainer.h" + +static GList *_arrange_list = NULL; + +static gControl *get_next_child_widget (gContainer *gtk_control, int *gtk_index) +{ + gControl *ctrl; + + for(;;) + { + ctrl = gtk_control->child(*gtk_index); + if (!ctrl) + return NULL; + + (*gtk_index)++; + if (ctrl->border && ctrl->widget && ctrl->isVisible()) + return ctrl; + } +} + +#ifdef GTK3 +static gboolean cb_draw(GtkWidget *wid, cairo_t *cr, gContainer *data) +{ + CUSERCONTROL_cb_draw(data, cr); + return false; +} +#else +static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gContainer *data) +{ + CUSERCONTROL_cb_draw(data, e->region, wid->allocation.x, wid->allocation.y); + return false; +} +#endif + +static void cb_map(GtkWidget *widget, gContainer *sender) +{ + //fprintf(stderr, "cb_map: %s %d\n", sender->name(), gApplication::_disable_mapping_events); + + sender->setShown(true); + sender->arrangeLater(); +} + +#if GTK3 +static void cb_remap_children(GtkWidget *widget, GdkEvent *event, gContainer *sender) +{ + int i; + gControl *child; + + //fprintf(stderr, "cb_remap_children: '%s' <<<\n", sender->name()); + + for (i = 0; i < sender->childCount(); i++) + { + child = sender->child(i); + if (gtk_widget_get_visible(child->border)) + { + child->hideButKeepFocus(); + child->showButKeepFocus(); + } + } + + //fprintf(stderr, "cb_remap_children: >>> '%s'\n", sender->name()); +} +#endif + +static void cb_unmap(GtkWidget *widget, gContainer *sender) +{ + //fprintf(stderr, "cb_unmap: %s %d\n", sender->name(), gApplication::_disable_mapping_events); + if (gApplication::_disable_mapping_events) + return; + + sender->setShown(false); +} + +static void resize_container(gContainer *cont, int w, int h) +{ + /*w += cont->width() - cont->containerWidth(); + h += cont->height() - cont->containerHeight();*/ + + if (w >= 0 && h >= 0) + cont->resize(w, h); +} + + +#define WIDGET_TYPE gControl* +#define CONTAINER_TYPE gContainer* +#define ARRANGEMENT_TYPE gContainerArrangement* +#define IS_RIGHT_TO_LEFT(_object) (gtk_widget_get_direction(((gControl *)_object)->widget) == GTK_TEXT_DIR_RTL) +#define GET_WIDGET(_object) _object +#define GET_CONTAINER(_object) _object +#define GET_ARRANGEMENT(_object) (((gContainer*)_object)->getArrangement()) +#define IS_EXPAND(_object) (((gControl*)_object)->isExpand()) +#define IS_IGNORE(_object) (((gControl*)_object)->isIgnore()) +#define IS_DESIGN(_object) (((gControl*)_object)->isDesign()) +#define IS_WIDGET_VISIBLE(_widget) (((gControl*)_widget)->isVisible()) + +#define CAN_ARRANGE(_object) (((gContainer *)_object)->isShown() && !((gControl *)_object)->isDestroyed()) +//|| (((gControl *)_object)->isTopLevel() && ((gMainWindow *)_object)->opened)) + +// BM: ClientX() & ClientY() are relative to the border. +// We need X & Y relative to the container widget. + +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) _x=((gContainer*)_widget)->containerX(); \ + _y=((gContainer*)_widget)->containerY(); \ + _w=((gContainer*)_widget)->containerWidth(); \ + _h=((gContainer*)_widget)->containerHeight() + +#define GET_WIDGET_X(_widget) (((gControl*)_widget)->left()) +#define GET_WIDGET_Y(_widget) (((gControl*)_widget)->top()) +#define GET_WIDGET_W(_widget) (((gControl*)_widget)->width()) +#define GET_WIDGET_H(_widget) (((gControl*)_widget)->height()) +#define MOVE_WIDGET(_object, _widget, _x, _y) (((gControl*)_widget)->move( _x, _y)) +#define RESIZE_WIDGET(_object, _widget, _w, _h) (((gControl*)_widget)->resize( _w, _h, true)) +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) (((gControl*)_widget)->moveResize(_x, _y, _w, _h, true)) +#define RESIZE_CONTAINER(_object, _cont, _w, _h) resize_container((gContainer *)(_cont), _w, _h) + +#define INIT_CHECK_CHILDREN_LIST(_widget) \ + gContainer *gtk_control = (gContainer*)_widget; \ + int gtk_index = 0; \ + +#define HAS_CHILDREN() (gtk_control->childCount() != 0) + +#define RESET_CHILDREN_LIST() gtk_index = 0; + +#define GET_NEXT_CHILD_WIDGET() get_next_child_widget(gtk_control, >k_index) + +#define GET_OBJECT_FROM_WIDGET(_widget) ((void*)_widget) + +#define GET_OBJECT_NAME(_object) (((gControl *)_object)->name()) + +#define RAISE_ARRANGE_EVENT(_object) CB_container_arrange((gContainer *)_object); +#define RAISE_BEFORE_ARRANGE_EVENT(_object) CB_container_before_arrange((gContainer *)_object); + +#define DESKTOP_SCALE gDesktop::scale() + +#define FUNCTION_NAME arrangeContainer + +#include "gb.form.arrangement.h" + +void gContainer::performArrange() +{ + if (_no_arrangement) + _did_arrangement = true; + else + { + _did_arrangement = false; + //fprintf(stderr, "performArrange: %p / %p [%d] '%s'\n", this, hFree, isShown(), name()); + arrangeContainer((void *)this); + } +} + + +static int _max_w, _max_h; +static int _gms_x, _gms_y, _gms_w, _gms_h; + +static void gms_move_widget(gControl *wid, int x, int y) +{ + int w = x + wid->width(); + int h = y + wid->height(); + + if (w > _max_w) _max_w = w; + if (h > _max_h) _max_h = h; +} + +static void gms_move_resize_widget(gControl *wid, int x, int y, int w, int h) +{ + w += x; + h += y; + + if (w > _max_w) _max_w = w; + if (h > _max_h) _max_h = h; +} + +#undef MOVE_WIDGET +#define MOVE_WIDGET(_object, _widget, _x, _y) gms_move_widget(_widget, _x, _y) +#undef RESIZE_WIDGET +#define RESIZE_WIDGET(_object, _widget, _w, _h) (0) +#undef MOVE_RESIZE_WIDGET +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) gms_move_resize_widget(_widget, _x, _y, _w, _h) +#undef RAISE_BEFORE_ARRANGE_EVENT +#define RAISE_BEFORE_ARRANGE_EVENT(_object) (0) +#undef RAISE_ARRANGE_EVENT +#define RAISE_ARRANGE_EVENT(_object) (0) +#undef FUNCTION_NAME +#define FUNCTION_NAME get_max_size +#undef RESIZE_CONTAINER +#define RESIZE_CONTAINER(_object, _cont, _w, _h) (0) +#undef GET_WIDGET_CONTENTS +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) \ + _x = _gms_x; \ + _y = _gms_y; \ + _w = _gms_w; \ + _h = _gms_h; + +//#undef IS_WIDGET_VISIBLE +//#define IS_WIDGET_VISIBLE(_cont) (1) +#define GET_MAX_SIZE + +#include "gb.form.arrangement.h" + +void gContainer::getMaxSize(int xc, int yc, int wc, int hc, int *w, int *h) +{ + int add; + gContainerArrangement *arr = getArrangement(); + bool locked; + + locked = arr->locked; + arr->locked = false; + + _max_w = 0; + _max_h = 0; + _gms_x = xc; + _gms_y = yc; + _gms_w = wc; + _gms_h = hc; + get_max_size((void *)this); + + if (arr->margin) + add = arr->padding ? arr->padding : gDesktop::scale(); + else if (!arr->spacing) + add = arr->padding; + else + add = 0; + + *w = _max_w + add; + *h = _max_h + add; + + arr->locked = locked; +} + +void gContainer::decide(gControl *child, bool *width, bool *height) +{ + *width = *height = FALSE; + + if (!isShown() || !child->_allow_show || child->isIgnore() || autoResize()) + return; + + if ((arrange() == ARRANGE_VERTICAL) + || (arrange() == ARRANGE_HORIZONTAL && child->isExpand()) + || (arrange() == ARRANGE_ROW && child->isExpand()) + || (arrange() == ARRANGE_FILL)) + *width = TRUE; + + if ((arrange() == ARRANGE_HORIZONTAL) + || (arrange() == ARRANGE_VERTICAL && child->isExpand()) + || (arrange() == ARRANGE_COLUMN && child->isExpand()) + || (arrange() == ARRANGE_FILL)) + *height = TRUE; +} + +void gContainer::initialize() +{ + _children = g_ptr_array_new(); + + radiogroup = NULL; + + _proxyContainer = NULL; + _proxyContainerFor = NULL; + _client_x = -1; + _client_y = -1; + _client_w = 0; + _client_h = 0; + _no_arrangement = 0; + _did_arrangement = false; + _is_container = true; + _user_container = false; + _shown = false; + _arrange_later = false; + + arrangement.mode = 0; + arrangement.spacing = false; + arrangement.padding = 0; + arrangement.autoresize = false; + arrangement.locked = false; + arrangement.user = false; + arrangement.margin = false; + arrangement.indent = false; + arrangement.centered = false; + arrangement.invert = false; + arrangement.paint = false; +} + +gContainer::gContainer() +{ + //g_print("gContainer() %d\n",this); + initialize(); +} + +gContainer::gContainer(gContainer *parent) : gControl(parent) +{ + //g_print("gContainer(parent) %d par: %d\n",this,parent); + initialize(); +} + +gContainer::~gContainer() +{ + int i; + + resetArrangeLater(); + + for (i = 0; i < childCount(); i++) + child(i)->removeParent(); + + g_ptr_array_unref(_children); + _children = NULL; + + if (radiogroup) { g_object_unref(G_OBJECT(radiogroup)); radiogroup=NULL; } +} + +int gContainer::childCount() const +{ + return _children->len; +} + +gControl* gContainer::child(int index) const +{ + if (index < 0 || index >= (int)_children->len) + return NULL; + else + return (gControl *)g_ptr_array_index(_children, index); +} + +int gContainer::childIndex(gControl *ch) const +{ + int i; + + for (i = 0; i < childCount(); i++) + { + if (child(i) == ch) + return i; + } + + return -1; +} + +void gContainer::setArrange(int vl) +{ + switch(vl) + { + case ARRANGE_NONE: + case ARRANGE_HORIZONTAL: + case ARRANGE_VERTICAL: + case ARRANGE_LEFT_RIGHT: + case ARRANGE_TOP_BOTTOM: + case ARRANGE_FILL: + if (vl != arrangement.mode) + { + arrangement.mode = vl; + updateScrollBar(); + performArrange(); + } + default: + break; + } +} + +void gContainer::setPadding(int vl) +{ + if (vl >= 0 && vl <= 255 && vl != arrangement.padding) + { + arrangement.padding = vl; + performArrange(); + } +} + +void gContainer::setSpacing(bool vl) +{ + if (vl != arrangement.spacing) + { + arrangement.spacing = vl; + performArrange(); + } +} + +void gContainer::setMargin(bool vl) +{ + if (vl != arrangement.margin) + { + arrangement.margin = vl; + performArrange(); + } +} + +void gContainer::setIndent(bool vl) +{ + if (vl != arrangement.indent) + { + arrangement.indent = vl; + performArrange(); + } +} + +void gContainer::setCentered(bool vl) +{ + if (vl != arrangement.centered) + { + arrangement.centered = vl; + performArrange(); + } +} + +void gContainer::setAutoResize(bool vl) +{ + if (vl != arrangement.autoresize) + { + arrangement.autoresize = vl; + performArrange(); + } +} + +void gContainer::setUser() +{ + if (arrangement.user) + return; + + arrangement.user = true; + performArrange(); + updateDesignChildren(); +} + +void gContainer::setPaint() +{ + arrangement.paint = true; + ON_DRAW_BEFORE(border, this, cb_expose, cb_draw); +} + +void gContainer::setInvert(bool vl) +{ + if (vl != arrangement.invert) + { + arrangement.invert = vl; + performArrange(); + } +} + +int gContainer::clientX() +{ + gint xc, yc; + GtkWidget *cont = getContainer(); + + if (_client_x >= 0) + return _client_x; + + + if (!_scroll && gtk_widget_get_window(cont) && gtk_widget_get_window(border)) + { + gtk_widget_translate_coordinates(cont, border, 0, 0, &xc, &yc); + xc += containerX(); + } + else + xc = getFrameWidth(); + + return xc; +} + +int gContainer::containerX() +{ + GtkWidget *cont = getContainer(); + + if (cont == widget && widget == frame) + return getFrameWidth(); + else + return 0; +} + +int gContainer::clientY() +{ + gint xc, yc; + GtkWidget *cont = getContainer(); + + if (_client_y >= 0) + return _client_y; + + if (!_scroll && gtk_widget_get_window(cont) && gtk_widget_get_window(border)) + { + gtk_widget_translate_coordinates(cont, border, 0, 0, &xc, &yc); + yc += containerY(); + } + else + yc = getFrameWidth(); + + return yc; +} + +#if 0 +int gContainer::clientY() +{ + gint xc, yc; + GtkWidget *cont = getContainer(); + + if (!cont->window || !border->window || cont == widget) + return getFrameWidth(); + +// if (width() != border->allocation.width || height() != border->allocation.height) +// { +// updateGeometry(); +// GtkAllocation a = { x(), y(), width(), height() }; +// gtk_widget_size_allocate(widget, &a); +// } + + gtk_widget_translate_coordinates(cont, border, 0, 0, &xc, &yc); + if (cont == widget) + yc += getFrameWidth(); + + return yc; // + getFrameWidth(); +} +#endif + +int gContainer::containerY() +{ + GtkWidget *cont = getContainer(); + + if (cont == widget && widget == frame) + return getFrameWidth(); + else + return 0; +} + +int gContainer::clientWidth() +{ + GtkWidget *cont = getContainer(); + + if (_client_w > 0) + return _client_w; + + if (cont != widget && gtk_widget_get_window(cont)) + { + GtkAllocation a; + + gtk_widget_get_allocation(widget, &a); + + if ((width() != a.width || height() != a.height) + && a.width > 0 && a.height > 0) + { + //g_debug("clientWidth: %s: %d", name(), width()); + a.width = width(); a.height = height(); + gt_disable_warnings(true); + gtk_widget_size_allocate(widget, &a); + gt_disable_warnings(false); + } + //g_debug("ClientWidth: %s -> %d", this->name(), cont->allocation.width); + + gtk_widget_get_allocation(cont, &a); + + if (a.width > 0) + return a.width; + } + + if (_scroll) + return (int)gtk_adjustment_get_page_size(gtk_scrolled_window_get_hadjustment(_scroll)); + + return width() - getFrameWidth() * 2; +} + +int gContainer::containerWidth() +{ + return clientWidth(); +} + +int gContainer::clientHeight() +{ + GtkWidget *cont = getContainer(); + + if (_client_h > 0) + return _client_h; + + if (cont != widget && gtk_widget_get_window(cont)) + { + GtkAllocation a; + + gtk_widget_get_allocation(widget, &a); + + if ((width() != a.width || height() != a.height) + && a.width > 0 && a.height > 0) + { + //g_debug("clientHeight: %s: %d", name(), height()); + a.width = width(); a.height = height(); + //gt_disable_warnings(true); + gtk_widget_size_allocate(widget, &a); + //gt_disable_warnings(false); + //gtk_container_resize_children(GTK_CONTAINER(widget)); + } + //g_debug("ClientHeight: %s -> %d", this->name(), cont->allocation.height); + gtk_widget_get_allocation(cont, &a); + + if (a.height > 0) + return a.height; + } + + if (_scroll) + return (int)gtk_adjustment_get_page_size(gtk_scrolled_window_get_vadjustment(_scroll)); + + return height() - getFrameWidth() * 2; +} + +int gContainer::containerHeight() +{ + return clientHeight(); +} + +void gContainer::insert(gControl *child, bool realize) +{ + //fprintf(stderr, "insert %s into %s\n", child->name(), name()); + + if (!gtk_widget_get_parent(child->border)) + { + //gtk_layout_put(GTK_LAYOUT(getContainer()), child->border, 0, 0); + gtk_container_add(GTK_CONTAINER(getContainer()), child->border); + } + child->bufX = child->bufY = 0; + + g_ptr_array_add(_children, child); + + if (realize) + child->_visible = true; + + //g_debug("gContainer::insert: visible = %d", isReallyVisible()); + if (!realize) + performArrange(); + //fprintf(stderr, "--> %d %d %d %d\n", child->x(), child->y(), child->width(), child->height()); + + if (realize) + { + //gtk_widget_realize(child->border); + //gtk_widget_show(child->border); + if (child->frame) + gtk_widget_show(child->frame); + if (child->widget != child->border) + gtk_widget_show(child->widget); + } + +#ifndef GTK3 + if (hasBackground() && !child->_bg_set) child->setBackground(); + if (hasForeground() && !child->_fg_set) child->setForeground(); +#endif + child->updateFont(); + + if ((isUser() && isDesign()) || isDesignIgnore()) + child->setDesign(true); + + child->updateDirection(); +} + +void gContainer::remove(gControl *child) +{ + g_ptr_array_remove(_children, child); +} + + +gControl *gContainer::find(int x, int y) +{ + int i; + gControl *ch; + + //fprintf(stderr, "gContainer::find: %s (C %d %d %d %d) (S %d %d): %d %d\n", name(), clientX(), clientY(), clientWidth(), clientHeight(), scrollX(), scrollY(), x, y); + + x -= clientX(); + y -= clientY(); + + if (gApplication::_button_grab != this) + { + if (x < 0 || y < 0 || x >= clientWidth() || y >= clientHeight()) + return NULL; + } + + if (_scroll) + { + x += scrollX(); + y += scrollY(); + } + + for (i = childCount() - 1; i >= 0; i--) + { + ch = child(i); + //fprintf(stderr, "test: %s %d: %d %d %d %d / %d %d\n", ch->name(), ch->isVisible(), ch->x(), ch->y(), ch->width(), ch->height(), x, y); + if (ch->isVisible() && x >= ch->left() && y >= ch->top() && x < (ch->left() + ch->width()) && y < (ch->top() + ch->height())) + { + //fprintf(stderr, "--> %s\n", ch->name()); + return ch; + } + } + + //fprintf(stderr, "--> NULL\n"); + return NULL; +} + + +#ifndef GTK3 +void gContainer::setBackground(gColor color) +{ + int i; + gControl *ch; + + gControl::setBackground(color); + + for (i = 0; i < childCount(); i++) + { + ch = gContainer::child(i); + if (!ch->_bg_set) + ch->setBackground(); + } +} +#endif + +/*#ifdef GTK3 +void gContainer::updateColor() +{ + int i; + + for (i = 0; i < childCount(); i++) + gContainer::child(i)->updateColor(); +} +#endif*/ + +void gContainer::setForeground(gColor color) +{ + int i; + gControl *ch; + + gControl::setForeground(color); + + for (i = 0; i < childCount(); i++) + { + ch = gContainer::child(i); + if (!ch->_fg_set) + ch->setForeground(); + } +} + +GtkWidget *gContainer::getContainer() +{ + return widget; +} + +void gContainer::borderSignals() +{ + gControl::borderSignals(); + + g_signal_connect(G_OBJECT(border), "map", G_CALLBACK(cb_map), (gpointer)this); + g_signal_connect(G_OBJECT(border), "unmap", G_CALLBACK(cb_unmap), (gpointer)this); +#ifdef GTK3 + g_signal_connect_after(G_OBJECT(border), "map-event", G_CALLBACK(cb_remap_children), (gpointer)this); +#endif +} + +bool gContainer::resize(int w, int h, bool no_decide) +{ + if (gControl::resize(w, h, no_decide)) + return true; + + _client_w = 0; + _client_h = 0; + + if (arrangement.paint) + CUSERCONTROL_cb_resize(this); + + performArrange(); + return false; +} + +void gContainer::setVisible(bool vl) +{ + //bool arr; + + if (vl == isVisible()) + return; + + gControl::setVisible(vl); + + /*if (arr) + performArrange();*/ +} + +/*void gContainer::updateFocusChain() +{ + GList *chain = NULL; + int i; + gControl *ch; + + for (i = 0; i < childCount(); i++) + { + ch = child(i); + if (ch->isNoTabFocus()) + continue; + chain = g_list_prepend(chain, ch->border); + } + + chain = g_list_reverse(chain); + + gtk_container_set_focus_chain(GTK_CONTAINER(widget), chain); + + g_list_free(chain); +}*/ + +void gContainer::updateFont() +{ + int i; + + gControl::updateFont(); + + for (i = 0; i < childCount(); i++) + child(i)->updateFont(); + + if (arrangement.paint) + CUSERCONTROL_cb_font(this); +} + +void gContainer::moveChild(gControl *child, int x, int y) +{ + GtkWidget *cont = gtk_widget_get_parent(child->border); //getContainer(); + + if (GTK_IS_LAYOUT(cont)) + gtk_layout_move(GTK_LAYOUT(cont), child->border, x, y); + else + gtk_fixed_move(GTK_FIXED(cont), child->border, x, y); +} + +bool gContainer::hasBackground() const +{ + return _bg_set || (parent() && parent()->hasBackground()); +} + +bool gContainer::hasForeground() const +{ + return _fg_set || (parent() && parent()->hasForeground()); +} + +void gContainer::setFullArrangement(gContainerArrangement *arr) +{ + bool locked = arrangement.locked; + + arrangement = *arr; + arrangement.locked = locked; + performArrange(); +} + +void gContainer::disableArrangement() +{ + if (_no_arrangement == 0) + _did_arrangement = false; + + _no_arrangement++; +} + +void gContainer::enableArrangement() +{ + _no_arrangement--; + if (_no_arrangement == 0 && _did_arrangement) + performArrange(); +} + +void gContainer::hideHiddenChildren() +{ + int i; + gControl *child; + + for (i = 0;; i++) + { + child = gContainer::child(i); + if (!child) + break; + if (!child->isVisible()) + gtk_widget_hide(child->border); + else if (child->isContainer()) + ((gContainer *)child)->hideHiddenChildren(); + } +} + +void gContainer::reparent(gContainer *newpr, int x, int y) +{ + gControl::reparent(newpr, x, y); + hideHiddenChildren(); +} + + +void gContainer::clear() +{ + gContainer *cont = proxyContainer(); + gControl *ch; + + for(;;) + { + ch = cont->child(0); + if (!ch) + break; + ch->destroy(); + } +} + +void gContainer::updateDesignChildren() +{ + int i; + gContainer *cont; + + if (!isDesign()) + return; + + if (!isUser() && !isDesignIgnore()) + return; + + if (isUserContainer() && !_proxyContainer) + return; + + cont = isDesignIgnore() ? this : proxyContainer(); + + for (i = 0; i < cont->childCount(); i++) + cont->child(i)->setDesign(true); +} + +void gContainer::setDesign(bool ignore) +{ + if (isDesign()) + return; + + gControl::setDesign(ignore); + updateDesignChildren(); +} + +void gContainer::setProxyContainer(gContainer *proxy) +{ + if (_proxyContainer != this) + _proxyContainer = proxy; + else + _proxyContainer = NULL; + + updateDesignChildren(); +} + +void gContainer::resetArrangeLater() +{ + if (_arrange_later) + { + //fprintf(stderr, "resetArrangeLater: %p / %p '%s'\n", this, hFree, name()); + + _arrange_later = false; + _arrange_list = g_list_remove(_arrange_list, this); + } +} + +void gContainer::arrangeLater() +{ + if (!_arrange_later) + { + //fprintf(stderr, "arrangeLater: %p / %p '%s'\n", this, hFree, name()); + + _arrange_later = true; + _arrange_list = g_list_prepend(_arrange_list, (gpointer)this); + } +} + +void gContainer::postArrange() +{ + GList *iter; + gContainer *cont; + + if (!_arrange_list) return; + + for(;;) + { + iter = g_list_first(_arrange_list); + if (!iter) + break; + cont = (gContainer *)iter->data; + cont->resetArrangeLater(); + cont->performArrange(); + } + + g_list_free(_arrange_list); + _arrange_list = NULL; +} + +void gContainer::updateDirection() +{ + int i; + gControl *child; + + gControl::updateDirection(); + + for (i = 0;; i++) + { + child = gContainer::child(i); + if (!child) + break; + child->updateDirection(); + } + + performArrange(); +} diff --git a/gb.gtk/src/gcontainer.h b/gb.gtk/src/gcontainer.h new file mode 100644 index 00000000..0990805d --- /dev/null +++ b/gb.gtk/src/gcontainer.h @@ -0,0 +1,187 @@ +/*************************************************************************** + + gcontainer.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCONTAINER_H +#define __GCONTAINER_H + +#include "gcontrol.h" + +#ifdef GTK3 +void CUSERCONTROL_cb_draw(gContainer *sender, cairo_t *cr); +#else +void CUSERCONTROL_cb_draw(gContainer *sender, GdkRegion *region, int dx, int dy); +#endif +void CUSERCONTROL_cb_font(gContainer *sender); +void CUSERCONTROL_cb_resize(gContainer *sender); + +struct gContainerArrangement +{ + unsigned mode : 4; + unsigned user : 1; + unsigned locked : 1; + unsigned margin : 1; + unsigned spacing : 1; + unsigned padding : 8; + unsigned indent : 1; + unsigned centered : 1; + unsigned dirty : 1; + unsigned autoresize : 1; + unsigned invert : 1; + unsigned paint : 1; + unsigned _reserved: 10; +}; + +class gContainer : public gControl +{ +public: + gContainer(); + gContainer(gContainer *parent); + virtual ~gContainer(); + + int arrange() const { return arrangement.mode; } + bool autoResize() const { return arrangement.autoresize; } + bool isUser() const { return arrangement.user; } + int padding() const { return arrangement.padding; } + bool spacing() const { return arrangement.spacing; } + bool margin() const { return arrangement.margin; } + bool indent() const { return arrangement.indent; } + bool invert() const { return arrangement.invert; } + bool centered() const { return arrangement.centered; } + bool isPaint() const { return arrangement.paint; } + bool isArranging() const { return arrangement.locked; } + + virtual int clientWidth(); + virtual int clientHeight(); + virtual int clientX(); + virtual int clientY(); + virtual int containerX(); + virtual int containerY(); + virtual int containerWidth(); + virtual int containerHeight(); + + void setArrange(int vl); + void setUser(); + void setPaint(); + void setAutoResize(bool vl); + void setPadding(int vl); + void setSpacing(bool vl); + void setMargin(bool vl); + void setIndent(bool vl); + void setInvert(bool vl); + void setCentered(bool vl); + + void setUserContainer() { _user_container = true; } + bool isUserContainer() const { return _user_container; } + + virtual int childCount() const; + virtual gControl *child(int index) const; + gControl *firstChild() const { return child(0); }; + gControl *lastChild() const { return child(childCount() - 1); } + + int childIndex(gControl *ch) const; + + int childRec(int index) const; + int childCountRec(int index) const; + + void clear(); + + virtual gControl *find(int x, int y); + + gContainerArrangement *getArrangement() { return &arrangement; } + gContainerArrangement fullArrangement() { return arrangement; } + void setFullArrangement(gContainerArrangement *arr); + + virtual void performArrange(); + void decide(gControl *control, bool *width, bool *height); + void getMaxSize(int xc, int yc, int wc, int hc, int *w, int *h); + +#ifndef GTK3 + virtual void setBackground(gColor color = COLOR_DEFAULT); +#endif + virtual void setForeground(gColor color = COLOR_DEFAULT); + virtual void updateFont(); + + bool hasBackground() const; + bool hasForeground() const; + + virtual bool resize(int w, int h, bool no_decide = false); + + virtual void setVisible(bool vl); + + gContainer *proxyContainer() { return _proxyContainer ? _proxyContainer : this; } + void setProxyContainer(gContainer *proxy); + gContainer *proxyContainerFor() { return _proxyContainerFor; } + void setProxyContainerFor(gContainer *proxy) { if (proxy != this) _proxyContainerFor = proxy; else _proxyContainerFor = NULL; } + + virtual void setDesign(bool ignore = false); + + void disableArrangement(); + void enableArrangement(); + bool isArrangementEnabled() const { return _no_arrangement == 0; } + +//"Private" + GtkWidget *radiogroup; + GPtrArray *_children; + int _client_x, _client_y, _client_w, _client_h; + + virtual void insert(gControl *child, bool realize = false); + virtual void remove(gControl *child); + virtual void moveChild(gControl *child, int x, int y); + virtual void reparent(gContainer *newpr, int x, int y); + void hideHiddenChildren(); + virtual GtkWidget *getContainer(); + + void setShown(bool v) { _shown = v; } + bool isShown() const { return _shown; } + void arrangeLater(); + void resetArrangeLater(); + + virtual void borderSignals(); + + virtual void updateDirection(); + + static void postArrange(); + +private: + + void initialize(); + void updateDesignChildren(); + + gContainerArrangement arrangement; + gContainer *_proxyContainer; + gContainer *_proxyContainerFor; + unsigned _did_arrangement : 1; + unsigned _user_container : 1; + unsigned _shown : 1; + unsigned _arrange_later : 1; + unsigned char _no_arrangement; +}; + +// Callbacks +//"Signals" +void CB_container_arrange(gContainer *sender); +void CB_container_before_arrange(gContainer *sender); + + + +#endif diff --git a/gb.gtk/src/gcontrol.cpp b/gb.gtk/src/gcontrol.cpp new file mode 100644 index 00000000..ebb470e5 --- /dev/null +++ b/gb.gtk/src/gcontrol.cpp @@ -0,0 +1,3045 @@ +/*************************************************************************** + + gcontrol.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include <unistd.h> + +#include "widgets.h" +#include "gapplication.h" +#include "gbutton.h" +#include "gdrawingarea.h" +#include "gmainwindow.h" +#include "gscrollbar.h" +#include "gslider.h" +#include "gdesktop.h" +#include "gdrag.h" +#include "gmouse.h" +#include "gmenu.h" + +#ifndef GTK3 +#include "gplugin.h" +#endif + +#include "gcontrol.h" + +//#define DEBUG_FOCUS 1 +//#define DEBUG_ENTER_LEAVE 1 +//#define DEBUG_DESTROY 1 + +static GList *_destroy_list = NULL; + +#if 0 +static const char *_cursor_fdiag[] = +{ +"16 16 4 1", +"# c None", +"a c #000000", +"b c #c0c0c0", +". c #ffffff", +"..........######", +".aaaaaaaa.######", +".a....ba.#######", +".a...ba.########", +".a....ab.#######", +".a.b...ab.######", +".abaa...ab.###..", +".aa.ba...ab.#.a.", +".a.#.ba...ab.aa.", +"..###.ba...aaba.", +"######.ba...b.a.", +"#######.ba....a.", +"########.ab...a.", +"#######.ab....a.", +"######.aaaaaaaa.", +"######.........." +}; + +static const char *_cursor_bdiag[] = +{ +"16 16 4 1", +". c None", +"a c #000000", +"b c #c0c0c0", +"# c #ffffff", +"......##########", +"......#aaaaaaaa#", +".......#ab####a#", +"........#ab###a#", +".......#ba####a#", +"......#ba###b#a#", +"##...#ba###aaba#", +"#a#.#ba###ab#aa#", +"#aa#ba###ab#.#a#", +"#abaa###ab#...##", +"#a#b###ab#......", +"#a####ab#.......", +"#a###ba#........", +"#a####ba#.......", +"#aaaaaaaa#......", +"##########......" +}; +#endif + +// Geometry optimization hack - Sometimes fails, so it is disabled... +#define GEOMETRY_OPTIMIZATION 0 + +// Private structures that allow to implement raise() and lower() methods + +#ifdef GTK3 + +struct _GtkLayoutPrivate +{ + /* Properties */ + guint width; + guint height; + + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + /* GtkScrollablePolicy needs to be checked when + * driving the scrollable adjustment values */ + guint hscroll_policy : 1; + guint vscroll_policy : 1; + + /* Properties */ + + GdkVisibilityState visibility; + GdkWindow *bin_window; + + GList *children; + + gint scroll_x; + gint scroll_y; + + guint freeze_count; +}; + +struct _GtkFixedPrivate +{ + GList *children; +}; + +typedef struct _GtkLayoutChild GtkLayoutChild; + +struct _GtkLayoutChild { + GtkWidget *widget; + gint x; + gint y; +}; + +#define GET_CHILDREN_LIST(_widget) + +#else + +typedef struct _GtkLayoutChild GtkLayoutChild; + +struct _GtkLayoutChild { + GtkWidget *widget; + gint x; + gint y; +}; + +#endif + + +#ifdef GTK3 +static gboolean cb_background_draw(GtkWidget *wid, cairo_t *cr, gControl *control) +{ + control->drawBackground(cr); + return false; +} +#else +static gboolean cb_background_expose(GtkWidget *wid, GdkEventExpose *e, gControl *control) +{ + control->drawBackground(e); + return false; +} +#endif + +#ifdef GTK3 +static gboolean cb_frame_draw(GtkWidget *wid, cairo_t *cr, gControl *control) +{ + control->drawBorder(cr); + return false; +} +#else +static gboolean cb_frame_expose(GtkWidget *wid, GdkEventExpose *e, gControl *control) +{ + control->drawBorder(e); + return false; +} +#endif + +#ifndef GTK3 + +/**************************************************************************** + +gPlugin + +****************************************************************************/ + +gboolean gPlugin_OnUnplug(GtkSocket *socket,gPlugin *data) +{ + if (data->onUnplug) data->onUnplug(data); + return true; +} + +void gPlugin_OnPlug(GtkSocket *socket,gPlugin *data) +{ + if (data->onPlug) data->onPlug(data); +} + + +gPlugin::gPlugin(gContainer *parent) : gControl(parent) +{ + border = gtk_socket_new(); + widget = border; + realize(); + + onPlug = NULL; + onUnplug = NULL; + + g_signal_connect(G_OBJECT(widget), "plug-removed", G_CALLBACK(gPlugin_OnUnplug), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "plug-added", G_CALLBACK(gPlugin_OnPlug), (gpointer)this); + + ON_DRAW_BEFORE(border, this, cb_background_expose, cb_background_draw); + + setCanFocus(true); +} + +int gPlugin::client() +{ + //GdkNativeWindow win = gtk_socket_get_id(GTK_SOCKET(widget)); + //return (long)win; + GdkWindow *win = gtk_socket_get_plug_window(GTK_SOCKET(widget)); + if (!win) + return 0; + else + return (int)GDK_WINDOW_XID(win); +} + +void gPlugin::plug(int id) +{ + void (*func)(gControl *); + int i; + Display *d = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + + func = onPlug; + onPlug = NULL; + + for (i = 1; i >= 0; i--) + { + if (i == 0) + onPlug = func; + + gtk_socket_add_id(GTK_SOCKET(widget), (Window)id); + } + + if (client()) + XAddToSaveSet(d, client()); + else + emit(SIGNAL(onError)); +} + +void gPlugin::discard() +{ + #ifdef GDK_WINDOWING_X11 + if (MAIN_display_x11) + { + Display *d = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + + if (!client()) return; + + XRemoveFromSaveSet(d, client()); + XReparentWindow(d, client(), GDK_ROOT_WINDOW(), 0, 0); + } + #else + stub("no-X11/gPlugin:discard()"); + #endif +} +#endif + +/***************************************************************** + +CREATION AND DESTRUCTION + +******************************************************************/ + +void gControl::postDelete() +{ + GList *iter; + gControl *control; + + gMenu::cleanRemovedMenus(); + + if (!_destroy_list) return; + + for(;;) + { + iter = g_list_first(_destroy_list); + if (!iter) + break; + control = (gControl *)iter->data; +#if DEBUG_DESTROY + fprintf(stderr, "postDelete: %p %s\n", control, control->name()); +#endif + gtk_widget_destroy(control->border); + } + + _destroy_list = NULL; +} + +void gControl::initAll(gContainer *parent) +{ + bufW = 1; + bufH = 1; + bufX = -16; + bufY = -16; + + _min_w = _min_h = 1; + _minimum_size_set = FALSE; + curs = NULL; + _font = NULL; + _resolved_font = NULL; + _design = false; + _design_ignore = false; + _no_design = false; + _expand = false; + _ignore = false; + _inverted = false; + _accept_drops = false; + _dragging = false; + frame_border = 0; + frame_padding = 0; + _bg_set = false; + _fg_set = false; + have_cursor = false; + use_base = false; + _mouse = CURSOR_DEFAULT; + pr = parent; + _name = NULL; + _visible = false; + _locked = 0; + _destroyed = false; + _no_delete = false; + _action = false; + _dirty_pos = _dirty_size = false; + _tracking = false; + _has_input_method = false; + _no_default_mouse_event = false; + _proxy = _proxy_for = NULL; + _no_tab_focus = false; + _inside = false; + _no_auto_grab = false; + _no_background = false; + _use_wheel = false; + _scrollbar = SCROLL_NONE; + _input_method = NULL; + _tooltip = NULL; + _is_container = false; + _is_window = false; + _is_button = false; + _is_drawingarea = false; + _has_native_popup = false; + _eat_return_key = false; + //_hidden_temp = false; + _allow_show = false; + _direction = DIRECTION_DEFAULT; + + frame = border = widget = NULL; + _scroll = NULL; + hFree = NULL; + _grab = false; + + _fg = _bg = COLOR_DEFAULT; +#ifdef GTK3 + _css = NULL; + _has_css_id = false; + _style_dirty = false; + _no_style_without_child = false; +#endif +} + +gControl::gControl() +{ + initAll(NULL); +} + +gControl::gControl(gContainer *parent) +{ + initAll(parent); +} + +void gControl::dispose() +{ + gMainWindow *win; + + win = window(); + if (win && win->_initial_focus == this) + win->_initial_focus = NULL; + + win = gMainWindow::_current; + while (win) + { + if (win && win->_save_focus == this) + win->_save_focus = NULL; + win = win->_previous; + } + + if (pr) + { + pr->remove(this); + pr = NULL; + } +} + +gControl::~gControl() +{ + //fprintf(stderr, "~gControl: %s %p %s\n", name(), this, GB.GetClassName(hFree)); + CB_control_finish(this); + + dispose(); + + if (_proxy) + _proxy->_proxy_for = NULL; + if (_proxy_for) + _proxy_for->_proxy = NULL; + + if (gDrag::getSource() == this) + gDrag::cancel(); + + if (curs) + { + delete curs; + curs=NULL; + } + + if (_font) + { + gFont::assign(&_font); + gFont::assign(&_resolved_font); + } + +#ifdef GTK3 + if (_css) + g_object_unref(_css); +#endif + + if (_name) + g_free(_name); + if (_tooltip) + g_free(_tooltip); + +#if DEBUG_DESTROY + fprintf(stderr, "remove from destroy list: %p (%d)\n", this, _destroyed); +#endif + _destroy_list = g_list_remove(_destroy_list, this); + + #define CLEAN_POINTER(_p) if (_p == this) _p = NULL + + CLEAN_POINTER(gApplication::_enter); + CLEAN_POINTER(gApplication::_leave); + CLEAN_POINTER(gApplication::_active_control); + CLEAN_POINTER(gApplication::_previous_control); + CLEAN_POINTER(gApplication::_old_active_control); + CLEAN_POINTER(gApplication::_button_grab); + CLEAN_POINTER(gApplication::_enter_after_button_grab); + CLEAN_POINTER(gApplication::_control_grab); + CLEAN_POINTER(gApplication::_ignore_until_next_enter); + CLEAN_POINTER(gDrag::_destination); + CLEAN_POINTER(gDrag::_source); + CLEAN_POINTER(gDrag::_current); + CLEAN_POINTER(gMouse::_control); +} + +void gControl::destroy() +{ + if (_destroyed) + return; + +#if DEBUG_DESTROY + fprintf(stderr, "destroy: %p %s (%d)\n", this, name(), _destroyed); +#endif + hide(); + _destroyed = true; + dispose(); + +#if DEBUG_DESTROY + fprintf(stderr, "added to destroy list: %p %s (%d)\n", this, name(), _destroyed); + if (g_list_find(_destroy_list, this)) + { + fprintf(stderr, "already present!!!\n"); + BREAKPOINT(); + } +#endif + + _destroy_list = g_list_prepend(_destroy_list, (gpointer)this); +} + + +bool gControl::isEnabled() const +{ + return gtk_widget_is_sensitive(border); +} + +bool gControl::isReallyVisible() +{ + if (!isTopLevel() && !topLevel()->isReallyVisible()) + return false; + +#if GTK_CHECK_VERSION(2, 20, 0) + return gtk_widget_get_mapped(border); +#else + return GTK_WIDGET_MAPPED(border); +#endif +} + +void gControl::setEnabled(bool vl) +{ + gtk_widget_set_sensitive(border, vl); +} + +void gControl::setVisibility(bool vl) +{ + _visible = vl; + + if (!_allow_show) + return; + + if (vl == gtk_widget_get_visible(border)) + return; + + if (vl) + { + if (bufW >= minimumWidth() && bufH >= minimumHeight()) + { + gtk_widget_show(border); + _dirty_size = true; + updateGeometry(); + #ifdef GTK3 + updateStyleSheet(false); + #endif + } + } + else + { + if (hasFocus()) + { + if (parent()) + gcb_focus(widget, GTK_DIR_TAB_FORWARD, this); + gApplication::setActiveControl(this, false); + } + if (gtk_widget_has_grab(border)) + gtk_grab_remove(border); + gtk_widget_hide(border); + } + + if (!isIgnore() && pr) pr->performArrange(); +} + +void gControl::setVisible(bool vl) +{ + setVisibility(vl); + checkVisibility(); +} + +/***************************************************************** + +POSITION AND SIZE + +******************************************************************/ + +bool gControl::getScreenPos(int *x, int *y) +{ + if (!gtk_widget_get_window(border)) + { + if (pr) + { + pr->getScreenPos(x, y); + x += pr->clientX() + bufX; + y += pr->clientY() + bufY; + return false; + } + + *x = *y = 0; // widget is not realized + return true; + } + + gdk_window_get_origin(gtk_widget_get_window(border), x, y); + + //fprintf(stderr, "getScreenPos: %s: %d %d: %d\n", name(), *x, *y, gtk_widget_get_has_window(border)); + + #if GTK_CHECK_VERSION(2, 18, 0) + if (!gtk_widget_get_has_window(border)) + { + GtkAllocation a; + gtk_widget_get_allocation(border, &a); + *x += a.x; + *y += a.y; + } + #endif + + //fprintf(stderr, "getScreenPos: --> %d %d\n", *x, *y); + return false; +} + +int gControl::screenX() +{ + if (isTopLevel()) + { + GdkWindow *window = gtk_widget_get_window(border); + int x = 0; + GtkAllocation a; + + if (window) + gdk_window_get_origin(window, &x, NULL); + + gtk_widget_get_allocation(widget, &a); + + return x + a.x - ((gContainer *)this)->clientX(); + } + + return pr->screenX() + x() - pr->clientX() - pr->scrollX(); +} + +int gControl::screenY() +{ + if (isTopLevel()) + { + GdkWindow *window = gtk_widget_get_window(border); + int y = 0; + GtkAllocation a; + + if (window) + gdk_window_get_origin(window, NULL, &y); + + gtk_widget_get_allocation(widget, &a); + + return y + a.y - ((gContainer *)this)->clientY(); + } + + return pr->screenY() + y() + pr->clientY() - pr->scrollY(); +} + +static void send_configure(gControl *control) +{ + GtkWidget *widget; + GdkEvent *event; + + widget = control->border; + + if (!gtk_widget_get_realized(widget)) + return; + +// if (control->isWindow()) +// g_debug("send configure to window: %s", control->name()); + + event = gdk_event_new(GDK_CONFIGURE); + + event->configure.window = NULL; //(GdkWindow *)g_object_ref(widget->window); + event->configure.send_event = TRUE; + event->configure.x = control->x(); + event->configure.y = control->y(); + event->configure.width = control->width(); + event->configure.height = control->height(); + + gtk_widget_event(widget, event); + gdk_event_free(event); +} + +void gControl::move(int x, int y) +{ + //GtkLayout *fx; + + if (x == bufX && y == bufY) + return; + + bufX = x; + bufY = y; + + //g_debug("move: %p: %d %d", this, x, y); + _dirty_pos = true; + if (pr && !isIgnore()) + { + // TODO: check the following optimization to see if it can be enabled again + //if (gtk_widget_get_parent(border) == pr->getContainer()) + pr->performArrange(); + } + + #if GEOMETRY_OPTIMIZATION + gApplication::setDirty(); + #else + updateGeometry(); + #endif + + checkVisibility(); + + send_configure(this); // needed for Watcher and Form Move events +} + +void gControl::hideButKeepFocus() +{ + //fprintf(stderr, "gControl::hideButKeepFocus: %s\n", gApplication::_active_control ? gApplication::_active_control->name() : "NULL"); + + if (gtk_widget_get_visible(border)) + { + gApplication::_keep_focus = true; + gApplication::_disable_mapping_events = true; + gtk_widget_hide(border); + gApplication::_disable_mapping_events = false; + gApplication::_keep_focus = false; + } +} + +void gControl::showButKeepFocus() +{ + gControl *focus; + + //fprintf(stderr, "gControl::showButKeepFocus: %s\n", gApplication::_active_control ? gApplication::_active_control->name() : "NULL"); + + if (_allow_show && !gtk_widget_get_visible(border)) + { + gApplication::_disable_mapping_events = true; + gtk_widget_show(border); + gApplication::_disable_mapping_events = false; + } + + focus = gApplication::_active_control; + gApplication::_active_control = NULL; + if (focus && !focus->hasFocus()) + focus->setFocus(); + gApplication::_active_control = focus; + + //_hidden_temp = false; +} + +bool gControl::resize(int w, int h, bool no_decide) +{ + bool decide_w, decide_h; + + if (w < 0 && h < 0) + return true; + + if (pr && !no_decide) + { + pr->decide(this, &decide_w, &decide_h); + + if (w < 0 || decide_w) + w = width(); + + if (h < 0 || decide_h) + h = height(); + } + + if (w < 0) w = 0; + if (h < 0) h = 0; + + if (width() == w && height() == h) + return true; + + bufW = w; + bufH = h; + + if (w < minimumWidth() || h < minimumHeight()) + { + hideButKeepFocus(); + } + else + { + //g_debug("resize: %p %s: %d %d", this, name(), w, h); + _dirty_size = true; + + #if GEOMETRY_OPTIMIZATION + gApplication::setDirty(); + #else + updateGeometry(); + #endif + + if (isVisible() && !isReallyVisible()) + { + showButKeepFocus(); +#ifdef GTK3 + updateStyleSheet(false); +#endif + } + } + + checkVisibility(); + + if (pr && !isIgnore()) + pr->performArrange(); + + send_configure(this); // needed for Watcher and Form Resize events + return false; +} + +void gControl::moveResize(int x, int y, int w, int h, bool no_decide) +{ + if (pr) + pr->disableArrangement(); + + move(x, y); + resize(w, h, no_decide); + + if (pr) + pr->enableArrangement(); +} + +void gControl::updateGeometry(bool force) +{ +// if (_dirty_pos) +// { +// g_debug("move: %p -> %d %d", this, x(), y()); +// _dirty_pos = false; +// GtkLayout *fx = GTK_LAYOUT(gtk_widget_get_parent(border)); +// gtk_layout_move(fx, border, x(), y()); +// } +// +// if (_dirty_size) +// { +// GtkAllocation a = { x(), y(), width(), height() }; +// g_debug("resize: %p -> %d %d", this, width(), height()); +// _dirty_size = false; +// //gtk_widget_set_size_request(border, width(), height()); +// gtk_widget_size_allocate(border, +// } + if (force || _dirty_pos || _dirty_size) + { + //g_debug("move-resize: %s: %d %d %d %d", this->name(), x(), y(), width(), height()); + if (force || _dirty_pos) + { + if (pr) + pr->moveChild(this, x(), y()); + + _dirty_pos = false; + } + if ((force || _dirty_size) && isVisible()) + { + gtk_widget_set_size_request(border, width(), height()); + _dirty_size = false; + } + } +} + +/***************************************************************** + +APPEARANCE + +******************************************************************/ + + +void gControl::setExpand(bool vl) +{ + if (vl == _expand) + return; + + _expand = vl; + checkVisibility(); + + if (pr && !_ignore) + pr->performArrange(); +} + +void gControl::setIgnore(bool vl) +{ + if (vl == _ignore) + return; + + _ignore = vl; + if (pr) + pr->performArrange(); +} + +void gControl::setTooltip(char *vl) +{ + char *pango; + + if (_tooltip) g_free(_tooltip); + _tooltip = NULL; + if (vl && *vl) _tooltip = g_strdup(vl); + + if (_tooltip) + { + pango = gt_html_to_pango_string(_tooltip, -1, true); + gtk_widget_set_tooltip_markup(border, pango); + g_free(pango); + } + else + gtk_widget_set_tooltip_markup(border, NULL); +} + +gFont* gControl::font() const +{ + if (_resolved_font) + { + //fprintf(stderr, "%s: font -> _resolved_font\n", name()); + return _resolved_font; + } + else if (pr) + { + //fprintf(stderr, "%s: font -> parent\n", name()); + return pr->font(); + } + else + { + //fprintf(stderr, "%s: font -> desktop\n", name()); + return gDesktop::font(); + } +} + +void gControl::actualFontTo(gFont *ft) +{ + font()->copyTo(ft); +} + +void gControl::resolveFont() +{ + gFont *font; + + if (_font) + { + font = new gFont(); + font->mergeFrom(_font); + if (pr) + font->mergeFrom(pr->font()); + else + font->mergeFrom(gDesktop::font()); + + gFont::set(&_resolved_font, font); + } + else + gFont::assign(&_resolved_font); +} + +void gControl::setFont(gFont *ft) +{ + //fprintf(stderr, "setFont: %s: %s\n", name(), ft->toFullString()); + + if (ft) + { + if (_font && _font->equals(ft)) + return; + + gFont::assign(&_font, ft); + } + else + { + if (!_font) + return; + + gFont::assign(&_font); + } + + gFont::assign(&_resolved_font); + + updateFont(); + + resize(); + + //fprintf(stderr, "--> %s: _font = %s\n", name(), _font ? _font->toFullString() : NULL); +} + +#ifdef GTK3 + +void gControl::updateFont() +{ + resolveFont(); + updateStyleSheet(true); + updateSize(); +} + +#else + +static void cb_update_font(GtkWidget *widget, gpointer data) +{ + PangoFontDescription *desc = (PangoFontDescription *)data; + gtk_widget_modify_font(widget, desc); +} + +void gControl::updateFont() +{ + resolveFont(); + gtk_widget_modify_font(widget, font()->desc()); + if (!isContainer() && GTK_IS_CONTAINER(widget)) + gtk_container_forall(GTK_CONTAINER(widget), (GtkCallback)cb_update_font, (gpointer)font()->desc()); + refresh(); + updateSize(); +} + +#endif + +void gControl::updateSize() +{ +} + +int gControl::mouse() +{ + if (_proxy) + return _proxy->mouse(); + else + return _mouse; +} + +gCursor* gControl::cursor() +{ + if (_proxy) + return _proxy->cursor(); + + if (!curs) return NULL; + return new gCursor(curs); +} + +void gControl::setCursor(gCursor *vl) +{ + if (_proxy) + { + _proxy->setCursor(vl); + return; + } + + if (curs) { delete curs; curs=NULL;} + if (!vl) + { + setMouse(CURSOR_DEFAULT); + return; + } + curs=new gCursor(vl); + setMouse(CURSOR_CUSTOM); +} + +void gControl::updateCursor(GdkCursor *cursor) +{ + if (GDK_IS_WINDOW(gtk_widget_get_window(border)) && _inside) + { + if (cursor || isWindow()) + gdk_window_set_cursor(gtk_widget_get_window(border), cursor); + + if (!cursor && parent()) + parent()->updateCursor(parent()->getGdkCursor()); + } +} + +GdkCursor *gControl::getGdkCursor() +{ + const char *name; + GdkCursor *cr = NULL; + int m = _mouse; + + if (gApplication::isBusy()) + m = CURSOR_WAIT; + + if (m == CURSOR_CUSTOM) + { + if (curs && curs->cur) + return curs->cur; + } + + if (m != CURSOR_DEFAULT) + { + switch(m) + { + case CURSOR_NONE: name = "none"; break; + case CURSOR_ARROW: name = "default"; break; + case CURSOR_HELP: name = "help"; break; + case CURSOR_POINTER: name = "pointer"; break; + case CURSOR_CONTEXT_MENU: name = "context-menu"; break; + case CURSOR_PROGRESS: name = "progress"; break; + case CURSOR_WAIT: name = "wait"; break; + case CURSOR_CELL: name = "cell"; break; + case CURSOR_CROSSHAIR: name = "crosshair"; break; + case CURSOR_TEXT: name = "text"; break; + case CURSOR_VERTICAL_TEXT: name = "vertical-text"; break; + case CURSOR_ALIAS: name = "alias"; break; + case CURSOR_COPY: name = "copy"; break; + case CURSOR_NO_DROP: name = "no-drop"; break; + case CURSOR_MOVE: name = "move"; break; + case CURSOR_NOT_ALLOWED: name = "not-allowed"; break; + case CURSOR_GRAB: name = "grab"; break; + case CURSOR_GRABBING: name = "grabbing"; break; + case CURSOR_ALL_SCROLL: name = "all-scroll"; break; + case CURSOR_COL_RESIZE: name = "col-resize"; break; + case CURSOR_ROW_RESIZE: name = "row-resize"; break; + case CURSOR_N_RESIZE: name = "n-resize"; break; + case CURSOR_E_RESIZE: name = "e-resize"; break; + case CURSOR_S_RESIZE: name = "s-resize"; break; + case CURSOR_W_RESIZE: name = "w-resize"; break; + case CURSOR_NE_RESIZE: name = "ne-resize"; break; + case CURSOR_NW_RESIZE: name = "nw-resize"; break; + case CURSOR_SW_RESIZE: name = "sw-resize"; break; + case CURSOR_SE_RESIZE: name = "se-resize"; break; + case CURSOR_EW_RESIZE: name = "ew-resize"; break; + case CURSOR_NS_RESIZE: name = "ns-resize"; break; + case CURSOR_NESW_RESIZE: name = "nesw-resize"; break; + case CURSOR_NWSE_RESIZE: name = "nwse-resize"; break; + case CURSOR_ZOOM_IN: name = "zoom-in"; break; + case CURSOR_ZOOM_OUT: name = "zoom-out"; break; + default: name = "default"; + } + + cr = gdk_cursor_new_from_name(gdk_display_get_default(), name); + if (!cr) + cr = gdk_cursor_new_for_display(gdk_display_get_default(), (GdkCursorType)m); + } + + return cr; +} + +void gControl::setMouse(int m) +{ + if (_proxy) + { + _proxy->setMouse(m); + return; + } + + //fprintf(stderr, "setMouse: %s\n", name()); + _mouse = m; + updateCursor(getGdkCursor()); +} + + + +/***************************************************************** + +HANDLES + +******************************************************************/ + +gMainWindow* gControl::window() const +{ + if (isWindow()) + return (gMainWindow *)this; + + if (!pr) + return NULL; + else + return pr->window(); +} + +gMainWindow* gControl::topLevel() const +{ + const gControl *child = this; + + while (!child->isTopLevel()) + child = child->parent(); + + return (gMainWindow *)child; +} + +long gControl::handle() +{ +#ifdef GTK3 + gtk_widget_realize(border); + GdkWindow *window = gtk_widget_get_window(border); + return PLATFORM.Window.GetId(window); +#else + if (MAIN_display_x11) + { + GdkWindow *window = gtk_widget_get_window(border); + return window ? GDK_WINDOW_XID(window) : 0; + } + else + return 0; +#endif +} + +/***************************************************************** + +MISC + +******************************************************************/ + +void gControl::refresh() +{ + //fprintf(stderr, "%s: refresh %s\n", GB.Debug.GetCurrentPosition(), name()); + gtk_widget_queue_draw(border); + if (frame != border && GTK_IS_WIDGET(frame)) + gtk_widget_queue_draw(frame); + if (widget != frame && GTK_IS_WIDGET(widget)) + gtk_widget_queue_draw(widget); + + afterRefresh(); +} + +void gControl::refresh(int x, int y, int w, int h) +{ + GtkAllocation a; + /*GdkRectangle r; + GtkAllocation a;*/ + + //gtk_widget_get_allocation(border, &a); + + if (x < 0 || y < 0 || w <= 0 || h <= 0) + { + x = y = 0; + w = width(); + h = height(); + } + + if (w <= 0 || h <= 0) + return; + + gtk_widget_get_allocation(widget, &a); + + gtk_widget_queue_draw_area(widget, x + a.x, y + a.y, w, h); + + afterRefresh(); +} + +void gControl::afterRefresh() +{ +} + +void gControl::setDesign(bool ignore) +{ + if (_design) + return; + + //fprintf(stderr, "setDesign: %s %d\n", name(), ignore); + setCanFocus(false); + setMouse(GDK_LEFT_PTR); + setTooltip(NULL); + _design = true; + _design_ignore = ignore; +} + +void gControl::updateDesign() +{ + if (!_design) + return; + + _design = false; + setDesign(_design_ignore); +} + +gControl *gControl::ignoreDesign() +{ + //fprintf(stderr, "ignoreDesign: %s", name()); + + if (!isDesignIgnore()) + return this; + + gControl *ctrl = this; + while (ctrl && ctrl->isDesignIgnore()) + ctrl = ctrl->parent(); + + //fprintf(stderr, " --> %s\n", ctrl->name()); + return ctrl; +} + +bool gControl::canFocus() const +{ + /*#if DEBUG_FOCUS + fprintf(stderr, "canFocus: %s -> %d\n", name(), gtk_widget_get_can_focus(widget)); + #endif*/ + +#if GTK_CHECK_VERSION(2, 18, 0) + return gtk_widget_get_can_focus(widget); +#else + return GTK_WIDGET_CAN_FOCUS(widget); +#endif +} + +bool gControl::canFocusOnClick() const +{ + /*if (_proxy) + return _proxy->canFocusOnClick();*/ + if (isWindow()) + return false; + if (!GTK_IS_BUTTON(widget)) + return true; + return gt_get_focus_on_click(widget); +} + +void gControl::setCanFocus(bool vl) +{ + /*#if DEBUG_FOCUS + fprintf(stderr, "setCanFocus: %s %d ?\n", name(), vl); + #endif*/ + if (isDesign() || vl == canFocus()) + return; + + /*if (_proxy) + _proxy->setCanFocus(vl); + else*/ + { + /*#if DEBUG_FOCUS + fprintf(stderr, "setCanFocus: %s %p %d\n", name(), this, vl); + #endif*/ + gtk_widget_set_can_focus(widget, vl); + } + + /*_has_input_method = vl; + + if (_input_method && !vl) + { + g_object_unref(_input_method); + _input_method = NULL; + } + else if (!_input_method && vl) + { + _input_method = gtk_im_multicontext_new(); + }*/ + +} + +void gControl::setFocus() +{ + #if DEBUG_FOCUS + fprintf(stderr, "setFocus %s ?\n", name()); + #endif + + if (_proxy) + { + _proxy->setFocus(); + return; + } + + if (!canFocus() || hasFocus()) + return; + + gMainWindow *win = window(); + + if (!win) + return; + + if (win->isVisible()) + { + gtk_widget_grab_focus(widget); + + #if DEBUG_FOCUS + fprintf(stderr, "setFocus NOW: %s %s %p -> %d\n", GB.GetClassName(hFree), name(), hFree, hasFocus()); + #endif + } + else + { + #if DEBUG_FOCUS + fprintf(stderr, "setFocus later: %s %s %p\n", GB.GetClassName(hFree), name(), hFree); + #endif + win->_initial_focus = this; + } +} + +bool gControl::hasFocus() const +{ + if (_proxy) + return _proxy->hasFocus(); + else +#if GTK_CHECK_VERSION(2, 18, 0) + return (border && gtk_widget_has_focus(border)) || (widget && gtk_widget_has_focus(widget)) || gApplication::activeControl() == this; +#else + return (border && GTK_WIDGET_HAS_FOCUS(border)) || (widget && GTK_WIDGET_HAS_FOCUS(widget)) || gApplication::activeControl() == this; +#endif +} + +#if GTK_CHECK_VERSION(3, 2, 0) +bool gControl::hasVisibleFocus() const +{ + if (_proxy) + return _proxy->hasVisibleFocus(); + else + return (border && gtk_widget_has_visible_focus(border)) || (widget && gtk_widget_has_visible_focus(widget)); +} +#endif + +gControl* gControl::next() +{ + int index; + + if (!pr) + return NULL; + + index = pr->childIndex(this); + if (index < 0 || index >= pr->childCount()) + return NULL; + else + return pr->child(index + 1); +} + +gControl* gControl::previous() +{ + int index; + + if (!pr) + return NULL; + + index = pr->childIndex(this); + if (index <= 0) + return NULL; + else + return pr->child(index - 1); +} + +static int find_child_fixed(GtkFixedChild *data, GtkWidget *widget) +{ + return !(data->widget == widget); +} + +static int find_child_layout(GtkLayoutChild *data, GtkWidget *widget) +{ + return !(data->widget == widget); +} + +static GList **get_children_list(GtkContainer *parent) +{ +#ifdef GTK3 + if (GTK_IS_LAYOUT(parent)) + return &((GtkLayout *)parent)->priv->children; + else + return &((GtkFixed *)parent)->priv->children; +#else + if (GTK_IS_LAYOUT(parent)) + return &((GtkLayout *)parent)->children; + else + return &((GtkFixed *)parent)->children; +#endif +} + +void gControl::restack(bool raise) +{ + GtkContainer *parent; + GList **children; + GList *find; + gpointer *p; + + if (!pr) + return; + + parent = GTK_CONTAINER(gtk_widget_get_parent(border)); + + //fprintf(stderr, "%s: %s -> %s (%s%s)\n", raise ? "raise" : "lower", name(), pr->name(), GTK_IS_LAYOUT(parent) ? "L" : "F", gtk_widget_get_has_window(border) ? "W" : ""); + + children = get_children_list(parent); + + if (GTK_IS_LAYOUT(parent)) + find = g_list_find_custom(*children, border, (GCompareFunc)find_child_layout); + else if (GTK_IS_FIXED(parent)) + find = g_list_find_custom(*children, border, (GCompareFunc)find_child_fixed); + else + return; + + if (_visible) + hideButKeepFocus(); + + *children = g_list_remove_link(*children, find); + if (raise) + *children = g_list_concat(*children, find); + else + *children = g_list_concat(find, *children); + + if (gtk_widget_get_has_window(border)) + { + if (raise) + gdk_window_raise(gtk_widget_get_window(border)); + else + gdk_window_lower(gtk_widget_get_window(border)); + } + + g_ptr_array_remove(pr->_children, this); + + if (raise) + { + g_ptr_array_add(pr->_children, this); + } + else + { + g_ptr_array_add(pr->_children, NULL); + p = pr->_children->pdata; + memmove(&p[1], &p[0], (pr->_children->len - 1) * sizeof(gpointer)); + p[0] = this; + } + + if (_visible) + showButKeepFocus(); + + updateGeometry(true); + pr->performArrange(); + pr->refresh(); +} + +void gControl::setNext(gControl *ctrl) +{ + GPtrArray *ch; + uint i; + + if (!ctrl) + { + raise(); + return; + } + + if (ctrl == this || isTopLevel() || ctrl->parent() != parent()) + return; + + if (gtk_widget_get_has_window(ctrl->border) && gtk_widget_get_has_window(border)) + gdk_window_restack(gtk_widget_get_window(border), gtk_widget_get_window(ctrl->border), FALSE); + + ch = pr->_children; + g_ptr_array_remove(ch, this); + g_ptr_array_add(ch, NULL); + + for (i = 0; i < ch->len; i++) + { + if (g_ptr_array_index(ch, i) == ctrl) + { + memmove(&ch->pdata[i + 1], &ch->pdata[i], (ch->len - i - 1) * sizeof(gpointer)); + ch->pdata[i] = this; + break; + } + } + + pr->performArrange(); +} + +void gControl::setPrevious(gControl *ctrl) +{ + if (!ctrl) + lower(); + else + setNext(ctrl->next()); +} + +/********************************************************************* + +Drag & Drop + +**********************************************************************/ + +/*static void print_full_name(gControl *ctrl) +{ + if (ctrl->parent()) + { + print_full_name(ctrl->parent()); + fprintf(stderr, ">"); + } + + fprintf(stderr, "%s", ctrl->name() ? ctrl->name() : "?"); +}*/ + +void gControl::updateAcceptDrops() +{ + GtkWidget *w = _scroll ? widget : border; + + if (_accept_drops) + { + gtk_drag_dest_set(w, (GtkDestDefaults)0, NULL, 0, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)); + gtk_drag_dest_set_track_motion(w, TRUE); + } + else + { + gtk_drag_dest_unset(w); + } + + /*fprintf(stderr, "updateAcceptDrops: "); + print_full_name(this); + fprintf(stderr, " -> %d\n", _accept_drops);*/ +} + +bool gControl::acceptDrops() const +{ + if (_proxy) + return _proxy->acceptDrops(); + else + return _accept_drops; +} + +void gControl::setAcceptDrops(bool vl) +{ + if (_proxy) + { + _proxy->setAcceptDrops(vl); + return; + } + + if (vl == _accept_drops) + return; + + _accept_drops = vl; + updateAcceptDrops(); +} + +/********************************************************************* + +Internal + +**********************************************************************/ + +void gControl::connectParent() +{ + if (pr) + pr->insert(this, true); + + // BM: Widget has been created, so we can set its cursor if application is busy + if (gApplication::isBusy() && mustUpdateCursor()) + setMouse(mouse()); +} + +gColor gControl::getFrameColor() +{ + return gDesktop::getColor(gDesktop::LIGHT_FOREGROUND); +} + +#ifdef GTK3 +void gControl::drawBorder(cairo_t *cr) +{ + /*if (getFrameBorder() != BORDER_NONE) + fprintf(stderr, "gControl::drawBorder: %s: %d %d\n", name(), width(), height());*/ + gt_draw_border(cr, gtk_widget_get_style_context(widget), gtk_widget_get_state_flags(widget), getFrameBorder(), getFrameColor(), 0, 0, width(), height(), use_base); +} +#else +void gControl::drawBorder(GdkEventExpose *e) +{ + GdkWindow *win; + GtkShadowType shadow; + gint x, y, w, h; + cairo_t *cr; + GtkWidget *wid; + GtkAllocation a; + + if (getFrameBorder() == BORDER_NONE) + return; + + x = 0; + y = 0; + w = width(); + h = height(); + + if (frame) + wid = frame; + else + wid = widget; + + if (GTK_IS_LAYOUT(wid)) + win = gtk_layout_get_bin_window(GTK_LAYOUT(wid)); + else + win = gtk_widget_get_window(wid); + + gtk_widget_get_allocation(wid, &a); + x = a.x; + y = a.y; + + if (w < 1 || h < 1) + return; + + switch (getFrameBorder()) + { + case BORDER_PLAIN: + + cr = gdk_cairo_create(win); + gt_cairo_draw_rect(cr, x, y, w, h, getFrameColor()); + cairo_destroy(cr); + return; + + case BORDER_SUNKEN: shadow = GTK_SHADOW_IN; break; + case BORDER_RAISED: shadow = GTK_SHADOW_OUT; break; + case BORDER_ETCHED: shadow = GTK_SHADOW_ETCHED_IN; break; + + default: + return; + } + + GdkRectangle clip; + gdk_region_get_clipbox(e->region, &clip); + GtkStyle *st = gtk_widget_get_style(widget); + if (use_base) + gtk_paint_box(st, win, GTK_STATE_NORMAL, shadow, &clip, widget, "entry", x, y, w, h); + else + gtk_paint_shadow(st, win, GTK_STATE_NORMAL, shadow, &clip, widget, NULL, x, y, w, h); +} +#endif + +/*static void cb_size_allocate(GtkWidget *wid, GtkAllocation *a, gContainer *container) +{ + if (!container->isTopLevel()) + container->performArrange(); +}*/ + + +/* + The different cases managed by gControl::realize() + + border frame widget + 0 0 W + B 0 W + 0 F W + B F W +*/ + +static void add_container(GtkWidget *parent, GtkWidget *child) +{ + GtkWidget *ch; + + for(;;) + { + if (!GTK_IS_BIN(parent)) + break; + + ch = gtk_bin_get_child(GTK_BIN(parent)); + if (!ch) + break; + + parent = ch; + } + + gtk_container_add(GTK_CONTAINER(parent), child); +} + +void gControl::registerControl() +{ + gt_register_control(border, this); +} + +#ifndef GTK3 + +static gboolean cb_clip_children(GtkWidget *wid, GdkEventExpose *e, gContainer *d) +{ + GdkRegion *me; + GtkAllocation a; + + gtk_widget_get_allocation(wid, &a); + me = gdk_region_rectangle((GdkRectangle *)&a); + + gdk_region_intersect(e->region, me); + + gdk_region_destroy(me); + + if (gdk_region_empty(e->region)) + return TRUE; + + return FALSE; +} + +#endif + +#if 0 +static gboolean cb_clip_by_parent(GtkWidget *wid, GdkEventExpose *e, gControl *d) +{ + GdkRegion *preg; + GdkRectangle prect = { 0, 0, d->parent()->width() - d->x(), d->parent()->height() - d->y() }; + + fprintf(stderr, "area = %d %d %d %d prect = %d %d %d %d\n", + e->area.x, e->area.y, e->area.width, e->area.height, + prect.x, prect.y, prect.width, prect.height); + + preg = gdk_region_rectangle(&prect); + + gdk_region_intersect(e->region, preg); + + gdk_region_destroy(preg); + + if (gdk_region_empty(e->region)) + return TRUE; + + gdk_region_get_clipbox(e->region, &prect); + e->area = prect; + fprintf(stderr, "--> %d %d %d %d\n", prect.x, prect.y, prect.width, prect.height); + + return FALSE; +} +#endif + +#ifdef GTK3 + +//fprintf(stderr, "get_preferred_width [%p %s] %p\n", klass, G_OBJECT_TYPE_NAME(widget), klass->_gtk_reserved2); +//fprintf(stderr, "get_preferred_height [%p %s] %p\n", klass, G_OBJECT_TYPE_NAME(widget), klass->_gtk_reserved3); + +//#define must_patch(_widget) (gt_get_control(_widget) != NULL) + +static bool _do_not_patch = false; + +static bool must_patch(GtkWidget *widget) +{ + GtkWidget *parent; + gControl *parent_control; + + if (_do_not_patch) + return false; + + if (gt_get_control(widget)) + { + //fprintf(stderr, "must_patch: %p -> 1\n", widget); + return true; + } + + parent = gtk_widget_get_parent(widget); + if (!parent) + { + //fprintf(stderr, "must_patch: %p -> 0\n", widget); + return false; + } + + if (GTK_IS_NOTEBOOK(parent) && GTK_IS_FIXED(widget)) + return true; + + if (GTK_IS_SCROLLED_WINDOW(parent)) + { + parent = gtk_widget_get_parent(parent); + if (!parent) + return false; + } + + if (GTK_IS_ENTRY(widget)) + { + parent = gtk_widget_get_parent(parent); + if (GTK_IS_COMBO_BOX(parent)) + return true; + } + + parent_control = gt_get_control(parent); + if (!parent_control) + return false; + + return (parent_control->widget == widget || (GtkWidget *)parent_control->_scroll == widget); +} + +static gboolean draw_container(GtkWidget *widget, cairo_t *cr) +{ + GList *children; + GtkFixedChild *child; + GtkAllocation a; + + gtk_widget_get_allocation(widget, &a); + + cairo_save(cr); + cairo_rectangle(cr, 0, 0, a.width, a.height); + cairo_clip(cr); + + for (children = *get_children_list(GTK_CONTAINER(widget)); children; children = children->next) + { + child = (GtkFixedChild *)children->data; + cairo_save(cr); + gtk_container_propagate_draw (GTK_CONTAINER(widget), child->widget, cr); + cairo_restore(cr); + } + + cairo_restore(cr); + return FALSE; +} + +#include "gb.gtk.patch.h" + +PATCH_DECLARE(GTK_TYPE_WINDOW) +PATCH_DECLARE(GTK_TYPE_ENTRY) +PATCH_DECLARE(GTK_TYPE_COMBO_BOX) +PATCH_DECLARE(GTK_TYPE_SPIN_BUTTON) +PATCH_DECLARE(GTK_TYPE_BUTTON) +PATCH_DECLARE(GTK_TYPE_FIXED) +PATCH_DECLARE(GTK_TYPE_EVENT_BOX) +PATCH_DECLARE(GTK_TYPE_BOX) +PATCH_DECLARE(GTK_TYPE_TOGGLE_BUTTON) +PATCH_DECLARE(GTK_TYPE_SCROLLED_WINDOW) +PATCH_DECLARE(GTK_TYPE_CHECK_BUTTON) +PATCH_DECLARE(GTK_TYPE_RADIO_BUTTON) +PATCH_DECLARE(GTK_TYPE_NOTEBOOK) +PATCH_DECLARE(GTK_TYPE_TEXT_VIEW) +PATCH_DECLARE(GTK_TYPE_SCROLLBAR) +PATCH_DECLARE(GTK_TYPE_SCALE) + +#if GTK_CHECK_VERSION(3,10,0) +PATCH_DECLARE_BASELINE(GTK_TYPE_ENTRY) +PATCH_DECLARE_BASELINE(GTK_TYPE_COMBO_BOX) +PATCH_DECLARE_BASELINE(GTK_TYPE_SPIN_BUTTON) +PATCH_DECLARE_BASELINE(GTK_TYPE_BUTTON) +#endif + +void gt_patch_control(GtkWidget *widget) +{ + PATCH_CLASS(widget, GTK_TYPE_WINDOW) + else PATCH_CLASS_BASELINE(widget, GTK_TYPE_ENTRY) + else PATCH_CLASS_BASELINE(widget, GTK_TYPE_SPIN_BUTTON) + else PATCH_CLASS_BASELINE(widget, GTK_TYPE_BUTTON) + else PATCH_CLASS(widget, GTK_TYPE_FIXED) + else PATCH_CLASS(widget, GTK_TYPE_EVENT_BOX) + else PATCH_CLASS(widget, GTK_TYPE_BOX) + else PATCH_CLASS(widget, GTK_TYPE_TOGGLE_BUTTON) + else PATCH_CLASS(widget, GTK_TYPE_SCROLLED_WINDOW) + else PATCH_CLASS(widget, GTK_TYPE_CHECK_BUTTON) + else PATCH_CLASS(widget, GTK_TYPE_RADIO_BUTTON) + else PATCH_CLASS(widget, GTK_TYPE_NOTEBOOK) + else PATCH_CLASS(widget, GTK_TYPE_TEXT_VIEW) + else PATCH_CLASS(widget, GTK_TYPE_SCROLLBAR) + else PATCH_CLASS(widget, GTK_TYPE_SCALE) + else PATCH_CLASS_BASELINE(widget, GTK_TYPE_COMBO_BOX) + else PATCH_CLASS(widget, GTK_TYPE_TEXT_VIEW) + + PATCH_CLASS_FIXED(widget, GTK_TYPE_FIXED) +} + +#endif + +void gControl::setMinimumSize() +{ + #ifdef GTK3 + + if (isContainer()) + { + _min_w = _min_h = 1; + } + else + { + GtkRequisition minimum_size, natural_size; + bool mapped = gtk_widget_get_mapped(border); + + if (!mapped) + { + gApplication::_disable_mapping_events = true; + gtk_widget_show(border); + } + + _do_not_patch = true; + gtk_widget_get_preferred_size(widget, &minimum_size, &natural_size); + _do_not_patch = false; + + if (!mapped) + { + gtk_widget_hide(border); + gApplication::_disable_mapping_events = false; + } + + //fprintf(stderr, "gtk_widget_get_preferred_size: %s: min = %d %d / nat = %d %d\n", GB.GetClassName(hFree), minimum_size.width, minimum_size.height, natural_size.width, natural_size.height); + + _min_w = minimum_size.width; + _min_h = minimum_size.height; + } + + #else + + _min_w = _min_h = 1; + + #endif +} + + +void gControl::updateEventMask() +{ + gtk_widget_add_events(widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK + | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + + if (widget != border && (GTK_IS_WINDOW(border) || (GTK_IS_EVENT_BOX(border) && !gtk_event_box_get_visible_window(GTK_EVENT_BOX(border))))) + { + gtk_widget_add_events(border, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK + | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + } +} + +void gControl::realize(bool draw_frame) +{ + if (!_scroll) + { + if (!border) + border = widget; + + if (frame) + { + if (border != frame && border != widget) + add_container(border, frame); + if (frame != widget) + add_container(frame, widget); + } + else if (border != widget) + add_container(border, widget); + } + +#ifdef GTK3 + gt_patch_control(border); + if (widget && widget != border) + gt_patch_control(widget); +#endif + + initSignals(); + connectParent(); + + setMinimumSize(); + resize(Max(8, _min_w), Max(8, _min_h), true); + + if (!_no_background && !gtk_widget_get_has_window(border)) + ON_DRAW_BEFORE(border, this, cb_background_expose, cb_background_draw); + + if (draw_frame && frame) + ON_DRAW_BEFORE(frame, this, cb_frame_expose, cb_frame_draw); + +#ifndef GTK3 + if (isContainer() && !gtk_widget_get_has_window(widget)) + g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(cb_clip_children), (gpointer)this); +#endif + + updateEventMask(); + registerControl(); + updateFont(); +} + +void gControl::realizeScrolledWindow(GtkWidget *wid, bool doNotRealize) +{ + _scroll = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); + +#ifdef GTK3 + PATCH_CLASS(_scroll, GTK_TYPE_SCROLLED_WINDOW) + PATCH_CLASS(wid, GTK_TYPE_TEXT_VIEW) +#endif + +#ifdef GTK3 + border = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_hexpand(wid, TRUE); +#else + border = gtk_alignment_new(0, 0, 1, 1); +#endif + gtk_widget_set_redraw_on_allocate(border, TRUE); + widget = wid; + frame = border; + _no_auto_grab = true; + + //gtk_container_add(GTK_CONTAINER(border), GTK_WIDGET(_scroll)); + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(_scroll, GTK_SHADOW_NONE); + gtk_container_add(GTK_CONTAINER(border), GTK_WIDGET(_scroll)); + gtk_container_add(GTK_CONTAINER(_scroll), widget); + + if (!doNotRealize) + realize(true); + else + registerControl(); + + updateFont(); + + gtk_widget_show_all(border); +} + +void gControl::updateBorder() +{ + int pad; + + if (!frame) + return; + +#if GTK3 + if (!GTK_IS_BOX(frame)) +#else + if (!GTK_IS_ALIGNMENT(frame)) +#endif + { + refresh(); + return; + } + + switch (frame_border) + { + case BORDER_NONE: pad = 0; break; + case BORDER_PLAIN: pad = 1; break; + default: pad = gApplication::getFrameWidth(); break; + } + + if ((int)frame_padding > pad) + pad = frame_padding; + +#if GTK3 + g_object_set(widget, "margin", pad, NULL); +#else + gtk_alignment_set_padding(GTK_ALIGNMENT(frame), pad, pad, pad, pad); + refresh(); +#endif + //gtk_widget_queue_draw(frame); +} + +int gControl::getFrameWidth() const +{ + guint p; + + if (frame) + { +#if GTK3 + if (GTK_IS_BOX(frame)) + { + g_object_get(widget, "margin", &p, NULL); + return p; + } +#else + if (GTK_IS_ALIGNMENT(frame)) + { + gtk_alignment_get_padding(GTK_ALIGNMENT(frame), &p, NULL, NULL, NULL); + return p; + } +#endif + } + + /*if (_scroll) + { + if (gtk_scrolled_window_get_shadow_type(_scroll) == GTK_SHADOW_NONE) + return 0; + else + return gApplication::getFrameWidth(); + }*/ + + switch (frame_border) + { + case BORDER_NONE: p = 0; break; + case BORDER_PLAIN: p = 1; break; + default: p = gApplication::getFrameWidth(); break; + } + return p; +} + +void gControl::setFrameBorder(int border) +{ + if (border < BORDER_NONE || border > BORDER_ETCHED) + return; + + frame_border = border; + updateBorder(); +} + +bool gControl::hasBorder() const +{ + return getFrameBorder() != BORDER_NONE; +} + +void gControl::setBorder(bool vl) +{ + setFrameBorder(vl ? BORDER_SUNKEN : BORDER_NONE); + _has_border = vl; +} + + +void gControl::setFramePadding(int padding) +{ + if (padding < 0) + padding = 0; + frame_padding = padding; + updateBorder(); +} + + +void gControl::setName(char *name) +{ + if (_name) g_free(_name); + _name = NULL; + if (name) _name = g_strdup(name); +} + +gColor gControl::defaultBackground() const +{ + return gDesktop::getColor(gDesktop::BACKGROUND, !isEnabled()); +} + +#ifdef GTK3 + +GtkWidget *gControl::getStyleSheetWidget() +{ + return border; +} + +const char *gControl::getStyleSheetColorNode() +{ + return ""; +} + +const char *gControl::getStyleSheetFontNode() +{ + return ""; +} + +void gControl::customStyleSheet(GString *css) +{ +} + +void gControl::setStyleSheetNode(GString *css, const char *node) +{ + if (node == _css_node) + return; + + if (node && _css_node && !::strcmp(node, _css_node)) + return; + + if (_css_node) + g_string_append(css, "}\n"); + + _css_node = node; + + if (!node) + return; + + if (!_has_css_id) + { + gt_widget_set_name(getStyleSheetWidget(), name()); + _has_css_id = true; + } + + g_string_append_printf(css, "#%s %s {\ntransition:none;\n", gtk_widget_get_name(getStyleSheetWidget()), node); +} + +void gControl::updateStyleSheet(bool dirty) +{ + GString *css; + gColor bg, fg; + + if (dirty) + _style_dirty = true; + + if (isContainer()) + { + gContainer *cont = (gContainer *)this; + + if (_no_style_without_child && cont->childCount() == 0) + return; + + if (!dirty) + { + for (int i = 0; i < cont->childCount(); i++) + cont->child(i)->updateStyleSheet(false); + } + } + + if (!isReallyVisible() || !_style_dirty) + return; + + bg = _no_background ? background() : COLOR_DEFAULT; + fg = foreground(); //realForeground(); + + css = g_string_new(NULL); + _css_node = NULL; + + if (bg != COLOR_DEFAULT || fg != COLOR_DEFAULT) + { + setStyleSheetNode(css, getStyleSheetColorNode()); + gt_css_add_color(css, bg, fg); + } + + if (_resolved_font) + { + setStyleSheetNode(css, getStyleSheetFontNode()); + gt_css_add_font(css, _resolved_font); + } + + customStyleSheet(css); + + setStyleSheetNode(css, NULL); + + gt_define_style_sheet(&_css, css); + + /*if (_css) + { + char *css_str = gtk_css_provider_to_string(GTK_CSS_PROVIDER(_css)); + fprintf(stderr, "---- %s\n%s", gtk_widget_get_name(getStyleSheetWidget()), css_str); + g_free(css_str); + }*/ + + _style_dirty = false; +} + +gColor gControl::realBackground(bool no_default) +{ + if (_bg != COLOR_DEFAULT) + return _bg; + + if (!no_default) + return COLOR_DEFAULT; + + if (!use_base && parent()) + return parent()->realBackground(true); + + return defaultBackground(); +} + +void gControl::setRealBackground(gColor color) +{ +} + +void gControl::setBackground(gColor color) +{ + if (_bg == color) + return; + + _bg = color; + updateStyleSheet(true); + updateColor(); +} + +gColor gControl::realForeground(bool no_default) +{ + if (_fg != COLOR_DEFAULT) + return _fg; + else if (pr) + return pr->realForeground(no_default); + else + return no_default ? gDesktop::getColor(gDesktop::FOREGROUND) : COLOR_DEFAULT; +} + +void gControl::setRealForeground(gColor color) +{ +} + +void gControl::setForeground(gColor color) +{ + if (_fg == color) + return; + + _fg = color; + _fg_set = color != COLOR_DEFAULT; +#ifdef GTK3 + updateStyleSheet(true); +#endif + //gt_widget_set_color(border, TRUE, _fg, _fg_name, &_fg_default); + updateColor(); + /*if (::strcmp(name(), "dwgInfo") == 0) + fprintf(stderr, "setForeground: %08X\n", _fg);*/ +} + +#else + +gColor gControl::realBackground(bool no_default) +{ + if (_bg_set) + return use_base ? get_gdk_base_color(widget, isEnabled()) : get_gdk_bg_color(widget, isEnabled()); + else + return no_default ? defaultBackground() : COLOR_DEFAULT; +} + +static void set_background(GtkWidget *widget, gColor color, bool use_base) +{ + if (use_base) + set_gdk_base_color(widget, color); + else + set_gdk_bg_color(widget, color); +} + +void gControl::setRealBackground(gColor color) +{ + set_background(border, color, use_base); + if (border != frame && GTK_IS_WIDGET(frame)) + set_background(frame, color, use_base); + if (frame != widget) + set_background(widget, color, use_base); +} + +void gControl::setBackground(gColor color) +{ + _bg = color; + _bg_set = color != COLOR_DEFAULT; + + if (!_bg_set) + { + if (pr && !use_base) + color = pr->realBackground(); + } + + setRealBackground(color); +} + +gColor gControl::realForeground(bool no_default) +{ + if (_fg_set) + return use_base ? get_gdk_text_color(widget, isEnabled()) : get_gdk_fg_color(widget, isEnabled()); + else if (pr) + return pr->realForeground(no_default); + else + return no_default ? gDesktop::getColor(gDesktop::FOREGROUND) : COLOR_DEFAULT; +} + +static void set_foreground(GtkWidget *widget, gColor color, bool use_base) +{ + if (use_base) + set_gdk_text_color(widget, color); + else + set_gdk_fg_color(widget, color); +} + +void gControl::setRealForeground(gColor color) +{ + set_foreground(widget, color, use_base); +} + +void gControl::setForeground(gColor color) +{ + _fg = color; + _fg_set = color != COLOR_DEFAULT; + + if (!_fg_set) + { + if (pr) + color = pr->realForeground(); + } + + setRealForeground(color); +} + +#endif + +void gControl::emit(void *signal) +{ + if (!signal || locked()) + return; + (*((void (*)(gControl *))signal))(this); +} + +void gControl::emit(void *signal, intptr_t arg) +{ + if (!signal || locked()) + return; + (*((void (*)(gControl *, intptr_t))signal))(this, arg); +} + +void gControl::reparent(gContainer *newpr, int x, int y) +{ + gContainer *oldpr; + bool was_visible = isVisible(); + + // newpr can be equal to pr: for example, to move a control for one + // tab to another tab of the same TabStrip! + + if (!newpr || !newpr->getContainer()) + return; + + if (pr == newpr && gtk_widget_get_parent(border) == newpr->getContainer()) + { + move(x, y); + return; + } + + if (was_visible) hide(); + //gtk_widget_unrealize(border); + + oldpr = pr; + pr = newpr; + + if (oldpr == newpr) + { + gt_widget_reparent(border, newpr->getContainer()); + oldpr->performArrange(); + } + else + { + if (oldpr) + { + gt_widget_reparent(border, newpr->getContainer()); + oldpr->remove(this); + oldpr->performArrange(); + } + + newpr->insert(this); + } + + //gtk_widget_realize(border); + bufX = !x; + move(x, y); + if (was_visible) + { + //fprintf(stderr, "was_visible\n"); + show(); + } +} + +int gControl::scrollX() +{ + if (!_scroll) + return 0; + + return (int)gtk_adjustment_get_value(gtk_scrolled_window_get_hadjustment(_scroll)); +} + +int gControl::scrollY() +{ + if (!_scroll) + return 0; + + return (int)gtk_adjustment_get_value(gtk_scrolled_window_get_vadjustment(_scroll)); +} + +void gControl::setScrollX(int vl) +{ + GtkAdjustment* adj; + int max; + + if (!_scroll) + return; + + adj = gtk_scrolled_window_get_hadjustment(_scroll); + + max = (int)(gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj)); + + if (vl < 0) + vl = 0; + else if (vl > max) + vl = max; + + gtk_adjustment_set_value(adj, (gdouble)vl); +} + +void gControl::setScrollY(int vl) +{ + GtkAdjustment* adj; + int max; + + if (!_scroll) + return; + + adj = gtk_scrolled_window_get_vadjustment(_scroll); + + max = (int)(gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj)); + + if (vl < 0) + vl = 0; + else if (vl > max) + vl = max; + + gtk_adjustment_set_value(adj, (gdouble)vl); +} + +void gControl::scroll(int x, int y) +{ + setScrollX(x); + setScrollY(y); +} + +/*int gControl::scrollWidth() +{ + return widget->requisition.width; +} + +int gControl::scrollHeight() +{ + return widget->requisition.height; +}*/ + +void gControl::setScrollBar(int vl) +{ + if (!_scroll) + return; + + _scrollbar = vl & 3; + updateScrollBar(); +} + +void gControl::updateScrollBar() +{ + if (!_scroll) + return; + + switch(_scrollbar) + { + case SCROLL_NONE: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER); + break; + case SCROLL_HORIZONTAL: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); + break; + case SCROLL_VERTICAL: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + break; + case SCROLL_BOTH: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + break; + } +} + +bool gControl::isTracking() const +{ + if (_proxy) + return _proxy->isTracking(); + else + return _tracking; +} + +void gControl::setTracking(bool v) +{ + if (_proxy) + _proxy->setTracking(v); + else + _tracking = v; + /* + GtkWidget *wid; + + if (GTK_IS_EVENT_BOX(border)) + wid = border; + else + wid = widget; + + if (v != _tracking) + { + uint event_mask = gtk_widget_get_events(wid); + _tracking = v; + if (v) + { + _old_tracking = event_mask & GDK_POINTER_MOTION_MASK; + event_mask |= GDK_POINTER_MOTION_MASK; + } + else + { + event_mask &= ~GDK_POINTER_MOTION_MASK; + } + + if (!_old_tracking) + { + gtk_widget_unrealize(wid); + gtk_widget_set_events(wid, event_mask); + gtk_widget_realize(wid); + } + } + */ +} + +bool gControl::grab() +{ + gControl *old_control_grab; + bool save_tracking; + + if (_grab) + return false; + + if (gt_grab(border, FALSE, gApplication::lastEventTime())) + return true; + + _grab = true; + save_tracking = _tracking; + _tracking = true; + + old_control_grab = gApplication::_control_grab; + gApplication::_control_grab = this; + + gApplication::enterLoop(this); + + gApplication::_control_grab = old_control_grab; + + gt_ungrab(); + + _tracking = save_tracking; + _grab = false; + return false; +} + +bool gControl::hovered() +{ + //int x, y, xm, ym; + + if (!isVisible()) + return false; + else + return _inside; + + /*getScreenPos(&x, &y); + gMouse::getScreenPos(&xm, &ym); + + return (xm >= x && ym >= y && xm < (x + width()) && ym < (y + height()));*/ +} + +bool gControl::setProxy(gControl *proxy) +{ + gControl *check = proxy; + + while (check) + { + if (check == this) + return true; + + check = check->_proxy; + } + + if (proxy == _proxy) + return false; + + //fprintf(stderr, "proxy: (%p %s) -> (%p %s)\n", this, name(), proxy, proxy ? proxy->name() : "NULL"); + + if (proxy && proxy->_proxy_for) + proxy->_proxy_for->_proxy = NULL; + + if (_proxy) + _proxy->_proxy_for = NULL; + + _proxy = proxy; + + if (_proxy) + _proxy->_proxy_for = this; + + return false; +} + +void gControl::setNoTabFocus(bool v) +{ + if (_proxy) + { + _proxy->setNoTabFocus(v); + return; + } + + _no_tab_focus = v; +} + +bool gControl::isNoTabFocus() const +{ + if (_proxy) + return _proxy->isNoTabFocus(); + else + return _no_tab_focus; +} + +bool gControl::isNoTabFocusRec() const +{ + return isNoTabFocus() || (parent() && parent()->isNoTabFocusRec()); +} + +#ifdef GTK3 +void gControl::onEnterEvent() +{ +} + +void gControl::onLeaveEvent() +{ +} +#endif + +void gControl::emitEnterEvent(bool no_leave) +{ + gContainer *cont; + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "========== START ENTER %s (%d)\n", name(), no_leave); + #endif + + if (parent()) + parent()->emitEnterEvent(true); + + if (!no_leave && isContainer()) + { + cont = (gContainer *)this; + int i; + + for (i = 0; i < cont->childCount(); i++) + cont->child(i)->emitLeaveEvent(); + } + + gApplication::_enter = this; + + if (gApplication::_leave) + { + if (gApplication::_leave == this || gApplication::_leave->isAncestorOf(this)) + gApplication::_leave = NULL; + } + + if (_inside) + return; + + _inside = true; + + #ifdef GTK3 + onEnterEvent(); + #endif + + if (!no_leave) + setMouse(mouse()); + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, ">>>>>>>>>> END ENTER %s\n", name()); + #endif + + if (gApplication::_ignore_until_next_enter) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "ignore next enter for %s\n", name()); + #endif + if (gApplication::_ignore_until_next_enter == this) + gApplication::_ignore_until_next_enter = NULL; + return; + } + + //fprintf(stderr, "RAISE ENTER: %s\n", name()); + CB_control_enter_leave(this, gEvent_Enter); +} + +void gControl::emitLeaveEvent() +{ + if (gApplication::_enter == this) + gApplication::_enter = NULL; + + if (!_inside) + return; + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "========== START LEAVE %s\n", name()); + #endif + + if (isContainer()) + { + gContainer *cont = (gContainer *)this; + int i; + + for (i = 0; i < cont->childCount(); i++) + cont->child(i)->emitLeaveEvent(); + } + + _inside = false; + + #ifdef GTK3 + onLeaveEvent(); + #endif + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, ">>>>>>>>>> END LEAVE %s\n", name()); + #endif + + if (parent()) parent()->setMouse(parent()->mouse()); + + if (gApplication::_ignore_until_next_enter) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "ignore next leave for %s\n", name()); + #endif + return; + } + + //fprintf(stderr, "RAISE LEAVE: %s\n", name()); + CB_control_enter_leave(this, gEvent_Leave); +} + +bool gControl::isAncestorOf(gControl *child) +{ + if (!isContainer()) + return false; + + for(;;) + { + child = child->parent(); + if (!child) + return false; + else if (child == this) + return true; + } +} + +#ifdef GTK3 +void gControl::drawBackground(cairo_t *cr) +{ + gColor col= background(); + + if (col == COLOR_DEFAULT) + { + if (!gtk_widget_get_has_window(border)) + return; + col = realBackground(true); + } + + gt_cairo_set_source_color(cr, col); + cairo_rectangle(cr, 0, 0, width(), height()); + cairo_fill(cr); +} +#else +void gControl::drawBackground(GdkEventExpose *e) +{ + GtkAllocation a; + + if (background() == COLOR_DEFAULT) + return; + + cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(border)); + + gdk_cairo_region(cr, e->region); + cairo_clip(cr); + gt_cairo_set_source_color(cr, background()); + + gtk_widget_get_allocation(border, &a); + cairo_rectangle(cr, a.x, a.y, width(), height()); + cairo_fill(cr); + + cairo_destroy(cr); +} +#endif + +#ifdef GTK3 +void gControl::updateColor() +{ +} + +/*void gControl::setColorNames(const char *bg_names[], const char *fg_names[]) +{ + _bg_name_list = bg_names; + _fg_name_list = fg_names; + + if (!bg_names) + { + _bg_name = NULL; + _fg_name = NULL; + use_base = FALSE; + return; + } + + gt_style_lookup_color(gtk_widget_get_style_context(widget), bg_names, &_bg_name, &_bg_default); + gt_style_lookup_color(gtk_widget_get_style_context(widget), fg_names, &_fg_name, &_fg_default); +} + +void gControl::setColorBase() +{ + static const char *bg_names[] = { "base_color", "theme_base_color", NULL }; + static const char *fg_names[] = { "text_color", "theme_text_color", NULL }; + setColorNames(bg_names, fg_names); + use_base = TRUE; +} + +void gControl::setColorButton() +{ + const char *bg_names[] = { "button_bg_color", "theme_button_bg_color", "theme_bg_color", NULL }; + const char *fg_names[] = { "button_fg_color", "theme_button_fg_color", "theme_fg_color", NULL }; + setColorNames(bg_names, fg_names); + use_base = FALSE; +}*/ +#endif + +GtkIMContext *gControl::getInputMethod() +{ + return _input_method; +} + +gControl *gControl::nextFocus() +{ + gControl *ctrl; + gControl *next_ctrl; + + ctrl = this; + + if (ctrl->isContainer() && ((gContainer *)ctrl)->childCount()) + return ((gContainer *)ctrl)->firstChild(); + + for(;;) + { + next_ctrl = ctrl->next(); + if (next_ctrl) + return next_ctrl; + if (ctrl->isTopLevel()) + return ctrl; + ctrl = ctrl->parent(); + } +} + +gControl *gControl::previousFocus() +{ + gControl *ctrl; + gControl *next_ctrl; + + ctrl = this; + + if (ctrl->isContainer() && ((gContainer *)ctrl)->childCount()) + return ((gContainer *)ctrl)->lastChild(); + + for(;;) + { + next_ctrl = ctrl->previous(); + if (next_ctrl) + return next_ctrl; + if (ctrl->isTopLevel()) + return ctrl; + ctrl = ctrl->parent(); + } +} + +void gControl::createWidget() +{ +#ifdef GTK3 + if (_css) + { + g_object_unref(_css); + _css = NULL; + } +#endif +} + +void gControl::createBorder(GtkWidget *new_border, bool keep_widget) +{ + GtkWidget *old = border; + + border = new_border; + + if (keep_widget && widget) + gt_widget_reparent(widget, border); + + if (old) + { + _no_delete = true; + gtk_widget_destroy(old); + _no_delete = false; + createWidget(); + } + + updateAcceptDrops(); +} + +int gControl::actualDirection() const +{ + const gControl *ctrl = this; + + for(;;) + { + if (ctrl->direction() != DIRECTION_DEFAULT) + return ctrl->direction(); + if (ctrl->isWindow()) + return DIRECTION_DEFAULT; + ctrl = ctrl->parent(); + } +} + +void gControl::updateDirection() +{ + GtkTextDirection dir; + + switch(actualDirection()) + { + case DIRECTION_LTR: dir = GTK_TEXT_DIR_LTR; break; + case DIRECTION_RTL: dir = GTK_TEXT_DIR_RTL; break; + default: dir = MAIN_rtl ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR; + } + + if (_inverted) + dir = dir == GTK_TEXT_DIR_LTR ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR; + + gtk_widget_set_direction(widget, dir); +} + +bool gControl::isRightToLeft() const +{ + return gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL; +} + + +void gControl::setInverted(bool v) +{ + if (v == _inverted) + return; + + _inverted = v; + updateDirection(); +} + +void gControl::setDirection(int v) +{ + if (v == _direction) + return; + + _direction = v; + updateDirection(); +} + +void gControl::checkVisibility() +{ + if (_allow_show) + return; + + _allow_show = true; + setVisibility(_visible); +} diff --git a/gb.gtk/src/gcontrol.h b/gb.gtk/src/gcontrol.h new file mode 100644 index 00000000..48cb491f --- /dev/null +++ b/gb.gtk/src/gcontrol.h @@ -0,0 +1,396 @@ +/*************************************************************************** + + gcontrol.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCONTROL_H +#define __GCONTROL_H + +#include "gcolor.h" +#include "gdrag.h" + +class gContainer; +class gMainWindow; + +#ifdef GTK3 +void gt_patch_control(GtkWidget *widget); +#endif + +class gControl +{ +public: + gControl(); + gControl(gContainer *parent); + virtual ~gControl(); + + void *hFree; + + bool isContainer() const { return _is_container; } + bool isWindow() const { return _is_window; } + bool isButton() const { return _is_button; } + bool isDrawingArea() const { return _is_drawingarea; } + bool isTopLevel() const { return pr == NULL; } + bool isDestroyed() const { return _destroyed; } + + gMainWindow *window() const; + gMainWindow *topLevel() const; + + gContainer *parent() const { return pr; } + bool isAncestorOf(gControl *child); + + bool isDesign() const { return _design && !_no_design; } + bool isDesignIgnore() const { return _design_ignore; } + + bool hovered(); + virtual long handle(); + + int left() const { return bufX; } + int x() const { return left(); } + int top() const { return bufY; } + int y() const { return top(); } + int width() const { return bufW; } + int height() const { return bufH; } + void setLeft(int v) { move(v, y()); } + void setTop(int v) { move(x(), v); } + void setWidth(int w) { resize(w, height()); } + void setHeight(int h) { resize(width(), h); } + virtual void move(int x, int y); + virtual bool resize(int w, int h, bool no_decide = false); + void resize() { resize(width(), height()); } + void moveResize(int x, int y, int w, int h, bool no_decide = false); + void getGeometry(GdkRectangle *rect) const { rect->x = bufX; rect->y = bufY; rect->width = bufW; rect->height = bufH; } + void setGeometry(GdkRectangle *rect) { moveResize(rect->x, rect->y, rect->width, rect->height); } + + bool isVisible() const { return _visible; } + bool isReallyVisible(); + virtual void setVisible(bool v); + void hide() { setVisible(false); } + void show() { setVisible(true); } + + void setVisibility(bool v); + void checkVisibility(); + + virtual bool isEnabled() const; + virtual void setEnabled(bool vl); + + int mouse(); + void setMouse(int m); + gCursor* cursor(); + void setCursor(gCursor *vl); + virtual void updateCursor(GdkCursor *cursor); + + gControl *next(); + gControl *previous(); + gControl *nextFocus(); + gControl *previousFocus(); + void setPrevious(gControl *prev); + void setNext(gControl *next); + + int screenX(); + int screenY(); + virtual bool getScreenPos(int *x, int *y); + + bool isExpand() const { return _expand; } + bool isIgnore() const { return _ignore; } + void setExpand (bool vl); + void setIgnore (bool vl); + + bool acceptDrops() const; + void setAcceptDrops(bool vl); + void updateAcceptDrops(); + + const char *name() const { return _name; } + void setName(char *name); + + bool action() const { return _action; } + void setAction(bool v) { _action = v; } + + virtual void setDesign(bool ignore = false); + gControl *ignoreDesign(); + void updateDesign(); + + char *tooltip() { return _tooltip; } + void setTooltip(char *vl); + + bool isTracking() const; + void setTracking(bool vl); + + bool isNoTabFocus() const; + bool isNoTabFocusRec() const; + void setNoTabFocus(bool v); + + void setDirection(int v); + int direction() const { return _direction; } + + void setInverted(bool v); + bool isInverted() const { return _inverted; } + + bool isRightToLeft() const; + + gColor background() const { return _bg; } + gColor foreground() const { return _fg; } + virtual void setBackground(gColor color = COLOR_DEFAULT); + virtual void setForeground(gColor color = COLOR_DEFAULT); + virtual gColor defaultBackground() const; + gColor realBackground(bool no_default = false); + gColor realForeground(bool no_default = false); + virtual void setRealBackground(gColor color); + virtual void setRealForeground(gColor color); + + virtual gFont *font() const; + void actualFontTo(gFont *ft); + virtual void setFont(gFont *ft); + bool ownFont() { return _font != NULL; } + virtual void updateFont(); + virtual void updateSize(); + + bool hasNativePopup() const { return _has_native_popup; } + +#ifdef GTK3 + void setWidgetName(); + virtual GtkWidget *getStyleSheetWidget(); + virtual const char *getStyleSheetColorNode(); + virtual const char *getStyleSheetFontNode(); + void updateStyleSheet(bool dirty); + virtual void customStyleSheet(GString *css); + void setStyleSheetNode(GString *css, const char *node); + virtual void updateColor(); +#else + void setColorNames(const char **, const char **) {} +#endif + void setColorBase() { use_base = TRUE; } + void setColorButton() { use_base = FALSE; } + + virtual bool canFocus() const; + bool canFocusOnClick() const; + void setCanFocus(bool vl); + + bool eatReturnKey() const { return _eat_return_key; } + + gControl *proxy() const { return _proxy; } + bool setProxy(gControl *proxy); + + int scrollX(); + int scrollY(); + void scroll(int x, int y); + void setScrollX(int vl); + void setScrollY(int vl); + + int scrollBar() const { return _scrollbar; } + void setScrollBar(int vl); + virtual void updateScrollBar(); + + bool isDragging() const { return _dragging; } + + void dragText(char *txt, char *format = NULL) { gDrag::dragText(this, txt, format); } + void dragImage(gPicture *pic) { gDrag::dragImage(this, pic); } + + virtual void reparent(gContainer *newpr, int x, int y); + + void lower() { restack(false); } + void raise() { restack(true); } + virtual void restack(bool raise); + + virtual void setFocus(); + bool hasFocus() const; +#if GTK_CHECK_VERSION(3, 2, 0) + bool hasVisibleFocus() const; +#else + bool hasVisibleFocus() const { return hasFocus(); } +#endif + + void refresh(); + void refresh(int x, int y, int w, int h); + virtual void afterRefresh(); + + bool grab(); + + virtual void destroy(); + void destroyNow() { destroy(); postDelete(); } + + void lock() { _locked++; } + void unlock() { _locked--; } + bool locked() const { return _locked; } + + void emit(void *signal); + void emit(void *signal, intptr_t arg); + void emit(void *signal, char *arg) { emit(signal, (intptr_t)arg); } + +// "Private" + gint bufW,bufH,bufX,bufY; + int _min_w, _min_h; + gCursor *curs; + gFont *_font; + gFont *_resolved_font; + GtkWidget *widget; + GtkWidget *border; + GtkWidget *frame; + GtkScrolledWindow *_scroll; + short _mouse; + gControl *_proxy, *_proxy_for; + gColor _bg, _fg; + char *_tooltip; +#ifdef GTK3 + GtkStyleProvider *_css; + const char *_css_node; +#endif + + unsigned _destroyed : 1; // If the control has already been added to the destroy list + unsigned _design : 1; + unsigned _design_ignore : 1; + unsigned _no_design : 1; + unsigned _expand : 1; + unsigned _ignore : 1; + unsigned _action : 1; // *reserved* + unsigned _inverted : 1; // if the widget direction has been inverted + + unsigned _accept_drops : 1; // If the control accepts drops + unsigned _dragging : 1; // if the control is being dragged + unsigned _tracking : 1; // If we are tracking mouse move even if no mouse button is pressed + unsigned _old_tracking : 1; // real value when Tracking is false + unsigned _bg_set : 1; // Have a private background + unsigned _fg_set : 1; // Have a private foreground + unsigned have_cursor : 1; // If gApplication::setBusy() must update the cursor + unsigned use_base : 1; // Use base and text color for foreground and background + + unsigned _visible : 1; // A control can be hidden if its width or height is zero + unsigned _no_delete : 1; // Do not delete on destroy signal + unsigned _scrollbar : 2; + unsigned _dirty_pos : 1; // If the position of the widget has changed + unsigned _dirty_size : 1; // If the size of the widget has changed + unsigned _inside : 1; // if we got an enter event, but not a leave event yet. + unsigned _has_border : 1; // if the control has a border + + unsigned _locked : 4; // For locking events + unsigned frame_border : 4; + + unsigned frame_padding : 8; + + unsigned _has_input_method : 1; // Has its own input method management + unsigned _no_default_mouse_event : 1; // No default mouse events + unsigned _grab : 1; // control is currently grabbing mouse and keyboard + unsigned _no_tab_focus : 1; // Don't put inside focus chain + unsigned _no_auto_grab : 1; // do not automatically grab widget on button press event + unsigned _no_background : 1; // Don't draw the background automatically + unsigned _use_wheel : 1; // Do not propagate the mouse wheel event + unsigned _has_native_popup : 1; // I have a native popup menu + + unsigned _is_container : 1; // I am a container + unsigned _is_window : 1; // I am a window + unsigned _is_button : 1; // I am a button + unsigned _is_drawingarea : 1; // I am a drawing area + unsigned _eat_return_key : 1; // If the control eats the return key + unsigned _minimum_size_set : 1; // If minimum size has been computed + unsigned _direction : 2; // Text direction + + unsigned _allow_show : 1; // Allowed to be visible (after the first resize) + + +#ifdef GTK3 + unsigned _style_dirty : 1; // If the style must be refreshed + unsigned _no_style_without_child : 1; // For containers that do not need a css if they have no children + unsigned _has_css_id : 1; // If the widget has a css id +#endif + + void removeParent() { pr = NULL; } + void initSignals(); + virtual void borderSignals(); + void connectParent(); + void setParent(gContainer *parent) { pr = parent; } + void initAll(gContainer *pr); + void realize(bool draw_frame = false); + void realizeScrolledWindow(GtkWidget *wid, bool doNotRealize = false); + void registerControl(); + void updateGeometry(bool force = false); + bool mustUpdateCursor() { return mouse() != -1 || have_cursor || !parent(); } + void updateEventMask(); + + bool hasInputMethod() { return _has_input_method; } + virtual GtkIMContext *getInputMethod(); + + GdkCursor *getGdkCursor(); + virtual void updateBorder(); + int getFrameBorder() const { return frame_border; } + void setFrameBorder(int border); + virtual void setBorder(bool b); + bool hasBorder() const; + int getFramePadding() const { return frame_padding; } + void setFramePadding(int padding); + virtual int getFrameWidth() const; + virtual gColor getFrameColor(); +#ifdef GTK3 + void drawBorder(cairo_t *cr); + void drawBackground(cairo_t *cr); +#else + void drawBorder(GdkEventExpose *e); + void drawBackground(GdkEventExpose *e); +#endif + + void createBorder(GtkWidget *new_border, bool keep_widget = false); + void createWidget(); + + int minimumWidth() const { return _min_w; } + int minimumHeight() const { return _min_h; } + virtual void setMinimumSize(); + + void resolveFont(); + + void emitEnterEvent(bool no_leave = false); + void emitLeaveEvent(); +#ifdef GTK3 + virtual void onEnterEvent(); + virtual void onLeaveEvent(); +#endif + + void hideButKeepFocus(); + void showButKeepFocus(); + + int actualDirection() const; + virtual void updateDirection(); + + static void postDelete(); + +private: + + gContainer *pr; + char *_name; + GtkIMContext *_input_method; + + void dispose(); +}; + +#define SIGNAL(_signal) ((void *)_signal) + +// Callbacks + +// "Signals" +bool CB_control_can_raise(gControl *sender, int type); +void CB_control_finish(gControl *sender); +void CB_control_focus(gControl *sender, int type); +bool CB_control_key(gControl *sender, int type); +bool CB_control_mouse(gControl *sender, int type); +void CB_control_enter_leave(gControl *sender, int type); +bool CB_control_drag(gControl *sender); +bool CB_control_drag_move(gControl *sender); +bool CB_control_drop(gControl *sender); +void CB_control_drag_leave(gControl *sender); + +#endif diff --git a/gb.gtk/src/gcursor.cpp b/gb.gtk/src/gcursor.cpp new file mode 100644 index 00000000..9b15f59c --- /dev/null +++ b/gb.gtk/src/gcursor.cpp @@ -0,0 +1,80 @@ +/*************************************************************************** + + gcursor.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gb.form.font.h" + +#include "gcursor.h" + +gCursor::gCursor(gPicture *pic, int px, int py) +{ + static bool check = false; + GdkDisplay *dp = gdk_display_get_default(); + + if (!check) + { + if (!gdk_display_supports_cursor_color(dp) || !gdk_display_supports_cursor_alpha(dp)) + fprintf(stderr, GTK_NAME ": warning: RGBA cursors are not supported\n"); + check = true; + } + + x = px; + y = py; + cur = NULL; + if (!pic || pic->isVoid()) + return; + + if (pic->width() <= x) + x = pic->width() - 1; + if (pic->height() <= y) + y = pic->height() - 1; + + cur=gdk_cursor_new_from_pixbuf(dp, pic->getPixbuf(), x, y); +} + +gCursor::gCursor(gCursor *cursor) +{ + cur = NULL; + if (!cursor) return; + if (!cursor->cur) return; + + cur = cursor->cur; + x = cursor->x; + y = cursor->y; + if (cur) +#ifdef GTK3 + g_object_ref(cur); +#else + gdk_cursor_ref(cur); +#endif +} + +gCursor::~gCursor() +{ + if (cur) +#ifdef GTK3 + g_object_unref(cur); +#else + gdk_cursor_unref(cur); +#endif +} diff --git a/gb.gtk/src/gcursor.h b/gb.gtk/src/gcursor.h new file mode 100644 index 00000000..7fef8ff3 --- /dev/null +++ b/gb.gtk/src/gcursor.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gcursor.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCURSOR_H +#define __GCURSOR_H + +#define CURSOR_DEFAULT GDK_X_CURSOR +#define CURSOR_CUSTOM GDK_CURSOR_IS_PIXMAP + +class gPicture; + +class gCursor +{ +public: + gCursor(gPicture *pic,int x,int y); + gCursor(gCursor *cursor); + ~gCursor(); + +//"Properties" + int left() const { return x; } + int top() const { return y; } + +//"Private" + GdkCursor *cur; + int x; + int y; +}; + +#endif diff --git a/gb.gtk/src/gdesktop.cpp b/gb.gtk/src/gdesktop.cpp new file mode 100644 index 00000000..848f7ef4 --- /dev/null +++ b/gb.gtk/src/gdesktop.cpp @@ -0,0 +1,267 @@ +/*************************************************************************** + + gdesktop.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gb.form.font.h" + +#ifndef GTK3 +#include "x11.h" +#endif + +#include "gapplication.h" +#include "gmainwindow.h" +#include "gdesktop.h" + +/*********************************************************************** + +Desktop + +************************************************************************/ + +bool gDesktop::_colors_valid = false; +gColor gDesktop::_colors[NUM_COLORS]; +gColor gDesktop::_colors_disabled[NUM_COLORS]; + +bool gDesktop::rightToLeft() +{ + return MAIN_rtl; //gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; +} + +gMainWindow* gDesktop::activeWindow() +{ + return gMainWindow::_active ? gMainWindow::_active->topLevel() : NULL; +} + +int gDesktop::width() +{ +#if GTK_CHECK_VERSION(3, 22, 0) + GdkRectangle rect; + gdk_monitor_get_geometry(gdk_display_get_primary_monitor(gdk_display_get_default()), &rect); + return rect.width; +#else + return gdk_screen_get_width(gdk_screen_get_default ()); +#endif +} + +int gDesktop::height() +{ +#if GTK_CHECK_VERSION(3, 22, 0) + GdkRectangle rect; + gdk_monitor_get_geometry(gdk_display_get_primary_monitor(gdk_display_get_default()), &rect); + return rect.height; +#else + return gdk_screen_get_height(gdk_screen_get_default()); +#endif +} + +int gDesktop::resolution() +{ + gdouble res = gdk_screen_get_resolution(gdk_screen_get_default()); + if (res == -1) + res = 96; + return res; +} + + +gPicture* gDesktop::screenshot(int x, int y, int w, int h) +{ + return gt_grab_window(gdk_get_default_root_window(), x, y, w, h); +} + +int gDesktop::count() +{ +#if GTK_CHECK_VERSION(3, 22, 0) + return gdk_display_get_n_monitors(gdk_display_get_default()); +#elif defined(GTK3) + return gdk_screen_get_n_monitors(gdk_screen_get_default()); +#else + return gdk_display_get_n_screens(gdk_display_get_default()); +#endif +} + +void gDesktop::geometry(int screen, GdkRectangle *rect) +{ + rect->x = rect->y = rect->width = rect->height = 0; + if (screen < 0 || screen >= count()) + return; + +#if GTK_CHECK_VERSION(3, 22, 0) + gdk_monitor_get_geometry(gdk_display_get_monitor(gdk_display_get_default(), screen), rect); +#elif defined(GTK3) + gdk_screen_get_monitor_geometry(gdk_screen_get_default(), screen, rect); +#else + rect->width = gdk_screen_get_width(gdk_display_get_screen(gdk_display_get_default(), screen)); + rect->height = gdk_screen_get_height(gdk_display_get_screen(gdk_display_get_default(), screen)); +#endif +} + +void gDesktop::availableGeometry(int screen, GdkRectangle *rect) +{ + rect->x = rect->y = rect->width = rect->height = 0; + if (screen < 0 || screen >= count()) + return; + +#if GTK_CHECK_VERSION(3, 22, 0) + gdk_monitor_get_workarea(gdk_display_get_monitor(gdk_display_get_default(), screen), rect); +#elif defined(GTK3) + gdk_screen_get_monitor_workarea(gdk_screen_get_default(), screen, rect); +#else + if (X11_get_available_geometry(screen, &rect->x, &rect->y, &rect->width, &rect->height)) + geometry(screen, rect); +#endif +} + +void gDesktop::onThemeChange() +{ + gt_on_theme_change(); + _colors_valid = false; +} + +#ifdef GTK3 + +static gColor get_color(GType type, bool fg, GtkStateFlags state, bool disabled) +{ + GtkStyleContext *style = gt_get_style(type, state == STATE_SELECTED ? "selection" : NULL, (type == GTK_TYPE_TOOLTIP && !fg) ? GTK_STYLE_CLASS_BACKGROUND : NULL); + + if (disabled) + state = (GtkStateFlags)(state | STATE_INSENSITIVE); + + gtk_style_context_set_state(style, state); + + if (!fg) + { + cairo_surface_t *image; + cairo_t *cairo; + uchar *p; + + image = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 32, 32); + cairo = cairo_create(image); + gtk_render_background(style, cairo, 0, 0, 32, 32); + cairo_destroy(cairo); + + p = (uchar *)cairo_image_surface_get_data(image) + sizeof(uint) * (16 * 33); + return gt_rgba_to_color(p[2], p[1], p[0], p[3]); + } + else + { + GdkRGBA rgba; + gtk_style_context_get_color(style, state, &rgba); + return gt_to_color(&rgba); + } + + if (state == STATE_SELECTED) + g_object_unref(G_OBJECT(style)); +} + +#else + +static gColor get_color(GType type, bool fg, GtkStateType state, bool disabled) +{ + GtkStyle *st = gt_get_style(type); + GdkColor *color; + + if (disabled) + state = STATE_INSENSITIVE; + + if (type == GTK_TYPE_ENTRY) + { + if (fg) + color = &st->text[state]; + else + color = &st->base[state]; + } + else + { + if (fg) + color = &st->fg[state]; + else + color = &st->bg[state]; + } + + return gt_gdkcolor_to_color(color); +} + +#endif + +void gDesktop::calc_colors(gColor colors[], bool disabled) +{ + colors[BACKGROUND] = get_color(GTK_TYPE_WINDOW, false, STATE_NORMAL, disabled); + colors[FOREGROUND] = get_color(GTK_TYPE_WINDOW, true, STATE_NORMAL, disabled); + colors[TEXT_BACKGROUND] = get_color(GTK_TYPE_ENTRY, false, STATE_NORMAL, disabled); + colors[TEXT_FOREGROUND] = get_color(GTK_TYPE_ENTRY, true, STATE_NORMAL, disabled); + colors[SELECTED_BACKGROUND] = get_color(GTK_TYPE_ENTRY, false, STATE_SELECTED, disabled); + colors[SELECTED_FOREGROUND] = get_color(GTK_TYPE_ENTRY, true, STATE_SELECTED, disabled); + colors[BUTTON_BACKGROUND] = get_color(GTK_TYPE_BUTTON, false, STATE_NORMAL, disabled); + colors[BUTTON_FOREGROUND] = get_color(GTK_TYPE_BUTTON, true, STATE_NORMAL, disabled); + colors[TOOLTIP_BACKGROUND] = get_color(GTK_TYPE_TOOLTIP, false, STATE_NORMAL, disabled); + colors[TOOLTIP_FOREGROUND] = get_color(GTK_TYPE_TOOLTIP, true, STATE_NORMAL, disabled); + #ifdef GTK3 + #if GTK_CHECK_VERSION(3, 12, 0) + colors[LINK_FOREGROUND] = get_color(GTK_TYPE_LINK_BUTTON, true, STATE_LINK, disabled); + colors[VISITED_FOREGROUND] = get_color(GTK_TYPE_LINK_BUTTON, true, (STATE_T)((int)STATE_LINK + (int)STATE_VISITED), disabled); + #else + colors[LINK_FOREGROUND] = get_color(GTK_TYPE_LINK_BUTTON, true, STATE_NORMAL, disabled); + colors[VISITED_FOREGROUND] = IMAGE.DarkerColor(_colors[LINK_FOREGROUND]); + #endif + #else + colors[LINK_FOREGROUND] = IMAGE.LighterColor(_colors[SELECTED_BACKGROUND]); + colors[VISITED_FOREGROUND] = IMAGE.DarkerColor(_colors[LINK_FOREGROUND]); + #endif + colors[LIGHT_BACKGROUND] = IMAGE.MergeColor(_colors[SELECTED_BACKGROUND], _colors[SELECTED_FOREGROUND], 0.3); + colors[LIGHT_FOREGROUND] = IMAGE.MergeColor(_colors[BACKGROUND], _colors[FOREGROUND], 0.3); +} + +gColor gDesktop::getColor(int color, bool disabled) +{ + if (!_colors_valid) + { + calc_colors(_colors, false); + calc_colors(_colors_disabled, true); + _colors_valid = true; + } + + return disabled ? _colors_disabled[color] : _colors[color]; +} + +void gDesktop::screenResolution(int screen, double *x, double *y) +{ + GdkRectangle rect; + + if (screen < 0 || screen >= count()) + { + if (*x) *x = 0; + if (*y) *y = 0; + return; + } + +#if GTK_CHECK_VERSION(3, 22, 0) + GdkMonitor *monitor = gdk_display_get_monitor(gdk_display_get_default(), screen); + gdk_monitor_get_geometry(monitor, &rect); + if (x) *x = rect.width / (gdk_monitor_get_width_mm(monitor) / 25.4); + if (y) *y = rect.height / (gdk_monitor_get_height_mm(monitor) / 25.4); +#else + gdk_screen_get_monitor_geometry(gdk_screen_get_default(), screen, &rect); + if (x) *x = rect.width / (gdk_screen_get_monitor_width_mm(gdk_screen_get_default(), screen) / 25.4); + if (y) *y = rect.height / (gdk_screen_get_monitor_height_mm(gdk_screen_get_default(), screen) / 25.4); +#endif +} diff --git a/gb.gtk/src/gdesktop.h b/gb.gtk/src/gdesktop.h new file mode 100644 index 00000000..57a3bf52 --- /dev/null +++ b/gb.gtk/src/gdesktop.h @@ -0,0 +1,88 @@ +/*************************************************************************** + + gdesktop.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDESKTOP_H +#define __GDESKTOP_H + +class gPicture; +class gMainWindow; +class gControl; +class gFont; + +class gDesktop +{ +public: + + enum { + BACKGROUND, + FOREGROUND, + TEXT_BACKGROUND, + TEXT_FOREGROUND, + SELECTED_BACKGROUND, + SELECTED_FOREGROUND, + BUTTON_BACKGROUND, + BUTTON_FOREGROUND, + LIGHT_BACKGROUND, + LIGHT_FOREGROUND, + TOOLTIP_BACKGROUND, + TOOLTIP_FOREGROUND, + LINK_FOREGROUND, + VISITED_FOREGROUND, + NUM_COLORS + }; + + static gFont* font() { return gFont::desktopFont(); } + static void setFont(gFont *vl) { gFont::setDesktopFont(vl); } + static int scale() { return gFont::desktopScale(); } + + static int height(); + static int width(); + static int resolution(); + static gPicture* screenshot(int x = 0, int y = 0, int w = 0, int h = 0); + static gMainWindow* activeWindow(); + + static bool rightToLeft(); + + static int count(); + static void geometry(int screen, GdkRectangle *rect); + static void availableGeometry(int screen, GdkRectangle *rect); + + static void geometry(GdkRectangle *rect) { geometry(0, rect); } + static void availableGeometry(GdkRectangle *rect) { availableGeometry(0, rect); } + + static void screenResolution(int screen, double *x, double *y); + + static gColor getColor(int color, bool disabled = false); + + static void onThemeChange(); + +private: + + static bool _colors_valid; + static gColor _colors[NUM_COLORS]; + static gColor _colors_disabled[NUM_COLORS]; + + static void calc_colors(gColor colors[], bool disabled); +}; + +#endif diff --git a/gb.gtk/src/gdialog.cpp b/gb.gtk/src/gdialog.cpp new file mode 100644 index 00000000..4603bac0 --- /dev/null +++ b/gb.gtk/src/gdialog.cpp @@ -0,0 +1,598 @@ +/*************************************************************************** + + gdialog.cpp + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gdialog.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gapplication.h" + +static gColor _color = 0; +static char *_path = NULL; +static char **_paths = NULL; +static char *_title = NULL; +static gFont *_font = NULL; +static bool _show_hidden = false; +static GPtrArray *_filter = NULL; +static int _filter_index = -1; +static bool _filter_index_set = false; + +static int run_dialog(GtkDialog *window) +{ + gMainWindow *active; + GtkWindowGroup *oldGroup; + int ret; + bool busy; + + active = gDesktop::activeWindow(); + if (active) + gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(active->border)); + + //gApplication::setActiveControl(gApplication::activeControl(), false); + //GB.CheckPost(); + + busy = gApplication::isBusy(); + gApplication::setBusy(false); + + gtk_window_present(GTK_WINDOW(window)); + oldGroup = gApplication::enterGroup(); + gApplication::_loopLevel++; + (*gApplication::onEnterEventLoop)(); + ret = gtk_dialog_run(window); + (*gApplication::onLeaveEventLoop)(); + gApplication::_loopLevel--; + gApplication::exitGroup(oldGroup); + + gApplication::setBusy(busy); + + return ret; +} + +//------------------------------------------------------------------------- + + +static void free_path(void) +{ + if (_path) + { + g_free(_path); + _path = NULL; + } + + if (_paths) + { + int i = 0; + + while (_paths[i]) + { + g_free(_paths[i]); + i++; + } + g_free(_paths); + _paths=NULL; + } +} + +static void set_filters(GtkFileChooser* ch) +{ + char **filters; + int nfilters; + int i, p; + GString *name; + char *filter; + char **patterns; + GtkFileFilter *ft; + GtkFileFilter *select = NULL; + int index; + + filters = gDialog::filter(&nfilters); + if (!nfilters) + return; + + if (_filter_index_set) + { + _filter_index_set = false; + index = _filter_index; + } + else + index = -1; + + for (i = 0; i < nfilters / 2; i ++) + { + filter = filters[i * 2]; + if (filter && !strcmp(filter, "*")) + continue; + + ft = gtk_file_filter_new(); + + name = g_string_new(filters[i * 2 + 1]); + g_string_append_printf(name, " (%s)", filter); + gtk_file_filter_set_name(ft, name->str); + g_string_free(name, true); + + patterns = g_strsplit(filter, ";", 0); + for (p = 0; patterns[p]; p++) + gtk_file_filter_add_pattern(ft, patterns[p]); + + g_strfreev(patterns); + + gtk_file_chooser_add_filter(ch, ft); + + if (i == index) + select = ft; + } + + ft = gtk_file_filter_new(); + + name = g_string_new(GB.Translate("All files")); + g_string_append(name, " (*)"); + gtk_file_filter_set_name(ft, name->str); + g_string_free(name, true); + + gtk_file_filter_add_pattern(ft, "*"); + gtk_file_chooser_add_filter(ch, ft); + + if (!select) + select = ft; + + gtk_file_chooser_set_filter(ch, select); +} + +static void find_filter(GtkFileChooser*ch) +{ + GtkFileFilter *ft = gtk_file_chooser_get_filter(ch); + GSList *lft; + + if (ft) + { + lft = gtk_file_chooser_list_filters(ch); + if (lft) + { + _filter_index = g_slist_index(lft, (gconstpointer)ft); + if (_filter_index >= 0 && _filter_index < ((int)g_slist_length(lft) - 1)) + return; + } + } + + _filter_index = -1; +} + +static bool run_file_dialog(GtkFileChooserDialog *msg) +{ + GSList *names,*iter; + char *buf; + long b=0; + + set_filters((GtkFileChooser*)msg); + + if (run_dialog(GTK_DIALOG(msg)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return true; + } + + free_path(); + + names=gtk_file_chooser_get_filenames((GtkFileChooser*)msg); + + if (names) + { + buf=(char*)names->data; + if (buf) { + _path=(char*)g_malloc( sizeof(char)*(strlen(buf)+1) ); + strcpy(_path,buf); + } + + b=0; + _paths=(char**)g_malloc(sizeof(char*)*(g_slist_length(names)+1) ); + _paths[g_slist_length(names)]=NULL; + iter=names; + while(iter) + { + buf=(char*)iter->data; + _paths[b]=(char*)g_malloc( sizeof(char)*(strlen(buf)+1) ); + strcpy(_paths[b++],buf); + iter=iter->next; + } + + g_slist_free(names); + } + + find_filter((GtkFileChooser *)msg); + + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return false; +} + +void gDialog::exit() +{ + free_path(); + + gDialog::setFilter(NULL, 0); + gFont::assign(&_font); +} + +gFont* gDialog::font() +{ + return _font; +} + +void gDialog::setFont(gFont *ft) +{ + gFont::set(&_font, ft->copy()); +} + +gColor gDialog::color() +{ + return _color; +} + +void gDialog::setColor(gColor col) +{ + _color=col; +} + +char* gDialog::title() +{ + return _title; +} + +void gDialog::setTitle(char *vl) +{ + if (_title) + { + g_free(_title); + _title=NULL; + } + + if (vl && *vl) + _title = g_strdup(vl); +} + +char *gDialog::path() +{ + return _path; +} + +char **gDialog::paths() +{ + return _paths; +} + +void gDialog::setPath(char *vl) +{ + if (_path) + { + g_free(_path); + _path=NULL; + } + + if (!vl) return; + + _path=(char*)g_malloc( sizeof(char)*(strlen(vl)+1) ); + strcpy(_path,vl); +} + +bool gDialog::showHidden() +{ + return _show_hidden; +} + +void gDialog::setShowHidden(bool v) +{ + _show_hidden = v; +} + +char** gDialog::filter(int *nfilter) +{ + if (!_filter) + { + *nfilter = 0; + return NULL; + } + + *nfilter = _filter->len; + return (char **)(_filter->pdata); +} + +void gDialog::setFilter(char** filter, int nfilter) +{ + int i; + + if (_filter) + { + for (i = 0; i < (int)_filter->len; i++) + g_free(g_ptr_array_index(_filter, i)); + + g_ptr_array_free(_filter, true); + _filter = NULL; + } + + if (!filter) + return; + + _filter = g_ptr_array_new(); + for (i = 0; i < nfilter; i++) + g_ptr_array_add(_filter, (gpointer)g_strdup(filter[i])); +} + +bool gDialog::openFile(bool multi) +{ + GtkFileChooserDialog *msg; + + msg = (GtkFileChooserDialog*)gtk_file_chooser_dialog_new( + _title ? _title : GB.Translate("Open file"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, +#ifdef GTK3 + GB.Translate("Cancel"), GTK_RESPONSE_CANCEL, GB.Translate("Open"), GTK_RESPONSE_OK, +#else + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, +#endif + (void *)NULL); + + gtk_file_chooser_set_local_only((GtkFileChooser*)msg, true); + gtk_file_chooser_set_select_multiple((GtkFileChooser*)msg, multi); + gtk_widget_show(GTK_WIDGET(msg)); + gtk_file_chooser_unselect_all((GtkFileChooser*)msg); + + if (_path) + { + if (g_file_test(_path, G_FILE_TEST_IS_DIR)) + gtk_file_chooser_set_current_folder((GtkFileChooser*)msg, _path); + else + gtk_file_chooser_select_filename((GtkFileChooser*)msg, _path); + } + + gtk_file_chooser_set_show_hidden((GtkFileChooser*)msg, _show_hidden); + + return run_file_dialog(msg); +} + +bool gDialog::saveFile() +{ + GtkFileChooserDialog *msg; + + msg = (GtkFileChooserDialog*)gtk_file_chooser_dialog_new( + _title ? _title : GB.Translate("Save file"), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, +#ifdef GTK3 + GB.Translate("Cancel"), GTK_RESPONSE_CANCEL, GB.Translate("Save"), GTK_RESPONSE_OK, +#else + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, +#endif + (void *)NULL); + + gtk_file_chooser_set_do_overwrite_confirmation((GtkFileChooser*)msg, true); + gtk_file_chooser_set_local_only((GtkFileChooser*)msg, true); + gtk_file_chooser_set_select_multiple((GtkFileChooser*)msg, false); + gtk_widget_show(GTK_WIDGET(msg)); + gtk_file_chooser_unselect_all((GtkFileChooser*)msg); + + if (_path) + { + if (*_path && _path[strlen(_path) - 1] == '/' && g_file_test(_path, G_FILE_TEST_IS_DIR)) + gtk_file_chooser_set_current_folder((GtkFileChooser*)msg, _path); + else + gtk_file_chooser_select_filename((GtkFileChooser*)msg, _path); + } + + gtk_file_chooser_set_show_hidden((GtkFileChooser*)msg, _show_hidden); + + return run_file_dialog(msg); +} + +bool gDialog::selectFolder() +{ + GtkFileChooserDialog *msg; + + msg = (GtkFileChooserDialog*)gtk_file_chooser_dialog_new( + _title ? _title : GB.Translate("Select directory"), + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, +#ifdef GTK3 + GB.Translate("Cancel"), GTK_RESPONSE_CANCEL, GB.Translate("Open"), GTK_RESPONSE_OK, +#else + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, +#endif + (void *)NULL); + + gtk_file_chooser_set_local_only((GtkFileChooser*)msg, true); + gtk_file_chooser_set_select_multiple((GtkFileChooser*)msg, false); + gtk_widget_show(GTK_WIDGET(msg)); + gtk_file_chooser_unselect_all((GtkFileChooser*)msg); + if (_path) + { + if (g_file_test(_path, G_FILE_TEST_IS_DIR)) + gtk_file_chooser_set_current_folder((GtkFileChooser*)msg, _path); + else + gtk_file_chooser_select_filename((GtkFileChooser*)msg, _path); + } + + gtk_file_chooser_set_show_hidden((GtkFileChooser*)msg, _show_hidden); + + return run_file_dialog(msg); +} + +#ifdef GTK3 + +GType type1, type2; + +bool gDialog::selectFont() +{ + GtkFontChooserDialog *dialog; + PangoFontDescription *desc; + gFont *font; + + type1 = pango_font_family_get_type(); + type2 = pango_font_face_get_type(); + + dialog = (GtkFontChooserDialog *)gtk_font_chooser_dialog_new(_title, NULL); + + if (_font) + gtk_font_chooser_set_font_desc(GTK_FONT_CHOOSER(dialog), _font->desc()); + + if (run_dialog(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + return true; + } + + desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog)); + + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + + font = new gFont(desc); + setFont(font); + gFont::assign(&font); + + pango_font_description_free(desc); + + //printf("-> %s/%s/%s/%d\n", _font->name(), _font->bold() ? "BOLD" : "", _font->italic() ? "ITALIC" : "", _font->size()); + + return false; +} +#else +bool gDialog::selectFont() +{ + GtkFontSelectionDialog *msg; + PangoFontDescription *desc; + char *buf; + gFont *font; + + if (_title) + msg=(GtkFontSelectionDialog*)gtk_font_selection_dialog_new (_title); + else + msg=(GtkFontSelectionDialog*)gtk_font_selection_dialog_new ("Select Font"); + + + if (_font) + { + desc = _font->desc(); + buf = pango_font_description_to_string(desc); + gtk_font_selection_dialog_set_font_name(msg, buf); + g_free(buf); + } + + if (run_dialog(GTK_DIALOG(msg)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return true; + } + + buf = gtk_font_selection_dialog_get_font_name(msg); + desc = pango_font_description_from_string(buf); + g_free(buf); + + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + + font = new gFont(desc); + setFont(font); + gFont::assign(&font); + + pango_font_description_free(desc); + + //printf("-> %s/%s/%s/%d\n", _font->name(), _font->bold() ? "BOLD" : "", _font->italic() ? "ITALIC" : "", _font->size()); + + return false; +} +#endif + +#ifdef GTK3 +bool gDialog::selectColor() +{ + GtkColorChooserDialog *dialog; + GdkRGBA color; + + gt_color_to_frgba(_color, &color.red, &color.green, &color.blue, &color.alpha); + + dialog = (GtkColorChooserDialog *)gtk_color_chooser_dialog_new(_title, NULL); + + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color); + + gtk_window_present(GTK_WINDOW(dialog)); + + if (run_dialog(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + return true; + } + + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color); + _color = gt_frgba_to_color(color.red, color.green, color.blue, color.alpha); + + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + return false; +} +#else +bool gDialog::selectColor() +{ + GtkColorSelectionDialog *msg; + GdkColor gcol; + + fill_gdk_color(&gcol, _color); + + if (_title) + msg=(GtkColorSelectionDialog*)gtk_color_selection_dialog_new (_title); + else + msg=(GtkColorSelectionDialog*)gtk_color_selection_dialog_new(GB.Translate("Select Color")); + + gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(msg)), &gcol); + + gtk_window_present(GTK_WINDOW(msg)); + if (run_dialog(GTK_DIALOG(msg)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return true; + } + + gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(msg)), &gcol); + + _color = gt_gdkcolor_to_color(&gcol); + + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return false; +} +#endif + +int gDialog::filterIndex() +{ + return _filter_index; +} + +void gDialog::setFilterIndex(int index) +{ + _filter_index = index; + _filter_index_set = true; +} diff --git a/gb.gtk/src/gdialog.h b/gb.gtk/src/gdialog.h new file mode 100644 index 00000000..c2e5289a --- /dev/null +++ b/gb.gtk/src/gdialog.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + gdialog.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDIALOG_H +#define __GDIALOG_H + +#include "gcolor.h" + +class gDialog +{ +public: + static void exit(); + + static gColor color(); + static char** filter(int *nfilter); + static int filterIndex(); + static char** paths(); + static char* path(); + static char* title(); + static gFont* font(); + static bool showHidden(); + + static void setColor(gColor col); + static void setFilter(char **filter, int nfilter); + static void setFilterIndex(int index); + static void setPath(char *vl); + static void setTitle(char *title); + static void setFont(gFont *ft); + static void setShowHidden(bool v); + + static bool selectColor(); + static bool selectFolder(); + static bool selectFont(); + static bool openFile(bool multi=false); + static bool saveFile(); +}; + +#endif diff --git a/gb.gtk/src/gdrag.cpp b/gb.gtk/src/gdrag.cpp new file mode 100644 index 00000000..88a6ad66 --- /dev/null +++ b/gb.gtk/src/gdrag.cpp @@ -0,0 +1,815 @@ +/*************************************************************************** + + gdrag.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gmainwindow.h" +#include "gmouse.h" +#include "gclipboard.h" +#include "gdrag.h" + +//#define DEBUG_ME 1 + +#ifdef GTK3 +struct _GtkTargetList +{ + GList *list; + guint ref_count; +}; + +#if GTK_CHECK_VERSION(3, 12, 0) +#else +struct _GtkTargetPair +{ + GdkAtom target; + guint flags; + guint info; +}; +#endif + +typedef + struct _GtkTargetPair GtkTargetPair; +#endif + +static int _current_clipboard = gClipboard::Clipboard; +static GtkClipboard *_clipboard = NULL; +static GtkClipboard *_selection = NULL; +static bool _clipboard_has_changed[2] = { TRUE }; + +static char *convert_format(char *fmt) +{ + if (!strcmp(fmt, "STRING")) + return (char *)"text/plain"; + if (!strcmp(fmt, "UTF8_STRING")) + return (char *)"text/plain;charset=utf-8"; + return fmt; +} + +#if GTK_CHECK_VERSION(2, 14, 0) +#else +static gint gtk_selection_data_get_length(const GtkSelectionData *sel) +{ + return sel->length; +} + +static const guchar *gtk_selection_data_get_data(const GtkSelectionData *sel) +{ + return sel->data; +} +#endif + +#if GTK_CHECK_VERSION(2, 22, 0) +#else +static GList *gdk_drag_context_list_targets(GdkDragContext *context) +{ + return context->targets; +} +#endif + +/*********************************************************************** + + Clipboard + +************************************************************************/ + +static void cb_change(GtkClipboard *clipboard, GdkEvent *, gpointer) +{ + _clipboard_has_changed[clipboard == _selection ? gClipboard::Clipboard : gClipboard::Selection] = TRUE; +} + + +static GtkClipboard *get_clipboard() +{ + if (_current_clipboard == gClipboard::Selection) + { + if (!_selection) + { + _selection = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + g_signal_connect(G_OBJECT(_selection), "owner-change", G_CALLBACK(cb_change), (gpointer)gClipboard::Clipboard); + } + return _selection; + } + else + { + if (!_clipboard) + { + _clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + g_signal_connect(G_OBJECT(_clipboard), "owner-change", G_CALLBACK(cb_change), (gpointer)gClipboard::Selection); + } + return _clipboard; + } +} + +bool gClipboard::hasChanged() +{ + return _clipboard_has_changed[_current_clipboard]; +} + +void gClipboard::setCurrent(int clipboard) +{ + _current_clipboard = clipboard; +} + +int gClipboard::getCurrent() +{ + return _current_clipboard; +} + +void gClipboard::clear() +{ + if (gApplication::isInit()) + gtk_clipboard_clear(get_clipboard()); +} + +char *gClipboard::getFormat(int n) +{ + int i; + gint n_tg; + GdkAtom *targets; + char *fmt, *cfmt; + + if (!gtk_clipboard_wait_for_targets(get_clipboard(), &targets, &n_tg)) + return NULL; + + for (i = 0; i < n_tg; i++) + { + fmt = gdk_atom_name(targets[i]); + cfmt = convert_format(fmt); + if (!islower(cfmt[0])) + { + g_free(fmt); + continue; + } + if (n == 0) + { + gt_free_later(fmt); + return cfmt; + } + n--; + } + + return NULL; +} + +static void cb_clear_text(GtkClipboard *clipboard, gpointer text) +{ + if (text) g_free(text); +} + +static void cb_get_text(GtkClipboard *clipboard, GtkSelectionData *selection, guint info, gpointer text) +{ + gtk_selection_data_set(selection, gtk_selection_data_get_target(selection), 8, (guchar *)text, strlen((char *)text)); +} + +int gClipboard::getType() +{ + if (gtk_clipboard_wait_is_image_available(get_clipboard())) return Image; + if (gtk_clipboard_wait_is_text_available(get_clipboard())) return Text; + return Nothing; +} + +void gClipboard::setImage(gPicture *image) +{ + gtk_clipboard_set_image(get_clipboard(), image->getPixbuf()); +} + +gPicture * gClipboard::getImage() +{ + _clipboard_has_changed[_current_clipboard] = FALSE; + return new gPicture(gtk_clipboard_wait_for_image(get_clipboard())); +} + +static void +gt_clipboard_set_text (GtkClipboard *clipboard, char *format, const gchar *text, gint len) +{ + GtkTargetList *list; + GList *l; + GtkTargetEntry *targets; + gint n_targets, i; + + list = gtk_target_list_new (NULL, 0); + if (format) + gtk_target_list_add (list, gdk_atom_intern(format, false), 0, 0); + gtk_target_list_add_text_targets (list, 0); + + n_targets = g_list_length(list->list); + targets = g_new0 (GtkTargetEntry, n_targets); + for (l = list->list, i = 0; l; l = l->next, i++) + { + GtkTargetPair *pair = (GtkTargetPair *)l->data; + targets[i].target = gdk_atom_name (pair->target); + } + + if (len < 0) + len = strlen (text); + + gtk_clipboard_set_with_data (clipboard, + targets, n_targets, + cb_get_text, cb_clear_text, + g_strndup (text, len)); + gtk_clipboard_set_can_store (clipboard, NULL, 0); + + for (i = 0; i < n_targets; i++) + g_free (targets[i].target); + g_free (targets); + gtk_target_list_unref (list); +} + +void gClipboard::setText(char *text, int len, char *format) +{ + if (!text) + return; + + gt_clipboard_set_text(get_clipboard(), format, text, len); +} + +char *gClipboard::getText(int *len, const char *format) +{ + GdkAtom target = GDK_NONE; + gint n_tg; + GdkAtom *targets; + char *fmt; + int i; + GtkSelectionData *data; + char *text; + + *len = 0; + + if (!gtk_clipboard_wait_for_targets(get_clipboard(), &targets, &n_tg) || n_tg <= 0) + return NULL; + + if (format && !strcmp(format, "text/plain")) + format = "text/plain;charset=utf-8"; + + for (i = 0; i < n_tg; i++) + { + target = targets[i]; + fmt = convert_format(gt_free_later(gdk_atom_name(target))); + if (!islower(fmt[0])) + continue; + if (!format && !strncasecmp(fmt, "text/", 5) && strcasecmp(fmt, "text/plain")) + break; + if (format && !strcasecmp(fmt, format)) + break; + } + + if (i >= n_tg) + return NULL; + + if (!gtk_clipboard_wait_is_target_available(get_clipboard(), target)) + return NULL; + + data = gtk_clipboard_wait_for_contents(get_clipboard(), target); + *len = gtk_selection_data_get_length(data); + text = (char *)g_malloc(*len); + memcpy(text, gtk_selection_data_get_data(data), *len); + + gtk_selection_data_free(data); + + _clipboard_has_changed[_current_clipboard] = FALSE; + + return gt_free_later(text); +} + +/*********************************************************************** + + Drag & Drop + +************************************************************************/ + +bool gDrag::_active = false; +gPicture *gDrag::_icon = NULL; +int gDrag::_icon_x = 0; +int gDrag::_icon_y = 0; +gControl *gDrag::_source = NULL; +gControl *gDrag::_destination = NULL; +gControl *gDrag::_dest = NULL; +int gDrag::_action = 0; +int gDrag::_type = 0; +gPicture *gDrag::_picture = NULL; +char *gDrag::_text = NULL; +int gDrag::_text_len = 0; +char *gDrag::_format = NULL; +int gDrag::_enabled = 0; +int gDrag::_x = -1; +int gDrag::_y = -1; +GdkDragContext *gDrag::_context = NULL; +guint32 gDrag::_time = 0; +bool gDrag::_local = false; +volatile bool gDrag::_got_data = false; +volatile bool gDrag::_end = false; +gControl *gDrag::_current = NULL; + +void gDrag::setIcon(gPicture *vl) +{ + gPicture::assign(&_icon, vl); +} + +void gDrag::cancel() +{ + #if DEBUG_ME + fprintf(stderr, "gDrag::cancel\n"); + #endif + + hide(); + setIcon(NULL); + setDropText(NULL); + setDropImage(NULL); + g_free(_format); + _format = NULL; + _source = NULL; + _destination = NULL; + _dest = NULL; + _type = Nothing; + _x = _y = -1; + _time = 0; + _got_data = false; + _local = false; + _active = false; + + gApplication::setButtonGrab(NULL); + +#if DEBUG_ME + fprintf(stderr, "_active -> false\n"); +#endif +} + +void gDrag::exit() +{ + cancel(); +} + +gControl *gDrag::drag(gControl *source, GtkTargetList *list) +{ + GdkDragContext *ct; + gControl *dest; + int button; + + #if DEBUG_ME + fprintf(stderr, "gDrag::drag: source = %p list = %p event = %p\n", source, list, gApplication::lastEvent()); + #endif + + button = gMouse::left() ? 1 : gMouse::middle() ? 2 : gMouse::right() ? 3 : 0; + +#if GTK_CHECK_VERSION(3, 10, 0) + ct = gtk_drag_begin_with_coordinates(source->border, list, GDK_ACTION_MOVE, button, gApplication::lastEvent(), -1, -1); +#else + ct = gtk_drag_begin(source->border, list, GDK_ACTION_MOVE, button, gApplication::lastEvent()); +#endif + if (!ct) + return NULL; + + _local = true; + _active = true; + +#if DEBUG_ME + fprintf(stderr, "gDrag::drag: begin\n"); +#endif + + if (_icon) + { + GdkPixbuf *icon = _icon->getIconPixbuf(); + gtk_drag_set_icon_pixbuf(ct, icon, _icon_x, _icon_y); + if (icon != _icon->getPixbuf()) + g_object_unref(G_OBJECT(icon)); + } + + source->_dragging = true; + + _end = false; + while (!_end) + MAIN_do_iteration(true); + + source->_dragging = false; + + gtk_target_list_unref(list); + + dest = _destination; + cancel(); + +#if DEBUG_ME + fprintf(stderr, "gDrag::drag: end\n"); +#endif + + return dest; +} + +gControl *gDrag::dragText(gControl *source, char *text, char *format) +{ + GtkTargetList *list; + + //cancel(); + + #if DEBUG_ME + fprintf(stderr, "gDrag::dragText: %s\n", text); + #endif + + setDropText(text); + + list = gtk_target_list_new (NULL, 0); + if (format) + gtk_target_list_add(list, gdk_atom_intern(format, false), 0, 0); + gtk_target_list_add_text_targets(list, 0); + + //gtk_target_list_add (list,gdk_atom_intern("UTF8_STRING",false),0,0); + //gtk_target_list_add (list,gdk_atom_intern("COMPOUND_TEXT",false),0,0); + //gtk_target_list_add (list,gdk_atom_intern("TEXT",false),0,0); + //gtk_target_list_add (list,GDK_TARGET_STRING,0,0); + //gtk_target_list_add (list,gdk_atom_intern("text/plain;charset=utf-8",false),0,0); + //gtk_target_list_add (list,gdk_atom_intern("text/plain",false),0,0); + + setDropInfo(Text, format); + + return drag(source, list); +} + +gControl *gDrag::dragImage(gControl *source, gPicture *image) +{ + GtkTargetList *list; + + setDropImage(image); + + list = gtk_target_list_new (NULL,0); + + gtk_target_list_add(list, gdk_atom_intern("image/png", false), 0, 0); + gtk_target_list_add(list, gdk_atom_intern("image/jpg", false), 0, 0); + gtk_target_list_add(list, gdk_atom_intern("image/jpeg", false), 0, 0); + gtk_target_list_add(list, gdk_atom_intern("image/gif", false), 0, 0); + + setDropInfo(Image, NULL); + + return drag(source, list); +} + +void gDrag::setDropInfo(int type, char *format) +{ + _type = type; + g_free(_format); + _format = g_strdup(format); +} + + +void gDrag::setDropData(int action, int x, int y, gControl *source, gControl *dest) +{ + #if DEBUG_ME + fprintf(stderr, "gDrag::setDropData: action = %d x = %d y = %d source = %p\n", action, x, y, source); + #endif + + _x = x; + _y = y; + _action = action; + _source = source; + _destination = dest; + _active = true; +} + +void gDrag::setDropText(char *text, int len) +{ + #if DEBUG_ME + fprintf(stderr, "gDrag::setDropText: text = '%s' %d\n", text ? text : "(null)", len); + #endif + + g_free(_text); + if (text) + { + if (len < 0) len = strlen(text); + _text_len = len; + _text = (char *)g_malloc(len); + memcpy(_text, text, len); + } + else + { + _text = NULL; + _text_len = 0; + } +} + +void gDrag::setDropImage(gPicture* image) +{ + //g_debug("gDrag::setDropImage: image = %p\n", image); + gPicture::assign(&_picture, image); +} + +void gDrag::setDropImage(char *buf, int len) +{ + GdkPixbufLoader *ld; + GdkPixbuf *pixbuf = NULL; + + //g_debug("gDrag::setDropImage: buf = %p len = %d\n", buf, len); + + if (buf && len > 0) + { + ld = gdk_pixbuf_loader_new (); + if (gdk_pixbuf_loader_write(ld, (const guchar*)buf, len, NULL)) + { + gdk_pixbuf_loader_close (ld, NULL); + pixbuf = gdk_pixbuf_loader_get_pixbuf(ld); + } + g_object_unref(G_OBJECT(ld)); + } + + if (pixbuf) + setDropImage(new gPicture(pixbuf)); + else + setDropImage(NULL); +} + +bool gDrag::checkThreshold(gControl *control, int x, int y, int sx, int sy) +{ + if (_active) + return false; + else + return gtk_drag_check_threshold(control->border, sx, sy, x, y); +} + +GdkDragContext *gDrag::enable(GdkDragContext *context, gControl *control, guint32 time) +{ + GdkDragContext *old = _context; + + #if DEBUG_ME + fprintf(stderr, "gDrag::enable: %p -> %p\n", old, context); + #endif + + _enabled++; + _context = context; + _time = time; + _dest = control; + return old; +} + +GdkDragContext *gDrag::disable(GdkDragContext *context) +{ + GdkDragContext *old = _context; + + #if DEBUG_ME + fprintf(stderr, "gDrag::disable: %p -> %p\n", old, context); + #endif + + _context = context; + _enabled--; + return old; +} + +void gDrag::show(gControl *control, int x, int y, int w, int h) +{ + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_ShowDNDFrame", NULL, NULL); + init = TRUE; + } + + GB.Push(5, GB_T_OBJECT, control->hFree, GB_T_INTEGER, x, GB_T_INTEGER, y, GB_T_INTEGER, w, GB_T_INTEGER, h); + GB.Call(&func, 5, FALSE); +} + +void gDrag::hide(gControl *control) +{ + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_HideDNDFrame", NULL, NULL); + init = TRUE; + } + + GB.Push(1, GB_T_OBJECT, control ? control->hFree : NULL); + GB.Call(&func, 1, FALSE); +} + + +char *gDrag::getFormat(int n) +{ + GList *tg; + gchar *format, *cformat; + + //if (gDrag::getType()) // local DnD + // return; + + //g_debug("set_from_context: non local\n"); + + if (_format) + return n == 0 ? _format : NULL; + + if (!_context) + return NULL; + + tg = g_list_first(gdk_drag_context_list_targets(_context)); + + while (tg) + { + format = gdk_atom_name((GdkAtom)tg->data); + cformat = convert_format(format); + + if (islower(cformat[0])) + { + if (n <= 0) + { + gt_free_later(format); + return cformat;; + } + n--; + } + + g_free(format); + tg = g_list_next(tg); + } + + return NULL; +} + +int gDrag::getType() +{ + int i; + char *format; + + if (_type) + return _type; + + for (i = 0;; i++) + { + format = getFormat(i); + if (!format) + return Nothing; + if (strlen(format) >= 5 && !strncasecmp(format, "text/", 5)) + return Text; + if (strlen(format) >= 6 &&! strncasecmp(format, "image/", 6)) + return Image; + } +} + +static void cb_drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *sel, guint info, guint time, gControl *data) +{ +#if DEBUG_ME + fprintf(stderr, "cb_drag_data_received\n"); +#endif + + if (gDrag::getType() == gDrag::Text) + { + if (gtk_selection_data_get_length(sel) != -1) + gDrag::setDropText((char*)gtk_selection_data_get_data(sel), gtk_selection_data_get_length(sel)); + else + gDrag::setDropText(NULL); + } + + if (gDrag::getType() == gDrag::Image) + { + //fprintf(stderr, "Image\n"); + if (gtk_selection_data_get_length(sel) != -1) + gDrag::setDropImage((char*)gtk_selection_data_get_data(sel), gtk_selection_data_get_length(sel)); + else + gDrag::setDropImage(NULL); + } + + gDrag::_got_data = true; +} + + +bool gDrag::getData(const char *prefix) +{ + GList *tg; + gchar *format = NULL; + char *cfmt; + gulong id; + static bool norec = false; + gControl *dest; + +#if DEBUG_ME + fprintf(stderr, "getData: norec = %d _local = %d\n", norec, _local); +#endif + + if (norec || _local) // local DnD + return false; + + tg = g_list_first(gdk_drag_context_list_targets(_context)); + + while (tg) + { + g_free(format); + format = gdk_atom_name((GdkAtom)tg->data); + cfmt = convert_format(format); + //fprintf(stderr, "getData: prefix = %s format = '%s'\n", prefix, format); + + if (strlen(cfmt) >= strlen(prefix) && !strncasecmp(cfmt, prefix, strlen(prefix))) + { + g_free(format); + + dest = _dest; + + id = g_signal_connect(dest->border, "drag-data-received", G_CALLBACK(cb_drag_data_received), (gpointer)dest); + //fprintf(stderr, "gDrag::getData: g_signal_connect -> %p %ld\n", dest->border, id); + + _got_data = false; + + norec = true; + + gtk_drag_get_data (_dest->border, _context, (GdkAtom)tg->data, _time); + + while (!_got_data) + MAIN_do_iteration(true); + + norec = false; + + //fprintf(stderr, "gDrag::getData: g_signal_disconnect -> %p %ld\n", dest->border, id); + g_signal_handler_disconnect(dest->border, id); + + return false; + } + + tg = g_list_next(tg); + } + + g_free(format); + return true; +} + +char *gDrag::getText(int *len, const char *format, bool fromOutside) +{ + //setDropText(NULL); + + if (!format) + format = "text/"; + + if (!fromOutside && getData(format)) + { + *len = 0; + return NULL; + } + else + { + *len = _text_len; + return _text; + } +} + +gPicture *gDrag::getImage(bool fromOutside) +{ + if (_picture) + return _picture; + + if (!fromOutside && getData("image/")) + return NULL; + + return _picture; +} + +void gDrag::end() +{ + _end = true; + _active = false; +} + + +bool gDrag::setCurrent(gControl *control) +{ + gControl *current; + + //fprintf(stderr, "gDrag::setCurrent: %s -> %s\n", _current ? _current->name() : "NULL", control ? control->name() : "NULL"); + + if (_current == control) + return false; + + if (_current) + { + current = _current; + while (current) + { + CB_control_drag_leave(current); + current = current->_proxy_for; + } + } + + _current = control; + + while (control) + { + if (CB_control_drag(control)) + return true; + control = control->_proxy_for; + } + + return false; +} diff --git a/gb.gtk/src/gdrag.h b/gb.gtk/src/gdrag.h new file mode 100644 index 00000000..50832b37 --- /dev/null +++ b/gb.gtk/src/gdrag.h @@ -0,0 +1,122 @@ +/*************************************************************************** + + gdrag.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDRAG_H +#define __GDRAG_H + +#include "gb.form.const.h" + +class gPicture; +class gControl; + +class gDrag +{ +public: + enum + { + Nothing = 0, + Text = 1, + Image = 2 + }; + + static void exit(); + + static bool isActive() { return _active; } + static bool isEnabled() { return _enabled; } + + static void setIcon(gPicture *vl); + static gPicture *getIcon() { return _icon; } + static void getIconPos(int *x, int *y) { *x = _icon_x; *y = _icon_y; } + static void setIconPos(int x, int y) { _icon_x = x; _icon_y = y; } + + static gControl *dragText(gControl *source, char *text, char *format = 0); + static gControl *dragImage(gControl *source, gPicture *image); + static void end(); + static void cancel(); + + static gControl *getSource() { return _source; } + static gControl *getDestination() { return _destination; } + static void setDestination(gControl *dest) { _destination = dest; } + static int getAction() { return _action; } + + static int getType(); + static char *getFormat(int n = 0); + static char *getText(int *len, const char *format, bool fromOutside = false); + static gPicture *getImage(bool fromOutside = false); + + static int getDropX() { return _x; } + static int getDropY() { return _y; } + + static void setDropX(int v) { _x = v; } + static void setDropY(int v) { _y = v; } + + static void show(gControl *control, int x = 0, int y = 0, int w = -1, int h = -1); + static void hide(gControl *control = NULL); + + static bool checkThreshold(gControl *control, int x, int y, int sx, int sy); + + // "Private" + static void setDropInfo(int type, char *format); + static void setDropData(int action, int x, int y, gControl *source, gControl *dest); + static void setDropText(char *text, int len = -1); + static void setDropImage(gPicture *image); + static void setDropImage(char *buf, int len); + + static GdkDragContext *enable(GdkDragContext *context, gControl *control, guint32 time); + static GdkDragContext *disable(GdkDragContext *context); + static bool getData(const char *prefix); + + static bool setCurrent(gControl *control); + static bool isCurrent(gControl *control) { return control == _current; } + + static volatile bool _got_data; + + static gControl *_source; + static gControl *_destination; + static gControl *_dest; + static gControl *_current; + +private: + + static gControl *drag(gControl *source, GtkTargetList *list); + + static bool _active; + static gPicture *_icon; + static int _icon_x; + static int _icon_y; + static int _action; + static int _type; + static gPicture *_picture; + static char *_text; + static int _text_len; + static char *_format; + static int _enabled; + static int _x; + static int _y; + static GdkDragContext *_context; + static guint32 _time; + static bool _local; + static volatile bool _end; +}; + +#endif diff --git a/gb.gtk/src/gdrawingarea.cpp b/gb.gtk/src/gdrawingarea.cpp new file mode 100644 index 00000000..ca1b6674 --- /dev/null +++ b/gb.gtk/src/gdrawingarea.cpp @@ -0,0 +1,411 @@ +/*************************************************************************** + + gdrawingarea.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gmouse.h" +#include "gdrawingarea.h" + +#ifdef GTK3 +#define UNREF_BUFFER() (cairo_surface_destroy(buffer), buffer = NULL) +#else +#define UNREF_BUFFER() (g_object_unref(G_OBJECT(buffer)), buffer = NULL) +#endif + + +/**************************************************************************************** + +gDrawingArea Widget + +*****************************************************************************************/ + +int gDrawingArea::_in_any_draw_event = 0; + +#ifdef GTK3 +static gboolean cb_draw(GtkWidget *wid, cairo_t *cr, gDrawingArea *data) +{ + if (data->cached()) + { + cairo_set_source_surface(cr, data->buffer, 0, 0); + cairo_paint(cr); + data->drawBorder(cr); + } + else + { + data->drawBackground(cr); + + gDrawingArea::_in_any_draw_event++; + data->_in_draw_event = true; + CB_drawingarea_expose(data, cr); + gDrawingArea::_in_any_draw_event--; + data->_in_draw_event = false; + data->drawBorder(cr); + } + + return false; +} +#else +static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gDrawingArea *data) +{ + if (data->cached()) + { + data->drawBorder(e); + } + else + { + //data->drawBackground(); + + gDrawingArea::_in_any_draw_event++; + data->_in_draw_event = true; + + GtkAllocation a; + gtk_widget_get_allocation(wid, &a); + //fprintf(stderr, "%s: %d %d\n", data->parent()->name(), a.x, a.y); + + CB_drawingarea_expose(data, e->region, a.x, a.y); + gDrawingArea::_in_any_draw_event--; + data->_in_draw_event = false; + data->drawBorder(e); + } + + return false; +} +#endif + +static void cb_size(GtkWidget *wid, GtkAllocation *a, gDrawingArea *data) +{ + data->updateCache(); +} + +void gDrawingArea::create(void) +{ + int i; + GtkWidget *ch; + bool doReparent = false; + bool was_visible = isVisible(); + bool can_focus = widget ? canFocus() : false; + GdkRectangle rect; + int bg, fg; + + if (border) + { + getGeometry(&rect); + bg = background(); + fg = foreground(); + parent()->remove(this); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + g_object_ref(G_OBJECT(ch)); + gtk_container_remove(GTK_CONTAINER(widget), ch); + } + + doReparent = true; + } + +#ifdef GTK3 + if (_cached || _use_tablet || _own_window) +#else + if (_cached || _use_tablet || _own_window || background() != COLOR_DEFAULT) +#endif + { + createBorder(gtk_event_box_new()); + widget = gtk_fixed_new(); + box = widget; +#ifndef GTK3 + gtk_widget_set_app_paintable(border, TRUE); + gtk_widget_set_app_paintable(box, TRUE); +#endif + } + else + { + createBorder(gtk_fixed_new()); + widget = border; + box = NULL; + } + + realize(); + + if (_cached) + g_signal_connect(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_size), (gpointer)this); + + ON_DRAW_BEFORE(border, this, cb_expose, cb_draw); + + updateUseTablet(); + + gtk_widget_set_can_focus(widget, can_focus); + + if (doReparent) + { + if (box) + gtk_widget_realize(box); + + setBackground(bg); + setForeground(fg); + setFont(font()); + bufX = bufY = bufW = bufH = -1; + setGeometry(&rect); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + gtk_container_add(GTK_CONTAINER(widget), ch); + moveChild(child(i), child(i)->x(), child(i)->y()); + g_object_unref(G_OBJECT(ch)); + } + + if (was_visible) + show(); + else + hide(); + } +} + +gDrawingArea::gDrawingArea(gContainer *parent) : gContainer(parent) +{ + _is_drawingarea = true; + _own_window = false; + _cached = false; + buffer = NULL; + box = NULL; + _resize_cache = false; + _no_background = false; + _use_tablet = false; +#ifdef GTK3 + _no_style_without_child = true; +#endif + + create(); +} + +gDrawingArea::~gDrawingArea() +{ + if (buffer) + UNREF_BUFFER(); +} + +void gDrawingArea::setCached(bool vl) +{ + if (vl == _cached) return; + + _cached = vl; + + if (!_cached) + { + UNREF_BUFFER(); + #ifndef GTK3 + set_gdk_bg_color(border, background()); + #endif + } + + create(); + resizeCache(); +} + +void gDrawingArea::resizeCache() +{ + int bw, bh; + int w, h; +#ifdef GTK3 + cairo_surface_t *buf; +#else + GdkPixmap *buf; +#endif + GdkWindow *win; + cairo_t *cr; + + if (!_cached) + return; + + win = gtk_widget_get_window(GTK_WIDGET(box)); + if (!win) + return; + + w = width(); + h = height(); + + if (buffer) + { +#ifdef GTK3 + bw = cairo_image_surface_get_width(buffer); + bh = cairo_image_surface_get_height(buffer); +#else + gdk_drawable_get_size(buffer, &bw, &bh); +#endif + } + else + bw = bh = 0; + + if (bw != w || bh != h) + { +#ifdef GTK3 + buf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cr = cairo_create(buf); +#else + buf = gdk_pixmap_new(win, w, h, -1); + cr = gdk_cairo_create(buf); +#endif + + if (w > bw || h > bh || !buffer) + { + gt_cairo_set_source_color(cr, realBackground(true)); + cairo_rectangle(cr, 0, 0, w, h); + cairo_fill(cr); + } + + if (buffer) + { + if (bw > w) bw = w; + if (bh > h) bh = h; + //gdk_draw_drawable(buf, gc2, buffer, 0, 0, 0, 0, bw, bh); +#ifdef GTK3 + cairo_set_source_surface(cr, buffer, 0, 0); +#else + gdk_cairo_set_source_pixmap(cr, buffer, 0, 0); +#endif + cairo_rectangle(cr, 0, 0, bw, bh); + cairo_fill(cr); + + UNREF_BUFFER(); + } + + buffer = buf; + cairo_destroy(cr); + } + + //drawBorder(buffer); + refreshCache(); +} + +void gDrawingArea::setCache() +{ + if (!_cached) + return; + +#ifdef GTK3 +#else + gdk_window_set_back_pixmap(gtk_widget_get_window(box), buffer, FALSE); +#endif + refreshCache(); +} + +static gboolean resize_cache(gDrawingArea *data) +{ + //fprintf(stderr, "resize_cache\n"); + data->resizeCache(); + data->setCache(); + data->_resize_cache = false; + return false; +} + +void gDrawingArea::updateCache() +{ + if (!_cached) + return; + + if (!_resize_cache) + { + _resize_cache = true; + g_timeout_add(10, (GSourceFunc)resize_cache, (gpointer)this); + } +} + +void gDrawingArea::clear() +{ + if (_cached && buffer) + { + UNREF_BUFFER(); + resizeCache(); + setCache(); + } +} + +void gDrawingArea::refreshCache() +{ + gtk_widget_queue_draw(box); + //if (box && box->window) + // gdk_window_clear(box->window); +} + +void gDrawingArea::setNoBackground(bool vl) +{ + if (vl != _no_background) + { + _no_background = vl; + create(); + } +} + +void gDrawingArea::setRealBackground(gColor color) +{ + gControl::setRealBackground(color); + clear(); +} + +void gDrawingArea::updateUseTablet() +{ + if (_use_tablet) + gMouse::initDevices(); +#ifndef GTK3 + gtk_widget_set_extension_events(border, _use_tablet ? GDK_EXTENSION_EVENTS_CURSOR : GDK_EXTENSION_EVENTS_NONE); +#endif + //fprintf(stderr, "gtk_widget_set_extension_events: %s %p: %d\n", name(), border, _use_tablet); +} + +void gDrawingArea::setUseTablet(bool vl) +{ + if (vl == _use_tablet) + return; + _use_tablet = vl; + create(); +} + +void gDrawingArea::updateFont() +{ + gContainer::updateFont(); + CB_drawingarea_font(this); +} + +#ifdef GTK3 +#else +void gDrawingArea::setBackground(gColor color) +{ + bool set = background() != COLOR_DEFAULT; + + gContainer::setBackground(color); + + if (set != (background() != COLOR_DEFAULT)) + create(); +} +#endif + +long gDrawingArea::handle() +{ + if (!_own_window) + { + _own_window = true; + create(); + } + + return gControl::handle(); +} diff --git a/gb.gtk/src/gdrawingarea.h b/gb.gtk/src/gdrawingarea.h new file mode 100644 index 00000000..85f51cc6 --- /dev/null +++ b/gb.gtk/src/gdrawingarea.h @@ -0,0 +1,88 @@ +/*************************************************************************** + + gdrawingarea.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDRAWINGAREA_H +#define __GDRAWINGAREA_H + +class gDrawingArea : public gContainer +{ +public: + gDrawingArea(gContainer *parent); + virtual ~gDrawingArea(); + + int getBorder() const { return getFrameBorder(); } + bool cached() const { return _cached; } + bool hasNoBackground() const { return _no_background; } + bool useTablet() const { return _use_tablet; } + + void setBorder(int vl) { setFrameBorder(vl); } + void setCached(bool vl); + void setNoBackground(bool vl); + void setUseTablet(bool vl); + + bool inDrawEvent() const { return _in_draw_event; } + static bool inAnyDrawEvent() { return _in_any_draw_event; } + +//"Methods" + void clear(); +#ifndef GTK3 + virtual void setBackground(gColor color = COLOR_DEFAULT); +#endif + virtual void setRealBackground(gColor color); + virtual void updateFont(); + virtual long handle(); + + +//"Private" + void create(); + void updateCache(); + void resizeCache(); + void refreshCache(); + void setCache(); + void updateUseTablet(); + +#ifdef GTK3 + cairo_surface_t *buffer; +#else + GdkPixmap *buffer; +#endif + GtkWidget *box; + unsigned _cached : 1; + unsigned _resize_cache : 1; + unsigned _in_draw_event : 1; + unsigned _use_tablet : 1; + unsigned _own_window : 1; + static int _in_any_draw_event; +}; + + +// Callbacks + +#ifdef GTK3 + void CB_drawingarea_expose(gDrawingArea *sender, cairo_t *cr); +#else + void CB_drawingarea_expose(gDrawingArea *sender, GdkRegion *region, int dx, int dy); +#endif + void CB_drawingarea_font(gDrawingArea *sender); + +#endif diff --git a/gb.gtk/src/gfont.cpp b/gb.gtk/src/gfont.cpp new file mode 100644 index 00000000..468fda11 --- /dev/null +++ b/gb.gtk/src/gfont.cpp @@ -0,0 +1,818 @@ +/*************************************************************************** + + gfont.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "font-parser.h" +#include "gapplication.h" +#include "gdesktop.h" +#include "gtools.h" +#include "gb.form.font.h" + +#include <math.h> + +gFont *gFont::_desktop_font = NULL; +int gFont::_desktop_scale = 0; +#ifdef GTK3 +GtkStyleProvider *gFont::_desktop_css = NULL; +#endif + +static int FONT_n_families; +GList *FONT_families = NULL; +static int _nfont = 0; + +//--------------------------------------------------------------------------- + +static int count_newlines(const char *str, int len) +{ + int i, n; + + for (i = 0, n = 0; i < len; i++) + { + if (str[i] == '\n') + n++; + } + return n; +} + +//--------------------------------------------------------------------------- + +void gFont::setFromString(const char *str) +{ + gchar **tokens, **p; + gchar *copy, *elt; + int grade; + double size; + int len; + + if (!str || !*str) + return; + + tokens = g_strsplit(str, ",", 0); + + p = tokens; + for(p = tokens; *p; p++) + { + copy = g_strdup(*p); + elt = g_strstrip(copy); + + if (!strcasecmp(elt, "bold")) + setBold(true); + else if (!strcasecmp(elt, "italic")) + setItalic(true); + else if (!strcasecmp(elt, "underline")) + setUnderline(true); + else if (!strcasecmp(elt, "strikeout")) + setStrikeout(true); + else if (elt[0] == '+' || elt[0] == '-' || elt[0] == '0') + { + grade = atoi(elt); + if (grade || elt[0] == '0') + setGrade(grade); + } + else + { + size = atof(elt); + if (isdigit(*elt) && size != 0.0) + setSize(size); + else + { + setBold(false); + setItalic(false); + setUnderline(false); + setStrikeout(false); + + len = strlen(elt); + if (len > 2 && elt[0] == '"' && elt[len - 1] == '"') + { + elt[len - 1] = 0; + elt++; + } + setName(elt); + } + } + + g_free(copy); + } + + g_strfreev(tokens); +} + + +void gFont::init() +{ + PangoFontFamily **_families; + PangoContext *ct; + char *buf1,*buf2; + int bucle; + + ct = gdk_pango_context_get(); + pango_context_list_families(ct, &_families, &FONT_n_families); + + for (bucle = 0; bucle<FONT_n_families; bucle++) + { + buf1 = (char *)pango_font_family_get_name(_families[bucle]); + if (buf1) + { + buf2 = (char *)g_malloc(sizeof(char)*(strlen(buf1)+1)); + strcpy(buf2,buf1); + FONT_families = g_list_prepend (FONT_families,buf2); + } + } + + if (FONT_families) + FONT_families = g_list_sort(FONT_families, (GCompareFunc)strcasecmp); + + g_free(_families); + g_object_unref(G_OBJECT(ct)); +} + +void gFont::exit() +{ + GList *iter; + + gFont::assign(&_desktop_font); + + iter = FONT_families; + + if (iter) + { + iter = g_list_first(iter); + while (iter) + { + g_free(iter->data); + iter=iter->next; + } + } + + if (FONT_families) g_list_free(FONT_families); + + //if (_nfont) + // fprintf(stderr, "WARNING: %d gFont objects were not freed.\n", _nfont); +} + +int gFont::count() +{ + if (!FONT_families) gFont::init(); + return FONT_n_families; +} + +const char *gFont::familyItem(int pos) +{ + if (!FONT_families) gFont::init(); + if ( (pos < 0) || (pos >= FONT_n_families) ) return NULL; + + return (const char *)g_list_nth(FONT_families, pos)->data; +} + +#if 0 +void gFont::updateWidget() +{ + if (!wid) return; + + PangoFontDescription *desc = pango_context_get_font_description(ct); + gtk_widget_modify_font(wid,desc); + + if (G_OBJECT_TYPE(wid)==GTK_TYPE_LABEL) + { + PangoAttrList *pal=pango_attr_list_new(); + if (_strikeout) + { + PangoAttribute *pa=pango_attr_strikethrough_new(true); + pa->start_index = 0; + pa->end_index = g_utf8_strlen(gtk_label_get_text(GTK_LABEL(wid)), -1); + pango_attr_list_insert(pal, pa); + } + if (_underline) + { + PangoAttribute *pa=pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); + pa->start_index = 0; + pa->end_index = g_utf8_strlen(gtk_label_get_text(GTK_LABEL(wid)), -1); + pango_attr_list_insert(pal, pa); + } + gtk_label_set_attributes(GTK_LABEL(wid), pal); + pango_attr_list_unref(pal); + } + +} +#endif + +void gFont::reset() +{ + _strikeout = false; + _underline = false; + + _bold_set = false; + _italic_set = false; + _name_set = false; + _size_set = false; + _strikeout_set = false; + _underline_set = false; +} + +void gFont::setAll(bool v) +{ + _bold_set = v; + _italic_set = v; + _name_set = v; + _size_set = v; + _strikeout_set = v; + _underline_set = v; +} + +void gFont::setAllFrom(gFont *font) +{ + if (!font) + setAll(false); + else + { + _bold_set = font->_bold_set; + _italic_set = font->_italic_set; + _name_set = font->_name_set; + _size_set = font->_size_set; + _strikeout_set = font->_strikeout_set; + _underline_set = font->_underline_set; + } +} + +void gFont::realize() +{ + _context = NULL; + _height = 0; + + reset(); + + _nfont++; +} + +void gFont::invalidateMetrics() +{ + pango_context_changed(_context); + _height = 0; +} + +void gFont::initFlags() +{ + gFont *comp = gDesktop::font(); + + _bold_set = comp->bold() != bold(); + _italic_set = comp->italic() != italic(); + _name_set = strcmp(comp->name(), name()) != 0; + _size_set = comp->size() != size(); + _strikeout_set = comp->strikeout() != strikeout(); + _underline_set = comp->underline() != underline(); + + checkMustFixSpacing(); +} + + +gFont::gFont() : gShare() +{ + PangoFontDescription *fd; + bool free_fd = false; + + realize(); + _context = gdk_pango_context_get(); + + if (_desktop_font) + { + _desktop_font->copyTo(this); + reset(); + } + + if (_desktop_font) + fd = _desktop_font->desc(); + else + { +#ifdef GTK3 + + char *font; + + g_object_get(gtk_settings_get_default(), "gtk-font-name", &font, (char *)NULL); + fd = pango_font_description_from_string(font); + g_free(font); + free_fd = true; + +#else + + fd = gtk_widget_get_default_style()->font_desc; + +#endif + } + + pango_context_set_font_description(_context, fd); + + if (free_fd) + pango_font_description_free(fd); + + checkMustFixSpacing(); +} + +gFont::gFont(PangoFontDescription *fd) : gShare() +{ + realize(); + _context = gdk_pango_context_get(); + pango_context_set_font_description(_context, fd); + initFlags(); +} + +void gFont::copyTo(gFont *dst) +{ + dst->reset(); + if (_name_set) dst->setName(name()); + if (_size_set) dst->setSize(size()); + if (_bold_set) dst->setBold(bold()); + if (_italic_set) dst->setItalic(italic()); + if (_underline_set) dst->setUnderline(underline()); + if (_strikeout_set) dst->setStrikeout(strikeout()); +} + +void gFont::mergeFrom(gFont *src) +{ + if (!_name_set && src->_name_set) setName(src->name()); + if (!_size_set && src->_size_set) setSize(src->size()); + if (!_bold_set && src->_bold_set) setBold(src->bold()); + if (!_italic_set && src->_italic_set) setItalic(src->italic()); + if (!_underline_set && src->_underline_set) setUnderline(src->underline()); + if (!_strikeout_set && src->_strikeout_set) setStrikeout(src->strikeout()); +} + +gFont *gFont::copy() +{ + gFont *f = new gFont(); + copyTo(f); + return f; +} + +gFont::~gFont() +{ + g_object_unref(_context); + _nfont--; +} + + +int gFont::ascent() +{ + return gt_pango_to_pixel(pango_font_metrics_get_ascent(metrics())); +} + +float gFont::ascentF() +{ + return (float)pango_font_metrics_get_ascent(metrics()) / PANGO_SCALE; +} + +int gFont::descent() +{ + return gt_pango_to_pixel(pango_font_metrics_get_descent(metrics())); +} + +bool gFont::bold() +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + PangoWeight w; + + w = pango_font_description_get_weight(desc); + return (w > PANGO_WEIGHT_NORMAL); +} + +void gFont::setBold(bool vl) +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + + if (vl) + pango_font_description_set_weight(desc,PANGO_WEIGHT_BOLD); + else + pango_font_description_set_weight(desc,PANGO_WEIGHT_NORMAL); + + _bold_set = true; + invalidateMetrics(); +} + +bool gFont::italic() +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + + return pango_font_description_get_style(desc) !=PANGO_STYLE_NORMAL; +} + +void gFont::setItalic(bool vl) +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + + if (vl) + pango_font_description_set_style(desc,PANGO_STYLE_ITALIC); + else + pango_font_description_set_style(desc,PANGO_STYLE_NORMAL); + + _italic_set = true; + invalidateMetrics(); +} + +char* gFont::name() +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + return (char *)pango_font_description_get_family(desc); +} + +void gFont::setName(char *nm) +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + + pango_font_description_set_family(desc, nm); + + _name_set = true; + invalidateMetrics(); + + checkMustFixSpacing(); +} + +double gFont::size() +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + return (double)pango_font_description_get_size(desc) / (double)PANGO_SCALE; +} + +int gFont::grade() +{ + double desktop = gDesktop::font()->size(); + return SIZE_TO_GRADE(size(), desktop); +} + +void gFont::setSize(double sz) +{ + PangoFontDescription *desc = pango_context_get_font_description(_context); + + pango_font_description_set_size(desc, (int)(sz * PANGO_SCALE + 0.5)); + + _size_set = true; + invalidateMetrics(); +} + +void gFont::setGrade(int grade) +{ + double desktop = gDesktop::font()->size(); + + if (grade < FONT_GRADE_MIN) + grade = FONT_GRADE_MIN; + else if (grade > FONT_GRADE_MAX) + grade = FONT_GRADE_MAX; + + setSize(GRADE_TO_SIZE(grade, desktop)); +} + +const char *gFont::toString() +{ + char *family; + GString *desc; + char *ret; + int s; + + desc = g_string_new(NULL); + + family = name(); + if (isdigit(*family) && atof(family)) + g_string_append_printf(desc, "\"%s\"", family); + else + g_string_append(desc, family); + + s = (int)(size() * 10 + 0.5); + + g_string_append_printf(desc, ",%d", s / 10); + if (s % 10) + g_string_append_printf(desc, ".%d", s % 10); + if (bold()) + g_string_append(desc, ",Bold"); + if (italic()) + g_string_append(desc, ",Italic"); + if (underline()) + g_string_append(desc, ",Underline"); + if (strikeout()) + g_string_append(desc, ",Strikeout"); + + ret = g_string_free(desc, false); + gt_free_later(ret); + + return ret; +} + +const char *gFont::toFullString() +{ + GString *desc = g_string_new(""); + char *ret; + + g_string_append_printf(desc, "[ "); + + if (_name_set) + g_string_append_printf(desc, "%s ", name()); + + if (_size_set) + g_string_append_printf(desc, "%g ", (double)((int)(size() * 10 + 0.5)) / 10); + + if (_bold_set) + g_string_append_printf(desc, "%s ", bold() ? "Bold" : "NotBold"); + if (_italic_set) + g_string_append_printf(desc, "%s ", italic() ? "Italic" : "NotItalic"); + if (_underline_set) + g_string_append_printf(desc, "%s ", underline() ? "Underline" : "NotUnderline"); + if (_strikeout_set) + g_string_append_printf(desc, "%s ", strikeout() ? "Strikeout" : "NotStrikeout"); + + g_string_append_printf(desc, "]"); + + ret = g_string_free(desc, false); + gt_free_later(ret); + return ret; +} + +void gFont::textSize(const char *text, int len, float *w, float *h) +{ + PangoLayout *ly; + PangoRectangle ink_rect, rect = { 0 }; + + if (text && len) + { + ly = pango_layout_new(_context); + pango_layout_set_text(ly, text, len); + gt_set_layout_from_font(ly, this); + pango_layout_get_extents(ly, &ink_rect, &rect); + g_object_unref(ly); + rect.width = Max(rect.width, ink_rect.width); + rect.height = Max(rect.height, ink_rect.height); + } + + if (w) *w = (float)rect.width / PANGO_SCALE; + if (h) + { + *h = (float)rect.height / PANGO_SCALE; + if (mustFixSpacing()) + *h += 1; + } +} + +int gFont::width(const char *text, int len) +{ + float fw; + textSize(text, len, &fw, NULL); + return gt_pango_to_pixel(fw * PANGO_SCALE); +} + +int gFont::height(const char *text, int len) +{ + int n = count_newlines(text, len); + return height() * (n + 1); +} + +int gFont::height() +{ + if (!_height) + { + float h1, h2; + textSize("A\nA", 3, NULL, &h1); + textSize("A\nA\nA", 5, NULL, &h2); + _height = gt_pango_to_pixel((h2 - h1) * PANGO_SCALE); + } + return _height; +} + +bool gFont::scalable() +{ + bool ret=false; + + PangoFontDescription *desc = pango_context_get_font_description(_context); + //PangoFontDescription *tmp; + const char* name=pango_font_description_get_family(desc); + PangoFontFamily **families; + PangoFontFace **faces; + int *sizes; + int n_families; + int n_faces; + int n_sizes; + //int b2; + const char *buf; + + if (!name) return false; + + pango_context_list_families(_context, &families,&n_families); + + if (!families) return false; + + for (int bucle=0;bucle<n_families;bucle++) + { + buf=pango_font_family_get_name(families[bucle]); + if (!strcmp(buf,name)) + { + pango_font_family_list_faces(families[bucle],&faces,&n_faces); + if (faces) + { + pango_font_face_list_sizes(faces[0],&sizes,&n_sizes); + if (sizes) + g_free(sizes); + else + ret=true; + g_free(faces); + g_free(families); + return ret; + } + else + { + g_free(families); + return false; + } + } + + } + + g_free(families); + return false; +} + +bool gFont::fixed() +{ + bool ret=false; + + PangoFontDescription *desc = pango_context_get_font_description(_context); + const char* name=pango_font_description_get_family(desc); + PangoFontFamily **families; + int n_families; + const char *buf; + + if (!name) return false; + + pango_context_list_families(_context, &families,&n_families); + + if (!families) return false; + + for (int bucle=0;bucle<n_families;bucle++) + { + buf=pango_font_family_get_name(families[bucle]); + if (!strcmp(buf,name)) + { + ret=pango_font_family_is_monospace (families[bucle]); + g_free(families); + return ret; + } + + } + + g_free(families); + return false; +} + +char** gFont::styles() +{ + return 0; +} + +int gFont::resolution() +{ + return 0; +} + +void gFont::setResolution(int vl) +{ +} + +void gFont::setStrikeout(bool vl) +{ + _strikeout = vl; + _strikeout_set = true; +} + +void gFont::setUnderline(bool vl) +{ + _underline = vl; + _underline_set = true; +} + +bool gFont::isAllSet() +{ + return + _bold_set + && _italic_set + && _name_set + && _size_set + && _strikeout_set + && _underline_set; +} + +void gFont::richTextSize(const char *text, int len, float sw, float *w, float *h) +{ + PangoLayout *ly; + PangoRectangle ink_rect, rect = { 0 }; + char *html; + + if (text && len) + { + ly = pango_layout_new(_context); + if (sw > 0) + { + pango_layout_set_wrap(ly, PANGO_WRAP_WORD_CHAR); + pango_layout_set_width(ly, (int)ceilf(sw * PANGO_SCALE)); + } + html = gt_html_to_pango_string(text, len, false); + pango_layout_set_markup(ly, html, -1); + gt_add_layout_from_font(ly, this); + pango_layout_get_extents(ly, &ink_rect, &rect); + g_free(html); + g_object_unref(ly); + rect.width = Max(rect.width, ink_rect.width); + rect.height = Max(rect.height, ink_rect.height); + } + + if (w) *w = (float)rect.width / PANGO_SCALE; + if (h) + { + *h = (float)rect.height / PANGO_SCALE; + if (mustFixSpacing()) + *h += 1; + } +} + +void gFont::checkMustFixSpacing() +{ + _must_fix_spacing = ::strcmp(name(), "Gambas") == 0 || ::strcmp(name(), "GambasRound") == 0 ; +} + +bool gFont::equals(gFont *src) +{ + if (src == this) + return true; + + return !::strcmp(name(), src->name()) && bold() == src->bold() && italic() == src->italic() + && size() == src->size() && strikeout() == src->strikeout() && underline() == src->underline(); +} + + +int gFont::desktopScale() +{ + if (!_desktop_scale) + { + gFont *ft = desktopFont(); + _desktop_scale = GET_DESKTOP_SCALE(ft->size(), gDesktop::resolution()); + } + + return _desktop_scale; +} + + +gFont* gFont::desktopFont() +{ + if (!_desktop_font) + { + _desktop_font = new gFont(); + _desktop_font->setAll(true); + } + + return _desktop_font; +} + +#ifndef GTK3 +static void cb_update_font(gControl *control) +{ + control->updateFont(); +} +#endif + +void gFont::setDesktopFont(gFont *ft) +{ + gFont::set(&_desktop_font, ft ? ft->copy() : new gFont()); + _desktop_scale = 0; + +#ifndef GTK3 + + gApplication::forEachControl(cb_update_font); + +#else + + gt_define_style_sheet(&_desktop_css, NULL); + + if (ft) + { + GString *css = g_string_new(NULL); + g_string_append(css, "* {\n"); + gt_css_add_font(css, _desktop_font); + g_string_append(css, "}"); + gt_define_style_sheet(&_desktop_css, css); + } + +#endif +} diff --git a/gb.gtk/src/gfont.h b/gb.gtk/src/gfont.h new file mode 100644 index 00000000..a0455160 --- /dev/null +++ b/gb.gtk/src/gfont.h @@ -0,0 +1,126 @@ +/*************************************************************************** + + gfont.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GFONT_H +#define __GFONT_H + +#include "gshare.h" + +class gFont : public gShare +{ +public: + gFont(); + virtual ~gFont(); + + static void assign(gFont **dst, gFont *src = 0) { gShare::assign((gShare **)dst, src); } + static void set(gFont **dst, gFont *src = 0) { gShare::assign((gShare **)dst, src); src->unref(); } + + static void init(); + static void exit(); + static int count(); + static const char *familyItem(int pos); + + gFont *copy(); + void copyTo(gFont *dst); + void mergeFrom(gFont *src); + bool equals(gFont *src); + + int ascent(); + float ascentF(); + int descent(); + bool fixed(); + bool scalable(); + char **styles(); + + bool bold(); + bool italic(); + char* name(); + int resolution(); + double size(); + bool strikeout() const { return _strikeout; } + bool underline() const { return _underline; } + int grade(); + + void setBold(bool vl); + void setItalic(bool vl); + void setName(char *nm); + void setResolution(int vl); + void setSize(double sz); + void setGrade(int grade); + void setStrikeout(bool vl); + void setUnderline(bool vl); + + const char *toString(); + const char *toFullString(); + void setFromString(const char *str); + + int width(const char *text, int len = -1); + int height(const char *text, int len = -1); + int height(); + void textSize(const char *text, int len, float *w, float *h); + void richTextSize(const char *txt, int len, float sw, float *w, float *h); + + bool mustFixSpacing() const { return _must_fix_spacing; } + + static gFont *desktopFont(); + static void setDesktopFont(gFont *vl); + static int desktopScale(); + +//"Private" + gFont(PangoFontDescription *fd); + PangoFontDescription *desc() { return pango_context_get_font_description(_context); } + bool isAllSet(); + void setAll(bool v); + void setAllFrom(gFont *font); + void reset(); + + unsigned _bold_set : 1; + unsigned _italic_set : 1; + unsigned _name_set : 1; + unsigned _size_set : 1; + unsigned _strikeout_set : 1; + unsigned _underline_set : 1; + +private: + + bool _underline; + bool _strikeout; + + void realize(); + void initFlags(); + void checkMustFixSpacing(); + PangoFontMetrics *metrics() { return pango_context_get_metrics(_context, NULL, NULL); } + void invalidateMetrics(); + + PangoContext* _context; + int _height; + unsigned _must_fix_spacing : 1; + + static gFont *_desktop_font; + static int _desktop_scale; +#ifdef GTK3 + static GtkStyleProvider *_desktop_css; +#endif +}; + +#endif diff --git a/gb.gtk/src/ggambastag.h b/gb.gtk/src/ggambastag.h new file mode 100644 index 00000000..de36caf6 --- /dev/null +++ b/gb.gtk/src/ggambastag.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + ggambastag.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GGAMBASTAG_H +#define __GGAMBASTAG_H + +#include "gtag.h" + +class gGambasTag: public gTag +{ +public: + gGambasTag(void *p) : gTag(p) { } + virtual ~gGambasTag() {} + virtual void ref(void *v) { GB.Ref(v); } + virtual void unref(void *v) { GB.Unref((void **)&v); } +}; + +#endif diff --git a/gb.gtk/src/gglarea.cpp b/gb.gtk/src/gglarea.cpp new file mode 100644 index 00000000..1707e57a --- /dev/null +++ b/gb.gtk/src/gglarea.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + + gglarea.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GGLAREA_C + +#include "widgets.h" +#include "gglarea.h" + +gGLArea::gGLArea(gContainer *parent, void (*init)(GtkWidget *)) : gControl(parent) +{ + border = widget = gtk_event_box_new(); + gtk_widget_set_can_focus(widget, TRUE); + (*init)(widget); + realize(); +} + diff --git a/gb.gtk/src/gglarea.h b/gb.gtk/src/gglarea.h new file mode 100644 index 00000000..cbdb16c0 --- /dev/null +++ b/gb.gtk/src/gglarea.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + gglarea.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GGLAREA_H +#define __GGLAREA_H + +class gGLArea : public gControl +{ +public: + gGLArea(gContainer *parent, void (*init)(GtkWidget *)); +}; + +#endif diff --git a/gb.gtk/src/gkey.cpp b/gb.gtk/src/gkey.cpp new file mode 100644 index 00000000..926a48f8 --- /dev/null +++ b/gb.gtk/src/gkey.cpp @@ -0,0 +1,621 @@ +/*************************************************************************** + + gkey.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GKEY_CPP + +#include <ctype.h> +#include <time.h> +#include <unistd.h> + +#include "widgets.h" +#include "gapplication.h" +#include "gtrayicon.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gkey.h" + +//#define DEBUG_IM 1 + +/************************************************************************* + +gKey + +**************************************************************************/ + +int gKey::_valid = 0; +bool gKey::_canceled = false; +GdkEventKey gKey::_event; +int gKey::_last_key_press = 0; +int gKey::_last_key_release = 0; + +static GtkIMContext *_im_context = NULL; +static char *_im_default_slave = NULL; +static bool _im_has_input_method = FALSE; +static gControl *_im_control = NULL; +static bool _im_no_commit = false; +static GdkWindow *_im_window = NULL; +static bool _im_is_xim = FALSE; +static bool _im_ignore_event = FALSE; +static bool _im_got_commit = FALSE; + +//#define MAX_CODE 16 +//static uint _key_code[MAX_CODE] = { 0 }; + + +//char *_im_text = NULL; + +const char *gKey::text() +{ + if (!_valid) + return 0; + else + return _event.string; +} + +int gKey::code() +{ + if (!_valid) + return 0; + + int code = _event.keyval; + + if (code >= GDK_a && code <= GDK_z) + code += GDK_A - GDK_a; + else if (code == GDK_Alt_R) + code = GDK_Alt_L; + else if (code == GDK_Control_R) + code = GDK_Control_L; + else if (code == GDK_Meta_R) + code = GDK_Meta_L; + else if (code == GDK_Shift_R) + code = GDK_Shift_L; + else + { + int unicode = gdk_keyval_to_unicode(code); + if (unicode >= 32 && unicode < 127) + code = unicode; + } + + return code; +} + +int gKey::state() +{ + if (!_valid) + return 0; + else + return _event.state; +} + +bool gKey::shift() +{ + return state() & GDK_SHIFT_MASK; +} + +bool gKey::control() +{ + return state() & GDK_CONTROL_MASK; +} + +bool gKey::alt() +{ + return state() & GDK_MOD1_MASK; +} + +bool gKey::meta() +{ + return state() & GDK_META_MASK; +} + +bool gKey::normal() +{ + return (state() & (GDK_MOD1_MASK | GDK_CONTROL_MASK | GDK_META_MASK | GDK_SHIFT_MASK)) == 0; +} + +int gKey::fromString(const char *str) +{ + char *lstr; + int key; + + if (!str || !*str) + return 0; + + lstr = g_ascii_strup(str, -1); + key = gdk_keyval_from_name(lstr); + g_free(lstr); + if (key) return key; + + lstr = g_ascii_strdown(str, -1); + key = gdk_keyval_from_name(lstr); + g_free(lstr); + if (key) return key; + + key = gdk_keyval_from_name(str); + if (key) return key; + + if (!str[1] && isascii(str[0])) + return str[0]; + else + return 0; +} + +void gKey::disable() +{ + _valid--; + if (_valid) + return; + + _valid = false; + _event.keyval = 0; + _event.state = 0; + //g_free(_event.string); +} + +bool gKey::enable(gControl *control, GdkEventKey *event) +{ + bool f = false; + + _valid++; + _canceled = false; + + if (event) + { + _im_no_commit = false; + + _event = *event; + _event.window = _im_window; + + if (_event.keyval == GDK_Alt_L || _event.keyval == GDK_Alt_R) + _event.state ^= GDK_MOD1_MASK; + if (_event.keyval == GDK_Control_L || _event.keyval == GDK_Control_R) + _event.state ^= GDK_CONTROL_MASK; + if (_event.keyval == GDK_Meta_L || _event.keyval == GDK_Meta_R) + _event.state ^= GDK_META_MASK; + if (_event.keyval == GDK_Shift_L || _event.keyval == GDK_Shift_R) + _event.state ^= GDK_SHIFT_MASK; + + if (gKey::mustIgnoreEvent(event)) + return true; + + if (control == _im_control) + { + #if DEBUG_IM + fprintf(stderr, "gKey::enable: [%p] flag = %d event->string = %d\n", event, (event->state & (1 << 25)) != 0, *event->string); + #endif + +#if 0 + if (_im_slave_is_xim) + { + /*if (_im_xim_abort == 2) + { + f = true; + _im_xim_abort = 0; + } + else*/ + { + GdkEventKey save = *event; + if (save.string) + save.string = g_strdup(save.string); + + f = gtk_im_context_filter_keypress(_im_context, event); + *event = save; + _im_xim_abort++; + } + } + else + f = gtk_im_context_filter_keypress(_im_context, event); +#endif + + if (!_im_has_input_method) + { + initContext(); + f = gtk_im_context_filter_keypress(_im_context, event); + } + + #if DEBUG_IM + fprintf(stderr, "gKey::enable: [%p] filter -> %d\n", event, f); + #endif + } + } + + return f || _canceled; +} + +bool gKey::mustIgnoreEvent(GdkEventKey *event) +{ + if (!_im_has_input_method) + return false; + else + return (event->type == GDK_KEY_PRESS) && (event->keyval == 0 || !event->string || ((uchar)*event->string >= 32 && ((event->keyval & 0xFF00) != 0xFF00))); +} + +void gcb_im_commit(GtkIMContext *context, const char *str, gControl *control) +{ + bool disable = false; + + if (!control) + control = _im_control; + + // Not called from a key press event! + if (!control) + return; + + #if DEBUG_IM + fprintf(stderr, "cb_im_commit: \"%s\" _im_no_commit = %d gKey::valid = %d\n", str, _im_no_commit, gKey::isValid()); + #endif + + if (!gKey::isValid()) + { + gKey::enable(control, NULL); + gKey::_event.keyval = gKey::_last_key_press; + disable = true; + } + + gKey::_canceled = gKey::raiseEvent(gEvent_KeyPress, control, str); +#if DEBUG_IM + fprintf(stderr, "cb_im_commit: canceled = %d\n", gKey::_canceled); +#endif + + if (disable) + gKey::disable(); + + _im_no_commit = true; +} + +static gboolean hook_commit(GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, gpointer data) +{ + _im_got_commit = TRUE; + return true; +} + +void gKey::initContext() +{ + if (_im_context) + return; + + _im_context = gtk_im_multicontext_new(); + gtk_im_context_set_client_window (_im_context, _im_window); + + _im_default_slave = g_strdup(gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(_im_context))); + + g_signal_connect(_im_context, "commit", G_CALLBACK(gcb_im_commit), NULL); + + g_signal_add_emission_hook(g_signal_lookup("commit", GTK_TYPE_IM_CONTEXT), (GQuark)0, hook_commit, (gpointer)0, NULL); +} + +void gKey::init() +{ + GdkWindowAttr attr; + + attr.event_mask = GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK; + attr.width = attr.height = 10; + attr.wclass = GDK_INPUT_OUTPUT; + attr.window_type = GDK_WINDOW_TOPLEVEL; + + _im_window = gdk_window_new(NULL, &attr, 0); +} + +void gKey::exit() +{ + disable(); + if (_im_context) + { + g_free(_im_default_slave); + g_object_unref(_im_context); + } +} + +void gKey::setActiveControl(gControl *control) +{ + const char *slave; + GtkIMContext *context; + + if (_im_control) + { +#if DEBUG_IM + fprintf(stderr, "gtk_im_context_focus_out\n"); +#endif + if (!_im_has_input_method) + { + initContext(); + gtk_im_context_reset(_im_context); + gtk_im_context_set_client_window (_im_context, 0); + gtk_im_context_reset(_im_context); + gtk_im_context_focus_out(_im_context); + gtk_im_context_reset(_im_context); + } + + _im_control = NULL; + } + + if (control) + { + _im_control = control; + + if (!control->hasInputMethod()) + { + initContext(); + _im_has_input_method = FALSE; + gtk_im_context_reset(_im_context); + gtk_im_context_set_client_window (_im_context, gtk_widget_get_window(control->widget)); + gtk_im_context_reset(_im_context); + gtk_im_context_focus_in(_im_context); + gtk_im_context_reset(_im_context); + //slave = gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(_im_context)); + _im_is_xim = FALSE; + } + else + { + _im_has_input_method = TRUE; + context = control->getInputMethod(); + if (context && GTK_IS_IM_MULTICONTEXT(context)) + { + slave = gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(context)); + _im_is_xim = slave && strcmp(slave, "xim") == 0; + } + else + { + _im_is_xim = FALSE; + } + } + + _im_ignore_event = FALSE; + +#if DEBUG_IM + fprintf(stderr,"\n------------------------\n"); + fprintf(stderr, "gtk_im_context_focus_in: %s _im_has_input_method = %d\n", control ? control->name() : "-", _im_has_input_method); +#endif + } +} + +static bool raise_key_event_to_parent_window(gControl *control, int type) +{ + gMainWindow *win; + + while (control->parent()) + { + win = control->parent()->window(); + if (CB_control_can_raise(win, type)) + { + //fprintf(stderr, "onKeyEvent: %d %p %s\n", type, win, win->name()); + if (CB_control_key(win, type)) + return true; + } + + control = win; + } + + return false; +} + +#if 0 +static bool can_raise(GdkEventKey *event) +{ + int i; + + if (event->type == GDK_KEY_PRESS) + { + for (i = 0; i < MAX_CODE; i++) + { + if (event->keyval == _key_code[i]) + return false; + } + for (i = 0; i < MAX_CODE; i++) + { + if (!_key_code[i]) + { + //fprintf(stderr, "store key %d\n", event->keyval); + _key_code[i] = event->keyval; + break; + } + } + return true; + } + else + { + for (i = 0; i < MAX_CODE; i++) + { + if (event->keyval == _key_code[i]) + { + //fprintf(stderr, "remove key %d\n", event->keyval); + _key_code[i] = 0; + return true; + } + } + return false; + } +} +#endif + +bool gKey::raiseEvent(int type, gControl *control, const char *text) +{ + bool parent_got_it = false; + bool cancel = false; + bool handled = false; + +#if DEBUG_IM + fprintf(stderr, "gKey::raiseEvent %s to %p %s\n", type == gEvent_KeyPress ? "KeyPress" : "KeyRelease", control, control->name()); +#endif + + if (text) + _event.string = (gchar *)text; + + //if (!can_raise(&_event)) + // return false; + +__KEY_TRY_PROXY: + + if (!parent_got_it) + { + parent_got_it = true; + + if (gApplication::onKeyEvent) + cancel = gApplication::onKeyEvent(type); + + if (!cancel) + cancel = raise_key_event_to_parent_window(control, type); + } + + if (!cancel && CB_control_can_raise(control, type)) + { + //fprintf(stderr, "gEvent_KeyPress on %p %s\n", control, control->name()); + //fprintf(stderr, "onKeyEvent: %p %d %p %s\n", event, type, control, control->name()); + #if DEBUG_IM + fprintf(stderr, "--> %s\n", control->name()); + #endif + handled = true; + cancel = CB_control_key(control, type); + } + + if (cancel) + { + #if DEBUG_IM + fprintf(stderr, "--> cancel\n"); + #endif + return true; + } + + if (control->_proxy_for) + { + control = control->_proxy_for; + goto __KEY_TRY_PROXY; + } + + if (!handled) + { + control = control->parent(); + if (control && !control->isWindow()) + goto __KEY_TRY_PROXY; + } + + return false; +} + +static bool check_button(gControl *w) +{ + return w && w->isReallyVisible() && w->isEnabled(); +} + +gboolean gcb_key_event(GtkWidget *widget, GdkEvent *event, gControl *control) +{ + gMainWindow *win; + int type; + bool cancel; + +#if DEBUG_IM + fprintf(stderr, "gcb_key_event: %s for %p %s / active = %p %s\n", event->type == GDK_KEY_PRESS ? "GDK_KEY_PRESS" : "GDK_KEY_RELEASE", control, control->name(), gApplication::activeControl(), gApplication::activeControl() ? gApplication::activeControl()->name() : "-"); +#endif + + /*if (!control->_grab && gApplication::activeControl()) + control = gApplication::activeControl();*/ + if (!control || control != gApplication::activeControl()) + return false; + +#if DEBUG_IM + fprintf(stderr, "handle it\n"); +#endif + + //if (event->type == GDK_KEY_PRESS) + // fprintf(stderr, "GDK_KEY_PRESS: control = %p %s %p %08X\n", control, control ? control->name() : "", event, event->key.state); + + if (_im_is_xim) + { + _im_ignore_event = !_im_ignore_event; + if (_im_ignore_event) + return false; + } + + type = (event->type == GDK_KEY_PRESS) ? gEvent_KeyPress : gEvent_KeyRelease; + + if (gKey::enable(control, &event->key)) + { + gKey::disable(); + return gKey::canceled() || !_im_has_input_method; + } + + if (gKey::mustIgnoreEvent(&event->key)) + { + gKey::disable(); + return true; + } + + cancel = gKey::raiseEvent(type, control, NULL); + gKey::disable(); + + if (cancel) + return true; + + win = control->window(); + + for(;;) + { + if (event->key.keyval == GDK_Escape) + { + if (control->_grab) + { + gApplication::exitLoop(control); + return true; + } + + if (check_button(win->_cancel)) + { + #if DEBUG_IM + fprintf(stderr, "gcb_key_event: cancel button\n"); + #endif + //win->_cancel->setFocus(); + win->_cancel->animateClick(type == gEvent_KeyRelease); + return true; + } + } + else if (event->key.keyval == GDK_Return || event->key.keyval == GDK_KP_Enter) + { + if (check_button(win->_default) && !control->eatReturnKey()) + { + #if DEBUG_IM + fprintf(stderr, "gcb_key_event: default button\n"); + #endif + //win->_default->setFocus(); + win->_default->animateClick(type == gEvent_KeyRelease); + return true; + } + } + + if (win->isTopLevel()) + break; + + win = win->parent()->window(); + } + + if (control->_grab) + return true; + + return false; +} + +bool gKey::gotCommit() +{ + bool ret = _im_got_commit; + _im_got_commit = FALSE; + return ret; +} diff --git a/gb.gtk/src/gkey.h b/gb.gtk/src/gkey.h new file mode 100644 index 00000000..7f09ef86 --- /dev/null +++ b/gb.gtk/src/gkey.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + gkey.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GKEY_H +#define __GKEY_H + +class gKey +{ +public: + static bool isValid() { return _valid; } + static const char *text(); + static int code(); + static int state(); + + static bool alt(); + static bool control(); + static bool meta(); + static bool normal(); + static bool shift(); + + static int fromString(const char *str); + +//"Private" + static void disable(); + static bool enable(gControl *control, GdkEventKey *e); + static bool canceled() { return _canceled; } + static void init(); + static void exit(); + + static void setActiveControl(gControl *control); + + static bool raiseEvent(int type, gControl *control, const char *text); + + static bool mustIgnoreEvent(GdkEventKey *e); + + static bool gotCommit(); + + static bool _canceled; + static GdkEventKey _event; + static int _last_key_press; + static int _last_key_release; + +private: + static int _valid; + + static void initContext(); +}; + +void gcb_im_commit(GtkIMContext *context, const char *str, gControl *control); +gboolean gcb_key_event(GtkWidget *widget, GdkEvent *event, gControl *data); + +#endif diff --git a/gb.gtk/src/gmainwindow.cpp b/gb.gtk/src/gmainwindow.cpp new file mode 100644 index 00000000..3aa06575 --- /dev/null +++ b/gb.gtk/src/gmainwindow.cpp @@ -0,0 +1,2136 @@ +/*************************************************************************** + + gmainwindow.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include <ctype.h> +#include <time.h> + +#include "widgets.h" + +#ifndef GTK3 +#include "x11.h" +#include "sm/sm.h" +#endif + +#include "gapplication.h" +#include "gdesktop.h" +#include "gkey.h" +#include "gmenu.h" +#include "gdialog.h" +#include "gmouse.h" +#include "gmainwindow.h" + +//#define DEBUG_RESIZE 1 + +GList *gMainWindow::windows = NULL; +gMainWindow *gMainWindow::_active = NULL; +gMainWindow *gMainWindow::_current = NULL; + + +#define CHECK_STATE(_var, _state) \ + if (event->changed_mask & _state) \ + { \ + v = (event->new_window_state & _state) != 0; \ + if (v != data->_var) \ + { \ + data->_var = v; \ + has_changed = true; \ + } \ + } + +static gboolean cb_frame(GtkWidget *widget,GdkEventWindowState *event,gMainWindow *data) +{ + bool has_changed = false; + bool v; + + CHECK_STATE(_minimized, GDK_WINDOW_STATE_ICONIFIED); + CHECK_STATE(_maximized, GDK_WINDOW_STATE_MAXIMIZED); + CHECK_STATE(_sticky, GDK_WINDOW_STATE_STICKY); + CHECK_STATE(_fullscreen, GDK_WINDOW_STATE_FULLSCREEN); + + if (event->changed_mask & GDK_WINDOW_STATE_ABOVE) + { + if (event->new_window_state & GDK_WINDOW_STATE_ABOVE) + data->stack = 1; + else if (data->stack == 1) + data->stack = 0; + } + if (event->changed_mask & GDK_WINDOW_STATE_BELOW) + { + if (event->new_window_state & GDK_WINDOW_STATE_BELOW) + data->stack = 2; + else if (data->stack == 2) + data->stack = 0; + } + + if (has_changed) + { + #ifdef DEBUG_RESIZE + fprintf(stderr, "cb_frame: min = %d max = %d fs = %d\n", data->_minimized, data->_maximized, data->_fullscreen); + #endif + data->_csd_w = data->_csd_h = -1; + /*data->calcCsdSize(); + data->performArrange();*/ + } + + if (event->changed_mask & (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN | GDK_WINDOW_STATE_STICKY | GDK_WINDOW_STATE_ABOVE | GDK_WINDOW_STATE_BELOW)) + CB_window_state(data); + + return false; +} + +static gboolean cb_show(GtkWidget *widget, gMainWindow *data) +{ + if (gApplication::_disable_mapping_events) + return false; + + if (data->_grab_on_show) + { + data->_grab_on_show = FALSE; + gApplication::grabPopup(); + } + + data->emitOpen(); + + if (data->_opened) + { + data->performArrange(); + #ifdef DEBUG_RESIZE + fprintf(stderr, "cb_show\n"); + #endif + data->emitResize(); + CB_window_show(data); + data->_not_spontaneous = false; + } + return false; +} + +static gboolean cb_map(GtkWidget *widget, GdkEvent *event, gMainWindow *data) +{ + if (gApplication::_disable_mapping_events) + return false; + + data->_unmap = false; + return cb_show(widget, data); +} + +static gboolean cb_hide(GtkWidget *widget, gMainWindow *data) +{ + if (gApplication::_disable_mapping_events) + return false; + + if (!data->_unmap) + { + CB_window_hide(data); + data->_not_spontaneous = false; + } + + return false; +} + +static gboolean cb_unmap(GtkWidget *widget, GdkEvent *event, gMainWindow *data) +{ + if (gApplication::_disable_mapping_events) + return false; + + bool ret = cb_hide(widget, data); + data->_unmap = true; + return ret; +} + +static gboolean cb_close(GtkWidget *widget,GdkEvent *event, gMainWindow *data) +{ + if ((!gMainWindow::_current || data == gMainWindow::_current) && gApplication::areInputEventsEnabled()) + data->doClose(); + + return true; +} + +static gboolean cb_configure(GtkWidget *widget, GdkEventConfigure *event, gMainWindow *data) +{ +#if 0 + gint x, y, w, h; + + if (data->_opened) + { + if (data->isTopLevel()) + { + gtk_window_get_position(GTK_WINDOW(data->border), &x, &y); + } + else + { + x = event->x; + y = event->y; + } + + #ifdef DEBUG_RESIZE + fprintf(stderr, "cb_configure: %s: (%d %d %d %d) -> (%d/%d %d/%d %d %d) window = %p resized = %d send_event = %d\n", data->name(), data->bufX, data->bufY, data->bufW, data->bufH, x, event->x, y, event->y, event->width, event->height, event->window, data->_event_resized, event->send_event); + #endif + + if (x != data->bufX || y != data->bufY) + { + data->bufX = x; + data->bufY = y; + if (data->onMove) data->onMove(data); + } + + /*#ifdef GTK3 + //data->_csd_w = data->_csd_h = -1; + if (data->isTopLevel()) + return false; + #endif*/ + + w = event->width; + h = event->height; + + if ((w != data->bufW) || (h != data->bufH) || (data->_event_resized) || !event->window) + { + data->_event_resized = false; + data->bufW = w; + data->bufH = h; + #ifdef DEBUG_RESIZE + fprintf(stderr, "cb_configure\n"); + #endif + data->emitResize(); + } + } +#endif + + int x, y; + + if (!data->isOpened()) + return false; + + if (data->isTopLevel()) + { + gtk_window_get_position(GTK_WINDOW(data->border), &x, &y); + } + else + { + x = event->x; + y = event->y; + } + + if (x != data->bufX || y != data->bufY) + { + data->bufX = x; + data->bufY = y; + CB_window_move(data); + } + + #ifdef DEBUG_RESIZE + fprintf(stderr, "cb_configure: %s: (%d %d %d %d) -> (%d/%d %d/%d %d %d) window = %p send_event = %d\n", data->name(), data->bufX, data->bufY, data->bufW, data->bufH, x, event->x, y, event->y, event->width, event->height, event->window, event->send_event); + #endif + + data->calcCsdSize(); + + data->bufW = event->width - data->_csd_w; + data->bufH = event->height - data->_csd_h; + + #ifdef DEBUG_RESIZE + fprintf(stderr, "-> %d %d\n", data->bufW, data->bufH); + #endif + + data->emitResize(); + + return false; +} + +#ifdef GTK3 +static gboolean cb_draw(GtkWidget *wid, cairo_t *cr, gMainWindow *data) +{ + if (data->background() != COLOR_DEFAULT) + { + gt_cairo_set_source_color(cr, data->background()); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + } + + if (data->_picture) + { + cairo_pattern_t *pattern; + + pattern = cairo_pattern_create_for_surface(data->_picture->getSurface()); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + cairo_set_source(cr, pattern); + cairo_paint(cr); + + cairo_pattern_destroy(pattern); + } + + return false; +} +#else +static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gMainWindow *data) +{ + bool draw_bg = data->isTransparent(); + bool draw_pic = data->_picture; + + if (!draw_bg && !draw_pic) + return false; + + cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(wid)); + + if (draw_bg) + { + if (data->background() == COLOR_DEFAULT) + gt_cairo_set_source_color(cr, 0xFF000000); + else + gt_cairo_set_source_color(cr, data->background()); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + } + + if (draw_pic) + { + cairo_pattern_t *pattern; + + gdk_cairo_region(cr, e->region); + cairo_clip(cr); + + pattern = cairo_pattern_create_for_surface(data->_picture->getSurface()); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + cairo_set_source(cr, pattern); + cairo_paint(cr); + + cairo_pattern_destroy(pattern); + } + + cairo_destroy(cr); + return false; +} +#endif + +static gboolean my_key_press_event(GtkWidget *widget, GdkEventKey *event) +{ + GtkWindow *window = GTK_WINDOW(widget); + gboolean handled = FALSE; + gboolean propagated = FALSE; + GtkWidget *focus; + + focus = gtk_window_get_focus(window); + //fprintf(stderr, "focus %p = %p\n", window, focus); + if (focus && gtk_widget_get_realized(focus)) + { + if (GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus)) + { + propagated = TRUE; + handled = gtk_window_propagate_key_event(window, event); + if (handled) + return TRUE; + } + } + + /* handle mnemonics and accelerators */ + handled = gtk_window_activate_key(window, event); + if (handled) + return TRUE; + + if (!propagated && focus && gtk_widget_get_realized(focus)) + { + handled = gtk_window_propagate_key_event(window, event); + if (handled) + return TRUE; + } + + /* Chain up, invokes binding set */ + GtkWidgetClass *parent_klass = (GtkWidgetClass*)g_type_class_peek(g_type_parent(GTK_TYPE_WINDOW)); + handled = parent_klass->key_press_event(widget, event); + + return handled; +} + + +static gboolean my_key_release_event(GtkWidget *widget, GdkEventKey *event) +{ + GtkWindow *window = GTK_WINDOW (widget); + gboolean handled = FALSE; + GtkWidget *focus; + + focus = gtk_window_get_focus(window); + if (focus && !gtk_widget_get_realized(focus)) + return handled; + + /* handle focus widget key events */ + if (!handled) + handled = gtk_window_propagate_key_event(window, event); + + /* Chain up, invokes binding set */ + if (!handled) + { + GtkWidgetClass *parent_klass = (GtkWidgetClass*)g_type_class_peek(g_type_parent(GTK_TYPE_WINDOW)); + handled = parent_klass->key_release_event(widget, event); + } + + return handled; +} + + +//------------------------------------------------------------------------- + +void gMainWindow::initialize() +{ + //fprintf(stderr, "new window: %p in %p\n", this, parent()); + + stack = 0; + accel = NULL; + _default = NULL; + _cancel = NULL; + menuBar = NULL; + _icon = NULL; + _picture = NULL; + _initial_focus = NULL; + _save_focus = NULL; + _title = NULL; + _resize_last_w = _resize_last_h = -1; + _min_w = _min_h = _default_min_w = _default_min_h = 0; + _csd_w = _csd_h = -1; + _previous = NULL; + + _opened = false; + _sticky = false; + _persistent = false; + _mask = false; + _masked = false; + _resized = false; + _top_only = false; + _closing = false; + _closed = false; + _not_spontaneous = false; + _skip_taskbar = false; + _xembed = false; + _activate = false; + _hidden = false; + _hideMenuBar = false; + _showMenuBar = true; + _initMenuBar = true; + _popup = false; + _maximized = _minimized = _fullscreen = false; + _transparent = false; + _utility = false; + _no_take_focus = false; + _moved = false; + _resizable = true; + _unmap = false; + _grab_on_show = false; + _is_window = true; + _no_background = true; + _frame_init = false; + _set_focus = false; + + accel = gtk_accel_group_new(); +} + +void gMainWindow::initWindow() +{ + if (!isTopLevel()) + { + //g_signal_connect(G_OBJECT(border), "configure-event", G_CALLBACK(cb_configure), (gpointer)this); + g_signal_connect_after(G_OBJECT(border), "map", G_CALLBACK(cb_show), (gpointer)this); + g_signal_connect(G_OBJECT(border),"unmap", G_CALLBACK(cb_hide),(gpointer)this); + //g_signal_connect_after(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_configure), (gpointer)this); + ON_DRAW_BEFORE(widget, this, cb_expose, cb_draw); + } + else + { + //g_signal_connect(G_OBJECT(border),"size-request",G_CALLBACK(cb_realize),(gpointer)this); + //g_signal_connect(G_OBJECT(border), "show", G_CALLBACK(cb_show),(gpointer)this); + g_signal_connect(G_OBJECT(border), "hide", G_CALLBACK(cb_hide),(gpointer)this); + g_signal_connect(G_OBJECT(border), "map-event", G_CALLBACK(cb_map),(gpointer)this); + g_signal_connect(G_OBJECT(border), "unmap-event", G_CALLBACK(cb_unmap),(gpointer)this); + g_signal_connect(G_OBJECT(border), "delete-event", G_CALLBACK(cb_close),(gpointer)this); + g_signal_connect(G_OBJECT(border), "window-state-event", G_CALLBACK(cb_frame),(gpointer)this); + + gtk_widget_add_events(widget, GDK_BUTTON_MOTION_MASK | GDK_STRUCTURE_MASK); + ON_DRAW_BEFORE(widget, this, cb_expose, cb_draw); + + //g_signal_connect(G_OBJECT(border), "configure-event", G_CALLBACK(cb_configure), (gpointer)this); + } + + gtk_widget_add_events(border, GDK_STRUCTURE_MASK); + g_signal_connect(G_OBJECT(border), "configure-event", G_CALLBACK(cb_configure), (gpointer)this); + + /*if (!_frame_init) + { + #if DEBUG_RESIZE + fprintf(stderr, "init cb_resize_frame: %s\n", name()); + #endif + g_signal_connect_after(G_OBJECT(frame), "size-allocate", G_CALLBACK(cb_resize_frame), (gpointer)this); + _frame_init = true; + }*/ + + gtk_window_add_accel_group(GTK_WINDOW(topLevel()->border), accel); + + have_cursor = true; //parent() == 0 && !_xembed; + setCanFocus(true); +} + + +// workaround GTK+ accelerator management + +static void workaround_accel_management() +{ + static bool _init = FALSE; + if (_init) + return; + + GtkWidgetClass *klass = (GtkWidgetClass*)g_type_class_peek(GTK_TYPE_WINDOW); + klass->key_press_event = my_key_press_event; + klass->key_release_event = my_key_release_event; + _init = TRUE; +} + +gMainWindow::gMainWindow() : gContainer(NULL) +{ + initialize(); + + windows = g_list_append(windows, (gpointer)this); + + border = gtk_window_new(GTK_WINDOW_TOPLEVEL); + workaround_accel_management(); + + frame = gtk_fixed_new(); + widget = gtk_fixed_new(); + + realize(); + initWindow(); + + gtk_widget_show(frame); + gtk_widget_show(widget); + gtk_window_resize(GTK_WINDOW(border), 1, 1); +} + +gMainWindow::gMainWindow(int plug) : gContainer(NULL) +{ + initialize(); + + windows = g_list_append(windows, (gpointer)this); + + _xembed = true; + + #ifdef GTK3 + border = PLATFORM.CreatePlug(plug); + if (!border) + return; + #else + border = gtk_plug_new(plug); + #endif + + frame = gtk_fixed_new(); + widget = gtk_fixed_new(); + + realize(); + initWindow(); + + //gtk_widget_realize(border); + gtk_widget_show(frame); + gtk_widget_show(widget); + gtk_window_resize(GTK_WINDOW(border), 1, 1); + //gtk_widget_set_size_request(border, 1, 1); +} + +gMainWindow::gMainWindow(gContainer *par) : gContainer(par) +{ + initialize(); + + border = gtk_event_box_new(); + frame = gtk_fixed_new(); + widget = gtk_fixed_new(); + + realize(); + initWindow(); + + gtk_widget_show(frame); + gtk_widget_show(widget); + + setVisibility(false); +} + +gMainWindow::~gMainWindow() +{ + if (!border) + return; + + gApplication::finishFocus(); + + if (_opened) + { + CB_window_close(this); + _opened = false; + if (GTK_IS_WINDOW(border) && isModal()) + gApplication::exitLoop(this); + } + + gPicture::assign(&_picture); + gPicture::assign(&_icon); + if (_title) g_free(_title); + g_object_unref(accel); + + if (_active == this) + _active = NULL; + + if (gApplication::mainWindow() == this) + gApplication::setMainWindow(NULL); + + windows = g_list_remove(windows, (gpointer)this); +} + +int gMainWindow::getStacking() +{ + return stack; +} + +void gMainWindow::setSticky(bool vl) +{ + if (!isTopLevel()) return; + + _sticky = vl; + + if (vl) + gtk_window_stick(GTK_WINDOW(border)); + else + gtk_window_unstick(GTK_WINDOW(border)); +} + +void gMainWindow::setStacking(int vl) +{ + stack=vl; + if (!isTopLevel()) return; + + switch (vl) + { + case 0: + gtk_window_set_keep_below(GTK_WINDOW(border),FALSE); + gtk_window_set_keep_above(GTK_WINDOW(border),FALSE); + break; + case 1: + gtk_window_set_keep_below(GTK_WINDOW(border),FALSE); + gtk_window_set_keep_above(GTK_WINDOW(border),TRUE); + break; + case 2: + gtk_window_set_keep_above(GTK_WINDOW(border),FALSE); + gtk_window_set_keep_below(GTK_WINDOW(border),TRUE); + break; + } +} + +void gMainWindow::setRealBackground(gColor color) +{ + if (!_picture) + { + gControl::setRealBackground(color); + gMenu::updateColor(this); + } +} + +void gMainWindow::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + gMenu::updateColor(this); +} + +void gMainWindow::move(int x, int y) +{ + if (isTopLevel()) + { + if (!_moved && (x || y)) + _moved = true; + + if (x == bufX && y == bufY) + return; + + bufX = x; + bufY = y; + + gtk_window_move(GTK_WINDOW(border), x, y); + } + else + { + gContainer::move(x,y); + } +} + + +void gMainWindow::updateSize() +{ + if (!isTopLevel() || !isOpened()) + return; + + #ifdef DEBUG_RESIZE + fprintf(stderr, "updateSize: %s: %d %d / %d / %d %d\n", name(), width(), height(), isResizable(), _csd_w, _csd_h); + #endif + + if (width() < 1 || height() < 1) + { + if (isVisible()) + gtk_widget_hide(border); + } + else + { + setGeometryHints(); + if (isResizable()) + gtk_window_resize(GTK_WINDOW(border), width(), height()); + else + { + //fprintf(stderr, "gMainWindow::updateSize: %s: %d %d\n", name(), width() + Max(_csd_w, 0), height() + Max(_csd_h, 0)); + gtk_widget_set_size_request(border, width() + Max(_csd_w, 0), height() + Max(_csd_h, 0)); + } + + if (isVisible()) + gtk_widget_show(border); + } +} + +bool gMainWindow::resize(int w, int h, bool no_decide) +{ + if (!isTopLevel()) + { + if (gContainer::resize(w, h, no_decide)) + return true; + } + else + { + if (w == bufW && h == bufH) + { + _resized = true; + return true; + } + + bufW = w < 0 ? 0 : w; + bufH = h < 0 ? 0 : h; + + // we check for _resized to ignore the first resize() + if (_resized && _default_min_w <= 0 && _default_min_h <= 0) + { + _default_min_w = w; + _default_min_h = h; + } + + updateSize(); + } + + _resized = true; + return false; +} + +bool gMainWindow::emitOpen() +{ + //fprintf(stderr, "emit Open: %p (%d %d) %d resizable = %d fullscreen = %d\n", this, width(), height(), _opened, isResizable(), fullscreen()); + + if (_opened) + return false; + + _opened = true; + _closed = false; + //_no_resize_event = true; // If the event loop is run during emitOpen(), some spurious configure events are received. + + updateSize(); + //performArrange(); + + gtk_widget_realize(border); + + CB_window_open(this); + if (_closed) + { + _opened = false; + return true; + } + + //fprintf(stderr, "emit Move & Resize: %p\n", this); + CB_window_move(this); + #ifdef DEBUG_RESIZE + fprintf(stderr, "cb_open\n"); + #endif + emitResize(); + + return false; +} + +void gMainWindow::present() +{ + if (_no_take_focus) + gtk_widget_show(GTK_WIDGET(border)); + else + gtk_window_present(GTK_WINDOW(border)); + + #ifdef GTK3 + updateStyleSheet(false); + #endif +} + +void gMainWindow::afterShow() +{ + if (_activate) + { + present(); + _activate = false; + } +} + +void gMainWindow::setTransientFor() +{ + gMainWindow *parent = _current; + + if (!parent) + parent = _active; + + if (parent) + { + parent = parent->topLevel(); + if (parent != this) + { + //fprintf(stderr, "setTransientFor: %s -> %s\n", name(), parent->name()); + gtk_window_set_transient_for(GTK_WINDOW(border), GTK_WINDOW(parent->border)); + } + } +} + +void gMainWindow::setVisible(bool vl) +{ + if (!vl) + _hidden = true; + + if (vl == isVisible()) + return; + + if (!isTopLevel()) + { + gContainer::setVisible(vl); + if (vl) + { + _hidden = false; + //setActiveWindow(this); + } + return; + } + + if (vl) + { + //bool arr = !isVisible(); + + emitOpen(); + if (!_opened) + return; + + _not_spontaneous = !isVisible(); + _visible = true; + _hidden = false; + + setTransparent(_transparent); // must not call gtk_window_present! + + if (isTopLevel()) + { + /*if (!_xembed) + { + fprintf(stderr, "gtk_window_group_add_window: %p -> %p\n", border, gApplication::currentGroup()); + gtk_window_group_add_window(gApplication::currentGroup(), GTK_WINDOW(border)); + fprintf(stderr, "-> %p\n", gtk_window_get_group(GTK_WINDOW(border))); + }*/ + + // Thanks for Ubuntu's GTK+ patching :-( + #ifndef GTK3 + //gtk_window_set_has_resize_grip(GTK_WINDOW(border), false); + if (g_object_class_find_property(G_OBJECT_GET_CLASS(border), "has-resize-grip")) + g_object_set(G_OBJECT(border), "has-resize-grip", false, (char *)NULL); + #endif + + gtk_window_move(GTK_WINDOW(border), bufX, bufY); + + /*if (isPopup()) + { + gtk_widget_show_now(border); + gtk_widget_grab_focus(border); + } + else + {*/ + present(); + //} + + if (!_title || !*_title) + gtk_window_set_title(GTK_WINDOW(border), gApplication::defaultTitle()); + + if (isUtility()) + { + setTransientFor(); + if (!_no_take_focus) + present(); + } + + #ifndef GTK3 + if (gApplication::mainWindow() == this) + { + int desktop = session_manager_get_desktop(); + if (desktop >= 0) + { + //fprintf(stderr, "X11_window_set_desktop: %d (%d)\n", desktop, true); + X11_window_set_desktop((Window)handle(), true, desktop); + session_manager_set_desktop(-1); + } + } + #endif + } + else + { + gtk_widget_show(border); + parent()->performArrange(); + performArrange(); + } + + drawMask(); + + _set_focus = true; + + if (isSkipTaskBar()) + _activate = true; + + /*if (arr) + { + fprintf(stderr, "#4\n"); + performArrange(); + }*/ + } + else + { + if (this == _active) + _initial_focus = gApplication::activeControl(); + + _not_spontaneous = isVisible(); + gContainer::setVisible(false); + + if (_popup) + gApplication::exitLoop(this); + + if (gApplication::_button_grab && !gApplication::_button_grab->isReallyVisible()) + gApplication::setButtonGrab(NULL); + } +} + + +void gMainWindow::setMinimized(bool vl) +{ + if (!isTopLevel()) return; + + _minimized = vl; + if (vl) gtk_window_iconify(GTK_WINDOW(border)); + else gtk_window_deiconify(GTK_WINDOW(border)); +} + +void gMainWindow::setMaximized(bool vl) +{ + if (!isTopLevel()) + return; + + _maximized = vl; + _csd_w = _csd_h = -1; + + if (vl) + gtk_window_maximize(GTK_WINDOW(border)); + else + gtk_window_unmaximize(GTK_WINDOW(border)); +} + +void gMainWindow::setFullscreen(bool vl) +{ + if (!isTopLevel()) + return; + + _fullscreen = vl; + _csd_w = _csd_h = -1; + + if (vl) + { + gtk_window_fullscreen(GTK_WINDOW(border)); + if (isVisible()) + present(); + } + else + gtk_window_unfullscreen(GTK_WINDOW(border)); +} + +void gMainWindow::center() +{ + if (!isTopLevel()) return; + +#ifdef GTK3 + + if (MAIN_platform_is_wayland) + gtk_window_set_position(GTK_WINDOW(border), GTK_WIN_POS_CENTER_ON_PARENT); + +#endif + + GdkRectangle rect; + int x, y; + + gtk_widget_realize(border); + gDesktop::availableGeometry(screen(), &rect); + + x = rect.x + (rect.width - width()) / 2; + y = rect.y + (rect.height - height()) / 2; + + move(x, y); +} + +bool gMainWindow::isModal() const +{ + if (!isTopLevel()) return false; + + return gtk_window_get_modal(GTK_WINDOW(border)); +} + +void gMainWindow::showModal() +{ + if (!isTopLevel()) return; + if (isModal() || isPopup()) return; + + gApplication::finishFocus(); + gMouse::finishEvent(); + + //show(); + setType(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_modal(GTK_WINDOW(border), true); + setTransientFor(); + + _save_focus = gApplication::activeControl(); + _previous = _current; + _current = this; + + center(); + show(); + //gtk_grab_add(border); + gApplication::enterLoop(this); + + _current = _previous; + _previous = NULL; + + //gtk_grab_remove(border); + gtk_window_set_modal(GTK_WINDOW(border), false); + + if (!_persistent) + destroy(); + else + hide(); + + if (_save_focus) + { + gApplication::setActiveControl(_save_focus, true); + _save_focus = NULL; + } +} + +void gMainWindow::showPopup(int x, int y) +{ + bool has_border; + int oldx, oldy; + GdkWindowTypeHint type; + + if (!isTopLevel()) return; + if (isModal() || isPopup()) return; + + gApplication::finishFocus(); + gMouse::finishEvent(); + + //gtk_widget_unrealize(border); + //((GtkWindow *)border)->type = GTK_WINDOW_POPUP; + //gtk_widget_realize(border); + + oldx = left(); + oldy = top(); + + _popup = true; + setType(GTK_WINDOW_POPUP); + + has_border = gtk_window_get_decorated(GTK_WINDOW(border)); + type = gtk_window_get_type_hint(GTK_WINDOW(border)); + + gtk_window_set_decorated(GTK_WINDOW(border), false); + gtk_window_set_type_hint(GTK_WINDOW(border), GDK_WINDOW_TYPE_HINT_COMBO); + + setTransientFor(); + + _save_focus = gApplication::activeControl(); + _previous = _current; + _current = this; + + gtk_window_resize(GTK_WINDOW(border), bufW, bufH); + move(x, y); + //raise(); + setFocus(); + + gApplication::enterPopup(this); + + _current = _previous; + _previous = NULL; + _popup = false; + + if (!_persistent) + destroy(); + else + { + hide(); + + gtk_window_set_decorated(GTK_WINDOW(border), has_border); + gtk_window_set_type_hint(GTK_WINDOW(border), type); + + move(oldx, oldy); + } + + if (_save_focus) + { + gApplication::setActiveControl(_save_focus, true); + _save_focus = NULL; + } +} + +void gMainWindow::showActivate() +{ + bool v = isTopLevel() && isVisible() && !_no_take_focus; + + setType(GTK_WINDOW_TOPLEVEL); + + if (!_moved) + center(); + emitOpen(); + if (!_opened) + return; + show(); + if (v) + present(); +} + +void gMainWindow::activate() +{ + if (isTopLevel() && isVisible()) + present(); +} + +void gMainWindow::showPopup() +{ + int x, y; + gMouse::getScreenPos(&x, &y); + showPopup(x, y); +} + +void gMainWindow::restack(bool raise) +{ + if (!isTopLevel()) + { + gControl::restack(raise); + return; + } + + if (raise) + present(); + else + gdk_window_lower(gtk_widget_get_window(border)); +} + +const char* gMainWindow::text() +{ + return _title; +} + +void gMainWindow::setText(const char *txt) +{ + if (txt != _title) + { + if (_title) + { + g_free(_title); + _title = NULL; + } + + if (txt && *txt) + _title = g_strdup(txt); + } + + if (isTopLevel()) + gtk_window_set_title(GTK_WINDOW(border), _title ? _title : ""); +} + +bool gMainWindow::hasBorder() +{ + if (isTopLevel()) + return gtk_window_get_decorated(GTK_WINDOW(border)); + else + return false; +} + +bool gMainWindow::isResizable() +{ + if (isTopLevel()) + return _resizable; + else + return false; +} + +void gMainWindow::setBorder(bool b) +{ + if (!isTopLevel()) + return; + + gtk_window_set_decorated(GTK_WINDOW(border), b); +} + +void gMainWindow::setResizable(bool b) +{ + if (!isTopLevel()) + return; + + if (b == isResizable()) + return; + + _resizable = b; + updateSize(); +} + +void gMainWindow::setSkipTaskBar(bool b) +{ + if (!isTopLevel()) return; + _skip_taskbar = b; + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(border), b); +} + + +/*gPicture* gMainWindow::icon() +{ + GdkPixbuf *buf; + gPicture *pic; + + if (!isTopLevel()) return NULL; + + buf=gtk_window_get_icon(GTK_WINDOW(border)); + if (!buf) return NULL; + + pic=gPicture::fromPixbuf(buf); + + return pic; +}*/ + +void gMainWindow::setIcon(gPicture *pic) +{ + gPicture::assign(&_icon, pic); + + if (!isTopLevel()) return; + gtk_window_set_icon(GTK_WINDOW(border), pic ? pic->getPixbuf() : NULL); +} + + +void gMainWindow::setTopOnly(bool vl) +{ + if (!isTopLevel()) return; + + _top_only = vl; + gtk_window_set_keep_above (GTK_WINDOW(border), vl); +} + + +void gMainWindow::setMask(bool vl) +{ + if (_mask == vl) + return; + + _mask = vl; + drawMask(); +} + +void gMainWindow::setPicture(gPicture *pic) +{ + gPicture::assign(&_picture, pic); + drawMask(); +} + +void gMainWindow::remap() +{ + if (!isVisible()) + return; + + gtk_widget_unmap(border); + gtk_widget_map(border); + + if (_skip_taskbar) { setSkipTaskBar(false); setSkipTaskBar(true); } + if (_top_only) { setTopOnly(false); setTopOnly(true); } + if (_sticky) { setSticky(false); setSticky(true); } + if (stack) { setStacking(0); setStacking(stack); } +} + +void gMainWindow::drawMask() +{ + bool do_remap = false; + + if (!isVisible()) + return; + +#ifdef GTK3 + + cairo_region_t *mask; + + if (_mask && _picture) + mask = gdk_cairo_region_create_from_surface(_picture->getSurface()); + else + mask = NULL; + + gdk_window_shape_combine_region(gtk_widget_get_window(border), mask, 0, 0); + if (mask) + cairo_region_destroy(mask); + + refresh(); + +#else + + GdkBitmap *mask = (_mask && _picture) ? _picture->getMask() : NULL; + do_remap = !mask && _masked; + + gdk_window_shape_combine_mask(border->window, mask, 0, 0); + +#endif + + if (_picture) + { + gtk_widget_set_app_paintable(border, TRUE); + gtk_widget_realize(border); + gtk_widget_realize(widget); + // What for?? + /*for (int i = 0; i < controlCount(); i++) + getControl(i)->refresh();*/ + } + else if (!_transparent) + { + gtk_widget_set_app_paintable(border, FALSE); + setRealBackground(background()); + } + + _masked = mask != NULL; + + if (do_remap) + remap(); + else + { + if (!_skip_taskbar) + { + setSkipTaskBar(true); + setSkipTaskBar(false); + } + } +} + +int gMainWindow::menuCount() +{ + if (!menuBar) return 0; + return gMenu::winChildCount(this); +} + +void gMainWindow::setPersistent(bool vl) +{ + _persistent = vl; +} + +bool gMainWindow::doClose(bool destroying) +{ + if (_closing || _closed) + return false; + + if (!isTopLevel()) + { + if (_opened) + { + _closing = true; + _closed = !CB_window_close(this); + _closing = false; + _opened = !_closed; + } + else + _closed = true; + + if (_closed) + { + if (_persistent || destroying) + hide(); + else + destroy(); + } + } + else + { + if (_opened) + { + if (isModal() && !gApplication::hasLoop(this)) + return true; + + _closing = true; + _closed = !CB_window_close(this); + _closing = false; + _opened = !_closed; + + if (!_opened && isModal()) + gApplication::exitLoop(this); + } + + if (!_opened) // && !modal()) + { + if (_active == this) + setActiveWindow(NULL); + + if (!isModal()) + { + if (_persistent || destroying) + hide(); + else + destroy(); + } + } + } + + return _opened; +} + + +bool gMainWindow::close() +{ + return doClose(); +} + +static void hide_hidden_children(gContainer *cont) +{ + int i; + gControl *child; + + for (i = 0;; i++) + { + child = cont->child(i); + if (!child) + break; + if (!child->isVisible()) + gtk_widget_hide(child->border); + else if (child->isContainer()) + hide_hidden_children((gContainer *)child); + } +} + +void gMainWindow::createWindow(GtkWidget *new_border) +{ + gt_widget_reparent(frame, new_border); + createBorder(new_border); + updateEventMask(); + registerControl(); +} + +void gMainWindow::reparent(gContainer *newpr, int x, int y) +{ + int w, h; + gColor fg, bg; + bool was_visible; + + if (_xembed) + return; + + bg = background(); + fg = foreground(); + was_visible = isVisible(); + + if (isTopLevel() && newpr) + { + windows = g_list_remove(windows, (gpointer)this); + gtk_window_remove_accel_group(GTK_WINDOW(topLevel()->border), accel); + + createWindow(gtk_event_box_new()); + + setParent(newpr); + connectParent(); + //newpr->insert(this); + borderSignals(); + + initWindow(); + + setBackground(bg); + setForeground(fg); + setFont(font()); + + checkMenuBar(); + + bufX = bufY = 0; + move(x, y); + + gtk_widget_set_size_request(border, width(), height()); + + if (was_visible) + gtk_widget_show(border); + // Hidden children are incorrectly shown. Fix that! + hideHiddenChildren(); + + if (!isIgnore()) + newpr->performArrange(); + } + else if ((!isTopLevel() && !newpr) + || (isTopLevel() && isPopup())) + { + windows = g_list_append(windows, (gpointer)this); + gtk_window_remove_accel_group(GTK_WINDOW(topLevel()->border), accel); + // TODO: test that + + createWindow(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + + if (parent()) + { + parent()->remove(this); + if (!isIgnore()) + parent()->performArrange(); + setParent(NULL); + } + + borderSignals(); + initWindow(); + + setBackground(bg); + setForeground(fg); + setFont(font()); + setText(text()); + + move(x, y); + w = width(); + h = height(); + bufW = bufH = -1; + gtk_widget_set_size_request(border, 1, 1); + resize(w, h); + + gtk_widget_set_sensitive(frame, FALSE); + gtk_widget_set_sensitive(frame, TRUE); + + if (was_visible) + present(); + hideHiddenChildren(); + + _popup = false; //type == GTK_WINDOW_POPUP; + } + else + { + gContainer::reparent(newpr, x, y); + } +} + +void gMainWindow::setType(GtkWindowType type) +{ + int w, h; + gColor bg, fg; + + if (!isTopLevel()) + return; + if (gtk_window_get_window_type(GTK_WINDOW(border)) == type) + return; + + bg = background(); + fg = foreground(); + + gtk_window_remove_accel_group(GTK_WINDOW(border), accel); + // TODO: test that + + createWindow(gtk_window_new(type)); + + initWindow(); + borderSignals(); + setBackground(bg); + setForeground(fg); + setFont(font()); + + w = width(); + h = height(); + bufW = bufH = -1; + gtk_widget_set_size_request(border, 1, 1); + resize(w, h); + + hideHiddenChildren(); +} + +static void fill_children_list(gContainer *cont, GPtrArray *list) +{ + int i; + gControl *control; + + for (i = 0; i < cont->childCount(); i++) + { + control = cont->child(i); + if (control->isContainer()) // && !control->isWindow()) + fill_children_list((gContainer *)control, list); + g_ptr_array_add(list, control); + } +} + +GPtrArray *gMainWindow::getControlList() +{ + GPtrArray *list = g_ptr_array_new(); + fill_children_list(this, list); + return list; +} + +gControl *gMainWindow::getControl(const char *name) +{ + GPtrArray *list = getControlList(); + uint i; + gControl *ctrl; + + for (i = 0; i < list->len; i++) + { + ctrl = (gControl *)g_ptr_array_index(list, i); + if (!ctrl->isDestroyed() && !strcasecmp(ctrl->name(), name)) + break; + ctrl = NULL; + } + + g_ptr_array_unref(list); + return ctrl; +} + +int gMainWindow::clientX() +{ + return 0; +} + +int gMainWindow::containerX() +{ + return 0; +} + +int gMainWindow::clientY() +{ + if (isMenuBarVisible()) + return menuBarHeight(); + else + return 0; +} + +int gMainWindow::containerY() +{ + return 0; +} + + +int gMainWindow::clientWidth() +{ + return width(); +} + + +int gMainWindow::menuBarHeight() +{ + int h = 0; + + if (menuBar) + { + //gtk_widget_show(GTK_WIDGET(menuBar)); + //fprintf(stderr, "menuBarHeight: gtk_widget_get_visible: %d\n", gtk_widget_get_visible(GTK_WIDGET(menuBar))); +#ifdef GTK3 + gtk_widget_get_preferred_height(GTK_WIDGET(menuBar), NULL, &h); +#else + GtkRequisition req = { 0, 0 }; + gtk_widget_size_request(GTK_WIDGET(menuBar), &req); + h = req.height; +#endif + //fprintf(stderr, "menuBarHeight: %d\n", h); + } + + return h; +} + +int gMainWindow::clientHeight() +{ + if (isMenuBarVisible()) + return height() - menuBarHeight(); + else + return height(); +} + +void gMainWindow::setActiveWindow(gControl *control) +{ + _active = CB_window_activate(control); +} + +#ifdef GDK_WINDOWING_X11 +bool gMainWindow::isUtility() const +{ + return _utility; +} + +void gMainWindow::setUtility(bool v) +{ + bool remap = false; + + if (!isTopLevel()) + return; + + // TODO: works only if the window is not mapped! + + _utility = v; +#if GTK_CHECK_VERSION(2, 20, 0) + if (gtk_widget_get_mapped(border)) +#else + if (GTK_WIDGET_MAPPED(border)) +#endif + { + remap = true; + gtk_widget_unmap(border); + } + + gtk_window_set_type_hint(GTK_WINDOW(border), v ? GDK_WINDOW_TYPE_HINT_DIALOG : GDK_WINDOW_TYPE_HINT_NORMAL); + + if (remap) + gtk_widget_map(border); +} +#else +bool gMainWindow::isUtility() +{ + return _utility; +} + +void gMainWindow::setUtility(bool v) +{ + _utility = v; +} +#endif + +void gMainWindow::configure() +{ + static bool init = FALSE; + static GB_FUNCTION _init_menubar_shortcut_func; + + int h; + + if (bufW < 1 || bufH < 1) + return; + + if (_initMenuBar != isMenuBarVisible()) + { + _initMenuBar = !_initMenuBar; + + if (!init) + { + GB.GetFunction(&_init_menubar_shortcut_func, (void *)GB.FindClass("_Gui"), "_InitMenuBarShortcut", NULL, NULL); + init = TRUE; + } + + GB.Push(1, GB_T_OBJECT, hFree); + GB.Call(&_init_menubar_shortcut_func, 1, FALSE); + } + + h = menuBarHeight(); + + #ifdef DEBUG_RESIZE + fprintf(stderr, "configure: %s: menu = %d h = %d / %d x %d\n", name(), isMenuBarVisible(), h, width(), height()); + #endif + + if (isMenuBarVisible()) + { + gtk_fixed_move(GTK_FIXED(frame), GTK_WIDGET(menuBar), 0, 0); + if (h > 1) + gtk_widget_set_size_request(GTK_WIDGET(menuBar), width(), h); + gtk_fixed_move(GTK_FIXED(frame), widget, 0, h); + gtk_widget_set_size_request(widget, width(), Max(0, height() - h)); + } + else + { + if (menuBar) + gtk_fixed_move(GTK_FIXED(frame), GTK_WIDGET(menuBar), -width(), -h); + gtk_fixed_move(GTK_FIXED(frame), widget, 0, 0); + gtk_widget_set_size_request(widget, width(), height()); + } +} + +bool gMainWindow::setMenuBarVisible(bool v) +{ + if (_showMenuBar == v) + return true; + + _showMenuBar = v; + + if (!menuBar) + return true; + + configure(); + performArrange(); + + return false; +} + +bool gMainWindow::isMenuBarVisible() +{ + //fprintf(stderr, "isMenuBarVisible: %d\n", !!(menuBar && !_hideMenuBar && _showMenuBar)); + return menuBar && !_hideMenuBar && _showMenuBar; //|| (menuBar && GTK_WIDGET_MAPPED(GTK_WIDGET(menuBar))); +} + +void gMainWindow::updateFont() +{ + gContainer::updateFont(); + gMenu::updateFont(this); + CB_window_font(this); +} + +void gMainWindow::checkMenuBar() +{ + int i; + gMenu *menu; + + //fprintf(stderr, "gMainWindow::checkMenuBar\n"); + + if (menuBar) + { + _hideMenuBar = true; + for (i = 0;; i++) + { + menu = gMenu::winChildMenu(this, i); + if (!menu) + break; + if (menu->isVisible() && !menu->isSeparator()) + { + _hideMenuBar = false; + break; + } + } + } + + configure(); + performArrange(); +} + +void gMainWindow::embedMenuBar(GtkWidget *border) +{ + if (menuBar) + { + g_object_ref(G_OBJECT(menuBar)); + + if (gtk_widget_get_parent(GTK_WIDGET(menuBar))) + gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(menuBar))), GTK_WIDGET(menuBar)); + + gtk_fixed_put(GTK_FIXED(frame), GTK_WIDGET(menuBar), 0, 0); + + g_object_unref(G_OBJECT(menuBar)); + + gtk_widget_show(GTK_WIDGET(menuBar)); + + gMenu::updateFont(this); + gMenu::updateColor(this); + + checkMenuBar(); + } +} + +/*bool gMainWindow::getScreenPos(int *x, int *y) +{ + return gContainer::getScreenPos(x, y); +}*/ + +double gMainWindow::opacity() +{ + if (isTopLevel()) +#if GTK_CHECK_VERSION(3, 8, 0) + return gtk_widget_get_opacity(border); +#else + return gtk_window_get_opacity(GTK_WINDOW(border)); +#endif + else + return 1.0; +} + +void gMainWindow::setOpacity(double v) +{ + if (isTopLevel()) +#if GTK_CHECK_VERSION(3, 8, 0) + gtk_widget_set_opacity(border, v); +#else + gtk_window_set_opacity(GTK_WINDOW(border), v); +#endif +} + +int gMainWindow::screen() +{ + gMainWindow *tl = topLevel(); +#if GTK_CHECK_VERSION(3, 22, 0) + GdkWindow *window = gtk_widget_get_window(tl->border); + if (window) + return gt_find_monitor(gdk_display_get_monitor_at_window(gdk_display_get_default(), window)); + else + return -1; +#else + return gdk_screen_get_number(gtk_window_get_screen(GTK_WINDOW(tl->border))); +#endif +} + +void gMainWindow::emitResize() +{ + if (bufW == _resize_last_w && bufH == _resize_last_h) + return; + + #ifdef DEBUG_RESIZE + fprintf(stderr, "emitResize: %s: %d %d\n", name(), bufW, bufH); + #endif + _resize_last_w = bufW; + _resize_last_h = bufH; + configure(); + performArrange(); + CB_window_resize(this); +} + +static void emit_resize_later(gMainWindow *window) +{ + window->emitResize(); +} + +void gMainWindow::emitResizeLater() +{ + GB.Post((GB_CALLBACK)emit_resize_later, (intptr_t)this); +} + +void gMainWindow::setGeometryHints() +{ + GdkGeometry geometry; + int min_w, min_h; + + if (isTopLevel()) + { + min_w = _min_w; + min_h = _min_h; + + if (isResizable()) + { + if (isModal() || isUtility()) + { + if (!min_w && !min_h) + { + min_w = _default_min_w; + min_h = _default_min_h; + } + } + + geometry.min_width = min_w + Max(_csd_w, 0); + geometry.min_height = min_h + Max(_csd_h, 0); + + geometry.max_width = 32767; + geometry.max_height = 32767; + } + else + { + geometry.max_width = geometry.min_width = width() + Max(_csd_w, 0); + geometry.max_height = geometry.min_height = height() + Max(_csd_h, 0); + } + + #if DEBUG_RESIZE + fprintf(stderr, "setGeometryHints: %s: min size: %d %d (%d x %d)\n", name(), geometry.min_width, geometry.min_height, width(), height()); + #endif + gtk_window_set_geometry_hints(GTK_WINDOW(border), NULL, &geometry, (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); + //gdk_window_set_geometry_hints(gtk_widget_get_window(border), &geometry, (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_POS)); + } +} + +void gMainWindow::setBackground(gColor vl) +{ + if (!_transparent) + gControl::setBackground(vl); + else + _bg = vl; +} + +void gMainWindow::setTransparent(bool vl) +{ + if (!vl) + return; + + _transparent = TRUE; + + /*#ifdef GTK3 + if (MAIN_platform_is_wayland) + return; + #endif*/ + + if (!isVisible()) + return; + +#ifdef GTK3 + GdkScreen *screen = NULL; + GdkVisual *visual = NULL; + + screen = gtk_widget_get_screen(border); + visual = gdk_screen_get_rgba_visual(screen); + if (visual == NULL) + return; +#else + GdkScreen *screen; + GdkColormap *colormap; + + screen = gtk_widget_get_screen(border); + colormap = gdk_screen_get_rgba_colormap(screen); + if (colormap == NULL) + return; +#endif + + gtk_widget_unrealize(border); + + gtk_widget_set_app_paintable(border, TRUE); + +#ifdef GTK3 + gtk_widget_set_visual(border, visual); +#else + gtk_widget_set_colormap(border, colormap); +#endif + + gtk_widget_realize(border); + + /*int w = width(); + int h = height(); + + bufW = w - 1; + resize(w, h);*/ + + //gtk_window_present(GTK_WINDOW(border)); + //updateSize(); +} + +bool gMainWindow::closeAll() +{ + int i; + gMainWindow *win; + + for(i = 0; i < count(); i++) + { + win = get(i); + if (!win) + break; + if (!win->isTopLevel()) + continue; + if (win == gApplication::mainWindow()) + continue; + if (win->close()) + return true; + } + + return false; +} + +void gMainWindow::setNoTakeFocus(bool v) +{ + _no_take_focus = v; + if (isTopLevel()) + gtk_window_set_focus_on_map(GTK_WINDOW(border), !_no_take_focus); +} + +void gMainWindow::calcCsdSize() +{ + GtkAllocation ba; + GtkAllocation wa; + + if (_csd_w >= 0) + return; + + if (!isTopLevel()) + { + _csd_w = _csd_h = 0; + return; + } + + gtk_widget_get_allocation(border, &ba); + if (ba.width <= 1 && ba.height <= 1) + return; + + gtk_widget_get_allocation(frame, &wa); + if (wa.width <= 1 && wa.height <= 1) + return; + + _csd_w = ba.width - wa.width; + _csd_h = ba.height - wa.height; + #ifdef DEBUG_RESIZE + fprintf(stderr, "calcCsdSize: border: %d %d layout: %d %d\n", ba.width, ba.height, wa.width, wa.height); + fprintf(stderr, "calcCsdSize: --> %s: csd = %d %d\n", name(), _csd_w, _csd_h); + #endif + + if (!isResizable()) + updateSize(); + else + setGeometryHints(); +} + +void gMainWindow::destroy() +{ + doClose(true); + gControl::destroy(); +} + +void gMainWindow::setCustomMinimumSize(int w, int h) +{ + w = Max(0, w); + h = Max(0, h); + if (w == _min_w && h == _min_h) + return; + _min_w = w; + _min_h = h; + updateSize(); +} + +void gMainWindow::getCustomMinimumSize(int *w, int *h) const +{ + *w = _min_w; + *h = _min_h; +} + +gControl *gMainWindow::getInitialFocus() +{ + gControl *ctrl; + + if (!_set_focus) + return this; + + _set_focus = false; + + if (_initial_focus) + { + ctrl = _initial_focus; + _initial_focus = NULL; + //fprintf(stderr, "focus = %p %s\n", focus->border, focus->name()); + //focus->setFocus(); + //fprintf(stderr, "focus of window %p -> %p\n", border, gtk_window_get_focus(GTK_WINDOW(border))); + //focus = NULL; + } + else + { + ctrl = this; + + for(;;) + { + ctrl = ctrl->nextFocus(); + if (!ctrl) + break; + + if (ctrl->isReallyVisible() && ctrl->isEnabled() && !ctrl->isWindow() && ctrl->canFocus()) + break; + + if (ctrl == this) + break; + } + } + + return ctrl ? ctrl : this; +} + +#ifdef GTK3 +GtkWidget *gMainWindow::getStyleSheetWidget() +{ + return frame; +} +#endif diff --git a/gb.gtk/src/gmainwindow.h b/gb.gtk/src/gmainwindow.h new file mode 100644 index 00000000..03f48132 --- /dev/null +++ b/gb.gtk/src/gmainwindow.h @@ -0,0 +1,230 @@ +/*************************************************************************** + + gmainwindow.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMAINWINDOW_H +#define __GMAINWINDOW_H + +#include "gbutton.h" + +class gMainWindow : public gContainer +{ +public: + gMainWindow(); + gMainWindow(gContainer *parent); + gMainWindow(int plug); + ~gMainWindow(); + +//"Properties" + bool hasBorder(); + bool isResizable(); + bool isUtility() const; + bool isEmbedded() const { return _xembed; } + gPicture *icon() { return _icon; } + gPicture *picture() { return _picture; } + bool mask() { return _mask; } + int menuCount(); + bool isModal() const; + const char *text(); + bool isTopOnly() const { return isTopLevel() && _top_only; } + bool isSkipTaskBar() const { return isTopLevel() && _skip_taskbar; } + bool minimized() const { return _minimized; } + bool maximized() const { return _maximized; } + bool fullscreen() const { return _fullscreen; } + bool isSticky() const { return isTopLevel() && _sticky; }; + int getStacking(); + bool isPersistent() const { return _persistent; } + bool isOpened() const { return _opened; } + bool isClosed() const { return _closed; } + bool isHidden() const { return _hidden; } + bool isPopup() const { return _popup; } + bool isTransparent() const { return _transparent; } + bool isNoTakeFocus() const { return _no_take_focus; } + int screen(); + + GPtrArray *getControlList(); + gControl *getControl(const char *name); + + void setBorder(bool b); + void setResizable(bool b); + void setUtility(bool v); + void setIcon(gPicture *pic); + void setMask(bool vl); + void setPicture(gPicture *pic); + void setText(const char *txt); + void setTopOnly(bool vl); + void setSkipTaskBar(bool b); + void setMinimized(bool vl); + void setMaximized(bool vl); + void setFullscreen(bool vl); + void setSticky(bool vl); + void setStacking(int vl); + void setPersistent(bool vl); + void setTransparent(bool vl); + void setNoTakeFocus(bool vl); + + void setCustomMinimumSize(int w, int h); + void getCustomMinimumSize(int *w, int *h) const; + + virtual void setVisible(bool vl); + virtual void setBackground(gColor vl); + virtual void setRealBackground(gColor vl); + virtual void setRealForeground(gColor vl); + + virtual int clientWidth(); + virtual int clientHeight(); + virtual int clientX(); + virtual int clientY(); + virtual int containerX(); + virtual int containerY(); + + //virtual bool getScreenPos(int *x, int *y); + + bool spontaneous() { return !_not_spontaneous; } + + bool setMenuBarVisible(bool v); + bool isMenuBarVisible(); + + double opacity(); + void setOpacity(double v); + +//"Methods" + void center(); + void showActivate(); + void showModal(); + void showPopup(); + void showPopup(int x, int y); + void activate(); + virtual void move(int x, int y); + virtual bool resize(int w, int h, bool no_decide = false); + bool close(); + virtual void reparent(gContainer *newpr, int x, int y); + virtual void destroy(); + virtual void restack(bool raise); + +//"Static" + static GList *windows; + static int count() { return g_list_length(windows); } + static gMainWindow *get(int index) { return (gMainWindow *)g_list_nth_data(windows, index); } + static gMainWindow *_active; + static void setActiveWindow(gControl *control); + static gMainWindow *_current; + static bool closeAll(); + +//"Private" + void initialize(); + void drawMask(); + void initWindow(); + bool emitOpen(); + void remap(); + bool doClose(bool destroying = false); + void afterShow(); + void checkMenuBar(); + int menuBarHeight(); + void configure(); + void embedMenuBar(GtkWidget *border); + void emitResize(); + void emitResizeLater(); + void setGeometryHints(); + virtual void updateFont(); + void present(); + + void setTransientFor(); + void setType(GtkWindowType type); + void calcCsdSize(); + void createWindow(GtkWidget *new_border); + void updateSize(); + gControl *getInitialFocus(); +#ifdef GTK3 + virtual GtkWidget *getStyleSheetWidget(); +#endif + + GtkWindowGroup *group; + GtkAccelGroup *accel; + GtkMenuBar *menuBar; + int stack; + gPicture *_icon; + gPicture *_picture; + char *_title; + gMainWindow *_previous; + + gControl *_initial_focus; + gControl *_save_focus; + gButton *_default; + gButton *_cancel; + + int _resize_last_w; + int _resize_last_h; + + int _min_w; + int _min_h; + int _default_min_w; + int _default_min_h; + + int _csd_w; + int _csd_h; + + unsigned _mask : 1; + unsigned _top_only : 1; + unsigned _persistent : 1; + unsigned _sticky : 1; + unsigned _opened : 1; + unsigned _closed : 1; + unsigned _closing : 1; + unsigned _not_spontaneous : 1; + unsigned _skip_taskbar : 1; + unsigned _masked : 1; + unsigned _xembed : 1; + unsigned _activate : 1; + unsigned _hidden : 1; + unsigned _hideMenuBar : 1; + unsigned _showMenuBar : 1; + unsigned _popup : 1; + unsigned _maximized : 1; + unsigned _minimized : 1; + unsigned _fullscreen : 1; + unsigned _utility : 1; + unsigned _transparent : 1; + unsigned _no_take_focus : 1; + unsigned _moved : 1; + unsigned _resized : 1; + unsigned _resizable : 1; + unsigned _unmap : 1; + unsigned _initMenuBar : 1; + unsigned _grab_on_show : 1; + unsigned _frame_init : 1; + unsigned _set_focus : 1; +}; + +// Callbacks + +void CB_window_open(gMainWindow *sender); +void CB_window_show(gMainWindow *sender); +void CB_window_hide(gMainWindow *sender); +void CB_window_move(gMainWindow *sender); +void CB_window_resize(gMainWindow *sender); +bool CB_window_close(gMainWindow *sender); +gMainWindow *CB_window_activate(gControl *sender); +void CB_window_state(gMainWindow *sender); +void CB_window_font(gMainWindow *sender); + +#endif diff --git a/gb.gtk/src/gmenu.cpp b/gb.gtk/src/gmenu.cpp new file mode 100644 index 00000000..85af2ca3 --- /dev/null +++ b/gb.gtk/src/gmenu.cpp @@ -0,0 +1,1375 @@ +/*************************************************************************** + + gmenu.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gmainwindow.h" +#include "gapplication.h" +#include "gdesktop.h" +#include "gmouse.h" +#include "gmenu.h" +#include "CStyle.h" + +//#define DEBUG_DELETE 1 + +typedef + struct { + int x; + int y; + } + MenuPosition; + +gMenu *gMenu::_current_popup = NULL; +int gMenu::_in_popup = 0; +int gMenu::_popup_count = 0; + +static GList *menus = NULL; +static GList *menus_destroyed = NULL; + +static gint my_menu_shell_enter_notify(GtkWidget *widget, GdkEventCrossing *event) +{ + GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS(widget); + GtkWidget *menu_item; + gMenu *menu; + + if (event->mode == GDK_CROSSING_GTK_GRAB || + event->mode == GDK_CROSSING_GTK_UNGRAB || + event->mode == GDK_CROSSING_STATE_CHANGED) + goto __PREVIOUS; + + menu_item = gtk_get_event_widget((GdkEvent*) event); + if (!menu_item) + goto __PREVIOUS; + + menu = (gMenu *)g_object_get_data(G_OBJECT(menu_item), "gambas-menu"); + if (menu) + menu->ensureChildMenu(); + +__PREVIOUS: + + if (klass->_gtk_reserved6) + return ((gint (*)(GtkWidget *, GdkEventCrossing *))klass->_gtk_reserved6)(widget, event); + else + return 0; +} + +static void patch_classes(void) +{ + GtkWidgetClass *klass; + + klass = (GtkWidgetClass *)g_type_class_peek(GTK_TYPE_MENU_SHELL); + if (klass->enter_notify_event != my_menu_shell_enter_notify) + { + //fprintf(stderr, "patch_class: %p\n", klass->enter_notify_event); + klass->_gtk_reserved6 = (void (*)())klass->enter_notify_event; + klass->enter_notify_event = my_menu_shell_enter_notify; + } + + klass = (GtkWidgetClass *)g_type_class_peek(GTK_TYPE_MENU_BAR); + if (klass->enter_notify_event != my_menu_shell_enter_notify) + { + //fprintf(stderr, "patch_class: %p\n", klass->enter_notify_event); + klass->_gtk_reserved6 = (void (*)())klass->enter_notify_event; + klass->enter_notify_event = my_menu_shell_enter_notify; + } +} + + +static void cb_destroy(GtkWidget *object, gMenu *data) +{ + if (data->ignoreDestroy()) + return; + + delete data; +} + +static void cb_activate(GtkMenuItem *menuitem, gMenu *data) +{ + if (data->ignoreActivate()) + return; + + if (data->_popup) + return; + + if (data->radio()) + data->updateRadio(); + else if (data->toggle()) + data->updateChecked(); + else if (data->checked()) + { + data->_ignore_activate = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), true); + } + + //fprintf(stderr, "cb_activate: %s\n", data->name()); + CB_menu_click(data); +} + +static void cb_show(GtkWidget *menu, gMenu *data) +{ + if (!data->_opened) + { + data->_opened = true; + CB_menu_show(data); + //gtk_menu_reposition(GTK_MENU(menu)); + } +} + +static gboolean cb_map(GtkWidget *menu, gMenu *data) +{ + //fprintf(stderr, "cb_map: >>> %s %d\n", data->name(), data->_mapping); + + if (data->_mapping) + return false; + + data->_mapping = true; + + gtk_widget_hide(gtk_widget_get_parent(menu)); + data->hideSeparators(); + gtk_widget_show(gtk_widget_get_parent(menu)); + gtk_menu_reposition(GTK_MENU(menu)); + + data->_mapping = false; + + //fprintf(stderr, "cb_map: <<<\n"); + return false; +} + +static gboolean cb_unmap(GtkWidget *menu, gMenu *data) +{ + //fprintf(stderr, "cb_unmap: >>> %s %d\n", data->name(), data->_mapping); + + if (data->_mapping) + return false; + + data->_opened = false; + CB_menu_hide(data); + //gtk_widget_set_size_request(menu, -1, -1); + + //fprintf(stderr, "cb_unmap: <<<\n"); + return false; +} + +static int get_menu_pos(GtkWidget *menu) +{ + GList *children, *iter; + int pos; + + if (!gtk_widget_get_parent(menu)) + { + //g_debug("get_menu_pos: no parent for menu %p", menu); + return -1; + } + + children = gtk_container_get_children(GTK_CONTAINER(gtk_widget_get_parent(menu))); + iter = g_list_first(children); + + for(pos = 0;; pos++) + { + if (iter->data == (gpointer)menu) + break; + iter = g_list_next(iter); + } + + g_list_free(children); + + return pos; +} + + +void gMenu::update() +{ + GtkMenuShell *shell = NULL; + gint pos; + int size; + int ds = gDesktop::scale(); + char *buf; + + if (!_text || !*_text) + _style = SEPARATOR; + else if (!_popup && (_radio || _toggle || _checked)) + _style = CHECK; + else + _style = NORMAL; + + if (_no_update) + return; + + //g_debug("%p: START UPDATE (menu = %p _popup = %p parent = %p)", this, menu); + + if (_style != _oldstyle) + { + //fprintf(stderr, "update %s\n", name()); + + if (_popup) + { + g_object_ref(G_OBJECT(_popup)); + if (_style == NORMAL) + gtk_menu_item_set_submenu(menu, NULL); + } + + if (menu) + { + pos = get_menu_pos(GTK_WIDGET(menu)); + //shell = (GtkMenuShell*)GTK_WIDGET(menu)->parent; + if (_style != NOTHING) + _ignore_destroy = true; + gtk_widget_hide(GTK_WIDGET(menu)); + gtk_widget_destroy(GTK_WIDGET(menu)); + _shortcut_key = 0; + _shortcut_mods = (GdkModifierType)0; + //g_debug("%p: delete old menu/separator", this); + } + else + { + pos = -1; + //shell = NULL; + } + + if (_style != NOTHING) + { + if (_style == SEPARATOR) + { + menu = (GtkMenuItem *)gtk_separator_menu_item_new(); + + hbox = NULL; + label = NULL; + shlabel = NULL; + image = NULL; + + #ifdef GTK3 + #else + GtkRequisition req; + gtk_widget_size_request(GTK_WIDGET(menu), &req); + if (req.height > 5) + gtk_widget_set_size_request(GTK_WIDGET(menu), -1, 5); + #endif + //g_debug("%p: create new separator %p", this, menu); + } + else + { + if (_style == CHECK) + { + menu = (GtkMenuItem *)gtk_check_menu_item_new(); + if (radio()) + gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(menu), true); + if (checked()) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), true); + } + else + { + menu = (GtkMenuItem *)gtk_menu_item_new(); + } + //g_debug("%p: create new menu %p", this, menu); + + if (!_toplevel) + { + #ifdef GTK3 + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, ds); + #else + hbox = gtk_hbox_new(false, ds); + #endif + //set_gdk_bg_color(hbox, 0xFF0000); + + image = gtk_image_new(); + //check = gtk_fixed_new(); + label = gtk_label_new_with_mnemonic(""); + shlabel = gtk_label_new(""); + + #if GTK_CHECK_VERSION(3, 16, 0) + gtk_label_set_xalign(GTK_LABEL(shlabel), 0); + #else + gtk_misc_set_alignment(GTK_MISC(shlabel), 0, 0.5); + #endif + gtk_size_group_add_widget(parentMenu()->getSizeGroup(), shlabel); + + size = window()->font()->height(); + + //gtk_widget_set_size_request(check, size, size); + //ON_DRAW(check, this, cb_check_expose, cb_check_draw); + //g_signal_connect_after(G_OBJECT(check), "expose-event", G_CALLBACK(cb_check_expose), (gpointer)this); + + gtk_widget_set_size_request(image, size, size); + + gtk_container_add(GTK_CONTAINER(menu), GTK_WIDGET(hbox)); + //gtk_box_pack_start(GTK_BOX(hbox), check, false, false, 0); + gtk_box_pack_start(GTK_BOX(hbox), image, false, false, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, false, false, 0); + gtk_box_pack_end(GTK_BOX(hbox), shlabel, false, false, 0); + } + else + { + hbox = NULL; + shlabel = NULL; + image = NULL; + //check = NULL; + label = gtk_label_new_with_mnemonic(""); + gtk_container_add(GTK_CONTAINER(menu), label); + } + + if (_popup) + { + gtk_menu_item_set_submenu(menu, GTK_WIDGET(_popup)); + g_object_unref(G_OBJECT(_popup)); + } + + + //set_gdk_fg_color(label, get_gdk_fg_color(GTK_WIDGET(shell))); + //set_gdk_bg_color(label, get_gdk_bg_color(GTK_WIDGET(shell))); + } + + gtk_widget_show_all(GTK_WIDGET(menu)); + + if (_toplevel) + { + gMainWindow *win = (gMainWindow *)pr; + + //set_gdk_fg_color(GTK_WIDGET(menu), win->foreground()); + //set_gdk_bg_color(GTK_WIDGET(menu), win->background()); + + //gtk_menu_shell_append(GTK_MENU_SHELL(win->menuBar), GTK_WIDGET(menu)); + shell = GTK_MENU_SHELL(win->menuBar); + } + else + { + gMenu *parent = parentMenu(); + + if (!parent->_popup) + { + parent->_popup = (GtkMenu *)gtk_menu_new(); + g_object_ref_sink(parent->_popup); + + //fprintf(stderr, "creates a new child menu container in parent %s\n", parent->name()); + + //g_signal_connect(G_OBJECT(parent->_popup), "size-allocate", G_CALLBACK(cb_size_allocate), (gpointer)parent); + g_signal_connect(G_OBJECT(parent->_popup), "show", G_CALLBACK(cb_show), (gpointer)parent); + g_signal_connect(G_OBJECT(parent->_popup), "map", G_CALLBACK(cb_map), (gpointer)parent); + g_signal_connect(G_OBJECT(parent->_popup), "unmap", G_CALLBACK(cb_unmap), (gpointer)parent); + gtk_widget_show_all(GTK_WIDGET(parent->_popup)); + + parent->update(); + + if (parent->style() == NORMAL) + gtk_menu_item_set_submenu(parent->menu, GTK_WIDGET(parent->_popup)); + + //parent->setColor(); + } + shell = GTK_MENU_SHELL(parent->_popup); + } + + if (shell) + { + patch_classes(); + + if (pos < 0) + { + gtk_menu_shell_append(shell, GTK_WIDGET(menu)); + //g_debug("%p: append to parent %p", this, shell); + } + else + { + gtk_menu_shell_insert(shell, GTK_WIDGET(menu), pos); + //g_debug("%p: insert into parent %p", this, shell); + } + } + + g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(cb_destroy), (gpointer)this); + g_signal_connect(G_OBJECT(menu), "activate", G_CALLBACK(cb_activate), (gpointer)this); + + g_object_set_data(G_OBJECT(menu), "gambas-menu", this); + } + + _oldstyle = _style; + updateVisible(); + } + + if (_style == NORMAL || _style == CHECK) + { + gMnemonic_correctText(_text, &buf); + gtk_label_set_text_with_mnemonic(GTK_LABEL(label), buf); + g_free(buf); + + if (!_toplevel) + { + if (_shortcut) + { + buf = g_strconcat("\t", _shortcut, " ",(void *)NULL); + gtk_label_set_text(GTK_LABEL(shlabel), buf); + g_free(buf); + } + else + gtk_label_set_text(GTK_LABEL(shlabel), "\t"); + + updatePicture(); + } + + //setColor(); + setFont(); + } + + //g_debug("%p: END UPDATE", this); +} + +void gMenu::updatePicture() +{ + int size; + gPicture *pic; + + if (!image || isTopLevel()) + return; + + if (!_picture) + { + gtk_image_set_from_pixbuf(GTK_IMAGE(image), NULL); + return; + } + + gtk_widget_get_size_request(image, NULL, &size); + size = size & ~3; + + pic = _picture->stretch(size, size, true); + if (_disabled) + pic->makeGray(); + + gtk_image_set_from_pixbuf(GTK_IMAGE(image), pic->getPixbuf()); + + delete pic; +} + +void gMenu::initialize() +{ + //fprintf(stderr, "gMenu::gMenu: %p (%p)\n", this, pr); + + hFree = NULL; + _popup = NULL; + image = NULL; + label = NULL; + shlabel = NULL; + //check = NULL; + menu = NULL; + _toplevel = false; + + _text = NULL; + _shortcut = NULL; + _shortcut_key = 0; + _shortcut_mods = (GdkModifierType)0; + _checked = false; + _picture = NULL; + _name = NULL; + _toggle = false; + _radio = false; + + _style = NOTHING; + _oldstyle = NOTHING; + + _ignore_destroy = false; + _ignore_activate = false; + _no_update = false; + _destroyed = false; + _delete_later = false; + _action = false; + _visible = false; + _opened = false; + _exec = false; + _disabled = false; + _mapping = false; + _proxy_for = false; + + _proxy = NULL; + + sizeGroup = NULL; + _children = NULL; + + menus = g_list_append (menus,(gpointer)this); +} + + +static gboolean cb_menubar_changed(GtkWidget *widget, gMainWindow *data) +{ + data->configure(); + return false; +} + +gMenu::gMenu(gMainWindow *par, bool hidden) +{ + pr = (gpointer)par; + + if (!par->menuBar) + { + par->menuBar = (GtkMenuBar*)gtk_menu_bar_new(); + g_signal_connect_after(G_OBJECT(par->menuBar), "map", G_CALLBACK(cb_menubar_changed), (gpointer)par); + g_signal_connect(G_OBJECT(par->menuBar),"unmap", G_CALLBACK(cb_menubar_changed), (gpointer)par); + par->embedMenuBar(par->border); + } + + initialize(); + _toplevel = true; + + accel = par->accel; + g_object_ref(accel); + + setText(NULL); + setVisible(!hidden); +} + +gMenu::gMenu(gMenu *par, bool hidden) +{ + pr = (gpointer)par; + + initialize(); + //sizeGroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + if (!par) return; + if (!par->menu) return; + + par->insert(this); + + accel = par->accel; + g_object_ref(accel); + + setText(NULL); + setVisible(!hidden); +} + +void gMenu::dispose() +{ + GList *item; + gMenu *mn; + int i; + + if (_destroyed) + return; + + #if DEBUG_DELETE + fprintf(stderr, "dispose: %s %p --> %p\n", name(), this, pr); + #endif + + setProxy(NULL); + ensureChildMenu(); + + if (_proxy_for) + { + #define CLEAR_POINTER(_field) if ((_field) == (void *)this) _field = NULL + + item = g_list_first(menus); + while (item) + { + mn = (gMenu*)item->data; + //CLEAR_POINTER(mn->pr); + CLEAR_POINTER(mn->_proxy); + item = g_list_next(item); + } + } + + if (_children) + { + for (i = 0; i < childCount(); i++) + child(i)->removeParent(); + g_ptr_array_unref(_children); + _children = NULL; + } + + //if (_style != NOTHING) + { + if (shlabel && (!_toplevel) && pr) + gtk_size_group_remove_widget(((gMenu*)pr)->sizeGroup, shlabel); + + if (sizeGroup) + g_object_unref(G_OBJECT(sizeGroup)); + + if (accel) + g_object_unref(accel); + } + + gMenu *parent = parentMenu(); + if (parent) + { + parent->remove(this); + parent = NULL; + } + + menus = g_list_remove(menus, (gpointer)this); + + #if DEBUG_DELETE + fprintf(stderr, "dispose: >>> %s\n", name()); + #endif +} + +gMenu::~gMenu() +{ + #if DEBUG_DELETE + fprintf(stderr, "~gMenu: %s %p --> %p\n", name(), this, pr); + #endif + + dispose(); + + _no_update = true; + + setText(NULL); + setPicture(NULL); + setShortcut(NULL); + + _style = NOTHING; + + if (_popup) + g_object_unref(_popup); + + if (_current_popup == this) + _current_popup = NULL; + + menus_destroyed = g_list_remove(menus_destroyed, this); + + #if DEBUG_DELETE + fprintf(stderr, "~gMenu: >>> %s\n", name()); + #endif + CB_menu_finish(this); +} + +void gMenu::setEnabled(bool vl) +{ + if (vl != _disabled) + return; + + _disabled = !vl; + gtk_widget_set_sensitive(GTK_WIDGET(menu), vl); + updateShortcutRecursive(); +} + +bool gMenu::isFullyEnabled() const +{ + const gMenu *menu = this; + + for(;;) + { + if (menu->_exec) + return true; + + if (!menu->isEnabled()) + return false; + + if (menu->isTopLevel()) + return true; + + menu = menu->parentMenu(); + } +} + +void gMenu::updateShortcut() +{ + if (_no_update) + return; + + if (isTopLevel()) + return; + + if (_shortcut_key) + { + //fprintf(stderr, "gtk_widget_remove_accelerator: (%s %p) accel = %p (%d,%d)\n", name(), menu, accel, _shortcut_key, _shortcut_mods); + gtk_widget_remove_accelerator(GTK_WIDGET(menu), accel, _shortcut_key, _shortcut_mods); + _shortcut_key = 0; + } + + if (isFullyEnabled() && _shortcut) + { + gt_shortcut_parse(_shortcut, &_shortcut_key, &_shortcut_mods); + if (_shortcut_key) + { + //fprintf(stderr, "gtk_widget_add_accelerator: (%s %p) accel = %p (%d,%d)\n", name(), menu, accel, _shortcut_key, _shortcut_mods); + gtk_widget_add_accelerator(GTK_WIDGET(menu), "activate", accel, _shortcut_key, _shortcut_mods, (GtkAccelFlags)0); + } + } +} + +void gMenu::updateShortcutRecursive() +{ + gMenu *ch; + int i; + + if (_exec) + return; + + updateShortcut(); + + for (i = 0;; i++) + { + ch = child(i); + if (!ch) + break; + ch->updateShortcutRecursive(); + } +} + +void gMenu::setText(const char *text) +{ + g_free(_text); + if (text) + _text = g_strdup(text); + else + _text = NULL; + + update(); +} + +bool gMenu::isVisible() +{ + if (!menu) return false; + return _visible; +} + +void gMenu::updateVisible() +{ + bool vl = _visible; + + if (_toplevel && _style != NORMAL) + vl = false; + + //fprintf(stderr, "gMenu::updateVisible: %s '%s' %d\n", name(), text(), vl); + + gtk_widget_set_visible(GTK_WIDGET(menu), vl); + //g_object_set(G_OBJECT(menu),"visible",vl,(void *)NULL); + + if (_toplevel && pr) + ((gMainWindow *)pr)->checkMenuBar(); +} + +void gMenu::setVisible(bool vl) +{ + if (!menu) return; + if (vl == _visible) return; + + _visible = vl; + updateVisible(); +} + +void gMenu::setPicture(gPicture *pic) +{ + //fprintf(stderr, "gMenu::setPicture: %p\n", pic); + gPicture::assign(&_picture, pic); + update(); +} + +void gMenu::setChecked(bool vl) +{ + if (vl == _checked || _popup) + return; + + _checked = vl; + if (_toggle || _radio) + { + _ignore_activate = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), vl); + } + else + update(); +} + +void gMenu::setToggle(bool vl) +{ + if (vl == _toggle) + return; + + _toggle = vl; + update(); +} + +void gMenu::setRadio(bool vl) +{ + if (vl == _radio) + return; + + _radio = vl; + update(); +} + +int gMenu::childCount() const +{ + if (!_children) + return 0; + else + return _children->len; +} + +gMenu *gMenu::child(int index) const +{ + if (!_children || index < 0 || index >= (int)_children->len) + return NULL; + else + return (gMenu *)g_ptr_array_index(_children, index); +} + +void gMenu::destroy() +{ + if (_destroyed) + return; + + hide(); + dispose(); + menus_destroyed = g_list_prepend(menus_destroyed, (gpointer)this); + _destroyed = true; +} + +#if GTK_CHECK_VERSION(3, 22, 0) +#else +static void position_menu(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, MenuPosition *pos) +{ + *x = pos->x; + *y = pos->y; + *push_in = true; +} +#endif + +void gMenu::doPopup(bool move, int x, int y) +{ + if (!_popup) + return; + + gMenu *save_current_popup = _current_popup; + GtkWidget *save_grab = gApplication::_popup_grab; + + _current_popup = this; + gApplication::_popup_grab = GTK_WIDGET(_popup); + + _in_popup++; + _popup_count++; + _exec = true; + +#if GTK_CHECK_VERSION(3, 22, 0) + + GdkWindow *win; + GdkRectangle rect; + GdkEvent *event; + GdkEvent *last_event; + + gt_disable_warnings(true); + + event = gdk_event_new(GDK_BUTTON_PRESS); + event->button.time = gApplication::lastEventTime(); //GDK_CURRENT_TIME; + + last_event = gApplication::lastEvent(); + if (last_event && last_event->type == GDK_BUTTON_PRESS) + { + event->button.button = last_event->button.button; + event->button.window = last_event->button.window; + } + else + { + event->button.button = 1; + event->button.window = gtk_widget_get_window(window()->border); + } + + gdk_event_set_device(event, gMouse::getPointer()); + + if (move) + { + win = gdk_event_get_window(event); + gdk_window_get_origin(win, &rect.x, &rect.y); + + rect.x = x - rect.x; + rect.y = y - rect.y; + rect.width = rect.height = 1; + + gtk_menu_popup_at_rect(_popup, win, &rect, GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST, event); + } + else + gtk_menu_popup_at_pointer(_popup, event); + + gt_disable_warnings(false); + + event->button.window = NULL; + gdk_event_free(event); + +#else + + MenuPosition *pos = NULL; + + if (move) + { + pos = new MenuPosition; + pos->x = x; + pos->y = y; + } + + gtk_menu_popup(_popup, NULL, NULL, move ? (GtkMenuPositionFunc)position_menu : NULL, (gpointer)pos, 0, gApplication::lastEventTime()); + +#endif + +#if GTK_CHECK_VERSION(2, 20, 0) + while (_current_popup && _popup && gtk_widget_get_mapped(GTK_WIDGET(_popup))) +#else + while (_current_popup && _popup && GTK_WIDGET_MAPPED(_popup)) +#endif + MAIN_do_iteration(false); + + _exec = false; + + _current_popup = save_current_popup; + gApplication::_popup_grab = save_grab; + + _in_popup--; + +#ifdef GTK3 +#else + if (pos) + delete pos; +#endif + + // flush the event loop so that the main window is reactivated before the click menu event is raised + + while (gApplication::eventsPending()) + MAIN_do_iteration(false); +} + +void gMenu::popup(int x, int y) +{ + doPopup(true, x, y); +} + +void gMenu::popup() +{ + doPopup(false); +} + +void gMenu::close() +{ + if (!_popup) + return; + + gtk_menu_popdown(_popup); +} + +int gMenu::winChildCount(gMainWindow *par) +{ + GList *item; + gMenu *mn; + int ct=0; + + if (!menus) return 0; + + item=g_list_first(menus); + while (item) + { + mn=(gMenu*)item->data; + if (mn->pr == (void*)par) ct++; + item=g_list_next(item); + } + + return ct; +} + +gMenu* gMenu::winChildMenu(gMainWindow *par,int pos) +{ + GList *item; + gMenu *mn; + int ct=0; + + if (!menus) return NULL; + + item=g_list_first(menus); + while (item) + { + mn=(gMenu*)item->data; + if (mn->pr == (void*)par) + { + if (ct==pos) return mn; + ct++; + } + item=g_list_next(item); + } + + return NULL; +} + +gMenu *gMenu::findFromName(gMainWindow *win, const char *name) +{ + int i; + int count; + gMenu *menu; + + for(;;) + { + count = winChildCount(win); + for (i = 0; i < count; i++) + { + menu = winChildMenu(win, i); + if (!strcasecmp(menu->name(), name)) + return menu; + } + + if (!win->parent()) + break; + win = win->parent()->window(); + if (!win) + break; + } + + return NULL; +} + +void gMenu::setShortcut(char *shortcut) +{ + if (_shortcut) + { + g_free(_shortcut); + _shortcut = NULL; + } + + if (shortcut) + _shortcut = g_strdup(shortcut); + + updateShortcut(); + update(); +} + +gMainWindow *gMenu::window() +{ + if (!pr) + return NULL; + + if (_toplevel) + return (gMainWindow *)pr; + + return ((gMenu *)pr)->window(); +} + +void gMenu::setName(char *name) +{ + if (_name) + { + g_free(_name); + _name = NULL; + } + + if (name) + _name = g_strdup(name); +} + +void gMenu::hideSeparators() +{ + gMenu *ch; + gMenu *last_ch; + bool is_sep; + bool last_sep; + //bool show_check = false; + bool show_image = false; + int i; + + if (!_popup) + return; + + last_sep = true; + last_ch = 0; + + for (i = 0; i < childCount(); i++) + { + ch = child(i); + + is_sep = ch->style() == SEPARATOR; + + if (is_sep) + { + if (last_sep) + { + ch->hide(); + } + else + { + ch->show(); + last_sep = true; + last_ch = ch; + } + } + else + { + if (ch->isVisible()) + { + ch->ensureChildMenu(); + last_sep = false; + /*if (ch->radio() || ch->toggle() || ch->checked()) + show_check = true;*/ + if (ch->picture()) + show_image = true; + } + } + } + + if (last_sep && last_ch) + last_ch->hide(); + + for (i = 0; i < childCount(); i++) + { + ch = child(i); + if (!ch->image || !ch->isVisible()) + continue; + + if (show_image) + gtk_widget_show(ch->image); + else + gtk_widget_hide(ch->image); + } +} + +void gMenu::setFont() +{ + gMainWindow *win = window(); +#ifdef GTK3 + if (label) gt_widget_update_css(GTK_WIDGET(label), win->font(), COLOR_DEFAULT, COLOR_DEFAULT); + if (shlabel) gt_widget_update_css(GTK_WIDGET(shlabel), win->font(), COLOR_DEFAULT, COLOR_DEFAULT); +#else + if (label) gtk_widget_modify_font(GTK_WIDGET(label), win->font()->desc()); + if (shlabel) gtk_widget_modify_font(GTK_WIDGET(shlabel), win->font()->desc()); +#endif +} + +/*void gMenu::setColor() +{ + gMainWindow *win = window(); + + if (pr == win) + { + if (label) set_gdk_fg_color(GTK_WIDGET(label), win->foreground()); + } + //if (shortcut) set_gdk_fg_color(GTK_WIDGET(shortcut), win->foreground()); +}*/ + +void gMenu::updateColor(gMainWindow *win) +{ + //GList *item; + //gMenu *mn; + + if (!win->menuBar) + return; + + #ifdef GTK3 + gt_widget_update_css(GTK_WIDGET(win->menuBar), NULL, win->background(), win->foreground()); + #else + set_gdk_bg_color(GTK_WIDGET(win->menuBar), win->background()); + set_gdk_fg_color(GTK_WIDGET(win->menuBar), win->foreground()); + #endif + + /*if (!menus) + return; + + item = g_list_first(menus); + while (item) + { + mn = (gMenu*)item->data; + //if (mn->pr == (void*)win) + mn->setColor(); + item = g_list_next(item); + }*/ +} + +void gMenu::updateFont(gMainWindow *win) +{ + GList *item; + gMenu *mn; + + if (win->menuBar) + { + //fprintf(stderr, "set menu bar font\n"); +#ifdef GTK3 + gt_widget_update_css(GTK_WIDGET(win->menuBar), win->ownFont() ? win->font() : NULL, COLOR_DEFAULT, COLOR_DEFAULT); +#else + gtk_widget_modify_font(GTK_WIDGET(win->menuBar), win->ownFont() ? win->font()->desc() : NULL); +#endif + } + + if (!menus) + return; + + item = g_list_first(menus); + while (item) + { + mn = (gMenu*)item->data; + if (mn->pr == (void*)win) + mn->setFont(); + item=g_list_next(item); + } +} + +void gMenu::updateRadio() +{ + gMenu *parent, *ch; + int i; + int start = -1; + + parent = parentMenu(); + if (!parent) + return; + + for (i = 0; i < parent->childCount(); i++) + { + ch = parent->child(i); + if (ch->radio()) + { + if (start < 0) + start = i; + if (ch == this) + break; + } + else + start = -1; + } + + if (start >= 0) + { + for (i = start; i < parent->childCount(); i++) + { + ch = parent->child(i); + if (!ch->radio()) + break; + + ch->setChecked(ch == this); + } + } +} + +bool gMenu::setProxy(gMenu *proxy) +{ + gMenu *check = proxy; + + while (check) + { + if (check == this) + return true; + + check = check->_proxy; + } + + _proxy = proxy; + if (proxy) + proxy->_proxy_for = true; + + return false; +} + +GtkMenu *gMenu::getSubMenu() +{ + if (_proxy) + return _proxy->getSubMenu(); + else + return _popup; +} + +void gMenu::ensureChildMenu() +{ + GtkMenu *sub_menu = getSubMenu(); + + // TODO: create parent menu? + if (sub_menu && gtk_menu_item_get_submenu(menu) != (GtkWidget *)sub_menu) + { + //fprintf(stderr, "ensureChildMenu: %p\n", sub_menu); + g_object_ref(sub_menu); + /*attach = gtk_menu_get_attach_widget(sub_menu); + if (attach) + gtk_menu_item_set_submenu(GTK_MENU_ITEM(attach), NULL);*/ + if (gtk_menu_get_attach_widget(sub_menu)) + gtk_menu_detach(sub_menu); + gtk_menu_item_set_submenu(menu, GTK_WIDGET(sub_menu)); + g_object_unref(sub_menu); + } +} + + +GtkSizeGroup *gMenu::getSizeGroup() +{ + if (!sizeGroup) + sizeGroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + return sizeGroup; +} + +void gMenu::insert(gMenu *child) +{ + if (!_children) + _children = g_ptr_array_new(); + + g_ptr_array_add(_children, child); +} + +void gMenu::remove(gMenu *child) +{ + g_ptr_array_remove(_children, child); +} + +void gMenu::willBeDeletedLater() +{ + //gMenu *parent = parentMenu(); + _delete_later = TRUE; + dispose(); +} + +void gMenu::removeParent() +{ + #if DEBUG_DELETE + fprintf(stderr, "removeParent: %s %p --> %p\n", name(), this, pr); + #endif + pr = NULL; +} + +void gMenu::updateChecked() +{ + if (_style == CHECK) + _checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)); + else + _checked = false; +} + +bool gMenu::ignoreDestroy() +{ + if (_ignore_destroy) + { + _ignore_destroy = false; + return true; + } + else + return false; +} + +bool gMenu::ignoreActivate() +{ + if (_ignore_activate) + { + _ignore_activate = false; + return true; + } + else + return false; +} + +void gMenu::cleanRemovedMenus() +{ + GList *iter; + gMenu *menu; + + if (!menus_destroyed) return; + + for(;;) + { + iter = g_list_first(menus_destroyed); + if (!iter) + break; + menu = (gMenu *)iter->data; + gtk_widget_destroy(GTK_WIDGET(menu->menu)); + } + + menus_destroyed = NULL; +} diff --git a/gb.gtk/src/gmenu.h b/gb.gtk/src/gmenu.h new file mode 100644 index 00000000..10eccc89 --- /dev/null +++ b/gb.gtk/src/gmenu.h @@ -0,0 +1,185 @@ +/*************************************************************************** + + gmenu.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMENU_H +#define __GMENU_H + +class gMainWindow; +class gPicture; + +class gMenu +{ +public: + gMenu(gMainWindow *par, bool hidden); + gMenu(gMenu *par, bool hidden); + ~gMenu(); + + void *hFree; + + static int winChildCount(gMainWindow *win); + static gMenu* winChildMenu(gMainWindow *par,int pos); + static void updateFont(gMainWindow *win); + static void updateColor(gMainWindow *win); + static gMenu *findFromName(gMainWindow *win, const char *name); + + static int popupCount() { return _popup_count; } + +//"Properties" + bool checked() const { return _checked; } + bool toggle() const { return _toggle; } + bool radio() const { return _radio; } + bool isEnabled() const { return !_disabled; } + bool isFullyEnabled() const; + gMenu *child(int index) const; + int childCount() const; + char* shortcut() const { return _shortcut; } + char* text() const { return _text; } + bool isVisible(); + gPicture* picture() const { return _picture; } + gMainWindow* window(); + char *name() const { return _name; } + bool isTopLevel() const { return _toplevel; } + bool isSeparator() const { return _style == SEPARATOR; } + void *parent() const { return pr; } + gMenu *parentMenu() const { return _toplevel ? NULL : (gMenu *)pr; } + bool isClosed() const { return !_opened; } + bool isDestroyed() const { return _destroyed; } + + void setChecked(bool vl); + void setToggle(bool vl); + void setRadio(bool vl); + void setEnabled(bool vl); + void setShortcut(char *txt); + void setText(const char *vl); + void setVisible(bool vl); + void show() { setVisible(true); } + void hide() { setVisible(false); } + void setPicture(gPicture *pic); + void setName(char *name); + bool action() const { return _action; } + void setAction(bool v) { _action = v; } + void setFont(); + //void setColor(); + + bool setProxy(gMenu *menu); + gMenu *proxy() const { return _proxy; } + +//"Methods" + void popup(); + void popup(int x, int y); + void close(); + void destroy(); + static bool insidePopup() { return _in_popup > 0; } + static gMenu *currentPopup() { return _current_popup; } + +//"Private" + enum gMenuStyle { NOTHING, SEPARATOR, CHECK, NORMAL }; + + void *pr; + + GtkMenuItem *menu; + GtkWidget *hbox; + //GtkWidget *check; + GtkWidget *image; + GtkWidget *label; + GtkWidget *shlabel; + + GtkMenu *_popup; + + GtkSizeGroup *sizeGroup; + GtkAccelGroup *accel; + + gMenu *_proxy; + unsigned _opened : 1; + unsigned _exec: 1; + unsigned _disabled : 1; + unsigned _mapping : 1; + unsigned _proxy_for : 1; + unsigned _ignore_destroy : 1; + unsigned _ignore_activate : 1; + + void initialize(); + gMenuStyle style() const { return _style; } + void hideSeparators(); + void willBeDeletedLater(); + void destroyNow() { _delete_later = false; destroy(); } + void updateRadio(); + void updateChecked(); + void updatePicture(); + GtkMenu *getSubMenu(); + void ensureChildMenu(); + void updateShortcutRecursive(); + GtkSizeGroup *getSizeGroup(); + + void insert(gMenu *child); + void remove(gMenu *child); + void removeParent(); + + bool ignoreDestroy(); + bool ignoreActivate(); + + static void cleanRemovedMenus(); + +private: + + gMenuStyle _style, _oldstyle; + char *_name; + gPicture *_picture; + char *_text; + + char *_shortcut; + guint _shortcut_key; + GdkModifierType _shortcut_mods; + + GPtrArray *_children; + + unsigned _checked : 1; + unsigned _toggle : 1; + unsigned _radio : 1; + unsigned _no_update : 1; + unsigned _destroyed : 1; + unsigned _delete_later : 1; + unsigned _toplevel : 1; + unsigned _action : 1; + unsigned _visible : 1; + + static gMenu *_current_popup; + static int _in_popup; + static int _popup_count; + + void doPopup(bool move, int x = 0, int y = 0); + void update(); + void updateVisible(); + void updateShortcut(); + void dispose(); +}; + +// Callbacks +void CB_menu_finish(gMenu *sender); +void CB_menu_click(gMenu *sender); +void CB_menu_show(gMenu *sender); +void CB_menu_hide(gMenu *sender); + + + +#endif diff --git a/gb.gtk/src/gmouse.cpp b/gb.gtk/src/gmouse.cpp new file mode 100644 index 00000000..559d369f --- /dev/null +++ b/gb.gtk/src/gmouse.cpp @@ -0,0 +1,387 @@ +/*************************************************************************** + + gmouse.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GMOUSE_CPP + +#include "widgets.h" +#include "gapplication.h" +#include "gmouse.h" + +int gMouse::_isValid = 0; +int gMouse::_x; +int gMouse::_y; +int gMouse::_screen_x; +int gMouse::_screen_y; +int gMouse::_button; +int gMouse::_state; +int gMouse::_delta; +int gMouse::_orientation; +int gMouse::_start_x; +int gMouse::_start_y; +int gMouse::_dx = 0; +int gMouse::_dy = 0; +GdkEvent *gMouse::_event = 0; +int gMouse::_click_count = 0; +int gMouse::_click_x = -1; +int gMouse::_click_y = -1; +double gMouse::_click_timer = 0; +gControl *gMouse::_control = NULL; + +#ifdef GTK3 +GdkDevice *gMouse::getPointer() +{ +#if GTK_CHECK_VERSION(3, 22, 0) + return gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display_get_default())); +#else + return gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); +#endif +} +#endif + +void gMouse::move(int x, int y) +{ + GdkDisplay* dpy = gdk_display_get_default(); +#ifdef GTK3 + gdk_device_warp(getPointer(), gdk_display_get_default_screen(dpy), x, y); +#else + gdk_display_warp_pointer(dpy, gdk_display_get_default_screen(dpy), x, y); +#endif +} + +int gMouse::button() +{ + int button = 0; + + if (_isValid) + { + button = _button; + if (_button >= 4) + button -= 4; + } + + return button; +} + +int gMouse::state() +{ + int state = 0; + + if (_isValid) + { + if ((_state & GDK_BUTTON1_MASK) || _button == 1) state |= MOUSE_LEFT; + if ((_state & GDK_BUTTON2_MASK) || _button == 2) state |= MOUSE_MIDDLE; + if ((_state & GDK_BUTTON3_MASK) || _button == 3) state |= MOUSE_RIGHT; + if ((_state & GDK_BUTTON4_MASK) || _button == 8) state |= MOUSE_BUTTON4; + if ((_state & GDK_BUTTON5_MASK) || _button == 9) state |= MOUSE_BUTTON5; + if (_state & GDK_SHIFT_MASK) state |= MOUSE_SHIFT; + if (_state & GDK_CONTROL_MASK) state |= MOUSE_CTRL; + if (_state & GDK_MOD1_MASK) state |= MOUSE_ALT; + if (_state & GDK_MOD2_MASK) state |= MOUSE_META; + } + + return state; +} + +bool gMouse::left() +{ + return _isValid ? (_state & GDK_BUTTON1_MASK || _button == 1) : false; +} + +bool gMouse::right() +{ + return _isValid ? (_state & GDK_BUTTON3_MASK || _button == 3) : false; +} + +bool gMouse::middle() +{ + return _isValid ? (_state & GDK_BUTTON2_MASK || _button == 2) : false; +} + +bool gMouse::shift() +{ + return _isValid ? (_state & GDK_SHIFT_MASK) : false; +} + +bool gMouse::control() +{ + return _isValid ? (_state & GDK_CONTROL_MASK) : false; +} + +bool gMouse::alt() +{ + return _isValid ? (_state & GDK_MOD1_MASK) : false; +} + +bool gMouse::meta() +{ + return _isValid ? (_state & GDK_MOD2_MASK) : false; +} + +bool gMouse::normal() +{ + return _isValid ? (_state & 0xFF) : false; +} + +int gMouse::x() +{ + return _isValid ? _x + _dx : -1; +} + +int gMouse::y() +{ + return _isValid ? _y + _dy : -1; +} + +void gMouse::getScreenPos(int *x, int *y) +{ + if (_isValid) + { + *x = _screen_x; + *y = _screen_y; + } + else + { +#ifdef GTK3 + gdk_device_get_position(getPointer(), NULL, x, y); +#else + gdk_display_get_pointer(gdk_display_get_default(), NULL, x, y, NULL); +#endif + } +} + +int gMouse::screenX() +{ + gint x; + +#ifdef GTK3 + gdk_device_get_position(getPointer(), NULL, &x, NULL); +#else + gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, NULL, NULL); +#endif + + return x; +} + +int gMouse::screenY() +{ + gint y; + +#ifdef GTK3 + gdk_device_get_position(getPointer(), NULL, NULL, &y); +#else + gdk_display_get_pointer(gdk_display_get_default(), NULL, NULL, &y, NULL); +#endif + + return y; +} + +int gMouse::delta() +{ + return _isValid ? _delta : -1; +} + +int gMouse::orientation() +{ + return _isValid ? _orientation : -1; +} + +//"Private" + +void gMouse::setWheel(int dt,int orn) +{ + _delta = dt; + _orientation = orn; +} + +void gMouse::setStart(int sx, int sy) +{ + _start_x = sx; + _start_y = sy; +} + +void gMouse::setMouse(int x, int y, int sx, int sy, int button, int state) +{ + _delta = 0; + _orientation = 0; + + _x = x; + _y = y; + _state = state; + _button = button; + _screen_x = sx; + _screen_y = sy; +} + +static GdkDevice *get_event_device(GdkEvent *event) +{ + switch(event->type) + { + case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: case GDK_BUTTON_RELEASE: + return ((GdkEventButton *)event)->device; + + case GDK_SCROLL: + return ((GdkEventScroll *)event)->device; + + case GDK_MOTION_NOTIFY: + return ((GdkEventMotion *)event)->device; + + case GDK_PROXIMITY_IN: case GDK_PROXIMITY_OUT: + return ((GdkEventProximity *)event)->device; + + default: + return NULL; + } +} + +void gMouse::setEvent(GdkEvent *event) +{ + _event = gdk_event_copy(event); + //fprintf(stderr, "device = %p\n", get_event_device(event)); +} + +double gMouse::getAxis(GdkAxisUse axis) +{ + double value; + + if (gdk_event_get_axis(_event, axis, &value)) + return value; + else + return 0.0; +} + + +int gMouse::getType() +{ + GdkDevice *device = get_event_device(_event); + + if (!device) + return POINTER_MOUSE; + + switch(gdk_device_get_source(device)) + { + case GDK_SOURCE_PEN: return POINTER_PEN; + case GDK_SOURCE_ERASER: return POINTER_ERASER; + case GDK_SOURCE_CURSOR: return POINTER_CURSOR; + default: return POINTER_MOUSE; + } +} + +void gMouse::initDevices() +{ +#ifndef GTK3 + static bool done = false; + + GList *devices; + GdkDevice *device; + + if (done) + return; + + //fprintf(stderr, "initDevices\n"); + + devices = gdk_devices_list(); + + while (devices) + { + device = (GdkDevice *)devices->data; + + if (gdk_device_get_source(device) != GDK_SOURCE_MOUSE) + { + //fprintf(stderr, "enable device '%s'\n", gdk_device_get_name(device)); + gdk_device_set_mode(device, GDK_MODE_SCREEN); + } + + devices = devices->next; + } + + done = true; +#endif +} + +double gMouse::getPointerX() +{ + return ((GdkEventMotion *)_event)->x + _dx; +} + +double gMouse::getPointerY() +{ + return ((GdkEventMotion *)_event)->y + _dy; +} + +double gMouse::getPointerScreenX() +{ + return getAxis(GDK_AXIS_X); +} + +double gMouse::getPointerScreenY() +{ + return getAxis(GDK_AXIS_Y); +} + +void gMouse::invalidate() +{ + _isValid--; + + if (_isValid == 0) + { + if (_event) + { + gdk_event_free(_event); + _event = 0; + } + } +} + +void gMouse::translate(int dx, int dy) +{ + _dx = dx; + _dy = dy; +} + + +void gMouse::handleClickCount(GdkEvent *event) +{ + double timer; + + GB.GetTime(&timer, TRUE); + if (abs((int)event->button.x_root - _click_x) < 4 && abs((int)event->button.y_root - _click_y) < 4 && ((timer - _click_timer) * 1000) < gApplication::dblClickTime()) + _click_count++; + else + { + _click_x = (int)event->button.x_root; + _click_y = (int)event->button.y_root; + _click_count = 1; + } + + _click_timer = timer; +} + +void gMouse::finishEvent() +{ + if (_control) + { + CB_control_mouse(_control, gEvent_MouseRelease); + _control = NULL; + } +} diff --git a/gb.gtk/src/gmouse.h b/gb.gtk/src/gmouse.h new file mode 100644 index 00000000..81a4c889 --- /dev/null +++ b/gb.gtk/src/gmouse.h @@ -0,0 +1,109 @@ +/*************************************************************************** + + gmouse.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMOUSE_H +#define __GMOUSE_H + +class gMouse +{ +public: + +//"Properties" + static int button(); + static int state(); + static bool left(); + static bool right(); + static bool middle(); + static bool shift(); + static bool control(); + static bool alt(); + static bool meta(); + static bool normal(); + static int x(); + static int y(); + static int screenX(); + static int screenY(); + static void getScreenPos(int *x, int *y); + static int delta(); + static int orientation(); + static bool isValid() { return _isValid; } + static int startX() { return _start_x + _dx; } + static int startY() { return _start_y + _dy; } + + static double getAxis(GdkAxisUse axis); + static int getType(); + static double getPointerX(); + static double getPointerY(); + static double getPointerScreenX(); + static double getPointerScreenY(); + + static void initDevices(); + +//"Methods" + static void move(int x, int y); + static void translate(int dx, int dy); + static void resetTranslate() { translate(0, 0); } + +//"Private" + static void setWheel(int dt, int orn); + static void setStart(int sx, int sy); + static void setMouse(int x, int y, int sx, int sy, int button, int state); + static void setEvent(GdkEvent *event); + static void validate() { _isValid++; } + static void invalidate(); + + static void handleClickCount(GdkEvent *event); + static int clickCount() { return _isValid ? _click_count : 0; } + + static void setControl(gControl *control) { _control = control; } + static gControl *getControl() { return _control; } + static void finishEvent(); + +#ifdef GTK3 + static GdkDevice *getPointer(); +#endif + + static gControl *_control; + +private: + static int _isValid; + static int _x; + static int _y; + static int _screen_x; + static int _screen_y; + static int _button; + static int _state; + static int _delta; + static int _orientation; + static int _start_x; + static int _start_y; + static int _dx; + static int _dy; + static GdkEvent *_event; + static int _click_count; + static int _click_x; + static int _click_y; + static double _click_timer; +}; + +#endif diff --git a/gb.gtk/src/gpanel.cpp b/gb.gtk/src/gpanel.cpp new file mode 100644 index 00000000..ac3e75a8 --- /dev/null +++ b/gb.gtk/src/gpanel.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + + gpanel.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gpanel.h" + +/**************************************************************************** + +Panel + +****************************************************************************/ + +void gPanel::create(void) +{ + int i; + GtkWidget *ch, *box; + bool doReparent = false; + bool was_visible = isVisible(); + GdkRectangle rect = { 0 }; + int bg, fg; + gControl *nextSibling; + + if (border) + { + getGeometry(&rect); + bg = background(); + fg = foreground(); + nextSibling = next(); + parent()->remove(this); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + g_object_ref(G_OBJECT(ch)); + gtk_container_remove(GTK_CONTAINER(widget), ch); + } + + doReparent = true; + } + + if (_bg_set) + { + createBorder(gtk_event_box_new()); + widget = gtk_fixed_new(); + box = widget; + //gtk_widget_set_app_paintable(border, TRUE); + //gtk_widget_set_app_paintable(box, TRUE); + } + else + { + createBorder(gtk_fixed_new()); + widget = border; + box = NULL; + } + + frame = border; + realize(true); + + //g_signal_connect(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_size), (gpointer)this); + //g_signal_connect(G_OBJECT(border), "expose-event", G_CALLBACK(cb_expose), (gpointer)this); + + if (doReparent) + { + if (isPaint()) + setPaint(); + + if (box) + gtk_widget_realize(box); + + setNext(nextSibling); + setBackground(bg); + setForeground(fg); + updateFont(); + bufX = bufY = bufW = bufH = -1; + setGeometry(&rect); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + gtk_container_add(GTK_CONTAINER(widget), ch); + moveChild(child(i), child(i)->x(), child(i)->y()); + g_object_unref(G_OBJECT(ch)); + } + + if (was_visible) + show(); + else + hide(); + + //gApplication::checkHoveredControl(this); + + if (_inside) + { + _inside = false; + if (gApplication::_enter == this) + gApplication::_enter = NULL; + gApplication::_ignore_until_next_enter = this; + } + } +} + +gPanel::gPanel(gContainer *parent) : gContainer(parent) +{ + border = NULL; +#ifdef GTK3 + _no_style_without_child = true; +#endif + create(); +} + +#ifdef GTK3 +#else +void gPanel::setBackground(gColor color) +{ + bool set = _bg_set; + + gContainer::setBackground(color); + + if (set != _bg_set) + create(); +} +#endif diff --git a/gb.gtk/src/gpanel.h b/gb.gtk/src/gpanel.h new file mode 100644 index 00000000..d7f4a69e --- /dev/null +++ b/gb.gtk/src/gpanel.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + gpanel.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GPANEL_H +#define __GPANEL_H + +#include "gcontainer.h" +#include "gcolor.h" + +class gPanel : public gContainer +{ +public: + gPanel(gContainer *parent); + + int getBorder() { return getFrameBorder(); } + void setBorder(int vl) { setFrameBorder(vl); } +#ifndef GTK3 + virtual void setBackground(gColor color = COLOR_DEFAULT); +#endif + +private: + + void create(); +}; + +#endif diff --git a/gb.gtk/src/gpicture.cpp b/gb.gtk/src/gpicture.cpp new file mode 100644 index 00000000..42a49142 --- /dev/null +++ b/gb.gtk/src/gpicture.cpp @@ -0,0 +1,1223 @@ +/*************************************************************************** + + gpicture.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include <math.h> +#include "gpicture.h" + + +/***************************************************************** + +gPicture + +******************************************************************/ + +#define LOAD_INC 65536L + +static bool pixbufFromMemory(GdkPixbuf **ppixbuf, char *addr, unsigned int len, bool *trans) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + GError *error = NULL; + gsize size; + + *ppixbuf = 0; + + loader = gdk_pixbuf_loader_new(); + + while (len > 0) + { + size = len > LOAD_INC ? LOAD_INC : len; + if (!gdk_pixbuf_loader_write(loader,(guchar*)addr,size, &error)) + { + //g_debug("ERROR: %s", error->message); + goto __ERROR; + } + addr += size; + len -= size; + } + + if (!gdk_pixbuf_loader_close(loader, &error)) + { + //g_debug("ERROR: %s", error->message); + goto __ERROR; + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); + g_object_ref(pixbuf); + + if (gdk_pixbuf_get_n_channels(pixbuf) == 3) + { + // Rowstride breaks gb.image (it is rounded up so that a line is always a four bytes multiple). + GdkPixbuf *aimg; + aimg = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); + g_object_unref(pixbuf); + pixbuf = aimg; + *trans = false; + } + else + *trans = true; + + g_object_unref(G_OBJECT(loader)); + + *ppixbuf = pixbuf; + return true; + +__ERROR: + g_object_unref(G_OBJECT(loader)); + return false; +} + + +void gPicture::initialize() +{ +#ifndef GTK3 + pixmap = NULL; + mask = NULL; +#endif + pixbuf = NULL; + surface = NULL; + _transparent = false; + _type = VOID; + _width = 0; + _height = 0; +} + +gPicture::gPicture() : gShare() +{ + //fprintf(stderr, "gPicture(): %p\n", this); + initialize(); +} + +static cairo_surface_t *create_surface(int w, int h) +{ + return cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); +} + +#ifndef GTK3 +static GdkPixmap *create_pixmap(int w, int h) +{ + GdkScreen *scr; + gint depth; + GdkPixmap *pixmap; + + scr = gdk_screen_get_default(); + depth = (gdk_screen_get_system_visual(scr))->depth; + + pixmap = gdk_pixmap_new(NULL, w, h, depth); + gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), gdk_colormap_get_system()); + return pixmap; +} +#endif + +#ifndef GTK3 +void gPicture::createMask(bool opaque) +{ + GdkGC *gc; + GdkGCValues gc_values; + + if (mask || !_transparent) + return; + + mask = gdk_pixmap_new(NULL, _width, _height, 1); + + gc_values.foreground.pixel = opaque ? 1 : 0; + gc = gdk_gc_new_with_values(mask, &gc_values, GDK_GC_FOREGROUND); + + gdk_gc_set_fill(gc, GDK_SOLID); + gdk_draw_rectangle(mask, gc, TRUE, 0, 0, _width, _height); + + g_object_unref(gc); +} +#endif + +gPicture::gPicture(gPictureType type, int w, int h, bool trans) : gShare() +{ + //fprintf(stderr, "gPicture(): %p: %d %d %d %d\n", this, type, w, h, trans); + initialize(); + + _transparent = trans; + + if (!type) + return; + + if (w <= 0 || h <= 0) return; + + _type = type; + _width = w; + _height = h; + + if (_type == SURFACE) + { + surface = create_surface(w, h); + } +#ifndef GTK3 + if (_type == PIXMAP) + { + pixmap = create_pixmap(w, h); + createMask(false); + } + else +#endif + if (_type == PIXBUF) + { + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h); + } +} + + +// The gPicture takes the GdkPixbuf object. Do not unreference it after. + +gPicture::gPicture(GdkPixbuf *image, bool trans) : gShare() +{ + //fprintf(stderr, "gPicture(image): %p: %p %d\n", this, image, trans); + initialize(); + if (!image) + return; + + _type = PIXBUF; + _width = gdk_pixbuf_get_width(image); + _height = gdk_pixbuf_get_height(image); + _transparent = trans; + pixbuf = image; + + if (gdk_pixbuf_get_n_channels(pixbuf) == 3) + { + GdkPixbuf *aimg; + aimg = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); + g_object_unref(G_OBJECT(pixbuf)); + //g_object_ref(G_OBJECT(aimg)); + pixbuf = aimg; + _transparent = false; + } + +} + +gPicture::gPicture(cairo_surface_t *surf) : gShare() +{ + initialize(); + if (!surf) + return; + _type = SURFACE; + surface = surf; + _width = cairo_image_surface_get_width(surf); + _height = cairo_image_surface_get_height(surf); +} + +#ifndef GTK3 + +// The gPicture takes the GdkPixmap object. Do not unreference it after. + +gPicture::gPicture(GdkPixmap *pix) : gShare() +{ + initialize(); + if (!pix) + return; + + _type = PIXMAP; + gdk_drawable_get_size((GdkDrawable *)pix, &_width, &_height); + pixmap = pix; +} +#endif + +gPicture::~gPicture() +{ + //fprintf(stderr, "~gPicture: %p\n", this); + clear(); +} + +void gPicture::invalidate() +{ +#ifndef GTK3 + if (pixmap && _type != PIXMAP) + { + g_object_unref(G_OBJECT(pixmap)); + pixmap = NULL; + if (mask) + { + g_object_unref(mask); + mask = NULL; + } + } +#endif + + if (pixbuf && _type != PIXBUF) + { + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = NULL; + } + + if (surface && _type != SURFACE) + { + cairo_surface_destroy(surface); + surface = NULL; + } +} + +GdkPixbuf *gPicture::getPixbuf() +{ + if (_type == VOID) + return NULL; + + if (pixbuf) + return pixbuf; + +#ifndef GTK3 + if (_type == PIXMAP) + { + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width(), height()); + gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, width(), height()); + + if (mask) + { + uchar *s, *d; + int i; + GdkPixbuf *alpha; + + alpha = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width(), height()); + gdk_pixbuf_get_from_drawable(alpha, mask, NULL, 0, 0, 0, 0, width(), height()); + s = gdk_pixbuf_get_pixels(alpha); + d = gdk_pixbuf_get_pixels(pixbuf) + 3; + //fprintf(stderr, "mask -> pixbuf\n\n"); + for (i = 0; i < (_width * _height); i++) + { + //fprintf(stderr, "%08X%c", *((uint *)s), (1 + (i % _width)) == _width ? '\n' : ' '); + d[0] = s[0]; + d += 4; + s += 4; + } + g_object_unref(alpha); + } + } + else +#endif + if (_type == SURFACE) + { +#ifdef GTK3 + pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, _width, _height); +#else + fprintf(stderr, "gb.gtk: warning: cairo surface to pixbuf conversion not implemented.\n"); + return NULL; +#endif + } + + _type = PIXBUF; + return pixbuf; +} + +#ifdef GTK3 +cairo_surface_t *gPicture::getSurface() +{ + if (_type == VOID) + return NULL; + + if (_type != SURFACE) + { + getPixbuf(); + surface = gt_cairo_create_surface_from_pixbuf(pixbuf); + } + + _type = SURFACE; + return surface; +} +#else +cairo_surface_t *gPicture::getSurface() +{ + if (_type == VOID) + return NULL; + + if (surface) + return surface; + + getPixbuf(); + surface = gt_cairo_create_surface_from_pixbuf(pixbuf); + + return surface; +} +#endif + +#ifndef GTK3 +GdkPixmap *gPicture::getPixmap() +{ + if (_type == VOID) + return NULL; + + if (_type != PIXMAP) + { + if (_type != PIXBUF) + getPixbuf(); + + if (pixmap) + g_object_unref(G_OBJECT(pixmap)); + if (mask) + g_object_unref(G_OBJECT(mask)); + + gt_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap, &mask, 128); + } + + _type = PIXMAP; + return pixmap; +} + +GdkBitmap *gPicture::getMask() +{ + getPixmap(); + return mask; +} +#endif + + +gPicture *gPicture::fromMemory(char *addr, unsigned int len) +{ + GdkPixbuf *pixbuf; + bool trans; + + if (!pixbufFromMemory(&pixbuf, addr, len, &trans)) + return 0; + else + { + gPicture *pic = new gPicture(pixbuf); + return pic; + } +} + +gPicture *gPicture::fromData(const char *data, int width, int height) +{ + GdkPixbuf *pixbuf; + + if (width <= 0 || height <= 0) + return new gPicture(); + else + { + pixbuf = gdk_pixbuf_new_from_data((const guchar *)data, GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * sizeof(int), NULL, NULL); + return new gPicture(pixbuf); + } +} + +int gPicture::depth() +{ + int depth = 0; + +#ifndef GTK3 + if (pixmap) + depth = gdk_drawable_get_depth(GDK_DRAWABLE(pixmap)); + else +#endif + if (pixbuf || surface) + depth = 32; + + return depth; +} + +void gPicture::setTransparent(bool vl) +{ + if (vl == _transparent) + return; + + _transparent = vl; + +#ifndef GTK3 + if (_type == PIXMAP) + { + if (_transparent) + createMask(true); + else + { + if (mask) + { + g_object_unref(G_OBJECT(mask)); + mask = 0; + } + } + } +#endif +} + +void gPicture::fill(gColor col) +{ +#ifndef GTK3 + if (_type == PIXMAP) + { + gt_pixmap_fill(pixmap, col, NULL); + } + else +#endif + if (_type == PIXBUF) + { + int r, g, b, a; + union + { + char c[4]; + uint value; + } + color; + + gt_color_to_rgba(col, &r, &g, &b, &a); + + color.c[0] = a ^ 0xFF; + color.c[1] = b; + color.c[2] = g; + color.c[3] = r; + + gdk_pixbuf_fill(pixbuf, color.value); + } +#ifdef GTK3 + else if (_type == SURFACE) + { + cairo_t *cr = cairo_create(surface); + gt_cairo_set_source_color(cr, col); + cairo_paint(cr); + cairo_destroy(cr); + } +#endif + + invalidate(); +} + + +// returns -> 0, OK / -1, Bad format / -2 invalid path + +int gPicture::save(const char *path, int quality) +{ + bool ok=false; + int b; + char *type; + const char *buf=NULL; + GSList *formats = gdk_pixbuf_get_formats(); + GSList *iter=formats; + GdkPixbuf *image = getPixbuf(); + char arg[16]; + + for (b=strlen(path)-1;b>=0;b--) + if (path[b]=='.') { buf=path+b+1; break; } + + if (!buf) return -1; + + while (iter && (!ok) ) + { + if (gdk_pixbuf_format_is_writable ((GdkPixbufFormat*)iter->data)) + { + type=gdk_pixbuf_format_get_name((GdkPixbufFormat*)iter->data); + if (!strcasecmp(type,buf)) + { + ok=true; + break; + } + else + g_free(type); + } + iter=iter->next; + } + + if (!ok) + { + g_slist_free(formats); + if (!strcasecmp("jpg", buf)) + type = (char *)"jpeg"; + else + return -1; + } + + if (quality >= 0) + { + sprintf(arg, "%d", quality); + b = gdk_pixbuf_save(image, path, type, NULL, "quality", arg, (void *)NULL); + } + else + b = gdk_pixbuf_save(image, path, type, NULL, (void *)NULL); + + + if (ok) { + g_free(type); + g_slist_free(formats); + } + + if (!b) return -2; + return 0; +} + + +/*********************************************************************** +The following function tries to load an icon from predefined or "stock" +items. It accepts the format: StockSize/IconName, where StockSize can be: + +"Menu", "SmallToolBar","LargeToolBar","Button","Dnd","Dialog" + +And IconName can be: + +"Add","Apply","Bold","Cancel", +"CDRom","Clear","Close","ColorPicker", +"Convert","Copy","Cut","Delete", +"DialogAuthentication""DialogError","DialogInfo","DialogQuestion", +"DialogWarning","Dnd","DndMultiple", "Execute", +"Find","FindAndReplace","Floppy","GotoBottom", +"GotoFirst", "GotoLast","GotoTop","GoBack", +"GoDown","GoForward","GoUp","HardDisk" +"Help","Home","Indent","Index", +"Italic","JumpTo","JustifyCenter", "JustifyFill", +"JustifyLeft","JustifyRight","MissingImage","Network", +"New","No","Ok","Open", +"Paste","Preferences","Print","PrintPreview", +"Properties","Quit","Redo","Refresh", +"Remove","RevertToSaved","Save","SaveAs", +"SelectColor","SelectFont","SortAscending","SortDescending", +"SpellCheck","Stop","StrikeThrough","Undelete", +"Underline","Undo","Unindent","Yes", +"Zoom100","ZoomFit","ZoomIn","ZoomOut" +*************************************************************************/ + + + +/*********************************************************************** +The following function tries to load an icon from predefined system +paths +***********************************************************************/ +gPicture* gPicture::fromNamedIcon(const char *name, int len) +{ + GtkIconTheme* theme; + GdkPixbuf *buf; + gPicture *pic = NULL; + int r_type=32; + char *c_name, *r_name; + + if (len < 0) len = strlen(name); + + c_name = g_strndup(name, len); + r_name = strchr(c_name, '/'); + + if (!r_name) + r_name = c_name; + else + { + r_name[0] = 0; r_name++; + if (!strcasecmp(c_name,"menu")) r_type=8; + else if (!strcasecmp(c_name,"smalltoolbar")) r_type=16; + else if (!strcasecmp(c_name,"largetoolbar")) r_type=32; + else if (!strcasecmp(c_name,"button")) r_type=16; + else if (!strcasecmp(c_name,"dnd")) r_type=32; + else if (!strcasecmp(c_name,"dialog")) r_type=48; + else { r_name--; r_name[0]='/'; g_free(c_name); return NULL; } + } + + + theme=gtk_icon_theme_get_default(); + buf=gtk_icon_theme_load_icon(theme,r_name,r_type,GTK_ICON_LOOKUP_USE_BUILTIN,NULL); + g_free(c_name); + if (!buf) return NULL; + + pic = gPicture::fromPixbuf(buf); + g_object_unref(buf); + + return pic; +} + +void gPicture::clear() +{ + //fprintf(stderr, "gPicture::clear: %p (%d %d) pixmap = %p pixbuf = %p\n", this, _width, _height, pixmap, pixbuf); + + _width = 0; + _height = 0; + _type = VOID; + +#ifndef GTK3 + if (pixmap) + g_object_unref(G_OBJECT(pixmap)); + if (mask) + g_object_unref(G_OBJECT(mask)); + + pixmap = NULL; + mask = NULL; +#endif + + if (pixbuf) + g_object_unref(G_OBJECT(pixbuf)); + if (surface) + cairo_surface_destroy(surface); + + pixbuf = NULL; + surface = NULL; +} + +void gPicture::resize(int w, int h) +{ + if (_width <= 0 || _height <= 0) + { + clear(); + return; + } + +#ifndef GTK3 + if (_type == PIXMAP) + { + GdkPixmap *buf; + GdkBitmap *save; + GdkGC *gc; + + buf = create_pixmap(w, h); + + gc=gdk_gc_new(buf); + gdk_draw_drawable(buf, gc, pixmap, 0, 0, 0, 0, w, h); + g_object_unref(gc); + + g_object_unref(G_OBJECT(pixmap)); + pixmap = buf; + + if (_transparent) + { + save = mask; + + mask = gdk_pixmap_new(NULL, w, h, 1); + + gc=gdk_gc_new(mask); + gdk_draw_drawable(mask, gc, save, 0, 0, 0, 0, w, h); + g_object_unref(gc); + + g_object_unref(save); + } + } + else +#endif + if (_type == PIXBUF) + { + GdkPixbuf *buf; + + if (w > width() || h > height()) + { + buf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h); + if (w > width()) w = width(); + if (h > height()) h = height(); + gdk_pixbuf_copy_area(pixbuf, 0, 0, w, h, buf, 0, 0); + } + else + { + buf = gdk_pixbuf_new_subpixbuf(pixbuf, 0, 0, w, h); + } + + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = buf; + } +#ifdef GTK3 + else if (_type == SURFACE) + { + cairo_surface_t *buf = create_surface(w, h); + cairo_t *cr = cairo_create(buf); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + cairo_destroy(cr); + } +#endif + + _width = w; + _height = h; + + invalidate(); +} + + +gPicture *gPicture::copy(int x, int y, int w, int h) +{ + gPicture *ret = NULL; + + if (_type == VOID || w <= 0 || h <= 0) + return new gPicture(); + +#ifndef GTK3 + if (_type == PIXMAP) + { + GdkGC *gc; + + ret = new gPicture(_type, w, h, _transparent); + + gc=gdk_gc_new(ret->pixmap); + gdk_draw_drawable(ret->pixmap, gc, pixmap, x, y, 0, 0, w, h); + g_object_unref(gc); + + if (ret->mask) + { + gc=gdk_gc_new(ret->mask); + gdk_draw_drawable(ret->mask, gc, mask, x, y, 0, 0, w, h); + g_object_unref(gc); + } + } + else +#endif + if (_type == PIXBUF) + { + GdkPixbuf *buf; + if (x == 0 && y == 0 && w == width() && h == height()) + buf = gdk_pixbuf_copy(pixbuf); + else + { + buf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h); + gdk_pixbuf_copy_area(pixbuf, x, y, w, h, buf, 0, 0); + } + + ret = new gPicture(buf, _transparent); + } +#ifdef GTK3 + else if (_type == SURFACE) + { + cairo_surface_t *buf = create_surface(w, h); + cairo_t *cr = cairo_create(buf); + cairo_set_source_surface(cr, surface, x, y); + cairo_rectangle(cr, 0, 0, w, h); + cairo_fill(cr); + cairo_destroy(cr); + + ret = new gPicture(buf); + } +#endif + + return ret; +} + +gPicture *gPicture::copy() +{ + return copy(0, 0, width(), height()); +} + +void gPicture::putPixel(int x, int y, gColor col) +{ + guchar *p; + unsigned int nchannels; + unsigned int rowstride; + GdkPixbuf *image; + + if ( (x<0) || (x>width()) ) return; + if ( (y<0) || (y>height()) ) return; + + image = getPixbuf(); + + nchannels=gdk_pixbuf_get_n_channels(image); + rowstride=gdk_pixbuf_get_rowstride(image); + + p = gdk_pixbuf_get_pixels(image) + (x * nchannels) + (y * rowstride); + + /*if (nchannels>0) p[0]=( (col>>16) & 0xFF); + if (nchannels>1) p[1]=( (col>>8) & 0xFF ); + if (nchannels>2) p[2]=( col & 0xFF ); + //if (nchannels>3) p[3]=(col>>24);*/ + p[0]=((col>>16) & 0xFF); + p[1]=((col>>8) & 0xFF); + p[2]=(col & 0xFF); + if (nchannels>3) p[3]=255 - (col >> 24); + + invalidate(); +} + +gColor gPicture::getPixel(int x, int y) +{ + guchar *p; + unsigned int nchannels; + unsigned int rowstride; + gColor ret=0; + GdkPixbuf *image; + + if ( (x<0) || (x>width()) ) return 0; + if ( (y<0) || (y>height()) ) return 0; + + image = getPixbuf(); + + nchannels=gdk_pixbuf_get_n_channels(image); + rowstride=gdk_pixbuf_get_rowstride(image); + + p = gdk_pixbuf_get_pixels(image) + (x * nchannels) + (y * rowstride); + + if (nchannels>3) ret += (((gColor)255-p[3]) << 24); + if (nchannels>0) ret += (((gColor)p[0]) << 16); + if (nchannels>1) ret += (((gColor)p[1]) << 8); + if (nchannels>2) ret += ((gColor)p[2]); + + + return ret; +} + +unsigned char *gPicture::data() +{ + GdkPixbuf *pixbuf = getPixbuf(); + if (!pixbuf) + return NULL; + else + return gdk_pixbuf_get_pixels(pixbuf); +} + +// void gPicture::replace(gColor src, gColor dst, bool noteq) +// { +// if (_type == VOID) +// return; +// +// gt_pixbuf_replace_color(getPixbuf(), src, dst, noteq); +// invalidate(); +// } + + +gPicture* gPicture::flip(bool mirror) +{ + gPicture *ret; + guint32 *src, *dst; + int w, h; + guint32 *s, *d; + int x, y; + int rowstride; + + getPixbuf(); + ret = copy(); + + if (!isVoid()) + { + src = (guint32 *)data(); + dst = (guint32 *)ret->data(); + w = width(); + h = height(); + + rowstride = gdk_pixbuf_get_rowstride(getPixbuf()) / sizeof(guint32); + + if (!mirror) + { + for (y = 0; y < h; y++) + { + s = src + y * rowstride; + d = dst + y * rowstride + w; + for (x = 0; x < w; x++) + *--d = *s++; + } + } + else + { + s = src; + d = dst + h * rowstride; + for (y = 0; y < h; y++) + { + d -= rowstride; + memcpy(d, s, w * sizeof(guint32)); + s += rowstride; + } + } + } + + return ret; +} + +/* + This algorithm is inspired from the QT one +*/ + +static void rotate_image(double mat11, double mat12, double mat21, double mat22, double matdx, double matdy, + uchar *dptr, int dbpl, int dWidth, int dHeight, + uchar *sptr, int sbpl, int sWidth, int sHeight + ) +{ + int m11 = int(mat11 * 65536.0 + 1.0); + int m12 = int(mat12 * 65536.0 + 1.0); + int m21 = int(mat21 * 65536.0 + 1.0); + int m22 = int(mat22 * 65536.0 + 1.0); + + uint trigx; + uint trigy; + uint maxws = sWidth << 16; + uint maxhs = sHeight << 16; + + int m21ydx = int(matdx * 65536.0 + 1.0); + int m22ydy = int(matdy * 65536.0 + 1.0); + + for (int y = 0; y < dHeight; y++ ) + { + trigx = m21ydx; + trigy = m22ydy; + uchar *maxp = dptr + dbpl; + while (dptr < maxp) + { + if (trigx < maxws && trigy < maxhs) + *((uint*)dptr) = *((uint *)(sptr + sbpl * (trigy >> 16) + ((trigx >> 16) << 2))); + trigx += m11; + trigy += m12; + dptr += 4; + } + m21ydx += m21; + m22ydy += m22; + } +} + +gPicture* gPicture::rotate(double angle) +{ + double cosa = cos(-angle); + double sina = sin(-angle); + + if (angle == 0.0 || (cosa == 1.0 && sina == 0.0) || (width() <= 1 && height() <= 1)) + return copy(); + + double dx, dy, cx, cy; + int nw, nh; + double minx, miny, maxx, maxy; + int i, px[3], py[3]; + + nw = 0; + nh = 0; + minx = miny = maxx = maxy = 0; + + px[0] = (int)(width() * cosa + height() * (-sina) + 0.5); + py[0] = (int)(width() * sina + height() * cosa + 0.5); + + px[1] = (int)(width() * cosa + 0.5); + py[1] = (int)(width() * sina + 0.5); + + px[2] = (int)(height() * (-sina) + 0.5); + py[2] = (int)(height() * cosa + 0.5); + + for (i = 0; i < 3; i++) + { + if (px[i] > maxx) maxx = px[i]; + if (px[i] < minx) minx = px[i]; + if (py[i] > maxy) maxy = py[i]; + if (py[i] < miny) miny = py[i]; + } + + nw = (int)(maxx - minx + 0.5); + nh = (int)(maxy - miny + 0.5); + + cx = nw / 2.0 * cosa + nh / 2.0 * sina; + cy = nw / 2.0 * -sina + nh / 2.0 * cosa; + + dx = width() / 2.0 - cx; + dy = height() / 2.0 - cy; + + GdkPixbuf *src = getPixbuf(); + gPicture *npic = new gPicture(PIXBUF, nw, nh, isTransparent()); + npic->fill(0); + GdkPixbuf *dst = npic->getPixbuf(); + + rotate_image(cosa, -sina, sina, cosa, dx, dy, + gdk_pixbuf_get_pixels(dst), nw * 4, nw, nh, + gdk_pixbuf_get_pixels(src), width() * 4, width(), height()); + + return npic; +} + +gPicture *gPicture::stretch(int w, int h, bool smooth) +{ + gPicture *ret; + GdkPixbuf *image; + int ws, hs; + + if (w <= 0 && h <= 0) + return new gPicture(); + + if (w < 0) + w = width() * h / height(); + else if (h < 0) + h = height() * w / width(); + + if (w <= 0 || h <= 0) + return new gPicture(); + + ret = copy(); + if (ret->isVoid()) + return ret; + + image = ret->getPixbuf(); + + if (smooth) + { + ws = w; + hs = h; + if (ws < (width() / 4)) + ws = w * 4; + if (hs < (height() / 4)) + hs = h * 4; + if (ws != w || hs != h) + { + ret->pixbuf = gdk_pixbuf_scale_simple(image, ws, hs, GDK_INTERP_NEAREST); + g_object_unref(G_OBJECT(image)); + image = ret->pixbuf; + } + ret->pixbuf = gdk_pixbuf_scale_simple(image, w, h, GDK_INTERP_BILINEAR); + } + else + ret->pixbuf = gdk_pixbuf_scale_simple(image, w, h, GDK_INTERP_NEAREST); + + g_object_unref(G_OBJECT(image)); + + ret->_width = w; + ret->_height = h; + + ret->invalidate(); + return ret; +} + +void gPicture::draw(gPicture *pic, int x, int y, int w, int h, int sx, int sy, int sw, int sh) +{ + if (isVoid() || pic->isVoid()) + return; + + GT_NORMALIZE(x, y, w, h, sx, sy, sw, sh, pic->width(), pic->height()); + + if (x >= width() || y >= height()) + return; + + +#ifndef GTK3 + if (_type == PIXMAP) + { + GdkPixmap *dst = getPixmap(); + + if (pic->type() == gPicture::PIXMAP && !pic->isTransparent() && w == sw && h == sh) + { + GdkGC *gc = gdk_gc_new(GDK_DRAWABLE(dst)); + GdkPixmap *src = pic->getPixmap(); + gdk_draw_drawable(GDK_DRAWABLE(dst), gc, src, sx, sy, x, y, sw, sh); + g_object_unref(G_OBJECT(gc)); + } + else + { + bool del = false; + + if (w != sw || h != sh) + { + gPicture *pic2; + pic2 = pic->copy(sx, sy, sw, sh); + pic = pic2->stretch(w, h, true); + delete pic2; + del = true; + sx = 0; sy = 0; sw = w; sh = h; + } + + gdk_draw_pixbuf(GDK_DRAWABLE(dst), NULL, pic->getPixbuf(), sx, sy, x, y, sw, sh, GDK_RGB_DITHER_MAX, 0, 0); + + if (del) + delete pic; + } + } + else +#endif + if (_type == PIXBUF) + { + GdkPixbuf *dst = getPixbuf(); + GdkPixbuf *src = pic->getPixbuf(); + double scale_x, scale_y, offset_x, offset_y; + + scale_x = (double)w / sw; + scale_y = (double)h / sh; + offset_x = x - sx * scale_x; + offset_y = y - sy * scale_y; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + if ((x + w) > width()) + w = width() - x; + + if ((y + h) > height()) + h = height() - y; + + gdk_pixbuf_composite(src, dst, x, y, w, h, offset_x, offset_y, scale_x, scale_y, GDK_INTERP_BILINEAR, 255); + } + + invalidate(); +} + + +/***************************************************************** + +gPictureCache + +******************************************************************/ + +GHashTable *gPictureCache::cache = 0; + +static void destroy_key(char *key) +{ + g_free(key); +} + +static void destroy_value(gPicture *pic) +{ + //fprintf(stderr, "gPictureCache: destroy_value %p\n", pixmap); + pic->unref(); +} + +void gPictureCache::init() +{ + cache = g_hash_table_new_full((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal, (GDestroyNotify)destroy_key, (GDestroyNotify)destroy_value); +} + +void gPictureCache::exit() +{ + g_hash_table_destroy(cache); +} + +void gPictureCache::put(const char *key, gPicture *pic) +{ + if (!key || !*key) return; + + //fprintf(stderr, "gPictureCache: put %p\n", pixmap); + pic->ref(); + g_hash_table_replace(cache, (gpointer)g_strdup(key), (gpointer)pic); +} + +gPicture *gPictureCache::get(const char *key) +{ + if (!key || !*key) return 0; + + return (gPicture *)g_hash_table_lookup(cache, (gconstpointer)key); +} + +void gPictureCache::flush() +{ + exit(); + init(); +} + +void gPicture::makeGray() +{ + if (_type == VOID) + return; + + gt_pixbuf_make_gray(getPixbuf()); + invalidate(); +} + +void gPicture::makeTransparent(gColor color) +{ + if (_type == VOID) + return; + + gt_pixbuf_make_alpha(getPixbuf(), color); + invalidate(); +} + +GdkPixbuf *gPicture::getIconPixbuf() +{ + GdkPixbuf *icon = getPixbuf(); + + if ((_width & 7) || (_height & 7)) + { + icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, (_width + 7) & ~7, (_height + 7) & ~7); + gdk_pixbuf_fill(icon, 0); + gdk_pixbuf_copy_area(getPixbuf(), 0, 0, _width, _height, icon, 0, 0); + } + + return icon; +} + diff --git a/gb.gtk/src/gpicture.h b/gb.gtk/src/gpicture.h new file mode 100644 index 00000000..ad13b734 --- /dev/null +++ b/gb.gtk/src/gpicture.h @@ -0,0 +1,133 @@ +/*************************************************************************** + + gpicture.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GPICTURE_H +#define __GPICTURE_H + +#include "widgets.h" +#include "gshare.h" +#include "gcolor.h" +#include "gtag.h" + +class alphaCache; + +class gPicture : public gShare +{ +public: + + enum gPictureType + { + VOID, + PIXBUF, +#ifndef GTK3 + PIXMAP, +#endif + SURFACE + }; + + gPicture(); + gPicture(gPictureType type, int w, int h, bool trans); + ~gPicture(); + gPicture *copy(); + + bool isVoid() { return _type == VOID; } + + static void assign(gPicture **dst, gPicture *src = 0) { gShare::assign((gShare **)dst, src); } + + gPictureType type() const { return _type; } + int width() const { return _width; } + int height() const { return _height; } + int depth(); + bool isTransparent() const { return _transparent; } + unsigned char *data(); + + void setTransparent(bool vl); + + void clear(); + void resize(int width,int height); + int save(const char *path, int quality = -1); + void fill(gColor col); + gPicture *copy(int x, int y, int w, int h); + + gPicture *flip(bool mirror = false); + gPicture *mirror() { return flip(true); } + gPicture *rotate(double ang); + gPicture *stretch(int w, int h, bool smooth); + + gColor getPixel(int x, int y); + void putPixel(int x, int y, gColor col); + void draw(gPicture *src, int x, int y, int w = -1, int h = -1, int sx = 0, int sy = 0, int sw = -1, int sh = -1); + void makeGray(); + void makeTransparent(gColor color); + + static gPicture *fromNamedIcon(const char *name, int len = -1); + static gPicture *fromMemory(char *addr, unsigned int len); + static gPicture *fromData(const char *data, int width, int height); + +//"Private" +#ifndef GTK3 + GdkPixmap *pixmap; + GdkBitmap *mask; +#endif + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + + gPictureType _type; + bool _transparent; + int _width; + int _height; + + gPicture(GdkPixbuf *image, bool trans = true); + gPicture(cairo_surface_t *surf); +#ifndef GTK3 + gPicture(GdkPixmap *pix); +#endif + void initialize(); + GdkPixbuf *getPixbuf(); +#ifndef GTK3 + GdkPixmap *getPixmap(); + GdkBitmap *getMask(); +#endif + GdkPixbuf *getIconPixbuf(); + cairo_surface_t *getSurface(); + + static gPicture* fromPixbuf(GdkPixbuf *buf) { return new gPicture(buf); } + +// "Private" + void invalidate(); + void createMask(bool white); +}; + +class gPictureCache +{ +public: + static void put(const char *key, gPicture *img); + static gPicture *get(const char *key); + static void flush(); + static void init(); + static void exit(); +private: + static GHashTable *cache; +}; + +#endif diff --git a/gb.gtk/src/gplugin.h b/gb.gtk/src/gplugin.h new file mode 100644 index 00000000..344d3d89 --- /dev/null +++ b/gb.gtk/src/gplugin.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + gplugin.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GPLUGIN_H +#define __GPLUGIN_H + +class gPlugin : public gControl +{ +public: + gPlugin(gContainer *parent); + void plug(int id); + void discard(); +//"Properties" + int client(); + + int getBorder() { return getFrameBorder(); } + void setBorder(int vl) { setFrameBorder(vl); } + +//"Signals" + void (*onPlug)(gControl *sender); + void (*onUnplug)(gControl *sender); + void (*onError)(gControl *sender); +}; + +#endif diff --git a/gb.gtk/src/gprinter.cpp b/gb.gtk/src/gprinter.cpp new file mode 100644 index 00000000..061be90b --- /dev/null +++ b/gb.gtk/src/gprinter.cpp @@ -0,0 +1,728 @@ +/*************************************************************************** + + gprinter.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GPRINTER_CPP + +#include <unistd.h> + +#include "widgets.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gapplication.h" +#include "gb.form.print.h" +#include "gprinter.h" + +//#define DEBUG_ME 1 + +static void cb_begin_cancel(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + if (printer->_preview) + { + CB_printer_begin(printer, context); + return; + } + + #if DEBUG_ME + fprintf(stderr, "cb_begin_cancel: %d\n", cairo_surface_get_type(cairo_get_target(gtk_print_context_get_cairo_context(context)))); + #endif + printer->storeSettings(); + printer->cancel(); + printer->_configure_ok = true; +} + +static void cb_begin(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_begin\n"); + #endif + printer->defineSettings(); + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-begin-before.txt", NULL); + CB_printer_begin(printer, context); + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-begin-after.txt", NULL); +} + +static void cb_end(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_end: %d\n", printer->_preview); + #endif + if (printer->_preview) + CB_printer_end(printer); +} + +static gboolean cb_paginate(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_paginate\n"); + #endif + CB_printer_paginate(printer); + return printer->isPageCountSet(); +} + +static void cb_draw(GtkPrintOperation *operation, GtkPrintContext *context, int page, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_draw\n"); + #endif + CB_printer_draw(printer, context, page); +} + +static gboolean cb_preview(GtkPrintOperation *operation, GtkPrintOperationPreview *preview,GtkPrintContext *context, GtkWindow *parent, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_preview\n"); + #endif + printer->_preview = true; + return FALSE; +} + + +static gboolean find_default_printer(GtkPrinter *gtk_printer, gPrinter *printer) +{ + if (!printer->name()) + printer->setName(gtk_printer_get_name(gtk_printer)); + + if (gtk_printer_is_default(gtk_printer)) + { + #if DEBUG_ME + fprintf(stderr, "find_default_printer: %s\n", gtk_printer_get_name(gtk_printer)); + #endif + + printer->setName(gtk_printer_get_name(gtk_printer)); + return TRUE; + } + + return FALSE; +} + +static gboolean find_file_printer(GtkPrinter *gtk_printer, gPrinter *printer) +{ + if (!strcmp(G_OBJECT_TYPE_NAME(gtk_printer_get_backend(gtk_printer)), "GtkPrintBackendFile")) + { + #if DEBUG_ME + fprintf(stderr, "find_file_printer: %s\n", gtk_printer_get_name(gtk_printer)); + #endif + + printer->setName(gtk_printer_get_name(gtk_printer)); + return TRUE; + } + + return FALSE; +} + +#ifdef GTK3 +extern "C" void gtk_printer_option_widget_get_type(void); +#endif + +gPrinter *gPrinter::_current = NULL; + +gPrinter::gPrinter() +{ + _operation = NULL; + _settings = gtk_print_settings_new(); + _page = gtk_page_setup_new(); + _page_count = 1; + _page_count_set = false; + +#ifdef GTK3 + gtk_printer_option_widget_get_type(); +#endif + gtk_enumerate_printers((GtkPrinterFunc)find_default_printer, this, NULL, TRUE); + + setPaperModel(GB_PRINT_A4); + setUseFullPage(false); +} + +gPrinter::~gPrinter() +{ + g_object_unref(G_OBJECT(_settings)); + g_object_unref(G_OBJECT(_page)); +} + +void gPrinter::storeSettings() +{ + if (!_operation) + return; + + g_object_unref(G_OBJECT(_settings)); + _settings = gtk_print_settings_copy(gtk_print_operation_get_print_settings(_operation)); + #if DEBUG_ME + gtk_print_settings_to_file(_settings, "/home/benoit/settings.txt", NULL); + gtk_page_setup_to_file(_page, "/home/benoit/page.txt", NULL); + #endif +} + +void gPrinter::defineSettings() +{ + if (!_operation) + return; + + gtk_print_operation_set_print_settings(_operation, _settings); + gtk_print_operation_set_default_page_setup(_operation, _page); +} + +bool gPrinter::run(bool configure) +{ + GtkPrintOperation *operation; + GtkPrintOperationResult res; + GtkPrintOperationAction action; + gMainWindow *active; + GError *error; + const char *file; + + #if DEBUG_ME + fprintf(stderr, "gPrinter::run: %d\n", configure); + fprintf(stderr, "name = %s\n", name()); + fprintf(stderr, "orientation = %d\n", orientation()); + fprintf(stderr, "isVirtual = %d\n", isVirtual()); + #endif + + operation = gtk_print_operation_new(); + _operation = operation; + + gtk_print_operation_set_embed_page_setup(operation, true); + gtk_print_operation_set_n_pages(operation, _page_count); + gtk_print_operation_set_use_full_page(operation, _use_full_page); + gtk_print_operation_set_print_settings(operation, _settings); + gtk_print_operation_set_default_page_setup(_operation, _page); + + if (configure) + { + _preview = false; + _configure_ok = false; + g_signal_connect(operation, "begin_print", G_CALLBACK(cb_begin_cancel), this); + g_signal_connect(operation, "preview", G_CALLBACK(cb_preview), this); + g_signal_connect(operation, "end_print", G_CALLBACK(cb_end), this); + g_signal_connect(operation, "paginate", G_CALLBACK(cb_paginate), this); + g_signal_connect(operation, "draw_page", G_CALLBACK(cb_draw), this); + } + else + { + _preview = true; + g_signal_connect(operation, "begin_print", G_CALLBACK(cb_begin), this); + g_signal_connect(operation, "end_print", G_CALLBACK(cb_end), this); + g_signal_connect(operation, "paginate", G_CALLBACK(cb_paginate), this); + g_signal_connect(operation, "draw_page", G_CALLBACK(cb_draw), this); + } + + active = gDesktop::activeWindow(); + + if (isVirtual()) + { + _current = this; + gApplication::_fix_printer_dialog = true; + } + else + gApplication::_fix_printer_dialog = false; + + action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + if (!configure) + { + file = outputFileName(); + if (file) + { + unlink(file); + setOutputFileName(outputFileName()); + defineSettings(); + } + // GTK+ bug: GTK_PRINT_OPERATION_ACTION_PRINT does not work with virtual printers. + if (isVirtual()) + { + gApplication::_close_next_window = true; + } + else + action = GTK_PRINT_OPERATION_ACTION_PRINT; + } + + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-before.txt", NULL); + res = gtk_print_operation_run(operation, action, active ? GTK_WINDOW(active->border) : NULL, &error); + _current = NULL; + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-after.txt", NULL); + + #if DEBUG_ME + fprintf(stderr, "_preview = %d\n", _preview); + #endif + + if (_preview) + { + _preview = false; + res = GTK_PRINT_OPERATION_RESULT_CANCEL; + } + else if (_configure_ok) + res = GTK_PRINT_OPERATION_RESULT_APPLY; + + if (res == GTK_PRINT_OPERATION_RESULT_ERROR) + { + #if DEBUG_ME + fprintf(stderr, "error: %s\n", error->message); + #endif + g_error_free(error); + } + else if (res == GTK_PRINT_OPERATION_RESULT_CANCEL) + { + #if DEBUG_ME + fprintf(stderr, "cancel\n"); + #endif + } + else + { + #if DEBUG_ME + fprintf(stderr, "ok\n"); + #endif + } + + if (!configure) + _page_count_set = false; + + if (configure && res == GTK_PRINT_OPERATION_RESULT_APPLY) + { + g_object_unref(G_OBJECT(_page)); + _page = gtk_page_setup_copy(gtk_print_operation_get_default_page_setup(operation)); + } + + g_object_unref(G_OBJECT(operation)); + _operation = NULL; + + #if DEBUG_ME + fprintf(stderr, "orientation => %d\n", orientation()); + #endif + + return res != GTK_PRINT_OPERATION_RESULT_APPLY; +} + +void gPrinter::cancel() +{ + if (!_operation) + return; + + #if DEBUG_ME + fprintf(stderr, "gPrinter::cancel\n"); + #endif + gtk_print_operation_cancel(_operation); +} + +void gPrinter::setPageCount(int v) +{ + if (v < 1 || v > 32767) + return; + + _page_count = v; + _page_count_set = true; + if (_operation) + gtk_print_operation_set_n_pages(_operation, _page_count); +} + +int gPrinter::orientation() const +{ + switch (gtk_page_setup_get_orientation(_page)) + //switch (gtk_print_settings_get_orientation(_settings)) + { + case GTK_PAGE_ORIENTATION_LANDSCAPE: return GB_PRINT_LANDSCAPE; + case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT: return GB_PRINT_PORTRAIT; + case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE: return GB_PRINT_LANDSCAPE; + case GTK_PAGE_ORIENTATION_PORTRAIT: default: return GB_PRINT_PORTRAIT; + } +} + +void gPrinter::setOrientation(int v) +{ + GtkPageOrientation orient; + + switch(v) + { + case GB_PRINT_LANDSCAPE: orient = GTK_PAGE_ORIENTATION_LANDSCAPE; break; + //case GB_PRINT_REVERSE_PORTRAIT: orient = GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT; break; + //case GB_PRINT_REVERSE_LANDSCAPE: orient = GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE; break; + case GB_PRINT_PORTRAIT: default: orient = GTK_PAGE_ORIENTATION_PORTRAIT; break; + } + + gtk_print_settings_set_orientation(_settings, orient); + gtk_page_setup_set_orientation(_page, orient); +} + + +static GtkPaperSize *get_paper_size(int paperSize) +{ + const char *name; + + switch(paperSize) + { + case GB_PRINT_A3: name = GTK_PAPER_NAME_A3; break; + case GB_PRINT_A5: name = GTK_PAPER_NAME_A5; break; + case GB_PRINT_B5: name = GTK_PAPER_NAME_B5; break; + case GB_PRINT_LETTER: name = GTK_PAPER_NAME_LETTER; break; + case GB_PRINT_EXECUTIVE: name = GTK_PAPER_NAME_EXECUTIVE; break; + case GB_PRINT_LEGAL: name = GTK_PAPER_NAME_LEGAL; break; + default: name = GTK_PAPER_NAME_A4; + } + + return gtk_paper_size_new(name); +} + +static void get_paper_dimensions(GtkPaperSize *paper, double *w, double *h) +{ + *w = gtk_paper_size_get_width(paper, GTK_UNIT_MM); + *h = gtk_paper_size_get_height(paper, GTK_UNIT_MM); +} + +void gPrinter::setPaperModel(int v) +{ + GtkPaperSize *paper; + + paper = get_paper_size(v); + gtk_print_settings_set_paper_size(_settings, paper); + gtk_page_setup_set_paper_size(_page, paper); + gtk_paper_size_free(paper); +} + +int gPrinter::paperModel() const +{ + static int modes[] = { GB_PRINT_A4, GB_PRINT_A3, GB_PRINT_A5, GB_PRINT_B5, GB_PRINT_LETTER, GB_PRINT_EXECUTIVE, GB_PRINT_LEGAL, GB_PRINT_CUSTOM }; + + double w, h; + double wc, hc; + int i; + GtkPaperSize *paper; + GtkPaperSize *compare; + int ret = GB_PRINT_CUSTOM; + + paper = gtk_page_setup_get_paper_size(_page); + get_paper_dimensions(paper, &w, &h); + + for (i = 0;; i++) + { + if (modes[i] == GB_PRINT_CUSTOM) + break; + compare = get_paper_size(modes[i]); + get_paper_dimensions(compare, &wc, &hc); + gtk_paper_size_free(compare); + if (fabs(wc - w) < 1E-6 && fabs(hc - h) < 1E-6) + { + ret = modes[i]; + break; + } + } + + return ret; +} + +void gPrinter::getPaperSize(double *width, double *height) +{ + GtkPaperSize *paper = gtk_page_setup_get_paper_size(_page); + + get_paper_dimensions(paper, width, height); + + if (orientation() == GB_PRINT_LANDSCAPE) + { + double swap = *width; + *width = *height; + *height = swap; + } +} + +void gPrinter::setPaperSize(double width, double height) +{ + GtkPaperSize *paper; + + if (orientation() == GB_PRINT_LANDSCAPE) + { + double swap = width; + width = height; + height = swap; + } + + paper = gtk_paper_size_new_custom("Custom", "Custom", width, height, GTK_UNIT_MM); + gtk_page_setup_set_paper_size(_page, paper); + gtk_print_settings_set_paper_size(_settings, paper); + gtk_paper_size_free(paper); +} + +bool gPrinter::collateCopies() const +{ + return gtk_print_settings_get_collate(_settings); +} + +void gPrinter::setCollateCopies(bool v) +{ + gtk_print_settings_set_collate(_settings, v); +} + +bool gPrinter::reverserOrder() const +{ + return gtk_print_settings_get_reverse(_settings); +} + +void gPrinter::setReverseOrder(bool v) +{ + gtk_print_settings_set_reverse(_settings, v); +} + +int gPrinter::duplex() const +{ + switch (gtk_print_settings_get_duplex(_settings)) + { + case GTK_PRINT_DUPLEX_SIMPLEX: return GB_PRINT_SIMPLEX; + case GTK_PRINT_DUPLEX_HORIZONTAL: return GB_PRINT_DUPLEX_HORIZONTAL; + case GTK_PRINT_DUPLEX_VERTICAL: return GB_PRINT_DUPLEX_VERTICAL; + default: return GB_PRINT_SIMPLEX; + } +} + +void gPrinter::setDuplex(int v) +{ + GtkPrintDuplex duplex; + + switch(v) + { + case GB_PRINT_SIMPLEX: duplex = GTK_PRINT_DUPLEX_SIMPLEX; break; + case GB_PRINT_DUPLEX_HORIZONTAL: duplex = GTK_PRINT_DUPLEX_HORIZONTAL; break; + case GB_PRINT_DUPLEX_VERTICAL: duplex = GTK_PRINT_DUPLEX_VERTICAL; break; + default: duplex = GTK_PRINT_DUPLEX_SIMPLEX; break; + } + + gtk_print_settings_set_duplex(_settings, duplex); +} + +bool gPrinter::useColor() const +{ + return gtk_print_settings_get_use_color(_settings); +} + +void gPrinter::setUseColor(bool v) +{ + gtk_print_settings_set_use_color(_settings, v); +} + +int gPrinter::numCopies() const +{ + return gtk_print_settings_get_n_copies(_settings); +} + +void gPrinter::setNumCopies(int v) +{ + gtk_print_settings_set_n_copies(_settings, v); +} + +int gPrinter::resolution() const +{ + return gtk_print_settings_get_resolution(_settings); +} + +void gPrinter::setResolution(int v) +{ + gtk_print_settings_set_resolution(_settings, v); +} + +void gPrinter::getPrintPages(int *from, int *to) const +{ + GtkPageRange *range; + int nrange; + + if (gtk_print_settings_get_print_pages(_settings) == GTK_PRINT_PAGES_ALL) + { + *from = *to = -1; + return; + } + + range = gtk_print_settings_get_page_ranges(_settings, &nrange); + + if (nrange <= 0) + *from = *to = -1; + else + { + *from = range->start; + *to = range->end; + g_free(range); + } +} + +void gPrinter::setPrintPages(int from, int to) +{ + GtkPageRange range = { from, to }; + + gtk_print_settings_set_page_ranges(_settings, &range, 1); + if (from < 0) + gtk_print_settings_set_print_pages(_settings, GTK_PRINT_PAGES_ALL); + else + gtk_print_settings_set_print_pages(_settings, GTK_PRINT_PAGES_RANGES); +} + +void gPrinter::setUseFullPage(bool v) +{ + _use_full_page = v; + if (_operation) + gtk_print_operation_set_use_full_page(_operation, v); +} + +const char *gPrinter::name() const +{ + return gtk_print_settings_get_printer(_settings); +} + +void gPrinter::setName(const char *name) +{ + gtk_print_settings_set_printer(_settings, name); +} + +static char *unescape_uri(const char *uri) +{ + char *path; + + if (!uri) + return NULL; + + if (strncmp(uri, "file://", 7)) + return NULL; + + path = g_uri_unescape_string(&uri[7], "/"); + gt_free_later(path); + + return path; +} + +const char *gPrinter::outputFileName() const +{ + //fprintf(stderr, "outputFileName: %s\n", gtk_print_settings_get(_settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); + return unescape_uri(gtk_print_settings_get(_settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); +} + +void gPrinter::setOutputFileName(const char *file) +{ + char *escaped; + char *uri = NULL; //[strlen(file) + 7]; + //const char *format; + + escaped = g_uri_escape_string(file, "/", true); + g_stradd(&uri, "file://"); + g_stradd(&uri, escaped); + g_free(escaped); + + /*if (g_str_has_suffix(uri, ".ps")) + format = "ps"; + else if (g_str_has_suffix(uri, ".pdf")) + format = "pdf"; + else if (g_str_has_suffix(uri, ".svg")) + format = "svg"; + else + format = NULL;*/ + + if (file && *file) + gtk_enumerate_printers((GtkPrinterFunc)find_file_printer, this, NULL, TRUE); + + gtk_print_settings_set(_settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri); + g_free(uri); + + // It does not work!!! + //if (format) + // gtk_print_settings_set(_settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT, format); +} + + +static bool _is_virtual; + +static gboolean find_printer(GtkPrinter *gtk_printer, gPrinter *printer) +{ + if (strcmp(printer->name(), gtk_printer_get_name(gtk_printer))) + return false; + + _is_virtual = gtk_printer_is_virtual(gtk_printer); + return true; +} + +bool gPrinter::isVirtual() +{ + _is_virtual = false; + gtk_enumerate_printers((GtkPrinterFunc)find_printer, this, NULL, TRUE); + return _is_virtual; +} + +static int _dump_tree_radio_button; +static int _dump_tree_entry; + +static void dump_tree(GtkWidget *wid, GtkPrintUnixDialog *dialog) +{ + //fprintf(stderr, "dump_tree: %s\n", G_OBJECT_TYPE_NAME(wid)); + if (GTK_IS_RADIO_BUTTON(wid)) + { + //fprintf(stderr, "dump_tree: radio button: %s\n", gtk_button_get_label(GTK_BUTTON(wid))); + _dump_tree_radio_button--; + if (_dump_tree_radio_button == 0) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(wid), TRUE); + } + } + else if (GTK_IS_ENTRY(wid)) + { + //fprintf(stderr, "dump_tree: entry: %s\n", gtk_entry_get_text(GTK_ENTRY(wid))); + _dump_tree_entry--; + if (_dump_tree_entry == 0) + { + char *path; + char *name; + + path = unescape_uri(gtk_print_settings_get(gPrinter::_current->_settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); + //fprintf(stderr, "dump_tree: path = %s\n", path); + if (path) + { + name = g_path_get_basename(path); + gtk_entry_set_text(GTK_ENTRY(wid), name); + g_free(name); + } + } + } + else if (GTK_IS_CONTAINER(wid)) + gtk_container_foreach(GTK_CONTAINER(wid), (GtkCallback)dump_tree, (gpointer)dialog); +} + +void gPrinter::fixPrintDialog(GtkPrintUnixDialog *dialog) +{ + const char *output = gtk_print_settings_get(gPrinter::_current->_settings, GTK_PRINT_SETTINGS_OUTPUT_URI); + + _dump_tree_entry = 1; + _dump_tree_radio_button = 0; + + if (output) + { + if (g_str_has_suffix(output, ".pdf")) + _dump_tree_radio_button = 0; + if (g_str_has_suffix(output, ".ps")) + _dump_tree_radio_button = 2; + else if (g_str_has_suffix(output, ".svg")) + _dump_tree_radio_button = 3; + } + + dump_tree(GTK_WIDGET(dialog), dialog); +} + +static gboolean find_all_printers(GtkPrinter *gtk_printer, bool (*callback)(const char *, bool)) +{ + if (strcmp(G_OBJECT_TYPE_NAME(gtk_printer_get_backend(gtk_printer)), "GtkPrintBackendFile")) + return (*callback)(gtk_printer_get_name(gtk_printer), gtk_printer_is_default(gtk_printer)); + + return FALSE; +} + +void gPrinter::enumeratePrinters(bool (*callback)(const char *, bool)) +{ + gtk_enumerate_printers((GtkPrinterFunc)find_all_printers, (gpointer)callback, NULL, TRUE); +} diff --git a/gb.gtk/src/gprinter.h b/gb.gtk/src/gprinter.h new file mode 100644 index 00000000..7bc6c999 --- /dev/null +++ b/gb.gtk/src/gprinter.h @@ -0,0 +1,114 @@ +/*************************************************************************** + + gprinter.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GPRINTER_H +#define __GPRINTER_H + +#include <gtk/gtkunixprint.h> + +class gPrinter +{ +public: + gPrinter(); + virtual ~gPrinter(); + void *tag; + + bool configure() { return run(true); } + bool print() { return run(false); } + void cancel(); + + void setPageCount(int v); + int pageCount() const { return _page_count; } + bool isPageCountSet() const { return _page_count_set; } + void clearPageCountSet() { _page_count_set = false; } + + int orientation() const; + void setOrientation(int v); + + int paperModel() const; + void setPaperModel(int v); + + void getPaperSize(double *width, double *height); + void setPaperSize(double width, double height); + + bool collateCopies() const; + void setCollateCopies(bool v); + + bool reverserOrder() const; + void setReverseOrder(bool v); + + int duplex() const; + void setDuplex(int v); + + bool useColor() const; + void setUseColor(bool v); + + int numCopies() const; + void setNumCopies(int v); + + int resolution() const; + void setResolution(int v); + + void getPrintPages(int *from, int *to) const; + void setPrintPages(int from, int to); + + bool useFullPage() const { return _use_full_page; } + void setUseFullPage(bool v); + + const char *name() const; + void setName(const char *name); + + const char *outputFileName() const; + void setOutputFileName(const char *file); + + void defineSettings(); + void storeSettings(); + + bool _preview; + bool _configure_ok; + GtkPrinter *_printer; + GtkPrintSettings *_settings; + + static void fixPrintDialog(GtkPrintUnixDialog *dialog); + static gPrinter *_current; + + static void enumeratePrinters(bool (*callback)(const char *name, bool)); + +private: + bool run(bool configure); + bool isVirtual(); + + GtkPrintOperation *_operation; + GtkPageSetup *_page; + int _page_count; + bool _page_count_set; + bool _use_full_page; +}; + +// Callbacks +void CB_printer_begin(gPrinter *me, GtkPrintContext *context); +void CB_printer_end(gPrinter *me); +void CB_printer_draw(gPrinter *me, GtkPrintContext *context, int page); +void CB_printer_paginate(gPrinter *me); + +#endif diff --git a/gb.gtk/src/gscrollbar.h b/gb.gtk/src/gscrollbar.h new file mode 100644 index 00000000..57c4b1d3 --- /dev/null +++ b/gb.gtk/src/gscrollbar.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gscrollbar.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSCROLLBAR_H +#define __GSCROLLBAR_H + +#include "gslider.h" + +class gScrollBar : public gSlider +{ +public: + gScrollBar(gContainer *parent); +}; + +#endif diff --git a/gb.gtk/src/gshare.h b/gb.gtk/src/gshare.h new file mode 100644 index 00000000..fa210560 --- /dev/null +++ b/gb.gtk/src/gshare.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + gshare.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSHARE_H +#define __GSHARE_H + +#include "gtag.h" + +//#define DEBUG_SHARE 1 + +#ifdef DEBUG_SHARE + +class gShare +{ +public: + gShare() { nref = 1; tag = NULL; fprintf(stderr, "gShare: %p (1)\n", this); } + virtual ~gShare() { fprintf(stderr, "~gShare: %p\n", this); if (tag) while (nref > 1) nref--, tag->unref(); } + + void ref() { nref++; if (tag) tag->ref(); fprintf(stderr, "gShare::ref: %p (%d)\n", this, nref); } + void unref() { nref--; fprintf(stderr, "gShare::unref: %p (%d)\n", this, nref); if (nref <= 0) delete this; else if (tag) tag->unref(); } + int refCount() { return nref; } + + void setTag(gTag *t) { tag = t; for (int i = 0; i < (nref - 1); i++) tag->ref(); } + gTag *getTag() { return tag; } + void *getTagValue() { return tag->get(); } + +protected: + static void assign(gShare **dst, gShare *src = 0) + { + if (src) src->ref(); + if (*dst) (*dst)->unref(); + *dst = src; + } + +private: + int nref; + gTag *tag; +}; + +#else + +class gShare +{ +public: + gShare() { nref = 1; tag = NULL; } + virtual ~gShare() { if (tag) { while (nref > 1) nref--, tag->unref(); delete tag; } } + + void ref() { nref++; if (tag) tag->ref(); } + void unref() { nref--; if (nref <= 0) delete this; else if (tag) tag->unref(); } + int refCount() { return nref; } + + void setTag(gTag *t) { tag = t; for (int i = 0; i < (nref - 1); i++) tag->ref(); } + gTag *getTag() { return tag; } + void *getTagValue() { return tag->get(); } + +protected: + static void assign(gShare **dst, gShare *src = 0) + { + if (src) src->ref(); + if (*dst) (*dst)->unref(); + *dst = src; + } + +private: + int nref; + gTag *tag; +}; + +#endif + +#endif + diff --git a/gb.gtk/src/gsignals.cpp b/gb.gtk/src/gsignals.cpp new file mode 100644 index 00000000..f6fe2cd9 --- /dev/null +++ b/gb.gtk/src/gsignals.cpp @@ -0,0 +1,399 @@ +/*************************************************************************** + + gsignals.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include <gdk/gdkkeysyms.h> + +#include "gapplication.h" +#include "gdrawingarea.h" +#include "gkey.h" +#include "gmouse.h" +#include "gmainwindow.h" +#include "gdrag.h" +#include "gdesktop.h" + +//#define DEBUG_DND 1 + +static void cb_destroy(GtkWidget *object, gControl *data) +{ + if (data->_no_delete) + return; + + //if (!data->_destroyed) + delete data; +} + +static gboolean cb_menu(GtkWidget *widget, gControl *data) +{ + return CB_control_mouse(data, gEvent_MouseMenu); +} + +gboolean gcb_focus_in(GtkWidget *widget, GdkEventFocus *event, gControl *data) +{ + //fprintf(stderr, "gcb_focus_in: %s\n", data->name()); + + gApplication::setActiveControl(data, true); + if (data->frame) + data->refresh(); + + return false; +} + +gboolean gcb_focus_out(GtkWidget *widget, GdkEventFocus *event, gControl *data) +{ + //fprintf(stderr, "gcb_focus_out: %s\n", data->name()); + + /*if (!::strcmp(data->name(), "txtName")) + BREAKPOINT();*/ + + if (!gApplication::_keep_focus) + gApplication::setActiveControl(data, false); + if (data->frame) + data->refresh(); + + return false; +} + +gboolean gcb_focus(GtkWidget *widget, GtkDirectionType direction, gControl *data) +{ + gControl *ctrl; + + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD) + { + ctrl = gApplication::activeControl(); + + if (!ctrl) + return true; + + if (ctrl->topLevel() != data) + return true; + + for(;;) + { + if (direction == GTK_DIR_TAB_FORWARD) + ctrl = ctrl->nextFocus(); + else + ctrl = ctrl->previousFocus(); + + if (!ctrl) + break; + + //fprintf(stderr, "gcb_focus: %s / %d %d %d %d\n", ctrl->name(), ctrl->isReallyVisible(), ctrl->isEnabled(), ctrl->canFocus(), !ctrl->isNoTabFocusRec()); + + if (!ctrl->isTopLevel() && ctrl->isReallyVisible() && ctrl->isEnabled() && ctrl->canFocus() && !ctrl->isNoTabFocusRec()) + { + //fprintf(stderr, "cb_focus: --> %s / %d\n", ctrl->name(), ctrl->isNoTabFocusRec()); + ctrl->setFocus(); + break; + } + if (ctrl == gApplication::activeControl()) + break; + } + } + + return true; +} + + +/**************************************************** + Drag +*****************************************************/ + +static void cb_drag_data_get(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *dt, guint i, guint time, gControl *data) +{ + char *text; + int len; + gPicture *pic; + //g_debug("sg_drag_data_get\n"); + + context = gDrag::enable(context, data, time); + + text = gDrag::getText(&len, NULL, true); + if (text) + { + gtk_selection_data_set_text(dt, text, len); + return; + } + + pic = gDrag::getImage(true); + if (pic) + { + gtk_selection_data_set_pixbuf(dt, pic->getPixbuf()); + } + + gDrag::disable(context); +} + +static void cb_drag_end(GtkWidget *widget,GdkDragContext *ct,gControl *data) +{ + #if DEBUG_DND + fprintf(stderr, "\nsg_drag_end: %s\n", data->name()); + #endif + + gDrag::end(); +} + + +/**************************************************** + Drop +*****************************************************/ + +// BM: What for? +//static guint32 _drag_time = 0; + +static void cb_drag_leave(GtkWidget *widget, GdkDragContext *context, guint time, gControl *data) +{ + if (!gDrag::isCurrent(data)) + return; + + #if DEBUG_DND + fprintf(stderr, "\ncb_drag_leave: %s\n", data->name()); + #endif + + gDrag::setCurrent(NULL); + gDrag::hide(data); +} + +static gboolean cb_drag_motion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gControl *data) +{ + bool cancel; + + GdkModifierType mask; + GdkDragAction dnd_action; + int action; + gControl *source; + +#ifdef GTK3 + + GdkDevice *pointer; + + #if GDK_MAJOR_VERSION > 3 || (GDK_MAJOR_VERSION == 3 && GDK_MINOR_VERSION >= 20) + pointer = gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display_get_default())); + #else + pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); + #endif + + gdk_window_get_device_position(gtk_widget_get_window(widget), pointer, NULL, NULL, &mask); + +#else + + gdk_window_get_pointer(gtk_widget_get_window (widget), NULL, NULL, &mask); + +#endif + + mask = (GdkModifierType)(mask & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)); + + if (mask == GDK_CONTROL_MASK) + { + dnd_action = GDK_ACTION_COPY; + action = DRAG_COPY; + } + else if (mask == GDK_SHIFT_MASK) + { + dnd_action = GDK_ACTION_LINK; + action = DRAG_LINK; + } + else + { + dnd_action = GDK_ACTION_MOVE; + action = DRAG_MOVE; + } + + #if DEBUG_DND + fprintf(stderr, "\ncb_drag_motion: %s / %d / %d\n", data->name(), action, gdk_drag_context_get_selected_action(context)); + #endif + + gApplication::checkHoveredControl(data); + + source = gApplication::controlItem(gtk_drag_get_source_widget(context)); + gDrag::setDropData(action, x, y, source, NULL); + + context = gDrag::enable(context, data, time); + + cancel = gDrag::setCurrent(data); + #if DEBUG_DND + fprintf(stderr, "setCurrent -> %d\n", cancel); + #endif + + if (!cancel) + { + //fprintf(stderr, "cb_drag_motion: onDragMove: %p\n", widget); + gControl *control = data; + + //while (control->_proxy) + // control = control->_proxy; + + while (control) + { + #if DEBUG_DND + fprintf(stderr, "send DragMove %s\n", control->name()); + #endif + if (CB_control_can_raise(control, gEvent_DragMove)) + { + cancel = CB_control_drag_move(control); + if (cancel) + break; + } + control = control->_proxy_for; + } + } + + context = gDrag::disable(context); + + if (cancel) + { + #if DEBUG_DND + fprintf(stderr, "cb_drag_motion: cancel\n"); + #endif + gDrag::hide(data); + } + else + { + #if DEBUG_DND + fprintf(stderr, "cb_drag_motion: accept %d / %d\n", action, gdk_drag_context_get_selected_action(context)); + #endif + gdk_drag_status(context, dnd_action, time); + return true; + } + + return false; +} + + +static gboolean cb_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gControl *data) +{ + gControl *source; + + #if DEBUG_DND + fprintf(stderr, "\ncb_drag_drop: %s\n", data->name()); + #endif + + /*if (!gDrag::isCurrent(data)) + return false;*/ + + // cb_drag_leave() is automatically called when a drop occurs + //cb_drag_leave(widget, context, time, data); + + /*if (!data->_accept_drops) //acceptDrops()) + return false;*/ + + /*if (!CB_control_can_raise(data, gEvent_Drop)) + { + gtk_drag_finish(context, false, false, time); + return false; + }*/ + + source = gApplication::controlItem(gtk_drag_get_source_widget(context)); + + gDrag::setDropData(gDrag::getAction(), x, y, source, data); + + context = gDrag::enable(context, data, time); + + while (data) + { + if (CB_control_drop(data)) + break; + data = data->_proxy_for; + } + + context = gDrag::disable(context); + + //fprintf(stderr, "cancel = %d\n", cancel); + + gtk_drag_finish(context, true, false, time); + + //data->_drag_enter = false; + + return true; +} + +static void cb_show(GtkWidget *widget, gContainer *data) +{ + //fprintf(stderr, "cb_show: %s '%s'\n", GB.GetClassName(data->hFree), data->name()); + data->performArrange(); +} + +void gControl::borderSignals() +{ + GtkWidget *w; + + g_signal_connect_after(G_OBJECT(border), "destroy", G_CALLBACK(cb_destroy), (gpointer)this); + //g_signal_connect(G_OBJECT(border),"drag-data-received",G_CALLBACK(sg_drag_data_received),(gpointer)this); + //g_signal_connect(G_OBJECT(border),"enter-notify-event",G_CALLBACK(sg_enter),(gpointer)this); + //g_signal_connect(G_OBJECT(border),"leave-notify-event",G_CALLBACK(sg_enter),(gpointer)this); + + //g_signal_connect_after(G_OBJECT(border),"size-allocate",G_CALLBACK(sg_size),(gpointer)this); + + if (isContainer()) + g_signal_connect(G_OBJECT(border), "show", G_CALLBACK(cb_show), (gpointer)this); + + if (border != widget && !_scroll) + { + /*if (!_no_default_mouse_event) + { + g_signal_connect(G_OBJECT(border),"button-release-event",G_CALLBACK(gcb_button_release),(gpointer)this); + g_signal_connect(G_OBJECT(border),"button-press-event",G_CALLBACK(gcb_button_press),(gpointer)this); + }*/ + g_signal_connect(G_OBJECT(border), "popup-menu", G_CALLBACK(cb_menu), (gpointer)this); + //g_signal_connect_after(G_OBJECT(border),"motion-notify-event",G_CALLBACK(sg_motion),(gpointer)this); + //g_signal_connect(G_OBJECT(border),"scroll-event",G_CALLBACK(sg_scroll),(gpointer)this); + } + + w = _scroll ? widget : border; + + g_signal_connect(G_OBJECT(w), "drag-motion", G_CALLBACK(cb_drag_motion),(gpointer)this); + g_signal_connect(G_OBJECT(w), "drag-leave", G_CALLBACK(cb_drag_leave),(gpointer)this); + g_signal_connect(G_OBJECT(w), "drag-drop", G_CALLBACK(cb_drag_drop),(gpointer)this); + g_signal_connect(G_OBJECT(w), "drag-data-get", G_CALLBACK(cb_drag_data_get),(gpointer)this); + g_signal_connect(G_OBJECT(w), "drag-end", G_CALLBACK(cb_drag_end),(gpointer)this); +} + +void gControl::initSignals() +{ + borderSignals(); + + if (!(border != widget && !_scroll)) + { + //g_signal_connect(G_OBJECT(widget),"scroll-event",G_CALLBACK(sg_scroll),(gpointer)this); + /*if (!_no_default_mouse_event) + { + g_signal_connect(G_OBJECT(widget),"button-release-event",G_CALLBACK(gcb_button_release),(gpointer)this); + g_signal_connect(G_OBJECT(widget),"button-press-event",G_CALLBACK(gcb_button_press),(gpointer)this); + }*/ + //g_signal_connect(G_OBJECT(widget),"motion-notify-event",G_CALLBACK(sg_motion),(gpointer)this); + g_signal_connect(G_OBJECT(widget), "popup-menu", G_CALLBACK(cb_menu), (gpointer)this); + } + + g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(gcb_key_event), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(gcb_key_event), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "focus", G_CALLBACK(gcb_focus), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "focus-in-event", G_CALLBACK(gcb_focus_in), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "focus-out-event", G_CALLBACK(gcb_focus_out), (gpointer)this); + //g_signal_connect(G_OBJECT(widget),"event",G_CALLBACK(sg_event),(gpointer)this); + + /*if (widget != border) + { + g_signal_connect(G_OBJECT(widget), "drag-end", G_CALLBACK(cb_drag_end), (gpointer)this); + }*/ +} + diff --git a/gb.gtk/src/gslider.cpp b/gb.gtk/src/gslider.cpp new file mode 100644 index 00000000..c119031c --- /dev/null +++ b/gb.gtk/src/gslider.cpp @@ -0,0 +1,309 @@ +/*************************************************************************** + + gslider.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb.form.const.h" +#include "widgets.h" +#include "gdesktop.h" +#include "gapplication.h" +#include "gscrollbar.h" +#include "gslider.h" + +static void cb_change(GtkRange *wid, gSlider *data) +{ + int new_value = gtk_adjustment_get_value(gtk_range_get_adjustment(wid)); + + if (data->_value == new_value) + return; + + data->_value = new_value; + CB_slider_change(data); +} + +void gSlider::update() +{ + GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget)); + int value = _value; + int max; + + if (value < _min) + value = _min; + else if (value > _max) + value = _max; + + //gtk_range_set_adjustment(GTK_RANGE(widget), NULL); + + max = _max + _page_step; + /*if (!isScrollBar()) + { + #ifndef GTK3 + if (max == _min) + max = _min + 1; + #endif + }*/ + + gtk_adjustment_configure(adj, value, _min, max, _step, _page_step, _page_step); + + #ifndef GTK3 + gtk_range_set_update_policy(GTK_RANGE(widget), _tracking ? GTK_UPDATE_CONTINUOUS : GTK_UPDATE_DISCONTINUOUS); + #endif + + checkInverted(); +} + +void gSlider::init() +{ + //GtkAdjustment* adj = gtk_range_get_adjustment(GTK_RANGE(widget)); + + _use_wheel = true; + + g_signal_connect(widget, "value-changed", G_CALLBACK(cb_change), (gpointer)this); + //g_signal_connect(adj, "changed", G_CALLBACK(cb_change), (gpointer)this); +} + +gSlider::gSlider(gContainer *par, bool scrollbar) : gControl(par) +{ + _mark = false; + _step = 1; + _page_step = 10; + _value = 0; + _min = 0; + _max = 100; + _tracking = true; + _is_scrollbar = scrollbar; + _orientation = ORIENTATION_AUTO; + +/*#ifdef GTK3 + border = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); +#else + border = gtk_alignment_new(0, 0, 1, 1); +#endif*/ + + if (scrollbar) + return; + +#ifdef GTK3 + widget = gtk_scale_new(GTK_ORIENTATION_VERTICAL, NULL); + //gtk_widget_set_hexpand(widget, TRUE); +#else + widget = gtk_vscale_new(NULL); +#endif + gtk_scale_set_draw_value(GTK_SCALE(widget), false); + + init(); + update(); + realize(); + //g_signal_connect_after(G_OBJECT(border),"expose-event",G_CALLBACK(slider_Expose),(gpointer)this); +} + +gScrollBar::gScrollBar(gContainer *par) : gSlider(par, true) +{ +#ifdef GTK3 + widget = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL); +#else + widget = gtk_hscrollbar_new(NULL); +#endif + + init(); + update(); + realize(); + +#ifndef GTK3 + gtk_range_set_update_policy(GTK_RANGE(widget),GTK_UPDATE_CONTINUOUS); +#endif +} + +void gSlider::updateMark() +{ + int i; + int step; + + if (!_mark) + return; + + gtk_scale_clear_marks(GTK_SCALE(widget)); + + step = _page_step; + while (step < ((_max - _min) / 20)) + step *= 2; + + for (i = _min; i <= _max; i += step) + gtk_scale_add_mark(GTK_SCALE(widget), i, isVertical() ? GTK_POS_TOP : GTK_POS_RIGHT, NULL); +} + +void gSlider::setMark(bool vl) +{ + if (vl == _mark) return; + + _mark = vl; + gtk_scale_clear_marks(GTK_SCALE(widget)); + updateMark(); +} + +void gSlider::setStep(int vl) +{ + if (vl < 1) vl = 1; + if (vl == _step) return; + + _step = vl; + update(); + if (_mark) gtk_widget_queue_draw(widget); +} + +void gSlider::setPageStep(int vl) +{ + if (vl < 1) vl = 1; + if (vl == _page_step) return; + + _page_step = vl; + update(); + updateMark(); +} + +void gSlider::setMax(int vl) +{ + if (vl == _max) + return; + + _max = vl; + if (_min > _max) + _min = _max; + update(); + updateMark(); +} + +void gSlider::setMin(int vl) +{ + if (vl == _min) + return; + + _min = vl; + if (_min > _max) + _max = _min; + update(); + updateMark(); +} + +void gSlider::setTracking(bool vl) +{ + _tracking = vl; + update(); +} + +void gSlider::setValue(int vl) +{ + if (vl < _min) + vl = _min; + else if (vl > _max) + vl = _max; + + if (_value == vl) + return; + + _value = vl; + update(); + + CB_slider_change(this); +} + + +void gSlider::applyOrientation(GtkOrientation orientation) +{ + GtkOrientation old_orientation = gtk_orientable_get_orientation(GTK_ORIENTABLE(widget)); + int swap; + + if (orientation != old_orientation) + { + gtk_orientable_set_orientation(GTK_ORIENTABLE(widget), orientation); + + swap = _min_w; + _min_w = _min_h; + _min_h = swap; + } +} + +bool gSlider::resize(int w, int h, bool no_decide) +{ + if (gControl::resize(w, h, no_decide)) + return true; + + if (!_orientation) + applyOrientation((width() < height()) ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); + + return false; +} + +int gSlider::getDefaultSize() +{ + GtkRequisition req; + +#ifdef GTK3 + gtk_widget_get_preferred_size(widget, &req, NULL); +#else + gtk_widget_size_request(widget, &req); +#endif + + if (width() < height()) + return req.width; + else + return req.height; +} + +bool gSlider::isVertical() const +{ + return gtk_orientable_get_orientation(GTK_ORIENTABLE(widget)) == GTK_ORIENTATION_VERTICAL; +} + +void gSlider::checkInverted() +{ + gtk_range_set_inverted(GTK_RANGE(widget), !isVertical() && gDesktop::rightToLeft()); +} + +void gSlider::setOrientation(int vl) +{ + if (vl == _orientation) + return; + + _orientation = vl; + + switch(_orientation) + { + case ORIENTATION_HORIZONTAL: + case ORIENTATION_VERTICAL: + applyOrientation(_orientation == ORIENTATION_HORIZONTAL ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + break; + + default: + _orientation = ORIENTATION_AUTO; + applyOrientation((width() < height()) ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); + } +} + +void gSlider::setMinimumSize() +{ + gControl::setMinimumSize(); + if (!_is_scrollbar) + { + if (_min_w > (gDesktop::scale() * 4)) + _min_w = gDesktop::scale() * 4; + } +} diff --git a/gb.gtk/src/gslider.h b/gb.gtk/src/gslider.h new file mode 100644 index 00000000..8c2f2f89 --- /dev/null +++ b/gb.gtk/src/gslider.h @@ -0,0 +1,83 @@ +/*************************************************************************** + + gslider.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSLIDER_H +#define __GSLIDER_H + +class gSlider : public gControl +{ +public: + gSlider(gContainer *parent, bool scrollbar = false); + + bool isScrollBar() const { return _is_scrollbar; } + +//"Properties" + + int max() const { return _max; } + int min() const { return _min; } + bool tracking() const { return _tracking; } + int value() const { return _value; } + bool mark() const { return _mark; } + int step() const { return _step; } + int pageStep() const { return _page_step; } + int orientation() const { return _orientation; } + + //void setForeground(int vl); + //void setBackground(int vl); + void setMax(int vl); + void setMin(int vl); + void setTracking(bool vl); + void setValue(int vl); + void setMark(bool vl); + void setStep(int vl); + void setPageStep(int vl); + void setOrientation(int vl); + + int getDefaultSize(); + bool isVertical() const; + + virtual bool resize(int w, int h, bool no_decide); + void applyOrientation(GtkOrientation orientation); + +//"Private" + void updateMark(); + void init(); + void update(); + void checkInverted(); + virtual void setMinimumSize(); + + unsigned _mark : 1; + unsigned _tracking : 1; + unsigned _is_scrollbar : 1; + unsigned _orientation : 2; + + int _step; + int _page_step; + int _value; + int _min, _max; +}; + +// Callbacks +void CB_slider_change(gSlider *sender); + +#endif diff --git a/gb.gtk/src/gtabstrip.cpp b/gb.gtk/src/gtabstrip.cpp new file mode 100644 index 00000000..b33880db --- /dev/null +++ b/gb.gtk/src/gtabstrip.cpp @@ -0,0 +1,901 @@ +/*************************************************************************** + + gtabstrip.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gmouse.h" +#include "gdesktop.h" +#include "gtabstrip.h" + +static const struct { + guint width; + guint height; + guint bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ + guint8 pixel_data[16 * 16 * 4 + 1]; +} _close_button = { + 16, 16, 4, + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_a]\206`d_\344bd`x\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0__]v`d_\344ac_\204\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0bd`\335\261\264\254\377}\200z\356`b^\200\0\0\0\0\0\0" + "\0\0`b\\}z}w\355\260\263\253\377ce`\331\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0_a]v}\200z\356\272\275\265\377\201\203}\356`b^\200`b\\}z}w" + "\355\261\264\254\377|~y\355ad_n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0ac]~\203\205\200\356\300\304\274\377\206\211\203\356\200\202" + "}\355\261\264\254\377|~y\356`d^x\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0bd`\177\212\215\210\356\301\304\274\377\271" + "\274\264\377\201\204}\356ac_y\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0bd`}\213\215\210\355\303\306\276\377" + "\272\276\266\377\177\201|\355`c\\w\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0bd^}\204\206\201\355\307\313\303\377\212" + "\215\206\355\203\205\200\355\261\264\254\377z|v\355`c\\w\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0aa]v\200\202~\355\302\306\276\377" + "\205\207\202\356bd^\200`b^}{}w\355\261\264\254\377z|v\355`c`o\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0dfa\342\271\274\264\377\203\205\200\355" + "`d^\200\0\0\0\0\0\0\0\0`b\\}|~y\354\261\264\254\377bd^\340\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ab_\231cfa\341`b^z\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0^`\\wcfa\335`b^\222\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0", +}; + + + +cairo_surface_t *gTabStrip::_button_normal = NULL; +cairo_surface_t *gTabStrip::_button_disabled = NULL; + +static void cb_click(GtkNotebook *nb, GtkWidget *pg, guint pnum, gTabStrip *data) +{ + data->updateFont(); + data->performArrange(); + CB_tabstrip_click(data); +} + +static void cb_size_allocate(GtkWidget *wid, GtkAllocation *alloc, gTabStrip *data) +{ + if (wid == data->getContainer() && (alloc->width != data->_client_w || alloc->height != data->_client_h)) + { + GtkAllocation alloc_parent; + /*int tx, ty, px, py; + GdkWindow *win;*/ + + gtk_widget_get_allocation(data->widget, &alloc_parent); + + /*if (data->getScreenPos(&tx, &ty)) + return; + + win = gtk_widget_get_window(wid); + if (!win) + return; + + gdk_window_get_origin(win, &px, &py); + //fprintf(stderr, "alloc: tab = %d %d page = %d %d alloc = %d %d\n", tx, ty, px, py, alloc->x, alloc->y); + */ + + data->_client_x = alloc->x - alloc_parent.x; + data->_client_y = alloc->y - alloc_parent.y; + data->_client_w = alloc->width; + data->_client_h = alloc->height; + //data->performArrange(); + } +} + +#ifdef GTK3 +static gboolean cb_button_draw(GtkWidget *wid, cairo_t *cr, gTabStrip *data) +#else +static gboolean cb_button_expose(GtkWidget *wid, GdkEventExpose *e, gTabStrip *data) +#endif +{ +#ifndef GTK3 + cairo_t *cr; +#endif + cairo_surface_t *img; + GdkRectangle rpix = {0,0,0,0}; + GdkRectangle rect = {0,0}; + int py, px; + int dx, dy; + +#ifndef GTK3 + GTK_BUTTON(wid)->relief = GTK_RELIEF_NORMAL; +#endif + + gtk_widget_get_allocation(wid, &rect); +#ifdef GTK3 + rect.x = rect.y = 0; +#endif + px = rect.width; + +#ifdef GTK3 + if (gtk_widget_get_state_flags(data->widget) & GTK_STATE_FLAG_ACTIVE) +#else + if (GTK_WIDGET_STATE(data->widget) == GTK_STATE_ACTIVE) +#endif + { + gtk_widget_style_get (wid, + "child-displacement-x", &dx, + "child-displacement-y", &dy, + (void *)NULL); + rect.x += dx; + rect.y += dy; + } + +#ifdef GTK3 + if (gtk_widget_get_state_flags(data->widget) & GTK_STATE_FLAG_INSENSITIVE) +#else + if (GTK_WIDGET_STATE(data->widget) == GTK_STATE_INSENSITIVE) +#endif + img = data->_button_disabled; + else + img = data->_button_normal; + + rpix.width = cairo_image_surface_get_width(img); + rpix.height = cairo_image_surface_get_height(img); + + py = (rect.height - rpix.height) / 2; + + rect.x += (px - rpix.width) / 2; + rect.y += py; + +#ifndef GTK3 + cr = gdk_cairo_create(wid->window); +#endif + + cairo_set_source_surface(cr, img, rect.x, rect.y); + cairo_paint(cr); + +#ifndef GTK3 + cairo_destroy(cr); +#endif + + return false; +} + +static void cb_button_clicked(GtkWidget *wid, gTabStrip *data) +{ + CB_tabstrip_close(data, data->getRealIndex((GtkWidget *)g_object_get_data(G_OBJECT(wid), "gambas-tab-page"))); +} + +#ifdef GTK3 +static void cb_scroll(GtkWidget *wid, GdkEvent *event, gTabStrip *data) +{ + int dir = event->scroll.direction; + int page; + + if (dir == GDK_SCROLL_SMOOTH) + return; + + page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->widget)); + + switch (dir) + { + case GDK_SCROLL_UP: + case GDK_SCROLL_LEFT: + page--; + if (page >= 0) + gtk_notebook_set_current_page(GTK_NOTEBOOK(data->widget), page); + break; + case GDK_SCROLL_RIGHT: + case GDK_SCROLL_DOWN: default: + page++; + if (page < gtk_notebook_get_n_pages(GTK_NOTEBOOK(data->widget))) + gtk_notebook_set_current_page(GTK_NOTEBOOK(data->widget), page); + break; + } +} +#endif + +/**************************************************************************** + + gTabStripPage + +****************************************************************************/ + +class gTabStripPage +{ +public: + gTabStripPage(gTabStrip *tab); + ~gTabStripPage(); + + char *text() const; + void setText(char *text); + gPicture *picture() const { return _picture; } + void setPicture(gPicture *picture); + bool isVisible() const { return _visible; } + void setVisible(bool v); + bool enabled() const; + void setEnabled(bool v); + /*int count() const; + gControl *child(int n) const;*/ +#ifdef GTK3 + void updateStyleSheet(); + void updateColors() { updateStyleSheet(); } + void updateFont() { updateStyleSheet(); } +#else + void updateColors(); + void updateFont(); +#endif + void updateButton(); + + GtkWidget *fix; + GtkWidget *widget; + GtkWidget *label; + GtkWidget *image; + GtkWidget *hbox; + GtkWidget *_button; + + gPicture *_picture; + gTabStrip *parent; + bool _visible; + int index; +}; + +gTabStripPage::gTabStripPage(gTabStrip *tab) +{ + char text[16]; + + parent = tab; + + widget = gtk_fixed_new(); + + //fix = gtk_event_box_new(); + + hbox = gtk_hbox_new(false, gDesktop::scale() * 3 / 4); + fix = hbox; + //gtk_box_set_spacing(GTK_BOX(hbox), 4); + //gtk_container_add(GTK_CONTAINER(fix), hbox); + + image = gtk_image_new(); + //gtk_container_add(GTK_CONTAINER(hbox), image); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic(""); + //gtk_container_add(GTK_CONTAINER(hbox), label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + +#ifdef GTK3 + updateStyleSheet(); +#else + updateColors(); + updateFont(); +#endif + + g_signal_connect_after(G_OBJECT(widget), "size-allocate", G_CALLBACK(cb_size_allocate), (gpointer)parent); + + g_object_ref(widget); + g_object_ref(fix); + + #ifdef GTK3 + gt_patch_control(widget); + #endif + + _visible = false; + _picture = NULL; + + if (parent->count()) + index = parent->get(parent->count() - 1)->index + 1; + else + index = 0; + + gtk_widget_show(label); + gtk_widget_hide(image); + + _button = NULL; + updateButton(); + + sprintf(text, "Tab %d", index); + setText(text); + + setVisible(true); +} + +gTabStripPage::~gTabStripPage() +{ + setVisible(false); + gPicture::assign(&_picture); + g_object_unref(fix); + g_object_unref(widget); +} + +#ifdef GTK3 + +void gTabStripPage::updateStyleSheet() +{ + gt_widget_update_css(widget, NULL, parent->background(), COLOR_DEFAULT); + gt_widget_update_css(label, parent->textFont(), COLOR_DEFAULT, COLOR_DEFAULT); +} + +#else + +void gTabStripPage::updateColors() +{ + set_gdk_bg_color(widget, parent->realBackground()); + set_gdk_fg_color(label, parent->realForeground()); +} + +void gTabStripPage::updateFont() +{ + PangoFontDescription *desc = NULL; + gFont *fnt = parent->textFont(); + + if (!fnt) + fnt = parent->font(); + + if (fnt) + desc = fnt->desc(); + + gtk_widget_modify_font(widget, desc); + gtk_widget_modify_font(label, desc); +} + +#endif + +void gTabStripPage::setText(char *text) +{ + char *buf; + + gMnemonic_correctText(text, &buf); + gtk_label_set_text_with_mnemonic(GTK_LABEL(label), buf); + g_free(buf); +} + +char *gTabStripPage::text() const +{ + char *buf; + + gMnemonic_returnText((char*)gtk_label_get_text(GTK_LABEL(label)), &buf); + gt_free_later(buf); + return buf; +} + +void gTabStripPage::setPicture(gPicture *picture) +{ + GdkPixbuf *buf; + + gPicture::assign(&_picture, picture); + + buf = _picture ? _picture->getPixbuf() : NULL; + + if (!buf) + { + gtk_image_clear(GTK_IMAGE(image)); + gtk_widget_hide(image); + } + else + { + gtk_image_set_from_pixbuf(GTK_IMAGE(image), buf); + gtk_widget_show(image); + } +} + +bool gTabStripPage::enabled() const +{ +#if GTK_CHECK_VERSION(2, 18, 0) + return gtk_widget_get_sensitive(hbox); +#else + return GTK_WIDGET_SENSITIVE(hbox); +#endif +} + +void gTabStripPage::setEnabled(bool v) +{ + gtk_widget_set_sensitive(label, v); + gtk_widget_set_sensitive(image, v); + gtk_widget_set_sensitive(widget, v); +} + +void gTabStripPage::setVisible(bool v) +{ + int ind, n; + gTabStripPage *page; + + if (_visible == v) + return; + + _visible = v; + + if (v) + { + n = 0; + for (ind = 0; ind < parent->count(); ind++) + { + page = parent->get(ind); + if (index <= page->index) + break; + if (page->isVisible()) + n++; + } + gtk_notebook_insert_page(GTK_NOTEBOOK(parent->widget), widget, fix, n); + //gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(parent->widget), widget, TRUE); + gtk_widget_realize(widget); + gtk_widget_realize(fix); + gtk_widget_show_all(widget); + gtk_widget_show(fix); + //gtk_container_resize_children(GTK_CONTAINER(gtk_widget_get_parent(widget))); + //gtk_container_resize_children(GTK_CONTAINER(gtk_widget_get_parent(gtk_widget_get_parent(widget)))); + } + else + { + ind = gtk_notebook_page_num(GTK_NOTEBOOK(parent->widget), widget); + gtk_notebook_remove_page(GTK_NOTEBOOK(parent->widget), ind); + } +} + +#ifndef GTK3 +static gboolean cb_button_fix(GtkWidget *wid, GdkEvent *e, gTabStripPage *data) +{ + GTK_BUTTON(wid)->relief = GTK_RELIEF_NONE; + return false; +} +#endif + +void gTabStripPage::updateButton() +{ + bool v = parent->isClosable(); + + if (v && !_button) + { + _button = gtk_button_new(); + gt_set_focus_on_click(_button, false); + +#ifdef GTK3 + gtk_button_set_relief(GTK_BUTTON(_button), GTK_RELIEF_NONE); +#else + g_signal_connect(G_OBJECT(_button), "expose-event", G_CALLBACK(cb_button_fix), (gpointer)this); +#endif + + //g_signal_connect_after(G_OBJECT(_button), "expose-event", G_CALLBACK(cb_button_expose), (gpointer)parent); + ON_DRAW(_button, parent, cb_button_expose, cb_button_draw); + g_signal_connect(G_OBJECT(_button), "clicked", G_CALLBACK(cb_button_clicked), (gpointer)parent); + g_object_set_data(G_OBJECT(_button), "gambas-tab-page", (void *)widget); + + gtk_widget_show(_button); + + gtk_box_pack_start(GTK_BOX(hbox), _button, FALSE, FALSE, 0); + } + else if (!v && _button) + { + gtk_widget_destroy(_button); + _button = NULL; + } + + if (_button) + { + gtk_widget_set_size_request(_button, 20, 20); + } +} + + +/**************************************************************************** + + gTabStrip + +****************************************************************************/ + +gTabStrip::gTabStrip(gContainer *parent) : gContainer(parent) +{ + _pages = g_ptr_array_new(); + _textFont = NULL; + _closable = false; + _no_design = true; + + //border = gtk_event_box_new(); + //gtk_event_box_set_visible_window(GTK_EVENT_BOX(border), false); + + border = widget = gtk_notebook_new(); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(widget), TRUE); + gtk_drag_dest_unset(widget); + + realize(); + + gtk_widget_add_events(border, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK); + + setCount(1); + + g_signal_connect_after(G_OBJECT(widget), "switch-page", G_CALLBACK(cb_click), (gpointer)this); +#ifdef GTK3 + g_signal_connect(G_OBJECT(widget), "scroll-event", G_CALLBACK(cb_scroll), (gpointer)this); +#endif +} + +gTabStrip::~gTabStrip() +{ + // Do not use setCount() or removeTab(), because they cannot remove non-empty tabs + lock(); + while (count()) + destroyTab(count() - 1); + unlock(); + gFont::assign(&_textFont); + setClosable(false); + g_ptr_array_free(_pages, TRUE); +} + + +int gTabStrip::getRealIndex(GtkWidget *page) const +{ + int i; + + for (i = 0; i < count(); i++) + { + if (get(i)->widget == page) + return i; + } + + return -1; +} + +int gTabStrip::index() const +{ + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), gtk_notebook_get_current_page(GTK_NOTEBOOK(widget))); + return getRealIndex(page); +} + +void gTabStrip::setIndex(int vl) +{ + GtkWidget *page; + + if ((vl < 0) || (vl >= count()) || !get(vl)->isVisible()) + return; + + page = get(vl)->widget; + gtk_notebook_set_current_page(GTK_NOTEBOOK(widget), getRealIndex(page)); + //widget = get(vl)->widget; +} + +int gTabStrip::orientation() const +{ + return gtk_notebook_get_tab_pos(GTK_NOTEBOOK(widget)); +} + +void gTabStrip::destroyTab(int ind) +{ + delete (gTabStripPage *)g_ptr_array_index(_pages, ind); + g_ptr_array_remove_index(_pages, ind); +} + +bool gTabStrip::removeTab(int ind) +{ + gTabStripPage *page = get(ind); + + if (!page || tabCount(ind)) + return true; + + destroyTab(ind); + return false; +} + +bool gTabStrip::setCount(int vl) +{ + int i, ind; + + if (vl == count()) + return false; + + ind = index(); + + if (vl > count()) + { + lock(); + while (vl > count()) + g_ptr_array_add(_pages, (gpointer)new gTabStripPage(this)); + setIndex(count() - 1); + unlock(); + setMinimumSize(); + } + + if (vl < count()) + { + //GList *chd,*iter,*ok=NULL; + /*chd=gtk_container_get_children(GTK_CONTAINER(border)); + iter=chd; + while (iter) + { + if (gtk_notebook_page_num(GTK_NOTEBOOK(border),GTK_WIDGET(iter->data))>=vl) + { + ok=gtk_container_get_children(GTK_CONTAINER(iter->data)); + if (ok) break; + } + iter=g_list_next(iter); + } + g_list_free(chd); + if (ok) return -1;*/ + + for (i = vl; i < count(); i++) + { + if (tabCount(i)) + return true; + } + + lock(); + while (vl < count()) + removeTab(count() - 1); + unlock(); + } + + if (ind != index()) + CB_tabstrip_click(this); + + return false; +} + +void gTabStrip::setOrientation(int vl) +{ + _client_x = -1; + _client_y = -1; + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(widget), GtkPositionType(vl)); +} + +gPicture* gTabStrip::tabPicture(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return NULL; + else + return get(ind)->picture(); +} + +void gTabStrip::setTabPicture(int ind,gPicture *pic) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setPicture(pic); +} + +bool gTabStrip::tabEnabled(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return FALSE; + else + return get(ind)->enabled(); +} + +void gTabStrip::setTabEnabled(int ind, bool vl) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setEnabled(vl); +} + +bool gTabStrip::tabVisible(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return FALSE; + else + return get(ind)->isVisible(); +} + +void gTabStrip::setTabVisible(int ind, bool vl) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setVisible(vl); +} + +char* gTabStrip::tabText(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return NULL; + else + return get(ind)->text(); +} + +void gTabStrip::setTabText(int ind, char *txt) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setText(txt); +} + +int gTabStrip::tabCount(int ind) const +{ + int i; + gControl *ch; + int ct = 0; + + if ((ind < 0) || (ind >= count())) + return 0; + + for (i = 0; i < gContainer::childCount(); i++) + { + ch = gContainer::child(i); + if (gtk_widget_get_parent(ch->border) == get(ind)->widget) + ct++; + } + + return ct; +} + +gControl *gTabStrip::tabChild(int ind, int n) const +{ + int i; + gControl *ch; + int ct = 0; + + if ((ind < 0) || (ind >= count())) + return NULL; + + for (i = 0; i < gContainer::childCount(); i++) + { + ch = gContainer::child(i); + if (gtk_widget_get_parent(ch->border) == get(ind)->widget) + { + if (ct == n) + return ch; + ct++; + } + } + + return NULL; +} + +int gTabStrip::childCount() const +{ + return tabCount(index()); +} + +gControl *gTabStrip::child(int ind) const +{ + return tabChild(index(), ind); +} + +GtkWidget *gTabStrip::getContainer() +{ + gTabStripPage *page = get(index()); + + if (page) + return page->widget; + else + return NULL; +} + +#ifdef GTK3 + +void gTabStrip::customStyleSheet(GString *css) +{ + gColor bg = background(); + if (bg == COLOR_DEFAULT) + return; + + setStyleSheetNode(css, " > header"); + gt_css_add_color(css, bg, COLOR_DEFAULT); + setStyleSheetNode(css, " > header tab:checked"); + gt_css_add_color(css, bg, COLOR_DEFAULT); +} + +void gTabStrip::updateColor() +{ + for (int i = 0; i < count(); i++) + get(i)->updateColors(); +} +#else +void gTabStrip::setRealBackground(gColor color) +{ + gControl::setRealBackground(color); + + for (int i = 0; i < count(); i++) + get(i)->updateColors(); +} +#endif + +void gTabStrip::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + + for (int i = 0; i < count(); i++) + get(i)->updateColors(); +} + +gTabStripPage *gTabStrip::get(int ind) const +{ + if (ind < 0 || ind >= count()) + return NULL; + else + return (gTabStripPage *)g_ptr_array_index(_pages, ind); +} + +void gTabStrip::updateFont() +{ + int i; + + gContainer::updateFont(); + + for (i = 0; i < count(); i++) + get(i)->updateFont(); +} + +gFont *gTabStrip::textFont() +{ + return _textFont; +} + +void gTabStrip::setTextFont(gFont *font) +{ + gFont::assign(&_textFont, font); + updateFont(); +} + +void gTabStrip::setClosable(bool v) +{ + int i; + + if (v == _closable) + return; + + _closable = v; + + if (v) + { + if (!_button_normal) + { + GdkPixbuf *normal, *disabled; + + normal = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "window-close", 16, GTK_ICON_LOOKUP_USE_BUILTIN, NULL); + if (!normal) + { + normal = gdk_pixbuf_new_from_data(_close_button.pixel_data, GDK_COLORSPACE_RGB, TRUE, 8, + _close_button.width, _close_button.height, + _close_button.width * sizeof(int), NULL, NULL); + } + + disabled = gt_pixbuf_create_disabled(normal); + + _button_normal = gt_cairo_create_surface_from_pixbuf(normal); + _button_disabled = gt_cairo_create_surface_from_pixbuf(disabled); + + g_object_unref(normal); + g_object_unref(disabled); + + } + } + + for (i = 0; i < count(); i++) + get(i)->updateButton(); + +} + +int gTabStrip::findIndex(gControl *child) const +{ + int i; + GtkWidget *page; + + page = gtk_widget_get_parent(child->border); + + for (i = 0; i < count(); i++) + { + if (get(i)->widget == page) + return i; + } + + return -1; +} + +void gTabStrip::setMinimumSize() +{ + _min_w = gDesktop::scale() * 6; + _min_h = _min_w; +} + diff --git a/gb.gtk/src/gtabstrip.h b/gb.gtk/src/gtabstrip.h new file mode 100644 index 00000000..f342399c --- /dev/null +++ b/gb.gtk/src/gtabstrip.h @@ -0,0 +1,98 @@ +/*************************************************************************** + + gtabstrip.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTABSTRIP_H +#define __GTABSTRIP_H + +class gTabStripPage; + +class gTabStrip : public gContainer +{ + friend class gTabStripPage; + +public: + gTabStrip(gContainer *parent); + ~gTabStrip(); + +//"Properties" + int count() const { return _pages->len; } + int index() const; + int orientation() const; + void setOrientation(int vl); + bool tabEnabled(int ind) const; + gPicture* tabPicture(int ind) const; + bool tabVisible(int ind) const; + char *tabText(int ind) const; + int tabCount(int ind) const; + gControl *tabChild(int ind, int n) const; + int findIndex(gControl *child) const; + + bool setCount(int vl); + void setIndex(int vl); + void setTabPicture(int ind, gPicture *pic); + void setTabEnabled(int ind, bool vl); + void setTabText(int ind, char *txt); + void setTabVisible(int ind, bool vl); + bool removeTab(int ind); + void setClosable(bool v); + bool isClosable() const { return _closable; } + + virtual int childCount() const; + virtual gControl *child(int index) const; + +#ifdef GTK3 + virtual void customStyleSheet(GString *css); + virtual void updateColor(); +#else + virtual void setRealBackground(gColor color); +#endif + virtual void setRealForeground(gColor color); + virtual void updateFont(); + + gFont *textFont(); + void setTextFont(gFont *ft); + + virtual void setMinimumSize(); + +//"Private" + virtual GtkWidget *getContainer(); + int getRealIndex(GtkWidget *page) const; + + static cairo_surface_t *_button_normal; + static cairo_surface_t *_button_disabled; + +private: + bool _closable; + GPtrArray *_pages; + gFont *_textFont; + gTabStripPage *get(int ind) const; + void destroyTab(int ind); +}; + +// Callbacks +void CB_tabstrip_click(gTabStrip *sender); +void CB_tabstrip_close(gTabStrip *sender, int index); + + + +#endif diff --git a/gb.gtk/src/gtag.h b/gb.gtk/src/gtag.h new file mode 100644 index 00000000..4fa8b7ed --- /dev/null +++ b/gb.gtk/src/gtag.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gtag.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTAG_H +#define __GTAG_H + +class gTag +{ +public: + gTag() {} + gTag(void *v) { value = v; } + virtual ~gTag() {} + virtual void ref(void *v) {}; + virtual void unref(void *v) {}; + void *get() { return value; } + void ref() { ref(value); } + void unref() { unref(value); } +private: + void *value; +}; + +#endif diff --git a/gb.gtk/src/gtextarea.cpp b/gb.gtk/src/gtextarea.cpp new file mode 100644 index 00000000..1b281967 --- /dev/null +++ b/gb.gtk/src/gtextarea.cpp @@ -0,0 +1,1166 @@ +/*************************************************************************** + + gtextarea.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gclipboard.h" +#include "gdesktop.h" +#include "gkey.h" +#include "gtextarea.h" + +#ifdef GTK3 + +struct _GtkTextViewPrivate +{ + void *layout; + GtkTextBuffer *buffer; + + guint blink_time; /* time in msec the cursor has blinked since last user event */ + guint im_spot_idle; + gchar *im_module; + GdkDevice *grab_device; + GdkDevice *dnd_device; + + gulong selection_drag_handler; + void *text_handle; + GtkWidget *selection_bubble; + guint selection_bubble_timeout_id; + + GtkWidget *magnifier_popover; + GtkWidget *magnifier; + + void *text_window; + void *left_window; + void *right_window; + void *top_window; + void *bottom_window; + + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + gint xoffset; /* Offsets between widget coordinates and buffer coordinates */ + gint yoffset; + gint width; /* Width and height of the buffer */ + gint height; + + /* This is used to monitor the overall size request + * and decide whether we need to queue resizes when + * the buffer content changes. + * + * FIXME: This could be done in a simpler way by + * consulting the above width/height of the buffer + some + * padding values, however all of this request code needs + * to be changed to use GtkWidget Iface and deserves + * more attention. + */ + GtkRequisition cached_size_request; + + /* The virtual cursor position is normally the same as the + * actual (strong) cursor position, except in two circumstances: + * + * a) When the cursor is moved vertically with the keyboard + * b) When the text view is scrolled with the keyboard + * + * In case a), virtual_cursor_x is preserved, but not virtual_cursor_y + * In case b), both virtual_cursor_x and virtual_cursor_y are preserved. + */ + gint virtual_cursor_x; /* -1 means use actual cursor position */ + gint virtual_cursor_y; /* -1 means use actual cursor position */ + + GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */ + gint first_para_pixels; /* Offset of top of screen in the first onscreen paragraph */ + + guint blink_timeout; + guint scroll_timeout; + + guint first_validate_idle; /* Idle to revalidate onscreen portion, runs before resize */ + guint incremental_validate_idle; /* Idle to revalidate offscreen portions, runs after redraw */ + + gint pending_place_cursor_button; + + GtkTextMark *dnd_mark; + + GtkIMContext *im_context; + GtkWidget *popup_menu; + + gint drag_start_x; + gint drag_start_y; + + GSList *children; + + void *pending_scroll; + + void *pixel_cache; + + /* Default style settings */ + gint pixels_above_lines; + gint pixels_below_lines; + gint pixels_inside_wrap; + GtkWrapMode wrap_mode; + GtkJustification justify; + gint left_margin; + gint right_margin; + gint indent; + PangoTabArray *tabs; + guint editable : 1; + + guint overwrite_mode : 1; + guint cursor_visible : 1; + + /* if we have reset the IM since the last character entered */ + guint need_im_reset : 1; + + guint accepts_tab : 1; + + guint width_changed : 1; + + /* debug flag - means that we've validated onscreen since the + * last "invalidate" signal from the layout + */ + guint onscreen_validated : 1; + + guint mouse_cursor_obscured : 1; + + guint scroll_after_paste : 1; + + /* GtkScrollablePolicy needs to be checked when + * driving the scrollable adjustment values */ + guint hscroll_policy : 1; + guint vscroll_policy : 1; + guint cursor_handle_dragged : 1; + guint selection_handle_dragged : 1; + guint populate_all : 1; + + guint in_scroll : 1; +}; + +#define TEXT_AREA(_textview) (gtk_text_view_get_window(GTK_TEXT_VIEW(_textview), GTK_TEXT_WINDOW_TEXT)) + +#else + +// Private structure took from GTK+ 2.10 code. Used for setting the text area cursor. +// **** May not work on future GTK+ versions **** + +struct PrivateGtkTextWindow +{ + GtkTextWindowType type; + GtkWidget *widget; + GdkWindow *window; + GdkWindow *bin_window; + GtkRequisition requisition; + GdkRectangle allocation; +}; + +// Private exported functions to get the size of the text +// **** May not work on future GTK+ versions **** + +extern "C" { +void gtk_text_layout_set_screen_width(struct _GtkTextLayout *layout, gint width); +void gtk_text_layout_get_size(struct _GtkTextLayout *layout, gint *width, gint *height); +void gtk_text_layout_invalidate (struct _GtkTextLayout *layout, const GtkTextIter *start, const GtkTextIter *end); +void gtk_text_layout_validate (struct _GtkTextLayout *layout, gint max_pixels); +} + +#define TEXT_AREA(_textview) (((PrivateGtkTextWindow *)GTK_TEXT_VIEW(_textview)->text_window)->bin_window) + +#endif + +#if !GLIB_CHECK_VERSION(2, 32, 0) +/** + * g_signal_handlers_disconnect_by_data: + * @instance: The instance to remove handlers from + * @data: the closure data of the handlers' closures + * + * Disconnects all handlers on an instance that match @data. + * + * Returns: The number of handlers that matched. + * + * Since: 2.32 + */ +#define g_signal_handlers_disconnect_by_data(instance, data) \ + g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (data)) +#endif + +// Undo/Redo actions + +enum { ACTION_VOID, ACTION_INSERT, ACTION_DELETE }; + +class gTextAreaAction +{ +public: + gTextAreaAction *prev; + gTextAreaAction *next; + GString *text; + int length; + int start; + int end; + unsigned mergeable : 1; + unsigned delete_key_used : 1; + unsigned type : 2; + + gTextAreaAction(); + ~gTextAreaAction(); + static gTextAreaAction *insertAction(GtkTextBuffer *buffer, char *text, int length, GtkTextIter *where); + static gTextAreaAction *deleteAction(GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end); + bool canBeMerged(gTextAreaAction *prev); + void addText(char *text, int length); +}; + +gTextAreaAction::gTextAreaAction() +{ + prev = next = NULL; + text = NULL; + length = start = end = 0; + mergeable = delete_key_used = false; + type = ACTION_VOID; +} + +gTextAreaAction::~gTextAreaAction() +{ + if (text) + g_string_free(text, TRUE); +} + +gTextAreaAction *gTextAreaAction::insertAction(GtkTextBuffer *buffer, char *text, int len, GtkTextIter *where) +{ + gTextAreaAction *action = new gTextAreaAction; + + action->type = ACTION_INSERT; + action->start = gtk_text_iter_get_offset(where); + action->text = g_string_new_len(text, len); + action->length = g_utf8_strlen(text, len); + + action->mergeable = (len == 1) && *text != '\r' && *text != '\n' && *text != ' '; + + return action; +} + +gTextAreaAction *gTextAreaAction::deleteAction(GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end) +{ + GtkTextIter insert_iter; + int insert_offset; + GtkTextMark *mark; + gTextAreaAction *action = new gTextAreaAction; + char *text; + + action->type = ACTION_DELETE; + text = gtk_text_buffer_get_text(buffer, start, end, FALSE); + action->text = g_string_new(text); + action->length = g_utf8_strlen(action->text->str, action->text->len); + g_free(text); + action->start = gtk_text_iter_get_offset(start); + action->end = gtk_text_iter_get_offset(end); + + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter, mark); + insert_offset = gtk_text_iter_get_offset(&insert_iter); + + action->delete_key_used = insert_offset < action->start; + + action->mergeable = (action->length == 1) && *(action->text->str) != '\r' && *(action->text->str) != '\n' && *(action->text->str) != ' '; + + return action; +} + +bool gTextAreaAction::canBeMerged(gTextAreaAction *prev) +{ + if (!prev || prev->type != type) + return false; + + if (!mergeable || !prev->mergeable) + return false; + + if (type == ACTION_INSERT) + { + if (start != (prev->start + prev->length)) + return false; + if (isspace(*text->str) != isspace(*prev->text->str)) + return false; + } + else if (type == ACTION_DELETE) + { + if (prev->delete_key_used != delete_key_used) + return false; + if (prev->start != start && prev->start != end) + return false; + if (isspace(*text->str) != isspace(*prev->text->str)) + return false; + } + else + return false; + + return true; +} + +void gTextAreaAction::addText(char *add, int len) +{ + g_string_append_len(text, add, len); + length += g_utf8_strlen(add, len); +} + + +// Callbacks + +/*static gboolean cb_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gTextArea *data) +{ + // Workaround GtkTextView internal cursor changes + if (gApplication::isBusy()) + data->setMouse(data->mouse()); + return FALSE; +}*/ + +static void cb_changed(GtkTextBuffer *buf, gTextArea *data) +{ + data->updateFixSpacing(); + CB_textarea_change(data); +} + +static void cb_mark_set(GtkTextBuffer *buf, GtkTextIter *location, GtkTextMark *mark, gTextArea *data) +{ + //if (mark == gtk_text_buffer_get_insert(buf)) + data->emitCursor(); +} + +static void cb_insert_text(GtkTextBuffer *buf, GtkTextIter *location, gchar *text, gint len, gTextArea *ctrl) +{ + gTextAreaAction *action, *prev; + + if (gKey::gotCommit()) + { + gcb_im_commit(NULL, text, ctrl); + if (gKey::canceled()) + { + g_signal_stop_emission_by_name(G_OBJECT(buf), "insert-text"); + return; + } + } + + if (!ctrl->_undo_in_progress) + ctrl->clearRedoStack(); + + if (ctrl->_not_undoable_action) + return; + + action = gTextAreaAction::insertAction(buf, text, len, location); + + prev = ctrl->_undo_stack; + if (action->canBeMerged(prev)) + { + prev->addText(action->text->str, action->length); + //fprintf(stderr, "insert merge: %d '%.*s'\n", prev->start, prev->text->len, prev->text->str); + delete action; + } + else + { + //fprintf(stderr, "insert: %d '%.*s'\n", action->start, action->text->len, action->text->str); + action->next = ctrl->_undo_stack; + if (ctrl->_undo_stack) + ctrl->_undo_stack->prev = action; + ctrl->_undo_stack = action; + } +} + +static void cb_delete_range(GtkTextBuffer *buf, GtkTextIter *start, GtkTextIter *end, gTextArea *ctrl) +{ + gTextAreaAction *action, *prev; + + if (!ctrl->_undo_in_progress) + ctrl->clearRedoStack(); + + if (ctrl->_not_undoable_action) + return; + + action = gTextAreaAction::deleteAction(buf, start, end); + + prev = ctrl->_undo_stack; + if (action->canBeMerged(prev)) + { + if (prev->start == action->start) + { + prev->addText(action->text->str, action->length); + prev->end += action->end - action->start; + } + else + { + GString *text = prev->text; + prev->text = action->text; + action->text = NULL; + prev->addText(text->str, text->len); + g_string_free(text, TRUE); + prev->start = action->start; + } + //fprintf(stderr, "delete merge: %d %d '%.*s'\n", prev->start, prev->end, prev->text->len, prev->text->str); + delete action; + } + else + { + //fprintf(stderr, "delete: %d %d '%.*s'\n", action->start, action->end, action->text->len, action->text->str); + action->next = ctrl->_undo_stack; + if (ctrl->_undo_stack) + ctrl->_undo_stack->prev = action; + ctrl->_undo_stack = action; + } +} + +static gboolean cb_keypress(GtkWidget *widget, GdkEvent *event, gTextArea *ctrl) +{ + if (event->key.state & GDK_CONTROL_MASK) + { + int key = gdk_keyval_to_unicode(gdk_keyval_to_upper(event->key.keyval)); + + if (!ctrl->readOnly()) + { + if (key == 'Z') + { + ctrl->undo(); + return true; + } + else if (key == 'Y') + { + ctrl->redo(); + return true; + } + else if (key == 'X') + { + ctrl->cut(); + ctrl->ensureVisible(); + return true; + } + else if (key == 'V') + { + ctrl->paste(); + ctrl->ensureVisible(); + return true; + } + } + + if (key == 'A') + { + ctrl->selectAll(); + return true; + } + else if (key == 'C') + { + ctrl->copy(); + return true; + } + } + + return false; +} + +// TextArea + +gTextArea::gTextArea(gContainer *parent) : gControl(parent) +{ + _align_normal = false; + _last_pos = -1; + + have_cursor = true; + _undo_stack = NULL; + _redo_stack = NULL; + _not_undoable_action = 0; + _undo_in_progress = false; + _has_input_method = true; + _use_wheel = true; + _fix_spacing_tag = NULL; + _has_native_popup = true; + _eat_return_key = true; + _text_area_visible = false; + _no_background = true; + + textview = gtk_text_view_new(); + realizeScrolledWindow(textview); + + setColorBase(); + + //g_signal_connect_after(G_OBJECT(textview), "motion-notify-event", G_CALLBACK(cb_motion_notify_event), (gpointer)this); + g_signal_connect(G_OBJECT(textview), "key-press-event", G_CALLBACK(cb_keypress), (gpointer)this); + + _buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); + g_signal_connect_after(G_OBJECT(_buffer), "changed", G_CALLBACK(cb_changed), (gpointer)this); + g_signal_connect_after(G_OBJECT(_buffer), "mark-set", G_CALLBACK(cb_mark_set), (gpointer)this); + g_signal_connect(G_OBJECT(_buffer), "insert-text", G_CALLBACK(cb_insert_text), (gpointer)this); + g_signal_connect(G_OBJECT(_buffer), "delete-range", G_CALLBACK(cb_delete_range), (gpointer)this); + + /*gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 2); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 2);*/ + + setBorder(true); + setWrap(false); +} + +void gTextArea::clearUndoStack() +{ + gTextAreaAction *action; + + while (_undo_stack) + { + action = _undo_stack; + _undo_stack = _undo_stack->next; + delete action; + } +} + +void gTextArea::clearRedoStack() +{ + gTextAreaAction *action; + + while (_redo_stack) + { + action = _redo_stack; + _redo_stack = _redo_stack->next; + delete action; + } +} + +gTextArea::~gTextArea() +{ + g_signal_handlers_disconnect_by_data(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)), this); + clearRedoStack(); + clearUndoStack(); +} + +char *gTextArea::text() const +{ + GtkTextIter start; + GtkTextIter end; + char *text; + + gtk_text_buffer_get_bounds(_buffer, &start, &end); + + text = gtk_text_buffer_get_text(_buffer, &start, &end, true); + gt_free_later(text); + return text; +} + +void gTextArea::setText(const char *txt, int len) +{ + if (!txt) + { + txt = ""; + len = 0; + } + + _last_pos = -1; + begin(); + gtk_text_buffer_set_text(_buffer, (const gchar *)txt, len); + end(); + refresh(); +} + +bool gTextArea::readOnly() const +{ + return !gtk_text_view_get_editable(GTK_TEXT_VIEW(textview)); +} + +void gTextArea::setReadOnly(bool vl) +{ + gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), !vl); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview), !vl); + _eat_return_key = !vl; +} + +GtkTextIter *gTextArea::getIterAt(int pos) const +{ + static GtkTextIter iter; + + if (pos < 0) + gtk_text_buffer_get_iter_at_mark(_buffer, &iter, gtk_text_buffer_get_insert(_buffer)); + else + gtk_text_buffer_get_iter_at_offset(_buffer, &iter, pos); + + return &iter; +} + +int gTextArea::line() const +{ + return gtk_text_iter_get_line(getIterAt()); +} + +void gTextArea::setLine(int vl) +{ + int col = column(); + GtkTextIter *iter = getIterAt(); + + if (vl < 0) + { + setPosition(0); + return; + } + else if (vl >= gtk_text_buffer_get_line_count(_buffer)) + { + setPosition(length()); + return; + } + + gtk_text_iter_set_line(iter, vl); + if (gtk_text_iter_get_chars_in_line(iter) <= col) + col = gtk_text_iter_get_chars_in_line(iter) - 1; + gtk_text_iter_set_line_offset(iter, col); + gtk_text_buffer_place_cursor(_buffer, iter); + ensureVisible(); +} + +int gTextArea::column() const +{ + return gtk_text_iter_get_line_offset(getIterAt()); +} + +void gTextArea::setColumn(int vl) +{ + GtkTextIter *iter = getIterAt(); + + if (vl < 0) + { + vl = gtk_text_iter_get_chars_in_line(iter)-1; + } + else + { + if (gtk_text_iter_get_chars_in_line (iter) <= vl) + vl = gtk_text_iter_get_chars_in_line(iter) - 1; + } + + gtk_text_iter_set_line_offset(iter,vl); + gtk_text_buffer_place_cursor(_buffer, iter); + ensureVisible(); +} + +int gTextArea::position() const +{ + return gtk_text_iter_get_offset(getIterAt()); +} + +void gTextArea::setPosition(int vl) +{ + GtkTextIter *iter = getIterAt(); + + gtk_text_iter_set_offset(iter, vl); + gtk_text_buffer_place_cursor(_buffer, iter); + ensureVisible(); +} + +int gTextArea::length() const +{ + GtkTextIter iter; + + gtk_text_buffer_get_end_iter(_buffer, &iter); + return gtk_text_iter_get_offset(&iter); +} + +bool gTextArea::wrap() const +{ + return (gtk_text_view_get_wrap_mode(GTK_TEXT_VIEW(textview)) != GTK_WRAP_NONE); +} + +void gTextArea::setWrap(bool vl) +{ + if (vl) + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR); + else + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_NONE); +} + +/********************************************************************************** + +gTextArea methods + +***********************************************************************************/ + +void gTextArea::copy() +{ + gtk_text_buffer_copy_clipboard(_buffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); +} + +void gTextArea::cut() +{ + gtk_text_buffer_cut_clipboard(_buffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), true); +} + +void gTextArea::ensureVisible() +{ + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textview), gtk_text_buffer_get_insert(_buffer)); +} + +void gTextArea::paste() +{ + char *txt; + int len; + + if (gClipboard::getType() != gClipboard::Text) + return; + + txt = gClipboard::getText(&len, "text/plain"); + if (txt) + { + gtk_text_buffer_insert_at_cursor(_buffer, (const gchar *)txt, len); + refresh(); + } +} + +void gTextArea::insert(const char *txt) +{ + if (!txt || !*txt) + return; + + gtk_text_buffer_insert_at_cursor(_buffer, (const gchar *)txt, -1); + refresh(); +} + +int gTextArea::toLine(int pos) const +{ + if (pos < 0) + pos=0; + else if (pos > length()) + pos = length(); + + return gtk_text_iter_get_line(getIterAt(pos)); +} + +int gTextArea::toColumn(int pos) const +{ + if (pos < 0) + pos=0; + else if (pos > length()) + pos = length(); + + return gtk_text_iter_get_line_offset(getIterAt(pos)); +} + +int gTextArea::toPosition(int line, int col) const +{ + GtkTextIter iter; + int lm, cm; + + if (line < 0) line = 0; + if (col < 0) col = 0; + + lm = gtk_text_buffer_get_line_count(_buffer) - 1; + if (line >= lm) + line = lm; + + gtk_text_buffer_get_start_iter(_buffer, &iter); + gtk_text_iter_set_line(&iter, line); + + cm = gtk_text_iter_get_chars_in_line(&iter); + if (line < lm) + cm--; + + if (col > cm) + col = cm; + + gtk_text_iter_set_line_offset(&iter, col); + + return gtk_text_iter_get_offset(&iter); +} + + +/********************************************************************************** + +gTextArea selection + +***********************************************************************************/ + +bool gTextArea::isSelected() const +{ + return gtk_text_buffer_get_selection_bounds(_buffer, NULL, NULL); + //return gtk_text_buffer_get_has_selection(buf); // Only since 2.10 +} + +int gTextArea::selStart() const +{ + GtkTextIter start, end; + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + return gtk_text_iter_get_offset(&start); +} + +int gTextArea::selEnd() const +{ + GtkTextIter start, end; + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + return gtk_text_iter_get_offset(&end); +} + +char *gTextArea::selText() const +{ + GtkTextIter start, end; + char *text; + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + text = gtk_text_buffer_get_text(_buffer, &start, &end, true); + gt_free_later(text); + return text; +} + +void gTextArea::setSelText(const char *vl) +{ + GtkTextIter start, end; + + if (!vl) + vl = ""; + + if (gtk_text_buffer_get_selection_bounds(_buffer, &start, &end)) + gtk_text_buffer_delete(_buffer, &start, &end); + + gtk_text_buffer_insert(_buffer, &start, vl, -1); + refresh(); +} + +void gTextArea::selDelete() +{ + GtkTextIter start, end; + + if (gtk_text_buffer_get_selection_bounds(_buffer, &start, &end)) + { + gtk_text_iter_set_offset(&end, gtk_text_iter_get_offset(&start)); + gtk_text_buffer_select_range(_buffer, &start, &end); + } +} + +void gTextArea::selSelect(int pos, int length) +{ + GtkTextIter start, end; + + gtk_text_buffer_get_end_iter(_buffer, &start); + + if (gtk_text_iter_get_offset(&start) < pos) + pos = gtk_text_iter_get_offset(&start); + + if (pos < 0) + { + length -= pos; + pos = 0; + } + + if ((pos + length) < 0) + length = (-pos); + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + gtk_text_iter_set_offset(&start, pos); + gtk_text_iter_set_offset(&end, pos + length); + gtk_text_buffer_select_range(_buffer, &start, &end); +} + +void gTextArea::updateCursor(GdkCursor *cursor) +{ + GdkWindow *win = TEXT_AREA(textview); + + gControl::updateCursor(cursor); + + if (!win) + return; + + if (cursor) + gdk_window_set_cursor(win, cursor); + else + { + cursor = gdk_cursor_new_for_display(gtk_widget_get_display(textview), GDK_XTERM); + gdk_window_set_cursor(win, cursor); +#ifdef GTK3 + g_object_unref(cursor); +#else + gdk_cursor_unref(cursor); +#endif + } +} + +/*void gTextArea::waitForLayout(int *tw, int *th) +{ + GtkTextIter start; + GtkTextIter end; + gint w, h; + + gtk_text_layout_set_screen_width(GTK_TEXT_VIEW(textview)->layout, width()); + + gtk_text_buffer_get_bounds (_buffer, &start, &end); + gtk_text_layout_invalidate (GTK_TEXT_VIEW(textview)->layout, &start, &end); + gtk_text_layout_validate(GTK_TEXT_VIEW(textview)->layout, 0x7FFFFFFF); + gtk_text_layout_get_size(GTK_TEXT_VIEW(textview)->layout, &w, &h); + + *tw = w; + *th = h; +} + +int gTextArea::textWidth() +{ + int w, h; + waitForLayout(&w, &h); + return w; +} + +int gTextArea::textHeight() +{ + int w, h; + waitForLayout(&w, &h); + return h; +}*/ + +int gTextArea::alignment() const +{ + if (_align_normal) + return ALIGN_NORMAL; + + switch(gtk_text_view_get_justification(GTK_TEXT_VIEW(textview))) + { + case GTK_JUSTIFY_RIGHT: return ALIGN_RIGHT; + case GTK_JUSTIFY_CENTER: return ALIGN_CENTER; + case GTK_JUSTIFY_LEFT: default: return ALIGN_LEFT; + } +} + +void gTextArea::setAlignment(int vl) +{ + GtkJustification align; + + _align_normal = false; + + switch(vl & 0xF) + { + case ALIGN_LEFT: align = GTK_JUSTIFY_LEFT; break; + case ALIGN_RIGHT: align = GTK_JUSTIFY_RIGHT; break; + case ALIGN_CENTER: align = GTK_JUSTIFY_CENTER; break; + case ALIGN_NORMAL: default: align = gDesktop::rightToLeft() ? GTK_JUSTIFY_RIGHT: GTK_JUSTIFY_LEFT; _align_normal = true; break; + } + + gtk_text_view_set_justification(GTK_TEXT_VIEW(textview), align); +} + +void gTextArea::undo() +{ + gTextAreaAction *action; + GtkTextIter start, stop; + + if (!_undo_stack) + return; + + begin(); + _undo_in_progress = true; + + action = _undo_stack; + _undo_stack = _undo_stack->next; + + action->prev = NULL; + action->next = _redo_stack; + if (_redo_stack) + _redo_stack->prev = action; + _redo_stack = action; + + if (action->type == ACTION_INSERT) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_get_iter_at_offset(_buffer, &stop, action->start + action->length); + gtk_text_buffer_delete(_buffer, &start, &stop); + gtk_text_buffer_place_cursor(_buffer, &start); + } + else if (action->type == ACTION_DELETE) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_insert(_buffer, &start, action->text->str, action->text->len); + gtk_text_buffer_get_iter_at_offset(_buffer, &stop, action->end); + if (action->delete_key_used) + gtk_text_buffer_place_cursor(_buffer, &start); + else + gtk_text_buffer_place_cursor(_buffer, &stop); + } + + end(); + ensureVisible(); + _undo_in_progress = false; +} + +void gTextArea::redo() +{ + gTextAreaAction *action; + GtkTextIter start, stop; + + if (!_redo_stack) + return; + + begin(); + _undo_in_progress = true; + + action = _redo_stack; + _redo_stack = _redo_stack->next; + + action->prev = NULL; + action->next = _undo_stack; + if (_undo_stack) + _undo_stack->prev = action; + _undo_stack = action; + + if (action->type == ACTION_INSERT) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_insert(_buffer, &start, action->text->str, action->text->len); + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start + action->length); + gtk_text_buffer_place_cursor(_buffer, &start); + } + else if (action->type == ACTION_DELETE) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_get_iter_at_offset(_buffer, &stop, action->end); + gtk_text_buffer_delete(_buffer, &start, &stop); + gtk_text_buffer_place_cursor(_buffer, &start); + } + + end(); + ensureVisible(); + _undo_in_progress = false; +} + +void gTextArea::clear() +{ + begin(); + setText(""); + clearUndoStack(); + clearRedoStack(); + end(); +} + +GtkIMContext *gTextArea::getInputMethod() +{ +#ifdef GTK3 + return GTK_TEXT_VIEW(widget)->priv->im_context; +#else + return GTK_TEXT_VIEW(widget)->im_context; +#endif +} + +void gTextArea::updateFixSpacing() +{ + GtkTextIter start; + GtkTextIter end; + + if (font()->mustFixSpacing()) + { + if (!_fix_spacing_tag) + _fix_spacing_tag = gtk_text_buffer_create_tag(_buffer, NULL, "letter-spacing", PANGO_SCALE, NULL); + + gtk_text_buffer_get_bounds(_buffer, &start, &end); + gtk_text_buffer_apply_tag (_buffer, _fix_spacing_tag, &start, &end); + } + else + { + if (_fix_spacing_tag) + { + gtk_text_buffer_get_bounds(_buffer, &start, &end); + gtk_text_buffer_remove_tag(_buffer, _fix_spacing_tag, &start, &end); + gtk_text_tag_table_remove(gtk_text_buffer_get_tag_table(_buffer), _fix_spacing_tag); + _fix_spacing_tag = NULL; + } + } +} + +#ifdef GTK3 +GtkWidget *gTextArea::getStyleSheetWidget() +{ + return textview; +} + +const char *gTextArea::getStyleSheetColorNode() +{ + return "text"; +} + +void gTextArea::customStyleSheet(GString *) +{ + gtk_text_view_set_pixels_inside_wrap(GTK_TEXT_VIEW(widget), font()->mustFixSpacing()); + gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(widget), font()->mustFixSpacing()); + + updateFixSpacing(); +} + +#endif + +void gTextArea::getCursorPos(int *x, int *y, int pos) const +{ + GdkRectangle rect; + int f = getFrameWidth(); + GtkTextIter *iter = getIterAt(pos); + + gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), iter, &rect); + gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_WIDGET, rect.x, rect.y + rect.height, x, y); + *x += f; + *y += f; +} + +void gTextArea::emitCursor() +{ + int pos = position(); + + if (pos == _last_pos) + return; + + _last_pos = pos; + CB_textarea_cursor(this); +} + +#ifdef GTK3 +void gTextArea::onEnterEvent() +{ + if (_text_area_visible) + gdk_window_show(TEXT_AREA(textview)); +} + +void gTextArea::onLeaveEvent() +{ + _text_area_visible = !hasFocus() && gdk_window_is_visible(TEXT_AREA(textview)); + if (_text_area_visible) + gdk_window_hide(TEXT_AREA(textview)); +} +#endif + +void gTextArea::setMinimumSize() +{ + if (scrollBar()) + _min_h = gApplication::getScrollbarBigSize(); // + font()->height() + (hasBorder() ? 4 : 0); + else + _min_h = font()->height() + (hasBorder() ? 4 : 0); + + _min_w = _min_h; +} + +void gTextArea::updateScrollBar() +{ + gControl::updateScrollBar(); + setMinimumSize(); +} + +void gTextArea::setFont(gFont *ft) +{ + gControl::setFont(ft); + setMinimumSize(); +} + +void gTextArea::setBorder(bool b) +{ + gControl::setBorder(b); + setFramePadding(hasBorder() ? gDesktop::scale() * 3 / 4 : 0); +} + +gColor gTextArea::defaultBackground() const +{ + return gDesktop::getColor(gDesktop::TEXT_BACKGROUND, !isEnabled()); +} diff --git a/gb.gtk/src/gtextarea.h b/gb.gtk/src/gtextarea.h new file mode 100644 index 00000000..47ae46e1 --- /dev/null +++ b/gb.gtk/src/gtextarea.h @@ -0,0 +1,131 @@ +/*************************************************************************** + + gtextarea.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTEXTAREA_H +#define __GTEXTAREA_H + +class gTextAreaAction; + +class gTextArea : public gControl +{ +public: + gTextArea(gContainer *parent); + ~gTextArea(); + +//"Properties" + int column() const; + int length() const; + int line() const; + int position() const; + bool readOnly() const; + char* text() const; + bool wrap() const; + bool isSelected() const; + + void setColumn(int vl); + void setLine(int vl); + void setPosition(int vl); + void setReadOnly(bool vl); + void setText(const char *txt, int len = -1); + void setWrap(bool vl); + //int textWidth(); + //int textHeight(); + + int alignment() const; + void setAlignment(int vl); + +//"Methods" + void copy(); + void cut(); + void ensureVisible(); + void paste(); + void insert(const char *txt); + int toLine(int pos) const; + int toColumn(int pos) const; + int toPosition(int line, int col) const; + +//"Selection properties" + int selStart() const; + int selEnd() const; + char* selText() const; + + void setSelText(const char *vl); + +//"Selection methods" + void selDelete(); + void selSelect(int pos, int length); + void selectAll() { selSelect(0, length()); } + + bool canUndo() const { return _undo_stack != 0; } + bool canRedo() const { return _redo_stack != 0; } + void begin() { _not_undoable_action++; } + void end() { _not_undoable_action--; } + void undo(); + void redo(); + void clear(); + + void getCursorPos(int *x, int *y, int pos) const; + + void emitCursor(); + +//"Private" + virtual void updateCursor(GdkCursor *cursor); + virtual void updateScrollBar(); + virtual void setMinimumSize(); + virtual void setFont(gFont *ft); + virtual void setBorder(bool b); + virtual gColor defaultBackground() const; +#ifdef GTK3 + virtual GtkWidget *getStyleSheetWidget(); + virtual const char *getStyleSheetColorNode(); + virtual void customStyleSheet(GString *css); + virtual void onEnterEvent(); + virtual void onLeaveEvent(); +#endif + void updateFixSpacing(); + virtual GtkIMContext *getInputMethod(); + //void waitForLayout(int *tw, int *th); + void clearUndoStack(); + void clearRedoStack(); + + gTextAreaAction *_undo_stack; + gTextAreaAction *_redo_stack; + int _not_undoable_action; + unsigned _undo_in_progress : 1; + +private: + GtkWidget *textview; + GtkTextBuffer *_buffer; + unsigned _align_normal : 1; + unsigned _text_area_visible : 1; + int _last_pos; + GtkTextTag *_fix_spacing_tag; + + GtkTextIter *getIterAt(int pos = -1) const; +}; + +// Callbacks +void CB_textarea_change(gTextArea *sender); +void CB_textarea_cursor(gTextArea *sender); + +#endif diff --git a/gb.gtk/src/gtextbox.cpp b/gb.gtk/src/gtextbox.cpp new file mode 100644 index 00000000..73972950 --- /dev/null +++ b/gb.gtk/src/gtextbox.cpp @@ -0,0 +1,565 @@ +/*************************************************************************** + + gtextbox.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gkey.h" +#include "gdesktop.h" +#include "gtextbox.h" + +#ifdef GTK3 + +#define MAX_ICONS 2 + +#if GTK_CHECK_VERSION(3,14,0) + +struct _GtkEntryPrivate +{ + void *icons[MAX_ICONS]; + + GtkEntryBuffer *buffer; + GtkIMContext *im_context; + GtkWidget *popup_menu; + + GdkWindow *text_area; +}; + +#else + +struct _GtkEntryPrivate +{ + void *icons[MAX_ICONS]; + + GtkEntryBuffer *buffer; + GtkIMContext *im_context; + GtkWidget *popup_menu; + + GdkDevice *device; + + GdkWindow *text_area; +}; + +#endif + +#define TEXT_AREA(_entry) (GTK_ENTRY(_entry)->priv->text_area) + +#else + +#define TEXT_AREA(_entry) (GTK_ENTRY(_entry)->text_area) + +#endif + + +static gboolean raise_change(gTextBox *data) +{ + if (data->_changed) + { + CB_textbox_change(data); + data->_changed = false; + } + return FALSE; +} + +static void cb_before_insert(GtkEditable *editable, gchar *new_text, gint new_text_length, gint *position, gTextBox *data) +{ + if (gKey::gotCommit()) + { + gcb_im_commit(NULL, new_text, data); + if (gKey::canceled()) + g_signal_stop_emission_by_name (G_OBJECT(editable), "insert-text"); + *position = gtk_editable_get_position(editable); + } +} + +static void cb_change_insert(GtkEditable *editable, gchar *new_text, gint new_text_length, gint *position, gTextBox *data) +{ + data->_changed = false; + gtk_editable_set_position(editable, *position); + CB_textbox_change(data); + *position = gtk_editable_get_position(editable); +} + +static void cb_change_delete(GtkEditable *editable, gint start_pos, gint end_pos, gTextBox *data) +{ + if (!data->_changed) + { + data->_changed = true; + g_timeout_add(0, (GSourceFunc)raise_change, data); + } +} + +static void cb_activate(GtkEntry *editable, gTextBox *data) +{ + CB_textbox_activate(data); +} + +static void cb_cursor(GtkWidget *entry, GParamSpec *param, gTextBox *data) +{ + if (data->_last_position != data->position()) + { + data->_last_position = data->position(); + CB_textbox_cursor(data); + } +} + +gTextBox::gTextBox(gContainer *parent) : gControl(parent) +{ +#ifndef GTK3 + _placeholder = NULL; +#endif + _changed = false; + _has_border = true; + _has_native_popup = true; + _text_area_visible = true; + _last_position = 0; + + have_cursor = true; + _no_background = TRUE; + + entry = widget = gtk_entry_new(); + realize(); + setColorBase(); + initEntry(); +} + +gTextBox::~gTextBox() +{ +#ifndef GTK3 + if (_placeholder) g_free(_placeholder); +#endif +} + +/*static void cb_im_commit(GtkIMContext *context, const char *str, gpointer pointer) +{ + fprintf(stderr, "cb_im_commit: %s\n", str); +}*/ + +void gTextBox::initEntry() +{ + _has_input_method = entry != NULL; + + if (!entry) + return; + + // This imitates the QT behaviour, where change signal is raised after the cursor has been moved. + g_signal_connect(G_OBJECT(entry), "insert-text", G_CALLBACK(cb_before_insert), (gpointer)this); + g_signal_connect_after(G_OBJECT(entry), "insert-text", G_CALLBACK(cb_change_insert), (gpointer)this); + g_signal_connect_after(G_OBJECT(entry), "delete-text", G_CALLBACK(cb_change_delete), (gpointer)this); + g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(cb_activate), (gpointer)this); + g_signal_connect(G_OBJECT(entry), "notify::cursor-position", G_CALLBACK(cb_cursor), (gpointer)this); + //g_signal_connect(getInputMethod(), "commit", G_CALLBACK(cb_im_commit), (gpointer)this); +} + +char* gTextBox::text() +{ + return (char*)gtk_entry_get_text(GTK_ENTRY(entry)); +} + +void gTextBox::setText(const char *vl) +{ + int save; + + if (!vl) vl = ""; + + if (!entry || !strcmp(vl, text())) + return; + + save = _last_position; + lock(); + gtk_entry_set_text(GTK_ENTRY(entry), vl); + gtk_editable_set_position(GTK_EDITABLE(entry), -1); + unlock(); + CB_textbox_change(this); + _last_position = save; + cb_cursor(entry, NULL, this); +} + +#ifdef GTK3 +char* gTextBox::placeholder() const +{ + return (char*)gtk_entry_get_placeholder_text(GTK_ENTRY(entry)); +} + +void gTextBox::setPlaceholder(const char *vl) +{ + if (!vl) vl = ""; + + if (!entry) + return; + + gtk_entry_set_placeholder_text(GTK_ENTRY(entry), vl); +} +#else +char* gTextBox::placeholder() const +{ + return _placeholder; +} + +void gTextBox::setPlaceholder(const char *vl) +{ + if (_placeholder) g_free(_placeholder); + _placeholder = g_strdup(vl); +} + +#endif + +bool gTextBox::password() const +{ + if (entry) + return !gtk_entry_get_visibility(GTK_ENTRY(entry)); + else + return false; +} + +void gTextBox::setPassword(bool vl) +{ + if (!entry) + return; + + gtk_entry_set_visibility(GTK_ENTRY(entry),!vl); + if (vl) + gtk_entry_set_invisible_char(GTK_ENTRY(entry), (gunichar)0x25CF); +} + +bool gTextBox::isReadOnly() const +{ + return !gtk_editable_get_editable(GTK_EDITABLE(entry)); +} + +void gTextBox::setReadOnly(bool vl) +{ + gtk_editable_set_editable(GTK_EDITABLE(entry),!vl); +} + +int gTextBox::position() const +{ + if (entry) + return gtk_editable_get_position(GTK_EDITABLE(entry)); + else + return 0; +} + +void gTextBox::setPosition(int pos) +{ + int len; + + if (!entry) + return; + + len = length(); + + if (pos < 0) + pos = 0; + else if (pos > len) + pos = -1; + + gtk_editable_set_position(GTK_EDITABLE(entry), pos); +} + +void gTextBox::setBorder(bool vl) +{ + if (!entry) + return; + + if (vl == _has_border) + return; + + _has_border = vl; + + gtk_entry_set_has_frame(GTK_ENTRY(entry), vl); +#ifdef GTK3 + updateStyleSheet(true); + setMinimumSize(); +#endif +} + +void gTextBox::insert(char *txt, int len) +{ + if (!entry || !len || !txt) return; + + lock(); + gtk_editable_delete_selection(GTK_EDITABLE(entry)); + unlock(); + int pos = position(); + gtk_editable_insert_text(GTK_EDITABLE(entry), txt, len, &pos); +} + +int gTextBox::length() +{ + const gchar *buf; + + if (!entry) + return 0; + + buf = gtk_entry_get_text(GTK_ENTRY(entry)); + if (!buf) return 0; + + return g_utf8_strlen(buf, -1); +} + +int gTextBox::maxLength() const +{ + if (entry) + return gtk_entry_get_max_length(GTK_ENTRY(entry)); + else + return 0; +} + +void gTextBox::setMaxLength(int vl) +{ + if (!entry) + return; + if (vl<0) vl=0; + if (vl>65536) vl=0; + gtk_entry_set_max_length(GTK_ENTRY(entry), vl); + +} + +bool gTextBox::isSelected() const +{ + if (entry) + return gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), NULL, NULL); + else + return false; +} + +int gTextBox::selStart() const +{ + int start; + + if (!entry) + return -1; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,NULL); + return start; +} + +int gTextBox::selLength() const +{ + int start,end; + + if (!entry) + return 0; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,&end); + return end - start; +} + +char* gTextBox::selText() const +{ + int start,end; + + if (!entry) + return NULL; + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,&end); + return gtk_editable_get_chars(GTK_EDITABLE(entry),start,end); + +} + +void gTextBox::setSelText(char *txt,int len) +{ + int start,end; + + if (!entry) + return; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,&end); + gtk_editable_delete_text(GTK_EDITABLE(entry),start,end); + gtk_editable_insert_text(GTK_EDITABLE(entry),txt,len,&start); + +} + +void gTextBox::selClear() +{ + int start; + + if (!entry) + return; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,NULL); + gtk_editable_select_region(GTK_EDITABLE(entry),start,start); +} + +void gTextBox::selectAll() +{ + if (entry) + gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); +} + +void gTextBox::select(int start,int len) +{ + if (!entry) + return; + if ( (len<=0) || (start<0) ) { selClear(); return; } + gtk_editable_select_region(GTK_EDITABLE(entry),start,start+len); +} + + +int gTextBox::alignment() const +{ + if (entry) + return gt_to_alignment(gtk_entry_get_alignment(GTK_ENTRY(entry))); + else + return ALIGN_NORMAL; +} + +void gTextBox::setAlignment(int al) +{ + if (!entry) + return; + gtk_entry_set_alignment(GTK_ENTRY(entry), gt_from_alignment(al)); +} + +void gTextBox::updateCursor(GdkCursor *cursor) +{ + GdkWindow *win; + + gControl::updateCursor(cursor); + if (!entry) + return; + + win = TEXT_AREA(entry); + if (!win) + return; + + if (cursor) + gdk_window_set_cursor(win, cursor); + else + { + cursor = gdk_cursor_new_for_display(gtk_widget_get_display(widget), GDK_XTERM); + gdk_window_set_cursor(win, cursor); +#ifdef GTK3 + g_object_unref(cursor); +#else + gdk_cursor_unref(cursor); +#endif + } +} + +void gTextBox::clear() +{ + setText(""); +} + + +void gTextBox::setMinimumSize() +{ + int h = font()->height(); + _min_h = h + (hasBorder() ? 4 : 0); + _min_w = h / 2 + (hasBorder() ? 4 : 0); +} + + +void gTextBox::setFont(gFont *ft) +{ + gControl::setFont(ft); + setMinimumSize(); +} + +GtkIMContext *gTextBox::getInputMethod() +{ +#ifdef GTK3 + return entry ? GTK_ENTRY(entry)->priv->im_context : NULL; +#else + return entry ? GTK_ENTRY(entry)->im_context : NULL; +#endif +} + + +void gTextBox::getCursorPos(int *x, int *y, int pos) +{ + int px, py; + PangoLayout *layout; + PangoRectangle rect; + + layout = gtk_entry_get_layout(GTK_ENTRY(entry)); + pos = gtk_entry_text_index_to_layout_index(GTK_ENTRY(entry), pos < 0 ? position() : pos); + pango_layout_get_cursor_pos(layout, pos, &rect, NULL); + + gtk_entry_get_layout_offsets(GTK_ENTRY(entry), &px, &py); + +#ifdef GTK3 + GdkRectangle r; + gtk_entry_get_text_area(GTK_ENTRY(entry), &r); + py = r.y; +#endif + + *x = px + PANGO_PIXELS(rect.x); + *y = py + PANGO_PIXELS(rect.y + rect.height); +} + +void gTextBox::setFocus() +{ + bool r = isReadOnly(); + + if (!r) + setReadOnly(true); + gControl::setFocus(); + if (!r) + setReadOnly(false); +} + +#ifdef GTK3 +void gTextBox::customStyleSheet(GString *css) +{ + if (!_has_border) + { + setStyleSheetNode(css, ""); + g_string_append_printf(css, "border:none;box-shadow:none;padding-top:0;padding-bottom:0;\n"); + /*if (background() == COLOR_DEFAULT) + g_string_append_printf(css, "background:none;");*/ + } +} +#endif + +#ifdef GTK3 +void gTextBox::onEnterEvent() +{ + if (!entry) + return; + + if (_text_area_visible) + { + //fprintf(stderr, "gTextBox::onEnterEvent: show\n"); + gdk_window_show(TEXT_AREA(entry)); + } +} + +void gTextBox::onLeaveEvent() +{ + if (!entry) + return; + + _text_area_visible = !hasFocus() && gdk_window_is_visible(TEXT_AREA(entry)); + if (_text_area_visible) + { + //fprintf(stderr, "gTextBox::onEnterEvent: hide\n"); + gdk_window_hide(TEXT_AREA(entry)); + } +} +#endif + +gColor gTextBox::defaultBackground() const +{ + return gDesktop::getColor(gDesktop::TEXT_BACKGROUND, !isEnabled()); +} diff --git a/gb.gtk/src/gtextbox.h b/gb.gtk/src/gtextbox.h new file mode 100644 index 00000000..8655ab21 --- /dev/null +++ b/gb.gtk/src/gtextbox.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + gtextbox.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTEXTBOX_H +#define __GTEXTBOX_H + +class gTextBox : public gControl +{ +public: + gTextBox(gContainer *parent); + ~gTextBox(); + +//"Properties" + int alignment() const; + bool hasBorder() const { return _has_border; } + virtual int length(); + int maxLength() const; + bool password() const; + int position() const; + virtual char *text(); + virtual char *placeholder() const; + virtual bool isReadOnly() const; + int selLength() const; + int selStart() const; + char* selText() const; + bool isSelected() const; + + void setAlignment(int vl); + void setBorder(bool vl); + void setMaxLength(int len); + void setPassword(bool vl); + void setPosition(int pos); + virtual void setReadOnly(bool vl); + virtual void setText(const char *vl); + virtual void setPlaceholder(const char *vl); + void setSelText(char *txt, int len); + +//"Methods" + virtual void clear(); + virtual void setFocus(); + void insert(char* txt,int len); + void selClear(); + void select(int start,int len); + void selectAll(); + bool hasEntry() const { return entry != 0; } + + void getCursorPos(int *x, int *y, int pos); + +#ifdef GTK3 + virtual void customStyleSheet(GString *css); +#endif + +//"Private" + virtual void updateCursor(GdkCursor *cursor); + void initEntry(); + virtual GtkIMContext *getInputMethod(); + + virtual void setMinimumSize(); + virtual void setFont(gFont *ft); + + virtual gColor defaultBackground() const; + +#ifdef GTK3 + virtual void onEnterEvent(); + virtual void onLeaveEvent(); +#endif + + GtkWidget *entry; + + unsigned _changed : 1; + unsigned _has_border : 1; + unsigned _text_area_visible : 1; + + int _last_position; + +#ifndef GTK3 + char *_placeholder; +#endif +}; + +// Callbacks +void CB_textbox_change(gTextBox *sender); +void CB_textbox_activate(gTextBox *sender); +void CB_textbox_cursor(gTextBox *sender); + +#endif diff --git a/gb.gtk/src/gtools.cpp b/gb.gtk/src/gtools.cpp new file mode 100644 index 00000000..adf9e50f --- /dev/null +++ b/gb.gtk/src/gtools.cpp @@ -0,0 +1,2583 @@ +/*************************************************************************** + + gtools.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gcolor.h" +#include "gdesktop.h" +#include "gtools.h" +#include "gpicture.h" +#include "gapplication.h" + +// HTML character entities +#include "kentities.h" + +#include "CKey.h" + +void stub(const char *function) +{ + printf("gb.gtk: warning: %s not yet implemented\n", function); +} + +/******************************************************************* + +Conversion between GDK and long type colors + +********************************************************************/ + +#define SCALE(i) ((int)(i * 255.0 / 65535.0 + 0.5)) +#define UNSCALE(d) ((int)(d / 255.0 * 65535.0 + 0.5)) + +#ifndef GTK3 +static GtkStateType _color_style_bg[] = { GTK_STATE_INSENSITIVE, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_NORMAL }; +static GtkStateType _color_style_fg[] = { GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_NORMAL }; +#endif + +#ifdef GTK3 +void fill_gdk_color(GdkColor *gcol, gColor color) +{ + int r, g, b; + + gt_color_to_rgb(color, &r, &g, &b); + + gcol->red = UNSCALE(r); + gcol->green = UNSCALE(g); + gcol->blue = UNSCALE(b); +} +#else +void fill_gdk_color(GdkColor *gcol, gColor color, GdkColormap *cmap) +{ + int r, g, b; + + if (!cmap) + cmap = gdk_colormap_get_system(); + + gt_color_to_rgb(color, &r, &g, &b); + + gcol->red = UNSCALE(r); + gcol->green = UNSCALE(g); + gcol->blue = UNSCALE(b); + + gdk_colormap_alloc_color(cmap, gcol, TRUE, TRUE); +} +#endif + +gColor gt_gdkcolor_to_color(GdkColor *gcol) +{ + return gt_rgb_to_color(SCALE(gcol->red), SCALE(gcol->green), SCALE(gcol->blue)); +} + +#ifndef GTK3 + +static void set_color(GtkWidget *wid, gColor color, void (*func)(GtkWidget *, GtkStateType, const GdkColor *), bool fg) +{ + GdkColor gcol; + GdkColor *pcol; + int i; + GtkStateType st; + + if (color == COLOR_DEFAULT) + { + pcol = NULL; + } + else + { + fill_gdk_color(&gcol, color); + pcol = &gcol; + } + + for (i = 0;; i++) + { + st = fg ? _color_style_fg[i] : _color_style_bg[i]; + (*func)(wid, st, pcol); + if (st == GTK_STATE_NORMAL) + break; + } +} + +gColor get_gdk_fg_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return gt_gdkcolor_to_color(&st->fg[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} + +void set_gdk_fg_color(GtkWidget *wid, gColor color) +{ + set_color(wid, color, gtk_widget_modify_fg, true); +} + +gColor get_gdk_bg_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return gt_gdkcolor_to_color(&st->bg[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} + +void set_gdk_bg_color(GtkWidget *wid,gColor color) +{ + set_color(wid, color, gtk_widget_modify_bg, false); +} + +gColor get_gdk_text_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return gt_gdkcolor_to_color(&st->text[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} + +void set_gdk_text_color(GtkWidget *wid,gColor color) +{ + set_color(wid, color, gtk_widget_modify_text, true); +} + +gColor get_gdk_base_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return gt_gdkcolor_to_color(&st->base[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} + +void set_gdk_base_color(GtkWidget *wid,gColor color) +{ + set_color(wid, color, gtk_widget_modify_base, false); +} +#endif + +void gt_color_to_rgb(gColor color, int *r, int *g, int *b) +{ + *b = color & 0xFF; + *g = (color >> 8) & 0xFF; + *r = (color >> 16) & 0xFF; +} + +gColor gt_rgb_to_color(int r, int g, int b) +{ + return (gColor)(b | (g << 8) | (r << 16)); +} + +void gt_color_to_rgba(gColor color, int *r, int *g, int *b, int *a) +{ + *b = color & 0xFF; + *g = (color >> 8) & 0xFF; + *r = (color >> 16) & 0xFF; + *a = 255 - ((color >> 24) & 0xFF); +} + +gColor gt_rgba_to_color(int r, int g, int b, int a) +{ + return (gColor)(b | (g << 8) | (r << 16) | ((255 - a) << 24)); +} + +void gt_color_to_frgba(gColor color, double *r, double *g, double *b, double *a) +{ + *b = (color & 0xFF) / 255.0; + *g = ((color >> 8) & 0xFF) / 255.0; + *r = ((color >> 16) & 0xFF) / 255.0; + *a = 1 - ((color >> 24) & 0xFF) / 255.0; +} + +#ifdef GTK3 + +void gt_from_color(gColor color, GdkRGBA *rgba) +{ + gt_color_to_frgba(color, &rgba->red, &rgba->green, &rgba->blue, &rgba->alpha); +} + +gColor gt_to_color(GdkRGBA *rgba) +{ + return gt_frgba_to_color(rgba->red, rgba->green, rgba->blue, rgba->alpha); +} + +void gt_to_css_color(char *css, gColor color) +{ + int r, g, b, a; + char buffer[8]; + + gt_color_to_rgba(color, &r, &g, &b, &a); + if (a == 255) + sprintf(css, "#%02X%02X%02X", r, g, b); + else + { + sprintf(buffer, "%.3f", a / 255.0); + sprintf(css, "rgba(%d,%d,%d,0.%s)", r, g, b, &buffer[2]); + } +} + +void gt_add_css_color(char **pcss, gColor color) +{ + char buffer[64]; + gt_to_css_color(buffer, color); + g_stradd(pcss, buffer); +} + +#endif + +gColor gt_frgba_to_color(double r, double g, double b, double a) +{ + return gt_rgba_to_color((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0)); +} + +void gt_rgb_to_hsv(int r, int g, int b, int *H, int *S,int *V ) +{ + float R = (float)r, G = (float)g, B = (float)b; + float v, x, f; + int i; + + R/=255; + G/=255; + B/=255; + + x=R; + if (G<x) x=G; + if (B<x) x=B; + + v=R; + if (G>v) v=G; + if (B>v) v=B; + + + if(v == x) { + *H=-1; + *S=0; + *V = (int)(v * 255); + return; + } + + f = (R == x) ? G - B : ((G == x) ? B - R : R - G); + i = (R == x) ? 3 : ((G == x) ? 5 : 1); + *H=(int)((i - f /(v - x))*60); + *S=(int)(((v - x)/v)*255); + *V=(int)(v*255); +} + +void gt_hsv_to_rgb(int h, int s, int v, int *R, int *G, int *B) +{ + double H,S,V; + double var_h,var_i,var_1,var_2,var_3,tmp_r,tmp_g,tmp_b; + + if (h < 0) + h = 360 - ((-h) % 360); + else + h = h % 360; + + H=((double)h)/360; + S=((double)s)/255; + V=((double)v)/255; + + if ( S == 0 ) + { + *R = (int)(V * 255); + *G = (int)(V * 255); + *B = (int)(V * 255); + } + else + { + var_h = H * 6; + var_i = (int)var_h; + var_1 = V * ( 1 - S ); + var_2 = V * ( 1 - S * ( var_h - var_i ) ); + var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) ); + + switch ((int)var_i) + { + case 0: + tmp_r = V; + tmp_g = var_3; + tmp_b = var_1; + break; + + case 1: + tmp_r = var_2; + tmp_g = V; + tmp_b = var_1; + break; + + case 2: + tmp_r = var_1; + tmp_g = V; + tmp_b = var_3; + break; + + case 3: + tmp_r = var_1; + tmp_g = var_2; + tmp_b = V; + break; + + case 4: + tmp_r = var_3; + tmp_g = var_1; + tmp_b = V; + break; + + default: + tmp_r = V; + tmp_g = var_1; + tmp_b = var_2; + break; + } + + *R = (int)(tmp_r * 255); + *G = (int)(tmp_g * 255); + *B = (int)(tmp_b * 255); + + } +} + + +#if 0 +/******************************************************************* + + Border style in gWidgets that use a Frame to show it + + *******************************************************************/ +int Frame_getBorder(GtkFrame *fr) +{ + switch (gtk_frame_get_shadow_type(fr)) + { + case GTK_SHADOW_NONE: return BORDER_NONE; + case GTK_SHADOW_ETCHED_OUT: return BORDER_PLAIN; + case GTK_SHADOW_IN: return BORDER_SUNKEN; + case GTK_SHADOW_OUT: return BORDER_RAISED; + case GTK_SHADOW_ETCHED_IN: return BORDER_ETCHED; + } + + return BORDER_NONE; +} + +void Frame_setBorder(GtkFrame *fr,int vl) +{ + switch(vl) + { + case BORDER_NONE: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_NONE); + break; + case BORDER_PLAIN: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_ETCHED_OUT); + break; + case BORDER_SUNKEN: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_IN); + break; + case BORDER_RAISED: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_OUT); + break; + case BORDER_ETCHED: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_ETCHED_IN); + break; + default: return; + } +} +#endif + +/************************************************************* + + Takes a snapshot of a GdkWindow + + *************************************************************/ + +gPicture *gt_grab_window(GdkWindow *win, int x, int y, int w, int h) +{ + int ow, oh; + int ww, hh; + int dx, dy; + GdkPixbuf *buf = NULL; + gPicture *pic; + +#ifdef GTK3 + gdk_window_get_geometry(win, NULL, NULL, &ww, &hh); +#else + gdk_window_get_geometry(win, 0, 0, &ww, &hh, 0); +#endif + + if (w <= 0 || h <= 0) + { + w = ww; + h = hh; + } + + dx = dy = 0; + ow = w; + oh = h; + + if (x < 0) + { + w += x; + dx = -x; + x = 0; + } + + if (y < 0) + { + h += y; + dy = -y; + y = 0; + } + + if ((x + w) > ww) + w = ww - x; + + if ((y + h) > hh) + h = hh - y; + + if (w > 0 && h > 0) + { +#ifdef GTK3 + buf = gdk_pixbuf_get_from_window(win, x, y, w, h); +#else + buf = gdk_pixbuf_get_from_drawable(NULL, win, NULL, x, y, 0, 0, w, h); +#endif + } + + if (w == ow && h == oh) + return new gPicture(buf); + + pic = new gPicture(gPicture::PIXBUF, ow, oh, false); + pic->fill(0); + + if (w <= 0 || h <= 0) + return pic; + + gdk_pixbuf_copy_area(buf, 0, 0, w, h, pic->getPixbuf(), dx, dy); + return pic; +} + +/************************************************************* + + GtkLabel with accelerators + + *************************************************************/ + +void gMnemonic_correctText(char *st,char **buf) +{ + int bucle,b2; + + if (!st || !*st) + { + *buf = g_strdup(""); + return; + } + + int len = strlen(st); + int len_in = len; + + for (bucle=0;bucle<len_in;bucle++) + { + if (st[bucle]=='&') + { + if (bucle<(len_in-1)) + if (st[bucle+1]=='&') + len--; + + } + else if (st[bucle]=='_') len++; + } + + *buf=(char*)g_malloc(sizeof(char)*(len+1)); + b2=0; + + for (bucle=0;bucle<len_in;bucle++) + { + if (st[bucle]=='&') + { + if (bucle<(len_in-1)) + { + if (st[bucle+1]=='&') + { + (*buf)[b2++]='&'; + bucle++; + } + else + { + (*buf)[b2++]='_'; + } + } + else + { + (*buf)[b2]=' '; + b2++; + } + } + else if (st[bucle]=='_') + { + (*buf)[b2++]='_'; + (*buf)[b2++]='_'; + } + else + { + (*buf)[b2++]=st[bucle]; + } + (*buf)[b2]=0; + } +} + +guint gMnemonic_correctMarkup(char *st,char **buf) +{ + int bucle,b2; + guint retval=0; + + if (!st || !*st) + { + *buf = g_strdup(""); + return retval; + } + + int len = strlen(st); + int len_in = len; + + for (bucle=0;bucle<len_in;bucle++) + { + if (st[bucle]=='&') + { + if (bucle<(len_in-1)) + { + if (st[bucle+1]=='&') len+-3; + else len+=6; + } + else + { + len+=4; + } + + } + else if (st[bucle]=='<') + { + len+=3; + } + else if (st[bucle]=='>') + { + len+=3; + } + } + + *buf=(char*)g_malloc(sizeof(char)*(len+1)); + (*buf[0])=0; + b2=0; + + for (bucle=0;bucle<len_in;bucle++) + { + if (st[bucle]=='&') + { + if (bucle<(len_in-1)) + { + if (st[bucle+1]=='&') + { + (*buf)[b2++]='&'; + (*buf)[b2++]='a'; + (*buf)[b2++]='m'; + (*buf)[b2++]='p'; + (*buf)[b2++]=';'; + bucle++; + } + else + { + bucle++; + retval=(guint)st[bucle]; + (*buf)[b2++]='<'; + (*buf)[b2++]='u'; + (*buf)[b2++]='>'; + (*buf)[b2++]=st[bucle]; + (*buf)[b2++]='<'; + (*buf)[b2++]='/'; + (*buf)[b2++]='u'; + (*buf)[b2++]='>'; + } + } + else + { + (*buf)[b2++]='&'; + (*buf)[b2++]='a'; + (*buf)[b2++]='m'; + (*buf)[b2++]='p'; + (*buf)[b2++]=';'; + } + } + else if (st[bucle]=='<') + { + (*buf)[b2++]='&'; + (*buf)[b2++]='l'; + (*buf)[b2++]='t'; + (*buf)[b2++]=';'; + } + else if (st[bucle]=='>') + { + (*buf)[b2++]='&'; + (*buf)[b2++]='g'; + (*buf)[b2++]='t'; + (*buf)[b2++]=';'; + } + else + { + (*buf)[b2++]=st[bucle]; + } + (*buf)[b2]=0; + } + + return retval; +} + +void gMnemonic_returnText(char *st,char **buf) +{ + int bucle,b2; + + if (!st || !*st) + { + *buf = g_strdup(""); + return; + } + + int len = strlen(st); + int len_in = len; + + for (bucle=0;bucle<len_in;bucle++) + { + if (st[bucle]=='_') + { + if (bucle<(len_in-1)) + if (st[bucle+1]=='_') + len--; + + } + else if (st[bucle]=='&') len++; + } + + *buf=(char*)g_malloc(sizeof(char)*(len+1)); + b2=0; + + for (bucle=0;bucle<len_in;bucle++) + { + if (st[bucle]=='_') + { + if (bucle<(len_in-1)) + { + if (st[bucle+1]=='_') + { + (*buf)[b2++]='&'; + bucle++; + } + else + { + (*buf)[b2++]='_'; + } + } + else + { + (*buf)[b2]=' '; + b2++; + } + } + else if (st[bucle]=='&') + { + (*buf)[b2++]='&'; + (*buf)[b2++]='&'; + } + else + { + (*buf)[b2++]=st[bucle]; + } + (*buf)[b2]=0; + } +} + +void g_stradd(gchar **res, const gchar *s) +{ + if (!*res) + *res = g_strdup(s); + else + { + gchar *old = *res; + *res = g_strconcat(*res, s, (void *)NULL); + g_free(old); + } +} + +void gt_shortcut_parse(char *shortcut, guint *key, GdkModifierType *mods) +{ + char **tokens; + char *token; + int i; + int m; + + *key = 0; + *mods = (GdkModifierType)0; + + m = 0; + + if (!shortcut || !*shortcut) + return; + + tokens = g_strsplit(shortcut, "+", 0); + + i = 0; + while (tokens[i]) + { + g_strstrip(tokens[i]); + i++; + } + + for (i = 0; (token = tokens[i]); i++) + { + if (!strcasecmp(token, "ctrl") || !strcasecmp(token, "control")) + m |= GDK_CONTROL_MASK; + else if (!strcasecmp(token, "shift")) + m |= GDK_SHIFT_MASK; + else if (!strcasecmp(token, "alt")) + m |= GDK_MOD1_MASK; + else + { + *key = KEY_get_keyval_from_name(token); + *mods = (GdkModifierType)m; + break; + } + } + + g_strfreev(tokens); + + //fprintf(stderr, "gt_shortcut_parse: %s -> %d / %d\n", shortcut, *key, m); +} + +#define MAX_FREE_LATER 16 + +static char *_free_later_ptr[MAX_FREE_LATER] = { 0 }; +static int _free_later_index = 0; + +char *gt_free_later(char *ptr) +{ + if (_free_later_ptr[_free_later_index]) + g_free(_free_later_ptr[_free_later_index]); + + _free_later_ptr[_free_later_index] = ptr; + + _free_later_index++; + + if (_free_later_index >= MAX_FREE_LATER) + _free_later_index = 0; + + return ptr; +} + +static void free_all_later() +{ + int i; + char *p; + + for (i = 0; i < MAX_FREE_LATER; i++) + { + p = _free_later_ptr[i]; + if (p) + { + g_free(p); + _free_later_ptr[i] = NULL; + } + } +} + +void gt_exit() +{ + free_all_later(); +} + +static const char *html_entity(char c) +{ + static char same[2]; + + if (c == '<') + return "<"; + else if (c == '>') + return ">"; + else if (c == '&') + return "&"; + + same[0] = c; + same[1] = 0; + return (const char *)same; +} + +static void add_space(GString *str) +{ + char c; + + if (str->len == 0) + return; + + c = str->str[str->len - 1]; + if (c != ' ' && c != '\n') + g_string_append_c(str, ' '); +} + +static void add_paragraph_break(GString *str) +{ + int i; + char c; + bool markup = false; + int nl = 0; + + for (i = str->len - 1; i >= 0; i--) + { + c = str->str[i]; + if (markup) + { + if (c == '<') + markup = false; + continue; + } + if (c == '>') + { + markup = true; + continue; + } + if (c != '\n') + break; + nl++; + if (nl >= 2) + break; + } + + if (nl == 2 || i < 0) + return; + + while (nl < 2) + { + g_string_append_c(str, '\n'); + nl++; + } +} + +static void add_attr(GString *pango, const char *attr, const char *value) +{ + bool add_quote = *value != '"' && *value !='\''; + + g_string_append_c(pango, ' '); + g_string_append(pango, attr); + g_string_append_c(pango, '='); + if (add_quote) g_string_append_c(pango, '"'); + g_string_append(pango, value); + if (add_quote) g_string_append_c(pango, '"'); +} + +char *gt_html_to_pango_string(const char *html, int len_html, bool newline_are_break) +{ + static const char *title[] = + { + "h1", "size=\"xx-large\"", + "h2", "size=\"x-large\"", + "h3", "size=\"large\"", + "h4", "size=\"medium\"", + "h5", "size=\"small\"", + "h6", "size=\"x-small\"", + NULL, NULL + }; + + static const char *accept[] = { "b", "big", "i", "s", "sub", "sup", "small", "tt", "u", NULL }; + + static const char *size_name[] = { "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" }; + static const int size_stack_len = 16; + + GString *pango = g_string_new(""); + const char *p, *p_end, *p_markup; + char c; + const char *token; + const char *markup; + GB_ARRAY attr_array; + char **attr; + int attr_count; + const char *pp; + gsize len; + bool start_token = false; + bool end_token = false; + const char **pt; + int size = 3; // medium + char size_stack[size_stack_len]; + int size_stack_ptr = 0; + int i; + int mode_pre = 0; + + //fprintf(stderr, "gt_html_to_pango_string: %.*s\n", len_html, html); + + p_end = &html[len_html < 0 ? strlen(html) : len_html]; + p_markup = NULL; + + if (len_html == 0) + goto RETURN_STRING; + +#if PANGO_VERSION_CHECK(1, 41, 0) +#else + // Sometimes the first markup is not taken into account. + // This is a workaround for this bug: + g_string_append_unichar(pango, 0xFEFF); +#endif + + for (p = html;; p++) + { + c = *p; + if (!c) + break; + + if (c == '<') + { + if (p[1] == '/') + { + p_markup = &p[2]; + start_token = false; + end_token = true; + } + else + { + p_markup = &p[1]; + start_token = true; + end_token = false; + } + continue; + } + + if (c == '>') + { + if (!p_markup) + { + g_string_append(pango, ">"); + continue; + } + + len = p - p_markup; + + if (p[-1] == '/') + { + if (len > 0) + len--; + end_token = true; + } + + if (len <= 0) + { + g_string_append(pango, "<"); + if (end_token) g_string_append_c(pango, '/'); + g_string_append(pango, ">"); + p_markup = NULL; + continue; + } + + markup = g_strndup(p_markup, len); + + attr_array = GB.String.Split(markup, len, " ", 1, "\"", 1, FALSE, TRUE); + attr_count = GB.Array.Count(attr_array); + attr = (char **)GB.Array.Get(attr_array, 0); + token = NULL; + + for (i = 0; i < attr_count; i++) + { + if (attr[i][0]) + { + token = attr[i]; + break; + } + } + + if (!token) + goto __FOUND_TOKEN; + + for (pt = title; *pt; pt += 2) + { + if (!strcasecmp(*pt, token)) + { + if (start_token && !end_token) + { + add_paragraph_break(pango); + g_string_append_printf(pango, "<span %s><b>", pt[1]); + } + else if (end_token && !start_token) + { + g_string_append_printf(pango, "</b></span>"); + add_paragraph_break(pango); + //g_string_append(pango, "<span size=\"smaller\">\n</span>"); + } + goto __FOUND_TOKEN; + } + } + + for (pt = accept; *pt; pt++) + { + if (!strcasecmp(*pt, token)) + { + if (start_token != end_token) + g_string_append_printf(pango, "<%s%s>", end_token ? "/" : "", *pt); + goto __FOUND_TOKEN; + } + } + + if (!strcasecmp(token, "br") || !strcasecmp(token, "hr")) + { + if (start_token) + { + g_string_append_c(pango, '\n'); + } + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "p")) + { + add_paragraph_break(pango); + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "font")) + { + if (end_token && !start_token) + { + if (size_stack_ptr > 0) + { + size_stack_ptr--; + if (size_stack_ptr < size_stack_len) + size = size_stack[size_stack_ptr]; + } + g_string_append(pango, "</span>"); + } + else if (start_token && !end_token) + { + if (size_stack_ptr < size_stack_len) + size_stack[size_stack_ptr] = size; + size_stack_ptr++; + + g_string_append(pango, "<span"); + for (i = 0; i < attr_count; i++) + { + if (!strncasecmp(attr[i], "color=", 6)) + { + add_attr(pango, "foreground", attr[i] + 6); + } + else if (!strncasecmp(attr[i], "face=", 5)) + { + add_attr(pango, "face", attr[i] + 5); + } + else if (!strncasecmp(attr[i], "size=", 5)) + { + g_string_append(pango, " size="); + pp = attr[i] + 6; + if (*pp == '"') + pp++; + if (isdigit(*pp)) + { + size = *pp - '1'; + pp++; + } + else if (*pp == '+' && isdigit(pp[1])) + { + size += pp[1] - '0'; + pp += 2; + } + else if (*pp == '-' && isdigit(pp[1])) + { + size -= pp[1] - '0'; + pp += 2; + } + + if (size < 0) + size = 0; + else if (size > 6) + size = 6; + + g_string_append_c(pango, '"'); + g_string_append(pango, size_name[size]); + g_string_append_c(pango, '"'); + } + } + g_string_append_c(pango, '>'); + } + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "a") || !strncasecmp(token, "a href", 6)) + { + if (start_token && !end_token) + g_string_append(pango, "<span foreground=\"blue\"><u>"); + else if (end_token && !start_token) + g_string_append(pango, "</u></span>"); + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "code")) + { + if (start_token && !end_token) + g_string_append(pango, "<tt>"); + else if (end_token && !start_token) + g_string_append(pango, "</tt>"); + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "pre")) + { + if (start_token && !end_token) + { + add_paragraph_break(pango); + g_string_append(pango, "<tt>"); + mode_pre++; + } + else if (end_token && !start_token) + { + mode_pre--; + g_string_append(pango, "</tt>"); + add_paragraph_break(pango); + } + goto __FOUND_TOKEN; + } + + g_string_append(pango, "<"); + if (end_token) g_string_append(pango, "/"); + while (p_markup < p) + { + g_string_append(pango, html_entity(*p_markup)); + p_markup++; + } + g_string_append(pango, ">"); + + __FOUND_TOKEN: + + GB.Unref(POINTER(&attr_array)); + p_markup = NULL; + continue; + } + + if (c == '&') + { + const char *entity_start = ++p; + int entity_len; + int entity_unicode; + char *endptr; + +// if ((p_end - p) >= 6 && !strncasecmp(p, " ", 6)) +// { +// g_string_append(pango, " "); +// p += 5; +// continue; +// } + while (p < p_end && *p != ';' && ((uchar)*p) > ' ') + p++; + + entity_len = p - entity_start; + if (*p != ';') + entity_len = 0; + + if (entity_len > 1) + { + if ((entity_len == 3 && !strncasecmp(entity_start, "amp", 3)) + || (entity_len == 2 && (!strncasecmp(entity_start, "lt", 2) || !strncasecmp(entity_start, "gt", 2)))) + { + g_string_append_len(pango, entity_start - 1, entity_len + 2); + continue; + } + else + { + entity_unicode = -1; + endptr = NULL; + errno = 0; + + if (*entity_start == '#') + { + if (entity_start[1] == 'x') + { + entity_unicode = strtol(&entity_start[2], &endptr, 16); + if (entity_unicode <= 0) + entity_unicode = -1; + } + else if (isdigit(entity_start[1])) + { + entity_unicode = strtol(&entity_start[1], &endptr, 10); + if (entity_unicode <= 0) + entity_unicode = -1; + } + } + else + { + const struct entity *e = kde_findEntity(entity_start, entity_len); + if (e) + entity_unicode = e->code; + } + + if (entity_unicode >= 0) + { + g_string_append_unichar(pango, entity_unicode); + continue; + } + } + } + + g_string_append(pango, "&"); + p = entity_start - 1; + continue; + } + + if (!p_markup) + { + if (c == '\n' && !newline_are_break && mode_pre == 0) + { + add_space(pango); + continue; + } + + if (c == '\r') + { + add_space(pango); + continue; + } + + if (c == ' ') + { + if (mode_pre) + g_string_append_unichar(pango, 160); + else + add_space(pango); + continue; + } + + g_string_append(pango, html_entity(*p)); + } + } + +RETURN_STRING: + + p = g_string_free(pango, false); + //fprintf(stderr, "==> '%s'\n", p); + return (char *)p; +} + +int gt_to_alignment(double halign, double valign) +{ + int align = 0; + + if (halign == 0.0) + align += 0x1; + else if (halign == 0.5) + align += 0x2; + else if (halign == 1.0) + align += 0x3; + + if (valign == 0.0) + align += 0x10; + else if (valign == 1.0) + align += 0x20; + + return align; +} + +double gt_from_alignment(int align, bool vertical) +{ + if (vertical) + { + switch(align & 0xF0) + { + case 0x10: return 0.0; + case 0x20: return 1.0; + default: return 0.5; + } + } + else + { + switch (align & 0x0F) + { + case 1: return 0.0; + case 2: return 1.0; + case 3: return 0.5; + default: return gDesktop::rightToLeft() ? 1.0 : 0.0; + } + } +} + + +void gt_ensure_visible(GtEnsureVisible *arg, int x, int y, int w, int h) +{ + w = (w + 1) / 2; + h = (h + 1) / 2; + x += w; + y += h; + + int pw = arg->clientWidth; + int ph = arg->clientHeight; + + int cx = -arg->scrollX; + int cy = -arg->scrollY; + int cw = arg->scrollWidth; + int ch = arg->scrollHeight; + + if ( pw < w*2 ) + w=pw/2; + if ( ph < h*2 ) + h=ph/2; + + if ( cw <= pw ) { + w=0; + cx=0; + } + if ( ch <= ph ) { + h=0; + cy=0; + } + + if ( x < -cx+w ) + cx = -x+w; + else if ( x >= -cx+pw-w ) + cx = -x+pw-w; + + if ( y < -cy+h ) + cy = -y+h; + else if ( y >= -cy+ph-h ) + cy = -y+ph-h; + + if ( cx > 0 ) + cx=0; + else if ( cx < pw-cw && cw>pw ) + cx=pw-cw; + + if ( cy > 0 ) + cy=0; + else if ( cy < ph-ch && ch>ph ) + cy=ph-ch; + + arg->scrollX = -cx; + arg->scrollY = -cy; +} + +#ifdef GTK3 +#else +void gt_pixmap_fill(GdkPixmap *pix, gColor col, GdkGC *gc) +{ + GdkColor color; + int w, h; + bool free_gc = false; + + fill_gdk_color(&color, col); + + #if GTK_CHECK_VERSION(2, 24, 0) + gdk_pixmap_get_size(pix, &w, &h); + #else + gdk_drawable_get_size(GDK_DRAWABLE(pix), &w, &h); + #endif + + if (!gc) + { + gc = gdk_gc_new(pix); + free_gc = true; + } + + gdk_gc_set_foreground(gc, &color); + gdk_gc_set_background(gc, &color); + gdk_draw_rectangle(pix, gc, true, 0, 0, w, h); + + if (free_gc) + g_object_unref(gc); +} + +void gt_pixbuf_render_pixmap_and_mask(GdkPixbuf *pixbuf, GdkPixmap **pixmap_return, GdkBitmap **mask_return, int alpha_threshold) +{ + GdkColormap *colormap = gdk_rgb_get_colormap(); + GdkScreen *screen; + + screen = gdk_colormap_get_screen (colormap); + + if (pixmap_return) + { + GdkGC *gc; + *pixmap_return = gdk_pixmap_new (gdk_screen_get_root_window (screen), + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), + gdk_colormap_get_visual (colormap)->depth); + + gdk_drawable_set_colormap (GDK_DRAWABLE (*pixmap_return), colormap); + + gc = gdk_gc_new(*pixmap_return); + + gt_pixmap_fill(*pixmap_return, 0, gc); + + gdk_draw_pixbuf (*pixmap_return, gc, pixbuf, + 0, 0, 0, 0, + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), + GDK_RGB_DITHER_NORMAL, + 0, 0); + + g_object_unref(gc); + } + + if (mask_return) + { + if (gdk_pixbuf_get_has_alpha (pixbuf)) + { + *mask_return = gdk_pixmap_new (gdk_screen_get_root_window (screen), + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), 1); + + gdk_pixbuf_render_threshold_alpha (pixbuf, *mask_return, + 0, 0, 0, 0, + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), + alpha_threshold); + } + else + *mask_return = NULL; + } +} +#endif + +#if 0 +void gt_pixbuf_replace_color(GdkPixbuf *pixbuf, gColor src, gColor dst, bool noteq) +{ + guint32 *p; + int i, n; + + p = (guint32 *)gdk_pixbuf_get_pixels(pixbuf); + n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf); + + src ^= 0xFF000000; + dst ^= 0xFF000000; + + if (noteq) + { + for (i = 0; i < n; i++, p ++) + { + if (*p != src) + *p = dst; + } + } + else + { + for (i = 0; i < n; i++, p ++) + { + if (*p == src) + *p = dst; + } + } +} +#endif + +// Comes from the GIMP + +typedef + struct { + float r; + float b; + float g; + float a; + } + RGB; + +static void color_to_alpha(RGB *src, const RGB *color) +{ + RGB alpha; + + alpha.a = src->a; + + if (color->r < 0.0001) + alpha.r = src->r; + else if (src->r > color->r) + alpha.r = (src->r - color->r) / (1.0 - color->r); + else if (src->r < color->r) + alpha.r = (color->r - src->r) / color->r; + else alpha.r = 0.0; + + if (color->g < 0.0001) + alpha.g = src->g; + else if (src->g > color->g) + alpha.g = (src->g - color->g) / (1.0 - color->g); + else if (src->g < color->g) + alpha.g = (color->g - src->g) / (color->g); + else alpha.g = 0.0; + + if (color->b < 0.0001) + alpha.b = src->b; + else if (src->b > color->b) + alpha.b = (src->b - color->b) / (1.0 - color->b); + else if (src->b < color->b) + alpha.b = (color->b - src->b) / (color->b); + else alpha.b = 0.0; + + if (alpha.r > alpha.g) + { + if (alpha.r > alpha.b) + { + src->a = alpha.r; + } + else + { + src->a = alpha.b; + } + } + else if (alpha.g > alpha.b) + { + src->a = alpha.g; + } + else + { + src->a = alpha.b; + } + + if (src->a < 0.0001) + return; + + src->r = (src->r - color->r) / src->a + color->r; + src->g = (src->g - color->g) / src->a + color->g; + src->b = (src->b - color->b) / src->a + color->b; + + src->a *= alpha.a; +} + +void gt_pixbuf_make_alpha(GdkPixbuf *pixbuf, gColor color) +{ + guchar *p; + int i, n; + RGB rgb_color, rgb_src; + int r, g, b; + + p = gdk_pixbuf_get_pixels(pixbuf); + n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf); + + gt_color_to_rgb(color, &r, &g, &b); + + rgb_color.b = b / 255.0; + rgb_color.g = g / 255.0; + rgb_color.r = r / 255.0; + rgb_color.a = 1.0; + + for (i = 0; i < n; i++, p += 4) + { + rgb_src.r = p[0] / 255.0; + rgb_src.g = p[1] / 255.0; + rgb_src.b = p[2] / 255.0; + rgb_src.a = p[3] / 255.0; + + color_to_alpha(&rgb_src, &rgb_color); + + p[0] = rgb_src.r * 255.0 + 0.5; + p[1] = rgb_src.g * 255.0 + 0.5; + p[2] = rgb_src.b * 255.0 + 0.5; + //p[3] = 0xFF ^ (uchar)(rgb_src.a * 255.0 + 0.5); + p[3] = rgb_src.a * 255.0 + 0.5; + } +} + +void gt_pixbuf_make_gray(GdkPixbuf *pixbuf) +{ + guchar *p; + int i, n; + + p = gdk_pixbuf_get_pixels(pixbuf); + n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf); + + for (i = 0; i < n; i++, p += 4) + p[0] = p[1] = p[2] = (p[0] * 11 + p[1] * 16 + p[2] * 5) / 32; +} + +GdkPixbuf *gt_pixbuf_create_disabled(GdkPixbuf *img) +{ + gint w, h; + guchar *r, *g, *b, *end; + GdkPixbuf *dimg; + + dimg = gdk_pixbuf_copy(img); + w = gdk_pixbuf_get_width(dimg); + h = gdk_pixbuf_get_height(dimg); + r = gdk_pixbuf_get_pixels(dimg); + g = r + 1; + b = r + 2; + end = r + w * h * gdk_pixbuf_get_n_channels(img); + + while (r != end) + { + *r = *g = *b = (*r * 11 + *g * 16 + *b * 5) / 32; + r += 4; + g += 4; + b += 4; + } + + return dimg; +} + + + +static void disabled_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer data) +{ + // Print only debugging messages + //if (log_level & G_LOG_LEVEL_DEBUG) + // g_log_default_handler(log_domain, log_level, message, data); +} + +static GLogFunc old_handler = NULL; + +void gt_disable_warnings(bool disable) +{ + //fprintf(stderr, "disable warnings\n"); + if (disable) + old_handler = g_log_set_default_handler(disabled_handler, NULL); + else + g_log_set_default_handler(old_handler, NULL); + //fprintf(stderr, "enable warnings\n"); +} + +void gt_set_cell_renderer_text_from_font(GtkCellRendererText *cell, gFont *font) +{ + g_object_set(G_OBJECT(cell), + "font-desc", font->desc(), + "underline", font->underline() ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE, + "strikethrough", font->strikeout(), + (void *)NULL); +} + +static void set_layout_from_font(PangoLayout *layout, gFont *font, bool add, int dpi) +{ + PangoFontDescription *desc; + PangoAttrList *attrs; + PangoAttribute *attr; + bool copy = false; + + desc = font->desc(); + + /*if ((dpi && dpi != gDesktop::resolution())) + { + double size = pango_font_description_get_size(desc); + desc = pango_font_description_copy(desc); + copy = true; + + size *= dpi / gDesktop::resolution(); + + pango_font_description_set_size(desc, (int)size); + }*/ + + pango_layout_set_font_description(layout, desc); + + if (add) + { + attrs = pango_layout_get_attributes(layout); + if (!attrs) + { + attrs = pango_attr_list_new(); + add = false; + } + } + else + attrs = pango_attr_list_new(); + + if (font->underline()) + { + attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); + pango_attr_list_insert(attrs, attr); + } + + if (font->strikeout()) + { + attr = pango_attr_strikethrough_new(true); + pango_attr_list_insert(attrs, attr); + } + + if (font->mustFixSpacing()) + { + attr = pango_attr_letter_spacing_new(PANGO_SCALE); + pango_attr_list_insert(attrs, attr); + } + + pango_layout_set_attributes(layout, attrs); + + if (!add) + pango_attr_list_unref(attrs); + + pango_layout_context_changed(layout); + + if (copy) + pango_font_description_free(desc); +} + +void gt_set_layout_from_font(PangoLayout *layout, gFont *font, int dpi) +{ + set_layout_from_font(layout, font, false, dpi); +} + +void gt_add_layout_from_font(PangoLayout *layout, gFont *font, int dpi) +{ + set_layout_from_font(layout, font, true, dpi); +} + +void gt_layout_alignment(PangoLayout *layout, const char *text, int len, float w, float h, float *tw, float *th, int align, float *offX, float *offY) +{ + int ptw, pth; + gt_layout_get_extents(layout, &ptw, &pth, FALSE); + *tw = (float)ptw / PANGO_SCALE; + *th = (float)pth / PANGO_SCALE; + + if (w < 0) w = *tw; + if (h < 0) h = *th; + + if (ALIGN_IS_NORMAL(align)) + { + PangoDirection dir = pango_find_base_dir(text, len); + + switch (align) + { + case ALIGN_BOTTOM_NORMAL: + align = dir == PANGO_DIRECTION_RTL ? ALIGN_BOTTOM_RIGHT : ALIGN_BOTTOM_LEFT; + break; + + case ALIGN_NORMAL: + align = dir == PANGO_DIRECTION_RTL ? ALIGN_RIGHT : ALIGN_LEFT; + break; + + case ALIGN_TOP_NORMAL: + align = dir == PANGO_DIRECTION_RTL ? ALIGN_TOP_RIGHT : ALIGN_TOP_LEFT; + break; + } + } + + switch (align) + { + case ALIGN_BOTTOM: + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + *offX = (w - *tw) / 2; + *offY = h - *th; + break; + + case ALIGN_BOTTOM_LEFT: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + *offX = 0; + *offY = h - *th; + break; + + case ALIGN_BOTTOM_RIGHT: + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + *offX = w - *tw; + *offY = h - *th; + break; + + case ALIGN_CENTER: + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + *offX = (w - *tw) / 2; + *offY = (h - *th) / 2; + break; + + case ALIGN_LEFT: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + *offX = 0; + *offY = (h - *th) / 2; + break; + + case ALIGN_RIGHT: + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + *offX = w - *tw; + *offY = (h - *th) / 2; + break; + + case ALIGN_TOP: + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + *offX = (w - *tw) / 2; + *offY = 0; + break; + + case ALIGN_TOP_LEFT: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + *offX = 0; + *offY = 0; + break; + + case ALIGN_TOP_RIGHT: + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + *offX = w - *tw; + *offY = 0; + break; + } +} + +#if GTK_CHECK_VERSION(2, 18, 0) +#else +void +gtk_widget_set_can_focus (GtkWidget *widget, + gboolean can_focus) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (can_focus != GTK_WIDGET_CAN_FOCUS (widget)) + { + if (can_focus) + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); + + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "can-focus"); + } +} + +void gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation) +{ + *allocation = widget->allocation; +} +#endif + +static void add_again(GtkWidget *widget, GtkWidget *ignore) +{ + int x, y; + GtkContainer *parent; + gControl *control; + gContainer *parent_control; + + if (widget == ignore) + return; + + parent = GTK_CONTAINER(gtk_widget_get_parent(ignore)); + control = gt_get_control(widget); + parent_control = (gContainer *)gt_get_control(parent); + + if (control && parent_control) + { + x = control->x(); + y = control->y(); + } + + g_object_ref(G_OBJECT(widget)); + gtk_container_remove(parent, widget); + gtk_container_add(parent, widget); + g_object_unref(G_OBJECT(widget)); + + if (control && parent_control) + parent_control->moveChild(control, x, y); +} + +void gt_lower_widget(GtkWidget *widget) +{ + GtkContainer *parent; + + parent = GTK_CONTAINER(gtk_widget_get_parent(widget)); + if (parent) + gtk_container_foreach(parent, (GtkCallback)add_again, widget); +} + +#if GTK_CHECK_VERSION(2, 22, 0) +#else +int gdk_device_get_source(GdkDevice *device) +{ + return device->source; +} + +//GtkWidget *_gtk_window_group_get_current_grab(GtkWindowGroup *window_group); + +GtkWidget *gtk_window_group_get_current_grab(GtkWindowGroup *window_group) +{ + if (window_group->grabs) + return GTK_WIDGET(window_group->grabs->data); + return NULL; +} +#endif + +void gt_cairo_set_source_color(cairo_t *cr, GB_COLOR color) +{ + int r, g, b, a; + + GB_COLOR_SPLIT(color, r, g, b, a); + cairo_set_source_rgba(cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0); +} + +void gt_cairo_draw_rect(cairo_t *cr, int x, int y, int w, int h, GB_COLOR color) +{ + gt_cairo_set_source_color(cr, color); + + cairo_rectangle(cr, x, y, w, 1); + cairo_fill(cr); + if (h <= 1) + return; + cairo_rectangle(cr, x, y + h - 1, w, 1); + cairo_fill(cr); + if (h <= 2) + return; + cairo_rectangle(cr, x, y + 1, 1, h - 2); + cairo_fill(cr); + cairo_rectangle(cr, x + w - 1, y + 1, 1, h - 2); + cairo_fill(cr); +} + +// Function partially taken from the GTK+ source code. +cairo_surface_t *gt_cairo_create_surface_from_pixbuf(const GdkPixbuf *pixbuf) +{ + gint width = gdk_pixbuf_get_width (pixbuf); + gint height = gdk_pixbuf_get_height (pixbuf); + guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf); + int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf); + int n_channels = gdk_pixbuf_get_n_channels (pixbuf); + int cairo_stride; + guchar *cairo_pixels; + cairo_format_t format; + cairo_surface_t *surface; + static const cairo_user_data_key_t key = { 0 }; + int j; + + //fprintf(stderr, "gt_cairo_create_surface_from_pixbuf: %d %d / %d\n", width, height, n_channels); + + if (n_channels == 3) + format = CAIRO_FORMAT_RGB24; + else + format = CAIRO_FORMAT_ARGB32; + + cairo_stride = cairo_format_stride_for_width (format, width); + cairo_pixels = (uchar *)g_malloc_n (height, cairo_stride); + surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels, + format, + width, height, cairo_stride); + + cairo_surface_set_user_data (surface, &key, + cairo_pixels, (cairo_destroy_func_t)g_free); + + for (j = height; j; j--) + { + guchar *p = gdk_pixels; + guchar *q = cairo_pixels; + + if (n_channels == 3) + { + guchar *end = p + 3 * width; + + while (p < end) + { + #if G_BYTE_ORDER == G_LITTLE_ENDIAN + q[0] = p[2]; + q[1] = p[1]; + q[2] = p[0]; + #else + q[1] = p[0]; + q[2] = p[1]; + q[3] = p[2]; + #endif + p += 3; + q += 4; + } + } + else + { + guchar *end = p + 4 * width; + guint t1,t2,t3; + + #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END + + while (p < end) + { + #if G_BYTE_ORDER == G_LITTLE_ENDIAN + MULT(q[0], p[2], p[3], t1); + MULT(q[1], p[1], p[3], t2); + MULT(q[2], p[0], p[3], t3); + q[3] = p[3]; + #else + q[0] = p[3]; + MULT(q[1], p[0], p[3], t1); + MULT(q[2], p[1], p[3], t2); + MULT(q[3], p[2], p[3], t3); + #endif + + p += 4; + q += 4; + } + + #undef MULT + } + + gdk_pixels += gdk_rowstride; + cairo_pixels += cairo_stride; + } + + return surface; +} + +void gt_cairo_draw_pixbuf(cairo_t *cr, GdkPixbuf *pixbuf, float x, float y, float w, float h, float opacity, GB_RECT *source) +{ + cairo_surface_t *surface; + cairo_pattern_t *pattern = NULL, *save; + cairo_matrix_t matrix; + + cairo_save(cr); + + save = cairo_get_source(cr); + cairo_pattern_reference(save); + + if (source) + pixbuf = gdk_pixbuf_new_subpixbuf(pixbuf, source->x, source->y, source->w, source->h); + + if (w < 0 || h < 0) + { + //fprintf(stderr, "gdk_cairo_set_source_pixbuf: %d %d\n", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y); + cairo_rectangle(cr, x, y, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + } + else + { + surface = gt_cairo_create_surface_from_pixbuf(pixbuf); + pattern = cairo_pattern_create_for_surface(surface); + cairo_surface_destroy(surface); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + + //gdk_cairo_set_source_pixbuf(cr, picture->getPixbuf(), x, y); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + cairo_matrix_scale(&matrix, w / gdk_pixbuf_get_width(pixbuf), h / gdk_pixbuf_get_height(pixbuf)); + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + + cairo_rectangle(cr, x, y, w, h); + } + + if (opacity == 1.0) + { + cairo_fill(cr); + } + else + { + cairo_clip(cr); + cairo_paint_with_alpha(cr, opacity); + } + + cairo_set_source(cr, save); + cairo_pattern_destroy(save); + + cairo_restore(cr); + + if (pattern) + cairo_pattern_destroy(pattern); + + if (source) + g_object_unref(pixbuf); +} + + +// Style management + +#define NUM_STYLES 12 + +#ifdef GTK3 +static GtkStyleContext *_style[NUM_STYLES] = { NULL }; +#else +static int _style_loaded = 0; +static GtkStyle *_style[NUM_STYLES]; +#endif + +static int type_to_index(GType type) +{ + if (type == GTK_TYPE_ENTRY) + return 1; + else if (type == GTK_TYPE_LAYOUT) + return 2; + else if (type == GTK_TYPE_TOOLTIP) + return 3; + else if (type == GTK_TYPE_SCROLLBAR) + return 4; + else if (type == GTK_TYPE_SCROLLED_WINDOW) + return 5; + else if (type == GTK_TYPE_CHECK_BUTTON) + return 6; + else if (type == GTK_TYPE_RADIO_BUTTON) + return 7; + else if (type == GTK_TYPE_FRAME) + return 8; + else if (type == GTK_TYPE_LABEL) + return 9; + else if (type == GTK_TYPE_BUTTON) + return 10; + else if (type == GTK_TYPE_WINDOW) + return 11; + else + return 0; +} + +#ifdef GTK3 + +const char *gt_get_style_class(GType type) +{ + static const char *_class[] = { + GTK_STYLE_CLASS_DEFAULT, GTK_STYLE_CLASS_ENTRY, GTK_STYLE_CLASS_BACKGROUND, GTK_STYLE_CLASS_TOOLTIP, + GTK_STYLE_CLASS_SCROLLBAR, GTK_STYLE_CLASS_DEFAULT, GTK_STYLE_CLASS_CHECK, GTK_STYLE_CLASS_RADIO, + GTK_STYLE_CLASS_FRAME, GTK_STYLE_CLASS_BACKGROUND, GTK_STYLE_CLASS_BUTTON, GTK_STYLE_CLASS_BACKGROUND + }; + + int index = type_to_index(type); + if (index < 0) + return NULL; + else + return _class[index]; +} + +GtkStyleContext *gt_get_style(GType type, const char *node, const char *more_klass) +{ + int index = 0; + GtkStyleContext *style; + + if (!node && !more_klass) + { + index = type_to_index(type); + style = _style[index]; + if (style) + return style; + } + + GtkWidgetPath *path = gtk_widget_path_new(); + const char *klass = gt_get_style_class(type); + + style = gtk_style_context_new(); + + if (klass) + gtk_style_context_add_class(style, klass); + + if (more_klass) + gtk_style_context_add_class(style, more_klass); + + gtk_widget_path_append_type(path, type); + + #if GTK_CHECK_VERSION(3, 20, 0) + gtk_widget_path_iter_set_object_name(path, -1, klass); + if (node) + { + gtk_widget_path_append_type(path, type); + gtk_widget_path_iter_set_object_name(path, 1, node); + } + #endif + + gtk_style_context_set_path(style, path); + + if (!node && !more_klass) + _style[index] = style; + + return style; +} + +#else + +static GtkStyle *get_style(const char *name, GType type) +{ + GtkSettings *set = gtk_settings_get_default(); + GtkStyle* st = gtk_rc_get_style_by_paths(set, NULL, name, type); + if (!st) st = gtk_widget_get_default_style(); + return st; +} + +static GtkStyle *get_widget_style(const char *name) +{ + GtkSettings *set = gtk_settings_get_default(); + GtkStyle* st = gtk_rc_get_style_by_paths(set, name, NULL, G_TYPE_NONE); + if (!st) st = gtk_widget_get_default_style(); + return st; +} + +GtkStyle *gt_get_style(GType type) +{ + int index = type_to_index(type); + if (index < 0) + return NULL; + + if ((_style_loaded & (1 << index)) == 0) + { + if (type == GTK_TYPE_TOOLTIP) + _style[index] = get_widget_style("gtk-tooltip"); + else + _style[index] = get_style(g_type_name(type), type); + + _style_loaded |= (1 << index); + } + + return _style[index]; +} + +#endif + +void gt_get_style_property(GType type, const char *name, void *pvalue) +{ +#ifdef GTK3 + gtk_style_context_get_style(gt_get_style(type), name, pvalue, NULL); +#else + gtk_style_get(gt_get_style(type), type, name, pvalue, (char *)NULL); +#endif +} + + +#ifdef GTK3 + +// Draw a styled border + +void gt_draw_border(cairo_t *cr, GtkStyleContext *st, GtkStateFlags state, int border, gColor color, int x, int y, int w, int h, bool bg) +{ + if (border == BORDER_NONE) + return; + + if (w < 2 || h < 2) + return; + + if (border == BORDER_PLAIN) + { + gt_cairo_draw_rect(cr, x, y, w, h, color); + return; + } + + if (strcmp(gApplication::getStyleName(), "oxygen-gtk") == 0) + { + x -= 3; + w += 6; + } + + gboolean interior_focus; + + if (border == BORDER_RAISED) + { + st = gt_get_style(GTK_TYPE_BUTTON); + gtk_style_context_set_state(st, state); + //gtk_style_context_add_class(st, GTK_STYLE_CLASS_BUTTON); + gtk_render_frame(st, cr, x, y, w, h); + } + else if (border == BORDER_SUNKEN) + { + st = gt_get_style(GTK_TYPE_ENTRY); + gtk_style_context_set_state(st, state); + //gtk_style_context_add_class(st, GTK_STYLE_CLASS_ENTRY); + if (bg) + gtk_render_background(st, cr, x, y, w, h); + gtk_render_frame(st, cr, x, y, w, h); + } + else if (border == BORDER_ETCHED) + { + st = gt_get_style(GTK_TYPE_FRAME); + gtk_style_context_set_state(st, state); + //gtk_style_context_add_class(st, GTK_STYLE_CLASS_FRAME); + gtk_render_frame(st, cr, x, y, w, h); + } + + if (state & GTK_STATE_FLAG_FOCUSED) + { + gtk_style_context_get_style (st, "interior-focus", &interior_focus, NULL); + if (!interior_focus) + gtk_render_focus(st, cr, x, y, w, h); + } +} +#endif + +bool gt_grab(GtkWidget *widget, bool owner_event, guint32 time) +{ + GdkWindow *win = gtk_widget_get_window(widget); + int ret; + +#if GDK_MAJOR_VERSION > 3 || (GDK_MAJOR_VERSION == 3 && GDK_MINOR_VERSION >= 20) + + GdkSeat *seat = gdk_display_get_default_seat(gdk_display_get_default()); + + ret = gdk_seat_grab(seat, win, GDK_SEAT_CAPABILITY_ALL, owner_event, gdk_window_get_cursor(win), NULL, NULL, NULL); + if (ret == GDK_GRAB_SUCCESS) + return FALSE; + +#elif defined(GTK3) + + GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); + GdkDevice *keyboard = gdk_device_get_associated_device(pointer); + + ret = gdk_device_grab(pointer, win, GDK_OWNERSHIP_APPLICATION, owner_event, + (GdkEventMask)(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), + gdk_window_get_cursor(win), + time); + + if (ret == GDK_GRAB_SUCCESS) + { + ret = gdk_device_grab(keyboard, win, GDK_OWNERSHIP_APPLICATION, owner_event, + (GdkEventMask)(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK), + NULL, time); + + if (ret == GDK_GRAB_SUCCESS) + return FALSE; + + gdk_device_ungrab(pointer, GDK_CURRENT_TIME); + } + +#else + + ret = gdk_pointer_grab(win, owner_event, + (GdkEventMask)(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), + NULL, +#if GTK_CHECK_VERSION(2, 18, 0) + gdk_window_get_cursor(win), +#else + NULL, +#endif + time); + + if (ret == GDK_GRAB_SUCCESS) + { + ret = gdk_keyboard_grab(win, owner_event, time); + + if (ret == GDK_GRAB_SUCCESS) + return FALSE; + + gdk_pointer_ungrab(GDK_CURRENT_TIME); + } + +#endif + + fprintf(stderr, "gb.gtk: warning: grab failed: %d\n", ret); + return TRUE; +} + +void gt_ungrab(void) +{ +#if GDK_MAJOR_VERSION > 3 || (GDK_MAJOR_VERSION == 3 && GDK_MINOR_VERSION >= 20) + + GdkSeat *seat = gdk_display_get_default_seat(gdk_display_get_default()); + gdk_seat_ungrab(seat); + +#elif defined(GTK3) + + GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); + GdkDevice *keyboard = gdk_device_get_associated_device(pointer); + + gdk_device_ungrab(pointer, GDK_CURRENT_TIME); + gdk_device_ungrab(keyboard, GDK_CURRENT_TIME); + +#else + + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + +#endif +} + +#ifdef GTK3 +void gt_widget_reparent(GtkWidget *widget, GtkWidget *new_parent) +{ + GtkWidget *parent = gtk_widget_get_parent(widget); + + if (parent == new_parent) + return; + + g_object_ref(widget); + gtk_container_remove(GTK_CONTAINER(parent), widget); + gtk_container_add(GTK_CONTAINER(new_parent), widget); + g_object_unref(widget); +} +#endif + +void gt_on_theme_change() +{ + int i; + + for (i = 0; i < NUM_STYLES; i++) + { + if (_style[i]) + g_object_unref(G_OBJECT(_style[i])); + _style[i] = NULL; + } +} + +#if GTK_CHECK_VERSION(3, 22, 0) +int gt_find_monitor(GdkMonitor *monitor) +{ + GdkDisplay *display = gdk_display_get_default(); + int i; + + for (i = 0; i < gdk_display_get_n_monitors(display); i++) + { + if (gdk_display_get_monitor(display, i) == monitor) + return i; + } + + return -1; +} +#endif + +#ifdef GTK3 +const char *gt_widget_set_name(GtkWidget *widget, const char *suffix) +{ + static int count = 0; + + char buffer[256]; + const char *name; + + name = gtk_widget_get_name(widget); + if (name && name[0] == 'g') + return name; + + count++; + sprintf(buffer, "g%d_%s", count, suffix ? suffix : ""); + gtk_widget_set_name(widget, buffer); + return gtk_widget_get_name(widget); +} + +void gt_css_add_color(GString *css, gColor bg, gColor fg) +{ + char buffer[32]; + + if (bg != COLOR_DEFAULT) + { + gt_to_css_color(buffer, bg); + g_string_append(css, "background-color:"); + g_string_append(css, buffer); + g_string_append(css, ";\nbackground-image:none;\n"); + } + + if (fg != COLOR_DEFAULT) + { + gt_to_css_color(buffer, fg); + g_string_append(css, "color:"); + g_string_append(css, buffer); + g_string_append(css, ";\n"); + } +} + +void gt_css_add_font(GString *css, gFont *font) +{ + int s; + char buffer[32]; + + if (!font) + return; + + if (font->_name_set) + { + g_string_append(css, "font-family:\""); + g_string_append(css, font->name()); + g_string_append(css, "\";\n"); + } + + if (font->_size_set) + { + s = (int)(font->size() * 10 + 0.5); + sprintf(buffer, "%dpt;\n", s / 10); //, s % 10); + g_string_append(css, "font-size:"); + g_string_append(css, buffer); + } + + if (font->_bold_set) + { + g_string_append(css, "font-weight:"); + g_string_append(css, font->bold() ? "bold" : "normal"); + g_string_append(css, ";\n"); + } + + if (font->_italic_set) + { + g_string_append(css, "font-style:"); + g_string_append(css, font->italic() ? "italic" : "normal"); + g_string_append(css, ";\n"); + } + + if (font->_underline_set || font->_strikeout_set) + { + g_string_append(css, "text-decoration-line:"); + if (font->strikeout()) + g_string_append(css, "line-through"); + else if (font->underline()) + g_string_append(css, "underline"); + else + g_string_append(css, "none"); + g_string_append(css, ";\n"); + } + + if (font->mustFixSpacing()) + { + g_string_append(css, "letter-spacing:1px;\n"); + } +} + +void gt_widget_update_css(GtkWidget *widget, gFont *font, gColor bg, gColor fg) +{ + GtkStyleContext *context; + GtkStyleProvider *css_provider; + const char *name; + GString *css; + char *css_str; + + context = gtk_widget_get_style_context(widget); + name = gt_widget_set_name(widget, NULL); + + css = g_string_new(NULL); + + if (font || bg != COLOR_DEFAULT || fg != COLOR_DEFAULT) + { + g_string_append_printf(css, "#%s {\ntransition:none;\n", name); + gt_css_add_color(css, bg, fg); + gt_css_add_font(css, font); + g_string_append(css, "}\n"); + } + + css_provider = (GtkStyleProvider *)g_object_get_data(G_OBJECT(widget), "gambas-css"); + + if (css->len) + { + if (!css_provider) + { + css_provider = GTK_STYLE_PROVIDER(gtk_css_provider_new()); + g_object_set_data_full(G_OBJECT(widget), "gambas-css", (gpointer)css_provider, g_object_unref); + } + css_str = g_string_free(css, FALSE); + //fprintf(stderr, "==== %s\n%s", name, css_str); + gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(css_provider), css_str, -1, NULL); + g_free(css_str); + + gtk_style_context_add_provider(context, css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + else + { + if (css_provider) + { + gtk_style_context_remove_provider(context, css_provider); + g_object_set_data(G_OBJECT(widget), "gambas-css", NULL); + } + } +} + +void gt_define_style_sheet(GtkStyleProvider **provider, GString *css) +{ + GdkScreen *screen = gdk_screen_get_default(); + char *css_str; + + if (css && css->len) + { + if (!*provider) + *provider = GTK_STYLE_PROVIDER(gtk_css_provider_new()); + + css_str = g_string_free(css, FALSE); + gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(*provider), css_str, -1, NULL); + g_free(css_str); + gtk_style_context_add_provider_for_screen(screen, *provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + else + { + if (*provider) + { + gtk_style_context_remove_provider_for_screen(screen, *provider); + *provider = NULL; + } + } +} + +#endif + +void gt_layout_get_extents(PangoLayout *layout, int *w, int *h, bool pixels) +{ + PangoRectangle ink_rect, log_rect; + pango_layout_get_extents(layout, &ink_rect, &log_rect); + + *w = Max(ink_rect.width, log_rect.width); + *h = Max(ink_rect.height, log_rect.height); + + if (pixels) + { + *w = gt_pango_to_pixel(*w); + *h = gt_pango_to_pixel(*h); + } + +} + diff --git a/gb.gtk/src/gtools.h b/gb.gtk/src/gtools.h new file mode 100644 index 00000000..53e751c5 --- /dev/null +++ b/gb.gtk/src/gtools.h @@ -0,0 +1,249 @@ +/*************************************************************************** + + gtools.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTOOLS_H +#define __GTOOLS_H + +#include "widgets.h" +#include "gpicture.h" +#include "gcontrol.h" +#include "gb.paint.h" + +void stub(const char *function); + +void gt_exit(); + +void g_stradd(gchar **res, const gchar *s); + +// Frees a string later + +char *gt_free_later(char *ptr); + +// Parse a shortcut string + +void gt_shortcut_parse(char *shortcut, guint *key, GdkModifierType *mods); + +// Converts HTML to Pango format + +char* gt_html_to_pango_string(const char *html, int len_html, bool newline); + +// Converts to/from GTK+ alignment + +int gt_to_alignment(double halign, double valign = 0.5); +double gt_from_alignment(int align, bool vertical = false); + +// Global signal handlers + +gboolean gcb_focus_in(GtkWidget *widget, GdkEventFocus *event, gControl *data); +gboolean gcb_focus_out(GtkWidget *widget, GdkEventFocus *event, gControl *data); +gboolean gcb_focus(GtkWidget *widget, GtkDirectionType direction, gControl *data); + +// Where to scroll to ensure that a specific area is visible + +typedef + struct { + int clientWidth; + int clientHeight; + int scrollX; + int scrollY; + int scrollWidth; + int scrollHeight; + } + GtEnsureVisible; + +void gt_ensure_visible(GtEnsureVisible *arg, int x, int y, int w, int h); + +#define GT_NORMALIZE(x, y, w, h, sx, sy, sw, sh, width, height) \ + if (w < 0) w = width; \ + if (h < 0) h = height; \ + if (sw < 0) sw = width; \ + if (sh < 0) sh = height; \ + if (sx >= (width) || sy >= (height) || sw <= 0 || sh <= 0) \ + return; \ + if (sx < 0) x -= sx, sx = 0; \ + if (sy < 0) y -= sy, sy = 0; \ + if (sw > ((width) - sx)) \ + sw = ((width) - sx); \ + if (sh > ((height) - sy)) \ + sh = ((height) - sy); + +#ifndef GTK3 +void gt_pixmap_fill(GdkPixmap *pix, gColor col, GdkGC *gc); +#endif + +// Creates a disabled version of a pixbuf + +GdkPixbuf *gt_pixbuf_create_disabled(GdkPixbuf *img); +#ifndef GTK3 +void gt_pixbuf_render_pixmap_and_mask(GdkPixbuf *pixbuf, GdkPixmap **pixmap_return, GdkBitmap **mask_return, int alpha_threshold); +#endif +void gt_pixbuf_make_alpha(GdkPixbuf *pixbuf, gColor color); +void gt_pixbuf_make_gray(GdkPixbuf *pixbuf); + +// Enable/disable warning messages + +void gt_disable_warnings(bool disable); + +// Initialize a GtkCellRendererText from a font + +void gt_set_cell_renderer_text_from_font(GtkCellRendererText *cell, gFont *font); + +// Initialize a PangoLayout from a font + +void gt_set_layout_from_font(PangoLayout *layout, gFont *font, int dpi = 0); +void gt_add_layout_from_font(PangoLayout *layout, gFont *font, int dpi = 0); + +#define gt_pango_to_pixel(_pango) (PANGO_PIXELS_CEIL(_pango)) +//(_pango) + (PANGO_SCALE / 2)) / PANGO_SCALE) + +// Grab a window + +gPicture *gt_grab_window(GdkWindow *win, int x = 0, int y = 0, int w = 0, int h = 0); + +// Compute the alignment of a PangoLayout + +void gt_layout_alignment(PangoLayout *layout, const char *text, int len, float w, float h, float *tw, float *th, int align, float *offX, float *offY); + +// Compute the extents of a layout + +void gt_layout_get_extents(PangoLayout *layout, int *w, int *h, bool pixels); + +#if GTK_CHECK_VERSION(2, 18, 0) +#else +void gtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus); +void gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation); +#endif + +void gt_lower_widget(GtkWidget *widget); + +#if GTK_CHECK_VERSION(2, 22, 0) +#else +int gdk_device_get_source(GdkDevice *device); +GtkWidget *gtk_window_group_get_current_grab(GtkWindowGroup *window_group); +#endif + +// Cairo support + +cairo_surface_t *gt_cairo_create_surface_from_pixbuf(const GdkPixbuf *pixbuf); + +void gt_cairo_set_source_color(cairo_t *cr, GB_COLOR color); +void gt_cairo_draw_rect(cairo_t *cr, int x, int y, int w, int h, GB_COLOR color); +void gt_cairo_draw_pixbuf(cairo_t *cr, GdkPixbuf *pixbuf, float x, float y, float w, float h, float opacity, GB_RECT *source); + +// Color functions + +gColor gt_gdkcolor_to_color(GdkColor *gcol); +#ifdef GTK3 +void fill_gdk_color(GdkColor *gcol, gColor color); +#else +void fill_gdk_color(GdkColor *gcol, gColor color, GdkColormap *cmap = NULL); +gColor get_gdk_text_color(GtkWidget *wid, bool enabled); +void set_gdk_text_color(GtkWidget *wid,gColor color); +gColor get_gdk_base_color(GtkWidget *wid, bool enabled); +void set_gdk_base_color(GtkWidget *wid,gColor color); +gColor get_gdk_fg_color(GtkWidget *wid, bool enabled); +void set_gdk_fg_color(GtkWidget *wid,gColor color); +gColor get_gdk_bg_color(GtkWidget *wid, bool enabled); +void set_gdk_bg_color(GtkWidget *wid,gColor color); +#endif + +void gt_color_to_rgb(gColor color, int *r, int *g, int *b); +gColor gt_rgb_to_color(int r, int g, int b); +void gt_color_to_rgba(gColor color, int *r, int *g, int *b, int *a); +gColor gt_rgba_to_color(int r, int g, int b, int a); +void gt_rgb_to_hsv(int r, int g, int b, int *h, int *s, int *v); +void gt_hsv_to_rgb(int h, int s, int v, int *r, int *g, int *b); +void gt_color_to_frgba(gColor color, double *r, double *g, double *b, double *a); +gColor gt_frgba_to_color(double r, double g, double b, double a); + +#ifdef GTK3 +void gt_from_color(gColor color, GdkRGBA *rgba); +gColor gt_to_color(GdkRGBA *rgba); +void gt_to_css_color(char *css, gColor color); +void gt_add_css_color(char **pcss, gColor color); +#endif + +// Draw a control border + +#ifdef GTK3 +void gt_draw_border(cairo_t *cr, GtkStyleContext *st, GtkStateFlags state, int border, gColor color, int x, int y, int w, int h, bool bg = false); +#endif + +// Style management + +#ifdef GTK3 +GtkStyleContext *gt_get_style(GType type, const char *node = NULL, const char *more_klass = NULL); +#else +GtkStyle *gt_get_style(GType type); +#endif + +void gt_get_style_property(GType type, const char *name, void *pvalue); + +void gt_on_theme_change(); + +void gMnemonic_correctText(char *st,char **buf); +guint gMnemonic_correctMarkup(char *st,char **buf); +void gMnemonic_returnText(char *st,char **buf); + +/*#ifdef GTK3 +int gt_get_preferred_width(GtkWidget *widget); +#endif*/ + +#define gt_get_control(_widget) ((gControl *)g_object_get_data(G_OBJECT(_widget), "gambas-control")) +#define gt_register_control(_widget, _control) g_object_set_data(G_OBJECT(_widget), "gambas-control", _control) + +bool gt_grab(GtkWidget *widget, bool owner_event, guint32 time); +void gt_ungrab(); + +#ifdef GTK3 +void gt_widget_reparent(GtkWidget *widget, GtkWidget *new_parent); +#else +#define gt_widget_reparent gtk_widget_reparent +#endif + +#if GTK_CHECK_VERSION(3, 20, 0) +#define gt_set_focus_on_click(_widget, _flag) gtk_widget_set_focus_on_click(GTK_WIDGET(_widget), (_flag)) +#define gt_get_focus_on_click(_widget) gtk_widget_get_focus_on_click(GTK_WIDGET(_widget)) +#else +#define gt_set_focus_on_click(_widget, _flag) (gtk_button_set_focus_on_click(GTK_BUTTON(_widget), (_flag))) +#define gt_get_focus_on_click(_widget) (gtk_button_get_focus_on_click(GTK_BUTTON(_widget))) +#endif + +#if GTK_CHECK_VERSION(3, 22, 0) +int gt_find_monitor(GdkMonitor *monitor); +#endif + +#ifdef GTK3 +void gt_css_add_font(GString *css, gFont *font); +void gt_css_add_color(GString *css, gColor bg, gColor fg); +const char *gt_widget_set_name(GtkWidget *widget, const char *name); +void gt_widget_update_css(GtkWidget *widget, gFont *font, gColor bg, gColor fg); +void gt_define_style_sheet(GtkStyleProvider **provider, GString *css); +#endif + +#if GTK_CHECK_VERSION(2, 20, 0) +#else +#define gtk_widget_get_realized(_widget) GTK_WIDGET_REALIZED(_widget) +#endif + +#endif diff --git a/gb.gtk/src/gtrayicon.cpp b/gb.gtk/src/gtrayicon.cpp new file mode 100644 index 00000000..665cb186 --- /dev/null +++ b/gb.gtk/src/gtrayicon.cpp @@ -0,0 +1,283 @@ +/*************************************************************************** + + gtrayicon.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" + +#include <unistd.h> +#ifndef GTK3 +#include "x11.h" +#endif +#include "gapplication.h" +#include "gmouse.h" +#include "gtrayicon.h" + +#include "gb.form.trayicon.h" + +int gTrayIcon::_visible_count = 0; + +/************************************************************************* + +gTrayIcon + +**************************************************************************/ + +static gboolean cb_button_press(GtkStatusIcon *plug, GdkEventButton *event, gTrayIcon *data) +{ + if (gApplication::loopLevel() > data->loopLevel()) return false; + + gApplication::updateLastEventTime(); + + gMouse::validate(); + gMouse::setMouse((int)event->x, (int)event->y, (int)event->x_root, (int)event->y_root, event->button, event->state); + if (event->type == GDK_BUTTON_PRESS) + CB_trayicon_click(data, event->button); + /*else if (event->type == GDK_2BUTTON_PRESS) + data->onDoubleClick(data);*/ + gMouse::invalidate(); + + /*if (event->button == 3) + if (data->onMenu) + data->onMenu(data);*/ + + return false; +} + +static gboolean cb_menu(GtkStatusIcon *plug, guint button, guint activate_time, gTrayIcon *data) +{ + if (gApplication::loopLevel() > data->loopLevel()) return false; + + gApplication::updateLastEventTime(); + + CB_trayicon_menu(data); + + return false; +} + +static gboolean cb_scroll(GtkStatusIcon *plug, GdkEventScroll *event, gTrayIcon *data) +{ + GdkScrollDirection dir; + int dt = 0; + int ort = 0; + + if (gApplication::loopLevel() > data->loopLevel()) return false; + + gApplication::updateLastEventTime(); + + dir = event->direction; + +#ifdef GTK3 + if (dir == GDK_SCROLL_SMOOTH) + return false; + /*{ + gdouble dx = 0, dy = 0; + gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy); + if (fabs(dy) > fabs(dx)) + dir = (dy < 0) ? GDK_SCROLL_UP : GDK_SCROLL_DOWN; + else + dir = (dx < 0) ? GDK_SCROLL_LEFT : GDK_SCROLL_RIGHT; + }*/ +#endif + + switch (dir) + { + case GDK_SCROLL_UP: dt=1; ort=1; break; + case GDK_SCROLL_DOWN: dt=-1; ort=1; break; + case GDK_SCROLL_LEFT: dt=-1; ort=0; break; + case GDK_SCROLL_RIGHT: default: dt=1; ort=0; break; + } + + gMouse::validate(); + gMouse::setMouse((int)event->x, (int)event->y, (int)event->x_root, (int)event->y_root, 0, event->state); + gMouse::setWheel(dt, ort); + CB_trayicon_scroll(data); + gMouse::invalidate(); + + return false; +} + + +GList *gTrayIcon::trayicons = NULL; +gPicture *gTrayIcon::_default_icon = NULL; + +gTrayIcon::gTrayIcon() +{ + plug = NULL; + _tooltip = NULL; + _icon = NULL; + _loopLevel = 0; + + trayicons = g_list_append(trayicons, (gpointer)this); +} + +gTrayIcon::~gTrayIcon() +{ + setVisible(false); + + gPicture::assign(&_icon); + + if (_tooltip) + { + g_free(_tooltip); + _tooltip = NULL; + } + + trayicons = g_list_remove(trayicons, (gpointer)this); + + if (!trayicons && _default_icon) + { + delete _default_icon; + _default_icon = NULL; + } + + CB_trayicon_destroy(this); +} + +gPicture *gTrayIcon::defaultIcon() +{ + if (!_default_icon) + { + GdkPixbuf *img = gdk_pixbuf_new_from_data(_default_trayicon_data, GDK_COLORSPACE_RGB, TRUE, 8, + DEFAULT_TRAYICON_WIDTH, DEFAULT_TRAYICON_HEIGHT, + DEFAULT_TRAYICON_WIDTH * sizeof(int), NULL, NULL); + _default_icon = new gPicture(img); + } + + return _default_icon; +} + +void gTrayIcon::updatePicture() +{ + GdkPixbuf *pixbuf; + + if (!plug) + return; + + if (_icon) + pixbuf = _icon->getPixbuf(); + else + pixbuf = defaultIcon()->getPixbuf(); + + gtk_status_icon_set_from_pixbuf(plug, pixbuf); + + _iconw = gdk_pixbuf_get_width(pixbuf); + _iconh = gdk_pixbuf_get_height(pixbuf); +} + +void gTrayIcon::setPicture(gPicture *picture) +{ + gPicture::assign(&_icon, picture); + updatePicture(); +} + +void gTrayIcon::updateTooltip() +{ + if (!plug) + return; + + gtk_status_icon_set_tooltip_text(plug, _tooltip); +} + +void gTrayIcon::setTooltip(char* vl) +{ + if (_tooltip) + g_free(_tooltip); + + _tooltip = vl && *vl ? g_strdup(vl) : NULL; + updateTooltip(); +} + +bool gTrayIcon::isVisible() +{ + return (bool)plug; +} + +static void hide_icon(GtkStatusIcon *plug) +{ + gtk_status_icon_set_visible(plug, FALSE); + g_object_unref(plug); +} + +void gTrayIcon::setVisible(bool vl) +{ + if (vl) + { + if (!plug) + { + _loopLevel = gApplication::loopLevel() + 1; + + plug = gtk_status_icon_new(); + + updatePicture(); + updateTooltip(); + + #ifdef GDK_WINDOWING_X11 + #ifdef GTK3 + PLATFORM.Desktop.ShowTrayIcon(plug, _iconw, _iconh); + #else + // Needed, otherwise the icon does not appear in Gnome or XFCE notification area! + XSizeHints hints; + hints.flags = PMinSize; + hints.min_width = _iconw; + hints.min_height = _iconh; + XSetWMNormalHints(gdk_x11_display_get_xdisplay(gdk_display_get_default()), gtk_status_icon_get_x11_window_id(plug), &hints); + #endif + #endif + + gtk_status_icon_set_visible(plug, TRUE); + + g_signal_connect(G_OBJECT(plug), "button-press-event", G_CALLBACK(cb_button_press), (gpointer)this); + g_signal_connect(G_OBJECT(plug), "popup-menu", G_CALLBACK(cb_menu), (gpointer)this); + g_signal_connect(G_OBJECT(plug), "scroll-event", G_CALLBACK(cb_scroll), (gpointer)this); + + _visible_count++; + + usleep(10000); // BUG: Embedding too fast sometimes fails with GTK+ + } + } + else + { + if (plug) + { + GB.Post((void (*)())hide_icon, (intptr_t)plug); + plug = NULL; + _visible_count--; + } + } +} + +void gTrayIcon::exit() +{ + gTrayIcon *icon; + + while((icon = get(0))) + delete icon; +} + +bool gTrayIcon::hasSystemTray() +{ + #ifdef GTK3 + return PLATFORM.Desktop.HasSystemTray(); + #else + return X11_get_system_tray() != 0; + #endif +} diff --git a/gb.gtk/src/gtrayicon.h b/gb.gtk/src/gtrayicon.h new file mode 100644 index 00000000..069a8b0a --- /dev/null +++ b/gb.gtk/src/gtrayicon.h @@ -0,0 +1,84 @@ +/*************************************************************************** + + gtrayicon.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTRAYICON_H +#define __GTRAYICON_H + +class gTrayIcon +{ +public: +//"Properties" + gTrayIcon(); + ~gTrayIcon(); + + void *hFree; + + char *key(); + gPicture* picture() { return _icon; } + void setPicture(gPicture *pic); + char* tooltip() const { return _tooltip; } + void setTooltip(char *txt); + bool isVisible(); + void setVisible(bool vl); + +//"Methods" + void destroy(); + void show() { setVisible(true); } + void hide() { setVisible(false); } + int loopLevel() { return _loopLevel; } + +//"Static" + + static int count() { return g_list_length(trayicons); } + static int visibleCount() { return _visible_count; } + static gTrayIcon *get(int index) { return (gTrayIcon *)g_list_nth_data(trayicons, index); } + static void exit(); + static gPicture *defaultIcon(); + static bool hasSystemTray(); + +//"Private" + GtkStatusIcon *plug; + gPicture *_icon; + int _iconw, _iconh; + char *_tooltip; + bool onHide; + int _loopLevel; + gPicture *getIcon() { return _icon ? _icon : defaultIcon(); } + void updateTooltip(); + void updatePicture(); + + static GList *trayicons; + static gPicture *_default_icon; + +private: + + static int _visible_count; +}; + +// Callbacks +void CB_trayicon_click(gTrayIcon *sender, int button); +void CB_trayicon_scroll(gTrayIcon *sender); +void CB_trayicon_menu(gTrayIcon *sender); +void CB_trayicon_destroy(gTrayIcon *sender); + +#endif diff --git a/gb.gtk/src/gtree.cpp b/gb.gtk/src/gtree.cpp new file mode 100644 index 00000000..4b3ead31 --- /dev/null +++ b/gb.gtk/src/gtree.cpp @@ -0,0 +1,1287 @@ +/*************************************************************************** + + gtree.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gpicture.h" +#include "gtree.h" + +#if GTK_CHECK_VERSION(2, 12, 0) + +#define HIDDEN_COL 0 + +#else + +#define HIDDEN_COL 1 + +#endif + +static GtkTreeViewColumn* gt_tree_view_find_column(GtkTreeView *tree, int ind) +{ + GtkTreeViewColumn *col=NULL; + GList *cols; + GList *iter; + + if (!tree) return NULL; + cols=gtk_tree_view_get_columns(GTK_TREE_VIEW(tree)); + if (!cols) return NULL; + iter=g_list_nth(cols,(guint)ind + HIDDEN_COL); + if (iter) { + col=(GtkTreeViewColumn*)iter->data; + } + g_list_free(cols); + + return col; +} + +/* +static int gt_tree_view_find_index(GtkTreeView *tree, GtkTreeViewColumn *column) +{ + int index; + GList *cols; + GList *iter; + + if (!tree) return -1; + cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(tree)); + if (!cols) return -1; + + iter = cols; + index = 0; + if (HIDDEN_COL) + iter = g_list_next(iter); + + while (iter) + { + if (iter->data == (gpointer)column) + break; + iter = g_list_next(iter); + index++; + } + + g_list_free(cols); + + if (!iter) + index = -1; + + return index; +} +*/ + +/************************************************************ + +gTreeCell + +*************************************************************/ + +gTreeCell::gTreeCell() +{ + _text = NULL; + _picture = NULL; +} + +gTreeCell::~gTreeCell() +{ + setText(NULL); + setPicture(NULL); +} + +void gTreeCell::setText(const char *vl) +{ + if (_text) + g_free(_text); + + _text = vl ? g_strdup(vl) : NULL; +} + +void gTreeCell::setPicture(gPicture *vl) +{ + gPicture::assign(&_picture, vl); +} + +/************************************************************ + +gTreeRow + +*************************************************************/ + +gTreeRow::gTreeRow(gTree *tr, char *key, GtkTreeIter *iter) +{ + int count; + + data = NULL; + dataiter = iter; + tree = tr; + _key = key; + //_expanded = false; + //_editable = tr->isEditable(); + + count = tree->columnCount(); + + while (count > 0) + { + count--; + data = g_list_prepend(data, (gpointer)new gTreeCell()); + } + + if (data) + data = g_list_reverse(data); + + //fprintf(stderr, "new key: (%p) %s\n", _key, _key); +} + +gTreeRow::~gTreeRow() +{ + GList *iter=NULL; + + if (tree->onRemove) + (*tree->onRemove)(tree, _key); + + if (dataiter) gtk_tree_iter_free(dataiter); + if (data) iter=g_list_first(data); + + while (iter) + { + delete (gTreeCell*)iter->data; + iter=g_list_next(iter); + } + + if (data) g_list_free(data); + + //fprintf(stderr, "free key: (%p) %s\n", _key, _key); + g_free(_key); +} + +void gTreeRow::add() +{ + data = g_list_append(data, (gpointer)new gTreeCell()); +} + +void gTreeRow::remove() +{ + GList *iter=NULL; + gTreeCell *cell; + + if (!data) return; + + iter = g_list_last(data); + cell = (gTreeCell *)iter->data; + data = g_list_remove(data, cell); + delete cell; +} + +/*int gTreeRow::children() +{ + GtkTreeIter iter; + int ct=1; + + if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(tree->store),&iter,dataiter)) return 0; + + while ( gtk_tree_model_iter_next(GTK_TREE_MODEL(tree->store),&iter) ) ct++; + return ct; +}*/ + +void gTreeRow::update() +{ + GtkTreePath *path; + + path=gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (path) + { + gtk_tree_model_row_changed (GTK_TREE_MODEL(tree->store),path,dataiter); + gtk_tree_path_free(path); + } +} + +/*char* gTreeRow::parentKey() +{ + GtkTreeIter it; + char *key; + + if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(parent->store),&it,dataiter)) return NULL; + gtk_tree_model_get(GTK_TREE_MODEL(parent->store),&it,0,&key,-1); + return key; +}*/ + +/* +void gTreeRow::setExpanded(bool ex) +{ + GtkTreePath *path; + //char *current; + + //fprintf(stderr, "setExpanded: %s = %d\n", _key, ex); + + if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(tree->store),dataiter)) + { + _expanded = ex; + return; + } + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (!path) + return; + + if (ex) + gtk_tree_view_expand_row(GTK_TREE_VIEW(tree->widget), path, false); + else + gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree->widget), path); + + gtk_tree_path_free(path); +} + +void gTreeRow::setExpanded() +{ + tree->view->lock(); + //_expanded = !_expanded; + setExpanded(_expanded); + tree->view->unlock(); +} + +bool gTreeRow::isExpanded() +{ +// GtkTreePath *path; +// bool real = false; +// +// path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); +// if (path) +// { +// real = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree->widget),path); +// gtk_tree_path_free(path); +// } + + //fprintf(stderr, "isExpanded: %s: %d (%d)\n", _key, _expanded, real); + + return _expanded; +} +*/ + +#if 0 +void gTreeRow::updateExpanded(bool ex) +{ + //GtkTreePath *path; + + /*path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (!path) + return; + + _expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree->widget),path); + gtk_tree_path_free(path);*/ + + _expanded = ex; + + //fprintf(stderr, "updateExpanded: %s = %d\n", _key, _expanded); +} +#endif + +gTreeCell* gTreeRow::get(int ind) +{ + GList *iter; + + if (!data) + return NULL; + + iter = g_list_nth(data, ind); + if (!iter) + return NULL; + + return (gTreeCell*)iter->data; +} + +void gTreeRow::ensureVisible() +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (path) + { + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree->widget), path, NULL, false, 0.0, 0.0); + gtk_tree_path_free(path); + } +} + +#if 0 +char *gTreeRow::next() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + gtk_tree_path_next(path); + return tree->pathToKey(path); +} + +char *gTreeRow::prev() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + gtk_tree_path_prev(path); + return tree->pathToKey(path); +} + +char *gTreeRow::child() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + gtk_tree_path_down(path); + return tree->pathToKey(path); +} + +char *gTreeRow::last() +{ + GtkTreePath* path; + GtkTreeIter iter, save_iter; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(tree->store), &iter, path)) + return NULL; + + gtk_tree_path_free(path); + + //count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(tree->store), NULL); + //if (!count) return NULL; + + //while (--count) + for(;;) + { + save_iter = iter; + if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(tree->store), &iter)) + { + iter = save_iter; + break; + } + } + + return tree->iterToKey(&iter); +} + +char *gTreeRow::parent() +{ + GtkTreeIter *iter = gtk_tree_iter_copy(dataiter); + char *key; + //GtkTreePath* path; + + if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(tree->store), iter, dataiter)) + key = NULL; + else + key = tree->iterToKey(iter); + + gtk_tree_iter_free(iter); + return key; + + /*path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + if (!gtk_tree_path_up(path)) + return NULL; + + return tree->pathToKey(path);*/ +} + +char *gTreeRow::above() +{ + char *key, *key2; + + key = prev(); + if (!key) + return parent(); + + for(;;) + { + key2 = (*tree)[key]->child(); + if (!key2) + break; + + key = key2; + + for(;;) + { + key2 = (*tree)[key]->next(); + if (!key2) + break; + key = key2; + } + } + + return key; +} + +char *gTreeRow::below() +{ + char *key, *key2; + + key = child(); + if (key) + return key; + + key = next(); + if (key) + return key; + + key = parent(); + + for(;;) + { + if (!key) + return NULL; + + key2 = (*tree)[key]->next(); + if (key2) + return key2; + + key = (*tree)[key]->parent(); + } +} + + +void gTreeRow::rect(int *x, int *y, int *w, int *h) +{ + GtkTreePath* path; + GdkRectangle rect; + GtkTreeViewColumn *column; + gint depth; + gint size; + gint margin; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return; + + column = gt_tree_view_find_column(GTK_TREE_VIEW(tree->widget), tree->columnCount() - 1); + gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tree->widget), path, column, &rect); + depth = gtk_tree_path_get_depth(path); + gtk_tree_path_free(path); + + gtk_widget_style_get (tree->widget, + "expander-size", &size, + "vertical-separator", &margin, + (void *)NULL); + + size += 4; // Constant that is hard-coded into GTK+ source code + + if (!tree->hasExpanders()) + depth--; + + *x = depth * size; + *w = rect.x + rect.width - *x; + *h = rect.height + margin; + *y = rect.y; +} + +void gTreeRow::moveFirst() +{ + gtk_tree_store_move_after(tree->store, dataiter, NULL); +} + +void gTreeRow::moveAfter(char *key) +{ + gTreeRow *row; + + if (!key || !*key) + { + moveFirst(); + return; + } + + row = tree->getRow(key); + if (!row) + return; + if (strcmp(row->parent(), parent())) + return; + gtk_tree_store_move_after(tree->store, dataiter, row->dataiter); +} + +void gTreeRow::moveLast() +{ + gtk_tree_store_move_before(tree->store, dataiter, NULL); +} + +void gTreeRow::moveBefore(char *key) +{ + gTreeRow *row; + + if (!key || !*key) + { + moveLast(); + return; + } + + row = tree->getRow(key); + if (!row) + return; + if (strcmp(row->parent(), parent())) + return; + gtk_tree_store_move_before(tree->store, dataiter, row->dataiter); +} + +void gTreeRow::startRename() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return; + + //tree->view->setFocus(); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree->widget), path, gt_tree_view_find_column(GTK_TREE_VIEW(tree->widget), 0), true); + gtk_tree_path_free(path); +} +#endif + +/************************************************************ + +gTree + +*************************************************************/ + +static gboolean gTree_equal(char *a,char *b) +{ + return !strcasecmp(a,b); +} + +#if 0 +static void cb_tree_edited(GtkCellRendererText *renderer, gchar *spath, gchar *new_text, gTree *tree) +{ + gTreeView *view = tree->view; + + if (!tree->_edited_row) + return; + + view->setItemText(tree->_edited_row, new_text); + view->emit(SIGNAL(view->onRename), tree->_edited_row); + tree->_edited_row = NULL; +} + +static void cb_tree_started(GtkCellRenderer *renderer, GtkCellEditable *editable, gchar *spath, gTree *tree) +{ + GtkTreePath *path = gtk_tree_path_new_from_string(spath); + if (path) + tree->_edited_row = tree->pathToKey(path); + else + tree->_edited_row = NULL; +} + +static void cb_tree_canceled(GtkCellRendererText *renderer, gTree *tree) +{ + gTreeView *view = tree->view; + view->emit(SIGNAL(view->onCancel), tree->_edited_row); + tree->_edited_row = NULL; +} + +static void cb_column_clicked(GtkTreeViewColumn *col, gTree *tree) +{ + int index = gt_tree_view_find_index(GTK_TREE_VIEW(tree->widget), col); + + if (index == tree->getSortColumn()) + tree->setSortAscending(!tree->isSortAscending()); + else + tree->setSortColumn(index); +} +#endif + +static gint tree_compare(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gTree *tree) +{ + bool def = true; + int comp; + char *ka = tree->iterToKey(a); + char *kb = tree->iterToKey(b); + const char *ta, *tb; + + //fprintf(stderr, "ka = '%s' kb = '%s'\n", ka, kb); + + //if (tree->view && tree->view->onCompare) + // def = tree->view->onCompare(tree->view, ka, kb, &comp); + + if (def) + { + ta = tree->getRow(ka)->get(tree->getSortColumn())->text(); + if (!ta) ta = ""; + tb = tree->getRow(kb)->get(tree->getSortColumn())->text(); + if (!tb) tb = ""; + + //fprintf(stderr, "ta = '%s' tb = '%s'\n", ta, tb); + + comp = g_utf8_collate(ta, tb); + } + + if (!tree->isSortAscending()) + comp = (-comp); + + return comp; +} + + +void gTree::showExpanders() +{ + #if GTK_CHECK_VERSION(2, 12, 0) + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(widget), true); + #else + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(widget), gt_tree_view_find_column(GTK_TREE_VIEW(widget), 0)); + #endif + _expander = true; +} + +gTree::gTree() +{ + onRemove = NULL; + + datakey = g_hash_table_new((GHashFunc)g_str_hash,(GEqualFunc)gTree_equal); + + store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); + widget = NULL; + + //_editable = false; + _resizable = false; + _auto_resize = false; + //_edited_row = NULL; + _sorted = false; + _ascending = true; + _sort_column = 0; + _init_sort = false; + _sort_dirty = false; + _expander = false; + _no_click = 0; + +#if 0 + if (view) + { + #if GTK_CHECK_VERSION(2, 12, 0) + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(widget), false); + #else + GtkTreeViewColumn *column = gtk_tree_view_column_new(); + //gtk_tree_view_column_pack_start(column,rgraph,false); + //gtk_tree_view_column_pack_start(column,rtext,true); + //gtk_tree_view_column_set_cell_data_func(column,rgraph,(GtkTreeCellDataFunc)tree_cell_graph,(gpointer)this,NULL); + //gtk_tree_view_column_set_cell_data_func(column,rtext,(GtkTreeCellDataFunc)tree_cell_text,(gpointer)this,NULL); + gtk_tree_view_column_set_visible(column, false); + gtk_tree_view_append_column(GTK_TREE_VIEW(widget), column); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(widget), column); + #endif + + rgraph = gtk_cell_renderer_pixbuf_new(); + g_object_ref_sink(rgraph); + rtext = gtk_cell_renderer_text_new(); + g_object_ref_sink(rtext); + + g_signal_connect(G_OBJECT(rtext), "edited", G_CALLBACK(cb_tree_edited), (gpointer)this); + g_signal_connect(G_OBJECT(rtext), "editing-started", G_CALLBACK(cb_tree_started), (gpointer)this); + g_signal_connect(G_OBJECT(rtext), "editing-canceled", G_CALLBACK(cb_tree_canceled), (gpointer)this); + //addColumn(); + setAutoResize(true); + } +#endif +} + +gTree::~gTree() +{ + clear(); + g_hash_table_destroy(datakey); + /*if (view) + { + g_object_unref(rgraph); + g_object_unref(rtext); + }*/ +} + +void gTree::clear() +{ + char *key; + + while ((key = firstRow())) + removeRow(key); +} + +/* +void gTree::clear(char *parent) +{ + gTreeRow *row; + char *child; + + row = (*this)[parent]; + if (!row) + return; + + lock(); + while((child = row->child())) + removeRow(child); + unlock(); +} +*/ + +/* +int gTree::visibleWidth() +{ + GdkRectangle rect; + gint w,h; + + gtk_tree_view_get_visible_rect (GTK_TREE_VIEW(widget),&rect); + gtk_tree_view_convert_bin_window_to_widget_coords(GTK_TREE_VIEW(widget),rect.width,rect.height,&w,&h); + return w; +} + +int gTree::visibleHeight() +{ + GdkRectangle rect; + gint w,h; + + gtk_tree_view_get_visible_rect (GTK_TREE_VIEW(widget),&rect); + gtk_tree_view_convert_bin_window_to_widget_coords(GTK_TREE_VIEW(widget),rect.width,rect.height,&w,&h); + return h; +} +*/ + +char *gTree::iterToKey(GtkTreeIter *iter) +{ + char *key; + gtk_tree_model_get(GTK_TREE_MODEL(store), iter, 1, &key, -1); + return key; +} + +char *gTree::pathToKey(GtkTreePath *path, bool free) +{ + GtkTreeIter iter; + char *key; + + if (!path) return NULL; + + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path)) + key = iterToKey(&iter); + else + key = NULL; + + if (free) gtk_tree_path_free(path); + + return key; +} + +char* gTree::cursor() +{ + GtkTreePath *path; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL); + return pathToKey(path); +} + +void gTree::setCursor(char *vl) +{ + GtkTreePath *path; + gTreeRow *row = getRow(vl); + + if (!row) return; + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store),row->dataiter); + if (path) + { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget),path,NULL,false); + gtk_tree_path_free(path); + } +} + +char *gTree::firstRow() +{ + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) + return NULL; + + return iterToKey(&iter); +} + +#if 0 +char *gTree::lastRow() +{ + GtkTreeIter iter; + gint count; + + if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) + return NULL; + + count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL); + if (!count) return NULL; + + if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, count - 1)) + return NULL; + + //while (--count) + // gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); + + return iterToKey(&iter); +} +#endif + +bool gTree::rowExists(char *key) +{ + return (bool)(key && *key && g_hash_table_lookup(datakey,(gconstpointer)key)); +} + +bool gTree::rowSelected(char *key) +{ + GtkTreeSelection *sel; + gTreeRow *row=(gTreeRow*)g_hash_table_lookup(datakey,(gconstpointer)key); + if (!row) return false; + + sel=gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (!sel) return false; + return gtk_tree_selection_iter_is_selected(sel,row->dataiter); + +} + +void gTree::setRowSelected(char *key,bool vl) +{ + GtkTreeSelection *sel; + gTreeRow *row = (gTreeRow*)g_hash_table_lookup(datakey, (gconstpointer)key); + if (!row) return; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (!sel) return; + + _no_click++; + if (vl) + gtk_tree_selection_select_iter(sel,row->dataiter); + else + gtk_tree_selection_unselect_iter(sel,row->dataiter); + _no_click--; +} + +/* +bool gTree::isRowEditable(char *key) +{ + gTreeRow *row = getRow(key); + if (!row) + return false; + else + return row->isEditable(); +} + +void gTree::setRowEditable(char *key, bool vl) +{ + gTreeRow *row = getRow(key); + if (!row) + return; + + row->setEditable(vl); +} +*/ + +int gTree::rowCount() +{ + return g_hash_table_size(datakey); +} + + +bool gTree::removeRow(char *key) +{ + gTreeRow *row; + + if (!key || !*key) return false; + + row=(gTreeRow*)g_hash_table_lookup(datakey, (gconstpointer)key); + if (!row) return false; + + //fprintf(stderr, "gTree::removeRow: '%s'\n", key); + + //while((child = row->child())) + // removeRow(child); + + //fprintf(stderr, "gTree::removeRow: '%s' removed\n", key); + + g_hash_table_remove (datakey, (gconstpointer)key); + gtk_tree_store_remove(store, row->dataiter); + delete row; + + return true; +} + +gTreeRow* gTree::getRow(char *key) const +{ + if (!key) + return NULL; + else + return (gTreeRow*)g_hash_table_lookup (datakey,(gconstpointer)key); +} + +gTreeRow* gTree::addRow(char *key, char *after, bool before) +{ + GtkTreeIter iter; + GtkTreeIter *piter; + gTreeRow *row,*aft = NULL; + char *buf; + + if (!key) + return NULL; + + if (g_hash_table_lookup (datakey,(gconstpointer)key)) return NULL; + + if (after) + { + aft=(gTreeRow*)g_hash_table_lookup (datakey,(gconstpointer)after); + if (!aft) return NULL; + } + + piter = NULL; + + //fprintf(stderr, "[0]: %d %d\n", columnResizable(0), gtk_tree_view_column_get_sizing(gt_tree_view_find_column(GTK_TREE_VIEW(widget), 0))); + + if (aft) + { + if (before) + gtk_tree_store_insert_before(store, &iter, piter, aft->dataiter); + else + gtk_tree_store_insert_after(store, &iter, piter, aft->dataiter); + } + else + gtk_tree_store_append (store, &iter, piter); + + buf = g_strdup(key); // Will be freed by ~gTreeRow() + + row = new gTreeRow(this, buf, gtk_tree_iter_copy(&iter)); + + g_hash_table_insert(datakey, (gpointer)buf, (gpointer)row); + gtk_tree_store_set(store, &iter, 1, buf, -1); + + //fprintf(stderr, "[0]: -> %d %d\n", columnResizable(0), gtk_tree_view_column_get_sizing(gt_tree_view_find_column(GTK_TREE_VIEW(widget), 0))); + + return row; +} + +#if 0 +static void tree_cell_text(GtkTreeViewColumn *col,GtkCellRenderer *cell,GtkTreeModel *md,GtkTreeIter *iter,gTree *Tr) +{ + gTreeRow *row = NULL; + gTreeCell *data; + const char *buf = ""; + char *key; + int index = -1; + double align; + + key = Tr->iterToKey(iter); + if (key) + row= (gTreeRow*)g_hash_table_lookup(Tr->datakey, (gpointer)key); + + if (row) + { + index = gt_tree_view_find_index(GTK_TREE_VIEW(Tr->widget), col); + data = row->get(index); + if (data) + { + if (data->text()) + buf = data->text(); + } + } + + align = gtk_tree_view_column_get_alignment(col); + + g_object_set(G_OBJECT(cell), + "text", buf, + "editable", false, + "xalign", align, + (void *)NULL); +} + +static void tree_cell_graph(GtkTreeViewColumn *col,GtkCellRenderer *cell,GtkTreeModel *md,GtkTreeIter *iter,gTree *Tr) +{ + gTreeRow *row=NULL; + gTreeCell *data; + GdkPixbuf *buf=NULL; + char *key; + + key = Tr->iterToKey(iter); + if (key) + row=(gTreeRow*)g_hash_table_lookup(Tr->datakey,(gpointer)key); + + if (row) + { + data = row->get(gt_tree_view_find_index(GTK_TREE_VIEW(Tr->widget), col)); + if (data) + { + buf = data->picture() ? data->picture()->getPixbuf() : NULL; + } + } + + g_object_set(G_OBJECT(cell),"pixbuf",buf,(void *)NULL); + +} +#endif + +static void gTree_addColumn(char *key,gTreeRow *value,gpointer data) +{ + value->add(); +} + +void gTree::addColumn() +{ + g_hash_table_foreach(datakey,(GHFunc)gTree_addColumn,NULL); +} + +static void gTree_removeColumn(char *key,gTreeRow *value,gpointer data) +{ + value->remove(); +} + +void gTree::removeColumn() +{ + int vl; + + if (!(vl = columnCount()) ) + return; + g_hash_table_foreach(datakey,(GHFunc)gTree_removeColumn,NULL); +} + +int gTree::columnCount() +{ + return 1; +} + +#if 0 +char* gTree::columnName(int ind) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return NULL; + return (char *)gtk_tree_view_column_get_title(col); +} + +void gTree::setColumnName(int ind,char *vl) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return; + gtk_tree_view_column_set_title(col,(const gchar*)vl); +} + +bool gTree::columnVisible(int ind) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return false; + return gtk_tree_view_column_get_visible(col); + +} + +void gTree::setColumnVisible(int ind,bool vl) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return; + gtk_tree_view_column_set_visible(col,vl); +} + +bool gTree::columnResizable(int ind) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return false; + return gtk_tree_view_column_get_resizable(col); +} + +void gTree::setColumnResizable(int ind, bool vl) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + if (!col) return; + gtk_tree_view_column_set_resizable(col,vl); +} + +int gTree::columnAlignment(int ind) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + + if (!col) + return ALIGN_LEFT; + else + return gt_to_alignment(gtk_tree_view_column_get_alignment(col)); +} + +void gTree::setColumnAlignment(int ind, int align) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + + if (!col) + return; + + gtk_tree_view_column_set_alignment(col, gt_from_alignment(align)); +} + +int gTree::columnWidth(int ind) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + int w; + + if (!col) + return 0; + + w = gtk_tree_view_column_get_width(col); + if (w == 0) + w = gtk_tree_view_column_get_fixed_width(col); + + return w; +} + +void gTree::setColumnWidth(int ind, int w) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + + if (!col) + return; + + if (w > 0) + { + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(col, w); + } + else + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + + setColumnResizable(ind, isResizable()); +} +#endif + +#if 0 +bool gTree::headers() +{ + if (!widget) return false; + return gtk_tree_view_get_headers_visible(GTK_TREE_VIEW(widget)); +} + +void gTree::setHeaders(bool vl) +{ + if (!widget) return; + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(widget),vl); +} + +void gTree::setResizable(bool vl) +{ + int i; + + for (i = 0; i < columnCount(); i++) + gtk_tree_view_column_set_resizable(gt_tree_view_find_column(GTK_TREE_VIEW(widget), i), vl); + + _resizable = vl; +} +#endif + +void gTree::setAutoResize(bool vl) +{ + int i; + + for (i = 0; i < columnCount(); i++) + gtk_tree_view_column_set_sizing(gt_tree_view_find_column(GTK_TREE_VIEW(widget), i), vl ? GTK_TREE_VIEW_COLUMN_AUTOSIZE : GTK_TREE_VIEW_COLUMN_FIXED); + + _auto_resize = vl; +} + +void gTree::selectAll() +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + + if (!sel) + return; + + _no_click++; + gtk_tree_selection_select_all(sel); + _no_click--; +} + +void gTree::unselectAll() +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (!sel) + return; + + _no_click++; + gtk_tree_selection_unselect_all(sel); + _no_click--; +} + +void gTree::setSorted(bool v) +{ + if (v == _sorted) + return; + + _sorted = v; + _sort_column = v ? 0 : -1; + if (!_sorted) + { + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING); + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), NULL, NULL, NULL); + } + updateSort(); +} + +void gTree::setSortColumn(int v) +{ + if (_sort_column < 0) + setSorted(false); + else + { + _sort_column = v; + _ascending = true; + updateSort(); + } +} + +void gTree::setSortAscending(bool v) +{ + _ascending = v; + updateSort(); +} + +void gTree::sort() +{ + if (!_sorted) + return; + + // BM: force the store to be sorted + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), (GtkTreeIterCompareFunc)tree_compare, this, NULL); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING); + _sort_dirty = false; +} + +void gTree::updateSort() +{ + sortLater(); +} + +static gboolean tree_sort_later(gTree *tree) +{ + tree->sort(); + return FALSE; +} + +void gTree::sortLater() +{ + if (!isSorted() || _sort_dirty) + return; + + _sort_dirty = true; + g_timeout_add(0, (GSourceFunc)tree_sort_later, this); +} + diff --git a/gb.gtk/src/gtree.h b/gb.gtk/src/gtree.h new file mode 100644 index 00000000..56fb715a --- /dev/null +++ b/gb.gtk/src/gtree.h @@ -0,0 +1,190 @@ +/*************************************************************************** + + gtree.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTREE_H +#define __GTREE_H + +#include <gtk/gtk.h> + +class gTree; + +class gTreeCell +{ +public: + gTreeCell(); + ~gTreeCell(); + + char *text() { return _text; } + gPicture *picture() { return _picture; } + + void setText(const char *vl); + void setPicture(gPicture *vl); + +private: + char *_text; + gPicture *_picture; +}; + +class gTreeRow +{ +public: + GList *data; + GtkTreeIter *dataiter; + gTree *tree; + + gTreeRow(gTree *tr, char *key, GtkTreeIter *iter); + ~gTreeRow(); + + void add(); + void remove(); + void update(); + //int children(); + char *key() { return _key; } + + //void setExpanded(); + //void setExpanded(bool ex); + //bool isExpanded(); + void ensureVisible(); + + gTreeCell* get(int ind); + + /*char *next(); + char *prev(); + char *child(); + char *last(); + char *parent(); + char *above(); + char *below(); + + void moveFirst(); + void moveLast(); + void moveAfter(char *key); + void moveBefore(char *key); + + void rect(int *x, int *y, int *w, int *h); + void updateExpanded(bool ex);*/ + + //bool isEditable() { return _editable; } + //void setEditable(bool vl) { _editable = vl; } + + //void startRename(); + +private: + char *_key; + char *_parent; + //bool _expanded; + //bool _editable; +}; + +class gTree +{ +public: + GtkWidget *widget; + GtkTreeStore *store; + GtkCellRenderer *rtext; + GtkCellRenderer *rgraph; + GHashTable *datakey; + void (*onRemove)(gTree *tree, char *key); + char *_edited_row; + unsigned _editable : 1; + unsigned _resizable : 1; + unsigned _auto_resize : 1; + unsigned _expander : 1; + unsigned _sorted : 1; + unsigned _ascending : 1; + unsigned _init_sort : 1; + unsigned _sort_dirty : 1; + int _no_click; + int _sort_column; + + gTree(); + ~gTree(); + + //General + //int visibleWidth(); + //int visibleHeight(); + char* cursor(); + void setCursor(char *vl); + void selectAll(); + void unselectAll(); + //bool isEditable() { return _editable; } + //void setEditable(bool vl) { _editable = vl; } + //bool isRenaming() const { return _edited_row != NULL; } + + // Rows + gTreeRow* addRow(char *key, char *after = NULL, bool before = false); + gTreeRow* getRow(char *key) const; + gTreeRow* operator[](char *key) const { return getRow(key); } + + bool removeRow(char *key); + int rowCount(); + bool rowExists(char *key); + bool rowSelected(char *key); + void setRowSelected(char *key,bool vl); + //bool isRowEditable(char *key); + //void setRowEditable(char *key, bool vl); + char *firstRow(); + //char *lastRow(); + + void clear(); + //void clear(char *parent); + + // Columns + //bool headers(); + //void setHeaders(bool vl); + /*bool isResizable() { return _resizable; } + void setResizable(bool vl);*/ + bool isAutoResize() { return _auto_resize; } + void setAutoResize(bool vl); + void addColumn(); + void removeColumn(); + int columnCount(); + /*char *columnName(int ind); + void setColumnName(int ind,char *vl); + bool columnVisible(int ind); + void setColumnVisible(int ind, bool vl); + bool columnResizable(int ind); + void setColumnResizable(int ind, bool vl); + int columnAlignment(int ind); + void setColumnAlignment(int ind, int a); + int columnWidth(int col); + void setColumnWidth(int col, int w);*/ + + bool isSorted() { return _sorted; } + void setSorted(bool v); + int getSortColumn() { return _sort_column; } + void setSortColumn(int v); + bool isSortAscending() { return _ascending; } + void setSortAscending(bool v); + + char *pathToKey(GtkTreePath *path, bool free = true); + char *iterToKey(GtkTreeIter *iter); + + void showExpanders(); + bool hasExpanders() { return _expander; } + void sortLater(); + void sort(); + void updateSort(); +}; + +#endif diff --git a/gb.gtk/src/kentities.h b/gb.gtk/src/kentities.h new file mode 100644 index 00000000..5a6d569b --- /dev/null +++ b/gb.gtk/src/kentities.h @@ -0,0 +1,860 @@ +/*************************************************************************** + + kentities.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* ANSI-C code produced by gperf version 3.0.1 */ +/* Command-line: gperf -a -L ANSI-C -E -C -c -o -t -k '*' -Nkde_findEntity -D -Hhash_Entity -Wwordlist_Entity -s 2 kentities.gperf */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." +#endif + +#line 1 "kentities.gperf" + +/* This file is part of the KDE libraries + + Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + ---------------------------------------------------------------------------- + + kentities.gperf: input file to generate a hash table for entities + kentities.c: DO NOT EDIT! generated by the command + "gperf -a -L "ANSI-C" -C -G -c -o -t -k '*' -Nkde_findEntity -D -s 2 khtmlentities.gperf > entities.c" + from kentities.gperf + + $Id: kentities.c 465272 2005-09-29 09:47:40Z mueller $ +*/ +#line 31 "kentities.gperf" +struct entity { + const char *name; + int code; +}; +/* maximum key range = 924, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash_Entity (const char *str, unsigned int len) +{ + static const unsigned short asso_values[] = + { + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 0, + 145, 120, 0, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 125, 315, 35, 60, 325, + 5, 5, 0, 130, 932, 10, 0, 20, 110, 120, + 100, 0, 45, 40, 10, 135, 0, 932, 0, 15, + 50, 932, 932, 932, 932, 932, 932, 0, 15, 70, + 0, 20, 200, 250, 295, 10, 285, 5, 140, 90, + 15, 5, 65, 195, 5, 100, 25, 25, 130, 0, + 75, 190, 30, 35, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932 + }; + int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[7]]; + /*FALLTHROUGH*/ + case 7: + hval += asso_values[(unsigned char)str[6]]; + /*FALLTHROUGH*/ + case 6: + hval += asso_values[(unsigned char)str[5]]; + /*FALLTHROUGH*/ + case 5: + hval += asso_values[(unsigned char)str[4]]; + /*FALLTHROUGH*/ + case 4: + hval += asso_values[(unsigned char)str[3]]; + /*FALLTHROUGH*/ + case 3: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + hval += asso_values[(unsigned char)str[1]+1]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +#ifdef __GNUC__ +__inline +#endif +const struct entity * +kde_findEntity (const char *str, unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 280, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 8, + MIN_HASH_VALUE = 8, + MAX_HASH_VALUE = 931 + }; + + static const struct entity wordlist_Entity[] = + { +#line 116 "kentities.gperf" + {"and", 0x22a5}, +#line 264 "kentities.gperf" + {"rho", 0x03c1}, +#line 142 "kentities.gperf" + {"darr", 0x2193}, +#line 143 "kentities.gperf" + {"dcaron", 0x10f}, +#line 257 "kentities.gperf" + {"rarr", 0x2192}, +#line 258 "kentities.gperf" + {"rcaron", 0x0159}, +#line 95 "kentities.gperf" + {"Tcaron", 0x0164}, +#line 185 "kentities.gperf" + {"int", 0x222b}, +#line 214 "kentities.gperf" + {"ncaron", 0x0148}, +#line 161 "kentities.gperf" + {"eta", 0x03b7}, +#line 150 "kentities.gperf" + {"ecaron", 0x011b}, +#line 94 "kentities.gperf" + {"Tau", 0x03a4}, +#line 299 "kentities.gperf" + {"uarr", 0x2191}, +#line 288 "kentities.gperf" + {"tcaron", 0x0165}, +#line 48 "kentities.gperf" + {"Chi", 0x03a7}, +#line 312 "kentities.gperf" + {"zcaron", 0x017e}, +#line 90 "kentities.gperf" + {"Rho", 0x03a1}, +#line 188 "kentities.gperf" + {"isin", 0x2208}, +#line 293 "kentities.gperf" + {"thorn", 0x00fe}, +#line 46 "kentities.gperf" + {"Ccaron", 0x010c}, +#line 287 "kentities.gperf" + {"tau", 0x03c4}, +#line 96 "kentities.gperf" + {"Theta", 0x0398}, +#line 91 "kentities.gperf" + {"Scaron", 0x0160}, +#line 110 "kentities.gperf" + {"acute", 0x00b4}, +#line 89 "kentities.gperf" + {"Rcaron", 0x0158}, +#line 106 "kentities.gperf" + {"Zcaron", 0x017d}, +#line 115 "kentities.gperf" + {"amp", 38}, +#line 220 "kentities.gperf" + {"nsub", 0x2284}, +#line 290 "kentities.gperf" + {"theta", 0x03b8}, +#line 289 "kentities.gperf" + {"there4", 0x2234}, +#line 243 "kentities.gperf" + {"phi", 0x03c6}, +#line 238 "kentities.gperf" + {"para", 0x00b6}, +#line 109 "kentities.gperf" + {"acirc", 0x00e2}, +#line 50 "kentities.gperf" + {"Dcaron", 0x010e}, +#line 132 "kentities.gperf" + {"chi", 0x03c7}, +#line 224 "kentities.gperf" + {"ocirc", 0x00f4}, +#line 180 "kentities.gperf" + {"icirc", 0x00ee}, +#line 128 "kentities.gperf" + {"ccaron", 0x010d}, +#line 251 "kentities.gperf" + {"psi", 0x03c8}, +#line 186 "kentities.gperf" + {"iota", 0x03b9}, +#line 254 "kentities.gperf" + {"radic", 0x221a}, +#line 231 "kentities.gperf" + {"or", 0x22a6}, +#line 218 "kentities.gperf" + {"not", 0x00ac}, +#line 152 "kentities.gperf" + {"ecirc", 0x00ea}, +#line 239 "kentities.gperf" + {"part", 0x2202}, +#line 300 "kentities.gperf" + {"ucirc", 0x00fb}, +#line 84 "kentities.gperf" + {"Phi", 0x03a6}, +#line 69 "kentities.gperf" + {"Lambda", 0x039b}, +#line 269 "kentities.gperf" + {"scaron", 0x0161}, +#line 229 "kentities.gperf" + {"omicron", 0x03bf}, +#line 88 "kentities.gperf" + {"QUOT", 34}, +#line 219 "kentities.gperf" + {"notin", 0x2209}, +#line 70 "kentities.gperf" + {"LT", 60}, +#line 87 "kentities.gperf" + {"Psi", 0x03a8}, +#line 72 "kentities.gperf" + {"Ncaron", 0x0147}, +#line 62 "kentities.gperf" + {"GT", 62}, +#line 227 "kentities.gperf" + {"oline", 0x203e}, +#line 222 "kentities.gperf" + {"nu", 0x03bd}, +#line 296 "kentities.gperf" + {"trade", 0x2122}, +#line 71 "kentities.gperf" + {"Mu", 0x039c}, +#line 127 "kentities.gperf" + {"cap", 0x2229}, +#line 270 "kentities.gperf" + {"sdot", 0x22c5}, +#line 190 "kentities.gperf" + {"kappa", 0x03ba}, +#line 68 "kentities.gperf" + {"Kappa", 0x039a}, +#line 108 "kentities.gperf" + {"aacute", 0x00e1}, +#line 164 "kentities.gperf" + {"euro", 0x20ac}, +#line 223 "kentities.gperf" + {"oacute", 0x00f3}, +#line 205 "kentities.gperf" + {"lt", 60}, +#line 195 "kentities.gperf" + {"larr", 0x2190}, +#line 179 "kentities.gperf" + {"iacute", 0x00ed}, +#line 249 "kentities.gperf" + {"prod", 0x220f}, +#line 247 "kentities.gperf" + {"pound", 0x00a3}, +#line 104 "kentities.gperf" + {"Yacute", 0x00dd}, +#line 259 "kentities.gperf" + {"rceil", 0x2309}, +#line 149 "kentities.gperf" + {"eacute", 0x00e9}, +#line 302 "kentities.gperf" + {"uml", 0x00a8}, +#line 206 "kentities.gperf" + {"macr", 0x00af}, +#line 137 "kentities.gperf" + {"crarr", 0x21b5}, +#line 298 "kentities.gperf" + {"uacute", 0x00fa}, +#line 265 "kentities.gperf" + {"rlm", 0x200f}, +#line 212 "kentities.gperf" + {"nabla", 0x2207}, +#line 187 "kentities.gperf" + {"iquest", 0x00bf}, +#line 158 "kentities.gperf" + {"ensp", 0x2002}, +#line 160 "kentities.gperf" + {"equiv", 0x2261}, +#line 233 "kentities.gperf" + {"ordm", 0x00ba}, +#line 121 "kentities.gperf" + {"atilde", 0x00e3}, +#line 156 "kentities.gperf" + {"emsp", 0x2003}, +#line 61 "kentities.gperf" + {"Gamma", 0x0393}, +#line 235 "kentities.gperf" + {"otilde", 0x00f5}, +#line 148 "kentities.gperf" + {"dol", 0x0024}, +#line 77 "kentities.gperf" + {"Ocirc", 0x00d4}, +#line 47 "kentities.gperf" + {"Ccedil", 0x00c7}, +#line 38 "kentities.gperf" + {"Acirc", 0x00c2}, +#line 221 "kentities.gperf" + {"ntilde", 0x00f1}, +#line 216 "kentities.gperf" + {"ne", 0x2260}, +#line 64 "kentities.gperf" + {"Icirc", 0x00ce}, +#line 211 "kentities.gperf" + {"mu", 0x03bc}, +#line 66 "kentities.gperf" + {"Iota", 0x0399}, +#line 98 "kentities.gperf" + {"Ucirc", 0x00db}, +#line 292 "kentities.gperf" + {"thinsp", 0x2009}, +#line 201 "kentities.gperf" + {"loz", 0x25ca}, +#line 250 "kentities.gperf" + {"prop", 0x221d}, +#line 74 "kentities.gperf" + {"Nu", 0x039d}, +#line 124 "kentities.gperf" + {"beta", 0x03b2}, +#line 184 "kentities.gperf" + {"infin", 0x221e}, +#line 129 "kentities.gperf" + {"ccedil", 0x00e7}, +#line 80 "kentities.gperf" + {"Omicron", 0x039f}, +#line 277 "kentities.gperf" + {"sub", 0x2282}, +#line 256 "kentities.gperf" + {"raquo", 0x00bb}, +#line 139 "kentities.gperf" + {"curren", 0x00a4}, +#line 213 "kentities.gperf" + {"nbsp", 0x00a0}, +#line 260 "kentities.gperf" + {"rdquo", 0x201d}, +#line 236 "kentities.gperf" + {"otimes", 0x2297}, +#line 117 "kentities.gperf" + {"ang", 0x2220}, +#line 313 "kentities.gperf" + {"zeta", 0x03b6}, +#line 267 "kentities.gperf" + {"rsquo", 0x2019}, +#line 266 "kentities.gperf" + {"rsaquo", 0x203a}, +#line 123 "kentities.gperf" + {"bdquo", 0x201e}, +#line 192 "kentities.gperf" + {"lambda", 0x03bb}, +#line 138 "kentities.gperf" + {"cup", 0x222a}, +#line 278 "kentities.gperf" + {"sube", 0x2286}, +#line 125 "kentities.gperf" + {"brvbar", 0x00a6}, +#line 174 "kentities.gperf" + {"gt", 62}, +#line 107 "kentities.gperf" + {"Zeta", 0x0396}, +#line 76 "kentities.gperf" + {"Oacute", 0x00d3}, +#line 37 "kentities.gperf" + {"Aacute", 0x00c1}, +#line 103 "kentities.gperf" + {"Xi", 0x039e}, +#line 255 "kentities.gperf" + {"rang", 0x232a}, +#line 248 "kentities.gperf" + {"prime", 0x2032}, +#line 63 "kentities.gperf" + {"Iacute", 0x00cd}, +#line 228 "kentities.gperf" + {"omega", 0x03c9}, +#line 97 "kentities.gperf" + {"Uacute", 0x00da}, +#line 284 "kentities.gperf" + {"sup", 0x2283}, +#line 280 "kentities.gperf" + {"sup1", 0x00b9}, +#line 183 "kentities.gperf" + {"image", 0x2111}, +#line 217 "kentities.gperf" + {"ni", 0x220b}, +#line 272 "kentities.gperf" + {"shy", 0x00ad}, +#line 118 "kentities.gperf" + {"apos", 0x0027}, +#line 134 "kentities.gperf" + {"clubs", 0x2663}, +#line 307 "kentities.gperf" + {"weierp", 0x2118}, +#line 232 "kentities.gperf" + {"ordf", 0x00aa}, +#line 73 "kentities.gperf" + {"Ntilde", 0x00d1}, +#line 131 "kentities.gperf" + {"cent", 0x00a2}, +#line 196 "kentities.gperf" + {"lceil", 0x2308}, +#line 285 "kentities.gperf" + {"supe", 0x2287}, +#line 155 "kentities.gperf" + {"empty", 0x2205}, +#line 82 "kentities.gperf" + {"Otilde", 0x00d5}, +#line 279 "kentities.gperf" + {"sum", 0x2211}, +#line 176 "kentities.gperf" + {"harr", 0x2194}, +#line 86 "kentities.gperf" + {"Prime", 0x2033}, +#line 43 "kentities.gperf" + {"Atilde", 0x00c3}, +#line 140 "kentities.gperf" + {"dArr", 0x21d3}, +#line 202 "kentities.gperf" + {"lrm", 0x200e}, +#line 253 "kentities.gperf" + {"rArr", 0x21d2}, +#line 151 "kentities.gperf" + {"eague", 0x00e9}, +#line 200 "kentities.gperf" + {"lowast", 0x2217}, +#line 41 "kentities.gperf" + {"AMP", 38}, +#line 242 "kentities.gperf" + {"perp", 0x22a5}, +#line 198 "kentities.gperf" + {"le", 0x2264}, +#line 162 "kentities.gperf" + {"eth", 0x00f0}, +#line 261 "kentities.gperf" + {"real", 0x211c}, +#line 165 "kentities.gperf" + {"exist", 0x2203}, +#line 309 "kentities.gperf" + {"yacute", 0x00fd}, +#line 244 "kentities.gperf" + {"pi", 0x03c0}, +#line 59 "kentities.gperf" + {"Eta", 0x0397}, +#line 297 "kentities.gperf" + {"uArr", 0x21d1}, +#line 54 "kentities.gperf" + {"Ecaron", 0x011a}, +#line 252 "kentities.gperf" + {"quot", 34}, +#line 308 "kentities.gperf" + {"xi", 0x03be}, +#line 122 "kentities.gperf" + {"auml", 0x00e4}, +#line 237 "kentities.gperf" + {"ouml", 0x00f6}, +#line 145 "kentities.gperf" + {"delta", 0x03b4}, +#line 189 "kentities.gperf" + {"iuml", 0x00ef}, +#line 120 "kentities.gperf" + {"asymp", 0x2248}, +#line 169 "kentities.gperf" + {"frac14", 0x00bc}, +#line 105 "kentities.gperf" + {"Yuml", 0x0178}, +#line 119 "kentities.gperf" + {"aring", 0x00e5}, +#line 163 "kentities.gperf" + {"euml", 0x00eb}, +#line 194 "kentities.gperf" + {"laquo", 0x00ab}, +#line 240 "kentities.gperf" + {"percnt", 0x0025}, +#line 85 "kentities.gperf" + {"Pi", 0x03a0}, +#line 306 "kentities.gperf" + {"uuml", 0x00fc}, +#line 197 "kentities.gperf" + {"ldquo", 0x201c}, +#line 246 "kentities.gperf" + {"plusmn", 0x00b1}, +#line 314 "kentities.gperf" + {"zwj", 0x200d}, +#line 136 "kentities.gperf" + {"copy", 0x00a9}, +#line 204 "kentities.gperf" + {"lsquo", 0x2018}, +#line 203 "kentities.gperf" + {"lsaquo", 0x2039}, +#line 271 "kentities.gperf" + {"sect", 0x00a7}, +#line 268 "kentities.gperf" + {"sbquo", 0x201a}, +#line 135 "kentities.gperf" + {"cong", 0x2245}, +#line 305 "kentities.gperf" + {"uring", 0x016f}, +#line 310 "kentities.gperf" + {"yen", 0x00a5}, +#line 315 "kentities.gperf" + {"zwnj", 0x200c}, +#line 79 "kentities.gperf" + {"Omega", 0x03a9}, +#line 209 "kentities.gperf" + {"middot", 0x00b7}, +#line 166 "kentities.gperf" + {"fnof", 0x0192}, +#line 55 "kentities.gperf" + {"Ecirc", 0x00ca}, +#line 263 "kentities.gperf" + {"rfloor", 0x230b}, +#line 283 "kentities.gperf" + {"sup3", 0x00b3}, +#line 93 "kentities.gperf" + {"THORN", 0x00de}, +#line 276 "kentities.gperf" + {"spades", 0x2660}, +#line 193 "kentities.gperf" + {"lang", 0x2329}, +#line 130 "kentities.gperf" + {"cedil", 0x00b8}, +#line 157 "kentities.gperf" + {"endash", 0x2013}, +#line 126 "kentities.gperf" + {"bull", 0x2022}, +#line 51 "kentities.gperf" + {"Delta", 0x0394}, +#line 133 "kentities.gperf" + {"circ", 0x02c6}, +#line 215 "kentities.gperf" + {"ndash", 0x2013}, +#line 154 "kentities.gperf" + {"emdash", 0x2014}, +#line 281 "kentities.gperf" + {"supl", 0x00b9}, +#line 282 "kentities.gperf" + {"sup2", 0x00b2}, +#line 172 "kentities.gperf" + {"gamma", 0x03b3}, +#line 147 "kentities.gperf" + {"divide", 0x00f7}, +#line 173 "kentities.gperf" + {"ge", 0x2265}, +#line 144 "kentities.gperf" + {"deg", 0x00b0}, +#line 114 "kentities.gperf" + {"alpha", 0x03b1}, +#line 112 "kentities.gperf" + {"agrave", 0x00e0}, +#line 262 "kentities.gperf" + {"reg", 0x00ae}, +#line 208 "kentities.gperf" + {"micro", 0x00b5}, +#line 226 "kentities.gperf" + {"ograve", 0x00f2}, +#line 52 "kentities.gperf" + {"ETH", 0x00d0}, +#line 182 "kentities.gperf" + {"igrave", 0x00ec}, +#line 291 "kentities.gperf" + {"thetasym", 0x03d1}, +#line 191 "kentities.gperf" + {"lArr", 0x21d0}, +#line 230 "kentities.gperf" + {"oplus", 0x2295}, +#line 294 "kentities.gperf" + {"tilde", 0x02dc}, +#line 153 "kentities.gperf" + {"egrave", 0x00e8}, +#line 275 "kentities.gperf" + {"sim", 0x223c}, +#line 146 "kentities.gperf" + {"diams", 0x2666}, +#line 301 "kentities.gperf" + {"ugrave", 0x00f9}, +#line 245 "kentities.gperf" + {"piv", 0x03d6}, +#line 83 "kentities.gperf" + {"Ouml", 0x00d6}, +#line 53 "kentities.gperf" + {"Eacute", 0x00c9}, +#line 44 "kentities.gperf" + {"Auml", 0x00c4}, +#line 159 "kentities.gperf" + {"epsilon", 0x03b5}, +#line 67 "kentities.gperf" + {"Iuml", 0x00cf}, +#line 170 "kentities.gperf" + {"frac34", 0x00be}, +#line 304 "kentities.gperf" + {"upsilon", 0x03c5}, +#line 102 "kentities.gperf" + {"Uuml", 0x00dc}, +#line 181 "kentities.gperf" + {"iexcl", 0x00a1}, +#line 42 "kentities.gperf" + {"Aring", 0x00c5}, +#line 207 "kentities.gperf" + {"mdash", 0x2014}, +#line 101 "kentities.gperf" + {"Uring", 0x016e}, +#line 241 "kentities.gperf" + {"permil", 0x2030}, +#line 210 "kentities.gperf" + {"minus", 0x2212}, +#line 168 "kentities.gperf" + {"frac12", 0x00bd}, +#line 295 "kentities.gperf" + {"times", 0x00d7}, +#line 75 "kentities.gperf" + {"OElig", 0x0152}, +#line 36 "kentities.gperf" + {"AElig", 0x00c6}, +#line 286 "kentities.gperf" + {"szlig", 0x00df}, +#line 45 "kentities.gperf" + {"Beta", 0x0392}, +#line 171 "kentities.gperf" + {"frasl", 0x2044}, +#line 141 "kentities.gperf" + {"dagger", 0x2020}, +#line 199 "kentities.gperf" + {"lfloor", 0x230a}, +#line 311 "kentities.gperf" + {"yuml", 0x00ff}, +#line 167 "kentities.gperf" + {"forall", 0x2200}, +#line 234 "kentities.gperf" + {"oslash", 0x00f8}, +#line 78 "kentities.gperf" + {"Ograve", 0x00d2}, +#line 40 "kentities.gperf" + {"Alpha", 0x0391}, +#line 39 "kentities.gperf" + {"Agrave", 0x00c0}, +#line 65 "kentities.gperf" + {"Igrave", 0x00cc}, +#line 99 "kentities.gperf" + {"Ugrave", 0x00d9}, +#line 111 "kentities.gperf" + {"aelig", 0x00e6}, +#line 49 "kentities.gperf" + {"Dagger", 0x2021}, +#line 100 "kentities.gperf" + {"Upsilon", 0x03a5}, +#line 225 "kentities.gperf" + {"oelig", 0x0153}, +#line 175 "kentities.gperf" + {"hArr", 0x21d4}, +#line 303 "kentities.gperf" + {"upsih", 0x03d2}, +#line 177 "kentities.gperf" + {"hearts", 0x2665}, +#line 57 "kentities.gperf" + {"Eague", 0x00c9}, +#line 92 "kentities.gperf" + {"Sigma", 0x03a3}, +#line 81 "kentities.gperf" + {"Oslash", 0x00d8}, +#line 60 "kentities.gperf" + {"Euml", 0x00cb}, +#line 113 "kentities.gperf" + {"alefsym", 0x2135}, +#line 273 "kentities.gperf" + {"sigma", 0x03c3}, +#line 56 "kentities.gperf" + {"Egrave", 0x00c8}, +#line 58 "kentities.gperf" + {"Epsilon", 0x0395}, +#line 178 "kentities.gperf" + {"hellip", 0x2026}, +#line 274 "kentities.gperf" + {"sigmaf", 0x03c2} + }; + + static const short lookup[] = + { + -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, 2, + -1, 3, -1, -1, 4, -1, 5, -1, -1, -1, + -1, 6, -1, 7, -1, -1, 8, -1, 9, -1, + -1, 10, -1, 11, 12, -1, 13, -1, 14, -1, + -1, 15, -1, 16, 17, 18, 19, -1, 20, -1, + 21, 22, -1, -1, -1, 23, 24, -1, -1, -1, + -1, 25, -1, 26, 27, 28, 29, -1, 30, 31, + 32, 33, -1, 34, -1, 35, -1, -1, -1, -1, + 36, 37, -1, 38, 39, 40, -1, 41, 42, -1, + 43, -1, -1, -1, 44, 45, -1, -1, -1, -1, + -1, -1, -1, 46, -1, -1, 47, -1, -1, -1, + -1, 48, 49, -1, 50, 51, -1, 52, 53, -1, + -1, 54, 55, -1, -1, 56, -1, 57, -1, -1, + 58, -1, 59, 60, 61, 62, -1, -1, -1, -1, + 63, 64, -1, -1, 65, -1, 66, 67, -1, 68, + -1, 69, -1, -1, 70, 71, 72, -1, -1, -1, + 73, 74, -1, 75, 76, 77, 78, -1, 79, -1, + 80, 81, -1, -1, 82, 83, -1, -1, -1, 84, + -1, 85, -1, -1, 86, 87, 88, -1, 89, -1, + 90, 91, -1, -1, -1, 92, 93, 94, -1, -1, + 95, -1, 96, -1, 97, 98, -1, -1, -1, -1, + -1, 99, -1, -1, -1, -1, -1, -1, 100, 101, + -1, -1, 102, -1, 103, 104, 105, 106, 107, -1, + 108, 109, -1, -1, 110, 111, 112, -1, 113, 114, + 115, 116, -1, -1, -1, 117, 118, -1, 119, 120, + -1, 121, -1, -1, -1, -1, -1, 122, -1, 123, + -1, 124, -1, -1, -1, -1, 125, 126, -1, 127, + 128, 129, -1, -1, -1, 130, 131, -1, 132, 133, + 134, -1, 135, 136, 137, 138, 139, -1, -1, 140, + -1, 141, -1, -1, 142, 143, -1, -1, -1, 144, + 145, 146, -1, 147, 148, 149, 150, -1, -1, 151, + -1, -1, -1, 152, 153, 154, 155, -1, 156, 157, + -1, -1, 158, 159, -1, -1, -1, -1, -1, 160, + 161, 162, 163, 164, 165, -1, 166, -1, -1, 167, + -1, -1, 168, -1, 169, -1, -1, -1, -1, 170, + 171, -1, -1, -1, 172, 173, 174, -1, -1, 175, + 176, -1, -1, -1, 177, 178, 179, 180, -1, 181, + 182, 183, -1, 184, 185, 186, 187, -1, -1, 188, + 189, -1, -1, -1, 190, 191, -1, -1, 192, 193, + 194, 195, -1, -1, 196, 197, 198, -1, -1, 199, + 200, 201, -1, -1, 202, 203, 204, -1, -1, 205, + 206, -1, -1, -1, 207, 208, 209, -1, -1, 210, + -1, -1, -1, -1, 211, -1, -1, -1, -1, -1, + 212, 213, 214, 215, -1, 216, 217, -1, 218, -1, + 219, 220, -1, 221, -1, -1, 222, -1, 223, 224, + 225, -1, -1, -1, -1, 226, 227, -1, 228, -1, + 229, 230, -1, 231, 232, -1, 233, -1, -1, 234, + -1, -1, 235, -1, 236, -1, 237, 238, -1, 239, + 240, -1, -1, -1, -1, 241, -1, -1, -1, -1, + 242, -1, -1, -1, -1, 243, 244, -1, -1, -1, + 245, 246, -1, -1, -1, 247, -1, -1, -1, -1, + 248, -1, -1, -1, -1, 249, -1, -1, -1, -1, + 250, -1, -1, -1, 251, 252, 253, -1, -1, -1, + -1, 254, -1, -1, 255, -1, 256, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 257, -1, -1, -1, -1, 258, -1, -1, -1, + 259, 260, -1, -1, -1, -1, 261, -1, -1, -1, + -1, 262, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 263, 264, 265, -1, -1, + 266, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 267, -1, -1, -1, -1, -1, + 268, 269, -1, -1, -1, -1, -1, -1, -1, -1, + 270, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 271, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 272, -1, -1, 273, + -1, -1, -1, -1, -1, -1, -1, 274, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 275, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 276, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 277, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 278, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 279 + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + int key = hash_Entity (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + int index = lookup[key]; + + if (index >= 0) + { + const char *s = wordlist_Entity[index].name; + + if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') + return &wordlist_Entity[index]; + } + } + } + return 0; +} +#line 316 "kentities.gperf" + + diff --git a/gb.gtk/src/main.cpp b/gb.gtk/src/main.cpp new file mode 100644 index 00000000..34eeaec1 --- /dev/null +++ b/gb.gtk/src/main.cpp @@ -0,0 +1,698 @@ +/*************************************************************************** + + main.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include <stdio.h> + +#include "main.h" +#include "gb.image.h" +#include "gb.gtk.h" +#include "watcher.h" +#include "gglarea.h" +#include "gkey.h" + +#include "x11.h" +#include "CScreen.h" +#include "CStyle.h" +#include "CDraw.h" +#include "CConst.h" +#include "CColor.h" +#include "CFont.h" +#include "CKey.h" +#include "CPicture.h" +#include "CImage.h" +#include "CClipboard.h" +#include "CMouse.h" +#include "CDialog.h" +#include "CWatcher.h" +#include "CWidget.h" +#include "CDrawingArea.h" +#include "CContainer.h" +#include "CPanel.h" +#include "CMenu.h" +#include "CWindow.h" +#include "CButton.h" +#include "CTextBox.h" +#include "CTextArea.h" +#include "CSlider.h" +#include "CTabStrip.h" +#include "CTrayIcon.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "canimation.h" + +#include <gtk/gtk.h> +#include <string.h> + +GB_CLASS CLASS_Control; +GB_CLASS CLASS_ContainerChildren; +GB_CLASS CLASS_UserControl; +GB_CLASS CLASS_UserContainer; +GB_CLASS CLASS_Picture; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_DrawingArea; +GB_CLASS CLASS_Menu; +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Printer; +GB_CLASS CLASS_SvgImage; + +static void hook_lang(char *lang, int rtl1); +static bool hook_error(int code, char *error, char *where, bool can_ignore); +static void hook_quit(void); +static void hook_main(int *argc, char ***argv); +static void hook_timer(GB_TIMER *timer,bool on); +static void hook_wait(int duration); +static void hook_post(void); +static int hook_loop(); +static void hook_watch(int fd, int type, void *callback, intptr_t param); + +static bool _post_check = false; +static bool _must_check_quit = false; + +static bool _application_keypress = false; +static GB_FUNCTION _application_keypress_func; + +static void *_old_hook_main; + +bool MAIN_display_x11 = FALSE; +int MAIN_scale = 0; +bool MAIN_debug_busy = false; +bool MAIN_rtl = false; + +//------------------------------------------------------------------------- + +static void GTK_CreateControl(CWIDGET *ob, void *parent, GtkWidget *widget, uint flags) +{ + gControl *ctrl; + bool recreate; + + if (!parent) + { + recreate = true; + ctrl = ob->widget; + ctrl->parent()->remove(ctrl); + ctrl->createBorder(widget); + } + else + { + recreate = false; + ctrl = new gControl(CONTAINER(parent)); + ctrl->border = widget; + } + + ctrl->widget = ctrl->border; + InitControl(ctrl, ob); + ctrl->realize(); + + if (flags & CCF_HAS_INPUT_METHOD) + ctrl->_has_input_method = TRUE; + + if (recreate) + ctrl->updateGeometry(true); +} + +static GtkWidget *GTK_CreateGLArea(void *_object, void *parent, void (*init)(GtkWidget *)) +{ + gControl *ctrl = new gGLArea(CONTAINER(parent), init); + InitControl(ctrl, (CWIDGET *)_object); + //WIDGET->onExpose = Darea_Expose; + return ctrl->widget; +} + +static void *GTK_CreatePicture(cairo_surface_t *surf, int w, int h) +{ + gPicture *p = new gPicture(surf); + + if (w > 0 && h > 0) + { + gPicture *p2 = p->stretch(w, h, true); + p->unref(); + p = p2; + } + + return CPICTURE_create(p); +} + +static int GTK_GetDesktopScale(void) +{ + return MAIN_scale; +} + + +//------------------------------------------------------------------------- + +extern "C" +{ + +const GB_INTERFACE *GB_PTR EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; + +static void declare_tray_icon() +{ + GB.Component.Declare(TrayIconsDesc); + GB.Component.Declare(TrayIconDesc); +} + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ScreenDesc, + ScreensDesc, + DesktopDesc, + ApplicationDesc, + StyleDesc, + SelectDesc, + AlignDesc, + ArrangeDesc, + BorderDesc, + ScrollDesc, + DirectionDesc, + CColorDesc, + CFontsDesc, + CFontDesc, + CKeyDesc, + CImageDesc, + CPictureDesc, + AnimationDesc, + CClipboardDesc, + CDragDesc, + CCursorDesc, + CMouseDesc, + CPointerDesc, + CDialogDesc, + CWatcherDesc, + CWidgetDesc, + ContainerChildrenDesc, + ContainerDesc, + CDrawingAreaDesc, + UserControlDesc, + UserContainerDesc, + CPanelDesc, + CHBoxDesc, + CVBoxDesc, + CHPanelDesc, + CVPanelDesc, + CMenuDesc, + CMenuChildrenDesc, + CWindowMenusDesc, + CWindowControlsDesc, + CWindowDesc, + CWindowsDesc, + CFormDesc, + SliderDesc, + ScrollBarDesc, + CButtonDesc, + CToggleButtonDesc, + CCheckBoxDesc, + CRadioButtonDesc, + CToolButtonDesc, + CTextBoxSelectionDesc, + CTextBoxDesc, + CTextAreaDesc, + CTextAreaSelectionDesc, + CTabStripDesc, + CTabStripContainerDesc, + CTabStripContainerChildrenDesc, + CPluginDesc, + PrinterDesc, + SvgImageDesc, + NULL +}; + +#ifdef GTK3 +void *GB_GTK3_1[] EXPORT = +#else +void *GB_GTK_1[] EXPORT = +#endif +{ + (void *)GTK_INTERFACE_VERSION, + (void *)GTK_CreateControl, + (void *)GTK_CreateGLArea, + (void *)GTK_CreatePicture, + (void *)GTK_GetDesktopScale, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.draw,gb.gui.base"; + +int EXPORT GB_INIT(void) +{ + char *env; + + env = getenv("GB_GUI_BUSY"); + if (env && atoi(env)) + MAIN_debug_busy = true; + + putenv((char *)"GTK_OVERLAY_SCROLLING=0"); + + GB.Hook(GB_HOOK_QUIT, (void *)hook_quit); + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + GB.Hook(GB_HOOK_WAIT, (void *)hook_wait); + GB.Hook(GB_HOOK_TIMER,(void *)hook_timer); + GB.Hook(GB_HOOK_WATCH,(void *)hook_watch); + GB.Hook(GB_HOOK_POST,(void*)hook_post); + GB.Hook(GB_HOOK_ERROR,(void*)hook_error); + GB.Hook(GB_HOOK_LANG,(void*)hook_lang); + GB.Hook(GB_HOOK_LOOP, (void *)hook_loop); + + GB.Component.Load("gb.draw"); + GB.Component.Load("gb.image"); + GB.Component.Load("gb.gui.base"); + + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + + GB.Signal.MustCheck(SIGCHLD); + + IMAGE.SetDefaultFormat(GB_IMAGE_RGBA); + DRAW_init(); + + CWatcher::init(); + + CLASS_Control = GB.FindClass("Control"); + CLASS_ContainerChildren = GB.FindClass("ContainerChildren"); + CLASS_UserControl = GB.FindClass("UserControl"); + CLASS_UserContainer = GB.FindClass("UserContainer"); + CLASS_Window = GB.FindClass("Window"); + CLASS_Menu = GB.FindClass("Menu"); + CLASS_Picture = GB.FindClass("Picture"); + //CLASS_Drawing = GB.FindClass("Drawing"); + CLASS_DrawingArea = GB.FindClass("DrawingArea"); + CLASS_Printer = GB.FindClass("Printer"); + CLASS_Image = GB.FindClass("Image"); + CLASS_SvgImage = GB.FindClass("SvgImage"); + +#if !defined(GLIB_VERSION_2_36) + g_type_init(); +#endif /* !defined(GLIB_VERSION_2_36) */ + + hook_lang(GB.System.Language(), GB.System.IsRightToLeft()); + + return -1; +} + +void EXPORT GB_EXIT() +{ + CWatcher::exit(); +} + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (MAIN_display_x11) + { + if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)gdk_x11_display_get_xdisplay(gdk_display_get_default()); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)gdk_x11_get_default_root_xwindow(); + return TRUE; + } + } + + if (!strcasecmp(key, "GET_HANDLE")) + { + *value = (void *)CWIDGET_get_handle; + return TRUE; + } + else if (!strcasecmp(key, "SET_EVENT_FILTER")) + { + *value = (void *)gApplication::setEventFilter; + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)(intptr_t)gtk_get_current_event_time(); //gdk_x11_display_get_user_time(gdk_display_get_default()); + return TRUE; + } + else if (!strcasecmp(key, "DECLARE_TRAYICON")) + { + *value = (void *)declare_tray_icon; + return TRUE; + } + else + return FALSE; +} + +static void activate_main_window(intptr_t) +{ + if (gMainWindow::_active) + gtk_window_present(GTK_WINDOW(gMainWindow::_active->topLevel()->border)); +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + static GtkWidget *save_popup_grab = NULL; + + switch(signal) + { + case GB_SIGNAL_DEBUG_BREAK: + if (gApplication::_popup_grab) + { + save_popup_grab = gApplication::_popup_grab; + gApplication::ungrabPopup(); + } + break; + + case GB_SIGNAL_DEBUG_FORWARD: + //while (qApp->activePopupWidget()) + // delete qApp->activePopupWidget(); + if (gdk_display_get_default()) + gdk_display_sync(gdk_display_get_default()); + break; + + case GB_SIGNAL_DEBUG_CONTINUE: + GB.Post((GB_CALLBACK)activate_main_window, 0); + if (save_popup_grab) + { + gApplication::_popup_grab = save_popup_grab; + save_popup_grab = NULL; + gApplication::grabPopup(); + } + break; + } +} + +} // extern "C" + +void hook_quit (void) +{ + GB_FUNCTION func; + + while (!gApplication::processInputEvent()); + while (gApplication::eventsPending()) + gtk_main_iteration(); + + if (GB.ExistClass("TrayIcons")) + { + if (!GB.GetFunction(&func, (void *)GB.FindClass("TrayIcons"), "DeleteAll", NULL, NULL)) + GB.Call(&func, 0, FALSE); + } + + if (!GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_Quit", NULL, NULL)) + GB.Call(&func, 0, FALSE); + + CWINDOW_delete_all(true); + gControl::postDelete(); + + //CWatcher::Clear(); + gApplication::exit(); + + #ifdef GDK_WINDOWING_X11 + if (MAIN_display_x11) + X11_exit(); + #endif +} + +static bool global_key_event_handler(int type) +{ + GB.Call(&_application_keypress_func, 0, FALSE); + return GB.Stopped(); +} + +static void hook_main(int *argc, char ***argv) +{ + static bool init = false; + char *env; + + if (init) + return; + + env = getenv("GB_X11_INIT_THREADS"); + if (env && atoi(env)) + XInitThreads(); + + gtk_init(argc, argv); + gApplication::init(argc, argv); + gApplication::setDefaultTitle(GB.Application.Title()); + CAPPLICATION_MiddleClickPaste = gApplication::hasMiddleClickPaste(); + + gApplication::onEnterEventLoop = GB.Debug.EnterEventLoop; + gApplication::onLeaveEventLoop = GB.Debug.LeaveEventLoop; + + MAIN_scale = gDesktop::scale(); + #ifdef GDK_WINDOWING_X11 + #ifdef GTK3 + if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) + #endif + { + X11_init(gdk_x11_display_get_xdisplay(gdk_display_get_default()), gdk_x11_get_default_root_xwindow()); + MAIN_display_x11 = TRUE; + } + #endif + + if (GB.GetFunction(&_application_keypress_func, (void *)GB.Application.StartupClass(), "Application_KeyPress", "", "") == 0) + { + _application_keypress = true; + gApplication::onKeyEvent = global_key_event_handler; + } + + init = true; + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + +/*static void raise_timer(GB_TIMER *timer) +{ + GB.RaiseTimer(timer); + GB.Unref(POINTER(&timer)); +}*/ + +typedef + struct { + int source; + GTimer *timer; + int timeout; + } + MyTimerId; + +gboolean hook_timer_function(GB_TIMER *timer) +{ + intptr_t old_id = timer->id; + + GB.RaiseTimer(timer); + + if (timer->id == old_id) // If the event handler has removed the timer or restarted it, do nothing, just let the current source be removed. + { + MyTimerId *id = (MyTimerId *)timer->id; + GTimer *t = id->timer; + int elapsed = (int)(g_timer_elapsed(t, NULL) * 1000) - id->timeout; + int next = timer->delay - elapsed; + if (next < 10) + next = 10; + id->timeout = next; + g_timer_start(t); + id->source = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, next, (GSourceFunc)hook_timer_function, (gpointer)timer, NULL); + //fprintf(stderr, "elapsed = %d delay = %d next = %d\n", elapsed, timer->delay, next); + } + + return false; +} + +static void hook_timer(GB_TIMER *timer, bool on) +{ + if (timer->id) + { + MyTimerId *id = (MyTimerId *)timer->id; + g_source_remove(id->source); + g_timer_destroy(id->timer); + g_free(id); + timer->id = 0; + } + + if (on) + { + MyTimerId *id = g_new(MyTimerId, 1); + id->timer = g_timer_new(); + id->timeout = timer->delay; + id->source = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, timer->delay, (GSourceFunc)hook_timer_function, (gpointer)timer, NULL); + timer->id = (intptr_t)id; + } + else + MAIN_check_quit(); +} + +static void hook_post(void) +{ + _post_check = true; +} + +void MAIN_check_quit() +{ + _must_check_quit = true; +} + +static int hook_loop() +{ + gControl::postDelete(); + _must_check_quit = true; + + for(;;) + { + if (_must_check_quit) + { + if (gApplication::mustQuit()) + break; + if (CWINDOW_must_quit() && CWatcher::count() == 0 && gTrayIcon::visibleCount() == 0 && !GB.HasActiveTimer()) + break; + _must_check_quit = false; + } + MAIN_do_iteration(false); + } + + hook_quit(); + + return 0; +} + +static void hook_wait(int duration) +{ + static bool _warning = FALSE; + + if (gDrawingArea::inAnyDrawEvent()) + { + GB.Error("Wait is forbidden during a repaint event"); + return; + } + + if (duration != -1 && gKey::isValid()) + { + if (!_warning) + { + fprintf(stderr, "gb.gtk3: warning: calling the event loop during a keyboard event handler is ignored\n"); + _warning = TRUE; + } + return; + } + + if (duration >= 0) + { + MAIN_do_iteration(true); + } + else if (duration == -1) + { + bool d = gApplication::disableInputEvents(true); + MAIN_do_iteration(true); // To process deferred routines + while (gApplication::eventsPending()) + MAIN_do_iteration(false); + gApplication::disableInputEvents(d); + } + else if (duration == -2) + MAIN_do_iteration(false); +} + +static void hook_watch(int fd, int type, void *callback, intptr_t param) +{ + CWatcher::Add(fd,type,callback,param); +} + +static bool hook_error(int code, char *error, char *where, bool can_ignore) +{ + gMainWindow *active; + GtkWidget *dialog; + char *msg; + char scode[16]; + gint res; + + if (code > 0) + sprintf(scode, " (#%d)", code); + else + *scode = 0; + + msg = g_strconcat("<b>This application has raised an unexpected error and must abort.</b>\n\n", error, scode, ".\n\n<tt>", where, "</tt>", NULL); + + dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, NULL); + gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), msg); + if (can_ignore) + gtk_dialog_add_button(GTK_DIALOG(dialog), GB.Translate("Ignore"), 2); + gtk_dialog_add_button(GTK_DIALOG(dialog), GB.Translate("Close"), 1); + + active = gDesktop::activeWindow(); + if (active) + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(active->border)); + + res = gtk_dialog_run(GTK_DIALOG(dialog)); + + gtk_widget_destroy(dialog); + + g_free(msg); + return (res == 2); +} + +static void cb_update_lang(gControl *control) +{ + if (control->isVisible() && control->isContainer()) + ((gContainer*)control)->performArrange(); +} + +static void hook_lang(char *lang, int rtl) +{ + MAIN_rtl = rtl; + + if (rtl) + gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); + else + gtk_widget_set_default_direction(GTK_TEXT_DIR_LTR); + + gApplication::forEachControl(cb_update_lang); +} + +void MAIN_do_iteration_just_events() +{ + if (gApplication::eventsPending()) + { + while (!gApplication::processInputEvent()); + gtk_main_iteration_do(false); + } +} + +void MAIN_do_iteration(bool do_not_block) +{ + gApplication::_loopLevel++; + + while (!gApplication::processInputEvent()); + + if (do_not_block) + { + if (gApplication::eventsPending()) + gtk_main_iteration(); + } + else + gtk_main_iteration_do(true); + + gApplication::_loopLevel--; + + gContainer::postArrange(); + + if (_post_check) + { + _post_check = false; + GB.CheckPost(); + } + + gControl::postDelete(); +} + diff --git a/gb.gtk/src/main.h b/gb.gtk/src/main.h new file mode 100644 index 00000000..5ac1eb88 --- /dev/null +++ b/gb.gtk/src/main.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + main.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb.geom.h" + +#include "widgets.h" +#include "gb.gtk.h" +#include "CWidget.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +extern IMAGE_INTERFACE IMAGE; +extern GEOM_INTERFACE GEOM; + +extern GB_CLASS CLASS_Control; +extern GB_CLASS CLASS_ContainerChildren; +extern GB_CLASS CLASS_UserControl; +extern GB_CLASS CLASS_UserContainer; +extern GB_CLASS CLASS_Picture; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_DrawingArea; +extern GB_CLASS CLASS_Menu; +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Printer; +extern GB_CLASS CLASS_SvgImage; + +extern bool MAIN_debug_busy; +extern bool MAIN_rtl; +extern bool MAIN_display_x11; +#endif + +#define GB (*GB_PTR) + +void MAIN_do_iteration(bool do_not_block); +void MAIN_do_iteration_just_events(); +void MAIN_check_quit(); + +#endif diff --git a/gb.gtk/src/opengl/Makefile.am b/gb.gtk/src/opengl/Makefile.am new file mode 100644 index 00000000..48468dae --- /dev/null +++ b/gb.gtk/src/opengl/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.gtk.opengl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gtk.opengl.la + +gb_gtk_opengl_la_LIBADD = @GTK_LIB@ @GTKOPENGL_LIB@ +gb_gtk_opengl_la_LDFLAGS = -module @LD_FLAGS@ @GTK_LDFLAGS@ @GTKOPENGL_LDFLAGS@ +gb_gtk_opengl_la_CPPFLAGS = @GTK_INC@ @GTKOPENGL_INC@ + +gb_gtk_opengl_la_SOURCES = \ + main.h main.c \ + c_glarea.h c_glarea.c + diff --git a/gb.gtk/src/opengl/c_glarea.c b/gb.gtk/src/opengl/c_glarea.c new file mode 100644 index 00000000..f8bf09e7 --- /dev/null +++ b/gb.gtk/src/opengl/c_glarea.c @@ -0,0 +1,185 @@ +/*************************************************************************** + + c_glarea.c + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_GLAREA_C + +#include <GL/gl.h> +#include "c_glarea.h" + +//-- GLArea ----------------------------------------------------------------- + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +static void cb_init_ext(GtkWidget *widget) +{ + GdkGLConfig *config; // la config qui va aller avec + + static const gint attrList[] = { // les paramètres de la config + GDK_GL_DOUBLEBUFFER, + GDK_GL_RGBA, + GDK_GL_RED_SIZE, 1, + GDK_GL_GREEN_SIZE, 1, + GDK_GL_BLUE_SIZE, 1, + GDK_GL_ALPHA_SIZE, 1, + GDK_GL_DEPTH_SIZE, 1, + GDK_GL_ATTRIB_LIST_NONE }; + + config = gdk_gl_config_new(attrList); + + /* ajout du support Opengl */ + gtk_widget_set_gl_capability(widget, config, NULL, TRUE, GDK_GL_RGBA_TYPE); +} + +static void init_control(void *_object) +{ + GdkGLContext *context; + GdkGLDrawable *surface; + GtkWidget *widget = THIS->widget; + + if (THIS->init) + return; + + //fprintf(stderr, "init_control: %p\n", THIS); + + /* recuperation du contexte et de la surface de notre widget */ + context = gtk_widget_get_gl_context(widget); + surface = gtk_widget_get_gl_drawable(widget); + + /* activation du contexte */ + if (gdk_gl_drawable_gl_begin(surface, context)) + { + GL.Init(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + GB.Raise(THIS, EVENT_Open, 0); + GB.Raise(THIS, EVENT_Resize, 0); + gdk_gl_drawable_gl_end(surface); // désactivation du contexte + } + + THIS->init = TRUE; + + return; +} + +static gboolean cb_reshape_ext(GtkWidget *widget, GdkEventConfigure *ev, void *_object) +{ + /* recuperation du contexte et de la surface de notre widget */ + GdkGLContext *context; + GdkGLDrawable *surface; + + if (!THIS->init) + return TRUE; + + //fprintf(stderr, "cb_reshape_ext: %p\n", THIS); + + if (!gtk_widget_is_gl_capable(widget)) + { + fprintf(stderr, "not capable!\n"); + return TRUE; + } + + context = gtk_widget_get_gl_context(widget); + surface = gtk_widget_get_gl_drawable(widget); + + /* activation du contexte */ + if(gdk_gl_drawable_gl_begin(surface, context)) + { + //reshape(ev−>height,ev−>width); // redimensionnement Opengl + GB.Raise(THIS, EVENT_Resize, 0); + gdk_gl_drawable_gl_end(surface); // désactivation du contexte + } + + return TRUE; +} + +static gboolean cb_draw_ext(GtkWidget *widget, GdkEventExpose *e, void *_object) +{ + /* recuperation du contexte et de la surface de notre widget */ + GdkGLContext *context; + GdkGLDrawable *surface; + + //fprintf(stderr, "cb_draw_ext: %p\n", THIS); + + if (!gtk_widget_is_gl_capable(widget)) + { + fprintf(stderr, "not capable!\n"); + return TRUE; + } + + context = gtk_widget_get_gl_context(widget); + surface = gtk_widget_get_gl_drawable(widget); + + /* activation du contexte */ + if(gdk_gl_drawable_gl_begin(surface, context)) + { + //draw(); // dessin Opengl + init_control(THIS); + GB.Raise(THIS, EVENT_Draw, 0); + gdk_gl_drawable_swap_buffers(surface); // permutation tampons + gdk_gl_drawable_gl_end(surface); // désactivation du contexte + } + + return TRUE; +} + + +BEGIN_METHOD(GLArea_new, GB_OBJECT parent) + + THIS->widget = GTK.CreateGLArea(THIS, VARG(parent), cb_init_ext); + + if (!gtk_widget_is_gl_capable(THIS->widget)) + { + GB.Error("Unable to set OpenGL capability"); + return; + } + + g_signal_connect(G_OBJECT(THIS->widget), "configure-event", G_CALLBACK(cb_reshape_ext), (gpointer)THIS); + g_signal_connect(G_OBJECT(THIS->widget), "expose-event", G_CALLBACK(cb_draw_ext), (gpointer)THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC GLAreaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + //GB_STATIC_METHOD("_exit", NULL, GLArea_exit, NULL), + + GB_METHOD("_new", NULL, GLArea_new, "(Parent)Container;"), + //GB_METHOD("_free", NULL, GLArea_free, NULL), + //GB_METHOD("Update", NULL, GLArea_update, NULL), + //GB_METHOD("Refresh", NULL, GLArea_Refresh, NULL), + //GB_METHOD("Select", NULL, CGLAREA_select, NULL), + //GB_METHOD("Text", NULL, CGLAREA_text, "(Text)s(X)i(Y)i"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/opengl/c_glarea.h b/gb.gtk/src/opengl/c_glarea.h new file mode 100644 index 00000000..cd5eec5b --- /dev/null +++ b/gb.gtk/src/opengl/c_glarea.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_glarea.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_GLAREA_H +#define __C_GLAREA_H + +#include "main.h" + +typedef + struct { + GTK_CONTROL control; + GtkWidget *widget; + bool init; + } + CGLAREA; + +#ifndef __C_GLAREA_C +extern GB_DESC GLAreaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((GTK_CONTROL *)_object)->widget) + +//#define CGLAREA_PROPERTIES QT_WIDGET_PROPERTIES + +#endif /* __CGLAREA_CPP */ + +#endif diff --git a/gb.gtk/src/opengl/gb.gtk.opengl.component b/gb.gtk/src/opengl/gb.gtk.opengl.component new file mode 100644 index 00000000..fdb78303 --- /dev/null +++ b/gb.gtk/src/opengl/gb.gtk.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.gtk,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Stable diff --git a/gb.gtk/src/opengl/main.c b/gb.gtk/src/opengl/main.c new file mode 100644 index 00000000..1fe6a6e3 --- /dev/null +++ b/gb.gtk/src/opengl/main.c @@ -0,0 +1,61 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_glarea.h" + +GB_INTERFACE GB EXPORT; +GTK_INTERFACE GTK; +GL_INTERFACE GL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + GLAreaDesc, + NULL +}; + +static void *_old_hook_main; + +static void hook_main(int *argc, char ***argv) +{ + gtk_init(argc, argv); + gtk_gl_init(argc, argv); + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.gtk", GTK_INTERFACE_VERSION, >K); + GB.GetInterface("gb.opengl", GL_INTERFACE_VERSION, &GL); + + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.gtk/src/opengl/main.h b/gb.gtk/src/opengl/main.h new file mode 100644 index 00000000..c3dbdf32 --- /dev/null +++ b/gb.gtk/src/opengl/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.gtk.h" +#include "gb.gl.h" +#include <gtk/gtk.h> +#include <gtk/gtkgl.h> + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern GTK_INTERFACE GTK; +extern GL_INTERFACE GL; +#endif + +#endif diff --git a/gb.gtk/src/sm/bonobo-macros.h b/gb.gtk/src/sm/bonobo-macros.h new file mode 100644 index 00000000..fdf49568 --- /dev/null +++ b/gb.gtk/src/sm/bonobo-macros.h @@ -0,0 +1,100 @@ +/** + * Useful macros. + * + * Author: + * Darin Adler <darin@bentspoon.com> + * + * Copyright 2001 Ben Tea Spoons, Inc. + */ +#ifndef _BONOBO_MACROS_H_ +#define _BONOBO_MACROS_H_ + +#include <glib.h> + +G_BEGIN_DECLS + +/* Macros for defining classes. Ideas taken from Nautilus and GOB. */ + +/* Define the boilerplate type stuff to reduce typos and code size. Defines + * the get_type method and the parent_class static variable. */ + +#define BONOBO_BOILERPLATE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro, \ + register_type_macro) \ +static void type_as_function ## _class_init (type ## Class *klass); \ +static void type_as_function ## _instance_init (type *object); \ +static parent_type ## Class *parent_class = NULL; \ +static void \ +type_as_function ## _class_init_trampoline (gpointer klass, \ + gpointer data) \ +{ \ + parent_class = (parent_type ## Class *)g_type_class_ref ( \ + parent_type_macro); \ + type_as_function ## _class_init ((type ## Class *)klass); \ +} \ +GType \ +type_as_function ## _get_type (void) \ +{ \ + static GType object_type = 0; \ + if (object_type == 0) { \ + static const GTypeInfo object_info = { \ + sizeof (type ## Class), \ + NULL, /* base_init */ \ + NULL, /* base_finalize */ \ + type_as_function ## _class_init_trampoline, \ + NULL, /* class_finalize */ \ + NULL, /* class_data */ \ + sizeof (type), \ + 0, /* n_preallocs */ \ + (GInstanceInitFunc) type_as_function ## _instance_init \ + }; \ + object_type = register_type_macro \ + (type, type_as_function, corba_type, \ + parent_type, parent_type_macro); \ + } \ + return object_type; \ +} + +/* Just call the parent handler. This assumes that there is a variable + * named parent_class that points to the (duh!) parent class. Note that + * this macro is not to be used with things that return something, use + * the _WITH_DEFAULT version for that */ +#define BONOBO_CALL_PARENT(parent_class_cast, name, args) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : (void)0) + +/* Same as above, but in case there is no implementation, it evaluates + * to def_return */ +#define BONOBO_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \ + name, args, def_return) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : def_return) + + +#define BONOBO_CLASS_BOILERPLATE(type, type_as_function, \ + parent_type, parent_type_macro) \ + BONOBO_BOILERPLATE(type, type_as_function, type, \ + parent_type, parent_type_macro, \ + BONOBO_REGISTER_TYPE) +#define BONOBO_REGISTER_TYPE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + bonobo_type_unique (parent_type_macro, NULL, NULL, 0, \ + &object_info, #type) + +#define BONOBO_CLASS_BOILERPLATE_FULL(type, type_as_function, \ + corba_type, \ + parent_type, parent_type_macro) \ + BONOBO_BOILERPLATE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro, \ + BONOBO_REGISTER_TYPE_FULL) +#define BONOBO_REGISTER_TYPE_FULL(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + bonobo_type_unique (parent_type_macro, \ + POA_##corba_type##__init, \ + POA_##corba_type##__fini, \ + G_STRUCT_OFFSET (type##Class, epv), \ + &object_info, #type) + +G_END_DECLS + +#endif /* _BONOBO_MACROS_H_ */ diff --git a/gb.gtk/src/sm/gnome-client.c b/gb.gtk/src/sm/gnome-client.c new file mode 100644 index 00000000..f96d2283 --- /dev/null +++ b/gb.gtk/src/sm/gnome-client.c @@ -0,0 +1,3094 @@ +/* gnome-client.c - GNOME session management client support + * + * Copyright (C) 1998, 1999 Carsten Schaar + * All rights reserved. + * + * Author: Carsten Schaar <nhadcasc@fs-maphy.uni-hannover.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + */ +/* + @NOTATION@ +*/ + +#define _XOPEN_SOURCE 500 + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <errno.h> + +/* Must be before all other gnome includes!! */ +//#include <glib/gi18n-lib.h> + +#include "gnome-client.h" +#include "gnome-uidefs.h" +#include "gnome-ice.h" +#include "gnome-marshal.h" + +#include "libgnomeuiP.h" + +#ifdef HAVE_LIBSM +#include <X11/SM/SMlib.h> +#endif /* HAVE_LIBSM */ + +//#define DEBUG_ME + +static int _sm_desktop = -1; + +static GtkWidget *client_grab_widget = NULL; + +enum { + SAVE_YOURSELF, + DIE, + SAVE_COMPLETE, + SHUTDOWN_CANCELLED, + CONNECT, + DISCONNECT, + LAST_SIGNAL +}; + +//static void gnome_real_client_destroy (GtkObject *object); +static void gnome_real_client_finalize (GObject *object); +static void gnome_real_client_save_complete (GnomeClient *client); +static void gnome_real_client_shutdown_cancelled (GnomeClient *client); +static void gnome_real_client_connect (GnomeClient *client, + gboolean restarted); +static void gnome_real_client_disconnect (GnomeClient *client); +static void master_client_connect (GnomeClient *client, + gboolean restarted, + gpointer client_data); +static void master_client_disconnect (GnomeClient *client, + gpointer client_data); + + +static void client_unset_config_prefix (GnomeClient *client); + +static gchar** array_init_from_arg (gint argc, + gchar *argv[]); + +static gint client_signals[LAST_SIGNAL] = { 0 }; + +static const char *sm_client_id_arg_name G_GNUC_UNUSED = "-session"; +static const char *sm_client_desktop_arg_name G_GNUC_UNUSED = "-session-desktop"; +static const char *sm_config_prefix_arg_name G_GNUC_UNUSED = "--sm-config-prefix"; +#ifdef HAVE_GTK_MULTIHEAD +static const char *sm_screen G_GNUC_UNUSED = "--screen"; +#endif + +//G_GNUC_INTERNAL extern void _gnome_ui_gettext_init (gboolean bind_codeset); + +/* The master client. */ +static GnomeClient *master_client= NULL; + +static gboolean master_client_restored= FALSE; + +// BM: device grab management + +#ifdef GTK3 +static GdkDevice *gt_get_pointer() +{ +#if GTK_CHECK_VERSION(3, 22, 0) + return gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display_get_default())); +#else + return gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); +#endif +} +#endif + +static gboolean gt_pointer_is_grabbed() +{ +#ifdef GTK3 + return gdk_display_device_is_grabbed(gdk_display_get_default(), gt_get_pointer()); +#else + return gdk_pointer_is_grabbed(); +#endif +} + +static void gt_ungrab(void) +{ +#if GTK_CHECK_VERSION(3, 22, 0) + gdk_seat_ungrab(gdk_display_get_default_seat(gdk_display_get_default())); +#elif defined(GTK3) + GdkDevice *pointer = gt_get_pointer(); + GdkDevice *keyboard = gdk_device_get_associated_device(pointer); + gdk_device_ungrab(pointer, GDK_CURRENT_TIME); + gdk_device_ungrab(keyboard, GDK_CURRENT_TIME); +#else + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); +#endif +} + +void gnome_type_init() +{ + (void) gnome_interact_style_get_type (); + (void) gnome_dialog_type_get_type (); + (void) gnome_save_style_get_type (); + (void) gnome_restart_style_get_type (); + (void) gnome_client_state_get_type (); + (void) gnome_client_flags_get_type (); +} + +/*****************************************************************************/ +/* Managing the interaction keys. */ + +/* The following function handle the interaction keys. Each time an + application requests interaction an interaction key is created. If + the session manager decides, that a client may interact now, the + application is given the according interaction key. No other + interaction may take place, until the application returns the + application key. */ + +#ifdef HAVE_LIBSM + +typedef struct _InteractionKey InteractionKey; + + +struct _InteractionKey +{ + gint tag; + GnomeClient *client; + GnomeDialogType dialog_type; + gboolean in_use; + gboolean interp; + GnomeInteractFunction function; + gpointer data; + GDestroyNotify destroy; +}; + + +/* List of all existing interaction keys. */ +static GList *interact_functions = NULL; + + +static InteractionKey * +interaction_key_new (GnomeClient *client, + GnomeDialogType dialog_type, + gboolean interp, + GnomeInteractFunction function, + gpointer data, + GDestroyNotify destroy) +{ + static gint tag= 1; + + InteractionKey *key= g_new (InteractionKey, 1); + + if (key) + { + key->tag = tag++; + key->client = client; + key->dialog_type= dialog_type; + key->in_use = FALSE; + key->interp = interp; + key->function = function; + key->data = data; + key->destroy = destroy; + + interact_functions= g_list_append (interact_functions, key); + } + + return key; +} + + +static void +interaction_key_destroy (InteractionKey *key) +{ + interact_functions= g_list_remove (interact_functions, key); + + if (key->destroy) + (key->destroy) (key->data); + + g_free (key); +} + + +static void +interaction_key_destroy_if_possible (InteractionKey *key) +{ + if (key->in_use) + key->client = NULL; + else + interaction_key_destroy (key); +} + + +static InteractionKey * +interaction_key_find_by_tag (gint tag) +{ + InteractionKey *key; + GList *tmp= interact_functions; + + while (tmp) + { + key= (InteractionKey *) tmp->data; + + if (key->tag == tag) + return key; + + tmp= tmp->next; + } + + return NULL; +} + + +static void +interaction_key_use (InteractionKey *key) +{ + key->in_use= TRUE; + + if (!key->interp) + key->function (key->client, key->tag, key->dialog_type, key->data); +} + +#endif /* HAVE_LIBSM */ + + +/*****************************************************************************/ +/* Helper functions, that set session management properties. */ + +/* The following functions are used to set and unset session + management properties of a special type. */ + +#ifdef HAVE_LIBSM + + +static void +client_set_value (GnomeClient *client, + gchar *name, + char *type, + int num_vals, + SmPropValue *vals) +{ + SmProp *proplist[1]; + SmProp prop; + + prop.name = name; + prop.type = type; + prop.num_vals = num_vals; + prop.vals = vals; + + proplist[0]= ∝ + SmcSetProperties ((SmcConn) client->smc_conn, 1, proplist); +} + + +static void +client_set_string (GnomeClient *client, gchar *name, gchar *value) +{ + SmPropValue val; + + g_return_if_fail (name); + + if (!GNOME_CLIENT_CONNECTED (client) || (value == NULL)) + return; + + val.length = strlen (value)+1; + val.value = value; + + client_set_value (client, name, SmARRAY8, 1, &val); +} + + +static void +client_set_gchar (GnomeClient *client, gchar *name, gchar value) +{ + SmPropValue val; + + g_return_if_fail (name); + + if (!GNOME_CLIENT_CONNECTED (client)) + return; + + val.length = 1; + val.value = &value; + + client_set_value (client, name, SmCARD8, 1, &val); +} + + +static void +client_set_ghash0 (gchar *key, gchar *value, SmPropValue **vals) +{ + (*vals)->length= strlen (key); + (*vals)->value = key; + (*vals)++; + + (*vals)->length= strlen (value); + (*vals)->value = value; + (*vals)++; +} + +static void +client_set_ghash (GnomeClient *client, gchar *name, GHashTable *table) +{ + gint argc; + SmPropValue *vals; + SmPropValue *tmp; + + g_return_if_fail (name); + g_return_if_fail (table); + + if (!GNOME_CLIENT_CONNECTED (client)) + return; + + /* multiply by 2 because for each element + * we have a key and a value + */ + argc = 2 * g_hash_table_size (table); + + if (argc == 0) + return; + + /* Now initialize the 'vals' array. */ + vals = g_new (SmPropValue, argc); + tmp = vals; + + g_hash_table_foreach (table, (GHFunc) client_set_ghash0, &tmp); + + client_set_value (client, name, SmLISTofARRAY8, argc, vals); + + g_free (vals); + +} + + +static void +client_set_array (GnomeClient *client, gchar *name, gchar *array[]) +{ + gint argc; + gchar **ptr; + gint i; + + SmPropValue *vals; + + g_return_if_fail (name); + + if (!GNOME_CLIENT_CONNECTED (client) || (array == NULL)) + return; + + /* We count the number of elements in our array. */ + for (ptr = array, argc = 0; *ptr ; ptr++, argc++) /* LOOP */; + + /* Now initialize the 'vals' array. */ + vals = g_new (SmPropValue, argc); + for (ptr = array, i = 0 ; i < argc ; ptr++, i++) + { + vals[i].length = strlen (*ptr); + vals[i].value = *ptr; + } + + client_set_value (client, name, SmLISTofARRAY8, argc, vals); + + g_free (vals); +} + +static void +client_set_clone_command (GnomeClient *client) +{ + GList *list; + gint argc; + gchar **ptr; + gint i = 0; + + SmPropValue *vals; + + if (!GNOME_CLIENT_CONNECTED (client)) + return; + + ptr=client->clone_command ? client->clone_command : client->restart_command; + + if (!ptr) + return; + + for (argc = 0; *ptr ; ptr++) argc++; + + /* Add space for static arguments and config prefix. */ + argc += g_list_length (client->static_args) + 2; + + vals = g_new (SmPropValue, argc); + + ptr=client->clone_command ? client->clone_command : client->restart_command; + + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + + if (client->config_prefix) + { + vals[i].length = strlen (sm_config_prefix_arg_name); + vals[i++].value = (char *) sm_config_prefix_arg_name; + vals[i].length = strlen (client->config_prefix); + vals[i++].value = client->config_prefix; + } + + for (list = client->static_args; list; list= g_list_next (list)) + { + vals[i].length= strlen ((gchar *)list->data); + vals[i++].value = (gchar *)list->data; + } + + while (*ptr) + { + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + } + + client_set_value (client, SmCloneCommand, SmLISTofARRAY8, i, vals); + + g_free (vals); +} + +static void +client_set_restart_command (GnomeClient *client) +{ + GList *list; + gint argc; + gchar **ptr; + gint i = 0; + gchar *tmp; + +#ifdef HAVE_GTK_MULTIHEAD + gint disp; +#endif + + SmPropValue *vals; + + if (!GNOME_CLIENT_CONNECTED (client) || (client->restart_command == NULL)) + return; + +#ifdef DEBUG_ME + fprintf(stderr, "client_set_restart_command: desktop = %d\n", _sm_desktop); +#endif + + for (ptr = client->restart_command, argc = 0; *ptr ; ptr++) argc++; + + /* Add space for static arguments, desktop and client id. */ + argc += g_list_length (client->static_args) + 2; + if (_sm_desktop >= 0) + argc += 2; + +#ifdef HAVE_GTK_MULTIHEAD + /* add two more for --screen */ + argc += 2; +#endif + + vals = g_new (SmPropValue, argc); + + ptr = client->restart_command; + + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + + for (list = client->static_args; list; list = g_list_next (list)) + { + vals[i].length= strlen ((gchar *)list->data); + vals[i++].value = (gchar *)list->data; + } + + while (*ptr) + { + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + } + + /*if (client->config_prefix) + { + vals[i].length = strlen (sm_config_prefix_arg_name); + vals[i++].value = (char *) sm_config_prefix_arg_name; + vals[i].length = strlen (client->config_prefix); + vals[i++].value = client->config_prefix; + }*/ + + vals[i].length = strlen (sm_client_id_arg_name); + vals[i++].value = (char *) sm_client_id_arg_name; + vals[i].length = strlen (client->client_id); + vals[i++].value = client->client_id; + + if (_sm_desktop >= 0) + { + tmp = g_strdup_printf ("%d", _sm_desktop); + vals[i].length = strlen(sm_client_desktop_arg_name); + vals[i++].value = (char *) sm_client_desktop_arg_name; + vals[i].length = strlen(tmp); + vals[i++].value = tmp; + } + +#ifdef HAVE_GTK_MULTIHEAD + /* set the screen number */ + disp = gdk_screen_get_number (gdk_screen_get_default ()); + tmp = g_strdup_printf ("%d", disp); + vals[i].length = strlen (sm_screen); + vals[i++].value = (char *)sm_screen; + vals[i].length = strlen (tmp); + vals[i++].value = tmp; +#endif + +#ifdef DEBUG_ME + { + int j; + + fprintf(stderr, "client_set_restart_command: "); + for (j = 0; j < i; j++) + fprintf(stderr, "%s ", (char *)vals[j].value); + fputc('\n', stderr); + } +#endif + + client_set_value (client, SmRestartCommand, SmLISTofARRAY8, i, vals); + + g_free (vals); + +#ifdef HAVE_GTK_MULTIHEAD + g_free (tmp); +#endif +} + +static void +client_unset (GnomeClient *client, gchar *name) +{ + g_return_if_fail (name != NULL); + + if (GNOME_CLIENT_CONNECTED (client)) + SmcDeleteProperties ((SmcConn) client->smc_conn, 1, &name); +} + +#endif /* HAVE_LIBSM */ + +static void +client_set_state (GnomeClient *client, GnomeClientState state) +{ + client->state= state; +} + + +/*****************************************************************************/ +/* Callback functions */ + +/* The following functions are callback functions (and helper + functions). They are registered in 'gnome_client_connect', + 'gnome_interaction_key_return' and + 'gnome_client_request_interaction_internal', + 'client_save_yourself_callback'. */ + +#ifdef HAVE_LIBSM + + +static void +client_save_phase_2_callback (SmcConn smc_conn, SmPointer client_data); + +static void +client_save_yourself_possibly_done (GnomeClient *client) +{ +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client->interaction_keys) + return; + + if ((client->state == GNOME_CLIENT_SAVING_PHASE_1) && + client->save_phase_2_requested) + { + Status status; + + status= SmcRequestSaveYourselfPhase2 ((SmcConn) client->smc_conn, + client_save_phase_2_callback, + (SmPointer) client); + + if (status) + client_set_state (client, GNOME_CLIENT_WAITING_FOR_PHASE_2); + } + + if ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)) + { + SmcSaveYourselfDone ((SmcConn) client->smc_conn, + client->save_successfull); + + if (client->shutdown) + client_set_state (client, GNOME_CLIENT_FROZEN); + else + client_set_state (client, GNOME_CLIENT_IDLE); + } +} + + +static void +client_save_phase_2_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient*) client_data; + gboolean ret; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + client_set_state (client, GNOME_CLIENT_SAVING_PHASE_2); + + g_signal_emit (client, client_signals[SAVE_YOURSELF], 0, + 2, + client->save_style, + client->shutdown, + client->interact_style, + client->fast, + &ret); + + client_save_yourself_possibly_done (client); +} + +static gint +end_wait (gpointer data) +{ + gboolean *waiting = (gboolean*)data; + *waiting = FALSE; + return 0; +} + +static void +client_save_yourself_callback (SmcConn smc_conn, + SmPointer client_data, + int save_style, + gboolean shutdown, + int interact_style, + gboolean fast) +{ + GnomeClient *client= (GnomeClient*) client_data; + //gchar *name, *prefix; + //int fd, len; + gboolean ret; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (!client_grab_widget) + { + GDK_THREADS_ENTER(); + client_grab_widget = gtk_widget_new (GTK_TYPE_INVISIBLE, NULL); + GDK_THREADS_LEAVE(); + } + + /* The first SaveYourself after registering for the first time + * is a special case (SM specs 7.2). + * + * This SaveYourself seems to be included in the protocol to + * ask the client to specify its initial SmProperties since + * there is little point saving a copy of the initial state. + * + * A bug in xsm means that it does not send us a SaveComplete + * in response to this initial SaveYourself. Therefore, we + * must not set a grab because it would never be released. + * Indeed, even telling the app that this SaveYourself has + * arrived is hazardous as the app may take its own steps + * to freeze its WM state while waiting for the SaveComplete. + * + * Fortunately, we have already set the SmProperties during + * gnome_client_connect so there is little lost in simply + * returning immediately. + * + * Apps which really want to save their initial states can + * do so safely using gnome_client_save_yourself_request. */ + + if (client->state == GNOME_CLIENT_REGISTERING) + { + client_set_state (client, GNOME_CLIENT_IDLE); + + /* Double check that this is a section 7.2 SaveYourself: */ + + if (save_style == SmSaveLocal && + interact_style == SmInteractStyleNone && + !shutdown && !fast) + { + /* The protocol requires this even if xsm ignores it. */ + SmcSaveYourselfDone ((SmcConn) client->smc_conn, TRUE); + return; + } + } + + switch (save_style) + { + case SmSaveGlobal: + client->save_style= GNOME_SAVE_GLOBAL; + break; + + case SmSaveLocal: + client->save_style= GNOME_SAVE_LOCAL; + break; + + case SmSaveBoth: + default: + client->save_style= GNOME_SAVE_BOTH; + break; + } + client->shutdown= shutdown; + switch (interact_style) + { + case SmInteractStyleErrors: + client->interact_style= GNOME_INTERACT_ERRORS; + break; + + case SmInteractStyleAny: + client->interact_style= GNOME_INTERACT_ANY; + break; + + case SmInteractStyleNone: + default: + client->interact_style= GNOME_INTERACT_NONE; + break; + } + client->fast = fast; + + client->save_phase_2_requested= FALSE; + client->save_successfull = TRUE; + client->save_yourself_emitted = FALSE; + + client_set_state (client, GNOME_CLIENT_SAVING_PHASE_1); + + GDK_THREADS_ENTER(); + + if (gt_pointer_is_grabbed()) + { + gboolean waiting = TRUE; + gint id = g_timeout_add (4000, end_wait, &waiting); + + while (gt_pointer_is_grabbed() && waiting) + gtk_main_iteration(); + g_source_remove (id); + } + + /* Check that we did not receive a shutdown cancelled while waiting + * for the grab to be released. The grab should prevent it but ... */ + if (client->state != GNOME_CLIENT_SAVING_PHASE_1) + { + GDK_THREADS_LEAVE(); + return; + } + + gt_ungrab(); + gtk_grab_add (client_grab_widget); + + GDK_THREADS_LEAVE(); + +#if 0 + name = g_strdup (gnome_client_get_global_config_prefix(client)); + name[strlen (name) - 1] = '\0'; + + prefix = g_strconcat (name, "-XXXXX", "/", NULL); + g_free (name); + len = strlen (prefix); + + name = gnome_config_get_real_path (prefix); + g_free (prefix); + + name [strlen (name) - 1] = 'X'; + fd = mkstemp (name); + + if (fd != -1) + { + unlink (name); + close (fd); + g_free (client->config_prefix); + client->config_prefix = g_strconcat (name+strlen(name) - len, "/", NULL); + + if (client == master_client) + { + /* The config prefix has been changed, so it cannot be used + anymore to restore a saved session. */ + master_client_restored= FALSE; + } + } + g_free (name); +#endif + + g_signal_emit (client, client_signals[SAVE_YOURSELF], 0, + 1, + client->save_style, + shutdown, + client->interact_style, + fast, + &ret); + + client_set_clone_command (client); + client_set_restart_command (client); + +#ifdef BREAK_KDE_SESSION_MANAGER + /* <jsh> The KDE session manager actually cares about the `success' + field of the SaveYourselfDone message (unlike gnome-session, which + totally ignores it. Hence the code below has the effect of making + KDE unable to shutdown when any GNOME apps are running that haven't + connected to the "save_yourself" signal. */ + + if (!client->save_yourself_emitted) + client->save_successfull= FALSE; +#endif + + client_save_yourself_possibly_done (client); +} + + +static void +client_die_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient*) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client_grab_widget) + { + GDK_THREADS_ENTER(); + gtk_grab_remove (client_grab_widget); + GDK_THREADS_LEAVE(); + } + + g_signal_emit (client, client_signals[DIE], 0); +} + + +static void +client_save_complete_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client = (GnomeClient*) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client_grab_widget) + { + GDK_THREADS_ENTER(); + gtk_grab_remove (client_grab_widget); + GDK_THREADS_LEAVE(); + } + + g_signal_emit (client, client_signals[SAVE_COMPLETE], 0); +} + + +static void +client_shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient*) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client_grab_widget) + { + GDK_THREADS_ENTER(); + gtk_grab_remove (client_grab_widget); + GDK_THREADS_LEAVE(); + } + + g_signal_emit (client, client_signals[SHUTDOWN_CANCELLED], 0); +} + + +static void +client_interact_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient *) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client->interaction_keys) + { + GSList *tmp= client->interaction_keys; + InteractionKey *key= (InteractionKey *) tmp->data; + + client->interaction_keys= g_slist_remove (tmp, tmp->data); + + interaction_key_use (key); + } + else + { + /* This branch should never be executed. But if it is executed, + we just finish interacting. */ + SmcInteractDone ((SmcConn) client->smc_conn, FALSE); + } +} + + +#endif /* HAVE_LIBSM */ + + +/*****************************************************************************/ +/* Managing the master client */ + +#if 0 +/* The following environment variables will be set on the master + client, if they are defined the programs environment. The array + must end with a NULL entry. + For now we have no entries. You might think that saving DISPLAY, + or HOME, or something like that would be right. It isn't. We + definitely want to inherit these values from the user's (possibly + changing) environment. */ +static char* master_environment[]= +{ + NULL +}; +#endif + +/********* gnome_client module */ + +#if 0 +/* Forward declaration for our module functions. */ +static void client_parse_func (poptContext ctx, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, void *data); + +static gboolean gnome_client_goption_sm_config_prefix (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); + +static gboolean gnome_client_goption_sm_disable (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); + +static void gnome_client_pre_args_parse(GnomeProgram *app, GnomeModuleInfo *mod_info); +static void gnome_client_post_args_parse(GnomeProgram *app, GnomeModuleInfo *mod_info); +#endif + +enum { ARG_SM_CLIENT_ID=1, ARG_SM_CONFIG_PREFIX, ARG_SM_DISABLE }; + +typedef struct { + guint sm_connect_id; +} GnomeProgramClass_gnome_client; + +typedef struct { + gboolean sm_connect; +} GnomeProgramPrivate_gnome_client; + +//static gchar *gnome_client_goption_sm_client_id = NULL; + +//static GQuark quark_gnome_program_private_gnome_client = 0; +//static GQuark quark_gnome_program_class_gnome_client = 0; + +#if 0 +static void +gnome_client_module_get_property (GObject *object, guint param_id, GValue *value, + GParamSpec *pspec) +{ + GnomeProgramClass_gnome_client *cdata; + GnomeProgramPrivate_gnome_client *priv; + GnomeProgram *program; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_PROGRAM (object)); + + program = GNOME_PROGRAM (object); + + cdata = g_type_get_qdata (G_OBJECT_TYPE (program), + quark_gnome_program_class_gnome_client); + priv = g_object_get_qdata (G_OBJECT (program), + quark_gnome_program_private_gnome_client); + + if (param_id == cdata->sm_connect_id) + g_value_set_boolean (value, priv->sm_connect); + else + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); +} + +static void +gnome_client_module_set_property (GObject *object, guint param_id, + const GValue *value, GParamSpec *pspec) +{ + GnomeProgramClass_gnome_client *cdata; + GnomeProgramPrivate_gnome_client *priv; + GnomeProgram *program; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_PROGRAM (object)); + + program = GNOME_PROGRAM (object); + + cdata = g_type_get_qdata (G_OBJECT_TYPE (program), + quark_gnome_program_class_gnome_client); + priv = g_object_get_qdata (G_OBJECT (program), + quark_gnome_program_private_gnome_client); + + if (param_id == cdata->sm_connect_id) { + priv->sm_connect = g_value_get_boolean (value); + } else { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gnome_client_module_init_pass (const GnomeModuleInfo *mod_info) +{ + if (!quark_gnome_program_private_gnome_client) + quark_gnome_program_private_gnome_client = g_quark_from_static_string + ("gnome-program-private:gnome_client"); + + if (!quark_gnome_program_class_gnome_client) + quark_gnome_program_class_gnome_client = g_quark_from_static_string + ("gnome-program-class:gnome_client"); +} + +static void +gnome_client_module_class_init (GnomeProgramClass *klass, const GnomeModuleInfo *mod_info) +{ + GnomeProgramClass_gnome_client *cdata = g_new0 (GnomeProgramClass_gnome_client, 1); + + g_type_set_qdata (G_OBJECT_CLASS_TYPE (klass), quark_gnome_program_class_gnome_client, cdata); + + cdata->sm_connect_id = gnome_program_install_property ( + klass, + gnome_client_module_get_property, + gnome_client_module_set_property, + g_param_spec_boolean (GNOME_CLIENT_PARAM_SM_CONNECT, NULL, NULL, + sm_connect_default, + (G_PARAM_READABLE | G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT))); +} + +static void +gnome_client_module_instance_init (GnomeProgram *program, GnomeModuleInfo *mod_info) +{ + GnomeProgramPrivate_gnome_client *priv = g_new0 (GnomeProgramPrivate_gnome_client, 1); + + g_object_set_qdata_full (G_OBJECT (program), quark_gnome_program_private_gnome_client, priv, g_free); +} + +static GOptionGroup * +gnome_client_module_get_goption_group (void) +{ + const GOptionEntry session_goptions[] = { + { "sm-client-id", '\0', 0, G_OPTION_ARG_STRING, + &gnome_client_goption_sm_client_id, + N_("Specify session management ID"), N_("ID")}, + { "sm-config-prefix", '\0', 0, G_OPTION_ARG_CALLBACK, + gnome_client_goption_sm_config_prefix, + N_("Specify prefix of saved configuration"), N_("PREFIX")}, + { "sm-disable", '\0', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + gnome_client_goption_sm_disable, + N_("Disable connection to session manager"), NULL}, + { NULL } + }; + GOptionGroup *option_group; + + //_gnome_ui_gettext_init (TRUE); + + option_group = g_option_group_new ("gnome-session", + N_("Session management:"), + N_("Show session management options"), + NULL, NULL); + //g_option_group_set_translation_domain (option_group, GETTEXT_PACKAGE); + g_option_group_add_entries(option_group, session_goptions); + + return option_group; +} + +const GnomeModuleInfo * +gnome_client_module_info_get (void) +{ + /* Command-line arguments understood by this module. */ + static const struct poptOption options[] = { + //{ NULL, '\0', POPT_ARG_INTL_DOMAIN, GETTEXT_PACKAGE, 0, NULL, NULL }, + + { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_PRE | POPT_CBFLAG_POST, + client_parse_func, 0, NULL, NULL }, + + { "sm-client-id", '\0', POPT_ARG_STRING, NULL, ARG_SM_CLIENT_ID, + N_("Specify session management ID"), N_("ID") }, + + { "sm-config-prefix", '\0', POPT_ARG_STRING, NULL, ARG_SM_CONFIG_PREFIX, + N_("Specify prefix of saved configuration"), N_("PREFIX") }, + + { "sm-disable", '\0', POPT_ARG_NONE, NULL, ARG_SM_DISABLE, + N_("Disable connection to session manager"), NULL }, + + { NULL, '\0', 0, NULL, 0 } + }; + + static GnomeModuleInfo module_info = { + "gnome-client", VERSION, N_("Session management"), + NULL, gnome_client_module_instance_init, + gnome_client_pre_args_parse, gnome_client_post_args_parse, + (struct poptOption *)options, + gnome_client_module_init_pass, gnome_client_module_class_init, + NULL, NULL + }; + + module_info.get_goption_group_func = gnome_client_module_get_goption_group; + + /*if (module_info.requirements == NULL) { + static GnomeModuleRequirement req[3]; + + //_gnome_ui_gettext_init (FALSE); + + req[0].required_version = "1.3.7"; + req[0].module_info = gnome_gtk_module_info_get (); + + req[1].required_version = "1.102.0"; + req[1].module_info = libgnome_module_info_get (); + + req[2].required_version = NULL; + req[2].module_info = NULL; + + module_info.requirements = req; + }*/ + + return &module_info; +} + +/* Parse command-line arguments we recognize. */ +static void +client_parse_func (poptContext ctx, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, void *data) +{ + switch (reason) { + case POPT_CALLBACK_REASON_OPTION: + switch (opt->val) { + case ARG_SM_CLIENT_ID: + gnome_client_set_id (master_client, arg); + break; + case ARG_SM_DISABLE: + g_object_set (G_OBJECT (gnome_program_get()), + GNOME_CLIENT_PARAM_SM_CONNECT, FALSE, NULL); + break; + case ARG_SM_CONFIG_PREFIX: + g_free(master_client->config_prefix); + master_client->config_prefix= g_strdup (arg); + master_client_restored = TRUE; + break; + } + break; + default: + break; + } +} + +static gboolean +gnome_client_goption_sm_disable (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_object_set (G_OBJECT (gnome_program_get()), + GNOME_CLIENT_PARAM_SM_CONNECT, FALSE, NULL); + + return TRUE; +} + +static gboolean +gnome_client_goption_sm_config_prefix (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_free (master_client->config_prefix); + master_client->config_prefix = g_strdup (value); + master_client_restored = TRUE; + + return TRUE; +} +#endif + +int session_manager_get_desktop() +{ + return _sm_desktop; +} + +void session_manager_set_desktop(int desktop) +{ +#ifdef DEBUG_ME + fprintf(stderr, "%s: %d\n", __func__, desktop); +#endif + _sm_desktop = desktop; +} + +void session_manager_init(int *argc, char ***argv) +{ + char *cwd; + + gnome_type_init(); + + /* Create the master client. */ + master_client = gnome_client_new_without_connection (); + /* Connect the master client's default signals. */ + g_signal_connect (master_client, "connect", G_CALLBACK (master_client_connect), NULL); + g_signal_connect (master_client, "disconnect", G_CALLBACK (master_client_disconnect), NULL); + + /* Initialise ICE */ + gnome_ice_init (); + + cwd = g_get_current_dir(); + if (cwd != NULL) + { + gnome_client_set_current_directory (master_client, cwd); + g_free (cwd); + } + + /* needed on non-glibc systems: */ + gnome_client_set_program(master_client, (*argv)[0]); + /* Argument parsing is starting. We set the restart and the + clone command to a default value, so other functions can use + the master client while parsing the command line. */ + gnome_client_set_restart_command(master_client, 1, &master_client->program); + + if (*argc > 2) + { + if (strcmp((*argv)[*argc - 2], "-session-desktop") == 0) + { + char *endptr; + int desktop; + + desktop = strtol((*argv)[*argc - 1], &endptr, 10); + if (!*endptr) + session_manager_set_desktop(desktop); + *argc -= 2; + } + } + + if (*argc > 2) + { + if (strcmp((*argv)[*argc - 2], "-session") == 0) + { + gnome_client_set_id(master_client, (*argv)[*argc - 1]); + *argc -= 2; + } + } + + gnome_client_connect(master_client); +} + +void session_manager_exit() +{ + gnome_client_disconnect(master_client); +} + +#if 0 +static void +gnome_client_post_args_parse(GnomeProgram *app, GnomeModuleInfo *mod_info) +{ + gboolean do_connect = TRUE; + + g_object_get (G_OBJECT (app), GNOME_CLIENT_PARAM_SM_CONNECT, &do_connect, NULL); + + if (gnome_client_goption_sm_client_id != NULL) + { + gnome_client_set_id (master_client, gnome_client_goption_sm_client_id); + g_free (gnome_client_goption_sm_client_id); + gnome_client_goption_sm_client_id = NULL; + } + else + { + const gchar *desktop_autostart_id; + + desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + + if (desktop_autostart_id != NULL) + gnome_client_set_id (master_client, desktop_autostart_id); + } + + /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to + * use the same client id. */ + g_unsetenv ("DESKTOP_AUTOSTART_ID"); + + /* We're done, so we can connect to the session manager now. */ + if (do_connect) + gnome_client_connect (master_client); +} +#endif + +#ifndef GNOME_DISABLE_DEPRECATED_SOURCE +/** + * gnome_client_disable_master_connection + * + * Description: Don't connect the master client to the session manager. Usually + * invoked by users when they pass the --sm-disable argument to a Gnome application. + **/ + +void +gnome_client_disable_master_connection (void) +{ +} +#endif /* GNOME_DISABLE_DEPRECATED_SOURCE */ + +#if 0 +static void _gdk_windowing_display_set_sm_client_id (GdkDisplay *display, const gchar *sm_client_id) +{ + if (gdk_display_is_closed(display) + return; + + if (sm_client_id && strcmp (sm_client_id, "")) + { + XChangeProperty (gdk_x11_display_get_xdisplay(display), display_x11->leader_window, + gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"), + XA_STRING, 8, PropModeReplace, (guchar *)sm_client_id, + strlen (sm_client_id)); + } + else + XDeleteProperty (gdk_x11_display_get_xdisplay(display), display_x11->leader_window, + gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID")); +} + +static void gdk_set_sm_client_id (const gchar* sm_client_id) +{ + GSList *displays, *tmp_list; + + g_free (gdk_sm_client_id); + gdk_sm_client_id = g_strdup (sm_client_id); + + displays = gdk_display_manager_list_displays (gdk_display_manager_get ()); + for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next) + _gdk_windowing_display_set_sm_client_id (tmp_list->data, sm_client_id); + + g_slist_free (displays); +} +#endif + +static void +master_client_connect (GnomeClient *client, + gboolean restarted, + gpointer client_data) +{ + gdk_x11_set_sm_client_id(gnome_client_get_id (client)); +} + +static void +master_client_disconnect (GnomeClient *client, + gpointer client_data) +{ + if(client_grab_widget && gtk_grab_get_current() == client_grab_widget) + gtk_grab_remove(client_grab_widget); + + gdk_x11_set_sm_client_id(NULL); +} + +/** + * gnome_master_client + * + * Description: + * Get the master session management client. This master client gets a client + * id, that may be specified by the '--sm-client-id' command line option. A + * master client will be generated by gnome_program_init(). If possible the + * master client will contact the session manager after command-line parsing is + * finished (unless gnome_client_disable_master_connection() was called). The + * master client will also set the SM_CLIENT_ID property on the client leader + * window of your application. + * + * Additionally, the master client gets some static arguments set automatically + * (see gnome_client_add_static_arg() for static arguments): + * gnome_program_init() passes all the command line options which are + * recognised by gtk as static arguments to the master client. + * + * Returns: Pointer to the master client + **/ + +GnomeClient* +gnome_master_client (void) +{ + return master_client; +} + + +/*****************************************************************************/ +/* GTK-class managing functions */ + +GNOME_CLASS_BOILERPLATE (GnomeClient, gnome_client, GObject, G_TYPE_OBJECT) + +static void +gnome_client_class_init (GnomeClientClass *klass) +{ + //GObjectClass *object_class = (GObjectClass*) klass; + GObjectClass *gobject_class = (GObjectClass*) klass; + + client_signals[SAVE_YOURSELF] = + g_signal_new ("save_yourself", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GnomeClientClass, save_yourself), + NULL, NULL, + _gnome_marshal_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN, + G_TYPE_BOOLEAN, 5, + G_TYPE_INT, + GNOME_TYPE_SAVE_STYLE, + G_TYPE_BOOLEAN, + GNOME_TYPE_INTERACT_STYLE, + G_TYPE_BOOLEAN); + client_signals[DIE] = + g_signal_new ("die", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GnomeClientClass, die), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + client_signals[SAVE_COMPLETE] = + g_signal_new ("save_complete", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, save_complete), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + client_signals[SHUTDOWN_CANCELLED] = + g_signal_new ("shutdown_cancelled", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, shutdown_cancelled), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + client_signals[CONNECT] = + g_signal_new ("connect", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, connect), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + client_signals[DISCONNECT] = + g_signal_new ("disconnect", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, disconnect), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + //object_class->destroy = gnome_real_client_destroy; + gobject_class->finalize = gnome_real_client_finalize; + + klass->save_yourself = NULL; + klass->die = gnome_client_disconnect; + klass->save_complete = gnome_real_client_save_complete; + klass->shutdown_cancelled = gnome_real_client_shutdown_cancelled; + klass->connect = gnome_real_client_connect; + klass->disconnect = gnome_real_client_disconnect; +} + +static void +gnome_client_instance_init (GnomeClient *client) +{ + client->smc_conn = NULL; + client->client_id = NULL; + client->previous_id = NULL; + client->input_id = 0; + client->config_prefix = NULL; + client->global_config_prefix= NULL; + + client->static_args = NULL; + + /* Preset some default values. */ + client->clone_command = NULL; + client->current_directory = NULL; + client->discard_command = NULL; + client->environment = g_hash_table_new (g_str_hash, g_str_equal); + + client->process_id = getpid (); + + client->program = NULL; + client->resign_command = NULL; + client->restart_command = NULL; + + client->restart_style = -1; + + client->shutdown_command = NULL; + + client->user_id= g_strdup (g_get_user_name ()); + + client->state = GNOME_CLIENT_DISCONNECTED; + client->interaction_keys = NULL; +} + +static gboolean +environment_entry_remove (gchar *key, gchar *value, gpointer data) +{ + g_free (key); + g_free (value); + + return TRUE; +} + +#if 0 +static void +gnome_real_client_destroy (GtkObject *object) +{ + GnomeClient *client; + + /* remember, destroy can be run multiple times! */ + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_CLIENT (object)); + + client = GNOME_CLIENT (object); + + if (GNOME_CLIENT_CONNECTED (client)) + gnome_client_disconnect (client); + + GNOME_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} +#endif + +static void +gnome_real_client_finalize (GObject *object) +{ + GnomeClient *client; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_CLIENT (object)); + + client = GNOME_CLIENT (object); + + g_free (client->client_id); + client->client_id = NULL; + g_free (client->previous_id); + client->previous_id = NULL; + g_free (client->config_prefix); + client->config_prefix = NULL; + g_free (client->global_config_prefix); + client->global_config_prefix = NULL; + + if (client->static_args != NULL) { + g_list_foreach (client->static_args, (GFunc)g_free, NULL); + g_list_free (client->static_args); + client->static_args = NULL; + } + + g_strfreev (client->clone_command); + client->clone_command = NULL; + g_free (client->current_directory); + client->current_directory = NULL; + g_strfreev (client->discard_command); + client->discard_command = NULL; + + if(client->environment != NULL) { + g_hash_table_foreach_remove (client->environment, + (GHRFunc)environment_entry_remove, NULL); + g_hash_table_destroy (client->environment); + client->environment = NULL; + } + + g_free (client->program); + client->program = NULL; + g_strfreev (client->resign_command); + client->resign_command = NULL; + g_strfreev (client->restart_command); + client->restart_command = NULL; + g_strfreev (client->shutdown_command); + client->shutdown_command = NULL; + g_free (client->user_id); + client->user_id = NULL; + + GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +/*****************************************************************************/ +/* 'gnome_client' public member functions */ + + +/** + * gnome_client_new + * + * Description: Allocates memory for a new GNOME session management client + * object. After allocating, the client tries to connect to a session manager. + * You probably want to use gnome_master_client() instead. + * + * Returns: Pointer to a newly allocated GNOME session management client object. + **/ + +GnomeClient * +gnome_client_new (void) +{ + GnomeClient *client; + + client= gnome_client_new_without_connection (); + + gnome_client_connect (client); + + return client; +} + + +/** + * gnome_client_new_without_connection + * + * Description: Allocates memory for a new GNOME session management client + * object. You probably want to use gnome_master_client() instead. + * + * Returns: Pointer to a newly allocated GNOME session management client object. + **/ + +GnomeClient * +gnome_client_new_without_connection (void) +{ + GnomeClient *client; + + client = g_object_new (GNOME_TYPE_CLIENT, NULL); + + /* Preset the CloneCommand, RestartCommand and Program properties. + FIXME: having a default would be cool, but it is probably hard to + get this to interact correctly with the distributed command-line + parsing. */ + client->clone_command = NULL; + client->restart_command = NULL; + + /* Non-glibc systems do not get this set on the master_client until + client_parse_func but this is not a problem. + The SM specs require explictly require that this is the value: */ + client->program = g_strdup (g_get_prgname ()); + + return client; +} + + +/** + * gnome_client_flush + * @client: A #GnomeClient instance. + * + * Description: + * This will force the underlying connection to the session manager to be + * flushed. This is useful if you have some pending changes that you want to + * make sure get committed. + **/ + +void +gnome_client_flush (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if (GNOME_CLIENT_CONNECTED (client)) { + IceConn conn = SmcGetIceConnection ((SmcConn) client->smc_conn); + IceFlush (conn); + } +#endif +} + +/*****************************************************************************/ + +#define ERROR_STRING_LENGTH 256 + + +/** + * gnome_client_connect + * @client: A #GnomeClient instance. + * + * Description: Causes the client to connect to the session manager. + * Usually happens automatically; no need to call this function. + **/ + +void +gnome_client_connect (GnomeClient *client) +{ +#ifdef HAVE_LIBSM + SmcCallbacks callbacks; + gchar *client_id; +#endif /* HAVE_LIBSM */ + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + +#ifdef HAVE_LIBSM + if (GNOME_CLIENT_CONNECTED (client)) + return; + + callbacks.save_yourself.callback = client_save_yourself_callback; + callbacks.die.callback = client_die_callback; + callbacks.save_complete.callback = client_save_complete_callback; + callbacks.shutdown_cancelled.callback = client_shutdown_cancelled_callback; + + callbacks.save_yourself.client_data = + callbacks.die.client_data = + callbacks.save_complete.client_data = + callbacks.shutdown_cancelled.client_data = (SmPointer) client; + + + if (g_getenv ("SESSION_MANAGER")) + { + gchar error_string_ret[ERROR_STRING_LENGTH] = ""; + + client->smc_conn= (gpointer) + SmcOpenConnection (NULL, client, + SmProtoMajor, SmProtoMinor, + SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask, + &callbacks, + client->client_id, &client_id, + ERROR_STRING_LENGTH, error_string_ret); + + if (error_string_ret[0]) + g_warning ("While connecting to session manager:\n%s.", + error_string_ret); + +#ifdef DEBUG_ME + fprintf(stderr, "gnome_client_connect: id = %s\n", client_id); +#endif + } + + if (GNOME_CLIENT_CONNECTED (client)) + { + gint restarted= FALSE; + + g_free (client->previous_id); + client->previous_id= client->client_id; + client->client_id= client_id; + + restarted= (client->previous_id && + !strcmp (client->previous_id, client_id)); + + client_set_state (client, (restarted ? + GNOME_CLIENT_IDLE : + GNOME_CLIENT_REGISTERING)); + + /* Let all the world know, that we have a connection to a + session manager. */ + g_signal_emit (client, client_signals[CONNECT], 0, restarted); + } +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_disconnect + * @client: A #GnomeClient instance. + * + * Description: Disconnect the client from the session manager. + **/ + +void +gnome_client_disconnect (GnomeClient *client) +{ +#ifdef DEBUG_ME + fprintf(stderr, "gnome_client_disconnect\n"); +#endif + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (GNOME_CLIENT_CONNECTED (client)) + { + gnome_client_flush (client); + g_signal_emit (client, client_signals[DISCONNECT], 0); + } +} + +/*****************************************************************************/ + +/** + * gnome_client_get_flags: + * @client: Pointer to GNOME session client object. + * + * Description: Determine the client's status with the session manager., + * + * Returns: Various #GnomeClientFlag flags which have been or'd together. + **/ + +GnomeClientFlags +gnome_client_get_flags (GnomeClient *client) +{ + GnomeClientFlags flags= 0; + + g_return_val_if_fail (client != NULL, 0); + g_return_val_if_fail (GNOME_IS_CLIENT (client), 0); + + /* To not break binary compability with existing code, the + GnomeClient struct has not been changed. So the flags have to be + calculated every time. */ + + if (GNOME_CLIENT_CONNECTED (client)) + { + flags |= GNOME_CLIENT_IS_CONNECTED; + + if (client->previous_id && + !strcmp (client->previous_id, client->client_id)) + flags |= GNOME_CLIENT_RESTARTED; + + if (master_client_restored && (client == master_client)) + flags |= GNOME_CLIENT_RESTORED; + } + + return flags; +} + + +/*****************************************************************************/ + +/** + * gnome_client_set_clone_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in the @argv vector. + * @argv: Argument strings, suitable for use with execv(). + * + * Description: Set a command the session manager can use to create a new + * instance of the application. + **/ + +void +gnome_client_set_clone_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* Whenever clone_command == NULL then restart_command is used instead */ + g_strfreev (client->clone_command); + client->clone_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_clone_command (client); +#endif /* HAVE_LIBSM */ +} + +/** + * gnome_client_set_current_directory + * @client: Pointer to GNOME session client object. + * @dir: Directory path. + * + * Description: Set the directory to be in when running shutdown, discard, + * restart, etc. commands. + **/ + +void +gnome_client_set_current_directory (GnomeClient *client, + const gchar *dir) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + g_free (client->current_directory); + + if (dir) + { + client->current_directory= g_strdup (dir); +#ifdef HAVE_LIBSM + client_set_string (client, SmCurrentDirectory, + client->current_directory); +#endif /* HAVE_LIBSM */ + } + else + { + client->current_directory= NULL; +#ifdef HAVE_LIBSM + client_unset (client, SmCurrentDirectory); +#endif /* HAVE_LIBSM */ + } +} + + +/* See doc/reference/tmpl/gnome-client.sgml for documentation. */ + +void +gnome_client_set_discard_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (argv == NULL) + { + g_return_if_fail (argc == 0); + + g_strfreev (client->discard_command); + client->discard_command= NULL; +#ifdef HAVE_LIBSM + client_unset (client, SmDiscardCommand); +#endif /* HAVE_LIBSM */ + } + else + { + g_strfreev (client->discard_command); + client->discard_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_array (client, SmDiscardCommand, + client->discard_command); +#endif /* HAVE_LIBSM */ + } +} + + + +/** + * gnome_client_set_environment + * @client: Pointer to GNOME session client object. + * @name: Name of the environment variable + * @value: Value of the environment variable + * + * Description: Set an environment variable to be placed in the + * client's environment prior to running restart, shutdown, discard, etc. commands. + **/ + +void +gnome_client_set_environment (GnomeClient *client, + const gchar *name, + const gchar *value) +{ + gchar *old_name; + gchar *old_value; + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (name != NULL); + + if (g_hash_table_lookup_extended (client->environment, + name, + (gpointer *) &old_name, + (gpointer *) &old_value)) + { + if (value) + { + g_hash_table_insert (client->environment, old_name, + g_strdup (value)); + g_free (old_value); + } + else + { + g_hash_table_remove (client->environment, name); + g_free (old_name); + g_free (old_value); + } + } + else if (value) + { + g_hash_table_insert (client->environment, + g_strdup (name), + g_strdup (value)); + } + +#ifdef HAVE_LIBSM + client_set_ghash (client, SmEnvironment, client->environment); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_set_process_id + * @client: Pointer to GNOME session client object. + * @pid: PID to set as the client's PID. + * + * Description: The client should tell the session manager the result of + * getpid(). However, GNOME does this automatically; so you do not need this + * function. + **/ + +void +gnome_client_set_process_id (GnomeClient *client, pid_t pid) +{ + gchar str_pid[32]; + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + client->process_id= pid; + + g_snprintf (str_pid, sizeof(str_pid), "%d", client->process_id); + +#ifdef HAVE_LIBSM + client_set_string (client, SmProcessID, str_pid); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_set_program + * @client: Pointer to GNOME session client object. + * @program: Name of the program. + * + * Description: Used to tell the session manager the name of your program. Set + * automatically; this function isn't needed. + **/ + +void +gnome_client_set_program (GnomeClient *client, + const gchar *program) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* The Program property is required, so you must not unset it. */ + g_return_if_fail (program != NULL); + + g_free (client->program); + + client->program= g_strdup (program); + + client_unset_config_prefix (client); + +#ifdef HAVE_LIBSM + client_set_string (client, SmProgram, client->program); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_set_resign_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in @argv. + * @argv: execv()-style command to undo the effects of the client. + * + * Description: Some clients can be "undone," removing their effects and + * deleting any saved state. For example, xmodmap could register a resign + * command to undo the keymap changes it saved. + * + * Used by clients that use the #GNOME_RESTART_ANYWAY restart style to to undo + * their effects (these clients usually perform initialisation functions and + * leave effects behind after they die). The resign command combines the + * effects of a shutdown command and a discard command. It is executed when the + * user decides that the client should cease to be restarted. + **/ + +void +gnome_client_set_resign_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (argv == NULL) + { + g_return_if_fail (argc == 0); + + g_strfreev (client->resign_command); + client->resign_command= NULL; + +#ifdef HAVE_LIBSM + client_unset (client, SmResignCommand); +#endif /* HAVE_LIBSM */ + } + else + { + g_strfreev (client->resign_command); + client->resign_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_array (client, SmResignCommand, client->resign_command); +#endif /* HAVE_LIBSM */ + } +} + + + +/** + * gnome_client_set_restart_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in argv. + * @argv: Argument vector to an execv() to restart the client. + * + * Description: When clients crash or the user logs out and back in, they are + * restarted. This command should perform the restart. Executing the restart + * command on the local host should reproduce the state of the client at the + * time of the session save as closely as possible. Saving config info under + * the gnome_client_get_config_prefix() is generally useful. + **/ + +void +gnome_client_set_restart_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* The RestartCommand property is required, so you must not unset + it. */ + g_return_if_fail (argc != 0); + g_return_if_fail (argv != NULL); + + g_strfreev (client->restart_command); + client->restart_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_restart_command (client); +#endif /* HAVE_LIBSM */ +} + +/** + * gnome_client_set_priority + * @client: Pointer to GNOME session client object. + * @priority: Position of client in session start up ordering. + * + * Description: + * + * The gnome-session manager restarts clients in order of their + * priorities in a similar way to the start up ordering in SysV. + * This function allows the app to suggest a position in this + * ordering. The value should be between 0 and 99. A default + * value of 50 is assigned to apps that do not provide a value. + * The user may assign a different priority. + **/ + +void +gnome_client_set_priority (GnomeClient *client, guint priority) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if (priority > 99) + priority = 99; + + client_set_gchar (client, "_GSM_Priority", (gchar) priority); +#endif +} + +/** + * gnome_client_set_restart_style + * @client: Pointer to GNOME session client object. + * @style: When to restart the client. + * + * Tells the session manager how the client should be restarted in future + * session. The options are given by the #GnomeRestartStyle enum. + **/ + +void +gnome_client_set_restart_style (GnomeClient *client, + GnomeRestartStyle style) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + switch (style) + { + case GNOME_RESTART_IF_RUNNING: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartIfRunning); + break; +#endif /* HAVE_LIBSM */ + + case GNOME_RESTART_ANYWAY: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartAnyway); + break; +#endif /* HAVE_LIBSM */ + + case GNOME_RESTART_IMMEDIATELY: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartImmediately); + break; +#endif /* HAVE_LIBSM */ + + case GNOME_RESTART_NEVER: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartNever); +#endif /* HAVE_LIBSM */ + break; + + default: + g_assert_not_reached (); + return; + } + + client->restart_style= style; +} + + + +/** + * gnome_client_set_shutdown_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in argv. + * @argv: Command to shutdown the client if the client isn't running. + * + * Description: GNOME_RESTART_ANYWAY clients can set this command to run + * when the user logs out but the client is no longer running. + * + * Used by clients that use the GNOME_RESTART_ANYWAY restart style to + * to undo their effects (these clients usually perform initialisation + * functions and leave effects behind after they die). The shutdown + * command simply undoes the effects of the client. It is executed + * during a normal logout. + **/ + +void +gnome_client_set_shutdown_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (argv == NULL) + { + g_return_if_fail (argc == 0); + + g_strfreev (client->shutdown_command); + client->shutdown_command = NULL; +#ifdef HAVE_LIBSM + client_unset (client, SmShutdownCommand); +#endif /* HAVE_LIBSM */ + } + else + { + g_strfreev (client->shutdown_command); + client->shutdown_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_array (client, SmShutdownCommand, + client->shutdown_command); +#endif /* HAVE_LIBSM */ + } +} + + + +/** + * gnome_client_set_user_id + * @client: Pointer to GNOME session client object. + * @id: Username. + * + * Description: Tell the session manager the user's login name. GNOME + * does this automatically; no need to call the function. + **/ + +void +gnome_client_set_user_id (GnomeClient *client, + const gchar *id) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* The UserID property is required, so you must not unset it. */ + g_return_if_fail (id != NULL); + + g_free (client->user_id); + + client->user_id= g_strdup (id); +#ifdef HAVE_LIBSM + client_set_string (client, SmUserID, client->user_id); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_add_static_arg + * @client: Pointer to GNOME session client object. + * @...: %NULL-terminated list of arguments to add to the restart command. + * + * Description: You can add arguments to your restart command's argv with this + * function. This function provides an alternative way of adding new arguments + * to the restart command. The arguments are placed before the arguments + * specified by gnome_client_set_restart_command() and after the arguments + * recognised by GTK+ that are specified by the user on the original command + * line. + **/ + +void +gnome_client_add_static_arg (GnomeClient *client, ...) +{ + va_list args; + gchar *str; + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + va_start (args, client); + str= va_arg (args, gchar*); + while (str) + { + client->static_args= g_list_append (client->static_args, g_strdup(str)); + str= va_arg (args, gchar*); + } + va_end (args); +} + +/*****************************************************************************/ + + +/** + * gnome_client_set_id + * @client: A #GnomeClient instance. + * @id: Session management ID. + * + * Description: Set the client's session management ID; must be done + * before connecting to the session manager. There is usually no reason to call + * this function. + **/ + +void +gnome_client_set_id (GnomeClient *client, const gchar *id) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (!GNOME_CLIENT_CONNECTED (client)); + + g_return_if_fail (id != NULL); + + g_free (client->client_id); + client->client_id = g_strdup (id); + +#ifdef DEBUG_ME + fprintf(stderr, "gnome_client_set_id: %s\n", id); +#endif +} + + + +/** + * gnome_client_get_id + * @client: A #GnomeClient instance. + * + * Description: Returns session management ID + * + * Returns: Session management ID for this client; %NULL if not connected to a + * session manager. + **/ + +const gchar * +gnome_client_get_id (GnomeClient *client) +{ + g_return_val_if_fail (client != NULL, NULL); + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + return client->client_id; +} + +/** + * gnome_client_get_previous_id + * @client: A #GnomeClient instance. + * + * Description: Get the session management ID from the previous session. + * + * Returns: Pointer to the session management ID the client had in the last + * session, or %NULL if it was not in a previous session. + **/ + +const gchar * +gnome_client_get_previous_id (GnomeClient *client) +{ + g_return_val_if_fail (client != NULL, NULL); + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + return client->previous_id; +} + +/** + * gnome_client_get_desktop_id: + * @client: A #GnomeClient instance. + * + * Get the client ID of the desktop's current instance, i.e. if + * you consider the desktop as a whole as a session managed app, this + * returns its session ID using a GNOME extension to session + * management. May return %NULL for apps not running under a recent + * version of gnome-session; apps should handle that case. + * + * Returns: Session ID of GNOME desktop instance, or %NULL if none. + **/ +const gchar* +gnome_client_get_desktop_id (GnomeClient *client) +{ + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + return g_getenv ("GNOME_DESKTOP_SESSION_ID"); +} + +/** + * gnome_client_get_config_prefix + * @client: Pointer to GNOME session client object. + * + * Description: Get the config prefix for a client. This config prefix + * provides a suitable place to store any details about the state of + * the client which can not be described using the app's command line + * arguments (as set in the restart command). You may push the + * returned value using gnome_config_push_prefix() and read or write + * any values you require. + * + * Returns: Config prefix. The returned string belongs to libgnomeui library + * and should NOT be freed by the caller. + **/ + +const gchar * +gnome_client_get_config_prefix (GnomeClient *client) +{ + g_return_val_if_fail (client == NULL || GNOME_IS_CLIENT (client), NULL); + + if (!client) + client = master_client; + + if (!client) + return gnome_client_get_global_config_prefix (client); + + if (!client->config_prefix) + client->config_prefix = (char *)gnome_client_get_global_config_prefix (client); + + return client->config_prefix; +} + +static gchar* config_prefix = NULL; + +/** + * gnome_client_set_global_config_prefix + * @client: Pointer to GNOME session client object. + * @prefix: Prefix for saving the global configuration. + * + * Description: Set the value used for the global config prefix. The config + * prefixes returned by gnome_client_get_config_prefix() are formed by + * extending this prefix with an unique identifier. + * + * The global config prefix defaults to a name based on the name of + * the executable. This function allows you to set it to a different + * value. It should be called BEFORE retrieving the config prefix for + * the first time. Later calls will be ignored. + * + * For example, setting a global config prefix of "/app.d/session/" + * would ensure that all your session save files or directories would + * be gathered together into the app.d directory. + **/ + +void +gnome_client_set_global_config_prefix (GnomeClient *client, const gchar* prefix) +{ + if (client == NULL) + { + config_prefix= g_strdup (prefix); + return; + } + + g_return_if_fail (GNOME_IS_CLIENT (client)); + + client->global_config_prefix = g_strdup (prefix); +} + +/** + * gnome_client_get_global_config_prefix + * @client: Pointer to GNOME session client object. + * + * Description: Get the config prefix that will be returned by + * gnome_client_get_config_prefix() for clients which have NOT been restarted + * or cloned (i.e. for clients started by the user without `--sm-' options). + * This config prefix may be used to write the user's preferred config for + * these "new" clients. + * + * You could also use this prefix as a place to store and retrieve config + * details that you wish to apply to ALL instances of the app. However, this + * practice limits the users freedom to configure each instance in a different + * way so it should be used with caution. + * + * Returns: The config prefix as a newly allocated string. + **/ + +const gchar * +gnome_client_get_global_config_prefix (GnomeClient *client) +{ + if (client == NULL) + { + if (!config_prefix) { + const char *prgname; + const char *name; + + prgname = g_get_prgname (); + + g_assert (prgname != NULL); + + name= strrchr (prgname, '/'); + name= name ? (name+1) : prgname; + + config_prefix= g_strconcat ("/", name, "/", NULL); + } + + return config_prefix; + } + + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + if (!client->global_config_prefix) + { + char *name; + name= strrchr (client->program, '/'); + + name= name ? (name+1) : client->program; + + client->global_config_prefix= g_strconcat ("/", name, "/", NULL); + } + + return client->global_config_prefix; +} + +static void +client_unset_config_prefix (GnomeClient *client) +{ + g_free (client->config_prefix); + client->config_prefix= NULL; + g_free (client->global_config_prefix); + client->global_config_prefix= NULL; +} + + +/*****************************************************************************/ + +static void +gnome_real_client_save_complete (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + client_set_state (client, GNOME_CLIENT_IDLE); +} + +/* The following function is executed, after receiving a SHUTDOWN + * CANCELLED message from the session manager. It is executed before + * the function connected to the 'shutdown_cancelled' signal are + * called */ + +static void +gnome_real_client_shutdown_cancelled (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_WAITING_FOR_PHASE_2) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)) + SmcSaveYourselfDone ((SmcConn) client->smc_conn, False); + + client_set_state (client, GNOME_CLIENT_IDLE); + + /* Free all interation keys but the one in use. */ + while (client->interaction_keys) + { + GSList *tmp= client->interaction_keys; + + interaction_key_destroy_if_possible ((InteractionKey *) tmp->data); + + client->interaction_keys= g_slist_remove (tmp, tmp->data); + } +#endif /* HAVE_LIBSM */ +} + +static void +gnome_real_client_connect (GnomeClient *client, + gboolean restarted) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + /* We now set all non empty properties. */ + client_set_string (client, SmCurrentDirectory, client->current_directory); + client_set_array (client, SmDiscardCommand, client->discard_command); + client_set_ghash (client, SmEnvironment, client->environment); + { + gchar str_pid[32]; + + g_snprintf (str_pid, sizeof(str_pid), "%d", client->process_id); + client_set_string (client, SmProcessID, str_pid); + } + client_set_string (client, SmProgram, client->program); + client_set_array (client, SmResignCommand, client->resign_command); + + client_set_clone_command (client); + client_set_restart_command (client); + + switch (client->restart_style) + { + case GNOME_RESTART_IF_RUNNING: + client_set_gchar (client, SmRestartStyleHint, SmRestartIfRunning); + break; + + case GNOME_RESTART_ANYWAY: + client_set_gchar (client, SmRestartStyleHint, SmRestartAnyway); + break; + + case GNOME_RESTART_IMMEDIATELY: + client_set_gchar (client, SmRestartStyleHint, SmRestartImmediately); + break; + + case GNOME_RESTART_NEVER: + client_set_gchar (client, SmRestartStyleHint, SmRestartNever); + break; + + default: + break; + } + + client_set_array (client, SmShutdownCommand, client->shutdown_command); + client_set_string (client, SmUserID, client->user_id); +#endif /* HAVE_LIBSM */ +} + + +static void +gnome_real_client_disconnect (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if (GNOME_CLIENT_CONNECTED (client)) + { + SmcCloseConnection ((SmcConn) client->smc_conn, 0, NULL); + client->smc_conn = NULL; + } + + client_set_state (client, GNOME_CLIENT_DISCONNECTED); + + /* Free all interation keys but the one in use. */ + while (client->interaction_keys) + { + GSList *tmp= client->interaction_keys; + + interaction_key_destroy_if_possible ((InteractionKey *) tmp->data); + + client->interaction_keys= g_slist_remove (tmp, tmp->data); + } + +#endif /* HAVE_LIBSM */ +} + +/*****************************************************************************/ + +static void +gnome_client_request_interaction_internal (GnomeClient *client, + GnomeDialogType dialog_type, + gboolean interp, + GnomeInteractFunction function, + gpointer data, + GDestroyNotify destroy) +{ +#ifdef HAVE_LIBSM + Status status; + InteractionKey *key; + int _dialog_type; +#endif + + /* Convert GnomeDialogType into XSM library values */ + switch (dialog_type) + { + case GNOME_DIALOG_ERROR: +#ifdef HAVE_LIBSM + _dialog_type= SmDialogError; +#endif + break; + + case GNOME_DIALOG_NORMAL: +#ifdef HAVE_LIBSM + _dialog_type= SmDialogError; +#endif + break; + + default: + g_assert_not_reached (); + return; + } + +#ifdef HAVE_LIBSM + + key= interaction_key_new (client, dialog_type, interp, + function, data, destroy); + + g_return_if_fail (key); + + status= SmcInteractRequest ((SmcConn) client->smc_conn, _dialog_type, + client_interact_callback, (SmPointer) client); + + if (status) + { + client->interaction_keys = g_slist_append (client->interaction_keys, + key); + } + else + { + interaction_key_destroy (key); + } +#endif /* HAVE_LIBSM */ +} + +static void +gnome_client_save_dialog_show (GnomeClient *client, gint key, + GnomeDialogType type, gpointer data) +{ + GtkDialog* dialog = GTK_DIALOG (data); + gboolean shutdown_cancelled; + + if (client->shutdown) + gtk_dialog_add_button (dialog, _("Cancel Logout"), GTK_RESPONSE_CANCEL); + gtk_widget_show_all (GTK_WIDGET (dialog)); + /* These are SYSTEM modal dialogs so map them above everything else */ +#if 0 + /* FIXME: hmmm, _NET doesn't include a way to do this, I'm not sure + * if window type == DOCK is a correct way and the old win_hints + * layer stuff is no more */ + gnome_win_hints_set_layer (GTK_WIDGET (dialog), WIN_LAYER_ABOVE_DOCK); +#endif + shutdown_cancelled = (gtk_dialog_run (dialog) == GTK_RESPONSE_CANCEL); + gnome_interaction_key_return (key, shutdown_cancelled); +} + +/** + * gnome_client_save_any_dialog + * @client: Pointer to #GnomeClient object. + * @dialog: Pointer to GNOME dialog widget (a #GtkDialog widget). + * + * Description: + * May be called during a "save_youself" handler to request that a + * (modal) dialog is presented to the user. The session manager decides + * when the dialog is shown, but it will not be shown it unless the + * session manager is sending an interaction style of #GNOME_INTERACT_ANY. + * A "Cancel Logout" button will be added during a shutdown. + **/ +void +gnome_client_save_any_dialog (GnomeClient *client, GtkDialog *dialog) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (dialog != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (GTK_IS_DIALOG (dialog)); + + if (client->interact_style == GNOME_INTERACT_ANY) + gnome_client_request_interaction (client, + GNOME_DIALOG_NORMAL, + gnome_client_save_dialog_show, + (gpointer) dialog); +} + +/** + * gnome_client_save_error_dialog + * @client: Pointer to #GnomeClient object. + * @dialog: Pointer to GNOME dialog widget (a #GtkDialog widget). + * + * Description: + * May be called during a "save_youself" handler when an error has occurred + * during the save. The session manager decides when the dialog is shown, but + * it will not be shown it unless the session manager is sending an interaction + * style of #GNOME_INTERACT_ANY. A "Cancel Logout" button will be added + * during a shutdown. + **/ + +void +gnome_client_save_error_dialog (GnomeClient *client, GtkDialog *dialog) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (dialog != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (GTK_IS_DIALOG (dialog)); + + if (client->interact_style != GNOME_INTERACT_NONE) + gnome_client_request_interaction (client, + GNOME_DIALOG_ERROR, + gnome_client_save_dialog_show, + (gpointer) dialog); +} + +/** + * gnome_client_request_interaction + * @client: A #GnomeClient object. + * @dialog_type: The type of dialog to create. + * @function: Callback to invoke to perform the interaction. + * @data: Callback data. + * + * Description: Use the following functions, if you want to interact with the + * user during a "save_yourself" handler without being restricted to using the + * dialog based commands gnome_client_save_any_dialog() or + * gnome_client_save_error_dialog(). Note, however, that overriding the session + * manager specified preference in this way (by using arbitrary dialog boxes) + * is not very nice. + * + * If and when the session manager decides that it's the app's turn to interact + * then 'func' will be called with the specified arguments and a unique + * 'GnomeInteractionKey'. The session manager will block other + * clients from interacting until this key is returned with + * gnome_interaction_key_return(). + **/ + +void +gnome_client_request_interaction (GnomeClient *client, + GnomeDialogType dialog_type, + GnomeInteractFunction function, + gpointer data) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + g_return_if_fail ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)); + + g_return_if_fail ((client->interact_style != GNOME_INTERACT_NONE) && + ((client->interact_style == GNOME_INTERACT_ANY) || + (dialog_type == GNOME_DIALOG_ERROR))); + + gnome_client_request_interaction_internal (client, dialog_type, + FALSE, function, data, NULL); +} + + +/** + * gnome_client_request_interaction_interp + * @client: Pointer to GNOME session client object. + * @dialog_type: Type of dialog to show. + * @function: Callback to perform the interaction (a #GnomeInteractFunction). + * @data: Callback data. + * @destroy: Function to destroy callback data. + * + * Description: Similar to gnome_client_request_interaction(), but used when + * you need to destroy the callback data after the interaction. + **/ + +#if 0 +void +gnome_client_request_interaction_interp (GnomeClient *client, + GnomeDialogType dialog_type, + GtkCallbackMarshal function, + gpointer data, + GDestroyNotify destroy) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + g_return_if_fail ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)); + + g_return_if_fail ((client->interact_style != GNOME_INTERACT_NONE) && + ((client->interact_style == GNOME_INTERACT_ANY) || + (dialog_type == GNOME_DIALOG_ERROR))); + + gnome_client_request_interaction_internal (client, dialog_type, + TRUE, + (GnomeInteractFunction)function, + data, destroy); +} +#endif + +/*****************************************************************************/ + + +/** + * gnome_client_request_phase_2 + * @client: A #GnomeClient object. + * + * Description: Request the session managaer to emit the "save_yourself" + * signal for a second time after all the clients in the session have ceased + * interacting with the user and entered an idle state. This might be useful if + * your app manages other apps and requires that they are in an idle state + * before saving its final data. + **/ + +void +gnome_client_request_phase_2 (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* Check if we are in save phase one */ + g_return_if_fail (client->state == GNOME_CLIENT_SAVING_PHASE_1); + + client->save_phase_2_requested= TRUE; +} + + +/** + * gnome_client_request_save + * @client: Pointer to GNOME session client object. + * @save_style: Save style to request. + * @shutdown: Whether to log out of the session. + * @interact_style: Whether to allow user interaction. + * @fast: Minimize activity to save as soon as possible. + * @global: Request that all other apps in the session also save their state. + * + * Description: Request the session manager to save the session in + * some way. The arguments correspond with the arguments passed to the + * "save_yourself" signal handler. + * + * The save_style indicates whether the save should affect data + * accessible to other users (#GNOME_SAVE_GLOBAL) or only the state + * visible to the current user (#GNOME_SAVE_LOCAL) or both. Setting + * shutdown to %TRUE will initiate a logout. The interact_style + * specifies which kinds of interaction will be available. Setting + * fast to %TRUE will limit the save to setting the session manager + * properties plus any essential data. Setting the value of global to + * %TRUE will request that all the other apps in the session do a save + * as well. A global save is mandatory when doing a shutdown. + * + **/ + +void +gnome_client_request_save (GnomeClient *client, + GnomeSaveStyle save_style, + gboolean shutdown, + GnomeInteractStyle interact_style, + gboolean fast, + gboolean global) +{ +#ifdef HAVE_LIBSM + int _save_style; + int _interact_style; +#endif + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + switch (save_style) + { + case GNOME_SAVE_GLOBAL: +#ifdef HAVE_LIBSM + _save_style= SmSaveGlobal; + break; +#endif + + case GNOME_SAVE_LOCAL: +#ifdef HAVE_LIBSM + _save_style= SmSaveLocal; + break; +#endif + + case GNOME_SAVE_BOTH: +#ifdef HAVE_LIBSM + _save_style= SmSaveBoth; +#endif + break; + + default: + g_assert_not_reached (); + return; + } + + switch (interact_style) + { + case GNOME_INTERACT_NONE: +#ifdef HAVE_LIBSM + _interact_style= SmInteractStyleNone; + break; +#endif + + case GNOME_INTERACT_ERRORS: +#ifdef HAVE_LIBSM + _interact_style= SmInteractStyleErrors; + break; +#endif + + case GNOME_INTERACT_ANY: +#ifdef HAVE_LIBSM + _interact_style= SmInteractStyleAny; +#endif + break; + + default: + g_assert_not_reached (); + return; + } + + if (GNOME_CLIENT_CONNECTED (client)) + { +#ifdef HAVE_LIBSM +#if 0 + if (shutdown) + { + gnome_triggers_do("Session shutdown", NULL, "gnome", "logout", NULL); + } +#endif + + SmcRequestSaveYourself ((SmcConn) client->smc_conn, _save_style, + shutdown, _interact_style, + fast, global); +#endif + } + else + { + gboolean ret; + g_signal_emit (client, client_signals[SAVE_YOURSELF], 0, + 1, save_style, shutdown, interact_style, fast, &ret); + if (shutdown) + g_signal_emit (client, client_signals[DIE], 0); + } +} + +/*****************************************************************************/ +/* 'InteractionKey' stuff */ + + +/** + * gnome_interaction_key_return + * @key: Key passed to interaction callback + * @cancel_shutdown: If %TRUE, cancel the shutdown + * + * Description: Used in interaction callback to tell the session manager + * you are done interacting. + **/ + +void +gnome_interaction_key_return (gint tag, + gboolean cancel_shutdown) +{ +#ifdef HAVE_LIBSM + InteractionKey *key; + GnomeClient *client; + + key= interaction_key_find_by_tag (tag); + g_return_if_fail (key); + + client= key->client; + + interaction_key_destroy (key); + + /* The case that 'client != NULL' should only occur, if the + connection to the session manager was closed, while we where + interacting or we received a SHUTDOWN_CANCELLED while + interacting. */ + if (client == NULL) + return; + + client->interaction_keys= g_slist_remove (client->interaction_keys, key); + + if (cancel_shutdown && !client->shutdown) + cancel_shutdown= FALSE; + + SmcInteractDone ((SmcConn) client->smc_conn, cancel_shutdown); + + client_save_yourself_possibly_done (client); +#endif /* HAVE_LIBSM */ +} + +/*****************************************************************************/ +/* array helping functions - these function should be replaced by g_lists */ + +static gchar ** +array_init_from_arg (gint argc, gchar *argv[]) +{ + gchar **array; + int i; + + if (argv == NULL) + { + g_return_val_if_fail (argc == 0, NULL); + + return NULL; + } + else + { + /* Now initialize the array. */ + array = g_new (gchar *, argc + 1); + + for(i = 0; i < argc; i++) + array[i] = g_strdup(argv[i]); + + array[i] = NULL; + } + + return array; +} diff --git a/gb.gtk/src/sm/gnome-client.h b/gb.gtk/src/sm/gnome-client.h new file mode 100644 index 00000000..8bbd6d43 --- /dev/null +++ b/gb.gtk/src/sm/gnome-client.h @@ -0,0 +1,489 @@ +/* gnome-client.h - GNOME session management client support + * + * Copyright (C) 1998 Carsten Schaar + * All rights reserved + * + * Author: Carsten Schaar <nhadcasc@fs-maphy.uni-hannover.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + */ +/* + @NOTATION@ +*/ + +#ifndef GNOME_CLIENT_H +#define GNOME_CLIENT_H + +#include <unistd.h> +#include <sys/types.h> + +#define HAVE_LIBSM +#define N_(x) x +#define _(x) x +//#define GNOME_DISABLE_DEPRECATED +//#define GTK_DISABLE_DEPRECATED + +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_26 + +#include <glib.h> +#include <gtk/gtk.h> +#include <gdk/gdkprivate.h> +#include <gdk/gdkx.h> + +#ifdef GTK3 +#undef GDK_THREADS_ENTER +#define GDK_THREADS_ENTER() +#undef GDK_THREADS_LEAVE +#define GDK_THREADS_LEAVE() +#endif + +//#include "gnome-program.h" + +G_BEGIN_DECLS + +#define GNOME_TYPE_CLIENT (gnome_client_get_type ()) +#define GNOME_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CLIENT, GnomeClient)) +#define GNOME_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CLIENT, GnomeClientClass)) +#define GNOME_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CLIENT)) +#define GNOME_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (((klass), GNOME_TYPE_CLIENT))) +#define GNOME_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CLIENT, GnomeClientClass)) + +#define GNOME_CLIENT_CONNECTED(obj) (GNOME_CLIENT (obj)->smc_conn) + +typedef struct _GnomeClient GnomeClient; +typedef struct _GnomeClientClass GnomeClientClass; + + +typedef enum +{ + GNOME_INTERACT_NONE, + GNOME_INTERACT_ERRORS, + GNOME_INTERACT_ANY +} GnomeInteractStyle; + +typedef enum +{ + GNOME_DIALOG_ERROR, + GNOME_DIALOG_NORMAL +} GnomeDialogType; + +typedef enum +{ + /* update structure when adding an enum */ + GNOME_SAVE_GLOBAL, + GNOME_SAVE_LOCAL, + GNOME_SAVE_BOTH +} GnomeSaveStyle; + +typedef enum +{ + /* update structure when adding an enum */ + GNOME_RESTART_IF_RUNNING, + GNOME_RESTART_ANYWAY, + GNOME_RESTART_IMMEDIATELY, + GNOME_RESTART_NEVER +} GnomeRestartStyle; + +typedef enum +{ + /* update structure when adding an enum */ + GNOME_CLIENT_IDLE, + GNOME_CLIENT_SAVING_PHASE_1, + GNOME_CLIENT_WAITING_FOR_PHASE_2, + GNOME_CLIENT_SAVING_PHASE_2, + GNOME_CLIENT_FROZEN, + GNOME_CLIENT_DISCONNECTED, + GNOME_CLIENT_REGISTERING +} GnomeClientState; + +typedef enum +{ + GNOME_CLIENT_IS_CONNECTED= 1 << 0, + GNOME_CLIENT_RESTARTED = 1 << 1, + GNOME_CLIENT_RESTORED = 1 << 2 +} GnomeClientFlags; + + +typedef void (*GnomeInteractFunction) (GnomeClient *client, + gint key, + GnomeDialogType dialog_type, + gpointer data); + +struct _GnomeClient +{ + GObject object; + + /* general information about the connection to the session manager */ + gpointer smc_conn; + + /* client id of this client */ + gchar *client_id; + + /* Previous client id of this client. */ + gchar *previous_id; + + /* Prefix for per save configuration files. */ + gchar *config_prefix; + + /* Prefix for app configuration files. */ + gchar *global_config_prefix; + + /* Static command line options. */ + GList *static_args; + + /* The following properties are predefined in the X session + management protocol. The entries marked with a 'x' are required + by the session management protocol. The entries marked with a + 's' are set automatically when creating a new gnome client. */ + gchar **clone_command; /*[xs]*/ + gchar *current_directory; /*[ ]*/ + gchar **discard_command; /*[ ]*/ + GHashTable *environment; /*[ ]*/ + pid_t process_id; /*[ s]*/ + gchar *program; /*[xs]*/ + gchar **resign_command; /*[ ]*/ + gchar **restart_command; /*[xs]*/ + GnomeRestartStyle restart_style; /*[ ]*/ + gchar **shutdown_command; /*[ ]*/ + gchar *user_id; /*[xs]*/ + + GSList *interaction_keys; + + + gint input_id; + + /* values sent with the last SaveYourself message */ + guint save_style : 2; /* GnomeRestartStyle */ + guint interact_style : 2; /* GnomeInteractStyle */ + + /* other internal state information */ + guint state : 3; /* GnomeClientState */ + + guint shutdown : 1; + guint fast : 1; + guint save_phase_2_requested : 1; + guint save_successfull : 1; + guint save_yourself_emitted : 1; + + gpointer reserved; /* Reserved for private struct */ +}; + + +struct _GnomeClientClass +{ + GObjectClass parent_class; + + gboolean (* save_yourself) (GnomeClient *client, + gint phase, + GnomeSaveStyle save_style, + gboolean shutdown, + GnomeInteractStyle interact_style, + gboolean fast); + void (* die) (GnomeClient *client); + void (* save_complete) (GnomeClient *client); + void (* shutdown_cancelled) (GnomeClient *client); + + void (* connect) (GnomeClient *client, + gboolean restarted); + void (* disconnect) (GnomeClient *client); + + /* Padding for possible expansion */ + gpointer padding1; + gpointer padding2; +}; + +//#define GNOME_CLIENT_MODULE gnome_client_module_info_get() +//const GnomeModuleInfo *gnome_client_module_info_get (void) G_GNUC_CONST; + +#define GNOME_CLIENT_PARAM_SM_CONNECT "sm-connect" + +GType gnome_client_get_type (void) G_GNUC_CONST; + +/* Get the master session management client. This master client gets + a client id, that may be specified by the '--sm-client-id' command + line option. A master client will be generated by 'gnome-init'. + If possible the master client will contact the session manager + after command-line parsing is finished (unless + 'gnome_client_disable_master_connection' was called). The master + client will also set the SM_CLIENT_ID property on the client leader + window of your application. + + Additionally, the master client gets some static arguments set + automatically (see 'gnome_client_add_static_arg' for static + arguments): 'gnome_init' passes all the command line options which + are recognised by gtk as static arguments to the master client. */ +GnomeClient *gnome_master_client (void); + +/* Get the config prefix for a client. This config prefix provides a + suitable place to store any details about the state of the client + which can not be described using the app's command line arguments (as + set in the restart command). You may push the returned value using + 'gnome_config_push_prefix' and read or write any values you require. */ +const gchar* gnome_client_get_config_prefix (GnomeClient *client); + +/* Get the config prefix that will be returned by the previous function + for clients which have NOT been restarted or cloned (i.e. for clients + started by the user without `--sm-' options). This config prefix may be + used to write the user's preferred config for these "new" clients. + + You could also use this prefix as a place to store and retrieve config + details that you wish to apply to ALL instances of the app. However, + this practice limits the users freedom to configure each instance in + a different way so it should be used with caution. */ +const gchar* gnome_client_get_global_config_prefix (GnomeClient *client); + +/* Set the value used for the global config prefix. The config prefixes + returned by gnome_client_get_config_prefix are formed by extending + this prefix with an unique identifier. + + The global config prefix defaults to a name based on the name of + the executable. This function allows you to set it to a different + value. It should be called BEFORE retrieving the config prefix for + the first time. Later calls will be ignored. + + For example, setting a global config prefix of "/app.d/session/" + would ensure that all your session save files or directories would + be gathered together into the app.d directory. */ +void gnome_client_set_global_config_prefix (GnomeClient *client, + const gchar* prefix); + +/* Returns some flags, that give additional information about this + client. Right now, the following flags are supported: + + - GNOME_CLIENT_IS_CONNECTED: The client is connected to a session + manager (It's the same information like using + GNOME_CLIENT_CONNECTED). + + - GNOME_CLIENT_RESTARTED: The client has been restarted, i. e. it + has been running with the same client id before. + + - GNOME_CLIENT_RESTORED: This flag is only used for the master + client. It indicates, that there may be a configuraion file from + which the clients state should be restored (using the + gnome_client_get_config_prefix call). */ + +GnomeClientFlags gnome_client_get_flags (GnomeClient *client); + +/* The following functions are used to set or unset the session + management properties that are used by the session manager to determine + how to handle the app. If you want to unset an array property, you + have to specify a NULL argv, if you want to unset a string property + you have to specify NULL as parameter. + + The `--sm-' options are automatically added as the first arguments + to the restart and clone commands and you should not try to set them. */ + +/* The session manager usually only restarts clients which were running + when the session was last saved. You can set the restart style to make + the manager restart the client: + - at the start of every session (GNOME_RESTART_ANYWAY) or + - whenever the client dies (GNOME_RESTART_IMMEDIATELY) or + - never (GNOME_RESTART_NEVER). */ +void gnome_client_set_restart_style (GnomeClient *client, + GnomeRestartStyle style); + +/* The gnome-session manager includes an extension to the protocol which + allows the order in which clients are started up to be organised into + a number of run levels. This function may be used to inform the + gnome-session manager of where this client should appear in this + run level ordering. The priority runs from 0 (started first) to 99 + (started last) and defaults to 50. Users may override the value + that is suggested to gnome-session by calling this function. */ +void gnome_client_set_priority (GnomeClient *client, + guint priority); + +/* Executing the restart command on the local host should reproduce + the state of the client at the time of the session save as closely + as possible. Saving config info under the gnome_client_get_config_prefix + is generally useful. */ +void gnome_client_set_restart_command (GnomeClient *client, + gint argc, gchar *argv[]); + +/* This function provides an alternative way of adding new arguments + to the restart command. The arguments are placed before the arguments + specified by 'gnome_client_set_restart_command' and after the arguments + recognised by gtk specified by the user on the original command line. */ +void gnome_client_add_static_arg (GnomeClient *client, ...); + +/* Executing the discard command on the local host should delete the + information saved as part of the session save that was in progress + when the discard command was set. For example: + + gchar *prefix = gnome_client_get_config_prefix (client); + gchar *argv[] = { "rm", "-r", NULL }; + argv[2] = gnome_config_get_real_path (prefix); + gnome_client_set_discard_command (client, 3, argv); */ +void gnome_client_set_discard_command (GnomeClient *client, + gint argc, gchar *argv[]); + +/* These two commands are used by clients that use the GNOME_RESTART_ANYWAY + restart style to to undo their effects (these clients usually perform + initialisation functions and leave effects behind after they die). + The shutdown command simply undoes the effects of the client. It is + executed during a normal logout. The resign command combines the effects + of a shutdown command and a discard command. It is executed when the user + decides that the client should cease to be restarted. */ +void gnome_client_set_resign_command (GnomeClient *client, + gint argc, gchar *argv[]); +void gnome_client_set_shutdown_command (GnomeClient *client, + gint argc, gchar *argv[]); + +/* All the preceeding session manager commands are executed in the directory + and environment set up by these two commands: */ +void gnome_client_set_current_directory (GnomeClient *client, + const gchar *dir); +void gnome_client_set_environment (GnomeClient *client, + const gchar *name, + const gchar *value); + +/* These four values are set automatically to the values required by the + session manager and you should not need to change them. The clone + command is directly copied from the restart command. */ +void gnome_client_set_clone_command (GnomeClient *client, + gint argc, gchar *argv[]); +void gnome_client_set_process_id (GnomeClient *client, + pid_t pid); +void gnome_client_set_program (GnomeClient *client, + const gchar *program); +void gnome_client_set_user_id (GnomeClient *client, + const gchar *id); + +/* The following function may be called during a "save_youself" handler + to request that a (modal) dialog is presented to the user. The session + manager decides when the dialog is shown and it will not be shown + unless the interact_style == GNOME_INTERACT_ANY. A "Cancel Logout" + button will be added during a shutdown. */ +void gnome_client_save_any_dialog (GnomeClient *client, + GtkDialog *dialog); + +/* The following function may be called during a "save_youself" handler + when an error has occurred during the save. The session manager decides + when the dialog is shown and it will not be shown when the interact_style + == GNOME_INTERACT_NONE. A "Cancel Logout" button will be added + during a shutdown. */ +void gnome_client_save_error_dialog (GnomeClient *client, + GtkDialog *dialog); + +/* Request the session manager to emit the "save_yourself" signal for + a second time after all the clients in the session have ceased + interacting with the user and entered an idle state. This might be + useful if your app manages other apps and requires that they are in + an idle state before saving its final data. */ +void gnome_client_request_phase_2 (GnomeClient *client); + +/* Request the session manager to save the session in some way. The + arguments correspond with the arguments passed to the "save_yourself" + signal handler. + + The save_style indicates whether the save should affect data accessible + to other users (GNOME_SAVE_GLOBAL) or only the state visible to + the current user (GNOME_SAVE_LOCAL) or both. Setting shutdown to + TRUE will initiate a logout. The interact_style specifies which kinds + of interaction will be available. Setting fast to TRUE will limit the + save to setting the session manager properties plus any essential data. + Setting the value of global to TRUE will request that all the other + apps in the session do a save as well. A global save is mandatory when + doing a shutdown. */ +void gnome_client_request_save (GnomeClient *client, + GnomeSaveStyle save_style, + gboolean shutdown, + GnomeInteractStyle interact_style, + gboolean fast, + gboolean global); + +/* Flush the underlying connection to the session manager. This is + useful if you have some pending changes that you want to make sure + get committed. */ +void gnome_client_flush (GnomeClient *client); + +#ifndef GNOME_DISABLE_DEPRECATED +/* Note: Use the GNOME_CLIENT_PARAM_SM_CONNECT property + * of GnomeProgram */ +/* Normally the master client is connected to the session manager + automatically, when calling 'gnome_init'. One can disable this + automatic connect by calling this function. Using this function + should definitely be an exception. */ +void gnome_client_disable_master_connection (void); +#endif /* GNOME_DISABLE_DEPRECATED */ + +/* Create a new session management client and try to connect to a + session manager. This is useful if you are acting as a proxy for + other executables that do not communicate with session manager. */ +GnomeClient *gnome_client_new (void); + +/* Create a new session management client. */ +GnomeClient *gnome_client_new_without_connection (void); + +/* Try to connect to a session manager. If the client was created + with a valid session management client id, we will try to connect + to the manager with this old id. If the connection was successful, + the "connect" signal will be emitted, after some default properties + have been sent to the session manager. */ +void gnome_client_connect (GnomeClient *client); + +/* Disconnect from the session manager. After disconnecting, the + "disconnect" signal will be emitted. */ +void gnome_client_disconnect (GnomeClient *client); + +/* Set the client id. This is only possible, if the client is not + connected to a session manager. */ +void gnome_client_set_id (GnomeClient *client, + const gchar *id); + +/* Get the client id of a session management client object. If this + object has never been connected to a session manager and a client + id hasn't been set, this function returns 'NULL'. */ +const gchar* gnome_client_get_id (GnomeClient *client); + +/* Get the client id from the last session. If this client was not + recreated from a previous session, returns NULL. The session + manager tries to maintain the same id from one session to another. */ +const gchar* gnome_client_get_previous_id (GnomeClient *client); + +/* Get the client ID of the desktop's current instance, i.e. if you + * consider the desktop as a whole as a session managed app, this + * returns its session ID (GNOME extension to SM) + */ +const gchar* gnome_client_get_desktop_id (GnomeClient *client); + +/* Use the following functions, if you want to interact with the user + during a "save_yourself" handler without being restricted to using + the dialog based commands gnome_client_save_[any/error]_dialog. + If and when the session manager decides that it's the app's turn to + interact then 'func' will be called with the specified arguments and + a unique 'GnomeInteractionKey'. The session manager will block other + clients from interacting until this key is returned with + 'gnome_interaction_key_return'. */ +void gnome_client_request_interaction (GnomeClient *client, + GnomeDialogType dialog_type, + GnomeInteractFunction function, + gpointer data); + +#if 0 +void gnome_client_request_interaction_interp (GnomeClient *client, + GnomeDialogType dialog_type, + GtkCallbackMarshal function, + gpointer data, + GDestroyNotify destroy); +#endif + +/* 'gnome_interaction_key_return' is used to tell gnome, that you are + finished with interaction */ +void gnome_interaction_key_return (gint key, + gboolean cancel_shutdown); + +G_END_DECLS + +#endif /* GNOME_CLIENT_H */ diff --git a/gb.gtk/src/sm/gnome-ice.c b/gb.gtk/src/sm/gnome-ice.c new file mode 100644 index 00000000..c82e9bc2 --- /dev/null +++ b/gb.gtk/src/sm/gnome-ice.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +/* gnome-ice.c - Interface between ICE and Gtk. + Written by Tom Tromey <tromey@cygnus.com>. */ + +#include "sm.h" + +#ifdef HAVE_LIBSM +#include <X11/ICE/ICElib.h> +#endif /* HAVE_LIBSM */ + +#include <unistd.h> +#include <fcntl.h> + +#include <gtk/gtk.h> +#include "gnome-ice.h" + +#ifdef HAVE_LIBSM + +static void gnome_ice_io_error_handler (IceConn connection); + +static void new_ice_connection (IceConn connection, IcePointer client_data, + Bool opening, IcePointer *watch_data); + +/* This is called when data is available on an ICE connection. */ +static gboolean +process_ice_messages (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + IceConn connection = (IceConn) data; + IceProcessMessagesStatus status; + + status = IceProcessMessages (connection, NULL, NULL); + + if (status == IceProcessMessagesIOError) + { + IcePointer context = IceGetConnectionContext (connection); + + if (context && G_IS_OBJECT (context)) + { + guint disconnect_id = g_signal_lookup ("disconnect", G_OBJECT_TYPE (context)); + + if (disconnect_id > 0) + g_signal_emit (context, disconnect_id, 0); + } + else + { + IceSetShutdownNegotiation (connection, False); + IceCloseConnection (connection); + } + } + + return TRUE; +} + +/* This is called when a new ICE connection is made. It arranges for + the ICE connection to be handled via the event loop. */ +static void +new_ice_connection (IceConn connection, IcePointer client_data, Bool opening, + IcePointer *watch_data) +{ + guint input_id; + + if (opening) + { + GIOChannel *channel; + /* Make sure we don't pass on these file descriptors to any + exec'ed children */ + fcntl(IceConnectionNumber(connection),F_SETFD, + fcntl(IceConnectionNumber(connection),F_GETFD,0) | FD_CLOEXEC); + + channel = g_io_channel_unix_new (IceConnectionNumber (connection)); + input_id = g_io_add_watch (channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI, + process_ice_messages, + connection); + g_io_channel_unref (channel); + + *watch_data = (IcePointer) GUINT_TO_POINTER (input_id); + } + else + { + input_id = GPOINTER_TO_UINT ((gpointer) *watch_data); + + g_source_remove (input_id); + } +} + +static IceIOErrorHandler gnome_ice_installed_handler; + +/* We call any handler installed before (or after) gnome_ice_init but + avoid calling the default libICE handler which does an exit() */ +static void +gnome_ice_io_error_handler (IceConn connection) +{ + if (gnome_ice_installed_handler) + (*gnome_ice_installed_handler) (connection); +} + +#endif /* HAVE_LIBSM */ + +/** + * gnome_ice_init: + * + * Initialises the ICE(Inter-Client Exchange). Used to make some initial calls into + * the ICE library in order to accept ICE connection . + */ +void +gnome_ice_init (void) +{ +#ifdef HAVE_LIBSM + static gboolean ice_init = FALSE; + + if (! ice_init) + { + IceIOErrorHandler default_handler; + + gnome_ice_installed_handler = IceSetIOErrorHandler (NULL); + default_handler = IceSetIOErrorHandler (gnome_ice_io_error_handler); + + if (gnome_ice_installed_handler == default_handler) + gnome_ice_installed_handler = NULL; + + IceAddConnectionWatch (new_ice_connection, NULL); + + ice_init = TRUE; + } +#endif /* HAVE_LIBSM */ +} diff --git a/gb.gtk/src/sm/gnome-ice.h b/gb.gtk/src/sm/gnome-ice.h new file mode 100644 index 00000000..5b8677f4 --- /dev/null +++ b/gb.gtk/src/sm/gnome-ice.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +/* gnome-ice.h - Interface between ICE and Gtk. + Written by Tom Tromey <tromey@cygnus.com>. */ + +#ifndef GNOME_ICE_H +#define GNOME_ICE_H + + + +G_BEGIN_DECLS + +/* This function should be called before any ICE functions are used. + It will arrange for ICE connections to be read and dispatched via + the Gtk event loop. This function can be called any number of + times without harm. */ +void gnome_ice_init (void); + +G_END_DECLS + +#endif /* GNOME_ICE_H */ diff --git a/gb.gtk/src/sm/gnome-macros.h b/gb.gtk/src/sm/gnome-macros.h new file mode 100644 index 00000000..7b521864 --- /dev/null +++ b/gb.gtk/src/sm/gnome-macros.h @@ -0,0 +1,64 @@ +/* gnome-macros.h + * Macros for making GObject objects to avoid typos and reduce code size + * Copyright (C) 2000 Eazel, Inc. + * + * Authors: George Lebl <jirka@5z.com> + * + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* + @NOTATION@ +*/ + +#ifndef GNOME_MACROS_H +#define GNOME_MACROS_H + +#ifndef GNOME_DISABLE_DEPRECATED + +#include "bonobo-macros.h" + +/* Macros for defining classes. Ideas taken from Nautilus and GOB. */ + +/* Define the boilerplate type stuff to reduce typos and code size. Defines + * the get_type method and the parent_class static variable. */ +#define GNOME_CLASS_BOILERPLATE(type, type_as_function, \ + parent_type, parent_type_macro) \ + BONOBO_BOILERPLATE(type, type_as_function, type, \ + parent_type, parent_type_macro, \ + GNOME_REGISTER_TYPE) +#define GNOME_REGISTER_TYPE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + g_type_register_static (parent_type_macro, #type, &object_info, 0) + +/* Just call the parent handler. This assumes that there is a variable + * named parent_class that points to the (duh!) parent class. Note that + * this macro is not to be used with things that return something, use + * the _WITH_DEFAULT version for that */ +#define GNOME_CALL_PARENT(parent_class_cast, name, args) \ + BONOBO_CALL_PARENT (parent_class_cast, name, args) + +/* Same as above, but in case there is no implementation, it evaluates + * to def_return */ +#define GNOME_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \ + name, args, def_return) \ + BONOBO_CALL_PARENT_WITH_DEFAULT ( \ + parent_class_cast, name, args, def_return) + +#endif /* !GNOME_DISABLE_DEPRECATED */ + +#endif /* GNOME_MACROS_H */ diff --git a/gb.gtk/src/sm/gnome-marshal.c b/gb.gtk/src/sm/gnome-marshal.c new file mode 100644 index 00000000..fcef6149 --- /dev/null +++ b/gb.gtk/src/sm/gnome-marshal.c @@ -0,0 +1,369 @@ + +#include <glib-object.h> + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_schar (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* BOOLEAN:INT,ENUM,BOOLEAN,ENUM,BOOLEAN (./gnome-marshal.list:1) */ +void +_gnome_marshal_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN) (gpointer data1, + gint arg_1, + gint arg_2, + gboolean arg_3, + gint arg_4, + gboolean arg_5, + gpointer data2); + GMarshalFunc_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 6); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_enum (param_values + 2), + g_marshal_value_peek_boolean (param_values + 3), + g_marshal_value_peek_enum (param_values + 4), + g_marshal_value_peek_boolean (param_values + 5), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN:INT,STRING (./gnome-marshal.list:2) */ +void +_gnome_marshal_BOOLEAN__INT_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_STRING) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + GMarshalFunc_BOOLEAN__INT_STRING callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_STRING) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN:OBJECT (./gnome-marshal.list:3) */ +void +_gnome_marshal_BOOLEAN__OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer data1, + gpointer arg_1, + gpointer data2); + GMarshalFunc_BOOLEAN__OBJECT callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_object (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN:VOID (./gnome-marshal.list:4) */ +void +_gnome_marshal_BOOLEAN__VOID (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__VOID) (gpointer data1, + gpointer data2); + GMarshalFunc_BOOLEAN__VOID callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 1); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__VOID) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* INT:VOID (./gnome-marshal.list:5) */ +void +_gnome_marshal_INT__VOID (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gint (*GMarshalFunc_INT__VOID) (gpointer data1, + gpointer data2); + GMarshalFunc_INT__VOID callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + gint v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 1); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_INT__VOID) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + data2); + + g_value_set_int (return_value, v_return); +} + +/* VOID:ENUM,BOOLEAN (./gnome-marshal.list:6) */ +void +_gnome_marshal_VOID__ENUM_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__ENUM_BOOLEAN) (gpointer data1, + gint arg_1, + gboolean arg_2, + gpointer data2); + GMarshalFunc_VOID__ENUM_BOOLEAN callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__ENUM_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_enum (param_values + 1), + g_marshal_value_peek_boolean (param_values + 2), + data2); +} + +/* VOID:INT,BOXED (./gnome-marshal.list:7) */ +void +_gnome_marshal_VOID__INT_BOXED (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_BOXED) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + GMarshalFunc_VOID__INT_BOXED callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); +} + +/* VOID:OBJECT (./gnome-marshal.list:8) */ + +/* VOID:UINT,UINT,UINT,UINT (./gnome-marshal.list:9) */ +void +_gnome_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (gpointer data1, + guint arg_1, + guint arg_2, + guint arg_3, + guint arg_4, + gpointer data2); + GMarshalFunc_VOID__UINT_UINT_UINT_UINT callback; + GCClosure *cc = (GCClosure*) closure; + gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_uint (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + g_marshal_value_peek_uint (param_values + 3), + g_marshal_value_peek_uint (param_values + 4), + data2); +} + diff --git a/gb.gtk/src/sm/gnome-marshal.h b/gb.gtk/src/sm/gnome-marshal.h new file mode 100644 index 00000000..95c7a997 --- /dev/null +++ b/gb.gtk/src/sm/gnome-marshal.h @@ -0,0 +1,79 @@ + +#ifndef ___gnome_marshal_MARSHAL_H__ +#define ___gnome_marshal_MARSHAL_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/* BOOLEAN:INT,ENUM,BOOLEAN,ENUM,BOOLEAN (./gnome-marshal.list:1) */ +extern void _gnome_marshal_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:INT,STRING (./gnome-marshal.list:2) */ +extern void _gnome_marshal_BOOLEAN__INT_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:OBJECT (./gnome-marshal.list:3) */ +extern void _gnome_marshal_BOOLEAN__OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:VOID (./gnome-marshal.list:4) */ +extern void _gnome_marshal_BOOLEAN__VOID (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* INT:VOID (./gnome-marshal.list:5) */ +extern void _gnome_marshal_INT__VOID (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:ENUM,BOOLEAN (./gnome-marshal.list:6) */ +extern void _gnome_marshal_VOID__ENUM_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:INT,BOXED (./gnome-marshal.list:7) */ +extern void _gnome_marshal_VOID__INT_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:OBJECT (./gnome-marshal.list:8) */ +#define _gnome_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT + +/* VOID:UINT,UINT,UINT,UINT (./gnome-marshal.list:9) */ +extern void _gnome_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* ___gnome_marshal_MARSHAL_H__ */ + diff --git a/gb.gtk/src/sm/gnome-uidefs.h b/gb.gtk/src/sm/gnome-uidefs.h new file mode 100644 index 00000000..dbf0e828 --- /dev/null +++ b/gb.gtk/src/sm/gnome-uidefs.h @@ -0,0 +1,122 @@ +/* gnome-uidefs.h: + * Copyright (C) 1998 Havoc Pennington + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + */ +/* + @NOTATION@ +*/ + +#ifndef GNOME_UIDEFS_H +#define GNOME_UIDEFS_H + +/* This file defines standard sizes, spacings, and whatever + else seems standardizable via simple defines. */ + +/* All-purpose padding. If you always use these instead of making up + some arbitrary padding number that looks good on your screen, + people can change the "spaciousness" of the GUI globally. */ +#define GNOME_PAD 8 +#define GNOME_PAD_SMALL 4 +#define GNOME_PAD_BIG 12 + +/* Deprecated with GnomeDialog, GTK has a much nicer way of + * doing this */ +#ifndef GNOME_DISABLE_DEPRECATED + +/* These are the button numbers on a yes-no or ok-cancel GnomeDialog, + and in the gnome-app-util callbacks. Make the program more + readable, is all. */ +#define GNOME_YES 0 +#define GNOME_NO 1 +#define GNOME_OK 0 +#define GNOME_CANCEL 1 + +#endif + +#include <gdk/gdkkeysyms.h> + +/* These are keybindings, in GnomeUIInfo format. USE THEM OR DIE! + Add to the list as well.. +*/ + +#define GNOME_KEY_NAME_QUIT 'q' +#define GNOME_KEY_MOD_QUIT (GDK_CONTROL_MASK) + +#ifndef GNOME_DISABLE_DEPRECATED + +#define GNOME_KEY_NAME_EXIT GNOME_KEY_NAME_QUIT +#define GNOME_KEY_MOD_EXIT GNOME_KEY_MOD_QUIT + +#endif + +#define GNOME_KEY_NAME_CLOSE 'w' +#define GNOME_KEY_MOD_CLOSE (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_CUT 'x' +#define GNOME_KEY_MOD_CUT (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_COPY 'c' +#define GNOME_KEY_MOD_COPY (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_PASTE 'v' +#define GNOME_KEY_MOD_PASTE (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_SELECT_ALL 'a' +#define GNOME_KEY_MOD_SELECT_ALL (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_CLEAR 0 +#define GNOME_KEY_MOD_CLEAR (0) + +#define GNOME_KEY_NAME_UNDO 'z' +#define GNOME_KEY_MOD_UNDO (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_REDO 'z' +#define GNOME_KEY_MOD_REDO (GDK_CONTROL_MASK | GDK_SHIFT_MASK) + +#define GNOME_KEY_NAME_SAVE 's' +#define GNOME_KEY_MOD_SAVE (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_OPEN 'o' +#define GNOME_KEY_MOD_OPEN (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_SAVE_AS 's' +#define GNOME_KEY_MOD_SAVE_AS (GDK_CONTROL_MASK | GDK_SHIFT_MASK) +#define GNOME_KEY_NAME_NEW 'n' +#define GNOME_KEY_MOD_NEW (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_PRINT 'p' +#define GNOME_KEY_MOD_PRINT (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_PRINT_SETUP 0 +#define GNOME_KEY_MOD_PRINT_SETUP (0) + +#define GNOME_KEY_NAME_FIND 'f' +#define GNOME_KEY_MOD_FIND (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_FIND_AGAIN 'g' +#define GNOME_KEY_MOD_FIND_AGAIN (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_REPLACE 'r' +#define GNOME_KEY_MOD_REPLACE (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_NEW_WINDOW 0 +#define GNOME_KEY_MOD_NEW_WINDOW (0) +#define GNOME_KEY_NAME_CLOSE_WINDOW 0 +#define GNOME_KEY_MOD_CLOSE_WINDOW (0) + +#define GNOME_KEY_NAME_REDO_MOVE 'z' +#define GNOME_KEY_MOD_REDO_MOVE (GDK_CONTROL_MASK | GDK_SHIFT_MASK) +#define GNOME_KEY_NAME_UNDO_MOVE 'z' +#define GNOME_KEY_MOD_UNDO_MOVE (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_PAUSE_GAME GDK_Pause +#define GNOME_KEY_MOD_PAUSE_GAME (0) +#define GNOME_KEY_NAME_NEW_GAME 'n' +#define GNOME_KEY_MOD_NEW_GAME (GDK_CONTROL_MASK) + +#endif diff --git a/gb.gtk/src/sm/gnometypebuiltins.c b/gb.gtk/src/sm/gnometypebuiltins.c new file mode 100644 index 00000000..2727c751 --- /dev/null +++ b/gb.gtk/src/sm/gnometypebuiltins.c @@ -0,0 +1,459 @@ + + + +//#include "libgnomeui.h" + +#include "gnome-client.h" +#include "gnometypebuiltins.h" + +#if 0 +/* enumerations from "gnome-app-helper.h" */ +GType +gnome_ui_info_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_ui_info_type_values[] = { + { GNOME_APP_UI_ENDOFINFO, "GNOME_APP_UI_ENDOFINFO", "endofinfo" }, + { GNOME_APP_UI_ITEM, "GNOME_APP_UI_ITEM", "item" }, + { GNOME_APP_UI_TOGGLEITEM, "GNOME_APP_UI_TOGGLEITEM", "toggleitem" }, + { GNOME_APP_UI_RADIOITEMS, "GNOME_APP_UI_RADIOITEMS", "radioitems" }, + { GNOME_APP_UI_SUBTREE, "GNOME_APP_UI_SUBTREE", "subtree" }, + { GNOME_APP_UI_SEPARATOR, "GNOME_APP_UI_SEPARATOR", "separator" }, + { GNOME_APP_UI_HELP, "GNOME_APP_UI_HELP", "help" }, + { GNOME_APP_UI_BUILDER_DATA, "GNOME_APP_UI_BUILDER_DATA", "builder-data" }, + { GNOME_APP_UI_ITEM_CONFIGURABLE, "GNOME_APP_UI_ITEM_CONFIGURABLE", "item-configurable" }, + { GNOME_APP_UI_SUBTREE_STOCK, "GNOME_APP_UI_SUBTREE_STOCK", "subtree-stock" }, + { GNOME_APP_UI_INCLUDE, "GNOME_APP_UI_INCLUDE", "include" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeUIInfoType", _gnome_ui_info_type_values); + } + + return type; +} + +GType +gnome_ui_info_configurable_types_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_ui_info_configurable_types_values[] = { + { GNOME_APP_CONFIGURABLE_ITEM_NEW, "GNOME_APP_CONFIGURABLE_ITEM_NEW", "new" }, + { GNOME_APP_CONFIGURABLE_ITEM_OPEN, "GNOME_APP_CONFIGURABLE_ITEM_OPEN", "open" }, + { GNOME_APP_CONFIGURABLE_ITEM_SAVE, "GNOME_APP_CONFIGURABLE_ITEM_SAVE", "save" }, + { GNOME_APP_CONFIGURABLE_ITEM_SAVE_AS, "GNOME_APP_CONFIGURABLE_ITEM_SAVE_AS", "save-as" }, + { GNOME_APP_CONFIGURABLE_ITEM_REVERT, "GNOME_APP_CONFIGURABLE_ITEM_REVERT", "revert" }, + { GNOME_APP_CONFIGURABLE_ITEM_PRINT, "GNOME_APP_CONFIGURABLE_ITEM_PRINT", "print" }, + { GNOME_APP_CONFIGURABLE_ITEM_PRINT_SETUP, "GNOME_APP_CONFIGURABLE_ITEM_PRINT_SETUP", "print-setup" }, + { GNOME_APP_CONFIGURABLE_ITEM_CLOSE, "GNOME_APP_CONFIGURABLE_ITEM_CLOSE", "close" }, + { GNOME_APP_CONFIGURABLE_ITEM_QUIT, "GNOME_APP_CONFIGURABLE_ITEM_QUIT", "quit" }, + { GNOME_APP_CONFIGURABLE_ITEM_CUT, "GNOME_APP_CONFIGURABLE_ITEM_CUT", "cut" }, + { GNOME_APP_CONFIGURABLE_ITEM_COPY, "GNOME_APP_CONFIGURABLE_ITEM_COPY", "copy" }, + { GNOME_APP_CONFIGURABLE_ITEM_PASTE, "GNOME_APP_CONFIGURABLE_ITEM_PASTE", "paste" }, + { GNOME_APP_CONFIGURABLE_ITEM_CLEAR, "GNOME_APP_CONFIGURABLE_ITEM_CLEAR", "clear" }, + { GNOME_APP_CONFIGURABLE_ITEM_UNDO, "GNOME_APP_CONFIGURABLE_ITEM_UNDO", "undo" }, + { GNOME_APP_CONFIGURABLE_ITEM_REDO, "GNOME_APP_CONFIGURABLE_ITEM_REDO", "redo" }, + { GNOME_APP_CONFIGURABLE_ITEM_FIND, "GNOME_APP_CONFIGURABLE_ITEM_FIND", "find" }, + { GNOME_APP_CONFIGURABLE_ITEM_FIND_AGAIN, "GNOME_APP_CONFIGURABLE_ITEM_FIND_AGAIN", "find-again" }, + { GNOME_APP_CONFIGURABLE_ITEM_REPLACE, "GNOME_APP_CONFIGURABLE_ITEM_REPLACE", "replace" }, + { GNOME_APP_CONFIGURABLE_ITEM_PROPERTIES, "GNOME_APP_CONFIGURABLE_ITEM_PROPERTIES", "properties" }, + { GNOME_APP_CONFIGURABLE_ITEM_PREFERENCES, "GNOME_APP_CONFIGURABLE_ITEM_PREFERENCES", "preferences" }, + { GNOME_APP_CONFIGURABLE_ITEM_ABOUT, "GNOME_APP_CONFIGURABLE_ITEM_ABOUT", "about" }, + { GNOME_APP_CONFIGURABLE_ITEM_SELECT_ALL, "GNOME_APP_CONFIGURABLE_ITEM_SELECT_ALL", "select-all" }, + { GNOME_APP_CONFIGURABLE_ITEM_NEW_WINDOW, "GNOME_APP_CONFIGURABLE_ITEM_NEW_WINDOW", "new-window" }, + { GNOME_APP_CONFIGURABLE_ITEM_CLOSE_WINDOW, "GNOME_APP_CONFIGURABLE_ITEM_CLOSE_WINDOW", "close-window" }, + { GNOME_APP_CONFIGURABLE_ITEM_NEW_GAME, "GNOME_APP_CONFIGURABLE_ITEM_NEW_GAME", "new-game" }, + { GNOME_APP_CONFIGURABLE_ITEM_PAUSE_GAME, "GNOME_APP_CONFIGURABLE_ITEM_PAUSE_GAME", "pause-game" }, + { GNOME_APP_CONFIGURABLE_ITEM_RESTART_GAME, "GNOME_APP_CONFIGURABLE_ITEM_RESTART_GAME", "restart-game" }, + { GNOME_APP_CONFIGURABLE_ITEM_UNDO_MOVE, "GNOME_APP_CONFIGURABLE_ITEM_UNDO_MOVE", "undo-move" }, + { GNOME_APP_CONFIGURABLE_ITEM_REDO_MOVE, "GNOME_APP_CONFIGURABLE_ITEM_REDO_MOVE", "redo-move" }, + { GNOME_APP_CONFIGURABLE_ITEM_HINT, "GNOME_APP_CONFIGURABLE_ITEM_HINT", "hint" }, + { GNOME_APP_CONFIGURABLE_ITEM_SCORES, "GNOME_APP_CONFIGURABLE_ITEM_SCORES", "scores" }, + { GNOME_APP_CONFIGURABLE_ITEM_END_GAME, "GNOME_APP_CONFIGURABLE_ITEM_END_GAME", "end-game" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeUIInfoConfigurableTypes", _gnome_ui_info_configurable_types_values); + } + + return type; +} + +GType +gnome_ui_pixmap_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_ui_pixmap_type_values[] = { + { GNOME_APP_PIXMAP_NONE, "GNOME_APP_PIXMAP_NONE", "none" }, + { GNOME_APP_PIXMAP_STOCK, "GNOME_APP_PIXMAP_STOCK", "stock" }, + { GNOME_APP_PIXMAP_DATA, "GNOME_APP_PIXMAP_DATA", "data" }, + { GNOME_APP_PIXMAP_FILENAME, "GNOME_APP_PIXMAP_FILENAME", "filename" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeUIPixmapType", _gnome_ui_pixmap_type_values); + } + + return type; +} +#endif + +/* enumerations from "gnome-client.h" */ +GType +gnome_interact_style_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_interact_style_values[] = { + { GNOME_INTERACT_NONE, "GNOME_INTERACT_NONE", "none" }, + { GNOME_INTERACT_ERRORS, "GNOME_INTERACT_ERRORS", "errors" }, + { GNOME_INTERACT_ANY, "GNOME_INTERACT_ANY", "any" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeInteractStyle", _gnome_interact_style_values); + } + + return type; +} + +GType +gnome_dialog_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_dialog_type_values[] = { + { GNOME_DIALOG_ERROR, "GNOME_DIALOG_ERROR", "error" }, + { GNOME_DIALOG_NORMAL, "GNOME_DIALOG_NORMAL", "normal" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeDialogType", _gnome_dialog_type_values); + } + + return type; +} + +GType +gnome_save_style_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_save_style_values[] = { + { GNOME_SAVE_GLOBAL, "GNOME_SAVE_GLOBAL", "global" }, + { GNOME_SAVE_LOCAL, "GNOME_SAVE_LOCAL", "local" }, + { GNOME_SAVE_BOTH, "GNOME_SAVE_BOTH", "both" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeSaveStyle", _gnome_save_style_values); + } + + return type; +} + +GType +gnome_restart_style_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_restart_style_values[] = { + { GNOME_RESTART_IF_RUNNING, "GNOME_RESTART_IF_RUNNING", "if-running" }, + { GNOME_RESTART_ANYWAY, "GNOME_RESTART_ANYWAY", "anyway" }, + { GNOME_RESTART_IMMEDIATELY, "GNOME_RESTART_IMMEDIATELY", "immediately" }, + { GNOME_RESTART_NEVER, "GNOME_RESTART_NEVER", "never" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeRestartStyle", _gnome_restart_style_values); + } + + return type; +} + +GType +gnome_client_state_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_client_state_values[] = { + { GNOME_CLIENT_IDLE, "GNOME_CLIENT_IDLE", "idle" }, + { GNOME_CLIENT_SAVING_PHASE_1, "GNOME_CLIENT_SAVING_PHASE_1", "saving-phase-1" }, + { GNOME_CLIENT_WAITING_FOR_PHASE_2, "GNOME_CLIENT_WAITING_FOR_PHASE_2", "waiting-for-phase-2" }, + { GNOME_CLIENT_SAVING_PHASE_2, "GNOME_CLIENT_SAVING_PHASE_2", "saving-phase-2" }, + { GNOME_CLIENT_FROZEN, "GNOME_CLIENT_FROZEN", "frozen" }, + { GNOME_CLIENT_DISCONNECTED, "GNOME_CLIENT_DISCONNECTED", "disconnected" }, + { GNOME_CLIENT_REGISTERING, "GNOME_CLIENT_REGISTERING", "registering" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeClientState", _gnome_client_state_values); + } + + return type; +} + +GType +gnome_client_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_client_flags_values[] = { + { GNOME_CLIENT_IS_CONNECTED, "GNOME_CLIENT_IS_CONNECTED", "is-connected" }, + { GNOME_CLIENT_RESTARTED, "GNOME_CLIENT_RESTARTED", "restarted" }, + { GNOME_CLIENT_RESTORED, "GNOME_CLIENT_RESTORED", "restored" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeClientFlags", _gnome_client_flags_values); + } + + return type; +} + +#if 0 +/* enumerations from "gnome-dateedit.h" */ +GType +gnome_date_edit_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_date_edit_flags_values[] = { + { GNOME_DATE_EDIT_SHOW_TIME, "GNOME_DATE_EDIT_SHOW_TIME", "show-time" }, + { GNOME_DATE_EDIT_24_HR, "GNOME_DATE_EDIT_24_HR", "24-hr" }, + { GNOME_DATE_EDIT_WEEK_STARTS_ON_MONDAY, "GNOME_DATE_EDIT_WEEK_STARTS_ON_MONDAY", "week-starts-on-monday" }, + { GNOME_DATE_EDIT_DISPLAY_SECONDS, "GNOME_DATE_EDIT_DISPLAY_SECONDS", "display-seconds" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeDateEditFlags", _gnome_date_edit_flags_values); + } + + return type; +} + + +/* enumerations from "gnome-druid-page-edge.h" */ +GType +gnome_edge_position_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_edge_position_values[] = { + { GNOME_EDGE_START, "GNOME_EDGE_START", "start" }, + { GNOME_EDGE_FINISH, "GNOME_EDGE_FINISH", "finish" }, + { GNOME_EDGE_OTHER, "GNOME_EDGE_OTHER", "other" }, + { GNOME_EDGE_LAST, "GNOME_EDGE_LAST", "last" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeEdgePosition", _gnome_edge_position_values); + } + + return type; +} + + +/* enumerations from "gnome-font-picker.h" */ +GType +gnome_font_picker_mode_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_font_picker_mode_values[] = { + { GNOME_FONT_PICKER_MODE_PIXMAP, "GNOME_FONT_PICKER_MODE_PIXMAP", "pixmap" }, + { GNOME_FONT_PICKER_MODE_FONT_INFO, "GNOME_FONT_PICKER_MODE_FONT_INFO", "font-info" }, + { GNOME_FONT_PICKER_MODE_USER_WIDGET, "GNOME_FONT_PICKER_MODE_USER_WIDGET", "user-widget" }, + { GNOME_FONT_PICKER_MODE_UNKNOWN, "GNOME_FONT_PICKER_MODE_UNKNOWN", "unknown" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeFontPickerMode", _gnome_font_picker_mode_values); + } + + return type; +} + + +/* enumerations from "gnome-icon-list.h" */ +GType +gnome_icon_list_mode_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_icon_list_mode_values[] = { + { GNOME_ICON_LIST_ICONS, "GNOME_ICON_LIST_ICONS", "icons" }, + { GNOME_ICON_LIST_TEXT_BELOW, "GNOME_ICON_LIST_TEXT_BELOW", "text-below" }, + { GNOME_ICON_LIST_TEXT_RIGHT, "GNOME_ICON_LIST_TEXT_RIGHT", "text-right" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeIconListMode", _gnome_icon_list_mode_values); + } + + return type; +} + + +/* enumerations from "gnome-icon-lookup.h" */ +GType +gnome_icon_lookup_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_icon_lookup_flags_values[] = { + { GNOME_ICON_LOOKUP_FLAGS_NONE, "GNOME_ICON_LOOKUP_FLAGS_NONE", "none" }, + { GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT, "GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT", "embedding-text" }, + { GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES, "GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES", "show-small-images-as-themselves" }, + { GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES, "GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES", "allow-svg-as-themselves" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeIconLookupFlags", _gnome_icon_lookup_flags_values); + } + + return type; +} + +GType +gnome_icon_lookup_result_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_icon_lookup_result_flags_values[] = { + { GNOME_ICON_LOOKUP_RESULT_FLAGS_NONE, "GNOME_ICON_LOOKUP_RESULT_FLAGS_NONE", "none" }, + { GNOME_ICON_LOOKUP_RESULT_FLAGS_THUMBNAIL, "GNOME_ICON_LOOKUP_RESULT_FLAGS_THUMBNAIL", "thumbnail" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeIconLookupResultFlags", _gnome_icon_lookup_result_flags_values); + } + + return type; +} + + +/* enumerations from "gnome-mdi.h" */ +GType +gnome_mdi_mode_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_mdi_mode_values[] = { + { GNOME_MDI_NOTEBOOK, "GNOME_MDI_NOTEBOOK", "notebook" }, + { GNOME_MDI_TOPLEVEL, "GNOME_MDI_TOPLEVEL", "toplevel" }, + { GNOME_MDI_MODAL, "GNOME_MDI_MODAL", "modal" }, + { GNOME_MDI_DEFAULT_MODE, "GNOME_MDI_DEFAULT_MODE", "default-mode" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeMDIMode", _gnome_mdi_mode_values); + } + + return type; +} + + +/* enumerations from "gnome-password-dialog.h" */ +GType +gnome_password_dialog_remember_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_password_dialog_remember_values[] = { + { GNOME_PASSWORD_DIALOG_REMEMBER_NOTHING, "GNOME_PASSWORD_DIALOG_REMEMBER_NOTHING", "nothing" }, + { GNOME_PASSWORD_DIALOG_REMEMBER_SESSION, "GNOME_PASSWORD_DIALOG_REMEMBER_SESSION", "session" }, + { GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER, "GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER", "forever" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomePasswordDialogRemember", _gnome_password_dialog_remember_values); + } + + return type; +} + + +/* enumerations from "gnome-thumbnail.h" */ +GType +gnome_thumbnail_size_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_thumbnail_size_values[] = { + { GNOME_THUMBNAIL_SIZE_NORMAL, "GNOME_THUMBNAIL_SIZE_NORMAL", "normal" }, + { GNOME_THUMBNAIL_SIZE_LARGE, "GNOME_THUMBNAIL_SIZE_LARGE", "large" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeThumbnailSize", _gnome_thumbnail_size_values); + } + + return type; +} + + +/* enumerations from "gnome-types.h" */ +GType +gnome_preferences_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_preferences_type_values[] = { + { GNOME_PREFERENCES_NEVER, "GNOME_PREFERENCES_NEVER", "never" }, + { GNOME_PREFERENCES_USER, "GNOME_PREFERENCES_USER", "user" }, + { GNOME_PREFERENCES_ALWAYS, "GNOME_PREFERENCES_ALWAYS", "always" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomePreferencesType", _gnome_preferences_type_values); + } + + return type; +} +#endif + + + + diff --git a/gb.gtk/src/sm/gnometypebuiltins.h b/gb.gtk/src/sm/gnometypebuiltins.h new file mode 100644 index 00000000..09eb506c --- /dev/null +++ b/gb.gtk/src/sm/gnometypebuiltins.h @@ -0,0 +1,76 @@ + + + +#ifndef __GNOMETYPEBUILTINS_H__ +#define __GNOMETYPEBUILTINS_H__ 1 + +#include <glib-object.h> + +G_BEGIN_DECLS + + +/* --- gnome-app-helper.h --- */ +#define GNOME_TYPE_UI_INFO_TYPE gnome_ui_info_type_get_type() +GType gnome_ui_info_type_get_type (void); +#define GNOME_TYPE_UI_INFO_CONFIGURABLE_TYPES gnome_ui_info_configurable_types_get_type() +GType gnome_ui_info_configurable_types_get_type (void); +#define GNOME_TYPE_UI_PIXMAP_TYPE gnome_ui_pixmap_type_get_type() +GType gnome_ui_pixmap_type_get_type (void); + +/* --- gnome-client.h --- */ +#define GNOME_TYPE_INTERACT_STYLE gnome_interact_style_get_type() +GType gnome_interact_style_get_type (void); +#define GNOME_TYPE_DIALOG_TYPE gnome_dialog_type_get_type() +GType gnome_dialog_type_get_type (void); +#define GNOME_TYPE_SAVE_STYLE gnome_save_style_get_type() +GType gnome_save_style_get_type (void); +#define GNOME_TYPE_RESTART_STYLE gnome_restart_style_get_type() +GType gnome_restart_style_get_type (void); +#define GNOME_TYPE_CLIENT_STATE gnome_client_state_get_type() +GType gnome_client_state_get_type (void); +#define GNOME_TYPE_CLIENT_FLAGS gnome_client_flags_get_type() +GType gnome_client_flags_get_type (void); + +/* --- gnome-dateedit.h --- */ +#define GNOME_TYPE_DATE_EDIT_FLAGS gnome_date_edit_flags_get_type() +GType gnome_date_edit_flags_get_type (void); + +/* --- gnome-druid-page-edge.h --- */ +#define GNOME_TYPE_EDGE_POSITION gnome_edge_position_get_type() +GType gnome_edge_position_get_type (void); + +/* --- gnome-font-picker.h --- */ +#define GNOME_TYPE_FONT_PICKER_MODE gnome_font_picker_mode_get_type() +GType gnome_font_picker_mode_get_type (void); + +/* --- gnome-icon-list.h --- */ +#define GNOME_TYPE_ICON_LIST_MODE gnome_icon_list_mode_get_type() +GType gnome_icon_list_mode_get_type (void); + +/* --- gnome-icon-lookup.h --- */ +#define GNOME_TYPE_ICON_LOOKUP_FLAGS gnome_icon_lookup_flags_get_type() +GType gnome_icon_lookup_flags_get_type (void); +#define GNOME_TYPE_ICON_LOOKUP_RESULT_FLAGS gnome_icon_lookup_result_flags_get_type() +GType gnome_icon_lookup_result_flags_get_type (void); + +/* --- gnome-mdi.h --- */ +#define GNOME_TYPE_MDI_MODE gnome_mdi_mode_get_type() +GType gnome_mdi_mode_get_type (void); + +/* --- gnome-password-dialog.h --- */ +#define GNOME_TYPE_PASSWORD_DIALOG_REMEMBER gnome_password_dialog_remember_get_type() +GType gnome_password_dialog_remember_get_type (void); + +/* --- gnome-thumbnail.h --- */ +#define GNOME_TYPE_THUMBNAIL_SIZE gnome_thumbnail_size_get_type() +GType gnome_thumbnail_size_get_type (void); + +/* --- gnome-types.h --- */ +#define GNOME_TYPE_PREFERENCES_TYPE gnome_preferences_type_get_type() +GType gnome_preferences_type_get_type (void); +G_END_DECLS + +#endif /* __GNOMETYPEBUILTINS_H__ */ + + + diff --git a/gb.gtk/src/sm/libgnomeui.h b/gb.gtk/src/sm/libgnomeui.h new file mode 100644 index 00000000..0dcd851f --- /dev/null +++ b/gb.gtk/src/sm/libgnomeui.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; c-set-style: linux; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +#ifndef LIBGNOMEUI_H +#define LIBGNOMEUI_H + + +#include <libgnomeui/gnome-uidefs.h> + +#include <libgnomeui/gnome-about.h> +#include <libgnomeui/gnome-app.h> +#include <libgnomeui/gnome-app-helper.h> +#include <libgnomeui/gnome-app-util.h> +#include <libgnomeui/gnome-appbar.h> +#include <libgnomeui/gnome-authentication-manager.h> +#include <libgnomecanvas/gnome-canvas.h> +#include <libgnomecanvas/gnome-canvas-pixbuf.h> +#include <libgnomecanvas/gnome-canvas-line.h> +#include <libgnomecanvas/gnome-canvas-rect-ellipse.h> +#include <libgnomecanvas/gnome-canvas-polygon.h> +#include <libgnomecanvas/gnome-canvas-text.h> +#include <libgnomecanvas/gnome-canvas-util.h> +#include <libgnomecanvas/gnome-canvas-widget.h> +#include <libgnomeui/gnome-color-picker.h> +#include <libgnomeui/gnome-entry.h> +#include <libgnomeui/gnome-file-entry.h> +#include <libgnomeui/gnome-font-picker.h> +#include <libgnomeui/gnome-icon-entry.h> +#include <libgnomeui/gnome-icon-item.h> +#include <libgnomeui/gnome-icon-list.h> +#include <libgnomeui/gnome-icon-sel.h> +#include <libgnomeui/gnome-ui-init.h> +#include <libgnomeui/gnome-types.h> +#include <libgnomeui/gnome-icon-theme.h> +#include <libgnomeui/gnome-thumbnail.h> +#include <libgnomeui/gnome-icon-lookup.h> +#include <libgnomeui/gnome-stock-icons.h> +#include <libgnomeui/gnome-scores.h> +#include <libgnomeui/gnome-client.h> +#include <libgnomeui/gnome-dateedit.h> +#include <libgnomeui/gnometypebuiltins.h> +#include <libgnomeui/gnome-href.h> +#include <libgnomeui/gnome-dialog.h> +#include <libgnomeui/gnome-dialog-util.h> +#include <libgnomeui/gnome-druid.h> +#include <libgnomeui/gnome-druid-page.h> +#include <libgnomeui/gnome-druid-page-edge.h> +#include <libgnomeui/gnome-druid-page-standard.h> +#include <libgnomeui/gnome-messagebox.h> +#include <libgnomeui/gnome-mdi.h> +#include <libgnomeui/gnome-mdi-child.h> +#include <libgnomeui/gnome-mdi-generic-child.h> +#include <libgnomeui/gnome-mdi-session.h> +#include <libgnomeui/gnome-vfs-util.h> +#include <libgnomeui/gnome-pixmap.h> +#include <libgnomeui/gnome-pixmap-entry.h> +#include <libgnomeui/gnome-popup-menu.h> +#include <libgnomeui/gnome-propertybox.h> +#include <libgnomeui/gnome-window.h> +#include <libgnomeui/gnome-window-icon.h> +#include <libgnomeui/gnome-password-dialog.h> +#include <libgnomeui/gnome-help.h> +#include <libgnomeui/gnome-url.h> + +#endif diff --git a/gb.gtk/src/sm/libgnomeuiP.h b/gb.gtk/src/sm/libgnomeuiP.h new file mode 100644 index 00000000..fb234868 --- /dev/null +++ b/gb.gtk/src/sm/libgnomeuiP.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; c-set-style: linux; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +#ifndef LIBGNOMEUIP_H +#define LIBGNOMEUIP_H + +#include <glib.h> + + +//#include <libgnome/libgnome.h> +#include "gnome-macros.h" +#include "gnometypebuiltins.h" +//#include "gnome-gconf-ui.h" + +G_BEGIN_DECLS + +void gnome_type_init(void); + +#ifdef G_OS_WIN32 +const char *_gnome_ui_get_localedir (void); +const char *_gnome_ui_get_datadir (void); + +#undef GNOMEUILOCALEDIR +#define GNOMEUILOCALEDIR _gnome_ui_get_localedir() +#undef LIBGNOMEUI_DATADIR +#define LIBGNOMEUI_DATADIR _gnome_ui_get_datadir() + +#endif + +G_END_DECLS + +#endif /* LIBGNOMEUIP_H */ + diff --git a/gb.gtk/src/sm/sm.h b/gb.gtk/src/sm/sm.h new file mode 100644 index 00000000..7e9d3162 --- /dev/null +++ b/gb.gtk/src/sm/sm.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + sm.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SM_H +#define __SM_H + +#include "gnome-client.h" + +G_BEGIN_DECLS + +void session_manager_init(int *argc, char ***argv); +void session_manager_exit(void); +int session_manager_get_desktop(void); +void session_manager_set_desktop(int desktop); + +G_END_DECLS + +#endif diff --git a/gb.gtk/src/watcher.cpp b/gb.gtk/src/watcher.cpp new file mode 100644 index 00000000..6cf10efc --- /dev/null +++ b/gb.gtk/src/watcher.cpp @@ -0,0 +1,164 @@ +/*************************************************************************** + + watcher.cpp + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "main.h" +#include "gambas.h" +#include "watcher.h" + +//#define DEBUG_ME 1 + +static WATCH **watch = NULL; + +static gboolean watch_adaptor(GIOChannel *source, GIOCondition condition, gpointer param) +{ + WATCH *data = (WATCH *)param; + +#if DEBUG_ME + fprintf(stderr, "watch_adaptor: %p: condition = %d, data = %p\n", source, (int)condition, (void *)param); +#endif + + if (!data) return true; + + if (condition & G_IO_IN) + (*data->callback_read)(data->fd, GB_WATCH_READ, data->param_read); + else if (condition & G_IO_OUT) + (*data->callback_write)(data->fd, GB_WATCH_WRITE, data->param_write); + + return true; +} + +void CWatcher::init() +{ + GB.NewArray(POINTER(&watch), sizeof(WATCH *), 0); +} + +void CWatcher::exit() +{ + Clear(); + GB.FreeArray(POINTER(&watch)); +} + +void CWatcher::Clear() +{ + while (count()) + { + CWatcher::Add(watch[0]->fd, GB_WATCH_NONE, NULL, 0); + } +} + +void CWatcher::Remove(int fd) +{ + CWatcher::Add(fd, GB_WATCH_NONE, NULL, 0); +} + +void CWatcher::Add(int fd, int type, void *callback, intptr_t param) +{ + WATCH *data = NULL; + WATCH **pwatch; + int i; + + for (i = 0; i < count(); i++) + { + if (watch[i]->fd == fd) + { + data = watch[i]; + break; + } + } + + if (!data) + { + if (type == GB_WATCH_NONE || !callback) + return; + + pwatch = (WATCH **)GB.Add(&watch); + + GB.Alloc(POINTER(pwatch), sizeof(WATCH)); + data = *pwatch; + data->fd = fd; + data->channel_read = data->channel_write = 0; + data->callback_read = data->callback_write = 0; + } + + if (data->callback_read && (type == GB_WATCH_NONE || type == GB_WATCH_READ)) + { +#if DEBUG_ME + fprintf(stderr, "remove watch on fd %d for read (%p)\n", data->fd, data->channel_read); +#endif + g_source_remove(data->id_read); + g_io_channel_unref(data->channel_read); + data->callback_read = 0; + data->channel_read = 0; + } + + if (data->callback_write && (type == GB_WATCH_NONE || type == GB_WATCH_WRITE)) + { +#if DEBUG_ME + fprintf(stderr, "remove watch on fd %d for read (%p)\n", data->fd, data->channel_write); +#endif + g_source_remove(data->id_write); + g_io_channel_unref(data->channel_write); + data->callback_write = 0; + data->channel_write = 0; + } + + if (callback) + { + if (type == GB_WATCH_READ) + { + data->callback_read = (WATCH_CALLBACK)callback; + data->param_read = param; + data->channel_read = g_io_channel_unix_new(fd); + g_io_channel_set_encoding(data->channel_read, NULL, NULL); + g_io_channel_set_buffered(data->channel_read, FALSE); + data->id_read = g_io_add_watch_full(data->channel_read, G_PRIORITY_DEFAULT_IDLE, G_IO_IN, watch_adaptor, (void*)data, NULL); +#if DEBUG_ME + fprintf(stderr, "add watch on fd %d for read (%p)\n", fd, data->channel_read); +#endif + } + else if (type == GB_WATCH_WRITE) + { + data->callback_write = (WATCH_CALLBACK)callback; + data->param_write = param; + data->channel_write = g_io_channel_unix_new(fd); + g_io_channel_set_encoding(data->channel_write, NULL, NULL); + g_io_channel_set_buffered(data->channel_write, FALSE); + data->id_write = g_io_add_watch_full(data->channel_write, G_PRIORITY_DEFAULT_IDLE, G_IO_OUT, watch_adaptor, (void*)data, NULL); +#if DEBUG_ME + fprintf(stderr, "add watch on fd %d for write (%p)\n", fd, data->channel_write); +#endif + } + } + + if (!data->callback_read && !data->callback_write) + { + GB.Free(POINTER(&data)); + GB.Remove(&watch, i, 1); + MAIN_check_quit(); + } +} + +int CWatcher::count() +{ + return GB.Count(watch); +} diff --git a/gb.gtk/src/watcher.h b/gb.gtk/src/watcher.h new file mode 100644 index 00000000..8eb08f1e --- /dev/null +++ b/gb.gtk/src/watcher.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + watcher.h + + (c) 2004-2007 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __WATCHER_H +#define __WATCHER_H + +#include "gambas.h" +#include <gtk/gtk.h> + +typedef + GB_WATCH_CALLBACK WATCH_CALLBACK; + +typedef + struct { + int fd; + GIOChannel *channel_read; + guint id_read; + WATCH_CALLBACK callback_read; + intptr_t param_read; + GIOChannel *channel_write; + guint id_write; + WATCH_CALLBACK callback_write; + intptr_t param_write; + } + WATCH; + +class CWatcher +{ +public: + static void init(); + static void exit(); + static void Add(int fd, int type, void *callback, intptr_t param); + static void Clear(); + static void Remove(int fd); + static int count(); +}; + +#endif diff --git a/gb.gtk/src/widgets.h b/gb.gtk/src/widgets.h new file mode 100644 index 00000000..f19a5d3f --- /dev/null +++ b/gb.gtk/src/widgets.h @@ -0,0 +1,149 @@ +/*************************************************************************** + + widgets.h + + (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __WIDGETS_H +#define __WIDGETS_H + +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_26 + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include <gdk-pixbuf/gdk-pixbuf.h> + +#ifdef GTK3 +#include <gdk/gdkkeysyms-compat.h> +#else +#ifndef GAMBAS_DIRECTFB +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysymdef.h> +#include <X11/X.h> +#endif +#endif +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <inttypes.h> + +#include "gb_common.h" + +#include "gb.form.properties.h" +#include "gb.form.const.h" + +#include "gpicture.h" +#include "gcursor.h" +#include "gfont.h" +#include "gcontrol.h" +#include "gcontainer.h" +#include "gtools.h" + +#include "main.h" +//#define GTK_DEBUG_SIGNALS +//#define GTK_DEBUG_OBJECTS + +enum +{ + gEvent_MousePress, + gEvent_MouseRelease, + gEvent_MouseMove, + gEvent_MouseDrag, + gEvent_MouseWheel, + gEvent_MouseDblClick, + gEvent_MouseMenu, + gEvent_KeyPress, + gEvent_KeyRelease, + gEvent_FocusIn, + gEvent_FocusOut, + gEvent_Enter, + gEvent_Leave, + gEvent_DragMove, + gEvent_Drop +}; + +typedef + unsigned char uchar; + +#ifdef GTK3 + #define ON_DRAW_BEFORE(_widget, _this, _gtk, _gtk3) g_signal_connect(G_OBJECT(_widget), "draw", G_CALLBACK(_gtk3), (gpointer)_this) + #define ON_DRAW(_widget, _this, _gtk, _gtk3) g_signal_connect_after(G_OBJECT(_widget), "draw", G_CALLBACK(_gtk3), (gpointer)_this) +#else + #define ON_DRAW_BEFORE(_widget, _this, _gtk, _gtk3) g_signal_connect(G_OBJECT(_widget), "expose-event", G_CALLBACK(_gtk), (gpointer)_this) + #define ON_DRAW(_widget, _this, _gtk, _gtk3) g_signal_connect_after(G_OBJECT(_widget), "expose-event", G_CALLBACK(_gtk), (gpointer)_this) +#endif + +#ifdef GTK3 + +#define STATE_T GtkStateFlags +#define STYLE_T GtkStyleContext + +#define STATE_NORMAL GTK_STATE_FLAG_NORMAL +#define STATE_ACTIVE GTK_STATE_FLAG_ACTIVE +#define STATE_INSENSITIVE GTK_STATE_FLAG_INSENSITIVE +#define STATE_PRELIGHT GTK_STATE_FLAG_PRELIGHT +#define STATE_SELECTED GTK_STATE_FLAG_SELECTED +#define STATE_FOCUSED GTK_STATE_FLAG_FOCUSED +#define STATE_LINK GTK_STATE_FLAG_LINK +#define STATE_VISITED GTK_STATE_FLAG_VISITED + +#if GTK_CHECK_VERSION(3, 14, 0) +#else +#define GTK_STATE_FLAG_CHECKED GTK_STATE_FLAG_ACTIVE +#endif + + +#define gtk_hbox_new(_homogeneous, _spacing) gtk_box_new(GTK_ORIENTATION_HORIZONTAL, _spacing) +#define gtk_vbox_new(_homogeneous, _spacing) gtk_box_new(GTK_ORIENTATION_VERTICAL, _spacing) + +#define gtk_hscale_new(_adj) gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, _adj) +#define gtk_vscale_new(_adj) gtk_scale_new(GTK_ORIENTATION_VERTICAL, _adj) + +#define gtk_hscrollbar_new(_adj) gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, _adj) +#define gtk_vscrollbar_new(_adj) gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, _adj) + +#define gtk_modify_font gtk_override_font + +#else + +#define STATE_T GtkStateType +#define STYLE_T GtkStyle + +#define STATE_NORMAL GTK_STATE_NORMAL +#define STATE_ACTIVE GTK_STATE_ACTIVE +#define STATE_INSENSITIVE GTK_STATE_INSENSITIVE +#define STATE_PRELIGHT GTK_STATE_PRELIGHT +#define STATE_SELECTED GTK_STATE_SELECTED +#define STATE_LINK GTK_STATE_NORMAL +#define STATE_VISITED GTK_STATE_NORMAL + +#endif + +#endif diff --git a/gb.gtk/src/x11.c b/gb.gtk/src/x11.c new file mode 120000 index 00000000..944e55f3 --- /dev/null +++ b/gb.gtk/src/x11.c @@ -0,0 +1 @@ +../../gb.qt4/src/x11.c \ No newline at end of file diff --git a/gb.gtk/src/x11.h b/gb.gtk/src/x11.h new file mode 120000 index 00000000..479f5274 --- /dev/null +++ b/gb.gtk/src/x11.h @@ -0,0 +1 @@ +../../gb.qt4/src/x11.h \ No newline at end of file diff --git a/gb.gtk3/AUTHORS b/gb.gtk3/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/COPYING b/gb.gtk3/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gtk3/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gtk3/ChangeLog b/gb.gtk3/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/INSTALL b/gb.gtk3/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gtk3/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gtk3/Makefile.am b/gb.gtk3/Makefile.am new file mode 100644 index 00000000..533d6473 --- /dev/null +++ b/gb.gtk3/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GTK3_DIR@ +EXTRA_DIST = reconf spec share gb*.h gambas.h diff --git a/gb.gtk3/NEWS b/gb.gtk3/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/README b/gb.gtk3/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/TODO b/gb.gtk3/TODO new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/acinclude.m4 b/gb.gtk3/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gtk3/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gtk3/component.am b/gb.gtk3/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gtk3/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gtk3/configure.ac b/gb.gtk3/configure.ac new file mode 100644 index 00000000..5a4c7705 --- /dev/null +++ b/gb.gtk3/configure.ac @@ -0,0 +1,52 @@ +dnl ---- configure.ac for gb.gtk3 component + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-gtk3],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gtk3) +LT_INIT + +GB_CHECK_XWINDOW() + +GB_COMPONENT_PKG_CONFIG( + gtk3, GTK3, gb.gtk3, [src], + 'gtk+-3.0 >= 3.4' 'librsvg-2.0 >= 2.14.3' 'cairo >= 1.6.0' 'cairo-ft >= 1.6.0' 'gtk+-unix-print-3.0 >= 3.4' sm ice +) + +GB_COMPONENT_PKG_CONFIG( + gtk3x11, GTK3X11, gb.gtk3.x11, [x11], + 'gtk+-3.0 >= 3.4' gdk-x11-3.0 +) + +GB_COMPONENT_PKG_CONFIG( + gtk3wayland, GTK3WAYLAND, gb.gtk3.wayland, [wayland], + 'gtk+-3.0 >= 3.4' gdk-wayland-3.0 +) + +GB_COMPONENT_PKG_CONFIG( + gtk3webview, GTK3WEBVIEW, gb.gtk3.webview, [webview], + 'gtk+-3.0 >= 3.4' 'webkit2gtk-4.0 >= 2.20' +) + +GB_COMPONENT_PKG_CONFIG_AGAIN( + gtk3webview, GTK3WEBVIEW, gb.gtk3.webview, [webview], + 'gtk+-3.0 >= 3.4' 'webkit2gtk-4.1 >= 2.20', + [Try again with webkit2gtk-4.1]) + +GB_COMPONENT_PKG_CONFIG( + gtk3opengl, GTK3OPENGL, gb.gtk3.opengl, [opengl], + 'gtk+-3.0 >= 3.16' gl +) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +src/x11/Makefile \ +src/wayland/Makefile \ +src/webview/Makefile \ +src/opengl/Makefile +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.gtk3/gambas.h b/gb.gtk3/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gtk3/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gtk3/gb.draw.h b/gb.gtk3/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.gtk3/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.gtk3/gb.geom.h b/gb.gtk3/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.gtk3/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.gtk3/gb.gl.h b/gb.gtk3/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.gtk3/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.gtk3/gb.image.h b/gb.gtk3/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.gtk3/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.gtk3/gb.paint.h b/gb.gtk3/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.gtk3/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.gtk3/gb_common.h b/gb.gtk3/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gtk3/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gtk3/m4 b/gb.gtk3/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gtk3/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gtk3/reconf b/gb.gtk3/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gtk3/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gtk3/share b/gb.gtk3/share new file mode 120000 index 00000000..638490a4 --- /dev/null +++ b/gb.gtk3/share @@ -0,0 +1 @@ +../gb.qt4/share \ No newline at end of file diff --git a/gb.gtk3/src/CButton.cpp b/gb.gtk3/src/CButton.cpp new file mode 120000 index 00000000..b206d256 --- /dev/null +++ b/gb.gtk3/src/CButton.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CButton.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CButton.h b/gb.gtk3/src/CButton.h new file mode 120000 index 00000000..6a9cc03e --- /dev/null +++ b/gb.gtk3/src/CButton.h @@ -0,0 +1 @@ +../../gb.gtk/src/CButton.h \ No newline at end of file diff --git a/gb.gtk3/src/CClipboard.cpp b/gb.gtk3/src/CClipboard.cpp new file mode 120000 index 00000000..6d673158 --- /dev/null +++ b/gb.gtk3/src/CClipboard.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CClipboard.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CClipboard.h b/gb.gtk3/src/CClipboard.h new file mode 120000 index 00000000..f2d94a27 --- /dev/null +++ b/gb.gtk3/src/CClipboard.h @@ -0,0 +1 @@ +../../gb.gtk/src/CClipboard.h \ No newline at end of file diff --git a/gb.gtk3/src/CColor.cpp b/gb.gtk3/src/CColor.cpp new file mode 120000 index 00000000..efb6578f --- /dev/null +++ b/gb.gtk3/src/CColor.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CColor.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CColor.h b/gb.gtk3/src/CColor.h new file mode 120000 index 00000000..6eabe04b --- /dev/null +++ b/gb.gtk3/src/CColor.h @@ -0,0 +1 @@ +../../gb.gtk/src/CColor.h \ No newline at end of file diff --git a/gb.gtk3/src/CConst.cpp b/gb.gtk3/src/CConst.cpp new file mode 120000 index 00000000..2f9cf1cd --- /dev/null +++ b/gb.gtk3/src/CConst.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CConst.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CConst.h b/gb.gtk3/src/CConst.h new file mode 120000 index 00000000..797d0bec --- /dev/null +++ b/gb.gtk3/src/CConst.h @@ -0,0 +1 @@ +../../gb.gtk/src/CConst.h \ No newline at end of file diff --git a/gb.gtk3/src/CContainer.cpp b/gb.gtk3/src/CContainer.cpp new file mode 120000 index 00000000..ee69dfdd --- /dev/null +++ b/gb.gtk3/src/CContainer.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CContainer.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CContainer.h b/gb.gtk3/src/CContainer.h new file mode 120000 index 00000000..db213757 --- /dev/null +++ b/gb.gtk3/src/CContainer.h @@ -0,0 +1 @@ +../../gb.gtk/src/CContainer.h \ No newline at end of file diff --git a/gb.gtk3/src/CDialog.cpp b/gb.gtk3/src/CDialog.cpp new file mode 120000 index 00000000..14d97758 --- /dev/null +++ b/gb.gtk3/src/CDialog.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CDialog.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CDialog.h b/gb.gtk3/src/CDialog.h new file mode 120000 index 00000000..f63dd4ba --- /dev/null +++ b/gb.gtk3/src/CDialog.h @@ -0,0 +1 @@ +../../gb.gtk/src/CDialog.h \ No newline at end of file diff --git a/gb.gtk3/src/CDraw.cpp b/gb.gtk3/src/CDraw.cpp new file mode 120000 index 00000000..e31bbfb6 --- /dev/null +++ b/gb.gtk3/src/CDraw.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CDraw.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CDraw.h b/gb.gtk3/src/CDraw.h new file mode 120000 index 00000000..b3dade9d --- /dev/null +++ b/gb.gtk3/src/CDraw.h @@ -0,0 +1 @@ +../../gb.gtk/src/CDraw.h \ No newline at end of file diff --git a/gb.gtk3/src/CDrawingArea.cpp b/gb.gtk3/src/CDrawingArea.cpp new file mode 120000 index 00000000..b354fe6f --- /dev/null +++ b/gb.gtk3/src/CDrawingArea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CDrawingArea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CDrawingArea.h b/gb.gtk3/src/CDrawingArea.h new file mode 120000 index 00000000..04c3d7b4 --- /dev/null +++ b/gb.gtk3/src/CDrawingArea.h @@ -0,0 +1 @@ +../../gb.gtk/src/CDrawingArea.h \ No newline at end of file diff --git a/gb.gtk3/src/CFont.cpp b/gb.gtk3/src/CFont.cpp new file mode 120000 index 00000000..36deecbc --- /dev/null +++ b/gb.gtk3/src/CFont.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CFont.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CFont.h b/gb.gtk3/src/CFont.h new file mode 120000 index 00000000..bb6359de --- /dev/null +++ b/gb.gtk3/src/CFont.h @@ -0,0 +1 @@ +../../gb.gtk/src/CFont.h \ No newline at end of file diff --git a/gb.gtk3/src/CImage.cpp b/gb.gtk3/src/CImage.cpp new file mode 120000 index 00000000..7ba515a4 --- /dev/null +++ b/gb.gtk3/src/CImage.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CImage.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CImage.h b/gb.gtk3/src/CImage.h new file mode 120000 index 00000000..890fb998 --- /dev/null +++ b/gb.gtk3/src/CImage.h @@ -0,0 +1 @@ +../../gb.gtk/src/CImage.h \ No newline at end of file diff --git a/gb.gtk3/src/CKey.cpp b/gb.gtk3/src/CKey.cpp new file mode 120000 index 00000000..abbe6451 --- /dev/null +++ b/gb.gtk3/src/CKey.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CKey.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CKey.h b/gb.gtk3/src/CKey.h new file mode 120000 index 00000000..492bec84 --- /dev/null +++ b/gb.gtk3/src/CKey.h @@ -0,0 +1 @@ +../../gb.gtk/src/CKey.h \ No newline at end of file diff --git a/gb.gtk3/src/CMenu.cpp b/gb.gtk3/src/CMenu.cpp new file mode 120000 index 00000000..0c697bb8 --- /dev/null +++ b/gb.gtk3/src/CMenu.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CMenu.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CMenu.h b/gb.gtk3/src/CMenu.h new file mode 120000 index 00000000..4e66e412 --- /dev/null +++ b/gb.gtk3/src/CMenu.h @@ -0,0 +1 @@ +../../gb.gtk/src/CMenu.h \ No newline at end of file diff --git a/gb.gtk3/src/CMouse.cpp b/gb.gtk3/src/CMouse.cpp new file mode 120000 index 00000000..6ac84fa9 --- /dev/null +++ b/gb.gtk3/src/CMouse.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CMouse.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CMouse.h b/gb.gtk3/src/CMouse.h new file mode 120000 index 00000000..131b313c --- /dev/null +++ b/gb.gtk3/src/CMouse.h @@ -0,0 +1 @@ +../../gb.gtk/src/CMouse.h \ No newline at end of file diff --git a/gb.gtk3/src/CPanel.cpp b/gb.gtk3/src/CPanel.cpp new file mode 120000 index 00000000..3efd8311 --- /dev/null +++ b/gb.gtk3/src/CPanel.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CPanel.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CPanel.h b/gb.gtk3/src/CPanel.h new file mode 120000 index 00000000..3c776d9d --- /dev/null +++ b/gb.gtk3/src/CPanel.h @@ -0,0 +1 @@ +../../gb.gtk/src/CPanel.h \ No newline at end of file diff --git a/gb.gtk3/src/CPicture.cpp b/gb.gtk3/src/CPicture.cpp new file mode 120000 index 00000000..39a951ac --- /dev/null +++ b/gb.gtk3/src/CPicture.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CPicture.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CPicture.h b/gb.gtk3/src/CPicture.h new file mode 120000 index 00000000..ff13a6c2 --- /dev/null +++ b/gb.gtk3/src/CPicture.h @@ -0,0 +1 @@ +../../gb.gtk/src/CPicture.h \ No newline at end of file diff --git a/gb.gtk3/src/CScreen.cpp b/gb.gtk3/src/CScreen.cpp new file mode 120000 index 00000000..1efce7fd --- /dev/null +++ b/gb.gtk3/src/CScreen.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CScreen.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CScreen.h b/gb.gtk3/src/CScreen.h new file mode 120000 index 00000000..914350a8 --- /dev/null +++ b/gb.gtk3/src/CScreen.h @@ -0,0 +1 @@ +../../gb.gtk/src/CScreen.h \ No newline at end of file diff --git a/gb.gtk3/src/CSlider.cpp b/gb.gtk3/src/CSlider.cpp new file mode 120000 index 00000000..4e35d1d7 --- /dev/null +++ b/gb.gtk3/src/CSlider.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CSlider.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CSlider.h b/gb.gtk3/src/CSlider.h new file mode 120000 index 00000000..2e44081e --- /dev/null +++ b/gb.gtk3/src/CSlider.h @@ -0,0 +1 @@ +../../gb.gtk/src/CSlider.h \ No newline at end of file diff --git a/gb.gtk3/src/CStyle.cpp b/gb.gtk3/src/CStyle.cpp new file mode 120000 index 00000000..7066f397 --- /dev/null +++ b/gb.gtk3/src/CStyle.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CStyle.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CStyle.h b/gb.gtk3/src/CStyle.h new file mode 120000 index 00000000..9b3c5c1b --- /dev/null +++ b/gb.gtk3/src/CStyle.h @@ -0,0 +1 @@ +../../gb.gtk/src/CStyle.h \ No newline at end of file diff --git a/gb.gtk3/src/CTabStrip.cpp b/gb.gtk3/src/CTabStrip.cpp new file mode 120000 index 00000000..c841c6d4 --- /dev/null +++ b/gb.gtk3/src/CTabStrip.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTabStrip.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTabStrip.h b/gb.gtk3/src/CTabStrip.h new file mode 120000 index 00000000..309e53e3 --- /dev/null +++ b/gb.gtk3/src/CTabStrip.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTabStrip.h \ No newline at end of file diff --git a/gb.gtk3/src/CTextArea.cpp b/gb.gtk3/src/CTextArea.cpp new file mode 120000 index 00000000..c0c19b03 --- /dev/null +++ b/gb.gtk3/src/CTextArea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTextArea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTextArea.h b/gb.gtk3/src/CTextArea.h new file mode 120000 index 00000000..6fa7b022 --- /dev/null +++ b/gb.gtk3/src/CTextArea.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTextArea.h \ No newline at end of file diff --git a/gb.gtk3/src/CTextBox.cpp b/gb.gtk3/src/CTextBox.cpp new file mode 120000 index 00000000..a0243b8c --- /dev/null +++ b/gb.gtk3/src/CTextBox.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTextBox.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTextBox.h b/gb.gtk3/src/CTextBox.h new file mode 120000 index 00000000..7f2661ed --- /dev/null +++ b/gb.gtk3/src/CTextBox.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTextBox.h \ No newline at end of file diff --git a/gb.gtk3/src/CTrayIcon.cpp b/gb.gtk3/src/CTrayIcon.cpp new file mode 120000 index 00000000..1a52f108 --- /dev/null +++ b/gb.gtk3/src/CTrayIcon.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTrayIcon.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTrayIcon.h b/gb.gtk3/src/CTrayIcon.h new file mode 120000 index 00000000..a09627d4 --- /dev/null +++ b/gb.gtk3/src/CTrayIcon.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTrayIcon.h \ No newline at end of file diff --git a/gb.gtk3/src/CWatcher.cpp b/gb.gtk3/src/CWatcher.cpp new file mode 120000 index 00000000..3b2d66c0 --- /dev/null +++ b/gb.gtk3/src/CWatcher.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CWatcher.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CWatcher.h b/gb.gtk3/src/CWatcher.h new file mode 120000 index 00000000..677919ef --- /dev/null +++ b/gb.gtk3/src/CWatcher.h @@ -0,0 +1 @@ +../../gb.gtk/src/CWatcher.h \ No newline at end of file diff --git a/gb.gtk3/src/CWidget.cpp b/gb.gtk3/src/CWidget.cpp new file mode 120000 index 00000000..5c61624d --- /dev/null +++ b/gb.gtk3/src/CWidget.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CWidget.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CWidget.h b/gb.gtk3/src/CWidget.h new file mode 120000 index 00000000..94f1cfa6 --- /dev/null +++ b/gb.gtk3/src/CWidget.h @@ -0,0 +1 @@ +../../gb.gtk/src/CWidget.h \ No newline at end of file diff --git a/gb.gtk3/src/CWindow.cpp b/gb.gtk3/src/CWindow.cpp new file mode 120000 index 00000000..a7a11bbd --- /dev/null +++ b/gb.gtk3/src/CWindow.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CWindow.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CWindow.h b/gb.gtk3/src/CWindow.h new file mode 120000 index 00000000..b47fa6cb --- /dev/null +++ b/gb.gtk3/src/CWindow.h @@ -0,0 +1 @@ +../../gb.gtk/src/CWindow.h \ No newline at end of file diff --git a/gb.gtk3/src/Makefile.am b/gb.gtk3/src/Makefile.am new file mode 100644 index 00000000..adad355a --- /dev/null +++ b/gb.gtk3/src/Makefile.am @@ -0,0 +1,75 @@ +COMPONENT = gb.gtk3 +include $(top_srcdir)/component.am + +SUBDIRS = . @GTK3X11_DIR@ @GTK3WAYLAND_DIR@ @GTK3WEBVIEW_DIR@ @GTK3OPENGL_DIR@ + +gblib_LTLIBRARIES = gb.gtk3.la + +gb_gtk3_la_LIBADD = @GTK3_LIB@ +gb_gtk3_la_LDFLAGS = -module @LD_FLAGS@ @GTK3_LDFLAGS@ +gb_gtk3_la_CPPFLAGS = @GTK3_INC@ -I$(top_srcdir)/share -DGTK3 -DGTK_DISABLE_SINGLE_INCLUDES -DGSEAL_ENABLE + +gb_gtk3_la_SOURCES = \ + gtag.h ggambastag.h gshare.h \ + gb.gtk.h gb.gtk.patch.h widgets.h font-parser.h font-parser.cpp \ + gtools.cpp gcolor.h \ + gfont.cpp gpicture.cpp \ + gdesktop.cpp gdialog.cpp \ + gcontrol.cpp gcontainer.cpp \ + gbutton.cpp gslider.cpp gsignals.cpp \ + gtextbox.cpp gtextarea.cpp \ + gpanel.cpp gtabstrip.cpp \ + gmenu.cpp gtrayicon.cpp gmainwindow.cpp \ + gtree.h gtree.cpp \ + watcher.h watcher.cpp \ + CConst.h CConst.cpp CColor.h CColor.cpp \ + CFont.h CFont.cpp \ + CKey.h CKey.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CDialog.h CDialog.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CImage.h CImage.cpp \ + CPicture.h CPicture.cpp \ + canimation.h canimation.cpp \ + CClipboard.h CClipboard.cpp \ + CMouse.h CMouse.cpp \ + CWatcher.h CWatcher.cpp \ + CWidget.h CWidget.cpp CContainer.h CContainer.cpp \ + CDrawingArea.h CDrawingArea.cpp \ + CSlider.h CSlider.cpp \ + CButton.h CButton.cpp \ + CTextBox.h CTextBox.cpp \ + CTextArea.h CTextArea.cpp \ + CTabStrip.h CTabStrip.cpp \ + CPanel.h CPanel.cpp \ + CMenu.h CMenu.cpp CTrayIcon.h CTrayIcon.cpp CWindow.h CWindow.cpp \ + cprinter.h cprinter.cpp \ + csvgimage.h csvgimage.cpp \ + main.h main.cpp \ + gkey.h gkey.cpp \ + gcursor.h gcursor.cpp \ + gmouse.h gmouse.cpp \ + gdesktop.h \ + gpicture.h \ + gfont.h \ + gdialog.h \ + gcontrol.h \ + gseparator.h \ + gtrayicon.h \ + gbutton.h \ + gtextbox.h \ + gtextarea.h \ + gslider.h \ + gscrollbar.h \ + gcontainer.h \ + gdrawingarea.h gdrawingarea.cpp \ + gpanel.h gtabstrip.h \ + gmenu.h \ + gmainwindow.h \ + gapplication.h gapplication.cpp \ + gclipboard.h \ + gdrag.h gdrag.cpp \ + gtools.h kentities.h \ + gprinter.h gprinter.cpp diff --git a/gb.gtk3/src/canimation.cpp b/gb.gtk3/src/canimation.cpp new file mode 120000 index 00000000..04f6268d --- /dev/null +++ b/gb.gtk3/src/canimation.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/canimation.cpp \ No newline at end of file diff --git a/gb.gtk3/src/canimation.h b/gb.gtk3/src/canimation.h new file mode 120000 index 00000000..d2f02a68 --- /dev/null +++ b/gb.gtk3/src/canimation.h @@ -0,0 +1 @@ +../../gb.gtk/src/canimation.h \ No newline at end of file diff --git a/gb.gtk3/src/cpaint_impl.cpp b/gb.gtk3/src/cpaint_impl.cpp new file mode 120000 index 00000000..3a30576c --- /dev/null +++ b/gb.gtk3/src/cpaint_impl.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/cpaint_impl.cpp \ No newline at end of file diff --git a/gb.gtk3/src/cpaint_impl.h b/gb.gtk3/src/cpaint_impl.h new file mode 120000 index 00000000..7bce6656 --- /dev/null +++ b/gb.gtk3/src/cpaint_impl.h @@ -0,0 +1 @@ +../../gb.gtk/src/cpaint_impl.h \ No newline at end of file diff --git a/gb.gtk3/src/cprinter.cpp b/gb.gtk3/src/cprinter.cpp new file mode 120000 index 00000000..a0b7e15e --- /dev/null +++ b/gb.gtk3/src/cprinter.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/cprinter.cpp \ No newline at end of file diff --git a/gb.gtk3/src/cprinter.h b/gb.gtk3/src/cprinter.h new file mode 120000 index 00000000..bf4ec774 --- /dev/null +++ b/gb.gtk3/src/cprinter.h @@ -0,0 +1 @@ +../../gb.gtk/src/cprinter.h \ No newline at end of file diff --git a/gb.gtk3/src/csvgimage.cpp b/gb.gtk3/src/csvgimage.cpp new file mode 120000 index 00000000..ff4c044c --- /dev/null +++ b/gb.gtk3/src/csvgimage.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/csvgimage.cpp \ No newline at end of file diff --git a/gb.gtk3/src/csvgimage.h b/gb.gtk3/src/csvgimage.h new file mode 120000 index 00000000..f4e94823 --- /dev/null +++ b/gb.gtk3/src/csvgimage.h @@ -0,0 +1 @@ +../../gb.gtk/src/csvgimage.h \ No newline at end of file diff --git a/gb.gtk3/src/font-parser.cpp b/gb.gtk3/src/font-parser.cpp new file mode 120000 index 00000000..8cb01289 --- /dev/null +++ b/gb.gtk3/src/font-parser.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/font-parser.cpp \ No newline at end of file diff --git a/gb.gtk3/src/font-parser.h b/gb.gtk3/src/font-parser.h new file mode 120000 index 00000000..716f4340 --- /dev/null +++ b/gb.gtk3/src/font-parser.h @@ -0,0 +1 @@ +../../gb.gtk/src/font-parser.h \ No newline at end of file diff --git a/gb.gtk3/src/gapplication.cpp b/gb.gtk3/src/gapplication.cpp new file mode 120000 index 00000000..da5ab25a --- /dev/null +++ b/gb.gtk3/src/gapplication.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gapplication.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gapplication.h b/gb.gtk3/src/gapplication.h new file mode 120000 index 00000000..cd414365 --- /dev/null +++ b/gb.gtk3/src/gapplication.h @@ -0,0 +1 @@ +../../gb.gtk/src/gapplication.h \ No newline at end of file diff --git a/gb.gtk3/src/gb.gtk.h b/gb.gtk3/src/gb.gtk.h new file mode 120000 index 00000000..d19b9b97 --- /dev/null +++ b/gb.gtk3/src/gb.gtk.h @@ -0,0 +1 @@ +../../gb.gtk/src/gb.gtk.h \ No newline at end of file diff --git a/gb.gtk3/src/gb.gtk.patch.h b/gb.gtk3/src/gb.gtk.patch.h new file mode 100644 index 00000000..9437f6c2 --- /dev/null +++ b/gb.gtk3/src/gb.gtk.patch.h @@ -0,0 +1,211 @@ +/*************************************************************************** + + gb.gtk.patch.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef GB_GTK_PATCH_H +#define GB_GTK_PATCH_H + +typedef + struct { + void (*get_preferred_height)(GtkWidget *widget, gint *minimum_height, gint *natural_height); + void (*get_preferred_width_for_height)(GtkWidget *widget, gint height, gint *minimum_width, gint *natural_width); + void (*get_preferred_width)(GtkWidget *widget, gint *minimum_width, gint *natural_width); + void (*get_preferred_height_for_width)(GtkWidget *widget, gint width, gint *minimum_height, gint *natural_height); +#if GTK_CHECK_VERSION(3, 10, 0) + void (*get_preferred_height_and_baseline_for_width)(GtkWidget *widget, gint width, gint *minimum, gint *natural, gint *minimum_baseline, gint *natural_baseline); + void (*size_allocate)(GtkWidget *widget, GtkAllocation *allocation); +#endif + } + PATCH_FUNCS; + +#define PATCH_OLD_FUNC ((PATCH_FUNCS *)(klass->_gtk_reserved6)) + +#define PATCH_DECLARE_COMMON(_type, _name) \ +static void _name##get_preferred_width(GtkWidget *widget, gint *minimum_size, gint *natural_size) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass*)g_type_class_peek(_type); \ + (*PATCH_OLD_FUNC->get_preferred_width)(widget, minimum_size, natural_size); \ + if (minimum_size && must_patch(widget)) \ + *minimum_size = 0; \ +} \ +static void _name##get_preferred_height(GtkWidget *widget, gint *minimum_size, gint *natural_size) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*PATCH_OLD_FUNC->get_preferred_height)(widget, minimum_size, natural_size); \ + if (minimum_size && must_patch(widget)) \ + *minimum_size = 0; \ +} \ +static void _name##get_preferred_height_for_width(GtkWidget *widget, gint width, gint *minimum_size, gint *natural_size) \ +{ \ + if (minimum_size && must_patch(widget)) \ + { \ + *minimum_size = 0; \ + *natural_size = 0; \ + return; \ + } \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*PATCH_OLD_FUNC->get_preferred_height_for_width)(widget, width, minimum_size, natural_size); \ +} \ +static void _name##get_preferred_width_for_height(GtkWidget *widget, gint height, gint *minimum_size, gint *natural_size) \ +{ \ + if (minimum_size && must_patch(widget)) \ + { \ + *minimum_size = 0; \ + *natural_size = 0; \ + return; \ + } \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*PATCH_OLD_FUNC->get_preferred_height_for_width)(widget, height, minimum_size, natural_size); \ +} + +#if GTK_CHECK_VERSION(3, 14, 0) + +#define PATCH_DECLARE_SIZE(_type, _name) \ +static void _name##size_allocate(GtkWidget *widget, GtkAllocation *allocation) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*PATCH_OLD_FUNC->size_allocate)(widget, allocation); \ + gtk_widget_set_clip(widget, allocation); \ +} + +#elif GTK_CHECK_VERSION(3, 10, 0) + +#define PATCH_DECLARE_SIZE(_type, _name) \ +static void _name##size_allocate(GtkWidget *widget, GtkAllocation *allocation) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*PATCH_OLD_FUNC->size_allocate)(widget, allocation); \ +} + +#else + +#define PATCH_DECLARE_SIZE(_type, _name) + +#endif + +#define PATCH_DECLARE(type) PATCH_DECLARE_COMMON(type, type##_) PATCH_DECLARE_SIZE(type, type##_) + +#define PATCH_DECLARE_BASELINE(type) \ +static void type##_get_preferred_height_and_baseline_for_width(GtkWidget *widget, gint width, gint *minimum, gint *natural, gint *minimum_baseline, gint *natural_baseline) \ +{ \ + if (minimum && minimum_baseline && must_patch(widget)) \ + { \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(type); \ + if (PATCH_OLD_FUNC->get_preferred_height_and_baseline_for_width) \ + (*PATCH_OLD_FUNC->get_preferred_height_and_baseline_for_width)(widget, width, minimum, natural, minimum_baseline, natural_baseline); \ + else \ + { \ + *minimum_baseline = 0; \ + *natural_baseline = 0; \ + } \ + *minimum = 0; \ + *natural = 0; \ + return; \ + } \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(type); \ + if (PATCH_OLD_FUNC->get_preferred_height_and_baseline_for_width) \ + (*PATCH_OLD_FUNC->get_preferred_height_and_baseline_for_width)(widget, width, minimum, natural, minimum_baseline, natural_baseline); \ +} + +//fprintf(stderr, "patching [%p %s] (%p %p)\n", klass, G_OBJECT_TYPE_NAME(widget), klass->get_preferred_width, klass->get_preferred_height); +// fprintf(stderr, "PATCH_CLASS: %s\n", G_OBJECT_TYPE_NAME(widget)); + +#if GTK_CHECK_VERSION(3,10,0) + +#define PATCH_CLASS(widget, type) \ +if (G_OBJECT_TYPE(widget) == type) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)GTK_WIDGET_GET_CLASS(widget); \ + if (klass->get_preferred_width != type##_get_preferred_width) \ + { \ + PATCH_FUNCS *funcs = g_new0(PATCH_FUNCS, 1); \ + funcs->get_preferred_width = klass->get_preferred_width; \ + funcs->get_preferred_height = klass->get_preferred_height; \ + funcs->get_preferred_height_for_width = klass->get_preferred_height_for_width; \ + funcs->get_preferred_width_for_height = klass->get_preferred_width_for_height; \ + funcs->size_allocate = klass->size_allocate; \ + klass->_gtk_reserved6 = (void(*)())funcs; \ + klass->get_preferred_width = type##_get_preferred_width; \ + klass->get_preferred_height = type##_get_preferred_height; \ + klass->get_preferred_height_for_width = type##_get_preferred_height_for_width; \ + klass->get_preferred_width_for_height = type##_get_preferred_width_for_height; \ + klass->size_allocate = type##_size_allocate; \ + } \ +} + +#define PATCH_CLASS_BASELINE(widget, type) \ +if (G_OBJECT_TYPE(widget) == type) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)GTK_WIDGET_GET_CLASS(widget); \ + if (klass->get_preferred_width != type##_get_preferred_width) \ + { \ + PATCH_FUNCS *funcs = g_new0(PATCH_FUNCS, 1); \ + funcs->get_preferred_width = klass->get_preferred_width; \ + funcs->get_preferred_height = klass->get_preferred_height; \ + funcs->get_preferred_height_for_width = klass->get_preferred_height_for_width; \ + funcs->get_preferred_width_for_height = klass->get_preferred_width_for_height; \ + funcs->size_allocate = klass->size_allocate; \ + funcs->get_preferred_height_and_baseline_for_width = klass->get_preferred_height_and_baseline_for_width; \ + klass->_gtk_reserved6 = (void(*)())funcs; \ + klass->get_preferred_width = type##_get_preferred_width; \ + klass->get_preferred_height = type##_get_preferred_height; \ + klass->get_preferred_height_for_width = type##_get_preferred_height_for_width; \ + klass->get_preferred_width_for_height = type##_get_preferred_width_for_height; \ + klass->size_allocate = type##_size_allocate; \ + if (klass->get_preferred_height_and_baseline_for_width) \ + klass->get_preferred_height_and_baseline_for_width = type##_get_preferred_height_and_baseline_for_width; \ + } \ +} + +#else + +#define PATCH_CLASS(widget, type) \ +if (G_OBJECT_TYPE(widget) == type) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)GTK_WIDGET_GET_CLASS(widget); \ + if (klass->get_preferred_width != type##_get_preferred_width) \ + { \ + PATCH_FUNCS *funcs = g_new0(PATCH_FUNCS, 1); \ + funcs->get_preferred_width = klass->get_preferred_width; \ + funcs->get_preferred_height = klass->get_preferred_height; \ + funcs->get_preferred_height_for_width = klass->get_preferred_height_for_width; \ + funcs->get_preferred_width_for_height = klass->get_preferred_width_for_height; \ + klass->_gtk_reserved6 = (void(*)())funcs; \ + klass->get_preferred_width = type##_get_preferred_width; \ + klass->get_preferred_height = type##_get_preferred_height; \ + klass->get_preferred_height_for_width = type##_get_preferred_height_for_width; \ + klass->get_preferred_width_for_height = type##_get_preferred_width_for_height; \ + } \ +} + +#define PATCH_CLASS_BASELINE PATCH_CLASS + +#endif + +#define PATCH_CLASS_FIXED(widget, type) \ +if (G_OBJECT_TYPE(widget) == type) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)GTK_WIDGET_GET_CLASS(widget); \ + klass->draw = draw_container; \ +} + +#endif diff --git a/gb.gtk3/src/gb.gtk.platform.h b/gb.gtk3/src/gb.gtk.platform.h new file mode 100644 index 00000000..20a14cc5 --- /dev/null +++ b/gb.gtk3/src/gb.gtk.platform.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + gb.gtk.platform.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_GTK_PLATFORM_H +#define __GB_GTK_PLATFORM_H + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +// Gambas GTK+ platform component interface + +#define GTK_PLATFORM_INTERFACE_VERSION 1 + +typedef + struct { + intptr_t version; + + void (*Init)(void); + void (*Exit)(void); + + GtkWidget *(*CreatePlug)(long wid); + + struct { + long (*GetId)(GdkWindow *); + } Window; + + struct { + bool (*HasSystemTray)(); + void (*ShowTrayIcon)(GtkStatusIcon *icon, int width, int height); + } Desktop; + + /*void (*ReleaseGrab)(void); + void (*UnreleaseGrab)(void); + int (*GetLastKeyCode)(void); + + struct { + int (*GetResolutionX)(void); + int (*GetResolutionY)(void); + void (*Screenshot)(QPixmap *pixmap, int x, int y, int w, int h); + } Desktop; + + struct { + int (*GetVirtualDesktop)(QWidget *window); + void (*SetVirtualDesktop)(QWidget *window, bool visible, int desktop); + void (*Remap)(QWidget *window); + void (*SetProperties)(QWidget *window, int which, QT_WINDOW_PROP *prop); + void (*SetUserTime)(QWidget *window, int timestamp); + void (*SetTransientFor)(QWidget *window, QWidget *parent); + + } Window;*/ + } + GTK_PLATFORM_INTERFACE; + +#endif diff --git a/gb.gtk3/src/gb.gtk3.component b/gb.gtk3/src/gb.gtk3.component new file mode 100644 index 00000000..02ef83b5 --- /dev/null +++ b/gb.gtk3/src/gb.gtk3.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.gtk3 +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form +State=NotFinished diff --git a/gb.gtk3/src/gbutton.cpp b/gb.gtk3/src/gbutton.cpp new file mode 120000 index 00000000..732700e5 --- /dev/null +++ b/gb.gtk3/src/gbutton.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gbutton.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gbutton.h b/gb.gtk3/src/gbutton.h new file mode 120000 index 00000000..0693b18f --- /dev/null +++ b/gb.gtk3/src/gbutton.h @@ -0,0 +1 @@ +../../gb.gtk/src/gbutton.h \ No newline at end of file diff --git a/gb.gtk3/src/gclipboard.h b/gb.gtk3/src/gclipboard.h new file mode 120000 index 00000000..f4034bb6 --- /dev/null +++ b/gb.gtk3/src/gclipboard.h @@ -0,0 +1 @@ +../../gb.gtk/src/gclipboard.h \ No newline at end of file diff --git a/gb.gtk3/src/gcolor.h b/gb.gtk3/src/gcolor.h new file mode 120000 index 00000000..93b7741c --- /dev/null +++ b/gb.gtk3/src/gcolor.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcolor.h \ No newline at end of file diff --git a/gb.gtk3/src/gcontainer.cpp b/gb.gtk3/src/gcontainer.cpp new file mode 120000 index 00000000..2dc004fd --- /dev/null +++ b/gb.gtk3/src/gcontainer.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gcontainer.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gcontainer.h b/gb.gtk3/src/gcontainer.h new file mode 120000 index 00000000..b6a4be6f --- /dev/null +++ b/gb.gtk3/src/gcontainer.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcontainer.h \ No newline at end of file diff --git a/gb.gtk3/src/gcontrol.cpp b/gb.gtk3/src/gcontrol.cpp new file mode 120000 index 00000000..978b4ee6 --- /dev/null +++ b/gb.gtk3/src/gcontrol.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gcontrol.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gcontrol.h b/gb.gtk3/src/gcontrol.h new file mode 120000 index 00000000..20d8af74 --- /dev/null +++ b/gb.gtk3/src/gcontrol.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcontrol.h \ No newline at end of file diff --git a/gb.gtk3/src/gcursor.cpp b/gb.gtk3/src/gcursor.cpp new file mode 120000 index 00000000..113d89bb --- /dev/null +++ b/gb.gtk3/src/gcursor.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gcursor.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gcursor.h b/gb.gtk3/src/gcursor.h new file mode 120000 index 00000000..66be2fe3 --- /dev/null +++ b/gb.gtk3/src/gcursor.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcursor.h \ No newline at end of file diff --git a/gb.gtk3/src/gdesktop.cpp b/gb.gtk3/src/gdesktop.cpp new file mode 120000 index 00000000..a41bce43 --- /dev/null +++ b/gb.gtk3/src/gdesktop.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gdesktop.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gdesktop.h b/gb.gtk3/src/gdesktop.h new file mode 120000 index 00000000..f8410ca3 --- /dev/null +++ b/gb.gtk3/src/gdesktop.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdesktop.h \ No newline at end of file diff --git a/gb.gtk3/src/gdialog.cpp b/gb.gtk3/src/gdialog.cpp new file mode 120000 index 00000000..4a0a000d --- /dev/null +++ b/gb.gtk3/src/gdialog.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gdialog.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gdialog.h b/gb.gtk3/src/gdialog.h new file mode 120000 index 00000000..a91068eb --- /dev/null +++ b/gb.gtk3/src/gdialog.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdialog.h \ No newline at end of file diff --git a/gb.gtk3/src/gdrag.cpp b/gb.gtk3/src/gdrag.cpp new file mode 120000 index 00000000..a120b2b3 --- /dev/null +++ b/gb.gtk3/src/gdrag.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gdrag.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gdrag.h b/gb.gtk3/src/gdrag.h new file mode 120000 index 00000000..c1eee5d7 --- /dev/null +++ b/gb.gtk3/src/gdrag.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdrag.h \ No newline at end of file diff --git a/gb.gtk3/src/gdrawingarea.cpp b/gb.gtk3/src/gdrawingarea.cpp new file mode 120000 index 00000000..742990cd --- /dev/null +++ b/gb.gtk3/src/gdrawingarea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gdrawingarea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gdrawingarea.h b/gb.gtk3/src/gdrawingarea.h new file mode 120000 index 00000000..f3eb6d39 --- /dev/null +++ b/gb.gtk3/src/gdrawingarea.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdrawingarea.h \ No newline at end of file diff --git a/gb.gtk3/src/gfont.cpp b/gb.gtk3/src/gfont.cpp new file mode 120000 index 00000000..3c8c7ad1 --- /dev/null +++ b/gb.gtk3/src/gfont.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gfont.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gfont.h b/gb.gtk3/src/gfont.h new file mode 120000 index 00000000..0dccf9d3 --- /dev/null +++ b/gb.gtk3/src/gfont.h @@ -0,0 +1 @@ +../../gb.gtk/src/gfont.h \ No newline at end of file diff --git a/gb.gtk3/src/ggambastag.h b/gb.gtk3/src/ggambastag.h new file mode 120000 index 00000000..7fb73da3 --- /dev/null +++ b/gb.gtk3/src/ggambastag.h @@ -0,0 +1 @@ +../../gb.gtk/src/ggambastag.h \ No newline at end of file diff --git a/gb.gtk3/src/gkey.cpp b/gb.gtk3/src/gkey.cpp new file mode 120000 index 00000000..dd925606 --- /dev/null +++ b/gb.gtk3/src/gkey.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gkey.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gkey.h b/gb.gtk3/src/gkey.h new file mode 120000 index 00000000..714b68eb --- /dev/null +++ b/gb.gtk3/src/gkey.h @@ -0,0 +1 @@ +../../gb.gtk/src/gkey.h \ No newline at end of file diff --git a/gb.gtk3/src/gmainwindow.cpp b/gb.gtk3/src/gmainwindow.cpp new file mode 120000 index 00000000..65dab4d5 --- /dev/null +++ b/gb.gtk3/src/gmainwindow.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmainwindow.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmainwindow.h b/gb.gtk3/src/gmainwindow.h new file mode 120000 index 00000000..1b869bdf --- /dev/null +++ b/gb.gtk3/src/gmainwindow.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmainwindow.h \ No newline at end of file diff --git a/gb.gtk3/src/gmenu.cpp b/gb.gtk3/src/gmenu.cpp new file mode 120000 index 00000000..c9162903 --- /dev/null +++ b/gb.gtk3/src/gmenu.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmenu.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmenu.h b/gb.gtk3/src/gmenu.h new file mode 120000 index 00000000..2f3122a7 --- /dev/null +++ b/gb.gtk3/src/gmenu.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmenu.h \ No newline at end of file diff --git a/gb.gtk3/src/gmouse.cpp b/gb.gtk3/src/gmouse.cpp new file mode 120000 index 00000000..36c37b97 --- /dev/null +++ b/gb.gtk3/src/gmouse.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmouse.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmouse.h b/gb.gtk3/src/gmouse.h new file mode 120000 index 00000000..e7335241 --- /dev/null +++ b/gb.gtk3/src/gmouse.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmouse.h \ No newline at end of file diff --git a/gb.gtk3/src/gpanel.cpp b/gb.gtk3/src/gpanel.cpp new file mode 120000 index 00000000..26afab69 --- /dev/null +++ b/gb.gtk3/src/gpanel.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gpanel.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gpanel.h b/gb.gtk3/src/gpanel.h new file mode 120000 index 00000000..19ad8661 --- /dev/null +++ b/gb.gtk3/src/gpanel.h @@ -0,0 +1 @@ +../../gb.gtk/src/gpanel.h \ No newline at end of file diff --git a/gb.gtk3/src/gpicture.cpp b/gb.gtk3/src/gpicture.cpp new file mode 120000 index 00000000..720e5134 --- /dev/null +++ b/gb.gtk3/src/gpicture.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gpicture.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gpicture.h b/gb.gtk3/src/gpicture.h new file mode 120000 index 00000000..97ddb1a0 --- /dev/null +++ b/gb.gtk3/src/gpicture.h @@ -0,0 +1 @@ +../../gb.gtk/src/gpicture.h \ No newline at end of file diff --git a/gb.gtk3/src/gprinter.cpp b/gb.gtk3/src/gprinter.cpp new file mode 120000 index 00000000..2fd4ba7d --- /dev/null +++ b/gb.gtk3/src/gprinter.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gprinter.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gprinter.h b/gb.gtk3/src/gprinter.h new file mode 120000 index 00000000..b44ee706 --- /dev/null +++ b/gb.gtk3/src/gprinter.h @@ -0,0 +1 @@ +../../gb.gtk/src/gprinter.h \ No newline at end of file diff --git a/gb.gtk3/src/gscrollbar.h b/gb.gtk3/src/gscrollbar.h new file mode 120000 index 00000000..bc71add4 --- /dev/null +++ b/gb.gtk3/src/gscrollbar.h @@ -0,0 +1 @@ +../../gb.gtk/src/gscrollbar.h \ No newline at end of file diff --git a/gb.gtk3/src/gshare.h b/gb.gtk3/src/gshare.h new file mode 120000 index 00000000..d4cfea20 --- /dev/null +++ b/gb.gtk3/src/gshare.h @@ -0,0 +1 @@ +../../gb.gtk/src/gshare.h \ No newline at end of file diff --git a/gb.gtk3/src/gsignals.cpp b/gb.gtk3/src/gsignals.cpp new file mode 120000 index 00000000..65ebd3db --- /dev/null +++ b/gb.gtk3/src/gsignals.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gsignals.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gslider.cpp b/gb.gtk3/src/gslider.cpp new file mode 120000 index 00000000..2c102be2 --- /dev/null +++ b/gb.gtk3/src/gslider.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gslider.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gslider.h b/gb.gtk3/src/gslider.h new file mode 120000 index 00000000..9edb0927 --- /dev/null +++ b/gb.gtk3/src/gslider.h @@ -0,0 +1 @@ +../../gb.gtk/src/gslider.h \ No newline at end of file diff --git a/gb.gtk3/src/gtabstrip.cpp b/gb.gtk3/src/gtabstrip.cpp new file mode 120000 index 00000000..44502f74 --- /dev/null +++ b/gb.gtk3/src/gtabstrip.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtabstrip.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtabstrip.h b/gb.gtk3/src/gtabstrip.h new file mode 120000 index 00000000..1955f5fb --- /dev/null +++ b/gb.gtk3/src/gtabstrip.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtabstrip.h \ No newline at end of file diff --git a/gb.gtk3/src/gtag.h b/gb.gtk3/src/gtag.h new file mode 120000 index 00000000..1c95e72b --- /dev/null +++ b/gb.gtk3/src/gtag.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtag.h \ No newline at end of file diff --git a/gb.gtk3/src/gtextarea.cpp b/gb.gtk3/src/gtextarea.cpp new file mode 120000 index 00000000..ebf404d6 --- /dev/null +++ b/gb.gtk3/src/gtextarea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtextarea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtextarea.h b/gb.gtk3/src/gtextarea.h new file mode 120000 index 00000000..c9375c10 --- /dev/null +++ b/gb.gtk3/src/gtextarea.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtextarea.h \ No newline at end of file diff --git a/gb.gtk3/src/gtextbox.cpp b/gb.gtk3/src/gtextbox.cpp new file mode 120000 index 00000000..445b644e --- /dev/null +++ b/gb.gtk3/src/gtextbox.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtextbox.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtextbox.h b/gb.gtk3/src/gtextbox.h new file mode 120000 index 00000000..8fac46eb --- /dev/null +++ b/gb.gtk3/src/gtextbox.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtextbox.h \ No newline at end of file diff --git a/gb.gtk3/src/gtools.cpp b/gb.gtk3/src/gtools.cpp new file mode 120000 index 00000000..2ecdae0a --- /dev/null +++ b/gb.gtk3/src/gtools.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtools.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtools.h b/gb.gtk3/src/gtools.h new file mode 120000 index 00000000..2edf4162 --- /dev/null +++ b/gb.gtk3/src/gtools.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtools.h \ No newline at end of file diff --git a/gb.gtk3/src/gtrayicon.cpp b/gb.gtk3/src/gtrayicon.cpp new file mode 120000 index 00000000..2190b99a --- /dev/null +++ b/gb.gtk3/src/gtrayicon.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtrayicon.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtrayicon.h b/gb.gtk3/src/gtrayicon.h new file mode 120000 index 00000000..00dab936 --- /dev/null +++ b/gb.gtk3/src/gtrayicon.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtrayicon.h \ No newline at end of file diff --git a/gb.gtk3/src/gtree.cpp b/gb.gtk3/src/gtree.cpp new file mode 120000 index 00000000..52418b62 --- /dev/null +++ b/gb.gtk3/src/gtree.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtree.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtree.h b/gb.gtk3/src/gtree.h new file mode 120000 index 00000000..d30e5966 --- /dev/null +++ b/gb.gtk3/src/gtree.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtree.h \ No newline at end of file diff --git a/gb.gtk3/src/kentities.h b/gb.gtk3/src/kentities.h new file mode 120000 index 00000000..8e1ac6de --- /dev/null +++ b/gb.gtk3/src/kentities.h @@ -0,0 +1 @@ +../../gb.gtk/src/kentities.h \ No newline at end of file diff --git a/gb.gtk3/src/main.cpp b/gb.gtk3/src/main.cpp new file mode 100644 index 00000000..ef7009a4 --- /dev/null +++ b/gb.gtk3/src/main.cpp @@ -0,0 +1,712 @@ +/*************************************************************************** + + main.cpp + + (c) Daniel Campos Fernández <dcamposf@gmail.com> + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include <stdio.h> + +#include "main.h" +#include "gb.image.h" +#include "gb.gtk.h" +#include "watcher.h" +#include "gkey.h" + +#include "CScreen.h" +#include "CStyle.h" +#include "CDraw.h" +#include "CConst.h" +#include "CColor.h" +#include "CFont.h" +#include "CKey.h" +#include "CPicture.h" +#include "CImage.h" +#include "CClipboard.h" +#include "CMouse.h" +#include "CDialog.h" +#include "CWatcher.h" +#include "CWidget.h" +#include "CDrawingArea.h" +#include "CContainer.h" +#include "CPanel.h" +#include "CMenu.h" +#include "CWindow.h" +#include "CButton.h" +#include "CTextBox.h" +#include "CTextArea.h" +#include "CSlider.h" +#include "CTabStrip.h" +#include "CTrayIcon.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "canimation.h" + +#include "gb.gtk.platform.h" + +#include <gdk/gdkx.h> + +#ifdef GDK_WINDOWING_WAYLAND +#include <gdk/gdkwayland.h> +#endif + +#include <gtk/gtk.h> +#include <string.h> + +GB_CLASS CLASS_Control; +GB_CLASS CLASS_ContainerChildren; +GB_CLASS CLASS_UserControl; +GB_CLASS CLASS_UserContainer; +GB_CLASS CLASS_Picture; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_DrawingArea; +GB_CLASS CLASS_Menu; +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Printer; +GB_CLASS CLASS_SvgImage; + +static void hook_lang(char *lang, int rtl1); +static bool hook_error(int code, char *error, char *where, bool can_ignore); +static void hook_quit(void); +static void hook_main(int *argc, char ***argv); +static void hook_timer(GB_TIMER *timer,bool on); +static void hook_wait(int duration); +static void hook_post(void); +static int hook_loop(); +static void hook_watch(int fd, int type, void *callback, intptr_t param); + +static bool _post_check = false; +static bool _must_check_quit = false; + +static bool _application_keypress = false; +static GB_FUNCTION _application_keypress_func; + +static void *_old_hook_main; + +int MAIN_scale = 0; +bool MAIN_debug_busy = false; +bool MAIN_rtl = false; +const char *MAIN_platform = NULL; +bool MAIN_platform_is_wayland = false; + +//------------------------------------------------------------------------- + +static void GTK_CreateControl(CWIDGET *ob, void *parent, GtkWidget *widget, uint flags) +{ + gControl *ctrl; + bool recreate; + + if (!parent) + { + recreate = true; + ctrl = ob->widget; + ctrl->parent()->remove(ctrl); + ctrl->createBorder(widget); + } + else + { + recreate = false; + ctrl = new gControl(CONTAINER(parent)); + ctrl->border = widget; + } + + ctrl->widget = ctrl->border; + InitControl(ctrl, ob); + ctrl->realize(); + + if (flags & CCF_HAS_INPUT_METHOD) + ctrl->_has_input_method = TRUE; + + if (recreate) + ctrl->updateGeometry(true); +} + +static void *GTK_CreatePicture(cairo_surface_t *surf, int w, int h) +{ + gPicture *p = new gPicture(surf); + + if (w > 0 && h > 0) + { + gPicture *p2 = p->stretch(w, h, true); + p->unref(); + p = p2; + } + + return CPICTURE_create(p); +} + +static int GTK_GetDesktopScale(void) +{ + return MAIN_scale; +} + + +//------------------------------------------------------------------------- + +extern "C" +{ + +const GB_INTERFACE *GB_PTR EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; +GTK_PLATFORM_INTERFACE PLATFORM EXPORT; + +static void declare_tray_icon() +{ + GB.Component.Declare(TrayIconsDesc); + GB.Component.Declare(TrayIconDesc); +} + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ScreenDesc, + ScreensDesc, + DesktopDesc, + ApplicationDesc, + StyleDesc, + SelectDesc, + AlignDesc, + ArrangeDesc, + BorderDesc, + ScrollDesc, + DirectionDesc, + CColorDesc, + CFontsDesc, + CFontDesc, + CKeyDesc, + CImageDesc, + CPictureDesc, + AnimationDesc, + CClipboardDesc, + CDragDesc, + CCursorDesc, + CMouseDesc, + CPointerDesc, + CDialogDesc, + CWatcherDesc, + CWidgetDesc, + ContainerChildrenDesc, + ContainerDesc, + CDrawingAreaDesc, + UserControlDesc, + UserContainerDesc, + CPanelDesc, + CHBoxDesc, + CVBoxDesc, + CHPanelDesc, + CVPanelDesc, + CMenuDesc, + CMenuChildrenDesc, + CWindowMenusDesc, + CWindowControlsDesc, + CWindowDesc, + CWindowsDesc, + CFormDesc, + SliderDesc, + ScrollBarDesc, + CButtonDesc, + CToggleButtonDesc, + CCheckBoxDesc, + CRadioButtonDesc, + CToolButtonDesc, + CTextBoxSelectionDesc, + CTextBoxDesc, + CTextAreaDesc, + CTextAreaSelectionDesc, + CTabStripDesc, + CTabStripContainerDesc, + CTabStripContainerChildrenDesc, + PrinterDesc, + SvgImageDesc, + NULL +}; + +void *GB_GTK3_1[] EXPORT = +{ + (void *)GTK_INTERFACE_VERSION, + (void *)GTK_CreateControl, + (void *)NULL, + (void *)GTK_CreatePicture, + (void *)GTK_GetDesktopScale, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.draw,gb.gui.base"; + +int EXPORT GB_INIT(void) +{ + char *env; + + env = getenv("GB_GUI_BUSY"); + if (env && atoi(env)) + MAIN_debug_busy = true; + + GB.Hook(GB_HOOK_QUIT, (void *)hook_quit); + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + GB.Hook(GB_HOOK_WAIT, (void *)hook_wait); + GB.Hook(GB_HOOK_TIMER,(void *)hook_timer); + GB.Hook(GB_HOOK_WATCH,(void *)hook_watch); + GB.Hook(GB_HOOK_POST,(void*)hook_post); + GB.Hook(GB_HOOK_ERROR,(void*)hook_error); + GB.Hook(GB_HOOK_LANG,(void*)hook_lang); + GB.Hook(GB_HOOK_LOOP, (void *)hook_loop); + + GB.Component.Load("gb.draw"); + GB.Component.Load("gb.image"); + GB.Component.Load("gb.gui.base"); + + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + + GB.Signal.MustCheck(SIGCHLD); + + IMAGE.SetDefaultFormat(GB_IMAGE_RGBA); + DRAW_init(); + + CWatcher::init(); + + CLASS_Control = GB.FindClass("Control"); + CLASS_ContainerChildren = GB.FindClass("ContainerChildren"); + CLASS_UserControl = GB.FindClass("UserControl"); + CLASS_UserContainer = GB.FindClass("UserContainer"); + CLASS_Window = GB.FindClass("Window"); + CLASS_Menu = GB.FindClass("Menu"); + CLASS_Picture = GB.FindClass("Picture"); + //CLASS_Drawing = GB.FindClass("Drawing"); + CLASS_DrawingArea = GB.FindClass("DrawingArea"); + CLASS_Printer = GB.FindClass("Printer"); + CLASS_Image = GB.FindClass("Image"); + CLASS_SvgImage = GB.FindClass("SvgImage"); + +#if !defined(GLIB_VERSION_2_36) + g_type_init(); +#endif /* !defined(GLIB_VERSION_2_36) */ + + hook_lang(GB.System.Language(), GB.System.IsRightToLeft()); + + return -1; +} + +void EXPORT GB_EXIT() +{ + CWatcher::exit(); +} + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "GET_HANDLE")) + { + *value = (void *)CWIDGET_get_handle; + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)(intptr_t)gtk_get_current_event_time(); //gdk_x11_display_get_user_time(gdk_display_get_default()); + return TRUE; + } + else if (!strcasecmp(key, "DECLARE_TRAYICON")) + { + *value = (void *)declare_tray_icon; + return TRUE; + } + else + return FALSE; +} + +static void activate_main_window(intptr_t) +{ + if (gMainWindow::_active) + gtk_window_present(GTK_WINDOW(gMainWindow::_active->topLevel()->border)); +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + static GtkWidget *save_popup_grab = NULL; + + switch(signal) + { + case GB_SIGNAL_DEBUG_BREAK: + if (gApplication::_popup_grab) + { + save_popup_grab = gApplication::_popup_grab; + gApplication::ungrabPopup(); + } + break; + + case GB_SIGNAL_DEBUG_FORWARD: + if (gdk_display_get_default()) + gdk_display_sync(gdk_display_get_default()); + break; + + case GB_SIGNAL_DEBUG_CONTINUE: + GB.Post((GB_CALLBACK)activate_main_window, 0); + if (save_popup_grab) + { + gApplication::_popup_grab = save_popup_grab; + save_popup_grab = NULL; + gApplication::grabPopup(); + } + break; + } +} + +} // extern "C" + +static void load_platform(void) +{ + const char *comp; + + GdkDisplay *display = gdk_display_get_default(); + //fprintf(stderr, "display = %p\n", display); + + #ifdef GDK_WINDOWING_WAYLAND + //fprintf(stderr, "checking wayland\n"); + if (GDK_IS_WAYLAND_DISPLAY(display)) + { + MAIN_platform = "wayland"; + MAIN_platform_is_wayland = true; + comp = "gb.gtk3.wayland"; + } + else + #endif + #ifdef GDK_WINDOWING_X11 + //fprintf(stderr, "checking x11\n"); + if (GDK_IS_X11_DISPLAY(display)) + { + MAIN_platform = "x11"; + comp = "gb.gtk3.x11"; + } + else + #endif + //if (!MAIN_platform) + { + fprintf(stderr, "gb.gtk3: error: unsupported platform\n"); + abort(); + } + + GB.Component.Load(comp); + GB.GetInterface(comp, GTK_PLATFORM_INTERFACE_VERSION, &PLATFORM); +} + +void hook_quit(void) +{ + GB_FUNCTION func; + + while (!gApplication::processInputEvent()); + while (gApplication::eventsPending()) + gtk_main_iteration(); + + if (GB.ExistClass("TrayIcons")) + { + if (!GB.GetFunction(&func, (void *)GB.FindClass("TrayIcons"), "DeleteAll", NULL, NULL)) + GB.Call(&func, 0, FALSE); + } + + if (!GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_Quit", NULL, NULL)) + GB.Call(&func, 0, FALSE); + + CWINDOW_delete_all(true); + gControl::postDelete(); + + //CWatcher::Clear(); + gApplication::exit(); + + PLATFORM.Exit(); +} + +static bool global_key_event_handler(int type) +{ + GB.Call(&_application_keypress_func, 0, FALSE); + return GB.Stopped(); +} + +static void hook_main(int *argc, char ***argv) +{ + static bool init = false; + + char *env; + + if (init) + return; + + env = getenv("GB_GUI_PLATFORM"); + if (env && *env) + { + if (!strcasecmp(env, "X11")) + putenv((char *)"GDK_BACKEND=x11"); + #ifdef GDK_WINDOWING_WAYLAND + else if (!strcasecmp(env, "WAYLAND")) + putenv((char *)"GDK_BACKEND=wayland"); + #endif + else + fprintf(stderr, "gb.gtk3: warning: unknown platform: %s\n", env); + } + + gtk_init(argc, argv); + + load_platform(); + PLATFORM.Init(); + + gApplication::init(argc, argv); + + gApplication::setDefaultTitle(GB.Application.Title()); + CAPPLICATION_MiddleClickPaste = gApplication::hasMiddleClickPaste(); + + gApplication::onEnterEventLoop = GB.Debug.EnterEventLoop; + gApplication::onLeaveEventLoop = GB.Debug.LeaveEventLoop; + + MAIN_scale = gDesktop::scale(); + + if (GB.GetFunction(&_application_keypress_func, (void *)GB.Application.StartupClass(), "Application_KeyPress", "", "") == 0) + { + _application_keypress = true; + gApplication::onKeyEvent = global_key_event_handler; + } + + init = true; + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + +/*static void raise_timer(GB_TIMER *timer) +{ + GB.RaiseTimer(timer); + GB.Unref(POINTER(&timer)); +}*/ + +typedef + struct { + guint source; + GTimer *timer; + int timeout; + } + MyTimerId; + +gboolean hook_timer_function(GB_TIMER *timer) +{ + intptr_t old_id = timer->id; + + GB.RaiseTimer(timer); + + if (timer->id == old_id) // If the event handler has removed the timer or restarted it, do nothing, just let the current source be removed. + { + MyTimerId *id = (MyTimerId *)timer->id; + GTimer *t = id->timer; + int elapsed = (int)(g_timer_elapsed(t, NULL) * 1000) - id->timeout; + int next = timer->delay - elapsed; + if (next < 10) + next = 10; + id->timeout = next; + g_timer_start(t); + id->source = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, next, (GSourceFunc)hook_timer_function, (gpointer)timer, NULL); + //fprintf(stderr, "elapsed = %d delay = %d next = %d\n", elapsed, timer->delay, next); + } + + return false; +} + +static void hook_timer(GB_TIMER *timer, bool on) +{ + if (timer->id) + { + MyTimerId *id = (MyTimerId *)timer->id; + g_source_remove(id->source); + g_timer_destroy(id->timer); + g_free(id); + timer->id = 0; + } + + if (on) + { + MyTimerId *id = g_new(MyTimerId, 1); + id->timer = g_timer_new(); + id->timeout = timer->delay; + id->source = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, timer->delay, (GSourceFunc)hook_timer_function, (gpointer)timer, NULL); + timer->id = (intptr_t)id; + } + else + MAIN_check_quit(); +} + +static void hook_post(void) +{ + _post_check = true; +} + +void MAIN_check_quit() +{ + _must_check_quit = true; +} + +static int hook_loop() +{ + gControl::postDelete(); + _must_check_quit = true; + + for(;;) + { + if (_must_check_quit) + { + if (gApplication::mustQuit()) + break; + if (CWINDOW_must_quit() && CWatcher::count() == 0 && gTrayIcon::visibleCount() == 0 && !GB.HasActiveTimer()) + break; + _must_check_quit = false; + } + MAIN_do_iteration(false); + } + + hook_quit(); + + return 0; +} + +static void hook_wait(int duration) +{ + static bool _warning = FALSE; + + if (gDrawingArea::inAnyDrawEvent()) + { + GB.Error("Wait is forbidden during a repaint event"); + return; + } + + if (duration != -1 && gKey::isValid()) + { + if (!_warning) + { + fprintf(stderr, "gb.gtk3: warning: calling the event loop during a keyboard event handler is ignored\n"); + _warning = TRUE; + } + return; + } + + if (duration >= 0) + { + MAIN_do_iteration(true); + } + else if (duration == -1) + { + bool d = gApplication::disableInputEvents(true); + MAIN_do_iteration(true); // To process deferred routines + while (gApplication::eventsPending()) + MAIN_do_iteration(false); + gApplication::disableInputEvents(d); + } + else if (duration == -2) + MAIN_do_iteration(false); +} + +static void hook_watch(int fd, int type, void *callback, intptr_t param) +{ + CWatcher::Add(fd,type,callback,param); +} + +static bool hook_error(int code, char *error, char *where, bool can_ignore) +{ + gMainWindow *active; + GtkWidget *dialog; + char *msg; + char scode[16]; + gint res; + + if (code > 0) + sprintf(scode, " (#%d)", code); + else + *scode = 0; + + msg = g_strconcat("<b>This application has raised an unexpected error and must abort.</b>\n\n", error, scode, ".\n\n<tt>", where, "</tt>", NULL); + + dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, NULL); + gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), msg); + if (can_ignore) + gtk_dialog_add_button(GTK_DIALOG(dialog), GB.Translate("Ignore"), 2); + gtk_dialog_add_button(GTK_DIALOG(dialog), GB.Translate("Close"), 1); + + active = gDesktop::activeWindow(); + if (active) + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(active->border)); + + res = gtk_dialog_run(GTK_DIALOG(dialog)); + + gtk_widget_destroy(dialog); + + g_free(msg); + return (res == 2); +} + +static void cb_update_lang(gControl *control) +{ + if (control->isVisible() && control->isContainer()) + ((gContainer*)control)->performArrange(); +} + +static void hook_lang(char *lang, int rtl) +{ + MAIN_rtl = rtl; + + if (rtl) + gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); + else + gtk_widget_set_default_direction(GTK_TEXT_DIR_LTR); + + gApplication::forEachControl(cb_update_lang); +} + +void MAIN_do_iteration_just_events() +{ + if (gApplication::eventsPending()) + { + while (!gApplication::processInputEvent()); + gtk_main_iteration_do(false); + } +} + +void MAIN_do_iteration(bool do_not_block) +{ + gApplication::_loopLevel++; + + while (!gApplication::processInputEvent()); + + if (do_not_block) + { + if (gApplication::eventsPending()) + gtk_main_iteration(); + } + else + { + gtk_main_iteration_do(true); + } + + gApplication::_loopLevel--; + + gContainer::postArrange(); + + if (_post_check) + { + _post_check = false; + GB.CheckPost(); + } + + gControl::postDelete(); +} + diff --git a/gb.gtk3/src/main.h b/gb.gtk3/src/main.h new file mode 100644 index 00000000..a521aa3e --- /dev/null +++ b/gb.gtk3/src/main.h @@ -0,0 +1,67 @@ +/*************************************************************************** + + main.h + + (c) Daniel Campos Fernández <dcamposf@gmail.com> + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb.geom.h" +#include "widgets.h" +#include "gb.gtk.h" +#include "gb.gtk.platform.h" +#include "CWidget.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +extern IMAGE_INTERFACE IMAGE; +extern GEOM_INTERFACE GEOM; +extern GTK_PLATFORM_INTERFACE PLATFORM; + +extern GB_CLASS CLASS_Control; +extern GB_CLASS CLASS_ContainerChildren; +extern GB_CLASS CLASS_UserControl; +extern GB_CLASS CLASS_UserContainer; +extern GB_CLASS CLASS_Picture; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_DrawingArea; +extern GB_CLASS CLASS_Menu; +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Printer; +extern GB_CLASS CLASS_SvgImage; + +extern bool MAIN_debug_busy; +extern bool MAIN_rtl; +extern const char *MAIN_platform; +extern bool MAIN_platform_is_wayland; +#endif + +#define GB (*GB_PTR) + +void MAIN_do_iteration(bool do_not_block); +void MAIN_do_iteration_just_events(); +void MAIN_check_quit(); + +#endif + diff --git a/gb.gtk3/src/opengl/Makefile.am b/gb.gtk3/src/opengl/Makefile.am new file mode 100644 index 00000000..e7f5744d --- /dev/null +++ b/gb.gtk3/src/opengl/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.gtk3.opengl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gtk3.opengl.la + +gb_gtk3_opengl_la_LIBADD = @GTK3OPENGL_LIB@ +gb_gtk3_opengl_la_LDFLAGS = -module @LD_FLAGS@ @GTK3OPENGL_LDFLAGS@ +gb_gtk3_opengl_la_CPPFLAGS = @GTK3OPENGL_INC@ + +gb_gtk3_opengl_la_SOURCES = \ + main.h main.c \ + c_glarea.h c_glarea.c + diff --git a/gb.gtk3/src/opengl/c_glarea.c b/gb.gtk3/src/opengl/c_glarea.c new file mode 100644 index 00000000..3febba06 --- /dev/null +++ b/gb.gtk3/src/opengl/c_glarea.c @@ -0,0 +1,101 @@ +/*************************************************************************** + + c_glarea.c + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_GLAREA_C + +#include <GL/gl.h> +#include "c_glarea.h" + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +//------------------------------------------------------------------------- + +static void init_control(void *_object) +{ + if (THIS->init) + return; + + GL.Init(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + GB.Raise(THIS, EVENT_Open, 0); + THIS->init = TRUE; + + return; +} + +static void cb_realize(GtkGLArea *area, void *_object) +{ + gtk_gl_area_make_current(area); + init_control(THIS); +} + +static gboolean cb_resize(GtkGLArea *area, int width, int height, void *_object) +{ + GB.Raise(THIS, EVENT_Resize, 0); + return TRUE; +} + +static gboolean cb_render(GtkGLArea *area, GdkGLContext *context, void *_object) +{ + GB.Raise(THIS, EVENT_Draw, 0); + return TRUE; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(GLArea_new, GB_OBJECT parent) + + THIS->widget = gtk_gl_area_new(); + + GTK.CreateControl(THIS, VARG(parent), THIS->widget, CCF_NONE); + gtk_widget_set_can_focus(THIS->widget, TRUE); + + //gtk_gl_area_set_has_alpha(WIDGET, TRUE); + gtk_gl_area_set_has_depth_buffer(WIDGET, TRUE); + gtk_gl_area_set_has_stencil_buffer(WIDGET, TRUE); + + g_signal_connect(G_OBJECT(THIS->widget), "realize", G_CALLBACK(cb_realize), (gpointer)THIS); + g_signal_connect(G_OBJECT(THIS->widget), "resize", G_CALLBACK(cb_resize), (gpointer)THIS); + g_signal_connect(G_OBJECT(THIS->widget), "render", G_CALLBACK(cb_render), (gpointer)THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC GLAreaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, GLArea_new, "(Parent)Container;"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; diff --git a/gb.gtk3/src/opengl/c_glarea.h b/gb.gtk3/src/opengl/c_glarea.h new file mode 100644 index 00000000..b4241357 --- /dev/null +++ b/gb.gtk3/src/opengl/c_glarea.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_glarea.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_GLAREA_H +#define __C_GLAREA_H + +#include "main.h" + +typedef + struct { + GTK_CONTROL control; + GtkWidget *widget; + bool init; + } + CGLAREA; + +#ifndef __C_GLAREA_C +extern GB_DESC GLAreaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GtkGLArea *)(THIS->widget)) + +//#define CGLAREA_PROPERTIES QT_WIDGET_PROPERTIES + +#endif /* __CGLAREA_CPP */ + +#endif diff --git a/gb.gtk3/src/opengl/gb.gtk3.opengl.component b/gb.gtk3/src/opengl/gb.gtk3.opengl.component new file mode 100644 index 00000000..cf3612d2 --- /dev/null +++ b/gb.gtk3/src/opengl/gb.gtk3.opengl.component @@ -0,0 +1,5 @@ +[Component] +Author=Benoît Minisini +Require=gb.gtk3,gb.opengl +Implement=OpenGLViewer +State=Stable diff --git a/gb.gtk3/src/opengl/main.c b/gb.gtk3/src/opengl/main.c new file mode 100644 index 00000000..89a3d387 --- /dev/null +++ b/gb.gtk3/src/opengl/main.c @@ -0,0 +1,54 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_glarea.h" + +GB_INTERFACE GB EXPORT; +GTK_INTERFACE GTK; +GL_INTERFACE GL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + GLAreaDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + char *env = getenv("GDK_GL"); + if (!env || !*env) + putenv("GDK_GL=legacy"); + + GB.GetInterface("gb.gtk3", GTK_INTERFACE_VERSION, >K); + GB.GetInterface("gb.opengl", GL_INTERFACE_VERSION, &GL); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.gtk3/src/opengl/main.h b/gb.gtk3/src/opengl/main.h new file mode 100644 index 00000000..c68e89f1 --- /dev/null +++ b/gb.gtk3/src/opengl/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.gtk.h" +#include "gb.gl.h" +#include <gtk/gtk.h> + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern GTK_INTERFACE GTK; +extern GL_INTERFACE GL; +#endif + +#endif diff --git a/gb.gtk3/src/watcher.cpp b/gb.gtk3/src/watcher.cpp new file mode 120000 index 00000000..c2bd449e --- /dev/null +++ b/gb.gtk3/src/watcher.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/watcher.cpp \ No newline at end of file diff --git a/gb.gtk3/src/watcher.h b/gb.gtk3/src/watcher.h new file mode 120000 index 00000000..02c01ecb --- /dev/null +++ b/gb.gtk3/src/watcher.h @@ -0,0 +1 @@ +../../gb.gtk/src/watcher.h \ No newline at end of file diff --git a/gb.gtk3/src/wayland/Makefile.am b/gb.gtk3/src/wayland/Makefile.am new file mode 100644 index 00000000..b8dd9b4c --- /dev/null +++ b/gb.gtk3/src/wayland/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.gtk3.wayland +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gtk3.wayland.la + +gb_gtk3_wayland_la_LIBADD = @GTK3WAYLAND_LIB@ +gb_gtk3_wayland_la_LDFLAGS = -module @LD_FLAGS@ @GTK3WAYLAND_LDFLAGS@ +gb_gtk3_wayland_la_CFLAGS = @GTK3WAYLAND_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_gtk3_wayland_la_SOURCES = \ + main.h main.c diff --git a/gb.gtk3/src/wayland/gb.gtk3.wayland.component b/gb.gtk3/src/wayland/gb.gtk3.wayland.component new file mode 100644 index 00000000..58584e3c --- /dev/null +++ b/gb.gtk3/src/wayland/gb.gtk3.wayland.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.qt5 +Hidden=True + + diff --git a/gb.gtk3/src/wayland/main.c b/gb.gtk3/src/wayland/main.c new file mode 100644 index 00000000..301e5f22 --- /dev/null +++ b/gb.gtk3/src/wayland/main.c @@ -0,0 +1,126 @@ +/*************************************************************************** + + main.c + + (c) Benoît Minisini <gambas@users.sourceforge.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +//------------------------------------------------------------------------- + +static void platform_init(void) +{ +} + +static void platform_exit(void) +{ +} + +static GtkWidget *platform_create_plug(long wid) +{ + return NULL; +} + +//------------------------------------------------------------------------- + +static long window_get_id(GdkWindow *window) +{ + return 0; +} + +//------------------------------------------------------------------------- + +bool desktop_has_system_tray() +{ + return FALSE; +} + +void desktop_show_tray_icon(GtkStatusIcon *icon, int width, int height) +{ +} +//------------------------------------------------------------------------- + +const GB_INTERFACE *GB_PTR EXPORT; + +void *GB_GTK3_WAYLAND_1[] EXPORT = { + + (void *)GTK_PLATFORM_INTERFACE_VERSION, + + (void *)platform_init, + (void *)platform_exit, + + (void *)platform_create_plug, + + (void *)window_get_id, + + (void *)desktop_has_system_tray, + (void *)desktop_show_tray_icon, + + /*(void *)release_grab, + (void *)unrelease_grab, + (void *)get_last_key_code, + + (void *)desktop_get_resolution_x, + (void *)desktop_get_resolution_y, + (void *)desktop_screenshot, + + (void *)window_get_virtual_desktop, + (void *)window_set_virtual_desktop, + (void *)window_remap, + (void *)window_set_properties, + (void *)window_set_user_time, + (void *)window_set_transient_for,*/ + + NULL + }; + + +int EXPORT GB_INFO(const char *key, void **value) +{ + /*if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)QX11Info::display(); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)QX11Info::appRootWindow(); + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)QX11Info::appTime(); + return TRUE; + } + else*/ + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.gtk3/src/wayland/main.h b/gb.gtk3/src/wayland/main.h new file mode 100644 index 00000000..8423c24a --- /dev/null +++ b/gb.gtk3/src/wayland/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.gtk.platform.h" + +#ifndef __MAIN_C +extern "C" const GB_INTERFACE *GB_PTR; +#endif +#define GB (*GB_PTR) + +#endif diff --git a/gb.gtk3/src/webview/Makefile.am b/gb.gtk3/src/webview/Makefile.am new file mode 100644 index 00000000..d127e316 --- /dev/null +++ b/gb.gtk3/src/webview/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.gtk3.webview +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gtk3.webview.la + +gb_gtk3_webview_la_LIBADD = @GTK3_LIB@ @GTK3WEBVIEW_LIB@ +gb_gtk3_webview_la_LDFLAGS = -module @LD_FLAGS@ @GTK3_LDFLAGS@ @GTK3WEBVIEW_LDFLAGS@ +gb_gtk3_webview_la_CPPFLAGS = @GTK3_INC@ @GTK3WEBVIEW_INC@ + +gb_gtk3_webview_la_SOURCES = \ + main.h main.c \ + c_webview.h c_webview.c \ + c_websettings.h c_websettings.c + diff --git a/gb.gtk3/src/webview/c_websettings.c b/gb.gtk3/src/webview/c_websettings.c new file mode 100644 index 00000000..b451bd98 --- /dev/null +++ b/gb.gtk3/src/webview/c_websettings.c @@ -0,0 +1,403 @@ +/*************************************************************************** + + c_websettings.c + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WEBSETTINGS_C + +#include <errno.h> +#include <unistd.h> + +#include "c_webview.h" +#include "c_websettings.h" + +static WebKitSettings *_default_settings = NULL; + +static WebKitSettings *get_settings(void *_object) +{ + if (!_object || GB.Is(_object, GB.FindClass("WebSettings"))) + { + if (!_default_settings) + { + GtkWidget *widget = webkit_web_view_new(); + _default_settings = g_object_ref(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(widget))); + gtk_widget_destroy(widget); + } + return _default_settings; + } + else + { + return webkit_web_view_get_settings(WIDGET); + } +} + +//------------------------------------------------------------------------- + +static bool get_flag(WebKitSettings *settings, int flag) +{ + switch(flag) + { + case 0: return webkit_settings_get_auto_load_images(settings); + case 1: return webkit_settings_get_enable_javascript(settings); + case 2: return webkit_settings_get_javascript_can_open_windows_automatically(settings); + case 3: return webkit_settings_get_javascript_can_access_clipboard(settings); // + 27 + case 5: return webkit_settings_get_enable_html5_local_storage(settings); + case 8: return webkit_settings_get_enable_spatial_navigation(settings); + case 9: return webkit_settings_get_allow_file_access_from_file_urls(settings); + case 10: return webkit_settings_get_enable_hyperlink_auditing(settings); +#if WEBKIT_CHECK_VERSION(2, 32, 0) +#else + case 13: return webkit_settings_get_enable_plugins(settings); +#endif + case 14: return webkit_settings_get_enable_fullscreen(settings); + case 16: return webkit_settings_get_enable_webgl(settings); +#if WEBKIT_CHECK_VERSION(2, 32, 0) +#else + case 17: return webkit_settings_get_enable_accelerated_2d_canvas(settings); +#endif + case 21: return webkit_settings_get_print_backgrounds(settings); + case 26: return webkit_settings_get_media_playback_requires_user_gesture(settings); + case 29: return webkit_settings_get_enable_dns_prefetching(settings); + } + + return FALSE; +} + +static void set_flag(WebKitSettings *settings, int flag, bool value) +{ + switch(flag) + { + case 0: webkit_settings_set_auto_load_images(settings, value); break; + case 1: webkit_settings_set_enable_javascript(settings, value); break; + case 2: webkit_settings_set_javascript_can_open_windows_automatically(settings, value); break; + case 3: webkit_settings_set_javascript_can_access_clipboard(settings, value); break; // + 27 + case 5: webkit_settings_set_enable_html5_local_storage(settings, value); break; + case 8: webkit_settings_set_enable_spatial_navigation(settings, value); break; + case 9: webkit_settings_set_allow_file_access_from_file_urls(settings, value); break; + case 10: webkit_settings_set_enable_hyperlink_auditing(settings, value); break; +#if WEBKIT_CHECK_VERSION(2, 32, 0) +#else + case 13: webkit_settings_set_enable_plugins(settings, value); break; +#endif + case 14: webkit_settings_set_enable_fullscreen(settings, value); break; + case 16: webkit_settings_set_enable_webgl(settings, value); break; +#if WEBKIT_CHECK_VERSION(2, 32, 0) +#else + case 17: webkit_settings_set_enable_accelerated_2d_canvas(settings, value); break; +#endif + case 21: webkit_settings_set_print_backgrounds(settings, value); break; + case 26: webkit_settings_set_media_playback_requires_user_gesture(settings, value); break; + case 29: webkit_settings_set_enable_dns_prefetching(settings, value); break; + } +} + +BEGIN_METHOD(WebSettings_get, GB_INTEGER flag) + + GB.ReturnBoolean(get_flag(get_settings(_object), VARG(flag))); + +END_METHOD + +BEGIN_METHOD(WebSettings_put, GB_BOOLEAN value; GB_INTEGER flag) + + int flag = VARG(flag); + + if (flag >= 0) + set_flag(get_settings(_object), flag, VARG(value)); + +END_METHOD + +BEGIN_METHOD_VOID(WebSettings_exit) + + if (_default_settings) + { + g_object_unref(_default_settings); + _default_settings = NULL; + } + +END_METHOD + + +//------------------------------------------------------------------------- + +#define IMPLEMENT_FONT_PROPERTY(_Font, _font) \ +BEGIN_PROPERTY(WebSettingsFonts_##_Font) \ + \ + WebKitSettings *settings = get_settings(_object); \ + \ + if (READ_PROPERTY) \ + GB.ReturnNewZeroString(webkit_settings_get_##_font##_font_family(settings)); \ + else \ + webkit_settings_set_##_font##_font_family(settings, GB.ToZeroString(PROP(GB_STRING))); \ + \ +END_PROPERTY + +IMPLEMENT_FONT_PROPERTY(Default, default) +IMPLEMENT_FONT_PROPERTY(Fixed, monospace) +IMPLEMENT_FONT_PROPERTY(Serif, serif) +IMPLEMENT_FONT_PROPERTY(SansSerif, sans_serif) +IMPLEMENT_FONT_PROPERTY(Cursive, cursive) +IMPLEMENT_FONT_PROPERTY(Fantasy, fantasy) +IMPLEMENT_FONT_PROPERTY(Pictograph, pictograph) + +#define IMPLEMENT_SIZE_PROPERTY(_Size, _size) \ +BEGIN_PROPERTY(WebSettingsFonts_##_Size##Size) \ + \ + WebKitSettings *settings = get_settings(_object); \ + \ + if (READ_PROPERTY) \ + GB.ReturnInteger(webkit_settings_get_##_size##_font_size(settings) * 72 / 96); \ + else \ + webkit_settings_set_##_size##_font_size(settings, VPROP(GB_INTEGER) * 96 / 72); \ + \ +END_PROPERTY + +IMPLEMENT_SIZE_PROPERTY(Default, default) +IMPLEMENT_SIZE_PROPERTY(DefaultFixed, default_monospace) +IMPLEMENT_SIZE_PROPERTY(Minimum, minimum) + +//------------------------------------------------------------------------- + +void WEBVIEW_init_settings(void *_object) +{ + WebKitSettings *init = get_settings(NULL); + WebKitSettings *settings = get_settings(THIS); + + for (int i = 0; i <= 29; i++) + set_flag(settings, i, get_flag(init, i)); + + webkit_settings_set_default_font_family(settings, webkit_settings_get_default_font_family(init)); + webkit_settings_set_monospace_font_family(settings, webkit_settings_get_monospace_font_family(init)); + webkit_settings_set_serif_font_family(settings, webkit_settings_get_serif_font_family(init)); + webkit_settings_set_sans_serif_font_family(settings, webkit_settings_get_sans_serif_font_family(init)); + webkit_settings_set_cursive_font_family(settings, webkit_settings_get_cursive_font_family(init)); + webkit_settings_set_fantasy_font_family(settings, webkit_settings_get_fantasy_font_family(init)); + webkit_settings_set_pictograph_font_family(settings, webkit_settings_get_pictograph_font_family(init)); + + webkit_settings_set_default_font_size(settings, webkit_settings_get_default_font_size(init)); + webkit_settings_set_default_monospace_font_size(settings, webkit_settings_get_default_monospace_font_size(init)); + webkit_settings_set_minimum_font_size(settings, webkit_settings_get_minimum_font_size(init)); + + //webkit_settings_set_enable_developer_extras(settings, TRUE); +} + +#if 0 +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettings_exit) + + GB.FreeString(&_cache_path); + +END_METHOD + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsProxy_Host) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.hostName()); + else + { + proxy.setHostName(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_User) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.user()); + else + { + proxy.setUser(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Password) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.password()); + else + { + proxy.setPassword(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Port) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.port()); + else + { + proxy.setPort(VPROP(GB_INTEGER)); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Type) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.type()); + else + { + int type = VPROP(GB_INTEGER); + if (type == QNetworkProxy::DefaultProxy || type == QNetworkProxy::NoProxy || type == QNetworkProxy::Socks5Proxy || type == QNetworkProxy::HttpProxy) + { + proxy.setType((QNetworkProxy::ProxyType)type); + nam->setProxy(proxy); + } + } + +END_PROPERTY +#endif + +/***************************************************************************/ + +#if 0 +GB_DESC WebSettingsIconDatabaseDesc[] = +{ + GB_DECLARE(".WebSettings.IconDatabase", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Clear", NULL, WebSettingsIconDatabase_Clear, NULL), + GB_STATIC_PROPERTY("Path", "s", WebSettingsIconDatabase_Path), + GB_STATIC_METHOD("_get", "Picture", WebSettingsIconDatabase_get, "(Url)s"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsCacheDesc[] = +{ + GB_DECLARE(".WebSettings.Cache", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Enabled", "b", WebSettingsCache_Enabled), + GB_STATIC_PROPERTY("Path", "s", WebSettingsCache_Path), + GB_STATIC_METHOD("Clear", NULL, WebSettingsCache_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsProxyDesc[] = +{ + GB_DECLARE(".WebSettings.Proxy", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Type", "i", WebSettingsProxy_Type), + GB_STATIC_PROPERTY("Host", "s", WebSettingsProxy_Host), + GB_STATIC_PROPERTY("Port", "i", WebSettingsProxy_Port), + GB_STATIC_PROPERTY("User", "s", WebSettingsProxy_User), + GB_STATIC_PROPERTY("Password", "s", WebSettingsProxy_Password), + + GB_END_DECLARE +}; +#endif + +GB_DESC WebSettingsFontsDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebSettings.Fonts"), + + GB_PROPERTY("Default", "s", WebSettingsFonts_Default), + GB_PROPERTY("Fixed", "s", WebSettingsFonts_Fixed), + GB_PROPERTY("Serif", "s", WebSettingsFonts_Serif), + GB_PROPERTY("SansSerif", "s", WebSettingsFonts_SansSerif), + GB_PROPERTY("Cursive", "s", WebSettingsFonts_Cursive), + GB_PROPERTY("Fantasy", "s", WebSettingsFonts_Fantasy), + GB_PROPERTY("Pictograph", "s", WebSettingsFonts_Pictograph), + + GB_PROPERTY("MinimumSize", "i", WebSettingsFonts_MinimumSize), + GB_PROPERTY("DefaultSize", "i", WebSettingsFonts_DefaultSize), + GB_PROPERTY("DefaultFixedSize", "i", WebSettingsFonts_DefaultFixedSize), + + GB_END_DECLARE +}; + +GB_DESC WebViewSettingsDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Settings"), + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + GB_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsDesc[] = +{ + GB_DECLARE("WebSettings", sizeof(CWEBSETTINGS)), GB_AUTO_CREATABLE(), GB_NOT_CREATABLE(), + + GB_CONSTANT("AutoLoadImages", "i", 0), + GB_CONSTANT("Javascript", "i", 1), + GB_CONSTANT("JavascriptCanOpenWindows", "i", 2), + GB_CONSTANT("JavascriptCanAccessClipboard", "i", 3), + GB_CONSTANT("LocalStorage", "i", 5), + GB_CONSTANT("SpatialNavigation", "i", 8), + GB_CONSTANT("LocalContentCanAccessFileUrls", "i", 9), + GB_CONSTANT("HyperlinkAuditing", "i", 10), + GB_CONSTANT("Plugins", "i", 13), + GB_CONSTANT("FullScreenSupport", "i", 14), + GB_CONSTANT("WebGL", "i", 16), + GB_CONSTANT("Accelerated2dCanvas", "i", 17), + GB_CONSTANT("PrintElementBackgrounds", "i", 21), + GB_CONSTANT("PlaybackRequiresUserGesture", "i", 26), + GB_CONSTANT("DnsPrefetch", "i", 29), + + /*GB_CONSTANT("DefaultProxy", "i", QNetworkProxy::DefaultProxy), + GB_CONSTANT("NoProxy", "i", QNetworkProxy::NoProxy), + GB_CONSTANT("Socks5Proxy", "i", QNetworkProxy::Socks5Proxy), + GB_CONSTANT("HttpProxy", "i", QNetworkProxy::HttpProxy),*/ + + /*GB_STATIC_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + GB_STATIC_PROPERTY_SELF("IconDatabase", ".WebSettings.IconDatabase"), + GB_STATIC_PROPERTY_SELF("Cache", ".WebSettings.Cache"), + GB_STATIC_PROPERTY_SELF("Proxy", ".WebSettings.Proxy"),*/ + + GB_STATIC_METHOD("_exit", NULL, WebSettings_exit, NULL), + + GB_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + //GB_STATIC_METHOD("_exit", NULL, WebSettings_exit, NULL), + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk3/src/webview/c_websettings.h b/gb.gtk3/src/webview/c_websettings.h new file mode 100644 index 00000000..513eb1c2 --- /dev/null +++ b/gb.gtk3/src/webview/c_websettings.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + c_websettings.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WEBSETTINGS_H +#define __C_WEBSETTINGS_H + +#include "main.h" + +typedef + struct { + GB_BASE ob; + } + CWEBSETTINGS; + +#ifndef __C_WEBSETTINGS_C + +extern GB_DESC WebViewSettingsDesc[]; +extern GB_DESC WebSettingsDesc[]; +extern GB_DESC WebSettingsFontsDesc[]; + +#else + +#define THIS ((CWEBVIEW *)_object) +#define WIDGET (WEBKIT_WEB_VIEW(THIS->widget)) + +#endif + +void WEBVIEW_init_settings(void *_object); + +#endif diff --git a/gb.gtk3/src/webview/c_webview.c b/gb.gtk3/src/webview/c_webview.c new file mode 100644 index 00000000..fc7bd73b --- /dev/null +++ b/gb.gtk3/src/webview/c_webview.c @@ -0,0 +1,679 @@ +/*************************************************************************** + + c_webview.c + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WEBVIEW_C + +#include "c_websettings.h" +#include "c_webview.h" + +#define HISTORY (webkit_web_view_get_back_forward_list(WIDGET)) + +static bool _init = FALSE; + +//--------------------------------------------------------------------------- + +static void set_link(void *_object, const char *link, int len) +{ + GB.FreeString(&THIS->link); + if (len == 0 || !link) + return; + if (len < 0) len = strlen(link); + THIS->link = GB.NewString(link, len); +} + +//--------------------------------------------------------------------------- + +DECLARE_EVENT(EVENT_TITLE); +DECLARE_EVENT(EVENT_URL); +DECLARE_EVENT(EVENT_ICON); +DECLARE_EVENT(EVENT_START); +DECLARE_EVENT(EVENT_PROGRESS); +DECLARE_EVENT(EVENT_FINISH); +DECLARE_EVENT(EVENT_ERROR); +DECLARE_EVENT(EVENT_LINK); +DECLARE_EVENT(EVENT_NEW_VIEW); + +static int EVENT_MENU = -1; + +static void cb_title(WebKitWebView *widget, GParamSpec *pspec, CWEBVIEW *_object) +{ + GB.Raise(THIS, EVENT_TITLE, 0); +} + +static void cb_url(WebKitWebView *widget, GParamSpec *pspec, CWEBVIEW *_object) +{ + //fprintf(stderr, "cb_url: %s\n", webkit_web_view_get_uri(WIDGET)); + GB.Raise(THIS, EVENT_URL, 0); + if (!THIS->got_load_event) + GB.Raise(THIS, EVENT_FINISH, 0); +} + +static void cb_icon(WebKitWebView *widget, GParamSpec *pspec, CWEBVIEW *_object) +{ + GB.Unref(POINTER(&THIS->icon)); + THIS->icon = NULL; + GB.Raise(THIS, EVENT_ICON, 0); +} + +static void cb_load_changed(WebKitWebView *widget, WebKitLoadEvent load_event, CWEBVIEW *_object) +{ + //fprintf(stderr, "cb_load_changed: %d\n", load_event); + + switch (load_event) + { + case WEBKIT_LOAD_STARTED: + THIS->got_load_event = TRUE; + break; + + case WEBKIT_LOAD_FINISHED: + GB.Raise(THIS, EVENT_FINISH, 0); + GB.FreeString(&THIS->link); + break; + + default: + break; + } +} + +static gboolean cb_load_failed(WebKitWebView *widget, WebKitLoadEvent load_event, gchar *failing_uri, GError *error, CWEBVIEW *_object) +{ + //fprintf(stderr, "cb_load_failed\n"); + if (!THIS->error) + { + THIS->error = TRUE; + GB.Raise(THIS, EVENT_ERROR, 0); + } + return FALSE; +} + +static void cb_progress(WebKitWebView *widget, GParamSpec *pspec, CWEBVIEW *_object) +{ + //fprintf(stderr, "cb_progress: %f\n", webkit_web_view_get_estimated_load_progress(WIDGET)); + if (!THIS->error) + { + GB.Raise(THIS, EVENT_PROGRESS, 0); + if (webkit_web_view_get_estimated_load_progress(WIDGET) == 1.0) + GB.Raise(THIS, EVENT_FINISH, 0); + } +} + +static void cb_link(WebKitWebView *widget, WebKitHitTestResult *hit_test_result, guint modifiers, CWEBVIEW *_object) +{ + const char *uri = webkit_hit_test_result_get_link_uri(hit_test_result); + set_link(THIS, uri, -1); + GB.Raise(THIS, EVENT_LINK, 0); +} + +static GtkWidget *cb_create(WebKitWebView *widget, WebKitNavigationAction *navigation_action, CWEBVIEW *_object) +{ + GtkWidget *new_view; + + if (GB.Raise(THIS, EVENT_NEW_VIEW, 0)) + return NULL; + + if (!THIS->new_view) + return NULL; + + new_view = ((CWEBVIEW *)THIS->new_view)->widget; + GB.Unref(POINTER(&THIS->new_view)); + THIS->new_view = NULL; + return new_view; +} + +static gboolean cb_decide_policy(WebKitWebView *widget, WebKitPolicyDecision *decision, WebKitPolicyDecisionType type, CWEBVIEW *_object) +{ + if (type == WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) + { + /*const char *uri = webkit_uri_request_get_uri( + webkit_navigation_action_get_request( + webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(decision)))); + + fprintf(stderr, "cb_decide_policy: %s\n", uri);*/ + + if (THIS->accept_next) + { + THIS->accept_next = FALSE; + return FALSE; + } + + THIS->error = FALSE; + THIS->got_load_event = FALSE; + if (GB.Raise(THIS, EVENT_START, 0)) + { + //fprintf(stderr, "cancel !\n"); + webkit_policy_decision_ignore(decision); + GB.RaiseLater(THIS, EVENT_ERROR); + } + } + + return FALSE; +} + +static bool start_callback(void *_object) +{ + if (THIS->cb_running) + { + GB.Error("Pending asynchronous method"); + return TRUE; + } + + THIS->cb_running = TRUE; + GB.Ref(THIS); + return FALSE; +} + +static void run_callback(void *_object, const char *error) +{ + while(THIS->cb_running) + GB.Wait(-1); + + if (THIS->cb_error) + { + GB.Error(error, THIS->cb_result); + GB.FreeString(&THIS->cb_result); + } + else + { + GB.ReturnString(GB.FreeStringLater(THIS->cb_result)); + THIS->cb_result = NULL; + } + + THIS->cb_error = FALSE; + GB.Unref(POINTER(&_object)); +} + +static void cb_javascript_finished(WebKitWebView *widget, GAsyncResult *result, void *_object) +{ + WebKitJavascriptResult *js_result; + JSCValue *value; + GError *error = NULL; + JSCException *exception; + char *json; + + js_result = webkit_web_view_run_javascript_finish(widget, result, &error); + if (!js_result) + { + THIS->cb_result = GB.NewZeroString(error->message); + g_error_free(error); + THIS->cb_error = TRUE; + } + else + { + value = webkit_javascript_result_get_js_value(js_result); + json = jsc_value_to_json(value, 0); + + exception = jsc_context_get_exception(jsc_value_get_context (value)); + if (exception) + { + THIS->cb_result = GB.NewZeroString(jsc_exception_get_message(exception)); + THIS->cb_error = TRUE; + } + else + { + THIS->cb_result = GB.NewZeroString(json); + } + + g_free(json); + webkit_javascript_result_unref(js_result); + } + + THIS->cb_running = FALSE; +} + +static char *get_encoding(const char *mimetype) +{ + char *p = strstr(mimetype, ";charset="); + + if (!p) + return NULL; + + p += 9; + if (!strcasecmp(p, "utf-8") || !strcasecmp(p, "utf8")) + return NULL; + + return p; +} + +static void cb_html_finished(WebKitWebResource *resource, GAsyncResult *result, void *_object) +{ + GError *error = NULL; + gsize length = 0; + guchar *html; + char *encoding; + + html = webkit_web_resource_get_data_finish(resource, result, &length, &error); + + if (!html) + { + THIS->cb_result = GB.NewZeroString(error->message); + g_error_free(error); + THIS->cb_error = TRUE; + return; + } + + encoding = get_encoding(webkit_uri_response_get_mime_type(webkit_web_resource_get_response(resource))); + + if (encoding) + { + if (GB.ConvString(&THIS->cb_result, (char *)html, length, encoding, "UTF-8")) + { + THIS->cb_result = GB.NewZeroString(GB.GetErrorMessage()); + THIS->cb_error = TRUE; + } + } + else + { + THIS->cb_result = GB.NewString((char *)html, length); + } + + g_free(html); + THIS->cb_running = FALSE; +} + +static gboolean cb_context_menu(WebKitWebView *web_view, WebKitContextMenu *context_menu, GdkEvent *event, WebKitHitTestResult *hit_test_result, void *_object) +{ + if (EVENT_MENU < 0) + EVENT_MENU = GB.GetEvent(GB.GetClass(THIS), "Menu"); + + return GB.CanRaise(THIS, EVENT_MENU); +} + +static void update_language(void *_object) +{ + if (THIS->language && *THIS->language) + { + gchar **languages = g_strsplit(THIS->language, ",", -1); + webkit_web_context_set_preferred_languages(THIS->context, (const gchar **)languages); + g_strfreev(languages); + } + else + { + const gchar *languages[2] = { NULL, NULL }; + char *lang, *p; + + lang = g_strdup(GB.System.Language()); + p = index(lang, '_'); + if (p) *p = '-'; + languages[0] = lang; + webkit_web_context_set_preferred_languages(THIS->context, languages); + g_free(lang); + } +} + +//--------------------------------------------------------------------------- + +#define must_patch(_widget) (true) +#include "../gb.gtk.patch.h" + +PATCH_DECLARE(WEBKIT_TYPE_WEB_VIEW) + +static void create_widget(void *_object, void *parent) +{ + THIS->context = webkit_web_context_new_ephemeral(); + THIS->widget = webkit_web_view_new_with_context(THIS->context); + + GTK.CreateControl(THIS, parent, THIS->widget, CCF_HAS_INPUT_METHOD); + + PATCH_CLASS(THIS->widget, WEBKIT_TYPE_WEB_VIEW) + + if (!_init) + { + //webkit_settings_set_load_icons_ignoring_image_load_setting(WEBVIEW_default_settings, TRUE); + //webkit_web_context_set_use_system_appearance_for_scrollbars(webkit_web_context_get_default(), TRUE); + webkit_web_context_set_favicon_database_directory(webkit_web_context_get_default(), NULL); + _init = TRUE; + } + + g_signal_connect(G_OBJECT(WIDGET), "notify::title", G_CALLBACK(cb_title), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "notify::uri", G_CALLBACK(cb_url), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "notify::favicon", G_CALLBACK(cb_icon), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "notify::estimated-load-progress", G_CALLBACK(cb_progress), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "load-changed", G_CALLBACK(cb_load_changed), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "load-failed", G_CALLBACK(cb_load_failed), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "mouse-target-changed", G_CALLBACK(cb_link), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "create", G_CALLBACK(cb_create), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "decide-policy", G_CALLBACK(cb_decide_policy), (gpointer)THIS); + g_signal_connect(G_OBJECT(WIDGET), "context-menu", G_CALLBACK(cb_context_menu), (gpointer)THIS); + + WEBVIEW_init_settings(THIS); +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(WebView_new, GB_OBJECT parent) + + create_widget(THIS, VARG(parent)); + update_language(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_free) + + GB.FreeString(&THIS->link); + GB.FreeString(&THIS->language); + + GB.Unref(POINTER(&THIS->icon)); + GB.Unref(POINTER(&THIS->new_view)); + + g_object_unref(THIS->context); + +END_METHOD + +BEGIN_PROPERTY(WebView_Url) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(webkit_web_view_get_uri(WIDGET)); + else + { + set_link(THIS, PSTRING(), PLENGTH()); + webkit_web_view_load_uri(WIDGET, THIS->link); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Title) + + GB.ReturnNewZeroString(webkit_web_view_get_title(WIDGET)); + +END_PROPERTY + +BEGIN_METHOD(WebView_SetHtml, GB_STRING html; GB_STRING root) + + THIS->accept_next = TRUE; + webkit_web_view_load_html(WIDGET, GB.ToZeroString(ARG(html)), MISSING(root) ? NULL : GB.ToZeroString(ARG(root))); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Back) + + webkit_web_view_go_back(WIDGET); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Forward) + + webkit_web_view_go_forward(WIDGET); + +END_METHOD + +BEGIN_METHOD(WebView_Reload, GB_BOOLEAN bypass) + + if (VARGOPT(bypass, FALSE)) + webkit_web_view_reload_bypass_cache(WIDGET); + else + webkit_web_view_reload(WIDGET); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Stop) + + webkit_web_view_stop_loading(WIDGET); + +END_METHOD + +BEGIN_PROPERTY(WebView_Zoom) + + if (READ_PROPERTY) + GB.ReturnFloat(webkit_web_view_get_zoom_level(WIDGET)); + else + webkit_web_view_set_zoom_level(WIDGET, VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Icon) + + cairo_surface_t *icon; + + if (!THIS->icon) + { + icon = webkit_web_view_get_favicon(WIDGET); + + if (icon) + { + int size = GTK.GetDesktopScale() * 2; + cairo_surface_reference(icon); + THIS->icon = GTK.CreatePicture(icon, size, size); + GB.Ref(THIS->icon); + } + } + + GB.ReturnObject(THIS->icon); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Progress) + + GB.ReturnFloat(webkit_web_view_get_estimated_load_progress(WIDGET)); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_NewView) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->new_view); + else + GB.StoreObject(PROP(GB_OBJECT), &THIS->new_view); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Link) + + GB.ReturnString(THIS->link); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebView_Clear) + + THIS->accept_next = TRUE; + webkit_web_view_load_html(WIDGET, "", NULL); + +END_METHOD + +BEGIN_METHOD(WebView_ExecJavascript, GB_STRING script) + + char *script; + + if (LENGTH(script) == 0) + return; + + script = GB.ToZeroString(ARG(script)); + + if (start_callback(THIS)) + return; + + webkit_web_view_run_javascript(WIDGET, script, NULL, (GAsyncReadyCallback)cb_javascript_finished, (gpointer)THIS); + + run_callback(THIS, "Javascript error: &1"); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_GetHtml) + + if (start_callback(THIS)) + return; + + webkit_web_resource_get_data(webkit_web_view_get_main_resource(WIDGET), NULL, (GAsyncReadyCallback)cb_html_finished, (gpointer)THIS); + + run_callback(THIS, "Unable to retrieve HTML contents: &1"); + +END_METHOD + +BEGIN_PROPERTY(WebView_Language) + + if (READ_PROPERTY) + GB.ReturnString(THIS->language); + else + { + GB.StoreString(PROP(GB_STRING), &THIS->language); + update_language(THIS); + } + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_PROPERTY(WebViewHistoryItem_Title) + + GB.ReturnNewZeroString(webkit_back_forward_list_item_get_title(THIS->item)); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistoryItem_Url) + + GB.ReturnNewZeroString(webkit_back_forward_list_item_get_uri(THIS->item)); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebViewHistoryItem_GoTo) + + webkit_web_view_go_to_back_forward_list_item(WIDGET, THIS->item); + +END_METHOD + +BEGIN_METHOD_VOID(WebViewHistory_Clear) + + fprintf(stderr, "gb.gtk3.webview: warning: WebKitGTK does not know how to clear its history at the moment.\n"); + +END_METHOD + +BEGIN_METHOD(WebViewHistory_get, GB_INTEGER index) + + WebKitBackForwardListItem *item = webkit_back_forward_list_get_nth_item(HISTORY, VARG(index)); + + if (!item) + GB.ReturnNull(); + else + { + THIS->item = item; + RETURN_SELF(); + } + +END_METHOD + +BEGIN_PROPERTY(WebViewHistory_CanGoBack) + + GB.ReturnBoolean(webkit_web_view_can_go_back(WIDGET)); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_CanGoForward) + + GB.ReturnBoolean(webkit_web_view_can_go_forward(WIDGET)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +static void cb_cookies_clear_finished(WebKitWebView *widget, GAsyncResult *result, void *_object) +{ +} + +BEGIN_METHOD_VOID(WebView_Cookies_Clear) + + WebKitWebsiteDataManager *manager = webkit_web_view_get_website_data_manager(WIDGET); + + webkit_website_data_manager_clear(manager, WEBKIT_WEBSITE_DATA_COOKIES, 0, NULL, (GAsyncReadyCallback)cb_cookies_clear_finished, (gpointer)THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC WebViewHistoryItemDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History.Item"), + + GB_PROPERTY_READ("Title", "s", WebViewHistoryItem_Title), + GB_PROPERTY_READ("Url", "s", WebViewHistoryItem_Url), + GB_METHOD("GoTo", NULL, WebViewHistoryItem_GoTo, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewHistoryDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History"), + + GB_METHOD("Clear", NULL, WebViewHistory_Clear, NULL), + GB_METHOD("_get", ".WebView.History.Item", WebViewHistory_get, "(Index)i"), + GB_PROPERTY_READ("CanGoBack", "b", WebViewHistory_CanGoBack), + GB_PROPERTY_READ("CanGoForward", "b", WebViewHistory_CanGoForward), + + GB_END_DECLARE +}; + +GB_DESC WebViewCookiesDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Cookies"), + + GB_METHOD("Clear", NULL, WebView_Cookies_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewDesc[] = +{ + GB_DECLARE("WebView", sizeof(CWEBVIEW)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, WebView_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, WebView_free, NULL), + + GB_PROPERTY("Url", "s", WebView_Url), + GB_PROPERTY("Title", "s", WebView_Title), + GB_PROPERTY("Zoom", "f", WebView_Zoom), + GB_PROPERTY_READ("Icon", "Picture", WebView_Icon), + GB_PROPERTY_READ("Progress", "f", WebView_Progress), + GB_PROPERTY("NewView", "WebView", WebView_NewView), + GB_PROPERTY_READ("Link", "s", WebView_Link), + GB_PROPERTY("Language", "s", WebView_Language), + + GB_METHOD("SetHtml", NULL, WebView_SetHtml, "(Html)s[(Root)s]"), + GB_METHOD("GetHtml", "s", WebView_GetHtml, NULL), + + GB_METHOD("Clear", NULL, WebView_Clear, NULL), + + GB_METHOD("Back", NULL, WebView_Back, NULL), + GB_METHOD("Forward", NULL, WebView_Forward, NULL), + GB_METHOD("Reload", NULL, WebView_Reload, "[(BypassCache)b]"), + GB_METHOD("Stop", NULL, WebView_Stop, NULL), + + GB_METHOD("ExecJavascript", "s", WebView_ExecJavascript, "(Javascript)s"), + + GB_PROPERTY_SELF("History", ".WebView.History"), + GB_PROPERTY_SELF("Settings", ".WebView.Settings"), + GB_PROPERTY_SELF("Cookies", ".WebView.Cookies"), + + GB_CONSTANT("_Properties", "s", "*,Url,Zoom=1"), + GB_CONSTANT("_Group", "s", "View"), + + GB_EVENT("Title", NULL, NULL, &EVENT_TITLE), + GB_EVENT("Url", NULL, NULL, &EVENT_URL), + GB_EVENT("Icon", NULL, NULL, &EVENT_ICON), + GB_EVENT("Start", NULL, NULL, &EVENT_START), + GB_EVENT("Progress", NULL, NULL, &EVENT_PROGRESS), + GB_EVENT("Finish", NULL, NULL, &EVENT_FINISH), + GB_EVENT("Error", NULL, NULL, &EVENT_ERROR), + GB_EVENT("Link", NULL, NULL, &EVENT_LINK), + GB_EVENT("NewView", NULL, NULL, &EVENT_NEW_VIEW), + + GB_END_DECLARE +}; diff --git a/gb.gtk3/src/webview/c_webview.h b/gb.gtk3/src/webview/c_webview.h new file mode 100644 index 00000000..adec25d7 --- /dev/null +++ b/gb.gtk3/src/webview/c_webview.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + c_webview.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WEBVIEW_H +#define __C_WEBVIEW_H + +#include <webkit2/webkit2.h> +#include "main.h" + +typedef + struct { + GTK_CONTROL control; + GtkWidget *widget; + WebKitWebContext *context; + WebKitBackForwardListItem *item; + GTK_PICTURE icon; + void *new_view; + char *link; + char *cb_result; + char *language; + unsigned error : 1; + unsigned accept_next : 1; + unsigned got_load_event : 1; + unsigned cb_running : 1; + unsigned cb_error : 1; + } + CWEBVIEW; + +#ifndef __C_WEBVIEW_C +extern GB_DESC WebViewDesc[]; +extern GB_DESC WebViewHistoryDesc[]; +extern GB_DESC WebViewHistoryItemDesc[]; +extern GB_DESC WebViewCookiesDesc[]; +#else + +#define THIS ((CWEBVIEW *)_object) +#define WIDGET (WEBKIT_WEB_VIEW(THIS->widget)) + +//#define CGLAREA_PROPERTIES QT_WIDGET_PROPERTIES + +#endif /* __CGLAREA_CPP */ + +#endif diff --git a/gb.gtk3/src/webview/gb.gtk3.webview.component b/gb.gtk3/src/webview/gb.gtk3.webview.component new file mode 100644 index 00000000..3b2939a4 --- /dev/null +++ b/gb.gtk3/src/webview/gb.gtk3.webview.component @@ -0,0 +1,5 @@ +[Component] +Author=Benoît Minisini +Require=gb.gtk3 +Type=Form +State=Stable diff --git a/gb.gtk3/src/webview/main.c b/gb.gtk3/src/webview/main.c new file mode 100644 index 00000000..f0aad239 --- /dev/null +++ b/gb.gtk3/src/webview/main.c @@ -0,0 +1,54 @@ +/*************************************************************************** + + main.c + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_websettings.h" +#include "c_webview.h" + +GB_INTERFACE GB EXPORT; +GTK_INTERFACE GTK; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + WebSettingsFontsDesc, + WebSettingsDesc, + WebViewSettingsDesc, + WebViewHistoryItemDesc, + WebViewHistoryDesc, + WebViewCookiesDesc, + WebViewDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.gtk3", GTK_INTERFACE_VERSION, >K); + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.gtk3/src/webview/main.h b/gb.gtk3/src/webview/main.h new file mode 100644 index 00000000..f115d546 --- /dev/null +++ b/gb.gtk3/src/webview/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.gtk.h" +#include <gtk/gtk.h> + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern GTK_INTERFACE GTK; +#endif + +#endif diff --git a/gb.gtk3/src/widgets.h b/gb.gtk3/src/widgets.h new file mode 120000 index 00000000..f7ef9769 --- /dev/null +++ b/gb.gtk3/src/widgets.h @@ -0,0 +1 @@ +../../gb.gtk/src/widgets.h \ No newline at end of file diff --git a/gb.gtk3/src/x11/Makefile.am b/gb.gtk3/src/x11/Makefile.am new file mode 100644 index 00000000..4784fdb9 --- /dev/null +++ b/gb.gtk3/src/x11/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gtk3.x11 +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gtk3.x11.la + +gb_gtk3_x11_la_LIBADD = @GTK3X11_LIB@ +gb_gtk3_x11_la_LDFLAGS = -module @LD_FLAGS@ @GTK3X11_LDFLAGS@ +gb_gtk3_x11_la_CFLAGS = @GTK3X11_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_gtk3_x11_la_SOURCES = \ + main.h main.c \ + x11.h x11.c diff --git a/gb.gtk3/src/x11/gb.gtk3.x11.component b/gb.gtk3/src/x11/gb.gtk3.x11.component new file mode 100644 index 00000000..58584e3c --- /dev/null +++ b/gb.gtk3/src/x11/gb.gtk3.x11.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.qt5 +Hidden=True + + diff --git a/gb.gtk3/src/x11/main.c b/gb.gtk3/src/x11/main.c new file mode 100644 index 00000000..801dc1b6 --- /dev/null +++ b/gb.gtk3/src/x11/main.c @@ -0,0 +1,168 @@ +/*************************************************************************** + + main.c + + (c) Benoît Minisini <gambas@users.sourceforge.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "x11.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +//------------------------------------------------------------------------- + +static void platform_init(void) +{ + char *env; + + env = getenv("GB_X11_INIT_THREADS"); + if (env && atoi(env)) + XInitThreads(); + + X11_init(gdk_x11_display_get_xdisplay(gdk_display_get_default()), gdk_x11_get_default_root_xwindow()); +} + +static void platform_exit(void) +{ + X11_exit(); +} + +static GtkWidget *platform_create_plug(Window id) +{ + return gtk_plug_new(id); +} + +//------------------------------------------------------------------------- + +static long window_get_id(GdkWindow *window) +{ + return window ? GDK_WINDOW_XID(window) : 0; +} + +//------------------------------------------------------------------------- + +static bool desktop_has_system_tray() +{ + return X11_get_system_tray() != 0; +} + +static void desktop_show_tray_icon(GtkStatusIcon *icon, int width, int height) +{ + XSizeHints hints; + hints.flags = PMinSize; + hints.min_width = width; + hints.min_height = height; + XSetWMNormalHints(gdk_x11_display_get_xdisplay(gdk_display_get_default()), gtk_status_icon_get_x11_window_id(icon), &hints); +} + +//------------------------------------------------------------------------- + +static GdkFilterReturn x11_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) +{ + ((X11_EVENT_FILTER)data)((XEvent *)xevent); + return GDK_FILTER_CONTINUE; +} + +static void x11_set_event_filter(X11_EVENT_FILTER filter) +{ + static X11_EVENT_FILTER save_filter = NULL; + static GdkEventMask save_mask = (GdkEventMask)0; + + if (save_filter) + { + gdk_window_remove_filter(NULL, (GdkFilterFunc)x11_event_filter, (gpointer)save_filter); + gdk_window_set_events(gdk_get_default_root_window(), save_mask); + } + + if (filter) + { + save_mask = gdk_window_get_events(gdk_get_default_root_window()); + gdk_window_set_events(gdk_get_default_root_window(), (GdkEventMask)(save_mask | GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK)); + gdk_window_add_filter(NULL, (GdkFilterFunc)x11_event_filter, (gpointer)filter); + } + + save_filter = filter; +} + +//------------------------------------------------------------------------- + +void *GB_GTK3_X11_1[] EXPORT = { + + (void *)GTK_PLATFORM_INTERFACE_VERSION, + + (void *)platform_init, + (void *)platform_exit, + (void *)platform_create_plug, + + (void *)window_get_id, + + (void *)desktop_has_system_tray, + (void *)desktop_show_tray_icon, + + /*(void *)release_grab, + (void *)unrelease_grab, + (void *)get_last_key_code, + + (void *)desktop_get_resolution_x, + (void *)desktop_get_resolution_y, + (void *)desktop_screenshot, + + (void *)window_get_virtual_desktop, + (void *)window_set_virtual_desktop, + (void *)window_remap, + (void *)window_set_properties, + (void *)window_set_user_time, + (void *)window_set_transient_for,*/ + + NULL + }; + + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)gdk_x11_display_get_xdisplay(gdk_display_get_default()); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)gdk_x11_get_default_root_xwindow(); + return TRUE; + } + else if (!strcasecmp(key, "SET_EVENT_FILTER")) + { + *value = (void *)x11_set_event_filter; + return TRUE; + } + else + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.gtk3/src/x11/main.h b/gb.gtk3/src/x11/main.h new file mode 100644 index 00000000..c269bed4 --- /dev/null +++ b/gb.gtk3/src/x11/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini <benoit.minisini@gambas-basic.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.gtk.platform.h" +#include <gdk/gdkx.h> +#include <gtk/gtkx.h> + +typedef + void (*X11_EVENT_FILTER)(XEvent *); + +#ifndef __MAIN_C +extern "C" const GB_INTERFACE *GB_PTR; +#endif +#define GB (*GB_PTR) + +#endif diff --git a/gb.gtk3/src/x11/x11.c b/gb.gtk3/src/x11/x11.c new file mode 120000 index 00000000..68e60e76 --- /dev/null +++ b/gb.gtk3/src/x11/x11.c @@ -0,0 +1 @@ +../../../gb.qt4/src/x11.c \ No newline at end of file diff --git a/gb.gtk3/src/x11/x11.h b/gb.gtk3/src/x11/x11.h new file mode 120000 index 00000000..966df48f --- /dev/null +++ b/gb.gtk3/src/x11/x11.h @@ -0,0 +1 @@ +../../../gb.qt4/src/x11.h \ No newline at end of file diff --git a/gb.httpd/AUTHORS b/gb.httpd/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/COPYING b/gb.httpd/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.httpd/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.httpd/ChangeLog b/gb.httpd/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/INSTALL b/gb.httpd/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.httpd/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.httpd/Makefile.am b/gb.httpd/Makefile.am new file mode 100644 index 00000000..4cac8ec7 --- /dev/null +++ b/gb.httpd/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @HTTPD_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.httpd/NEWS b/gb.httpd/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/README b/gb.httpd/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/acinclude.m4 b/gb.httpd/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.httpd/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.httpd/component.am b/gb.httpd/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.httpd/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.httpd/configure.ac b/gb.httpd/configure.ac new file mode 100644 index 00000000..07282dbd --- /dev/null +++ b/gb.httpd/configure.ac @@ -0,0 +1,150 @@ +dnl ---- configure.ac for gb.httpd + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-httpd],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.httpd) +LT_INIT + +V_CCOPT="-O" +if test "$GCC" = yes ; then + AC_MSG_CHECKING(gcc version) + AC_CACHE_VAL(ac_cv_lbl_gcc_vers, + ac_cv_lbl_gcc_vers=`$CC -dumpversion 2>&1 | \ + sed -e 's/\..*//'`) + AC_MSG_RESULT($ac_cv_lbl_gcc_vers) + if test "$ac_cv_lbl_gcc_vers" -gt 1 ; then + V_CCOPT="-O2" + fi +fi +if test -f .devel ; then + V_CCOPT="-g $V_CCOPT -Wall -Wmissing-prototypes -Wstrict-prototypes" +fi + +dnl +dnl maybe this should be a loop +dnl +AC_MSG_CHECKING(how to link static binaries) +AC_CACHE_VAL(ac_cv_lbl_static_flag, + ac_cv_lbl_static_flag=unknown + echo 'int main() {}' > conftest.c + if test "$GCC" != yes ; then + trial_flag="-Bstatic" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + if test "$ac_cv_lbl_static_flag" = unknown ; then + trial_flag="-static" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + rm conftest.c) +AC_MSG_RESULT($ac_cv_lbl_static_flag) +if test "$ac_cv_lbl_static_flag" != unknown ; then + V_STATICFLAG="$ac_cv_lbl_static_flag" +fi + +AC_MSG_CHECKING(for __progname) +AC_CACHE_VAL(ac_cv_extern__progname, + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdio.h>]], + [[extern char *__progname; + puts(__progname)]])],[ac_cv_extern__progname=yes],[ac_cv_extern__progname=no])) +if test $ac_cv_extern__progname = yes ; then + AC_DEFINE([HAVE__PROGNAME], [], [have __progname]) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AC_CHECK_HEADERS(fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/devpoll.h sys/event.h osreldate.h) +AC_CHECK_HEADERS_ONCE([sys/time.h]) + +AC_HEADER_DIRENT + +dnl d="/usr/local/v6/lib" +dnl AC_MSG_CHECKING(for $d) +dnl if test -d $d; then +dnl AC_MSG_RESULT(yes (Adding -L$d to LDFLAGS)) +dnl LDFLAGS="$LDFLAGS -L$d" +dnl else +dnl AC_MSG_RESULT(no) +dnl fi + +dnl +dnl Most operating systems have gethostbyname() in the default searched +dnl libraries (i.e. libc): +dnl +V_NETLIBS="" +AC_CHECK_FUNC(gethostbyname, , + # Some OSes (eg. Solaris) place it in libnsl: + GB_AC_LBL_CHECK_LIB(nsl, gethostbyname, + V_NETLIBS="-lnsl $V_NETLIBS", + # Some strange OSes (SINIX) have it in libsocket: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, + V_NETLIBS="-lsocket $V_NETLIBS", + # Unfortunately libsocket sometimes depends on libnsl. + # AC_CHECK_LIB's API is essentially broken so the + # following ugliness is necessary: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, + V_NETLIBS="-lsocket -lnsl $V_NETLIBS", + AC_CHECK_LIB(resolv, gethostbyname, + V_NETLIBS="-lresolv $V_NETLIBS"), + -lnsl)))) +AC_CHECK_FUNC(socket, , + AC_CHECK_LIB(socket, socket, + V_NETLIBS="-lsocket $V_NETLIBS", + GB_AC_LBL_CHECK_LIB(socket, socket, + V_NETLIBS="-lsocket -lnsl $V_NETLIBS", , -lnsl))) + +AC_CHECK_LIB(inet6, main) + +AC_CHECK_FUNC(crypt, , AC_CHECK_LIB(crypt, crypt)) +AC_CHECK_FUNC(hstrerror, , + AC_CHECK_LIB(resolv, hstrerror, V_NETLIBS="-lresolv $V_NETLIBS")) + +AC_REPLACE_FUNCS(strerror) +AC_CHECK_FUNCS(waitpid vsnprintf daemon setsid setlogin getaddrinfo getnameinfo gai_strerror kqueue atoll) +AC_FUNC_MMAP + +case "$target_os" in +solaris*) + dnl Solaris's select() is a bad wrapper routine. + AC_CHECK_FUNCS(poll) + ;; +*) + AC_CHECK_FUNCS(select poll) + ;; +esac + +GB_AC_ACME_TM_GMTOFF +GB_AC_ACME_INT64T +GB_AC_ACME_SOCKLENT + +AC_PROG_MAKE_SET +AC_PROG_INSTALL + +AC_SUBST(DEFS) +AC_SUBST(V_CCOPT) +AC_SUBST(V_STATICFLAG) +AC_SUBST(V_NETLIBS) + +GB_COMPONENT( + httpd, + HTTPD, + gb.httpd, + [src], + [], + [], + [], + []) + +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT +GB_PRINT_MESSAGES diff --git a/gb.httpd/gambas.h b/gb.httpd/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.httpd/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.httpd/gb_common.h b/gb.httpd/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.httpd/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.httpd/m4 b/gb.httpd/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.httpd/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.httpd/reconf b/gb.httpd/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.httpd/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.httpd/src/Makefile.am b/gb.httpd/src/Makefile.am new file mode 100644 index 00000000..baa21f56 --- /dev/null +++ b/gb.httpd/src/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.httpd +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.httpd.la + +gb_httpd_la_LIBADD = @HTTPD_LIB@ +gb_httpd_la_LDFLAGS = -module @LD_FLAGS@ @HTTPD_LDFLAGS@ +gb_httpd_la_CPPFLAGS = @HTTPD_INC@ + +gb_httpd_la_SOURCES = main.c main.h \ + fdwatch.h fdwatch.c \ + libhttpd.h libhttpd.c \ + match.h match.c \ + mime_encodings.h \ + mime_types.h \ + tdate_parse.h tdate_parse.c \ + thttpd.h thttpd.c \ + timers.h timers.c \ + version.h + diff --git a/gb.httpd/src/fdwatch.c b/gb.httpd/src/fdwatch.c new file mode 100644 index 00000000..f8f4a2fd --- /dev/null +++ b/gb.httpd/src/fdwatch.c @@ -0,0 +1,840 @@ +/* fdwatch.c - fd watcher routines, either select() or poll() +** +** Copyright (c) 1999,2000 by Jef Poskanzer <jef@mail.acme.com>. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include "config.h" + +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <syslog.h> +#include <fcntl.h> + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifdef HAVE_POLL_H +#include <poll.h> +#else /* HAVE_POLL_H */ +#ifdef HAVE_SYS_POLL_H +#include <sys/poll.h> +#endif /* HAVE_SYS_POLL_H */ +#endif /* HAVE_POLL_H */ + +#ifdef HAVE_SYS_DEVPOLL_H +#include <sys/devpoll.h> +#ifndef HAVE_DEVPOLL +#define HAVE_DEVPOLL +#endif /* !HAVE_DEVPOLL */ +#endif /* HAVE_SYS_DEVPOLL_H */ + +#ifdef HAVE_SYS_EVENT_H +#include <sys/event.h> +#endif /* HAVE_SYS_EVENT_H */ + +#include "fdwatch.h" + +#ifdef HAVE_SELECT +#ifndef FD_SET +#define NFDBITS 32 +#define FD_SETSIZE 32 +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) bzero((char*)(p), sizeof(*(p))) +#endif /* !FD_SET */ +#endif /* HAVE_SELECT */ + +static int nfiles; +static long nwatches; +static int *fd_rw; +static void **fd_data; +static int nreturned, next_ridx; + +#ifdef HAVE_KQUEUE + +#define WHICH "kevent" +#define INIT( nfiles ) kqueue_init( nfiles ) +#define ADD_FD( fd, rw ) kqueue_add_fd( fd, rw ) +#define DEL_FD( fd ) kqueue_del_fd( fd ) +#define WATCH( timeout_msecs ) kqueue_watch( timeout_msecs ) +#define CHECK_FD( fd ) kqueue_check_fd( fd ) +#define GET_FD( ridx ) kqueue_get_fd( ridx ) + +static int kqueue_init(int nfiles); +static void kqueue_add_fd(int fd, int rw); +static void kqueue_del_fd(int fd); +static int kqueue_watch(long timeout_msecs); +static int kqueue_check_fd(int fd); +static int kqueue_get_fd(int ridx); + +#else /* HAVE_KQUEUE */ +#ifdef HAVE_DEVPOLL + +#define WHICH "devpoll" +#define INIT( nfiles ) devpoll_init( nfiles ) +#define ADD_FD( fd, rw ) devpoll_add_fd( fd, rw ) +#define DEL_FD( fd ) devpoll_del_fd( fd ) +#define WATCH( timeout_msecs ) devpoll_watch( timeout_msecs ) +#define CHECK_FD( fd ) devpoll_check_fd( fd ) +#define GET_FD( ridx ) devpoll_get_fd( ridx ) + +static int devpoll_init(int nfiles); +static void devpoll_add_fd(int fd, int rw); +static void devpoll_del_fd(int fd); +static int devpoll_watch(long timeout_msecs); +static int devpoll_check_fd(int fd); +static int devpoll_get_fd(int ridx); + +#else /* HAVE_DEVPOLL */ +#ifdef HAVE_POLL + +#define WHICH "poll" +#define INIT( nfiles ) poll_init( nfiles ) +#define ADD_FD( fd, rw ) poll_add_fd( fd, rw ) +#define DEL_FD( fd ) poll_del_fd( fd ) +#define WATCH( timeout_msecs ) poll_watch( timeout_msecs ) +#define CHECK_FD( fd ) poll_check_fd( fd ) +#define GET_FD( ridx ) poll_get_fd( ridx ) + +static int poll_init(int nfiles); +static void poll_add_fd(int fd, int rw); +static void poll_del_fd(int fd); +static int poll_watch(long timeout_msecs); +static int poll_check_fd(int fd); +static int poll_get_fd(int ridx); + +#else /* HAVE_POLL */ +#ifdef HAVE_SELECT + +#define WHICH "select" +#define INIT( nfiles ) select_init( nfiles ) +#define ADD_FD( fd, rw ) select_add_fd( fd, rw ) +#define DEL_FD( fd ) select_del_fd( fd ) +#define WATCH( timeout_msecs ) select_watch( timeout_msecs ) +#define CHECK_FD( fd ) select_check_fd( fd ) +#define GET_FD( ridx ) select_get_fd( ridx ) + +static int select_init(int nfiles); +static void select_add_fd(int fd, int rw); +static void select_del_fd(int fd); +static int select_watch(long timeout_msecs); +static int select_check_fd(int fd); +static int select_get_fd(int ridx); + +#endif /* HAVE_SELECT */ +#endif /* HAVE_POLL */ +#endif /* HAVE_DEVPOLL */ +#endif /* HAVE_KQUEUE */ + + +/* Routines. */ + +/* Figure out how many file descriptors the system allows, and +** initialize the fdwatch data structures. Returns -1 on failure. +*/ +int fdwatch_get_nfiles(void) +{ + int i; +#ifdef RLIMIT_NOFILE + struct rlimit rl; +#endif /* RLIMIT_NOFILE */ + + /* Figure out how many fd's we can have. */ + nfiles = getdtablesize(); +#ifdef RLIMIT_NOFILE + /* If we have getrlimit(), use that, and attempt to raise the limit. */ + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) + { + nfiles = rl.rlim_cur; + if (rl.rlim_max == RLIM_INFINITY) + rl.rlim_cur = 8192; /* arbitrary */ + else if (rl.rlim_max > rl.rlim_cur) + rl.rlim_cur = rl.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rl) == 0) + nfiles = rl.rlim_cur; + } +#endif /* RLIMIT_NOFILE */ + +#if defined(HAVE_SELECT) && ! ( defined(HAVE_POLL) || defined(HAVE_DEVPOLL) || defined(HAVE_KQUEUE) ) + /* If we use select(), then we must limit ourselves to FD_SETSIZE. */ + nfiles = MIN(nfiles, FD_SETSIZE); +#endif /* HAVE_SELECT && ! ( HAVE_POLL || HAVE_DEVPOLL || HAVE_KQUEUE ) */ + + /* Initialize the fdwatch data structures. */ + nwatches = 0; + fd_rw = (int *) malloc(sizeof(int) * nfiles); + fd_data = (void **) malloc(sizeof(void *) * nfiles); + if (fd_rw == (int *) 0 || fd_data == (void **) 0) + return -1; + for (i = 0; i < nfiles; ++i) + fd_rw[i] = -1; + if (INIT(nfiles) == -1) + return -1; + + return nfiles; +} + + +/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ +void fdwatch_add_fd(int fd, void *client_data, int rw) +{ + if (fd < 0 || fd >= nfiles || fd_rw[fd] != -1) + { + syslog(LOG_ERR, "bad fd (%d) passed to fdwatch_add_fd!", fd); + return; + } + ADD_FD(fd, rw); + fd_rw[fd] = rw; + fd_data[fd] = client_data; +} + + +/* Remove a descriptor from the watch list. */ +void fdwatch_del_fd(int fd) +{ + if (fd < 0 || fd >= nfiles || fd_rw[fd] == -1) + { + syslog(LOG_ERR, "bad fd (%d) passed to fdwatch_del_fd!", fd); + return; + } + DEL_FD(fd); + fd_rw[fd] = -1; + fd_data[fd] = (void *) 0; +} + +/* Do the watch. Return value is the number of descriptors that are ready, +** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means +** wait indefinitely. +*/ +int fdwatch(long timeout_msecs) +{ + ++nwatches; + nreturned = WATCH(timeout_msecs); + next_ridx = 0; + return nreturned; +} + + +/* Check if a descriptor was ready. */ +int fdwatch_check_fd(int fd) +{ + if (fd < 0 || fd >= nfiles || fd_rw[fd] == -1) + { + syslog(LOG_ERR, "bad fd (%d) passed to fdwatch_check_fd!", fd); + return 0; + } + return CHECK_FD(fd); +} + + +void *fdwatch_get_next_client_data(void) +{ + int fd; + + if (next_ridx >= nreturned) + return (void *) -1; + fd = GET_FD(next_ridx++); + if (fd < 0 || fd >= nfiles) + return (void *) 0; + return fd_data[fd]; +} + + +/* Generate debugging statistics syslog message. */ +void fdwatch_logstats(long secs) +{ + if (secs > 0) + syslog(LOG_INFO, " fdwatch - %ld %ss (%g/sec)", + nwatches, WHICH, (float) nwatches / secs); + nwatches = 0; +} + + +#ifdef HAVE_KQUEUE + +static int maxkqevents; +static struct kevent *kqevents; +static int nkqevents; +static struct kevent *kqrevents; +static int *kqrfdidx; +static int kq; + + +static int kqueue_init(int nfiles) +{ + kq = kqueue(); + if (kq == -1) + return -1; + maxkqevents = nfiles * 2; + kqevents = (struct kevent *) malloc(sizeof(struct kevent) * maxkqevents); + kqrevents = (struct kevent *) malloc(sizeof(struct kevent) * nfiles); + kqrfdidx = (int *) malloc(sizeof(int) * nfiles); + if (kqevents == (struct kevent *) 0 || kqrevents == (struct kevent *) 0 || + kqrfdidx == (int *) 0) + return -1; + (void) memset(kqevents, 0, sizeof(struct kevent) * maxkqevents); + (void) memset(kqrfdidx, 0, sizeof(int) * nfiles); + return 0; +} + + +static void kqueue_add_fd(int fd, int rw) +{ + if (nkqevents >= maxkqevents) + { + syslog(LOG_ERR, "too many kqevents in kqueue_add_fd!"); + return; + } + kqevents[nkqevents].ident = fd; + kqevents[nkqevents].flags = EV_ADD; + switch (rw) + { + case FDW_READ: + kqevents[nkqevents].filter = EVFILT_READ; + break; + case FDW_WRITE: + kqevents[nkqevents].filter = EVFILT_WRITE; + break; + default: + break; + } + ++nkqevents; +} + + +static void kqueue_del_fd(int fd) +{ + if (nkqevents >= maxkqevents) + { + syslog(LOG_ERR, "too many kqevents in kqueue_del_fd!"); + return; + } + kqevents[nkqevents].ident = fd; + kqevents[nkqevents].flags = EV_DELETE; + switch (fd_rw[fd]) + { + case FDW_READ: + kqevents[nkqevents].filter = EVFILT_READ; + break; + case FDW_WRITE: + kqevents[nkqevents].filter = EVFILT_WRITE; + break; + } + ++nkqevents; +} + + +static int kqueue_watch(long timeout_msecs) +{ + int i, r; + + if (timeout_msecs == INFTIM) + r = + kevent(kq, kqevents, nkqevents, kqrevents, nfiles, + (struct timespec *) 0); + else + { + struct timespec ts; + ts.tv_sec = timeout_msecs / 1000L; + ts.tv_nsec = (timeout_msecs % 1000L) * 1000000L; + r = kevent(kq, kqevents, nkqevents, kqrevents, nfiles, &ts); + } + nkqevents = 0; + if (r == -1) + return -1; + + for (i = 0; i < r; ++i) + kqrfdidx[kqrevents[i].ident] = i; + + return r; +} + + +static int kqueue_check_fd(int fd) +{ + int ridx = kqrfdidx[fd]; + + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in kqueue_check_fd!", ridx); + return 0; + } + if (ridx >= nreturned) + return 0; + if (kqrevents[ridx].ident != fd) + return 0; + if (kqrevents[ridx].flags & EV_ERROR) + return 0; + switch (fd_rw[fd]) + { + case FDW_READ: + return kqrevents[ridx].filter == EVFILT_READ; + case FDW_WRITE: + return kqrevents[ridx].filter == EVFILT_WRITE; + default: + return 0; + } +} + + +static int kqueue_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in kqueue_get_fd!", ridx); + return -1; + } + return kqrevents[ridx].ident; +} + +#else /* HAVE_KQUEUE */ + + +#ifdef HAVE_DEVPOLL + +static int maxdpevents; +static struct pollfd *dpevents; +static int ndpevents; +static struct pollfd *dprevents; +static int *dp_rfdidx; +static int dp; + + +static int devpoll_init(int nfiles) +{ + dp = open("/dev/poll", O_RDWR); + if (dp == -1) + return -1; + (void) fcntl(dp, F_SETFD, 1); + maxdpevents = nfiles * 2; + dpevents = (struct pollfd *) malloc(sizeof(struct pollfd) * maxdpevents); + dprevents = (struct pollfd *) malloc(sizeof(struct pollfd) * nfiles); + dp_rfdidx = (int *) malloc(sizeof(int) * nfiles); + if (dpevents == (struct pollfd *) 0 || dprevents == (struct pollfd *) 0 || + dp_rfdidx == (int *) 0) + return -1; + (void) memset(dp_rfdidx, 0, sizeof(int) * nfiles); + return 0; +} + + +static void devpoll_add_fd(int fd, int rw) +{ + if (ndpevents >= maxdpevents) + { + syslog(LOG_ERR, "too many fds in devpoll_add_fd!"); + return; + } + dpevents[ndpevents].fd = fd; + switch (rw) + { + case FDW_READ: + dpevents[ndpevents].events = POLLIN; + break; + case FDW_WRITE: + dpevents[ndpevents].events = POLLOUT; + break; + default: + break; + } + ++ndpevents; +} + + +static void devpoll_del_fd(int fd) +{ + if (ndpevents >= maxdpevents) + { + syslog(LOG_ERR, "too many fds in devpoll_del_fd!"); + return; + } + dpevents[ndpevents].fd = fd; + dpevents[ndpevents].events = POLLREMOVE; + ++ndpevents; +} + + +static int devpoll_watch(long timeout_msecs) +{ + int i, r; + struct dvpoll dvp; + + r = sizeof(struct pollfd) * ndpevents; + if (r > 0 && write(dp, dpevents, r) != r) + return -1; + + ndpevents = 0; + dvp.dp_fds = dprevents; + dvp.dp_nfds = nfiles; + dvp.dp_timeout = (int) timeout_msecs; + + r = ioctl(dp, DP_POLL, &dvp); + if (r == -1) + return -1; + + for (i = 0; i < r; ++i) + dp_rfdidx[dprevents[i].fd] = i; + + return r; +} + + +static int devpoll_check_fd(int fd) +{ + int ridx = dp_rfdidx[fd]; + + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in devpoll_check_fd!", ridx); + return 0; + } + if (ridx >= nreturned) + return 0; + if (dprevents[ridx].fd != fd) + return 0; + if (dprevents[ridx].revents & POLLERR) + return 0; + switch (fd_rw[fd]) + { + case FDW_READ: + return dprevents[ridx].revents & (POLLIN | POLLHUP | POLLNVAL); + case FDW_WRITE: + return dprevents[ridx].revents & (POLLOUT | POLLHUP | POLLNVAL); + default: + return 0; + } +} + + +static int devpoll_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in devpoll_get_fd!", ridx); + return -1; + } + return dprevents[ridx].fd; +} + + +#else /* HAVE_DEVPOLL */ + + +#ifdef HAVE_POLL + +static struct pollfd *pollfds; +static int npoll_fds; +static int *poll_fdidx; +static int *poll_rfdidx; + + +static int poll_init(int nfiles) +{ + int i; + + pollfds = (struct pollfd *) malloc(sizeof(struct pollfd) * nfiles); + poll_fdidx = (int *) malloc(sizeof(int) * nfiles); + poll_rfdidx = (int *) malloc(sizeof(int) * nfiles); + if (pollfds == (struct pollfd *) 0 || poll_fdidx == (int *) 0 || + poll_rfdidx == (int *) 0) + return -1; + for (i = 0; i < nfiles; ++i) + pollfds[i].fd = poll_fdidx[i] = -1; + return 0; +} + + +static void poll_add_fd(int fd, int rw) +{ + if (npoll_fds >= nfiles) + { + syslog(LOG_ERR, "too many fds in poll_add_fd!"); + return; + } + pollfds[npoll_fds].fd = fd; + switch (rw) + { + case FDW_READ: + pollfds[npoll_fds].events = POLLIN; + break; + case FDW_WRITE: + pollfds[npoll_fds].events = POLLOUT; + break; + default: + break; + } + poll_fdidx[fd] = npoll_fds; + ++npoll_fds; +} + + +static void poll_del_fd(int fd) +{ + int idx = poll_fdidx[fd]; + + if (idx < 0 || idx >= nfiles) + { + syslog(LOG_ERR, "bad idx (%d) in poll_del_fd!", idx); + return; + } + --npoll_fds; + pollfds[idx] = pollfds[npoll_fds]; + poll_fdidx[pollfds[idx].fd] = idx; + pollfds[npoll_fds].fd = -1; + poll_fdidx[fd] = -1; +} + + +static int poll_watch(long timeout_msecs) +{ + int r, ridx, i; + + r = poll(pollfds, npoll_fds, (int) timeout_msecs); + if (r <= 0) + return r; + + ridx = 0; + for (i = 0; i < npoll_fds; ++i) + if (pollfds[i].revents & + (POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL)) + { + poll_rfdidx[ridx++] = pollfds[i].fd; + if (ridx == r) + break; + } + + return ridx; /* should be equal to r */ +} + + +static int poll_check_fd(int fd) +{ + int fdidx = poll_fdidx[fd]; + + if (fdidx < 0 || fdidx >= nfiles) + { + syslog(LOG_ERR, "bad fdidx (%d) in poll_check_fd!", fdidx); + return 0; + } + if (pollfds[fdidx].revents & POLLERR) + return 0; + switch (fd_rw[fd]) + { + case FDW_READ: + return pollfds[fdidx].revents & (POLLIN | POLLHUP | POLLNVAL); + case FDW_WRITE: + return pollfds[fdidx].revents & (POLLOUT | POLLHUP | POLLNVAL); + default: + return 0; + } +} + + +static int poll_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in poll_get_fd!", ridx); + return -1; + } + return poll_rfdidx[ridx]; +} + +#else /* HAVE_POLL */ + + +#ifdef HAVE_SELECT + +static fd_set master_rfdset; +static fd_set master_wfdset; +static fd_set working_rfdset; +static fd_set working_wfdset; +static int *select_fds; +static int *select_fdidx; +static int *select_rfdidx; +static int nselect_fds; +static int maxfd; +static int maxfd_changed; + + +static int select_init(int nfiles) +{ + int i; + + FD_ZERO(&master_rfdset); + FD_ZERO(&master_wfdset); + select_fds = (int *) malloc(sizeof(int) * nfiles); + select_fdidx = (int *) malloc(sizeof(int) * nfiles); + select_rfdidx = (int *) malloc(sizeof(int) * nfiles); + if (select_fds == (int *) 0 || select_fdidx == (int *) 0 || + select_rfdidx == (int *) 0) + return -1; + nselect_fds = 0; + maxfd = -1; + maxfd_changed = 0; + for (i = 0; i < nfiles; ++i) + select_fds[i] = select_fdidx[i] = -1; + return 0; +} + + +static void select_add_fd(int fd, int rw) +{ + if (nselect_fds >= nfiles) + { + syslog(LOG_ERR, "too many fds in select_add_fd!"); + return; + } + select_fds[nselect_fds] = fd; + switch (rw) + { + case FDW_READ: + FD_SET(fd, &master_rfdset); + break; + case FDW_WRITE: + FD_SET(fd, &master_wfdset); + break; + default: + break; + } + if (fd > maxfd) + maxfd = fd; + select_fdidx[fd] = nselect_fds; + ++nselect_fds; +} + + +static void select_del_fd(int fd) +{ + int idx = select_fdidx[fd]; + + if (idx < 0 || idx >= nfiles) + { + syslog(LOG_ERR, "bad idx (%d) in select_del_fd!", idx); + return; + } + + --nselect_fds; + select_fds[idx] = select_fds[nselect_fds]; + select_fdidx[select_fds[idx]] = idx; + select_fds[nselect_fds] = -1; + select_fdidx[fd] = -1; + + FD_CLR(fd, &master_rfdset); + FD_CLR(fd, &master_wfdset); + + if (fd >= maxfd) + maxfd_changed = 1; +} + + +static int select_get_maxfd(void) +{ + if (maxfd_changed) + { + int i; + maxfd = -1; + for (i = 0; i < nselect_fds; ++i) + if (select_fds[i] > maxfd) + maxfd = select_fds[i]; + maxfd_changed = 0; + } + return maxfd; +} + + +static int select_watch(long timeout_msecs) +{ + int mfd; + int r, idx, ridx; + + working_rfdset = master_rfdset; + working_wfdset = master_wfdset; + mfd = select_get_maxfd(); + if (timeout_msecs == INFTIM) + r = select(mfd + 1, &working_rfdset, &working_wfdset, (fd_set *) 0, + (struct timeval *) 0); + else + { + struct timeval timeout; + timeout.tv_sec = timeout_msecs / 1000L; + timeout.tv_usec = (timeout_msecs % 1000L) * 1000L; + r = + select(mfd + 1, &working_rfdset, &working_wfdset, (fd_set *) 0, + &timeout); + } + if (r <= 0) + return r; + + ridx = 0; + for (idx = 0; idx < nselect_fds; ++idx) + if (select_check_fd(select_fds[idx])) + { + select_rfdidx[ridx++] = select_fds[idx]; + if (ridx == r) + break; + } + + return ridx; /* should be equal to r */ +} + + +static int select_check_fd(int fd) +{ + switch (fd_rw[fd]) + { + case FDW_READ: + return FD_ISSET(fd, &working_rfdset); + case FDW_WRITE: + return FD_ISSET(fd, &working_wfdset); + default: + return 0; + } +} + + +static int select_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in select_get_fd!", ridx); + return -1; + } + return select_rfdidx[ridx]; +} + +#endif /* HAVE_SELECT */ + +#endif /* HAVE_POLL */ + +#endif /* HAVE_DEVPOLL */ + +#endif /* HAVE_KQUEUE */ diff --git a/gb.httpd/src/fdwatch.h b/gb.httpd/src/fdwatch.h new file mode 100644 index 00000000..2ed52713 --- /dev/null +++ b/gb.httpd/src/fdwatch.h @@ -0,0 +1,85 @@ +/* fdwatch.h - header file for fdwatch package +** +** This package abstracts the use of the select()/poll()/kqueue() +** system calls. The basic function of these calls is to watch a set +** of file descriptors for activity. select() originated in the BSD world, +** while poll() came from SysV land, and their interfaces are somewhat +** different. fdwatch lets you write your code to a single interface, +** with the portability differences hidden inside the package. +** +** Usage is fairly simple. Call fdwatch_get_nfiles() to initialize +** the package and find out how many fine descriptors are available. +** Then each time through your main loop, call fdwatch_clear(), then +** fdwatch_add_fd() for each of the descriptors you want to watch, +** then call fdwatch() to actually perform the watch. After it returns +** you can check which descriptors are ready via fdwatch_check_fd(). +** +** If your descriptor set hasn't changed from the last time through +** the loop, you can skip calling fdwatch_clear() and fdwatch_add_fd() +** to save a little CPU time. +** +** +** Copyright (c) 1999 by Jef Poskanzer <jef@mail.acme.com>. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _FDWATCH_H_ +#define _FDWATCH_H_ + +#define FDW_READ 0 +#define FDW_WRITE 1 + +#ifndef INFTIM +#define INFTIM -1 +#endif /* INFTIM */ + +/* Figure out how many file descriptors the system allows, and +** initialize the fdwatch data structures. Returns -1 on failure. +*/ +extern int fdwatch_get_nfiles(void); + +/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ +extern void fdwatch_add_fd(int fd, void *client_data, int rw); + +/* Delete a descriptor from the watch list. */ +extern void fdwatch_del_fd(int fd); + +/* Do the watch. Return value is the number of descriptors that are ready, +** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means +** wait indefinitely. +*/ +extern int fdwatch(long timeout_msecs); + +/* Check if a descriptor was ready. */ +extern int fdwatch_check_fd(int fd); + +/* Get the client data for the next returned event. Returns -1 when there +** are no more events. +*/ +extern void *fdwatch_get_next_client_data(void); + +/* Generate debugging statistics syslog message. */ +extern void fdwatch_logstats(long secs); + +#endif /* _FDWATCH_H_ */ diff --git a/gb.httpd/src/gb.httpd.component b/gb.httpd/src/gb.httpd.component new file mode 100644 index 00000000..2ed93b42 --- /dev/null +++ b/gb.httpd/src/gb.httpd.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Stable diff --git a/gb.httpd/src/libhttpd.c b/gb.httpd/src/libhttpd.c new file mode 100644 index 00000000..4ecec5a0 --- /dev/null +++ b/gb.httpd/src/libhttpd.c @@ -0,0 +1,4376 @@ +/* libhttpd.c - HTTP protocol library +** +** Copyright (c) 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@mail.acme.com>. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include "main.h" +#include "thttpd.h" +#include "version.h" + +#ifdef SHOW_SERVER_VERSION +#define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE +#else /* SHOW_SERVER_VERSION */ +#define EXPOSED_SERVER_SOFTWARE "gb.httpd" +#endif /* SHOW_SERVER_VERSION */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <time.h> +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif /* HAVE_MEMORY_H */ +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +//#include <syslog.h> +#include <unistd.h> +#include <stdarg.h> + +#ifdef HAVE_OSRELDATE_H +#include <osreldate.h> +#endif /* HAVE_OSRELDATE_H */ + +#ifdef HAVE_DIRENT_H +#include <dirent.h> +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#ifdef HAVE_SYS_NDIR_H +#include <sys/ndir.h> +#endif +#ifdef HAVE_SYS_DIR_H +#include <sys/dir.h> +#endif +#ifdef HAVE_NDIR_H +#include <ndir.h> +#endif +#endif + +extern char *crypt(const char *key, const char *setting); + +#include "libhttpd.h" +#include "timers.h" +#include "match.h" +#include "tdate_parse.h" + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + +#ifndef HAVE_SOCKLENT +typedef int socklen_t; +#endif + +#ifdef __CYGWIN__ +#define timezone _timezone +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +extern char **environ; + +/* Forwards. */ +static void check_options(void); +static void free_httpd_server(httpd_server * hs); +static int initialize_listen_socket(httpd_sockaddr * saP); +static void add_response(httpd_conn * hc, char *str); +static void send_mime(httpd_conn * hc, int status, char *title, + char *encodings, char *extraheads, char *type, + off_t length, time_t mod); +static void send_response(httpd_conn * hc, int status, char *title, + char *extraheads, char *form, char *arg); +static void send_response_tail(httpd_conn * hc); +static void defang(char *str, char *dfstr, int dfsize); +#ifdef ERR_DIR +static int send_err_file(httpd_conn * hc, int status, char *title, + char *extraheads, char *filename); +#endif /* ERR_DIR */ +#ifdef AUTH_FILE +static void send_authenticate(httpd_conn * hc, char *realm); +static int b64_decode(const char *str, unsigned char *space, int size); +//static int auth_check (httpd_conn * hc, char *dirname); +//static int auth_check2 (httpd_conn * hc, char *dirname); +#endif /* AUTH_FILE */ +//static void send_dirredirect (httpd_conn * hc); +static int hexit(char c); +static void strdecode(char *to, char *from); +#ifdef GENERATE_INDEXES +static void strencode(char *to, int tosize, char *from); +#endif /* GENERATE_INDEXES */ +#ifdef TILDE_MAP_1 +static int tilde_map_1(httpd_conn * hc); +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 +static int tilde_map_2(httpd_conn * hc); +#endif /* TILDE_MAP_2 */ +static int vhost_map(httpd_conn * hc); +//static char *expand_symlinks (char *path, char **restP, int no_symlink_check, int tildemapped); +static char *bufgets(httpd_conn * hc); +static void de_dotdot(char *file); +static void init_mime(void); +static void figure_mime (httpd_conn * hc); +#ifdef CGI_TIMELIMIT +static void cgi_kill2(ClientData client_data, struct timeval *nowP); +static void cgi_kill(ClientData client_data, struct timeval *nowP); +#endif /* CGI_TIMELIMIT */ +#ifdef GENERATE_INDEXES +static int ls(httpd_conn * hc); +#endif /* GENERATE_INDEXES */ +static char *build_env(char *fmt, char *arg); +#ifdef SERVER_NAME_LIST +static char *hostname_map(char *hostname); +#endif /* SERVER_NAME_LIST */ +static char **make_envp(httpd_conn * hc); +static char **make_argp(httpd_conn * hc); +static void cgi_interpose_input(httpd_conn * hc, int wfd); +static void post_post_garbage_hack(httpd_conn * hc); +static void cgi_interpose_output(httpd_conn * hc, int rfd); +static void cgi_child(httpd_conn * hc); +static int cgi(httpd_conn * hc); +static int really_start_request(httpd_conn * hc, struct timeval *nowP); +static void make_log_entry(httpd_conn * hc, struct timeval *nowP); +static int check_referer(httpd_conn * hc); +static int really_check_referer(httpd_conn * hc); +static int sockaddr_check(httpd_sockaddr * saP); +static size_t sockaddr_len(httpd_sockaddr * saP); +static int my_snprintf(char *str, size_t size, const char *format, ...); +#ifndef HAVE_ATOLL +static long long atoll(const char *str); +#endif /* HAVE_ATOLL */ + + +/* This global keeps track of whether we are in the main process or a +** sub-process. The reason is that httpd_write_response() can get called +** in either context; when it is called from the main process it must use +** non-blocking I/O to avoid stalling the server, but when it is called +** from a sub-process it wants to use blocking I/O so that the whole +** response definitely gets written. So, it checks this variable. A bit +** of a hack but it seems to do the right thing. +*/ +static int sub_process = 0; + + +static void check_options(void) +{ +#if defined(TILDE_MAP_1) && defined(TILDE_MAP_2) + syslog(LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined"); + exit(1); +#endif /* both */ +} + + +static void free_httpd_server(httpd_server * hs) +{ + if (hs->binding_hostname != (char *) 0) + free((void *) hs->binding_hostname); + if (hs->cwd != (char *) 0) + free((void *) hs->cwd); + if (hs->cgi_pattern != (char *) 0) + free((void *) hs->cgi_pattern); + if (hs->charset != (char *) 0) + free((void *) hs->charset); + if (hs->p3p != (char *) 0) + free((void *) hs->p3p); + if (hs->url_pattern != (char *) 0) + free((void *) hs->url_pattern); + if (hs->local_pattern != (char *) 0) + free((void *) hs->local_pattern); + free((void *) hs); +} + + +httpd_server *httpd_initialize(char *hostname, httpd_sockaddr * sa4P, + httpd_sockaddr * sa6P, unsigned short port, + char *cgi_pattern, int cgi_limit, + int cgi_timelimit, char *charset, char *p3p, + int max_age, char *cwd, int no_log, + FILE * logfp, int no_symlink_check, int vhost, + int global_passwd, char *url_pattern, + char *local_pattern, int no_empty_referers) +{ + httpd_server *hs; + static char ghnbuf[256]; + char *cp; + + check_options(); + + hs = NEW(httpd_server, 1); + if (hs == (httpd_server *) 0) + { + syslog(LOG_CRIT, "out of memory allocating an httpd_server"); + return (httpd_server *) 0; + } + + if (hostname != (char *) 0) + { + hs->binding_hostname = strdup(hostname); + if (hs->binding_hostname == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying hostname"); + return (httpd_server *) 0; + } + hs->server_hostname = hs->binding_hostname; + } + else + { + hs->binding_hostname = (char *) 0; + hs->server_hostname = (char *) 0; + if (gethostname(ghnbuf, sizeof(ghnbuf)) < 0) + ghnbuf[0] = '\0'; +#ifdef SERVER_NAME_LIST + if (ghnbuf[0] != '\0') + hs->server_hostname = hostname_map(ghnbuf); +#endif /* SERVER_NAME_LIST */ + if (hs->server_hostname == (char *) 0) + { +#ifdef SERVER_NAME + hs->server_hostname = SERVER_NAME; +#else /* SERVER_NAME */ + if (ghnbuf[0] != '\0') + hs->server_hostname = ghnbuf; +#endif /* SERVER_NAME */ + } + } + + hs->port = port; + if (cgi_pattern == (char *) 0) + hs->cgi_pattern = (char *) 0; + else + { + /* Nuke any leading slashes. */ + if (cgi_pattern[0] == '/') + ++cgi_pattern; + hs->cgi_pattern = strdup(cgi_pattern); + if (hs->cgi_pattern == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying cgi_pattern"); + return (httpd_server *) 0; + } + /* Nuke any leading slashes in the cgi pattern. */ + while ((cp = strstr(hs->cgi_pattern, "|/")) != (char *) 0) + (void) strcpy(cp + 1, cp + 2); + } + hs->cgi_limit = cgi_limit; + hs->cgi_timelimit = cgi_timelimit; + hs->cgi_count = 0; + hs->charset = strdup(charset); + hs->p3p = strdup(p3p); + hs->max_age = max_age; + hs->cwd = strdup(cwd); + if (hs->cwd == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying cwd"); + return (httpd_server *) 0; + } + if (url_pattern == (char *) 0) + hs->url_pattern = (char *) 0; + else + { + hs->url_pattern = strdup(url_pattern); + if (hs->url_pattern == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying url_pattern"); + return (httpd_server *) 0; + } + } + if (local_pattern == (char *) 0) + hs->local_pattern = (char *) 0; + else + { + hs->local_pattern = strdup(local_pattern); + if (hs->local_pattern == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying local_pattern"); + return (httpd_server *) 0; + } + } + hs->no_log = no_log; + hs->logfp = (FILE *) 0; + httpd_set_logfp(hs, logfp); + hs->no_symlink_check = no_symlink_check; + hs->vhost = vhost; + hs->global_passwd = global_passwd; + hs->no_empty_referers = no_empty_referers; + + /* Initialize listen sockets. Try v6 first because of a Linux peculiarity; + ** like some other systems, it has magical v6 sockets that also listen for + ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails. + */ + if (sa6P == (httpd_sockaddr *) 0) + hs->listen6_fd = -1; + else + hs->listen6_fd = initialize_listen_socket(sa6P); + if (sa4P == (httpd_sockaddr *) 0) + hs->listen4_fd = -1; + else + hs->listen4_fd = initialize_listen_socket(sa4P); + /* If we didn't get any valid sockets, fail. */ + if (hs->listen4_fd == -1 && hs->listen6_fd == -1) + { + free_httpd_server(hs); + return (httpd_server *) 0; + } + + init_mime(); + + /* Done initializing. */ + if (hs->binding_hostname == (char *) 0) + syslog(LOG_NOTICE, "starting on port %d", (int) hs->port); + else + syslog(LOG_NOTICE, "starting on %.80s, port %d", httpd_ntoa(hs->listen4_fd != -1 ? sa4P : sa6P), (int) hs->port); + + return hs; +} + + +static int initialize_listen_socket(httpd_sockaddr * saP) +{ + int listen_fd; + int on, flags; + + /* Check sockaddr. */ + if (!sockaddr_check(saP)) + { + syslog(LOG_CRIT, "unknown sockaddr family on listen socket"); + return -1; + } + + /* Create socket. */ + listen_fd = socket(saP->sa.sa_family, SOCK_STREAM, 0); + if (listen_fd < 0) + { + syslog(LOG_CRIT, "socket %.80s - %m", httpd_ntoa(saP)); + return -1; + } + (void) fcntl(listen_fd, F_SETFD, 1); + + /* Allow reuse of local addresses. */ + on = 1; + if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, + sizeof(on)) < 0) + syslog(LOG_CRIT, "setsockopt SO_REUSEADDR - %m"); + + /* Bind to it. */ + if (bind(listen_fd, &saP->sa, sockaddr_len(saP)) < 0) + { + syslog(LOG_CRIT, "bind %.80s - %m", httpd_ntoa(saP)); + (void) close(listen_fd); + return -1; + } + + /* Set the listen file descriptor to no-delay / non-blocking mode. */ + flags = fcntl(listen_fd, F_GETFL, 0); + if (flags == -1) + { + syslog(LOG_CRIT, "fcntl F_GETFL - %m"); + (void) close(listen_fd); + return -1; + } + if (fcntl(listen_fd, F_SETFL, flags | O_NDELAY) < 0) + { + syslog(LOG_CRIT, "fcntl O_NDELAY - %m"); + (void) close(listen_fd); + return -1; + } + + /* Start a listen going. */ + if (listen(listen_fd, LISTEN_BACKLOG) < 0) + { + syslog(LOG_CRIT, "listen - %m"); + (void) close(listen_fd); + return -1; + } + + /* Use accept filtering, if available. */ +#ifdef SO_ACCEPTFILTER + { +#if ( __FreeBSD_version >= 411000 ) +#define ACCEPT_FILTER_NAME "httpready" +#else +#define ACCEPT_FILTER_NAME "dataready" +#endif + struct accept_filter_arg af; + (void) bzero(&af, sizeof(af)); + (void) strcpy(af.af_name, ACCEPT_FILTER_NAME); + (void) setsockopt(listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char *) &af, + sizeof(af)); + } +#endif /* SO_ACCEPTFILTER */ + + return listen_fd; +} + + +void httpd_set_logfp(httpd_server * hs, FILE * logfp) +{ + if (hs->logfp != (FILE *) 0) + (void) fclose(hs->logfp); + hs->logfp = logfp; +} + + +void httpd_terminate(httpd_server * hs) +{ + httpd_unlisten(hs); + if (hs->logfp != (FILE *) 0) + (void) fclose(hs->logfp); + free_httpd_server(hs); +} + + +void httpd_unlisten(httpd_server * hs) +{ + if (hs->listen4_fd != -1) + { + (void) close(hs->listen4_fd); + hs->listen4_fd = -1; + } + if (hs->listen6_fd != -1) + { + (void) close(hs->listen6_fd); + hs->listen6_fd = -1; + } +} + + +/* Conditional macro to allow two alternate forms for use in the built-in +** error pages. If EXPLICIT_ERROR_PAGES is defined, the second and more +** explicit error form is used; otherwise, the first and more generic +** form is used. +*/ +#ifdef EXPLICIT_ERROR_PAGES +#define ERROR_FORM(a,b) b +#else /* EXPLICIT_ERROR_PAGES */ +#define ERROR_FORM(a,b) a +#endif /* EXPLICIT_ERROR_PAGES */ + + +static char *ok200title = "OK"; +static char *ok206title = "Partial Content"; + +static char *err302title = "Found"; +//static char *err302form = "The actual URL is '%.80s'.\n"; + +static char *err304title = "Not Modified"; + +char *httpd_err400title = "Bad Request"; +char *httpd_err400form = + "Your request has bad syntax or is inherently impossible to satisfy.\n"; + +#ifdef AUTH_FILE +static char *err401title = "Unauthorized"; +static char *err401form = "Authorization required for the URL '%.80s'.\n"; +#endif /* AUTH_FILE */ + +static char *err403title = "Forbidden"; +#ifndef EXPLICIT_ERROR_PAGES +static char *err403form = + "You do not have permission to get URL '%.80s' from this server.\n"; +#endif /* !EXPLICIT_ERROR_PAGES */ + +static char *err404title = "Not Found"; +static char *err404form = "The requested URL '%.80s' was not found on this server.\n"; + +char *httpd_err408title = "Request Timeout"; +char *httpd_err408form = + "No request appeared within a reasonable time period.\n"; + +static char *err500title = "Internal Error"; +static char *err500form = + "There was an unusual problem serving the requested URL '%.80s'.\n"; + +static char *err501title = "Not Implemented"; +static char *err501form = + "The requested method '%.80s' is not implemented by this server.\n"; + +char *httpd_err503title = "Service Temporarily Overloaded"; +char *httpd_err503form = + "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n"; + + +/* Append a string to the buffer waiting to be sent as response. */ +static void add_response(httpd_conn * hc, char *str) +{ + size_t len; + + len = strlen(str); + httpd_realloc_str(&hc->response, &hc->maxresponse, hc->responselen + len); + (void) memmove(&(hc->response[hc->responselen]), str, len); + hc->responselen += len; +} + +/* Send the buffered response. */ +void httpd_write_response(httpd_conn * hc) +{ + /* If we are in a sub-process, turn off no-delay mode. */ + if (sub_process) + httpd_clear_ndelay(hc->conn_fd); + /* Send the response, if necessary. */ + if (hc->responselen > 0) + { + (void) httpd_write_fully(hc->conn_fd, hc->response, hc->responselen); + hc->responselen = 0; + } +} + + +/* Set no-delay / non-blocking mode on a socket. */ +void httpd_set_ndelay(int fd) +{ + int flags, newflags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags != -1) + { + newflags = flags | (int) O_NDELAY; + if (newflags != flags) + (void) fcntl(fd, F_SETFL, newflags); + } +} + + +/* Clear no-delay / non-blocking mode on a socket. */ +void httpd_clear_ndelay(int fd) +{ + int flags, newflags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags != -1) + { + newflags = flags & ~(int) O_NDELAY; + if (newflags != flags) + (void) fcntl(fd, F_SETFL, newflags); + } +} + + +static void +send_mime(httpd_conn * hc, int status, char *title, char *encodings, + char *extraheads, char *type, off_t length, time_t mod) +{ + time_t now, expires; + const char *rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT"; + char nowbuf[100]; + char modbuf[100]; + char expbuf[100]; + char fixed_type[500]; + char buf[1000]; + int partial_content; + int s100; + + hc->status = status; + hc->bytes_to_send = length; + if (hc->mime_flag) + { + if (status == 200 && hc->got_range && + (hc->last_byte_index >= hc->first_byte_index) && + ((hc->last_byte_index != length - 1) || + (hc->first_byte_index != 0)) && + (hc->range_if == (time_t) - 1 || hc->range_if == hc->sb.mtime)) + { + partial_content = 1; + hc->status = status = 206; + title = ok206title; + } + else + { + partial_content = 0; + hc->got_range = 0; + } + + now = time((time_t *) 0); + if (mod == (time_t) 0) + mod = now; + (void) strftime(nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime(&now)); + (void) strftime(modbuf, sizeof(modbuf), rfc1123fmt, gmtime(&mod)); + (void) my_snprintf(fixed_type, sizeof(fixed_type), type, hc->hs->charset); + (void) my_snprintf(buf, sizeof(buf), + "%.20s %d %s\015\012Server: %s\015\012Content-Type: %s\015\012Date: %s\015\012Last-Modified: %s\015\012Accept-Ranges: bytes\015\012Connection: close\015\012", + hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, + fixed_type, nowbuf, modbuf); + add_response(hc, buf); + s100 = status / 100; + if (s100 != 2 && s100 != 3) + { + (void) my_snprintf(buf, sizeof(buf), + "Cache-Control: no-cache,no-store\015\012"); + add_response(hc, buf); + } + if (encodings[0] != '\0') + { + (void) my_snprintf(buf, sizeof(buf), + "Content-Encoding: %s\015\012", encodings); + add_response(hc, buf); + } + if (partial_content) + { + (void) my_snprintf(buf, sizeof(buf), + "Content-Range: bytes %lld-%lld/%lld\015\012Content-Length: %lld\015\012", + (int64_t) hc->first_byte_index, + (int64_t) hc->last_byte_index, (int64_t) length, + (int64_t) (hc->last_byte_index - + hc->first_byte_index + 1)); + add_response(hc, buf); + } + else if (length >= 0) + { + (void) my_snprintf(buf, sizeof(buf), + "Content-Length: %lld\015\012", (int64_t) length); + add_response(hc, buf); + } + if (hc->hs->p3p[0] != '\0') + { + (void) my_snprintf(buf, sizeof(buf), "P3P: %s\015\012", hc->hs->p3p); + add_response(hc, buf); + } + if (hc->hs->max_age >= 0) + { + expires = now + hc->hs->max_age; + (void) strftime(expbuf, sizeof(expbuf), rfc1123fmt, gmtime(&expires)); + (void) my_snprintf(buf, sizeof(buf), + "Cache-Control: max-age=%d\015\012Expires: %s\015\012", + hc->hs->max_age, expbuf); + add_response(hc, buf); + } + if (extraheads[0] != '\0') + add_response(hc, extraheads); + add_response(hc, "\015\012"); + } +} + + +static int str_alloc_count = 0; +static size_t str_alloc_size = 0; + +void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size) +{ + if (*maxsizeP == 0) + { + *maxsizeP = MAX(200, size + 100); + *strP = NEW(char, *maxsizeP + 1); + ++str_alloc_count; + str_alloc_size += *maxsizeP; + } + else if (size > *maxsizeP) + { + str_alloc_size -= *maxsizeP; + *maxsizeP = MAX(*maxsizeP * 2, size * 5 / 4); + *strP = RENEW(*strP, char, *maxsizeP + 1); + str_alloc_size += *maxsizeP; + } + else + return; + if (*strP == (char *) 0) + { + syslog(LOG_ERR, "out of memory reallocating a string to %d bytes", + *maxsizeP); + exit(1); + } +} + + +static void +send_response(httpd_conn * hc, int status, char *title, char *extraheads, + char *form, char *arg) +{ + char defanged_arg[1000], buf[2000]; + + send_mime(hc, status, title, "", extraheads, "text/html; charset=%s", + (off_t) - 1, (time_t) 0); + (void) my_snprintf(buf, sizeof(buf), "\ +<html>\n\ +<head><title>%d %s\n\ +\n\ +

    %d %s

    \n", status, title, status, title); + add_response(hc, buf); + defang(arg, defanged_arg, sizeof(defanged_arg)); + (void) my_snprintf(buf, sizeof(buf), form, defanged_arg); + add_response(hc, buf); + if (match("**MSIE**", hc->useragent)) + { + int n; + add_response(hc, "\n"); + } + send_response_tail(hc); +} + + +static void send_response_tail(httpd_conn * hc) +{ +#if 0 + char buf[1000]; + + (void) my_snprintf(buf, sizeof(buf), "\ +
    \n\ +
    %s
    \n\ +\n\ +\n", SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE); + add_response(hc, buf); +#endif +} + + +static void defang(char *str, char *dfstr, int dfsize) +{ + char *cp1; + char *cp2; + + for (cp1 = str, cp2 = dfstr; + *cp1 != '\0' && cp2 - dfstr < dfsize - 5; ++cp1, ++cp2) + { + switch (*cp1) + { + case '<': + *cp2++ = '&'; + *cp2++ = 'l'; + *cp2++ = 't'; + *cp2 = ';'; + break; + case '>': + *cp2++ = '&'; + *cp2++ = 'g'; + *cp2++ = 't'; + *cp2 = ';'; + break; + default: + *cp2 = *cp1; + break; + } + } + *cp2 = '\0'; +} + + +void +httpd_send_err(httpd_conn * hc, int status, char *title, char *extraheads, + char *form, char *arg) +{ +#ifdef ERR_DIR + + char filename[1000]; + + /* Try virtual host error page. */ + if (hc->hs->vhost && hc->hostdir[0] != '\0') + { + (void) my_snprintf(filename, sizeof(filename), + "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status); + if (send_err_file(hc, status, title, extraheads, filename)) + return; + } + + /* Try server-wide error page. */ + (void) my_snprintf(filename, sizeof(filename), + "%s/err%d.html", ERR_DIR, status); + if (send_err_file(hc, status, title, extraheads, filename)) + return; + + /* Fall back on built-in error page. */ + send_response(hc, status, title, extraheads, form, arg); + +#else /* ERR_DIR */ + + send_response(hc, status, title, extraheads, form, arg); + +#endif /* ERR_DIR */ +} + + +#ifdef ERR_DIR +static int +send_err_file(httpd_conn * hc, int status, char *title, char *extraheads, + char *filename) +{ + FILE *fp; + char buf[1000]; + size_t r; + + fp = fopen(filename, "r"); + if (fp == (FILE *) 0) + return 0; + send_mime(hc, status, title, "", extraheads, "text/html; charset=%s", + (off_t) - 1, (time_t) 0); + for (;;) + { + r = fread(buf, 1, sizeof(buf) - 1, fp); + if (r == 0) + break; + buf[r] = '\0'; + add_response(hc, buf); + } + (void) fclose(fp); + +#ifdef ERR_APPEND_SERVER_INFO + send_response_tail(hc); +#endif /* ERR_APPEND_SERVER_INFO */ + + return 1; +} +#endif /* ERR_DIR */ + + +#ifdef AUTH_FILE + +static void send_authenticate(httpd_conn * hc, char *realm) +{ + static char *header; + static size_t maxheader = 0; + static char headstr[] = "WWW-Authenticate: Basic realm=\""; + + httpd_realloc_str(&header, &maxheader, sizeof(headstr) + strlen(realm) + 3); + (void) my_snprintf(header, maxheader, "%s%s\"\015\012", headstr, realm); + httpd_send_err(hc, 401, err401title, header, err401form, hc->encodedurl); + /* If the request was a POST then there might still be data to be read, + ** so we need to do a lingering close. + */ + if (hc->method == METHOD_POST) + hc->should_linger = 1; +} + + +/* Base-64 decoding. This represents binary data as printable ASCII +** characters. Three 8-bit binary bytes are turned into four 6-bit +** values, like so: +** +** [11111111] [22222222] [33333333] +** +** [111111] [112222] [222233] [333333] +** +** Then the 6-bit values are represented using the characters "A-Za-z0-9+/". +*/ + +static int b64_decode_table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */ +}; + +/* Do base-64 decoding on a string. Ignore any non-base64 bytes. +** Return the actual number of bytes generated. The decoded size will +** be at most 3/4 the size of the encoded, and may be smaller if there +** are padding characters (blanks, newlines). +*/ +static int b64_decode(const char *str, unsigned char *space, int size) +{ + const char *cp; + int space_idx, phase; + int d, prev_d = 0; + unsigned char c; + + space_idx = 0; + phase = 0; + for (cp = str; *cp != '\0'; ++cp) + { + d = b64_decode_table[(int) *cp]; + if (d != -1) + { + switch (phase) + { + case 0: + ++phase; + break; + case 1: + c = ((prev_d << 2) | ((d & 0x30) >> 4)); + if (space_idx < size) + space[space_idx++] = c; + ++phase; + break; + case 2: + c = (((prev_d & 0xf) << 4) | ((d & 0x3c) >> 2)); + if (space_idx < size) + space[space_idx++] = c; + ++phase; + break; + case 3: + c = (((prev_d & 0x03) << 6) | d); + if (space_idx < size) + space[space_idx++] = c; + phase = 0; + break; + } + prev_d = d; + } + } + return space_idx; +} + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int auth_check(httpd_conn * hc, char *dirname) +{ + if (hc->hs->global_passwd) + { + char *topdir; + if (hc->hs->vhost && hc->hostdir[0] != '\0') + topdir = hc->hostdir; + else + topdir = "."; + switch (auth_check2(hc, topdir)) + { + case -1: + return -1; + case 1: + return 1; + } + } + return auth_check2(hc, dirname); +} + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int auth_check2(httpd_conn * hc, char *dirname) +{ + static char *authpath; + static size_t maxauthpath = 0; + struct stat sb; + char authinfo[500]; + char *authpass; + char *colon; + int l; + FILE *fp; + char line[500]; + char *cryp; + static char *prevauthpath; + static size_t maxprevauthpath = 0; + static time_t prevmtime; + static char *prevuser; + static size_t maxprevuser = 0; + static char *prevcryp; + static size_t maxprevcryp = 0; + + /* Construct auth filename. */ + httpd_realloc_str(&authpath, &maxauthpath, + strlen(dirname) + 1 + sizeof(AUTH_FILE)); + (void) my_snprintf(authpath, maxauthpath, "%s/%s", dirname, AUTH_FILE); + + /* Does this directory have an auth file? */ + if (stat(authpath, &sb) < 0) + /* Nope, let the request go through. */ + return 0; + + /* Does this request contain basic authorization info? */ + if (hc->authorization[0] == '\0' || + strncmp(hc->authorization, "Basic ", 6) != 0) + { + /* Nope, return a 401 Unauthorized. */ + send_authenticate(hc, dirname); + return -1; + } + + /* Decode it. */ + l = b64_decode(&(hc->authorization[6]), (unsigned char *) authinfo, + sizeof(authinfo) - 1); + authinfo[l] = '\0'; + /* Split into user and password. */ + authpass = strchr(authinfo, ':'); + if (authpass == (char *) 0) + { + /* No colon? Bogus auth info. */ + send_authenticate(hc, dirname); + return -1; + } + *authpass++ = '\0'; + /* If there are more fields, cut them off. */ + colon = strchr(authpass, ':'); + if (colon != (char *) 0) + *colon = '\0'; + + /* See if we have a cached entry and can use it. */ + if (maxprevauthpath != 0 && + strcmp(authpath, prevauthpath) == 0 && + sb.st_mtime == prevmtime && strcmp(authinfo, prevuser) == 0) + { + /* Yes. Check against the cached encrypted password. */ + if (strcmp(crypt(authpass, prevcryp), prevcryp) == 0) + { + /* Ok! */ + httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, + strlen(authinfo)); + (void) strcpy(hc->remoteuser, authinfo); + return 1; + } + else + { + /* No. */ + send_authenticate(hc, dirname); + return -1; + } + } + + /* Open the password file. */ + fp = fopen(authpath, "r"); + if (fp == (FILE *) 0) + { + /* The file exists but we can't open it? Disallow access. */ + syslog(LOG_ERR, "%.80s auth file %.80s could not be opened - %m", + httpd_ntoa(&hc->client_addr), authpath); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n"), + hc->encodedurl); + return -1; + } + + /* Read it. */ + while (fgets(line, sizeof(line), fp) != (char *) 0) + { + /* Nuke newline. */ + l = strlen(line); + if (line[l - 1] == '\n') + line[l - 1] = '\0'; + /* Split into user and encrypted password. */ + cryp = strchr(line, ':'); + if (cryp == (char *) 0) + continue; + *cryp++ = '\0'; + /* Is this the right user? */ + if (strcmp(line, authinfo) == 0) + { + /* Yes. */ + (void) fclose(fp); + /* So is the password right? */ + if (strcmp(crypt(authpass, cryp), cryp) == 0) + { + /* Ok! */ + httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, strlen(line)); + (void) strcpy(hc->remoteuser, line); + /* And cache this user's info for next time. */ + httpd_realloc_str(&prevauthpath, &maxprevauthpath, strlen(authpath)); + (void) strcpy(prevauthpath, authpath); + prevmtime = sb.st_mtime; + httpd_realloc_str(&prevuser, &maxprevuser, strlen(authinfo)); + (void) strcpy(prevuser, authinfo); + httpd_realloc_str(&prevcryp, &maxprevcryp, strlen(cryp)); + (void) strcpy(prevcryp, cryp); + return 1; + } + else + { + /* No. */ + send_authenticate(hc, dirname); + return -1; + } + } + } + + /* Didn't find that user. Access denied. */ + (void) fclose(fp); + send_authenticate(hc, dirname); + return -1; +} + +#endif /* AUTH_FILE */ + +#if 0 +static void send_dirredirect(httpd_conn * hc) +{ + static char *location; + static char *header; + static size_t maxlocation = 0, maxheader = 0; + static char headstr[] = "Location: "; + + if (hc->query[0] != '\0') + { + char *cp = strchr(hc->encodedurl, '?'); + if (cp != (char *) 0) /* should always find it */ + *cp = '\0'; + httpd_realloc_str(&location, &maxlocation, + strlen(hc->encodedurl) + 2 + strlen(hc->query)); + (void) my_snprintf(location, maxlocation, + "%s/?%s", hc->encodedurl, hc->query); + } + else + { + httpd_realloc_str(&location, &maxlocation, strlen(hc->encodedurl) + 1); + (void) my_snprintf(location, maxlocation, "%s/", hc->encodedurl); + } + httpd_realloc_str(&header, &maxheader, sizeof(headstr) + strlen(location)); + (void) my_snprintf(header, maxheader, "%s%s\015\012", headstr, location); + send_response(hc, 302, err302title, header, err302form, location); +} +#endif + +char *httpd_method_str(int method) +{ + switch (method) + { + case METHOD_GET: + return "GET"; + case METHOD_HEAD: + return "HEAD"; + case METHOD_POST: + return "POST"; + default: + return "UNKNOWN"; + } +} + + +static int hexit(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return 0; /* shouldn't happen, we're guarded by isxdigit() */ +} + + +/* Copies and decodes a string. It's ok for from and to to be the +** same string. +*/ +static void strdecode(char *to, char *from) +{ + for (; *from != '\0'; ++to, ++from) + { + if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) + { + *to = hexit(from[1]) * 16 + hexit(from[2]); + from += 2; + } + else + *to = *from; + } + *to = '\0'; +} + + +#ifdef GENERATE_INDEXES +/* Copies and encodes a string. */ +static void strencode(char *to, int tosize, char *from) +{ + int tolen; + + for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) + { + if (isalnum(*from) || strchr("/_.-~", *from) != (char *) 0) + { + *to = *from; + ++to; + ++tolen; + } + else + { + (void) sprintf(to, "%%%02x", (int) *from & 0xff); + to += 3; + tolen += 3; + } + } + *to = '\0'; +} +#endif /* GENERATE_INDEXES */ + + +#ifdef TILDE_MAP_1 +/* Map a ~username/whatever URL into /username. */ +static int tilde_map_1(httpd_conn * hc) +{ + static char *temp; + static size_t maxtemp = 0; + int len; + static char *prefix = TILDE_MAP_1; + + len = strlen(hc->expnfilename) - 1; + httpd_realloc_str(&temp, &maxtemp, len); + (void) strcpy(temp, &hc->expnfilename[1]); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(prefix) + 1 + len); + (void) strcpy(hc->expnfilename, prefix); + if (prefix[0] != '\0') + (void) strcat(hc->expnfilename, "/"); + (void) strcat(hc->expnfilename, temp); + return 1; +} +#endif /* TILDE_MAP_1 */ + +#ifdef TILDE_MAP_2 +/* Map a ~username/whatever URL into /. */ +static int tilde_map_2(httpd_conn * hc) +{ + static char *temp; + static size_t maxtemp = 0; + static char *postfix = TILDE_MAP_2; + char *cp; + struct passwd *pw; + char *alt; + char *rest; + + /* Get the username. */ + httpd_realloc_str(&temp, &maxtemp, strlen(hc->expnfilename) - 1); + (void) strcpy(temp, &hc->expnfilename[1]); + cp = strchr(temp, '/'); + if (cp != (char *) 0) + *cp++ = '\0'; + else + cp = ""; + + /* Get the passwd entry. */ + pw = getpwnam(temp); + if (pw == (struct passwd *) 0) + return 0; + + /* Set up altdir. */ + httpd_realloc_str(&hc->altdir, &hc->maxaltdir, + strlen(pw->pw_dir) + 1 + strlen(postfix)); + (void) strcpy(hc->altdir, pw->pw_dir); + if (postfix[0] != '\0') + { + (void) strcat(hc->altdir, "/"); + (void) strcat(hc->altdir, postfix); + } + alt = expand_symlinks(hc->altdir, &rest, 0, 1); + if (rest[0] != '\0') + return 0; + httpd_realloc_str(&hc->altdir, &hc->maxaltdir, strlen(alt)); + (void) strcpy(hc->altdir, alt); + + /* And the filename becomes altdir plus the post-~ part of the original. */ + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(hc->altdir) + 1 + strlen(cp)); + (void) my_snprintf(hc->expnfilename, hc->maxexpnfilename, + "%s/%s", hc->altdir, cp); + + /* For this type of tilde mapping, we want to defeat vhost mapping. */ + hc->tildemapped = 1; + + return 1; +} +#endif /* TILDE_MAP_2 */ + + +/* Virtual host mapping. */ +static int vhost_map(httpd_conn * hc) +{ + httpd_sockaddr sa; + socklen_t sz; + static char *tempfilename; + static size_t maxtempfilename = 0; + char *cp1; + int len; +#ifdef VHOST_DIRLEVELS + int i; + char *cp2; +#endif /* VHOST_DIRLEVELS */ + + /* Figure out the virtual hostname. */ + if (hc->reqhost[0] != '\0') + hc->hostname = hc->reqhost; + else if (hc->hdrhost[0] != '\0') + hc->hostname = hc->hdrhost; + else + { + sz = sizeof(sa); + if (getsockname(hc->conn_fd, &sa.sa, &sz) < 0) + { + syslog(LOG_ERR, "getsockname - %m"); + return 0; + } + hc->hostname = httpd_ntoa(&sa); + } + /* Pound it to lower case. */ + for (cp1 = hc->hostname; *cp1 != '\0'; ++cp1) + if (isupper(*cp1)) + *cp1 = tolower(*cp1); + + if (hc->tildemapped) + return 1; + + /* Figure out the host directory. */ +#ifdef VHOST_DIRLEVELS + httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, + strlen(hc->hostname) + 2 * VHOST_DIRLEVELS); + if (strncmp(hc->hostname, "www.", 4) == 0) + cp1 = &hc->hostname[4]; + else + cp1 = hc->hostname; + for (cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i) + { + /* Skip dots in the hostname. If we don't, then we get vhost + ** directories in higher level of filestructure if dot gets + ** involved into path construction. It's `while' used here instead + ** of `if' for it's possible to have a hostname formed with two + ** dots at the end of it. + */ + while (*cp1 == '.') + ++cp1; + /* Copy a character from the hostname, or '_' if we ran out. */ + if (*cp1 != '\0') + *cp2++ = *cp1++; + else + *cp2++ = '_'; + /* Copy a slash. */ + *cp2++ = '/'; + } + (void) strcpy(cp2, hc->hostname); +#else /* VHOST_DIRLEVELS */ + httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, strlen(hc->hostname)); + (void) strcpy(hc->hostdir, hc->hostname); +#endif /* VHOST_DIRLEVELS */ + + /* Prepend hostdir to the filename. */ + len = strlen(hc->expnfilename); + httpd_realloc_str(&tempfilename, &maxtempfilename, len); + (void) strcpy(tempfilename, hc->expnfilename); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(hc->hostdir) + 1 + len); + (void) strcpy(hc->expnfilename, hc->hostdir); + (void) strcat(hc->expnfilename, "/"); + (void) strcat(hc->expnfilename, tempfilename); + return 1; +} + + +#if 0 +/* Expands all symlinks in the given filename, eliding ..'s and leading /'s. +** Returns the expanded path (pointer to static string), or (char*) 0 on +** errors. Also returns, in the string pointed to by restP, any trailing +** parts of the path that don't exist. +** +** This is a fairly nice little routine. It handles any size filenames +** without excessive mallocs. +*/ +static char *expand_symlinks(char *path, char **restP, int no_symlink_check, + int tildemapped) +{ + static char *checked; + static char *rest; + char link[5000]; + static size_t maxchecked = 0, maxrest = 0; + size_t checkedlen, restlen, linklen, prevcheckedlen, prevrestlen; + int nlinks, i; + char *r; + char *cp1; + char *cp2; + + if (no_symlink_check) + { + /* If we are chrooted, we can actually skip the symlink-expansion, + ** since it's impossible to get out of the tree. However, we still + ** need to do the pathinfo check, and the existing symlink expansion + ** code is a pretty reasonable way to do this. So, what we do is + ** a single stat() of the whole filename - if it exists, then we + ** return it as is with nothing in restP. If it doesn't exist, we + ** fall through to the existing code. + ** + ** One side-effect of this is that users can't symlink to central + ** approved CGIs any more. The workaround is to use the central + ** URL for the CGI instead of a local symlinked one. + */ + struct stat sb; + if (stat(path, &sb) != -1) + { + checkedlen = strlen(path); + httpd_realloc_str(&checked, &maxchecked, checkedlen); + (void) strcpy(checked, path); + /* Trim trailing slashes. */ + while (checked[checkedlen - 1] == '/') + { + checked[checkedlen - 1] = '\0'; + --checkedlen; + } + httpd_realloc_str(&rest, &maxrest, 0); + rest[0] = '\0'; + *restP = rest; + return checked; + } + } + + /* Start out with nothing in checked and the whole filename in rest. */ + httpd_realloc_str(&checked, &maxchecked, 1); + checked[0] = '\0'; + checkedlen = 0; + restlen = strlen(path); + httpd_realloc_str(&rest, &maxrest, restlen); + (void) strcpy(rest, path); + if (rest[restlen - 1] == '/') + rest[--restlen] = '\0'; /* trim trailing slash */ + if (!tildemapped) + /* Remove any leading slashes. */ + while (rest[0] == '/') + { + (void) strcpy(rest, &(rest[1])); + --restlen; + } + r = rest; + nlinks = 0; + + /* While there are still components to check... */ + while (restlen > 0) + { + /* Save current checkedlen in case we get a symlink. Save current + ** restlen in case we get a non-existant component. + */ + prevcheckedlen = checkedlen; + prevrestlen = restlen; + + /* Grab one component from r and transfer it to checked. */ + cp1 = strchr(r, '/'); + if (cp1 != (char *) 0) + { + i = cp1 - r; + if (i == 0) + { + /* Special case for absolute paths. */ + httpd_realloc_str(&checked, &maxchecked, checkedlen + 1); + (void) strncpy(&checked[checkedlen], r, 1); + checkedlen += 1; + } + else if (strncmp(r, "..", MAX(i, 2)) == 0) + { + /* Ignore ..'s that go above the start of the path. */ + if (checkedlen != 0) + { + cp2 = strrchr(checked, '/'); + if (cp2 == (char *) 0) + checkedlen = 0; + else if (cp2 == checked) + checkedlen = 1; + else + checkedlen = cp2 - checked; + } + } + else + { + httpd_realloc_str(&checked, &maxchecked, checkedlen + 1 + i); + if (checkedlen > 0 && checked[checkedlen - 1] != '/') + checked[checkedlen++] = '/'; + (void) strncpy(&checked[checkedlen], r, i); + checkedlen += i; + } + checked[checkedlen] = '\0'; + r += i + 1; + restlen -= i + 1; + } + else + { + /* No slashes remaining, r is all one component. */ + if (strcmp(r, "..") == 0) + { + /* Ignore ..'s that go above the start of the path. */ + if (checkedlen != 0) + { + cp2 = strrchr(checked, '/'); + if (cp2 == (char *) 0) + checkedlen = 0; + else if (cp2 == checked) + checkedlen = 1; + else + checkedlen = cp2 - checked; + checked[checkedlen] = '\0'; + } + } + else + { + httpd_realloc_str(&checked, &maxchecked, checkedlen + 1 + restlen); + if (checkedlen > 0 && checked[checkedlen - 1] != '/') + checked[checkedlen++] = '/'; + (void) strcpy(&checked[checkedlen], r); + checkedlen += restlen; + } + r += restlen; + restlen = 0; + } + + /* Try reading the current filename as a symlink */ + if (checked[0] == '\0') + continue; + linklen = readlink(checked, link, sizeof(link) - 1); + if (linklen == -1) + { + if (errno == EINVAL) + continue; /* not a symlink */ + if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) + { + /* That last component was bogus. Restore and return. */ + *restP = r - (prevrestlen - restlen); + if (prevcheckedlen == 0) + (void) strcpy(checked, "."); + else + checked[prevcheckedlen] = '\0'; + return checked; + } + syslog(LOG_ERR, "readlink %.80s - %m", checked); + return (char *) 0; + } + ++nlinks; + if (nlinks > MAX_LINKS) + { + syslog(LOG_ERR, "too many symlinks in %.80s", path); + return (char *) 0; + } + link[linklen] = '\0'; + if (link[linklen - 1] == '/') + link[--linklen] = '\0'; /* trim trailing slash */ + + /* Insert the link contents in front of the rest of the filename. */ + if (restlen != 0) + { + (void) strcpy(rest, r); + httpd_realloc_str(&rest, &maxrest, restlen + linklen + 1); + for (i = restlen; i >= 0; --i) + rest[i + linklen + 1] = rest[i]; + (void) strcpy(rest, link); + rest[linklen] = '/'; + restlen += linklen + 1; + r = rest; + } + else + { + /* There's nothing left in the filename, so the link contents + ** becomes the rest. + */ + httpd_realloc_str(&rest, &maxrest, linklen); + (void) strcpy(rest, link); + restlen = linklen; + r = rest; + } + + if (rest[0] == '/') + { + /* There must have been an absolute symlink - zero out checked. */ + checked[0] = '\0'; + checkedlen = 0; + } + else + { + /* Re-check this component. */ + checkedlen = prevcheckedlen; + checked[checkedlen] = '\0'; + } + } + + /* Ok. */ + *restP = r; + if (checked[0] == '\0') + (void) strcpy(checked, "."); + return checked; +} +#endif + +int httpd_get_conn(httpd_server * hs, int listen_fd, httpd_conn * hc) +{ + httpd_sockaddr sa; + socklen_t sz; + + if (!hc->initialized) + { + hc->read_size = 0; + httpd_realloc_str(&hc->read_buf, &hc->read_size, 500); + hc->maxdecodedurl = + hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings = + hc->maxpathinfo = hc->maxquery = hc->maxaccept = + hc->maxaccepte = hc->maxreqhost = hc->maxhostdir = + hc->maxremoteuser = hc->maxresponse = 0; +#ifdef TILDE_MAP_2 + hc->maxaltdir = 0; +#endif /* TILDE_MAP_2 */ + httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, 1); + httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, 1); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, 0); + httpd_realloc_str(&hc->encodings, &hc->maxencodings, 0); + httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, 0); + httpd_realloc_str(&hc->query, &hc->maxquery, 0); + httpd_realloc_str(&hc->accept, &hc->maxaccept, 0); + httpd_realloc_str(&hc->accepte, &hc->maxaccepte, 0); + httpd_realloc_str(&hc->reqhost, &hc->maxreqhost, 0); + httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, 0); + httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, 0); + httpd_realloc_str(&hc->response, &hc->maxresponse, 0); +#ifdef TILDE_MAP_2 + httpd_realloc_str(&hc->altdir, &hc->maxaltdir, 0); +#endif /* TILDE_MAP_2 */ + hc->initialized = 1; + } + + /* Accept the new connection. */ + sz = sizeof(sa); + hc->conn_fd = accept(listen_fd, &sa.sa, &sz); + if (hc->conn_fd < 0) + { + if (errno == EWOULDBLOCK) + return GC_NO_MORE; + syslog(LOG_ERR, "accept - %m"); + return GC_FAIL; + } + if (!sockaddr_check(&sa)) + { + syslog(LOG_ERR, "unknown sockaddr family"); + close(hc->conn_fd); + hc->conn_fd = -1; + return GC_FAIL; + } + (void) fcntl(hc->conn_fd, F_SETFD, 1); + hc->hs = hs; + (void) memset(&hc->client_addr, 0, sizeof(hc->client_addr)); + (void) memmove(&hc->client_addr, &sa, sockaddr_len(&sa)); + hc->read_idx = 0; + hc->checked_idx = 0; + hc->checked_state = CHST_FIRSTWORD; + hc->method = METHOD_UNKNOWN; + hc->status = 0; + hc->bytes_to_send = 0; + hc->bytes_sent = 0; + hc->encodedurl = ""; + hc->decodedurl[0] = '\0'; + hc->protocol = "UNKNOWN"; + hc->origfilename[0] = '\0'; + hc->expnfilename[0] = '\0'; + hc->encodings[0] = '\0'; + hc->pathinfo[0] = '\0'; + hc->query[0] = '\0'; + hc->referer = ""; + hc->useragent = ""; + hc->accept[0] = '\0'; + hc->accepte[0] = '\0'; + hc->acceptl = ""; + hc->cookie = ""; + hc->contenttype = ""; +#ifdef X_CGI_HEADER + hc->xcgi = ""; +#endif + hc->reqhost[0] = '\0'; + hc->hdrhost = ""; + hc->hostdir[0] = '\0'; + hc->authorization = ""; + hc->remoteuser[0] = '\0'; + hc->response[0] = '\0'; +#ifdef TILDE_MAP_2 + hc->altdir[0] = '\0'; +#endif /* TILDE_MAP_2 */ + hc->responselen = 0; + hc->if_modified_since = (time_t) - 1; + hc->range_if = (time_t) - 1; + hc->contentlength = -1; + hc->type = ""; + hc->hostname = (char *) 0; + hc->mime_flag = 1; + hc->one_one = 0; + hc->got_range = 0; + hc->tildemapped = 0; + hc->first_byte_index = 0; + hc->last_byte_index = -1; + hc->keep_alive = 0; + hc->should_linger = 0; + hc->file_address = (char *) 0; + return GC_OK; +} + + +/* Checks hc->read_buf to see whether a complete request has been read so far; +** either the first line has two words (an HTTP/0.9 request), or the first +** line has three words and there's a blank line present. +** +** hc->read_idx is how much has been read in; hc->checked_idx is how much we +** have checked so far; and hc->checked_state is the current state of the +** finite state machine. +*/ +int httpd_got_request(httpd_conn * hc) +{ + char c; + + for (; hc->checked_idx < hc->read_idx; ++hc->checked_idx) + { + c = hc->read_buf[hc->checked_idx]; + switch (hc->checked_state) + { + case CHST_FIRSTWORD: + switch (c) + { + case ' ': + case '\t': + hc->checked_state = CHST_FIRSTWS; + break; + case '\012': + case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_FIRSTWS: + switch (c) + { + case ' ': + case '\t': + break; + case '\012': + case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_SECONDWORD; + break; + } + break; + case CHST_SECONDWORD: + switch (c) + { + case ' ': + case '\t': + hc->checked_state = CHST_SECONDWS; + break; + case '\012': + case '\015': + /* The first line has only two words - an HTTP/0.9 request. */ + return GR_GOT_REQUEST; + } + break; + case CHST_SECONDWS: + switch (c) + { + case ' ': + case '\t': + break; + case '\012': + case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_THIRDWORD; + break; + } + break; + case CHST_THIRDWORD: + switch (c) + { + case ' ': + case '\t': + hc->checked_state = CHST_THIRDWS; + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_THIRDWS: + switch (c) + { + case ' ': + case '\t': + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_LINE: + switch (c) + { + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_LF: + switch (c) + { + case '\012': + /* Two newlines in a row - a blank line - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CR: + switch (c) + { + case '\012': + hc->checked_state = CHST_CRLF; + break; + case '\015': + /* Two returns in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLF: + switch (c) + { + case '\012': + /* Two newlines in a row - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CRLFCR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLFCR: + switch (c) + { + case '\012': + case '\015': + /* Two CRLFs or two CRs in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_BOGUS: + return GR_BAD_REQUEST; + } + } + return GR_NO_REQUEST; +} + + +int httpd_parse_request(httpd_conn * hc) +{ + char *buf; + char *method_str; + char *url; + char *protocol; + char *reqhost; + char *eol; + char *cp; + char *pi; + + hc->checked_idx = 0; /* reset */ + method_str = bufgets(hc); + url = strpbrk(method_str, " \t\012\015"); + if (url == (char *) 0) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + *url++ = '\0'; + url += strspn(url, " \t\012\015"); + protocol = strpbrk(url, " \t\012\015"); + if (protocol == (char *) 0) + { + protocol = "HTTP/0.9"; + hc->mime_flag = 0; + } + else + { + *protocol++ = '\0'; + protocol += strspn(protocol, " \t\012\015"); + if (*protocol != '\0') + { + eol = strpbrk(protocol, " \t\012\015"); + if (eol != (char *) 0) + *eol = '\0'; + if (strcasecmp(protocol, "HTTP/1.0") != 0) + hc->one_one = 1; + } + } + hc->protocol = protocol; + + /* Check for HTTP/1.1 absolute URL. */ + if (strncasecmp(url, "http://", 7) == 0) + { + if (!hc->one_one) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + reqhost = url + 7; + url = strchr(reqhost, '/'); + if (url == (char *) 0) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + *url = '\0'; + if (strchr(reqhost, '/') != (char *) 0 || reqhost[0] == '.') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + httpd_realloc_str(&hc->reqhost, &hc->maxreqhost, strlen(reqhost)); + (void) strcpy(hc->reqhost, reqhost); + *url = '/'; + } + + if (*url != '/') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + + if (strcasecmp(method_str, httpd_method_str(METHOD_GET)) == 0) + hc->method = METHOD_GET; + else if (strcasecmp(method_str, httpd_method_str(METHOD_HEAD)) == 0) + hc->method = METHOD_HEAD; + else if (strcasecmp(method_str, httpd_method_str(METHOD_POST)) == 0) + hc->method = METHOD_POST; + else + { + httpd_send_err(hc, 501, err501title, "", err501form, method_str); + return -1; + } + + hc->encodedurl = url; + httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, + strlen(hc->encodedurl)); + strdecode(hc->decodedurl, hc->encodedurl); + + httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, + strlen(hc->decodedurl)); + + pi = &hc->decodedurl[1]; + while (*pi == '/') + pi++; + + (void) strcpy(hc->origfilename, pi); + + /* Special case for top-level URL. */ + //if ( hc->origfilename[0] == '\0' ) + // (void) strcpy( hc->origfilename, "." ); + + /* Extract query string from encoded URL. */ + cp = strchr(hc->encodedurl, '?'); + if (cp != (char *) 0) + { + ++cp; + httpd_realloc_str(&hc->query, &hc->maxquery, strlen(cp)); + (void) strcpy(hc->query, cp); + /* Remove query from (decoded) origfilename. */ + cp = strchr(hc->origfilename, '?'); + if (cp != (char *) 0) + *cp = '\0'; + } + + de_dotdot(hc->origfilename); + + if (hc->origfilename[0] == '/' || + (hc->origfilename[0] == '.' && hc->origfilename[1] == '.' && + (hc->origfilename[2] == '\0' || hc->origfilename[2] == '/'))) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + + if (hc->mime_flag) + { + /* Read the MIME headers. */ + while ((buf = bufgets(hc)) != (char *) 0) + { + if (buf[0] == '\0') + break; + if (strncasecmp(buf, "Referer:", 8) == 0) + { + cp = &buf[8]; + cp += strspn(cp, " \t"); + hc->referer = cp; + } + else if (strncasecmp(buf, "User-Agent:", 11) == 0) + { + cp = &buf[11]; + cp += strspn(cp, " \t"); + hc->useragent = cp; + } + else if (strncasecmp(buf, "Host:", 5) == 0) + { + cp = &buf[5]; + cp += strspn(cp, " \t"); + hc->hdrhost = cp; + cp = strchr(hc->hdrhost, ':'); + if (cp != (char *) 0) + *cp = '\0'; + if (strchr(hc->hdrhost, '/') != (char *) 0 || hc->hdrhost[0] == '.') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, + ""); + return -1; + } + } + else if (strncasecmp(buf, "Accept:", 7) == 0) + { + cp = &buf[7]; + cp += strspn(cp, " \t"); + if (hc->accept[0] != '\0') + { + if (strlen(hc->accept) > 5000) + { + syslog(LOG_ERR, "%.80s way too much Accept: data", + httpd_ntoa(&hc->client_addr)); + continue; + } + httpd_realloc_str(&hc->accept, &hc->maxaccept, + strlen(hc->accept) + 2 + strlen(cp)); + (void) strcat(hc->accept, ", "); + } + else + httpd_realloc_str(&hc->accept, &hc->maxaccept, strlen(cp)); + + (void) strcat(hc->accept, cp); + } + else if (strncasecmp(buf, "Accept-Encoding:", 16) == 0) + { + cp = &buf[16]; + cp += strspn(cp, " \t"); + if (hc->accepte[0] != '\0') + { + if (strlen(hc->accepte) > 5000) + { + syslog(LOG_ERR, "%.80s way too much Accept-Encoding: data", + httpd_ntoa(&hc->client_addr)); + continue; + } + httpd_realloc_str(&hc->accepte, &hc->maxaccepte, + strlen(hc->accepte) + 2 + strlen(cp)); + (void) strcat(hc->accepte, ", "); + } + else + httpd_realloc_str(&hc->accepte, &hc->maxaccepte, strlen(cp)); + + (void) strcpy(hc->accepte, cp); + } + else if (strncasecmp(buf, "Accept-Language:", 16) == 0) + { + cp = &buf[16]; + cp += strspn(cp, " \t"); + hc->acceptl = cp; + } + else if (strncasecmp(buf, "If-Modified-Since:", 18) == 0) + { + cp = &buf[18]; + hc->if_modified_since = tdate_parse(cp); + if (hc->if_modified_since == (time_t) - 1) + syslog(LOG_DEBUG, "unparsable time: %.80s", cp); + } + else if (strncasecmp(buf, "Cookie:", 7) == 0) + { + cp = &buf[7]; + cp += strspn(cp, " \t"); + hc->cookie = cp; + } + else if (strncasecmp(buf, "Range:", 6) == 0) + { + /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */ + if (strchr(buf, ',') == (char *) 0) + { + char *cp_dash; + cp = strpbrk(buf, "="); + if (cp != (char *) 0) + { + cp_dash = strchr(cp + 1, '-'); + if (cp_dash != (char *) 0 && cp_dash != cp + 1) + { + *cp_dash = '\0'; + hc->got_range = 1; + hc->first_byte_index = atoll(cp + 1); + if (hc->first_byte_index < 0) + hc->first_byte_index = 0; + if (isdigit((int) cp_dash[1])) + { + hc->last_byte_index = atoll(cp_dash + 1); + if (hc->last_byte_index < 0) + hc->last_byte_index = -1; + } + } + } + } + } + else if (strncasecmp(buf, "Range-If:", 9) == 0 || + strncasecmp(buf, "If-Range:", 9) == 0) + { + cp = &buf[9]; + hc->range_if = tdate_parse(cp); + if (hc->range_if == (time_t) - 1) + syslog(LOG_DEBUG, "unparsable time: %.80s", cp); + } + else if (strncasecmp(buf, "Content-Type:", 13) == 0) + { + cp = &buf[13]; + cp += strspn(cp, " \t"); + hc->contenttype = cp; + } + else if (strncasecmp(buf, "Content-Length:", 15) == 0) + { + cp = &buf[15]; + hc->contentlength = atol(cp); + } + else if (strncasecmp(buf, "Authorization:", 14) == 0) + { + cp = &buf[14]; + cp += strspn(cp, " \t"); + hc->authorization = cp; + } + else if (strncasecmp(buf, "Connection:", 11) == 0) + { + cp = &buf[11]; + cp += strspn(cp, " \t"); + if (strcasecmp(cp, "keep-alive") == 0) + hc->keep_alive = 1; + } +#ifdef X_CGI_HEADER + else if (strncasecmp(buf, "X-Cgi:", 6) == 0) + { + cp = &buf[6]; + cp += strspn(cp, " \t"); + hc->xcgi = cp; + } +#endif +#ifdef LOG_UNKNOWN_HEADERS + else if (strncasecmp(buf, "Accept-Charset:", 15) == 0 || + strncasecmp(buf, "Accept-Language:", 16) == 0 || + strncasecmp(buf, "Agent:", 6) == 0 || + strncasecmp(buf, "Cache-Control:", 14) == 0 || + strncasecmp(buf, "Cache-Info:", 11) == 0 || + strncasecmp(buf, "Charge-To:", 10) == 0 || + strncasecmp(buf, "Client-IP:", 10) == 0 || + strncasecmp(buf, "Date:", 5) == 0 || + strncasecmp(buf, "Extension:", 10) == 0 || + strncasecmp(buf, "Forwarded:", 10) == 0 || + strncasecmp(buf, "From:", 5) == 0 || + strncasecmp(buf, "HTTP-Version:", 13) == 0 || + strncasecmp(buf, "Max-Forwards:", 13) == 0 || + strncasecmp(buf, "Message-Id:", 11) == 0 || + strncasecmp(buf, "MIME-Version:", 13) == 0 || + strncasecmp(buf, "Negotiate:", 10) == 0 || + strncasecmp(buf, "Pragma:", 7) == 0 || + strncasecmp(buf, "Proxy-Agent:", 12) == 0 || + strncasecmp(buf, "Proxy-Connection:", 17) == 0 || + strncasecmp(buf, "Security-Scheme:", 16) == 0 || + strncasecmp(buf, "Session-Id:", 11) == 0 || + strncasecmp(buf, "UA-Color:", 9) == 0 || + strncasecmp(buf, "UA-CPU:", 7) == 0 || + strncasecmp(buf, "UA-Disp:", 8) == 0 || + strncasecmp(buf, "UA-OS:", 6) == 0 || + strncasecmp(buf, "UA-Pixels:", 10) == 0 || + strncasecmp(buf, "User:", 5) == 0 || + strncasecmp(buf, "Via:", 4) == 0 || + strncasecmp(buf, "X-", 2) == 0) + ; /* ignore */ + else + syslog(LOG_DEBUG, "unknown request header: %.80s", buf); +#endif /* LOG_UNKNOWN_HEADERS */ + } + } + + if (hc->one_one) + { + /* Check that HTTP/1.1 requests specify a host, as required. */ + if (hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + + /* If the client wants to do keep-alives, it might also be doing + ** pipelining. There's no way for us to tell. Since we don't + ** implement keep-alives yet, if we close such a connection there + ** might be unread pipelined requests waiting. So, we have to + ** do a lingering close. + */ + if (hc->keep_alive) + hc->should_linger = 1; + } + + /* Ok, the request has been parsed. Now we resolve stuff that + ** may require the entire request. + */ + + /* Copy original filename to expanded filename. */ + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(hc->origfilename)); + (void) strcpy(hc->expnfilename, hc->origfilename); + + /* Tilde mapping. */ + if (hc->expnfilename[0] == '~') + { +#ifdef TILDE_MAP_1 + if (!tilde_map_1(hc)) + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 + if (!tilde_map_2(hc)) + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } +#endif /* TILDE_MAP_2 */ + } + + /* Virtual host mapping. */ + if (hc->hs->vhost) + if (!vhost_map(hc)) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + +#if 0 + /* Expand all symbolic links in the filename. This also gives us + ** any trailing non-existing components, for pathinfo. + */ + cp = + expand_symlinks(hc->expnfilename, &pi, hc->hs->no_symlink_check, + hc->tildemapped); + + //fprintf(stderr, "expnfilename: %s -> cp = %s pi = %s\n", hc->expnfilename, cp, pi); + + if (cp == (char *) 0) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, strlen(cp)); + (void) strcpy(hc->expnfilename, cp); + httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, strlen(pi)); + (void) strcpy(hc->pathinfo, pi); + + /* Remove pathinfo stuff from the original filename too. */ + if (hc->pathinfo[0] != '\0') + { + int i; + i = strlen(hc->origfilename) - strlen(hc->pathinfo); + if (i > 0 && strcmp(&hc->origfilename[i], hc->pathinfo) == 0) + hc->origfilename[i - 1] = '\0'; + } + + /* If the expanded filename is an absolute path, check that it's still + ** within the current directory or the alternate directory. + */ + if (hc->expnfilename[0] == '/') + { + if (strncmp(hc->expnfilename, hc->hs->cwd, strlen(hc->hs->cwd)) == 0) + { + /* Elide the current directory. */ + (void) strcpy(hc->expnfilename, &hc->expnfilename[strlen(hc->hs->cwd)]); + } +#ifdef TILDE_MAP_2 + else if (hc->altdir[0] != '\0' && + (strncmp(hc->expnfilename, hc->altdir, + strlen(hc->altdir)) == 0 && + (hc->expnfilename[strlen(hc->altdir)] == '\0' || + hc->expnfilename[strlen(hc->altdir)] == '/'))) + { + } +#endif /* TILDE_MAP_2 */ + else + { + syslog(LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n"), + hc->encodedurl); + return -1; + } + } +#endif + + return 0; +} + + +static char *bufgets(httpd_conn * hc) +{ + int i; + char c; + + for (i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx) + { + c = hc->read_buf[hc->checked_idx]; + if (c == '\012' || c == '\015') + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + if (c == '\015' && hc->checked_idx < hc->read_idx && + hc->read_buf[hc->checked_idx] == '\012') + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + } + return &(hc->read_buf[i]); + } + } + return (char *) 0; +} + + +static void de_dotdot(char *file) +{ + char *cp; + char *cp2; + int l; + + /* Collapse any multiple / sequences. */ + while ((cp = strstr(file, "//")) != (char *) 0) + { + for (cp2 = cp + 2; *cp2 == '/'; ++cp2) + continue; + (void) strcpy(cp + 1, cp2); + } + + /* Remove leading ./ and any /./ sequences. */ + while (strncmp(file, "./", 2) == 0) + (void) strcpy(file, file + 2); + while ((cp = strstr(file, "/./")) != (char *) 0) + (void) strcpy(cp, cp + 2); + + /* Alternate between removing leading ../ and removing xxx/../ */ + for (;;) + { + while (strncmp(file, "../", 3) == 0) + (void) strcpy(file, file + 3); + cp = strstr(file, "/../"); + if (cp == (char *) 0) + break; + for (cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2) + continue; + (void) strcpy(cp2 + 1, cp + 4); + } + + /* Also elide any xxx/.. at the end. */ + while ((l = strlen(file)) > 3 && strcmp((cp = file + l - 3), "/..") == 0) + { + for (cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2) + continue; + if (cp2 < file) + break; + *cp2 = '\0'; + } +} + + +void httpd_close_conn(httpd_conn * hc, struct timeval *nowP) +{ + make_log_entry(hc, nowP); + + if (hc->file_address != (char *) 0) + { + GB.ReleaseFile(hc->file_address, hc->file_len); + //mmc_unmap(hc->file_address, &(hc->sb), nowP); + hc->file_address = (char *) 0; + } + if (hc->conn_fd >= 0) + { + (void) close(hc->conn_fd); + hc->conn_fd = -1; + } +} + +void httpd_destroy_conn(httpd_conn * hc) +{ + if (hc->initialized) + { + free((void *) hc->read_buf); + free((void *) hc->decodedurl); + free((void *) hc->origfilename); + free((void *) hc->expnfilename); + free((void *) hc->encodings); + free((void *) hc->pathinfo); + free((void *) hc->query); + free((void *) hc->accept); + free((void *) hc->accepte); + free((void *) hc->reqhost); + free((void *) hc->hostdir); + free((void *) hc->remoteuser); + free((void *) hc->response); +#ifdef TILDE_MAP_2 + free((void *) hc->altdir); +#endif /* TILDE_MAP_2 */ + hc->initialized = 0; + } +} + + +struct mime_entry +{ + char *ext; + size_t ext_len; + char *val; + size_t val_len; +}; +static struct mime_entry enc_tab[] = { +#include "mime_encodings.h" +}; + +static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab); +static struct mime_entry typ_tab[] = { +#include "mime_types.h" +}; + +static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab); + + +/* qsort comparison routine - declared old-style on purpose, for portability. */ +static int ext_compare(a, b) + struct mime_entry *a; + struct mime_entry *b; +{ + return strcmp(a->ext, b->ext); +} + + +static void init_mime(void) +{ + int i; + + /* Sort the tables so we can do binary search. */ + qsort(enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare); + qsort(typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare); + + /* Fill in the lengths. */ + for (i = 0; i < n_enc_tab; ++i) + { + enc_tab[i].ext_len = strlen(enc_tab[i].ext); + enc_tab[i].val_len = strlen(enc_tab[i].val); + } + for (i = 0; i < n_typ_tab; ++i) + { + typ_tab[i].ext_len = strlen(typ_tab[i].ext); + typ_tab[i].val_len = strlen(typ_tab[i].val); + } + +} + +/* Figure out MIME encodings and type based on the filename. Multiple +** encodings are separated by commas, and are listed in the order in +** which they were applied to the file. +*/ +static void figure_mime(httpd_conn * hc) +{ + char *prev_dot; + char *dot; + char *ext; + int me_indexes[100], n_me_indexes; + size_t ext_len, encodings_len; + int i, top, bot, mid; + int r; + char *default_type = "text/plain; charset=%s"; + + /* Peel off encoding extensions until there aren't any more. */ + n_me_indexes = 0; + for (prev_dot = &hc->expnfilename[strlen(hc->expnfilename)];; + prev_dot = dot) + { + for (dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot) + ; + if (dot < hc->expnfilename) + { + /* No dot found. No more encoding extensions, and no type + ** extension either. + */ + hc->type = default_type; + goto done; + } + ext = dot + 1; + ext_len = prev_dot - ext; + /* Search the encodings table. Linear search is fine here, there + ** are only a few entries. + */ + for (i = 0; i < n_enc_tab; ++i) + { + if (ext_len == enc_tab[i].ext_len + && strncasecmp(ext, enc_tab[i].ext, ext_len) == 0) + { + if (n_me_indexes < sizeof(me_indexes) / sizeof(*me_indexes)) + { + me_indexes[n_me_indexes] = i; + ++n_me_indexes; + } + goto next; + } + } + /* No encoding extension found. Break and look for a type extension. */ + break; + + next:; + } + + /* Binary search for a matching type extension. */ + top = n_typ_tab - 1; + bot = 0; + while (top >= bot) + { + mid = (top + bot) / 2; + r = strncasecmp(ext, typ_tab[mid].ext, ext_len); + if (r < 0) + top = mid - 1; + else if (r > 0) + bot = mid + 1; + else if (ext_len < typ_tab[mid].ext_len) + top = mid - 1; + else if (ext_len > typ_tab[mid].ext_len) + bot = mid + 1; + else + { + hc->type = typ_tab[mid].val; + goto done; + } + } + hc->type = default_type; + +done: + + /* The last thing we do is actually generate the mime-encoding header. */ + hc->encodings[0] = '\0'; + encodings_len = 0; + for (i = n_me_indexes - 1; i >= 0; --i) + { + httpd_realloc_str(&hc->encodings, &hc->maxencodings, + encodings_len + enc_tab[me_indexes[i]].val_len + 1); + if (hc->encodings[0] != '\0') + { + (void) strcpy(&hc->encodings[encodings_len], ","); + ++encodings_len; + } + (void) strcpy(&hc->encodings[encodings_len], enc_tab[me_indexes[i]].val); + encodings_len += enc_tab[me_indexes[i]].val_len; + } + +} + +#ifdef CGI_TIMELIMIT +static void cgi_kill2(ClientData client_data, struct timeval *nowP) +{ + pid_t pid; + + pid = (pid_t) client_data.i; + if (kill(pid, SIGKILL) == 0) + syslog(LOG_ERR, "hard-killed CGI process %d", pid); +} + +static void cgi_kill(ClientData client_data, struct timeval *nowP) +{ + pid_t pid; + + pid = (pid_t) client_data.i; + if (kill(pid, SIGINT) == 0) + { + syslog(LOG_ERR, "killed CGI process %d", pid); + /* In case this isn't enough, schedule an uncatchable kill. */ + if (tmr_create(nowP, cgi_kill2, client_data, 5 * 1000L, 0) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(cgi_kill2) failed"); + exit(1); + } + } +} +#endif /* CGI_TIMELIMIT */ + + +#ifdef GENERATE_INDEXES + +/* qsort comparison routine - declared old-style on purpose, for portability. */ +static int name_compare(a, b) + char **a; + char **b; +{ + return strcmp(*a, *b); +} + +#if 0 +static int ls(httpd_conn * hc) +{ + DIR *dirp; + struct dirent *de; + int namlen; + static int maxnames = 0; + int nnames; + static char *names; + static char **nameptrs; + static char *name; + static size_t maxname = 0; + static char *rname; + static size_t maxrname = 0; + static char *encrname; + static size_t maxencrname = 0; + FILE *fp; + int i, r; + struct stat sb; + struct stat lsb; + char modestr[20]; + char *linkprefix; + char link[MAXPATHLEN + 1]; + int linklen; + char *fileclass; + time_t now; + char *timestr; + ClientData client_data; + + dirp = opendir(hc->expnfilename); + if (dirp == (DIR *) 0) + { + syslog(LOG_ERR, "opendir %.80s - %m", hc->expnfilename); + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } + + if (hc->method == METHOD_HEAD) + { + closedir(dirp); + send_mime(hc, 200, ok200title, "", "", "text/html; charset=%s", + (off_t) - 1, hc->sb.mtime); + } + else if (hc->method == METHOD_GET) + { + if (hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit) + { + closedir(dirp); + httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl); + return -1; + } + ++hc->hs->cgi_count; + r = fork(); + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + closedir(dirp); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + if (r == 0) + { + /* Child process. */ + sub_process = 1; + httpd_unlisten(hc->hs); + send_mime(hc, 200, ok200title, "", "", "text/html; charset=%s", + (off_t) - 1, hc->sb.mtime); + httpd_write_response(hc); + +#ifdef CGI_NICE + /* Set priority. */ + (void) nice(CGI_NICE); +#endif /* CGI_NICE */ + + /* Open a stdio stream so that we can use fprintf, which is more + ** efficient than a bunch of separate write()s. We don't have + ** to worry about double closes or file descriptor leaks cause + ** we're in a subprocess. + */ + fp = fdopen(hc->conn_fd, "w"); + if (fp == (FILE *) 0) + { + syslog(LOG_ERR, "fdopen - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + closedir(dirp); + exit(1); + } + + (void) fprintf(fp, "\ +\n\ +Index of %.80s\n\ +\n\ +

    Index of %.80s

    \n\ +
    \n\
    +mode  links  bytes  last-changed  name\n\
    +
    ", hc->encodedurl, hc->encodedurl); + + /* Read in names. */ + nnames = 0; + while ((de = readdir(dirp)) != 0) /* dirent or direct */ + { + if (nnames >= maxnames) + { + if (maxnames == 0) + { + maxnames = 100; + names = NEW(char, maxnames * (MAXPATHLEN + 1)); + nameptrs = NEW(char *, maxnames); + } + else + { + maxnames *= 2; + names = RENEW(names, char, maxnames * (MAXPATHLEN + 1)); + nameptrs = RENEW(nameptrs, char *, maxnames); + } + if (names == (char *) 0 || nameptrs == (char **) 0) + { + syslog(LOG_ERR, "out of memory reallocating directory names"); + exit(1); + } + for (i = 0; i < maxnames; ++i) + nameptrs[i] = &names[i * (MAXPATHLEN + 1)]; + } + namlen = NAMLEN(de); + (void) strncpy(nameptrs[nnames], de->d_name, namlen); + nameptrs[nnames][namlen] = '\0'; + ++nnames; + } + closedir(dirp); + + /* Sort the names. */ + qsort(nameptrs, nnames, sizeof(*nameptrs), name_compare); + + /* Generate output. */ + for (i = 0; i < nnames; ++i) + { + httpd_realloc_str(&name, &maxname, + strlen(hc->expnfilename) + 1 + strlen(nameptrs[i])); + httpd_realloc_str(&rname, &maxrname, + strlen(hc->origfilename) + 1 + strlen(nameptrs[i])); + if (hc->expnfilename[0] == '\0' || strcmp(hc->expnfilename, ".") == 0) + { + (void) strcpy(name, nameptrs[i]); + (void) strcpy(rname, nameptrs[i]); + } + else + { + (void) my_snprintf(name, maxname, + "%s/%s", hc->expnfilename, nameptrs[i]); + if (strcmp(hc->origfilename, ".") == 0) + (void) my_snprintf(rname, maxrname, "%s", nameptrs[i]); + else + (void) my_snprintf(rname, maxrname, + "%s%s", hc->origfilename, nameptrs[i]); + } + httpd_realloc_str(&encrname, &maxencrname, 3 * strlen(rname) + 1); + strencode(encrname, maxencrname, rname); + + if (stat(name, &sb) < 0 || lstat(name, &lsb) < 0) + continue; + + linkprefix = ""; + link[0] = '\0'; + /* Break down mode word. First the file type. */ + switch (lsb.st_mode & S_IFMT) + { + case S_IFIFO: + modestr[0] = 'p'; + break; + case S_IFCHR: + modestr[0] = 'c'; + break; + case S_IFDIR: + modestr[0] = 'd'; + break; + case S_IFBLK: + modestr[0] = 'b'; + break; + case S_IFREG: + modestr[0] = '-'; + break; + case S_IFSOCK: + modestr[0] = 's'; + break; + case S_IFLNK: + modestr[0] = 'l'; + linklen = readlink(name, link, sizeof(link) - 1); + if (linklen != -1) + { + link[linklen] = '\0'; + linkprefix = " -> "; + } + break; + default: + modestr[0] = '?'; + break; + } + /* Now the world permissions. Owner and group permissions + ** are not of interest to web clients. + */ + modestr[1] = (lsb.st_mode & S_IROTH) ? 'r' : '-'; + modestr[2] = (lsb.st_mode & S_IWOTH) ? 'w' : '-'; + modestr[3] = (lsb.st_mode & S_IXOTH) ? 'x' : '-'; + modestr[4] = '\0'; + + /* We also leave out the owner and group name, they are + ** also not of interest to web clients. Plus if we're + ** running under chroot(), they would require a copy + ** of /etc/passwd and /etc/group, which we want to avoid. + */ + + /* Get time string. */ + now = time((time_t *) 0); + timestr = ctime(&lsb.st_mtime); + timestr[0] = timestr[4]; + timestr[1] = timestr[5]; + timestr[2] = timestr[6]; + timestr[3] = ' '; + timestr[4] = timestr[8]; + timestr[5] = timestr[9]; + timestr[6] = ' '; + if (now - lsb.st_mtime > 60 * 60 * 24 * 182) /* 1/2 year */ + { + timestr[7] = ' '; + timestr[8] = timestr[20]; + timestr[9] = timestr[21]; + timestr[10] = timestr[22]; + timestr[11] = timestr[23]; + } + else + { + timestr[7] = timestr[11]; + timestr[8] = timestr[12]; + timestr[9] = ':'; + timestr[10] = timestr[14]; + timestr[11] = timestr[15]; + } + timestr[12] = '\0'; + + /* The ls -F file class. */ + switch (sb.st_mode & S_IFMT) + { + case S_IFDIR: + fileclass = "/"; + break; + case S_IFSOCK: + fileclass = "="; + break; + case S_IFLNK: + fileclass = "@"; + break; + default: + fileclass = (sb.st_mode & S_IXOTH) ? "*" : ""; + break; + } + + /* And print. */ + (void) fprintf(fp, + "%s %3ld %10ld %s %.80s%s%s%s\n", + modestr, (long) lsb.st_nlink, (int64_t) lsb.st_size, + timestr, encrname, S_ISDIR(sb.st_mode) ? "/" : "", + nameptrs[i], linkprefix, link, fileclass); + } + + (void) fprintf(fp, "
    \n\n"); + (void) fclose(fp); + exit(0); + } + + /* Parent process. */ + closedir(dirp); + syslog(LOG_INFO, "spawned indexing process %d for directory '%.200s'", r, + hc->expnfilename); +#ifdef CGI_TIMELIMIT + /* Schedule a kill for the child process, in case it runs too long */ + if (hc->hs->cgi_timelimit) + { + client_data.i = r; + if (tmr_create + ((struct timeval *) 0, cgi_kill, client_data, + hc->hs->cgi_timelimit * 1000L, 0) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(cgi_kill ls) failed"); + exit(1); + } + } +#endif /* CGI_TIMELIMIT */ + hc->status = 200; + hc->bytes_sent = CGI_BYTECOUNT; + hc->should_linger = 0; + } + else + { + closedir(dirp); + httpd_send_err(hc, 501, err501title, "", err501form, + httpd_method_str(hc->method)); + return -1; + } + + return 0; +} +#endif + +#endif /* GENERATE_INDEXES */ + + +static char *build_env(char *fmt, char *arg) +{ + char *cp; + size_t size; + static char *buf; + static size_t maxbuf = 0; + + size = strlen(fmt) + strlen(arg); + if (size > maxbuf) + httpd_realloc_str(&buf, &maxbuf, size); + + (void) my_snprintf(buf, maxbuf, fmt, arg); + cp = strdup(buf); + if (cp == (char *) 0) + { + syslog(LOG_ERR, "out of memory copying environment variable"); + exit(1); + } + //fprintf(stderr, "build_env: %s\n", cp); + return cp; +} + + +#ifdef SERVER_NAME_LIST +static char *hostname_map(char *hostname) +{ + int len, n; + static char *list[] = { SERVER_NAME_LIST }; + + len = strlen(hostname); + for (n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n) + if (strncasecmp(hostname, list[n], len) == 0) + if (list[n][len] == '/') /* check in case of a substring match */ + return &list[n][len + 1]; + return (char *) 0; +} +#endif /* SERVER_NAME_LIST */ + + +/* Set up environment variables. Be real careful here to avoid +** letting malicious clients overrun a buffer. We don't have +** to worry about freeing stuff since we're a sub-process. +*/ +static char **make_envp(httpd_conn * hc) +{ + static char *envp[50]; + int envn; + char *cp; + char buf[256]; + + envn = 0; + //envp[envn++] = build_env("PATH=%s", CGI_PATH); +#ifdef CGI_LD_LIBRARY_PATH + envp[envn++] = build_env("LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH); +#endif /* CGI_LD_LIBRARY_PATH */ + envp[envn++] = build_env("SERVER_SOFTWARE=%s", SERVER_SOFTWARE); + /* If vhosting, use that server-name here. */ + if (hc->hs->vhost && hc->hostname != (char *) 0) + cp = hc->hostname; + else + cp = hc->hs->server_hostname; + if (cp != (char *) 0) + envp[envn++] = build_env("SERVER_NAME=%s", cp); + envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1"; + envp[envn++] = build_env("SERVER_PROTOCOL=%s", hc->protocol); + (void) my_snprintf(buf, sizeof(buf), "%d", (int) hc->hs->port); + envp[envn++] = build_env("SERVER_PORT=%s", buf); + envp[envn++] = build_env("REQUEST_METHOD=%s", httpd_method_str(hc->method)); + + /*if ( hc->pathinfo[0] != '\0' ) + { + char* cp2; + size_t l; + envp[envn++] = build_env( "PATH_INFO=/%s", hc->pathinfo ); + l = strlen( hc->hs->cwd ) + strlen( hc->pathinfo ) + 1; + cp2 = NEW( char, l ); + if ( cp2 != (char*) 0 ) + { + (void) my_snprintf( cp2, l, "%s%s", hc->hs->cwd, hc->pathinfo ); + envp[envn++] = build_env( "PATH_TRANSLATED=%s", cp2 ); + } + } */ + + envp[envn++] = "SCRIPT_NAME=/"; + + envp[envn++] = build_env("PATH_INFO=/%s", hc->expnfilename); + //envp[envn++] = build_env( "SCRIPT_NAME=%s", ""); + + if (hc->query[0] != '\0') + envp[envn++] = build_env("QUERY_STRING=%s", hc->query); + envp[envn++] = build_env("REMOTE_ADDR=%s", httpd_ntoa(&hc->client_addr)); + if (hc->referer[0] != '\0') + envp[envn++] = build_env("HTTP_REFERER=%s", hc->referer); + if (hc->useragent[0] != '\0') + envp[envn++] = build_env("HTTP_USER_AGENT=%s", hc->useragent); + if (hc->accept[0] != '\0') + envp[envn++] = build_env("HTTP_ACCEPT=%s", hc->accept); + if (hc->accepte[0] != '\0') + envp[envn++] = build_env("HTTP_ACCEPT_ENCODING=%s", hc->accepte); + if (hc->acceptl[0] != '\0') + envp[envn++] = build_env("HTTP_ACCEPT_LANGUAGE=%s", hc->acceptl); + if (hc->cookie[0] != '\0') + envp[envn++] = build_env("HTTP_COOKIE=%s", hc->cookie); + if (hc->contenttype[0] != '\0') + envp[envn++] = build_env("CONTENT_TYPE=%s", hc->contenttype); + if (hc->hdrhost[0] != '\0') + envp[envn++] = build_env("HTTP_HOST=%s", hc->hdrhost); + + if (hc->contentlength != -1) + { + (void) my_snprintf(buf, sizeof(buf), "%lu", + (unsigned long) hc->contentlength); + envp[envn++] = build_env("CONTENT_LENGTH=%s", buf); + } + if (hc->remoteuser[0] != '\0') + envp[envn++] = build_env("REMOTE_USER=%s", hc->remoteuser); + if (hc->authorization[0] != '\0') + envp[envn++] = build_env("AUTH_TYPE=%s", "Basic"); + + /* We only support Basic auth at the moment. */ + if (getenv("TZ") != (char *) 0) + envp[envn++] = build_env("TZ=%s", getenv("TZ")); + + //fprintf(stderr, "make_envp #2.4: %d %p\n", getpid(), hc->hs->cgi_pattern); + //envp[envn++] = build_env( "CGI_PATTERN=%s", hc->hs->cgi_pattern ); + +#ifdef X_CGI_HEADER + //if (hc->xcgi[0]) + envp[envn++] = build_env("X_CGI=%s", hc->xcgi); + if (hc->if_modified_since != (time_t) - 1) + { + my_snprintf(buf, sizeof(buf), "%lld", (int64_t) (hc->if_modified_since)); + envp[envn++] = build_env("HTTP_IF_MODIFIED_SINCE=%s", buf); + } +#endif + + envp[envn] = (char *) 0; + return envp; +} + + +/* Set up argument vector. Again, we don't have to worry about freeing stuff +** since we're a sub-process. This gets done after make_envp() because we +** scribble on hc->query. +*/ +static char **make_argp(httpd_conn * hc) +{ + char **argp; + int argn; + char *cp1; + char *cp2; + + /* By allocating an arg slot for every character in the query, plus + ** one for the filename and one for the NULL, we are guaranteed to + ** have enough. We could actually use strlen/2. + */ + argp = NEW(char *, strlen(hc->query) + 2); + if (argp == (char **) 0) + return (char **) 0; + + argp[0] = strrchr(hc->expnfilename, '/'); + if (argp[0] != (char *) 0) + ++argp[0]; + else + argp[0] = hc->expnfilename; + + argn = 1; + /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html, + ** "The server should search the query information for a non-encoded = + ** character to determine if the command line is to be used, if it finds + ** one, the command line is not to be used." + */ + if (strchr(hc->query, '=') == (char *) 0) + { + for (cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2) + { + if (*cp2 == '+') + { + *cp2 = '\0'; + strdecode(cp1, cp1); + argp[argn++] = cp1; + cp1 = cp2 + 1; + } + } + if (cp2 != cp1) + { + strdecode(cp1, cp1); + argp[argn++] = cp1; + } + } + + argp[argn] = (char *) 0; + return argp; +} + + +/* This routine is used only for POST requests. It reads the data +** from the request and sends it to the child process. The only reason +** we need to do it this way instead of just letting the child read +** directly is that we have already read part of the data into our +** buffer. +*/ +static void cgi_interpose_input(httpd_conn * hc, int wfd) +{ + size_t c; + ssize_t r; + char buf[1024]; + + c = hc->read_idx - hc->checked_idx; + if (c > 0) + { + if (httpd_write_fully(wfd, &(hc->read_buf[hc->checked_idx]), c) != c) + return; + } + while (c < hc->contentlength) + { + r = read(hc->conn_fd, buf, MIN(sizeof(buf), hc->contentlength - c)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r <= 0) + return; + if (httpd_write_fully(wfd, buf, r) != r) + return; + c += r; + } + post_post_garbage_hack(hc); +} + + +/* Special hack to deal with broken browsers that send a LF or CRLF +** after POST data, causing TCP resets - we just read and discard up +** to 2 bytes. Unfortunately this doesn't fix the problem for CGIs +** which avoid the interposer process due to their POST data being +** short. Creating an interposer process for all POST CGIs is +** unacceptably expensive. The eventual fix will come when interposing +** gets integrated into the main loop as a tasklet instead of a process. +*/ +static void post_post_garbage_hack(httpd_conn * hc) +{ + char buf[2]; + + /* If we are in a sub-process, turn on no-delay mode in case we + ** previously cleared it. + */ + if (sub_process) + httpd_set_ndelay(hc->conn_fd); + /* And read up to 2 bytes. */ + (void) read(hc->conn_fd, buf, sizeof(buf)); +} + + +/* This routine is used for parsed-header CGIs. The idea here is that the +** CGI can return special headers such as "Status:" and "Location:" which +** change the return status of the response. Since the return status has to +** be the very first line written out, we have to accumulate all the headers +** and check for the special ones before writing the status. Then we write +** out the saved headers and proceed to echo the rest of the response. +*/ +static void cgi_interpose_output(httpd_conn * hc, int rfd) +{ + int r; + char buf[1024]; + size_t headers_size, headers_len; + char *headers; + char *br; + int status; + char *title; + char *cp; + + /* Make sure the connection is in blocking mode. It should already + ** be blocking, but we might as well be sure. + */ + httpd_clear_ndelay(hc->conn_fd); + + /* Slurp in all headers. */ + headers_size = 0; + httpd_realloc_str(&headers, &headers_size, 500); + headers_len = 0; + for (;;) + { + r = read(rfd, buf, sizeof(buf)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r <= 0) + { + br = &(headers[headers_len]); + break; + } + httpd_realloc_str(&headers, &headers_size, headers_len + r); + (void) memmove(&(headers[headers_len]), buf, r); + headers_len += r; + headers[headers_len] = '\0'; + if ((br = strstr(headers, "\015\012\015\012")) != (char *) 0 || + (br = strstr(headers, "\012\012")) != (char *) 0) + break; + } + + /* If there were no headers, bail. */ + if (headers[0] == '\0') + return; + + /* Figure out the status. Look for a Status: or Location: header; + ** else if there's an HTTP header line, get it from there; else + ** default to 200. + */ + status = 200; + if (strncmp(headers, "HTTP/", 5) == 0) + { + cp = headers; + cp += strcspn(cp, " \t"); + status = atoi(cp); + } + if ((cp = strstr(headers, "Status:")) != (char *) 0 && + cp < br && (cp == headers || *(cp - 1) == '\012')) + { + cp += 7; + cp += strspn(cp, " \t"); + status = atoi(cp); + } + if ((cp = strstr(headers, "Location:")) != (char *) 0 && + cp < br && (cp == headers || *(cp - 1) == '\012')) + status = 302; + + /* Write the status line. */ + switch (status) + { + case 200: + title = ok200title; + break; + case 302: + title = err302title; + break; + case 304: + title = err304title; + break; + case 400: + title = httpd_err400title; + break; +#ifdef AUTH_FILE + case 401: + title = err401title; + break; +#endif /* AUTH_FILE */ + case 403: + title = err403title; + break; + case 404: + title = err404title; + break; + case 408: + title = httpd_err408title; + break; + case 500: + title = err500title; + break; + case 501: + title = err501title; + break; + case 503: + title = httpd_err503title; + break; + default: + title = "Something"; + break; + } + (void) my_snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, + title); + (void) httpd_write_fully(hc->conn_fd, buf, strlen(buf)); + + /* Write the saved headers. */ + (void) httpd_write_fully(hc->conn_fd, headers, headers_len); + + /* Echo the rest of the output. */ + for (;;) + { + r = read(rfd, buf, sizeof(buf)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r <= 0) + break; + if (httpd_write_fully(hc->conn_fd, buf, r) != r) + break; + } + shutdown(hc->conn_fd, SHUT_WR); +} + + +/* CGI child process. */ +static void cgi_child(httpd_conn * hc) +{ + int r; + char **argp; + char **envp; + //char* binary; + //char* directory; + + /* Unset close-on-exec flag for this socket. This actually shouldn't + ** be necessary, according to POSIX a dup()'d file descriptor does + ** *not* inherit the close-on-exec flag, its flag is always clear. + ** However, Linux messes this up and does copy the flag to the + ** dup()'d descriptor, so we have to clear it. This could be + ** ifdeffed for Linux only. + */ + (void) fcntl(hc->conn_fd, F_SETFD, 0); + + /* Close the syslog descriptor so that the CGI program can't + ** mess with it. All other open descriptors should be either + ** the listen socket(s), sockets from accept(), or the file-logging + ** fd, and all of those are set to close-on-exec, so we don't + ** have to close anything else. + */ + closelog(); + + /* If the socket happens to be using one of the stdin/stdout/stderr + ** descriptors, move it to another descriptor so that the dup2 calls + ** below don't screw things up. We arbitrarily pick fd 3 - if there + ** was already something on it, we clobber it, but that doesn't matter + ** since at this point the only fd of interest is the connection. + ** All others will be closed on exec. + */ + if (hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO + || hc->conn_fd == STDERR_FILENO) + { + int newfd = dup2(hc->conn_fd, STDERR_FILENO + 1); + if (newfd >= 0) + hc->conn_fd = newfd; + /* If the dup2 fails, shrug. We'll just take our chances. + ** Shouldn't happen though. + */ + } + + /* Make the environment vector. */ + envp = make_envp(hc); + + /* Make the argument vector. */ + argp = make_argp(hc); + + /* Set up stdin. For POSTs we may have to set up a pipe from an + ** interposer process, depending on if we've read some of the data + ** into our buffer. + */ + if (hc->method == METHOD_POST && hc->read_idx > hc->checked_idx) + { + int p[2]; + + if (pipe(p) < 0) + { + syslog(LOG_ERR, "pipe - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + + r = fork(); + + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + + if (r == 0) + { + /* Interposer process. */ + sub_process = 1; + (void) close(p[0]); + cgi_interpose_input(hc, p[1]); + exit(0); + } + + /* Need to schedule a kill for process r; but in the main process! */ + (void) close(p[1]); + if (p[0] != STDIN_FILENO) + { + (void) dup2(p[0], STDIN_FILENO); + (void) close(p[0]); + } + } + else + { + /* Otherwise, the request socket is stdin. */ + if (hc->conn_fd != STDIN_FILENO) + (void) dup2(hc->conn_fd, STDIN_FILENO); + } + + /* Set up stdout/stderr. If we're doing CGI header parsing, + ** we need an output interposer too. + */ + if (strncmp(argp[0], "nph-", 4) != 0 && hc->mime_flag) + { + int p[2]; + + if (pipe(p) < 0) + { + syslog(LOG_ERR, "pipe - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + r = fork(); + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + if (r == 0) + { + /* Interposer process. */ + sub_process = 1; + (void) close(p[1]); + cgi_interpose_output(hc, p[0]); + exit(0); + } + /* Need to schedule a kill for process r; but in the main process! */ + (void) close(p[0]); + if (p[1] != STDOUT_FILENO) + (void) dup2(p[1], STDOUT_FILENO); + // BM: let stderr go to the IDE + //if (p[1] != STDERR_FILENO) + // (void) dup2(p[1], STDERR_FILENO); + if (p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO) + (void) close(p[1]); + } + else + { + /* Otherwise, the request socket is stdout/stderr. */ + if (hc->conn_fd != STDOUT_FILENO) + (void) dup2(hc->conn_fd, STDOUT_FILENO); + // BM: let stderr go to the IDE + // if (hc->conn_fd != STDERR_FILENO) + // (void) dup2(hc->conn_fd, STDERR_FILENO); + } + + /* At this point we would like to set close-on-exec again for hc->conn_fd + ** (see previous comments on Linux's broken behavior re: close-on-exec + ** and dup.) Unfortunately there seems to be another Linux problem, or + ** perhaps a different aspect of the same problem - if we do this + ** close-on-exec in Linux, the socket stays open but stderr gets + ** closed - the last fd duped from the socket. What a mess. So we'll + ** just leave the socket as is, which under other OSs means an extra + ** file descriptor gets passed to the child process. Since the child + ** probably already has that file open via stdin stdout and/or stderr, + ** this is not a problem. + */ + /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */ + +#ifdef CGI_NICE + /* Set priority. */ + (void)nice(CGI_NICE); +#endif /* CGI_NICE */ + +#if 0 + /* Split the program into directory and binary, so we can chdir() + ** to the program's own directory. This isn't in the CGI 1.1 + ** spec, but it's what other HTTP servers do. + */ + directory = strdup(hc->expnfilename); + if (directory == (char *) 0) + binary = hc->expnfilename; /* ignore errors */ + else + { + binary = strrchr(directory, '/'); + if (binary == (char *) 0) + binary = hc->expnfilename; + else + { + *binary++ = '\0'; + (void) chdir(directory); /* ignore errors */ + } + } +#endif + + // Reset signal handlers + + (void) signal(SIGTERM, SIG_DFL); + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGCHLD, SIG_DFL); + (void) signal(SIGPIPE, SIG_DFL); + (void) signal(SIGHUP, SIG_DFL); + (void) signal(SIGUSR1, SIG_DFL); + (void) signal(SIGUSR2, SIG_DFL); + (void) signal(SIGALRM, SIG_DFL); + +#if 0 + /* Run the program. */ + (void) execve(binary, argp, envp); + + /* Something went wrong. */ + syslog(LOG_ERR, "execve %.80s - %m", hc->expnfilename); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); +#endif + + environ = envp; + //fprintf(stderr, "%s\n", hc->encodedurl); + run_cgi(); + +} + +static int cgi_start(httpd_conn *hc) +{ + int r; + ClientData client_data; + + ++hc->hs->cgi_count; + httpd_clear_ndelay(hc->conn_fd); + r = fork(); + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + + if (r == 0) + { + /* Child process. */ + sub_process = 1; + httpd_unlisten(hc->hs); + cgi_child(hc); + } + else + { + /* Parent process. */ + syslog(LOG_INFO, "spawned CGI process %d for path '%.200s'", r, hc->expnfilename); +#ifdef CGI_TIMELIMIT + /* Schedule a kill for the child process, in case it runs too long */ + if (hc->hs->cgi_timelimit) + { + client_data.i = r; + if (tmr_create + ((struct timeval *) 0, cgi_kill, client_data, + hc->hs->cgi_timelimit * 1000L, 0) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(cgi_kill child) failed"); + exit(1); + } + } +#endif /* CGI_TIMELIMIT */ + hc->status = 200; + hc->bytes_sent = CGI_BYTECOUNT; + hc->should_linger = 0; + } + + return 0; +} + +int httpd_check_paused(httpd_conn *hc) +{ + //syslog(LOG_INFO, "[%p] try again cgi script '%.200s'", hc, hc->expnfilename); + + if (hc->hs->cgi_count >= 1) + { + //syslog(LOG_INFO, "[%p] postponed cgi script '%.200s'", hc, hc->expnfilename); + return 1; + } + + return cgi_start(hc); +} + +static int cgi(httpd_conn * hc) +{ + if (hc->method == METHOD_GET || hc->method == METHOD_POST) + { + if (hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit) + { + if (hc->hs->debug) + { + //syslog(LOG_INFO, "postponed cgi script '%.200s'", hc->expnfilename); + return 1; + } + else + { + httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl); + return -1; + } + } + + cgi_start(hc); + } + else + { + httpd_send_err(hc, 501, err501title, "", err501form, httpd_method_str(hc->method)); + return -1; + } + + return 0; +} + + +static int really_start_request(httpd_conn * hc, struct timeval *nowP) +{ + //static char *indexname; + //static size_t maxindexname = 0; + //static const char *index_names[] = { INDEX_NAMES }; + //int i; +#ifdef AUTH_FILE + //static char *dirname; + //static size_t maxdirname = 0; +#endif /* AUTH_FILE */ + //size_t expnlen, indxlen; + //char *cp; + //char *pi; + char *public_file = NULL; + int public_file_len = 0; + + if (hc->method != METHOD_GET && hc->method != METHOD_HEAD && + hc->method != METHOD_POST) + { + httpd_send_err(hc, 501, err501title, "", err501form, httpd_method_str(hc->method)); + return -1; + } + + //-------------------------------------------------------------- + + if (strlen(hc->expnfilename) == 0) + { + public_file = NULL; + } + else + { + public_file_len = strlen(hc->expnfilename) + strlen(PUBLIC_PREFIX); + public_file = alloca(public_file_len + 1); + strcpy(public_file, PUBLIC_PREFIX); + strcpy(&public_file[strlen(PUBLIC_PREFIX)], hc->expnfilename); + } + + /* Stat the file. */ + //if (stat(hc->expnfilename, &hc->sb) < 0) + /*{ + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + //httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + }*/ + +#if 0 + /* Is it world-readable or world-executable? We check explicitly instead + ** of just trying to open it, so that no one ever gets surprised by + ** a file that's not set world-readable and yet somehow is + ** readable by the HTTP server and therefore the *whole* world. + */ + if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH))) + { + syslog(LOG_INFO, + "%.80s URL \"%.80s\" resolves to a non world-readable file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file that is not world-readable.\n"), + hc->encodedurl); + return -1; + } +#endif + + if (public_file && !GB.StatFile(public_file, &hc->sb, FALSE)) + { + /* Is it a directory? */ + if (hc->sb.type == GB_STAT_DIRECTORY) + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } + + /* Fill in last_byte_index, if necessary. */ + if (hc->got_range && + (hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.size)) + hc->last_byte_index = hc->sb.size - 1; + + figure_mime(hc); + + if (hc->method == METHOD_HEAD) + { + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.size, hc->sb.mtime); + } + else if (hc->if_modified_since != (time_t) - 1 && + hc->if_modified_since >= hc->sb.mtime) + { + send_mime(hc, 304, err304title, hc->encodings, "", hc->type, (off_t) - 1, hc->sb.mtime); + } + else + { + //hc->file_address = mmc_map(hc->expnfilename, &(hc->sb), nowP); + //if (hc->file_address == (char *) 0) + + //syslog(LOG_DEBUG, "%.*s", public_file_len, public_file); + + if (GB.LoadFile(public_file, public_file_len, &hc->file_address, &hc->file_len)) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.size, hc->sb.mtime); + } + + return 0; + } + +#if 0 + /* If there's pathinfo, it's just a non-existent file. */ + /*if (hc->pathinfo[0] != '\0') + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + }*/ + + /* Special handling for directory URLs that don't end in a slash. + ** We send back an explicit redirect with the slash, because + ** otherwise many clients can't build relative URLs properly. + */ + if (strcmp(hc->origfilename, "") != 0 && + strcmp(hc->origfilename, ".") != 0 && + hc->origfilename[strlen(hc->origfilename) - 1] != '/') + { + send_dirredirect(hc); + return -1; + } +#endif + +#if 0 + /* Check for an index file. */ + for (i = 0; i < sizeof(index_names) / sizeof(char *); ++i) + { + httpd_realloc_str(&indexname, &maxindexname, + expnlen + 1 + strlen(index_names[i])); + (void) strcpy(indexname, hc->expnfilename); + indxlen = strlen(indexname); + if (indxlen == 0 || indexname[indxlen - 1] != '/') + (void) strcat(indexname, "/"); + if (strcmp(indexname, "./") == 0) + indexname[0] = '\0'; + (void) strcat(indexname, index_names[i]); + if (stat(indexname, &hc->sb) >= 0) + goto got_one; + } + + /* Nope, no index file, so it's an actual directory request. */ +#ifdef GENERATE_INDEXES + /* Directories must be readable for indexing. */ + if (!(hc->sb.st_mode & S_IROTH)) + { + syslog(LOG_INFO, + "%.80s URL \"%.80s\" tried to index a directory with indexing disabled", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n"), + hc->encodedurl); + return -1; + } +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + if (auth_check(hc, hc->expnfilename) == -1) + return -1; +#endif /* AUTH_FILE */ + /* Referer check. */ + if (!check_referer(hc)) + return -1; + /* Ok, generate an index. */ + return ls(hc); +#else /* GENERATE_INDEXES */ + syslog(LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n"), + hc->encodedurl); + return -1; +#endif /* GENERATE_INDEXES */ + + got_one:; + /* Got an index file. Expand symlinks again. More pathinfo means + ** something went wrong. + */ + cp = + expand_symlinks(indexname, &pi, hc->hs->no_symlink_check, + hc->tildemapped); + if (cp == (char *) 0 || pi[0] != '\0') + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + expnlen = strlen(cp); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, expnlen); + (void) strcpy(hc->expnfilename, cp); + + /* Now, is the index version world-readable or world-executable? */ + if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH))) + { + syslog(LOG_INFO, + "%.80s URL \"%.80s\" resolves to a non-world-readable index file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to an index file that is not world-readable.\n"), + hc->encodedurl); + return -1; + } + } + +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + httpd_realloc_str(&dirname, &maxdirname, expnlen); + (void) strcpy(dirname, hc->expnfilename); + cp = strrchr(dirname, '/'); + if (cp == (char *) 0) + (void) strcpy(dirname, "."); + else + *cp = '\0'; + if (auth_check(hc, dirname) == -1) + return -1; + + /* Check if the filename is the AUTH_FILE itself - that's verboten. */ + if (expnlen == sizeof(AUTH_FILE) - 1) + { + if (strcmp(hc->expnfilename, AUTH_FILE) == 0) + { + syslog(LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an auth file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n"), + hc->encodedurl); + return -1; + } + } + else if (expnlen >= sizeof(AUTH_FILE) && + strcmp(&(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), + AUTH_FILE) == 0 + && hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/') + { + syslog(LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an auth file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n"), + hc->encodedurl); + return -1; + } +#endif /* AUTH_FILE */ +#endif + + //-------------------------------------------------------------- + + /* Referer check. */ + if (!check_referer(hc)) + return -1; + + /* Is it world-executable and in the CGI area? */ + //if ( hc->hs->cgi_pattern != (char*) 0 && ( hc->sb.st_mode & S_IXOTH ) && match( hc->hs->cgi_pattern, hc->expnfilename ) ) + return cgi(hc); + +#if 0 + /* Is it world-executable and in the CGI area? */ + if (hc->hs->cgi_pattern != (char *) 0 && + (hc->sb.st_mode & S_IXOTH) && + match(hc->hs->cgi_pattern, hc->expnfilename)) + return cgi(hc); + + /* It's not CGI. If it's executable or there's pathinfo, someone's + ** trying to either serve or run a non-CGI file as CGI. Either case + ** is prohibited. + */ + if (hc->sb.st_mode & S_IXOTH) + { + syslog(LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n"), + hc->encodedurl); + return -1; + } + if (hc->pathinfo[0] != '\0') + { + syslog(LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n"), + hc->encodedurl); + return -1; + } + + /* Fill in last_byte_index, if necessary. */ + if (hc->got_range && + (hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.size)) + hc->last_byte_index = hc->sb.size - 1; + + figure_mime(hc); + + if (hc->method == METHOD_HEAD) + { + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, + hc->sb.size, hc->sb.mtime); + } + else if (hc->if_modified_since != (time_t) - 1 && + hc->if_modified_since >= hc->sb.mtime) + { + send_mime(hc, 304, err304title, hc->encodings, "", hc->type, (off_t) - 1, + hc->sb.mtime); + } + else + { + hc->file_address = mmc_map(hc->expnfilename, &(hc->sb), nowP); + if (hc->file_address == (char *) 0) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, + hc->sb.size, hc->sb.mtime); + } + + return 0; +#endif +} + + +int httpd_start_request(httpd_conn * hc, struct timeval *nowP) +{ + int r; + + /* Really start the request. */ + r = really_start_request(hc, nowP); + + /* And return the status. */ + return r; +} + + +static void make_log_entry(httpd_conn * hc, struct timeval *nowP) +{ + char *ru; + char url[305]; + char bytes[40]; + + if (hc->hs->no_log) + return; + + /* This is straight CERN Combined Log Format - the only tweak + ** being that if we're using syslog() we leave out the date, because + ** syslogd puts it in. The included syslogtocern script turns the + ** results into true CERN format. + */ + + /* Format remote user. */ + if (hc->remoteuser[0] != '\0') + ru = hc->remoteuser; + else + ru = "-"; + /* If we're vhosting, prepend the hostname to the url. This is + ** a little weird, perhaps writing separate log files for + ** each vhost would make more sense. + */ + if (hc->hs->vhost && !hc->tildemapped) + (void) my_snprintf(url, sizeof(url), + "/%.100s%.200s", + hc->hostname == + (char *) 0 ? hc->hs->server_hostname : hc->hostname, + hc->encodedurl); + else + (void) my_snprintf(url, sizeof(url), "%.200s", hc->encodedurl); + /* Format the bytes. */ + if (hc->bytes_sent >= 0) + (void) my_snprintf(bytes, sizeof(bytes), "%lld", + (int64_t) hc->bytes_sent); + else + (void) strcpy(bytes, "-"); + + /* Logfile or syslog? */ + if (hc->hs->logfp != (FILE *) 0) + { + time_t now; + struct tm *t; + const char *cernfmt_nozone = "%d/%b/%Y:%H:%M:%S"; + char date_nozone[100]; + int zone; + char sign; + char date[100]; + + /* Get the current time, if necessary. */ + if (nowP != (struct timeval *) 0) + now = nowP->tv_sec; + else + now = time((time_t *) 0); + /* Format the time, forcing a numeric timezone (some log analyzers + ** are stoooopid about this). + */ + t = localtime(&now); + (void) strftime(date_nozone, sizeof(date_nozone), cernfmt_nozone, t); +#ifdef HAVE_TM_GMTOFF + zone = t->tm_gmtoff / 60L; +#else + zone = -timezone / 60L; + /* Probably have to add something about daylight time here. */ +#endif + if (zone >= 0) + sign = '+'; + else + { + sign = '-'; + zone = -zone; + } + zone = (zone / 60) * 100 + zone % 60; + (void) my_snprintf(date, sizeof(date), + "%s %c%04d", date_nozone, sign, zone); + /* And write the log entry. */ + (void) fprintf(hc->hs->logfp, + "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n", + httpd_ntoa(&hc->client_addr), ru, date, + httpd_method_str(hc->method), url, hc->protocol, + hc->status, bytes, hc->referer, hc->useragent); +#ifdef FLUSH_LOG_EVERY_TIME + (void) fflush(hc->hs->logfp); +#endif + } + else + syslog(LOG_INFO, + "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"", + httpd_ntoa(&hc->client_addr), ru, + httpd_method_str(hc->method), url, hc->protocol, + hc->status, bytes, hc->referer, hc->useragent); +} + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int check_referer(httpd_conn * hc) +{ + int r; + char *cp; + + /* Are we doing referer checking at all? */ + if (hc->hs->url_pattern == (char *) 0) + return 1; + + r = really_check_referer(hc); + + if (!r) + { + if (hc->hs->vhost && hc->hostname != (char *) 0) + cp = hc->hostname; + else + cp = hc->hs->server_hostname; + if (cp == (char *) 0) + cp = ""; + syslog(LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"", + httpd_ntoa(&hc->client_addr), cp, hc->encodedurl, hc->referer); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "You must supply a local referer to get URL '%.80s' from this server.\n"), + hc->encodedurl); + } + return r; +} + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int really_check_referer(httpd_conn * hc) +{ + httpd_server *hs; + char *cp1; + char *cp2; + char *cp3; + static char *refhost = (char *) 0; + static size_t refhost_size = 0; + char *lp; + + hs = hc->hs; + + /* Check for an empty referer. */ + if (hc->referer == (char *) 0 || hc->referer[0] == '\0' || + (cp1 = strstr(hc->referer, "//")) == (char *) 0) + { + /* Disallow if we require a referer and the url matches. */ + if (hs->no_empty_referers && match(hs->url_pattern, hc->origfilename)) + return 0; + /* Otherwise ok. */ + return 1; + } + + /* Extract referer host. */ + cp1 += 2; + for (cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2) + continue; + httpd_realloc_str(&refhost, &refhost_size, cp2 - cp1); + for (cp3 = refhost; cp1 < cp2; ++cp1, ++cp3) + if (isupper(*cp1)) + *cp3 = tolower(*cp1); + else + *cp3 = *cp1; + *cp3 = '\0'; + + /* Local pattern? */ + if (hs->local_pattern != (char *) 0) + lp = hs->local_pattern; + else + { + /* No local pattern. What's our hostname? */ + if (!hs->vhost) + { + /* Not vhosting, use the server name. */ + lp = hs->server_hostname; + if (lp == (char *) 0) + /* Couldn't figure out local hostname - give up. */ + return 1; + } + else + { + /* We are vhosting, use the hostname on this connection. */ + lp = hc->hostname; + if (lp == (char *) 0) + /* Oops, no hostname. Maybe it's an old browser that + ** doesn't send a Host: header. We could figure out + ** the default hostname for this IP address, but it's + ** not worth it for the few requests like this. + */ + return 1; + } + } + + /* If the referer host doesn't match the local host pattern, and + ** the filename does match the url pattern, it's an illegal reference. + */ + if (!match(lp, refhost) && match(hs->url_pattern, hc->origfilename)) + return 0; + /* Otherwise ok. */ + return 1; +} + + +char *httpd_ntoa(httpd_sockaddr * saP) +{ +#ifdef USE_IPV6 + static char str[200]; + + if (getnameinfo + (&saP->sa, sockaddr_len(saP), str, sizeof(str), 0, 0, + NI_NUMERICHOST) != 0) + { + str[0] = '?'; + str[1] = '\0'; + } + else if (IN6_IS_ADDR_V4MAPPED(&saP->sa_in6.sin6_addr) + && strncmp(str, "::ffff:", 7) == 0) + /* Elide IPv6ish prefix for IPv4 addresses. */ + (void) strcpy(str, &str[7]); + + return str; + +#else /* USE_IPV6 */ + + return inet_ntoa(saP->sa_in.sin_addr); + +#endif /* USE_IPV6 */ +} + + +static int sockaddr_check(httpd_sockaddr * saP) +{ + switch (saP->sa.sa_family) + { + case AF_INET: + return 1; +#ifdef USE_IPV6 + case AF_INET6: + return 1; +#endif /* USE_IPV6 */ + default: + return 0; + } +} + + +static size_t sockaddr_len(httpd_sockaddr * saP) +{ + switch (saP->sa.sa_family) + { + case AF_INET: + return sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + case AF_INET6: + return sizeof(struct sockaddr_in6); +#endif /* USE_IPV6 */ + default: + return 0; /* shouldn't happen */ + } +} + + +/* Some systems don't have snprintf(), so we make our own that uses +** either vsnprintf() or vsprintf(). If your system doesn't have +** vsnprintf(), it is probably vulnerable to buffer overruns. +** Upgrade! +*/ +static int my_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int r; + + va_start(ap, format); +#ifdef HAVE_VSNPRINTF + r = vsnprintf(str, size, format, ap); +#else /* HAVE_VSNPRINTF */ + r = vsprintf(str, format, ap); +#endif /* HAVE_VSNPRINTF */ + va_end(ap); + return r; +} + + +#ifndef HAVE_ATOLL +static long long atoll(const char *str) +{ + long long value; + long long sign; + + while (isspace(*str)) + ++str; + switch (*str) + { + case '-': + sign = -1; + ++str; + break; + case '+': + sign = 1; + ++str; + break; + default: + sign = 1; + break; + } + value = 0; + while (isdigit(*str)) + { + value = value * 10 + (*str - '0'); + ++str; + } + return sign * value; +} +#endif /* HAVE_ATOLL */ + + +/* Read the requested buffer completely, accounting for interruptions. */ +int httpd_read_fully(int fd, void *buf, size_t nbytes) +{ + int nread; + + nread = 0; + while (nread < nbytes) + { + int r; + + r = read(fd, (char *) buf + nread, nbytes - nread); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r < 0) + return r; + if (r == 0) + break; + nread += r; + } + + return nread; +} + + +/* Write the requested buffer completely, accounting for interruptions. */ +int httpd_write_fully(int fd, const void *buf, size_t nbytes) +{ + int nwritten; + + nwritten = 0; + while (nwritten < nbytes) + { + int r; + + r = write(fd, (char *) buf + nwritten, nbytes - nwritten); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r < 0) + return r; + if (r == 0) + break; + nwritten += r; + } + + return nwritten; +} + + +/* Generate debugging statistics syslog message. */ +void httpd_logstats(long secs) +{ + if (str_alloc_count > 0) + syslog(LOG_INFO, + " libhttpd - %d strings allocated, %lu bytes (%g bytes/str)", + str_alloc_count, (unsigned long) str_alloc_size, + (float) str_alloc_size / str_alloc_count); +} diff --git a/gb.httpd/src/libhttpd.h b/gb.httpd/src/libhttpd.h new file mode 100644 index 00000000..90e06bb1 --- /dev/null +++ b/gb.httpd/src/libhttpd.h @@ -0,0 +1,298 @@ +/* libhttpd.h - defines for libhttpd +** +** Copyright (c) 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _LIBHTTPD_H_ +#define _LIBHTTPD_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) +#define USE_IPV6 +#endif + + +/* A few convenient defines. */ + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#define NEW(t,n) ((t*) malloc( sizeof(t) * (n) )) +#define RENEW(o,t,n) ((t*) realloc( (void*) o, sizeof(t) * (n) )) + + +/* The httpd structs. */ + +/* A multi-family sockaddr. */ +typedef union +{ + struct sockaddr sa; + struct sockaddr_in sa_in; +#ifdef USE_IPV6 + struct sockaddr_in6 sa_in6; + struct sockaddr_storage sa_stor; +#endif /* USE_IPV6 */ +} httpd_sockaddr; + +/* A server. */ +typedef struct +{ + char *binding_hostname; + char *server_hostname; + unsigned short port; + char *cgi_pattern; + int cgi_limit, cgi_timelimit, cgi_count; + char *charset; + char *p3p; + int max_age; + char *cwd; + int listen4_fd, listen6_fd; + int no_log; + FILE *logfp; + int no_symlink_check; + int vhost; + int global_passwd; + char *url_pattern; + char *local_pattern; + int no_empty_referers; + unsigned debug : 1; +} httpd_server; + +/* A connection. */ +typedef struct +{ + int initialized; + httpd_server *hs; + httpd_sockaddr client_addr; + char *read_buf; + size_t read_size, read_idx, checked_idx; + int checked_state; + int method; + int status; + off_t bytes_to_send; + off_t bytes_sent; + char *encodedurl; + char *decodedurl; + char *protocol; + char *origfilename; + char *expnfilename; + char *encodings; + char *pathinfo; + char *query; + char *referer; + char *useragent; + char *accept; + char *accepte; + char *acceptl; + char *cookie; + char *contenttype; +#ifdef X_CGI_HEADER + char *xcgi; +#endif + char *reqhost; + char *hdrhost; + char *hostdir; + char *authorization; + char *remoteuser; + char *response; + size_t maxdecodedurl, maxorigfilename, maxexpnfilename, maxencodings, + maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost, maxhostdir, + maxremoteuser, maxresponse; +#ifdef TILDE_MAP_2 + char *altdir; + size_t maxaltdir; +#endif /* TILDE_MAP_2 */ + size_t responselen; + time_t if_modified_since, range_if; + size_t contentlength; + char *type; /* not malloc()ed */ + char *hostname; /* not malloc()ed */ + int mime_flag; + int one_one; /* HTTP/1.1 or better */ + int got_range; + int tildemapped; /* this connection got tilde-mapped */ + off_t first_byte_index, last_byte_index; + int keep_alive; + int should_linger; + GB_FILE_STAT sb; + int conn_fd; + char *file_address; + int file_len; +} httpd_conn; + +/* Methods. */ +#define METHOD_UNKNOWN 0 +#define METHOD_GET 1 +#define METHOD_HEAD 2 +#define METHOD_POST 3 + +/* States for checked_state. */ +#define CHST_FIRSTWORD 0 +#define CHST_FIRSTWS 1 +#define CHST_SECONDWORD 2 +#define CHST_SECONDWS 3 +#define CHST_THIRDWORD 4 +#define CHST_THIRDWS 5 +#define CHST_LINE 6 +#define CHST_LF 7 +#define CHST_CR 8 +#define CHST_CRLF 9 +#define CHST_CRLFCR 10 +#define CHST_BOGUS 11 + + +/* Initializes. Does the socket(), bind(), and listen(). Returns an +** httpd_server* which includes a socket fd that you can select() on. +** Return (httpd_server*) 0 on error. +*/ +extern httpd_server *httpd_initialize(char *hostname, httpd_sockaddr * sa4P, + httpd_sockaddr * sa6P, + unsigned short port, char *cgi_pattern, + int cgi_limit, int cgi_timelimit, + char *charset, char *p3p, int max_age, + char *cwd, int no_log, FILE * logfp, + int no_symlink_check, int vhost, + int global_passwd, char *url_pattern, + char *local_pattern, + int no_empty_referers); + +/* Change the log file. */ +extern void httpd_set_logfp(httpd_server * hs, FILE * logfp); + +/* Call to unlisten/close socket(s) listening for new connections. */ +extern void httpd_unlisten(httpd_server * hs); + +/* Call to shut down. */ +extern void httpd_terminate(httpd_server * hs); + + +/* When a listen fd is ready to read, call this. It does the accept() and +** returns an httpd_conn* which includes the fd to read the request from and +** write the response to. Returns an indication of whether the accept() +** failed, succeeded, or if there were no more connections to accept. +** +** In order to minimize malloc()s, the caller passes in the httpd_conn. +** The caller is also responsible for setting initialized to zero before the +** first call using each different httpd_conn. +*/ +extern int httpd_get_conn(httpd_server * hs, int listen_fd, httpd_conn * hc); +#define GC_FAIL 0 +#define GC_OK 1 +#define GC_NO_MORE 2 + +/* Checks whether the data in hc->read_buf constitutes a complete request +** yet. The caller reads data into hc->read_buf[hc->read_idx] and advances +** hc->read_idx. This routine checks what has been read so far, using +** hc->checked_idx and hc->checked_state to keep track, and returns an +** indication of whether there is no complete request yet, there is a +** complete request, or there won't be a valid request due to a syntax error. +*/ +extern int httpd_got_request(httpd_conn * hc); +#define GR_NO_REQUEST 0 +#define GR_GOT_REQUEST 1 +#define GR_BAD_REQUEST 2 + +/* Parses the request in hc->read_buf. Fills in lots of fields in hc, +** like the URL and the various headers. +** +** Returns -1 on error. +*/ +extern int httpd_parse_request(httpd_conn * hc); + +/* Starts sending data back to the client. In some cases (directories, +** CGI programs), finishes sending by itself - in those cases, hc->file_fd +** is <0. If there is more data to be sent, then hc->file_fd is a file +** descriptor for the file to send. If you don't have a current timeval +** handy just pass in 0. +** +** Returns -1 on error. +*/ +extern int httpd_start_request(httpd_conn * hc, struct timeval *nowP); + +/* Actually sends any buffered response text. */ +extern void httpd_write_response(httpd_conn * hc); + +/* Call this to close down a connection and free the data. A fine point, +** if you fork() with a connection open you should still call this in the +** parent process - the connection will stay open in the child. +** If you don't have a current timeval handy just pass in 0. +*/ +extern void httpd_close_conn(httpd_conn * hc, struct timeval *nowP); + +/* Call this to de-initialize a connection struct and *really* free the +** mallocced strings. +*/ +extern void httpd_destroy_conn(httpd_conn * hc); + + +/* Send an error message back to the client. */ +extern void httpd_send_err(httpd_conn * hc, int status, char *title, + char *extraheads, char *form, char *arg); + +/* Some error messages. */ +extern char *httpd_err400title; +extern char *httpd_err400form; +extern char *httpd_err408title; +extern char *httpd_err408form; +extern char *httpd_err503title; +extern char *httpd_err503form; + +/* Generate a string representation of a method number. */ +extern char *httpd_method_str(int method); + +/* Reallocate a string. */ +extern void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size); + +/* Format a network socket to a string representation. */ +extern char *httpd_ntoa(httpd_sockaddr * saP); + +/* Set NDELAY mode on a socket. */ +extern void httpd_set_ndelay(int fd); + +/* Clear NDELAY mode on a socket. */ +extern void httpd_clear_ndelay(int fd); + +/* Read the requested buffer completely, accounting for interruptions. */ +extern int httpd_read_fully(int fd, void *buf, size_t nbytes); + +/* Write the requested buffer completely, accounting for interruptions. */ +extern int httpd_write_fully(int fd, const void *buf, size_t nbytes); + +/* Generate debugging statistics syslog message. */ +extern void httpd_logstats(long secs); + +extern int httpd_check_paused(httpd_conn *hc); + +#endif /* _LIBHTTPD_H_ */ diff --git a/gb.httpd/src/main.c b/gb.httpd/src/main.c new file mode 100644 index 00000000..30f3e1ee --- /dev/null +++ b/gb.httpd/src/main.c @@ -0,0 +1,90 @@ +/*************************************************************************** + + main.c + + gb.httpd component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gb_common.h" + +#include +#include + +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +int thttpd_main(int argc, char **argv, bool debug); + +static jmp_buf _setjmp_env; +static bool _debug = FALSE; + +void syslog(int priority, const char *format, ...) +{ + va_list args; + + if (!_debug && priority != LOG_CRIT) + return; + + va_start(args, format); + + fprintf(stderr, "gb.httpd: "); + vfprintf(stderr, format, args); + putc('\n', stderr); + + va_end(args); +} + +void run_cgi(void) +{ + longjmp(_setjmp_env, 1); +} + +void EXPORT GB_MAIN(int argc, char **argv) +{ + char *env; + + if (setjmp(_setjmp_env) == 0) + { + GB.System.SetLanguage("C"); + + env = getenv("GB_HTTPD_DEBUG"); + if (env && env[0] && strcmp(env, "0") != 0) + _debug = TRUE; + + thttpd_main(argc, argv, GB.System.Debug()); + } + else + { + GB.System.HasForked(); + } +} + +int EXPORT GB_INIT() +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.httpd/src/main.h b/gb.httpd/src/main.h new file mode 100644 index 00000000..e5b3125b --- /dev/null +++ b/gb.httpd/src/main.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + main.h + + gb.httpd component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +void syslog(int priority, const char *format, ...); +#define closelog() + +void run_cgi(); + +#endif /* __MAIN_H */ diff --git a/gb.httpd/src/match.c b/gb.httpd/src/match.c new file mode 100644 index 00000000..f3ebbaf7 --- /dev/null +++ b/gb.httpd/src/match.c @@ -0,0 +1,86 @@ +/* match.c - simple shell-style filename matcher +** +** Only does ? * and **, and multiple patterns separated by |. Returns 1 or 0. +** +** (c) 1995,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include + +#include "match.h" + +static int match_one(const char *pattern, int patternlen, const char *string); + +int match(const char *pattern, const char *string) +{ + const char *or; + + for (;;) + { + or = strchr(pattern, '|'); + if (or == (char *) 0) + return match_one(pattern, strlen(pattern), string); + if (match_one(pattern, or - pattern, string)) + return 1; + pattern = or + 1; + } +} + + +static int match_one(const char *pattern, int patternlen, const char *string) +{ + const char *p; + + for (p = pattern; p - pattern < patternlen; ++p, ++string) + { + if (*p == '?' && *string != '\0') + continue; + if (*p == '*') + { + int i, pl; + ++p; + if (*p == '*') + { + /* Double-wildcard matches anything. */ + ++p; + i = strlen(string); + } + else + /* Single-wildcard matches anything but slash. */ + i = strcspn(string, "/"); + pl = patternlen - (p - pattern); + for (; i >= 0; --i) + if (match_one(p, pl, &(string[i]))) + return 1; + return 0; + } + if (*p != *string) + return 0; + } + if (*string == '\0') + return 1; + return 0; +} diff --git a/gb.httpd/src/match.h b/gb.httpd/src/match.h new file mode 100644 index 00000000..0de85eee --- /dev/null +++ b/gb.httpd/src/match.h @@ -0,0 +1,36 @@ +/* match.h - simple shell-style filename patcher +** +** (c) 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _MATCH_H_ +#define _MATCH_H_ + +/* Simple shell-style filename pattern matcher. Only does ? * and **, and +** multiple patterns separated by |. Returns 1 or 0. +*/ +extern int match(const char *pattern, const char *string); + +#endif /* _MATCH_H_ */ diff --git a/gb.httpd/src/mime_encodings.h b/gb.httpd/src/mime_encodings.h new file mode 100644 index 00000000..b1130e7e --- /dev/null +++ b/gb.httpd/src/mime_encodings.h @@ -0,0 +1,8 @@ +{ +"Z", 0, "compress", 0}, + +{ +"gz", 0, "gzip", 0}, + +{ +"uu", 0, "x-uuencode", 0}, diff --git a/gb.httpd/src/mime_types.h b/gb.httpd/src/mime_types.h new file mode 100644 index 00000000..e049fc4c --- /dev/null +++ b/gb.httpd/src/mime_types.h @@ -0,0 +1,569 @@ +{ +"a", 0, "application/octet-stream", 0}, + +{ +"aab", 0, "application/x-authorware-bin", 0}, + +{ +"aam", 0, "application/x-authorware-map", 0}, + +{ +"aas", 0, "application/x-authorware-seg", 0}, + +{ +"ai", 0, "application/postscript", 0}, + +{ +"aif", 0, "audio/x-aiff", 0}, + +{ +"aifc", 0, "audio/x-aiff", 0}, + +{ +"aiff", 0, "audio/x-aiff", 0}, + +{ +"asc", 0, "text/plain", 0}, + +{ +"asf", 0, "video/x-ms-asf", 0}, + +{ +"asx", 0, "video/x-ms-asf", 0}, + +{ +"au", 0, "audio/basic", 0}, + +{ +"avi", 0, "video/x-msvideo", 0}, + +{ +"bcpio", 0, "application/x-bcpio", 0}, + +{ +"bin", 0, "application/octet-stream", 0}, + +{ +"bmp", 0, "image/bmp", 0}, + +{ +"cdf", 0, "application/x-netcdf", 0}, + +{ +"class", 0, "application/x-java-vm", 0}, + +{ +"cpio", 0, "application/x-cpio", 0}, + +{ +"cpt", 0, "application/mac-compactpro", 0}, + +{ +"crl", 0, "application/x-pkcs7-crl", 0}, + +{ +"crt", 0, "application/x-x509-ca-cert", 0}, + +{ +"csh", 0, "application/x-csh", 0}, + +{ +"css", 0, "text/css", 0}, + +{ +"dcr", 0, "application/x-director", 0}, + +{ +"dir", 0, "application/x-director", 0}, + +{ +"djv", 0, "image/vnd.djvu", 0}, + +{ +"djvu", 0, "image/vnd.djvu", 0}, + +{ +"dll", 0, "application/octet-stream", 0}, + +{ +"dms", 0, "application/octet-stream", 0}, + +{ +"doc", 0, "application/msword", 0}, + +{ +"dtd", 0, "text/xml", 0}, + +{ +"dump", 0, "application/octet-stream", 0}, + +{ +"dvi", 0, "application/x-dvi", 0}, + +{ +"dxr", 0, "application/x-director", 0}, + +{ +"eps", 0, "application/postscript", 0}, + +{ +"etx", 0, "text/x-setext", 0}, + +{ +"exe", 0, "application/octet-stream", 0}, + +{ +"ez", 0, "application/andrew-inset", 0}, + +{ +"fgd", 0, "application/x-director", 0}, + +{ +"fh", 0, "image/x-freehand", 0}, + +{ +"fh4", 0, "image/x-freehand", 0}, + +{ +"fh5", 0, "image/x-freehand", 0}, + +{ +"fh7", 0, "image/x-freehand", 0}, + +{ +"fhc", 0, "image/x-freehand", 0}, + +{ +"gif", 0, "image/gif", 0}, + +{ +"gtar", 0, "application/x-gtar", 0}, + +{ +"hdf", 0, "application/x-hdf", 0}, + +{ +"hqx", 0, "application/mac-binhex40", 0}, + +{ +"htm", 0, "text/html; charset=%s", 0}, + +{ +"html", 0, "text/html; charset=%s", 0}, + +{ +"ice", 0, "x-conference/x-cooltalk", 0}, + +{ +"ief", 0, "image/ief", 0}, + +{ +"iges", 0, "model/iges", 0}, + +{ +"igs", 0, "model/iges", 0}, + +{ +"iv", 0, "application/x-inventor", 0}, + +{ +"jar", 0, "application/x-java-archive", 0}, + +{ +"jfif", 0, "image/jpeg", 0}, + +{ +"jpe", 0, "image/jpeg", 0}, + +{ +"jpeg", 0, "image/jpeg", 0}, + +{ +"jpg", 0, "image/jpeg", 0}, + +{ +"js", 0, "application/x-javascript", 0}, + +{ +"kar", 0, "audio/midi", 0}, + +{ +"latex", 0, "application/x-latex", 0}, + +{ +"lha", 0, "application/octet-stream", 0}, + +{ +"lzh", 0, "application/octet-stream", 0}, + +{ +"m3u", 0, "audio/x-mpegurl", 0}, + +{ +"man", 0, "application/x-troff-man", 0}, + +{ +"mathml", 0, "application/mathml+xml", 0}, + +{ +"me", 0, "application/x-troff-me", 0}, + +{ +"mesh", 0, "model/mesh", 0}, + +{ +"mid", 0, "audio/midi", 0}, + +{ +"midi", 0, "audio/midi", 0}, + +{ +"mif", 0, "application/vnd.mif", 0}, + +{ +"mime", 0, "message/rfc822", 0}, + +{ +"mml", 0, "application/mathml+xml", 0}, + +{ +"mov", 0, "video/quicktime", 0}, + +{ +"movie", 0, "video/x-sgi-movie", 0}, + +{ +"mp2", 0, "audio/mpeg", 0}, + +{ +"mp3", 0, "audio/mpeg", 0}, + +{ +"mp4", 0, "video/mp4", 0}, + +{ +"mpe", 0, "video/mpeg", 0}, + +{ +"mpeg", 0, "video/mpeg", 0}, + +{ +"mpg", 0, "video/mpeg", 0}, + +{ +"mpga", 0, "audio/mpeg", 0}, + +{ +"ms", 0, "application/x-troff-ms", 0}, + +{ +"msh", 0, "model/mesh", 0}, + +{ +"mv", 0, "video/x-sgi-movie", 0}, + +{ +"mxu", 0, "video/vnd.mpegurl", 0}, + +{ +"nc", 0, "application/x-netcdf", 0}, + +{ +"o", 0, "application/octet-stream", 0}, + +{ +"oda", 0, "application/oda", 0}, + +{ +"ogg", 0, "application/x-ogg", 0}, + +{ +"pac", 0, "application/x-ns-proxy-autoconfig", 0}, + +{ +"pbm", 0, "image/x-portable-bitmap", 0}, + +{ +"pdb", 0, "chemical/x-pdb", 0}, + +{ +"pdf", 0, "application/pdf", 0}, + +{ +"pgm", 0, "image/x-portable-graymap", 0}, + +{ +"pgn", 0, "application/x-chess-pgn", 0}, + +{ +"png", 0, "image/png", 0}, + +{ +"pnm", 0, "image/x-portable-anymap", 0}, + +{ +"ppm", 0, "image/x-portable-pixmap", 0}, + +{ +"ppt", 0, "application/vnd.ms-powerpoint", 0}, + +{ +"ps", 0, "application/postscript", 0}, + +{ +"qt", 0, "video/quicktime", 0}, + +{ +"ra", 0, "audio/x-realaudio", 0}, + +{ +"ram", 0, "audio/x-pn-realaudio", 0}, + +{ +"ras", 0, "image/x-cmu-raster", 0}, + +{ +"rdf", 0, "application/rdf+xml", 0}, + +{ +"rgb", 0, "image/x-rgb", 0}, + +{ +"rm", 0, "audio/x-pn-realaudio", 0}, + +{ +"roff", 0, "application/x-troff", 0}, + +{ +"rpm", 0, "audio/x-pn-realaudio-plugin", 0}, + +{ +"rss", 0, "application/rss+xml", 0}, + +{ +"rtf", 0, "text/rtf", 0}, + +{ +"rtx", 0, "text/richtext", 0}, + +{ +"sgm", 0, "text/sgml", 0}, + +{ +"sgml", 0, "text/sgml", 0}, + +{ +"sh", 0, "application/x-sh", 0}, + +{ +"shar", 0, "application/x-shar", 0}, + +{ +"silo", 0, "model/mesh", 0}, + +{ +"sit", 0, "application/x-stuffit", 0}, + +{ +"skd", 0, "application/x-koan", 0}, + +{ +"skm", 0, "application/x-koan", 0}, + +{ +"skp", 0, "application/x-koan", 0}, + +{ +"skt", 0, "application/x-koan", 0}, + +{ +"smi", 0, "application/smil", 0}, + +{ +"smil", 0, "application/smil", 0}, + +{ +"snd", 0, "audio/basic", 0}, + +{ +"so", 0, "application/octet-stream", 0}, + +{ +"spl", 0, "application/x-futuresplash", 0}, + +{ +"src", 0, "application/x-wais-source", 0}, + +{ +"stc", 0, "application/vnd.sun.xml.calc.template", 0}, + +{ +"std", 0, "application/vnd.sun.xml.draw.template", 0}, + +{ +"sti", 0, "application/vnd.sun.xml.impress.template", 0}, + +{ +"stw", 0, "application/vnd.sun.xml.writer.template", 0}, + +{ +"sv4cpio", 0, "application/x-sv4cpio", 0}, + +{ +"sv4crc", 0, "application/x-sv4crc", 0}, + +{ +"svg", 0, "image/svg+xml", 0}, + +{ +"svgz", 0, "image/svg+xml", 0}, + +{ +"swf", 0, "application/x-shockwave-flash", 0}, + +{ +"sxc", 0, "application/vnd.sun.xml.calc", 0}, + +{ +"sxd", 0, "application/vnd.sun.xml.draw", 0}, + +{ +"sxg", 0, "application/vnd.sun.xml.writer.global", 0}, + +{ +"sxi", 0, "application/vnd.sun.xml.impress", 0}, + +{ +"sxm", 0, "application/vnd.sun.xml.math", 0}, + +{ +"sxw", 0, "application/vnd.sun.xml.writer", 0}, + +{ +"t", 0, "application/x-troff", 0}, + +{ +"tar", 0, "application/x-tar", 0}, + +{ +"tcl", 0, "application/x-tcl", 0}, + +{ +"tex", 0, "application/x-tex", 0}, + +{ +"texi", 0, "application/x-texinfo", 0}, + +{ +"texinfo", 0, "application/x-texinfo", 0}, + +{ +"tif", 0, "image/tiff", 0}, + +{ +"tiff", 0, "image/tiff", 0}, + +{ +"tr", 0, "application/x-troff", 0}, + +{ +"tsp", 0, "application/dsptype", 0}, + +{ +"tsv", 0, "text/tab-separated-values", 0}, + +{ +"txt", 0, "text/plain; charset=%s", 0}, + +{ +"ustar", 0, "application/x-ustar", 0}, + +{ +"vcd", 0, "application/x-cdlink", 0}, + +{ +"vrml", 0, "model/vrml", 0}, + +{ +"vx", 0, "video/x-rad-screenplay", 0}, + +{ +"wav", 0, "audio/x-wav", 0}, + +{ +"wax", 0, "audio/x-ms-wax", 0}, + +{ +"wbmp", 0, "image/vnd.wap.wbmp", 0}, + +{ +"wbxml", 0, "application/vnd.wap.wbxml", 0}, + +{ +"wm", 0, "video/x-ms-wm", 0}, + +{ +"wma", 0, "audio/x-ms-wma", 0}, + +{ +"wmd", 0, "application/x-ms-wmd", 0}, + +{ +"wml", 0, "text/vnd.wap.wml", 0}, + +{ +"wmlc", 0, "application/vnd.wap.wmlc", 0}, + +{ +"wmls", 0, "text/vnd.wap.wmlscript", 0}, + +{ +"wmlsc", 0, "application/vnd.wap.wmlscriptc", 0}, + +{ +"wmv", 0, "video/x-ms-wmv", 0}, + +{ +"wmx", 0, "video/x-ms-wmx", 0}, + +{ +"wmz", 0, "application/x-ms-wmz", 0}, + +{ +"wrl", 0, "model/vrml", 0}, + +{ +"wsrc", 0, "application/x-wais-source", 0}, + +{ +"wvx", 0, "video/x-ms-wvx", 0}, + +{ +"xbm", 0, "image/x-xbitmap", 0}, + +{ +"xht", 0, "application/xhtml+xml", 0}, + +{ +"xhtml", 0, "application/xhtml+xml", 0}, + +{ +"xls", 0, "application/vnd.ms-excel", 0}, + +{ +"xml", 0, "text/xml", 0}, + +{ +"xpm", 0, "image/x-xpixmap", 0}, + +{ +"xsl", 0, "text/xml", 0}, + +{ +"xwd", 0, "image/x-xwindowdump", 0}, + +{ +"xyz", 0, "chemical/x-xyz", 0}, + +{ +"zip", 0, "application/zip", 0}, diff --git a/gb.httpd/src/strerror.c b/gb.httpd/src/strerror.c new file mode 100644 index 00000000..0299e332 --- /dev/null +++ b/gb.httpd/src/strerror.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strerror.c 5.1 (Berkeley) 4/9/89"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +char *strerror(errnum) + int errnum; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + static char ebuf[20]; + + if ((unsigned int) errnum < sys_nerr) + return (sys_errlist[errnum]); + (void) sprintf(ebuf, "Unknown error: %d", errnum); + return (ebuf); +} diff --git a/gb.httpd/src/tdate_parse.c b/gb.httpd/src/tdate_parse.c new file mode 100644 index 00000000..dcc91e19 --- /dev/null +++ b/gb.httpd/src/tdate_parse.c @@ -0,0 +1,312 @@ +/* tdate_parse - parse string dates into internal form, stripped-down version +** +** (c) 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +/* This is a stripped-down version of date_parse.c, available at +** http://www.acme.com/software/date_parse/ +*/ + +#include + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include +#include + +#include "tdate_parse.h" + + +struct strlong +{ + char *s; + long l; +}; + + +static void pound_case(char *str) +{ + for (; *str != '\0'; ++str) + { + if (isupper((int) *str)) + *str = tolower((int) *str); + } +} + +static int strlong_compare(v1, v2) + char *v1; + char *v2; +{ + return strcmp(((struct strlong *) v1)->s, ((struct strlong *) v2)->s); +} + + +static int strlong_search(char *str, struct strlong *tab, int n, long *lP) +{ + int i, h, l, r; + + l = 0; + h = n - 1; + for (;;) + { + i = (h + l) / 2; + r = strcmp(str, tab[i].s); + if (r < 0) + h = i - 1; + else if (r > 0) + l = i + 1; + else + { + *lP = tab[i].l; + return 1; + } + if (h < l) + return 0; + } +} + + +static int scan_wday(char *str_wday, long *tm_wdayP) +{ + static struct strlong wday_tab[] = { + {"sun", 0}, {"sunday", 0}, + {"mon", 1}, {"monday", 1}, + {"tue", 2}, {"tuesday", 2}, + {"wed", 3}, {"wednesday", 3}, + {"thu", 4}, {"thursday", 4}, + {"fri", 5}, {"friday", 5}, + {"sat", 6}, {"saturday", 6}, + }; + static int sorted = 0; + + if (!sorted) + { + (void) qsort(wday_tab, sizeof(wday_tab) / sizeof(struct strlong), + sizeof(struct strlong), strlong_compare); + sorted = 1; + } + pound_case(str_wday); + return strlong_search(str_wday, wday_tab, + sizeof(wday_tab) / sizeof(struct strlong), tm_wdayP); +} + + +static int scan_mon(char *str_mon, long *tm_monP) +{ + static struct strlong mon_tab[] = { + {"jan", 0}, {"january", 0}, + {"feb", 1}, {"february", 1}, + {"mar", 2}, {"march", 2}, + {"apr", 3}, {"april", 3}, + {"may", 4}, + {"jun", 5}, {"june", 5}, + {"jul", 6}, {"july", 6}, + {"aug", 7}, {"august", 7}, + {"sep", 8}, {"september", 8}, + {"oct", 9}, {"october", 9}, + {"nov", 10}, {"november", 10}, + {"dec", 11}, {"december", 11}, + }; + static int sorted = 0; + + if (!sorted) + { + (void) qsort(mon_tab, sizeof(mon_tab) / sizeof(struct strlong), + sizeof(struct strlong), strlong_compare); + sorted = 1; + } + pound_case(str_mon); + return strlong_search(str_mon, mon_tab, + sizeof(mon_tab) / sizeof(struct strlong), tm_monP); +} + + +static int is_leap(int year) +{ + return year % 400 ? (year % 100 ? (year % 4 ? 0 : 1) : 0) : 1; +} + + +/* Basically the same as mktime(). */ +static time_t tm_to_time(struct tm *tmP) +{ + time_t t; + static int monthtab[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + + /* Years since epoch, converted to days. */ + t = (tmP->tm_year - 70) * 365; + /* Leap days for previous years. */ + t += (tmP->tm_year - 69) / 4; + /* Days for the beginning of this month. */ + t += monthtab[tmP->tm_mon]; + /* Leap day for this year. */ + if (tmP->tm_mon >= 2 && is_leap(tmP->tm_year + 1900)) + ++t; + /* Days since the beginning of this month. */ + t += tmP->tm_mday - 1; /* 1-based field */ + /* Hours, minutes, and seconds. */ + t = t * 24 + tmP->tm_hour; + t = t * 60 + tmP->tm_min; + t = t * 60 + tmP->tm_sec; + + return t; +} + + +time_t tdate_parse(char *str) +{ + struct tm tm; + char *cp; + char str_mon[500], str_wday[500]; + int tm_sec, tm_min, tm_hour, tm_mday, tm_year; + long tm_mon, tm_wday; + time_t t; + + /* Initialize. */ + (void) memset((char *) &tm, 0, sizeof(struct tm)); + + /* Skip initial whitespace ourselves - sscanf is clumsy at this. */ + for (cp = str; *cp == ' ' || *cp == '\t'; ++cp) + continue; + + /* And do the sscanfs. WARNING: you can add more formats here, + ** but be careful! You can easily screw up the parsing of existing + ** formats when you add new ones. The order is important. + */ + + /* DD-mth-YY HH:MM:SS GMT */ + if (sscanf(cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* DD mth YY HH:MM:SS GMT */ + else if (sscanf(cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* HH:MM:SS GMT DD-mth-YY */ + else if (sscanf(cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* HH:MM:SS GMT DD mth YY */ + else if (sscanf(cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* wdy, DD-mth-YY HH:MM:SS GMT */ + else if (sscanf(cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 7 && + scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy, DD mth YY HH:MM:SS GMT */ + else if (sscanf(cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 7 && + scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy mth DD HH:MM:SS GMT YY */ + else if (sscanf(cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + &tm_year) == 7 && + scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_year = tm_year; + } + else + return (time_t) - 1; + + if (tm.tm_year > 1900) + tm.tm_year -= 1900; + else if (tm.tm_year < 70) + tm.tm_year += 100; + + t = tm_to_time(&tm); + + return t; +} diff --git a/gb.httpd/src/tdate_parse.h b/gb.httpd/src/tdate_parse.h new file mode 100644 index 00000000..1a908163 --- /dev/null +++ b/gb.httpd/src/tdate_parse.h @@ -0,0 +1,33 @@ +/* tdate_parse.h - parse string dates into internal form, stripped-down version +** +** (c) 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _TDATE_PARSE_H_ +#define _TDATE_PARSE_H_ + +extern time_t tdate_parse(char *str); + +#endif /* _TDATE_PARSE_H_ */ diff --git a/gb.httpd/src/thttpd.c b/gb.httpd/src/thttpd.c new file mode 100644 index 00000000..c4dd6a3a --- /dev/null +++ b/gb.httpd/src/thttpd.c @@ -0,0 +1,2281 @@ +/* thttpd.c - tiny/turbo/throttling HTTP server +** +** (c) 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include "main.h" +#include "thttpd.h" +#include "version.h" + +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#ifdef HAVE_GRP_H +#include +#endif +#include +#include +#include +#include +//#include +#include +#include + +#include "fdwatch.h" +#include "libhttpd.h" +//#include "mmc.h" +#include "timers.h" +#include "match.h" + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + + +static char *argv0; +static bool _debug; +//static int debug; +static unsigned short port; +static char *dir; +static char *data_dir; +static int do_chroot, no_log, no_symlink_check, do_vhost, do_global_passwd; +static char *cgi_pattern; +static int cgi_limit; +static int cgi_timelimit; +static char *url_pattern; +static int no_empty_referers; +static char *local_pattern; +static char *logfile; +static char *throttlefile; +static char *hostname; +static char *pidfile; +static char *user; +static char *charset; +static char *p3p; +static int max_age; + + +typedef struct +{ + char *pattern; + long max_limit, min_limit; + long rate; + off_t bytes_since_avg; + int num_sending; +} throttletab; +static throttletab *throttles; +static int numthrottles, maxthrottles; + +#define THROTTLE_NOLIMIT -1 + + +typedef struct +{ + int conn_state; + int next_free_connect; + httpd_conn *hc; + int tnums[MAXTHROTTLENUMS]; /* throttle indexes */ + int numtnums; + long max_limit, min_limit; + time_t started_at, active_at; + Timer *wakeup_timer; + Timer *linger_timer; + long wouldblock_delay; + off_t bytes; + off_t end_byte_index; + off_t next_byte_index; +} connecttab; +static connecttab *connects; +static int num_connects, max_connects, first_free_connect; +static int httpd_conn_count; + +/* The connection states. */ +#define CNST_FREE 0 +#define CNST_READING 1 +#define CNST_SENDING 2 +#define CNST_PAUSING 3 +#define CNST_LINGERING 4 + + +static httpd_server *hs = (httpd_server *) 0; +int terminate = 0; +time_t start_time, stats_time; +long stats_connections; +off_t stats_bytes; +int stats_simultaneous; + +static volatile int got_hup, got_usr1, watchdog_flag; + + +/* Forwards. */ +static void parse_args(int argc, char **argv); +/*static void usage (void); +static void read_config (char *filename); +static void value_required (char *name, char *value); +static void no_value_required (char *name, char *value);*/ +static char *e_strdup(char *oldstr); +static void lookup_hostname(httpd_sockaddr * sa4P, size_t sa4_len, + int *gotv4P, httpd_sockaddr * sa6P, + size_t sa6_len, int *gotv6P); +static void read_throttlefile(char *throttlefile); +static void shut_down(void); +static int handle_newconnect(struct timeval *tvP, int listen_fd); +static void handle_read(connecttab * c, struct timeval *tvP); +static void handle_send(connecttab * c, struct timeval *tvP); +static void handle_linger(connecttab * c, struct timeval *tvP); +static int check_throttles(connecttab * c); +static void clear_throttles(connecttab * c, struct timeval *tvP); +static void update_throttles(ClientData client_data, struct timeval *nowP); +static void finish_connection(connecttab * c, struct timeval *tvP); +static void clear_connection(connecttab * c, struct timeval *tvP); +static void really_clear_connection(connecttab * c, struct timeval *tvP); +static void idle(ClientData client_data, struct timeval *nowP); +static void wakeup_connection(ClientData client_data, struct timeval *nowP); +static void linger_clear_connection(ClientData client_data, + struct timeval *nowP); +static void occasional(ClientData client_data, struct timeval *nowP); +#ifdef STATS_TIME +static void show_stats(ClientData client_data, struct timeval *nowP); +#endif /* STATS_TIME */ +static void logstats(struct timeval *nowP); +static void thttpd_logstats(long secs); + + +/* SIGTERM and SIGINT say to exit immediately. */ +static void handle_term(int sig) +{ + /* Don't need to set up the handler again, since it's a one-shot. */ + + shut_down(); + syslog(LOG_NOTICE, "exiting due to signal %d", sig); + closelog(); + exit(1); +} + + +/* SIGCHLD - a chile process exitted, so we need to reap the zombie */ +static void handle_chld(int sig) +{ + const int oerrno = errno; + pid_t pid; + int status; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGCHLD, handle_chld); +#endif /* ! HAVE_SIGSET */ + + /* Reap defunct children until there aren't any more. */ + for (;;) + { +#ifdef HAVE_WAITPID + pid = waitpid((pid_t) - 1, &status, WNOHANG); +#else /* HAVE_WAITPID */ + pid = wait3(&status, WNOHANG, (struct rusage *) 0); +#endif /* HAVE_WAITPID */ + if ((int) pid == 0) /* none left */ + break; + if ((int) pid < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; + /* ECHILD shouldn't happen with the WNOHANG option, + ** but with some kernels it does anyway. Ignore it. + */ + if (errno != ECHILD) + syslog(LOG_ERR, "child wait - %m"); + break; + } + /* Decrement the CGI count. Note that this is not accurate, since + ** each CGI can involve two or even three child processes. + ** Decrementing for each child means that when there is heavy CGI + ** activity, the count will be lower than it should be, and therefore + ** more CGIs will be allowed than should be. + */ + if (hs != (httpd_server *) 0) + { + --hs->cgi_count; + if (hs->cgi_count < 0) + hs->cgi_count = 0; + } + } + + /* Restore previous errno. */ + errno = oerrno; +} + + +/* SIGHUP says to re-open the log file. */ +static void handle_hup(int sig) +{ + const int oerrno = errno; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGHUP, handle_hup); +#endif /* ! HAVE_SIGSET */ + + /* Just set a flag that we got the signal. */ + got_hup = 1; + + /* Restore previous errno. */ + errno = oerrno; +} + + +/* SIGUSR1 says to exit as soon as all current connections are done. */ +static void handle_usr1(int sig) +{ + /* Don't need to set up the handler again, since it's a one-shot. */ + + if (!_debug && num_connects == 0) + { + /* If there are no active connections we want to exit immediately + ** here. Not only is it faster, but without any connections the + ** main loop won't wake up until the next new connection. + */ + shut_down(); + syslog(LOG_NOTICE, "exiting"); + closelog(); + exit(0); + } + + /* Otherwise, just set a flag that we got the signal. */ + got_usr1 = 1; + + /* Don't need to restore old errno, since we didn't do any syscalls. */ +} + + +/* SIGUSR2 says to generate the stats syslogs immediately. */ +static void handle_usr2(int sig) +{ + const int oerrno = errno; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGUSR2, handle_usr2); +#endif /* ! HAVE_SIGSET */ + + logstats((struct timeval *) 0); + + /* Restore previous errno. */ + errno = oerrno; +} + + +/* SIGALRM is used as a watchdog. */ +static void handle_alrm(int sig) +{ + const int oerrno = errno; + + /* If nothing has been happening */ + if (!watchdog_flag) + { + /* Try changing dirs to someplace we can write. */ + if (chdir("/tmp")); // We don't care if chdir() fails. + /* Dump core. */ + abort(); + } + watchdog_flag = 0; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGALRM, handle_alrm); +#endif /* ! HAVE_SIGSET */ + /* Set up alarm again. */ + (void) alarm(OCCASIONAL_TIME * 3); + + /* Restore previous errno. */ + errno = oerrno; +} + + +static void re_open_logfile(void) +{ + FILE *logfp; + + if (no_log || hs == (httpd_server *) 0) + return; + + /* Re-open the log file. */ + if (logfile != (char *) 0 && strcmp(logfile, "-") != 0) + { + syslog(LOG_NOTICE, "re-opening logfile"); + logfp = fopen(logfile, "a"); + if (logfp == (FILE *) 0) + { + syslog(LOG_CRIT, "re-opening %.80s - %m", logfile); + return; + } + (void) fcntl(fileno(logfp), F_SETFD, 1); + httpd_set_logfp(hs, logfp); + } +} + + +int thttpd_main(int argc, char **argv, bool debug) +{ + char *cp; + struct passwd *pwd; + uid_t uid = 32767; + gid_t gid = 32767; + char cwd[MAXPATHLEN + 1]; + FILE *logfp; + int num_ready; + int cnum; + connecttab *c; + httpd_conn *hc; + httpd_sockaddr sa4; + httpd_sockaddr sa6; + int gotv4, gotv6; + struct timeval tv; + + argv0 = argv[0]; + _debug = debug; + + cp = strrchr(argv0, '/'); + if (cp != (char *) 0) + ++cp; + else + cp = argv0; + + //openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY ); + + /* Handle command-line arguments. */ + parse_args(argc, argv); + //debug = 1; + do_chroot = 0; + if (_debug) + { + cgi_limit = 1; + cgi_timelimit = 0; + } + + /* Read zone info now, in case we chroot(). */ + tzset(); + + /* Look up hostname now, in case we chroot(). */ + lookup_hostname(&sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6); + if (!(gotv4 || gotv6)) + { + syslog(LOG_ERR, "can't find any valid address"); + (void) fprintf(stderr, "%s: can't find any valid address\n", argv0); + exit(1); + } + + /* Throttle file. */ + numthrottles = 0; + maxthrottles = 0; + throttles = (throttletab *) 0; + if (throttlefile != (char *) 0) + read_throttlefile(throttlefile); + + /* If we're root and we're going to become another user, get the uid/gid + ** now. + */ + if (getuid() == 0) + { + pwd = getpwnam(user); + if (pwd == (struct passwd *) 0) + { + syslog(LOG_CRIT, "unknown user - '%.80s'", user); + (void) fprintf(stderr, "%s: unknown user - '%s'\n", argv0, user); + exit(1); + } + uid = pwd->pw_uid; + gid = pwd->pw_gid; + } + + /* Log file. */ +#if 0 + if (logfile != (char *) 0) + { + if (strcmp(logfile, "/dev/null") == 0) + { + no_log = 1; + logfp = (FILE *) 0; + } + else if (strcmp(logfile, "-") == 0) + logfp = stdout; + else + { + logfp = fopen(logfile, "a"); + if (logfp == (FILE *) 0) + { + syslog(LOG_CRIT, "%.80s - %m", logfile); + perror(logfile); + exit(1); + } + if (logfile[0] != '/') + { + syslog(LOG_WARNING, + "logfile is not an absolute path, you may not be able to re-open it"); + (void) fprintf(stderr, + "%s: logfile is not an absolute path, you may not be able to re-open it\n", + argv0); + } + (void) fcntl(fileno(logfp), F_SETFD, 1); + if (getuid() == 0) + { + /* If we are root then we chown the log file to the user we'll + ** be switching to. + */ + if (fchown(fileno(logfp), uid, gid) < 0) + { + syslog(LOG_WARNING, "fchown logfile - %m"); + perror("fchown logfile"); + } + } + } + } + else +#endif + logfp = (FILE *) 0; + +#if 0 + /* Switch directories if requested. */ + if (dir != (char *) 0) + { + if (chdir(dir) < 0) + { + syslog(LOG_CRIT, "chdir - %m"); + perror("chdir"); + exit(1); + } + } +#ifdef USE_USER_DIR + else if (getuid() == 0) + { + /* No explicit directory was specified, we're root, and the + ** USE_USER_DIR option is set - switch to the specified user's + ** home dir. + */ + if (chdir(pwd->pw_dir) < 0) + { + syslog(LOG_CRIT, "chdir - %m"); + perror("chdir"); + exit(1); + } + } +#endif /* USE_USER_DIR */ +#endif + + /* Get current directory. */ + if (getcwd(cwd, sizeof(cwd) - 1)) + { + if (cwd[strlen(cwd) - 1] != '/') + strcat(cwd, "/"); + } + else + strcpy(cwd, "/"); + + if (0) //!debug) + { + /* We're not going to use stdin stdout or stderr from here on, so close + ** them to save file descriptors. + */ + (void) fclose(stdin); + if (logfp != stdout) + (void) fclose(stdout); + (void) fclose(stderr); + + /* Daemonize - make ourselves a subprocess. */ +#ifdef HAVE_DAEMON + if (daemon(1, 1) < 0) + { + syslog(LOG_CRIT, "daemon - %m"); + exit(1); + } +#else /* HAVE_DAEMON */ + switch (fork()) + { + case 0: + break; + case -1: + syslog(LOG_CRIT, "fork - %m"); + exit(1); + default: + exit(0); + } +#ifdef HAVE_SETSID + (void) setsid(); +#endif /* HAVE_SETSID */ +#endif /* HAVE_DAEMON */ + } + else + { + /* Even if we don't daemonize, we still want to disown our parent + ** process. + */ +#ifdef HAVE_SETSID + (void) setsid(); +#endif /* HAVE_SETSID */ + } + +#if 0 + if (pidfile != (char *) 0) + { + /* Write the PID file. */ + FILE *pidfp = fopen(pidfile, "w"); + if (pidfp == (FILE *) 0) + { + syslog(LOG_CRIT, "%.80s - %m", pidfile); + exit(1); + } + (void) fprintf(pidfp, "%d\n", (int) getpid()); + (void) fclose(pidfp); + } +#endif + + /* Initialize the fdwatch package. Have to do this before chroot, + ** if /dev/poll is used. + */ + max_connects = fdwatch_get_nfiles(); + if (max_connects < 0) + { + syslog(LOG_CRIT, "fdwatch initialization failure"); + exit(1); + } + max_connects -= SPARE_FDS; + + /* Chroot if requested. */ +#if 0 + if (0) //do_chroot) + { + if (chroot(cwd) < 0) + { + syslog(LOG_CRIT, "chroot - %m"); + perror("chroot"); + exit(1); + } + /* If we're logging and the logfile's pathname begins with the + ** chroot tree's pathname, then elide the chroot pathname so + ** that the logfile pathname still works from inside the chroot + ** tree. + */ + if (logfile != (char *) 0 && strcmp(logfile, "-") != 0) + { + if (strncmp(logfile, cwd, strlen(cwd)) == 0) + { + (void) strcpy(logfile, &logfile[strlen(cwd) - 1]); + /* (We already guaranteed that cwd ends with a slash, so leaving + ** that slash in logfile makes it an absolute pathname within + ** the chroot tree.) + */ + } + else + { + syslog(LOG_WARNING, + "logfile is not within the chroot tree, you will not be able to re-open it"); + (void) fprintf(stderr, + "%s: logfile is not within the chroot tree, you will not be able to re-open it\n", + argv0); + } + } + (void) strcpy(cwd, "/"); + /* Always chdir to / after a chroot. */ + if (chdir(cwd) < 0) + { + syslog(LOG_CRIT, "chroot chdir - %m"); + perror("chroot chdir"); + exit(1); + } + } +#endif + + /* Switch directories again if requested. */ +#if 0 + if (data_dir != (char *) 0) + { + if (chdir(data_dir) < 0) + { + syslog(LOG_CRIT, "data_dir chdir - %m"); + perror("data_dir chdir"); + exit(1); + } + } +#endif + + /* Set up to catch signals. */ +#ifdef HAVE_SIGSET + (void) sigset(SIGTERM, handle_term); + (void) sigset(SIGINT, handle_term); + (void) sigset(SIGCHLD, handle_chld); + (void) sigset(SIGPIPE, SIG_IGN); /* get EPIPE instead */ + (void) sigset(SIGHUP, handle_hup); + (void) sigset(SIGUSR1, handle_usr1); + (void) sigset(SIGUSR2, handle_usr2); + (void) sigset(SIGALRM, handle_alrm); +#else /* HAVE_SIGSET */ + (void) signal(SIGTERM, handle_term); + (void) signal(SIGINT, handle_term); + (void) signal(SIGCHLD, handle_chld); + (void) signal(SIGPIPE, SIG_IGN); /* get EPIPE instead */ + (void) signal(SIGHUP, handle_hup); + (void) signal(SIGUSR1, handle_usr1); + (void) signal(SIGUSR2, handle_usr2); + (void) signal(SIGALRM, handle_alrm); +#endif /* HAVE_SIGSET */ + got_hup = 0; + got_usr1 = 0; + watchdog_flag = 0; + (void) alarm(OCCASIONAL_TIME * 3); + + /* Initialize the timer package. */ + tmr_init(); + + /* Initialize the HTTP layer. Got to do this before giving up root, + ** so that we can bind to a privileged port. + */ + hs = httpd_initialize(hostname, + gotv4 ? &sa4 : (httpd_sockaddr *) 0, + gotv6 ? &sa6 : (httpd_sockaddr *) 0, port, + cgi_pattern, cgi_limit, cgi_timelimit, charset, p3p, + max_age, cwd, no_log, logfp, no_symlink_check, + do_vhost, do_global_passwd, url_pattern, + local_pattern, no_empty_referers); + if (hs == (httpd_server *) 0) + exit(1); + + hs->debug = _debug; + + /* Set up the occasional timer. */ + if (tmr_create + ((struct timeval *) 0, occasional, JunkClientData, + OCCASIONAL_TIME * 1000L, 1) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(occasional) failed"); + exit(1); + } + /* Set up the idle timer. */ + if (tmr_create((struct timeval *) 0, idle, JunkClientData, 5 * 1000L, 1) == + (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(idle) failed"); + exit(1); + } + if (numthrottles > 0) + { + /* Set up the throttles timer. */ + if (tmr_create + ((struct timeval *) 0, update_throttles, JunkClientData, + THROTTLE_TIME * 1000L, 1) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(update_throttles) failed"); + exit(1); + } + } +#ifdef STATS_TIME + /* Set up the stats timer. */ + if (tmr_create + ((struct timeval *) 0, show_stats, JunkClientData, STATS_TIME * 1000L, + 1) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(show_stats) failed"); + exit(1); + } +#endif /* STATS_TIME */ + start_time = stats_time = time((time_t *) 0); + stats_connections = 0; + stats_bytes = 0; + stats_simultaneous = 0; + + /* If we're root, try to become someone else. */ + if (getuid() == 0) + { + /* Set aux groups to null. */ + if (setgroups(0, (const gid_t *) 0) < 0) + { + syslog(LOG_CRIT, "setgroups - %m"); + exit(1); + } + /* Set primary group. */ + if (setgid(gid) < 0) + { + syslog(LOG_CRIT, "setgid - %m"); + exit(1); + } + /* Try setting aux groups correctly - not critical if this fails. */ + if (initgroups(user, gid) < 0) + syslog(LOG_WARNING, "initgroups - %m"); +#ifdef HAVE_SETLOGIN + /* Set login name. */ + (void) setlogin(user); +#endif /* HAVE_SETLOGIN */ + /* Set uid. */ + if (setuid(uid) < 0) + { + syslog(LOG_CRIT, "setuid - %m"); + exit(1); + } + /* Check for unnecessary security exposure. */ + if (!do_chroot) + syslog(LOG_WARNING, + "started as root without requesting chroot(), warning only"); + } + + /* Initialize our connections table. */ + connects = NEW(connecttab, max_connects); + if (connects == (connecttab *) 0) + { + syslog(LOG_CRIT, "out of memory allocating a connecttab"); + exit(1); + } + for (cnum = 0; cnum < max_connects; ++cnum) + { + connects[cnum].conn_state = CNST_FREE; + connects[cnum].next_free_connect = cnum + 1; + connects[cnum].hc = (httpd_conn *) 0; + } + connects[max_connects - 1].next_free_connect = -1; /* end of link list */ + first_free_connect = 0; + num_connects = 0; + httpd_conn_count = 0; + + if (hs != (httpd_server *) 0) + { + if (hs->listen4_fd != -1) + fdwatch_add_fd(hs->listen4_fd, (void *) 0, FDW_READ); + if (hs->listen6_fd != -1) + fdwatch_add_fd(hs->listen6_fd, (void *) 0, FDW_READ); + } + + /* Main loop. */ + (void) gettimeofday(&tv, (struct timezone *) 0); + while ((!terminate) || num_connects > 0) + { + /* Do we need to re-open the log file? */ + if (got_hup) + { + re_open_logfile(); + got_hup = 0; + } + + /* Do the fd watch. */ + num_ready = fdwatch(tmr_mstimeout(&tv)); + if (num_ready < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; /* try again */ + syslog(LOG_ERR, "fdwatch - %m"); + exit(1); + } + (void) gettimeofday(&tv, (struct timezone *) 0); + + if (num_ready == 0) + { + /* No fd's are ready - run the timers. */ + tmr_run(&tv); + continue; + } + + /* Is it a new connection? */ + if (hs != (httpd_server *) 0 && hs->listen6_fd != -1 && + fdwatch_check_fd(hs->listen6_fd)) + { + if (handle_newconnect(&tv, hs->listen6_fd)) + /* Go around the loop and do another fdwatch, rather than + ** dropping through and processing existing connections. + ** New connections always get priority. + */ + continue; + } + if (hs != (httpd_server *) 0 && hs->listen4_fd != -1 && + fdwatch_check_fd(hs->listen4_fd)) + { + if (handle_newconnect(&tv, hs->listen4_fd)) + /* Go around the loop and do another fdwatch, rather than + ** dropping through and processing existing connections. + ** New connections always get priority. + */ + continue; + } + + /* Find the connections that need servicing. */ + while ((c = + (connecttab *) fdwatch_get_next_client_data()) != + (connecttab *) - 1) + { + if (c == (connecttab *) 0) + continue; + hc = c->hc; + if (!fdwatch_check_fd(hc->conn_fd)) + /* Something went wrong. */ + clear_connection(c, &tv); + else + switch (c->conn_state) + { + case CNST_READING: + handle_read(c, &tv); + break; + case CNST_SENDING: + handle_send(c, &tv); + break; + case CNST_LINGERING: + handle_linger(c, &tv); + break; + } + } + tmr_run(&tv); + + if (!_debug && got_usr1 && !terminate) + { + terminate = 1; + if (hs != (httpd_server *) 0) + { + if (hs->listen4_fd != -1) + fdwatch_del_fd(hs->listen4_fd); + if (hs->listen6_fd != -1) + fdwatch_del_fd(hs->listen6_fd); + httpd_unlisten(hs); + } + } + } + + /* The main loop terminated. */ + shut_down(); + syslog(LOG_NOTICE, "exiting"); + //closelog(); + exit(0); +} + + +static void parse_args(int argc, char **argv) +{ + char *env; + int val; + + //debug = 0; + port = DEFAULT_PORT; + dir = (char *) 0; + data_dir = (char *) 0; +#ifdef ALWAYS_CHROOT + do_chroot = 1; +#else /* ALWAYS_CHROOT */ + do_chroot = 0; +#endif /* ALWAYS_CHROOT */ + no_log = 0; + no_symlink_check = do_chroot; +#ifdef ALWAYS_VHOST + do_vhost = 1; +#else /* ALWAYS_VHOST */ + do_vhost = 0; +#endif /* ALWAYS_VHOST */ +#ifdef ALWAYS_GLOBAL_PASSWD + do_global_passwd = 1; +#else /* ALWAYS_GLOBAL_PASSWD */ + do_global_passwd = 0; +#endif /* ALWAYS_GLOBAL_PASSWD */ +#ifdef CGI_PATTERN + cgi_pattern = CGI_PATTERN; +#else /* CGI_PATTERN */ + cgi_pattern = (char *) 0; +#endif /* CGI_PATTERN */ +#ifdef CGI_LIMIT + cgi_limit = CGI_LIMIT; +#else /* CGI_LIMIT */ + cgi_limit = 0; +#endif /* CGI_LIMIT */ +#ifdef CGI_TIMELIMIT + cgi_timelimit = CGI_TIMELIMIT; +#else /* CGI_LIMIT */ + cgi_timelimit = 0; +#endif /* CGI_LIMIT */ + url_pattern = (char *) 0; + no_empty_referers = 0; + local_pattern = (char *) 0; + throttlefile = (char *) 0; + hostname = (char *) 0; + logfile = (char *) 0; + pidfile = (char *) 0; + user = DEFAULT_USER; + charset = DEFAULT_CHARSET; + p3p = ""; + max_age = -1; + + env = getenv("GB_HTTPD_PORT"); + if (env && *env) + { + port = (unsigned short) atoi(env); + if (port == 0) + port = 80; + } + + env = getenv("GB_HTTPD_TIMEOUT"); + if (env && *env) + { + val = atoi(env); + if (val == 0) + { + if (env[0] == '0' && env[1] == 0) + cgi_timelimit = 0; + } + else + cgi_timelimit = val; + } + +#if 0 + argn = 1; + while (argn < argc && argv[argn][0] == '-') + { + if (strcmp(argv[argn], "-V") == 0) + { + (void) printf("%s\n", SERVER_SOFTWARE); + exit(0); + } + else if (strcmp(argv[argn], "-C") == 0 && argn + 1 < argc) + { + ++argn; + read_config(argv[argn]); + } + else if (strcmp(argv[argn], "-p") == 0 && argn + 1 < argc) + { + ++argn; + port = (unsigned short) atoi(argv[argn]); + } + else if (strcmp(argv[argn], "-d") == 0 && argn + 1 < argc) + { + ++argn; + dir = argv[argn]; + } + else if (strcmp(argv[argn], "-r") == 0) + { + do_chroot = 1; + no_symlink_check = 1; + } + else if (strcmp(argv[argn], "-nor") == 0) + { + do_chroot = 0; + no_symlink_check = 0; + } + else if (strcmp(argv[argn], "-dd") == 0 && argn + 1 < argc) + { + ++argn; + data_dir = argv[argn]; + } + else if (strcmp(argv[argn], "-s") == 0) + no_symlink_check = 0; + else if (strcmp(argv[argn], "-nos") == 0) + no_symlink_check = 1; + else if (strcmp(argv[argn], "-u") == 0 && argn + 1 < argc) + { + ++argn; + user = argv[argn]; + } + else if (strcmp(argv[argn], "-c") == 0 && argn + 1 < argc) + { + ++argn; + cgi_pattern = argv[argn]; + } + else if (strcmp(argv[argn], "-t") == 0 && argn + 1 < argc) + { + ++argn; + throttlefile = argv[argn]; + } + else if (strcmp(argv[argn], "-h") == 0 && argn + 1 < argc) + { + ++argn; + hostname = argv[argn]; + } + else if (strcmp(argv[argn], "-l") == 0 && argn + 1 < argc) + { + ++argn; + logfile = argv[argn]; + } + else if (strcmp(argv[argn], "-v") == 0) + do_vhost = 1; + else if (strcmp(argv[argn], "-nov") == 0) + do_vhost = 0; + else if (strcmp(argv[argn], "-g") == 0) + do_global_passwd = 1; + else if (strcmp(argv[argn], "-nog") == 0) + do_global_passwd = 0; + else if (strcmp(argv[argn], "-i") == 0 && argn + 1 < argc) + { + ++argn; + pidfile = argv[argn]; + } + else if (strcmp(argv[argn], "-T") == 0 && argn + 1 < argc) + { + ++argn; + charset = argv[argn]; + } + else if (strcmp(argv[argn], "-P") == 0 && argn + 1 < argc) + { + ++argn; + p3p = argv[argn]; + } + else if (strcmp(argv[argn], "-M") == 0 && argn + 1 < argc) + { + ++argn; + max_age = atoi(argv[argn]); + } + else if (strcmp(argv[argn], "-D") == 0) + debug = 1; + else + usage(); + ++argn; + } + if (argn != argc) + usage(); +#endif +} + +#if 0 +static void usage(void) +{ + (void) fprintf(stderr, + "usage: %s [-C configfile] [-p port] [-d dir] [-r|-nor] [-dd data_dir] [-s|-nos] [-v|-nov] [-g|-nog] [-u user] [-c cgipat] [-t throttles] [-h host] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage] [-V] [-D]\n", + argv0); + exit(1); +} +#endif + +#if 0 +static void read_config(char *filename) +{ + FILE *fp; + char line[10000]; + char *cp; + char *cp2; + char *name; + char *value; + + fp = fopen(filename, "r"); + if (fp == (FILE *) 0) + { + perror(filename); + exit(1); + } + + while (fgets(line, sizeof(line), fp) != (char *) 0) + { + /* Trim comments. */ + if ((cp = strchr(line, '#')) != (char *) 0) + *cp = '\0'; + + /* Skip leading whitespace. */ + cp = line; + cp += strspn(cp, " \t\n\r"); + + /* Split line into words. */ + while (*cp != '\0') + { + /* Find next whitespace. */ + cp2 = cp + strcspn(cp, " \t\n\r"); + /* Insert EOS and advance next-word pointer. */ + while (*cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r') + *cp2++ = '\0'; + /* Split into name and value. */ + name = cp; + value = strchr(name, '='); + if (value != (char *) 0) + *value++ = '\0'; + /* Interpret. */ + if (strcasecmp(name, "debug") == 0) + { + no_value_required(name, value); + debug = 1; + } + else if (strcasecmp(name, "port") == 0) + { + value_required(name, value); + port = (unsigned short) atoi(value); + } + else if (strcasecmp(name, "dir") == 0) + { + value_required(name, value); + dir = e_strdup(value); + } + else if (strcasecmp(name, "chroot") == 0) + { + no_value_required(name, value); + do_chroot = 1; + no_symlink_check = 1; + } + else if (strcasecmp(name, "nochroot") == 0) + { + no_value_required(name, value); + do_chroot = 0; + no_symlink_check = 0; + } + else if (strcasecmp(name, "data_dir") == 0) + { + value_required(name, value); + data_dir = e_strdup(value); + } + else if (strcasecmp(name, "symlink") == 0) + { + no_value_required(name, value); + no_symlink_check = 0; + } + else if (strcasecmp(name, "nosymlink") == 0) + { + no_value_required(name, value); + no_symlink_check = 1; + } + else if (strcasecmp(name, "symlinks") == 0) + { + no_value_required(name, value); + no_symlink_check = 0; + } + else if (strcasecmp(name, "nosymlinks") == 0) + { + no_value_required(name, value); + no_symlink_check = 1; + } + else if (strcasecmp(name, "user") == 0) + { + value_required(name, value); + user = e_strdup(value); + } + else if (strcasecmp(name, "cgipat") == 0) + { + value_required(name, value); + cgi_pattern = e_strdup(value); + } + else if (strcasecmp(name, "cgilimit") == 0) + { + value_required(name, value); + cgi_limit = atoi(value); + } + else if (strcasecmp(name, "cgitimelimit") == 0) + { + value_required(name, value); + cgi_timelimit = atoi(value); + } + else if (strcasecmp(name, "urlpat") == 0) + { + value_required(name, value); + url_pattern = e_strdup(value); + } + else if (strcasecmp(name, "noemptyreferers") == 0) + { + no_value_required(name, value); + no_empty_referers = 1; + } + else if (strcasecmp(name, "localpat") == 0) + { + value_required(name, value); + local_pattern = e_strdup(value); + } + else if (strcasecmp(name, "throttles") == 0) + { + value_required(name, value); + throttlefile = e_strdup(value); + } + else if (strcasecmp(name, "host") == 0) + { + value_required(name, value); + hostname = e_strdup(value); + } + else if (strcasecmp(name, "logfile") == 0) + { + value_required(name, value); + logfile = e_strdup(value); + } + else if (strcasecmp(name, "vhost") == 0) + { + no_value_required(name, value); + do_vhost = 1; + } + else if (strcasecmp(name, "novhost") == 0) + { + no_value_required(name, value); + do_vhost = 0; + } + else if (strcasecmp(name, "globalpasswd") == 0) + { + no_value_required(name, value); + do_global_passwd = 1; + } + else if (strcasecmp(name, "noglobalpasswd") == 0) + { + no_value_required(name, value); + do_global_passwd = 0; + } + else if (strcasecmp(name, "pidfile") == 0) + { + value_required(name, value); + pidfile = e_strdup(value); + } + else if (strcasecmp(name, "charset") == 0) + { + value_required(name, value); + charset = e_strdup(value); + } + else if (strcasecmp(name, "p3p") == 0) + { + value_required(name, value); + p3p = e_strdup(value); + } + else if (strcasecmp(name, "max_age") == 0) + { + value_required(name, value); + max_age = atoi(value); + } + else + { + (void) fprintf(stderr, "%s: unknown config option '%s'\n", + argv0, name); + exit(1); + } + + /* Advance to next word. */ + cp = cp2; + cp += strspn(cp, " \t\n\r"); + } + } + + (void) fclose(fp); +} + +static void value_required(char *name, char *value) +{ + if (value == (char *) 0) + { + (void) fprintf(stderr, "%s: value required for %s option\n", argv0, name); + exit(1); + } +} + + +static void no_value_required(char *name, char *value) +{ + if (value != (char *) 0) + { + (void) fprintf(stderr, "%s: no value required for %s option\n", + argv0, name); + exit(1); + } +} +#endif + + +static char *e_strdup(char *oldstr) +{ + char *newstr; + + newstr = strdup(oldstr); + if (newstr == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying a string"); + (void) fprintf(stderr, "%s: out of memory copying a string\n", argv0); + exit(1); + } + return newstr; +} + + +static void +lookup_hostname(httpd_sockaddr * sa4P, size_t sa4_len, int *gotv4P, + httpd_sockaddr * sa6P, size_t sa6_len, int *gotv6P) +{ +#ifdef USE_IPV6 + + struct addrinfo hints; + char portstr[10]; + int gaierr; + struct addrinfo *ai; + struct addrinfo *ai2; + struct addrinfo *aiv6; + struct addrinfo *aiv4; + + (void) memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + (void) snprintf(portstr, sizeof(portstr), "%d", (int) port); + if ((gaierr = getaddrinfo(hostname, portstr, &hints, &ai)) != 0) + { + syslog(LOG_CRIT, "getaddrinfo %.80s - %.80s", + hostname, gai_strerror(gaierr)); + (void) fprintf(stderr, "%s: getaddrinfo %s - %s\n", + argv0, hostname, gai_strerror(gaierr)); + exit(1); + } + + /* Find the first IPv6 and IPv4 entries. */ + aiv6 = (struct addrinfo *) 0; + aiv4 = (struct addrinfo *) 0; + for (ai2 = ai; ai2 != (struct addrinfo *) 0; ai2 = ai2->ai_next) + { + switch (ai2->ai_family) + { + case AF_INET6: + if (aiv6 == (struct addrinfo *) 0) + aiv6 = ai2; + break; + case AF_INET: + if (aiv4 == (struct addrinfo *) 0) + aiv4 = ai2; + break; + } + } + + if (aiv6 == (struct addrinfo *) 0) + *gotv6P = 0; + else + { + if (sa6_len < aiv6->ai_addrlen) + { + syslog(LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", + hostname, (unsigned long) sa6_len, + (unsigned long) aiv6->ai_addrlen); + exit(1); + } + (void) memset(sa6P, 0, sa6_len); + (void) memmove(sa6P, aiv6->ai_addr, aiv6->ai_addrlen); + *gotv6P = 1; + } + + if (aiv4 == (struct addrinfo *) 0) + *gotv4P = 0; + else + { + if (sa4_len < aiv4->ai_addrlen) + { + syslog(LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", + hostname, (unsigned long) sa4_len, + (unsigned long) aiv4->ai_addrlen); + exit(1); + } + (void) memset(sa4P, 0, sa4_len); + (void) memmove(sa4P, aiv4->ai_addr, aiv4->ai_addrlen); + *gotv4P = 1; + } + + freeaddrinfo(ai); + +#else /* USE_IPV6 */ + + struct hostent *he; + + *gotv6P = 0; + + (void) memset(sa4P, 0, sa4_len); + sa4P->sa.sa_family = AF_INET; + if (hostname == (char *) 0) + sa4P->sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + else + { + sa4P->sa_in.sin_addr.s_addr = inet_addr(hostname); + if ((int) sa4P->sa_in.sin_addr.s_addr == -1) + { + he = gethostbyname(hostname); + if (he == (struct hostent *) 0) + { +#ifdef HAVE_HSTRERROR + syslog(LOG_CRIT, "gethostbyname %.80s - %.80s", + hostname, hstrerror(h_errno)); + (void) fprintf(stderr, "%s: gethostbyname %s - %s\n", + argv0, hostname, hstrerror(h_errno)); +#else /* HAVE_HSTRERROR */ + syslog(LOG_CRIT, "gethostbyname %.80s failed", hostname); + (void) fprintf(stderr, "%s: gethostbyname %s failed\n", argv0, + hostname); +#endif /* HAVE_HSTRERROR */ + exit(1); + } + if (he->h_addrtype != AF_INET) + { + syslog(LOG_CRIT, "%.80s - non-IP network address", hostname); + (void) fprintf(stderr, "%s: %s - non-IP network address\n", + argv0, hostname); + exit(1); + } + (void) memmove(&sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length); + } + } + sa4P->sa_in.sin_port = htons(port); + *gotv4P = 1; + +#endif /* USE_IPV6 */ +} + + +static void read_throttlefile(char *throttlefile) +{ + FILE *fp; + char buf[5000]; + char *cp; + int len; + char pattern[5000]; + long max_limit, min_limit; + struct timeval tv; + + fp = fopen(throttlefile, "r"); + if (fp == (FILE *) 0) + { + syslog(LOG_CRIT, "%.80s - %m", throttlefile); + perror(throttlefile); + exit(1); + } + + (void) gettimeofday(&tv, (struct timezone *) 0); + + while (fgets(buf, sizeof(buf), fp) != (char *) 0) + { + /* Nuke comments. */ + cp = strchr(buf, '#'); + if (cp != (char *) 0) + *cp = '\0'; + + /* Nuke trailing whitespace. */ + len = strlen(buf); + while (len > 0 && + (buf[len - 1] == ' ' || buf[len - 1] == '\t' || + buf[len - 1] == '\n' || buf[len - 1] == '\r')) + buf[--len] = '\0'; + + /* Ignore empty lines. */ + if (len == 0) + continue; + + /* Parse line. */ + if (sscanf + (buf, " %4900[^ \t] %ld-%ld", pattern, &min_limit, &max_limit) == 3) + { + } + else if (sscanf(buf, " %4900[^ \t] %ld", pattern, &max_limit) == 2) + min_limit = 0; + else + { + syslog(LOG_CRIT, "unparsable line in %.80s - %.80s", throttlefile, buf); + (void) fprintf(stderr, + "%s: unparsable line in %.80s - %.80s\n", + argv0, throttlefile, buf); + continue; + } + + /* Nuke any leading slashes in pattern. */ + if (pattern[0] == '/') + (void) strcpy(pattern, &pattern[1]); + while ((cp = strstr(pattern, "|/")) != (char *) 0) + (void) strcpy(cp + 1, cp + 2); + + /* Check for room in throttles. */ + if (numthrottles >= maxthrottles) + { + if (maxthrottles == 0) + { + maxthrottles = 100; /* arbitrary */ + throttles = NEW(throttletab, maxthrottles); + } + else + { + maxthrottles *= 2; + throttles = RENEW(throttles, throttletab, maxthrottles); + } + if (throttles == (throttletab *) 0) + { + syslog(LOG_CRIT, "out of memory allocating a throttletab"); + (void) fprintf(stderr, + "%s: out of memory allocating a throttletab\n", argv0); + exit(1); + } + } + + /* Add to table. */ + throttles[numthrottles].pattern = e_strdup(pattern); + throttles[numthrottles].max_limit = max_limit; + throttles[numthrottles].min_limit = min_limit; + throttles[numthrottles].rate = 0; + throttles[numthrottles].bytes_since_avg = 0; + throttles[numthrottles].num_sending = 0; + + ++numthrottles; + } + (void) fclose(fp); +} + + +static void shut_down(void) +{ + int cnum; + struct timeval tv; + + (void) gettimeofday(&tv, (struct timezone *) 0); + logstats(&tv); + for (cnum = 0; cnum < max_connects; ++cnum) + { + if (connects[cnum].conn_state != CNST_FREE) + httpd_close_conn(connects[cnum].hc, &tv); + if (connects[cnum].hc != (httpd_conn *) 0) + { + httpd_destroy_conn(connects[cnum].hc); + free((void *) connects[cnum].hc); + --httpd_conn_count; + connects[cnum].hc = (httpd_conn *) 0; + } + } + if (hs != (httpd_server *) 0) + { + httpd_server *ths = hs; + hs = (httpd_server *) 0; + if (ths->listen4_fd != -1) + fdwatch_del_fd(ths->listen4_fd); + if (ths->listen6_fd != -1) + fdwatch_del_fd(ths->listen6_fd); + httpd_terminate(ths); + } + //mmc_destroy(); + tmr_destroy(); + free((void *) connects); + if (throttles != (throttletab *) 0) + free((void *) throttles); +} + + +static int handle_newconnect(struct timeval *tvP, int listen_fd) +{ + connecttab *c; + //ClientData client_data; + + /* This loops until the accept() fails, trying to start new + ** connections as fast as possible so we don't overrun the + ** listen queue. + */ + for (;;) + { + /* Is there room in the connection table? */ + if (num_connects >= max_connects) + { + /* Out of connection slots. Run the timers, then the + ** existing connections, and maybe we'll free up a slot + ** by the time we get back here. + */ + syslog(LOG_WARNING, "too many connections!"); + tmr_run(tvP); + return 0; + } + /* Get the first free connection entry off the free list. */ + if (first_free_connect == -1 + || connects[first_free_connect].conn_state != CNST_FREE) + { + syslog(LOG_CRIT, "the connects free list is messed up"); + exit(1); + } + c = &connects[first_free_connect]; + /* Make the httpd_conn if necessary. */ + if (c->hc == (httpd_conn *) 0) + { + c->hc = NEW(httpd_conn, 1); + if (c->hc == (httpd_conn *) 0) + { + syslog(LOG_CRIT, "out of memory allocating an httpd_conn"); + exit(1); + } + c->hc->initialized = 0; + ++httpd_conn_count; + } + + /* Get the connection. */ + switch (httpd_get_conn(hs, listen_fd, c->hc)) + { + /* Some error happened. Run the timers, then the + ** existing connections. Maybe the error will clear. + */ + case GC_FAIL: + tmr_run(tvP); + return 0; + + /* No more connections to accept for now. */ + case GC_NO_MORE: + return 1; + } + c->conn_state = CNST_READING; + /* Pop it off the free list. */ + first_free_connect = c->next_free_connect; + c->next_free_connect = -1; + ++num_connects; + //client_data.p = c; + c->active_at = tvP->tv_sec; + c->wakeup_timer = (Timer *) 0; + c->linger_timer = (Timer *) 0; + c->next_byte_index = 0; + c->numtnums = 0; + + /* Set the connection file descriptor to no-delay mode. */ + httpd_set_ndelay(c->hc->conn_fd); + + fdwatch_add_fd(c->hc->conn_fd, c, FDW_READ); + + ++stats_connections; + if (num_connects > stats_simultaneous) + stats_simultaneous = num_connects; + } +} + +static void check_paused(ClientData client_data, struct timeval *tvP) +{ + connecttab *c = client_data.p; + int result; + + result = httpd_check_paused(c->hc); + + if (result == 1) + { + if (tmr_create((struct timeval *)0, check_paused, client_data, 100, 0) == NULL) + { + syslog(LOG_CRIT, "tmr_create(check_paused) failed"); + exit(1); + } + } + else if (result == 0) + { + c->conn_state = CNST_READING; + } + else + finish_connection(c, tvP); +} + +static void handle_read(connecttab * c, struct timeval *tvP) +{ + int sz; + int action; + httpd_conn *hc = c->hc; + ClientData client_data; + + /* Is there room in our buffer to read more bytes? */ + if (hc->read_idx >= hc->read_size) + { + if (hc->read_size > 5000) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + 1000); + } + + /* Read some more bytes. */ + sz = read(hc->conn_fd, &(hc->read_buf[hc->read_idx]), + hc->read_size - hc->read_idx); + if (sz == 0) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + if (sz < 0) + { + /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. At first glance + ** you would think that connections returned by fdwatch as readable + ** should never give an EWOULDBLOCK; however, this apparently can + ** happen if a packet gets garbled. + */ + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + return; + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + hc->read_idx += sz; + c->active_at = tvP->tv_sec; + + /* Do we have a complete request yet? */ + switch (httpd_got_request(hc)) + { + case GR_NO_REQUEST: + return; + case GR_BAD_REQUEST: + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + + /* Yes. Try parsing and resolving it. */ + if (httpd_parse_request(hc) < 0) + { + finish_connection(c, tvP); + return; + } + + /* Check the throttle table */ + if (!check_throttles(c)) + { + httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl); + finish_connection(c, tvP); + return; + } + + /* Start the connection going. */ + action = httpd_start_request(hc, tvP); + //syslog(LOG_INFO, "[%p] action = %d", hc, action); + + if (action < 0) + { + /* Something went wrong. Close down the connection. */ + finish_connection(c, tvP); + return; + } + + if (action == 1) + { + //syslog(LOG_INFO, "[%p] pausing connection", hc); + c->conn_state = CNST_PAUSING; + client_data.p = c; + if (tmr_create((struct timeval *)0, check_paused, client_data, 100, 0) == NULL) + { + syslog(LOG_CRIT, "tmr_create(check_paused) failed"); + exit(1); + } + return; + } + + /* Fill in end_byte_index. */ + if (hc->got_range) + { + c->next_byte_index = hc->first_byte_index; + c->end_byte_index = hc->last_byte_index + 1; + } + else if (hc->bytes_to_send < 0) + c->end_byte_index = 0; + else + c->end_byte_index = hc->bytes_to_send; + + /* Check if it's already handled. */ + if (hc->file_address == (char *) 0) + { + /* No file address means someone else is handling it. */ + int tind; + for (tind = 0; tind < c->numtnums; ++tind) + throttles[c->tnums[tind]].bytes_since_avg += hc->bytes_sent; + c->next_byte_index = hc->bytes_sent; + finish_connection(c, tvP); + return; + } + if (c->next_byte_index >= c->end_byte_index) + { + /* There's nothing to send. */ + finish_connection(c, tvP); + return; + } + + /* Cool, we have a valid connection and a file to send to it. */ + c->conn_state = CNST_SENDING; + c->started_at = tvP->tv_sec; + c->wouldblock_delay = 0; + + fdwatch_del_fd(hc->conn_fd); + fdwatch_add_fd(hc->conn_fd, c, FDW_WRITE); +} + + +static void handle_send(connecttab * c, struct timeval *tvP) +{ + size_t max_bytes; + int sz, coast; + ClientData client_data; + time_t elapsed; + httpd_conn *hc = c->hc; + int tind; + + if (c->max_limit == THROTTLE_NOLIMIT) + max_bytes = 1000000000L; + else + max_bytes = c->max_limit / 4; /* send at most 1/4 seconds worth */ + + /* Do we need to write the headers first? */ + if (hc->responselen == 0) + { + /* No, just write the file. */ + sz = write(hc->conn_fd, &(hc->file_address[c->next_byte_index]), + MIN(c->end_byte_index - c->next_byte_index, max_bytes)); + } + else + { + /* Yes. We'll combine headers and file into a single writev(), + ** hoping that this generates a single packet. + */ + struct iovec iv[2]; + + iv[0].iov_base = hc->response; + iv[0].iov_len = hc->responselen; + iv[1].iov_base = &(hc->file_address[c->next_byte_index]); + iv[1].iov_len = MIN(c->end_byte_index - c->next_byte_index, max_bytes); + sz = writev(hc->conn_fd, iv, 2); + } + + if (sz < 0 && errno == EINTR) + return; + + if (sz == 0 || (sz < 0 && (errno == EWOULDBLOCK || errno == EAGAIN))) + { + /* This shouldn't happen, but some kernels, e.g. + ** SunOS 4.1.x, are broken and select() says that + ** O_NDELAY sockets are always writable even when + ** they're actually not. + ** + ** Current workaround is to block sending on this + ** socket for a brief adaptively-tuned period. + ** Fortunately we already have all the necessary + ** blocking code, for use with throttling. + */ + c->wouldblock_delay += MIN_WOULDBLOCK_DELAY; + c->conn_state = CNST_PAUSING; + fdwatch_del_fd(hc->conn_fd); + client_data.p = c; + if (c->wakeup_timer != (Timer *) 0) + syslog(LOG_ERR, "replacing non-null wakeup_timer!"); + c->wakeup_timer = + tmr_create(tvP, wakeup_connection, client_data, c->wouldblock_delay, 0); + if (c->wakeup_timer == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(wakeup_connection) failed"); + exit(1); + } + return; + } + + if (sz < 0) + { + /* Something went wrong, close this connection. + ** + ** If it's just an EPIPE, don't bother logging, that + ** just means the client hung up on us. + ** + ** On some systems, write() occasionally gives an EINVAL. + ** Dunno why, something to do with the socket going + ** bad. Anyway, we don't log those either. + ** + ** And ECONNRESET isn't interesting either. + */ + if (errno != EPIPE && errno != EINVAL && errno != ECONNRESET) + syslog(LOG_ERR, "write - %m sending %.80s", hc->encodedurl); + clear_connection(c, tvP); + return; + } + + /* Ok, we wrote something. */ + c->active_at = tvP->tv_sec; + /* Was this a headers + file writev()? */ + if (hc->responselen > 0) + { + /* Yes; did we write only part of the headers? */ + if (sz < hc->responselen) + { + /* Yes; move the unwritten part to the front of the buffer. */ + int newlen = hc->responselen - sz; + (void) memmove(hc->response, &(hc->response[sz]), newlen); + hc->responselen = newlen; + sz = 0; + } + else + { + /* Nope, we wrote the full headers, so adjust accordingly. */ + sz -= hc->responselen; + hc->responselen = 0; + } + } + /* And update how much of the file we wrote. */ + c->next_byte_index += sz; + c->hc->bytes_sent += sz; + for (tind = 0; tind < c->numtnums; ++tind) + throttles[c->tnums[tind]].bytes_since_avg += sz; + + /* Are we done? */ + if (c->next_byte_index >= c->end_byte_index) + { + /* This connection is finished! */ + finish_connection(c, tvP); + return; + } + + /* Tune the (blockheaded) wouldblock delay. */ + if (c->wouldblock_delay > MIN_WOULDBLOCK_DELAY) + c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY; + + /* If we're throttling, check if we're sending too fast. */ + if (c->max_limit != THROTTLE_NOLIMIT) + { + elapsed = tvP->tv_sec - c->started_at; + if (elapsed == 0) + elapsed = 1; /* count at least one second */ + if (c->hc->bytes_sent / elapsed > c->max_limit) + { + c->conn_state = CNST_PAUSING; + fdwatch_del_fd(hc->conn_fd); + /* How long should we wait to get back on schedule? If less + ** than a second (integer math rounding), use 1/2 second. + */ + coast = c->hc->bytes_sent / c->max_limit - elapsed; + client_data.p = c; + if (c->wakeup_timer != (Timer *) 0) + syslog(LOG_ERR, "replacing non-null wakeup_timer!"); + c->wakeup_timer = tmr_create(tvP, wakeup_connection, client_data, + coast > 0 ? (coast * 1000L) : 500L, 0); + if (c->wakeup_timer == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(wakeup_connection) failed"); + exit(1); + } + } + } + /* (No check on min_limit here, that only controls connection startups.) */ +} + + +static void handle_linger(connecttab * c, struct timeval *tvP) +{ + char buf[4096]; + int r; + + /* In lingering-close mode we just read and ignore bytes. An error + ** or EOF ends things, otherwise we go until a timeout. + */ + r = read(c->hc->conn_fd, buf, sizeof(buf)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + return; + if (r <= 0) + really_clear_connection(c, tvP); +} + + +static int check_throttles(connecttab * c) +{ + int tnum; + long l; + + c->numtnums = 0; + c->max_limit = c->min_limit = THROTTLE_NOLIMIT; + for (tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS; ++tnum) + if (match(throttles[tnum].pattern, c->hc->expnfilename)) + { + /* If we're way over the limit, don't even start. */ + if (throttles[tnum].rate > throttles[tnum].max_limit * 2) + return 0; + /* Also don't start if we're under the minimum. */ + if (throttles[tnum].rate < throttles[tnum].min_limit) + return 0; + if (throttles[tnum].num_sending < 0) + { + syslog(LOG_ERR, + "throttle sending count was negative - shouldn't happen!"); + throttles[tnum].num_sending = 0; + } + c->tnums[c->numtnums++] = tnum; + ++throttles[tnum].num_sending; + l = throttles[tnum].max_limit / throttles[tnum].num_sending; + if (c->max_limit == THROTTLE_NOLIMIT) + c->max_limit = l; + else + c->max_limit = MIN(c->max_limit, l); + l = throttles[tnum].min_limit; + if (c->min_limit == THROTTLE_NOLIMIT) + c->min_limit = l; + else + c->min_limit = MAX(c->min_limit, l); + } + return 1; +} + +static void clear_throttles(connecttab * c, struct timeval *tvP) +{ + int tind; + + for (tind = 0; tind < c->numtnums; ++tind) + --throttles[c->tnums[tind]].num_sending; +} + + +static void update_throttles(ClientData client_data, struct timeval *nowP) +{ + int tnum, tind; + int cnum; + connecttab *c; + long l; + + /* Update the average sending rate for each throttle. This is only used + ** when new connections start up. + */ + for (tnum = 0; tnum < numthrottles; ++tnum) + { + throttles[tnum].rate = + (2 * throttles[tnum].rate + + throttles[tnum].bytes_since_avg / THROTTLE_TIME) / 3; + throttles[tnum].bytes_since_avg = 0; + /* Log a warning message if necessary. */ + if (throttles[tnum].rate > throttles[tnum].max_limit + && throttles[tnum].num_sending != 0) + { + if (throttles[tnum].rate > throttles[tnum].max_limit * 2) + syslog(LOG_NOTICE, + "throttle #%d '%.80s' rate %ld greatly exceeding limit %ld; %d sending", + tnum, throttles[tnum].pattern, throttles[tnum].rate, + throttles[tnum].max_limit, throttles[tnum].num_sending); + else + syslog(LOG_INFO, + "throttle #%d '%.80s' rate %ld exceeding limit %ld; %d sending", + tnum, throttles[tnum].pattern, throttles[tnum].rate, + throttles[tnum].max_limit, throttles[tnum].num_sending); + } + if (throttles[tnum].rate < throttles[tnum].min_limit + && throttles[tnum].num_sending != 0) + { + syslog(LOG_NOTICE, + "throttle #%d '%.80s' rate %ld lower than minimum %ld; %d sending", + tnum, throttles[tnum].pattern, throttles[tnum].rate, + throttles[tnum].min_limit, throttles[tnum].num_sending); + } + } + + /* Now update the sending rate on all the currently-sending connections, + ** redistributing it evenly. + */ + for (cnum = 0; cnum < max_connects; ++cnum) + { + c = &connects[cnum]; + if (c->conn_state == CNST_SENDING || c->conn_state == CNST_PAUSING) + { + c->max_limit = THROTTLE_NOLIMIT; + for (tind = 0; tind < c->numtnums; ++tind) + { + tnum = c->tnums[tind]; + l = throttles[tnum].max_limit / throttles[tnum].num_sending; + if (c->max_limit == THROTTLE_NOLIMIT) + c->max_limit = l; + else + c->max_limit = MIN(c->max_limit, l); + } + } + } +} + + +static void finish_connection(connecttab * c, struct timeval *tvP) +{ + /* If we haven't actually sent the buffered response yet, do so now. */ + httpd_write_response(c->hc); + + /* And clear. */ + clear_connection(c, tvP); +} + + +static void clear_connection(connecttab * c, struct timeval *tvP) +{ + ClientData client_data; + + if (c->wakeup_timer != (Timer *) 0) + { + tmr_cancel(c->wakeup_timer); + c->wakeup_timer = 0; + } + + /* This is our version of Apache's lingering_close() routine, which is + ** their version of the often-broken SO_LINGER socket option. For why + ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html + ** What we do is delay the actual closing for a few seconds, while reading + ** any bytes that come over the connection. However, we don't want to do + ** this unless it's necessary, because it ties up a connection slot and + ** file descriptor which means our maximum connection-handling rate + ** is lower. So, elsewhere we set a flag when we detect the few + ** circumstances that make a lingering close necessary. If the flag + ** isn't set we do the real close now. + */ + if (c->conn_state == CNST_LINGERING) + { + /* If we were already lingering, shut down for real. */ + tmr_cancel(c->linger_timer); + c->linger_timer = (Timer *) 0; + c->hc->should_linger = 0; + } + if (c->hc->should_linger) + { + if (c->conn_state != CNST_PAUSING) + fdwatch_del_fd(c->hc->conn_fd); + c->conn_state = CNST_LINGERING; + shutdown(c->hc->conn_fd, SHUT_WR); + fdwatch_add_fd(c->hc->conn_fd, c, FDW_READ); + client_data.p = c; + if (c->linger_timer != (Timer *) 0) + syslog(LOG_ERR, "replacing non-null linger_timer!"); + c->linger_timer = + tmr_create(tvP, linger_clear_connection, client_data, LINGER_TIME, 0); + if (c->linger_timer == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(linger_clear_connection) failed"); + exit(1); + } + } + else + really_clear_connection(c, tvP); +} + + +static void really_clear_connection(connecttab * c, struct timeval *tvP) +{ + stats_bytes += c->hc->bytes_sent; + if (c->conn_state != CNST_PAUSING) + fdwatch_del_fd(c->hc->conn_fd); + httpd_close_conn(c->hc, tvP); + clear_throttles(c, tvP); + if (c->linger_timer != (Timer *) 0) + { + tmr_cancel(c->linger_timer); + c->linger_timer = 0; + } + c->conn_state = CNST_FREE; + c->next_free_connect = first_free_connect; + first_free_connect = c - connects; /* division by sizeof is implied */ + --num_connects; +} + + +static void idle(ClientData client_data, struct timeval *nowP) +{ + int cnum; + connecttab *c; + + for (cnum = 0; cnum < max_connects; ++cnum) + { + c = &connects[cnum]; + switch (c->conn_state) + { + case CNST_READING: + if (nowP->tv_sec - c->active_at >= IDLE_READ_TIMELIMIT) + { + syslog(LOG_INFO, + "%.80s connection timed out reading", + httpd_ntoa(&c->hc->client_addr)); + httpd_send_err(c->hc, 408, httpd_err408title, "", + httpd_err408form, ""); + finish_connection(c, nowP); + } + break; + case CNST_SENDING: + case CNST_PAUSING: + if (nowP->tv_sec - c->active_at >= IDLE_SEND_TIMELIMIT) + { + syslog(LOG_INFO, + "%.80s connection timed out sending", + httpd_ntoa(&c->hc->client_addr)); + clear_connection(c, nowP); + } + break; + } + } +} + + +static void wakeup_connection(ClientData client_data, struct timeval *nowP) +{ + connecttab *c; + + c = (connecttab *) client_data.p; + c->wakeup_timer = (Timer *) 0; + if (c->conn_state == CNST_PAUSING) + { + c->conn_state = CNST_SENDING; + fdwatch_add_fd(c->hc->conn_fd, c, FDW_WRITE); + } +} + +static void +linger_clear_connection(ClientData client_data, struct timeval *nowP) +{ + connecttab *c; + + c = (connecttab *) client_data.p; + c->linger_timer = (Timer *) 0; + really_clear_connection(c, nowP); +} + + +static void occasional(ClientData client_data, struct timeval *nowP) +{ + //mmc_cleanup(nowP); + tmr_cleanup(); + watchdog_flag = 1; /* let the watchdog know that we are alive */ +} + + +#ifdef STATS_TIME +static void show_stats(ClientData client_data, struct timeval *nowP) +{ + logstats(nowP); +} +#endif /* STATS_TIME */ + + +/* Generate debugging statistics syslog messages for all packages. */ +static void logstats(struct timeval *nowP) +{ + struct timeval tv; + time_t now; + long up_secs, stats_secs; + + if (nowP == (struct timeval *) 0) + { + (void) gettimeofday(&tv, (struct timezone *) 0); + nowP = &tv; + } + now = nowP->tv_sec; + up_secs = now - start_time; + stats_secs = now - stats_time; + if (stats_secs == 0) + stats_secs = 1; /* fudge */ + stats_time = now; + syslog(LOG_INFO, + "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs); + + thttpd_logstats(stats_secs); + httpd_logstats(stats_secs); + //mmc_logstats(stats_secs); + fdwatch_logstats(stats_secs); + tmr_logstats(stats_secs); +} + + +/* Generate debugging statistics syslog message. */ +static void thttpd_logstats(long secs) +{ + if (secs > 0) + syslog(LOG_INFO, + " gb.httpd - %ld connections (%g/sec), %d max simultaneous, %lld bytes (%g/sec), %d httpd_conns allocated", + stats_connections, (float) stats_connections / secs, + stats_simultaneous, (int64_t) stats_bytes, + (float) stats_bytes / secs, httpd_conn_count); + stats_connections = 0; + stats_bytes = 0; + stats_simultaneous = 0; +} diff --git a/gb.httpd/src/thttpd.h b/gb.httpd/src/thttpd.h new file mode 100644 index 00000000..e85e7239 --- /dev/null +++ b/gb.httpd/src/thttpd.h @@ -0,0 +1,405 @@ +/* config.h - configuration defines for thttpd and libhttpd +** +** (c) 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _THTTPD_H_ +#define _THTTPD_H_ + +#include "config.h" + +/* The following configuration settings are sorted in order of decreasing +** likelihood that you'd want to change them - most likely first, least +** likely last. +** +** In case you're not familiar with the convention, "#ifdef notdef" +** is a Berkeleyism used to indicate temporarily disabled code. +** The idea here is that you re-enable it by just moving it outside +** of the ifdef. +*/ + +/* CONFIGURE: CGI programs must match this pattern to get executed. It's +** a simple shell-style wildcard pattern, with * meaning any string not +** containing a slash, ** meaning any string at all, and ? meaning any +** single character; or multiple such patterns separated by |. The +** patterns get checked against the filename part of the incoming URL. +** +** Restricting CGI programs to a single directory lets the site administrator +** review them for security holes, and is strongly recommended. If there +** are individual users that you trust, you can enable their directories too. +** +** You can also specify a CGI pattern on the command line, with the -c flag. +** Such a pattern overrides this compiled-in default. +** +** If no CGI pattern is specified, neither here nor on the command line, +** then CGI programs cannot be run at all. If you want to disable CGI +** as a security measure that's how you do it, just don't define any +** pattern here and don't run with the -c flag. +*/ +#ifdef notdef +/* Some sample patterns. Allow programs only in one central directory: */ +#define CGI_PATTERN "/cgi-bin/*" +/* Allow programs in a central directory, or anywhere in a trusted +** user's tree: */ +#define CGI_PATTERN "/cgi-bin/*|/jef/**" +/* Allow any program ending with a .cgi: */ +#define CGI_PATTERN "**.cgi" +/* When virtual hosting, enable the central directory on every host: */ +#define CGI_PATTERN "/*/cgi-bin/*" +#endif + +/* CONFIGURE: How many seconds to allow CGI programs to run before killing +** them. This is in case someone writes a CGI program that goes into an +** infinite loop, or does a massive database lookup that would take hours, +** or whatever. If you don't want any limit, comment this out, but that's +** probably a really bad idea. +*/ +#define CGI_TIMELIMIT 600 + +/* CONFIGURE: Maximum number of simultaneous CGI programs allowed. +** If this many are already running, then attempts to run more will +** return an HTTP 503 error. If this is not defined then there's +** no limit (and you'd better have a lot of memory). This can also be +** set in the runtime config file. +*/ +#ifdef notdef +#define CGI_LIMIT 50 +#endif + +/* CONFIGURE: How many seconds to allow for reading the initial request +** on a new connection. +*/ +#define IDLE_READ_TIMELIMIT 60 + +/* CONFIGURE: How many seconds before an idle connection gets closed. +*/ +#define IDLE_SEND_TIMELIMIT 300 + +/* CONFIGURE: The syslog facility to use. Using this you can set up your +** syslog.conf so that all thttpd messages go into a separate file. Note +** that even if you use the -l command line flag to send logging to a +** file, errors still get sent via syslog. +*/ +#define LOG_FACILITY LOG_DAEMON + +/* CONFIGURE: Tilde mapping. Many URLs use ~username to indicate a +** user's home directory. thttpd provides two options for mapping +** this construct to an actual filename. +** +** 1) Map ~username to /username. This is the recommended choice. +** Each user gets a subdirectory in the main chrootable web tree, and +** the tilde construct points there. The prefix could be something +** like "users", or it could be empty. See also the makeweb program +** for letting users create their own web subdirectories. +** +** 2) Map ~username to /. The postfix would be +** the name of a subdirectory off of the user's actual home dir, something +** like "public_html". This is what Apache and other servers do. The problem +** is, you can't do this and chroot() at the same time, so it's inherently +** a security hole. This is strongly dis-recommended, but it's here because +** some people really want it. Use at your own risk. +** +** You can also leave both options undefined, and thttpd will not do +** anything special about tildes. Enabling both options is an error. +*/ +#ifdef notdef +#define TILDE_MAP_1 "users" +#define TILDE_MAP_2 "public_html" +#endif + +/* CONFIGURE: The file to use for authentication. If this is defined then +** thttpd checks for this file in the local directory before every fetch. +** If the file exists then authentication is done, otherwise the fetch +** proceeds as usual. +** +** If you undefine this then thttpd will not implement authentication +** at all and will not check for auth files, which saves a bit of CPU time. +*/ +//#define AUTH_FILE ".htpasswd" + +/* CONFIGURE: The default character set name to use with text MIME types. +** This gets substituted into the MIME types where they have a "%s". +** +** You can override this in the config file with the "charset" setting, +** or on the command like with the -T flag. +*/ +#define DEFAULT_CHARSET "utf-8" + + +/* Most people won't want to change anything below here. */ + +/* CONFIGURE: This controls the SERVER_NAME environment variable that gets +** passed to CGI programs. By default thttpd does a gethostname(), which +** gives the host's canonical name. If you want to always use some other name +** you can define it here. +** +** Alternately, if you want to run the same thttpd binary on multiple +** machines, and want to build in alternate names for some or all of +** them, you can define a list of canonical name to altername name +** mappings. thttpd seatches the list and when it finds a match on +** the canonical name, that alternate name gets used. If no match +** is found, the canonical name gets used. +** +** If both SERVER_NAME and SERVER_NAME_LIST are defined here, thttpd searches +** the list as above, and if no match is found then SERVER_NAME gets used. +** +** In any case, if thttpd is started with the -h flag, that name always +** gets used. +*/ +#ifdef notdef +#define SERVER_NAME "your.hostname.here" +#define SERVER_NAME_LIST \ + "canonical.name.here/alternate.name.here", \ + "canonical.name.two/alternate.name.two" +#endif + +/* CONFIGURE: Undefine this if you want thttpd to hide its specific version +** when returning into to browsers. Instead it'll just say "thttpd" with +** no version. +*/ +//#define SHOW_SERVER_VERSION + +/* CONFIGURE: Define this if you want to always chroot(), without having +** to give the -r command line flag. Some people like this as a security +** measure, to prevent inadvertant exposure by accidentally running without -r. +** You can still disable it at runtime with the -nor flag. +*/ +#ifdef notdef +#define ALWAYS_CHROOT +#endif + +/* CONFIGURE: Define this if you want to always do virtual hosting, without +** having to give the -v command line flag. You can still disable it at +** runtime with the -nov flag. +*/ +#ifdef notdef +#define ALWAYS_VHOST +#endif + +/* CONFIGURE: If you're using the vhost feature and you have a LOT of +** virtual hostnames (like, hundreds or thousands), you will want to +** enable this feature. It avoids a problem with most Unix filesystems, +** where if there are a whole lot of items in a directory then name lookup +** becomes very slow. This feature makes thttpd use subdirectories +** based on the first characters of each hostname. You can set it to use +** from one to three characters. If the hostname starts with "www.", that +** part is skipped over. Dots are also skipped over, and if the name isn't +** long enough then "_"s are used. Here are some examples of how hostnames +** would get turned into directory paths, for each different setting: +** 1: www.acme.com -> a/www.acme.com +** 1: foobar.acme.com -> f/foobar.acme.com +** 2: www.acme.com -> a/c/www.acme.com +** 2: foobar.acme.com -> f/o/foobar.acme.com +** 3: www.acme.com -> a/c/m/www.acme.com +** 3: foobar.acme.com -> f/o/o/foobar.acme.com +** 3: m.tv -> m/t/v/m.tv +** 4: m.tv -> m/t/v/_/m.tv +** Note that if you compile this setting in but then forget to set up +** the corresponding subdirectories, the only error indication you'll +** get is a "404 Not Found" when you try to visit a site. So be careful. +*/ +#ifdef notdef +#define VHOST_DIRLEVELS 1 +#define VHOST_DIRLEVELS 2 +#define VHOST_DIRLEVELS 3 +#endif + +/* CONFIGURE: Define this if you want to always use a global passwd file, +** without having to give the -P command line flag. You can still disable +** it at runtime with the -noP flag. +*/ +#ifdef notdef +#define ALWAYS_GLOBAL_PASSWD +#endif + +/* CONFIGURE: When started as root, the default username to switch to after +** initializing. If this user (or the one specified by the -u flag) does +** not exist, the program will refuse to run. +*/ +#define DEFAULT_USER "nobody" + +/* CONFIGURE: When started as root, the program can automatically chdir() +** to the home directory of the user specified by -u or DEFAULT_USER. +** An explicit -d still overrides this. +*/ +#ifdef notdef +#define USE_USER_DIR +#endif + +/* CONFIGURE: If this is defined, some of the built-in error pages will +** have more explicit information about exactly what the problem is. +** Some sysadmins don't like this, for security reasons. +*/ +#define EXPLICIT_ERROR_PAGES + +/* CONFIGURE: Subdirectory for custom error pages. The error filenames are +** $WEBDIR/$ERR_DIR/err%d.html - if virtual hosting is enabled then +** $WEBDIR/hostname/$ERR_DIR/err%d.html is searched first. This allows +** different custom error pages for each virtual hosting web server. If +** no custom page for a given error can be found, the built-in error page +** is generated. If ERR_DIR is not defined at all, only the built-in error +** pages will be generated. +*/ +#define ERR_DIR "errors" + +/* CONFIGURE: Define this if you want a standard HTML tail containing +** $SERVER_SOFTWARE and $SERVER_ADDRESS to be appended to the custom error +** pages. (It is always appended to the built-in error pages.) +*/ +#define ERR_APPEND_SERVER_INFO + +/* CONFIGURE: nice(2) value to use for CGI programs. If this is undefined, +** CGI programs run at normal priority. +*/ +//#define CGI_NICE 10 + +/* CONFIGURE: $PATH to use for CGI programs. +*/ +//#define CGI_PATH "/usr/local/bin:/usr/ucb:/bin:/usr/bin" + +/* CONFIGURE: If defined, $LD_LIBRARY_PATH to use for CGI programs. +*/ +#ifdef notdef +#define CGI_LD_LIBRARY_PATH "/usr/local/lib:/usr/lib" +#endif + +/* CONFIGURE: How often to run the occasional cleanup job. +*/ +#define OCCASIONAL_TIME 120 + +/* CONFIGURE: Seconds between stats syslogs. If this is undefined then +** no stats are accumulated and no stats syslogs are done. +*/ +//#define STATS_TIME 3600 + +/* CONFIGURE: The mmap cache tries to keep the total number of mapped +** files below this number, so you don't run out of kernel file descriptors. +** If you have reconfigured your kernel to have more descriptors, you can +** raise this and thttpd will keep more maps cached. However it's not +** a hard limit, thttpd will go over it if you really are accessing +** a whole lot of files. +*/ +#define DESIRED_MAX_MAPPED_FILES 1000 + +/* CONFIGURE: The mmap cache also tries to keep the total mapped bytes +** below this number, so you don't run out of address space. Again +** it's not a hard limit, thttpd will go over it if you really are +** accessing a bunch of large files. +*/ +#define DESIRED_MAX_MAPPED_BYTES 1000000000 + +/* CONFIGURE: Minimum and maximum intervals between child-process reaping, +** in seconds. +*/ +#define MIN_REAP_TIME 30 +#define MAX_REAP_TIME 900 + + +/* You almost certainly don't want to change anything below here. */ + +/* CONFIGURE: When throttling CGI programs, we don't know how many bytes +** they send back to the client because it would be inefficient to +** interpose a counter. CGI programs are much more expensive than +** regular files to serve, so we set an arbitrary and high byte count +** that gets applied to all CGI programs for throttling purposes. +*/ +#define CGI_BYTECOUNT 25000 + +/* CONFIGURE: The default port to listen on. 80 is the standard HTTP port. +*/ +#define DEFAULT_PORT 80 + +/* CONFIGURE: A list of index filenames to check. The files are searched +** for in this order. +*/ +#define INDEX_NAMES "index.html", "index.htm", "index.xhtml", "index.xht", "Default.htm", "index.cgi" + +/* CONFIGURE: If this is defined then thttpd will automatically generate +** index pages for directories that don't have an explicit index file. +** If you want to disable this behavior site-wide, perhaps for security +** reasons, just undefine this. Note that you can disable indexing of +** individual directories by merely doing a "chmod 711" on them - the +** standard Unix file permission to allow file access but disable "ls". +*/ +//#define GENERATE_INDEXES + +/* CONFIGURE: Whether to log unknown request headers. Most sites will not +** want to log them, which will save them a bit of CPU time. +*/ +#ifdef notdef +#define LOG_UNKNOWN_HEADERS +#endif + +/* CONFIGURE: Whether to fflush() the log file after each request. If +** this is turned off there's a slight savings in CPU cycles. +*/ +//#define FLUSH_LOG_EVERY_TIME + +/* CONFIGURE: Time between updates of the throttle table's rolling averages. */ +#define THROTTLE_TIME 2 + +/* CONFIGURE: The listen() backlog queue length. The 1024 doesn't actually +** get used, the kernel uses its maximum allowed value. This is a config +** parameter only in case there's some OS where asking for too high a queue +** length causes an error. Note that on many systems the maximum length is +** way too small - see http://www.acme.com/software/thttpd/notes.html +*/ +#define LISTEN_BACKLOG 1024 + +/* CONFIGURE: Maximum number of throttle patterns that any single URL can +** be included in. This has nothing to do with the number of throttle +** patterns that you can define, which is unlimited. +*/ +#define MAXTHROTTLENUMS 10 + +/* CONFIGURE: Number of file descriptors to reserve for uses other than +** connections. Currently this is 10, representing one for the listen fd, +** one for dup()ing at connection startup time, one for reading the file, +** one for syslog, and possibly one for the regular log file, which is +** five, plus a factor of two for who knows what. +*/ +#define SPARE_FDS 10 + +/* CONFIGURE: How many milliseconds to leave a connection open while doing a +** lingering close. +*/ +#define LINGER_TIME 500 + +/* CONFIGURE: Maximum number of symbolic links to follow before +** assuming there's a loop. +*/ +#define MAX_LINKS 32 + +/* CONFIGURE: You don't even want to know. +*/ +#define MIN_WOULDBLOCK_DELAY 100L + +/* CONFIGURE: Pass the X-Cgi header to the CGI script +*/ +#define X_CGI_HEADER + +#define PUBLIC_PREFIX ".public/" + +#endif /* _THTTPD_H_ */ diff --git a/gb.httpd/src/timers.c b/gb.httpd/src/timers.c new file mode 100644 index 00000000..587a720d --- /dev/null +++ b/gb.httpd/src/timers.c @@ -0,0 +1,334 @@ +/* timers.c - simple timer routines +** +** (c) 1995,1998,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include + +#include +#include +//#include + +#include "main.h" +#include "timers.h" + + +#define HASH_SIZE 67 +static Timer *timers[HASH_SIZE]; +static Timer *free_timers; +static int alloc_count, active_count, free_count; + +ClientData JunkClientData; + + + +static unsigned int hash(Timer * t) +{ + /* We can hash on the trigger time, even though it can change over + ** the life of a timer via either the periodic bit or the tmr_reset() + ** call. This is because both of those guys call l_resort(), which + ** recomputes the hash and moves the timer to the appropriate list. + */ + return ((unsigned int) t->time.tv_sec ^ + (unsigned int) t->time.tv_usec) % HASH_SIZE; +} + + +static void l_add(Timer * t) +{ + int h = t->hash; + Timer *t2; + Timer *t2prev; + + t2 = timers[h]; + if (t2 == (Timer *) 0) + { + /* The list is empty. */ + timers[h] = t; + t->prev = t->next = (Timer *) 0; + } + else + { + if (t->time.tv_sec < t2->time.tv_sec || + (t->time.tv_sec == t2->time.tv_sec && + t->time.tv_usec <= t2->time.tv_usec)) + { + /* The new timer goes at the head of the list. */ + timers[h] = t; + t->prev = (Timer *) 0; + t->next = t2; + t2->prev = t; + } + else + { + /* Walk the list to find the insertion point. */ + for (t2prev = t2, t2 = t2->next; t2 != (Timer *) 0; + t2prev = t2, t2 = t2->next) + { + if (t->time.tv_sec < t2->time.tv_sec || + (t->time.tv_sec == t2->time.tv_sec && + t->time.tv_usec <= t2->time.tv_usec)) + { + /* Found it. */ + t2prev->next = t; + t->prev = t2prev; + t->next = t2; + t2->prev = t; + return; + } + } + /* Oops, got to the end of the list. Add to tail. */ + t2prev->next = t; + t->prev = t2prev; + t->next = (Timer *) 0; + } + } +} + + +static void l_remove(Timer * t) +{ + int h = t->hash; + + if (t->prev == (Timer *) 0) + timers[h] = t->next; + else + t->prev->next = t->next; + if (t->next != (Timer *) 0) + t->next->prev = t->prev; +} + + +static void l_resort(Timer * t) +{ + /* Remove the timer from its old list. */ + l_remove(t); + /* Recompute the hash. */ + t->hash = hash(t); + /* And add it back in to its new list, sorted correctly. */ + l_add(t); +} + + +void tmr_init(void) +{ + int h; + + for (h = 0; h < HASH_SIZE; ++h) + timers[h] = (Timer *) 0; + free_timers = (Timer *) 0; + alloc_count = active_count = free_count = 0; +} + + +Timer *tmr_create(struct timeval *nowP, TimerProc * timer_proc, + ClientData client_data, long msecs, int periodic) +{ + Timer *t; + + if (free_timers != (Timer *) 0) + { + t = free_timers; + free_timers = t->next; + --free_count; + } + else + { + t = (Timer *) malloc(sizeof(Timer)); + if (t == (Timer *) 0) + return (Timer *) 0; + ++alloc_count; + } + + t->timer_proc = timer_proc; + t->client_data = client_data; + t->msecs = msecs; + t->periodic = periodic; + if (nowP != (struct timeval *) 0) + t->time = *nowP; + else + (void) gettimeofday(&t->time, (struct timezone *) 0); + t->time.tv_sec += msecs / 1000L; + t->time.tv_usec += (msecs % 1000L) * 1000L; + if (t->time.tv_usec >= 1000000L) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + t->hash = hash(t); + /* Add the new timer to the proper active list. */ + l_add(t); + ++active_count; + + return t; +} + + +struct timeval *tmr_timeout(struct timeval *nowP) +{ + long msecs; + static struct timeval timeout; + + msecs = tmr_mstimeout(nowP); + if (msecs == INFTIM) + return (struct timeval *) 0; + timeout.tv_sec = msecs / 1000L; + timeout.tv_usec = (msecs % 1000L) * 1000L; + return &timeout; +} + + +long tmr_mstimeout(struct timeval *nowP) +{ + int h; + int gotone; + long msecs, m; + Timer *t; + + gotone = 0; + msecs = 0; /* make lint happy */ + /* Since the lists are sorted, we only need to look at the + ** first timer on each one. + */ + for (h = 0; h < HASH_SIZE; ++h) + { + t = timers[h]; + if (t != (Timer *) 0) + { + m = (t->time.tv_sec - nowP->tv_sec) * 1000L + + (t->time.tv_usec - nowP->tv_usec) / 1000L; + if (!gotone) + { + msecs = m; + gotone = 1; + } + else if (m < msecs) + msecs = m; + } + } + if (!gotone) + return INFTIM; + if (msecs <= 0) + msecs = 0; + return msecs; +} + + +void tmr_run(struct timeval *nowP) +{ + int h; + Timer *t; + Timer *next; + + for (h = 0; h < HASH_SIZE; ++h) + for (t = timers[h]; t != (Timer *) 0; t = next) + { + next = t->next; + /* Since the lists are sorted, as soon as we find a timer + ** that isn't ready yet, we can go on to the next list. + */ + if (t->time.tv_sec > nowP->tv_sec || + (t->time.tv_sec == nowP->tv_sec && t->time.tv_usec > nowP->tv_usec)) + break; + (t->timer_proc) (t->client_data, nowP); + if (t->periodic) + { + /* Reschedule. */ + t->time.tv_sec += t->msecs / 1000L; + t->time.tv_usec += (t->msecs % 1000L) * 1000L; + if (t->time.tv_usec >= 1000000L) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + l_resort(t); + } + else + tmr_cancel(t); + } +} + + +void tmr_reset(struct timeval *nowP, Timer * t) +{ + t->time = *nowP; + t->time.tv_sec += t->msecs / 1000L; + t->time.tv_usec += (t->msecs % 1000L) * 1000L; + if (t->time.tv_usec >= 1000000L) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + l_resort(t); +} + + +void tmr_cancel(Timer * t) +{ + /* Remove it from its active list. */ + l_remove(t); + --active_count; + /* And put it on the free list. */ + t->next = free_timers; + free_timers = t; + ++free_count; + t->prev = (Timer *) 0; +} + + +void tmr_cleanup(void) +{ + Timer *t; + + while (free_timers != (Timer *) 0) + { + t = free_timers; + free_timers = t->next; + --free_count; + free((void *) t); + --alloc_count; + } +} + + +void tmr_destroy(void) +{ + int h; + + for (h = 0; h < HASH_SIZE; ++h) + while (timers[h] != (Timer *) 0) + tmr_cancel(timers[h]); + tmr_cleanup(); +} + + +/* Generate debugging statistics syslog message. */ +void tmr_logstats(long secs) +{ + syslog(LOG_INFO, " timers - %d allocated, %d active, %d free", + alloc_count, active_count, free_count); + if (active_count + free_count != alloc_count) + syslog(LOG_ERR, "timer counts don't add up!"); +} diff --git a/gb.httpd/src/timers.h b/gb.httpd/src/timers.h new file mode 100644 index 00000000..abb2efb4 --- /dev/null +++ b/gb.httpd/src/timers.h @@ -0,0 +1,110 @@ +/* timers.h - header file for timers package +** +** (c) 1995,1998,1999,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _TIMERS_H_ +#define _TIMERS_H_ + +#include + +#ifndef INFTIM +#define INFTIM -1 +#endif /* INFTIM */ + +/* ClientData is a random value that tags along with a timer. The client +** can use it for whatever, and it gets passed to the callback when the +** timer triggers. +*/ +typedef union +{ + void *p; + int i; + long l; +} ClientData; + +extern ClientData JunkClientData; /* for use when you don't care */ + +/* The TimerProc gets called when the timer expires. It gets passed +** the ClientData associated with the timer, and a timeval in case +** it wants to schedule another timer. +*/ +typedef void TimerProc(ClientData client_data, struct timeval *nowP); + +/* The Timer struct. */ +typedef struct TimerStruct +{ + TimerProc *timer_proc; + ClientData client_data; + long msecs; + int periodic; + struct timeval time; + struct TimerStruct *prev; + struct TimerStruct *next; + int hash; +} Timer; + +/* Initialize the timer package. */ +extern void tmr_init(void); + +/* Set up a timer, either periodic or one-shot. Returns (Timer*) 0 on errors. */ +extern Timer *tmr_create(struct timeval *nowP, TimerProc * timer_proc, + ClientData client_data, long msecs, int periodic); + +/* Returns a timeout indicating how long until the next timer triggers. You +** can just put the call to this routine right in your select(). Returns +** (struct timeval*) 0 if no timers are pending. +*/ +extern struct timeval *tmr_timeout(struct timeval *nowP); + +/* Returns a timeout in milliseconds indicating how long until the next timer +** triggers. You can just put the call to this routine right in your poll(). +** Returns INFTIM (-1) if no timers are pending. +*/ +extern long tmr_mstimeout(struct timeval *nowP); + +/* Run the list of timers. Your main program needs to call this every so often, +** or as indicated by tmr_timeout(). +*/ +extern void tmr_run(struct timeval *nowP); + +/* Reset the clock on a timer, to current time plus the original timeout. */ +extern void tmr_reset(struct timeval *nowP, Timer * timer); + +/* Deschedule a timer. Note that non-periodic timers are automatically +** descheduled when they run, so you don't have to call this on them. +*/ +extern void tmr_cancel(Timer * timer); + +/* Clean up the timers package, freeing any unused storage. */ +extern void tmr_cleanup(void); + +/* Cancel all timers and free storage, usually in preparation for exitting. */ +extern void tmr_destroy(void); + +/* Generate debugging statistics syslog message. */ +extern void tmr_logstats(long secs); + +#endif /* _TIMERS_H_ */ diff --git a/gb.httpd/src/version.h b/gb.httpd/src/version.h new file mode 100644 index 00000000..271a4c4b --- /dev/null +++ b/gb.httpd/src/version.h @@ -0,0 +1,9 @@ +/* version.h - version defines for thttpd and libhttpd */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define SERVER_SOFTWARE "gb.httpd" +#define SERVER_ADDRESS "http://gambas.sourceforge.net" + +#endif /* _VERSION_H_ */ diff --git a/gb.image.imlib/AUTHORS b/gb.image.imlib/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/COPYING b/gb.image.imlib/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.image.imlib/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.image.imlib/ChangeLog b/gb.image.imlib/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/INSTALL b/gb.image.imlib/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.image.imlib/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.image.imlib/Makefile.am b/gb.image.imlib/Makefile.am new file mode 100644 index 00000000..b8e6e696 --- /dev/null +++ b/gb.image.imlib/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @IMAGE_IMLIB_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.image.imlib/NEWS b/gb.image.imlib/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/README b/gb.image.imlib/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/acinclude.m4 b/gb.image.imlib/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.image.imlib/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.image.imlib/component.am b/gb.image.imlib/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.image.imlib/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.image.imlib/configure.ac b/gb.image.imlib/configure.ac new file mode 100644 index 00000000..493a616d --- /dev/null +++ b/gb.image.imlib/configure.ac @@ -0,0 +1,18 @@ +dnl ---- configure.ac for gb.image.imlib + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-image-imlib],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.image.imlib) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + image_imlib, IMAGE_IMLIB, gb.image.imlib, [src], + 'imlib2 >= 1.4.0' + ) + +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.image.imlib/gambas.h b/gb.image.imlib/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.image.imlib/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.image.imlib/gb.draw.h b/gb.image.imlib/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.image.imlib/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.image.imlib/gb.image.h b/gb.image.imlib/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.image.imlib/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.image.imlib/gb_common.h b/gb.image.imlib/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.image.imlib/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.image.imlib/m4 b/gb.image.imlib/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.image.imlib/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.image.imlib/reconf b/gb.image.imlib/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.image.imlib/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.image.imlib/src/Makefile.am b/gb.image.imlib/src/Makefile.am new file mode 100644 index 00000000..9e9f3161 --- /dev/null +++ b/gb.image.imlib/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.image.imlib +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.image.imlib.la + +gb_image_imlib_la_LIBADD = @IMAGE_IMLIB_LIB@ +gb_image_imlib_la_LDFLAGS = -module @LD_FLAGS@ @IMAGE_IMLIB_LDFLAGS@ +gb_image_imlib_la_CPPFLAGS = @IMAGE_IMLIB_INC@ + +gb_image_imlib_la_SOURCES = main.c main.h c_image.c c_image.h c_imlib.c c_imlib.h diff --git a/gb.image.imlib/src/c_image.c b/gb.image.imlib/src/c_image.c new file mode 100644 index 00000000..f3a106f9 --- /dev/null +++ b/gb.image.imlib/src/c_image.c @@ -0,0 +1,281 @@ +/*************************************************************************** + + c_image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMAGE_C + +#include "gb.draw.h" +#include "c_image.h" + +static void free_image(GB_IMG *img, void *image) +{ + if (image) + { + imlib_context_set_image((Imlib_Image)image); + imlib_free_image(); + } +} + +static void *temp_image(GB_IMG *img) +{ + Imlib_Image image; + + if (!img->data) + image = NULL; + else + { + image = imlib_create_image_using_data(img->width, img->height, (DATA32 *)img->data); + } + + return image; +} + +static GB_IMG_OWNER _image_owner = { + "gb.image.imlib", + GB_IMAGE_BGRA, + free_image, + free_image, + temp_image, + NULL, + }; + +static Imlib_Image check_image(void *_object) +{ + Imlib_Image image = (Imlib_Image)IMAGE.Check(THIS, &_image_owner); + imlib_context_set_image(image); + imlib_image_set_has_alpha(TRUE); + return image; +} + +Imlib_Image IMAGE_check(void *_object) +{ + return check_image(_object); +} + +static void take_image(CIMAGE *_object, Imlib_Image image) +{ + imlib_context_set_image((Imlib_Image)image); + IMAGE.Take(THIS, &_image_owner, image, imlib_image_get_width(), imlib_image_get_height(), (void *)imlib_image_get_data()); +} + +CIMAGE *create_image(Imlib_Image image) +{ + CIMAGE *img; + + img = GB.New(GB.FindClass("Image"), NULL, NULL); + take_image(img, image); + return img; +} + +/***************************************************************************/ + +// Imlib2 cannot load images from memory + +#if 0 +static const char *get_error(Imlib_Load_Error error) +{ + switch(error) + { + case IMLIB_LOAD_ERROR_NONE: return NULL; + case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: return "File does not exist"; + case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: return "File is a directory"; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: return "Read permission denied"; + case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: return "Unknown file format"; + case IMLIB_LOAD_ERROR_PATH_TOO_LONG: return "Path too long"; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: return "Path component does not exist"; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY: return "Path component is not a directory"; + case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: return "Path points oustide of address space"; + case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: return "Too many symbolic links"; + case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: return "Out of memory"; + case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: return "Out of file descriptors"; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE: return "Write permission denied"; + case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: return "Out of disk space"; + default: return "Unknown error"; + }; +} + +BEGIN_METHOD(CIMAGE_load, GB_STRING path) + + Imlib_Image image; + Imlib_Load_Error error; + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + GB.Error("Unable to load image"); + return; + } + + image = imlib_load_image_with_error_return(GB.ToZeroString(ARG(path)), &error); + + if (image) + GB.ReturnObject(create_image(image)); + else + GB.Error("Unable to load image: &1", get_error(error)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_save, GB_STRING path; GB_INTEGER quality) + + Imlib_Load_Error error; + + check_image(THIS); + + imlib_context_set_image(THIS_IMAGE); + imlib_save_image_with_error_return(GB.ToZeroString(ARG(path)), &error); + + if (error != IMLIB_LOAD_ERROR_NONE) + GB.Error("Unable to save picture: &1", get_error(error)); + +END_METHOD +#endif + +BEGIN_METHOD(Image_Rotate, GB_FLOAT angle) + + Imlib_Image image; + + check_image(THIS); + imlib_context_set_anti_alias(TRUE); + image = imlib_create_rotated_image(VARG(angle)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height) + + Imlib_Image image; + + check_image(THIS); + imlib_context_set_anti_alias(TRUE); + image = imlib_create_cropped_scaled_image(0, 0, imlib_image_get_width(), imlib_image_get_height(), VARG(width), VARG(height)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_Blur, GB_INTEGER radius) + + Imlib_Image image; + + check_image(THIS); + + image = imlib_clone_image(); + imlib_context_set_image(image); + imlib_image_blur(VARGOPT(radius, 2)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Tile) + + Imlib_Image image; + + check_image(THIS); + + image = imlib_clone_image(); + imlib_context_set_image(image); + imlib_image_tile(); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_Sharpen, GB_INTEGER radius) + + Imlib_Image image; + + check_image(THIS); + + image = imlib_clone_image(); + imlib_context_set_image(image); + imlib_image_sharpen(VARGOPT(radius, 2)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT img; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + int x, y, w, h, sx, sy, sw, sh, src_w, src_h; + CIMAGE *image = (CIMAGE *)VARG(img); + Imlib_Image src; + + if (GB.CheckObject(image)) + return; + + src = check_image(image); + src_w = imlib_image_get_width(); + src_h = imlib_image_get_height(); + + check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, -1); + sh = VARGOPT(sh, -1); + + DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, src_w, src_h); + + imlib_blend_image_onto_image(src, TRUE, sx, sy, sw, sh, x, y, w, h); + +END_METHOD + +BEGIN_METHOD(Image_Scroll, GB_INTEGER dx; GB_INTEGER dy; GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + int x, y, width, height; + + check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + width = VARGOPT(width, imlib_image_get_width()); + height = VARGOPT(height, imlib_image_get_height()); + + imlib_image_scroll_rect(x, y, width, height, VARG(dx), VARG(dy)); + +END_METHOD + +GB_DESC ImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + //GB_STATIC_METHOD("Load", "Image", CIMAGE_load, "(Path)s"), + //GB_METHOD("Save", NULL, CIMAGE_save, "(Path)s[(Quality)i]"), + + GB_METHOD("Rotate", "Image", Image_Rotate, "(Angle)f"), + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i"), + + GB_METHOD("Blur", "Image", Image_Blur, "[(Radius)i]"), + GB_METHOD("Sharpen", "Image", Image_Sharpen, "[(Radius)i]"), + GB_METHOD("Tile", "Image", Image_Tile, NULL), + + GB_METHOD("PaintImage", NULL, Image_PaintImage, "(Image)Image;[(X)i(Y)i(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + GB_METHOD("Scroll", NULL, Image_Scroll, "(DX)i(DY)i[(X)i(Y)i(Width)i(Height)i]"), + //Gb_INTERFACE("Draw", &DRAW_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.image.imlib/src/c_image.h b/gb.image.imlib/src/c_image.h new file mode 100644 index 00000000..7fbded43 --- /dev/null +++ b/gb.image.imlib/src/c_image.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + c_image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMAGE_H +#define __C_IMAGE_H + +#include "main.h" + +typedef + struct + { + GB_IMG img; + } + CIMAGE; + +#ifndef __C_IMAGE_C +extern GB_DESC ImageDesc[]; +#else + +#define THIS ((GB_IMG *)_object) +#define THIS_IMAGE ((Imlib_Image)THIS->temp_handle) + +#endif + +Imlib_Image IMAGE_check(void *_object); + +#endif /* __CIMAGE_H */ diff --git a/gb.image.imlib/src/c_imlib.c b/gb.image.imlib/src/c_imlib.c new file mode 100644 index 00000000..d07430c2 --- /dev/null +++ b/gb.image.imlib/src/c_imlib.c @@ -0,0 +1,129 @@ +/*************************************************************************** + + c_imlib.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMLIB_C + +#include "c_image.h" +#include "c_imlib.h" + +#if 0 +typedef + struct IMLIB_DRAW { + struct IMLIB_DRAW *previous; + CIMAGE *device; + Imlib_Image image; + } + IMLIB_DRAW; + +IMLIB_DRAW *_current = NULL; + +#define THIS _current +#define IMAGE (_current->image) + +static bool check_device() +{ + if (!_current) + { + GB.Error("No current device"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_DEVICE() \ + if (check_device()) \ + return; \ + imlib_context_set_image(THIS->image); + +/**** Cairo ****************************************************************/ + +BEGIN_METHOD(Imlib_Begin, GB_OBJECT device) + + void *device = VARG(device); + IMLIB_DRAW *draw; + + if (GB.CheckObject(device)) + return; + + GB.Alloc(POINTER(&draw), sizeof(IMLIB_DRAW)); + draw->previous = _current; + + if (GB.Is(device, GB.FindClass("Image"))) + { + draw->image = IMAGE_check(device); + } + else + { + GB.Free(POINTER(&draw)); + GB.Error("Bad device"); + return; + } + + draw->device = device; + GB.Ref(device); + + _current = draw; + +END_METHOD + +static void end_current() +{ + IMLIB_DRAW *draw = _current; + + if (!_current) + return; + + _current = draw->previous; + + GB.Unref(POINTER(&draw->device)); + GB.Free(POINTER(&draw)); +} + +BEGIN_METHOD_VOID(Imlib_End) + + if (check_device()) + return; + + end_current(); + +END_METHOD + +BEGIN_METHOD_VOID(Imlib_exit) + + while (_current) + end_current(); + +END_METHOD +#endif + +GB_DESC ImlibDesc[] = +{ + GB_DECLARE("Imlib", 0), GB_VIRTUAL_CLASS(), + + //GB_STATIC_PROPERTY("Image", "Image", Imlib_Image), + //GB_STATIC_PROPERTY("AntiAlias", "b", Imlib_AntiAlias), + + GB_END_DECLARE +}; + diff --git a/gb.image.imlib/src/c_imlib.h b/gb.image.imlib/src/c_imlib.h new file mode 100644 index 00000000..2758f10f --- /dev/null +++ b/gb.image.imlib/src/c_imlib.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + c_imlib.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMLIB_H +#define __C_IMLIB_H + +#include "main.h" + +#ifndef __C_IMLIB_C +extern GB_DESC ImlibDesc[]; +#endif + +#endif diff --git a/gb.image.imlib/src/gb.image.imlib.component b/gb.image.imlib/src/gb.image.imlib.component new file mode 100644 index 00000000..bb9d093c --- /dev/null +++ b/gb.image.imlib/src/gb.image.imlib.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.image.imlib +Author=Benoît Minisini +State=NotFinished +Requires=gb.image diff --git a/gb.image.imlib/src/main.c b/gb.image.imlib/src/main.c new file mode 100644 index 00000000..e387cdfa --- /dev/null +++ b/gb.image.imlib/src/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "c_image.h" +#include "c_imlib.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ImageDesc, + //ImlibDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + imlib_set_cache_size(0); + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.image.imlib/src/main.h b/gb.image.imlib/src/main.h new file mode 100644 index 00000000..9dee489d --- /dev/null +++ b/gb.image.imlib/src/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb_common.h" + +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.image.io/AUTHORS b/gb.image.io/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/COPYING b/gb.image.io/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.image.io/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.image.io/ChangeLog b/gb.image.io/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/INSTALL b/gb.image.io/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.image.io/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.image.io/Makefile.am b/gb.image.io/Makefile.am new file mode 100644 index 00000000..0f51dc58 --- /dev/null +++ b/gb.image.io/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @IMAGE_IO_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.image.io/NEWS b/gb.image.io/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/README b/gb.image.io/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/acinclude.m4 b/gb.image.io/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.image.io/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.image.io/component.am b/gb.image.io/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.image.io/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.image.io/configure.ac b/gb.image.io/configure.ac new file mode 100644 index 00000000..4b50d316 --- /dev/null +++ b/gb.image.io/configure.ac @@ -0,0 +1,18 @@ +dnl ---- configure.ac for gb.image.io + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-image-io],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.image.io) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + image_io, IMAGE_IO, gb.image.io, [src], + 'gdk-pixbuf-2.0 >= 2.4.13' + ) + +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.image.io/gambas.h b/gb.image.io/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.image.io/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.image.io/gb.image.h b/gb.image.io/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.image.io/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.image.io/gb_common.h b/gb.image.io/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.image.io/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.image.io/m4 b/gb.image.io/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.image.io/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.image.io/reconf b/gb.image.io/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.image.io/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.image.io/src/Makefile.am b/gb.image.io/src/Makefile.am new file mode 100644 index 00000000..37b00bcf --- /dev/null +++ b/gb.image.io/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.image.io +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.image.io.la + +gb_image_io_la_LIBADD = @IMAGE_IO_LIB@ +gb_image_io_la_LDFLAGS = -module @LD_FLAGS@ @IMAGE_IO_LDFLAGS@ +gb_image_io_la_CPPFLAGS = @IMAGE_IO_INC@ + +gb_image_io_la_SOURCES = main.c main.h c_image.c c_image.h diff --git a/gb.image.io/src/c_image.c b/gb.image.io/src/c_image.c new file mode 100644 index 00000000..80e8ca2d --- /dev/null +++ b/gb.image.io/src/c_image.c @@ -0,0 +1,311 @@ +/*************************************************************************** + + c_image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMAGE_C + +#include "c_image.h" + +#define LOAD_INC 65536L + +static void load_image(const char *addr, int len) +{ + GdkPixbuf *img = NULL; + const char *laddr; + int llen; + GdkPixbufLoader* loader = NULL; + GError *error = NULL; + int size; + int format; + GB_IMG *image; + + loader = gdk_pixbuf_loader_new(); + + laddr = addr; + llen = len; + while (llen > 0) + { + size = llen > LOAD_INC ? LOAD_INC : llen; + if (!gdk_pixbuf_loader_write(loader, (guchar*)laddr, (gsize)size, &error)) + { + fprintf(stderr, "gb.image.io: error: cannot load %d image bytes\n", size); + GB.Error(error->message); + goto __END; + } + laddr += size; + llen -= size; + } + + gdk_pixbuf_loader_close(loader, NULL); + + img = gdk_pixbuf_loader_get_pixbuf(loader); + if (!img) + { + GB.Error("Unable to load image"); + goto __END; + } + + g_object_ref(G_OBJECT(img)); + + // Rowstride breaks gb.image (it is rounded up so that a line is always a four bytes multiple). + if (gdk_pixbuf_get_n_channels(img) == 3) + { + GdkPixbuf *aimg; + aimg = gdk_pixbuf_add_alpha(img, FALSE, 0, 0, 0); + g_object_unref(G_OBJECT(img)); + img = aimg; + } + + //fprintf(stderr, "nchannels = %d size = %d x %d rowstride = %d\n", gdk_pixbuf_get_n_channels(img), gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), gdk_pixbuf_get_rowstride(img)); + + switch (gdk_pixbuf_get_n_channels(img)) + { + case 3: format = GB_IMAGE_RGB; break; + case 4: format = GB_IMAGE_RGBA; break; + default: + g_object_unref(G_OBJECT(img)); + GB.Error("Unsupported number of channels"); + goto __END; + } + + image = IMAGE.Create(gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), format, gdk_pixbuf_get_pixels(img)); + IMAGE.Convert(image, IMAGE.GetDefaultFormat()); + GB.ReturnObject(image); + + g_object_unref(G_OBJECT(img)); + +__END: + + if (loader) + g_object_unref(G_OBJECT(loader)); +} + + +BEGIN_METHOD(Image_Load, GB_STRING path) + + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + GB.Error("Unable to load image"); + return; + } + + load_image(addr, len); + + GB.ReleaseFile(addr, len); + +END_METHOD + + +BEGIN_METHOD(Image_FromString, GB_STRING data) + + load_image(STRING(data), LENGTH(data)); + +END_METHOD + + +static const char *FILE_get_ext(const char *path) +{ + const char *p; + + p = rindex(path, '/'); + if (p) + path = &p[1]; + + p = rindex(path, '.'); + if (p == NULL) + return &path[strlen(path)]; + else + return p + 1; +} + +BEGIN_METHOD(Image_Save, GB_STRING path; GB_INTEGER quality) + + char *path = GB.FileName(STRING(path), LENGTH(path)); + const char *ext = NULL; + bool ok = FALSE; + int b; + char *format = NULL; + GSList *formats, *iter; + GdkPixbuf *image = NULL; + int quality = VARGOPT(quality, -1); + char arg[4]; + GError *error = NULL; + + ext = FILE_get_ext(path); + if (!ext || !*ext) + { + GB.Error("No extension specified"); + goto __END; + } + + SYNCHRONIZE_IMAGE(THIS); + IMAGE.Convert(THIS, GB_IMAGE_RGBA); + image = gdk_pixbuf_new_from_data((const guchar *)THIS->data, GDK_COLORSPACE_RGB, TRUE, 8, THIS->width, THIS->height, THIS->width * sizeof(uint), NULL, NULL); + + formats = gdk_pixbuf_get_formats(); + + iter = formats; + while (iter && (!ok) ) + { + if (gdk_pixbuf_format_is_writable((GdkPixbufFormat*)iter->data)) + { + format = gdk_pixbuf_format_get_name((GdkPixbufFormat*)iter->data); + if (!strcasecmp(format, ext)) + { + ok = TRUE; + break; + } + else + g_free(format); + } + iter = iter->next; + } + + g_slist_free(formats); + + if (!ok) + { + if (!strcasecmp("jpg", ext)) + format = (char *)"jpeg"; + } + + if (!format) + { + GB.Error("Unknown format"); + goto __END; + } + + if (quality >= 0) + { + if (strcmp(format, "jpeg") == 0) + { + if (quality > 100) + quality = 100; + sprintf(arg, "%d", quality); + b = gdk_pixbuf_save(image, path, format, &error, "quality", arg, (void *)NULL); + } + else if (strcmp(format, "png") == 0) + { + if (quality > 9) + quality = 9; + sprintf(arg, "%d", quality); + b = gdk_pixbuf_save(image, path, format, &error, "compression", arg, (void *)NULL); + } + else + b = gdk_pixbuf_save(image, path, format, &error, (void *)NULL); + } + else + b = gdk_pixbuf_save(image, path, format, &error, (void *)NULL); + + if (ok) + g_free(format); + + if (!b) + { + GB.Error(error->message); + goto __END; + } + +__END: + + if (image) + g_object_unref(G_OBJECT(image)); + +END_METHOD + + +static GdkPixbuf *stretch_pixbuf(GdkPixbuf *img, int w, int h) +{ + GdkPixbuf *image = NULL; + int wimg, himg; + int ws, hs; + + if (w <= 0 && h <= 0) + goto __RETURN; + + wimg = gdk_pixbuf_get_width(img); + himg = gdk_pixbuf_get_height(img); + + if (w < 0) + w = wimg * h / himg; + else if (h < 0) + h = himg * w / wimg; + + if (w <= 0 || h <= 0) + goto __RETURN; + + ws = w; + hs = h; + if (ws < (wimg / 4)) + ws = w * 4; + if (hs < (himg / 4)) + hs = h * 4; + if (ws != w || hs != h) + { + image = gdk_pixbuf_scale_simple(img, ws, hs, GDK_INTERP_NEAREST); + g_object_unref(G_OBJECT(img)); + img = image; + } + + image = gdk_pixbuf_scale_simple(img, w, h, GDK_INTERP_BILINEAR); + +__RETURN: + + g_object_unref(G_OBJECT(img)); + return image; +} + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height) + + GdkPixbuf *img; + GB_IMG *image; + + SYNCHRONIZE_IMAGE(THIS); + IMAGE.Convert(THIS, GB_IMAGE_RGBA); + img = gdk_pixbuf_new_from_data((const guchar *)THIS->data, GDK_COLORSPACE_RGB, TRUE, 8, THIS->width, THIS->height, THIS->width * sizeof(uint), NULL, NULL); + + img = stretch_pixbuf(img, VARG(width), VARG(height)); + + image = IMAGE.Create(gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), GB_IMAGE_RGBA, gdk_pixbuf_get_pixels(img)); + IMAGE.Convert(image, IMAGE.GetDefaultFormat()); + GB.ReturnObject(image); + + g_object_unref(G_OBJECT(img)); + +END_METHOD + + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Image", Image_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Image_Save, "(Path)s[(Quality)i]"), + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i"), + + GB_END_DECLARE +}; + diff --git a/gb.image.io/src/c_image.h b/gb.image.io/src/c_image.h new file mode 100644 index 00000000..f6ec47cc --- /dev/null +++ b/gb.image.io/src/c_image.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + c_image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMAGE_H +#define __C_IMAGE_H + +#include "main.h" + +typedef + struct + { + GB_IMG img; + } + CIMAGE; + +#ifndef __C_IMAGE_C +extern GB_DESC CImageDesc[]; +#else + +#define THIS ((GB_IMG *)_object) + +#endif + +#endif /* __CIMAGE_H */ diff --git a/gb.image.io/src/gb.image.io.component b/gb.image.io/src/gb.image.io.component new file mode 100644 index 00000000..470a2b88 --- /dev/null +++ b/gb.image.io/src/gb.image.io.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.image.io +Author=Benoît Minisini +State=0 +Implements=ImageIO +Requires=gb.image diff --git a/gb.image.io/src/main.c b/gb.image.io/src/main.c new file mode 100644 index 00000000..37dadfc9 --- /dev/null +++ b/gb.image.io/src/main.c @@ -0,0 +1,51 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "c_image.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CImageDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + +#if !defined(GLIB_VERSION_2_36) + // Bug in the gdk-pixbuf documentation: the following is mandatory. + g_type_init(); +#endif /* !defined(GLIB_VERSION_2_36) */ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.image.io/src/main.h b/gb.image.io/src/main.h new file mode 100644 index 00000000..cdf048b4 --- /dev/null +++ b/gb.image.io/src/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb_common.h" + +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.jit.llvm/AUTHORS b/gb.jit.llvm/AUTHORS new file mode 100644 index 00000000..4fcce32d --- /dev/null +++ b/gb.jit.llvm/AUTHORS @@ -0,0 +1 @@ +Emil Lenngren diff --git a/gb.jit.llvm/COPYING b/gb.jit.llvm/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.jit.llvm/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.jit.llvm/ChangeLog b/gb.jit.llvm/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.jit.llvm/INSTALL b/gb.jit.llvm/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.jit.llvm/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.jit.llvm/Makefile.am b/gb.jit.llvm/Makefile.am new file mode 100644 index 00000000..ed93e642 --- /dev/null +++ b/gb.jit.llvm/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @JITLLVM_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.jit.llvm/NEWS b/gb.jit.llvm/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.jit.llvm/README b/gb.jit.llvm/README new file mode 100644 index 00000000..3122f86f --- /dev/null +++ b/gb.jit.llvm/README @@ -0,0 +1,17 @@ +You should use the latest version of LLVM. To check it out, run: + +svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm +cd llvm +mkdir build +cd build +../configure --prefix=/usr --enable-optimized --enable-jit --enable-shared +make -j4 +sudo make install + +You might need to edit the configure.ac file in order to set the llvm +location settings appropriate, followed by ./reconf. + +To enable the JIT for all functions in a Gambas class, add the word "Fast" +on a separate line at the top of the class file. The functions will then be +JIT compiled to native machine code, instead of letting the interpreter to +run it, if gb.jit is available. diff --git a/gb.jit.llvm/acinclude.m4 b/gb.jit.llvm/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.jit.llvm/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.jit.llvm/component.am b/gb.jit.llvm/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.jit.llvm/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.jit.llvm/configure.ac b/gb.jit.llvm/configure.ac new file mode 100644 index 00000000..4a36b8e9 --- /dev/null +++ b/gb.jit.llvm/configure.ac @@ -0,0 +1,54 @@ +dnl ---- configure.ac for gb.jit + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-jit-llvm],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.jit.llvm) +LT_INIT + +min_llvm_version=3.1 +max_llvm_version=3.5 +next_max_llvm_version=3.6 + +dnl llvm-config file can be forced with LLVM_CONFIG env var +if test "x$LLVM_CONFIG" = x; then + AC_PATH_PROG([LLVM_CONFIG], [llvm-config], [no]) +fi + +if test "x$LLVM_CONFIG" = xno; then + touch DISABLED DISABLED.gb.jit.llvm +else + AC_MSG_CHECKING([for LLVM, version between $min_llvm_version and $max_llvm_version]) + + LLVM_VERSION=`$LLVM_CONFIG --version` + + AX_COMPARE_VERSION($LLVM_VERSION, [ge], $min_llvm_version, [min_llvm_version_ok=y], [min_llvm_version_ok=n]) + AX_COMPARE_VERSION($LLVM_VERSION, [lt], $next_max_llvm_version, [max_llvm_version_ok=y], [max_llvm_version_ok=n]) + + if test "x$min_llvm_version_ok$max_llvm_version_ok" = xyy; then + AC_MSG_RESULT([yes ($LLVM_VERSION)]) + + if test "x$LLVM_LIBS" = x; then + LLVM_LIBS=-lLLVM-$LLVM_VERSION + fi + + else + AC_MSG_RESULT(no) + touch DISABLED DISABLED.gb.jit.llvm + fi +fi + +dnl [GB_FIND(libLLVM-$LLVM_VERSION.$SHLIBEXT, `$LLVM_CONFIG --prefix` /usr/lib/llvm* /usr/local /usr, lib)], + +GB_COMPONENT( + jitllvm, JITLLVM, gb.jit.llvm, [src], + [GB_FIND(llvm/ExecutionEngine/JIT.h llvm/Config/llvm-config.h llvm-c/Core.h, `$LLVM_CONFIG --prefix` /usr/lib/llvm* /usr/local /usr, include)], + [], + [$C_LIB `$LLVM_CONFIG --ldflags` $LLVM_LIBS], + [-I../../main/gbx -I../../main/share -D_DEBUG -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS]) + +AC_CONFIG_FILES([Makefile src/Makefile]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.jit.llvm/gambas.h b/gb.jit.llvm/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.jit.llvm/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.jit.llvm/gb_common.h b/gb.jit.llvm/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.jit.llvm/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.jit.llvm/m4 b/gb.jit.llvm/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.jit.llvm/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.jit.llvm/reconf b/gb.jit.llvm/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.jit.llvm/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.jit.llvm/src/Makefile.am b/gb.jit.llvm/src/Makefile.am new file mode 100644 index 00000000..dee551c1 --- /dev/null +++ b/gb.jit.llvm/src/Makefile.am @@ -0,0 +1,34 @@ +COMPONENT = gb.jit.llvm +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.jit.llvm.la +noinst_LTLIBRARIES = libjit.llvm.la + +libjit_llvm_la_LIBADD = +libjit_llvm_la_LDFLAGS = -module @LD_FLAGS@ +libjit_llvm_la_CPPFLAGS = @JITLLVM_INC@ +libjit_llvm_la_CXXFLAGS = $(AM_CFLAGS) -std=gnu++0x -fno-exceptions -fno-rtti + +gb_jit_llvm_la_LIBADD = libjit.la @JITLLVM_LIB@ +gb_jit_llvm_la_LDFLAGS = -module @LD_FLAGS@ @JITLLVM_LDFLAGS@ +gb_jit_llvm_la_CPPFLAGS = @JITLLVM_INC@ +gb_jit_llvm_la_CXXFLAGS = $(AM_CXXFLAGS) -std=gnu++0x -fno-exceptions + +libjit_llvm_la_SOURCES = \ + jit_gambas_pass.cpp \ + jit_gambas_pass.h + +gb_jit_llvm_la_SOURCES = \ + gb.jit.h \ + jit_api.cpp \ + jit_codegen_conv.h \ + jit_codegen.cpp \ + jit_compile.cpp \ + jit_conv.cpp \ + jit_expressions.cpp \ + jit.h \ + jit_read.cpp \ + jit_runtime.c \ + jit_runtime.h \ + main.cpp \ + main.h diff --git a/gb.jit.llvm/src/gb.jit.h b/gb.jit.llvm/src/gb.jit.h new file mode 100644 index 00000000..2fe62566 --- /dev/null +++ b/gb.jit.llvm/src/gb.jit.h @@ -0,0 +1,218 @@ +/*************************************************************************** + + gb.jit.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_JIT_H +#define __GB_JIT_H + +#include "gbx_extern.h" + +typedef + struct { + //void (*GetExec)(void); + + void (*F_EXEC_release)(TYPE type, VALUE *value); + void (*F_RELEASE_many)(VALUE* val, int n); + + void (*F_EXEC_push_unknown)(ushort code); + void (*F_EXEC_push_array)(ushort code); + + void (*F_EXEC_pop_unknown)(void); + void (*F_EXEC_pop_array)(ushort code); + + void *(*F_CENUM_create)(void *object); + void (*F_EXEC_enum_first)(PCODE code); + char (*F_EXEC_enum_next)(PCODE code); + + void (*F_EXEC_new)(void); + + void (*F_EXEC_enter_quick)(void); + void (*F_EXEC_enter)(void); + //void (*F_EXEC_jit_execute_function)(void); + void (*F_EXEC_native_quick)(void); + void (*F_EXEC_native)(void); + char (*F_EXEC_call_native)(void (*exec)(), void *object, TYPE type, VALUE *param); + void (*F_EXEC_function_real)(void); + void (*F_EXEC_leave_keep)(void); + void (*F_EXEC_leave_drop)(void); + void (*F_EXEC_function_loop)(void); + + char (*F_EXEC_special)(int special, CLASS *klass, void *object, int nparam, bool drop); + CLASS *(*F_EXEC_object_variant)(VALUE *val, OBJECT **pobject); + char (*F_EXEC_object_other)(VALUE *val, CLASS **pclass, OBJECT **pobject); + + EXTERN_FUNC_INFO (*F_EXTERN_get_function_info)(CLASS_EXTERN *ext); + void (*F_EXTERN_call)(void); + void *(*F_EXTERN_make_callback)(VALUE_FUNCTION* value); + + char *(*F_STRING_new)(const char *src, int len); + void (*F_STRING_new_temp_value)(VALUE *value, const char* src, int len); + void (*F_STRING_free_real)(char *ptr); + char *(*F_STRING_free_later)(char *ptr); + int (*F_STRING_compare)(const char *ptr1, int len1, const char *ptr2, int len2); + char (*F_STRING_equal_ignore_case_same)(const char *str1, const char *str2, int len); + int (*F_STRING_conv)(char **result, const char *str, int len, const char *src, const char *dst, char _throw); + + int (*F_OBJECT_comp_value)(VALUE *object_1, VALUE *object_2); + int (*F_COMPARE_object)(void *object_1, void *object_2); + char (*F_CLASS_inherits)(CLASS *klass, CLASS *parent); + void *(*F_OBJECT_create)(CLASS *klass, const char *name, void *parent, int nparam); + void (*F_CLASS_free)(void *object); + int (*F_SYMBOL_find)(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix); + + CLASS *(*F_CARRAY_get_array_class)(CLASS *klass, CTYPE ctype); + void *(*F_CARRAY_get_data_multi)(void *_object, GB_INTEGER *arg, int nparam); + void *(*F_CARRAY_create_static)(CLASS *klass, void *ref, CLASS_ARRAY *desc, void *data); + void *(*F_CSTRUCT_create_static)(void *ref, CLASS *klass, char *addr); + + void (*F_REGEXP_scan)(void *array, const char *pattern, int len_pattern, const char *string, int len_string); + + void (*F_VALUE_convert)(VALUE *value, TYPE type); + void (*F_VALUE_to_string)(VALUE *value, char **addr, int *len); + void (*F_VALUE_convert_float)(VALUE *value); + void (*F_VALUE_convert_variant)(VALUE *value); + void (*F_VALUE_convert_string)(VALUE *value); + char (*F_VALUE_is_null)(VALUE *val); + void (*F_VALUE_undo_variant)(VALUE *value); + + char (*F_NUMBER_from_string)(int option, const char *str, int len, VALUE *value); + void (*F_NUMBER_int_to_string)(uint64_t nbr, int prec, int base, VALUE *value); + + char (*F_LOCAL_format_number)(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, char local); + + int (*F_DATE_to_string)(char *buffer, VALUE *value); + char (*F_DATE_from_string)(const char *str, int len, VALUE *val, char local); + int (*F_DATE_comp)(DATE *date1, DATE *date2); + void (*F_DATE_timer)(double *result, int from_start); + void (*F_DATE_now)(VALUE *val); + void (*F_DATE_add)(VALUE *date, int period, int val); + void (*F_DATE_diff)(VALUE *date1, VALUE *date2, int period); + + void (*F_randomize)(char set, uint seed); + double (*F_rnd)(void); + + void (*F_THROW)(int code, ...) NORETURN; + void (*F_ERROR_panic)(const char *error, ...) NORETURN; + void (*F_ERROR_propagate)(void) NORETURN; + void (*F_ERROR_reset)(ERROR_INFO *info); + void (*F_ERROR_set_last)(char bt); + void (*F_ERROR_lock)(void); + void (*F_ERROR_unlock)(void); + + const char *(*F_TYPE_get_name)(TYPE type); + + void (*F_SUBR_not)(ushort code); + void (*F_SUBR_and_)(ushort code); + void (*F_SUBR_cat)(ushort code); + void (*F_SUBR_file)(ushort code); + void (*F_SUBR_like)(ushort code); + void (*F_SUBR_string)(void); + void (*F_SUBR_upper)(ushort code); + void (*F_SUBR_instr)(ushort code); + void (*F_SUBR_subst)(ushort code); + void (*F_SUBR_replace)(ushort code); + void (*F_SUBR_split)(ushort code); + void (*F_SUBR_strcomp)(ushort code); + void (*F_SUBR_sconv)(ushort code); + void (*F_SUBR_abs)(ushort code); + void (*F_SUBR_int)(ushort code); + void (*F_SUBR_fix)(ushort code); + void (*F_SUBR_sgn)(ushort code); + void (*F_SUBR_min_max)(ushort code); + void (*F_SUBR_choose)(ushort code); + void (*F_SUBR_bit)(ushort code); + void (*F_SUBR_is_type)(ushort code); + void (*F_SUBR_hex_bin)(ushort code); + void (*F_SUBR_val)(ushort code); + void (*F_SUBR_format)(ushort code); + void (*F_SUBR_year)(ushort code); + void (*F_SUBR_week)(ushort code); + void (*F_SUBR_date)(ushort code); + void (*F_SUBR_time)(ushort code); + void (*F_SUBR_eval)(ushort code); + void (*F_SUBR_debug)(void); + void (*F_SUBR_wait)(ushort code); + void (*F_SUBR_open)(ushort code); + void (*F_SUBR_close)(ushort code); + void (*F_SUBR_input)(ushort code); + void (*F_SUBR_linput)(void); + void (*F_SUBR_print)(ushort code); + void (*F_SUBR_read)(ushort code); + void (*F_SUBR_write)(ushort code); + void (*F_SUBR_flush)(void); + void (*F_SUBR_lock)(void); + void (*F_SUBR_inp_out)(ushort code); + void (*F_SUBR_eof)(ushort code); + void (*F_SUBR_lof)(ushort code); + void (*F_SUBR_seek)(ushort code); + void (*F_SUBR_kill)(ushort code); + void (*F_SUBR_move)(ushort code); + void (*F_SUBR_exist)(ushort code); + void (*F_SUBR_access)(ushort code); + void (*F_SUBR_stat)(ushort code); + void (*F_SUBR_dfree)(void); + void (*F_SUBR_temp)(ushort code); + void (*F_SUBR_isdir)(ushort code); + void (*F_SUBR_dir)(ushort code); + void (*F_SUBR_rdir)(ushort code); + void (*F_SUBR_exec)(ushort code); + void (*F_SUBR_alloc)(ushort code); + void (*F_SUBR_free)(void); + void (*F_SUBR_realloc)(ushort code); + void (*F_SUBR_strptr)(ushort code); + void (*F_SUBR_collection)(ushort code); + void (*F_SUBR_tr)(ushort code); + void (*F_SUBR_quote)(ushort code); + void (*F_SUBR_unquote)(ushort code); + void (*F_SUBR_ptr)(ushort code); + + void (*F_CLASS_load_from_jit)(CLASS *klass); + void (*F_CLASS_run_inits)(CLASS *klass); + + const char *(*F_DEBUG_get_current_position)(void); + void (*F_DEBUG_Profile_Add)(void *cp, void *fp, void *pc); + + void (*F_EXEC_quit)(void); + + + //In GB: LOCAL_gettext, CLASS_auto_create, GB_Raise, GB_CollectionGet, GB_CollectionSet + + } + GB_JIT_INTERFACE; + +typedef + struct { + intptr_t version; + void (*Init)(GB_JIT_INTERFACE *jif, char **STACK_limit, STACK_CONTEXT *EXEC_current, VALUE **SP, VALUE *TEMP, + VALUE *RET, char *GAMBAS_StopEvent, char **EXEC_enum, EXEC_GLOBAL *EXEC, + const char **EXEC_unknown_name, char *__EXEC_profile, char *__EXEC_profile_instr, unsigned char *__EXEC_quit_value, + void **EVENT_Last, ERROR_CONTEXT **ERROR_current, ERROR_HANDLER **ERROR_handler, const char *STRING_char_string); + void (*CompileAndExecute)(void); + void (*LoadClass)(CLASS *klass); + } + JIT_INTERFACE; + +#define JIT_INTERFACE_VERSION 1 + +#endif /* __GB_JIT_H */ diff --git a/gb.jit.llvm/src/gb.jit.llvm.component b/gb.jit.llvm/src/gb.jit.llvm.component new file mode 100644 index 00000000..5d62b136 --- /dev/null +++ b/gb.jit.llvm/src/gb.jit.llvm.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.jit.llvm +Name=Gambas JIT Compiler based on LLVM +Author=Emil Lenngren +Hidden=True diff --git a/gb.jit.llvm/src/jit.h b/gb.jit.llvm/src/jit.h new file mode 100644 index 00000000..03dcd899 --- /dev/null +++ b/gb.jit.llvm/src/jit.h @@ -0,0 +1,1309 @@ +/*************************************************************************** + + jit.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_H +#define __JIT_H + +#include + +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 3) + #include "llvm/IR/LLVMContext.h" + #include "llvm/IR/Module.h" + #include "llvm/IR/Constants.h" + #include "llvm/IR/DerivedTypes.h" + #include "llvm/IR/Intrinsics.h" + #include "llvm/IR/Instructions.h" +#else + #include "llvm/LLVMContext.h" + #include "llvm/Module.h" + #include "llvm/Constants.h" + #include "llvm/DerivedTypes.h" + #include "llvm/Intrinsics.h" + #include "llvm/Instructions.h" +#endif + +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 5) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/CFG.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/Support/CFG.h" +#endif + +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/Interpreter.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/raw_ostream.h" + +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 3) + #include "llvm/IR/IRBuilder.h" +#elif (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR ==2) + #include "llvm/IRBuilder.h" +#else + #include "llvm/Support/IRBuilder.h" +#endif + +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/PassManager.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#include +#include + +extern "C" { +#include +#include + +#define class klass + +#include "gb_common.h" +//#include "gb_error.h" +#include "gbx_type.h" + +#include "gb_limit.h" +//#include "gbx_subr.h" +#include "gbx_stack.h" +//#include "gbx_exec.h" + +#include "gbx_local.h" +#include "gbx_event.h" + +#define throw _throw +#include "gbx_c_array.h" +#undef throw + +#undef class +} + +#include "main.h" + +struct Expression; + +void JIT_conv(Expression*& value, TYPE type, Expression* other = NULL); + +void JIT_read(void); +void JIT_codegen(void); + +void ref_stack(void); +void set_ctrl_type(TYPE type, int index, CLASS* second = NULL); +TYPE get_ctrl_type(int index); +CLASS* get_ctrl_class(int index); + +int special_ctrl_type(TYPE type); +bool is_ctrl_type_used(int type, int index); +int special_ctrl_type(TYPE type); + +struct Expression; +void register_new_expression(Expression*); +void free_all_expressions(void); + +void mark_address_taken(int addr); + +unsigned short* get_current_read_pos(void); + +extern int ngosubs; +extern uint64_t func_byref_mask; + +template +static bool isa(Expression* expr){ + return typeid(*expr) == typeid(T); +} + +template +static T* dyn_cast(Expression* expr){ + if (typeid(*expr) == typeid(T)) + return (T*)expr; + return NULL; +} + +struct Expression { + TYPE type; + bool on_stack; + bool stack_if_ref; + bool no_ref_variant; + + void ref_on_stack(){ + stack_if_ref = true; + if (type == T_STRING || type == T_OBJECT || type == T_VARIANT || TYPE_is_pure_object(type)) + on_stack = true; + } + void must_on_stack(){ + on_stack = true; + } + Expression(){ + type = T_VOID; + on_stack = false; + stack_if_ref = false; + no_ref_variant = false; + register_new_expression(this); + } + virtual void codegen(){ + puts(typeid(*this).name()); + assert(false && "Codegen not done yet for this type"); + } + virtual llvm::Value* codegen_get_value(){ + puts(typeid(*this).name()); + assert(false && "codegen_get_value not done yet for this type"); + } + virtual void codegen_on_stack(){ + puts(typeid(*this).name()); + assert(false && "codegen_on_stack not done yet for this type"); + } + virtual ~Expression(){} +}; + +struct ConvExpression : Expression { + Expression* expr; + ConvExpression(Expression* expr, TYPE type) : expr(expr) { + this->type = type; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +enum FunctionType { + PrivateFn, + PureObjectFn, + PureObjectStaticFn, + VirtualObjectFn, + VirtualObjectStaticFn, + StaticFn, + EventFn, + + ClassFn, + ObjectAsFn + //UnknownFn +}; + +struct FunctionExpression { + llvm::Value* effective_class; + CLASS* function_class; + Expression* function_object; + CLASS_DESC* function_desc; + const char* function_unknown; + char function_kind; + char function_defined; + short function_index; + char function_expr_type; //Contains enum FunctionType +}; + +struct PushCStringExpression : Expression { + char* addr; + int start; + int len; + PushCStringExpression(char* a, int s, int l) : addr(a), start(s), len(l) { + type = T_CSTRING; + } + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +//PushQuick, PushInteger +struct PushIntegerExpression : Expression { + int bits; + int64_t i; + PushIntegerExpression(int bits, int64_t i) : bits(bits), i(i) { + switch(bits){ + case 1: type = T_BOOLEAN; return; + case 8: type = T_BYTE; return; + case 16: type = T_SHORT; return; + case 32: type = T_INTEGER; return; + case 64: type = T_LONG; return; + } + } + void codegen(); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct AddQuickExpression : Expression { + Expression* expr; + int add; + AddQuickExpression(Expression* val, int add); + void codegen(){ + if (on_stack) codegen_on_stack(); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct PushFloatExpression : Expression { + double val; + PushFloatExpression(int bits, double val) : val(val) { + type = bits == 32 ? T_SINGLE : T_FLOAT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushNullPointerExpression : Expression { + PushNullPointerExpression() { + type = T_POINTER; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushVoidDateExpression : Expression { + PushVoidDateExpression() { + type = T_DATE; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushNullExpression : Expression { + PushNullExpression() { + type = T_NULL; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushVoidExpression : Expression { + PushVoidExpression() { + type = T_VOID; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushLastExpression : Expression { + PushLastExpression() { + type = T_OBJECT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushComplexExpression : Expression { + Expression* op; + PushComplexExpression(Expression* op) : op(op) { + JIT_conv(this->op, T_FLOAT); + //FIXME + abort(); + } +}; + +struct ReadVariableExpression : Expression { + char* addr; + CTYPE* ctype; + CLASS* klass; + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +static TYPE ctype_to_type(CTYPE* ctype, CLASS* klass = CP){ + TYPE type = ctype->id; + if (ctype->id == TC_ARRAY){ + CTYPE* ctype2 = &klass->load->array[ctype->value]->ctype; + type = (TYPE)(void*)JIF.F_CARRAY_get_array_class(klass, *ctype2); + } else if (ctype->id == T_OBJECT || ctype->id == 14){ + if (ctype->id != T_OBJECT || ctype->value >= 0) + type = (TYPE)(void*)klass->load->class_ref[ctype->value]; + } + return type; +} + +struct PushAutoCreateExpression : Expression { + CLASS* klass; + PushAutoCreateExpression(CLASS* klass) : klass(klass) { + type = (TYPE)(void*)klass; + } + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushStaticExpression : ReadVariableExpression { + PushStaticExpression(int index); +}; + +struct PopStaticExpression : Expression { + Expression* val; + char* addr; + PopStaticExpression(Expression* val, int index); + void codegen(); +}; + +struct PushDynamicExpression : Expression { + int index; + int offset; + CTYPE* ctype; + PushDynamicExpression(int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PopDynamicExpression : Expression { + Expression* val; + int index; + int offset; + PopDynamicExpression(Expression* val, int index); + void codegen(); +}; + +struct PushFunctionExpression : Expression, FunctionExpression { + int index; + PushFunctionExpression(int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PushClassExpression : Expression { + CLASS* klass; + PushClassExpression(int index) { + type = T_CLASS; + klass = CP->load->class_ref[index]; + JIT_load_class(klass); + } + PushClassExpression(CLASS* klass) : klass(klass) { + type = T_CLASS; + } + void codegen_on_stack(); +}; + +struct PushLocalExpression : Expression { + int index; + PushLocalExpression(int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PushParamExpression : Expression { + int index; + PushParamExpression(int index) : index(index) { + type = FP->param[index + FP->n_param].type; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PopLocalExpression : Expression { + Expression* val; + int index; + PopLocalExpression(Expression* val, int index); + void codegen(); +}; + +struct PopParamExpression : Expression { + Expression* val; + int index; + PopParamExpression(Expression* val, int index); + void codegen(); +}; + +struct PopCtrlExpression : Expression { + Expression* val; + int index; + PopCtrlExpression(Expression* val, int index); + void codegen(); +}; + +struct PopOptionalExpression : Expression { + Expression* val; + int index; + bool is_default; + PopOptionalExpression(Expression* val, int index); + void codegen(); +}; + +struct PushExternExpression : Expression { + CLASS* klass; + Expression* object_to_release; + int index; + bool must_variant_dispatch; + PushExternExpression(CLASS* klass, int index, Expression* object_to_release) + : klass(klass), object_to_release(object_to_release), index(index) { + type = T_FUNCTION; + must_variant_dispatch = false; //Might get changed later in CallExpression::CallExpression + } +}; + +struct PushEventExpression : Expression, FunctionExpression { + int index; + PushEventExpression(int ind, const char* unknown_name); +}; + +struct PushArrayExpression : Expression { + std::vector args; + unsigned short* pc; + bool can_quick; + PushArrayExpression(Expression** it, int nargs); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PopArrayExpression : Expression { + std::vector args; + Expression* val; + unsigned short* pc; + bool can_quick; + PopArrayExpression(Expression** it, int nargs, Expression* val); + void codegen(); +}; + +struct PushSuperExpression : Expression { + PushSuperExpression(){ + type = (TYPE)(void*)CP->parent; + if (type == 0) + THROW(E_PARENT); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushMeExpression : Expression { + PushMeExpression() { + type = (TYPE)(void*)CP; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +//Base class, not to be used directly +struct PushPureObjectExpression : Expression { + Expression* obj; + int index; + PushPureObjectExpression(Expression* obj, int index) : obj(obj), index(index) {} + CLASS* klass(){ + return (CLASS*)(void*)obj->type; + } + CLASS_DESC* desc(){ + CLASS* c = (CLASS*)(void*)obj->type; + return c->table[index].desc; + } +}; + +struct PushPureObjectConstantExpression : PushPureObjectExpression { + PushPureObjectConstantExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = desc()->constant.type; + ref_stack(); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushPureObjectVariableExpression : PushPureObjectExpression { + PushPureObjectVariableExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + ref_stack(); + + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +/*struct PushPureObjectStaticVariableExpression : PushPureObjectExpression { + PushPureObjectStaticVariableExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = ctype_to_type(&desc()->variable.ctype); + } +};*/ + +struct PushPureObjectPropertyExpression : PushPureObjectExpression { + PushPureObjectPropertyExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + } + llvm::Value* codegen_private(bool get_value); + llvm::Value* codegen_get_value(){ return codegen_private(true); } + void codegen_on_stack(){ codegen_private(false); } +}; + +//The object expression must return null, but must still be executed +//This should only be used with virtual classes +//For example: Application.Env.Count +struct PushPureObjectStaticPropertyExpression : PushPureObjectExpression { + const char* name; + PushPureObjectStaticPropertyExpression(Expression* obj, int index, const char* name) + : PushPureObjectExpression(obj, index), name(name) { + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + } + llvm::Value* codegen_private(bool get_value); + llvm::Value* codegen_get_value(){ return codegen_private(true); } + void codegen_on_stack(){ codegen_private(false); } +}; + +struct PushPureObjectStructFieldExpression : PushPureObjectExpression { + PushPureObjectStructFieldExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushPureObjectUnknownExpression : Expression { + Expression* obj; + const char* name; + int name_id; + PushPureObjectUnknownExpression(Expression* obj, const char* name, int name_id) : obj(obj), name(name), name_id(name_id) { + type = T_VARIANT; + ref_stack(); + obj->must_on_stack(); + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PushPureObjectFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushPureObjectFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushPureObjectStaticFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushPureObjectStaticFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +//Virtual classes +struct PushVirtualPropertyExpression : PushPureObjectExpression { + PushVirtualPropertyExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index) { + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + + /*if (TYPE_is_pure_object(type)) + while (((CLASS*)(void*)type)->override) + type = (TYPE)(((CLASS*)(void*)type)->override);*/ + } + llvm::Value* codegen_private(bool get_value); + llvm::Value* codegen_get_value(){ return codegen_private(true); } + void codegen_on_stack(){ codegen_private(false); } +}; + +struct PushVirtualFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushVirtualFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushVirtualStaticFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushVirtualStaticFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushUnknownExpression : Expression { + Expression* obj; + unsigned short* pc; + int name_id; + PushUnknownExpression(Expression* obj, int name_id, unsigned short* pc) : + obj(obj), pc(pc), name_id(name_id) { + type = T_VARIANT; + ref_stack(); + obj->must_on_stack(); + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +//This actually reads an arbitrary memory address into a value +struct PushStaticVariableExpression : ReadVariableExpression { + PushStaticVariableExpression(CLASS_DESC_VARIABLE* var) { + this->type = var->type; + this->addr = (char*)var->klass->stat + var->offset; + this->ctype = &var->ctype; + this->klass = var->klass; + } +}; + +struct PushStaticPropertyExpression : Expression { + Expression* obj; + int index; + PushStaticPropertyExpression(Expression* obj, int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushStaticFunctionExpression : Expression, FunctionExpression { + Expression* obj; + int index; + PushStaticFunctionExpression(Expression* obj, int index, const char* unknown_name = NULL); + CLASS* klass(){ + return ((PushClassExpression*)obj)->klass; + //return (CLASS*)(void*)obj->type; + } + CLASS_DESC* desc(){ + CLASS_DESC* desc = klass()->table[index].desc; + return desc; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushStaticUnknownExpression : Expression { + CLASS* klass; + const char* name; + int name_id; + PushStaticUnknownExpression(CLASS* klass, const char* name, int name_id) : klass(klass), name(name), name_id(name_id) { + type = T_VARIANT; + ref_stack(); + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +//Base class, not to be used directly +struct PopPureObjectExpression : Expression { + Expression* obj; + Expression* val; + int index; + PopPureObjectExpression(Expression* obj, Expression* val, int index) + : obj(obj), val(val), index(index) {} + CLASS_DESC* desc(){ + CLASS* c = (CLASS*)(void*)obj->type; + return c->table[index].desc; + } +}; + +struct PopPureObjectVariableExpression : PopPureObjectExpression { + PopPureObjectVariableExpression(Expression* obj, Expression* val, int index) + : PopPureObjectExpression(obj, val, index) { + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + JIT_conv(this->val, type); + } + void codegen(); +}; + +struct PopPureObjectStructFieldExpression : PopPureObjectExpression { + PopPureObjectStructFieldExpression(Expression* obj, Expression* val, int index) + : PopPureObjectExpression(obj, val, index) { + if (desc()->variable.ctype.id == TC_ARRAY || desc()->variable.ctype.id == TC_STRUCT) + THROW_ILLEGAL(); + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + JIT_conv(this->val, type); + this->val->ref_on_stack(); + } + void codegen(); +}; + +/*struct PopPureObjectStaticVariableExpression : PopPureObjectExpression { + PopPureObjectStaticVariableExpression(Expression* obj, Expression* val, int index) + : PopPureObjectExpression(obj, val, index) { + type = ctype_to_type(&desc()->variable.ctype); + JIT_conv(val, type); + } +};*/ + +struct PopVirtualPropertyExpression : PopPureObjectExpression { + const char* name; + bool is_static; + PopVirtualPropertyExpression(Expression* obj, Expression* val, int index, const char* name, bool is_static); + void codegen(); +}; + +struct PopPureObjectPropertyExpression : PopPureObjectExpression { + PopPureObjectPropertyExpression(Expression* obj, Expression* val, int index); + void codegen(); +}; + +//Actually writes the contents of a Value to an arbitrary address +struct PopStaticVariableExpression : Expression { + char* addr; + Expression* val; + PopStaticVariableExpression(TYPE type, char* addr, Expression* val) + : addr(addr), val(val) { + this->type = type; + JIT_conv(this->val, type); + } + void codegen(); +}; + +struct PopStaticPropertyExpression : Expression { + CLASS* klass; + Expression* val; + int index; + PopStaticPropertyExpression(CLASS* klass, Expression* val, int index); + void codegen(); +}; + +struct PopUnknownPropertyUnknownExpression : Expression { + Expression* obj; + Expression* val; + const char* name; + PopUnknownPropertyUnknownExpression(Expression* obj, Expression* val, const char* name) + : obj(obj), val(val), name(name) { + //type = T_VARIANT; + ref_stack(); + val->must_on_stack(); + obj->must_on_stack(); + } + void codegen(); +}; + +struct PopUnknownExpression : Expression { + Expression* obj; + Expression* val; + int name_id; + unsigned short* pc; + PopUnknownExpression(Expression* obj, Expression* val, int name_id, unsigned short* pc) + : obj(obj), val(val), name_id(name_id), pc(pc) { + //type = T_VARIANT; + ref_stack(); + val->must_on_stack(); + obj->must_on_stack(); + } + void codegen(); +}; + + +/** +A Swap statement: +Push A +Push B +Pop A +Pop B +So we replace Push A, Push B, Pop A to a SwapExpression, +that is taken as input for Pop B +**/ +struct SwapExpression : Expression { + Expression* push_a_expr; + Expression* pop_a_expr; //Of course this includes Push B + SwapExpression(Expression* push_a, Expression* pop_a) : push_a_expr(push_a), pop_a_expr(pop_a) { + type = push_a_expr->type; + on_stack = push_a_expr->on_stack; + stack_if_ref = push_a_expr->stack_if_ref; + no_ref_variant = push_a_expr->no_ref_variant; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct CallExpression : Expression { + std::vector args; + std::vector byref_expressions; + Expression* func; + CLASS* klass; + CLASS_DESC* desc; + unsigned short* pc; + int index; + char kind; + bool variant_call; + bool can_quick; + CallExpression(Expression* function, int nargs, Expression** it, unsigned short* pc); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct OnGotoExpression : Expression { + Expression* condition; + std::vector destinations; + int default_addr; + OnGotoExpression(Expression* condition, std::vector&& destinations, int default_addr) + : condition(condition), destinations(destinations), default_addr(default_addr) { + for(size_t i=0, e=destinations.size(); i!=e; i++) + mark_address_taken(destinations[i]); + mark_address_taken(default_addr); + JIT_conv(condition, T_INTEGER); + } + void codegen(); +}; + +struct JumpExpression : Expression { + int addr; + JumpExpression(int addr) : addr(addr) { + mark_address_taken(addr); + } + void codegen(); +}; + +struct GosubExpression : Expression { + int end_ctrl; + + //Normal use + int gosubaddr; + + //On Gosub use + Expression* condition; + std::vector destinations; + int default_addr; + + GosubExpression(int end_ctrl, int gosubaddr) : end_ctrl(end_ctrl), gosubaddr(gosubaddr) { + mark_address_taken(gosubaddr); + } + + GosubExpression(int end_ctrl, Expression* condition, std::vector&& destinations, int default_addr) + : end_ctrl(end_ctrl), condition(condition), destinations(destinations), default_addr(default_addr) { + for(size_t i=0, e=destinations.size(); i!=e; i++) + mark_address_taken(destinations[i]); + mark_address_taken(default_addr); + JIT_conv(condition, T_INTEGER); + } + void codegen(); +}; + +struct JumpIfExpression : Expression { + Expression* val; + int jump_addr; + int next_addr; + bool jump_if_true; + JumpIfExpression(Expression* value, int jump_addr, int next_addr, bool t) + : val(value), jump_addr(jump_addr), next_addr(next_addr), jump_if_true(t) { + mark_address_taken(jump_addr); + mark_address_taken(next_addr); + JIT_conv(val, T_BOOLEAN); + } + void codegen(); +}; + +struct JumpFirstExpression : Expression { + Expression *to, *step; + int ctrl_to, local_var; + int body_addr, done_addr; + JumpFirstExpression(int ctrl_to, Expression* to_expr, Expression* step_expr, int local_var, int body_addr, int done_addr); + void codegen(); +}; + +struct JumpNextExpression : Expression { + int ctrl_to, local_var; + int body_addr, done_addr; + JumpNextExpression(int ctrl_to, int local_var, int body_addr, int done_addr) + : ctrl_to(ctrl_to), local_var(local_var), body_addr(body_addr), done_addr(done_addr) {} + void codegen(); +}; + +struct JumpEnumFirstExpression : Expression { + Expression* obj; + llvm::Value* effective_class; + llvm::Value* ob; + int ctrl; + JumpEnumFirstExpression(int ctrl, Expression* obj) + : obj(obj), ctrl(ctrl) { + set_ctrl_type(obj->type, ctrl); + } + void codegen(); +}; + +//Workaround for return value for JumpEnumNext statement +//Also in use for ByRef +struct OnStackExpression : Expression { + OnStackExpression(){ + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){} +}; + +struct JumpEnumNextExpression : Expression { + JumpEnumFirstExpression* jfirst; + OnStackExpression* retval; + int cont_addr; + int addr; + unsigned short* pc; + bool drop; + bool defined; + JumpEnumNextExpression(JumpEnumFirstExpression* jfirst, int cont_addr, int addr, unsigned short* pc, bool drop, OnStackExpression* retval); + void codegen(); +}; + +struct NopExpression : Expression { + char* buf; + bool test_stack; + + NopExpression(const char* str){ + int len = strlen(str); + buf = (char*)malloc(len+1); + memcpy(buf, str, len+1); + test_stack = true; + } + + NopExpression(bool test_stack) : test_stack(test_stack) { + buf = (char*)"..."; + } + + void codegen(); + ~NopExpression(){ + //free(buf); + } +}; + +struct ProfileLineExpression : Expression { + unsigned short* pc; + ProfileLineExpression(unsigned short* pc) : pc(pc) {} + void codegen(); +}; + +struct DropExpression : Expression { + Expression* expr; + DropExpression(Expression* expr) : expr(expr) {} + void codegen(); +}; + +//Dup is only used in: With something = something_else ... End With +struct DupedValueExpression : Expression { + llvm::Value* llvm_value; + DupedValueExpression(TYPE t){ + type = t; + llvm_value = NULL; + } + llvm::Value* codegen_get_value(){ return llvm_value; } +}; + +struct DupExpression : Expression { + Expression* expr; + DupedValueExpression* duped; + DupExpression(Expression*& expression) : expr(expression) { + expression = duped = new DupedValueExpression(type = expr->type); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct NewExpression : Expression { + std::vector args; + unsigned short* pc; + bool event; + NewExpression(Expression** it, int nargs, bool event); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct ReturnExpression : Expression { + Expression* retval; + unsigned short* pc; + int kind; + ReturnExpression(Expression* expr, int kind); + void codegen(); +}; + +struct QuitExpression : Expression { + Expression* quitval; + QuitExpression(Expression* quitval); + void codegen(); +}; + +struct StopEventExpression : Expression { + void codegen(); +}; + +struct TryExpression : Expression { + int addr1, addr2; + TryExpression(int addr1, int addr2) : addr1(addr1), addr2(addr2) { + mark_address_taken(addr1); + mark_address_taken(addr2); + } + void codegen(); +}; + +struct EndTryExpression : Expression { + void codegen(); +}; + +//Used as first statement in functions having a Catch/Finally +struct LargeTryExpression : Expression { + int addr2; + LargeTryExpression(){ + addr2 = (int)(EC - get_current_read_pos()); + mark_address_taken(0); + mark_address_taken(addr2); + } + void codegen(); +}; +//Inserted where EC points to +struct LargeCatchExpression : Expression { + void codegen(); +}; +//This is the Catch statement in the bytecode +struct CatchExpression : Expression { + void codegen(); +}; + +struct UnaryExpression : Expression { + Expression* expr; + UnaryExpression(Expression* expr) : expr(expr) {} +}; + +//Used by SubrExpression +struct CheckStringExpression : UnaryExpression { + CheckStringExpression(Expression* expr) : UnaryExpression(expr) { + type = T_STRING; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; +struct CheckIntegerExpression : UnaryExpression { + CheckIntegerExpression(Expression* expr) : UnaryExpression(expr) { + type = T_INTEGER; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; +struct CheckFloatExpression : UnaryExpression { + CheckFloatExpression(Expression* expr) : UnaryExpression(expr) { + type = T_FLOAT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; +struct CheckPointerExpression : UnaryExpression { + CheckPointerExpression(Expression* expr) : UnaryExpression(expr) { + type = T_POINTER; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + + +struct SubrExpression : Expression { + std::vector args; + int digit; + int extra; + unsigned short* pc; + + TYPE type2; + + SubrExpression(int digit, Expression** it, int nargs, int extra); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct BinOpExpression : Expression { + Expression *left, *right; + BinOpExpression(Expression** it) : left(it[0]), right(it[1]) {} + std::pair codegen_operands(); +}; + +struct EqExpression : BinOpExpression { + TYPE t; + EqExpression(Expression** it); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct NotExpression : UnaryExpression { + NotExpression(Expression* expr); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct LessExpression : BinOpExpression { + TYPE t; + LessExpression(Expression** it); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct NearExpression : BinOpExpression { + NearExpression(Expression** it) : BinOpExpression(it) { + type = T_BOOLEAN; + JIT_conv(left, T_STRING); + JIT_conv(right, T_STRING); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct AddSubBaseExpression : BinOpExpression { + AddSubBaseExpression(Expression** it); +}; + +struct AddExpression : AddSubBaseExpression { + AddExpression(Expression** it) : AddSubBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct SubExpression : AddSubBaseExpression { + SubExpression(Expression** it) : AddSubBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct MulExpression : BinOpExpression { + MulExpression(Expression** it); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct DivExpression : BinOpExpression { + DivExpression(Expression** it) : BinOpExpression(it) { + type = T_FLOAT; + ref_stack(); + JIT_conv(left, T_FLOAT); + JIT_conv(right, T_FLOAT); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct QuoRemBaseExpression : BinOpExpression { + QuoRemBaseExpression(Expression** it); +}; + +struct QuoExpression : QuoRemBaseExpression { + QuoExpression(Expression** it) : QuoRemBaseExpression(it) {} + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct RemExpression : QuoRemBaseExpression { + RemExpression(Expression** it) : QuoRemBaseExpression(it) {} + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PowExpression : BinOpExpression { + PowExpression(Expression** it) : BinOpExpression(it) { + JIT_conv(left, T_FLOAT); + if (TYPE_is_integer(right->type)){ + JIT_conv(right, T_INTEGER); + } else { + JIT_conv(right, T_FLOAT); + } + type = T_FLOAT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct AndOrXorBaseExpression : BinOpExpression { + AndOrXorBaseExpression(Expression** it); +}; + +struct AndExpression : AndOrXorBaseExpression { + AndExpression(Expression** it) : AndOrXorBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct OrExpression : AndOrXorBaseExpression { + OrExpression(Expression** it) : AndOrXorBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct XorExpression : AndOrXorBaseExpression { + XorExpression(Expression** it) : AndOrXorBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct IsExpression : BinOpExpression { + IsExpression(Expression** it) : BinOpExpression(it) { + JIT_conv(left, T_OBJECT); + type = T_BOOLEAN; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct NegExpression : UnaryExpression { + NegExpression(Expression* expr) : UnaryExpression(expr) { + type = expr->type; + if (type == T_VARIANT){ + ref_stack(); + no_ref_variant = true; + } else if (!TYPE_is_number(type)) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct CatFileExpression : Expression { + std::vector args; + CatFileExpression(Expression** it, int nargs){ + args.resize(nargs); + for(int i=0; imust_on_stack(); + } + type = T_STRING; + on_stack = true; + } +}; + +struct CatExpression : CatFileExpression { + CatExpression(Expression** it, int nargs) + : CatFileExpression(it, nargs) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct FileExpression : CatFileExpression { + FileExpression(Expression** it, int nargs) + : CatFileExpression(it, nargs) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct LikeExpression : BinOpExpression { + int kind; + LikeExpression(Expression** it, int kind) + : BinOpExpression(it), kind(kind) { + type = T_BOOLEAN; + left->must_on_stack(); + right->must_on_stack(); + //The runtime converts to string, so ref_stack is kind of needed .. + ref_stack(); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct Statement { + int addr; + bool address_taken; + Expression* expr; +}; + +extern std::vector all_statements; + +void mark_address_taken(int addr); + +#endif /* __JIT_H */ diff --git a/gb.jit.llvm/src/jit_api.cpp b/gb.jit.llvm/src/jit_api.cpp new file mode 100644 index 00000000..0ba3c8d1 --- /dev/null +++ b/gb.jit.llvm/src/jit_api.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + + jit_api.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_API_C + +#include "main.h" + +extern "C" { + +GB_JIT_INTERFACE JIF; + +char **_STACK_limit; +STACK_CONTEXT *_EXEC_current; +VALUE **_SP; +VALUE *_TEMP; +VALUE *_RET; + +char *_GAMBAS_StopEvent; +char **_EXEC_enum; +EXEC_GLOBAL *_EXEC; +const char **_EXEC_unknown_name; +char *_EXEC_profile; +char *_EXEC_profile_instr; +unsigned char *_EXEC_quit_value; + +void **_EVENT_Last; + +ERROR_CONTEXT **_ERROR_current; +ERROR_HANDLER **_ERROR_handler; + +const char *_STRING_char_string; + +void JIT_init(GB_JIT_INTERFACE *jif, char **__STACK_limit, STACK_CONTEXT *__EXEC_current, VALUE **__SP, VALUE *__TEMP, + VALUE *__RET, char *__GAMBAS_StopEvent, char **__EXEC_enum, EXEC_GLOBAL *__EXEC, + const char **__EXEC_unknown_name, char *__EXEC_profile, char *__EXEC_profile_instr, unsigned char *__EXEC_quit_value, + void **__EVENT_Last, ERROR_CONTEXT **__ERROR_current, ERROR_HANDLER **__ERROR_handler, const char *__STRING_char_string) +{ + JIF = *jif; + _STACK_limit = __STACK_limit; + _EXEC_current = __EXEC_current; + _SP = __SP; + _TEMP = __TEMP; + _RET = __RET; + _GAMBAS_StopEvent = __GAMBAS_StopEvent; + _EXEC_enum = __EXEC_enum; + _EXEC = __EXEC; + _EXEC_unknown_name = __EXEC_unknown_name; + _EXEC_profile = __EXEC_profile; + _EXEC_profile_instr = __EXEC_profile_instr; + _EXEC_quit_value = __EXEC_quit_value; + _EVENT_Last = __EVENT_Last; + _ERROR_current = __ERROR_current; + _ERROR_handler = __ERROR_handler; + _STRING_char_string = __STRING_char_string; +} + +} diff --git a/gb.jit.llvm/src/jit_codegen.cpp b/gb.jit.llvm/src/jit_codegen.cpp new file mode 100644 index 00000000..96c11f98 --- /dev/null +++ b/gb.jit.llvm/src/jit_codegen.cpp @@ -0,0 +1,7001 @@ +/*************************************************************************** + + jit_codegen.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_CODEGEN_C + +#include +#include +#include +#include +#include + +#include "jit.h" + +#include "jit_runtime.h" +#include "jit_gambas_pass.h" + +#define class klass +extern "C" { +#include "gbx_struct.h" +/*#include "gbx_api.h" +#include "gbx_regexp.h" +#include "gbx_math.h" + +#define throw _throw +#include "gbx_compare.h" +#undef throw*/ +} +#undef class + +#ifdef OS_64BITS +#define TARGET_BITS 64 +#else +#define TARGET_BITS 32 +#endif + +#define GOSUB_ON_STACK + +#define llvmType(t) llvm::Type::t(llvm_context) +#define pointer_t(type) llvm::PointerType::get((type), 0) +#define charPP llvm::PointerType::get(llvmType(getInt8PtrTy), 0) +#define get_nullptr() llvm::ConstantPointerNull::get(llvmType(getInt8PtrTy)) +#define get_voidstring() get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + +#define LONG_TYPE (TARGET_BITS == 64 ? llvmType(getInt64Ty) : llvmType(getInt32Ty)) + +#ifdef __CYGWIN__ +#define __finite finite +#define __isnan __isnand +#define __isinf __isinfd +#endif + +const size_t TYPE_sizeof_memory_tab[16] = { 0, 1, 1, 2, 4, 8, 4, 8, 8, sizeof(void *), sizeof(void *), sizeof(void *), sizeof(VARIANT), 0, 0, 0 }; + +///DEBUG +void print_type(llvm::Value* v); + +static std::set mappings; +//static std::map variable_mappings; + +static llvm::LLVMContext llvm_context; + +static llvm::StructType* string_type; +static llvm::StructType* date_type; +static llvm::StructType* function_type; +static llvm::StructType* variant_type; +static llvm::StructType* object_type; +static llvm::StructType* OBJECT_type; +static llvm::StructType* value_type; +static llvm::StructType* value_types[17]; +static llvm::StructType* two_longs_type; +static llvm::StructType* gosub_stack_node_type; + +static llvm::Module* M; +static llvm::ExecutionEngine* EE = NULL; + +static llvm::Function* llvm_function; +static llvm::BasicBlock* entry_block; +static llvm::IRBuilder<>* builder; +static llvm::Value* temp_value; +static llvm::Value* temp_value2; +static llvm::Value* temp_voidptr; +static llvm::Value* temp_int; +static llvm::Value* temp_double; +static llvm::Value* temp_date; +static llvm::Value* temp_2longs; +static llvm::Value* temp_errcontext1; +static llvm::Value* temp_errcontext2; +static llvm::Value* temp_got_error; +static llvm::Value* temp_got_error2; +//static llvm::Value* temp_gosub_stack; +//static llvm::Value* temp_num_gosubs_on_stack; +static llvm::Value* gp; +static llvm::Value* gosub_return_point; + +static std::vector return_points; +static std::vector addr_blocks; //addr_blocks[i] contains the basic block at code pos i, if the address is taken +struct PendingBranch { + llvm::BasicBlock* insert_point; + llvm::Value* condition; //i1, NULL if unconditional branch + int true_addr; + int false_addr; //Not used if unconditional branch +}; +static std::vector pending_branches; +struct JumpTablePendingBranch { + llvm::BasicBlock* insert_point; + llvm::Value* condition; //int32 + std::vector* destinations; + int default_addr; +}; +static std::vector jump_table_pending_branches; +static std::vector gosub_continue_points; +static std::vector gosub_return_points; + +static std::vector current_ctrl_types; +static std::vector > ctrl_values; +static std::vector locals; +static std::vector params; +static llvm::Value* current_op; + +static bool in_try = false; +static bool has_tries = false; +static std::vector try_blocks; + +static unsigned short* statement_pc; + +static llvm::ConstantInt* getInteger(int bits, uint64_t value){ + return llvm::ConstantInt::get(llvm_context, llvm::APInt(bits, value, true)); +} + +template +static llvm::ConstantFP* getFloat(_FloatType_ value){ + return llvm::ConstantFP::get(llvm_context, llvm::APFloat(value)); +} + +static llvm::Type* TYPE_llvm(TYPE type){ + if (TYPE_is_object(type)) + return object_type; + + static llvm::Type* const arr[] = { + llvmType(getVoidTy), + llvmType(getInt1Ty), + llvmType(getInt8Ty), + llvmType(getInt16Ty), + llvmType(getInt32Ty), + llvmType(getInt64Ty), + llvmType(getFloatTy), + llvmType(getDoubleTy), + date_type, + string_type, + string_type, + llvmType(getInt8PtrTy), + variant_type, + NULL, + llvmType(getInt8PtrTy), + NULL + }; + return arr[type]; +} + +static llvm::Type* type_from_char(char c){ + switch (c){ + case 'v': return llvmType(getVoidTy); + case 'b': return llvmType(getInt1Ty); + case 'c': return llvmType(getInt8Ty); + case 'h': return llvmType(getInt16Ty); + case 'i': return llvmType(getInt32Ty); + case 'l': return llvmType(getInt64Ty); + case 'f': return llvmType(getFloatTy); + case 'd': return llvmType(getDoubleTy); + case 'p': return llvmType(getInt8PtrTy); + + case 'j': return LONG_TYPE; + } + return 0; //Error +} + +static std::vector string_to_type_vector(const char* args){ + std::vector llvm_args; + for(int i=0, e=strlen(args); i!=e; i++) + llvm_args.push_back(type_from_char(args[i])); + return llvm_args; +} + +static llvm::FunctionType* get_function_type(char ret, const char* args, bool vararg = false){ + std::vector llvm_args = string_to_type_vector(args); + return llvm::FunctionType::get(type_from_char(ret), llvm_args, vararg); +} + +static llvm::Type* get_value_type(TYPE type){ + if (TYPE_is_object(type)) return value_types[T_OBJECT]; + return value_types[type]; +} + +static llvm::BasicBlock* create_bb(const char* name){ + return llvm::BasicBlock::Create(llvm_context, name, llvm_function); +} + +extern "C" double __powidf2(double, int); + +static void llvm_init(){ + llvm::InitializeNativeTarget(); + + //string_type = llvm::StructType::create(llvm_context, string_to_type_vector("jpii"), "String"); + date_type = llvm::StructType::create(llvm_context, string_to_type_vector("ii"), "Date"); + function_type = llvm::StructType::create(llvm_context, string_to_type_vector("ppcch"), "Function"); + variant_type = llvm::StructType::create(llvm_context, string_to_type_vector("jl"), "Variant", true); + object_type = llvm::StructType::create(llvm_context, string_to_type_vector("pp"), "Object"); + OBJECT_type = llvm::StructType::create(llvm_context, string_to_type_vector("pj"), "OBJECT"); + value_type = llvm::StructType::create(llvm_context, string_to_type_vector("jjjj"), "Value"); + two_longs_type = llvm::StructType::create(llvm_context, string_to_type_vector("jj"), "TwoLongs"); + +#if TARGET_BITS == 64 + static const char* const arr[17] = {"jjjj", "ji", "ji", "ji", "ji", "jl", "jf", "jd", "jii", "jpii", NULL, "jp", "jjl", "jppcch", "jp", "j", "ppp"}; +#else + static const char* const arr[17] = {"jjjj", "ji", "ji", "ji", "ji", "jil", "jf", "jid", "jii", "jpii", NULL, "jp", "jjl", "jppcch", "jp", "j", "ppp"}; +#endif + static const char* const names[17] = {"Void", "Boolean", "Byte", "Short", "Integer", "Long", "Single", "Float", "Date", "String", NULL, "Pointer", "ValueVariant", "ValueFunction", "Class", "Null", "ValueObject"}; + for(int i=0; i<=16; i++) + if (arr[i]) + value_types[i] = llvm::StructType::create(llvm_context, string_to_type_vector(arr[i]), names[i]); + value_types[T_CSTRING] = value_types[T_STRING]; + + string_type = value_types[T_STRING]; + + { + std::vector llvm_args; + //llvm_args.push_back(llvmType(getInt8PtrTy)); //Stores the blockaddress. Is a ushort PC in gbx_stack.h but this should work due to alignment ;) + llvm_args.push_back(llvmType(getInt16Ty)); //Stores an id of the return point, 0 == none + llvm_args.push_back(pointer_t(value_type)); + gosub_stack_node_type = llvm::StructType::create(llvm_context, llvm_args, "GosubStackNode"); + } + + llvm::sys::DynamicLibrary::AddSymbol("__powidf2", (void*)__powidf2); +} + +void register_global_symbol(llvm::StringRef name, llvm::GlobalValue* value, void* address){ + if (mappings.insert(name).second) + EE->addGlobalMapping(value, address); +} + +static llvm::Value* get_global(void* var, llvm::Type* llvm_type = llvmType(getInt8PtrTy)){ + /*char buf[32]; + sprintf(buf, "v_%p", var); + std::string name = buf; + if (var == &SP) + name = "SP"; + else if (var == &BP) + name = "BP"; + else if (var == &CP) + name = "CP"; + auto it = variable_mappings.insert(std::make_pair(var, (llvm::GlobalVariable*)0)); + if (it.second){ + llvm::GlobalVariable* v = new llvm::GlobalVariable(*M, llvm_type, false, llvm::GlobalValue::ExternalLinkage, 0, name); + it.first->second = v; + EE->addGlobalMapping(v, var); + return v; + } else { + return it.first->second; + }*/ + return builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(var)), pointer_t(llvm_type)); +} + +#define get_global_function(n, ret, args) get_global_function_real(#n, (void*)n, ret, args) +#define get_global_function_jif(n, ret, args) get_global_function_real(#n, (void*)JIF.F_##n, ret, args) +#define get_global_function_vararg(n, ret, args) get_global_function_real(#n, (void*)n, ret, args, true) +#define get_global_function_jif_vararg(n, ret, args) get_global_function_real(#n, (void*)JIF.F_##n, ret, args, true) +static llvm::Value* get_global_function_real(const char* name, void* func, char ret, const char* args, bool vararg = false){ + llvm::FunctionType* ft = get_function_type(ret, args, vararg); + //llvm::sys::DynamicLibrary::AddSymbol + //return get_global(func, ft); + llvm::GlobalValue* val = (llvm::GlobalValue*)M->getOrInsertFunction(name, ft); + register_global_symbol(name, val, func); + return val; +} + +static llvm::Value* read_global(void* var, llvm::Type* pt = llvmType(getInt8PtrTy)){ + //llvm::Value* addr = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(var)), pointer_t(pt)); + llvm::Value* addr = get_global(var, pt); + llvm::Value* v = builder->CreateLoad(addr); + return v; +} + +static llvm::Value* create_gep(llvm::Value* addr, int bits1, int val1, int bits2, int val2){ + llvm::Value* arr[] = {getInteger(bits1, val1), getInteger(bits2, val2)}; + return builder->CreateGEP(addr, arr); +} + +static llvm::Value* get_element_addr(llvm::Value* addr, int index){ + return create_gep(addr, TARGET_BITS, 0, 32, index); +} + +static llvm::Value* load_element(llvm::Value* addr, int index){ + return builder->CreateLoad(get_element_addr(addr, index)); +} + +static void store_element(llvm::Value* addr, int index, llvm::Value* val){ + builder->CreateStore(val, get_element_addr(addr, index)); +} + +static llvm::Value* get_new_struct(llvm::StructType* st){ + return llvm::UndefValue::get(st); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1){ + static const unsigned int arr[1] = {0}; + return builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2){ + static const unsigned int arr[2][1] = {{0}, {1}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + return builder->CreateInsertValue(val, v2, arr[1]); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3){ + static const unsigned int arr[3][1] = {{0}, {1}, {2}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + val = builder->CreateInsertValue(val, v2, arr[1]); + return builder->CreateInsertValue(val, v3, arr[2]); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3, llvm::Value* v4){ + static const unsigned int arr[5][1] = {{0}, {1}, {2}, {3}, {4}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + val = builder->CreateInsertValue(val, v2, arr[1]); + val = builder->CreateInsertValue(val, v3, arr[2]); + return builder->CreateInsertValue(val, v4, arr[3]); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3, llvm::Value* v4, llvm::Value* v5){ + static const unsigned int arr[5][1] = {{0}, {1}, {2}, {3}, {4}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + val = builder->CreateInsertValue(val, v2, arr[1]); + val = builder->CreateInsertValue(val, v3, arr[2]); + val = builder->CreateInsertValue(val, v4, arr[3]); + return builder->CreateInsertValue(val, v5, arr[4]); +} + +static llvm::Value* extract_value(llvm::Value* val, int index){ + unsigned int arr[] = {index}; + return builder->CreateExtractValue(val, arr); +} + +static llvm::Value* insert_value(llvm::Value* container, llvm::Value* value, int index){ + unsigned int arr[] = {index}; + return builder->CreateInsertValue(container, value, arr); +} + +static llvm::Value* read_value(llvm::Value* addr, TYPE type){ + addr = builder->CreateBitCast(addr, pointer_t(get_value_type(type))); + switch(type){ + case T_VOID: return NULL; + case T_BOOLEAN: + return builder->CreateICmpNE(load_element(addr, 1), getInteger(32, 0)); + case T_BYTE: + case T_SHORT: + return builder->CreateTrunc(load_element(addr, 1), TYPE_llvm(type)); + case T_LONG: + case T_FLOAT: +#if TARGET_BITS == 32 + return load_element(addr, 2); +#endif + case T_INTEGER: + case T_SINGLE: + case T_POINTER: + case T_CLASS: + return load_element(addr, 1); + case T_DATE: + return get_new_struct(date_type, load_element(addr, 1), load_element(addr, 2)); + case T_STRING: + case T_CSTRING: { + //return builder->CreateLoad(addr); + llvm::Value* t = load_element(addr, 0); + llvm::Value* res = get_new_struct(string_type, t, load_element(addr, 1), load_element(addr, 2), load_element(addr, 3)); + + /*llvm::BasicBlock *bb1 = create_bb("bb1"), *bb2 = create_bb("bb2"); + + builder->CreateCondBr(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, 12)), bb1, bb2); + builder->SetInsertPoint(bb1); + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + builder->SetInsertPoint(bb2);*/ + return res; + //FIXME remove above - now outcommented! + + /*gen_if_noreturn(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, 12)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + });*/ + } + //case T_CSTRING: + // return get_new_struct(string_type, getInteger(1, true), load_element(addr, 1), load_element(addr, 2), load_element(addr, 3)); + case T_VARIANT: { + llvm::Value* not_null = get_new_struct(variant_type, load_element(addr, 1), load_element(addr, 2)); + llvm::Value* if_null = get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)); + return builder->CreateSelect(builder->CreateICmpNE(load_element(addr, 0), getInteger(TARGET_BITS, T_NULL)), not_null, if_null); + } + case T_FUNCTION: + return get_new_struct(function_type, load_element(addr, 1), load_element(addr, 2), load_element(addr, 3), load_element(addr, 4), load_element(addr, 5)); + case T_NULL: + abort(); + default: { + //FIXME + //builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"laddar %p\n", llvmType(getInt8Ty)), load_element(addr, 0)); + llvm::Value* is_null = load_element(builder->CreateBitCast(addr, pointer_t(value_types[T_NULL])), 0); + is_null = builder->CreateICmpEQ(is_null, getInteger(TARGET_BITS, T_NULL)); + //llvm::Value* nullstruct = insert_value(get_new_struct(object_type), get_nullptr(), 1); + llvm::Value* nullstruct = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()); + return builder->CreateSelect(is_null, nullstruct, get_new_struct(object_type, load_element(addr, 0), load_element(addr, 1))); + } + } +} + +static void store_value(llvm::Value* addr, llvm::Value* val, TYPE type, bool store_type = true){ + addr = builder->CreateBitCast(addr, pointer_t(get_value_type(type))); + if (store_type && type != T_STRING) + store_element(addr, 0, TYPE_is_object(type) ? builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)type), llvmType(getInt8PtrTy)) : getInteger(TARGET_BITS, type)); + switch(type){ + case T_BOOLEAN: + case T_SHORT: + store_element(addr, 1, builder->CreateSExt(val, llvmType(getInt32Ty))); break; + case T_BYTE: + store_element(addr, 1, builder->CreateZExt(val, llvmType(getInt32Ty))); break; + case T_LONG: + case T_FLOAT: +#if TARGET_BITS == 32 + store_element(addr, 2, val); break; +#endif + case T_INTEGER: + case T_SINGLE: + case T_POINTER: + case T_CLASS: + store_element(addr, 1, val); break; + case T_DATE: + case T_VARIANT: + store_element(addr, 1, extract_value(val, 0)); + store_element(addr, 2, extract_value(val, 1)); + break; + case T_STRING: + case T_CSTRING: + //builder->CreateStore(val, addr); + store_element(addr, 0, extract_value(val, 0)); + store_element(addr, 1, extract_value(val, 1)); + store_element(addr, 2, extract_value(val, 2)); + store_element(addr, 3, extract_value(val, 3)); + /* store_element(addr, 0, builder->CreateSelect(extract_value(val, 0), getInteger(TARGET_BITS, T_CSTRING), getInteger(TARGET_BITS, T_STRING))); + store_element(addr, 1, extract_value(val, 1)); + store_element(addr, 2, extract_value(val, 2)); + store_element(addr, 3, extract_value(val, 3)); + break; + case T_CSTRING: + store_element(addr, 1, extract_value(val, 1)); + store_element(addr, 2, extract_value(val, 2)); + store_element(addr, 3, extract_value(val, 3));*/ + break; + case T_FUNCTION: + store_element(addr, 1, extract_value(val, 0)); + store_element(addr, 2, extract_value(val, 1)); + store_element(addr, 3, extract_value(val, 2)); + store_element(addr, 4, extract_value(val, 3)); + store_element(addr, 5, extract_value(val, 4)); + break; + case T_VOID: + case T_NULL: + break; + default: + store_element(addr, 0, extract_value(val, 0)); + store_element(addr, 1, extract_value(val, 1)); + break; + } +} + +/*static llvm::Value* array_read(llvm::Value* addr, TYPE type){ + switch(type){ + case T_VOID: + case T_CSTRING: + case T_NULL: + case T_CLASS: + case T_FUNCTION: + abort(); + case T_BOOLEAN: return builder->CreateTrunc(builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))), llvmType(getInt1Ty)); + case T_BYTE: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))); + case T_SHORT: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt16PtrTy))); + case T_INTEGER: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt32PtrTy))); + case T_LONG: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt64PtrTy))); + case T_SINGLE: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getFloatPtrTy))); + case T_FLOAT: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getDoublePtrTy))); + case T_DATE: return builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(date_type))); + case T_STRING: { + addr = builder->CreateBitCast(addr, charPP); + llvm::Value* len_addr = builder->CreateGEP(addr, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), addr, getInteger(32, 0), builder->CreateLoad(len_addr)); + } + case T_POINTER: return builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + case T_VARIANT: { + //FIXME can we skip the T_VOID -> T_NULL test? + return builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type))); + } + default: { + return get_new_struct(object_type, getInteger(TARGET_BITS, type), builder->CreateLoad(builder->CreateBitCast(addr, charPP))); + } + } +}*/ + +static void store_default(llvm::Value* addr, TYPE type){ + switch(type){ + case T_VOID: return; + case T_BOOLEAN: builder->CreateStore(getInteger(1, false), addr); return; + case T_BYTE: builder->CreateStore(getInteger(8, 0), addr); return; + case T_SHORT: builder->CreateStore(getInteger(16, 0), addr); return; + case T_INTEGER: builder->CreateStore(getInteger(32, 0), addr); return; + case T_LONG: builder->CreateStore(getInteger(64, 0), addr); return; + case T_SINGLE: builder->CreateStore(getFloat(0.0f), addr); return; + case T_FLOAT: builder->CreateStore(getFloat(0.0), addr); return; + case T_DATE: builder->CreateStore(get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0)), addr); return; + case T_STRING: + case T_CSTRING: builder->CreateStore(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), addr); return; + case T_CLASS: //FIXME? + case T_POINTER: builder->CreateStore(get_nullptr(), addr); return; + case T_VARIANT: builder->CreateStore(get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, undef int64*/), addr); return; + case T_FUNCTION: + case T_NULL: abort(); + default: + builder->CreateStore(get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()), addr); return; + } +} + +static llvm::Value* get_default(TYPE type){ + switch(type){ + case T_VOID: return NULL; + case T_BOOLEAN: return getInteger(1, false); + case T_BYTE: return getInteger(8, 0); + case T_SHORT: return getInteger(16, 0); + case T_INTEGER: return getInteger(32, 0); + case T_LONG: return getInteger(64, 0); + case T_SINGLE: return getFloat(0.0f); + case T_FLOAT: return getFloat(0.0); + case T_DATE: return get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0)); + case T_STRING: + case T_CSTRING: return get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + case T_CLASS: return NULL; + case T_POINTER: return get_nullptr(); + case T_VARIANT: return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)); + case T_FUNCTION: + case T_NULL: abort(); + default: + return get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()); + } +} + +template +static void gen_if(llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){ + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + func(); + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->CreateBr(cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, cont_block); + + builder->SetInsertPoint(cont_block); +} + +template +static llvm::PHINode* gen_if_phi(llvm::Value* else_value, llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){ + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + llvm::Value* ret = func(); + llvm::BasicBlock* then_block2 = builder->GetInsertBlock(); + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->CreateBr(cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, cont_block); + + builder->SetInsertPoint(cont_block); + + llvm::PHINode* phi = builder->CreatePHI(ret->getType(), 2); + phi->addIncoming(ret, then_block2); + phi->addIncoming(else_value, from_block); + return phi; +} + +template +static void gen_if_noreturn(llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){ + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + func(); + + builder->SetInsertPoint(from_block); + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->CreateCondBr(cond, then_block, cont_block); + + builder->SetInsertPoint(cont_block); +} + +template +static void gen_if_else(llvm::Value* cond, T1 then_func, T2 else_func, const char* if_name="if.then", const char* else_name="if.else", const char* cont_name="if.cont"){ + + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + then_func(); + llvm::BasicBlock* then_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* else_block = create_bb(else_name); + builder->SetInsertPoint(else_block); + else_func(); + llvm::BasicBlock* else_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, else_block); + builder->SetInsertPoint(then_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(else_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(cont_block); +} + +template +static llvm::PHINode* gen_if_else_phi(llvm::Value* cond, T1 then_func, T2 else_func, const char* if_name="if.then", const char* else_name="if.else", const char* cont_name="if.cont"){ + + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + llvm::Value* ret1 = then_func(); + llvm::BasicBlock* then_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* else_block = create_bb(else_name); + builder->SetInsertPoint(else_block); + llvm::Value* ret2 = else_func(); + llvm::BasicBlock* else_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, else_block); + builder->SetInsertPoint(then_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(else_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(cont_block); + + llvm::PHINode* phi = builder->CreatePHI(ret1->getType(), 2); + phi->addIncoming(ret1, then_block2); + phi->addIncoming(ret2, else_block2); + return phi; +} + +template +static void gen_while(llvm::Value* initial_condition, T1 body_func){ + llvm::BasicBlock* body_block = create_bb("while_block"); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(body_block); + llvm::Value* cont = body_func(); + llvm::BasicBlock* cont_block = create_bb("while_cont"); + builder->CreateCondBr(cont, body_block, cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(initial_condition, body_block, cont_block); + + builder->SetInsertPoint(cont_block); +} + +template +static llvm::Value* gen_and_if(llvm::Value* left, T1 right_func){ + llvm::BasicBlock* right_block = create_bb("and_if"); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(right_block); + llvm::Value* right = right_func(); + llvm::BasicBlock* cont_block = create_bb("and_if_cont"); + builder->CreateBr(cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(left, right_block, cont_block); + + builder->SetInsertPoint(cont_block); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, false), from_block); + phi->addIncoming(right, right_block); + return phi; +} + +static llvm::Value* get_cstring_from_addr(llvm::Value* addr){ + llvm::Value* str; + llvm::BasicBlock* B1 = builder->GetInsertBlock(); + llvm::BasicBlock* B2; + + gen_if(builder->CreateICmpNE(addr, get_nullptr()), [&](){ + llvm::Value* len = builder->CreateCall(get_global_function(strlen, 'j', "p"), addr); + if (TARGET_BITS == 64) + len = builder->CreateTrunc(len, llvmType(getInt32Ty)); + str = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), addr, getInteger(32, 0), len); + B2 = builder->GetInsertBlock(); + }, "cstring_strlen", "cstring_null_or_done_strlen"); + + + llvm::PHINode* phi = builder->CreatePHI(string_type, 2); + phi->addIncoming(get_default(T_CSTRING), B1); + phi->addIncoming(str, B2); + return phi; +} + +static llvm::Value* to_target_int(llvm::Value* integer32){ + if (TARGET_BITS == 64) + return builder->CreateZExt(integer32, llvmType(getInt64Ty)); + return integer32; +} + +static void init_locals(){ + int n_local = FP->n_local; + int n_ctrl = FP->n_ctrl; + int n_param = FP->n_param; + + locals.resize(n_local + n_ctrl); + int i; + for(i=0; iCreateAlloca(TYPE_llvm(ctype_to_type(&FP->local[i].type))); + builder->CreateStore(get_default(ctype_to_type(&FP->local[i].type)), locals[i]); + //builder->CreateStore(read_value(builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))), ctype_to_type(&FP->local[i].type)), locals[i]); + } + /*for(i; i < n_local + n_ctrl; i++){ + TYPE type = get_ctrl_type(i); + if (type != T_VOID){ + locals[i] = builder->CreateAlloca(TYPE_llvm(type)); + store_default(locals[i], type); + } + }*/ + ctrl_values.resize(n_ctrl); + current_ctrl_types.resize(n_ctrl); + for(; i < n_local + n_ctrl; i++){ + set_ctrl_type(T_VOID, i); + locals[i] = NULL; + current_ctrl_types[i - n_local] = builder->CreateAlloca(llvmType(getInt32Ty)); + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - n_local]); + ctrl_values[i - n_local][1] = builder->CreateAlloca(string_type); + ctrl_values[i - n_local][2] = builder->CreateAlloca(object_type); + ctrl_values[i - n_local][3] = builder->CreateAlloca(variant_type); + } + + params.resize(n_param); + for(i=0; iCreateAlloca(TYPE_llvm(FP->param[i].type)); + builder->CreateStore(read_value(builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, (i-n_param)*sizeof(VALUE))), FP->param[i].type), params[i]); + } +} + +static llvm::Value* release_ctrl(int i); + +static void finish_gosub_returns(){ + llvm::BasicBlock* bb = builder->GetInsertBlock(); + for(size_t i=0, e=gosub_return_points.size(); i!=e; i++){ + builder->SetInsertPoint(gosub_return_points[i]); + /*llvm::Value* index = builder->CreateSub(builder->CreateLoad(temp_num_gosubs_on_stack), getInteger(32, 1)); + builder->CreateStore(index, temp_num_gosubs_on_stack); + + llvm::Value* indices[2] = {getInteger(TARGET_BITS, 0), to_target_int(index)}; + llvm::Value* addr = builder->CreateGEP(temp_gosub_stack, indices);*/ + + //Cleanup current ctrl values + for(int i=0; in_ctrl; i++){ + llvm::Value* old_type = release_ctrl(i + FP->n_local); + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i]); + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, (i + FP->n_local)*sizeof(VALUE))); + store_value(stack_addr, NULL, T_VOID); + }, "old_ctrl_needs_to_be_cleaned1"); + } + //And return + /*llvm::Value* addr = builder->CreateLoad(gosub_return_point); + llvm::IndirectBrInst* indbrinst = builder->CreateIndirectBr(addr, gosub_continue_points.size()); + + for(size_t j=0, e=gosub_continue_points.size(); j!=e; j++){ + indbrinst->addDestination(gosub_continue_points[j]); + }*/ + llvm::Value* return_id = builder->CreateLoad(gosub_return_point); + llvm::SwitchInst* switchinst = builder->CreateSwitch(return_id, gosub_continue_points[0], gosub_continue_points.size()-1); + + for(size_t j=1, e=gosub_continue_points.size(); j!=e; j++){ + switchinst->addCase(getInteger(16, j+1), gosub_continue_points[j]); + } + } + gosub_return_points.clear(); + builder->SetInsertPoint(bb); +} + +static void create_return(){ + llvm::BasicBlock* BB = create_bb("return"); + builder->CreateBr(BB); + + for(size_t i=0, e=return_points.size(); i!=e; i++){ + builder->SetInsertPoint(return_points[i]); + builder->CreateBr(BB); + } + return_points.clear(); + + builder->SetInsertPoint(BB); + + if (func_byref_mask){ + int nparam = FP->n_param; + for(int i=0; iparam[i].type; + if (type != T_STRING && !TYPE_is_object(type)){ + llvm::Value* v = builder->CreateLoad(params[i]); + llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, (i-nparam)*sizeof(VALUE))); + store_value(stack_addr, v, type); + } + } + } + + //static int counter = 0; + + //builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"leave %d\n", llvmType(getInt8Ty)), getInteger(32, counter++)); + + builder->CreateCall(get_global_function_jif(EXEC_leave_keep, 'v', "")); + //builder->CreateCall(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"leave done\n", llvmType(getInt8Ty))); + builder->CreateRetVoid(); +} +static llvm::Value* read_sp(); +static void codegen_statements(){ + addr_blocks.resize(all_statements.back()->addr + 1); + + for(size_t i=0, e=all_statements.size(); i!=e; i++){ + if (all_statements[i]->address_taken){ + llvm::BasicBlock* block = create_bb("block"); + builder->CreateBr(block); + builder->SetInsertPoint(block); + addr_blocks[all_statements[i]->addr] = block; + } + statement_pc = FP->code + all_statements[i]->addr; + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"before: %p\n", llvmType(getInt8Ty)), read_sp());*/ + all_statements[i]->expr->codegen(); + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"after: %p\n", llvmType(getInt8Ty)), read_sp());*/ + } +} + +static void insert_pending_branches(){ + for(size_t i=0, e=pending_branches.size(); i!=e; i++){ + PendingBranch& p = pending_branches[i]; + builder->SetInsertPoint(p.insert_point); + if (p.condition){ + builder->CreateCondBr(p.condition, addr_blocks[p.true_addr], addr_blocks[p.false_addr]); + } else { + builder->CreateBr(addr_blocks[p.true_addr]); + } + } + pending_branches.clear(); + for(size_t i=0, e=jump_table_pending_branches.size(); i!=e; i++){ + JumpTablePendingBranch& p = jump_table_pending_branches[i]; + builder->SetInsertPoint(p.insert_point); + llvm::SwitchInst* sw = builder->CreateSwitch(p.condition, addr_blocks[p.default_addr], p.destinations->size()); + for(size_t j=0, ee=p.destinations->size(); j!=ee; j++) + sw->addCase(getInteger(32, j), addr_blocks[(*p.destinations)[j]]); + } + jump_table_pending_branches.clear(); +} + +static void store_pc(unsigned short* pc){ + builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)pc), get_global((void*)&PC, LONG_TYPE)); +} + +static void create_throw(int code){ + if (FP->debug) + store_pc(statement_pc); + builder->CreateCall(get_global_function_jif_vararg(THROW, 'v', "i"), getInteger(32, code)); + builder->CreateUnreachable(); +} +static void create_throw(int code, const char* a, const char* b){ + if (FP->debug) + store_pc(statement_pc); + builder->CreateCall3(get_global_function_jif_vararg(THROW, 'v', "i"), getInteger(32, code), get_global((void*)a, llvmType(getInt8Ty)), get_global((void*)b, llvmType(getInt8Ty))); + builder->CreateUnreachable(); +} +static void create_throw(llvm::Value* code){ + if (FP->debug) + store_pc(statement_pc); + builder->CreateCall(get_global_function_jif_vararg(THROW, 'v', "i"), code); + builder->CreateUnreachable(); +} + +static llvm::Value* get_value_on_top_addr(){ + //llvm::Value* sp = builder->CreateBitCast(read_global(&SP), pointer_t(value_type)); + llvm::Value* sp = read_global(&SP, pointer_t(value_type)); + return builder->CreateGEP(sp, getInteger(TARGET_BITS, -1)); +} + +static llvm::Value* get_top(TYPE type){ + return read_value(get_value_on_top_addr(), type); +} + +static llvm::Value* read_sp(){ + return read_global(&SP, pointer_t(value_type)); +} + +static void store_sp(llvm::Value* sp){ + llvm::Value* sp_addr = get_global(&SP, pointer_t(value_type)); + builder->CreateStore(sp, sp_addr); +} + +static void c_SP(int diff){ + if (diff == 0) return; + llvm::Value* sp_addr = get_global(&SP, pointer_t(value_type)); + llvm::Value* sp = builder->CreateLoad(sp_addr); + sp = builder->CreateGEP(sp, getInteger(TARGET_BITS, diff)); + builder->CreateStore(sp, sp_addr); +} + +static void push_value(llvm::Value* val, TYPE type){ + //c_SP(1); + //store_value(get_value_on_top_addr(), val, type); + llvm::Value* top = get_global(&SP, pointer_t(value_type)); + llvm::Value* addr = builder->CreateLoad(top); + store_value(addr, val, type); + builder->CreateStore(builder->CreateGEP(addr, getInteger(TARGET_BITS, 1)), top); +} + +static void set_top_value(llvm::Value* val, TYPE type, bool store_type = true){ + store_value(get_value_on_top_addr(), val, type, store_type); +} + +static llvm::Value* ret_top_stack(TYPE type, bool save){ + llvm::Value* val = read_value(get_value_on_top_addr(), type); + if (!save) + c_SP(-1); + return val; +} + +static void unref_string_no_nullcheck(llvm::Value* ptr){ + llvm::Value* str_ptr = builder->CreateBitCast(ptr, pointer_t(llvmType(getInt32Ty))); + llvm::Value* ref_addr = builder->CreateGEP(str_ptr, getInteger(TARGET_BITS, -2)); + llvm::Value* ref = builder->CreateLoad(ref_addr); + /*gen_if_noreturn(builder->CreateICmpSLE(ref, getInteger(32, 0)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + }); + gen_if_noreturn(builder->CreateICmpUGE(ref, getInteger(32, 10000)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + });*/ + ref = builder->CreateSub(ref, getInteger(32, 1)); + builder->CreateStore(ref, ref_addr); + llvm::Value* slt = builder->CreateICmpSLT(ref, getInteger(32, 1)); + if (llvm::Instruction* inst = llvm::dyn_cast(slt)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("unref_slt", llvm::MDNode::get(llvm_context, arr)); + } + gen_if(slt, [&](){ + llvm::Value* free_func = get_global_function_jif(STRING_free_real, 'v', "p"); + builder->CreateCall(free_func, ptr); + }, "release_str", "release_done"); +} + +static void unref_string(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + unref_string_no_nullcheck(ptr); + }, "str_not_null", "unref_done"); +} + +static void unref_object_no_nullcheck(llvm::Value* ptr){ + /*static int counter = 0; + builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"unref #: %d, %p\n", llvmType(getInt8Ty)), getInteger(32, counter++), ptr);*/ + + llvm::Value* object_ptr = builder->CreateBitCast(ptr, pointer_t(OBJECT_type)); + llvm::Value* ref_addr = get_element_addr(object_ptr, 1); + llvm::Value* ref = builder->CreateLoad(ref_addr); + ref = builder->CreateSub(ref, getInteger(TARGET_BITS, 1)); + builder->CreateStore(ref, ref_addr); + /*builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%p %ld -\n", llvmType(getInt8Ty)), ptr, ref);*/ + llvm::Value* slt = builder->CreateICmpSLT(ref, getInteger(TARGET_BITS, 1)); + if (llvm::Instruction* inst = llvm::dyn_cast(slt)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("unref_slt", llvm::MDNode::get(llvm_context, arr)); + } + gen_if(slt, [&](){ + llvm::Value* free_func = get_global_function_jif(CLASS_free, 'v', "p"); + builder->CreateCall(free_func, ptr); + }, "release_obj", "release_done"); +} + +static void unref_object(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + unref_object_no_nullcheck(ptr); + }, "obj_not_null", "unref_done"); +} + +static void codegen_printf(const char* str1, int tal){ + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)str1, llvmType(getInt8Ty)), getInteger(32, tal));*/ +} +static void codegen_printf(const char* str1){ + /*builder->CreateCall(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)str1, llvmType(getInt8Ty)));*/ +} +static void codegen_printf(const char* str1, llvm::Value* intval){ + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)str1, llvmType(getInt8Ty)), intval);*/ +} + +static void borrow_string_no_nullcheck(llvm::Value* ptr){ + llvm::Value* str_ptr = builder->CreateBitCast(ptr, pointer_t(llvmType(getInt32Ty))); + llvm::Value* ref_addr = builder->CreateGEP(str_ptr, getInteger(TARGET_BITS, -2)); + llvm::Value* ref = builder->CreateLoad(ref_addr); + /*gen_if_noreturn(builder->CreateICmpSLE(ref, getInteger(32, 0)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + }); + gen_if_noreturn(builder->CreateICmpUGT(ref, getInteger(32, 10000)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + }); + + builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%p %d\n", llvmType(getInt8Ty)), ptr, ref);*/ + + ref = builder->CreateAdd(ref, getInteger(32, 1)); + builder->CreateStore(ref, ref_addr); +} + +static void borrow_string(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + borrow_string_no_nullcheck(ptr); + }, "str_not_null", "string_borrow_done"); +} + +static void borrow_object_no_nullcheck(llvm::Value* ptr){ + llvm::Value* object_ptr = builder->CreateBitCast(ptr, pointer_t(OBJECT_type)); + llvm::Value* ref_addr = get_element_addr(object_ptr, 1); + llvm::Value* ref = builder->CreateLoad(ref_addr); + ref = builder->CreateAdd(ref, getInteger(TARGET_BITS, 1)); + builder->CreateStore(ref, ref_addr); + /*builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%p %ld +\n", llvmType(getInt8Ty)), ptr, ref);*/ +} + +static void borrow_object(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + borrow_object_no_nullcheck(ptr); + }, "obj_not_null", "borrow_object_done"); +} + +static void borrow_variant(llvm::Value* val){ + builder->CreateCall2( + get_global_function(JR_borrow_variant, 'v', "jl"), + extract_value(val, 0), + extract_value(val, 1)); +} + +static void release(llvm::Value* val, TYPE type){ + if (TYPE_is_object(type)) + unref_object(extract_value(val, 1)); + else if (type == T_STRING){ + gen_if(builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)), [&](){ + unref_string(extract_value(val, 1)); + }, "release_T_STRING", "str_release_done"); + } else if (TYPE_is_variant(type)) + builder->CreateCall2( + get_global_function(JR_release_variant, 'v', "jl"), + extract_value(val, 0), + extract_value(val, 1)); +} + +static void borrow(llvm::Value* val, TYPE type){ + if (TYPE_is_object(type)) + borrow_object(extract_value(val, 1)); + else if (type == T_STRING){ + gen_if(builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)), [&](){ + borrow_string(extract_value(val, 1)); + }, "borrow_T_STRING", "str_borrow_done"); + } else if (TYPE_is_variant(type)) + borrow_variant(val); +} + +static void borrow_top(TYPE type){ + borrow(get_top(type), type); +} + +static void release_top(Expression* expr){ + if (TYPE_is_object(expr->type)){ + unref_object(extract_value(expr->codegen_get_value(), 1)); + } else if (expr->type == T_STRING){ + release(expr->codegen_get_value(), T_STRING); + } else if (TYPE_is_variant(expr->type)){ + expr->codegen(); + llvm::Value* free_variant_func = get_global_function_jif(EXEC_release, 'v', "jp"); + builder->CreateCall2(free_variant_func, getInteger(TARGET_BITS, T_VARIANT), get_value_on_top_addr()); + } else + expr->codegen(); + c_SP(-1); +} + +static void release_val(Expression* expr){ + if (TYPE_is_object(expr->type)){ + unref_object(extract_value(expr->codegen_get_value(), 1)); + } else if (TYPE_is_string(expr->type)){ + release(expr->codegen_get_value(), T_STRING); + } else if (TYPE_is_variant(expr->type)){ + expr->on_stack = true; + expr->codegen(); + llvm::Value* free_variant_func = get_global_function_jif(EXEC_release, 'v', "jp"); + builder->CreateCall2(free_variant_func, getInteger(TARGET_BITS, T_VARIANT), get_value_on_top_addr()); + c_SP(-1); + } else + expr->codegen(); +} + +///Convert a String to a pointer that can be put in an array or a Variant. +///If 'val' is a guaranteed to be T_CSTRING, set type to T_CSTRING, otherwise set it to T_STRING +static llvm::Value* string_for_array_or_variant(llvm::Value* val, TYPE type){ + llvm::Value* len = extract_value(val, 3); + return gen_if_phi(get_nullptr(), builder->CreateICmpNE(len, getInteger(32, 0)), [&](){ + llvm::Value* ptr = extract_value(val, 1); + llvm::Value* offset = extract_value(val, 2); + if (type == T_STRING){ + llvm::Value* is_t_string = builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)); + llvm::Value* test1 = gen_and_if(builder->CreateAnd( + is_t_string, + builder->CreateICmpEQ(offset, getInteger(32, 0))), [&](){ + llvm::Value* len_addr = builder->CreateGEP(ptr, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + return builder->CreateICmpEQ(builder->CreateLoad(len_addr), len); + }); + return (llvm::Value*)gen_if_phi(ptr, builder->CreateXor(test1, getInteger(1, 1)), [&](){ + llvm::Value* newstr = builder->CreateCall2(get_global_function_jif(STRING_new, 'p', "pi"), builder->CreateGEP(ptr, to_target_int(offset)), len); + newstr = builder->CreateCall(get_global_function_jif(STRING_free_later, 'p', "p"), newstr); + gen_if(is_t_string, [&](){ + unref_string_no_nullcheck(ptr); + }); + borrow_string_no_nullcheck(newstr); + return newstr; + }); + } else { + llvm::Value* newstr = builder->CreateCall2(get_global_function_jif(STRING_new, 'p', "pi"), builder->CreateGEP(ptr, to_target_int(offset)), len); + newstr = builder->CreateCall(get_global_function_jif(STRING_free_later, 'p', "p"), newstr); + borrow_string_no_nullcheck(newstr); + return newstr; + } + }); +} + +///Transfers the ownership from val to the variable in memory +///The old data in memory is not released +static void variable_write(llvm::Value* addr, llvm::Value* val, TYPE type){ + if (type != T_BOOLEAN && type != T_STRING && type != T_CSTRING && !TYPE_is_object(type)) + addr = builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type))); + /*if (type == T_BOOLEAN) + char* */ + switch(type){ + case T_VOID: + case T_NULL: + case T_CLASS: + case T_FUNCTION: + abort(); + case T_BOOLEAN: builder->CreateStore(builder->CreateSExt(val, llvmType(getInt8Ty)), addr); break; + case T_BYTE: + case T_SHORT: + case T_INTEGER: + case T_LONG: + case T_SINGLE: + case T_FLOAT: + case T_DATE: + case T_POINTER: + case T_VARIANT: + builder->CreateStore(val, addr); break; + case T_STRING: + case T_CSTRING: { + addr = builder->CreateBitCast(addr, charPP); + builder->CreateStore(string_for_array_or_variant(val, type), addr); + break; + } + default: { + builder->CreateStore(extract_value(val, 1), builder->CreateBitCast(addr, charPP)); + break; + } + } +} + +///Borrows the data +static llvm::Value* array_read(llvm::Value* addr, TYPE type){ + if (type != T_BOOLEAN && type != T_STRING && type != T_CSTRING && !TYPE_is_object(type)) + addr = builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type))); + switch(type){ + case T_VOID: + case T_CSTRING: + case T_NULL: + case T_CLASS: + case T_FUNCTION: + abort(); + case T_BOOLEAN: return builder->CreateTrunc(builder->CreateLoad(addr), llvmType(getInt1Ty)); + case T_BYTE: + case T_SHORT: + case T_INTEGER: + case T_LONG: + case T_SINGLE: + case T_FLOAT: + case T_DATE: + case T_POINTER: + return builder->CreateLoad(addr); + case T_VARIANT: { + llvm::Value* ret = builder->CreateLoad(addr); + borrow_variant(ret); + return ret; + } + case T_STRING: { + llvm::Value* ptr = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + return gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), + builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + + llvm::Value* len_addr = builder->CreateGEP(ptr, getInteger(TARGET_BITS, -4)); + llvm::Value* len = builder->CreateLoad(builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy))); + + borrow_string_no_nullcheck(ptr); + + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ptr, getInteger(32, 0), len); + }); + } + default: { + llvm::Value* ret = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + borrow_object(ret); + return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } + } +} + +void DropExpression::codegen(){ + /*if (expr->on_stack){ + release_top(expr); + } else { + release_val(expr); + }*/ + if (CallExpression* ce = dyn_cast(expr)){ + if (ce->variant_call){ + ce->codegen_on_stack(); + llvm::Value* ret = get_value_on_top_addr(); + gen_if(builder->CreateICmpNE(load_element(ret, 0), getInteger(TARGET_BITS, 0)), [&](){ + release(ret_top_stack(T_VARIANT, true), T_VARIANT); + }); + c_SP(-1); + return; + } + } + release(expr->codegen_get_value(), expr->type); + if (expr->on_stack) + c_SP(-1); +} + +llvm::Value* PushCStringExpression::codegen_get_value(){ + llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)addr), llvmType(getInt8PtrTy)), + getInteger(32, start), getInteger(32, len)); + if (on_stack) + push_value(ret, type); + return ret; +} + +void PushIntegerExpression::codegen(){ + if (on_stack) codegen_on_stack(); +} +llvm::Value* PushIntegerExpression::codegen_get_value(){ + llvm::Value* val = getInteger(bits, i); + if (on_stack) codegen_on_stack(); + return val; +} +void PushIntegerExpression::codegen_on_stack(){ + push_value(getInteger(bits, i), type); +} + +llvm::Value* PushFloatExpression::codegen_get_value(){ + llvm::Value* ret = type == T_SINGLE ? getFloat((float)val) : getFloat(val); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushNullPointerExpression::codegen_get_value(){ + llvm::Value* ret = get_nullptr(); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushVoidDateExpression::codegen_get_value(){ + llvm::Value* ret = get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0)); + if (on_stack) + push_value(ret, T_DATE); + return ret; +} + +llvm::Value* PushNullExpression::codegen_get_value(){ + if (on_stack) + push_value(NULL, T_NULL); + return NULL; +} + +llvm::Value* PushVoidExpression::codegen_get_value(){ + if (on_stack) + push_value(NULL, T_VOID); + return NULL; +} + +llvm::Value* PushLastExpression::codegen_get_value(){ + llvm::Value* obj = read_global((void*)&EVENT_Last); + borrow_object(obj); + + llvm::Value* ret = get_new_struct(object_type, + builder->CreateIntToPtr(getInteger(TARGET_BITS, 16), llvmType(getInt8PtrTy)), obj); + + if (on_stack) + push_value(ret, T_OBJECT); + return ret; +} + +static llvm::Value* read_variable(TYPE type, char* addr){ + llvm::Value* ret; + if (type == T_BOOLEAN) + ret = builder->CreateTrunc(read_global(addr, llvmType(getInt8Ty)), llvmType(getInt1Ty)); + else if (type < T_STRING || type == T_POINTER) + ret = read_global(addr, TYPE_llvm(type)); + else if (type == T_STRING){ + llvm::Value* ad = read_global(addr); + ret = gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), builder->CreateICmpNE(ad, get_nullptr()), [&](){ + borrow_string_no_nullcheck(ad); + + llvm::Value* len_addr = builder->CreateGEP(ad, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + llvm::Value* len = builder->CreateLoad(len_addr); + + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ad, getInteger(32, 0), len); + }); + } else if (type == T_CSTRING){ + llvm::Value* ad = read_global(addr); + ret = get_cstring_from_addr(ad); + } else if (TYPE_is_object(type)){ + llvm::Value* obj = read_global(addr); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj); + borrow_object(obj); + } else if (type == T_VARIANT){ //FIXME + ret = read_global(addr, variant_type); + ret = gen_if_else_phi(builder->CreateICmpEQ(extract_value(ret, 0), getInteger(TARGET_BITS, T_VOID)), [&](){ + return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, extract_value(ret, 1)*/); + }, [&](){ + borrow_variant(ret); + return ret; + }, "Variant_T_VOID", "Variant_not_T_VOID", "Variant_T_VOID_done"); + } else abort(); + return ret; +} + +static llvm::Value* codegen_tc_array(CLASS* klass, llvm::Value* ref, int array_load_index, llvm::Value* start_addr, TYPE type){ + llvm::Value* obj = builder->CreateCall4(get_global_function_jif(CARRAY_create_static, 'p', "pppp"), + get_global((void*)klass, llvmType(getInt8Ty)), + ref, + get_global(klass->load->array[array_load_index], llvmType(getInt8Ty)), + start_addr); + + borrow_object_no_nullcheck(obj); + + return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj); +} + +llvm::Value* ReadVariableExpression::codegen_get_value(){ + llvm::Value* ret; + if (ctype->id == TC_ARRAY){ + ret = codegen_tc_array(CP, get_global((void*)CP, llvmType(getInt8Ty)), ctype->value, get_global((void*)addr, llvmType(getInt8Ty)), type); + } else if (ctype->id == TC_STRUCT){ + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)klass, llvmType(getInt8Ty)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), + get_global((void*)addr, llvmType(getInt8Ty))); + borrow_object_no_nullcheck(ret); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } else { + ret = read_variable(type, addr); + } + if (on_stack) + push_value(ret, type); + return ret; +} + + +//FIXME this is almost same as array_read, only difference is in variant +static llvm::Value* read_variable(TYPE type, llvm::Value* addr){ + llvm::Value* ret; + if (type == T_BOOLEAN) + ret = builder->CreateTrunc(builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))), llvmType(getInt1Ty)); + else if (type < T_STRING || type == T_POINTER) + ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type)))); + else if (type == T_STRING){ + llvm::Value* ad = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + ret = gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), builder->CreateICmpNE(ad, get_nullptr()), [&](){ + borrow_string_no_nullcheck(ad); + + llvm::Value* len_addr = builder->CreateGEP(ad, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + llvm::Value* len = builder->CreateLoad(len_addr); + + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ad, getInteger(32, 0), len); + }); + } else if (type == T_CSTRING){ + llvm::Value* ad = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + ret = get_cstring_from_addr(ad); + } else if (TYPE_is_object(type)){ + llvm::Value* nobj = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), nobj); + borrow_object(nobj); + } else if (type == T_VARIANT){ + ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type))); + ret = gen_if_else_phi(builder->CreateICmpEQ(extract_value(ret, 0), getInteger(TARGET_BITS, T_VOID)), [&](){ + return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, extract_value(ret, 1)*/); + }, [&](){ + borrow_variant(ret); + return ret; + }, "Variant_T_VOID", "Variant_not_T_VOID", "Variant_T_VOID_done"); + } else abort(); + return ret; + +} + +///Reading an instance variable +static llvm::Value* read_variable_offset(TYPE type, llvm::Value* obj, llvm::Value* offset){ + return read_variable(type, builder->CreateGEP(obj, offset)); +} + +static void release_variable(TYPE type, llvm::Value* addr){ + if (type == T_STRING){ + unref_string(builder->CreateLoad(builder->CreateBitCast(addr, charPP))); + } else if (TYPE_is_object(type)){ + unref_object(builder->CreateLoad(builder->CreateBitCast(addr, charPP))); + } else if (type == T_VARIANT){ + release(builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type))), T_VARIANT); + /*builder->CreateCall2(get_global_function(JR_release_variant, 'v', "jl"), + builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(LONG_TYPE))), + builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(addr, llvmType(getInt64PtrTy)), getInteger(TARGET_BITS, 1))) + );*/ + } +} + +static void write_variable_offset(TYPE type, llvm::Value* obj, int offset, llvm::Value* val){ + variable_write(builder->CreateGEP(obj, getInteger(TARGET_BITS, offset)), val, type); +} + +static void make_nullcheck(llvm::Value* addr){ + gen_if_noreturn(builder->CreateICmpEQ(addr, get_nullptr()), [&](){ + create_throw(E_NULL); + }, "is_null", "not_null"); +} + +template +static void make_nullcheck(llvm::Value* addr, T func_if_error){ + gen_if_noreturn(builder->CreateICmpEQ(addr, get_nullptr()), [&](){ + func_if_error(); + create_throw(E_NULL); + }, "is_null", "not_null"); +} + +static void make_double_nullcheck(llvm::Value* value){ + //FIXME: I think it is better to maybe make sure that an object on the stack actually + //is of type object (nullpointer or not), instead of T_NULL. + + llvm::Value* valtype = builder->CreatePtrToInt(extract_value(value, 0), LONG_TYPE); + + gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + create_throw(E_NULL); + }); + + llvm::Value* object_ptr = extract_value(value, 1); + make_nullcheck(object_ptr); +} + +template +static void make_double_nullcheck(llvm::Value* value, T func_if_error){ + //FIXME: I think it is better to maybe make sure that an object on the stack actually + //is of type object (nullpointer or not), instead of T_NULL. + + llvm::Value* valtype = builder->CreatePtrToInt(extract_value(value, 0), LONG_TYPE); + + gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + func_if_error(); + create_throw(E_NULL); + }); + + llvm::Value* object_ptr = extract_value(value, 1); + make_nullcheck(object_ptr, func_if_error); +} + +static llvm::Value* get_class_desc_entry(llvm::Value* object_ptr, int index){ + llvm::Value* class_ptr = load_element(builder->CreateBitCast(object_ptr, pointer_t(OBJECT_type)), 0); + llvm::Value* vtable_ptr = builder->CreateGEP(class_ptr, getInteger(TARGET_BITS, offsetof(CLASS, table))); + llvm::Value* vtable = builder->CreateLoad(builder->CreateBitCast(vtable_ptr, charPP)); + + llvm::Value* class_desc_ptr = builder->CreateGEP(vtable, getInteger(TARGET_BITS, index*sizeof(CLASS_DESC_SYMBOL)+offsetof(CLASS_DESC_SYMBOL, desc))); + llvm::Value* class_desc = builder->CreateLoad(builder->CreateBitCast(class_desc_ptr, charPP)); + return class_desc; +} + +static void create_check(CLASS* klass, llvm::Value* effective_class, llvm::Value* object){ + if (klass->must_check){ + //Assume klass->must_check only needs to be checked here, and not in the generated code + int offset_check = offsetof(CLASS, check)/sizeof(void*); //From CLASS structure in gbx_class.h + llvm::Value* check_func = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, charPP), getInteger(TARGET_BITS, offset_check))); + check_func = builder->CreateBitCast(check_func, pointer_t(get_function_type('i', "p"))); + gen_if_noreturn(builder->CreateICmpNE(builder->CreateCall(check_func, object), getInteger(32, 0)), [&](){ + + //static int counter = 0; + + //builder->CreateCall3(get_global_function_vararg(printf, 'v', "ppi"), + // get_global((void*)"Illegal: %p %d\n", llvmType(getInt8Ty)), object, getInteger(32, counter++)); + + create_throw(E_IOBJECT); + }, "illegal_object", "legal_object"); + } +} + +//Same body as PopStaticExpression ... +void PopStaticVariableExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* var_addr = get_global((void*)addr, llvmType(getInt8Ty)); + release_variable(type, var_addr); + variable_write(var_addr, value, type); + c_SP(-val->on_stack); +} + +void PushUnknownExpression::codegen_on_stack(){ + obj->codegen_on_stack(); + store_pc(pc); + builder->CreateCall(get_global_function_jif(EXEC_push_unknown, 'v', "h"), getInteger(16, 0)); +} + +llvm::Value* PushUnknownExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_VARIANT, true); +} + +llvm::Value* PushPureObjectVariableExpression::codegen_get_value(){ + if (isa(obj)){ + int offset = desc()->variable.offset; + llvm::Value* ret = read_variable_offset(type, current_op, getInteger(TARGET_BITS, offset)); + if (on_stack) + push_value(ret, type); + return ret; + } + llvm::Value* object = obj->codegen_get_value(); + make_double_nullcheck(object); + + llvm::Value* object_ptr = extract_value(object, 1); + + //We have a non-native object, so dispatch is not needed for effective class for checking + create_check(klass(), extract_value(object, 0), object_ptr); + + llvm::Value* desc_variable = get_class_desc_entry(object_ptr, index); + llvm::Value* offset = to_target_int(builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_variable, getInteger(TARGET_BITS, offsetof(CLASS_DESC_VARIABLE, offset))), llvmType(getInt32PtrTy)))); + + llvm::Value* ret = read_variable_offset(type, object_ptr, offset); + + unref_object_no_nullcheck(object_ptr); + + c_SP(-obj->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; +} + +void PopPureObjectVariableExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* object = obj->codegen_get_value(); + make_double_nullcheck(object, [&](){ + release(value, val->type); + }); + + llvm::Value* object_ptr = extract_value(object, 1); + + //We have a non-native object, so dispatch is not needed for effective class for checking + create_check((CLASS*)(void*)obj->type, extract_value(object, 0), object_ptr); + + llvm::Value* desc_variable = get_class_desc_entry(object_ptr, index); + llvm::Value* offset = to_target_int(builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_variable, getInteger(TARGET_BITS, offsetof(CLASS_DESC_VARIABLE, offset))), llvmType(getInt32PtrTy)))); + + llvm::Value* addr = builder->CreateGEP(object_ptr, offset); + + release_variable(type, addr); + variable_write(addr, value, type); + + unref_object_no_nullcheck(object_ptr); + + c_SP(-val->on_stack-obj->on_stack); +} + +llvm::Value* PushPureObjectConstantExpression::codegen_get_value(){ + llvm::Value* object = obj->codegen_get_value(); + if (obj->on_stack) + c_SP(-1); + + llvm::Value* object_ptr = extract_value(object, 1); + + make_nullcheck(object_ptr); + + llvm::Value* desc_const = get_class_desc_entry(object_ptr, index); + llvm::Value* ret; + + if (TYPE_is_string(type)){ + llvm::Value* translated = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(desc_const, getInteger(TARGET_BITS, 2*sizeof(void*)+8))), llvmType(getInt1Ty)); + llvm::Value* straddr = builder->CreateGEP(desc_const, getInteger(TARGET_BITS, offsetof(CLASS_DESC_CONSTANT, value))); + straddr = builder->CreateLoad(builder->CreateBitCast(straddr, llvmType(getInt8PtrTy))); + straddr = gen_if_phi(straddr, translated, [&](){ + return builder->CreateCall(get_global_function(GB.Translate, 'p', "p"), straddr); + }); + llvm::Value* len = builder->CreateCall(get_global_function(strlen, 'j', "p"), straddr); + if (TARGET_BITS == 64) + len = builder->CreateTrunc(len, llvmType(getInt32Ty)); + ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), straddr, getInteger(32, 0), len); + } else { + llvm::Type* t = TYPE_llvm(type); + llvm::Value* addr = builder->CreateGEP(desc_const, getInteger(TARGET_BITS, offsetof(CLASS_DESC_CONSTANT, value))); + ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(t))); + } + + unref_object_no_nullcheck(object_ptr); + + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushPureObjectPropertyExpression::codegen_private(bool get_value){ + obj->codegen_on_stack(); + + llvm::Value* object = ret_top_stack(obj->type, true); + + bool super = isa(obj); + + if (!super) + make_double_nullcheck(object); + + llvm::Value* object_ptr = extract_value(object, 1); + + llvm::Value* class_desc_property; + llvm::Value* is_native; + + if (super){ + is_native = getInteger(1, klass()->is_native); + } else { + class_desc_property = get_class_desc_entry(object_ptr, index); + if (!klass()->table[index].desc->property.native) + is_native = getInteger(1, false); //Assume no children can be native either + else + is_native = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, 4*TARGET_BITS/8))), llvmType(getInt1Ty)); + } + + //FIXME dispatch: + create_check(klass(), extract_value(object, 0), object_ptr); + + llvm::Value* ret = gen_if_else_phi(is_native, [&](){ + llvm::Value* read = super ? get_global((void*)this->desc()->property.read, llvmType(getInt8Ty)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, read))), charPP)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + read, + object_ptr, + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + return ret; + }, [&](){ + llvm::Value* read = super ? (llvm::Value*)getInteger(32, (int)(intptr_t)this->desc()->property.read) : (llvm::Value*)builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, read))), llvmType(getInt32PtrTy))); + llvm::Value* klass = super ? builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, klass))), charPP)); + + builder->CreateStore(klass, get_global((void*)&EXEC.klass)); + builder->CreateStore(object_ptr, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 0), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(read, get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 1));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + + llvm::Value* ret = read_value(get_global(RP, value_type), type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE)); + return ret; + }, "property_native", "property_non_native", "property_read_done"); + + unref_object_no_nullcheck(object_ptr); + + if (get_value && !on_stack){ + c_SP(-1); + return ret; + } else { + store_value(get_value_on_top_addr(), ret, type); + return ret; + } +} + +void PopPureObjectPropertyExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + obj->codegen_on_stack(); + + llvm::Value* object = ret_top_stack(obj->type, true); + + bool super = isa(obj); + + if (!super) + make_double_nullcheck(object); + + llvm::Value* object_ptr = extract_value(object, 1); + + llvm::Value* class_desc_property; + llvm::Value* is_native; + + if (super){ + is_native = getInteger(1, ((CLASS*)(void*)obj->type)->is_native); + } else { + CLASS* klass = (CLASS*)(void*)obj->type; + class_desc_property = get_class_desc_entry(object_ptr, index); + if (!klass->table[index].desc->property.native) + is_native = getInteger(1, false); //Assume no children can be native either + else + is_native = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, 4*TARGET_BITS/8))), llvmType(getInt1Ty)); + } + + //FIXME dispatch + create_check((CLASS*)(void*)obj->type, extract_value(object, 0), object_ptr); + + gen_if_else(is_native, [&](){ + llvm::Value* write = super ? get_global((void*)this->desc()->property.write, llvmType(getInt8Ty)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, write))), charPP)); + + llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -2)), llvmType(getInt8PtrTy)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + write, + object_ptr, + getInteger(TARGET_BITS, 0), + val_ptr); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + release(v, val->type); + }, [&](){ + push_value(v, val->type); + llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -3)), pointer_t(LONG_TYPE)); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), val_ptr); + + llvm::Value* write = super ? (llvm::Value*)getInteger(32, (int)(intptr_t)this->desc()->property.write) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, write))), llvmType(getInt32PtrTy))); + llvm::Value* klass = super ? builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, klass))), charPP)); + + builder->CreateStore(klass, get_global((void*)&EXEC.klass)); + builder->CreateStore(object_ptr, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 1), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(write, get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall5(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%d %p %p %d\n", llvmType(getInt8Ty)), getInteger(32, 2), klass, class_desc_property, getInteger(32, index));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + //I think the release of RP is not needed, it should already be T_VOID... + }, "property_native", "property_non_native", "property_write_done"); + + unref_object_no_nullcheck(object_ptr); + c_SP(-2); +} + +void PopVirtualPropertyExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + llvm::Value* object = obj->codegen_get_value(); + llvm::Value* object_ptr = extract_value(object, 1); + + if (is_static){ + //Only allowed on virtual classes + //Assume it returned a T_CLASS, or threw an error... + + gen_if_noreturn(builder->CreateICmpNE(load_element(get_value_on_top_addr(), 0), getInteger(TARGET_BITS, T_CLASS)), [&](){ + create_throw(E_STATIC, ((CLASS*)(void*)obj->type)->name, this->name); + }); + + object_ptr = get_nullptr(); + } else + create_check((CLASS*)(void*)obj->type, get_global((void*)obj->type, llvmType(getInt8Ty)), object_ptr); + + llvm::Value* write = get_global((void*)desc()->property.write, llvmType(getInt8Ty)); + llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -2)), llvmType(getInt8PtrTy)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + write, + object_ptr, + getInteger(TARGET_BITS, 0), + val_ptr); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + release(v, val->type); + if (!is_static) + unref_object_no_nullcheck(object_ptr); + c_SP(-2); +} + +llvm::Value* PushVirtualPropertyExpression::codegen_private(bool get_value){ + obj->codegen_on_stack(); + + llvm::Value* object = ret_top_stack(obj->type, true); + + //Might be T_NULL or T_CLASS or the virtual class: + llvm::Value* valtype = builder->CreatePtrToInt(extract_value(object, 0), LONG_TYPE); + + gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + create_throw(E_NULL); + }); + + //T_CLASS could be resolved at compile time, since only gb.db returns a (virtual) object + //when invoking a static read property/static method... + + llvm::Value* object_ptr = builder->CreateSelect( + builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_CLASS)), + get_nullptr(), + extract_value(object, 1)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc()->property.read, llvmType(getInt8Ty)), //Assume no override by inheritance + object_ptr, + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + + unref_object(object_ptr); + + if (get_value && !on_stack){ + c_SP(-1); + return ret; + } else { + store_value(get_value_on_top_addr(), ret, type); + return ret; + } +} + +llvm::Value* PushPureObjectStaticPropertyExpression::codegen_private(bool get_value){ + obj->codegen_on_stack(); + + //Assume it returned a T_CLASS, or threw an error... + + gen_if_noreturn(builder->CreateICmpNE(load_element(get_value_on_top_addr(), 0), getInteger(TARGET_BITS, T_CLASS)), [&](){ + create_throw(E_STATIC, this->klass()->name, this->name); + }); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc()->property.read, llvmType(getInt8Ty)), //Assume no override by inheritance + get_nullptr(), + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + + if (get_value && !on_stack){ + c_SP(-1); + return ret; + } else { + store_value(get_value_on_top_addr(), ret, type); + return ret; + } +} + +llvm::Value* PushStaticPropertyExpression::codegen_get_value(){ + CLASS* c = ((PushClassExpression*)obj)->klass; + CLASS_DESC* desc = c->table[index].desc; + + llvm::Value* ret; + + if (desc->property.native){ + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc->property.read, llvmType(getInt8Ty)), + get_nullptr(), + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + } else { + builder->CreateStore(get_global((void*)desc->property.klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + builder->CreateStore(get_nullptr(), get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 0), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(getInteger(32, (int)(intptr_t)desc->property.read), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 3));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + + ret = read_value(get_global(RP, value_type), type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE)); + } + + if (on_stack) + push_value(ret, type); + return ret; +} + +void PopStaticPropertyExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + + CLASS_DESC* desc = klass->table[index].desc; + + if (desc->property.native){ + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc->property.write, llvmType(getInt8Ty)), + get_nullptr(), + getInteger(TARGET_BITS, type), + builder->CreateBitCast(get_value_on_top_addr(), llvmType(getInt8PtrTy))); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + release(value, type); + c_SP(-val->on_stack); + } else { + builder->CreateStore(get_global((void*)desc->property.klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + builder->CreateStore(get_nullptr(), get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 1), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(getInteger(32, (int)(intptr_t)desc->property.write), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 4));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + } +} + +void PopUnknownPropertyUnknownExpression::codegen(){ + val->codegen_on_stack(); + + llvm::Value* effective_class; + llvm::Value* object; + + if (PushClassExpression* pce = dyn_cast(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy)); + object = get_nullptr(); + push_value(NULL, T_VOID); + } else { + llvm::Value* ob = obj->codegen_get_value(); + object = extract_value(ob, 1); + CLASS* k = (CLASS*)(void*)obj->type; + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + } + + builder->CreateCall3(get_global_function(JR_pop_unknown_property_unknown, 'v', "ppj"), + effective_class, object, getInteger(TARGET_BITS, (uint64_t)(void*)name)); +} + +void PopUnknownExpression::codegen(){ + val->codegen_on_stack(); + obj->codegen_on_stack(); + store_pc(pc); + builder->CreateCall(get_global_function_jif(EXEC_pop_unknown, 'v', "")); +} + +llvm::Value* PushDynamicExpression::codegen_get_value(){ + llvm::Value* ret; + if (ctype->id == TC_ARRAY){ + /*llvm::Value* obj = builder->CreateCall4(get_global_function_jif(CARRAY_create_static, 'p', "pppp"), + get_global((void*)CP, llvmType(getInt8Ty)), + current_op, + get_global(CP->load->array[ctype->value], llvmType(getInt8Ty)), + builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset))); + + borrow_object_no_nullcheck(obj); + + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj);*/ + ret = codegen_tc_array(CP, current_op, ctype->value, builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset)), type); + } else if (ctype->id == TC_STRUCT){ + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)CP, llvmType(getInt8Ty)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), + builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset))); + borrow_object_no_nullcheck(ret); + return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } else { + ret = read_variable_offset(type, current_op, getInteger(TARGET_BITS, offset)); + } + if (on_stack) + push_value(ret, type); + return ret; +} + +void PopDynamicExpression::codegen(){ + llvm::Value* new_val = val->codegen_get_value(); + c_SP(-val->on_stack); + release_variable(type, builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset))); + write_variable_offset(type, current_op, offset, new_val); +} + +//Same body as PopStaticVariableExpression ... +void PopStaticExpression::codegen(){ + llvm::Value* new_val = val->codegen_get_value(); + llvm::Value* ad = get_global((void*)addr, llvmType(getInt8Ty)); + release_variable(type, ad); + variable_write(ad, new_val, type); + c_SP(-val->on_stack); +} + +llvm::Value* PushFunctionExpression::codegen_get_value(){ + llvm::Value* func = insert_value(get_new_struct(function_type), current_op, 1); + borrow_object(current_op); + push_value(func, T_FUNCTION); + return func; +} +void PushFunctionExpression::codegen_on_stack(){ + //llvm::Value* func = get_new_struct(function_type, read_global((void*)&CP), current_op, getInteger(8, FUNCTION_PRIVATE), getInteger(8, true), getInteger(16, index)); + llvm::Value* func = insert_value(get_new_struct(function_type), current_op, 1); + borrow_object(current_op); + push_value(func, T_FUNCTION); +} + +void PushClassExpression::codegen_on_stack(){ + llvm::Value* sp = read_global((void*)&SP, pointer_t(value_types[T_CLASS])); + store_element(sp, 0, getInteger(TARGET_BITS, T_CLASS)); + store_element(sp, 1, get_global((void*)klass, llvmType(getInt8Ty))); + c_SP(1); +} + +llvm::Value* AddQuickExpression::codegen_get_value(){ + if (type == T_VARIANT){ + expr->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_aq_variant, 'v', "i"), getInteger(32, add)); + return ret_top_stack(T_VARIANT, on_stack); + } + llvm::Value* orig = expr->codegen_get_value(); + if (expr->on_stack) c_SP(-1); + + llvm::Value* new_val; + switch(type){ + case T_BYTE: new_val = builder->CreateAdd(orig, getInteger(8, add)); break; + case T_SHORT: new_val = builder->CreateAdd(orig, getInteger(16, add)); break; + case T_INTEGER: new_val = builder->CreateAdd(orig, getInteger(32, add)); break; + case T_LONG: new_val = builder->CreateAdd(orig, getInteger(64, add)); break; + case T_SINGLE: new_val = builder->CreateFAdd(orig, getFloat((float)add)); break; + case T_FLOAT: new_val = builder->CreateFAdd(orig, getFloat((double)add)); break; + case T_POINTER: new_val = builder->CreateGEP(orig, getInteger(TARGET_BITS, add)); break; + default: __builtin_unreachable(); + } + if (on_stack) + push_value(new_val, type); + return new_val; +} + +llvm::Value* PushMeExpression::codegen_get_value(){ + //llvm::Value* ret = get_new_struct(object_type, read_global((void*)CP), current_op); + llvm::Value* ret = get_new_struct(object_type, get_global((void*)CP, llvmType(getInt8Ty)), current_op); + borrow_object_no_nullcheck(current_op); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushSuperExpression::codegen_get_value(){ + llvm::Value* ret = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), current_op); + borrow_object_no_nullcheck(current_op); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushLocalExpression::codegen_get_value(){ + llvm::Value* ret = builder->CreateLoad(locals[index]); + if (on_stack) + push_value(ret, type); + if (no_ref_variant == false) + borrow(ret, type); + return ret; +} +void PushLocalExpression::codegen_on_stack(){ + llvm::Value* ret = builder->CreateLoad(locals[index]); + push_value(ret, type); + + borrow(ret, type); //Is this better optimized? + //borrow_top(type); +} + +llvm::Value* PushParamExpression::codegen_get_value(){ + llvm::Value* ret = builder->CreateLoad(params[index + FP->n_param]); + if (on_stack) + push_value(ret, type); + borrow(ret, type); + return ret; +} +void PushParamExpression::codegen_on_stack(){ + llvm::Value* ret = builder->CreateLoad(params[index + FP->n_param]); + push_value(ret, type); + borrow(ret, type); + //borrow_top(type); +} + +void PopLocalExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + if (val->on_stack) c_SP(-1); + llvm::Value* old_val = builder->CreateLoad(locals[index]); + release(old_val, type); + builder->CreateStore(v, locals[index]); + if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + if (TYPE_is_object(type)){ + //codegen_printf("%p\n", extract_value(v, 0)); + } + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type, false); + } else if (type == T_VARIANT && val->no_ref_variant){ + llvm::Value* vtype = extract_value(old_val, 0); + llvm::Value* is_str = builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_STRING)); + llvm::Value* is_obj = builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)); + gen_if(builder->CreateOr(is_str, is_obj), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, get_default(T_VARIANT), T_VARIANT, false); + }); + } +} + +void PopParamExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + if (val->on_stack) c_SP(-1); + llvm::Value* old_val = builder->CreateLoad(params[index + FP->n_param]); + release(old_val, type); + builder->CreateStore(v, params[index + FP->n_param]); + if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type, false); + } else if (type == T_VARIANT && val->no_ref_variant){ + llvm::Value* vtype = extract_value(old_val, 0); + llvm::Value* is_str = builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_STRING)); + llvm::Value* is_obj = builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)); + gen_if(builder->CreateOr(is_str, is_obj), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, get_default(T_VARIANT), T_VARIANT, false); + }); + } +} + +static llvm::AllocaInst* create_alloca_in_entry(llvm::Type* t){ + llvm::BasicBlock* bb = builder->GetInsertBlock(); + builder->SetInsertPoint(entry_block, entry_block->begin()); + llvm::AllocaInst* ret = builder->CreateAlloca(t); + builder->SetInsertPoint(bb); + return ret; +} + +static llvm::AllocaInst* create_alloca_in_entry_init_default(llvm::Type* t, TYPE type){ + llvm::BasicBlock* bb = builder->GetInsertBlock(); + builder->SetInsertPoint(entry_block, entry_block->begin()); + llvm::AllocaInst* ret = builder->CreateAlloca(t); + store_default(ret, type); + builder->SetInsertPoint(bb); + return ret; +} + +static llvm::Value* release_ctrl(int index){ + llvm::Value* old_type = builder->CreateLoad(current_ctrl_types[index - FP->n_local]); + if (is_ctrl_type_used(1, index)){ + gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 1)), [&](){ + release(builder->CreateLoad(ctrl_values[index - FP->n_local][1]), T_STRING); + }, "was_string_ctrl_before"); + } + if (is_ctrl_type_used(2, index)){ + gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 2)), [&](){ + release(builder->CreateLoad(ctrl_values[index - FP->n_local][2]), T_OBJECT); + }, "was_object_ctrl_before"); + } + if (is_ctrl_type_used(3, index)){ + gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 3)), [&](){ + release(builder->CreateLoad(ctrl_values[index - FP->n_local][3]), T_VARIANT); + }, "was_variant_ctrl_before"); + } + return old_type; +} + +static void set_ctrl(llvm::Value* v, TYPE type, int index){ + llvm::Value* old_type = release_ctrl(index); + + int type_next = special_ctrl_type(type); + builder->CreateStore(getInteger(32, type_next), current_ctrl_types[index - FP->n_local]); + set_ctrl_type(type, index); + + if (type_next == 0){ + if (locals[index] == NULL || locals[index]->getType() != TYPE_llvm(type)) + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } else { + locals[index] = ctrl_values[index - FP->n_local][type_next]; + } + + if (type == T_STRING || type == T_VARIANT || TYPE_is_object(type)){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } else { + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + }, "old_ctrl_needs_to_be_cleaned2"); + } + + if (type != T_NULL) + builder->CreateStore(v, locals[index]); +#if 0 + TYPE old_ctrl_type = get_ctrl_type(index); + set_ctrl_type(type, index); + + if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL) + release(builder->CreateLoad(locals[index]), old_ctrl_type); + if (type == T_NULL) return; + if (old_ctrl_type != type){ + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } + builder->CreateStore(v, locals[index]); + if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) || + type == T_STRING || type == T_VARIANT || TYPE_is_object(type)){ + + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } +#endif + /*builder->CreateStore(v, locals[index]); + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type);*/ +} + +static void codegen_pop_ctrl(llvm::Value* v, Expression* val, int index){ + TYPE type = val->type; + + llvm::Value* old_type = release_ctrl(index); + + int type_next = special_ctrl_type(type); + builder->CreateStore(getInteger(32, type_next), current_ctrl_types[index - FP->n_local]); + set_ctrl_type(type, index); + + if (type_next == 0){ + if (locals[index] == NULL || locals[index]->getType() != TYPE_llvm(type)) + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } else { + locals[index] = ctrl_values[index - FP->n_local][type_next]; + } + + if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } else { + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + }, "old_ctrl_needs_to_be_cleaned3"); + } + + if (val->on_stack) c_SP(-1); + + if (type != T_NULL) + builder->CreateStore(v, locals[index]); + + /* + // + TYPE type = val->type; + + TYPE old_ctrl_type = get_ctrl_type(index); + set_ctrl_type(type, index); + + if (val->on_stack) c_SP(-1); + if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL) + release(builder->CreateLoad(locals[index]), old_ctrl_type); + if (type == T_NULL) return; + if (old_ctrl_type != type){ + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } + builder->CreateStore(v, locals[index]); + if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) || + type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } + */ +} + +static void codegen_pop_ctrl(Expression* val, int index){ + codegen_pop_ctrl(val->codegen_get_value(), val, index); + return; + + /*TYPE type = val->type; + + TYPE old_ctrl_type = get_ctrl_type(index); + set_ctrl_type(type, index); + + llvm::Value* v = val->codegen_get_value(); + if (val->on_stack) c_SP(-1); + if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL) + release(builder->CreateLoad(locals[index]), old_ctrl_type); + if (type == T_NULL) return; + if (old_ctrl_type != type){ + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } + builder->CreateStore(v, locals[index]); + if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) || + type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + }*/ +} + +//Same code as PopLocalExpression::codegen +void PopCtrlExpression::codegen(){ + if (!isa(val)) + codegen_pop_ctrl(val, index); +} + +void PopOptionalExpression::codegen(){ + llvm::Value* addr = builder->CreateBitCast(builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE))), pointer_t(LONG_TYPE)); + llvm::Value* t = builder->CreateLoad(addr); + gen_if(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, T_VOID)), [&](){ + if (is_default){ + store_default(params[FP->n_param + index], type); + } else { + llvm::Value* value = val->codegen_get_value(); + store_value(addr, value, type); + builder->CreateStore(value, params[FP->n_param + index]); + if (val->on_stack) c_SP(-1); + } + }, "not_passed", "passed_or_done"); +} + +llvm::Value* PushAutoCreateExpression::codegen_get_value(){ + llvm::Value* ret = builder->CreateCall2(get_global_function(GB.AutoCreate, 'p', "pi"), + get_global((void*)klass, llvmType(getInt8Ty)), getInteger(32, 0)); + + borrow_object_no_nullcheck(ret); + + ret = get_new_struct(object_type, + get_global((void*)klass, llvmType(getInt8Ty)), ret); + + if (on_stack) + push_value(ret, type); + return ret; +} + +void PushPureObjectUnknownExpression::codegen_on_stack(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = (CLASS*)(void*)obj->type; + llvm::Value* effective_class; + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + + /*llvm::Value* func = insert_value(get_new_struct(function_type), object, 1);*/ + + builder->CreateCall4(get_global_function(JR_push_unknown_property_unknown, 'v', "pipp"), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)name), llvmType(getInt8PtrTy)), + getInteger(32, name_id), + effective_class, + object); +} + +llvm::Value* PushPureObjectUnknownExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_VARIANT, true); +} + +void PushStaticUnknownExpression::codegen_on_stack(){ + builder->CreateCall4(get_global_function(JR_push_unknown_property_unknown, 'v', "pipp"), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)name), llvmType(getInt8PtrTy)), + getInteger(32, name_id), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)klass), llvmType(getInt8PtrTy)), + get_nullptr()); +} + +llvm::Value* PushStaticUnknownExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_VARIANT, true); +} + +llvm::Value* PushPureObjectFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + CLASS_DESC* d = desc(); + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + //FIXME Doesn't PushVirtualFunctionExpression take care of this? + //CLASS_load(k); + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + + llvm::Value* func = insert_value(get_new_struct(function_type), object, 1); + + if (!d->method.native){ + //Assume native classes don't inherit non-native ones :) + } else { + int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4; + int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8; + int offset_native_flag = TARGET_BITS == 64 ? 35 : 19; + //table_addr = (char*)effective_class->table + llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table))); + //desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset) + llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol)); + //desc_addr = *(char**)desc_addr_addr + llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy)))); + //native_flag_addr = (char*)(desc_addr + offset_native_flag) + llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag)); + //native_subr_flag = *native_flag_addr + llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr); + //native_flag = (bool)(native_subr_flag & 1) + llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty)); + //subr_flag = (bool)(native_subr_flag & 2) + //llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0)); + + //llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC)); + llvm::Value* function_kind = builder->CreateSelect(native_flag, getInteger(8, FUNCTION_NATIVE), getInteger(8, FUNCTION_PUBLIC)); + ///Disable subr because it is only meant to be used static + func = insert_value(func, function_kind, 2); + } + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +llvm::Value* PushVirtualFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + + effective_class = get_global((void*)k, llvmType(getInt8Ty)); + + create_check(k, effective_class, object); + + llvm::Value* func = insert_value(insert_value(get_new_struct(function_type), object, 1), getInteger(8, FUNCTION_NATIVE), 2); + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +llvm::Value* PushVirtualStaticFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + + effective_class = get_global((void*)k, llvmType(getInt8Ty)); + + llvm::Value* func = insert_value(insert_value(get_new_struct(function_type), object, 1), getInteger(8, FUNCTION_NATIVE), 2); + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +//Almost the same code as above, but setting object to null ... +llvm::Value* PushPureObjectStaticFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + CLASS_DESC* d = desc(); + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + //CLASS_load(k); + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + + unref_object(object); + object = get_nullptr(); + + llvm::Value* func = insert_value(get_new_struct(function_type), object, 1); + + if (!d->method.native){ + //Assume native classes don't inherit non-native ones :) + } else { + int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4; + int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8; + int offset_native_flag = TARGET_BITS == 64 ? 35 : 19; + //table_addr = (char*)effective_class->table + llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table))); + //desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset) + llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol)); + //desc_addr = *(char**)desc_addr_addr + llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy)))); + //native_flag_addr = (char*)(desc_addr + offset_native_flag) + llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag)); + //native_subr_flag = *native_flag_addr + llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr); + //native_flag = (bool)(native_subr_flag & 1) + llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty)); + //subr_flag = (bool)(native_subr_flag & 2) + //llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0)); + + //llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC)); + llvm::Value* function_kind = builder->CreateSelect(native_flag, getInteger(8, FUNCTION_NATIVE), getInteger(8, FUNCTION_PUBLIC)); + ///Disable subr, because I think it will never be used this way anyway, else it is a (big) performance hit. + func = insert_value(func, function_kind, 2); + } + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +llvm::Value* PushStaticFunctionExpression::codegen_get_value(){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)klass()), llvmType(getInt8PtrTy)); + + llvm::Value* ret = get_new_struct(function_type, effective_class, get_nullptr()); + + if (on_stack) + push_value(ret, T_FUNCTION); + return ret; +} + +llvm::Value* PushPureObjectStructFieldExpression::codegen_get_value(){ + llvm::Value* struct_obj = extract_value(obj->codegen_get_value(), 1); + + make_nullcheck(struct_obj); + + llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP); + + llvm::Value* element_addr = gen_if_else_phi(builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()), [&](){ + llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP); + return builder->CreateGEP(builder->CreateLoad(addr_addr), getInteger(TARGET_BITS, this->desc()->variable.offset)); + }, [&](){ + return builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, sizeof(CSTRUCT) + this->desc()->variable.offset)); + }); + + llvm::Value* ret; + + int ctype_id = desc()->variable.ctype.id; + if (ctype_id == TC_ARRAY){ + //Embedded array inside struct + ret = codegen_tc_array(desc()->variable.klass, struct_obj, desc()->variable.ctype.value, element_addr, type); + } else if (ctype_id == TC_STRUCT){ + //Embedded struct inside struct + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)desc()->variable.klass, llvmType(getInt8Ty)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), + element_addr); + borrow_object_no_nullcheck(ret); + + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } else { + ret = read_variable(type, element_addr); + } + + unref_object_no_nullcheck(struct_obj); + + c_SP(-obj->on_stack); + if (on_stack) + push_value(ret, type); + return ret; +} + +void PopPureObjectStructFieldExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* struct_obj = extract_value(obj->codegen_get_value(), 1); + + make_nullcheck(struct_obj); + + llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP); + + llvm::Value* element_addr = gen_if_else_phi(builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()), [&](){ + llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP); + return builder->CreateGEP(builder->CreateLoad(addr_addr), getInteger(TARGET_BITS, this->desc()->variable.offset)); + }, [&](){ + return builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, sizeof(CSTRUCT) + this->desc()->variable.offset)); + }); + + release_variable(type, element_addr); + variable_write(element_addr, value, type); + + unref_object_no_nullcheck(struct_obj); +} + +llvm::Value* SwapExpression::codegen_get_value(){ + llvm::Value* ret = push_a_expr->codegen_get_value(); + pop_a_expr->codegen(); + return ret; +} + +void SwapExpression::codegen_on_stack(){ + push_a_expr->codegen_on_stack(); + pop_a_expr->codegen(); +} + +static llvm::Type* const extern_types[] = { + llvmType(getVoidTy), + llvmType(getInt8Ty), + llvmType(getInt8Ty), + llvmType(getInt16Ty), + llvmType(getInt32Ty), + llvmType(getInt64Ty), + llvmType(getFloatTy), + llvmType(getDoubleTy), + NULL, + llvmType(getInt8PtrTy), + llvmType(getInt8PtrTy), + llvmType(getInt8PtrTy), + NULL, + NULL, + NULL, + llvmType(getInt8PtrTy), + llvmType(getInt8PtrTy) +}; + +llvm::Value* codegen_extern_manage_value(llvm::Value* val, TYPE type){ + if (type == T_BOOLEAN) + val = builder->CreateZExt(val, llvmType(getInt8Ty)); + + else if (TYPE_is_string(type)) + val = builder->CreateGEP(extract_value(val, 1), to_target_int(extract_value(val, 2))); + + else if (TYPE_is_object(type)){ + val = extract_value(val, 1); + val = gen_if_phi(get_nullptr(), builder->CreateICmpNE(val, get_nullptr()), [&](){ + CLASS* object_class = (CLASS*)(void*)type; + + llvm::Value* normal = builder->CreateGEP(val, getInteger(TARGET_BITS, sizeof(OBJECT))); + + llvm::Value* OBJ = builder->CreateBitCast(val, pointer_t(OBJECT_type)); + llvm::Value* klass = load_element(OBJ, 0); + + auto get_bit_from_class = [](llvm::Value* obj, int offset_byte, int offset_bit){ + return builder->CreateTrunc(builder->CreateLShr(builder->CreateLoad(builder->CreateGEP(obj, getInteger(TARGET_BITS, offset_byte))), getInteger(8, offset_bit)), llvmType(getInt1Ty)); + }; + + auto handle_class_object = [normal, &get_bit_from_class](llvm::Value* obj){ + const int offset_is_native = TARGET_BITS == 64 ? 34 : 22; + const int bit_index_is_native = 2; + + llvm::Value* is_native = get_bit_from_class(obj, offset_is_native, bit_index_is_native); + return gen_if_phi(normal, builder->CreateXor(is_native, getInteger(1, true)), [obj](){ + llvm::Value* stat = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CLASS, stat))), charPP); + return builder->CreateLoad(stat); + }, "not_native"); + }; + + auto handle_struct_object = [](llvm::Value* obj){ + /*if (((CSTRUCT *)ob)->ref) + addr = (char *)((CSTATICSTRUCT *)ob)->addr; + else + addr = (char *)ob + sizeof(CSTRUCT);*/ + llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP); + llvm::Value* ref_not_null = builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()); + + return gen_if_phi(builder->CreateGEP(obj, getInteger(TARGET_BITS, sizeof(CSTRUCT))), ref_not_null, [&](){ + llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP); + return builder->CreateLoad(addr_addr); + }); + }; + + if (TYPE_is_pure_object(type) && object_class == (CLASS*)(void*)GB.FindClass("Class")){ + val = handle_class_object(val); + } else if (TYPE_is_pure_object(type) && CLASS_is_array(object_class)){ + val = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(val, getInteger(TARGET_BITS, offsetof(CARRAY, data))), charPP)); + } else if (TYPE_is_pure_object(type) && CLASS_is_struct(object_class)){ + val = handle_struct_object(val); + } else if (TYPE_is_pure_object(type)){ + val = normal; + } else { + val = gen_if_else_phi(builder->CreateICmpEQ(klass, builder->CreateIntToPtr(getInteger(TARGET_BITS, GB.FindClass("Class")), llvmType(getInt8PtrTy))), [&](){ + return handle_class_object(val); + }, [&](){ + const int offset_is_array = TARGET_BITS == 64 ? 34 : 22; + const int bit_index_is_array = 6; + + return gen_if_else_phi(get_bit_from_class(klass, offset_is_array, bit_index_is_array), [&](){ + return builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(val, getInteger(TARGET_BITS, offsetof(CARRAY, data))), charPP)); + }, [&](){ + const int offset_is_struct = TARGET_BITS == 64 ? 34 : 22; + const int bit_index_is_struct = 5; + + return gen_if_phi(normal, get_bit_from_class(klass, offset_is_struct, bit_index_is_struct), [&](){ + return handle_struct_object(val); + }, "extern_arg_is_struct"); + }, "extern_arg_is_array"); + }, "extern_arg_is_class"); + } + return val; + }, "OBJ_not_null_for_extern"); + } + return val; +} + +static llvm::Value* codegen_extern_manage_return_value(llvm::Value* ret, TYPE type){ + if (type == T_BOOLEAN) + ret = builder->CreateICmpNE(ret, getInteger(8, false)); + else if (TYPE_is_string(type)){ + ret = gen_if_phi(get_default(T_CSTRING), builder->CreateICmpNE(ret, get_nullptr()), [&](){ + return get_cstring_from_addr(ret); + }, "extern_return_not_nullstring"); + } else if (TYPE_is_object(type)){ + if (TYPE_is_pure_object(type)){ + CLASS* class_struct = (CLASS*)(void*)type; + if (CLASS_is_struct(class_struct)){ + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)-1, llvmType(getInt8Ty)), + get_global((void*)class_struct, llvmType(getInt8Ty)), + ret); + } + } + borrow_object(ret); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } + return ret; +} + +static void func_extern_call_variant_vararg(void* return_value_addr, void* func_addr, int nargs, TYPE return_type); + +static llvm::Value* codegen_raise_event(std::vector& args, int index, bool on_stack){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + llvm::Value* event_stop = builder->CreateCall3(get_global_function_vararg(GB.Raise, 'c', "pii"), + current_op, getInteger(32, index), getInteger(32, -args.size())); + event_stop = builder->CreateTrunc(event_stop, llvmType(getInt1Ty)); + + if (on_stack) + push_value(event_stop, T_BOOLEAN); + return event_stop; +} + +llvm::Value* CallExpression::codegen_get_value(){ + if (func->type == T_CLASS && desc == NULL){ + //Cast + llvm::Value* ret = args[0]->codegen_get_value(); + if (!args[0]->on_stack && on_stack){ + push_value(ret, type); + } else if (args[0]->on_stack && !on_stack){ + c_SP(-1); + } + return ret; + } + if (FunctionExpression* fe = dynamic_cast(func)){ + if (fe->function_expr_type == EventFn){ + return codegen_raise_event(args, ((PushEventExpression*)func)->index, on_stack); + } + } + if (PushExternExpression* ee = dynamic_cast(func)){ + llvm::Value* call_addr; + + if (ee->object_to_release){ + llvm::Value* obj = ee->object_to_release->codegen_get_value(); + c_SP(-ee->object_to_release->on_stack); + call_addr = builder->CreateCall2(get_global_function(JR_extern_dispatch_object, 'p', "pi"), extract_value(obj, 1), getInteger(32, ee->index)); + } + + CLASS_EXTERN* ext; + if (ee->object_to_release) + ext = &ee->klass->load->ext[ee->klass->table[ee->index].desc->ext.exec]; //An extern method with the correct signature + else + ext = &ee->klass->load->ext[ee->index]; + EXTERN_FUNC_INFO func = JIF.F_EXTERN_get_function_info(ext); + + llvm::Value* ret = NULL; + + if (!ee->must_variant_dispatch){ + std::vector ft; + std::vector orig_args; + std::vector func_args; + ft.resize(ext->n_param); + orig_args.resize(args.size()); + func_args.resize(args.size()); + + for(size_t i=0; in_param) + ft[i] = extern_types[ext->param[i].type]; + + llvm::Value* val = args[i]->codegen_get_value(); + orig_args[i] = val; + + val = codegen_extern_manage_value(val, args[i]->type); + func_args[i] = val; + } + + llvm::FunctionType* function_type = llvm::FunctionType::get(extern_types[type > T_OBJECT ? T_OBJECT : type], ft, true); + + std::string function_name = ext->library; + function_name += '.'; + function_name += func.alias; + + llvm::Value* call_function; + if (!ee->object_to_release){ + llvm::GlobalValue* glob_val = (llvm::GlobalValue*)M->getOrInsertFunction(function_name, function_type); + register_global_symbol(function_name, glob_val, func.call); + call_function = glob_val; + } else { + call_function = builder->CreateBitCast(call_addr, pointer_t(function_type)); + } + + ret = builder->CreateCall(call_function, func_args); + + ret = codegen_extern_manage_return_value(ret, type); + + for(size_t arg=args.size(); arg --> 0; ){ + release(orig_args[arg], args[arg]->type); + if (args[arg]->on_stack) + c_SP(-1); + } + } else { + for(size_t i=0; icodegen_on_stack(); + } + + llvm::Value* return_value_addr = type == T_VOID ? get_nullptr() : (llvm::Value*)create_alloca_in_entry(TYPE_llvm(type)); + if (!ee->object_to_release) + call_addr = builder->CreateIntToPtr(getInteger(TARGET_BITS, (uint64_t)func.call), llvmType(getInt8PtrTy)); + llvm::Value* args_size = getInteger(32, args.size()); + llvm::Value* return_type = getInteger(TARGET_BITS, type); + + builder->CreateCall4(get_global_function(func_extern_call_variant_vararg, 'v', "ppij"), + builder->CreateBitCast(return_value_addr, llvmType(getInt8PtrTy)), + call_addr, + args_size, + return_type); + + if (type != T_VOID) + ret = builder->CreateLoad(return_value_addr); + } + + if (on_stack) + push_value(ret, type); + + return ret; + } + codegen_on_stack(); + return ret_top_stack(type, on_stack); +} +void CallExpression::codegen_on_stack(){ + FunctionExpression* fe = dynamic_cast(func); + if (fe && fe->function_kind == FUNCTION_SUBR){ + //String.Mid, String.Left etc + + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + + void* f = (void*)klass->table[index].desc->method.exec; + builder->CreateCall( + builder->CreateBitCast(get_global(f, llvmType(getInt8Ty)), pointer_t(get_function_type('v', "h"))), + getInteger(16, args.size())); + + return; + } + if (fe && fe->function_kind == FUNCTION_EVENT){ + codegen_raise_event(args, ((PushEventExpression*)func)->index, on_stack); + return; + } + if (isa(func)){ + codegen_get_value(); + return; + } + + llvm::Value *func_value, *object; + + FunctionExpression fe_temp; + + if (func->type == T_CLASS){ + if (desc == NULL){ + //Cast + llvm::Value* ret = args[0]->codegen_get_value(); + if (!args[0]->on_stack){ + push_value(ret, type); + } + return; + } + func_value = get_new_struct(function_type, get_global((void*)klass, llvmType(getInt8Ty)), get_nullptr()); + push_value(func_value, T_FUNCTION); + object = get_nullptr(); + + fe = &fe_temp; + + fe->function_class = klass; + fe->function_expr_type = ClassFn; + fe->function_kind = desc->method.native ? FUNCTION_NATIVE : FUNCTION_PUBLIC; + fe->effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + fe->function_unknown = NULL; + } else if (fe != NULL){ + func_value = func->codegen_get_value(); + object = extract_value(func_value, 1); + } else { + //Variant call + func->codegen_on_stack(); + } + //llvm::Value* func_value_addr = builder->CreateBitCast(get_value_on_top_addr(), pointer_t(value_types[T_FUNCTION])); + //llvm::Value* object = load_element(func_value_addr, 2); + + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + + //FIXME might only be needed for non-quick calls: + builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)pc), get_global((void*)&PC, LONG_TYPE)); + + if (fe != NULL){ + builder->CreateStore(getInteger(8, args.size()), get_global((void*)&EXEC.nparam, llvmType(getInt8Ty))); + if (fe->function_expr_type == PrivateFn){ + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(current_op, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function_jif(EXEC_enter_quick, 'v', "")); + else + builder->CreateCall(get_global_function_jif(EXEC_enter, 'v', "")); + if (klass->load->func[index].fast) + builder->CreateCall(get_global_function(JR_EXEC_jit_execute_function, 'v', "")); + else + builder->CreateCall(get_global_function_jif(EXEC_function_loop, 'v', "")); + } /*else if (fe->function_expr_type == UnknownFn){ + *pc |= CODE_CALL_VARIANT; + } */else { + if (fe->function_unknown){ + builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)fe->function_unknown), get_global((void*)&EXEC_unknown_name, LONG_TYPE)); + *pc |= CODE_CALL_VARIANT; + } + + if (fe->function_kind == FUNCTION_PUBLIC){ + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + } else if (fe->function_kind == FUNCTION_NATIVE){ + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc, llvmType(getInt8PtrTy))); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(fe->effective_class, get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function_jif(EXEC_native_quick, 'v', "")); + else { + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + builder->CreateCall(get_global_function_jif(EXEC_native, 'v', "")); + } + } else if (fe->function_kind == -1){ + llvm::Value* kind = extract_value(func_value, 2); + gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_PUBLIC)), [&](){ + //Public + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + //builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', "")); + }, [&](){ + //Native + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc)); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(fe->effective_class, get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function_jif(EXEC_native_quick, 'v', "")); + else { + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + builder->CreateCall(get_global_function_jif(EXEC_native, 'v', "")); + } + }); + } else { + //FIXME hmm does this ever happen? + abort(); + } + } + } else { + //Variant call + builder->CreateCall(get_global_function(JR_call, 'v', "i"), getInteger(32, args.size())); + } + if (!byref_expressions.empty()){ + gen_if_noreturn(builder->CreateICmpEQ(read_global((void*)&PC, llvmType(getInt16PtrTy)), get_global((void*)pc, llvmType(getInt16Ty))), [&](){ + create_throw(E_BYREF); + }); + for(size_t i=0, e=byref_expressions.size(); i!=e; i++){ + byref_expressions[i]->codegen(); + } + } + + return; +#if 0 + if (desc != NULL || kind == FUNCTION_PRIVATE){ + if (kind != -1){ + switch(kind){ + case FUNCTION_NULL: abort(); + case FUNCTION_NATIVE: + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + //builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.native, llvmType(getInt8Ty))); + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc, llvmType(getInt8PtrTy))); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + + + if (can_quick) + builder->CreateCall(get_global_function(EXEC_native_quick, 'v', "")); + else + builder->CreateCall(get_global_function(EXEC_native, 'v', "")); + break; + + case FUNCTION_PRIVATE: + //builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.native, llvmType(getInt8Ty))); + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function(EXEC_enter_quick, 'v', "")); + else + builder->CreateCall(get_global_function(EXEC_enter, 'v', "")); + builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', "")); + break; + + case FUNCTION_PUBLIC: { + //builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.native, llvmType(getInt8Ty))); + /*builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc)); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));*/ + PushPureObjectExpression* ppoe = (PushPureObjectExpression*)func; + + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + break; + } + case FUNCTION_EVENT: + builder->CreateCall3(get_global_function_vararg(GB_Raise, 'i', "pii"), + current_op, getInteger(32, index + (CP->parent ? CP->parent->n_event : 0)), getInteger(32, -args.size())); + break; + + case FUNCTION_UNKNOWN: + case FUNCTION_CALL: + case FUNCTION_EXTERN: + case FUNCTION_SUBR: + abort(); + } + } else { + llvm::Value* kind = extract_value(func_value, 2); + gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_SUBR)), [&](){ + //Subr + //FIXME + }, [&](){ + PushPureObjectExpression* ppoe = (PushPureObjectExpression*)func; + + + gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_PUBLIC)), [&](){ + //Public + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + //builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', "")); + }, [&](){ + //Native + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc)); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(ppoe->effective_class, get_global((void*)&EXEC.klass)); + + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + if (can_quick) + builder->CreateCall(get_global_function(EXEC_native_quick, 'v', "")); + else + builder->CreateCall(get_global_function(EXEC_native, 'v', "")); + }); + }); + } + } else { + //FIXME call variant/object runtime + } +#endif +} + +llvm::Value* DupExpression::codegen_get_value(){ + if (on_stack || expr->on_stack) on_stack = expr->on_stack = true; + duped->llvm_value = expr->codegen_get_value(); + borrow(duped->llvm_value, type); + return duped->llvm_value; +} + +void DupExpression::codegen_on_stack(){ + on_stack = expr->on_stack = true; + expr->codegen_on_stack(); + duped->llvm_value = ret_top_stack(type, true); + borrow(duped->llvm_value, type); +} + +void OnGotoExpression::codegen(){ + JumpTablePendingBranch p; + p.condition = condition->codegen_get_value(); + if (condition->on_stack) + c_SP(-1); + p.insert_point = builder->GetInsertBlock(); //Must be after condition codegen! + p.destinations = &destinations; + p.default_addr = default_addr; + jump_table_pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpExpression::codegen(){ + PendingBranch p; + p.condition = NULL; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void GosubExpression::codegen(){ + llvm::Value* cond = NULL; + if (!destinations.empty()){ + cond = condition->codegen_get_value(); + if (condition->on_stack) + c_SP(-1); + } + + gen_if_else(!cond ? getInteger(1, true) : builder->CreateICmpULT(cond, getInteger(32, destinations.size())), [&](){ + /*llvm::Value* index = builder->CreateLoad(temp_num_gosubs_on_stack); + llvm::Value* indices[2] = {getInteger(TARGET_BITS, 0), to_target_int(index)}; + builder->CreateStore(llvm::BlockAddress::get(contpoint), builder->CreateGEP(temp_gosub_stack, indices)); + + index = builder->CreateAdd(index, getInteger(32, 1)); + builder->CreateStore(index, temp_num_gosubs_on_stack);*/ + +#ifndef GOSUB_ON_STACK + llvm::Value* gosub_stack_node = builder->CreateBitCast(builder->CreateCall(get_global_function(GB.Add, 'p', "p"), get_global(&GP, llvmType(getInt8Ty))), pointer_t(gosub_stack_node_type)); + + /*builder->CreateStore(builder->CreateLoad(gosub_return_point), create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0)); + builder->CreateStore(llvm::BlockAddress::get(contpoint), gosub_return_point);*/ + unsigned int gosub_return_id = gosub_continue_points.size() + 1; + builder->CreateStore(builder->CreateLoad(gosub_return_point), create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0)); + builder->CreateStore(getInteger(16, gosub_return_id), gosub_return_point); +#else + llvm::Value* stack_addr = builder->CreateLoad(gp); + unsigned int gosub_return_id = gosub_continue_points.size() + 1; + store_value(stack_addr, builder->CreateLoad(gosub_return_point), T_SHORT); + builder->CreateStore(getInteger(16, gosub_return_id), gosub_return_point); + + int diff = 1 + end_ctrl - FP->n_local; + llvm::Value* new_gp = builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, diff)); + gen_if_noreturn(builder->CreateICmpUGE( + builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, 1 + FP->stack_usage - FP->n_local + 8)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)STACK_limit), pointer_t(value_type))), [&](){ + + create_throw(E_STACK); + }); + builder->CreateStore(new_gp, gp); + //c_SP(diff); + builder->CreateStore(new_gp, get_global(&SP, pointer_t(value_type))); +#endif + + + if (FP->n_ctrl){ +#ifndef GOSUB_ON_STACK + llvm::Value* gp_ctrl_addr = create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 1); + + builder->CreateCall2(get_global_function(GB.Alloc, 'v', "pj"), builder->CreateBitCast(gp_ctrl_addr, llvmType(getInt8PtrTy)), getInteger(TARGET_BITS, sizeof(VALUE) * FP->n_ctrl)); + + llvm::Value* gp_ctrl = builder->CreateLoad(gp_ctrl_addr); +#else + llvm::Value* gp_ctrl = builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, 1)); +#endif + + + for(int i=FP->n_local; in_local ? gp_ctrl : builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), builder->CreateLoad(locals[i]), get_ctrl_type(i)); + int spec_type = special_ctrl_type(get_ctrl_type(i)); + if (spec_type != 0){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))); + store_value(stack_addr, NULL, T_VOID); + } + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]); + } + for(int i=end_ctrl; in_local+FP->n_ctrl; i++){ + store_value(builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), NULL, T_VOID); + + llvm::Value* old_type = release_ctrl(i); + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]); + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))); + store_value(stack_addr, NULL, T_VOID); + }, "old_ctrl_needs_to_be_cleaned4"); + } + } + }, [&](){ + if (cond){ + PendingBranch p; + p.condition = NULL; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = default_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); + } + }, "gosub_should_run"); + + if (!cond){ + PendingBranch p; + p.condition = NULL; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = gosubaddr; + pending_branches.push_back(p); + } else { + JumpTablePendingBranch p; + p.condition = cond; + p.insert_point = builder->GetInsertBlock(); + p.destinations = &destinations; + p.default_addr = destinations.back(); + jump_table_pending_branches.push_back(p); + } + + llvm::BasicBlock* contpoint = create_bb("gosub_continue_point"); + gosub_continue_points.push_back(contpoint); + + builder->SetInsertPoint(contpoint); + + //On return: +#ifndef GOSUB_ON_STACK + llvm::Value* gp = read_global(&GP, pointer_t(gosub_stack_node_type)); + llvm::Value* gp_array = builder->CreateBitCast(gp, llvmType(getInt32PtrTy)); + llvm::Value* count_addr = builder->CreateGEP(gp_array, getInteger(TARGET_BITS, -4)); //((int*)GP)[-4] == ARRAY_count(GP) + llvm::Value* index = builder->CreateAdd(builder->CreateLoad(count_addr), getInteger(32, -1)); + llvm::Value* gosub_stack_node = builder->CreateGEP(gp, to_target_int(index)); + builder->CreateStore(index, count_addr); + + llvm::Value* old_return_point = builder->CreateLoad(create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0)); + builder->CreateStore(old_return_point, gosub_return_point); +#else + + int diff = 1 + end_ctrl - FP->n_local; + llvm::Value* new_gp = builder->CreateGEP(builder->CreateLoad(gp), getInteger(TARGET_BITS, -diff)); + builder->CreateStore(new_gp, gp); + //c_SP(-diff) + builder->CreateStore(new_gp, get_global(&SP, pointer_t(value_type))); + + llvm::Value* old_return_point = read_value(new_gp, T_SHORT); + builder->CreateStore(old_return_point, gosub_return_point); +#endif + + + if (FP->n_ctrl){ +#ifndef GOSUB_ON_STACK + llvm::Value* gp_ctrl_addr = create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 1); + llvm::Value* gp_ctrl = builder->CreateLoad(gp_ctrl_addr); +#else + llvm::Value* gp_ctrl = builder->CreateGEP(new_gp, getInteger(TARGET_BITS, 1)); +#endif + + for(int i=FP->n_local; in_local ? gp_ctrl : builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), get_ctrl_type(i)); + builder->CreateStore(val, locals[i]); + builder->CreateStore(getInteger(32, special_ctrl_type(get_ctrl_type(i))), current_ctrl_types[i - FP->n_local]); + + if (special_ctrl_type(get_ctrl_type(i)) != 0){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))); + store_value(stack_addr, val, get_ctrl_type(i)); + } + } + for(int i=end_ctrl; in_local+FP->n_ctrl; i++){ + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]); + } + +#ifndef GOSUB_ON_STACK + builder->CreateCall(get_global_function(GB.Free, 'v', "p"), builder->CreateBitCast(gp_ctrl_addr, llvmType(getInt8PtrTy))); +#endif + } + +} + +void JumpIfExpression::codegen(){ + PendingBranch p; + p.condition = val->codegen_get_value(); + if (val->on_stack) + c_SP(-1); + p.insert_point = builder->GetInsertBlock(); //Must be after condition codegen! + p.true_addr = jump_if_true ? jump_addr : next_addr; + p.false_addr = jump_if_true ? next_addr : jump_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpFirstExpression::codegen(){ + llvm::Value* to_val = to->codegen_get_value(); + if (to->on_stack) + c_SP(-1); + llvm::Value* step_val = step->codegen_get_value(); + if (step->on_stack) + c_SP(-1); + + //builder->CreateStore(to_val, locals[ctrl_to]); + //builder->CreateStore(step_val, locals[ctrl_to+1]); + set_ctrl(to_val, to->type, ctrl_to); + set_ctrl(step_val, step->type, ctrl_to+1); + llvm::Value* ival = builder->CreateLoad(locals[local_var]); + + //If step is 0, don't execute the loop + llvm::Value* condition; + if (step->type == T_SINGLE) + condition = builder->CreateFCmpUEQ(step_val, getFloat(0.0f)); + else if (step->type == T_FLOAT) + condition = builder->CreateFCmpUEQ(step_val, getFloat(0.0)); + else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + condition = builder->CreateICmpEQ(step_val, getInteger(bits[step->type], 0)); + } + gen_if_noreturn(condition, [&](){ + PendingBranch p; + p.insert_point = builder->GetInsertBlock(); + p.condition = NULL; + p.true_addr = done_addr; + pending_branches.push_back(p); + }, "step_is_zero", "step_not_zero"); + + TYPE to_type = to->type; + TYPE step_type = step->type; + + //Pre-test: + bool is_float = to_type >= T_SINGLE; + llvm::Value *is_positive, *cont_neg, *cont_pos; + if (is_float){ + is_positive = builder->CreateFCmpUGE(step_val, to_type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0)); + cont_neg = builder->CreateFCmpUGE(ival, to_val); + cont_pos = builder->CreateFCmpULE(ival, to_val); + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + is_positive = builder->CreateICmpSGE(step_val, getInteger(bits[step_type], 0)); + if (to_type == T_BYTE){ + cont_neg = builder->CreateICmpUGE(ival, to_val); + cont_pos = builder->CreateICmpULE(ival, to_val); + } else { + cont_neg = builder->CreateICmpSGE(ival, to_val); + cont_pos = builder->CreateICmpSLE(ival, to_val); + } + } + llvm::Value* cont = builder->CreateSelect(is_positive, cont_pos, cont_neg); + + PendingBranch p; + p.insert_point = builder->GetInsertBlock(); + p.condition = cont; + p.true_addr = body_addr; + p.false_addr = done_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpNextExpression::codegen(){ + llvm::Value* to_val = builder->CreateLoad(locals[ctrl_to]); + llvm::Value* step_val = builder->CreateLoad(locals[ctrl_to+1]); + llvm::Value* ival = builder->CreateLoad(locals[local_var]); + + TYPE to_type = get_ctrl_type(ctrl_to); + TYPE step_type = get_ctrl_type(ctrl_to+1); + + //Add step + if (step_type != to_type){ + if (to_type == T_BYTE) + ival = builder->CreateZExt(ival, llvmType(getInt32Ty)); + else + ival = builder->CreateSExt(ival, llvmType(getInt32Ty)); + ival = builder->CreateAdd(ival, step_val, "", false, true /*nsw*/); + ival = builder->CreateTrunc(ival, to_val->getType()); + builder->CreateStore(ival, locals[local_var]); + } else { + if (to_type == T_SINGLE || to_type == T_FLOAT) + ival = builder->CreateFAdd(ival, step_val); + else + ival = builder->CreateAdd(ival, step_val, "", false, true /*nsw*/); + builder->CreateStore(ival, locals[local_var]); + } + + //Test if continue + bool is_float = to_type >= T_SINGLE; + llvm::Value *is_positive, *cont_neg, *cont_pos; + if (is_float){ + is_positive = builder->CreateFCmpUGE(step_val, to_type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0)); + cont_neg = builder->CreateFCmpUGE(ival, to_val); + cont_pos = builder->CreateFCmpULE(ival, to_val); + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + is_positive = builder->CreateICmpSGE(step_val, getInteger(bits[step_type], 0)); + if (to_type == T_BYTE){ + cont_neg = builder->CreateICmpUGE(ival, to_val); + cont_pos = builder->CreateICmpULE(ival, to_val); + } else { + cont_neg = builder->CreateICmpSGE(ival, to_val); + cont_pos = builder->CreateICmpSLE(ival, to_val); + } + } + llvm::Value* cont = builder->CreateSelect(is_positive, cont_pos, cont_neg); + + PendingBranch p; + p.insert_point = builder->GetInsertBlock(); + p.condition = cont; + p.true_addr = body_addr; + p.false_addr = done_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpEnumFirstExpression::codegen(){ + //FIXME fix class enum + if (!TYPE_is_pure_object(obj->type)){ + codegen_pop_ctrl(obj, ctrl); + builder->CreateCall(get_global_function_jif(EXEC_enum_first, 'v', "h"), getInteger(16, ctrl)); + set_ctrl_type(T_OBJECT, ctrl+1); + } else { + llvm::Value* object = obj->codegen_get_value(); + ob = extract_value(object, 1); + + codegen_pop_ctrl(object, obj, ctrl); + + /* Instance variables: + llvm::Value* effective_class; + llvm::Value* obj; + */ + llvm::Value* enum_object; + + CLASS* klass = (CLASS*)(void*)obj->type; + if (klass->is_virtual){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)); + //Ok, so what we should pass to CENUM_create is the pointer to the class (in case of a virtual class is enumerated), or + //pointer to the object (in case of an instance of a virtual class is enumerated). + //As val._class.class is the same memory as val._object.object, we don't need to do anything special here. + enum_object = ob; + //However, when running EXEC_special with SPEC_FIRST, we need to pass null as object (if virtual class), so we replace ob with null + ob = builder->CreateSelect(builder->CreateICmpEQ(builder->CreatePtrToInt(extract_value(object, 0), llvmType(getInt32Ty)), getInteger(32, T_CLASS)), get_nullptr(), ob); + + //old: enum_object = builder->CreateSelect(builder->CreateICmpEQ(ob, get_nullptr()), effective_class, ob); + } else { + effective_class = extract_value(object, 0); + make_nullcheck(ob); + enum_object = ob; + } + + llvm::Value* cenum_obj = builder->CreateCall(get_global_function_jif(CENUM_create, 'p', "p"), enum_object); + + //unref_object(extract_value(builder->CreateLoad(locals[ctrl+1]), 1)); + borrow_object_no_nullcheck(cenum_obj); + + llvm::Value* cenum_object = get_new_struct(object_type, + get_global((void*)GB.FindClass("Enum"), llvmType(getInt8Ty)), cenum_obj); + + set_ctrl(cenum_object, T_OBJECT, ctrl+1); + + builder->CreateStore(read_global((void*)&EXEC_enum), temp_voidptr); + builder->CreateStore(cenum_obj, get_global((void*)&EXEC_enum)); + builder->CreateCall5(get_global_function_jif(EXEC_special, 'c', "ippic"), + getInteger(32, SPEC_FIRST), + effective_class, + ob, + getInteger(32, 0), + getInteger(8, true)); + builder->CreateStore(builder->CreateLoad(temp_voidptr), get_global((void*)&EXEC_enum)); + } +} + +llvm::Value* OnStackExpression::codegen_get_value(){ + return get_top(type); +} + +void JumpEnumNextExpression::codegen(){ + //FIXME class enum + llvm::Value* stop; + if (!TYPE_is_pure_object(jfirst->obj->type)){ + store_pc(pc); + stop = builder->CreateICmpNE(builder->CreateCall(get_global_function_jif(EXEC_enum_next, 'c', "h"), getInteger(16, drop)), getInteger(8, false)); + } else { + llvm::Value* cenum_obj = extract_value(builder->CreateLoad(locals[jfirst->ctrl+1]), 1); + int stop_offset = sizeof(GB_BASE) + sizeof(LIST) + 5*sizeof(void*); + stop = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(cenum_obj, getInteger(TARGET_BITS, stop_offset))), llvmType(getInt1Ty)); + + stop = gen_if_phi(stop, builder->CreateXor(stop, getInteger(1, 1)), [&](){ + builder->CreateStore(read_global((void*)&EXEC_enum), temp_voidptr); + builder->CreateStore(cenum_obj, get_global((void*)&EXEC_enum)); + store_pc(pc); //Used by EXEC_native + llvm::Value* err = builder->CreateCall5(get_global_function_jif(EXEC_special, 'c', "ippic"), + getInteger(32, SPEC_NEXT), + jfirst->effective_class, + jfirst->ob, + getInteger(32, 0), + getInteger(8, false)); + builder->CreateStore(builder->CreateLoad(temp_voidptr), get_global((void*)&EXEC_enum)); + + gen_if_noreturn(builder->CreateICmpNE(err, getInteger(8, false)), [&](){ + create_throw(E_ENUM); + }); + + llvm::Value* stop_after = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(cenum_obj, getInteger(TARGET_BITS, stop_offset))), llvmType(getInt1Ty)); + gen_if(builder->CreateOr(getInteger(1, drop), stop_after), [&](){ + //When enumeration stops, T_VOID can be returned instead of the given type + llvm::Value* t = get_top(type); + llvm::Value* tt = load_element(get_value_on_top_addr(), 0); + if (TYPE_is_object(type)) + tt = builder->CreatePtrToInt(tt, LONG_TYPE); + gen_if(builder->CreateICmpNE(tt, getInteger(TARGET_BITS, T_VOID)), [&](){ + release(t, type); + }); + c_SP(-1); + }); + return stop_after; + }, "enum_next_cont"); + } + + PendingBranch p; + p.condition = stop; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr; + p.false_addr = cont_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void TryExpression::codegen(){ + in_try = true; + has_tries = true; + + try_blocks.push_back(builder->GetInsertBlock()); + + builder->CreateStore(read_sp(), get_global((void*)&EP, pointer_t(value_type))); + //We only want it to be non-null, so let's put in 1: + builder->CreateStore(get_global((void*)1, llvmType(getInt8Ty)), get_global((void*)&EC)); + + llvm::Value* jmpbuf = builder->CreateCall(get_global_function(JR_try, 'p', "p"), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0)); + + llvm::Function* f = llvm::cast(get_global_function(_setjmp, 'i', "p")); +#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2) + f->addFnAttr(llvm::Attributes::ReturnsTwice); +#else + f->addFnAttr(llvm::Attribute::ReturnsTwice); +#endif + + llvm::Value* setjmp_return = builder->CreateCall(f, jmpbuf); + + builder->CreateStore(builder->CreateTrunc(setjmp_return, llvmType(getInt8Ty)), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, offsetof(ERROR_CONTEXT, ret))); + + llvm::Value* second_time = builder->CreateICmpNE(setjmp_return, getInteger(32, 0)); + + gen_if_else(second_time, [&](){ + builder->CreateCall(get_global_function(JR_try_unwind, 'v', "p"), builder->CreateBitCast(builder->CreateLoad(gp), llvmType(getInt8PtrTy))); + builder->CreateStore(getInteger(1, true), temp_got_error); + }, [&](){ + builder->CreateStore(getInteger(1, false), temp_got_error); + }, "Try_cleanup"); + + PendingBranch p; + p.condition = second_time; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr2; + p.false_addr = addr1; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void EndTryExpression::codegen(){ + in_try = false; + + llvm::Value* call = builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0)); + + if (llvm::Instruction* inst = llvm::dyn_cast(call)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("end_try", llvm::MDNode::get(llvm_context, arr)); + } + + builder->CreateStore(get_nullptr(), get_global((void*)&EP)); + //We only need to store NULL (no catch) or something whatever else inside EC (there is a catch) + llvm::Value* got_large_error = builder->CreateZExt(builder->CreateXor(builder->CreateLoad(temp_got_error2), getInteger(1, 1)), LONG_TYPE); + builder->CreateStore(got_large_error, get_global((void*)&EC, LONG_TYPE)); +} + +void LargeTryExpression::codegen(){ + has_tries = true; + + builder->CreateStore(getInteger(1, false), temp_got_error2); + + llvm::Value* jmpbuf = builder->CreateCall(get_global_function(JR_try, 'p', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + + llvm::Function* f = llvm::cast(get_global_function(_setjmp, 'i', "p")); +#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2) + f->addFnAttr(llvm::Attributes::ReturnsTwice); +#else + f->addFnAttr(llvm::Attribute::ReturnsTwice); +#endif + + llvm::Value* setjmp_return = builder->CreateCall(f, jmpbuf); + + builder->CreateStore(builder->CreateTrunc(setjmp_return, llvmType(getInt8Ty)), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, offsetof(ERROR_CONTEXT, ret))); + + llvm::Value* second_time = builder->CreateICmpNE(setjmp_return, getInteger(32, 0)); + + gen_if(second_time, [&](){ + builder->CreateCall(get_global_function(JR_try_unwind, 'v', "p"), builder->CreateBitCast(builder->CreateLoad(gp), llvmType(getInt8PtrTy))); + builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + builder->CreateStore(get_nullptr(), get_global((void*)&EC)); + builder->CreateStore(getInteger(1, true), temp_got_error2); + }, "Try_cleanup"); + + PendingBranch p; + p.condition = second_time; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr2; + p.false_addr = 0; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void LargeCatchExpression::codegen(){ + //Also in ReturnExpression if returning before this + builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + //Differs from original interpreter, error thrown in a Finally block is not caught. + builder->CreateStore(get_nullptr(), get_global((void*)&EC)); +} + +void CatchExpression::codegen(){ + llvm::Value* got_error = builder->CreateLoad(temp_got_error2); + + gen_if_noreturn(builder->CreateXor(got_error, getInteger(1, 1)), [&](){ + store_value(get_global((void*)RP), get_default(FP->type), FP->type); + return_points.push_back(builder->GetInsertBlock()); + }, "do_not_catch", "do_catch"); +} + +llvm::Value* NewExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(type, true); +} +void NewExpression::codegen_on_stack(){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + store_pc(pc); + builder->CreateCall(get_global_function_jif(EXEC_new, 'v', "")); +} + +std::pair BinOpExpression::codegen_operands(){ + llvm::Value* l = left->codegen_get_value(); + if (left->on_stack) + c_SP(-1); + llvm::Value* r = right->codegen_get_value(); + if (right->on_stack) + c_SP(-1); + return std::make_pair(l, r); +} + +llvm::Value* EqExpression::codegen_get_value(){ + llvm::Value* ret; + if ((t >= T_BOOLEAN && t <= T_LONG) || t == T_POINTER){ + auto op = codegen_operands(); + ret = builder->CreateICmpEQ(op.first, op.second); + } else if (t == T_DATE){ + auto op = codegen_operands(); + ret = builder->CreateAnd( + builder->CreateICmpEQ(extract_value(op.first, 0), extract_value(op.second, 0)), + builder->CreateICmpEQ(extract_value(op.first, 1), extract_value(op.second, 1)) + ); + } else if (t == T_NULL){ + //FIXME gör några unit tests på den här biten + if (left->type == T_NULL && right->type == T_NULL){ + ret = getInteger(1, true); + } else { + Expression* testexpr = left->type == T_NULL ? right : left; + llvm::Value* val = testexpr->codegen_get_value(); + switch(testexpr->type){ + case T_STRING: + case T_CSTRING: + ret = builder->CreateICmpEQ(extract_value(val, 3), getInteger(32, 0)); + if (testexpr->type == T_STRING) + release(val, T_STRING); + break; + case T_DATE: + ret = builder->CreateICmpEQ(builder->CreateOr(extract_value(val, 0), extract_value(val, 1)), getInteger(32, 0)); + break; + case T_POINTER: + ret = builder->CreateICmpEQ(val, get_nullptr()); + break; + case T_VARIANT: { + llvm::Value* vtype = extract_value(val, 0); + ret = gen_if_phi(getInteger(1, true), builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + if (TARGET_BITS == 64){ + llvm::Value* date_str_ptr_obj = builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_DATE)), + builder->CreateICmpULE(vtype, getInteger(TARGET_BITS, T_POINTER)) + ), + builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)) + ); + llvm::Value* zero_data = builder->CreateICmpEQ(extract_value(val, 1), getInteger(64, 0)); + return builder->CreateSelect(date_str_ptr_obj, zero_data, getInteger(1, false)); + } else { + llvm::Value* vdata = extract_value(val, 1); + llvm::Value* ptr_is_null = builder->CreateICmpEQ(builder->CreateTrunc(vdata, llvmType(getInt32Ty)), getInteger(32, 0)); + return (llvm::Value*)gen_if_phi(ptr_is_null, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_STRING)), [&](){ + llvm::Value* zero_date = builder->CreateICmpEQ(vdata, getInteger(64, 0)); + return gen_if_phi(zero_date, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_DATE)), [&](){ + return builder->CreateSelect( + builder->CreateOr( + builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_POINTER)), + builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)) + ), + ptr_is_null, getInteger(1, false) + ); + }); + }); + } + }); + gen_if(builder->CreateXor(ret, getInteger(1, 1)), [&](){ + release(val, T_VARIANT); + }); + break; + } + default: + if (TYPE_is_object(testexpr->type)){ + llvm::Value* object = extract_value(val, 1); + ret = builder->CreateICmpEQ(object, get_nullptr()); + unref_object(object); + } else { + ret = getInteger(1, false); + } + break; + } + if (left->on_stack) + c_SP(-1); + if (right->on_stack) + c_SP(-1); + } + } else if (t == T_STRING || t == T_CSTRING){ + auto op = codegen_operands(); + llvm::Value* len1 = extract_value(op.first, 3); + llvm::Value* len2 = extract_value(op.second, 3); + + llvm::Value *ret1, *ret2; + llvm::BasicBlock *BB1, *BB2; + + gen_if_else(builder->CreateICmpNE(len1, len2), [&](){ + ret1 = getInteger(1, false); + BB1 = builder->GetInsertBlock(); + }, [&](){ + llvm::Value* addr1 = extract_value(op.first, 1); + llvm::Value* addr2 = extract_value(op.second, 1); + llvm::Value* offset1 = extract_value(op.first, 2); + llvm::Value* offset2 = extract_value(op.second, 2); + if (TARGET_BITS == 64){ + offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty)); + offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty)); + len1 = builder->CreateZExt(len1, llvmType(getInt64Ty)); + } + addr1 = builder->CreateGEP(addr1, offset1); + addr2 = builder->CreateGEP(addr2, offset2); + ret2 = builder->CreateICmpEQ(builder->CreateCall3(get_global_function(memcmp, 'i', "ppj"), addr1, addr2, len1), getInteger(32, 0)); + BB2 = builder->GetInsertBlock(); + }, "strcomp_not_same_length", "strcomp_same_length", "strcomp_done"); + + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(ret1, BB1); + phi->addIncoming(ret2, BB2); + ret = phi; + + release(op.first, left->type); + release(op.second, right->type); + } else if (t == T_SINGLE || t == T_FLOAT){ + auto op = codegen_operands(); + ret = builder->CreateFCmpUEQ(op.first, op.second); + } else if (t == T_OBJECT){ + llvm::Value* l = left->codegen_get_value(); + llvm::Value* r = right->codegen_get_value(); + llvm::Value* sp = read_sp();//builder->CreateBitCast(read_sp(), pointer_t(value_types[T_OBJECT])); + llvm::Value* laddr = builder->CreateGEP(sp, getInteger(TARGET_BITS, -2));//create_gep(sp, TARGET_BITS, -2, 32, 1); + llvm::Value* raddr = builder->CreateGEP(sp, getInteger(TARGET_BITS, -1));//create_gep(sp, TARGET_BITS, -1, 32, 1); + laddr = create_gep(builder->CreateBitCast(laddr, pointer_t(value_types[T_OBJECT])), TARGET_BITS, 0, 32, 1); + raddr = create_gep(builder->CreateBitCast(raddr, pointer_t(value_types[T_OBJECT])), TARGET_BITS, 0, 32, 1); + + llvm::Value* ret = builder->CreateCall2(get_global_function_jif(COMPARE_object, 'i', "pp"), + builder->CreateBitCast(laddr, llvmType(getInt8PtrTy)), + builder->CreateBitCast(raddr, llvmType(getInt8PtrTy))); + ret = builder->CreateICmpEQ(ret, getInteger(32, 0)); + + unref_object(extract_value(l, 1)); + unref_object(extract_value(r, 1)); + + c_SP(-2); + + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; + } else if (t == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_variant_equal, 'v', "")); + return ret_top_stack(T_BOOLEAN, on_stack); + } + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; +} + +static llvm::Value* is_empty_string(llvm::Value* str){ + //Null test of address should not be needed. Length check should be enough... + llvm::Value* len = extract_value(str, 3); + return builder->CreateICmpEQ(len, getInteger(32, 0)); +} + +llvm::Value* NotExpression::codegen_get_value(){ + if (expr->type == T_NULL){ + if (on_stack) + push_value(getInteger(1, true), T_BOOLEAN); + return getInteger(1, true); + } else if (TYPE_is_variant(expr->type)){ + expr->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_not, 'v', "h"), getInteger(16, 0/*T_VARIANT*/)); + return ret_top_stack(T_VARIANT, true); + } else { + llvm::Value* op = expr->codegen_get_value(); + if (expr->on_stack) + c_SP(-1); + llvm::Value* ret; + if (expr->type <= T_LONG){ + static const int bits[] = {0, 1, 8, 16, 32, 64}; + ret = builder->CreateXor(op, getInteger(bits[expr->type], -1)); + } else if (TYPE_is_string(expr->type)){ + ret = is_empty_string(op); + release(op, expr->type); + } else if (TYPE_is_object(expr->type)){ + ret = builder->CreateICmpEQ(extract_value(op, 1), get_nullptr()); + release(op, expr->type); + } + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; + } +} + +static llvm::Value* LessDate(llvm::Value* date_left, llvm::Value* date_right){ + llvm::Value* d1 = extract_value(date_left, 0); + llvm::Value* d2 = extract_value(date_right, 0); + llvm::Value* t1 = extract_value(date_left, 1); + llvm::Value* t2 = extract_value(date_right, 1); + + llvm::BasicBlock *BB1, *BB2, *BB3, *BB4; + llvm::Value* r3; + + gen_if_else(builder->CreateICmpSLT(d1, d2), [&](){ + BB1 = builder->GetInsertBlock(); //true + }, [&](){ + gen_if_else(builder->CreateICmpSGT(d1, d2), [&](){ + BB2 = builder->GetInsertBlock(); //false + }, [&](){ + r3 = builder->CreateICmpSLT(t1, t2); + BB3 = builder->GetInsertBlock(); //r3 + }); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, false), BB2); + phi->addIncoming(r3, BB3); + r3 = phi; + BB4 = builder->GetInsertBlock(); + }); + + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, true), BB1); + phi->addIncoming(r3, BB4); + return phi; +} + +llvm::Value* LessExpression::codegen_get_value(){ + if (t == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_variant_compi_less_than, 'v', "")); + return ret_top_stack(T_BOOLEAN, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + if (t <= T_LONG || t == T_POINTER){ + ret = builder->CreateICmpSLT(op.first, op.second); + } else if (t == T_DATE){ + ret = LessDate(op.first, op.second); + } else if (t == T_SINGLE || t == T_FLOAT){ + ret = builder->CreateFCmpULT(op.first, op.second); + } /*else if (TYPE_is_object(t)){ + //FIXME the interpreter does not allow this .. + abort(); + } */else if (TYPE_is_string(t)){ + llvm::Value* addr1 = extract_value(op.first, 1); + llvm::Value* addr2 = extract_value(op.second, 1); + llvm::Value* offset1 = extract_value(op.first, 2); + llvm::Value* offset2 = extract_value(op.second, 2); + llvm::Value* len1 = extract_value(op.first, 3); + llvm::Value* len2 = extract_value(op.second, 3); + if (TARGET_BITS == 64){ + offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty)); + offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty)); + } + addr1 = builder->CreateGEP(addr1, offset1); + addr2 = builder->CreateGEP(addr2, offset2); + ret = builder->CreateICmpEQ(builder->CreateCall4(get_global_function_jif(STRING_compare, 'i', "pipi"), addr1, len1, addr2, len2), getInteger(32, -1)); + release(op.first, left->type); + release(op.second, right->type); + } + if (on_stack) + push_value(ret, T_BOOLEAN); + + return ret; + } +} + +llvm::Value* NearExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* ret; + + llvm::Value* len1 = extract_value(op.first, 3); + llvm::Value* len2 = extract_value(op.second, 3); + + llvm::Value *ret1, *ret2; + llvm::BasicBlock *BB1, *BB2; + + gen_if_else(builder->CreateICmpNE(len1, len2), [&](){ + ret1 = getInteger(1, false); + BB1 = builder->GetInsertBlock(); + }, [&](){ + llvm::Value* addr1 = extract_value(op.first, 1); + llvm::Value* addr2 = extract_value(op.second, 1); + llvm::Value* offset1 = extract_value(op.first, 2); + llvm::Value* offset2 = extract_value(op.second, 2); + if (TARGET_BITS == 64){ + offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty)); + offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty)); + } + addr1 = builder->CreateGEP(addr1, offset1); + addr2 = builder->CreateGEP(addr2, offset2); + ret2 = builder->CreateICmpNE(builder->CreateCall3(get_global_function_jif(STRING_equal_ignore_case_same, 'c', "ppi"), addr1, addr2, len1), getInteger(8, 0)); + BB2 = builder->GetInsertBlock(); + }, "strcomp_not_same_length", "strcomp_same_length", "strcomp_done"); + + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(ret1, BB1); + phi->addIncoming(ret2, BB2); + ret = phi; + + release(op.first, left->type); + release(op.second, right->type); + + if (on_stack) + push_value(ret, T_BOOLEAN); + + return ret; +} + +llvm::Value* AddExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_add, 'v', "h"), getInteger(16, 0)); + return ret_top_stack(T_VARIANT, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + + if (type == T_BOOLEAN){ + ret = builder->CreateOr(op.first, op.second); + } else if (type <= T_LONG || type == T_POINTER){ + ret = builder->CreateAdd(op.first, op.second); + } else /*if (type <= T_FLOAT)*/{ + ret = builder->CreateFAdd(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* SubExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_sub, 'v', "h"), getInteger(16, 0)); + return ret_top_stack(T_VARIANT, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + + if (type == T_BOOLEAN){ + ret = builder->CreateXor(op.first, op.second); + } else if (type <= T_LONG || type == T_POINTER){ + ret = builder->CreateSub(op.first, op.second); + } else /*if (type <= T_FLOAT)*/{ + ret = builder->CreateFSub(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* MulExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_mul, 'v', "h"), getInteger(16, 0)); + return ret_top_stack(T_VARIANT, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + + if (type == T_BOOLEAN){ + ret = builder->CreateAnd(op.first, op.second); + } else if (type <= T_LONG || type == T_POINTER){ + ret = builder->CreateMul(op.first, op.second); + } else /*if (type <= T_FLOAT)*/{ + ret = builder->CreateFMul(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* DivExpression::codegen_get_value(){ + auto op = codegen_operands(); + + gen_if_noreturn(builder->CreateFCmpUEQ(op.second, getFloat(0.0)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + + llvm::Value* ret = builder->CreateFDiv(op.first, op.second); + + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* QuoExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* ret; + if (type == T_BOOLEAN){ + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(1, false)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + ret = op.first; + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(bits[type], 0)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + if (type == T_BYTE) + ret = builder->CreateUDiv(op.first, op.second); + else + ret = builder->CreateSDiv(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* RemExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* ret; + if (type == T_BOOLEAN){ + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(1, false)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + ret = op.first; + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(bits[type], 0)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + if (type == T_BYTE) + ret = builder->CreateURem(op.first, op.second); + else + ret = builder->CreateSRem(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PowExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* func; + if (right->type == T_INTEGER){ + llvm::Type* types[] = {llvmType(getDoubleTy)}; + func = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::powi, types); + //func = get_global_function(__powidf2, 'd', "di"); + //func = M->getOrInsertFunction("llvm.powi.f64", get_function_type('d', "di")); + } else { + func = M->getOrInsertFunction("llvm.pow.f64", get_function_type('d', "dd")); + } + llvm::Value* ret = builder->CreateCall2(func, op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* AndExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_AND)); + return ret_top_stack(T_VARIANT, on_stack); + } else { + auto op = codegen_operands(); + llvm::Value* ret = builder->CreateAnd(op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* OrExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_OR)); + return ret_top_stack(T_VARIANT, on_stack); + } else { + auto op = codegen_operands(); + llvm::Value* ret = builder->CreateOr(op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* XorExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_XOR)); + return ret_top_stack(T_VARIANT, on_stack); + } else { + auto op = codegen_operands(); + llvm::Value* ret = builder->CreateXor(op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* IsExpression::codegen_get_value(){ + llvm::Value* obj = left->codegen_get_value(); + if (left->on_stack) + c_SP(-1); + + llvm::Value* addr = extract_value(obj, 1); + + llvm::BasicBlock *BB1, *BB2, *BB3, *BB4; + llvm::Value* ret; + + BB1 = builder->GetInsertBlock(); //false + gen_if(builder->CreateICmpNE(addr, get_nullptr()), [&](){ + llvm::Value* OBJ = builder->CreateBitCast(addr, pointer_t(OBJECT_type)); + llvm::Value* obj_klass = load_element(OBJ, 0); + + PushClassExpression* pce = dyn_cast(right); + assert(pce); + gen_if_else(builder->CreateICmpEQ(builder->CreateIntToPtr(getInteger(TARGET_BITS, (intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy)), obj_klass), [&](){ + BB2 = builder->GetInsertBlock(); //true + }, [&](){ + ret = builder->CreateCall2(get_global_function_jif(CLASS_inherits, 'c', "pp"), obj_klass, builder->CreateIntToPtr(getInteger(TARGET_BITS, (intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy))); + ret = builder->CreateICmpNE(ret, getInteger(8, false)); + BB3 = builder->GetInsertBlock(); + }); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, true), BB2); + phi->addIncoming(ret, BB3); + ret = phi; + + unref_object(addr); + + BB4 = builder->GetInsertBlock(); + }); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, false), BB1); + phi->addIncoming(ret, BB4); + ret = phi; + + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* NegExpression::codegen_get_value(){ + if (type == T_VARIANT){ + llvm::Value* val = expr->codegen_get_value(); + c_SP(-expr->on_stack); + llvm::Value* vtype = extract_value(val, 0); + llvm::Value* data = extract_value(val, 1); + + llvm::BasicBlock* bb[8] = { + create_bb("else"), + create_bb("bool"), + create_bb("byte"), + create_bb("short"), + create_bb("integer"), + create_bb("long"), + create_bb("single"), + create_bb("float") + }; + llvm::BasicBlock* done_block = create_bb("done_neg"); + + llvm::SwitchInst* si = builder->CreateSwitch(vtype, bb[0], 7); + for(int i=1; i<8; i++) + si->addCase(getInteger(TARGET_BITS, i), bb[i]); + + llvm::Value* res[8]; + + builder->SetInsertPoint(bb[1]); //bool + res[1] = data; + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[2]); //byte + res[2] = builder->CreateZExt( + builder->CreateSub(getInteger(8, 0), builder->CreateTrunc(data, llvmType(getInt8Ty))), + llvmType(getInt64Ty) + ); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[3]); //short + res[3] = builder->CreateSExt( + builder->CreateSub(getInteger(16, 0), builder->CreateTrunc(data, llvmType(getInt16Ty))), + llvmType(getInt64Ty) + ); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[4]); //integer + res[4] = builder->CreateSExt( + builder->CreateSub(getInteger(32, 0), builder->CreateTrunc(data, llvmType(getInt32Ty))), + llvmType(getInt64Ty) + ); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[5]); //long + res[5] = builder->CreateSub(getInteger(64, 0), data); + builder->CreateBr(done_block); + + /* %1 = trunc i64 %data to i32 + %2 = bitcast i32 %1 to float + %3 = fsub float -0.000000e+00, %2 + %4 = bitcast float %3 to i32 + %5 = zext i32 %4 to i64 + ret i64 %5 */ + builder->SetInsertPoint(bb[6]); //single + res[6] = builder->CreateFSub(getFloat(0.0f), builder->CreateBitCast(builder->CreateTrunc(data, llvmType(getInt32Ty)), llvmType(getFloatTy))); + res[6] = builder->CreateZExt(builder->CreateBitCast(res[6], llvmType(getInt32Ty)), llvmType(getInt64Ty)); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[7]); //float + res[7] = builder->CreateBitCast(builder->CreateFSub(getFloat(0.0), builder->CreateBitCast(data, llvmType(getDoubleTy))), llvmType(getInt64Ty)); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[0]); //else + release(val, T_VARIANT); + create_throw(E_TYPE, "Number", "(unknown)"); + + builder->SetInsertPoint(done_block); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt64Ty), 7, "variant_neg_result"); + for(int i=1; i<8; i++){ + phi->addIncoming(res[i], bb[i]); + } + + llvm::Value* ret = get_new_struct(variant_type, vtype, phi); + + if (on_stack) + push_value(ret, type); + return ret; + } else { + llvm::Value* val = expr->codegen_get_value(); + if (expr->on_stack) + c_SP(-1); + llvm::Value* ret; + if (type == T_BOOLEAN){ + ret = val; + } else if (type <= T_LONG){ + static const int bits[] = {0, 1, 8, 16, 32, 64}; + ret = builder->CreateSub(getInteger(bits[type], 0), val); + } else { + ret = builder->CreateFSub(type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0), val); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* CatExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_STRING, on_stack); +} +void CatExpression::codegen_on_stack(){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + builder->CreateCall(get_global_function_jif(SUBR_cat, 'v', "h"), getInteger(16, args.size())); +} + +llvm::Value* FileExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_STRING, on_stack); +} +void FileExpression::codegen_on_stack(){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + builder->CreateCall(get_global_function_jif(SUBR_file, 'v', "h"), getInteger(16, args.size())); +} + +llvm::Value* LikeExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_BOOLEAN, on_stack); +} +void LikeExpression::codegen_on_stack(){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_like, 'v', "h"), getInteger(16, kind)); +} + +llvm::Value* CheckStringExpression::codegen_get_value(){ + llvm::Value* val = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(val, 0); + llvm::Value* vdata = extract_value(val, 1); + + llvm::Value* nullstring = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + + llvm::Value* ret = gen_if_phi(nullstring, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + gen_if_noreturn(builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_STRING)), [&](){ + //codegen_printf("okända typen: %ld\n", vtype); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_STRING), "(unknown)"); + }); + llvm::Value* strptr = builder->CreateIntToPtr(vdata, llvmType(getInt8PtrTy)); + llvm::Value* len = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(strptr, llvmType(getInt32PtrTy)), getInteger(TARGET_BITS, -1))); + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), strptr, getInteger(32, 0), len); + }); + + c_SP(-expr->on_stack); + + if (on_stack) + push_value(ret, type); + return ret; +} +llvm::Value* CheckIntegerExpression::codegen_get_value(){ + llvm::Value* ret = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(ret, 0); + llvm::Value* data = extract_value(ret, 1); + llvm::Value* not_ok = builder->CreateICmpUGT(vtype, getInteger(TARGET_BITS, T_INTEGER)); + + c_SP(-expr->on_stack); + + gen_if_noreturn(not_ok, [&](){ + release(ret, T_VARIANT); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), "(unknown)"); + }); + + ret = builder->CreateTrunc(data, llvmType(getInt32Ty)); + if (on_stack) + push_value(ret, T_INTEGER); + return ret; +} +llvm::Value* CheckFloatExpression::codegen_get_value(){ + llvm::Value* ret = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(ret, 0); + llvm::Value* data = extract_value(ret, 1); + llvm::Value* not_ok = builder->CreateICmpUGT(vtype, getInteger(TARGET_BITS, T_FLOAT)); + + c_SP(-expr->on_stack); + + gen_if_noreturn(not_ok, [&](){ + release(ret, T_VARIANT); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), "(unknown)"); + }); + + llvm::Value* low32 = builder->CreateTrunc(data, llvmType(getInt32Ty)); + llvm::Value* float_from_int = builder->CreateSIToFP(low32, llvmType(getDoubleTy)); + + ret = gen_if_phi(float_from_int, builder->CreateICmpSGT(vtype, getInteger(TARGET_BITS, T_INTEGER)), [&](){ + llvm::Value* float_data = builder->CreateBitCast(data, llvmType(getDoubleTy)); + llvm::Value* single_data = builder->CreateFPExt(builder->CreateBitCast(low32, llvmType(getFloatTy)), llvmType(getDoubleTy)); + llvm::Value* long_data = builder->CreateSIToFP(data, llvmType(getDoubleTy)); + return builder->CreateSelect( + builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_FLOAT)), + float_data, + builder->CreateSelect(builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_SINGLE)), single_data, long_data)); + }); + + if (on_stack) + push_value(ret, T_FLOAT); + return ret; +} +llvm::Value* CheckPointerExpression::codegen_get_value(){ + llvm::Value* ret = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(ret, 0); + llvm::Value* data = extract_value(ret, 1); + llvm::Value* not_ok = builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_POINTER)); + + c_SP(-expr->on_stack); + + gen_if_noreturn(not_ok, [&](){ + release(ret, T_VARIANT); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_POINTER), "(unknown)"); + }); + + ret = builder->CreateIntToPtr(data, llvmType(getInt8PtrTy)); + if (on_stack) + push_value(ret, T_POINTER); + return ret; +} + +static llvm::Value* gen_max(llvm::Value* val1, llvm::Value* val2){ + return builder->CreateSelect(builder->CreateICmpSLT(val1, val2), val2, val1); +} + +static llvm::Value* gen_min(llvm::Value* val1, llvm::Value* val2){ + return builder->CreateSelect(builder->CreateICmpSLT(val1, val2), val1, val2); +} + +static llvm::Value* gen_minmax(llvm::Value* val, llvm::Value* lo, llvm::Value* hi){ + llvm::BasicBlock* BB1 = builder->GetInsertBlock(); + llvm::BasicBlock* BB2 = create_bb("minmax1"); + llvm::BasicBlock* BB3 = create_bb("minmax2"); + llvm::Value* cmp = builder->CreateICmpSLT(val, lo); + builder->CreateCondBr(cmp, BB3, BB2); + + builder->SetInsertPoint(BB2); + llvm::Value* cmp2 = builder->CreateICmpSGT(val, hi); + llvm::Value* r1 = builder->CreateSelect(cmp2, hi, val); + builder->CreateBr(BB3); + + builder->SetInsertPoint(BB3); + llvm::PHINode* phi = builder->CreatePHI(val->getType(), 2); + phi->addIncoming(lo, BB1); + phi->addIncoming(r1, BB2); + + return phi; +} + +static llvm::Value* get_string(llvm::Value* str){ + llvm::Value* ptr = extract_value(str, 1); + return builder->CreateGEP(ptr, to_target_int(extract_value(str, 2))); +} + +static std::pair get_string_len(llvm::Value* str){ + llvm::Value* ptr = extract_value(str, 1); + ptr = builder->CreateGEP(ptr, to_target_int(extract_value(str, 2))); + return std::pair(ptr, extract_value(str, 3)); +} + +static llvm::Value* codegen_spec_method(CLASS_DESC* desc, int index, bool dispatch, bool can_quick, bool object_on_stack, CLASS* klass, llvm::Value* effective_class, llvm::Value* obj, int nargs, bool noret){ + llvm::Value* is_native; + llvm::Value* desc_method; + llvm::Value* desc_exec; + llvm::Value* desc_class; + if (dispatch){ + int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4; + int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8; + int offset_native_flag = TARGET_BITS == 64 ? 35 : 19; + //table_addr = (char*)effective_class->table + llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table))); + //desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset) + llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol)); + //desc_addr = *(char**)desc_addr_addr + llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy)))); + //native_flag_addr = (char*)(desc_addr + offset_native_flag) + llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag)); + //native_subr_flag = *native_flag_addr + llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr); + //native_flag = (bool)(native_subr_flag & 1) + llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty)); + //subr_flag = (bool)(native_subr_flag & 2) + //llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0)); + + //llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC)); + ///Disable subr, because I think it will never be used this way anyway, else it is a (big) performance hit. + is_native = native_flag; + desc_method = desc_addr; + desc_exec = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offsetof(CLASS_DESC_METHOD, exec))), charPP)); + desc_class = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offsetof(CLASS_DESC_METHOD, klass))), charPP)); + + if (!desc->method.native){ + //Assume native classes don't inherit non-native ones :) + is_native = getInteger(1, false); + } + } else { + is_native = getInteger(1, desc->method.native); + desc_method = get_global((void*)&desc->method, llvmType(getInt8Ty)); + desc_exec = get_global((void*)desc->method.exec, llvmType(getInt8Ty)); + desc_class = get_global((void*)desc->method.klass, llvmType(getInt8Ty)); + } + + llvm::Value* dummy = getInteger(32, 0); + + llvm::Value* ret = gen_if_else_phi(is_native, [&](){ + if (can_quick){ + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + desc_exec, + obj, + getInteger(TARGET_BITS, desc->method.type), + builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -nargs)), llvmType(getInt8PtrTy))); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = dummy; + if (!noret){ + ret = read_value(get_global(&TEMP, value_type), desc->method.type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(&TEMP, LONG_TYPE)); + borrow(ret, desc->method.type); + } + + if (nargs > 0){ + builder->CreateCall2(get_global_function_jif(RELEASE_many, 'v', "pi"), + builder->CreateBitCast(read_sp(), llvmType(getInt8PtrTy)), getInteger(32, nargs)); + c_SP(-nargs); + } + unref_object(obj); + if (object_on_stack) + c_SP(-1); + if (!noret) + push_value(ret, desc->method.type); + return ret; + } else { + builder->CreateStore(desc_class, get_global((void*)&EXEC.klass)); + builder->CreateStore(obj, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, nargs), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + + builder->CreateStore(desc_method, get_global((void*)&EXEC.desc)); + builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + + builder->CreateCall(get_global_function_jif(EXEC_native, 'v' , "")); + + llvm::Value* ret = dummy; + if (!noret) + ret = read_value(get_value_on_top_addr(), desc->method.type); + + unref_object(obj); + if (object_on_stack){ + c_SP(-1); + if (!noret) + store_value(get_value_on_top_addr(), ret, desc->method.type); + } + if (noret) + c_SP(-1); + return ret; + } + }, [&](){ + builder->CreateStore(desc_class, get_global((void*)&EXEC.klass)); + builder->CreateStore(obj, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, nargs), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(builder->CreatePtrToInt(desc_exec, llvmType(getInt32Ty)), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 5));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + + llvm::Value* ret = dummy; + if (!noret){ + ret = read_value(get_global(RP, value_type), desc->method.type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE)); + } + unref_object(obj); + if (object_on_stack) + c_SP(-1); + if (!noret) + push_value(ret, desc->method.type); + return ret; + }, "spec_native", "spec_non_native", "spec_done"); + return ret; +} + +struct InlineArrayGetAddrRet { + llvm::Value* element_addr; + CLASS* klass; + CLASS* struct_class; +}; +InlineArrayGetAddrRet inline_array_get_addr(std::vector& args, bool is_push_dynamic){ + CTYPE* ctype; + CLASS* klass; + llvm::Value* array_start; + if (is_push_dynamic){ + ctype = &CP->load->dyn[((PushDynamicExpression*)args[0])->index].type; + klass = CP; + array_start = builder->CreateGEP(current_op, getInteger(TARGET_BITS, ((PushDynamicExpression*)args[0])->offset)); + } else { + ctype = ((ReadVariableExpression*)args[0])->ctype; + klass = ((ReadVariableExpression*)args[0])->klass; + array_start = get_global((void*)((PushStaticExpression*)args[0])->addr, llvmType(getInt8Ty)); + } + + CLASS_ARRAY* ca = klass->load->array[ctype->value]; + int* dim = ca->dim; + int ndim = args.size()-1; + + int dims[ndim]; + memcpy(dims, dim, ndim*sizeof(int)); + dims[ndim-1] = -dims[ndim-1]; + + llvm::Value* index = getInteger(32, 0); + + for(int i=0; icodegen_get_value(); + gen_if_noreturn(builder->CreateICmpUGE(d, getInteger(32, dims[i])), [&](){ + create_throw(E_BOUND); + }); + if (i != 0) + index = builder->CreateMul(index, getInteger(32, dims[i])); + index = builder->CreateAdd(index, d); + + c_SP(-args[i+1]->on_stack); + } + + CLASS* struct_class = NULL; + int element_size; + if (ca->ctype.id == TC_STRUCT){ + //Array of structs + struct_class = klass->load->class_ref[ca->ctype.value]; + element_size = struct_class->size - sizeof(CSTRUCT); + } else { + element_size = TYPE_sizeof_memory(ctype_to_type(&ca->ctype, klass)); + } + + return { + builder->CreateGEP(array_start, to_target_int(builder->CreateMul(index, getInteger(32, element_size)))), + klass, + struct_class + }; +} + +llvm::Value* PushArrayExpression::codegen_get_value(){ + int nargs = args.size(); + + CLASS* klass; + bool is_push_class; + + bool is_push_dynamic = false; + + if ( ((is_push_dynamic = isa(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + auto ret = inline_array_get_addr(args, is_push_dynamic); + + llvm::Value* val; + + if (ret.struct_class){ + //Array of structs + llvm::Value* ref_obj; + if (is_push_dynamic) + ref_obj = current_op; + else + ref_obj = get_global((void*)((ReadVariableExpression*)args[0])->klass, llvmType(getInt8Ty)); + + val = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + ref_obj, + get_global((void*)ret.struct_class, llvmType(getInt8Ty)), + ret.element_addr); + + borrow_object_no_nullcheck(val); + + val = get_new_struct(object_type, get_global((void*)ret.struct_class, llvmType(getInt8Ty)), val); + } else { + val = array_read(ret.element_addr, type); + } + if (on_stack) + push_value(val, type); + return val; + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* data; + if (args.size() == 2){ + llvm::Value* index = args[1]->codegen_get_value(); + + //We do not check that the dimensions are correct here ... :) + int element_size = TYPE_sizeof_memory(type); + int data_offset = offsetof(CARRAY, data); + int count_offset = offsetof(CARRAY, count); + + make_nullcheck(obj); + + c_SP(-args[0]->on_stack-args[1]->on_stack); + + llvm::Value* count = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, count_offset)), llvmType(getInt32PtrTy))); + data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, data_offset)), charPP)); + + gen_if_noreturn(builder->CreateICmpUGE(index, count), [&](){ + unref_object_no_nullcheck(obj); + create_throw(E_BOUND); + }, "array_get_out_of_bounds"); + + data = builder->CreateGEP(data, to_target_int(builder->CreateMul(index, getInteger(32, element_size)))); + } else { + args[1]->codegen_on_stack(); + llvm::Value* ind_addr = get_value_on_top_addr(); + for(int i=2; icodegen_on_stack(); + + make_nullcheck(obj); + + c_SP(-args[0]->on_stack-(nargs-1)); + + data = builder->CreateCall3(get_global_function_jif(CARRAY_get_data_multi, 'p', "ppi"), + obj, builder->CreateBitCast(ind_addr, llvmType(getInt8PtrTy)), getInteger(32, nargs-1)); + + gen_if_noreturn(builder->CreateICmpEQ(data, get_nullptr()), [&](){ + unref_object_no_nullcheck(obj); + + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + } + data = array_read(data, type); + + unref_object_no_nullcheck(obj); + + if (on_stack) + push_value(data, type); + return data; + } else if (klass->quick_array == CQA_COLLECTION){ + llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* key = args[1]->codegen_get_value(); + auto str = get_string_len(key); + + make_nullcheck(obj, [&](){ + if (!args[1]->on_stack) + release(key, T_STRING); + }); + + llvm::Value* sp = builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -args[0]->on_stack-args[1]->on_stack)); + llvm::Value* sp_int8 = builder->CreateBitCast(sp, llvmType(getInt8PtrTy)); + + /*llvm::Value* is_null =*/ builder->CreateCall4(get_global_function(GB.Collection.Get, 'c', "ppip"), + obj, str.first, str.second, sp_int8); + + //type is always T_VARIANT, not T_NULL + llvm::Value* ret = builder->CreateBitCast(sp, pointer_t(value_types[T_VARIANT])); + ret = get_new_struct(variant_type, load_element(ret, 1), load_element(ret, 2)); + + borrow_variant(ret); + + store_sp(builder->CreateGEP(sp, getInteger(TARGET_BITS, 1))); + + release(key, T_STRING); + unref_object_no_nullcheck(obj); + + return ret; + } + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_GET_METHOD; + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + //llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + klass = pce->klass; + is_push_class = true; + goto _SPEC_GET_METHOD; + } else { + for(size_t i=0, e=args.size(); i!=e; i++) + args[i]->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(EXEC_push_array, 'v', "h"), getInteger(16, *pc)); + return ret_top_stack(T_VARIANT, on_stack); + } + + _SPEC_GET_METHOD: { + int index = klass->special[SPEC_GET]; + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + llvm::Value* obj = NULL; + llvm::Value* effective_class = NULL; + bool dispatch; + + if (!is_push_class){ + llvm::Value* object = args[0]->codegen_get_value(); + obj = extract_value(object, 1); + + if (!klass->is_virtual && !isa(args[0])){ + make_nullcheck(obj); + + effective_class = load_element(builder->CreateBitCast(obj, pointer_t(OBJECT_type)), 0); + create_check(klass, effective_class, obj); + dispatch = true; + } else { + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + } else { + obj = get_nullptr(); + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + + for(size_t i=1; icodegen_on_stack(); + + return codegen_spec_method(desc, index, dispatch, can_quick, !is_push_class, klass, effective_class, obj, args.size()-1, false); + } +} + +void PopArrayExpression::codegen(){ + int nargs = args.size(); + + CLASS* klass; + bool is_push_class; + bool is_push_dynamic = false; + + if ( ((is_push_dynamic = isa(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + + llvm::Value* addr = inline_array_get_addr(args, is_push_dynamic).element_addr; + llvm::Value* new_val = val->codegen_get_value(); + + release_variable(type, addr); + variable_write(addr, new_val, type); + + c_SP(-val->on_stack); //FIXME what if destructor in release_variable throws an error? + + return; + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + llvm::Value* new_val = val->codegen_get_value(); + + llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* data; + if (args.size() == 2){ + llvm::Value* index = args[1]->codegen_get_value(); + + //We do not check that the dimensions are correct here ... :) + int element_size = TYPE_sizeof_memory(type); + int data_offset = offsetof(CARRAY, data); + int count_offset = offsetof(CARRAY, count); + + make_nullcheck(obj, [&](){ + release(new_val, val->type); + }); + + c_SP(-val->on_stack-args[0]->on_stack-args[1]->on_stack); //FIXME what if destructor in release_variable throws an error? + + llvm::Value* count = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, count_offset)), llvmType(getInt32PtrTy))); + data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, data_offset)), charPP)); + + gen_if_noreturn(builder->CreateICmpUGE(index, count), [&](){ + release(new_val, val->type); + unref_object_no_nullcheck(obj); + create_throw(E_BOUND); + }, "array_get_out_of_bounds"); + + data = builder->CreateGEP(data, to_target_int(builder->CreateMul(index, getInteger(32, element_size)))); + } else { + args[1]->codegen_on_stack(); + llvm::Value* ind_addr = get_value_on_top_addr(); + for(int i=2; icodegen_on_stack(); + + make_nullcheck(obj, [&](){ + if (!val->on_stack) + release(new_val, val->type); + }); + + c_SP(-val->on_stack-args[0]->on_stack-(nargs-1)); + + data = builder->CreateCall3(get_global_function_jif(CARRAY_get_data_multi, 'p', "ppi"), + obj, builder->CreateBitCast(ind_addr, llvmType(getInt8PtrTy)), getInteger(32, nargs-1)); + + gen_if_noreturn(builder->CreateICmpEQ(data, get_nullptr()), [&](){ + release(new_val, val->type); + unref_object_no_nullcheck(obj); + + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + } + release_variable(type, data); + variable_write(data, new_val, type); + + unref_object_no_nullcheck(obj); + + return; + } else if (klass->quick_array == CQA_COLLECTION){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* object = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* key = args[1]->codegen_get_value(); + + c_SP(-1-args[0]->on_stack-args[1]->on_stack); + + make_nullcheck(object, [&](){ + release(key, T_STRING); + }); + + gen_if_noreturn(builder->CreateICmpEQ(extract_value(key, 3), getInteger(32, 0)), [&](){ + release(key, T_STRING); + unref_object_no_nullcheck(object); + create_throw(E_VKEY); + }); + + auto str = get_string_len(key); + + builder->CreateCall4(get_global_function(GB.Collection.Set, 'c', "ppip"), + object, str.first, str.second, builder->CreateBitCast(read_sp(), llvmType(getInt8PtrTy))); + + release(value, T_VARIANT); + release(key, T_STRING); + unref_object_no_nullcheck(object); + return; + } + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_PUT_METHOD; + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + klass = pce->klass; + is_push_class = true; + goto _SPEC_PUT_METHOD; + } else { + val->codegen_on_stack(); + for(size_t i=0, e=args.size(); i!=e; i++) + args[i]->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(EXEC_pop_array, 'v', "h"), getInteger(16, *pc)); + return; + } + + _SPEC_PUT_METHOD: { + int index = klass->special[SPEC_PUT]; + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + llvm::Value* obj = NULL; + llvm::Value* effective_class = NULL; + bool dispatch; + + if (!is_push_class){ + llvm::Value* object = args[0]->codegen_get_value(); + obj = extract_value(object, 1); + + if (!klass->is_virtual && !isa(args[0])){ + make_nullcheck(obj); + + effective_class = load_element(builder->CreateBitCast(obj, pointer_t(OBJECT_type)), 0); + create_check(klass, effective_class, obj); + dispatch = true; + } else { + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + } else { + obj = get_nullptr(); + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + + val->codegen_on_stack(); + for(size_t i=1; icodegen_on_stack(); + + codegen_spec_method(desc, index, dispatch, can_quick, !is_push_class, klass, effective_class, obj, args.size(), true); + } +} + +#include "jit_codegen_conv.h" + +#define codegen_stack for(size_t i=0, e=args.size(); i!=e; i++){ args[i]->must_on_stack(); args[i]->codegen_on_stack(); } stack_diff -= args.size(); +#define codegen_value for(size_t i=0, e=args.size(); i!=e; i++){ param[i] = args[i]->codegen_get_value(); stack_diff -= args[i]->on_stack; } + +#define SUBR_CODE(func) on_stack = true; builder->CreateCall(get_global_function_jif(func, 'v', "h"), getInteger(16, (digit << 8) | extra)); break; +#define SUBR(func) on_stack = true; builder->CreateCall(get_global_function_jif(func, 'v', "")); break; + +static llvm::Value* create_phi(llvm::Value* v1, llvm::BasicBlock* BB1, llvm::Value* v2, llvm::BasicBlock* BB2){ + llvm::PHINode* phi = builder->CreatePHI(v1->getType(), 2); + phi->addIncoming(v1, BB1); + phi->addIncoming(v2, BB2); + return phi; +} + +llvm::Value* SubrExpression::codegen_get_value(){ + int nargs = args.size(); + llvm::Value* param[nargs]; + + int stack_diff = on_stack; + /*for(int i=0; ion_stack;*/ + + switch(digit){ + case 0x40: { //Left + codegen_value + llvm::Value* string_length = extract_value(param[0], 3); + llvm::Value* val = nargs == 1 ? getInteger(32, 1) : param[1]; + llvm::Value* backwards = builder->CreateAdd(val, string_length); + val = builder->CreateSelect(builder->CreateICmpSLT(val, getInteger(32, 0)), backwards, val); + val = gen_minmax(val, getInteger(32, 0), string_length); + param[0] = insert_value(param[0], val, 3); + c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack))); + if (on_stack) + push_value(param[0], args[0]->type); + return param[0]; + } + case 0x42: { //Right + codegen_value + llvm::Value* string_offset = extract_value(param[0], 2); + llvm::Value* string_length = extract_value(param[0], 3); + llvm::Value* val = nargs == 1 ? getInteger(32, 1) : param[1]; + llvm::Value* backwards = builder->CreateAdd(val, string_length); + val = builder->CreateSelect(builder->CreateICmpSLT(val, getInteger(32, 0)), backwards, val); + llvm::Value* len = gen_minmax(val, getInteger(32, 0), string_length); + val = builder->CreateSub(string_length, len); + val = builder->CreateAdd(string_offset, val); + param[0] = insert_value(insert_value(param[0], val, 2), len, 3); + c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack))); + if (on_stack) + push_value(param[0], args[0]->type); + return param[0]; + } + case 0x41: { //Mid + codegen_value + llvm::Value* start = builder->CreateSub(param[1], getInteger(32, 1)); + gen_if_noreturn(builder->CreateICmpSLT(start, getInteger(32, 0)), [&](){ + create_throw(E_ARG); + }); + llvm::Value* string_length = extract_value(param[0], 3); + + llvm::BasicBlock* BB1 = create_bb("Mid_return_empty_string"); + llvm::BasicBlock* BB2 = create_bb("cont1"); + llvm::BasicBlock* BB3 = create_bb("cont2"); + llvm::BasicBlock* BB4 = create_bb("cont3"); + + builder->CreateCondBr(builder->CreateICmpSGE(start, string_length), BB1, BB2); + builder->SetInsertPoint(BB2); + + llvm::Value* newlen = nargs == 2 ? string_length : param[2]; + + llvm::Value* string_length_minus_start = builder->CreateSub(string_length, start); + + llvm::Value* newlen_if_negative = gen_max(getInteger(32, 0), + builder->CreateAdd(string_length_minus_start, newlen)); + newlen = builder->CreateSelect(builder->CreateICmpSLT(newlen, getInteger(32, 0)), + newlen_if_negative, newlen); + + newlen = gen_minmax(newlen, getInteger(32, 0), string_length_minus_start); + + builder->CreateCondBr(builder->CreateICmpEQ(newlen, getInteger(32, 0)), BB1, BB3); + builder->SetInsertPoint(BB3); + + llvm::Value* ret1 = insert_value(insert_value(param[0], builder->CreateAdd(extract_value(param[0], 2), start), 2), newlen, 3); + + builder->CreateBr(BB4); + builder->SetInsertPoint(BB1); + release(param[0], args[0]->type); + llvm::BasicBlock* BB5 = builder->GetInsertBlock(); + llvm::Value* ret2 = get_voidstring(); + builder->CreateBr(BB4); + + builder->SetInsertPoint(BB4); + param[0] = create_phi(ret1, BB3, ret2, BB5); + c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack) + (nargs == 3 && args[2]->on_stack))); + if (on_stack) + push_value(param[0], T_STRING); + return param[0]; + } + case 0x43: { //Len + codegen_value + llvm::Value* string_length = extract_value(param[0], 3); + release(param[0], args[0]->type); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(string_length, T_INTEGER); + return string_length; + } + case 0x44: { //Space + codegen_value + gen_if_noreturn(builder->CreateICmpSLT(param[0], getInteger(32, 0)), [&](){ + create_throw(E_ARG); + }); + llvm::Value* ret = gen_if_else_phi(builder->CreateICmpEQ(param[0], getInteger(32, 0)), [&](){ + return get_voidstring(); + }, [&](){ + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(temp_value, llvmType(getInt8PtrTy)), get_nullptr(), param[0]); + + llvm::Value* str = builder->CreateLoad(builder->CreateBitCast(temp_value, pointer_t(string_type))); + if (TARGET_BITS == 64) + param[0] = builder->CreateZExt(param[0], llvmType(getInt64Ty)); + + llvm::Value* ptr = extract_value(str, 1); + //llvm::Function* memset_func = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::memset, string_to_type_vector("pj")); + const char* memset_name = TARGET_BITS == 64 ? "llvm.memset.p0i8.i64" : "llvm.memset.p0i8.i32"; + llvm::Function* memset_func = llvm::cast(M->getOrInsertFunction(memset_name, get_function_type('v', "pcjib", false))); + builder->CreateCall5(memset_func, ptr, getInteger(8, ' '), param[0], getInteger(32, 0), getInteger(1, false)); + borrow_string_no_nullcheck(ptr); + return str; + }); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(ret, T_STRING); + return ret; + } + case 0x45: //String + codegen_stack + SUBR(SUBR_string) + + case 0x46: { //Trim + codegen_value + bool left = extra == 0 || extra == 1; + bool right = extra == 0 || extra == 2; + llvm::Value* string_ptr = extract_value(param[0], 1); + llvm::Value* string_offset = extract_value(param[0], 2); + llvm::Value* string_length = extract_value(param[0], 3); + + string_ptr = builder->CreateGEP(string_ptr, to_target_int(string_offset)); + + if (left){ + llvm::Value* enter_loop = gen_and_if( + builder->CreateICmpSGT(string_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE(builder->CreateLoad(string_ptr), getInteger(8, ' '));} + ); + llvm::BasicBlock *from_block = builder->GetInsertBlock(), *loop_block; + llvm::Value *ret_offset, *ret_length, *ret_ptr; + + gen_while(enter_loop, [&](){ + llvm::PHINode* phi_offset = builder->CreatePHI(llvmType(getInt32Ty), 2); + llvm::PHINode* phi_length = builder->CreatePHI(llvmType(getInt32Ty), 2); + llvm::PHINode* phi_ptr = builder->CreatePHI(llvmType(getInt8PtrTy), 2); + ret_offset = builder->CreateAdd(phi_offset, getInteger(32, 1)); + ret_length = builder->CreateSub(phi_length, getInteger(32, 1)); + ret_ptr = builder->CreateGEP(phi_ptr, getInteger(TARGET_BITS, 1)); + + llvm::Value* ret = gen_and_if( + builder->CreateICmpSGT(ret_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE(builder->CreateLoad(ret_ptr), getInteger(8, ' ')); } + ); + loop_block = builder->GetInsertBlock(); + phi_offset->addIncoming(string_offset, from_block); + phi_offset->addIncoming(ret_offset, loop_block); + phi_length->addIncoming(string_length, from_block); + phi_length->addIncoming(ret_length, loop_block); + phi_ptr->addIncoming(string_ptr, from_block); + phi_ptr->addIncoming(ret_ptr, loop_block); + return ret; + }); + string_ptr = create_phi(string_ptr, from_block, ret_ptr, loop_block); + string_offset = create_phi(string_offset, from_block, ret_offset, loop_block); + string_length = create_phi(string_length, from_block, ret_length, loop_block); + } + if (right){ + llvm::Value* enter_loop = gen_and_if( + builder->CreateICmpSGT(string_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE( + builder->CreateLoad( + builder->CreateGEP( + string_ptr, + to_target_int(builder->CreateSub(string_length, getInteger(32, 1))) + ) + ), getInteger(8, ' ') + );} + ); + llvm::BasicBlock *from_block = builder->GetInsertBlock(), *loop_block; + llvm::Value* ret_length; + gen_while(enter_loop, [&](){ + llvm::PHINode* phi_length = builder->CreatePHI(llvmType(getInt32Ty), 2); + ret_length = builder->CreateSub(phi_length, getInteger(32, 1)); + + llvm::Value* ret = gen_and_if( + builder->CreateICmpSGT(ret_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE( + builder->CreateLoad( + builder->CreateGEP( + string_ptr, + to_target_int(builder->CreateSub(ret_length, getInteger(32, 1))) + ) + ), getInteger(8, ' ') + );} + ); + loop_block = builder->GetInsertBlock(); + phi_length->addIncoming(string_length, from_block); + phi_length->addIncoming(ret_length, loop_block); + return ret; + }); + string_length = create_phi(string_length, from_block, ret_length, loop_block); + } + llvm::Value* ret = insert_value(insert_value(param[0], string_offset, 2), string_length, 3); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(ret, args[0]->type); + return ret; + } + case 0x47: + case 0x48: { //UCase, LCase + codegen_stack + SUBR_CODE(SUBR_upper) + } + + case 0x49: { //Chr + codegen_value + gen_if_noreturn(builder->CreateICmpUGT(param[0], getInteger(32, 255)), [&](){ + create_throw(E_ARG); + }, "Chr_out_of_range", "Chr_ok"); + llvm::Value* arr = get_global((void*)STRING_char_string, llvmType(getInt8Ty)); + param[0] = builder->CreateMul(param[0], getInteger(32, 2)); + if (TARGET_BITS == 64) + param[0] = builder->CreateZExt(param[0], llvmType(getInt64Ty)); + llvm::Value* retaddr = builder->CreateGEP(arr, param[0]); + llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), retaddr, getInteger(32, 0), getInteger(32, 1)); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(ret, T_CSTRING); + return ret; + } + + case 0x4A: { //Asc + codegen_value + llvm::Value* pos = nargs == 2 ? builder->CreateSub(param[1], getInteger(32, 1)) : getInteger(32, 0); + llvm::Value* strptr = extract_value(param[0], 1); + llvm::Value* ret = gen_if_else_phi(builder->CreateOr(builder->CreateICmpSLT(pos, getInteger(32, 0)), builder->CreateICmpSGE(pos, extract_value(param[0], 3))), [&](){ + return getInteger(32, 0); + }, [&](){ + pos = builder->CreateAdd(pos, extract_value(param[0], 2)); + llvm::Value* target_type_pos = TARGET_BITS == 64 ? builder->CreateZExt(pos, llvmType(getInt64Ty)) : pos; + llvm::Value* addr = builder->CreateGEP(strptr, target_type_pos); + return builder->CreateZExt(builder->CreateLoad(addr), llvmType(getInt32Ty)); + }, "empty_string", "not_empty_string", "Asc_done"); + release(param[0], args[0]->type); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_INTEGER); + return ret; + } + + case 0x4B: //InStr + case 0x4C: //RInStr + codegen_stack + SUBR_CODE(SUBR_instr) + + case 0x4D: //Subst + codegen_stack + SUBR_CODE(SUBR_subst) + + case 0x4E: //Replace + codegen_stack + SUBR_CODE(SUBR_replace) + + case 0x4F: //Split + codegen_stack + SUBR_CODE(SUBR_split) + + case 0x50: { //Scan + codegen_value + auto str = get_string_len(param[0]); + auto pat = get_string_len(param[1]); + + llvm::Value* arr = builder->CreateCall4(get_global_function_jif(OBJECT_create, 'p', "pppi"), get_global((void*)GB.FindClass("String[]"), llvmType(getInt8Ty)), get_nullptr(), get_nullptr(), getInteger(32, 0)); + + gen_if(builder->CreateAnd( + builder->CreateICmpNE(str.second, getInteger(32, 0)), + builder->CreateICmpNE(pat.second, getInteger(32, 0))), [&](){ + builder->CreateCall5(get_global_function_jif(REGEXP_scan, 'c', "ppipi"), arr, pat.first, pat.second, str.first, str.second); + }, "scan_ok_params", "scan_empty_params"); + + llvm::Value* ret = get_new_struct(object_type, get_global((void*)GB.FindClass("String[]"), llvmType(getInt8Ty)), arr); + borrow_object_no_nullcheck(arr); + release(param[0], args[0]->type); + release(param[1], args[1]->type); + + c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, (TYPE)(void*)GB.FindClass("String[]")); + return ret; + } + + case 0x51: //Comp + codegen_stack + SUBR_CODE(SUBR_strcomp) + + case 0x52: { //Conv + codegen_value + auto str = get_string_len(param[0]); + llvm::Value* src = get_string(param[1]); + llvm::Value* dst = get_string(param[2]); + + llvm::Value* llvm_args[6] = {builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)), str.first, str.second, src, dst, getInteger(8, false)}; + llvm::Value* conv_error = builder->CreateCall(get_global_function_jif(STRING_conv, 'i', "ppippc"), llvm_args); + release(param[0], args[0]->type); + release(param[1], args[1]->type); + release(param[2], args[2]->type); + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack+on_stack); + + gen_if_noreturn(builder->CreateICmpNE(conv_error, getInteger(32, 0)), [&](){ + create_throw(conv_error); + }, "iconv_error", "iconv_ok"); + + llvm::Value* result = builder->CreateLoad(temp_voidptr); + + llvm::Value* ret = gen_if_else_phi(builder->CreateICmpEQ(result, get_nullptr()), [&](){ + return get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + }, [&](){ + borrow_string_no_nullcheck(result); //FIXME no nullcheck ok? + llvm::Value* len = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(result, getInteger(TARGET_BITS, -4)), llvmType(getInt32PtrTy))); + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), result, getInteger(32, 0), len); + }); + if (on_stack) + set_top_value(ret, T_STRING); + return ret; + } + + case 0x53: { //DConv, SConv + /*auto str = get_string_len(param[0]); + llvm::Value* src = builder->CreateIntToPtr(getInteger(TARGET_BITS, -2), llvmType(getInt8PtrTy)); + llvm::Value* dst = get_global(;*/ + codegen_stack + SUBR_CODE(SUBR_sconv) + } + + case 0x54: //Abs + if (type != T_VARIANT){ + codegen_value + llvm::Value* ret; + switch(type){ + #define ab(bits) ret = builder->CreateSelect(builder->CreateICmpSLT(param[0], getInteger(bits, 0)), builder->CreateSub(getInteger(bits, 0), param[0]), param[0]); break; + #define fab(zero) ret = builder->CreateSelect(builder->CreateFCmpULT(param[0], getFloat(zero)), builder->CreateFSub(getFloat(zero), param[0]), param[0]); break; + case T_BOOLEAN: + case T_BYTE: ret = param[0]; break; + case T_SHORT: ab(16) + case T_INTEGER: ab(32) + case T_LONG: ab(64) + case T_SINGLE: fab(0.0f) + case T_FLOAT: fab(0.0) + #undef ab + #undef fab + default: abort(); + } + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + //store_pc(pc); Should not be needed... + codegen_stack + SUBR_CODE(SUBR_abs) + } + + case 0x55: //Int + if (type != T_VARIANT){ + codegen_value + llvm::Value* ret = param[0]; + if (type == T_SINGLE) + ret = builder->CreateCall(get_global_function(floorf, 'f', "f"), param[0]); + else if (type == T_FLOAT) + ret = builder->CreateCall(get_global_function(floor, 'd', "d"), param[0]); + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_int) + } + + case 0x56: //Fix + if (type != T_VARIANT){ + codegen_value + llvm::Value* ret = param[0]; + if (type >= T_SINGLE){ + llvm::Value* zero = type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0); + llvm::Value* func1 = type == T_SINGLE ? get_global_function(floorf, 'f', "f") : get_global_function(floor, 'd', "d"); + llvm::Value* func2 = type == T_SINGLE ? get_global_function(fabsf, 'f', "f") : get_global_function(fabs, 'd', "d"); + ret = gen_if_else_phi(builder->CreateFCmpUGE(param[0], zero), [&](){ + return builder->CreateCall(func1, param[0]); + }, [&](){ + return builder->CreateFSub(zero, builder->CreateCall(func1, builder->CreateCall(func2, param[0]))); + }); + } + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_fix) + } + + case 0x57: //Sgn + if (args[0]->type != T_VARIANT){ + llvm::Type* intTy = llvmType(getInt32Ty); + static const int bits[] = {0, 1, 8, 16, 32, 64}; + static llvm::Value* const zero[] = {NULL, getInteger(1, 0), getInteger(8, 0), getInteger(16, 0), getInteger(32, 0), getInteger(64, 0), getFloat(0.0f), getFloat(0.0)}; + codegen_value + llvm::Value* ret; + TYPE t = args[0]->type; + switch(t){ + case T_BOOLEAN: ret = builder->CreateSExt(param[0], intTy); break; + case T_BYTE: ret = builder->CreateZExt(builder->CreateICmpNE(param[0], zero[t]), intTy); break; + case T_SHORT: + case T_INTEGER: + case T_LONG: { + llvm::Value* lobit = builder->CreateAShr(param[0], bits[t]-1); + if (t < T_INTEGER) lobit = builder->CreateSExt(param[0], intTy); + else if (t > T_INTEGER) lobit = builder->CreateTrunc(param[0], intTy); + ret = builder->CreateSelect(builder->CreateICmpSGT(param[0], zero[t]), getInteger(32, 1), lobit); + break; + } + case T_SINGLE: + case T_FLOAT: { + ret = builder->CreateSelect(builder->CreateFCmpOGT(param[0], zero[t]), getInteger(32, 1), + builder->CreateSelect(builder->CreateFCmpOLT(param[0], zero[t]), getInteger(32, -1), zero[T_INTEGER])); + break; + } + default: __builtin_unreachable(); + } + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_sgn) + } + + case 0x58: { //Math + codegen_value + llvm::Value* ret; + if (extra == 1){ + //frac + llvm::Value* x = builder->CreateCall(get_global_function(fabs, 'd', "d"), param[0]); + ret = builder->CreateFSub(x, builder->CreateCall(get_global_function(floor, 'd', "d"), x)); + } else if (extra == 11){ + //deg + ret = builder->CreateFDiv(builder->CreateFMul(param[0], getFloat(180.0)), getFloat(M_PI)); + } else if (extra == 12){ + //rad + ret = builder->CreateFDiv(builder->CreateFMul(param[0], getFloat(M_PI)), getFloat(180.0)); + } else { + #define f(name) get_global_function(name, 'd', "d") + llvm::Value* functions[28] = { + NULL, NULL, f(log), f(exp), f(sqrt), f(sin), f(cos), f(tan), f(atan), + f(asin), f(acos), NULL, NULL, f(log10), f(sinh), f(cosh), f(tanh), f(asinh), + f(acosh), f(atanh), f(exp2), f(exp10), f(log2), f(cbrt), f(expm1), f(log1p), + f(floor), f(ceil) + }; + #undef f + ret = builder->CreateCall(functions[extra], param[0]); + } + gen_if_noreturn(builder->CreateICmpEQ(builder->CreateCall(get_global_function(__finite, 'i', "d"), ret), getInteger(32, 0)), [&](){ + create_throw(E_MATH); + }); + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } + + case 0x59: { //Pi + if (nargs == 0){ + if (on_stack) + push_value(getFloat(M_PI), T_FLOAT); + return getFloat(M_PI); + } else { + codegen_value + llvm::Value* ret = builder->CreateFMul(getFloat(M_PI), param[0]); + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, T_FLOAT, args[0]->on_stack ? false : true); + return ret; + } + } + + case 0x5A: { //Round + codegen_value + llvm::Value* val = nargs == 2 ? param[1] : getInteger(32, 0); + llvm::Value* power = builder->CreateCall2(M->getOrInsertFunction("llvm.powi.f64", get_function_type('d', "di")), getFloat(10.0), val); + + llvm::Value* ret = builder->CreateFMul( + builder->CreateCall(get_global_function(floor, 'd', "d"), + builder->CreateFAdd( + builder->CreateFDiv(param[0], power), + getFloat(0.5) + ) + ), power + ); + c_SP(-args[0]->on_stack-(nargs == 2 && args[1]->on_stack)+on_stack); + if (on_stack) + set_top_value(ret, T_FLOAT, args[0]->on_stack ? false : true); + return ret; + } + + case 0x5B: { //Randomize + codegen_value + llvm::Value* set = getInteger(8, nargs == 0 ? false : true); + llvm::Value* seed = nargs == 0 ? getInteger(32, 0) : param[0]; + builder->CreateCall2(get_global_function_jif(randomize, 'v', "ci"), set, seed); + c_SP(-(nargs && args[0]->on_stack)+on_stack); + if (on_stack) + set_top_value(NULL, T_VOID); + return NULL; + } + + case 0x5C: { //Rnd + codegen_value + llvm::Value *min, *max; + int stackdiff = on_stack; + if (nargs == 0){ + min = getFloat(0.0); + max = getFloat(1.0); + } else if (nargs == 1){ + min = getFloat(0.0); + max = param[0]; + stackdiff -= args[0]->on_stack; + } else if (nargs == 2){ + min = param[0]; + max = param[1]; + stackdiff -= args[0]->on_stack; + stackdiff -= args[1]->on_stack; + } + llvm::Value* r = builder->CreateCall(get_global_function_jif(rnd, 'd', "")); + llvm::Value* ret = builder->CreateFAdd(builder->CreateFMul(r, builder->CreateFSub(max, min)), min); + c_SP(stackdiff); + if (on_stack) + set_top_value(ret, T_FLOAT, (nargs >= 1 && args[0]->on_stack) ? false : true); + return ret; + } + + case 0x5D: //Min + case 0x5E: { //Max + if (type != T_VARIANT){ + bool is_max = digit == 0x5E; + codegen_value + llvm::Value* ret; + if (type == T_BOOLEAN || (type >= T_SHORT && type <= T_LONG)) + ret = (is_max ? gen_max : gen_min)(param[0], param[1]); + else if (type == T_BYTE) + ret = builder->CreateSelect(builder->CreateICmpULT(param[0], param[1]), is_max ? param[1] : param[0], is_max ? param[0] : param[1]); + else if (type == T_FLOAT) + ret = builder->CreateSelect(builder->CreateFCmpOLT(param[0], param[1]), is_max ? param[1] : param[0], is_max ? param[0] : param[1]); + else if (type == T_DATE) + ret = builder->CreateSelect(LessDate(param[0], param[1]), is_max ? param[0] : param[1], is_max ? param[1] : param[0]); + c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + extra = digit << 8; + SUBR_CODE(SUBR_min_max) + } + } + + case 0x5F: { //IIf + //FIXME if a destructor throws an error, this does not work ... + codegen_value + llvm::Value* ret; + if (args[1]->type == args[2]->type || (TYPE_is_string(args[1]->type) && TYPE_is_string(args[2]->type))){ + ret = builder->CreateSelect(param[0], param[1], param[2]); + gen_if_else(param[0], [&](){ + release(param[2], args[2]->type); + }, [&](){ + release(param[1], args[1]->type); + }, "IIf_release_false_argument", "IIf_release_true_argument", "IIf_release_done"); + + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; + } else { + //Convert to variant + + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack); + + args[1]->on_stack = false; + args[2]->on_stack = false; + + ret = gen_if_else_phi(param[0], [&](){ + if (args[1]->type == type) + return param[1]; + + release(param[2], args[2]->type); + + return JIT_conv_to_variant(args[1], param[1], on_stack, NULL); + }, [&](){ + if (args[2]->type == type) + return param[2]; + + release(param[1], args[1]->type); + + return JIT_conv_to_variant(args[2], param[2], on_stack, NULL); + }, "IIf_then", "IIf_else"); + return ret; + } + break; + } + + case 0x60: { //Choose + llvm::Value* ret; + if (nargs < 4){ + codegen_value + ret = get_default(type); + } + if (nargs == 1){ + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; + } else if (nargs == 2){ + llvm::Value* cmp = builder->CreateICmpEQ(param[0], getInteger(32, 1)); + ret = builder->CreateSelect(cmp, param[1], ret); + c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + gen_if(builder->CreateXor(cmp, getInteger(1, true)), [&](){ + release(param[1], args[1]->type); + }, "release_1st_choose"); + return ret; + } else if (nargs == 3){ + llvm::Value* index = builder->CreateSub(param[0], getInteger(32, 1)); + + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack); + + args[1]->on_stack = false; + args[2]->on_stack = false; + + bool stack_already_set_in_conv = false; + + ret = gen_if_else_phi(builder->CreateICmpULT(index, getInteger(32, 2)), [&](){ + llvm::Value* r; + if (args[1]->type == args[2]->type || (TYPE_is_string(args[1]->type) && TYPE_is_string(args[2]->type))){ + r = builder->CreateSelect(builder->CreateTrunc(index, llvmType(getInt1Ty)), param[2], param[1]); + c_SP(on_stack); + return r; + } else { + r = gen_if_else_phi(builder->CreateTrunc(index, llvmType(getInt1Ty)), [&](){ + return JIT_conv_to_variant(args[2], param[2], on_stack, NULL); + }, [&](){ + return JIT_conv_to_variant(args[1], param[1], on_stack, NULL); + }); + stack_already_set_in_conv = true; + return r; + } + }, [&](){ + c_SP(on_stack); + if (stack_already_set_in_conv && on_stack) + set_top_value(ret, type); + return ret; + }); + + if (!stack_already_set_in_conv && on_stack) + set_top_value(ret, type); + + gen_if(builder->CreateICmpNE(param[0], getInteger(32, 1)), [&](){ + release(param[1], args[1]->type); + }, "release_1st_choose"); + gen_if(builder->CreateICmpNE(param[0], getInteger(32, 2)), [&](){ + release(param[2], args[2]->type); + }, "release_2st_choose"); + + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_choose) + } + } + + case 0x61: { //Array + codegen_value + if (nargs > 0){ + push_value(getInteger(32, nargs), T_INTEGER); + } + llvm::Value* arr = builder->CreateCall4(get_global_function_jif(OBJECT_create, 'p', "pppi"), get_global((void*)type, llvmType(getInt8Ty)), get_nullptr(), get_nullptr(), getInteger(32, nargs > 0 ? 1 : 0)); + borrow_object_no_nullcheck(arr); + int element_size = TYPE_sizeof_memory(type2); + int data_offset = offsetof(CARRAY, data); + llvm::Value* data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(arr, getInteger(TARGET_BITS, data_offset)), charPP)); + for(int i=0; iCreateGEP(data, getInteger(TARGET_BITS, i*element_size)); + variable_write(addr, param[i], args[i]->type); + } + llvm::Value* ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), arr); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, type); + return ret; + } + + case 0x62: { //Math2 + codegen_value + llvm::Value* ret; + if (extra == 2){ + llvm::Value* temp = param[0]; + param[0] = param[1]; + param[1] = temp; + } + if (extra <= 2){ + //Atan2, Ang + ret = builder->CreateCall2(get_global_function(atan2, 'd', "dd"), param[0], param[1]); + } else { + ret = builder->CreateCall(get_global_function(sqrt, 'd', "d"), builder->CreateFAdd(builder->CreateFMul(param[0], param[0]), builder->CreateFMul(param[1], param[1]))); + } + gen_if_noreturn(builder->CreateICmpEQ(builder->CreateCall(get_global_function(__finite, 'i', "d"), ret), getInteger(32, 0)), [&](){ + create_throw(E_MATH); + }); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_FLOAT); + return ret; + } + + case 0x63: { //IsAscii, IsLetter, ... + codegen_value + llvm::Value* orig_ptr = extract_value(param[0], 1); + llvm::Value* ptr = builder->CreateGEP(orig_ptr, extract_value(param[0], 2)); + llvm::Value* endptr = builder->CreateGEP(ptr, to_target_int(extract_value(param[0], 3))); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + llvm::BasicBlock* body_block = create_bb("IsChr..."); + llvm::BasicBlock* cont_block = create_bb("IsChr_done"); + builder->CreateCondBr(builder->CreateICmpNE(extract_value(param[0], 3), getInteger(32, 0)), body_block, cont_block); + + builder->SetInsertPoint(body_block); + llvm::PHINode* current_ptr = builder->CreatePHI(llvmType(getInt8PtrTy), 2); + llvm::Value* next = builder->CreateGEP(current_ptr, getInteger(TARGET_BITS, 1)); + current_ptr->addIncoming(ptr, from_block); + current_ptr->addIncoming(next, body_block); + llvm::Value* c = builder->CreateLoad(current_ptr); + llvm::Value* res; + switch(extra){ + case 1: //IsAscii: + res = builder->CreateICmpEQ(builder->CreateAnd(c, getInteger(8, ~0x7F)), getInteger(8, 0)); + break; + case 2: //IsLetter: + res = builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'z')) + ), builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'Z')) + ) + ); break; + case 3: //IsLower: + res = builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'z')) + ); break; + case 4: //IsUpper: + res = builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'Z')) + ); break; + case 5: //IsDigit: + res = builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, '0')), + builder->CreateICmpSLE(c, getInteger(8, '9')) + ); break; + case 6: //IsHexa: + res = builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, '0')), + builder->CreateICmpSLE(c, getInteger(8, '9')) + ), + builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'f')) + ), builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'F')) + ) + ) + ); break; + case 7: { //IsSpace: + const char* conds = " \n\r\t\f\v"; + res = builder->CreateICmpEQ(c, getInteger(8, conds[0])); + for(size_t i=1; iCreateOr(res, builder->CreateICmpEQ(c, getInteger(8, conds[i]))); + break; + } + case 8: //IsBlank: + res = builder->CreateOr(builder->CreateICmpEQ(c, getInteger(8, 32)), builder->CreateICmpEQ(c, getInteger(8, '\t'))); + break; + case 9: //IsPunct: + res = builder->CreateAnd( + builder->CreateICmpSGT(c, getInteger(8, 32)), + builder->CreateXor( + builder->CreateOr( + builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'z')) + ), builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'Z')) + ) + ), + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, '0')), + builder->CreateICmpSLE(c, getInteger(8, '9')) + ) + ), + getInteger(1, 1) + ) + ); + break; + } + llvm::Value* still_inside_string = builder->CreateICmpNE(next, endptr); + builder->CreateCondBr(builder->CreateAnd(still_inside_string, res), body_block, cont_block); + + builder->SetInsertPoint(cont_block); + res = create_phi(getInteger(1, false), from_block, res, body_block); + release(param[0], args[0]->type); + c_SP(stack_diff); + if (on_stack) + set_top_value(res, T_BOOLEAN); + return res; + } + + case 0x64: //Bit manipulation + if (args[0]->type == T_VARIANT){ + codegen_stack + SUBR_CODE(SUBR_bit) + } else { + codegen_value + static const int bits[] = {0, 0, 8, 16, 32, 64}; + int nbits = bits[args[0]->type]; + gen_if_noreturn(builder->CreateICmpUGE(param[1], getInteger(32, nbits)), [&](){ + create_throw(E_ARG); + }, "bit_out_of_range"); + llvm::Value* bt = nbits == 32 ? param[1] : (nbits == 64 ? builder->CreateZExt(param[1], llvmType(getInt64Ty)) : builder->CreateTrunc(param[1], param[0]->getType())); + llvm::Value* res; + switch(extra){ + case 1: //BClr + res = builder->CreateAnd(param[0], builder->CreateXor(builder->CreateShl(getInteger(nbits, 1), bt), getInteger(nbits, -1))); + break; + case 2: //BSet + res = builder->CreateOr(param[0], builder->CreateShl(getInteger(nbits, 1), bt)); + break; + case 3: //BTst + res = builder->CreateICmpNE(builder->CreateAnd(param[0], builder->CreateShl(getInteger(nbits, 1), bt)), getInteger(nbits, 0)); + break; + case 4: //BChg: + res = builder->CreateXor(param[0], builder->CreateShl(getInteger(nbits, 1), bt)); + break; + case 5: //Asl: + res = builder->CreateShl(param[0], bt); + if (nbits != 8){ + llvm::Value* max_signed_int = getInteger(nbits, (1ULL << nbits)-1); //0x7F... + llvm::Value* min_signed_int = getInteger(nbits, 1ULL << nbits); //0x80... + res = builder->CreateOr( + builder->CreateAnd(res, max_signed_int), builder->CreateAnd(param[0], min_signed_int) + ); + } + break; + case 6: //Asr: + res = builder->CreateAShr(param[0], bt); + break; + case 7: //Rol: + res = builder->CreateOr( + builder->CreateShl(param[0], bt), builder->CreateLShr(param[0], builder->CreateSub(getInteger(nbits, nbits), bt)) + ); + break; + case 8: //Ror: + res = builder->CreateOr( + builder->CreateLShr(param[0], bt), builder->CreateShl(param[0], builder->CreateSub(getInteger(nbits, nbits), bt)) + ); + break; + case 9: //Lsl: + res = builder->CreateShl(param[0], bt); + break; + case 10: //Lsr: + res = builder->CreateLShr(param[0], bt); + break; + } + c_SP(stack_diff); + if (on_stack) + set_top_value(res, type, extra == 3 || !args[0]->on_stack); + return res; + } + + case 0x65: //IsBoolean ... + codegen_stack + SUBR_CODE(SUBR_is_type) + + case 0x66: { //TypeOf + codegen_value + llvm::Value* ret; + if (extra){ //Sizeof + ret = gen_if_phi(getInteger(32, TARGET_BITS/8), builder->CreateICmpULT(param[0], getInteger(32, 16)), [&](){ + llvm::Value* s = builder->CreateLoad(builder->CreateGEP(get_global((void*)TYPE_sizeof_memory_tab, LONG_TYPE), to_target_int(param[0]))); + if (TARGET_BITS == 64) + s = builder->CreateTrunc(s, llvmType(getInt32Ty)); + return s; + }, "SizeOf"); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_INTEGER, !args[0]->on_stack); + } else { //TypeOf + TYPE t = args[0]->type; + if (t == T_VARIANT){ + ret = extract_value(param[0], 0); + if (TARGET_BITS == 64) + ret = builder->CreateTrunc(ret, llvmType(getInt32Ty)); + llvm::BasicBlock *BB0 = builder->GetInsertBlock(), *BB1 = create_bb("TypeOf1"), *BB2 = create_bb("TypeOf2"), *BB3 = create_bb("TypeOf3"); + + builder->CreateCondBr(builder->CreateICmpEQ(ret, getInteger(32, T_CSTRING)), BB3, BB1); + builder->SetInsertPoint(BB1); + builder->CreateCondBr(builder->CreateICmpSGE(ret, getInteger(32, T_OBJECT)), BB2, BB3); + builder->SetInsertPoint(BB2); + builder->CreateBr(BB3); + builder->SetInsertPoint(BB3); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt32Ty), 3); + phi->addIncoming(getInteger(32, T_OBJECT), BB2); + phi->addIncoming(ret, BB1); + phi->addIncoming(getInteger(32, T_STRING), BB0); + ret = phi; + } else if (t == T_CSTRING){ + ret = getInteger(32, T_STRING); + } else if (TYPE_is_object(t) && t != T_NULL){ + ret = getInteger(32, T_OBJECT); + } else { + ret = getInteger(32, t); + } + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_INTEGER, args[0]->type != T_INTEGER || !args[0]->on_stack); + } + release(param[0], args[0]->type); + return ret; + } + + case 0x68: //Bin + case 0x69: //Hex + codegen_stack + SUBR_CODE(SUBR_hex_bin) + + case 0x6A: //Val + codegen_stack + SUBR_CODE(SUBR_val) + + case 0x6B: { //Str + args[0]->must_on_stack(); + codegen_value + llvm::Value* stack_top = get_value_on_top_addr(); + builder->CreateCall3(get_global_function_jif(VALUE_to_string, 'v', "ppp"), + builder->CreateBitCast(stack_top, llvmType(getInt8PtrTy)), + builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)), + builder->CreateBitCast(temp_int, llvmType(getInt8PtrTy)) + ); + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(on_stack ? stack_top : temp_value, llvmType(getInt8PtrTy)), + builder->CreateLoad(temp_voidptr), + builder->CreateLoad(temp_int) + ); + llvm::Value* str = read_value(on_stack ? stack_top : temp_value, T_STRING); + release(param[0], args[0]->type); + borrow_string(extract_value(str, 1)); //Nullcheck needed, might be empty string = null pointer + if (!on_stack) + c_SP(-1); + return str; + } + + case 0x6C: //Format + codegen_stack + SUBR_CODE(SUBR_format) + + case 0x6D: { //Timer + builder->CreateCall2(get_global_function_jif(DATE_timer, 'v', "pi"), + builder->CreateBitCast(temp_double, llvmType(getInt8PtrTy)), + getInteger(32, 1)); + llvm::Value* ret = builder->CreateLoad(temp_double); + if (on_stack) + push_value(ret, T_FLOAT); + return ret; + } + + case 0x6E: { //Now + c_SP(on_stack); + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + builder->CreateCall(get_global_function_jif(DATE_now, 'v', "p"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy))); + return read_value(addr, T_DATE); + } + + case 0x6F: //Year + codegen_stack + SUBR_CODE(SUBR_year) + + case 0x70: //Week + codegen_stack + SUBR_CODE(SUBR_week) + + case 0x71: //Date + codegen_stack + SUBR_CODE(SUBR_date) + + case 0x72: //Time + codegen_stack + SUBR_CODE(SUBR_time) + + case 0x73: //DateAdd, DateDiff + if (extra == 0){ //DateAdd + if (on_stack) + args[0]->must_on_stack(); + codegen_value + c_SP(stack_diff); + llvm::Value* addr = args[0]->on_stack ? get_value_on_top_addr() : temp_value; + if (!args[0]>on_stack) + store_value(temp_value, param[0], T_DATE, false /*not needed*/); + builder->CreateCall3(get_global_function_jif(DATE_add, 'v', "pii"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + param[1], param[2]); + return read_value(addr, T_DATE); + } else { //DateDiff + codegen_value + c_SP(stack_diff); + + llvm::Value* addr1 = args[0]->on_stack ? get_value_on_top_addr() : temp_value2; + llvm::Value* addr2 = args[1]->on_stack ? (args[0]->on_stack ? builder->CreateGEP(addr1, getInteger(TARGET_BITS, 1)) : get_value_on_top_addr()) : temp_value; + + if (!args[0]->on_stack) + store_value(temp_value2, param[0], T_DATE, false); + if (!args[1]->on_stack) + store_value(temp_value, param[1], T_DATE, false); + + llvm::Value* ret = builder->CreateCall3(get_global_function_jif(DATE_diff, 'i', "ppi"), + builder->CreateBitCast(addr2, llvmType(getInt8PtrTy)), + builder->CreateBitCast(addr1, llvmType(getInt8PtrTy)), + param[2]); + if (on_stack) + set_top_value(ret, T_INTEGER); + return ret; + } + + case 0x74: //Eval + codegen_stack + SUBR_CODE(SUBR_eval) + + case 0x75: { //Error + //llvm::Value* ret = builder->CreateICmpNE(read_global((void*)&EXEC_got_error, llvmType(getInt8Ty)), getInteger(8, 0)); + llvm::Value* ret = builder->CreateLoad(temp_got_error); + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; + } + + case 0x76: //Debug + store_pc(pc); + SUBR(SUBR_debug) + + case 0x77: //Wait + codegen_stack + SUBR_CODE(SUBR_wait) + + case 0x78: //Open + codegen_stack + SUBR_CODE(SUBR_open) + + case 0x79: //Close + codegen_stack + SUBR_CODE(SUBR_close) + + case 0x7A: //Input + codegen_stack + SUBR_CODE(SUBR_input) + + case 0x7B: //Line Input + codegen_stack + SUBR(SUBR_linput) + + case 0x7C: //Print + codegen_stack + SUBR_CODE(SUBR_print) + + case 0x7D: //Read + codegen_stack + SUBR_CODE(SUBR_read) + + case 0x7E: //Write + codegen_stack + SUBR_CODE(SUBR_write) + + case 0x7F: //Flush + codegen_stack + SUBR(SUBR_flush) + + case 0x80: //Lock + codegen_stack + SUBR(SUBR_lock) + + case 0x81: //Input From, Output To, Error To + codegen_stack + SUBR_CODE(SUBR_inp_out) + + case 0x82: //Eof + codegen_stack + SUBR_CODE(SUBR_eof) + + case 0x83: //Lof + codegen_stack + SUBR_CODE(SUBR_lof) + + case 0x84: //Seek + codegen_stack + SUBR_CODE(SUBR_seek) + + case 0x86: //Mkdir, deprecated -> Even() & Odd() + if (extra != 0){ + codegen_value + llvm::Value* val = builder->CreateTrunc(param[0], llvmType(getInt1Ty)); + if (extra == 1) //Even + val = builder->CreateXor(val, getInteger(1, 1)); + c_SP(stack_diff); + if (on_stack) + set_top_value(val, T_BOOLEAN); + return val; + } + //Else fallthrough: deprecated Kill(1) + + case 0x85: //Kill + case 0x87: //Rmdir, deprecated + if (extra == 0) extra = digit - 0x85; + codegen_stack + SUBR_CODE(SUBR_kill) + + case 0x8A: //Link, deprecated -> IsNan() & IsInf + if (extra != 0){ + codegen_value + if (extra == 1){ + //IsNan + llvm::Value* res = builder->CreateICmpNE(builder->CreateCall(get_global_function(__isnan, 'i', "d"), param[0]), getInteger(32, 0)); + c_SP(stack_diff); + if (on_stack) + set_top_value(res, T_BOOLEAN); + return res; + } else if (extra == 2){ + //IsInf + llvm::Value* res = builder->CreateCall(get_global_function(__isinf, 'i', "d"), param[0]); + c_SP(stack_diff); + if (on_stack) + set_top_value(res, T_INTEGER); + return res; + } + } + //Else fallthrough: deprecated Link + + case 0x88: //Move + case 0x89: //Copy, deprecated + if (extra == 0) extra = digit - 0x88; + codegen_stack + SUBR_CODE(SUBR_move) + + case 0x8B: //Exist + codegen_stack + SUBR_CODE(SUBR_exist) + + case 0x8C: //Access + codegen_stack + SUBR_CODE(SUBR_access) + + case 0x8D: //Stat + codegen_stack + SUBR_CODE(SUBR_stat) + + case 0x8E: //Dfree + codegen_stack + SUBR(SUBR_dfree) + + case 0x8F: //Temp + codegen_stack + SUBR_CODE(SUBR_temp) + + case 0x90: //IsDir + codegen_stack + SUBR_CODE(SUBR_isdir) + + case 0x91: //Dir + codegen_stack + SUBR_CODE(SUBR_dir) + + case 0x92: //RDir + codegen_stack + SUBR_CODE(SUBR_rdir) + + case 0x93: //Exec + codegen_stack + SUBR_CODE(SUBR_exec) + + case 0x94: //Alloc + codegen_stack + SUBR_CODE(SUBR_alloc) + + case 0x95: //Free + codegen_stack + SUBR(SUBR_free) + + case 0x96: //Realloc + codegen_stack + SUBR_CODE(SUBR_realloc) + + case 0x97: //StrPtr + codegen_stack + SUBR_CODE(SUBR_strptr) + + case 0x98: { //Sleep + codegen_value + store_element(temp_2longs, 0, builder->CreateFPToSI(param[0], LONG_TYPE)); + llvm::Value* fabs_ret = builder->CreateCall(get_global_function(fabs, 'd', "d"), param[0]); + llvm::Value* floor_ret = builder->CreateCall(get_global_function(floor, 'd', "d"), fabs_ret); + llvm::Value* nsec = builder->CreateFMul(builder->CreateFSub(fabs_ret, floor_ret), getFloat(1000000000.0)); + store_element(temp_2longs, 1, builder->CreateFPToSI(nsec, LONG_TYPE)); + + llvm::BasicBlock *BB1 = create_bb("nanosleep_loop"), *BB2 = create_bb("nanosleep_cont"); + + llvm::Value* ptr = builder->CreateBitCast(temp_2longs, llvmType(getInt8PtrTy)); + + builder->CreateBr(BB1); + builder->SetInsertPoint(BB1); + llvm::Value* res = builder->CreateCall2(get_global_function(nanosleep, 'i', "pp"), ptr, ptr); + builder->CreateCondBr(builder->CreateICmpSLT(res, getInteger(32, 0)), BB1, BB2); + + builder->SetInsertPoint(BB2); + + if (on_stack) + push_value(NULL, T_VOID); + return NULL; + } + + case 0x99: { //VarPtr + auto pie = (PushIntegerExpression*)args[0]; + + llvm::Value* ret; + + ushort op = pie->i; + int index = op & 0xFF; + if ((op & 0xFF00) == C_PUSH_LOCAL){ + CLASS_LOCAL* var = &FP->local[index]; + TYPE t = ctype_to_type(&var->type); + if (TYPE_is_string(t)){ + ret = builder->CreateGEP(load_element(locals[index], 1), to_target_int(load_element(locals[index], 2))); + } else { + ret = builder->CreateBitCast(locals[index], llvmType(getInt8PtrTy)); + } + } else if ((op & 0xF800) == C_PUSH_DYNAMIC) { + CLASS_VAR* var = &CP->load->dyn[op & 0x7FF]; + ret = builder->CreateGEP(current_op, getInteger(TARGET_BITS, var->pos)); + } else if ((op & 0xF800) == C_PUSH_STATIC) { + CLASS_VAR* var = &CP->load->stat[op & 0x7FF]; + ret = builder->CreateGEP(get_global((void*)CP->stat, llvmType(getInt8Ty)), getInteger(TARGET_BITS, var->pos)); + } + if (on_stack) + push_value(ret, T_POINTER); + return ret; + } + + case 0x9A: //Collection + codegen_stack + SUBR_CODE(SUBR_collection) + + case 0x9B: //Tr + codegen_stack + SUBR(SUBR_tr) + + case 0x9C: //Quote, Shell, Html + codegen_stack + SUBR_CODE(SUBR_quote) + + case 0x9D: //Unquote + codegen_stack + SUBR_CODE(SUBR_unquote) + + case 0x9E: //MkInteger, ... + codegen_value + if (extra <= T_BYTE){ + llvm::Value* offset = builder->CreateShl(builder->CreateZExt(param[0], LONG_TYPE), getInteger(TARGET_BITS, 1)); + + llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), + builder->CreateGEP(builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)&STRING_char_string[0]), llvmType(getInt8PtrTy)), offset), + getInteger(32, 0), getInteger(32, 1)); + + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_CSTRING); + return ret; + } else { + static const int sizes[] = {0, 1, 1, 2, 4, 8, 4, 8, 8, 0, 0, TARGET_BITS/8}; + c_SP(stack_diff); + llvm::Value* stack_top = get_value_on_top_addr(); + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(stack_top, llvmType(getInt8PtrTy)), + get_nullptr(), + getInteger(32, sizes[extra])); + + llvm::Value* ret = read_value(stack_top, T_STRING); + llvm::Value* ptr = extract_value(ret, 1); + + borrow_string_no_nullcheck(ptr); + + builder->CreateStore(param[0], builder->CreateBitCast(ptr, pointer_t(param[0]->getType()))); + return ret; + } + break; + + case 0x9F: //Ptr + codegen_stack + SUBR_CODE(SUBR_ptr) + + default: assert(false && "Subr not implemented yet"); + } + return ret_top_stack(type, on_stack); +} +void SubrExpression::codegen_on_stack(){ + codegen_get_value(); +} + +void stack_corrupted_abort(){ + fprintf(stderr, "Stack became corrupted in a JIT function. Please make a bug report.\n"); + abort(); +} + +void NopExpression::codegen(){ + /*builder->CreateCall4(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"Nu: %s %p %p\n", llvmType(getInt8Ty)), + get_global((void*)buf, llvmType(getInt8Ty)), + read_global((void*)&SP), + read_global((void*)&BP)); + builder->CreateCall4(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"Nu: %d %p %p\n", llvmType(getInt8Ty)), + getInteger(32, FP->n_local + FP->n_ctrl), + read_global((void*)&SP), + read_global((void*)&BP));*/ + + if (test_stack){ +#ifndef GOSUB_ON_STACK + llvm::Value* sp = read_global((void*)&SP); + llvm::Value* bp = read_global((void*)&BP); + bp = builder->CreateGEP(bp, getInteger(TARGET_BITS, sizeof(VALUE)*(FP->n_local+FP->n_ctrl))); +#else + llvm::Value* sp = builder->CreateBitCast(read_global((void*)&SP), pointer_t(value_type)); + llvm::Value* bp = builder->CreateLoad(gp); +#endif + gen_if_noreturn(builder->CreateICmpNE(bp, sp), [&](){ + builder->CreateCall(get_global_function(stack_corrupted_abort, 'v', "")); + builder->CreateUnreachable(); + }); + } +} + +void ProfileLineExpression::codegen(){ + gen_if(builder->CreateICmpNE(read_global((void*)&EXEC_profile_instr, llvmType(getInt8Ty)), getInteger(8, false)), [&](){ + builder->CreateCall3(get_global_function_jif(DEBUG_Profile_Add, 'v', "ppp"), + get_global((void*)CP, llvmType(getInt8Ty)), + get_global((void*)FP, llvmType(getInt8Ty)), + get_global((void*)pc, llvmType(getInt8Ty))); + }); +} + +void StopEventExpression::codegen(){ + builder->CreateStore(getInteger(8, true), get_global((void*)&GAMBAS_StopEvent, llvmType(getInt8Ty))); +} + +void ReturnExpression::codegen(){ + auto normal_return = [&](){ + llvm::Value* ret; + if (retval){ + ret = retval->codegen_get_value(); + if (retval->on_stack) + c_SP(-1); + } else { + ret = get_default(type); + } + store_value(get_global((void*)RP), ret, type); + if (in_try) + builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0)); + if (EC != NULL){ + gen_if(builder->CreateXor(builder->CreateLoad(temp_got_error2), getInteger(1, 1)), [&](){ + llvm::Value* call = builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + + if (llvm::Instruction* inst = llvm::dyn_cast(call)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("large_end_try", llvm::MDNode::get(llvm_context, arr)); + } + }, "return_in_large_try"); + } + return_points.push_back(builder->GetInsertBlock()); + }; + + if (ngosubs == 0 || kind > 0){ + normal_return(); + } else { + gen_if_noreturn(builder->CreateICmpEQ(builder->CreateLoad(gosub_return_point), getInteger(16, 0)), [&](){ + normal_return(); + }); + //Else return in Gosub + gosub_return_points.push_back(builder->GetInsertBlock()); + } + builder->SetInsertPoint(create_bb("dummy")); +} + +void QuitExpression::codegen(){ + if (quitval){ + llvm::Value* val = quitval->codegen_get_value(); + if (quitval->on_stack) + c_SP(-1); + + builder->CreateStore(val, get_global((void*)&EXEC_quit_value, llvmType(getInt8Ty))); + } + + builder->CreateCall(get_global_function_jif(EXEC_quit, 'v', "")); + builder->CreateUnreachable(); + builder->SetInsertPoint(create_bb("dummy")); +} + +static void debug_print_line(){ + for (int i = 1; i < 10; i++) + fputs("--------", stderr); + fputc('\n', stderr); +} + +static void run_optimizations(){ + bool changed = true; + while(changed){ + llvm::FunctionPassManager FPM(M); + llvm::PassManager MPM; + llvm::PassManagerBuilder PMB; + PMB.OptLevel = 2; + PMB.SizeLevel = 1; + PMB.populateFunctionPassManager(FPM); + PMB.populateModulePassManager(MPM); + + //FPM.add(createGambasPass()); + + FPM.doInitialization(); + FPM.run(*llvm_function); + FPM.doFinalization(); + MPM.run(*M); + + llvm::FunctionPass* pass = createGambasPass(); + changed = pass->runOnFunction(*llvm_function); + delete pass; + } +} + +template +static void do_search(llvm::BasicBlock* block, T func){ + std::queue q; + std::set vis; + + q.push(block); + vis.insert(block); + + while(!q.empty()){ + llvm::BasicBlock* BB = q.front(); q.pop(); + + for(llvm::BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ){ + if (func(I++)) + goto next_block_in_queue; + } + + for(llvm::succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E; ++SI){ + if (vis.count(*SI) == 0){ + q.push(*SI); + vis.insert(*SI); + } + } + next_block_in_queue:; + } +} + +static void fix_setjmp(llvm::BasicBlock* catch_block, llvm::BasicBlock* try_block, const char* end_try_string){ + std::set vars; + std::set volatile_vars; + std::vector stores; + + do_search(try_block, [&](llvm::Value* value){ + if (llvm::StoreInst* si = llvm::dyn_cast(value)){ + if (llvm::AllocaInst* ai = llvm::dyn_cast(si->getPointerOperand())){ + vars.insert(ai); + stores.push_back(si); + } + } else if (llvm::CallInst* ci = llvm::dyn_cast(value)){ + if (ci->hasMetadata() && ci->getMetadata(end_try_string)) + return true; + } + return false; + }); + + do_search(catch_block, [&](llvm::Value* value){ + if (llvm::LoadInst* li = llvm::dyn_cast(value)){ + if (llvm::AllocaInst* ai = llvm::dyn_cast(li->getPointerOperand())){ + if (vars.count(ai) != 0){ + li->setVolatile(true); + volatile_vars.insert(ai); + } + } + } + return false; + }); + + for(size_t i=0, e=stores.size(); i!=e; i++){ + llvm::StoreInst* si = stores[i]; + llvm::AllocaInst* ai = llvm::dyn_cast(si->getPointerOperand()); + if (volatile_vars.count(ai) != 0){ + si->setVolatile(true); + } + } +} + +static void fix_setjmps(){ + if (!has_tries) + return; + + if (EC != NULL){ + //There is a Catch/Finally in the function, so a setjmp is somewhere in the entry block + //and the first branch is a conditional branch: true -> catch part, false -> try part + + llvm::BranchInst* br = llvm::dyn_cast(entry_block->getTerminator()); + assert(br && br->isConditional()); + + llvm::BasicBlock* catch_block = br->getSuccessor(0); + llvm::BasicBlock* try_block = br->getSuccessor(1); + + fix_setjmp(catch_block, try_block, "large_end_try"); + } + + for(size_t i=0, e=try_blocks.size(); i!=e; i++){ + llvm::BasicBlock* BB = try_blocks[i]; + + llvm::BranchInst* br = llvm::dyn_cast(BB->getTerminator()); + assert(br && br->isConditional()); + + llvm::BasicBlock* catch_block = br->getSuccessor(0); + llvm::BasicBlock* try_block = br->getSuccessor(1); + + fix_setjmp(catch_block, try_block, "end_try"); + } +} + + +struct DynamicAllocatedString { + char* data; + int len; + DynamicAllocatedString(const char* data, int len){ + this->data = new char[len]; + this->len = len; + memcpy(this->data, data, len); + } + ~DynamicAllocatedString(){ + delete[] data; + } +}; + +static std::vector extern_signature_strings; +static std::map extern_signatures; + +static void func_extern_call_variant_vararg(void* return_value_addr, void* func_addr, int nargs, TYPE return_type){ + std::map& signatures = extern_signatures; + + char signature_string[nargs+sizeof(TYPE)]; + + *(TYPE*)&signature_string[nargs] = return_type; + + for(int i=0; isetTargetTriple("x86_64-pc-linux-gnu"); + M->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); + } else { + M->setTargetTriple("i386-unknown-linux-gnu"); + M->setDataLayout("e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"); + } + EE->addModule(M); + + static char buf[256] = "extern_func_caller_"; + int eob = strlen("extern_func_caller_"); + for(int i=0; i(M->getOrInsertFunction(buf, get_function_type('v', "pp", true))); + llvm::Function::arg_iterator arg_it = llvm_function->arg_begin(); + llvm::Value* func_addr_arg = arg_it++; + llvm::Value* return_value_addr_arg = arg_it; + + entry_block = create_bb("entry"); + builder = new llvm::IRBuilder<>(entry_block); + + std::vector ft; + std::vector orig_args; + std::vector func_args; + ft.resize(nargs); + orig_args.resize(nargs); + func_args.resize(nargs); + + llvm::Value* SP_base = builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -nargs)); + llvm::Value* SP_current = SP_base; + + for(int i=0; iCreateGEP(SP_current, getInteger(TARGET_BITS, 1)); + } + + //Call function + llvm::Type* function_type = llvm::FunctionType::get(extern_types[return_type > T_OBJECT ? T_OBJECT : return_type], ft, true); + llvm::Value* call_function = builder->CreateBitCast(func_addr_arg, pointer_t(function_type)); + + llvm::Value* ret = builder->CreateCall(call_function, func_args); + + //Manage return value + ret = codegen_extern_manage_return_value(ret, return_type); + if (return_type != T_VOID) + builder->CreateStore(ret, builder->CreateBitCast(return_value_addr_arg, pointer_t(TYPE_llvm(return_type)))); + + //Release arguments + for(int i=nargs; i --> 0; ){ + release(orig_args[i], signature_string[i]); + c_SP(-1); + } + + builder->CreateRetVoid(); + + //M->dump(); + + llvm::verifyModule(*M); + + llvm::FunctionPassManager FPM(M); + llvm::PassManager MPM; + llvm::PassManagerBuilder PMB; + PMB.OptLevel = 2; + PMB.SizeLevel = 1; + PMB.populateFunctionPassManager(FPM); + PMB.populateModulePassManager(MPM); + + FPM.doInitialization(); + FPM.run(*llvm_function); + FPM.doFinalization(); + MPM.run(*M); + + //Print out the code after optimization + if (MAIN_debug){ + debug_print_line(); + fprintf(stderr, "gb.jit: dumping vararg extern call function\n"); + debug_print_line(); + M->dump(); + debug_print_line(); + fputc('\n', stderr); + } + + void (*fn)(void*, void*) = (void(*)(void*, void*))EE->getPointerToFunction(llvm_function); + + delete builder; + + llvm_function->deleteBody(); + mappings.clear(); + + extern_signature_strings.emplace_back((char*)signature_string, nargs+sizeof(TYPE)); + signatures.insert(std::pair(llvm::StringRef(extern_signature_strings.back().data, nargs+sizeof(TYPE)), fn)); + (*fn)(func_addr, return_value_addr); + } else { + (*(it->second))(func_addr, return_value_addr); + } +} + +static void func_void(){ + RP->type = T_VOID; + JIF.F_EXEC_leave_keep(); +} +static void func_boolean(){ + RP->type = T_BOOLEAN; + RP->_boolean.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_byte(){ + RP->type = T_BYTE; + RP->_byte.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_short(){ + RP->type = T_SHORT; + RP->_short.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_integer(){ + RP->type = T_INTEGER; + RP->_integer.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_long(){ + RP->type = T_LONG; + RP->_long.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_single(){ + RP->type = T_SINGLE; + RP->_single.value = 0.0f; + JIF.F_EXEC_leave_keep(); +} +static void func_float(){ + RP->type = T_FLOAT; + RP->_float.value = 0.0; + JIF.F_EXEC_leave_keep(); +} +static void func_date(){ + RP->type = T_DATE; + RP->_date.date = 0; + RP->_date.time = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_string(){ + RP->type = T_CSTRING; + RP->_string.addr = NULL; + RP->_string.start = 0; + RP->_string.len = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_pointer(){ + RP->type = T_POINTER; + RP->_pointer.value = NULL; + JIF.F_EXEC_leave_keep(); +} +static void func_variant(){ + RP->type = T_VARIANT; + RP->_variant.vtype = T_NULL; + JIF.F_EXEC_leave_keep(); +} +static void func_object(){ + RP->type = T_OBJECT; + RP->_object.object = NULL; + JIF.F_EXEC_leave_keep(); +} + +void JIT_codegen(){ + if (FP->type <= T_OBJECT && all_statements.size() == 1){ + if (auto re = dyn_cast(all_statements.front()->expr)){ + if (re->retval == NULL){ + //Standard empty function + + delete all_statements.front(); + all_statements.clear(); + + static void (* const funcs[17])(void) = {func_void, func_boolean, func_byte, func_short, func_integer, + func_long, func_single, func_float, func_date, func_string, func_string, + func_pointer, func_variant, NULL, NULL, NULL, func_object}; + CP->jit_functions[EXEC.index] = funcs[FP->type]; + + return; + } + } + } + + static bool inited = false; + if (!inited){ + llvm_init(); + inited = true; + } + + M = new llvm::Module("jit_mod", llvm_context); + if (TARGET_BITS == 64){ + M->setTargetTriple("x86_64-pc-linux-gnu"); + M->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); + } else { + M->setTargetTriple("i386-unknown-linux-gnu"); + M->setDataLayout("e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"); + } + if (EE) + EE->addModule(M); + else + EE = llvm::EngineBuilder(M)/*.setOptLevel(llvm::CodeGenOpt::Aggressive)*/.create(); + + + static int counter = 0; + char buf[256]; + sprintf(buf, "func_%d_%s_%d", counter++, CP->name, EXEC.index); + llvm_function = llvm::cast(M->getOrInsertFunction(buf, get_function_type('v', ""))); + + entry_block = create_bb("entry"); + builder = new llvm::IRBuilder<>(entry_block); + + //builder->CreateCall(get_global_function((void*)&puts, 'v', "p"), get_global((void*)"hej", llvmType(getInt8Ty))); + //builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"enter %d\n", llvmType(getInt8Ty)), getInteger(32, counter-1)); + + current_op = OP == NULL ? get_nullptr() : read_global((void*)&OP); + temp_value = builder->CreateAlloca(value_type); + temp_value2 = builder->CreateAlloca(value_type); + temp_voidptr = builder->CreateAlloca(llvmType(getInt8PtrTy)); + temp_int = builder->CreateAlloca(llvmType(getInt32Ty)); + temp_double = builder->CreateAlloca(llvmType(getDoubleTy)); + temp_date = builder->CreateAlloca(date_type); + temp_2longs = builder->CreateAlloca(two_longs_type); + temp_errcontext1 = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8Ty), sizeof(ERROR_CONTEXT))); + temp_errcontext2 = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8Ty), sizeof(ERROR_CONTEXT))); + temp_got_error = builder->CreateAlloca(llvmType(getInt1Ty)); + temp_got_error2 = builder->CreateAlloca(llvmType(getInt1Ty)); + if (ngosubs != 0){ + //temp_gosub_stack = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8PtrTy), ngosubs + 100)); + /*temp_num_gosubs_on_stack = builder->CreateAlloca(llvmType(getInt32Ty)); + builder->CreateStore(getInteger(32, 0), temp_num_gosubs_on_stack);*/ + +#ifndef GOSUB_ON_STACK + builder->CreateCall3(get_global_function(GB.NewArray, 'v', "pii"), + get_global(&GP, llvmType(getInt8Ty)), getInteger(32, sizeof(STACK_GOSUB)), getInteger(32, 0)); +#endif + + /*gosub_return_point = builder->CreateAlloca(llvmType(getInt8PtrTy)); + builder->CreateStore(get_nullptr(), gosub_return_point);*/ + gosub_return_point = builder->CreateAlloca(llvmType(getInt16Ty)); + builder->CreateStore(getInteger(16, 0), gosub_return_point); + } + //For GoSubs and JR_try_unwind + gp = builder->CreateAlloca(pointer_t(value_type)); + builder->CreateStore(builder->CreateGEP(read_global((void*)&BP, pointer_t(value_type)), getInteger(TARGET_BITS, FP->n_local + FP->n_ctrl)), gp); + + has_tries = false; + try_blocks.clear(); + + init_locals(); + + codegen_statements(); + + finish_gosub_returns(); + create_return(); + + insert_pending_branches(); + //puts("Dump:"); + + //Print out the code before optimization + //M->dump(); + + llvm::verifyModule(*M); + + for(size_t i=0, e=all_statements.size(); i!=e; i++) + delete all_statements[i]; + all_statements.clear(); + + fix_setjmps(); + + run_optimizations(); + + //Print out the code after optimization + if (MAIN_debug){ + debug_print_line(); + fprintf(stderr, "gb.jit: dumping function %s.", CP->name); + if (FP->debug) + fprintf(stderr, "%s:\n", FP->debug->name); + else + fprintf(stderr, "%d:\n", EXEC.index); + debug_print_line(); + M->dump(); + debug_print_line(); + fputc('\n', stderr); + } + + void (*fn)(void) = (void(*)(void))EE->getPointerToFunction(llvm_function); + + delete builder; + + llvm_function->deleteBody(); + + CP->jit_functions[EXEC.index] = fn; + /*if (CP->jit_data == NULL){ + ALLOC_ZERO(&CP->jit_data, sizeof(llvm::ExecutionEngine*) * CP->load->n_func, "JIT_codegen"); + } + ((llvm::ExecutionEngine**)CP->jit_data)[EXEC.index] = EE;*/ + + mappings.clear(); + //variable_mappings.clear(); +} + + +static void JIT_cleanup(llvm::ExecutionEngine** EE, int n_func){ + /*for(int i=0; ifreeMachineCodeForFunction(EE[i]->FindFunctionNamed("func")); + delete EE[i]; + } + }*/ +} + +void JIT_end(){ + delete EE; + llvm::llvm_shutdown(); +} + +///DEBUG +void print_type(llvm::Value* v){ + v->getType()->print(llvm::outs()); + puts(""); +} +void print_expr_type(Expression* expr){ + puts(typeid(*expr).name()); +} diff --git a/gb.jit.llvm/src/jit_codegen_conv.h b/gb.jit.llvm/src/jit_codegen_conv.h new file mode 100644 index 00000000..09e30f6f --- /dev/null +++ b/gb.jit.llvm/src/jit_codegen_conv.h @@ -0,0 +1,786 @@ +/*************************************************************************** + + jit_codegen_conv.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_CODEGEN_CONV_H +#define __JIT_CODEGEN_CONV_H + +extern "C" { +#include "gbx_number.h" +#include "gb_common_buffer.h" +} + +#ifdef __CYGWIN__ +#define __finite finite +#endif + +llvm::Value* JIT_conv_to_variant(Expression* value, llvm::Value* val, bool on_stack, bool* no_ref_variant){ + llvm::Value* ret; + if (TYPE_is_string(value->type)){ + ret = string_for_array_or_variant(val, value->type); + ret = get_new_struct(variant_type, getInteger(TARGET_BITS, T_STRING), builder->CreatePtrToInt(ret, llvmType(getInt64Ty))); + } else { + if (value->type < T_OBJECT && no_ref_variant) + *no_ref_variant = true; + + llvm::Value* data; + llvm::Type* t64 = llvmType(getInt64Ty); + + if (value->type < T_OBJECT) + ret = get_new_struct(variant_type, getInteger(TARGET_BITS, value->type)); + else + ret = get_new_struct(variant_type, builder->CreatePtrToInt(extract_value(val, 0), LONG_TYPE)); + + switch(value->type){ + case T_BYTE: + data = builder->CreateZExt(val, t64); + break; + case T_BOOLEAN: + case T_SHORT: + case T_INTEGER: + data = builder->CreateSExt(val, t64); + break; + case T_LONG: + data = val; + break; + case T_SINGLE: + data = builder->CreateBitCast(val, llvmType(getInt32Ty)); + data = builder->CreateZExt(data, t64); + break; + case T_FLOAT: + data = builder->CreateBitCast(val, t64); + break; + case T_DATE: + data = builder->CreateShl(builder->CreateZExt(extract_value(val, 1), t64), getInteger(64, 32)); + data = builder->CreateOr(data, builder->CreateZExt(extract_value(val, 0), t64)); + break; + case T_POINTER: + data = builder->CreatePtrToInt(val, t64); + break; + case T_CLASS: + assert(dynamic_cast(value)); + data = getInteger(64, (uint64_t)(void*)((PushClassExpression*)value)->klass); + val = builder->CreateIntToPtr(data, llvmType(getInt8PtrTy)); + break; + case T_NULL: + break; + default: + data = builder->CreatePtrToInt(extract_value(val, 1), t64); + break; + } + if (value->type != T_NULL) + ret = insert_value(ret, data, 1); + + if (on_stack){ //FIXME is this code really good/correct? stack seems strange + c_SP(-value->on_stack+1); + llvm::Value* addr = builder->CreateBitCast(get_value_on_top_addr(), pointer_t(LONG_TYPE)); + + builder->CreateStore(getInteger(TARGET_BITS, T_VARIANT), addr); + + addr = builder->CreateGEP(addr, getInteger(TARGET_BITS, 1)); + if (value->type < T_OBJECT) + builder->CreateStore(getInteger(TARGET_BITS, value->type), addr); + else + builder->CreateStore(builder->CreatePtrToInt(extract_value(val, 0), LONG_TYPE), addr); + + if (value->type != T_NULL){ + addr = builder->CreateGEP(addr, getInteger(TARGET_BITS, 1)); + if (value->type == T_BYTE) + builder->CreateStore(builder->CreateZExt(val, llvmType(getInt32Ty)), builder->CreateBitCast(addr, llvmType(getInt32PtrTy))); + else if (value->type < T_INTEGER) + builder->CreateStore(builder->CreateSExt(val, llvmType(getInt32Ty)), builder->CreateBitCast(addr, llvmType(getInt32PtrTy))); + else if (value->type < T_OBJECT) + builder->CreateStore(val, builder->CreateBitCast(addr, pointer_t(TYPE_llvm(value->type)))); + else + builder->CreateStore(extract_value(val, 1), builder->CreateBitCast(addr, charPP)); + } + return ret; + } + } + c_SP(-value->on_stack+on_stack); + if (on_stack) + set_top_value(ret, T_VARIANT); + return ret; +} + +llvm::Value* ConvExpression::codegen_get_value() +{ + Expression* value = expr; + static const void *jump[16][16] = + { + /* ,-------> void b c h i l g f d cs s p v func class n */ + // | + /* void */ { &&__OK, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, }, + /* b */ { &&__N, &&__OK, &&__b2c, &&__b2h, &&__b2i, &&__b2l, &&__b2g, &&__b2f, &&__N, &&__b2s, &&__b2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* c */ { &&__N, NULL, &&__OK, &&__c2h, &&__c2i, &&__c2l, &&__c2g, &&__c2f, &&__c2d, &&__c2s, &&__c2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* h */ { &&__N, NULL, &&__h2c, &&__OK, &&__h2i, &&__h2l, &&__h2g, &&__h2f, &&__h2d, &&__h2s, &&__h2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* i */ { &&__N, NULL, &&__i2c, &&__i2h, &&__OK, &&__i2l, &&__i2g, &&__i2f, &&__i2d, &&__i2s, &&__i2s, &&__i2p, &&__2v, &&__N, &&__N, &&__N, }, + /* l */ { &&__N, NULL, &&__l2c, &&__l2h, &&__l2i, &&__OK, &&__l2g, &&__l2f, &&__l2d, &&__l2s, &&__l2s, &&__l2p, &&__2v, &&__N, &&__N, &&__N, }, + /* g */ { &&__N, NULL, &&__g2c, &&__g2h, &&__g2i, &&__g2l, &&__OK, &&__g2f, &&__g2d, &&__g2s, &&__g2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* f */ { &&__N, NULL, &&__f2c, &&__f2h, &&__f2i, &&__f2l, &&__f2g, &&__OK, &&__f2d, &&__f2s, &&__f2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* d */ { &&__N, &&__d2b, &&__d2c, &&__d2h, &&__d2i, &&__d2l, &&__d2g, &&__d2f, &&__OK, &&__d2s, &&__d2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* cs */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__N, &&__s2v, &&__N, &&__N, &&__N, }, + /* s */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__N, &&__s2v, &&__N, &&__N, &&__N, }, + /* p */ { &&__N, &&__N, &&__N, &&__N, &&__p2i, &&__p2l, &&__N, &&__N, &&__N, &&__N, &&__N, &&__OK, &&__2v, &&__N, &&__N, &&__N, }, + /* v */ { &&__N, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__OK, &&__N, &&__v2, &&__v2, }, + /* func */ { &&__N, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__F2p, &&__func, &&__OK, &&__N, &&__func, }, + /* class */ { &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__2v, &&__N, &&__OK, &&__N, }, + /* null */ { &&__N, NULL, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, NULL, NULL, NULL, &&__N, &&__2v, &&__N, &&__N, &&__OK, }, + }; + + llvm::Value* val = NULL; + llvm::Value* ret; + + if (value->type == (TYPE)-1) + goto __UNKNOWN; + else if ((type | value->type) >> 4) + goto __OBJECT; + else { + if (value->type != T_FUNCTION && value->type != T_CLASS) + val = value->codegen_get_value(); + goto *jump[value->type][type]; + } + + +__d2b: + ret = builder->CreateICmpNE(builder->CreateOr(extract_value(val, 0), extract_value(val, 1)), getInteger(32, 0)); + goto __DONE; + +__b2c: +__b2h: +__b2i: +__h2i: +__b2l: +__h2l: +__i2l: + + ret = builder->CreateSExt(val, TYPE_llvm(type)); + goto __DONE; + +__h2c: +__i2c: +__l2c: +__i2h: +__l2h: +__l2i: + + ret = builder->CreateTrunc(val, TYPE_llvm(type)); + goto __DONE; + +__g2c: +__f2c: + + ret = builder->CreateFPToUI(val, llvmType(getInt8Ty)); + goto __DONE; + +__c2h: +__c2i: +__c2l: + + ret = builder->CreateZExt(val, TYPE_llvm(type)); + goto __DONE; + +__g2h: +__f2h: +__g2i: +__f2i: +__g2l: +__f2l: + + ret = builder->CreateFPToSI(val, TYPE_llvm(type)); + goto __DONE; + +__p2i: +__p2l: + + ret = builder->CreatePtrToInt(val, TYPE_llvm(type)); + goto __DONE; + +__c2g: +__c2f: + + ret = builder->CreateUIToFP(val, TYPE_llvm(type)); + goto __DONE; + +__b2g: +__h2g: +__i2g: +__b2f: +__h2f: +__i2f: +__l2f: + + ret = builder->CreateSIToFP(val, TYPE_llvm(type)); + goto __DONE; + +__l2g: + + ret = builder->CreateSIToFP(val, TYPE_llvm(type)); + goto __test_overflow; + + +__f2g: + + ret = builder->CreateFPTrunc(val, llvmType(getFloatTy)); + goto __test_overflow; + +__test_overflow: +{ + llvm::Value* ret64 = type == T_SINGLE ? builder->CreateFPExt(ret, llvmType(getDoubleTy)) : ret; + llvm::Value* res = builder->CreateCall(get_global_function(__finite, 'i', "d"), ret64); + + gen_if_noreturn(builder->CreateICmpEQ(res, getInteger(32, 0)), [&](){ + create_throw(E_OVERFLOW); + }, "test_overflow"); + + goto __DONE; +} + +__g2f: + + ret = builder->CreateFPExt(val, llvmType(getDoubleTy)); + goto __DONE; + +__c2d: +__h2d: +__i2d: + + ret = val; + if (value->type != T_INTEGER){ + if (value->type == T_BYTE) + ret = builder->CreateZExt(ret, llvmType(getInt32Ty)); + else + ret = builder->CreateSExt(ret, llvmType(getInt32Ty)); + } + ret = gen_max(getInteger(32, 0), ret); + ret = get_new_struct(date_type, ret, getInteger(32, 0)); + goto __DONE; + +__l2d: + + ret = builder->CreateSelect( + builder->CreateICmpSLT(val, getInteger(64, 0)), + getInteger(32, 0), + builder->CreateSelect( + builder->CreateICmpSGT(val, getInteger(64, INT_MAX)), + getInteger(32, INT_MAX), + builder->CreateTrunc(val, llvmType(getInt32Ty)) + ) + ); + ret = get_new_struct(date_type, ret, getInteger(32, 0)); + goto __DONE; + +__g2d: +{ + llvm::Value* ival = builder->CreateCall(get_global_function(floorf, 'f', "f"), val); + llvm::Value* timepart = builder->CreateFPToSI(builder->CreateFAdd(builder->CreateFMul(builder->CreateFSub(val, ival), getFloat(86400000.0f)), getFloat(0.5f)), llvmType(getInt32Ty)); + ret = get_new_struct(date_type, builder->CreateFPToSI(ival, llvmType(getInt32Ty)), timepart); + goto __DONE; +} + +__f2d: +{ + llvm::Value* ival = builder->CreateCall(get_global_function(floor, 'd', "d"), val); + llvm::Value* timepart = builder->CreateFPToSI(builder->CreateFAdd(builder->CreateFMul(builder->CreateFSub(val, ival), getFloat(86400000.0)), getFloat(0.5)), llvmType(getInt32Ty)); + ret = get_new_struct(date_type, builder->CreateFPToSI(ival, llvmType(getInt32Ty)), timepart); + goto __DONE; +} + +__d2c: +__d2h: +__d2i: +__d2l: + + ret = extract_value(val, 0); + if (type < T_INTEGER) + ret = builder->CreateTrunc(ret, TYPE_llvm(type)); + else if (type == T_LONG) + ret = builder->CreateSExt(ret, llvmType(getInt64Ty)); + goto __DONE; + +__d2g: +{ + llvm::Value* datepart = builder->CreateSIToFP(extract_value(val, 0), llvmType(getFloatTy)); + llvm::Value* timepart = builder->CreateSIToFP(extract_value(val, 1), llvmType(getFloatTy)); + + ret = builder->CreateFAdd(datepart, builder->CreateFDiv(timepart, getFloat(86400000.0f))); + goto __DONE; +} + +__d2f: +{ + llvm::Value* datepart = builder->CreateSIToFP(extract_value(val, 0), llvmType(getDoubleTy)); + llvm::Value* timepart = builder->CreateSIToFP(extract_value(val, 1), llvmType(getDoubleTy)); + + ret = builder->CreateFAdd(datepart, builder->CreateFDiv(timepart, getFloat(86400000.0))); + goto __DONE; +} + +__b2s: + + ret = builder->CreateSelect(val, + get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_global((void*)"T", llvmType(getInt8Ty)), getInteger(32, 0), getInteger(32, 1)), + get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)) + ); + goto __DONE; + +__c2s: +__h2s: +__i2s: +__l2s: +{ + llvm::Value* num; + if (value->type == T_BYTE) + num = builder->CreateZExt(val, llvmType(getInt64Ty)); + else if (value->type == T_LONG) + num = val; + else + num = builder->CreateSExt(val, llvmType(getInt64Ty)); + + c_SP(-value->on_stack+on_stack); + + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + builder->CreateCall4(get_global_function_jif(NUMBER_int_to_string, 'v', "liip"), + num, getInteger(32, 0), getInteger(32, 10), builder->CreateBitCast(addr, llvmType(getInt8PtrTy))); + + ret = read_value(addr, T_STRING); + borrow(ret, T_STRING); + return ret; +} + +__g2s: +__f2s: +{ + c_SP(-value->on_stack+on_stack); + + if (value->type == T_SINGLE) + val = builder->CreateFPExt(val, llvmType(getDoubleTy)); + + llvm::Value* args[] = { + val, getInteger(32, LF_GENERAL_NUMBER), get_nullptr(), getInteger(32, 0), + builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)), + builder->CreateBitCast(temp_int, llvmType(getInt8PtrTy)), getInteger(8, 0) + }; + /*llvm::Value* got_error =*/ builder->CreateCall(get_global_function_jif(LOCAL_format_number, 'c', "dipippc"), args); + + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + builder->CreateLoad(temp_voidptr), + builder->CreateLoad(temp_int) + ); + + ret = read_value(addr, T_STRING); + borrow(ret, T_STRING); + return ret; +} + +__d2s: +{ + llvm::Value* src_addr = value->on_stack ? get_value_on_top_addr() : temp_value; + if (!value->on_stack) + store_value(src_addr, val, T_DATE, false); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + static char buffer[128]; + + llvm::Value* len = builder->CreateCall2(get_global_function_jif(DATE_to_string, 'i', "pp"), + get_global((void*)buffer, llvmType(getInt8Ty)), + builder->CreateBitCast(src_addr, llvmType(getInt8PtrTy)) + ); + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + get_global((void*)buffer, llvmType(getInt8Ty)), + len + ); + + ret = read_value(addr, T_STRING); + borrow(ret, T_STRING); + return ret; +} + +__s2b: + + ret = builder->CreateICmpNE(extract_value(val, 3), getInteger(32, 0)); + release(val, value->type); + goto __DONE; + +__s2c: +__s2h: +__s2i: +__s2l: +{ + auto str = get_string_len(val); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = (on_stack && (type == T_INTEGER || type == T_LONG)) ? get_value_on_top_addr() : temp_value; + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(NUMBER_from_string, 'c', "ipip"), + getInteger(32, type == T_LONG ? NB_READ_LONG : NB_READ_INTEGER), str.first, str.second, + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)) + ); + + release(val, value->type); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, 0)), [&](){ + create_throw(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + }); + + llvm::Value* intval = read_value(addr, type == T_LONG ? T_LONG : T_INTEGER); + + if (type < T_INTEGER) + intval = builder->CreateTrunc(intval, TYPE_llvm(type)); + + if (on_stack && addr == temp_value) + set_top_value(intval, type); + + return intval; +} + +__s2g: +__s2f: +{ + auto str = get_string_len(val); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = (on_stack && type == T_FLOAT) ? get_value_on_top_addr() : temp_value; + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(NUMBER_from_string, 'c', "ipip"), + getInteger(32, NB_READ_FLOAT), str.first, str.second, + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)) + ); + + release(val, value->type); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, 0)), [&](){ + create_throw(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + }); + + llvm::Value* ret = read_value(addr, T_FLOAT); + + if (type == T_SINGLE) + ret = builder->CreateFPTrunc(ret, llvmType(getFloatTy)); + + if (on_stack && addr == temp_value) + set_top_value(ret, type); + + return ret; +} + +__s2d: +{ + auto str = get_string_len(val); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(DATE_from_string, 'c', "pipc"), + str.first, str.second, + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + getInteger(8, false) + ); + + release(val, value->type); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, 0)), [&](){ + create_throw(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + }); + + return read_value(addr, T_DATE); +} + + /*addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (DATE_from_string(value->_string.addr + value->_string.start, value->_string.len, value, FALSE)) + goto __N; + + STRING_unref(&addr); + return;*/ + +__UNKNOWN: + value->on_stack = true; + value->codegen_on_stack(); +__v2: + + builder->CreateCall2(get_global_function_jif(VALUE_convert, 'v', "pj"), + builder->CreateBitCast(get_value_on_top_addr(), llvmType(getInt8PtrTy)), + getInteger(TARGET_BITS, type)); + return ret_top_stack(type, on_stack); + + /*VALUE_undo_variant(value); + goto __CONV;*/ + +__s2v: +__2v: + return JIT_conv_to_variant(value, val, on_stack, &no_ref_variant); + + /* VALUE_put ne fonctionne pas avec T_STRING ! */ + /*if (value->type != T_NULL) + VALUE_put(value, &value->_variant.value, value->type); + + value->_variant.vtype = value->type; + value->type = T_VARIANT; + return;*/ + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__i2p: +__l2p: + + ret = builder->CreateIntToPtr(val, llvmType(getInt8PtrTy)); + goto __DONE; + +__F2p: + + //FIXME + abort(); + /*value->_pointer.value = EXTERN_make_callback(&value->_function); + value->type = T_POINTER; + return;*/ + +__OBJECT: +{ + if (!TYPE_is_object(type)){ + val = value->codegen_get_value(); + if (type == T_BOOLEAN){ + llvm::Value* obj = extract_value(val, 1); + ret = builder->CreateICmpNE(obj, get_nullptr()); + unref_object(obj); + goto __DONE; + } + //type == T_VARIANT + goto __2v; + } + + if (!TYPE_is_object(value->type)){ + if (value->type == T_NULL){ + value->on_stack = false; + ret = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()); + goto __DONE; + } + + if (value->type == T_VARIANT){ + //Variant to Object + val = value->codegen_get_value(); + goto __v2; + } + + if (value->type == T_CLASS){ + llvm::Value* object = get_global((void*)((PushClassExpression*)value)->klass, llvmType(getInt8Ty)); + + val = get_new_struct(object_type, + get_global((void*)GB.FindClass("Class"), llvmType(getInt8Ty)), + object); + + borrow_object_no_nullcheck(object); + + //To simplify code below + value->on_stack = false; + value->type = (TYPE)(void*)GB.FindClass("Class"); + } + } + + if (val == NULL) + val = value->codegen_get_value(); + + /*llvm::Value* klass = extract_value(val, 0);*/ + llvm::Value* object = extract_value(val, 1); + llvm::Value* to_class = get_global((void*)type, llvmType(getInt8Ty)); + + if (type == T_OBJECT){ + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, T_OBJECT), llvmType(getInt8PtrTy)), object); + goto __DONE; + } + + c_SP(-value->on_stack); + + ret = gen_if_else_phi(builder->CreateICmpEQ(object, get_nullptr()), [&](){ + //OK + return get_new_struct(object_type, to_class, object); + }, [&](){ + if (value->type == T_OBJECT){ + //klass = load_element(builder->CreateBitCast(klass, pointer_t(OBJECT_TYPE)), 0); + + //return get_new_struct(object_type, to_class, builder->CreateCall3(get_global_function(JR_conv_from_T_OBJECT, 'p', "pp"), object, to_class)); + } + + /*//Check if klass is virtual + llvm::Value* class_flags = builder->CreateBitCast(builder->CreateGEP(klass, getInteger(TARGET_BITS, TARGET_BITS == 64 ? 32 : 20)), llvmType(getInt32Ty)); + llvm::Value* is_virtual = builder->CreateTrunc(builder->CreateLShr(class_flags, getInteger(32, 10)), llvmType(getInt1Ty)); + + gen_if_noreturn(is_virtual, [&](){ + create_throw(E_VIRTUAL); + }, "virtual_class");*/ + + if (value->type != T_OBJECT){ + if (JIF.F_CLASS_inherits((CLASS*)(void*)value->type, (CLASS*)(void*)type)){ + //This cast is always possible + return get_new_struct(object_type, to_class, object); + } + + /*llvm::Value* first_invalid_test = builder->CreateICmpEQ(builder->CreateCall2(get_global_function(CLASS_inherits, 'c', "pp"), klass, to_class), getInteger(8, false)); + + llvm::Value* invalid_cast = gen_and_if(builder->CreateICmpNE(klass, to_class), [&](){ + return builder->CreateICmpEQ(builder->CreateCall2(get_global_function(CLASS_inherits, 'c', "pp"), klass, to_class), getInteger(8, false)); + });*/ + } + return get_new_struct(object_type, to_class, builder->CreateCall2(get_global_function(JR_object_cast, 'p', "pp"), object, to_class)); + + }); + + if (on_stack) + push_value(ret, type); + return ret; +} +#if 0 + if (!TYPE_is_object(type)) + { + if (type == T_BOOLEAN) + { + test = (value->_object.object != NULL); + OBJECT_UNREF(value->_object.object, "VALUE_convert"); + value->_boolean.value = -test; + value->type = T_BOOLEAN; + return; + } + + if (type == T_VARIANT) + goto __2v; + + goto __N; + } + + if (!TYPE_is_object(value->type)) + { + if (value->type == T_NULL) + { + OBJECT_null(value, (CLASS *)type); /* marche aussi pour type = T_OBJECT */ + goto __TYPE; + } + + if (value->type == T_VARIANT) + goto __v2; + + if (value->type == T_FUNCTION) + goto __func; + + if (value->type == T_CLASS) + { + klass = value->_class.klass; + + if (CLASS_is_virtual(klass)) + THROW(E_VIRTUAL); + + CLASS_load(klass); + + if (klass->auto_create) + value->_object.object = CLASS_auto_create(klass, 0); + else + value->_object.object = klass; + + OBJECT_REF(value->_object.object, "VALUE_convert"); + value->type = T_OBJECT; + /* on continue... */ + } + else + goto __N; + } + + if (value->_object.object == NULL) + goto __TYPE; + + if (value->type == T_OBJECT) + { + /*if (value->_object.object == NULL) + goto __TYPE;*/ + + klass = OBJECT_class(value->_object.object); + /* on continue */ + } + else + klass = value->_object.klass; + + if (CLASS_is_virtual(klass)) + THROW(E_VIRTUAL); + + if (type == T_OBJECT) + goto __TYPE; +#endif + +/* + if ((klass == (CLASS *)type) || CLASS_inherits(klass, (CLASS *)type)) + goto __TYPE; + + if (value->type != T_OBJECT && value->_object.object) + { + klass = OBJECT_class(value->_object.object); + value->type = T_OBJECT; + goto __RETRY; + } + + if (klass->special[SPEC_CONVERT] != NO_SYMBOL) + { + void *conv = ((void *(*)())(CLASS_get_desc(klass, klass->special[SPEC_CONVERT])->constant.value._pointer))(value->_object.object, type); + if (conv) + { + OBJECT_REF(conv, "VALUE_conv"); + OBJECT_UNREF(value->_object.object, "VALUE_conv"); + value->_object.object = conv; + goto __TYPE; + } + } + + THROW(E_TYPE, TYPE_get_name(type), TYPE_get_name((TYPE)klass));*/ + +__DONE: + + c_SP(-value->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; + +__OK: + + abort(); + return val; + +__N: + + THROW(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + +__NR: + + THROW(E_NRETURN); +} + +#endif /* __JIT_CODEGEN_CONV_H */ diff --git a/gb.jit.llvm/src/jit_compile.cpp b/gb.jit.llvm/src/jit_compile.cpp new file mode 100644 index 00000000..4b88981f --- /dev/null +++ b/gb.jit.llvm/src/jit_compile.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + + jit_compile.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_COMPILE_C + +#include +#include +#include "jit.h" + +static std::vector all_expressions; //Save all expressions so they can be deleted +static std::vector > ctrl_types; //second contains a class if first is T_CLASS +static std::vector > used_ctrl_types; //0 = primitive type, 1 = string, 2 = object, 3 = variant +std::vector all_statements; +std::vector classes_to_load; +int ngosubs; +uint64_t func_byref_mask; + +void register_new_expression(Expression* expr){ + all_expressions.push_back(expr); +} + +void free_all_expressions(){ + for(size_t i=0, e=all_expressions.size(); i!=e; i++){ + //printf("%d %p\n", i, all_expressions[i]); + delete all_expressions[i]; + } + all_expressions.clear(); +} + +CLASS* get_ctrl_class(int index){ + return ctrl_types[index - FP->n_local].second; +} +TYPE get_ctrl_type(int index){ + return ctrl_types[index - FP->n_local].first; +} + +int special_ctrl_type(TYPE type){ + int t = 0; + if (TYPE_is_string(type)) + t = 1; + else if (TYPE_is_object(type)) + t = 2; + else if (TYPE_is_variant(type)) + t = 3; + return t; +} + +void set_ctrl_type(TYPE type, int index, CLASS* second){ + ctrl_types[index - FP->n_local] = std::make_pair(type, second); + used_ctrl_types[index - FP->n_local][special_ctrl_type(type)] = 1; +} + +bool is_ctrl_type_used(int type, int index){ + return used_ctrl_types[index - FP->n_local][type]; +} + +void JIT_load_class(CLASS* klass){ + if (klass->ready) + return; + + JIF.F_CLASS_load_from_jit(klass); + if (!klass->is_array) + classes_to_load.push_back(klass); +} + +static void print_line() +{ + int i; + + for (i = 1; i < 10; i++) + fputs("--------", stderr); + fputc('\n', stderr); +} + +void JIT_compile_and_execute(){ + if (MAIN_debug){ + print_line(); + fprintf(stderr, "gb.jit: beginning compiling %s.", CP->name); + if (FP->debug) + fprintf(stderr, "%s:\n", FP->debug->name); + else + fprintf(stderr, "%d:\n", EXEC.index); + print_line(); + fputc('\n', stderr); + } + + ctrl_types.resize(FP->n_ctrl); + used_ctrl_types.resize(FP->n_ctrl); + ngosubs = 0; + size_t load_classes_size = classes_to_load.size(); + TRY { + JIT_read(); + } CATCH { + classes_to_load.resize(load_classes_size); + JIF.F_ERROR_propagate(); + } END_TRY + JIT_codegen(); + free_all_expressions(); + /*for(size_t i=0, e=all_statements.size(); i!=e; i++) + delete all_statements[i]; + all_statements.clear();*/ + + void (*fn)(void) = CP->jit_functions[EXEC.index]; + + std::reverse(classes_to_load.begin() + load_classes_size, classes_to_load.end()); + + while(classes_to_load.size() > load_classes_size){ + CLASS* klass = classes_to_load.back(); classes_to_load.pop_back(); + + klass->loaded = true; + klass->ready = true; + + JIF.F_CLASS_run_inits(klass); + } + + (*fn)(); +} diff --git a/gb.jit.llvm/src/jit_conv.cpp b/gb.jit.llvm/src/jit_conv.cpp new file mode 100644 index 00000000..6acecd7a --- /dev/null +++ b/gb.jit.llvm/src/jit_conv.cpp @@ -0,0 +1,280 @@ +/*************************************************************************** + + jit_conv.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_CONV_C + +#include "jit.h" + +static const int type_to_bits[] = {0, 1, 8, 16, 32, 64, 32, 64}; + +void JIT_conv(Expression*& value, TYPE type, Expression* other){ + static const void *jump[16][16] = + { + /* ,-------> void b c h i l g f d cs s p v func class n */ + // | + /* void */ { &&__OK, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, }, + /* b */ { &&__N, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* c */ { &&__N, &&__c2b, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* h */ { &&__N, &&__h2b, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* i */ { &&__N, &&__i2b, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__N, &&__N, }, + /* l */ { &&__N, &&__l2b, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE2, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__N, &&__N, }, + /* g */ { &&__N, &&__g2b, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* f */ { &&__N, &&__f2b, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE2, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* d */ { &&__N, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* cs */ { &&__N, &&__TYPE, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__OK, &&__OK, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* s */ { &&__N, &&__TYPE, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__OK, &&__OK, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* p */ { &&__N, &&__N, &&__N, &&__N, &&__TYPE, &&__TYPE, &&__N, &&__N, &&__N, &&__N, &&__N, &&__OK, &&__TYPE, &&__N, &&__N, &&__N, }, + /* v */ { &&__N, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__OK, &&__N, &&__v2, &&__v2, }, + /* func */ { &&__N, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__F2p, &&__func, &&__OK, &&__N, &&__func, }, + /* class */ { &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__TYPE, &&__N, &&__OK, &&__N, }, + /* null */ { &&__N, &&__n2b, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__n2d, &&__n2s, &&__n2s, &&__N, &&__TYPE, &&__N, &&__N, &&__OK, }, + }; + + if (type == T_CSTRING) + type = T_STRING; + + if (value->type == (TYPE)-1) //Unknown, so always do a VALUE_convert + goto __TYPE2; + else if ((type | value->type) >> 4) + goto __OBJECT; + else + goto *jump[value->type][type]; + +__c2b: +__h2b: +__i2b: +__l2b: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + Expression* arr[] = {value, new PushIntegerExpression(type_to_bits[value->type], 0)}; + value = new NotExpression(new EqExpression(arr)); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__g2b: +__f2b: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + Expression* arr[] = {value, new PushFloatExpression(type_to_bits[value->type], 0.0)}; + value = new NotExpression(new EqExpression(arr)); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__n2b: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + //delete value; + value = new PushIntegerExpression(1, false); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__n2d: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + //delete value; + value = new PushVoidDateExpression(); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__n2s: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + //delete value; + value = new PushCStringExpression(NULL, 0, 0); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__F2p: { + if (PushFunctionExpression* pfe = dynamic_cast(value)){ + bool on_stack_save = value->on_stack; + VALUE_FUNCTION v; + v.type = T_FUNCTION; + v.klass = CP; + v.object = OP; + v.kind = FUNCTION_PRIVATE; + v.index = pfe->index; + v.defined = true; + if (OP) + ((OBJECT*)OP)->ref++; + value = new PushIntegerExpression(8*sizeof(void*), (int64_t)(void*)JIF.F_EXTERN_make_callback(&v)); + JIT_conv(value, T_POINTER); + value->on_stack = on_stack_save; + return; + } + assert(false && "Not implemented yet!"); +} + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__v2: + + ref_stack(); + value->must_on_stack(); + value = new ConvExpression(value, type); + value->on_stack = true; + return; + +__OBJECT: + + if (TYPE_is_pure_object(type)) + JIT_load_class((CLASS*)(void*)type); + + if (TYPE_is_pure_object(value->type)) + JIT_load_class((CLASS*)(void*)value->type); + + if (!TYPE_is_object(type)) + { + if (type == T_BOOLEAN) + { + goto __TYPE; + } + + if (type == T_VARIANT) + goto __TYPE; + + goto __N; + } + + if (!TYPE_is_object(value->type)) + { + if (value->type == T_NULL) + { + goto __TYPE; + } + + if (value->type == T_VARIANT) + goto __TYPE2; + + if (value->type == T_FUNCTION) + goto __func; + + if (value->type == T_CLASS) + { + //Virtual, Auto create or 'Class' object + PushClassExpression* pce = dyn_cast(value); + assert(pce); + + CLASS* klass = pce->klass; + + if (CLASS_is_virtual(klass)) + THROW(E_VIRTUAL); + + if (klass->auto_create){ + Expression* tmp = new PushAutoCreateExpression(klass); + tmp->on_stack = value->on_stack; + tmp->stack_if_ref = value->stack_if_ref; + //delete value; + value = tmp; + } else { + //Get the 'Class' object from the object + if (type != T_OBJECT && type != GB.FindClass("Class")) + goto __TYPE2; //This only works if there is a SPEC_CONVERT + goto __TYPE; + } + } + else + goto __N; + } + + if (type == value->type) + return; + + if (value->type != T_OBJECT) + if (((CLASS*)(void*)value->type)->is_virtual) + THROW(E_VIRTUAL); + + /*if (value->type != T_OBJECT && type != T_OBJECT){ + if (!CLASS_inherits((CLASS*)(void*)value->type, (CLASS*)(void*)type) + && !CLASS_inherits((CLASS*)(void*)type, (CLASS*)(void*)value->type)){ + goto __N; + } + } Does not work for e.g. Integer[] -> Float[] ...*/ + + if (type == T_OBJECT) + goto __TYPE; + else + goto __TYPE2; + + + +__TYPE: { + + bool on_stack = value->on_stack; + bool stack_if_ref = value->stack_if_ref; + //value->on_stack = false; + //value->stack_if_ref = false; + value = new ConvExpression(value, type); + value->on_stack = on_stack; + value->stack_if_ref = stack_if_ref; + return; +} + +__TYPE2: { + + if (other) + other->must_on_stack(); + ref_stack(); + bool on_stack = value->on_stack; + bool stack_if_ref = value->stack_if_ref; + value->on_stack = true; + value = new ConvExpression(value, type); + value->on_stack = on_stack; + value->stack_if_ref = stack_if_ref; + + +} + +__OK: + + return; + +__N: + + THROW(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + +__NR: + + THROW(E_NRETURN); +} diff --git a/gb.jit.llvm/src/jit_expressions.cpp b/gb.jit.llvm/src/jit_expressions.cpp new file mode 100644 index 00000000..2a97bbbf --- /dev/null +++ b/gb.jit.llvm/src/jit_expressions.cpp @@ -0,0 +1,1872 @@ +/*************************************************************************** + + jit_expressions.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_EXPRESSIONS_C + +#include "jit.h" +#include "jit_runtime.h" + +static void ref_variant(Expression* expr){ + if (expr->no_ref_variant) return; + ref_stack(); +} + +static void check_string(Expression*& expr){ + TYPE t = expr->type; + if (!TYPE_is_string(t) && t != T_NULL && t != T_VARIANT) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_STRING), JIF.F_TYPE_get_name(t)); + if (TYPE_is_string(t)) + return; + if (t == T_NULL){ + //abort(); + //FIXME is this good? + assert(isa(expr)); + //delete expr; + expr = new PushCStringExpression(NULL, 0, 0); + return; + } + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + } + expr = new CheckStringExpression(expr); +} + +static void check_integer(Expression*& expr){ + TYPE t = expr->type; + if (!TYPE_is_integer(t) && t != T_VARIANT) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), JIF.F_TYPE_get_name(t)); + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + expr = new CheckIntegerExpression(expr); + } +} + +static void check_float(Expression*& expr){ + TYPE t = expr->type; + if (!TYPE_is_number(t) && t != T_VARIANT) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_FLOAT), JIF.F_TYPE_get_name(t)); + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + expr = new CheckFloatExpression(expr); + } else + JIT_conv(expr, T_FLOAT); +} + +static void check_pointer(Expression*& expr){ + TYPE t = expr->type; + if (t != T_POINTER && t != T_VARIANT) + THROW(E_TYPE, "Pointer", JIF.F_TYPE_get_name(t)); + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + expr = new CheckPointerExpression(expr); + } +} + +static TYPE check_good_type(TYPE t1, TYPE t2){ + if (t1 == T_CSTRING) t1 = T_STRING; + if (t2 == T_CSTRING) t2 = T_STRING; + + if (t1 == t2){ + + } else if (t1 == T_NULL){ + if (t2 <= T_FLOAT) + return T_VARIANT; + t1 = t2; + } else if (t1 <= T_FLOAT && t2 <= T_FLOAT){ + if (t2 > t1) + t1 = t2; + } else if (t2 == T_NULL){ + if (t1 <= T_FLOAT) + return T_VARIANT; + } else if (TYPE_is_object(t1) && TYPE_is_object(t2)){ + return T_OBJECT; + } else + return T_VARIANT; + + if (t1 == T_VOID) + THROW(E_NRETURN); + else if (t1 > T_VARIANT && t1 < T_OBJECT) + THROW(E_TYPE, "Standard type", JIF.F_TYPE_get_name(t1)); + + return t1; +} + +AddQuickExpression::AddQuickExpression(Expression* val, int add) : expr(val), add(add) { + no_ref_variant = true; + + if (TYPE_is_string(expr->type) || expr->type == T_DATE) + JIT_conv(expr, T_FLOAT); + + if (expr->type <= T_BOOLEAN || expr->type > T_VARIANT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(expr->type)); + + type = expr->type; + + if (type == T_VARIANT) + expr->must_on_stack(); +} +PushStaticExpression::PushStaticExpression(int index) { + CLASS_VAR* var = &CP->load->stat[index]; + ctype = &var->type; + klass = CP; + type = ctype_to_type(ctype); + addr = (char*)CP->stat + var->pos; +} +PopStaticExpression::PopStaticExpression(Expression* val, int index) : val(val) { + CLASS_VAR* var = &CP->load->stat[index]; + CTYPE* ctype = &var->type; + if (ctype->id == TC_ARRAY || ctype->id == TC_STRUCT) + THROW_ILLEGAL(); + type = ctype_to_type(ctype); + addr = (char*)CP->stat + var->pos; + JIT_conv(this->val, type); +} +PushDynamicExpression::PushDynamicExpression(int index) : index(index) { + CLASS_VAR* var = &CP->load->dyn[index]; + ctype = &var->type; + type = ctype_to_type(ctype); + offset = var->pos; +} +PopDynamicExpression::PopDynamicExpression(Expression* val, int index) : val(val), index(index) { + CLASS_VAR* var = &CP->load->dyn[index]; + CTYPE* ctype = &var->type; + if (ctype->id == TC_ARRAY || ctype->id == TC_STRUCT) + THROW_ILLEGAL(); + type = ctype_to_type(ctype); + offset = var->pos; + JIT_conv(this->val, type); +} +PushFunctionExpression::PushFunctionExpression(int index) : index(index) { + type = T_FUNCTION; + + function_class = CP; + function_object = NULL; //Will be set later + function_kind = FUNCTION_PRIVATE; + function_defined = true; + function_unknown = NULL; + function_index = index; + function_expr_type = PrivateFn; +} +PushLocalExpression::PushLocalExpression(int index) : index(index) { + if (index >= FP->n_local){ + type = get_ctrl_type(index); + } else { + CLASS_LOCAL* var = &FP->local[index]; + CTYPE* ctype = &var->type; + type = ctype_to_type(ctype); + } +} +PopLocalExpression::PopLocalExpression(Expression* val, int index) : val(val), index(index) { + CLASS_LOCAL* var = &FP->local[index]; + CTYPE* ctype = &var->type; + type = ctype_to_type(ctype); + JIT_conv(this->val, type); +} +PopParamExpression::PopParamExpression(Expression* val, int index) : val(val), index(index) { + type = FP->param[index + FP->n_param].type; + JIT_conv(this->val, type); +} +PopCtrlExpression::PopCtrlExpression(Expression* val, int index) : val(val), index(index) { + CLASS* klass = NULL; + if (auto pce = dyn_cast(val)){ + klass = pce->klass; + } + set_ctrl_type(val->type, index, klass); + type = val->type; +} +PopOptionalExpression::PopOptionalExpression(Expression* val, int index) : val(val), index(index) { + type = FP->param[FP->n_param + index].type; + if (val->type == T_VOID){ + is_default = true; + } else { + is_default = false; + JIT_conv(this->val, type); + } +} +PushEventExpression::PushEventExpression(int ind, const char* unknown_name) : index(ind) { + //type = CP->load->event[index].type; + type = T_FUNCTION; + if (unknown_name){ + index = CLASS_find_symbol(CP, unknown_name); + if (index == NO_SYMBOL) + THROW(E_DYNAMIC, CP->name, unknown_name); + + CLASS_DESC* desc = CP->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_EVENT) + THROW(E_DYNAMIC, CP->name, unknown_name); + + index = desc->event.index; + } else if (CP->parent){ + index += CP->parent->n_event; + } + + function_kind = FUNCTION_EVENT; + function_expr_type = EventFn; +} +PushArrayExpression::PushArrayExpression(Expression** it, int nargs){ + args.resize(nargs); + for(int i=0; i(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + CTYPE* ctype; + CLASS* klass; + if (is_push_dynamic){ + ctype = &CP->load->dyn[((PushDynamicExpression*)args[0])->index].type; + klass = CP; + } else { + ctype = ((ReadVariableExpression*)args[0])->ctype; + klass = ((ReadVariableExpression*)args[0])->klass; + } + + CLASS_ARRAY* ca = klass->load->array[ctype->value]; + int* dim = ca->dim; + + int ndim = 1; + while(*dim++ > 0) + ndim++; + + if (nargs-1 != ndim) + THROW(E_NDIM); + + for(int i=0; ictype, klass); + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + ref_stack(); + type = klass->array_type; + for(int i=1; i 2) + for(int i=1; imust_on_stack(); + } else if (klass->quick_array == CQA_COLLECTION){ + if (nargs > 2) + THROW(E_TMPARAM); + JIT_conv(args[1], T_STRING); + on_stack = true; + type = T_VARIANT; + } else { + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_GET_METHOD; + } + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + klass = pce->klass; + is_push_class = true; + goto _SPEC_GET_METHOD; + } else if (args[0]->type == T_OBJECT || args[0]->type == T_VARIANT){ + pc = get_current_read_pos(); + *pc |= 3 << 6; + type = T_VARIANT; + on_stack = true; + for(int i=0; imust_on_stack(); + } + } else { + THROW(E_NOBJECT); + } + + return; + + _SPEC_GET_METHOD: { + int index = klass->special[SPEC_GET]; + + if (index == NO_SYMBOL) + THROW(E_NARRAY, klass->name); + + ref_stack(); + + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + type = desc->method.type; + + if (is_push_class){ + if (CLASS_DESC_get_type(desc) != CD_STATIC_METHOD){ + if (!klass->auto_create) + THROW(E_NOBJECT); + //delete args[0]; + args[0] = new PushAutoCreateExpression(klass); + } + } else { + if (CLASS_DESC_get_type(desc) == CD_STATIC_METHOD && !klass->is_virtual) + THROW(E_NARRAY); + } + + nargs--; + + if (nargs < desc->method.npmin) + THROW(E_NEPARAM); + if (nargs > desc->method.npmax && !desc->method.npvar) + THROW(E_TMPARAM); + + can_quick = !(desc->method.npmin < desc->method.npmax || nargs != desc->method.npmin || desc->method.npvar); + + if (can_quick){ + for(int i=0; imethod.signature[i]); + } + } + on_stack = true; + for(uint i=0; imust_on_stack(); + } +} +PopArrayExpression::PopArrayExpression(Expression** it, int nargs, Expression* val) : val(val) { + args.resize(nargs); + for(int i=0; i(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + CTYPE* ctype; + CLASS* klass; + if (is_push_dynamic){ + ctype = &CP->load->dyn[((PushDynamicExpression*)args[0])->index].type; + klass = CP; + } else { + ctype = ((ReadVariableExpression*)args[0])->ctype; + klass = ((ReadVariableExpression*)args[0])->klass; + } + + CLASS_ARRAY* ca = klass->load->array[ctype->value]; + int* dim = ca->dim; + + int ndim = 1; + while(*dim++ > 0) + ndim++; + + if (nargs-1 != ndim) + THROW(E_NDIM); + + for(int i=0; ictype.id == TC_STRUCT) + THROW_ILLEGAL(); + + type = ctype_to_type(&ca->ctype, klass); + + JIT_conv(this->val, type); + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + type = klass->array_type; + JIT_conv(this->val, type); + ref_stack(); + for(int i=1; i 2) + for(int i=1; imust_on_stack(); + } else if (klass->quick_array == CQA_COLLECTION){ + if (nargs > 2) + THROW(E_TMPARAM); + JIT_conv(this->val, T_VARIANT); + JIT_conv(args[1], T_STRING); + this->val->must_on_stack(); + } else { + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_PUT_METHOD; + } + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + klass = pce->klass; + is_push_class = true; + goto _SPEC_PUT_METHOD; + } else if (args[0]->type == T_OBJECT || args[0]->type == T_VARIANT){ + pc = get_current_read_pos(); + *pc |= 3 << 6; + type = T_VARIANT; + on_stack = true; + this->val->must_on_stack(); + for(int i=0; imust_on_stack(); + } + } else { + THROW(E_NOBJECT); + } + + return; + + _SPEC_PUT_METHOD: { + int index = klass->special[SPEC_PUT]; + + if (index == NO_SYMBOL) + THROW(E_NARRAY, klass->name); + + ref_stack(); + + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + type = desc->method.type; + + if (is_push_class){ + if (CLASS_DESC_get_type(desc) != CD_STATIC_METHOD){ + if (!klass->auto_create) + THROW(E_NOBJECT); + //delete args[0]; + args[0] = new PushAutoCreateExpression(klass); + } + } else { + if (CLASS_DESC_get_type(desc) == CD_STATIC_METHOD && !klass->is_virtual) + THROW(E_NARRAY); + } + + //nargs contains the number of parameters, since object and value are swapped + + if (nargs < desc->method.npmin) + THROW(E_NEPARAM); + if (nargs > desc->method.npmax && !desc->method.npvar) + THROW(E_TMPARAM); + + can_quick = !(desc->method.npmin < desc->method.npmax || nargs != desc->method.npmin || desc->method.npvar); + + if (can_quick){ + JIT_conv(this->val, desc->method.signature[0]); + for(int i=1; imethod.signature[i]); + } + } + on_stack = true; + for(int i=0; imust_on_stack(); + this->val->must_on_stack(); + } +} +/*PushPureObjectUnknownFunctionExpression::PushPureObjectUnknownFunctionExpression(Expression* obj) : obj(obj){ + CLASS* klass = (CLASS*)(void*)obj->type; + + type = T_FUNCTION; + if (klass->must_check){ + ref_stack(); + obj->must_on_stack(); + } + + function_class = klass; + function_object = obj; + function_defined = true; + function_expr_type = UnknownFn; +}*/ +PushPureObjectFunctionExpression::PushPureObjectFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index){ + type = T_FUNCTION; + if (klass()->must_check){ + ref_stack(); + obj->must_on_stack(); + } + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = desc()->method.native ? -1 : FUNCTION_PUBLIC; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = PureObjectFn; +} +PushPureObjectStaticFunctionExpression::PushPureObjectStaticFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index){ + type = T_FUNCTION; + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = desc()->method.native ? -1 : FUNCTION_PUBLIC; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = PureObjectStaticFn; +} +PushVirtualFunctionExpression::PushVirtualFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index) { + type = T_FUNCTION; + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = FUNCTION_NATIVE; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = VirtualObjectFn; +} +PushVirtualStaticFunctionExpression::PushVirtualStaticFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index) { + //type = desc()->method.type; + type = T_FUNCTION; + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = FUNCTION_NATIVE; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = VirtualObjectStaticFn; +} +PushStaticPropertyExpression::PushStaticPropertyExpression(Expression* obj, int index) +: obj(obj), index(index) { + CLASS* c = ((PushClassExpression*)obj)->klass; + CLASS_DESC* desc = c->table[index].desc; + type = desc->property.type; + ref_stack(); + obj->must_on_stack(); +} +PushStaticFunctionExpression::PushStaticFunctionExpression(Expression* obj, int index, const char* unknown_name) +: obj(obj), index(index) { + //type = desc()->method.type; + type = T_FUNCTION; + ref_stack(); + obj->must_on_stack(); + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = function_desc->method.native ? (function_desc->method.subr ? FUNCTION_SUBR : FUNCTION_NATIVE) : FUNCTION_PUBLIC; + function_defined = true; + function_unknown = unknown_name; + function_index = index; + function_expr_type = StaticFn; +} +PopVirtualPropertyExpression::PopVirtualPropertyExpression(Expression* obj, Expression* val, int index, const char* name, bool is_static) +: PopPureObjectExpression(obj, val, index), name(name), is_static(is_static) { + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + this->val->must_on_stack(); + JIT_conv(this->val, type); +} +PopPureObjectPropertyExpression::PopPureObjectPropertyExpression(Expression* obj, Expression* val, int index) +: PopPureObjectExpression(obj, val, index) { + type = desc()->property.type; + ref_stack(); + val->must_on_stack(); + obj->must_on_stack(); + JIT_conv(this->val, type); +} +PopStaticPropertyExpression::PopStaticPropertyExpression(CLASS* klass, Expression* val, int index) +: klass(klass), val(val), index(index) { + CLASS_DESC* desc = klass->table[index].desc; + type = desc->property.type; + ref_stack(); + JIT_conv(this->val, type); + this->val->must_on_stack(); +} +CallExpression::CallExpression(Expression* function, int nargs, Expression** it, unsigned short* pc) : func(function), pc(pc) { + on_stack = true; //Gets replaced by false if EventFn or Extern + desc = NULL; + variant_call = false; + ref_stack(); + args.resize(nargs); + for(int i=0; i(func)){ + CLASS_EXTERN* ext; + if (ee->object_to_release) + ext = &ee->klass->load->ext[ee->klass->table[ee->index].desc->ext.exec]; //An extern method with the correct signature + else + ext = &ee->klass->load->ext[ee->index]; + + if (nargs < ext->n_param) + THROW(E_NEPARAM); + if (!ext->vararg && nargs > ext->n_param) + THROW(E_TMPARAM); + + for(int i=0; in_param; i++){ + TYPE t = ext->param[i].type; + + if (t == T_VOID || t == T_DATE || t == T_VARIANT || t == T_CLASS || t == T_FUNCTION) + THROW(E_UTYPE); + + JIT_conv(args[i], t); + + if (args[i]->type == T_STRING || TYPE_is_object(t)) + args[i]->must_on_stack(); + } + + for(int i=ext->n_param; itype; + + if (t == T_VOID || t == T_DATE || t == T_CLASS || t == T_FUNCTION) + THROW(E_UTYPE); + + if (t == T_VARIANT) + ee->must_variant_dispatch = true; + + if (args[i]->type == T_STRING || TYPE_is_object(t)) + args[i]->must_on_stack(); + } + + if (ee->must_variant_dispatch) + for(int i=0; imust_on_stack(); + + type = ext->type; + on_stack = false; + return; + } + + if (TYPE_is_pure_object(func->type)){ + JIT_load_class((CLASS*)(void*)func->type); + CLASS_DESC_METHOD* method_desc = JR_CLASS_get_special_desc((CLASS*)(void*)func->type, SPEC_CALL); + if (method_desc == NULL) + THROW(E_NFUNC); + //Can be virtual static... See Draw.Clip._call + //Atm no virtual non-static methods are written + + //Note: func can be Super + + + desc = (CLASS_DESC*)(void*)method_desc; //Works, since CLASS_DESC is a union containing CLASS_DESC_METHOD + klass = method_desc->klass; + index = klass->special[SPEC_CALL]; + + switch(CLASS_DESC_get_type(desc)){ + case CD_METHOD: + if (klass->is_virtual) + func = new PushVirtualFunctionExpression(func, index, NULL); + else + func = new PushPureObjectFunctionExpression(func, index, NULL); + break; + case CD_STATIC_METHOD: + if (klass->is_virtual) + func = new PushVirtualStaticFunctionExpression(func, index, NULL); + else + func = new PushPureObjectStaticFunctionExpression(func, index, NULL); + break; + default: + THROW(E_NFUNC); + } + } + func->must_on_stack(); + + if (FunctionExpression* fe = dynamic_cast(func)){ + switch(fe->function_expr_type){ + case PrivateFn: { + index = fe->function_index; + kind = FUNCTION_PRIVATE; + klass = CP; + + FUNCTION* fp = &CP->load->func[index]; + if (nargs < fp->npmin) + THROW(E_NEPARAM); + if (nargs > fp->n_param && !fp->vararg) + THROW(E_TMPARAM); + + can_quick = !(fp->npmin < fp->n_param || nargs != fp->npmin || fp->vararg); + if (can_quick){ + for(int i=0; iparam[i].type); + } + type = fp->type; + goto _SET_ON_STACK; + return; + } + case EventFn: { + on_stack = false; + type = T_BOOLEAN; + goto _SET_ON_STACK; + return; + } + /*case UnknownFn: { + type = T_VARIANT; + goto _SET_ON_STACK; + }*/ + } + desc = fe->function_desc; + index = fe->function_index; + klass = fe->function_class; + type = fe->function_unknown ? T_VARIANT : desc->method.type; + + goto _NORMAL_METHOD; + } + + if (PushClassExpression* ce = dyn_cast(func)){ + int special_call_function_index = ce->klass->special[SPEC_CALL]; + if (special_call_function_index == NO_SYMBOL){ + //Then it is a cast: Dim obj2 As SomeClass = SomeClass(obj1); + if (nargs != 1) + THROW(E_NFUNC); + + desc = NULL; + type = (TYPE)(void*)ce->klass; + JIT_conv(args[0], (TYPE)(void*)ce->klass); + return; + } + CLASS_DESC_METHOD* method_desc = &CLASS_get_desc(ce->klass, special_call_function_index)->method; + desc = (CLASS_DESC*)(void*)method_desc; //Works, since CLASS_DESC is a union containing CLASS_DESC_METHOD + index = special_call_function_index; + klass = method_desc->klass; + type = method_desc->type; + + goto _NORMAL_METHOD; + } + + if (func->type != T_OBJECT && func->type != T_VARIANT) + THROW(E_NFUNC); + else { + type = T_VARIANT; + can_quick = false; + variant_call = true; + goto _SET_ON_STACK; + } + + return; //Line unreachable + + _NORMAL_METHOD: + if (nargs < desc->method.npmin) + THROW(E_NEPARAM); + if (nargs > desc->method.npmax && !desc->method.npvar) + THROW(E_TMPARAM); + + can_quick = !(desc->method.npmin < desc->method.npmax || nargs != desc->method.npmin || desc->method.npvar); + + if (can_quick){ + for(int i=0; imethod.signature[i]); + } + } + + _SET_ON_STACK: + for(int i=0; imust_on_stack(); + } +} +JumpFirstExpression::JumpFirstExpression(int ctrl_to, Expression* to_expr, Expression* step_expr, int local_var, int body_addr, int done_addr) +: to(to_expr), step(step_expr), ctrl_to(ctrl_to), local_var(local_var), body_addr(body_addr), done_addr(done_addr) { + TYPE type = FP->local[local_var].type.id; + + if (type < T_BYTE || type > T_FLOAT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + + if (TYPE_is_integer(type)) + JIT_conv(step, T_INTEGER); + else + JIT_conv(step, type); + + JIT_conv(to, type); + + set_ctrl_type(type, ctrl_to); + set_ctrl_type(step->type, ctrl_to+1); + + mark_address_taken(body_addr); + mark_address_taken(done_addr); +} +JumpEnumNextExpression::JumpEnumNextExpression(JumpEnumFirstExpression* jfirst, int cont_addr, int addr, unsigned short* pc, bool drop, OnStackExpression* retval) +: jfirst(jfirst), retval(retval), cont_addr(cont_addr), addr(addr), pc(pc), drop(drop) { + mark_address_taken(cont_addr); + mark_address_taken(addr); + TYPE obj_type = get_ctrl_type(jfirst->ctrl); + + CLASS* klass; + + if (obj_type == T_VARIANT || obj_type == T_OBJECT){ + defined = false; + type = T_VARIANT; + } else if (obj_type == T_CLASS) { + defined = true; + PushClassExpression* pce = dyn_cast(jfirst->obj); + assert(pce); + klass = pce->klass; + + /*if (klass->auto_create && !klass->enum_static){ + delete pce; + jfirst->obj = PushAutoCreate(klass); + obj_type = (CLASS*)(void*)klass; + }*/ + } else if (TYPE_is_pure_object(obj_type)){ + defined = true; + klass = (CLASS*)(void*)obj_type; + } else { + THROW(E_NOBJECT); + } + + if (defined){ + short index = klass->special[SPEC_NEXT]; + if (index == NO_SYMBOL) + THROW(E_ENUM); + + CLASS_DESC* desc = CLASS_get_desc(klass, index); + type = desc->method.type; + } + + if (!drop) + retval->type = type; + + set_ctrl_type(T_OBJECT, jfirst->ctrl+1); +} +NewExpression::NewExpression(Expression** it, int nargs, bool event) : event(event) { + pc = get_current_read_pos(); + must_on_stack(); + args.resize(nargs); + for(int i=0; imust_on_stack(); + } + + if (PushClassExpression* pce = dyn_cast(args[0])){ + /*if (pce->klass->override) + pce->klass = pce->klass->override;*/ + type = (TYPE)(void*)pce->klass; + } else { + type = T_OBJECT; + } +} +ReturnExpression::ReturnExpression(Expression* expr, int kind) : retval(expr), kind(kind) { + pc = get_current_read_pos(); + type = FP->type; + if (retval){ + /*if (TYPE_is_pure_object(type) && ((CLASS*)type)->override) + type = (TYPE)(((CLASS*)type)->override);*/ + JIT_conv(retval, type); + } +} +QuitExpression::QuitExpression(Expression* quitval) : quitval(quitval) { + if (quitval) + JIT_conv(this->quitval, T_BYTE); +} +SubrExpression::SubrExpression(int digit, Expression** it, int nargs, int extra) : digit(digit), extra(extra) { + args.resize(nargs); + for(int i=0; imust_on_stack(); + } + + switch(digit){ + case 0x41: //Mid + if (nargs == 3) + JIT_conv(args[2], T_INTEGER); + //Fallthrough + ref_stack(); + case 0x40: //Left + case 0x42: //Right + if (nargs != 1) + JIT_conv(args[1], T_INTEGER); + /*type = args[0]->type; //Want T_STRING or T_CSTRING + if (type == T_VARIANT || type = T_NULL) + type = T_STRING; + if (!TYPE_is_string(type)) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_STRING), JIF.F_TYPE_get_name(type)); + if (type == T_VARIANT) + ref_stack();*/ + check_string(args[0]); + type = args[0]->type == T_CSTRING ? T_CSTRING : T_STRING; + break; + + case 0x43: //Len + check_string(args[0]); + type = T_INTEGER; + break; + + case 0x45: //String + check_string(args[1]); + //Fallthrough + case 0x44: //Space + check_integer(args[0]); + ref_stack(); + type = T_STRING; + break; + + case 0x4A: //Asc + check_string(args[0]); + if (nargs == 2) + check_integer(args[1]); + type = T_INTEGER; + break; + + case 0x46: //Trim + check_string(args[0]); + type = args[0]->type; + break; + + case 0x48: //LCase + extra = 1; + case 0x47: //UCase + check_string(args[0]); + type = T_STRING; + break; + + case 0x49: //Chr + ref_stack(); + JIT_conv(args[0], T_INTEGER); + type = T_CSTRING; + break; + + case 0x4B: //Instr + case 0x4C: //RInStr + type = T_INTEGER; + check_string(args[0]); + check_string(args[1]); + if (nargs >= 3) + check_integer(args[2]); + if (nargs == 4) + check_integer(args[3]); + break; + + case 0x4D: //Subst + type = T_STRING; + check_string(args[0]); + for(int i=1; i= 2){ + check_string(args[1]); + if (nargs >= 3){ + check_string(args[2]); + if (nargs >= 4){ + JIT_conv(args[3], T_BOOLEAN); + if (nargs == 5) + JIT_conv(args[4], T_BOOLEAN); + } + } + } + break; + + case 0x50: //Scan + type = (TYPE)(void*)GB.FindClass("String[]"); + check_string(args[0]); + check_string(args[1]); + break; + + case 0x51: //Comp + type = T_INTEGER; + JIT_conv(args[0], T_STRING); + JIT_conv(args[1], T_STRING); + if (nargs == 2) + check_integer(args[2]); + break; + + case 0x52: //Conv + ref_stack(); + type = T_STRING; + check_string(args[0]); + check_string(args[1]); + check_string(args[2]); + break; + + case 0x53: //DConv + ref_stack(); + type = T_STRING; + check_string(args[0]); + break; + + case 0x54: //Abs + case 0x55: //Int + case 0x56: //Fix + pc = get_current_read_pos(); + type = args[0]->type; + if (!TYPE_is_number(type) && type != T_VARIANT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + if (type == T_VARIANT){ + args[0]->must_on_stack(); + no_ref_variant = true; + on_stack = true; + } + break; + + case 0x57: //Sgn + type = T_INTEGER; + if (!TYPE_is_number(args[0]->type) && args[0]->type != T_VARIANT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(args[0]->type)); + if (args[0]->type == T_VARIANT){ + args[0]->must_on_stack(); + no_ref_variant = true; + on_stack = true; + } + break; + + case 0x58: //Math + ref_stack(); + type = T_FLOAT; + JIT_conv(args[0], T_FLOAT); + break; + + case 0x59: //Pi + type = T_FLOAT; + if (nargs == 1) + JIT_conv(args[0], T_FLOAT); + break; + + case 0x5A: //Round + type = T_FLOAT; + JIT_conv(args[0], T_FLOAT); + if (nargs == 2){ + check_integer(args[1]); + JIT_conv(args[1], T_INTEGER); + } + break; + + case 0x5B: //Randomize + type = T_VOID; + if (nargs != 0){ + check_integer(args[0]); + JIT_conv(args[0], T_INTEGER); + } + break; + + case 0x5C: //Rnd + type = T_FLOAT; + if (nargs >= 1) + JIT_conv(args[0], T_FLOAT); + if (nargs == 2) + JIT_conv(args[1], T_FLOAT); + break; + + case 0x5D: //Min + case 0x5E: { //Max + TYPE t1 = args[0]->type, t2 = args[1]->type; + + if (!TYPE_is_number_date(t1) && t1 != T_VARIANT) + THROW(E_TYPE, "Number or date", JIF.F_TYPE_get_name(t1)); + if (!TYPE_is_number_date(t2) && t2 != T_VARIANT) + THROW(E_TYPE, "Number or date", JIF.F_TYPE_get_name(t2)); + + type = Max(t1, t2); + if (type != T_VARIANT){ + if (type == T_SINGLE) + type = T_FLOAT; + if (t1 != type) JIT_conv(args[0], type); + else if (t2 != type) JIT_conv(args[1], type); + } else { + /*if (t1 == T_VARIANT && !args[0]->no_ref_variant) + ref_variant(args[0]); + if (t2 == T_VARIANT && !args[1]->no_ref_variant) + ref_variant(args[1]);*/ + ref_stack(); + if (t1 == T_VARIANT) + args[0]->must_on_stack(); + if (t2 == T_VARIANT) + args[1]->must_on_stack(); + no_ref_variant = true; + } + break; + } + + case 0x5F: //IIf + if (args[0]->type == T_VOID || args[1]->type == T_VOID || args[2]->type == T_VOID) + THROW(E_NRETURN); + type = check_good_type(args[1]->type, args[2]->type); + JIT_conv(args[0], T_BOOLEAN); + if (type == T_VARIANT && (args[1]->type != T_VARIANT || args[2]->type != T_VARIANT)){ + /*ref_stack(); + args[1]->ref_on_stack(); + on_stack = true;*/ + } else { + JIT_conv(args[1], type); + JIT_conv(args[2], type); + } + break; + + case 0x60: //Choose + JIT_conv(args[0], T_INTEGER); + type = args[1]->type; + extra = 1; //All same type + for(int i=1; itype == T_VOID) + THROW(E_NRETURN); + if (args[i]->type != type && !(TYPE_is_string(type) && TYPE_is_string(args[i]->type))) + extra = 0; //Different types + } + if (extra == 0 || nargs > 3) + type = T_VARIANT; + break; + + case 0x61: { //Array + for(int i=0; itype == T_VOID) + THROW(E_NRETURN); + type2 = args[0]->type; + if (type2 > T_VARIANT && type2 < T_OBJECT) + THROW(E_TYPE, "Standard type", JIF.F_TYPE_get_name(type2)); + for(int i=1; itype); + } + if (type2 == T_CSTRING) + type2 = T_STRING; + for(int i=0; itype; + if (type != T_VARIANT && (type <= T_BOOLEAN || type > T_LONG)) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + if (type == T_VARIANT && (extra & 0x1F) != 3) + no_ref_variant = true; + ref_stack(); + JIT_conv(args[1], T_INTEGER); + + if ((extra & 0x1F) == 3) //BTst + type = T_BOOLEAN; + break; + + case 0x65: //IsBoolean ... + type = T_BOOLEAN; + if (extra != T_NULL) + check_string(args[0]); + break; + + case 0x66: //TypeOf + type = T_INTEGER; + if (extra){ + check_integer(args[0]); + JIT_conv(args[0], T_INTEGER); + } + break; + + /*case 0x67: //CBool ... + JIT_conv(args[0], extra); + type = extra; + break; + */ //Done in jit_read.cpp + + case 0x68: //Bin + case 0x69: //Hex + type = T_STRING; + JIT_conv(args[0], T_LONG); + if (nargs == 2){ + ref_stack(); + JIT_conv(args[1], T_INTEGER); + } + break; + + case 0x6A: //Val + check_string(args[0]); + type = T_VARIANT; + no_ref_variant = true; + break; + + case 0x6B: //Str + type = T_STRING; + if (args[0]->type == T_VOID) + THROW(E_NRETURN); + break; + + case 0x6C: //Format + type = T_STRING; + ref_stack(); + break; + + case 0x6D: //Timer + type = T_FLOAT; + break; + + case 0x6E: //Now + type = T_DATE; + break; + + case 0x6F: //Year + JIT_conv(args[0], T_DATE); + type = T_INTEGER; + break; + + case 0x70: //Week + if (nargs >= 1){ + JIT_conv(args[0], T_DATE); + if (nargs >= 2){ + check_integer(args[1]); + if (nargs == 3) + JIT_conv(args[2], T_BOOLEAN); + } + } + type = T_INTEGER; + break; + + case 0x71: //Date + case 0x72: //Time + if (nargs == 1) + JIT_conv(args[0], T_DATE); + else if (nargs >= 3){ + for(int i=0; itype != T_POINTER) + THROW(E_TYPE, "Pointer", JIF.F_TYPE_get_name(args[0]->type)); + else if (extra == 0) + check_string(args[0]); + type = (TYPE)(void*)GB.FindClass("File"); + //ref_stack(); Nothing else can be on the stack ;) + break; + + case 0x79: //Close + //ref_stack(); + type = T_VOID; + break; + + case 0x7A: //Input + //ref_stack(); + type = -1; //Unknown type + break; + + case 0x7B: //Line Input + //ref_stack(); + type = T_STRING; + break; + + case 0x7C: //Print + type = T_VOID; + if (nargs < 1) + THROW(E_NEPARAM); + for(int i=1; itype == T_FUNCTION) + assert(false && "Jit compiler cannot call Print with a Function as argument yet."); + if (args[i]->type == T_CLASS) + assert(false && "Jit compiler cannot call Print with a Class as argument yet."); + } + break; + + case 0x7D: //Read + if (extra){ + //ReadBytes + JIT_conv(args[1], T_INTEGER); + type = T_STRING; + } else { + //Read + if (args[1]->type == T_INTEGER){ + auto int_expr = dyn_cast(args[1]); + assert(int_expr != NULL); + type = int_expr->i; + } else if (args[1]->type == T_CLASS){ + auto class_expr = dyn_cast(args[1]); + assert(class_expr != NULL); + type = (TYPE)(void*)class_expr->klass; + } else + THROW_ILLEGAL(); + } + break; + + case 0x7E: //Write + if (extra){ + //WriteBytes + JIT_conv(args[2], T_INTEGER); + if (args[1]->type != T_POINTER) + check_string(args[1]); + } else { + //Write + TYPE type; + if (args[2]->type == T_INTEGER){ + auto int_expr = dyn_cast(args[2]); + assert(int_expr != NULL); + type = int_expr->i; + } else if (args[2]->type == T_CLASS){ + auto class_expr = dyn_cast(args[2]); + assert(class_expr != NULL); + type = (TYPE)(void*)class_expr->klass; + } else + THROW_ILLEGAL(); + JIT_conv(args[1], type); + } + type = T_VOID; + break; + + case 0x7F: //Flush + type = T_VOID; + break; + + case 0x80: //Lock + if (extra){ + //Unlock + type = T_VOID; + } else { + //Lock + check_string(args[0]); + type = (TYPE)(void*)GB.FindClass("File"); + } + break; + + case 0x81: //Input From, Output To, Error To + //extra contains 0, 1 or 2 + type = T_VOID; + break; + + case 0x82: //Eof + ref_stack(); + type = T_BOOLEAN; + break; + + case 0x83: //Lof + ref_stack(); + type = T_LONG; + break; + + case 0x84: //Seek + ref_stack(); + if (nargs >= 2){ + JIT_conv(args[1], T_LONG); + if (nargs == 3){ + JIT_conv(args[2], T_INTEGER); + } + type = T_VOID; + } else + type = T_LONG; + break; + + case 0x85: //Kill + case 0x87: //Rmdir, deprecated + //New syntax: Kill(0) = Kill, Kill(1) = Mkdir, Kill(2) = Rmdir + //extra = 0, 1 or 2 + check_string(args[0]); + type = T_VOID; + break; + + case 0x86: //Mkdir, deprecated -> Even() & Odd() + if (extra == 0){ + check_string(args[0]); + type = T_VOID; + } else if (extra == 1 || extra == 2){ + JIT_conv(args[0], T_LONG); + type = T_BOOLEAN; + } else { + THROW_ILLEGAL(); + } + break; + + case 0x88: //Move + case 0x89: //Copy, deprecated + //New syntax: func = [Move, Copy, Link, Chmod, Chown, Chgrp][extra] + check_string(args[0]); + check_string(args[1]); + type = T_VOID; + break; + + case 0x8A: //Link, deprecated -> IsNan() & IsInf() + if (extra == 0){ + check_string(args[0]); + check_string(args[1]); + type = T_VOID; + } else if (extra == 1){ + JIT_conv(args[0], T_FLOAT); + type = T_BOOLEAN; + } else if (extra == 2){ + JIT_conv(args[0], T_FLOAT); + type = T_INTEGER; + } else { + THROW_ILLEGAL(); + } + break; + + case 0x8B: //Exist + check_string(args[0]); + if (nargs == 2) + JIT_conv(args[1], T_BOOLEAN); + type = T_BOOLEAN; + break; + + case 0x8C: //Access + check_string(args[0]); + if (nargs != 1) + JIT_conv(args[1], T_INTEGER); + type = T_BOOLEAN; + break; + + case 0x8D: //Stat + ref_stack(); + check_string(args[0]); + if (nargs == 2) + JIT_conv(args[1], T_BOOLEAN); + type = (TYPE)(void*)GB.FindClass("Stat"); + break; + + case 0x8E: //Dfree + check_string(args[0]); + type = T_LONG; + break; + + case 0x8F: //Temp + if (nargs != 0) + check_string(args[0]); + type = T_STRING; + break; + + case 0x90: //IsDir + check_string(args[0]); + type = T_BOOLEAN; + break; + + case 0x91: //Dir + ref_stack(); + check_string(args[0]); + if (nargs >= 2){ + check_string(args[1]); + if (nargs == 3) + check_integer(args[2]); + } + type = (TYPE)(void*)GB.FindClass("String[]"); + break; + + case 0x92: //RDir + ref_stack(); + check_string(args[0]); + if (nargs >= 2){ + check_string(args[1]); + if (nargs >= 3){ + check_integer(args[2]); + if (nargs == 4) + JIT_conv(args[3], T_BOOLEAN); + } + } + type = (TYPE)(void*)GB.FindClass("String[]"); + break; + + case 0x93: { //Exec + if (extra){ + //Shell + check_string(args[0]); + } else { + JIT_conv(args[0], (TYPE)(void*)GB.FindClass("String[]")); + } + auto int_expr = dyn_cast(args[2]); + assert(int_expr != NULL); + check_string(args[3]); + if (int_expr->i & 8){ // 8 = TS_EXEC_STRING = PM_STRING + type = T_STRING; + } else { + type = (TYPE)(void*)GB.FindClass("Process"); + } + break; + } + + case 0x94: //Alloc + ref_stack(); + if (nargs == 2) + check_integer(args[1]); + if (!TYPE_is_string(args[0]->type)) + check_integer(args[0]); + type = T_POINTER; + break; + + case 0x95: //Free + check_pointer(args[0]); + type = T_VOID; + break; + + case 0x96: //Realloc + ref_stack(); + if (nargs == 3) + check_integer(args[2]); + check_integer(args[1]); + check_pointer(args[0]); + type = T_POINTER; + break; + + case 0x97: //StrPtr + ref_stack(); + check_pointer(args[0]); + if (nargs != 1) + check_integer(args[1]); + type = T_CSTRING; + break; + + case 0x98: //Sleep + check_float(args[0]); + type = T_VOID; + break; + + case 0x99: { //VarPtr + type = T_POINTER; + //FIXME check numeric, pointer or string + auto pie = dyn_cast(args[0]); + assert(pie); + ushort op = pie->i; + if ((op & 0xFF00) == C_PUSH_LOCAL){ + CLASS_LOCAL* var = &FP->local[op & 0xFF]; + TYPE t = ctype_to_type(&var->type); + if (t == T_VOID || t > T_POINTER) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(t)); + } else if ((op & 0xF800) == C_PUSH_DYNAMIC){ + if (OP == NULL) + THROW_ILLEGAL(); + } else if ((op & 0xF800) == C_PUSH_STATIC){ + } else + THROW_ILLEGAL(); + break; + } + + case 0x9A: //Collection + ref_stack(); + type = (TYPE)(void*)GB.FindClass("Collection"); + break; + + case 0x9B: //Tr + JIT_conv(args[0], T_STRING); + type = T_CSTRING; + break; + + case 0x9C: //Quote, Shell, Html + case 0x9D: //Unquote + JIT_conv(args[0], T_STRING); + type = T_STRING; + break; + + case 0x9E: //MkInteger, ... + if (extra == 0 || (extra > T_DATE && extra < T_POINTER) || extra > T_POINTER) + THROW_ILLEGAL(); + JIT_conv(args[0], extra); + type = extra <= T_BYTE ? T_CSTRING : T_STRING; + if (extra > T_BYTE) + on_stack = true; + break; + + case 0x9F: //Ptr + ref_stack(); + type = args[0]->type; + if (type != T_VARIANT && !TYPE_is_string(type) && type != T_POINTER) + THROW(E_TYPE, "Pointer", JIF.F_TYPE_get_name(type)); + type = extra; + break; + } +} +EqExpression::EqExpression(Expression** it) : BinOpExpression(it) { + type = T_BOOLEAN; + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_NULL || right->type == T_NULL){ + t = T_NULL; + return; + } + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); + left->must_on_stack(); + right->must_on_stack(); + t = T_VARIANT; + return; + } + + t = Max(left->type, right->type); + if (TYPE_is_object(left->type) && TYPE_is_object(right->type)){ + t = T_OBJECT; + left->must_on_stack(); + right->must_on_stack(); + } + else if (TYPE_is_object(t)) + THROW(E_TYPE, "Object", JIF.F_TYPE_get_name(Min(left->type, right->type))); + + if (!TYPE_is_object(t)){ + JIT_conv(left, t); + JIT_conv(right, t); + } +} +NotExpression::NotExpression(Expression* expr) : UnaryExpression(expr) { + type = expr->type; + if (type >= T_BOOLEAN && type <= T_LONG) return; + if (type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + expr->must_on_stack(); + on_stack = true; + no_ref_variant = true; + return; + } + if (TYPE_is_object(type) || type == T_NULL || TYPE_is_string(type)) + type = T_BOOLEAN; + else + THROW(E_TYPE, "Number, String or Object", JIF.F_TYPE_get_name(type)); +} +LessExpression::LessExpression(Expression** it) : BinOpExpression(it) { + type = T_BOOLEAN; + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + left->must_on_stack(); + right->must_on_stack(); + must_on_stack(); + t = T_VARIANT; + return; + } + + t = Max(left->type, right->type); + if (t == T_NULL || TYPE_is_string(t)){ + TYPE typem = Min(left->type, right->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, JIF.F_TYPE_get_name(typem), JIF.F_TYPE_get_name(t)); + } else if (TYPE_is_object(t)) + THROW(E_TYPE, "Number, Date or String", JIF.F_TYPE_get_name(type)); + + if (t == T_BYTE && Min(left->type, right->type) == T_BOOLEAN){ + //To get the expected result: + JIT_conv(left, T_INTEGER); + JIT_conv(right, T_INTEGER); + } else { + JIT_conv(left, t); + JIT_conv(right, t); + } +} +AddSubBaseExpression::AddSubBaseExpression(Expression** it) : BinOpExpression(it) { + type = Max(left->type, right->type); + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true; + on_stack = true; + return; + } + + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)){ + if (type == T_DATE){ + JIT_conv(left, T_FLOAT); + JIT_conv(right, T_FLOAT); + } else { + JIT_conv(left, type); + JIT_conv(right, type); + } + } + + if (TYPE_is_string(left->type)){ + JIT_conv(left, T_FLOAT); + } + + if (TYPE_is_string(right->type)){ + JIT_conv(right, T_FLOAT); + } + + if (left->type == T_NULL || right->type == T_NULL) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + type = Max(left->type, right->type); + + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)){ + if (type == T_DATE){ + JIT_conv(left, T_FLOAT); + JIT_conv(right, T_FLOAT); + } else { + JIT_conv(left, type); + JIT_conv(right, type); + } + } else + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); +} +MulExpression::MulExpression(Expression** it) : BinOpExpression(it) { + type = Max(left->type, right->type); + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true; + on_stack = true; + return; + } + + if (TYPE_is_number_date(type)){ + JIT_conv(left, type); + JIT_conv(right, type); + return; + } + + if (TYPE_is_string(left->type)){ + JIT_conv(left, T_FLOAT); + } + + if (TYPE_is_string(right->type)){ + JIT_conv(right, T_FLOAT, left); + } + + if (left->type == T_NULL || right->type == T_NULL) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + type = Max(left->type, right->type); + + if (TYPE_is_number_date(type)){ + JIT_conv(left, type); + JIT_conv(right, type, left); + } else + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); +} +QuoRemBaseExpression::QuoRemBaseExpression(Expression** it) : BinOpExpression(it) { + type = Max(left->type, right->type); + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + ref_stack(); //Division by zero possible + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + /*left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true;*/ + //FIXME is this good/ok to use Long here? + type = T_LONG; + JIT_conv(left, T_LONG); + JIT_conv(right, T_LONG); + return; + } + + if (left->type == T_NULL || right->type == T_NULL) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + if (TYPE_is_integer_long(type)){ + JIT_conv(left, type); + JIT_conv(right, type, left); + } else { + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + } +} +AndOrXorBaseExpression::AndOrXorBaseExpression(Expression** it) : BinOpExpression(it) { + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (TYPE_is_variant(left->type) || TYPE_is_variant(right->type)){ + ref_stack(); + left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true; + on_stack = true; + return; + } + + if (TYPE_is_string(left->type)) + JIT_conv(left, T_BOOLEAN); + if (TYPE_is_string(right->type)) + JIT_conv(right, T_BOOLEAN); + + type = Max(left->type, right->type); + + if (TYPE_is_null(left->type) || TYPE_is_null(right->type)) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + if (TYPE_is_number_date(type)){ + JIT_conv(left, type); + JIT_conv(right, type); + } else + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); +} diff --git a/gb.jit.llvm/src/jit_gambas_pass.cpp b/gb.jit.llvm/src/jit_gambas_pass.cpp new file mode 100644 index 00000000..ab73edf4 --- /dev/null +++ b/gb.jit.llvm/src/jit_gambas_pass.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + + jit_gambas_pass.cpp + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "llvm/Pass.h" +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 3) +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Function.h" +#elif (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2) +#include "llvm/IRBuilder.h" +#include "llvm/Function.h" +#else +#include "llvm/Support/IRBuilder.h" +#include "llvm/Function.h" +#endif +#include "llvm/Support/raw_ostream.h" + +#include "main.h" + +#ifdef __CYGWIN__ +#define __finite finite +#define __isnan __isnand +#define __isinf __isinfd +#endif +#define FUNCTION_NAME(s) _FN(s) +#define _FN(s) #s + +using namespace llvm; + +namespace { + +struct GambasPass : public FunctionPass { + static char ID; + GambasPass() : FunctionPass(ID) {} + + virtual bool runOnFunction(Function &F); +}; + +} + +char GambasPass::ID = 0; + +static RegisterPass X("gamabs-pass", "Gambas Pass", false, false); + +FunctionPass* createGambasPass(){ + return new GambasPass(); +} + +bool GambasPass::runOnFunction(Function &F){ + IRBuilder<> Builder(F.getContext()); + + bool changed = false; + for(Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) { + for(BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ){ + ICmpInst* ICI = dyn_cast(I); + CallInst* CI = dyn_cast(I++); + + if (ICI && ICI->hasMetadata() && ICI->getMetadata("unref_slt") && dyn_cast(ICI->getOperand(0))){ + ICI->replaceAllUsesWith(ConstantInt::get(ICI->getType(), false)); + ICI->eraseFromParent(); + changed = true; + continue; + } + + if (!CI) + continue; + + Function* callee = CI->getCalledFunction(); + if (callee == NULL || !callee->isDeclaration()) + continue; + + StringRef name = callee->getName(); + if (name == "JR_release_variant" || name == "JR_borrow_variant"){ + ConstantInt* vtype_int = dyn_cast(CI->getArgOperand(0)); + if (!vtype_int) + continue; + + uint64_t vtype = vtype_int->getZExtValue(); + if (TYPE_is_string(vtype) || TYPE_is_object(vtype)) + continue; + + CI->eraseFromParent(); + changed = true; + } else if (name == FUNCTION_NAME(__finite)){ + ConstantFP* op = dyn_cast(CI->getArgOperand(0)); + if (!op) + continue; + + int val = __finite(op->getValueAPF().convertToDouble()); + Constant* res = ConstantInt::get(CI->getType(), val); + CI->replaceAllUsesWith(res); + CI->eraseFromParent(); + changed = true; + } else if (name == FUNCTION_NAME(__isnan)){ + ConstantFP* op = dyn_cast(CI->getArgOperand(0)); + if (!op) + continue; + + int val = __isnan(op->getValueAPF().convertToDouble()); + Constant* res = ConstantInt::get(CI->getType(), val); + CI->replaceAllUsesWith(res); + CI->eraseFromParent(); + changed = true; + } else if (name == FUNCTION_NAME(__isinf)){ + ConstantFP* op = dyn_cast(CI->getArgOperand(0)); + if (!op) + continue; + + int val = __isinf(op->getValueAPF().convertToDouble()); + Constant* res = ConstantInt::get(CI->getType(), val); + CI->replaceAllUsesWith(res); + CI->eraseFromParent(); + changed = true; + } + } + } + return changed; +} diff --git a/gb.jit.llvm/src/jit_gambas_pass.h b/gb.jit.llvm/src/jit_gambas_pass.h new file mode 100644 index 00000000..e50afbc1 --- /dev/null +++ b/gb.jit.llvm/src/jit_gambas_pass.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + jit_gambas_pass.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_GAMBAS_PASS_H +#define __JIT_GAMBAS_PASS_H + +llvm::FunctionPass* createGambasPass(void); + +#endif diff --git a/gb.jit.llvm/src/jit_read.cpp b/gb.jit.llvm/src/jit_read.cpp new file mode 100644 index 00000000..1a686442 --- /dev/null +++ b/gb.jit.llvm/src/jit_read.cpp @@ -0,0 +1,1100 @@ +/*************************************************************************** + + jit_read.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_READ_C + +#include "jit.h" +#include "gb_pcode.h" + +#include +#include + +static unsigned short* code; +static int size; +static int pos; +static int addr; +static uint stack_start; + +static bool in_dup = false; + +unsigned short* get_current_read_pos(){ + return code + pos; +} + +static std::vector _stack; + +void ref_stack(){ + for(size_t i=0, e=_stack.size(); i!=e; i++) + _stack[i]->ref_on_stack(); +} + +static int stack_size(){ + return _stack.size(); +} + +static bool stack_empty(){ + return _stack.size() == stack_start; +} + +static void push(Expression* expr){ + _stack.push_back(expr); +} + +static Expression*& top(){ + return _stack.back(); +} + +static Expression* pop(){ + Expression* val = _stack.back(); + _stack.pop_back(); + return val; +} + +static Expression** extract(int num){ + size_t s = _stack.size(); + size_t start = (int)s-num; + Expression** it = &_stack[start]; + _stack.resize(start); + return it; +} + +static Expression** extract_reverse(int num){ + Expression** it = extract(num); + std::reverse(it, it+num); + return it; +} + +std::vector taken_addresses; + +void mark_address_taken(int addr){ + taken_addresses.push_back(addr); +} + +static void push_statement(Expression* expr){ + Statement* s = new Statement(); + s->addr = addr; + s->address_taken = false; + s->expr = expr; + all_statements.push_back(s); +} + +static Expression*& top_statement(){ + return all_statements.back()->expr; +} + +static Expression* pop_statement(){ + Expression* ret = top_statement(); + delete all_statements.back(); + all_statements.pop_back(); + return ret; +} + +static void push_constant(CLASS_DESC* desc){ + if (TYPE_is_string(desc->constant.type) && desc->constant.translate){ + char* addr = (char *)GB.Translate(desc->constant.value._string); + push(new PushCStringExpression(addr, 0, strlen(addr))); + return; + } + void* value = &desc->constant.value; + switch(desc->constant.type){ + case T_BOOLEAN: + push(new PushIntegerExpression(1, *(bool*)value)); return; + case T_BYTE: + push(new PushIntegerExpression(8, *(unsigned char*)value)); return; + case T_SHORT: + push(new PushIntegerExpression(16, *(short*)value)); return; + case T_INTEGER: + push(new PushIntegerExpression(32, *(int*)value)); return; + case T_LONG: + push(new PushIntegerExpression(64, *(int64_t*)value)); return; + case T_SINGLE: + push(new PushFloatExpression(32, *(float*)value)); return; + case T_FLOAT: + push(new PushFloatExpression(64, *(double*)value)); return; + case T_CSTRING: + push(new PushCStringExpression(*(char**)value, 0, strlen(*(char**)value))); return; + default: + assert(false && "Illegal constant type"); + } +} + +static int get_subr_nargs(int digit, int extra){ + int nargs = extra; + switch(digit){ + case 0x6D: //Timer + case 0x6E: //Now + case 0x75: //Error + case 0x76: //Debug + nargs = 0; break; + + case 0x43: //Len + case 0x44: //Space + case 0x46: //Trim + case 0x47: //UCase + case 0x48: //LCase + case 0x49: //Chr + case 0x53: //DConv + case 0x54: //Abs + case 0x55: //Int + case 0x56: //Fix + case 0x57: //Sgn + case 0x58: //Math + case 0x63: //IsAscii, IsLetter ... + case 0x65: //IsBoolean ... + case 0x66: //TypeOf + case 0x67: //CBool ... + case 0x6A: //Val + case 0x6B: //Str + case 0x6F: //Year + case 0x79: //Close + case 0x7B: //Line Input + case 0x7F: //Flush + case 0x80: //Lock + case 0x81: //Input From, Output To, Error To + case 0x85: //Kill + case 0x86: //Mkdir, deprecated -> Even() & Odd() + case 0x87: //Rmdir + case 0x8E: //DFree + case 0x90: //IsDir + case 0x95: //Free + case 0x98: //Sleep + case 0x99: //VarPtr + case 0x9B: //Tr + case 0x9C: //Quote, Shell, Html + case 0x9D: //Unquote + case 0x9E: //MkInteger, ... + case 0x9F: //Bool@, Byte@, ... + nargs = 1; break; + + case 0x45: //String + case 0x50: //Scan + case 0x5D: //Min + case 0x5E: //Max + case 0x62: //Math2 + case 0x64: //Bit + case 0x78: //Open + case 0x7D: //Read + case 0x88: //Move + case 0x89: //Copy + nargs = 2; break; + + case 0x8A: //Link, deprecated -> IsNan() & IsInf() + nargs = extra == 0 ? 2 : 1; break; + + case 0x52: //Conv + case 0x5F: //IIf + case 0x73: //DateAdd, DateDiff + case 0x7E: //Write + nargs = 3; break; + + case 0x93: //Exec + nargs = 4; break; + } + return nargs; +} + +static void JIT_push_unknown(){ + const char* name = CP->load->unknown[code[pos+1]]; + pos += 2; + bool defined; + + if (TYPE_is_pure_object(top()->type)){ + //These can now be on top of the stack: + //T_NULL, a null object of type top()->type + //A pure object (type > T_OBJECT) with type top()->type + //When top()->type is a virtual class: + // T_NULL can be returned (only by non-static methods at the moment) + // An object represented as a virtual class top()->type is the virtual class, + // but the real type can be something else. + // A T_CLASS of the class top()->type: + // happens when a native static method says it returns a virtual object + // But beware, a native static method might as well return a virtual object + // of type .SubCollection + defined = true; + CLASS* klass = (CLASS*)(void*)top()->type; + + JIT_load_class(klass); + + int index = CLASS_find_symbol(klass, name); + + bool is_unknown = false; + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] != NO_SYMBOL){ + push(new PushPureObjectUnknownExpression(pop(), name, code[pos-1])); + return; + } + + index = klass->special[SPEC_UNKNOWN]; + is_unknown = true; + } + + CLASS_DESC* desc = klass->table[index].desc; + + switch (CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: + if (klass->is_virtual){ + //FIXME OK to extract the value and ignore actually evaluating, and what if we got a null value..??? + /*delete*/ pop(); + push_constant(desc); + return; + } + push(new PushPureObjectConstantExpression(pop(), index)); + return; + case CD_VARIABLE: + /*int offset = desc->variable.offset; + TYPE type = desc->variable.ctype;*/ + push(new PushPureObjectVariableExpression(pop(), index)); + return; + + case CD_STATIC_VARIABLE: + THROW(E_STATIC, klass->name, name); + //push(new PushPureObjectStaticVariableExpression(pop(), index)); + return; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + if (klass->is_virtual) + push(new PushVirtualPropertyExpression(pop(), index)); + else + push(new PushPureObjectPropertyExpression(pop(), index)); + return; + + case CD_STRUCT_FIELD: + push(new PushPureObjectStructFieldExpression(pop(), index)); + return; + + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + //See System.User why this is a problem ... + //Runtime check is now done. + //if (!klass->is_virtual) + // THROW(E_STATIC, klass->name, name); + //push(new PushVirtualClassStaticProperty(pop(), index)); + push(new PushPureObjectStaticPropertyExpression(pop(), index, name)); + return; + + case CD_METHOD: + if (klass->is_virtual) + push(new PushVirtualFunctionExpression(pop(), index, is_unknown ? name : NULL)); + else + push(new PushPureObjectFunctionExpression(pop(), index, is_unknown ? name : NULL)); + return; + + case CD_STATIC_METHOD: + if (klass->is_virtual) + push(new PushVirtualStaticFunctionExpression(pop(), index, is_unknown ? name : NULL)); + else + push(new PushPureObjectStaticFunctionExpression(pop(), index, is_unknown ? name : NULL)); + return; + + case CD_EXTERN: + push(new PushExternExpression(klass, index, pop())); + return; + + default: THROW(E_NSYMBOL, klass->name, name); + } + + } else if (top()->type == T_OBJECT){ + defined = false; + push(new PushUnknownExpression(pop(), code[pos-1], code + pos - 2)); + } else if (top()->type == T_CLASS){ + defined = true; + + PushClassExpression* pce = dyn_cast(top()); + assert(pce); + + CLASS* klass = pce->klass; + + int index = CLASS_find_symbol(klass, name); + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] != NO_SYMBOL){ + pop(); + if (!klass->unknown_static || !klass->property_static){ + if (!klass->auto_create) + THROW(E_DYNAMIC); + push(new PushPureObjectUnknownExpression(new PushAutoCreateExpression(klass), name, code[pos-1])); + return; + } + push(new PushStaticUnknownExpression(klass, name, code[pos-1])); + return; + } + + index = klass->special[SPEC_UNKNOWN]; + + if (!klass->unknown_static){ + pop(); + if (!klass->auto_create) + THROW(E_DYNAMIC); + + push(new PushPureObjectFunctionExpression(new PushAutoCreateExpression(klass), index, name)); + return; + } + + push(new PushStaticFunctionExpression(pop(), index, name)); + return; + } + + CLASS_DESC* desc = klass->table[index].desc; + + switch (CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: { + /*delete*/ pop(); + push_constant(desc); + return; + } + case CD_VARIABLE: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + /*delete*/ pop(); + push(new PushPureObjectVariableExpression(new PushAutoCreateExpression(klass), index)); + return; + + case CD_STRUCT_FIELD: + THROW(E_DYNAMIC, klass->name, name); + + case CD_STATIC_VARIABLE: + /*delete*/ pop(); + push(new PushStaticVariableExpression(&desc->variable)); + return; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + /*delete*/ pop(); + push(new PushPureObjectPropertyExpression(new PushAutoCreateExpression(klass), index)); + return; + + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + push(new PushStaticPropertyExpression(pop(), index)); + return; + + case CD_METHOD: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + /*delete*/ pop(); + push(new PushPureObjectFunctionExpression(new PushAutoCreateExpression(klass), index, NULL)); + return; + + case CD_STATIC_METHOD: + /*int kind = FUNCTION_PUBLIC; + if (FUNCTION_is_native(&desc->method)){ + if (desc->method.subr) + kind = FUNCTION_SUBR; + else + kind = FUNCTION_NATIVE; + }*/ + push(new PushStaticFunctionExpression(pop(), index)); + return; + + case CD_EXTERN: + pop(); + push(new PushExternExpression(klass, desc->ext.exec, NULL)); + return; + + default: THROW(E_NSYMBOL, klass->name, name); + + } + + } else if (top()->type == T_FUNCTION){ + assert(false && "Syntax error"); + } else if (top()->type == T_VARIANT){ + defined = false; + push(new PushUnknownExpression(pop(), code[pos-1], code + pos - 2)); + } else if (top()->type == T_NULL) { + THROW(E_NULL); + } else { + THROW(E_NOBJECT); + } +} + +static void JIT_pop_unknown(){ + const char* name = CP->load->unknown[code[pos+1]]; + pos += 2; + + if (TYPE_is_pure_object(top()->type)){ + //These can now be on top of the stack: + //T_NULL, a null object of type top()->type + //A pure object (type > T_OBJECT) with type top()->type + //When top()->type is a virtual class: + // T_NULL can be returned (only by non-static methods at the moment) + // An object represented as a virtual class top()->type is the virtual class, + // but the real type can be something else. + // A T_CLASS of the class top()->type: + // happens when a native static method says it returns a virtual object + // But beware, a native static method might as well return a virtual object + // of type .SubCollection + // By the time of this writing, there are no write properties taking virtual classes. + CLASS* klass = (CLASS*)(void*)top()->type; + + int index = CLASS_find_symbol(klass, name); + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] == NO_SYMBOL) + THROW(E_NPROPERTY, klass->name, name); + + //Unknown property + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownPropertyUnknownExpression(obj, val, name)); + return; + } + + CLASS_DESC* desc = klass->table[index].desc; + + Expression* obj = pop(); + Expression* val = pop(); + + switch(CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: + THROW(E_NPROPERTY, klass->name, name); + + case CD_VARIABLE: + push_statement(new PopPureObjectVariableExpression(obj, val, index)); + return; + + case CD_STRUCT_FIELD: + if (desc->variable.ctype.id == TC_STRUCT || desc->variable.ctype.id == TC_ARRAY) + THROW(E_NWRITE, klass->name, name); + push_statement(new PopPureObjectStructFieldExpression(obj, val, index)); + return; + + case CD_STATIC_VARIABLE: + THROW(E_STATIC, klass->name, name); + //push_statement(new PopPureObjectStaticVariableExpression(obj, val, index)); + return; + + case CD_PROPERTY: + if (klass->is_virtual) + push_statement(new PopVirtualPropertyExpression(obj, val, index, name, false)); + else + push_statement(new PopPureObjectPropertyExpression(obj, val, index)); + return; + + case CD_STATIC_PROPERTY: + //Should only be used on virtual classes. Runtime check is added if not, throwing E_STATIC + //THROW(E_STATIC, klass->name, name); + push_statement(new PopVirtualPropertyExpression(obj, val, index, name, true)); + return; + + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + THROW(E_NWRITE, klass->name, name); + + case CD_METHOD: + case CD_STATIC_METHOD: + THROW(E_NPROPERTY, klass->name, name); + + default: THROW(E_NSYMBOL, klass->name, name); + } + } else if (top()->type == T_OBJECT){ + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownExpression(obj, val, code[pos-1], code + pos - 2)); + } else if (top()->type == T_CLASS){ + + PushClassExpression* pce = dyn_cast(top()); + assert(pce); + + CLASS* klass = pce->klass; + + int index = CLASS_find_symbol(klass, name); + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] == NO_SYMBOL) + THROW(E_NPROPERTY, klass->name, name); + + //Unknown property + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownPropertyUnknownExpression(obj, val, name)); + return; + } + + CLASS_DESC* desc = klass->table[index].desc; + + pop(); + Expression* val = pop(); + + switch(CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: + THROW(E_NPROPERTY, klass->name, name); + + case CD_VARIABLE: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + //delete obj; + push_statement(new PopPureObjectVariableExpression(new PushAutoCreateExpression(klass), val, index)); + return; + + case CD_STRUCT_FIELD: + THROW(E_DYNAMIC, klass->name, name); + + case CD_STATIC_VARIABLE: + //delete obj; + if (desc->variable.ctype.id == TC_ARRAY || desc->variable.ctype.id == TC_STRUCT) + THROW_ILLEGAL(); + push_statement(new PopStaticVariableExpression(desc->variable.type, (char*)desc->variable.klass->stat + desc->variable.offset, val)); + return; + + case CD_PROPERTY: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + //delete obj; + push_statement(new PopPureObjectPropertyExpression(new PushAutoCreateExpression(klass), val, index)); + return; + + case CD_STATIC_PROPERTY: + //delete obj; + push_statement(new PopStaticPropertyExpression(klass, val, index)); + return; + + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + THROW(E_NWRITE, klass->name, name); + + case CD_METHOD: + case CD_STATIC_METHOD: + THROW(E_NPROPERTY, klass->name, name); + + default: THROW(E_NSYMBOL, klass->name, name); + } + } else if (top()->type == T_FUNCTION){ + assert(false && "syntax error"); + } else if (top()->type == T_VARIANT){ + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownExpression(obj, val, code[pos-1], code + pos - 2)); + return; + } else if (top()->type == T_NULL) { + THROW(E_NULL); + } else { + THROW(E_NOBJECT); + } +} + +static void check_swap(){ + if (!in_dup && !stack_empty()){ + /* Then this have happened: + Push A + Push B + Pop A + Pop B + and we have just made the Pop A + So we replace Push A, Push B, Pop A to a SwapExpression, + that is taken as input for Pop B */ + push(new SwapExpression(pop(), pop_statement())); + } +} + +#define NEXT pos+=ncode; goto __NEXT; +#define CONT goto __NEXT; + +static void JIT_read_statement(){ + while(pos < size){ + unsigned short op = code[pos]; + int ncode; + switch (op & 0xFF00){ + case C_PUSH_UNKNOWN: case C_POP_UNKNOWN: + case C_PUSH_INTEGER: + case C_JUMP: case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: case C_GOSUB: + case C_NEXT: case C_JUMP_NEXT: + case C_TRY: + + ncode = 2; + break; + + case C_PUSH_LONG: + + ncode = 3; + break; + + case C_BYREF: + ncode = 2 + (op & 0xFF); + break; + + default: + + if ((op & 0xFF00) == (C_PUSH_CONST | 0xF00)) + ncode = 2; + else + ncode = 1; + } + + unsigned short digit = (op >> 12U); + int value = op & 0xFFF; + if (value >= 0x800) value |= 0xFFFFF000; + + switch (digit){ + case 0xF: + push(new PushIntegerExpression(32, value)); + NEXT + case 0xE: { + VALUE val; + unsigned short ind = op & 0xFFF; + if ((op & 0xF00) == 0xF00) + ind = code[++pos]; + #define LOCAL_gettext GB.Translate + VALUE_class_constant_inline(CP, &val, ind); + #undef LOCAL_gettext + + switch(val.type){ + case T_INTEGER: + push(new PushIntegerExpression(32, val._integer.value)); + NEXT + case T_LONG: + push(new PushIntegerExpression(64, val._long.value)); + NEXT + case T_SINGLE: + push(new PushFloatExpression(32, val._single.value)); + NEXT + case T_FLOAT: + push(new PushFloatExpression(64, val._float.value)); + NEXT + case T_CSTRING: + push(new PushCStringExpression(val._string.addr, val._string.start, val._string.len)); + NEXT + case T_POINTER: + push(new PushNullPointerExpression()); + NEXT + } + assert(false && "Invalid constant type"); + } + case 0xD: + if (value & 0x800) + push_statement(new PopStaticExpression(pop(), value & 0x7FF)); + else + push_statement(new PopDynamicExpression(pop(), value & 0x7FF)); + check_swap(); + NEXT + case 0xC: + if (value & 0x800) + push(new PushStaticExpression(value & 0x7FF)); + else + push(new PushDynamicExpression(value & 0x7FF)); + NEXT + case 0xB: + if (value & 0x800) + push(new PushFunctionExpression(value & 0x7FF)); + else + push(new PushClassExpression(value & 0x7FF)); + NEXT + case 0xA: + push(new AddQuickExpression(pop(), value)); + NEXT + + default: { + digit = op & 0xFF00; + value = op & 0xFF; + if (value >= 0x80) value |= 0xFFFFFF00; + + switch(digit){ + case C_PUSH_LOCAL: + if (value >= FP->n_local && get_ctrl_type(value) == T_CLASS){ + //Control variable + push(new PushClassExpression(get_ctrl_class(value))); + } else { + push(new PushLocalExpression(value)); + } + NEXT + case C_PUSH_PARAM: + push(new PushParamExpression(value)); + NEXT + case C_POP_LOCAL: + push_statement(new PopLocalExpression(pop(), value)); + check_swap(); + NEXT + case C_POP_PARAM: + push_statement(new PopParamExpression(pop(), value)); + check_swap(); + NEXT + case C_PUSH_UNKNOWN: + JIT_push_unknown(); + CONT + case C_POP_UNKNOWN: + JIT_pop_unknown(); + check_swap(); + CONT + case C_POP_CTRL: + in_dup = false; //From Dup + push_statement(new PopCtrlExpression(pop(), value)); + NEXT + case C_POP_OPTIONAL: + push_statement(new PopOptionalExpression(pop(), value)); + NEXT + case C_PUSH_EXTERN: + push(new PushExternExpression(CP, value, NULL)); + NEXT + case C_PUSH_EVENT: + if ((unsigned char)op == 0xFF){ + push(new PushEventExpression(0, CP->load->unknown[code[pos+1]])); + pos++; + } else { + push(new PushEventExpression(value, NULL)); + } + NEXT + case C_PUSH_ARRAY: + push(new PushArrayExpression(extract(value), value)); + NEXT + case C_POP_ARRAY: { + Expression** it = extract(value); + Expression* val = pop(); + push_statement(new PopArrayExpression(it, value, val)); + check_swap(); + NEXT + } + case C_CALL: { + Expression** it = extract(value); + Expression* func = pop(); + CallExpression* ce = new CallExpression(func, value, it, code + pos); + push(ce); + if ((code[pos+1] & 0xFF00) == C_BYREF){ + int nbyref_codes = 1 + (code[pos+1] & 0xFF); + uint64_t byref_mask = 0; + for(int i=0; idesc){ + signature = ce->desc->method.signature; + npmax = ce->desc->method.npmax; + } else if (ce->kind == FUNCTION_PRIVATE){ + FunctionExpression* fe = dynamic_cast(func); + int index = fe->function_index; + FUNCTION* fp = &CP->load->func[index]; + if (sizeof(TYPE) != sizeof(CLASS_PARAM)){ + //Something has changed... + abort(); + } + signature = (TYPE*)fp->param; + npmax = fp->n_param; + } + + for(int i=value; i --> 0;) if (1ULL<= npmax) + THROW(E_BYREF); + auto ex = new OnStackExpression(); + ex->type = signature ? signature[i] : -1; + + uint stack_start_save = stack_start; + stack_start = stack_size(); + push(ex); + JIT_read_statement(); + stack_start = stack_start_save; + + ce->byref_expressions.push_back(pop_statement()); + } + CONT + } else { + NEXT + } + } + case C_BYREF: { + int nbyref_codes = 1 + (code[pos] & 0xFF); + for(int i=0; iparent)); + } else { + if (OP) + push(new PushMeExpression()); + else + push(new PushClassExpression(CP)); + } + NEXT + } + case C_PUSH_MISC: + switch(value){ + case CPM_NULL: push(new PushNullExpression()); NEXT + case CPM_VOID: push(new PushVoidExpression()); NEXT + case CPM_FALSE: push(new PushIntegerExpression(1, false)); goto push_false_true; + case CPM_TRUE: push(new PushIntegerExpression(1, true)); goto push_false_true; + case CPM_LAST: push(new PushLastExpression()); NEXT + case CPM_STRING: push(new PushCStringExpression(NULL, 0, 0)); NEXT + case CPM_PINF: push(new PushFloatExpression(64, INFINITY)); NEXT + case CPM_MINF: push(new PushFloatExpression(64, -INFINITY)); NEXT + case CPM_COMPLEX: push(new PushComplexExpression(pop())); NEXT + } + assert(false && "Illegal Push Misc"); + push_false_true: { + if ((code[pos+1] & 0xFF00) == C_JUMP){ + //Bytecode representation of And if, Or if is implemented in this strange way + int addr = pos + 3 + code[pos+2]; + //There is a JUMP IF FALSE at addr + int false_addr = addr + code[addr+1] + 2; + push_statement(new JumpIfExpression(pop(), false_addr, addr + 2, false)); + pos += 3; + CONT + } + NEXT + } + case C_ON: { + int num_cases = value; + std::vector destinations; + destinations.resize(num_cases); + for(int i=0; i= EC){ + push_statement(new EndTryExpression()); + push_statement(new JumpExpression(pos + (short)code[pos+1] + 2 + 1)); + } else {*/ + push_statement(new JumpExpression(pos + (short)code[pos+1] + 2)); + //} + NEXT + case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: + push_statement(new JumpIfExpression(pop(), pos + (short)code[pos+1] + 2, pos + 2, digit == C_JUMP_IF_TRUE)); + NEXT + case C_JUMP_FIRST: { + Expression* step = pop(); + Expression* to = pop(); + push_statement(new JumpFirstExpression(value, to, step, code[pos+3] & 0xFF, pos + 4, pos + code[pos+2] + 3)); + NEXT + } + case C_JUMP_NEXT: + push_statement(new JumpNextExpression(code[pos-1] & 0xFF, code[pos+2] & 0xFF, pos + 3, pos + code[pos+1] + 2)); + pos += 3; + CONT + case C_GOSUB: { + ngosubs++; + push_statement(new GosubExpression(value, pos + (short)code[pos+1] + 2)); + NEXT + } + case C_FIRST: { + Expression* obj = pop(); + push_statement(new JumpEnumFirstExpression(value, obj)); + NEXT + } + case C_NEXT: + if (value) //Drop + push_statement(new JumpEnumNextExpression((JumpEnumFirstExpression*)top_statement(), pos + 2, pos + code[pos+1] + 2, code + pos, true, NULL)); + else { + OnStackExpression* ose = new OnStackExpression(); + push_statement(new JumpEnumNextExpression((JumpEnumFirstExpression*)top_statement(), pos + 2, pos + code[pos+1] + 2, code + pos, false, ose)); + push(ose); + addr = pos+2; + } + NEXT + case C_DROP: + push_statement(new DropExpression(pop())); + NEXT + case C_DUP: + in_dup = true; //Becomes false again after Pop Ctrl + push(new DupExpression(top())); + NEXT + case C_NEW: { + int nargs = value & 0x3F; + Expression** it = extract(nargs); + push(new NewExpression(it, nargs, value & CODE_NEW_EVENT)); + NEXT + } + case C_BREAK: + //PC = code+pos; + if (EXEC_profile) + push_statement(new ProfileLineExpression(code + pos)); + else + push_statement(new NopExpression(true/*JIF.F_DEBUG_get_current_position()*/)); + //PC = code; + //push_statement(new NopExpression()); + NEXT + case C_RETURN: + push_statement(new ReturnExpression(value == 1 ? pop() : NULL, value)); + NEXT + case C_QUIT: + switch(value){ + case 0: default: push_statement(new QuitExpression(NULL)); NEXT + case 1: push_statement(new NopExpression(false)); NEXT //FIXME breakpoint + case 2: push_statement(new StopEventExpression()); NEXT + case 3: push_statement(new QuitExpression(pop())); NEXT + } + case C_PUSH_CHAR: + push(new PushCStringExpression((char*)&STRING_char_string[value * 2], 0, 1)); + NEXT + case C_TRY: + push_statement(new TryExpression(pos + 2, pos + code[pos+1] + 2)); + NEXT + case C_END_TRY: + push_statement(new EndTryExpression()); + NEXT + case C_CATCH: + if (code + pos < EC) // No finally + push_statement(new ReturnExpression(NULL, 2)); + else // We have a finally somewhere above + push_statement(new CatchExpression()); + NEXT + + case C_EQ: push(new EqExpression(extract(2))); NEXT + case C_NE: push(new NotExpression(new EqExpression(extract(2)))); NEXT + case C_GT: push(new LessExpression(extract_reverse(2))); NEXT + case C_LE: push(new NotExpression(new LessExpression(extract_reverse(2)))); NEXT + case C_LT: push(new LessExpression(extract(2))); NEXT + case C_GE: push(new NotExpression(new LessExpression(extract(2)))); NEXT + case C_NEAR: push(new NearExpression(extract(2))); NEXT + case C_CASE: assert(false && "C_CASE is not used anymore?"); + #define op(_c, _n) case _c: push(new _n##Expression(extract(2))); NEXT + op(C_ADD, Add) + op(C_SUB, Sub) + op(C_MUL, Mul) + op(C_DIV, Div) + + op(C_QUO, Quo) + op(C_REM, Rem) + op(C_POW, Pow) + op(C_AND, And) + op(C_OR, Or) + op(C_XOR, Xor) + + op(C_IS, Is) + #undef op + case C_NEG: push(new NegExpression(pop())); NEXT + case C_NOT: push(new NotExpression(pop())); NEXT + case C_CAT: push(new CatExpression(extract(value), value)); NEXT + case C_FILE: push(new FileExpression(extract(value), value)); NEXT + case C_LIKE: push(new LikeExpression(extract(2), value)); NEXT + + + default: { + digit >>= 8U; + // 0x40 <= digit <= 0x9F + int extra = value & 0x3F; + int nargs = get_subr_nargs(digit, extra); + switch(digit){ + case 0x67: //CBool ... + JIT_conv(top(), extra); + goto _next; + case 0x6B: //Str + if (TYPE_is_string(top()->type)) + goto _next; + break; + } + push(new SubrExpression(digit, extract(nargs), nargs, extra)); + _next: + NEXT + } + } + } + } + __NEXT: + if (stack_empty()) //Next code row + return; + } +} + +void JIT_read(){ + code = FP->code; + size = ((int*)code)[-1] / sizeof(short); + if (FP->code[size-1] == 0) + --size; + + pos = 0; + + TRY { + if (EC != NULL){ + addr = -1; + push_statement(new LargeTryExpression()); + } + + func_byref_mask = 0; + + stack_start = 0; + while(pos < size){ + addr = pos; + PC = code + pos; + JIT_read_statement(); + } + + std::sort(taken_addresses.begin(), taken_addresses.end()); + for(size_t i=0, j=0, e1=taken_addresses.size(), e2=all_statements.size(); i!=e1 && j!=e2;){ + int a = taken_addresses[i]; + int b = all_statements[j]->addr; + if (a == b){ + all_statements[j]->address_taken = true; + i++, j++; + } else if (a < b){ + i++; + } else if (a > b){ + j++; + } + } + taken_addresses.clear(); + } + CATCH { + //puts("Fel:"); + //ERROR_print_at(stderr, 1, 1); + //abort(); + for(size_t i=0, e=all_statements.size(); i!=e; i++) + delete all_statements[i]; + all_statements.clear(); + free_all_expressions(); + taken_addresses.clear(); + JIF.F_ERROR_propagate(); + } + END_TRY +} diff --git a/gb.jit.llvm/src/jit_runtime.c b/gb.jit.llvm/src/jit_runtime.c new file mode 100644 index 00000000..a6b6df5a --- /dev/null +++ b/gb.jit.llvm/src/jit_runtime.c @@ -0,0 +1,1090 @@ +/*************************************************************************** + + jit_runtime.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_RUNTIME_C + +#include "gb_common.h" +#include "gbx_type.h" +#include "gbx_string.h" +//#include "gbx_object.h" +//#include "gbx_exec.h" +#include "gbx_number.h" + +#include "main.h" + +#include "jit_runtime.h" + +#define JR_STRING_unref(_p) \ +({ \ + char **pptr = _p; \ + char *ptr = *pptr; \ + STRING *str; \ + if (LIKELY(ptr != NULL)) \ + { \ + str = STRING_from_ptr(ptr); \ + if ((--str->ref) <= 0) \ + { \ + JIF.F_STRING_free_real(ptr); \ + *pptr = NULL; \ + } \ + } \ +}) + +#define JR_OBJECT_unref(_object) \ +{ \ + if (_object) \ + { \ + if ((--((OBJECT *)(_object))->ref) <= 0) \ + { \ + void *temp = (void *)(_object); \ + _object = NULL; \ + JIF.F_CLASS_free(temp); \ + } \ + } \ +} + +#define MANAGE_VARIANT(_func) \ +({ \ + if (P1->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P1); \ + if (P2->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + JIF.F_VALUE_convert_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + JIF.F_VALUE_convert_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + JIF.F_VALUE_convert_variant(P1); \ + return; \ + } \ +}) + +#define MANAGE_VARIANT_POINTER(_func) \ +({ \ + if (P1->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P1); \ + if (P2->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + JIF.F_VALUE_convert_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + JIF.F_VALUE_convert_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + JIF.F_VALUE_convert_variant(P1); \ + return; \ + } \ +}) + +static const char JR_EXEC_should_borrow[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 0, 0 }; + +#define JR_RELEASE(_value) \ +do { \ + VALUE *_v = (_value); \ + TYPE type = _v->type; \ + if (TYPE_is_object(type)) \ + { \ + JR_OBJECT_unref(_v->_object.object); \ + } \ + else if (JR_EXEC_should_borrow[type]) \ + { \ + if (type == T_STRING) \ + JR_STRING_unref(&_v->_string.addr); \ + else \ + JIF.F_EXEC_release(type, _v); \ + } \ +} while (0) + +CLASS_DESC_METHOD *JR_CLASS_get_special_desc(CLASS *class, int spec) +{ + short index = class->special[spec]; + + if (index == NO_SYMBOL) + return NULL; + else + return &CLASS_get_desc(class, index)->method; +} + +typedef +union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + DATE _date; + char *_string; + void *_pointer; + void *_object; + int64_t data; + } +VARIANT_value; + +void JR_release_variant(long vtype, char* val){ + if (vtype == T_STRING) + JR_STRING_unref(&val); + else if (TYPE_is_object(vtype)) + JR_OBJECT_unref(val); +} + +void JR_borrow_variant(long vtype, char* val){ + if (vtype == T_STRING) + STRING_ref(val); + else if (TYPE_is_object(vtype)) + OBJECT_REF(val); +} + +static double JR_date_to_float(DATE dateval){ + double date = dateval.date; + double time = dateval.time; + return date + time / 86400000.0; +} + +static double JR_string_to_float(char* addr){ + VALUE val; + int len = STRING_from_ptr(addr)->len; + int res = JIF.F_NUMBER_from_string(NB_READ_FLOAT, addr, len, &val); + JR_STRING_unref(&addr); + if (res) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_STRING)); + return val._float.value; +} + +void JR_aq_variant(int add){ + static void *_aq_jump[] = { + NULL, &&__AQ_BOOLEAN, &&__AQ_BYTE, &&__AQ_SHORT, &&__AQ_INTEGER, &&__AQ_LONG, &&__AQ_SINGLE, &&__AQ_FLOAT, + &&__AQ_DATE, &&__AQ_STRING, &&__AQ_STRING, &&__AQ_POINTER + }; + + TYPE NO_WARNING(type); + int NO_WARNING(value); + VALUE * NO_WARNING(P1); + void * NO_WARNING(jump_end); + + P1 = SP - 1; + + jump_end = &&__AQ_VARIANT_END; + JIF.F_VALUE_undo_variant(P1); + + type = P1->type; + value = add; + + if (LIKELY(type <= T_POINTER)) + goto *_aq_jump[type]; + + __AQ_BOOLEAN: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + + __AQ_BYTE: + + P1->_integer.value = (unsigned char)(P1->_integer.value + value); + goto *jump_end; + + __AQ_SHORT: + + P1->_integer.value = (short)(P1->_integer.value + value); + goto *jump_end; + + __AQ_INTEGER: + + P1->_integer.value += value; + goto *jump_end; + + __AQ_LONG: + + P1->_long.value += (int64_t)value; + goto *jump_end; + + __AQ_SINGLE: + + P1->_single.value += (float)value; + goto *jump_end; + + __AQ_DATE: + __AQ_STRING: + + JIF.F_VALUE_convert_float(P1); + + __AQ_FLOAT: + + P1->_float.value += (double)value; + goto *jump_end; + + __AQ_POINTER: + + P1->_pointer.value += value; + goto *jump_end; + + __AQ_VARIANT_END: + + JIF.F_VALUE_convert_variant(P1); + +} + +void JR_variant_equal(){ + static void *jump[17] = { + &&__SC_VARIANT, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG, &&__SC_SINGLE, &&__SC_FLOAT, &&__SC_DATE, + &&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, &&__SC_OBJECT + }; + + char NO_WARNING(result); + VALUE *NO_WARNING(P1); + VALUE *NO_WARNING(P2); + + P1 = SP - 2; + P2 = SP - 1; + + goto __SC_VARIANT; + +__SC_BOOLEAN: +__SC_BYTE: +__SC_SHORT: +__SC_INTEGER: + + result = P1->_integer.value == P2->_integer.value; + goto __SC_END; + +__SC_LONG: + + if (P1->type != T_LONG) + JIF.F_VALUE_convert(P1, T_LONG); + if (P2->type != T_LONG) + JIF.F_VALUE_convert(P2, T_LONG); + + result = P1->_long.value == P2->_long.value; + goto __SC_END; + +__SC_DATE: + + if (P1->type != T_DATE) + JIF.F_VALUE_convert(P1, T_DATE); + if (P2->type != T_DATE) + JIF.F_VALUE_convert(P2, T_DATE); + + result = JIF.F_DATE_comp((DATE *)&P1->_date.date, (DATE *)&P2->_date.date) == 0; + goto __SC_END; + +__SC_NULL: + + if (P2->type == T_NULL) + { + result = JIF.F_VALUE_is_null(P1); + goto __SC_END_RELEASE; + } + else if (P1->type == T_NULL) + { + result = JIF.F_VALUE_is_null(P2); + goto __SC_END_RELEASE; + } + +__SC_STRING: + + if (!TYPE_is_string(P1->type)) + JIF.F_VALUE_convert_string(P1); + if (!TYPE_is_string(P2->type)) + JIF.F_VALUE_convert_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0; + else + //result = STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + result = memcmp(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len) == 0; + + if (P1->type == T_STRING) + JR_STRING_unref(&P1->_string.addr); + if (P2->type == T_STRING) + JR_STRING_unref(&P2->_string.addr); + goto __SC_END; + +__SC_SINGLE: + + if (P1->type != T_SINGLE) + JIF.F_VALUE_convert(P1, T_SINGLE); + if (P2->type != T_SINGLE) + JIF.F_VALUE_convert(P2, T_SINGLE); + + result = P1->_single.value == P2->_single.value; + goto __SC_END; + +__SC_FLOAT: + + if (P1->type != T_FLOAT) + JIF.F_VALUE_convert_float(P1); + if (P2->type != T_FLOAT) + JIF.F_VALUE_convert_float(P2); + + result = P1->_float.value == P2->_float.value; + goto __SC_END; + +__SC_POINTER: + + if (P1->type != T_POINTER) + JIF.F_VALUE_convert(P1, T_POINTER); + if (P2->type != T_POINTER) + JIF.F_VALUE_convert(P2, T_POINTER); + + result = P1->_pointer.value == P2->_pointer.value; + goto __SC_END; + +__SC_OBJECT: + + result = JIF.F_OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SC_END_RELEASE; + +__SC_VARIANT: + + { + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + JIF.F_VALUE_undo_variant(P1); + } + + if (TYPE_is_variant(P2->type)) + { + JIF.F_VALUE_undo_variant(P2); + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", JIF.F_TYPE_get_name(Min(P1->type, P2->type))); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + goto *jump[type]; + } + +__SC_ERROR: + + THROW(E_TYPE, "Something comparable", JIF.F_TYPE_get_name(T_VARIANT)); + +__SC_END_RELEASE: + + JR_RELEASE(P1); + JR_RELEASE(P2); + +__SC_END: + + P1->type = T_BOOLEAN; + SP--; + P1->_boolean.value = -result; +} + +void JR_variant_compi_less_than(void) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT + }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + TYPE type; + + P1 = SP - 2; + P2 = P1 + 1; + + type = T_VARIANT; + goto __VARIANT; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value < P2->_integer.value ? -1 : 0; + goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + result = P1->_long.value < P2->_long.value ? -1 : 0; + goto __END; + +__DATE: + + JIF.F_VALUE_convert(P1, T_DATE); + JIF.F_VALUE_convert(P2, T_DATE); + + result = JIF.F_DATE_comp((DATE *)&P1->_date.date, (DATE *)&P2->_date.date) < 0 ? -1 : 0; + goto __END; + +__NULL: +__STRING: + + JIF.F_VALUE_convert_string(P1); + JIF.F_VALUE_convert_string(P2); + + result = JIF.F_STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) < 0 ? -1 : 0; + + if (P1->type == T_STRING) + JR_STRING_unref(&P1->_string.addr); + if (P2->type == T_STRING) + JR_STRING_unref(&P2->_string.addr); + goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + result = P1->_single.value < P2->_single.value ? -1 : 0; + goto __END; + +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + result = P1->_float.value < P2->_float.value ? -1 : 0; + goto __END; + +__POINTER: + + JIF.F_VALUE_convert(P1, T_POINTER); + JIF.F_VALUE_convert(P2, T_POINTER); + + result = P1->_pointer.value < P2->_pointer.value ? -1 : 0; + goto __END; + +__OBJECT: + + result = JIF.F_OBJECT_comp_value(P1, P2) < 0 ? -1 : 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__VARIANT: + + { + bool variant = FALSE; + + if (TYPE_is_variant(P1->type)) + { + JIF.F_VALUE_undo_variant(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + JIF.F_VALUE_undo_variant(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, JIF.F_TYPE_get_name(typem), JIF.F_TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", JIF.F_TYPE_get_name(type)); + +__END_RELEASE: + + JR_RELEASE(P1); + JR_RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = result; + SP--; + return; +} +void JR_add(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value | P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value + P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value + P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value += P2->_integer.value; goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + P1->_long.value += P2->_long.value; goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + P1->_single.value += P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + P1->_float.value += P2->_float.value; + //fprintf(stderr, "+: %.24g\n", P1->_float.value); + goto __END; + +__POINTER: + + JIF.F_VALUE_convert(P1, T_POINTER); + JIF.F_VALUE_convert(P2, T_POINTER); + + P1->_pointer.value += (intptr_t)P2->_pointer.value; goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER(JR_add); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + +__END: + + SP--; +} + +void JR_sub(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value ^ P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value - P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value - P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value -= P2->_integer.value; goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + P1->_long.value -= P2->_long.value; goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + P1->_single.value -= P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + P1->_float.value -= P2->_float.value; goto __END; + +__POINTER: + + JIF.F_VALUE_convert(P1, T_POINTER); + JIF.F_VALUE_convert(P2, T_POINTER); + + P1->_pointer.value -= (intptr_t)P2->_pointer.value; goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER(JR_sub); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + +__END: + + SP--; +} + +void JR_mul(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value & P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value * P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value * P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value *= P2->_integer.value; goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + P1->_long.value *= P2->_long.value; goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + P1->_single.value *= P2->_single.value; goto __END; + +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + P1->_float.value *= P2->_float.value; + //fprintf(stderr, "*: %.24g\n", P1->_float.value); + goto __END; + +__VARIANT: + + MANAGE_VARIANT(JR_mul); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + +__END: + + SP--; +} + +void JR_push_unknown_property_unknown(const char *name, int name_id, CLASS *class, void *object){ + bool call_static = object == NULL; + EXEC_unknown_name = name; + JIF.F_EXEC_special(SPEC_PROPERTY, class, class->property_static ? NULL : object, 0, FALSE); + if (class->unknown_static){ + JR_OBJECT_unref(object); + object = NULL; + } + if (SP[-1]._boolean.value){ + SP--; + EXEC_unknown_name = name; + JIF.F_EXEC_special(SPEC_UNKNOWN, class, object, 0, FALSE); + JIF.F_VALUE_convert_variant(&SP[-1]); + if (!call_static){ + SP[-2]._variant = SP[-1]._variant; + SP--; + } + JR_OBJECT_unref(object); + } else { + SP -= call_static ? 1 : 2; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + SP->_function.kind = FUNCTION_UNKNOWN; + SP->_function.index = name_id; + SP->_function.defined = FALSE; + SP++; + } +} + +void JR_pop_unknown_property_unknown(CLASS *class, void *object, const char *name){ + EXEC_unknown_name = name; + JIF.F_EXEC_special(SPEC_PROPERTY, class, object, 0, FALSE); + SP--; + if (!SP->_boolean.value) + THROW(E_NPROPERTY, class->name, name); + + EXEC_unknown_name = name; + + *SP = SP[-2]; + SP[-2].type = T_VOID; + SP++; + + JIF.F_EXEC_special(SPEC_UNKNOWN, class, object, 1, TRUE); + JR_OBJECT_unref(object); + + SP -= 2; +} + +void JR_call(int nparam){ + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE, &&__CALL_PRIVATE, &&__CALL_PUBLIC, + NULL, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + NULL + }; + + VALUE * NO_WARNING(val); + + int ind = nparam; + val = &SP[-(ind + 1)]; + + if (UNLIKELY(!TYPE_is_function(val->type))) + { + bool defined = FALSE; + if (TYPE_is_variant(val->type)) + EXEC.class = JIF.F_EXEC_object_variant(val, (OBJECT **)&EXEC.object); + else + JIF.F_EXEC_object_other(val, &EXEC.class, (OBJECT **)&EXEC.object); + + val->type = T_FUNCTION; + val->_function.kind = FUNCTION_CALL; + val->_function.defined = defined; + val->_function.class = EXEC.class; + val->_function.object = EXEC.object; + //goto _CALL; + } + else + { + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + } + + EXEC.nparam = ind; + EXEC.use_stack = TRUE; + + if (UNLIKELY(!val->_function.defined)) + *PC |= CODE_CALL_VARIANT; + + goto *call_jump[(int)val->_function.kind]; + +__CALL_NULL: + + while (ind > 0) + { + SP--; + JR_RELEASE(SP); + ind--; + } + + SP--; + JR_RELEASE(SP); + + //if (!PCODE_is_void(code)) + { + /*VALUE_default(SP, (TYPE)(val->_function.function));*/ + SP->type = T_NULL; + SP++; + } + + return; + +__CALL_NATIVE: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + //EXEC.use_stack = TRUE; + + goto __EXEC_NATIVE; + +__CALL_PRIVATE: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER; + +__CALL_PUBLIC: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + + goto __EXEC_ENTER; + +__EXEC_ENTER: + + JIF.F_EXEC_enter(); + if (EXEC.class->load->func[EXEC.index].fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); + return; + +__EXEC_NATIVE: + + JIF.F_EXEC_native(); + return; + +__CALL_UNKNOWN: + + EXEC_unknown_name = CP->load->unknown[val->_function.index]; + EXEC.desc = JR_CLASS_get_special_desc(EXEC.class, SPEC_UNKNOWN); + //EXEC.use_stack = TRUE; + goto __CALL_SPEC; + +__CALL_CALL: + + EXEC.desc = JR_CLASS_get_special_desc(EXEC.class, SPEC_CALL); + if (UNLIKELY(!EXEC.desc && !EXEC.object && EXEC.nparam == 1 && !EXEC.class->is_virtual)) + { + SP[-2] = SP[-1]; + SP--; + JIF.F_VALUE_convert(SP - 1, (TYPE)EXEC.class); + return; + } + + goto __CALL_SPEC; + +__CALL_SPEC: + + if (UNLIKELY(!EXEC.desc)) + THROW(E_NFUNC); + + EXEC.native = FUNCTION_is_native(EXEC.desc); + + if (EXEC.native) + { + JIF.F_EXEC_native(); + return; + } + else + { + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + JIF.F_EXEC_enter(); + if (EXEC.class->load->func[EXEC.index].fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); + return; + } + +__CALL_EXTERN: + + EXEC.index = val->_function.index; + JIF.F_EXTERN_call(); + return; +} + +OBJECT* JR_object_cast(OBJECT* object, CLASS* target_class){ + CLASS* class = object->class; + if ((class == target_class) || JIF.F_CLASS_inherits(class, target_class)) + return object; + + if (class->has_convert){ + OBJECT* conv = ((OBJECT *(*)())(class->convert))(object, target_class); + if (conv){ + OBJECT_REF(conv); + JR_OBJECT_unref(object); + return conv; + } + } + + JR_OBJECT_unref(object); + + THROW(E_TYPE, JIF.F_TYPE_get_name((TYPE)(void*)target_class), JIF.F_TYPE_get_name((TYPE)(void*)class)); +} + +void* JR_extern_dispatch_object(OBJECT* object, int index){ + if (object == NULL) + THROW(E_NULL); + CLASS* class = object->class; + JR_OBJECT_unref(object); + return JIF.F_EXTERN_get_function_info(&class->load->ext[class->table[index].desc->ext.exec]).call; +} + +void JR_exec_enter_quick(CLASS* klass, void* object, int index){ + CLASS_DESC_METHOD* desc = &klass->table[index].desc->method; + EXEC.desc = desc; + EXEC.index = (int)(intptr_t)(desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.object = object; + JIF.F_EXEC_enter_quick(); + if (FP->fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); +} +void JR_exec_enter(CLASS* klass, void* object, int index){ + CLASS_DESC_METHOD* desc = &klass->table[index].desc->method; + EXEC.desc = desc; + EXEC.index = (int)(intptr_t)(desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.object = object; + JIF.F_EXEC_enter(); + if (FP->fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); +} + +void JR_EXEC_jit_execute_function(){ + (*CP->jit_functions[EXEC.index])(); +} + +/*void JR_call_native_or_public(CLASS* klass, void* object, int index, int nparam){ + CLASS_DESC_METHOD* desc = &klass->table[index].desc->method; + if (desc->native){ + if (desc->subr){ + } else { + EXEC.native = TRUE; + EXEC.index = index; + EXEC.desc = desc; + EXEC.class = klass; + EXEC.object = object; + EXEC.nparam = nparam; + } + } else { + EXEC.desc = desc; + EXEC.index = (int)(intptr_t)(desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.object = object; + EXEC_enter(); + EXEC_jit_execute_function(); + } +}*/ +void* JR_try(ERROR_CONTEXT* err){ + ERROR_enter(err); + return err->env; +} +void JR_end_try(ERROR_CONTEXT* err){ + ERROR_leave(err); +} + +//FIXME Can this happen: Try native_call -> do non_native call -> non_native throw, directly back to first try? +//FIXME FIXME exec_function_keep måste ju sätta upp en try catch .. väl .. +void JR_try_unwind(VALUE* stack_start){ + JIF.F_ERROR_set_last(EP != NULL ? FALSE : TRUE); + + JIF.F_ERROR_lock(); + while(EC == NULL){ + JIF.F_EXEC_leave_drop(); + } + while(SP > stack_start){ + SP--; + JR_RELEASE(SP); + } + JIF.F_ERROR_unlock(); + + EP = NULL; +} diff --git a/gb.jit.llvm/src/jit_runtime.h b/gb.jit.llvm/src/jit_runtime.h new file mode 100644 index 00000000..e1cf9335 --- /dev/null +++ b/gb.jit.llvm/src/jit_runtime.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + jit_runtime.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_RUNTIME_H +#define __JIT_RUNTIME_H + +#include "gambas.h" + +#ifdef __cplusplus +extern "C" { +#endif +void JR_release_variant(long vtype, char* data); +void JR_borrow_variant(long vtype, char* data); +void JR_aq_variant(int add); +void JR_variant_equal(void); +void JR_variant_compi_less_than(void); +void JR_add(ushort code); +void JR_sub(ushort code); +void JR_mul(ushort code); +void JR_call(int nparam); + +void JR_push_unknown_property_unknown(const char *name, int name_id, CLASS *klass, void *object); +void JR_pop_unknown_property_unknown(CLASS *klass, void *object, const char *name); + +void* JR_extern_dispatch_object(OBJECT* object, int index); + +void JR_exec_enter_quick(CLASS* klass, void* object, int index); +void JR_exec_enter(CLASS* klass, void* object, int index); +OBJECT* JR_object_cast(OBJECT* object, CLASS* target_class); + +void JR_EXEC_jit_execute_function(void); + +void* JR_try(ERROR_CONTEXT* err); +void JR_end_try(ERROR_CONTEXT* err); +void JR_try_unwind(VALUE* stack_start); + +CLASS_DESC_METHOD *JR_CLASS_get_special_desc(CLASS *klass, int spec); +#ifdef __cplusplus +} +#endif + +#endif /* __JIT_RUNTIME_H */ diff --git a/gb.jit.llvm/src/main.cpp b/gb.jit.llvm/src/main.cpp new file mode 100644 index 00000000..b191a215 --- /dev/null +++ b/gb.jit.llvm/src/main.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "main.h" +// Don't use if we just need the version +#include + +extern "C" { + GB_INTERFACE GB EXPORT; + bool MAIN_debug = false; +} + +void *GB_JIT_1[] EXPORT = { + (void *)1, + (void *)JIT_init, + (void *)JIT_compile_and_execute, + (void *)JIT_load_class, + NULL +}; + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + + +extern "C" int EXPORT GB_INIT(void) +{ + char *env; + + env = getenv("GB_JIT_DEBUG"); + if (!env || !*env) + env = getenv("GB_JIT"); + + if (env && env[0] && strcmp(env, "0") != 0) + MAIN_debug = true; + + if (LLVM_VERSION_MAJOR < 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 1)) + fprintf(stderr, "gb.jit: warning: LLVM %d.%d is not supported.\n", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR); + else + { + if (MAIN_debug) + fprintf(stderr, "gb.jit: using LLVM %d.%d.\n", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR); + } + + return 0; +} + +extern "C" void EXPORT GB_EXIT() +{ + JIT_end(); +} diff --git a/gb.jit.llvm/src/main.h b/gb.jit.llvm/src/main.h new file mode 100644 index 00000000..371ad401 --- /dev/null +++ b/gb.jit.llvm/src/main.h @@ -0,0 +1,257 @@ +/*************************************************************************** + + main.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifdef __cplusplus +#define class klass +#endif + +#include "gbx_value.h" +#include "gbx_stack.h" +#include "gbx_class.h" +#include "gbx_object.h" + +#ifdef __cplusplus +#undef class +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern bool MAIN_debug; +#endif + +typedef + struct { +#ifdef __cplusplus + CLASS *klass; +#else + CLASS *class; +#endif + OBJECT *object; + int index; + CLASS_DESC_METHOD *desc; + char nparam; + bool native; + bool use_stack; + } + EXEC_GLOBAL; + +/*typedef + struct { + char code; // Error code + char native; // A native method has raised an error + char free; // If 'msg' sould be freed + char _reserved; + void *cp; + void *fp; + void *pc; + char *msg; + } + ERROR_INFO; + +typedef + struct _ERROR_CONTEXT { + struct _ERROR_CONTEXT *prev; + struct _ERROR_HANDLER *handler; + ERROR_INFO info; + jmp_buf env; + char ret; + } + ERROR_CONTEXT; + +typedef + struct _ERROR_HANDLER { + struct _ERROR_HANDLER *prev; + ERROR_CONTEXT *context; + void (*handler)(); + } + ERROR_HANDLER; + +#define ERROR_LEAVE_DONE ((ERROR_CONTEXT *)-1)*/ + +#include "gb.jit.h" + +#ifndef __JIT_API_CPP +extern GB_JIT_INTERFACE JIF; +extern char **_STACK_limit; +extern STACK_CONTEXT *_EXEC_current; +extern VALUE **_SP; +extern VALUE *_TEMP; +extern VALUE *_RET; + +extern char *_GAMBAS_StopEvent; +extern char **_EXEC_enum; +extern EXEC_GLOBAL *_EXEC; +extern const char **_EXEC_unknown_name; +extern char *_EXEC_profile; +extern char *_EXEC_profile_instr; +extern unsigned char *_EXEC_quit_value; + +extern void **_EVENT_Last; + +extern ERROR_CONTEXT **_ERROR_current; +extern ERROR_HANDLER **_ERROR_handler; + +extern const char *_STRING_char_string; +#endif + +#ifdef __cplusplus +} +#endif + +#define STACK_limit (*_STACK_limit) +#define EXEC_current (*_EXEC_current) +#define SP (*_SP) +#define TEMP (*_TEMP) +#define RET (*_RET) + +#define GAMBAS_StopEvent (*_GAMBAS_StopEvent) +#define EXEC_enum (*_EXEC_enum) +#define EXEC (*_EXEC) +#define EXEC_unknown_name (*_EXEC_unknown_name) +#define EXEC_profile (*_EXEC_profile) +#define EXEC_profile_instr (*_EXEC_profile_instr) +#define EXEC_quit_value (*_EXEC_quit_value) + +#define EVENT_Last (*_EVENT_Last) + +#define ERROR_current (*_ERROR_current) +#define ERROR_handler (*_ERROR_handler) + +#define STRING_char_string (_STRING_char_string) + +// Local variables base pointer +#define BP EXEC_current.bp +// Arguments base pointer +#define PP EXEC_current.pp +// Current class +#define CP EXEC_current.cp +// Current object +#define OP EXEC_current.op +// Save stack pointer for a TRY +#define EP EXEC_current.ep +// Current function +#define FP EXEC_current.fp +// Program counter +#define PC EXEC_current.pc +// Where to go if there is an error +#define EC EXEC_current.ec +// Save register for TRY +#define ET EXEC_current.et +// Last break in the function +#define TC EXEC_current.tc +// Stack at the last break in the function +#define TP EXEC_current.tp +// GoSub stack +#define GP EXEC_current.gp + +// Function return value pointer +#define RP (&RET) + +#define THROW JIF.F_THROW +#define THROW_ILLEGAL() THROW(E_ILLEGAL) +#define SYMBOL_find JIF.F_SYMBOL_find + +#undef TRY +#undef CATCH +#undef END_TRY +#undef ERROR_enter +#undef ERROR_leave + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_CONTEXT *__err = &__err_context; \ + ERROR_enter(__err); \ + __err->ret = setjmp(__err->env); \ + if (__err->ret == 0) + +#define CATCH \ + else + +#define END_TRY \ + ERROR_leave(__err); \ + } \ + } + + +#define ERROR_enter(_err) \ +do { \ + _err->prev = ERROR_current; \ + _err->info.code = 0; \ + _err->info.native = 0; \ + _err->handler = ERROR_handler; \ + ERROR_current = _err; \ +} while(0) + +#define ERROR_leave(_err) \ +do { \ + ERROR_CONTEXT *_prev = (_err); \ + if (_prev->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_current = _prev->prev; \ + if (ERROR_current) \ + { \ + if (_prev->info.code) \ + { \ + JIF.F_ERROR_reset(&ERROR_current->info); \ + ERROR_current->info = _prev->info; \ + ERROR_current->info.native = FALSE; \ + } \ + } \ + else \ + JIF.F_ERROR_reset(&_prev->info); \ + _prev->prev = ERROR_LEAVE_DONE; \ + } \ +} while(0) + + +#ifdef __cplusplus +extern "C" { +#endif + +void JIT_init(GB_JIT_INTERFACE *jif, char **__STACK_limit, STACK_CONTEXT *__EXEC_current, VALUE **__SP, VALUE *__TEMP, + VALUE *__RET, char *__GAMBAS_StopEvent, char ** __EXEC_enum, EXEC_GLOBAL *__EXEC, + const char **__EXEC_unknown_name, char *__EXEC_profile, char *__EXEC_profile_instr, unsigned char *__EXEC_quit_value, + void **__EVENT_Last, ERROR_CONTEXT **__ERROR_current, ERROR_HANDLER **__ERROR_handler, const char *__STRING_char_string); +void JIT_compile_and_execute(void); +void JIT_end(void); +void JIT_load_class(CLASS *klass); + +#ifdef __cplusplus +} +#endif + +#endif /* __MAIN_H */ diff --git a/gb.libxml/AUTHORS b/gb.libxml/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/COPYING b/gb.libxml/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.libxml/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.libxml/ChangeLog b/gb.libxml/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/INSTALL b/gb.libxml/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.libxml/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.libxml/Makefile.am b/gb.libxml/Makefile.am new file mode 100644 index 00000000..beea2ef3 --- /dev/null +++ b/gb.libxml/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @XML_DIR@ +EXTRA_DIST = reconf spec gambas.h diff --git a/gb.libxml/NEWS b/gb.libxml/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/README b/gb.libxml/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/acinclude.m4 b/gb.libxml/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.libxml/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.libxml/component.am b/gb.libxml/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.libxml/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.libxml/configure.ac b/gb.libxml/configure.ac new file mode 100644 index 00000000..ff422932 --- /dev/null +++ b/gb.libxml/configure.ac @@ -0,0 +1,20 @@ +dnl ---- configure.ac for gb.xml + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-libxml],[GB_VERSION],[GB_MAIL],[],[GB_URL]) + +AC_CONFIG_MACRO_DIR([m4]) +GB_INIT(gb.libxml) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + xml, XML, gb.libxml, [src], + libxml-2.0) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.libxml/gambas.h b/gb.libxml/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.libxml/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.libxml/m4 b/gb.libxml/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.libxml/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.libxml/reconf b/gb.libxml/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.libxml/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.libxml/src/CXMLDocument.c b/gb.libxml/src/CXMLDocument.c new file mode 100644 index 00000000..5de5545e --- /dev/null +++ b/gb.libxml/src/CXMLDocument.c @@ -0,0 +1,174 @@ +/*************************************************************************** + + CXMLDocument.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLDOCUMENT_C + +#include +#include +#include +#include +#include "main.h" +#include "CXMLDocument.h" + +CXMLNODE *XML_CreateNode(CXMLDOCUMENT *doc, xmlNode *node) +{ + CXMLNODE *p; + + if (!node) + return NULL; + + p = GB.New(GB.FindClass("XmlNode"), NULL, NULL); + p->node = node; + p->doc = doc; + GB.Ref(doc); + return p; +} + +static void free_document(CXMLDOCUMENT *_object) +{ + if (THIS->doc) + { + xmlFreeDoc(THIS->doc); + THIS->doc = NULL; + } +} + + +void XML_InitDocument(CXMLDOCUMENT *_object, xmlDoc *doc, const char *err) +{ + if (!doc) + { + GB.Error(err ? err : "Unable to parse XML file"); + return; + } + + free_document(THIS); + THIS->doc = doc; +} + + +BEGIN_METHOD_VOID (CXMLDocument_Free) + + free_document(THIS); + +END_METHOD + + +BEGIN_METHOD (CXMLDocument_Open,GB_STRING FileName;) + + const char *path; + + path = GB.RealFileName(STRING(FileName), LENGTH(FileName)); + XML_InitDocument(THIS, xmlParseFile(path), NULL); + +END_METHOD + +BEGIN_METHOD (CXMLDocument_FromString,GB_STRING Data;) + + XML_InitDocument(THIS, xmlParseDoc((xmlChar *)GB.ToZeroString(ARG(Data))), NULL); + +END_METHOD + +BEGIN_METHOD (CXMLDocument_HtmlFromString,GB_STRING Data;) + + XML_InitDocument(THIS, htmlParseDoc((xmlChar *)GB.ToZeroString(ARG(Data)),NULL), NULL); + +END_METHOD + + +BEGIN_METHOD (CXMLDocument_Write,GB_STRING FileName; GB_STRING Encoding;) + + char *enc; + + if (!THIS->doc) + { + GB.Error("Unable to write NULL document"); + return; + } + + if (MISSING(Encoding)) + enc="UTF-8"; + else + enc=GB.ToZeroString(ARG(Encoding)); + + xmlSaveFormatFileEnc(GB.ToZeroString(ARG(FileName)), THIS->doc,enc , 1); + +END_METHOD + +BEGIN_METHOD(CXMLDocument_ToString, GB_STRING Encoding) + + xmlChar *mem; + int size; + char *encoding; + + if (!THIS->doc) + { + GB.ReturnVoidString(); + return; + } + + if (MISSING(Encoding)) + encoding = "UTF-8"; + else + encoding = GB.ToZeroString(ARG(Encoding)); + + xmlDocDumpFormatMemoryEnc(THIS->doc, &mem, &size, encoding, 1); + + GB.ReturnNewString((char*)mem,size); + xmlFree(mem); + +END_METHOD + +BEGIN_PROPERTY (CXMLDocument_Root) + + GB.ReturnObject(XML_CreateNode(THIS, xmlDocGetRootElement(THIS->doc))); + +END_PROPERTY + + +GB_DESC CXmlDocumentDesc[] = +{ + + GB_DECLARE("XmlDocument", sizeof(CXMLDOCUMENT)), + + GB_PROPERTY_READ("Root","XmlNode",CXMLDocument_Root), + + GB_METHOD("_free",NULL,CXMLDocument_Free,NULL), + + GB_METHOD("Open", NULL, CXMLDocument_Open, "(FileName)s"), + GB_METHOD("FromString", NULL, CXMLDocument_FromString, "(Data)s"), + GB_METHOD("HtmlFromString", NULL, CXMLDocument_HtmlFromString, "(Data)s"), + GB_METHOD("Write", NULL, CXMLDocument_Write, "(FileName)s[(Encoding)s]"), + //GB_METHOD("Write",NULL,CXMLDocument_Write,"(FileName)s[(Encoding)s]"), + GB_METHOD("ToString", "s", CXMLDocument_ToString, "[(Encoding)s]"), + + + + GB_END_DECLARE +}; + + + + + + diff --git a/gb.libxml/src/CXMLDocument.h b/gb.libxml/src/CXMLDocument.h new file mode 100644 index 00000000..2d8f5a52 --- /dev/null +++ b/gb.libxml/src/CXMLDocument.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CXMLDocument.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLDOCUMENT_H +#define __CXMLDOCUMENT_H + +#include +#include +#include "gambas.h" +#include "CXMLNode.h" + +#ifndef __CXMLDOCUMENT_C + +extern GB_DESC CXmlDocumentDesc[]; + +#else + +#define THIS ((CXMLDOCUMENT *)_object) + +#endif + +typedef + struct _CXMLDOCUMENT + { + GB_BASE ob; + xmlDoc *doc; + } + CXMLDOCUMENT; + +CXMLNODE *XML_CreateNode(CXMLDOCUMENT *doc, xmlNode *node); +void XML_InitDocument(CXMLDOCUMENT *_object, xmlDoc *doc, const char *err); + +#endif + diff --git a/gb.libxml/src/CXMLNode.c b/gb.libxml/src/CXMLNode.c new file mode 100644 index 00000000..5da94bc0 --- /dev/null +++ b/gb.libxml/src/CXMLNode.c @@ -0,0 +1,300 @@ +/*************************************************************************** + + CXMLNode.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLNODE_C + +#include +#include +#include "main.h" +#include "CXMLDocument.h" +#include "CXMLNode.h" + +/*int CXMLNode_check(void *_object) +{ + return !THIS->parent; +}*/ + + +BEGIN_PROPERTY(CXMLNode_Next) + + if (!THIS->node->next) + GB.ReturnNull(); + else + GB.ReturnObject(XML_CreateNode(THIS->doc, THIS->node->next)); + +END_PROPERTY + +BEGIN_PROPERTY(CXMLNode_Prev) + + if (!THIS->node->prev) + GB.ReturnNull(); + else + GB.ReturnObject(XML_CreateNode(THIS->doc, THIS->node->prev)); + +END_PROPERTY + +BEGIN_PROPERTY(CXMLNode_Parent) + + if (!THIS->node->parent) + GB.ReturnNull(); + else + GB.ReturnObject(XML_CreateNode(THIS->doc, THIS->node->parent)); + +END_PROPERTY + + +BEGIN_PROPERTY(CXMLNode_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString((const char *)THIS->node->name); + else + xmlNodeSetName(THIS->node, (const xmlChar *)GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(CXMLNode_Value) + + if (READ_PROPERTY) + GB.ReturnNewZeroString((const char *)xmlNodeGetContent(THIS->node)); + else + fprintf(stderr, "*NOT IMPLEMENTED*"); + +END_PROPERTY + + +BEGIN_PROPERTY(CXmlNode_c_count) + + int nval=0; + xmlNode *ch; + + ch=THIS->node->children; + + while(ch) + { + nval++; + ch=ch->next; + } + + GB.ReturnInteger(nval); + +END_METHOD + +/*************************************************************** + "Creating" functions + *************************************************************/ +BEGIN_METHOD(CXMLNode_AddAttr,GB_STRING Name; GB_STRING Value) + + char *name,*value; + + name=GB.ToZeroString(ARG(Name)); + value=GB.ToZeroString(ARG(Value)); + + if (!xmlNewProp(THIS->node,BAD_CAST name,BAD_CAST value)) + GB.Error("Unable to add XML Attribute"); + +END_METHOD + +BEGIN_METHOD(CXMLNode_AddElement,GB_STRING Name; GB_STRING Value) + + char *name,*value; + + name=GB.ToZeroString(ARG(Name)); + value=GB.ToZeroString(ARG(Value)); + + + if (!xmlNewChild(THIS->node, NULL, BAD_CAST name, BAD_CAST value)) + GB.Error("Unable to add XML Element"); + +END_METHOD + +/************************************************************** + NODE CHILDREN +***************************************************************/ +BEGIN_METHOD(CXmlNode_c_get,GB_INTEGER Element) + + int nval=0; + int nMax; + xmlNode *ch; + + nMax = VARG(Element); + + ch = THIS->node->children; + + if (!ch) + { + GB.Error("Out of bounds"); + return; + } + + for (nval=0;nvalnext; + if (!ch) break; + } + + if (!ch) + { + GB.Error("Out of bounds"); + return; + } + + GB.ReturnObject(XML_CreateNode(THIS->doc, ch)); + +END_METHOD + +/****************************************************************** + NODE ATTRIBUTES +*******************************************************************/ + +BEGIN_METHOD_VOID(CXmlNode_a_next) + + xmlNodePtr attr; + int bucle; + long *wenum; + + wenum=(long*)GB.GetEnum(); + + attr=(xmlNodePtr)THIS->node->properties; + + if (!attr) + { + GB.StopEnum(); + return; + } + + for(bucle=0;bucle<(*wenum);bucle++) + { + attr=attr->next; + if (!attr) + { + GB.StopEnum(); + return; + } + } + + (*wenum)++; + + GB.ReturnObject(XML_CreateNode(THIS->doc, attr)); + +END_METHOD + +BEGIN_PROPERTY(CXmlNode_a_count) + + int nval=0; + xmlAttr *ch; + + ch=THIS->node->properties; + + while(ch) + { + nval++; + ch=ch->next; + } + + GB.ReturnInteger(nval); + +END_METHOD + + +BEGIN_METHOD_VOID(CXMLNode_Free) + + GB.Unref(POINTER(&THIS->doc)); + +END_METHOD + +GB_DESC CXmlNodeChildrenDesc[] = +{ + GB_DECLARE(".XmlNode.Children", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "XmlNode",CXmlNode_c_get, "(Element)i"), + GB_PROPERTY_READ("Count", "i", CXmlNode_c_count), + + GB_END_DECLARE +}; + + +GB_DESC CXmlNodeAttributesDesc[] = +{ + GB_DECLARE(".XmlNode.Attributes", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "XmlNode", CXmlNode_a_next, NULL), + GB_PROPERTY_READ("Count", "i", CXmlNode_a_count), + + GB_END_DECLARE +}; + + + +GB_DESC CXmlNodeDesc[] = +{ + GB_DECLARE("XmlNode", sizeof(CXMLNODE)), GB_NOT_CREATABLE(), + + GB_NOT_CREATABLE(), + //GB_HOOK_CHECK(CXMLNode_check), + + GB_CONSTANT("ElementNode", "i", XML_ELEMENT_NODE), + GB_CONSTANT("AttributeNode", "i", XML_ATTRIBUTE_NODE), + GB_CONSTANT("TextNode", "i", XML_TEXT_NODE), + GB_CONSTANT("CDataSectionNode", "i", XML_CDATA_SECTION_NODE), + GB_CONSTANT("EntityRefNode", "i", XML_ENTITY_REF_NODE), + GB_CONSTANT("EntityNode", "i", XML_ENTITY_NODE), + GB_CONSTANT("PiNode", "i", XML_PI_NODE), + GB_CONSTANT("CommentNode", "i", XML_COMMENT_NODE), + GB_CONSTANT("DocumentNode", "i", XML_DOCUMENT_NODE), + GB_CONSTANT("DocumentTypeNode", "i", XML_DOCUMENT_TYPE_NODE), + GB_CONSTANT("DocumentFragNode", "i", XML_DOCUMENT_FRAG_NODE), + GB_CONSTANT("NotationNode", "i", XML_NOTATION_NODE), + GB_CONSTANT("HtmlDocumentNode", "i", XML_HTML_DOCUMENT_NODE), + GB_CONSTANT("DtdNode", "i", XML_DTD_NODE), + GB_CONSTANT("ElementDecl", "i", XML_ELEMENT_DECL), + GB_CONSTANT("AttributeDecl", "i", XML_ATTRIBUTE_DECL), + GB_CONSTANT("EntityDecl", "i", XML_ENTITY_DECL), + GB_CONSTANT("NamespaceDecl", "i", XML_NAMESPACE_DECL), + GB_CONSTANT("XIncludeStart", "i", XML_XINCLUDE_START), + GB_CONSTANT("XIncludeEnd", "i", XML_XINCLUDE_END), + GB_CONSTANT("DocbDocumentNode", "i", XML_DOCB_DOCUMENT_NODE), + + GB_PROPERTY("Name","s",CXMLNode_Name), + GB_PROPERTY("Value","s",CXMLNode_Value), + + GB_PROPERTY_READ("Parent","XmlNode",CXMLNode_Parent), + GB_PROPERTY_READ("Next","XmlNode",CXMLNode_Next), + GB_PROPERTY_READ("Previous","XmlNode",CXMLNode_Prev), + GB_PROPERTY_SELF("Children",".XmlNode.Children"), + GB_PROPERTY_SELF("Attributes",".XmlNode.Attributes"), + + GB_METHOD("NewAttribute",NULL,CXMLNode_AddAttr,"(Name)s(Value)s"), + GB_METHOD("NewElement",NULL,CXMLNode_AddElement,"(Name)s(Value)s"), + + GB_METHOD("_free",NULL,CXMLNode_Free,NULL), + + GB_END_DECLARE +}; + + + + + + + + diff --git a/gb.libxml/src/CXMLNode.h b/gb.libxml/src/CXMLNode.h new file mode 100644 index 00000000..8232528c --- /dev/null +++ b/gb.libxml/src/CXMLNode.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CXMLNode.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLNODE_H +#define __CXMLNODE_H + +#include +#include "gambas.h" + +#ifndef __CXMLNODE_C + + +extern GB_DESC CXmlNodeAttributesDesc[]; +extern GB_DESC CXmlNodeChildrenDesc[]; +extern GB_DESC CXmlNodeDesc[]; + +#else + +#define THIS ((CXMLNODE *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + xmlNode *node; + struct _CXMLDOCUMENT *doc; + } + CXMLNODE; + +#endif diff --git a/gb.libxml/src/CXMLReader.c b/gb.libxml/src/CXMLReader.c new file mode 100644 index 00000000..d0104f58 --- /dev/null +++ b/gb.libxml/src/CXMLReader.c @@ -0,0 +1,517 @@ +/*************************************************************************** + + CXMLReader.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLREADER_C + +#include +#include +#include +#include +#include "main.h" +#include "CXMLReader.h" + +unsigned char b64value(char car) +{ + if ( (car>=65) && (car<=90) ) return car-65; + if ( (car>=97) && (car<=122) ) return car-71; + if ( (car>=48) && (car<=57) ) return car+4; + if (car=='+') return 62; + if (car=='/') return 63; + if (car=='=') return 254; + return 255; + +} + +void FromBinHex(char *src,char *dst) +{ + char b; + unsigned long bucle; + int zone=0; + + for (bucle=0;bucle>4); + dst[retval-2]=car<<4; + zone++; + break; + case 2: + dst[retval-2]+=car>>2; + dst[retval-1]=car<<6; + zone++; + break; + case 3: + dst[retval-1]+=car; + zone=0; + break; + case 4: + return retval; + } + } + } + + return retval-pads; +} + + +int Check_Reader(CXMLREADER *test) +{ + if (!test->reader) + { + GB.Error("No XML file or string to read from"); + return 1; + } + + if (test->eof) + { + GB.Error("Reached end of file"); + return 1; + } + + return 0; +} + +void Free_Reader(CXMLREADER *test) +{ + if (test->buffer)GB.Free(POINTER(&test->buffer)); + if (test->reader) + { + xmlTextReaderClose(test->reader); + xmlFreeTextReader(test->reader); + test->reader=NULL; + } + test->eof=0; +} + +BEGIN_METHOD (CXmlReader_Open,GB_STRING FileName) + + Free_Reader(THIS); + THIS->reader=xmlReaderForFile(GB.ToZeroString(ARG(FileName)),NULL,0); + if (!THIS->reader) GB.Error ("Unable to parse XML file"); + + +END_METHOD + +BEGIN_METHOD (CXmlReader_FromString,GB_STRING Buffer;GB_STRING URL;) + + if (!LENGTH(Buffer)) + { + GB.Error ("Unable to parse NULL string"); + return; + } + + Free_Reader(THIS); + + GB.Alloc(POINTER(&THIS->buffer),LENGTH(Buffer)); + memcpy (THIS->buffer,STRING(Buffer),LENGTH(Buffer)); + + if (!MISSING(URL)) + THIS->reader=xmlReaderForMemory(THIS->buffer,LENGTH(Buffer),GB.ToZeroString(ARG(URL)),NULL,0); + else + THIS->reader=xmlReaderForMemory(THIS->buffer,LENGTH(Buffer),"",NULL,0); + + if (!THIS->reader) GB.Error ("Unable to parse XML file"); + + +END_METHOD + +BEGIN_METHOD_VOID (CXmlReader_Read) + + int retval; + + if (Check_Reader(THIS)) return; + + retval=xmlTextReaderRead(THIS->reader); + switch(retval) + { + case 0: + THIS->eof=1; + break; + + case -1: + Free_Reader(THIS); + GB.Error("Error parsing XML file"); + break; + } + + +END_METHOD + +BEGIN_METHOD (CXmlReader_Decode,GB_STRING Data;GB_STRING Encoding;) + + char *dst=NULL; + unsigned long len; + + if (!strcasecmp(GB.ToZeroString(ARG(Encoding)),"base64") ) { + if (!LENGTH(Data)) return; + + GB.Alloc (POINTER(&dst),LENGTH(Data) ); + len=FromBase64(GB.ToZeroString(ARG(Data)),dst); + GB.ReturnNewString(dst,len); + GB.Free(POINTER(&dst)); + return; + } + + if (!strcasecmp(GB.ToZeroString(ARG(Encoding)),"binhex") ) { + if (!LENGTH(Data)) return; + if (LENGTH(Data)%2) return; + + dst=STRING(Data); + for(len=0;lenreader); + else eval=xmlTextReaderMoveToNextAttribute(THIS->reader); + + if (eval==-1) + { + xmlFreeTextReader(THIS->reader); + THIS->reader=NULL; + GB.StopEnum(); + GB.Error("Error parsing XML file"); + return; + } + + if (!eval) + { + if (wenum[0]) xmlTextReaderMoveToElement(THIS->reader); + GB.StopEnum(); + return; + } + + wenum[0]=1; + RETURN_SELF(); + + + +END_METHOD + +BEGIN_PROPERTY(CXmlReader_count) + + int nval; + + if (Check_Reader(THIS)) return; + + nval=xmlTextReaderAttributeCount(THIS->reader); + + if (nval==-1) + { + xmlFreeTextReader(THIS->reader); + THIS->reader=NULL; + GB.Error("Error parsing XML file"); + return; + } + + GB.ReturnInteger(nval); + +END_METHOD + +BEGIN_PROPERTY(CXMLReader_EOF) + + if (!THIS->reader) + { + GB.ReturnBoolean(1); + return; + } + + GB.ReturnBoolean(THIS->eof); + +END_PROPERTY + + +BEGIN_PROPERTY(CRNODE_BaseUri) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderBaseUri(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Depth) + + if (Check_Reader(THIS)) return; + GB.ReturnInteger( xmlTextReaderDepth(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_IsDefault) + + if (Check_Reader(THIS)) return; + GB.ReturnBoolean(xmlTextReaderIsDefault(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_IsEmptyElement) + + if (Check_Reader(THIS)) return; + GB.ReturnBoolean(xmlTextReaderIsEmptyElement(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_LocalName) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderLocalName(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Name) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderName(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_NamespaceUri) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderNamespaceUri(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Prefix) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderPrefix(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_QuoteChar) + + char car='\"'; + + if (Check_Reader(THIS)) return; + + car=(char)xmlTextReaderQuoteChar(THIS->reader); + GB.ReturnNewString(&car,1); + +END_PROPERTY + + +BEGIN_PROPERTY(CRNODE_Value) + + char *buf; + + if (Check_Reader(THIS)) return; + + buf=(char *)xmlTextReaderValue(THIS->reader); + GB.ReturnNewZeroString(buf); + if (buf) xmlFree(buf); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Type) + + if (Check_Reader(THIS)) return; + GB.ReturnInteger( xmlTextReaderNodeType(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_XmlLang) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((char *)xmlTextReaderXmlLang(THIS->reader)); + +END_PROPERTY + + +GB_DESC CXmlReaderNodeTypeDesc[] = +{ + GB_DECLARE("XmlReaderNodeType", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", XML_READER_TYPE_NONE), + GB_CONSTANT("Element", "i", XML_READER_TYPE_ELEMENT), + GB_CONSTANT("Attribute", "i", XML_READER_TYPE_ATTRIBUTE), + GB_CONSTANT("Text", "i",XML_READER_TYPE_TEXT), + GB_CONSTANT("CDATA", "i", XML_READER_TYPE_CDATA), + GB_CONSTANT("EntityReference", "i", XML_READER_TYPE_ENTITY_REFERENCE), + GB_CONSTANT("Entity", "i",XML_READER_TYPE_ENTITY), + GB_CONSTANT("ProcessingInstruction", "i",XML_READER_TYPE_PROCESSING_INSTRUCTION), + GB_CONSTANT("Comment", "i", XML_READER_TYPE_COMMENT), + GB_CONSTANT("Document", "i", XML_READER_TYPE_DOCUMENT), + GB_CONSTANT("DocumentType", "i", XML_READER_TYPE_DOCUMENT_TYPE), + GB_CONSTANT("DocumentFragment", "i", XML_READER_TYPE_DOCUMENT_FRAGMENT), + GB_CONSTANT("Notation", "i", XML_READER_TYPE_NOTATION), + GB_CONSTANT("Whitespace", "i",XML_READER_TYPE_WHITESPACE), + GB_CONSTANT("SignificantWhitespace", "i",XML_READER_TYPE_SIGNIFICANT_WHITESPACE), + GB_CONSTANT("EndElement", "i", XML_READER_TYPE_END_ELEMENT), + GB_CONSTANT("EndEntity", "i", XML_READER_TYPE_END_ENTITY), + GB_CONSTANT("XmlDeclaration", "i",XML_READER_TYPE_XML_DECLARATION), + + GB_END_DECLARE +}; + +GB_DESC CXmlReaderNodeAttributesDesc[] = +{ + GB_DECLARE(".XmlReader.Node.Attributes", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", ".XmlReader.Node", CXmlReader_next, NULL), + GB_PROPERTY_READ("Count", "i", CXmlReader_count), + + GB_END_DECLARE +}; + +GB_DESC CXmlReaderNodeDesc[] = +{ + GB_DECLARE(".XmlReader.Node", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Attributes", ".XmlReader.Node.Attributes",CXMLReader_Node), + GB_PROPERTY_READ("BaseUri","s",CRNODE_BaseUri), + GB_PROPERTY_READ("Depth","i",CRNODE_Depth), + GB_PROPERTY_READ("IsDefault","b",CRNODE_IsDefault), + GB_PROPERTY_READ("IsEmptyElement","b",CRNODE_IsEmptyElement), + GB_PROPERTY_READ("LocalName","s",CRNODE_LocalName), + GB_PROPERTY_READ("Name", "s", CRNODE_Name), + GB_PROPERTY_READ("NamespaceUri", "s", CRNODE_NamespaceUri), + GB_PROPERTY_READ("Prefix", "s", CRNODE_Prefix), + GB_PROPERTY_READ("QuoteChar", "s", CRNODE_QuoteChar), + GB_PROPERTY_READ("Type","i",CRNODE_Type), + GB_PROPERTY_READ("Value", "s", CRNODE_Value), + GB_PROPERTY_READ("XmlLang", "s", CRNODE_XmlLang), + + GB_END_DECLARE +}; + +GB_DESC CXmlReaderDesc[] = +{ + GB_DECLARE("XmlReader", sizeof(CXMLREADER)), + + GB_STATIC_METHOD("Decode","s",CXmlReader_Decode,"(Data)s(Encoding)s"), + + GB_METHOD("_free",NULL,CXmlReader_Free,NULL), + + GB_METHOD("Open", NULL, CXmlReader_Open,"(FileName)s"), + GB_METHOD("Close",NULL,CXmlReader_Free,NULL), + GB_METHOD("FromString",NULL,CXmlReader_FromString,"(Buffer)s[(URL)s]"), + + GB_METHOD("Read",NULL,CXmlReader_Read,NULL), + + GB_PROPERTY_READ("Eof","b",CXMLReader_EOF), + GB_PROPERTY_READ("Node",".XmlReader.Node",CXMLReader_Node), + + GB_END_DECLARE +}; + + + + diff --git a/gb.libxml/src/CXMLReader.h b/gb.libxml/src/CXMLReader.h new file mode 100644 index 00000000..0c38e214 --- /dev/null +++ b/gb.libxml/src/CXMLReader.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CXMLReader.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLREADER_H +#define __CXMLREADER_H + +#include "gambas.h" +#include + +#ifndef __CXMLREADER_C + +extern GB_DESC CXmlReaderNodeTypeDesc[]; +extern GB_DESC CXmlReaderNodeDesc[]; +extern GB_DESC CXmlReaderNodeAttributesDesc[]; +extern GB_DESC CXmlReaderDesc[]; + +#else + +#define THIS ((CXMLREADER *)_object) + +#endif + + +typedef struct +{ + GB_BASE ob; + xmlTextReaderPtr reader; + char *buffer; + int eof; + +} CXMLREADER; + + +#endif diff --git a/gb.libxml/src/CXMLWriter.c b/gb.libxml/src/CXMLWriter.c new file mode 100644 index 00000000..81d263df --- /dev/null +++ b/gb.libxml/src/CXMLWriter.c @@ -0,0 +1,429 @@ +/*************************************************************************** + + CXMLWriter.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLWRITER_C + +#include +#include +#include +#include "main.h" +#include "CXMLWriter.h" + + + +int Check_Writer(CXMLWRITER *test) +{ + if (!test->writer) + { + GB.Error("No XML file or string to write to"); + return 1; + } + return 0; +} + +void Free_Writer(CXMLWRITER *test) +{ + if (test->writer) + { + xmlTextWriterEndDocument(test->writer); + xmlFreeTextWriter(test->writer); + test->writer=NULL; + } + if (test->buffer) xmlBufferFree(test->buffer); + test->buffer=NULL; +} + +int Resul_Writer(CXMLWRITER *test,int value) +{ + if (value==-1) + { + Free_Writer(test); + GB.Error("Error writing XML data"); + return -1; + } + return 0; +} + +BEGIN_METHOD_VOID(CXmlWriter_Free) + + Free_Writer(THIS); + +END_METHOD + +BEGIN_METHOD (CXmlWriter_Open,GB_STRING FileName; GB_BOOLEAN Indent; GB_STRING Encoding;) + + int res; + int indent=0; + char *encoding=NULL; + + if (!MISSING(Indent)) + if (VARG(Indent)) indent=1; + + if (!MISSING(Encoding)) + encoding=GB.ToZeroString(ARG(Encoding)); + + Free_Writer (THIS); + + if (!LENGTH(FileName)) + { + THIS->buffer=xmlBufferCreate(); + THIS->writer = xmlNewTextWriterMemory(THIS->buffer, 0); + xmlTextWriterSetIndent(THIS->writer,indent); + } + else + { + THIS->writer = xmlNewTextWriterFilename(GB.ToZeroString(ARG(FileName)),0); + xmlTextWriterSetIndent(THIS->writer,indent); + } + + if (!THIS->writer) + { + GB.Error("Unable to write XML file"); + return; + } + + res=xmlTextWriterStartDocument(THIS->writer, NULL,encoding, NULL); + + if (res==-1) + { + Free_Writer (THIS); + GB.Error("Unable to write XML file"); + return; + } + + + +END_METHOD + + +BEGIN_METHOD (CXmlWriter_StartElement,GB_STRING Name; GB_OBJECT Attributes;GB_STRING Prefix;GB_STRING URI;) + + int bucle; + int nmax; + int res; + char *sname; + char *svalue; + char *prefix=NULL; + char *uri=NULL; + + if (!MISSING(Prefix)) prefix=GB.ToZeroString(ARG(Prefix)); + if (!MISSING(URI)) uri=GB.ToZeroString(ARG(URI)); + + if (Check_Writer(THIS)) return; + + if (prefix || uri) + res=xmlTextWriterStartElementNS(THIS->writer,(xmlChar *)prefix,(xmlChar *)GB.ToZeroString(ARG(Name)),(xmlChar *)uri); + else + res=xmlTextWriterStartElement(THIS->writer,(xmlChar *)GB.ToZeroString(ARG(Name))); + + if (Resul_Writer(THIS,res)) return; + + if (MISSING(Attributes)) return; + if (!VARG(Attributes)) return; + + nmax=GB.Array.Count(VARG(Attributes)); + + for (bucle=0;buclewriter,(xmlChar *)sname,(xmlChar *)svalue); + if (Resul_Writer(THIS,res)) return; + } + + +END_METHOD + +BEGIN_METHOD_VOID (CXmlWriter_EndElement) + + if (Check_Writer(THIS)) return; + + Resul_Writer(THIS,xmlTextWriterEndElement(THIS->writer)); + + +END_METHOD + +BEGIN_METHOD(CXmlWriter_Element,GB_STRING Name;GB_STRING Value;GB_STRING Prefix;GB_STRING URI;) + + xmlChar *name,*value; + int resul; + char *prefix=NULL; + char *uri=NULL; + + if (!MISSING(Prefix)) prefix=GB.ToZeroString(ARG(Prefix)); + if (!MISSING(URI)) uri=GB.ToZeroString(ARG(URI)); + + if (Check_Writer(THIS)) return; + + name=(xmlChar *)GB.ToZeroString(ARG(Name)); + if (!MISSING(Value)) + { + value=(xmlChar *)GB.ToZeroString(ARG(Value)); + + if ( prefix || uri ) + resul=xmlTextWriterWriteElementNS(THIS->writer,(xmlChar *)prefix,name,(xmlChar *)uri,value); + else + resul=xmlTextWriterWriteElement(THIS->writer,name,value); + } + else + { + if ( prefix || uri ) + resul=xmlTextWriterStartElementNS(THIS->writer,(xmlChar *)prefix,name,(xmlChar *)uri); + else + resul=xmlTextWriterStartElement(THIS->writer,name); + if (resul != -1) resul=xmlTextWriterEndElement(THIS->writer); + } + + + Resul_Writer(THIS,resul); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_Text,GB_STRING Name;) + + xmlChar *name; + + if (Check_Writer(THIS)) return; + name=(xmlChar *)GB.ToZeroString(ARG(Name)); + Resul_Writer(THIS,xmlTextWriterWriteString(THIS->writer,name)); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_Base64,GB_STRING Name;) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteBase64 (THIS->writer,STRING(Name),0,LENGTH(Name))); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_BinHex,GB_STRING Name;) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteBinHex (THIS->writer,STRING(Name),0,LENGTH(Name))); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_CDATA,GB_STRING Name;) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteCDATA(THIS->writer,(xmlChar *)GB.ToZeroString(ARG(Name)))); + +END_METHOD + + +BEGIN_METHOD(CXmlWriter_Attribute,GB_STRING Name;GB_STRING Value;GB_STRING Prefix;GB_STRING URI;) + + char *name,*value; + char *prefix=NULL; + char *uri=NULL; + int res; + + if (!MISSING(Prefix)) prefix=GB.ToZeroString(ARG(Prefix)); + if (!MISSING(URI)) uri=GB.ToZeroString(ARG(URI)); + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + value=GB.ToZeroString(ARG(Value)); + + if (prefix || uri) + res=xmlTextWriterWriteAttributeNS(THIS->writer,(xmlChar *)prefix,(xmlChar *)name,(xmlChar *)uri,(xmlChar *)value); + else + res=xmlTextWriterWriteAttribute(THIS->writer,(xmlChar *)name,(xmlChar *)value); + + Resul_Writer(THIS,res); + + +END_METHOD + +BEGIN_METHOD(CXmlWriter_WritePI,GB_STRING Target;GB_STRING Content;) + + char *target,*content; + + if (Check_Writer(THIS)) return; + target=GB.ToZeroString(ARG(Target)); + content=GB.ToZeroString(ARG(Content)); + Resul_Writer(THIS,xmlTextWriterWritePI(THIS->writer,(xmlChar *)target,(xmlChar *)content)); + + +END_METHOD + + + + +BEGIN_METHOD(CXmlWriter_Comment,GB_STRING Comment) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteComment(THIS->writer,(xmlChar *)GB.ToZeroString(ARG(Comment)))); + +END_METHOD + +BEGIN_METHOD_VOID(CXmlWriter_EndDocument) + + if (Check_Writer(THIS)) return; + + xmlTextWriterEndDocument(THIS->writer); + xmlFreeTextWriter(THIS->writer); + THIS->writer=NULL; + if (!THIS->buffer) + { + GB.ReturnVoidString(); + return; + } + GB.ReturnNewZeroString((char *)THIS->buffer->content); + xmlBufferFree(THIS->buffer); + THIS->buffer=NULL; + +END_METHOD + + +BEGIN_PROPERTY(CXMLWriter_DTD) + + RETURN_SELF(); + +END_PROPERTY + + +BEGIN_METHOD(CXmlWriter_StartDTD,GB_STRING Name;GB_STRING PubID;GB_STRING SysID;) + + char *name,*pubid=NULL,*sysid=NULL; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + if (!MISSING(PubID)) pubid=GB.ToZeroString(ARG(PubID)); + if (!MISSING(SysID)) pubid=GB.ToZeroString(ARG(SysID)); + Resul_Writer(THIS,xmlTextWriterStartDTD(THIS->writer,(xmlChar *)name,(xmlChar *)pubid,(xmlChar *)sysid)); + +END_METHOD + +BEGIN_METHOD_VOID (CXmlWriter_EndDTD) + + if (Check_Writer(THIS)) return; + + Resul_Writer(THIS,xmlTextWriterEndDTD(THIS->writer)); + +END_METHOD + +BEGIN_METHOD (CXmlWriter_DTDElement,GB_STRING Name;GB_STRING Content;) + + char *name,*content; + int resul; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + if (!MISSING(Content)) + { + content=GB.ToZeroString(ARG(Content)); + resul=xmlTextWriterWriteDTDElement (THIS->writer,(xmlChar *)name,(xmlChar *)content); + } + else + { + resul=xmlTextWriterStartDTDElement (THIS->writer,(xmlChar *)name); + if (resul != 1) resul=xmlTextWriterEndDTDElement(THIS->writer); + } + + Resul_Writer(THIS,resul); + +END_METHOD + + +BEGIN_METHOD (CXmlWriter_DTDInternalEntity,GB_STRING Name;GB_STRING Content;GB_BOOLEAN IsParameter;) + + char *name,*content; + int isparameter=0; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + content=GB.ToZeroString(ARG(Content)); + if (!MISSING(IsParameter)) isparameter=VARG(IsParameter); + Resul_Writer(THIS,xmlTextWriterWriteDTDInternalEntity(THIS->writer,isparameter,(xmlChar *)name,(xmlChar *)content)); + +END_METHOD + + +BEGIN_METHOD (CXmlWriter_DTDAttList,GB_STRING Name;GB_STRING Content;) + + char *name,*content; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + content=GB.ToZeroString(ARG(Content)); + Resul_Writer(THIS,xmlTextWriterWriteDTDAttlist(THIS->writer,(xmlChar *)name,(xmlChar *)content)); + + +END_METHOD + + + +GB_DESC CXmlWriterDTDDesc[] = +{ + // TOOD: external entity + // Namespace + GB_DECLARE(".XmlWriter.DTD", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Start",NULL,CXmlWriter_StartDTD,"(Name)s[(PubID)s(SysID)s]"), + GB_METHOD("Element",NULL,CXmlWriter_DTDElement,"(Name)s[(Content)s]"), + GB_METHOD("InternalEntity",NULL,CXmlWriter_DTDInternalEntity,"(Name)s(Content)s[(IsParameter)b]"), + GB_METHOD("AttList",NULL,CXmlWriter_DTDAttList,"(Name)s(Content)s"), + GB_METHOD("End",NULL,CXmlWriter_EndDTD,NULL), + + GB_END_DECLARE +}; + +GB_DESC CXmlWriterDesc[] = +{ + GB_DECLARE("XmlWriter", sizeof(CXMLWRITER)), + + GB_PROPERTY_READ("DTD",".XmlWriter.DTD",CXMLWriter_DTD), + + GB_METHOD("_free",NULL,CXmlWriter_Free,NULL), + + GB_METHOD("Open", NULL, CXmlWriter_Open,"(FileName)s[(Indent)b(Encoding)s]"), + + GB_METHOD("StartElement",NULL,CXmlWriter_StartElement,"(Name)s[(Attributes)String[];(Prefix)s(URI)s]"), + GB_METHOD("Attribute",NULL,CXmlWriter_Attribute,"(Name)s(Value)s[(Prefix)s(URI)s]"), + GB_METHOD("Element",NULL,CXmlWriter_Element,"(Name)s[(Value)s(Prefix)s(URI)s]"), + GB_METHOD("Comment",NULL,CXmlWriter_Comment,"(Comment)s"), + GB_METHOD("Text",NULL,CXmlWriter_Text,"(Data)s"), + GB_METHOD("Base64",NULL,CXmlWriter_Base64,"(Data)s"), + GB_METHOD("BinHex",NULL,CXmlWriter_BinHex,"(Data)s"), + GB_METHOD("CDATA",NULL,CXmlWriter_CDATA,"(Data)s"), + GB_METHOD("EndElement",NULL,CXmlWriter_EndElement,NULL), + GB_METHOD("EndDocument","s",CXmlWriter_EndDocument,NULL), + GB_METHOD("PI",NULL,CXmlWriter_WritePI,"(Target)s(Content)s"), + + GB_END_DECLARE +}; + + + + diff --git a/gb.libxml/src/CXMLWriter.h b/gb.libxml/src/CXMLWriter.h new file mode 100644 index 00000000..1d45f8e0 --- /dev/null +++ b/gb.libxml/src/CXMLWriter.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CXMLWriter.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLWRITER_H +#define __CXMLWRITER_H + +#include "gambas.h" +#include + +#ifndef __CXMLWRITER_C + +extern GB_DESC CXmlWriterDTDDesc[]; +extern GB_DESC CXmlWriterDesc[]; + +#else + +#define THIS ((CXMLWRITER *)_object) + +#endif + + +typedef struct +{ + GB_BASE ob; + xmlTextWriterPtr writer; + xmlBufferPtr buffer; + + +} CXMLWRITER; + + +#endif diff --git a/gb.libxml/src/Makefile.am b/gb.libxml/src/Makefile.am new file mode 100644 index 00000000..48dc277d --- /dev/null +++ b/gb.libxml/src/Makefile.am @@ -0,0 +1,15 @@ +COMPONENT = gb.libxml +include $(top_srcdir)/component.am + +EXTRA_DIST = *.kateproject + +gblib_LTLIBRARIES = gb.libxml.la + +gb_libxml_la_LIBADD = @XML_LIB@ +gb_libxml_la_LDFLAGS = -module @LD_FLAGS@ @XML_LDFLAGS@ +gb_libxml_la_CPPFLAGS = @XML_INC@ + +gb_libxml_la_SOURCES = \ + main.h main.c CXMLNode.h CXMLNode.c CXMLReader.h CXMLReader.c \ + CXMLWriter.h CXMLWriter.c CXMLDocument.h CXMLDocument.c + diff --git a/gb.libxml/src/gb.libxml.component b/gb.libxml/src/gb.libxml.component new file mode 100644 index 00000000..62f74101 --- /dev/null +++ b/gb.libxml/src/gb.libxml.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.xml +Author=Daniel Campos Fernández +State=Deprecated +Implements=XML + + diff --git a/gb.libxml/src/libxml.kateproject b/gb.libxml/src/libxml.kateproject new file mode 100644 index 00000000..c23d8d79 --- /dev/null +++ b/gb.libxml/src/libxml.kateproject @@ -0,0 +1,7 @@ +[Project Dir] +Dirs= +Files=CXMLReader.h/main.h/Makefile.am/CXMLNode.h/CXMLDocument.h/CXMLDocument.c/CXMLNode.c/CXMLReader.c/main.c/CXMLWriter.c/CXMLWriter.h + +[Project File] +Name=LibXML +Type=Default diff --git a/gb.libxml/src/main.c b/gb.libxml/src/main.c new file mode 100644 index 00000000..fbf773b8 --- /dev/null +++ b/gb.libxml/src/main.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + main.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include "main.h" +#include "CXMLReader.h" +#include "CXMLWriter.h" +#include "CXMLNode.h" +#include "CXMLDocument.h" + + + +//extern "C" { + + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + + CXmlReaderNodeTypeDesc, + CXmlReaderNodeAttributesDesc, + CXmlReaderNodeDesc, + CXmlReaderDesc, + + CXmlWriterDTDDesc, + CXmlWriterDesc, + + CXmlNodeAttributesDesc, + CXmlNodeChildrenDesc, + CXmlNodeDesc, + CXmlDocumentDesc, + + NULL +}; + + +int EXPORT GB_INIT(void) +{ + return -1; +} + +void EXPORT GB_EXIT() +{ + +} + +//} + + + diff --git a/gb.libxml/src/main.h b/gb.libxml/src/main.h new file mode 100644 index 00000000..dd9b6fe4 --- /dev/null +++ b/gb.libxml/src/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + + +#include "gambas.h" + + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.media/AUTHORS b/gb.media/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/COPYING b/gb.media/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.media/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.media/ChangeLog b/gb.media/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/INSTALL b/gb.media/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.media/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.media/Makefile.am b/gb.media/Makefile.am new file mode 100644 index 00000000..61a826f8 --- /dev/null +++ b/gb.media/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @MEDIA_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.media/NEWS b/gb.media/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/README b/gb.media/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/acinclude.m4 b/gb.media/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.media/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.media/component.am b/gb.media/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.media/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.media/configure.ac b/gb.media/configure.ac new file mode 100644 index 00000000..f55c50b6 --- /dev/null +++ b/gb.media/configure.ac @@ -0,0 +1,17 @@ +dnl ---- configure.ac for gb.media + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-media],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.media) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + media, MEDIA, gb.media, [src], + gstreamer-1.0 gstreamer-video-1.0 +) +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.media/gambas.h b/gb.media/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.media/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.media/gb.image.h b/gb.media/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.media/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.media/gb_common.h b/gb.media/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.media/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.media/m4 b/gb.media/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.media/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.media/reconf b/gb.media/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.media/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.media/src/Makefile.am b/gb.media/src/Makefile.am new file mode 100644 index 00000000..7ddd3708 --- /dev/null +++ b/gb.media/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.media +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.media.la + +gb_media_la_LIBADD = @THREAD_LIB@ @MEDIA_LIB@ +gb_media_la_LDFLAGS = -module @LD_FLAGS@ @MEDIA_LDFLAGS@ +gb_media_la_CPPFLAGS = @THREAD_INC@ @MEDIA_INC@ + +gb_media_la_SOURCES = \ + main.c main.h \ + c_media.c c_media.h \ + c_mediaplayer.c c_mediaplayer.h diff --git a/gb.media/src/c_media.c b/gb.media/src/c_media.c new file mode 100644 index 00000000..1f10ebc0 --- /dev/null +++ b/gb.media/src/c_media.c @@ -0,0 +1,2260 @@ +/*************************************************************************** + + c_media.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MEDIA_C + +#include "c_media.h" +//#include +#include +#include +#include + +#if GST_CHECK_VERSION(1, 16, 0) +#else +#define GST_SEEK_FLAG_TRICKMODE GST_SEEK_FLAG_SKIP +#define GST_SEEK_FLAG_TRICKMODE_KEY_UNITS (1 << 7) +#define GST_SEEK_FLAG_TRICKMODE_NO_AUDIO (1 << 8) +#endif + +static void *_from_element = NULL; + +static int cb_message(CMEDIAPIPELINE *_object); + +void MEDIA_raise_event(void *_object, int event) +{ + gst_element_post_message(ELEMENT, gst_message_new_application(GST_OBJECT(ELEMENT), gst_structure_new("SendEvent", "event", G_TYPE_INT, event, NULL))); +} + +/*void MEDIA_raise_event_arg(void *_object, int event, char *arg) +{ + gst_element_post_message(ELEMENT, gst_message_new_application(GST_OBJECT(ELEMENT), gst_structure_new("SendEvent", "event", G_TYPE_INT, event, "arg", G_TYPE_STRING, arg, NULL))); +}*/ + +static const char *get_element_type(GstElement *element) +{ + GstElementFactory *factory = gst_element_get_factory(element); + if (!factory) + return NULL; + else + return gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); +} + +CMEDIACONTROL *MEDIA_get_control_from_element(void *element, bool create) +{ + CMEDIACONTROL *ctrl; + GB_CLASS klass; + //const char *type; + + if (!element) + return NULL; + + ctrl = (CMEDIACONTROL *)g_object_get_data(G_OBJECT(element), "gambas-control"); + if (create) + { + if (!ctrl) + { + _from_element = element; + + /*type = get_element_type(element); + + if (type && strcmp(type, "v4l2src") == 0) + klass = GB.FindClass("MediaVideo"); + else*/ + if (GST_IS_PIPELINE(element)) + klass = GB.FindClass("MediaPipeline"); + else if (GST_IS_BIN(element)) + klass = GB.FindClass("MediaContainer"); + else + klass = GB.FindClass("MediaControl"); + + ctrl = (CMEDIACONTROL *)GB.New(klass, NULL, NULL); + } + } + else + { + if (ctrl && ctrl->borrow) + ctrl = NULL; + } + + return ctrl; +} + +bool MEDIA_set_state(void *_object, int state, bool error, bool async) +{ + GstStateChangeReturn status; + + status = gst_element_set_state(ELEMENT, state); + if (async) + return FALSE; + + if (status == GST_STATE_CHANGE_ASYNC) + status = gst_element_get_state(ELEMENT, NULL, NULL, GST_SECOND); + + if (status == GST_STATE_CHANGE_FAILURE) + { + if (error) GB.Error("Cannot set status"); + return TRUE; + } + + cb_message(THIS_PIPELINE); + return FALSE; +} + +bool MEDIA_get_flag(void *element, char *property, int flag) +{ + int flags; + + g_object_get(G_OBJECT(element), property, &flags, NULL); + return (flags & flag) != 0; +} + +void MEDIA_set_flag(void *element, char *property, int flag, bool value) +{ + int flags; + + g_object_get(G_OBJECT(element), property, &flags, NULL); + if (value) + flags |= flag; + else + flags &= ~flag; + g_object_set(G_OBJECT(element), property, flags, NULL); +} + + + +static GB_TYPE to_gambas_type(const GValue *value) +{ + switch (G_VALUE_TYPE(value)) + { + case G_TYPE_BOOLEAN: return GB_T_BOOLEAN; + case G_TYPE_INT: return GB_T_INTEGER; + case G_TYPE_UINT: return GB_T_INTEGER; + case G_TYPE_UINT64: return GB_T_LONG; + case G_TYPE_STRING: return GB_T_STRING; + case G_TYPE_FLOAT: return GB_T_FLOAT; + case G_TYPE_DOUBLE: return GB_T_FLOAT; + default: + if (G_VALUE_HOLDS(value, G_TYPE_DATE)) + return GB_T_DATE; + else if (GST_VALUE_HOLDS_DATE_TIME(value)) + return GB_T_DATE; + else + { + if (MAIN_debug) + fprintf(stderr, "gb.media: warning: unsupported data type: %s\n", G_VALUE_TYPE_NAME(value)); + //GB.Error("Unsupported property datatype"); + return GB_T_NULL; + } + } +} + +static void to_gambas_value(const GValue *value, GB_VALUE *gvalue) +{ + switch (G_VALUE_TYPE(value)) + { + case G_TYPE_BOOLEAN: + gvalue->type = GB_T_BOOLEAN; + gvalue->_boolean.value = g_value_get_boolean(value) == 0 ? 0 : -1; + break; + + case G_TYPE_INT: + gvalue->type = GB_T_INTEGER; + gvalue->_integer.value = g_value_get_int(value); + break; + + case G_TYPE_UINT: + gvalue->type = GB_T_INTEGER; + gvalue->_integer.value = (int)g_value_get_uint(value); + break; + + case G_TYPE_UINT64: + gvalue->type = GB_T_LONG; + gvalue->_long.value = (int64_t)g_value_get_uint64(value); + break; + + case G_TYPE_STRING: + gvalue->type = GB_T_STRING; + gvalue->_string.value.addr = GB.NewZeroString(g_value_get_string(value)); + gvalue->_string.value.start = 0; + gvalue->_string.value.len = GB.StringLength(gvalue->_string.value.addr); + break; + + case G_TYPE_FLOAT: + gvalue->type = GB_T_FLOAT; + gvalue->_float.value = (double)g_value_get_float(value); + break; + + case G_TYPE_DOUBLE: + gvalue->type = GB_T_FLOAT; + gvalue->_float.value = g_value_get_double(value); + break; + + default: + + if (G_VALUE_HOLDS(value, G_TYPE_DATE)) + { + GB_DATE_SERIAL ds; + GDate *date = (GDate *)g_value_get_boxed(value); + + CLEAR(&ds); + ds.year = date->year; + ds.month = date->month; + ds.day = date->day; + + if (ds.year && (ds.month == 0 || ds.day == 0)) + { + ds.month = 1; + ds.day = 1; + } + + GB.MakeDate(&ds, (GB_DATE *)gvalue); + break; + } + else if (GST_VALUE_HOLDS_DATE_TIME(value)) + { + GB_DATE_SERIAL ds; + GstDateTime *date = (GstDateTime *)g_value_get_boxed(value); + + CLEAR(&ds); + if (gst_date_time_has_year(date)) + ds.year = gst_date_time_get_year(date); + if (gst_date_time_has_month(date)) + ds.month = gst_date_time_get_month(date); + if (gst_date_time_has_day(date)) + ds.day = gst_date_time_get_day(date); + + if (ds.year && (ds.month == 0 || ds.day == 0)) + { + ds.month = 1; + ds.day = 1; + } + + if (gst_date_time_has_time(date)) + { + ds.hour = gst_date_time_get_hour(date); + ds.min = gst_date_time_get_minute(date); + ds.sec = gst_date_time_get_second(date); + ds.msec = gst_date_time_get_microsecond(date); + } + + GB.MakeDate(&ds, (GB_DATE *)gvalue); + break; + } + else + gvalue->type = GB_T_NULL; + } +} + +static GParamSpec *get_property(GstElement *element, const char *property) +{ + GParamSpec *desc; + + desc = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(element)), property); + if (!desc) + GB.Error("Unknown property: '&1'", property); + + return desc; +} + +static void return_value(const GValue *value) +{ + if(value == NULL) + { + GB.ReturnNull(); + return; + } + GType type = G_VALUE_TYPE(value); + + switch (type) + { + case G_TYPE_BOOLEAN: GB.ReturnBoolean(g_value_get_boolean(value)); break; + case G_TYPE_INT: GB.ReturnInteger(g_value_get_int(value)); break; + case G_TYPE_UINT: GB.ReturnInteger(g_value_get_uint(value)); break; + case G_TYPE_UINT64: GB.ReturnLong(g_value_get_uint64(value)); break; + case G_TYPE_STRING: GB.ReturnNewZeroString(g_value_get_string(value)); break; + case G_TYPE_FLOAT: GB.ReturnFloat(g_value_get_float(value)); break; + case G_TYPE_DOUBLE: GB.ReturnFloat(g_value_get_double(value)); break; + default: + if (G_VALUE_HOLDS(value, G_TYPE_DATE) || GST_VALUE_HOLDS_DATE_TIME(value)) + { + GB_VALUE date; + to_gambas_value(value, &date); + GB.ReturnDate((GB_DATE *)&date); + } + else if (G_TYPE_IS_ENUM(type)) + { + GEnumValue *enum_value; + + enum_value = g_enum_get_value(G_ENUM_CLASS(g_type_class_ref(type)), g_value_get_enum(value)); + if (!enum_value) + { + char buf[16]; + sprintf(buf, "%d", g_value_get_enum(value)); + GB.ReturnNewZeroString(buf); + } + else + GB.ReturnNewZeroString(enum_value->value_nick); + } + else if (type == GST_TYPE_CAPS) + { + char *caps; + caps = gst_caps_to_string((GstCaps *)g_value_get_boxed(value)); + GB.ReturnNewZeroString(caps); + g_free(caps); + } + else if(GST_VALUE_HOLDS_LIST(value)) + { + guint size = gst_value_list_get_size(value); + GB_ARRAY array; + + if(size <= 0) + { + GB.Array.New(&array, GB_T_VARIANT, 0); + } + else + { + GB.Array.New(&array, GB_T_VARIANT, size); + + GB_VALUE val; + int i; + for (i = 0; i < size; i++) + { + to_gambas_value(gst_value_list_get_value(value, i), &val); + GB.Store(GB_T_VARIANT, &val, GB.Array.Get(array, i)); + GB.ReleaseValue(&val); + } + } + + GB.ReturnObject(array); + } + else if(GST_VALUE_HOLDS_ARRAY(value)) + { + guint size = gst_value_array_get_size(value); + GB_ARRAY array; + + if(size <= 0) + { + GB.Array.New(&array, GB_T_VARIANT, 0); + } + else + { + GB.Array.New(&array, GB_T_VARIANT, size); + + GB_VALUE val; + int i; + for (i = 0; i < size; i++) + { + to_gambas_value(gst_value_array_get_value(value, i), &val); + GB.Store(GB_T_VARIANT, &val, GB.Array.Get(array, i)); + GB.ReleaseValue(&val); + } + } + + GB.ReturnObject(array); + } +#if defined(G_TYPE_VALUE_ARRAY) && !GLIB_CHECK_VERSION(2, 32, 0) + else if (G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY)) + { + GValueArray *garray = (GValueArray *)g_value_get_boxed(value); + guint len = garray->n_values; + GB_ARRAY array; + + GB.Array.New(&array, GB_T_VARIANT, len); + + if (len <= 0) + { + GB.Array.New(&array, GB_T_VARIANT, 0); + } + else + { + GB_VALUE val; + int i; + for (i = 0; i < len; i++) + { + to_gambas_value(g_value_array_get_nth(garray, i), &val); + GB.Store(GB_T_VARIANT, &val, GB.Array.Get(array, i)); + GB.ReleaseValue(&val); + } + } + GB.ReturnObject(array); + } +#endif + else + { + if (MAIN_debug) + fprintf(stderr, "gb.media: warning: unsupported datatype: %s\n", G_VALUE_TYPE_NAME(value)); + GB.ReturnNull(); + } + } +} + +void MEDIA_return_property(void *_object, const char *property) +{ + GParamSpec *desc; + GValue value = G_VALUE_INIT; + + desc = get_property(ELEMENT, property); + if (!desc) + return; + + //fprintf(stderr, "type = %s\n", g_type_name(desc->value_type)); + g_value_init(&value, desc->value_type); + g_object_get_property(G_OBJECT(ELEMENT), property, &value); + return_value(&value); + g_value_unset(&value); +} + + +static bool set_value(GValue *value, GB_VALUE *v, GParamSpec *desc) +{ + GType type = desc->value_type; + + g_value_init(value, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + if (GB.Conv(v, GB_T_BOOLEAN)) + return TRUE; + g_value_set_boolean(value, v->_boolean.value); + break; + + case G_TYPE_INT: + if (GB.Conv(v, GB_T_INTEGER)) + return TRUE; + g_value_set_int(value, v->_integer.value); + break; + + case G_TYPE_UINT: + if (GB.Conv(v, GB_T_INTEGER)) + return TRUE; + g_value_set_uint(value, (uint)v->_integer.value); + break; + + case G_TYPE_UINT64: + if (GB.Conv(v, GB_T_LONG)) + return TRUE; + g_value_set_uint64(value, (guint64)v->_long.value); + break; + + case G_TYPE_STRING: + if (GB.Conv(v, GB_T_STRING)) + return TRUE; + g_value_set_string(value, GB.ToZeroString((GB_STRING *)v)); + break; + + case G_TYPE_FLOAT: + if (GB.Conv(v, GB_T_FLOAT)) + return TRUE; + g_value_set_float(value, v->_float.value); + break; + + case G_TYPE_DOUBLE: + if (GB.Conv(v, GB_T_FLOAT)) + return TRUE; + g_value_set_double(value, v->_float.value); + break; + + default: + + if (G_TYPE_IS_ENUM(type)) + { + GEnumValue *enum_value; + int real_value; + + if (!GB.Conv(v, GB_T_INTEGER)) + { + real_value = ((GB_INTEGER *)v)->value; + } + else + { + if (GB.Conv(v, GB_T_STRING)) + return TRUE; + + enum_value = g_enum_get_value_by_nick(G_ENUM_CLASS(g_type_class_ref(type)), GB.ToZeroString((GB_STRING *)v)); + if (!enum_value) + { + GB.Error("Unknown enumeration value"); + return TRUE; + } + + real_value = enum_value->value; + } + + g_value_set_enum(value, real_value); + } + else if (type == GST_TYPE_CAPS) + { + GstCaps *caps; + + if (GB.Conv(v, GB_T_STRING)) + return TRUE; + + caps = gst_caps_from_string(GB.ToZeroString((GB_STRING *)v)); + if (!caps) + { + GB.Error("Incorrect filter"); + return TRUE; + } + + g_value_take_boxed(value, caps); + } + else + { + GB.Error("Unsupported datatype: &1", g_type_name(type)); + return TRUE; + } + } + + return FALSE; +} + +void MEDIA_set_property(void *_object, const char *property, GB_VALUE *v) +{ + GParamSpec *desc; + GValue value = G_VALUE_INIT; + + desc = get_property(ELEMENT, property); + if (!desc) + return; + + if (set_value(&value, v, desc)) + return; + + g_object_set_property(G_OBJECT(ELEMENT), property, &value); + g_value_unset(&value); +} + +GB_IMG *MEDIA_get_image_from_sample(GstSample *sample, bool convert) +{ + GstSample *temp; + GError *err = NULL; + GstStructure *s; + GstCaps *to_caps, *sample_caps; + gint outwidth = 0; + gint outheight = 0; + GstMemory *memory; + GstMapInfo info; + const char *format; + int gb_format; + GB_IMG *img; + + switch (IMAGE.GetDefaultFormat()) + { + case GB_IMAGE_BGRA: + case GB_IMAGE_BGRP: + format = "BGR"; + gb_format = GB_IMAGE_BGR; + break; + + case GB_IMAGE_RGBA: + case GB_IMAGE_RGBP: + format = "RGB"; + gb_format = GB_IMAGE_RGB; + break; + + default: + GB.Error("Unsupported default image format"); + return NULL; + } + + if (convert) + { + /* our desired output format (RGB or BGR) */ + to_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, format, + /* Note: we don't ask for a specific width/height here, so that + * videoscale can adjust dimensions from a non-1/1 pixel aspect + * ratio to a 1/1 pixel-aspect-ratio. We also don't ask for a + * specific framerate, because the input framerate won't + * necessarily match the output framerate if there's a deinterlacer + * in the pipeline. */ + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); + + temp = gst_video_convert_sample(sample, to_caps, 25 * GST_SECOND, &err); + + if (temp == NULL && err) + { + GB.Error(err->message); + gst_caps_unref(to_caps); + gst_sample_unref(sample); + g_error_free(err); + return NULL; + } + + gst_sample_unref(sample); + gst_caps_unref(to_caps); + + sample = temp; + } + + if (!sample) + { + GB.Error("Unable to retrieve or convert video frame"); + return NULL; + } + + sample_caps = gst_sample_get_caps(sample); + if (!sample_caps) + { + GB.Error("No caps on video frame"); + gst_sample_unref(sample); + return NULL; + } + + //fprintf(stderr, "frame caps: %" GST_PTR_FORMAT, sample_caps); + + s = gst_caps_get_structure (sample_caps, 0); + gst_structure_get_int (s, "width", &outwidth); + gst_structure_get_int (s, "height", &outheight); + if (outwidth <= 0 || outheight <= 0) + { + GB.Error("Bad image dimensions"); + gst_sample_unref(sample); + return NULL; + } + + memory = gst_buffer_get_memory (gst_sample_get_buffer (sample), 0); + gst_memory_map(memory, &info, GST_MAP_READ); + + /* create pixbuf from that - use our own destroy function */ + /*pixbuf = gdk_pixbuf_new_from_data (info.data, + GDK_COLORSPACE_RGB, FALSE, 8, outwidth, outheight, + GST_ROUND_UP_4 (outwidth * 3), destroy_pixbuf, sample);*/ + + img = IMAGE.Create(outwidth, outheight, gb_format, info.data); + + gst_memory_unmap(memory, &info); + + gst_sample_unref(sample); + return img; +} + +static GB_IMG *get_last_image(void *_object) +{ + GstElement *elt = GST_ELEMENT(ELEMENT); + GstSample *sample; + + if (!GST_IS_BASE_SINK(elt)) + { + GB.Error("Not supported on this control"); + return NULL; + } + + sample = gst_base_sink_get_last_sample(GST_BASE_SINK(elt)); + if (sample == NULL) + return NULL; + + return MEDIA_get_image_from_sample(sample, TRUE); +} + +static GstIteratorResult iterator_next_pad(GstIterator *iter, GstPad **pad) +{ + GstIteratorResult ret; + GValue value = G_VALUE_INIT; + + ret = gst_iterator_next(iter, &value); + if (ret == GST_ITERATOR_OK) + { + if (G_VALUE_HOLDS_BOXED(&value)) + *pad = g_value_get_boxed(&value); + else + *pad = (GstPad *)g_value_get_object(&value); + } + + return ret; +} + +static GstElement *find_sink(GstElement *pipeline) +{ + int i; + GstIterator *iter; + GstElement *element; + GstPad *pad; + bool done; + bool got_pad; + + for (i = 0; i < gst_child_proxy_get_children_count(GST_CHILD_PROXY(pipeline)); i++) + { + element = (GstElement *)gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(pipeline), i); + + iter = gst_element_iterate_src_pads(element); + + done = FALSE; + got_pad = FALSE; + while (!done) + { + switch (iterator_next_pad(iter, &pad)) + { + case GST_ITERATOR_OK: + gst_object_unref(pad); + got_pad = TRUE; + done = TRUE; + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync(iter); + break; + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + + gst_iterator_free(iter); + + if (!got_pad) + return element; + + gst_object_unref(element); + } + + GB.Error("Unable to find sink"); + return NULL; +} + +static void set_pipeline_rate(void *_object) +{ + gint64 pos; + GstElement *sink; + double rate; + + if (THIS->state != GST_STATE_PLAYING && THIS->state != GST_STATE_PAUSED) + return; + + rate = THIS_PIPELINE->next_rate; + if (rate == THIS_PIPELINE->rate) + return; + + sink = find_sink(ELEMENT); + if (!sink) + return; + + gst_element_query_position(ELEMENT, GST_FORMAT_TIME, &pos); + + if (rate > 0) + gst_element_seek(sink, rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_END, 0); + else + gst_element_seek(sink, rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos); + + gst_object_unref(sink); + + THIS_PIPELINE->rate = THIS_PIPELINE->next_rate; +} + +#if 0 +//---- MediaSignalArguments ----------------------------------------------- + +static int check_signal_arguments(void *_object) +{ + return THIS_ARG->param_values == NULL; +} + +BEGIN_METHOD(MediaSignalArguments_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= THIS_ARG->n_param_values) + { + GB.Error(GB_ERR_BOUND); + return; + } + + return_value(&THIS_ARG->param_values[index]); + +END_METHOD +#endif + +//---- MediaTagList ------------------------------------------------------- + +static int MediaTagList_check(void *_object) +{ + return THIS_TAGLIST->tags == NULL; +} + +static CMEDIATAGLIST *create_tag_list(GstTagList *tags) +{ + CMEDIATAGLIST *ob; + + ob = GB.New(GB.FindClass("MediaTagList"), NULL, NULL); + ob->tags = tags; + return ob; +} + +BEGIN_METHOD(MediaTagList_get, GB_STRING name) + + char *name = GB.ToZeroString(ARG(name)); + GstTagList *tags = THIS_TAGLIST->tags; + const GValue *value; + int nvalue; + + nvalue = gst_tag_list_get_tag_size(tags, name); + + if (nvalue <= 0) + { + GB.ReturnNull(); + } + else if (nvalue == 1) + { + value = gst_tag_list_get_value_index(tags, name, 0); + return_value(value); + } + else + { + GB_ARRAY array; + GB_TYPE type; + GB_VALUE gvalue; + int i; + + value = gst_tag_list_get_value_index(tags, name, 0); + type = to_gambas_type(value); + if (type == GB_T_NULL) + GB.ReturnNull(); + else + { + GB.Array.New(&array, type, nvalue); + for (i = 0; i < nvalue; i++) + { + value = gst_tag_list_get_value_index(tags, name, i); + to_gambas_value(value, &gvalue); + GB.Store(type, &gvalue, GB.Array.Get(array, i)); + GB.ReleaseValue(&gvalue); + } + + GB.ReturnObject(array); + } + } + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_PROPERTY(MediaTagList_Tags) + + GB_ARRAY array; + GstTagList *tags = THIS_TAGLIST->tags; + int ntags, i; + + ntags = gst_tag_list_n_tags(tags); + + GB.Array.New(&array, GB_T_STRING, ntags); + + for (i = 0; i < ntags; i++) + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(gst_tag_list_nth_tag_name(tags, i)); + + GB.ReturnObject(array); + +END_PROPERTY + +//---- MediaMessage ------------------------------------------------------- + +#define MESSAGE_DATA (gst_message_get_structure(THIS_MESSAGE->message)) + +static int MediaMessage_check(void *_object) +{ + return THIS_MESSAGE->message == NULL; +} + +static CMEDIAMESSAGE *create_message(GstMessage *message) +{ + CMEDIAMESSAGE *ob; + + ob = GB.New(GB.FindClass("MediaMessage"), NULL, NULL); + ob->message = message; + ob->lastKey = NULL; + return ob; +} + +BEGIN_METHOD(MediaMessage_get, GB_STRING name) + + char *name = GB.ToZeroString(ARG(name)); + const GValue *value; + + value = gst_structure_get_value(MESSAGE_DATA, name); + return_value(value); + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_PROPERTY(MediaMessage_Keys) + + GB_ARRAY array; + const GstStructure *data = MESSAGE_DATA; + int nfields, i; + + nfields = gst_structure_n_fields(data); + + GB.Array.New(&array, GB_T_STRING, nfields); + + for (i = 0; i < nfields; i++) + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(gst_structure_nth_field_name(data, i)); + + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Type) + + GB.ReturnInteger(GST_MESSAGE_TYPE(THIS_MESSAGE->message)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Name) + + GB.ReturnNewZeroString(gst_structure_get_name(MESSAGE_DATA)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Count) + + GB.ReturnInteger(gst_structure_n_fields(MESSAGE_DATA)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Key) + + GB.ReturnNewZeroString(THIS_MESSAGE->lastKey); + +END_PROPERTY + +BEGIN_METHOD_VOID(MediaMessage_next) + + const GstStructure *data = MESSAGE_DATA; + int count = gst_structure_n_fields(data); + int *index = (int *)GB.GetEnum(); + + if (*index < 0 || *index >= count) + GB.StopEnum(); + else + { + THIS_MESSAGE->lastKey = gst_structure_nth_field_name(data, *index); + const GValue *value = gst_structure_get_value(data, THIS_MESSAGE->lastKey); + return_value(value); + + GB.ReturnConvVariant(); + (*index)++; + } + +END_PROPERTY + +//---- MediaLink ---------------------------------------------------------- + +static CMEDIALINK *create_link(GstPad *pad) +{ + CMEDIALINK *ob; + + ob = GB.New(GB.FindClass("MediaLink"), NULL, NULL); + ob->pad = pad; + return ob; +} + +BEGIN_METHOD_VOID(MediaLink_free) + + gst_object_unref(LINK); + +END_METHOD + +BEGIN_PROPERTY(MediaLink_Name) + + char *name = gst_pad_get_name(LINK); + GB.ReturnNewZeroString(name); + g_free(name); + +END_PROPERTY + +BEGIN_PROPERTY(MediaLink_Peer) + + GstPad *peer = gst_pad_get_peer(LINK); + + if (!peer) + { + GB.ReturnNull(); + return; + } + + GB.ReturnObject(MEDIA_get_control_from_element(gst_pad_get_parent_element(peer), TRUE)); + gst_object_unref(peer); + +END_PROPERTY + +static void return_peer_name(void *_object, GstPadDirection dir) +{ + if (gst_pad_get_direction(LINK) == dir) + { + GstPad *peer = gst_pad_get_peer(LINK); + if (peer) + { + char *name = gst_pad_get_name(peer); + GB.ReturnNewZeroString(name); + g_free(name); + gst_object_unref(peer); + return; + } + } + + GB.ReturnVoidString(); +} + +BEGIN_PROPERTY(MediaLink_Input) + + return_peer_name(THIS_LINK, GST_PAD_SRC); + +END_PROPERTY + +BEGIN_PROPERTY(MediaLink_Output) + + return_peer_name(THIS_LINK, GST_PAD_SINK); + +END_PROPERTY + +//---- MediaControl ------------------------------------------------------- + +DECLARE_EVENT(EVENT_State); +//DECLARE_EVENT(EVENT_Signal); + +typedef + struct { + char *klass; + char *type; + } + MEDIA_TYPE; + +static MEDIA_TYPE _types[] = +{ + { "MediaContainer", "bin" }, + { "MediaPipeline", "pipeline" }, + { "Media", "pipeline" }, + { "MediaPlayer", "playbin" }, + { "MediaFilter", "capsfilter" }, + { "MediaVideo", "v4l2src" }, + //{ "MediaDecoder", "decodebin" }, + { NULL, NULL } +}; + +static void cb_pad_added(GstElement *element, GstPad *pad, CMEDIACONTROL *_object) +{ + char *name; + + if (!THIS->dest) + return; + + name = gst_pad_get_name(pad); + gst_element_link_pads(ELEMENT, name, ((CMEDIACONTROL *)THIS->dest)->elt, NULL); + g_free(name); +} + +BEGIN_METHOD(MediaControl_new, GB_OBJECT parent; GB_STRING type) + + char *type; + CMEDIACONTAINER *parent; + MEDIA_TYPE *mtp; + GB_CLASS klass; + char *filter = NULL; + + //fprintf(stderr, "MediaControl_new: %p\n", THIS); + + THIS->tag.type = GB_T_NULL; + + if (_from_element) + { + //GstState state = GST_STATE_NULL; + + THIS->borrow = TRUE; + THIS->elt = _from_element; + _from_element = NULL; + + gst_object_ref(GST_OBJECT(ELEMENT)); + + g_object_set_data(G_OBJECT(ELEMENT), "gambas-control", THIS); + + //gst_element_get_state(ELEMENT, &state, NULL, GST_SECOND); + THIS->state = GST_STATE_NULL; + + //THIS->type = GB.NewZeroString(gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(gst_element_get_factory(ELEMENT)))); + } + else + { + if (MISSING(type)) + { + klass = GB.GetClass(THIS); + type = NULL; + + for (mtp = _types; mtp->klass; mtp++) + { + if (GB.FindClass(mtp->klass) == klass) + { + type = mtp->type; + break; + } + } + + if (!type) + { + GB.Error("The type must be specified"); + return; + } + } + else + { + type = GB.ToZeroString(ARG(type)); + if (strchr(type, '/')) + { + filter = type; + type = "capsfilter"; + } + } + + THIS->state = GST_STATE_NULL; + //THIS->type = GB.NewZeroString(type); + + + ELEMENT = gst_element_factory_make(type, NULL); + if (!ELEMENT) + { + GB.Error("Unable to create media control"); + return; + } + + gst_object_ref(GST_OBJECT(ELEMENT)); + g_object_set_data(G_OBJECT(ELEMENT), "gambas-control", THIS); + + parent = VARGOPT(parent, NULL); + if (parent) + { + gst_bin_add(GST_BIN(parent->elt), ELEMENT); + gst_element_sync_state_with_parent(ELEMENT); + } + else if (!GST_IS_PIPELINE(ELEMENT)) + GB.CheckObject(parent); + + if (filter) + MEDIA_set_property(THIS, "caps", (GB_VALUE *)ARG(type)); + } + +END_METHOD + +BEGIN_METHOD_VOID(MediaControl_free) + + //fprintf(stderr, "MediaControl_free: %p\n", THIS); + + GB.Unref(POINTER(&THIS->dest)); + //GB.FreeString(&THIS->type); + GB.StoreVariant(NULL, &THIS->tag); + + if (ELEMENT) + { + if (!THIS->borrow) + gst_element_set_state(ELEMENT, GST_STATE_NULL); + + g_object_set_data(G_OBJECT(ELEMENT), "gambas-control", NULL); + gst_object_unref(GST_OBJECT(ELEMENT)); + } + +END_METHOD + +BEGIN_PROPERTY(MediaControl_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&THIS->tag)); + +END_METHOD + +BEGIN_PROPERTY(MediaControl_Type) + + const char *type = get_element_type(ELEMENT); + + if (!type) + GB.ReturnConstZeroString("?"); + else + GB.ReturnNewZeroString(type); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_Parent) + + GstElement *parent = GST_ELEMENT(gst_element_get_parent(ELEMENT)); + GB.ReturnObject(MEDIA_get_control_from_element(parent, TRUE)); + if (parent) + gst_object_unref(parent); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(gst_element_get_name(ELEMENT)); + else + gst_element_set_name(ELEMENT, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_State) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->state); + /*GstState state; + + status = gst_element_get_state(ELEMENT, &state, NULL, GST_SECOND); + + if (status != GST_STATE_CHANGE_SUCCESS) + GB.ReturnInteger(-1); + else + GB.ReturnInteger(state);*/ + } + else + { + MEDIA_set_state(THIS, VPROP(GB_INTEGER), TRUE, FALSE); + } + +END_PROPERTY + +BEGIN_METHOD(MediaControl_get, GB_STRING property) + + char *property = GB.ToZeroString(ARG(property)); + GParamSpec *desc; + GValue value = G_VALUE_INIT; + + desc = get_property(ELEMENT, property); + if (!desc) + return; + + //fprintf(stderr, "type = %s\n", g_type_name(desc->value_type)); + g_value_init(&value, desc->value_type); + g_object_get_property(G_OBJECT(ELEMENT), property, &value); + return_value(&value); + g_value_unset(&value); + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_METHOD(MediaControl_put, GB_VARIANT value; GB_STRING property) + + char *property = GB.ToZeroString(ARG(property)); + GB_VALUE *v = (GB_VALUE *)ARG(value); + + MEDIA_set_property(THIS, property, v); + +END_METHOD + +BEGIN_METHOD(MediaControl_LinkTo, GB_OBJECT dest; GB_STRING output; GB_STRING input) + + CMEDIACONTROL *dest = (CMEDIACONTROL *)VARG(dest); + char *output; + char *input; + + if (GB.CheckObject(dest)) + return; + + output = MISSING(output) ? NULL : GB.ToZeroString(ARG(output)); + if (output && !*output) output = NULL; + input = MISSING(input) ? NULL : GB.ToZeroString(ARG(input)); + if (input && !*input) input = NULL; + + if (output) + { + GstPad *pad = gst_element_get_static_pad (ELEMENT, output); + if (pad) + { + if (GST_PAD_IS_SRC(pad)) + { + GstPad *peer = gst_pad_get_peer(pad); + gst_pad_unlink(pad, peer); + gst_object_unref(peer); + } + gst_object_unref(pad); + } + } + + if (!gst_element_link_pads(ELEMENT, output, dest->elt, input)) + GB.Error("Unable to link controls"); + +END_METHOD + +BEGIN_METHOD(MediaControl_LinkLaterTo, GB_OBJECT dest) + + CMEDIACONTROL *dest = (CMEDIACONTROL *)VARG(dest); + + if (GB.CheckObject(dest)) + return; + + GB.Unref(POINTER(&THIS->dest)); + GB.Ref(dest); + THIS->dest = dest; + g_signal_connect(ELEMENT, "pad-added", G_CALLBACK(cb_pad_added), THIS); + +END_METHOD + +static void fill_pad_list(GB_ARRAY array, GstIterator *iter) +{ + bool done = FALSE; + GstPad *pad; + char *name; + + while (!done) + { + switch (iterator_next_pad(iter, &pad)) + { + case GST_ITERATOR_OK: + name = gst_pad_get_name(pad); + *((char **)GB.Array.Add(array)) = GB.NewZeroString(name); + g_free(name); + gst_object_unref(pad); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync(iter); + break; + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + + gst_iterator_free(iter); +} + +BEGIN_PROPERTY(MediaControl_Inputs) + + GstIterator *iter; + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + iter = gst_element_iterate_sink_pads(ELEMENT); + fill_pad_list(array, iter); + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_Outputs) + + GstIterator *iter; + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + iter = gst_element_iterate_src_pads(ELEMENT); + fill_pad_list(array, iter); + GB.ReturnObject(array); + +END_PROPERTY + +#if 0 +static void closure_marshal(GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GObject *src; + CMEDIACONTROL *_object; + CMEDIASIGNALARGUMENTS *arg; + + src = g_value_peek_pointer (param_values + 0); + _object = get_control_from_element(src); + + arg = GB.New(GB.FindClass("MediaSignalArguments"), NULL, NULL); + arg->n_param_values = n_param_values; + arg->param_values = param_values; + + GB.Ref(arg); + GB.Raise(THIS, EVENT_Signal, 1, GB_T_OBJECT, arg); + MEDIA_raise_event_arg(THIS, EVENT_Signal, ); + + arg->n_param_values = 0; + arg->param_values = NULL; + GB.Unref(POINTER(&arg)); +} + +static GClosure *get_closure() +{ + static GClosure *closure = NULL; + + if (!closure) + { + closure = g_closure_new_simple(sizeof(GClosure), NULL); + g_closure_set_marshal(closure, closure_marshal); + } + + return closure; +} + +BEGIN_METHOD(MediaControl_Activate, GB_STRING signal) + + char *signal = GB.ToZeroString(ARG(signal)); + GClosure *closure = get_closure(); + + if (g_signal_handler_find(ELEMENT, G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_ID, g_signal_lookup(signal, G_OBJECT_TYPE(ELEMENT)), (GQuark)0, closure, NULL, NULL)) + { + GB.Error("Signal is already activated"); + return; + } + + g_signal_connect_closure(ELEMENT, GB.ToZeroString(ARG(signal)), closure, FALSE); + +END_METHOD +#endif + +BEGIN_METHOD(MediaControl_SetWindow, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + void *control = VARG(control); + long wid; + int x, y, w, h; + + if (!GST_IS_VIDEO_OVERLAY(ELEMENT)) + { + GB.Error("Not supported on this control"); + return; + } + + if (control && GB.CheckObject(control)) + return; + + if (control) + { + wid = MAIN_get_x11_handle(control); + if (wid == 0) + return; + } + else + wid = 0; + + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(ELEMENT), (guintptr)wid); + + if (wid && !MISSING(x) && !MISSING(y) && !MISSING(w) && !MISSING(h)) + { + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (w > 0 && h > 0) + gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(ELEMENT), x, y, w, h); + } + gst_video_overlay_expose(GST_VIDEO_OVERLAY(ELEMENT)); + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(MediaControl_Protocols) + + char **protocols; + GB_ARRAY array; + + if (!GST_IS_URI_HANDLER(ELEMENT)) + { + GB.ReturnNull(); + return; + } + + protocols = (char **)gst_uri_handler_get_protocols(GST_URI_HANDLER(ELEMENT)); + GB.Array.New(&array, GB_T_STRING, 0); + while (*protocols) + { + *((char **)GB.Array.Add(array)) = GB.NewZeroString(*protocols); + protocols++; + } + GB.ReturnObject(array); + +END_PROPERTY +#endif + +BEGIN_METHOD(MediaControl_GetLink, GB_STRING name) + + bool done = FALSE; + GstIterator *iter; + GstPad *pad; + char *name; + char *search; + CMEDIALINK *link = NULL; + + search = GB.ToZeroString(ARG(name)); + iter = gst_element_iterate_pads(ELEMENT); + + while (!done) + { + switch (iterator_next_pad(iter, &pad)) + { + case GST_ITERATOR_OK: + name = gst_pad_get_name(pad); + if (strcmp(name, search) == 0) + { + link = create_link(pad); + done = TRUE; + break; + } + g_free(name); + gst_object_unref(pad); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync(iter); + break; + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + + gst_iterator_free(iter); + GB.ReturnObject(link); + +END_METHOD + +BEGIN_METHOD_VOID(MediaControl_GetLastImage) + + GB.ReturnObject(get_last_image(THIS)); + +END_PROPERTY + + +//---- MediaFilter -------------------------------------------------------- + +BEGIN_PROPERTY(MediaFilter_Filter) + + if (READ_PROPERTY) + MEDIA_return_property(THIS, "caps"); + else + MEDIA_set_property(THIS, "caps", PROP(GB_VALUE)); + +END_PROPERTY + +//---- MediaContainerChildren --------------------------------------------- + +BEGIN_PROPERTY(MediaContainerChildren_Count) + + GB.ReturnInteger(gst_child_proxy_get_children_count(GST_CHILD_PROXY(ELEMENT))); + +END_PROPERTY + +BEGIN_METHOD(MediaContainerChildren_get, GB_INTEGER index) + + int count = gst_child_proxy_get_children_count(GST_CHILD_PROXY(ELEMENT)); + int index = VARG(index); + + if (index < 0 || index >= count) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(MEDIA_get_control_from_element(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(ELEMENT), index), TRUE)); + +END_PROPERTY + +BEGIN_METHOD_VOID(MediaContainerChildren_next) + + int count = gst_child_proxy_get_children_count(GST_CHILD_PROXY(ELEMENT)); + int *index = (int *)GB.GetEnum(); + + if (*index < 0 || *index >= count) + GB.StopEnum(); + else + { + GB.ReturnObject(MEDIA_get_control_from_element(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(ELEMENT), *index), TRUE)); + (*index)++; + } + +END_PROPERTY + +//---- MediaContainer ----------------------------------------------------- + +static bool add_input_output(void *_object, CMEDIACONTROL *child, char *name, int direction, const char *dir_error, const char *unknown_error) +{ + GstPad *pad; + GstIterator *iter; + GstIteratorResult res; + + if (GB.CheckObject(child)) + return TRUE; + + if (!name) + { + if (direction == GST_PAD_SINK) + iter = gst_element_iterate_sink_pads(child->elt); + else + iter = gst_element_iterate_src_pads(child->elt); + + for(;;) + { + res = iterator_next_pad(iter, &pad); + if (res == GST_ITERATOR_RESYNC) + gst_iterator_resync(iter); + else + break; + } + + gst_iterator_free(iter); + + if (res != GST_ITERATOR_OK) + { + GB.Error(unknown_error); + return TRUE; + } + } + else + { + pad = gst_element_get_static_pad(child->elt, name); + if (!pad) + { + GB.Error(unknown_error); + return TRUE; + } + + if (gst_pad_get_direction(pad) != direction) + { + gst_object_unref (GST_OBJECT(pad)); + GB.Error(dir_error); + return TRUE; + } + } + + gst_element_add_pad(ELEMENT, gst_ghost_pad_new(name, pad)); + gst_object_unref(GST_OBJECT(pad)); + + return FALSE; +} + +// Just there for the documentation wiki! + +BEGIN_METHOD_VOID(MediaContainer_new) + +END_METHOD + +BEGIN_METHOD(MediaContainer_AddInput, GB_OBJECT child; GB_STRING name) + + add_input_output(THIS, (CMEDIACONTROL *)VARG(child), MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), GST_PAD_SINK, "Not an input", "Unknown input"); + +END_METHOD + +BEGIN_METHOD(MediaContainer_AddOutput, GB_OBJECT child; GB_STRING name) + + add_input_output(THIS, (CMEDIACONTROL *)VARG(child), MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), GST_PAD_SRC, "Not an output", "Unknown output"); + +END_METHOD + +//---- MediaPipeline ------------------------------------------------------ + +DECLARE_EVENT(EVENT_Start); +DECLARE_EVENT(EVENT_End); +DECLARE_EVENT(EVENT_Message); +DECLARE_EVENT(EVENT_Tag); +DECLARE_EVENT(EVENT_Event); +DECLARE_EVENT(EVENT_Buffering); +DECLARE_EVENT(EVENT_Duration); +DECLARE_EVENT(EVENT_Position); +DECLARE_EVENT(EVENT_Progress); +DECLARE_EVENT(EVENT_AboutToFinish); + +static int cb_message(CMEDIAPIPELINE *_object) +{ + GstMessage *msg; + GstMessageType type; + int msg_type; + GstBus *bus; + CMEDIACONTROL *control; + + if (THIS_PIPELINE->in_message) + return FALSE; + + THIS_PIPELINE->in_message = TRUE; + + bus = gst_pipeline_get_bus(PIPELINE); + + for(;;) + { + msg = gst_bus_pop(bus); + if (msg == NULL) + break; + + type = GST_MESSAGE_TYPE(msg); + control = MEDIA_get_control_from_element(GST_MESSAGE_SRC(msg), FALSE); + + if (type == GST_MESSAGE_APPLICATION && control) + { + //CMEDIACONTROL *target = (CMEDIACONTROL *)g_value_get_pointer(gst_structure_get_value(gst_message_get_structure(msg), "control")); + int event = g_value_get_int(gst_structure_get_value(gst_message_get_structure(msg), "event")); + GB.Raise(control, event, 0); + } + else if (type == GST_MESSAGE_STATE_CHANGED && control) + { + GstState old_state, new_state; + + gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); + control->state = new_state; + if (new_state == GST_STATE_NULL) + control->error = FALSE; + GB.Raise(control, EVENT_State, 0); + } + else //if (GST_MESSAGE_SRC(msg) == GST_OBJECT(PIPELINE)) + { + if (GB.CanRaise(THIS, EVENT_Event)) + { + CMEDIAMESSAGE *ob = create_message(msg); + GB.Ref(ob); + + GB.Raise(THIS, EVENT_Event, 1, GB_T_OBJECT, ob); + + ob->message = NULL; + GB.Unref(POINTER(&ob)); + } + + switch (type) + { + case GST_MESSAGE_EOS: + THIS->eos = TRUE; + GB.Raise(THIS, EVENT_End, 0); + break; + + case GST_MESSAGE_ERROR: + case GST_MESSAGE_WARNING: + case GST_MESSAGE_INFO: + { + gchar *debug; + GError *error; + + if (type == GST_MESSAGE_ERROR) + { + gst_message_parse_error(msg, &error, &debug); + msg_type = 2; + if (control) + control->error = TRUE; + THIS->error = TRUE; + } + else if (type == GST_MESSAGE_WARNING) + { + gst_message_parse_warning(msg, &error, &debug); + msg_type = 1; + } + else + { + gst_message_parse_info(msg, &error, &debug); + msg_type = 0; + } + + g_free(debug); + + if (!control) + control = THIS; + + GB.Ref(control); + GB.Raise(THIS, EVENT_Message, 3, GB_T_OBJECT, control, GB_T_INTEGER, msg_type, GB_T_STRING, error->message, -1); + g_error_free(error); + GB.Unref(POINTER(&control)); + break; + } + + case GST_MESSAGE_TAG: + { + GstTagList *tags = NULL; + CMEDIATAGLIST *ob; + //char *list; + + gst_message_parse_tag(msg, &tags); + + //list = gst_tag_list_to_string(tags); + //fprintf(stderr, "--> %s\n", list); + //g_free(list); + + ob = create_tag_list(tags); + GB.Ref(ob); + + GB.Raise(THIS, EVENT_Tag, 1, GB_T_OBJECT, ob); + ob->tags = NULL; + GB.Unref(POINTER(&ob)); + + gst_tag_list_free(tags); + + break; + } + case GST_MESSAGE_BUFFERING: GB.Raise(THIS, EVENT_Buffering, 0); break; + case GST_MESSAGE_DURATION_CHANGED: GB.Raise(THIS, EVENT_Duration, 0); break; + case GST_MESSAGE_PROGRESS: GB.Raise(THIS, EVENT_Progress, 0); break; + + case GST_MESSAGE_STREAM_START: + THIS_PIPELINE->about_to_finish = FALSE; + THIS_PIPELINE->pos = 0; + gst_element_query_duration(ELEMENT, GST_FORMAT_TIME, &THIS_PIPELINE->duration); + GB.Raise(THIS, EVENT_Start, 0); + break; + + default: break; + } + } + + gst_message_unref(msg); + } + + gst_object_unref(bus); + + THIS_PIPELINE->in_message = FALSE; + + if ((THIS->state == GST_STATE_PLAYING || THIS->state == GST_STATE_PAUSED) && !THIS->error) + { + gint64 pos; + gst_element_query_position(ELEMENT, GST_FORMAT_TIME, &pos); + if (pos >= 0 && pos != THIS_PIPELINE->pos) + { + THIS_PIPELINE->pos = pos; + GB.Raise(THIS, EVENT_Position, 0); + //fprintf(stderr, "%" PRId64 " / %" PRId64 "\n", THIS_PIPELINE->pos, THIS_PIPELINE->duration); + if (!THIS_PIPELINE->about_to_finish && THIS_PIPELINE->pos > (THIS_PIPELINE->duration - 2000000000)) + { + THIS_PIPELINE->about_to_finish = TRUE; + GB.Raise(THIS, EVENT_AboutToFinish, 0); + } + } + } + + return FALSE; +} + +void MEDIA_stop_pipeline(CMEDIACONTROL *_object) +{ + int try; + + // "It is not allowed to post GST_MESSAGE_EOS when not in the PLAYING state." says the documentation + if ((THIS->state == GST_STATE_PLAYING) && !THIS->eos) + { + try = 0; + gst_element_send_event(ELEMENT, gst_event_new_eos()); + while (!THIS->eos) + { + try++; + if (try > 25) + { + if (MAIN_debug) + fprintf(stderr, "gb.media: warning: could not catch end of stream\n"); + break; + } + cb_message(THIS_PIPELINE); + usleep(10000); + } + } + + MEDIA_set_state(THIS, GST_STATE_READY, TRUE, FALSE); +} + + +BEGIN_METHOD(MediaPipeline_new, GB_INTEGER polling) + + if (!_from_element) + { + int polling = VARGOPT(polling, 250); + + if (polling <= 0) + polling = 250; + else if (polling < 10) + polling = 10; + else if (polling >= 1000) + polling = 1000; + + THIS_PIPELINE->polling = polling; + THIS_PIPELINE->watch = GB.Every(polling, (GB_TIMER_CALLBACK)cb_message, (intptr_t)THIS); + } + + THIS_PIPELINE->rate = THIS_PIPELINE->next_rate = 1.0; + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_free) + + //fprintf(stderr, "MediaPipeline_free\n"); + MEDIA_stop_pipeline(THIS); + if (THIS_PIPELINE->watch) + GB.Unref(POINTER(&THIS_PIPELINE->watch)); + +END_METHOD + +BEGIN_METHOD(MediaPipeline_Play, GB_BOOLEAN async) + + THIS->eos = FALSE; + MEDIA_set_state(THIS, GST_STATE_PLAYING, TRUE, VARGOPT(async, FALSE)); + set_pipeline_rate(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_Stop) + + MEDIA_stop_pipeline(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_Close) + + MEDIA_set_state(THIS, GST_STATE_NULL, TRUE, FALSE); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_Pause) + + if (THIS->state != GST_STATE_PLAYING) + return; + + MEDIA_set_state(THIS, GST_STATE_PAUSED, TRUE, FALSE); + +END_METHOD + +static void pipeline_seek(void *_object, gint64 pos, GstSeekFlags flags) +{ + if (pos < 0) + pos = 0; + + gst_element_seek(ELEMENT, THIS_PIPELINE->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | flags, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_NONE, 0); + cb_message(THIS_PIPELINE); +} + +BEGIN_METHOD(MediaPipeline_Seek, GB_FLOAT position; GB_INTEGER flag) + + gint64 pos = VARG(position) * 1E9; + GstSeekFlags flags = (GstSeekFlags)VARGOPT(flag, GST_SEEK_FLAG_NONE); + + pipeline_seek(THIS, pos, flags); + +END_METHOD + +BEGIN_PROPERTY(MediaPipeline_Position) + + if (READ_PROPERTY) + { + GB.ReturnFloat((double)(THIS_PIPELINE->pos / 1000) / 1E6); + } + else + { + gint64 pos = VPROP(GB_FLOAT) * 1E9; + pipeline_seek(THIS, pos, GST_SEEK_FLAG_ACCURATE); + } + +END_PROPERTY + +BEGIN_PROPERTY(MediaPipeline_Duration) + + GB.ReturnFloat((double)(THIS_PIPELINE->duration / 1000) / 1E6); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPipeline_Speed) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS_PIPELINE->rate); + else + { + double rate = VPROP(GB_FLOAT); + if (fabs(rate) <= 1E-6) + { + GB.Error(GB_ERR_ARG); + return; + } + + THIS_PIPELINE->next_rate = rate; + set_pipeline_rate(THIS); + } + +END_PROPERTY + +BEGIN_METHOD(MediaPipeline_Forward, GB_INTEGER count) + + GstElement *sink; + int count = VARGOPT(count, 1); + + if (count <= 0) + return; + + sink = find_sink(ELEMENT); + if (!sink) + return; + + gst_element_send_event(sink, gst_event_new_step(GST_FORMAT_BUFFERS, fabs(count), 1.0, TRUE, FALSE)); + +END_METHOD + +//---- Media -------------------------------------------------------------- + +BEGIN_METHOD(Media_Link, GB_OBJECT controls) + + GB_OBJECT *controls = ARG(controls); + int i; + CMEDIACONTROL *c1, *c2; + + if (GB.CheckObject(VARG(controls))) + return; + + // GB.NParam() returns 0 when MediaPipeline_Link has just two arguments + + for (i = 0; i <= GB.NParam(); i++) + { + c1 = VALUE(&controls[i]); + c2 = VALUE(&controls[i + 1]); + if ((i == 0 && GB.CheckObject(c1)) || GB.CheckObject(c2)) + return; + gst_element_link(c1->elt, c2->elt); + } + +END_METHOD + +BEGIN_METHOD(Media_Time, GB_FLOAT second) + + GB.ReturnLong(VARG(second) * 1E9); + +END_METHOD + +BEGIN_METHOD(Media_URL, GB_STRING path) + + char *path = GB.RealFileName(STRING(path), LENGTH(path)); + + path = g_filename_to_uri(path, NULL, NULL); + GB.ReturnNewZeroString(path); + g_free(path); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC MediaTagListDesc[] = +{ + GB_DECLARE("MediaTagList", sizeof(CMEDIATAGLIST)), + GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(MediaTagList_check), + + GB_METHOD("_get", "v", MediaTagList_get, "(Name)s"), + GB_PROPERTY_READ("Tags", "String[]", MediaTagList_Tags), + + GB_END_DECLARE +}; + +GB_DESC MediaMessageDesc[] = +{ + GB_DECLARE("MediaMessage", sizeof(CMEDIAMESSAGE)), + GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(MediaMessage_check), + + GB_PROPERTY_READ("Type", "i", MediaMessage_Type), + GB_PROPERTY_READ("Name", "s", MediaMessage_Name), + + GB_METHOD("_get", "v", MediaMessage_get, "(Name)s"), + GB_METHOD("_next", "v", MediaMessage_next, NULL), + GB_PROPERTY_READ("Key", "s", MediaMessage_Key), + GB_PROPERTY_READ("Keys", "String[]", MediaMessage_Keys), + GB_PROPERTY_READ("Count", "i", MediaMessage_Count), + + //Constants + GB_CONSTANT("Eos", "i", GST_MESSAGE_EOS), + GB_CONSTANT("Error", "i", GST_MESSAGE_ERROR), + GB_CONSTANT("Warning", "i", GST_MESSAGE_WARNING), + GB_CONSTANT("Info", "i", GST_MESSAGE_INFO), + GB_CONSTANT("Tag", "i", GST_MESSAGE_TAG), + GB_CONSTANT("Buffering", "i", GST_MESSAGE_BUFFERING), + GB_CONSTANT("StateChanged", "i", GST_MESSAGE_STATE_CHANGED), + GB_CONSTANT("StepStart", "i", GST_MESSAGE_STEP_START), + GB_CONSTANT("StepDone", "i", GST_MESSAGE_STEP_DONE), + GB_CONSTANT("ClockLost", "i", GST_MESSAGE_CLOCK_LOST), + GB_CONSTANT("NewClock", "i", GST_MESSAGE_NEW_CLOCK), + GB_CONSTANT("Status", "i", GST_MESSAGE_STREAM_STATUS), + GB_CONSTANT("Element", "i", GST_MESSAGE_ELEMENT), + GB_CONSTANT("SegmentDone", "i", GST_MESSAGE_SEGMENT_DONE), + GB_CONSTANT("DurationChanged", "i", GST_MESSAGE_DURATION_CHANGED), + GB_CONSTANT("Latency", "i", GST_MESSAGE_LATENCY), + GB_CONSTANT("StateAsync", "i", GST_MESSAGE_ASYNC_DONE), + GB_CONSTANT("RequestState", "i", GST_MESSAGE_REQUEST_STATE), + GB_CONSTANT("Qos", "i", GST_MESSAGE_QOS), + GB_CONSTANT("Progress", "i", GST_MESSAGE_PROGRESS), + GB_CONSTANT("Toc", "i", GST_MESSAGE_TOC), + GB_CONSTANT("Start", "i", GST_MESSAGE_STREAM_START), + + GB_END_DECLARE +}; + + +GB_DESC MediaLinkDesc[] = +{ + GB_DECLARE("MediaLink", sizeof(CMEDIALINK)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, MediaLink_free, NULL), + + GB_PROPERTY_READ("Name", "s", MediaLink_Name), + GB_PROPERTY_READ("Peer", "MediaControl", MediaLink_Peer), + GB_PROPERTY_READ("Input", "s", MediaLink_Input), + GB_PROPERTY_READ("Output", "s", MediaLink_Output), + + GB_END_DECLARE +}; + + +GB_DESC MediaControlDesc[] = +{ + GB_DECLARE("MediaControl", sizeof(CMEDIACONTROL)), + + GB_METHOD("_new", NULL, MediaControl_new, "[(Parent)MediaContainer;(Type)s]"), + GB_METHOD("_free", NULL, MediaControl_free, NULL), + + GB_PROPERTY("Name", "s", MediaControl_Name), + GB_PROPERTY_READ("Type", "s", MediaControl_Type), + GB_PROPERTY_READ("Parent", "MediaContainer", MediaControl_Parent), + GB_PROPERTY("State", "i", MediaControl_State), + GB_PROPERTY("Tag", "v", MediaControl_Tag), + + GB_METHOD("_put", NULL, MediaControl_put, "(Value)v(Property)s"), + GB_METHOD("_get", "v", MediaControl_get, "(Property)s"), + + GB_METHOD("LinkTo", NULL, MediaControl_LinkTo, "(Target)MediaControl;[(Output)s(Input)s]"), + GB_METHOD("LinkLaterTo", NULL, MediaControl_LinkLaterTo, "(Target)MediaControl;"), + + GB_PROPERTY_READ("Inputs", "String[]", MediaControl_Inputs), + GB_PROPERTY_READ("Outputs", "String[]", MediaControl_Outputs), + + GB_METHOD("GetLink", "MediaLink", MediaControl_GetLink, "(Name)s"), + + GB_METHOD("SetWindow", NULL, MediaControl_SetWindow, "(Control)Control;[(X)i(Y)i(Width)i(Height)i]"), + GB_METHOD("GetLastImage", "Image", MediaControl_GetLastImage, NULL), + + GB_EVENT("State", NULL, NULL, &EVENT_State), + //GB_EVENT("Signal", NULL, "(Arg)MediaSignalArguments", &EVENT_Signal), + + //GB_PROPERTY_READ("Protocols", "String[]", MediaControl_Protocols), + + GB_END_DECLARE +}; + +GB_DESC MediaFilterDesc[] = +{ + GB_DECLARE("MediaFilter", sizeof(CMEDIACONTROL)), + GB_INHERITS("MediaControl"), + + //GB_METHOD("_new", NULL, MediaFilter_new, NULL), + + GB_PROPERTY("Filter", "s", MediaFilter_Filter), + + GB_END_DECLARE +}; + +GB_DESC MediaContainerChildrenDesc[] = +{ + GB_DECLARE_VIRTUAL(".MediaContainer.Children"), + + GB_PROPERTY_READ("Count", "i", MediaContainerChildren_Count), + GB_METHOD("_get", "MediaControl", MediaContainerChildren_get, "(Index)i"), + GB_METHOD("_next", "MediaControl", MediaContainerChildren_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC MediaContainerDesc[] = +{ + GB_DECLARE("MediaContainer", sizeof(CMEDIACONTAINER)), + GB_INHERITS("MediaControl"), + + GB_METHOD("_new", NULL, MediaContainer_new, NULL), + + GB_METHOD("AddInput", NULL, MediaContainer_AddInput, "(Child)MediaControl;[(Name)s]"), + GB_METHOD("AddOutput", NULL, MediaContainer_AddOutput, "(Child)MediaControl;[(Name)s]"), + + GB_PROPERTY_SELF("Children", ".MediaContainer.Children"), + + GB_END_DECLARE +}; + +GB_DESC MediaPipelineDesc[] = +{ + GB_DECLARE("MediaPipeline", sizeof(CMEDIAPIPELINE)), + GB_INHERITS("MediaContainer"), + + GB_METHOD("_new", NULL, MediaPipeline_new, "[(Polling)i]"), + GB_METHOD("_free", NULL, MediaPipeline_free, NULL), + + /*GB_CONSTANT("Null", "i", GST_STATE_NULL), + GB_CONSTANT("Ready", "i", GST_STATE_READY), + GB_CONSTANT("Paused", "i", GST_STATE_PAUSED), + GB_CONSTANT("Playing", "i", GST_STATE_PLAYING), + + GB_CONSTANT("Info", "i", 0), + GB_CONSTANT("Warning", "i", 1), + GB_CONSTANT("Error", "i", 2),*/ + + GB_PROPERTY("Position", "f", MediaPipeline_Position), + GB_PROPERTY_READ("Duration", "f", MediaPipeline_Duration), + GB_PROPERTY("Pos", "f", MediaPipeline_Position), + GB_PROPERTY_READ("Length", "f", MediaPipeline_Duration), + + GB_METHOD("Play", NULL, MediaPipeline_Play, "[(Async)b]"), + GB_METHOD("Stop", NULL, MediaPipeline_Stop, NULL), + GB_METHOD("Pause", NULL, MediaPipeline_Pause, NULL), + GB_METHOD("Close", NULL, MediaPipeline_Close, NULL), + + GB_METHOD("Seek", NULL, MediaPipeline_Seek, "(Position)f[(Flags)i]"), + GB_PROPERTY("Speed", "f", MediaPipeline_Speed), + GB_METHOD("Forward", NULL, MediaPipeline_Forward, "[(Frames)i]"), + + GB_EVENT("Start", NULL, NULL, &EVENT_Start), + GB_EVENT("End", NULL, NULL, &EVENT_End), + GB_EVENT("Message", NULL, "(Source)MediaControl;(Type)i(Message)s", &EVENT_Message), + GB_EVENT("Tag", NULL, "(TagList)MediaTagList;", &EVENT_Tag), + GB_EVENT("Event", NULL, "(Message)MediaMessage;", &EVENT_Event), + GB_EVENT("Buffering", NULL, NULL, &EVENT_Buffering), + GB_EVENT("Duration", NULL, NULL, &EVENT_Duration), + GB_EVENT("Position", NULL, NULL, &EVENT_Position), + GB_EVENT("Progress", NULL, NULL, &EVENT_Progress), + GB_EVENT("AboutToFinish", NULL, NULL, &EVENT_AboutToFinish), + + GB_END_DECLARE +}; + +GB_DESC MediaDesc[] = +{ + GB_DECLARE_VIRTUAL("Media"), + + GB_CONSTANT("Unknown", "i", -1), + + GB_CONSTANT("Null", "i", GST_STATE_NULL), + GB_CONSTANT("Ready", "i", GST_STATE_READY), + GB_CONSTANT("Paused", "i", GST_STATE_PAUSED), + GB_CONSTANT("Playing", "i", GST_STATE_PLAYING), + + GB_CONSTANT("Info", "i", 0), + GB_CONSTANT("Warning", "i", 1), + GB_CONSTANT("Error", "i", 2), + + GB_CONSTANT("SeekAccurate", "i", GST_SEEK_FLAG_ACCURATE), + GB_CONSTANT("SeekKeyUnit", "i", GST_SEEK_FLAG_KEY_UNIT), + GB_CONSTANT("SeekTrickMode", "i", GST_SEEK_FLAG_TRICKMODE), + GB_CONSTANT("SeekSnapBefore", "i", GST_SEEK_FLAG_SNAP_BEFORE), + GB_CONSTANT("SeekSnapAfter", "i", GST_SEEK_FLAG_SNAP_AFTER), + GB_CONSTANT("SeekSnapNearest", "i", GST_SEEK_FLAG_SNAP_NEAREST), + GB_CONSTANT("SeekTrickModeKeyUnit", "i", GST_SEEK_FLAG_TRICKMODE_KEY_UNITS), + GB_CONSTANT("SeekTrickModeNoAudio", "i", GST_SEEK_FLAG_TRICKMODE_NO_AUDIO), + + GB_STATIC_METHOD("Link", NULL, Media_Link, "(FirstControl)MediaControl;(SecondControl)MediaControl;."), + GB_STATIC_METHOD("Time", "l", Media_Time, "(Seconds)f"), + GB_STATIC_METHOD("URL", "s", Media_URL, "(Path)s"), + + GB_END_DECLARE +}; + diff --git a/gb.media/src/c_media.h b/gb.media/src/c_media.h new file mode 100644 index 00000000..fe12611e --- /dev/null +++ b/gb.media/src/c_media.h @@ -0,0 +1,138 @@ +/*************************************************************************** + + c_media.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MEDIA_H +#define __C_MEDIA_H + +#include "main.h" + +#ifndef __C_MEDIA_C + +//extern GB_DESC MediaSignalArgumentsDesc[]; +extern GB_DESC MediaTagListDesc[]; +extern GB_DESC MediaMessageDesc[]; +extern GB_DESC MediaLinkDesc[]; +extern GB_DESC MediaControlDesc[]; +extern GB_DESC MediaFilterDesc[]; +extern GB_DESC MediaContainerChildrenDesc[]; +extern GB_DESC MediaContainerDesc[]; +extern GB_DESC MediaPipelineDesc[]; +extern GB_DESC MediaDesc[]; + +#else + +#define THIS ((CMEDIACONTROL *)_object) +#define THIS_ARG ((CMEDIASIGNALARGUMENTS *)_object) +#define ELEMENT THIS->elt +#define PIPELINE ((GstPipeline *)THIS->elt) + +#define THIS_TAGLIST ((CMEDIATAGLIST *)_object) +#define THIS_MESSAGE ((CMEDIAMESSAGE *)_object) +#define THIS_PIPELINE ((CMEDIAPIPELINE *)_object) + +#define THIS_LINK ((CMEDIALINK *)_object) +#define LINK THIS_LINK->pad + +#endif + +typedef + struct { + GB_BASE ob; + GstPad *pad; + } + CMEDIALINK; + +typedef + struct { + GB_BASE ob; + GstElement *elt; + void *dest; + GB_VARIANT_VALUE tag; + unsigned state : 3; + unsigned error : 1; + unsigned borrow : 1; + unsigned eos : 1; + } + CMEDIACONTROL; + +typedef + CMEDIACONTROL CMEDIACONTAINER; + +typedef + struct { + CMEDIACONTROL control; + GB_TIMER *watch; + int polling; + gint64 pos; + gint64 duration; + double rate; + double next_rate; + unsigned in_message : 1; + unsigned about_to_finish : 1; + } + CMEDIAPIPELINE; + +typedef + struct { + GB_BASE ob; + GstTagList *tags; + } + CMEDIATAGLIST; + +typedef + struct { + GB_BASE ob; + GstMessage *message; + const char *lastKey; + } + CMEDIAMESSAGE; + +#if 0 +typedef + struct { + GB_BASE ob; + guint n_param_values; + const GValue *param_values; + } + CMEDIASIGNALARGUMENTS; +#endif + +#define TO_SECOND(_time) ((double)((_time) / 1000) / 1E6) +#define TO_TIME(_second) ((gint64)((_second) * 1E9)) + +void MEDIA_raise_event(void *_object, int event); +CMEDIACONTROL *MEDIA_get_control_from_element(void *element, bool create); +bool MEDIA_set_state(void *_object, int state, bool error, bool async); + +bool MEDIA_get_flag(void *element, char *property, int flag); +void MEDIA_set_flag(void *element, char *property, int flag, bool value); + +void MEDIA_set_property(void *_object, const char *property, GB_VALUE *v); + +GB_IMG *MEDIA_get_image_from_sample(GstSample *sample, bool convert); + +void MEDIA_stop_pipeline(CMEDIACONTROL *_object); + +#endif /* __C_MEDIA_H */ diff --git a/gb.media/src/c_mediaplayer.c b/gb.media/src/c_mediaplayer.c new file mode 100644 index 00000000..81dbbad6 --- /dev/null +++ b/gb.media/src/c_mediaplayer.c @@ -0,0 +1,658 @@ +/*************************************************************************** + + c_mediaplayer.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MEDIAPLAYER_C + +#include "c_mediaplayer.h" + +// GStreamer playbin documents a enum that is not defined anywhere!? +// If someone knows where I can find it... + +typedef enum { + GST_PLAY_FLAG_VIDEO = (1 << 0), + GST_PLAY_FLAG_AUDIO = (1 << 1), + GST_PLAY_FLAG_TEXT = (1 << 2), + GST_PLAY_FLAG_VIS = (1 << 3), + GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), + GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), + GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), + GST_PLAY_FLAG_DOWNLOAD = (1 << 7), + GST_PLAY_FLAG_BUFFERING = (1 << 8), + GST_PLAY_FLAG_DEINTERLACE = (1 << 9) +} GstPlayFlags; + + +static int get_int(CMEDIAPLAYER *_object, char *name) +{ + int value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_int(CMEDIAPLAYER *_object, char *name, int value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static int get_int64(CMEDIAPLAYER *_object, char *name) +{ + gint64 value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_int64(CMEDIAPLAYER *_object, char *name, gint64 value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static double get_double(CMEDIAPLAYER *_object, char *name) +{ + double value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_double(CMEDIAPLAYER *_object, char *name, double value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static gboolean get_boolean(CMEDIAPLAYER *_object, char *name) +{ + gboolean value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_boolean(CMEDIAPLAYER *_object, char *name, gboolean value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static char *get_string(CMEDIAPLAYER *_object, char *name) +{ + gchar *value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_string(CMEDIAPLAYER *_object, char *name, gchar *value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static CMEDIACONTROL *get_control(CMEDIAPLAYER *_object, char *name) +{ + GstElement *value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return MEDIA_get_control_from_element(value, TRUE); +} + +static void set_control(CMEDIAPLAYER *_object, char *name, CMEDIACONTROL *control) +{ + GstElement *elt; + GstBin *parent; + + if (!control) + { + g_object_set(G_OBJECT(ELEMENT), name, NULL, NULL); + return; + } + + elt = control->elt; + parent = GST_BIN(gst_element_get_parent(elt)); + if (parent) + { + gst_object_ref(elt); + gst_bin_remove(parent, elt); + } + + g_object_set(G_OBJECT(ELEMENT), name, elt, NULL); + + if (parent) + gst_object_unref(elt); +} + +#define set_flag(_object, _flag, _value) MEDIA_set_flag(ELEMENT, "flags", _flag, _value) + +#define IMPLEMENT_FLAG(_func, _flag) \ +BEGIN_PROPERTY(_func) \ +\ + if (READ_PROPERTY) \ + GB.ReturnBoolean(MEDIA_get_flag(ELEMENT, "flags", _flag)); \ + else \ + MEDIA_set_flag(ELEMENT, "flags", _flag, VPROP(GB_BOOLEAN)); \ +\ +END_PROPERTY + +static GB_IMG *get_frame(void *_object) +{ + GstElement *play = GST_ELEMENT(ELEMENT); + GstSample *sample; + GstCaps *to_caps; + const char *format; + + //g_return_val_if_fail (play != NULL, NULL); + //g_return_val_if_fail (GST_IS_ELEMENT (play), NULL); + + switch (IMAGE.GetDefaultFormat()) + { + case GB_IMAGE_BGRA: + case GB_IMAGE_BGRP: + format = "BGR"; + break; + + case GB_IMAGE_RGBA: + case GB_IMAGE_RGBP: + format = "RGB"; + break; + + default: + GB.Error("Unsupported default image format"); + return NULL; + } + + /* our desired output format (RGB or BGR) */ + to_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, format, + /* Note: we don't ask for a specific width/height here, so that + * videoscale can adjust dimensions from a non-1/1 pixel aspect + * ratio to a 1/1 pixel-aspect-ratio. We also don't ask for a + * specific framerate, because the input framerate won't + * necessarily match the output framerate if there's a deinterlacer + * in the pipeline. */ + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); + + /* get frame */ + g_signal_emit_by_name (play, "convert-sample", to_caps, &sample); + gst_caps_unref(to_caps); + + return MEDIA_get_image_from_sample(sample, FALSE); +} + + +//---- MediaPlayerAudio -------------------------------------------------- + +BEGIN_PROPERTY(MediaPlayerAudio_Count) + + GB.ReturnInteger(get_int(THIS, "n-audio")); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(get_int(THIS, "current-audio")); + else + set_int(THIS, "current-audio", VPROP(GB_INTEGER)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayerAudio_Enabled, GST_PLAY_FLAG_AUDIO) +IMPLEMENT_FLAG(MediaPlayerAudio_SoftwareVolume, GST_PLAY_FLAG_SOFT_VOLUME) +IMPLEMENT_FLAG(MediaPlayerAudio_NativeOnly, GST_PLAY_FLAG_NATIVE_AUDIO) + +BEGIN_PROPERTY(MediaPlayerAudio_Volume) + + if (READ_PROPERTY) + GB.ReturnFloat(get_double(THIS, "volume")); + else + set_double(THIS, "volume", VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Mute) + + if (READ_PROPERTY) + GB.ReturnBoolean(get_boolean(THIS, "mute")); + else + set_boolean(THIS, "mute", VPROP(GB_BOOLEAN) != 0); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Offset) + + if (READ_PROPERTY) + GB.ReturnFloat(TO_SECOND(get_int64(THIS, "av-offset"))); + else + set_int64(THIS, "av-offset", TO_TIME(VPROP(GB_FLOAT))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Output) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "audio-sink")); + else + set_control(THIS, "audio-sink", VPROP(GB_OBJECT)); + +END_PROPERTY + +//---- MediaPlayerVideo -------------------------------------------------- + +BEGIN_PROPERTY(MediaPlayerVideo_Count) + + GB.ReturnInteger(get_int(THIS, "n-video")); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerVideo_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(get_int(THIS, "current-video")); + else + set_int(THIS, "current-video", VPROP(GB_INTEGER)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayerVideo_Enabled, GST_PLAY_FLAG_VIDEO) +IMPLEMENT_FLAG(MediaPlayerVideo_NativeOnly, GST_PLAY_FLAG_NATIVE_VIDEO) +IMPLEMENT_FLAG(MediaPlayerVideo_Deinterlace, GST_PLAY_FLAG_DEINTERLACE) + +BEGIN_PROPERTY(MediaPlayerVideo_Output) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "video-sink")); + else + set_control(THIS, "video-sink", VPROP(GB_OBJECT)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerVideo_Visualisation) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "vis-plugin")); + else + { + CMEDIACONTROL *vis = VPROP(GB_OBJECT); + //CMEDIACONTROL *old = get_control(THIS, "vis-plugin"); + bool playing; + + set_flag(THIS, GST_PLAY_FLAG_VIS, FALSE); + + playing = THIS_CONTROL->state == GST_STATE_PLAYING; + if (playing) + MEDIA_set_state(THIS, GST_STATE_PAUSED, FALSE, FALSE); + + //set_control(THIS, "vis-plugin", NULL); + //if (vis) + set_control(THIS, "vis-plugin", vis); + if (vis) + set_flag(THIS, GST_PLAY_FLAG_VIS, TRUE); + + if (playing) + MEDIA_set_state(THIS, GST_STATE_PLAYING, FALSE, FALSE); + } + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerVideo_Image) + + GB.ReturnObject(get_frame(THIS)); + +END_PROPERTY + +//---- MediaPlayerSubtitles ---------------------------------------------- + +BEGIN_PROPERTY(MediaPlayerSubtitles_Count) + + GB.ReturnInteger(get_int(THIS, "n-text")); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerSubtitles_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(get_int(THIS, "current-text")); + else + set_int(THIS, "current-text", VPROP(GB_INTEGER)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayerSubtitles_Enabled, GST_PLAY_FLAG_TEXT) + +BEGIN_PROPERTY(MediaPlayerSubtitles_Charset) + + if (READ_PROPERTY) + { + char *charset = get_string(THIS, "subtitle-encoding"); + GB.ReturnNewZeroString(charset); + g_free(charset); + } + else + set_string(THIS, "subtitle-encoding", GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerSubtitles_URL) + + if (READ_PROPERTY) + { + char *charset = get_string(THIS, "suburi"); + GB.ReturnNewZeroString(charset); + g_free(charset); + } + else + set_string(THIS, "suburi", GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerSubtitles_Output) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "text-sink")); + else + set_control(THIS, "text-sink", VPROP(GB_OBJECT)); + +END_PROPERTY + +//---- MediaPlayerBalanceChannel ----------------------------------------- + +static GstColorBalanceChannel *get_channel(void *_object) +{ + GList *channels = (GList *)gst_color_balance_list_channels(BALANCE); + GstColorBalanceChannel *channel = (GstColorBalanceChannel *)g_list_nth_data(channels, THIS->channel); + if (!channel) GB.Error(GB_ERR_ARG); + return channel; +} + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Name) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (channel) + GB.ReturnNewZeroString(channel->label); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Min) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (channel) + GB.ReturnInteger(channel->min_value); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Max) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (channel) + GB.ReturnInteger(channel->max_value); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Value) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (!channel) + return; + + if (READ_PROPERTY) + GB.ReturnInteger(gst_color_balance_get_value(BALANCE, channel)); + else + gst_color_balance_set_value(BALANCE, channel, VPROP(GB_INTEGER)); + +END_PROPERTY + +//---- MediaPlayerBalance ------------------------------------------------ + +BEGIN_PROPERTY(MediaPlayerBalance_Count) + + GB.ReturnInteger(g_list_length((GList *)gst_color_balance_list_channels(BALANCE))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalance_Hardware) + + GB.ReturnBoolean(gst_color_balance_get_balance_type(BALANCE) == GST_COLOR_BALANCE_HARDWARE); + +END_PROPERTY + +BEGIN_METHOD(MediaPlayerBalance_get, GB_INTEGER index) + + GList *channels = (GList *)gst_color_balance_list_channels(BALANCE); + int index = VARG(index); + + if (index < 0 || index >= g_list_length(channels)) + GB.Error(GB_ERR_BOUND); + else + { + THIS->channel = index; + RETURN_SELF(); + } + +END_PROPERTY + +//---- MediaPlayer ------------------------------------------------------- + +DECLARE_EVENT(EVENT_AudioChanged); +DECLARE_EVENT(EVENT_SubtitlesChanged); +DECLARE_EVENT(EVENT_VideoChanged); +//DECLARE_EVENT(EVENT_SourceSetup); + +static void cb_about_to_finish(void *playbin, void *_object) +{ + g_mutex_lock(&THIS->next_uri_mutex); + if (THIS->next_uri) + { + set_string(THIS, "uri", THIS->next_uri); + GB.StoreString(NULL, &THIS->next_uri); + } + g_mutex_unlock(&THIS->next_uri_mutex); +} + +static void cb_audio_changed(void *playbin, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_AudioChanged); +} + +static void cb_text_changed(void *playbin, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_SubtitlesChanged); +} + +static void cb_video_changed(void *playbin, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_VideoChanged); +} + +/*static void cb_source_setup(void *playbin, GstElement *source, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_SourceSetup); +}*/ + +BEGIN_METHOD_VOID(MediaPlayer_new) + + g_mutex_init(&THIS->next_uri_mutex); + + g_signal_connect(ELEMENT, "about-to-finish", G_CALLBACK(cb_about_to_finish), THIS); + g_signal_connect(ELEMENT, "audio-changed", G_CALLBACK(cb_audio_changed), THIS); + g_signal_connect(ELEMENT, "text-changed", G_CALLBACK(cb_text_changed), THIS); + g_signal_connect(ELEMENT, "video-changed", G_CALLBACK(cb_video_changed), THIS); + //g_signal_connect(ELEMENT, "source-setup", G_CALLBACK(cb_source_setup), THIS); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPlayer_free) + + g_mutex_lock(&THIS->next_uri_mutex); + GB.StoreString(NULL, &THIS->next_uri); + g_mutex_unlock(&THIS->next_uri_mutex); + g_mutex_clear(&THIS->next_uri_mutex); + +END_METHOD + +BEGIN_PROPERTY(MediaPlayer_ConnectionSpeed) + + if (READ_PROPERTY) + GB.ReturnLong(get_int64(THIS, "connection-speed")); + else + set_int64(THIS, "connection-speed", VPROP(GB_LONG)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayer_ProgressiveDownload, GST_PLAY_FLAG_DOWNLOAD) +IMPLEMENT_FLAG(MediaPlayer_Buffering, GST_PLAY_FLAG_BUFFERING) + +BEGIN_PROPERTY(MediaPlayer_URL) + + if (READ_PROPERTY) + { + char *charset = get_string(THIS, "uri"); + GB.ReturnNewZeroString(charset); + g_free(charset); + } + else + { + MEDIA_stop_pipeline(THIS_CONTROL); + set_string(THIS, "uri", GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayer_NextURL) + + g_mutex_lock(&THIS->next_uri_mutex); + if (READ_PROPERTY) + GB.ReturnString(THIS->next_uri); + else + GB.StoreString(PROP(GB_STRING), &THIS->next_uri); + g_mutex_unlock(&THIS->next_uri_mutex); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayer_Input) + + GB.ReturnObject(get_control(THIS, "source")); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC MediaPlayerAudioDesc[] = +{ + GB_DECLARE(".MediaPlayer.Audio", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerAudio_Count), + GB_PROPERTY("Current", "i", MediaPlayerAudio_Current), + GB_PROPERTY("Enabled", "b", MediaPlayerAudio_Enabled), + GB_PROPERTY("SoftwareVolume", "b", MediaPlayerAudio_SoftwareVolume), + GB_PROPERTY("NativeOnly", "b", MediaPlayerAudio_NativeOnly), + GB_PROPERTY("Volume", "f", MediaPlayerAudio_Volume), + GB_PROPERTY("Mute", "b", MediaPlayerAudio_Mute), + GB_PROPERTY("Offset", "f", MediaPlayerAudio_Offset), + GB_PROPERTY("Output", "MediaControl", MediaPlayerAudio_Output), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerVideoDesc[] = +{ + GB_DECLARE(".MediaPlayer.Video", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerVideo_Count), + GB_PROPERTY("Current", "i", MediaPlayerVideo_Current), + GB_PROPERTY("Enabled", "b", MediaPlayerVideo_Enabled), + GB_PROPERTY("NativeOnly", "b", MediaPlayerVideo_NativeOnly), + GB_PROPERTY("Deinterlace", "b", MediaPlayerVideo_Deinterlace), + GB_PROPERTY("Output", "MediaControl", MediaPlayerVideo_Output), + GB_PROPERTY("Visualisation", "MediaControl", MediaPlayerVideo_Visualisation), + GB_PROPERTY_READ("Image", "Image", MediaPlayerVideo_Image), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerSubtitlesDesc[] = +{ + GB_DECLARE(".MediaPlayer.Subtitles", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerSubtitles_Count), + GB_PROPERTY("Current", "i", MediaPlayerSubtitles_Current), + GB_PROPERTY("Enabled", "b", MediaPlayerSubtitles_Enabled), + GB_PROPERTY("Charset", "s", MediaPlayerSubtitles_Charset), + GB_PROPERTY("URL", "s", MediaPlayerSubtitles_URL), + GB_PROPERTY("Output", "MediaControl", MediaPlayerSubtitles_Output), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerBalanceChannelDesc[] = +{ + GB_DECLARE(".MediaPlayer.Balance.Channel", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Name", "s", MediaPlayerBalanceChannel_Name), + GB_PROPERTY_READ("Min", "i", MediaPlayerBalanceChannel_Min), + GB_PROPERTY_READ("Max", "i", MediaPlayerBalanceChannel_Max), + GB_PROPERTY("Value", "i", MediaPlayerBalanceChannel_Value), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerBalanceDesc[] = +{ + GB_DECLARE(".MediaPlayer.Balance", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerBalance_Count), + GB_PROPERTY_READ("Hardware", "b", MediaPlayerBalance_Hardware), + GB_METHOD("_get", ".MediaPlayer.Balance.Channel", MediaPlayerBalance_get, "(Channel)i"), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerDesc[] = +{ + GB_DECLARE("MediaPlayer", sizeof(CMEDIAPLAYER)), + GB_INHERITS("MediaPipeline"), + + GB_METHOD("_new", NULL, MediaPlayer_new, NULL), + GB_METHOD("_free", NULL, MediaPlayer_free, NULL), + + GB_PROPERTY_SELF("Audio", ".MediaPlayer.Audio"), + GB_PROPERTY_SELF("Video", ".MediaPlayer.Video"), + GB_PROPERTY_SELF("Subtitles", ".MediaPlayer.Subtitles"), + GB_PROPERTY_SELF("Balance", ".MediaPlayer.Balance"), + //GB_PROPERTY_SELF("Visualisation", ".MediaPlayer.Visualisation"), + + GB_PROPERTY("ConnectionSpeed", "l", MediaPlayer_ConnectionSpeed), + GB_PROPERTY("ProgressiveDownload", "b", MediaPlayer_ProgressiveDownload), + GB_PROPERTY("Buffering", "b", MediaPlayer_Buffering), + GB_PROPERTY("URL", "s", MediaPlayer_URL), + GB_PROPERTY("NextURL", "s", MediaPlayer_NextURL), + GB_PROPERTY_READ("Input", "MediaControl", MediaPlayer_Input), + + GB_EVENT("AudioChanged", NULL, NULL, &EVENT_AudioChanged), + GB_EVENT("SubtitlesChanged", NULL, NULL, &EVENT_SubtitlesChanged), + GB_EVENT("VideoChanged", NULL, NULL, &EVENT_VideoChanged), + //GB_EVENT("SourceSetup", NULL, NULL, &EVENT_SourceSetup), + + GB_END_DECLARE +}; + diff --git a/gb.media/src/c_mediaplayer.h b/gb.media/src/c_mediaplayer.h new file mode 100644 index 00000000..3eb2b2b8 --- /dev/null +++ b/gb.media/src/c_mediaplayer.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + c_mediaplayer.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MEDIAPLAYER_H +#define __C_MEDIAPLAYER_H + +#include "main.h" +#include "c_media.h" + +#include +#include + +#ifndef __C_MEDIAPLAYER_C + +extern GB_DESC MediaPlayerDesc[]; +extern GB_DESC MediaPlayerAudioDesc[]; +extern GB_DESC MediaPlayerVideoDesc[]; +extern GB_DESC MediaPlayerSubtitlesDesc[]; +extern GB_DESC MediaPlayerBalanceDesc[]; +extern GB_DESC MediaPlayerBalanceChannelDesc[]; + +#else + +#define THIS ((CMEDIAPLAYER *)_object) +#define THIS_CONTROL (&(THIS->base.control)) +#define ELEMENT ((GstPipeline *)(THIS_CONTROL->elt)) +#define BALANCE (GST_COLOR_BALANCE(ELEMENT)) + +#endif + +typedef + struct { + CMEDIAPIPELINE base; + int channel; + GMutex next_uri_mutex; + char *next_uri; + } + CMEDIAPLAYER; + +#endif /* __C_MEDIAPLAYER_H */ diff --git a/gb.media/src/c_mediavideo.c b/gb.media/src/c_mediavideo.c new file mode 100644 index 00000000..ed72be06 --- /dev/null +++ b/gb.media/src/c_mediavideo.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + c_mediavideo.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MEDIAVIDEO_C + +#include + +#include "c_mediavideo.h" + +BEGIN_METHOD_VOID(MediaVideo_new) + +END_METHOD + +//------------------------------------------------------------------------- + +#define IMPLEMENT_FLAG(_proc, _flag) \ +BEGIN_PROPERTY(MediaVideo_##_proc) \ +\ + GB.ReturnBoolean(MEDIA_get_flag(ELEMENT, "flags", _flag)); \ +\ +END_PROPERTY + +IMPLEMENT_FLAG(HasCapture, V4L2_CAP_VIDEO_CAPTURE) +IMPLEMENT_FLAG(HasOutput, V4L2_CAP_VIDEO_OUTPUT) +IMPLEMENT_FLAG(HasOverlay, V4L2_CAP_VIDEO_OVERLAY) +IMPLEMENT_FLAG(HasVBICapture, V4L2_CAP_VBI_CAPTURE) +IMPLEMENT_FLAG(HasVBIOutput, V4L2_CAP_VBI_OUTPUT) +IMPLEMENT_FLAG(HasTuner, V4L2_CAP_TUNER) +IMPLEMENT_FLAG(HasAudio, V4L2_CAP_AUDIO) + +GB_DESC MediaVideoDesc[] = +{ + GB_DECLARE("MediaVideo", sizeof(CMEDIAVIDEO)), + GB_INHERITS("MediaControl"), + + GB_METHOD("_new", NULL, MediaVideo_new, NULL), + + //GB_PROPERTY("Device", "s", MediaVideo_Device), + //GB_PROPERTY_READ("Name", "s", MediaVideo_Name), + + GB_PROPERTY_READ("HasCapture", "b", MediaVideo_HasCapture), + GB_PROPERTY_READ("HasOutput", "b", MediaVideo_HasOutput), + GB_PROPERTY_READ("HasOverlay", "b", MediaVideo_HasOverlay), + GB_PROPERTY_READ("HasVBICapture", "b", MediaVideo_HasVBICapture), + GB_PROPERTY_READ("HasVBIOutput", "b", MediaVideo_HasVBIOutput), + GB_PROPERTY_READ("HasTuner", "b", MediaVideo_HasTuner), + GB_PROPERTY_READ("HasAudio", "b", MediaVideo_HasAudio), + + GB_END_DECLARE +}; + diff --git a/gb.media/src/c_mediavideo.h b/gb.media/src/c_mediavideo.h new file mode 100644 index 00000000..d3da214b --- /dev/null +++ b/gb.media/src/c_mediavideo.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + c_mediavideo.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MEDIAVIDEO_H +#define __C_MEDIAVIDEO_H + +#include "main.h" +#include "c_media.h" + +#include +#include + +#ifndef __C_MEDIAVIDEO_C + +extern GB_DESC MediaVideoDesc[]; + +#else + +#define THIS ((CMEDIAVIDEO *)_object) +#define ELEMENT ((GstElement *)THIS->elt) + +#endif + +typedef + CMEDIACONTROL + CMEDIAVIDEO; + +#endif /* __C_MEDIAVIDEO_H */ diff --git a/gb.media/src/gb.media.component b/gb.media/src/gb.media.component new file mode 100644 index 00000000..4edd9a70 --- /dev/null +++ b/gb.media/src/gb.media.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +State=Stable +Require=gb.image \ No newline at end of file diff --git a/gb.media/src/main.c b/gb.media/src/main.c new file mode 100644 index 00000000..6d44bf2a --- /dev/null +++ b/gb.media/src/main.c @@ -0,0 +1,92 @@ +/*************************************************************************** + + main.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "c_media.h" +#include "c_mediaplayer.h" + +bool MAIN_debug = FALSE; + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + //MediaSignalArgumentsDesc, + MediaTagListDesc, + MediaMessageDesc, + MediaLinkDesc, + MediaControlDesc, + MediaFilterDesc, + MediaContainerChildrenDesc, + MediaContainerDesc, + MediaPipelineDesc, + MediaDesc, + MediaPlayerAudioDesc, + MediaPlayerVideoDesc, + MediaPlayerSubtitlesDesc, + MediaPlayerBalanceChannelDesc, + MediaPlayerBalanceDesc, + MediaPlayerDesc, + NULL +}; + +int MAIN_get_x11_handle(void *control) +{ + int (*get_handle)(void *) = NULL; + + if (!get_handle) + { + GB.Component.GetInfo("GET_HANDLE", (void **)&get_handle); + if (!get_handle) + { + GB.Error("Unable to get window handle"); + return 0; + } + } + + return (*get_handle)(control); +} + +int EXPORT GB_INIT() +{ + char *env; + + gst_init(NULL, NULL); + + env = getenv("GB_MEDIA_DEBUG"); + if (env && atoi(env)) + MAIN_debug = TRUE; + + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.media/src/main.h b/gb.media/src/main.h new file mode 100644 index 00000000..bdc4af59 --- /dev/null +++ b/gb.media/src/main.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + main.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern bool MAIN_debug; +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +int MAIN_get_x11_handle(void *control); + +#endif /* __MAIN_H */ diff --git a/gb.mime/AUTHORS b/gb.mime/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/COPYING b/gb.mime/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.mime/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.mime/ChangeLog b/gb.mime/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/INSTALL b/gb.mime/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.mime/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.mime/Makefile.am b/gb.mime/Makefile.am new file mode 100644 index 00000000..12966aca --- /dev/null +++ b/gb.mime/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @MIME_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.mime/NEWS b/gb.mime/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/README b/gb.mime/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/acinclude.m4 b/gb.mime/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.mime/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.mime/component.am b/gb.mime/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.mime/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.mime/configure.ac b/gb.mime/configure.ac new file mode 100644 index 00000000..22f1886c --- /dev/null +++ b/gb.mime/configure.ac @@ -0,0 +1,58 @@ +dnl ---- configure.ac for gb.mime + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-mime],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.mime) +LT_INIT + +gb_in_component_search=yes + +GB_COMPONENT_PKG_CONFIG( + mime, + MIME, + gb.mime, + [src], + gmime-3.0) + +if test "$have_mime" = "no"; then + + rm -f DISABLED DISABLED.gb.mime + + GB_COMPONENT_PKG_CONFIG( + mime, + MIME, + gb.mime, + [src], + gmime-2.6) + + if test "$have_mime" = "yes"; then + GB_WARNING([But gmime 2.6 has been detected!]) + fi + +fi + +gb_in_component_search=no + +if test "$have_mime" = "no"; then + + rm -f DISABLED DISABLED.gb.mime + + GB_COMPONENT_PKG_CONFIG( + mime, + MIME, + gb.mime, + [src], + gmime-2.6) + + if test "$have_mime" = "yes"; then + GB_WARNING([But gmime 2.6 has been detected!]) + fi + +fi + +AC_CONFIG_FILES([Makefile src/Makefile]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.mime/gambas.h b/gb.mime/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.mime/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.mime/gb_common.h b/gb.mime/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.mime/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.mime/m4 b/gb.mime/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.mime/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.mime/reconf b/gb.mime/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.mime/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.mime/src/Makefile.am b/gb.mime/src/Makefile.am new file mode 100644 index 00000000..201d68c3 --- /dev/null +++ b/gb.mime/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.mime +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.mime.la + +gb_mime_la_LIBADD = @MIME_LIB@ +gb_mime_la_LDFLAGS = -module @LD_FLAGS@ @MIME_LDFLAGS@ +gb_mime_la_CPPFLAGS = @MIME_INC@ + +gb_mime_la_SOURCES = main.c main.h \ + c_mime.h c_mime.c \ + c_mimemessage.h c_mimemessage.c \ + c_mimepart.h c_mimepart.c diff --git a/gb.mime/src/c_mime.c b/gb.mime/src/c_mime.c new file mode 100644 index 00000000..d2d211e1 --- /dev/null +++ b/gb.mime/src/c_mime.c @@ -0,0 +1,108 @@ +/*************************************************************************** + + c_mime.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MIME_C + +#include "c_mime.h" + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Mime_Encode, GB_STRING data; GB_INTEGER encoding) + + GMimeEncoding menc; + int encoding = VARG(encoding); + size_t outlen; + char *outbuf; + + switch(encoding) + { + case GMIME_CONTENT_ENCODING_BASE64: + case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE: + case GMIME_CONTENT_ENCODING_UUENCODE: + break; + default: + GB.Error("Bad encoding"); + return; + } + + g_mime_encoding_init_encode(&menc, encoding); + outlen = g_mime_encoding_outlen(&menc, LENGTH(data)); + outbuf = GB.NewString(NULL, outlen); + outlen = g_mime_encoding_step(&menc, STRING(data), LENGTH(data), outbuf); + outbuf = GB.ExtendString(outbuf, outlen); + + GB.ReturnString(GB.FreeStringLater(outbuf)); + +END_METHOD + +BEGIN_METHOD(Mime_Decode, GB_STRING data; GB_INTEGER encoding) + + GMimeEncoding menc; + int encoding = VARG(encoding); + size_t outlen; + char *outbuf; + + switch(encoding) + { + case GMIME_CONTENT_ENCODING_BASE64: + case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE: + case GMIME_CONTENT_ENCODING_UUENCODE: + break; + default: + GB.Error("Bad encoding"); + return; + } + + g_mime_encoding_init_decode(&menc, encoding); + outlen = g_mime_encoding_outlen(&menc, LENGTH(data)); + outbuf = GB.NewString(NULL, outlen); + outlen = g_mime_encoding_step(&menc, STRING(data), LENGTH(data), outbuf); + outbuf = GB.ExtendString(outbuf, outlen); + + GB.ReturnString(GB.FreeStringLater(outbuf)); + +END_METHOD + +//------------------------------------------------------------------------- + + + +GB_DESC MimeDesc[] = +{ + GB_DECLARE_VIRTUAL("Mime"), + + GB_CONSTANT("Default", "i", GMIME_CONTENT_ENCODING_DEFAULT), + GB_CONSTANT("7Bit", "i", GMIME_CONTENT_ENCODING_7BIT), + GB_CONSTANT("8Bit", "i", GMIME_CONTENT_ENCODING_8BIT), + GB_CONSTANT("Binary", "i", GMIME_CONTENT_ENCODING_BINARY), + GB_CONSTANT("Base64", "i", GMIME_CONTENT_ENCODING_BASE64), + GB_CONSTANT("QuotedPrintable", "i", GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE), + GB_CONSTANT("UUEncode", "i", GMIME_CONTENT_ENCODING_UUENCODE), + + GB_STATIC_METHOD("Encode", "s", Mime_Encode, "(Data)s(Encoding)i"), + GB_STATIC_METHOD("Decode", "s", Mime_Decode, "(Data)s(Encoding)i"), + + GB_END_DECLARE +}; \ No newline at end of file diff --git a/gb.mime/src/c_mime.h b/gb.mime/src/c_mime.h new file mode 100644 index 00000000..7fc2a142 --- /dev/null +++ b/gb.mime/src/c_mime.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + c_mime.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MIME_H +#define __C_MIME_H + +#include "main.h" + +#ifndef __C_MIME_C +extern GB_DESC MimeDesc[]; +#endif + +#endif /* __C_MIME_H */ diff --git a/gb.mime/src/c_mimemessage.c b/gb.mime/src/c_mimemessage.c new file mode 100644 index 00000000..8f4d4b3a --- /dev/null +++ b/gb.mime/src/c_mimemessage.c @@ -0,0 +1,304 @@ +/*************************************************************************** + + c_mimemessage.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + (c) 2018 Bastian Germann + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MIMEMESSAGE_C + +#include "c_mimemessage.h" + +#define THIS ((CMIMEMESSAGE *)_object) +#define MESSAGE THIS->message + +//------------------------------------------------------------------------- + +static GMimeMessage *_message = NULL; + +CMIMEMESSAGE *CMIMEMESSAGE_create(GMimeMessage *message) +{ + CMIMEMESSAGE *mmsg; + + if (!message) + return NULL; + + mmsg = (CMIMEMESSAGE *)g_object_get_data(G_OBJECT(message), "gambas-object"); + if (!mmsg) + { + _message = message; + g_object_ref(message); + mmsg = (CMIMEMESSAGE *)GB.New(GB.FindClass("MimeMessage"), NULL, NULL); + _message = NULL; + } + + return mmsg; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimeMessage_new, GB_STRING contents) + + GMimeMessage *message = _message; + + if (!message) + { + if (MISSING(contents)) + { + message = g_mime_message_new(FALSE); + // Add a default part? + } + else + { + GMimeParser *parser; + GMimeStream *stream; + + /* create a stream to read from memory */ + stream = g_mime_stream_mem_new_with_buffer(STRING(contents), LENGTH(contents)); + + /* create a new parser object to parse the stream */ + parser = g_mime_parser_new_with_stream (stream); + + /* unref the stream (parser owns a ref, so this object does not actually get free'd until we destroy the parser) */ + g_object_unref(stream); + + /* parse the message from the stream */ + message = g_mime_parser_construct_message (parser NULLGM3); + + /* free the parser (and the stream) */ + g_object_unref(parser); + + if (!message) + { + GB.Error("Unable to parse message"); + return; + } + } + } + + THIS->message = message; + g_object_set_data(G_OBJECT(message), "gambas-object", THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(MimeMessage_free) + + if (MESSAGE) + { + g_object_set_data(G_OBJECT(MESSAGE), "gambas-object", NULL); + g_object_unref(MESSAGE); + } + +END_METHOD + + +BEGIN_PROPERTY(MimeMessage_Subject) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(g_mime_message_get_subject(MESSAGE)); + else + g_mime_message_set_subject(MESSAGE, GB.ToZeroString(PROP(GB_STRING)) NULLGM3); + +END_PROPERTY + + +#define IMPLEMENT_STRING_PROPERTY(_name, _func) \ +BEGIN_PROPERTY(MimeMessage_##_name) \ +\ + if (READ_PROPERTY) \ + GB.ReturnNewZeroString(g_mime_message_get_##_func(MESSAGE)); \ + else \ + g_mime_message_set_##_func(MESSAGE, GB.ToZeroString(PROP(GB_STRING))); \ +\ +END_PROPERTY + + +#define IMPLEMENT_LIST_PROPERTY(_name, _func) \ +BEGIN_PROPERTY(MimeMessage_##_name) \ +\ + InternetAddressList *list; \ + InternetAddress *addr; \ + if (READ_PROPERTY) \ + { \ + list = g_mime_message_get_##_func(MESSAGE); \ + addr = internet_address_list_get_address(list, 0); \ + GB.ReturnNewZeroString(internet_address_to_string(addr, NULL, FALSE)); \ + } \ + else \ + { \ + addr = internet_address_mailbox_new("", GB.ToZeroString(PROP(GB_STRING))); \ + list = g_mime_message_get_##_func(MESSAGE); \ + internet_address_list_set_address(list, 0, addr); \ + } \ +\ +END_PROPERTY + +#if GMIME_MAJOR_VERSION < 3 + IMPLEMENT_STRING_PROPERTY(Sender, sender) + IMPLEMENT_STRING_PROPERTY(ReplyTo, reply_to) + IMPLEMENT_STRING_PROPERTY(Id, message_id) +#else + IMPLEMENT_LIST_PROPERTY(Sender, sender) + IMPLEMENT_LIST_PROPERTY(ReplyTo, reply_to) + IMPLEMENT_STRING_PROPERTY(Id, message_id) +#endif + + +#define IMPLEMENT_RECIPIENT_PROPERTY(_name, _type, _adr_func, _string) \ +BEGIN_PROPERTY(MimeMessage_##_name) \ +\ + InternetAddressList *addr = g_mime_message_get_##_adr_func(MESSAGE, _type); \ + \ + if (READ_PROPERTY) \ + { \ + char *list = internet_address_list_to_string(addr NULLGM3, FALSE); \ + GB.ReturnNewZeroString(list); \ + g_free(list); \ + } \ + else \ + { \ + InternetAddressList *new_addr; \ + \ + internet_address_list_clear(addr); \ + \ + new_addr = internet_address_list_parse##_string(GM3NULL GB.ToZeroString(PROP(GB_STRING))); \ + internet_address_list_append(addr, new_addr); \ + g_object_unref(new_addr); \ + } \ +\ +END_PROPERTY + +#if GMIME_MAJOR_VERSION < 3 + IMPLEMENT_RECIPIENT_PROPERTY(To, GMIME_RECIPIENT_TYPE_TO, recipients, _string); + IMPLEMENT_RECIPIENT_PROPERTY(Cc, GMIME_RECIPIENT_TYPE_CC, recipients, _string); + IMPLEMENT_RECIPIENT_PROPERTY(Bcc, GMIME_RECIPIENT_TYPE_BCC, recipients, _string); +#else + IMPLEMENT_RECIPIENT_PROPERTY(To, GMIME_ADDRESS_TYPE_TO, addresses,); + IMPLEMENT_RECIPIENT_PROPERTY(Cc, GMIME_ADDRESS_TYPE_CC, addresses,); + IMPLEMENT_RECIPIENT_PROPERTY(Bcc, GMIME_ADDRESS_TYPE_BCC, addresses,); +#endif + +BEGIN_PROPERTY(MimeMessage_Part) + + if (READ_PROPERTY) + GB.ReturnObject(CMIMEPART_create(g_mime_message_get_mime_part(MESSAGE))); + else + { + CMIMEPART *mpart = VPROP(GB_OBJECT); + g_mime_message_set_mime_part(MESSAGE, mpart ? mpart->part : NULL); + } + +END_PROPERTY + +BEGIN_PROPERTY(MimeMessage_Body) + + GB.ReturnObject(CMIMEPART_create(g_mime_message_get_body(MESSAGE))); + +END_PROPERTY + +BEGIN_METHOD_VOID(MimeMessage_ToString) + + char *str = g_mime_object_to_string((GMimeObject *)MESSAGE NULLGM3); + GB.ReturnNewZeroString(str); + g_free(str); + +END_METHOD + +/*BEGIN_PROPERTY(MimeMessage_Date) + + if (READ_PROPERTY) + { + time_t time; + GB_DATE date; + + g_mime_message_get_date(MESSAGE, &time, NULL); + GB.MakeDateFromTime(time, 0, &date); + GB.ReturnDate(&date); + } + else + { + GB_DATE_SERIAL *date; + struct tm time; + + date = GB.SplitDate(PROP(GB_DATE)); + time.tm_sec = date. + } + +END_PROPERTY*/ + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimeMessage_Headers_get, GB_STRING name) + + GB.ReturnNewZeroString(g_mime_object_get_header((GMimeObject *)MESSAGE, GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(MimeMessage_Headers_put, GB_STRING value; GB_STRING name) + + if (LENGTH(name)) + g_mime_object_set_header((GMimeObject *)MESSAGE, GB.ToZeroString(ARG(name)), GB.ToZeroString(ARG(value)) NULLGM3); + else + g_mime_object_remove_header((GMimeObject *)MESSAGE, GB.ToZeroString(ARG(name))); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC MimeMessageHeadersDesc[] = +{ + GB_DECLARE_VIRTUAL(".MimeMessage.Headers"), + + GB_METHOD("_get", "s", MimeMessage_Headers_get, "(Name)s"), + GB_METHOD("_put", NULL, MimeMessage_Headers_put, "(Value)s(Name)s"), + + GB_END_DECLARE +}; + +//------------------------------------------------------------------------- + +GB_DESC MimeMessageDesc[] = +{ + GB_DECLARE("MimeMessage", sizeof(CMIMEMESSAGE)), + + GB_METHOD("_new", NULL, MimeMessage_new, "[(Contents)s]"), + GB_METHOD("_free", NULL, MimeMessage_free, NULL), + + GB_PROPERTY("Sender", "s", MimeMessage_Sender), + GB_PROPERTY("ReplyTo", "s", MimeMessage_ReplyTo), + GB_PROPERTY("To", "s", MimeMessage_To), + GB_PROPERTY("Cc", "s", MimeMessage_Cc), + GB_PROPERTY("Bcc", "s", MimeMessage_Bcc), + GB_PROPERTY("Subject", "s", MimeMessage_Subject), + //GB_PROPERTY("Date", "d", MimeMessage_Date), + GB_PROPERTY("Id", "s", MimeMessage_Id), + + GB_PROPERTY_SELF("Headers", ".MimeMessage.Headers"), + + GB_PROPERTY("Part", "MimePart", MimeMessage_Part), + GB_PROPERTY_READ("Body", "MimePart", MimeMessage_Body), + + GB_METHOD("ToString", "s", MimeMessage_ToString, NULL), + + GB_END_DECLARE +}; diff --git a/gb.mime/src/c_mimemessage.h b/gb.mime/src/c_mimemessage.h new file mode 100644 index 00000000..3b508a2b --- /dev/null +++ b/gb.mime/src/c_mimemessage.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_mimemessage.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MIMEMESSAGE_H +#define __C_MIMEMESSAGE_H + +#include "main.h" +#include "c_mimepart.h" + +#ifndef __C_MIMEMESSAGE_C +extern GB_DESC MimeMessageDesc[]; +extern GB_DESC MimeMessageHeadersDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + GMimeMessage *message; + CMIMEPART *part; + CMIMEPART *body; + } + CMIMEMESSAGE; + +CMIMEMESSAGE *CMIMEMESSAGE_create(GMimeMessage *message); + +#endif /* __MAIN_H */ diff --git a/gb.mime/src/c_mimepart.c b/gb.mime/src/c_mimepart.c new file mode 100644 index 00000000..f316d9e5 --- /dev/null +++ b/gb.mime/src/c_mimepart.c @@ -0,0 +1,459 @@ +/*************************************************************************** + + c_mimepart.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MIMEPART_C + +#include + +#include "c_mimemessage.h" +#include "c_mimepart.h" + +#define THIS ((CMIMEPART *)_object) +#define PART THIS->part +#define MPART ((GMimePart *)THIS->part) +#define MMPART ((GMimeMultipart *)THIS->part) +#define MSPART ((GMimeMessagePart *)THIS->part) + +//------------------------------------------------------------------------- + +static bool _do_not_create_part = FALSE; + +CMIMEPART *CMIMEPART_create(GMimeObject *part) +{ + CMIMEPART *mpart; + + if (!part) + return NULL; + + mpart = (CMIMEPART *)g_object_get_data(G_OBJECT(part), "gambas-object"); + if (!mpart) + { + _do_not_create_part = TRUE; + mpart = (CMIMEPART *)GB.New(GB.FindClass("MimePart"), NULL, NULL); + _do_not_create_part = FALSE; + mpart->part = part; + g_object_ref(part); + g_object_set_data(G_OBJECT(part), "gambas-object", (gpointer)mpart); + } + + return mpart; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimePart_new, GB_STRING ctype) + + GMimeObject *part; + GMimeContentType *ctype; + const char *content_type; + + if (_do_not_create_part) + return; + + if (MISSING(ctype)) + content_type = "text/plain;charset=utf-8"; + else + content_type = GB.ToZeroString(ARG(ctype)); + +#if GMIME_MAJOR_VERSION < 3 + ctype = g_mime_content_type_new_from_string(content_type); +#else + ctype = g_mime_content_type_parse(NULL, content_type); +#endif + + if (g_mime_content_type_is_type(ctype, "multipart", "*")) + part = (GMimeObject *)g_mime_multipart_new_with_subtype(g_mime_content_type_get_media_subtype(ctype)); + else if (g_mime_content_type_is_type(ctype, "message", "*")) + part = (GMimeObject *)g_mime_message_part_new(g_mime_content_type_get_media_subtype(ctype)); + else + { + part = (GMimeObject* )g_mime_part_new(); + g_mime_object_set_content_type(part, ctype); + + if (g_mime_content_type_is_type(ctype, "text", "*")) + g_mime_part_set_content_encoding((GMimePart *)part, GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE); + else + g_mime_part_set_content_encoding((GMimePart *)part, GMIME_CONTENT_ENCODING_BASE64); + } + + PART = part; + //g_object_ref(part); + g_object_set_data(G_OBJECT(part), "gambas-object", (gpointer)THIS); + +END_METHOD + +BEGIN_METHOD_VOID(MimePart_free) + + g_object_set_data(G_OBJECT(PART), "gambas-object", NULL); + g_object_unref(PART); + +END_METHOD + +#define IMPLEMENT_STRING_PROPERTY(_name, _func) \ +BEGIN_PROPERTY(MimePart_##_name) \ +\ + if (READ_PROPERTY) \ + GB.ReturnNewZeroString(g_mime_object_get_##_func(PART)); \ + else \ + g_mime_object_set_##_func(PART, GB.ToZeroString(PROP(GB_STRING))); \ +\ +END_PROPERTY + +IMPLEMENT_STRING_PROPERTY(Disposition, disposition); +IMPLEMENT_STRING_PROPERTY(ContentId, content_id); + +BEGIN_PROPERTY(MimePart_ContentType) + + GMimeContentType *ctype; + + if (READ_PROPERTY) + { + ctype = g_mime_object_get_content_type(PART); + #if GMIME_MAJOR_VERSION < 3 + char *str = g_mime_content_type_to_string(ctype); + #else + char *str = g_mime_content_type_encode(ctype, NULL); + #endif + GB.ReturnNewZeroString(str); + g_free(str); + } + else + { + #if GMIME_MAJOR_VERSION < 3 + ctype = g_mime_content_type_new_from_string(GB.ToZeroString(PROP(GB_STRING))); + #else + ctype = g_mime_content_type_parse(NULL, GB.ToZeroString(PROP(GB_STRING))); + #endif + g_mime_object_set_content_type(PART, ctype); + g_object_unref(ctype); + } + +END_PROPERTY + + +BEGIN_PROPERTY(MimePart_ContentDisposition) + + GMimeContentDisposition *cdisp; + + //if (READ_PROPERTY) + //{ + cdisp = g_mime_object_get_content_disposition(PART); + char *str; + #if GMIME_MAJOR_VERSION < 3 + str = g_mime_content_disposition_to_string(cdisp, TRUE); + #else + str = g_mime_content_disposition_encode(cdisp, NULL); + #endif + GB.ReturnNewZeroString(str); + g_free(str); + /*} + else + { + cdisp = g_mime_content_disposition_new_from_string(GB.ToZeroString(PROP(GB_STRING))); + g_mime_object_set_content_disposition(PART, cdisp); + g_object_unref(cdisp); + }*/ + +END_PROPERTY + + +BEGIN_METHOD_VOID(MimePart_ToString) + + char *str = g_mime_object_to_string(PART NULLGM3); + GB.ReturnNewZeroString(str); + g_free(str); + +END_METHOD + + +//------------------------------------------------------------------------- + +#define CHECK_PART() if (!GMIME_IS_PART(PART)) { GB.Error("Not a part"); return; } + +BEGIN_PROPERTY(MimePart_ContentEncoding) + + CHECK_PART(); + + if (READ_PROPERTY) + GB.ReturnInteger(g_mime_part_get_content_encoding(MPART)); + else + g_mime_part_set_content_encoding(MPART, VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(MimePart_Data) + + GMimeDataWrapper *content; + GMimeStream *stream; + GByteArray *array; + + if (READ_PROPERTY) + { + if (!GMIME_IS_PART(PART)) + { + GB.ReturnNull(); + return; + } + + #if GMIME_MAJOR_VERSION < 3 + content = g_mime_part_get_content_object(MPART); + #else + content = g_mime_part_get_content(MPART); + #endif + + array = g_byte_array_new(); + + /* create a new stream for writing to memory */ + stream = g_mime_stream_mem_new_with_byte_array(array); + g_mime_stream_mem_set_owner((GMimeStreamMem *)stream, TRUE); + + /* write the contents to the stream */ + g_mime_data_wrapper_write_to_stream(content, stream); + + if (!array->data) + GB.ReturnVoidString(); + else + GB.ReturnNewString((char *)array->data, (int)array->len); + + /* free the output stream */ + g_object_unref (stream); + + } + else + { + CHECK_PART(); + + /* create the parts' content stream */ + stream = g_mime_stream_mem_new_with_buffer(PSTRING(), PLENGTH()); + + /* create the content object - since the stream is not base64 + or QP encoded or anything, we can use + GMIME_CONTENT_ENCODING_DEFAULT as the encoding type (_DEFAULT + is the same as saying "nothing specified") */ + content = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT); + g_object_unref (stream); + + /* set the content object on the new mime part */ + #if GMIME_MAJOR_VERSION < 3 + g_mime_part_set_content_object(MPART, content); + #else + g_mime_part_set_content(MPART, content); + #endif + g_object_unref(content); + + /* if we want, we can tell GMime that the content should be base64 encoded when written to disk... */ + //g_mime_part_set_content_encoding (mime_part, GMIME_CONTENT_ENCODING_BASE64); + } + +END_PROPERTY + + +BEGIN_PROPERTY(MimePart_FileName) + + if (READ_PROPERTY) + { + if (GMIME_IS_PART(PART)) + GB.ReturnNewZeroString(g_mime_part_get_filename(MPART)); + else + GB.ReturnNull(); + } + else + { + CHECK_PART(); + g_mime_part_set_filename(MPART, GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(MimePart_Count) + + if (!GMIME_IS_MULTIPART(PART)) + GB.ReturnInteger(0); + else + GB.ReturnInteger(g_mime_multipart_get_count(MMPART)); + +END_PROPERTY + + +BEGIN_METHOD(MimePart_get, GB_INTEGER index) + + int count; + int index; + + if (!GMIME_IS_MULTIPART(PART)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + count = g_mime_multipart_get_count(MMPART); + index = VARG(index); + + if (index < 0 || index >= count) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CMIMEPART_create(g_mime_multipart_get_part(MMPART, index))); + +END_METHOD + + +BEGIN_METHOD_VOID(MimePart_next) + + int count; + int *index; + + if (!GMIME_IS_MULTIPART(PART)) + goto __STOP; + + count = g_mime_multipart_get_count(MMPART); + index = (int *)GB.GetEnum(); + + if (*index >= count) + goto __STOP; + + GB.ReturnObject(CMIMEPART_create(g_mime_multipart_get_part(MMPART, *index))); + (*index)++; + return; + +__STOP: + + GB.StopEnum(); + +END_METHOD + + +BEGIN_METHOD(MimePart_Add, GB_OBJECT part) + + CMIMEPART *part = VARG(part); + + if (GB.CheckObject(part)) + return; + + if (!GMIME_IS_MULTIPART(PART)) + { + GB.Error("Not a multipart"); + return; + } + + g_mime_multipart_add(MMPART, part->part); + +END_METHOD + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(MimePart_Message) + + if (READ_PROPERTY) + { + if (!GMIME_IS_MESSAGE_PART(PART)) + GB.ReturnNull(); + else + GB.ReturnObject(CMIMEMESSAGE_create(g_mime_message_part_get_message(MSPART))); + } + else + { + CMIMEMESSAGE *mmsg; + + if (!GMIME_IS_MESSAGE_PART(PART)) + { + GB.Error("Not a message part"); + return; + } + + mmsg = VPROP(GB_OBJECT); + g_mime_message_part_set_message(MSPART, mmsg ? mmsg->message : NULL); + } + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimePart_Headers_get, GB_STRING name) + + GB.ReturnNewZeroString(g_mime_object_get_header(PART, GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(MimePart_Headers_put, GB_STRING value; GB_STRING name) + + if (LENGTH(name)) + g_mime_object_set_header(PART, GB.ToZeroString(ARG(name)), GB.ToZeroString(ARG(value)) NULLGM3); + else + g_mime_object_remove_header(PART, GB.ToZeroString(ARG(name))); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC MimePartHeadersDesc[] = +{ + GB_DECLARE_VIRTUAL(".MimePart.Headers"), + + GB_METHOD("_get", "s", MimePart_Headers_get, "(Name)s"), + GB_METHOD("_put", NULL, MimePart_Headers_put, "(Value)s(Name)s"), + + GB_END_DECLARE +}; + +GB_DESC MimePartDesc[] = +{ + GB_DECLARE("MimePart", sizeof(CMIMEPART)), + + GB_METHOD("_new", NULL, MimePart_new, "[(ContentType)s]"), + GB_METHOD("_free", NULL, MimePart_free, NULL), + + GB_PROPERTY("ContentType", "s", MimePart_ContentType), + GB_PROPERTY("Disposition", "s", MimePart_Disposition), + GB_PROPERTY("ContentDisposition", "s", MimePart_ContentDisposition), + GB_PROPERTY("ContentId", "s", MimePart_ContentId), + + GB_PROPERTY_SELF("Headers", ".MimePart.Headers"), + + GB_METHOD("ToString", "s", MimePart_ToString, NULL), + + // Specific to GMimePart + + //GB_PROPERTY("ContentDescription", "s", MimePart_ContentDescription), + //GB_PROPERTY("ContentMD5", "s", MimePart_ContentMD5), + //GB_PROPERTY("ContentLocation", "s", MimePart_ContentLocation), + GB_PROPERTY("ContentEncoding", "i", MimePart_ContentEncoding), + GB_PROPERTY("FileName", "s", MimePart_FileName), + GB_PROPERTY("Data", "s", MimePart_Data), + GB_PROPERTY("Message", "MimeMessage", MimePart_Message), + + GB_PROPERTY_READ("Count", "i", MimePart_Count), + //GB_METHOD("Clear", NULL, MimePart_Clear, NULL), + GB_METHOD("_get", "MimePart", MimePart_get, "(Index)i"), + GB_METHOD("_next", "MimePart", MimePart_next, NULL), + GB_METHOD("Add", NULL, MimePart_Add, "(Part)MimePart"), + + GB_END_DECLARE +}; diff --git a/gb.mime/src/c_mimepart.h b/gb.mime/src/c_mimepart.h new file mode 100644 index 00000000..868c2e31 --- /dev/null +++ b/gb.mime/src/c_mimepart.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + c_mimepart.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MIMEPART_H +#define __C_MIMEPART_H + +#include "main.h" + +#ifndef __C_MIMEPART_C +extern GB_DESC MimePartDesc[]; +extern GB_DESC MimePartHeadersDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + GMimeObject *part; + } + CMIMEPART; + +CMIMEPART *CMIMEPART_create(GMimeObject *part); + +#endif /* __MAIN_H */ diff --git a/gb.mime/src/gb.mime.component b/gb.mime/src/gb.mime.component new file mode 100644 index 00000000..2ed93b42 --- /dev/null +++ b/gb.mime/src/gb.mime.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Stable diff --git a/gb.mime/src/main.c b/gb.mime/src/main.c new file mode 100644 index 00000000..c41d4277 --- /dev/null +++ b/gb.mime/src/main.c @@ -0,0 +1,61 @@ +/*************************************************************************** + + main.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_mime.h" +#include "c_mimemessage.h" +#include "c_mimepart.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + MimeDesc, + MimePartHeadersDesc, + MimePartDesc, + MimeMessageHeadersDesc, + MimeMessageDesc, + NULL +}; + +int EXPORT GB_INIT() +{ + g_mime_init( + #if GMIME_MAJOR_VERSION < 3 + 0 + #endif + ); + return 0; +} + + +void EXPORT GB_EXIT() +{ + g_mime_shutdown(); +} diff --git a/gb.mime/src/main.h b/gb.mime/src/main.h new file mode 100644 index 00000000..3d966d0f --- /dev/null +++ b/gb.mime/src/main.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + main.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#include +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#if GMIME_MAJOR_VERSION < 3 +#define NULLGM3 +#define GM3NULL +#else +#define NULLGM3 ,NULL +#define GM3NULL NULL, +#endif + +#endif /* __MAIN_H */ diff --git a/gb.ncurses/AUTHORS b/gb.ncurses/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/COPYING b/gb.ncurses/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.ncurses/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.ncurses/ChangeLog b/gb.ncurses/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/INSTALL b/gb.ncurses/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.ncurses/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.ncurses/Makefile.am b/gb.ncurses/Makefile.am new file mode 100644 index 00000000..57cb2965 --- /dev/null +++ b/gb.ncurses/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @NCURSES_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.ncurses/NEWS b/gb.ncurses/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/README b/gb.ncurses/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/acinclude.m4 b/gb.ncurses/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.ncurses/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.ncurses/component.am b/gb.ncurses/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.ncurses/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.ncurses/configure.ac b/gb.ncurses/configure.ac new file mode 100644 index 00000000..006c9038 --- /dev/null +++ b/gb.ncurses/configure.ac @@ -0,0 +1,33 @@ +dnl ---- configure.ac for gb.ncurses + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-ncurses],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.ncurses) +LT_INIT +PKG_PROG_PKG_CONFIG + +# Most distributions I have seen apparently provide the ncurses and panel +# pkg-config files (which may be synonymous with ncursesw and panelw for +# wide-character support). Solus only provides the *w versions. +# +# We should prefer linking with the wide-character version but still +# allow to compile on systems that don't have ncursesw pkg-config files. +$PKG_CONFIG --silence-errors --exists ncursesw panelw +if test $? -eq "0" + then gb_ncurses_pkgconfig_names="ncursesw panelw" + else gb_ncurses_pkgconfig_names="ncurses panel" +fi + +GB_COMPONENT_PKG_CONFIG( + ncurses, NCURSES, gb.ncurses, [src], + $gb_ncurses_pkgconfig_names) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.ncurses/gambas.h b/gb.ncurses/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.ncurses/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.ncurses/gb_common.h b/gb.ncurses/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.ncurses/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.ncurses/m4 b/gb.ncurses/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.ncurses/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.ncurses/reconf b/gb.ncurses/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.ncurses/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.ncurses/src/Makefile.am b/gb.ncurses/src/Makefile.am new file mode 100644 index 00000000..b9c54bd5 --- /dev/null +++ b/gb.ncurses/src/Makefile.am @@ -0,0 +1,17 @@ +COMPONENT = gb.ncurses +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.ncurses.la + +gb_ncurses_la_LIBADD = @NCURSES_LIB@ +gb_ncurses_la_LDFLAGS = -module @LD_FLAGS@ @NCURSES_LDFLAGS@ +gb_ncurses_la_CPPFLAGS = @NCURSES_INC@ + +gb_ncurses_la_SOURCES = \ + main.h main.c \ + c_window.h c_window.c \ + c_key.h c_key.c \ + c_color.h c_color.c \ + c_screen.h c_screen.c \ + c_input.h c_input.c + diff --git a/gb.ncurses/src/c_color.c b/gb.ncurses/src/c_color.c new file mode 100644 index 00000000..83d12686 --- /dev/null +++ b/gb.ncurses/src/c_color.c @@ -0,0 +1,279 @@ +/* + * c_color.c - gb.ncurses Color static class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_COLOR_C + +#include + +#include "../gambas.h" + +#include "main.h" +#include "c_screen.h" + +#define PAIR_VALID(p) (p >= 0 && p < COLOR_PAIRS) +#define COLOR_VALID(c) (c >= -1 && c < COLORS) + +static int _color; +static short colors[] = { + COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, + COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE +}; + +void COLOR_init() +{ + start_color(); + //use_default_colors(); + + /* + * Initialise all possible pairs + */ +#define ARRAY_NUM(arr) (sizeof(arr) / sizeof(arr[0])) + int i, j, n; + + for (n = 0, i = 0; i < ARRAY_NUM(colors); i++) + for (j = 0; j < ARRAY_NUM(colors); j++) + init_pair(++n, colors[i], colors[j]); +} + +/* + * Color + */ + +BEGIN_PROPERTY(Color_Available) + + GB.ReturnBoolean(has_colors()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_CanChange) + + GB.ReturnBoolean(can_change_color()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_Count) + + GB.ReturnInteger(COLORS); + +END_PROPERTY + +BEGIN_METHOD(Color_get, GB_INTEGER index) + + if (!COLOR_VALID(VARG(index))) { + GB.Error(GB_ERR_BOUND); + return; + } + _color = VARG(index); + RETURN_SELF(); + +END_METHOD + +int CCOLOR_setcolor(short index, float r, float g, float b) +{ + return init_color(index, r * 1000, g * 1000, b * 1000); +} + +BEGIN_METHOD(Color_Set, GB_INTEGER color; GB_FLOAT r; GB_FLOAT g;GB_FLOAT b) + + if (!COLOR_VALID(VARG(color))) { + GB.Error(GB_ERR_BOUND); + return; + } + CCOLOR_setcolor(VARG(color), VARG(r), VARG(g), VARG(b)); + REAL_REFRESH(); + +END_METHOD + +GB_DESC CColorDesc[] = { + GB_DECLARE("Color", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("Black", "i", COLOR_BLACK), + GB_CONSTANT("Red", "i", COLOR_RED), + GB_CONSTANT("Green", "i", COLOR_GREEN), + GB_CONSTANT("Yellow", "i", COLOR_YELLOW), + GB_CONSTANT("Blue", "i", COLOR_BLUE), + GB_CONSTANT("Magenta", "i", COLOR_MAGENTA), + GB_CONSTANT("Cyan", "i", COLOR_CYAN), + GB_CONSTANT("White", "i", COLOR_WHITE), + + GB_STATIC_PROPERTY_READ("Available", "b", Color_Available), + GB_STATIC_PROPERTY_READ("CanChange", "b", Color_CanChange), + GB_STATIC_PROPERTY_READ("Count", "i", Color_Count), + + GB_STATIC_METHOD("_get", ".ColorInfo", Color_get, "(Index)i"), + GB_STATIC_METHOD("Set", NULL, Color_Set, "(Color)i(R)i(G)i(B)i"), + + GB_END_DECLARE +}; + +/* + * .ColorInfo + */ + +enum { + COLOR_R, + COLOR_G, + COLOR_B +}; + +static int CCOLOR_content(short color, float *r, float *g, float *b) +{ + short ar, ag, ab; + + color_content(color, &ar, &ag, &ab); + if (r) + *r = (float) ar / 1000; + if (g) + *g = (float) ag / 1000; + if (b) + *b = (float) ab / 1000; + return 0; +} + +int CCOLOR_setcolor_one(short index, float val, int which) +{ + short r, g, b; + float rf, gf, bf; + + color_content(index, &r, &g, &b); + rf = ((float) r) / 1000; + gf = ((float) g) / 1000; + bf = ((float) b) / 1000; + switch (which) { + case COLOR_R: + rf = val; + break; + case COLOR_G: + gf = val; + break; + case COLOR_B: + bf = val; + break; + default: + return -1; + } + return CCOLOR_setcolor(index, rf, gf, bf); +} + +BEGIN_PROPERTY(ColorInfo_Red) + + float red; + + if (READ_PROPERTY) { + CCOLOR_content(_color, &red, NULL, NULL); + GB.ReturnFloat(red); + return; + } + CCOLOR_setcolor_one(_color, VPROP(GB_FLOAT), COLOR_R); + REAL_REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Green) + + float green; + + if (READ_PROPERTY) { + CCOLOR_content(_color, NULL, &green, NULL); + GB.ReturnFloat(green); + return; + } + CCOLOR_setcolor_one(_color, VPROP(GB_FLOAT), COLOR_G); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Blue) + + float blue; + + if (READ_PROPERTY) { + CCOLOR_content(_color, NULL, NULL, &blue); + GB.ReturnFloat(blue); + return; + } + CCOLOR_setcolor_one(_color, VPROP(GB_FLOAT), COLOR_B); + +END_PROPERTY + +GB_DESC CColorInfoDesc[] = { + GB_DECLARE(".ColorInfo", 0), + GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Red", "i", ColorInfo_Red), + GB_STATIC_PROPERTY("Green", "i", ColorInfo_Green), + GB_STATIC_PROPERTY("Blue", "i", ColorInfo_Blue), + + GB_END_DECLARE +}; + +/* + * Pair + */ + +BEGIN_PROPERTY(Pair_Count) + + GB.ReturnInteger(COLOR_PAIRS); + +END_PROPERTY + +short CPAIR_get(short fg, short bg) +{ + short i, j; + int n; + + i = j = -1; + for (n = 0; n < ARRAY_NUM(colors); n++) { + if (colors[n] == fg) + i = fg; + if (colors[n] == bg) + j = bg; + if (i != -1 && j != -1) + break; + } + if (n == ARRAY_NUM(colors)) + return -1; + /* See COLOR_init() */ + return i * ARRAY_NUM(colors) + j + 1; +} + +BEGIN_METHOD(Pair_get, GB_INTEGER fore; GB_INTEGER back) + + short pairn = CPAIR_get(VARG(fore), VARG(back)); + + if (pairn == -1) { + GB.Error(GB_ERR_BOUND); + return; + } + GB.ReturnInteger(pairn); + +END_METHOD + +GB_DESC CPairDesc[] = { + GB_DECLARE("Pair", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("Count", "i", Pair_Count), + + GB_STATIC_METHOD("_get", "i", Pair_get, "(Fore)i(Back)i"), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_color.h b/gb.ncurses/src/c_color.h new file mode 100644 index 00000000..0b7a9a44 --- /dev/null +++ b/gb.ncurses/src/c_color.h @@ -0,0 +1,13 @@ +#ifndef __C_COLOR_H +#define __C_COLOR_H + +extern GB_DESC CColorDesc[]; +extern GB_DESC CColorInfoDesc[]; +extern GB_DESC CPairDesc[]; + +extern void COLOR_init(); +extern int CCOLOR_setcolor(short index, float r, float g, float b); +extern int CCOLOR_setcolor_one(short index, float val, int which); +extern short CPAIR_get(short fg, short bg); + +#endif /* __C_COLOR_H */ diff --git a/gb.ncurses/src/c_input.c b/gb.ncurses/src/c_input.c new file mode 100644 index 00000000..0e847be0 --- /dev/null +++ b/gb.ncurses/src/c_input.c @@ -0,0 +1,632 @@ +/* + * c_input.c - gb.ncurses opaque input routines + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_INPUT_C + +#include +#include +/* +#include +#include +#include +*/ +#include +#include +#include + +#include + +#include "gambas.h" +#include "gb_common.h" + +#include "main.h" +#include "c_input.h" +#include "c_window.h" + +#define E_UNSUPP "Unsupported input mode" + +static char _watch_fd = -1; + +int INPUT_init() +{ + INPUT_watch(0); + return 0; +} + +void INPUT_exit() +{ + INPUT_watch(-1); +} + +static void INPUT_watch(int fd) +{ + if (fd == _watch_fd) + return; + + if (_watch_fd != -1) + GB.Watch(_watch_fd, GB_WATCH_NONE, NULL, 0); + _watch_fd = fd; + if (_watch_fd == -1) + return; + + GB.Watch(_watch_fd, GB_WATCH_READ, INPUT_callback, 0); +} + +static void INPUT_callback(int fd, int flag, intptr_t arg) +{ + CWINDOW_raise_read(NULL); +} + +void INPUT_mode(CSCREEN *scr, int mode) +{ + if (mode == scr->input) + return; + + switch (mode) { + case INPUT_COOKED: + nocbreak(); + break; + case INPUT_CBREAK: + cbreak(); + break; + case INPUT_RAW: + raw(); + break; + default: + GB.Error(E_UNSUPP); + return; + } + scr->input = mode; +} + +void INPUT_drain() +{ + flushinp(); +} + +static int INPUT_get_ncurses(int time_out) +{ + int ret; + + if (time_out >= 0) + timeout(time_out); + ret = getch(); + if (ret == ERR) { + /* Had a timeout, the manual doesn't define any errors to + happen for wgetch() besides NULL pointer arguments. The + only source of ERR is timeout expired. */ + if (time_out >= 0) + ret = 0; + } + + if (time_out >= 0) + timeout(-1); + return ret; +} + +int INPUT_get(int timeout) +{ + return INPUT_get_ncurses(timeout); +} + +#if 0 +BEGIN_PROPERTY(Input_IsConsole) + + int fd = NODELAY_consolefd(); + + if (fd == -1) { + GB.ReturnBoolean(FALSE); + return; + } + close(fd); + GB.ReturnBoolean(TRUE); + +END_PROPERTY + +BEGIN_PROPERTY(Input_RepeatDelay) + + if (READ_PROPERTY) { + GB.ReturnInteger(NODELAY_repeater_delay(INPUT_RETURN)); + return; + } + if (NODELAY_repeater_delay(VPROP(GB_INTEGER)) == -1) { + GB.Error("Invalid value"); + return; + } + +END_PROPERTY +#endif + +GB_DESC CInputDesc[] = { + GB_DECLARE("Input", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("NoTimeout", "i", TIMEOUT_NOTIMEOUT), + + GB_CONSTANT("Cooked", "i", INPUT_COOKED), + GB_CONSTANT("CBreak", "i", INPUT_CBREAK), + GB_CONSTANT("Raw", "i", INPUT_RAW), +#if 0 + GB_CONSTANT("NoDelay", "i", INPUT_NODELAY), + + GB_STATIC_PROPERTY_READ("IsConsole", "b", Input_IsConsole), + GB_STATIC_PROPERTY("RepeatDelay", "i", Input_RepeatDelay), +#endif + GB_END_DECLARE +}; + +#if 0 +/* + * NODELAY routines + */ +static struct { + struct { + struct termios term; + int kbmode; + void (*error_hook)(); + } old; + int fd; + unsigned short pressed; + unsigned int delay; + GB_TIMER *timer; +} no_delay; +static char _exiting_nodelay = 0; + +/** + * Init NoDelay mode + * We save old settings and prepare the TTY driver and Gambas + * Precisely: + * - (TTY driver:) + * - Save all related values + * - Set terminal to (ncurses) raw()-like input mode as base for NoDelay + * - Set keyboard to K_MEDIUMRAW mode to see key make and break codes + * - (Gambas:) + * - Install specific error hook + * - Reset repeater timer + * - Begin watching console fd + */ +static int NODELAY_init() +{ + int fd = NODELAY_consolefd(); + struct termios term; + + if (fd == -1) + return -1; + + /* TODO: implement switching between vts, need available signals to + * be sent */ + + tcgetattr(fd, &no_delay.old.term); + ioctl(fd, KDGKBMODE, &no_delay.old.kbmode); + + memcpy(&term, &no_delay.old.term, sizeof(term)); + term.c_lflag &= ~(ICANON | ECHO | ISIG); + term.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON); + /* Have no timeout per default */ + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = TIMEOUT_NOTIMEOUT; + tcsetattr(fd, TCSAFLUSH, &term); + no_delay.old.error_hook = GB.Hook(GB_HOOK_ERROR, + NODELAY_error_hook); + no_delay.fd = fd; + + no_delay.timer = NULL; + + ioctl(no_delay.fd, KDSKBMODE, K_MEDIUMRAW); + + INPUT_watch(fd); + + return 0; +} + +/** + * Cleanup NoDelay mode + * Restore old settings, see NODELAY_init() for details + * This function gets called by our error hook and the error hook is removed + * here. When removing a hook, it gets automatically called: Hence + * @_exiting_nodelay + */ +static int NODELAY_exit() +{ + if (_exiting_nodelay) + return 0; + + _exiting_nodelay = 1; + + INPUT_watch(0); + + ioctl(no_delay.fd, KDSKBMODE, no_delay.old.kbmode); + + if (no_delay.timer) + GB.Unref((void **) &no_delay.timer); + + GB.Hook(GB_HOOK_ERROR, no_delay.old.error_hook); + tcsetattr(no_delay.fd, TCSANOW, &no_delay.old.term); + + close(no_delay.fd); + + _exiting_nodelay = 0; + + return 0; +} + +/** + * The NoDelay mode error hook + * This calls the former error hook, saved by NODELAY_init() to not + * disturb any piece code + */ +static void NODELAY_error_hook() +{ + if (_exiting_nodelay) + return; + NODELAY_exit(); + no_delay.old.error_hook(); +} + +/** + * Return if the given fd can be used with console_ioctls + * @fd: file descriptor to test + * The idea was derived from "kbd" package, getfd.c, is_a_console() + */ +static inline char NODELAY_is_cons(int fd) +{ + char type; + + if (fd != -1 && isatty(fd) && ioctl(fd, KDGKBTYPE, &type) != -1 + && (type == KB_101 || type == KB_84)) + return 1; + return 0; +} + +/** + * Returns an fd that can be used with console_ioctls or -1 if none + * available + */ +static int NODELAY_consolefd() +{ + int fd; + + if (NODELAY_is_cons(0)) + return 0; + + fd = open("/dev/tty", O_RDWR); + if (fd == -1) + return -1; + if (NODELAY_is_cons(fd)) + return fd; + + close(fd); + return -1; +} + +/** + * Drain NoDelay input queue + */ +static inline void NODELAY_drain() +{ + tcflush(no_delay.fd, TCIFLUSH); +} + +/** + * Return or set the repeater delay + * @val: value to set the delay to. This value must be at least 1 or an + * error is returned. If it is REPEATER_RETURN, the current value is + * returned to the caller. + * Note that this setting affects the repeater function itself, that gets + * called in this interval to generate events and the INPUT_get_nodelay() + * function which will wait to return the amount of milliseconds if it is to + * return the pressed key. + */ +static int NODELAY_repeater_delay(int val) +{ + if (val == INPUT_RETURN) + return no_delay.delay; + if (val < 1) + return -1; + no_delay.delay = (unsigned int) val; + return 0; +} + +/** + * Post callback to insert new read event during next event loop. + * This is important because the NODELAY_repeater() gets called by the + * timer. If we raise an event from there, the event handler may destroy the + * timer we are currently in by issuing a NODELAY_change_pressed(). This has + * consequently be done outside the timer tick. + * @arg: unused + */ +static void NODELAY_post_read(intptr_t arg) +{ + WINDOW_raise_read(NULL); +} + +/** + * NoDelay mode event repeater. This function is the timer callback + * Used to insert Window_Read events if there is a key pressed + */ +static int NODELAY_repeater() +{ + MY_DEBUG(); + + + if (!no_delay.pressed) + return TRUE; + GB.Post(NODELAY_post_read, 0); + return FALSE; +} + +/* + * Return codes from NODELAY_trans_keycode() + */ +enum { + TRANS_NEED_MORE = -1, + TRANS_KEY_MIN +}; + +/* + * States of the modifier keys that we recognise + */ +enum { + MOD_NONE = 0, + MOD_SHIFT = 1, + MOD_CTRL = 2, + MOD_ALT = 4 +}; + +#define IS_BREAK(k) ((k) & 0x80) + +/** + * Translate a keycode (or a sequence) to an ncurses compatible int + * @kc: keycode to translate + * This function returns one of the above codes. TRANS_NEED_MORE means that + * the given @kc is considered part of a multi-keycode sequence (or is a + * Shift, Control, Alt), so we need more keys which are (in first case + * likely) available without waiting from the tty driver then; in the latter + * case (modifier keys), it does not count as a keypress anyway. + * Note that after a usual tty key sequence is assembled it is passed to the + * driver to try to get an escape sequence for it. If there is no escape + * seqeuence for that key, we can simply return the plain value. Otherwise + * we exploit ncurses key_defined() routine to translate the sequence to an + * int for us. This gives an int like ncurses getch() would do. + */ +static int NODELAY_trans_keycode(unsigned char kc) +{ + /* Pause/Break has the largest scancode: e1 1d 45 e1 9d c5 */ + static unsigned char codes[8]; + static int num = 0; + static int modifiers = MOD_NONE; + + struct kbentry kbe; + struct kbsentry kbs; + int mod; + + + + MY_DEBUG(); + + + +#define KEYCODE_LCTRL 0x1d +#define KEYCODE_RCTRL 0x61 +#define KEYCODE_ALT 0x38 +#define KEYCODE_LSHIFT 0x2a +#define KEYCODE_RSHIFT 0x36 + /* Modifiers */ + switch (kc) { + case KEYCODE_LCTRL: + case KEYCODE_RCTRL: + mod = MOD_CTRL; + goto apply_mod; + case KEYCODE_ALT: + mod = MOD_ALT; + goto apply_mod; + case KEYCODE_LSHIFT: + case KEYCODE_RSHIFT: + mod = MOD_SHIFT; + goto apply_mod; + default: + goto account_key; + } +apply_mod: + if (IS_BREAK(kc)) + modifiers &= ~mod; + else + modifiers |= mod; + return TRANS_NEED_MORE; + +account_key: + codes[num++] = kc; + /* Break key, sends make and break code together */ + if (codes[0] == '\xe1') { + if (num == 6) + return KEY_BREAK; + else + return TRANS_NEED_MORE; + } + /* Keys with two keycodes, no matter, those correspond to + * single-keycode keys, we can safely use @kc != 0xe0 */ + if (codes[0] == '\xe0' && num != 2) + return TRANS_NEED_MORE; + + /* TODO: what to do with ctrl- ? */ + + /* Set table and get action code */ + if (modifiers & MOD_ALT) { + if (modifiers & MOD_SHIFT) + kbe.kb_table = K_ALTSHIFTTAB; + else + kbe.kb_table = K_ALTTAB; + } else if (modifiers & MOD_SHIFT) { + kbe.kb_table = K_SHIFTTAB; + } else { + kbe.kb_table = K_NORMTAB; + } + kbe.kb_index = kc & 0x7f; + kbe.kb_value = 0; + ioctl(no_delay.fd, KDGKBENT, &kbe); + /* Has an escape sequence? */ + kbs.kb_func = (unsigned char) kbe.kb_value; + ioctl(no_delay.fd, KDGKBSENT, &kbs); + if (kbs.kb_string[0]) + return key_defined((char *) kbs.kb_string); + else + return (int) ((unsigned char) kbe.kb_value); +} + +/** + * Change the currently pressed key + * @key: current key. 0 means that no key is pressed + * This installs the repeater + */ +static void NODELAY_change_pressed(int key) +{ + + + MY_DEBUG(); + + + +/* if (key == no_delay.pressed) + return; + if (key == 0) { + GB.Unref((void **) no_delay.timer); + } else { + no_delay.timer = GB.Every(no_delay.delay, + (GB_TIMER_CALLBACK) NODELAY_repeater, 0); + } +*/ no_delay.pressed = key; + //NODELAY_repeater(); + WINDOW_raise_read(NULL); +} + +/** + * Retrieve input in NoDelay mode, using console_ioctl(4). + * If there is a key pressed and no input available on the input queue, + * the pressed key is returned. + * @timeout: timeout in milliseconds. If that timeout expires, we return 0. + * Note that @timeout can only be in the scope of deciseconds and is + * silently cut down to those. + * The Repeater Delay applies to this function, too, in that we only return + * a pressed key when we waited for that delay. + * Note carefully, that there is an emergency exit: pressing ESC thrice + * during one second will immediately abort NoDelay mode and enter CBreak. + */ +static int NODELAY_get(int timeout) +{ + static char esc = 0; + struct termios old, new; + unsigned char b; + static time_t stamp; + int ret, res, num; + int key; /* This will already be ncurses compatible */ + struct timeval tv1, tv2; + + + + + MY_DEBUG(); + + + + if (timeout > -1) { + gettimeofday(&tv1, NULL); + tcgetattr(no_delay.fd, &old); + memcpy(&new, &old, sizeof(new)); + } + +recalc_timeout: +#define USEC2MSEC(us) (us / 1000) +#define MSEC2USEC(ms) (ms * 1000) +#define MSEC2DSEC(ms) (ms / 100) +#define DSEC2MSEC(ds) (ds * 100) +#define SEC2MSEC(s) (s * 1000) +#define MSEC2SEC(ms) (ms / 1000) + + if (timeout > -1) { + gettimeofday(&tv2, NULL); + timeout -= USEC2MSEC(tv2.tv_usec - tv1.tv_usec); + timeout -= SEC2MSEC(tv2.tv_sec - tv1.tv_sec); + if (timeout < 0) { + ret = 0; + goto cleanup; + } + } + + /* Set timeout */ + ioctl(no_delay.fd, TIOCINQ, &num); + /* We don't need to set any timeout if there are bytes available */ + if (!num && timeout > -1) { + new.c_cc[VTIME] = MSEC2DSEC(timeout); + tcsetattr(no_delay.fd, TCSANOW, &new); + gettimeofday(&tv1, NULL); + } + + /* Begin reading */ + if (no_delay.pressed && !num) { + usleep(MSEC2USEC(no_delay.delay)); + ret = no_delay.pressed; + goto cleanup; + } + /* Try to stick to the user-supplied timeout */ + ioctl(no_delay.fd, TIOCINQ, &num); + if ((res = read(no_delay.fd, &b, 1)) == -1 && errno == EINTR) { + goto recalc_timeout; + } else if (res == 0) { /* Timeout expired */ + ret = 0; + goto cleanup; + } else { /* Got a key */ + /* Emergency exit from NoDelay mode */ +#define KEYCODE_ESC 0x01 + if (b == KEYCODE_ESC) { + if (time(NULL) - stamp > 0) + esc = 0; + if (++esc == 3) { + NODELAY_exit(); + INPUT_mode(INPUT_CBREAK); + return 0; + } + stamp = time(NULL); + } + /* We use ncurses keys for operations on our no_delay data */ + if ((key = NODELAY_trans_keycode(b)) == TRANS_NEED_MORE) + goto recalc_timeout; + /* Ignore break codes, except when it is the currently + pressed key */ + if (IS_BREAK(b)) { + if (no_delay.pressed == key) +// NODELAY_change_pressed(0); + /* Key release isn't visible to the gambas programmer + * and thus not really an event to gb.ncurses. If + * time is left, we try again reading another key */ + goto recalc_timeout; + } else { +// NODELAY_change_pressed(key); + } + } + + ret = key; + +cleanup: + if (timeout > -1) + tcsetattr(no_delay.fd, TCSANOW, &old); + return ret; +} +#endif diff --git a/gb.ncurses/src/c_input.h b/gb.ncurses/src/c_input.h new file mode 100644 index 00000000..839c0b3e --- /dev/null +++ b/gb.ncurses/src/c_input.h @@ -0,0 +1,63 @@ +/* + * c_input.h - gb.ncurses opaque input routines for use of either ncurses or + * the terminal driver directly + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_INPUT_H +#define __C_INPUT_H + +#include "c_screen.h" + +enum { + /* Return the current mode */ + INPUT_RETURN = -1, + /* Line discipline, signal generation */ + INPUT_COOKED, + /* No line discipline, signal generation */ + INPUT_CBREAK, + /* No line discipline, no signal generation */ + INPUT_RAW, +#if 0 + /* Use terminal driver, enabled to use raw scancodes + (which are internally converted to ncurses keys but enable to + distinguish key press and release) */ + INPUT_NODELAY +#endif +}; + +enum { + TIMEOUT_NOTIMEOUT = -1 +}; + +#ifndef __C_INPUT_C +extern GB_DESC CInputDesc[]; +#endif + +int INPUT_init(); +void INPUT_exit(); +void INPUT_mode(CSCREEN *scr, int mode); +int INPUT_get(int); +void INPUT_drain(); +#ifdef __C_INPUT_C +static void INPUT_watch(int); +static void INPUT_callback(int, int, intptr_t); +#endif + +#endif /* __C_INPUT_H */ diff --git a/gb.ncurses/src/c_key.c b/gb.ncurses/src/c_key.c new file mode 100644 index 00000000..11a78b56 --- /dev/null +++ b/gb.ncurses/src/c_key.c @@ -0,0 +1,76 @@ +/* + * c_key.c - gb.ncurses Key static class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_KEY_C + +#include + +#include "../gambas.h" + +#include "main.h" +#include "c_key.h" + +BEGIN_METHOD(Key_get, GB_STRING key) + + GB.ReturnInteger((int) *STRING(key)); + +END_METHOD + +GB_DESC CKeyDesc[] = { + GB_DECLARE("Key", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("Return", "i", (int) '\n'), + GB_CONSTANT("Esc", "i", (int) '\x1b'), + + GB_CONSTANT("Break", "i", KEY_BREAK), + GB_CONSTANT("Home", "i", KEY_HOME), + GB_CONSTANT("Backspace", "i", KEY_BACKSPACE), + GB_CONSTANT("PageUp", "i", KEY_PPAGE), + GB_CONSTANT("PageDown", "i", KEY_NPAGE), + GB_CONSTANT("Enter", "i", KEY_ENTER), + + /* Arrow Keys */ + GB_CONSTANT("Left", "i", KEY_LEFT), + GB_CONSTANT("Right", "i", KEY_RIGHT), + GB_CONSTANT("Up", "i", KEY_UP), + GB_CONSTANT("Down", "i", KEY_DOWN), + + /* F keys */ + GB_CONSTANT("F1", "i", KEY_F(1)), + GB_CONSTANT("F2", "i", KEY_F(2)), + GB_CONSTANT("F3", "i", KEY_F(3)), + GB_CONSTANT("F4", "i", KEY_F(4)), + GB_CONSTANT("F5", "i", KEY_F(5)), + GB_CONSTANT("F6", "i", KEY_F(6)), + GB_CONSTANT("F7", "i", KEY_F(7)), + GB_CONSTANT("F8", "i", KEY_F(8)), + GB_CONSTANT("F9", "i", KEY_F(9)), + GB_CONSTANT("F10", "i", KEY_F(10)), + GB_CONSTANT("F11", "i", KEY_F(11)), + GB_CONSTANT("F12", "i", KEY_F(12)), + + /* ncurses.h is full of other ones. Just tell me what you need. */ + + GB_STATIC_METHOD("_get", "i", Key_get, "(Key)s"), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_key.h b/gb.ncurses/src/c_key.h new file mode 100644 index 00000000..c32b2b3e --- /dev/null +++ b/gb.ncurses/src/c_key.h @@ -0,0 +1,29 @@ +/* + * c_key.h - gb.ncurses Key static class + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_KEY_H +#define __C_KEY_H + +#ifndef __C_KEY_C +extern GB_DESC CKeyDesc[]; +#endif + +#endif /* __C_KEY_H */ diff --git a/gb.ncurses/src/c_screen.c b/gb.ncurses/src/c_screen.c new file mode 100644 index 00000000..7ba90ccd --- /dev/null +++ b/gb.ncurses/src/c_screen.c @@ -0,0 +1,293 @@ +/* + * c_screen.c - gb.ncurses Screen class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_SCREEN_C + +#include +#include +#include + +#include +#include + +#include "gambas.h" + +#include "main.h" +#include "c_screen.h" +#include "c_input.h" + +#define THIS _active + +#define E_UNSUPP "Unsupported value" + +static CSCREEN *_active; + +GB_SIGNAL_CALLBACK *_sigwinch_cb; + +DECLARE_EVENT(EVENT_Resize); + +static void SCREEN_sigwinch(int signum, intptr_t data) +{ + /* TODO: ncurses may be configured to provide its own SIGWINCH + * handler. See resizeterm(3X). */ + if (signum == SIGWINCH) + GB.Raise(_active, EVENT_Resize, 0); +} + +int SCREEN_init() +{ + _sigwinch_cb = GB.Signal.Register(SIGWINCH, SCREEN_sigwinch, + (intptr_t) NULL); + return 0; +} + +void SCREEN_exit() +{ + GB.Signal.Unregister(SIGWINCH, _sigwinch_cb); +} + +void SCREEN_refresh() +{ + if (!NCURSES_RUNNING) + return; + if (THIS->suspended) + return; + update_panels(); + doupdate(); +} + +BEGIN_METHOD_VOID(Screen_init) + + _active = GB.AutoCreate(GB.FindClass("Screen"), 0); + +END_METHOD + +static int CSCREEN_cursor(CSCREEN *scr, int mode) +{ + if (mode >= 0 && mode <= 2) + curs_set(mode); + else + return -1; + scr->cursor = mode; + return 0; +} + +static void CSCREEN_echo(CSCREEN *scr, int mode) +{ + if (mode) + echo(); + else + noecho(); + scr->echo = mode; +} + +BEGIN_METHOD_VOID(Screen_new) + + THIS->suspended = 0; + CSCREEN_cursor(THIS, 1); + CSCREEN_echo(THIS, 1); + INPUT_mode(THIS, INPUT_CBREAK); + +END_METHOD + +#if 0 +BEGIN_METHOD(Screen_new, GB_STRING termpath) + + char path[LENGTH(termpath) + 1]; + FILE *finout; + + strncpy(path, STRING(termpath), LENGTH(termpath)); + path[LENGTH(termpath)] = 0; + + finout = fopen(path, "r+"); + if (!finout) { + GB.Error(NULL); + return; + } + assert(_maxscreen < MAX_SCREENS); + _screens[_maxscreen] = THIS; + THIS->index = _curscreen = _maxscreen++; + THIS->screen = newterm(NULL, finout, finout); + set_term(THIS->screen); + THIS->finout = finout; + THIS->buffered = 1; + CSCREEN_cursor(THIS, 0); + CSCREEN_echo(THIS, 0); + INPUT_mode(THIS, INPUT_CBREAK); + refresh(); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_free) + + void *dst, *src; + + if (THIS->index != _maxscreen - 1) { + dst = &_screens[THIS->index]; + src = dst + sizeof(_screens[0]); + memmove(dst, src, _maxscreen - THIS->index); + } + _screens[THIS->index] = NULL; + if (_curscreen) + _curscreen--; + _maxscreen--; + endwin(); + fclose(THIS->finout); + delscreen(THIS->screen); + if (!_maxscreen) { + SCREEN_exit(); + return; + } + set_term(_active->screen); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Screen_Beep) + + beep(); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_Flash) + + flash(); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_Refresh) + + SCREEN_refresh(); + +END_METHOD + +BEGIN_METHOD(Screen_Resize, GB_INTEGER lines; GB_INTEGER cols) + + resizeterm(VARG(lines), VARG(cols)); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_Suspend) + + THIS->suspended = 1; + def_prog_mode(); + endwin(); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_Resume) + + THIS->suspended = 0; + doupdate(); + +END_METHOD + +GB_DESC CCursorDesc[] = { + GB_DECLARE("Cursor", 0), + GB_NOT_CREATABLE(), + + /* According to curs_set */ + GB_CONSTANT("Hidden", "i", 0), + GB_CONSTANT("Visible", "i", 1), + GB_CONSTANT("VeryVisible", "i", 2), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(Screen_Cursor) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->cursor); + return; + } + + if (CSCREEN_cursor(THIS, VPROP(GB_INTEGER)) == -1) + GB.Error(E_UNSUPP); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Echo) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->echo); + return; + } + CSCREEN_echo(THIS, !!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Input) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->input); + return; + } + INPUT_mode(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Lines) + + if (READ_PROPERTY) { + GB.ReturnInteger(LINES); + return; + } + resizeterm(VPROP(GB_INTEGER), COLS); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Cols) + + if (READ_PROPERTY) { + GB.ReturnInteger(COLS); + return; + } + resizeterm(LINES, VPROP(GB_INTEGER)); + +END_PROPERTY + +GB_DESC CScreenDesc[] = { + GB_DECLARE("Screen", sizeof(CSCREEN)), + GB_AUTO_CREATABLE(), + GB_NOT_CREATABLE(), + + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_STATIC_METHOD("_init", NULL, Screen_init, NULL), + GB_STATIC_METHOD("Beep", NULL, Screen_Beep, NULL), + GB_STATIC_METHOD("Flash", NULL, Screen_Flash, NULL), + GB_STATIC_METHOD("Refresh", NULL, Screen_Refresh, NULL), + GB_STATIC_METHOD("Resize", NULL, Screen_Resize, "(Lines)i(Cols)i"), + + GB_STATIC_METHOD("Suspend", NULL, Screen_Suspend, NULL), + GB_STATIC_METHOD("Resume", NULL, Screen_Resume, NULL), + + GB_STATIC_PROPERTY("Cursor", "i", Screen_Cursor), + GB_STATIC_PROPERTY("Echo", "b", Screen_Echo), + GB_STATIC_PROPERTY("Input", "i", Screen_Input), + + GB_STATIC_PROPERTY("Height", "i", Screen_Lines), + GB_STATIC_PROPERTY("H", "i", Screen_Lines), + GB_STATIC_PROPERTY("Width", "i", Screen_Cols), + GB_STATIC_PROPERTY("W", "i", Screen_Cols), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_screen.h b/gb.ncurses/src/c_screen.h new file mode 100644 index 00000000..a3309721 --- /dev/null +++ b/gb.ncurses/src/c_screen.h @@ -0,0 +1,50 @@ +/* + * c_screen.h - gb.ncurses Screen class + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "gambas.h" +#include "c_input.h" + +#ifndef __C_SCREEN_H +#define __C_SCREEN_H + +/* This will produce final output on terminal screen */ +#define REAL_REFRESH() SCREEN_refresh() + +#ifndef __C_SCREEN_C +extern GB_DESC CScreenDesc[]; +extern GB_DESC CCursorDesc[]; +#endif + +typedef struct { + GB_BASE ob; + int index; + int echo; + int cursor; + int input; + int buffered; + int suspended; +} CSCREEN; + +int SCREEN_init(); +void SCREEN_exit(); +void SCREEN_refresh(); + +#endif /* __C_SCREEN_H */ diff --git a/gb.ncurses/src/c_window.c b/gb.ncurses/src/c_window.c new file mode 100644 index 00000000..ffb581f8 --- /dev/null +++ b/gb.ncurses/src/c_window.c @@ -0,0 +1,944 @@ +/* + * c_window.c - gb.ncurses Window class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_WINDOW_C + +#include +#include + +#include +#include + +#include "gambas.h" + +#include "main.h" +#include "c_window.h" +#include "c_screen.h" +#include "c_color.h" + +#define THIS ((CWINDOW *) _object) + +#define MAKE_THING(snip, win, a, b) \ +do { \ + if ((a) == -1) \ + a = get##snip##x(win); \ + if ((b) == -1) \ + b = get##snip##y(win); \ +} while (0) + +#define MAKE_CURSOR(win, x, y) MAKE_THING(cur, win, x, y) +#define MAKE_COORDS(win, x, y) MAKE_THING(beg, win, x, y) +#define MAKE_DIM(win, x, y) MAKE_THING(max, win, w, h) + +#define CHECK_RAISE_THING(snip, win, a, b) \ +if ((a) < 0 || (a) > get##snip##x(win) \ + || (b) < 0 || (b) > get##snip##y(win)) { /* >= ? */ \ + GB.Error(GB_ERR_BOUND); \ + return; \ +} + +#define CHECK_RAISE_COORDS(win, x, y) CHECK_RAISE_THING(max, win, x, y) +#define CHECK_RAISE_DIM(win, x, y) CHECK_RAISE_THING(max, win, x, y) + +static CWINDOW *_curwin = NULL; + +DECLARE_EVENT(EVENT_Read); + +/* This macro is mostly called by Gambas implementation functions to request + * output on screen (read: to check if the output is buffered and if not + * produce output by means of REAL_REFRESH()) */ +#define REFRESH() CWINDOW_refresh(THIS) + +static void CWINDOW_refresh(CWINDOW *win) +{ + if (win->buffered) + return; + REAL_REFRESH(); +} + +void CWINDOW_raise_read(CWINDOW *win) +{ + if (!win) { + if (!_curwin) + return; + win = _curwin; + } + + if (GB.CanRaise(win, EVENT_Read)) + GB.Raise(win, EVENT_Read, 0); +} + +static void CWINDOW_from_ncurses(CWINDOW *win, WINDOW *ncwin, bool border) +{ + win->main = ncwin; + win->pan = new_panel(ncwin); + keypad(ncwin, TRUE); + win->has_border = border; + win->border = 0; + win->buffered = 0; + win->wrap = 1; + if (border) { + WINDOW *new = derwin(ncwin, getmaxy(ncwin) - 2, + getmaxx(ncwin) - 2, + 1, 1); + syncok(new, TRUE); + win->content = new; + } else { + win->content = win->main; + } + win->caption = NULL; +} + +BEGIN_METHOD(Window_new, GB_BOOLEAN out_border; GB_INTEGER x; GB_INTEGER y; + GB_INTEGER w; GB_INTEGER h) + + WINDOW *new; + int w, h; + + w = VARGOPT(w, COLS); + h = VARGOPT(h, LINES); + if (VARGOPT(out_border, 1)) { + w = MIN(w + 2, COLS); + h = MIN(h + 2, LINES); + } + new = newwin(h, w, VARGOPT(y, 0), VARGOPT(x, 0)); + CWINDOW_from_ncurses(THIS, new, VARGOPT(out_border, 1)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_free) + + if (_curwin == THIS) { + _curwin = NULL; + INPUT_exit(); + } + del_panel(THIS->pan); + if (THIS->has_border) + delwin(THIS->content); + delwin(THIS->main); + if (THIS->caption) + GB.FreeString(&THIS->caption); + REAL_REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_get, GB_INTEGER y; GB_INTEGER x) + + THIS->pos.line = VARG(y); + THIS->pos.col = VARG(x); + RETURN_SELF(); + +END_METHOD + +/**G + * Wait until the user typed one of the characters listed in Opts or until + * they typed wrong Tries times (infinity if not given). + * + * If there is an uppercase letter in the Opts string and the user hits + * Return, the first uppercase letter found in Opts is taken as the default + * value and the function returns. The case of letters does not matter in + * any other regard (this function is case-insensitive). + * + * This function returns the typed character (always in lowercase!). + **/ +BEGIN_METHOD(Window_Ask, GB_STRING opts; GB_INTEGER tries) + + int t, ch, i; + char *o, c; + + t = VARGOPT(tries, -1); + o = STRING(opts); + + while (t--) { + ch = INPUT_get(TIMEOUT_NOTIMEOUT); + /* XXX: not meant for ncurses special keys */ + if (ch > 127) + continue; + if (ch == '\n') + for (i = 0; i < LENGTH(opts); i++) + if (isupper(o[i])) + goto found; + for (i = 0; i < LENGTH(opts); i++) + if (tolower(o[i]) == (char) ch) + goto found; + } + GB.ReturnNull(); + return; + +found: + c = tolower(o[i]); + GB.ReturnNewString(&c, 1); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Lower) + + bottom_panel(THIS->pan); + REFRESH(); + +END_METHOD + +static void CWINDOW_clear(CWINDOW *win) +{ + werase(win->content); +} + +BEGIN_METHOD_VOID(Window_Clear) + + CWINDOW_clear(THIS); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Drain) + + INPUT_drain(); + +END_METHOD + +BEGIN_METHOD(Window_DrawHLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; + GB_STRING ch) + + mvwhline(THIS->content, VARG(y), VARG(x), *STRING(ch), VARG(len)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_DrawVLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; + GB_STRING ch) + + mvwvline(THIS->content, VARG(y), VARG(x), *STRING(ch), VARG(len)); + REFRESH(); + +END_METHOD + +static void CWINDOW_get(CWINDOW *win, int x, int y, unsigned int len, + char **ret) +{ + char *buf; + + MAKE_COORDS(win->content, x, y); + CHECK_RAISE_COORDS(win->content, x, y); + if (len == -1) + len = getmaxx(win->content) - getcurx(win->content); + len = MIN(len, (getmaxy(win->content) - getcury(win->content)) * + getmaxx(win->content) - getcurx(win->content) - 1); + + GB.Alloc((void **) &buf, len + 1); + len = mvwinnstr(win->content, y, x, buf, len); + if (len != ERR) + buf[len] = 0; + else + GB.Free((void **) &buf); + *ret = buf; +} + +BEGIN_METHOD(Window_Get, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len) + + int len; + char *ret; + + len = VARGOPT(len, -1); + CWINDOW_get(THIS, VARG(x), VARG(y), len, &ret); + GB.ReturnNewZeroString(ret); + GB.Free((void **) &ret); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Hide) + + hide_panel(THIS->pan); + REFRESH(); + +END_METHOD + +static void CWINDOW_locate(CWINDOW *win, int x, int y) +{ + MAKE_CURSOR(win->content, x, y); + CHECK_RAISE_COORDS(win->content, x, y); + wmove(win->content, y, x); +} + +BEGIN_METHOD(Window_Locate, GB_INTEGER x; GB_INTEGER y) + + CWINDOW_locate(THIS, VARG(x), VARG(y)); + REFRESH(); + +END_METHOD + +static void CWINDOW_move(CWINDOW *win, int x, int y) +{ + MAKE_COORDS(win->main, x, y); + CHECK_RAISE_COORDS(stdscr, x, y); + + move_panel(win->pan, y, x); +#if 0 + if (HAS_BORDER) { + mvwin(THIS->content, y + 1, x + 1); + WINDOW_draw_border(THIS, 1); + } +#endif +} + +BEGIN_METHOD(Window_Move, GB_INTEGER x; GB_INTEGER y) + + CWINDOW_move(THIS, VARGOPT(x, -1), VARGOPT(y, -1)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Center) + + int x, y; + + x = (COLS - getmaxx(THIS->main)) / 2; + y = (LINES - getmaxy(THIS->main)) / 2; + CWINDOW_move(THIS, x, y); + REFRESH(); + +END_METHOD + +static void CWINDOW_print(CWINDOW *win, char *str, int x, int y, + attr_t attr, int pair) +{ + int width; + char *p, *q; + attr_t asave; short psave; + + wattr_get(win->content, &asave, &psave, NULL); + if (attr == -1) + attr = asave; + if (pair == -1) + pair = psave; + wattr_set(win->content, attr, pair, NULL); + + p = str; + do { + CWINDOW_locate(win, x, y); + width = strlen(p); + if (!win->wrap) + width = MIN(width, getmaxx(win->content) - x); + + /* waddnstr, being subsequent calls to waddch, rests at the + * end of the current line but we want to go to the next + * one. */ + width = MIN(width, getmaxx(win->content) * + (getmaxy(win->content) - y) - x); + if ((q = strchr(p, '\n'))) + width = MIN(width, q - p); + waddnstr(win->content, p, width); + p += width; + x = getcurx(win->content); + y = getcury(win->content); + if (y == getmaxy(win->content) - 1) + break; + if (*p == '\n') { + y++; + p++; + } + if (*p) + x = 0; + } while (*p); + CWINDOW_locate(win, x, y); + wattr_set(win->content, asave, psave, NULL); +} + +BEGIN_METHOD(Window_Print, GB_STRING text; GB_INTEGER x; GB_INTEGER y; + GB_INTEGER attr; GB_INTEGER pair) + + char text[LENGTH(text) + 1]; + + strncpy(text, STRING(text), LENGTH(text)); + text[LENGTH(text)] = 0; + CWINDOW_print(THIS, text, VARGOPT(x, -1), VARGOPT(y, -1), + VARGOPT(attr, -1), VARGOPT(pair, -1)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Raise) + + top_panel(THIS->pan); + REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_PrintCenter, GB_STRING text; GB_INTEGER attr; + GB_INTEGER pair) + + int lines = 1; + int x, y; + char text[LENGTH(text) + 1]; + char *p, *q; + attr_t attr = VARGOPT(attr, -1); + short pair = VARGOPT(pair, -1); + + memcpy(text, STRING(text), LENGTH(text)); + text[LENGTH(text)] = 0; + p = text; + while ((q = strchr(p, '\n'))) { + lines++; + p = q + 1; + } + + p = text; + y = (getmaxy(THIS->content) - lines) / 2; + while (lines--) { + if (!lines) { + x = (getmaxx(THIS->content) - strlen(p)) / 2; + CWINDOW_print(THIS, p, x, y, attr, pair); + } else { + q = strchr(p, '\n'); + if (q == p + 1) { + y++; + continue; + } + *q = 0; + x = (getmaxx(THIS->content) - (q - p)) / 2; + CWINDOW_print(THIS, p, x, y, attr, pair); + y++; + p = q + 1; + *q = '\n'; + } + } + REFRESH(); + +END_METHOD + +static void CWINDOW_draw_border(CWINDOW *win) +{ + switch (win->border) { + case BORDER_NONE: + wborder(win->main, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); + break; + case BORDER_ASCII: + wborder(win->main, '|', '|', '-', '-', '+', '+', '+', '+'); + break; + case BORDER_ACS: + box(win->main, 0, 0); + break; + } + if (win->border != BORDER_NONE && win->caption) { + int width = getmaxx(win->main) - 2; + + width = MIN(width, strlen(win->caption)); + mvwaddnstr(win->main, 0, 1, win->caption, width); + } +} + +static void CWINDOW_resize(CWINDOW *win, int w, int h) +{ + int x, y; + + MAKE_DIM(win->main, w, h); + + getbegyx(win->main, y, x); + if (win->has_border) { + w += 2; + h += 2; + } + /* XXX: Not rather raise an error? */ + w = MIN(w, COLS - x); + h = MIN(h, LINES - y); + + /* TODO: With the auto-created Window it does not work properly in + * Screen_Resize() from within xterm (my testcase) */ + if (win->border) + wborder(win->main, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); + wresize(win->main, h, w); + if (win->has_border) + wresize(win->content, h - 2, w - 2); + replace_panel(win->pan, win->main); + CWINDOW_draw_border(win); +} + +BEGIN_METHOD(Window_Resize, GB_INTEGER w; GB_INTEGER h) + + CWINDOW_resize(THIS, VARGOPT(w, -1), VARGOPT(h, -1)); + REAL_REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_SetFullscreen) + + CWINDOW_move(THIS, 0, 0); + CWINDOW_resize(THIS, COLS, LINES); + REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_Read, GB_INTEGER timeout) + + int t; + int ret; + + t = VARGOPT(timeout, -1); + + ret = INPUT_get(t); + GB.ReturnInteger(ret); + +END_METHOD + +BEGIN_METHOD_VOID(Window_ReadLine) + + char line[256]; /* XXX: Must be enough! */ + int ret; + + bzero(line, sizeof(line)); + ret = wgetnstr(THIS->main, line, sizeof(line) - 1); + if (ret == ERR) { + GB.ReturnNull(); + return; + } + GB.ReturnNewZeroString(line); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Show) + + show_panel(THIS->pan); + REFRESH(); + +END_METHOD + +static void CWINDOW_setfocus(CWINDOW *win) +{ + if (!_curwin) + INPUT_init(); + _curwin = win; +} + +BEGIN_METHOD_VOID(Window_SetFocus) + + CWINDOW_setfocus(THIS); + +END_METHOD + +BEGIN_PROPERTY(Window_Attributes) + + if (READ_PROPERTY) { + attr_t attr = 0; + short pair; + + wattr_get(THIS->content, &attr, &pair, NULL); + GB.ReturnInteger(attr); + return; + } + wattrset(THIS->content, VPROP(GB_INTEGER)); + +END_PROPERTY + +/* This is *not* a shortcut to setting Window.{Fore,Back}ground. The latter + * two set the colour of everything in the window. This function only sets + * the colour pair for all subsequent writes! */ +BEGIN_PROPERTY(Window_Pair) + + chtype bkgd; + + bkgd = getbkgd(THIS->content); + + if (READ_PROPERTY) { + GB.ReturnInteger(PAIR_NUMBER(bkgd & A_COLOR)); + return; + } + bkgd = COLOR_PAIR(VPROP(GB_INTEGER)); + wbkgdset(THIS->content, bkgd); + +END_PROPERTY + +#define COLOR_METHOD(r, f, b) \ +short pair = 0, fg, bg; \ +attr_t attr; \ + \ +wattr_get(THIS->content, &attr, &pair, NULL); \ +pair_content(pair, &fg, &bg); \ +if (READ_PROPERTY) { \ + GB.ReturnInteger(r); \ + return; \ +} \ +pair = CPAIR_get(f, b); \ +if (pair == -1) { \ + GB.Error(GB_ERR_BOUND); \ + return; \ +} \ +wbkgd(THIS->content, COLOR_PAIR(pair) | ' '); \ +REFRESH(); + +/* + * One should be careful when using these two properties. wbkgd() changes + * fore- *and* background of *all* characters in the window. So setting + * the background may result in resetting even the foreground of differently + * coloured characters. + * + * Best is to only set Background/Foreground once at the beginning of the + * program when using colours extensively during the program. + */ + +BEGIN_PROPERTY(Window_Background) + + COLOR_METHOD(bg, fg, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Foreground) + + COLOR_METHOD(fg, VPROP(GB_INTEGER), bg); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Border) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->border); + return; + } + + THIS->border = VPROP(GB_INTEGER); + CWINDOW_draw_border(THIS); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Buffered) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->buffered); + return; + } + THIS->buffered = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Caption) + + if (READ_PROPERTY) { + GB.ReturnString(THIS->caption); + return; + } + + if (THIS->caption) + GB.FreeString(&THIS->caption); + THIS->caption = GB.NewZeroString(PSTRING()); + CWINDOW_draw_border(THIS); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_CursorX) + + if (READ_PROPERTY) { + GB.ReturnInteger(getcurx(THIS->content)); + return; + } + CWINDOW_locate(THIS, VPROP(GB_INTEGER), -1); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_CursorY) + + if (READ_PROPERTY) { + GB.ReturnInteger(getcury(THIS->content)); + return; + } + CWINDOW_locate(THIS, -1, VPROP(GB_INTEGER)); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Height) + + if (READ_PROPERTY) { + GB.ReturnInteger(getmaxy(THIS->content)); + return; + } + CWINDOW_resize(THIS, -1, VPROP(GB_INTEGER)); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Wrap) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->wrap); + return; + } + THIS->wrap = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Width) + + if (READ_PROPERTY) { + GB.ReturnInteger(getmaxx(THIS->content)); + return; + } + CWINDOW_resize(THIS, VPROP(GB_INTEGER), -1); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_X) + + if (READ_PROPERTY) { + GB.ReturnInteger(getbegx(THIS->main)); + return; + } + CWINDOW_move(THIS, VPROP(GB_INTEGER), -1); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Y) + + if (READ_PROPERTY) { + GB.ReturnInteger(getbegy(THIS->main)); + return; + } + CWINDOW_move(THIS, -1, VPROP(GB_INTEGER)); + REFRESH(); + +END_PROPERTY + +GB_DESC CWindowDesc[] = { + GB_DECLARE("Window", sizeof(CWINDOW)), + GB_AUTO_CREATABLE(), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + + /* Methods */ + GB_METHOD("_new", NULL, Window_new, "[(BorderFrame)b(X)i(Y)i(W)i(H)i]"), + GB_METHOD("_free", NULL, Window_free, NULL), + GB_METHOD("_get", ".Char.Attrs", Window_get, "(Y)i(X)i"), + + GB_METHOD("Ask", "s", Window_Ask, "(Opts)s[(Tries)i]"), + GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"), + GB_METHOD("ReadLine", "s", Window_ReadLine, NULL), + GB_METHOD("Drain", NULL, Window_Drain, NULL), + + GB_METHOD("Get", "s", Window_Get, "[(X)i(Y)i(Len)i]"), + + GB_METHOD("Clear", NULL, Window_Clear, NULL), + GB_METHOD("Cls", NULL, Window_Clear, NULL), + + GB_METHOD("Lower", NULL, Window_Lower, NULL), + GB_METHOD("Raise", NULL, Window_Raise, NULL), + GB_METHOD("Hide", NULL, Window_Hide, NULL), + GB_METHOD("Show", NULL, Window_Show, NULL), + + GB_METHOD("DrawHLine", NULL, Window_DrawHLine,"(X)i(Y)i(Len)i(C)s"), + GB_METHOD("DrawVLine", NULL, Window_DrawVLine,"(X)i(Y)i(Len)i(C)s"), + GB_METHOD("Print", NULL, Window_Print, "(Text)s[(X)i(Y)i(Attr)i(Pair)i]"), + GB_METHOD("PrintCenter", NULL, Window_PrintCenter, "(Text)s[(Attr)i(Pair)i]"), + + GB_METHOD("Locate", NULL, Window_Locate, "(X)i(Y)i"), + + GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"), + GB_METHOD("Center", NULL, Window_Center, NULL), + GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"), + GB_METHOD("SetFullscreen", NULL, Window_SetFullscreen, NULL), + + GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL), + + GB_PROPERTY("Attributes", "i", Window_Attributes), + + GB_PROPERTY("Background", "i", Window_Background), + GB_PROPERTY("Paper", "i", Window_Background), + GB_PROPERTY("Foreground", "i", Window_Foreground), + GB_PROPERTY("Pen", "i", Window_Foreground), + GB_PROPERTY("Pair", "i", Window_Pair), + + GB_PROPERTY("Border", "i", Window_Border), + GB_PROPERTY("Buffered", "b", Window_Buffered), + GB_PROPERTY("Caption", "s", Window_Caption), + + GB_PROPERTY("CursorX", "i", Window_CursorX), + GB_PROPERTY("CursorY", "i", Window_CursorY), + + /* GB_PROPERTY("ClientHeight", ...), etc */ + GB_PROPERTY("Height", "i", Window_Height), + GB_PROPERTY("H", "i", Window_Height), + GB_PROPERTY("Width", "i", Window_Width), + GB_PROPERTY("W", "i", Window_Width), + + GB_PROPERTY("Wrap", "b", Window_Wrap), + + GB_PROPERTY("X", "i", Window_X), + GB_PROPERTY("Y", "i", Window_Y), + + GB_END_DECLARE +}; + +/* + * Attrs + */ + +GB_DESC CWindowAttrsDesc[] = { + GB_DECLARE("Attr", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("Normal", "i", A_NORMAL), + GB_CONSTANT("Underline", "i", A_UNDERLINE), + GB_CONSTANT("Reverse", "i", A_REVERSE), + GB_CONSTANT("Blink", "i", A_BLINK), + GB_CONSTANT("Bold", "i", A_BOLD), + + GB_END_DECLARE +}; + +/* + * .Char.Attrs + */ + +/* Seemingly, chgat() doesn't mark the window dirty. We use wtouchln() and + * wsyncup(). */ +#define CHAR_ATTR_METHOD(a) \ +int ox, oy; \ +chtype ch; \ + \ +getyx(THIS->content, oy, ox); \ +ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col); \ +if (READ_PROPERTY) { \ + GB.ReturnBoolean(ch & a); \ + return; \ +} \ +if (VPROP(GB_BOOLEAN)) \ + wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) | a, \ + PAIR_NUMBER(ch), NULL); \ +else \ + wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) & ~a, \ + PAIR_NUMBER(ch), NULL); \ +wtouchln(THIS->content, THIS->pos.line, 1, 1); \ +wsyncup(THIS->content); \ +wmove(THIS->content, oy, ox); \ +REFRESH(); + +BEGIN_PROPERTY(CharAttrs_Normal) + + int ox, oy; + chtype ch; + + getyx(THIS->content, oy, ox); + ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col); + if (READ_PROPERTY) { + GB.ReturnBoolean((ch & A_ATTRIBUTES) == A_NORMAL); + return; + } + if (VPROP(GB_BOOLEAN)) + wchgat(THIS->content, 1, A_NORMAL, PAIR_NUMBER(ch), NULL); + wtouchln(THIS->content, THIS->pos.line, 1, 1); + wmove(THIS->content, oy, ox); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Underline) + + CHAR_ATTR_METHOD(A_UNDERLINE); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Reverse) + + CHAR_ATTR_METHOD(A_REVERSE); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Blink) + + CHAR_ATTR_METHOD(A_BLINK); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Bold) + + CHAR_ATTR_METHOD(A_BOLD); + +END_PROPERTY + +#define CHAR_COLOR_METHOD(r, f, b) \ +short pair; \ +int ox, oy; \ +chtype ch; \ +short fg, bg; \ + \ +getyx(THIS->content, oy, ox); \ +ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col);\ +pair = PAIR_NUMBER(ch & A_COLOR); \ +pair_content(pair, &fg, &bg); \ +if (READ_PROPERTY) { \ + GB.ReturnInteger(r); \ + return; \ +} \ +pair = CPAIR_get(f, b); \ +if (pair == -1) { \ + GB.Error(GB_ERR_BOUND); \ + return; \ +} \ +wchgat(THIS->content, 1, (ch & A_ATTRIBUTES), pair, NULL);\ +wtouchln(THIS->content, THIS->pos.line, 1, 1); \ +wsyncup(THIS->content); \ +wmove(THIS->content, oy, ox); \ +REFRESH(); + +BEGIN_PROPERTY(CharAttrs_Background) + + CHAR_COLOR_METHOD(bg, fg, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Foreground) + + CHAR_COLOR_METHOD(fg, VPROP(GB_INTEGER), bg); + +END_PROPERTY + +GB_DESC CCharAttrsDesc[] = { + GB_DECLARE(".Char.Attrs", 0), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Normal", "b", CharAttrs_Normal), + GB_PROPERTY("Underline", "b", CharAttrs_Underline), + GB_PROPERTY("Reverse", "b", CharAttrs_Reverse), + GB_PROPERTY("Blink", "b", CharAttrs_Blink), + GB_PROPERTY("Bold", "b", CharAttrs_Bold), + + GB_PROPERTY("Background", "i", CharAttrs_Background), + GB_PROPERTY("Foreground", "i", CharAttrs_Foreground), + + GB_END_DECLARE +}; + +/* + * Border + */ + +GB_DESC CBorderDesc[] = { + GB_DECLARE("Border", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("None", "i", BORDER_NONE), + GB_CONSTANT("Ascii", "i", BORDER_ASCII), + GB_CONSTANT("ACS", "i", BORDER_ACS), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_window.h b/gb.ncurses/src/c_window.h new file mode 100644 index 00000000..65597531 --- /dev/null +++ b/gb.ncurses/src/c_window.h @@ -0,0 +1,63 @@ +/* + * c_window.h - gb.ncurses Window class + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_WINDOW_H +#define __C_WINDOW_H + +#include +#include + +#include "gambas.h" +#include "gb_common.h" + +/* Border constants */ +enum { + BORDER_NONE = 0, + BORDER_ASCII, + BORDER_ACS +}; + +typedef struct { + GB_BASE ob; + WINDOW *main; + WINDOW *content; + PANEL *pan; + bool has_border; + int border; + bool buffered; + bool wrap; + char *caption; + struct { + int line; + int col; + } pos; +} CWINDOW; + +#ifndef __C_WINDOW_C +extern GB_DESC CWindowDesc[]; +extern GB_DESC CWindowAttrsDesc[]; +extern GB_DESC CCharAttrsDesc[]; +extern GB_DESC CBorderDesc[]; +#endif + +void CWINDOW_raise_read(CWINDOW *win); + +#endif /* __C_WINDOW_C */ diff --git a/gb.ncurses/src/gb.ncurses.component b/gb.ncurses/src/gb.ncurses.component new file mode 100644 index 00000000..7bb19321 --- /dev/null +++ b/gb.ncurses/src/gb.ncurses.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Unfinished diff --git a/gb.ncurses/src/main.c b/gb.ncurses/src/main.c new file mode 100644 index 00000000..d60382d3 --- /dev/null +++ b/gb.ncurses/src/main.c @@ -0,0 +1,106 @@ +/* + * main.c - gb.ncurses main object + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __MAIN_C + +#include "c_window.h" +#include "c_key.h" +#include "c_color.h" +#include "c_screen.h" +#include "c_input.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + CScreenDesc, + CInputDesc, + CCursorDesc, + + CWindowDesc, + CWindowAttrsDesc, + CCharAttrsDesc, + CBorderDesc, + + CKeyDesc, + + CColorDesc, + CColorInfoDesc, + CPairDesc, + + NULL +}; + +static bool _init = FALSE; + +bool MAIN_running() +{ + return _init && (!isendwin() || stdscr); +} + +static void MAIN_init() +{ + if (_init) + return; + + initscr(); + keypad(stdscr, TRUE); + + SCREEN_init(); + COLOR_init(); + + refresh(); + + _init = TRUE; +} + +static void MAIN_exit() +{ + if (_init) { + SCREEN_exit(); + endwin(); + _init = FALSE; + } +} + +static bool MAIN_hook_error(int code, char *error, char *where, bool can_ignore) +{ + MAIN_exit(); + return FALSE; +} + +static void MAIN_hook_main(int *argc, char **argv) +{ + MAIN_init(); +} + +int EXPORT GB_INIT() +{ + GB.Hook(GB_HOOK_ERROR, (void *) MAIN_hook_error); + GB.Hook(GB_HOOK_MAIN, (void *) MAIN_hook_main); + return 0; +} + + +void EXPORT GB_EXIT() +{ + MAIN_exit(); +} diff --git a/gb.ncurses/src/main.h b/gb.ncurses/src/main.h new file mode 100644 index 00000000..46b63967 --- /dev/null +++ b/gb.ncurses/src/main.h @@ -0,0 +1,37 @@ +/* + * main.h - gb.ncurses main object + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MAIN_H +#define __MAIN_H + + +#include "gb_common.h" +#include "gambas.h" + +#define NCURSES_RUNNING MAIN_running() + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +bool MAIN_running(); + +#endif /* __MAIN_H */ diff --git a/gb.net.curl/AUTHORS b/gb.net.curl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/COPYING b/gb.net.curl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.net.curl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.net.curl/ChangeLog b/gb.net.curl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/INSTALL b/gb.net.curl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.net.curl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.net.curl/Makefile.am b/gb.net.curl/Makefile.am new file mode 100644 index 00000000..35e0fec0 --- /dev/null +++ b/gb.net.curl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @CURL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.net.curl/NEWS b/gb.net.curl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/README b/gb.net.curl/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/acinclude.m4 b/gb.net.curl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.net.curl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.net.curl/component.am b/gb.net.curl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.net.curl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.net.curl/configure.ac b/gb.net.curl/configure.ac new file mode 100644 index 00000000..cf1e2b3a --- /dev/null +++ b/gb.net.curl/configure.ac @@ -0,0 +1,21 @@ +dnl ---- configure.ac for gb.net.curl + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-net-curl],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.net.curl) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + curl, CURL, gb.net.curl, [src], + 'libcurl >= 7.13' + ) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.net.curl/gambas.h b/gb.net.curl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.net.curl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.net.curl/gb_common.h b/gb.net.curl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.net.curl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.net.curl/m4 b/gb.net.curl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.net.curl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.net.curl/reconf b/gb.net.curl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.net.curl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.net.curl/src/CCurl.c b/gb.net.curl/src/CCurl.c new file mode 100644 index 00000000..ae2fef29 --- /dev/null +++ b/gb.net.curl/src/CCurl.c @@ -0,0 +1,777 @@ +/*************************************************************************** + + CCurl.c + + (c) 2003-2008 Daniel Campos Fernández + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCURL_C + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "main.h" +#include "gambas.h" +#include "CCurl.h" +#include "CProxy.h" + +DECLARE_EVENT(EVENT_Finished); +DECLARE_EVENT(EVENT_Error); +DECLARE_EVENT(EVENT_Connect); +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Progress); +DECLARE_EVENT(EVENT_Cancel); + +static CCURL *_async_list = NULL; + +static void add_to_async_list(CCURL *_object) +{ + if (THIS->in_list) + return; + + #ifdef DEBUG + fprintf(stderr, "add_to_async_list: %p\n", THIS); + #endif + + GB.List.Add(&_async_list, THIS, &THIS->list); + THIS->in_list = TRUE; + GB.Ref(THIS); +} + +static void remove_from_async_list(CCURL *_object) +{ + if (!THIS->in_list) + return; + + #ifdef DEBUG + fprintf(stderr, "remove_from_async_list: %p\n", THIS); + #endif + + GB.List.Remove(&_async_list, THIS, &THIS->list); + THIS->in_list = FALSE; + GB.Unref(POINTER(&_object)); +} + +/***************************************************** +CURLM : a pointer to use curl_multi interface, +allowing asynchrnous work without using threads +in this class. +******************************************************/ +CURLM *CCURL_multicurl; +int CCURL_pipe[2] = {-1, -1}; + +/****************************************************** +Events from this class +******************************************************/ +GB_STREAM_DESC CurlStream = +{ + .open = CCURL_stream_open, + .close = CCURL_stream_close, + .read = CCURL_stream_read, + .write = CCURL_stream_write, + .seek = CCURL_stream_seek, + .tell = CCURL_stream_tell, + .flush = CCURL_stream_flush, + .eof = CCURL_stream_eof, + .lof = CCURL_stream_lof, + .handle = CCURL_stream_handle, +}; + +//////////////////////////////////////////////////////////////////// +// STREAM // +//////////////////////////////////////////////////////////////////// + +/* not allowed stream methods */ + +int CCURL_stream_handle(GB_STREAM *stream) { return -1;} +int CCURL_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +int CCURL_stream_seek(GB_STREAM *stream, int64_t pos, int whence){ return -1;} +int CCURL_stream_tell(GB_STREAM *stream, int64_t *pos){return -1; } +int CCURL_stream_flush(GB_STREAM *stream) { return 0;} +int CCURL_stream_close(GB_STREAM *stream) { return 0;} +int CCURL_stream_write(GB_STREAM *stream, char *buffer, int len){return -1;} + +int CCURL_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = STREAM_TO_OBJECT(stream); + + *len = 0; + + if ((THIS_STATUS != NET_RECEIVING_DATA ) && (THIS_STATUS != NET_INACTIVE)) + return -1; + + *len = GB.StringLength(THIS->data); + return 0; +} + +int CCURL_stream_eof(GB_STREAM *stream) +{ + void *_object = STREAM_TO_OBJECT(stream); + + if ((THIS_STATUS != NET_RECEIVING_DATA ) && (THIS_STATUS != NET_INACTIVE)) return -1; + if (!GB.StringLength(THIS->data)) return -1; + return 0; +} + +int CCURL_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = STREAM_TO_OBJECT(stream); + int len_data; + char *new_data; + + if ((THIS_STATUS != NET_RECEIVING_DATA ) && (THIS_STATUS != NET_INACTIVE)) return -1; + + len_data = GB.StringLength(THIS->data); + + if (len_data < len) + len = len_data; + + memcpy(buffer, THIS->data, len); + + len_data -= len; + + if (len_data > 0) + new_data = GB.NewString(THIS->data + len, len_data); + else + new_data = NULL; + + GB.FreeString(&THIS->data); + THIS->data = new_data; + + return len; +} + +static void raise_event(void *_object, int event) +{ + GB.Raise(THIS, event, 0); + GB.Unref(POINTER(&_object)); +} + +void CURL_raise_finished(void *_object) +{ + raise_event(THIS, EVENT_Finished); +} + +void CURL_raise_error(void *_object) +{ + raise_event(THIS, EVENT_Error); +} + +void CURL_raise_cancel(void *_object) +{ + raise_event(THIS, EVENT_Cancel); +} + +void CURL_raise_connect(void *_object) +{ + raise_event(THIS, EVENT_Connect); +} + +void CURL_raise_read(void *_object) +{ + if (GB.CanRaise(THIS, EVENT_Read)) + { + GB.Raise(THIS, EVENT_Read, 0); + + if (!GB.Stream.Eof(&THIS->stream)) + { + GB.Ref(THIS); + GB.Post(CURL_raise_read, (intptr_t)THIS); + } + } + + GB.Unref(POINTER(&_object)); +} + +void CURL_manage_error(void *_object, int error) +{ + if (THIS_FILE) + { + fclose(THIS_FILE); + THIS_FILE = NULL; + } + + switch (error) + { + case CURLE_OK: + + if (THIS->async) + { + #if DEBUG + fprintf(stderr, "-- [%p] curl_multi_remove_handle(%p)\n", THIS, THIS_CURL); + #endif + curl_multi_remove_handle(CCURL_multicurl,THIS_CURL); + } + GB.Ref(THIS); + GB.Post(CURL_raise_finished, (intptr_t)THIS); + CURL_stop(THIS); + THIS_STATUS = NET_INACTIVE; + break; + + default: + + if (THIS->async) + { + #if DEBUG + fprintf(stderr, "-- [%p] curl_multi_remove_handle(%p)\n", THIS, THIS_CURL); + #endif + curl_multi_remove_handle(CCURL_multicurl,THIS_CURL); + } + GB.Ref(THIS); + GB.Post(CURL_raise_error, (intptr_t)THIS); + CURL_stop(THIS); + THIS_STATUS = (- (1000 + error)); + break; + } +} + +void CURL_init_stream(void *_object) +{ + THIS->stream.desc = &CurlStream; + THIS->stream.tag = THIS; + GB.Stream.SetAvailableNow(&THIS->stream, TRUE); +} + +void CURL_init_options(void *_object) +{ + curl_easy_setopt(THIS_CURL, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(THIS_CURL, CURLOPT_TIMEOUT, THIS->timeout); + curl_easy_setopt(THIS_CURL, CURLOPT_VERBOSE, (bool)THIS->debug); + curl_easy_setopt(THIS_CURL, CURLOPT_PRIVATE, (char*)_object); + + if (THIS->buffer_size) + curl_easy_setopt(THIS_CURL, CURLOPT_BUFFERSIZE, THIS->buffer_size); + + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYPEER, THIS->ssl_verify_peer ? 1 : 0); + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYHOST , THIS->ssl_verify_host ? 2 : 0); + + CURL_proxy_set(&THIS->proxy, THIS_CURL); + CURL_user_set(&THIS->user, THIS_CURL); + curl_easy_setopt(THIS_CURL, CURLOPT_URL, THIS_URL); +} + +#define CHECK_PROGRESS_VAL(_var) if (THIS->_var != (int64_t)_var) { THIS->_var = (int64_t)_var; raise = TRUE; } + +static int curl_progress(void *_object, double dltotal, double dlnow, double ultotal, double ulnow) +{ + bool raise = FALSE; + + if (THIS->progresscb) + (*THIS->progresscb)(THIS, &dltotal, &dlnow, &ultotal, &ulnow); + + CHECK_PROGRESS_VAL(dltotal); + CHECK_PROGRESS_VAL(dlnow); + CHECK_PROGRESS_VAL(ultotal); + CHECK_PROGRESS_VAL(ulnow); + + if (raise) + GB.RaiseLater(THIS, EVENT_Progress); + + return 0; +} + + +/*************************************************************** +This CallBack is called each event loop by Gambas to test +the status of curl descriptors +***************************************************************/ + +static void stop_post() +{ + if (CCURL_pipe[0] < 0) return; + + GB.Watch (CCURL_pipe[0], GB_WATCH_NONE, NULL, 0); + close(CCURL_pipe[0]); + close(CCURL_pipe[1]); + CCURL_pipe[0]=-1; +} + +static void CCURL_post_curl(intptr_t data) +{ + CURLMsg *Msg; + int nread; + int post=1; + void *_object; + char *tmp; + + do + { + usleep(1000); + } + while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(CCURL_multicurl,&nread)); + + if (!nread) post=0; + + do + { + Msg=curl_multi_info_read(CCURL_multicurl,&nread); + if (!Msg) nread=0; + if (Msg) + { + curl_easy_getinfo(Msg->easy_handle,CURLINFO_PRIVATE,&tmp); + _object=(void*)tmp; + CURL_manage_error(THIS,Msg->data.result); + } + } + while (nread); + + if (!post) + stop_post(); +} + +void CURL_stop(void *_object) +{ + if (THIS_STATUS == NET_INACTIVE) + return; + + if (THIS_CURL) + { + #if DEBUG + fprintf(stderr, "-- CURL_stop: [%p] curl_multi_remove_handle(%p)\n", THIS, THIS_CURL); + #endif + curl_multi_remove_handle(CCURL_multicurl,THIS_CURL); + #if DEBUG + fprintf(stderr, "-- CURL_stop: [%p] curl_easy_cleanup(%p)\n", THIS, THIS_CURL); + #endif + curl_easy_cleanup(THIS_CURL); + THIS_CURL = NULL; + } + + if (THIS_FILE) + { + fclose(THIS_FILE); + THIS_FILE = NULL; + } + + THIS_STATUS = NET_INACTIVE; + + remove_from_async_list(THIS); +} + +static void init_post(void) +{ + if (CCURL_pipe[0]!=-1) return; + + if (pipe(CCURL_pipe)) + { + fprintf(stderr, "gb.net.curl: warning: unable to create the client watching pipe: %s\n", strerror(errno)); + return; + } + + GB.Watch (CCURL_pipe[0], GB_WATCH_READ, CCURL_post_curl, 0); + if (write(CCURL_pipe[1], "1", sizeof(char)) != 1) + fprintf(stderr, "gb.net.curl: warning: unable to write to the client watching pipe: %s\n", strerror(errno)); +} + +void CURL_start_post(void *_object) +{ + init_post(); + curl_multi_add_handle(CCURL_multicurl, THIS_CURL); + add_to_async_list(THIS); +} + +bool CURL_check_active(void *_object) +{ + if (THIS_STATUS > 0) + { + GB.Error("Property is read-only while client is active"); + return TRUE; + } + else + return FALSE; +} + +void CURL_set_progress(void *_object, bool progress, CURL_FIX_PROGRESS_CB cb) +{ + #ifdef DEBUG + fprintf(stderr, "CURL_set_progress: %p %d\n", _object, progress); + #endif + + curl_easy_setopt(THIS_CURL, CURLOPT_NOPROGRESS, progress ? 0 : 1); + if (progress) + { + curl_easy_setopt(THIS_CURL, CURLOPT_PROGRESSFUNCTION , curl_progress); + curl_easy_setopt(THIS_CURL, CURLOPT_PROGRESSDATA , _object); + } + + THIS->progresscb = cb; +} + +#define COPY_STRING(_field) \ +{ \ + GB.FreeString(&dest->_field); \ + dest->_field = src->_field; \ + if (dest->_field) dest->_field = GB.NewString(dest->_field, GB.StringLength(dest->_field)); \ +} + +bool CURL_copy_from(CCURL *dest, CCURL *src) +{ + if (CURL_check_active(dest)) + return TRUE; + + dest->async = src->async; + dest->timeout = src->timeout; + dest->debug = src->debug; + dest->buffer_size = src->buffer_size; + COPY_STRING(url); + + dest->user.auth = src->user.auth; + COPY_STRING(user.user); + COPY_STRING(user.userpwd); + COPY_STRING(user.pwd); + + dest->proxy.type = src->proxy.type; + dest->proxy.auth = src->proxy.auth; + COPY_STRING(proxy.host); + COPY_STRING(proxy.user); + COPY_STRING(proxy.pwd); + COPY_STRING(proxy.userpwd); + + return FALSE; +} + +//--------------------------------------------------------------------------- + + +BEGIN_PROPERTY(Curl_User) + + if (READ_PROPERTY) + GB.ReturnString(THIS->user.user); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &(THIS->user.user)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Async) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->async); + else + { + if (CURL_check_active(THIS)) + return; + + THIS->async = VPROP(GB_BOOLEAN); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Timeout) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->timeout); + else + { + int timeout; + + if (CURL_check_active(THIS)) + return; + + timeout = VPROP(GB_INTEGER); + if (timeout < 0) + timeout = 0; + + THIS->timeout = timeout; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_BufferSize) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->buffer_size); + else + { + int buffer_size; + + if (CURL_check_active(THIS)) + return; + + buffer_size = VPROP(GB_INTEGER); + if (buffer_size <= 0) + buffer_size = 0; + else if (buffer_size < 1024) + buffer_size = 1024; + else if (buffer_size > CURL_MAX_READ_SIZE) + buffer_size = CURL_MAX_READ_SIZE; + + THIS->buffer_size = buffer_size; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Password) + + if (READ_PROPERTY) + GB.ReturnString(THIS->user.pwd); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &(THIS->user.pwd)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Status) + + GB.ReturnInteger(THIS_STATUS); + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_ErrorText) + + if (THIS_STATUS >= 0) + GB.ReturnVoidString(); + else + GB.ReturnConstZeroString(curl_easy_strerror((-THIS_STATUS) - 1000)); + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_URL) + + if (READ_PROPERTY) + { + GB.ReturnString(THIS_URL); + return; + } + + if (CURL_check_active(THIS)) + return; + + CURL_set_url(THIS, PSTRING(), PLENGTH()); + +END_PROPERTY + +BEGIN_METHOD_VOID(Curl_new) + + #if DEBUG + fprintf(stderr, "Curl_new: %p\n", THIS); + #endif + + CURL_user_init(&THIS->user); + CURL_proxy_init(&THIS->proxy); + + THIS->ssl_verify_peer = TRUE; + THIS->ssl_verify_host = TRUE; + +END_METHOD + +BEGIN_METHOD_VOID(Curl_free) + + #if DEBUG + fprintf(stderr, "Curl_free: %p\n", THIS); + #endif + + CURL_stop(THIS); + + GB.FreeString(&THIS_URL); + GB.FreeString(&THIS->target); + + CURL_user_clear(&THIS->user); + CURL_proxy_clear(&THIS->proxy); + +END_METHOD + +BEGIN_METHOD_VOID(Curl_init) + + #if DEBUG + fprintf(stderr, "-- curl_multi_init()\n"); + #endif + CCURL_multicurl = curl_multi_init(); + +END_METHOD + +BEGIN_METHOD_VOID(Curl_exit) + + CCURL *curl, *next; + + #if DEBUG + fprintf(stderr, "-- CURL_exit: clear async list\n"); + #endif + + curl = _async_list; + while (curl) + { + next = curl->list.next; + remove_from_async_list(curl); + curl = next; + } + + #if DEBUG + fprintf(stderr, "-- curl_multi_cleanup()\n"); + #endif + + curl_multi_cleanup(CCURL_multicurl); + + CURL_default_proxy_clear(); + +END_METHOD + +BEGIN_METHOD_VOID(Curl_Peek) + + GB.ReturnString(THIS->data); + +END_METHOD + +BEGIN_PROPERTY(Curl_Debug) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->debug); + else + THIS->debug = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_Downloaded) + + GB.ReturnLong(THIS->dlnow); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_Uploaded) + + GB.ReturnLong(THIS->ulnow); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_TotalDownloaded) + + GB.ReturnLong(THIS->dltotal); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_TotalUploaded) + + GB.ReturnLong(THIS->ultotal); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_TargetFile) + + if (READ_PROPERTY) + GB.ReturnString(THIS->target); + else + GB.StoreString(PROP(GB_STRING), &THIS->target); + +END_PROPERTY + + +//--------------------------------------------------------------------------- + +BEGIN_PROPERTY(Curl_SSL_VerifyPeer) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->ssl_verify_peer); + else + { + THIS->ssl_verify_peer = VPROP(GB_BOOLEAN); + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYPEER, THIS->ssl_verify_peer ? 1 : 0); + } + +END_PROPERTY + +BEGIN_PROPERTY(Curl_SSL_VerifyHost) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->ssl_verify_host); + else + { + THIS->ssl_verify_host = VPROP(GB_BOOLEAN); + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYHOST , THIS->ssl_verify_host ? 2 : 0); + } + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CurlSSLDesc[] = +{ + GB_DECLARE_VIRTUAL(".Curl.SSL"), + + GB_PROPERTY("VerifyPeer", "b", Curl_SSL_VerifyPeer), + GB_PROPERTY("VerifyHost", "b", Curl_SSL_VerifyHost), + + GB_END_DECLARE +}; + +GB_DESC CurlDesc[] = +{ + GB_DECLARE("Curl", sizeof(CCURL)), GB_NOT_CREATABLE(), + + GB_INHERITS("Stream"), + + GB_STATIC_METHOD("_init", NULL, Curl_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Curl_exit, NULL), + + GB_METHOD("_new", NULL, Curl_new, NULL), + GB_METHOD("_free", NULL, Curl_free, NULL), + + GB_METHOD("Peek","s", Curl_Peek, NULL), + + GB_PROPERTY("URL", "s", Curl_URL), + GB_PROPERTY("User", "s", Curl_User), + GB_PROPERTY("Password", "s", Curl_Password), + GB_PROPERTY("Async", "b", Curl_Async), + GB_PROPERTY("Timeout", "i", Curl_Timeout), + GB_STATIC_PROPERTY_SELF("DefaultProxy", ".Curl.Proxy"), + GB_PROPERTY_SELF("Proxy", ".Curl.Proxy"), + GB_PROPERTY_SELF("SSL", ".Curl.SSL"), + GB_PROPERTY_READ("Status", "i", Curl_Status), + GB_PROPERTY_READ("ErrorText", "s", Curl_ErrorText), + GB_PROPERTY("Debug", "b", Curl_Debug), + GB_PROPERTY("BufferSize", "i", Curl_BufferSize), + GB_PROPERTY("TargetFile", "s", Curl_TargetFile), + + GB_PROPERTY_READ("Downloaded", "l", Curl_Downloaded), + GB_PROPERTY_READ("Uploaded", "l", Curl_Uploaded), + GB_PROPERTY_READ("TotalDownloaded", "l", Curl_TotalDownloaded), + GB_PROPERTY_READ("TotalUploaded", "l", Curl_TotalUploaded), + + GB_EVENT("Finished", NULL, NULL, &EVENT_Finished), + GB_EVENT("Connect", NULL, NULL, &EVENT_Connect), + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Error", NULL, NULL, &EVENT_Error), + GB_EVENT("Progress", NULL, NULL, &EVENT_Progress), + GB_EVENT("Cancel", NULL, NULL, &EVENT_Cancel), + + GB_END_DECLARE +}; + diff --git a/gb.net.curl/src/CCurl.h b/gb.net.curl/src/CCurl.h new file mode 100644 index 00000000..9e195da3 --- /dev/null +++ b/gb.net.curl/src/CCurl.h @@ -0,0 +1,121 @@ +/*************************************************************************** + + CCurl.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCURL_H +#define __CCURL_H + +#include "gambas.h" +#include "gb_common.h" +#include "gbcurl.h" +#include "CNet.h" +#include "CProxy.h" +#include +#include + +#ifndef CURL_MAX_READ_SIZE +#define CURL_MAX_READ_SIZE 524288 +#endif + +//#define DEBUG 1 + +#ifndef __CCURL_C + +extern GB_DESC CurlDesc[]; +extern GB_DESC CurlSSLDesc[]; +extern GB_STREAM_DESC CurlStream; + +#endif + +#define THIS ((CCURL *)_object) +#define THIS_STATUS THIS->status +#define THIS_CURL THIS->curl +#define THIS_URL THIS->url +#define THIS_FILE THIS->file + +typedef + void (*CURL_FIX_PROGRESS_CB)(void *, double *, double *, double *, double *); + +typedef + struct { + GB_BASE ob; + GB_STREAM stream; + GB_LIST list; // List of async curl objects + int status; + CURL *curl; + char *url; + FILE *file; + CURL_PROXY proxy; + CURL_USER user; + int timeout; + int buffer_size; + int method; // 0->Get, 1->Put + char *data; + char *target; + int64_t dltotal; + int64_t dlnow; + int64_t ultotal; + int64_t ulnow; + CURL_FIX_PROGRESS_CB progresscb; + unsigned async : 1; + unsigned in_list : 1; + unsigned debug : 1; + unsigned ssl_verify_peer : 1; + unsigned ssl_verify_host : 1; + } + CCURL; + +#define STREAM_TO_OBJECT(_stream) (_stream->tag) + +void CCURL_stream_init(GB_STREAM *stream,int fd); +int CCURL_stream_read(GB_STREAM *stream, char *buffer, int len); +int CCURL_stream_write(GB_STREAM *stream, char *buffer, int len); +int CCURL_stream_eof(GB_STREAM *stream); +int CCURL_stream_lof(GB_STREAM *stream, int64_t *len); +int CCURL_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CCURL_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CCURL_stream_tell(GB_STREAM *stream, int64_t *pos); +int CCURL_stream_flush(GB_STREAM *stream); +int CCURL_stream_close(GB_STREAM *stream); +int CCURL_stream_handle(GB_STREAM *stream); + +void CURL_raise_finished(void *_object); +void CURL_raise_error(void *_object); +void CURL_raise_cancel(void *_object); +void CURL_raise_connect(void *_object); +void CURL_raise_read(void *_object); + +void CURL_start_post(void *_object); +void CURL_stop(void *_object); + +void CURL_manage_error(void *_object, int error); + +void CURL_init_stream(void *_object); +void CURL_init_options(void *_object); + +bool CURL_check_active(void *_object); + +void CURL_set_progress(void *_object, bool progress, CURL_FIX_PROGRESS_CB cb); + +bool CURL_copy_from(CCURL *dest, CCURL *src); + +#endif diff --git a/gb.net.curl/src/CFtpClient.c b/gb.net.curl/src/CFtpClient.c new file mode 100644 index 00000000..74307736 --- /dev/null +++ b/gb.net.curl/src/CFtpClient.c @@ -0,0 +1,333 @@ +/*************************************************************************** + + CFtpClient.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFTPCLIENT_C + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "main.h" +#include "gambas.h" +#include "CCurl.h" +#include "CFtpClient.h" +#include "CProxy.h" + +#define EXEC_GET 0 +#define EXEC_PUT 1 +#define EXEC_CMD 2 + + +static int ftp_read_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + FILE *file = THIS_FILE; + THIS_STATUS = NET_RECEIVING_DATA; + + if (!feof(file)) + nmemb = fread(buffer, size, nmemb, file); + else + nmemb = 0; + + return nmemb; +} + + +static int ftp_write_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + THIS_STATUS = NET_RECEIVING_DATA; + nmemb *= size; + + if (THIS_FILE) + { + return fwrite(buffer,size,nmemb,THIS_FILE); + } + else + { + THIS->data = GB.AddString(THIS->data, buffer, nmemb); + } + + if (THIS->async) + { + GB.Ref(THIS); + GB.Post(CURL_raise_read, (intptr_t)THIS); + } + + return nmemb; +} + +static void ftp_reset(void *_object) +{ + GB.FreeString(&THIS->data); + GB.Unref(&THIS_FTP->commands); +} + + +static void ftp_initialize_curl_handle(void *_object) +{ + if (THIS_CURL) + { + if (CURL_check_userpwd(&THIS->user)) + { + CURL_stop(_object); + ftp_reset(_object); + THIS_CURL = curl_easy_init(); + #if DEBUG + fprintf(stderr, "-- [%p] curl_easy_init() -> %p\n", THIS, THIS_CURL); + #endif + } + } + else + { + THIS_CURL = curl_easy_init(); + #if DEBUG + fprintf(stderr, "-- [%p] curl_easy_init() -> %p\n", THIS, THIS_CURL); + #endif + } + + CURL_init_options(THIS); + + curl_easy_setopt(THIS_CURL, CURLOPT_FTP_USE_EPSV, (long)(THIS_FTP->no_epsv ? 0 : 1)); + + ftp_reset(THIS_FTP); + THIS_STATUS = NET_CONNECTING; + + CURL_init_stream(THIS); +} + + +static int ftp_exec(void *_object, int what, GB_ARRAY commands) +{ + struct stat info; + struct curl_slist *list; + int i; + + if (THIS_STATUS > 0) + return 1; + + THIS->method = what == EXEC_PUT ? 1 : 0; + + ftp_initialize_curl_handle(THIS); + + switch(what) + { + case EXEC_GET: + + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEFUNCTION , (curl_write_callback)ftp_write_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEDATA , _object); + curl_easy_setopt(THIS_CURL, CURLOPT_UPLOAD , 0); + + CURL_set_progress(THIS, TRUE, NULL); + + break; + + case EXEC_PUT: + + if (THIS_FILE && fstat(fileno(THIS_FILE), &info) == 0) + curl_easy_setopt(THIS_CURL, CURLOPT_INFILESIZE_LARGE, (curl_off_t)info.st_size); + + curl_easy_setopt(THIS_CURL, CURLOPT_READFUNCTION , (curl_read_callback)ftp_read_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_READDATA , _object); + curl_easy_setopt(THIS_CURL, CURLOPT_UPLOAD , 1); + + CURL_set_progress(THIS, TRUE, NULL); + + break; + + case EXEC_CMD: + + curl_easy_setopt(THIS_CURL, CURLOPT_NOBODY, 1); + + if (commands) + { + char *cmd; + + GB.Unref(&THIS_FTP->commands); + THIS_FTP->commands = commands; + GB.Ref(commands); + + list = NULL; + for (i = 0; i < GB.Array.Count(commands); i++) { + cmd = *((char **) GB.Array.Get(commands, i)); + if (!cmd) + continue; + list = curl_slist_append(list, cmd); + } + if (list) + curl_easy_setopt(THIS_CURL, CURLOPT_QUOTE, list); + } + + break; + } + + if (THIS->async) + { + #if DEBUG + fprintf(stderr, "-- [%p] curl_multi_add_handle(%p)\n", THIS, THIS_CURL); + #endif + CURL_start_post(THIS); + return 0; + } + + CURL_manage_error(_object, curl_easy_perform(THIS_CURL)); + return 0; +} + + +BEGIN_METHOD(FtpClient_Get, GB_STRING target) + + const char *target = NULL; + + if (MISSING(target)) + target = THIS->target; + else + target = GB.FileName(STRING(target), LENGTH(target)); + + if (target && *target) + { + /*if (THIS_STATUS > 0) + { + GB.Error("Still active"); + return; + }*/ + + THIS_FILE = fopen(target, "w"); + + if (!THIS_FILE) + { + GB.Error("Unable to open file for writing"); + return; + } + } + + if (ftp_exec(THIS, EXEC_GET, NULL)) + GB.Error("Still active"); + +END_METHOD + + +BEGIN_METHOD(FtpClient_Put, GB_STRING SourceFile) + + if (THIS_STATUS > 0) + { + GB.Error("Still active"); + return; + } + + THIS_FILE = fopen(GB.FileName(STRING(SourceFile), LENGTH(SourceFile)),"r"); + if (!THIS_FILE) + { + GB.Error("Unable to open file for reading"); + return; + } + + if (ftp_exec(THIS, EXEC_PUT, NULL)) + GB.Error("Still active"); + +END_METHOD + + +BEGIN_METHOD(FtpClient_Exec, GB_OBJECT commands) + + if (THIS_STATUS > 0) + { + GB.Error("Still active"); + return; + } + + ftp_exec(THIS, EXEC_CMD, VARG(commands)); + +END_METHOD + + +BEGIN_METHOD_VOID(FtpClient_Stop) + + CURL_stop(THIS); + ftp_reset(THIS); + + GB.Ref(THIS); + CURL_raise_cancel(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(FtpClient_new) + + THIS_URL = GB.NewZeroString("ftp://127.0.0.1:21"); + + THIS->async = TRUE; + + CURL_user_set_auth(&THIS->user, CURLAUTH_BASIC); + + THIS->user.user = GB.NewZeroString("anonymous"); + +END_METHOD + + +BEGIN_METHOD_VOID(FtpClient_free) + + //CURL_stop(_object); + ftp_reset(THIS_FTP); + +END_METHOD + +BEGIN_PROPERTY(FtpClient_NoEPSV) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_FTP->no_epsv); + else + THIS_FTP->no_epsv = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +GB_DESC CFtpClientDesc[] = +{ + GB_DECLARE("FtpClient", sizeof(CFTPCLIENT)), + + GB_INHERITS("Curl"), + + GB_METHOD("_new", NULL, FtpClient_new, NULL), + GB_METHOD("_free", NULL, FtpClient_free, NULL), + + GB_METHOD("Stop", NULL, FtpClient_Stop, NULL), + GB_METHOD("Get", NULL, FtpClient_Get, "[(TargetFile)s]"), + GB_METHOD("Exec", NULL, FtpClient_Exec, "(Commands)String[]"), + GB_METHOD("Put", NULL, FtpClient_Put, "(LocalFile)s"), + + GB_PROPERTY("NoEPSV", "b", FtpClient_NoEPSV), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", FTP_PROPERTIES), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; + diff --git a/gb.net.curl/src/CFtpClient.h b/gb.net.curl/src/CFtpClient.h new file mode 100644 index 00000000..1d88ca72 --- /dev/null +++ b/gb.net.curl/src/CFtpClient.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CFtpClient.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFTPCLIENT_H +#define __CFTPCLIENT_H + +#include "gambas.h" +#include "gbcurl.h" +#include "CCurl.h" +#include "CProxy.h" +#include +#include + +#ifndef __CFTPCLIENT_C + + +extern GB_DESC CFtpClientDesc[]; +extern GB_STREAM_DESC FtpStream; + +#else + +#define THIS_FTP ((CFTPCLIENT *)_object) + +#endif + +typedef + struct { + CCURL curl; + GB_ARRAY commands; + unsigned no_epsv : 1; + } + CFTPCLIENT; + +#define FTP_PROPERTIES "URL=127.0.0.1:21,Async=True,Timeout=0,User,Password" + +#endif diff --git a/gb.net.curl/src/CHttpClient.c b/gb.net.curl/src/CHttpClient.c new file mode 100644 index 00000000..a01cc99c --- /dev/null +++ b/gb.net.curl/src/CHttpClient.c @@ -0,0 +1,701 @@ +/*************************************************************************** + + CHttpClient.c + + (c) 2003-2008 Daniel Campos Fernández + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CHTTPCLIENT_C + +#include +#include +#include +#include + +#include +#include +#include + +#include "main.h" +#include "gambas.h" +#include "CCurl.h" +#include "CHttpClient.h" +#include "CProxy.h" + +#define ERR_STILL_ACTIVE "Still active" +#define ERR_INVALID_CONTENT_TYPE "Invalid content type" +#define ERR_INVALID_DATA "Invalid data" + +#define SEND_POST 1 +#define SEND_PUT 2 +#define SEND_FILE 4 + +static void http_parse_header(CHTTPCLIENT *_object) +{ + char *header; + int len; + char *p; + int ret; + + if (THIS_HTTP->return_string) + return; + + if (!THIS_HTTP->headers || GB.Array.Count(THIS_HTTP->headers) == 0) + return; + + header = *(char **)GB.Array.Get(THIS_HTTP->headers, 0); + len = GB.StringLength(header); + + p = strchr(header, ' '); + if (!p) + return; + p++; + + ret = 0; + while (isdigit(*p)) + { + ret = (ret * 10) + (*p - '0'); + p++; + } + + + if (*p != ' ') + return; + + p++; + + THIS_HTTP->return_code = ret; + //GB.FreeString(&THIS_HTTP->return_string); + THIS_HTTP->return_string = GB.NewString(p, header + len - p); +} + + +static int http_header_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + if (!THIS_HTTP->headers) + { + GB.Array.New(&THIS_HTTP->headers, GB_T_STRING, 0); + GB.Ref(THIS_HTTP->headers); + } + + if (nmemb > 2) + *(char **)GB.Array.Add(THIS_HTTP->headers) = GB.NewString(buffer, (nmemb - 2) * size); + + if ((THIS_STATUS == NET_CONNECTING) && THIS->async) + { + THIS_STATUS = NET_RECEIVING_DATA; + GB.Ref(THIS); + GB.Post(CURL_raise_connect, (intptr_t)THIS); + } + + return size * nmemb; +} + + +static size_t http_read_curl(void *ptr, size_t size, size_t nmemb, void *_object) +{ + size *= nmemb; + + if (size > (THIS_HTTP->len_data - THIS_HTTP->len_sent)) + size = THIS_HTTP->len_data - THIS_HTTP->len_sent; + + if (size > 0) + { + memcpy(ptr, THIS_HTTP->data + THIS_HTTP->len_sent, size); + THIS_HTTP->len_sent += size; + } + + return size; +} + + +static int http_write_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + http_parse_header(THIS_HTTP); + + nmemb *= size; + + if (THIS_FILE) + { + return fwrite(buffer,size,nmemb,THIS_FILE); + } + else + { + THIS->data = GB.AddString(THIS->data, buffer, nmemb); + } + + if (THIS->async) + { + GB.Ref(THIS); + GB.Post(CURL_raise_read, (intptr_t)THIS); + } + + return nmemb; +} + + +static void http_reset(void *_object) +{ + GB.FreeString(&THIS->data); + + GB.Unref(&THIS_HTTP->headers); + THIS_HTTP->headers = NULL; + GB.Unref(&THIS_HTTP->sent_headers); + THIS_HTTP->sent_headers = NULL; + + if (THIS_HTTP->sContentType) + { + GB.Free((void**)POINTER(&THIS_HTTP->sContentType)); + THIS_HTTP->sContentType=NULL; + } + + if (THIS_HTTP->data) + { + if (THIS_HTTP->send_file) + GB.ReleaseFile(THIS_HTTP->data, THIS_HTTP->len_data); + else + GB.Free(POINTER(&THIS_HTTP->data)); + THIS_HTTP->data = NULL; + } + + THIS_HTTP->send_file = FALSE; +} + + +static void http_initialize_curl_handle(void *_object, GB_ARRAY custom_headers) +{ + if (THIS_CURL) + { + if (CURL_check_userpwd(&THIS->user)) + { + CURL_stop(_object); + http_reset(_object); + THIS_CURL = curl_easy_init(); + } + } + else + { + THIS_CURL = curl_easy_init(); + } + + CURL_init_options(THIS); + + curl_easy_setopt(THIS_CURL, CURLOPT_USERAGENT, THIS_HTTP->sUserAgent); + curl_easy_setopt(THIS_CURL, CURLOPT_ENCODING, THIS_HTTP->encoding); + curl_easy_setopt(THIS_CURL, CURLOPT_HEADERFUNCTION, (curl_write_callback)http_header_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEFUNCTION, (curl_write_callback)http_write_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEDATA, _object); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEHEADER, _object); + curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEFILE, THIS_HTTP->cookiesfile); + curl_easy_setopt(THIS_CURL, CURLOPT_FOLLOWLOCATION, (long)THIS_HTTP->redirect); + + if (THIS_HTTP->updatecookies) + curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEJAR, THIS_HTTP->cookiesfile); + else + curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEJAR, NULL); + + THIS_HTTP->return_code = 0; + GB.FreeString(&THIS_HTTP->return_string); + + http_reset(_object); + THIS_STATUS = NET_CONNECTING; + + if (custom_headers) + { + GB.Unref(&THIS_HTTP->sent_headers); + THIS_HTTP->sent_headers = custom_headers; + GB.Ref(custom_headers); + } + + CURL_init_stream(THIS); +} + + +static bool check_request(void *_object, char *contentType, char *data, int len) +{ + int i; + unsigned char c; + + if (THIS_STATUS > 0) + { + GB.Error(ERR_STILL_ACTIVE); + return TRUE; + } + + if (!contentType) + { + GB.Error(ERR_INVALID_CONTENT_TYPE); + return TRUE; + } + + for (i = 0; i < strlen(contentType); i++) + { + c = contentType[i]; + if (isalnum(c) || c == '-' || c == '+' || c == '.' || c == '/' || c == ';' || c == ' ' || c == '=') + continue; + GB.Error(ERR_INVALID_CONTENT_TYPE); + return TRUE; + } + + /*if (!data || !len) + { + GB.Error(ERR_INVALID_DATA); + return TRUE; + };*/ + + return FALSE; +} + +static void http_fix_progress_cb(void *_object, double *dltotal, double *dlnow, double *ultotal, double *ulnow) +{ + *ultotal = THIS_HTTP->len_data; + *ulnow = THIS_HTTP->len_sent; +} + +static void http_get(void *_object, GB_ARRAY custom_headers, char *target, CURLoption method) +{ + struct curl_slist *headers = NULL; + int i; + + if (THIS_STATUS > 0) + { + GB.Error(ERR_STILL_ACTIVE); + return; + } + + if (!target) + target = THIS->target; + + if (target && *target) + { + target = GB.FileName(target, 0); + THIS_FILE = fopen(target, "w"); + if (!THIS_FILE) + { + GB.Error("Unable to open file for writing: &1", target); + return; + } + } + + THIS->method = 0; + + http_initialize_curl_handle(_object, custom_headers); + + curl_easy_setopt(THIS_CURL, method, 1); + + if (THIS_HTTP->sent_headers) + { + for(i = 0; i < GB.Array.Count(THIS_HTTP->sent_headers); i++) + headers = curl_slist_append(headers, *(char **)GB.Array.Get(THIS_HTTP->sent_headers, i)); + } + + curl_easy_setopt(THIS_CURL, CURLOPT_HTTPHEADER, headers); + CURL_set_progress(THIS, TRUE, NULL); + + if (THIS->async) + { + CURL_start_post(THIS); + return; + } + + CURL_manage_error(_object, curl_easy_perform(THIS_CURL)); +} + + +static void http_send(void *_object, int type, char *sContent, char *sData, int lendata, GB_ARRAY custom_headers, char *target) +{ + int mylen; + struct curl_slist *headers = NULL; + int i; + + if (check_request(_object, sContent, sData, lendata)) + return; + + if (!target) + target = THIS->target; + + if (target && *target) + { + target = GB.FileName(target, 0); + THIS_FILE = fopen(target, "w"); + if (!THIS_FILE) + { + GB.Error("Unable to open file for writing: &1", target); + return; + } + } + + http_initialize_curl_handle(_object, custom_headers); + + if (type & SEND_FILE) + { + if (GB.LoadFile(sData, lendata, &THIS_HTTP->data, &mylen)) + return; + + THIS_HTTP->len_data = mylen; + THIS_HTTP->send_file = TRUE; + } + else + { + THIS_HTTP->send_file = FALSE; + THIS_HTTP->len_data = lendata; + + if (lendata) + { + GB.Alloc((void*)&THIS_HTTP->data, lendata + 1); + strncpy(THIS_HTTP->data, sData, lendata); + } + else + THIS_HTTP->data = NULL; + + } + + THIS_HTTP->len_sent = 0; + + mylen = strlen(sContent) + strlen("Content-Type: ") + 1; + GB.Alloc((void*)&THIS_HTTP->sContentType, mylen); + + THIS_HTTP->sContentType[0] = 0; + strcpy(THIS_HTTP->sContentType, "Content-Type: " ); + strcat(THIS_HTTP->sContentType, sContent); + + THIS->method = 1; + headers = curl_slist_append(headers, THIS_HTTP->sContentType); + + if (THIS_HTTP->sent_headers) + { + for (i = 0; i < GB.Array.Count(THIS_HTTP->sent_headers); i++) + headers = curl_slist_append(headers, *(char **)GB.Array.Get(THIS_HTTP->sent_headers, i)); + } + + curl_easy_setopt(THIS_CURL, CURLOPT_HTTPHEADER, headers); + + if (type == SEND_PUT) + { + curl_easy_setopt(THIS_CURL, CURLOPT_INFILESIZE_LARGE, (curl_off_t)lendata); + curl_easy_setopt(THIS_CURL, CURLOPT_UPLOAD, 1); + } + else // SEND_POST + { + curl_easy_setopt(THIS_CURL, CURLOPT_POSTFIELDSIZE, lendata); + curl_easy_setopt(THIS_CURL, CURLOPT_POSTFIELDS, NULL); + } + + curl_easy_setopt(THIS_CURL, CURLOPT_READFUNCTION, http_read_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_READDATA, _object); + + CURL_set_progress(THIS, TRUE, http_fix_progress_cb); + + if (THIS->async) + { + CURL_start_post(THIS); + return; + } + + CURL_manage_error(THIS, curl_easy_perform(THIS_CURL)); +} + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(HttpClient_UpdateCookies) + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS_HTTP->updatecookies); + return; + } + + if (THIS_STATUS > 0) + { + GB.Error ("UpdateCookies property can not be changed if the client is active"); + return; + } + + if (VPROP(GB_BOOLEAN)) + THIS_HTTP->updatecookies=1; + else + THIS_HTTP->updatecookies=0; + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_CookiesFile) + + const char *file; + + if (READ_PROPERTY) + { + GB.ReturnString(THIS_HTTP->cookiesfile); + return; + } + + if (CURL_check_active(THIS)) + return; + + if (THIS_HTTP->cookiesfile) + GB.FreeString(&THIS_HTTP->cookiesfile); + + file = GB.FileName(PSTRING(), PLENGTH()); + + if (file) + THIS_HTTP->cookiesfile = GB.NewZeroString(file); + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_Auth) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS_HTTP->auth); + return; + } + + if (CURL_check_active(THIS)) + return; + + if (CURL_user_set_auth(&THIS->user, VPROP(GB_INTEGER))) + GB.Error ("Unknown authentication method"); + else + THIS_HTTP->auth = VPROP(GB_INTEGER); + + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_UserAgent) + + if (READ_PROPERTY) + GB.ReturnString(THIS_HTTP->sUserAgent); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &THIS_HTTP->sUserAgent); + } + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_Encoding) + + if (READ_PROPERTY) + GB.ReturnString(THIS_HTTP->encoding); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &THIS_HTTP->encoding); + } + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_ReturnCode) + + http_parse_header(THIS_HTTP); + GB.ReturnInteger(THIS_HTTP->return_code); + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_ReturnString) + + http_parse_header(THIS_HTTP); + GB.ReturnString(THIS_HTTP->return_string); + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_Headers) + + http_parse_header(THIS_HTTP); + GB.ReturnObject(THIS_HTTP->headers); + +END_PROPERTY + + +BEGIN_METHOD_VOID(HttpClient_new) + + THIS_URL = GB.NewZeroString("http://127.0.0.1:80"); + THIS_HTTP->sUserAgent = GB.NewZeroString("Gambas/" GAMBAS_FULL_VERSION_STRING " (gb.net.curl; " SYSTEM ")"); + THIS->async = TRUE; + +END_METHOD + + +BEGIN_METHOD_VOID(HttpClient_free) + + //CURL_stop(_object); + http_reset(THIS); + + GB.FreeString(&THIS_HTTP->sUserAgent); + GB.FreeString(&THIS_HTTP->encoding); + GB.FreeString(&THIS_HTTP->cookiesfile); + GB.FreeString(&THIS_HTTP->return_string); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Get, GB_OBJECT headers; GB_STRING target) + + http_get(THIS, VARGOPT(headers, 0), MISSING(target) ? NULL : GB.ToZeroString(ARG(target)), CURLOPT_HTTPGET); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Head, GB_OBJECT headers) + + http_get(THIS, VARGOPT(headers, 0), NULL, CURLOPT_NOBODY); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Post, GB_STRING contentType; GB_STRING data; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_POST, GB.ToZeroString(ARG(contentType)), STRING(data), LENGTH(data), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD(HttpClient_PostFile, GB_STRING contentType; GB_STRING file; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_POST | SEND_FILE, GB.ToZeroString(ARG(contentType)), STRING(file), LENGTH(file), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Put, GB_STRING contentType; GB_STRING data; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_PUT, GB.ToZeroString(ARG(contentType)), STRING(data), LENGTH(data), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD(HttpClient_PutFile, GB_STRING contentType; GB_STRING file; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_PUT | SEND_FILE, GB.ToZeroString(ARG(contentType)), STRING(file), LENGTH(file), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD_VOID(HttpClient_Stop) + + CURL_stop(THIS); + http_reset(_object); + + GB.Ref(THIS); + CURL_raise_cancel(THIS); + +END_METHOD + +#define COPY_STRING(_field) \ +{ \ + GB.FreeString(&THIS_HTTP->_field); \ + THIS_HTTP->_field = from->_field; \ + if (THIS_HTTP->_field) THIS_HTTP->_field = GB.NewString(THIS_HTTP->_field, GB.StringLength(THIS_HTTP->_field)); \ +} + +BEGIN_METHOD(HttpClient_CopyFrom, GB_OBJECT from) + + CHTTPCLIENT *from = (CHTTPCLIENT *)VARG(from); + + if (GB.CheckObject(from)) + return; + + if (CURL_copy_from((CCURL *)THIS, (CCURL *)from)) + return; + + THIS_HTTP->updatecookies = from->updatecookies; + THIS_HTTP->auth = from->auth; + COPY_STRING(sUserAgent); + COPY_STRING(encoding); + COPY_STRING(cookiesfile); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Download, GB_STRING url; GB_OBJECT headers) + + _object = GB.New(GB.FindClass("HttpClient"), NULL, NULL); + + GB.Ref(THIS); + THIS->async = FALSE; + if (CURL_set_url(THIS, STRING(url), LENGTH(url))) + return; + + http_get(THIS, VARGOPT(headers, NULL), NULL, CURLOPT_HTTPGET); + GB.ReturnString(THIS->data); + GB.Unref(POINTER(&_object)); + +END_METHOD + + +BEGIN_PROPERTY(HttpClient_Redirect) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_HTTP->redirect); + else + THIS_HTTP->redirect = VPROP(GB_BOOLEAN); + +END_PROPERTY + +GB_DESC HttpClientDesc[] = +{ + GB_DECLARE("HttpClient", sizeof(CHTTPCLIENT)), + + GB_INHERITS("Curl"), + + GB_METHOD("_new", NULL, HttpClient_new, NULL), + GB_METHOD("_free", NULL, HttpClient_free, NULL), + GB_METHOD("Stop", NULL, HttpClient_Stop, NULL), + GB_METHOD("Get", NULL, HttpClient_Get, "[(Headers)String[];(TargetFile)s]"), + GB_METHOD("Head", NULL, HttpClient_Head, "[(Headers)String[]]"), + GB_METHOD("Post", NULL, HttpClient_Post, "(ContentType)s(Data)s[(Headers)String[];(TargetFile)s]"), + GB_METHOD("Put", NULL, HttpClient_Put, "(ContentType)s(Data)s[(Headers)String[];(TargetFile)s]"), + GB_METHOD("PostFile", NULL, HttpClient_PostFile, "(ContentType)s(Path)s[(Headers)String[];(TargetFile)s]"), + GB_METHOD("PutFile", NULL, HttpClient_PutFile, "(ContentType)s(Path)s[(Headers)String[];(TargetFile)s]"), + + GB_PROPERTY("Auth", "i", HttpClient_Auth), + GB_PROPERTY("CookiesFile", "s",HttpClient_CookiesFile), + GB_PROPERTY("UpdateCookies", "b",HttpClient_UpdateCookies), + GB_PROPERTY_READ("Headers", "String[]", HttpClient_Headers), + GB_PROPERTY("UserAgent", "s", HttpClient_UserAgent), + GB_PROPERTY("Encoding", "s", HttpClient_Encoding), + GB_PROPERTY("Redirect", "b", HttpClient_Redirect), + + GB_PROPERTY_READ("Code", "i", HttpClient_ReturnCode), + GB_PROPERTY_READ("Reason", "s", HttpClient_ReturnString), + + GB_METHOD("CopyFrom", NULL, HttpClient_CopyFrom, "(HttpClient)Source"), + + GB_STATIC_METHOD("Download", "s", HttpClient_Download, "(URL)s[(Headers)String[];]"), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", HTTP_PROPERTIES), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; diff --git a/gb.net.curl/src/CHttpClient.h b/gb.net.curl/src/CHttpClient.h new file mode 100644 index 00000000..596803a8 --- /dev/null +++ b/gb.net.curl/src/CHttpClient.h @@ -0,0 +1,77 @@ +/*************************************************************************** + + CHttpClient.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CHTTPCLIENT_H +#define __CHTTPCLIENT_H + +#include "gambas.h" +#include "gbcurl.h" +#include "CCurl.h" +#include "CProxy.h" +#include +#include + +#ifndef __CHTTPCLIENT_C + + +extern GB_DESC HttpClientDesc[]; +extern GB_STREAM_DESC HttpStream; + +#else + +#define THIS_HTTP ((CHTTPCLIENT *)_object) + +#endif + +typedef + struct { + CCURL curl; + int auth; + char *cookiesfile; + int updatecookies; + char *sContentType; + char *sUserAgent; + char *encoding; + GB_ARRAY headers; + GB_ARRAY sent_headers; + int return_code; + char *return_string; + char *data; + size_t len_data; + size_t len_sent; + unsigned send_file : 1; + unsigned redirect : 1; + } + CHTTPCLIENT; + + +/*int http_find_info (CURL *curlfind); +int http_header_curl(void *buffer, size_t size, size_t nmemb, void *c_handle); +int http_write_curl(void *buffer, size_t size, size_t nmemb, void *c_handle); +void http_parse_header(CHTTPCLIENT *mythis); +void http_reset(void *_object); +void http_stop(void *_object);*/ + +#define HTTP_PROPERTIES "URL=127.0.0.1:80,User,Password,Auth=0,Async=True,Timeout=0,CookiesFile,UpdateCookies=FALSE,UserAgent=Gambas (gb.net.curl) HTTP/1.0" + +#endif diff --git a/gb.net.curl/src/CNet.c b/gb.net.curl/src/CNet.c new file mode 100644 index 00000000..c248b1aa --- /dev/null +++ b/gb.net.curl/src/CNet.c @@ -0,0 +1,201 @@ +/*************************************************************************** + + CNet.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CNET_C +#include "main.h" +#include +#include + +#include "CNet.h" + +#define GBCURL(x) (-(1000+x)) + +// Constants not defined in old versions of libcurl + +#ifndef CURLE_FTP_PRET_FAILED +#define CURLE_FTP_PRET_FAILED 84 +#endif +#ifndef CURLE_FTP_BAD_FILE_LIST +#define CURLE_FTP_BAD_FILE_LIST 87 +#endif +#ifndef CURLE_SSL_CRL_BADFILE +#define CURLE_SSL_CRL_BADFILE 82 /* was on 7.19.0 */ +#endif +#ifndef CURLAUTH_DIGEST_IE +#define CURLAUTH_DIGEST_IE (1<<4) /* Digest with IE flavour */ +#endif +#ifndef CURLE_SSL_ISSUER_ERROR +#define CURLE_SSL_ISSUER_ERROR 83 +#endif +#ifndef CURLE_CHUNK_FAILED +#define CURLE_CHUNK_FAILED 88 +#endif + +#ifndef CURLAUTH_NONE +#define CURLAUTH_NONE ((unsigned long)0) /* nothing */ +#define CURLAUTH_BASIC (((unsigned long)1)<<0) /* Basic (default) */ +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) /* Digest */ +#define CURLAUTH_GSSNEGOTIATE (((unsigned long)1)<<2) /* GSS-Negotiate */ +#define CURLAUTH_NTLM (((unsigned long)1)<<3) /* NTLM */ +#define CURLAUTH_ANY ~0 /* all types set */ +#define CURLAUTH_ANYSAFE (~CURLAUTH_BASIC) +#endif + +#ifndef CURLAUTH_DIGEST_IE +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#undef CURLAUTH_ANY +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#undef CURLAUTH_ANYSAGE +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) +#endif + +#ifndef CURLAUTH_NTLM_WB +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#endif + +#if LIBCURL_VERSION_NUM < 0x071202 +#define CURLE_AGAIN 81 +#endif + +#if LIBCURL_VERSION_NUM < 0x071300 +#define CURLE_SSL_CRL_BADFILE 82 +#define CURLE_SSL_ISSUER_ERROR 83 +#define CURLE_FTP_PRET_FAILED 84 +#define CURLE_RTSP_CSEQ_ERROR 85 +#define CURLE_RTSP_SESSION_ERROR 86 +#define CURLE_FTP_BAD_FILE_LIST 87 +#define CURLE_CHUNK_FAILED 88 +#endif + +#if LIBCURL_VERSION_NUM < 0x071505 +#define CURLE_NOT_BUILT_IN 4 +#endif + +#if LIBCURL_VERSION_NUM < 0x071800 +#define CURLE_FTP_ACCEPT_FAILED 10 +#define CURLE_FTP_ACCEPT_TIMEOUT 12 +#endif + +GB_DESC CNetDesc[] = +{ + GB_DECLARE("Net", 0), GB_VIRTUAL_CLASS(), + + // Net states used by curl + //GB_CONSTANT("Inactive", "i", 0), + //GB_CONSTANT("ReceivingData","i",4), + //GB_CONSTANT("Connecting", "i", 6), + + GB_CONSTANT ("Synchronous", "i", 0), + GB_CONSTANT ("Asynchronous", "i", 1), + /* net-curl proxies */ + GB_CONSTANT ("ProxyHTTP", "i", CURLPROXY_HTTP), + GB_CONSTANT ("ProxySocks5", "i", CURLPROXY_SOCKS5), + /* net-curl autohorization */ + GB_CONSTANT("AuthNone", "i", CURLAUTH_NONE), + GB_CONSTANT("AuthBasic", "i", CURLAUTH_BASIC), + GB_CONSTANT("AuthNtlm", "i", CURLAUTH_NTLM), + GB_CONSTANT("AuthDigest", "i", CURLAUTH_DIGEST), + GB_CONSTANT("AuthDigestIE", "i", CURLAUTH_DIGEST_IE), + GB_CONSTANT("AuthNtlmWb", "i", CURLAUTH_NTLM_WB), + GB_CONSTANT("AuthGssNegotiate", "i", CURLAUTH_GSSNEGOTIATE), + GB_CONSTANT("AuthAny", "i", CURLAUTH_ANY), + GB_CONSTANT("AuthAnySafe", "i", CURLAUTH_ANYSAFE), + + GB_CONSTANT("UnsupportedProtocol", "i", GBCURL(CURLE_UNSUPPORTED_PROTOCOL)), + GB_CONSTANT("FailedInit", "i", GBCURL(CURLE_FAILED_INIT)), + GB_CONSTANT("URLMalformat", "i", GBCURL(CURLE_URL_MALFORMAT)), + GB_CONSTANT("UnableToResolveProxy", "i", GBCURL(CURLE_COULDNT_RESOLVE_PROXY)), + GB_CONSTANT("UnableToResolveHost", "i", GBCURL(CURLE_COULDNT_RESOLVE_HOST)), + GB_CONSTANT("UnableToConnect", "i", GBCURL(CURLE_COULDNT_CONNECT)), + GB_CONSTANT("FTPWeirdServerReply", "i", GBCURL(CURLE_FTP_WEIRD_SERVER_REPLY)), + GB_CONSTANT("RemoteAccessDenied", "i", GBCURL(CURLE_REMOTE_ACCESS_DENIED)), + GB_CONSTANT("FTPWeirdPassReply", "i", GBCURL(CURLE_FTP_WEIRD_PASS_REPLY)), + GB_CONSTANT("FTPWeirdPasvReply", "i", GBCURL(CURLE_FTP_WEIRD_PASV_REPLY)), + GB_CONSTANT("FTPWeird227Format", "i", GBCURL(CURLE_FTP_WEIRD_227_FORMAT)), + GB_CONSTANT("FTPUnableToGetHost", "i", GBCURL(CURLE_FTP_CANT_GET_HOST)), + GB_CONSTANT("FTPUnableToSetType", "i", GBCURL(CURLE_FTP_COULDNT_SET_TYPE)), + GB_CONSTANT("PartialFile", "i", GBCURL(CURLE_PARTIAL_FILE)), + GB_CONSTANT("FTPUnableToRETRFile", "i", GBCURL(CURLE_FTP_COULDNT_RETR_FILE)), + GB_CONSTANT("QuoteError", "i", GBCURL(CURLE_QUOTE_ERROR)), + GB_CONSTANT("HttpReturnedError", "i", GBCURL(CURLE_HTTP_RETURNED_ERROR)), + GB_CONSTANT("WriteError", "i", GBCURL(CURLE_WRITE_ERROR)), + GB_CONSTANT("UploadFailed", "i", GBCURL(CURLE_UPLOAD_FAILED)), + GB_CONSTANT("ReadError", "i", GBCURL(CURLE_READ_ERROR)), + GB_CONSTANT("OutOfMemory", "i", GBCURL(CURLE_OUT_OF_MEMORY)), + GB_CONSTANT("OperationTimeout", "i", GBCURL(CURLE_OPERATION_TIMEDOUT)), + GB_CONSTANT("FTPPortFailed", "i", GBCURL(CURLE_FTP_PORT_FAILED)), + GB_CONSTANT("FTPUnableToUseRest", "i", GBCURL(CURLE_FTP_COULDNT_USE_REST)), + GB_CONSTANT("RangeError", "i", GBCURL(CURLE_RANGE_ERROR)), + GB_CONSTANT("HTTPPostError", "i", GBCURL(CURLE_HTTP_POST_ERROR)), + GB_CONSTANT("SSLConnectError", "i", GBCURL(CURLE_SSL_CONNECT_ERROR)), + GB_CONSTANT("BadDownloadResume", "i", GBCURL(CURLE_BAD_DOWNLOAD_RESUME)), + //GB_CONSTANT("FileUnableToReadFile", "i", GBCURL(CURLE_FILE_COULDNT_READ_FILE)), + //GB_CONSTANT("LDAPCannotBind", "i", GBCURL(CURLE_LDAP_CANNOT_BIND)), + //GB_CONSTANT("LDAPSearchFailed", "i", GBCURL(CURLE_LDAP_SEARCH_FAILED)), + GB_CONSTANT("FunctionNotFound", "i", GBCURL(CURLE_FUNCTION_NOT_FOUND)), + GB_CONSTANT("AbortedByCallback", "i", GBCURL(CURLE_ABORTED_BY_CALLBACK)), + GB_CONSTANT("BadFunctionArgument", "i", GBCURL(CURLE_BAD_FUNCTION_ARGUMENT)), + GB_CONSTANT("InterfaceFailed", "i", GBCURL(CURLE_INTERFACE_FAILED)), + GB_CONSTANT("TooManyRedirects", "i", GBCURL(CURLE_TOO_MANY_REDIRECTS )), + //GB_CONSTANT("UnknownTelnetOption", "i", GBCURL(CURLE_UNKNOWN_TELNET_OPTION)), + //GB_CONSTANT("TelnetOptionSyntax", "i", GBCURL(CURLE_TELNET_OPTION_SYNTAX)), + GB_CONSTANT("PeerFailedVerification", "i", GBCURL(CURLE_PEER_FAILED_VERIFICATION)), + GB_CONSTANT("GotNothing", "i", GBCURL(CURLE_GOT_NOTHING)), + GB_CONSTANT("SSLEngineNotFound", "i", GBCURL(CURLE_SSL_ENGINE_NOTFOUND)), + GB_CONSTANT("SSLEngineSetFailed", "i", GBCURL(CURLE_SSL_ENGINE_SETFAILED)), + GB_CONSTANT("SendError", "i", GBCURL(CURLE_SEND_ERROR)), + GB_CONSTANT("RecvError", "i", GBCURL(CURLE_RECV_ERROR)), + GB_CONSTANT("SSLCertProblem", "i", GBCURL(CURLE_SSL_CERTPROBLEM)), + GB_CONSTANT("SSLCipher", "i", GBCURL(CURLE_SSL_CIPHER)), + GB_CONSTANT("SSLCacert", "i", GBCURL(CURLE_SSL_CACERT)), + GB_CONSTANT("BadContentEncoding", "i", GBCURL(CURLE_BAD_CONTENT_ENCODING)), + //GB_CONSTANT("LDAPInvalidURL", "i", GBCURL(CURLE_LDAP_INVALID_URL)), + GB_CONSTANT("FileSizeExceeded", "i", GBCURL(CURLE_FILESIZE_EXCEEDED)), + //GB_CONSTANT("UseSSLFailed", "i", GBCURL(CURLE_USE_SSL_FAILED)), + GB_CONSTANT("SendFailRewind", "i", GBCURL(CURLE_SEND_FAIL_REWIND)), + GB_CONSTANT("SSLEngineInitFailed", "i", GBCURL(CURLE_SSL_ENGINE_INITFAILED)), + GB_CONSTANT("LoginDenied", "i", GBCURL(CURLE_LOGIN_DENIED)), + //GB_CONSTANT("TFTPNotFound", "i", GBCURL(CURLE_TFTP_NOTFOUND)), + //GB_CONSTANT("TFTPPerm", "i", GBCURL(CURLE_TFTP_PERM)), + GB_CONSTANT("RemoteDiskFull", "i", GBCURL(CURLE_REMOTE_DISK_FULL)), + //GB_CONSTANT("TFTPIllegal", "i", GBCURL(CURLE_TFTP_ILLEGAL)), + //GB_CONSTANT("TFTPUnknownID", "i", GBCURL(CURLE_TFTP_UNKNOWNID)), + GB_CONSTANT("RemoteFileExists", "i", GBCURL(CURLE_REMOTE_FILE_EXISTS)), + //GB_CONSTANT("TFTPNoSuchUser", "i", GBCURL(CURLE_TFTP_NOSUCHUSER)), + GB_CONSTANT("ConvFailed", "i", GBCURL(CURLE_CONV_FAILED)), + GB_CONSTANT("ConvRequired", "i", GBCURL(CURLE_CONV_REQD)), + GB_CONSTANT("SSLCacertBadFile", "i", GBCURL(CURLE_SSL_CACERT_BADFILE)), + GB_CONSTANT("RemoteFileNotFound", "i", GBCURL(CURLE_REMOTE_FILE_NOT_FOUND)), + //GB_CONSTANT("SSH", "i", GBCURL(CURLE_SSH)), + GB_CONSTANT("SSLShutdownFailed", "i", GBCURL(CURLE_SSL_SHUTDOWN_FAILED)), + //GB_CONSTANT("Again", "i", GBCURL(CURLE_AGAIN)), + GB_CONSTANT("SSLCrlBadfile", "i", GBCURL(CURLE_SSL_CRL_BADFILE)), + GB_CONSTANT("SSLIssuerError", "i", GBCURL(CURLE_SSL_ISSUER_ERROR)), + GB_CONSTANT("FTPPretFailed", "i", GBCURL(CURLE_FTP_PRET_FAILED)), + //GB_CONSTANT("RTSPCSeqError", "i", GBCURL(CURLE_RTSP_CSEQ_ERROR)), + //GB_CONSTANT("RTSPSessionError", "i", GBCURL(CURLE_RTSP_SESSION_ERROR)), + GB_CONSTANT("FTPBadFileList", "i", GBCURL(CURLE_FTP_BAD_FILE_LIST)), + GB_CONSTANT("ChunkFailed", "i", GBCURL(CURLE_CHUNK_FAILED)), + + GB_END_DECLARE +}; diff --git a/gb.net.curl/src/CNet.h b/gb.net.curl/src/CNet.h new file mode 100644 index 00000000..de0e549c --- /dev/null +++ b/gb.net.curl/src/CNet.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + CNet.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CNET_H +#define __CNET_H + +#include "gambas.h" + +#ifndef __CNET_C + +extern GB_DESC CNetDesc[]; + +#else + +#define THIS ((CNET *)_object) + +#endif + +enum +{ + NET_INACTIVE = 0, + NET_ACTIVE = 1, + NET_RECEIVING_DATA = 4, + NET_CONNECTING = 6, +}; + +#endif diff --git a/gb.net.curl/src/CProxy.c b/gb.net.curl/src/CProxy.c new file mode 100644 index 00000000..15004fe7 --- /dev/null +++ b/gb.net.curl/src/CProxy.c @@ -0,0 +1,147 @@ +/*************************************************************************** + + CProxy.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPROXY_C +#include +#include +#include +#include "main.h" + +#include "CProxy.h" + +static bool check_active(CCURL *_object) +{ + if (THIS && THIS->status > 0) + { + GB.Error("Proxy cannot be modified while client is active"); + return TRUE; + } + else + return FALSE; +} + +#define GET_PROXY() CURL_PROXY *proxy = _object ? &THIS->proxy : &CURL_default_proxy + +BEGIN_PROPERTY(CurlProxy_Auth) + + GET_PROXY(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy->auth); + else + { + if (check_active(THIS)) + return; + + if (CURL_proxy_set_auth(proxy, VPROP(GB_INTEGER))) + GB.Error("Unknown authentication method"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CurlProxy_Type) + + GET_PROXY(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy->type); + else + { + if (check_active(THIS)) + return; + + if (CURL_proxy_set_type(proxy, VPROP(GB_INTEGER))) + GB.Error("Unknown proxy type"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CurlProxy_User) + + GET_PROXY(); + + if (READ_PROPERTY) + GB.ReturnString(proxy->user); + else + { + if (check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &proxy->user); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CurlProxy_Password) + + GET_PROXY(); + + if (READ_PROPERTY) + GB.ReturnString(proxy->pwd); + else + { + if (check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &proxy->pwd); + } + +END_PROPERTY + + +BEGIN_PROPERTY (CurlProxy_Host) + + GET_PROXY(); + + if (READ_PROPERTY) + GB.ReturnString(proxy->host); + else + { + if (check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &proxy->host); + } + +END_PROPERTY + + +GB_DESC CProxyDesc[] = +{ + GB_DECLARE_VIRTUAL(".Curl.Proxy"), + + GB_PROPERTY("Host", "s", CurlProxy_Host), + GB_PROPERTY("User", "s", CurlProxy_User), + GB_PROPERTY("Password", "s", CurlProxy_Password), + GB_PROPERTY("Type", "i", CurlProxy_Type), + GB_PROPERTY("Auth", "i", CurlProxy_Auth), + + GB_END_DECLARE +}; + + + + diff --git a/gb.net.curl/src/CProxy.h b/gb.net.curl/src/CProxy.h new file mode 100644 index 00000000..ca17eb53 --- /dev/null +++ b/gb.net.curl/src/CProxy.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + CProxy.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPROXY_H +#define __CPROXY_H + +#include "gambas.h" +#include "gbcurl.h" + +#include "CCurl.h" + +#ifndef __CPROXY_C + +extern GB_DESC CProxyDesc[]; + +#endif + +#endif diff --git a/gb.net.curl/src/Makefile.am b/gb.net.curl/src/Makefile.am new file mode 100644 index 00000000..f2738cdb --- /dev/null +++ b/gb.net.curl/src/Makefile.am @@ -0,0 +1,21 @@ +COMPONENT = gb.net.curl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.net.curl.la + +gb_net_curl_la_LIBADD = @THREAD_LIB@ @CURL_LIB@ +gb_net_curl_la_LDFLAGS = -module @LD_FLAGS@ @CURL_LDFLAGS@ +gb_net_curl_la_CPPFLAGS = @THREAD_INC@ @CURL_INC@ + +gb_net_curl_la_SOURCES = \ + gbcurl.h gbcurl.c \ + main.h main.c \ + CCurl.h CCurl.c \ + CHttpClient.h CHttpClient.c \ + CFtpClient.h CFtpClient.c \ + CNet.h CNet.c \ + CProxy.h CProxy.c + + + + diff --git a/gb.net.curl/src/gb.net.curl.component b/gb.net.curl/src/gb.net.curl.component new file mode 100644 index 00000000..4c35d9d5 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl.component @@ -0,0 +1,12 @@ +[Component] +Key=gb.net.curl +Require=gb.net +Name=Network high-level protocols management +Name[es]=Manejo de protocolos de alto nivel de red +Name[fr]=Gestion des protocoles réseaux avancés +Name[pl]=Zarządzanie sieciowymi protokołami wysokiego poziomu +Name[tr]=Yüksek-seviye ağ protokolleri yönetimi +Author=Daniel Campos Fernández + +[Network] +Virtual=HttpClient,FtpClient diff --git a/gb.net.curl/src/gb.net.curl/.directory b/gb.net.curl/src/gb.net.curl/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.net.curl/src/gb.net.curl/.icon.png b/gb.net.curl/src/gb.net.curl/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 Download.Finish Then + bKeepRunning = True + Endif + hDownload = Null + Next + + If Not hDownload Then + Wait + If bKeepRunning Then + $hRunTimer.Trigger + Else + $bStart = False + Raise End + Endif + Return + Endif + + hDownload._State = Download.Downloading + + hClient = hDownload._GetClient() + hClient.Tag = hDownload._Key + hClient.TargetFile = Temp$() + Object.Attach(hClient, Me, "Client") + + hObject = hClient + hObject.Get() + + $aClient.Add(hClient) + + Wend + +End + +Public Sub Start() + + If Not $bStart Then Raise Progress + $bStart = True + $fSpeedTimer = Timer + Run() + +End + + +Public Sub Stop() + + Dim hClient As Object + + $bStart = False + For Each hClient In $aClient + hClient.Stop() + Next + $fProgress = 0 + $iSize = 0 + +End + +Private Sub DownloadIsReady(hDownload As Download) + + If hDownload._State = Download.Ready Then Return + hDownload._State = Download.Ready + Raise Size(hDownload._Key) + UpdateProgress + $hRunTimer.Trigger + +End + + +Public Sub GetSize_Progress() + + Dim hClient As Object = Last + Dim hDownload As Download + + If hClient.TotalDownloaded > 0 Then + hDownload = $cDownload[hClient.Tag] + hDownload._Size = hClient.TotalDownloaded + hClient.Stop() + DownloadIsReady(hDownload) + Endif + +End + +Public Sub GetSize_Error() + + Dim hClient As Object = Last + Dim hDownload As Download + + hDownload = $cDownload[hClient.Tag] + DownloadIsReady(hDownload) + +End + +Public Sub GetSize_Finished() + + GetSize_Error + +End + + +Public Sub Run_Timer() + + Run() + +End + +Public Sub Client_Connect() + + ' Dim hDownload As Download = $cDownload[Last.Tag] + ' Raise Message(hDownload._Key, Subst(("Connected to &1"), hDownload._GetHost())) + Raise Connect + +End + +Private Sub UpdateProgress() + + Dim hDownload As Download + Dim iSize As Long + Dim fProgress As Float + Dim iTotalSize As Long + Dim bDownloadWithSize As Boolean + Dim iFullSize As Long + Dim bRaiseProgress As Boolean + + For Each hDownload In $cDownload + iFullSize += hDownload._Current + If hDownload._Size Then + iSize += hDownload._Current + iTotalSize += hDownload._Size + If hDownload.State = Download.Downloading Then + bDownloadWithSize = True + Endif + Endif + Next + + $iSize = iFullSize + + If (Timer - $fSpeedTimer) >= 1 Then + Try $iSpeed = (iFullSize - $iSpeedSize) / (Timer - $fSpeedTimer) + $iSpeedSize = iFullSize + $fSpeedTimer = Timer + bRaiseProgress = True + Endif + + If iTotalSize And If bDownloadWithSize Then + fProgress = Round(iSize / iTotalSize, -3) + Else + fProgress = 0 + Endif + + If bRaiseProgress Or If fProgress <> $fProgress Then + $fProgress = fProgress + Raise Progress + Endif + +End + +Public Sub Client_Progress() + + Dim hClient As Curl = Last + Dim hDownload As Download = $cDownload[hClient.Tag] + + hDownload._Current = hClient.Downloaded + UpdateProgress + +End + +Private Function Progress_Read() As Float + + Return $fProgress + +End + +Private Sub Finish(hClient As Curl, iState As Integer) + + Dim sKey As String + Dim hDownload As Download + + sKey = hClient.Tag + + hDownload = $cDownload[sKey] + hDownload._State = iState + hDownload._ErrorText = hClient.ErrorText + + $aClient.Remove($aClient.FindByRef(hClient)) + + $hRunTimer.Trigger + +End + +Public Sub Client_Cancel() + + Dim hClient As Curl = Last + Finish(hClient, Download.Error) + +End + +Public Sub Client_Error() + + Dim hClient As Curl = Last + Raise Error(hClient.Tag) + Finish(Last, Download.Error) + Stop() + +End + + +Public Sub Client_Finished() + + Dim hClient As Curl = Last + $cDownload[hClient.Tag]._Path = hClient.TargetFile + Finish(hClient, Download.Finish) + Raise Finish(hClient.Tag) + +End + +Private Function MaxClient_Read() As Integer + + Return $nClientMax + +End + +Private Sub MaxClient_Write(Value As Integer) + + If Value < 1 Or If Value > 32 Then Error.Raise("Bad argument") + If $nClientMax = Value Then Return + $nClientMax = Value + $hRunTimer.Trigger + +End + +Public Sub _get(Key As String) As Download + + Return $cDownload[Key] + +End + +Private Function Size_Read() As Long + + Return $iSize + +End + +Private Function Speed_Read() As Integer + + Return $iSpeed + +End + +Private Function Count_Read() As Integer + + Return $cDownload.Count + +End + +Private Function State_Read() As Integer + + Dim hDownload As Download + Dim iState As Integer + + For Each hDownload In $cDownload + iState = Max(iState, hDownload.State) + Next + + Return iState + +End diff --git a/gb.net.curl/src/gb.net.curl/.src/HttpForm.class b/gb.net.curl/src/gb.net.curl/.src/HttpForm.class new file mode 100644 index 00000000..b5f91264 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl/.src/HttpForm.class @@ -0,0 +1,124 @@ +' Gambas class file + +Export + +Inherits HttpClient + +Public Const _IsControl As Boolean = False + +Private $cVal As New Collection +Private $aField As New String[] +Private $cFile As New Collection + +Public Sub Add(Field As String, Value As String) + + If Not $aField.Exist(Field) Then $aField.Add(Field) + $cVal[Field] = Value + +End + +Public Sub AddFile(Field As String, Path As String, Optional Name As String) + + If Left(Path) <> "/" Then Path = ".." &/ Path + If Not Exist(Path) Then Error.Raise("Path does not exist") + If IsDir(Path) Then Error.Raise("Path is a directory") + + If Not Name Then Name = File.Name(Path) + Add(Field, Name) + $cFile[Field] = Path + +End + +Public Sub Remove(Field As String) + + Dim iPos As Integer + + iPos = $aField.Find(Field) + If iPos < 0 Then Return + + $aField.Remove(iPos) + $cVal.Remove(Field) + $cFile.Remove(Field) + +End + +Public Sub _get(Field As String) As String + + Return $cVal[Field] + +End + +Public Sub _put(Value As String, Field As String) + + Add(Field, Value) + +End + +Public Sub Clear() + + $cVal.Clear + $aField.Clear + +End + + +Public Sub Submit() + + Dim sBoundary As String + Dim sPath As Variant + Dim I As Integer + Dim sPost As String + Dim hPost As File + Dim sField As String + + Do + + sBoundary = "------" + For I = 1 To 4 + sBoundary &= Hex$(Int(Rnd(65536)), 4) + Next + + For Each sPath In $cFile + If InStr(File.Load(sPath), sBoundary) Then Continue + Next + + Break + + Loop + + sPost = Temp$() + + hPost = Open sPost For Create + hPost.EndOfLine = gb.Windows + + For Each sField In $aField + + Print #hPost, "--"; sBoundary + + sPath = $cFile[sField] + + If sPath Then + + Print #hPost, "Content-Disposition: form-data; name=\""; sField; "\"; filename=\""; $cVal[sField]; "\"" + Print #hPost, "Content-Type: application/octet-stream" + Print #hPost + Print #hPost, File.Load(sPath) + + Else + + Print #hPost, "Content-Disposition: form-data; name=\""; sField; "\"" + Print #hPost, "Content-Type: text/plain; charset=\"utf-8\"" + Print #hPost + Print #hPost, $cVal[sField] + + Endif + + Next + + Print #hPost, "--"; sBoundary; "--" + + hPost.Close + + Me.PostFile("multipart/form-data; boundary=" & sBoundary, sPost, ["Content-Length: " & CStr(Stat(sPost).Size)]) + +End diff --git a/gb.net.curl/src/gb.net.curl/.src/MMain.module b/gb.net.curl/src/gb.net.curl/.src/MMain.module new file mode 100644 index 00000000..0dd149f7 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl/.src/MMain.module @@ -0,0 +1,42 @@ +' Gambas module file + +Private $hManager As DownloadManager + +Public Sub Main() + + $hManager = New DownloadManager As "Manager" + $hManager.MaxClient = 3 + + $hManager.Add("https://gitlab.com/gambas/gambas/-/archive/master/gambas-master.tar.bz2") + $hManager.Add("http://ipv4.download.thinkbroadband.com/512MB.zip") + '$hManager.Add("https://gitlab.com/gambas/gambas/-/archive/master/gambas-master.tar.gz") + '$hManager.Add("http://gambaswiki.org/zorglub.txt") + $hManager.Add("http://gambaswiki.org/gambas-wiki.tar.bz2") + $hManager.Start() + +End + +Public Sub Manager_Progress() + + Print Format($hManager.Progress * 100, "##0"); "% "; Format($hManager.Speed / 1048576, "#.#"); " MB/s"; Space$(10); "\r"; + Flush + +End + +Public Sub Manager_Message(Key As String, Text As String) + + Print "["; Key; "] "; Text + +End + +Public Sub Manager_Finish(Key As String) + + Print "["; Key; "] ==> "; $hManager[Key].Path + +End + +Public Sub Manager_Size(Key As String) + + Print "["; Key; "] size = "; $hManager[Key].Size + +End diff --git a/gb.net.curl/src/gbcurl.c b/gb.net.curl/src/gbcurl.c new file mode 100644 index 00000000..dd33c9a8 --- /dev/null +++ b/gb.net.curl/src/gbcurl.c @@ -0,0 +1,379 @@ +/*************************************************************************** + + gbcurl.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_CURL_C + +/***************************** + NOTE THAT : + libcurl <= 7.10.7 lacks CURLE_LDAP_INVALID_URL and CURLE_FILESIZE_EXCEEDED constants + libcurl <= 7.10.6 lacks proxy authentication support + libcurl <= 7.10.5 lacks user authentication support + *****************************/ +#include +#include +#include +#include "gbcurl.h" +#include "CCurl.h" + +CURL_PROXY CURL_default_proxy = { CURLPROXY_HTTP, CURLAUTH_NONE, NULL, NULL, NULL, NULL }; + +static char *_protocols[] = { "ftp://", "ftps://", "http://", "https://", NULL }; + +static void warning(const char *msg) +{ + fprintf(stderr, "gb.net.curl: warning: %s\n", msg); +} + +#ifndef CURLAUTH_NONE +static void warning_auth(void) +{ + warning("This component was compiled without authentication support."); + warning("Use libcurl version 7.10.7 or later."); +} +#define CURLAUTH_NONE 0 +#define LACKS_AUTH +#warning ####################################################################### +#warning COMPILING WITHOUT AUTHENTICATION SUPPORT - YOU MUST USE LIBCURL>=7.10.6 +#warning ####################################################################### +#endif + + +static void warning_proxy_auth(void) +{ + warning("This component was compiled without proxy authentication support."); + warning("Use libcurl version 7.10.8 or later."); +} + + +static char *CURL_get_protocol(char *url, char *default_protocol) +{ + char **p; + char *pos; + + for (p = _protocols; *p; p++) + { + if (!strncmp(url, *p, strlen(*p))) + return *p; + } + + pos = strstr(url, "://"); + if (pos) + return "?"; + + return default_protocol; +} + +bool CURL_set_url(void *_object, const char *src, int len) +{ + char *url, *tmp, *protocol; + + if (len == 0) + goto UNKNOWN_PROTOCOL; + + url = GB.NewString(src, len); + + if (GB.Is(THIS, GB.FindClass("FtpClient"))) + { + protocol = CURL_get_protocol(url, "ftp://"); + if (strcmp(protocol, "ftp://") && strcmp(protocol, "ftps://")) + goto UNKNOWN_PROTOCOL; + } + else if (GB.Is(THIS, GB.FindClass("HttpClient"))) + { + protocol = CURL_get_protocol(url, "http://"); + if (strcmp(protocol, "http://") && strcmp(protocol, "https://")) + goto UNKNOWN_PROTOCOL; + } + else + goto UNKNOWN_PROTOCOL; + + if (strncmp(url, protocol, strlen(protocol))) + { + tmp = GB.NewZeroString(protocol); + tmp = GB.AddString(tmp, url, GB.StringLength(url)); + GB.FreeString(&url); + url = tmp; + } + + GB.FreeString(&THIS_URL); + THIS_URL = url; + return FALSE; + +UNKNOWN_PROTOCOL: + + GB.Error("Unknown protocol"); + return TRUE; +} + +#if 0 +void Adv_correct_url(char **buf,char *protocol) +{ + char *buftmp; + int len; + int myloop,myloop2; + int pos=-1; + int myok=1; + + len=strlen(*buf); + for (myloop=0;myloop0x39) ) + { + myok=0; + break; + } + } + if (!myok) pos=myloop; + break; + } + } + } + } + + myok=0; + + if (pos==-1) + { + + GB.Alloc((void**)POINTER(&buftmp),len+1); + strcpy(buftmp,*buf); + GB.Free((void**)POINTER(buf)); + GB.Alloc((void**)POINTER(buf),len+strlen(protocol)+1); + } + else + { + GB.Alloc((void**)POINTER(&buftmp),(len-pos)+1); + strcpy(buftmp,*buf+pos+1); + GB.Free((void**)POINTER(buf)); + GB.Alloc((void**)POINTER(buf),strlen(buftmp)+strlen(protocol)+1); + } + + strcpy(*buf,protocol); + if (strlen(buftmp)>=2) + { + if ( buftmp[0]=='/') myok++; + if ( buftmp[1]=='/') myok++; + } + strcat(*buf,buftmp+myok); + GB.Free((void**)POINTER(&buftmp)); +} +#endif + +bool CURL_check_userpwd(CURL_USER *user) +{ + char *tmp = NULL; + bool ret; + + if (user->user || user->pwd) + { + tmp = GB.AddString(tmp, user->user, 0); + tmp = GB.AddChar(tmp, ':'); + tmp = GB.AddString(tmp, user->pwd, 0); + } + + if (tmp && user->userpwd) + ret = (strcmp(tmp, user->userpwd) != 0); + else + ret = (tmp == user->userpwd); + + GB.FreeString(&tmp); + return ret; +} + +/***************************************************************************/ + +void CURL_proxy_init(CURL_PROXY *proxy) +{ + *proxy = CURL_default_proxy; + GB.RefString(proxy->host); + GB.RefString(proxy->user); + GB.RefString(proxy->pwd); + GB.RefString(proxy->userpwd); +} + +void CURL_proxy_clear(CURL_PROXY *proxy) +{ + GB.FreeString(&proxy->host); + GB.FreeString(&proxy->user); + GB.FreeString(&proxy->pwd); + GB.FreeString(&proxy->userpwd); +} + +void CURL_default_proxy_clear() +{ + CURL_proxy_clear(&CURL_default_proxy); +} + + +void CURL_proxy_set(CURL_PROXY *proxy, CURL *curl) +{ + GB.FreeString(&proxy->userpwd); + + if (proxy->user || proxy->pwd) + { + proxy->userpwd = GB.AddString(proxy->userpwd, proxy->user, 0); + proxy->userpwd = GB.AddChar(proxy->userpwd, ':'); + proxy->userpwd = GB.AddString(proxy->userpwd, proxy->pwd, 0); + } + + if (!proxy->host) + { + curl_easy_setopt(curl, CURLOPT_PROXY, NULL); + if ( LIBCURL_VERSION_NUM >= 0x070a08 ) + curl_easy_setopt(curl, 111, CURLAUTH_NONE); + return; + } + + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy->type); + curl_easy_setopt(curl, CURLOPT_PROXY, proxy->host); + if ( LIBCURL_VERSION_NUM >= 0x070a08 ) + { + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy->userpwd); + curl_easy_setopt(curl, 111, proxy->auth); + } + else + warning_proxy_auth(); +} + +bool CURL_proxy_set_auth(CURL_PROXY *proxy, int auth) +{ + #ifdef LACKS_AUTH + return FALSE; + #else + if (LIBCURL_VERSION_NUM < 0x070a08) + { + if (auth) + warning_proxy_auth(); + return FALSE; + } + + switch (auth) + { + case CURLAUTH_NONE: + case CURLAUTH_BASIC: + case CURLAUTH_NTLM: + proxy->auth=auth; + return FALSE; + default: + return TRUE; + } + #endif +} + +bool CURL_proxy_set_type(CURL_PROXY *proxy, int type) +{ + switch (type) + { + case CURLPROXY_HTTP: + case CURLPROXY_SOCKS5: + proxy->type = type; + return FALSE; + default: + return TRUE; + } +} + +/***************************************************************************/ + +void CURL_user_init(CURL_USER *user) +{ + user->auth = CURLAUTH_NONE; + user->user = NULL; + user->pwd = NULL; + user->userpwd = NULL; +} + + +void CURL_user_clear(CURL_USER *user) +{ + GB.FreeString(&user->user); + GB.FreeString(&user->pwd); + GB.FreeString(&user->userpwd); +} + + +void CURL_user_set(CURL_USER *user, CURL *curl) +{ + #ifdef LACKS_AUTH + return; + #else + + if (user->auth == CURLAUTH_NONE) + { + curl_easy_setopt(curl, CURLOPT_USERPWD, NULL); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NONE); + return; + } + + GB.FreeString(&user->userpwd); + user->userpwd = GB.AddString(user->userpwd, user->user, 0); + user->userpwd = GB.AddChar(user->userpwd, ':'); + user->userpwd = GB.AddString(user->userpwd, user->pwd, 0); + + curl_easy_setopt(curl, CURLOPT_USERPWD, user->userpwd); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, user->auth); + + #endif +} + +bool CURL_user_set_auth(CURL_USER *user, int auth) +{ + #ifdef LACKS_AUTH + if (auth) + warning_auth(); + return FALSE; + #else + switch (auth) + { + case CURLAUTH_NONE: + case CURLAUTH_BASIC: + case CURLAUTH_NTLM: + case CURLAUTH_GSSNEGOTIATE: + case CURLAUTH_DIGEST: + user->auth = auth; + return FALSE; + + default: + return TRUE; + } + #endif +} diff --git a/gb.net.curl/src/gbcurl.h b/gb.net.curl/src/gbcurl.h new file mode 100644 index 00000000..b2143664 --- /dev/null +++ b/gb.net.curl/src/gbcurl.h @@ -0,0 +1,79 @@ +/*************************************************************************** + + gbcurl.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_CURL_H +#define __GB_CURL_H + +#include +#include "main.h" + +typedef + struct + { + int type; + int auth; + char *host; + char *user; + char *pwd; + char *userpwd; + } + CURL_PROXY; + +typedef + struct + { + char *user; + char *pwd; + char *userpwd; + int auth; + } + CURL_USER; + +#ifndef __GB_CURL_C + +extern CURL_PROXY CURL_default_proxy; + +#endif + +//char *CURL_get_protocol(char *url, char *default_protocol); +bool CURL_set_url(void *_object, const char *src, int len); + +bool CURL_check_userpwd(CURL_USER *user); + +void CURL_proxy_init(CURL_PROXY *proxy); +void CURL_proxy_clear(CURL_PROXY *proxy); +void CURL_proxy_set(CURL_PROXY *proxy, CURL *curl); +bool CURL_proxy_set_auth(CURL_PROXY *user, int auth); +bool CURL_proxy_set_type(CURL_PROXY *proxy, int type); + +void CURL_default_proxy_clear(); + +void CURL_user_init(CURL_USER *user); +void CURL_user_clear(CURL_USER *user); +void CURL_user_set(CURL_USER *user,CURL *curl); +bool CURL_user_set_auth(CURL_USER *user,int auth); + +#endif + + + diff --git a/gb.net.curl/src/main.c b/gb.net.curl/src/main.c new file mode 100644 index 00000000..edc03690 --- /dev/null +++ b/gb.net.curl/src/main.c @@ -0,0 +1,60 @@ +/*************************************************************************** + + main.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include + +#include "CCurl.h" +#include "CHttpClient.h" +#include "CFtpClient.h" +#include "CProxy.h" +#include "CNet.h" + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CNetDesc, + CProxyDesc, + CurlSSLDesc, + CurlDesc, + HttpClientDesc, + CFtpClientDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + curl_global_init(CURL_GLOBAL_ALL); + return 0; +} + +void EXPORT GB_EXIT() +{ + curl_global_cleanup(); +} + diff --git a/gb.net.curl/src/main.h b/gb.net.curl/src/main.h new file mode 100644 index 00000000..223fdac5 --- /dev/null +++ b/gb.net.curl/src/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.net/AUTHORS b/gb.net/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net/COPYING b/gb.net/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.net/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.net/ChangeLog b/gb.net/ChangeLog new file mode 100644 index 00000000..86f0c1cb --- /dev/null +++ b/gb.net/ChangeLog @@ -0,0 +1,241 @@ +0.4.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.94) + +* Fixed a "segmentation fault", when changing user name or password in both HttpClient and FtpClient + +0.4.2 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* FtpClient external interface stabilized. + +* Finished 'Curl' class code, and code sharing between 'HttpClient' and 'FtpClient'. + +0.4.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* Added a new class 'Curl', that is the base for the rest of classes in this component + +* Now HttpClient and FtpClient inherits from 'Curl', so they share a lot of code + +0.4.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* HttpClient : some code improvements so now it is faster,smaller and wastes less memory. + +* Added 'FtpClient' class. + +0.3.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + + * Added two new properties to Socket class. They are read/write, the first is called 'Port'. if + value is zero (Net.Local), connection will try to stablish a Local socket, else a TCP connection + will be stablished. The second, 'HostOrPath' can be a Host or a local path. + + * Socket constructor has now no parameters. + + * Conect method has now two optional parameters. The first can override 'HostOrPath' property, and the + second overrides 'Port' property + +0.3.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + +* Added code to let the IDE show icons for all net classes. +* Removed lots of code to make the component lighter and faster. + +0.2.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.90) + +* Fixed a bug in UdpClient : segmentation fault when reading or writing data +* Fixed a bug in UDPServerClient example : trying to use CLOSE when UdpClient is not active +* defined before to allow compile on FreeBSD + +0.2.2 (ALPHA - compiles on Gambas 0.81) + +* 'NetCode' and 'AdvancedCode' classes changed to 'Net' +* Proxy properties from 'HttpClient' has been added into a new class called 'Proxy' +* 'ReturnCode' and 'ReturnString' properties are now called 'Code' and 'Reason' +* This version should compile now using libcurl 7.10.3, 7.10.4, 7.10.5, 7.10.6, 7.10.7, 7.10.8 and 7.11.0 +* now is after in all files to allow compile it on FreeBSD + +0.2.1 (ALPHA - compiles on Gambas 0.81) + +* Examples Updated +* New HttpClient interface defined +* 'AdvancedCode' class provides constants for 'Net.Advanced' component + +0.2.0 (ALPHA - compiles on Gambas 0.80) + +* Examples updated +* Modifications in configuration scripts to detect libcurl +* 'NetCode' provide the constants needed to work with all network classes +* 'Net advanced' includes not : CHttpClient, and will include other classes using libcurl +* 'Net' includes now : Socket, SerialPort,ServerSocket,UdpSocket,DnsClient and NetCode, + that is, basic networking stuff +* 'Net' component splitted it two components: 'Net' and 'Advanced net' + +0.1.4 (STABLE - Gambas 0.80) + +BM - Changes to allow the component compile on systems without MSG_NOSIGNAL flag + +0.1.3 (Gambas 0.74) + +BM - 20 Dec 2003 - Let component compile with gcc 2.95 + +0.1.2 + +* Added HTTP proxy support for 'HttpClient' class + +0.1.1 (Gambas 0.73) + +* Corrected bug in 'HttpClient' class that didn't convert correctly + document query to HTTP codification +* Added support for Solaris + +0.1.0 + +* Added 'HttpClient.Local' constant as sinonym of 'HttpClient.Unix' +* Changed 'HttpClient.Inet' constant to 'HttpClient.Internet' + +0.1.0pre7 + +* Using sys/un.h instead of linux/un.h in 'Socket' and 'ServerSocket' + classes +* Added option 'SO_REUSEADDR' to socket in 'ServerSocket' class +* 'ServerSocket' example fixed +* Documentation fixed + + +0.1.0pre6 + +* Memory allocation bug fixed in 'Socket' class +* Memory allocation bug fixed in 'SerialPort' class +* Lots of internal code reorganization +* Some memory optimizations in 'Socket' and 'ServerSocket' +* UDPServerClient example fixed +* ServerSocket example fixed + +0.1.0pre5 + +* Constant names changed in all classes to be more simple +* Parameters in methods and events does not include its type + as a prefix now +* 'ConnectUnix' and 'ConnectSocket' methods merged in one + method : 'Connect' +* 'ServerSocket' 'SocketType' property changed to 'Type' +* 'ConnectionRequest' event in 'ServerSocket' changed to 'Connection' +* 'Accept' method from 'ServerSocket' does not take any parameter + now +* 'HostFound' events from 'Socket' and 'HttpClient' changed to 'Found' +* Datagram class now inherits from '.Stream' +* Removed 'DataPacket' class +* New properties 'SourceHost', 'SourcePort', 'TargetHost', 'TargetPort' + in 'Datagram' class +* New method 'Peek' in 'Datagram' class +* Removed methods 'Stop','Receive' and 'Send' from 'Datagram' class +* 'Start' method from 'Datagram' Changed to 'Bind' +* 'Datagram' class changed its name to 'UdpSocket' +* Documentation updated +* Examples updated + + + +0.1.0pre4 + +* Removed Close() method from 'Socket' and 'SerialPort' classes, + translated to standard stream methods +* 'LookingHostIP' constant in 'Socket' and 'HttpClient' classes, + changed to 'LookingUpHostIP' +* Examples updated +* Documentation updated + + +0.1.0pre3 + + +* 'Accept' method from 'ServerSocket' changed its way to + act. Now it returns a new 'Socket' and accpets an optional + Event Handler +* Old method 'Receive' from HttpClient, splitted + in two new methods : 'Receive' and 'Peek' +* Now 'Socket' class inherits from '.Stream' +* Now 'SerialPort' class inherits from 'Stream' +* Removed 'Send' and 'Receive' methods from 'Socket' +* Removed 'Send' and 'Receive' methods from 'SerialPort' +* Added 'Peek' method to 'Socket' +* Adaptations from generic stream methods to 'Socket' characteristics +* Adaptations from generic stream methods to 'SerialPort' characteristics +* Documentation updated +* Examples updated + + +0.1.0pre2 + +* Added 0.0.17 = 0.1.0pre1 to CHANGELOG file +* 'SocketError' event from Socket and Datagram now is called 'Error' +* 'Error' codes are now negative values in Status property +* 'Error' events from classes which support it, now takes + zero parameters +* References to class names removed from 'GB.Error()' messages +* Class 'ClientSocket' changed its name to 'Socket' +* 'RemoteHostIP' and 'LocalHostIP' properties from 'Socket' + class changed its name to 'RemoteHost' and 'LocalHost' +* Constants from ServerSocket changed its name "TypeTCP"->"iNet", + "TypeUnix"->"Unix" +* 'DataAvailable' event changed its name to 'Read' in all classes + which supports it. +* 'Wait' method from ServerSocket changed to 'Pause' +* 'Path' property from 'Socket' class now returns IP:Port when + connected using TCP sockets. +* 'CloseSocket' method from 'Socket' class changed to 'Close' +* 'Socket' Class has a new constructor. You can both use no + parameters, or pass a string as parameter, which can be : + 'HostName:Port' or 'HostIP:Port' for TCP connections, or + 'Absolute_Path' for Unix connections. +* 'ServerSocket' Class has a new constructor. You can both use no + parameters, or pass a string and a number as parameter, which can be : + ':Port' for TCP connections, or 'Absolute_Path' for Unix connections. +* 'Datagram' Class has a new constructor. You can both use no + parameters, or pass an integer as Port value to start its + work. +* 'MaxConn' parameter from 'Listen' method if ServerSocket class + now is optional. +* 'SendData' and 'ReceiveData' changed to 'Send' and 'Receive' in all + classes containing that methods. + + +0.0.17 = 0.1.0pre1 + +* Removed prefix 'Is' in constats beginning with that prefix. +* 'GetData' method changed its name to 'ReceiveData' is some + classes +* 'Connected' event from 'ClientSocket' changed its name + to 'Connect'. +* Examples updated. +* HttpClient class finished. +* HttpClient documentation added. + + +0.0.16 + +* Added CHANGELOG file. +* Changed component name from 'networking' to 'net'. +* Changed 'sData' property from DataPacket to 'Data'. +* Documentation updated for DataPacket class. +* Changed example names. +* Constant names and values have changed in ClientSocket, some + new constants have been added. +* ConnectSocket and ConnectUnix methods from ClientSocket + have changed, now they not return any value, as error + codes are managed by "SocketError" event. +* Documentation updated for ClientSocket class. +* Example "ClientSocket" updated. +* DnsClient has two new constants. +* Documentation updated for DnsClient class. +* Some ServerSocket constants have changed its name. +* New Constants added to ServerSocket. +* A new static property 'UnixMaxPath' added to ServerSocket. +* ServerSocket 'Listen' method does not return any value now. +* ServerSocket example updated. +* Documentation updated for ServerSocket class. +* Datagram class has new constants. +* Datagram 'Start' method does not return any value now. +* Datagram 'SocketError' event implemented. +* Documentation updated for Datagram class. +* Example "UDPServerClient" updated. +* SerialPort 'GetData' method now is called 'ReceiveData'. +* Added constants to SerialPort class. +* Documentation updated for SerialPort class. +* SerialPort Example updated. diff --git a/gb.net/INSTALL b/gb.net/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.net/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.net/Makefile.am b/gb.net/Makefile.am new file mode 100644 index 00000000..71cb56ec --- /dev/null +++ b/gb.net/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @NET_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.net/NEWS b/gb.net/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net/README b/gb.net/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.net/acinclude.m4 b/gb.net/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.net/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.net/component.am b/gb.net/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.net/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.net/configure.ac b/gb.net/configure.ac new file mode 100644 index 00000000..cc4f1767 --- /dev/null +++ b/gb.net/configure.ac @@ -0,0 +1,20 @@ +dnl ---- configure.ac for gb.net + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-net],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.net) +LT_INIT + +GB_COMPONENT( + net, NET, gb.net, [src], + [], + [], + [$C_LIB $THREAD_LIB], + [$THREAD_INC]) + +AC_CONFIG_FILES([Makefile src/Makefile]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.net/gambas.h b/gb.net/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.net/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.net/gb_common.h b/gb.net/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.net/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.net/m4 b/gb.net/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.net/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.net/reconf b/gb.net/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.net/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.net/src/CDnsClient.c b/gb.net/src/CDnsClient.c new file mode 100644 index 00000000..e7522872 --- /dev/null +++ b/gb.net/src/CDnsClient.c @@ -0,0 +1,720 @@ +/*************************************************************************** + + CDnsClient.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDNSCLIENT_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "main.h" + +#include "CDnsClient.h" +#ifndef _REENTRANT +#define _REENTRANT +#endif + +static void **dns_object=NULL; +static int dns_count=0; +static sem_t dns_th_pipe; +static int dns_r_pipe=-1; +static int dns_w_pipe=-1; +static int dns_async_count=0; /* protected by dns_th_pipe */ + +DECLARE_EVENT(EVENT_Finished); + +int dns_init(void) +{ + int dpipe[2]; + + if (pipe(dpipe)) + return 1; + dns_r_pipe=dpipe[0]; + dns_w_pipe=dpipe[1]; + sem_init(&dns_th_pipe,0,1); + return 0; +} + +void dns_exit(void) +{ + close(dns_r_pipe); + close(dns_w_pipe); + dns_r_pipe=-1; + dns_w_pipe=-1; +} + +/********************************************************** + DnsClient pipe message protocol: + + 1) (void*)DNSCLIENT* + 2) Action : '0' --> dns_get_name, '1' --> dns_get_ip + 3) Result : string finished with '\x10' + *********************************************************/ + +static bool read_dns_pipe(void *data, size_t length) +{ + if (read(dns_r_pipe, data, length) != length) + { + fprintf(stderr, "gb.net: cannot read DNS pipe: %s\n", strerror(errno)); + return TRUE; + } + + return FALSE; +} + +/* Callers hold the dns_th_pipe semaphore as we change dns_async_count! */ +static void dns_start_async(void) +{ + assert(dns_async_count >= 0); + if (!dns_async_count++) + GB.Watch(dns_r_pipe, GB_WATCH_READ, (void *) dns_callback, 0); +} + +/* Callers hold the dns_th_pipe semaphore */ +static void dns_stop_async(void) +{ + if (!--dns_async_count) + GB.Watch(dns_r_pipe, GB_WATCH_NONE, (void *) dns_callback, 0); + assert(dns_async_count >= 0); +} + +void dns_callback(intptr_t lParam) +{ + /*********************************************************** + This function reads a message sent by a thread, and then + raises "Finished" event if necessary, and fills HostName + and HostIP properties. This function will run on the main + thread. + ************************************************************/ + CDNSCLIENT *mythis; + void *v_obj; + char Action[1]; + char BufRead[1]; + char *Buf=NULL; + + int Position; + int myloop; + int test_id; + struct pollfd mypoll; + int idata=1; + if (dns_r_pipe==-1) return; + sem_wait(&dns_th_pipe); + + for(;;) + { + Position=0; + BufRead[0]='\0'; + mypoll.fd=dns_r_pipe; + mypoll.events=POLLIN; + mypoll.revents=0; + idata=poll(&mypoll,1,0); + if (idata <= 0) break; + read_dns_pipe(&v_obj,sizeof(void*)); + read_dns_pipe(&test_id,sizeof(int)); + read_dns_pipe(Action,sizeof(char)); + GB.Alloc(POINTER(&Buf),sizeof(char)); + + while (BufRead[0] != '\x10') + { + read_dns_pipe(BufRead,sizeof(char)); + if (BufRead[0]!='\x10') + { + Buf[Position]=BufRead[0]; + Position++; + GB.Realloc(POINTER(&Buf),(Position+1)*sizeof(char)); + } + else + Buf[Position]='\0'; + } + Position=-1; + for (myloop=0;myloop=0) + { + mythis=(CDNSCLIENT*)v_obj; + if (mythis->iStatus && (mythis->i_id==test_id)) + { + if (Action[0]=='1') + { + GB.FreeString(&mythis->sHostIP); + mythis->sHostIP = GB.NewZeroString(Buf); + } + else + { + GB.FreeString(&mythis->sHostName); + mythis->sHostName = GB.NewZeroString(Buf); + } + mythis->iStatus=0; + if (mythis->finished_callback) + { + //GB.Ref(mythis); + GB.Post(mythis->finished_callback, (intptr_t)mythis->CliParent); + } + else + { + GB.Ref(mythis); + GB.Post((void (*)())dns_event, (intptr_t)mythis); + } + } + /* Release one DnsClient in async mode */ + if (mythis->iAsync) + dns_stop_async(); + } + GB.Free(POINTER(&Buf)); + } + + sem_post(&dns_th_pipe); +} +/************************************************** + To raise an event + **************************************************/ + +void dns_event(CDNSCLIENT *mythis) +{ + GB.Raise(mythis,EVENT_Finished,0); + GB.Unref(POINTER(&mythis)); +} + +static void write_dns_pipe(void *data, size_t length) +{ + if (write(dns_w_pipe, data, length) != length) + fprintf(stderr, "gb.net: cannot write to DNS pipe: %s\n", strerror(errno)); +} + +void* dns_get_name(void* v_obj) +{ + /**************************************************************** + This function will run in a thread different from main thread, + and when it finish its proccess, it sends a message to the + main thread using a pipe + *****************************************************************/ + char Buf[1]; + int myid; + int res; + CDNSCLIENT *mythis; + + char host[1024]; + struct sockaddr_in sa; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + Buf[0]='0'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + + + //((struct sockaddr*)&sa)->sa_family=AF_INET; + sa.sin_family = AF_INET; + bzero(host,1024); + sa.sin_port=0; + inet_aton(mythis->sHostIP, &sa.sin_addr); + + res=getnameinfo((struct sockaddr*)&sa,sizeof(struct sockaddr_in),host,1024*sizeof(char),NULL,0,NI_NAMEREQD); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + sem_wait(&dns_th_pipe); + write_dns_pipe(&v_obj,sizeof(void*)); /* object */ + write_dns_pipe(&myid,sizeof(int)); /* id */ + write_dns_pipe(Buf,sizeof(char)); /* Action */ + if (!res) write_dns_pipe(host,strlen(host)*sizeof(char)); + write_dns_pipe("\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} + + +/**************************************************************** +This function will run in a thread different from main thread, +and when it finish its proccess, it sends a message to the +main thread using a pipe +*****************************************************************/ + +void* dns_get_ip(void* v_obj) +{ + char Buf[1]; + char *BufData; + int myid; + struct addrinfo *stHost; + struct sockaddr_in *addr; + CDNSCLIENT *mythis; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + + Buf[0]='1'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + + if (getaddrinfo(mythis->sHostName,NULL,NULL,&stHost)) stHost=NULL; + if (stHost) if ( stHost[0].ai_family!=PF_INET ) stHost=NULL; + + sem_wait(&dns_th_pipe); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + + write_dns_pipe(&v_obj, sizeof(void*)); /* object */ + write_dns_pipe(&myid, sizeof(int)); /* id */ + write_dns_pipe(Buf, sizeof(char)); /* action */ + + if (stHost!=NULL) + { + addr=(struct sockaddr_in*)stHost[0].ai_addr; + BufData=inet_ntoa(addr->sin_addr); + if (BufData) write_dns_pipe(BufData,strlen(BufData)*sizeof(char)); + } + write_dns_pipe("\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} + + +void dns_close_all(CDNSCLIENT *mythis) +{ + if (mythis->iStatus) + { + pthread_cancel(mythis->th_id); /* cancel thread */ + pthread_join(mythis->th_id,NULL); + sem_destroy(&mythis->sem_id); + mythis->iStatus=0; /* inactive */ + dns_callback(dns_r_pipe); /* freeing pipe data */ + + } +} +int dns_thread_getname(CDNSCLIENT *mythis) +{ + sem_wait(&mythis->sem_id); + mythis->i_id++; + sem_post(&mythis->sem_id); + + mythis->iStatus=1; + /* We need to register the watch in the main thread to not anger qt */ + sem_wait(&dns_th_pipe); + dns_start_async(); + sem_post(&dns_th_pipe); + + if (pthread_create(&mythis->th_id,NULL,dns_get_name,(void*)mythis) ) + { + mythis->iStatus=0; + return 1; + } + pthread_detach(mythis->th_id); + return 0; +} + +int dns_thread_getip(CDNSCLIENT *mythis) +{ + sem_wait(&mythis->sem_id); + mythis->i_id++; + sem_post(&mythis->sem_id); + + mythis->iStatus=1; + sem_wait(&dns_th_pipe); + dns_start_async(); + sem_post(&dns_th_pipe); + + if (pthread_create(&mythis->th_id,NULL,dns_get_ip,(void*)mythis) ) + { + mythis->iStatus=0; + return 1; + } + pthread_detach(mythis->th_id); + return 0; +} + +void dns_set_async_mode(int myval, CDNSCLIENT *mythis) +{ + mythis->iAsync = myval; +} + +/************************************************************************************* +###################################################################################### + --------------------- DNSCLIENT GAMBAS INTERFACE IMPLEMENTATION ----------------- +###################################################################################### +**************************************************************************************/ + +/********************************************* + This property gets/sets the name of a Host + *********************************************/ +BEGIN_PROPERTY ( HostName ) + + if (READ_PROPERTY) + { + if (THIS->iStatus){ GB.ReturnString(NULL); return; } + GB.ReturnString(THIS->sHostName); + return; + } + + if (THIS->iStatus) { GB.Error("HostIP can not be changed while working"); return;} + GB.FreeString(&THIS->sHostName); + GB.StoreString(PROP(GB_STRING), &THIS->sHostName); + +END_PROPERTY + +/************************************************* + This property gets/sets the IP address of a Host + *************************************************/ +BEGIN_PROPERTY ( HostIP ) + + if (READ_PROPERTY) + { + if (THIS->iStatus) { GB.ReturnString(NULL); return; } + GB.ReturnString(THIS->sHostIP); + return; + } + + if (THIS->iStatus) { GB.Error("HostIP can not be changed while working"); return; } + GB.FreeString(&THIS->sHostIP); + GB.StoreString(PROP(GB_STRING), &THIS->sHostIP); + +END_PROPERTY + +/************************************************* + This property gets/sets asynchronous state: + + FALSE : Synchronous + TRUE : Asynchronous + *************************************************/ + + +BEGIN_PROPERTY ( CDNSCLIENT_Async ) + + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->iAsync); + return; + } + THIS->iAsync = VPROP(GB_BOOLEAN); + +END_PROPERTY + +/********************************************************** + This property gets status : 0 --> Inactive, 1 --> Working + **********************************************************/ +BEGIN_PROPERTY ( CDNSCLIENT_Status ) + + GB.ReturnInteger(THIS->iStatus); + +END_PROPERTY +/************************************************* + Gambas object "Constructor" + *************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_new) + + THIS->CliParent=NULL; + THIS->finished_callback=NULL; + THIS->sHostIP=NULL; + THIS->sHostName=NULL; + THIS->iStatus=0; + THIS->iAsync=0; + THIS->i_id=0; + sem_init(&THIS->sem_id,0,1); + dns_count++; + if (dns_object==NULL) + GB.Alloc(POINTER(&dns_object),sizeof(void*)); + else + GB.Realloc(POINTER(&dns_object),dns_count*sizeof(void*)); + + dns_object[dns_count-1]=(void*)THIS; + +END_METHOD + +/************************************************* + Gambas object "Destructor" + *************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_free) + + int myloop; + int Position=-1; + dns_close_all(THIS); + GB.FreeString(&THIS->sHostIP); + GB.FreeString(&THIS->sHostName); + for (myloop=0;myloop=0) + { + for (myloop=Position;myloop< (dns_count-1);myloop++) + { + dns_object[myloop]=dns_object[myloop+1]; + } + dns_count--; + if (!dns_count) + GB.Free(POINTER(&dns_object)); + } + +END_METHOD + +/************************************************* + To cancel an asynchronous operation + *************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_Stop) + + dns_close_all(THIS); + +END_METHOD +/***************************************************************************** + This method takes the Host IP, which was stored using HostName property, + and trasnlates it to IP address + *****************************************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_GetHostName) + + struct hostent *stHost=NULL; + struct in_addr addr; + + + if (THIS->iStatus) + { + GB.Error("Object is already working"); + return; + } + + if (THIS->sHostIP) + { + if ( THIS->iAsync) + { /* Asynchronous mode */ + sem_wait(&THIS->sem_id); + THIS->i_id++; + sem_post(&THIS->sem_id); + THIS->iStatus=1; + if (dns_thread_getname(THIS)) + { + GB.Error("No resources available to create a thread"); + return; + } + } + else + { /* Synchronous mode */ + inet_aton(THIS->sHostIP,&addr); +#ifdef __sun__ + stHost=gethostbyaddr((const char *)&addr,sizeof(addr),AF_INET); +#else + stHost=gethostbyaddr(&addr,sizeof(addr),AF_INET); +#endif + if (stHost==NULL) + { + GB.FreeString(&THIS->sHostName); + } + else + { + GB.FreeString(&THIS->sHostName); + THIS->sHostName = GB.NewZeroString(stHost->h_name); + } + GB.Raise((void*)THIS,EVENT_Finished,0); + } + } + else + { + GB.FreeString(&THIS->sHostName); + } + + +END_METHOD + +/***************************************************************************** + This method takes the Host IP, which was stored using HostIP property, + and trasnlates it to host name + *****************************************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_GetHostIP) + + struct hostent *stHost=NULL; + if (THIS->iStatus) + { + GB.Error("Object is already working"); + return; + } + + + if (THIS->sHostName) + { + if ( THIS->iAsync ) + { /* Asynchronous mode */ + + sem_wait(&THIS->sem_id); + THIS->i_id++; + sem_post(&THIS->sem_id); + THIS->iStatus=1; + if (dns_thread_getip(THIS)) + { + GB.Error("No resource available to create a thread"); + return; + } + } + else + { /* Synchronous mode */ + stHost=gethostbyname(THIS->sHostName); + if (stHost==NULL) + { + GB.FreeString(&THIS->sHostIP); + } + else + { + GB.FreeString(&THIS->sHostIP); + THIS->sHostIP = GB.NewZeroString(inet_ntoa(*((struct in_addr*)stHost->h_addr))); + } + GB.Raise((void*)THIS,EVENT_Finished,0); + } + } + else + { + GB.FreeString(&THIS->sHostIP); + } + + + +END_METHOD + + +/*************************************************************** + Here we declare the public interface of DnsClient class + ***************************************************************/ +GB_DESC CDnsClientDesc[] = +{ + + GB_DECLARE("DnsClient", sizeof(CDNSCLIENT)), + + GB_EVENT("Finished", NULL, NULL, &EVENT_Finished), + + GB_METHOD("_new", NULL, CDNSCLIENT_new, NULL), + GB_METHOD("_free", NULL, CDNSCLIENT_free, NULL), + GB_METHOD("Stop", NULL, CDNSCLIENT_Stop, NULL), + GB_METHOD("GetHostName", NULL, CDNSCLIENT_GetHostName, NULL), + GB_METHOD("GetHostIP", NULL, CDNSCLIENT_GetHostIP, NULL), + + GB_PROPERTY("HostName", "s", HostName), + GB_PROPERTY("HostIP", "s", HostIP), + GB_PROPERTY("Async", "b", CDNSCLIENT_Async), + GB_PROPERTY_READ("Status", "i", CDNSCLIENT_Status), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "HostName,HostIP,Async"), + GB_CONSTANT("_DefaultEvent", "s", "Finished"), + + GB_END_DECLARE +}; + + +/****************************************************************************** + +I do not know if Solaris accepts getaddrinfo and getnameinfo, so here +is the old implementation for that O.S. + +*******************************************************************************/ +/* +void* dns_get_ip(void* v_obj) +{ + char Buf[1]; + char *BufData; + int myid; + int herr; + + struct hostent hostbuf, *stHost; + size_t hstbuflen; + char tmphstbuf[1024]; + CDNSCLIENT *mythis; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + hstbuflen=1024; + Buf[0]='1'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + + stHost=gethostbyname_r (mythis->sHostName, &hostbuf, tmphstbuf, hstbuflen, &herr); + + sem_wait(&dns_th_pipe); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + write (dns_w_pipe,&v_obj,sizeof(void*)); + write (dns_w_pipe,&myid,sizeof(int)); + write (dns_w_pipe,Buf,sizeof(char)); + if (stHost!=NULL) + { + BufData=inet_ntoa(*( (struct in_addr*)stHost->h_addr )); + write (dns_w_pipe,BufData,strlen(BufData)*sizeof(char)); + } + write (dns_w_pipe,"\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} + + +void* dns_get_name(void* v_obj) +{ + + char Buf[1]; + int myid; + int herr; + size_t hstbuflen; + char tmphstbuf[2048]; + struct hostent hostbuf,*stHost=NULL; + CDNSCLIENT *mythis; + struct in_addr addr; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + hstbuflen=2048; + Buf[0]='0'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + inet_aton(mythis->sHostIP,&addr); + stHost=gethostbyaddr_r((const char *)&addr, sizeof (addr), AF_INET, &hostbuf,tmphstbuf,hstbuflen,&herr); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + sem_wait(&dns_th_pipe); + write (dns_w_pipe,&v_obj,sizeof(void*)); + write (dns_w_pipe,&myid,sizeof(int)); + write (dns_w_pipe,Buf,sizeof(char)); + if (stHost!=NULL) write (dns_w_pipe,stHost->h_name,strlen(stHost->h_name)*sizeof(char)); + write (dns_w_pipe,"\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} +*/ diff --git a/gb.net/src/CDnsClient.h b/gb.net/src/CDnsClient.h new file mode 100644 index 00000000..6b6734b9 --- /dev/null +++ b/gb.net/src/CDnsClient.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + CDnsClient.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDNSCLIENT_H +#define __CDNSCLIENT_H + +#include +#include +#include "gambas.h" + +#ifndef __CDNSCLIENT_C + +extern GB_DESC CDnsClientDesc[]; + +#else + +#define THIS ((CDNSCLIENT *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + char* sHostName; + char* sHostIP; + int iStatus; + int iAsync; + int i_id; + pthread_t th_id; + sem_t sem_id; + void (*finished_callback)(void*); + void *CliParent; + } + CDNSCLIENT; + +int dns_init(void); +void dns_exit(void); + +void dns_callback(intptr_t lParam); +void* dns_get_name(void* v_obj); +void* dns_get_ip(void* v_obj); +void dns_event(CDNSCLIENT *mythis); +void dns_close_all(CDNSCLIENT *mythis); +int dns_thread_getname(CDNSCLIENT *mythis); +int dns_thread_getip(CDNSCLIENT *mythis); +void dns_set_async_mode(int myval, CDNSCLIENT *mythis); +#endif diff --git a/gb.net/src/CNet.c b/gb.net/src/CNet.c new file mode 100644 index 00000000..182e1ba7 --- /dev/null +++ b/gb.net/src/CNet.c @@ -0,0 +1,161 @@ +/*************************************************************************** + + CNet.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CNET_C +#include "main.h" +#include +#include + +#include "CNet.h" + + +size_t NET_get_address_size(NET_ADDRESS *addr) +{ + switch (addr->a.sa_family) + { + case PF_UNIX: return sizeof(struct sockaddr_un); + case PF_INET: return sizeof(struct sockaddr_in); + default: return 0; + } +} + +void ToIPv4(char *src,char *dst,int leadzero) +{ + int myloop; + int zone=0; + int np=0; + int nc[4]={0,0,0,0}; + + dst[0]=0; + + if (!src) return; + + for (myloop=0;myloop57) ) + { + if (src[myloop]=='.') { if (++np > 3) return; } + else { return; } + } + else + { + nc[np]*=10; + nc[np]+=(src[myloop]-48); + if (nc[np]>255) return; + } + } + break; + + case 2: + if (src[myloop]!=' ') return; + break; + + } + } + + if (!leadzero) + sprintf(dst,"%d.%d.%d.%d",nc[0],nc[1],nc[2],nc[3]); + else + sprintf(dst,"%03d.%03d.%03d.%03d",nc[0],nc[1],nc[2],nc[3]); +} + +BEGIN_METHOD(CNET_Format,GB_STRING IpString;GB_INTEGER Format;GB_BOOLEAN LeadZero;) + + int leadzero=0; + + char dst[16]; + + if (!MISSING(Format)) + if (VARG(Format)!=0) { GB.Error("Unknown Format"); return; } + + if (!MISSING(LeadZero)) leadzero=VARG(LeadZero); + if (!LENGTH(IpString)) return; + + ToIPv4 (STRING(IpString),dst,leadzero); + GB.ReturnNewZeroString(dst); + + +END_METHOD + +/*************************************************************** +Here we declare the public interface of NetCode class +***************************************************************/ +GB_DESC CNetDesc[] = +{ + GB_DECLARE("Net", 0), GB_VIRTUAL_CLASS(), + + /* IP Formatting */ + GB_CONSTANT("IPv4","i",0), + /* normal operation */ + GB_CONSTANT("Inactive", "i", NET_INACTIVE), + GB_CONSTANT("Active", "i", NET_ACTIVE), + GB_CONSTANT("Pending", "i", NET_PENDING), + GB_CONSTANT("Accepting", "i", NET_ACCEPTING), + GB_CONSTANT("ReceivingData", "i", NET_RECEIVING_DATA), + GB_CONSTANT("Searching", "i", NET_SEARCHING), + GB_CONSTANT("Connecting", "i", NET_CONNECTING), + GB_CONSTANT("Connected", "i", NET_CONNECTED), + /* net error codes */ + GB_CONSTANT("CannotCreateSocket", "i", NET_CANNOT_CREATE_SOCKET), + GB_CONSTANT("ConnectionRefused", "i", NET_CONNECTION_REFUSED), + GB_CONSTANT("CannotRead", "i", NET_CANNOT_READ), + GB_CONSTANT("CannotWrite", "i", NET_CANNOT_WRITE), + GB_CONSTANT("HostNotFound", "i", NET_HOST_NOT_FOUND), + GB_CONSTANT("CannotBindSocket", "i", NET_CANNOT_BIND_SOCKET), + GB_CONSTANT("CannotListen", "i", NET_CANNOT_LISTEN), + GB_CONSTANT("CannotBindInterface", "i", NET_CANNOT_BIND_INTERFACE), + GB_CONSTANT("CannotAuthenticate", "i", NET_CANNOT_AUTHENTICATE), + GB_CONSTANT("ConnectionTimeout", "i", NET_CONNECTION_TIMEOUT), + /* SeverSocket, type */ + GB_CONSTANT("Internet", "i", NET_TYPE_INTERNET), + GB_CONSTANT("Local", "i", NET_TYPE_LOCAL), + GB_CONSTANT("Unix", "i", NET_TYPE_LOCAL), + + // Max path length for local sockets + //GB_CONSTANT("MaxPathLength", "i", NET_UNIX_PATH_MAX), + + GB_STATIC_METHOD("Format", "s", CNET_Format, "(IpString)s[(Format)i(LeadZero)b]"), + + GB_END_DECLARE +}; + + + + diff --git a/gb.net/src/CNet.h b/gb.net/src/CNet.h new file mode 100644 index 00000000..746a4432 --- /dev/null +++ b/gb.net/src/CNet.h @@ -0,0 +1,86 @@ +/*************************************************************************** + + CNet.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CNET_H +#define __CNET_H + +#include "gambas.h" + +#include +#include +#include +#include +#include + +#ifndef __CNET_C + +extern GB_DESC CNetDesc[]; + +#else + +#define THIS ((CNET *)_object) + +#endif + +typedef + union + { + struct sockaddr a; + struct sockaddr_in in; + struct sockaddr_un un; + } + NET_ADDRESS; + +#define NET_UNIX_PATH_MAX 108 + +enum +{ + NET_INACTIVE = 0, + NET_ACTIVE = 1, + NET_PENDING = 2, + NET_ACCEPTING = 3, + NET_RECEIVING_DATA = 4, + NET_SEARCHING = 5, + NET_CONNECTING = 6, + NET_CONNECTED = 7, + NET_CANNOT_CREATE_SOCKET = -2, + NET_CONNECTION_REFUSED = -3, + NET_CANNOT_READ = -4, + NET_CANNOT_WRITE = -5, + NET_HOST_NOT_FOUND = -6, + NET_CANNOT_BIND_SOCKET = -10, + NET_CANNOT_LISTEN = -14, + NET_CANNOT_BIND_INTERFACE = -15, + NET_CANNOT_AUTHENTICATE = -16, // For gb.net.pop3 + NET_CONNECTION_TIMEOUT = -17 +}; + +enum +{ + NET_TYPE_LOCAL = 0, + NET_TYPE_INTERNET = 1 +}; + +size_t NET_get_address_size(NET_ADDRESS *addr); + +#endif diff --git a/gb.net/src/CSerialPort.c b/gb.net/src/CSerialPort.c new file mode 100644 index 00000000..1ce84bc1 --- /dev/null +++ b/gb.net/src/CSerialPort.c @@ -0,0 +1,762 @@ +/*************************************************************************** + + CSerialPort.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSERIALPORT_C + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CYGWIN__ +/* Cygwin defines FIONREAD in . */ +#include +/* TIOCOUTQ is not implemented on Cygwin */ +#define TIOCOUTQ ((unsigned int) -1) +#endif /* __CYGWIN__ */ + +#ifndef TIOCINQ +#define TIOCINQ FIONREAD +#endif + +#include "main.h" +#include "tools.h" + +#include "CSerialPort.h" + +#define MAX_SERIAL_BUFFER_SIZE 65536 + + +GB_STREAM_DESC SerialStream = +{ + .open = CSerialPort_stream_open, + .close = CSerialPort_stream_close, + .read = CSerialPort_stream_read, + .write = CSerialPort_stream_write, + .seek = CSerialPort_stream_seek, + .tell = CSerialPort_stream_tell, + .flush = CSerialPort_stream_flush, + .handle = CSerialPort_stream_handle +}; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_DTR); +DECLARE_EVENT(EVENT_DSR); +DECLARE_EVENT(EVENT_RTS); +DECLARE_EVENT(EVENT_CTS); +DECLARE_EVENT(EVENT_DCD); +DECLARE_EVENT(EVENT_RNG); + +static SERIAL_SIGNAL get_signals(CSERIALPORT *_object) +{ + int ist = 0; + SERIAL_SIGNAL signals = { 0 }; + + ioctl(THIS->port, TIOCMGET, &ist); + + signals.DSR = (ist & TIOCM_DSR) != 0; + signals.DTR = (ist & TIOCM_DTR) != 0; + signals.RTS = (ist & TIOCM_RTS) != 0; + signals.CTS = (ist & TIOCM_CTS) != 0; + signals.DCD = (ist & TIOCM_CAR) != 0; + signals.RNG = (ist & TIOCM_RNG) != 0; + + return signals; +} + +static void raise_event(CSERIALPORT *_object, intptr_t event) +{ + int val = 0; + + if (event == EVENT_DSR) + val = THIS->signals.DSR; + else if (event == EVENT_DTR) + val = THIS->signals.DTR; + else if (event == EVENT_RTS) + val = THIS->signals.RTS; + else if (event == EVENT_CTS) + val = THIS->signals.CTS; + else if (event == EVENT_DCD) + val = THIS->signals.DCD; + else if (event == EVENT_RNG) + val = THIS->signals.RNG; + + GB.Raise(THIS, (int)event, 1, GB_T_BOOLEAN, val); + GB.Unref(POINTER(&_object)); +} + +#define CHECK_SIGNAL(_signal) \ +if (THIS->signals._signal != new_signals._signal) \ +{ \ + THIS->signals._signal = new_signals._signal; \ + GB.Ref(THIS); \ + GB.Post2(raise_event, (intptr_t)THIS, (intptr_t)EVENT_##_signal); \ +} + + +static int cb_change(intptr_t _object) +{ + SERIAL_SIGNAL new_signals; + //struct timespec mywait; + + // Just sleeping a bit to reduce CPU waste + //mywait.tv_sec = 0; + //mywait.tv_nsec = 1000000; // 1 ms + //nanosleep(&mywait, NULL); + + /* Serial port signals status */ + new_signals = get_signals(THIS); + + CHECK_SIGNAL(DSR); + CHECK_SIGNAL(DTR); + CHECK_SIGNAL(RTS); + CHECK_SIGNAL(CTS); + CHECK_SIGNAL(DCD); + CHECK_SIGNAL(RNG); + + return FALSE; +} + +static void cb_read(int fd, int type, CSERIALPORT *_object) +{ + GB.Raise(THIS, EVENT_Read, 0); +} + +static void assign_callback(CSERIALPORT *_object, int polling) +{ + int port = THIS->port; + + if (GB.CanRaise(THIS, EVENT_Read)) + GB.Watch(port, GB_WATCH_READ, (void *)cb_read, (intptr_t)THIS); + + if (GB.CanRaise(THIS, EVENT_DTR) + || GB.CanRaise(THIS, EVENT_CTS) + || GB.CanRaise(THIS, EVENT_DCD) + || GB.CanRaise(THIS, EVENT_DSR) + || GB.CanRaise(THIS, EVENT_RNG) + || GB.CanRaise(THIS, EVENT_RTS)) + { + // Polling is 50ms by default + THIS->every = GB.Every(polling, cb_change, (intptr_t)THIS); + } +} + +static void release_callback(CSERIALPORT *_object) +{ + GB.Watch(THIS->port, GB_WATCH_NONE, 0, 0); +} + +static void close_serial_port(CSERIALPORT *_object) +{ + if (THIS->every) + { + GB.Unref(POINTER(&THIS->every)); + THIS->every = NULL; + } + + if (THIS->status) + { + release_callback(THIS); + THIS->stream.desc = NULL; + CloseSerialPort(THIS->port, &THIS->oldtio); + THIS->status = 0; + } +} + +/**************************************************************************** + + Stream implementation + +****************************************************************************/ + +int CSerialPort_stream_open(GB_STREAM *stream, const char *path, int mode, void *data) +{ + return -1; /* not allowed */ +} + +int CSerialPort_stream_seek(GB_STREAM *stream, int64_t pos, int whence) +{ + return -1; /* not allowed */ +} + +int CSerialPort_stream_tell(GB_STREAM *stream, int64_t *pos) +{ + return -1; /* not allowed */ +} + +int CSerialPort_stream_flush(GB_STREAM *stream) +{ + void *_object = stream->tag; + return tcdrain(THIS->port); +} + +int CSerialPort_stream_handle(GB_STREAM *stream) +{ + void *_object = stream->tag; + return THIS->port; /* OK */ +} + +int CSerialPort_stream_close(GB_STREAM *stream) +{ + void *_object = stream->tag; + + if (!_object) + return -1; + + close_serial_port(THIS); + return 0; +} + +/*int CSerialPort_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = stream->tag; + int bytes; + + *len=0; + if (!_object) return -1; + + if (ioctl(THIS->port,FIONREAD,&bytes)) return -1; + *len = bytes; + return 0; +} + +int CSerialPort_stream_eof(GB_STREAM *stream) +{ + void *_object = stream->tag; + int bytes; + + if (!_object) return -1; + + if (ioctl(THIS->port,FIONREAD,&bytes)) return -1; + if (!bytes) return -1; + return 0; +}*/ + +int CSerialPort_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + return read(THIS->port, (void*)buffer, len); +} + +/*int CSerialPort_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos = -1; + int no_block = 0; + int bytes; + + if (!_object) + return -1; + + if (ioctl(THIS->port, FIONREAD, &bytes)) + return -1; + + if (bytes < len) + return -1; + + ioctl(THIS->port, FIONBIO, &no_block); + + npos = read(THIS->port, (void*)buffer, len); + + no_block++; + ioctl(THIS->port, FIONBIO, &no_block); + + if (npos != len) + return -1; + + return 0; +}*/ + +int CSerialPort_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + return write(THIS->port, (void*)buffer, len); +} + + +/*int CSerialPort_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos = -1; + int no_block = 0; + + if (!_object) + return -1; + + ioctl(THIS->port, FIONBIO, &no_block); + + npos = write(THIS->port, (void*)buffer, len); + + no_block++; + ioctl(THIS->port, FIONBIO, &no_block); + + if (npos < 0) + return -1; + + return 0; +}*/ + +static bool check_open(CSERIALPORT *_object) +{ + if (!THIS->status) + { + GB.Error("Port is closed"); + return TRUE; + } + else + return FALSE; +} + +static bool check_close(CSERIALPORT *_object) +{ + if (THIS->status) + { + GB.Error("Port must be closed first"); + return TRUE; + } + else + return FALSE; +} + +/**************************************************************************** + + SerialPort + +****************************************************************************/ + +BEGIN_PROPERTY(SerialPort_Status) + + GB.ReturnInteger(THIS->status); + +END_PROPERTY + +// Data Set Ready + +BEGIN_PROPERTY(SerialPort_DSR) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.DSR); + } + +END_PROPERTY + +// Data Transmission Ready + +BEGIN_PROPERTY(SerialPort_DTR) + + int ist; + + if (READ_PROPERTY) + { + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.DTR); + } + } + else + { + if (check_open(THIS)) + return; + + ioctl(THIS->port, TIOCMGET, &ist); + if (!VPROP(GB_BOOLEAN)) + ist &= ~TIOCM_DTR; + else + ist = ist | TIOCM_DTR; + ioctl(THIS->port, TIOCMSET, &ist); + } + +END_PROPERTY + +// Ready to send + +BEGIN_PROPERTY(SerialPort_RTS) + + int ist; + + if (READ_PROPERTY) + { + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.RTS); + } + } + else + { + if (check_open(THIS)) + return; + + ioctl(THIS->port, TIOCMGET, &ist); + if(!VPROP(GB_BOOLEAN)) + ist &= ~TIOCM_RTS; + else + ist = ist | TIOCM_RTS; + ioctl(THIS->port, TIOCMSET, &ist); + } + +END_PROPERTY + +// Clear to send + +BEGIN_PROPERTY(SerialPort_CTS) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.CTS); + } + +END_PROPERTY + +// Data Carrier Detect + +BEGIN_PROPERTY(SerialPort_DCD) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.DCD); + } + +END_PROPERTY + +// Ring + +BEGIN_PROPERTY(SerialPort_RNG) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.RNG); + } + +END_PROPERTY + +// Gets / Sets serial port name (on Linux /dev/ttyS0, etc) + +BEGIN_PROPERTY(SerialPort_Port) + + if (READ_PROPERTY) + GB.ReturnString(THIS->portName); + else + { + if (check_close(THIS)) + return; + GB.StoreString(PROP(GB_STRING), &THIS->portName); + } + +END_PROPERTY + +// FlowControl : 1->CRTSCTS , 2-> XON/XOFF , 3-> XON/OFF plus CRTSCTS, 0 --> NONE + +BEGIN_PROPERTY(SerialPort_FlowControl) + + int flow; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->flow); + else + { + if (check_close(THIS)) + return; + + flow = VPROP(GB_INTEGER); + if (flow < 0 || flow > 3) + { + GB.Error(GB_ERR_ARG); + return; + } + + THIS->flow = VPROP(GB_INTEGER); + } + +END_PROPERTY + +// Gets / Sets serial parity (E,O,N) + +BEGIN_PROPERTY(SerialPort_Parity) + + int parity; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->parity); + else + { + if (check_close(THIS)) + return; + + parity = VPROP(GB_INTEGER); + if (parity < 0 || parity > 2) + { + GB.Error(GB_ERR_ARG); + return; + } + + THIS->parity = parity; + } + +END_PROPERTY + +// Gets / Sets serial port Speed + +BEGIN_PROPERTY(SerialPort_Speed) + + int speed; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->speed); + else + { + if (check_close(THIS)) + return; + + speed = VPROP(GB_INTEGER); + + if (speed < 0) + GB.Error(GB_ERR_ARG); + else + THIS->speed = speed; + } + +END_PROPERTY + +// Gets / Sets serial port Data Bits + +BEGIN_PROPERTY(SerialPort_DataBits) + + int value; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->dataBits); + else + { + if (check_close(THIS)) + return; + + value = VPROP(GB_INTEGER); + + if (ConvertDataBits(value) == -1) + GB.Error(GB_ERR_ARG); + else + THIS->dataBits = value; + } + +END_PROPERTY + +// Gets / Sets serial port Stop Bits + +BEGIN_PROPERTY(SerialPort_StopBits) + + int value; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->stopBits); + else + { + if (check_close(THIS)) + return; + + value = VPROP(GB_INTEGER); + + if (ConvertStopBits(value) == -1) + GB.Error(GB_ERR_ARG); + else + THIS->stopBits = value; + } + +END_PROPERTY + +// Gambas object "Constructor" + +BEGIN_METHOD_VOID(SerialPort_new) + + THIS->portName = GB.NewZeroString("/dev/ttyS0"); + THIS->speed = 19200; + THIS->parity = 0; + THIS->dataBits = 8; + THIS->stopBits = 1; + THIS->flow = 1; + +END_METHOD + +// Gambas object "Destructor" + +BEGIN_METHOD_VOID(SerialPort_free) + + close_serial_port(THIS); + GB.FreeString(&THIS->portName); + +END_METHOD + +// To open the port + +BEGIN_METHOD(SerialPort_Open, GB_INTEGER polling) + + int polling = VARGOPT(polling, 50); + + if (THIS->status) + { + GB.Error("Port is already opened"); + return; + } + + if (OpenSerialPort(THIS)) + return; + + THIS->signals = get_signals(THIS); + THIS->stream.desc = &SerialStream; + THIS->stream.tag = THIS; + assign_callback(THIS, polling); + + THIS->status = 1; + +END_METHOD + +BEGIN_PROPERTY(SerialPort_InputBufferSize) + + int ret = 0; + + if (THIS->status) + { + if (ioctl(THIS->port, TIOCINQ, &ret)) + GB.Error("Unable to read input buffer size: &1", strerror(errno)); + } + + GB.ReturnInteger(ret); + +END_PROPERTY + +BEGIN_PROPERTY(SerialPort_OutputBufferSize) + + int ret = 0; + + if (THIS->status) + { + if (ioctl(THIS->port, TIOCOUTQ, &ret)) + GB.Error("Unable to read output buffer size: &1", strerror(errno)); + } + + GB.ReturnInteger(ret); + +END_PROPERTY + +BEGIN_METHOD(SerialPort_Clear, GB_INTEGER buffer) + + int buffer = VARGOPT(buffer, GB_ST_READ + GB_ST_WRITE); + + if (THIS->status) + { + if (buffer & GB_ST_READ) + tcflush(THIS->port, TCIFLUSH); + if (buffer & GB_ST_WRITE) + tcflush(THIS->port, TCOFLUSH); + } + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC SerialPortDesc[] = +{ + GB_DECLARE("SerialPort", sizeof(CSERIALPORT)), + + GB_INHERITS("Stream"), + + GB_CONSTANT("None", "i", 0), + + GB_CONSTANT("Hardware", "i", 1), + GB_CONSTANT("Software", "i", 2), + GB_CONSTANT("Both", "i", 3), + + GB_CONSTANT("Even", "i", 1), + GB_CONSTANT("Odd", "i", 2), + + GB_CONSTANT("Bits1", "i", 1), + GB_CONSTANT("Bits2", "i", 2), + + GB_CONSTANT("Bits5", "i", 5), + GB_CONSTANT("Bits6", "i", 6), + GB_CONSTANT("Bits7", "i", 7), + GB_CONSTANT("Bits8", "i", 8), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("DTRChange", NULL, "(CurrentValue)b", &EVENT_DTR), + GB_EVENT("DSRChange", NULL, "(CurrentValue)b", &EVENT_DSR), + GB_EVENT("RTSChange", NULL, "(CurrentValue)b", &EVENT_RTS), + GB_EVENT("CTSChange", NULL, "(CurrentValue)b", &EVENT_CTS), + GB_EVENT("DCDChange", NULL, "(CurrentValue)b", &EVENT_DCD), + GB_EVENT("RNGChange", NULL, "(CurrentValue)b", &EVENT_RNG), + + GB_METHOD("_new", NULL, SerialPort_new, NULL), + GB_METHOD("_free", NULL, SerialPort_free, NULL), + GB_METHOD("Open", NULL, SerialPort_Open, "[(Polling)i]"), + + GB_PROPERTY("FlowControl","i",SerialPort_FlowControl), + GB_PROPERTY("PortName", "s", SerialPort_Port), + GB_PROPERTY("Parity", "i", SerialPort_Parity), + GB_PROPERTY("Speed", "i", SerialPort_Speed), + + GB_PROPERTY("DataBits", "i", SerialPort_DataBits), + GB_PROPERTY("StopBits", "i", SerialPort_StopBits), + GB_PROPERTY("DTR", "b", SerialPort_DTR), + GB_PROPERTY("RTS", "b", SerialPort_RTS), + GB_PROPERTY_READ("Status", "i", SerialPort_Status), + GB_PROPERTY_READ("DSR", "b", SerialPort_DSR), + GB_PROPERTY_READ("CTS", "b", SerialPort_CTS), + GB_PROPERTY_READ("DCD", "b", SerialPort_DCD), + GB_PROPERTY_READ("RNG", "b", SerialPort_RNG), + + GB_PROPERTY_READ("InputBufferSize", "i", SerialPort_InputBufferSize), + GB_PROPERTY_READ("OutputBufferSize", "i", SerialPort_OutputBufferSize), + + GB_METHOD("Clear", NULL, SerialPort_Clear, "[(Buffer)i]"), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "FlowControl{SerialPort.None;Hardware;Software;Both}=Hardware,PortName,Parity{SerialPort.None;Even;Odd}=None,Speed=19200,DataBits{SerialPort.Bits5;Bits6;Bits7;Bits8}=Bits8,StopBits{SerialPort.Bits1;Bits2}=Bits1"), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; + + diff --git a/gb.net/src/CSerialPort.h b/gb.net/src/CSerialPort.h new file mode 100644 index 00000000..9809e00e --- /dev/null +++ b/gb.net/src/CSerialPort.h @@ -0,0 +1,82 @@ +/*************************************************************************** + + CSerialPort.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSERIALPORT_H +#define __CSERIALPORT_H + +#include "gambas.h" +#include + +#ifndef __CSERIALPORT_C + +extern GB_DESC SerialPortDesc[]; +extern GB_STREAM_DESC SerialStream; + +#else + +#define THIS ((CSERIALPORT *)_object) + +#endif + +typedef + struct + { + unsigned DSR : 1; + unsigned DTR : 1; + unsigned RTS : 1; + unsigned CTS : 1; + unsigned DCD : 1; + unsigned RNG : 1; + } + SERIAL_SIGNAL; + +typedef + struct + { + GB_BASE ob; + GB_STREAM stream; + int port; + int status; + char *portName; + int parity; + int speed; + int dataBits; + int stopBits; + int flow; + int polling; + GB_TIMER *every; + SERIAL_SIGNAL signals; + struct termios oldtio; + } + CSERIALPORT; + +int CSerialPort_stream_read(GB_STREAM *stream, char *buffer, int len); +int CSerialPort_stream_write(GB_STREAM *stream, char *buffer, int len); +int CSerialPort_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CSerialPort_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CSerialPort_stream_tell(GB_STREAM *stream, int64_t *pos); +int CSerialPort_stream_flush(GB_STREAM *stream); +int CSerialPort_stream_close(GB_STREAM *stream); +int CSerialPort_stream_handle(GB_STREAM *stream); + +#endif diff --git a/gb.net/src/CServerSocket.c b/gb.net/src/CServerSocket.c new file mode 100644 index 00000000..9d340a25 --- /dev/null +++ b/gb.net/src/CServerSocket.c @@ -0,0 +1,644 @@ +/*************************************************************************** + + CServerSocket.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSERVERSOCKET_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "tools.h" + +#include "CServerSocket.h" +#include "CSocket.h" + +DECLARE_EVENT(EVENT_Connection); +DECLARE_EVENT(EVENT_Error); + +//------------------------------------------------------------------------- + +static void srvsock_post_error(CSERVERSOCKET *_object) +{ + GB.Raise(THIS, EVENT_Error, 0); + GB.Unref(POINTER(&_object)); +} + +static void CServerSocket_CallBack(int fd, int type, intptr_t lParam) +{ + int okval = 0; + char *remote_ip; + unsigned int clen; + CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam; + + if (SOCKET->status != NET_ACTIVE) return; + + SOCKET->status = NET_PENDING; + clen = sizeof(struct sockaddr_in); + THIS->client = accept(SOCKET->socket, (struct sockaddr*)&THIS->so_client.in, &clen); + + if (THIS->client == -1) + { + //close(THIS->client); + SOCKET->status = NET_ACTIVE; + return; + } + + if ((!THIS->max_conn) || (THIS->num_conn < THIS->max_conn)) + okval = 1; + + if ((!THIS->pause) && (okval)) + { + remote_ip = GB.NewZeroString(inet_ntoa(THIS->so_client.in.sin_addr)); + GB.Raise(THIS, EVENT_Connection, 1, GB_T_STRING, remote_ip, GB.StringLength(remote_ip)); + GB.FreeString(&remote_ip); + } + + if (SOCKET->status == NET_PENDING) + { + close(THIS->client); + THIS->client = -1; + } + + SOCKET->status = NET_ACTIVE; +} + +static void CServerSocket_CallBackUnix(int fd, int type, intptr_t lParam) +{ + //int position=0; + int okval=0; + unsigned int ClientLen; + CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam; + + if ( SOCKET->status != NET_ACTIVE) return; + + SOCKET->status = NET_PENDING; + ClientLen=sizeof(struct sockaddr_un); + THIS->client=accept(SOCKET->socket,(struct sockaddr*)&THIS->so_client.un,&ClientLen); + if (THIS->client == -1) + { + close(THIS->client); + SOCKET->status = NET_ACTIVE; + return; + } + if ( (!THIS->max_conn) || (THIS->num_conn < THIS->max_conn) ) okval=1; + if ( (!THIS->pause) && (okval) ) + GB.Raise(THIS,EVENT_Connection,1,GB_T_STRING,NULL,0); + if ( SOCKET->status == NET_PENDING) close(THIS->client); + SOCKET->status = NET_ACTIVE; + +} + + +//------------------------------------------------------------------------- + +/********************************************************* + Starts listening (TCP/UDP/UNIX) + **********************************************************/ + +static int do_srvsock_listen(CSERVERSOCKET* _object, int mymax) +{ + int retval; + int auth = 1; + + if (THIS->port == 0 && THIS->type == NET_TYPE_INTERNET) + return 8; + + if (SOCKET->status > NET_INACTIVE) return 1; + + if (mymax < 0) + return 13; + + if (THIS->type == NET_TYPE_LOCAL && !THIS->path) + return 7; + + if (THIS->type == NET_TYPE_INTERNET) + { + THIS->so_server.in.sin_family = AF_INET; + THIS->so_server.in.sin_addr.s_addr = INADDR_ANY; + THIS->so_server.in.sin_port = htons(THIS->port); + SOCKET->socket = socket(PF_INET, SOCK_STREAM, 0); + } + else + { + unlink(THIS->path); + THIS->so_server.un.sun_family = AF_UNIX; + strcpy(THIS->so_server.un.sun_path, THIS->path); + SOCKET->socket=socket(AF_UNIX, SOCK_STREAM,0); + } + + if ( SOCKET->socket==-1 ) + { + SOCKET->status = NET_CANNOT_CREATE_SOCKET; + GB.Ref(THIS); + GB.Post(srvsock_post_error,(intptr_t)THIS); + return 2; + } + // thanks to Benoit : this option allows non-root users to reuse the + // port after closing it and reopening it in a short interval of time. + // However, If you are porting this component to other O.S., be careful, + // as this is not a standard unix option + setsockopt(SOCKET->socket, SOL_SOCKET, SO_REUSEADDR, &auth, sizeof(int)); + + // Define specific interface: does not really work... :-/ + #ifdef SO_BINDTODEVICE + if (THIS->interface) + { + if (setsockopt(SOCKET->socket, SOL_SOCKET, SO_BINDTODEVICE, THIS->interface, GB.StringLength(THIS->interface))) + { + fprintf(stderr, "unable to bind socket to interface: %s\n", strerror(errno)); + SOCKET->status = NET_CANNOT_BIND_INTERFACE; + return 15; + } + } + #endif + + SOCKET_update_timeout(SOCKET); + // + if (THIS->type == NET_TYPE_INTERNET) + retval = bind(SOCKET->socket, (struct sockaddr *)&THIS->so_server.in, sizeof(struct sockaddr_in)); + else + retval = bind(SOCKET->socket, (struct sockaddr *)&THIS->so_server.un, sizeof(struct sockaddr_un)); + + if (retval == -1) + { + close(SOCKET->socket); + SOCKET->status = NET_CANNOT_BIND_SOCKET; + GB.Ref(THIS); + GB.Post(srvsock_post_error,(intptr_t)THIS); + return 10; + } + + // Set socket to non-blocking mode + SOCKET_set_blocking(SOCKET, FALSE); + + if ( listen(SOCKET->socket,mymax) == -1 ) + { + close(SOCKET->socket); + SOCKET->status = NET_CANNOT_LISTEN; + GB.Ref(THIS); + GB.Post(srvsock_post_error,(intptr_t)THIS); + return 14; + } + THIS->num_conn = 0; + THIS->max_conn = mymax; + SOCKET->status = NET_ACTIVE; + + //CServerSocket_AssignCallBack((intptr_t)THIS,SOCKET->socket); + if (THIS->type == NET_TYPE_INTERNET) + GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBack,(intptr_t)THIS); + else + GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBackUnix,(intptr_t)THIS); + + return 0; +} + +static void srvsock_listen(CSERVERSOCKET *_object, int max) +{ + switch(do_srvsock_listen(THIS, max)) + { + case 1: GB.Error("Socket is already listening"); break; + case 2: GB.Error("Cannot create socket"); break; + case 7: GB.Error("Path is not defined"); break; + case 8: GB.Error("Port is not defined"); break; + case 10: GB.Error("Cannot bind to socket"); break; + case 13: GB.Error("Invalid maximum number of connections"); break; + case 14: GB.Error("Cannot listen on socket"); break; + case 15: GB.Error("Unable to bind socket to interface"); break; + default: break; + } +} + +static void add_child(CSERVERSOCKET *_object, CSOCKET *child) +{ + *((CSOCKET **)GB.Add(&THIS->children)) = child; + child->parent = THIS; + GB.Ref(child); +} + +static void unref_child_later(CSOCKET *child) +{ + GB.Unref(POINTER(&child)); +} + +static void remove_child(CSERVERSOCKET *_object, CSOCKET *child) +{ + int i; + + for (i = 0; i < GB.Count(THIS->children); i++) + { + if (THIS->children[i] == child) + { + child->parent = NULL; + GB.Remove(&THIS->children, i, 1); + GB.Post(unref_child_later, (intptr_t)child); + return; + } + } +} + +void CServerSocket_OnClose(void *child) +{ + CSERVERSOCKET *_object; + + if (!child) return; + + _object = (CSERVERSOCKET*)(((CSOCKET *)child)->parent); + + if (!THIS) return; + + remove_child(THIS, child); + THIS->num_conn--; +} + +//------------------------------------------------------------------------- + +/*************************************************** + This property reflects current status of the + socket (closed, listening...) + ***************************************************/ +BEGIN_PROPERTY(ServerSocket_Status) + + GB.ReturnInteger(SOCKET->status); + +END_PROPERTY + +/****************************************************************** + This property gets/sets the port to listen to (TCP or UDP sockets) + ******************************************************************/ +BEGIN_PROPERTY(ServerSocket_Port) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->port); + return; + } + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Port cannot be changed when socket is active"); + return; + } + if ( (VPROP(GB_INTEGER) < 1) || (VPROP(GB_INTEGER) > 65535) ) + { + GB.Error("Invalid port Value"); + return; + } + THIS->port = VPROP(GB_INTEGER); + +END_PROPERTY + + +/*************************************************************** + This property gets/sets the address to listen to (UNIX socket) + ***************************************************************/ +BEGIN_PROPERTY(ServerSocket_Path) + + if (READ_PROPERTY) + { + GB.ReturnString(THIS->path); + return; + } + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Path cannot be changed while socket is active"); + return; + } + if (PLENGTH() > NET_UNIX_PATH_MAX) + { + GB.Error("Path is too long"); + return; + } + GB.StoreString(PROP(GB_STRING), &THIS->path); + +END_PROPERTY + + +BEGIN_PROPERTY(ServerSocket_Interface) + + if (READ_PROPERTY) + { + GB.ReturnString(THIS->path); + } + else + { + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Interface cannot be changed while socket is active"); + return; + } + if (PLENGTH() > IFNAMSIZ) + { + GB.Error ("Interface name is too long"); + return; + } + + GB.StoreString(PROP(GB_STRING), &THIS->interface); + } + +END_PROPERTY + + +/*************************************************************** + This property gets/sets the socket type (0 -> TCP, 1 -> UNIX) + ***************************************************************/ +BEGIN_PROPERTY(ServerSocket_Type) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->type); + return; + } + + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Type cannot be changed when the socket is active"); + return; + } + + switch(VPROP(GB_INTEGER)) + { + case NET_TYPE_LOCAL: THIS->type = NET_TYPE_LOCAL; break; + case NET_TYPE_INTERNET: THIS->type = NET_TYPE_INTERNET; break; + default: GB.Error("Invalid socket type"); + } + +END_PROPERTY + + +BEGIN_METHOD(ServerSocket_new, GB_STRING sPath; GB_INTEGER iMaxConn) + + char *buf = NULL; + int nport = 0; + int iMax; + + THIS->type = NET_TYPE_INTERNET; + GB.NewArray(&THIS->children, sizeof(void *), 0); + + if (MISSING(sPath)) return; + if (LENGTH(sPath) == 0) return; + + iMax = VARGOPT(iMaxConn, 0); + + switch(IsHostPath(STRING(sPath), LENGTH(sPath), &buf, &nport)) + { + case 0: + GB.Error("Invalid Host or Path"); + return; + + case 1: + if (buf) + { + GB.Free(POINTER(&buf)); + GB.Error("Invalid Host"); + return; + } + if (nport < 1) + { + GB.Error("Invalid Port"); + return; + } + + THIS->type = NET_TYPE_INTERNET; + THIS->port = nport; + srvsock_listen(THIS, iMax); + break; + + case 2: + THIS->type = NET_TYPE_LOCAL; + if (LENGTH(sPath) > NET_UNIX_PATH_MAX) + { + GB.Error ("Path is too long"); + return; + } + GB.StoreString(ARG(sPath), &THIS->path); + break; + } + +END_METHOD + +void close_server(CSERVERSOCKET *_object) +{ + CSOCKET *chd; + + if (SOCKET->status <= NET_INACTIVE) return; + + GB.Watch (SOCKET->socket , GB_WATCH_NONE , (void *)CServerSocket_CallBack,0); + close(SOCKET->socket); + SOCKET->status = NET_INACTIVE; + + while (GB.Count(THIS->children)) + { + chd = THIS->children[0]; + if (chd->common.stream.desc) CSocket_stream_close(&chd->common.stream); + remove_child(THIS, chd); + } +} + +BEGIN_METHOD_VOID(ServerSocket_free) + + close_server(THIS); + GB.FreeArray(&THIS->children); + GB.FreeString(&THIS->path); + GB.FreeString(&THIS->interface); + GB.StoreVariant(NULL, POINTER(&(THIS->tag))); + +END_METHOD + +/******************************************************** + Stops listening (TCP/UDP/UNIX), and closes all client sockets associated + to this server + *********************************************************/ +BEGIN_METHOD_VOID(ServerSocket_Close) + + close_server(THIS); + +END_METHOD + +/******************************************************** + Do not accept more connections until Resume is used + *********************************************************/ +BEGIN_METHOD_VOID(ServerSocket_Pause) + + THIS->pause = TRUE; + +END_METHOD + +/******************************************************** + Accept connections again + *********************************************************/ +BEGIN_METHOD_VOID(ServerSocket_Resume) + + THIS->pause = FALSE; + +END_METHOD + +BEGIN_METHOD(ServerSocket_Listen, GB_INTEGER MaxConn) + + srvsock_listen(THIS, VARGOPT(MaxConn, 0)); + +END_METHOD + +/****************************************************************** + To accept a pending connection and delegate it to a Socket object +*******************************************************************/ + +BEGIN_METHOD_VOID(ServerSocket_Accept) + + CSOCKET *socket; + struct sockaddr_in myhost; + unsigned int mylen; + + if ( SOCKET->status != NET_PENDING) + { + GB.Error("No connection to accept"); + return; + } + + socket = GB.New(GB.FindClass("Socket"), "Socket", NULL); + socket->common.socket = THIS->client; + socket->common.status = NET_CONNECTED; + socket->OnClose = CServerSocket_OnClose; + + THIS->num_conn++; + + GB.FreeString(&socket->remote_host_ip); + GB.FreeString(&socket->local_host_ip); + //GB.FreeString(&socket->sPath); + + socket->local_port = 0; + socket->port = 0; + socket->conn_type = THIS->type; + + if (THIS->type == NET_TYPE_INTERNET) + { + socket->remote_host_ip = GB.NewZeroString(inet_ntoa(THIS->so_client.in.sin_addr)); + socket->host = GB.NewZeroString (inet_ntoa(THIS->so_client.in.sin_addr)); + mylen = sizeof(struct sockaddr); + getsockname(socket->common.socket, (struct sockaddr*)&myhost, &mylen); + socket->local_host_ip = GB.NewZeroString(inet_ntoa(myhost.sin_addr)); + socket->local_port = ntohs(myhost.sin_port); + socket->port = ntohs(THIS->so_client.in.sin_port); + socket->use_port = ntohs(THIS->so_client.in.sin_port); + } + else + { + socket->conn_type=1; + socket->path = GB.NewZeroString(THIS->path); + } + + add_child(THIS, socket); + + CSOCKET_init_connected(socket); + // Socket returned by accept is non-blocking by default + GB.Stream.Block(&socket->common.stream, FALSE); + //socket->stream._free[0]=(intptr_t)socket; + + GB.Ref(socket); + GB.Post(CSocket_post_connected,(intptr_t)socket); + SOCKET->status = NET_ACCEPTING; + + GB.ReturnObject((void*)socket); + +END_METHOD + +// BM: Enumeration of child sockets + +BEGIN_METHOD_VOID(ServerSocket_next) + + int *index = (int *)GB.GetEnum(); + + if (*index >= GB.Count(THIS->children)) + GB.StopEnum(); + else + { + GB.ReturnObject(THIS->children[*index]); + (*index)++; + } + +END_METHOD + +BEGIN_PROPERTY(ServerSocket_count) + + GB.ReturnInteger(GB.Count(THIS->children)); + +END_PROPERTY + +BEGIN_PROPERTY(ServerSocket_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&(THIS->tag))); + +END_METHOD + + +//------------------------------------------------------------------------- + +GB_DESC CServerSocketDesc[] = +{ + GB_DECLARE("ServerSocket", sizeof(CSERVERSOCKET)), + + GB_EVENT("Connection", NULL, "(RemoteHostIP)s", &EVENT_Connection), + GB_EVENT("Error", NULL,NULL, &EVENT_Error), + + GB_METHOD("_new", NULL, ServerSocket_new, "[(Path)s(MaxConn)i]"), + GB_METHOD("_free", NULL, ServerSocket_free, NULL), + GB_METHOD("Listen",NULL, ServerSocket_Listen, "[(MaxConn)i]"), + GB_METHOD("Pause", NULL, ServerSocket_Pause, NULL), + GB_METHOD("Resume", NULL, ServerSocket_Resume, NULL), + GB_METHOD("Accept","Socket",ServerSocket_Accept,NULL), + GB_METHOD("Close",NULL,ServerSocket_Close,NULL), + + GB_PROPERTY("Type","i",ServerSocket_Type), + GB_PROPERTY("Path","s",ServerSocket_Path), + GB_PROPERTY("Port", "i", ServerSocket_Port), + GB_PROPERTY("Interface", "s", ServerSocket_Interface), + GB_PROPERTY_READ("Status","i", ServerSocket_Status), + GB_PROPERTY("Tag", "v", ServerSocket_Tag), + + GB_METHOD("_next", "Socket", ServerSocket_next, NULL), + GB_PROPERTY_READ("Count", "i", ServerSocket_count), + + GB_PROPERTY("Timeout", "i", Socket_Timeout), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "Type=Local,Path,Port"), + GB_CONSTANT("_DefaultEvent", "s", "Connection"), + + GB_END_DECLARE +}; + + + + diff --git a/gb.net/src/CServerSocket.h b/gb.net/src/CServerSocket.h new file mode 100644 index 00000000..baec8823 --- /dev/null +++ b/gb.net/src/CServerSocket.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + CServerSocket.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSERVERSOCKET_H +#define __CSERVERSOCKET_H + +#include "gambas.h" +#include "CNet.h" +#include "CSocket.h" + +#ifndef __CSERVERSOCKET_C + +extern GB_DESC CServerSocketDesc[]; + +#else + +#define THIS ((CSERVERSOCKET *)_object) +#define SOCKET (&THIS->common) + +#endif + + +typedef + union + { + struct sockaddr_in in; + struct sockaddr_un un; + } + st_so_sock; + +typedef + struct + { + CSOCKET_COMMON common; + unsigned type : 2; + unsigned pause : 1; + unsigned short port; + char *path; + int num_conn; + int max_conn; + st_so_sock so_server; + st_so_sock so_client; + int client; + CSOCKET **children; + char *interface; + GB_VARIANT_VALUE tag; + } + CSERVERSOCKET; + +#endif diff --git a/gb.net/src/CSocket.c b/gb.net/src/CSocket.c new file mode 100644 index 00000000..1d012a5d --- /dev/null +++ b/gb.net/src/CSocket.c @@ -0,0 +1,949 @@ +/*************************************************************************** + + CSocket.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSOCKET_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "tools.h" + +#include "CSocket.h" +#include "CServerSocket.h" +#include "CDnsClient.h" + +//#define DEBUG_ME 1 + +#define MAX_CLIENT_BUFFER_SIZE 65536 +#define UNIXPATHMAX 108 + +DECLARE_EVENT(EVENT_Error); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Found); +DECLARE_EVENT(EVENT_Ready); +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Write); + +GB_STREAM_DESC SocketStream = +{ + .open = CSocket_stream_open, + .close = CSocket_stream_close, + .read = CSocket_stream_read, + .write = CSocket_stream_write, + .seek = CSocket_stream_seek, + .tell = CSocket_stream_tell, + .flush = CSocket_stream_flush, + /*.eof = CSocket_stream_eof, + .lof = CSocket_stream_lof,*/ + .handle = CSocket_stream_handle +}; + +#if DEBUG_ME +static void set_status(CSOCKET *_object, int status) +{ + static const char *status_name[] = { "Inactive", "Active", "Pending", "Accepting", "Receiving data", "Searching", "Connecting", "Connected" }; + static const char *error_name[] = { NULL, NULL, "Cannot create socket", "Connection refused", "Cannot read", "Cannot write", "Host not found", NULL, NULL, NULL, + "Cannot bind socket", NULL, NULL, NULL, "Cannot listen", "Cannot bind interface", "Cannot authenticate" }; + SOCKET->status = status; + + fprintf(stderr, "gb.net: socket %p: ", THIS); + if (status >= 0 && status < 7) + fprintf(stderr, "%s", status_name[status]); + else if (status >= -16 && status < 0) + fprintf(stderr, "%s", error_name[-status]); + + fprintf(stderr, " (%d)\n", status); +} +#else +#define set_status(_object, _status) SOCKET->status = (_status) +#endif + +bool SOCKET_update_timeout(CSOCKET_COMMON *socket) +{ + struct timeval timeout; + + if (socket->socket < 0) + return TRUE; + + timeout.tv_sec = socket->timeout / 1000; + timeout.tv_usec = (socket->timeout % 1000) * 1000; + + if (setsockopt(socket->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) + { + GB.Error("Cannot set sending timeout"); + return TRUE; + } + + if (setsockopt(socket->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) + { + GB.Error("Cannot set receiving timeout"); + return TRUE; + } + + return FALSE; +} + +void SOCKET_set_blocking(CSOCKET_COMMON *socket, bool block) +{ + int do_not_block = block ? 0 : 1; + + ioctl(socket->socket, FIONBIO, &do_not_block); + SOCKET_update_timeout(socket); +} + +/********************************** +Routines to call events +**********************************/ + +static void CSocket_post_error(void *_object) +{ + GB.Raise(THIS,EVENT_Error,0); + GB.Unref(POINTER(&_object)); +} + +static void CSocket_post_closed(void *_object) +{ + GB.Raise(THIS, EVENT_Close, 0); + GB.Unref(POINTER(&_object)); +} + +static void CSocket_post_hostfound(void *_object) +{ + GB.Raise(THIS,EVENT_Found,0); + GB.Unref(POINTER(&_object)); +} + +void CSocket_post_connected(void *_object) +{ + GB.Raise(THIS,EVENT_Ready,0); + GB.Unref(POINTER(&_object)); +} + +static void CSocket_close(CSOCKET *_object) +{ + int fd; + + if (THIS->dns_client) + { + dns_close_all(THIS->dns_client); + GB.Unref(POINTER(&THIS->dns_client)); + THIS->dns_client = NULL; + } + + fd = SOCKET->socket; + if (fd >= 0) + { + //fprintf(stderr, "CSocket_close: %p: set fd %d to -1\n", THIS, fd); + SOCKET->socket = -1; + + GB.Watch(fd , GB_WATCH_NONE, NULL, 0); + SOCKET->stream.desc = NULL; + close(fd); + + set_status(THIS, NET_INACTIVE); + } + + if (THIS->timer) + GB.Unref(POINTER(&THIS->timer)); + + if (THIS->OnClose) + THIS->OnClose(_object); +} + +static int connect_timeout(void *_object) +{ + CSocket_stream_internal_error(THIS, NET_CONNECTION_TIMEOUT, TRUE); + return TRUE; +} + +/* + This function is called by DnsClient to inform + that it has finished its work +*/ + +void CSocket_CallBackFromDns(void *_object) +{ + int myval=0; + + if (SOCKET->status != NET_SEARCHING) + return; + + if (!THIS->dns_client->sHostIP) + { + // Host not found + CSocket_stream_internal_error(THIS, NET_HOST_NOT_FOUND, TRUE); + return; + } + + GB.FreeString (&THIS->remote_host_ip); + THIS->remote_host_ip = GB.NewZeroString (THIS->dns_client->sHostIP); + + // We connect to the socket + + THIS->tcp_server.sin_family = AF_INET; + THIS->tcp_server.sin_port = htons(THIS->port); + THIS->tcp_server.sin_addr.s_addr = inet_addr(THIS->dns_client->sHostIP); + bzero(&(THIS->tcp_server.sin_zero), 8); + + // Don't block, so that connect() returns immediately + + SOCKET_set_blocking(SOCKET, FALSE); + myval = connect(SOCKET->socket,(struct sockaddr*)&(THIS->tcp_server), sizeof(struct sockaddr)); + SOCKET_set_blocking(SOCKET, TRUE); + + if (!myval || errno == EINPROGRESS) // this is the good answer : connect in progress + { + set_status(THIS, NET_CONNECTING); + if (SOCKET->timeout > 0) + THIS->timer = GB.Every(SOCKET->timeout, (GB_TIMER_CALLBACK)connect_timeout, (intptr_t)THIS); + GB.Watch(SOCKET->socket, GB_WATCH_WRITE, (void *)CSocket_CallBackConnecting, (intptr_t)THIS); + } + else + { + GB.Watch(SOCKET->socket , GB_WATCH_NONE, NULL, 0); + SOCKET->stream.desc = NULL; + close(SOCKET->socket); + SOCKET->socket = -1; + set_status(THIS, NET_INACTIVE); + } + + if (THIS->dns_client) + { + dns_close_all(THIS->dns_client); + GB.Unref(POINTER(&THIS->dns_client)); + THIS->dns_client = NULL; + } + + if (SOCKET->status <= NET_INACTIVE) + { + CSocket_stream_internal_error(THIS, NET_CONNECTION_REFUSED, TRUE); + return; + } + + GB.Ref(THIS); + GB.Post(CSocket_post_hostfound, (intptr_t)THIS); +} + + +void CSOCKET_init_connected(CSOCKET *_object) +{ + GB.Watch(SOCKET->socket, GB_WATCH_READ, (void *)CSocket_CallBack, (intptr_t)THIS); + SOCKET->stream.desc = &SocketStream; + SOCKET_update_timeout(SOCKET); +} + +/******************************************************************* +This CallBack is used while waiting to finish a connection process +******************************************************************/ +void CSocket_CallBackConnecting(int t_sock,int type,intptr_t param) +{ + struct sockaddr_in myhost; + int mylen; + void *_object = (void *)param; + + GB.Watch(SOCKET->socket, GB_WATCH_NONE, NULL, 0); + + if (SOCKET->status != NET_CONNECTING) return; + + /**************************************************** + Checks if Connection was Stablished or there was + an error trying to connect + ****************************************************/ + + set_status(THIS, CheckConnection(SOCKET->socket)); + if (SOCKET->status == NET_INACTIVE) + { + CSocket_stream_internal_error(THIS, NET_CONNECTION_REFUSED, TRUE); + return; + } + if (SOCKET->status != NET_CONNECTED) return; + // we obtain local IP and host + + mylen=sizeof(struct sockaddr); + getsockname (SOCKET->socket,(struct sockaddr*)&myhost,(socklen_t *)&mylen); + THIS->local_port = ntohs(myhost.sin_port); + GB.FreeString(&THIS->local_host_ip); + THIS->local_host_ip = GB.NewZeroString(inet_ntoa(myhost.sin_addr)); + + CSOCKET_init_connected(THIS); + GB.Stream.SetSwapping(&SOCKET->stream, htons(1234) != 1234); + + GB.Ref(THIS); + GB.Post(CSocket_post_connected,(intptr_t)THIS); + + GB.Unref(POINTER(&THIS->timer)); +} + +/******************************************************************* +This CallBack is used while socket is connected to remote host +******************************************************************/ +static void callback_write(int t_sock,int type, CSOCKET *_object) +{ + //fprintf(stderr, "callback write %p\n", THIS); + THIS->watch_write = FALSE; + GB.Watch(SOCKET->socket, GB_WATCH_WRITE, NULL, 0); + GB.Raise(THIS, EVENT_Write, 0); +} + +void CSocket_CallBack(int t_sock,int type, CSOCKET *_object) +{ + char buf[1]; + struct pollfd mypoll; + int numpoll; + struct timespec mywait; + + //fprintf(stderr, "callback read %p\n", THIS); + /* Just sleeping a little to reduce CPU waste */ + mywait.tv_sec=0; + mywait.tv_nsec=100000; + nanosleep(&mywait,NULL); + + /* is there data available or an error? */ + if (SOCKET->status != NET_CONNECTED) return; + + mypoll.fd=t_sock; + mypoll.events=POLLIN | POLLNVAL; + mypoll.revents=0; + numpoll=poll(&mypoll,1,0); + if (numpoll<=0) return; + /* there's data available */ + + USE_MSG_NOSIGNAL(numpoll=recv(t_sock,(void*)buf,sizeof(char),MSG_PEEK | MSG_NOSIGNAL)); + if (!numpoll) + { /* socket error, no valid data received */ + + CSocket_close(THIS); + GB.Ref(THIS); + GB.Post(CSocket_post_closed, (intptr_t)THIS); + return; + } + /****************************************************** + There's data available to read, so we'll raise event + EVENT_Read + *******************************************************/ + + GB.Raise(THIS, EVENT_Read, 0); + //GB.Ref(_object); + //GB.Post(CSocket_post_data_available,(intptr_t)THIS); +} + + +void CSocket_stream_internal_error(void *_object, int ncode, bool post) +{ + CSocket_close(THIS); + + /* fatal socket error handling */ + set_status(THIS, ncode); + + if (post) + { + GB.Ref(THIS); + GB.Post(CSocket_post_error, (intptr_t)THIS); + } +} + +////////////////////////////////////////////////////////////////////////////////// +//################################################################################ +/********************************************************************************* + "PUBLIC" C/C++ INTERFACE +**********************************************************************************/ +//################################################################################ +////////////////////////////////////////////////////////////////////////////////// + +/* not allowed methods */ +int CSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +int CSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence){return -1;} +int CSocket_stream_tell(GB_STREAM *stream, int64_t *pos){return -1;} + +int CSocket_stream_flush(GB_STREAM *stream) +{ + return 0; /* OK */ +} + +int CSocket_stream_handle(GB_STREAM *stream) +{ + void *_object = stream->tag; + return SOCKET->socket; +} + +int CSocket_stream_close(GB_STREAM *stream) +{ + void *_object = stream->tag; + + if (!THIS) return -1; + CSocket_close(THIS); + + return 0; +} + +/*int CSocket_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = stream->tag; + int bytes; + + *len=0; + if (!THIS) return -1; + + if (ioctl(SOCKET->socket,FIONREAD,&bytes)) + { + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + return -1; + } + *len=bytes; + return 0; +} + +int CSocket_stream_eof(GB_STREAM *stream) +{ + void *_object = stream->tag; + int bytes; + + if (!THIS) return -1; + + if (ioctl(SOCKET->socket,FIONREAD,&bytes)) + { + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + return -1; + } + if (!bytes) return -1; + return 0; +}*/ + +int CSocket_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos=-1; + //int bytes; + + if (!THIS) return -1; + + /*if (ioctl(SOCKET->socket,FIONREAD,&bytes)) + { + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + return -1; + } + //if (bytes < len) return -1; + if (bytes < len) + len = bytes;*/ + + USE_MSG_NOSIGNAL(npos = recv(SOCKET->socket,(void*)buffer,len*sizeof(char),MSG_NOSIGNAL)); + + if (npos < 0 && errno != EAGAIN) + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + + return npos; +} + +int CSocket_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos=-1; + + if (!THIS) return -1; + + USE_MSG_NOSIGNAL(npos = send(SOCKET->socket,(void*)buffer,len*sizeof(char),MSG_NOSIGNAL)); + + if (npos >= 0 || errno == EAGAIN) + { + if (GB.CanRaise(THIS, EVENT_Write) && !THIS->watch_write) + { + //fprintf(stderr, "watch write %p\n", THIS); + THIS->watch_write = TRUE; + GB.Watch(SOCKET->socket, GB_WATCH_WRITE, (void *)callback_write, (intptr_t)THIS); + } + } + + if ((npos < 0) && errno != EAGAIN) + CSocket_stream_internal_error(THIS, NET_CANNOT_WRITE, FALSE); + + return npos; +} + + + +/************************************************************************** +To start a UNIX connection +**************************************************************************/ +int CSocket_connect_unix(void *_object,char *sPath, int lenpath) +{ + int ret; + + if ( SOCKET->status > NET_INACTIVE ) return 1; + if (!sPath) return 7; + if ( (lenpath<1) || (lenpath>UNIXPATHMAX) ) return 7; + + GB.FreeString(&THIS->remote_host_ip); + GB.FreeString(&THIS->local_host_ip); + + THIS->unix_server.sun_family=AF_UNIX; + strcpy(THIS->unix_server.sun_path,sPath); + if ( (SOCKET->socket=socket(AF_UNIX,SOCK_STREAM,0))==-1 ) + { + set_status(THIS, NET_CANNOT_CREATE_SOCKET); + GB.Ref (THIS); + CSocket_post_error(_object); /* Unable to create socket */ + return 2; + } + + //GB.FreeString(&THIS->sPath); + //THIS->sPath = GB.NewZeroString(THIS->unix_server.sun_path); + + THIS->conn_type = NET_TYPE_INTERNET; + + ret = connect(SOCKET->socket,(struct sockaddr*)&THIS->unix_server,sizeof(struct sockaddr_un)); + + // Set socket to blocking mode, after the connect() call! + SOCKET_set_blocking(SOCKET, TRUE); + + if (ret == 0) + { + set_status(THIS, NET_CONNECTED); + CSOCKET_init_connected(THIS); + + // $BM + if (THIS->host) GB.FreeString(&THIS->host); + if (THIS->path) GB.FreeString(&THIS->path); + + THIS->path = GB.NewZeroString(sPath); + GB.Ref (THIS); + CSocket_post_connected(_object); + + return 0; + } + + // Error + SOCKET->stream.desc = NULL; + close(SOCKET->socket); + //GB.FreeString(&THIS->sPath); + set_status(THIS, NET_CONNECTION_REFUSED); + + GB.Ref (THIS); + CSocket_post_error(_object); /* Unable to connect to remote host */ + + return 3; +} + +/************************************************************************** +To start a TCP connection +**************************************************************************/ +int CSocket_connect_socket(void *_object,char *sHost,int lenhost,int myport) +{ + if ( SOCKET->status > NET_INACTIVE ) return 1; + if (!lenhost) return 9; + if (!sHost) return 9; + if ( (myport<1) || (myport>65535) ) return 8; + + GB.FreeString(&THIS->remote_host_ip); + GB.FreeString(&THIS->local_host_ip); + + if ( (SOCKET->socket=socket(AF_INET,SOCK_STREAM,0))==-1 ) + { + set_status(THIS, NET_CANNOT_CREATE_SOCKET); + GB.Ref (THIS); + CSocket_post_error(_object); + return 2; + } + + // Set socket to blocking mode + SOCKET_set_blocking(SOCKET, TRUE); + + THIS->port=myport; + THIS->conn_type = NET_TYPE_INTERNET; + + /****************************************** + Let's turn hostname into host IP + *******************************************/ + if (!THIS->dns_client) + { + THIS->dns_client = GB.New(GB.FindClass("DnsClient"), NULL, NULL); + THIS->dns_client->CliParent=_object; + } + + if (THIS->dns_client->iStatus > 0 ) dns_close_all(THIS->dns_client); + + dns_set_async_mode(1,THIS->dns_client); + GB.FreeString (&(THIS->dns_client->sHostName)); + THIS->dns_client->sHostName = GB.NewString(sHost,lenhost); + THIS->dns_client->finished_callback=CSocket_CallBackFromDns; + + /******************************************** + We start DNS lookup, when it is finished + it will call to CSocket_CallBack_fromDns, + and we'll continue there connection proccess + ********************************************/ + set_status(THIS, NET_SEARCHING); /* looking for IP */ + dns_thread_getip(THIS->dns_client); + SOCKET->stream.desc=&SocketStream; + THIS->use_port = THIS->port; + + // $BM + if (THIS->path) GB.FreeString(&THIS->path); + + if (sHost != THIS->host) + { + if (THIS->host) GB.FreeString(&THIS->host); + THIS->host = GB.NewZeroString(sHost); + } + + return 0; +} + +/********************************************** +This function is used to peek data from socket, +you have to pass 3 parameters: + +_object-> CSocket object +buf -> Data Buffer (you have to free it after using it!) +MaxLen -> 0 no limit, >0 max. data t read +***********************************************/ +int CSocket_peek_data(void *_object,char **buf,int MaxLen) +{ + int retval=0; + int nread=0; + int bytes=0; + + (*buf)=NULL; + nread=ioctl(SOCKET->socket,FIONREAD,&bytes); /* Is there anythig to read? */ + if (nread) + retval=-1; + else + retval=0; + if ( (!retval) && (bytes) ) /* if there's anything to receive */ + { + if (bytes > MAX_CLIENT_BUFFER_SIZE) bytes=MAX_CLIENT_BUFFER_SIZE; + if (MaxLen >0 ) bytes=MaxLen; + GB.Alloc((void**)buf,bytes); + (*buf)[0]='\0'; + USE_MSG_NOSIGNAL(retval=recv(SOCKET->socket,(void*)(*buf),bytes*sizeof(char),MSG_PEEK|MSG_NOSIGNAL)); + } + + if (retval==-1) + { + /* An error happened while trying to receive data : SOCKET ERROR */ + if (*buf) + { + GB.Free(POINTER(buf)); + buf=NULL; + } + GB.Watch (SOCKET->socket , GB_WATCH_NONE , (void *)CSocket_CallBack,0); + SOCKET->stream.desc=NULL; + close(SOCKET->socket); + set_status(THIS, NET_CANNOT_READ); + GB.Ref (THIS); + CSocket_post_error(_object); + return -1; + } + + return retval; +} + +/***************************************************************************************** +########################################################################################## +-------------------------SOCKET GAMBAS INTERFACE IMPLEMENTATION--------------------- +########################################################################################## +******************************************************************************************/ +/******************************************************************** +Returns current Status of the socket (connected,connecting,closed) +*********************************************************************/ +BEGIN_PROPERTY(Socket_Status) + + GB.ReturnInteger(SOCKET->status); + +END_PROPERTY + +/******************************************************************** +Port to connect to. Can be 'Net.Local' for Unix sockets, or +1-65535 for TCP sockets +********************************************************************/ +BEGIN_PROPERTY(Socket_Port) + + int port; + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->use_port); + return; + } + + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Port property cannot be changed while the socket is active"); + return; + } + + port = VPROP(GB_INTEGER); + + if (port < 0 || port > 65535) + { + GB.Error("Invalid port number"); + return; + } + + THIS->use_port = port; + +END_PROPERTY + +/********************************************************************* + Host or Path to connect to. If 'Port' value is zero, this + will be a local path, else, a remote host name +**********************************************************************/ + +BEGIN_PROPERTY (Socket_Host) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(THIS->host); + else + GB.StoreString(PROP(GB_STRING), &THIS->host); + +END_PROPERTY + +BEGIN_PROPERTY (Socket_Path) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(THIS->path); + else + GB.StoreString(PROP(GB_STRING), &THIS->path); + +END_PROPERTY + +/******************************************************************** +Returns current TCP remote port (only when connected via TCP) +*********************************************************************/ +BEGIN_PROPERTY(Socket_RemotePort) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnInteger(0); + else + GB.ReturnInteger(THIS->port); + +END_PROPERTY + +/******************************************************************** +Returns current TCP local port (only when connected via TCP) +*********************************************************************/ +BEGIN_PROPERTY(Socket_LocalPort) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnInteger(0); + else + GB.ReturnInteger(THIS->local_port); + +END_PROPERTY + +/*********************************************************************** +Returns current foreing host IP (only when connected via TCP) +***********************************************************************/ +BEGIN_PROPERTY(Socket_RemoteHost) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnVoidString(); + else + GB.ReturnString(THIS->remote_host_ip); + +END_PROPERTY + +/*********************************************************************** +Returns current local host IP (only when connected via TCP) +***********************************************************************/ +BEGIN_PROPERTY(Socket_LocalHost) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnVoidString(); + else + GB.ReturnString(THIS->local_host_ip); + +END_PROPERTY + +/**************************************************** +Gambas object "Constructor" +****************************************************/ +BEGIN_METHOD_VOID(Socket_new) + + SOCKET->stream.tag = THIS; + THIS->use_port = 80; + SOCKET->socket = -1; + +END_METHOD + +/************************************************** +Gambas object "Destructor" +**************************************************/ +BEGIN_METHOD_VOID(Socket_free) + + CSocket_close(THIS); + + //GB.FreeString(&THIS->sPath); + GB.FreeString(&THIS->local_host_ip); + GB.FreeString(&THIS->remote_host_ip); + GB.FreeString(&THIS->host); + GB.FreeString(&THIS->path); + +END_METHOD + +/************************************************************* +To Peek data arrived from the other side of the socket +**************************************************************/ +BEGIN_METHOD_VOID(Socket_Peek) + + char *buf=NULL; + int retval=0; + + if (SOCKET->status != NET_CONNECTED) /* if socket is not connected we can't receive anything */ + { + GB.Error("Socket is not connected"); + return; + } + + retval = CSocket_peek_data(_object, &buf, 0); + + if (retval == -1) + { + /* An error happened while trying to receive data : SOCKET ERROR */ + if (buf) GB.Free(POINTER(&buf)); + GB.ReturnVoidString(); + return; + } + + if (retval > 0) + GB.ReturnNewString(buf, retval); + else + GB.ReturnVoidString(); + + if (buf) GB.Free(POINTER(&buf)); + +END_METHOD + + + +/************************************************************************** +To start a TCP or UNIX connection +**************************************************************************/ + +BEGIN_METHOD(Socket_Connect, GB_STRING HostOrPath; GB_INTEGER Port) + + int port; + int err; + + port = VARGOPT(Port, THIS->use_port); + + if (!port) + { + if (MISSING(HostOrPath)) + err = CSocket_connect_unix(_object,THIS->path,GB.StringLength(THIS->path)); + else + err = CSocket_connect_unix(_object,STRING(HostOrPath),LENGTH(HostOrPath)); + } + else + { + if (MISSING(HostOrPath)) + err = CSocket_connect_socket(_object,THIS->host,GB.StringLength(THIS->host),port); + else + err = CSocket_connect_socket(_object,STRING(HostOrPath),LENGTH(HostOrPath),port); + } + + switch (err) + { + case 1: GB.Error("Socket is already connected"); return; + case 2: GB.Error("Invalid path length"); return; + case 8: GB.Error("Port value out of range"); return; + case 9: GB.Error("Invalid host name"); return; + } + +END_METHOD + +BEGIN_PROPERTY(Socket_Timeout) + + if (READ_PROPERTY) + GB.ReturnInteger(SOCKET->timeout); + else + { + int val = VPROP(GB_INTEGER); + if (val < 0) + val = 0; + SOCKET->timeout = val; + SOCKET_update_timeout(SOCKET); + } + +END_PROPERTY + +BEGIN_PROPERTY(Socket_Server) + + GB.ReturnObject(THIS->parent); + +END_PROPERTY + +/********************************************************** +Here we declare public structure of Socket Class +***********************************************************/ + +GB_DESC CSocketDesc[] = +{ + GB_DECLARE("Socket", sizeof(CSOCKET)), GB_INHERITS("Stream"), + + GB_EVENT("Error", NULL, NULL, &EVENT_Error), + GB_EVENT("Ready", NULL, NULL, &EVENT_Ready), + GB_EVENT("Closed", NULL, NULL, &EVENT_Close), + GB_EVENT("Found", NULL, NULL, &EVENT_Found), + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Write", NULL, NULL, &EVENT_Write), + + GB_METHOD("_new", NULL, Socket_new, NULL), + GB_METHOD("_free", NULL, Socket_free, NULL), + GB_METHOD("Peek", "s", Socket_Peek, NULL), + GB_METHOD("Connect",NULL, Socket_Connect,"[(HostOrPath)s(Port)i]"), + + GB_PROPERTY_READ("Status", "i", Socket_Status), + GB_PROPERTY_READ("RemotePort", "i", Socket_RemotePort), + GB_PROPERTY_READ("LocalPort", "i", Socket_LocalPort), + GB_PROPERTY_READ("RemoteHost", "s", Socket_RemoteHost), + GB_PROPERTY_READ("LocalHost", "s", Socket_LocalHost), + + GB_PROPERTY("Timeout", "i", Socket_Timeout), + + GB_PROPERTY("Host", "s", Socket_Host), + GB_PROPERTY("Path", "s", Socket_Path), + GB_PROPERTY("Port", "i", Socket_Port), + GB_PROPERTY_READ("Server", "ServerSocket", Socket_Server), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "Host,Path,Port=80,Timeout{Range:0;3600000;10;ms}"), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; diff --git a/gb.net/src/CSocket.h b/gb.net/src/CSocket.h new file mode 100644 index 00000000..6a1349f6 --- /dev/null +++ b/gb.net/src/CSocket.h @@ -0,0 +1,112 @@ +/*************************************************************************** + + CSocket.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSOCKET_H +#define __CSOCKET_H + +#include "gambas.h" +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include "CDnsClient.h" + +#ifndef __CSOCKET_C + +extern GB_DESC CSocketDesc[]; +extern GB_STREAM_DESC SocketStream; + +#else + +#define THIS ((CSOCKET *)_object) +#define SOCKET (&THIS->common) + +#endif + +typedef + struct { + GB_BASE ob; + GB_STREAM stream; + int socket; + int status; + int timeout; + } + CSOCKET_COMMON; + +typedef + struct + { + CSOCKET_COMMON common; + struct sockaddr_in tcp_server; // struct for TCP connections + struct sockaddr_un unix_server; // struct for UNIX connections + unsigned short use_port; + unsigned short port; + unsigned short local_port; + unsigned conn_type : 2; + unsigned watch_write : 1; + char *local_host_ip; + char *remote_host_ip; + char *host; + char *path; + CDNSCLIENT *dns_client; + GB_TIMER *timer; + void *parent; + void (*OnClose)(void *sck); + } + CSOCKET; + +void CSocket_CallBack(int t_sock,int type, CSOCKET *_object); +void CSocket_CallBackConnecting(int t_sock,int type,intptr_t lParam); + +void CSOCKET_init_connected(CSOCKET *_object); + +void CSocket_post_connected(void *_object); +// +int CSocket_connect_unix(void *_object, char *sPath, int lenpath); +int CSocket_connect_socket(void *_object, char *sHost,int lenhost,int myport); +int CSocket_peek_data(void *_object,char **buf,int MaxLen); +// +void CSocket_stream_internal_error(void *_object,int ncode, bool post); +// +int CSocket_stream_read(GB_STREAM *stream, char *buffer, int len); +int CSocket_stream_write(GB_STREAM *stream, char *buffer, int len); +int CSocket_stream_eof(GB_STREAM *stream); +int CSocket_stream_lof(GB_STREAM *stream, int64_t *len); +int CSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CSocket_stream_tell(GB_STREAM *stream, int64_t *pos); +int CSocket_stream_flush(GB_STREAM *stream); +int CSocket_stream_close(GB_STREAM *stream); +int CSocket_stream_handle(GB_STREAM *stream); + +bool SOCKET_update_timeout(CSOCKET_COMMON *socket); +void SOCKET_set_blocking(CSOCKET_COMMON *socket, bool block); + +DECLARE_METHOD(Socket_Timeout); + +#endif diff --git a/gb.net/src/CUdpSocket.c b/gb.net/src/CUdpSocket.c new file mode 100644 index 00000000..8c1d4dff --- /dev/null +++ b/gb.net/src/CUdpSocket.c @@ -0,0 +1,792 @@ +/*************************************************************************** + + CUdpSocket.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CUDPSOCKET_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "tools.h" + +#include "CUdpSocket.h" + +DECLARE_EVENT (EVENT_Read); +DECLARE_EVENT (EVENT_SocketError); + +GB_STREAM_DESC UdpSocketStream = { + .open = CUdpSocket_stream_open, + .close = CUdpSocket_stream_close, + .read = CUdpSocket_stream_read, + .write = CUdpSocket_stream_write, + .seek = CUdpSocket_stream_seek, + .tell = CUdpSocket_stream_tell, + .flush = CUdpSocket_stream_flush, + .eof = CUdpSocket_stream_eof, + .lof = CUdpSocket_stream_lof, + .handle = CUdpSocket_stream_handle +}; + +static bool fill_in_addr(struct in_addr *addr, const char *str) +{ + if (!str || !*str) + addr->s_addr = htonl(INADDR_ANY); + else if (inet_aton(str, addr) == 0) + { + GB.Error("Incorrect address"); + return TRUE; + } + + return FALSE; +} + +void CUdpSocket_post_data(intptr_t Param) +{ + CUDPSOCKET *t_obj; + t_obj=(CUDPSOCKET*)Param; + GB.Raise(t_obj,EVENT_Read,0); + GB.Unref(POINTER(&t_obj)); +} + +void CUdpSocket_post_error(intptr_t Param) +{ + CUDPSOCKET *t_obj; + t_obj=(CUDPSOCKET*)Param; + GB.Raise(t_obj,EVENT_SocketError,0); + GB.Unref(POINTER(&t_obj)); +} + +static void clear_buffer(CUDPSOCKET *_object) +{ + if (THIS->buffer) + { + GB.Free(POINTER(&THIS->buffer)); + THIS->buffer_pos = 0; + THIS->buffer_len = 0; + } +} + +static void fill_buffer(CUDPSOCKET *_object) +{ + socklen_t host_len; + int ret, block; + char buffer[1]; + + //fprintf(stderr, "fill_buffer\n"); + + clear_buffer(THIS); + + host_len = sizeof(THIS->addr); + + block = GB.Stream.Block(&SOCKET->stream, TRUE); + USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void*)buffer, sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len)); + GB.Stream.Block(&SOCKET->stream, block); + + if (ioctl(SOCKET->socket, FIONREAD, &THIS->buffer_len)) + return; + + //fprintf(stderr, "buffer_len = %d\n", THIS->buffer_len); + + if (THIS->buffer_len) + GB.Alloc(POINTER(&THIS->buffer), THIS->buffer_len); + + USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void *)THIS->buffer, THIS->buffer_len, MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len)); + + //fprintf(stderr, "recvfrom() -> %d\n", ret); + + if (ret < 0) + { + CUdpSocket_stream_close(&SOCKET->stream); + SOCKET->status = NET_CANNOT_READ; + return; + } + +// THIS->sport = ntohs(host.sin_port); +// GB.FreeString(&THIS->shost); +// GB.NewString (&THIS->shost , inet_ntoa(host.sin_addr) ,0); +} + +void CUdpSocket_CallBack(int t_sock,int type, intptr_t param) +{ + //struct sockaddr_in t_test; + //unsigned int t_test_len; + struct timespec mywait; + CUDPSOCKET *_object = (CUDPSOCKET *)param; + + /* Just sleeping a little to reduce CPU waste */ + mywait.tv_sec=0; + mywait.tv_nsec=100000; + nanosleep(&mywait,NULL); + + if (SOCKET->status <= NET_INACTIVE) return; + + //t_test.sin_port=0; + //t_test_len=sizeof(struct sockaddr); + + //USE_MSG_NOSIGNAL(numpoll=recvfrom(t_sock,POINTER(buf), sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&t_test, &t_test_len)); + fill_buffer(THIS); + + if (THIS->buffer) + { + GB.Ref((void*)THIS); + GB.Post(CUdpSocket_post_data, (intptr_t)THIS); + } +} +/* not allowed methods */ +int CUdpSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +int CUdpSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence){return -1;} +int CUdpSocket_stream_tell(GB_STREAM *stream, int64_t *pos) +{ + *pos=0; + return -1; /* not allowed */ +} + +int CUdpSocket_stream_flush(GB_STREAM *stream) +{ + return 0; /* OK */ +} + +int CUdpSocket_stream_handle(GB_STREAM *stream) +{ + void *_object = stream->tag; + return SOCKET->socket; +} + +int CUdpSocket_stream_close(GB_STREAM *stream) +{ + void *_object = stream->tag; + + if ( !_object ) return -1; + stream->desc=NULL; + if (SOCKET->status > NET_INACTIVE) + { + GB.Watch (SOCKET->socket,GB_WATCH_NONE,(void *)CUdpSocket_CallBack,(intptr_t)THIS); + close(SOCKET->socket); + SOCKET->status = NET_INACTIVE; + } + + GB.FreeString(&THIS->thost); + GB.FreeString(&THIS->tpath); + + if (THIS->path) + { + unlink(THIS->path); + GB.FreeString(&THIS->path); + } + + THIS->tport=0; + SOCKET->status = NET_INACTIVE; + clear_buffer(THIS); + return 0; +} + +int CUdpSocket_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = stream->tag; + *len = THIS->buffer_len - THIS->buffer_pos; + return 0; +} + +int CUdpSocket_stream_eof(GB_STREAM *stream) +{ + void *_object = stream->tag; + return THIS->buffer_pos >= THIS->buffer_len; +} + +int CUdpSocket_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int len_max; + + if ( !_object ) return TRUE; + + len_max = THIS->buffer_len - THIS->buffer_pos; + + if (len_max <= 0) + return TRUE; + + if (len > len_max) + len = len_max; + + memcpy(buffer, &THIS->buffer[THIS->buffer_pos], len); + THIS->buffer_pos += len; + + return len; +} + +int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int retval; + //struct in_addr dest_ip; + NET_ADDRESS dest; + size_t size; + struct sockaddr *addr; + + if (!THIS) return -1; + + CLEAR(&dest); + + if (THIS->tpath && *THIS->tpath) + { + dest.un.sun_family = PF_UNIX; + strcpy(dest.un.sun_path, THIS->tpath); + size = sizeof(struct sockaddr_un); + addr = (struct sockaddr *)&dest.un; + } + else + { + if (fill_in_addr(&dest.in.sin_addr, THIS->thost)) + return -1; + //dest.in.sin_addr.s_addr = dest_ip.s_addr; + dest.in.sin_family = PF_INET; + dest.in.sin_port = htons(THIS->tport); + size = sizeof(struct sockaddr); + addr = (struct sockaddr *)&dest.in; + } + + //fprintf(stderr, "write: %s %d %d '%.*s'\n", THIS->thost, THIS->tport, len, len, buffer); + + USE_MSG_NOSIGNAL(retval = sendto(SOCKET->socket, (void*)buffer, len, MSG_NOSIGNAL, addr, size)); + + if (retval < 0) + { + CUdpSocket_stream_close(stream); + SOCKET->status= NET_CANNOT_WRITE; + } + + return retval; +} + +/************************************************************************************************ +################################################################################################ +--------------------UDPSOCKET CLASS GAMBAS INTERFACE IMPLEMENTATION------------------------------ +################################################################################################ +***********************************************************************************************/ + +static bool return_error(int err, const char *msg) +{ + if (err != 0) + { + GB.Error(msg, strerror(errno)); + return TRUE; + } + else + return FALSE; +} + +static bool update_broadcast(CUDPSOCKET *_object) +{ + int broadcast; + + if (SOCKET->socket < 0) + return FALSE; + + broadcast = THIS->broadcast; + return return_error(setsockopt(SOCKET->socket, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(int)), + "Cannot set broadcast socket option: &1"); +} + +static bool update_multicast_loop(CUDPSOCKET *_object) +{ + if (SOCKET->socket < 0) + return FALSE; + + return return_error(setsockopt(SOCKET->socket, IPPROTO_IP, IP_MULTICAST_LOOP, &THIS->mc_loop, sizeof(unsigned char)), + "Cannot set multicast loop socket option: &1"); +} + +static bool update_multicast_ttl(CUDPSOCKET *_object) +{ + if (SOCKET->socket < 0) + return FALSE; + + return return_error(setsockopt(SOCKET->socket, IPPROTO_IP, IP_MULTICAST_TTL, &THIS->mc_ttl, sizeof(unsigned char)), + "Cannot set multicast ttl socket option: &1"); +} + +static void dgram_start(CUDPSOCKET *_object) +{ + sa_family_t domain; + size_t size; + struct stat info; + struct sockaddr *addr; + struct in_addr mc_interface_addr; + + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Socket is active"); + return; + } + + if (THIS->path && *THIS->path) + { + domain = PF_UNIX; + if (strlen(THIS->path) >= NET_UNIX_PATH_MAX) + { + GB.Error("Socket path is too long"); + return; + } + } + else + { + domain = PF_INET; + } + + if ((SOCKET->socket = socket(domain, SOCK_DGRAM, 0)) < 0) + goto CANNOT_CREATE_SOCKET; + + if (update_broadcast(THIS) || SOCKET_update_timeout(SOCKET)) + goto CANNOT_CREATE_SOCKET; + + CLEAR(&THIS->addr); + + if (domain == PF_UNIX) + { + if (stat(THIS->path, &info) >= 0 && S_ISSOCK(info.st_mode)) + unlink(THIS->path); + THIS->addr.un.sun_family = domain; + strcpy(THIS->addr.un.sun_path, THIS->path); + size = sizeof(struct sockaddr_un); + addr = (struct sockaddr *)&THIS->addr.un; + } + else + { + THIS->addr.in.sin_family = domain; + if (fill_in_addr(&THIS->addr.in.sin_addr, THIS->host)) + goto CANNOT_CREATE_SOCKET; + + THIS->addr.in.sin_port = htons(THIS->port); + size = sizeof(struct sockaddr_in); + addr = (struct sockaddr *)&THIS->addr.in; + + if (update_multicast_loop(THIS) || update_multicast_ttl(THIS)) + goto CANNOT_CREATE_SOCKET; + + if (fill_in_addr(&mc_interface_addr, THIS->mc_interface)) + goto CANNOT_CREATE_SOCKET; + + setsockopt(SOCKET->socket, IPPROTO_IP, IP_MULTICAST_IF, &mc_interface_addr, sizeof(mc_interface_addr)); + } + + if (bind(SOCKET->socket, addr, size) < 0) + { + close(SOCKET->socket); + SOCKET->status = NET_CANNOT_BIND_SOCKET; + GB.Ref(THIS); + GB.Post(CUdpSocket_post_error, (intptr_t)THIS); + return; + } + + SOCKET->status = NET_ACTIVE; + SOCKET->stream.desc = &UdpSocketStream; + GB.Stream.SetSwapping(&SOCKET->stream, htons(0x1234) != 0x1234); + + GB.Watch(SOCKET->socket, GB_WATCH_READ, (void *)CUdpSocket_CallBack, (intptr_t)THIS); + return; + +CANNOT_CREATE_SOCKET: + + SOCKET->status = NET_CANNOT_CREATE_SOCKET; + GB.Ref(THIS); + GB.Post(CUdpSocket_post_error, (intptr_t)THIS); + return; +} + + +BEGIN_PROPERTY(UdpSocket_Status) + + GB.ReturnInteger(SOCKET->status); + +END_PROPERTY + + +BEGIN_PROPERTY(UdpSocket_SourceHost) + + if (THIS->addr.a.sa_family == PF_INET) + GB.ReturnNewZeroString(inet_ntoa(THIS->addr.in.sin_addr)); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocket_SourcePort) + + if (THIS->addr.a.sa_family == PF_INET) + GB.ReturnInteger(ntohs(THIS->addr.in.sin_port)); + else + GB.ReturnInteger(0); + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocket_SourcePath) + + if (THIS->addr.a.sa_family == PF_UNIX) + GB.ReturnNewZeroString(THIS->addr.un.sun_path); + else + GB.ReturnVoidString(); + +END_PROPERTY + +static void handle_address_property(void *_object, void *_param, char **store, bool allow_any) +{ + char *addr; + struct in_addr rem_ip; + + if (READ_PROPERTY) + { + GB.ReturnString(*store); + } + else + { + addr = GB.ToZeroString(PROP(GB_STRING)); + + if ((*addr == 0 && !allow_any) || !inet_aton(addr, &rem_ip)) + { + GB.Error("Invalid IP address"); + return; + } + + GB.StoreString(PROP(GB_STRING), store); + } +} + +BEGIN_PROPERTY(UdpSocket_TargetHost) + + handle_address_property(_object, _param, &THIS->thost, FALSE); + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocket_TargetPort) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->tport); + else + { + int port = VPROP(GB_INTEGER); + + if (port < 1 || port > 65535) + { + GB.Error("Invalid port number"); + return; + } + + THIS->tport = port; + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocket_TargetPath) + + if (READ_PROPERTY) + GB.ReturnString(THIS->tpath); + else + { + if (PLENGTH() >= NET_UNIX_PATH_MAX) + { + GB.Error("Socket path is too long"); + return; + } + GB.StoreString(PROP(GB_STRING), &THIS->tpath); + } + +END_PROPERTY + +/************************************************* +Gambas object "Constructor" +*************************************************/ + +BEGIN_METHOD_VOID(UdpSocket_new) + + SOCKET->stream.tag = _object; + SOCKET->socket = -1; + THIS->mc_loop = 1; + THIS->mc_ttl = 1; + +END_METHOD + +/************************************************* +Gambas object "Destructor" +*************************************************/ + +BEGIN_METHOD_VOID(UdpSocket_free) + + CUdpSocket_stream_close(&SOCKET->stream); + GB.FreeString(&THIS->host); + +END_METHOD + + +BEGIN_METHOD_VOID (UdpSocket_Peek) + + char *sData=NULL; + socklen_t host_len; + int retval=0; + //int NoBlock=0; + int peeking; + int bytes=0; + if (SOCKET->status <= NET_INACTIVE) + { + GB.Error("Socket is inactive"); + return; + } + + peeking = MSG_NOSIGNAL | MSG_PEEK; + + ioctl(SOCKET->socket,FIONREAD,&bytes); + if (bytes) + { + GB.Alloc( POINTER(&sData),bytes*sizeof(char) ); + host_len = sizeof(THIS->addr); + //ioctl(SOCKET->socket,FIONBIO,&NoBlock); + USE_MSG_NOSIGNAL(retval=recvfrom(SOCKET->socket, (void*)sData, 1024 * sizeof(char), peeking, (struct sockaddr*)&THIS->addr, &host_len)); + if (retval<0) + { + GB.Free(POINTER(&sData)); + CUdpSocket_stream_close(&SOCKET->stream); + SOCKET->status = NET_CANNOT_READ; + GB.Raise(THIS,EVENT_SocketError,0); + GB.ReturnVoidString(); + return; + } + //NoBlock++; + //ioctl(SOCKET->socket,FIONBIO,&NoBlock); + if (retval>0) + GB.ReturnNewString(sData,retval); + else + GB.ReturnVoidString(); + GB.Free(POINTER(&sData)); + } + else + { + GB.ReturnVoidString(); + } + +END_METHOD + +BEGIN_METHOD_VOID(UdpSocket_Bind) + + dgram_start(THIS); + +END_METHOD + + +BEGIN_PROPERTY(UdpSocket_Broadcast) + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->broadcast); + } + else + { + THIS->broadcast = VPROP(GB_BOOLEAN); + update_broadcast(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocket_Port) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->port); + else + { + int port = VPROP(GB_INTEGER); + if (port < 0 || port > 65535) + { + GB.Error("Invalid port value"); + return; + } + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Socket is active"); + return; + } + THIS->port = port; + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocket_Path) + + if (READ_PROPERTY) + GB.ReturnString(THIS->path); + else + { + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Socket is active"); + return; + } + GB.StoreString(PROP(GB_STRING), &THIS->path); + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocket_Host) + + handle_address_property(_object, _param, &THIS->host, FALSE); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(UdpSocketMulticast_Loop) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->mc_loop); + else + { + THIS->mc_loop = VPROP(GB_BOOLEAN); + update_multicast_loop(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocketMulticast_Ttl) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->mc_ttl); + else + { + int ttl = VPROP(GB_INTEGER); + if (ttl < 0 || ttl > 255) + { + GB.Error(GB_ERR_ARG); + return; + } + THIS->mc_ttl = ttl; + update_multicast_ttl(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocketMulticast_Interface) + + handle_address_property(_object, _param, &THIS->mc_interface, TRUE); + +END_PROPERTY + +static void handle_multicast_membership(CUDPSOCKET *_object, bool add, GB_STRING *group, GB_STRING *interface) +{ + struct ip_mreq membership; + + if (SOCKET->socket < 0) + { + GB.Error("UDP socket is not binded"); + return; + } + + if (fill_in_addr(&membership.imr_multiaddr, GB.ToZeroString(group))) + return; + + if (fill_in_addr(&membership.imr_interface, interface ? GB.ToZeroString(interface) : NULL)) + return; + + if (setsockopt(SOCKET->socket, IPPROTO_IP, add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &membership, sizeof(membership))) + GB.Error(add ? "Cannot join multicast group: &1" : "Cannot leave multicast group: &1", strerror(errno)); +} + +BEGIN_METHOD(UdpSocketMulticast_Join, GB_STRING group; GB_STRING interface) + + handle_multicast_membership(THIS, TRUE, ARG(group), MISSING(interface) ? NULL : ARG(interface)); + +END_METHOD + +BEGIN_METHOD(UdpSocketMulticast_Leave, GB_STRING group; GB_STRING interface) + + handle_multicast_membership(THIS, FALSE, ARG(group), MISSING(interface) ? NULL : ARG(interface)); + +END_METHOD + + +/*************************************************************** +Here we declare the public interface of UdpSocket class +***************************************************************/ + +GB_DESC CUdpSocketMulticastDesc[] = +{ + GB_DECLARE_VIRTUAL(".UdpSocket.Multicast"), + + GB_PROPERTY("Loop", "b", UdpSocketMulticast_Loop), + GB_PROPERTY("Ttl", "i", UdpSocketMulticast_Ttl), + GB_PROPERTY("Interface", "s", UdpSocketMulticast_Interface), + + GB_METHOD("Join", NULL, UdpSocketMulticast_Join, "(Group)s[(Interface)s]"), + GB_METHOD("Leave", NULL, UdpSocketMulticast_Leave, "(Group)s[(Interface)s]"), + + GB_END_DECLARE +}; + +GB_DESC CUdpSocketDesc[] = +{ + GB_DECLARE("UdpSocket", sizeof(CUDPSOCKET)), + + GB_INHERITS("Stream"), + + GB_EVENT("Error", NULL, NULL, &EVENT_SocketError), + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + + GB_METHOD("_new", NULL, UdpSocket_new, NULL), + GB_METHOD("_free", NULL, UdpSocket_free, NULL), + GB_METHOD("Bind", NULL, UdpSocket_Bind, NULL), + GB_METHOD("Peek", "s", UdpSocket_Peek,NULL), + + GB_PROPERTY_READ("Status", "i", UdpSocket_Status), + GB_PROPERTY_READ("SourceHost", "s", UdpSocket_SourceHost), + GB_PROPERTY_READ("SourcePort", "i", UdpSocket_SourcePort), + GB_PROPERTY_READ("SourcePath", "s", UdpSocket_SourcePath), + GB_PROPERTY("TargetHost", "s", UdpSocket_TargetHost), + GB_PROPERTY("TargetPort", "i", UdpSocket_TargetPort), + GB_PROPERTY("TargetPath", "s", UdpSocket_TargetPath), + + GB_PROPERTY("Host", "s", UdpSocket_Host), + GB_PROPERTY("Port", "i", UdpSocket_Port), + GB_PROPERTY("Path", "s", UdpSocket_Path), + + GB_PROPERTY("Broadcast", "b", UdpSocket_Broadcast), + GB_PROPERTY("Timeout", "i", Socket_Timeout), + + GB_PROPERTY_SELF("Multicast", ".UdpSocket.Multicast"), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "Port{Range:0;65535},Path,TargetHost,TargetPort,Broadcast"), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; + + diff --git a/gb.net/src/CUdpSocket.h b/gb.net/src/CUdpSocket.h new file mode 100644 index 00000000..6edb9f56 --- /dev/null +++ b/gb.net/src/CUdpSocket.h @@ -0,0 +1,76 @@ +/*************************************************************************** + + CUdpSocket.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CUDPSOCKET_H +#define __CUDPSOCKET_H + +#include "gambas.h" +#include "CNet.h" +#include "CSocket.h" + +#ifndef __CUDPSOCKET_C + +extern GB_DESC CUdpSocketDesc[]; +extern GB_DESC CUdpSocketMulticastDesc[]; +extern GB_STREAM_DESC UdpSocketStream; + +#else + +#define THIS ((CUDPSOCKET *)_object) +#define SOCKET (&THIS->common) + +#endif + +typedef + struct + { + CSOCKET_COMMON common; + NET_ADDRESS addr; + char *thost; + char *tpath; + char *buffer; + int buffer_pos; + int buffer_len; + char *path; + char *host; + char *mc_interface; + unsigned short tport; + unsigned short port; + unsigned char mc_loop; + unsigned char mc_ttl; + unsigned broadcast : 1; + } + CUDPSOCKET; + +int CUdpSocket_stream_read(GB_STREAM *stream, char *buffer, int len); +int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len); +int CUdpSocket_stream_eof(GB_STREAM *stream); +int CUdpSocket_stream_lof(GB_STREAM *stream, int64_t *len); +int CUdpSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CUdpSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CUdpSocket_stream_tell(GB_STREAM *stream, int64_t *pos); +int CUdpSocket_stream_flush(GB_STREAM *stream); +int CUdpSocket_stream_close(GB_STREAM *stream); +int CUdpSocket_stream_handle(GB_STREAM *stream); + +#endif diff --git a/gb.net/src/Makefile.am b/gb.net/src/Makefile.am new file mode 100644 index 00000000..9fd44917 --- /dev/null +++ b/gb.net/src/Makefile.am @@ -0,0 +1,25 @@ +COMPONENT = gb.net +include $(top_srcdir)/component.am + +EXTRA_DIST = doc + +gblib_LTLIBRARIES = gb.net.la + +gb_net_la_LIBADD = @NET_LIB@ +gb_net_la_LDFLAGS = -module @LD_FLAGS@ @NET_LDFLAGS@ +gb_net_la_CPPFLAGS = @NET_INC@ + +gb_net_la_SOURCES = \ + gb_network.h main.h main.c \ + tools.h tools.c \ + speed.h speed.c \ + CDnsClient.h CDnsClient.c \ + CSocket.h CSocket.c \ + CServerSocket.h CServerSocket.c \ + CUdpSocket.h CUdpSocket.c \ + CSerialPort.h CSerialPort.c \ + CNet.h CNet.c + + + + diff --git a/gb.net/src/doc/README b/gb.net/src/doc/README new file mode 100644 index 00000000..2024770b --- /dev/null +++ b/gb.net/src/doc/README @@ -0,0 +1,146 @@ +Net Advanced Component +---------------------- + +WARNING :READ THIS DOCUMENT CAREFULLY AS IT CONTAINS IMPORTANT +INFORMATION ABOUT NET AND NET-ADVENCED COMPONENTS + + +New network structure for Gambas +-------------------------------- + + Previous versions of 'net' component (included from Gambas-0.73 to +Gambas 0.80) were a simple package in which there was the following +classes: + +- Socket +- ServerSocket +- UdpSocket +- SerialPort +- DnsClient +- HttpClient + + 1) The first five classes listed, could be considered the basic stuff +or low-level stuff to manage communications, but HttpClient is in the +next floor,(levels 4-5) according to ISO/OSI model. + 2) In the future there will be new classes : FtpClient, TelnetClient... + 3) HTTP, FTP , TELNET , etc protocols, have a lot of features, and +it is hard to implement all stuff using just standard Unix C libraries, +so it would take a lot of time to implement it. + 4) The answer to that problem is libcurl ( http://curl.haxx.se/ ), which +can manage a lot of protocols, and is designed to be multi-platform. + 5) So, the new implementation of 'net' is the following: + +'Net' class: + +- Socket +- ServerSocket +- UdpSocket +- SerialPort +- DnsClient + + This class does not need libcurl to work, as it uses just Unix C libraries, +if you don't need to manage HTTP, FTP, etc, you don't need more. + +'Net Advanced' class: + +- HttpClient +... (more in the future) + + It uses libcurl to perform its work, so you need to download and install +libcurl in your computer to use it. You'll need binary libraries, and, if +you want to compile it, you'll need also devel stuff, that contains headers +and config tools to compile a program that uses libcurl. + + You'll need at least 7.10.8 version from libcurl and Gambas 0.80 (previous +versions from both programs probably won't work or even compile). + +You have to ways to install libcurl: + +* You can compile it from sources, once copiled it will provide you both +binaries and headers. +* You can download it in binary format, but remember you have to select +a version of binary package that contains all devel stuff. + +You can find both sources and binaries from: + +http://curl.haxx.se/download.html + +it is almost sure that your O.S. will be supported! + +Remember also, that if you want SSL support, libcurl uses OpenSSL, so you +need also to download and install that package: http://www.openssl.org/ is +tha main page, but probably you will find packages for you own distribution, +and even for Windows. + +COMPILING THE COMPONENTS +------------------------ + +You need gambas-0.80 source package, download and uncompress it. + +- Go to [Sources]/src/lib, delete the old 'net' folder, and place the new +'net' folder instead. +- Go to [Sources] root, delete the old 'configure.in' file, and place +the new 'configure.in' that is in current folder with this document. +- Into [Sources] root, type: + +./reconf + +- Now, type + +./configure + +(adding all parameters you need, as usual, for example:) + +./configure --prefix=/opt --disable-mysql-driver + +To compile all Gambas package type: + +make + +and then, to install + +make install + +If you just want to compile the new 'net' and 'net advanced' components, + +go to [Sources]/src/lib/net + +and type + +./make + +to compile them and + +./make install + +to install it + +Probably you will need to do also: + +gbi gb.net > /opt/lib/info/gb.net.info + +gbi gb.net.advanced > /opt/lib/info/gb.net.advanced.info + +(if you installed Gambas in other prefix different than /opt, +you have to select the right folder, for instance, for /usr/local +as prefix, type gbi gb.net > /usr/local/info/gb.net.info ...) + + + +KNOWN PROBLEMS +-------------- + + +- This is an Alpha component, and sure this is a problem! :) + +- Actuallly (7.10.8) libcurl does not perform DNS requests in asynchronous mode, +so your Gambas application will not respond to events until this part +of the connection process has finished (they say they will correct it in +future versions) + +- The rest of bugs listed in http://curl.haxx.se/docs/knownbugs.html + +- The bugs added by me to adding libcurl to Gambas! + + + \ No newline at end of file diff --git a/gb.net/src/doc/changes.txt b/gb.net/src/doc/changes.txt new file mode 100644 index 00000000..2a8649d1 --- /dev/null +++ b/gb.net/src/doc/changes.txt @@ -0,0 +1,87 @@ +Net changes +----------- + +* Now net are two components: + + - 'net' : basic stuff + - 'advanced net' : classes that needs libcurl to work (HttpClient at this moment) + +* Now there's a new class 'NetCode' in which you'll find all constants that were placed +in all the rest of classes in previous versions (status, error codes, etc) + +* 'Network advanced' has also a class to do the same as 'NetCode', called 'AdvancedCode' +which will include all constants needed for HTTP,FTP, etc + +The new HttpClient +------------------ + +* Host, Document and Port properties do not exist anymore, now, there's a +property called "URL" in which you can put all data in url format, some +examples: + +127.0.0.1 + +http://localhost/index.html + +yntp://localhost/index.html <-- the 'wrong' part "yntp" will be ignored + +http://stratoria.dynu.net:85/Actualites.html <-- port 85 instead of default 80 + +* Cookies: + + - CookiesFile : if you place here a zero string (""), HttpClient will not manage + cookies (default), but if you place here the path to a file containing cookies + in plain, netscape or mozilla format, it will use use them and it will also manage cookies + from current session in memory. If you place the path to a non existing or not readable + file, it will also manage cookies from current session in memory. By default, cookies from + current session are deleted once you free the object or call Stop() method + + - UpdateCookies : by default, cookie file (if any) is not updated with the new cookies + arrived from current session, so they're lost when you free the object or call 'Stop' + method. If you set this value to TRUE, cookies file will be updated with the new cookies + once you free the object or call to 'Stop' method + +* Stop method : previous versions of 'HttpClient' were working with 'connection : close' header, +so once a document was received from server, connection with server was broken. Now it uses +'connection : keep-alive', that means that (if server accepts it), connection will be alive to +reuse it to receive new documents from that server, with less waste of resources. Now, if you +use 'Stop' method when status is 0 (Inactive), connection with server will be closed if it +was alive, and cookies will be also stored in a file if UpdateCookies=TRUE and CookiesFile is +set to a valid path + +* 'Receive' method exists no more, as now, HttpClient inherits from '.Stream', so you can use + 'READ' standard method instead ('READ' is the only stream method that owrks, at this moment) + +* 'DataHeader' property has been replaced, now it exists as: + + Property Headers As String[] + + Each string in the returned array contains one line of that header + +* 'Get' and 'Put' methods have changed : Now they are: + + Get (Optional TargetFile As String) + + - if you do not use 'TargetFile' as parameter, you will receive the URL requested as usual: when + data is coming from server, HttpClient saves it in memory until you use 'READ' stream method, + and each time new data arrives, 'HttpClient.Read' event raises. + + - if you use 'TargetFile' as parameter, requested URL will be saved in the local file you have + specified as parameter, instead of saving it in memory, and you will not receive 'HttpClient.Read' events. + + Put(ContentType As String,Data As String, Optional TargetFile As String) + +* User and authentication : + + - User : the name and password of the user to access to HTTP server, you must set it + in user:password format, for example User="myself:mypass" + - Auth : authentication type supported by HTTP server : basic, NTLM, Digest, GSS + +* Proxy support : now HttpClient can manage authentication : + + - Proxy : URL from Proxy, for instance : 192.168.0.1:8080 + - ProxyType : can be a HTTP proxy, or a Socks-5 proxy + - ProxyAuth : authentication supported by Proxy : Basic or NTLM + + + diff --git a/gb.net/src/doc/threading.sxw b/gb.net/src/doc/threading.sxw new file mode 100644 index 0000000000000000000000000000000000000000..896748028b424178b7003cd87e0f1df3b9c48dfa GIT binary patch literal 8432 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 literal 0 HcmV?d00001 diff --git a/gb.net/src/gb.net.component b/gb.net/src/gb.net.component new file mode 100644 index 00000000..13d2d101 --- /dev/null +++ b/gb.net/src/gb.net.component @@ -0,0 +1,12 @@ +[Component] +Key=gb.net +Name=Networking component +Name[es]=Componente de red +Name[fr]=Composant d'accès au réseau +Name[pl]=Komponent sieci +Name[tr]=Ağ bileşeni +Author=Daniel Campos Fernández + +[Network] +Virtual=DnsClient,ServerSocket,SerialPort,Socket,UdpSocket + diff --git a/gb.net/src/gb_network.h b/gb.net/src/gb_network.h new file mode 100644 index 00000000..0a836fdd --- /dev/null +++ b/gb.net/src/gb_network.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gb_network.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CSocket.h" + +typedef + struct + { + struct + { + int (*ConnectLocal)(void *_object, char *sPath, int lenpath); + int (*ConnectTCP)(void *_object, char *sHost, int lenhost, int myport); + int (*Peek)(void *_object, char **buf, int MaxLen); + } Socket; + } + NETWORK_INTERFACE; + diff --git a/gb.net/src/main.c b/gb.net/src/main.c new file mode 100644 index 00000000..6785521a --- /dev/null +++ b/gb.net/src/main.c @@ -0,0 +1,68 @@ +/*************************************************************************** + + main.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include + +#include "CUdpSocket.h" +#include "CDnsClient.h" +#include "CSocket.h" +#include "CServerSocket.h" +#include "CSerialPort.h" +#include "CNet.h" +#include "gb_network.h" +#include "main.h" + + +NETWORK_INTERFACE NET EXPORT; + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CDnsClientDesc, + CSocketDesc, + CServerSocketDesc, + CUdpSocketMulticastDesc, + CUdpSocketDesc, + SerialPortDesc, + CNetDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + NET.Socket.ConnectLocal=CSocket_connect_unix; + NET.Socket.ConnectTCP=CSocket_connect_socket; + NET.Socket.Peek=CSocket_peek_data; + + return dns_init(); +} + +void EXPORT GB_EXIT() +{ + dns_exit(); +} + diff --git a/gb.net/src/main.h b/gb.net/src/main.h new file mode 100644 index 00000000..47cad6d0 --- /dev/null +++ b/gb.net/src/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.net/src/speed.c b/gb.net/src/speed.c new file mode 100644 index 00000000..bba7c6be --- /dev/null +++ b/gb.net/src/speed.c @@ -0,0 +1,66 @@ +/*************************************************************************** + + speed.c + + (c) 2019 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __SPEED_C + +#include "config.h" +#include "speed.h" + +#if OS_LINUX + +#if ARCH_PPC + #include + #include +#else + #include +#endif + +int ioctl(int fd, unsigned long request, ...); + +int SetCustomSpeed(int fd, int speed) +{ + struct termios2 io; + + if (ioctl(fd, TCGETS2, &io)) + return 1; + + io.c_cflag &= ~CBAUD; + io.c_cflag |= BOTHER; + io.c_ispeed = speed; + io.c_ospeed = speed; + + if (ioctl(fd, TCSETS2, &io)) + return 1; + + return 0; +} + +#else + +int SetCustomSpeed(int fd, int speed) +{ + return 1; +} + +#endif + diff --git a/gb.net/src/speed.h b/gb.net/src/speed.h new file mode 100644 index 00000000..7f984284 --- /dev/null +++ b/gb.net/src/speed.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + speed.h + + (c) 2019 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SPEED_H +#define __SPEED_H + +int SetCustomSpeed(int fd, int speed); + +#endif diff --git a/gb.net/src/tools.c b/gb.net/src/tools.c new file mode 100644 index 00000000..9bff3113 --- /dev/null +++ b/gb.net/src/tools.c @@ -0,0 +1,464 @@ +/*************************************************************************** + + tools.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __TOOLS_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "gambas.h" +#include "speed.h" +#include "tools.h" + +#ifdef __CYGWIN__ +#define FIONREAD TIOCINQ +#endif + +#define THIS ((CSERIALPORT *)_object) + +void correct_url(char **buf,char *protocol) +{ + char *buftmp; + int len; + int myloop,myloop2; + int pos=-1; + int myok=1; + + len=strlen(*buf); + for (myloop=0;myloop0x39) ) + { + myok=0; + break; + } + } + if (!myok) pos=myloop; + break; + } + } + } + } + + myok=0; + + if (pos==-1) + { + + GB.Alloc(POINTER(&buftmp),len+1); + strcpy(buftmp,*buf); + GB.Free(POINTER(buf)); + GB.Alloc(POINTER(buf),len+strlen(protocol)+1); + strcpy(*buf,protocol); + if (strlen(buftmp)>=2) + { + if ( buftmp[0]=='/') myok++; + if ( buftmp[1]=='/') myok++; + } + strcat(*buf,buftmp+myok); + GB.Free(POINTER(&buftmp)); + } + else + { + GB.Alloc(POINTER(&buftmp),(len-pos)+1); + strcpy(buftmp,*buf+pos+1); + GB.Free(POINTER(buf)); + GB.Alloc(POINTER(buf),strlen(buftmp)+strlen(protocol)+1); + strcpy(*buf,protocol); + if (strlen(buftmp)>=2) + { + if ( buftmp[0]=='/') myok++; + if ( buftmp[1]=='/') myok++; + } + strcat(*buf,buftmp+myok); + GB.Free(POINTER(&buftmp)); + } +} + +void Alloc_CallBack_Pointers(long nobjs,long **objs,long **scks) +{ + if (!nobjs) + { + if (*objs) + { + GB.Free((void**)objs); + GB.Free((void**)scks); + *objs=NULL; + } + return; + } + if (*objs) + { + GB.Realloc((void**)objs,nobjs*sizeof(long)); + GB.Realloc((void**)scks,nobjs*sizeof(long)); + } + else + { + GB.Alloc((void**)objs,sizeof(long)); + GB.Alloc((void**)scks,sizeof(long)); + } +} + +int CheckConnection(int Socket) +{ + struct pollfd mypoll; + int numpoll; + int retval=6; + + mypoll.fd=Socket; + mypoll.events=POLLERR; + mypoll.revents=0; + numpoll=poll(&mypoll,1,0); + if (numpoll>=0) + { + if (!numpoll) + { + mypoll.fd=Socket; + mypoll.events=POLLIN | POLLOUT; + mypoll.revents=0; + numpoll=poll(&mypoll,1,0); + if (numpoll<0) + { + retval=0; + } + else + { + if (numpoll>0) retval=7; + } + } + else + { + retval=0; + } + + } + else + { + retval=0; + + } + return retval; +} + +/* free "buf" after using it! */ +int IsHostPath(char *sCad, int lenCad, char **buf,int *port) +{ + /******************* + 0 --> Error + 1 --> TCP + 2 -> Unix + *******************/ + int npos=0; + int npoint=0; + int myloop; + int bufport=0; + *port=0; + *buf=NULL; + if ( sCad[0] == '/' ) + { + return 2; + } + for (myloop=0;myloop'9') ) return 0; + bufport*=10; + bufport+= (sCad[myloop]-48); + if ( (bufport) >65535) return 0; + } + *port=bufport; + if (npos>0) + { + GB.Alloc((void**)buf,npos); + *buf[0]=0; + sCad[npos]=0; + strcpy(*buf,sCad); + sCad[npos]=':'; + } + return 1; + +} +/******************************************************** + Watching stuff + ********************************************************/ +int search_by_integer(long *objlist,long nobj,long iData) +{ + int myloop; + int position=0; + for (myloop=0;myloopflow) + { + case 1: hard_flow = CRTSCTS; break; + case 2: soft_flow = IXON | IXOFF | IXANY; break; + case 3: hard_flow = CRTSCTS; soft_flow = IXON | IXOFF | IXANY; break; + } + + speed = ConvertBaudRate(THIS->speed); + parity = ConvertParity(THIS->parity); + data_bits = ConvertDataBits(THIS->dataBits); + stop_bits = ConvertStopBits(THIS->stopBits); + + if (parity < 0 || data_bits < 0 || stop_bits < 0) + { + GB.Error(GB_ERR_ARG); + return TRUE; + } + + fd = open(THIS->portName, O_RDWR | O_NOCTTY | O_NDELAY ); + if (fd < 0) + { + GB.Error("Unable to open port: &1", strerror(errno)); + return TRUE; + } + + if (tcgetattr(fd, &newtio)) + { + close(fd); + GB.Error("Unable to get configuration: &1", strerror(errno)); + return TRUE; + } + + THIS->oldtio = newtio; + + // cleaning default options + newtio.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS); + newtio.c_iflag &= ~(INPCK | ISTRIP | IGNPAR | IXON | IXOFF | IXANY | ICRNL | INLCR); + newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + // setting options + newtio.c_cflag= data_bits | stop_bits | parity | CLOCAL | CREAD | HUPCL | hard_flow; + + if (parity & PARENB) + newtio.c_iflag |= INPCK; // Why stripping the eight bit when parity is enabled?? | ISTRIP; + else + newtio.c_iflag |= IGNPAR; + + newtio.c_iflag |= soft_flow; + + newtio.c_oflag = 0; + + newtio.c_cc[VMIN] = 1; + newtio.c_cc[VTIME] = 1; + newtio.c_cc[VSTART] = 17; //DC1; + newtio.c_cc[VSTOP] = 19; //DC3; + + if (speed >= 0) + { + cfsetispeed(&newtio, speed); + cfsetospeed(&newtio, speed); + } + + tcflush(fd, TCIFLUSH); + if (tcsetattr(fd, TCSANOW, &newtio)) + { + close(fd); + GB.Error("Unable to set configuration: &1", strerror(errno)); + return TRUE; + } + + if (speed < 0) + { + if (SetCustomSpeed(fd, THIS->speed)) + { + close(fd); + GB.Error("Unable to set custom speed: &1", strerror(errno)); + return TRUE; + } + } + + /*if (tcgetattr(fd, &io)) + { + close(fd); + GB.Error("Unable to check configuration: &1", strerror(errno)); + }*/ + + THIS->port = fd; + + return FALSE; +} + + diff --git a/gb.net/src/tools.h b/gb.net/src/tools.h new file mode 100644 index 00000000..54571a04 --- /dev/null +++ b/gb.net/src/tools.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + tools.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __TOOLS_H +#define __TOOLS_H + +#include "CSerialPort.h" + +#ifdef __sun__ + + #include + #include + +#endif + +#ifdef MSG_PEEK +#ifdef MSG_NOSIGNAL + + #define USE_MSG_NOSIGNAL(_code) _code + +#else + + #include + #define MSG_NOSIGNAL 0 + #define USE_MSG_NOSIGNAL(_code) \ + { \ + void (*_oldsigpipe)(int) = signal(SIGPIPE, SIG_IGN); \ + _code ; \ + signal(SIGPIPE, _oldsigpipe); \ + } + +#endif +#endif + +void Alloc_CallBack_Pointers(long nobjs,long **objs,long **scks); + +int search_by_integer(long *objlist,long nobj,long iData); + +int CheckConnection(int Socket); +int IsHostPath(char *sCad, int lenCad, char **buf,int *port); + +void correct_url(char **buf,char *protocol); +int ConvertBaudRate(int nBauds); +int ConvertStopBits(int nStop); +int ConvertDataBits(int nBits); +int ConvertParity(int parity); +void CloseSerialPort(int fd,struct termios *oldtio); +bool OpenSerialPort(CSERIALPORT *_object); + +#endif diff --git a/gb.openal/AUTHORS b/gb.openal/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/COPYING b/gb.openal/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.openal/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.openal/ChangeLog b/gb.openal/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/INSTALL b/gb.openal/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.openal/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.openal/Makefile.am b/gb.openal/Makefile.am new file mode 100644 index 00000000..e0ebd8ce --- /dev/null +++ b/gb.openal/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @OPENAL_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.openal/NEWS b/gb.openal/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/README b/gb.openal/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/acinclude.m4 b/gb.openal/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.openal/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.openal/component.am b/gb.openal/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.openal/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.openal/configure.ac b/gb.openal/configure.ac new file mode 100644 index 00000000..98670e59 --- /dev/null +++ b/gb.openal/configure.ac @@ -0,0 +1,18 @@ +dnl ---- configure.ac for gb.openal + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-openal],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.openal) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + openal, + OPENAL, + gb.openal, + [src], + ['openal >= 1.13' alure]) +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT +GB_PRINT_MESSAGES diff --git a/gb.openal/gambas.h b/gb.openal/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.openal/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.openal/gb_common.h b/gb.openal/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.openal/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.openal/m4 b/gb.openal/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.openal/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.openal/reconf b/gb.openal/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.openal/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.openal/src/Makefile.am b/gb.openal/src/Makefile.am new file mode 100644 index 00000000..506c2568 --- /dev/null +++ b/gb.openal/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.openal +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.openal.la + +gb_openal_la_LIBADD = @OPENAL_LIB@ +gb_openal_la_LDFLAGS = -module @LD_FLAGS@ @OPENAL_LDFLAGS@ +gb_openal_la_CPPFLAGS = @OPENAL_INC@ + +gb_openal_la_SOURCES = main.c main.h c_al.h c_al.c c_alc.h c_alc.c c_alure.h c_alure.c diff --git a/gb.openal/src/c_al.c b/gb.openal/src/c_al.c new file mode 100644 index 00000000..ee001ce4 --- /dev/null +++ b/gb.openal/src/c_al.c @@ -0,0 +1,673 @@ +/*************************************************************************** + + c_al.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_AL_C + +#include "c_al.h" + +/* + * LISTENER + * Listener represents the location and orientation of the + * 'user' in 3D-space. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Orientation AL_ORIENTATION ALfloat[6] (Forward then Up vectors) +*/ + +static int get_listener_float_param_size(int param) +{ + switch(param) + { + case AL_GAIN: return 1; + case AL_POSITION: return 3; + case AL_VELOCITY: return 3; + case AL_ORIENTATION: return 6; + default: return 0; + } +} + +static int get_listener_integer_param_size(int param) +{ + return 0; +} + +/* + * SOURCE + * Sources represent individual sound objects in 3D-space. + * Sources take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial arrangement etc. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Min Gain AL_MIN_GAIN ALfloat + * Max Gain AL_MAX_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Direction AL_DIRECTION ALfloat[3] + * Head Relative Mode AL_SOURCE_RELATIVE ALint (AL_TRUE or AL_FALSE) + * Reference Distance AL_REFERENCE_DISTANCE ALfloat + * Max Distance AL_MAX_DISTANCE ALfloat + * RollOff Factor AL_ROLLOFF_FACTOR ALfloat + * Inner Angle AL_CONE_INNER_ANGLE ALint or ALfloat + * Outer Angle AL_CONE_OUTER_ANGLE ALint or ALfloat + * Cone Outer Gain AL_CONE_OUTER_GAIN ALint or ALfloat + * Pitch AL_PITCH ALfloat + * Looping AL_LOOPING ALint (AL_TRUE or AL_FALSE) + * MS Offset AL_MSEC_OFFSET ALint or ALfloat + * Byte Offset AL_BYTE_OFFSET ALint or ALfloat + * Sample Offset AL_SAMPLE_OFFSET ALint or ALfloat + * Attached Buffer AL_BUFFER ALint + * State (Query only) AL_SOURCE_STATE ALint + * Buffers Queued (Query only) AL_BUFFERS_QUEUED ALint + * Buffers Processed (Query only) AL_BUFFERS_PROCESSED ALint + */ + +static int get_source_float_param_size(int param) +{ + switch(param) + { + case AL_GAIN: case AL_MIN_GAIN: case AL_MAX_GAIN: case AL_REFERENCE_DISTANCE: case AL_MAX_DISTANCE: case AL_ROLLOFF_FACTOR: + case AL_CONE_INNER_ANGLE: case AL_CONE_OUTER_ANGLE: case AL_CONE_OUTER_GAIN: case AL_PITCH: case AL_BYTE_OFFSET: + case AL_SAMPLE_OFFSET: + //case AL_MSEC_OFFSET: + return 1; + + case AL_POSITION: case AL_VELOCITY: case AL_DIRECTION: + return 3; + + default: + return 0; + } +} + +static int get_source_integer_param_size(int param) +{ + switch(param) + { + case AL_SOURCE_RELATIVE: case AL_CONE_INNER_ANGLE: case AL_CONE_OUTER_ANGLE: case AL_CONE_OUTER_GAIN: case AL_LOOPING: + //case AL_MSEC_OFFSET: + case AL_BYTE_OFFSET: case AL_SAMPLE_OFFSET: case AL_BUFFER: case AL_SOURCE_STATE: case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: + return 1; + + default: + return 0; + } +} + +/* + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. One Buffer can be used + * by multiple Sources. + * + * Properties include: - + * + * Frequency (Query only) AL_FREQUENCY ALint + * Size (Query only) AL_SIZE ALint + * Bits (Query only) AL_BITS ALint + * Channels (Query only) AL_CHANNELS ALint + */ + +static int get_buffer_float_param_size(int param) +{ + return 0; +} + +static int get_buffer_integer_param_size(int param) +{ + switch(param) + { + case AL_FREQUENCY: case AL_SIZE: case AL_BITS: case AL_CHANNELS: + return 1; + + default: + return 0; + } +} + + +//--------------------------------------------------------------------------- + + +BEGIN_METHOD_VOID(AL_GetError) + + GB.ReturnInteger(alGetError()); + +END_METHOD + +BEGIN_METHOD(AL_Enable, GB_INTEGER cap) + + alEnable(VARG(cap)); + +END_METHOD + +BEGIN_METHOD(AL_Disable, GB_INTEGER cap) + + alDisable(VARG(cap)); + +END_METHOD + +BEGIN_METHOD(AL_IsEnabled, GB_INTEGER cap) + + GB.ReturnBoolean(alIsEnabled(VARG(cap))); + +END_METHOD + +BEGIN_METHOD(AL_GenBuffers, GB_INTEGER count) + + int count = VARG(count); + GB_ARRAY array; + + if (count <= 0) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_INTEGER, count); + alGenBuffers(VARG(count), (ALuint *)GB.Array.Get(array, 0)); + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(AL_GenSources, GB_INTEGER count) + + int count = VARG(count); + GB_ARRAY array; + + if (count <= 0) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_INTEGER, count); + alGenSources(VARG(count), (ALuint *)GB.Array.Get(array, 0)); + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(AL_DeleteBuffers, GB_OBJECT array) + + GB_ARRAY array = (GB_ARRAY)VARG(array); + int count; + + if (GB.CheckObject(array)) + return; + + count = GB.Array.Count(array); + if (count <= 0) + return; + + alDeleteBuffers(count, GB.Array.Get(array, 0)); + +END_METHOD + +BEGIN_METHOD(AL_DeleteSources, GB_OBJECT array) + + GB_ARRAY array = (GB_ARRAY)VARG(array); + int count; + + if (GB.CheckObject(array)) + return; + + count = GB.Array.Count(array); + if (count <= 0) + return; + + alDeleteSources(count, GB.Array.Get(array, 0)); + +END_METHOD + +BEGIN_METHOD(AL_IsBuffer, GB_INTEGER name) + + GB.ReturnBoolean(alIsBuffer(VARG(name))); + +END_METHOD + +BEGIN_METHOD(AL_IsSource, GB_INTEGER name) + + GB.ReturnBoolean(alIsSource(VARG(name))); + +END_METHOD + +BEGIN_METHOD(AL_GetBoolean, GB_INTEGER param) + + GB.ReturnBoolean(alGetBoolean(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetInteger, GB_INTEGER param) + + GB.ReturnInteger(alGetInteger(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetFloat, GB_INTEGER param) + + GB.ReturnSingle(alGetFloat(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetDouble, GB_INTEGER param) + + GB.ReturnFloat(alGetDouble(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetString, GB_INTEGER param) + + GB.ReturnNewZeroString(alGetString(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_IsExtensionPresent, GB_STRING name) + + GB.ReturnBoolean(alIsExtensionPresent(GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(AL_GetEnumValue, GB_STRING name) + + GB.ReturnInteger(alGetEnumValue(GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(AL_DistanceModel, GB_INTEGER model) + + alDistanceModel(VARG(model)); + +END_METHOD + +BEGIN_METHOD(AL_DopplerFactor, GB_FLOAT factor) + + alDopplerFactor((float)VARG(factor)); + +END_METHOD + +BEGIN_METHOD(AL_DopplerVelocity, GB_FLOAT velocity) + + alDopplerVelocity((float)VARG(velocity)); + +END_METHOD + +BEGIN_METHOD(AL_SpeedOfSound, GB_FLOAT speed) + + alSpeedOfSound((float)VARG(speed)); + +END_METHOD + +#define IMPLEMENT_X(_name, _type) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param; _type value) \ + al##_name(ID_ARG VARG(param), VARG(value)); \ +END_METHOD + +#define IMPLEMENT_3X(_name, _type) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param; _type value1; _type value2; _type value3) \ + al##_name(ID_ARG VARG(param), VARG(value1), VARG(value2), VARG(value3)); \ +END_METHOD + +#define IMPLEMENT_XV(_name, _type) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param; GB_OBJECT array) \ + GB_ARRAY array = VARG(array); \ + if (GB.CheckObject(array)) \ + return; \ + al##_name(ID_ARG VARG(param), GB.Array.Get(array, 0)); \ +END_METHOD + +#define IMPLEMENT_GET_X(_name, _return, _ctype) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param) \ + _ctype val; \ + al##_name(ID_ARG VARG(param), &val); \ + GB.Return##_return(val); \ +END_METHOD + +#define IMPLEMENT_GET_XV(_name, _type, _get_size) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param) \ + GB_ARRAY array; \ + int size = _get_size(VARG(param)); \ + if (size == 0) \ + GB.ReturnNull(); \ + else \ + { \ + GB.Array.New(&array, _type, size); \ + al##_name(ID_ARG VARG(param), GB.Array.Get(array, 0)); \ + GB.ReturnObject(array); \ + } \ +END_METHOD + +#define ID_PARAM +#define ID_ARG + +IMPLEMENT_X(Listenerf, GB_SINGLE) +IMPLEMENT_3X(Listener3f, GB_SINGLE) +IMPLEMENT_XV(Listenerfv, GB_SINGLE) +IMPLEMENT_X(Listeneri, GB_INTEGER) +IMPLEMENT_3X(Listener3i, GB_INTEGER) +IMPLEMENT_XV(Listeneriv, GB_INTEGER) + +IMPLEMENT_GET_X(GetListenerf, Single, ALfloat) +IMPLEMENT_GET_XV(GetListenerfv, GB_T_SINGLE, get_listener_float_param_size) +IMPLEMENT_GET_X(GetListeneri, Integer, ALint) +IMPLEMENT_GET_XV(GetListeneriv, GB_T_INTEGER, get_listener_integer_param_size) + +#undef ID_PARAM +#define ID_PARAM GB_INTEGER id; +#undef ID_ARG +#define ID_ARG VARG(id), + +IMPLEMENT_X(Sourcef, GB_SINGLE) +IMPLEMENT_3X(Source3f, GB_SINGLE) +IMPLEMENT_XV(Sourcefv, GB_SINGLE) +IMPLEMENT_X(Sourcei, GB_INTEGER) +IMPLEMENT_3X(Source3i, GB_INTEGER) +IMPLEMENT_XV(Sourceiv, GB_INTEGER) + +IMPLEMENT_GET_X(GetSourcef, Single, ALfloat) +IMPLEMENT_GET_XV(GetSourcefv, GB_T_SINGLE, get_source_float_param_size) +IMPLEMENT_GET_X(GetSourcei, Integer, ALint) +IMPLEMENT_GET_XV(GetSourceiv, GB_T_INTEGER, get_source_integer_param_size) + +#define IMPLEMENT_ACTION(_name) \ +BEGIN_METHOD(AL_##_name, GB_INTEGER id) \ + al##_name(VARG(id)); \ +END_METHOD + +#define IMPLEMENT_ACTION_V(_name) \ +BEGIN_METHOD(AL_##_name, GB_OBJECT ids) \ + GB_ARRAY array = VARG(ids); \ + if (GB.CheckObject(array)) \ + return; \ + al##_name(GB.Array.Count(array), GB.Array.Get(array, 0)); \ +END_METHOD + +IMPLEMENT_ACTION(SourcePlay) +IMPLEMENT_ACTION(SourceStop) +IMPLEMENT_ACTION(SourceRewind) +IMPLEMENT_ACTION(SourcePause) + +IMPLEMENT_ACTION_V(SourcePlayv) +IMPLEMENT_ACTION_V(SourceStopv) +IMPLEMENT_ACTION_V(SourceRewindv) +IMPLEMENT_ACTION_V(SourcePausev) + +#define IMPLEMENT_QUEUE_BUFFER(_name) \ +BEGIN_METHOD(AL_##_name, GB_INTEGER source; GB_OBJECT buffers) \ + GB_ARRAY buffers = VARG(buffers); \ + if (GB.CheckObject(buffers)) \ + return; \ + al##_name(VARG(source), GB.Array.Count(buffers), GB.Array.Get(buffers, 0)); \ +END_METHOD + +IMPLEMENT_QUEUE_BUFFER(SourceQueueBuffers) +IMPLEMENT_QUEUE_BUFFER(SourceUnqueueBuffers) + +BEGIN_METHOD(AL_BufferData, GB_INTEGER buffer; GB_INTEGER format; GB_VARIANT data; GB_INTEGER size; GB_INTEGER freq) + + void *data; + int size = VARGOPT(size, -1); + int max_size = -1; + + if (VARG(data).type == GB_T_STRING) + { + data = VARG(data).value._string; + max_size = GB.StringLength(data); + } + else if (VARG(data).type == GB_T_POINTER) + { + data = (void *)VARG(data).value._pointer; + max_size = size; + if (max_size < 0) + max_size = 0; + } + else if (VARG(data).type == GB_T_OBJECT) + { + void *object = VARG(data).value._object; + + if (GB.Is(object, GB.FindClass("Byte[]")) + || GB.Is(object, GB.FindClass("Short[]")) + || GB.Is(object, GB.FindClass("Integer[]"))) + { + int count = GB.Array.Count((GB_ARRAY)object); + + if (count == 0) + { + data = NULL; + max_size = 0; + } + else + { + data = GB.Array.Get((GB_ARRAY)object, 0); + max_size = count * (GB.Array.Get((GB_ARRAY)object, 1) - data); + } + } + } + + if (max_size < 0) + { + GB.Error("Unsupported data type. String, Pointer, Byte[], Short[] or Integer[] expected"); + return; + } + + if (size < 0) + size = max_size; + else if (size > max_size) + size = max_size; + + if (size <= 0) + return; + + alBufferData(VARG(buffer), VARG(format), data, size, VARGOPT(freq, 44100)); + +END_METHOD + +#undef ID_PARAM +#define ID_PARAM GB_INTEGER id; +#undef ID_ARG +#define ID_ARG VARG(id), + +IMPLEMENT_X(Bufferf, GB_SINGLE) +IMPLEMENT_3X(Buffer3f, GB_SINGLE) +IMPLEMENT_XV(Bufferfv, GB_SINGLE) +IMPLEMENT_X(Bufferi, GB_INTEGER) +IMPLEMENT_3X(Buffer3i, GB_INTEGER) +IMPLEMENT_XV(Bufferiv, GB_INTEGER) + +IMPLEMENT_GET_X(GetBufferf, Single, ALfloat) +IMPLEMENT_GET_XV(GetBufferfv, GB_T_SINGLE, get_buffer_float_param_size) +IMPLEMENT_GET_X(GetBufferi, Integer, ALint) +IMPLEMENT_GET_XV(GetBufferiv, GB_T_INTEGER, get_buffer_integer_param_size) + + +//--------------------------------------------------------------------------- + +GB_DESC AlDesc[] = +{ + GB_DECLARE_VIRTUAL("Al"), + + GB_STATIC_METHOD("GetError", "i", AL_GetError, NULL), + + GB_STATIC_METHOD("Enable", NULL, AL_Enable, "(Capability)i"), + GB_STATIC_METHOD("Disable", NULL, AL_Disable, "(Capability)i"), + GB_STATIC_METHOD("IsEnabled", "b", AL_IsEnabled, "(Capability)i"), + + GB_STATIC_METHOD("GetBoolean", "b", AL_GetBoolean, "(Param)i"), + GB_STATIC_METHOD("GetInteger", "i", AL_GetInteger, "(Param)i"), + GB_STATIC_METHOD("GetFloat", "g", AL_GetFloat, "(Param)i"), + GB_STATIC_METHOD("GetDouble", "f", AL_GetDouble, "(Param)i"), + + //GB_STATIC_METHOD("GetBooleanv", "Boolean[]", AL_GetBooleanv, "(Param)i"), + //GB_STATIC_METHOD("GetIntegerv", "Integer[]", AL_GetIntegerv, "(Param)i"), + //GB_STATIC_METHOD("GetFloatv", "Single[]", AL_GetFloatv, "(Param)i"), + //GB_STATIC_METHOD("GetDoublev", "Float[]", AL_GetDoublev, "(Param)i"), + + GB_STATIC_METHOD("GetString", "s", AL_GetString, "(Param)i"), + + GB_STATIC_METHOD("IsExtensionPresent", "b", AL_IsExtensionPresent, "(Name)s"), + + GB_STATIC_METHOD("GetEnumValue", "i", AL_GetEnumValue, "(Name)s"), + + GB_STATIC_METHOD("Listenerf", NULL, AL_Listenerf, "(Param)i(Value)g"), + GB_STATIC_METHOD("Listener3f", NULL, AL_Listener3f, "(Param)i(Value1)g(Value2)g(Value3)g"), + GB_STATIC_METHOD("Listenerfv", NULL, AL_Listenerfv, "(Param)i(Values)Single[];"), + GB_STATIC_METHOD("Listeneri", NULL, AL_Listeneri, "(Param)i(Value)i"), + GB_STATIC_METHOD("Listener3i", NULL, AL_Listener3i, "(Param)i(Value1)i(Value2)i(Value3)i"), + GB_STATIC_METHOD("Listeneriv", NULL, AL_Listeneriv, "(Param)i(Values)Integer[];"), + + GB_STATIC_METHOD("GetListenerf", "g", AL_GetListenerf, "(Param)i"), + GB_STATIC_METHOD("GetListener3f", "Single[]", AL_GetListenerfv, "(Param)i"), + GB_STATIC_METHOD("GetListenerfv", "Single[]", AL_GetListenerfv, "(Param)i"), + GB_STATIC_METHOD("GetListeneri", "i", AL_GetListenerf, "(Param)i"), + GB_STATIC_METHOD("GetListener3i", "Integer[]", AL_GetListenerfv, "(Param)i"), + GB_STATIC_METHOD("GetListeneriv", "Integer[]", AL_GetListenerfv, "(Param)i"), + + GB_STATIC_METHOD("GenSources", "Integer[]", AL_GenSources, "(Count)i"), + GB_STATIC_METHOD("DeleteSources", NULL, AL_DeleteSources, "(Sources)Integer[];"), + GB_STATIC_METHOD("IsSource", "b", AL_IsSource, "(Source)i"), + + GB_STATIC_METHOD("Sourcef", NULL, AL_Sourcef, "(Source)i(Param)i(Value)g"), + GB_STATIC_METHOD("Source3f", NULL, AL_Source3f, "(Source)i(Param)i(Value1)g(Value2)g(Value3)g"), + GB_STATIC_METHOD("Sourcefv", NULL, AL_Sourcefv, "(Source)i(Param)i(Values)Single[];"), + GB_STATIC_METHOD("Sourcei", NULL, AL_Sourcei, "(Source)i(Param)i(Value)i"), + GB_STATIC_METHOD("Source3i", NULL, AL_Source3i, "(Source)i(Param)i(Value1)i(Value2)i(Value3)i"), + GB_STATIC_METHOD("Sourceiv", NULL, AL_Sourceiv, "(Source)i(Param)i(Values)Integer[];"), + + GB_STATIC_METHOD("GetSourcef", "g", AL_GetSourcef, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSource3f", "Single[]", AL_GetSourcefv, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSourcefv", "Single[]", AL_GetSourcefv, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSourcei", "i", AL_GetSourcef, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSource3i", "Integer[]", AL_GetSourcefv, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSourceiv", "Integer[]", AL_GetSourcefv, "(Source)i(Param)i"), + + GB_STATIC_METHOD("SourcePlay", NULL, AL_SourcePlay, "(Source)i"), + GB_STATIC_METHOD("SourceStop", NULL, AL_SourceStop, "(Source)i"), + GB_STATIC_METHOD("SourceRewind", NULL, AL_SourceRewind, "(Source)i"), + GB_STATIC_METHOD("SourcePause", NULL, AL_SourcePause, "(Source)i"), + + GB_STATIC_METHOD("SourcePlayv", NULL, AL_SourcePlayv, "(Sources)Integer[];"), + GB_STATIC_METHOD("SourceStopv", NULL, AL_SourceStopv, "(Sources)Integer[];"), + GB_STATIC_METHOD("SourceRewindv", NULL, AL_SourceRewindv, "(Sources)Integer[];"), + GB_STATIC_METHOD("SourcePausev", NULL, AL_SourcePausev, "(Sources)Integer[];"), + + GB_STATIC_METHOD("SourceQueueBuffers", NULL, AL_SourceQueueBuffers, "(Source)i(Buffers)Integer[];"), + GB_STATIC_METHOD("SourceUnqueueBuffers", NULL, AL_SourceUnqueueBuffers, "(Source)i(Buffers)Integer[];"), + + GB_STATIC_METHOD("GenBuffers", "Integer[]", AL_GenBuffers, "(Count)i"), + GB_STATIC_METHOD("DeleteBuffers", NULL, AL_DeleteBuffers, "(Buffers)Integer[];"), + GB_STATIC_METHOD("IsBuffer", "b", AL_IsBuffer, "(Buffer)i"), + + GB_STATIC_METHOD("BufferData", NULL, AL_BufferData, "(Buffer)i(Format)i(Data)v[(Size)i(Frequency)i]"), + + GB_STATIC_METHOD("Bufferf", NULL, AL_Bufferf, "(Buffer)i(Param)i(Value)g"), + GB_STATIC_METHOD("Buffer3f", NULL, AL_Buffer3f, "(Buffer)i(Param)i(Value1)g(Value2)g(Value3)g"), + GB_STATIC_METHOD("Bufferfv", NULL, AL_Bufferfv, "(Buffer)i(Param)i(Values)Single[];"), + GB_STATIC_METHOD("Bufferi", NULL, AL_Bufferi, "(Buffer)i(Param)i(Value)i"), + GB_STATIC_METHOD("Buffer3i", NULL, AL_Buffer3i, "(Buffer)i(Param)i(Value1)i(Value2)i(Value3)i"), + GB_STATIC_METHOD("Bufferiv", NULL, AL_Bufferiv, "(Buffer)i(Param)i(Values)Integer[];"), + + GB_STATIC_METHOD("GetBufferf", "g", AL_GetBufferf, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBuffer3f", "Single[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBufferfv", "Single[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBufferi", "i", AL_GetBufferf, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBuffer3i", "Integer[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBufferiv", "Integer[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + + GB_STATIC_METHOD("DopplerFactor", NULL, AL_DopplerFactor, "(DopplerFactor)f"), + GB_STATIC_METHOD("DopplerVelocity", NULL, AL_DopplerVelocity, "(DopplerVelocity)f"), + GB_STATIC_METHOD("SpeedOfSound", NULL, AL_SpeedOfSound, "(SpeedOfSound)f"), + GB_STATIC_METHOD("DistanceModel", NULL, AL_DistanceModel, "(DistanceModel)i"), + + GB_CONSTANT("NONE", "i", AL_NONE), + GB_CONSTANT("FALSE", "i", AL_FALSE), + GB_CONSTANT("TRUE", "i", AL_TRUE), + GB_CONSTANT("SOURCE_RELATIVE", "i", AL_SOURCE_RELATIVE), + GB_CONSTANT("CONE_INNER_ANGLE", "i", AL_CONE_INNER_ANGLE), + GB_CONSTANT("CONE_OUTER_ANGLE", "i", AL_CONE_OUTER_ANGLE), + GB_CONSTANT("PITCH", "i", AL_PITCH), + GB_CONSTANT("POSITION", "i", AL_POSITION), + GB_CONSTANT("DIRECTION", "i", AL_DIRECTION), + GB_CONSTANT("VELOCITY", "i", AL_VELOCITY), + GB_CONSTANT("LOOPING", "i", AL_LOOPING), + GB_CONSTANT("BUFFER", "i", AL_BUFFER), + GB_CONSTANT("GAIN", "i", AL_GAIN), + GB_CONSTANT("MIN_GAIN", "i", AL_MIN_GAIN), + GB_CONSTANT("MAX_GAIN", "i", AL_MAX_GAIN), + GB_CONSTANT("ORIENTATION", "i", AL_ORIENTATION), + GB_CONSTANT("SOURCE_STATE", "i", AL_SOURCE_STATE), + GB_CONSTANT("INITIAL", "i", AL_INITIAL), + GB_CONSTANT("PLAYING", "i", AL_PLAYING), + GB_CONSTANT("PAUSED", "i", AL_PAUSED), + GB_CONSTANT("STOPPED", "i", AL_STOPPED), + GB_CONSTANT("BUFFERS_QUEUED", "i", AL_BUFFERS_QUEUED), + GB_CONSTANT("BUFFERS_PROCESSED", "i", AL_BUFFERS_PROCESSED), + GB_CONSTANT("SEC_OFFSET", "i", AL_SEC_OFFSET), + GB_CONSTANT("SAMPLE_OFFSET", "i", AL_SAMPLE_OFFSET), + GB_CONSTANT("BYTE_OFFSET", "i", AL_BYTE_OFFSET), + GB_CONSTANT("SOURCE_TYPE", "i", AL_SOURCE_TYPE), + GB_CONSTANT("STATIC", "i", AL_STATIC), + GB_CONSTANT("STREAMING", "i", AL_STREAMING), + GB_CONSTANT("UNDETERMINED", "i", AL_UNDETERMINED), + GB_CONSTANT("FORMAT_MONO8", "i", AL_FORMAT_MONO8), + GB_CONSTANT("FORMAT_MONO16", "i", AL_FORMAT_MONO16), + GB_CONSTANT("FORMAT_STEREO8", "i", AL_FORMAT_STEREO8), + GB_CONSTANT("FORMAT_STEREO16", "i", AL_FORMAT_STEREO16), + GB_CONSTANT("REFERENCE_DISTANCE", "i", AL_REFERENCE_DISTANCE), + GB_CONSTANT("ROLLOFF_FACTOR", "i", AL_ROLLOFF_FACTOR), + GB_CONSTANT("CONE_OUTER_GAIN", "i", AL_CONE_OUTER_GAIN), + GB_CONSTANT("MAX_DISTANCE", "i", AL_MAX_DISTANCE), + GB_CONSTANT("FREQUENCY", "i", AL_FREQUENCY), + GB_CONSTANT("BITS", "i", AL_BITS), + GB_CONSTANT("CHANNELS", "i", AL_CHANNELS), + GB_CONSTANT("SIZE", "i", AL_SIZE), + GB_CONSTANT("UNUSED", "i", AL_UNUSED), + GB_CONSTANT("PENDING", "i", AL_PENDING), + GB_CONSTANT("PROCESSED", "i", AL_PROCESSED), + GB_CONSTANT("NO_ERROR", "i", AL_NO_ERROR), + GB_CONSTANT("INVALID_NAME", "i", AL_INVALID_NAME), + GB_CONSTANT("INVALID_ENUM", "i", AL_INVALID_ENUM), + GB_CONSTANT("INVALID_VALUE", "i", AL_INVALID_VALUE), + GB_CONSTANT("INVALID_OPERATION", "i", AL_INVALID_OPERATION), + GB_CONSTANT("OUT_OF_MEMORY", "i", AL_OUT_OF_MEMORY), + GB_CONSTANT("VENDOR", "i", AL_VENDOR), + GB_CONSTANT("VERSION", "i", AL_VERSION), + GB_CONSTANT("RENDERER", "i", AL_RENDERER), + GB_CONSTANT("EXTENSIONS", "i", AL_EXTENSIONS), + GB_CONSTANT("DOPPLER_FACTOR", "i", AL_DOPPLER_FACTOR), + GB_CONSTANT("DOPPLER_VELOCITY", "i", AL_DOPPLER_VELOCITY), + GB_CONSTANT("SPEED_OF_SOUND", "i", AL_SPEED_OF_SOUND), + GB_CONSTANT("DISTANCE_MODEL", "i", AL_DISTANCE_MODEL), + GB_CONSTANT("INVERSE_DISTANCE", "i", AL_INVERSE_DISTANCE), + GB_CONSTANT("INVERSE_DISTANCE_CLAMPED", "i", AL_INVERSE_DISTANCE_CLAMPED), + GB_CONSTANT("LINEAR_DISTANCE", "i", AL_LINEAR_DISTANCE), + GB_CONSTANT("LINEAR_DISTANCE_CLAMPED", "i", AL_LINEAR_DISTANCE_CLAMPED), + GB_CONSTANT("EXPONENT_DISTANCE", "i", AL_EXPONENT_DISTANCE), + GB_CONSTANT("EXPONENT_DISTANCE_CLAMPED", "i", AL_EXPONENT_DISTANCE_CLAMPED), + + GB_END_DECLARE +}; diff --git a/gb.openal/src/c_al.h b/gb.openal/src/c_al.h new file mode 100644 index 00000000..02bd24ac --- /dev/null +++ b/gb.openal/src/c_al.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + c_al.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_AL_H +#define __C_AL_H + +#include "main.h" + +#ifndef __C_AL_C +extern GB_DESC AlDesc[]; +#endif + +#endif diff --git a/gb.openal/src/c_alc.c b/gb.openal/src/c_alc.c new file mode 100644 index 00000000..1ca31196 --- /dev/null +++ b/gb.openal/src/c_alc.c @@ -0,0 +1,509 @@ +/*************************************************************************** + + c_alc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_ALC_C + +#include "c_alc.h" + +//--------------------------------------------------------------------------- + +#define THIS ((CALCCONTEXT *)_object) + +static CALCCONTEXT *_current_context = NULL; + +static int check_context(void *_object) +{ + return THIS->context == NULL; +} + +static void init_context(CALCCONTEXT *_object, CALCDEVICE *device, GB_ARRAY array) +{ + ALCint *attrs = NULL; + int count; + + if (GB.CheckObject(device)) + return; + + if (array) + { + count = GB.Array.Count(array); + if (count) + { + attrs = alloca(sizeof(ALCint) * (count + 1)); + memcpy(attrs, GB.Array.Get(array, 0), count * sizeof(ALCint)); + attrs[count] = 0; + } + } + + THIS->context = alcCreateContext(device->device, attrs); + THIS->device = device; + GB.Ref(device); +} + +static void set_current_context(CALCCONTEXT *_object) +{ + if (_current_context == THIS) + return; + + GB.Unref(POINTER(&_current_context)); + + _current_context = THIS; + + if (THIS) + GB.Ref(THIS); +} + +static void destroy_context(CALCCONTEXT *_object) +{ + if (_current_context == THIS) + set_current_context(NULL); + + if (!THIS->context) + return; + + alcDestroyContext(THIS->context); + + THIS->context = NULL; + + GB.Unref(POINTER(&THIS->device)); +} + +BEGIN_METHOD_VOID(AlcContext_exit) + + GB.Unref(POINTER(&_current_context)); + +END_METHOD + +BEGIN_METHOD(AlcContext_new, GB_OBJECT device; GB_OBJECT attrs) + + init_context(THIS, VARG(device), VARGOPT(attrs, NULL)); + +END_METHOD + +BEGIN_METHOD_VOID(AlcContext_free) + + destroy_context(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(AlcContext_MakeCurrent) + + ALCboolean err = alcMakeContextCurrent(THIS->context); + if (err == ALC_FALSE) + set_current_context(THIS); + GB.ReturnBoolean(err); + +END_METHOD + +//--------------------------------------------------------------------------- + +#undef THIS +#define THIS ((CALCDEVICE *)_object) + +static int check_device(void *_object) +{ + return THIS->device == NULL; +} + +static CALCDEVICE *create_device(ALCdevice *device) +{ + CALCDEVICE *_object; + + if (!device) + return NULL; + + _object = GB.New(GB.FindClass("AlcDevice"), NULL, NULL); + THIS->device = device; + return THIS; +} + +static bool close_device(CALCDEVICE *_object) +{ + bool err = FALSE; + + if (THIS->device) + { + if (THIS->capture) + err = alcCaptureCloseDevice(THIS->device); + else + err = alcCloseDevice(THIS->device); + + THIS->device = NULL; + } + + return err; +} + +BEGIN_METHOD_VOID(AlcDevice_free) + + close_device(THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(ALC_CreateContext, GB_OBJECT device; GB_OBJECT attrs) + + CALCCONTEXT *context; + + context = GB.Create(GB.FindClass("AlcContext"), NULL, NULL); + init_context(context, VARG(device), VARGOPT(attrs, NULL)); + if (context->context) + GB.ReturnObject(context); + else + { + GB.Unref(POINTER(&context)); + GB.ReturnNull(); + } + +END_METHOD + +#define GET_CONTEXT() \ + CALCCONTEXT *context = VARG(context); \ + if (GB.CheckObject(context)) \ + return; + +BEGIN_METHOD(ALC_MakeContextCurrent, GB_OBJECT context) + + CALCCONTEXT *context = VARG(context); + ALCboolean err = alcMakeContextCurrent(context ? context->context : NULL); + if (err == ALC_FALSE) + set_current_context(context); + GB.ReturnBoolean(err); + +END_METHOD + +BEGIN_METHOD(ALC_ProcessContext, GB_OBJECT context) + + GET_CONTEXT(); + alcProcessContext(context->context); + +END_METHOD + +BEGIN_METHOD(ALC_SuspendContext, GB_OBJECT context) + + GET_CONTEXT(); + alcSuspendContext(context->context); + +END_METHOD + +BEGIN_METHOD(ALC_DestroyContext, GB_OBJECT context) + + GET_CONTEXT(); + destroy_context(context); + +END_METHOD + +BEGIN_METHOD_VOID(ALC_GetCurrentContext) + + GB.ReturnObject(_current_context); + +END_METHOD + +BEGIN_METHOD(ALC_GetContextsDevice, GB_OBJECT context) + + GET_CONTEXT(); + GB.ReturnObject(THIS->device); + +END_METHOD + +BEGIN_METHOD(ALC_OpenDevice, GB_STRING name) + + GB.ReturnObject(create_device(alcOpenDevice(GB.ToZeroString(ARG(name))))); + +END_METHOD + +#define GET_DEVICE() \ + CALCDEVICE *device = VARG(device); \ + if (GB.CheckObject(device)) \ + return; + +BEGIN_METHOD(ALC_CloseDevice, GB_OBJECT device) + + GET_DEVICE(); + GB.ReturnBoolean(close_device(device)); + +END_METHOD + +BEGIN_METHOD(ALC_GetError, GB_OBJECT device) + + GET_DEVICE(); + GB.ReturnInteger(alcGetError(device->device)); + +END_METHOD + +BEGIN_METHOD(ALC_IsExtensionPresent, GB_OBJECT device; GB_STRING ext) + + GET_DEVICE(); + GB.ReturnBoolean(alcIsExtensionPresent(device->device, GB.ToZeroString(ARG(ext)))); + +END_METHOD + +BEGIN_METHOD(ALC_GetString, GB_OBJECT device; GB_INTEGER param) + + CALCDEVICE *device = VARG(device); + ALCenum param = VARG(param); + + if (device && GB.CheckObject(device)) + return; + + if (!device && (param == ALC_DEVICE_SPECIFIER || param == ALC_CAPTURE_DEVICE_SPECIFIER)) + { + GB.Error("This query actually returns a string array. Use ALC_GetStringv instead"); + return; + } + + GB.ReturnNewZeroString(alcGetString(device ? device->device : NULL, param)); + +END_METHOD + +BEGIN_METHOD(ALC_GetStringv, GB_OBJECT device; GB_INTEGER param) + + CALCDEVICE *device = VARG(device); + ALCenum param = VARG(param); + const char *result, *p; + int len; + GB_ARRAY array; + + if (!(!device && (param == ALC_DEVICE_SPECIFIER || param == ALC_CAPTURE_DEVICE_SPECIFIER))) + { + GB.ReturnNull(); + return; + } + + result = alcGetString(NULL, param); + if (!result) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_STRING, 0); + + for(;;) + { + p = index(result, 0); + if (!p) + break; + len = p - result; + if (len <= 0) + break; + + *((char **)GB.Array.Add(array)) = GB.NewString(result, len); + result = p + 1; + } + + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(ALC_GetIntegerv, GB_OBJECT device; GB_INTEGER param; GB_INTEGER size) + + GB_ARRAY array; + int size = VARG(size); + + GET_DEVICE(); + + if (size <= 0 || size >= 65536) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_INTEGER, size); + alcGetIntegerv(device->device, VARG(param), size, GB.Array.Get(array, 0)); + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureOpenDevice, GB_STRING name; GB_INTEGER freq; GB_INTEGER format; GB_INTEGER buffer_size) + + CALCDEVICE *device = create_device(alcCaptureOpenDevice(GB.ToZeroString(ARG(name)), VARG(freq), VARG(format), VARG(buffer_size))); + + if (device) + { + device->capture = TRUE; + + switch(VARG(format)) + { + case AL_FORMAT_MONO8: device->sampleSize = 1; break; + case AL_FORMAT_MONO16: case AL_FORMAT_STEREO8: device->sampleSize = 2; break; + case AL_FORMAT_STEREO16: device->sampleSize = 4; break; + default: device->sampleSize = 0; + } + } + + GB.ReturnObject(device); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureStart, GB_OBJECT device) + + GET_DEVICE(); + alcCaptureStart(device->device); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureStop, GB_OBJECT device) + + GET_DEVICE(); + alcCaptureStop(device->device); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureSamples, GB_OBJECT device; GB_INTEGER samples; GB_POINTER buffer) + + GB_ARRAY array = NULL; + int samples = VARG(samples); + void *buffer; + + GET_DEVICE(); + + if (samples <= 0) + { + GB.ReturnNull(); + return; + } + + if (MISSING(buffer)) + { + if (device->sampleSize == 0) + { + GB.Error("Unknown sample format"); + return; + } + GB.Array.New(&array, device->sampleSize == 1 ? GB_T_BYTE : device->sampleSize == 2 ? GB_T_SHORT : GB_T_INTEGER, samples); + buffer = GB.Array.Get(array, 0); + } + else + buffer = (void *)VARG(buffer); + + alcCaptureSamples(device->device, buffer, samples); + + GB.ReturnObject(array); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC AlcDeviceDesc[] = +{ + GB_DECLARE("AlcDevice", sizeof(CALCDEVICE)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(check_device), + + GB_METHOD("_free", NULL, AlcDevice_free, NULL), + + GB_END_DECLARE +}; + +GB_DESC AlcContextDesc[] = +{ + GB_DECLARE("AlcContext", sizeof(CALCCONTEXT)), + GB_HOOK_CHECK(check_context), + + GB_STATIC_METHOD("_exit", NULL, AlcContext_exit, NULL), + + GB_METHOD("_new", NULL, AlcContext_new, "(Device)AlcDevice;[(Attributes)Integer[];]"), + GB_METHOD("MakeCurrent", "b", AlcContext_MakeCurrent, NULL), + + GB_END_DECLARE +}; + +GB_DESC AlcDesc[] = +{ + GB_DECLARE_VIRTUAL("Alc"), + + GB_CONSTANT("FALSE", "i", ALC_FALSE), + GB_CONSTANT("TRUE", "i", ALC_TRUE), + GB_CONSTANT("FREQUENCY", "i", ALC_FREQUENCY), + GB_CONSTANT("REFRESH", "i", ALC_REFRESH), + GB_CONSTANT("SYNC", "i", ALC_SYNC), + GB_CONSTANT("MONO_SOURCES", "i", ALC_MONO_SOURCES), + GB_CONSTANT("STEREO_SOURCES", "i", ALC_STEREO_SOURCES), + GB_CONSTANT("NO_ERROR", "i", ALC_NO_ERROR), + GB_CONSTANT("INVALID_DEVICE", "i", ALC_INVALID_DEVICE), + GB_CONSTANT("INVALID_CONTEXT", "i", ALC_INVALID_CONTEXT), + GB_CONSTANT("INVALID_ENUM", "i", ALC_INVALID_ENUM), + GB_CONSTANT("INVALID_VALUE", "i", ALC_INVALID_VALUE), + GB_CONSTANT("OUT_OF_MEMORY", "i", ALC_OUT_OF_MEMORY), + GB_CONSTANT("DEFAULT_DEVICE_SPECIFIER", "i", ALC_DEFAULT_DEVICE_SPECIFIER), + GB_CONSTANT("DEVICE_SPECIFIER", "i", ALC_DEVICE_SPECIFIER), + GB_CONSTANT("EXTENSIONS", "i", ALC_EXTENSIONS), + GB_CONSTANT("MAJOR_VERSION", "i", ALC_MAJOR_VERSION), + GB_CONSTANT("MINOR_VERSION", "i", ALC_MINOR_VERSION), + GB_CONSTANT("ATTRIBUTES_SIZE", "i", ALC_ATTRIBUTES_SIZE), + GB_CONSTANT("ALL_ATTRIBUTES", "i", ALC_ALL_ATTRIBUTES), + GB_CONSTANT("EXT_CAPTURE", "i", ALC_EXT_CAPTURE), + GB_CONSTANT("CAPTURE_DEVICE_SPECIFIER", "i", ALC_CAPTURE_DEVICE_SPECIFIER), + GB_CONSTANT("CAPTURE_DEFAULT_DEVICE_SPECIFIER", "i", ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER), + GB_CONSTANT("CAPTURE_SAMPLES", "i", ALC_CAPTURE_SAMPLES), + GB_CONSTANT("ENUMERATE_ALL_EXT", "i", ALC_ENUMERATE_ALL_EXT), + GB_CONSTANT("DEFAULT_ALL_DEVICES_SPECIFIER", "i", ALC_DEFAULT_ALL_DEVICES_SPECIFIER), + GB_CONSTANT("ALL_DEVICES_SPECIFIER", "i", ALC_ALL_DEVICES_SPECIFIER), + + GB_STATIC_METHOD("CreateContext", "AlcContext", ALC_CreateContext, "(Device)AlcDevice;[(Attributes)Integer[];]"), + GB_STATIC_METHOD("MakeContextCurrent", "b", ALC_MakeContextCurrent, "(Context)AlcContext;"), + GB_STATIC_METHOD("ProcessContext", NULL, ALC_ProcessContext, "(Context)AlcContext;"), + GB_STATIC_METHOD("SuspendContext", NULL, ALC_SuspendContext, "(Context)AlcContext;"), + GB_STATIC_METHOD("DestroyContext", NULL, ALC_DestroyContext, "(Context)AlcContext;"), + GB_STATIC_METHOD("GetCurrentContext", "AlcContext", ALC_GetCurrentContext, NULL), + GB_STATIC_METHOD("GetContextsDevice", "AlcDevice", ALC_GetContextsDevice, "(Context)AlcContext;"), + GB_STATIC_METHOD("OpenDevice", "AlcDevice", ALC_OpenDevice, "(Name)s"), + GB_STATIC_METHOD("CloseDevice", "b", ALC_CloseDevice, "(Device)AlcDevice;"), + GB_STATIC_METHOD("GetError", "i", ALC_GetError, "(Device)AlcDevice;"), + GB_STATIC_METHOD("IsExtensionPresent", "b", ALC_IsExtensionPresent, "(Device)AlcDevice;(ExtensionName)s"), + GB_STATIC_METHOD("GetString", "s", ALC_GetString, "(Device)AlcDevice;(Param)i"), + GB_STATIC_METHOD("GetStringv", "String[]", ALC_GetStringv, "(Device)AlcDevice;(Param)i"), + GB_STATIC_METHOD("GetIntegerv", "Integer[]", ALC_GetIntegerv, "(Device)AlcDevice;(Param)i;(Size)i"), + GB_STATIC_METHOD("CaptureOpenDevice", "AlcDevice", ALC_CaptureOpenDevice, "(Name)s(Frequency)i(Format)i(BufferSize)i"), + GB_STATIC_METHOD("CaptureCloseDevice", "b", ALC_CloseDevice, "(Device)AlcDevice;"), + GB_STATIC_METHOD("CaptureStart", NULL, ALC_CaptureStart, "(Device)AlcDevice;"), + GB_STATIC_METHOD("CaptureStop", NULL, ALC_CaptureStop, "(Device)AlcDevice;"), + GB_STATIC_METHOD("CaptureSamples", "v", ALC_CaptureSamples, "(Device)AlcDevice;(Samples)i[(Buffer)p]"), + + GB_END_DECLARE +}; + +#ifdef TOTO +// ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); +// ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context ); +// ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context ); +// ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context ); +// ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context ); +// ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( void ); +// ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context ); +// ALC_API ALCdevice * ALC_APIENTRY alcOpenDevice( const ALCchar *devicename ); +// ALC_API ALCboolean ALC_APIENTRY alcCloseDevice( ALCdevice *device ); +// ALC_API ALCenum ALC_APIENTRY alcGetError( ALCdevice *device ); +// ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); +// ALC_API void * ALC_APIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname ); +// ALC_API ALCenum ALC_APIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname ); +// ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param ); +// ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); +// ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +// ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice( ALCdevice *device ); +// ALC_API void ALC_APIENTRY alcCaptureStart( ALCdevice *device ); +// ALC_API void ALC_APIENTRY alcCaptureStop( ALCdevice *device ); +// ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); +#endif diff --git a/gb.openal/src/c_alc.h b/gb.openal/src/c_alc.h new file mode 100644 index 00000000..1d3dc733 --- /dev/null +++ b/gb.openal/src/c_alc.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + c_alc.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_ALC_H +#define __C_ALC_H + +#include "main.h" + +#ifndef __C_AL_C +extern GB_DESC AlcDesc[]; +extern GB_DESC AlcContextDesc[]; +extern GB_DESC AlcDeviceDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + ALCdevice *device; + unsigned capture : 1; + unsigned sampleSize : 3; + } + CALCDEVICE; + +typedef + struct { + GB_BASE ob; + ALCcontext *context; + CALCDEVICE *device; + } + CALCCONTEXT; + +#endif diff --git a/gb.openal/src/c_alure.c b/gb.openal/src/c_alure.c new file mode 100644 index 00000000..569bd65b --- /dev/null +++ b/gb.openal/src/c_alure.c @@ -0,0 +1,411 @@ +/*************************************************************************** + + c_alure.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_ALURE_C + +#include "c_alure.h" + +static char *_version = NULL; +static GB_ARRAY _stop = NULL; +static bool _register_stop = FALSE; + +static void cb_play(void *userData, ALuint source) +{ + if (_register_stop) + { + if (!_stop) + GB.Array.New(&_stop, GB_T_INTEGER, 0); + + *((ALuint *)GB.Array.Add(_stop)) = source; + } +} + +//--------------------------------------------------------------------------- + +#define THIS ((CALURESTREAM *)_object) + +static int check_stream(CALURESTREAM *_object) +{ + return THIS->stream == NULL; +} + +CALURESTREAM *create_stream(int num_buf) +{ + CALURESTREAM *stream = GB.New(GB.FindClass("AlureStream"), NULL, NULL); + + if (num_buf > 0) + GB.Array.New(&stream->buffers, GB_T_INTEGER, num_buf); + + return stream; +} + +bool destroy_stream(CALURESTREAM *_object) +{ + ALsizei nbuf; + ALuint *buffers; + bool err; + + if (THIS->buffers) + { + nbuf = GB.Array.Count(THIS->buffers); + buffers = GB.Array.Get(THIS->buffers, 0); + } + else + { + nbuf = 0; + buffers = NULL; + } + + err = alureDestroyStream(THIS->stream, nbuf, buffers); + + GB.Unref(POINTER(&THIS->buffers)); + + if (THIS->addr) + { + GB.ReleaseFile(THIS->addr, THIS->len); + THIS->addr = NULL; + } + + THIS->stream = NULL; + + return err; +} + +BEGIN_METHOD_VOID(AlureStream_free) + + destroy_stream(THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Alure_exit) + + GB.FreeString(&_version); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_GetVersion) + + ALuint major, minor; + char buffer[32]; + + if (!_version) + { + alureGetVersion(&major, &minor); + sprintf(buffer, "%d.%d", major, minor); + _version = GB.NewZeroString(buffer); + } + + GB.ReturnString(_version); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_GetErrorString) + + GB.ReturnNewZeroString(alureGetErrorString()); + +END_METHOD + +BEGIN_METHOD(Alure_GetDeviceNames, GB_BOOLEAN all) + + GB_ARRAY array; + const ALchar **devices; + ALsizei count; + int i; + + devices = alureGetDeviceNames(VARG(all), &count); + + GB.Array.New(&array, GB_T_STRING, count); + for (i = 0; i < count; i++) + *((char**)GB.Array.Get(array, i)) = GB.NewZeroString(devices[i]); + + alureFreeDeviceNames(devices); + + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(Alure_InitDevice, GB_STRING name; GB_OBJECT array) + + GB_ARRAY array = VARG(array); + ALCint *attrs = NULL; + int count; + + if (array) + { + count = GB.Array.Count(array); + if (count) + { + attrs = alloca(sizeof(ALCint) * (count + 1)); + memcpy(attrs, GB.Array.Get(array, 0), count * sizeof(ALCint)); + attrs[count] = 0; + } + } + + GB.ReturnBoolean(alureInitDevice(MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), attrs)); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_ShutdownDevice) + + GB.ReturnBoolean(alureShutdownDevice()); + +END_METHOD + +BEGIN_METHOD(Alure_GetSampleFormat, GB_INTEGER channels; GB_INTEGER bits; GB_INTEGER fbits) + + GB.ReturnInteger(alureGetSampleFormat(VARG(channels), VARG(bits), VARG(fbits))); + +END_METHOD + +BEGIN_METHOD(Alure_Sleep, GB_FLOAT duration) + + GB.ReturnBoolean(alureSleep(VARG(duration))); + +END_METHOD + +BEGIN_METHOD(Alure_StreamSizeIsMicroSec, GB_BOOLEAN useus) + + GB.ReturnBoolean(alureStreamSizeIsMicroSec(VARG(useus))); + +END_METHOD + +BEGIN_METHOD(Alure_CreateBufferFromFile, GB_STRING path) + + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + GB.ReturnInteger(alureCreateBufferFromMemory((const ALubyte *)addr, len)); + + GB.ReleaseFile(addr, len); + +END_METHOD + +BEGIN_METHOD(Alure_BufferDataFromFile, GB_STRING path; GB_INTEGER buffer) + + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + GB.ReturnBoolean(alureBufferDataFromMemory((const ALubyte *)addr, len, VARG(buffer))); + + GB.ReleaseFile(addr, len); + +END_METHOD + +BEGIN_METHOD(Alure_CreateStreamFromFile, GB_STRING path; GB_INTEGER chunck_length; GB_INTEGER num_buf) + + CALURESTREAM *stream; + char *addr; + int len; + int num_buf = VARG(num_buf); + ALuint *buffers; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + stream = create_stream(num_buf); + stream->addr = addr; + stream->len = len; + + if (stream->buffers) + buffers = (ALuint *)GB.Array.Get(stream->buffers, 0); + else + buffers = NULL; + + stream->stream = alureCreateStreamFromStaticMemory((const ALubyte *)addr, len, VARG(chunck_length), num_buf, buffers); + + GB.ReturnObject(stream); + +END_METHOD + +#define GET_STREAM() \ + CALURESTREAM *stream = VARG(stream); \ + if (GB.CheckObject(stream)) \ + return; + +BEGIN_METHOD(Alure_GetStreamBuffers, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnObject(stream->buffers); + +END_METHOD + +BEGIN_METHOD(Alure_GetStreamLength, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnLong(alureGetStreamLength(stream->stream)); + +END_METHOD + +BEGIN_METHOD(Alure_GetStreamFrequency, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnInteger(alureGetStreamFrequency(stream->stream)); + +END_METHOD + +BEGIN_METHOD(Alure_BufferDataFromStream, GB_OBJECT stream; GB_OBJECT buffers) + + GB_ARRAY buffers = VARG(buffers); + + GET_STREAM(); + + if (GB.CheckObject(buffers)) + return; + + GB.ReturnInteger(alureBufferDataFromStream(stream->stream, GB.Array.Count(buffers), GB.Array.Get(buffers, 0))); + +END_METHOD + +BEGIN_METHOD(Alure_RewindStream, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnBoolean(alureRewindStream(stream->stream)); + +END_METHOD + +BEGIN_METHOD(Alure_SetStreamOrder, GB_OBJECT stream; GB_INTEGER order) + + GET_STREAM(); + GB.ReturnBoolean(alureSetStreamOrder(stream->stream, VARG(order))); + +END_METHOD + +BEGIN_METHOD(Alure_SetStreamPatchset, GB_OBJECT stream; GB_STRING patchset) + + GET_STREAM(); + GB.ReturnBoolean(alureSetStreamPatchset(stream->stream, GB.ToZeroString(ARG(patchset)))); + +END_METHOD + +BEGIN_METHOD(Alure_DestroyStream, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnBoolean(destroy_stream(stream)); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_Update) + + GB_ARRAY stop; + + _register_stop = TRUE; + alureUpdate(); + _register_stop = FALSE; + + stop = _stop; + _stop = NULL; + GB.ReturnObject(stop); + +END_METHOD + +BEGIN_METHOD(Alure_UpdateInterval, GB_FLOAT interval) + + GB.ReturnBoolean(alureUpdateInterval(VARG(interval))); + +END_METHOD + +BEGIN_METHOD(Alure_PlaySourceStream, GB_INTEGER source; GB_OBJECT stream; GB_INTEGER num_bufs; GB_INTEGER loop_count) + + GET_STREAM(); + + GB.ReturnBoolean(alurePlaySourceStream(VARG(source), stream->stream, VARG(num_bufs), VARG(loop_count), cb_play, NULL)); + +END_METHOD + +BEGIN_METHOD(Alure_PlaySource, GB_INTEGER source) + + GB.ReturnBoolean(alurePlaySource(VARG(source), cb_play, NULL)); + +END_METHOD + +BEGIN_METHOD(Alure_StopSource, GB_INTEGER source) + + GB.ReturnBoolean(alureStopSource(VARG(source), TRUE)); + +END_METHOD + +BEGIN_METHOD(Alure_ResumeSource, GB_INTEGER source) + + GB.ReturnBoolean(alureResumeSource(VARG(source))); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC AlureStreamDesc[] = +{ + GB_DECLARE("AlureStream", sizeof(CALURESTREAM)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(check_stream), + + GB_METHOD("_free", NULL, AlureStream_free, NULL), + + GB_END_DECLARE +}; + +GB_DESC AlureDesc[] = +{ + GB_DECLARE_VIRTUAL("Alure"), + + GB_STATIC_METHOD("_exit", NULL, Alure_exit, NULL), + + GB_STATIC_METHOD("GetVersion", "s", Alure_GetVersion, NULL), + GB_STATIC_METHOD("GetErrorString", "s", Alure_GetErrorString, NULL), + GB_STATIC_METHOD("GetDeviceNames", "String[]", Alure_GetDeviceNames, "(All)b"), + GB_STATIC_METHOD("InitDevice", "b", Alure_InitDevice, "[(Name)s(Attributes)Integer[];]"), + GB_STATIC_METHOD("ShutdownDevice", "b", Alure_ShutdownDevice, NULL), + GB_STATIC_METHOD("GetSampleFormat", "i", Alure_GetSampleFormat, "(Channels)i(Bits)i(FloatBits)i"), + GB_STATIC_METHOD("Sleep", "b", Alure_Sleep, "(Duration)f"), + GB_STATIC_METHOD("StreamSizeIsMicroSec", "b", Alure_StreamSizeIsMicroSec, "(UseMicroSeconds)b"), + GB_STATIC_METHOD("CreateBufferFromFile", "i", Alure_CreateBufferFromFile, "(Path)s"), + // GB_STATIC_METHOD("CreateBufferFromMemory", "i", Alure_CreateBufferFromMemory, "(Data)p(Size)i"), + GB_STATIC_METHOD("BufferDataFromFile", "b", Alure_BufferDataFromFile, "(Path)s(Buffer)i"), + // GB_STATIC_METHOD("BufferDataFromMemory", "b", Alure_BufferDataFromMemory, "(Data)p(Size)i(Buffer)i"), + GB_STATIC_METHOD("CreateStreamFromFile", "AlureStream", Alure_CreateStreamFromFile, "(Path)s(ChunkLength)i(NumBuf)i"), + // GB_STATIC_METHOD("CreateStreamFromMemory", "AlureStream", Alure_CreateStreamFromFile, "(Data)p(Length)i(ChunkLength)i(NumBuf)i"), + // GB_STATIC_METHOD("CreateStreamFromStaticMemory", "AlureStream", Alure_CreateStreamFromFile, "(Data)p(Length)i(ChunkLength)i(NumBuf)i"), + GB_STATIC_METHOD("GetStreamBuffers", "Integer[]", Alure_GetStreamBuffers, "(Stream)AlureStream;"), + GB_STATIC_METHOD("GetStreamLength", "l", Alure_GetStreamLength, "(Stream)AlureStream;"), + GB_STATIC_METHOD("GetStreamFrequency", "i", Alure_GetStreamFrequency, "(Stream)AlureStream;"), + GB_STATIC_METHOD("BufferDataFromStream", "i", Alure_BufferDataFromStream, "(Stream)AlureStream;(Buffers)Integer[];"), + GB_STATIC_METHOD("RewindStream", "b", Alure_RewindStream, "(Stream)AlureStream;"), + GB_STATIC_METHOD("SetStreamOrder", "b", Alure_SetStreamOrder, "(Stream)AlureStream;(Order)i"), + GB_STATIC_METHOD("SetStreamPatchset", "b", Alure_SetStreamPatchset, "(Stream)AlureStream;(Patchset)s"), + GB_STATIC_METHOD("DestroyStream", "b", Alure_DestroyStream, "(Stream)AlureStream;"), + GB_STATIC_METHOD("Update", "Integer[]", Alure_Update, NULL), + GB_STATIC_METHOD("UpdateInterval", "b", Alure_UpdateInterval, "(Interval)f"), + GB_STATIC_METHOD("PlaySourceStream", "b", Alure_PlaySourceStream, "(Source)i(Stream)AlureStream;(NumBufs)i(LoopCount)i"), + GB_STATIC_METHOD("PlaySource", "b", Alure_PlaySource, "(Source)i"), + GB_STATIC_METHOD("StopSource", "b", Alure_StopSource, "(Source)i"), + GB_STATIC_METHOD("ResumeSource", "b", Alure_StopSource, "(Source)i"), + + GB_END_DECLARE +}; diff --git a/gb.openal/src/c_alure.h b/gb.openal/src/c_alure.h new file mode 100644 index 00000000..21eb2dc0 --- /dev/null +++ b/gb.openal/src/c_alure.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + c_alure.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_ALURE_H +#define __C_ALURE_H + +#include "main.h" + +#ifndef __C_ALURE_C +extern GB_DESC AlureDesc[]; +extern GB_DESC AlureStreamDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + alureStream *stream; + GB_ARRAY buffers; + char *addr; + int len; + volatile int stop; + } + CALURESTREAM; + +#endif diff --git a/gb.openal/src/gb.openal.component b/gb.openal/src/gb.openal.component new file mode 100644 index 00000000..23a4d791 --- /dev/null +++ b/gb.openal/src/gb.openal.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Finished diff --git a/gb.openal/src/main.c b/gb.openal/src/main.c new file mode 100644 index 00000000..a93dae3f --- /dev/null +++ b/gb.openal/src/main.c @@ -0,0 +1,55 @@ +/*************************************************************************** + + main.c + + gb.openal component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "c_al.h" +#include "c_alc.h" +#include "c_alure.h" + +#include "main.h" + + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + AlDesc, + AlcDesc, + AlcContextDesc, + AlcDeviceDesc, + AlureStreamDesc, + AlureDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.openal/src/main.h b/gb.openal/src/main.h new file mode 100644 index 00000000..b50c99d4 --- /dev/null +++ b/gb.openal/src/main.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + main.h + + gb.openal component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#include +#include +#include +#include + +#ifndef ALC_EXT_CAPTURE +#define ALC_EXT_CAPTURE 1 +#endif + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.opengl/AUTHORS b/gb.opengl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.opengl/COPYING b/gb.opengl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.opengl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.opengl/ChangeLog b/gb.opengl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.opengl/INSTALL b/gb.opengl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.opengl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.opengl/Makefile.am b/gb.opengl/Makefile.am new file mode 100644 index 00000000..e9f331fd --- /dev/null +++ b/gb.opengl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @OPENGL_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.opengl/NEWS b/gb.opengl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.opengl/README b/gb.opengl/README new file mode 100644 index 00000000..53f792ab --- /dev/null +++ b/gb.opengl/README @@ -0,0 +1,7 @@ +TODO : +GLrasterisation : +glBitmap() +glPolygonStipple() +glGetPolygonStipple() + +Waiting for texture implementation under components (needed to test !) diff --git a/gb.opengl/acinclude.m4 b/gb.opengl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.opengl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.opengl/component.am b/gb.opengl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.opengl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.opengl/configure.ac b/gb.opengl/configure.ac new file mode 100644 index 00000000..2a7887f6 --- /dev/null +++ b/gb.opengl/configure.ac @@ -0,0 +1,35 @@ +dnl ---- configure.ac for gb.opengl + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-opengl],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.opengl) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + opengl, OPENGL, gb.opengl, [src], + [gl glew]) + +GB_COMPONENT_PKG_CONFIG( + glsl, GLSL, gb.opengl.glsl, [glsl], + [gl glew]) + +GB_COMPONENT_PKG_CONFIG( + glu, GLU, gb.opengl.glu, [glu], + [glu]) + +GB_COMPONENT_PKG_CONFIG( + sge, SGE, gb.opengl.sge, [sge], + [gl glew]) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +src/glu/Makefile \ +src/glsl/Makefile \ +src/sge/Makefile +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.opengl/gambas.h b/gb.opengl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.opengl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.opengl/gb.image.h b/gb.opengl/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.opengl/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.opengl/gb_common.h b/gb.opengl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.opengl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.opengl/m4 b/gb.opengl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.opengl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.opengl/reconf b/gb.opengl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.opengl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.opengl/src/GL.c b/gb.opengl/src/GL.c new file mode 100644 index 00000000..251aa9c7 --- /dev/null +++ b/gb.opengl/src/GL.c @@ -0,0 +1,1069 @@ +/*************************************************************************** + + GL.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GL_C + +#include "GL.h" + +#include "GLclipping.h" +#include "GLcolorLighting.h" +#include "GLcoordTransf.h" +#include "GLdisplayList.h" +#include "GLfog.h" +#include "GLframeBufferOps.h" +#include "GLmodesExec.h" +#include "GLpixelOperations.h" +#include "GLprimitives.h" +#include "GLrasterization.h" +#include "GLtextureMapping.h" +#include "GLinfo.h" +#include "GLselectFeedback.h" +#include "GLeval.h" +#include "framebufferobject.h" + +#define MAX_COMPAT_WARNING 16 + +/**************************************************************************/ + +BEGIN_METHOD_VOID(GLCHECKERROR) + + GB.ReturnInteger(glGetError()); + +END_METHOD + +BEGIN_METHOD(GLCHECKEXTENSIONS, GB_STRING extlist) + + GB.ReturnBoolean(glewIsSupported(GB.ToZeroString(ARG(extlist)))); + +END_METHOD + + +BEGIN_METHOD_VOID(GL_property) + + GB.ReturnBoolean(TRUE); + +END_METHOD + +BEGIN_METHOD_VOID(GL_unknown) + + static int nwarn = 0; + static void *klass = NULL; + static const char *same_name[] = { "VIEWPORT", "ACCUM", "CLEAR", NULL}; + static const int same_value[] = { GL_VIEWPORT, GL_ACCUM, GL_CLEAR }; + + char *name = GB.GetUnknown(); + const char **p; + bool add_underscore; + int same_index; + + if (strncasecmp(name, "GL_", 3)) + { + GB.Error(GB_ERR_NSYMBOL, "Gl", name); + return; + } + + if (GB.NParam()) + { + GB.Error(GB_ERR_NPROPERTY, "Gl", name); + return; + } + + name += 3; + + add_underscore = FALSE; + same_index = 0; + for (p = same_name; *p; p++, same_index++) + { + if (!strcasecmp(*p, name)) + { + add_underscore = TRUE; + break; + } + } + + nwarn++; + if (nwarn <= MAX_COMPAT_WARNING) + fprintf(stderr, "gb.opengl: warning: Gl.GL_%s constant is deprecated. Use Gl.%s%s now.\n", name, name, add_underscore ? "_" : ""); + else if (nwarn == (MAX_COMPAT_WARNING + 1)) + fprintf(stderr, "gb.opengl: warning: too many deprecated constant warnings.\n"); + + if (!klass) + klass = (void *)GB.FindClass("Gl"); + + if (add_underscore) + GB.ReturnInteger(same_value[same_index]); + else + GB.GetProperty(klass, name); + + GB.ReturnConvVariant(); + +END_METHOD + +/**************************************************************************/ + +GB_DESC Cgl[] = +{ + GB_DECLARE("Gl",0), GB_NOT_CREATABLE(), + + /* Check errors */ + GB_STATIC_METHOD("GetError", "i", GLCHECKERROR, NULL), + /* Check an extension or a driver ability */ + GB_STATIC_METHOD("CheckExtensions", "b", GLCHECKEXTENSIONS, "(Extensions)s"), + + /* Primitives - see GLprimitives.h */ + GB_STATIC_METHOD("Begin", NULL, GLBEGIN, "(Primitive)i"), + GB_STATIC_METHOD("EdgeFlag", NULL, GLEDGEFLAG, "(Flag)b"), + GB_STATIC_METHOD("End", NULL, GLEND, NULL), + GB_STATIC_METHOD("Rectf", NULL, GLRECTF, "(X1)f(Y1)f(X2)f(Y2)f"), + GB_STATIC_METHOD("Recti", NULL, GLRECTI, "(X1)i(Y1)i(X2)i(Y2)i"), + GB_STATIC_METHOD("Vertex2f", NULL, GLVERTEX2F, "(X)f(Y)f"), + GB_STATIC_METHOD("Vertex3f", NULL, GLVERTEX3F, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Vertex4f", NULL, GLVERTEXF, "(X)f(Y)f(Z)f(W)f"), + GB_STATIC_METHOD("Vertexf", NULL, GLVERTEXF, "(X)f(Y)f[(Z)f(W)f]"), + GB_STATIC_METHOD("Vertex2i", NULL, GLVERTEX2I, "(X)i(Y)i"), + GB_STATIC_METHOD("Vertex3i", NULL, GLVERTEX3I, "(X)i(Y)i(Z)i"), + GB_STATIC_METHOD("Vertex4i", NULL, GLVERTEXI, "(X)i(Y)i(Z)i(W)i"), + GB_STATIC_METHOD("Vertexi", NULL, GLVERTEXI, "(X)i(Y)i[(Z)i(W)i]"), + GB_STATIC_METHOD("Vertexfv", NULL, GLVERTEXFV, "(Coords)Float[]"), + GB_STATIC_METHOD("Vertexiv", NULL, GLVERTEXIV, "(Coords)Integer[]"), + + /* Display lists - see GLdisplayList.h */ + GB_STATIC_METHOD("CallList", NULL, GLCALLLIST, "(Index)i"), + GB_STATIC_METHOD("CallLists", NULL, GLCALLLISTS, "(Lists)Integer[]"), + GB_STATIC_METHOD("DeleteLists", NULL, GLDELETELISTS, "(Index)i(Range)i"), + GB_STATIC_METHOD("EndList", NULL, GLENDLIST, NULL), + GB_STATIC_METHOD("GenLists", "i", GLGENLISTS, "(Count)i"), + GB_STATIC_METHOD("IsList", "b", GLISLIST, "(Index)i"), + GB_STATIC_METHOD("ListBase", NULL, GLLISTBASE, "(Index)i"), + GB_STATIC_METHOD("NewList", NULL, GLNEWLIST, "(Index)i(Mode)i"), + + /* Coordinate Transformation - see GLcoordTransf.h */ + GB_STATIC_METHOD("DepthRange", NULL, GLDEPTHRANGE, "(Near)f(Far)f"), + GB_STATIC_METHOD("Frustum", NULL, GLFRUSTUM, "(Left)f(Right)f(Bottom)f(Top)f(Near)f(Far)f"), + GB_STATIC_METHOD("LoadIdentity", NULL, GLLOADIDENTITY, NULL), + GB_STATIC_METHOD("LoadMatrixf", NULL, GLLOADMATRIXF, "(Matrix)Float[]"), + GB_STATIC_METHOD("MatrixMode", NULL, GLMATRIXMODE, "(Mode)i"), + GB_STATIC_METHOD("MultMatrixf", NULL, GLMULTMATRIXF, "(Matrix)Float[]"), + GB_STATIC_METHOD("Ortho", NULL, GLORTHO, "(Left)f(Right)f(Bottom)f(Top)f(Near)f(Far)f"), + GB_STATIC_METHOD("PopMatrix", NULL, GLPOPMATRIX, NULL), + GB_STATIC_METHOD("PushMatrix", NULL, GLPUSHMATRIX, NULL), + GB_STATIC_METHOD("Rotatef", NULL, GLROTATEF, "(Angle)f(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Scalef", NULL, GLSCALEF, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Translatef", NULL, GLTRANSLATEF, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Viewport", NULL, GLVIEWPORT, "(X)i(Y)i(Width)i(Height)i"), + + /* Coloring and Lighting - see GLcolorLighting.h */ + GB_STATIC_METHOD("Color3f", NULL, GLCOLOR3F, "(Red)f(Green)f(Blue)f"), + GB_STATIC_METHOD("Color4f", NULL, GLCOLORF, "(Red)f(Green)f(Blue)f(Alpha)f"), + GB_STATIC_METHOD("Colorf", NULL, GLCOLORF, "(Red)f(Green)f(Blue)f[(Alpha)f]"), + GB_STATIC_METHOD("Color3i", NULL, GLCOLOR3I, "(Red)i(Green)i(Blue)i"), + GB_STATIC_METHOD("Color4i", NULL, GLCOLORI, "(Red)i(Green)i(Blue)i(Alpha)i"), + GB_STATIC_METHOD("Colori", NULL, GLCOLORI, "(Red)i(Green)i(Blue)i[(Alpha)i]"), + GB_STATIC_METHOD("Colorfv", NULL, GLCOLORFV, "(Colors)Float[]"), + GB_STATIC_METHOD("Coloriv", NULL, GLCOLORIV, "(Colors)Integer[]"), + GB_STATIC_METHOD("ColorMaterial", NULL, GLCOLORMATERIAL, "(Face)i(Mode)i"), + GB_STATIC_METHOD("FrontFace", NULL, GLFRONTFACE, "(Mode)i"), + GB_STATIC_METHOD("GetLightfv", "Float[]", GLGETLIGHTFV, "(Light)i(Pname)i"), + GB_STATIC_METHOD("GetLightiv", "Integer[]", GLGETLIGHTIV, "(Light)i(Pname)i"), + GB_STATIC_METHOD("GetMaterialfv", "Float[]", GLGETMATERIALFV, "(Face)i(Pname)i"), + GB_STATIC_METHOD("GetMaterialiv", "Integer[]", GLGETMATERIALIV, "(Face)i(Pname)i"), + GB_STATIC_METHOD("Indexf", NULL, GLINDEXF, "(Index)f"), + GB_STATIC_METHOD("Indexi", NULL, GLINDEXI, "(Index)i"), + GB_STATIC_METHOD("Lightf", NULL, GLLIGHTF, "(Light)i(Pname)i(Param)f"), + GB_STATIC_METHOD("Lighti", NULL, GLLIGHTI, "(Light)i(Pname)i(Param)i"), + GB_STATIC_METHOD("Lightfv", NULL, GLLIGHTFV, "(Light)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("Lightiv", NULL, GLLIGHTIV, "(Light)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("LightModelf", NULL, GLLIGHTMODELF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("LightModeli", NULL, GLLIGHTMODELI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("LightModelfv", NULL, GLLIGHTMODELFV, "(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("LightModeliv", NULL, GLLIGHTMODELIV, "(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("Materialf", NULL, GLMATERIALF, "(Face)i(Pname)i(Param)f"), + GB_STATIC_METHOD("Materiali", NULL, GLMATERIALI, "(Face)i(Pname)i(Param)i"), + GB_STATIC_METHOD("Materialfv", NULL, GLMATERIALFV, "(Face)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("Materialiv", NULL, GLMATERIALIV, "(Face)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("Normal3f", NULL, GLNORMAL3F, "(Nx)f(Ny)f(Nz)f"), + GB_STATIC_METHOD("Normal3i", NULL, GLNORMAL3I, "(Nx)i(Ny)i(Nz)i"), + GB_STATIC_METHOD("Normal3fv", NULL, GLNORMAL3FV, "(Params)Float[]"), + GB_STATIC_METHOD("Normal3iv", NULL, GLNORMAL3IV, "(Params)Integer[]"), + GB_STATIC_METHOD("ShadeModel", NULL, GLSHADEMODEL, "(Model)i"), + + /* Clipping - see GLclipping.h */ + GB_STATIC_METHOD("ClipPlane", NULL, GLCLIPPLANE, "(Plane)i(Equation)Float[]"), + GB_STATIC_METHOD("GetClipPlane", "Float[]", GLGETCLIPPLANE, "(Plane)i"), + + /* Rasterization - see GLrasterization.h */ + //GB_STATIC_METHOD("Bitmap", NULL, GLBITMAP, NULL), //TODO + GB_STATIC_METHOD("CullFace", NULL, GLCULLFACE, "(Mode)i"), + //GB_STATIC_METHOD("GetPolygonStipple", "i", GLPOLYGONSTIPPLE, NULL), //TODO + GB_STATIC_METHOD("LineStipple", NULL, GLLINESTIPPLE, "(Factor)i(Pattern)i"), + GB_STATIC_METHOD("LineWidth", NULL, GLLINEWIDTH, "(Width)f"), + GB_STATIC_METHOD("PointSize", NULL, GLPOINTSIZE, "(Size)f"), + GB_STATIC_METHOD("PolygonMode", NULL, GLPOLYGONMODE, "(Face)i(Mode)i"), + GB_STATIC_METHOD("PolygonOffset", NULL, GLPOLYGONOFFSET, "(Factor)f(Units)f"), + //GB_STATIC_METHOD("PolygonStipple", NULL, GLPOLYGONSTIPPLE, "(Mask)i"), //TODO + GB_STATIC_METHOD("RasterPos2f", NULL, GLRASTERPOS2F, "(X)f(Y)f"), + GB_STATIC_METHOD("RasterPos3f", NULL, GLRASTERPOS3F, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("RasterPos4f", NULL, GLRASTERPOSF, "(X)f(Y)f(Z)f(W)f"), + GB_STATIC_METHOD("RasterPosf", NULL, GLRASTERPOSF, "(X)f(Y)f[(Z)f(W)f]"), + GB_STATIC_METHOD("RasterPos2i", NULL, GLRASTERPOS2I, "(X)i(Y)i"), + GB_STATIC_METHOD("RasterPos3i", NULL, GLRASTERPOS3I, "(X)i(Y)i(Z)i"), + GB_STATIC_METHOD("RasterPos4i", NULL, GLRASTERPOSI, "(X)i(Y)i(Z)i(W)i"), + GB_STATIC_METHOD("RasterPosi", NULL, GLRASTERPOSI, "(X)i(Y)i[(Z)i(W)i]"), + GB_STATIC_METHOD("RasterPosfv", NULL, GLRASTERPOSFV, "(Coords)Float[]"), + GB_STATIC_METHOD("RasterPosiv", NULL, GLRASTERPOSIV, "(Coords)Integer[]"), + + /* PixelOperations - see GLpixelOperations.h */ + // TODO glReadPixels + GB_STATIC_METHOD("CopyPixels", NULL, GLCOPYPIXELS, "(X)i(Y)i(Width)i(Height)i(Buffer)i"), + GB_STATIC_METHOD("PixelStoref", NULL, GLPIXELSTOREF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("PixelStorei", NULL, GLPIXELSTOREI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("PixelTransferf", NULL, GLPIXELTRANSFERF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("PixelTransferi", NULL, GLPIXELTRANSFERI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("ReadBuffer", NULL, GLREADBUFFER, "(Mode)i"), + GB_STATIC_METHOD("DrawPixels", NULL, GLDRAWPIXELS, "(Image)Image;"), + + /* Modes and Execution - see GLmodesExec.h */ + GB_STATIC_METHOD("Disable", NULL, GLDISABLE, "(Capacity)i"), + GB_STATIC_METHOD("Enable", NULL, GLENABLE, "(Capacity)i"), + GB_STATIC_METHOD("Flush", NULL, GLFLUSH, NULL), + GB_STATIC_METHOD("Finish", NULL, GLFINISH, NULL), + GB_STATIC_METHOD("Hint", NULL, GLHINT, "(Target)i(Mode)i"), + GB_STATIC_METHOD("IsEnabled", "b", GLISENABLED, "(Capacity)i"), + + /* Fog - see GLfog.h */ + GB_STATIC_METHOD("Fogf", NULL, GLFOGF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("Fogi", NULL, GLFOGI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("Fogfv", NULL, GLFOGFV, "(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("Fogiv", NULL, GLFOGIV, "(Pname)i(Params)Integer[]"), + + /* Texture Mapping - see GLtextureMapping.h */ + GB_STATIC_METHOD("BindTexture", NULL, GLBINDTEXTURE, "(Target)i(Texture)i"), + GB_STATIC_METHOD("ActiveTexture", NULL, GLACTIVETEXTURE, "(Texture)i" ), + // TODO adapt to gambas + GB_STATIC_METHOD("CopyTexImage1D", NULL, GLCOPYTEXIMAGE1D, "(Target)i(Level)i(Format)i(X)i(Y)i(Width)i(Border)i"), + GB_STATIC_METHOD("CopyTexImage2D", NULL, GLCOPYTEXIMAGE2D, "(Target)i(Level)i(Format)i(X)i(Y)i(Width)i(Height)i(Border)i"), + GB_STATIC_METHOD("DeleteTextures", NULL, GLDELETETEXTURES, "(Textures)Integer[]"), + GB_STATIC_METHOD("GenTextures", "Integer[]", GLGENTEXTURES, "(Count)i"), + GB_STATIC_METHOD("IsTexture", "b", GLISTEXTURE, "(Texture)i"), + GB_STATIC_METHOD("TexCoord1f", NULL, GLTEXCOORD1F, "(S)f"), + GB_STATIC_METHOD("TexCoord2f", NULL, GLTEXCOORD2F, "(S)f(T)f"), + GB_STATIC_METHOD("TexCoord3f", NULL, GLTEXCOORD3F, "(S)f(T)f(R)f"), + GB_STATIC_METHOD("TexCoord4f", NULL, GLTEXCOORDF, "(S)f(T)f(R)f(Q)f"), + GB_STATIC_METHOD("TexCoordf", NULL, GLTEXCOORDF, "(S)f[(T)f(R)f(Q)f]"), + GB_STATIC_METHOD("TexCoord1i", NULL, GLTEXCOORD1I, "(S)i"), + GB_STATIC_METHOD("TexCoord2i", NULL, GLTEXCOORD2I, "(S)i(T)i"), + GB_STATIC_METHOD("TexCoord3i", NULL, GLTEXCOORD3I, "(S)i(T)i(R)i"), + GB_STATIC_METHOD("TexCoord4i", NULL, GLTEXCOORDI, "(S)i(T)i(R)i(Q)i"), + GB_STATIC_METHOD("TexCoordi", NULL, GLTEXCOORDI, "(S)i[(T)i(R)i(Q)i]"), + GB_STATIC_METHOD("TexEnvf", NULL, GLTEXENVF, "(Target)i(Pname)i(Param)f"), + GB_STATIC_METHOD("TexEnvfv", NULL, GLTEXENVFV, "(Target)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("TexEnvi", NULL, GLTEXENVI, "(Target)i(Pname)i(Param)i"), + GB_STATIC_METHOD("TexEnviv", NULL, GLTEXENVIV, "(Target)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("TexImage1D", NULL, GLTEXIMAGE1D, "(Image)Image;[(Level)i(Border)i]"), + GB_STATIC_METHOD("TexImage2D", NULL, GLTEXIMAGE2D,"(Image)Image;[(Level)i(Border)i]"), + GB_STATIC_METHOD("TexSubImage1D", NULL, GLTEXSUBIMAGE2D,"(Image)Image;(XOffset)i(Width)i[(Level)i]"), + GB_STATIC_METHOD("TexSubImage2D", NULL, GLTEXSUBIMAGE2D,"(Image)Image;(XOffset)i(YOffset)i(Width)i(Height)i[(Level)i]"), + GB_STATIC_METHOD("TexParameterf", NULL, GLTEXPARAMETERF, "(Target)i(Pname)i(Param)f"), + GB_STATIC_METHOD("TexParameterfv", NULL, GLTEXPARAMETERFV, "(Target)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("TexParameteri", NULL, GLTEXPARAMETERI, "(Target)i(Pname)i(Param)i"), + GB_STATIC_METHOD("TexParameteriv", NULL, GLTEXPARAMETERIV, "(Target)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("TexGeni", NULL, GLTEXGENI, "(Coord)i(Pname)i(Param)i"), + GB_STATIC_METHOD("MultiTexCoord2f", NULL, GLMULTITEXCOORD2F, "(Target)i(S)f(T)f"), + + /* FrameBuffer Operations - see GLframeBufferOps.h */ + GB_STATIC_METHOD("Accum", NULL, GLACCUM, "(Operation)i(Value)f"), + GB_STATIC_METHOD("AlphaFunc", NULL, GLALPHAFUNC, "(Function)i(Reference)f"), + GB_STATIC_METHOD("BlendFunc", NULL, GLBLENDFUNC, "(SrcFactor)i(DstFactor)i"), + GB_STATIC_METHOD("Clear", NULL, GLCLEAR, "(Mask)i"), + GB_STATIC_METHOD("ClearAccum", NULL, GLCLEARACCUM, "(Red)f(Green)f(Blue)f(Alpha)f"), + GB_STATIC_METHOD("ClearColor", NULL, GLCLEARCOLOR, "(Red)f(Green)f(Blue)f(Alpha)f"), + GB_STATIC_METHOD("ClearDepth", NULL, GLCLEARDEPTH, "(Depth)f"), + GB_STATIC_METHOD("ClearIndex", NULL, GLCLEARINDEX, "(Value)f"), + GB_STATIC_METHOD("ClearStencil", NULL, GLCLEARSTENCIL, "(Value)i"), + GB_STATIC_METHOD("ColorMask", NULL, GLCOLORMASK, "(Red)b(Green)b(Blue)b(Alpha)b"), + GB_STATIC_METHOD("DrawBuffer", NULL, GLDRAWBUFFER, "(Mode)i"), + GB_STATIC_METHOD("DepthFunc", NULL, GLDEPTHFUNC, "(Function)i"), + GB_STATIC_METHOD("DepthMask", NULL, GLDEPTHMASK, "(Flag)b"), + GB_STATIC_METHOD("IndexMask", NULL, GLINDEXMASK, "(Mask)i"), + GB_STATIC_METHOD("LogicOp", NULL, GLLOGICOP, "(Opcode)i"), + GB_STATIC_METHOD("Scissor", NULL, GLSCISSOR, "(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("StencilFunc", NULL, GLSTENCILFUNC, "(Function)i(Reference)i(Mask)i"), + GB_STATIC_METHOD("StencilMask", NULL, GLSTENCILMASK, "(Mask)i"), + GB_STATIC_METHOD("StencilOp", NULL, GLSTENCILOP, "(Fail)i(Zfail)i(Zpass)i"), + + /* Selection and Feedback - see GLselectPixmap.h */ + GB_STATIC_METHOD("FeedbackBuffer", NULL, GLFEEDBACKBUFFER, "(Type)i"), + GB_STATIC_METHOD("InitNames", NULL, GLINITNAMES, NULL), + GB_STATIC_METHOD("LoadName", NULL, GLLOADNAME, "(Name)i"), + GB_STATIC_METHOD("PassThrough", NULL, GLPASSTHROUGH, "(Token)f"), + GB_STATIC_METHOD("PopName", NULL, GLPOPNAME, NULL), + GB_STATIC_METHOD("PushName", NULL, GLPUSHNAME, "(Name)i"), + GB_STATIC_METHOD("RenderMode", "Array", GLRENDERMODE, "(Mode)i"), + GB_STATIC_METHOD("SelectBuffer", NULL, GLSELECTBUFFER, NULL), + + /* Evaluators - see Gleval.h/c */ + GB_STATIC_METHOD("Map1f", NULL, GLMAP1F, "(Target)i(U1)f(U2)f(Stride)i(Order)i(Points)Float[]"), + GB_STATIC_METHOD("Map2f", NULL, GLMAP2F, "(Target)i(U1)f(U2)f(Ustride)i(Uorder)i(V1)f(V2)f(Vstride)i(Vorder)i(Points)Float[]"), + GB_STATIC_METHOD("EvalCoord1f", NULL, GLEVALCOORD1F, "(U)f"), + GB_STATIC_METHOD("EvalCoord2f", NULL, GLEVALCOORD2F, "(U)f(V)f"), + GB_STATIC_METHOD("EvalCoord2fv", NULL, GLEVALCOORD2FV, "(Coords)Float[]"), + GB_STATIC_METHOD("MapGrid1f", NULL, GLMAPGRID1F, "(Un)i(U)f(V)f"), + GB_STATIC_METHOD("MapGrid2f", NULL, GLMAPGRID2F, "(Un)i(U1)f(U2)f(Vn)i(V1)f(V2)f"), + GB_STATIC_METHOD("EvalPoint1", NULL, GLEVALPOINT1, "(I)i"), + GB_STATIC_METHOD("EvalPoint2", NULL, GLEVALPOINT2, "(I)i(J)i"), + GB_STATIC_METHOD("EvalMesh1", NULL, GLEVALMESH1, "(Mode)i(I1)i(I2)i"), + GB_STATIC_METHOD("EvalMesh2", NULL, GLEVALMESH2, "(Mode)i(I1)i(I2)i(J1)i(J2)i"), + + /* glGetxxxx calls - see GLinfo.h/c */ + GB_STATIC_METHOD("GetBooleanv", "Boolean[]", GLGETBOOLEANV, "(Parameter)i"), + GB_STATIC_METHOD("GetFloatv", "Float[]", GLGETFLOATV, "(Parameter)i"), + GB_STATIC_METHOD("GetIntegerv", "Integer[]", GLGETINTEGERV, "(Parameter)i]"), + GB_STATIC_METHOD("GetString", "s", GLGETSTRING, "(Name)i"), + + /*********************/ + /* Opengl Extensions */ + /*********************/ + /* See http://www.opengl.org/registry/ */ + + /* ARB extensions */ + + /* 38; GL_ARB_texture_rectangle */ + /* Only constants */ + + /* EXT and Vendors extensions */ + + /* 310; GL_EXT_framebuffer_object */ + /* framebufferobject.c */ + GB_STATIC_METHOD("BindFramebufferEXT", NULL, GLBINDFRAMEBUFFEREXT, "(Target)i(Framebuffer)i"), + GB_STATIC_METHOD("BindRenderbufferEXT",NULL, GLBINDRENDERBUFFEREXT, "(Target)i(Renderbuffer)i"), + GB_STATIC_METHOD("CheckFramebufferStatusEXT", "i", GLCHECKFRAMEBUFFERSTATUSEXT, "(Target)i"), + GB_STATIC_METHOD("DeleteFramebuffersEXT", NULL, GLDELETEFRAMEBUFFERSEXT, "(Framebuffers)Integer[]"), + GB_STATIC_METHOD("DeleteRenderbuffersEXT", NULL, GLDELETERENDERBUFFERSEXT, "(Renderbuffers)Integer[]"), + GB_STATIC_METHOD("FramebufferRenderbufferEXT", NULL, GLFRAMEBUFFERRENDERBUFFEREXT, "(Target)i(Attachment)i(RenderbufferTarget)i(Renderbuffer)i"), + GB_STATIC_METHOD("FramebufferTexture1DEXT", NULL, GLFRAMEBUFFERTEXTURE1DEXT, "(Target)i(Attachment)i(TexTarget)i(Texture)i(Level)i"), + GB_STATIC_METHOD("FramebufferTexture2DEXT", NULL, GLFRAMEBUFFERTEXTURE2DEXT, "(Target)i(Attachment)i(TexTarget)i(Texture)i(Level)i"), + GB_STATIC_METHOD("FramebufferTexture3DEXT", NULL, GLFRAMEBUFFERTEXTURE3DEXT, "(Target)i(Attachment)i(TexTarget)i(Texture)i(Level)i(Zoffset)i"), + GB_STATIC_METHOD("GenerateMipmapEXT", NULL, GLGENERATEMIPMAPEXT, "(Target)i"), + GB_STATIC_METHOD("GenFramebuffersEXT", "Integer[]", GLGENFRAMEBUFFERSEXT, "(Count)i"), + GB_STATIC_METHOD("GenRenderbuffersEXT", "Integer[]", GLGENRENDERBUFFERSEXT, "(Count)i"), + GB_STATIC_METHOD("GetFramebufferAttachmentParameterivEXT", "Integer[]", GLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXT, "(Target)i(Attachment)i(Pname)i"), + GB_STATIC_METHOD("GetRenderbufferParameterivEXT", "Integer[]", GLGETRENDERBUFFERPARAMETERIVEXT, "(Target)i(Pname)i"), + GB_STATIC_METHOD("IsFramebufferEXT", "b", GLISFRAMEBUFFEREXT, "(Framebuffer)i"), + GB_STATIC_METHOD("IsRenderbufferEXT", "b", GLISRENDERBUFFEREXT, "(Renderbuffer)i"), + GB_STATIC_METHOD("RenderbufferStorageEXT", NULL, GLRENDERBUFFERSTORAGEEXT, "(Target)i(InternalFormat)i(Width)i(Height)i"), + + // Old constant naming support + GB_STATIC_METHOD("_unknown", "v", GL_unknown, "."), + GB_STATIC_METHOD("_property", "b", GL_property, NULL), + + /********************/ + /* opengl constants */ + /********************/ + + GB_CONSTANT("FLOAT" , "i", GL_FLOAT), + GB_CONSTANT("INTEGER" , "i", GL_FLOAT), + GB_CONSTANT("UNSIGNED_BYTE", "i", GL_UNSIGNED_BYTE), + GB_CONSTANT("UNSIGNED_INT", "i", GL_UNSIGNED_INT), + GB_CONSTANT("DOUBLE", "i", GL_DOUBLE), + + /* GetString */ + GB_CONSTANT("EXTENSIONS", "i", GL_EXTENSIONS), + GB_CONSTANT("VENDOR", "i", GL_VENDOR), + GB_CONSTANT("RENDERER", "i", GL_RENDERER), + GB_CONSTANT("VERSION", "i", GL_VERSION), + GB_CONSTANT("SHADING_LANGUAGE_VERSION", "i", GL_SHADING_LANGUAGE_VERSION), + + /* Primitives */ + GB_CONSTANT("POINTS", "i", GL_POINTS), + GB_CONSTANT("LINES", "i", GL_LINES), + GB_CONSTANT("LINE_LOOP", "i", GL_LINE_LOOP), + GB_CONSTANT("LINE_STRIP", "i", GL_LINE_STRIP), + GB_CONSTANT("TRIANGLES", "i", GL_TRIANGLES), + GB_CONSTANT("TRIANGLE_STRIP", "i", GL_TRIANGLE_STRIP), + GB_CONSTANT("TRIANGLE_FAN", "i", GL_TRIANGLE_FAN), + GB_CONSTANT("QUADS", "i", GL_QUADS), + GB_CONSTANT("QUAD_STRIP", "i", GL_QUAD_STRIP), + GB_CONSTANT("POLYGON", "i", GL_POLYGON), + GB_CONSTANT("VERTEX_ARRAY", "i", GL_VERTEX_ARRAY), + GB_CONSTANT("COLOR_ARRAY", "i", GL_COLOR_ARRAY), + GB_CONSTANT("NORMAL_ARRAY", "i", GL_NORMAL_ARRAY), + GB_CONSTANT("TEXTURE_COORD_ARRAY", "i", GL_TEXTURE_COORD_ARRAY), + + /* Matrix Mode */ + GB_CONSTANT("MATRIX_MODE", "i", GL_MATRIX_MODE), + GB_CONSTANT("MODELVIEW", "i", GL_MODELVIEW), + GB_CONSTANT("PROJECTION", "i", GL_PROJECTION), + GB_CONSTANT("TEXTURE", "i", GL_TEXTURE), + + /* Points */ + GB_CONSTANT("POINT_SMOOTH", "i", GL_POINT_SMOOTH), + GB_CONSTANT("POINT_SIZE", "i", GL_POINT_SIZE), + GB_CONSTANT("SMOOTH_POINT_SIZE_GRANULARITY", "i", GL_SMOOTH_POINT_SIZE_GRANULARITY), + GB_CONSTANT("SMOOTH_POINT_SIZE_RANGE", "i", GL_SMOOTH_POINT_SIZE_RANGE), + + /* Display Lists */ + GB_CONSTANT("COMPILE", "i", GL_COMPILE), + GB_CONSTANT("COMPILE_AND_EXECUTE", "i", GL_COMPILE_AND_EXECUTE), + GB_CONSTANT("LIST_BASE", "i", GL_LIST_BASE), + GB_CONSTANT("LIST_INDEX", "i", GL_LIST_INDEX), + GB_CONSTANT("LIST_MODE" ,"i", GL_LIST_MODE), + + /* Accumulation buffer */ + GB_CONSTANT("ACCUM_", "i", GL_ACCUM), + GB_CONSTANT("ACCUM_ALPHA_BITS", "i", GL_ACCUM_ALPHA_BITS), + GB_CONSTANT("ACCUM_CLEAR_VALUE", "i", GL_ACCUM_CLEAR_VALUE), + GB_CONSTANT("ACCUM_BLUE_BITS", "i", GL_ACCUM_BLUE_BITS), + GB_CONSTANT("ACCUM_GREEN_BITS", "i", GL_ACCUM_GREEN_BITS), + GB_CONSTANT("ACCUM_RED_BITS", "i", GL_ACCUM_RED_BITS), + GB_CONSTANT("ADD", "i", GL_ADD), + GB_CONSTANT("LOAD", "i", GL_LOAD), + GB_CONSTANT("MULT", "i", GL_MULT), + GB_CONSTANT("RETURN", "i", GL_RETURN), + + /* User clipping planes */ + GB_CONSTANT("CLIP_PLANE0", "i", GL_CLIP_PLANE0), + GB_CONSTANT("CLIP_PLANE1", "i", GL_CLIP_PLANE1), + GB_CONSTANT("CLIP_PLANE2", "i", GL_CLIP_PLANE2), + GB_CONSTANT("CLIP_PLANE3", "i", GL_CLIP_PLANE3), + GB_CONSTANT("CLIP_PLANE4", "i", GL_CLIP_PLANE4), + GB_CONSTANT("CLIP_PLANE5", "i", GL_CLIP_PLANE5), + + /* Alpha testing */ + GB_CONSTANT("ALPHA_TEST", "i", GL_ALPHA_TEST), + GB_CONSTANT("ALPHA_TEST_REF", "i", GL_ALPHA_TEST_REF), + GB_CONSTANT("ALPHA_TEST_FUNC", "i", GL_ALPHA_TEST_FUNC), + + /* glPush/PopAttrib bits */ + GB_CONSTANT("ACCUM_BUFFER_BIT", "i", GL_ACCUM_BUFFER_BIT), + GB_CONSTANT("ALL_ATTRIB_BITS", "i", GL_ALL_ATTRIB_BITS), + GB_CONSTANT("CURRENT_BIT", "i", GL_CURRENT_BIT), + GB_CONSTANT("COLOR_BUFFER_BIT", "i", GL_COLOR_BUFFER_BIT), + GB_CONSTANT("DEPTH_BUFFER_BIT", "i", GL_DEPTH_BUFFER_BIT), + GB_CONSTANT("ENABLE_BIT", "i", GL_ENABLE_BIT), + GB_CONSTANT("EVAL_BIT", "i", GL_EVAL_BIT), + GB_CONSTANT("FOG_BIT", "i", GL_FOG_BIT), + GB_CONSTANT("HINT_BIT", "i", GL_HINT_BIT), + GB_CONSTANT("LIGHTING_BIT", "i", GL_LIGHTING_BIT), + GB_CONSTANT("LINE_BIT", "i", GL_LINE_BIT), + GB_CONSTANT("LIST_BIT", "i", GL_LIST_BIT), + GB_CONSTANT("PIXEL_MODE_BIT", "i", GL_PIXEL_MODE_BIT), + GB_CONSTANT("POINT_BIT", "i", GL_POINT_BIT), + GB_CONSTANT("POLYGON_BIT", "i", GL_POLYGON_BIT), + GB_CONSTANT("POLYGON_STIPPLE_BIT", "i", GL_POLYGON_STIPPLE_BIT), + GB_CONSTANT("SCISSOR_BIT", "i", GL_SCISSOR_BIT), + GB_CONSTANT("STENCIL_BUFFER_BIT", "i", GL_STENCIL_BUFFER_BIT), + GB_CONSTANT("TEXTURE_BIT", "i", GL_TEXTURE_BIT), + GB_CONSTANT("TRANSFORM_BIT", "i", GL_TRANSFORM_BIT), + GB_CONSTANT("VIEWPORT_BIT", "i", GL_VIEWPORT_BIT), + + /* Polygons */ + GB_CONSTANT("POINT", "i", GL_POINT), + GB_CONSTANT("LINE", "i", GL_LINE), + GB_CONSTANT("FILL", "i", GL_FILL), + GB_CONSTANT("CW", "i", GL_CW), + GB_CONSTANT("CCW", "i", GL_CCW), + GB_CONSTANT("FRONT", "i", GL_FRONT), + GB_CONSTANT("BACK", "i", GL_BACK), + GB_CONSTANT("POLYGON_MODE", "i", GL_POLYGON_MODE), + GB_CONSTANT("POLYGON_SMOOTH", "i", GL_POLYGON_SMOOTH), + GB_CONSTANT("POLYGON_STIPPLE", "i", GL_POLYGON_STIPPLE), + GB_CONSTANT("EDGE_FLAG", "i", GL_EDGE_FLAG), + GB_CONSTANT("CULL_FACE", "i", GL_CULL_FACE), + GB_CONSTANT("CULL_FACE_MODE", "i", GL_CULL_FACE_MODE), + GB_CONSTANT("FRONT_FACE", "i", GL_FRONT_FACE), + GB_CONSTANT("POLYGON_OFFSET_FACTOR", "i", GL_POLYGON_OFFSET_FACTOR), + GB_CONSTANT("POLYGON_OFFSET_UNITS", "i", GL_POLYGON_OFFSET_UNITS), + GB_CONSTANT("POLYGON_OFFSET_POINT", "i", GL_POLYGON_OFFSET_POINT), + GB_CONSTANT("POLYGON_OFFSET_LINE", "i", GL_POLYGON_OFFSET_LINE), + GB_CONSTANT("POLYGON_OFFSET_FILL", "i", GL_POLYGON_OFFSET_FILL), + + /* Depth buffer */ + GB_CONSTANT("NEVER", "i", GL_NEVER), + GB_CONSTANT("LESS", "i", GL_LESS), + GB_CONSTANT("EQUAL", "i", GL_EQUAL), + GB_CONSTANT("LEQUAL", "i", GL_LEQUAL), + GB_CONSTANT("GREATER", "i", GL_GREATER), + GB_CONSTANT("NOTEQUAL", "i", GL_NOTEQUAL), + GB_CONSTANT("GEQUAL", "i", GL_GEQUAL), + GB_CONSTANT("ALWAYS", "i", GL_ALWAYS), + GB_CONSTANT("DEPTH_TEST", "i", GL_DEPTH_TEST), + GB_CONSTANT("DEPTH_BITS", "i", GL_DEPTH_BITS), + GB_CONSTANT("DEPTH_CLEAR_VALUE", "i", GL_DEPTH_CLEAR_VALUE), + GB_CONSTANT("DEPTH_FUNC", "i", GL_DEPTH_FUNC), + GB_CONSTANT("DEPTH_RANGE", "i", GL_DEPTH_RANGE), + GB_CONSTANT("DEPTH_WRITEMASK", "i", GL_DEPTH_WRITEMASK), + GB_CONSTANT("DEPTH_COMPONENT", "i", GL_DEPTH_COMPONENT), + + /* Lines */ + GB_CONSTANT("LINE_SMOOTH", "i", GL_LINE_SMOOTH), + GB_CONSTANT("LINE_STIPPLE", "i", GL_LINE_STIPPLE), + GB_CONSTANT("LINE_STIPPLE_PATTERN", "i", GL_LINE_STIPPLE_PATTERN), + GB_CONSTANT("LINE_STIPPLE_REPEAT", "i", GL_LINE_STIPPLE_REPEAT), + GB_CONSTANT("LINE_WIDTH", "i", GL_LINE_WIDTH), + GB_CONSTANT("SMOOTH_LINE_WIDTH_GRANULARITY", "i", GL_SMOOTH_LINE_WIDTH_GRANULARITY), + GB_CONSTANT("SMOOTH_LINE_WIDTH_RANGE", "i", GL_SMOOTH_LINE_WIDTH_RANGE), + + /* Render Mode */ + GB_CONSTANT("FEEDBACK", "i", GL_FEEDBACK), + GB_CONSTANT("RENDER", "i", GL_RENDER), + GB_CONSTANT("SELECT", "i", GL_SELECT), + + /* Feedback */ + GB_CONSTANT("_2D", "i", GL_2D), + GB_CONSTANT("_3D", "i", GL_3D), + GB_CONSTANT("_3D_COLOR", "i", GL_3D_COLOR), + GB_CONSTANT("_3D_COLOR_TEXTURE", "i", GL_3D_COLOR_TEXTURE), + GB_CONSTANT("_4D_COLOR_TEXTURE", "i", GL_4D_COLOR_TEXTURE), + GB_CONSTANT("POINT_TOKEN", "i", GL_POINT_TOKEN), + GB_CONSTANT("LINE_TOKEN", "i", GL_LINE_TOKEN), + GB_CONSTANT("LINE_RESET_TOKEN", "i", GL_LINE_RESET_TOKEN), + GB_CONSTANT("POLYGON_TOKEN", "i", GL_POLYGON_TOKEN), + GB_CONSTANT("BITMAP_TOKEN", "i", GL_BITMAP_TOKEN), + GB_CONSTANT("DRAW_PIXEL_TOKEN", "i", GL_DRAW_PIXEL_TOKEN), + GB_CONSTANT("COPY_PIXEL_TOKEN", "i", GL_COPY_PIXEL_TOKEN), + GB_CONSTANT("PASS_THROUGH_TOKEN", "i", GL_PASS_THROUGH_TOKEN), + GB_CONSTANT("FEEDBACK_BUFFER_POINTER", "i", GL_FEEDBACK_BUFFER_POINTER), + GB_CONSTANT("FEEDBACK_BUFFER_SIZE", "i", GL_FEEDBACK_BUFFER_SIZE), + GB_CONSTANT("FEEDBACK_BUFFER_TYPE", "i", GL_FEEDBACK_BUFFER_TYPE), + + /* Selection */ + GB_CONSTANT("SELECTION_BUFFER_POINTER", "i", GL_SELECTION_BUFFER_POINTER), + GB_CONSTANT("SELECTION_BUFFER_SIZE", "i", GL_SELECTION_BUFFER_SIZE), + + /* Fog */ + GB_CONSTANT("FOG", "i", GL_FOG), + GB_CONSTANT("FOG_MODE", "i", GL_FOG_MODE), + GB_CONSTANT("FOG_DENSITY", "i", GL_FOG_DENSITY), + GB_CONSTANT("FOG_COLOR", "i", GL_FOG_COLOR), + GB_CONSTANT("FOG_INDEX", "i", GL_FOG_INDEX), + GB_CONSTANT("FOG_START", "i", GL_FOG_START), + GB_CONSTANT("FOG_END", "i", GL_FOG_END), + GB_CONSTANT("LINEAR", "i", GL_LINEAR), + GB_CONSTANT("EXP", "i", GL_EXP), + GB_CONSTANT("EXP2", "i", GL_EXP2), + + /* Errors */ + GB_CONSTANT("NO_ERROR", "i", GL_NO_ERROR), + GB_CONSTANT("INVALID_VALUE", "i", GL_INVALID_VALUE), + GB_CONSTANT("INVALID_ENUM", "i", GL_INVALID_ENUM), + GB_CONSTANT("INVALID_OPERATION", "i", GL_INVALID_OPERATION), + GB_CONSTANT("STACK_OVERFLOW", "i", GL_STACK_OVERFLOW), + GB_CONSTANT("STACK_UNDERFLOW", "i", GL_STACK_UNDERFLOW), + GB_CONSTANT("OUT_OF_MEMORY", "i", GL_OUT_OF_MEMORY), + + /* Lighting */ + GB_CONSTANT("LIGHTING", "i", GL_LIGHTING), + GB_CONSTANT("LIGHT0", "i", GL_LIGHT0), + GB_CONSTANT("LIGHT1", "i", GL_LIGHT1), + GB_CONSTANT("LIGHT2", "i", GL_LIGHT2), + GB_CONSTANT("LIGHT3", "i", GL_LIGHT3), + GB_CONSTANT("LIGHT4", "i", GL_LIGHT4), + GB_CONSTANT("LIGHT5", "i", GL_LIGHT5), + GB_CONSTANT("LIGHT6", "i", GL_LIGHT6), + GB_CONSTANT("LIGHT7", "i", GL_LIGHT7), + GB_CONSTANT("SPOT_EXPONENT", "i", GL_SPOT_EXPONENT), + GB_CONSTANT("SPOT_CUTTOFF", "i", GL_SPOT_CUTOFF), + GB_CONSTANT("CONSTANT_ATTENUATION", "i", GL_CONSTANT_ATTENUATION), + GB_CONSTANT("LINEAR_ATTENUATION", "i", GL_LINEAR_ATTENUATION), + GB_CONSTANT("QUADRATIC_ATTENUATION", "i", GL_QUADRATIC_ATTENUATION), + GB_CONSTANT("AMBIENT", "i", GL_AMBIENT), + GB_CONSTANT("DIFFUSE", "i", GL_DIFFUSE), + GB_CONSTANT("SPECULAR", "i", GL_SPECULAR), + GB_CONSTANT("SHININESS", "i", GL_SHININESS), + GB_CONSTANT("EMISSION", "i", GL_EMISSION), + GB_CONSTANT("POSITION", "i", GL_POSITION), + GB_CONSTANT("SPOT_DIRECTION", "i", GL_SPOT_DIRECTION), + GB_CONSTANT("AMBIENT_AND_DIFFUSE", "i", GL_AMBIENT_AND_DIFFUSE), + GB_CONSTANT("COLOR_INDEXES", "i", GL_COLOR_INDEXES), + GB_CONSTANT("LIGHT_MODEL_TWO_SIDE", "i", GL_LIGHT_MODEL_TWO_SIDE), + GB_CONSTANT("LIGHT_MODEL_LOCAL_VIEWER", "i", GL_LIGHT_MODEL_LOCAL_VIEWER), + GB_CONSTANT("LIGHT_MODEL_AMBIENT", "i", GL_LIGHT_MODEL_AMBIENT), + GB_CONSTANT("FRONT_AND_BACK", "i", GL_FRONT_AND_BACK), + GB_CONSTANT("SHADE_MODEL", "i", GL_SHADE_MODEL), + GB_CONSTANT("FLAT", "i", GL_FLAT), + GB_CONSTANT("SMOOTH", "i", GL_SMOOTH), + GB_CONSTANT("COLOR_MATERIAL", "i", GL_COLOR_MATERIAL), + GB_CONSTANT("COLOR_MATERIAL_FACE", "i", GL_COLOR_MATERIAL_FACE), + GB_CONSTANT("COLOR_MATERIAL_PARAMETER", "i", GL_COLOR_MATERIAL_PARAMETER), + GB_CONSTANT("NORMALIZE", "i", GL_NORMALIZE), + + /* Blending */ + GB_CONSTANT("BLEND", "i", GL_BLEND), + GB_CONSTANT("BLEND_SRC", "i", GL_BLEND_SRC), + GB_CONSTANT("BLEND_DST", "i", GL_BLEND_DST), + GB_CONSTANT("ZERO", "i", GL_ZERO), + GB_CONSTANT("ONE", "i", GL_ONE), + GB_CONSTANT("SRC_COLOR", "i", GL_SRC_COLOR), + GB_CONSTANT("ONE_MINUS_SRC_COLOR", "i", GL_ONE_MINUS_SRC_COLOR), + GB_CONSTANT("SRC_ALPHA", "i", GL_SRC_ALPHA), + GB_CONSTANT("ONE_MINUS_SRC_ALPHA", "i", GL_ONE_MINUS_SRC_ALPHA), + GB_CONSTANT("DST_ALPHA", "i", GL_DST_ALPHA), + GB_CONSTANT("ONE_MINUS_DST_ALPHA", "i", GL_ONE_MINUS_DST_ALPHA), + GB_CONSTANT("DST_COLOR", "i", GL_DST_COLOR), + GB_CONSTANT("ONE_MINUS_DST_COLOR", "i", GL_ONE_MINUS_DST_COLOR), + GB_CONSTANT("SRC_ALPHA_SATURATE", "i", GL_SRC_ALPHA_SATURATE), + + /* Logic Ops */ + GB_CONSTANT("LOGIC_OP", "i", GL_LOGIC_OP), + GB_CONSTANT("INDEX_LOGIC_OP", "i", GL_INDEX_LOGIC_OP), + GB_CONSTANT("COLOR_LOGIC_OP", "i", GL_COLOR_LOGIC_OP), + GB_CONSTANT("LOGIC_OP_MODE", "i", GL_LOGIC_OP_MODE), + GB_CONSTANT("CLEAR_", "i", GL_CLEAR), + GB_CONSTANT("SET", "i", GL_SET), + GB_CONSTANT("COPY", "i", GL_COPY), + GB_CONSTANT("COPY_INVERTED", "i", GL_COPY_INVERTED), + GB_CONSTANT("NOOP", "i", GL_NOOP), + GB_CONSTANT("INVERT", "i", GL_INVERT), + GB_CONSTANT("AND", "i", GL_AND), + GB_CONSTANT("NAND", "i", GL_NAND), + GB_CONSTANT("OR", "i", GL_OR), + GB_CONSTANT("NOR", "i", GL_NOR), + GB_CONSTANT("XOR", "i", GL_XOR), + GB_CONSTANT("EQUIV", "i", GL_EQUIV), + GB_CONSTANT("AND_REVERSE", "i", GL_AND_REVERSE), + GB_CONSTANT("AND_INVERTED", "i", GL_AND_INVERTED), + GB_CONSTANT("OR_REVERSE", "i", GL_OR_REVERSE), + GB_CONSTANT("OR_INVERTED", "i", GL_OR_INVERTED), + + /* Stencil */ + GB_CONSTANT("STENCIL_BITS", "i", GL_STENCIL_BITS), + GB_CONSTANT("STENCIL_TEST", "i", GL_STENCIL_TEST), + GB_CONSTANT("STENCIL_CLEAR_VALUE", "i", GL_STENCIL_CLEAR_VALUE), + GB_CONSTANT("STENCIL_FUNC", "i", GL_STENCIL_FUNC), + GB_CONSTANT("STENCIL_VALUE_MASK", "i", GL_STENCIL_VALUE_MASK), + GB_CONSTANT("STENCIL_FAIL", "i", GL_STENCIL_FAIL), + GB_CONSTANT("STENCIL_PASS_DEPTH_FAIL", "i", GL_STENCIL_PASS_DEPTH_FAIL), + GB_CONSTANT("STENCIL_PASS_DEPTH_PASS", "i", GL_STENCIL_PASS_DEPTH_PASS), + GB_CONSTANT("STENCIL_REF", "i", GL_STENCIL_REF), + GB_CONSTANT("STENCIL_WRITEMASK", "i", GL_STENCIL_WRITEMASK), + GB_CONSTANT("STENCIL_INDEX", "i", GL_STENCIL_INDEX), + GB_CONSTANT("KEEP", "i", GL_KEEP), + GB_CONSTANT("REPLACE", "i", GL_REPLACE), + GB_CONSTANT("INCR", "i", GL_INCR), + GB_CONSTANT("DECR", "i", GL_DECR), + + /* Buffers, Pixel Drawing/Reading */ + GB_CONSTANT("AUX0", "i", GL_AUX0), + GB_CONSTANT("AUX1", "i", GL_AUX1), + GB_CONSTANT("AUX2", "i", GL_AUX2), + GB_CONSTANT("AUX3", "i", GL_AUX3), + GB_CONSTANT("BACK_LEFT", "i", GL_BACK_LEFT), + GB_CONSTANT("BACK_RIGHT", "i", GL_BACK_RIGHT), + GB_CONSTANT("FRONT_LEFT", "i", GL_FRONT_LEFT), + GB_CONSTANT("FRONT_RIGHT", "i", GL_FRONT_RIGHT), + GB_CONSTANT("LEFT", "i", GL_LEFT), + GB_CONSTANT("NONE", "i", GL_NONE), + GB_CONSTANT("RIGHT", "i", GL_RIGHT), + GB_CONSTANT("COLOR_INDEX", "i", GL_COLOR_INDEX), + GB_CONSTANT("RED", "i", GL_RED), + GB_CONSTANT("GREEN", "i", GL_GREEN), + GB_CONSTANT("BLUE", "i", GL_BLUE), + GB_CONSTANT("ALPHA", "i", GL_ALPHA), + GB_CONSTANT("LUMINANCE", "i", GL_LUMINANCE), + GB_CONSTANT("LUMINANCE_ALPHA", "i", GL_LUMINANCE_ALPHA), + GB_CONSTANT("ALPHA_BITS", "i", GL_ALPHA_BITS), + GB_CONSTANT("RED_BITS", "i", GL_RED_BITS), + GB_CONSTANT("GREEN_BITS", "i", GL_GREEN_BITS), + GB_CONSTANT("BLUE_BITS", "i", GL_BLUE_BITS), + GB_CONSTANT("INDEX_BITS", "i", GL_INDEX_BITS), + GB_CONSTANT("SUBPIXEL_BITS", "i", GL_SUBPIXEL_BITS), + GB_CONSTANT("AUX_BUFFERS", "i", GL_AUX_BUFFERS), + GB_CONSTANT("READ_BUFFER", "i", GL_READ_BUFFER), + GB_CONSTANT("DRAW_BUFFER", "i", GL_DRAW_BUFFER), + GB_CONSTANT("DOUBLEBUFFER", "i", GL_DOUBLEBUFFER), + GB_CONSTANT("STEREO", "i", GL_STEREO), + GB_CONSTANT("BITMAP_", "i", GL_BITMAP), + GB_CONSTANT("COLOR", "i", GL_COLOR), + GB_CONSTANT("DEPTH", "i", GL_DEPTH), + GB_CONSTANT("STENCIL", "i", GL_STENCIL), + GB_CONSTANT("DITHER", "i", GL_DITHER), + GB_CONSTANT("RGB", "i", GL_RGB), + GB_CONSTANT("RGBA", "i", GL_RGBA), + GB_CONSTANT("BGR", "i", GL_BGR), + GB_CONSTANT("BGRA", "i", GL_BGRA), + + /* Implementation limits */ + GB_CONSTANT("MAX_LIST_NESTING", "i", GL_MAX_LIST_NESTING), + GB_CONSTANT("MAX_ATTRIB_STACK_DEPTH", "i", GL_MAX_ATTRIB_STACK_DEPTH), + GB_CONSTANT("MAX_NAME_STACK_DEPTH", "i", GL_MAX_NAME_STACK_DEPTH), + GB_CONSTANT("MAX_PROJECTION_STACK_DEPTH", "i", GL_MAX_PROJECTION_STACK_DEPTH), + GB_CONSTANT("MAX_TEXTURE_STACK_DEPTH", "i", GL_MAX_TEXTURE_STACK_DEPTH), + GB_CONSTANT("MAX_EVAL_ORDER", "i", GL_MAX_EVAL_ORDER), + GB_CONSTANT("MAX_LIGHTS", "i", GL_MAX_LIGHTS), + GB_CONSTANT("MAX_CLIP_PLANES", "i", GL_MAX_CLIP_PLANES), + GB_CONSTANT("MAX_TEXTURE_SIZE", "i", GL_MAX_TEXTURE_SIZE), + GB_CONSTANT("MAX_PIXEL_MAP_TABLE", "i", GL_MAX_PIXEL_MAP_TABLE), + GB_CONSTANT("MAX_VIEWPORT_DIMS", "i", GL_MAX_VIEWPORT_DIMS), + GB_CONSTANT("MAX_CLIENT_ATTRIB_STACK_DEPTH", "i", GL_MAX_CLIENT_ATTRIB_STACK_DEPTH), + GB_CONSTANT("MAX_MODELVIEW_STACK_DEPTH", "i", GL_MAX_MODELVIEW_STACK_DEPTH), + + /* Gets */ + GB_CONSTANT("ATTRIB_STACK_DEPTH", "i", GL_ATTRIB_STACK_DEPTH), + GB_CONSTANT("CLIENT_ATTRIB_STACK_DEPTH", "i", GL_CLIENT_ATTRIB_STACK_DEPTH), + GB_CONSTANT("COLOR_CLEAR_VALUE", "i", GL_COLOR_CLEAR_VALUE), + GB_CONSTANT("COLOR_WRITEMASK", "i", GL_COLOR_WRITEMASK), + GB_CONSTANT("CURRENT_INDEX", "i", GL_CURRENT_INDEX), + GB_CONSTANT("CURRENT_COLOR", "i", GL_CURRENT_COLOR), + GB_CONSTANT("CURRENT_NORMAL", "i", GL_CURRENT_NORMAL), + GB_CONSTANT("CURRENT_RASTER_COLOR", "i", GL_CURRENT_RASTER_COLOR), + GB_CONSTANT("CURRENT_RASTER_DISTANCE", "i", GL_CURRENT_RASTER_DISTANCE), + GB_CONSTANT("CURRENT_RASTER_INDEX", "i", GL_CURRENT_RASTER_INDEX), + GB_CONSTANT("CURRENT_RASTER_POSITION", "i", GL_CURRENT_RASTER_POSITION), + GB_CONSTANT("CURRENT_RASTER_TEXTURE_COORDS", "i", GL_CURRENT_RASTER_TEXTURE_COORDS), + GB_CONSTANT("CURRENT_RASTER_POSITION_VALID", "i", GL_CURRENT_RASTER_POSITION_VALID), + GB_CONSTANT("CURRENT_TEXTURE_COORDS", "i", GL_CURRENT_TEXTURE_COORDS), + GB_CONSTANT("INDEX_CLEAR_VALUE", "i", GL_INDEX_CLEAR_VALUE), + GB_CONSTANT("INDEX_MODE", "i", GL_INDEX_MODE), + GB_CONSTANT("INDEX_WRITEMASK", "i", GL_INDEX_WRITEMASK), + GB_CONSTANT("MODELVIEW_MATRIX", "i", GL_MODELVIEW_MATRIX), + GB_CONSTANT("MODELVIEW_STACK_DEPTH", "i", GL_MODELVIEW_STACK_DEPTH), + GB_CONSTANT("NAME_STACK_DEPTH", "i", GL_NAME_STACK_DEPTH), + GB_CONSTANT("PROJECTION_MATRIX", "i", GL_PROJECTION_MATRIX), + GB_CONSTANT("PROJECTION_STACK_DEPTH", "i", GL_PROJECTION_STACK_DEPTH), + GB_CONSTANT("RENDER_MODE", "i", GL_RENDER_MODE), + GB_CONSTANT("RGBA_MODE", "i", GL_RGBA_MODE), + GB_CONSTANT("TEXTURE_MATRIX", "i", GL_TEXTURE_MATRIX), + GB_CONSTANT("TEXTURE_STACK_DEPTH", "i", GL_TEXTURE_STACK_DEPTH), + GB_CONSTANT("VIEWPORT_", "i", GL_VIEWPORT), + + /* Evaluators */ + GB_CONSTANT("AUTO_NORMAL", "i", GL_AUTO_NORMAL), + GB_CONSTANT("MAP1_COLOR_4","i", GL_MAP1_COLOR_4), + GB_CONSTANT("MAP1_INDEX","i", GL_MAP1_INDEX), + GB_CONSTANT("MAP1_NORMAL","i", GL_MAP1_NORMAL), + GB_CONSTANT("MAP1_TEXTURE_COORD_1","i", GL_MAP1_TEXTURE_COORD_1), + GB_CONSTANT("MAP1_TEXTURE_COORD_2","i", GL_MAP1_TEXTURE_COORD_2), + GB_CONSTANT("MAP1_TEXTURE_COORD_3","i", GL_MAP1_TEXTURE_COORD_3), + GB_CONSTANT("MAP1_TEXTURE_COORD_4","i", GL_MAP1_TEXTURE_COORD_4), + GB_CONSTANT("MAP1_VERTEX_3","i", GL_MAP1_VERTEX_3), + GB_CONSTANT("MAP1_VERTEX_4","i", GL_MAP1_VERTEX_4), + GB_CONSTANT("MAP2_COLOR_4","i", GL_MAP2_COLOR_4), + GB_CONSTANT("MAP2_INDEX","i", GL_MAP2_INDEX), + GB_CONSTANT("MAP2_NORMAL","i", GL_MAP2_NORMAL), + GB_CONSTANT("MAP2_TEXTURE_COORD_1","i", GL_MAP2_TEXTURE_COORD_1), + GB_CONSTANT("MAP2_TEXTURE_COORD_2","i", GL_MAP2_TEXTURE_COORD_2), + GB_CONSTANT("MAP2_TEXTURE_COORD_3","i", GL_MAP2_TEXTURE_COORD_3), + GB_CONSTANT("MAP2_TEXTURE_COORD_4","i", GL_MAP2_TEXTURE_COORD_4), + GB_CONSTANT("MAP2_VERTEX_3","i", GL_MAP2_VERTEX_3), + GB_CONSTANT("MAP2_VERTEX_4","i", GL_MAP2_VERTEX_4), + GB_CONSTANT("MAP1_GRID_DOMAIN","i", GL_MAP1_GRID_DOMAIN), + GB_CONSTANT("MAP1_GRID_SEGMENTS","i", GL_MAP1_GRID_SEGMENTS), + GB_CONSTANT("MAP2_GRID_DOMAIN","i", GL_MAP2_GRID_DOMAIN), + GB_CONSTANT("MAP2_GRID_SEGMENTS","i", GL_MAP2_GRID_SEGMENTS), + GB_CONSTANT("COEFF","i", GL_COEFF), + GB_CONSTANT("ORDER","i", GL_ORDER), + GB_CONSTANT("DOMAIN","i", GL_DOMAIN), + + /* Hints */ + GB_CONSTANT("FOG_HINT", "i", GL_FOG_HINT), + GB_CONSTANT("LINE_SMOOTH_HINT", "i", GL_LINE_SMOOTH_HINT), + GB_CONSTANT("PERSPECTIVE_CORRECTION_HINT", "i", GL_PERSPECTIVE_CORRECTION_HINT), + GB_CONSTANT("POINT_SMOOTH_HINT", "i", GL_POINT_SMOOTH_HINT), + GB_CONSTANT("POLYGON_SMOOTH_HINT", "i", GL_POLYGON_SMOOTH_HINT), + GB_CONSTANT("DONT_CARE", "i", GL_DONT_CARE), + GB_CONSTANT("FASTEST", "i", GL_FASTEST), + GB_CONSTANT("NICEST", "i", GL_NICEST), + + /* Scissor box */ + GB_CONSTANT("SCISSOR_BOX", "i", GL_SCISSOR_BOX), + GB_CONSTANT("SCISSOR_TEST", "i", GL_SCISSOR_TEST), + + /* Pixel Mode / Transfer */ + GB_CONSTANT("MAP_COLOR", "i", GL_MAP_COLOR), + GB_CONSTANT("MAP_STENCIL", "i", GL_MAP_STENCIL), + GB_CONSTANT("INDEX_SHIFT", "i", GL_INDEX_SHIFT), + GB_CONSTANT("INDEX_OFFSET", "i", GL_INDEX_OFFSET), + GB_CONSTANT("RED_SCALE", "i", GL_RED_SCALE), + GB_CONSTANT("RED_BIAS", "i", GL_RED_BIAS), + GB_CONSTANT("GREEN_SCALE", "i", GL_GREEN_SCALE), + GB_CONSTANT("GREEN_BIAS", "i", GL_GREEN_BIAS), + GB_CONSTANT("BLUE_SCALE", "i", GL_BLUE_SCALE), + GB_CONSTANT("BLUE_BIAS", "i", GL_BLUE_BIAS), + GB_CONSTANT("ALPHA_SCALE", "i", GL_ALPHA_SCALE), + GB_CONSTANT("ALPHA_BIAS", "i", GL_ALPHA_BIAS), + GB_CONSTANT("DEPTH_SCALE", "i", GL_DEPTH_SCALE), + GB_CONSTANT("DEPTH_BIAS", "i", GL_DEPTH_BIAS), + GB_CONSTANT("PIXEL_MAP_S_TO_S_SIZE", "i", GL_PIXEL_MAP_S_TO_S_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_I_SIZE", "i", GL_PIXEL_MAP_I_TO_I_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_R_SIZE", "i", GL_PIXEL_MAP_I_TO_R_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_G_SIZE", "i", GL_PIXEL_MAP_I_TO_G_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_B_SIZE", "i", GL_PIXEL_MAP_I_TO_B_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_A_SIZE", "i", GL_PIXEL_MAP_I_TO_A_SIZE), + GB_CONSTANT("PIXEL_MAP_R_TO_R_SIZE", "i", GL_PIXEL_MAP_R_TO_R_SIZE), + GB_CONSTANT("PIXEL_MAP_G_TO_G_SIZE", "i", GL_PIXEL_MAP_G_TO_G_SIZE), + GB_CONSTANT("PIXEL_MAP_B_TO_B_SIZE", "i", GL_PIXEL_MAP_B_TO_B_SIZE), + GB_CONSTANT("PIXEL_MAP_A_TO_A_SIZE", "i", GL_PIXEL_MAP_A_TO_A_SIZE), + GB_CONSTANT("PIXEL_MAP_S_TO_S", "i", GL_PIXEL_MAP_S_TO_S), + GB_CONSTANT("PIXEL_MAP_I_TO_I", "i", GL_PIXEL_MAP_I_TO_I), + GB_CONSTANT("PIXEL_MAP_I_TO_R", "i", GL_PIXEL_MAP_I_TO_R), + GB_CONSTANT("PIXEL_MAP_I_TO_G", "i", GL_PIXEL_MAP_I_TO_G), + GB_CONSTANT("PIXEL_MAP_I_TO_B", "i", GL_PIXEL_MAP_I_TO_B), + GB_CONSTANT("PIXEL_MAP_I_TO_A", "i", GL_PIXEL_MAP_I_TO_A), + GB_CONSTANT("PIXEL_MAP_R_TO_R", "i", GL_PIXEL_MAP_R_TO_R), + GB_CONSTANT("PIXEL_MAP_G_TO_G", "i", GL_PIXEL_MAP_G_TO_G), + GB_CONSTANT("PIXEL_MAP_B_TO_B", "i", GL_PIXEL_MAP_B_TO_B), + GB_CONSTANT("PIXEL_MAP_A_TO_A", "i", GL_PIXEL_MAP_A_TO_A), + GB_CONSTANT("PACK_ALIGNMENT", "i", GL_PACK_ALIGNMENT), + GB_CONSTANT("PACK_LSB_FIRST", "i", GL_PACK_LSB_FIRST), + GB_CONSTANT("PACK_ROW_LENGTH", "i", GL_PACK_ROW_LENGTH), + GB_CONSTANT("PACK_SKIP_PIXELS", "i", GL_PACK_SKIP_PIXELS), + GB_CONSTANT("PACK_SKIP_ROWS", "i", GL_PACK_SKIP_ROWS), + GB_CONSTANT("PACK_SWAP_BYTES", "i", GL_PACK_SWAP_BYTES), + GB_CONSTANT("UNPACK_ALIGNMENT", "i", GL_UNPACK_ALIGNMENT), + GB_CONSTANT("UNPACK_LSB_FIRST", "i", GL_UNPACK_LSB_FIRST), + GB_CONSTANT("UNPACK_ROW_LENGTH", "i", GL_UNPACK_ROW_LENGTH), + GB_CONSTANT("UNPACK_SKIP_PIXELS", "i", GL_UNPACK_SKIP_PIXELS), + GB_CONSTANT("UNPACK_SKIP_ROWS", "i", GL_UNPACK_SKIP_ROWS), + GB_CONSTANT("UNPACK_SWAP_BYTES", "i", GL_UNPACK_SWAP_BYTES), + GB_CONSTANT("ZOOM_X", "i", GL_ZOOM_X), + GB_CONSTANT("ZOOM_Y", "i", GL_ZOOM_Y), + + /* Texture mapping */ + GB_CONSTANT("TEXTURE_ENV", "i", GL_TEXTURE_ENV), + GB_CONSTANT("TEXTURE_ENV_MODE", "i", GL_TEXTURE_ENV_MODE), + GB_CONSTANT("TEXTURE_1D", "i", GL_TEXTURE_1D), + GB_CONSTANT("TEXTURE_2D", "i", GL_TEXTURE_2D), + GB_CONSTANT("TEXTURE_WRAP_S", "i", GL_TEXTURE_WRAP_S), + GB_CONSTANT("TEXTURE_WRAP_T", "i", GL_TEXTURE_WRAP_T), + GB_CONSTANT("TEXTURE_MAG_FILTER", "i", GL_TEXTURE_MAG_FILTER), + GB_CONSTANT("TEXTURE_MIN_FILTER", "i", GL_TEXTURE_MIN_FILTER), + GB_CONSTANT("TEXTURE_ENV_COLOR", "i", GL_TEXTURE_ENV_COLOR), + GB_CONSTANT("TEXTURE_GEN_S", "i", GL_TEXTURE_GEN_S), + GB_CONSTANT("TEXTURE_GEN_T", "i", GL_TEXTURE_GEN_T), + GB_CONSTANT("TEXTURE_GEN_MODE", "i", GL_TEXTURE_GEN_MODE), + GB_CONSTANT("TEXTURE_BORDER_COLOR", "i", GL_TEXTURE_BORDER_COLOR), + GB_CONSTANT("TEXTURE_WIDTH", "i", GL_TEXTURE_WIDTH), + GB_CONSTANT("TEXTURE_HEIGHT", "i", GL_TEXTURE_HEIGHT), + GB_CONSTANT("TEXTURE_BORDER", "i", GL_TEXTURE_BORDER), + GB_CONSTANT("TEXTURE_COMPONENTS", "i", GL_TEXTURE_COMPONENTS), + GB_CONSTANT("TEXTURE_RED_SIZE", "i", GL_TEXTURE_RED_SIZE), + GB_CONSTANT("TEXTURE_GREEN_SIZE", "i", GL_TEXTURE_GREEN_SIZE), + GB_CONSTANT("TEXTURE_BLUE_SIZE", "i", GL_TEXTURE_BLUE_SIZE), + GB_CONSTANT("TEXTURE_ALPHA_SIZE", "i", GL_TEXTURE_ALPHA_SIZE), + GB_CONSTANT("TEXTURE_LUMINANCE_SIZE", "i", GL_TEXTURE_LUMINANCE_SIZE), + GB_CONSTANT("TEXTURE_INTENSITY_SIZE", "i", GL_TEXTURE_INTENSITY_SIZE), + GB_CONSTANT("NEAREST_MIPMAP_NEAREST", "i", GL_NEAREST_MIPMAP_NEAREST), + GB_CONSTANT("NEAREST_MIPMAP_LINEAR", "i", GL_NEAREST_MIPMAP_LINEAR), + GB_CONSTANT("LINEAR_MIPMAP_NEAREST", "i", GL_LINEAR_MIPMAP_NEAREST), + GB_CONSTANT("LINEAR_MIPMAP_LINEAR", "i", GL_LINEAR_MIPMAP_LINEAR), + GB_CONSTANT("OBJECT_LINEAR", "i", GL_OBJECT_LINEAR), + GB_CONSTANT("OBJECT_PLANE", "i", GL_OBJECT_PLANE), + GB_CONSTANT("EYE_LINEAR", "i", GL_EYE_LINEAR), + GB_CONSTANT("EYE_PLANE", "i", GL_EYE_PLANE), + GB_CONSTANT("SPHERE_MAP", "i", GL_SPHERE_MAP), + GB_CONSTANT("DECAL", "i", GL_DECAL), + GB_CONSTANT("MODULATE", "i", GL_MODULATE), + GB_CONSTANT("NEAREST", "i", GL_NEAREST), + GB_CONSTANT("REPEAT", "i", GL_REPEAT), + GB_CONSTANT("CLAMP", "i", GL_CLAMP), + GB_CONSTANT("S", "i", GL_S), + GB_CONSTANT("T", "i", GL_T), + GB_CONSTANT("R", "i", GL_R), + GB_CONSTANT("Q", "i", GL_Q), + GB_CONSTANT("TEXTURE_GEN_R", "i", GL_TEXTURE_GEN_R), + GB_CONSTANT("TEXTURE_GEN_Q", "i", GL_TEXTURE_GEN_Q), + GB_CONSTANT("GENERATE_MIPMAP", "i", GL_GENERATE_MIPMAP), + GB_CONSTANT("TEXTURE_MAX_LEVEL", "i", GL_TEXTURE_MAX_LEVEL), + GB_CONSTANT("COMPRESSED_RGBA", "i", GL_COMPRESSED_RGBA), + + /* Multitexture Mapping ARB*/ + GB_CONSTANT("TEXTURE0_ARB", "i", GL_TEXTURE0_ARB), + GB_CONSTANT("TEXTURE1_ARB", "i", GL_TEXTURE1_ARB), + GB_CONSTANT("TEXTURE2_ARB", "i", GL_TEXTURE2_ARB), + GB_CONSTANT("TEXTURE3_ARB", "i", GL_TEXTURE3_ARB), + GB_CONSTANT("TEXTURE4_ARB", "i", GL_TEXTURE4_ARB), + + GB_CONSTANT("TEXTURE0", "i", GL_TEXTURE0), + GB_CONSTANT("TEXTURE1", "i", GL_TEXTURE1), + GB_CONSTANT("TEXTURE2", "i", GL_TEXTURE2), + GB_CONSTANT("TEXTURE3", "i", GL_TEXTURE3), + GB_CONSTANT("TEXTURE4", "i", GL_TEXTURE4), + GB_CONSTANT("COMBINE", "i", GL_COMBINE), + GB_CONSTANT("COMBINE_RGB", "i", GL_COMBINE_RGB), + GB_CONSTANT("COMBINE_ALPHA", "i", GL_COMBINE_ALPHA), + GB_CONSTANT("COMBINE_EXT", "i", GL_COMBINE_EXT), + GB_CONSTANT("COMBINE_RGB_EXT", "i", GL_COMBINE_RGB_EXT), + GB_CONSTANT("COMBINE_ALPHA_EXT", "i", GL_COMBINE_ALPHA_EXT), + GB_CONSTANT("COMBINE_ARB", "i", GL_COMBINE), + GB_CONSTANT("COMBINE_RGB_ARB", "i", GL_COMBINE_RGB_ARB), + GB_CONSTANT("COMBINE_ALPHA_ARB", "i", GL_COMBINE_ALPHA_ARB), + GB_CONSTANT("SOURCE0_RGB_ARB", "i", GL_SOURCE0_RGB_ARB), + GB_CONSTANT("SOURCE1_RGB_ARB", "i", GL_SOURCE1_RGB_ARB), + GB_CONSTANT("SOURCE2_RGB_ARB", "i", GL_SOURCE2_RGB_ARB), + GB_CONSTANT("SOURCE0_ALPHA_ARB", "i", GL_SOURCE0_ALPHA_ARB), + GB_CONSTANT("SOURCE1_ALPHA_ARB", "i", GL_SOURCE1_ALPHA_ARB), + GB_CONSTANT("SOURCE2_ALPHA_ARB", "i", GL_SOURCE2_ALPHA_ARB), + GB_CONSTANT("OPERAND0_RGB_ARB", "i", GL_OPERAND0_RGB_ARB), + GB_CONSTANT("OPERAND1_RGB_ARB", "i", GL_OPERAND1_RGB_ARB), + GB_CONSTANT("OPERAND2_RGB_ARB", "i", GL_OPERAND2_RGB_ARB), + GB_CONSTANT("OPERAND0_ALPHA_ARB", "i", GL_OPERAND0_ALPHA_ARB), + GB_CONSTANT("OPERAND1_ALPHA_ARB", "i", GL_OPERAND1_ALPHA_ARB), + GB_CONSTANT("OPERAND2_ALPHA_ARB", "i", GL_OPERAND2_ALPHA_ARB), + GB_CONSTANT("RGB_SCALE_ARB", "i", GL_RGB_SCALE_ARB), + GB_CONSTANT("ADD_SIGNED_ARB", "i", GL_ADD_SIGNED_ARB), + GB_CONSTANT("INTERPOLATE_ARB", "i", GL_INTERPOLATE_ARB), + GB_CONSTANT("SUBTRACT_ARB", "i", GL_SUBTRACT_ARB), + GB_CONSTANT("CONSTANT_ARB", "i", GL_CONSTANT_ARB), + GB_CONSTANT("PRIMARY_COLOR_ARB", "i", GL_PRIMARY_COLOR_ARB), + GB_CONSTANT("PREVIOUS_ARB", "i", GL_PREVIOUS_ARB), + GB_CONSTANT("COMPRESSED_RGBA_S3TC_DXT1_EXT", "i", GL_COMPRESSED_RGBA_S3TC_DXT1_EXT), + GB_CONSTANT("COMPRESSED_RGBA_S3TC_DXT3_EXT", "i", GL_COMPRESSED_RGBA_S3TC_DXT3_EXT), + GB_CONSTANT("COMPRESSED_RGBA_S3TC_DXT5_EXT", "i", GL_COMPRESSED_RGBA_S3TC_DXT5_EXT), + + /* 38; GL_ARB_texture_rectangle */ + GB_CONSTANT("TEXTURE_RECTANGLE_ARB", "i", GL_TEXTURE_RECTANGLE_ARB), + GB_CONSTANT("TEXTURE_BINDING_RECTANGLE_ARB", "i", GL_TEXTURE_BINDING_RECTANGLE_ARB), + GB_CONSTANT("PROXY_TEXTURE_RECTANGLE_ARB", "i", GL_PROXY_TEXTURE_RECTANGLE_ARB), + GB_CONSTANT("MAX_RECTANGLE_TEXTURE_SIZE_ARB", "i", GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB), + GB_CONSTANT("SAMPLER_2D_RECT_ARB", "i", GL_SAMPLER_2D_RECT_ARB), + GB_CONSTANT("SAMPLER_2D_RECT_SHADOW_ARB", "i", GL_SAMPLER_2D_RECT_SHADOW_ARB), + + /* 310; GL_EXT_framebuffer_object */ + GB_CONSTANT("ELEMENT_ARRAY_BUFFER", "i", GL_ELEMENT_ARRAY_BUFFER), + GB_CONSTANT("FRAMEBUFFER_EXT", "i", GL_FRAMEBUFFER_EXT), + GB_CONSTANT("RENDERBUFFER_EXT", "i", GL_RENDERBUFFER_EXT), + GB_CONSTANT("STENCIL_INDEX1_EXT", "i", GL_STENCIL_INDEX1_EXT), + GB_CONSTANT("STENCIL_INDEX4_EXT", "i", GL_STENCIL_INDEX4_EXT), + GB_CONSTANT("STENCIL_INDEX8_EXT", "i", GL_STENCIL_INDEX8_EXT), + GB_CONSTANT("STENCIL_INDEX16_EXT", "i", GL_STENCIL_INDEX16_EXT), + GB_CONSTANT("RENDERBUFFER_WIDTH_EXT", "i", GL_RENDERBUFFER_WIDTH_EXT), + GB_CONSTANT("RENDERBUFFER_HEIGHT_EXT", "i", GL_RENDERBUFFER_HEIGHT_EXT), + GB_CONSTANT("RENDERBUFFER_INTERNAL_FORMAT_EXT", "i", GL_RENDERBUFFER_INTERNAL_FORMAT_EXT), + GB_CONSTANT("RENDERBUFFER_RED_SIZE_EXT", "i", GL_RENDERBUFFER_RED_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_GREEN_SIZE_EXT", "i", GL_RENDERBUFFER_GREEN_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_BLUE_SIZE_EXT", "i", GL_RENDERBUFFER_BLUE_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_ALPHA_SIZE_EXT", "i", GL_RENDERBUFFER_ALPHA_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_DEPTH_SIZE_EXT", "i", GL_RENDERBUFFER_DEPTH_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_STENCIL_SIZE_EXT", "i", GL_RENDERBUFFER_STENCIL_SIZE_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT), + GB_CONSTANT("COLOR_ATTACHMENT0_EXT", "i", GL_COLOR_ATTACHMENT0_EXT), + GB_CONSTANT("COLOR_ATTACHMENT1_EXT", "i", GL_COLOR_ATTACHMENT1_EXT), + GB_CONSTANT("COLOR_ATTACHMENT2_EXT", "i", GL_COLOR_ATTACHMENT2_EXT), + GB_CONSTANT("COLOR_ATTACHMENT3_EXT", "i", GL_COLOR_ATTACHMENT3_EXT), + GB_CONSTANT("COLOR_ATTACHMENT4_EXT", "i", GL_COLOR_ATTACHMENT4_EXT), + GB_CONSTANT("COLOR_ATTACHMENT5_EXT", "i", GL_COLOR_ATTACHMENT5_EXT), + GB_CONSTANT("COLOR_ATTACHMENT6_EXT", "i", GL_COLOR_ATTACHMENT6_EXT), + GB_CONSTANT("COLOR_ATTACHMENT7_EXT", "i", GL_COLOR_ATTACHMENT7_EXT), + GB_CONSTANT("COLOR_ATTACHMENT8_EXT", "i", GL_COLOR_ATTACHMENT8_EXT), + GB_CONSTANT("COLOR_ATTACHMENT9_EXT", "i", GL_COLOR_ATTACHMENT9_EXT), + GB_CONSTANT("COLOR_ATTACHMENT10_EXT", "i", GL_COLOR_ATTACHMENT10_EXT), + GB_CONSTANT("COLOR_ATTACHMENT11_EXT", "i", GL_COLOR_ATTACHMENT11_EXT), + GB_CONSTANT("COLOR_ATTACHMENT12_EXT", "i", GL_COLOR_ATTACHMENT12_EXT), + GB_CONSTANT("COLOR_ATTACHMENT13_EXT", "i", GL_COLOR_ATTACHMENT13_EXT), + GB_CONSTANT("COLOR_ATTACHMENT14_EXT", "i", GL_COLOR_ATTACHMENT14_EXT), + GB_CONSTANT("COLOR_ATTACHMENT15_EXT", "i", GL_COLOR_ATTACHMENT15_EXT), + GB_CONSTANT("DEPTH_ATTACHMENT_EXT", "i", GL_DEPTH_ATTACHMENT_EXT), + GB_CONSTANT("STENCIL_ATTACHMENT_EXT", "i", GL_STENCIL_ATTACHMENT_EXT), + GB_CONSTANT("FRAMEBUFFER_COMPLETE_EXT", "i", GL_FRAMEBUFFER_COMPLETE_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_FORMATS_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT), + GB_CONSTANT("FRAMEBUFFER_UNSUPPORTED_EXT", "i", GL_FRAMEBUFFER_UNSUPPORTED_EXT), + GB_CONSTANT("FRAMEBUFFER_BINDING_EXT", "i", GL_FRAMEBUFFER_BINDING_EXT), + GB_CONSTANT("RENDERBUFFER_BINDING_EXT", "i", GL_RENDERBUFFER_BINDING_EXT), + GB_CONSTANT("MAX_COLOR_ATTACHMENTS_EXT", "i", GL_MAX_COLOR_ATTACHMENTS_EXT), + GB_CONSTANT("MAX_RENDERBUFFER_SIZE_EXT", "i", GL_MAX_RENDERBUFFER_SIZE_EXT), + GB_CONSTANT("INVALID_FRAMEBUFFER_OPERATION_EXT", "i", GL_INVALID_FRAMEBUFFER_OPERATION_EXT), + GB_CONSTANT("SAMPLES_PASSED", "i", GL_SAMPLES_PASSED), + GB_CONSTANT("QUERY_RESULT", "i", GL_QUERY_RESULT), + GB_CONSTANT("TRUE", "i", GL_TRUE), + GB_CONSTANT("FALSE", "i", GL_FALSE), + GB_CONSTANT("PRIMITIVES_GENERATED", "i", GL_PRIMITIVES_GENERATED), + GB_CONSTANT("TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", "i", GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN), + //GB_CONSTANT("TIME_ELAPSED", "i", GL_TIME_ELAPSED), + //GB_CONSTANT("TIMESTAMP", "i", ​ GL_TIMESTAMP​), + GB_CONSTANT("CURRENT_QUERY", "i", GL_CURRENT_QUERY), + GB_CONSTANT("QUERY_COUNTER_BITS", "i", GL_QUERY_COUNTER_BITS), + + /* GL Array */ + GB_CONSTANT("ARRAY_BUFFER", "i", GL_ARRAY_BUFFER), + GB_CONSTANT("STATIC_DRAW", "i", GL_STATIC_DRAW), + + + + + + +#if 0 + +/* OpenGL 1.1 */ + + GB_CONSTANT("ProxyTexture1d", "i", GL_PROXY_TEXTURE_1D), + GB_CONSTANT("ProxyTexture2d", "i", GL_PROXY_TEXTURE_2D), + GB_CONSTANT("TexturePriority", "i", GL_TEXTURE_PRIORITY), + GB_CONSTANT("TextureResident", "i", GL_TEXTURE_RESIDENT), + GB_CONSTANT("TextureBinding1d", "i", GL_TEXTURE_BINDING_1D), + GB_CONSTANT("TextureBinding2d", "i", GL_TEXTURE_BINDING_2D), + GB_CONSTANT("TextureInternalFormat", "i", GL_TEXTURE_INTERNAL_FORMAT), + GB_CONSTANT("Alpha4", "i", GL_ALPHA4), + GB_CONSTANT("Alpha8", "i", GL_ALPHA8), + GB_CONSTANT("Alpha12", "i", GL_ALPHA12), + GB_CONSTANT("Alpha16", "i", GL_ALPHA16), + GB_CONSTANT("Luminance4", "i", GL_LUMINANCE4), + GB_CONSTANT("Luminance8", "i", GL_LUMINANCE8), + GB_CONSTANT("Luminance12", "i", GL_LUMINANCE12), + GB_CONSTANT("Luminance16", "i", GL_LUMINANCE16), + GB_CONSTANT("Luminance4Alpha4", "i", GL_LUMINANCE4_ALPHA4), + GB_CONSTANT("Luminance6Alpha2", "i", GL_LUMINANCE6_ALPHA2), + GB_CONSTANT("Luminance8Alpha8", "i", GL_LUMINANCE8_ALPHA8), + GB_CONSTANT("Luminance12Alpha4", "i", GL_LUMINANCE12_ALPHA4), + GB_CONSTANT("Luminance12Alpha12", "i", GL_LUMINANCE12_ALPHA12), + GB_CONSTANT("Luminance16Alpha16", "i", GL_LUMINANCE16_ALPHA16), + GB_CONSTANT("Intensity", "i", GL_INTENSITY), + GB_CONSTANT("Intensity4", "i", GL_INTENSITY4), + GB_CONSTANT("Intensity8", "i", GL_INTENSITY8), + GB_CONSTANT("Intensity12", "i", GL_INTENSITY12), + GB_CONSTANT("Intensity16", "i", GL_INTENSITY16), + GB_CONSTANT("R3G3B2", "i", GL_R3_G3_B2), + GB_CONSTANT("Rgb4", "i", GL_RGB4), + GB_CONSTANT("Rgb5", "i", GL_RGB5), + GB_CONSTANT("Rgb8", "i", GL_RGB8), + GB_CONSTANT("Rgb10", "i", GL_RGB10), + GB_CONSTANT("Rgb12", "i", GL_RGB12), + GB_CONSTANT("Rgb16", "i", GL_RGB16), + GB_CONSTANT("Rgba2", "i", GL_RGBA2), + GB_CONSTANT("Rgba4", "i", GL_RGBA4), + GB_CONSTANT("Rgb5A1", "i", GL_RGB5_A1), + GB_CONSTANT("Rgba8", "i", GL_RGBA8), + GB_CONSTANT("Rgb10A2", "i", GL_RGB10_A2), + GB_CONSTANT("Rgba12", "i", GL_RGBA12), + GB_CONSTANT("Rgba16", "i", GL_RGBA16), + GB_CONSTANT("ClientPixelStoreBit", "i", GL_CLIENT_PIXEL_STORE_BIT), + GB_CONSTANT("ClientVertexArrayBit", "i", GL_CLIENT_VERTEX_ARRAY_BIT), + GB_CONSTANT("AllClientAttribBits", "i", GL_ALL_CLIENT_ATTRIB_BITS), + GB_CONSTANT("ClientAllAttribBits", "i", GL_CLIENT_ALL_ATTRIB_BITS), + +/* texture_border_clamp */ + + GB_CONSTANT("ClampToBorder", "i", GL_CLAMP_TO_BORDER), +#endif + + GB_END_DECLARE +}; diff --git a/gb.opengl/src/GL.h b/gb.opengl/src/GL.h new file mode 100644 index 00000000..4a343bd3 --- /dev/null +++ b/gb.opengl/src/GL.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + GL.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GL_H +#define __GL_H + +#include "main.h" + +#include + +#ifndef __GL_C +extern GB_DESC Cgl[]; +#endif /* __GL_C */ + +#endif /* __GL_H */ diff --git a/gb.opengl/src/GLclipping.c b/gb.opengl/src/GLclipping.c new file mode 100644 index 00000000..05fe9a96 --- /dev/null +++ b/gb.opengl/src/GLclipping.c @@ -0,0 +1,58 @@ +/*************************************************************************** + + GLclipping.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLCLIPPING_C + +#include "GL.h" + +BEGIN_METHOD(GLCLIPPLANE, GB_INTEGER plane; GB_OBJECT equation) + + GLdouble params[4]; + GB_ARRAY fArray = (GB_ARRAY) VARG(equation); + int count = GB.Array.Count(fArray); + uint i; + + count = (count > 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLCLIPPING_H +#define __GLCLIPPING_H + +#include "main.h" + +DECLARE_METHOD(GLCLIPPLANE); +DECLARE_METHOD(GLGETCLIPPLANE); + +#endif /* __GLCLIPPING_H */ diff --git a/gb.opengl/src/GLcolorLighting.c b/gb.opengl/src/GLcolorLighting.c new file mode 100644 index 00000000..a7d6d717 --- /dev/null +++ b/gb.opengl/src/GLcolorLighting.c @@ -0,0 +1,421 @@ +/*************************************************************************** + + GLcolorLighting.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLCOLORLIGHTING_C + +#include "GL.h" + +BEGIN_METHOD(GLCOLOR3F, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue) + + glColor3d(VARG(red), VARG(green), VARG(blue)); + +END_METHOD + +BEGIN_METHOD(GLCOLORF, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue; GB_FLOAT alpha) + + if (MISSING(alpha)) + glColor3d(VARG(red), VARG(green), VARG(blue)); + else + glColor4d(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCOLOR3I, GB_INTEGER red; GB_INTEGER green; GB_INTEGER blue) + + glColor3i(VARG(red), VARG(green), VARG(blue)); + +END_METHOD + +BEGIN_METHOD(GLCOLORI, GB_INTEGER red; GB_INTEGER green; GB_INTEGER blue; GB_INTEGER alpha) + + if (MISSING(alpha)) + glColor3i(VARG(red), VARG(green), VARG(blue)); + else + glColor4i(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCOLORFV, GB_OBJECT array) + + GLdouble r,g,b,a; + GB_ARRAY color = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(color); + + if (count<3) + return; + + r = *((GLdouble *)GB.Array.Get(color,0)); + g = *((GLdouble *)GB.Array.Get(color,1)); + b = *((GLdouble *)GB.Array.Get(color,2)); + + if (count==3) + glColor3d(r, g, b); + else + { + a = *((GLdouble *)GB.Array.Get(color,3)); + glColor4d(r, g, b, a); + } + +END_METHOD + +BEGIN_METHOD(GLCOLORIV, GB_OBJECT array) + + GLint r,g,b,a; + GB_ARRAY color = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(color); + + if (count<3) + return; + + r = *((GLint *)GB.Array.Get(color,0)); + g = *((GLint *)GB.Array.Get(color,1)); + b = *((GLint *)GB.Array.Get(color,2)); + + if (count==3) + glColor3i(r, g, b); + else + { + a = *((GLint *)GB.Array.Get(color,3)); + glColor4i(r, g, b, a); + } + +END_METHOD + +BEGIN_METHOD(GLCOLORMATERIAL, GB_INTEGER face; GB_INTEGER mode) + + glColorMaterial(VARG(face), VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLFRONTFACE, GB_INTEGER mode) + + glFrontFace(VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLGETLIGHTFV, GB_INTEGER light; GB_INTEGER pname) + + GLfloat params[4]; + GB_ARRAY fArray; + int i, count=1; + + switch(VARG(pname)) + { + case GL_AMBIENT : + case GL_DIFFUSE : + case GL_SPECULAR : + case GL_POSITION : + count = 4; + break; + case GL_SPOT_DIRECTION : + count = 3; + break; + } + + GB.Array.New(&fArray , GB_T_FLOAT , count); + glGetLightfv(VARG(light), VARG(pname), params); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLCOLORLIGHTING_H +#define __GLCOLORLIGHTING_H + +#include "main.h" + +DECLARE_METHOD(GLCOLOR3F); +DECLARE_METHOD(GLCOLORF); +DECLARE_METHOD(GLCOLOR3I); +DECLARE_METHOD(GLCOLORI); +DECLARE_METHOD(GLCOLORFV); +DECLARE_METHOD(GLCOLORIV); +DECLARE_METHOD(GLCOLORMATERIAL); +DECLARE_METHOD(GLFRONTFACE); +DECLARE_METHOD(GLGETLIGHTFV); +DECLARE_METHOD(GLGETLIGHTIV); +DECLARE_METHOD(GLGETMATERIALFV); +DECLARE_METHOD(GLGETMATERIALIV); +DECLARE_METHOD(GLINDEXF); +DECLARE_METHOD(GLINDEXI); +DECLARE_METHOD(GLLIGHTF); +DECLARE_METHOD(GLLIGHTI); +DECLARE_METHOD(GLLIGHTFV); +DECLARE_METHOD(GLLIGHTIV); +DECLARE_METHOD(GLLIGHTMODELF); +DECLARE_METHOD(GLLIGHTMODELI); +DECLARE_METHOD(GLLIGHTMODELFV); +DECLARE_METHOD(GLLIGHTMODELIV); +DECLARE_METHOD(GLMATERIALF); +DECLARE_METHOD(GLMATERIALI); +DECLARE_METHOD(GLNORMAL3FV); +DECLARE_METHOD(GLNORMAL3IV); +DECLARE_METHOD(GLMATERIALFV); +DECLARE_METHOD(GLMATERIALIV); +DECLARE_METHOD(GLNORMAL3F); +DECLARE_METHOD(GLNORMAL3I); +DECLARE_METHOD(GLSHADEMODEL); + +#endif /* __GLCOLORLIGHTING_H */ diff --git a/gb.opengl/src/GLcoordTransf.c b/gb.opengl/src/GLcoordTransf.c new file mode 100644 index 00000000..eeeb7595 --- /dev/null +++ b/gb.opengl/src/GLcoordTransf.c @@ -0,0 +1,122 @@ +/*************************************************************************** + + GLcoordTransf.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLCOORDTRANSF_C + +#include "GL.h" + +BEGIN_METHOD(GLDEPTHRANGE, GB_FLOAT near; GB_FLOAT far) + + glDepthRange(VARG(near),VARG(far)); + +END_METHOD + +BEGIN_METHOD(GLFRUSTUM, GB_FLOAT left; GB_FLOAT right; GB_FLOAT bottom; GB_FLOAT top; GB_FLOAT near; GB_FLOAT far) + + glFrustum(VARG(left),VARG(right),VARG(bottom),VARG(top),VARG(near),VARG(far)); + +END_METHOD + +BEGIN_METHOD_VOID(GLLOADIDENTITY) + + glLoadIdentity(); + +END_METHOD + +BEGIN_METHOD(GLLOADMATRIXF, GB_OBJECT array) + + GLdouble params[16]; + GB_ARRAY matrix = (GB_ARRAY) VARG(array); + int i, count = GB.Array.Count(matrix); + + count = (count > 16 ? 16 : count); + + for (i=0; i 16 ? 16 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLCOORDTRANSF_H +#define __GLCOORDTRANSF_H + +#include "main.h" + +DECLARE_METHOD(GLDEPTHRANGE); +DECLARE_METHOD(GLFRUSTUM); +DECLARE_METHOD(GLLOADIDENTITY); +DECLARE_METHOD(GLLOADMATRIXF); +DECLARE_METHOD(GLMATRIXMODE); +DECLARE_METHOD(GLMULTMATRIXF); +DECLARE_METHOD(GLORTHO); +DECLARE_METHOD(GLPOPMATRIX); +DECLARE_METHOD(GLPUSHMATRIX); +DECLARE_METHOD(GLROTATEF); +DECLARE_METHOD(GLSCALEF); +DECLARE_METHOD(GLTRANSLATEF); +DECLARE_METHOD(GLVIEWPORT); + +#endif /* __GLCOORDTRANSF_H */ diff --git a/gb.opengl/src/GLdisplayList.c b/gb.opengl/src/GLdisplayList.c new file mode 100644 index 00000000..45e6e540 --- /dev/null +++ b/gb.opengl/src/GLdisplayList.c @@ -0,0 +1,81 @@ +/*************************************************************************** + + GLdisplayList.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLDISPLAYLIST_C + +#include "GL.h" + +BEGIN_METHOD(GLCALLLIST, GB_INTEGER index) + + glCallList(VARG(index)); + +END_METHOD + +BEGIN_METHOD(GLCALLLISTS, GB_OBJECT lists) + + GB_ARRAY iArray = (GB_ARRAY) VARG(lists); + int i,count = GB.Array.Count(iArray); + + if (!count) + return; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLDISPLAYLIST_H +#define __GLDISPLAYLIST_H + +#include "main.h" + +DECLARE_METHOD(GLCALLLIST); +DECLARE_METHOD(GLCALLLISTS); +DECLARE_METHOD(GLDELETELISTS); +DECLARE_METHOD(GLENDLIST); +DECLARE_METHOD(GLGENLISTS); +DECLARE_METHOD(GLISLIST); +DECLARE_METHOD(GLLISTBASE); +DECLARE_METHOD(GLNEWLIST); + +#endif /* __GLDISPLAYLIST_H */ diff --git a/gb.opengl/src/GLeval.c b/gb.opengl/src/GLeval.c new file mode 100644 index 00000000..42388fbf --- /dev/null +++ b/gb.opengl/src/GLeval.c @@ -0,0 +1,115 @@ +/*************************************************************************** + + GLeval.c + + (c) 2005-2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLEVAL_C + +#include "GL.h" + +BEGIN_METHOD(GLMAP1F, GB_INTEGER target; GB_FLOAT u1; GB_FLOAT u2; GB_INTEGER stride; GB_INTEGER order; GB_OBJECT array) + + GB_ARRAY matrix = (GB_ARRAY) VARG(array); + int i, count = GB.Array.Count(matrix); + GLdouble params[count]; + + for (i=0; i0 ? *((double *)GB.Array.Get(fArray,0)) : 0; + params[1] = count>1 ? *((double *)GB.Array.Get(fArray,1)) : 0; + + glEvalCoord2dv(params); + +END_METHOD + +BEGIN_METHOD(GLMAPGRID1F, GB_INTEGER un; GB_FLOAT u1; GB_FLOAT u2) + + glMapGrid1d(VARG(un), VARG(u1), VARG(u2)); + +END_METHOD + +BEGIN_METHOD(GLMAPGRID2F, GB_INTEGER un; GB_FLOAT u1; GB_FLOAT u2; GB_INTEGER vn; GB_FLOAT v1; GB_FLOAT v2) + + glMapGrid2d(VARG(un), VARG(u1), VARG(u2), VARG(vn), VARG(v1), VARG(v2)); + +END_METHOD + +BEGIN_METHOD(GLEVALPOINT1, GB_INTEGER i) + + glEvalPoint1(VARG(i)); + +END_METHOD + +BEGIN_METHOD(GLEVALPOINT2, GB_INTEGER i; GB_INTEGER j) + + glEvalPoint2(VARG(i), VARG(j)); + +END_METHOD + +BEGIN_METHOD(GLEVALMESH1, GB_INTEGER mode; GB_INTEGER i1; GB_INTEGER i2) + + glEvalMesh1(VARG(mode), VARG(i1), VARG(i2)); + +END_METHOD + +BEGIN_METHOD(GLEVALMESH2, GB_INTEGER mode; GB_INTEGER i1; GB_INTEGER i2; GB_INTEGER j1; GB_INTEGER j2) + + glEvalMesh2(VARG(mode), VARG(i1), VARG(i2), VARG(j1), VARG(j2)); + +END_METHOD diff --git a/gb.opengl/src/GLeval.h b/gb.opengl/src/GLeval.h new file mode 100644 index 00000000..854760e3 --- /dev/null +++ b/gb.opengl/src/GLeval.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + GLeval.h + + (c) 2005-2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLEVAL_H +#define __GLEVAL_H + +#include "main.h" + +DECLARE_METHOD(GLMAP1F); +DECLARE_METHOD(GLMAP2F); +//DECLARE_METHOD(GLGETMAPFV); +//DECLARE_METHOD(GLGETMAPIV); +DECLARE_METHOD(GLEVALCOORD1F); +DECLARE_METHOD(GLEVALCOORD2F); +DECLARE_METHOD(GLEVALCOORD2FV); +DECLARE_METHOD(GLMAPGRID1F); +DECLARE_METHOD(GLMAPGRID2F); +DECLARE_METHOD(GLEVALPOINT1); +DECLARE_METHOD(GLEVALPOINT2); +DECLARE_METHOD(GLEVALMESH1); +DECLARE_METHOD(GLEVALMESH2); + +#endif /* __GLEVAL_H */ + + diff --git a/gb.opengl/src/GLfog.c b/gb.opengl/src/GLfog.c new file mode 100644 index 00000000..69701a07 --- /dev/null +++ b/gb.opengl/src/GLfog.c @@ -0,0 +1,68 @@ +/*************************************************************************** + + GLfog.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLFOG_C + +#include "GL.h" + +BEGIN_METHOD(GLFOGF, GB_INTEGER pname; GB_FLOAT param) + + glFogf(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLFOGI, GB_INTEGER face; GB_INTEGER pname; GB_INTEGER param) + + glMateriali(VARG(face), VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLFOGFV, GB_INTEGER pname; GB_OBJECT params) + + GLfloat params[4]; + GB_ARRAY fArray = (GB_ARRAY) VARG(params); + uint i, count = GB.Array.Count(fArray); + + count = (count > 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLFOG_H +#define __GLFOG_H + +#include "main.h" + +DECLARE_METHOD(GLFOGF); +DECLARE_METHOD(GLFOGI); +DECLARE_METHOD(GLFOGFV); +DECLARE_METHOD(GLFOGIV); + +#endif /* __GLFOG_H */ diff --git a/gb.opengl/src/GLframeBufferOps.c b/gb.opengl/src/GLframeBufferOps.c new file mode 100644 index 00000000..4ed24402 --- /dev/null +++ b/gb.opengl/src/GLframeBufferOps.c @@ -0,0 +1,142 @@ +/*************************************************************************** + + GLframeBufferOps.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLFRAMEBUFFEROPS_C + +#include "GL.h" + +/***************************************************************************/ + +BEGIN_METHOD(GLACCUM, GB_INTEGER operation; GB_FLOAT value) + + glAccum(VARG(operation), VARG(value)); + +END_METHOD + +BEGIN_METHOD(GLALPHAFUNC, GB_INTEGER function; GB_FLOAT reference) + + glAlphaFunc(VARG(function), VARG(reference)); + +END_METHOD + +BEGIN_METHOD(GLBLENDFUNC, GB_INTEGER sfactor; GB_INTEGER dfactor) + + glBlendFunc(VARG(sfactor), VARG(dfactor)); + +END_METHOD + +BEGIN_METHOD(GLCLEAR, GB_INTEGER mask) + + glClear(VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLCLEARACCUM, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue; GB_FLOAT alpha) + + glClearAccum(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCLEARCOLOR, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue; GB_FLOAT alpha) + + glClearColor(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCLEARDEPTH, GB_FLOAT depth) + + glClearDepth(VARG(depth)); + +END_METHOD + +BEGIN_METHOD(GLCLEARINDEX, GB_FLOAT value) + + glClearIndex(VARG(value)); + +END_METHOD + +BEGIN_METHOD(GLCLEARSTENCIL, GB_INTEGER value) + + glClearStencil(VARG(value)); + +END_METHOD + +BEGIN_METHOD(GLCOLORMASK, GB_BOOLEAN red; GB_BOOLEAN green; GB_BOOLEAN blue; GB_BOOLEAN alpha) + + glColorMask(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLDEPTHFUNC, GB_INTEGER function) + + glDepthFunc(VARG(function)); + +END_METHOD + +BEGIN_METHOD(GLDEPTHMASK, GB_BOOLEAN flag) + + glDepthMask(VARG(flag)); + +END_METHOD + +BEGIN_METHOD(GLDRAWBUFFER, GB_INTEGER mode) + + glDrawBuffer(VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLINDEXMASK, GB_INTEGER mask) + + glIndexMask(VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLLOGICOP, GB_INTEGER opcode) + + glLogicOp(VARG(opcode)); + +END_METHOD + +BEGIN_METHOD(GLSCISSOR, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + glScissor(VARG(x), VARG(y), VARG(width), VARG(height)); + +END_METHOD + +BEGIN_METHOD(GLSTENCILFUNC, GB_INTEGER function; GB_INTEGER reference; GB_INTEGER mask) + + glStencilFunc(VARG(function), VARG(reference), VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLSTENCILMASK, GB_INTEGER mask) + + glStencilMask(VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLSTENCILOP, GB_INTEGER fail; GB_INTEGER zfail; GB_INTEGER zpass) + + glStencilOp(VARG(fail), VARG(zfail), VARG(zpass)); + +END_METHOD diff --git a/gb.opengl/src/GLframeBufferOps.h b/gb.opengl/src/GLframeBufferOps.h new file mode 100644 index 00000000..f6c9ef21 --- /dev/null +++ b/gb.opengl/src/GLframeBufferOps.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + GLframeBufferOps.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLFRAMEBUFFEROPS_H +#define __GLFRAMEBUFFEROPS_H + +#include "main.h" + +DECLARE_METHOD(GLACCUM); +DECLARE_METHOD(GLALPHAFUNC); +DECLARE_METHOD(GLBLENDFUNC); +DECLARE_METHOD(GLCLEAR); +DECLARE_METHOD(GLCLEARACCUM); +DECLARE_METHOD(GLCLEARCOLOR); +DECLARE_METHOD(GLCLEARDEPTH); +DECLARE_METHOD(GLCLEARINDEX); +DECLARE_METHOD(GLCLEARSTENCIL); +DECLARE_METHOD(GLCOLORMASK); +DECLARE_METHOD(GLDEPTHFUNC); +DECLARE_METHOD(GLDEPTHMASK); +DECLARE_METHOD(GLDRAWBUFFER); +DECLARE_METHOD(GLINDEXMASK); +DECLARE_METHOD(GLLOGICOP); +DECLARE_METHOD(GLSCISSOR); +DECLARE_METHOD(GLSTENCILFUNC); +DECLARE_METHOD(GLSTENCILMASK); +DECLARE_METHOD(GLSTENCILOP); + +#endif /* __GLFRAMEBUFFEROPS_H */ diff --git a/gb.opengl/src/GLinfo.c b/gb.opengl/src/GLinfo.c new file mode 100644 index 00000000..606bca67 --- /dev/null +++ b/gb.opengl/src/GLinfo.c @@ -0,0 +1,501 @@ +/*************************************************************************** + + GLinfo.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLINFO_C + +#include "GL.h" + +int checkSize(GLenum value) +{ + int retSize = 0; + + if (value == GL_COMPRESSED_TEXTURE_FORMATS) + { + GLint size; + glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &size); + return size; + } + + switch(value) + { + case GL_ACCUM_ALPHA_BITS: + case GL_ACCUM_BLUE_BITS: + case GL_ACCUM_GREEN_BITS: + case GL_ACCUM_RED_BITS: + case GL_ACTIVE_TEXTURE: + case GL_ALPHA_BIAS: + case GL_ALPHA_BITS: + case GL_ALPHA_SCALE: + case GL_ALPHA_TEST: + case GL_ALPHA_TEST_FUNC: + case GL_ALPHA_TEST_REF: + case GL_ARRAY_BUFFER_BINDING: + case GL_ATTRIB_STACK_DEPTH: + case GL_AUTO_NORMAL: + case GL_AUX_BUFFERS: + case GL_BLEND: + case GL_BLEND_DST: + case GL_BLEND_DST_ALPHA: + case GL_BLEND_DST_RGB: + case GL_BLEND_EQUATION_ALPHA: + case GL_BLEND_EQUATION_RGB: + case GL_BLEND_SRC: + case GL_BLEND_SRC_ALPHA: + case GL_BLEND_SRC_RGB: + case GL_BLUE_BIAS: + case GL_BLUE_BITS: + case GL_BLUE_SCALE: + case GL_CLIENT_ACTIVE_TEXTURE: + case GL_CLIENT_ATTRIB_STACK_DEPTH: + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: + case GL_COLOR_ARRAY: + case GL_COLOR_ARRAY_BUFFER_BINDING: + case GL_COLOR_ARRAY_SIZE: + case GL_COLOR_ARRAY_STRIDE: + case GL_COLOR_ARRAY_TYPE: + case GL_COLOR_LOGIC_OP: + case GL_COLOR_MATERIAL: + case GL_COLOR_MATERIAL_FACE: + case GL_COLOR_MATERIAL_PARAMETER: + case GL_COLOR_MATRIX_STACK_DEPTH: + case GL_COLOR_SUM: + case GL_COLOR_TABLE: + case GL_CONVOLUTION_1D: + case GL_CONVOLUTION_2D: + case GL_CULL_FACE: + case GL_CULL_FACE_MODE: + case GL_CURRENT_FOG_COORD: + case GL_CURRENT_INDEX: + case GL_CURRENT_PROGRAM: + case GL_CURRENT_RASTER_DISTANCE: + case GL_CURRENT_RASTER_INDEX: + case GL_CURRENT_RASTER_POSITION_VALID: + case GL_DEPTH_BIAS: + case GL_DEPTH_BITS: + case GL_DEPTH_CLEAR_VALUE: + case GL_DEPTH_FUNC: + case GL_DEPTH_SCALE: + case GL_DEPTH_TEST: + case GL_DEPTH_WRITEMASK: + case GL_DITHER: + case GL_DOUBLEBUFFER: + case GL_DRAW_BUFFER: + case GL_EDGE_FLAG: + case GL_EDGE_FLAG_ARRAY: + case GL_EDGE_FLAG_ARRAY_BUFFER_BINDING: + case GL_EDGE_FLAG_ARRAY_STRIDE: + case GL_ELEMENT_ARRAY_BUFFER_BINDING: + case GL_FEEDBACK_BUFFER_SIZE: + case GL_FEEDBACK_BUFFER_TYPE: + case GL_FOG: + case GL_FOG_COORD_ARRAY: + case GL_FOG_COORD_ARRAY_BUFFER_BINDING: + case GL_FOG_COORD_ARRAY_STRIDE: + case GL_FOG_COORD_ARRAY_TYPE: + case GL_FOG_COORD_SRC: + case GL_FOG_DENSITY: + case GL_FOG_END: + case GL_FOG_HINT: + case GL_FOG_INDEX: + case GL_FOG_MODE: + case GL_FOG_START: + case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: + case GL_FRONT_FACE: + case GL_GENERATE_MIPMAP_HINT: + case GL_GREEN_BIAS: + case GL_GREEN_BITS: + case GL_GREEN_SCALE: + case GL_HISTOGRAM: + case GL_INDEX_ARRAY: + case GL_INDEX_ARRAY_BUFFER_BINDING: + case GL_INDEX_ARRAY_STRIDE: + case GL_INDEX_ARRAY_TYPE: + case GL_INDEX_BITS: + case GL_INDEX_CLEAR_VALUE: + case GL_INDEX_LOGIC_OP: + case GL_INDEX_MODE: + case GL_INDEX_OFFSET: + case GL_INDEX_SHIFT: + case GL_INDEX_WRITEMASK: + case GL_LIGHT0: + case GL_LIGHT1: + case GL_LIGHT2: + case GL_LIGHT3: + case GL_LIGHT4: + case GL_LIGHT5: + case GL_LIGHT6: + case GL_LIGHT7: + case GL_LIGHTING: + case GL_LIGHT_MODEL_COLOR_CONTROL: + case GL_LIGHT_MODEL_LOCAL_VIEWER: + case GL_LIGHT_MODEL_TWO_SIDE: + case GL_LINE_SMOOTH: + case GL_LINE_SMOOTH_HINT: + case GL_LINE_STIPPLE: + case GL_LINE_STIPPLE_PATTERN: + case GL_LINE_STIPPLE_REPEAT: + case GL_LINE_WIDTH: + // GL_LINE_WIDTH_GRANULARITY deprecated see GL_SMOOTH_LINE_WIDTH_GRANULARITY + case GL_LIST_BASE: + case GL_LIST_INDEX: + case GL_LIST_MODE: + // GL_LOGIC_OP same as GL_LOGIC_OP_INDEX + case GL_LOGIC_OP_MODE: + case GL_MAP1_COLOR_4: + case GL_MAP1_GRID_SEGMENTS: + case GL_MAP1_INDEX: + case GL_MAP1_NORMAL: + case GL_MAP1_TEXTURE_COORD_1: + case GL_MAP1_TEXTURE_COORD_2: + case GL_MAP1_TEXTURE_COORD_3: + case GL_MAP1_TEXTURE_COORD_4: + case GL_MAP1_VERTEX_3: + case GL_MAP1_VERTEX_4: + case GL_MAP2_COLOR_4: + case GL_MAP2_INDEX: + case GL_MAP2_NORMAL: + case GL_MAP2_TEXTURE_COORD_1: + case GL_MAP2_TEXTURE_COORD_2: + case GL_MAP2_TEXTURE_COORD_3: + case GL_MAP2_TEXTURE_COORD_4: + case GL_MAP2_VERTEX_3: + case GL_MAP2_VERTEX_4: + case GL_MAP_COLOR: + case GL_MAP_STENCIL: + case GL_MATRIX_MODE: + case GL_MAX_3D_TEXTURE_SIZE: + case GL_MAX_ATTRIB_STACK_DEPTH: + case GL_MAX_CLIENT_ATTRIB_STACK_DEPTH: + case GL_MAX_CLIP_PLANES: + case GL_MAX_COLOR_MATRIX_STACK_DEPTH: + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: + case GL_MAX_DRAW_BUFFERS: + case GL_MAX_ELEMENTS_INDICES: + case GL_MAX_ELEMENTS_VERTICES: + case GL_MAX_EVAL_ORDER: + case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: + case GL_MAX_LIGHTS: + case GL_MAX_LIST_NESTING: + case GL_MAX_MODELVIEW_STACK_DEPTH: + case GL_MAX_NAME_STACK_DEPTH: + case GL_MAX_PIXEL_MAP_TABLE: + case GL_MAX_PROJECTION_STACK_DEPTH: + case GL_MAX_TEXTURE_COORDS: + case GL_MAX_TEXTURE_IMAGE_UNITS: + case GL_MAX_TEXTURE_LOD_BIAS: + case GL_MAX_TEXTURE_SIZE: + case GL_MAX_TEXTURE_STACK_DEPTH: + case GL_MAX_TEXTURE_UNITS: + case GL_MAX_VARYING_FLOATS: + case GL_MAX_VERTEX_ATTRIBS: + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GL_MAX_VERTEX_UNIFORM_COMPONENTS: + case GL_MINMAX: + case GL_MODELVIEW_STACK_DEPTH: + case GL_NAME_STACK_DEPTH: + case GL_NORMAL_ARRAY: + case GL_NORMAL_ARRAY_BUFFER_BINDING: + case GL_NORMAL_ARRAY_STRIDE: + case GL_NORMAL_ARRAY_TYPE: + case GL_NORMALIZE: + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + case GL_PACK_ALIGNMENT: + case GL_PACK_IMAGE_HEIGHT: + case GL_PACK_LSB_FIRST: + case GL_PACK_ROW_LENGTH: + case GL_PACK_SKIP_IMAGES: + case GL_PACK_SKIP_PIXELS: + case GL_PACK_SKIP_ROWS: + case GL_PACK_SWAP_BYTES: + case GL_PERSPECTIVE_CORRECTION_HINT: + case GL_PIXEL_MAP_A_TO_A_SIZE: + case GL_PIXEL_MAP_B_TO_B_SIZE: + case GL_PIXEL_MAP_G_TO_G_SIZE: + case GL_PIXEL_MAP_I_TO_A_SIZE: + case GL_PIXEL_MAP_I_TO_B_SIZE: + case GL_PIXEL_MAP_I_TO_G_SIZE: + case GL_PIXEL_MAP_I_TO_I_SIZE: + case GL_PIXEL_MAP_I_TO_R_SIZE: + case GL_PIXEL_MAP_R_TO_R_SIZE: + case GL_PIXEL_MAP_S_TO_S_SIZE: + case GL_PIXEL_PACK_BUFFER_BINDING: + case GL_PIXEL_UNPACK_BUFFER_BINDING: + case GL_POINT_FADE_THRESHOLD_SIZE: + case GL_POINT_SIZE: + // GL_POINT_SIZE_GRANULARITY deprecated see GL_SMOOTH_POINT_SIZE_GRANULARITY + case GL_POINT_SIZE_MAX: + case GL_POINT_SIZE_MIN: + case GL_POINT_SMOOTH: + case GL_POINT_SMOOTH_HINT: + case GL_POINT_SPRITE: + case GL_POLYGON_OFFSET_FACTOR: + case GL_POLYGON_OFFSET_UNITS: + case GL_POLYGON_OFFSET_FILL: + case GL_POLYGON_OFFSET_LINE: + case GL_POLYGON_OFFSET_POINT: + case GL_POLYGON_SMOOTH: + case GL_POLYGON_SMOOTH_HINT: + case GL_POLYGON_STIPPLE: + case GL_POST_COLOR_MATRIX_COLOR_TABLE: + case GL_POST_COLOR_MATRIX_RED_BIAS: + case GL_POST_COLOR_MATRIX_GREEN_BIAS: + case GL_POST_COLOR_MATRIX_BLUE_BIAS: + case GL_POST_COLOR_MATRIX_ALPHA_BIAS: + case GL_POST_COLOR_MATRIX_RED_SCALE: + case GL_POST_COLOR_MATRIX_GREEN_SCALE: + case GL_POST_COLOR_MATRIX_BLUE_SCALE: + case GL_POST_COLOR_MATRIX_ALPHA_SCALE: + case GL_POST_CONVOLUTION_COLOR_TABLE: + case GL_POST_CONVOLUTION_RED_BIAS: + case GL_POST_CONVOLUTION_GREEN_BIAS: + case GL_POST_CONVOLUTION_BLUE_BIAS: + case GL_POST_CONVOLUTION_ALPHA_BIAS: + case GL_POST_CONVOLUTION_RED_SCALE: + case GL_POST_CONVOLUTION_GREEN_SCALE: + case GL_POST_CONVOLUTION_BLUE_SCALE: + case GL_POST_CONVOLUTION_ALPHA_SCALE: + case GL_PROJECTION_STACK_DEPTH: + case GL_READ_BUFFER: + case GL_RED_BIAS: + case GL_RED_BITS: + case GL_RED_SCALE: + case GL_RENDER_MODE: + case GL_RESCALE_NORMAL: + case GL_RGBA_MODE: + case GL_SAMPLE_BUFFERS: + case GL_SAMPLE_COVERAGE_VALUE: + case GL_SAMPLE_COVERAGE_INVERT: + case GL_SAMPLES: + case GL_SCISSOR_TEST: + case GL_SECONDARY_COLOR_ARRAY: + case GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING: + case GL_SECONDARY_COLOR_ARRAY_SIZE: + case GL_SECONDARY_COLOR_ARRAY_STRIDE: + case GL_SECONDARY_COLOR_ARRAY_TYPE: + case GL_SELECTION_BUFFER_SIZE: + case GL_SEPARABLE_2D: + case GL_SMOOTH_LINE_WIDTH_GRANULARITY: + case GL_SMOOTH_POINT_SIZE_GRANULARITY: + case GL_SHADE_MODEL: + case GL_STENCIL_BACK_FAIL: + case GL_STENCIL_BACK_FUNC: + case GL_STENCIL_BACK_PASS_DEPTH_FAIL: + case GL_STENCIL_BACK_PASS_DEPTH_PASS: + case GL_STENCIL_BACK_REF: + case GL_STENCIL_BACK_VALUE_MASK: + case GL_STENCIL_BACK_WRITEMASK: + case GL_STENCIL_BITS: + case GL_STENCIL_CLEAR_VALUE: + case GL_STENCIL_FAIL: + case GL_STENCIL_FUNC: + case GL_STENCIL_PASS_DEPTH_FAIL: + case GL_STENCIL_PASS_DEPTH_PASS: + case GL_STENCIL_REF: + case GL_STENCIL_TEST: + case GL_STENCIL_VALUE_MASK: + case GL_STENCIL_WRITEMASK: + case GL_STEREO: + case GL_SUBPIXEL_BITS: + case GL_TEXTURE_1D: + case GL_TEXTURE_BINDING_1D: + case GL_TEXTURE_2D: + case GL_TEXTURE_BINDING_2D: + case GL_TEXTURE_3D: + case GL_TEXTURE_BINDING_3D: + case GL_TEXTURE_BINDING_CUBE_MAP: + case GL_TEXTURE_COMPRESSION_HINT: + case GL_TEXTURE_COORD_ARRAY: + case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: + case GL_TEXTURE_COORD_ARRAY_SIZE: + case GL_TEXTURE_COORD_ARRAY_STRIDE: + case GL_TEXTURE_COORD_ARRAY_TYPE: + case GL_TEXTURE_CUBE_MAP: + case GL_TEXTURE_ENV_MODE: + case GL_TEXTURE_GEN_S: + case GL_TEXTURE_GEN_R: + case GL_TEXTURE_GEN_T: + case GL_TEXTURE_GEN_Q: + case GL_TEXTURE_STACK_DEPTH: + case GL_UNPACK_ALIGNMENT: + case GL_UNPACK_IMAGE_HEIGHT: + case GL_UNPACK_LSB_FIRST: + case GL_UNPACK_ROW_LENGTH: + case GL_UNPACK_SKIP_IMAGES: + case GL_UNPACK_SKIP_PIXELS: + case GL_UNPACK_SKIP_ROWS: + case GL_UNPACK_SWAP_BYTES: + case GL_VERTEX_ARRAY: + case GL_VERTEX_ARRAY_BUFFER_BINDING: + case GL_VERTEX_ARRAY_SIZE: + case GL_VERTEX_ARRAY_STRIDE: + case GL_VERTEX_ARRAY_TYPE: + case GL_VERTEX_PROGRAM_POINT_SIZE: + case GL_VERTEX_PROGRAM_TWO_SIDE: + case GL_ZOOM_X: + case GL_ZOOM_Y: + retSize = 1; + break; + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_ALIASED_LINE_WIDTH_RANGE: + case GL_DEPTH_RANGE: + // GL_LINE_WIDTH_RANGE deprecated see GL_SMOOTH_LINE_WIDTH_RANGE + case GL_MAP1_GRID_DOMAIN: + case GL_MAP2_GRID_SEGMENTS: + case GL_MAX_VIEWPORT_DIMS: + // GL_POINT_SIZE_RANGE deprecated see GL_SMOOTH_POINT_SIZE_RANGE + case GL_POLYGON_MODE: + case GL_SMOOTH_LINE_WIDTH_RANGE: + case GL_SMOOTH_POINT_SIZE_RANGE: + retSize = 2; + break; + case GL_CURRENT_NORMAL: + case GL_POINT_DISTANCE_ATTENUATION: + retSize = 3; + break; + case GL_ACCUM_CLEAR_VALUE: + case GL_BLEND_COLOR: + case GL_COLOR_CLEAR_VALUE: + case GL_COLOR_WRITEMASK: + case GL_CURRENT_COLOR: + case GL_CURRENT_RASTER_COLOR: + case GL_CURRENT_RASTER_POSITION: + case GL_CURRENT_RASTER_SECONDARY_COLOR: + case GL_CURRENT_RASTER_TEXTURE_COORDS: + case GL_CURRENT_SECONDARY_COLOR: + case GL_CURRENT_TEXTURE_COORDS: + case GL_FOG_COLOR: + case GL_LIGHT_MODEL_AMBIENT: + case GL_MAP2_GRID_DOMAIN: + case GL_SCISSOR_BOX: + case GL_TEXTURE_ENV_COLOR: + case GL_VIEWPORT: + retSize = 4; + break; + case GL_COLOR_MATRIX: + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + case GL_TEXTURE_MATRIX: + case GL_TRANSPOSE_COLOR_MATRIX: + case GL_TRANSPOSE_MODELVIEW_MATRIX: + case GL_TRANSPOSE_PROJECTION_MATRIX: + case GL_TRANSPOSE_TEXTURE_MATRIX: + retSize = 16; + break; + } + + return retSize; +} + +/**************************************************************************/ + +BEGIN_METHOD(GLGETBOOLEANV, GB_INTEGER parameter) + + GB_ARRAY bArray; + int size = checkSize(VARG(parameter)); + + if (!size) + { + GB.Error("Unknown parameter"); + return; + } + + int i; + GLboolean boolArray[size]; + + GB.Array.New(&bArray , GB_T_BOOLEAN , size); + glGetBooleanv(VARG(parameter), boolArray); + + for (i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLINFO_H +#define __GLINFO_H + +#include "main.h" + +DECLARE_METHOD(GLGETBOOLEANV); +DECLARE_METHOD(GLGETFLOATV); +DECLARE_METHOD(GLGETINTEGERV); +DECLARE_METHOD(GLGETSTRING); + +#endif /* __GLINFO_H */ diff --git a/gb.opengl/src/GLmodesExec.c b/gb.opengl/src/GLmodesExec.c new file mode 100644 index 00000000..43d42a37 --- /dev/null +++ b/gb.opengl/src/GLmodesExec.c @@ -0,0 +1,64 @@ +/*************************************************************************** + + GLmodesExec.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLMODESEXEC_C + +#include "GL.h" + +/**************************************************************************/ + +BEGIN_METHOD(GLDISABLE, GB_INTEGER capacity) + + glDisable(VARG(capacity)); + +END_METHOD + +BEGIN_METHOD(GLENABLE, GB_INTEGER capacity) + + glEnable(VARG(capacity)); + +END_METHOD + +BEGIN_METHOD_VOID(GLFLUSH) + + glFlush(); + +END_METHOD + +BEGIN_METHOD_VOID(GLFINISH) + + glFinish(); + +END_METHOD + +BEGIN_METHOD(GLHINT, GB_INTEGER target; GB_INTEGER mode) + + glHint(VARG(target), VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLISENABLED, GB_INTEGER capacity) + + GB.ReturnBoolean(glIsEnabled(VARG(capacity))); + +END_METHOD diff --git a/gb.opengl/src/GLmodesExec.h b/gb.opengl/src/GLmodesExec.h new file mode 100644 index 00000000..83b610e5 --- /dev/null +++ b/gb.opengl/src/GLmodesExec.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + GLmodesExec.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLMODESEXEC_H +#define __GLMODESEXEC_H + +#include "main.h" + +DECLARE_METHOD(GLDISABLE); +DECLARE_METHOD(GLENABLE); +DECLARE_METHOD(GLFLUSH); +DECLARE_METHOD(GLFINISH); +DECLARE_METHOD(GLHINT); +DECLARE_METHOD(GLISENABLED); + +#endif /* __GLMODESEXEC_H */ diff --git a/gb.opengl/src/GLpixelOperations.c b/gb.opengl/src/GLpixelOperations.c new file mode 100644 index 00000000..19208bef --- /dev/null +++ b/gb.opengl/src/GLpixelOperations.c @@ -0,0 +1,75 @@ +/*************************************************************************** + + GLpixelOperations.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPIXELOPERATIONS_C + +#include "GL.h" + +BEGIN_METHOD(GLCOPYPIXELS, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; GB_INTEGER type) + + glCopyPixels(VARG(x), VARG(y), VARG(width), VARG(height), VARG(type)); + +END_METHOD + +BEGIN_METHOD(GLPIXELSTOREF, GB_INTEGER pname; GB_FLOAT param) + + glPixelStoref(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLPIXELSTOREI, GB_INTEGER pname; GB_INTEGER param) + + glPixelStorei(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLPIXELTRANSFERF, GB_INTEGER pname; GB_FLOAT param) + + glPixelTransferf(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLPIXELTRANSFERI, GB_INTEGER pname; GB_INTEGER param) + + glPixelTransferi(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLREADBUFFER, GB_INTEGER mode) + + glReadBuffer(VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLDRAWPIXELS, GB_OBJECT Image) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glDrawPixels(image->width, image->height, format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + diff --git a/gb.opengl/src/GLpixelOperations.h b/gb.opengl/src/GLpixelOperations.h new file mode 100644 index 00000000..804736e1 --- /dev/null +++ b/gb.opengl/src/GLpixelOperations.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + GLpixelOperations.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLPIXELOPERATIONS_H +#define __GLPIXELOPERATIONS_H + +#include "main.h" + +DECLARE_METHOD(GLCOPYPIXELS); +DECLARE_METHOD(GLPIXELSTOREF); +DECLARE_METHOD(GLPIXELSTOREI); +DECLARE_METHOD(GLPIXELTRANSFERF); +DECLARE_METHOD(GLPIXELTRANSFERI); +DECLARE_METHOD(GLREADBUFFER); +DECLARE_METHOD(GLDRAWPIXELS); + +#endif /* __GLPIXELOPERATIONS_H */ diff --git a/gb.opengl/src/GLprimitives.c b/gb.opengl/src/GLprimitives.c new file mode 100644 index 00000000..7a4d5792 --- /dev/null +++ b/gb.opengl/src/GLprimitives.c @@ -0,0 +1,178 @@ +/*************************************************************************** + + GLprimitives.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPRIMITIVES_C + +#include "GL.h" + +#include + +BEGIN_METHOD(GLBEGIN, GB_INTEGER primitive) + + glBegin(VARG(primitive)); + +END_METHOD + +BEGIN_METHOD(GLEDGEFLAG, GB_BOOLEAN bValue) + + glEdgeFlag(VARG(bValue)); + +END_METHOD + +BEGIN_METHOD_VOID(GLEND) + + glEnd(); + +END_METHOD + +BEGIN_METHOD(GLRECTF, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2) + + glRectf(VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(GLRECTI, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2) + + glRecti(VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX2F, GB_FLOAT x; GB_FLOAT y) + + glVertex2d(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX3F, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + glVertex3d(VARG(x), VARG(y), VARG(z)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXF, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z; GB_FLOAT w) + + if (MISSING(z)) + { + glVertex2d(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glVertex3d(VARG(x), VARG(y), VARG(z)); + return; + } + + glVertex4d(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX2I, GB_INTEGER x; GB_INTEGER y) + + glVertex2i(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX3I, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z) + + glVertex3i(VARG(x), VARG(y), VARG(z)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXI, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z; GB_INTEGER w) + + if (MISSING(z)) + { + glVertex2i(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glVertex3i(VARG(x), VARG(y), VARG(z)); + return; + } + + glVertex4i(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXFV, GB_OBJECT array) + + GLfloat x,y,z,w; + GB_ARRAY vertex = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(vertex); + + if (count<2) + return; + + x = *((double *)GB.Array.Get(vertex,0)); + y = *((double *)GB.Array.Get(vertex,1)); + + if (count==2) + { + glVertex2d(x, y); + return; + } + + z = *((double *)GB.Array.Get(vertex,2)); + + if (count==3) + glVertex3d(x, y, z); + else + { + w = *((double *)GB.Array.Get(vertex,3)); + glVertex4d(x, y, z, w); + } + +END_METHOD + +BEGIN_METHOD(GLVERTEXIV, GB_OBJECT array) + + GLint x,y,z,w; + GB_ARRAY vertex = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(vertex); + + if (count<2) + return; + + x = *((GLint *)GB.Array.Get(vertex,0)); + y = *((GLint *)GB.Array.Get(vertex,1)); + + if (count==2) + { + glVertex2i(x, y); + return; + } + + z = *((GLint *)GB.Array.Get(vertex,2)); + + if (count==3) + glVertex3i(x, y, z); + else + { + w = *((GLint *)GB.Array.Get(vertex,3)); + glVertex4i(x, y, z, w); + } + +END_METHOD diff --git a/gb.opengl/src/GLprimitives.h b/gb.opengl/src/GLprimitives.h new file mode 100644 index 00000000..b7c838bd --- /dev/null +++ b/gb.opengl/src/GLprimitives.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + GLprimitives.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLPRIMITIVES_H +#define __GLPRIMITIVES_H + +#include "main.h" + +DECLARE_METHOD(GLBEGIN); +DECLARE_METHOD(GLEDGEFLAG); +DECLARE_METHOD(GLEND); +DECLARE_METHOD(GLRECTF); +DECLARE_METHOD(GLRECTI); +DECLARE_METHOD(GLVERTEX2F); +DECLARE_METHOD(GLVERTEX3F); +DECLARE_METHOD(GLVERTEXF); +DECLARE_METHOD(GLVERTEX2I); +DECLARE_METHOD(GLVERTEX3I); +DECLARE_METHOD(GLVERTEXI); +DECLARE_METHOD(GLVERTEXFV); +DECLARE_METHOD(GLVERTEXIV); + +#endif /* __GLPRIMITIVES_H */ diff --git a/gb.opengl/src/GLrasterization.c b/gb.opengl/src/GLrasterization.c new file mode 100644 index 00000000..2f2f3f11 --- /dev/null +++ b/gb.opengl/src/GLrasterization.c @@ -0,0 +1,214 @@ +/*************************************************************************** + + GLrasterization.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLRASTERIZATION_C + +#include "GL.h" + +BEGIN_METHOD_VOID(GLBITMAP) + + WARNING("glBitmap() Not implemented !"); + +END_METHOD + +BEGIN_METHOD(GLCULLFACE, GB_INTEGER mode) + + glCullFace(VARG(mode)); + +END_METHOD + +BEGIN_METHOD_VOID(GLGETPOLYGONSTIPPLE) + + WARNING("glGetPolygonStipple() Not implemented !"); + +END_METHOD + +BEGIN_METHOD(GLLINESTIPPLE, GB_INTEGER factor; GB_INTEGER pattern) + + glLineStipple(VARG(factor), VARG(pattern)); + +END_METHOD + +BEGIN_METHOD(GLLINEWIDTH, GB_FLOAT width) + + glLineWidth(VARG(width)); + +END_METHOD + +BEGIN_METHOD(GLPOINTSIZE, GB_FLOAT size) + + glPointSize(VARG(size)); + +END_PROPERTY + +BEGIN_METHOD(GLPOLYGONMODE, GB_INTEGER face; GB_INTEGER mode) + + glPolygonMode(VARG(face), VARG(mode)); + +END_METHOD +#if 0 +BEGIN_METHOD(GLPOLYGONSTIPPLE, GB_INTEGER mask) + + GLubyte mask[4*32]; + GB_ARRAY iArray = (GB_ARRAY) VARG(mask); + uint i; + + if (GB.Array.Count(iArray) != 32) + return; + + for (i=0; i<32; i++) + mask[i*4] = *((GLint *)GB.Array.Get(iArray,i)); + + glPolygonStipple(mask); + +END_METHOD +#endif +BEGIN_METHOD(GLPOLYGONOFFSET, GB_FLOAT factor; GB_FLOAT units) + + glPolygonOffset(VARG(factor), VARG(units)); + +END_METHOD + +BEGIN_METHOD(GLRASTERPOS2F, GB_FLOAT x; GB_FLOAT y) + + glRasterPos2d(VARG(x), VARG(y)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOS3F, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + glRasterPos3d(VARG(x), VARG(y), VARG(z)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOSF, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z; GB_FLOAT w) + + if (MISSING(z)) + { + glRasterPos2d(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glRasterPos3d(VARG(x), VARG(y), VARG(z)); + return; + } + + glRasterPos4d(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOS2I, GB_INTEGER x; GB_INTEGER y) + + glRasterPos2i(VARG(x), VARG(y)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOS3I, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z) + + glRasterPos3i(VARG(x), VARG(y), VARG(z)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOSI, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z; GB_INTEGER w) + + if (MISSING(z)) + { + glRasterPos2i(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glRasterPos3i(VARG(x), VARG(y), VARG(z)); + return; + } + + glRasterPos4i(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOSFV, GB_OBJECT array) + + GLdouble x,y,z,w; + GB_ARRAY fArray = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(fArray); + + if (count<2) + return; + + x = *((GLdouble *)GB.Array.Get(fArray,0)); + y = *((GLdouble *)GB.Array.Get(fArray,1)); + + if (count==2) + { + glRasterPos2d(x, y); + return; + } + + z = *((GLdouble *)GB.Array.Get(fArray,2)); + + if (count==3) + { + glRasterPos3d(x, y, z); + } + else + { + w = *((double *)GB.Array.Get(fArray,3)); + glRasterPos4d(x, y, z, w); + } + +END_METHOD + +BEGIN_METHOD(GLRASTERPOSIV, GB_OBJECT array) + + GLint x,y,z,w; + GB_ARRAY fArray = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(fArray); + + if (count<2) + return; + + x = *((GLint *)GB.Array.Get(fArray,0)); + y = *((GLint *)GB.Array.Get(fArray,1)); + + if (count==2) + { + glRasterPos2i(x, y); + return; + } + + z = *((GLint *)GB.Array.Get(fArray,2)); + + if (count==3) + { + glRasterPos3i(x, y, z); + } + else + { + w = *((double *)GB.Array.Get(fArray,3)); + glRasterPos4i(x, y, z, w); + } + +END_METHOD diff --git a/gb.opengl/src/GLrasterization.h b/gb.opengl/src/GLrasterization.h new file mode 100644 index 00000000..aa186084 --- /dev/null +++ b/gb.opengl/src/GLrasterization.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + GLrasterization.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLRASTERIZATION_H +#define __GLRASTERIZATION_H + +#include "main.h" + +// TODO +DECLARE_METHOD(GLBITMAP); +DECLARE_METHOD(GLPOLYGONSTIPPLE); +DECLARE_METHOD(GLGETPOLYGONSTIPPLE); +// +DECLARE_METHOD(GLCULLFACE); +DECLARE_METHOD(GLLINESTIPPLE); +DECLARE_METHOD(GLLINEWIDTH); +DECLARE_METHOD(GLPOINTSIZE); +DECLARE_METHOD(GLPOLYGONMODE); +DECLARE_METHOD(GLPOLYGONOFFSET); +DECLARE_METHOD(GLRASTERPOS2F); +DECLARE_METHOD(GLRASTERPOS3F); +DECLARE_METHOD(GLRASTERPOSF); +DECLARE_METHOD(GLRASTERPOS2I); +DECLARE_METHOD(GLRASTERPOS3I); +DECLARE_METHOD(GLRASTERPOSI); +DECLARE_METHOD(GLRASTERPOSFV); +DECLARE_METHOD(GLRASTERPOSIV); + +#endif /* __GLRASTERIZATION_H */ diff --git a/gb.opengl/src/GLselectFeedback.c b/gb.opengl/src/GLselectFeedback.c new file mode 100644 index 00000000..1280126f --- /dev/null +++ b/gb.opengl/src/GLselectFeedback.c @@ -0,0 +1,150 @@ +/*************************************************************************** + + GLselectFeedback.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLSELECTPIXMAP_C + +#include "GL.h" + +#define MAX_BUFFER_SIZE 2048 + +/* for feedback and select buffers */ +static GLuint selectbuffer[MAX_BUFFER_SIZE]; +static GLfloat feedbuffer[MAX_BUFFER_SIZE]; + +BEGIN_METHOD(GLFEEDBACKBUFFER, GB_INTEGER Type) + + glFeedbackBuffer(MAX_BUFFER_SIZE, VARG(Type), feedbuffer); + +END_METHOD + +BEGIN_METHOD_VOID(GLINITNAMES) + + glInitNames(); + +END_METHOD + +BEGIN_METHOD(GLLOADNAME, GB_INTEGER Name) + + glLoadName(VARG(Name)); + +END_METHOD + +BEGIN_METHOD(GLPASSTHROUGH, GB_FLOAT Token) + + glPassThrough(VARG(Token)); + +END_METHOD + +BEGIN_METHOD_VOID(GLPOPNAME) + + glPopName(); + +END_METHOD + +BEGIN_METHOD(GLPUSHNAME, GB_INTEGER Name) + + glPushName(VARG(Name)); + +END_METHOD + +BEGIN_METHOD(GLRENDERMODE, GB_INTEGER Mode) + + static GLuint oldrendermode = GL_RENDER; /* default render mode */ + GLint result, mode = VARG(Mode); + + /* invalid mode or same render mode: + return null object, and let propagate an opengl error */ + if ((mode < GL_RENDER) || (mode > GL_SELECT) || (mode == oldrendermode)) + { + GB.ReturnNull(); + glRenderMode(mode); + return; + } + + result = glRenderMode(mode); + + if (!result) + { + GB.ReturnNull(); + oldrendermode = mode; + return; + } + + if (result < 0) + { + GB.Error ("Gl.RenderMode, buffer is too small !"); + return; + } + + if (oldrendermode == GL_SELECT) + { + GB_ARRAY hitArray; + GLuint *hitbuffer = selectbuffer; + int idxhit, idxname; + + GB.Array.New(&hitArray, GB.FindClass("Integer[]"), result); + //GB.New(POINTER(&hitArray), GB.FindClass("Integer[][]"), NULL, NULL); + + for (idxhit=0; idxhit < result; idxhit++) + { + GB_ARRAY childhitArray; + + int names = *hitbuffer; + GB.Array.New(&childhitArray, GB_T_INTEGER, names + 3); + *((GLuint *)GB.Array.Get(childhitArray, 0)) = *hitbuffer++; // maxname + *((GLuint *)GB.Array.Get(childhitArray, 1)) = *hitbuffer++; // zmin + *((GLuint *)GB.Array.Get(childhitArray, 2)) = *hitbuffer++; // zmax + + for (idxname=0; idxname < names; idxname++) + *((GLuint *)GB.Array.Get(childhitArray, 3 + idxname)) = *hitbuffer++; // names + + GB.Ref(childhitArray); + *((GB_ARRAY *)GB.Array.Get(hitArray, idxhit)) = childhitArray; + } + + GB.ReturnObject(hitArray); + return; + } + + if (oldrendermode == GL_FEEDBACK) + { + GB_ARRAY feedArray; + GLfloat *resultbuffer = feedbuffer; + int idxfeed; + + GB.Array.New(&feedArray, GB_T_FLOAT, result); + + for (idxfeed=0; idxfeed < result; idxfeed++) + *((GLfloat *)GB.Array.Get(feedArray, idxfeed)) = *resultbuffer++; + + GB.ReturnObject(feedArray); + return; + } + +END_METHOD + +BEGIN_METHOD_VOID(GLSELECTBUFFER) + + glSelectBuffer(MAX_BUFFER_SIZE, selectbuffer); + +END_METHOD diff --git a/gb.opengl/src/GLselectFeedback.h b/gb.opengl/src/GLselectFeedback.h new file mode 100644 index 00000000..91026d43 --- /dev/null +++ b/gb.opengl/src/GLselectFeedback.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + GLselectFeedback.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLSELECTPIXMAP_H +#define __GLSELECTPIXMAP_H + +#include "main.h" + +DECLARE_METHOD(GLFEEDBACKBUFFER); +DECLARE_METHOD(GLINITNAMES); +DECLARE_METHOD(GLLOADNAME); +DECLARE_METHOD(GLPASSTHROUGH); +DECLARE_METHOD(GLPOPNAME); +DECLARE_METHOD(GLPUSHNAME); +DECLARE_METHOD(GLRENDERMODE); +DECLARE_METHOD(GLSELECTBUFFER); + +#endif /* __GLSELECTPIXMAP_H */ diff --git a/gb.opengl/src/GLtextureMapping.c b/gb.opengl/src/GLtextureMapping.c new file mode 100644 index 00000000..e548c77c --- /dev/null +++ b/gb.opengl/src/GLtextureMapping.c @@ -0,0 +1,334 @@ +/*************************************************************************** + + GLtextureMapping.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLTEXTUREMAPPING_C + +#include "GL.h" + +BEGIN_METHOD(GLBINDTEXTURE, GB_INTEGER target; GB_INTEGER texture) + + glBindTexture(VARG(target), VARG(texture)); + +END_METHOD + +BEGIN_METHOD(GLACTIVETEXTURE, GB_INTEGER texture) + + glActiveTexture(VARG(texture)); + +END_METHOD + +BEGIN_METHOD(GLCOPYTEXIMAGE1D, GB_INTEGER Ta; GB_INTEGER Le; GB_INTEGER Fo; GB_INTEGER X; GB_INTEGER Y; GB_INTEGER Wi; GB_INTEGER Bo) + + glCopyTexImage1D(VARG(Ta), VARG(Le), VARG(Fo), VARG(X), VARG(Y), VARG(Wi), VARG(Bo)); + +END_METHOD + +BEGIN_METHOD(GLCOPYTEXIMAGE2D, GB_INTEGER Ta; GB_INTEGER Le; GB_INTEGER Fo; GB_INTEGER X; GB_INTEGER Y; GB_INTEGER Wi; GB_INTEGER He; GB_INTEGER Bo) + + glCopyTexImage2D(VARG(Ta), VARG(Le), VARG(Fo), VARG(X), VARG(Y), VARG(Wi), VARG(He), VARG(Bo)); + +END_METHOD + +BEGIN_METHOD(GLDELETETEXTURES, GB_OBJECT textures) + + + GB_ARRAY iArray = (GB_ARRAY) VARG(textures); + int i,count = GB.Array.Count(iArray); + GLuint texture[1]; + + if (count<=0) + return; + + for (i=0;i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; iwidth, VARGOPT(Border, 0), + format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXIMAGE2D, GB_OBJECT Image; GB_INTEGER Level; GB_INTEGER Border) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glTexImage2D(GL_TEXTURE_2D, VARGOPT(Level, 0), IMAGE_get_ncolors(format), image->width, image->height, + VARGOPT(Border, 0), format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXSUBIMAGE1D, GB_OBJECT Image; GB_INTEGER XOffset; GB_INTEGER Width; GB_INTEGER Level) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glTexSubImage1D(GL_TEXTURE_1D, VARGOPT(Level, 0), VARG(XOffset), VARG(Width), + format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXSUBIMAGE2D, GB_OBJECT Image; GB_INTEGER XOffset; GB_INTEGER YOffset; GB_INTEGER Width; GB_INTEGER Height; GB_INTEGER Level) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glTexSubImage2D(GL_TEXTURE_2D, VARGOPT(Level, 0), VARG(XOffset), VARG(YOffset), VARG(Width), VARG(Height), + format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXPARAMETERF, GB_INTEGER Target; GB_INTEGER Pname; GB_FLOAT Param) + + glTexParameterf(VARG(Target), VARG(Pname), VARG(Param)); + +END_METHOD + +BEGIN_METHOD(GLTEXPARAMETERFV, GB_INTEGER Target; GB_INTEGER Pname; GB_OBJECT Params) + + GLfloat params[4]; + GB_ARRAY fArray = (GB_ARRAY) VARG(Params); + int count = GB.Array.Count(fArray); + int i; + + count = (count > 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLTEXTUREMAPPING_H +#define __GLTEXTUREMAPPING_H + +#include "main.h" + +DECLARE_METHOD(GLBINDTEXTURE); +DECLARE_METHOD(GLACTIVETEXTURE); +DECLARE_METHOD(GLCOPYTEXIMAGE1D); +DECLARE_METHOD(GLCOPYTEXIMAGE2D); +DECLARE_METHOD(GLDELETETEXTURES); +DECLARE_METHOD(GLGENTEXTURES); +DECLARE_METHOD(GLISTEXTURE); +DECLARE_METHOD(GLTEXCOORD1F); +DECLARE_METHOD(GLTEXCOORD2F); +DECLARE_METHOD(GLTEXCOORD3F); +DECLARE_METHOD(GLTEXCOORDF); +DECLARE_METHOD(GLTEXCOORD1I); +DECLARE_METHOD(GLTEXCOORD2I); +DECLARE_METHOD(GLTEXCOORD3I); +DECLARE_METHOD(GLTEXCOORDI); +DECLARE_METHOD(GLTEXENVF); +DECLARE_METHOD(GLTEXENVFV); +DECLARE_METHOD(GLTEXENVI); +DECLARE_METHOD(GLTEXENVIV); +DECLARE_METHOD(GLTEXIMAGE1D); +DECLARE_METHOD(GLTEXIMAGE2D); +DECLARE_METHOD(GLTEXSUBIMAGE1D); +DECLARE_METHOD(GLTEXSUBIMAGE2D); +DECLARE_METHOD(GLTEXPARAMETERF); +DECLARE_METHOD(GLTEXPARAMETERFV); +DECLARE_METHOD(GLTEXPARAMETERI); +DECLARE_METHOD(GLTEXPARAMETERIV); +DECLARE_METHOD(GLTEXGENI); +DECLARE_METHOD(GLMULTITEXCOORD2F); + +#endif /* __GLTEXTUREMAPPING_H */ diff --git a/gb.opengl/src/Makefile.am b/gb.opengl/src/Makefile.am new file mode 100644 index 00000000..fc741bd4 --- /dev/null +++ b/gb.opengl/src/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.opengl +include $(top_srcdir)/component.am + +SUBDIRS = . @GLSL_DIR@ @GLU_DIR@ @SGE_DIR@ + +gblib_LTLIBRARIES = gb.opengl.la + +gb_opengl_la_LIBADD = @OPENGL_LIB@ +gb_opengl_la_LDFLAGS = -module @LD_FLAGS@ @OPENGL_LDFLAGS@ +gb_opengl_la_CPPFLAGS = @OPENGL_INC@ + +gb_opengl_la_SOURCES = \ + main.h main.c gb.gl.h \ + GL.h GL.c \ + GLinfo.h GLinfo.c \ + GLclipping.c GLclipping.h \ + GLcolorLighting.h GLcolorLighting.c \ + GLcoordTransf.h GLcoordTransf.c \ + GLdisplayList.h GLdisplayList.c \ + GLeval.h GLeval.c \ + GLfog.h GLfog.c \ + GLframeBufferOps.h GLframeBufferOps.c \ + GLmodesExec.h GLmodesExec.c \ + GLprimitives.h GLprimitives.c \ + GLpixelOperations.h GLpixelOperations.c \ + GLrasterization.h GLrasterization.c \ + GLtextureMapping.h GLtextureMapping.c \ + GLselectFeedback.h GLselectFeedback.c \ + framebufferobject.h framebufferobject.c diff --git a/gb.opengl/src/framebufferobject.c b/gb.opengl/src/framebufferobject.c new file mode 100644 index 00000000..3aebad97 --- /dev/null +++ b/gb.opengl/src/framebufferobject.c @@ -0,0 +1,194 @@ +/*************************************************************************** + + framebufferobject.c + + (c) 2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __FBO_C + +#include "GL.h" + +BEGIN_METHOD(GLBINDFRAMEBUFFEREXT, GB_INTEGER target; GB_INTEGER framebuffer) + + glBindFramebufferEXT(VARG(target), VARG(framebuffer)); + +END_METHOD + +BEGIN_METHOD(GLBINDRENDERBUFFEREXT, GB_INTEGER target; GB_INTEGER renderbuffer) + + glBindRenderbufferEXT(VARG(target), VARG(renderbuffer)); + +END_METHOD + +BEGIN_METHOD(GLCHECKFRAMEBUFFERSTATUSEXT, GB_INTEGER target) + + GB.ReturnInteger(glCheckFramebufferStatusEXT(VARG(target))); + +END_METHOD + +BEGIN_METHOD(GLDELETEFRAMEBUFFERSEXT, GB_OBJECT buffers) + + + GB_ARRAY iArray = (GB_ARRAY) VARG(buffers); + int i,count = GB.Array.Count(iArray); + GLuint buffer[1]; + + if (count<=0) + return; + + for (i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __FBO_H +#define __FBO_H + +#include "main.h" +DECLARE_METHOD(GLBINDFRAMEBUFFEREXT); +DECLARE_METHOD(GLBINDRENDERBUFFEREXT); +DECLARE_METHOD(GLCHECKFRAMEBUFFERSTATUSEXT); +DECLARE_METHOD(GLDELETEFRAMEBUFFERSEXT); +DECLARE_METHOD(GLDELETERENDERBUFFERSEXT); +DECLARE_METHOD(GLFRAMEBUFFERRENDERBUFFEREXT); +DECLARE_METHOD(GLFRAMEBUFFERTEXTURE1DEXT); +DECLARE_METHOD(GLFRAMEBUFFERTEXTURE2DEXT); +DECLARE_METHOD(GLFRAMEBUFFERTEXTURE3DEXT); +DECLARE_METHOD(GLGENERATEMIPMAPEXT); +DECLARE_METHOD(GLGENFRAMEBUFFERSEXT); +DECLARE_METHOD(GLGENRENDERBUFFERSEXT); +DECLARE_METHOD(GLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXT); +DECLARE_METHOD(GLGETRENDERBUFFERPARAMETERIVEXT); +DECLARE_METHOD(GLISFRAMEBUFFEREXT); +DECLARE_METHOD(GLISRENDERBUFFEREXT); +DECLARE_METHOD(GLRENDERBUFFERSTORAGEEXT); + +#endif /* __FBO_H */ diff --git a/gb.opengl/src/gb.gl.h b/gb.opengl/src/gb.gl.h new file mode 100644 index 00000000..23d05711 --- /dev/null +++ b/gb.opengl/src/gb.gl.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb.gl.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_GL_H +#define __GB_GL_H + +#include "gambas.h" + +#define GL_INTERFACE_VERSION 1 + +typedef + struct { + intptr_t version; + // Must be called after the context is init ! + //** Perhaps also when context is changed but not tested ** + bool (*Init)(void); + + void *_null; + } + GL_INTERFACE; + +#endif diff --git a/gb.opengl/src/gb.opengl.component b/gb.opengl/src/gb.opengl.component new file mode 100644 index 00000000..49416dd4 --- /dev/null +++ b/gb.opengl/src/gb.opengl.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.opengl +Author=Laurent Carlier +Need=OpenGLViewer +Requires=gb.image +State=Stable diff --git a/gb.opengl/src/glsl/GL.c b/gb.opengl/src/glsl/GL.c new file mode 100644 index 00000000..b664a2a3 --- /dev/null +++ b/gb.opengl/src/glsl/GL.c @@ -0,0 +1,114 @@ +/*************************************************************************** + + GL.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GL_C + +#include "GL.h" + +#include "GLshader.h" +#include "GLprogram.h" +#include "GLuniform.h" +#include "GLattributes.h" + +GB_DESC Cgl[] = +{ + GB_DECLARE("Gl",0), GB_NOT_CREATABLE(), + + /* GLshader.c */ + GB_STATIC_METHOD("AttachShader", NULL, GLATTACHSHADER, "(Program)i(Shader)i"), + GB_STATIC_METHOD("CompileShader", NULL, GLCOMPILESHADER, "(Shader)i"), + GB_STATIC_METHOD("CreateShader", "i", GLCREATESHADER, "(ShaderType)i"), + GB_STATIC_METHOD("DeleteShader", NULL, GLDELETESHADER, "(Shader)i"), + GB_STATIC_METHOD("DetachShader", NULL, GLDETACHSHADER, "(Program)i(Shader)i"), + GB_STATIC_METHOD("GetAttachedShaders", "Integer[]", GLGETATTACHEDSHADERS, "(Program)i"), + GB_STATIC_METHOD("GetShaderInfoLog", "s", GLGETSHADERINFOLOG, "(Shader)i"), + GB_STATIC_METHOD("GetShaderiv", "Integer[]", GLGETSHADERIV, "(Shader)i(Pname)i"), + GB_STATIC_METHOD("GetShaderSource", "s", GLGETSHADERSOURCE, "(Shader)i"), + GB_STATIC_METHOD("IsShader", "b", GLISSHADER, "(Shader)i"), + GB_STATIC_METHOD("ShaderSource", NULL, GLSHADERSOURCE, "(Shader)i(Source)s"), + /* GLprogram.c */ + GB_STATIC_METHOD("CreateProgram", "i", GLCREATEPROGRAM, NULL), + GB_STATIC_METHOD("DeleteProgram", NULL, GLDELETEPROGRAM, "(Program)i"), + GB_STATIC_METHOD("GetProgramInfoLog", "s", GLGETPROGRAMINFOLOG, "(Program)i"), + GB_STATIC_METHOD("GetProgramiv", "Integer[]", GLGETPROGRAMIV, "(Program)i(Pname)i"), + GB_STATIC_METHOD("IsProgram", "b", GLISPROGRAM, "(Program)i"), + GB_STATIC_METHOD("LinkProgram", NULL, GLLINKPROGRAM, "(Program)i"), + GB_STATIC_METHOD("UseProgram", NULL, GLUSEPROGRAM, "(Program)i"), + GB_STATIC_METHOD("ValidateProgram", NULL, GLVALIDATEPROGRAM, "(Program)i"), + /* GLuniform.c */ + GB_STATIC_METHOD("GetUniformLocation", "i", GLGETUNIFORMLOCATION, "(Program)i(Name)s"), + GB_STATIC_METHOD("Uniform1f", NULL, GLUNIFORM1F, "(Location)i(V0)f"), + GB_STATIC_METHOD("Uniform2f", NULL, GLUNIFORM2F, "(Location)i(V0)f(V1)f"), + GB_STATIC_METHOD("Uniform3f", NULL, GLUNIFORM3F, "(Location)i(V0)f(V1)f(V2)f"), + GB_STATIC_METHOD("Uniform4f", NULL, GLUNIFORM4F, "(Location)i(V0)f(V1)f(V3)f(V3)f"), + GB_STATIC_METHOD("Uniform1i", NULL, GLUNIFORM1I, "(Location)i(V0)i"), + GB_STATIC_METHOD("Uniform2i", NULL, GLUNIFORM2I, "(Location)i(V0)i(V1)i"), + GB_STATIC_METHOD("Uniform3i", NULL, GLUNIFORM3I, "(Location)i(V0)i(V1)i(V2)i"), + GB_STATIC_METHOD("Uniform4i", NULL, GLUNIFORM4I, "(Location)i(V0)i(V1)i(V3)i(V3)i"), + GB_STATIC_METHOD("Uniform1fv", NULL, GLUNIFORM1FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform2fv", NULL, GLUNIFORM2FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform3fv", NULL, GLUNIFORM3FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform4fv", NULL, GLUNIFORM4FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform1iv", NULL, GLUNIFORM1IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("Uniform2iv", NULL, GLUNIFORM2IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("Uniform3iv", NULL, GLUNIFORM3IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("Uniform4iv", NULL, GLUNIFORM4IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("UniformMatrix2fv", NULL, GLUNIFORMMATRIX2FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix3fv", NULL, GLUNIFORMMATRIX3FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix4fv", NULL, GLUNIFORMMATRIX4FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix2x3fv", NULL, GLUNIFORMMATRIX2X3FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix3x2fv", NULL, GLUNIFORMMATRIX3X2FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix2x4fv", NULL, GLUNIFORMMATRIX2X4FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix4x2fv", NULL, GLUNIFORMMATRIX4X2FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix3x4fv", NULL, GLUNIFORMMATRIX3X4FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix4x3fv", NULL, GLUNIFORMMATRIX4X3FV, "(Location)i(Transpose)b(Values)Float[]"), + /* GLattributes.c */ + GB_STATIC_METHOD("BindAttribLocation",NULL, GLBINDATTRIBLOCATION, "(Program)i(Index)i(Name)s"), + GB_STATIC_METHOD("VertexAttrib1f", NULL, GLVERTEXATTRIB1F, "(Index)i(X)f"), + GB_STATIC_METHOD("VertexAttrib1fv", NULL, GLVERTEXATTRIB1FV, "(Index)i(V)Float[]"), + GB_STATIC_METHOD("VertexAttrib2f", NULL, GLVERTEXATTRIB2F, "(Index)i(X)f(Y)f"), + GB_STATIC_METHOD("VertexAttrib2fv", NULL, GLVERTEXATTRIB2FV, "(Index)i(V)Float[]"), + GB_STATIC_METHOD("VertexAttrib3f", NULL, GLVERTEXATTRIB3F, "(Index)i(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("VertexAttrib3fv", NULL, GLVERTEXATTRIB3FV, "(Index)i(V)Float[]"), + GB_STATIC_METHOD("VertexAttrib4f", NULL, GLVERTEXATTRIB4F, "(Index)i(X)f(Y)f(Z)f(W)f"), + GB_STATIC_METHOD("VertexAttrib4fv", NULL, GLVERTEXATTRIB4FV, "(Index)i(V)Float[]"), + + /* Contants */ + GB_CONSTANT("ACTIVE_ATTRIBUTES", "i", GL_ACTIVE_ATTRIBUTES), + GB_CONSTANT("ACTIVE_ATTRIBUTE_MAX_LENGTH", "i", GL_ACTIVE_ATTRIBUTE_MAX_LENGTH), + GB_CONSTANT("ACTIVE_UNIFORMS", "i", GL_ACTIVE_UNIFORMS), + GB_CONSTANT("ACTIVE_UNIFORM_MAX_LENGTH", "i", GL_ACTIVE_UNIFORM_MAX_LENGTH), + GB_CONSTANT("ATTACHED_SHADERS", "i", GL_ATTACHED_SHADERS), + GB_CONSTANT("COMPILE_STATUS", "i", GL_COMPILE_STATUS), + GB_CONSTANT("CURRENT_PROGRAM", "i", GL_CURRENT_PROGRAM), + GB_CONSTANT("DELETE_STATUS", "i", GL_DELETE_STATUS), + GB_CONSTANT("FRAGMENT_SHADER", "i", GL_FRAGMENT_SHADER), + GB_CONSTANT("INFO_LOG_LENGTH", "i", GL_INFO_LOG_LENGTH), + GB_CONSTANT("LINK_STATUS", "i", GL_LINK_STATUS), + GB_CONSTANT("SHADER_SOURCE_LENGTH", "i", GL_SHADER_SOURCE_LENGTH), + GB_CONSTANT("SHADER_TYPE", "i", GL_SHADER_TYPE), + GB_CONSTANT("VALIDATE_STATUS", "i", GL_VALIDATE_STATUS), + GB_CONSTANT("VERTEX_SHADER", "i", GL_VERTEX_SHADER), + + GB_END_DECLARE +}; diff --git a/gb.opengl/src/glsl/GL.h b/gb.opengl/src/glsl/GL.h new file mode 100644 index 00000000..0d057e40 --- /dev/null +++ b/gb.opengl/src/glsl/GL.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + GL.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GL_H +#define __GL_H + +#include "main.h" + +#include + +#ifndef __GL_C +extern GB_DESC Cgl[]; +#endif /* __GL_C */ + +#endif /* __GL_H */ diff --git a/gb.opengl/src/glsl/GLattributes.c b/gb.opengl/src/glsl/GLattributes.c new file mode 100644 index 00000000..891776fb --- /dev/null +++ b/gb.opengl/src/glsl/GLattributes.c @@ -0,0 +1,175 @@ +/*************************************************************************** + + GLattributes.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLATTRIBUTES_C + +#include "GL.h" + + + + + +/*GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); + + +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +//GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); + + + + GB_STATIC_METHOD("glVertexAttrib4Niv", NULL, GLVERTEXATTRIB4NIV, "(Index)i(V)Integer[]"), + GB_STATIC_METHOD("glVertexAttrib4Nuiv", NULL, GLVERTEXATTRIB4NUIV, "(Index)i(V)Integer[]"), + + GB_STATIC_METHOD("glVertexAttrib4iv", NULL, GLVERTEXATTRIB4IV, "(Index)i(V)Integer[]"), + GB_STATIC_METHOD("glVertexAttrib4uiv", NULL, GLVERTEXATTRIB4UIV, "(Index)i(V)Integer[]"), + //GB_STATIC_METHOD("glVertexAttribPointer", NULL, GLVERTEXATTRIBPOINTER, "(Index)i(Size)i(Type)i(Normalized)b(Stride)i(Pointer)p"),*/ + +BEGIN_METHOD(GLBINDATTRIBLOCATION, GB_INTEGER program; GB_INTEGER index; GB_STRING name) + + glBindAttribLocation (VARG(program), VARG(index), GB.ToZeroString(ARG(name))); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB1F, GB_INTEGER index; GB_FLOAT x) + + glVertexAttrib1d(VARG(index), VARG(x)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB2F, GB_INTEGER index; GB_FLOAT x; GB_FLOAT y) + + glVertexAttrib2d(VARG(index), VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB3F, GB_INTEGER index; GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + glVertexAttrib3d(VARG(index), VARG(x), VARG(y), VARG(z)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB4F, GB_INTEGER index; GB_FLOAT x; GB_FLOAT y; GB_FLOAT z; GB_FLOAT w) + + glVertexAttrib4d(VARG(index), VARG(x), VARG(y), VARG(z), VARG(w)); + +END_METHOD + + + +BEGIN_METHOD(GLVERTEXATTRIB1FV, GB_INTEGER index; GB_OBJECT v) + + GB_ARRAY fArray = VARG(v); + int count = GB.Array.Count(fArray); + + if (!count) + return; + + GLdouble values[count]; + int i; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLATTRIBUTES_H +#define __GLATTRIBUTES_H + +#include "main.h" +DECLARE_METHOD(GLBINDATTRIBLOCATION); +DECLARE_METHOD(GLVERTEXATTRIB1F); +DECLARE_METHOD(GLVERTEXATTRIB1FV); +DECLARE_METHOD(GLVERTEXATTRIB2F); +DECLARE_METHOD(GLVERTEXATTRIB2FV); +DECLARE_METHOD(GLVERTEXATTRIB3F); +DECLARE_METHOD(GLVERTEXATTRIB3FV); +DECLARE_METHOD(GLVERTEXATTRIB4F); +DECLARE_METHOD(GLVERTEXATTRIB4FV); + +#endif /* __GLATTRIBUTES_H */ diff --git a/gb.opengl/src/glsl/GLinfo.h b/gb.opengl/src/glsl/GLinfo.h new file mode 100644 index 00000000..ed736acc --- /dev/null +++ b/gb.opengl/src/glsl/GLinfo.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + GLinfo.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLINFO_H +#define __GLINFO_H + +#include "main.h" + +int CheckSize(GLanum value); + +#endif /* __GLINFO_H */ diff --git a/gb.opengl/src/glsl/GLprogram.c b/gb.opengl/src/glsl/GLprogram.c new file mode 100644 index 00000000..53359b5c --- /dev/null +++ b/gb.opengl/src/glsl/GLprogram.c @@ -0,0 +1,98 @@ +/*************************************************************************** + + GLprogram.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPROGRAM_C + +#include "GL.h" + +BEGIN_METHOD_VOID(GLCREATEPROGRAM) + + GLuint prog; + prog = glCreateProgram(); + GB.ReturnInteger(prog); + +END_METHOD + +BEGIN_METHOD(GLDELETEPROGRAM, GB_INTEGER program) + + glDeleteProgram(VARG(program)); + +END_METHOD + +BEGIN_METHOD(GLGETPROGRAMINFOLOG, GB_INTEGER program) + + GLint length; + + glGetProgramiv(VARG(program), GL_INFO_LOG_LENGTH, &length); + + if (!length) + { + GB.ReturnVoidString(); + return; + } + else + { + GLchar log[length]; + + glGetProgramInfoLog(VARG(program), length, NULL, log); + GB.ReturnNewZeroString((const char *)log); + } + +END_METHOD + +BEGIN_METHOD(GLGETPROGRAMIV, GB_INTEGER program; GB_INTEGER pname) + + GLint value; + GB_ARRAY iArray; + + glGetProgramiv(VARG(program), VARG(pname), &value); + + GB.Array.New(&iArray , GB_T_INTEGER , 1); + *((int *)GB.Array.Get(iArray, 0)) = value; + GB.ReturnObject(iArray); + +END_METHOD + +BEGIN_METHOD(GLISPROGRAM, GB_INTEGER program) + + GB.ReturnBoolean(glIsProgram(VARG(program))); + +END_METHOD + +BEGIN_METHOD(GLLINKPROGRAM, GB_INTEGER program) + + glLinkProgram(VARG(program)); + +END_METHOD + +BEGIN_METHOD(GLUSEPROGRAM, GB_INTEGER program) + + glUseProgram(VARG(program)); + +END_METHOD + +BEGIN_METHOD(GLVALIDATEPROGRAM, GB_INTEGER program) + + glValidateProgram(VARG(program)); + +END_METHOD diff --git a/gb.opengl/src/glsl/GLprogram.h b/gb.opengl/src/glsl/GLprogram.h new file mode 100644 index 00000000..ea072d50 --- /dev/null +++ b/gb.opengl/src/glsl/GLprogram.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + GLprogram.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLPROGRAM_H +#define __GLPROGRAM_H + +#include "main.h" + +DECLARE_METHOD(GLCREATEPROGRAM); +DECLARE_METHOD(GLDELETEPROGRAM); +DECLARE_METHOD(GLGETPROGRAMINFOLOG); +DECLARE_METHOD(GLGETPROGRAMIV); +DECLARE_METHOD(GLISPROGRAM); +DECLARE_METHOD(GLLINKPROGRAM); +DECLARE_METHOD(GLUSEPROGRAM); +DECLARE_METHOD(GLVALIDATEPROGRAM); + +#endif /* __GLPROGRAM_H */ diff --git a/gb.opengl/src/glsl/GLshader.c b/gb.opengl/src/glsl/GLshader.c new file mode 100644 index 00000000..13053a30 --- /dev/null +++ b/gb.opengl/src/glsl/GLshader.c @@ -0,0 +1,154 @@ +/*************************************************************************** + + GLshader.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLSHADER_C + +#include "GL.h" + +BEGIN_METHOD(GLATTACHSHADER, GB_INTEGER program; GB_INTEGER shader) + + glAttachShader(VARG(program), VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLCOMPILESHADER, GB_INTEGER shader) + + glCompileShader(VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLCREATESHADER, GB_INTEGER type) + + GLuint shader; + shader = glCreateShader(VARG(type)); + GB.ReturnInteger(shader); + +END_METHOD + +BEGIN_METHOD(GLDELETESHADER, GB_INTEGER shader) + + glDeleteShader(VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLDETACHSHADER, GB_INTEGER program; GB_INTEGER shader) + + glDetachShader(VARG(program), VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLGETATTACHEDSHADERS, GB_INTEGER program) + + GLint count; + GB_ARRAY iArray; + + glGetProgramiv(VARG(program), GL_ATTACHED_SHADERS, &count); + + if (!count) + { + GB.ReturnNull(); + return; + } + else + { + GLuint params[count]; + int i; + + GB.Array.New(&iArray , GB_T_INTEGER , count); + glGetAttachedShaders(VARG(program), count, NULL, params); + + for (i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLSHADER_H +#define __GLSHADER_H + +#include "main.h" + +DECLARE_METHOD(GLATTACHSHADER); +DECLARE_METHOD(GLCOMPILESHADER); +DECLARE_METHOD(GLCREATESHADER); +DECLARE_METHOD(GLDELETESHADER); +DECLARE_METHOD(GLDETACHSHADER); +DECLARE_METHOD(GLGETATTACHEDSHADERS); +DECLARE_METHOD(GLGETSHADERINFOLOG); +DECLARE_METHOD(GLGETSHADERIV); +DECLARE_METHOD(GLGETSHADERSOURCE); +DECLARE_METHOD(GLISSHADER); +DECLARE_METHOD(GLSHADERSOURCE); + +#endif /* __GLSHADER_H */ diff --git a/gb.opengl/src/glsl/GLuniform.c b/gb.opengl/src/glsl/GLuniform.c new file mode 100644 index 00000000..6ade97c1 --- /dev/null +++ b/gb.opengl/src/glsl/GLuniform.c @@ -0,0 +1,519 @@ +/*************************************************************************** + + GLuniform.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPROGRAM_C + +#include "GL.h" + +BEGIN_METHOD(GLGETUNIFORMLOCATION, GB_INTEGER program; GB_STRING name) + + if (!LENGTH(name)) + { + GB.ReturnInteger(0); + return; + } + + GB.ReturnInteger(glGetUniformLocation(VARG(program), GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM1F, GB_INTEGER location; GB_FLOAT v0) + + glUniform1f(VARG(location), VARG(v0)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM2F, GB_INTEGER location; GB_FLOAT v0; GB_FLOAT v1) + + glUniform2f(VARG(location), VARG(v0), VARG(v1)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM3F, GB_INTEGER location; GB_FLOAT v0; GB_FLOAT v1; GB_FLOAT v2) + + glUniform3f(VARG(location), VARG(v0), VARG(v1), VARG(v2)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM4F, GB_INTEGER location; GB_FLOAT v0; GB_FLOAT v1; GB_FLOAT v2; GB_FLOAT v3) + + glUniform4f(VARG(location), VARG(v0), VARG(v1), VARG(v2), VARG(v3)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM1I, GB_INTEGER location; GB_INTEGER v0) + + glUniform1i(VARG(location), VARG(v0)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM2I, GB_INTEGER location; GB_INTEGER v0; GB_INTEGER v1) + + glUniform2i(VARG(location), VARG(v0), VARG(v1)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM3I, GB_INTEGER location; GB_INTEGER v0; GB_INTEGER v1; GB_INTEGER v2) + + glUniform3i(VARG(location), VARG(v0), VARG(v1), VARG(v2)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM4I, GB_INTEGER location; GB_INTEGER v0; GB_INTEGER v1; GB_INTEGER v2; GB_INTEGER v3) + + glUniform4i(VARG(location), VARG(v0), VARG(v1), VARG(v2), VARG(v3)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM1FV, GB_INTEGER location; GB_OBJECT array) + + GB_ARRAY fArray = VARG(array); + int count = GB.Array.Count(fArray); + + if (!count) + return; + + GLfloat values[count]; + int i; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUNIFORM_H +#define __GLUNIFORM_H + +#include "main.h" + +DECLARE_METHOD(GLGETUNIFORMLOCATION); +DECLARE_METHOD(GLUNIFORM1F); +DECLARE_METHOD(GLUNIFORM2F); +DECLARE_METHOD(GLUNIFORM3F); +DECLARE_METHOD(GLUNIFORM4F); +DECLARE_METHOD(GLUNIFORM1I); +DECLARE_METHOD(GLUNIFORM2I); +DECLARE_METHOD(GLUNIFORM3I); +DECLARE_METHOD(GLUNIFORM4I); +DECLARE_METHOD(GLUNIFORM1FV); +DECLARE_METHOD(GLUNIFORM2FV); +DECLARE_METHOD(GLUNIFORM3FV); +DECLARE_METHOD(GLUNIFORM4FV); +DECLARE_METHOD(GLUNIFORM1IV); +DECLARE_METHOD(GLUNIFORM2IV); +DECLARE_METHOD(GLUNIFORM3IV); +DECLARE_METHOD(GLUNIFORM4IV); +DECLARE_METHOD(GLUNIFORMMATRIX2FV); +DECLARE_METHOD(GLUNIFORMMATRIX3FV); +DECLARE_METHOD(GLUNIFORMMATRIX4FV); +DECLARE_METHOD(GLUNIFORMMATRIX2X3FV); +DECLARE_METHOD(GLUNIFORMMATRIX3X2FV); +DECLARE_METHOD(GLUNIFORMMATRIX2X4FV); +DECLARE_METHOD(GLUNIFORMMATRIX4X2FV); +DECLARE_METHOD(GLUNIFORMMATRIX3X4FV); +DECLARE_METHOD(GLUNIFORMMATRIX4X3FV); + +#endif /* __GLUNIFORM_H */ diff --git a/gb.opengl/src/glsl/Makefile.am b/gb.opengl/src/glsl/Makefile.am new file mode 100644 index 00000000..7a1ac085 --- /dev/null +++ b/gb.opengl/src/glsl/Makefile.am @@ -0,0 +1,17 @@ +COMPONENT = gb.opengl.glsl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.opengl.glsl.la + +gb_opengl_glsl_la_LIBADD = @OPENGL_LIB@ +gb_opengl_glsl_la_LDFLAGS = -module @LD_FLAGS@ @OPENGL_LDFLAGS@ +gb_opengl_glsl_la_CPPFLAGS = @OPENGL_INC@ + +gb_opengl_glsl_la_SOURCES = \ + main.h main.c \ + GLprogram.h GLprogram.c \ + GLshader.h GLshader.c \ + GLuniform.h GLuniform.c \ + GLattributes.h GLattributes.c \ + GL.h GL.c + diff --git a/gb.opengl/src/glsl/gb.opengl.glsl.component b/gb.opengl/src/glsl/gb.opengl.glsl.component new file mode 100644 index 00000000..cc9cdb98 --- /dev/null +++ b/gb.opengl/src/glsl/gb.opengl.glsl.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.opengl.glsl +Author=Laurent Carlier +Need=OpenGLViewer +Require=gb.opengl +State=Stable diff --git a/gb.opengl/src/glsl/main.c b/gb.opengl/src/glsl/main.c new file mode 100644 index 00000000..9cda454a --- /dev/null +++ b/gb.opengl/src/glsl/main.c @@ -0,0 +1,48 @@ +/*************************************************************************** + + main.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "main.h" + +#include "GL.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ +/* GL */ + Cgl, + + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.opengl/src/glsl/main.h b/gb.opengl/src/glsl/main.h new file mode 100644 index 00000000..ed695ef2 --- /dev/null +++ b/gb.opengl/src/glsl/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#ifndef WARNING +#define WARNING(c) printf("WARNING: " c) +#endif /* WARNING */ + +#endif + diff --git a/gb.opengl/src/glu/GLU.c b/gb.opengl/src/glu/GLU.c new file mode 100644 index 00000000..4e36d931 --- /dev/null +++ b/gb.opengl/src/glu/GLU.c @@ -0,0 +1,151 @@ +/*************************************************************************** + + GLU.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLU_C + +#include "gb.image.h" + +#include "GLU.h" + +#include "GLUcoordTransf.h" +#include "GLUtextureImage.h" +#include "GLUquadratic.h" +#include "GLUnurb.h" +#include "GLUproject.h" + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Glu_Color, GB_INTEGER color) + + int r, g, b, a; + + GB_COLOR_SPLIT(VARG(color), r, g, b, a); + //fprintf(stderr, "Glu_Color: %d %d %d %d\n", r, g, b, a); + if (a == 0) + glColor3d(r / 255.0, g / 255.0, b / 255.0); + else + glColor4d(r / 255.0, g / 255.0, b / 255.0, a / 255.0); + +END_METHOD + +BEGIN_METHOD(Glu_ClearColor, GB_INTEGER color) + + int r, g, b, a; + + GB_COLOR_SPLIT(VARG(color), r, g, b, a); + glClearColor(r / 255.0, g / 255.0, b / 255.0, a / 255.0); + +END_METHOD + +BEGIN_METHOD(GLUERRORSTRING, GB_INTEGER code) + + const GLubyte *errStr = gluErrorString(VARG(code)); + GB.ReturnNewZeroString((char *) errStr); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC Cglu[] = +{ + GB_DECLARE("Glu",0), GB_NOT_CREATABLE(), + + /* Get error string */ + GB_STATIC_METHOD("ErrorString", "s", GLUERRORSTRING, "(ErrorCode)i"), + + /* Coordinate Transformation - see GLUcoordTransf.h */ + GB_STATIC_METHOD("LookAt", NULL, GLULOOKAT, "(EyeX)f(EyeY)f(EyeZ)f(CenterX)f(CenterY)f(CenterZ)f(UpX)f(UpY)f(UpZ)f"), + GB_STATIC_METHOD("Ortho2D", NULL, GLUORTHO2D, "(Left)f(Right)f(Bottom)f(Top)f"), + GB_STATIC_METHOD("Perspective", NULL, GLUPERSPECTIVE, "(Fovy)f(Aspect)f(ZNear)f(ZFar)f"), + GB_STATIC_METHOD("PickMatrix", NULL, GLUPICKMATRIX, "(X)f(Y)f(DelX)f(DelY)f(Viewport)Integer[]"), + + /* Texture Image - see GLUtextureImage.h */ + GB_STATIC_METHOD("Build1DMipmaps", "i", GLUBUILD1DMIPMAPS, "(Image)Image;"), + GB_STATIC_METHOD("Build2DMipmaps", "i", GLUBUILD2DMIPMAPS, "(Image)Image;"), + + /* Quadratics - see GLUquadratic.h */ + GB_STATIC_METHOD("NewQuadric", "GluQuadric", GLUNEWQUADRIC, NULL), + GB_STATIC_METHOD("QuadricNormals", NULL, GLUQUADRICNORMALS, "(Quad)GluQuadric;(Normal)i"), + GB_STATIC_METHOD("QuadricTexture", NULL, GLUQUADRICTEXTURE, "(Quad)GluQuadric;(Texture)b"), + GB_STATIC_METHOD("QuadricOrientation", NULL, GLUQUADRICORIENTATION, "(Quad)GluQuadric;(Orientation)i"), + GB_STATIC_METHOD("QuadricDrawStyle", NULL, GLUQUADRICDRAWSTYLE, "(Quad)GluQuadric;(DrawStyle)i"), + GB_STATIC_METHOD("Sphere", NULL, GLUSPHERE, "(Quad)GluQuadric;(Radius)f(Slices)i(Stacks)i"), + GB_STATIC_METHOD("Cylinder", NULL, GLUCYLINDER, "(Quad)GluQuadric;(Base)f(Top)f(Height)f(Slices)i(Stacks)i"), + GB_STATIC_METHOD("Disk", NULL, GLUDISK, "(Quad)GluQuadric;(Inner)f(Outer)f(Slices)i(Loops)i"), + GB_STATIC_METHOD("PartialDisk", NULL, GLUPARTIALDISK, "(Quad)GluQuadric;(Inner)f(Outer)f(Slices)i(Loops)i(Start)f(Sweep)f"), + + /* NURBS - SEE GLUnurbs.h */ + GB_STATIC_METHOD("BeginCurve", NULL, GLUBEGINCURVE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("BeginSurface", NULL, GLUBEGINSURFACE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("BeginTrim", NULL, GLUBEGINTRIM, "(Nurb)GluNurb"), + GB_STATIC_METHOD("DeleteNurbsRenderer", NULL, GLUDELETENURBSRENDERER, "(Nurb)GluNurb"), + GB_STATIC_METHOD("EndCurve", NULL, GLUENDCURVE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("EndSurface", NULL, GLUENDSURFACE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("EndTrim", NULL, GLUENDTRIM, "(Nurb)GluNurb"), + GB_STATIC_METHOD("NurbsCurve", NULL, GLUNURBSCURVE, "(Nurb)GluNurb;(KnotCount)i(Knots)Single[];(Stride)i(Control)Single[];(Order)i(Type)i"), + GB_STATIC_METHOD("NurbsProperty", NULL, GLUNURBSPROPERTY, "(Nurb)GluNurb;(Property)i(Value)f"), + GB_STATIC_METHOD("NurbsSurface", NULL, GLUNURBSSURFACE, "(Nurb)GluNurb;(SKnotCount)i(SKnots)Single[];(TKnotCount)i(TKnots)Single[];(SStride)i(TStride)i(SOrder)i(TOrder)i(Type)i(Control)Single[]"), + GB_STATIC_METHOD("NewNurbsRenderer","GluNurb", GLUNEWNURBSRENDERER, NULL), + + /* Projections - see GLUproject.h */ + GB_STATIC_METHOD("Project", "Float[]", GLUPROJECT, "(ObjectX)f(ObjectY)f(ObjectZ)f(Modelview)Float[];(Projection)Float[];(Viewport)Integer[];"), + GB_STATIC_METHOD("UnProject", "Float[]", GLUUNPROJECT, "(WindowX)f(WindowY)f(WindowZ)f(Modelview)Float[];(Projection)Float[];(Viewport)Integer[];"), + GB_STATIC_METHOD("UnProject4", "Float[]", GLUUNPROJECT4, "(WindowX)f(WindowY)f(WindowZ)f(ClipW)f(Modelview)Float[];(Projection)Float[];(Viewport)Integer[];(NearValue)f(FarValue)f"), + + /* Setting a Gambas color */ + GB_STATIC_METHOD("ClearColor", NULL, Glu_ClearColor, "(Color)i"), + GB_STATIC_METHOD("Color", NULL, Glu_Color, "(Color)i"), + + /********************/ + /* opengl constants */ + /********************/ + + /* Errors */ + GB_CONSTANT("INVALID_ENUM", "i", GLU_INVALID_ENUM), + GB_CONSTANT("INVALID_VALUE", "i", GLU_INVALID_OPERATION), + GB_CONSTANT("OUT_OF_MEMORY", "i", GLU_OUT_OF_MEMORY), + + GB_CONSTANT("NONE", "i", GLU_NONE), + GB_CONSTANT("FLAT", "i", GLU_FLAT), + GB_CONSTANT("SMOOTH", "i", GLU_SMOOTH), + + GB_CONSTANT("OUTSIDE", "i", GLU_OUTSIDE), + GB_CONSTANT("INSIDE", "i", GLU_INSIDE), + + GB_CONSTANT("FILL", "i", GLU_FILL), + GB_CONSTANT("LINE", "i", GLU_LINE), + GB_CONSTANT("POINT", "i", GLU_POINT), + GB_CONSTANT("SILHOUETTE", "i", GLU_SILHOUETTE), + + GB_CONSTANT("CULLING", "i", GLU_CULLING), + GB_CONSTANT("SAMPLING_TOLERANCE", "i", GLU_SAMPLING_TOLERANCE), + GB_CONSTANT("SAMPLING_METHOD", "i", GLU_SAMPLING_METHOD), + GB_CONSTANT("PARAMETRIC_TOLERANCE", "i", GLU_PARAMETRIC_TOLERANCE), + GB_CONSTANT("DISPLAY_MODE", "i", GLU_DISPLAY_MODE), + GB_CONSTANT("AUTO_LOAD_MATRIX", "i", GLU_AUTO_LOAD_MATRIX), + GB_CONSTANT("U_STEP", "i", GLU_U_STEP), + GB_CONSTANT("V_STEP", "i", GLU_V_STEP), + GB_CONSTANT("NURBS_MODE", "i", GLU_NURBS_MODE), + + GB_END_DECLARE +}; diff --git a/gb.opengl/src/glu/GLU.h b/gb.opengl/src/glu/GLU.h new file mode 100644 index 00000000..f68ddd2c --- /dev/null +++ b/gb.opengl/src/glu/GLU.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + GLU.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLU_H +#define __GLU_H + +#include "main.h" + +#ifndef __GLU_C +extern GB_DESC Cglu[]; +#endif /* __GLU_C */ + +#endif /* __GLU_H */ diff --git a/gb.opengl/src/glu/GLUcoordTransf.c b/gb.opengl/src/glu/GLUcoordTransf.c new file mode 100644 index 00000000..eec2ec03 --- /dev/null +++ b/gb.opengl/src/glu/GLUcoordTransf.c @@ -0,0 +1,61 @@ +/*************************************************************************** + + GLUcoordTransf.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUCOORDTRANSF_C + +#include "GLU.h" + +/**************************************************************************/ + +BEGIN_METHOD(GLULOOKAT, GB_FLOAT eyex; GB_FLOAT eyey; GB_FLOAT eyez; GB_FLOAT centerx; GB_FLOAT centery; GB_FLOAT centerz; GB_FLOAT upx; GB_FLOAT upy; GB_FLOAT upz) + + gluLookAt(VARG(eyex),VARG(eyey),VARG(eyez),VARG(centerx),VARG(centery),VARG(centerz),VARG(upx),VARG(upy),VARG(upz)); + +END_METHOD + +BEGIN_METHOD(GLUORTHO2D, GB_FLOAT left; GB_FLOAT right; GB_FLOAT bottom; GB_FLOAT top) + + gluOrtho2D(VARG(left),VARG(right),VARG(bottom),VARG(top)); + +END_METHOD + +BEGIN_METHOD(GLUPERSPECTIVE, GB_FLOAT fovy; GB_FLOAT aspect; GB_FLOAT znear; GB_FLOAT zfar) + + gluPerspective(VARG(fovy),VARG(aspect),VARG(znear),VARG(zfar)); + +END_METHOD + +BEGIN_METHOD(GLUPICKMATRIX, GB_FLOAT x; GB_FLOAT y; GB_FLOAT width; GB_FLOAT height; GB_OBJECT viewport) + + GLint iparams[4]; + GB_ARRAY viewport = (GB_ARRAY) VARG(viewport); + int i, count = GB.Array.Count(viewport); + + count = (count > 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUCOORDTRANSF_H +#define __GLUCOORDTRANSF_H + +#include "main.h" + +DECLARE_METHOD(GLULOOKAT); +DECLARE_METHOD(GLUORTHO2D); +DECLARE_METHOD(GLUPERSPECTIVE); +DECLARE_METHOD(GLUPICKMATRIX); + +#endif /* __GLUCOORDTRANSF_H */ diff --git a/gb.opengl/src/glu/GLUnurb.c b/gb.opengl/src/glu/GLUnurb.c new file mode 100644 index 00000000..9c22984c --- /dev/null +++ b/gb.opengl/src/glu/GLUnurb.c @@ -0,0 +1,153 @@ +/*************************************************************************** + + GLUnurb.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUNURB_C + +#include "cglunurb.h" +#include "GLU.h" + +static GLUnurbsObj *get_nurb(void *ob) +{ + if (GB.CheckObject(ob)) + return NULL; + else + return ((CGLUNURB *)ob)->nurb; +} + +#define GET_NURB() \ + GLUnurbsObj *thenurb = get_nurb(VARG(nurb)); \ + if (!thenurb) return; + +BEGIN_METHOD(GLUBEGINCURVE, GB_OBJECT nurb) + + GET_NURB(); + gluBeginCurve(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUBEGINSURFACE, GB_OBJECT nurb) + + GET_NURB(); + gluBeginSurface(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUBEGINTRIM, GB_OBJECT nurb) + + GET_NURB(); + gluBeginTrim(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUDELETENURBSRENDERER, GB_OBJECT nurb) + + GET_NURB(); + gluDeleteNurbsRenderer(thenurb); + // Make the nurb Gambas object invalid + ((CGLUNURB *)VARG(nurb))->nurb = NULL; + +END_METHOD + +BEGIN_METHOD(GLUENDCURVE, GB_OBJECT nurb) + + GET_NURB(); + gluEndCurve(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUENDSURFACE, GB_OBJECT nurb) + + GET_NURB(); + gluEndSurface(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUENDTRIM, GB_OBJECT nurb) + + GET_NURB(); + gluEndTrim(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUNURBSCURVE, GB_OBJECT nurb; GB_INTEGER knotCount; GB_OBJECT knots; GB_INTEGER stride; GB_OBJECT control; GB_INTEGER order; GB_INTEGER type) + + GET_NURB(); + + GB_ARRAY knot = (GB_ARRAY) VARG(knots); + GB_ARRAY controll = (GB_ARRAY) VARG(control); + int i; + int count1 = GB.Array.Count(knot); + int count2 = GB.Array.Count(controll); + GLfloat param1[count1], param2[count2]; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUNURB_H +#define __GLUNURB_H + +#include "main.h" +DECLARE_METHOD(GLUBEGINCURVE); +DECLARE_METHOD(GLUBEGINSURFACE); +DECLARE_METHOD(GLUBEGINTRIM); +DECLARE_METHOD(GLUDELETENURBSRENDERER); +DECLARE_METHOD(GLUENDCURVE); +DECLARE_METHOD(GLUENDSURFACE); +DECLARE_METHOD(GLUENDTRIM); +/*skip ***DECLARE_METHOD(GLUGETNURBSPROPERTY); +skip ***DECLARE_METHOD(GLULOADSAMPLINGMATRICES); +skip ***DECLARE_METHOD(GLUNURBSCALLBACK); +skip ***DECLARE_METHOD(GLUNURBSCALLBACKDATA); +skip ***DECLARE_METHOD(GLUNURBSCALLBACKDATAEXT);*/ +DECLARE_METHOD(GLUNURBSCURVE); +DECLARE_METHOD(GLUNURBSPROPERTY); +DECLARE_METHOD(GLUNURBSSURFACE); +DECLARE_METHOD(GLUNEWNURBSRENDERER); +//DECLARE_METHOD(GLUPWLCURVE); + + +#endif /* __GLUNURB_H */ diff --git a/gb.opengl/src/glu/GLUproject.c b/gb.opengl/src/glu/GLUproject.c new file mode 100644 index 00000000..fa206ff0 --- /dev/null +++ b/gb.opengl/src/glu/GLUproject.c @@ -0,0 +1,138 @@ +/*************************************************************************** + + GLUproject.c + + (c) 2005-2012 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUPROJECT_C + +#include "GLU.h" + +BEGIN_METHOD(GLUPROJECT, GB_FLOAT ObjX; GB_FLOAT ObjY; GB_FLOAT ObjZ; GB_OBJECT Model; GB_OBJECT Proj; GB_OBJECT View) + + GLdouble model[16], proj[16], win[3]; + GLint view[4]; + int i, status; + GB_ARRAY fArray; + + if ((GB.Array.Count(VARG(Model)) != 16) || (GB.Array.Count(VARG(Proj)) != 16) || (GB.Array.Count(VARG(View)) != 4)) + { + GB.ReturnNull(); + return; + } + + for (i=0; i<16; i++) + model[i] = *((GLdouble *)GB.Array.Get(VARG(Model),i)); + for (i=0; i<16; i++) + proj[i] = *((GLdouble *)GB.Array.Get(VARG(Proj),i)); + for (i=0; i<4; i++) + view[i] = *((GLint *)GB.Array.Get(VARG(View),i)); + + status = gluProject(VARG(ObjX), VARG(ObjY), VARG(ObjZ), model, proj, view, &win[0], &win[1], &win[2]); + + if (status == GLU_FALSE) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&fArray , GB_T_FLOAT , 3); + + for (i=0; i<3; i++) + *((GLdouble *)GB.Array.Get(fArray, i)) = win[i]; + + GB.ReturnObject(fArray); + +END_METHOD + +BEGIN_METHOD(GLUUNPROJECT, GB_FLOAT WinX; GB_FLOAT WinY; GB_FLOAT WinZ; GB_OBJECT Model; GB_OBJECT Proj; GB_OBJECT View) + + + GLdouble model[16], proj[16], obj[3]; + GLint view[4]; + int i, status; + GB_ARRAY fArray; + + if ((GB.Array.Count(VARG(Model)) != 16) || (GB.Array.Count(VARG(Proj)) != 16) || (GB.Array.Count(VARG(View)) != 4)) + { + GB.ReturnNull(); + return; + } + + for (i=0; i<16; i++) + model[i] = *((GLdouble *)GB.Array.Get(VARG(Model),i)); + for (i=0; i<16; i++) + proj[i] = *((GLdouble *)GB.Array.Get(VARG(Proj),i)); + for (i=0; i<4; i++) + view[i] = *((GLint *)GB.Array.Get(VARG(View),i)); + + status = gluUnProject(VARG(WinX), VARG(WinY), VARG(WinZ), model, proj, view, &obj[0], &obj[1], &obj[2]); + + if (status == GLU_FALSE) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&fArray , GB_T_FLOAT , 3); + + for (i=0; i<3; i++) + *((GLdouble *)GB.Array.Get(fArray, i)) = obj[i]; + + GB.ReturnObject(fArray); + +END_METHOD + +BEGIN_METHOD(GLUUNPROJECT4, GB_FLOAT WinX; GB_FLOAT WinY; GB_FLOAT WinZ; GB_FLOAT ClipW; GB_OBJECT Model; GB_OBJECT Proj; GB_OBJECT View; GB_FLOAT NearVal; GB_FLOAT FarVal) + + GLdouble model[16], proj[16], obj[4]; + GLint view[4]; + int i, status; + GB_ARRAY fArray; + + if ((GB.Array.Count(VARG(Model)) != 16) || (GB.Array.Count(VARG(Proj)) != 16) || (GB.Array.Count(VARG(View)) != 4)) + { + GB.ReturnNull(); + return; + } + + for (i=0; i<16; i++) + model[i] = *((GLdouble *)GB.Array.Get(VARG(Model),i)); + for (i=0; i<16; i++) + proj[i] = *((GLdouble *)GB.Array.Get(VARG(Proj),i)); + for (i=0; i<4; i++) + view[i] = *((GLint *)GB.Array.Get(VARG(View),i)); + + status = gluUnProject4(VARG(WinX), VARG(WinY), VARG(WinZ), VARG(ClipW), model, proj, view, VARG(NearVal), VARG(FarVal), &obj[0], &obj[1], &obj[2], &obj[3]); + + if (status == GLU_FALSE) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&fArray , GB_T_FLOAT , 4); + + for (i=0; i<4; i++) + *((GLdouble *)GB.Array.Get(fArray, i)) = obj[i]; + + GB.ReturnObject(fArray); + +END_METHOD diff --git a/gb.opengl/src/glu/GLUproject.h b/gb.opengl/src/glu/GLUproject.h new file mode 100644 index 00000000..39d4e78d --- /dev/null +++ b/gb.opengl/src/glu/GLUproject.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + GLUproject.h + + (c) 2005-2012 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUPROJECT_H +#define __GLUPROJECT_H + +#include "main.h" + +DECLARE_METHOD(GLUPROJECT); +DECLARE_METHOD(GLUUNPROJECT); +DECLARE_METHOD(GLUUNPROJECT4); + +#endif /* __GLUPROJECT_H */ + diff --git a/gb.opengl/src/glu/GLUquadratic.c b/gb.opengl/src/glu/GLUquadratic.c new file mode 100644 index 00000000..bbac3088 --- /dev/null +++ b/gb.opengl/src/glu/GLUquadratic.c @@ -0,0 +1,112 @@ +/*************************************************************************** + + GLUquadratic.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUQUADRATIC_C + +#include "GLU.h" +#include "cgluquadric.h" + +static GLUquadric *get_quadric(void *ob) +{ + if (GB.CheckObject(ob)) + return NULL; + else + return ((CGLUQUADRIC *)ob)->quadric; +} + +#define GET_QUADRIC() \ + GLUquadric *quad = get_quadric(VARG(Quad)); \ + if (!quad) return; + +BEGIN_METHOD_VOID(GLUNEWQUADRIC) + + GB.ReturnObject(CGLUQUADRIC_create()); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICNORMALS, GB_OBJECT Quad; GB_INTEGER Normal) + + GET_QUADRIC(); + gluQuadricNormals(quad, VARG(Normal)); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICORIENTATION, GB_OBJECT Quad; GB_INTEGER Orientation) + + GET_QUADRIC(); + gluQuadricOrientation(quad, VARG(Orientation)); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICDRAWSTYLE, GB_OBJECT Quad; GB_INTEGER DrawStyle) + + GET_QUADRIC(); + gluQuadricDrawStyle(quad, VARG(DrawStyle)); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICTEXTURE, GB_OBJECT Quad; GB_BOOLEAN Texture) + + GET_QUADRIC(); + gluQuadricTexture(quad, VARG(Texture)); + +END_METHOD + +BEGIN_METHOD(GLUDELETEQUADRIC, GB_OBJECT Quad) + + GET_QUADRIC(); + gluDeleteQuadric(quad); + // Make the quadric Gambas object invalid + ((CGLUQUADRIC *)VARG(Quad))->quadric = NULL; + +END_METHOD + +BEGIN_METHOD(GLUSPHERE, GB_OBJECT Quad; GB_FLOAT Radius; GB_INTEGER Slices; GB_INTEGER Stacks) + + GET_QUADRIC(); + gluSphere(quad, VARG(Radius), VARG(Slices), VARG(Stacks)); + +END_METHOD + +BEGIN_METHOD(GLUCYLINDER, GB_OBJECT Quad; GB_FLOAT Base; GB_FLOAT Top; GB_FLOAT Height; GB_INTEGER Slices; GB_INTEGER Stacks) + + GET_QUADRIC(); + gluCylinder(quad, VARG(Base), VARG(Top), VARG(Height), VARG(Slices), VARG(Stacks)); + +END_METHOD + +BEGIN_METHOD(GLUDISK, GB_OBJECT Quad; GB_FLOAT Inner; GB_FLOAT Outer; GB_INTEGER Slices; GB_INTEGER Loops) + + GET_QUADRIC(); + gluDisk(quad, VARG(Inner), VARG(Outer), VARG(Slices), VARG(Loops)); + +END_METHOD + +BEGIN_METHOD(GLUPARTIALDISK, GB_OBJECT Quad; GB_FLOAT Inner; GB_FLOAT Outer; GB_INTEGER Slices; GB_INTEGER Loops; GB_FLOAT Start; GB_FLOAT Sweep) + + GET_QUADRIC(); + gluPartialDisk(quad, VARG(Inner), VARG(Outer), VARG(Slices), VARG(Loops), VARG(Start), VARG(Sweep)); + +END_METHOD + + diff --git a/gb.opengl/src/glu/GLUquadratic.h b/gb.opengl/src/glu/GLUquadratic.h new file mode 100644 index 00000000..c6030602 --- /dev/null +++ b/gb.opengl/src/glu/GLUquadratic.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + GLUquadratic.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUQUADRATIC_H +#define __GLUQUADRATIC_H + +#include "main.h" +#include "cgluquadric.h" + +DECLARE_METHOD(GLUNEWQUADRIC); +DECLARE_METHOD(GLUQUADRICNORMALS); +DECLARE_METHOD(GLUQUADRICTEXTURE); +DECLARE_METHOD(GLUQUADRICORIENTATION); +DECLARE_METHOD(GLUQUADRICDRAWSTYLE); +DECLARE_METHOD(GLUSPHERE); +DECLARE_METHOD(GLUCYLINDER); +DECLARE_METHOD(GLUDISK); +DECLARE_METHOD(GLUPARTIALDISK); + +#endif /* __GLUQUADRATIC_H */ diff --git a/gb.opengl/src/glu/GLUtextureImage.c b/gb.opengl/src/glu/GLUtextureImage.c new file mode 100644 index 00000000..cf47fdd1 --- /dev/null +++ b/gb.opengl/src/glu/GLUtextureImage.c @@ -0,0 +1,53 @@ +/*************************************************************************** + + GLUtextureImage.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUTEXTUREIMAGE_C + +#include "GLU.h" + +BEGIN_METHOD(GLUBUILD1DMIPMAPS, GB_OBJECT Image) + + GB_IMG *image; + int format; + int status; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + status = gluBuild1DMipmaps(GL_TEXTURE_1D, 4, image->width, format, GL_UNSIGNED_BYTE, image->data); + + GB.ReturnInteger(status); + +END_METHOD + +BEGIN_METHOD(GLUBUILD2DMIPMAPS, GB_OBJECT Image) + + GB_IMG *image = VARG(Image); + int status = 0; + + status = gluBuild2DMipmaps(GL_TEXTURE_2D, 4, image->width, image->height, IMAGE_get_pixel_format(image), GL_UNSIGNED_BYTE, + image->data); + + GB.ReturnInteger(status); + +END_METHOD diff --git a/gb.opengl/src/glu/GLUtextureImage.h b/gb.opengl/src/glu/GLUtextureImage.h new file mode 100644 index 00000000..d2554fbd --- /dev/null +++ b/gb.opengl/src/glu/GLUtextureImage.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + GLUtextureImage.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUTEXTUREIMAGE_H +#define __GLUTEXTUREIMAGE_H + +#include "main.h" + +DECLARE_METHOD(GLUBUILD1DMIPMAPS); +DECLARE_METHOD(GLUBUILD2DMIPMAPS); + +#endif /* __GLUTEXTUREIMAGE_H */ + diff --git a/gb.opengl/src/glu/Makefile.am b/gb.opengl/src/glu/Makefile.am new file mode 100644 index 00000000..41a824b7 --- /dev/null +++ b/gb.opengl/src/glu/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.opengl.glu +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.opengl.glu.la + +gb_opengl_glu_la_LIBADD = @GLU_LIB@ +gb_opengl_glu_la_LDFLAGS = -module @LD_FLAGS@ @GLU_LDFLAGS@ +gb_opengl_glu_la_CPPFLAGS = @GLU_INC@ + +gb_opengl_glu_la_SOURCES = \ + main.h main.c \ + GLU.h GLU.c \ + GLUcoordTransf.h GLUcoordTransf.c \ + GLUtextureImage.h GLUtextureImage.c \ + GLUquadratic.h GLUquadratic.c \ + cgluquadric.h cgluquadric.c \ + cglunurb.h cglunurb.c\ + GLUnurb.h GLUnurb.c \ + GLUproject.h GLUproject.c + diff --git a/gb.opengl/src/glu/cglunurb.c b/gb.opengl/src/glu/cglunurb.c new file mode 100644 index 00000000..b9958d7c --- /dev/null +++ b/gb.opengl/src/glu/cglunurb.c @@ -0,0 +1,74 @@ +/*************************************************************************** + + cglunurb.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLUNURB_C + +#include "gb_common.h" +#include "cglunurb.h" + +CGLUNURB *CGLUNURB_create() +{ + CGLUNURB *ob = GB.New(GB.FindClass("GluNurb"), NULL, NULL); + return ob; +} + +static int GluNurb_check(void *_object) +{ + return NURB == NULL; +} + + +BEGIN_METHOD_VOID(GluNurb_new) + + THIS->nurb = gluNewNurbsRenderer(); + +END_METHOD + +BEGIN_METHOD_VOID(GluNurb_free) + + if (NURB) + gluDeleteNurbsRenderer(NURB); + +END_METHOD + + +GB_DESC GluNurbsDesc[] = +{ + GB_DECLARE("GluNurb", sizeof(CGLUNURB)), + + GB_HOOK_CHECK(GluNurb_check), + + GB_METHOD("_new", NULL, GluNurb_new, NULL), + GB_METHOD("_free", NULL, GluNurb_free, NULL), + + GB_END_DECLARE +}; + +/*BEGIN_METHOD(CGLUQUADRIC_free, GB_OBJECT quadric) + + if (!quadric) + return; + + gluDeleteNurbsRenderer (quadric); + +END_METHOD*/ diff --git a/gb.opengl/src/glu/cglunurb.h b/gb.opengl/src/glu/cglunurb.h new file mode 100644 index 00000000..d1ed4579 --- /dev/null +++ b/gb.opengl/src/glu/cglunurb.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + cglunurb.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLUNURB_H +#define __CGLUNURB_H + +#include "gambas.h" +#include "main.h" +#include "GLU.h" + +#ifndef __CGLUNURB_C +extern GB_DESC GluNurbsDesc[]; +#else + +#define THIS OBJECT(CGLUNURB) +#define NURB (THIS->nurb) + +#endif + +typedef + struct { + GB_BASE ob; + GLUnurbs *nurb; + } + CGLUNURB; + +CGLUNURB *CGLUNURB_create(); + + +#endif diff --git a/gb.opengl/src/glu/cgluquadric.c b/gb.opengl/src/glu/cgluquadric.c new file mode 100644 index 00000000..646c0e4b --- /dev/null +++ b/gb.opengl/src/glu/cgluquadric.c @@ -0,0 +1,64 @@ +/*************************************************************************** + + cgluquadric.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLUQUADRIC_C + +#include "gb_common.h" +#include "cgluquadric.h" + +CGLUQUADRIC *CGLUQUADRIC_create() +{ + return GB.New(GB.FindClass("GluQuadric"), NULL, NULL); +}; + +static int GluQuadric_check(void *_object) +{ + return QUADRIC == NULL; +} + + +BEGIN_METHOD_VOID(GluQuadric_new) + + THIS->quadric = gluNewQuadric(); + +END_METHOD + +BEGIN_METHOD_VOID(GluQuadric_free) + + if (QUADRIC) + gluDeleteQuadric(QUADRIC); + +END_METHOD + + +GB_DESC GluQuadricDesc[] = +{ + GB_DECLARE("GluQuadric", sizeof(CGLUQUADRIC)), + GB_HOOK_CHECK(GluQuadric_check), + + GB_METHOD("_new", NULL, GluQuadric_new, NULL), + GB_METHOD("_free", NULL, GluQuadric_free, NULL), + + GB_END_DECLARE +}; + diff --git a/gb.opengl/src/glu/cgluquadric.h b/gb.opengl/src/glu/cgluquadric.h new file mode 100644 index 00000000..ed30a466 --- /dev/null +++ b/gb.opengl/src/glu/cgluquadric.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + cgluquadric.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLUQUADRIC_H +#define __CGLUQUADRIC_H + +#include "gambas.h" +#include "main.h" +#include "GLU.h" + +#ifndef __CGLUQUADRIC_C + +extern GB_DESC GluQuadricDesc[]; + +#else + +#define THIS OBJECT(CGLUQUADRIC) +#define QUADRIC (THIS->quadric) + +#endif + +typedef + struct { + GB_BASE ob; + GLUquadric *quadric; + } + CGLUQUADRIC; + +CGLUQUADRIC *CGLUQUADRIC_create(); + +/* Gluquadric structure as described in glu.h source + + struct GLUquadric + { + GLint normals; + GLboolean textureCoords; + GLint orientation; + GLint drawStyle; + void (GLAPIENTRY *errorCallback)( GLint ); + }; +*/ + +#endif diff --git a/gb.opengl/src/glu/gb.opengl.glu.component b/gb.opengl/src/glu/gb.opengl.glu.component new file mode 100644 index 00000000..8409f1c8 --- /dev/null +++ b/gb.opengl/src/glu/gb.opengl.glu.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.opengl.glu +Author=Laurent Carlier +Need=OpenGLViewer +Requires=gb.opengl +State=Stable diff --git a/gb.opengl/src/glu/main.c b/gb.opengl/src/glu/main.c new file mode 100644 index 00000000..30b899a0 --- /dev/null +++ b/gb.opengl/src/glu/main.c @@ -0,0 +1,84 @@ +/*************************************************************************** + + main.c + + (c) 2005-2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "cglunurb.h" +#include "cgluquadric.h" +#include "GLU.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + Cglu, + GluQuadricDesc, + GluNurbsDesc, + NULL +}; + + +int IMAGE_get_pixel_format(GB_IMG *image) +{ + if ((image->format == GB_IMAGE_RGBA) || (image->format == GB_IMAGE_RGBX)) + return GL_RGBA; + // is it good ? + else if ((image->format == GB_IMAGE_BGRA) || (image->format == GB_IMAGE_BGRX)) + return GL_BGRA; + else if (image->format == GB_IMAGE_RGB) + return GL_RGB; + else if (image->format == GB_IMAGE_BGR) + return GL_BGR; + else + return 0; +} + +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format) +{ + *img = arg->value; + + if (GB.CheckObject(*img)) + return TRUE; + + *format = IMAGE_get_pixel_format(*img); + if (!*format) + { + IMAGE.Convert(*img, GB_IMAGE_RGBA); + *format = GL_RGBA; + } + + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.opengl/src/glu/main.h b/gb.opengl/src/glu/main.h new file mode 100644 index 00000000..37f6da08 --- /dev/null +++ b/gb.opengl/src/glu/main.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + main.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.image.h" +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#ifndef WARNING +#define WARNING(c) printf("WARNING: " c) +#endif /* WARNING */ + +int IMAGE_get_pixel_format(GB_IMG *image); +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format); + +#endif + diff --git a/gb.opengl/src/main.c b/gb.opengl/src/main.c new file mode 100644 index 00000000..4ea414b7 --- /dev/null +++ b/gb.opengl/src/main.c @@ -0,0 +1,111 @@ +/*************************************************************************** + + main.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "GL.h" + +#include "gb.gl.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +bool Init(void); + +GB_DESC *GB_CLASSES[] EXPORT = +{ + Cgl, + NULL +}; + +void *GB_OPENGL_1[] EXPORT = { + + (void *)1, + (void *)Init, + + NULL +}; + +int IMAGE_get_pixel_format(GB_IMG *image) +{ + if ((image->format == GB_IMAGE_RGBA) || (image->format == GB_IMAGE_RGBX)) + return GL_RGBA; + // is it good ? + else if ((image->format == GB_IMAGE_BGRA) || (image->format == GB_IMAGE_BGRX)) + return GL_BGRA; + else if (image->format == GB_IMAGE_RGB) + return GL_RGB; + else if (image->format == GB_IMAGE_BGR) + return GL_BGR; + else + return 0; +} + +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format) +{ + *img = arg->value; + + if (GB.CheckObject(*img)) + return TRUE; + + *format = IMAGE_get_pixel_format(*img); + if (!*format) + { + IMAGE.Convert(*img, GB_IMAGE_RGBA); + *format = GL_RGBA; + } + + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +bool Init(void) +{ + static bool _init = FALSE; + + if (_init) + return FALSE; + + GLenum err = glewInit(); + if (GLEW_OK != err) + { + /* Problem: glewInit failed, something is seriously wrong. */ + GB.Error("Failed to init GLEW: &1\n", glewGetErrorString(err)); + return TRUE; + } + else + { + _init = TRUE; + return FALSE; + } +} \ No newline at end of file diff --git a/gb.opengl/src/main.h b/gb.opengl/src/main.h new file mode 100644 index 00000000..706096aa --- /dev/null +++ b/gb.opengl/src/main.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + main.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#ifndef WARNING +#define WARNING(_c) printf("warning: " _c) +#endif /* WARNING */ + +int IMAGE_get_pixel_format(GB_IMG *image); +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format); + +#define IMAGE_get_ncolors(_format) (GB_IMAGE_FMT_IS_24_BITS(_format) ? 3 : 4) + + +#endif + diff --git a/gb.opengl/src/sge/Makefile.am b/gb.opengl/src/sge/Makefile.am new file mode 100644 index 00000000..4aa679ec --- /dev/null +++ b/gb.opengl/src/sge/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.opengl.sge +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.opengl.sge.la + +gb_opengl_sge_la_LIBADD = @SGE_LIB@ +gb_opengl_sge_la_LDFLAGS = -module @LD_FLAGS@ @SGE_LDFLAGS@ +gb_opengl_sge_la_CPPFLAGS = @SGE_INC@ + +gb_opengl_sge_la_SOURCES = \ + main.c main.h \ + cmd2model.c cmd2model.h \ + cmd2object.c cmd2object.h diff --git a/gb.opengl/src/sge/cmd2model.c b/gb.opengl/src/sge/cmd2model.c new file mode 100644 index 00000000..a57eeba7 --- /dev/null +++ b/gb.opengl/src/sge/cmd2model.c @@ -0,0 +1,589 @@ +/*************************************************************************** + + cmd2model.c + + (c) 2012 Tomasz Kołodziejczyk "Tommyline" + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMD2MODEL_C + +#include "gb_common.h" +#include "cmd2model.h" +#include + +#define THIS OBJECT(CMD2MODEL) + +/* Table of precalculated normals */ +GLfloat anorms_table[162][3] = +{ + { -0.525731f, 0.000000f, 0.850651f }, + { -0.442863f, 0.238856f, 0.864188f }, + { -0.295242f, 0.000000f, 0.955423f }, + { -0.309017f, 0.500000f, 0.809017f }, + { -0.162460f, 0.262866f, 0.951056f }, + { 0.000000f, 0.000000f, 1.000000f }, + { 0.000000f, 0.850651f, 0.525731f }, + { -0.147621f, 0.716567f, 0.681718f }, + { 0.147621f, 0.716567f, 0.681718f }, + { 0.000000f, 0.525731f, 0.850651f }, + { 0.309017f, 0.500000f, 0.809017f }, + { 0.525731f, 0.000000f, 0.850651f }, + { 0.295242f, 0.000000f, 0.955423f }, + { 0.442863f, 0.238856f, 0.864188f }, + { 0.162460f, 0.262866f, 0.951056f }, + { -0.681718f, 0.147621f, 0.716567f }, + { -0.809017f, 0.309017f, 0.500000f }, + { -0.587785f, 0.425325f, 0.688191f }, + { -0.850651f, 0.525731f, 0.000000f }, + { -0.864188f, 0.442863f, 0.238856f }, + { -0.716567f, 0.681718f, 0.147621f }, + { -0.688191f, 0.587785f, 0.425325f }, + { -0.500000f, 0.809017f, 0.309017f }, + { -0.238856f, 0.864188f, 0.442863f }, + { -0.425325f, 0.688191f, 0.587785f }, + { -0.716567f, 0.681718f, -0.147621f }, + { -0.500000f, 0.809017f, -0.309017f }, + { -0.525731f, 0.850651f, 0.000000f }, + { 0.000000f, 0.850651f, -0.525731f }, + { -0.238856f, 0.864188f, -0.442863f }, + { 0.000000f, 0.955423f, -0.295242f }, + { -0.262866f, 0.951056f, -0.162460f }, + { 0.000000f, 1.000000f, 0.000000f }, + { 0.000000f, 0.955423f, 0.295242f }, + { -0.262866f, 0.951056f, 0.162460f }, + { 0.238856f, 0.864188f, 0.442863f }, + { 0.262866f, 0.951056f, 0.162460f }, + { 0.500000f, 0.809017f, 0.309017f }, + { 0.238856f, 0.864188f, -0.442863f }, + { 0.262866f, 0.951056f, -0.162460f }, + { 0.500000f, 0.809017f, -0.309017f }, + { 0.850651f, 0.525731f, 0.000000f }, + { 0.716567f, 0.681718f, 0.147621f }, + { 0.716567f, 0.681718f, -0.147621f }, + { 0.525731f, 0.850651f, 0.000000f }, + { 0.425325f, 0.688191f, 0.587785f }, + { 0.864188f, 0.442863f, 0.238856f }, + { 0.688191f, 0.587785f, 0.425325f }, + { 0.809017f, 0.309017f, 0.500000f }, + { 0.681718f, 0.147621f, 0.716567f }, + { 0.587785f, 0.425325f, 0.688191f }, + { 0.955423f, 0.295242f, 0.000000f }, + { 1.000000f, 0.000000f, 0.000000f }, + { 0.951056f, 0.162460f, 0.262866f }, + { 0.850651f, -0.525731f, 0.000000f }, + { 0.955423f, -0.295242f, 0.000000f }, + { 0.864188f, -0.442863f, 0.238856f }, + { 0.951056f, -0.162460f, 0.262866f }, + { 0.809017f, -0.309017f, 0.500000f }, + { 0.681718f, -0.147621f, 0.716567f }, + { 0.850651f, 0.000000f, 0.525731f }, + { 0.864188f, 0.442863f, -0.238856f }, + { 0.809017f, 0.309017f, -0.500000f }, + { 0.951056f, 0.162460f, -0.262866f }, + { 0.525731f, 0.000000f, -0.850651f }, + { 0.681718f, 0.147621f, -0.716567f }, + { 0.681718f, -0.147621f, -0.716567f }, + { 0.850651f, 0.000000f, -0.525731f }, + { 0.809017f, -0.309017f, -0.500000f }, + { 0.864188f, -0.442863f, -0.238856f }, + { 0.951056f, -0.162460f, -0.262866f }, + { 0.147621f, 0.716567f, -0.681718f }, + { 0.309017f, 0.500000f, -0.809017f }, + { 0.425325f, 0.688191f, -0.587785f }, + { 0.442863f, 0.238856f, -0.864188f }, + { 0.587785f, 0.425325f, -0.688191f }, + { 0.688191f, 0.587785f, -0.425325f }, + { -0.147621f, 0.716567f, -0.681718f }, + { -0.309017f, 0.500000f, -0.809017f }, + { 0.000000f, 0.525731f, -0.850651f }, + { -0.525731f, 0.000000f, -0.850651f }, + { -0.442863f, 0.238856f, -0.864188f }, + { -0.295242f, 0.000000f, -0.955423f }, + { -0.162460f, 0.262866f, -0.951056f }, + { 0.000000f, 0.000000f, -1.000000f }, + { 0.295242f, 0.000000f, -0.955423f }, + { 0.162460f, 0.262866f, -0.951056f }, + { -0.442863f, -0.238856f, -0.864188f }, + { -0.309017f, -0.500000f, -0.809017f }, + { -0.162460f, -0.262866f, -0.951056f }, + { 0.000000f, -0.850651f, -0.525731f }, + { -0.147621f, -0.716567f, -0.681718f }, + { 0.147621f, -0.716567f, -0.681718f }, + { 0.000000f, -0.525731f, -0.850651f }, + { 0.309017f, -0.500000f, -0.809017f }, + { 0.442863f, -0.238856f, -0.864188f }, + { 0.162460f, -0.262866f, -0.951056f }, + { 0.238856f, -0.864188f, -0.442863f }, + { 0.500000f, -0.809017f, -0.309017f }, + { 0.425325f, -0.688191f, -0.587785f }, + { 0.716567f, -0.681718f, -0.147621f }, + { 0.688191f, -0.587785f, -0.425325f }, + { 0.587785f, -0.425325f, -0.688191f }, + { 0.000000f, -0.955423f, -0.295242f }, + { 0.000000f, -1.000000f, 0.000000f }, + { 0.262866f, -0.951056f, -0.162460f }, + { 0.000000f, -0.850651f, 0.525731f }, + { 0.000000f, -0.955423f, 0.295242f }, + { 0.238856f, -0.864188f, 0.442863f }, + { 0.262866f, -0.951056f, 0.162460f }, + { 0.500000f, -0.809017f, 0.309017f }, + { 0.716567f, -0.681718f, 0.147621f }, + { 0.525731f, -0.850651f, 0.000000f }, + { -0.238856f, -0.864188f, -0.442863f }, + { -0.500000f, -0.809017f, -0.309017f }, + { -0.262866f, -0.951056f, -0.162460f }, + { -0.850651f, -0.525731f, 0.000000f }, + { -0.716567f, -0.681718f, -0.147621f }, + { -0.716567f, -0.681718f, 0.147621f }, + { -0.525731f, -0.850651f, 0.000000f }, + { -0.500000f, -0.809017f, 0.309017f }, + { -0.238856f, -0.864188f, 0.442863f }, + { -0.262866f, -0.951056f, 0.162460f }, + { -0.864188f, -0.442863f, 0.238856f }, + { -0.809017f, -0.309017f, 0.500000f }, + { -0.688191f, -0.587785f, 0.425325f }, + { -0.681718f, -0.147621f, 0.716567f }, + { -0.442863f, -0.238856f, 0.864188f }, + { -0.587785f, -0.425325f, 0.688191f }, + { -0.309017f, -0.500000f, 0.809017f }, + { -0.147621f, -0.716567f, 0.681718f }, + { -0.425325f, -0.688191f, 0.587785f }, + { -0.162460f, -0.262866f, 0.951056f }, + { 0.442863f, -0.238856f, 0.864188f }, + { 0.162460f, -0.262866f, 0.951056f }, + { 0.309017f, -0.500000f, 0.809017f }, + { 0.147621f, -0.716567f, 0.681718f }, + { 0.000000f, -0.525731f, 0.850651f }, + { 0.425325f, -0.688191f, 0.587785f }, + { 0.587785f, -0.425325f, 0.688191f }, + { 0.688191f, -0.587785f, 0.425325f }, + { -0.955423f, 0.295242f, 0.000000f }, + { -0.951056f, 0.162460f, 0.262866f }, + { -1.000000f, 0.000000f, 0.000000f }, + { -0.850651f, 0.000000f, 0.525731f }, + { -0.955423f, -0.295242f, 0.000000f }, + { -0.951056f, -0.162460f, 0.262866f }, + { -0.864188f, 0.442863f, -0.238856f }, + { -0.951056f, 0.162460f, -0.262866f }, + { -0.809017f, 0.309017f, -0.500000f }, + { -0.864188f, -0.442863f, -0.238856f }, + { -0.951056f, -0.162460f, -0.262866f }, + { -0.809017f, -0.309017f, -0.500000f }, + { -0.681718f, 0.147621f, -0.716567f }, + { -0.681718f, -0.147621f, -0.716567f }, + { -0.850651f, 0.000000f, -0.525731f }, + { -0.688191f, 0.587785f, -0.425325f }, + { -0.587785f, 0.425325f, -0.688191f }, + { -0.425325f, 0.688191f, -0.587785f }, + { -0.425325f, -0.688191f, -0.587785f }, + { -0.587785f, -0.425325f, -0.688191f }, + { -0.688191f, -0.587785f, -0.425325f } +}; + + +CMD2MODEL *MD2MODEL_create(void) +{ + return (CMD2MODEL*)GB.New(GB.FindClass("Md2Model"), NULL, NULL); +}; + +int MD2MODEL_draw(CMD2MODEL *_object, double frame, int texture, float *pos, float *scale, float *rotate) +{ + int i; + int n, n2; + double interp; + GLfloat v_curr[3], v_next[3], v[3], norm[3]; + GLfloat *n_curr, *n_next; + const framemd2 *pframe1, *pframe2; + const vertexmd2 *pvert1, *pvert2; + bool enabled; + int nvert = 0; + + int *pglcmds; + glcmd *packet; + + if (texture < 0) + return 0; + + n = (int)frame; + interp = frame - n; + + if (n < 0 || n >= THIS->num_frames) + return 0; + + n2 = n + 1; + if (n2 >= THIS->num_frames) + n2 = 0; + + enabled = glIsEnabled(GL_TEXTURE_2D); + if (!enabled) + glEnable(GL_TEXTURE_2D); + + glPushMatrix(); + + if (pos) + glTranslatef(pos[0], pos[1], pos[2]); + + glRotatef(-90, 1, 0, 0); + glRotatef(-90, 0, 0, 1); + + if (rotate && rotate[0] != 0) + glRotatef(rotate[0], rotate[1], rotate[2], rotate[3]); + + glScalef(THIS->scale[0], THIS->scale[1], THIS->scale[2]); + + if (scale) + glScalef(scale[0], scale[1], scale[2]); + + glBindTexture(GL_TEXTURE_2D, texture); + +#if 1 + // pglcmds points at the start of the command list + pglcmds = THIS->glcmds; + + pframe1 = &THIS->frames[n]; + pframe2 = &THIS->frames[n2]; + + //fprintf(stderr, "\n******** %p: n = %d / %d | %p %p\n", _object, n, THIS->num_frames, pframe1->verts, pframe2->verts); + + // Draw the model + while ((i = *(pglcmds++)) != 0) + { + //fprintf(stderr, "i = %d\n", i); + + if (i < 0) + { + glBegin(GL_TRIANGLE_FAN); + i = -i; + } + else + { + glBegin(GL_TRIANGLE_STRIP); + } + + // Draw each vertex of this group + for (/* Nothing */ ; i > 0; --i, pglcmds += 3) + { + packet = (glcmd *)pglcmds; + + //fprintf(stderr, "%d (%d) ", i, packet->index); + + pvert1 = &pframe1->verts[packet->index]; + pvert2 = &pframe2->verts[packet->index]; + //if (!pvert1 || !pvert2) + // continue; + + // Pass texture coordinates to OpenGL + //glTexCoord2f (pGLcmd->s, 1.0f - pGLcmd->t); + glTexCoord2f(packet->s, packet->t); + + // Compute interpolated normal vector + n_curr = anorms_table[pvert1->normalIndex]; + n_next = anorms_table[pvert2->normalIndex]; + + norm[0] = n_curr[0] + interp * (n_next[0] - n_curr[0]); + norm[1] = n_curr[1] + interp * (n_next[1] - n_curr[1]); + norm[2] = n_curr[2] + interp * (n_next[2] - n_curr[2]); + + // Normal vector + glNormal3fv(norm); + + v_curr[0] = pframe1->scale[0] * pvert1->v[0] + pframe1->translate[0]; + v_curr[1] = pframe1->scale[1] * pvert1->v[1] + pframe1->translate[1]; + v_curr[2] = pframe1->scale[2] * pvert1->v[2] + pframe1->translate[2]; + + v_next[0] = pframe2->scale[0] * pvert2->v[0] + pframe2->translate[0]; + v_next[1] = pframe2->scale[1] * pvert2->v[1] + pframe2->translate[1]; + v_next[2] = pframe2->scale[2] * pvert2->v[2] + pframe2->translate[2]; + + v[0] = v_curr[0] + interp * (v_next[0] - v_curr[0]); + v[1] = v_curr[1] + interp * (v_next[1] - v_curr[1]); + v[2] = v_curr[2] + interp * (v_next[2] - v_curr[2]); + + glVertex3fv(v); + nvert++; + } + + //fprintf(stderr, "\n"); + + glEnd(); + } +#else + { + int j; + GLfloat s, t; + + glBegin (GL_TRIANGLES); + + for (i = 0; i < THIS->num_tris; ++i) + { + for (j = 0; j < 3; ++j) + { + pframe1 = &THIS->frames[n]; + pframe2 = &THIS->frames[n2]; + pvert1 = &pframe1->verts[THIS->triangles[i].vertex[j]]; + pvert2 = &pframe2->verts[THIS->triangles[i].vertex[j]]; + + s = (GLfloat)THIS->texcoords[THIS->triangles[i].st[j]].s / THIS->skinwidth; + t = (GLfloat)THIS->texcoords[THIS->triangles[i].st[j]].t / THIS->skinheight; + + glTexCoord2f (s, t); + + n_curr = anorms_table[pvert1->normalIndex]; + n_next = anorms_table[pvert2->normalIndex]; + + norm[0] = n_curr[0] + interp * (n_next[0] - n_curr[0]); + norm[1] = n_curr[1] + interp * (n_next[1] - n_curr[1]); + norm[2] = n_curr[2] + interp * (n_next[2] - n_curr[2]); + + glNormal3fv (norm); + + v_curr[0] = pframe1->scale[0] * pvert1->v[0] + pframe1->translate[0]; + v_curr[1] = pframe1->scale[1] * pvert1->v[1] + pframe1->translate[1]; + v_curr[2] = pframe1->scale[2] * pvert1->v[2] + pframe1->translate[2]; + + v_next[0] = pframe2->scale[0] * pvert2->v[0] + pframe2->translate[0]; + v_next[1] = pframe2->scale[1] * pvert2->v[1] + pframe2->translate[1]; + v_next[2] = pframe2->scale[2] * pvert2->v[2] + pframe2->translate[2]; + + v[0] = v_curr[0] + interp * (v_next[0] - v_curr[0]); + v[1] = v_curr[1] + interp * (v_next[1] - v_curr[1]); + v[2] = v_curr[2] + interp * (v_next[2] - v_curr[2]); + + glVertex3fv (v); + nvert++; + } + } + + glEnd(); + } +#endif + + //glScalef(1/THIS->scale[0], 1/THIS->scale[1], 1/THIS->scale[2]); + glPopMatrix(); + + if (!enabled) + glDisable(GL_TEXTURE_2D); + + return nvert; +} + + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Md2Model_new) + + THIS->texture = -1; + +END_METHOD + +BEGIN_METHOD_VOID(Md2Model_free) + + int i; + + GB.Free ((void *) &THIS->skins); + GB.Free ((void *) &THIS->texcoords); + GB.Free ((void *) &THIS->triangles); + + for (i = 0; i < THIS->num_frames; ++i) + GB.Free ((void *) &THIS->frames[i].verts); + + GB.Free ((void *) &THIS->frames); + GB.Free ((void *) &THIS->glcmds); + +END_METHOD + +/* + This method creates and loads new Md2 model. To use this in the program you should declare + it first as ie. + Public model as Md2Model + Then initiate it loading + model = Md2Model.Load(name as string) +*/ + +BEGIN_METHOD(Md2Model_Load, GB_STRING name) + + char *addr; + int len; + CMD2MODEL *mdl = NULL; + int i; + + if (GB.LoadFile(STRING(name), LENGTH(name), &addr, &len)) + return; + + mdl = MD2MODEL_create(); + + // Read header + { + int *p = (int *)addr; + + mdl->ident = *p++; + mdl->version = *p++; + mdl->skinwidth = *p++; + mdl->skinheight = *p++; + mdl->framesize = *p++; + mdl->num_skins = *p++; + mdl->num_vertices = *p++; + mdl->num_st = *p++; + mdl->num_tris = *p++; + mdl->num_glcmds = *p++; + mdl->num_frames = *p++; + mdl->offset_skins = *p++; + mdl->offset_st = *p++; + mdl->offset_tris = *p++; + mdl->offset_frames = *p++; + mdl->offset_glcmds = *p++; + mdl->offset_end = *p++; + } + + if ((mdl->ident != 844121161) || (mdl->version != 8)) + { + // Error! + GB.Error("Bad version or identifier"); + goto __ERROR; + } + + // Memory allocations + GB.Alloc ((void *) &mdl->skins, sizeof (skinmd2) * mdl->num_skins ); + GB.Alloc ((void *) &mdl->texcoords, sizeof (texCoordmd2) * mdl->num_st); + GB.Alloc ((void *) &mdl->triangles, sizeof (trianglemd2) * mdl->num_tris); + GB.Alloc ((void *) &mdl->frames, sizeof (framemd2) * mdl->num_frames); + GB.Alloc ((void *) &mdl->glcmds, sizeof (int) * mdl->num_glcmds); + + // Read model data + memcpy(mdl->skins, &addr[mdl->offset_skins], sizeof(skinmd2) * mdl->num_skins); + memcpy(mdl->texcoords, &addr[mdl->offset_st], sizeof(texCoordmd2) * mdl->num_st); + memcpy(mdl->triangles, &addr[mdl->offset_tris], sizeof(trianglemd2) * mdl->num_tris); + memcpy(mdl->glcmds, &addr[mdl->offset_glcmds], sizeof(int) * mdl->num_glcmds); + + // Read frames + { + char *p = &addr[mdl->offset_frames]; + + for (i = 0; i < mdl->num_frames; ++i) + { + // Memory allocation for vertices of this frame + GB.Alloc((void *) &mdl->frames[i].verts, sizeof (vertexmd2) * mdl->num_vertices); + + // Read frame data + memcpy(mdl->frames[i].scale, p, sizeof(float) * 3); p += sizeof(float) * 3; + memcpy(mdl->frames[i].translate, p, sizeof(float) * 3); p += sizeof(float) * 3; + memcpy(mdl->frames[i].name, p, 16); p += 16; + memcpy(mdl->frames[i].verts, p, sizeof(vertexmd2) * mdl->num_vertices); p += sizeof(vertexmd2) * mdl->num_vertices; + } + } + + mdl->scale[0] = 1; + mdl->scale[1] = 1; + mdl->scale[2] = 1; + + GB.ReleaseFile(addr, len); + GB.ReturnObject(mdl); + return; + +__ERROR: + + GB.ReleaseFile(addr, len); + GB.Unref(POINTER(&mdl)); + +END_METHOD + +BEGIN_METHOD(Md2Model_Scale, GB_FLOAT sx; GB_FLOAT sy; GB_FLOAT sz) + + THIS->scale[0] = VARG(sx); + THIS->scale[1] = VARG(sy); + THIS->scale[2] = VARG(sz); + +END_METHOD + +BEGIN_PROPERTY(Md2Model_Count) + + GB.ReturnInteger(THIS->num_frames); + +END_PROPERTY + +BEGIN_METHOD(Md2Model_get, GB_INTEGER frame) + + int frame = VARG(frame); + + if (frame < 0 || frame >= THIS->num_frames) + { + GB.Error(GB_ERR_BOUND); + return; + } + + THIS->frame = frame; + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(Md2Model_Texture) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->texture); + else + THIS->texture = VPROP(GB_INTEGER); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Md2Model_Frame_Draw, GB_FLOAT interp; GB_INTEGER texture) + + MD2MODEL_draw(THIS, (double)THIS->frame + VARGOPT(interp, 0.0), VARGOPT(texture, THIS->texture), NULL, NULL, NULL); + +END_METHOD + +BEGIN_PROPERTY(Md2Model_Frame_Name) + + GB.ReturnNewZeroString(THIS->frames[THIS->frame].name); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC Md2ModelFrameDesc[] = +{ + GB_DECLARE_VIRTUAL(".Md2Model.Frame"), + + GB_PROPERTY_READ("Name", "s", Md2Model_Frame_Name), + GB_METHOD("Draw", NULL, Md2Model_Frame_Draw, "[(Interpolation)f(Texture)i]"), + + GB_END_DECLARE +}; + +GB_DESC Md2ModelDesc[] = +{ + GB_DECLARE("Md2Model", sizeof(CMD2MODEL)), GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, Md2Model_new, NULL), + GB_METHOD("_free", NULL, Md2Model_free, NULL), + + GB_STATIC_METHOD("Load", "Md2Model" , Md2Model_Load, "(Name)s"), + + GB_PROPERTY_READ("Count", "i", Md2Model_Count), + GB_METHOD("_get", ".Md2Model.Frame", Md2Model_get, "(Frame)i"), + + GB_METHOD("Scale", NULL, Md2Model_Scale, "(SX)f(SY)f(SZ)f" ), + + GB_PROPERTY("Texture", "i", Md2Model_Texture), + + GB_END_DECLARE +}; + + diff --git a/gb.opengl/src/sge/cmd2model.h b/gb.opengl/src/sge/cmd2model.h new file mode 100644 index 00000000..b3fa2c39 --- /dev/null +++ b/gb.opengl/src/sge/cmd2model.h @@ -0,0 +1,167 @@ +/*************************************************************************** + + cmd2model.h + + (c) 2012 Tomasz Kolodziejczyk "Tommyline" + + Based on David HENRY's md2.c md2 model loader and renderer +========================================================================= + * + * md2.c -- md2 model loader + * last modification: aug. 14, 2007 + * + * Copyright (c) 2005-2007 David HENRY + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * gcc -Wall -ansi -lGL -lGLU -lglut md2.c -o md2 + * +========================================================================= + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General License for more details. + + You should have received a copy of the GNU General License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMD2MODEL_H +#define __CMD2MODEL_H + +#include "gambas.h" +#include "main.h" +#include "cmd2object.h" + +#ifndef __CMD2MODEL_C +extern GB_DESC Md2ModelFrameDesc[]; +extern GB_DESC Md2ModelDesc[]; +#endif + +/* Vector */ +typedef float vec3[3]; + + +/* Texture name */ +typedef +struct md2_skin +{ + char name[68]; +}skinmd2; + +/* Texture coords */ +typedef +struct md2_texCoordmd2 +{ + short s; + short t; +}texCoordmd2; + +/* Triangle info */ +typedef +struct md2_trianglemd2 +{ + unsigned short vertex[3]; + unsigned short st[3]; +}trianglemd2; + +/* Compressed vertexmd2 */ +typedef +struct md2_vertexmd2 +{ + unsigned char v[3]; + unsigned char normalIndex; +}vertexmd2; + +/* Model frame */ +typedef +struct md2_frame +{ + float scale[3]; + float translate[3]; + char name[16]; + vertexmd2 *verts; +}framemd2; + +/* GL command packet */ +typedef +struct md2_glcmd +{ + float s; + float t; + int index; +}glcmd; + + +typedef + struct CMD2MODEL { + GB_BASE ob; + //Header + int ident; + int version; + + int skinwidth; + int skinheight; + + int framesize; + + int num_skins; + int num_vertices; + int num_st; + int num_tris; + int num_glcmds; + int num_frames; + + int offset_skins; + int offset_st; + int offset_tris; + int offset_frames; + int offset_glcmds; + int offset_end; + //End header + + skinmd2 *skins; + texCoordmd2 *texcoords; + trianglemd2 *triangles; + framemd2 *frames; + int *glcmds; + + //Model specific data + float scale[3]; + int frame; // frame being accessed with the [] operator + GLuint texture; + //End model specific data + } + CMD2MODEL; + + +CMD2MODEL *MD2MODEL_create(void); +int MD2MODEL_draw(CMD2MODEL *_object, double frame, int texture, float *pos, float *rotate, float *scale); + +#endif diff --git a/gb.opengl/src/sge/cmd2object.c b/gb.opengl/src/sge/cmd2object.c new file mode 100644 index 00000000..96194144 --- /dev/null +++ b/gb.opengl/src/sge/cmd2object.c @@ -0,0 +1,175 @@ +/*************************************************************************** + + cmd2object.c + + (c) 2012 Tomasz Kołodziejczyk "Tommyline" + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMD2OBJECT_C + +#include "cmd2object.h" + +#define THIS OBJECT(CMD2OBJECT) + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Md2Object_new, GB_OBJECT model) + + CMD2MODEL *model = VARG(model); + + if (GB.CheckObject(model)) + return; + + THIS->model = model; + GB.Ref(model); + + THIS->texture = -1; + + THIS->scale[0] = THIS->scale[1] = THIS->scale[2] = 1; + +END_METHOD + +BEGIN_METHOD_VOID(Md2Object_free) + + GB.Unref(POINTER(&THIS->model)); + +END_METHOD + + +BEGIN_METHOD(Md2Object_Move, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + THIS->pos[0] = VARG(x); + THIS->pos[1] = VARG(y); + THIS->pos[2] = VARG(z); + +END_METHOD + +BEGIN_PROPERTY(Md2Object_X) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->pos[0]); + else + THIS->pos[0] = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Y) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->pos[1]); + else + THIS->pos[1] = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Z) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->pos[2]); + else + THIS->pos[2] = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_METHOD(Md2Object_Scale, GB_FLOAT sx; GB_FLOAT sy; GB_FLOAT sz) + + THIS->scale[0] = VARG(sx); + THIS->scale[1] = VARG(sy); + THIS->scale[2] = VARG(sz); + +END_METHOD + +BEGIN_METHOD(Md2Object_Rotate, GB_FLOAT angle; GB_FLOAT rx; GB_FLOAT ry; GB_FLOAT rz) + + THIS->rotate[0] = VARG(angle); + THIS->rotate[1] = VARG(rx); + THIS->rotate[2] = VARG(ry); + THIS->rotate[3] = VARG(rz); + +END_METHOD + +BEGIN_PROPERTY(Md2Object_Texture) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->texture); + else + THIS->texture = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Frame) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->frame); + else + THIS->frame = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_METHOD_VOID(Md2Object_Draw) + + int texture = THIS->texture; + + if (texture < 0) + texture = THIS->model->texture; + + GB.ReturnInteger(MD2MODEL_draw(THIS->model, THIS->frame, texture, THIS->pos, THIS->scale, THIS->rotate)); + +END_METHOD + +BEGIN_PROPERTY(Md2Object_Model) + + GB.ReturnObject(THIS->model); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Count) + + GB.ReturnInteger(THIS->model->num_frames); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC Md2ObjectDesc[] = +{ + GB_DECLARE("Md2Object", sizeof(CMD2OBJECT)), + + GB_METHOD("_new", NULL, Md2Object_new, "(Model)Md2Model;"), + GB_METHOD("_free", NULL, Md2Object_free, NULL), + + GB_PROPERTY_READ("X", "f", Md2Object_X), + GB_PROPERTY_READ("Y", "f", Md2Object_Y), + GB_PROPERTY_READ("Z", "f", Md2Object_Z), + + GB_METHOD("Move", NULL, Md2Object_Move, "(X)f(Y)f(Z)f"), + GB_METHOD("Scale", NULL, Md2Object_Scale, "(SX)f(SY)f(SZ)f"), + GB_METHOD("Rotate", NULL, Md2Object_Rotate, "(Angle)f(RX)f(RY)f(RZ)f"), + + GB_PROPERTY("Texture", "i", Md2Object_Texture), + GB_PROPERTY("Frame", "f", Md2Object_Frame), + GB_PROPERTY_READ("Count", "i", Md2Object_Count), + GB_METHOD("Draw", "i", Md2Object_Draw, NULL), + + GB_PROPERTY_READ("Model", "Md2Model", Md2Object_Model), + + GB_END_DECLARE +}; + + diff --git a/gb.opengl/src/sge/cmd2object.h b/gb.opengl/src/sge/cmd2object.h new file mode 100644 index 00000000..7fa5ad4c --- /dev/null +++ b/gb.opengl/src/sge/cmd2object.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + cmd2object.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General License for more details. + + You should have received a copy of the GNU General License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMD2OBJECT_H +#define __CMD2OBJECT_H + +#include "gambas.h" +#include "main.h" +#include "cmd2model.h" + +#ifndef __CMD2OBJECT_C +extern GB_DESC Md2ObjectDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + struct CMD2MODEL *model; + float pos[3]; + float scale[3]; + float rotate[4]; + double frame; + GLuint texture; + GB_VARIANT tag; + } + CMD2OBJECT; + +#endif diff --git a/gb.opengl/src/sge/gb.opengl.sge.component b/gb.opengl/src/sge/gb.opengl.sge.component new file mode 100644 index 00000000..c10cba65 --- /dev/null +++ b/gb.opengl/src/sge/gb.opengl.sge.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +State=Stable +Requires=gb.opengl diff --git a/gb.opengl/src/sge/main.c b/gb.opengl/src/sge/main.c new file mode 100644 index 00000000..03fddcb9 --- /dev/null +++ b/gb.opengl/src/sge/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.c + + gb.sge component + + (c) 2013 Tomek Kolodziejczyk + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "cmd2model.h" +#include "cmd2object.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + Md2ModelFrameDesc, + Md2ModelDesc, + Md2ObjectDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.opengl/src/sge/main.h b/gb.opengl/src/sge/main.h new file mode 100644 index 00000000..07fd9360 --- /dev/null +++ b/gb.opengl/src/sge/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + gb.sge component + + (c) 2013 Tomek Kolodziejczyk + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "GL/gl.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.openssl/AUTHORS b/gb.openssl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/COPYING b/gb.openssl/COPYING new file mode 100644 index 00000000..3a12a2d8 --- /dev/null +++ b/gb.openssl/COPYING @@ -0,0 +1,28 @@ +Copyright (C) 2013-2019 Tobias Boege + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, see . + +Linking gb.openssl statically or dynamically with other modules is making a +combined work based on gb.openssl. Thus, the terms and conditions of the +GNU General Public License cover the whole combination. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library under +certain conditions as described in each individual source file, and distribute +linked combinations including the two. You must obey the GNU General Public +License in all respects for all of the code used other than OpenSSL. +If you modify file(s) with this exception, you may extend this exception +to your version of the file(s), but you are not obligated to do so. +If you do not wish to do so, delete this exception statement from your +version. If you delete this exception statement from all source files +in the program, then also delete it here. diff --git a/gb.openssl/COPYING.GPL b/gb.openssl/COPYING.GPL new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/gb.openssl/COPYING.GPL @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/gb.openssl/ChangeLog b/gb.openssl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/INSTALL b/gb.openssl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.openssl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.openssl/Makefile.am b/gb.openssl/Makefile.am new file mode 100644 index 00000000..fa6d9c5d --- /dev/null +++ b/gb.openssl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @OPENSSL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.openssl/NEWS b/gb.openssl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/README b/gb.openssl/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/acinclude.m4 b/gb.openssl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.openssl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.openssl/component.am b/gb.openssl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.openssl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.openssl/configure.ac b/gb.openssl/configure.ac new file mode 100644 index 00000000..469b2305 --- /dev/null +++ b/gb.openssl/configure.ac @@ -0,0 +1,21 @@ +dnl ---- configure.ac for gb.openssl + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-openssl],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.openssl) +LT_INIT + +# We really only want to make sure that EVP_MD_do_all() exists. +# Don't do a version check because some systems may have the +# function from somewhere but don't have a high enough version. +#GB_COMPONENT_PKG_CONFIG(openssl, OPENSSL, gb.openssl, [src], 'openssl >= 1.0.0' libcrypto) + +AC_CHECK_LIB(crypto,EVP_MD_do_all,,touch DISABLED) + +GB_COMPONENT_PKG_CONFIG(openssl, OPENSSL, gb.openssl, [src], libcrypto) + +AC_CONFIG_FILES([Makefile src/Makefile]) +AC_OUTPUT +GB_PRINT_MESSAGES diff --git a/gb.openssl/gambas.h b/gb.openssl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.openssl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.openssl/gb_common.h b/gb.openssl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.openssl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.openssl/m4 b/gb.openssl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.openssl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.openssl/openssl-test/.directory b/gb.openssl/openssl-test/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.openssl/openssl-test/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.openssl/openssl-test/.icon.png b/gb.openssl/openssl-test/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb57088d4d27e28a1e2efdf17950e82cf02d8bf GIT binary patch literal 11170 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 cBO2S-%+hm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#define __C_CIPHER_C + +#include + +#include +#include +#include + +#include "main.h" +#include "c_cipher.h" + +/* + * Cipher + */ + +static GB_ARRAY _clist = NULL; + +static void clist_func(const EVP_CIPHER *ciph, const char *from, + const char *to, void *arg) +{ + const char *src; + + if (!ciph) + return; + src = EVP_CIPHER_name(ciph); + *((const char **) GB.Array.Add(_clist)) = GB.NewZeroString(src); +} + +static void get_clist(void) +{ + GB.Array.New(&_clist, GB_T_STRING, 0); + /* XXX: There is EVP_CIPHER_do_all_sorted() but that still returns + * entries twice (NOT next to each other), so we sort manually */ + EVP_CIPHER_do_all(clist_func, NULL); + sort_and_dedupe(_clist); +} + +/**G + * Return a list of all ciphers present in the local OpenSSL crypto library. + **/ +BEGIN_PROPERTY(Cipher_List) + + GB_FUNCTION copyfn; + GB_VALUE *copy; + + if (!_clist) + get_clist(); + + if (GB.GetFunction(©fn, _clist, "Copy", NULL, NULL)) { + GB.Error("Can't copy array"); + return; + } + copy = GB.Call(©fn, 0, 0); + GB.ReturnObject(copy->_object.value); + +END_PROPERTY + +BEGIN_METHOD_VOID(Cipher_init) + + OpenSSL_add_all_ciphers(); + +END_METHOD + +BEGIN_METHOD_VOID(Cipher_exit) + + if (!_clist) + return; + GB.Unref((void **) &_clist); + _clist = NULL; + +END_METHOD + +const static EVP_CIPHER *_method; + +/**G + * Return a virtual object representing a cipher algorithm by giving its + * name. Valid names can be looked up from the Cipher.List property. + **/ +BEGIN_METHOD(Cipher_get, GB_STRING method) + + _method = EVP_get_cipherbyname(GB.ToZeroString(ARG(method))); + if (!_method) { + GB.Error("Unknown cipher method"); + return; + } + RETURN_SELF(); + +END_METHOD + +/**G + * Check if the named cipher algorithm is supported. + **/ +BEGIN_METHOD(Cipher_IsSupported, GB_STRING method) + + _method = EVP_get_cipherbyname(STRING(method)); + GB.ReturnBoolean(!!_method); + +END_METHOD + +GB_DESC CCipher[] = { + GB_DECLARE("Cipher", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("List", "String[]", Cipher_List), + + GB_STATIC_METHOD("_init", NULL, Cipher_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Cipher_exit, NULL), + GB_STATIC_METHOD("_get", ".Cipher.Method", Cipher_get, "(Method)s"), + GB_STATIC_METHOD("IsSupported", "b", Cipher_IsSupported, "(Method)s"), + + GB_END_DECLARE +}; + +/* + * .Cipher.Method + */ + +/**G + * Return the key length that this cipher algorithm expects. + * + * See also: Cipher.Encrypt() + **/ +BEGIN_PROPERTY(CipherMethod_KeyLength) + + if (READ_PROPERTY) { + GB.ReturnInteger(EVP_CIPHER_key_length(_method)); + return; + } + +END_PROPERTY + +/**G + * Return the initialisation vector length that this cipher algorithm + * expects. + * + * See also: Cipher.Encrypt() + **/ +BEGIN_PROPERTY(CipherMethod_IvLength) + + if (READ_PROPERTY) { + GB.ReturnInteger(EVP_CIPHER_iv_length(_method)); + return; + } + +END_PROPERTY + +static char *do_cipher(const unsigned char *data, unsigned int dlen, + const unsigned char *key, const unsigned char *iv, + unsigned int *length, int enc, char **errmsg) +{ + EVP_CIPHER_CTX *ctx; + unsigned char block[1024 + EVP_MAX_BLOCK_LENGTH]; + char *out; + unsigned int ilen; + int blen; + + *errmsg = NULL; + ctx = EVP_CIPHER_CTX_new(); + + if (!ctx) { + *errmsg = "Could not allocate cipher context"; + return NULL; + } + EVP_CIPHER_CTX_init(ctx); + if (!EVP_CipherInit_ex(ctx, _method, NULL, key, iv, enc)) + return NULL; + + out = NULL; + *length = 0; + memset(block, 0, sizeof(block)); + while (dlen) { + ilen = MIN(dlen, 1024); + if (!EVP_CipherUpdate(ctx, block, &blen, data, ilen)) + goto __ERROR; + out = GB.AddString(out, (char *) block, blen); + *length += blen; + data += ilen; + dlen -= ilen; + } + if (!EVP_CipherFinal_ex(ctx, block, &blen)) + goto __ERROR; + if (!EVP_CIPHER_CTX_cleanup(ctx)) + goto __ERROR; + if (blen) { + out = GB.AddString(out, (char *) block, blen); + *length += blen; + } + EVP_CIPHER_CTX_free(ctx); + return out; + +__ERROR: + GB.FreeString(&out); + EVP_CIPHER_CTX_free(ctx); + return NULL; +} + +typedef struct { + GB_BASE ob; + char *cipher, *key, *iv; +} CCIPHERTEXT; + +/**G + * Encrypt a plaintext using the given key and initialisation vector. + **/ +BEGIN_METHOD(CipherMethod_Encrypt, GB_STRING plain; GB_STRING key; + GB_STRING iv) + + unsigned char key[EVP_CIPHER_key_length(_method)]; + unsigned char iv[EVP_CIPHER_iv_length(_method)]; + unsigned int length; + char *cipher, *errmsg; + CCIPHERTEXT *res; + + bzero(key, sizeof(key)); + bzero(iv, sizeof(iv)); + if (MISSING(key)) { + assert(RAND_bytes(key, sizeof(key))); + } else { + if (LENGTH(key) != sizeof(key)) { + GB.Error("Key length does not match method"); + return; + } + memcpy(key, STRING(key), sizeof(key)); + } + if (MISSING(iv)) { + assert(RAND_bytes(iv, sizeof(iv))); + } else { + if (LENGTH(iv) != sizeof(iv)) { + GB.Error("InitVector length does not match method"); + return; + } + memcpy(iv, STRING(iv), sizeof(iv)); + } + + cipher = do_cipher((uchar *) STRING(plain), LENGTH(plain), key, iv, + &length, 1, &errmsg); + if (!cipher) { + GB.Error(errmsg ? errmsg : "Encryption failed"); + return; + } + + GB.Push(3, GB_T_STRING, cipher, length, + GB_T_STRING, key, sizeof(key), + GB_T_STRING, iv, sizeof(iv)); + res = GB.New(GB.FindClass("CipherText"), NULL, (void*)(intptr_t) 3); + GB.FreeString(&cipher); + GB.ReturnObject(res); + +END_METHOD + +/**G + * Decrypt a ciphertext generated by Encrypt(). + **/ +BEGIN_METHOD(CipherMethod_Decrypt, GB_OBJECT ciph) + + CCIPHERTEXT *ciph = VARG(ciph); + unsigned int length; + char *plain, *errmsg; + + plain = do_cipher((uchar *) ciph->cipher, + GB.StringLength(ciph->cipher), + (uchar *) ciph->key, (uchar *) ciph->iv, + &length, 0, &errmsg); + if (!plain) { + GB.Error(errmsg ? errmsg : "Decryption failed"); + return; + } + GB.ReturnNewString(plain, length); + GB.FreeString(&plain); + +END_METHOD + +/* + * Retain compatibility with the 'openssl' program for now. + */ + +#define ITER 1 + +/**G# + * Encrypt a plaintext with a password to a standalone string. + * + * If 'salt' is given, it should be an 8-byte string. If it is not, it is + * padded with zeros or truncated to a size of 8. If the parameter is not + * given, a pseudo-random salt is generated. + * + * This uses a less up-to-date method (PKCS#5 v1.5) to generate an + * encryption key from the password. This is for compatibility with the + * openssl program when invoked like: + * + * `echo -n Plain | openssl CipherMethod -k Password -S HexSalt` + **/ +BEGIN_METHOD(CipherMethod_EncryptSalted, GB_STRING plain; GB_STRING passwd; + GB_STRING salt) + + unsigned char salt[8]; + unsigned char key[EVP_CIPHER_key_length(_method)]; + unsigned char iv[EVP_CIPHER_iv_length(_method)]; + unsigned int length; + char *cipher, *res, *errmsg; + + bzero(salt, sizeof(salt)); + if (MISSING(salt)) { + assert(RAND_bytes(salt, sizeof(salt))); + } else { + bzero(salt, sizeof(salt)); + memcpy(salt, STRING(salt), MIN(sizeof(salt), LENGTH(salt))); + } + +// "openssl enc" >= 1.1.0 uses SHA256 by default instead of MD5 +// EVP_BytesToKey(_method, EVP_sha256(), salt, (uchar *) STRING(passwd), +// LENGTH(passwd), ITER, key, iv); + EVP_BytesToKey(_method, EVP_md5(), salt, (uchar *) STRING(passwd), + LENGTH(passwd), ITER, key, iv); + cipher = do_cipher((uchar *) STRING(plain), LENGTH(plain), key, iv, + &length, 1, &errmsg); + if (!cipher) { + GB.Error(errmsg ? errmsg : "Encryption failed"); + return; + } + res = GB.NewZeroString("Salted__"); + res = GB.AddString(res, (char *) salt, sizeof(salt)); + res = GB.AddString(res, cipher, length); + GB.FreeString(&cipher); + GB.ReturnString(res); + GB.ReturnBorrow(); + GB.FreeString(&res); + GB.ReturnRelease(); + +END_METHOD + +/**G# + * Decrypt a ciphertext obtained from EncryptSalted(). + **/ +BEGIN_METHOD(CipherMethod_DecryptSalted, GB_STRING cipher; GB_STRING passwd) + + unsigned char salt[8], *cipher; + unsigned char key[EVP_CIPHER_key_length(_method)]; + unsigned char iv[EVP_CIPHER_iv_length(_method)]; + unsigned int clen, length; + char *plain, *errmsg; + + if (!strstr(STRING(cipher), "Salted__")) { + GB.Error("Unrecognised cipher string format"); + return; + } + /* salt begins at STRING(cipher) + strlen("Salted__") */ + memcpy(salt, STRING(cipher) + 8, sizeof(salt)); + +// "openssl enc" >= 1.1.0 uses SHA256 by default instead of MD5 +// EVP_BytesToKey(_method, EVP_sha256(), salt, (uchar *) STRING(passwd), +// LENGTH(passwd), ITER, key, iv); + EVP_BytesToKey(_method, EVP_md5(), salt, (uchar *) STRING(passwd), + LENGTH(passwd), ITER, key, iv); + cipher = (uchar *) STRING(cipher) + 8 + sizeof(salt); + clen = LENGTH(cipher) - (uint) (cipher - (uchar *) STRING(cipher)); + plain = do_cipher((uchar *) cipher, clen, key, iv, &length, 0, &errmsg); + if (!plain) { + GB.Error(errmsg ? errmsg : "Decryption failed"); + return; + } + GB.ReturnNewString(plain, length); + GB.FreeString(&plain); + +END_METHOD + +#if 0 +/**G + * Encrypt a plaintext with a password to a standalone string. + * + * We use the PKCS#5-v2.0-conforming PBKDF2 HMAC-SHA1 to generate an + * encryption key from a password. + **/ +BEGIN_METHOD(CipherMethod_EncryptSalted, GB_STRING plain; GB_STRING passwd) + + char salt[8]; /* 8 is recommended as minimum */ + char key[EVP_CIPHER_key_length(_method)]; + + bzero(key, sizeof(key)); + RAND_bytes(salt, sizeof(salt)); + PKCS5_PBKDF_HMAC_SHA1(STRING(passwd), LENGTH(passwd), salt, + sizeof(salt), ITER, sizeof(key), key); + + +END_METHOD + +/**G + * Decrypt a ciphertext obtained from EncryptSalted(). + **/ +BEGIN_METHOD(CipherMethod_DecryptSalted, GB_STRING cipher; GB_STRING passwd) + + + +END_METHOD +#endif + +GB_DESC CCipherMethod[] = { + /* + * TODO: Maybe add a stream interface? + */ + GB_DECLARE(".Cipher.Method", 0), + GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("KeyLength", "i", CipherMethod_KeyLength), + GB_STATIC_PROPERTY_READ("IvLength", "i", CipherMethod_IvLength), + + GB_STATIC_METHOD("Encrypt", "CipherText", CipherMethod_Encrypt, "(Plain)s[(Key)s(InitVector)s]"), + GB_STATIC_METHOD("Decrypt", "s", CipherMethod_Decrypt, "(Cipher)CipherText"), + + GB_STATIC_METHOD("EncryptSalted", "s", CipherMethod_EncryptSalted, "(Plain)s(Password)s[(Salt)s]"), + GB_STATIC_METHOD("DecryptSalted", "s", CipherMethod_DecryptSalted, "(Cipher)s(Password)s"), + + GB_END_DECLARE +}; + +/* + * CipherText + */ + +#define THIS ((CCIPHERTEXT *) _object) + +BEGIN_PROPERTY(CipherText_Cipher) + + GB.ReturnString(THIS->cipher); + +END_PROPERTY + +BEGIN_PROPERTY(CipherText_Key) + + GB.ReturnString(THIS->key); + +END_PROPERTY + +BEGIN_PROPERTY(CipherText_InitVector) + + GB.ReturnString(THIS->iv); + +END_PROPERTY + +BEGIN_METHOD(CipherText_new, GB_STRING ciph; GB_STRING key; GB_STRING iv) + + THIS->cipher = GB.NewString(STRING(ciph), LENGTH(ciph)); + THIS->key = GB.NewString(STRING(key), LENGTH(key)); + THIS->iv = GB.NewString(STRING(iv), LENGTH(iv)); + +END_METHOD + +BEGIN_METHOD_VOID(CipherText_free) + + GB.FreeString(&THIS->cipher); + GB.FreeString(&THIS->key); + GB.FreeString(&THIS->iv); + +END_METHOD + +GB_DESC CCipherText[] = { + GB_DECLARE("CipherText", sizeof(CCIPHERTEXT)), + + GB_PROPERTY_READ("Cipher", "s", CipherText_Cipher), + GB_PROPERTY_READ("Key", "s", CipherText_Key), + GB_PROPERTY_READ("InitVector", "s", CipherText_InitVector), + + GB_METHOD("_new", NULL, CipherText_new, "(Cipher)s(Key)s(InitVector)s"), + GB_METHOD("_free", NULL, CipherText_free, NULL), + + GB_END_DECLARE +}; diff --git a/gb.openssl/src/c_cipher.h b/gb.openssl/src/c_cipher.h new file mode 100644 index 00000000..d255efb2 --- /dev/null +++ b/gb.openssl/src/c_cipher.h @@ -0,0 +1,42 @@ +/* + * c_cipher.h + * + * Copyright (C) 2013-2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#ifndef __C_CIPHER_H +#define __C_CIPHER_H + +#ifndef __C_CIPHER_C +extern GB_DESC CCipher[], CCipherMethod[], CCipherText[]; +#endif + +#endif /* __C_CIPHER_H */ diff --git a/gb.openssl/src/c_digest.c b/gb.openssl/src/c_digest.c new file mode 100644 index 00000000..53db9e1c --- /dev/null +++ b/gb.openssl/src/c_digest.c @@ -0,0 +1,289 @@ +/* + * c_digest.c - Digest and .Digest.Method classes + * + * Copyright (C) 2013-2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#define __C_DIGEST_C + +#include +#include +#include + +#include "main.h" +#include "c_digest.h" + +/* + * Digest + */ + +static GB_ARRAY _dlist = NULL; + +static void dlist_func(const EVP_MD *dgst, const char *from, + const char *to, void *arg) +{ + const char *src; + + if (!dgst) + return; + src = EVP_MD_name(dgst); + *((const char **) GB.Array.Add(_dlist)) = GB.NewZeroString(src); +} + +static void get_dlist(void) +{ + GB.Array.New(&_dlist, GB_T_STRING, 0); + EVP_MD_do_all(dlist_func, NULL); + sort_and_dedupe(_dlist); +} + +/**G + * Return a list of all digests present in the local OpenSSL crypto library. + **/ +BEGIN_PROPERTY(Digest_List) + + GB_FUNCTION copyfn; + GB_VALUE *copy; + + if (!_dlist) + get_dlist(); + + if (GB.GetFunction(©fn, _dlist, "Copy", NULL, NULL)) { + GB.Error("Can't copy array"); + return; + } + copy = GB.Call(©fn, 0, 0); + GB.ReturnObject(copy->_object.value); + +END_PROPERTY + +BEGIN_METHOD_VOID(Digest_init) + + OpenSSL_add_all_digests(); + +END_METHOD + +BEGIN_METHOD_VOID(Digest_exit) + + if (!_dlist) + return; + GB.Unref((void **) &_dlist); + _dlist = NULL; + +END_METHOD + +const static EVP_MD *_method; + +/**G + * Return a virtual object representing a digest algorithm by giving its + * name. Valid names can be looked up from Digest.List. + **/ +BEGIN_METHOD(Digest_get, GB_STRING method) + + _method = EVP_get_digestbyname(GB.ToZeroString(ARG(method))); + if (!_method) { + GB.Error("Unknown digest method"); + return; + } + RETURN_SELF(); + +END_METHOD + +/**G + * Check whether the named digest algorithm is valid. + **/ +BEGIN_METHOD(Digest_IsSupported, GB_STRING method) + + _method = EVP_get_digestbyname(STRING(method)); + GB.ReturnBoolean(!!_method); + +END_METHOD + +GB_DESC CDigest[] = { + GB_DECLARE("Digest", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("List", "String[]", Digest_List), + + GB_STATIC_METHOD("_init", NULL, Digest_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Digest_exit, NULL), + GB_STATIC_METHOD("_get", ".Digest.Method", Digest_get, "(Method)s"), + GB_STATIC_METHOD("IsSupported", "b", Digest_IsSupported, "(Method)s"), + + GB_END_DECLARE +}; + +/* + * .Digest.Method + */ + +/* + * Forward compatibility for openssl 1.1 + * + * OPENSSL_zalloc() and OPENSSL_clear_free() have been written according to + * the behaviour described in their manpage. + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +/* + * Special treatment for OpenSSL 0.9.8*. The version hex below is 0.9.8zh. + * The *_new() and *_free() routines were called *_create() and *_destroy() + * there. + */ +#if OPENSSL_VERSION_NUMBER <= 0x0090821fL +inline int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) +{ + /* Don't assume ctx->md_data was cleaned in EVP_Digest_Final, + * because sometimes only copies of the context are ever finalised. + */ + if (ctx->digest && ctx->digest->cleanup + && !M_EVP_MD_CTX_test_flags(ctx,EVP_MD_CTX_FLAG_CLEANED)) + ctx->digest->cleanup(ctx); + if (ctx->digest && ctx->digest->ctx_size && ctx->md_data + && !M_EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) { + OPENSSL_cleanse(ctx->md_data,ctx->digest->ctx_size); + OPENSSL_free(ctx->md_data); + } +#ifndef OPENSSL_NO_ENGINE + if(ctx->engine) + /* The EVP_MD we used belongs to an ENGINE, release the + * functional reference we held for this reason. */ + do_engine_finish(ctx->engine); +#endif + memset(ctx,'\0',sizeof *ctx); + return 1; +} + +inline void EVP_MD_CTX_init(EVP_MD_CTX *ctx) +{ + memset(ctx, '\0', sizeof *ctx); +} + +inline EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof *ctx); + + if (ctx) + EVP_MD_CTX_init(ctx); + return ctx; +} + +inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} +#else /* Anything recent */ +static void OPENSSL_clear_free(void *str, size_t num) +{ + OPENSSL_cleanse(str, num); + OPENSSL_free(str); +} + +static void *OPENSSL_zalloc(size_t num) +{ + void *ret = OPENSSL_malloc(num); + + if (!ret) + return NULL; + memset(ret, 0, num); + return ret; +} + +static int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) +{ + if (ctx == NULL) + return 1; + + /* + * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because + * sometimes only copies of the context are ever finalised. + */ + if (ctx->digest && ctx->digest->cleanup + && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED)) + ctx->digest->cleanup(ctx); + if (ctx->digest && ctx->digest->ctx_size && ctx->md_data + && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) { + OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size); + } + EVP_PKEY_CTX_free(ctx->pctx); +#ifndef OPENSSL_NO_ENGINE + ENGINE_finish(ctx->engine); +#endif + OPENSSL_cleanse(ctx, sizeof(*ctx)); + + return 1; +} + +static EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); +} + +static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_reset(ctx); + OPENSSL_free(ctx); +} +#endif +#endif + +/**G + * Hash the given string using this digest algorithm. + **/ +BEGIN_METHOD(DigestMethod_Hash, GB_STRING data) + + EVP_MD_CTX *ctx; + char digest[EVP_MAX_MD_SIZE]; + unsigned int length; + + ctx = EVP_MD_CTX_new(); + if (!ctx) { + GB.Error("Could not allocate digest context"); + return; + } + memset(digest, 0, sizeof(digest)); + EVP_DigestInit(ctx, _method); + EVP_DigestUpdate(ctx, STRING(data), LENGTH(data)); + EVP_DigestFinal(ctx, (unsigned char *) digest, &length); + EVP_MD_CTX_free(ctx); + GB.ReturnNewString(digest, length); + +END_METHOD + +GB_DESC CDigestMethod[] = { + GB_DECLARE(".Digest.Method", 0), + GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Hash", "s", DigestMethod_Hash, "(Data)s"), + GB_STATIC_METHOD("_call", "s", DigestMethod_Hash, "(Data)s"), + + GB_END_DECLARE +}; diff --git a/gb.openssl/src/c_digest.h b/gb.openssl/src/c_digest.h new file mode 100644 index 00000000..7724bd33 --- /dev/null +++ b/gb.openssl/src/c_digest.h @@ -0,0 +1,43 @@ +/* + * c_digest.h + * + * Copyright (C) 2013-2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#ifndef __C_DIGEST_H +#define __C_DIGEST_H + +#ifndef __C_DIGEST_C +extern GB_DESC CDigest[], CDigestMethod[]; +#endif + +#endif /* __C_DIGEST_H */ + diff --git a/gb.openssl/src/c_hmac.c b/gb.openssl/src/c_hmac.c new file mode 100644 index 00000000..5f819602 --- /dev/null +++ b/gb.openssl/src/c_hmac.c @@ -0,0 +1,115 @@ +/* + * c_hmac.c - HMac class + * + * Copyright (C) 2013-2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#define __C_HMAC_C + +#include + +#include "main.h" +#include "c_hmac.h" + +/* + * HMac + */ + +static const EVP_MD *get_method(GB_VARIANT_VALUE *varg) +{ + const EVP_MD *method; + + switch (varg->type) { + case GB_T_INTEGER: + GB.Deprecated("gb.openssl", "HMac() with integer method", "a string method like 'sha1'"); + method = EVP_get_digestbynid(varg->value._integer); + break; + case GB_T_STRING: + method = EVP_get_digestbyname(varg->value._string); + break; + default: + GB.Error("Method argument to HMac() can only be Integer or String"); + return NULL; + } + + if (!method) + GB.Error("Unknown method"); + return method; +} + +/**G + * Make an HMAC authentication code out of a key and some data. + * + * *Method* is any digest name such as "sha1" or "ripemd160" that is + * supported by the Digest class. The default is "sha1". + **/ +BEGIN_METHOD(HMac_call, GB_STRING key; GB_STRING data; GB_VARIANT method) + + char hash[EVP_MAX_MD_SIZE]; + unsigned int len; + const EVP_MD *method; + + method = MISSING(method) ? + EVP_get_digestbynid(NID_sha1) : + get_method(&VARG(method)); + if (!method) + return; + + memset(hash, 0, sizeof(hash)); + HMAC(method, STRING(key), LENGTH(key), (unsigned char *) STRING(data), + LENGTH(data), (unsigned char *) hash, &len); + GB.ReturnNewString(hash, len); + +END_METHOD + +GB_DESC CHMac[] = { + GB_DECLARE("HMac", 0), + GB_NOT_CREATABLE(), + + /**G HMac Sha1 + * Use the SHA1 algorithm. + * + * Since Gambas 3.14: This constant it deprecated. Use the string + * "sha1" as an argument to HMac() instead. + **/ + GB_CONSTANT("Sha1", "i", NID_sha1), + /**G HMac RipeMD160 + * Use the RIPEMD160 algorithm. + * + * Since Gambas 3.14: This constant is deprecated. Use the string + * "ripemd160" as an argument to HMac() instead. + **/ + GB_CONSTANT("RipeMD160", "i", NID_ripemd160), + + GB_STATIC_METHOD("_call", "s", HMac_call, "(Key)s(Data)s[(Method)v]"), + + GB_END_DECLARE +}; diff --git a/gb.openssl/src/c_hmac.h b/gb.openssl/src/c_hmac.h new file mode 100644 index 00000000..be46c7ea --- /dev/null +++ b/gb.openssl/src/c_hmac.h @@ -0,0 +1,43 @@ +/* + * c_hmac.h + * + * Copyright (C) 2013-2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#ifndef __C_HMAC_H +#define __C_HMAC_H + +#ifndef __C_HMAC_C +extern GB_DESC CHMac[]; +#endif + +#endif /* __C_HMAC_H */ + diff --git a/gb.openssl/src/c_openssl.c b/gb.openssl/src/c_openssl.c new file mode 100644 index 00000000..19c261d8 --- /dev/null +++ b/gb.openssl/src/c_openssl.c @@ -0,0 +1,201 @@ +/* + * c_openssl.c - Static OpenSSL class + * + * Copyright (C) 2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#define __C_OPENSSL_C + +#include +#include +#include +#include + +#include "main.h" +#include "c_openssl.h" + +/* + * OpenSSL + */ + +/**G + * Return a cryptographically strongly pseudo-random string of the + * given *Length*. + **/ +BEGIN_METHOD(OpenSSL_RandomBytes, GB_INTEGER length) + + char *ret; + int res; + + if (VARG(length) < 1) { + GB.Error("Invalid Parameter: length must be greater than 0"); + return; + } + if (VARG(length) > 0x7FFFEFF7) { + GB.Error("Invalid Parameter: length must be less than 2,147,479,544"); + return; + } + ret = GB.TempString(NULL, VARG(length)); + #if OPENSSL_VERSION_NUMBER < 0x10100000L + res = RAND_pseudo_bytes((unsigned char *) ret, VARG(length)); + #else + res = RAND_bytes((unsigned char *) ret, VARG(length)); + #endif + if (res == -1) { + GB.Error("Random number generator not supported"); + return; + } else if (res == 0) { + GB.Error(ERR_error_string(ERR_get_error(), NULL)); + return; + } + + GB.ReturnString(ret); + +END_METHOD + + +BEGIN_METHOD(OpenSSL_Pbkdf2, GB_STRING password; GB_STRING salt; GB_LONG iterations; GB_INTEGER keylength; GB_STRING method) + +#if OPENSSL_VERSION_NUMBER < 0x10002000L + GB.Error("Pbkdf2 not supported"); + return; +#else + int ret, lKey; + char *hash; + const EVP_MD *emethod; + + if (VARG(iterations) < 1) { + GB.Error("Invalid Parameter: iterations must be greater than 0"); + return; + } + lKey = VARG(keylength); + if (lKey < 1) { + GB.Error("Invalid Parameter: keylength must be greater than 0"); + return; + } + if (lKey > 0x7FFFEFF7) { + GB.Error("Invalid Parameter: keylength must be less than 2,147,479,544"); + return; + } + hash = GB.TempString(NULL, lKey); + emethod = EVP_get_digestbyname(STRING(method)); + if (!emethod) { + GB.Error("Invalid Parameter: method not a supported digest"); + return; + } + memset(hash, 0, lKey); + ret = PKCS5_PBKDF2_HMAC((const char *) STRING(password), LENGTH(password), (const unsigned char *) STRING(salt), + LENGTH(salt), (int) VARG(iterations), emethod, lKey, (unsigned char *) hash); + if (ret == 0) { + GB.Error("OpenSSL Error: Pbkdf2 call failed"); + return; + } + GB.ReturnString(hash); +#endif + +END_METHOD + +BEGIN_METHOD(OpenSSL_Scrypt, GB_STRING password; GB_STRING salt; GB_LONG N; GB_LONG r; GB_LONG p; GB_LONG keylength) + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + GB.Error("Scrypt not supported"); + return; +#else + int ret; + uint64_t lN, lR, lP; + size_t lKey; + char *hash; + + lKey = VARG(keylength); + if (lKey < 1) { + GB.Error("Invalid Parameter: keylength must be greater than 0"); + return; + } + if (lKey > 0x7FFFEFF7L) { + GB.Error("Invalid Parameter: keylength must be less than 2,147,479,544"); + return; + } + hash = GB.TempString(NULL, lKey); + lN = VARG(N); + if (lN < 2) { + GB.Error("Invalid Parameter: N must be greater than 1"); + return; + } + /* Bitwise power of 2 test */ + if (lN == 0 || (lN & (lN - 1)) != 0) { + GB.Error("Invalid Parameter: N must be a power of 2"); + return; + } + lR = VARG(r); + if (lR < 1) { + GB.Error("Invalid Parameter: r must be greater than 0"); + return; + } + if (lR > 0xFFFFFFFFL) { + GB.Error("Invalid Parameter: r must be a 32-bit number"); + return; + } + lP = VARG(p); + if (lP < 1) { + GB.Error("Invalid Parameter: p must be greater than 0"); + return; + } + if (lP > 0xFFFFFFFFL) { + GB.Error("Invalid Parameter: p must be a 32-bit number"); + return; + } + if (EVP_PBE_scrypt(NULL, 0, NULL, 0, lN, lR, lP, 0, NULL, 0) == 0) { + GB.Error("Invalid Parameter: The combination of N, r, and p was rejected by OpenSSL"); + return; + } + memset(hash, 0, lKey); + ret = EVP_PBE_scrypt((const char *) STRING(password), LENGTH(password), (const unsigned char *) STRING(salt), + LENGTH(salt), lN, lR, lP, 0, (unsigned char *) hash, lKey); + if (ret == 0) { + GB.Error("OpenSSL Error: Scrypt call failed"); + return; + } + GB.ReturnString(hash); +#endif + +END_METHOD + +GB_DESC COpenSSL[] = { + GB_DECLARE_STATIC("OpenSSL"), + + GB_STATIC_METHOD("RandomBytes", "s", OpenSSL_RandomBytes, "(Length)i"), + + GB_STATIC_METHOD("Pbkdf2", "s", OpenSSL_Pbkdf2, "(Password)s(Salt)s(Iterations)l(KeyLength)i(Method)s"), + + GB_STATIC_METHOD("Scrypt", "s", OpenSSL_Scrypt, "(Password)s(Salt)s(N)l(R)l(P)l(KeyLength)l"), + + GB_END_DECLARE +}; diff --git a/gb.openssl/src/c_openssl.h b/gb.openssl/src/c_openssl.h new file mode 100644 index 00000000..30fa38c7 --- /dev/null +++ b/gb.openssl/src/c_openssl.h @@ -0,0 +1,43 @@ +/* + * c_openssl.h + * + * Copyright (C) 2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#ifndef __C_OPENSSL_H +#define __C_OPENSSL_H + +#ifndef __C_OPENSSL_C +extern GB_DESC COpenSSL[]; +#endif + +#endif /* __C_OPENSSL_H */ + diff --git a/gb.openssl/src/gb.openssl.component b/gb.openssl/src/gb.openssl.component new file mode 100644 index 00000000..33a5dc85 --- /dev/null +++ b/gb.openssl/src/gb.openssl.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Stable diff --git a/gb.openssl/src/main.c b/gb.openssl/src/main.c new file mode 100644 index 00000000..475f4519 --- /dev/null +++ b/gb.openssl/src/main.c @@ -0,0 +1,114 @@ +/* + * main.c - gb.openssl main object + * + * Copyright (C) 2013-2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#define __MAIN_C + +#include + +#include "main.h" +#include "c_openssl.h" +#include "c_digest.h" +#include "c_cipher.h" +#include "c_hmac.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + COpenSSL, + + CDigest, + CDigestMethod, + + CCipher, + CCipherMethod, + CCipherText, + + CHMac, + +// CSignature, +// CSignatureMethod, + + NULL +}; + +void sort_and_dedupe(GB_ARRAY list) +{ + GB_FUNCTION sortfn, removefn; + int i; + char **data; + int count; + char *a, *b; + + if (GB.GetFunction(&sortfn, list, "Sort", NULL, NULL)) { + GB.Error("Can't sort array"); + return; + } + GB.Push(1, GB_T_INTEGER, GB_COMP_ASCENT | GB_COMP_NOCASE); + GB.Call(&sortfn, 1, 0); + + if (GB.GetFunction(&removefn, list, "Remove", NULL, NULL)) { + GB.Error("Can't remove duplicates"); + return; + } + + data = (char **)GB.Array.Get(list, 0); + count = GB.Array.Count(list) - 1; + + for (i = 0; i < count;) + { + a = data[i]; + b = data[i + 1]; + + if ((a && b && !strcasecmp(a, b)) || (!a && !b)) + { + GB.Push(1, GB_T_INTEGER, i); + GB.Call(&removefn, 1, 0); + count--; + } + else + { + i++; + } + } +} + +int EXPORT GB_INIT() +{ + return 0; +} + +void EXPORT GB_EXIT() +{ + EVP_cleanup(); +} diff --git a/gb.openssl/src/main.h b/gb.openssl/src/main.h new file mode 100644 index 00000000..7b22a754 --- /dev/null +++ b/gb.openssl/src/main.h @@ -0,0 +1,47 @@ +/* + * main.h + * + * Copyright (C) 2013-2019 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. + * If you do not wish to do so, delete this exception statement + * from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +void sort_and_dedupe(GB_ARRAY list); + +#endif /* __MAIN_H */ diff --git a/gb.pcre/AUTHORS b/gb.pcre/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/COPYING b/gb.pcre/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.pcre/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.pcre/ChangeLog b/gb.pcre/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/INSTALL b/gb.pcre/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.pcre/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.pcre/Makefile.am b/gb.pcre/Makefile.am new file mode 100644 index 00000000..38fcf96a --- /dev/null +++ b/gb.pcre/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @PCRE_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.pcre/NEWS b/gb.pcre/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/README b/gb.pcre/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/acinclude.m4 b/gb.pcre/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.pcre/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.pcre/component.am b/gb.pcre/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.pcre/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.pcre/configure.ac b/gb.pcre/configure.ac new file mode 100644 index 00000000..ec5ca386 --- /dev/null +++ b/gb.pcre/configure.ac @@ -0,0 +1,38 @@ +dnl ---- configure.ac for gb.pcre + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-pcre],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.pcre) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + pcre, PCRE, gb.pcre, [src], + libpcre2-8) + +if test "$have_pcre" != "yes"; then + + GB_COMPONENT_PKG_CONFIG_AGAIN( + pcre, PCRE, gb.pcre, [src], + libpcre, [trying with pcre]) + gb_use_pcre2=0 + AC_MSG_NOTICE([Using libpcre]) + +else + + AC_DEFINE_UNQUOTED(PCRE2, 1, libpcre2 is used) + gb_use_pcre2=1 + AC_MSG_NOTICE([Using libpcre2]) + +fi + +AM_CONDITIONAL(USE_PCRE2, test "$gb_use_pcre2" = 1) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.pcre/gambas.h b/gb.pcre/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.pcre/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.pcre/gb_common.h b/gb.pcre/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.pcre/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.pcre/m4 b/gb.pcre/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.pcre/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.pcre/reconf b/gb.pcre/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.pcre/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.pcre/src/Makefile.am b/gb.pcre/src/Makefile.am new file mode 100644 index 00000000..b753e5c3 --- /dev/null +++ b/gb.pcre/src/Makefile.am @@ -0,0 +1,24 @@ +COMPONENT = gb.pcre +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.pcre.la + +gb_pcre_la_LIBADD = @PCRE_LIB@ +gb_pcre_la_LDFLAGS = -module @LD_FLAGS@ @PCRE_LDFLAGS@ +gb_pcre_la_CPPFLAGS = @PCRE_INC@ + +gb_pcre_la_SOURCES = \ + main.h main.c \ + gb.pcre.h + +if USE_PCRE2 + +gb_pcre_la_SOURCES += regexp2.h regexp2.c + +else + +gb_pcre_la_SOURCES += regexp.h regexp.c + +endif + + diff --git a/gb.pcre/src/README b/gb.pcre/src/README new file mode 100644 index 00000000..58c9bac9 --- /dev/null +++ b/gb.pcre/src/README @@ -0,0 +1,62 @@ +gb.pcre + +Gambas PCRE Component + +version 0.04 - 4 October 2004 + +Copyright 2004 Rob Kudla and Benoit Minisini + +This component is licensed under the same terms as Gambas itself, +namely those of the GNU General Public License. + +This component implements perl-style regular expressions using the +PCRE (Perl-Compatible Regular Expressions) library. It contains one +class, Regex, as described below. Currently it's only useful for +matching, and replacing will have to be done in Gambas code... I hope +to provide example Match() and MatchReplace() functions soon. + +Synopsis: + +dim re as Regex +re = new Regex("stringtosearch","regularexpression") + +Regex.Offset: The offset within the string where a match occurred. +Regex.Match: The text that matched the pattern. + +If you put parentheses around part of your pattern, and the whole +pattern matches, the parts in parens become submatches. + +Regex.SubMatchCount: The number of submatches found. +Regex.SubMatch(i as integer): The text of submatch i. +Regex.SubMatchOffset(i as integer): The offset of submatch i. + +Example: + +dim i as integer +dim re as Regex +re = new Regex("the quick brown fox","quick(.+)fox") +if re.Offset >= 0 then + print re.Match + for i = 1 to re.SubMatchCount + print re.SubMatch(i) + next +endif + +This should generate the following output: + +quick brown fox + brown + +To compile this component, you must have a Gambas 0.99 source tree +available and place this tarball's contents (the whole pcre directory) +under src/lib in the gambas source tree. Then you must modify a +number of files in the gambas source tree and rebuild according to the +instructions in: + +http://www.binara.com/gambas-wiki/bin/view/Gambas/HowToProgramComponentsQuick + +I'll try to make a Mandrake package of this and announce it on the +gambas-user@sourceforge.net list. + +Rob Kudla +pcre-component@kudla.org diff --git a/gb.pcre/src/gb.pcre.component b/gb.pcre/src/gb.pcre.component new file mode 100644 index 00000000..c0eaacf8 --- /dev/null +++ b/gb.pcre/src/gb.pcre.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.pcre +Name=Perl-compatible Regular Expression Matching +Name[fr]=Expressions rationnelles compatibles avec Perl +Name[tr]=Perl-uyumlu Düzenli İfade Eşleme +Author=Benoît Minisini,Rob Kudla +State=Stable diff --git a/gb.pcre/src/gb.pcre.h b/gb.pcre/src/gb.pcre.h new file mode 100644 index 00000000..c20c7d30 --- /dev/null +++ b/gb.pcre/src/gb.pcre.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gb.pcre.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_PCRE_H +#define __GB_PCRE_H + +#include "gambas.h" + +#define PCRE_INTERFACE_VERSION 1 + +typedef + struct { + int version; + bool (*Match)(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions); + } + PCRE_INTERFACE; + +#endif + + diff --git a/gb.pcre/src/main.c b/gb.pcre/src/main.c new file mode 100644 index 00000000..b87a8d17 --- /dev/null +++ b/gb.pcre/src/main.c @@ -0,0 +1,68 @@ +/*************************************************************************** + + main.c + + (c) 2004 Rob Kudla + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "config.h" + +#if PCRE2 +#include "regexp2.h" +#else +#include "regexp.h" +#endif + +#include "main.h" + +GB_INTERFACE *GB_PTR EXPORT; + +void *GB_PCRE_1[] EXPORT = { + + (void *)PCRE_INTERFACE_VERSION, + (void *)REGEXP_match, + NULL + }; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CRegexpDesc, + CRegexpSubmatchesDesc, + CRegexpSubmatchDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.pcre/src/main.h b/gb.pcre/src/main.h new file mode 100644 index 00000000..2c92092c --- /dev/null +++ b/gb.pcre/src/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2004 Rob Kudla + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.pcre.h" + +#ifndef __MAIN_C +extern GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif /* __MAIN_H */ diff --git a/gb.pcre/src/regexp.c b/gb.pcre/src/regexp.c new file mode 100644 index 00000000..45d5e01e --- /dev/null +++ b/gb.pcre/src/regexp.c @@ -0,0 +1,532 @@ +/*************************************************************************** + + regexp.c + + (c) Rob Kudla + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __REGEXP_C + +#include "gb_common.h" + +#include "regexp.h" +#include "main.h" + +//#define DEBUG_REPLACE 1 + +#define OVECSIZE_INC 99 + +#define PCRE_GREEDY 0x80000000 // The highest possible pcre constant. It must be not used by pcre of course! + +DECLARE_METHOD(RegExp_free); + +//--------------------------------------------------------------------------- + +static void compile(void *_object) +{ + int errptr; + const char *errstr; + + if (!THIS->pattern) { + GB.Error("No pattern provided"); + return; + } + + if (THIS->code) + free(THIS->code); + + THIS->code = pcre_compile(THIS->pattern, THIS->copts, &errstr, &errptr, NULL); + + if (!THIS->code) + { + THIS->error = errptr; + GB.Error(errstr); + } +} + +static void exec(void *_object, int lsubject) +{ + int ret; + char code[16]; + + if (!THIS->code) + { + GB.Error("No pattern compiled yet"); + return; + } + + if (lsubject < 0) + lsubject = GB.StringLength(THIS->subject); + + if (!THIS->subject) + { + GB.Error("No subject provided"); + return; + } + + for(;;) + { + ret = pcre_exec(THIS->code, + NULL, + THIS->subject, + lsubject, + 0, + THIS->eopts, + THIS->ovector, + THIS->ovecsize); + + if (ret > 0) + { + THIS->error = 0; + THIS->count = ret; + break; + } + else if (ret < 0) + { + THIS->error = ret; + + switch (ret) + { + case PCRE_ERROR_NOMATCH: + THIS->count = 0; return; + case PCRE_ERROR_NULL: + GB.Error("Pattern or subject is null"); return; + case PCRE_ERROR_BADOPTION: + GB.Error("Unknown option"); return; + case PCRE_ERROR_BADMAGIC: + case PCRE_ERROR_UNKNOWN_OPCODE: + GB.Error("Incorrect PCRE bytecode"); return; + case PCRE_ERROR_NOMEMORY: + GB.Error("Out of memory"); return; + case PCRE_ERROR_BADUTF8: + #ifdef PCRE_ERROR_SHORTUTF8 + case PCRE_ERROR_SHORTUTF8: + #endif + GB.Error("Bad UTF-8 string"); return; + #ifdef PCRE_ERROR_BADUTF8_OFFSET + case PCRE_ERROR_BADUTF8_OFFSET: + GB.Error("Bad UTF-8 offset"); return; + #endif + case PCRE_ERROR_INTERNAL: + GB.Error("Unexpected internal error"); return; + case PCRE_ERROR_BADNEWLINE: + GB.Error("Invalid combination of newline options"); return; + //case PCRE_ERROR_RECURSELOOP: + // GB.Error("Recursion loop detected"); return; + //case PCRE_ERROR_JIT_STACKLIMIT: + // GB.Error("JIT stack limit reached"); return; + default: + sprintf(code, "%d", -ret); + GB.Error("Unable to run regular expression: error #&1", code); + return; + } + } + + THIS->ovecsize += OVECSIZE_INC; + GB.Realloc(POINTER(&THIS->ovector), THIS->ovecsize * sizeof(int)); + } +} + +static void return_match(void *_object, int index) +{ + int len; + + if (index < 0 || index >= THIS->count) + { + GB.Error("Out of bounds"); + return; + } + + index *= 2; + len = THIS->ovector[index + 1] - THIS->ovector[index]; + if (len <= 0) + GB.ReturnVoidString(); + else + GB.ReturnNewString(&THIS->subject[THIS->ovector[index]], len); +} + +bool REGEXP_match(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions) +{ + /* + * The gb.pcre internal routines don't require the GB_BASE to be + * initialised by Gambas! + */ + + CREGEXP tmp; + bool ret = FALSE; + + if (lsubject <= 0) + return (lpattern <= 0); + + CLEAR(&tmp); + tmp.ovecsize = OVECSIZE_INC; + GB.Alloc(POINTER(&tmp.ovector), sizeof(int) * tmp.ovecsize); + tmp.copts = coptions; + tmp.pattern = GB.NewString(pattern, lpattern); + + compile(&tmp); + + if (tmp.code) + { + tmp.eopts = eoptions; + tmp.subject = GB.NewString(subject, lsubject); + + exec(&tmp, -1); + ret = (tmp.ovector[0] != -1); + } + + RegExp_free(&tmp, NULL); + + return ret; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(RegExp_Compile, GB_STRING pattern; GB_INTEGER coptions) + + THIS->copts = VARGOPT(coptions, 0); + + GB.FreeString(&THIS->pattern); + THIS->pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + compile(THIS); + +END_METHOD + + +BEGIN_METHOD(RegExp_Exec, GB_STRING subject; GB_INTEGER eoptions) + + THIS->eopts = VARGOPT(eoptions, 0); + + GB.FreeString(&THIS->subject); + THIS->subject = GB.NewString(STRING(subject), LENGTH(subject)); + exec(THIS, -1); + +END_METHOD + + +BEGIN_METHOD(RegExp_new, GB_STRING subject; GB_STRING pattern; GB_INTEGER coptions; GB_INTEGER eoptions) + + THIS->ovecsize = OVECSIZE_INC; + GB.Alloc(POINTER(&THIS->ovector), sizeof(int) * THIS->ovecsize); + + if (MISSING(pattern)) // the user didn't provide a pattern. + return; + + THIS->copts = VARGOPT(coptions, 0); + THIS->pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + THIS->code = NULL; + + compile(THIS); + if (!THIS->code) // we didn't get a compiled pattern back. + return; + + if (MISSING(subject)) // the user didn't specify any subject text. + return; + + THIS->eopts = VARGOPT(eoptions, 0); + THIS->subject = GB.NewString(STRING(subject), LENGTH(subject)); + + exec(THIS, -1); + +END_METHOD + + +BEGIN_METHOD_VOID(RegExp_free) + + if (THIS->code) + free(THIS->code); + GB.FreeString(&THIS->subject); + GB.FreeString(&THIS->pattern); + GB.Free(POINTER(&THIS->ovector)); + +END_METHOD + + +BEGIN_METHOD(RegExp_Match, GB_STRING subject; GB_STRING pattern; GB_INTEGER coptions; GB_INTEGER eoptions) + + GB.ReturnBoolean(REGEXP_match(STRING(subject), LENGTH(subject), STRING(pattern), LENGTH(pattern), VARGOPT(coptions, 0), VARGOPT(eoptions, 0))); + +END_METHOD + + +BEGIN_PROPERTY(RegExp_Pattern) + + GB.ReturnString(THIS->pattern); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Subject) + + GB.ReturnString(THIS->subject); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Offset) + + GB.ReturnInteger(THIS->ovector[0]); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Text) + + if (THIS->count == 0) + GB.ReturnVoidString(); + else + return_match(THIS, 0); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Error) + + GB.ReturnInteger(THIS->error); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatches) + + GB.Deprecated("gb.pcre", "Regexp.Submatches", NULL); + GB.ReturnSelf(THIS); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatches_Count) + + GB.ReturnInteger(THIS->count - 1); + +END_PROPERTY + + +BEGIN_METHOD(RegExp_Submatches_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= THIS->count) + { + GB.Error("Out of bounds"); + return; + } + + THIS->_submatch = index; + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(RegExp_Submatch_Text) + + return_match(THIS, THIS->_submatch); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatch_Offset) + + GB.ReturnInteger(THIS->ovector[2 * THIS->_submatch]); + +END_PROPERTY + + +static CREGEXP *_subst_regexp = NULL; + +static void subst_get_submatch(int index, const char **p, int *lp) +{ + if (index <= 0 || index >= _subst_regexp->count) + { + *p = NULL; + *lp = 0; + } + else + { + index *= 2; + *p = &_subst_regexp->subject[_subst_regexp->ovector[index]]; + *lp = _subst_regexp->ovector[index + 1] - _subst_regexp->ovector[index]; + } +} + +BEGIN_METHOD(RegExp_Replace, GB_STRING subject; GB_STRING pattern; GB_STRING replace; GB_INTEGER coptions; GB_INTEGER eoptions) + + CREGEXP r; + char *replace; + char *result = NULL; + char *subject; + int offset; + + CLEAR(&r); + r.ovecsize = OVECSIZE_INC; + GB.Alloc(POINTER(&r.ovector), sizeof(int) * r.ovecsize); + r.copts = VARGOPT(coptions, 0); + if (r.copts & PCRE_GREEDY) + r.copts &= ~PCRE_GREEDY; + else + r.copts |= PCRE_UNGREEDY; + r.pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + + compile(&r); + + if (r.code) + { + r.eopts = VARGOPT(eoptions, 0); + subject = GB.NewString(STRING(subject), LENGTH(subject)); + + offset = 0; + + while (offset < LENGTH(subject)) + { + r.subject = &subject[offset]; + #if DEBUG_REPLACE + fprintf(stderr, "\nsubject: (%d) %s\n", offset, r.subject); + #endif + exec(&r, GB.StringLength(subject) - offset); + + if (r.ovector[0] < 0) + break; + + _subst_regexp = &r; + + if (r.ovector[0] > 0) + { + #if DEBUG_REPLACE + fprintf(stderr, "add: (%d) %.*s\n", r.ovector[0], r.ovector[0], r.subject); + #endif + result = GB.AddString(result, r.subject, r.ovector[0]); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + } + + replace = GB.SubstString(STRING(replace), LENGTH(replace), (GB_SUBST_CALLBACK)subst_get_submatch); + #if DEBUG_REPLACE + fprintf(stderr, "replace = %s\n", replace); + #endif + result = GB.AddString(result, replace, GB.StringLength(replace)); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + + offset += r.ovector[1]; + + if (*r.pattern == '^') + break; + } + + if (offset < LENGTH(subject)) + result = GB.AddString(result, &subject[offset], LENGTH(subject) - offset); + + _subst_regexp = NULL; + + GB.FreeStringLater(result); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + r.subject = subject; + } + + RegExp_free(&r, NULL); + + GB.ReturnString(result); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC CRegexpDesc[] = +{ + GB_DECLARE("RegExp", sizeof(CREGEXP)), + + GB_METHOD("_new", NULL, RegExp_new, "[(Subject)s(Pattern)s(CompileOptions)i(ExecOptions)i]"), + GB_METHOD("_free", NULL, RegExp_free, NULL), + + GB_METHOD("Compile", NULL, RegExp_Compile, "(Pattern)s[(CompileOptions)i]"), + GB_METHOD("Exec", NULL, RegExp_Exec, "(Subject)s[(ExecOptions)i]"), + + GB_STATIC_METHOD("Match", "b", RegExp_Match, "(Subject)s(Pattern)s[(CompileOptions)i(ExecOptions)i]"), + GB_STATIC_METHOD("Replace", "s", RegExp_Replace, "(Subject)s(Pattern)s(Replace)s[(CompileOptions)i(ExecOptions)i]"), + + GB_CONSTANT("Caseless", "i", PCRE_CASELESS), + GB_CONSTANT("MultiLine", "i", PCRE_MULTILINE), + GB_CONSTANT("DotAll", "i", PCRE_DOTALL), + GB_CONSTANT("Extended", "i", PCRE_EXTENDED), + GB_CONSTANT("Anchored", "i", PCRE_ANCHORED), + GB_CONSTANT("DollarEndOnly", "i", PCRE_DOLLAR_ENDONLY), + GB_CONSTANT("Extra", "i", PCRE_EXTRA), + GB_CONSTANT("NotBOL", "i", PCRE_NOTBOL), + GB_CONSTANT("NotEOL", "i", PCRE_NOTEOL), + GB_CONSTANT("Ungreedy", "i", PCRE_UNGREEDY), + GB_CONSTANT("NotEmpty", "i", PCRE_NOTEMPTY), + GB_CONSTANT("UTF8", "i", PCRE_UTF8), + GB_CONSTANT("NoAutoCapture", "i", PCRE_NO_AUTO_CAPTURE), + GB_CONSTANT("NoUTF8Check", "i", PCRE_NO_UTF8_CHECK), + GB_CONSTANT("NoMatch", "i", PCRE_ERROR_NOMATCH), + GB_CONSTANT("Null", "i", PCRE_ERROR_NULL), + GB_CONSTANT("BadOption", "i", PCRE_ERROR_BADOPTION), + GB_CONSTANT("BadMagic", "i", PCRE_ERROR_BADMAGIC), + GB_CONSTANT("UnknownNode", "i", PCRE_ERROR_UNKNOWN_NODE), + GB_CONSTANT("NoMemory", "i", PCRE_ERROR_NOMEMORY), + GB_CONSTANT("NoSubstring", "i", PCRE_ERROR_NOSUBSTRING), + GB_CONSTANT("MatchLimit", "i", PCRE_ERROR_MATCHLIMIT), + GB_CONSTANT("Callout", "i", PCRE_ERROR_CALLOUT), + GB_CONSTANT("BadUTF8", "i", PCRE_ERROR_BADUTF8), +#if (((PCRE_MAJOR == 4) && (PCRE_MINOR < 5)) || (PCRE_MAJOR < 4)) + GB_CONSTANT("BadUTF8Offset", "i", 65535), /* PCRE_ERROR_BADUTF8_OFFSET not defined < 4.5 */ +#else + GB_CONSTANT("BadUTF8Offset", "i", PCRE_ERROR_BADUTF8_OFFSET), +#endif + GB_CONSTANT("Greedy", "i", PCRE_GREEDY), + + GB_PROPERTY_READ("SubMatches", ".Regexp.Submatches", RegExp_Submatches), + + GB_PROPERTY_READ("Text", "s", RegExp_Text), /* this is the string matched by the entire pattern */ + GB_PROPERTY_READ("Offset", "i", RegExp_Offset), /* this is the string matched by the entire pattern */ + GB_PROPERTY_READ("Pattern", "s", RegExp_Pattern), + GB_PROPERTY_READ("Subject", "s", RegExp_Subject), + GB_PROPERTY_READ("Error", "i", RegExp_Error), + + GB_METHOD("_get", ".Regexp.Submatch", RegExp_Submatches_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", RegExp_Submatches_Count), + + GB_END_DECLARE +}; + +GB_DESC CRegexpSubmatchesDesc[] = +{ + GB_DECLARE(".Regexp.Submatches", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Regexp.Submatch", RegExp_Submatches_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", RegExp_Submatches_Count), + + GB_END_DECLARE +}; + +GB_DESC CRegexpSubmatchDesc[] = +{ + GB_DECLARE(".Regexp.Submatch", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Offset", "i", RegExp_Submatch_Offset), + GB_PROPERTY_READ("Text", "s", RegExp_Submatch_Text), + + GB_END_DECLARE +}; + diff --git a/gb.pcre/src/regexp.h b/gb.pcre/src/regexp.h new file mode 100644 index 00000000..3c5ecbdd --- /dev/null +++ b/gb.pcre/src/regexp.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + regexp.h + + (c) Rob Kudla + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __REGEXP_H +#define __REGEXP_H + +#include "gambas.h" + +#include "pcre.h" + +#ifndef __REGEXP_C + +extern GB_DESC CRegexpDesc[]; +extern GB_DESC CRegexpSubmatchesDesc[]; +extern GB_DESC CRegexpSubmatchDesc[]; + +#else + +typedef + struct + { + GB_BASE ob; + char *subject; + char *pattern; + int *ovector; + int ovecsize; + int count; + int eopts; + int copts; + pcre *code; + int _submatch; + int error; + } + CREGEXP; + +#define THIS OBJECT(CREGEXP) + +#endif + +bool REGEXP_match(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions); + +#endif diff --git a/gb.pcre/src/regexp2.c b/gb.pcre/src/regexp2.c new file mode 100644 index 00000000..7e53b742 --- /dev/null +++ b/gb.pcre/src/regexp2.c @@ -0,0 +1,497 @@ +/*************************************************************************** + + regexp2.c + + (c) Rob Kudla + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __REGEXP_C + +#include "gb_common.h" + +#include "regexp2.h" +#include "main.h" + +//#define DEBUG_REPLACE 1 + +#define PCRE2_GREEDY 0x80000000 // The highest possible pcre constant. It must be not used by pcre of course! +#define PCRE2_EXTRA 0x80000001 + +DECLARE_METHOD(RegExp_free); + +//--------------------------------------------------------------------------- + +static void return_error(void *_object, int err_code) +{ + PCRE2_UCHAR8 err_msg[128]; + THIS->error = err_code; + pcre2_get_error_message(err_code, err_msg, sizeof(err_msg)); + GB.Error((char *)err_msg); +} + +static void compile(void *_object) +{ + int err_code; + PCRE2_SIZE err_offset; + + if (!THIS->pattern) { + GB.Error("No pattern provided"); + return; + } + + if (THIS->code) + free(THIS->code); + + THIS->code = pcre2_compile((PCRE2_SPTR)THIS->pattern, GB.StringLength(THIS->pattern), THIS->copts, &err_code, &err_offset, NULL); + + if (!THIS->code) + return_error(THIS, err_code); +} + +static void exec(void *_object, int lsubject) +{ + int ret; + + if (!THIS->code) + { + GB.Error("No pattern compiled yet"); + return; + } + + if (lsubject < 0) + lsubject = GB.StringLength(THIS->subject); + + if (!THIS->subject) + { + GB.Error("No subject provided"); + return; + } + + pcre2_match_data_free(THIS->match); // does nothing if THIS->match is NULL + THIS->match = pcre2_match_data_create_from_pattern(THIS->code, NULL); + THIS->ovector = NULL; + + THIS->error = 0; + + ret = pcre2_match(THIS->code, + (PCRE2_SPTR)THIS->subject, + lsubject, + 0, + THIS->eopts, + THIS->match, + NULL); + + if (ret > 0) + { + THIS->count = ret; + THIS->ovector = pcre2_get_ovector_pointer(THIS->match); + } + else if (ret < 0) + { + if (ret == PCRE2_ERROR_NOMATCH) + { + THIS->count = 0; + } + else + { + THIS->error = ret; + return_error(THIS, ret); + } + } +} + +static void return_match(void *_object, int index) +{ + int len; + + if (index < 0 || index >= THIS->count) + { + GB.Error("Out of bounds"); + return; + } + + index *= 2; + len = THIS->ovector[index + 1] - THIS->ovector[index]; + if (len <= 0) + GB.ReturnVoidString(); + else + GB.ReturnNewString(&THIS->subject[THIS->ovector[index]], len); +} + +bool REGEXP_match(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions) +{ + /* + * The gb.pcre internal routines don't require the GB_BASE to be + * initialised by Gambas! + */ + + CREGEXP tmp; + bool ret = FALSE; + + if (lsubject <= 0) + return (lpattern <= 0); + + CLEAR(&tmp); + tmp.copts = coptions; + tmp.pattern = GB.NewString(pattern, lpattern); + + compile(&tmp); + + if (tmp.code) + { + tmp.eopts = eoptions; + tmp.subject = GB.NewString(subject, lsubject); + + exec(&tmp, -1); + ret = (tmp.ovector && tmp.ovector[0] != -1); + } + + RegExp_free(&tmp, NULL); + + return ret; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(RegExp_Compile, GB_STRING pattern; GB_INTEGER coptions) + + THIS->copts = VARGOPT(coptions, 0); + + GB.FreeString(&THIS->pattern); + THIS->pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + compile(THIS); + +END_METHOD + + +BEGIN_METHOD(RegExp_Exec, GB_STRING subject; GB_INTEGER eoptions) + + THIS->eopts = VARGOPT(eoptions, 0); + + GB.FreeString(&THIS->subject); + THIS->subject = GB.NewString(STRING(subject), LENGTH(subject)); + exec(THIS, -1); + +END_METHOD + + +BEGIN_METHOD(RegExp_new, GB_STRING subject; GB_STRING pattern; GB_INTEGER coptions; GB_INTEGER eoptions) + + if (MISSING(pattern)) // the user didn't provide a pattern. + return; + + THIS->copts = VARGOPT(coptions, 0); + THIS->pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + THIS->code = NULL; + + compile(THIS); + if (!THIS->code) // we didn't get a compiled pattern back. + return; + + if (MISSING(subject)) // the user didn't specify any subject text. + return; + + THIS->eopts = VARGOPT(eoptions, 0); + THIS->subject = GB.NewString(STRING(subject), LENGTH(subject)); + + exec(THIS, -1); + +END_METHOD + + +BEGIN_METHOD_VOID(RegExp_free) + + if (THIS->code) + free(THIS->code); + GB.FreeString(&THIS->subject); + GB.FreeString(&THIS->pattern); + pcre2_match_data_free(THIS->match); // does nothing if THIS->match is NULL + +END_METHOD + + +BEGIN_METHOD(RegExp_Match, GB_STRING subject; GB_STRING pattern; GB_INTEGER coptions; GB_INTEGER eoptions) + + GB.ReturnBoolean(REGEXP_match(STRING(subject), LENGTH(subject), STRING(pattern), LENGTH(pattern), VARGOPT(coptions, 0), VARGOPT(eoptions, 0))); + +END_METHOD + + +BEGIN_PROPERTY(RegExp_Pattern) + + GB.ReturnString(THIS->pattern); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Subject) + + GB.ReturnString(THIS->subject); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Offset) + + if (THIS->ovector) + GB.ReturnInteger(THIS->ovector[0]); + else + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Text) + + if (THIS->count == 0) + GB.ReturnVoidString(); + else + return_match(THIS, 0); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Error) + + GB.ReturnInteger(THIS->error); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatches) + + GB.Deprecated("gb.pcre", "Regexp.Submatches", NULL); + GB.ReturnSelf(THIS); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatches_Count) + + GB.ReturnInteger(THIS->count - 1); + +END_PROPERTY + + +BEGIN_METHOD(RegExp_Submatches_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= THIS->count) + { + GB.Error("Out of bounds"); + return; + } + + THIS->_submatch = index; + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(RegExp_Submatch_Text) + + return_match(THIS, THIS->_submatch); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatch_Offset) + + GB.ReturnInteger(THIS->ovector ? THIS->ovector[2 * THIS->_submatch] : 0); + +END_PROPERTY + + +static CREGEXP *_subst_regexp = NULL; + +static void subst_get_submatch(int index, const char **p, int *lp) +{ + if (index <= 0 || index >= _subst_regexp->count) + { + *p = NULL; + *lp = 0; + } + else + { + index *= 2; + *p = &_subst_regexp->subject[_subst_regexp->ovector[index]]; + *lp = _subst_regexp->ovector[index + 1] - _subst_regexp->ovector[index]; + } +} + +BEGIN_METHOD(RegExp_Replace, GB_STRING subject; GB_STRING pattern; GB_STRING replace; GB_INTEGER coptions; GB_INTEGER eoptions) + + CREGEXP r; + char *replace; + char *result = NULL; + char *subject; + int offset; + + CLEAR(&r); + r.copts = VARGOPT(coptions, 0); + if (r.copts & PCRE2_GREEDY) + r.copts &= ~PCRE2_GREEDY; + else + r.copts |= PCRE2_UNGREEDY; + r.pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + + compile(&r); + + if (r.code) + { + r.eopts = VARGOPT(eoptions, 0); + subject = GB.NewString(STRING(subject), LENGTH(subject)); + + offset = 0; + + while (offset < LENGTH(subject)) + { + r.subject = &subject[offset]; + #if DEBUG_REPLACE + fprintf(stderr, "\nsubject: (%d) %s\n", offset, r.subject); + #endif + exec(&r, GB.StringLength(subject) - offset); + if (!r.ovector || r.ovector[0] < 0) + break; + + _subst_regexp = &r; + + if (r.ovector[0] > 0) + { + #if DEBUG_REPLACE + fprintf(stderr, "add: (%d) %.*s\n", r.ovector[0], r.ovector[0], r.subject); + #endif + result = GB.AddString(result, r.subject, r.ovector[0]); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + } + + replace = GB.SubstString(STRING(replace), LENGTH(replace), (GB_SUBST_CALLBACK)subst_get_submatch); + #if DEBUG_REPLACE + fprintf(stderr, "replace = %s\n", replace); + #endif + result = GB.AddString(result, replace, GB.StringLength(replace)); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + + offset += r.ovector[1]; + + if (*r.pattern == '^') + break; + } + + if (offset < LENGTH(subject)) + result = GB.AddString(result, &subject[offset], LENGTH(subject) - offset); + + _subst_regexp = NULL; + + GB.FreeStringLater(result); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + r.subject = subject; + } + + RegExp_free(&r, NULL); + + GB.ReturnString(result); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC CRegexpDesc[] = +{ + GB_DECLARE("RegExp", sizeof(CREGEXP)), + + GB_METHOD("_new", NULL, RegExp_new, "[(Subject)s(Pattern)s(CompileOptions)i(ExecOptions)i]"), + GB_METHOD("_free", NULL, RegExp_free, NULL), + + GB_METHOD("Compile", NULL, RegExp_Compile, "(Pattern)s[(CompileOptions)i]"), + GB_METHOD("Exec", NULL, RegExp_Exec, "(Subject)s[(ExecOptions)i]"), + + GB_STATIC_METHOD("Match", "b", RegExp_Match, "(Subject)s(Pattern)s[(CompileOptions)i(ExecOptions)i]"), + GB_STATIC_METHOD("Replace", "s", RegExp_Replace, "(Subject)s(Pattern)s(Replace)s[(CompileOptions)i(ExecOptions)i]"), + + GB_CONSTANT("Caseless", "i", PCRE2_CASELESS), + GB_CONSTANT("MultiLine", "i", PCRE2_MULTILINE), + GB_CONSTANT("DotAll", "i", PCRE2_DOTALL), + GB_CONSTANT("Extended", "i", PCRE2_EXTENDED), + GB_CONSTANT("Anchored", "i", PCRE2_ANCHORED), + GB_CONSTANT("DollarEndOnly", "i", PCRE2_DOLLAR_ENDONLY), + GB_CONSTANT("Extra", "i", PCRE2_EXTRA), + GB_CONSTANT("NotBOL", "i", PCRE2_NOTBOL), + GB_CONSTANT("NotEOL", "i", PCRE2_NOTEOL), + GB_CONSTANT("Ungreedy", "i", PCRE2_UNGREEDY), + GB_CONSTANT("NotEmpty", "i", PCRE2_NOTEMPTY), + GB_CONSTANT("UTF8", "i", PCRE2_UTF), + GB_CONSTANT("NoAutoCapture", "i", PCRE2_NO_AUTO_CAPTURE), + GB_CONSTANT("NoUTF8Check", "i", PCRE2_NO_UTF_CHECK), + + GB_CONSTANT("NoMatch", "i", PCRE2_ERROR_NOMATCH), + GB_CONSTANT("Null", "i", PCRE2_ERROR_NULL), + GB_CONSTANT("BadOption", "i", PCRE2_ERROR_BADOPTION), + GB_CONSTANT("BadMagic", "i", PCRE2_ERROR_BADMAGIC), + GB_CONSTANT("UnknownNode", "i", PCRE2_ERROR_INTERNAL), + GB_CONSTANT("NoMemory", "i", PCRE2_ERROR_NOMEMORY), + GB_CONSTANT("NoSubstring", "i", PCRE2_ERROR_NOSUBSTRING), + GB_CONSTANT("MatchLimit", "i", PCRE2_ERROR_MATCHLIMIT), + GB_CONSTANT("Callout", "i", PCRE2_ERROR_CALLOUT), + GB_CONSTANT("BadUTF8", "i", PCRE2_ERROR_UTF8_ERR1), + GB_CONSTANT("BadUTF8Offset", "i", PCRE2_ERROR_BADUTFOFFSET), + GB_CONSTANT("Greedy", "i", PCRE2_GREEDY), + + GB_PROPERTY_READ("SubMatches", ".Regexp.Submatches", RegExp_Submatches), + + GB_PROPERTY_READ("Text", "s", RegExp_Text), /* this is the string matched by the entire pattern */ + GB_PROPERTY_READ("Offset", "i", RegExp_Offset), /* this is the string matched by the entire pattern */ + GB_PROPERTY_READ("Pattern", "s", RegExp_Pattern), + GB_PROPERTY_READ("Subject", "s", RegExp_Subject), + GB_PROPERTY_READ("Error", "i", RegExp_Error), + + GB_METHOD("_get", ".Regexp.Submatch", RegExp_Submatches_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", RegExp_Submatches_Count), + + GB_END_DECLARE +}; + +GB_DESC CRegexpSubmatchesDesc[] = +{ + GB_DECLARE(".Regexp.Submatches", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Regexp.Submatch", RegExp_Submatches_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", RegExp_Submatches_Count), + + GB_END_DECLARE +}; + +GB_DESC CRegexpSubmatchDesc[] = +{ + GB_DECLARE(".Regexp.Submatch", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Offset", "i", RegExp_Submatch_Offset), + GB_PROPERTY_READ("Text", "s", RegExp_Submatch_Text), + + GB_END_DECLARE +}; + diff --git a/gb.pcre/src/regexp2.h b/gb.pcre/src/regexp2.h new file mode 100644 index 00000000..5d8ec69e --- /dev/null +++ b/gb.pcre/src/regexp2.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + regexp2.h + + (c) Rob Kudla + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __REGEXP_H +#define __REGEXP_H + +#include "gambas.h" + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include "pcre2.h" + +#ifndef __REGEXP_C + +extern GB_DESC CRegexpDesc[]; +extern GB_DESC CRegexpSubmatchesDesc[]; +extern GB_DESC CRegexpSubmatchDesc[]; + +#else + +typedef + struct + { + GB_BASE ob; + char *subject; + char *pattern; + pcre2_match_data *match; + PCRE2_SIZE *ovector; + int count; + int eopts; + int copts; + pcre2_code *code; + int _submatch; + int error; + } + CREGEXP; + +#define THIS OBJECT(CREGEXP) + +#endif + +bool REGEXP_match(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions); + +#endif diff --git a/gb.pdf/AUTHORS b/gb.pdf/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pdf/COPYING b/gb.pdf/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.pdf/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.pdf/ChangeLog b/gb.pdf/ChangeLog new file mode 100644 index 00000000..3ab04654 --- /dev/null +++ b/gb.pdf/ChangeLog @@ -0,0 +1,15 @@ +051030: + +* Removed glib dependencies from the library. +* "GetPicture" method removed. +* "GetImage" supports rotation. + +051006: + +* Added this Changelog. +* Added an "Info" interface to get document properties. + + +050929: + +* Initial stable release. \ No newline at end of file diff --git a/gb.pdf/INSTALL b/gb.pdf/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.pdf/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.pdf/Makefile.am b/gb.pdf/Makefile.am new file mode 100644 index 00000000..56782c85 --- /dev/null +++ b/gb.pdf/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @POPPLER_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.pdf/NEWS b/gb.pdf/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pdf/README b/gb.pdf/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.pdf/acinclude.m4 b/gb.pdf/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.pdf/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.pdf/component.am b/gb.pdf/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.pdf/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.pdf/configure.ac b/gb.pdf/configure.ac new file mode 100644 index 00000000..efe0d1c9 --- /dev/null +++ b/gb.pdf/configure.ac @@ -0,0 +1,45 @@ +dnl ---- configure.ac for gb.pdf + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-pdf],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.pdf) +LT_INIT +PKG_PROG_PKG_CONFIG + +GB_COMPONENT_PKG_CONFIG( + poppler, POPPLER, gb.pdf, [src], + 'poppler >= 0.20' 'poppler < 23.0' +) + +if test "$have_poppler" = "yes"; then + $PKG_CONFIG --atleast-version=0.58.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_58, $((1-$?)), Poppler version >= 0.58) + $PKG_CONFIG --atleast-version=0.64.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_64, $((1-$?)), Poppler version >= 0.64) + $PKG_CONFIG --atleast-version=0.71.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_71, $((1-$?)), Poppler version >= 0.71) + $PKG_CONFIG --atleast-version=0.72.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_72, $((1-$?)), Poppler version >= 0.72) + $PKG_CONFIG --atleast-version=0.76.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_76, $((1-$?)), Poppler version >= 0.76) + $PKG_CONFIG --atleast-version=0.83.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_83, $((1-$?)), Poppler version >= 0.83) + $PKG_CONFIG --atleast-version=0.85.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_85, $((1-$?)), Poppler version >= 0.85) + $PKG_CONFIG --atleast-version=0.86.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_86, $((1-$?)), Poppler version >= 0.86) + $PKG_CONFIG --atleast-version=21.06.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_21_06_0, $((1-$?)), Poppler version >= 21.06.0) + $PKG_CONFIG --atleast-version=22.06.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_22_06_0, $((1-$?)), Poppler version >= 22.06.0) +fi + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.pdf/gambas.h b/gb.pdf/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.pdf/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.pdf/gb.gtk.h b/gb.pdf/gb.gtk.h new file mode 120000 index 00000000..6719229a --- /dev/null +++ b/gb.pdf/gb.gtk.h @@ -0,0 +1 @@ +../gb.gtk/src/gb.gtk.h \ No newline at end of file diff --git a/gb.pdf/gb.image.h b/gb.pdf/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.pdf/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.pdf/gb_common.h b/gb.pdf/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.pdf/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.pdf/m4 b/gb.pdf/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.pdf/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.pdf/reconf b/gb.pdf/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.pdf/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.pdf/src/CPdfDocument.cpp b/gb.pdf/src/CPdfDocument.cpp new file mode 100644 index 00000000..8f4aac7f --- /dev/null +++ b/gb.pdf/src/CPdfDocument.cpp @@ -0,0 +1,1542 @@ +/*************************************************************************** + + CPdfDocument.cpp + + (C) 2005-2007 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPDFDOCUMENT_C + +#include "CPdfDocument.h" + +#include "gambas.h" +#include "main.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if POPPLER_VERSION_0_72 +#define getCString c_str +#endif + +/***************************************************************************/ + +static CPDFRECT *create_rect(void) +{ + return (CPDFRECT *)GB.New(GB.FindClass("PdfRect"), NULL, NULL); +} + +BEGIN_PROPERTY(PdfRect_X) + + GB.ReturnFloat(THIS_RECT->x); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Y) + + GB.ReturnFloat(THIS_RECT->y); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Width) + + GB.ReturnFloat(THIS_RECT->w); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Height) + + GB.ReturnFloat(THIS_RECT->h); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Right) + + GB.ReturnFloat(THIS_RECT->x + THIS_RECT->w); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Bottom) + + GB.ReturnFloat(THIS_RECT->y + THIS_RECT->h); + +END_PROPERTY + + + +/**************************************************************************** + + Translations from Poppler universe to Gambas universe + +****************************************************************************/ + +static void return_unicode_string(const Unicode *unicode, int len) +{ + GooString gstr; + char buf[8]; /* 8 is enough for mapping an unicode char to a string */ + int i, n; + +#if POPPLER_VERSION_0_85 + const UnicodeMap *uMap = globalParams->getUtf8Map(); +#else + static UnicodeMap *uMap = NULL; + if (uMap == NULL) + { + GooString *enc = new GooString("UTF-8"); + uMap = globalParams->getUnicodeMap(enc); + uMap->incRefCnt(); + delete enc; + } +#endif + + for (i = 0; i < len; ++i) { + n = uMap->mapUnicode(unicode[i], buf, sizeof(buf)); + gstr.append(buf, n); + } + + GB.ReturnNewZeroString(gstr.getCString()); +} + + +static void aux_return_string_info(void *_object, const char *key) +{ + Object obj; + Object dst; + const_GooString *goo_value; + Dict *info_dict; + char *tmpstr; + + #if POPPLER_VERSION_0_58 + obj = THIS->doc->getDocInfo (); + #else + THIS->doc->getDocInfo (&obj); + #endif + if (!obj.isDict()) { GB.ReturnNewZeroString(""); return; } + + info_dict=obj.getDict(); + #if POPPLER_VERSION_0_58 + dst = info_dict->lookup ((char *)key); + #else + info_dict->lookup ((char *)key, &dst); + #endif + if (!dst.isString ()) { GB.ReturnNewZeroString(""); } + else { + goo_value = dst.getString(); + + if (goo_value->hasUnicodeMarker()) + { + GB.ConvString (&tmpstr,goo_value->getCString()+2,goo_value->getLength()-2,"UTF-16BE","UTF-8"); + GB.ReturnNewZeroString(tmpstr); + } + else + GB.ReturnNewString(goo_value->getCString(),goo_value->getLength()); + } + #if ! POPPLER_VERSION_0_58 + dst.free(); + obj.free(); + #endif +} + +static void aux_return_date_info(void *_object, const char *key) +{ + // TODO: Y2K effect + GB_DATE_SERIAL ds; + GB_DATE ret; + Object obj; + Object dst; + const_GooString *goo; + Dict *info_dict; + char *datestr=NULL,*tofree=NULL; + int nnum; + + GB.ReturnDate(NULL); + + #if POPPLER_VERSION_0_58 + obj = THIS->doc->getDocInfo (); + #else + THIS->doc->getDocInfo (&obj); + #endif + if (!obj.isDict()) return; + + info_dict=obj.getDict(); + #if POPPLER_VERSION_0_58 + dst = info_dict->lookup ((char *)key); + #else + info_dict->lookup ((char *)key, &dst); + #endif + if (dst.isString ()) + { + goo = dst.getString(); + if (goo->hasUnicodeMarker()) + GB.ConvString (&datestr,goo->getCString()+2,goo->getLength()-2,"UTF-16BE","UTF-8"); + else + { + datestr = GB.NewString(goo->getCString(),goo->getLength()); + tofree=datestr; + } + + if (datestr) + { + if (datestr[0] == 'D' && datestr[1] == ':') datestr += 2; + nnum=sscanf(datestr, "%4d%2d%2d%2d%2d%2d",&ds.year, &ds.month, &ds.day, &ds.hour, &ds.min, &ds.sec); + if (nnum == 6) + { + if (!GB.MakeDate(&ds,&ret)) + GB.ReturnDate(&ret); + } + } + + } + + if (tofree) GB.FreeString(&tofree); + #if ! POPPLER_VERSION_0_58 + dst.free(); + obj.free(); + #endif +} + +static const_LinkDest *get_dest(const_LinkAction *act) +{ + if (!act) + return 0; + + switch (act->getKind()) + { + case actionGoTo: return ((LinkGoTo*)act)->getDest(); + case actionGoToR: return ((LinkGoToR*)act)->getDest(); + default: return 0; + } +} + +static uint32_t aux_get_page_from_action(void *_object, const_LinkAction *act) +{ + Ref pref; + const_LinkDest *dest = get_dest(act); + const_GooString *name; + + if (!dest) + { + // try to use NamedDest to get dest + if (!act) + return 0; + if (act->getKind () == actionGoTo) + { + name = ((LinkGoTo*)act)->getNamedDest(); + if (name) { + #if POPPLER_VERSION_0_86 + dest = THIS->doc->findDest(name).get(); + #elif POPPLER_VERSION_0_64 + dest = THIS->doc->findDest(name); + #else + dest = THIS->doc->findDest((GooString *) name); + #endif + } + } + } + + if (!dest) + return 0; + + if (dest->isPageRef() ) + { + pref= dest->getPageRef(); +#if POPPLER_VERSION_0_76 + return THIS->doc->findPage(pref); +#else + return THIS->doc->findPage(pref.num, pref.gen); +#endif + } + else + return dest->getPageNum(); +} + + +static void aux_get_dimensions_from_action(const_LinkAction *act, CPDFRECT *rect) +{ + const_LinkDest *dest = get_dest(act); + if (!dest) + return; + + rect->x = dest->getLeft(); + rect->w = dest->getRight() - rect->x; + rect->y = dest->getTop(); + rect->h = dest->getBottom() - rect->y; +} + +static double aux_get_zoom_from_action(const_LinkAction *act) +{ + const_LinkDest *dest = get_dest(act); + if (dest) + return dest->getZoom(); + else + return 1; +} + +static char* aux_get_target_from_action(const_LinkAction *act) +{ + char *vl = NULL; + char *uni = NULL; + const_GooString *tmp = NULL; +#if POPPLER_VERSION_0_86 + GooString gstr; +#endif + + switch (act->getKind()) + { + case actionGoToR: + tmp=((LinkGoToR*)act)->getFileName(); break; + + case actionLaunch: + tmp=((LinkLaunch*)act)->getFileName(); break; + + case actionURI: +#if POPPLER_VERSION_0_86 + gstr = GooString(((LinkURI*)act)->getURI()); + tmp = &gstr; +#else + tmp = ((LinkURI*)act)->getURI(); +#endif + break; + + case actionNamed: +#if POPPLER_VERSION_0_86 + gstr = GooString(((LinkNamed*)act)->getName()); + tmp = &gstr; +#else + tmp = ((LinkNamed*)act)->getName(); +#endif + break; + + case actionMovie: +#if POPPLER_VERSION_0_86 + gstr = GooString(((LinkMovie*)act)->getAnnotTitle()); + tmp = &gstr; +#else + tmp = ((LinkMovie*)act)->getAnnotTitle(); +#endif + break; + + default: + break; + } + + if (!tmp) return NULL; + + if (tmp->hasUnicodeMarker()) + { + GB.ConvString (&uni,tmp->getCString()+2,tmp->getLength()-2,"UTF-16BE","UTF-8"); + vl = GB.AddString(vl, uni, 0); + } + else + vl = GB.AddString(vl,tmp->getCString(),tmp->getLength()); + + + return vl; + +} + +/***************************************************************************** + + PDF document + +******************************************************************************/ + + +static void free_all(void *_object) +{ + if (THIS->doc) + { + delete THIS->doc; + THIS->doc=NULL; + } + + if (THIS->dev) + { + delete THIS->dev; + THIS->dev=NULL; + } + + if (THIS->buf) + { + GB.ReleaseFile(THIS->buf,THIS->len); + THIS->buf=NULL; + } + + if (THIS->Found) + { + GB.FreeArray(POINTER(&THIS->Found)); + THIS->Found=NULL; + } + + if (THIS->links) + { + delete THIS->links; + THIS->links=NULL; + } + + if (THIS->pindex) + { + GB.FreeArray(POINTER(&THIS->pindex)); + GB.FreeArray(POINTER(&THIS->oldindex)); + THIS->pindex=NULL; + THIS->oldindex=NULL; + } + + THIS->index=NULL; + THIS->currpage=-1; +} + +BEGIN_METHOD_VOID (PDFDOCUMENT_free) + + free_all(_object); + +END_METHOD + +BEGIN_PROPERTY(PDFDOCUMENT_scale) + + if (READ_PROPERTY){ GB.ReturnFloat(THIS->scale); return; } + + if (VPROP(GB_FLOAT)>0) { THIS->scale = VPROP(GB_FLOAT); return; } + + GB.Error("Zoom must be a positive value"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_rotation) + + int32_t rot; + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->rotation); + return; + } + + rot=VPROP(GB_INTEGER); + + while (rot<0) rot+=360; + while (rot>=360) rot-=360; + + switch (rot) + { + case 0: + case 90: + case 180: + case 270: + THIS->rotation = VPROP(GB_INTEGER); + break; + } + +END_PROPERTY + + +int32_t open_document (void *_object, char *sfile, int32_t lfile) +{ + SplashColor white; + PDFDoc *test; + MemStream *stream; + Object obj; + Outline *outline; + char *buf=NULL; + int32_t len=0; + int32_t ret; + + + if ( GB.LoadFile(sfile,lfile,&buf,&len) ) return -1; + + #if POPPLER_VERSION_0_58 + stream = new MemStream(buf,0,(uint)len,std::move(obj)); + #else + obj.initNull(); + stream = new MemStream(buf,0,(uint)len,&obj); + #endif + test = new PDFDoc(stream); + + if (!test->isOk()) + { + GB.ReleaseFile(buf,len); + ret=test->getErrorCode(); + delete test; + test=NULL; + if (ret == errEncrypted) return -2; + return -3; + } + + free_all(_object); + + THIS->doc=test; + THIS->buf=buf; + THIS->len=len; + + white[0] = 0xFF; white[1] = 0xFF; white[2] = 0xFF; + THIS->dev=new SplashOutputDev(splashModeRGB8, 3, false, white); + THIS->dev->startDoc(THIS->doc); + outline=THIS->doc->getOutline(); + if (outline) THIS->index=outline->getItems(); + + //if (THIS->index) + // if (!THIS->index->getLength()) THIS->index=NULL; + + THIS->currindex=0; + THIS->currpage=-1; + + return 0; + +} + + +BEGIN_METHOD(PDFDOCUMENT_new, GB_STRING File) + + THIS->scale = 1; + THIS->rotation = 0; + + if (!MISSING(File)) + { + switch (open_document( _object, STRING(File), LENGTH(File)) ) + { + case -1: GB.Error("File not found"); return; + case -2: GB.Error("PDF is encrypted"); return; + case -3: GB.Error("Bad PDF File"); return; + } + } + +END_METHOD + +BEGIN_METHOD (PDFDOCUMENT_open, GB_STRING File;) + + switch (open_document( _object, STRING(File), LENGTH(File)) ) + { + case -1: GB.Error("File not found"); return; + case -2: GB.Error("PDF is encrypted"); return; + case -3: GB.Error("Bad PDF File"); return; + } + +END_METHOD + +BEGIN_METHOD_VOID(PDFDOCUMENT_close) + + free_all(_object); + +END_METHOD + +BEGIN_METHOD(PDFDOCUMENT_get,GB_INTEGER index;) + + if (!THIS->doc || (VARG(index)<1) || ( VARG(index)>THIS->doc->getNumPages() ) ) + { + GB.Error("Invalid page number"); + return; + } + + if (THIS->currpage != (uint32_t)VARG(index) ) + { + if (THIS->Found) + { + GB.FreeArray(POINTER(&THIS->Found)); + THIS->Found=NULL; + } + + if (THIS->links) + { + delete THIS->links; + THIS->links=NULL; + } + + THIS->page=THIS->doc->getCatalog()->getPage(VARG(index)); + THIS->currpage=VARG(index); + } + + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(PDFDOCUMENT_ready) + + GB.ReturnBoolean( (bool)THIS->doc ); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_count) + + GB.ReturnInteger( (int32_t) (THIS->doc ? THIS->doc->getNumPages() : 0)); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_info) + + if (THIS->doc) RETURN_SELF(); + else GB.ReturnNull(); + +END_PROPERTY + +/***************************************************************************** + +PDF document information + +******************************************************************************/ + +BEGIN_PROPERTY(PDFINFO_title) + + aux_return_string_info(_object,"Title"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_format) + + char ctx[16]; + snprintf(ctx, sizeof(ctx), "%.2g", THIS->doc->getPDFMajorVersion () + THIS->doc->getPDFMinorVersion() / 10.0); + GB.ReturnNewZeroString(ctx); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_author) + + aux_return_string_info(_object,"Author"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_subject) + + aux_return_string_info(_object,"Subject"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_keywords) + + aux_return_string_info(_object,"Keywords"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_creator) + + aux_return_string_info(_object,"Creator"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_producer) + + aux_return_string_info(_object,"Producer"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_linearized) + + GB.ReturnBoolean(THIS->doc->isLinearized()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_layout) + + Catalog *catalog; + + catalog=THIS->doc->getCatalog(); + if (!catalog) { GB.ReturnInteger(Catalog::pageLayoutNone); return; } + if (!catalog->isOk()) { GB.ReturnInteger(Catalog::pageLayoutNone); return; } + + GB.ReturnInteger(catalog->getPageLayout()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_mode) + + Catalog *catalog; + + catalog=THIS->doc->getCatalog(); + if (!catalog) { GB.ReturnInteger(Catalog::pageModeNone); return; } + if (!catalog->isOk()) { GB.ReturnInteger(Catalog::pageModeNone); return; } + + GB.ReturnInteger(catalog->getPageMode()); + + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_canprint) + + GB.ReturnBoolean(THIS->doc->okToPrint()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_canmodify) + + GB.ReturnBoolean(THIS->doc->okToChange()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_cancopy) + + GB.ReturnBoolean(THIS->doc->okToCopy()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_canaddnotes) + + GB.ReturnBoolean(THIS->doc->okToAddNotes()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_creation) + + aux_return_date_info(_object,"CreationDate"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_modification) + + aux_return_date_info(_object,"ModDate"); + +END_PROPERTY + + +/***************************************************************************** + +PDF document index + +******************************************************************************/ + + +BEGIN_PROPERTY(PDFDOCUMENT_has_index) + + GB.ReturnBoolean(THIS->index && CPDF_index_count()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_index) + + if (!THIS->index) { GB.ReturnNull(); return; } + + THIS->action=(CPDF_index_get(THIS->currindex))->getAction(); + RETURN_SELF(); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_count) + + GB.ReturnInteger(CPDF_index_count()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_has_children) + + OutlineItem *item = CPDF_index_get(THIS->currindex); + + GB.ReturnBoolean(item->getKids() && CPDF_list_count(item->getKids())); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_is_open) + + OutlineItem *item = CPDF_index_get(THIS->currindex); + + if (READ_PROPERTY) + { + GB.ReturnBoolean(item->isOpen()); + return; + } + + if (VPROP(GB_INTEGER)) + item->open(); +#ifndef POPPLER_VERSION_21_06_0 + else + item->close(); +#endif + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_title) + + OutlineItem *item = CPDF_index_get(THIS->currindex); + return_unicode_string(item->getTitle(), item->getTitleLength()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(PDFINDEX_root) + + Outline *outline; + + outline=THIS->doc->getOutline(); + if (outline) THIS->index=outline->getItems(); + THIS->currindex=0; + if (THIS->pindex) { GB.FreeArray(POINTER(&THIS->pindex)); THIS->pindex=NULL; } + if (THIS->oldindex) { GB.FreeArray(POINTER(&THIS->oldindex)); THIS->oldindex=NULL; } + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_prev) + + if (!THIS->currindex) { GB.ReturnBoolean(true); return; } + + THIS->currindex--; + GB.ReturnBoolean(false); + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_next) + + if ((THIS->currindex + 1) >= (uint)CPDF_index_count()) + { + GB.ReturnBoolean(true); + return; + } + + THIS->currindex++; + GB.ReturnBoolean(false); + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_child) + + OutlineItem *item = CPDF_index_get(THIS->currindex); + + if (!item->hasKids() || CPDF_list_count(item->getKids()) == 0) { GB.ReturnBoolean(true); return; } + + if (THIS->pindex) + { + GB.Add(POINTER(&THIS->pindex)); + GB.Add(POINTER(&THIS->oldindex)); + } + else + { + GB.NewArray(POINTER(&THIS->pindex),sizeof(void*),1); + GB.NewArray(POINTER(&THIS->oldindex),sizeof(uint32_t),1); + } + + if (!item->isOpen()) item->open(); + THIS->pindex[GB.Count(POINTER(THIS->pindex))-1]=(void*)THIS->index; + THIS->oldindex[GB.Count(POINTER(THIS->pindex))-1]=THIS->currindex; + THIS->index=item->getKids(); + THIS->currindex=0; + + GB.ReturnBoolean(false); + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_parent) + + if (!THIS->pindex) { GB.ReturnBoolean(true); return; } + + THIS->index=(GooList*)THIS->pindex[GB.Count(POINTER(THIS->pindex))-1]; + THIS->currindex=THIS->oldindex[GB.Count(POINTER(THIS->pindex))-1]; + if (GB.Count(POINTER(THIS->pindex))==1) + { + GB.FreeArray(POINTER(&THIS->pindex)); + GB.FreeArray(POINTER(&THIS->oldindex)); + THIS->oldindex=NULL; + THIS->pindex=NULL; + } + else + { + GB.Remove(POINTER(&THIS->pindex),GB.Count(POINTER(THIS->pindex))-1,1); + GB.Remove(POINTER(&THIS->oldindex),GB.Count(POINTER(THIS->oldindex))-1,1); + } + + GB.ReturnBoolean(false); + +END_METHOD + +/***************************************************************************** + + PDF pages + +******************************************************************************/ + +static int get_rotation(void *_object) +{ + return (THIS->rotation + THIS->page->getRotate() + 720) % 360; +} + +static void get_page_size(void *_object, int *w, int *h) +{ + int rotation = get_rotation(THIS); + + if (rotation == 90 || rotation == 270) + { + if (w) *w = (int)(THIS->page->getMediaHeight() * THIS->scale); + if (h) *h = (int)(THIS->page->getMediaWidth() * THIS->scale); + } + else + { + if (w) *w = (int)(THIS->page->getMediaWidth() * THIS->scale); + if (h) *h = (int)(THIS->page->getMediaHeight() * THIS->scale); + } +} + +BEGIN_PROPERTY (PDFPAGE_width) + + int w; + get_page_size(THIS, &w, NULL); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY (PDFPAGE_height) + + int h; + get_page_size(THIS, NULL, &h); + GB.ReturnInteger(h); + +END_PROPERTY + +static uint32_t *get_page_data(CPDFDOCUMENT *_object, int32_t x, int32_t y, int32_t *width, int32_t *height, double scale, int32_t rotation) +{ + SplashBitmap *map; + uint32_t *data; + int32_t w, h; + int rw; + int rh; + + get_page_size(THIS, &rw, &rh); + + w = *width; + h = *height; + + if (w < 0) w = rw; + if (h < 0) h = rh; + + if (x<0) x=0; + if (y<0) y=0; + if (w<1) w=1; + if (h<1) h=1; + + + if ( (x+w) > rw ) w=rw-x; + if ( (y+h) > rh ) h=rh-y; + + if ( (w<0) || (h<0) ) return NULL; + + THIS->page->displaySlice(THIS->dev,72.0*scale,72.0*scale, + rotation, + false, + true, + x,y,w,h, + false); + + map=THIS->dev->getBitmap(); + + data=(uint32_t*)map->getDataPtr(); + + + *width = w; + *height = h; + + return data; +} + +BEGIN_METHOD(PDFPAGE_image, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + uint32_t *data; + int32_t x,y, w, h; + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + data = get_page_data(THIS, x, y, &w, &h, THIS->scale, THIS->rotation); + if (!data) { GB.ReturnNull(); return; } + /*GB.Image.Create(&img, data, w, h, GB_IMAGE_RGB); + GB.ReturnObject(img);*/ + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_RGB, (unsigned char *)data)); + +END_METHOD + +BEGIN_PROPERTY (PDFPAGE_property_image) + + int32_t w=-1; + int32_t h=-1; + uint32_t *data; + + data = get_page_data(THIS, 0, 0, &w, &h, THIS->scale, THIS->rotation); + if (!data) { GB.ReturnNull(); return; } + /*GB.Image.Create(&img, data, w, h, GB_IMAGE_RGB); + GB.ReturnObject(img);*/ + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_RGB, (unsigned char *)data)); + +END_PROPERTY + +BEGIN_METHOD(PDFPAGE_select, GB_INTEGER X; GB_INTEGER Y; GB_INTEGER W; GB_INTEGER H) + + TextOutputDev *dev; + GooString *str; + Gfx *gfx; + int32_t x,y,w,h; + + x = VARGOPT(X, 0); + y = VARGOPT(Y, 0); + w = VARGOPT(W, (int32_t)THIS->page->getMediaWidth()); + h = VARGOPT(H, (int32_t)THIS->page->getMediaHeight()); + + dev = new TextOutputDev (NULL, true, 0, false, false); + gfx = THIS->page->createGfx(dev,72.0,72.0,0,false,true,-1, -1, -1, -1, false, NULL, NULL); + + THIS->page->display(gfx); + dev->endPage(); + + str=dev->getText((double)x,(double)y,(double)(w+x),(double)(h+y)); + + delete gfx; + delete dev; + + if (!str) + { + GB.ReturnNewZeroString(""); + return; + } + + GB.ReturnNewString(str->getCString(),str->getLength()); + delete str; + +END_METHOD + +/***************************************************************************** + + Bookmarks of a PDF page + +******************************************************************************/ + +void aux_fill_links(void *_object) +{ + THIS->links = new Links (THIS->page->getAnnots ()); +} + +BEGIN_PROPERTY (PDFPAGELINKS_count) + + if (!THIS->links) aux_fill_links(_object); + if (!THIS->links) { GB.ReturnInteger(0); return; } + +#if POPPLER_VERSION_22_06_0 + int numlinks = 0; + for (AnnotLink *link : THIS->links->getLinks()) { + numlinks++; + } + GB.ReturnInteger(numlinks); +#else + GB.ReturnInteger(THIS->links->getNumLinks()); +#endif + +END_PROPERTY + +BEGIN_METHOD (PDFPAGELINKS_get,GB_INTEGER ind;) + +#if POPPLER_VERSION_22_06_0 + + if (!THIS->links) aux_fill_links(_object); + if (THIS->links && VARG(ind)>0) + { + int i = 0; + for (AnnotLink *link : THIS->links->getLinks()) { + if (VARG(ind)==i) { + THIS->action=link->getAction(); + RETURN_SELF(); + } + } + } + GB.Error("Out of bounds"); + return; + +#else + + bool pok = true; + + if (!THIS->links) aux_fill_links(_object); + if (!THIS->links) pok=false; + else + { + if (VARG(ind)<0) pok=false; + else + { + if (VARG(ind)>=THIS->links->getNumLinks()) pok=false; + } + } + + if (!pok) { GB.Error("Out of bounds"); return; } + + THIS->lcurrent=VARG(ind); + THIS->action=THIS->links->getLink(THIS->lcurrent)->getAction(); + + RETURN_SELF(); + +#endif + +END_METHOD + +BEGIN_PROPERTY (PDFPAGELINKDATA_parameters) + + if (THIS->action->getKind() != actionLaunch ) + { + GB.ReturnNewZeroString(""); + return; + } + + GB.ReturnNewZeroString(((LinkLaunch*)THIS->action)->getParams()->getCString()); + +END_PROPERTY + +BEGIN_PROPERTY (PDFPAGELINKDATA_uri) + + char *uri; + + uri=aux_get_target_from_action(THIS->action); + + GB.ReturnNewZeroString(uri); + if (uri) GB.FreeString(&uri); + +END_PROPERTY + +BEGIN_PROPERTY(PdfPageLinkData_Rect) + + CPDFRECT *rect = create_rect(); + aux_get_dimensions_from_action(THIS->action, rect); + GB.ReturnObject(rect); + +END_PROPERTY + +BEGIN_PROPERTY(PDFPAGELINKDATA_zoom) + + GB.ReturnFloat(aux_get_zoom_from_action(THIS->action)); + +END_PROPERTY + +BEGIN_PROPERTY(PDFPAGELINKDATA_page) + + GB.ReturnInteger(aux_get_page_from_action(_object,THIS->action)); + +END_PROPERTY + +BEGIN_PROPERTY (PDFPAGELINKDATA_type) + + GB.ReturnInteger ( (int32_t)THIS->action->getKind() ); + +END_PROPERTY + +BEGIN_PROPERTY(PDFPAGELINKDATA_check) + + if (THIS->action) + RETURN_SELF(); + else + GB.ReturnNull(); + +END_PROPERTY + +static void aux_get_link_dimensions(void *_object, CPDFRECT *rect) +{ + double l,t,w,h; + double pw,ph; + + pw=THIS->page->getMediaWidth(); + ph=THIS->page->getMediaHeight(); + +#if POPPLER_VERSION_22_06_0 + + uint i = 0; + l = t = w = h = 0; + for (AnnotLink *link : THIS->links->getLinks()) { + if (i == THIS->lcurrent) { + link->getRect(&l, &t, &w, &h); + break; + } + i++; + } + +#else + + THIS->links->getLink(THIS->lcurrent)->getRect(&l, &t, &w, &h); + +#endif + + w -= l; + h -= t; + + switch (get_rotation(THIS)) + { + case 0: + rect->x = (l*THIS->scale); + rect->y = ((ph-t-h)*THIS->scale); + rect->w = (w*THIS->scale); + rect->h = (h*THIS->scale); + break; + + case 90: + rect->y = (l*THIS->scale); + rect->x = (t*THIS->scale); + rect->h = (w*THIS->scale); + rect->w = (h*THIS->scale); + break; + + case 180: + rect->x = ((l-w)*THIS->scale); + rect->y = (t*THIS->scale); + rect->w = (w*THIS->scale); + rect->h = (h*THIS->scale); + break; + + case 270: + rect->y = ((pw-l-w)*THIS->scale); + rect->x = ((ph-t-h)*THIS->scale); + rect->h = (w*THIS->scale); + rect->w = (h*THIS->scale); + break; + } +} + +BEGIN_PROPERTY(PdfPageLink_rect) + + CPDFRECT *rect = create_rect(); + aux_get_link_dimensions(THIS, rect); + GB.ReturnObject(rect); + +END_PROPERTY + + +/***************************************************************************** + + Finding a text in a PDF page + +******************************************************************************/ + +BEGIN_METHOD (PDFPAGE_find,GB_STRING Text; GB_BOOLEAN Sensitive;) + + TextOutputDev *textdev; + double x0=0, y0=0; + double x1, y1; + CPDFFIND *el; + Unicode *block=NULL; + int nlen=0; + bool sensitive=false; + int count; + double x, y, w, h, wp, hp; + int rotation; + + // TODO: Use UCS-4BE on big endian systems? + if (GB.ConvString ((char **)(void *)&block,STRING(Text),LENGTH(Text),"UTF-8",GB_SC_UNICODE)) + { + GB.Error("Invalid UTF-8 string"); + return; + } + + nlen=GB.StringLength((char*)block)/sizeof(Unicode); + + if (!MISSING(Sensitive)) sensitive=VARG(Sensitive); + + textdev = new TextOutputDev (NULL, true, 0, false, false); + THIS->page->display (textdev, 72, 72, 0, false, false, false); + + if (THIS->Found) { GB.FreeArray(POINTER(&THIS->Found)); THIS->Found=NULL; } + + count = 0; + while (textdev->findText (block,nlen,false,true,true,false,sensitive,false,false,&x0,&y0,&x1,&y1)) + { + if (!THIS->Found) + GB.NewArray(POINTER(&THIS->Found),sizeof(CPDFFIND),1); + else + GB.Add(POINTER(&THIS->Found)); + + el = &(THIS->Found[count++]); //(CPDFFIND*)&((CPDFFIND*)THIS->Found)[GB.Count(POINTER(THIS->Found))-1]; + + x = x0; + y = y0; + w = x1 - x0; + h = y1 - y0; + + wp = THIS->page->getMediaWidth(); + hp = THIS->page->getMediaHeight(); + rotation = THIS->page->getRotate(); + if (rotation == 90 || rotation == 270) + { + x0 = wp; wp = hp; hp = x0; + } + + rotation = THIS->rotation; //get_rotation(THIS); + while (rotation > 0) + { + x0 = wp; wp = hp; hp = x0; + + x0 = wp - y - h; + y0 = x; + + x = w; w = h; h = x; + + x = x0; + y = y0; + + rotation -= 90; + } + + el->x0 = x * THIS->scale; + el->y0 = y * THIS->scale; + el->x1 = w * THIS->scale; + el->y1 = h * THIS->scale; + } + + delete textdev; + + GB.ReturnBoolean(count == 0); + +END_METHOD + + +BEGIN_METHOD(PDFPAGERESULT_get,GB_INTEGER Index) + + CPDFRECT *rect; + CPDFFIND *el; + int index; + + index = VARG(Index); + + if (!THIS->Found || index < 0 || index >= GB.Count(THIS->Found)) + { + GB.Error("Out of bounds"); + return; + } + + el = &(THIS->Found[index]); + rect = create_rect(); + + rect->x = el->x0; + rect->y = el->y0; + rect->w = el->x1; + rect->h = el->y1; + + GB.ReturnObject(rect); + +END_METHOD + +BEGIN_PROPERTY (PDFPAGERESULT_count) + + if (!THIS->Found) { GB.ReturnInteger(0); return; } + GB.ReturnInteger( GB.Count(POINTER(THIS->Found)) ); + +END_PROPERTY + + +/********************************************************************** + +Gambas Interface + +***********************************************************************/ + +GB_DESC PdfRectDesc[] = +{ + GB_DECLARE("PdfRect", sizeof(CPDFRECT)), GB_NOT_CREATABLE(), + + GB_PROPERTY_READ("X", "f", PdfRect_X), + GB_PROPERTY_READ("Y", "f", PdfRect_Y), + GB_PROPERTY_READ("Width", "f", PdfRect_Width), + GB_PROPERTY_READ("Height", "f", PdfRect_Height), + GB_PROPERTY_READ("W", "f", PdfRect_Width), + GB_PROPERTY_READ("H", "f", PdfRect_Height), + GB_PROPERTY_READ("Left", "f", PdfRect_X), + GB_PROPERTY_READ("Top", "f", PdfRect_Y), + GB_PROPERTY_READ("Right", "f", PdfRect_Right), + GB_PROPERTY_READ("Bottom", "f", PdfRect_Bottom), + + GB_END_DECLARE +}; + + +GB_DESC PdfResultDesc[]= +{ + GB_DECLARE(".PdfDocumentPage.Result",0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get","PdfRect",PDFPAGERESULT_get,"(Index)i"), + GB_PROPERTY_READ("Count","i",PDFPAGERESULT_count), + + GB_END_DECLARE +}; + + +GB_DESC PdfLinkDataDesc[]= +{ + GB_DECLARE(".PdfDocumentPage.Link.Data",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Type","i",PDFPAGELINKDATA_type), + GB_PROPERTY_READ("Target","s",PDFPAGELINKDATA_uri), + GB_PROPERTY_READ("Parameters","s",PDFPAGELINKDATA_parameters), + GB_PROPERTY_READ("Page","i",PDFPAGELINKDATA_page), + GB_PROPERTY_READ("Zoom","f",PDFPAGELINKDATA_zoom), + GB_PROPERTY_READ("Rect", "PdfRect", PdfPageLinkData_Rect), + + GB_END_DECLARE +}; + + +GB_DESC PdfLinkDesc[]= +{ + GB_DECLARE(".PdfDocumentPage.Link",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Rect", "PdfRect", PdfPageLink_rect), + GB_PROPERTY_READ("Data",".PdfDocumentPage.Link.Data", PDFPAGELINKDATA_check), + + GB_END_DECLARE +}; + + +GB_DESC PdfIndexDesc[]= +{ + GB_DECLARE(".PdfDocument.Index",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Expanded","b",PDFINDEX_is_open), + GB_PROPERTY_READ("Count","i",PDFINDEX_count), + GB_PROPERTY_READ("HasChildren","b",PDFINDEX_has_children), + GB_PROPERTY_READ("Title","s",PDFINDEX_title), + GB_PROPERTY_READ("Text","s",PDFINDEX_title), + + GB_PROPERTY_READ("Data", ".PdfDocumentPage.Link.Data", PDFPAGELINKDATA_check), + GB_METHOD("MovePrevious","b",PDFINDEX_prev,0), + GB_METHOD("MoveNext","b",PDFINDEX_next,0), + GB_METHOD("MoveChild","b",PDFINDEX_child,0), + GB_METHOD("MoveParent","b",PDFINDEX_parent,0), + GB_METHOD("MoveRoot",0,PDFINDEX_root,0), + + GB_END_DECLARE +}; + + +GB_DESC PdfPageDesc[]= +{ + GB_DECLARE(".PdfDocumentPage",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("W","f",PDFPAGE_width), + GB_PROPERTY_READ("H","f",PDFPAGE_height), + GB_PROPERTY_READ("Width","f",PDFPAGE_width), + GB_PROPERTY_READ("Height","f",PDFPAGE_height), + + GB_PROPERTY_READ("Image","Image",PDFPAGE_property_image), + GB_PROPERTY_SELF("Result",".PdfDocumentPage.Result"), + + GB_METHOD("GetImage","Image",PDFPAGE_image,"[(X)i(Y)i(Width)i(Height)i]"), + GB_METHOD("Find","b",PDFPAGE_find,"(Text)s[(CaseSensitive)b]"), + GB_METHOD("Select","s",PDFPAGE_select,"[(X)i(Y)i(W)i(H)i]"), + + GB_METHOD("_get",".PdfDocumentPage.Link",PDFPAGELINKS_get,"(Index)i"), + GB_PROPERTY_READ("Count","i",PDFPAGELINKS_count), + + GB_END_DECLARE +}; + + +GB_DESC PdfDocumentInfo[] = +{ + GB_DECLARE(".PdfDocument.Info",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Title","s",PDFINFO_title), + GB_PROPERTY_READ("Format","s",PDFINFO_format), + GB_PROPERTY_READ("Author","s",PDFINFO_author), + GB_PROPERTY_READ("Subject","s",PDFINFO_subject), + GB_PROPERTY_READ("Keywords","s",PDFINFO_keywords), + GB_PROPERTY_READ("Creator","s",PDFINFO_creator), + GB_PROPERTY_READ("Producer","s",PDFINFO_producer), + GB_PROPERTY_READ("CreationDate","d",PDFINFO_creation), + GB_PROPERTY_READ("ModificationDate","d",PDFINFO_modification), + GB_PROPERTY_READ("Linearized","b",PDFINFO_linearized), + GB_PROPERTY_READ("Layout","i",PDFINFO_layout), + GB_PROPERTY_READ("Mode","i",PDFINFO_mode), + GB_PROPERTY_READ("CanCopy","b",PDFINFO_cancopy), + GB_PROPERTY_READ("CanModify","b",PDFINFO_canmodify), + GB_PROPERTY_READ("CanPrint","b",PDFINFO_canprint), + GB_PROPERTY_READ("CanAddNotes","b",PDFINFO_canaddnotes), + + GB_END_DECLARE +}; + + +GB_DESC PdfLayoutDesc[] = +{ + GB_DECLARE("PdfLayout", 0), GB_NOT_CREATABLE(), + + GB_CONSTANT("Unset","i",Catalog::pageLayoutNone), + GB_CONSTANT("SinglePage","i",Catalog::pageLayoutSinglePage), + GB_CONSTANT("OneColumn","i",Catalog::pageLayoutOneColumn), + GB_CONSTANT("TwoColumnLeft","i",Catalog::pageLayoutTwoColumnLeft), + GB_CONSTANT("TwoColumnRight","i",Catalog::pageLayoutTwoColumnRight), + GB_CONSTANT("TwoPageLeft","i",Catalog::pageLayoutTwoPageLeft), + GB_CONSTANT("TwoPageRight","i",Catalog::pageLayoutTwoPageRight), + + GB_END_DECLARE +}; + + +GB_DESC PdfModeDesc[] = +{ + GB_DECLARE("PdfPageMode",0), GB_NOT_CREATABLE(), + + GB_CONSTANT("Unset","i",Catalog::pageModeNone), + GB_CONSTANT("UseOutlines","i",Catalog::pageModeOutlines), + GB_CONSTANT("UseThumbs","i",Catalog::pageModeThumbs), + GB_CONSTANT("FullScreen","i",Catalog::pageModeFullScreen), + GB_CONSTANT("UseOC","i",Catalog::pageModeOC), + GB_CONSTANT("UseAttachments","i",Catalog::pageModeAttach), + + GB_END_DECLARE +}; + +GB_DESC PdfDocumentDesc[] = +{ + GB_DECLARE("PdfDocument", sizeof(CPDFDOCUMENT)), + + GB_CONSTANT("Unknown","i",actionUnknown), /* unknown action */ + GB_CONSTANT("Goto","i",actionGoTo), /* go to destination */ + GB_CONSTANT("GotoRemote","i",actionGoToR), /* go to destination in new file */ + GB_CONSTANT("Launch","i",actionLaunch), /* launch app or open doc. */ + GB_CONSTANT("Uri","i",actionURI), /* URI */ + GB_CONSTANT("Named","i",actionNamed), /* named action*/ + GB_CONSTANT("Movie","i",actionMovie), /* movie action */ + + GB_CONSTANT("Normal","i",0), + GB_CONSTANT("Sideways","i",90), + GB_CONSTANT("Inverted","i",180), + GB_CONSTANT("SidewaysInverted","i",270), + + GB_METHOD("_new", 0, PDFDOCUMENT_new, "[(File)s]"), + GB_METHOD("_free", 0, PDFDOCUMENT_free, 0), + + GB_METHOD("Open",0,PDFDOCUMENT_open,"(File)s"), + GB_METHOD("Close",0,PDFDOCUMENT_close,0), + GB_METHOD("_get",".PdfDocumentPage",PDFDOCUMENT_get,"(Index)i"), + + GB_PROPERTY("Zoom", "f", PDFDOCUMENT_scale), + GB_PROPERTY("Orientation", "i", PDFDOCUMENT_rotation), + + GB_PROPERTY_READ("Ready","b",PDFDOCUMENT_ready), + GB_PROPERTY_READ("Count","i",PDFDOCUMENT_count), + GB_PROPERTY_READ("HasIndex","b",PDFDOCUMENT_has_index), + GB_PROPERTY_READ("Index",".PdfDocument.Index",PDFDOCUMENT_index), + GB_PROPERTY_READ("Info",".PdfDocument.Info",PDFDOCUMENT_info), + + GB_END_DECLARE +}; + + diff --git a/gb.pdf/src/CPdfDocument.h b/gb.pdf/src/CPdfDocument.h new file mode 100644 index 00000000..ef1219f9 --- /dev/null +++ b/gb.pdf/src/CPdfDocument.h @@ -0,0 +1,142 @@ +/*************************************************************************** + + CPdfDocument.h + + (C) 2005-2007 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPDFDOCUMENT_H +#define __CPDFDOCUMENT_H + +#include "gambas.h" + +#include +#include +#include +#if POPPLER_VERSION_0_76 +#include +#include +#else +#include +#endif +#include + +#if POPPLER_VERSION_0_76 + #define const_LinkAction const LinkAction + #define const_LinkDest const LinkDest + #define const_GooList const std::vector + #define GooList std::vector + #define const_GooString const GooString +#elif POPPLER_VERSION_0_64 + #define const_LinkAction const LinkAction + #define const_LinkDest const LinkDest + #define const_GooList const GooList + #define const_GooString const GooString +#else + #define const_LinkAction LinkAction + #define const_LinkDest LinkDest + #define const_GooList GooList + #define const_GooString GooString +#endif + +#ifndef __CPDFDOCUMENT_C + +extern GB_DESC PdfRectDesc[]; +extern GB_DESC PdfDocumentDesc[]; +extern GB_DESC PdfPageDesc[]; +extern GB_DESC PdfResultDesc[]; +extern GB_DESC PdfLinkDesc[]; +extern GB_DESC PdfLinkDataDesc[]; +extern GB_DESC PdfIndexDesc[]; +extern GB_DESC PdfDocumentInfo[]; +extern GB_DESC PdfLayoutDesc[]; +extern GB_DESC PdfModeDesc[]; + +#else + +#define THIS ((CPDFDOCUMENT *)_object) +#define THIS_RECT ((CPDFRECT *)_object) + +#endif + +#if POPPLER_VERSION_0_76 + +#define CPDF_list_get(_list, _i) ((_list)->at(_i)) +#define CPDF_list_count(_list) ((_list)->size()) + +#else + +#define CPDF_list_get(_list, _i) ((OutlineItem *)(_list)->get(_i)) +#define CPDF_list_count(_list) ((_list)->getLength()) + +#endif + +#define CPDF_index_get(_i) CPDF_list_get(THIS->index, _i) +#define CPDF_index_count() CPDF_list_count(THIS->index) + + +typedef + struct { + GB_BASE ob; + double x, y, w, h; + } + CPDFRECT; + +typedef + struct { + double x0; + double y0; + double x1; + double y1; + } + CPDFFIND; + +typedef + struct { + GB_BASE ob; + + char *buf; + int len; + + PDFDoc *doc; + SplashOutputDev *dev; + Page *page; + uint currpage; + + void **pindex; // Parent of current index entries + const_GooList *index; // Current entries + + uint currindex; // Current entry + uint *oldindex; // Parent entry + + Links *links; // Page bookmarks + uint lcurrent; // Current bookmark + + CPDFFIND *Found; // Found text elements + + const_LinkAction *action; // Current link action + + double scale; + int rotation; + } + CPDFDOCUMENT; + + + +#endif diff --git a/gb.pdf/src/Makefile.am b/gb.pdf/src/Makefile.am new file mode 100644 index 00000000..ba2a7781 --- /dev/null +++ b/gb.pdf/src/Makefile.am @@ -0,0 +1,17 @@ +COMPONENT = gb.pdf +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.pdf.la + +gb_pdf_la_LIBADD = @POPPLER_LIB@ +gb_pdf_la_LDFLAGS = -module @LD_FLAGS@ @POPPLER_LDFLAGS@ +gb_pdf_la_CXXFLAGS = $(AM_CXXFLAGS) $(GB_CXXFLAGS_STD_CPP17) +gb_pdf_la_CPPFLAGS = @POPPLER_INC@ + +gb_pdf_la_SOURCES = \ + main.h main.cpp \ + CPdfDocument.h CPdfDocument.cpp + + + + diff --git a/gb.pdf/src/gb.pdf.component b/gb.pdf/src/gb.pdf.component new file mode 100644 index 00000000..272d938a --- /dev/null +++ b/gb.pdf/src/gb.pdf.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.pdf +Author=Daniel Campos Fernández,Benoît Minisini,Ian Haywood +Requires=gb.image +State=Deprecated diff --git a/gb.pdf/src/main.cpp b/gb.pdf/src/main.cpp new file mode 100644 index 00000000..ff06493e --- /dev/null +++ b/gb.pdf/src/main.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.cpp + + (C) 2005 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "CPdfDocument.h" +#include "main.h" + +#include + +#include + +extern "C" { + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + PdfRectDesc, + PdfDocumentDesc, + PdfPageDesc, + PdfResultDesc, + PdfIndexDesc, + PdfLinkDesc, + PdfLinkDataDesc, + PdfDocumentInfo, + PdfLayoutDesc, + PdfModeDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + if (!globalParams) + { +#if POPPLER_VERSION_0_83 + globalParams = std::unique_ptr(new GlobalParams()); +#else + globalParams = new GlobalParams(); +#endif + } + + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + return 0; +} + +void EXPORT GB_EXIT() +{ + +} + + +} + + diff --git a/gb.pdf/src/main.h b/gb.pdf/src/main.h new file mode 100644 index 00000000..1d67d701 --- /dev/null +++ b/gb.pdf/src/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (C) 2005 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "config.h" +#include "gambas.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif diff --git a/gb.poppler/AUTHORS b/gb.poppler/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.poppler/COPYING b/gb.poppler/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.poppler/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.poppler/ChangeLog b/gb.poppler/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.poppler/INSTALL b/gb.poppler/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.poppler/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.poppler/Makefile.am b/gb.poppler/Makefile.am new file mode 100644 index 00000000..919cbe23 --- /dev/null +++ b/gb.poppler/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @POPPLER_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.poppler/NEWS b/gb.poppler/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.poppler/README b/gb.poppler/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.poppler/acinclude.m4 b/gb.poppler/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.poppler/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.poppler/component.am b/gb.poppler/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.poppler/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.poppler/configure.ac b/gb.poppler/configure.ac new file mode 100644 index 00000000..4365f513 --- /dev/null +++ b/gb.poppler/configure.ac @@ -0,0 +1,15 @@ +dnl ---- configure.ac for gb.poppler +m4_include([../version.m4]) +AC_INIT([gambas3-gb-poppler],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) +GB_INIT(gb.poppler) +LT_INIT +GB_COMPONENT_PKG_CONFIG( + poppler, + POPPLER, + gb.poppler, + [src], + 'poppler >= 0.20' poppler-cpp poppler-glib) +AC_CONFIG_FILES([Makefile src/Makefile ]) +AC_OUTPUT +GB_PRINT_MESSAGES diff --git a/gb.poppler/gambas.h b/gb.poppler/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.poppler/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.poppler/gb.geom.h b/gb.poppler/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.poppler/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.poppler/gb.image.h b/gb.poppler/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.poppler/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.poppler/gb_common.h b/gb.poppler/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.poppler/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.poppler/m4 b/gb.poppler/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.poppler/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.poppler/reconf b/gb.poppler/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.poppler/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.poppler/src/Makefile.am b/gb.poppler/src/Makefile.am new file mode 100644 index 00000000..5bffd3ad --- /dev/null +++ b/gb.poppler/src/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.poppler +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.poppler.la + +gb_poppler_la_LIBADD = @POPPLER_LIB@ +gb_poppler_la_LDFLAGS = -module @LD_FLAGS@ @POPPLER_LDFLAGS@ +gb_poppler_la_CXXFLAGS = $(AM_CXXFLAGS) $(GB_CXXFLAGS_STD_CPP17) +gb_poppler_la_CPPFLAGS = @POPPLER_INC@ + +gb_poppler_la_SOURCES = main.h main.cpp \ + c_pdf_document.h c_pdf_document.cpp diff --git a/gb.poppler/src/c_pdf_document.cpp b/gb.poppler/src/c_pdf_document.cpp new file mode 100644 index 00000000..9b8061f9 --- /dev/null +++ b/gb.poppler/src/c_pdf_document.cpp @@ -0,0 +1,874 @@ +/*************************************************************************** + + c_pdf_document.cpp + + gb.poppler component + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_PDF_DOCUMENT_CPP + +#if defined __has_include +# if __has_include () +# include +# endif +#endif + +#include + +#include "c_pdf_document.h" + +struct _PopplerPage +{ + GObject parent_instance; + PopplerDocument *document; + Page *page; + int index; + void *text; +}; + +#define GET_CURRENT_PAGE() (((_PopplerPage *)THIS->current)->page) + +struct _PopplerDocument +{ + GObject parent_instance; + void *initer; + PDFDoc *doc; + + GList *layers; + GList *layers_rbgroups; + void *output_dev; +}; + +#define GET_DOCUMENT() (((_PopplerDocument *)THIS->doc)->doc) + +/*static poppler::rotation_enum conv_rotation(int angle) +{ + if (angle < 0) + angle = 360 - (-angle) % 360; + else + angle = angle % 360; + + switch (angle) + { + case 0: return poppler::rotate_0; + case 90: return poppler::rotate_90; + case 180: return poppler::rotate_180; + case 270: return poppler::rotate_270; + default: return poppler::rotate_0; + } +}*/ + +static GEOM_RECTF *make_rect(PopplerRectangle *rect) +{ + GEOM_RECTF *ob = GEOM.CreateRectF(); + ob->x = rect->x1; + ob->y = rect->y1; + ob->w = rect->x2 - rect->x1; + ob->h = rect->y2 - rect->y1; + return ob; +} + +//-------------------------------------------------------------------------- + +BEGIN_METHOD(PdfDocument_new, GB_STRING path; GB_STRING password) + + const char *password; + GError *error = NULL; + std::string rpasswd; + + if (GB.LoadFile(STRING(path), LENGTH(path), &THIS->buffer, &THIS->length)) + return; + + if (MISSING(password)) + password = NULL; + else + password = GB.ToZeroString(ARG(password)); + +#if POPPLER_CHECK_VERSION(0,82,0) + GBytes *bytes = g_bytes_new_static(THIS->buffer, THIS->length); + THIS->doc = poppler_document_new_from_bytes(bytes, password, &error); + g_bytes_unref(bytes); +#else + THIS->doc = poppler_document_new_from_data(THIS->buffer, THIS->length, password, &error); +#endif + if (!THIS->doc) + { + GB.Error(error->message); + return; + } + + THIS->resolution = 72.0; + + if (password) + rpasswd = password; + THIS->rdoc = poppler::document::load_from_raw_data(THIS->buffer, THIS->length, rpasswd, rpasswd); + + THIS->renderer = new poppler::page_renderer; + + THIS->renderer->set_render_hint(poppler::page_renderer::antialiasing, true); + THIS->renderer->set_render_hint(poppler::page_renderer::text_antialiasing, true); + THIS->renderer->set_render_hint(poppler::page_renderer::text_hinting, false); + +END_METHOD + +BEGIN_METHOD_VOID(PdfDocument_free) + + int i; + + if (THIS->doc) + { + if (THIS->pages) + { + for (i = 0; i < poppler_document_get_n_pages(THIS->doc); i++) + { + if (THIS->pages[i]) + g_object_unref(THIS->pages[i]); + } + + GB.Free(POINTER(&THIS->pages)); + } + + if (THIS->index) + { + for (i = 0; i < GB.Count(THIS->index); i++) + GB.Unref(POINTER(&THIS->index[i])); + + GB.FreeArray(POINTER(&THIS->index)); + } + + delete THIS->renderer; + delete THIS->rdoc; + g_object_unref(THIS->doc); + } + + GB.ReleaseFile(THIS->buffer, THIS->length); + +END_METHOD + +BEGIN_PROPERTY(PdfDocument_Count) + + GB.ReturnInteger(poppler_document_get_n_pages(THIS->doc)); + +END_PROPERTY + +BEGIN_PROPERTY(PdfDocument_Max) + + GB.ReturnInteger(poppler_document_get_n_pages(THIS->doc) - 1); + +END_PROPERTY + +#define IMPLEMENT_DOC_STRING_PROP(_name, _func) \ +BEGIN_PROPERTY(PdfDocument_##_name) \ + GB.ReturnNewZeroString(poppler_document_get_##_func(THIS->doc)); \ +END_PROPERTY + +IMPLEMENT_DOC_STRING_PROP(Author, author) +IMPLEMENT_DOC_STRING_PROP(Creator, creator) +IMPLEMENT_DOC_STRING_PROP(Producer, producer) +IMPLEMENT_DOC_STRING_PROP(Subject, subject) +IMPLEMENT_DOC_STRING_PROP(Title, title) +IMPLEMENT_DOC_STRING_PROP(Keywords, keywords) + +#define IMPLEMENT_DOC_DATE_PROP(_name, _func) \ +BEGIN_PROPERTY(PdfDocument_##_name) \ + GB_DATE date; \ + GB.MakeDateFromTime(poppler_document_get_##_func(THIS->doc), 0, &date); \ + GB.ReturnDate(&date); \ +END_PROPERTY + +IMPLEMENT_DOC_DATE_PROP(CreationDate, creation_date) +IMPLEMENT_DOC_DATE_PROP(ModificationDate, modification_date) + +BEGIN_PROPERTY(PdfDocument_Version) + + guint major, minor; + char buf[32]; + int len; + + poppler_document_get_pdf_version(THIS->doc, &major, &minor); + + len = snprintf(buf, sizeof(buf), "%d.%d", major, minor); + GB.ReturnNewString(buf, len); + +END_PROPERTY + +BEGIN_PROPERTY(PdfDocument_Linearized) + + GB.ReturnBoolean(poppler_document_is_linearized(THIS->doc)); + +END_PROPERTY + +BEGIN_METHOD(PdfDocument_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= poppler_document_get_n_pages(THIS->doc)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + if (!THIS->pages) + GB.AllocZero(POINTER(&THIS->pages), sizeof(void *) * poppler_document_get_n_pages(THIS->doc)); + + if (!THIS->pages[index]) + THIS->pages[index] = poppler_document_get_page(THIS->doc, index); + + THIS->current = THIS->pages[index]; + + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(PdfDocument_Resolution) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->resolution); + else + { + double res = VPROP(GB_FLOAT); + + if (res <= 0) + { + GB.Error(GB_ERR_ARG); + return; + } + THIS->resolution = res; + } + +END_PROPERTY + +BEGIN_PROPERTY(PdfDocument_Rotation) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->rotation); + else + THIS->rotation = VPROP(GB_INTEGER); + +END_PROPERTY + +static int fill_index(void *_object, PopplerIndexIter *iter, int parent) +{ + PopplerIndexIter *child; + CPDFACTION *action; + int n = 0; + GB_CLASS class_action = GB.FindClass("PdfAction"); + + do + { + CPDFINDEX *item = (CPDFINDEX *)GB.New(GB.FindClass("PdfIndex"), NULL, NULL); + + item->index = GB.Count(THIS->index); + item->parent = parent; + item->opened = poppler_index_iter_is_open(iter); + + action = (CPDFACTION *)GB.New(class_action, NULL, NULL); + action->action = poppler_index_iter_get_action(iter); + GB.Ref(action); + item->action = action; + + *(void **)GB.Add(&THIS->index) = item; + GB.Ref(item); + n++; + + child = poppler_index_iter_get_child(iter); + if (child) + { + item->children = fill_index(_object, child, item->index); + poppler_index_iter_free(child); + } + + } + while (poppler_index_iter_next(iter)); + + return n; +} + +BEGIN_PROPERTY(PdfDocument_Index) + + PopplerIndexIter *iter; + + if (!THIS->index) + { + GB.NewArray(&THIS->index, sizeof(void *), 0); + iter = poppler_index_iter_new(THIS->doc); + if (iter) + { + fill_index(THIS, iter, -1); + poppler_index_iter_free(iter); + } + } + + RETURN_SELF(); + +END_PROPERTY + +BEGIN_METHOD(PdfDocument_Find, GB_STRING label) + + PopplerPage *page = poppler_document_get_page_by_label(THIS->doc, GB.ToZeroString(ARG(label))); + + if (!page) + GB.ReturnInteger(-1); + else + GB.ReturnInteger(poppler_page_get_index(page)); + +END_METHOD + +#define IMPLEMENT_DOC_HINT_PROP(_name, _hint) \ +BEGIN_PROPERTY(PdfDocument_##_name) \ + if (READ_PROPERTY) \ + GB.ReturnBoolean(THIS->renderer->render_hints() & poppler::page_renderer::_hint); \ + else \ + THIS->renderer->set_render_hint(poppler::page_renderer::_hint, VPROP(GB_BOOLEAN)); \ +END_PROPERTY + +IMPLEMENT_DOC_HINT_PROP(Antialiasing, antialiasing) +IMPLEMENT_DOC_HINT_PROP(TextAntialiasing, text_antialiasing) +IMPLEMENT_DOC_HINT_PROP(TextHinting, text_hinting) + +//-------------------------------------------------------------------------- + +/*BEGIN_PROPERTY(PdfPage_Orientation) + + GB.ReturnInteger(THIS->current->orientation()); + +END_PROPERTY*/ + +BEGIN_METHOD(PdfPage_Render, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; GB_INTEGER rotation; GB_FLOAT res) + + poppler::page *page; + poppler::rectf size; + poppler::rotation_enum rot; + poppler::image image; + + const char *data = NULL; + int rotation = VARGOPT(rotation, THIS->rotation); + int orientation = 0; + double res = VARGOPT(res, THIS->resolution); + int width, height; + int x, y, w, h; + + page = THIS->rdoc->create_page(poppler_page_get_index(THIS->current)); + + switch (page->orientation()) + { + case poppler::page::portrait: orientation = 0; break; + case poppler::page::landscape: orientation = 90; break; + case poppler::page::upside_down: orientation = 180; break; + case poppler::page::seascape: orientation = 270; break; + } + + orientation = (orientation + rotation + 720) % 360; + + switch (orientation) + { + case 90: rot = poppler::rotate_90; break; + case 180: rot = poppler::rotate_180; break; + case 270: rot = poppler::rotate_270; break; + default: rot = poppler::rotate_0; + } + + size = page->page_rect(poppler::media_box); + + if (orientation % 180) + { + width = (int)(size.height() * res / 72.0); + height = (int)(size.width() * res / 72.0); + } + else + { + width = (int)(size.width() * res / 72.0); + height = (int)(size.height() * res / 72.0); + } + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(width, width); + h = VARGOPT(height, height); + + if (x < 0) + { + w += x; + x = 0; + } + + if (y < 0) + { + h += y; + y = 0; + } + + if ((x + w) > width) + w = width - x; + + if ((y + h) > height) + h = height - y; + + if (w > 0 && h > 0) + { + image = THIS->renderer->render_page(page, res, res, x, y, w, h, rot); + data = image.const_data(); + } + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_BGRA, (unsigned char *)data)); + +END_METHOD + +BEGIN_PROPERTY(PdfPage_Thumbnail) + + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_PROPERTY(PdfPage_Label) + + GB.ReturnNewZeroString(poppler_page_get_label(THIS->current)); + +END_PROPERTY + +BEGIN_PROPERTY(PdfPage_Text) + + GB.ReturnNewZeroString(poppler_page_get_text(THIS->current)); + +END_METHOD + +BEGIN_METHOD(PdfPage_GetText, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + PopplerRectangle rect; + + rect.x1 = VARG(x); + rect.y1 = VARG(y); + rect.x2 = rect.x1 + VARG(w); + rect.y2 = rect.y1 + VARG(h); + + GB.ReturnNewZeroString(poppler_page_get_selected_text(THIS->current, POPPLER_SELECTION_GLYPH, &rect)); + +END_METHOD + +BEGIN_PROPERTY(PdfPage_Width) + + double w; + poppler_page_get_size(THIS->current, &w, NULL); + GB.ReturnFloat(w); + +END_PROPERTY + +BEGIN_PROPERTY(PdfPage_Height) + + double h; + poppler_page_get_size(THIS->current, NULL, &h); + GB.ReturnFloat(h); + +END_PROPERTY + +BEGIN_METHOD(PdfPage_FindText, GB_STRING search; GB_INTEGER options) + + GList *rects, *r; + GB_ARRAY result; + GEOM_RECTF *rect; + + rects = r = poppler_page_find_text_with_options(THIS->current, GB.ToZeroString(ARG(search)), (PopplerFindFlags)VARGOPT(options, POPPLER_FIND_DEFAULT)); + + GB.Array.New(&result, GB.FindClass("RectF"), 0); + + while (r) + { + rect = make_rect((PopplerRectangle *)r->data); + GB.Ref(rect); + *(GEOM_RECTF **)GB.Array.Add(result) = rect; + r = r->next; + } + + g_list_free(rects); + + GB.ReturnObject(result); + +END_METHOD + +//-------------------------------------------------------------------------- + +BEGIN_PROPERTY(PdfDocumentIndex_Count) + + GB.ReturnInteger(GB.Count(THIS->index)); + +END_PROPERTY + +BEGIN_PROPERTY(PdfDocumentIndex_Max) + + GB.ReturnInteger(GB.Count(THIS->index) - 1); + +END_PROPERTY + +BEGIN_METHOD(PdfDocumentIndex_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= GB.Count(THIS->index)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(THIS->index[index]); + +END_PROPERTY + +BEGIN_METHOD_VOID(PdfDocumentIndex_next) + + int *index = (int *)GB.GetEnum(); + + if (*index >= GB.Count(THIS->index)) + GB.StopEnum(); + else + { + GB.ReturnObject(THIS->index[*index]); + (*index)++; + } + +END_METHOD + +//-------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(PdfIndex_free) + + GB.Unref(POINTER(&THIS_INDEX->action)); + +END_METHOD + +BEGIN_PROPERTY(PdfIndex_Parent) + + GB.ReturnInteger(THIS_INDEX->parent); + +END_PROPERTY + +BEGIN_PROPERTY(PdfIndex_Children) + + GB.ReturnInteger(THIS_INDEX->children); + +END_PROPERTY + +BEGIN_PROPERTY(PdfIndex_Opened) + + GB.ReturnBoolean(THIS_INDEX->opened); + +END_PROPERTY + +BEGIN_PROPERTY(PdfIndex_Action) + + GB.ReturnObject(THIS_INDEX->action); + +END_PROPERTY + +BEGIN_PROPERTY(PdfIndex_Text) + + GB.ReturnNewZeroString(THIS_INDEX->action->action->any.title); + +END_PROPERTY + + +//-------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(PdfAction_free) + + poppler_action_free(ACTION); + +END_METHOD + +BEGIN_PROPERTY(PdfAction_Type) + + const char *type; + + switch(ACTION->type) + { + case POPPLER_ACTION_NONE: type = "None"; break; + case POPPLER_ACTION_GOTO_DEST: type = "GotoDest"; break; + case POPPLER_ACTION_GOTO_REMOTE: type = "GotoRemote"; break; + case POPPLER_ACTION_LAUNCH: type = "Launch"; break; + case POPPLER_ACTION_URI: type = "URI"; break; + case POPPLER_ACTION_NAMED: type = "Named"; break; + case POPPLER_ACTION_MOVIE: type = "Movie"; break; + case POPPLER_ACTION_RENDITION: type = "Rendition"; break; + case POPPLER_ACTION_OCG_STATE: type = "OGCState"; break; + case POPPLER_ACTION_JAVASCRIPT: type =" Javascript"; break; + default: type = NULL; + } + + GB.ReturnConstZeroString(type); + +END_PROPERTY + +BEGIN_PROPERTY(PdfAction_Text) + + GB.ReturnNewZeroString(ACTION->any.title); + +END_PROPERTY + +static PopplerDest *get_dest(PopplerAction *action) +{ + switch(action->type) + { + case POPPLER_ACTION_GOTO_DEST: return action->goto_dest.dest; + case POPPLER_ACTION_GOTO_REMOTE: return action->goto_remote.dest; + default: return NULL; + } +} + +BEGIN_PROPERTY(PdfAction_Page) + + PopplerDest *dest = get_dest(ACTION); + + if (dest) + GB.ReturnInteger(dest->page_num); + else + GB.ReturnInteger(-1); + +END_PROPERTY + +BEGIN_PROPERTY(PdfAction_Zoom) + + PopplerDest *dest = get_dest(ACTION); + + if (dest) + GB.ReturnFloat(dest->zoom); + else + GB.ReturnFloat(0); + +END_PROPERTY + +BEGIN_PROPERTY(PdfAction_Rect) + + PopplerDest *dest = get_dest(ACTION); + + if (dest) + { + GEOM_RECTF *rect = GEOM.CreateRectF(); + rect->x = dest->left; + rect->y = dest->top; + rect->w = dest->right - dest->left; + rect->h = dest->bottom - dest->top; + GB.ReturnObject(rect); + } + else + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_PROPERTY(PdfAction_Target) + + const char *target; + + switch(ACTION->type) + { + case POPPLER_ACTION_GOTO_REMOTE: target = ACTION->goto_remote.file_name; break; + case POPPLER_ACTION_LAUNCH: target = ACTION->launch.file_name; break; + case POPPLER_ACTION_URI: target = ACTION->uri.uri; break; + case POPPLER_ACTION_NAMED: target = ACTION->named.named_dest; break; + default: target = NULL; + } + + GB.ReturnNewZeroString(target); + +END_PROPERTY + +BEGIN_PROPERTY(PdfAction_Arguments) + + const char *args; + + switch(ACTION->type) + { + case POPPLER_ACTION_LAUNCH: args = ACTION->launch.params; break; + default: args = NULL; + } + + GB.ReturnNewZeroString(args); + +END_PROPERTY + +//-------------------------------------------------------------------------- + +GB_DESC PdfActionDesc[] = +{ + GB_DECLARE("PdfAction", sizeof(CPDFACTION)), + + GB_METHOD("_free", NULL, PdfAction_free, NULL), + GB_PROPERTY_READ("Type", "s", PdfAction_Type), + GB_PROPERTY_READ("Text", "s", PdfAction_Text), + GB_PROPERTY_SELF("Goto", ".PdfActionGoto"), + GB_PROPERTY_SELF("Launch", ".PdfActionLaunch"), + GB_PROPERTY_SELF("URI", ".PdfActionURI"), + + GB_END_DECLARE +}; + +GB_DESC PdfActionGotoDesc[] = +{ + GB_DECLARE_VIRTUAL(".PdfActionGoto"), + + GB_PROPERTY_READ("Page", "i", PdfAction_Page), + GB_PROPERTY_READ("Zoom", "f", PdfAction_Zoom), + GB_PROPERTY_READ("Rect", "RectF", PdfAction_Rect), + GB_PROPERTY_READ("Target", "s", PdfAction_Target), + + GB_END_DECLARE +}; + +GB_DESC PdfActionLaunchDesc[] = +{ + GB_DECLARE_VIRTUAL(".PdfActionLaunch"), + + GB_PROPERTY_READ("Target", "s", PdfAction_Target), + GB_PROPERTY_READ("Arguments", "s", PdfAction_Arguments), + + GB_END_DECLARE +}; + +GB_DESC PdfActionURIDesc[] = +{ + GB_DECLARE_VIRTUAL(".PdfActionURI"), + + GB_PROPERTY_READ("Target", "s", PdfAction_Target), + + GB_END_DECLARE +}; + +GB_DESC PdfIndexDesc[] = +{ + GB_DECLARE("PdfIndex", sizeof(CPDFINDEX)), + + GB_METHOD("_free", NULL, PdfIndex_free, NULL), + + GB_PROPERTY_READ("Action", "PdfAction", PdfIndex_Action), + GB_PROPERTY_READ("Text", "s", PdfIndex_Text), + GB_PROPERTY_READ("Parent", "i", PdfIndex_Parent), + GB_PROPERTY_READ("Children", "i", PdfIndex_Children), + GB_PROPERTY_READ("Opened", "b", PdfIndex_Opened), + + GB_END_DECLARE +}; + +GB_DESC PdfDocumentIndexDesc[] = +{ + GB_DECLARE_VIRTUAL(".PdfDocumentIndex"), + + GB_PROPERTY_READ("Count", "i", PdfDocumentIndex_Count), + GB_PROPERTY_READ("Max", "i", PdfDocumentIndex_Max), + GB_METHOD("_get", "PdfIndex", PdfDocumentIndex_get, "(Index)i"), + GB_METHOD("_next", "PdfIndex", PdfDocumentIndex_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC PdfPageDesc[] = +{ + GB_DECLARE_VIRTUAL(".PdfPage"), + + //GB_PROPERTY_READ("Orientation", "i", PdfPage_Orientation), + + GB_METHOD("Render", "Image", PdfPage_Render, "[(X)i(Y)i(Width)i(Height)i(Rotation)i(Resolution)f]"), + GB_PROPERTY_READ("Label", "s", PdfPage_Label), + GB_PROPERTY_READ("Text", "s", PdfPage_Text), + GB_METHOD("GetText", "s", PdfPage_GetText,"(X)f(Y)f(Width)f(Height)f"), + GB_PROPERTY_READ("Width", "f", PdfPage_Width), + GB_PROPERTY_READ("Height", "f", PdfPage_Height), + GB_PROPERTY_READ("W", "f", PdfPage_Width), + GB_PROPERTY_READ("H", "f", PdfPage_Height), + GB_METHOD("FindText", "RectF[]", PdfPage_FindText, "(Search)s[(Options)i]"), + GB_PROPERTY_READ("Thumbnail", "Image", PdfPage_Thumbnail), + + GB_END_DECLARE +}; + +GB_DESC PdfDocumentDesc[] = +{ + GB_DECLARE("PdfDocument", sizeof(CPDFDOCUMENT)), + + /*GB_CONSTANT("Unknown","i",actionUnknown), + GB_CONSTANT("Goto","i",actionGoTo), + GB_CONSTANT("GotoRemote","i",actionGoToR), + GB_CONSTANT("Launch","i",actionLaunch), + GB_CONSTANT("Uri","i",actionURI), + GB_CONSTANT("Named","i",actionNamed), + GB_CONSTANT("Movie","i",actionMovie), + + GB_CONSTANT("Normal","i",0), + GB_CONSTANT("Sideways","i",90), + GB_CONSTANT("Inverted","i",180), + GB_CONSTANT("SidewaysInverted","i",270),*/ + + GB_METHOD("_new", 0, PdfDocument_new, "(Path)s[(Owner)s(Password)s]"), + GB_METHOD("_free", 0, PdfDocument_free, 0), + + GB_METHOD("_get", ".PdfPage", PdfDocument_get, "(Index)i"), + GB_METHOD("Find", "i", PdfDocument_Find, "(Label)s"), + + //GB_PROPERTY("Zoom", "f", PDFDOCUMENT_scale), + //GB_PROPERTY("Orientation", "i", PDFDOCUMENT_rotation), + + //GB_PROPERTY_READ("Ready","b",PDFDOCUMENT_ready), + GB_PROPERTY_READ("Count","i",PdfDocument_Count), + GB_PROPERTY_READ("Max","i",PdfDocument_Max), + //GB_PROPERTY_READ("HasIndex","b",PDFDOCUMENT_has_index), + //GB_PROPERTY_READ("Index",".PdfDocument.Index",PDFDOCUMENT_index), + //GB_PROPERTY_READ("Info",".PdfDocument.Info",PDFDOCUMENT_info), + + GB_PROPERTY_READ("Author", "s", PdfDocument_Author), + GB_PROPERTY_READ("Creator", "s", PdfDocument_Creator), + GB_PROPERTY_READ("Producer", "s", PdfDocument_Producer), + GB_PROPERTY_READ("Subject", "s", PdfDocument_Subject), + GB_PROPERTY_READ("Title", "s", PdfDocument_Title), + GB_PROPERTY_READ("Keywords", "s", PdfDocument_Keywords), + + GB_PROPERTY_READ("CreationDate", "d", PdfDocument_CreationDate), + GB_PROPERTY_READ("ModificationDate", "d", PdfDocument_ModificationDate), + + GB_PROPERTY_READ("Version", "s", PdfDocument_Version), + GB_PROPERTY_READ("Linearized", "b", PdfDocument_Linearized), + + GB_PROPERTY("Resolution", "f", PdfDocument_Resolution), + GB_PROPERTY("Rotation", "i", PdfDocument_Rotation), + + GB_PROPERTY("Antialiasing", "b", PdfDocument_Antialiasing), + GB_PROPERTY("TextAntialiasing", "b", PdfDocument_TextAntialiasing), + GB_PROPERTY("TextHinting", "b", PdfDocument_TextHinting), + + GB_PROPERTY_READ("Index", ".PdfDocumentIndex", PdfDocument_Index), + + GB_END_DECLARE +}; + +GB_DESC PdfDesc[] = +{ + GB_DECLARE_STATIC("Pdf"), + + /*GB_CONSTANT("Landscape", "i", poppler::page::landscape), + GB_CONSTANT("Portrait", "i", poppler::page::portrait), + GB_CONSTANT("Seascape", "i", poppler::page::seascape), + GB_CONSTANT("UpsideDown", "i", poppler::page::upside_down),*/ + + GB_CONSTANT("CaseSensitive", "i", POPPLER_FIND_CASE_SENSITIVE), + GB_CONSTANT("Backwards", "i", POPPLER_FIND_BACKWARDS), + GB_CONSTANT("WholeWordsOnly", "i", POPPLER_FIND_WHOLE_WORDS_ONLY), +#if POPPLER_CHECK_VERSION(0,73,0) + GB_CONSTANT("IgnoreDiacritics", "i", POPPLER_FIND_IGNORE_DIACRITICS), +#endif + + GB_END_DECLARE +}; diff --git a/gb.poppler/src/c_pdf_document.h b/gb.poppler/src/c_pdf_document.h new file mode 100644 index 00000000..f91dde54 --- /dev/null +++ b/gb.poppler/src/c_pdf_document.h @@ -0,0 +1,89 @@ +/*************************************************************************** + + c_pdf_document.h + + gb.poppler component + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_PDF_DOCUMENT_H +#define __C_PDF_DOCUMENT_H + +//#include +//#include "splash/SplashBitmap.h" +#include "glib/poppler.h" +#include "cpp/poppler-document.h" +#include "cpp/poppler-page.h" +#include "cpp/poppler-page-renderer.h" +#include "main.h" + +#ifndef __C_PDF_DOCUMENT_CPP +extern GB_DESC PdfDesc[]; +extern GB_DESC PdfDocumentDesc[]; +extern GB_DESC PdfPageDesc[]; +extern GB_DESC PdfDocumentIndexDesc[]; +extern GB_DESC PdfIndexDesc[]; +extern GB_DESC PdfActionDesc[]; +extern GB_DESC PdfActionGotoDesc[]; +extern GB_DESC PdfActionLaunchDesc[]; +extern GB_DESC PdfActionURIDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + PopplerAction *action; + } + CPDFACTION; + +typedef + struct { + GB_BASE ob; + CPDFACTION *action; + int index; + int parent; + int children; + unsigned opened : 1; + } + CPDFINDEX; + +typedef + struct { + GB_BASE ob; + char *buffer; + int length; + PopplerDocument *doc; + PopplerPage **pages; + PopplerPage *current; + double resolution; + int rotation; + CPDFINDEX **index; + poppler::document *rdoc; + poppler::page_renderer *renderer; + } + CPDFDOCUMENT; + + +#define THIS ((CPDFDOCUMENT *)_object) +#define THIS_INDEX ((CPDFINDEX *)_object) +#define THIS_ACTION ((CPDFACTION *)_object) +#define ACTION (THIS_ACTION->action) + +#endif /* __C_PDF_DOCUMENT_H */ diff --git a/gb.poppler/src/gb.poppler.component b/gb.poppler/src/gb.poppler.component new file mode 100644 index 00000000..1c506d2e --- /dev/null +++ b/gb.poppler/src/gb.poppler.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +Requires=gb.image +State=NotFinished diff --git a/gb.poppler/src/main.cpp b/gb.poppler/src/main.cpp new file mode 100644 index 00000000..611a51d6 --- /dev/null +++ b/gb.poppler/src/main.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + + main.cpp + + gb.poppler component + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "c_pdf_document.h" +#include "main.h" + +extern "C" +{ +GB_INTERFACE GB EXPORT; +GEOM_INTERFACE GEOM EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + PdfDesc, + PdfActionDesc, + PdfActionGotoDesc, + PdfActionLaunchDesc, + PdfActionURIDesc, + PdfIndexDesc, + PdfPageDesc, + PdfDocumentIndexDesc, + PdfDocumentDesc, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.geom"; + +int EXPORT GB_INIT(void) +{ + GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + return 0; +} + +void EXPORT GB_EXIT() +{ + +} + +} diff --git a/gb.poppler/src/main.h b/gb.poppler/src/main.h new file mode 100644 index 00000000..8a11e673 --- /dev/null +++ b/gb.poppler/src/main.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + main.h + + gb.poppler component + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.geom.h" +#include "gb.image.h" + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern GEOM_INTERFACE GEOM; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.qt4/AUTHORS b/gb.qt4/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/COPYING b/gb.qt4/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.qt4/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.qt4/ChangeLog b/gb.qt4/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/INSTALL b/gb.qt4/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.qt4/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.qt4/Makefile.am b/gb.qt4/Makefile.am new file mode 100644 index 00000000..a3db3e0d --- /dev/null +++ b/gb.qt4/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @QT_DIR@ +EXTRA_DIST = reconf share gb*.h gambas.h diff --git a/gb.qt4/NEWS b/gb.qt4/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/README b/gb.qt4/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/acinclude.m4 b/gb.qt4/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.qt4/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.qt4/component.am b/gb.qt4/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.qt4/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.qt4/configure.ac b/gb.qt4/configure.ac new file mode 100644 index 00000000..1c9ba6b9 --- /dev/null +++ b/gb.qt4/configure.ac @@ -0,0 +1,63 @@ +dnl ---- configure.ac for gb.qt + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-qt4],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.qt4) +LT_INIT +PKG_PROG_PKG_CONFIG + +AC_ARG_ENABLE( + qt-translation, + [ --enable-qt-translation use qt translation files (default: yes)], + gb_use_qt_translation=$enableval, + gb_use_qt_translation=yes +) + +if test "$gb_use_qt_translation" = "yes"; then + AC_DEFINE(USE_QT_TRANSLATION, 1, [Use QT translation]) +fi + +GB_CHECK_XWINDOW +AM_CONDITIONAL(XWINDOW, test x"$have_x" = xyes) + +GB_COMPONENT_PKG_CONFIG( + qt, QT, gb.qt4, [src], + 'QtCore >= 4.5.0' QtGui QtSvg x11 +) + +GB_COMPONENT_PKG_CONFIG( + qtext, QTEXT, gb.qt4.ext, [ext], + 'QtCore >= 4.5.0' QtGui Qt3Support x11 +) + +GB_COMPONENT_PKG_CONFIG( + qtwebkit, QTWEBKIT, gb.qt4.webkit, [webkit], + 'QtCore >= 4.5.0' QtGui QtNetwork QtDBus QtXml 'QtWebKit >= 4.5.0' +) + +GB_COMPONENT_PKG_CONFIG( + qtwebview, QTWEBVIEW, gb.qt4.webview, [webview], + 'QtCore >= 4.5.0' QtGui QtNetwork QtDBus QtXml 'QtWebKit >= 4.5.0' +) + +GB_COMPONENT_PKG_CONFIG( + qtopengl, QTOPENGL, gb.qt4.opengl, [opengl], + 'QtCore >= 4.5.0' QtGui QtOpenGL x11 gl +) + +MOC=`$PKG_CONFIG --variable=moc_location QtCore` +AC_SUBST(MOC) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +src/ext/Makefile \ +src/webkit/Makefile \ +src/webview/Makefile \ +src/opengl/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.qt4/gambas.h b/gb.qt4/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.qt4/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.qt4/gb.draw.h b/gb.qt4/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.qt4/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.qt4/gb.eval.h b/gb.qt4/gb.eval.h new file mode 120000 index 00000000..33d332f0 --- /dev/null +++ b/gb.qt4/gb.eval.h @@ -0,0 +1 @@ +../main/lib/eval/gb.eval.h \ No newline at end of file diff --git a/gb.qt4/gb.geom.h b/gb.qt4/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.qt4/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.qt4/gb.gl.h b/gb.qt4/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.qt4/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.qt4/gb.image.h b/gb.qt4/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.qt4/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.qt4/gb.paint.h b/gb.qt4/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.qt4/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.qt4/gb.qt.am b/gb.qt4/gb.qt.am new file mode 100644 index 00000000..a475f791 --- /dev/null +++ b/gb.qt4/gb.qt.am @@ -0,0 +1,6 @@ +CLEANFILES = *_moc.cpp + +.h_moc.cpp: + $(MOC) -o $@ $< + +SUFFIXES = .h _moc.cpp \ No newline at end of file diff --git a/gb.qt4/gb_common.h b/gb.qt4/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.qt4/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.qt4/gbc_read_common.h b/gb.qt4/gbc_read_common.h new file mode 120000 index 00000000..9dd882ab --- /dev/null +++ b/gb.qt4/gbc_read_common.h @@ -0,0 +1 @@ +../main/share/gbc_read_common.h \ No newline at end of file diff --git a/gb.qt4/m4 b/gb.qt4/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.qt4/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.qt4/reconf b/gb.qt4/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.qt4/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.qt4/share/gb.form.action.h b/gb.qt4/share/gb.form.action.h new file mode 100644 index 00000000..a7cde837 --- /dev/null +++ b/gb.qt4/share/gb.form.action.h @@ -0,0 +1,93 @@ +/*************************************************************************** + + gb.form.action.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_FORM_ACTION_H + +/*************************************************************************** + +#define HAS_ACTION(_widget) +If a widget has the action flag set. + +#define SET_ACTION(_widget, _flag) +Sets or clears the action flag. + +***************************************************************************/ + +static GB_FUNCTION _action_register_func; +static GB_FUNCTION _action_raise_func; +//static GB_FUNCTION _action_get_func; + +static void init_action() +{ + static bool init = false; + void *klass; + + if (init) + return; + + klass = (void *)GB.FindClass("Action"); + GB.GetFunction(&_action_register_func, klass, "_Register", "oss", ""); + GB.GetFunction(&_action_raise_func, klass, "Raise", "o", ""); + //GB.GetFunction(&_action_get_func, klass, "Get", "o", "s"); + + init = true; +} + +void CACTION_register(void *control, const char *old, const char *key) +{ + //qDebug("CACTION_register: (%s %p) %s", GB.GetClassName(control), control, key); + //fprintf(stderr, "CACTION_register: (%s %p %p) %s\n", GB.GetClassName(control), control, ((CWIDGET *)control)->widget, key); + + if ((!key || !*key) && !HAS_ACTION(control)) + //&& !GB.Is(control, CLASS_UserControl) && !GB.Is(control, CLASS_UserContainer)) + return; + + init_action(); + + //GB.Ref(control); + + SET_ACTION(control, key && *key); + + GB.Push(3, + GB_T_OBJECT, control, + GB_T_STRING, old, 0, + GB_T_STRING, key, 0); + + // The register function must not raise an error, otherwise bad things may happen + GB.Call(&_action_register_func, 3, true); + + //GB.Unref(&control); +} + +void CACTION_raise(void *control) +{ + init_action(); + + //qDebug("CACTION_raise: (%s %p)", GB.GetClassName(THIS), THIS); + + if (!HAS_ACTION(control)) + return; + + GB.Push(1, GB_T_OBJECT, control); + GB.Call(&_action_raise_func, 1, true); +} diff --git a/gb.qt4/share/gb.form.arrangement.h b/gb.qt4/share/gb.form.arrangement.h new file mode 100644 index 00000000..78f6bd84 --- /dev/null +++ b/gb.qt4/share/gb.form.arrangement.h @@ -0,0 +1,736 @@ +/*************************************************************************** + + gb.form.arrangement.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_FORM_ARRANGEMENT_H + +/*************************************************************************** + +#define WIDGET_TYPE +Datatype of variables that points at a widget + +#define CONTAINER_TYPE +Datatype of variables that points at a container widget + +#define ARRANGEMENT_TYPE +Datatype of variables that points at the arrangement structure. +This structure contains all the arrangement flags, and must have (at least) +the following fields: +- mode : the arrangement style. +- spacing : if children must be separate by Desktop.Scale +- padding : number of pixels around the arrangement area. +- margin : if the arrangement area must have a margin of Desktop.Scale +- indent: if the arrangement area must have an indentation of Desktop.Scale +- autoresize : if the container must try to fit its contents. +- locked : if the container is being arranged. +- user : if the container is a UserControl or a UserContainer. + +#define IS_RIGHT_TO_LEFT(_object) +If the control is right to left written + +#define GET_WIDGET(_object) +Returns the widget associated with the gambas control + +#define GET_CONTAINER(_object) +Returns the container widget inside the gambas control that contains the +child widgets. It can be the same for a Panel, for example, or a sub widget +for something more complex, like a TabStrip. + +#define GET_ARRANGEMENT(_object) +Returns a pointer to the arrangement flags of a Gambas control. + +#define IS_EXPAND(_object) +Returns if the Expand property of a control is set or not. + +#define IS_DESIGN(_object) +Returns if the control is in design mode. + +#define IS_WIDGET_VISIBLE(_widget) +Returns if a widget is visible to the screen. + +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) +Sets the _x, _y, _w & _h variables with the dimensions of the area +inside the container that must be arranged, in container coordinates. +If the container has a border, for example, then _x > 0 & _y > 0. + +#define GET_WIDGET_X(_widget) +Returns the x position of a widget inside its parent. + +#define GET_WIDGET_Y(_widget) +Returns the y position of a widget inside its parent. + +#define GET_WIDGET_W(_widget) +Returns the width of a widget. + +#define GET_WIDGET_H(_widget) +Returns the height of a widget. + +#define MOVE_WIDGET(_object, _widget, _x, _y) +Moves a widget inside its parent. + +#define RESIZE_WIDGET(_object, _widget, _w, _h) +Resizes a widget. + +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) +Move & resize a widget simultaneously. + +#define RESIZE_CONTAINER(_object, _w, _h) +Resizes the container object by resizing the widget itself. + +#define INIT_CHECK_CHILDREN_LIST(_widget) +Initializes the list of children of _widget. Must return if there is no +children. + +#define RESET_CHILDREN_LIST() +Resets the children list so that its next enumeration starts from the first child. + +#define GET_NEXT_CHILD_WIDGET() +Returns the next child to arrange, or NULL if there is no child anymore. +Make a function, as this macro is called several times. + +#define GET_OBJECT_FROM_WIDGET(_widget) +Returns the Gambas object associated with the specified widget, or NULL if there is +no Gambas object. + +#define DESKTOP_SCALE +Returns the value of Desktop.Scale + +#define RAISE_ARRANGE_EVENT(_object) +Code to raise the Arrange event of containers. + +#define RAISE_BEFORE_ARRANGE_EVENT(_object) +Code to raise the BeforeArrange event of containers. + +#define FUNCTION_NAME +This is the name of the arrangement function + + +***************************************************************************/ + +#define ARRANGE_ROW ARRANGE_LEFT_RIGHT +#define ARRANGE_COLUMN ARRANGE_TOP_BOTTOM + +void FUNCTION_NAME(void *_object) //(QFrame *cont) +{ + CONTAINER_TYPE cont; + ARRANGEMENT_TYPE arr; + + WIDGET_TYPE wid; + int x, y, w, h, i; + int xc, yc, wc, hc; + #ifndef GET_MAX_SIZE + int wf, hf; + #endif + int dmax = 0, d; + int sexp, nexp; + bool swap, autoresize, invert; + #ifndef GET_MAX_SIZE + bool has_expand_children = false; + #endif + bool redo; + bool first; + void *ob; + bool rtl; + int rtlm; + int padding; + int spacing; + int indent; + bool centered; + + if (!CAN_ARRANGE(_object)) + return; + + cont = (CONTAINER_TYPE)GET_CONTAINER(_object); + arr = GET_ARRANGEMENT(_object); + + //if (!IS_WIDGET_VISIBLE(cont) && !IS_WIDGET_VISIBLE(GET_WIDGET(_object))) + // return; + + //fprintf(stderr, "CCONTAINER_arrange: %s: locked %d: mode %d: autoresize: %d\n", GET_OBJECT_NAME(_object), arr->locked, arr->mode, arr->autoresize); + + if (arr->locked) + return; + +// if (GET_WIDGET_W(cont) <= 1 || GET_WIDGET_H(cont) <= 1) +// return; + + //if (qstrcmp(GB.GetClassName(THIS), "FOutput") == 0) + // qDebug("CCONTAINER_arrange: do it!"); + + arr->locked = true; + + RAISE_BEFORE_ARRANGE_EVENT(_object); + + if (arr->mode != ARRANGE_NONE) + { + // g_debug("arrange: %s (%d %d) (%d %d)", ((gControl *)_object)->name(), ((gContainer *)_object)->width(), ((gContainer *)_object)->height(), ((gContainer *)_object)->clientWidth(), ((gContainer *)_object)->clientHeight()); + //usleep(50000); + + if (arr->user) + cont = (CONTAINER_TYPE)GET_WIDGET(_object); + + // INIT_CHECK_CHILDREN_LIST() can return + arr->locked = false; + if (!cont) + goto __RETURN; + + INIT_CHECK_CHILDREN_LIST(cont); + + //fprintf(stderr, "CCONTAINER_arrange: %s: children: %d\n", GET_OBJECT_NAME(_object), list.count()); + + if (!HAS_CHILDREN()) + goto __RETURN; + + arr->locked = true; + + //fprintf(stderr, "arrange: %s %d (%d %d) (%d %d)\n", ((gControl *)_object)->name(), arr->mode, ((gContainer *)_object)->width(), ((gContainer *)_object)->height(), ((gContainer *)_object)->clientWidth(), ((gContainer *)_object)->clientHeight()); + + invert = arr->invert; + rtl = IS_RIGHT_TO_LEFT(_object); + swap = (arr->mode & 1) == 0; // means "vertical" + + if (!swap && invert) + { + rtl = !rtl; + invert = false; + } + + rtlm = rtl ? -1 : 1; + + autoresize = arr->autoresize; // && !IS_EXPAND(_object); + + if (arr->margin) + padding = arr->padding ? arr->padding : DESKTOP_SCALE; + else if (!arr->spacing) + padding = arr->padding; + else + padding = 0; + + if (arr->spacing) + spacing = arr->padding ? arr->padding : DESKTOP_SCALE; + else + spacing = 0; + + indent = arr->indent * DESKTOP_SCALE; + + centered = arr->centered; + + for (i = 0; i < 3; i++) + { + redo = false; + + GET_WIDGET_CONTENTS(cont, xc, yc, wc, hc); + #ifndef GET_MAX_SIZE + wf = GET_WIDGET_W(cont) - wc; + hf = GET_WIDGET_H(cont) - hc; + #endif + + //fprintf(stderr, "cont: %s: %d %d %d %d (%d %d)\n", ((gControl *)_object)->name(), xc, yc, wc, hc, wf, hf); + + //if (hc > GET_WIDGET_H(cont)) + // qDebug("hc = %d H = %d ?", hc, GET_WIDGET_H(cont)); + + xc += padding; + yc += padding; + wc -= padding * 2; + hc -= padding * 2; + + //if (!strcmp(GET_OBJECT_NAME(_object), "TabStrip1")) + // fprintf(stderr, "#0: %d %d %d %d\n", xc, yc, wc, hc); + + if (indent) + { + if (swap) + { + if (!invert) + yc += indent; + hc -= indent; + } + else + { + if (!rtl) + xc += indent; + wc -= indent; + } + } + + // Beware! This is not the same test! + if (autoresize) + { + if (wc <= 0 && hc <= 0) + break; + } + else + { + if (wc <= 0 || hc <= 0) + break; + } + + //if (!strcmp(GET_OBJECT_NAME(_object), "HBox1")) + // fprintf(stderr, "#1\n"); + + x = xc; + y = yc; + w = h = 0; + + if (swap) + { + if (invert) + y += hc; + } + else + { + if (rtl) + x += wc; + } + + wid = 0; + RESET_CHILDREN_LIST(); + + switch (arr->mode) + { + case ARRANGE_HORIZONTAL: + case ARRANGE_VERTICAL: + + sexp = 0; + nexp = 0; + dmax = 0; + + for(;;) + { + wid = GET_NEXT_CHILD_WIDGET(); + + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + + if (!ob || IS_IGNORE(ob)) + continue; + + /*if (!::strcmp(GET_OBJECT_NAME(_object), "panFont")) + fprintf(stderr, "CCONTAINER_arrange: %s: child: %s\n", GET_OBJECT_NAME(_object), GET_OBJECT_NAME(ob));*/ + + if (IS_EXPAND(ob)) + nexp++; + else + sexp += (swap ? GET_WIDGET_H(wid) : GET_WIDGET_W(wid)); + + if (autoresize) + { + d = swap ? GET_WIDGET_W(wid) : GET_WIDGET_H(wid); + if (d > dmax) + { + dmax = d; + } + //fprintf(stderr, "%s: %s: d = %d dmax = %d\n", ((gControl *)_object)->name(), wid->name(), d, dmax); + } + + sexp += spacing; + } + + #ifndef GET_MAX_SIZE + has_expand_children = nexp > 0; + #endif + + if (autoresize) + { + // TODO: use rtl + if (swap) + wc = dmax + indent; + else + hc = dmax + indent; + //fprintf(stderr, "%s: dmax = %d\n", ((CWIDGET *)_object)->name, dmax); + } + + sexp -= spacing; + sexp = (swap ? hc : wc) - sexp; + if (sexp < 0) + sexp = 0; + + if (centered && sexp && nexp == 0) + { + if (swap) + { + y += sexp / 2; + hc -= sexp / 2; + } + else + { + x += sexp / 2; + wc -= sexp / 2; + } + sexp = 0; + } + + RESET_CHILDREN_LIST(); + wid = 0; + + /*if (!::strcmp(GET_OBJECT_NAME(_object), "panFont")) + fprintf(stderr, "CCONTAINER_arrange: %s: %d %d / %d x %d / nexp = %d\n", GET_OBJECT_NAME(_object), xc, yc, wc, hc, nexp);*/ + + for(;;) + { + first = wid == 0; + + wid = GET_NEXT_CHILD_WIDGET(); + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + if (!ob || IS_IGNORE(ob)) + continue; + + if (!first) + { + if (swap) + { + if (invert) + y -= spacing; + else + y += spacing; + } + else + x += spacing * rtlm; + } + + if (swap) + { + if (IS_EXPAND(ob)) // && !autoresize) + { + if (nexp == 0) // the list of expanded widget may change + h = 0; + else + { + h = sexp / nexp; + sexp -= h; + nexp--; + } + + if (h <= 0) + MOVE_WIDGET(ob, wid, -GET_WIDGET_W(wid), GET_WIDGET_Y(wid)); + } + else + h = GET_WIDGET_H(wid); + + //w = autoresize ? GET_WIDGET_W(wid) : wc; + w = wc; + + if (h >= 0 && w >= 0) + { + if (w != GET_WIDGET_W(wid) || h != GET_WIDGET_H(wid)) + redo = true; + + if (invert) + { + y -= h; + MOVE_RESIZE_WIDGET(ob, wid, x, y, w, h); + } + else + { + MOVE_RESIZE_WIDGET(ob, wid, x, y, w, h); + y += h; + } + } + } + else + { + if (IS_EXPAND(ob)) // && !autoresize) + { + if (nexp == 0) // the list of expanded widget may change + { + w = 0; + } + else + { + w = sexp / nexp; + sexp -= w; + nexp--; + } + + if (w <= 0) + MOVE_WIDGET(ob, wid, GET_WIDGET_X(wid), -GET_WIDGET_H(wid)); + } + else + w = GET_WIDGET_W(wid); + + //h = autoresize ? GET_WIDGET_H(wid) : hc; + h = hc; + + if (w >= 0 && h >= 0) + { + if (w != GET_WIDGET_W(wid) || h != GET_WIDGET_H(wid)) + redo = true; + + if (rtl) + MOVE_RESIZE_WIDGET(ob, wid, x - w, y, w, h); + else + MOVE_RESIZE_WIDGET(ob, wid, x, y, w, h); + x += w * rtlm; + } + } + } + break; + + case ARRANGE_ROW: + case ARRANGE_COLUMN: + + wc += xc; + hc += yc; + + for(;;) + { + wid = GET_NEXT_CHILD_WIDGET(); + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + if (!ob || IS_IGNORE(ob)) + continue; + + if (swap) + { + if (invert) + { + if ((y < hc) && ((y - (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_H(wid))) < yc)) + { + y = hc; + x += w + spacing; + w = 0; + } + + if (IS_EXPAND(ob)) + { + if (rtl) + MOVE_RESIZE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), yc, GET_WIDGET_W(wid), y - yc); + else + MOVE_RESIZE_WIDGET(ob, wid, x, yc, GET_WIDGET_W(wid), y - yc); + y = yc - spacing; + } + else + { + if (rtl) + MOVE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), y - GET_WIDGET_H(wid)); + else + MOVE_WIDGET(ob, wid, x, y - GET_WIDGET_H(wid)); + y -= GET_WIDGET_H(wid) + spacing; + } + + if (GET_WIDGET_W(wid) > w) + w = GET_WIDGET_W(wid); + } + else + { + if ((y > yc) && ((y + (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_H(wid))) > hc)) + { + y = yc; + x += w + spacing; + w = 0; + } + + if (IS_EXPAND(ob)) + { + if (rtl) + MOVE_RESIZE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), y, GET_WIDGET_W(wid), hc - y); + else + MOVE_RESIZE_WIDGET(ob, wid, x, y, GET_WIDGET_W(wid), hc - y); + y = hc + spacing; + } + else + { + if (rtl) + MOVE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), y); + else + MOVE_WIDGET(ob, wid, x, y); + y += GET_WIDGET_H(wid) + spacing; + } + + if (GET_WIDGET_W(wid) > w) + w = GET_WIDGET_W(wid); + } + } + else + { + if (rtl) + { + if ((x < wc) && ((x - (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_W(wid))) < xc)) + { + x = wc; + y += h + spacing; + h = 0; + } + + if (IS_EXPAND(ob)) + { + MOVE_RESIZE_WIDGET(ob, wid, xc, y, x - xc, GET_WIDGET_H(wid)); + x = xc - spacing; + } + else + { + MOVE_WIDGET(ob, wid, x - GET_WIDGET_W(wid), y); + x -= GET_WIDGET_W(wid) + spacing; + } + + if (GET_WIDGET_H(wid) > h) + h = GET_WIDGET_H(wid); + } + else + { + if ((x > xc) && ((x + (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_W(wid))) > wc)) + { + x = xc; + y += h + spacing; + h = 0; + } + + if (IS_EXPAND(ob)) + { + MOVE_RESIZE_WIDGET(ob, wid, x, y, wc - x, GET_WIDGET_H(wid)); + x = wc + spacing; + } + else + { + MOVE_WIDGET(ob, wid, x, y); + x += GET_WIDGET_W(wid) + spacing; + } + + if (GET_WIDGET_H(wid) > h) + h = GET_WIDGET_H(wid); + } + } + } + break; + + case ARRANGE_FILL: + + for(;;) + { + wid = GET_NEXT_CHILD_WIDGET(); + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + if (!ob || IS_IGNORE(ob)) + continue; + + if (centered) + MOVE_WIDGET(ob, wid, xc + (wc - GET_WIDGET_W(wid)) / 2, yc + (hc - GET_WIDGET_H(wid)) / 2); + else + MOVE_RESIZE_WIDGET(ob, wid, xc, yc, wc, hc); + + /*if (GET_WIDGET_H(wid) > h) + h = GET_WIDGET_H(wid); + + if (GET_WIDGET_W(wid) > w) + w = GET_WIDGET_W(wid);*/ + } + + w = wc; + h = hc; + + break; + } + + if (autoresize) + { + switch(arr->mode) + { + case ARRANGE_HORIZONTAL: +// #ifndef QNAMESPACE_H +// fprintf(stderr, "%s: HORIZONTAL: x = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d\n", +// ((gControl *)_object)->name(), x, x + arr->padding + wf, GET_WIDGET_H(cont), arr->padding, spacing, i, rtl); +// #else +// fprintf(stderr, "%s: HORIZONTAL: x = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d hec %d\n", +// ((CWIDGET *)_object)->name, x, has_expand_children ? GET_WIDGET_W(cont) : x + arr->padding + wf, +// dmax + hf, arr->padding, spacing, i, rtl, has_expand_children); +// #endif + //cont->resize(x + cont->width() - wc - xc, cont->height()); + if (dmax > 0) + { + if (rtl) + //RESIZE_WIDGET(cont, xc - x + arr->padding + wf, GET_WIDGET_H(cont)); + RESIZE_CONTAINER(_object, cont, has_expand_children ? GET_WIDGET_W(cont) : xc - x + padding + wf, dmax + hf + padding * 2); + else + //RESIZE_WIDGET(cont, x + arr->padding + wf, GET_WIDGET_H(cont)); + RESIZE_CONTAINER(_object, cont, has_expand_children ? GET_WIDGET_W(cont) : x + padding + wf, dmax + hf + padding * 2); + } + + break; + + case ARRANGE_VERTICAL: +// #ifndef QNAMESPACE_H +// fprintf(stderr, "%s: VERTICAL: x = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d\n", +// ((gControl *)_object)->name(), x, x + arr->padding + wf, GET_WIDGET_H(cont), arr->padding, spacing, i, rtl); +// #else +// fprintf(stderr, "%s: VERTICAL: y = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d hec %d\n", +// ((CWIDGET *)_object)->name, y, dmax + wf, has_expand_children ? GET_WIDGET_H(cont) : y + arr->padding + hf, +// arr->padding, spacing, i, rtl, has_expand_children); +// #endif + //RESIZE_WIDGET(cont, GET_WIDGET_W(cont), y + arr->padding + hf); + if (dmax > 0) + RESIZE_CONTAINER(_object, cont, dmax + wf + padding * 2, has_expand_children ? GET_WIDGET_H(cont) : y + padding + hf); + break; + + case ARRANGE_COLUMN: + if (rtl) + RESIZE_CONTAINER(_object, cont, (wc - x) + w + padding + wf, GET_WIDGET_H(cont)); + else + RESIZE_CONTAINER(_object, cont, x + w + padding + wf, GET_WIDGET_H(cont)); + break; + + case ARRANGE_ROW: +// #ifndef GET_MAX_SIZE +// #ifndef QNAMESPACE_H +// fprintf(stderr, "%s: ROW: autoresize (%d, %d)\n", ((gControl *)_object)->name(), GET_WIDGET_W(cont), y + h + padding + hf); +// #else +// fprintf(stderr, "%s: ROW: autoresize (%d, %d)\n", ((CWIDGET *)_object)->name, GET_WIDGET_W(cont), y + h + padding + hf); +// #endif +// #endif +// RESIZE_CONTAINER(_object, cont, GET_WIDGET_W(cont), y + h + padding + hf); + break; + + case ARRANGE_FILL: + //fprintf(stderr, "%s: RESIZE_CONTAINER(%p, %p, %d, %d)\n", GET_OBJECT_NAME(_object), GET_WIDGET(_object), cont, w + padding * 2, h + padding * 2); + RESIZE_CONTAINER(_object, cont, w + padding * 2, h + padding * 2); + break; + } + } + + if (!redo) + break; + } + + } + +__RETURN: + + RAISE_ARRANGE_EVENT(_object); + + arr->locked = false; + + //qDebug("%p: dirty = FALSE", THIS); + //arr->dirty = false; + + //qDebug("CCONTAINER_arrange: END %p", THIS); + + //qDebug("CCONTAINER_arrange: %p: END", cont); +} diff --git a/gb.qt4/share/gb.form.const.h b/gb.qt4/share/gb.form.const.h new file mode 100644 index 00000000..496ed4f7 --- /dev/null +++ b/gb.qt4/share/gb.form.const.h @@ -0,0 +1,171 @@ +/*************************************************************************** + + gb.form.const.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_H +#define __GB_FORM_H + +#include "gb.geom.h" + +#define CONST_MAGIC 0x12345678 + +enum { + BORDER_NONE = 0, + BORDER_PLAIN = 1, + BORDER_SUNKEN = 2, + BORDER_RAISED = 3, + BORDER_ETCHED = 4 + }; + +enum { + ARRANGE_NONE = 0, + ARRANGE_HORIZONTAL = 1, + ARRANGE_VERTICAL = 2, + ARRANGE_ROW = 3, + ARRANGE_LEFT_RIGHT = 3, + ARRANGE_COLUMN = 4, + ARRANGE_TOP_BOTTOM = 4, + ARRANGE_FILL = 5, + ARRANGE_TABLE = 6 + }; + +enum +{ + SELECT_NONE = 0, + SELECT_SINGLE = 1, + SELECT_MULTIPLE = 2 +}; + +enum +{ + LINE_NONE = 0, //Qt::NoPen, + LINE_SOLID = 1, //Qt::SolidLine, + LINE_DASH = 2, //Qt::DashLine, + LINE_DOT = 3, //Qt::DotLine, + LINE_DASH_DOT = 4, //Qt::DashDotLine, + LINE_DASH_DOT_DOT = 5, //Qt::DashDotDotLine +}; + +enum +{ + FILL_NONE = 0, //Qt::NoBrush, + FILL_SOLID = 1, //Qt::SolidPattern, + FILL_DENSE_94 = 2, //Qt::Dense1Pattern, + FILL_DENSE_88 = 3, //Qt::Dense2Pattern, + FILL_DENSE_63 = 4, //Qt::Dense3Pattern, + FILL_DENSE_50 = 5, //Qt::Dense4Pattern, + FILL_DENSE_37 = 6, //Qt::Dense5Pattern, + FILL_DENSE_12 = 7, //Qt::Dense6Pattern, + FILL_DENSE_06 = 8, //Qt::Dense7Pattern, + FILL_HORIZONTAL = 9, //Qt::HorPattern, + FILL_VERTICAL = 10, //Qt::VerPattern, + FILL_CROSS = 11, //Qt::CrossPattern, + FILL_DIAGONAL = 12, //Qt::BDiagPattern, + FILL_BACK_DIAGONAL = 13, //Qt::FDiagPattern, + FILL_CROSS_DIAGONAL = 14, //Qt::DiagCrossPattern +}; + +enum { + SCROLL_NONE = 0, + SCROLL_HORIZONTAL = 1, + SCROLL_VERTICAL = 2, + SCROLL_BOTH = 3 + }; + +enum { + POINTER_MOUSE = 0, + POINTER_PEN = 1, + POINTER_ERASER = 2, + POINTER_CURSOR = 3 +}; + +enum { + MOUSE_LEFT = 1, + MOUSE_MIDDLE = 2, + MOUSE_RIGHT = 4, + MOUSE_BUTTON4 = 8, + MOUSE_BUTTON5 = 16, + MOUSE_SHIFT = 256, + MOUSE_CTRL = 512, + MOUSE_ALT = 1024, + MOUSE_META = 2048 +}; + +enum { + ORIENTATION_AUTO = 0, + ORIENTATION_HORIZONTAL = 1, + ORIENTATION_VERTICAL = 2 +}; + +enum { + DRAG_MOVE = 0, + DRAG_COPY = 1, + DRAG_LINK = 2 +}; + +enum { + CURSOR_CUSTOM = -1, + CURSOR_DEFAULT = 0, + CURSOR_NONE, + CURSOR_ARROW, + CURSOR_HELP, + CURSOR_POINTER, + CURSOR_CONTEXT_MENU, + CURSOR_PROGRESS, + CURSOR_WAIT, + CURSOR_CELL, + CURSOR_CROSSHAIR, + CURSOR_TEXT, + CURSOR_VERTICAL_TEXT, + CURSOR_ALIAS, + CURSOR_COPY, + CURSOR_NO_DROP, + CURSOR_MOVE, + CURSOR_NOT_ALLOWED, + CURSOR_GRAB, + CURSOR_GRABBING, + CURSOR_ALL_SCROLL, + CURSOR_COL_RESIZE, + CURSOR_ROW_RESIZE, + CURSOR_N_RESIZE, + CURSOR_E_RESIZE, + CURSOR_S_RESIZE, + CURSOR_W_RESIZE, + CURSOR_NE_RESIZE, + CURSOR_NW_RESIZE, + CURSOR_SW_RESIZE, + CURSOR_SE_RESIZE, + CURSOR_EW_RESIZE, + CURSOR_NS_RESIZE, + CURSOR_NESW_RESIZE, + CURSOR_NWSE_RESIZE, + CURSOR_ZOOM_IN, + CURSOR_ZOOM_OUT +}; + +enum { + DIRECTION_DEFAULT = 0, + DIRECTION_LTR = 1, + DIRECTION_RTL = 2 +}; + +#endif diff --git a/gb.qt4/share/gb.form.font.h b/gb.qt4/share/gb.form.font.h new file mode 100644 index 00000000..ec45d525 --- /dev/null +++ b/gb.qt4/share/gb.form.font.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gb.form.font.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_FONT_H +#define __GB_FORM_FONT_H + +#define FONT_STEP 20 +#define GRADE_TO_SIZE(_grade, _desktop) ((int)(powf(_desktop, 1.0 + ((_grade) / (double)FONT_STEP)) + 0.5)) +#define SIZE_TO_GRADE(_size, _desktop) ((int)(FONT_STEP * (logf(_size) / logf(_desktop)) + 0.5) - FONT_STEP) +#define GET_DESKTOP_SCALE(_font_size, _dpi) (1 + ((_font_size) * (_dpi) * 2 / 3 / 96)) + +#define FONT_GRADE_MIN -8 +#define FONT_GRADE_MAX 24 + +#endif + + diff --git a/gb.qt4/share/gb.form.print.h b/gb.qt4/share/gb.form.print.h new file mode 100644 index 00000000..59673fd7 --- /dev/null +++ b/gb.qt4/share/gb.form.print.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + gb.form.print.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_PRINT_H +#define __GB_FORM_PRINT_H + +enum { + GB_PRINT_PORTRAIT, + GB_PRINT_LANDSCAPE + //GB_PRINT_REVERSE_PORTRAIT, + //GB_PRINT_REVERSE_LANDSCAPE +}; + +enum { + GB_PRINT_CUSTOM, + GB_PRINT_A3, + GB_PRINT_A4, + GB_PRINT_A5, + GB_PRINT_B5, + GB_PRINT_LETTER, + GB_PRINT_EXECUTIVE, + GB_PRINT_LEGAL +}; + +enum { + GB_PRINT_SIMPLEX, + GB_PRINT_DUPLEX_HORIZONTAL, + GB_PRINT_DUPLEX_VERTICAL +}; + + + +#endif + + diff --git a/gb.qt4/share/gb.form.properties.h b/gb.qt4/share/gb.form.properties.h new file mode 100644 index 00000000..ceb7652d --- /dev/null +++ b/gb.qt4/share/gb.form.properties.h @@ -0,0 +1,188 @@ +/*************************************************************************** + + gb.form.properties.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_PROPERTIES_H +#define __GB_FORM_PROPERTIES_H + +#define CCONTROL_PROPERTIES \ + "X{Position},Y{Position},Width{Dimension},Height{Dimension},Visible=True,Enabled=True,Font{Font}," \ + "Background{Color}=-1,Foreground{Color}=-1," \ + "Tag,Tracking,NoTabFocus,Mouse{Cursor.*}=Default,ToolTip,Drop,Expand,Ignore,PopupMenu{Menu},Direction{Direction.*}=Default" +#define CARRANGEMENT_PROPERTY "Arrangement{Arrange.None;Horizontal;Vertical;Row;Column;Fill}" +#define CPADDING_PROPERTIES "Centered,Spacing,Margin,Padding{Range:0;63},Indent" +#define CWINDOW_PADDING_PROPERTIES "Centered,Spacing,Margin,Padding{Range:0;63},Indent" +#define CARRANGEMENT_PROPERTIES CARRANGEMENT_PROPERTY ",AutoResize," CPADDING_PROPERTIES ",Invert" +#define CWINDOW_ARRANGEMENT_PROPERTIES CARRANGEMENT_PROPERTY ",AutoResize," CWINDOW_PADDING_PROPERTIES ",Invert" +#define CUSERCONTROL_PROPERTIES "*" +#define CUSERCONTAINER_PROPERTIES "*," CARRANGEMENT_PROPERTIES + +#define CBUTTON_PROPERTIES "*,Action,AutoResize,Text,Picture,Border=True,Default,Cancel" +#define CCHECKBOX_PROPERTIES "*,Action,AutoResize,Text,Tristate,Invert,Value{CheckBox.False;True;None}" +#define CDIAL_PROPERTIES "*,MinValue=0,MaxValue=100,Step=1,PageStep=10,Wrap,Mark=True" +#define CDRAWINGAREA_PROPERTIES "*," CARRANGEMENT_PROPERTIES ",Border{Border.None;Plain;Sunken;Raised;Etched},Cached,Focus,NoBackground,Tablet" +#define CEDITOR_PROPERTIES "*,Font,Border=True,ScrollBar{Scroll.*}=Both,Highlight{Highlight.None;Custom;Gambas;HTML;CSS;WebPage;Diff;JavaScript;SQL}=None,ReadOnly=False,TabSize{Range:1;16}=2" +#define CHBOX_PROPERTIES "*,AutoResize," CPADDING_PROPERTIES ",Invert" +#define CLABEL_PROPERTIES "*,Padding{Range:0;64},AutoResize,Text,Alignment{Align.*}=Normal,Border{Border.None;Plain;Sunken;Raised;Etched},Transparent" +#define CLCDNUMBER_PROPERTIES "*,Value,Digits{Range:1;64}=1,SmallDecimalPoint,Style{LCDNumber.Outline;Filled;Flat}=Outline,Mode{LCDNumber.Decimal;Hexadecimal;Binary}=Decimal,Border{Border.None;Plain;Sunken;Raised;Etched}" +#define CLISTBOX_PROPERTIES "*,List,Border=True,Mode{Select.*}=Single,Sorted" +#define CMENU_PROPERTIES "Action,Text,Picture,Enabled=True,Radio,Toggle,Checked,Visible=True,Tag,Shortcut" +#define CPANEL_PROPERTIES "*," CARRANGEMENT_PROPERTIES ",Border{Border.None;Plain;Sunken;Raised;Etched}" +#define CPRINTER_PROPERTIES "Orientation{Printer.Portrait;Landscape}=Portrait,Paper{Printer.A3;A4;A5;B5;Letter;Executive}=A4,CollateCopies,ReverseOrder,Duplex{Printer.Simplex;Horizontal;Vertical}=Simplex,GrayScale,FullPage" +#define CRADIOBUTTON_PROPERTIES "*,AutoResize,Text,Invert,Value" +#define CSCROLLBAR_PROPERTIES "*,MinValue=0,MaxValue=100,Step=1,PageStep=10,Tracking=True,Orientation{ScrollBar.Auto;Horizontal;Vertical}=Auto" +#define CSEPARATOR_PROPERTIES "*" +#define CSLIDER_PROPERTIES "*,Action,MinValue=0,MaxValue=100,Step=1,PageStep=10,Tracking=True,Orientation{ScrollBar.Auto;Horizontal;Vertical}=Auto,Mark,Value" +#define CTABSTRIP_PROPERTIES "*," CARRANGEMENT_PROPERTIES ",Count{Range:1;256}=1,Index,Text,TextFont,Picture,Orientation{Align.Top;Bottom;Left;Right}=Top,Closable" +#define CTEXTAREA_PROPERTIES "*,Text,Alignment{Align.Normal;Left;Center;Right}=Normal,ReadOnly,Wrap,Border=True,ScrollBar{Scroll.*}=Both" +#define CTEXTBOX_PROPERTIES "*,Action,Text,Alignment{Align.Normal;Left;Center;Right}=Normal,ReadOnly,Placeholder,Password,MaxLength,Border=True" +#define CTEXTEDIT_PROPERTIES "*,ReadOnly,Wrap,Border=True,ScrollBar{Scroll.*}=Both" +#define CTEXTLABEL_PROPERTIES "*,Padding{Range:0;63},AutoResize,Text,Alignment{Align.*}=TopNormal,Wrap=True,Border{Border.None;Plain;Sunken;Raised;Etched},Transparent" +#define CTOGGLEBUTTON_PROPERTIES "*,Action,AutoResize,Text,Picture,Border=True,Radio,Value" +#define CTOOLBUTTON_PROPERTIES "*,Action,AutoResize,Text,Picture,Border,Radio,Toggle,Value" +#define CTRAYICON_PROPERTIES "Visible=False,Tag,Tooltip,Picture{Picture:NoCache},PopupMenu{Menu}" +#define CVBOX_PROPERTIES "*,AutoResize," CPADDING_PROPERTIES ",Invert" +#define CWINDOW_PROPERTIES "*,Action,Text,Icon,Picture,Mask,Persistent,Resizable=True,Border=True,Utility,TakeFocus=True,Stacking{Window.Normal;Above;Below}=Normal,Minimized,Maximized,FullScreen,Sticky,SkipTaskbar,Opacity{Range:0;100}=100,Transparent," CWINDOW_ARRANGEMENT_PROPERTIES + +#define DESCRIBE_CONTROL(_prop, _event, _size) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", _size) + +#define DESCRIBE_VIEW_CONTROL(_prop, _event, _size) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", _size), \ + GB_CONSTANT("_Group", "s", "View") + +#define DESCRIBE_SPECIAL_CONTROL(_prop, _event, _size) \ + DESCRIBE_CONTROL(_prop, _event, _size), \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_IsVirtual", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "Form"), \ + GB_CONSTANT("_Group", "s", "Special") + +#define DESCRIBE_CONTAINER(_prop, _event) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", "24,24") + +#define DESCRIBE_CONTAINER_SMALL(_prop, _event) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", "8,8") + +#define DESCRIBE_CONTAINER_ARR(_prop, _event, _arr) \ + DESCRIBE_CONTAINER(_prop, _event), \ + GB_CONSTANT("_DefaultArrangement", "s", _arr) + +#define DESCRIBE_CONTAINER_SMALL_ARR(_prop, _event, _arr) \ + DESCRIBE_CONTAINER_SMALL(_prop, _event), \ + GB_CONSTANT("_DefaultArrangement", "s", _arr) + +#define DESCRIBE_MULTI_CONTAINER(_prop, _event) \ + GB_CONSTANT("_IsMultiContainer", "b", TRUE), \ + DESCRIBE_CONTAINER(_prop, _event, _arr) + +#define DESCRIBE_MULTI_CONTAINER_ARR(_prop, _event, _arr) \ + GB_CONSTANT("_IsMultiContainer", "b", TRUE), \ + DESCRIBE_CONTAINER_ARR(_prop, _event, _arr) + +#define CONTROL_DESCRIPTION \ + DESCRIBE_CONTROL(CCONTROL_PROPERTIES, "MouseDown", "16,16"), \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "Form") \ + +#define CONTAINER_DESCRIPTION \ + GB_CONSTANT("_IsContainer", "b", TRUE), \ + GB_CONSTANT("_Group", "s", "Container") \ + +#define SIMILAR(_similar) GB_CONSTANT("_Similar", "s", _similar) + +#define BUTTON_DESCRIPTION DESCRIBE_CONTROL(CBUTTON_PROPERTIES, "Click", "16,4") +#define CHECKBOX_DESCRIPTION DESCRIBE_CONTROL(CCHECKBOX_PROPERTIES, "Click", "16,4"), SIMILAR("Button") +#define DIAL_DESCRIPTION DESCRIBE_CONTROL(CDIAL_PROPERTIES, "Change", "6,6"), SIMILAR("Slider") +#define DRAWINGAREA_DESCRIPTION DESCRIBE_CONTAINER(CDRAWINGAREA_PROPERTIES, "Draw") +#define EDITOR_DESCRIPTION DESCRIBE_CONTROL(CEDITOR_PROPERTIES, "KeyPress", "16,16"), SIMILAR("TextArea") +#define EMBEDDER_DESCRIPTION DESCRIBE_CONTROL("*", "Embed", "16,16"), GB_CONSTANT("_Group", "s", "Deprecated") +#define FRAME_DESCRIPTION DESCRIBE_CONTAINER(CFRAME_PROPERTIES, "MouseDown"), SIMILAR("Panel") +#define HBOX_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CHBOX_PROPERTIES, "MouseDown", "H"), SIMILAR("Panel") +#define HPANEL_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CHBOX_PROPERTIES, "MouseDown", "R"), SIMILAR("Panel") +#define LABEL_DESCRIPTION DESCRIBE_CONTROL(CLABEL_PROPERTIES, "MouseDown", "16,4") +#define LCDNUMBER_DESCRIPTION DESCRIBE_CONTROL(CLCDNUMBER_PROPERTIES, "MouseDown", "24,6"), SIMILAR("Label"), GB_CONSTANT("_Group", "s", "Deprecated") +#define LISTBOX_DESCRIPTION DESCRIBE_CONTROL(CLISTBOX_PROPERTIES, "Click", "16,16") +#define MOVIEBOX_DESCRIPTION DESCRIBE_CONTROL(CMOVIEBOX_PROPERTIES, "MouseDown", "16,16") +#define PANEL_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CPANEL_PROPERTIES, "MouseDown", "F") +#define RADIOBUTTON_DESCRIPTION DESCRIBE_CONTROL(CRADIOBUTTON_PROPERTIES, "Click", "16,4"), SIMILAR("Button") +#define SCROLLBAR_DESCRIPTION DESCRIBE_CONTROL(CSCROLLBAR_PROPERTIES, "Change", "16,4"), SIMILAR("Slider") +#define SCROLLVIEW_DESCRIPTION DESCRIBE_CONTAINER_ARR(CSCROLLVIEW_PROPERTIES, "MouseDown", "F"), SIMILAR("Panel") +#define SEPARATOR_DESCRIPTION DESCRIBE_CONTROL(CSEPARATOR_PROPERTIES, "MouseDown", "1,4") +#define SLIDER_DESCRIPTION DESCRIBE_CONTROL(CSLIDER_PROPERTIES, "Change", "16,4") +#define SPINBOX_DESCRIPTION DESCRIBE_CONTROL(CSPINBOX_PROPERTIES, "Change", "9,4"), SIMILAR("TextBox,Slider") +#define TABSTRIP_DESCRIPTION DESCRIBE_MULTI_CONTAINER_ARR(CTABSTRIP_PROPERTIES, "Click", "F") +#define TEXTAREA_DESCRIPTION DESCRIBE_CONTROL(CTEXTAREA_PROPERTIES, "KeyPress", "16,16"), SIMILAR("TextBox") +#define TEXTBOX_DESCRIPTION DESCRIBE_CONTROL(CTEXTBOX_PROPERTIES, "KeyPress", "16,4") +#define TEXTEDIT_DESCRIPTION DESCRIBE_CONTROL(CTEXTEDIT_PROPERTIES, "Change", "16,16"), SIMILAR("TextArea") +#define TEXTLABEL_DESCRIPTION DESCRIBE_CONTROL(CTEXTLABEL_PROPERTIES, "MouseDown", "16,4"), SIMILAR("Label") +#define TOGGLEBUTTON_DESCRIPTION DESCRIBE_CONTROL(CTOGGLEBUTTON_PROPERTIES, "Click", "16,4"), SIMILAR("Button") +#define TOOLBUTTON_DESCRIPTION DESCRIBE_CONTROL(CTOOLBUTTON_PROPERTIES, "Click", "4,4"), SIMILAR("Button") +#define USERCONTAINER_DESCRIPTION DESCRIBE_CONTAINER_ARR(CUSERCONTAINER_PROPERTIES, "MouseDown", "F") +#define VBOX_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CVBOX_PROPERTIES, "MouseDown", "V"), SIMILAR("Panel") +#define VPANEL_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CVBOX_PROPERTIES, "MouseDown", "C"), SIMILAR("Panel") +#define WINDOW_DESCRIPTION DESCRIBE_CONTAINER_ARR(CWINDOW_PROPERTIES, "Open", "F") + +#define MENU_DESCRIPTION \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_IsContainer", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "Form"), \ + GB_CONSTANT("_Properties", "s", CMENU_PROPERTIES), \ + GB_CONSTANT("_DefaultEvent", "s", "Click") + +#define FORM_DESCRIPTION \ + GB_CONSTANT("_IsForm", "b", TRUE), \ + GB_CONSTANT("_HiddenControls", "s", "Form,Control,Menu,Container,UserControl,UserContainer,Window") + +#define TRAYICON_DESCRIPTION \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "*"), \ + GB_CONSTANT("_IsVirtual", "b", TRUE), \ + GB_CONSTANT("_Group", "s", "Special"), \ + GB_CONSTANT("_DefaultEvent", "s", "Click"), \ + GB_CONSTANT("_Properties", "s", CTRAYICON_PROPERTIES) + +#define PRINTER_DESCRIPTION \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "*"), \ + GB_CONSTANT("_IsVirtual", "b", TRUE), \ + GB_CONSTANT("_Group", "s", "Special"), \ + GB_CONSTANT("_Properties", "s", CPRINTER_PROPERTIES), \ + GB_CONSTANT("_DefaultEvent", "s", "Draw") + +#define USERCONTROL_DESCRIPTION \ + DESCRIBE_CONTROL(CUSERCONTROL_PROPERTIES, "MouseDown", "16,16"), \ + GB_CONSTANT("_Group", "s", ""), \ + GB_CONSTANT("_IsContainer", "b", FALSE) + + +#endif + + diff --git a/gb.qt4/share/gb.form.trayicon.h b/gb.qt4/share/gb.form.trayicon.h new file mode 100644 index 00000000..7a6493ad --- /dev/null +++ b/gb.qt4/share/gb.form.trayicon.h @@ -0,0 +1,615 @@ +/*************************************************************************** + + gb.form.trayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define DEFAULT_TRAYICON_WIDTH 24 +#define DEFAULT_TRAYICON_HEIGHT 24 + +#ifdef QT_INTERFACE_VERSION + +static const unsigned char _default_trayicon_data[] = +{ +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 19, 12, 3, 80, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +33, 23, 9, 54, 35, 28, 9, 180, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 25, 0, 0, 0, 69, +0, 0, 0, 78, 0, 0, 0, 37, +0, 0, 0, 2, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 3, 0, 0, 0, 11, +0, 0, 0, 10, 33, 25, 7, 108, +63, 48, 16, 172, 70, 55, 18, 198, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 4, 0, 0, 0, 61, +3, 3, 3, 196, 35, 35, 35, 225, +49, 49, 49, 230, 7, 7, 7, 202, +0, 0, 0, 102, 0, 0, 0, 9, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 20, 10, 0, 25, +18, 14, 3, 138, 16, 12, 4, 123, +30, 23, 6, 150, 125, 98, 32, 235, +121, 94, 31, 242, 95, 74, 24, 211, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +4, 4, 4, 103, 59, 59, 59, 236, +210, 210, 188, 254, 254, 254, 212, 255, +254, 254, 224, 255, 234, 234, 229, 254, +124, 124, 124, 250, 41, 41, 41, 111, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 1, 45, 30, 7, 34, +78, 61, 19, 104, 114, 89, 29, 216, +84, 65, 21, 232, 123, 95, 31, 238, +159, 123, 41, 248, 193, 151, 50, 254, +179, 139, 46, 254, 91, 71, 22, 179, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 23, +75, 75, 75, 170, 209, 209, 189, 252, +237, 237, 110, 255, 167, 167, 71, 255, +187, 187, 124, 255, 254, 254, 245, 255, +248, 248, 248, 255, 132, 132, 132, 225, +58, 47, 15, 96, 62, 48, 16, 94, +80, 61, 18, 108, 130, 102, 34, 195, +157, 123, 40, 239, 188, 147, 49, 254, +173, 135, 45, 254, 193, 150, 50, 255, +194, 151, 50, 255, 193, 150, 50, 255, +147, 115, 38, 245, 74, 58, 16, 92, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 32, 29, 21, 70, +152, 151, 148, 222, 250, 250, 139, 255, +141, 141, 31, 255, 20, 20, 7, 255, +147, 147, 141, 255, 254, 254, 254, 255, +255, 255, 255, 255, 226, 226, 226, 255, +138, 108, 37, 239, 168, 130, 43, 238, +171, 133, 44, 243, 189, 148, 49, 254, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 188, 146, 49, 255, +120, 95, 31, 220, 54, 43, 10, 47, +0, 0, 0, 0, 36, 24, 0, 21, +82, 63, 20, 136, 130, 105, 49, 239, +193, 187, 172, 254, 255, 255, 85, 255, +48, 48, 10, 255, 1, 1, 1, 255, +144, 144, 144, 255, 224, 224, 221, 255, +253, 253, 247, 255, 254, 254, 254, 255, +165, 136, 67, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +193, 151, 50, 255, 159, 123, 40, 250, +92, 69, 22, 146, 18, 0, 0, 14, +30, 30, 0, 17, 94, 72, 24, 138, +166, 129, 43, 241, 184, 147, 58, 254, +204, 193, 167, 255, 255, 255, 97, 255, +72, 72, 15, 255, 0, 0, 0, 255, +22, 22, 22, 255, 126, 126, 111, 255, +251, 251, 238, 255, 251, 251, 251, 255, +186, 149, 62, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 194, 151, 51, 255, +180, 140, 46, 255, 113, 87, 28, 212, +46, 34, 8, 60, 0, 0, 0, 0, +66, 50, 16, 61, 139, 107, 35, 227, +192, 149, 50, 254, 192, 150, 51, 255, +199, 179, 130, 255, 244, 244, 164, 255, +189, 189, 68, 255, 70, 70, 54, 255, +41, 41, 27, 255, 194, 194, 157, 255, +250, 248, 232, 255, 217, 202, 165, 255, +194, 152, 52, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 185, 144, 48, 255, +151, 117, 39, 253, 55, 41, 12, 124, +0, 0, 0, 5, 0, 0, 0, 0, +92, 72, 24, 176, 187, 146, 49, 254, +195, 152, 51, 255, 195, 152, 51, 255, +187, 148, 57, 255, 213, 198, 162, 255, +244, 244, 222, 255, 252, 252, 229, 255, +247, 242, 166, 255, 231, 213, 154, 255, +212, 181, 109, 255, 201, 164, 74, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +188, 147, 49, 255, 157, 122, 40, 251, +80, 63, 20, 186, 0, 0, 0, 3, +0, 0, 0, 0, 0, 0, 0, 0, +104, 81, 26, 200, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +182, 142, 47, 255, 164, 130, 50, 255, +203, 181, 129, 255, 221, 203, 148, 255, +214, 187, 115, 255, 203, 166, 81, 255, +199, 159, 65, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 193, 150, 50, 255, +163, 127, 42, 252, 105, 82, 28, 200, +51, 38, 12, 59, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +107, 82, 26, 209, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +188, 146, 49, 255, 152, 118, 39, 255, +127, 99, 34, 255, 181, 143, 52, 255, +196, 154, 56, 255, 195, 152, 52, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +192, 149, 50, 255, 176, 137, 45, 255, +192, 149, 50, 255, 174, 136, 45, 255, +112, 87, 28, 213, 56, 43, 13, 77, +0, 0, 0, 5, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +107, 85, 27, 216, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 194, 151, 50, 255, +83, 69, 35, 255, 52, 49, 44, 255, +57, 52, 46, 255, 76, 64, 47, 255, +111, 90, 44, 255, 131, 105, 50, 255, +107, 87, 44, 255, 72, 61, 34, 255, +110, 87, 32, 255, 188, 146, 49, 255, +165, 129, 43, 252, 120, 92, 31, 221, +35, 27, 7, 64, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +99, 77, 25, 190, 191, 149, 50, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +144, 115, 50, 255, 50, 51, 114, 255, +42, 48, 181, 255, 43, 49, 185, 255, +45, 49, 135, 255, 48, 52, 142, 255, +54, 58, 159, 255, 64, 61, 91, 255, +158, 124, 47, 255, 160, 124, 41, 251, +110, 86, 29, 201, 63, 49, 14, 88, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +84, 66, 20, 100, 162, 126, 42, 245, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +182, 142, 50, 255, 81, 67, 90, 255, +6, 15, 221, 255, 3, 15, 254, 255, +6, 17, 239, 255, 7, 18, 236, 255, +14, 24, 221, 255, 88, 74, 84, 255, +153, 120, 41, 255, 81, 63, 20, 185, +52, 38, 14, 53, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +49, 35, 7, 36, 118, 92, 30, 194, +185, 143, 48, 252, 194, 151, 50, 255, +195, 152, 51, 255, 195, 152, 51, 255, +194, 151, 50, 255, 180, 141, 48, 255, +90, 74, 77, 255, 15, 21, 194, 255, +6, 16, 221, 255, 12, 19, 212, 255, +59, 51, 96, 251, 116, 90, 31, 226, +68, 52, 17, 150, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 2, 47, 35, 7, 64, +121, 94, 31, 205, 192, 149, 50, 254, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +192, 149, 50, 255, 125, 100, 57, 255, +103, 84, 83, 255, 73, 59, 58, 240, +98, 75, 25, 168, 52, 39, 13, 78, +21, 21, 0, 12, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 3, +49, 35, 8, 57, 108, 84, 27, 203, +166, 130, 43, 246, 194, 151, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +188, 146, 49, 255, 134, 105, 35, 237, +98, 78, 26, 196, 76, 59, 19, 90, +31, 21, 0, 24, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 9, +45, 36, 12, 85, 64, 50, 16, 142, +75, 59, 18, 151, 75, 58, 18, 149, +51, 39, 11, 115, 0, 0, 0, 22, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +}; + +#else + +static const unsigned char _default_trayicon_data[] = +{ +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 3, 12, 19, 80, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +9, 23, 33, 54, 9, 28, 35, 180, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 25, 0, 0, 0, 69, +0, 0, 0, 78, 0, 0, 0, 37, +0, 0, 0, 2, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 3, 0, 0, 0, 11, +0, 0, 0, 10, 7, 25, 33, 108, +16, 48, 63, 172, 18, 55, 70, 198, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 4, 0, 0, 0, 61, +3, 3, 3, 196, 35, 35, 35, 225, +49, 49, 49, 230, 7, 7, 7, 202, +0, 0, 0, 102, 0, 0, 0, 9, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 10, 20, 25, +3, 14, 18, 138, 4, 12, 16, 123, +6, 23, 30, 150, 32, 98, 125, 235, +31, 94, 121, 242, 24, 74, 95, 211, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +4, 4, 4, 103, 59, 59, 59, 236, +188, 210, 210, 254, 212, 254, 254, 255, +224, 254, 254, 255, 229, 234, 234, 254, +124, 124, 124, 250, 41, 41, 41, 111, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 1, 7, 30, 45, 34, +19, 61, 78, 104, 29, 89, 114, 216, +21, 65, 84, 232, 31, 95, 123, 238, +41, 123, 159, 248, 50, 151, 193, 254, +46, 139, 179, 254, 22, 71, 91, 179, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 23, +75, 75, 75, 170, 189, 209, 209, 252, +110, 237, 237, 255, 71, 167, 167, 255, +124, 187, 187, 255, 245, 254, 254, 255, +248, 248, 248, 255, 132, 132, 132, 225, +15, 47, 58, 96, 16, 48, 62, 94, +18, 61, 80, 108, 34, 102, 130, 195, +40, 123, 157, 239, 49, 147, 188, 254, +45, 135, 173, 254, 50, 150, 193, 255, +50, 151, 194, 255, 50, 150, 193, 255, +38, 115, 147, 245, 16, 58, 74, 92, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 21, 29, 32, 70, +148, 151, 152, 222, 139, 250, 250, 255, +31, 141, 141, 255, 7, 20, 20, 255, +141, 147, 147, 255, 254, 254, 254, 255, +255, 255, 255, 255, 226, 226, 226, 255, +37, 108, 138, 239, 43, 130, 168, 238, +44, 133, 171, 243, 49, 148, 189, 254, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 49, 146, 188, 255, +31, 95, 120, 220, 10, 43, 54, 47, +0, 0, 0, 0, 0, 24, 36, 21, +20, 63, 82, 136, 49, 105, 130, 239, +172, 187, 193, 254, 85, 255, 255, 255, +10, 48, 48, 255, 1, 1, 1, 255, +144, 144, 144, 255, 221, 224, 224, 255, +247, 253, 253, 255, 254, 254, 254, 255, +67, 136, 165, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 151, 193, 255, 40, 123, 159, 250, +22, 69, 92, 146, 0, 0, 18, 14, +0, 30, 30, 17, 24, 72, 94, 138, +43, 129, 166, 241, 58, 147, 184, 254, +167, 193, 204, 255, 97, 255, 255, 255, +15, 72, 72, 255, 0, 0, 0, 255, +22, 22, 22, 255, 111, 126, 126, 255, +238, 251, 251, 255, 251, 251, 251, 255, +62, 149, 186, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 151, 194, 255, +46, 140, 180, 255, 28, 87, 113, 212, +8, 34, 46, 60, 0, 0, 0, 0, +16, 50, 66, 61, 35, 107, 139, 227, +50, 149, 192, 254, 51, 150, 192, 255, +130, 179, 199, 255, 164, 244, 244, 255, +68, 189, 189, 255, 54, 70, 70, 255, +27, 41, 41, 255, 157, 194, 194, 255, +232, 248, 250, 255, 165, 202, 217, 255, +52, 152, 194, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 48, 144, 185, 255, +39, 117, 151, 253, 12, 41, 55, 124, +0, 0, 0, 5, 0, 0, 0, 0, +24, 72, 92, 176, 49, 146, 187, 254, +51, 152, 195, 255, 51, 152, 195, 255, +57, 148, 187, 255, 162, 198, 213, 255, +222, 244, 244, 255, 229, 252, 252, 255, +166, 242, 247, 255, 154, 213, 231, 255, +109, 181, 212, 255, 74, 164, 201, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +49, 147, 188, 255, 40, 122, 157, 251, +20, 63, 80, 186, 0, 0, 0, 3, +0, 0, 0, 0, 0, 0, 0, 0, +26, 81, 104, 200, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +47, 142, 182, 255, 50, 130, 164, 255, +129, 181, 203, 255, 148, 203, 221, 255, +115, 187, 214, 255, 81, 166, 203, 255, +65, 159, 199, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 50, 150, 193, 255, +42, 127, 163, 252, 28, 82, 105, 200, +12, 38, 51, 59, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +26, 82, 107, 209, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +49, 146, 188, 255, 39, 118, 152, 255, +34, 99, 127, 255, 52, 143, 181, 255, +56, 154, 196, 255, 52, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 149, 192, 255, 45, 137, 176, 255, +50, 149, 192, 255, 45, 136, 174, 255, +28, 87, 112, 213, 13, 43, 56, 77, +0, 0, 0, 5, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +27, 85, 107, 216, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 50, 151, 194, 255, +35, 69, 83, 255, 44, 49, 52, 255, +46, 52, 57, 255, 47, 64, 76, 255, +44, 90, 111, 255, 50, 105, 131, 255, +44, 87, 107, 255, 34, 61, 72, 255, +32, 87, 110, 255, 49, 146, 188, 255, +43, 129, 165, 252, 31, 92, 120, 221, +7, 27, 35, 64, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +25, 77, 99, 190, 50, 149, 191, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 115, 144, 255, 114, 51, 50, 255, +181, 48, 42, 255, 185, 49, 43, 255, +135, 49, 45, 255, 142, 52, 48, 255, +159, 58, 54, 255, 91, 61, 64, 255, +47, 124, 158, 255, 41, 124, 160, 251, +29, 86, 110, 201, 14, 49, 63, 88, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +20, 66, 84, 100, 42, 126, 162, 245, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 142, 182, 255, 90, 67, 81, 255, +221, 15, 6, 255, 254, 15, 3, 255, +239, 17, 6, 255, 236, 18, 7, 255, +221, 24, 14, 255, 84, 74, 88, 255, +41, 120, 153, 255, 20, 63, 81, 185, +14, 38, 52, 53, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +7, 35, 49, 36, 30, 92, 118, 194, +48, 143, 185, 252, 50, 151, 194, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 151, 194, 255, 48, 141, 180, 255, +77, 74, 90, 255, 194, 21, 15, 255, +221, 16, 6, 255, 212, 19, 12, 255, +96, 51, 59, 251, 31, 90, 116, 226, +17, 52, 68, 150, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 2, 7, 35, 47, 64, +31, 94, 121, 205, 50, 149, 192, 254, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 149, 192, 255, 57, 100, 125, 255, +83, 84, 103, 255, 58, 59, 73, 240, +25, 75, 98, 168, 13, 39, 52, 78, +0, 21, 21, 12, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 3, +8, 35, 49, 57, 27, 84, 108, 203, +43, 130, 166, 246, 51, 151, 194, 255, +51, 152, 195, 255, 51, 152, 195, 255, +49, 146, 188, 255, 35, 105, 134, 237, +26, 78, 98, 196, 19, 59, 76, 90, +0, 21, 31, 24, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 9, +12, 36, 45, 85, 16, 50, 64, 142, +18, 59, 75, 151, 18, 58, 75, 149, +11, 39, 51, 115, 0, 0, 0, 22, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +}; + +#endif diff --git a/gb.qt4/share/gb.form.trayicon.large.h b/gb.qt4/share/gb.form.trayicon.large.h new file mode 100644 index 00000000..5695a608 --- /dev/null +++ b/gb.qt4/share/gb.form.trayicon.large.h @@ -0,0 +1,263 @@ +/*************************************************************************** + + gb.form.trayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define DEFAULT_TRAYICON_WIDTH 64 +#define DEFAULT_TRAYICON_HEIGHT 64 + +#ifdef QT_INTERFACE_VERSION + +static const unsigned char _default_trayicon_data[] = +{ +137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, +0, 0, 0, 64, 0, 0, 0, 64, 8, 6, 0, 0, 0, 170, 105, 113, +222, 0, 0, 14, 35, 73, 68, 65, 84, 120, 218, 237, 154, 107, 120, 85, +213, 153, 199, 127, 107, 173, 125, 206, 201, 61, 64, 2, 134, 91, 128, 128, +220, 68, 228, 18, 20, 42, 80, 91, 25, 69, 199, 94, 102, 106, 199, 62, +79, 109, 71, 208, 90, 113, 170, 117, 70, 234, 76, 189, 128, 143, 90, 160, +80, 43, 106, 103, 28, 28, 177, 66, 109, 173, 118, 112, 218, 194, 192, 168, +8, 142, 40, 96, 212, 138, 160, 32, 23, 73, 8, 152, 144, 144, 144, 251, +57, 103, 159, 189, 247, 122, 231, 67, 78, 32, 4, 144, 36, 196, 118, 90, +242, 255, 144, 147, 103, 159, 179, 215, 218, 255, 247, 254, 190, 107, 67, 55, +186, 209, 141, 110, 116, 163, 27, 221, 56, 39, 144, 157, 125, 78, 211, 87, +128, 65, 169, 111, 158, 171, 2, 8, 3, 15, 2, 114, 46, 91, 193, 126, +101, 28, 105, 101, 17, 231, 142, 249, 103, 142, 152, 16, 9, 247, 236, 35, +105, 131, 70, 74, 74, 94, 126, 79, 64, 211, 242, 231, 47, 28, 67, 209, +218, 52, 126, 188, 125, 195, 208, 239, 62, 100, 197, 247, 8, 226, 177, 244, +150, 47, 157, 46, 222, 76, 3, 6, 240, 90, 93, 115, 146, 126, 23, 252, +81, 105, 107, 237, 40, 165, 95, 149, 192, 175, 196, 218, 175, 139, 181, 159, +203, 28, 89, 136, 210, 26, 16, 213, 250, 129, 187, 2, 45, 130, 156, 9, +20, 1, 135, 129, 163, 64, 5, 176, 3, 184, 62, 249, 125, 168, 203, 35, +187, 49, 10, 99, 218, 250, 180, 163, 148, 126, 62, 103, 242, 149, 211, 209, +250, 43, 202, 56, 59, 70, 207, 91, 41, 214, 79, 224, 71, 27, 208, 78, +168, 169, 171, 181, 62, 27, 112, 147, 154, 182, 90, 107, 107, 140, 177, 198, +24, 171, 148, 178, 201, 235, 2, 252, 168, 235, 168, 171, 102, 97, 134, 195, +131, 209, 218, 73, 94, 171, 5, 80, 218, 60, 154, 81, 48, 70, 46, 94, +185, 77, 148, 113, 108, 206, 37, 87, 74, 225, 83, 91, 165, 240, 201, 45, +98, 210, 179, 100, 240, 13, 247, 154, 179, 13, 130, 173, 45, 103, 3, 32, +161, 80, 232, 24, 209, 11, 47, 188, 80, 102, 206, 156, 41, 83, 166, 76, +105, 33, 46, 225, 112, 184, 229, 251, 93, 93, 96, 112, 202, 164, 103, 78, +75, 174, 183, 223, 164, 164, 245, 5, 130, 212, 126, 5, 162, 180, 249, 126, +36, 183, 175, 76, 90, 94, 36, 227, 30, 94, 35, 105, 249, 195, 101, 210, +242, 34, 153, 248, 196, 235, 114, 209, 195, 255, 45, 104, 35, 167, 35, 210, +17, 88, 224, 60, 224, 45, 173, 245, 23, 0, 110, 188, 241, 70, 85, 92, +92, 140, 136, 176, 125, 251, 118, 214, 173, 91, 199, 230, 205, 155, 17, 17, +234, 235, 235, 89, 177, 98, 133, 106, 86, 88, 120, 4, 80, 214, 73, 173, +135, 1, 148, 97, 181, 14, 165, 188, 126, 254, 237, 143, 160, 195, 17, 9, +226, 209, 178, 113, 75, 255, 71, 185, 149, 7, 69, 71, 82, 151, 142, 93, +252, 123, 108, 34, 78, 237, 246, 45, 140, 158, 183, 18, 155, 136, 163, 140, +193, 111, 56, 26, 96, 131, 61, 109, 171, 163, 206, 104, 223, 2, 18, 137, +68, 36, 35, 35, 67, 237, 222, 189, 155, 156, 156, 28, 130, 32, 192, 24, +67, 77, 242, 135, 61, 91, 164, 37, 2, 34, 104, 173, 185, 245, 214, 91, +121, 242, 201, 39, 9, 130, 224, 101, 224, 234, 118, 6, 71, 3, 4, 78, +70, 246, 223, 248, 141, 117, 191, 26, 116, 253, 93, 161, 190, 127, 61, 203, +188, 59, 103, 58, 226, 39, 184, 232, 39, 107, 64, 44, 149, 27, 87, 73, +222, 204, 235, 149, 77, 184, 205, 228, 156, 16, 226, 55, 199, 227, 160, 169, +158, 234, 173, 47, 37, 14, 254, 230, 177, 69, 192, 252, 214, 11, 119, 180, +156, 124, 28, 56, 228, 56, 206, 156, 17, 35, 70, 168, 146, 146, 18, 76, +56, 140, 163, 53, 139, 181, 102, 25, 176, 89, 132, 29, 192, 7, 74, 17, +3, 250, 43, 133, 163, 20, 34, 194, 53, 215, 92, 67, 78, 78, 14, 47, +189, 244, 210, 80, 17, 249, 55, 224, 211, 2, 82, 8, 176, 161, 172, 94, +35, 172, 27, 123, 45, 163, 224, 194, 219, 70, 207, 91, 105, 210, 7, 143, +212, 54, 225, 226, 55, 213, 202, 200, 127, 89, 166, 36, 225, 130, 210, 164, +15, 30, 173, 196, 182, 146, 167, 181, 199, 254, 141, 31, 62, 64, 197, 171, +47, 104, 175, 161, 230, 30, 196, 150, 39, 149, 216, 41, 11, 232, 13, 84, +2, 34, 34, 202, 179, 150, 168, 214, 204, 0, 6, 4, 1, 125, 140, 33, +7, 200, 20, 161, 64, 41, 28, 224, 50, 32, 167, 205, 34, 115, 230, 204, +97, 217, 178, 101, 149, 34, 50, 160, 85, 144, 84, 40, 101, 16, 113, 1, +156, 244, 172, 175, 249, 77, 245, 243, 82, 251, 21, 140, 29, 114, 211, 124, +63, 125, 200, 5, 142, 141, 71, 65, 117, 236, 177, 173, 239, 225, 86, 148, +178, 251, 225, 219, 152, 240, 228, 155, 250, 237, 235, 199, 210, 82, 18, 171, +206, 165, 88, 29, 47, 46, 46, 142, 12, 24, 48, 0, 173, 53, 195, 129, +188, 68, 130, 212, 170, 42, 222, 190, 226, 10, 106, 62, 252, 176, 57, 84, +165, 167, 227, 53, 54, 18, 156, 198, 212, 148, 82, 86, 105, 51, 199, 201, +200, 222, 134, 54, 141, 226, 123, 189, 253, 104, 195, 120, 108, 112, 37, 48, +51, 103, 202, 85, 244, 187, 102, 182, 159, 58, 96, 168, 227, 55, 213, 163, +180, 233, 84, 232, 136, 149, 237, 71, 124, 223, 238, 90, 112, 227, 251, 192, +132, 83, 229, 239, 118, 115, 79, 79, 79, 207, 204, 206, 206, 142, 228, 231, +231, 227, 1, 223, 0, 156, 32, 32, 81, 95, 207, 166, 254, 253, 209, 161, +227, 169, 254, 209, 37, 75, 112, 93, 151, 72, 36, 114, 202, 197, 54, 110, +220, 168, 191, 250, 79, 247, 63, 161, 64, 219, 120, 20, 39, 35, 155, 180, +65, 35, 252, 236, 11, 38, 75, 230, 200, 137, 226, 213, 86, 41, 17, 113, +130, 88, 83, 167, 201, 75, 224, 97, 227, 81, 202, 86, 63, 29, 0, 115, +91, 197, 176, 142, 7, 193, 148, 188, 65, 17, 255, 200, 193, 223, 110, 217, +250, 214, 204, 194, 194, 194, 99, 129, 174, 31, 176, 47, 63, 31, 175, 188, +28, 241, 125, 84, 210, 68, 109, 43, 31, 60, 21, 130, 32, 32, 111, 250, +151, 25, 244, 247, 119, 163, 148, 66, 2, 31, 9, 2, 208, 186, 211, 132, +219, 34, 122, 104, 31, 38, 28, 97, 231, 67, 179, 176, 9, 87, 37, 57, +75, 7, 44, 64, 105, 16, 11, 220, 225, 30, 249, 228, 145, 200, 128, 17, +254, 136, 225, 195, 9, 128, 117, 64, 96, 45, 137, 67, 135, 72, 28, 60, +120, 60, 100, 27, 195, 140, 25, 51, 206, 28, 218, 141, 129, 163, 101, 205, +228, 173, 5, 165, 81, 78, 215, 181, 39, 65, 60, 138, 248, 9, 106, 119, +22, 121, 54, 225, 62, 210, 98, 20, 29, 170, 3, 116, 74, 74, 14, 16, +239, 61, 237, 203, 139, 38, 253, 252, 29, 156, 30, 125, 156, 244, 244, 52, +44, 176, 22, 8, 249, 62, 53, 203, 151, 183, 245, 109, 66, 161, 19, 171, +94, 145, 230, 125, 253, 54, 235, 79, 159, 52, 17, 249, 44, 58, 116, 165, +112, 43, 15, 161, 67, 17, 202, 215, 173, 12, 153, 212, 140, 5, 167, 178, +120, 231, 12, 202, 159, 96, 93, 247, 221, 11, 23, 174, 146, 80, 86, 47, +21, 52, 213, 227, 228, 228, 161, 181, 198, 7, 74, 0, 35, 66, 188, 184, +184, 57, 50, 183, 144, 244, 125, 86, 175, 94, 125, 194, 82, 245, 245, 245, +248, 217, 217, 100, 182, 217, 162, 223, 144, 161, 148, 32, 93, 222, 156, 187, +149, 135, 64, 44, 209, 3, 251, 252, 32, 214, 180, 10, 168, 59, 83, 73, +219, 22, 83, 117, 40, 252, 238, 164, 229, 69, 132, 123, 244, 86, 202, 56, +160, 20, 61, 47, 154, 138, 239, 121, 40, 96, 32, 96, 149, 34, 52, 112, +32, 173, 213, 40, 34, 40, 165, 80, 74, 49, 99, 198, 12, 242, 7, 13, +226, 162, 139, 47, 38, 230, 251, 39, 141, 99, 106, 83, 115, 81, 93, 108, +1, 18, 4, 4, 177, 70, 148, 19, 162, 244, 215, 63, 117, 116, 36, 245, +166, 211, 197, 59, 125, 170, 220, 164, 156, 80, 190, 114, 66, 155, 10, 255, +99, 11, 54, 17, 59, 33, 239, 246, 186, 224, 98, 98, 241, 56, 74, 132, +43, 128, 132, 49, 244, 248, 214, 183, 78, 126, 136, 164, 64, 54, 110, 220, +200, 193, 210, 82, 174, 186, 238, 58, 68, 228, 164, 13, 247, 230, 140, 234, +242, 41, 85, 236, 208, 62, 16, 161, 106, 243, 218, 192, 38, 220, 7, 172, +27, 107, 60, 221, 38, 167, 179, 60, 153, 240, 179, 13, 40, 231, 228, 238, +213, 104, 205, 195, 227, 83, 153, 90, 56, 30, 128, 12, 96, 16, 176, 175, +111, 95, 188, 35, 71, 154, 163, 120, 27, 95, 68, 132, 18, 17, 194, 64, +94, 155, 77, 39, 45, 123, 163, 11, 233, 43, 226, 21, 7, 177, 110, 19, +202, 56, 236, 124, 104, 54, 218, 9, 25, 235, 185, 182, 61, 93, 93, 11, +110, 239, 253, 249, 191, 77, 156, 174, 218, 10, 108, 192, 173, 47, 22, 29, +11, 104, 151, 39, 77, 110, 204, 166, 77, 39, 147, 111, 54, 5, 230, 44, +89, 194, 209, 68, 2, 221, 138, 188, 0, 207, 188, 246, 46, 182, 11, 53, +111, 221, 40, 214, 109, 2, 20, 123, 150, 222, 97, 17, 59, 226, 211, 200, +183, 21, 128, 74, 106, 236, 209, 130, 155, 230, 135, 149, 113, 78, 43, 229, +212, 130, 177, 124, 176, 191, 20, 7, 248, 29, 224, 27, 195, 63, 14, 27, +134, 136, 32, 34, 60, 255, 252, 243, 199, 165, 185, 114, 37, 215, 222, 113, +7, 38, 28, 38, 23, 136, 121, 150, 239, 252, 126, 59, 147, 150, 189, 193, +111, 203, 33, 172, 187, 38, 252, 137, 181, 196, 43, 74, 1, 104, 216, 253, +30, 126, 67, 237, 122, 96, 79, 123, 154, 155, 22, 83, 117, 148, 19, 154, +154, 61, 106, 210, 43, 5, 55, 63, 232, 156, 202, 252, 91, 35, 110, 225, +237, 111, 143, 35, 146, 150, 78, 141, 82, 39, 213, 250, 27, 173, 165, 88, +105, 122, 168, 230, 98, 41, 167, 46, 198, 63, 252, 231, 54, 92, 63, 224, +159, 167, 13, 99, 123, 69, 61, 107, 247, 86, 118, 89, 10, 140, 30, 248, +40, 233, 110, 150, 93, 11, 111, 70, 135, 34, 218, 122, 238, 25, 87, 119, +90, 153, 170, 47, 94, 98, 118, 223, 47, 205, 22, 218, 81, 133, 165, 106, +152, 250, 243, 34, 222, 190, 249, 115, 244, 116, 34, 208, 74, 147, 141, 192, +102, 173, 201, 21, 33, 132, 226, 199, 191, 40, 162, 162, 209, 101, 241, 21, +163, 112, 180, 226, 135, 235, 63, 194, 11, 164, 163, 61, 205, 105, 243, 125, +236, 147, 253, 199, 2, 245, 174, 133, 55, 11, 208, 179, 61, 228, 79, 21, +4, 155, 38, 63, 183, 51, 213, 171, 171, 86, 237, 190, 89, 107, 158, 189, +172, 55, 195, 207, 63, 31, 17, 33, 80, 138, 120, 82, 178, 171, 62, 40, +99, 233, 27, 31, 243, 249, 33, 185, 124, 119, 98, 62, 63, 120, 101, 23, +165, 117, 49, 140, 234, 162, 172, 175, 20, 110, 85, 25, 65, 83, 61, 74, +107, 74, 158, 89, 64, 244, 147, 253, 55, 32, 118, 69, 71, 250, 251, 150, +79, 113, 50, 122, 200, 248, 199, 215, 99, 221, 88, 251, 42, 100, 154, 7, +29, 94, 96, 233, 31, 14, 184, 103, 84, 152, 75, 47, 189, 20, 128, 111, +255, 215, 251, 20, 29, 170, 225, 233, 175, 92, 196, 235, 7, 170, 89, 181, +243, 112, 151, 31, 201, 248, 13, 53, 36, 106, 42, 65, 105, 170, 223, 92, +67, 229, 107, 47, 174, 75, 14, 89, 58, 60, 205, 85, 225, 158, 125, 242, +76, 106, 6, 226, 37, 218, 19, 113, 168, 121, 103, 3, 251, 159, 154, 79, +225, 83, 91, 113, 172, 203, 17, 27, 102, 206, 14, 48, 239, 188, 66, 40, +100, 176, 38, 204, 226, 191, 26, 197, 220, 151, 119, 17, 243, 131, 179, 215, +186, 200, 9, 245, 72, 16, 109, 108, 38, 15, 196, 14, 237, 181, 149, 175, +189, 88, 213, 81, 242, 173, 179, 128, 178, 158, 155, 231, 100, 246, 192, 182, +71, 0, 74, 211, 235, 146, 43, 232, 123, 205, 108, 254, 48, 103, 58, 65, +172, 17, 27, 248, 132, 9, 48, 41, 169, 88, 19, 38, 53, 100, 146, 190, +110, 59, 79, 94, 4, 9, 124, 156, 204, 30, 196, 43, 15, 30, 75, 160, +65, 60, 138, 123, 228, 16, 98, 45, 137, 186, 163, 65, 201, 138, 133, 137, +228, 140, 178, 195, 48, 199, 102, 28, 161, 200, 128, 112, 175, 243, 190, 211, +171, 240, 242, 246, 181, 162, 98, 201, 26, 53, 137, 158, 227, 47, 99, 199, +15, 191, 70, 195, 158, 247, 200, 26, 49, 129, 72, 159, 129, 4, 209, 6, +188, 32, 32, 228, 116, 98, 250, 44, 130, 245, 61, 76, 106, 58, 126, 83, +29, 165, 191, 252, 9, 38, 53, 131, 180, 254, 67, 147, 185, 62, 142, 91, +113, 160, 57, 222, 196, 99, 193, 190, 199, 239, 52, 201, 209, 89, 167, 164, +220, 114, 147, 113, 210, 179, 38, 165, 13, 30, 189, 101, 216, 156, 5, 156, +41, 5, 158, 40, 135, 128, 112, 175, 243, 40, 250, 198, 168, 99, 69, 77, +175, 241, 211, 201, 186, 96, 50, 125, 46, 191, 14, 29, 73, 33, 112, 163, +4, 209, 198, 100, 87, 173, 154, 77, 185, 197, 42, 146, 3, 83, 65, 8, +101, 246, 196, 38, 226, 148, 254, 114, 9, 94, 99, 29, 57, 147, 103, 210, +171, 112, 6, 65, 188, 9, 68, 8, 220, 24, 110, 69, 41, 98, 3, 108, +194, 245, 247, 60, 242, 125, 71, 135, 194, 97, 235, 123, 1, 34, 157, 170, +169, 156, 227, 86, 109, 170, 131, 104, 67, 135, 200, 39, 239, 67, 188, 4, +22, 184, 115, 238, 92, 42, 43, 43, 57, 92, 81, 193, 129, 45, 171, 40, +122, 118, 113, 243, 143, 178, 114, 233, 49, 108, 12, 41, 61, 123, 19, 238, +145, 75, 36, 183, 63, 225, 156, 60, 172, 151, 192, 107, 168, 33, 94, 94, +140, 91, 117, 152, 35, 239, 108, 4, 183, 121, 70, 122, 201, 179, 59, 240, +234, 171, 155, 201, 35, 4, 241, 38, 220, 202, 131, 136, 181, 248, 77, 245, +222, 190, 159, 221, 21, 50, 225, 212, 112, 224, 197, 109, 103, 201, 183, 22, +128, 152, 140, 236, 74, 191, 161, 6, 229, 132, 17, 63, 209, 161, 69, 188, +250, 163, 228, 92, 114, 37, 219, 183, 239, 32, 127, 64, 127, 10, 6, 13, +98, 252, 152, 49, 28, 168, 60, 66, 93, 125, 45, 229, 101, 135, 137, 186, +181, 12, 176, 154, 216, 193, 18, 106, 222, 127, 153, 192, 6, 84, 214, 215, +147, 149, 145, 193, 176, 252, 193, 100, 244, 200, 34, 229, 75, 87, 209, 16, +141, 177, 205, 244, 37, 136, 30, 159, 1, 250, 141, 245, 184, 213, 229, 128, +194, 173, 42, 75, 20, 47, 127, 64, 0, 29, 120, 113, 131, 200, 89, 157, +57, 182, 245, 27, 153, 252, 220, 78, 188, 186, 234, 78, 69, 232, 119, 111, +153, 198, 13, 179, 102, 83, 252, 214, 102, 174, 110, 168, 101, 218, 121, 189, +25, 156, 149, 69, 86, 74, 132, 114, 55, 193, 75, 229, 21, 172, 193, 161, +247, 184, 241, 76, 222, 253, 1, 19, 243, 242, 40, 119, 93, 118, 85, 215, +176, 91, 41, 182, 226, 80, 242, 254, 31, 24, 245, 227, 213, 152, 244, 44, +208, 6, 175, 166, 2, 175, 174, 26, 29, 142, 112, 244, 237, 87, 253, 242, +181, 43, 170, 128, 190, 45, 103, 5, 103, 223, 62, 157, 136, 213, 99, 30, +120, 110, 102, 164, 119, 63, 167, 57, 207, 119, 164, 7, 247, 41, 251, 245, +35, 220, 80, 180, 134, 133, 61, 211, 112, 125, 31, 227, 56, 205, 46, 229, +56, 136, 49, 248, 198, 144, 146, 158, 206, 156, 226, 50, 158, 24, 210, 143, +132, 19, 66, 180, 131, 53, 154, 212, 212, 20, 42, 140, 195, 192, 125, 181, +140, 187, 247, 233, 99, 163, 236, 32, 218, 128, 73, 203, 224, 208, 11, 143, +83, 187, 99, 243, 155, 192, 212, 182, 115, 189, 46, 18, 128, 114, 148, 214, +151, 101, 143, 155, 182, 182, 96, 246, 188, 80, 71, 99, 65, 114, 142, 207, +91, 179, 10, 113, 211, 5, 130, 128, 112, 235, 103, 148, 227, 159, 113, 129, +251, 173, 98, 81, 68, 33, 201, 1, 168, 104, 197, 224, 170, 56, 189, 126, +186, 22, 147, 150, 73, 236, 147, 253, 216, 68, 12, 29, 78, 97, 239, 99, +115, 9, 98, 13, 11, 108, 194, 189, 167, 171, 39, 103, 186, 181, 14, 197, +6, 235, 107, 223, 251, 223, 80, 168, 71, 110, 167, 22, 243, 26, 106, 24, +183, 232, 69, 114, 234, 124, 194, 234, 196, 151, 4, 104, 153, 199, 106, 136, +24, 88, 20, 22, 176, 22, 229, 251, 4, 9, 151, 245, 110, 64, 195, 23, +175, 197, 164, 103, 17, 61, 176, 27, 177, 1, 126, 83, 131, 191, 243, 193, +27, 16, 235, 207, 192, 113, 238, 251, 12, 38, 135, 167, 60, 175, 168, 243, +142, 86, 126, 33, 107, 228, 68, 167, 163, 163, 105, 165, 52, 40, 77, 230, +196, 203, 184, 115, 253, 239, 248, 65, 138, 66, 147, 236, 147, 212, 169, 253, +78, 146, 247, 77, 242, 83, 24, 252, 189, 37, 68, 75, 119, 99, 210, 50, +168, 126, 115, 13, 7, 159, 95, 90, 9, 100, 90, 63, 81, 38, 137, 132, +255, 89, 8, 224, 180, 19, 161, 137, 255, 190, 233, 164, 242, 179, 221, 241, +192, 79, 224, 86, 29, 230, 195, 251, 191, 201, 91, 25, 134, 139, 85, 128, +109, 101, 4, 199, 136, 3, 49, 20, 185, 65, 6, 195, 31, 248, 21, 238, +209, 10, 172, 27, 183, 123, 150, 222, 161, 149, 214, 255, 106, 189, 196, 247, +186, 210, 223, 219, 39, 0, 173, 181, 137, 164, 14, 69, 233, 61, 227, 31, +123, 229, 216, 233, 106, 199, 7, 147, 62, 202, 9, 241, 209, 221, 215, 146, +93, 125, 152, 13, 25, 138, 225, 173, 234, 31, 207, 10, 155, 98, 240, 213, +252, 209, 12, 154, 117, 31, 54, 17, 167, 234, 141, 53, 84, 190, 182, 234, +168, 147, 150, 117, 190, 31, 107, 104, 64, 196, 227, 51, 198, 167, 168, 87, +221, 150, 58, 160, 224, 177, 11, 230, 255, 226, 212, 163, 174, 14, 180, 172, +241, 178, 98, 138, 151, 221, 139, 91, 87, 133, 177, 22, 147, 158, 69, 250, +176, 177, 156, 127, 203, 2, 106, 247, 110, 195, 61, 82, 198, 254, 167, 230, +163, 157, 240, 66, 235, 39, 238, 110, 123, 124, 245, 39, 18, 0, 128, 122, +34, 181, 255, 144, 91, 70, 223, 183, 242, 236, 172, 80, 4, 17, 139, 14, +69, 80, 74, 97, 61, 151, 68, 205, 17, 226, 229, 37, 124, 188, 236, 30, +36, 8, 182, 73, 224, 143, 79, 22, 102, 62, 127, 68, 156, 217, 193, 149, +158, 171, 180, 90, 50, 110, 233, 203, 232, 112, 132, 179, 154, 97, 41, 5, +214, 82, 183, 99, 51, 197, 207, 44, 32, 136, 55, 150, 25, 39, 92, 16, +120, 174, 127, 182, 21, 221, 103, 39, 128, 230, 7, 31, 136, 72, 241, 192, +175, 223, 110, 250, 204, 248, 187, 164, 49, 72, 199, 136, 11, 84, 108, 248, +13, 135, 94, 120, 20, 180, 222, 171, 148, 30, 43, 214, 186, 136, 253, 147, +190, 186, 170, 58, 70, 66, 158, 0, 110, 25, 60, 235, 94, 114, 167, 92, +213, 124, 160, 249, 169, 198, 163, 169, 251, 96, 43, 37, 43, 23, 225, 213, +85, 129, 200, 131, 192, 188, 227, 149, 147, 3, 190, 207, 159, 135, 0, 142, +119, 127, 142, 216, 224, 71, 74, 235, 187, 76, 90, 166, 205, 157, 114, 53, +105, 67, 70, 161, 67, 17, 252, 198, 122, 154, 74, 63, 162, 110, 199, 102, +188, 218, 42, 157, 204, 32, 139, 129, 251, 77, 36, 45, 22, 184, 81, 254, +191, 161, 211, 115, 170, 80, 86, 47, 229, 213, 215, 100, 128, 140, 6, 46, +1, 210, 104, 126, 65, 114, 27, 176, 95, 135, 34, 113, 235, 185, 137, 214, +167, 67, 127, 137, 80, 167, 113, 151, 115, 234, 77, 236, 110, 116, 163, 27, +221, 232, 70, 55, 254, 76, 241, 127, 245, 103, 83, 89, 108, 29, 88, 9, +0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 +}; + +#endif diff --git a/gb.qt4/src/CButton.cpp b/gb.qt4/src/CButton.cpp new file mode 100644 index 00000000..317ce08c --- /dev/null +++ b/gb.qt4/src/CButton.cpp @@ -0,0 +1,685 @@ +/*************************************************************************** + + CButton.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CBUTTON_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +//Added by qt3to4: +#include +#include +#include +#include + +#include "gambas.h" + +#include "CWidget.h" +#include "CPicture.h" +#include "CWindow.h" +#include "CButton.h" + +/*#define DEBUG_CBUTTON*/ + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_ClickToggle); +DECLARE_EVENT(EVENT_ClickTool); + +//--------------------------------------------------------------------------- + +static void set_button_text(CBUTTON *_object, const QString &text) +{ + WIDGET->setText(text); + WIDGET->calcMinimumSize(); +} + +static void set_button_picture(CBUTTON *_object) +{ + QPixmap p; + QIcon icon; + + if (THIS->picture) + { + p = *(THIS->picture->pixmap); + CWIDGET_iconset(icon, p); + WIDGET->setIcon(icon); + WIDGET->setIconSize(p.size()); + } + else + { + WIDGET->setIcon(icon); + } + + WIDGET->calcMinimumSize(); +} + +static void set_tool_button(CBUTTON *_object, bool set_text, QString text) +{ + QPixmap p; + QIcon icon; + + if (!set_text) + text = WIDGET_TOOL->text(); + + if (THIS->picture) + { + p = *(THIS->picture->pixmap); + + WIDGET_TOOL->setText(text); + CWIDGET_iconset(icon, p); + + WIDGET_TOOL->setIcon(icon); + WIDGET->setIconSize(p.size()); + //WIDGET_TOOL->setUsesTextLabel(qtext.length() > 0); + if (text.length()) + WIDGET_TOOL->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + else + WIDGET_TOOL->setToolButtonStyle(Qt::ToolButtonIconOnly); + } + else + { + WIDGET_TOOL->setIcon(icon); + WIDGET_TOOL->setText(text); + WIDGET_TOOL->setToolButtonStyle(Qt::ToolButtonTextOnly); + //WIDGET_TOOL->setUsesTextLabel(qtext.length() > 0); + } + + WIDGET->calcMinimumSize(); +} + +//--------------------------------------------------------------------------- + + +BEGIN_METHOD(CBUTTON_new, GB_OBJECT parent) + + MyPushButton *wid = new MyPushButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(clicked()), &CButton::manager, SLOT(clicked())); + + wid->setAutoDefault(false); + + CWIDGET_new(wid, (void *)_object); + + // We assume that the button widget destructor will always be called before + // its gambas window is released. + WIDGET->top = CWidget::getWindow((CWIDGET *)THIS); + +END_METHOD + + +BEGIN_METHOD(CTOGGLEBUTTON_new, GB_OBJECT parent) + + QPushButton *wid = new MyPushButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(toggled(bool)), &CButton::manager, SLOT(clickedToggle())); + + //wid->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + wid->setAutoDefault(false); + wid->setCheckable(TRUE); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CTOOLBUTTON_new, GB_OBJECT parent) + + MyToolButton *wid = new MyToolButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(clicked()), &CButton::manager, SLOT(clickedTool())); + + //wid->setToggleButton(TRUE); + wid->setAutoRaise(true); + + CWIDGET_new(wid, (void *)_object); + wid->calcMinimumSize(); + +END_METHOD + + +BEGIN_METHOD_VOID(CBUTTON_free) + + CLEAR_PICTURE(&(OBJECT(CBUTTON)->picture)); + +END_METHOD + +BEGIN_PROPERTY(CBUTTON_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + set_button_text(THIS, QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->picture))); + set_button_picture(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + set_tool_button(THIS, true, QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->picture))); + set_tool_button(THIS, false, QString()); + } + +END_PROPERTY + +/* +BEGIN_PROPERTY(CTOOLBUTTON_picture) + + QToolButton *wid = QTOOLBUTTON(_object); + char **pict = (char **)&(OBJECT(CBUTTON)->picture); + + if (READ_PROPERTY) + GB.ReturnString(*pict, 0); + else + wid->setIconSet(QIconSet(*PIXMAP_set_widget(PROPERTY(GB_STRING), pict), QIconSet::Small)); + +END_PROPERTY +*/ + +BEGIN_PROPERTY(CBUTTON_value) + + if (READ_PROPERTY) + GB.ReturnBoolean(0); + else if (VPROP(GB_BOOLEAN)) + WIDGET->animateClick(); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOGGLEBUTTON_value) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isChecked()); + else + WIDGET->setChecked(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_value) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_TOOL->isChecked()); + else + { + if (WIDGET->isCheckable()) + WIDGET_TOOL->setChecked(VPROP(GB_BOOLEAN)); + else + WIDGET->animateClick(); + //qApp->postEvent(WIDGET_TOOL, new QEvent(QEvent::Leave)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_flat) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isFlat()); + else + WIDGET->setFlat(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WIDGET->isFlat()); + else + WIDGET->setFlat(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WIDGET_TOOL->autoRaise()); + else + WIDGET_TOOL->setAutoRaise(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_TOOL->isCheckable()); + else + { + WIDGET_TOOL->setCheckable(VPROP(GB_BOOLEAN)); + QObject::disconnect(WIDGET_TOOL, 0, &CButton::manager, 0); + if (VPROP(GB_BOOLEAN)) + QObject::connect(WIDGET_TOOL, SIGNAL(toggled(bool)), &CButton::manager, SLOT(clickedTool())); + else + QObject::connect(WIDGET_TOOL, SIGNAL(clicked()), &CButton::manager, SLOT(clickedTool())); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_default) + + CWINDOW *top = CWidget::getWindow((CWIDGET *)THIS); + + if (READ_PROPERTY) + GB.ReturnBoolean(top->defaultButton == WIDGET); + else + CWINDOW_set_default_button(top, WIDGET, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_cancel) + + CWINDOW *top = CWidget::getWindow((CWIDGET *)THIS); + + if (READ_PROPERTY) + GB.ReturnBoolean(top->cancelButton == WIDGET); + else + CWINDOW_set_cancel_button(top, WIDGET, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->radio); + else + THIS->radio = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_autoresize) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->autoresize); + else if (THIS->autoresize != VPROP(GB_BOOLEAN)) + { + THIS->autoresize = VPROP(GB_BOOLEAN); + WIDGET->calcMinimumSize(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTOOLBUTTON_autoresize) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->autoresize); + else if (THIS->autoresize != VPROP(GB_BOOLEAN)) + { + THIS->autoresize = VPROP(GB_BOOLEAN); + WIDGET_TOOL->calcMinimumSize(); + } + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(CBUTTON_stretch) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->stretch); + else if (THIS->stretch != VPROP(GB_BOOLEAN)) + { + THIS->stretch = VPROP(GB_BOOLEAN); + set_button(THIS, NULL, true); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTOOLBUTTON_stretch) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->stretch); + else if (THIS->stretch != VPROP(GB_BOOLEAN)) + { + THIS->stretch = VPROP(GB_BOOLEAN); + set_tool_button(THIS, NULL, true); + } + +END_PROPERTY +#endif + +#define CTOGGLEBUTTON_free CBUTTON_free +#define CTOGGLEBUTTON_text CBUTTON_text +#define CTOGGLEBUTTON_picture CBUTTON_picture +#define CTOGGLEBUTTON_border CBUTTON_border +#define CTOGGLEBUTTON_radio CBUTTON_radio +#define CTOGGLEBUTTON_stretch CBUTTON_stretch + +#define CTOOLBUTTON_free CBUTTON_free +#define CTOOLBUTTON_radio CBUTTON_radio + +GB_DESC CButtonDesc[] = +{ + GB_DECLARE("Button", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CBUTTON_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CBUTTON_free, NULL), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Default", "b", CBUTTON_default), + GB_PROPERTY("Cancel", "b", CBUTTON_cancel), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + BUTTON_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + + GB_END_DECLARE +}; + +GB_DESC CToggleButtonDesc[] = +{ + GB_DECLARE("ToggleButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTOGGLEBUTTON_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CTOGGLEBUTTON_free, NULL), + + GB_PROPERTY("Text", "s", CTOGGLEBUTTON_text), + GB_PROPERTY("Caption", "s", CTOGGLEBUTTON_text), + GB_PROPERTY("Picture", "Picture", CTOGGLEBUTTON_picture), + GB_PROPERTY("Value", "b", CTOGGLEBUTTON_value), + //GB_PROPERTY("Flat", "b", CBUTTON_flat), + GB_PROPERTY("Border", "b", CTOGGLEBUTTON_border), + GB_PROPERTY("Radio", "b", CTOGGLEBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + TOGGLEBUTTON_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_ClickToggle), + + GB_END_DECLARE +}; + + +GB_DESC CToolButtonDesc[] = +{ + GB_DECLARE("ToolButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTOOLBUTTON_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CTOOLBUTTON_free, NULL), + + GB_PROPERTY("Text", "s", CTOOLBUTTON_text), + GB_PROPERTY("Caption", "s", CTOOLBUTTON_text), + GB_PROPERTY("Picture", "Picture", CTOOLBUTTON_picture), + GB_PROPERTY("Value", "b", CTOOLBUTTON_value), + GB_PROPERTY("Toggle", "b", CTOOLBUTTON_toggle), + GB_PROPERTY("Border", "b", CTOOLBUTTON_border), + GB_PROPERTY("Radio", "b", CTOOLBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CTOOLBUTTON_autoresize), + + TOOLBUTTON_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_ClickTool), + + GB_END_DECLARE +}; + + + +/** class MyPushButton *****************************************************/ + +MyPushButton::MyPushButton(QWidget *parent) : + QPushButton(parent) +{ + calcMinimumSize(); + top = 0; +} + +MyPushButton::~MyPushButton() +{ + if (top) + { + CWINDOW_set_default_button(top, this, false); + CWINDOW_set_cancel_button(top, this, false); + } +} + +/*QSize MyPushButton::sizeHint(void) const +{ + return QSize(width(), height()); +}*/ + +void MyPushButton::changeEvent(QEvent *e) +{ + QPushButton::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + calcMinimumSize(); +} + +void MyPushButton::calcMinimumSize() +{ + CBUTTON *_object = (CBUTTON *)CWidget::getReal(this); + QSize size; + + if (!THIS || CWIDGET_is_design(THIS)) //CWIDGET_test_flag(THIS, WF_DESIGN_LEADER)) + return; + + if (text().length() > 0) + { + QFontMetrics fm = fontMetrics(); + setMinimumHeight(fm.lineSpacing() + 4); + } + else + setMinimumHeight(0); + + setMinimumWidth(0); + if (THIS->autoresize) + { + size = sizeHint(); + CWIDGET_resize(THIS, size.width(), height()); + setMinimumWidth(size.width()); + } + + //qDebug("%p: %s: %d", this, text().latin1(), minimumHeight()); +} + +/*void MyPushButton::resizeEvent(QResizeEvent *e) +{ + set_button((CBUTTON *)CWidget::get(this), NULL, true); +}*/ + +/*void MyPushButton::paintEvent(QPaintEvent *) +{ + CBUTTON *_object = (CBUTTON *)CWidget::get(this); + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + if (THIS->picture) + opt.iconSize = THIS->picture->pixmap->size(); + p.drawComplexControl(QStyle::CC_PushButton, opt); +}*/ + + +/** class MyToolButton *****************************************************/ + +MyToolButton::MyToolButton(QWidget *parent) : + QToolButton(parent) +{ +} + +MyToolButton::~MyToolButton() +{ +} + +void MyToolButton::changeEvent(QEvent *e) +{ + QToolButton::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + calcMinimumSize(); +} + +void MyToolButton::calcMinimumSize() +{ + CBUTTON *_object = (CBUTTON *)CWidget::get(this); + QSize size; + + if (!THIS || CWIDGET_is_design(THIS)) + return; + + if (text().length() > 0) + { + QFontMetrics fm = fontMetrics(); + setMinimumHeight(fm.lineSpacing() + 4); + } + else + setMinimumHeight(0); + + setMinimumWidth(0); + if (THIS->autoresize) + { + size = sizeHint(); + CWIDGET_resize(THIS, size.width(), height()); + setMinimumWidth(size.width()); + } +} + +/*void MyToolButton::resizeEvent(QResizeEvent *e) +{ + //set_tool_button((CBUTTON *)CWidget::get(this), NULL, true); +}*/ + +/*void MyToolButton::paintEvent(QPaintEvent *) +{ + CBUTTON *_object = (CBUTTON *)CWidget::get(this); + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + if (THIS->picture) + opt.iconSize = THIS->picture->pixmap->size(); + p.drawComplexControl(QStyle::CC_ToolButton, opt); +}*/ + + +/* Class CButton */ + +CButton CButton::manager; + +void CButton::onlyMe(CBUTTON *_object) +{ + QWidget *parent = WIDGET->parentWidget(); + QObjectList list = parent->children(); + int i; + QObject *o; + CBUTTON *other; + + for (i = 0; i < list.count(); i++) + { + o = list.at(i); + if (!o->isWidgetType()) + continue; + other = (CBUTTON *)CWidget::get(o); + if (other == THIS) + continue; + if (other->widget.ob.klass != THIS->widget.ob.klass) + continue; + if (!other->radio) + continue; + o->blockSignals(true); + /*if (qobject_cast(o)) + ((MyPushButton *)o)->setChecked(false); + else + ((MyToolButton *)o)->setChecked(false);*/ + (qobject_cast(o))->setChecked(false); + o->blockSignals(false); + } +} + +void CButton::clicked(void) +{ + RAISE_EVENT_ACTION(EVENT_Click); +} + +void CButton::clickedToggle(void) +{ + GET_SENDER(); + + if (THIS->radio) + { + if (WIDGET->isChecked()) + onlyMe(THIS); + else + { + WIDGET->setChecked(true); + return; + } + } + + RAISE_EVENT_ACTION(EVENT_ClickToggle); +} + +void CButton::clickedTool(void) +{ + GET_SENDER(); + + if (THIS->radio) + { + if (WIDGET_TOOL->isChecked()) + onlyMe(THIS); + else + { + WIDGET_TOOL->setChecked(true); + return; + } + } + + RAISE_EVENT_ACTION(EVENT_ClickTool); +} diff --git a/gb.qt4/src/CButton.h b/gb.qt4/src/CButton.h new file mode 100644 index 00000000..859ec37d --- /dev/null +++ b/gb.qt4/src/CButton.h @@ -0,0 +1,114 @@ +/*************************************************************************** + + CButton.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CBUTTON_H +#define __CBUTTON_H + +#include +#include +#include +#include +//Added by qt3to4: +#include + +#include "gambas.h" +#include "CWidget.h" +#include "CPicture.h" +#include "CWindow.h" + +#ifndef __CBUTTON_CPP +extern GB_DESC CButtonDesc[]; +extern GB_DESC CToggleButtonDesc[]; +extern GB_DESC CToolButtonDesc[]; +#else + +#define QBUTTON(object) ((QButton *)((CWIDGET *)object)->widget) +#define QPUSHBUTTON(object) ((MyPushButton *)((CWIDGET *)object)->widget) +#define QTOOLBUTTON(object) ((MyToolButton *)((CWIDGET *)object)->widget) + +#define THIS OBJECT(CBUTTON) +#define WIDGET QPUSHBUTTON(_object) +#define WIDGET_TOOL QTOOLBUTTON(_object) + +#endif + +typedef + struct { + CWIDGET widget; + CPICTURE *picture; + unsigned radio : 1; + unsigned autoresize : 1; + } + CBUTTON; + +class MyPushButton : public QPushButton +{ + Q_OBJECT + +public: + + explicit MyPushButton(QWidget *parent); + ~MyPushButton(); + virtual void changeEvent(QEvent *e); + void calcMinimumSize(); + //virtual void resizeEvent(QResizeEvent *e); + //virtual void paintEvent(QPaintEvent *e); + + CWINDOW *top; +}; + + +class MyToolButton : public QToolButton +{ + Q_OBJECT + +public: + + explicit MyToolButton(QWidget *parent); + ~MyToolButton(); + virtual void changeEvent(QEvent *e); + void calcMinimumSize(); + //virtual void resizeEvent(QResizeEvent *e); + //virtual void paintEvent(QPaintEvent *e); +}; + + +class CButton : public QObject +{ + Q_OBJECT + +public: + + static CButton manager; + + static void onlyMe(CBUTTON *); + +public slots: + + void clicked(void); + void clickedToggle(void); + void clickedTool(void); +}; + + +#endif diff --git a/gb.qt4/src/CCheckBox.cpp b/gb.qt4/src/CCheckBox.cpp new file mode 100644 index 00000000..032cb193 --- /dev/null +++ b/gb.qt4/src/CCheckBox.cpp @@ -0,0 +1,197 @@ +/*************************************************************************** + + CCheckBox.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCHECKBOX_CPP + +#include "gambas.h" + +#include + +#include "CStyle.h" +#include "CCheckBox.h" + +//------------------------------------------------------------------------- + +MyCheckBox::MyCheckBox(QWidget *parent) : QCheckBox(parent) +{ + _autoResize = false; +} + + +void MyCheckBox::changeEvent(QEvent *e) +{ + QCheckBox::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + adjust(); +} + +void MyCheckBox::adjust(bool force) +{ + void *_object = CWidget::getReal(this); + bool a; + QSize hint; + + if (!THIS || (!_autoResize && !force) || CWIDGET_is_design(THIS) || text().length() <= 0) + return; + + a = _autoResize; + _autoResize = false; + hint = sizeHint(); + CWIDGET_auto_resize(THIS, hint.width(), qMax(hint.height(), height())); + _autoResize = a; +} + +void MyCheckBox::resizeEvent(QResizeEvent *e) +{ + QCheckBox::resizeEvent(e); + + if (_autoResize && e->oldSize().width() != e->size().width()) + adjust(); +} + + +//------------------------------------------------------------------------- + +DECLARE_EVENT(EVENT_Click); + + +BEGIN_METHOD(CCHECKBOX_new, GB_OBJECT parent) + + QCheckBox *wid = new MyCheckBox(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(stateChanged(int)), &CCheckBox::manager, SLOT(clicked())); + + wid->setMinimumHeight(wid->sizeHint().height()); + + CWIDGET_new(wid, (void *)_object); + THIS->widget.flag.fillBackground = true; //CSTYLE_fix_breeze; + +END_METHOD + + +BEGIN_PROPERTY(CheckBox_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + { + WIDGET->setText(QSTRING_PROP()); + WIDGET->adjust(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CheckBox_Value) + + /*if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isChecked()); + else + WIDGET->setChecked(VPROP(GB_BOOLEAN));*/ + + if (READ_PROPERTY) + { + switch(WIDGET->checkState()) + { + case Qt::Unchecked: GB.ReturnInteger(0); break; + case Qt::Checked: GB.ReturnInteger(-1); break; + case Qt::PartiallyChecked: GB.ReturnInteger(1); break; + } + } + else + { + if (WIDGET->isTristate() && VPROP(GB_INTEGER) == 1) + WIDGET->setCheckState(Qt::PartiallyChecked); + else + WIDGET->setCheckState(VPROP(GB_INTEGER) == 0 ? Qt::Unchecked : Qt::Checked); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CheckBox_TriState) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isTristate()); + else + WIDGET->setTristate(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CheckBox_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isAutoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CheckBox_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->widget.flag.inverted); + else + CWIDGET_set_inverted(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CCheckBoxDesc[] = +{ + GB_DECLARE("CheckBox", sizeof(CCHECKBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CCHECKBOX_new, "(Parent)Container;"), + + GB_CONSTANT("False", "i", 0), + GB_CONSTANT("True", "i", -1), + GB_CONSTANT("None", "i", 1), + + GB_PROPERTY("Text", "s", CheckBox_Text), + GB_PROPERTY("Caption", "s", CheckBox_Text), + GB_PROPERTY("Value", "i", CheckBox_Value), + GB_PROPERTY("Tristate", "b", CheckBox_TriState), + GB_PROPERTY("AutoResize", "b", CheckBox_AutoResize), + GB_PROPERTY("Invert", "b", CheckBox_Invert), + + CHECKBOX_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + + GB_END_DECLARE +}; + + +//------------------------------------------------------------------------- + +CCheckBox CCheckBox::manager; + +void CCheckBox::clicked(void) +{ + RAISE_EVENT_ACTION(EVENT_Click); +} + + diff --git a/gb.qt4/src/CCheckBox.h b/gb.qt4/src/CCheckBox.h new file mode 100644 index 00000000..3f4b5542 --- /dev/null +++ b/gb.qt4/src/CCheckBox.h @@ -0,0 +1,83 @@ +/*************************************************************************** + + CCheckBox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCHECKBOX_H +#define __CCHECKBOX_H + +#include + +#include "gambas.h" + +#include "CWidget.h" + +#ifndef __CCHECKBOX_CPP +extern GB_DESC CCheckBoxDesc[]; +#else + +#define THIS ((CCHECKBOX *)_object) +#define WIDGET ((MyCheckBox *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + } + CCHECKBOX; + +class MyCheckBox : public QCheckBox +{ + Q_OBJECT + +public: + + explicit MyCheckBox(QWidget *parent); + void adjust(bool force = false); + bool isAutoResize() const { return _autoResize; } + void setAutoResize(bool a) { _autoResize = a; adjust(); } + +protected: + + virtual void changeEvent(QEvent *); + virtual void resizeEvent(QResizeEvent *); + +private: + + unsigned _autoResize : 1; +}; + +class CCheckBox : public QObject +{ + Q_OBJECT + +public: + + static CCheckBox manager; + +public slots: + + void clicked(void); + +}; + +#endif diff --git a/gb.qt4/src/CClipboard.cpp b/gb.qt4/src/CClipboard.cpp new file mode 100644 index 00000000..dd973828 --- /dev/null +++ b/gb.qt4/src/CClipboard.cpp @@ -0,0 +1,922 @@ +/*************************************************************************** + + CClipboard.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCLIPBOARD_CPP + +#include "gambas.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb.form.const.h" +#include "CWidget.h" +#include "CImage.h" +#include "CClipboard.h" + +CDRAG_INFO CDRAG_info = { 0 }; +bool CDRAG_dragging = false; +void *CDRAG_destination = NULL; +static char CDRAG_action = DRAG_MOVE; + +static CPICTURE *_picture = 0; +static int _picture_x = -1; +static int _picture_y = -1; + +enum { MIME_UNKNOWN, MIME_TEXT, MIME_IMAGE }; +enum { CLIPBOARD_DEFAULT, CLIPBOARD_SELECTION }; + +static int _current_clipboard = CLIPBOARD_DEFAULT; + +static int get_type(const QMimeData *src) +{ + if (src->hasImage()) + return MIME_IMAGE; + else if (src->formats().indexOf(QRegExp("text/.*")) >= 0) + return MIME_TEXT; + else + return MIME_UNKNOWN; +} + +static QString get_format(const QMimeData *src, int i = 0, bool charset = false) +{ + QStringList formats = src->formats(); + QString format; + + if (i >= 0 && i < formats.count()) + { + format = formats.at(i); + + if (!charset) + { + int pos = format.indexOf(';'); + if (pos >= 0) + format = format.left(pos); + } + } + + return format; +} + +static void get_formats(const QMimeData *src, GB_ARRAY array) +{ + int i, j; + QStringList formats = src->formats(); + QString fmt; + + for (i = 0; i < formats.count(); i++) + { + fmt = get_format(src, i, true); + if (!fmt[0].isLower()) + continue; + for (j = 0; j < GB.Array.Count(array); j++) + { + if (strcasecmp(fmt.toUtf8().data(), *((char **)GB.Array.Get(array, j))) == 0) + break; + } + if (j < GB.Array.Count(array)) + continue; + //fmt = get_format(src, i); + + *((char **)GB.Array.Add(array)) = GB.NewZeroString(fmt.toUtf8().data()); + } +} + +static QString get_first_format(const QMimeData *src) +{ + int i; + QString format; + + for (i = 0;; i++) + { + format = get_format(src, i); + if (format.length() && !format[0].isLower()) + continue; + break; + } + + return format; +} + +static bool paste(const QMimeData *data, const char *fmt) +{ + QString format; + QByteArray ba; + int type; +#if QT5 +#else + QTextCodec *codec = NULL; +#endif + + if (fmt) + format = fmt; + else + format = get_first_format(data); + + if (!data->hasFormat(format)) + { + GB.ReturnVariant(NULL); + return TRUE; + } + + if (format.startsWith("text/")) + type = MIME_TEXT; + else + type = get_type(data); + + switch(type) + { + case MIME_TEXT: + + ba = data->data(format); + + if (ba.size()) + { +#if QT5 + GB.ReturnNewString(ba.constData(), ba.size()); +#else + if (((uchar)ba[0] == 0xFE && (uchar)ba[1] == 0xFF) || ((uchar)ba[0] == 0xFF && (uchar)ba[1] == 0xFE)) + codec = QTextCodec::codecForUtfText(ba, NULL); + + if (codec) + RETURN_NEW_STRING(codec->toUnicode(ba)); + else + GB.ReturnNewString(ba.constData(), ba.size()); +#endif + } + else + GB.ReturnNull(); + break; + + case MIME_IMAGE: + { + QImage *image = new QImage(); + *image = qvariant_cast(data->imageData()); + *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); + GB.ReturnObject(CIMAGE_create(image)); + } + break; + + default: + GB.ReturnNull(); + } + + GB.ReturnConvVariant(); + return FALSE; +} + + + +/*************************************************************************** + + Clipboard + +***************************************************************************/ + +static GB_ARRAY _clipboard_formats[2] = { NULL }; +static bool _clipboard_has_changed[2] = { TRUE }; + +#define CURRENT_MODE() (_current_clipboard == CLIPBOARD_SELECTION ? QClipboard::Selection : QClipboard::Clipboard) + +void CLIPBOARD_has_changed(QClipboard::Mode mode) +{ + int clipboard = mode == QClipboard::Selection ? CLIPBOARD_SELECTION : CLIPBOARD_DEFAULT; + GB.Unref(POINTER(&_clipboard_formats[clipboard])); + _clipboard_formats[clipboard] = NULL; + _clipboard_has_changed[clipboard] = TRUE; +} + +static GB_ARRAY load_clipboard_formats() +{ + if (!_clipboard_formats[_current_clipboard]) + { + //qDebug("load clipboard formats"); + GB.Array.New(&_clipboard_formats[_current_clipboard], GB_T_STRING, 0); + get_formats(QApplication::clipboard()->mimeData(CURRENT_MODE()), _clipboard_formats[_current_clipboard]); + GB.Ref(_clipboard_formats[_current_clipboard]); + } + + return _clipboard_formats[_current_clipboard]; +} + +static int get_clipboard_type() +{ + int i; + QString format; + GB_ARRAY formats; + + formats = load_clipboard_formats(); + + for (i = 0; i < GB.Array.Count(formats); i++) + { + format = *((char **)GB.Array.Get(formats, i)); + if (format.startsWith("text/")) + return MIME_TEXT; + else if (format.startsWith("image/")) + return MIME_IMAGE; + else if (format == "application/x-qt-image") + return MIME_IMAGE; + } + + return MIME_UNKNOWN; +} + +BEGIN_METHOD_VOID(Clipboard_exit) + + CLIPBOARD_has_changed(QClipboard::Clipboard); + CLIPBOARD_has_changed(QClipboard::Selection); + +END_METHOD + + +BEGIN_METHOD_VOID(Clipboard_Clear) + + QApplication::clipboard()->clear(CURRENT_MODE()); + +END_METHOD + + +BEGIN_PROPERTY(Clipboard_Format) + + GB_ARRAY formats = load_clipboard_formats(); + + if (GB.Array.Count(formats) == 0) + GB.ReturnVoidString(); + else + GB.ReturnString(*((char **)GB.Array.Get(formats, 0))); + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_Formats) + + GB.ReturnObject(load_clipboard_formats()); + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_Type) + + GB.ReturnInteger(get_clipboard_type()); + +END_PROPERTY + + +BEGIN_METHOD(Clipboard_Copy, GB_VARIANT data; GB_STRING format) + + QString format; + QMimeData *data = new QMimeData(); + + if (VARG(data).type == GB_T_STRING) + { + if (MISSING(format)) + format = "text/plain"; + else + { + format = TO_QSTRING(GB.ToZeroString(ARG(format))); + if (format.left(5) != "text/") + goto _BAD_FORMAT; + if (format.length() == 5) + goto _BAD_FORMAT; + } + + data->setData(format, QByteArray(VARG(data).value._string, GB.StringLength(VARG(data).value._string))); + QApplication::clipboard()->setMimeData(data, CURRENT_MODE()); + } + else if (VARG(data).type >= GB_T_OBJECT && GB.Is(VARG(data).value._object, CLASS_Image)) + { + QImage img; + + if (!MISSING(format)) + goto _BAD_FORMAT; + + img = *CIMAGE_get((CIMAGE *)VARG(data).value._object); + img.detach(); + + QApplication::clipboard()->setImage(img, CURRENT_MODE()); + } + else + goto _BAD_FORMAT; + + return; + +_BAD_FORMAT: + + GB.Error("Bad clipboard format"); + +END_METHOD + + +BEGIN_METHOD(Clipboard_Paste, GB_STRING format) + + if (!paste(QApplication::clipboard()->mimeData(CURRENT_MODE()), MISSING(format) ? NULL : GB.ToZeroString(ARG(format)))) + _clipboard_has_changed[_current_clipboard] = FALSE; + +END_METHOD + +BEGIN_PROPERTY(Clipboard_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(_current_clipboard); + else + { + int val = VPROP(GB_INTEGER); + if (val != CLIPBOARD_DEFAULT && val != CLIPBOARD_SELECTION) + GB.Error(GB_ERR_ARG); + else + _current_clipboard = val; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_HasChanged) + + GB.ReturnBoolean(_clipboard_has_changed[_current_clipboard]); + +END_PROPERTY + +GB_DESC CClipboardDesc[] = +{ + GB_DECLARE_STATIC("Clipboard"), + + GB_STATIC_METHOD("_exit", NULL, Clipboard_exit, NULL), + + GB_CONSTANT("None", "i", 0), + GB_CONSTANT("Text", "i", 1), + GB_CONSTANT("Image", "i", 2), + + GB_CONSTANT("Default", "i", 0), + GB_CONSTANT("Selection", "i", 1), + + GB_STATIC_METHOD("Clear", NULL, Clipboard_Clear, NULL), + + GB_STATIC_PROPERTY_READ("Format", "s", Clipboard_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Clipboard_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", Clipboard_Type), + GB_STATIC_PROPERTY_READ("HasChanged", "b", Clipboard_HasChanged), + + GB_STATIC_PROPERTY("Current", "i", Clipboard_Current), + + GB_STATIC_METHOD("Copy", NULL, Clipboard_Copy, "(Data)v[(Format)s]"), + GB_STATIC_METHOD("Paste", "v", Clipboard_Paste, "[(Format)s]"), + + GB_END_DECLARE +}; + + +//--------------------------------------------------------------------------- + +static void hide_frame(CWIDGET *control) +{ + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_HideDNDFrame", NULL, NULL); + init = TRUE; + } + + GB.Push(1, GB_T_OBJECT, control); + GB.Call(&func, 1, FALSE); +} + +void CDRAG_hide_frame(CWIDGET *control) +{ + hide_frame(control); +} + +static void show_frame(CWIDGET *control, int x, int y, int w, int h) +{ + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_ShowDNDFrame", NULL, NULL); + init = TRUE; + } + + GB.Push(5, GB_T_OBJECT, control, GB_T_INTEGER, x, GB_T_INTEGER, y, GB_T_INTEGER, w, GB_T_INTEGER, h); + GB.Call(&func, 5, FALSE); +} + + +/** Drag *****************************************************************/ + +void CDRAG_clear(bool valid) +{ + if (valid) + CDRAG_info.valid++; + else + CDRAG_info.valid--; + + if (CDRAG_info.valid == 0) + CLEAR(&CDRAG_info); +} + +static void post_exit_drag(intptr_t param) +{ + CDRAG_dragging = false; +} + +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, GB_STRING *fmt) +{ + QDrag *drag; + QMimeData *mimeData; + QString format; + void *dest; + + if (GB.CheckObject(source)) + return NULL; + + if (CDRAG_dragging) + { + GB.Error("Undergoing drag"); + return NULL; + } + + mimeData = new QMimeData(); + + if (data->type == GB_T_STRING) + { + if (fmt == NULL) + format = "text/plain"; + else + { + format = TO_QSTRING(GB.ToZeroString(fmt)); + if (format.left(5) != "text/") + goto _BAD_FORMAT; + if (format.length() == 5) + goto _BAD_FORMAT; + } + + mimeData->setData(format, QByteArray(data->value._string, GB.StringLength(data->value._string))); + } + else if (data->type >= GB_T_OBJECT && GB.Is(data->value._object, CLASS_Image)) + { + QImage img; + + if (fmt) + goto _BAD_FORMAT; + + img = *CIMAGE_get((CIMAGE *)data->value._object); + img.detach(); + + mimeData->setImageData(img); + } + else + goto _BAD_FORMAT; + + source->flag.dragging = true; + + drag = new QDrag(source->widget); + drag->setMimeData(mimeData); + + if (_picture) + { + drag->setPixmap(*(_picture->pixmap)); + if (_picture_x >= 0 && _picture_y >= 0) + drag->setHotSpot(QPoint(_picture_x, _picture_y)); + } + + CDRAG_dragging = true; + + /*if (CDRAG_destination) + { + fprintf(stderr, "%d: unref destination %p\n", __LINE__, CDRAG_destination); + GB.Unref(POINTER(&CDRAG_destination)); + }*/ + + CDRAG_action = DRAG_MOVE; + + //qDebug("start drag"); + drag->exec(Qt::CopyAction | Qt::MoveAction | Qt::LinkAction); + + source->flag.dragging = false; + //qDebug("end drag"); + + hide_frame(NULL); + GB.Post((GB_CALLBACK)post_exit_drag, 0); + + dest = CDRAG_destination; + if (dest) + { + //fprintf(stderr, "%d: unref destination %p\n", __LINE__, CDRAG_destination); + GB.Unref(POINTER(&CDRAG_destination)); + CDRAG_destination = NULL; + } + + return dest; + +_BAD_FORMAT: + + GB.Error("Bad drag format"); + return NULL; +} + + +static void update_action(QDropEvent *e) +{ + Qt::KeyboardModifiers mod = e->keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier); + + if (mod == Qt::ControlModifier) + { + e->setDropAction(Qt::CopyAction); + CDRAG_action = DRAG_COPY; + } + else if (mod == Qt::ShiftModifier) + { + e->setDropAction(Qt::LinkAction); + CDRAG_action = DRAG_LINK; + } + else + { + e->setDropAction(Qt::MoveAction); + CDRAG_action = DRAG_MOVE; + } +} + + +bool CDRAG_drag_enter(QWidget *w, CWIDGET *control, QDropEvent *e) +{ + bool cancel; + + update_action(e); + + if (!GB.CanRaise(control, EVENT_Drag)) + { + if (GB.CanRaise(control, EVENT_DragMove) || GB.CanRaise(control, EVENT_Drop)) + e->accept(); + else + { + if (qobject_cast(w) || qobject_cast(w)) + return false; + + e->ignore(); + } + return true; + } + + //fprintf(stderr, "CDRAG_drag_enter: %s %s\n", GB.GetClassName(control), control->name); + + CDRAG_clear(true); + CDRAG_info.event = e; + + cancel = GB.Raise(control, EVENT_Drag, 0); + + CDRAG_clear(false); + + if (cancel) + e->ignore(); + else + e->accept(); + return cancel; +} + +#define EXT(_ob) ((CWIDGET_EXT *)((CWIDGET *)_ob)->ext) + +void CDRAG_drag_leave(CWIDGET *control) +{ + //fprintf(stderr, "CDRAG_drag_leave: %s %s\n", GB.GetClassName(control), control->name); + + CDRAG_hide_frame(control); + + GB.Raise(control, EVENT_DragLeave, 0); +} + + +bool CDRAG_drag_move(QWidget *w, CWIDGET *control, QDropEvent *e) +{ + bool cancel; + QPoint p; + + //qDebug("CDRAG_drag_move: (%s %p) %p", GB.GetClassName(control), control, qobject_cast(QWIDGET(control))); + + update_action(e); + + if (!GB.CanRaise(control, EVENT_DragMove)) + return true; + + //fprintf(stderr, "CDRAG_drag_move: %s %s\n", GB.GetClassName(control), control->name); + + CDRAG_clear(true); + CDRAG_info.event = e; + + p = e->pos(); + p = w->mapTo(QWIDGET(control), p); + CDRAG_info.x = p.x(); + CDRAG_info.y = p.y(); + + cancel = GB.Raise(control, EVENT_DragMove, 0); + if (cancel) + e->ignore(); + else + e->accept(); + + CDRAG_clear(false); + + return cancel; +} + +bool CDRAG_drag_drop(QWidget *w, CWIDGET *control, QDropEvent *e) +{ + QPoint p; + + //hide_frame(); + + if (!GB.CanRaise(control, EVENT_Drop)) + return false; + + // Hack for QScrollView + /*if (CWIDGET_test_flag(control, WF_SCROLLVIEW) && qobject_cast(QWIDGET(control))) + ((MyListView *)QWIDGET(control))->contentsDropEvent((QDragMoveEvent *)e);*/ + + //fprintf(stderr, "CDRAG_drag_drop: %s %s\n", GB.GetClassName(control), control->name); + + e->accept(); + + CDRAG_clear(true); + CDRAG_info.event = e; + CDRAG_destination = control; + //fprintf(stderr, "%d: ref destination %p\n", __LINE__, CDRAG_destination); + GB.Ref(CDRAG_destination); + + p = e->pos(); + p = w->mapTo(QWIDGET(control), p); + CDRAG_info.x = p.x(); + CDRAG_info.y = p.y(); + + GB.Raise(control, EVENT_Drop, 0); + + if (!CDRAG_dragging) // DnD run outside of the application + { + //fprintf(stderr, "%d: unref destination %p\n", __LINE__, CDRAG_destination); + GB.Unref(&CDRAG_destination); + CDRAG_destination = NULL; + hide_frame(control); + } + + CDRAG_clear(false); + + return true; +} + +BEGIN_METHOD(Drag_call, GB_OBJECT source; GB_VARIANT data; GB_STRING format) + + GB.ReturnObject(CDRAG_drag((CWIDGET *)VARG(source), &VARG(data), MISSING(format) ? NULL : ARG(format))); + +END_METHOD + +BEGIN_PROPERTY(Drag_Target) + + if (READ_PROPERTY) + GB.ReturnObject(CDRAG_destination); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&CDRAG_destination)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Drag_exit) + + hide_frame(NULL); + GB.Unref(POINTER(&_picture)); + +END_METHOD + +BEGIN_PROPERTY(Drag_Icon) + + if (READ_PROPERTY) + GB.ReturnObject(_picture); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&_picture)); + +END_PROPERTY + +BEGIN_PROPERTY(Drag_IconX) + + if (READ_PROPERTY) + GB.ReturnInteger(_picture_x); + else + _picture_x = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Drag_IconY) + + if (READ_PROPERTY) + GB.ReturnInteger(_picture_y); + else + _picture_y = VPROP(GB_INTEGER); + +END_PROPERTY + +#define CHECK_VALID() \ + if (!CDRAG_info.valid) \ + { \ + GB.Error("No drag data"); \ + return; \ + } + + +BEGIN_PROPERTY(Drag_Type) + + CHECK_VALID(); + + GB.ReturnInteger(get_type(CDRAG_info.event->mimeData())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Format) + + CHECK_VALID(); + + RETURN_NEW_STRING(get_format(CDRAG_info.event->mimeData())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Formats) + + GB_ARRAY array; + + CHECK_VALID(); + + GB.Array.New(&array, GB_T_STRING, 0); + get_formats(CDRAG_info.event->mimeData(), array); + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Data) + + if (!CDRAG_info.valid) + { + GB.ReturnVariant(NULL); + return; + } + + paste(CDRAG_info.event->mimeData(), NULL); + +END_PROPERTY + + +BEGIN_METHOD(Drag_Paste, GB_STRING format) + + if (!CDRAG_info.valid) + { + GB.ReturnVariant(NULL); + return; + } + + paste(CDRAG_info.event->mimeData(), MISSING(format) ? NULL : GB.ToZeroString(ARG(format))); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Action) + + CHECK_VALID(); + GB.ReturnInteger(CDRAG_action); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Source) + + CHECK_VALID(); + + GB.ReturnObject(CWidget::get(CDRAG_info.event->source())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_X) + + CHECK_VALID(); + + if (READ_PROPERTY) + GB.ReturnInteger(CDRAG_info.x); + else + CDRAG_info.x = VPROP(GB_INTEGER); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Y) + + CHECK_VALID(); + + if (READ_PROPERTY) + GB.ReturnInteger(CDRAG_info.y); + else + CDRAG_info.y = VPROP(GB_INTEGER); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Pending) + + GB.ReturnBoolean(CDRAG_dragging); + +END_PROPERTY + +BEGIN_METHOD(Drag_Show, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + if (GB.CheckObject(VARG(control))) + return; + + /*if (!CDRAG_dragging) + { + GB.Error("No undergoing drag"); + return; + }*/ + + if (MISSING(x) || MISSING(y) || MISSING(w) || MISSING(h)) + show_frame((CWIDGET *)VARG(control), 0, 0, -1, -1); + else + show_frame((CWIDGET *)VARG(control), VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD_VOID(Drag_Hide) + + hide_frame(NULL); + +END_METHOD + + +GB_DESC CDragDesc[] = +{ + GB_DECLARE_STATIC("Drag"), + + GB_CONSTANT("None", "i", MIME_UNKNOWN), + GB_CONSTANT("Text", "i", MIME_TEXT), + GB_CONSTANT("Image", "i", MIME_IMAGE), + + GB_CONSTANT("Copy", "i", DRAG_COPY), + GB_CONSTANT("Link", "i", DRAG_LINK), + GB_CONSTANT("Move", "i", DRAG_MOVE), + + GB_STATIC_PROPERTY("Icon", "Picture", Drag_Icon), + GB_STATIC_PROPERTY("IconX", "i", Drag_IconX), + GB_STATIC_PROPERTY("IconY", "i", Drag_IconY), + + GB_STATIC_PROPERTY_READ("Data", "v", Drag_Data), + GB_STATIC_PROPERTY_READ("Format", "s", Drag_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Drag_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", Drag_Type), + GB_STATIC_PROPERTY_READ("Action", "i", Drag_Action), + GB_STATIC_PROPERTY_READ("Source", "Control", Drag_Source), + GB_STATIC_PROPERTY("X", "i", Drag_X), + GB_STATIC_PROPERTY("Y", "i", Drag_Y), + GB_STATIC_PROPERTY_READ("Pending", "b", Drag_Pending), + GB_STATIC_PROPERTY("_Target", "Control", Drag_Target), + + GB_STATIC_METHOD("_call", "Control", Drag_call, "(Source)Control;(Data)v[(Format)s]"), + GB_STATIC_METHOD("_exit", NULL, Drag_exit, NULL), + GB_STATIC_METHOD("Show", NULL, Drag_Show, "(Control)Control;[(X)i(Y)i(Width)i(Height)i]"), + GB_STATIC_METHOD("Hide", NULL, Drag_Hide, NULL), + GB_STATIC_METHOD("Paste", "v", Drag_Paste, "[(Format)s]"), + + GB_END_DECLARE +}; + + + diff --git a/gb.qt4/src/CClipboard.h b/gb.qt4/src/CClipboard.h new file mode 100644 index 00000000..045f0ef6 --- /dev/null +++ b/gb.qt4/src/CClipboard.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CClipboard.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCLIPBOARD_H +#define __CCLIPBOARD_H + +#include +#include + +#include "gambas.h" +#include "CWidget.h" +#include "CPicture.h" + +typedef + struct { + const QDropEvent *event; + int x; + int y; + unsigned valid : 1; + } + CDRAG_INFO; + +#ifndef __CCLIPBOARD_CPP +extern GB_DESC CClipboardDesc[]; +extern GB_DESC CDragDesc[]; +extern CDRAG_INFO CDRAG_info; +extern bool CDRAG_dragging; +#endif + +void CDRAG_clear(bool valid); +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, GB_STRING *fmt); +bool CDRAG_drag_enter(QWidget *w, CWIDGET *control, QDropEvent *e); +void CDRAG_drag_leave(CWIDGET *control); +bool CDRAG_drag_move(QWidget *w, CWIDGET *control, QDropEvent *e); +bool CDRAG_drag_drop(QWidget *w, CWIDGET *control, QDropEvent *e); +void CDRAG_hide_frame(CWIDGET *control); + +void CLIPBOARD_has_changed(QClipboard::Mode mode); + +#endif diff --git a/gb.qt4/src/CColor.cpp b/gb.qt4/src/CColor.cpp new file mode 100644 index 00000000..72998e3e --- /dev/null +++ b/gb.qt4/src/CColor.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** + + CColor.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOLOR_CPP + +#include +#include +#include + +#include "gambas.h" + +#include "CWidget.h" +#include "CColor.h" + +GB_COLOR _link_foreground = COLOR_DEFAULT; +GB_COLOR _visited_foreground = COLOR_DEFAULT; +GB_COLOR _tooltip_foreground = COLOR_DEFAULT; +GB_COLOR _tooltip_background = COLOR_DEFAULT; + +static uint get_light_foreground() +{ + return IMAGE.MergeColor(qApp->palette().color(QPalette::Window).rgb() & 0xFFFFFF, qApp->palette().color(QPalette::WindowText).rgb() & 0xFFFFFF, 0.3); +} + +QColor CCOLOR_light_foreground() +{ + return TO_QCOLOR(get_light_foreground()); +} + +QColor CCOLOR_make(GB_COLOR color) +{ + int b = color & 0xFF; + int g = (color >> 8) & 0xFF; + int r = (color >> 16) & 0xFF; + int a = (color >> 24) ^ 0xFF; + + return QColor(r, g, b, a); +} + +static GB_COLOR get_color(QPalette::ColorRole role) +{ + return QApplication::palette().color(role).rgb() & 0xFFFFFF; +} + +static void return_color(QPalette::ColorRole role) +{ + GB.ReturnInteger(get_color(role)); +} + +static void handle_color(void *_param, GB_COLOR color, GB_COLOR *var) +{ + if (READ_PROPERTY) + GB.ReturnInteger(*var == COLOR_DEFAULT ? color : *var); + else + *var = VPROP(GB_INTEGER); +} + +BEGIN_PROPERTY(Color_Background) + + return_color(QPalette::Window); + +END_PROPERTY + +BEGIN_PROPERTY(Color_Foreground) + + return_color(QPalette::WindowText); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextBackground) + + return_color(QPalette::Base); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextForeground) + + return_color(QPalette::Text); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedBackground) + + return_color(QPalette::Highlight); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedForeground) + + return_color(QPalette::HighlightedText); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonBackground) + + return_color(QPalette::Button); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonForeground) + + return_color(QPalette::ButtonText); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightBackground) + + uint col = IMAGE.MergeColor(qApp->palette().color(QPalette::Base).rgb() & 0xFFFFFF, qApp->palette().color(QPalette::Highlight).rgb() & 0xFFFFFF, 0.5); + GB.ReturnInteger(col); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightForeground) + + GB.ReturnInteger(get_light_foreground()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TooltipBackground) + + handle_color(_param, get_color(QPalette::ToolTipBase), &_tooltip_background); + +END_PROPERTY + +static int get_luminance(QColor col) +{ + return (int)(0.299 * col.red() + 0.587 * col.green() + 0.114 * col.blue()); +} + +BEGIN_PROPERTY(Color_TooltipForeground) + + QColor bg = qApp->palette().color(QPalette::ToolTipBase); + QColor fg = qApp->palette().color(QPalette::ToolTipText); + int lbg = get_luminance(bg); + int lfg = get_luminance(fg); + GB_COLOR col; + + if (abs(lbg - lfg) <= 64) + fg.setHsv(fg.hue(), fg.saturation(), 255 - fg.value()); + + col = fg.rgb() & 0xFFFFFF; + + handle_color(_param, col, &_tooltip_foreground); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LinkForeground) + + handle_color(_param, get_color(QPalette::Link), &_link_foreground); + +END_PROPERTY + +BEGIN_PROPERTY(Color_VisitedForeground) + + handle_color(_param, get_color(QPalette::LinkVisited), &_visited_foreground); + +END_PROPERTY + +GB_DESC CColorDesc[] = +{ + GB_DECLARE_STATIC("Color"), + + GB_STATIC_PROPERTY_READ("Background", "i", Color_Background), + GB_STATIC_PROPERTY_READ("SelectedBackground", "i", Color_SelectedBackground), + GB_STATIC_PROPERTY_READ("LightBackground", "i", Color_LightBackground), + GB_STATIC_PROPERTY_READ("TextBackground", "i", Color_TextBackground), + GB_STATIC_PROPERTY_READ("ButtonBackground", "i", Color_ButtonBackground), + + GB_STATIC_PROPERTY_READ("Foreground", "i", Color_Foreground), + GB_STATIC_PROPERTY_READ("LightForeground", "i", Color_LightForeground), + GB_STATIC_PROPERTY_READ("SelectedForeground", "i", Color_SelectedForeground), + GB_STATIC_PROPERTY_READ("TextForeground", "i", Color_TextForeground), + GB_STATIC_PROPERTY_READ("ButtonForeground", "i", Color_ButtonForeground), + + GB_STATIC_PROPERTY("TooltipBackground", "i", Color_TooltipBackground), + GB_STATIC_PROPERTY("TooltipForeground", "i", Color_TooltipForeground), + + GB_STATIC_PROPERTY("LinkForeground", "i", Color_LinkForeground), + GB_STATIC_PROPERTY("VisitedForeground", "i", Color_VisitedForeground), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CColor.h b/gb.qt4/src/CColor.h new file mode 100644 index 00000000..6de03018 --- /dev/null +++ b/gb.qt4/src/CColor.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + CColor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOLOR_H +#define __CCOLOR_H + +#include "gambas.h" +#include "gb.image.h" + +#ifndef __CCOLOR_CPP +extern GB_DESC CColorDesc[]; +#endif + +QColor CCOLOR_make(GB_COLOR color); +QColor CCOLOR_light_foreground(); + +#endif diff --git a/gb.qt4/src/CConst.cpp b/gb.qt4/src/CConst.cpp new file mode 100644 index 00000000..2f4d3412 --- /dev/null +++ b/gb.qt4/src/CConst.cpp @@ -0,0 +1,229 @@ +/*************************************************************************** + + CConst.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONST_CPP + + +#include "gambas.h" + +#include + +#include +#include + +#include "main.h" +#include "CContainer.h" +#include "CConst.h" + +static int _alignment[] = +{ + ALIGN_NORMAL, Qt::AlignVCenter + Qt::AlignLeft, + ALIGN_LEFT, Qt::AlignVCenter + Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_RIGHT, Qt::AlignVCenter + Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_CENTER, Qt::AlignVCenter + Qt::AlignHCenter, + ALIGN_TOP_NORMAL, Qt::AlignTop + Qt::AlignLeft, + ALIGN_TOP_LEFT, Qt::AlignTop + Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_TOP_RIGHT, Qt::AlignTop + Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_TOP, Qt::AlignTop + Qt::AlignHCenter, + ALIGN_BOTTOM_NORMAL, Qt::AlignBottom + Qt::AlignLeft, + ALIGN_BOTTOM_LEFT, Qt::AlignBottom + Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_BOTTOM_RIGHT, Qt::AlignBottom + Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_BOTTOM, Qt::AlignBottom + Qt::AlignHCenter, + ALIGN_JUSTIFY, Qt::AlignVCenter + Qt::AlignJustify, + CONST_MAGIC +}; + +static int _horizontal_alignment[] = +{ + ALIGN_NORMAL, Qt::AlignLeft, + ALIGN_LEFT, Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_RIGHT, Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_CENTER, Qt::AlignHCenter, + ALIGN_JUSTIFY, Qt::AlignJustify, + CONST_MAGIC +}; + +int CCONST_convert(int *tab, int value, int def, bool to_qt) +{ + int *p = tab; + int ret; + + if (to_qt) + { + ret = p[1]; + + for(;;) + { + if (*p == CONST_MAGIC) + return ret; + else if (*p == def) + ret = p[1]; + else if (*p == value) + return p[1]; + p += 2; + } + } + else + { + for(;;) + { + if (*p == CONST_MAGIC) + return def; + else if (p[1] == value) + return p[0]; + p += 2; + } + } +} + +int CCONST_alignment(int value, int def, bool to_qt) +{ + return CCONST_convert(_alignment, value, def, to_qt); +} + +int CCONST_horizontal_alignment(int value, int def, bool to_qt) +{ + return CCONST_convert(_horizontal_alignment, value, def, to_qt); +} + +#define IMPLEMENT_ALIGN(_method, _code) \ +BEGIN_METHOD(_method, GB_INTEGER align) \ + int a = VARG(align); \ + GB.ReturnBoolean(_code); \ +END_METHOD + +IMPLEMENT_ALIGN(Align_IsTop, ALIGN_IS_TOP(a)) +IMPLEMENT_ALIGN(Align_IsBottom, ALIGN_IS_BOTTOM(a)) +IMPLEMENT_ALIGN(Align_IsMiddle, ALIGN_IS_MIDDLE(a)) +IMPLEMENT_ALIGN(Align_IsLeft, ALIGN_IS_LEFT(a)) +IMPLEMENT_ALIGN(Align_IsRight, ALIGN_IS_RIGHT(a)) +IMPLEMENT_ALIGN(Align_IsCenter, ALIGN_IS_CENTER(a)) + +BEGIN_METHOD(Align_Make, GB_INTEGER halign; GB_INTEGER valign) + + GB.ReturnInteger(ALIGN_MAKE(VARG(halign), VARG(valign))); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC AlignDesc[] = +{ + GB_DECLARE_STATIC("Align"), + + GB_CONSTANT("Normal", "i", ALIGN_NORMAL), + GB_CONSTANT("Left", "i", ALIGN_LEFT), + GB_CONSTANT("Right", "i", ALIGN_RIGHT), + GB_CONSTANT("Center", "i", ALIGN_CENTER), + + GB_CONSTANT("TopNormal", "i", ALIGN_TOP_NORMAL), + GB_CONSTANT("TopLeft", "i", ALIGN_TOP_LEFT), + GB_CONSTANT("TopRight", "i", ALIGN_TOP_RIGHT), + GB_CONSTANT("Top", "i", ALIGN_TOP), + + GB_CONSTANT("BottomNormal", "i", ALIGN_BOTTOM_NORMAL), + GB_CONSTANT("BottomLeft", "i", ALIGN_BOTTOM_LEFT), + GB_CONSTANT("BottomRight", "i", ALIGN_BOTTOM_RIGHT), + GB_CONSTANT("Bottom", "i", ALIGN_BOTTOM), + + GB_CONSTANT("Justify", "i", ALIGN_JUSTIFY), + + GB_STATIC_METHOD("IsTop", "b", Align_IsTop, "(Alignment)i"), + GB_STATIC_METHOD("IsBottom", "b", Align_IsBottom, "(Alignment)i"), + GB_STATIC_METHOD("IsMiddle", "b", Align_IsMiddle, "(Alignment)i"), + GB_STATIC_METHOD("IsLeft", "b", Align_IsLeft, "(Alignment)i"), + GB_STATIC_METHOD("IsCenter", "b", Align_IsCenter, "(Alignment)i"), + GB_STATIC_METHOD("IsRight", "b", Align_IsRight, "(Alignment)i"), + + GB_STATIC_METHOD("Make", "i", Align_Make, "(Horizontal)i(Vertical)i"), + + GB_END_DECLARE +}; + + +GB_DESC ArrangeDesc[] = +{ + GB_DECLARE_STATIC("Arrange"), + + GB_CONSTANT("None", "i", ARRANGE_NONE), + GB_CONSTANT("Horizontal", "i", ARRANGE_HORIZONTAL), + GB_CONSTANT("Vertical", "i", ARRANGE_VERTICAL), + GB_CONSTANT("LeftRight", "i", ARRANGE_ROW), + GB_CONSTANT("TopBottom", "i", ARRANGE_COLUMN), + GB_CONSTANT("Row", "i", ARRANGE_ROW), + GB_CONSTANT("Column", "i", ARRANGE_COLUMN), + GB_CONSTANT("Fill", "i", ARRANGE_FILL), + + GB_END_DECLARE +}; + + +GB_DESC BorderDesc[] = +{ + GB_DECLARE_STATIC("Border"), + + GB_CONSTANT("None", "i", BORDER_NONE), + GB_CONSTANT("Plain", "i", BORDER_PLAIN), + GB_CONSTANT("Sunken", "i", BORDER_SUNKEN), + GB_CONSTANT("Raised", "i", BORDER_RAISED), + GB_CONSTANT("Etched", "i", BORDER_ETCHED), + + GB_END_DECLARE +}; + + +GB_DESC ScrollDesc[] = +{ + GB_DECLARE_STATIC("Scroll"), + + GB_CONSTANT("None", "i", SCROLL_NONE), + GB_CONSTANT("Horizontal", "i", SCROLL_HORIZONTAL), + GB_CONSTANT("Vertical", "i", SCROLL_VERTICAL), + GB_CONSTANT("Both", "i", SCROLL_BOTH), + + GB_END_DECLARE +}; + + +GB_DESC SelectDesc[] = +{ + GB_DECLARE_STATIC("Select"), + + GB_CONSTANT("None", "i", SELECT_NONE), + GB_CONSTANT("Single", "i", SELECT_SINGLE), + GB_CONSTANT("Multiple", "i", SELECT_MULTIPLE), + + GB_END_DECLARE +}; + + +GB_DESC DirectionDesc[] = +{ + GB_DECLARE_STATIC("Direction"), + + GB_CONSTANT("Default", "i", DIRECTION_DEFAULT), + GB_CONSTANT("LeftToRight", "i", DIRECTION_LTR), + GB_CONSTANT("RightToLeft", "i", DIRECTION_RTL), + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/CConst.h b/gb.qt4/src/CConst.h new file mode 100644 index 00000000..13613d71 --- /dev/null +++ b/gb.qt4/src/CConst.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + CConst.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONST_H +#define __CCONST_H + +#include "gambas.h" +#include "gb.form.const.h" + +#ifndef __CCONST_CPP +extern GB_DESC AlignDesc[]; +extern GB_DESC ArrangeDesc[]; +extern GB_DESC BorderDesc[]; +extern GB_DESC ScrollDesc[]; +extern GB_DESC SelectDesc[]; +extern GB_DESC DirectionDesc[]; +#endif + +int CCONST_convert(int *tab, int value, int def, bool to_qt); +int CCONST_alignment(int value, int def, bool to_qt); +int CCONST_horizontal_alignment(int value, int def, bool to_qt); +int CCONST_line_style(int value, int def, bool to_qt); +int CCONST_fill_style(int value, int def, bool to_qt); + +#endif diff --git a/gb.qt4/src/CContainer.cpp b/gb.qt4/src/CContainer.cpp new file mode 100644 index 00000000..c6f0cdbe --- /dev/null +++ b/gb.qt4/src/CContainer.cpp @@ -0,0 +1,1662 @@ +/*************************************************************************** + + CContainer.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONTAINER_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "gb_common.h" + +#include "CWidget.h" +#include "CWindow.h" +#include "CConst.h" +#include "CTabStrip.h" +#include "CColor.h" +#include "cpaint_impl.h" +#include "CContainer.h" + +#if QT5 +#include +#define QStyleOptionFrameV3 QStyleOptionFrame +#else +#include +#endif + + +//#define DEBUG_ME +//#define USE_CACHE 1 + +#define CALL_FUNCTION(_this, _func) \ +{ \ + if ((_this)->_func) \ + { \ + GB_FUNCTION func; \ + func.object = (_this); \ + func.index = (_this)->_func; \ + GB.Call(&func, 0, TRUE); \ + } \ +} + +static void send_change_event(CWIDGET *_object) +{ + if (GB.Is(THIS, CLASS_UserControl)) + CALL_FUNCTION(THIS_USERCONTROL, change_func); +} + +void CUSERCONTROL_send_change_event() +{ + CWidget::each(send_change_event); +} + + +//------------------------------------------------------------------------- + +DECLARE_EVENT(EVENT_Insert); +//DECLARE_EVENT(EVENT_Remove); +DECLARE_EVENT(EVENT_BeforeArrange); +DECLARE_EVENT(EVENT_Arrange); + +#if 0 +static int _count_move, _count_resize, _count_set_geom; + +static void move_widget(QWidget *wid, int x, int y) +{ + if (wid->x() != x || wid->y() != y) + { + #if DEBUG_CONTAINER + _count_move++; + #endif + wid->move(x, y); + } +} + +static void resize_widget(QWidget *wid, int w, int h) +{ + if (wid->width() != w || wid->height() != h) + { + #if DEBUG_CONTAINER + _count_resize++; + #endif + wid->resize(w, h); + } +} + +static void move_resize_widget(QWidget *wid, int x, int y, int w, int h) +{ + if (wid->x() != x || wid->y() != y || wid->width() != w || wid->height() != h) + { + #if DEBUG_CONTAINER + _count_set_geom++; + #endif + wid->setGeometry(x, y, w, h); + } +} +#endif + +#if USE_CACHE +static QHash _cache; +static int _cache_level = 0; +#endif + +static QWidget *get_next_widget(QObjectList &list, int &index) +{ + QObject *ob; + + for(;;) + { + if (index >= list.count()) + return NULL; + + ob = list.at(index); // ob might be null if we are inside the QWidget destructor + index++; + + if (ob && ob->isWidgetType()) + { + QWidget *w = (QWidget *)ob; + if (!w->isHidden() && !qobject_cast(w)) + return w; + } + } +} + +#if USE_CACHE + +static QRect *ensure_widget_geometry(QWidget *w) +{ + if (_cache.contains(w)) + return _cache.value(w); + else + { + QRect *r = new QRect(w->geometry()); + _cache.insert(w, r); + return r; + } +} + +static QRect *get_widget_geometry(QWidget *w) +{ + static QRect wg; + + if (_cache.contains(w)) + return _cache.value(w); + else + { + wg = w->geometry(); + return &wg; + } +} + +static void move_widget(void *_object, int x, int y) +{ + QRect *r = ensure_widget_geometry(WIDGET); + if (x == r->x() && y == r->y()) + return; + r->setX(x); + r->setY(y); + CWIDGET_move_cached(THIS, x, y); +} + +static void resize_widget(void *_object, int w, int h) +{ + QRect *r = ensure_widget_geometry(WIDGET); + if (w == r->width() && h == r->height()) + return; + r->setWidth(w); + r->setHeight(h); + CWIDGET_resize_cached(THIS, w, h); +} + +static void move_resize_widget(void *_object, int x, int y, int w, int h) +{ + QRect *r = ensure_widget_geometry(WIDGET); + if (x == r->x() && y == r->y() && w == r->width() && h == r->height()) + return; + r->setRect(x, y, w, h); + CWIDGET_move_resize_cached(THIS, x, y, w, h); +} + +static void get_widget_contents(QWidget *wid, int &x, int &y, int &w, int &h) +{ + QRect wc = wid->contentsRect(); + QRect wg = wid->geometry(); + QRect *g = get_widget_geometry(wid); + + x = wc.x(); + y = wc.y(); + w = wc.width() + g->width() - wg.width(); + h = wc.height() + g->height() - wg.height(); +} + +static void flush_cache() +{ + QHash hash; + QWidget *w; + QRect *r; + + hash = _cache; + _cache.clear(); + + QHashIterator it(_cache); + while (it.hasNext()) + { + it.next(); + w = it.key(); + r = it.value(); + w->setGeometry(*r); + delete r; + //CWIDGET_move_resize(CWidget::getReal(w), r->x(), r->y(), r->width(), r->height()); + } + + _cache.clear(); +} + +#else +#if 0 + +static void get_widget_contents(QWidget *wid, int &x, int &y, int &w, int &h) +{ + QRect wc; + + wc = wid->contentsRect(); + + x = wc.x(); + y = wc.y(); + w = wc.width(); + h = wc.height(); + +} +#endif +#endif + +static void resize_container(void *_object, QWidget *cont, int w, int h) +{ + QWidget *wid = ((CWIDGET *)_object)->widget; + #if USE_CACHE + resize_widget(_object, w + wid->width() - cont->width(), h + wid->height() - cont->height()); + #else + CWIDGET_auto_resize(_object, w + wid->width() - cont->width(), h + wid->height() - cont->height()); + #endif +} + + +#define WIDGET_TYPE QWidget * +#define CONTAINER_TYPE QWidget * +#define ARRANGEMENT_TYPE CCONTAINER_ARRANGEMENT * + +#define GET_WIDGET(_object) ((CWIDGET *)_object)->widget +#define IS_RIGHT_TO_LEFT(_object) (((CWIDGET *)_object)->widget)->isRightToLeft() +#define GET_CONTAINER(_object) ((CCONTAINER *)_object)->container +#define GET_ARRANGEMENT(_object) ((CCONTAINER_ARRANGEMENT *)_object) +#define IS_EXPAND(_object) (((CWIDGET *)_object)->flag.expand) +#define IS_IGNORE(_object) (((CWIDGET *)_object)->flag.ignore) +#define IS_DESIGN(_object) (CWIDGET_is_design(_object)) +//#define IS_WIDGET_VISIBLE(_widget) (_widget)->isVisible() + +//#define CAN_ARRANGE(_object) ((_object) && !CWIDGET_test_flag(_object, WF_DELETED) && (!GB.Is(_object, CLASS_Window) || (((CWINDOW *)_object)->opened))) +#define CAN_ARRANGE(_object) ((_object) && ((CWIDGET *)(_object))->flag.shown && !((CWIDGET *)(_object))->flag.deleted) + +#if USE_CACHE + +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) get_widget_contents(_widget, _x, _y, _w, _h) +#define GET_WIDGET_X(_widget) get_widget_geometry(_widget)->x() +#define GET_WIDGET_Y(_widget) get_widget_geometry(_widget)->y() +#define GET_WIDGET_W(_widget) get_widget_geometry(_widget)->width() +#define GET_WIDGET_H(_widget) get_widget_geometry(_widget)->height() +#define MOVE_WIDGET(_object, _widget, _x, _y) move_widget((_object), (_x), (_y)) +#define RESIZE_WIDGET(_object, _widget, _w, _h) resize_widget((_object), (_w), (_h)) +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) move_resize_widget((_object), (_x), (_y), (_w), (_h)) +#define RESIZE_CONTAINER(_object, _cont, _w, _h) resize_container((_object), (_cont), (_w), (_h)) + +#else + +//#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) get_widget_contents(_widget, _x, _y, _w, _h) +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) \ + _x = (_widget)->contentsRect().x(); \ + _y = (_widget)->contentsRect().y(); \ + _w = (_widget)->contentsRect().width(); \ + _h = (_widget)->contentsRect().height(); +#define GET_WIDGET_X(_widget) (_widget)->x() +#define GET_WIDGET_Y(_widget) (_widget)->y() +#define GET_WIDGET_W(_widget) (_widget)->width() +#define GET_WIDGET_H(_widget) (_widget)->height() +#define MOVE_WIDGET(_object, _widget, _x, _y) CWIDGET_move((_object), (_x), (_y)) +#define RESIZE_WIDGET(_object, _widget, _w, _h) CWIDGET_resize((_object), (_w), (_h)) +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) CWIDGET_move_resize((_object), (_x), (_y), (_w), (_h)) +#define RESIZE_CONTAINER(_object, _cont, _w, _h) resize_container((_object), (_cont), (_w), (_h)) + +#endif + +#define INIT_CHECK_CHILDREN_LIST(_widget) \ + QObjectList list = (_widget)->children(); \ + int list_index = 0; + +#define HAS_CHILDREN() (list.count() != 0) + +#define RESET_CHILDREN_LIST() list_index = 0 +#define GET_NEXT_CHILD_WIDGET() get_next_widget(list, list_index) + +#define GET_OBJECT_FROM_WIDGET(_widget) CWidget::getRealExisting(_widget) + +#define GET_OBJECT_NAME(_object) (((CWIDGET *)_object)->name) + +#define RAISE_ARRANGE_EVENT(_object) \ +{ \ + GB.Raise(_object, EVENT_Arrange, 0); \ +} + +#define RAISE_BEFORE_ARRANGE_EVENT(_object) \ +{ \ + GB.Raise(_object, EVENT_BeforeArrange, 0); \ +} + +#define DESKTOP_SCALE MAIN_scale + +#define FUNCTION_NAME CCONTAINER_arrange_real + +#include "gb.form.arrangement.h" + +void CCONTAINER_arrange(void *_object) +{ + #if DEBUG_CONTAINER + static int level = 0; + + //if (!level) + // _count_move = _count_resize = _count_set_geom = 0; + level++; + #endif + + #if USE_CACHE + qDebug("CCONTAINER_arrange: %s %p", GB.GetClassName(THIS), THIS->widget.widget); + _cache_level++; + #endif + + /*for (int i = 1; i < level; i++) + fputs(" ", stderr); + fprintf(stderr, "CCONTAINER_arrange: [%d], %s: %d %d / %d x %d\n", level, THIS->widget.name, WIDGET->x(), WIDGET->y(), WIDGET->width(), WIDGET->height());*/ + + if (GB.Is(THIS, CLASS_TabStrip)) + CTABSTRIP_arrange(THIS); + + CCONTAINER_arrange_real(_object); + + /*for (int i = 1; i < level; i++) + fputs(" ", stderr); + fprintf(stderr, "CCONTAINER_arrange: [%d], %s: <<<<<<<<\n", level, THIS->widget.name);*/ + + #if USE_CACHE + _cache_level--; + + if (!_cache_level) + { + flush_cache(); + } + #endif + + //QWidget *cont = GET_CONTAINER(_object); + //if (cont->isA("MyContents")) + // ((MyContents *)cont)->afterArrange(); + + #if DEBUG_CONTAINER + level--; + /*if (!level) + { + if (_count_move || _count_resize || _count_set_geom) + qDebug("CCONTAINER_arrange: (%s %s): move = %d resize = %d setGeometry = %d", GB.GetClassName(THIS), THIS->widget.name, _count_move, _count_resize, _count_set_geom); + }*/ + #endif +} + +static int max_w, max_h; +static int _gms_x, _gms_y, _gms_w, _gms_h; + +static void gms_move_widget(QWidget *wid, int x, int y) +{ + int w = x + wid->width(); + int h = y + wid->height(); + + if (w > max_w) max_w = w; + if (h > max_h) max_h = h; +} + +static void gms_move_resize_widget(QWidget *wid, int x, int y, int w, int h) +{ + w += x; + h += y; + + if (w > max_w) max_w = w; + if (h > max_h) max_h = h; +} + +#undef MOVE_WIDGET +#define MOVE_WIDGET(_object, _widget, _x, _y) gms_move_widget(_widget, _x, _y) +#undef RESIZE_WIDGET +#define RESIZE_WIDGET(_object, _widget, _w, _h) (0) +#undef MOVE_RESIZE_WIDGET +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) gms_move_resize_widget(_widget, _x, _y, _w, _h) +#undef RAISE_BEFORE_ARRANGE_EVENT +#define RAISE_BEFORE_ARRANGE_EVENT(_object) (0) +#undef RAISE_ARRANGE_EVENT +#define RAISE_ARRANGE_EVENT(_object) (0) +#undef FUNCTION_NAME +#define FUNCTION_NAME get_max_size +#undef RESIZE_CONTAINER +#define RESIZE_CONTAINER(_object, _cont, _w, _h) (0) +#undef GET_WIDGET_CONTENTS +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) \ + _x = _gms_x; \ + _y = _gms_y; \ + _w = _gms_w; \ + _h = _gms_h; + +//#undef IS_WIDGET_VISIBLE +//#define IS_WIDGET_VISIBLE(_cont) (1) +#define GET_MAX_SIZE + +#include "gb.form.arrangement.h" + +void CCONTAINER_get_max_size(void *_object, int xc, int yc, int wc, int hc, int *w, int *h) +{ + int add; + CCONTAINER_ARRANGEMENT *arr = THIS_ARRANGEMENT; + bool locked; + + locked = arr->locked; + arr->locked = false; + + max_w = 0; + max_h = 0; + _gms_x = xc; + _gms_y = yc; + _gms_w = wc; + _gms_h = hc; + get_max_size(THIS); + + if (arr->margin) + add = arr->padding ? arr->padding : MAIN_scale; + else if (!arr->spacing) + add = arr->padding; + else + add = 0; + + *w = max_w + add; + *h = max_h + add; + + arr->locked = locked; +} + + +#define arrange_later arrange_now +#define arrange_now(_widget) CCONTAINER_arrange(CWidget::get(_widget)) + +#if 0 +static void post_arrange_later(void *_object) +{ + if (WIDGET && THIS_ARRANGEMENT->dirty) + CCONTAINER_arrange(THIS); + + GB.Unref(&_object); +} + +static void arrange_later(QWidget *cont) +{ + void *_object = CWidget::get(cont); + + if (THIS_ARRANGEMENT->dirty || THIS_ARRANGEMENT->mode == ARRANGE_NONE) + return; + + GB.Ref(_object); + //qDebug("later: %p: dirty = TRUE", THIS); + THIS_ARRANGEMENT->dirty = TRUE; + GB.Post((void (*)())post_arrange_later, (intptr_t)THIS); +} +#endif + +void CCONTAINER_insert_child(void *_object) +{ + CWIDGET *parent = CWidget::get(WIDGET->parentWidget()); + if (parent) + { + CCONTAINER_update_design(parent); + GB.Raise(parent, EVENT_Insert, 1, GB_T_OBJECT, THIS); + } +} + +void CCONTAINER_decide(CWIDGET *control, bool *width, bool *height) +{ + void *_object = CWIDGET_get_parent(control); + + *width = *height = FALSE; + + if (!THIS || !control->flag.resized || control->flag.ignore || THIS_ARRANGEMENT->autoresize) + return; + + if ((THIS_ARRANGEMENT->mode == ARRANGE_VERTICAL) + || (THIS_ARRANGEMENT->mode == ARRANGE_HORIZONTAL && control->flag.expand) + || (THIS_ARRANGEMENT->mode == ARRANGE_ROW && control->flag.expand) + || (THIS_ARRANGEMENT->mode == ARRANGE_FILL)) + *width = TRUE; + + if ((THIS_ARRANGEMENT->mode == ARRANGE_HORIZONTAL) + || (THIS_ARRANGEMENT->mode == ARRANGE_VERTICAL && control->flag.expand) + || (THIS_ARRANGEMENT->mode == ARRANGE_COLUMN && control->flag.expand) + || (THIS_ARRANGEMENT->mode == ARRANGE_FILL)) + *height = TRUE; +} + +void CCONTAINER_update_design(void *_object) +{ + QObjectList list; + CWIDGET *child; + int i; + + if (!THIS->widget.flag.design) + return; + + if (!THIS_ARRANGEMENT->user && !THIS->widget.flag.design_ignore) + return; + + //fprintf(stderr, "CCONTAINER_update_design: %s %d\n", THIS->widget.name, THIS->widget.flag.design_ignore); + + if (THIS->widget.flag.design_ignore) + { + list = THIS->widget.widget->children(); + + for (i = 0; i < list.count(); i++) + { + child = CWidget::getRealExisting(list.at(i)); + if (child) + CWIDGET_set_design(child, true); + } + } + + if (GB.Is(THIS, CLASS_UserContainer) && CONTAINER == WIDGET) + return; + + list = CONTAINER->children(); + + for (i = 0; i < list.count(); i++) + { + child = CWidget::getRealExisting(list.at(i)); + if (child) + CWIDGET_set_design(child, true); + } +} + +void *CCONTAINER_get_first_child(void *_object) +{ + if (!GB.Is(THIS, CLASS_Container)) + return NULL; + + QObjectList list = GB.Is(THIS, CLASS_Window) ? CONTAINER->children() : WIDGET->children(); + void *ob; + + for (int i = 0; i < list.count(); i++) + { + ob = CWidget::getRealExisting(list.at(i)); + if (ob) + return ob; + } + + return NULL; +} + +void *CCONTAINER_get_last_child(void *_object) +{ + if (!GB.Is(THIS, CLASS_Container)) + return NULL; + + QObjectList list = GB.Is(THIS, CLASS_Window) ? CONTAINER->children() : WIDGET->children(); + void *ob; + + for (int i = list.count() - 1; i >= 0; i--) + { + ob = CWidget::getRealExisting(list.at(i)); + if (ob) + return ob; + } + + return NULL; +} + + +/*************************************************************************** + + class MyFrame + +***************************************************************************/ + +MyFrame::MyFrame(QWidget *parent) +: QWidget(parent),_frame(0),_pixmap(0),_bg(false) +{ +} + +void MyFrame::setStaticContents(bool on) +{ + void *_object = CWidget::get(this); + setAttribute(Qt::WA_StaticContents, on && _frame == BORDER_NONE && (_pixmap || THIS->widget.flag.fillBackground)); +} + +void MyFrame::setFrameStyle(int frame) +{ + int margin; + + _frame = frame; + + setStaticContents(true); + + margin = frameWidth(); + setContentsMargins(margin, margin, margin, margin); + + update(); +} + +static void _draw_border(QPainter *p, int frame, QWidget *w, QStyleOptionFrame &opt) +{ + QStyle *style; + QStyleOptionFrameV3 optv3; + bool a; + QBrush save_brush; + //QRect rect = opt.rect; + + if (frame == BORDER_NONE) + return; + + if (w) + style = w->style(); + else + style = QApplication::style(); + + p->save(); + + switch (frame) + { + case BORDER_PLAIN: + //qDrawPlainRect(p, opt.rect, CCOLOR_light_foreground()); + a = p->testRenderHint(QPainter::Antialiasing); + if (a) + p->setRenderHint(QPainter::Antialiasing, false); + p->setPen(CCOLOR_light_foreground()); + p->setBrush(Qt::NoBrush); + opt.rect = QRect(opt.rect.x(), opt.rect.y(), opt.rect.width() - 1, opt.rect.height() - 1); + p->drawRect(opt.rect); + if (a) + p->setRenderHint(QPainter::Antialiasing, true); + break; + + case BORDER_SUNKEN: + optv3.rect = opt.rect; + optv3.state = opt.state | QStyle::State_Sunken; + optv3.frameShape = QFrame::StyledPanel; + + save_brush = p->brush(); + p->setBrush(QBrush()); + style->drawPrimitive(QStyle::PE_Frame, &optv3, p, w); + p->setBrush(save_brush); + break; + + case BORDER_RAISED: + optv3.rect = opt.rect; + optv3.state = opt.state | QStyle::State_Raised; + optv3.frameShape = QFrame::StyledPanel; + + save_brush = p->brush(); + p->setBrush(QBrush()); + style->drawPrimitive(QStyle::PE_Frame, &optv3, p, w); + p->setBrush(save_brush); + break; + + case BORDER_ETCHED: + optv3.rect = opt.rect; + //optv3.state = opt.state | QStyle::State_Raised; + optv3.frameShape = QFrame::StyledPanel; + style->drawPrimitive(QStyle::PE_FrameGroupBox, &optv3, p, w); + break; + } + + p->restore(); +} + +void CCONTAINER_draw_border(QPainter *p, char frame, QWidget *wid) +{ + QStyleOptionFrame opt; + opt.init(wid); + opt.rect = QRect(0, 0, wid->width(), wid->height()); + _draw_border(p, frame, wid, opt); +} + +void CCONTAINER_draw_border_without_widget(QPainter *p, char frame, QStyleOptionFrame &opt) +{ + _draw_border(p, frame, NULL, opt); +} + + +int CCONTAINER_get_border_width(char border) +{ + switch (border) + { + case BORDER_PLAIN: + return 1; + break; + + case BORDER_SUNKEN: + case BORDER_RAISED: + case BORDER_ETCHED: + + return qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth); + break; + + default: + return 0; + } +} + +void CCONTAINER_set_border(char *border, char new_border, QWidget *wid) +{ + int m; + + if (new_border < BORDER_NONE || new_border > BORDER_ETCHED || new_border == *border) + return; + + *border = new_border; + m = CCONTAINER_get_border_width(new_border); + + wid->setContentsMargins(m, m, m, m); + wid->update(); +} + +void MyFrame::drawFrame(QPainter *p) +{ + CCONTAINER_draw_border(p, _frame, this); +} + +int MyFrame::frameWidth() +{ + switch (_frame) + { + case BORDER_PLAIN: + return 1; + + case BORDER_SUNKEN: + case BORDER_RAISED: + return style()->pixelMetric(QStyle::PM_ComboBoxFrameWidth); + + case BORDER_ETCHED: + return 2; + + default: + return 0; + } +} + +void MyFrame::setPixmap(QPixmap *pixmap) +{ + if (_pixmap != pixmap) + { + _pixmap = pixmap; + setAttribute(Qt::WA_OpaquePaintEvent, _pixmap != 0); + setStaticContents(_pixmap != 0); + } +} + +void MyFrame::paintEvent(QPaintEvent *e) +{ + QPainter painter(this); + + if (_bg || (_pixmap && _pixmap->hasAlphaChannel())) + { + CWIDGET *window = CWidget::get(parentWidget()); + GB_COLOR col = CWIDGET_get_background(window); + if (col != COLOR_DEFAULT) + painter.fillRect(e->rect(), CCOLOR_make(col)); + } + + if (_pixmap) + painter.drawTiledPixmap(0, 0, width(), height(), *_pixmap); + + drawFrame(&painter); +} + + +/*************************************************************************** + + class MyContainer + +***************************************************************************/ + +MyContainer::MyContainer(QWidget *parent) +: MyFrame(parent) +{ + //setStaticContents(true); + //setAttribute(Qt::WA_StaticContents); + //setAttribute(Qt::WA_OpaquePaintEvent); //, _pixmap != 0); +} + +MyContainer::~MyContainer() +{ + CWIDGET *_object = CWidget::getReal(this); + if (THIS) + THIS->widget.flag.deleted = true; +} + +void MyContainer::showEvent(QShowEvent *e) +{ + void *_object = CWidget::get(this); + QWidget::showEvent(e); + THIS->widget.flag.shown = TRUE; + /*if (!qstrcmp(GB.GetClassName(THIS), "HBox")) + { + qDebug("MyContainer::showEvent: %s %p: shown = 1 (%d %d)", THIS->widget.name, THIS, THIS->widget.widget->isVisible() , !THIS->widget.widget->isHidden()); + //BREAKPOINT(); + }*/ + CCONTAINER_arrange(THIS); +} + +void MyContainer::hideEvent(QHideEvent *e) +{ + void *_object = CWidget::get(this); + QWidget::hideEvent(e); + THIS->widget.flag.shown = FALSE; + /*if (!qstrcmp(GB.GetClassName(THIS), "HBox")) + { + qDebug("MyContainer::hideEvent: %s %p: shown = 0", THIS->widget.name, THIS); + //BREAKPOINT(); + }*/ +} + +static void cleanup_drawing(intptr_t arg1, intptr_t arg2) +{ + PAINT_end(); +} + +void MyContainer::paintEvent(QPaintEvent *event) +{ + void *_object = CWidget::get(this); + //QPainter *p; + QRect r; + //GB_COLOR bg; + GB_ERROR_HANDLER handler; + + if (!THIS_ARRANGEMENT->paint) + { + MyFrame::paintEvent(event); + return; + } + + r = event->rect(); + + PAINT_begin(THIS); + //p = PAINT_get_current(); + + /*bg = CWIDGET_get_background((CWIDGET *)THIS); + if (bg != COLOR_DEFAULT) + p->fillRect(0, 0, width(), height(), TO_QCOLOR(bg));*/ + + //fprintf(stderr, "paintEvent: %s: %d %d %d %d\n", THIS->widget.name, r.x(), r.y(), r.width(), r.height()); + PAINT_clip(r.x(), r.y(), r.width(), r.height()); + + handler.handler = (GB_CALLBACK)cleanup_drawing; + + GB.OnErrorBegin(&handler); + CALL_FUNCTION(THIS_USERCONTROL, paint_func); + GB.OnErrorEnd(&handler); + + PAINT_end(); +} + +void MyContainer::changeEvent(QEvent *e) +{ + void *_object = CWidget::get(this); + + if (e->type() == QEvent::LayoutDirectionChange) + CCONTAINER_arrange(THIS); + + if (!THIS_ARRANGEMENT->paint) + { + MyFrame::changeEvent(e); + return; + } + + if (e->type() == QEvent::FontChange) + { + CALL_FUNCTION(THIS_USERCONTROL, font_func); + } + else if (e->type() == QEvent::EnabledChange) + { + update(); + } +} + +void MyContainer::resizeEvent(QResizeEvent *e) +{ + void *_object = CWidget::get(this); + CALL_FUNCTION(THIS_USERCONTROL, resize_func); +} + + +/*************************************************************************** + + CContainer + +***************************************************************************/ + +static QRect getRect(void *_object) +{ + QWidget *w = CONTAINER; + + if (qobject_cast(WIDGET)) + ((MyMainWindow *)WIDGET)->configure(); + + if (qobject_cast(WIDGET)) + return QRect(0, 0, w->width(), w->height()); + + return w->contentsRect(); +} + +BEGIN_PROPERTY(Container_ClientX) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + QRect r = getRect(THIS); //_CONTAINER); + QPoint p(r.x(), r.y()); + + GB.ReturnInteger(CONTAINER->mapTo(WIDGET, p).x()); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientY) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + QRect r = getRect(THIS); // _CONTAINER); + QPoint p(r.x(), r.y()); + + GB.ReturnInteger(CONTAINER->mapTo(WIDGET, p).y()); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientWidth) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + GB.ReturnInteger(getRect(THIS).width()); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientHeight) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + GB.ReturnInteger(getRect(THIS).height()); + +END_PROPERTY + +BEGIN_PROPERTY(Container_Border) + + MyContainer *w = qobject_cast(THIS->container); + + if (!w) + return; + + if (READ_PROPERTY) + GB.ReturnInteger(w->frameStyle()); + else + { + w->setFrameStyle(VPROP(GB_INTEGER)); + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Container_SimpleBorder) + + MyContainer *w = qobject_cast(THIS->container); + + if (!w) + return; + + if (READ_PROPERTY) + GB.ReturnBoolean(w->frameStyle()); + else + { + w->setFrameStyle(VPROP(GB_BOOLEAN) ? BORDER_PLAIN : BORDER_NONE); + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Container_Arrangement) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS_ARRANGEMENT->mode); + else + { + int arr = VPROP(GB_INTEGER); + if (arr < 0 || arr > 8 || arr == THIS_ARRANGEMENT->mode) + return; + THIS_ARRANGEMENT->mode = arr; + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Arrangement) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Arrangement(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (arrangement %d)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save, val); + } + +END_PROPERTY + +BEGIN_PROPERTY(UserControl_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_get_allow_focus(THIS)); + else + CWIDGET_set_allow_focus(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Container_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->autoresize); + else + { + bool v = VPROP(GB_BOOLEAN); + if (v == THIS_ARRANGEMENT->autoresize) + return; + + THIS_ARRANGEMENT->autoresize = v; + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_AutoResize) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_AutoResize(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (autoresize)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Margin) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->margin); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->margin) + { + THIS_ARRANGEMENT->margin = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Margin) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Margin(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (padding)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Spacing) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->spacing); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->spacing) + { + THIS_ARRANGEMENT->spacing = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Spacing) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Spacing(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (spacing)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->invert); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->invert) + { + THIS_ARRANGEMENT->invert = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Invert) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Invert(cont, _param); + if (!READ_PROPERTY) + THIS_USERCONTAINER->save = cont->arrangement; + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Padding) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS_ARRANGEMENT->padding); + else + { + int val = VPROP(GB_INTEGER); + if (val != THIS_ARRANGEMENT->padding && val >= 0 && val <= 255) + { + THIS_ARRANGEMENT->padding = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Padding) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Padding(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (spacing)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + +BEGIN_PROPERTY(Container_Indent) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->indent); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->indent) + { + THIS_ARRANGEMENT->indent = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Container_Centered) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->centered); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->centered) + { + THIS_ARRANGEMENT->centered = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_METHOD(UserControl_new, GB_OBJECT parent) + + GB_FUNCTION func; + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS_ARRANGEMENT->mode = ARRANGE_FILL; + THIS_ARRANGEMENT->user = true; + + CWIDGET_new(wid, (void *)_object); + + if (!GB.GetFunction(&func, THIS, "UserControl_Draw", NULL, NULL)) + { + THIS_ARRANGEMENT->paint = true; + THIS_USERCONTROL->paint_func = func.index; + if (!GB.GetFunction(&func, THIS, "UserControl_Font", NULL, NULL)) + THIS_USERCONTROL->font_func = func.index; + if (!GB.GetFunction(&func, THIS, "UserControl_Change", NULL, NULL)) + THIS_USERCONTROL->change_func = func.index; + if (!GB.GetFunction(&func, THIS, "UserControl_Resize", NULL, NULL)) + THIS_USERCONTROL->resize_func = func.index; + } + + GB.Error(NULL); + +END_METHOD + +BEGIN_PROPERTY(UserControl_Container) + + void *current = (CCONTAINER *)CWidget::get(CONTAINER); + + if (READ_PROPERTY) + { + GB.ReturnObject(current); + } + else + { + CCONTAINER *cont = (CCONTAINER *)VPROP(GB_OBJECT); + QWidget *w; + QWidget *p; + + // sanity checks + + if (!cont) + { + if (current) + CWIDGET_container_for(current, NULL); + THIS->container = WIDGET; + CCONTAINER_update_design(THIS); + CWIDGET_register_proxy(THIS, NULL); + return; + } + + if (GB.CheckObject(cont)) + return; + + w = cont->container; + if (w == THIS->container) + return; + + for (p = w; p; p = p->parentWidget()) + { + if (p == WIDGET) + break; + } + + if (!p) + { + GB.Error("Container must be a child control"); + return; + } + + GB_COLOR bg = CWIDGET_get_background((CWIDGET *)current, true); + GB_COLOR fg = CWIDGET_get_foreground((CWIDGET *)current, true); + + if (current) + CWIDGET_container_for(current, NULL); + CWIDGET_container_for(cont, THIS); + + THIS->container = w; + + CCONTAINER_arrange(THIS); + + CWIDGET_set_color((CWIDGET *)cont, bg, fg, true); + + CCONTAINER_update_design(THIS); + + CWIDGET_register_proxy(THIS, cont); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Indent) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Indent(cont, _param); + if (!READ_PROPERTY) + THIS_USERCONTAINER->save = cont->arrangement; + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Centered) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Centered(cont, _param); + if (!READ_PROPERTY) + THIS_USERCONTAINER->save = cont->arrangement; + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Container) + + //CCONTAINER *before; + CCONTAINER *after; + + if (READ_PROPERTY) + UserControl_Container(_object, _param); + else + { + UserControl_Container(_object, _param); + + after = (CCONTAINER *)CWidget::get(THIS->container); + bool old_locked = ((CCONTAINER_ARRANGEMENT *)after)->locked; + after->arrangement = THIS_USERCONTAINER->save; + ((CCONTAINER_ARRANGEMENT *)after)->locked = old_locked; + //qDebug("(%s %p): arrangement = %08X", GB.GetClassName(THIS), THIS, after->arrangement); + CCONTAINER_arrange(after); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Design) + + Control_Design(_object, _param); + + if (!READ_PROPERTY && VPROP(GB_BOOLEAN)) + { + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + + cont->arrangement = 0; + ((CCONTAINER_ARRANGEMENT *)cont)->user = true; + THIS_USERCONTAINER->save = cont->arrangement; + } + +END_PROPERTY + + +BEGIN_METHOD(Container_FindChild, GB_INTEGER x; GB_INTEGER y) + + QObjectList list = CONTAINER->children(); + int index = 0; + QWidget *w; + void *control; + + for(;;) + { + w = get_next_widget(list, index); + if (!w) + break; + if (w->geometry().contains(VARG(x), VARG(y))) + { + control = CWidget::get(w); + if (control && control != THIS) + { + GB.ReturnObject(control); + return; + } + } + } + + GB.ReturnNull(); + +END_METHOD + + +BEGIN_METHOD(Container_unknown, GB_VALUE x; GB_VALUE y) + + char *name = GB.GetUnknown(); + int nparam = GB.NParam(); + + if (strcasecmp(name, "Find")) + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + return; + } + + if (nparam < 2) + { + GB.Error("Not enough argument"); + return; + } + else if (nparam > 2) + { + GB.Error("Too many argument"); + return; + } + + GB.Deprecated(QT_NAME, "Container.Find", "Container.FindChild"); + + if (GB.Conv(ARG(x), GB_T_INTEGER) || GB.Conv(ARG(y), GB_T_INTEGER)) + return; + + Container_FindChild(_object, _param); + + GB.ReturnConvVariant(); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +BEGIN_PROPERTY(Container_Children) + + CCONTAINERCHILDREN *children = (CCONTAINERCHILDREN *)GB.New(CLASS_ContainerChildren, NULL, NULL); + QObjectList list = CONTAINER->children(); + CWIDGET *child; + int i; + + children->container = THIS; + GB.Ref(THIS); + + GB.NewArray(POINTER(&children->children), sizeof(void *), 0); + + for (i = 0; i < list.count(); i++) + { + child = CWidget::getRealExisting(list.at(i)); + if (!child) + continue; + GB.Ref(child); + *(void **)GB.Add(&children->children) = child; + } + + GB.ReturnObject(children); + +END_PROPERTY + + +BEGIN_METHOD_VOID(ContainerChildren_free) + + int i; + CWIDGET **array = THIS_CHILDREN->children; + + for (i = 0; i < GB.Count(array); i++) + GB.Unref(POINTER(&array[i])); + + GB.FreeArray(&THIS_CHILDREN->children); + GB.Unref(POINTER(&THIS_CHILDREN->container)); + +END_METHOD + + +BEGIN_PROPERTY(ContainerChildren_Count) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children)); + +END_PROPERTY + + +BEGIN_PROPERTY(ContainerChildren_Max) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children) - 1); + +END_PROPERTY + + +BEGIN_METHOD(ContainerChildren_get, GB_INTEGER index) + + CWIDGET **array = THIS_CHILDREN->children; + int index = VARG(index); + + if (index < 0 || index >= GB.Count(array)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(array[index]); + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_next) + + CWIDGET **array = THIS_CHILDREN->children; + int index; + + index = ENUM(int); + + if (index >= GB.Count(array)) + GB.StopEnum(); + else + { + ENUM(int) = index + 1; + GB.ReturnObject(array[index]); + } + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_Clear) + + CCONTAINER_ARRANGEMENT *container = (CCONTAINER_ARRANGEMENT *)THIS_CHILDREN->container; + CWIDGET **children = THIS_CHILDREN->children; + bool locked; + int i; + + locked = container->locked; + container->locked = true; + + for (i = 0; i < GB.Count(children); i++) + CWIDGET_destroy(children[i]); + + container->locked = locked; + CCONTAINER_arrange(container); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +GB_DESC ContainerChildrenDesc[] = +{ + GB_DECLARE("ContainerChildren", sizeof(CCONTAINER)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, ContainerChildren_free, NULL), + GB_METHOD("_next", "Control", ContainerChildren_next, NULL), + GB_METHOD("_get", "Control", ContainerChildren_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", ContainerChildren_Count), + GB_PROPERTY_READ("Max", "i", ContainerChildren_Max), + GB_METHOD("Clear", NULL, ContainerChildren_Clear, NULL), + + GB_END_DECLARE +}; + + +GB_DESC ContainerDesc[] = +{ + GB_DECLARE("Container", sizeof(CCONTAINER)), GB_INHERITS("Control"), + GB_NOT_CREATABLE(), + + GB_PROPERTY_READ("Children", "ContainerChildren", Container_Children), + + GB_PROPERTY_READ("ClientX", "i", Container_ClientX), + GB_PROPERTY_READ("ClientY", "i", Container_ClientY), + GB_PROPERTY_READ("ClientW", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientWidth", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientH", "i", Container_ClientHeight), + GB_PROPERTY_READ("ClientHeight", "i", Container_ClientHeight), + + GB_METHOD("_unknown", "v", Container_unknown, "."), + GB_METHOD("FindChild", "Control", Container_FindChild, "(X)i(Y)i"), + + CONTAINER_DESCRIPTION, + + GB_EVENT("BeforeArrange", NULL, NULL, &EVENT_BeforeArrange), + GB_EVENT("Arrange", NULL, NULL, &EVENT_Arrange), + GB_EVENT("NewChild", NULL, "(Child)Control", &EVENT_Insert), + + GB_END_DECLARE +}; + + +GB_DESC UserControlDesc[] = +{ + GB_DECLARE("UserControl", sizeof(CUSERCONTROL)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + + GB_PROPERTY("_Container", "Container", UserControl_Container), + GB_PROPERTY("_AutoResize", "b", Container_AutoResize), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + GB_PROPERTY("_Padding", "i", Container_Padding), + GB_PROPERTY("_Spacing", "b", Container_Spacing), + GB_PROPERTY("_Margin", "b", Container_Margin), + GB_PROPERTY("_Indent", "b", Container_Indent), + GB_PROPERTY("_Invert", "b", Container_Invert), + GB_PROPERTY("_Centered", "b", Container_Centered), + GB_PROPERTY("_Focus", "b", UserControl_Focus), + + USERCONTROL_DESCRIPTION, + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + + +GB_DESC UserContainerDesc[] = +{ + GB_DECLARE("UserContainer", sizeof(CUSERCONTAINER)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + + GB_PROPERTY("_Container", "Container", UserContainer_Container), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + + GB_PROPERTY("Arrangement", "i", UserContainer_Arrangement), + GB_PROPERTY("AutoResize", "b", UserContainer_AutoResize), + GB_PROPERTY("Padding", "i", UserContainer_Padding), + GB_PROPERTY("Spacing", "b", UserContainer_Spacing), + GB_PROPERTY("Margin", "b", UserContainer_Margin), + GB_PROPERTY("Indent", "b", UserContainer_Indent), + GB_PROPERTY("Invert", "b", UserContainer_Invert), + GB_PROPERTY("Centered", "b", UserContainer_Centered), + + GB_PROPERTY("Design", "b", UserContainer_Design), + + //GB_PROPERTY("Focus", "b", UserContainer_Focus), + + USERCONTAINER_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CContainer.h b/gb.qt4/src/CContainer.h new file mode 100644 index 00000000..54fec545 --- /dev/null +++ b/gb.qt4/src/CContainer.h @@ -0,0 +1,195 @@ +/*************************************************************************** + + CContainer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONTAINER_H +#define __CCONTAINER_H + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CWidget.h" + +typedef + struct { + unsigned mode : 4; + unsigned user : 1; + unsigned locked : 1; + unsigned margin : 1; + unsigned spacing : 1; + unsigned padding : 8; + unsigned indent : 1; + unsigned centered : 1; + unsigned dirty : 1; + unsigned autoresize : 1; + unsigned invert : 1; + unsigned paint : 1; + unsigned _reserved: 10; + } + CARRANGEMENT; + +#define ARRANGEMENT_FLAG_PROPERTIES \ + GB_PROPERTY("AutoResize", "b", Container_AutoResize), \ + GB_PROPERTY("Padding", "i", Container_Padding), \ + GB_PROPERTY("Spacing", "b", Container_Spacing), \ + GB_PROPERTY("Margin", "b", Container_Margin), \ + GB_PROPERTY("Indent", "b", Container_Indent), \ + GB_PROPERTY("Invert", "b", Container_Invert), \ + GB_PROPERTY("Centered", "b", Container_Centered) + +#define ARRANGEMENT_PROPERTIES \ + GB_PROPERTY("Arrangement", "i", Container_Arrangement), \ + ARRANGEMENT_FLAG_PROPERTIES + + +#ifndef __CCONTAINER_CPP + +extern GB_DESC ContainerDesc[]; +extern GB_DESC ContainerChildrenDesc[]; +extern GB_DESC UserControlDesc[]; +extern GB_DESC UserContainerDesc[]; + +#else + +typedef + struct { + CCONTAINER parent; + ushort paint_func; + ushort font_func; + ushort change_func; + ushort resize_func; + } + CUSERCONTROL; + +typedef + struct { + CCONTAINER parent; + int32_t save; + } + CUSERCONTAINER; + +typedef + struct { + GB_BASE ob; + CCONTAINER *container; + CWIDGET **children; + } + CCONTAINERCHILDREN; + +#define WIDGET QWIDGET(_object) +#define THIS ((CCONTAINER *)_object) +#define THIS_CHILDREN ((CCONTAINERCHILDREN *)_object) +#define CONTAINER (THIS->container) +#define THIS_ARRANGEMENT (((CCONTAINER_ARRANGEMENT *)_object)) +#define THIS_USERCONTROL (((CUSERCONTROL *)_object)) +#define THIS_USERCONTAINER (((CUSERCONTAINER *)_object)) + +//#define CCONTAINER_PROPERTIES CWIDGET_PROPERTIES ",Arrangement" + +#endif + +DECLARE_PROPERTY(Container_ClientX); +DECLARE_PROPERTY(Container_ClientY); + +DECLARE_PROPERTY(Container_Arrangement); +DECLARE_PROPERTY(Container_AutoResize); +DECLARE_PROPERTY(Container_Padding); +DECLARE_PROPERTY(Container_Spacing); +DECLARE_PROPERTY(Container_Margin); +DECLARE_PROPERTY(Container_Indent); +DECLARE_PROPERTY(Container_Border); +DECLARE_PROPERTY(Container_SimpleBorder); +DECLARE_PROPERTY(Container_Invert); +DECLARE_PROPERTY(Container_Centered); + +void CCONTAINER_arrange(void *_object); +void CCONTAINER_get_max_size(void *_object, int xc, int yc, int wc, int hc, int *w, int *h); +void CCONTAINER_insert_child(void *_object); +void CCONTAINER_remove_child(void *_object); +void CCONTAINER_decide(CWIDGET *control, bool *width, bool *height); + +void CCONTAINER_draw_border(QPainter *p, char border, QWidget *w); +void CCONTAINER_draw_border_without_widget(QPainter *p, char border, QStyleOptionFrame &opt); +void CCONTAINER_set_border(char *border, char new_border, QWidget *wid); +int CCONTAINER_get_border_width(char border); + +void CCONTAINER_update_design(void *_object); + +void *CCONTAINER_get_first_child(void *_object); +void *CCONTAINER_get_last_child(void *_object); + +void CUSERCONTROL_send_change_event(); + +class MyFrame : public QWidget +{ + Q_OBJECT + +public: + explicit MyFrame(QWidget *); + + int frameStyle() const { return _frame; } + void setFrameStyle(int frame); + void drawFrame(QPainter *); + int frameWidth(); + void setPixmap(QPixmap *pixmap); + void setPaintBackgroundColor(bool bg) { _bg = bg; } + +protected: + + virtual void setStaticContents(bool on); + virtual void paintEvent(QPaintEvent *); + +private: + + int _frame; + QPixmap *_pixmap; + bool _bg; +}; + +class MyContainer : public MyFrame +{ + Q_OBJECT + +public: + + MyContainer(QWidget *); + ~MyContainer(); + + bool inDrawEvent(); + +protected: + + virtual void showEvent(QShowEvent *); + virtual void hideEvent(QHideEvent *); + virtual void paintEvent(QPaintEvent *); + virtual void changeEvent(QEvent *); + virtual void resizeEvent(QResizeEvent *); +}; + +#endif diff --git a/gb.qt4/src/CDialog.cpp b/gb.qt4/src/CDialog.cpp new file mode 100644 index 00000000..53bfbce8 --- /dev/null +++ b/gb.qt4/src/CDialog.cpp @@ -0,0 +1,477 @@ +/*************************************************************************** + + CDialog.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDIALOG_CPP + +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "CColor.h" +#include "CFont.h" + +#include "CDialog.h" + +static QString dialog_title; +static GB_ARRAY dialog_filter = NULL; +static int _filter_index = -1; +static bool _filter_index_set = false; +static QString dialog_path; +static GB_ARRAY dialog_paths = NULL; +static CFONT *dialog_font = NULL; +static bool dialog_show_hidden = false; + +static unsigned int dialog_color = 0; + + +static void init_filter(QFileDialog &dialog) +{ + QString all; + int i; + QString filter; + QString select; + int index = 0; + + if (!dialog_filter) + return; + + if (_filter_index_set) + { + _filter_index_set = false; + index = _filter_index; + } + else + index = -1; + + for (i = 0; i < (GB.Array.Count(dialog_filter) / 2); i++) + { + filter = TO_QSTRING(*((char **)GB.Array.Get(dialog_filter, i * 2))); + if (filter == "*") + continue; + + filter = TO_QSTRING(*((char **)GB.Array.Get(dialog_filter, i * 2 + 1))) + " (" + filter.replace(";", " ") + ")"; + + if (all.length()) + all += ";;"; + all += filter; + + if (i == index) + select = filter; + } + + all += ";;"; + filter = TO_QSTRING(GB.Translate("All files")) + " (*)"; + all += filter; + + if (select.length() == 0) + select = filter; + + dialog.setNameFilter(all); + dialog.selectNameFilter(select); +} + + +static void find_filter(QFileDialog &dialog) +{ + int i; + QString filter; + QString select; + + if (dialog_filter) + { + select = dialog.selectedNameFilter(); + + for (i = 0; i < (GB.Array.Count(dialog_filter) / 2); i++) + { + filter = TO_QSTRING(*((char **)GB.Array.Get(dialog_filter, i * 2))); + if (filter == "*") + continue; + + filter = TO_QSTRING(*((char **)GB.Array.Get(dialog_filter, i * 2 + 1))) + " (" + filter.replace(";", " ") + ")"; + + if (filter == select) + { + _filter_index = i; + return; + } + } + } + + _filter_index = -1; +} + + +BEGIN_METHOD_VOID(CDIALOG_exit) + + GB.StoreObject(NULL, POINTER(&dialog_filter)); + GB.StoreObject(NULL, POINTER(&dialog_paths)); + GB.StoreObject(NULL, POINTER(&dialog_font)); + +END_METHOD + + +BEGIN_PROPERTY(Dialog_Title) + + if (READ_PROPERTY) + RETURN_NEW_STRING(dialog_title); + else + dialog_title = QSTRING_PROP(); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Filter) + + if (READ_PROPERTY) + GB.ReturnObject(dialog_filter); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&dialog_filter)); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_ShowHidden) + + if (READ_PROPERTY) + GB.ReturnBoolean(dialog_show_hidden); + else + dialog_show_hidden = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(dialog_path); + else + dialog_path = QSTRING_PROP(); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Paths) + + GB.ReturnObject(dialog_paths); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Font) + + if (READ_PROPERTY) + GB.ReturnObject(dialog_font); + else + { + CFONT *font = (CFONT *)VPROP(GB_OBJECT); + + GB.StoreObject(NULL, POINTER(&dialog_font)); + if (font) + { + dialog_font = CFONT_create(*font->font); + GB.Ref(dialog_font); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Color) + + if (READ_PROPERTY) + GB.ReturnInteger(dialog_color); + else + dialog_color = VPROP(GB_INTEGER); + +END_PROPERTY + + +static QString my_getOpenFileName() +{ + QString result; + QFileDialog dialog(qApp->activeWindow(), dialog_title, dialog_path); + + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setOption(QFileDialog::DontUseNativeDialog); + dialog.setFilter(dialog_show_hidden ? (dialog.filter() | QDir::Hidden | QDir::System) : (dialog.filter() & ~(QDir::Hidden | QDir::System))); + init_filter(dialog); + + if (dialog.exec() == QDialog::Accepted) + result = dialog.selectedFiles().value(0); + + find_filter(dialog); + return result; +} + +static QStringList my_getOpenFileNames() +{ + QStringList result; + QFileDialog dialog(qApp->activeWindow(), dialog_title, dialog_path); + + dialog.setFileMode(QFileDialog::ExistingFiles); + dialog.setOption(QFileDialog::DontUseNativeDialog); + dialog.setFilter(dialog_show_hidden ? (dialog.filter() | QDir::Hidden | QDir::System) : (dialog.filter() & ~(QDir::Hidden | QDir::System))); + init_filter(dialog); + + if (dialog.exec() == QDialog::Accepted) + result = dialog.selectedFiles(); + + find_filter(dialog); + return result; +} + +static QString my_getSaveFileName() +{ + QString dir, file; + QString result; + + dir = dialog_path; + if (!dialog_path.endsWith('/')) + { + int pos = dialog_path.lastIndexOf('/'); + if (pos >= 0) + { + dir = dialog_path.left(pos); + file = dialog_path.mid(pos + 1); + } + } + + QFileDialog dialog(qApp->activeWindow(), dialog_title, dir); + dialog.selectFile(file); + + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setOption(QFileDialog::DontUseNativeDialog); + dialog.setFilter(dialog_show_hidden ? (dialog.filter() | QDir::Hidden | QDir::System) : (dialog.filter() & ~(QDir::Hidden | QDir::System))); + init_filter(dialog); + + if (dialog.exec() == QDialog::Accepted) + result = dialog.selectedFiles().value(0); + + find_filter(dialog); + return result; +} + +static QString my_getExistingDirectory() +{ + QFileDialog dialog(qApp->activeWindow(), dialog_title, dialog_path); + + dialog.setFileMode(QFileDialog::Directory); + dialog.setOption(QFileDialog::DontUseNativeDialog); + dialog.setFilter(dialog_show_hidden ? (dialog.filter() | QDir::Hidden | QDir::System) : (dialog.filter() & ~(QDir::Hidden | QDir::System))); + + if (dialog.exec() == QDialog::Accepted) + return dialog.selectedFiles().value(0); + else + return QString(); +} + + +BEGIN_METHOD(Dialog_OpenFile, GB_BOOLEAN multi) + + if (!VARGOPT(multi, false)) + { + QString file; + + file = my_getOpenFileName(); + + if (file.isNull()) + GB.ReturnBoolean(true); + else + { + dialog_path = file; + GB.ReturnBoolean(false); + } + } + else + { + QStringList files; + GB_ARRAY list; + GB_OBJECT ob; + int i; + + files = my_getOpenFileNames(); + + if (files.isEmpty()) + { + GB.StoreObject(NULL, POINTER(&dialog_paths)); + GB.ReturnBoolean(true); + } + else + { + GB.Array.New(&list, GB_T_STRING, files.count()); + ob.value = list; + GB.StoreObject(&ob, POINTER(&dialog_paths)); + + for (i = 0; i < files.count(); i++) + *(char **)GB.Array.Get(list, i) = NEW_STRING(files[i]); + + GB.ReturnBoolean(false); + } + } + + dialog_title = QString(); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SaveFile) + + QString file; + + file = my_getSaveFileName(); + + if (file.isNull()) + GB.ReturnBoolean(true); + else + { + dialog_path = file; + GB.ReturnBoolean(false); + } + + dialog_title = QString(); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectDirectory) + + QString file; + + file = my_getExistingDirectory(); + + if (file.isNull()) + GB.ReturnBoolean(true); + else + { + dialog_path = file; + GB.ReturnBoolean(false); + } + + dialog_title = QString(); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectColor) + + QColor color; + + color = QColorDialog::getColor(dialog_color, qApp->activeWindow(), dialog_title); //, qApp->activeWindow()); + + if (!color.isValid()) + GB.ReturnBoolean(true); + else + { + dialog_color = color.rgb() & 0xFFFFFF; + GB.ReturnBoolean(false); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectFont) + + QFont qfont; + bool ok; + #ifdef USE_DPI + int dpiX, dpiY; + #endif + + if (dialog_font) + qfont = *dialog_font->font; + else + qfont = QApplication::font(); + //qDebug("AVANT: %g --> %g", qfont.pointSizeFloat(), SIZE_REAL_TO_VIRTUAL(qfont.pointSizeFloat())); + qfont.setPointSizeF(SIZE_REAL_TO_VIRTUAL(qfont.pointSizeF())); + + #ifdef USE_DPI + dpiX = QPaintDevice::x11AppDpiX(); + dpiY = QPaintDevice::x11AppDpiY(); + QPaintDevice::x11SetAppDpiX(CFONT_dpi); + QPaintDevice::x11SetAppDpiY(CFONT_dpi); + #endif + + qfont = QFontDialog::getFont(&ok, qfont, qApp->activeWindow(), dialog_title); + + #ifdef USE_DPI + QPaintDevice::x11SetAppDpiX(dpiX); + QPaintDevice::x11SetAppDpiY(dpiY); + #endif + + //qDebug("APRES: %g --> %g", qfont.pointSizeFloat(), SIZE_VIRTUAL_TO_REAL(qfont.pointSizeFloat())); + qfont.setPointSizeF(SIZE_VIRTUAL_TO_REAL(qfont.pointSizeF())); + + if (!ok) + GB.ReturnBoolean(true); + else + { + GB.StoreObject(NULL, POINTER(&dialog_font)); + dialog_font = CFONT_create(qfont); + GB.Ref(dialog_font); + GB.ReturnBoolean(false); + } + +END_METHOD + +BEGIN_PROPERTY(Dialog_FilterIndex) + + if (READ_PROPERTY) + GB.ReturnInteger(_filter_index); + else + { + _filter_index = VPROP(GB_INTEGER); + _filter_index_set = true; + } + +END_PROPERTY + +GB_DESC CDialogDesc[] = +{ + GB_DECLARE("Dialog", 0), GB_VIRTUAL_CLASS(), + + //GB_STATIC_METHOD("_init", NULL, CDIALOG_init, NULL), + GB_STATIC_METHOD("_exit", NULL, CDIALOG_exit, NULL), + + GB_STATIC_METHOD("OpenFile", "b", Dialog_OpenFile, "[(Multi)b]"), + GB_STATIC_METHOD("SaveFile", "b", Dialog_SaveFile, NULL), + GB_STATIC_METHOD("SelectDirectory", "b", Dialog_SelectDirectory, NULL), + GB_STATIC_METHOD("SelectColor", "b", Dialog_SelectColor, NULL), + GB_STATIC_METHOD("SelectFont", "b", Dialog_SelectFont, NULL), + + GB_STATIC_PROPERTY("Title", "s", Dialog_Title), + GB_STATIC_PROPERTY("Path", "s", Dialog_Path), + GB_STATIC_PROPERTY_READ("Paths", "String[]", Dialog_Paths), + GB_STATIC_PROPERTY("Filter", "String[]", Dialog_Filter), + GB_STATIC_PROPERTY("FilterIndex", "i", Dialog_FilterIndex), + GB_STATIC_PROPERTY("Color", "i", Dialog_Color), + GB_STATIC_PROPERTY("Font", "Font", Dialog_Font), + GB_STATIC_PROPERTY("ShowHidden", "b", Dialog_ShowHidden), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CDialog.h b/gb.qt4/src/CDialog.h new file mode 100644 index 00000000..c66df0b7 --- /dev/null +++ b/gb.qt4/src/CDialog.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + CDialog.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDIALOG_H +#define __CDIALOG_H + +#include "gambas.h" +#include "CWidget.h" + +#ifndef __CDIALOG_CPP +extern GB_DESC CDialogDesc[]; +#endif + +#endif diff --git a/gb.qt4/src/CDraw.cpp b/gb.qt4/src/CDraw.cpp new file mode 100644 index 00000000..99db7fb6 --- /dev/null +++ b/gb.qt4/src/CDraw.cpp @@ -0,0 +1,300 @@ +/*************************************************************************** + + CDraw.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_CPP + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +//Added by qt3to4: +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +#ifndef NO_X_WINDOW +#ifndef QT5 +#include +#endif +#endif + +#include "CConst.h" +#include "CFont.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CPicture.h" +#include "CImage.h" +#include "CDrawingArea.h" +#include "CColor.h" +#include "cprinter.h" +#include "CDraw.h" + +typedef + QT_DRAW_EXTRA GB_DRAW_EXTRA; + +#define EXTRA(d) ((GB_DRAW_EXTRA *)(d->extra)) +#define DP(d) (EXTRA(d)->p) +#define DPM(d) (EXTRA(d)->pm) + +#define COLOR_TO_INT(color) ((color).rgba() ^ 0xFF000000) +#define MASK_COLOR(col) ((col & 0xFF000000) ? Qt::color0 : Qt::color1) + +DRAW_INTERFACE DRAW EXPORT; + +/*static void set_background(GB_DRAW *d, int col); +static void set_foreground(GB_DRAW *d, int col); +static void set_fill_color(GB_DRAW *d, int col);*/ + +void DRAW_init() +{ + GB.GetInterface("gb.draw", DRAW_INTERFACE_VERSION, &DRAW); +} + +static Qt::Alignment get_horizontal_alignment(Qt::Alignment align, QString *t = 0) +{ + align &= Qt::AlignHorizontal_Mask; + switch (align) + { + case Qt::AlignLeft: + if (t ? t->isRightToLeft() : QApplication::isRightToLeft()) + return Qt::AlignRight; + break; + + case Qt::AlignRight: + if (t ? t->isRightToLeft() : QApplication::isRightToLeft()) + return Qt::AlignLeft; + break; + + default: + break; + } + + return align & ~Qt::AlignAbsolute; +} + +static QStringList text_sl; +static QVector text_w; +static float text_line; + +static void get_text_size(QPainter *dp, QString &s, float *tw, float *th) +{ + float w, width = 0; + int i; + QFontMetricsF fm(dp->font()); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + text_sl = s.split('\n', Qt::KeepEmptyParts); +#else + text_sl = s.split('\n', QString::KeepEmptyParts); +#endif + + text_w.resize(text_sl.count()); + + for (i = 0; i < (int)text_sl.count(); i++) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + w = fm.horizontalAdvance(text_sl[i]); +#else + w = fm.width(text_sl[i]); +#endif + if (w > width) width = w; + text_w[i] = w; + } + + *tw = width; + + text_line = fm.height(); + *th = text_line * (1 + s.count('\n')); +} + +void DRAW_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align) +{ + QPen pen, penm; + QString t = text; + float xx, ww; + float tw, th; + int i; + Qt::Alignment a; + + get_text_size(p, t, &tw, &th); + + if (w < 0) w = tw; + if (h < 0) h = th; + + y += p->fontMetrics().ascent(); + + switch(align & Qt::AlignVertical_Mask) + { + case Qt::AlignBottom: y += h - th; break; + case Qt::AlignVCenter: y += (h - th) / 2; break; + default: break; + } + + for (i = 0; i < (int)text_sl.count(); i++) + { + t = text_sl[i]; + ww = text_w[i]; + + a = get_horizontal_alignment((Qt::Alignment)align, &t); + + switch(a) + { + case Qt::AlignRight: xx = x + w - ww; break; + case Qt::AlignHCenter: xx = x + (w - ww) / 2; break; + default: xx = x; break; + } + + //(*callback)(xx, y, t); + p->drawText(xx, y, t); + /*if (p2) + p2->drawText(xx, y, t);*/ + + y += text_line; + } +} + + //margin = 256; // * 96 / p->device()->physicalDpiY() * d->fontScale; + + /*if (GB.Is(d->device, CLASS_Printer)) + margin = 256.0; // * ((CPRINTER *)d->device)->printer->resolution() / 96 * d->fontScale; + else + margin = 256.0 * p->device()->physicalDpiY() / 96;*/ +// GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + + +void DRAW_init_rich_text(QTextDocument *doc, const QFont &font) +{ + doc->setDocumentMargin(0); + doc->setDefaultFont(font); + doc->setDefaultStyleSheet(QString("p { margin-bottom: %1px; } h1,h2,h3,h4,h5,h6 { margin-bottom: %1px; }").arg(QFontMetrics(font).height())); +} + +void DRAW_rich_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align) +{ + static QTextDocument *doc = NULL; + + QString a; + float tw, th; + QColor fg = p->pen().color(); + QString t = "" + text + ""; + qreal opacity = 1.0; + bool hasAlpha = fg.alpha() < 255; + + switch(get_horizontal_alignment((Qt::Alignment)align)) + { + case Qt::AlignRight: a = "right"; break; + case Qt::AlignHCenter: a = "center"; break; + case Qt::AlignJustify: a = "justify"; break; + } + + if (a.length()) + t = "
    " + t + "
    "; + + if (!doc) + doc = new QTextDocument; + + DRAW_init_rich_text(doc, p->font()); + doc->setHtml(t); + + if (w > 0) + doc->setTextWidth(w); + + tw = ::ceilf(doc->idealWidth()); + th = ::ceilf(doc->size().height()); + + if (w < 0) w = tw; + if (h < 0) h = th; + + switch(align & Qt::AlignVertical_Mask) + { + case Qt::AlignBottom: y += h - th; break; + case Qt::AlignVCenter: y += (h - th) / 2; break; + default: break; + } + + if (hasAlpha) + { + opacity = p->opacity(); + p->setOpacity(p->opacity() * fg.alpha() / 255.0); + } + + p->translate(x, y); + doc->drawContents(p); + p->translate(-x, -y); + + if (hasAlpha) + p->setOpacity(opacity); + + /*if (p2) + { + p2->translate(x, y); + doc->drawContents(p2); + p2->translate(-x, -y); + }*/ +} + +void DRAW_aligned_pixmap(QPainter *p, const QPixmap &pix, int x, int y, int w, int h, int align) +{ + int xp, yp; + + if (pix.isNull() || pix.width() == 0 || pix.height() == 0) + return; + + xp = x; + switch(get_horizontal_alignment((Qt::Alignment)align)) + { + case Qt::AlignRight: xp += w - pix.width(); break; + case Qt::AlignHCenter: xp += (w - pix.width()) / 2; break; + default: break; + } + + yp = y; + switch(align & Qt::AlignVertical_Mask) + { + case Qt::AlignBottom: yp += h - pix.height(); break; + case Qt::AlignVCenter: yp += (h - pix.height()) / 2; break; + default: break; + } + + p->drawPixmap(xp, yp, pix); +} + diff --git a/gb.qt4/src/CDraw.h b/gb.qt4/src/CDraw.h new file mode 100644 index 00000000..964c835e --- /dev/null +++ b/gb.qt4/src/CDraw.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + CDraw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "gambas.h" +#include "gb.draw.h" +#include "gb.paint.h" + +#include +#include +#include + +#ifndef __CDRAW_C + +extern DRAW_INTERFACE DRAW; + +#endif + +typedef + void (*DRAW_TEXT_CB)(float, float, QString &); + +void DRAW_init(); +void DRAW_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align); +void DRAW_init_rich_text(QTextDocument *doc, const QFont &font); +void DRAW_rich_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align); +void DRAW_aligned_pixmap(QPainter *p, const QPixmap &pix, int x, int y, int w, int h, int align); + +#endif diff --git a/gb.qt4/src/CDrawingArea.cpp b/gb.qt4/src/CDrawingArea.cpp new file mode 100644 index 00000000..2ad653ac --- /dev/null +++ b/gb.qt4/src/CDrawingArea.cpp @@ -0,0 +1,740 @@ +/*************************************************************************** + + CDrawingArea.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAWINGAREA_CPP + +#include +#include +#include +#include +#include +#include +#include + +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CColor.h" +#include "CDrawingArea.h" + +#ifndef NO_X_WINDOW +#ifndef QT5 +#include +#include +#endif + +#ifdef FontChange +#undef FontChange +#endif +#endif + +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Font); +DECLARE_EVENT(EVENT_Change); + + +static void send_change_event_widget(CWIDGET *widget) +{ + if (GB.Is(widget, CLASS_DrawingArea)) + GB.Raise(widget, EVENT_Change, 0); +} + +void CDRAWINGAREA_send_change_event() +{ + CWidget::each(send_change_event_widget); +} + + +/*************************************************************************** + + class MyDrawingArea + +***************************************************************************/ + +int MyDrawingArea::_in_any_draw_event = 0; + +MyDrawingArea::MyDrawingArea(QWidget *parent) : MyContainer(parent) +{ + drawn = 0; + cache = 0; +#ifndef QT5 + _background = (Qt::HANDLE)0; +#endif + _frozen = false; + _event_mask = 0; + _set_background = true; + _cached = false; + _no_background = false; + _in_draw_event = false; + _draw_event = EVENT_Draw; + + setAttribute(Qt::WA_KeyCompression, false); + setAttribute(Qt::WA_PaintOnScreen, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + setAttribute(Qt::WA_StaticContents, false); + + setFocusPolicy(Qt::NoFocus); +} + + +MyDrawingArea::~MyDrawingArea() +{ + deleteBackground(); +} + +void MyDrawingArea::setVisible(bool visible) +{ + MyContainer::setVisible(visible); +#ifndef QT5 + if (_cached) + { + if (visible) + QTimer::singleShot(10, this, SLOT(setBackground())); + else + parentWidget()->update(); + } +#endif +} + +void MyDrawingArea::setFrozen(bool f) +{ + if (f == _frozen) + return; + +#ifndef QT5 +#ifndef NO_X_WINDOW + XWindowAttributes attr; + + if (f) + { + //setBackgroundMode(Qt::NoBackground); + XGetWindowAttributes(QX11Info::display(), winId(), &attr); + _event_mask = attr.your_event_mask; + XSelectInput(QX11Info::display(), winId(), ExposureMask); + //clearWFlags(Qt::WPaintClever); + //qDebug("frozen"); + } + else + { + //setBackgroundMode(Qt::PaletteBackground); + XSelectInput(QX11Info::display(), winId(), _event_mask); + //qDebug("unfrozen"); + } + XFlush(QX11Info::display()); +#endif +#endif + + _frozen = f; +} + +static void cleanup_drawing(intptr_t _object) +{ + PAINT_end(); +} + +void MyDrawingArea::redraw(QRect &r, bool frame) +{ + QPainter *p; + void *_object = CWidget::get(this); + int fw; + GB_COLOR bg; + GB_RAISE_HANDLER handler; + + if (!_object) + return; + + //qDebug("paint: %d %d %d %d", r.x(), r.y(), r.width(), r.height()); + + _in_draw_event = true; + _in_any_draw_event++; + + PAINT_begin(THIS); + p = PAINT_get_current(); + + /*if (!isTransparent()) + { + p->translate(-r.x(), -r.y()); + }*/ + + //p->save(); + + fw = frameWidth(); + + bg = CWIDGET_get_background((CWIDGET *)THIS); + if (bg != COLOR_DEFAULT) + { + p->fillRect(fw, fw, width() - fw * 2, height() - fw * 2, TO_QCOLOR(bg)); + } + + PAINT_clip(r.x(), r.y(), r.width(), r.height()); + + //p->setClipRegion(event->region().intersect(contentsRect())); + //p->setBrushOrigin(-r.x(), -r.y()); + + handler.callback = cleanup_drawing; + handler.data = (intptr_t)THIS; + + GB.RaiseBegin(&handler); + GB.Raise(THIS, _draw_event, 0); + GB.RaiseEnd(&handler); + + //p->restore(); + + if (frame) + { + QPainter pf(this); + pf.setClipping(false); + //pf.begin(this); // Qt 5.x: avoid message "QPainter::begin: Painter already active" + pf.setRenderHint(QPainter::Antialiasing, false); + drawFrame(&pf); + } + + PAINT_end(); + + _in_draw_event = false; + _in_any_draw_event--; +} + +void MyDrawingArea::createBackground(int w, int h) +{ + void *_object = CWidget::get(this); + QPixmap p; +#ifndef QT5 + QX11Info xinfo = x11Info(); + Qt::HANDLE old = _background; +#endif + +#ifndef QT5 + _background = (Qt::HANDLE)XCreatePixmap(QX11Info::display(), RootWindow(QX11Info::display(), xinfo.screen()), w, h, xinfo.depth()); + _background_pixmap = QPixmap::fromX11Pixmap(_background, QPixmap::ExplicitlyShared); +#else + _background_pixmap = QPixmap(w, h); +#endif + _background_w = w; + _background_h = h; + + //qDebug("createBackground: %d x %d : %d -> %08X / winId = %08X", w, h, xinfo.depth(), (int)_background, (int)winId()); + //p = QPixmap::fromX11Pixmap(_background, QPixmap::ExplicitlyShared); + //qDebug("color = %06X -> %06X", palette().color(backgroundRole()).rgb(), QColormap::instance().pixel((unsigned long)palette().color(backgroundRole()).rgb())); + +#if 0 + gc = XCreateGC(QX11Info::display(), _background, 0, 0); + //XSetForeground(QX11Info::display(), gc, QColormap::instance().pixel((unsigned long)palette().color(backgroundRole()).rgb())); + XSetForeground(QX11Info::display(), gc, QColormap::instance().pixel(CWIDGET_get_real_background((CWIDGET *)THIS))); + XFillRectangle(QX11Info::display(), _background, gc, 0, 0, w, h); + XFreeGC(QX11Info::display(), gc); +#endif + + _background_pixmap.fill(CCOLOR_make(CWIDGET_get_real_background((CWIDGET *)THIS))); + + //qDebug("XSetWindowBackgroundPixmap: %08X %08X", (int)winId(), (int)_background); +#ifndef QT5 + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), _background); + XClearArea(QX11Info::display(), winId(), 0, 0, 0, 0, True); + + if (old) + XFreePixmap(QX11Info::display(), (Pixmap)old); + + XFlush(QX11Info::display()); +#else + update(); +#endif + + _cached = true; +} + +void MyDrawingArea::deleteBackground() +{ + if (hasCacheBackground()) + { +#ifndef QT5 + //qDebug("XSetWindowBackgroundPixmap: %08X None", (int)winId()); + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), None); + XFreePixmap(QX11Info::display(), (Pixmap)_background); + XFlush(QX11Info::display()); + _background = 0; +#else + _background_pixmap = QPixmap(); +#endif + _cached = false; + _background_w = _background_h = 0; + } +} + +QPixmap *MyDrawingArea::getBackgroundPixmap() +{ + if (!hasCacheBackground()) + return NULL; + else + return &_background_pixmap; +} + +void MyDrawingArea::paintEvent(QPaintEvent *event) +{ + if (_cached) + { +#ifndef QT5 +#ifndef NO_X_WINDOW + if (_set_background) + { + //qDebug("XSetWindowBackgroundPixmap: %08X %08X (paint)", (int)winId(), (int)_background); + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), _background); + XFlush(QX11Info::display()); + _set_background = false; + } +#endif +#endif + + QPainter p(this); + +#ifdef QT5 + p.drawPixmap(0, 0, _background_pixmap); +#endif + + if (frameWidth()) + { + QRegion r(0, 0, width(), height()); + r = r.subtracted(QRegion(frameWidth(), frameWidth(), width() - frameWidth() * 2, height() - frameWidth() * 2)); + p.setClipRegion(r); + p.setClipping(true); + //p.drawPixmap(0, 0, *getBackgroundPixmap()); + } + + drawFrame(&p); + } + else + { + //QPainter paint( this ); + QRect r; + + r = event->rect().intersected(contentsRect()); + if (r.isValid()) + { + /*if (!isTransparent()) + { + cache = new QPixmap(r.width(), r.height()); + cache->fill(this, r.x(), r.y()); + }*/ + + redraw(r, true); + + /*if (!isTransparent()) + { + paint.drawPixmap(r.x(), r.y(), *cache); + delete cache; + cache = 0; + }*/ + } + } +} + +void MyDrawingArea::setBackground() +{ +#ifndef QT5 + if (_cached) + { + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), _background); + XFlush(QX11Info::display()); + refreshBackground(); + } +#endif +} + +void MyDrawingArea::refreshBackground() +{ + if (_cached) + { +#ifndef QT5 + int fw = frameWidth(); + XClearArea(QX11Info::display(), winId(), fw, fw, width() - fw * 2, height() - fw * 2, False); + XFlush(QX11Info::display()); +#else + update(); +#endif + } +} + + +void MyDrawingArea::clearBackground() +{ + if (drawn) + { + GB.Error("DrawingArea is being painted"); + return; + } + + if (_cached) + { + createBackground(width(), height()); + } + else + { +#ifndef QT5 + XClearArea(QX11Info::display(), winId(), 0, 0, 0, 0, True); + XFlush(QX11Info::display()); +#else + update(); +#endif + } +} + +void MyDrawingArea::resizeEvent(QResizeEvent *e) +{ + MyContainer::resizeEvent(e); + updateBackground(); +} + +void MyDrawingArea::updateBackground() +{ + int w, h; +#ifndef QT5 + int wb, hb; +#endif + + if (_cached) + { + if (drawn) + { + GB.Error("DrawingArea is being drawn"); + return; + } + + w = qMax(width(), 1); + h = qMax(height(), 1); + + if (w != _background_w || h != _background_h) + { +#ifndef QT5 + Qt::HANDLE old = _background; + + wb = qMin(w, _background_w); + hb = qMin(h, _background_h); + + _background = 0; + createBackground(w, h); + + GC gc = XCreateGC(QX11Info::display(), old, 0, 0); + XCopyArea(QX11Info::display(), old, _background, gc, 0, 0, wb, hb, 0, 0); + XFreeGC(QX11Info::display(), gc); + + XFreePixmap(QX11Info::display(), old); +#else + QPixmap old = _background_pixmap; + createBackground(w, h); + QPainter p(&_background_pixmap); + p.drawPixmap(0, 0, old); + p.end(); +#endif + + setBackground(); + } + } +} + +void MyDrawingArea::setStaticContents(bool on) +{ +} + + +void MyDrawingArea::updateCache() +{ + //qDebug("updateCache: %d %08X", _cached, (int)winId()); + + if (_cached) // && !_transparent) + { +#ifndef QT5 + setAttribute(Qt::WA_DontCreateNativeAncestors, true); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_PaintOnScreen, true); +#endif + setAttribute(Qt::WA_OpaquePaintEvent, true); + setAttribute(Qt::WA_StaticContents, true); + createBackground(width(), height()); + setBackground(); + } + else //if (_background) + { + deleteBackground(); + setAttribute(Qt::WA_PaintOnScreen, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + setAttribute(Qt::WA_StaticContents, false); + #ifdef NO_X_WINDOW + setBackgroundMode(Qt::NoBackground); + #else + //XClearArea(QX11Info::display(), winId(), 0, 0, 0, 0, True); + //repaint(); + update(); + #endif + } + + updateNoBackground(); +} + +void MyDrawingArea::setCached(bool c) +{ + if (c == _cached) + return; + + _cached = c; + updateCache(); +} + +void MyDrawingArea::setPalette(const QPalette &pal) +{ + if (_cached) + return; + MyContainer::setPalette(pal); + update(); +} + +void MyDrawingArea::updateNoBackground() +{ + setAttribute(Qt::WA_NoSystemBackground, _no_background); + if (_cached) + setBackground(); +} + +void MyDrawingArea::setNoBackground(bool on) +{ + _no_background = on; + updateNoBackground(); +} + +void MyDrawingArea::hideEvent(QHideEvent *e) +{ + if (_cached) + _set_background = true; + MyContainer::hideEvent(e); +} + +void MyDrawingArea::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::FontChange) + { + MyContainer::changeEvent(e); + void *_object = CWidget::get(this); + GB.Raise(THIS, EVENT_Font, 0); + } +} + +/*************************************************************************** + + DrawingArea + +***************************************************************************/ + +BEGIN_METHOD(DrawingArea_new, GB_OBJECT parent) + + MyDrawingArea *wid = new MyDrawingArea(QCONTAINER(VARG(parent))); + + //THIS->widget.background = QColorGroup::Base; + THIS->container = wid; + THIS->widget.flag.noBackground = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(DrawingArea_Cached) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isCached()); + else + { + GB_COLOR bg = CWIDGET_get_background((CWIDGET *)THIS); + GB_COLOR fg = CWIDGET_get_foreground((CWIDGET *)THIS); + + if (bg == COLOR_DEFAULT) + { + CWIDGET_set_color((CWIDGET *)THIS, WIDGET->palette().color(WIDGET->backgroundRole()).rgb() & 0xFFFFFF, fg); + WIDGET->clearBackground(); + } + WIDGET->setCached(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + + +DECLARE_METHOD(Control_Background); + + +BEGIN_METHOD_VOID(DrawingArea_Clear) + + WIDGET->clearBackground(); + +END_METHOD + + +BEGIN_PROPERTY(DrawingArea_Background) + + Control_Background(_object, _param); + + if (!READ_PROPERTY) + WIDGET->clearBackground(); + +END_PROPERTY + + +BEGIN_PROPERTY(DrawingArea_NoBackground) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasNoBackground()); + else + WIDGET->setNoBackground(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(DrawingArea_Border) + + Container_Border(_object, _param); + + if (!READ_PROPERTY) + { + WIDGET->clearBackground(); + } + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Enabled) + + Control_Enabled(_object, _param); + + if (!READ_PROPERTY) + WIDGET->setFrozen(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_get_allow_focus(THIS)); + else + CWIDGET_set_allow_focus(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Painted) + + static bool deprecated = false; + + if (!deprecated) + { + deprecated = true; + GB.Deprecated(QT_NAME, "DrawingArea.Painted", NULL); + } + + if (READ_PROPERTY) + GB.ReturnBoolean(true); + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(CDRAWINGAREA_transparent) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isTransparent()); + else + { + WIDGET->setTransparent(VPROP(GB_BOOLEAN)); + //THIS->widget.flag.fillBackground = !WIDGET->isTransparent(); + //CWIDGET_reset_color((CWIDGET *)THIS); + } + +END_PROPERTY +#endif + +BEGIN_METHOD(DrawingArea_Refresh, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + /*if (WIDGET->isCached()) + { + QRect r; + + if (!MISSING(x) && !MISSING(y)) + r.setRect(VARG(x), VARG(y), VARGOPT(w, WIDGET->width()), VARGOPT(h, WIDGET->height())); + else + r.setRect(0, 0, WIDGET->width(), WIDGET->height()); + + WIDGET->redraw(r, false); + }*/ + + if (!MISSING(x) && !MISSING(y)) + { + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, QWIDGET(_object)->width()); + h = VARGOPT(h, QWIDGET(_object)->height()); + WIDGET->update(x, y, w, h); + } + else + { + WIDGET->update(); + } + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Tablet) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->widget.flag.use_tablet); + else + THIS->widget.flag.use_tablet = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +GB_DESC CDrawingAreaDesc[] = +{ + GB_DECLARE("DrawingArea", sizeof(CDRAWINGAREA)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, DrawingArea_new, "(Parent)Container;"), + + GB_PROPERTY("Cached", "b", DrawingArea_Cached), + + ARRANGEMENT_PROPERTIES, + + GB_PROPERTY("Border", "i", DrawingArea_Border), + GB_PROPERTY("NoBackground", "b", DrawingArea_NoBackground), + GB_PROPERTY("Background", "i", DrawingArea_Background), + + GB_PROPERTY("Focus", "b", DrawingArea_Focus), + GB_PROPERTY("Enabled", "b", DrawingArea_Enabled), + GB_PROPERTY("Painted", "b", DrawingArea_Painted), + GB_PROPERTY("Tablet", "b", DrawingArea_Tablet), + + GB_METHOD("Clear", NULL, DrawingArea_Clear, NULL), + GB_METHOD("Refresh", NULL, DrawingArea_Refresh, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + //GB_INTERFACE("Draw", &DRAW_Interface), + GB_INTERFACE("Paint", &PAINT_Interface), + + DRAWINGAREA_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/CDrawingArea.h b/gb.qt4/src/CDrawingArea.h new file mode 100644 index 00000000..57312472 --- /dev/null +++ b/gb.qt4/src/CDrawingArea.h @@ -0,0 +1,144 @@ +/*************************************************************************** + + CDrawingArea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAWINGAREA_H +#define __CDRAWINGAREA_H + +#include +#include +#include +//#include + +#include "gambas.h" + +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CDRAWINGAREA_CPP +extern GB_DESC CDrawingAreaDesc[]; +#else + +#define THIS ((CDRAWINGAREA *)_object) +#define WIDGET ((MyDrawingArea *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + QWidget *container; + CARRANGEMENT arrangement; + } + CDRAWINGAREA; + +class MyDrawingArea : public MyContainer +{ + Q_OBJECT + + friend class MyScrollArea; + +public: + + explicit MyDrawingArea(QWidget *parent); + ~MyDrawingArea(); + + int drawn; + QPixmap *cache; + + virtual void setVisible(bool visible); + + //void setTransparent(bool); + //bool isTransparent(void) { return transparent; } + + void updateCache(); + void setCached(bool); + bool isCached() const { return _cached; } + + void clearBackground(); +#ifndef QT5 + Qt::HANDLE background() const { return _background; } +#endif + void refreshBackground(); + void updateBackground(); + + void setFrozen(bool f); + bool isFrozen() const { return _frozen; } + + void redraw(QRect &r, bool frame = false); + + bool hasNoBackground() const { return _no_background; } + void setNoBackground(bool on); + void updateNoBackground(); + + void setDrawEvent(int event) { _draw_event = event; } + bool inDrawEvent() const { return _in_draw_event; } + static bool inAnyDrawEvent() { return _in_any_draw_event > 0; } + + void createBackground(int w, int h); +#ifndef QT5 + bool hasCacheBackground() const { return _cached && _background; } +#else + bool hasCacheBackground() const { return _cached && !_background_pixmap.isNull(); } +#endif + void deleteBackground(); + + QPixmap *getBackgroundPixmap(); + +public slots: + + void setBackground(); + //bool isTransparent() { return _transparent; } + //void setTransparent(bool on); + +protected: + + virtual void setStaticContents(bool on); + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *); + virtual void hideEvent(QHideEvent *); + //virtual void drawContents(QPainter *p); + virtual void setPalette(const QPalette &); + virtual void changeEvent(QEvent *); + +private: + + QPixmap _background_pixmap; +#ifndef QT5 + Qt::HANDLE _background; +#endif + int _background_w, _background_h; + unsigned _frozen : 1; + unsigned _merge : 1; + unsigned _focus : 1; + unsigned _set_background : 1; + unsigned _cached : 1; + unsigned _no_background : 1; + unsigned _in_draw_event : 1; + int _event_mask; + int _draw_event; + static int _in_any_draw_event; +}; + +void CDRAWINGAREA_send_change_event(void); + +#endif diff --git a/gb.qt4/src/CEmbedder.cpp b/gb.qt4/src/CEmbedder.cpp new file mode 100644 index 00000000..19ff6cdc --- /dev/null +++ b/gb.qt4/src/CEmbedder.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + + CEmbedder.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CEMBEDDER_CPP + +#include "CEmbedder.h" + +DECLARE_EVENT(EVENT_Embed); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Error); + +#ifdef NO_X_WINDOW + +BEGIN_METHOD(CEMBEDDER_new, GB_OBJECT parent) + + QWidget *wid = new QWidget(QCONTAINER(VARG(parent))); + + //QObject::connect(wid, SIGNAL(clientIsEmbedded()), &CEmbedder::manager, SLOT(embedded())); + //QObject::connect(wid, SIGNAL(clientClosed()), &CEmbedder::manager, SLOT(closed())); + //QObject::connect(wid, SIGNAL(error(QX11EmbedContainer::Error)), &CEmbedder::manager, SLOT(error())); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(CEMBEDDER_client) + + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD(CEMBEDDER_embed, GB_INTEGER client) + + //WIDGET->embedClient(VARG(client)); + +END_METHOD + + +BEGIN_METHOD_VOID(CEMBEDDER_discard) + + //WIDGET->discardClient(); + +END_METHOD + +#else + +BEGIN_METHOD(CEMBEDDER_new, GB_OBJECT parent) + + QX11EmbedContainer *wid = new QX11EmbedContainer(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(clientIsEmbedded()), &CEmbedder::manager, SLOT(embedded())); + QObject::connect(wid, SIGNAL(clientClosed()), &CEmbedder::manager, SLOT(closed())); + QObject::connect(wid, SIGNAL(error(QX11EmbedContainer::Error)), &CEmbedder::manager, SLOT(error())); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(CEMBEDDER_client) + + GB.ReturnInteger((int)WIDGET->clientWinId()); + +END_PROPERTY + + +BEGIN_METHOD(CEMBEDDER_embed, GB_INTEGER client) + + WIDGET->embedClient(VARG(client)); + +END_METHOD + + +BEGIN_METHOD_VOID(CEMBEDDER_discard) + + WIDGET->discardClient(); + +END_METHOD + +/*--- CEmbedder -----------------------------------------------------------------------------------------*/ + +CEmbedder CEmbedder::manager; + +void CEmbedder::embedded() +{ + RAISE_EVENT(EVENT_Embed); +} + +void CEmbedder::closed() +{ + RAISE_EVENT(EVENT_Close); +} + +void CEmbedder::error() +{ + RAISE_EVENT(EVENT_Error); +} + +#endif + +GB_DESC CEmbedderDesc[] = +{ + GB_DECLARE("Embedder", sizeof(CEMBEDDER)), + GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CEMBEDDER_new, "(Parent)Container;"), + + GB_PROPERTY_READ("Client", "i", CEMBEDDER_client), + GB_METHOD("Embed", NULL, CEMBEDDER_embed, "(Client)i"), + GB_METHOD("Discard", NULL, CEMBEDDER_discard, NULL), + + EMBEDDER_DESCRIPTION, + + GB_EVENT("Embed", NULL, NULL, &EVENT_Embed), + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Error", NULL, NULL, &EVENT_Error), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CEmbedder.h b/gb.qt4/src/CEmbedder.h new file mode 100644 index 00000000..26fc1bf7 --- /dev/null +++ b/gb.qt4/src/CEmbedder.h @@ -0,0 +1,67 @@ +/*************************************************************************** + + CEmbedder.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CEMBEDDER_H +#define __CEMBEDDER_H + +#include "gambas.h" +#include "CWidget.h" + +#ifndef __CEMBEDDER_CPP +extern GB_DESC CEmbedderDesc[]; +#else + +typedef + struct { + CWIDGET widget; + } + CEMBEDDER; + +#define THIS ((CEMBEDDER *)_object) +#endif + +#ifndef NO_X_WINDOW + +#include +#include + +#define WIDGET ((QX11EmbedContainer *)((CWIDGET *)_object)->widget) + +class CEmbedder : public QObject +{ + Q_OBJECT + +public: + + static CEmbedder manager; + +public slots: + + void error(void); + void embedded(void); + void closed(void); + +}; +#endif + +#endif diff --git a/gb.qt4/src/CFont.cpp b/gb.qt4/src/CFont.cpp new file mode 100644 index 00000000..de57ed55 --- /dev/null +++ b/gb.qt4/src/CFont.cpp @@ -0,0 +1,729 @@ +/*************************************************************************** + + CFont.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFONT_CPP + +#include "gambas.h" +#include "main.h" + +#include + +#include +#include +#include +#include +#include + +#include "CWidget.h" +#include "CDraw.h" +#include "CFont.h" + +#include "gb.form.font.h" + +#ifdef USE_DPI +int CFONT_dpi = 96; +#endif + +static GB_CLASS CLASS_Font; + +static QStringList _families; +static QFontDatabase *_info = 0; + +static void init_font_database() +{ + if (_info) + return; + + _info = new QFontDatabase(); + _families = _info->families(); +} + +static void exit_font_database() +{ + if (_info) + delete _info; +} + + +CFONT *CFONT_create(const QFont &font, FONT_FUNC func, void *object) +{ + CFONT *_object = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + + *(THIS->font) = font; + THIS->func = func; + THIS->object = object; + if (object) + GB.Ref(object); + + return THIS; +} + +void CFONT_set(FONT_FUNC func, void *font, void *object) +{ + if (!font) + { + QFont f; + (*func)(f, object); + } + else + (*func)(*(((CFONT *)font)->font), object); +} + + +double CFONT_size_real_to_virtual(double size) +{ + #ifdef USE_DPI + return size * (double)QPaintDevice::x11AppDpiY() / CFONT_dpi; + #else + return size; + #endif +} + +double CFONT_size_virtual_to_real(double size) +{ + #ifdef USE_DPI + return size * CFONT_dpi / (double)QPaintDevice::x11AppDpiY(); + #else + return size; + #endif +} + + +static void set_font_from_string(CFONT *_object, QString &str) +{ + QStringList list; + QString name, elt, flag; + double size = 0; + bool number; + QFont f; + + // (!) Remove this test later, it is for backward compatibility + + if (str.length()) + { + list = str.split(","); + + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) + { + elt = (*it); + elt = elt.trimmed(); + flag = elt.toUpper(); + size = elt.toDouble(&number); + + if (flag == "BOLD") + { + f.setBold(true); +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) + f.setStyleName("Bold"); +#endif + } + else if (flag == "ITALIC") + f.setItalic(true); + else if (flag == "UNDERLINE") + f.setUnderline(true); + else if (flag == "STRIKEOUT") + f.setStrikeOut(true); + else if (flag[0] == '+' || flag[0] == '-' || flag[0] == '0') + { + //f.setPointSizeFloat((int)(powf(qApp->font().pointSizeFloat(), 1.0 + ((int)size / 10.0)) + 0.5)); + f.setPointSizeF(GRADE_TO_SIZE(size, qApp->font().pointSizeF())); + } + else if (number && size > 0.0) + f.setPointSizeF(SIZE_VIRTUAL_TO_REAL(size)); + else if (elt.length()) + { + f.setBold(false); + f.setItalic(false); + f.setUnderline(false); + f.setStrikeOut(false); + if (elt.startsWith('"') && elt.endsWith('"')) + elt = elt.mid(1, elt.length() - 2); + f.setFamily(elt); +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) + f.setStyleName(""); +#endif + } + } + } + + *(THIS->font) = f; +} + + +BEGIN_METHOD_VOID(Font_init) + + CLASS_Font = GB.FindClass("Font"); + +END_METHOD + + +BEGIN_METHOD_VOID(Font_exit) + + exit_font_database(); + +END_METHOD + + +BEGIN_METHOD(Font_new, GB_STRING font) + + QString s; + + THIS->font = new QFont; + THIS->func = 0; + THIS->object = 0; + + if (!MISSING(font)) + QString s = QSTRING_ARG(font); + + set_font_from_string(THIS, s); + +END_METHOD + + +BEGIN_METHOD_VOID(Font_free) + + if (THIS->object) + GB.Unref(POINTER(&THIS->object)); + delete THIS->font; + +END_METHOD + + +static void CFONT_manage(int prop, CFONT *_object, void *_param) +{ + bool noResize = false; + QFont *f = THIS->font; + double size; + + noResize = true; //((long)THIS->control == CFONT_DRAW && !DRAW_must_resize_font()); + + if (READ_PROPERTY) + { + switch(prop) + { + case CFONT::Name: + GB.ReturnNewZeroString(f->family().toUtf8()); + break; + + case CFONT::Size: + if (noResize) + GB.ReturnFloat(f->pointSizeF()); + else + GB.ReturnFloat(SIZE_REAL_TO_VIRTUAL(f->pointSizeF())); + break; + + case CFONT::Grade: + GB.ReturnInteger(SIZE_TO_GRADE(f->pointSizeF(), qApp->font().pointSizeF())); + break; + + case CFONT::Bold: + GB.ReturnBoolean(f->bold()); + break; + + case CFONT::Italic: + GB.ReturnBoolean(f->italic()); + break; + + case CFONT::Underline: + GB.ReturnBoolean(f->underline()); + break; + + case CFONT::Strikeout: + GB.ReturnBoolean(f->strikeOut()); + break; + } + } + else + { + switch (prop) + { + case CFONT::Name: + f->setFamily(GB.ToZeroString(PROP(GB_STRING))); + break; + + case CFONT::Size: + if (noResize) + size = VPROP(GB_FLOAT); + else + size = SIZE_VIRTUAL_TO_REAL(VPROP(GB_FLOAT)); + + if (size <= 0) + { + GB.Error("Bad font size"); + return; + } + + f->setPointSizeF(size); + break; + + case CFONT::Grade: + { + int g = VPROP(GB_INTEGER); + if (g < FONT_GRADE_MIN) + g = FONT_GRADE_MIN; + else if (g > FONT_GRADE_MAX) + g = FONT_GRADE_MAX; + f->setPointSizeF(GRADE_TO_SIZE(g, qApp->font().pointSizeF())); + } + break; + + case CFONT::Bold: + f->setBold(VPROP(GB_BOOLEAN)); + break; + + case CFONT::Italic: + f->setItalic(VPROP(GB_BOOLEAN)); + break; + + case CFONT::Underline: + f->setUnderline(VPROP(GB_BOOLEAN)); + break; + + case CFONT::Strikeout: + f->setStrikeOut(VPROP(GB_BOOLEAN)); + break; + } + + if (THIS->func) + (*(THIS->func))(*f, THIS->object); + else if (THIS->object) + { + GB_VALUE value; + value.type = GB_T_OBJECT; + value._object.value = THIS; + GB.SetProperty(THIS->object, "Font", &value); + } + + THIS->modified = TRUE; + } +} + + +BEGIN_PROPERTY(Font_Name) + + CFONT_manage(CFONT::Name, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Size) + + CFONT_manage(CFONT::Size, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Grade) + + CFONT_manage(CFONT::Grade, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Bold) + + CFONT_manage(CFONT::Bold, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Italic) + + CFONT_manage(CFONT::Italic, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Underline) + + CFONT_manage(CFONT::Underline, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Strikeout) + + CFONT_manage(CFONT::Strikeout, OBJECT(CFONT), _param); + +END_PROPERTY + + +static void add(QString &str, const QString& data) +{ + if (str.length()) + str += ','; + + str += data; +} + +BEGIN_METHOD_VOID(Font_ToString) + + QFont *f = THIS->font; + QString str; + double size; + QString family; + bool number; + + //str = qfont.family().left(1).upper() + qfont.family().mid(1).lower() + " " + QString::number(qfont.pointSize()); + family = f->family(); + family.toDouble(&number); + if (number) + str = '"' + str + '"'; + add(str, family); + + size = SIZE_REAL_TO_VIRTUAL(f->pointSizeF()); + size = (double)((int)(size * 10 + 0.5)) / 10; + add(str, QString::number(size)); + if (f->bold()) + add(str, "Bold"); + if (f->italic()) + add(str, "Italic"); + if (f->underline()) + add(str, "Underline"); + if (f->strikeOut()) + add(str, "StrikeOut"); + + RETURN_NEW_STRING(str); + +END_METHOD + + +BEGIN_METHOD(Font_get, GB_STRING str) + + CFONT *font; + QString s = QSTRING_ARG(str); + + //qDebug(">> Font_get: %s", s.latin1()); + + font = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + set_font_from_string(font, s); + + GB.ReturnObject(font); + + //qDebug("<< Font_get"); + +END_METHOD + +BEGIN_METHOD_VOID(Font_Copy) + + QFont f; + + f.fromString(THIS->font->toString()); + GB.ReturnObject(CFONT_create(f)); + +END_METHOD + + +BEGIN_PROPERTY(Font_Ascent) + + QFontMetrics fm(*(THIS->font)); + + GB.ReturnInteger(fm.ascent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Descent) + + QFontMetrics fm(*(THIS->font)); + + GB.ReturnInteger(fm.descent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Height) + + QFontMetricsF fm(*(THIS->font)); + + GB.ReturnInteger(fm.lineSpacing()); + +END_PROPERTY + +static void get_text_size(CFONT *_object, QString s, int *w, int *h) +{ + QFontMetricsF fm(*(THIS->font)); + + if (w) + { + QStringList sl; + qreal wt, width = 0; + int i; + + sl = s.split('\n'); + + for (i = 0; i < (int)sl.count(); i++) + { + wt = fm.width(sl[i]); + if (wt > width) width = wt; + } + + *w = ::ceilf(width); + } + + if (h) + { + int nl; + + nl = s.count('\n'); + + *h = ::ceilf(fm.height() * (1 + nl) + fm.leading() * nl); + } +} + +BEGIN_METHOD(Font_TextHeight, GB_STRING text) + + int h; + get_text_size(THIS, QSTRING_ARG(text), NULL, &h); + GB.ReturnInteger(h); + +END_METHOD + + +BEGIN_METHOD(Font_TextWidth, GB_STRING text) + + int w; + get_text_size(THIS, QSTRING_ARG(text), &w, NULL); + GB.ReturnInteger(w); + +END_METHOD + + +BEGIN_METHOD(Font_TextSize, GB_STRING text) + + GEOM_RECT *rect = GEOM.CreateRect(); + get_text_size(THIS, QSTRING_ARG(text), &rect->w, &rect->h); + GB.ReturnObject(rect); + +END_METHOD + + +static void get_rich_text_size(CFONT *_object, char *text, int len, int sw, int *w, int *h) +{ + QTextDocument rt; + + DRAW_init_rich_text(&rt, *(THIS->font)); + + rt.setHtml(QString::fromUtf8((const char *)text, len)); + + if (sw > 0) + rt.setTextWidth(sw); + + if (w) *w = ::ceilf(rt.idealWidth()); + if (h) *h = ::ceilf(rt.size().height()); +} + + +BEGIN_METHOD(Font_RichTextWidth, GB_STRING text; GB_INTEGER width) + + int w; + get_rich_text_size(THIS, STRING(text), LENGTH(text), VARGOPT(width, -1), &w, NULL); + GB.ReturnInteger(w); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextHeight, GB_STRING text; GB_INTEGER width) + + int h; + get_rich_text_size(THIS, STRING(text), LENGTH(text), VARGOPT(width, -1), NULL, &h); + GB.ReturnInteger(h); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextSize, GB_STRING text; GB_INTEGER width) + + GEOM_RECT *rect = GEOM.CreateRect(); + get_rich_text_size(THIS, STRING(text), LENGTH(text), VARGOPT(width, -1), &rect->w, &rect->h); + GB.ReturnObject(rect); + +END_METHOD + + + + +#ifdef USE_DPI +BEGIN_PROPERTY(Font_Resolution) + + if (READ_PROPERTY) + GB.ReturnInteger(CFONT_dpi); + else + { + CFONT_dpi = VPROP(GB_INTEGER); + if (CFONT_dpi < 1) + CFONT_dpi = 96; + } + +END_PROPERTY +#endif + +BEGIN_METHOD_VOID(Fonts_next) + + QString s; + int *index = (int *)GB.GetEnum(); + + if (*index == 0) + init_font_database(); + + if (*index >= _families.count()) + GB.StopEnum(); + else + { + s = _families[*index]; + RETURN_NEW_STRING(s); + (*index)++; + } + +END_METHOD + + +BEGIN_METHOD(Fonts_Exist, GB_STRING family) + + int i; + char *family = GB.ToZeroString(ARG(family)); + + init_font_database(); + + for (i = 0; i < _families.count(); i++) + { + if (_families[i] == family) + { + GB.ReturnBoolean(true); + return; + } + } + + GB.ReturnBoolean(false); + +END_METHOD + + +BEGIN_PROPERTY(Fonts_Count) + + init_font_database(); + GB.ReturnInteger(_families.count()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Fixed) + + init_font_database(); + GB.ReturnBoolean(_info->isFixedPitch(THIS->font->family())); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Scalable) + + init_font_database(); + GB.ReturnBoolean(_info->isSmoothlyScalable(THIS->font->family())); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Styles) + + QStringList styles; + GB_ARRAY array; + int i; + + init_font_database(); + styles = _info->styles(THIS->font->family()); + + GB.Array.New(&array, GB_T_STRING, styles.count()); + for (i = 0; i < styles.count(); i++) + *(char **)GB.Array.Get(array, i) = NEW_STRING(styles[i]); + + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Modified) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->modified); + else + THIS->modified = VPROP(GB_BOOLEAN); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CFontsDesc[] = +{ + GB_DECLARE_STATIC("Fonts"), + + GB_STATIC_METHOD("Exist", "b", Fonts_Exist, "(Family)s"), + GB_STATIC_METHOD("_next", "s", Fonts_next, NULL), + GB_STATIC_PROPERTY_READ("Count", "i", Fonts_Count), + + GB_END_DECLARE +}; + + +GB_DESC CFontDesc[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), + //GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_init", NULL, Font_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Font_exit, NULL), + GB_METHOD("_new", NULL, Font_new, "[(Font)s]"), + GB_METHOD("_free", NULL, Font_free, NULL), + GB_METHOD("Copy", "Font", Font_Copy, NULL), + + GB_PROPERTY("Name", "s", Font_Name), + GB_PROPERTY("Size", "f", Font_Size), + GB_PROPERTY("Grade", "i", Font_Grade), + GB_PROPERTY("Bold", "b", Font_Bold), + GB_PROPERTY("Italic", "b", Font_Italic), + GB_PROPERTY("Underline", "b", Font_Underline), + GB_PROPERTY("Strikeout", "b", Font_Strikeout), + GB_PROPERTY("Modified", "b", Font_Modified), + + GB_METHOD("ToString", "s", Font_ToString, NULL), + + GB_METHOD("TextWidth", "i", Font_TextWidth, "(Text)s"), + GB_METHOD("TextHeight", "i", Font_TextHeight, "(Text)s"), + GB_METHOD("TextSize", "Rect", Font_TextSize, "(Text)s"), + + GB_METHOD("RichTextWidth", "i", Font_RichTextWidth, "(Text)s[(Width)i]"), + GB_METHOD("RichTextHeight", "i", Font_RichTextHeight, "(Text)s[(Width)i]"), + GB_METHOD("RichTextSize", "Rect", Font_RichTextSize, "(Text)s[(Width)i]"), + + GB_STATIC_METHOD("_get", "Font", Font_get, "(Font)s"), + + #ifdef USE_DPI + GB_STATIC_PROPERTY("Resolution", "i", Font_Resolution), + #endif + + GB_PROPERTY_READ("Ascent", "i", Font_Ascent), + GB_PROPERTY_READ("Descent", "i", Font_Descent), + GB_PROPERTY_READ("Height", "i", Font_Height), + GB_PROPERTY_READ("H", "i", Font_Height), + + GB_PROPERTY_READ("Fixed", "b", Font_Fixed), + GB_PROPERTY_READ("Scalable", "b", Font_Scalable), + GB_PROPERTY_READ("Styles", "String[]", Font_Styles), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CFont.h b/gb.qt4/src/CFont.h new file mode 100644 index 00000000..65f1dbc8 --- /dev/null +++ b/gb.qt4/src/CFont.h @@ -0,0 +1,85 @@ +/*************************************************************************** + + CFont.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFONT_H +#define __CFONT_H + +#include + +#include "gambas.h" +//#include "main.h" +#include "CWidget.h" + +#ifndef NO_X_WINDOW +//#define USE_DPI +#endif + +#ifndef __CFONT_CPP +extern GB_DESC CFontDesc[]; +extern GB_DESC CFontsDesc[]; + +#ifdef USE_DPI +extern int CFONT_dpi; +#endif + +#else + +#define THIS OBJECT(CFONT) + +#endif + +typedef + void (*FONT_FUNC)(QFont &, void *); + +typedef + struct { + GB_BASE ob; + QString *family; + } + CFONTINFO; + +typedef + struct { + GB_BASE ob; + QFont *font; + FONT_FUNC func; + void *object; + unsigned modified : 1; + enum { Name, Size, Grade, Bold, Italic, Underline, Strikeout }; + } + CFONT; + +//#define CFONT_NORMAL 0 +//#define CFONT_APPLICATION 1 +//#define CFONT_DRAW 2 + +CFONT *CFONT_create(const QFont &font, FONT_FUNC func = 0, void *object = 0); +void CFONT_set(FONT_FUNC func, void *font, void *object); +//CFONT *CFONT_create_control(CWIDGET *control); +double CFONT_size_real_to_virtual(double); +double CFONT_size_virtual_to_real(double); + +#define SIZE_REAL_TO_VIRTUAL(_size) CFONT_size_real_to_virtual((double)(_size)) +#define SIZE_VIRTUAL_TO_REAL(_size) CFONT_size_virtual_to_real((double)(_size)) + +#endif diff --git a/gb.qt4/src/CImage.cpp b/gb.qt4/src/CImage.cpp new file mode 100644 index 00000000..f4d017dc --- /dev/null +++ b/gb.qt4/src/CImage.cpp @@ -0,0 +1,348 @@ +/*************************************************************************** + + CImage.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include + +#include +#include +#include +#include +#include + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif +#include + +#include "gambas.h" +#include "main.h" + +#include "CScreen.h" +#include "CPicture.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CImage.h" + +const char *CIMAGE_get_format(QString path) +{ + int pos; + + pos = path.lastIndexOf('.'); + if (pos < 0) + return NULL; + + path = path.mid(pos + 1).toLower(); + + if (path == "png") + return "PNG"; + else if (path == "jpg" || path == "jpeg") + return "JPEG"; + else if (path == "gif") + return "GIF"; + else if (path == "bmp") + return "BMP"; + else if (path == "xpm") + return "XPM"; + else + return NULL; +} + + +/******************************************************************************* + + Image + +*******************************************************************************/ + +static void free_image(GB_IMG *img, void *image) +{ + delete (QImage *)image; +} + +static void *temp_image(GB_IMG *img) +{ + QImage *image; + + if (!img->data) + image = new QImage(); + else + image = new QImage((uchar *)img->data, img->width, img->height, QImage::Format_ARGB32_Premultiplied); + + return image; +} + +static GB_IMG_OWNER _image_owner = { + QT_NAME, + GB_IMAGE_BGRP, + free_image, + free_image, + temp_image, + NULL, + }; + +QImage *CIMAGE_get(CIMAGE *_object) +{ + return (QImage *)IMAGE.Check(THIS_IMAGE, &_image_owner); +} + +#define check_image CIMAGE_get + +static void take_image(CIMAGE *_object, QImage *image) +{ + bool d = image->isDetached(); + //qDebug("take_image: %d x %d / %d [%c]", image->width(), image->height(), image->bytesPerLine(), image->isDetached() ? 'D' : '+'); + const uchar *data = ((const QImage *)image)->bits(); + //qDebug("take_image: [%c]", image->isDetached() ? 'D' : '+'); + if (image->isDetached() != d) + qDebug("image has been detached! %d x %d", image->width(), image->height()); + IMAGE.Take(THIS_IMAGE, &_image_owner, image, image->width(), image->height(), (uchar *)data); +} + +CIMAGE *CIMAGE_create(QImage *image) +{ + CIMAGE *img; + static GB_CLASS class_id = 0; + + if (!class_id) + class_id = GB.FindClass("Image"); + + img = (CIMAGE *)GB.New(class_id, NULL, NULL); + + if (image) + { + if (!image->isNull() && image->format() != QImage::Format_ARGB32_Premultiplied) + *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); + take_image(img, image); + } + else + take_image(img, new QImage()); + + return img; +} + +BEGIN_PROPERTY(Image_Picture) + + CPICTURE *pict; + QImage img; + + check_image(THIS); + + pict = (CPICTURE *)GB.New(GB.FindClass("Picture"), NULL, NULL); + if (!QIMAGE->isNull()) + { + img = *QIMAGE; + img.detach(); + *pict->pixmap = QPixmap::fromImage(img); + } + + GB.ReturnObject(pict); + +END_PROPERTY + +BEGIN_METHOD(Image_Load, GB_STRING path) + + QImage *p; + CIMAGE *img; + + if (CPICTURE_load_image(&p, STRING(path), LENGTH(path))) + { + //p->convertToFormat(QImage::Format_ARGB32); + img = CIMAGE_create(p); + GB.ReturnObject(img); + } + else + GB.Error("Unable to load image"); + +END_METHOD + +BEGIN_METHOD(Image_FromString, GB_STRING data) + + QImage *p; + CIMAGE *img; + + if (CPICTURE_from_string(&p, STRING(data), LENGTH(data))) + { + //p->convertToFormat(QImage::Format_ARGB32); + img = CIMAGE_create(p); + GB.ReturnObject(img); + } + else + GB.Error("Unable to load image"); + +END_METHOD + +BEGIN_METHOD(Image_Save, GB_STRING path; GB_INTEGER quality) + + QString path = TO_QSTRING(GB.FileName(STRING(path), LENGTH(path))); + bool ok = false; + const char *fmt = CIMAGE_get_format(path); + + if (!fmt) + { + GB.Error("Unknown format"); + return; + } + + check_image(THIS); + + ok = QIMAGE->save(path, fmt, VARGOPT(quality, -1)); + + if (!ok) + GB.Error("Unable to save picture"); + +END_METHOD + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height; GB_BOOLEAN fast) + + //static int count = 0; + QImage *stretch; + int w, h; + + check_image(THIS); + + stretch = new QImage(); + + if (!QIMAGE->isNull()) + { + w = VARG(width); + h = VARG(height); + + if (w < 0 && h > 0) + w = QIMAGE->width() * h / QIMAGE->height(); + else if (h < 0 && w > 0) + h = QIMAGE->height() * w / QIMAGE->width(); + + if (w > 0 && h > 0) + { + *stretch = QIMAGE->scaled(w, h, Qt::IgnoreAspectRatio, VARGOPT(fast, FALSE) ? Qt::FastTransformation : Qt::SmoothTransformation); + stretch->detach(); + } + } + + GB.ReturnObject(CIMAGE_create(stretch)); + +END_METHOD + + +BEGIN_METHOD(Image_Rotate, GB_FLOAT angle) + + QImage *rotate = new QImage(); + double angle = VARG(angle); + + check_image(THIS); + + if (angle != 0.0) + { +#ifdef QT5 + QTransform mat; +#else + QMatrix mat; +#endif + mat.rotate(VARG(angle) * -360.0 / 2 / M_PI); + *rotate = QIMAGE->transformed(mat, Qt::SmoothTransformation); + } + else + *rotate = QIMAGE->copy(); + + GB.ReturnObject(CIMAGE_create(rotate)); + +END_METHOD + + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT img; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + int x, y, w, h, sx, sy, sw, sh; + CIMAGE *image = (CIMAGE *)VARG(img); + QImage *src, *dst; + double scale_x, scale_y; + + if (GB.CheckObject(image)) + return; + + src = check_image(image); + dst = check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, -1); + sh = VARGOPT(sh, -1); + + DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, src->width(), src->height()); + + if (w != sw || h != sh) + { + QImage tmp; + + scale_x = (double)w / sw; + scale_y = (double)h / sh; + + tmp = src->scaled((int)(src->width() * scale_x + 0.5), (int)(src->height() * scale_y + 0.5), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + sx = (int)(sx * scale_x + 0.5); + sy = (int)(sy * scale_y + 0.5); + sw = w; + sh = h; + + QPainter p(dst); + p.drawImage(x, y, tmp, sx, sy, sw, sh); + p.end(); + } + else + { + QPainter p(dst); + p.drawImage(x, y, *src, sx, sy, sw, sh); + p.end(); + } + +END_METHOD + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Image", Image_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Image_Save, "(Path)s[(Quality)i]"), + + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i[(Fast)b]"), + GB_METHOD("Rotate", "Image", Image_Rotate, "(Angle)f"), + + GB_METHOD("PaintImage", NULL, Image_PaintImage, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_PROPERTY_READ("Picture", "Picture", Image_Picture), + + GB_INTERFACE("Paint", &PAINT_Interface), + GB_INTERFACE("PaintMatrix", &PAINT_MATRIX_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CImage.h b/gb.qt4/src/CImage.h new file mode 100644 index 00000000..c302da16 --- /dev/null +++ b/gb.qt4/src/CImage.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include +#include + +#include "gambas.h" +#include "gb.image.h" + +typedef + struct { + GB_IMG img; + } + CIMAGE; + +#ifndef __CIMAGE_CPP + +extern GB_DESC CImageDesc[]; + +#else + +#define THIS OBJECT(CIMAGE) +#define THIS_IMAGE (&THIS->img) +#define QIMAGE ((QImage *)THIS_IMAGE->temp_handle) +#define GET_QIMAGE(_image) ((QImage *)(_image->img.temp_handle)) + +#endif + +const char *CIMAGE_get_format(QString path); +CIMAGE *CIMAGE_create(QImage *image); +QImage *CIMAGE_get(CIMAGE *); + +#endif diff --git a/gb.qt4/src/CKey.cpp b/gb.qt4/src/CKey.cpp new file mode 100644 index 00000000..9fb2e03c --- /dev/null +++ b/gb.qt4/src/CKey.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + + CKey.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CKEY_CPP + + +#include "gambas.h" + +#include "main.h" +#include "CKey.h" + +CKEY_INFO CKEY_info = { 0 }; + +void CKEY_clear(int valid) +{ + if (valid) + CKEY_info.valid++; + else + CKEY_info.valid--; + + if (CKEY_info.valid == 0) + { + GB.FreeString(&CKEY_info.text); + CKEY_info = { 0 }; + } +} + +BEGIN_METHOD_VOID(CKEY_exit) + + GB.FreeString(&CKEY_info.text); + +END_METHOD + + +BEGIN_METHOD(CKEY_get, GB_STRING key) + + char *str = GB.ToZeroString(ARG(key)); + QKeySequence ks(str); + +#if QT_VERSION <= 0x030005 + GB.ReturnInteger(ks); +#else + GB.ReturnInteger(ks[0] & ~Qt::UNICODE_ACCEL); +#endif + +END_METHOD + +#define CHECK_VALID() \ + if (!CKEY_is_valid()) \ + { \ + GB.Error("No keyboard event data"); \ + return; \ + } + +BEGIN_PROPERTY(Key_Text) + + CHECK_VALID(); + GB.ReturnString(CKEY_info.text); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Code) + + CHECK_VALID(); + + /*switch(CKEY_info.code) + { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + GB.ReturnInteger(0); + break; + + default:*/ + GB.ReturnInteger(CKEY_info.code); + //} + +END_PROPERTY + +BEGIN_PROPERTY(Key_State) + + CHECK_VALID(); + GB.ReturnInteger(CKEY_info.state); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shift) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::ShiftModifier); // || (CKEY_info.code == Qt::Key_Shift)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Control) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::ControlModifier); // || (CKEY_info.code == Qt::Key_Control)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Alt) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::AltModifier); // || (CKEY_info.code == Qt::Key_Alt)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Meta) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::MetaModifier); // || (CKEY_info.code == Qt::Key_Meta)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Normal) + + CHECK_VALID(); + GB.ReturnBoolean((CKEY_info.state & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) == 0); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shortcut) + + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + init = TRUE; + GB.GetFunction(&func, (void *)GB.FindClass("Shortcut"), "FromKey", NULL, "s"); + } + + if (GB_FUNCTION_IS_VALID(&func)) + GB.Call(&func, 0, FALSE); + else + GB.ReturnNull(); + +END_PROPERTY + +GB_DESC CKeyDesc[] = +{ + GB_DECLARE("Key", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", "i", CKEY_get, "(Key)s"), + GB_STATIC_METHOD("_exit", NULL, CKEY_exit, NULL), + + GB_CONSTANT("Esc", "i", Qt::Key_Escape), + GB_CONSTANT("Escape", "i", Qt::Key_Escape), + GB_CONSTANT("Tab", "i", Qt::Key_Tab), + GB_CONSTANT("BackTab", "i", Qt::Key_Backtab), + GB_CONSTANT("Backspace", "i", Qt::Key_Backspace), + GB_CONSTANT("Return", "i", Qt::Key_Return), + GB_CONSTANT("Enter", "i", Qt::Key_Enter), + GB_CONSTANT("Ins", "i", Qt::Key_Insert), + GB_CONSTANT("Del", "i", Qt::Key_Delete), + GB_CONSTANT("Insert", "i", Qt::Key_Insert), + GB_CONSTANT("Delete", "i", Qt::Key_Delete), + GB_CONSTANT("Pause", "i", Qt::Key_Pause), + GB_CONSTANT("Print", "i", Qt::Key_Print), + GB_CONSTANT("SysReq", "i", Qt::Key_SysReq), + GB_CONSTANT("Home", "i", Qt::Key_Home), + GB_CONSTANT("End", "i", Qt::Key_End), + GB_CONSTANT("Left", "i", Qt::Key_Left), + GB_CONSTANT("Up", "i", Qt::Key_Up), + GB_CONSTANT("Right", "i", Qt::Key_Right), + GB_CONSTANT("Down", "i", Qt::Key_Down), + GB_CONSTANT("PgUp", "i", Qt::Key_PageUp), + GB_CONSTANT("PgDown", "i", Qt::Key_PageDown), + GB_CONSTANT("PageUp", "i", Qt::Key_PageUp), + GB_CONSTANT("PageDown", "i", Qt::Key_PageDown), + GB_CONSTANT("ShiftKey", "i", Qt::Key_Shift), + GB_CONSTANT("ControlKey", "i", Qt::Key_Control), + GB_CONSTANT("MetaKey", "i", Qt::Key_Meta), + GB_CONSTANT("AltKey", "i", Qt::Key_Alt), + GB_CONSTANT("AltGrKey", "i", Qt::Key_AltGr), + GB_CONSTANT("CapsLock", "i", Qt::Key_CapsLock), + GB_CONSTANT("NumLock", "i", Qt::Key_NumLock), + GB_CONSTANT("ScrollLock", "i", Qt::Key_ScrollLock), + GB_CONSTANT("F1", "i", Qt::Key_F1), + GB_CONSTANT("F2", "i", Qt::Key_F2), + GB_CONSTANT("F3", "i", Qt::Key_F3), + GB_CONSTANT("F4", "i", Qt::Key_F4), + GB_CONSTANT("F5", "i", Qt::Key_F5), + GB_CONSTANT("F6", "i", Qt::Key_F6), + GB_CONSTANT("F7", "i", Qt::Key_F7), + GB_CONSTANT("F8", "i", Qt::Key_F8), + GB_CONSTANT("F9", "i", Qt::Key_F9), + GB_CONSTANT("F10", "i", Qt::Key_F10), + GB_CONSTANT("F11", "i", Qt::Key_F11), + GB_CONSTANT("F12", "i", Qt::Key_F12), + GB_CONSTANT("F13", "i", Qt::Key_F13), + GB_CONSTANT("F14", "i", Qt::Key_F14), + GB_CONSTANT("F15", "i", Qt::Key_F15), + GB_CONSTANT("F16", "i", Qt::Key_F16), + GB_CONSTANT("F17", "i", Qt::Key_F17), + GB_CONSTANT("F18", "i", Qt::Key_F18), + GB_CONSTANT("F19", "i", Qt::Key_F19), + GB_CONSTANT("F20", "i", Qt::Key_F20), + GB_CONSTANT("F21", "i", Qt::Key_F21), + GB_CONSTANT("F22", "i", Qt::Key_F22), + GB_CONSTANT("F23", "i", Qt::Key_F23), + GB_CONSTANT("F24", "i", Qt::Key_F24), + GB_CONSTANT("Menu", "i", Qt::Key_Menu), + GB_CONSTANT("Help", "i", Qt::Key_Help), + GB_CONSTANT("Space", "i", Qt::Key_Space), + + GB_STATIC_PROPERTY_READ("Text", "s", Key_Text), + GB_STATIC_PROPERTY_READ("Code", "i", Key_Code), + GB_STATIC_PROPERTY_READ("State", "i", Key_State), + GB_STATIC_PROPERTY_READ("Shift", "b", Key_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Key_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Key_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Key_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Key_Normal), + + GB_STATIC_PROPERTY_READ("Shortcut", "s", Key_Shortcut), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CKey.h b/gb.qt4/src/CKey.h new file mode 100644 index 00000000..08a91fc9 --- /dev/null +++ b/gb.qt4/src/CKey.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + CKey.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CKEY_H +#define __CKEY_H + +#include "gambas.h" + +#include + +typedef + struct { + int valid; + char *text; + int code; + Qt::KeyboardModifiers state; + int cancel; + bool release; + } + CKEY_INFO; + +#ifndef __CKEY_CPP +extern GB_DESC CKeyDesc[]; +extern CKEY_INFO CKEY_info; +#endif + +void CKEY_clear(int valid); + +#define CKEY_is_valid() (CKEY_info.valid != 0) + +#endif diff --git a/gb.qt4/src/CMenu.cpp b/gb.qt4/src/CMenu.cpp new file mode 100644 index 00000000..3c2bea91 --- /dev/null +++ b/gb.qt4/src/CMenu.cpp @@ -0,0 +1,1276 @@ +/*************************************************************************** + + CMenu.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMENU_CPP + +#include +#include +#include +#include + +#include "gambas.h" +#include "gb_common.h" + +#include "CWidget.h" +#include "CWindow.h" +#include "CMenu.h" + +#include "main.h" +#ifdef QT5 +#include +#endif + +//#define DEBUG_MENU 1 + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); + +DECLARE_METHOD(Control_Window); +DECLARE_METHOD(Control_Name); +DECLARE_METHOD(Menu_Hide); + +static bool _popup_immediate = false; +static CMENU *_popup_menu_clicked = NULL; +int MENU_popup_count = 0; + +static void clear_menu(CMENU *_object); + +static GB_FUNCTION _init_shortcut_func; +static GB_FUNCTION _init_menubar_shortcut_func; + +#define EXT(_ob) ((CMENU_EXT *)((CWIDGET *)_ob)->ext) +#define ENSURE_EXT(_ob) (EXT(_ob) ? EXT(_ob) : alloc_ext((CMENU *)(_ob))) + +#define HANDLE_PROXY(_ob) \ + while (EXT(_ob) && EXT(_ob)->proxy) \ + _ob = (__typeof__ _ob)(EXT(_ob)->proxy); + +static CMENU_EXT *alloc_ext(CMENU *_object) +{ + GB.Alloc(POINTER(&(THIS->widget.ext)), sizeof(CMENU_EXT)); + THIS_EXT->proxy = NULL; + THIS_EXT->action = NULL; + THIS_EXT->tag.type = GB_T_NULL; + return THIS_EXT; +} + + +static void register_proxy(void *_object, CMENU *proxy) +{ + void *check = proxy; + CMENU *old_proxy = NULL; + + while (check) + { + if (check == THIS) + { + GB.Error("Circular proxy chain"); + return; + } + check = EXT(check) ? EXT(check)->proxy : NULL; + } + + //if (THIS_EXT && THIS_EXT->proxy && EXT(THIS_EXT->proxy)) + // EXT(THIS_EXT->proxy)->proxy_for = NULL; + + if (THIS_EXT && THIS_EXT->proxy) + { + old_proxy = (CMENU *)THIS_EXT->proxy; + THIS_EXT->proxy = NULL; + } + + if (proxy) + { + GB.Ref(proxy); + ENSURE_EXT(THIS)->proxy = proxy; + } + + if (ACTION) + { + if (!proxy || !proxy->menu) + ACTION->setMenu(THIS->menu); + else + ACTION->setMenu(proxy->menu); + + if (old_proxy) + ((QAction *)old_proxy->widget.widget)->setMenu(old_proxy->menu); + } + + if (old_proxy) + GB.Unref(POINTER(&old_proxy)); +} + +static int check_menu(void *_object) +{ + return THIS->deleted || ACTION == 0; +} + +static void refresh_menubar(CMENU *menu) +{ + int i; + QList list; + QAction *action; + MyMainWindow *toplevel; + CWINDOW *window; + QMenuBar *menuBar; + + if (!CMENU_is_toplevel(menu)) + return; + + toplevel = (MyMainWindow *)(menu->toplevel); + window = ((CWINDOW *)(menu->parent)); + menuBar = window->menuBar; + if (!menuBar) + return; + + list = menuBar->actions(); + + for (i = 0; i < list.count(); i++) + { + action = list.at(i); + menu = CMenu::dict[action]; + if (!menu || menu->deleted) + continue; + if (action->isVisible() && !action->isSeparator()) + break; + } + + window->hideMenuBar = i == list.count(); + + toplevel->configure(); +} + +static void unregister_menu(CMENU *_object) +{ + if (THIS_EXT && THIS_EXT->action) + { + CACTION_register((CWIDGET *)THIS, THIS_EXT->action, NULL); + GB.FreeString(&THIS_EXT->action); + } +} + +static void set_menu_visible(void *_object, bool v) +{ + THIS->visible = v; + ACTION->setVisible(v); + refresh_menubar(THIS); + //update_accel_recursive(THIS); +} + +static void delete_menu(CMENU *_object) +{ + if (THIS->deleted) + return; + + //qDebug("delete_menu: %s %p action %p", THIS->widget.name, THIS, ACTION); + + THIS->deleted = true; + + register_proxy(THIS, NULL); + + clear_menu(THIS); + + if (THIS->menu) + { + //GB.Post((GB_CALLBACK)delete_later, (intptr_t)THIS->menu); + THIS->menu->deleteLater(); + THIS->menu = 0; + } + + if (THIS->accel) + delete THIS->accel; + + if (ACTION) + { + refresh_menubar(THIS); + delete ACTION; + } + +// if (ACTION) +// { +// QAction *action = ACTION; +// THIS->widget.widget = 0; +// delete action; +// } +} + +static void clear_menu(CMENU *_object) +{ + int i; + CMENU *menu; + + if (THIS->menu) + { + QList list = THIS->menu->actions(); + + for (i = 0; i < list.count(); i++) + { + menu = CMenu::dict[list.at(i)]; + //GB.Ref(menu); + //delete ((QAction *)(menu->widget.widget)); + if (menu) + delete_menu(menu); + //GB.Unref(POINTER(&menu)); + } + + THIS->init_shortcut = FALSE; + } +} + +static bool is_fully_enabled(CMENU *_object) +{ + for(;;) + { + if (THIS->exec) + return true; + + if (THIS->disabled) + return false; + + if (CMENU_is_toplevel(THIS)) + return true; + + _object = (CMENU *)THIS->parent; + } +} + +static void update_accel(CMENU *_object) +{ + if (CMENU_is_toplevel(THIS)) + return; + + if (THIS->accel && !THIS->accel->isEmpty() && is_fully_enabled(THIS)) + { + //if (!qstrcmp(THIS->widget.name, "mnuCopy")) + // qDebug("update_accel: %s: %s", THIS->widget.name, (const char *)THIS->accel->toString().toUtf8()); + ACTION->setShortcut(*(THIS->accel)); + } + else + { + //if (!qstrcmp(THIS->widget.name, "mnuCopy")) + // qDebug("update_accel: %s: NULL", THIS->widget.name); + ACTION->setShortcut(QKeySequence()); + } +} + +static void update_accel_recursive(CMENU *_object) +{ + if (THIS->exec) + return; + + update_accel(THIS); + + if (THIS->menu) + { + int i; + + for (i = 0; i < THIS->menu->actions().count(); i++) + update_accel_recursive(CMenu::dict[THIS->menu->actions().at(i)]); + } +} + +static void update_check(CMENU *_object) +{ + if (THIS->checked || THIS->toggle || THIS->radio) + { + ACTION->setCheckable(true); + ACTION->setChecked(THIS->checked); + } + else + { + ACTION->setCheckable(false); + ACTION->setChecked(false); + } +} + +#if 0 +static void toggle_menu(CMENU *_object) +{ + if (CMENU_is_toplevel(THIS)) + return; + + qDebug("toggle_menu: %s %d", TO_UTF8(ACTION->text()), ACTION->isChecked()); + + //ACTION->setCheckable(true); + ACTION->setChecked(!ACTION->isChecked()); + //ACTION->setCheckable(false); + + qDebug("--> %d", ACTION->isChecked()); +} +#endif + +static void update_action_group(QMenu *parent) +{ + int i; + QAction *action; + CMENU *menu; + QActionGroup *group = NULL; + + for (i = 0; i < parent->actions().count(); i++) + { + action = parent->actions().at(i); + menu = CMenu::dict[action]; + if (!menu || menu->deleted) + continue; + + if (menu->radio) + { + if (!group) + { + if (action->actionGroup()) + group = action->actionGroup(); + else + group = new QActionGroup(parent); + } + action->setActionGroup(group); + } + else + { + group = NULL; + action->setActionGroup(NULL); + } + + //qDebug("action '%s' -> %p", TO_UTF8(action->text()), (void *)group); + } +} + +//--------------------------------------------------------------------------- + +MyAction::MyAction(QObject *parent): QAction(parent) +{ +} + +bool MyAction::event(QEvent *e) +{ + if (e->type() == QEvent::Shortcut) + { + activate(Trigger); + return true; + } + + return QAction::event(e); +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Menu_new, GB_OBJECT parent; GB_BOOLEAN hidden) + + QAction *action; + void *parent = VARG(parent); + QWidget *topLevel = 0; + QMenuBar *menuBar = 0; + + //printf("Menu_new %p\n", _object); + + if (GB.CheckObject(parent)) + return; + + //qDebug("Menu_new: (%s %p)", GB.GetClassName(THIS), THIS); + + if (GB.Is(parent, CLASS_Menu)) + { + CMENU *menu = (CMENU *)parent; + + topLevel = menu->toplevel; + + if (!menu->menu) + { + #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(5,5,0) + menu->menu = new MyMenu(); + #else + menu->menu = new QMenu(); + #endif + + menu->menu->setSeparatorsCollapsible(true); + ((QAction *)(menu->widget.widget))->setMenu(menu->menu); + + //QObject::connect(menu->menu, SIGNAL(triggered(QAction *)), &CMenu::manager, SLOT(slotTriggered(QAction *))); + QObject::connect(menu->menu, SIGNAL(aboutToShow()), &CMenu::manager, SLOT(slotShown())); + QObject::connect(menu->menu, SIGNAL(aboutToHide()), &CMenu::manager, SLOT(slotHidden())); + } + + action = new MyAction(menu->menu); + action->setSeparator(true); + QObject::connect(action, SIGNAL(toggled(bool)), &CMenu::manager, SLOT(slotToggled(bool))); + QObject::connect(action, SIGNAL(destroyed()), &CMenu::manager, SLOT(slotDestroyed())); + QObject::connect(action, SIGNAL(triggered()), &CMenu::manager, SLOT(slotTriggered())); + + menu->menu->addAction(action); + //qDebug("New action %p for Menu %p", action, THIS); + } + else if (GB.Is(parent, CLASS_Window)) + { + CWINDOW *window = (CWINDOW *)parent; + + topLevel = QWIDGET(CWidget::getWindow((CWIDGET *)parent)); + menuBar = window->menuBar; + if (!menuBar) + { + menuBar = new QMenuBar(topLevel); + menuBar->setNativeMenuBar(false); + //menuBar->setAutoFillBackground(true); + window->menuBar = menuBar; + } + + action = new MyAction(menuBar); + menuBar->addAction(action); + + action->setSeparator(true); + QObject::connect(action, SIGNAL(destroyed()), &CMenu::manager, SLOT(slotDestroyed())); + + //qDebug("New action %p for top level Menu %p", action, THIS); + } + else + { + GB.Error("Type mismatch. The parent control of a Menu must be a Window or another Menu."); + return; + } + + THIS->widget.widget = (QWidget *)action; + CMenu::dict.insert(action, THIS); + set_menu_visible(THIS, !VARGOPT(hidden, FALSE)); + + THIS->parent = parent; + THIS->widget.name = NULL; + THIS->picture = NULL; + THIS->deleted = false; + + CWIDGET_init_name((CWIDGET *)THIS); + +#ifdef DEBUG_MENU + //qDebug("Menu_new: item = %p (%d) parent = %p (%d) toplevel = %p", item, item->id, item->parent, item->parent ? item->parent->id : 0, item->toplevel); +#endif + + THIS->toplevel = topLevel; + refresh_menubar(THIS); + //qDebug("*** Menu_new %p", _object); + GB.Ref(THIS); + + //qDebug("Menu_new: (%s %p)", THIS->widget.name, THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_free) + +#ifdef DEBUG_MENU + qDebug("Menu_free: item = %p", THIS); +#endif + + //qDebug("Menu_free: (%s %p)", THIS->widget.name, THIS); + + delete_menu(THIS); + + GB.StoreObject(NULL, POINTER(&(THIS->picture))); + + if (THIS_EXT) + { + //CACTION_register(THIS, THIS_EXT->action, NULL); + //GB.FreeString(&THIS_EXT->action); + + /*if (THIS_EXT->proxy) + EXT(THIS_EXT->proxy)->proxy_for = NULL; + if (THIS_EXT->proxy_for) + EXT(THIS_EXT->proxy_for)->proxy = NULL;*/ + + GB.StoreVariant(NULL, &THIS_EXT->tag); + GB.FreeString(&THIS_EXT->action); + + GB.Free(POINTER(&THIS->widget.ext)); + } + + //qDebug("free_name: %s %p (Menu_free)", THIS->widget.name, THIS->widget.name); + //if (!strcmp(THIS->widget.name, "mnuCut")) + // BREAKPOINT(); + GB.FreeString(&THIS->widget.name); + GB.FreeString(&THIS->save_text); + + #ifdef DEBUG_MENU + qDebug("Menu_free: item = %p is freed!", THIS); + #endif + +END_METHOD + + +BEGIN_PROPERTY(Menu_Text) + + if (READ_PROPERTY) + { + if (THIS->save_text) + GB.ReturnString(THIS->save_text); + else + RETURN_NEW_STRING(ACTION->text()); + } + else + { + QString text = QSTRING_PROP(); + ACTION->setText(text); + ACTION->setSeparator(text.isEmpty()); + refresh_menubar(THIS); + + if (!CMENU_is_toplevel(THIS)) + ((CMENU *)THIS->parent)->init_shortcut = FALSE; + + GB.FreeString(&THIS->save_text); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + QIcon icon; + + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->picture))); + if (THIS->picture) + icon = QIcon(*THIS->picture->pixmap); + ACTION->setIcon(icon); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(!THIS->disabled); + else + { + bool b = VPROP(GB_BOOLEAN); + THIS->disabled = !b; + ACTION->setEnabled(b); + update_accel_recursive(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Menu_Checked) + + if (CMENU_is_toplevel(THIS)) + { + if (READ_PROPERTY) + GB.ReturnBoolean(0); + } + else + { + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->checked); + } + else + { + THIS->checked = VPROP(GB_BOOLEAN); + update_check(THIS); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->toggle); + else + { + THIS->toggle = VPROP(GB_BOOLEAN); + update_check(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->radio); + else if (THIS->radio != VPROP(GB_BOOLEAN)) + { + THIS->radio = VPROP(GB_BOOLEAN); + if (!CMENU_is_toplevel(THIS)) + update_action_group(((CMENU *)THIS->parent)->menu); + update_check(THIS); + } + +END_PROPERTY + + +static void send_click_event(CMENU *_object); + +BEGIN_PROPERTY(Menu_Value) + + if (THIS->toggle || THIS->radio) + { + Menu_Checked(_object, _param); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(0); + } + else if (!CMENU_is_toplevel(THIS)) + { + GB.Ref(THIS); + send_click_event(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Shortcut) + + if (CMENU_is_toplevel(THIS) || THIS->menu) + { + if (READ_PROPERTY) + GB.ReturnVoidString(); + + return; + } + + if (READ_PROPERTY) + GB.ReturnNewZeroString(THIS->accel ? (const char *)THIS->accel->toString().toUtf8() : NULL); + else + { + if (THIS->accel) + delete THIS->accel; + THIS->accel = new QKeySequence; + *(THIS->accel) = QKeySequence::fromString(QSTRING_PROP()); + + update_accel(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->visible); + else + set_menu_visible(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Menu_Show) + + set_menu_visible(THIS, true); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Hide) + + set_menu_visible(THIS, false); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Delete) + + delete_menu(THIS); + +END_METHOD + + +BEGIN_PROPERTY(MenuChildren_Count) + + if (THIS->menu) + GB.ReturnInteger(THIS->menu->actions().count()); + else + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD_VOID(MenuChildren_next) + + int index; + + if (!THIS->menu) + { + GB.StopEnum(); + return; + } + + index = ENUM(int); + + if (index >= THIS->menu->actions().count()) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menu->actions().at(index)]); + + ENUM(int) = index + 1; + +END_METHOD + + +BEGIN_METHOD(MenuChildren_get, GB_INTEGER index) + + int index = VARG(index); + + if (!THIS->menu || index < 0 || index >= THIS->menu->actions().count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menu->actions().at(index)]); + +END_METHOD + + +BEGIN_METHOD_VOID(MenuChildren_Clear) + + clear_menu(THIS); + +END_METHOD + +void CMENU_popup(CMENU *_object, const QPoint &pos) +{ + bool disabled; + void *save; + + HANDLE_PROXY(_object); + + if (THIS->menu && !THIS->exec) + { + disabled = THIS->disabled; + if (disabled) + { + THIS->disabled = false; + update_accel_recursive(THIS); + THIS->disabled = true; + } + + //qDebug("CMENU_popup: %p: open", THIS); + + // The Click event is posted, it does not occur immediately. + save = CWIDGET_enter_popup(); + THIS->exec = true; + _popup_immediate = true; + THIS->menu->exec(pos); + _popup_immediate = false; + THIS->exec = false; + CWIDGET_leave_popup(save); + + //qDebug("_popup_menu_clicked = %p", _popup_menu_clicked); + update_accel_recursive(THIS); + + //CWIDGET_check_hovered(); + + //qDebug("CMENU_popup: %p: close", THIS); + + if (_popup_menu_clicked) + { + CMENU *menu = _popup_menu_clicked; + _popup_menu_clicked = NULL; + send_click_event(menu); + //GB.Unref(POINTER(&menu)); + } + + //qDebug("CMENU_popup: %p: end", THIS); + + MENU_popup_count++; + + //MyMainWindow *toplevel = (MyMainWindow *)(THIS->toplevel); + //CWINDOW_fix_menubar((CWINDOW *)CWidget::get(toplevel)); + } +} + +BEGIN_METHOD(Menu_Popup, GB_INTEGER x; GB_INTEGER y) + + QPoint pos; + + if (MISSING(x) || MISSING(y)) + pos = QCursor::pos(); + else + pos = QPoint(VARG(x), VARG(y)); + + CMENU_popup(THIS, pos); + +END_METHOD + +BEGIN_METHOD_VOID(Menu_Close) + + HANDLE_PROXY(_object); + if (THIS->menu) + THIS->menu->close(); + +END_METHOD + +BEGIN_PROPERTY(Menu_Window) + + GB.ReturnObject(CWidget::get(THIS->toplevel)); + +END_PROPERTY + +BEGIN_PROPERTY(Menu_Parent) + + if (CMENU_is_toplevel(THIS)) + GB.ReturnNull(); + else + GB.ReturnObject(THIS->parent); + +END_PROPERTY + +BEGIN_PROPERTY(Menu_Action) + + char *current = THIS_EXT ? THIS_EXT->action : NULL; + + if (READ_PROPERTY) + GB.ReturnString(current); + else + { + char *action = PLENGTH() ? GB.NewString(PSTRING(), PLENGTH()) : NULL; + + CACTION_register(THIS, current, action); + + if (THIS_EXT) + GB.FreeString(&THIS_EXT->action); + + if (action) + ENSURE_EXT(THIS)->action = action; + } +END_PROPERTY + + +BEGIN_PROPERTY(Menu_SaveText) + + if (READ_PROPERTY) + GB.ReturnString(THIS->save_text); + else + GB.StoreString(PROP(GB_STRING), &THIS->save_text); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Proxy) + + if (READ_PROPERTY) + GB.ReturnObject(THIS_EXT ? THIS_EXT->proxy : NULL); + else + { + CMENU *menu = (CMENU *)VPROP(GB_OBJECT); + + if (menu && GB.CheckObject(menu)) + return; + + register_proxy(THIS, menu); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Tag) + + if (READ_PROPERTY) + { + if (THIS_EXT) + GB.ReturnVariant(&THIS_EXT->tag); + else + { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } + } + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&(ENSURE_EXT(THIS)->tag))); + +END_METHOD + + +BEGIN_PROPERTY(Menu_Closed) + + HANDLE_PROXY(_object); + GB.ReturnBoolean(!THIS->opened); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CMenuChildrenDesc[] = +{ + GB_DECLARE(".Menu.Children", sizeof(CMENU)), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", MenuChildren_next, NULL), + GB_METHOD("_get", "Menu", MenuChildren_get, "(Index)i"), + GB_METHOD("Clear", NULL, MenuChildren_Clear, NULL), + GB_PROPERTY_READ("Count", "i", MenuChildren_Count), + + GB_END_DECLARE +}; + + +GB_DESC CMenuDesc[] = +{ + GB_DECLARE("Menu", sizeof(CMENU)), //GB_HOOK_CHECK(CWIDGET_check), + GB_HOOK_CHECK(check_menu), + + GB_METHOD("_new", NULL, Menu_new, "(Parent)o[(Hidden)b]"), + GB_METHOD("_free", NULL, Menu_free, NULL), + + GB_PROPERTY("Name", "s", Control_Name), + GB_PROPERTY("Caption", "s", Menu_Text), + GB_PROPERTY("Text", "s", Menu_Text), + GB_PROPERTY("_Text", "s", Menu_SaveText), + GB_PROPERTY("Enabled", "b", Menu_Enabled), + GB_PROPERTY("Checked", "b", Menu_Checked), + GB_PROPERTY("Tag", "v", Menu_Tag), + GB_PROPERTY("Picture", "Picture", Menu_Picture), + GB_PROPERTY("Shortcut", "s", Menu_Shortcut), + GB_PROPERTY("Visible", "b", Menu_Visible), + GB_PROPERTY("Toggle", "b", Menu_Toggle), + GB_PROPERTY("Radio", "b", Menu_Radio), + GB_PROPERTY("Value", "b", Menu_Value), + GB_PROPERTY("Action", "s", Menu_Action), + GB_PROPERTY_READ("Parent", "Menu", Menu_Parent), + GB_PROPERTY_READ("Window", "Window", Menu_Window), + GB_PROPERTY("Proxy", "Menu", Menu_Proxy), + + GB_PROPERTY_SELF("Children", ".Menu.Children"), + + MENU_DESCRIPTION, + + GB_METHOD("Popup", NULL, Menu_Popup, "[(X)i(Y)i]"), + GB_METHOD("Close", NULL, Menu_Close, NULL), + GB_METHOD("Delete", NULL, Menu_Delete, NULL), + GB_METHOD("Show", NULL, Menu_Show, NULL), + GB_METHOD("Hide", NULL, Menu_Hide, NULL), + + GB_PROPERTY_READ("Closed", "b", Menu_Closed), + + //GB_EVENT("Delete", NULL, NULL, &EVENT_Destroy), // Must be first + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + + GB_END_DECLARE +}; + + + +/* CMenu class */ + +CMenu CMenu::manager; +QHash CMenu::dict; + +static void send_click_event(CMENU *_object) +{ + if (THIS->toggle && !THIS->radio) + { + THIS->checked = !THIS->checked; + update_check(THIS); + } + + GB.Raise(THIS, EVENT_Click, 0); + //qDebug("CACTION_raise: %s", THIS->widget.name); + CACTION_raise((CWIDGET *)THIS); + GB.Unref(POINTER(&_object)); +} + +static void send_menu_event(CMENU *_object, intptr_t event) +{ + GB.Raise(THIS, event, 0); + GB.Unref(POINTER(&_object)); +} + +/*void CMenu::slotTriggered(QAction *action) +{ + GET_MENU_SENDER(parent); + CMENU *menu = CMenu::dict[action]; + + qDebug("slotTriggered: %s %s from %s", menu->widget.name, (const char *)action->text().toUtf8(), parent->widget.name); + + if (GB.Is(parent->parent, CLASS_Menu)) + return; + + GB.Ref(menu); + + if (_popup_immediate) + _popup_menu_clicked = menu; + else + GB.Post((GB_CALLBACK)send_click_event, (intptr_t)menu); +}*/ + +void CMenu::slotTriggered() +{ + CMENU *_object = dict[(QAction *)sender()]; + + if (!_object) + return; + + //qDebug("slotTriggered: %s %s from %s", menu->widget.name, (const char *)action->text().toUtf8(), parent->widget.name); + + GB.Ref(THIS); + + if (_popup_immediate) + _popup_menu_clicked = THIS; + else + GB.Post((GB_CALLBACK)send_click_event, (intptr_t)THIS); +} + +void CMenu::slotShown(void) +{ + static bool init = FALSE; + + GET_MENU_SENDER(menu); + if (!menu) + return; + HANDLE_PROXY(menu); + + //fprintf(stderr, "slotShown: %s: menuAction = %s\n", menu->widget.name, TO_UTF8(((QMenu *)sender())->menuAction()->text())); + + #ifdef QT5 + if (menu->menu->windowHandle()) + { + QWidget *parent = qApp->activePopupWidget(); + if (!parent) parent = qApp->activeWindow(); + if (parent) + { + //fprintf(stderr, "set menu transient to %p\n", parent->windowHandle()); + menu->menu->windowHandle()->setTransientParent(parent->windowHandle()); + } + } + #endif + + GB.Ref(menu); + + menu->opened = TRUE; + + GB.Raise(menu, EVENT_Show, 0); + + if (!init) + { + GB.GetFunction(&_init_shortcut_func, (void *)GB.FindClass("_Gui"), "_DefineShortcut", NULL, NULL); + init = TRUE; + } + + GB.Push(1, GB_T_OBJECT, menu); + GB.Call(&_init_shortcut_func, 1, FALSE); + + GB.Unref(POINTER(&menu)); +} + +void CMenu::slotHidden(void) +{ + //qDebug("slotHidden: sender = %p menuAction = %p", sender(), ((QMenu *)sender())->menuAction()); + GET_MENU_SENDER(menu); + if (!menu) + return; + HANDLE_PROXY(menu); + + menu->opened = FALSE; + + if (GB.CanRaise(menu, EVENT_Hide)) + { + GB.Ref(menu); + GB.Post2((GB_CALLBACK)send_menu_event, (intptr_t)menu, EVENT_Hide); + } +} + + +#if 0 +void CMenu::enableAccel(CMENU *item, bool enable, bool rec) +{ + // Do not disable shortcuts when a menu is executed + if (item->exec && !enable) + return; + + if (!rec) + qDebug("CMenu::enableAccel: %s: %s", item->widget.name, enable ? "ON" : "OFF"); + + item->noshortcut = !enable; + update_accel(item); + + if (item->menu) + { + int i; + + for (i = 0; i < item->menu->actions().count(); i++) + CMenu::enableAccel(CMenu::dict[item->menu->actions().at(i)], enable, true); + } +} +#endif + +void CMenu::hideSeparators(CMENU *item) +{ + if (!item->menu) + return; + + #if 0 + CMENU *child; + CMENU *last_child; + //QListIterator it(*item->children); + bool is_sep; + bool last_sep; + QList children = *(item->children); + int i; + + //qDebug("checking separators"); + + last_sep = true; + last_child = 0; + + for(i = 0; i < children.count(); i++) + { + child = children.at(i); + + is_sep = CMENU_is_separator(child); + + //qDebug("separator = %d visible = %d (%p -> %p)", is_sep, CMENU_is_visible(child), child, it.current()); + + if (is_sep) + { + if (last_sep) + { + hide_menu(child); + } + else + { + show_menu(child); + last_sep = true; + last_child = child; + } + } + else + { + if (CMENU_is_visible(child)) + last_sep = false; + } + } + + if (last_sep && last_child) + hide_menu(last_child); + #endif +} + + +/* +void CMenu::unrefChildren(QWidget *w) +{ + int i; + QList list = w->actions(); + CMENU *child; + + for (i = 0; i < list.count(); i++) + { + child = dict[list.at(i)]; + //qDebug("CMenu::unrefChildren: (%s %p)", GB.GetClassName(child), child); + GB.Detach(child); + unregister_menu(child); + //qDebug("*** CMenu::destroy %p (child)", child); + GB.Unref(POINTER(&child)); + } +}*/ + +void CMenu::slotToggled(bool checked) +{ + CMENU *_object = dict[(QAction *)sender()]; + + if (!_object) + return; + + if (!THIS->radio) + return; + + THIS->checked = checked; +} + +void CMenu::slotDestroyed(void) +{ + CMENU *_object = dict[(QAction *)sender()]; + + #ifdef DEBUG_MENU + qDebug("*** { CMenu::destroy %p", THIS); + #endif + + //qDebug("CMenu::slotDestroyed: action = %p THIS = %p", sender(), _object); + + if (!_object) + return; + + CMenu::dict.remove(ACTION); + //qDebug("CMenu::slotDestroyed: (%s %p)", GB.GetClassName(THIS), THIS); + + //if (THIS->menu) + // unrefChildren(THIS->menu); + + #ifdef DEBUG_MENU + qDebug("UNREF (%s %p)", THIS->widget.name, THIS); + #endif + + unregister_menu(THIS); + THIS->widget.widget = NULL; + GB.Unref(POINTER(&_object)); + + //menu->dict = dict; + + #ifdef DEBUG_MENU + qDebug("*** } CMenu::destroy: %p", THIS); + #endif +} + +//--------------------------------------------------------------------------- + +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(5,5,0) + +void MyMenu::setVisible(bool visible) +{ + if (!visible) + setAttribute(Qt::WA_NoMouseReplay); + QMenu::setVisible(visible); +} + +#endif + + +void CMENU_update_menubar(CWINDOW *window) +{ + static bool init = FALSE; + static bool norec = FALSE; + + if (!init) + { + GB.GetFunction(&_init_menubar_shortcut_func, (void *)GB.FindClass("_Gui"), "_InitMenuBarShortcut", NULL, NULL); + init = TRUE; + } + + if (norec) + return; + + norec = TRUE; + GB.Push(1, GB_T_OBJECT, window); + GB.Call(&_init_menubar_shortcut_func, 1, FALSE); + norec = FALSE; +} + diff --git a/gb.qt4/src/CMenu.h b/gb.qt4/src/CMenu.h new file mode 100644 index 00000000..1a74bfb9 --- /dev/null +++ b/gb.qt4/src/CMenu.h @@ -0,0 +1,137 @@ +/*************************************************************************** + + CMenu.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMENU_H +#define __CMENU_H + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include "CWidget.h" +#include "CPicture.h" + +#ifndef __CMENU_CPP + +extern GB_DESC CMenuDesc[]; +extern GB_DESC CMenuChildrenDesc[]; +extern int MENU_popup_count; + +#else + +#define THIS OBJECT(CMENU) +#define THIS_EXT ((CMENU_EXT *)(THIS->widget.ext)) +#define ACTION ((QAction *)((CWIDGET *)_object)->widget) +#define PARENT_ACTION ((QAction *)((CWIDGET *)(THIS->parent))->widget) + +#define CMENU_is_toplevel(_menu) (GB.Is((_menu)->parent, CLASS_Window)) + +#define GET_MENU_SENDER(_menu) CMENU *_menu = CMenu::dict[((QMenu *)sender())->menuAction()] + +#endif + +typedef + struct { + GB_VARIANT_VALUE tag; + void *proxy; + char *action; + } + CMENU_EXT; + +typedef + struct _CMENU { + CWIDGET widget; + void *parent; + QWidget *toplevel; + QMenu *menu; + QKeySequence *accel; + CPICTURE *picture; + char *save_text; + unsigned deleted : 1; + unsigned toggle : 1; + unsigned radio : 1; + unsigned exec : 1; + unsigned checked : 1; + unsigned disabled : 1; + unsigned visible : 1; + unsigned init_shortcut : 1; + unsigned opened : 1; + } + CMENU; + +typedef + QList CMenuList; + +class MyAction : public QAction +{ + Q_OBJECT + +public: + + MyAction(QObject *parent); + +protected: + + virtual bool event(QEvent *); +}; + +class CMenu : public QObject +{ + Q_OBJECT + +public: + + static CMenu manager; + static QHash dict; + + //static void unrefChildren(QWidget *wid); + //static void enableAccel(CMENU *item, bool enable, bool rec = false); + static void hideSeparators(CMENU *item); + +public slots: + + void slotTriggered(); + void slotToggled(bool); + void slotDestroyed(); + void slotShown(); + void slotHidden(); +}; + +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(5,5,0) +class MyMenu: public QMenu +{ +public: + + virtual void setVisible(bool visible); +}; +#endif + +void CMENU_popup(CMENU *_object, const QPoint &pos); +void CMENU_update_menubar(CWINDOW *window); + +#endif diff --git a/gb.qt4/src/CMouse.cpp b/gb.qt4/src/CMouse.cpp new file mode 100644 index 00000000..5fb98ff3 --- /dev/null +++ b/gb.qt4/src/CMouse.cpp @@ -0,0 +1,678 @@ +/*************************************************************************** + + CMouse.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOUSE_CPP + +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" + +#include "gb.form.const.h" +#include "CWidget.h" +#include "CPicture.h" +#include "CMouse.h" + +int MOUSE_click_x = -1; +int MOUSE_click_y = -1; +int MOUSE_click_count = 0; +double MOUSE_timer = 0; + +MOUSE_INFO MOUSE_info = { 0 }; +POINTER_INFO POINTER_info = { 0 }; + +static int _dx = 0; +static int _dy = 0; + +static void *_control = NULL; + +void CMOUSE_clear(int valid) +{ + if (valid) + MOUSE_info.valid++; + else + MOUSE_info.valid--; + + if (MOUSE_info.valid == 0) + CLEAR(&POINTER_info); +} + +void CMOUSE_reset_translate() +{ + _dx = _dy = 0; +} + +void CMOUSE_set_control(void *control) +{ + if (_control) + GB.Unref(&_control); + if (control) + GB.Ref(control); + + _control = control; +} + +void CMOUSE_finish_event(void) +{ + if (_control) + GB.Raise(_control, EVENT_MouseUp, 0); +} + + +//int CMOUSE_last_state = 0; + +//static CCURSOR PredefinedCursor[LastCursor + 1] = { { { 0, 0 }, NULL, NULL } }; +//static int MouseClassID; + +#if 0 +static int translate_state(int s) +{ + int bst = 0; + + if (s & Button1Mask) + bst |= Qt::LeftButton; + if ( s & Button2Mask) + bst |= Qt::MidButton; + if ( s & Button3Mask) + bst |= Qt::RightButton; + if ( s & ShiftMask) + bst |= Qt::ShiftModifier; + if ( s & ControlMask) + bst |= Qt::ControlModifier; + if ( s & qt_alt_mask) + bst |= Qt::AltModifier; + if ( s & qt_meta_mask) + bst |= Qt::MetaModifier; + + return bst; +} + +static int get_state() +{ + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint state; + Display* dpy = QPaintDevice::x11AppDisplay(); + + for (int i = 0; i < ScreenCount(dpy); i++) + { + if (XQueryPointer(dpy, RootWindow(dpy, i), &root, &child, + &root_x, &root_y, &win_x, &win_y, &state)) + return translate_state(state); + } + + return 0; +} +#endif + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Cursor_new, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y) + + CPICTURE *pict = (CPICTURE *)VARG(picture); + + THIS->x = VARGOPT(x, -1); + THIS->y = VARGOPT(y, -1); + + //GB.StoreObject(ARG(picture), POINTER(&THIS->picture)); + if (GB.CheckObject(pict)) + return; + + if (THIS->x < 0 || THIS->x >= pict->pixmap->width()) + THIS->x = -1; + + if (THIS->y < 0 || THIS->y >= pict->pixmap->height()) + THIS->y = -1; + + THIS->cursor = new QCursor(*(pict->pixmap), THIS->x, THIS->y); + +END_METHOD + + +BEGIN_METHOD_VOID(Cursor_Delete) + + //GB.Unref(POINTER(&THIS->picture)); + delete THIS->cursor; + +END_METHOD + + +/*BEGIN_PROPERTY(CCURSOR_picture) + + GB.ReturnObject(THIS->picture); + +END_PROPERTY*/ + + +BEGIN_PROPERTY(Cursor_X) + + GB.ReturnInteger(THIS->x); + +END_PROPERTY + + +BEGIN_PROPERTY(Cursor_Y) + + GB.ReturnInteger(THIS->y); + +END_PROPERTY + + +// BEGIN_METHOD(CCURSOR_get, int shape) +// +// int shape = PARAM(shape); +// CCURSOR *p; +// +// if (shape < 0 || shape > LastCursor) +// GB.ReturnObject(NULL); +// +// p = &PredefinedCursor[shape]; +// if (p->ob.klass == 0) +// { +// p->ob.klass = MouseClassID; +// p->cursor = new QCursor(shape); +// GB.Ref(p); +// } +// +// GB.ReturnObject(p); +// +// END_METHOD + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Mouse_ScreenX) + + GB.ReturnInteger(QCursor::pos().x()); + +END_PROPERTY + + +BEGIN_PROPERTY(Mouse_ScreenY) + + GB.ReturnInteger(QCursor::pos().y()); + +END_PROPERTY + + +BEGIN_METHOD(Mouse_Move, GB_INTEGER x; GB_INTEGER y) + + QCursor::setPos(VARG(x), VARG(y)); + +END_PROPERTY + +#define CHECK_VALID() \ + if (MOUSE_info.valid == 0) \ + { \ + GB.Error("No mouse event data"); \ + return; \ + } + +BEGIN_PROPERTY(Mouse_X) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.x + _dx); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Y) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.y + _dy); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartX) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.sx + _dx); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartY) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.sy + _dy); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Button) + + int i; + + CHECK_VALID(); + + for (i = 0; i < 5; i++) + { + if ((int)MOUSE_info.button & (1 << i)) + { + GB.ReturnInteger(i + 1); + return; + } + } + + GB.ReturnInteger(0); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_State) + + CHECK_VALID(); + int state = (int)MOUSE_info.state; + if (MOUSE_info.modifier & Qt::ShiftModifier) + state |= MOUSE_SHIFT; + if (MOUSE_info.modifier & Qt::ControlModifier) + state |= MOUSE_CTRL; + if (MOUSE_info.modifier & Qt::AltModifier) + state |= MOUSE_ALT; + if (MOUSE_info.modifier & Qt::MetaModifier) + state |= MOUSE_META; + + GB.ReturnInteger(state); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Left) + + CHECK_VALID(); + GB.ReturnBoolean((MOUSE_info.state | MOUSE_info.button) & Qt::LeftButton); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Right) + + CHECK_VALID(); + GB.ReturnBoolean((MOUSE_info.state | MOUSE_info.button) & Qt::RightButton); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Middle) + + CHECK_VALID(); + GB.ReturnBoolean((MOUSE_info.state | MOUSE_info.button) & Qt::MidButton); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Shift) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::ShiftModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Control) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::ControlModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Alt) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::AltModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Meta) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::MetaModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Normal) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier == Qt::NoModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Orientation) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.orientation); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Delta) + + CHECK_VALID(); + GB.ReturnFloat((double)MOUSE_info.delta / 120); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Forward) + + CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.delta > 0); + +END_PROPERTY + +BEGIN_METHOD(Mouse_Inside, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + QPoint pos; + + if (GB.CheckObject(control)) + return; + + if (!CWIDGET_is_visible(control)) + { + GB.ReturnBoolean(false); + return; + } + + pos = QCursor::pos() - QWIDGET(control)->mapToGlobal(QPoint(0, 0)); + GB.ReturnBoolean(pos.x() >= 0 && pos.x() < QWIDGET(control)->width() && pos.y() >= 0 && pos.y() < QWIDGET(control)->height()); + +END_METHOD + +BEGIN_METHOD(Mouse_Translate, GB_INTEGER dx; GB_INTEGER dy) + + CHECK_VALID(); + + _dx = VARG(dx); + _dy = VARG(dy); + +END_METHOD + +BEGIN_PROPERTY(Mouse_Click) + + GB.ReturnInteger(MOUSE_info.valid ? MOUSE_click_count : 0); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(Mouse_Begin, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER state) + + void *control = VARG(control); + int state = VARGOPT(state, 0); + int x = VARG(x); + int y = VARG(y); + + if (GB.CheckObject(control)) + return; + + CMOUSE_clear(true); + MOUSE_info.x = x; + MOUSE_info.y = y; + QPoint p = QWIDGET(control)->mapToGlobal(QPoint(x, y)); + MOUSE_info.sx = p.x(); + MOUSE_info.sy = p.y(); + + MOUSE_info.state = (Qt::MouseButtons)(state & 0xFF); + if (state & MOUSE_LEFT) + MOUSE_info.button = Qt::LeftButton; + else if (state & MOUSE_MIDDLE) + MOUSE_info.button = Qt::MiddleButton; + else if (state & MOUSE_RIGHT) + MOUSE_info.button = Qt::RightButton; + + MOUSE_info.modifier = (Qt::KeyboardModifiers)0; + if (state & MOUSE_SHIFT) + MOUSE_info.modifier |= Qt::ShiftModifier; + if (state & MOUSE_CTRL) + MOUSE_info.modifier |= Qt::ShiftModifier; + if (state & MOUSE_ALT) + MOUSE_info.modifier |= Qt::ShiftModifier; + +END_METHOD + +BEGIN_METHOD_VOID(Mouse_End) + + CMOUSE_clear(false); + +END_METHOD +#endif + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Pointer_X) + + CHECK_VALID(); + GB.ReturnFloat((double)(MOUSE_info.x + _dx) + (POINTER_info.tx - (int)POINTER_info.tx)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Y) + + CHECK_VALID(); + GB.ReturnFloat((double)(MOUSE_info.y + _dy) + (POINTER_info.ty - (int)POINTER_info.ty)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_ScreenX) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.tx); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_ScreenY) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.ty); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_XTilt) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.xtilt); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_YTilt) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.ytilt); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Pressure) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.pressure); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Rotation) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.rotation); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Type) + + CHECK_VALID(); + GB.ReturnInteger(POINTER_info.type); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +GB_DESC CCursorDesc[] = +{ + GB_DECLARE("Cursor", sizeof(CCURSOR)), + + GB_METHOD("_new", NULL, Cursor_new, "(Picture)Picture;[(X)i(Y)i]"), + GB_METHOD("_free", NULL, Cursor_Delete, NULL), + + GB_CONSTANT("Custom", "i", CURSOR_CUSTOM), + GB_CONSTANT("Default", "i", CURSOR_DEFAULT), + + GB_CONSTANT("Blank", "i", CURSOR_NONE), + GB_CONSTANT("Arrow", "i", CURSOR_ARROW), + GB_CONSTANT("Cross", "i", CURSOR_CROSSHAIR), + GB_CONSTANT("Wait", "i", CURSOR_WAIT), + GB_CONSTANT("Text", "i", CURSOR_TEXT), + GB_CONSTANT("SizeAll", "i", CURSOR_MOVE), + GB_CONSTANT("SizeH", "i", CURSOR_EW_RESIZE), + GB_CONSTANT("SizeV", "i", CURSOR_NS_RESIZE), + GB_CONSTANT("SizeN", "i", CURSOR_N_RESIZE), + GB_CONSTANT("SizeS", "i", CURSOR_S_RESIZE), + GB_CONSTANT("SizeW", "i", CURSOR_W_RESIZE), + GB_CONSTANT("SizeE", "i", CURSOR_E_RESIZE), + GB_CONSTANT("SizeNW", "i", CURSOR_NW_RESIZE), //FDiag + GB_CONSTANT("SizeSE", "i", CURSOR_SE_RESIZE), + GB_CONSTANT("SizeNE", "i", CURSOR_NE_RESIZE), //BDiag + GB_CONSTANT("SizeSW", "i", CURSOR_SW_RESIZE), + GB_CONSTANT("SizeNWSE", "i", CURSOR_NWSE_RESIZE), + GB_CONSTANT("SizeNESW", "i", CURSOR_NESW_RESIZE), + GB_CONSTANT("SplitH", "i", CURSOR_COL_RESIZE), // SplitH + GB_CONSTANT("SplitV", "i", CURSOR_ROW_RESIZE), // SplitV + GB_CONSTANT("Pointing", "i", CURSOR_POINTER), + + GB_CONSTANT("None", "i", CURSOR_NONE), + GB_CONSTANT("Help", "i", CURSOR_HELP), + GB_CONSTANT("Pointer", "i", CURSOR_POINTER), + GB_CONSTANT("ContextMenu", "i", CURSOR_CONTEXT_MENU), + GB_CONSTANT("Progress", "i", CURSOR_PROGRESS), + GB_CONSTANT("Cell", "i", CURSOR_CELL), + GB_CONSTANT("CrossHair", "i", CURSOR_CROSSHAIR), + GB_CONSTANT("VerticalText", "i", CURSOR_VERTICAL_TEXT), + GB_CONSTANT("Alias", "i", CURSOR_ALIAS), + GB_CONSTANT("Copy", "i", CURSOR_COPY), + GB_CONSTANT("NoDrop", "i", CURSOR_NO_DROP), + GB_CONSTANT("Move", "i", CURSOR_MOVE), + GB_CONSTANT("NotAllowed", "i", CURSOR_NOT_ALLOWED), + GB_CONSTANT("Grab", "i", CURSOR_GRAB), + GB_CONSTANT("Grabbing", "i", CURSOR_GRABBING), + GB_CONSTANT("AllScroll", "i", CURSOR_ALL_SCROLL), + GB_CONSTANT("ColResize", "i", CURSOR_COL_RESIZE), + GB_CONSTANT("RowResize", "i", CURSOR_ROW_RESIZE), + GB_CONSTANT("NResize", "i", CURSOR_N_RESIZE), + GB_CONSTANT("EResize", "i", CURSOR_E_RESIZE), + GB_CONSTANT("SResize", "i", CURSOR_S_RESIZE), + GB_CONSTANT("WResize", "i", CURSOR_W_RESIZE), + GB_CONSTANT("NEResize", "i", CURSOR_NE_RESIZE), + GB_CONSTANT("NWResize", "i", CURSOR_NW_RESIZE), + GB_CONSTANT("SWResize", "i", CURSOR_SW_RESIZE), + GB_CONSTANT("SEResize", "i", CURSOR_SE_RESIZE), + GB_CONSTANT("EWResize", "i", CURSOR_EW_RESIZE), + GB_CONSTANT("NSResize", "i", CURSOR_NS_RESIZE), + GB_CONSTANT("NESWResize", "i", CURSOR_NESW_RESIZE), + GB_CONSTANT("NWSEResize", "i", CURSOR_NWSE_RESIZE), + GB_CONSTANT("ZoomIn", "i", CURSOR_ZOOM_IN), + GB_CONSTANT("ZoomOut", "i", CURSOR_ZOOM_OUT), + + GB_PROPERTY_READ("X", "i", Cursor_X), + GB_PROPERTY_READ("Y", "i", Cursor_Y), + //GB_PROPERTY_READ("Picture", "Picture", CCURSOR_picture), + + GB_END_DECLARE +}; + + +GB_DESC CMouseDesc[] = +{ + GB_DECLARE("Mouse", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("ScreenX", "i", Mouse_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "i", Mouse_ScreenY), + GB_STATIC_METHOD("Move", NULL, Mouse_Move, "(X)i(Y)i"), + GB_STATIC_METHOD("Inside", "b", Mouse_Inside, "(Control)Control"), + + GB_CONSTANT("Custom", "i", CURSOR_CUSTOM), + GB_CONSTANT("Default", "i", CURSOR_DEFAULT), + + GB_CONSTANT("Blank", "i", CURSOR_NONE), + GB_CONSTANT("Arrow", "i", CURSOR_ARROW), + GB_CONSTANT("Cross", "i", CURSOR_CROSSHAIR), + GB_CONSTANT("Wait", "i", CURSOR_WAIT), + GB_CONSTANT("Text", "i", CURSOR_TEXT), + GB_CONSTANT("SizeAll", "i", CURSOR_MOVE), + GB_CONSTANT("SizeH", "i", CURSOR_EW_RESIZE), + GB_CONSTANT("SizeV", "i", CURSOR_NS_RESIZE), + GB_CONSTANT("SizeN", "i", CURSOR_N_RESIZE), + GB_CONSTANT("SizeS", "i", CURSOR_S_RESIZE), + GB_CONSTANT("SizeW", "i", CURSOR_W_RESIZE), + GB_CONSTANT("SizeE", "i", CURSOR_E_RESIZE), + GB_CONSTANT("SizeNW", "i", CURSOR_NW_RESIZE), //FDiag + GB_CONSTANT("SizeSE", "i", CURSOR_SE_RESIZE), + GB_CONSTANT("SizeNE", "i", CURSOR_NE_RESIZE), //BDiag + GB_CONSTANT("SizeSW", "i", CURSOR_SW_RESIZE), + GB_CONSTANT("SizeNWSE", "i", CURSOR_NWSE_RESIZE), + GB_CONSTANT("SizeNESW", "i", CURSOR_NESW_RESIZE), + GB_CONSTANT("SplitH", "i", CURSOR_COL_RESIZE), // SplitH + GB_CONSTANT("SplitV", "i", CURSOR_ROW_RESIZE), // SplitV + GB_CONSTANT("Pointing", "i", CURSOR_POINTER), + + GB_CONSTANT("Horizontal", "i", Qt::Horizontal), + GB_CONSTANT("Vertical", "i", Qt::Vertical), + + GB_STATIC_PROPERTY_READ("X", "i", Mouse_X), + GB_STATIC_PROPERTY_READ("Y", "i", Mouse_Y), + GB_STATIC_PROPERTY_READ("StartX", "i", Mouse_StartX), + GB_STATIC_PROPERTY_READ("StartY", "i", Mouse_StartY), + GB_STATIC_PROPERTY_READ("Left", "b", Mouse_Left), + GB_STATIC_PROPERTY_READ("Right", "b", Mouse_Right), + GB_STATIC_PROPERTY_READ("Middle", "b", Mouse_Middle), + GB_STATIC_PROPERTY_READ("State", "i", Mouse_State), + GB_STATIC_PROPERTY_READ("Button", "i", Mouse_Button), + GB_STATIC_PROPERTY_READ("Shift", "b", Mouse_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Mouse_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Mouse_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Mouse_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Mouse_Normal), + GB_STATIC_PROPERTY_READ("Orientation", "i", Mouse_Orientation), + GB_STATIC_PROPERTY_READ("Delta", "f", Mouse_Delta), + GB_STATIC_PROPERTY_READ("Forward", "b", Mouse_Forward), + GB_STATIC_PROPERTY_READ("Click", "i", Mouse_Click), + + GB_STATIC_METHOD("Translate", NULL, Mouse_Translate, "(DX)i(DY)i"), + //GB_STATIC_METHOD("Begin", NULL, Mouse_Begin, "(Control)Control;(X)i(Y)i[(State)i]"), + //GB_STATIC_METHOD("End", NULL, Mouse_End, NULL), + + GB_END_DECLARE +}; + +GB_DESC CPointerDesc[] = +{ + GB_DECLARE_VIRTUAL("Pointer"), + + GB_CONSTANT("Mouse", "i", POINTER_MOUSE), + GB_CONSTANT("Pen", "i", POINTER_PEN), + GB_CONSTANT("Eraser", "i", POINTER_ERASER), + GB_CONSTANT("Cursor", "i", POINTER_CURSOR), + + GB_STATIC_PROPERTY_READ("Type", "i", Pointer_Type), + GB_STATIC_PROPERTY_READ("X", "f", Pointer_X), + GB_STATIC_PROPERTY_READ("Y", "f", Pointer_Y), + GB_STATIC_PROPERTY_READ("ScreenX", "f", Pointer_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "f", Pointer_ScreenY), + GB_STATIC_PROPERTY_READ("XTilt", "f", Pointer_XTilt), + GB_STATIC_PROPERTY_READ("YTilt", "f", Pointer_YTilt), + GB_STATIC_PROPERTY_READ("Pressure", "f", Pointer_Pressure), + GB_STATIC_PROPERTY_READ("Rotation", "f", Pointer_Rotation), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CMouse.h b/gb.qt4/src/CMouse.h new file mode 100644 index 00000000..e99a3c0f --- /dev/null +++ b/gb.qt4/src/CMouse.h @@ -0,0 +1,101 @@ +/*************************************************************************** + + CMouse.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOUSE_H +#define __CMOUSE_H + +#include +#include + +#include "gambas.h" + +#include "CPicture.h" + +typedef + struct { + int valid; + int x; + int y; + int sx; + int sy; + Qt::MouseButton button; + Qt::MouseButtons state; + Qt::KeyboardModifiers modifier; + int orientation; + int delta; + int screenX; + int screenY; + int dx; + int dy; + } + MOUSE_INFO; + +typedef + struct { + double tx; + double ty; + int xtilt; + int ytilt; + int type; + double pressure; + double rotation; + } + POINTER_INFO; + +#ifndef __CMOUSE_CPP + +extern GB_DESC CMouseDesc[]; +extern GB_DESC CCursorDesc[]; +extern GB_DESC CPointerDesc[]; + +extern int MOUSE_click_x; +extern int MOUSE_click_y; +extern int MOUSE_click_count; +extern double MOUSE_timer; + +extern MOUSE_INFO MOUSE_info; +extern POINTER_INFO POINTER_info; + +#else + +#define THIS ((CCURSOR *)_object) + +#endif + +typedef + struct _CCURSOR { + GB_BASE ob; + int x; + int y; + QCursor *cursor; + } + CCURSOR; + +void CMOUSE_clear(int valid); +void CMOUSE_reset_translate(void); +void CMOUSE_set_control(void *control); +void CMOUSE_finish_event(void); + +#define CMOUSE_is_valid() (MOUSE_info.valid != 0) + +#endif diff --git a/gb.qt4/src/CPanel.cpp b/gb.qt4/src/CPanel.cpp new file mode 100644 index 00000000..fc6b8b04 --- /dev/null +++ b/gb.qt4/src/CPanel.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + + CPanel.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPANEL_CPP + +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CPanel.h" + +BEGIN_METHOD(CPANEL_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + THIS->container = wid; + + //THIS->widget.flag.fillBackground = true; + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CHBOX_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_HORIZONTAL; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CVBOX_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_VERTICAL; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CHPANEL_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_ROW; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CVPANEL_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_COLUMN; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + + +GB_DESC CPanelDesc[] = +{ + GB_DECLARE("Panel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("Border", "i", Container_Border), + + ARRANGEMENT_PROPERTIES, + + PANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHBoxDesc[] = +{ + GB_DECLARE("HBox", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CHBOX_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + HBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVBoxDesc[] = +{ + GB_DECLARE("VBox", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CVBOX_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + VBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHPanelDesc[] = +{ + GB_DECLARE("HPanel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CHPANEL_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + HPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVPanelDesc[] = +{ + GB_DECLARE("VPanel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CVPANEL_new, "(Parent)Container;"), + + ARRANGEMENT_FLAG_PROPERTIES, + + VPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CPanel.h b/gb.qt4/src/CPanel.h new file mode 100644 index 00000000..ac951043 --- /dev/null +++ b/gb.qt4/src/CPanel.h @@ -0,0 +1,67 @@ +/*************************************************************************** + + CPanel.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPANEL_H +#define __CPANEL_H + +#include "gambas.h" + +#include + +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CPANEL_CPP +extern GB_DESC CPanelDesc[]; +extern GB_DESC CHBoxDesc[]; +extern GB_DESC CVBoxDesc[]; +extern GB_DESC CHPanelDesc[]; +extern GB_DESC CVPanelDesc[]; +#else + +#define THIS ((CPANEL *)_object) +#define WIDGET ((MyPanel *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + QWidget *container; + CARRANGEMENT arrangement; + } + CPANEL; + +// class MyPanel: public QFrame +// { +// Q_OBJECT +// +// public: +// MyPanel(QWidget *parent); +// void configure(); +// +// protected: +// virtual void resizeEvent(QResizeEvent *); +// }; + +#endif diff --git a/gb.qt4/src/CPicture.cpp b/gb.qt4/src/CPicture.cpp new file mode 100644 index 00000000..c34f681f --- /dev/null +++ b/gb.qt4/src/CPicture.cpp @@ -0,0 +1,389 @@ +/*************************************************************************** + + CPicture.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPICTURE_CPP + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" + +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CScreen.h" +#include "CImage.h" +#include "CPicture.h" + +#ifdef QT5 +#include +#include +#else +#include +#include +#endif + + + +static CPICTURE *create() +{ + return (CPICTURE *)GB.New(GB.FindClass("Picture"), NULL, NULL); +} + +#define CREATE_IMAGE_FROM_MEMORY(_image, _addr, _len, _ok) \ +{ \ + QImage img; \ + _ok = img.loadFromData((const uchar *)_addr, (uint)_len); \ + if (_ok) \ + { \ + if (img.depth() < 32 && !img.isNull()) \ + img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); \ + } \ + _image = new QImage(img); \ +} + +#define DELETE_IMAGE(_image) delete _image + +#define CREATE_PICTURE_FROM_IMAGE(_cpicture, _image) \ +{ \ + _cpicture = create(); \ + if ((_image) && !(_image)->isNull()) \ + *((_cpicture)->pixmap) = QPixmap::fromImage(*(_image)); \ +} + + +bool CPICTURE_from_string(QImage **p, const char *addr, int len) +{ + bool ok; + + *p = 0; + CREATE_IMAGE_FROM_MEMORY(*p, addr, len, ok) + return ok; +} + + +bool CPICTURE_load_image(QImage **p, const char *path, int lenp) +{ + char *addr; + int len; + bool ok; + + *p = 0; + + if (GB.LoadFile(path, lenp, &addr, &len)) + { + GB.Error(NULL); + return FALSE; + } + + ok = CPICTURE_from_string(p, addr, len); + + GB.ReleaseFile(addr, len); + return ok; +} + +CPICTURE *CPICTURE_grab(QWidget *wid, int screen, int x, int y, int w, int h) +{ + CPICTURE *pict; + + pict = create(); + + if (!wid) + { + if (w <= 0 || h <= 0) + { + x = 0; y = 0; w = -1; h = -1; + } + +#ifdef QT5 + //*pict->pixmap = QGuiApplication::primaryScreen()->grabWindow(QX11Info::appRootWindow(), x, y, w, h); + PLATFORM.Desktop.Screenshot(pict->pixmap, x, y, w, h); +#else + *pict->pixmap = QPixmap::grabWindow(QX11Info::appRootWindow(), x, y, w, h); +#endif + } + else + { +#ifdef QT5 + *pict->pixmap = QGuiApplication::screens().at(QApplication::desktop()->screenNumber(wid))->grabWindow(wid->winId()); +#else + *pict->pixmap = QPixmap::grabWindow(wid->winId()); +#endif + } + + return pict; +} + + +/*void CPICTURE_update_mask(CPICTURE *_object) +{ + if (THIS->pixmap && THIS->pixmap->hasAlpha()) + THIS->pixmap->setMask(THIS->pixmap->createHeuristicMask()); +}*/ + +CPICTURE *CPICTURE_create(const QPixmap *pixmap) +{ + CPICTURE *pict = create(); + if (pixmap) *pict->pixmap = *pixmap; + return pict; +} + +/******************************************************************************* + + class Picture + +*******************************************************************************/ + + +BEGIN_METHOD(Picture_new, GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN trans) + + int w, h; + + if (!MISSING(w) && !MISSING(h)) + { + w = VARG(w); + h = VARG(h); + if (h <= 0 || w <= 0) + { + GB.Error("Bad dimension"); + return; + } + + THIS->pixmap = new QPixmap(w, h); + + if (VARGOPT(trans, false)) + { + QBitmap b(w, h); + b.fill(Qt::color0); + THIS->pixmap->setMask(b); + } + } + else + THIS->pixmap = new QPixmap; + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_free) + + delete THIS->pixmap; + THIS->pixmap = 0; + +END_METHOD + + +BEGIN_METHOD(Picture_Resize, GB_INTEGER width; GB_INTEGER height) + + QPixmap *pixmap = new QPixmap(VARG(width), VARG(height)); + + QPainter p(pixmap); + p.drawPixmap(0, 0, *THIS->pixmap); + p.end(); + + delete THIS->pixmap; + THIS->pixmap = pixmap; + +END_METHOD + + +BEGIN_PROPERTY(Picture_Width) + + GB.ReturnInteger(THIS->pixmap->width()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Height) + + GB.ReturnInteger(THIS->pixmap->height()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Depth) + + GB.ReturnInteger(THIS->pixmap->depth()); + +END_PROPERTY + + +BEGIN_METHOD(Picture_Load, GB_STRING path) + + CPICTURE *pict; + QImage *img; + + if (!CPICTURE_load_image(&img, STRING(path), LENGTH(path))) + { + GB.Error("Unable to load picture"); + return; + } + + CREATE_PICTURE_FROM_IMAGE(pict, img); + DELETE_IMAGE(img); + GB.ReturnObject(pict); + +END_METHOD + +BEGIN_METHOD(Picture_FromString, GB_STRING data) + + CPICTURE *pict; + QImage *img; + + if (!CPICTURE_from_string(&img, STRING(data), LENGTH(data))) + { + GB.Error("Unable to load picture"); + return; + } + + CREATE_PICTURE_FROM_IMAGE(pict, img); + DELETE_IMAGE(img); + GB.ReturnObject(pict); + +END_METHOD + +BEGIN_METHOD(Picture_Save, GB_STRING path; GB_INTEGER quality) + + QString path = TO_QSTRING(GB.FileName(STRING(path), LENGTH(path))); + bool ok = false; + const char *fmt = CIMAGE_get_format(path); + + if (!fmt) + { + GB.Error("Unknown format"); + return; + } + + ok = THIS->pixmap->save(path, fmt, VARGOPT(quality, -1)); + + if (!ok) + GB.Error("Unable to save picture"); + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_Clear) + + delete THIS->pixmap; + THIS->pixmap = new QPixmap; + +END_METHOD + + +BEGIN_METHOD(Picture_Fill, GB_INTEGER col) + + int col = VARG(col); + QBitmap mask; + + THIS->pixmap->fill(QColor(QRgb(col & 0xFFFFFF))); + +END_METHOD + + +BEGIN_METHOD(Picture_Copy, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CPICTURE *pict; + int x = VARGOPT(x, 0); + int y = VARGOPT(y, 0); + int w = VARGOPT(w, THIS->pixmap->width()); + int h = VARGOPT(h, THIS->pixmap->height()); + + pict = create(); + *pict->pixmap = THIS->pixmap->copy(x, y, w, h); + + GB.ReturnObject(pict); + +END_METHOD + + +BEGIN_PROPERTY(Picture_Image) + + QImage *image = new QImage(); + + *image = THIS->pixmap->toImage(); + image->detach(); + + GB.ReturnObject(CIMAGE_create(image)); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Transparent) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->pixmap->hasAlpha()); + else + { + bool a = THIS->pixmap->hasAlpha(); + + if (a == VPROP(GB_BOOLEAN)) + return; + + if (a) + THIS->pixmap->setMask(QBitmap()); + else + THIS->pixmap->setMask(THIS->pixmap->createHeuristicMask()); + } + +END_PROPERTY + + +GB_DESC CPictureDesc[] = +{ + GB_DECLARE("Picture", sizeof(CPICTURE)), + + GB_METHOD("_new", NULL, Picture_new, "[(Width)i(Height)i(Transparent)b]"), + GB_METHOD("_free", NULL, Picture_free, NULL), + + GB_PROPERTY_READ("W", "i", Picture_Width), + GB_PROPERTY_READ("Width", "i", Picture_Width), + GB_PROPERTY_READ("H", "i", Picture_Height), + GB_PROPERTY_READ("Height", "i", Picture_Height), + GB_PROPERTY_READ("Depth", "i", Picture_Depth), + + GB_STATIC_METHOD("Load", "Picture", Picture_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Picture", Picture_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Picture_Save, "(Path)s[(Quality)i]"), + GB_METHOD("Resize", NULL, Picture_Resize, "(Width)i(Height)i"), + + GB_METHOD("Clear", NULL, Picture_Clear, NULL), + GB_METHOD("Fill", NULL, Picture_Fill, "(Color)i"), + //GB_METHOD("Mask", NULL, CPICTURE_mask, "[(Color)i]"), + + GB_PROPERTY("Transparent", "b", Picture_Transparent), + + GB_METHOD("Copy", "Picture", Picture_Copy, "[(X)i(Y)i(Width)i(Height)i]"), + GB_PROPERTY_READ("Image", "Image", Picture_Image), + + //GB_INTERFACE("Draw", &DRAW_Interface), + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CPicture.h b/gb.qt4/src/CPicture.h new file mode 100644 index 00000000..40c9683f --- /dev/null +++ b/gb.qt4/src/CPicture.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + CPicture.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPICTURE_H +#define __CPICTURE_H + +#include +#include + +#include "gambas.h" + +typedef + struct { + GB_BASE ob; + QPixmap *pixmap; + } + CPICTURE; + +#ifndef __CPICTURE_CPP + +extern GB_DESC CPictureDesc[]; +//extern GB_DESC CPicturePixelsDesc[]; + +#else + + +#define THIS OBJECT(CPICTURE) + +#endif + +#if 0 +#define CPICTURE_is_pixmap(_pict) ((_pict)->pixmap != 0) +#define CPICTURE_is_picture(_pict) ((_pict)->picture != 0) +#define CPICTURE_is_image(_pict) ((_pict)->image != 0) +#endif + +#define SET_PIXMAP(_method_pixmap, _store, _object) \ +{ \ + CPICTURE *_pict = (CPICTURE *)((_object) ? (_object)->value : NULL); \ + GB.StoreObject(_object, POINTER(_store)); \ + if (_pict && !((_pict)->pixmap->isNull())) \ + _method_pixmap(*((_pict)->pixmap)); \ + else \ + _method_pixmap(QPixmap()); \ +} + +#define SET_PICTURE(_method_pixmap, _method_picture, _store, _object) \ + SET_PIXMAP(_method_pixmap, _store, _object) + +#define CLEAR_PICTURE(_store) GB.StoreObject(NULL, (void **)(void *)_store) + +CPICTURE *CPICTURE_grab(QWidget *wid, int screen = -1, int x = 0, int y = 0, int w = -1, int h = -1); +CPICTURE *CPICTURE_get_picture(const char *path); +bool CPICTURE_load_image(QImage **p, const char *path, int lenp); +bool CPICTURE_from_string(QImage **p, const char *addr, int len); +CPICTURE *CPICTURE_create(const QPixmap *pixmap); + +#endif diff --git a/gb.qt4/src/CRadioButton.cpp b/gb.qt4/src/CRadioButton.cpp new file mode 100644 index 00000000..590d6c14 --- /dev/null +++ b/gb.qt4/src/CRadioButton.cpp @@ -0,0 +1,199 @@ +/*************************************************************************** + + CRadioButton.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRADIOBUTTON_CPP + +#include +#include +#include + +#include "gambas.h" + +#include "CStyle.h" +#include "CRadioButton.h" + + +//------------------------------------------------------------------------- + +MyRadioButton::MyRadioButton(QWidget *parent) : QRadioButton(parent) +{ + _autoResize = false; +} + + +void MyRadioButton::changeEvent(QEvent *e) +{ + QRadioButton::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + adjust(); +} + +void MyRadioButton::adjust(bool force) +{ + void *_object = CWidget::getReal(this); + bool a; + QSize hint; + + if (!THIS || (!_autoResize && !force) || CWIDGET_is_design(THIS) || text().length() <= 0) + return; + + a = _autoResize; + _autoResize = false; + hint = sizeHint(); + CWIDGET_auto_resize(THIS, hint.width(), qMax(hint.height(), height())); + _autoResize = a; +} + +void MyRadioButton::resizeEvent(QResizeEvent *e) +{ + QRadioButton::resizeEvent(e); + + if (_autoResize && e->oldSize().width() != e->size().width()) + adjust(); +} + +//------------------------------------------------------------------------- + +DECLARE_EVENT(EVENT_Click); + + +BEGIN_METHOD(RadioButton_new, GB_OBJECT parent) + + MyRadioButton *wid = new MyRadioButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(toggled(bool)), &CRadioButton::manager, SLOT(clicked(bool))); + + CWIDGET_new(wid, (void *)_object); + THIS->widget.flag.fillBackground = true; CSTYLE_fix_breeze; + +END_METHOD + + +BEGIN_PROPERTY(RadioButton_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + { + WIDGET->setText(QSTRING_PROP()); + WIDGET->adjust(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(RadioButton_Value) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isChecked()); + else + WIDGET->setChecked(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(RadioButton_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isAutoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(RadioButton_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->widget.flag.inverted); + else + CWIDGET_set_inverted(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CRadioButtonDesc[] = +{ + GB_DECLARE("RadioButton", sizeof(CRADIOBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, RadioButton_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", RadioButton_Text), + GB_PROPERTY("Caption", "s", RadioButton_Text), + GB_PROPERTY("Value", "b", RadioButton_Value), + GB_PROPERTY("AutoResize", "b", RadioButton_AutoResize), + GB_PROPERTY("Invert", "b", RadioButton_Invert), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + + RADIOBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + +//------------------------------------------------------------------------- + +CRadioButton CRadioButton::manager; + +void CRadioButton::clicked(bool on) +{ + QRadioButton *wid = (QRadioButton *)sender(); + GET_SENDER(); + QObject *parent = wid->parent(); + + QList list = parent->findChildren(); + int i; + QRadioButton *obj = 0; + + if (on) + { + for (i = 0; i < list.count(); i++) + { + obj = list.at(i); + if (obj != wid && obj->isChecked()) + { + obj->setChecked(false); + on = false; + } + } + + //if (!on) + GB.Raise(THIS, EVENT_Click, 0); + } + else + { + for (i = 0; i < list.count(); i++) + { + obj = list.at(i); + if (obj->isChecked()) + break; + } + + if (!obj) + wid->setChecked(true); + //else + // RAISE_EVENT(EVENT_Click); + } +} + diff --git a/gb.qt4/src/CRadioButton.h b/gb.qt4/src/CRadioButton.h new file mode 100644 index 00000000..31ef5310 --- /dev/null +++ b/gb.qt4/src/CRadioButton.h @@ -0,0 +1,83 @@ +/*************************************************************************** + + CRadioButton.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRADIOBUTTON_H +#define __CRADIOBUTTON_H + +#include + +#include "gambas.h" + +#include "CWidget.h" + +#ifndef __CRADIOBUTTON_CPP +extern GB_DESC CRadioButtonDesc[]; +#else + +#define THIS ((CRADIOBUTTON *)_object) +#define WIDGET ((MyRadioButton *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + } + CRADIOBUTTON; + +class MyRadioButton : public QRadioButton +{ + Q_OBJECT + +public: + + explicit MyRadioButton(QWidget *parent); + void adjust(bool force = false); + bool isAutoResize() const { return _autoResize; } + void setAutoResize(bool a) { _autoResize = a; adjust(); } + +protected: + + virtual void changeEvent(QEvent *); + virtual void resizeEvent(QResizeEvent *); + +private: + + unsigned _autoResize : 1; +}; + +class CRadioButton : public QObject +{ + Q_OBJECT + +public: + + static CRadioButton manager; + +public slots: + + void clicked(bool on); + +}; + +#endif diff --git a/gb.qt4/src/CScreen.cpp b/gb.qt4/src/CScreen.cpp new file mode 100644 index 00000000..c5ff0656 --- /dev/null +++ b/gb.qt4/src/CScreen.cpp @@ -0,0 +1,597 @@ +/*************************************************************************** + + CScreen.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSCREEN_CPP + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" +#include "gb.draw.h" +#include "gb.image.h" +#include "cpaint_impl.h" +#include "CPicture.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CFont.h" +#include "CDrawingArea.h" +#include "CContainer.h" +#include "CScreen.h" + +#ifndef QT5 +#include +#include "x11.h" +#endif + +#ifdef QT5 + #define DESKTOP_INFO() (QGuiApplication::screens().front()->availableGeometry()) + #define SCREEN_INFO(_id) (QGuiApplication::screens().at(_id)->geometry()) + #define SCREEN_AVAILABLE_SIZE(_id) (QGuiApplication::screens().at(_id)->availableGeometry()) + #define NUM_SCREENS() (QGuiApplication::screens().count()) +#else + #define DESKTOP_INFO() (QApplication::desktop()->availableGeometry()) + #define SCREEN_INFO(_id) (QApplication::desktop()->screenGeometry(_id)) + #define SCREEN_AVAILABLE_SIZE(_id) (QApplication::desktop()->availableGeometry(_id)) + #if QT_VERSION >= 0x040600 + #define NUM_SCREENS() (QApplication::desktop()->screenCount()) + #else + #define NUM_SCREENS() (QApplication::desktop()->numScreens()) + #endif +#endif + +#define MAX_SCREEN 16 + +char *CAPPLICATION_Theme = 0; +GB_ARRAY CAPPLICATION_Restart = NULL; + +static int screen_busy = 0; +static CSCREEN *_screens[MAX_SCREEN] = { NULL }; + +static bool _animations = FALSE; +static bool _shadows = FALSE; +static bool _middle_click_paste = TRUE; + +static CSCREEN *get_screen(int num) +{ + if (num < 0 || num >= MAX_SCREEN || num >= NUM_SCREENS()) + { + GB.Error(GB_ERR_ARG); + return NULL; + } + + if (!_screens[num]) + { + _screens[num] = (CSCREEN *)GB.New(GB.FindClass("Screen"), NULL, 0); + _screens[num]->index = num; + GB.Ref(_screens[num]); + } + + return _screens[num]; +} + +static void free_screens(void) +{ + int i; + + for (i = 0; i < MAX_SCREEN; i++) + { + if (_screens[i]) + GB.Unref(POINTER(&_screens[i])); + } +} + +static void send_change_event() +{ + CDRAWINGAREA_send_change_event(); + CUSERCONTROL_send_change_event(); +} + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Desktop_X) + + GB.ReturnInteger(DESKTOP_INFO().x()); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Y) + + GB.ReturnInteger(DESKTOP_INFO().y()); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Width) + + GB.ReturnInteger(DESKTOP_INFO().width()); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Height) + + GB.ReturnInteger(DESKTOP_INFO().height()); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Resolution) + +#ifdef QT5 + GB.ReturnInteger(PLATFORM.Desktop.GetResolutionY()); +#else + #ifdef NO_X_WINDOW + GB.ReturnInteger(72); + #else + GB.ReturnInteger(QX11Info::appDpiY()); + #endif +#endif + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_HasSystemTray) + + #ifdef NO_X_WINDOW + GB.Return(FALSE); + #else + GB.ReturnBoolean(QSystemTrayIcon::isSystemTrayAvailable()); + //GB.ReturnBoolean(X11_get_system_tray() != None); + #endif + +END_PROPERTY + +BEGIN_METHOD(Desktop_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + GB.ReturnObject(CPICTURE_grab(0, -1, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, 0), VARGOPT(h, 0))); + +END_METHOD + + +BEGIN_PROPERTY(Desktop_Scale) + + GB.ReturnInteger(MAIN_scale); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Platform) + + GB.ReturnConstZeroString(MAIN_platform); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Application_exit) + + GB.FreeString(&CAPPLICATION_Theme); + GB.StoreObject(NULL, POINTER(&CAPPLICATION_Restart)); + free_screens(); + +END_METHOD + +static void set_font(QFont &font, void *object = 0) +{ + MAIN_update_scale(font); + qApp->setFont(font); +} + +BEGIN_PROPERTY(Application_Font) + + if (READ_PROPERTY) + GB.ReturnObject(CFONT_create(qApp->font(), set_font)); + else + CFONT_set(set_font, (CFONT *)VPROP(GB_OBJECT), NULL); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveWindow) + + //GB.ReturnObject(CWidget::get(qApp->activeWindow())); + GB.ReturnObject(CWINDOW_Active); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveControl) + + GB.ReturnObject(CWIDGET_active_control); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_PreviousControl) + + GB.ReturnObject(CWIDGET_previous_control); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Busy) + + int busy; + + if (READ_PROPERTY) + GB.ReturnInteger(screen_busy); + else + { + busy = VPROP(GB_INTEGER); + if (busy < 0) + busy = 0; + + if (screen_busy == 0 && busy > 0) + qApp->setOverrideCursor(Qt::WaitCursor); + else if (screen_busy > 0 && busy == 0) + qApp->restoreOverrideCursor(); + + screen_busy = busy; + if (MAIN_debug_busy) + qDebug("%s: Application.Busy = %d", GB.Debug.GetCurrentPosition(), busy); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ShowTooltips) + + if (READ_PROPERTY) + GB.ReturnBoolean(MyApplication::isTooltipEnabled()); + else + MyApplication::setTooltipEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Animations) + + if (READ_PROPERTY) + GB.ReturnBoolean(_animations); + else if (_animations != VPROP(GB_BOOLEAN)) + { + _animations = VPROP(GB_BOOLEAN); + send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_MiddleClickPaste) + + if (READ_PROPERTY) + GB.ReturnBoolean(_middle_click_paste); + else + _middle_click_paste = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Shadows) + + if (READ_PROPERTY) + GB.ReturnBoolean(_shadows); + else if (_shadows != VPROP(GB_BOOLEAN)) + { + _shadows = VPROP(GB_BOOLEAN); + send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_MainWindow) + + if (READ_PROPERTY) + GB.ReturnObject(CWINDOW_Main); + else + { + if (CWINDOW_Main && CWINDOW_Main->menuBar) + CWINDOW_Main->menuBar->setNativeMenuBar(false); + + CWINDOW_Main = (CWINDOW *)VPROP(GB_OBJECT); + if (CWINDOW_Main) + { + if (CWINDOW_MainDesktop >= 0) + { + MyMainWindow *win = (MyMainWindow *)CWINDOW_Main->widget.widget; + #ifdef QT5 + PLATFORM.Window.SetVirtualDesktop(win, win->isVisible(), CWINDOW_MainDesktop); + #else + X11_window_set_desktop(win->winId(), win->isVisible(), CWINDOW_MainDesktop); + #endif + CWINDOW_MainDesktop = -1; + } + + if (CWINDOW_Main->menuBar) + CWINDOW_Main->menuBar->setNativeMenuBar(true); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Embedder) + +#ifdef QT5 + if (READ_PROPERTY) + GB.ReturnInteger(0); + else + GB.Deprecated("gb.qt5", "Application.Embedder", NULL); +#else + if (READ_PROPERTY) + GB.ReturnInteger(CWINDOW_Embedder); + else + { + if (CWINDOW_Embedded) + { + GB.Error("Application is already embedded"); + return; + } + + CWINDOW_Embedder = VPROP(GB_INTEGER); + } + +#endif + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Theme) + + if (READ_PROPERTY) + GB.ReturnString(CAPPLICATION_Theme); + else + GB.StoreString(PROP(GB_STRING), &CAPPLICATION_Theme); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_DarkTheme) + + static bool _init = FALSE; + static bool _dark = FALSE; + + uint bg; + char *env; + + if (!_init) + { + _init = TRUE; + bg = QApplication::palette().color(QPalette::Window).rgb() & 0xFFFFFF; + if (IMAGE.GetLuminance(bg) >= 128) + { + env = getenv("GB_GUI_DARK_THEME"); + if (env && atoi(env)) + _dark = TRUE; + } + else + _dark = TRUE; + } + + GB.ReturnBoolean(_dark); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Restart) + + if (READ_PROPERTY) + GB.ReturnObject(CAPPLICATION_Restart); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&CAPPLICATION_Restart)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_DblClickTime) + + GB.ReturnInteger(QApplication::doubleClickInterval()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Screens_Count) + + GB.ReturnInteger(NUM_SCREENS()); + +END_PROPERTY + +/*BEGIN_PROPERTY(Screens_Primary) + + GB.ReturnInteger(QApplication::desktop()->primaryScreen()); + +END_PROPERTY*/ + +BEGIN_METHOD(Screens_get, GB_INTEGER screen) + + GB.ReturnObject(get_screen(VARG(screen))); + +END_METHOD + +BEGIN_METHOD_VOID(Screens_next) + + int *index = (int *)GB.GetEnum(); + + if (*index >= NUM_SCREENS()) + GB.StopEnum(); + else + { + GB.ReturnObject(get_screen(*index)); + (*index)++; + } + +END_METHOD + +BEGIN_PROPERTY(Screen_X) + + GB.ReturnInteger(SCREEN_INFO(SCREEN->index).x()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Y) + + GB.ReturnInteger(SCREEN_INFO(SCREEN->index).y()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Width) + + GB.ReturnInteger(SCREEN_INFO(SCREEN->index).width()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Height) + + GB.ReturnInteger(SCREEN_INFO(SCREEN->index).height()); + +END_PROPERTY + + +BEGIN_PROPERTY(Screen_AvailableX) + + GB.ReturnInteger(SCREEN_AVAILABLE_SIZE(SCREEN->index).x()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableY) + + GB.ReturnInteger(SCREEN_AVAILABLE_SIZE(SCREEN->index).y()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableWidth) + + GB.ReturnInteger(SCREEN_AVAILABLE_SIZE(SCREEN->index).width()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableHeight) + + GB.ReturnInteger(SCREEN_AVAILABLE_SIZE(SCREEN->index).height()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_ResolutionX) + +#ifdef QT5 + GB.ReturnFloat(QGuiApplication::screens().at(SCREEN->index)->physicalDotsPerInchX()); +#else + GB.ReturnFloat(QX11Info::appDpiX()); +#endif + +END_PROPERTY + +BEGIN_PROPERTY(Screen_ResolutionY) + +#ifdef QT5 + GB.ReturnFloat(QGuiApplication::screens().at(SCREEN->index)->physicalDotsPerInchY()); +#else + GB.ReturnFloat(QX11Info::appDpiY()); +#endif + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC ScreenDesc[] = +{ + GB_DECLARE("Screen", sizeof(CSCREEN)), GB_NOT_CREATABLE(), GB_AUTO_CREATABLE(), + + GB_PROPERTY_READ("X", "i", Screen_X), + GB_PROPERTY_READ("Y", "i", Screen_Y), + GB_PROPERTY_READ("W", "i", Screen_Width), + GB_PROPERTY_READ("H", "i", Screen_Height), + GB_PROPERTY_READ("Width", "i", Screen_Width), + GB_PROPERTY_READ("Height", "i", Screen_Height), + + GB_PROPERTY_READ("AvailableX", "i", Screen_AvailableX), + GB_PROPERTY_READ("AvailableY", "i", Screen_AvailableY), + GB_PROPERTY_READ("AvailableWidth", "i", Screen_AvailableWidth), + GB_PROPERTY_READ("AvailableHeight", "i", Screen_AvailableHeight), + + GB_PROPERTY_READ("ResolutionX", "f", Screen_ResolutionX), + GB_PROPERTY_READ("ResolutionY", "f", Screen_ResolutionY), + + GB_END_DECLARE +}; + +GB_DESC ScreensDesc[] = +{ + GB_DECLARE("Screens", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("Count", "i", Screens_Count), + GB_STATIC_METHOD("_get", "Screen", Screens_get, "(Screen)i"), + GB_STATIC_METHOD("_next", "Screen", Screens_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC DesktopDesc[] = +{ + GB_DECLARE("Desktop", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("X", "i", Desktop_X), + GB_STATIC_PROPERTY_READ("Y", "i", Desktop_Y), + GB_STATIC_PROPERTY_READ("W", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("H", "i", Desktop_Height), + GB_STATIC_PROPERTY_READ("Width", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("Height", "i", Desktop_Height), + + GB_CONSTANT("Charset", "s", "UTF-8"), + GB_STATIC_PROPERTY_READ("Resolution", "i", Desktop_Resolution), + GB_STATIC_PROPERTY_READ("Scale", "i", Desktop_Scale), + + GB_STATIC_PROPERTY_READ("HasSystemTray", "b", Desktop_HasSystemTray), + + GB_STATIC_METHOD("Screenshot", "Picture", Desktop_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_STATIC_PROPERTY_READ("Platform", "s", Desktop_Platform), + + GB_END_DECLARE +}; + +GB_DESC ApplicationDesc[] = +{ + GB_DECLARE("Application", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Application_exit, NULL), + GB_STATIC_PROPERTY("Font", "Font", Application_Font), + GB_STATIC_PROPERTY_READ("ActiveWindow", "Window", Application_ActiveWindow), + GB_STATIC_PROPERTY_READ("ActiveControl", "Control", Application_ActiveControl), + GB_STATIC_PROPERTY_READ("PreviousControl", "Control", Application_PreviousControl), + GB_STATIC_PROPERTY("MainWindow", "Window", Application_MainWindow), + GB_STATIC_PROPERTY("Busy", "i", Application_Busy), + GB_STATIC_PROPERTY("ShowTooltips", "b", Application_ShowTooltips), + GB_STATIC_PROPERTY("Animations", "b", Application_Animations), + GB_STATIC_PROPERTY("MiddleClickPaste", "b", Application_MiddleClickPaste), + GB_STATIC_PROPERTY("Shadows", "b", Application_Shadows), + GB_STATIC_PROPERTY("Embedder", "i", Application_Embedder), + GB_STATIC_PROPERTY("Theme", "s", Application_Theme), + GB_STATIC_PROPERTY_READ("DarkTheme", "b", Application_DarkTheme), + GB_STATIC_PROPERTY("Restart", "String[]", Application_Restart), + GB_STATIC_PROPERTY_READ("DblClickTime", "i", Application_DblClickTime), + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/CScreen.h b/gb.qt4/src/CScreen.h new file mode 100644 index 00000000..79e82fdf --- /dev/null +++ b/gb.qt4/src/CScreen.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CScreen.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSCREEN_H +#define __CSCREEN_H + +#include "gambas.h" + +#ifndef __CSCREEN_CPP +extern GB_DESC DesktopDesc[]; +extern GB_DESC ApplicationSessionDesc[]; +extern GB_DESC ApplicationDesc[]; +extern GB_DESC ScreensDesc[]; +extern GB_DESC ScreenDesc[]; +extern GB_DESC StyleDesc[]; + +extern char *CAPPLICATION_Theme; +extern GB_ARRAY CAPPLICATION_Restart; +#else + +#define SCREEN ((CSCREEN *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + int index; + } + CSCREEN; + +#endif diff --git a/gb.qt4/src/CScrollBar.cpp b/gb.qt4/src/CScrollBar.cpp new file mode 100644 index 00000000..a77586ab --- /dev/null +++ b/gb.qt4/src/CScrollBar.cpp @@ -0,0 +1,246 @@ +/*************************************************************************** + + CScrollBar.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSCROLLBAR_CPP + +#include "main.h" +#include "gambas.h" + +#include + +#include "gb.form.const.h" +#include "CWidget.h" +#include "CScrollBar.h" + +DECLARE_EVENT(EVENT_Change); + +/*************************************************************************** + + class MyScrollBar + +***************************************************************************/ + + +MyScrollBar::MyScrollBar(QWidget *parent) +: QScrollBar(parent) +{ +} + +void MyScrollBar::updateOrientation() +{ + CSCROLLBAR *_object = (CSCROLLBAR *)CWidget::get(this); + + if (!THIS->widget.flag.orientation) + { + if (width() >= height()) + setOrientation(Qt::Horizontal); + else + setOrientation(Qt::Vertical); + } +} + +void MyScrollBar::resizeEvent(QResizeEvent *e) +{ + CSCROLLBAR *_object = (CSCROLLBAR *)CWidget::get(this); + + QScrollBar::resizeEvent(e); + + if (!THIS->widget.flag.orientation) + updateOrientation(); +} + + +/*************************************************************************** + + ScrollBar + +***************************************************************************/ + +BEGIN_METHOD(ScrollBar_new, GB_OBJECT parent) + + MyScrollBar *wid = new MyScrollBar(QCONTAINER(VARG(parent))); + + THIS->widget.flag.wheel = true; + + QObject::connect(wid, SIGNAL(valueChanged(int)), &CScrollBar::manager, + SLOT(event_change())); + + wid->setTracking(true); + wid->setMinimum(0); + wid->setMaximum(100); + wid->setSingleStep(1); + wid->setPageStep(10); + + CWIDGET_new(wid, _object); + +END_METHOD + + +BEGIN_PROPERTY(ScrollBar_Tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasTracking()); + else + WIDGET->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +/* +BEGIN_PROPERTY(CSCROLLBAR_orientation) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->orientation()); + else + { + switch PROPERTY(GB_INTEGER) + { + case Qt::Vertical: WIDGET->setOrientation(Qt::Vertical);break; + case Qt::Horizontal: WIDGET->setOrientation(Qt::Horizontal);break; + default: WIDGET->setOrientation(Qt::Vertical); + } + } + +END_PROPERTY +*/ + +BEGIN_PROPERTY(ScrollBar_Value) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->value()); + else + WIDGET->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(ScrollBar_MinVal) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->minimum()); + else + WIDGET->setMinimum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(ScrollBar_MaxVal) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->maximum()); + else + WIDGET->setMaximum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(ScrollBar_LineStep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->singleStep()); + else + WIDGET->setSingleStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(ScrollBar_PageStep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->pageStep()); + else + WIDGET->setPageStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(ScrollBar_DefaultSize) + + GB.ReturnInteger(WIDGET->style()->pixelMetric(QStyle::PM_ScrollBarExtent)); + +END_PROPERTY + +BEGIN_PROPERTY(ScrollBar_Orientation) + + if (READ_PROPERTY) + { + if (!THIS->widget.flag.orientation) + GB.ReturnInteger(ORIENTATION_AUTO); + else if (WIDGET->orientation() == Qt::Vertical) + GB.ReturnInteger(ORIENTATION_VERTICAL); + else + GB.ReturnInteger(ORIENTATION_HORIZONTAL); + } + else + { + switch(VPROP(GB_INTEGER)) + { + case ORIENTATION_HORIZONTAL: + WIDGET->setOrientation(Qt::Horizontal); + THIS->widget.flag.orientation = true; + break; + case ORIENTATION_VERTICAL: + WIDGET->setOrientation(Qt::Vertical); + THIS->widget.flag.orientation = true; + break; + default: + THIS->widget.flag.orientation = false; + WIDGET->updateOrientation(); + } + } + +END_PROPERTY + +//------------------------------------------------------------------------- + +CScrollBar CScrollBar::manager; + +void CScrollBar::event_change(void) +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_Change, 0); +} + +//------------------------------------------------------------------------- + +GB_DESC ScrollBarDesc[] = +{ + GB_DECLARE("ScrollBar", sizeof(CSCROLLBAR)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, ScrollBar_new, "(Parent)Container;"), + + GB_PROPERTY_READ("DefaultSize", "i", ScrollBar_DefaultSize), + + GB_PROPERTY("Tracking", "b", ScrollBar_Tracking), + GB_PROPERTY("Value", "i", ScrollBar_Value), + GB_PROPERTY("MinValue", "i", ScrollBar_MinVal), + GB_PROPERTY("MaxValue", "i", ScrollBar_MaxVal), + GB_PROPERTY("Step", "i", ScrollBar_LineStep), + GB_PROPERTY("PageStep", "i", ScrollBar_PageStep), + GB_PROPERTY("Orientation", "i", ScrollBar_Orientation), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + SCROLLBAR_DESCRIPTION, + + GB_CONSTANT("Auto", "i", ORIENTATION_AUTO), + GB_CONSTANT("Horizontal", "i", ORIENTATION_HORIZONTAL), + GB_CONSTANT("Vertical", "i", ORIENTATION_VERTICAL), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CScrollBar.h b/gb.qt4/src/CScrollBar.h new file mode 100644 index 00000000..373bc246 --- /dev/null +++ b/gb.qt4/src/CScrollBar.h @@ -0,0 +1,81 @@ +/*************************************************************************** + + CScrollBar.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSCROLLBAR_H +#define __CSCROLLBAR_H + +#include +#include +#include "gb.qt.h" +#include "gambas.h" +#include "CWidget.h" + +#ifndef __CSCROLLBAR_CPP + +extern GB_DESC ScrollBarDesc[]; + +#else + +#define THIS ((CSCROLLBAR *)_object) +#define WIDGET ((MyScrollBar *)((QT_WIDGET *)_object)->widget) + +#endif + + +typedef + struct { + CWIDGET widget; + } + CSCROLLBAR; + + +class MyScrollBar : public QScrollBar +{ + Q_OBJECT + +public: + + explicit MyScrollBar(QWidget *); + void updateOrientation(); + +protected: + + void resizeEvent(QResizeEvent *); +}; + + +class CScrollBar : public QObject +{ + Q_OBJECT + +public: + + static CScrollBar manager; + +public slots: + + void event_change(void); + +}; + +#endif diff --git a/gb.qt4/src/CSlider.cpp b/gb.qt4/src/CSlider.cpp new file mode 100644 index 00000000..d813f607 --- /dev/null +++ b/gb.qt4/src/CSlider.cpp @@ -0,0 +1,290 @@ +/*************************************************************************** + + CSlider.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSLIDER_CPP + +#include "main.h" +#include "gambas.h" + +#include "gb.form.const.h" +#include "CWidget.h" +#include "CSlider.h" + +DECLARE_EVENT(EVENT_Change); + + +/*************************************************************************** + + class MySlider + +***************************************************************************/ + + +MySlider::MySlider(QWidget *parent) +: QSlider(parent) +{ +} + +void MySlider::updateOrientation() +{ + CSLIDER *_object = (CSLIDER *)CWidget::get(this); + + if (!THIS->widget.flag.orientation) + { + if (width() >= height()) + setOrientation(Qt::Horizontal); + else + setOrientation(Qt::Vertical); + } +} + +void MySlider::resizeEvent(QResizeEvent *e) +{ + CSLIDER *_object = (CSLIDER *)CWidget::get(this); + + QSlider::resizeEvent(e); + + if (!THIS->widget.flag.orientation) + WIDGET->updateOrientation(); +} + + +/*************************************************************************** + + Slider + +***************************************************************************/ + +BEGIN_METHOD(Slider_new, GB_OBJECT parent) + + MySlider *wid = new MySlider(QCONTAINER(VARG(parent))); + + THIS->widget.flag.wheel = true; + + QObject::connect(wid, SIGNAL(valueChanged(int)), &CSlider::manager, SLOT(event_change())); + //QObject::connect(wid, SIGNAL(sliderPressed()), &CSlider::manager, + //SLOT(event_sliderpressed())); + //QObject::connect(wid, SIGNAL(sliderMoved(int)), &CSlider::manager, + //SLOT(event_slidermove())); + //QObject::connect(wid, SIGNAL(sliderReleased()), &CSlider::manager, + //SLOT(event_sliderreleased())); + + wid->setTracking(true); //Set the tracking off by default + wid->setMinimum(0); + wid->setMaximum(100); + wid->setSingleStep(1); + wid->setPageStep(10); + + CWIDGET_new(wid, _object); + +END_METHOD + + +BEGIN_PROPERTY(Slider_Tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasTracking()); + else + WIDGET->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CSLIDER_tickinterval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->tickInterval()); + else + WIDGET->setTickInterval(VPROP(GB_INTEGER)); + +END_PROPERTY*/ + +/* +BEGIN_PROPERTY(CSLIDER_orientation) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->orientation()); + else + { + switch PROPERTY(GB_INTEGER) + { + case Qt::Vertical: WIDGET->setOrientation(Qt::Vertical);break; + case Qt::Horizontal: WIDGET->setOrientation(Qt::Horizontal);break; + default: WIDGET->setOrientation(Qt::Vertical); + } + } + +END_PROPERTY +*/ + +BEGIN_PROPERTY(Slider_Value) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->value()); + else + WIDGET->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +/* +BEGIN_PROPERTY(CSLIDER_tickmarks) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->tickmarks()); + else + { + switch PROPERTY(GB_INTEGER) + { + case QSlider::NoMarks: WIDGET->setTickmarks(QSlider::NoMarks);break; + case QSlider::Both: WIDGET->setTickmarks(QSlider::Both);break; + case QSlider::Above: WIDGET->setTickmarks(QSlider::Above);break; + case QSlider::Below: WIDGET->setTickmarks(QSlider::Below);break; + //case QSlider::Left: WIDGET->setTickmarks(QSlider::Left);break; + //case QSlider::Right: WIDGET->setTickmarks(QSlider::Right);break; + default: WIDGET->setTickmarks(QSlider::NoMarks); + } + } + +END_PROPERTY +*/ + + +BEGIN_PROPERTY(Slider_Mark) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->tickPosition() != QSlider::NoTicks); + else + { + if (VPROP(GB_BOOLEAN)) + WIDGET->setTickPosition(QSlider::TicksBothSides); + else + WIDGET->setTickPosition(QSlider::NoTicks); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Slider_MinValue) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->minimum()); + else + WIDGET->setMinimum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_MaxValue) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->maximum()); + else + WIDGET->setMaximum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_LineStep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->singleStep()); + else + WIDGET->setSingleStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_PageStep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->pageStep()); + else + { + WIDGET->setPageStep(VPROP(GB_INTEGER)); + WIDGET->update(); + } + +END_PROPERTY + +BEGIN_PROPERTY(Slider_Orientation) + + if (READ_PROPERTY) + { + if (!THIS->widget.flag.orientation) + GB.ReturnInteger(ORIENTATION_AUTO); + else if (WIDGET->orientation() == Qt::Vertical) + GB.ReturnInteger(ORIENTATION_VERTICAL); + else + GB.ReturnInteger(ORIENTATION_HORIZONTAL); + } + else + { + switch(VPROP(GB_INTEGER)) + { + case ORIENTATION_HORIZONTAL: + WIDGET->setOrientation(Qt::Horizontal); + THIS->widget.flag.orientation = true; + break; + case ORIENTATION_VERTICAL: + WIDGET->setOrientation(Qt::Vertical); + THIS->widget.flag.orientation = true; + break; + default: + THIS->widget.flag.orientation = false; + WIDGET->updateOrientation(); + } + } + +END_PROPERTY + +//------------------------------------------------------------------------- + +CSlider CSlider::manager; + +void CSlider::event_change(void) +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_Change, 0); +} + +//------------------------------------------------------------------------- + +GB_DESC SliderDesc[] = +{ + GB_DECLARE("Slider", sizeof(CSLIDER)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, Slider_new, "(Parent)Container;"), + + GB_PROPERTY("Tracking", "b", Slider_Tracking), + GB_PROPERTY("Value", "i", Slider_Value), + GB_PROPERTY("Mark", "b", Slider_Mark), + GB_PROPERTY("MinValue", "i", Slider_MinValue), + GB_PROPERTY("MaxValue", "i", Slider_MaxValue), + GB_PROPERTY("Step", "i", Slider_LineStep), + GB_PROPERTY("PageStep", "i", Slider_PageStep), + GB_PROPERTY("Orientation", "i", Slider_Orientation), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + SLIDER_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CSlider.h b/gb.qt4/src/CSlider.h new file mode 100644 index 00000000..f7636085 --- /dev/null +++ b/gb.qt4/src/CSlider.h @@ -0,0 +1,84 @@ +/*************************************************************************** + + CSlider.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSLIDER_H +#define __CSLIDER_H + +#include +#include + +#include "gambas.h" + +#include "CWidget.h" + +#ifndef __CSLIDER_CPP + +extern GB_DESC SliderDesc[]; + +#else + +#define THIS ((CSLIDER *)_object) +#define WIDGET ((MySlider *)((QT_WIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + } + CSLIDER; + +class MySlider : public QSlider +{ + Q_OBJECT + +public: + + explicit MySlider(QWidget *); + void updateOrientation(); + +protected: + + void resizeEvent(QResizeEvent *); +}; + + + +class CSlider : public QObject +{ + Q_OBJECT + +public: + + static CSlider manager; + +public slots: + + void event_change(void); + //void event_sliderpressed(void); + //void event_slidermove(void); + //void event_sliderreleased(void); + +}; + +#endif diff --git a/gb.qt4/src/CStyle.cpp b/gb.qt4/src/CStyle.cpp new file mode 100644 index 00000000..24e15906 --- /dev/null +++ b/gb.qt4/src/CStyle.cpp @@ -0,0 +1,566 @@ +/*************************************************************************** + + CStyle.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CStyle_CPP + +#include +#include + +#include "gambas.h" +#include "main.h" +#include "gb.draw.h" +#include "cpaint_impl.h" +#include "CPicture.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CFont.h" +#include "CScreen.h" + +bool CSTYLE_fix_breeze = false; +bool CSTYLE_fix_oxygen = false; + +static char *_style_name = NULL; + +static QWidget *_fake = 0; + +static bool _is_breeze = false; +static bool _is_oxygen = false; +static bool _is_windows = false; +static bool _is_gtk = false; +static bool _is_qtcurve = false; +static bool _is_plastique = false; + +static QWidget *get_fake_widget() +{ + if (!_fake) + _fake = new QWidget; + return _fake; +} + +static char *get_style_name() +{ + if (!_style_name) + { + if (CSTYLE_fix_breeze) + { + _style_name = GB.NewZeroString("breeze"); + } + else if (CSTYLE_fix_oxygen) + { + _style_name = GB.NewZeroString("oxygen"); + } + else + { + const char *name = qApp->style()->metaObject()->className(); + int len = strlen(name); + + if (len >= 6 && strncasecmp(&name[len - 5], "style", 5) == 0) + len -= 5; + if (len >= 3 && strncmp(&name[len - 2], "::", 2) == 0) + len -= 2; + if (name[0] == 'Q' && isupper(name[1])) + { + len--; + name++; + } + + _style_name = GB.NewString(NULL, len); + for (int i = 0; i < len; i++) + _style_name[i] = tolower(name[i]); + } + + _is_breeze = ::strcmp(_style_name, "breeze") == 0; + _is_oxygen = ::strcmp(_style_name, "oxygen") == 0; + _is_windows = ::strcmp(_style_name, "windows") == 0; + _is_gtk = ::strcmp(_style_name, "gtk") == 0; + _is_qtcurve = ::strcmp(_style_name, "qtcurve") == 0; + _is_plastique = ::strcmp(_style_name, "plastique") == 0; + } + + return _style_name; +} + +static void init_option(QStyleOption &opt, int x, int y, int w, int h, int state, GB_COLOR color = COLOR_DEFAULT, QPalette::ColorRole role = QPalette::Window) +{ + opt.rect = QRect(x, y, w ,h); + opt.state = QStyle::State_None; + + if (!(state & GB_DRAW_STATE_DISABLED)) + opt.state |= QStyle::State_Enabled; + if (state & GB_DRAW_STATE_FOCUS) + opt.state |= QStyle::State_HasFocus | QStyle::State_KeyboardFocusChange; + if (state & GB_DRAW_STATE_HOVER) + opt.state |= QStyle::State_MouseOver; + if (state & GB_DRAW_STATE_ACTIVE) + opt.state |= QStyle::State_On | QStyle::State_Sunken | QStyle::State_Active; + + if (color != GB_COLOR_DEFAULT) + { + QPalette palette; + palette.setColor(role, TO_QCOLOR(color)); + opt.palette = palette; + } + + if (state & GB_DRAW_STATE_DISABLED) + opt.palette.setCurrentColorGroup(QPalette::Disabled); +} + +static void paint_focus(QPainter *p, int x, int y, int w, int h, int state) +{ + //bool do_clip = FALSE; + QStyleOptionFocusRect opt; + + if ((state & GB_DRAW_STATE_DISABLED) || !(state & GB_DRAW_STATE_FOCUS)) + return; + + init_option(opt, x, y, w, h, state); + + /*if (::strcmp(qApp->style()->metaObject()->className(), "QtCurve::Style") == 0) + { + QPainterPath clip; + + p->save(); + + clip.addRect(x, y, w, 1); + clip.addRect(x, y, 1, h); + clip.addRect(x, y + h - 1, w, 1); + clip.addRect(x + w - 1, y, 1, h); + p->setClipPath(clip); + do_clip = TRUE; + }*/ + + p->save(); + p->setBrush(QBrush()); + + QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p); + + p->restore(); + + /*if (do_clip) + p->restore();*/ +} + +static void style_arrow(QPainter *p, int x, int y, int w, int h, int type, int state) +{ + QStyleOption opt; + QStyle::PrimitiveElement pe; + + init_option(opt, x, y, w, h, state); + + switch (type) + { + case ALIGN_NORMAL: pe = GB.System.IsRightToLeft() ? QStyle::PE_IndicatorArrowLeft : QStyle::PE_IndicatorArrowRight; break; + case ALIGN_LEFT: pe = QStyle::PE_IndicatorArrowLeft; break; + case ALIGN_RIGHT: pe = QStyle::PE_IndicatorArrowRight; break; + case ALIGN_TOP: pe = QStyle::PE_IndicatorArrowUp; break; + case ALIGN_BOTTOM: pe = QStyle::PE_IndicatorArrowDown; break; + default: + return; + } + + QApplication::style()->drawPrimitive(pe, &opt, p); +} + +static void style_check(QPainter *p, int x, int y, int w, int h, int value, int state) +{ + QStyleOptionButton opt; + int d; + + get_style_name(); + if (_is_oxygen || _is_breeze) + d = 2; + else + d = 0; + + x -= d; + y -= d; + w += d * 2; + h += d * 2; + + init_option(opt, x, y, w, h, state); + + if (value == 1) + opt.state |= QStyle::State_NoChange; + else if (value) + opt.state |= QStyle::State_On; + else + opt.state |= QStyle::State_Off; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, p); + paint_focus(p, x, y, w, h, state); +} + +static void style_option(QPainter *p, int x, int y, int w, int h, int value, int state) +{ + QStyleOptionButton opt; + int d; + + get_style_name(); + if (_is_oxygen || _is_breeze) + d = 2; + else + d = 0; + + x -= d; + y -= d; + w += d * 2; + h += d * 2; + + init_option(opt, x, y, w, h, state); + + if (value) + opt.state |= QStyle::State_On; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorRadioButton, &opt, p); + paint_focus(p, x, y, w, h, state); +} + +static void style_separator(QPainter *p, int x, int y, int w, int h, int vertical, int state) +{ + QStyleOption opt; + init_option(opt, x, y, w, h, state); + + if (vertical) + opt.state |= QStyle::State_Horizontal; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, p); +} + + +static void style_button(QPainter *p, int x, int y, int w, int h, int value, int state, int flat, GB_COLOR color) +{ + if (flat) + { + QStyleOptionToolButton opt; + init_option(opt, x, y, w, h, state, color, QPalette::Button); + + //opt.state |= QStyle::State_Raised; + + if (value) + opt.state |= QStyle::State_On; + + //opt.state &= ~QStyle::State_HasFocus; + opt.state |= QStyle::State_AutoRaise; + if (opt.state & QStyle::State_MouseOver) + opt.state |= QStyle::State_Raised; + + if (opt.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_MouseOver)) + { + QApplication::style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p); + } + } + else + { + QStyleOptionButton opt; + init_option(opt, x, y, w, h, state, color, QPalette::Button); + + opt.state |= QStyle::State_Raised; + + if (value) + opt.state |= QStyle::State_On; + + QApplication::style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, p); + } + + paint_focus(p, x, y, w, h, state); +} + +static void style_panel(QPainter *p, int x, int y, int w, int h, int border, int state) +{ + QStyleOptionFrame opt; + init_option(opt, x, y, w, h, state); + + CCONTAINER_draw_border_without_widget(p, border, opt); +} + +static void style_handle(QPainter *p, int x, int y, int w, int h, int vertical, int state) +{ + QStyleOption opt; + init_option(opt, x, y, w, h, state); + + if (!vertical) + opt.state |= QStyle::State_Horizontal; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, p); + paint_focus(p, x, y, w, h, state); +} + + +static void style_box(QPainter *p, int x, int y, int w, int h, int state, GB_COLOR color) +{ + QStyleOptionFrame opt; + + //if (GB.Is(d->device, CLASS_DrawingArea)) + // opt.begin(QWIDGET(d->device)); + + init_option(opt, x, y, w, h, state, color, QPalette::Base); + + opt.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt); + opt.midLineWidth = 0; + opt.state |= QStyle::State_Sunken; + p->save(); + p->setBrush(Qt::NoBrush); + //opt.features = QStyleOptionFrameV2::None; + + if (color == GB_COLOR_DEFAULT) + QApplication::style()->drawPrimitive(QStyle::PE_FrameLineEdit, &opt, p); + else + { + get_style_name(); + if (_is_gtk) + { + QWidget *w = get_fake_widget(); + w->setAttribute(Qt::WA_SetPalette, true); + QApplication::style()->drawPrimitive(QStyle::PE_PanelLineEdit, &opt, p, w); + w->setAttribute(Qt::WA_SetPalette, false); + } + else + QApplication::style()->drawPrimitive(QStyle::PE_PanelLineEdit, &opt, p); + } + + p->restore(); + //paint_focus(d, x, y, w, h, state); + //if (state & GB_DRAW_STATE_FOCUS) + // QApplication::style()->drawControl(QStyle::CE_FocusFrame, &opt, DP(d), GB.Is(d->device, CLASS_DrawingArea) ? QWIDGET(d->device) : NULL); +} + +BEGIN_PROPERTY(Style_ScrollbarSize) + + GB.ReturnInteger(qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent)); + +END_PROPERTY + +BEGIN_PROPERTY(Style_ScrollbarSpacing) + + get_style_name(); + if (_is_breeze) + GB.ReturnInteger(0); + else + GB.ReturnInteger(qMax(0, qApp->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing))); + +END_PROPERTY + +BEGIN_PROPERTY(Style_FrameWidth) + + get_style_name(); + if (_is_breeze) + GB.ReturnInteger(2); + else + GB.ReturnInteger(qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth)); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameWidth) + + int w = qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameHeight) + + int w = qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY(Style_Name) + + GB.ReturnString(get_style_name()); + +END_PROPERTY + +#define GET_COORD() \ + int x, y, w, h; \ +\ + QPainter *p = PAINT_get_current(); \ + if (!p) \ + return; \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (w < 1 || h < 1) \ + return; + +BEGIN_METHOD(Style_PaintArrow, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER type; GB_INTEGER state) + + GET_COORD(); + style_arrow(p, x, y, w, h, VARG(type), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintCheck, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER value; GB_INTEGER state) + + GET_COORD(); + style_check(p, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintOption, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state) + + GET_COORD(); + style_option(p, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintSeparator, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + style_separator(p, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintButton, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state; GB_BOOLEAN flat; GB_INTEGER color) + + GET_COORD(); + style_button(p, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(flat, FALSE), VARGOPT(color, GB_COLOR_DEFAULT)); + +END_METHOD + +BEGIN_METHOD(Style_PaintPanel, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER border; GB_INTEGER state) + + GET_COORD(); + style_panel(p, x, y, w, h, VARG(border), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintHandle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + style_handle(p, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintBox, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER state; GB_INTEGER color) + + GET_COORD(); + style_box(p, x, y, w, h, VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(color, GB_COLOR_DEFAULT)); + +END_METHOD + +BEGIN_METHOD(Style_StateOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + QWidget *widget; + int state; + bool design; + + if (GB.CheckObject(control)) + return; + + widget = QWIDGET(control); + design = CWIDGET_is_design(control); + + state = GB_DRAW_STATE_NORMAL; + if (!widget->isEnabled()) + state |= GB_DRAW_STATE_DISABLED; + if (widget->hasFocus() && !design) + state |= GB_DRAW_STATE_FOCUS; + if (CWIDGET_is_visible(control) && control->flag.inside && !design) + state |= GB_DRAW_STATE_HOVER; + + GB.ReturnInteger(state); + +END_METHOD + +BEGIN_METHOD(Style_BackgroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(CWIDGET_get_real_background(control)); + +END_METHOD + +BEGIN_METHOD(Style_ForegroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(CWIDGET_get_real_foreground(control)); + +END_METHOD + +#if 0 +BEGIN_METHOD(Style_FontOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnObject(CWIDGET_get_real_font(control)); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Style_exit) + + GB.FreeString(&_style_name); + +END_METHOD + +GB_DESC StyleDesc[] = +{ + GB_DECLARE("Style", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Style_exit, NULL), + + GB_STATIC_PROPERTY_READ("ScrollbarSize", "i", Style_ScrollbarSize), + GB_STATIC_PROPERTY_READ("ScrollbarSpacing", "i", Style_ScrollbarSpacing), + GB_STATIC_PROPERTY_READ("FrameWidth", "i", Style_FrameWidth), + GB_STATIC_PROPERTY_READ("TextBoxFrameWidth", "i", Style_FrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameWidth", "i", Style_BoxFrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameHeight", "i", Style_BoxFrameHeight), + GB_STATIC_PROPERTY_READ("Name", "s", Style_Name), + + GB_STATIC_METHOD("PaintArrow", NULL, Style_PaintArrow, "(X)i(Y)i(Width)i(Height)i(Type)i[(Flag)i]"), + GB_STATIC_METHOD("PaintCheck", NULL, Style_PaintCheck, "(X)i(Y)i(Width)i(Height)i(Value)i[(Flag)i]"), + GB_STATIC_METHOD("PaintOption", NULL, Style_PaintOption, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i]"), + GB_STATIC_METHOD("PaintSeparator", NULL, Style_PaintSeparator, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintButton", NULL, Style_PaintButton, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i(Flat)b(Color)i]"), + GB_STATIC_METHOD("PaintPanel", NULL, Style_PaintPanel, "(X)i(Y)i(Width)i(Height)i(Border)i[(Flag)i]"), + GB_STATIC_METHOD("PaintHandle", NULL, Style_PaintHandle, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintBox", NULL, Style_PaintBox, "(X)i(Y)i(Width)i(Height)i[(Flag)i(Color)i]"), + + GB_CONSTANT("Normal", "i", GB_DRAW_STATE_NORMAL), + GB_CONSTANT("Disabled", "i", GB_DRAW_STATE_DISABLED), + GB_CONSTANT("HasFocus", "i", GB_DRAW_STATE_FOCUS), + GB_CONSTANT("Hovered", "i", GB_DRAW_STATE_HOVER), + GB_CONSTANT("Active", "i", GB_DRAW_STATE_ACTIVE), + + GB_STATIC_METHOD("StateOf", "i", Style_StateOf, "(Control)Control;"), + GB_STATIC_METHOD("BackgroundOf", "i", Style_BackgroundOf, "(Control)Control;"), + GB_STATIC_METHOD("ForegroundOf", "i", Style_ForegroundOf, "(Control)Control;"), + //GB_STATIC_METHOD("FontOf", "Font", Style_FontOf, "(Control)Control;"), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CStyle.h b/gb.qt4/src/CStyle.h new file mode 100644 index 00000000..352a32d5 --- /dev/null +++ b/gb.qt4/src/CStyle.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + CStyle.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSTYLE_H +#define __CSTYLE_H + +#include "gambas.h" + +#ifndef __CSTYLE_CPP +extern GB_DESC StyleDesc[]; +extern bool CSTYLE_fix_breeze; +extern bool CSTYLE_fix_oxygen; +#endif + +#endif diff --git a/gb.qt4/src/CTabStrip.cpp b/gb.qt4/src/CTabStrip.cpp new file mode 100644 index 00000000..706509e8 --- /dev/null +++ b/gb.qt4/src/CTabStrip.cpp @@ -0,0 +1,911 @@ +/*************************************************************************** + + CTabStrip.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTABSTRIP_CPP + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" + +#include "CPicture.h" +#include "CConst.h" +#include "CTabStrip.h" + + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Close); + +void CTABSTRIP_arrange(void *_object) +{ + WIDGET->layoutContainer(); +} + +/** CTab *****************************************************************/ + +class CTab +{ +public: + QWidget *widget; + QString text; + CPICTURE *icon; + int id; + bool visible; + bool enabled; + + CTab(CTABSTRIP *parent, QWidget *page); + ~CTab(); + + int index() const { return WIDGET->indexOf(widget); } + void ensureVisible(); + void setEnabled(bool e); + bool isEnabled() const { return enabled; } + bool isVisible() const { return visible; } + void setVisible(bool v); + void updateIcon(); + void updateText(); + int count() const; + bool isEmpty() const { return count() == 0; } + +private: + CTABSTRIP *_object; +}; + +CTab::CTab(CTABSTRIP *parent, QWidget *page) +{ + _object = parent; + widget = page; + icon = 0; + id = WIDGET->stack.count(); + visible = true; + setEnabled(true); + + //page->setAutoFillBackground(true); + page->hide(); +} + +CTab::~CTab() +{ + GB.Unref(POINTER(&icon)); +} + +void CTab::ensureVisible() +{ + setVisible(true); + int i = index(); + if (i >= 0) + { + WIDGET->setCurrentIndex(i); + if (!WIDGET->isVisible()) + WIDGET->layoutContainer(); + } +} + +int CTab::count() const +{ + QObjectList list = widget->children(); + QObject *ob; + int n = 0; + int i; + + for(i = 0; i < list.count(); i++) + { + ob = list.at(i); + if (ob->isWidgetType() && CWidget::getRealExisting(ob)) + n++; + } + + return n; +} + +void CTab::setVisible(bool v) +{ + int i, ind; + CTab *page; + + if (v == visible) + return; + + visible = v; + + if (!visible) + { + i = index(); + if (i >= 0) + { + text = WIDGET->tabText(i); + WIDGET->removeTab(i); + } + } + else + { + ind = 0; + for (i = 0; i < (int)WIDGET->stack.count(); i++) + { + page = WIDGET->stack.at(i); + if (!page->isVisible()) + continue; + if (id == WIDGET->stack.at(i)->id) + break; + ind++; + } + WIDGET->insertTab(ind, widget, text); + setEnabled(enabled); + updateIcon(); + if (WIDGET->count() == 1) + ensureVisible(); + } +} + +void CTab::setEnabled(bool e) +{ + int i = index(); + enabled = e; + if (i >= 0) + WIDGET->setTabEnabled(i, e && WIDGET->isEnabled()); +} + +void CTab::updateIcon() +{ + int i = index(); + QIcon iconset; + + if (icon) + CWIDGET_iconset(iconset, *(icon->pixmap)); + + if (i >= 0) + WIDGET->setTabIcon(i, iconset); +} + +void CTab::updateText() +{ + int i = index(); + if (i >= 0) + WIDGET->setTabText(i, text); +} + +/** MyTabWidget **********************************************************/ + +MyTabWidget::MyTabWidget(QWidget *parent) : QTabWidget(parent) +{ + _oldw = _oldh = 0; + //setMovable(true); + //tabBar()->installEventFilter(this); +} + +MyTabWidget::~MyTabWidget() +{ + int i; + CWIDGET *_object = CWidget::getReal(this); + + for (i = 0; i < stack.count(); i++) + delete stack.at(i); + + THIS->widget.flag.deleted = true; +} + +void MyTabWidget::setEnabled(bool e) +{ + CWIDGET *_object = CWidget::get(this); + int i; + + QTabWidget::setEnabled(e); + + for (i = 0; i < (int)WIDGET->stack.count(); i++) + WIDGET->stack.at(i)->widget->setEnabled(e); +} + +void MyTabWidget::layoutContainer() +{ + CWIDGET *_object = CWidget::get(this); + #if QT_VERSION >= 0x040600 && QT_VERSION < 0x050700 + QStyleOptionTabWidgetFrameV2 option; + #else + QStyleOptionTabWidgetFrame option; + #endif + QWidget *w = findChild(); + QRect contentsRect; + + if (_oldw != width() || _oldh != height()) + { + initStyleOption(&option); + contentsRect = style()->subElementRect(QStyle::SE_TabWidgetTabContents, &option, this); + _oldw = width(); + _oldh = height(); + w->setGeometry(contentsRect); + } + else + contentsRect = w->geometry(); + + if (THIS->container) + THIS->container->setGeometry(0, 0, contentsRect.width(), contentsRect.height()); +} + +void MyTabWidget::updateTextFont() +{ + CWIDGET *_object = CWidget::get(this); + if (THIS->textFont) + tabBar()->setFont(*THIS->textFont->font); + else + tabBar()->setFont(QFont()); +} + +#if 0 +bool MyTabWidget::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Wheel && qobject_cast(o)) + { + QWheelEvent *event = (QWheelEvent *)e; + QTabBar *tabBar = (QTabBar *)o; + int id = tabBar->currentIndex(); + + if (id >= 0) + { + for(;;) + { + if (event->delta() < 0) + id++; + else + id--; + + if (id < 0 || id >= tabBar->count()) + break; + + if (tabBar->isTabEnabled(id)) + { + tabBar->setCurrentIndex(id); + break; + } + } + } + + return true; + } + + return QObject::eventFilter(o, e); +} +#endif + +/*************************************************************************** + + TabStrip + +***************************************************************************/ + +static bool remove_page(void *_object, int i) +{ + CTab *tab = WIDGET->stack.at(i); + + if (!tab->isEmpty()) + { + GB.Error("Tab is not empty"); + return true; + } + + THIS->lock = true; + WIDGET->stack.removeAt(i); + + i = tab->index(); + if (i >= 0) + WIDGET->removeTab(i); + + delete tab->widget; + delete tab; + THIS->lock = false; + + return false; +} + +static void set_current_index(void *_object, int index) +{ + if (index < 0) + return; + + if (index >= (int)WIDGET->stack.count()) + index = WIDGET->stack.count() - 1; + + while (index > 0 && !WIDGET->stack.at(index)->isVisible()) + index--; + + WIDGET->stack.at(index)->ensureVisible(); + THIS->container = WIDGET->stack.at(index)->widget; +} + +static bool set_tab_count(void *_object, int new_count) +{ + int count = WIDGET->stack.count(); + int i; + int index; + QString label; + CTab *tab; + + if (new_count < 1 || new_count > 256) + { + GB.Error(GB_ERR_ARG); + return true; + } + + if (new_count == count) + return false; + + if (new_count > count) + { + for (i = count; i < new_count; i++) + { + tab = new CTab(THIS, new MyContainer(WIDGET)); + + label = QString("Tab %1").arg(i); + WIDGET->addTab(tab->widget, label); + + WIDGET->stack.append(tab); + } + + index = new_count - 1; + + set_current_index(THIS, index); + } + else + { + index = WIDGET->currentIndex(); + //same = (id == PARAM(index)); + + for (i = new_count; i < count; i++) + { + if (!WIDGET->stack.at(i)->isEmpty()) + { + GB.Error("Tab is not empty"); + return true; + } + } + + if (index >= new_count) + index = new_count - 1; + + set_current_index(THIS, index); + + for (i = count - 1; i >= new_count; i--) + { + remove_page(THIS, i); + } + + //WIDGET->stack.resize(new_count); + //THIS->icon->resize(new_count); + } + + return false; +} + +static bool check_index(CTABSTRIP *_object, int index) +{ + if (index < 0 || index >= (int)WIDGET->stack.count()) + { + GB.Error("Bad index"); + return true; + } + else + return false; +} + +static int get_real_index(CTABSTRIP *_object) +{ + int i; + QWidget *current = WIDGET->currentWidget(); + + for (i = 0; i < (int)WIDGET->stack.count(); i++) + { + if (WIDGET->stack.at(i)->widget == current) + return i; + } + + return -1; +} + + +BEGIN_METHOD(TabStrip_new, GB_OBJECT parent) + + MyTabWidget *wid = new MyTabWidget(QCONTAINER(VARG(parent))); //, 0, Qt::WNoMousePropagation); + + QObject::connect(wid, SIGNAL(currentChanged(int)), &CTabStrip::manager, SLOT(currentChanged(int))); + QObject::connect(wid, SIGNAL(tabCloseRequested(int)), &CTabStrip::manager, SLOT(tabCloseRequested(int))); + + THIS->widget.flag.no_design = TRUE; + THIS->container = NULL; + THIS->index = -1; + + CWIDGET_new(wid, (void *)_object); + set_tab_count(THIS, 1); + + //wid->updateLayout(); + +END_METHOD + + +BEGIN_METHOD_VOID(TabStrip_free) + + GB.Unref(POINTER(&THIS->textFont)); + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Count) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->stack.count()); + else + set_tab_count(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Index) + + if (READ_PROPERTY) + { + GB.ReturnInteger(get_real_index(THIS)); + } + else + { + int index = VPROP(GB_INTEGER); + + if (check_index(THIS, index)) + return; + + if (index == get_real_index(THIS)) + return; + + if (!WIDGET->stack.at(index)->isVisible()) + return; + + WIDGET->stack.at(index)->ensureVisible(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Current) + + THIS->index = get_real_index(THIS); + if (THIS->index < 0) + GB.ReturnNull(); + else + RETURN_SELF(); + +END_PROPERTY + + +BEGIN_METHOD(TabStrip_get, GB_INTEGER index) + + int index = VARG(index); + + if (check_index(THIS, index)) + return; + + THIS->index = index; + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Orientation) + + if (READ_PROPERTY) + { + switch(WIDGET->tabPosition()) + { + case QTabWidget::North: GB.ReturnInteger(ALIGN_TOP); break; + case QTabWidget::South: GB.ReturnInteger(ALIGN_BOTTOM); break; + case QTabWidget::West: GB.ReturnInteger(ALIGN_LEFT); break; + case QTabWidget::East: GB.ReturnInteger(ALIGN_RIGHT); break; + default: GB.ReturnInteger(ALIGN_NORMAL); break; + } + } + else + { + switch(VPROP(GB_INTEGER)) + { + case ALIGN_TOP: WIDGET->setTabPosition(QTabWidget::North); break; + case ALIGN_BOTTOM: WIDGET->setTabPosition(QTabWidget::South); break; + case ALIGN_LEFT: WIDGET->setTabPosition(QTabWidget::West); break; + case ALIGN_RIGHT: WIDGET->setTabPosition(QTabWidget::East); break; + } + } + +END_PROPERTY + + + +/*************************************************************************** + + .Tab + +***************************************************************************/ + +BEGIN_PROPERTY(CTAB_text) + + int index = THIS->index; + if (index < 0) + index = get_real_index(THIS); + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->stack.at(index)->text); + else + { + WIDGET->stack.at(index)->text = QSTRING_PROP(); + WIDGET->stack.at(index)->updateText(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_TextFont) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->textFont); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->textFont)); + WIDGET->updateTextFont(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_picture) + + int index; + + index = THIS->index; + if (index < 0) + index = get_real_index(THIS); + + if (READ_PROPERTY) + { + if (index < 0) + GB.ReturnNull(); + else + GB.ReturnObject(WIDGET->stack.at(index)->icon); + } + else if (index >= 0) + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(WIDGET->stack.at(index))->icon)); + WIDGET->stack.at(index)->updateIcon(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_enabled) + + CTab *tab = WIDGET->stack.at(THIS->index); + + if (READ_PROPERTY) + GB.ReturnBoolean(tab->isEnabled()); + else + tab->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_visible) + + CTab *tab = WIDGET->stack.at(THIS->index); + + if (READ_PROPERTY) + GB.ReturnBoolean(tab->isVisible()); + else + tab->setVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTAB_next) + + CTABSTRIP_ENUM *iter; + QWidget *page; + QObjectList list; + int child; + CWIDGET *widget; + + iter = (CTABSTRIP_ENUM *)GB.GetEnum(); + if (!iter->init) + { + iter->child = 0; + iter->index = THIS->index; + iter->init = true; + } + + //qDebug("CTAB_next: iter = (%d, %d, %d)", iter->init, iter->index, iter->child); + + page = WIDGET->stack.at(iter->index)->widget; + list = page->children(); + + for(;;) + { + child = iter->child; + + if (child >= list.count()) + { + GB.StopEnum(); + return; + } + + iter->child = child + 1; + + widget = CWidget::getRealExisting(list.at(child)); + if (widget) + { + GB.ReturnObject(widget); + return; + } + } + +END_METHOD + +BEGIN_METHOD(CTAB_get, GB_INTEGER index) + + QObjectList list = WIDGET->stack.at(THIS->index)->widget->children(); + int index = VARG(index); + int i; + CWIDGET *widget; + + if (index >= 0) + { + i = 0; + for(i = 0; i < list.count(); i++) + { + widget = CWidget::getRealExisting(list.at(i)); + if (!widget) + continue; + if (index == 0) + { + GB.ReturnObject(widget); + return; + } + index--; + } + } + + GB.Error(GB_ERR_BOUND); + +END_METHOD + + +BEGIN_PROPERTY(CTAB_count) + + GB.ReturnInteger(WIDGET->stack.at(THIS->index)->count()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTAB_delete) + + int index = get_real_index(THIS); + + if (WIDGET->stack.count() == 1) + { + GB.Error("TabStrip cannot be empty"); + return; + } + + if (remove_page(THIS, THIS->index)) + return; + + set_current_index(THIS, index); + + THIS->index = -1; + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Enabled) + + int i; + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isEnabled()); + else + { + WIDGET->setEnabled(VPROP(GB_BOOLEAN)); + for (i = 0; i < WIDGET->stack.count(); i++) + WIDGET->stack.at(i)->setEnabled(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Text) + + THIS->index = -1; + CTAB_text(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Picture) + + THIS->index = -1; + CTAB_picture(_object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(TabStrip_Closable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->tabsClosable()); + else + WIDGET->setTabsClosable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_ClientWidth) + + GB.ReturnInteger(THIS->container->width()); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_ClientHeight) + + GB.ReturnInteger(THIS->container->height()); + +END_PROPERTY + + +BEGIN_METHOD(TabStrip_FindIndex, GB_OBJECT child) + + void *child = VARG(child); + int i; + QWidget *parent; + + if (GB.CheckObject(child)) + return; + + parent = QWIDGET(child)->parentWidget(); + + for (i = 0; i < WIDGET->stack.count(); i++) + { + if (parent == WIDGET->stack.at(i)->widget) + { + GB.ReturnInteger(i); + return; + } + } + + GB.ReturnInteger(-1); + +END_METHOD + + +/** Class CTabStrip ******************************************************/ + + +CTabStrip CTabStrip::manager; + +void CTabStrip::currentChanged(int index) +{ + QWidget *wid; + GET_SENDER(); + + wid = WIDGET->currentWidget(); // wid can be null! + + if (wid != THIS->container) + { + //qDebug("CTabStrip::currentChanged: %d %p -> %p", index, THIS->container, wid); + if (THIS->container) + THIS->container->hide(); + THIS->container = wid; + + if (wid) + wid->show(); + + CCONTAINER_arrange(THIS); + + //if (wid->isVisible() && !THIS->lock) + if (!THIS->lock) + RAISE_EVENT(EVENT_Click); + } +} + +void CTabStrip::tabCloseRequested(int index) +{ + GET_SENDER(); + + GB.Raise(THIS, EVENT_Close, 1, GB_T_INTEGER, index); +} + + +/** Descriptions *********************************************************/ + +GB_DESC CTabStripContainerChildrenDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer.Children"), + + GB_METHOD("_next", "Control", CTAB_next, NULL), + GB_PROPERTY_READ("Count", "i", CTAB_count), + GB_METHOD("_get", "Control", CTAB_get, "(Index)i"), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripContainerDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer"), + + GB_PROPERTY("Text", "s", CTAB_text), + GB_PROPERTY("Picture", "Picture", CTAB_picture), + GB_PROPERTY("Caption", "s", CTAB_text), + GB_PROPERTY("Enabled", "b", CTAB_enabled), + GB_PROPERTY("Visible", "b", CTAB_visible), + GB_PROPERTY_SELF("Children", ".TabStripContainer.Children"), + GB_METHOD("Delete", NULL, CTAB_delete, NULL), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripDesc[] = +{ + GB_DECLARE("TabStrip", sizeof(CTABSTRIP)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, TabStrip_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, TabStrip_free, NULL), + + GB_PROPERTY("Count", "i", TabStrip_Count), + GB_PROPERTY("Text", "s", TabStrip_Text), + GB_PROPERTY("TextFont", "Font", TabStrip_TextFont), + GB_PROPERTY("Picture", "Picture", TabStrip_Picture), + GB_PROPERTY("Closable", "b", TabStrip_Closable), + GB_PROPERTY("Caption", "s", TabStrip_Text), + GB_PROPERTY_READ("Current", ".TabStripContainer", TabStrip_Current), + GB_PROPERTY("Index", "i", TabStrip_Index), + GB_PROPERTY("Orientation", "i", TabStrip_Orientation), + GB_PROPERTY("Enabled", "b", TabStrip_Enabled), + + GB_PROPERTY_READ("ClientX", "i", Container_ClientX), + GB_PROPERTY_READ("ClientY", "i", Container_ClientY), + GB_PROPERTY_READ("ClientW", "i", TabStrip_ClientWidth), + GB_PROPERTY_READ("ClientWidth", "i", TabStrip_ClientWidth), + GB_PROPERTY_READ("ClientH", "i", TabStrip_ClientHeight), + GB_PROPERTY_READ("ClientHeight", "i", TabStrip_ClientHeight), + + ARRANGEMENT_PROPERTIES, + + GB_METHOD("_get", ".TabStripContainer", TabStrip_get, "(Index)i"), + GB_METHOD("FindIndex", "i", TabStrip_FindIndex, "(Child)Control;"), + + TABSTRIP_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("Close", NULL, "(Index)i", &EVENT_Close), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CTabStrip.h b/gb.qt4/src/CTabStrip.h new file mode 100644 index 00000000..0c2b9bfe --- /dev/null +++ b/gb.qt4/src/CTabStrip.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + CTabStrip.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTABSTRIP_H +#define __CTABSTRIP_H + +#include +#include +#include + +#include "gambas.h" + +#include "CWidget.h" +#include "CContainer.h" +#include "CPicture.h" +#include "CFont.h" + +#ifndef __CTABSTRIP_CPP +extern GB_DESC CTabStripDesc[]; +extern GB_DESC CTabStripContainerDesc[]; +extern GB_DESC CTabStripContainerChildrenDesc[]; +#else + +#define QTABWIDGET(object) ((MyTabWidget *)((CWIDGET *)object)->widget) + +#define WIDGET QTABWIDGET(_object) +#define THIS OBJECT(CTABSTRIP) + +typedef + struct { + int index; + int child; + bool init; + } + CTABSTRIP_ENUM; + +typedef + struct { + CWIDGET widget; + QWidget *container; + CARRANGEMENT arrangement; + CFONT *textFont; + int index; + unsigned lock : 1; + } + CTABSTRIP; + +#endif + +class CTab; + +class MyTabWidget : public QTabWidget +{ +Q_OBJECT + +public: + + QList stack; + + explicit MyTabWidget(QWidget *parent); + virtual ~MyTabWidget(); + virtual void setEnabled(bool e); + void layoutContainer(); + void updateTextFont(); + +private: + int _oldw, _oldh; +}; + +class CTabStrip : public QObject +{ +Q_OBJECT + +public: + + static CTabStrip manager; + +public slots: + + void currentChanged(int); + void tabCloseRequested(int); +}; + +void CTABSTRIP_arrange(void *_object); + +#endif diff --git a/gb.qt4/src/CTextArea.cpp b/gb.qt4/src/CTextArea.cpp new file mode 100644 index 00000000..c5511764 --- /dev/null +++ b/gb.qt4/src/CTextArea.cpp @@ -0,0 +1,692 @@ +/*************************************************************************** + + CTextArea.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTAREA_CPP + +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" +#include "CConst.h" +#include "CFont.h" +#include "CTextArea.h" + + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); +DECLARE_EVENT(EVENT_Link); + +//------------------------------------------------------------------------- + +static int get_length(void *_object) +{ + if (THIS->length < 0) + { + QTextBlock block = WIDGET->document()->begin(); + int len = 0; + + while (block.isValid()) + { + len += block.length(); + block = block.next(); + } + + THIS->length = len - 1; + } + + return THIS->length; +} + +static bool is_empty(void *_object) +{ + QTextBlock block = WIDGET->document()->begin(); + return !block.isValid(); +} + +static void to_pos(QTextEdit *wid, int par, int car, int *pos) +{ + QTextCursor cursor; + QTextBlock block; + int p = 0; + + cursor = wid->textCursor(); + cursor.movePosition(QTextCursor::Start); + + block = cursor.block(); + + while (par) + { + if (!block.isValid()) + break; + p += block.length(); + block = block.next(); + par--; + } + + if (block.isValid()) + car = qMin(block.length() - 1, car); + + *pos = p + car; +} + + +static void from_pos(CTEXTAREA *_object, int pos, int *par, int *car) +{ + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + *par = cursor.blockNumber(); + *car = cursor.position() - cursor.block().position(); +} + + +static void get_selection(QTextEdit *wid, int *start, int *length) +{ + QTextCursor cursor = wid->textCursor(); + + *start = cursor.selectionStart(); + *length = cursor.selectionEnd() - *start; +} + +static void update_alignment(void *_object) +{ + THIS->no_change = TRUE; + + QTextOption opt = WIDGET->document()->defaultTextOption(); + opt.setAlignment((Qt::Alignment)CCONST_horizontal_alignment(THIS->align, ALIGN_NORMAL, true)); + WIDGET->document()->setDefaultTextOption(opt); + + THIS->no_change = FALSE; +} + +static void set_text_color(void *_object) +{ + QTextCharFormat fmt; + QBrush col; + GB_COLOR fg = CWIDGET_get_foreground((CWIDGET *)THIS); + + fmt = WIDGET->currentCharFormat(); + + if (fg == COLOR_DEFAULT) + { + fmt.clearForeground(); + //col = WIDGET->palette().text(); + } + else + { + fmt.setForeground(TO_QCOLOR(fg)); + } + + //WIDGET->setTextColor(col); + THIS->no_change = TRUE; + WIDGET->setCurrentCharFormat(fmt); + THIS->no_change = FALSE; +} + +void CTEXTAREA_set_foreground(void *_object) +{ + THIS->no_change = TRUE; + + if (is_empty(THIS)) + { + WIDGET->setPlainText(" "); + WIDGET->selectAll(); + WIDGET->setTextColor(Qt::black); + set_text_color(THIS); + WIDGET->textCursor().insertText(""); + } + else + { + QTextCursor oldCursor = WIDGET->textCursor(); + + WIDGET->selectAll(); + + WIDGET->setTextColor(Qt::black); + set_text_color(THIS); + + WIDGET->setTextCursor(oldCursor); + + set_text_color(THIS); + } + + THIS->no_change = FALSE; +} + + +//------------------------------------------------------------------------- + +BEGIN_METHOD(TextArea_new, GB_OBJECT parent) + + QTextEdit *wid = new QTextEdit(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(textChanged()), &CTextArea::manager, SLOT(changed())); + QObject::connect(wid, SIGNAL(cursorPositionChanged()), &CTextArea::manager, SLOT(cursor())); + + wid->setLineWrapMode(QTextEdit::NoWrap); + wid->setAcceptRichText(false); + + THIS->widget.flag.wheel = true; + THIS->widget.flag.autoFillBackground = true; + CWIDGET_new(wid, (void *)_object); + + THIS->length = -1; + THIS->align = ALIGN_NORMAL; + + wid->document()->setDocumentMargin(MAIN_scale * 3 / 4); + +END_METHOD + + +BEGIN_PROPERTY(TextArea_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->toPlainText()); + else + { + WIDGET->document()->setPlainText(QSTRING_PROP()); + update_alignment(THIS); + CTEXTAREA_set_foreground(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TextArea_Length) + + GB.ReturnInteger(get_length(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextArea_ReadOnly) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isReadOnly()); + else + WIDGET->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextArea_Wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->lineWrapMode() != QTextEdit::NoWrap); + else + WIDGET->setLineWrapMode(VPROP(GB_BOOLEAN) ? QTextEdit::WidgetWidth : QTextEdit::NoWrap); + +END_PROPERTY + + +/* +BEGIN_PROPERTY(CTEXTAREA_max_length) + + int max; + + if (READ_PROPERTY) + { + max = WIDGET->maxLength(); + GB.ReturnInteger((max < 0) ? 0 : max); + } + else + { + max = PROPERTY(int); + if (max <= 0) + max = -1; + WIDGET->setMaxLength(max); + } + +END_PROPERTY +*/ + +static int get_column(CTEXTAREA *_object) +{ + QTextCursor cursor = WIDGET->textCursor(); + return cursor.position() - cursor.block().position(); +} + +BEGIN_PROPERTY(TextArea_Column) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + //GB.ReturnInteger(WIDGET->textCursor().columnNumber()); + GB.ReturnInteger(get_column(THIS)); + else + { + int col = VPROP(GB_INTEGER); + + if (col <= 0) + cursor.movePosition(QTextCursor::QTextCursor::StartOfBlock); + else if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(TextArea_Line) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + GB.ReturnInteger(cursor.blockNumber()); + else + { + int col = get_column(THIS); + int line = VPROP(GB_INTEGER); + + if (line < 0) + cursor.movePosition(QTextCursor::Start); + else if (line >= WIDGET->document()->blockCount()) + cursor.movePosition(QTextCursor::End); + else + { + cursor.setPosition(WIDGET->document()->findBlockByNumber(line).position()); + if (col > 0) + { + if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + } + } + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(TextArea_Pos) + + if (READ_PROPERTY) + { + GB.ReturnInteger(WIDGET->textCursor().position()); + } + else + { + int pos = VPROP(GB_INTEGER); + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + + +/* +BEGIN_METHOD(CTEXTAREA_select, int line; int col; int selline; int selcol) + + MyMultiLineEdit *wid = QMULTILINEEDIT(_object); + + int line = PARAM(line); + int col = PARAM(col); + + look_pos(wid, &line, &col); + wid->setCursorPosition(line, col); + + line = PARAM(selline); + col = PARAM(selcol); + + look_pos(wid, &line, &col); + wid->setCursorPosition(line, col, TRUE); + +END_METHOD +*/ + +BEGIN_METHOD_VOID(TextArea_Clear) + + WIDGET->clear(); + +END_METHOD + + +BEGIN_METHOD(TextArea_Insert, GB_STRING text) + + WIDGET->textCursor().insertText(QSTRING_ARG(text)); + +END_METHOD + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(TextArea_Selection_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->textCursor().selection().toPlainText()); + else + WIDGET->textCursor().insertText(QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(TextArea_Selection_Length) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(length); + +END_PROPERTY + + +BEGIN_PROPERTY(TextArea_Selection_Start) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(start); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TextArea_Selection_Clear) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.clearSelection(); + WIDGET->setTextCursor(cursor); + +END_METHOD + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(TextArea_Selected) + + GB.ReturnBoolean(WIDGET->textCursor().hasSelection()); + +END_PROPERTY + + +BEGIN_METHOD(TextArea_Select, GB_INTEGER start; GB_INTEGER length) + + if (MISSING(start) && MISSING(length)) + WIDGET->textCursor().select(QTextCursor::Document); + else if (!MISSING(start) && !MISSING(length)) + { + QTextCursor cursor = WIDGET->textCursor(); + + cursor.setPosition(VARG(start)); + cursor.setPosition(VARG(start) + VARG(length), QTextCursor::KeepAnchor); + + WIDGET->setTextCursor(cursor); + } + +END_METHOD + + +BEGIN_METHOD_VOID(TextArea_SelectAll) //, GB_BOOLEAN sel) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.select(QTextCursor::Document); + WIDGET->setTextCursor(cursor); + +END_METHOD + + +BEGIN_METHOD(TextArea_ToPos, GB_INTEGER line; GB_INTEGER col) + + int pos; + + to_pos(WIDGET, VARG(line), VARG(col), &pos); + + GB.ReturnInteger(pos); + +END_METHOD + + +BEGIN_METHOD(TextArea_ToLine, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(line); + +END_METHOD + + +BEGIN_METHOD(TextArea_ToColumn, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(col); + +END_METHOD + + +BEGIN_METHOD_VOID(TextArea_Copy) + + WIDGET->copy(); + +END_METHOD + + +BEGIN_METHOD_VOID(TextArea_Cut) + + WIDGET->cut(); + +END_METHOD + + +BEGIN_METHOD_VOID(TextArea_Paste) + + WIDGET->paste(); + +END_METHOD + + +BEGIN_METHOD_VOID(TextArea_Undo) + + WIDGET->undo(); + +END_METHOD + + +BEGIN_METHOD_VOID(TextArea_Redo) + + WIDGET->redo(); + +END_METHOD + + +/*BEGIN_PROPERTY(CTEXTAREA_scrollbar) + + int scroll; + + if (READ_PROPERTY) + { + scroll = 0; + if (WIDGET->hScrollBarMode() == QScrollView::Auto) + scroll += 1; + if (WIDGET->vScrollBarMode() == QScrollView::Auto) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + WIDGET->setHScrollBarMode( (scroll & 1) ? QScrollView::Auto : QScrollView::AlwaysOff); + WIDGET->setVScrollBarMode( (scroll & 2) ? QScrollView::Auto : QScrollView::AlwaysOff); + } + +END_PROPERTY*/ + + +BEGIN_METHOD_VOID(TextArea_EnsureVisible) + + WIDGET->ensureCursorVisible(); + +END_METHOD + + +BEGIN_PROPERTY(TextArea_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->align); + else + { + THIS->align = VPROP(GB_INTEGER); + update_alignment(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(TextArea_Border) + + CWIDGET_border_simple(_object, _param); + + if (!READ_PROPERTY) + WIDGET->document()->setDocumentMargin(VPROP(GB_BOOLEAN) ? (MAIN_scale * 3 / 4) : 0); + +END_PROPERTY + +BEGIN_METHOD(TextArea_CursorAt, GB_INTEGER pos) + + QRect rect; + QTextCursor cursor = WIDGET->textCursor(); + + if (!MISSING(pos)) + cursor.setPosition(VARG(pos)); + + rect = WIDGET->cursorRect(cursor); + + GB.ReturnObject(GEOM.CreatePoint(rect.x() + WIDGET->viewport()->x(), rect.bottom() + WIDGET->viewport()->y())); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CTextAreaSelectionDesc[] = +{ + GB_DECLARE(".TextArea.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", TextArea_Selection_Text), + GB_PROPERTY_READ("Length", "i", TextArea_Selection_Length), + GB_PROPERTY_READ("Start", "i", TextArea_Selection_Start), + GB_PROPERTY_READ("Pos", "i", TextArea_Selection_Start), + + //GB_METHOD("Clear", NULL, TextArea_Selection_Clear, NULL), + GB_METHOD("Hide", NULL, TextArea_Selection_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC CTextAreaDesc[] = +{ + GB_DECLARE("TextArea", sizeof(CTEXTAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, TextArea_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", TextArea_Text), + GB_PROPERTY_READ("Length", "i", TextArea_Length), + GB_PROPERTY("ReadOnly", "b", TextArea_ReadOnly), + + //GB_PROPERTY_READ("Lines", ".TextArea.Line", CTEXTAREA_line_or_selection), + + GB_PROPERTY("ScrollBar", "i", CWIDGET_scrollbar), + GB_PROPERTY("Wrap", "b", TextArea_Wrap), + GB_PROPERTY("Border", "b", TextArea_Border), + GB_PROPERTY("Alignment", "i", TextArea_Alignment), + + GB_PROPERTY("Line", "i", TextArea_Line), + GB_PROPERTY("Column", "i", TextArea_Column), + GB_PROPERTY("Pos", "i", TextArea_Pos), + + GB_PROPERTY_SELF("Selection", ".TextArea.Selection"), + GB_METHOD("Select", NULL, TextArea_Select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, TextArea_SelectAll, NULL), + GB_METHOD("Unselect", NULL, TextArea_Selection_Clear, NULL), + GB_PROPERTY_READ("Selected", "b", TextArea_Selected), + + GB_METHOD("Clear", NULL, TextArea_Clear, NULL), + GB_METHOD("Insert", NULL, TextArea_Insert, "(Text)s"), + + GB_METHOD("Copy", NULL, TextArea_Copy, NULL), + GB_METHOD("Cut", NULL, TextArea_Cut, NULL), + GB_METHOD("Paste", NULL, TextArea_Paste, NULL), + GB_METHOD("Undo", NULL, TextArea_Undo, NULL), + GB_METHOD("Redo", NULL, TextArea_Redo, NULL), + + GB_METHOD("ToPos", "i", TextArea_ToPos, "(Line)i(Column)i"), + GB_METHOD("ToLine", "i", TextArea_ToLine, "(Pos)i"), + GB_METHOD("ToColumn", "i", TextArea_ToColumn, "(Pos)i"), + + GB_METHOD("CursorAt", "Point", TextArea_CursorAt, "[(Pos)i]"), + + GB_METHOD("EnsureVisible", NULL, TextArea_EnsureVisible, NULL), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + + TEXTAREA_DESCRIPTION, + + GB_END_DECLARE +}; + + +//------------------------------------------------------------------------- + +CTextArea CTextArea::manager; + +void CTextArea::changed(void) +{ + GET_SENDER(); + + if (THIS->no_change) + return; + + set_text_color(THIS); + THIS->length = -1; + GB.Raise(THIS, EVENT_Change, 0); +} + +void CTextArea::cursor(void) +{ + GET_SENDER(); + //set_text_color(THIS); + GB.Raise(THIS, EVENT_Cursor, 0); +} + +void CTextArea::link(const QString &path) +{ + GET_SENDER(); + const char *str = TO_UTF8(path); + GB.Raise(THIS, EVENT_Link, 1, GB_T_STRING, str, LAST_UTF8_LENGTH()); +} diff --git a/gb.qt4/src/CTextArea.h b/gb.qt4/src/CTextArea.h new file mode 100644 index 00000000..771ea3df --- /dev/null +++ b/gb.qt4/src/CTextArea.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + CTextArea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTAREA_H +#define __CTEXTAREA_H + +#include + +#include "gambas.h" + +#include "CWidget.h" + +#ifndef __CTEXTAREA_CPP +extern GB_DESC CTextAreaDesc[]; +extern GB_DESC CTextAreaSelectionDesc[]; +#else + +#define WIDGET ((QTextEdit *)((CWIDGET *)_object)->widget) +#define MYTEXTEDIT ((MyTextEdit *)((CWIDGET *)_object)->widget) +#define THIS ((CTEXTAREA *)_object) + +#endif + +typedef + struct { + CWIDGET widget; + int length; + int align; + unsigned no_change : 1; + } + CTEXTAREA; + +class CTextArea : public QObject +{ + Q_OBJECT + +public: + + static CTextArea manager; + +public slots: + + void changed(void); + void cursor(void); + void link(const QString &); +}; + +void CTEXTAREA_set_foreground(void *_object); + +#endif diff --git a/gb.qt4/src/CTextBox.cpp b/gb.qt4/src/CTextBox.cpp new file mode 100644 index 00000000..fd8764a8 --- /dev/null +++ b/gb.qt4/src/CTextBox.cpp @@ -0,0 +1,405 @@ +/*************************************************************************** + + CTextBox.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTBOX_CPP + +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CTextBox.h" + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Cursor); + +#define MAX_LEN 32767 + + +//------------------------------------------------------------------------- + + +#define GET_TEXT_BOX() QLineEdit *textbox = TEXTBOX; + + +BEGIN_METHOD(TextBox_new, GB_OBJECT parent) + + QLineEdit *wid = new QLineEdit(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(textChanged(const QString &)), &CTextBox::manager, SLOT(onChange())); + QObject::connect(wid, SIGNAL(returnPressed()), &CTextBox::manager, SLOT(onActivate())); + QObject::connect(wid, SIGNAL(cursorPositionChanged(int, int)), &CTextBox::manager, SLOT(onCursor())); + //QObject::connect(wid, SIGNAL(selectionChanged()), &CTextBox::manager, SLOT(onSelectionChanged())); + + wid->setAlignment(Qt::AlignLeft); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD_VOID(TextBox_Clear) + + TEXTBOX->clear(); + +END_METHOD + +BEGIN_METHOD(TextBox_Insert, GB_STRING text) + + GET_TEXT_BOX(); + + //textbox->insert(QString(GB.ToZeroString(ARG(text)))); + textbox->insert(QSTRING_ARG(text)); + +END_METHOD + + +BEGIN_PROPERTY(TextBox_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(TEXTBOX->text()); + else + { + TEXTBOX->deselect(); + TEXTBOX->setText(QSTRING_PROP()); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Placeholder) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(textbox->placeholderText()); + else + textbox->setPlaceholderText(QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Length) + + GB.ReturnInteger(TEXTBOX->text().length()); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(CCONST_alignment(TEXTBOX->alignment() + Qt::AlignVCenter, ALIGN_NORMAL, false)); + else + TEXTBOX->setAlignment((Qt::Alignment)CCONST_alignment(VPROP(GB_INTEGER), ALIGN_NORMAL, true) & Qt::AlignHorizontal_Mask); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Pos) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + GB.ReturnInteger(textbox->cursorPosition()); + else + textbox->setCursorPosition(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_ReadOnly) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTBOX->isReadOnly()); + else + TEXTBOX->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Border) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + GB.ReturnBoolean(textbox->hasFrame()); + else + textbox->setFrame(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Password) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + GB.ReturnBoolean(textbox->echoMode() != QLineEdit::Normal); + else + textbox->setEchoMode(VPROP(GB_BOOLEAN) ? QLineEdit::Password : QLineEdit::Normal); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_MaxLength) + + int max; + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + { + max = textbox->maxLength(); + GB.ReturnInteger(max >= MAX_LEN ? 0 : max); + } + else + { + max = VPROP(GB_INTEGER); + if (max < 1 || max > MAX_LEN) + max = MAX_LEN; + + textbox->setMaxLength(max); + } + +END_PROPERTY + + +BEGIN_METHOD(TextBox_CursorAt, GB_INTEGER pos) + + QRect rect; + int save = -1; + + GET_TEXT_BOX(); + + if (!MISSING(pos)) + { + save = textbox->cursorPosition(); + textbox->setCursorPosition(VARG(pos)); + } + + // Hack to call cursorRect() + rect = textbox->inputMethodQuery(Qt::ImMicroFocus).toRect(); + + if (save >= 0) + textbox->setCursorPosition(save); + + GB.ReturnObject(GEOM.CreatePoint((rect.left() + rect.right()) / 2 + 1, rect.bottom())); + +END_PROPERTY + +/*************************************************************************** + + .TextBox.Selection + +***************************************************************************/ + +BEGIN_PROPERTY(TextBox_Selection_Text) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(textbox->selectedText()); + else + textbox->insert(QSTRING_PROP()); + +END_PROPERTY + + +static void set_selection(QLineEdit *textbox, int start, int length) +{ + int len = (int)textbox->text().length(); + + if (start < 0 || start >= len) + { + start = textbox->cursorPosition(); + length = 0; + } + + textbox->setCursorPosition(start); + + if (length <= 0) + textbox->deselect(); + else + { + if ((start + length) >= len) + length = len - start; + textbox->setSelection(start, length); + } +} + +static void get_selection(QLineEdit *textbox, int *start, int *length) +{ + *start = textbox->selectionStart(); + if (*start < 0) + *start = textbox->cursorPosition(); + if (!textbox->hasSelectedText()) + *length = 0; + else + *length = textbox->selectedText().length(); +} + + +BEGIN_PROPERTY(TextBox_Selection_Length) + + int start, length; + + GET_TEXT_BOX(); + + get_selection(textbox, &start, &length); + + GB.ReturnInteger(length); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Selection_Start) + + int start, length; + + GET_TEXT_BOX(); + + get_selection(textbox, &start, &length); + GB.ReturnInteger(start); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TextBox_Unselect) + + GET_TEXT_BOX(); + + textbox->deselect(); + +END_METHOD + +BEGIN_METHOD_VOID(TextBox_Selected) + + GET_TEXT_BOX(); + + GB.ReturnBoolean(textbox->hasSelectedText()); + +END_METHOD + + +BEGIN_METHOD(TextBox_Select, GB_INTEGER start; GB_INTEGER length) + + GET_TEXT_BOX(); + + if (MISSING(start) && MISSING(length)) + textbox->selectAll(); + else if (!MISSING(start) && !MISSING(length)) + set_selection(textbox, VARG(start), VARG(length)); + +END_METHOD + +BEGIN_METHOD_VOID(TextBox_SelectAll) + + GET_TEXT_BOX(); + + textbox->selectAll(); + +END_METHOD + + + +//------------------------------------------------------------------------- + +CTextBox CTextBox::manager; + +void CTextBox::onChange(void) +{ + RAISE_EVENT(EVENT_Change); +} + + +void CTextBox::onActivate(void) +{ + RAISE_EVENT(EVENT_Activate); +} + + +void CTextBox::onCursor() +{ + RAISE_EVENT(EVENT_Cursor); +} + + +//------------------------------------------------------------------------- + +GB_DESC CTextBoxSelectionDesc[] = +{ + GB_DECLARE(".TextBox.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", TextBox_Selection_Text), + GB_PROPERTY_READ("Length", "i", TextBox_Selection_Length), + GB_PROPERTY_READ("Start", "i", TextBox_Selection_Start), + GB_PROPERTY_READ("Pos", "i", TextBox_Selection_Start), + + GB_METHOD("Hide", NULL, TextBox_Unselect, NULL), + + GB_END_DECLARE +}; + + +GB_DESC CTextBoxDesc[] = +{ + GB_DECLARE("TextBox", sizeof(CTEXTBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, TextBox_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", TextBox_Text), + GB_PROPERTY("Alignment", "i", TextBox_Alignment), + GB_PROPERTY_READ("Length", "i", TextBox_Length), + GB_PROPERTY("Pos", "i", TextBox_Pos), + GB_PROPERTY("ReadOnly", "b", TextBox_ReadOnly), + GB_PROPERTY("Border", "b", TextBox_Border), + GB_PROPERTY("Password", "b", TextBox_Password), + GB_PROPERTY("MaxLength", "i", TextBox_MaxLength), + GB_PROPERTY("Placeholder", "s", TextBox_Placeholder), + + GB_PROPERTY_SELF("Selection", ".TextBox.Selection"), + GB_METHOD("Select", NULL, TextBox_Select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, TextBox_SelectAll, NULL), + GB_METHOD("Unselect", NULL, TextBox_Unselect, NULL), + GB_PROPERTY_READ("Selected", "b", TextBox_Selected), + + GB_METHOD("Clear", NULL, TextBox_Clear, NULL), + GB_METHOD("Insert", NULL, TextBox_Insert, "(Text)s"), + + GB_METHOD("CursorAt", "Point", TextBox_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + + TEXTBOX_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CTextBox.h b/gb.qt4/src/CTextBox.h new file mode 100644 index 00000000..447449f8 --- /dev/null +++ b/gb.qt4/src/CTextBox.h @@ -0,0 +1,76 @@ +/*************************************************************************** + + CTextBox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTBOX_H +#define __CTEXTBOX_H + +#include +#include + +#include "gambas.h" + +#include "CWidget.h" + +#ifndef __CTEXTBOX_CPP + +extern GB_DESC CTextBoxSelectionDesc[]; +extern GB_DESC CTextBoxDesc[]; + +#else + +#define QLINEEDIT(object) ((QLineEdit *)((CWIDGET *)object)->widget) + +#define TEXTBOX ((QLineEdit *)((CWIDGET *)_object)->widget) +#define THIS ((CTEXTBOX *)_object) + +#endif + +typedef + struct { + CWIDGET widget; + int start; + int length; + int locked; + } + CTEXTBOX; + + +class CTextBox : public QObject +{ + Q_OBJECT + +public: + + static CTextBox manager; + +public slots: + + void onChange(void); + void onActivate(void); + void onCursor(void); + //void onSelectionChanged(void); + +}; + + +#endif diff --git a/gb.qt4/src/CWatch.cpp b/gb.qt4/src/CWatch.cpp new file mode 100644 index 00000000..827e0793 --- /dev/null +++ b/gb.qt4/src/CWatch.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + + CWatch.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWATCH_CPP + +//#include "gb_common.h" +#include "main.h" +#include "CWatch.h" + +QHash CWatch::readDict; +QHash CWatch::writeDict; +int CWatch::count = 0; + +void CWatch::watch(int fd, int type, GB_WATCH_CALLBACK callback, intptr_t param) +{ + CWatch *watch; + + //qDebug("CWatch::watch: %d %d %p", fd, type, callback); + + switch (type) + { + case GB_WATCH_NONE: + + watch = readDict[fd]; + if (watch) delete watch; + + watch = writeDict[fd]; + if (watch) delete watch; + + break; + + case GB_WATCH_READ: + + if (callback) + new CWatch(fd, QSocketNotifier::Read, callback, param); + else + { + watch = readDict[fd]; + if (watch) delete watch; + } + break; + + case GB_WATCH_WRITE: + + if (callback) + new CWatch(fd, QSocketNotifier::Write, callback, param); + else + { + watch = writeDict[fd]; + if (watch) delete watch; + } + break; + } +} + +void CWatch::stop() +{ + int fd; + + for (fd = 0; count > 0; fd++) + watch(fd, GB_WATCH_NONE, 0, 0); +} + +CWatch::CWatch(int fd, QSocketNotifier::Type type, GB_WATCH_CALLBACK callback, intptr_t param) +{ + count++; + + if (type == QSocketNotifier::Read) + { + if (readDict[fd]) + delete readDict[fd]; + } + else if (type == QSocketNotifier::Write) + { + if (writeDict[fd]) + delete writeDict[fd]; + } + + notifier = new QSocketNotifier(fd, type); + this->callback = callback; + this->param = param; + + if (type == QSocketNotifier::Read) + { + //qDebug("CWatch: %d (read)", fd); + + readDict.insert(fd, this); + QObject::connect(notifier, SIGNAL(activated(int)), this, SLOT(read(int))); + } + else if (type == QSocketNotifier::Write) + { + //qDebug("CWatch: %d (write)", fd); + + writeDict.insert(fd, this); + QObject::connect(notifier, SIGNAL(activated(int)), this, SLOT(write(int))); + } +} + + +CWatch::~CWatch() +{ + if (notifier->type() == QSocketNotifier::Read) + { + //qDebug("~CWatch: %d (read)", notifier->socket()); + readDict.remove(notifier->socket()); + } + else if (notifier->type() == QSocketNotifier::Write) + { + //qDebug("~CWatch: %d (write)", notifier->socket()); + writeDict.remove(notifier->socket()); + } + + //BREAKPOINT(); + + delete notifier; + + count--; + MAIN_check_quit(); +} + +void CWatch::read(int fd) +{ + //qDebug("CWatch::read: fd = %d readDict[fd] = %p", fd, readDict[fd]); + if (readDict[fd]) + (*callback)(fd, GB_WATCH_READ, param); +} + +void CWatch::write(int fd) +{ + //qDebug("CWatch::write: fd = %d writeDict[fd] = %p", fd, writeDict[fd]); + if (writeDict[fd]) + (*callback)(fd, GB_WATCH_WRITE, param); +} diff --git a/gb.qt4/src/CWatch.h b/gb.qt4/src/CWatch.h new file mode 100644 index 00000000..91d0be6c --- /dev/null +++ b/gb.qt4/src/CWatch.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + CWatch.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWATCH_H +#define __CWATCH_H + +#include +#include +#include + +#include "gambas.h" + +class CWatch: public QObject +{ + Q_OBJECT + +public: + + static void watch(int fd, int type, GB_WATCH_CALLBACK callback, intptr_t param); + static void stop(); + static int count; + + explicit CWatch(int fd, QSocketNotifier::Type type, GB_WATCH_CALLBACK callback, intptr_t param); + ~CWatch(); + +private: + + static QHash readDict; + static QHash writeDict; + + QSocketNotifier *notifier; + GB_WATCH_CALLBACK callback; + intptr_t param; + +public slots: + + void read(int); + void write(int); +}; + + +#endif diff --git a/gb.qt4/src/CWatcher.cpp b/gb.qt4/src/CWatcher.cpp new file mode 100644 index 00000000..b224dd79 --- /dev/null +++ b/gb.qt4/src/CWatcher.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + + CWatcher.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWATCHER_CPP + +#include + +#include "main.h" +#include "CWatcher.h" + +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +//DECLARE_EVENT(EVENT_Remove); + +/** ChildEvent class ********************************************************/ + +struct ChildEvent +{ + int event; + void *watcher; + void *child; + + ChildEvent(int e, void *w, void *c); + ~ChildEvent(); +}; + +ChildEvent::ChildEvent(int e, void *w, void *c) +{ + event = e; + watcher = w; + child = c; + + GB.Ref(watcher); + if (child) + GB.Ref(child); +} + +ChildEvent::~ChildEvent() +{ + GB.Unref(&watcher); + if (child) + GB.Unref(&child); +} + +/** CWatcher class ********************************************************/ + +CWatcher::CWatcher(CWATCHER *w, CWIDGET *o) +{ + watcher = w; + control = o; + + GB.Ref(control); + + widget = QWIDGET(control); + cont = 0; + + if (GB.Is(control, CLASS_Container)) + cont = QCONTAINER(control); + if (cont == widget) + cont = 0; + + widget->installEventFilter(this); + if (cont) + cont->installEventFilter(this); + QObject::connect(widget, SIGNAL(destroyed()), this, SLOT(destroy())); +} + +CWatcher::~CWatcher() +{ + if (control) + { + if (control->widget) + { + if (cont) + cont->removeEventFilter(this); + widget->removeEventFilter(this); + } + + GB.Unref(POINTER(&control)); + } +} + +void send_event(void *watcher, intptr_t event) +{ + GB.Raise(watcher, (int)event, 0); + GB.Unref(&watcher); +} + +bool CWatcher::eventFilter(QObject* o, QEvent *e) +{ + if (o == widget) + { + if (e->type() == QEvent::Move) + GB.Raise(watcher, EVENT_Move, 0); + else if (e->type() == QEvent::Resize) + GB.Raise(watcher, EVENT_Resize, 0); + else if (e->type() == QEvent::Show) + GB.Raise(watcher, EVENT_Show, 0); + else if (e->type() == QEvent::Hide) + GB.Raise(watcher, EVENT_Hide, 0); + } + + return false; //return QObject::eventFilter(o, e); +} + +void CWatcher::destroy() +{ + GB.Unref(POINTER(&control)); + control = 0; +} + +/** Watcher class *********************************************************/ + +BEGIN_METHOD(CWATCHER_new, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + THIS->watcher = new CWatcher(THIS, control); + + // No need to reference control, as it is already referenced as event observer! + +END_METHOD + +BEGIN_METHOD_VOID(CWATCHER_free) + + delete THIS->watcher; + THIS->watcher = 0; + +END_METHOD + +BEGIN_PROPERTY(CWATCHER_control) + + GB.ReturnObject(THIS->watcher->getControl()); + +END_PROPERTY + +GB_DESC CWatcherDesc[] = +{ + GB_DECLARE("Watcher", sizeof(CWATCHER)), + + GB_METHOD("_new", NULL, CWATCHER_new, "(Control)Control;"), + GB_METHOD("_free", NULL, CWATCHER_free, NULL), + + GB_PROPERTY("Control", "Control", CWATCHER_control), + + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + + GB_CONSTANT("_DefaultEvent", "s", "Resize"), + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/CWatcher.h b/gb.qt4/src/CWatcher.h new file mode 100644 index 00000000..66c35c62 --- /dev/null +++ b/gb.qt4/src/CWatcher.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + CWatcher.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWATCHER_H +#define __CWATCHER_H + +#include +//Added by qt3to4: +#include + +#include "gambas.h" +#include "CWidget.h" + +class CWatcher; + +typedef + struct { + GB_BASE ob; + CWatcher *watcher; + } + CWATCHER; + +#ifndef __CWATCHER_CPP +extern GB_DESC CWatcherDesc[]; +#else +#define THIS OBJECT(CWATCHER) +#endif + +class CWatcher: public QObject +{ + Q_OBJECT + +public: + + explicit CWatcher(CWATCHER *watcher, CWIDGET *o); + ~CWatcher(); + + bool eventFilter(QObject *, QEvent *); + CWIDGET *getControl() { return control; } + +public slots: + + void destroy(); + +private: + + CWATCHER *watcher; + CWIDGET *control; + QWidget *widget; + QWidget *cont; +}; + + +#endif diff --git a/gb.qt4/src/CWidget.cpp b/gb.qt4/src/CWidget.cpp new file mode 100644 index 00000000..d77d5653 --- /dev/null +++ b/gb.qt4/src/CWidget.cpp @@ -0,0 +1,3489 @@ +/*************************************************************************** + + CWidget.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWIDGET_CPP + +#undef QT3_SUPPORT + +#include "gambas.h" +#include "gb_common.h" + +#include +#include + +#include "CWidget.h" +#include "CFont.h" +#include "CMouse.h" +#include "CKey.h" +#include "CWindow.h" +#include "CConst.h" +#include "CColor.h" +#include "CClipboard.h" +#include "CMenu.h" +#include "CDrawingArea.h" +#include "CTextArea.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_X_WINDOW +static QMap _x11_to_qt_keycode; +#endif + +CWIDGET *CWIDGET_active_control = 0; +CWIDGET *CWIDGET_previous_control = 0; +CWIDGET *CWIDGET_hovered = 0; + +int CCONTROL_last_event_type = 0; + +static bool _focus_change = false; +static bool _doing_focus_change = false; +static CWIDGET *_old_active_control = 0; +static CWIDGET *_hovered = 0; +static CWIDGET *_official_hovered = 0; + +#ifdef QT5 +static CWIDGET *_last_entered = NULL; +#else +static QSet *_enter_leave_set = NULL; +#endif + +static QT_COLOR_FUNC _after_set_color = NULL; + +#define EXT(_ob) ((CWIDGET_EXT *)((CWIDGET *)_ob)->ext) +#define ENSURE_EXT(_ob) (EXT(_ob) ? EXT(_ob) : alloc_ext((CWIDGET *)(_ob))) + +#define HANDLE_PROXY(_ob) \ + while (EXT(_ob) && EXT(_ob)->proxy) \ + _ob = (__typeof__ _ob)(EXT(_ob)->proxy); + +static CWIDGET_EXT *alloc_ext(CWIDGET *_object) +{ + GB.Alloc(POINTER(&(THIS->ext)), sizeof(CWIDGET_EXT)); + CLEAR(THIS_EXT); + THIS_EXT->bg = COLOR_DEFAULT; + THIS_EXT->fg = COLOR_DEFAULT; + THIS_EXT->tag.type = GB_T_NULL; + return THIS_EXT; +} + +static void set_mouse(QWidget *w, int mouse, void *cursor) +{ + QObjectList children; + QObject *child; + int i; + + if (mouse == CURSOR_DEFAULT) + w->unsetCursor(); + else if (mouse == CURSOR_CUSTOM) + { + if (cursor) + w->setCursor(*((CCURSOR *)cursor)->cursor); + else + w->unsetCursor(); + } + else + { + Qt::CursorShape shape; + + switch(mouse) + { + case CURSOR_NONE: shape = Qt::BlankCursor; break; + case CURSOR_ARROW: shape = Qt::ArrowCursor; break; + case CURSOR_HELP: shape = Qt::WhatsThisCursor; break; + case CURSOR_POINTER: shape = Qt::PointingHandCursor; break; + //case CURSOR_CONTEXT_MENU: shape = ; break; + case CURSOR_PROGRESS: shape = Qt::BusyCursor; break; + case CURSOR_WAIT: shape = Qt::WaitCursor; break; + case CURSOR_CELL: shape = Qt::CrossCursor; break; + case CURSOR_CROSSHAIR: shape = Qt::CrossCursor; break; + case CURSOR_TEXT: shape = Qt::IBeamCursor; break; + case CURSOR_VERTICAL_TEXT: shape = Qt::IBeamCursor; break; + case CURSOR_ALIAS: shape = Qt::DragLinkCursor; break; + case CURSOR_COPY: shape = Qt::DragCopyCursor; break; + case CURSOR_NO_DROP: shape = Qt::ForbiddenCursor; break; + case CURSOR_MOVE: shape = Qt::SizeAllCursor; break; + case CURSOR_NOT_ALLOWED: shape = Qt::ForbiddenCursor; break; + case CURSOR_GRAB: shape = Qt::OpenHandCursor; break; + case CURSOR_GRABBING: shape = Qt::ClosedHandCursor; break; + case CURSOR_ALL_SCROLL: shape = Qt::SizeAllCursor; break; + case CURSOR_COL_RESIZE: shape = Qt::SplitHCursor; break; + case CURSOR_ROW_RESIZE: shape = Qt::SplitVCursor; break; + case CURSOR_N_RESIZE: shape = Qt::SizeVerCursor; break; + case CURSOR_E_RESIZE: shape = Qt::SizeHorCursor; break; + case CURSOR_S_RESIZE: shape = Qt::SizeVerCursor; break; + case CURSOR_W_RESIZE: shape = Qt::SizeHorCursor; break; + case CURSOR_NE_RESIZE: shape = Qt::SizeBDiagCursor; break; + case CURSOR_NW_RESIZE: shape = Qt::SizeFDiagCursor; break; + case CURSOR_SW_RESIZE: shape = Qt::SizeBDiagCursor; break; + case CURSOR_SE_RESIZE: shape = Qt::SizeFDiagCursor; break; + case CURSOR_EW_RESIZE: shape = Qt::SizeHorCursor; break; + case CURSOR_NS_RESIZE: shape = Qt::SizeVerCursor; break; + case CURSOR_NESW_RESIZE: shape = Qt::SizeBDiagCursor; break; + case CURSOR_NWSE_RESIZE: shape = Qt::SizeFDiagCursor; break; + //case CURSOR_ZOOM_IN: shape = "zoom-in"; break; + //case CURSOR_ZOOM_OUT: shape = "zoom-out"; break; + default: shape = Qt::ArrowCursor; + } + + w->setCursor(QCursor(shape)); + } + + children = w->children(); + + for (i = 0; i < children.count(); i++) + { + child = children.at(i); + + if (child->isWidgetType() && !CWidget::getReal(child)) + set_mouse((QWidget *)child, CURSOR_DEFAULT, 0); + } +} + +void CWIDGET_set_design(CWIDGET *_object, bool ignore) +{ + if (THIS->flag.design) + return; + + //fprintf(stderr, "CWIDGET_set_design: %s %d\n", THIS->name, ignore); + + CWidget::removeFocusPolicy(WIDGET); + set_mouse(WIDGET, CURSOR_DEFAULT, 0); + + THIS->flag.design = true; + THIS->flag.design_ignore = ignore; + + if (GB.Is(THIS, CLASS_Container)) + { + if (GB.Is(THIS, CLASS_TabStrip)) + { + THIS->flag.fillBackground = TRUE; + CWIDGET_reset_color(THIS); + } + + CCONTAINER_update_design((CCONTAINER *)THIS); + } +} + +void CWIDGET_set_name(CWIDGET *_object, const char *name) +{ + CWINDOW *window; + MyMainWindow *win = 0; + + if (GB.Is(THIS, CLASS_Menu)) + { + if (qobject_cast(((CMENU *)THIS)->toplevel)) + win = (MyMainWindow *)((CMENU *)THIS)->toplevel; + } + else + { + window = CWidget::getWindow(THIS); + if (window) + win = (MyMainWindow *)QWIDGET(window); + + if (win) + { + if (name) + win->setName(name, THIS); + else + win->setName(THIS->name, 0); + } + } + + GB.FreeString(&THIS->name); + + if (name) + THIS->name = GB.NewZeroString(name); +} + +void *CWIDGET_get_parent(void *_object) +{ + QWidget *parent = WIDGET->parentWidget(); + + if (!parent || (GB.Is(THIS, CLASS_Window) && ((CWINDOW *)_object)->toplevel)) + return NULL; + else + return CWidget::get(parent); +} + +void *CWIDGET_get_parent_container(void *_object) +{ + void *parent = CWIDGET_get_parent(THIS); + + if (!parent) + return NULL; + + if (EXT(parent) && EXT(parent)->container_for) + parent = EXT(parent)->container_for; + + return parent; +} + +int CWIDGET_get_handle(void *_object) +{ + return (int)WIDGET->winId(); +} + +bool CWIDGET_is_visible(void *_object) +{ + return THIS->flag.visible; // || !QWIDGET(_object)->isHidden(); +} + +void CWIDGET_register_proxy(void *_object, void *proxy) +{ + void *check = proxy; + + while (check) + { + if (check == THIS) + { + GB.Error("Circular proxy chain"); + return; + } + check = EXT(check) ? EXT(check)->proxy : NULL; + } + + if (proxy && THIS_EXT && proxy == THIS_EXT->proxy) + return; + else if (!proxy && !THIS_EXT) + return; + + if (proxy && EXT(proxy) && EXT(proxy)->proxy_for) + EXT(EXT(proxy)->proxy_for)->proxy = NULL; + + //fprintf(stderr, "proxy: (%p %s) -> (%p %s)\n", THIS, THIS->name, proxy, proxy ? ((CWIDGET *)proxy)->name : "NULL"); + + if (THIS_EXT && THIS_EXT->proxy && EXT(THIS_EXT->proxy)) + EXT(THIS_EXT->proxy)->proxy_for = NULL; + + if (proxy) + ENSURE_EXT(THIS)->proxy = proxy; + else if (EXT(THIS)) + EXT(THIS)->proxy = NULL; + + if (proxy) + ENSURE_EXT(proxy)->proxy_for = THIS; +} + +int CWIDGET_check(void *_object) +{ + return WIDGET == NULL || THIS->flag.deleted; +} + +static QWidget *get_viewport(QWidget *w) +{ + if (qobject_cast(w)) + return ((QAbstractScrollArea *)w)->viewport(); + //else if (qobject_cast(w)) + // return ((Q3ScrollView *)w)->viewport(); + //else if (qobject_cast(w)) + // return ((Q3ListView *)w)->viewport(); + else + return 0; +} + +/*void CWIDGET_update_design(CWIDGET *_object) +{ + if (!CWIDGET_test_flag(THIS, WF_DESIGN) && !CWIDGET_test_flag(THIS, WF_DESIGN_LEADER)) + return; + + //qDebug("CWIDGET_update_design: %s %p", GB.GetClassName(THIS), THIS); + set_design(THIS); +}*/ + +void CWIDGET_init_name(CWIDGET *_object) +{ + static int n = 0; + char *name = GB.GetLastEventName(); + + if (!name) + { + char buffer[16]; + n++; + sprintf(buffer, "#%d", n); + CWIDGET_set_name(THIS, buffer); + } + else + CWIDGET_set_name(THIS, name); +} + +bool CWIDGET_container_for(void *_object, void *container_for) +{ + if (THIS_EXT) + { + if (container_for) + { + if (!THIS_EXT->container_for) + { + THIS_EXT->container_for = container_for; + return false; + } + } + else + { + THIS_EXT->container_for = NULL; + return false; + } + } + else + { + if (container_for) + ENSURE_EXT(THIS)->container_for = container_for; + return false; + } + + return true; +} + +static void CWIDGET_enter(void *_object) +{ + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + + if (parent && !parent->flag.inside) + CWIDGET_enter(parent); + + if (!THIS->flag.inside) + { + //qDebug("CWIDGET_enter: %p %s", THIS, THIS->name); +#ifdef QT5 + _last_entered = THIS; +#endif + THIS->flag.inside = true; + GB.Raise(THIS, EVENT_Enter, 0); + } +} + +static void CWIDGET_leave(void *_object) +{ +#ifdef QT5 + if (_last_entered == THIS) + _last_entered = (CWIDGET *)CWIDGET_get_parent((void *)_last_entered); +#endif + if (THIS->flag.inside) + { + //qDebug("CWIDGET_leave: %p %s", THIS, THIS->name); + THIS->flag.inside = false; + GB.Raise(THIS, EVENT_Leave, 0); + } +} + +bool CWIDGET_get_allow_focus(void *_object) +{ + return WIDGET->focusPolicy() != Qt::NoFocus; +} + +void CWIDGET_set_allow_focus(void *_object, bool f) +{ + if (f) + { + WIDGET->setFocusPolicy(THIS->flag.wheel | GB.CanRaise(THIS, EVENT_MouseWheel) ? Qt::WheelFocus : Qt::StrongFocus); + WIDGET->setAttribute(Qt::WA_InputMethodEnabled, true); + } + else + { + WIDGET->setFocusPolicy(Qt::NoFocus); + } +} + + +void CWIDGET_new(QWidget *w, void *_object, bool no_show, bool no_filter, bool no_init) +{ + //QAbstractScrollArea *sa; + + CWidget::add(w, _object, no_filter); + + //QWidget *p = w->parentWidget(); + //qDebug("CWIDGET_new: %s %p: %p in (%s %p)", GB.GetClassName(THIS), THIS, w, p ? GB.GetClassName(CWidget::get(p)) : "", CWidget::get(p)); + + THIS->widget = w; + //THIS->level = MAIN_loop_level; + + if (!no_init) + CWIDGET_init_name(THIS); + + if (qobject_cast(w)) // || qobject_cast(w)) + THIS->flag.scrollview = TRUE; + + //w->setAttribute(Qt::WA_PaintOnScreen, true); + + CWIDGET_reset_color(THIS); //w->setPalette(QApplication::palette()); + + //THIS->flag.fillBackground = GB.Is(THIS, CLASS_Container); + //w->setAutoFillBackground(THIS->flag.fillBackground); + + if (!no_show) + { + w->setGeometry(-16, -16, 8, 8); + CWIDGET_set_visible(THIS, true); + w->raise(); + } + + CCONTAINER_insert_child(THIS); +} + + +QString CWIDGET_Utf8ToQString(GB_STRING *str) +{ + return QString::fromUtf8((const char *)(str->value.addr + str->value.start), str->value.len); +} + +#ifdef QT5 +void CWIDGET_leave_popup(void *) +{ + while (_last_entered) + CWIDGET_leave(_last_entered); +} +#else +void *CWIDGET_enter_popup() +{ + void *save = _enter_leave_set; + + _enter_leave_set = new QSet; + return save; +} + +void CWIDGET_leave_popup(void *save) +{ + CWIDGET *_object; + QSetIterator i(*_enter_leave_set); + + while (i.hasNext()) + { + _object = i.next(); + GB.Unref(POINTER(&_object)); + if (_object) + { + if (THIS->flag.inside_later != THIS->flag.inside) + { + if (THIS->flag.inside_later) + CWIDGET_enter(THIS); + else + CWIDGET_leave(THIS); + } + } + } + + delete _enter_leave_set; + _enter_leave_set = (QSet*) save; +} + +static void insert_enter_leave_event(CWIDGET *control, bool in) +{ + control->flag.inside_later = in; + + if (_enter_leave_set->contains(control)) + return; + + _enter_leave_set->insert(control); + GB.Ref(control); +} +#endif + +static bool _post_check_hovered = false; +static CWIDGET *_post_check_hovered_window = NULL; + +static void post_check_hovered(intptr_t) +{ + void *_object = _post_check_hovered_window; + + if (!THIS) + _object = CWINDOW_Active; + + if (THIS && WIDGET) + { + //qDebug("post_check_hovered"); + const QPoint globalPos(QCursor::pos()); + QPoint pos = WIDGET->mapFromGlobal(globalPos); + _hovered = CWidget::getRealExisting(WIDGET->childAt(pos)); + if (_hovered) + CWIDGET_enter(_hovered); + } + + _post_check_hovered = false; + _post_check_hovered_window = NULL; + +} + +void CWIDGET_check_hovered() +{ + _post_check_hovered_window = NULL; + post_check_hovered(0); +} + +void CWIDGET_destroy(CWIDGET *_object) +{ + if (!THIS || !WIDGET) + return; + + if (THIS->flag.deleted) + return; + + if (THIS->flag.dragging) + { + GB.Error("Control is being dragged"); + return; + } + +#ifdef QT5 + if (_last_entered == THIS) + _last_entered = NULL; +#endif + + //qDebug("CWIDGET_destroy: %s %p", GB.GetClassName(THIS), THIS); + + CWIDGET_set_visible(THIS, false); + THIS->flag.deleted = true; + + WIDGET->deleteLater(); +} + +#define COORD(_c) ((qobject_cast(WIDGET) && WIDGET->isWindow()) ? ((CWINDOW *)_object)->_c : WIDGET->pos()._c()) +#define get_widget(_object) QWIDGET(_object) + +static void arrange_parent(CWIDGET *_object) +{ + void *parent = CWIDGET_get_parent(THIS); + if (!parent) + return; + if (CWIDGET_check(parent)) + return; + CCONTAINER_arrange(parent); +} + +void CWIDGET_check_visibility(CWIDGET *_object) +{ + if (!THIS->flag.resized) + { + THIS->flag.resized = TRUE; + //qDebug("CWIDGET_check_visibility: %s %s %d", GB.GetClassName(THIS), THIS->name, THIS->flag.visible); + CWIDGET_set_visible(THIS, THIS->flag.visible); + } +} + +static void CWIDGET_after_geometry_change(void *_object, bool arrange) +{ + if (arrange) + { + if (GB.Is(THIS, CLASS_Container)) + CCONTAINER_arrange(THIS); + if (GB.Is(THIS, CLASS_DrawingArea)) + ((MyDrawingArea *)((CWIDGET *)_object)->widget)->updateBackground(); + } + + if (!THIS->flag.ignore) + arrange_parent(THIS); +} + +void CWIDGET_move_resize(void *_object, int x, int y, int w, int h) +{ + QWidget *wid = WIDGET; + bool arrange = true; + + if (GB.Is(THIS, CLASS_Window)) + { + CWINDOW_move_resize(THIS, x, y, w, h); + } + else + { + if (w < 0) + w = wid->width(); + + if (h < 0) + h = wid->height(); + + if (x == wid->x() && y == wid->y() && w == wid->width() && h == wid->height()) + return; + + if (w == wid->width() && h == wid->height()) + arrange = false; + + wid->setGeometry(x, y, w, h); + } + + CWIDGET_check_visibility(THIS); + CWIDGET_after_geometry_change(THIS, arrange); +} + +void CWIDGET_move(void *_object, int x, int y) +{ + CWIDGET_move_resize(THIS, x, y, -1, -1); +} + +void CWIDGET_resize(void *_object, int w, int h) +{ + CWIDGET_move_resize(THIS, COORD(x), COORD(y), w, h); +} + +void CWIDGET_auto_resize(void *_object, int w, int h) +{ + bool dw, dh; + CCONTAINER_decide(THIS, &dw, &dh); + CWIDGET_resize(THIS, dw ? -1 : w, dh ? -1 : h); +} + +void CWIDGET_auto_move_resize(void *_object, int x, int y, int w, int h) +{ + bool dw, dh; + CCONTAINER_decide(THIS, &dw, &dh); + CWIDGET_move_resize(THIS, x, y, dw ? -1 : w, dh ? -1 : h); +} + +#if 0 +void CWIDGET_check_hovered() +{ + //qDebug("CWIDGET_check_hovered: %p %s -> %p %s", _hovered, _hovered ? _hovered->name : 0, _official_hovered, _official_hovered ? _official_hovered->name : 0); + + if (_official_hovered != _hovered) + { + if (_official_hovered) + CWIDGET_leave(_official_hovered); + + if (_hovered) + CWIDGET_enter(_hovered); + + _official_hovered = _hovered; + } +} +#endif + +bool CWIDGET_is_design(void *_object) +{ + return THIS->flag.design && !THIS->flag.no_design; +} + +static void _cleanup_CWIDGET_raise_event_action(intptr_t object) +{ + GB.Unref(POINTER(&object)); +} + +void CWIDGET_raise_event_action(void *object, int event) +{ + GB_RAISE_HANDLER handler; + + GB.Ref(object); + + handler.callback = _cleanup_CWIDGET_raise_event_action; + handler.data = (intptr_t)object; + + GB.RaiseBegin(&handler); + GB.Raise(object, event, 0); + GB.RaiseEnd(&handler); + + CACTION_raise(object); + + GB.Unref(POINTER(&object)); +} + + +static void update_direction(void *_object) +{ + if (THIS->flag.inverted) + { + switch (THIS->flag.direction) + { + case DIRECTION_LTR: WIDGET->setLayoutDirection(Qt::RightToLeft); break; + case DIRECTION_RTL: WIDGET->setLayoutDirection(Qt::LeftToRight); break; + default: + WIDGET->unsetLayoutDirection(); + WIDGET->setLayoutDirection(WIDGET->isLeftToRight() ? Qt::RightToLeft : Qt::LeftToRight); + } + } + else + { + switch (THIS->flag.direction) + { + case DIRECTION_LTR: WIDGET->setLayoutDirection(Qt::LeftToRight); break; + case DIRECTION_RTL: WIDGET->setLayoutDirection(Qt::RightToLeft); break; + default: WIDGET->unsetLayoutDirection(); + } + } + + if (GB.Is(THIS, CLASS_Container)) + CCONTAINER_arrange(THIS); +} + + +void CWIDGET_set_inverted(void *_object, bool v) +{ + if (v == THIS->flag.inverted) + return; + + THIS->flag.inverted = v; + update_direction(THIS); +} + + +/*gControl *gControl::nextFocus() +{ + gControl *ctrl; + + if (isContainer()) + { + ctrl = ((gContainer *)this)->firstChild(); + if (ctrl) + return ctrl; + } + + ctrl = this; + + while (!ctrl->next()) + { + ctrl = ctrl->parent(); + if (ctrl->isTopLevel()) + return ctrl->nextFocus(); + } + + return ctrl->next(); +}*/ + +bool CWIDGET_has_no_tab_focus(void *_object) +{ + void *parent; + + for(;;) + { + parent = CWIDGET_get_parent(THIS); + HANDLE_PROXY(_object); + if (THIS->flag.no_tab_focus) + return true; + if (!parent) + return false; + _object = parent; + } +} + + +void *CWIDGET_get_next_focus(void *_object) +{ + void *ob, *next; + + //fprintf(stderr, "next: %s\n", CWIDGET_get_name(THIS)); + ob = CCONTAINER_get_first_child(THIS); + if (ob) + { + //fprintf(stderr, "=> %s\n", CWIDGET_get_name(ob)); + return ob; + } + + ob = THIS; + + for(;;) + { + //fprintf(stderr, "... %s\n", CWIDGET_get_name(ob)); + next = CWIDGET_get_next_previous(ob, true); + if (next) + { + //fprintf(stderr, "=> %s\n", CWIDGET_get_name(next)); + return next; + } + + ob = CWIDGET_get_parent(ob); + if (!ob) + return NULL; + if (!CWIDGET_get_parent(ob)) + { + ob = CWIDGET_get_next_focus(ob); + //fprintf(stderr, "=> %s\n", CWIDGET_get_name(ob)); + return ob; + } + } +} + +/*gControl *gControl::previousFocus() +{ + gControl *ctrl = previous(); + + if (!ctrl) + { + if (!isTopLevel()) + return parent()->previousFocus(); + + ctrl = this; + } + + while (ctrl->isContainer() && ((gContainer *)ctrl)->childCount()) + ctrl = ((gContainer *)ctrl)->lastChild(); + + return ctrl; +}*/ + +void *CWIDGET_get_previous_focus(void *_object) +{ + void *ob, *ctrl; + + //fprintf(stderr, "previous: %s\n", CWIDGET_get_name(THIS)); + ctrl = CWIDGET_get_next_previous(THIS, false); + + if (!ctrl) + { + ob = CWIDGET_get_parent(THIS); + if (ob) + { + ob = CWIDGET_get_previous_focus(ob); + //fprintf(stderr, "=> %s\n", CWIDGET_get_name(ob)); + return ob; + } + + ctrl = THIS; + } + + for(;;) + { + /*if (!CWIDGET_is_visible(ctrl)) + return ctrl;*/ + //fprintf(stderr, "... %s\n", CWIDGET_get_name(ctrl)); + ob = CCONTAINER_get_last_child(ctrl); + if (!ob) + { + //fprintf(stderr, "=> %s\n", CWIDGET_get_name(ctrl)); + return ctrl; + } + ctrl = ob; + } +} + + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Control_new) + + MAIN_CHECK_INIT(); + +END_METHOD + +BEGIN_PROPERTY(Control_X) + + if (READ_PROPERTY) + GB.ReturnInteger(COORD(x)); + else + { + CWIDGET_move(_object, VPROP(GB_INTEGER), COORD(y)); + /*if (WIDGET->isWindow()) + qDebug("X: %d ==> X = %d", PROPERTY(int), WIDGET->x());*/ + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_ScreenX) + + GB.ReturnInteger(WIDGET->mapToGlobal(QPoint(0, 0)).x()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Y) + + if (READ_PROPERTY) + GB.ReturnInteger(COORD(y)); + else + CWIDGET_move(_object, COORD(x), VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_ScreenY) + + GB.ReturnInteger(WIDGET->mapToGlobal(QPoint(0, 0)).y()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Width) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->width()); + else + CWIDGET_auto_resize(_object, VPROP(GB_INTEGER), -1); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Height) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->height()); + else + CWIDGET_auto_resize(_object, -1, VPROP(GB_INTEGER)); + +END_PROPERTY + +void *CWIDGET_get_real_font(CWIDGET *_object) +{ + if (THIS->font) + return CFONT_create(*((CFONT *)THIS->font)->font); + + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + if (parent) + return CWIDGET_get_real_font(parent); + else + return CFONT_create(qApp->font()); +} + +BEGIN_PROPERTY(Control_Font) + + CFONT *font; + + if (!THIS->font) + { + THIS->font = CFONT_create(WIDGET->font(), 0, THIS); + GB.Ref(THIS->font); + } + + if (READ_PROPERTY) + { + *(((CFONT *)THIS->font)->font) = WIDGET->font(); + GB.ReturnObject(THIS->font); + } + else + { + font = (CFONT *)VPROP(GB_OBJECT); + + if (!font) + { + WIDGET->setFont(QFont()); + GB.Unref(POINTER(&THIS->font)); + THIS->font = NULL; + } + else + { + WIDGET->setFont(*(font->font)); + *(((CFONT *)THIS->font)->font) = WIDGET->font(); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Design) + + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_is_design(THIS)); + else + { + if (VPROP(GB_BOOLEAN)) + CWIDGET_set_design(THIS); + else if (CWIDGET_is_design(THIS)) + GB.Error("Design property cannot be reset"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isEnabled()); + else + WIDGET->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_HasFocus) + + HANDLE_PROXY(_object); + + GB.ReturnBoolean(WIDGET->hasFocus()); + +END_PROPERTY + +BEGIN_PROPERTY(Control_Hovered) + + if (!CWIDGET_is_visible(THIS)) + GB.ReturnBoolean(false); + else + GB.ReturnBoolean(THIS->flag.inside); + +END_PROPERTY + +BEGIN_PROPERTY(Control_Expand) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.expand); + else if (THIS->flag.expand != VPROP(GB_BOOLEAN)) + { + THIS->flag.expand = VPROP(GB_BOOLEAN); + CWIDGET_check_visibility(THIS); + if (!THIS->flag.ignore) + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Ignore) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.ignore); + else if (THIS->flag.ignore != VPROP(GB_BOOLEAN)) + { + THIS->flag.ignore = VPROP(GB_BOOLEAN); + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD(Control_Move, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CWIDGET_auto_move_resize(_object, VARG(x), VARG(y), VARGOPT(w, -1), VARGOPT(h, -1)); + +END_METHOD + + +BEGIN_METHOD(Control_Resize, GB_INTEGER w; GB_INTEGER h) + + CWIDGET_auto_resize(_object, VARG(w), VARG(h)); + +END_METHOD + + +BEGIN_METHOD(Control_MoveScaled, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + int x, y, w, h; + int scale = MAIN_scale; + + x = (int)(VARG(x) * scale + 0.5); + y = (int)(VARG(y) * scale + 0.5); + w = (MISSING(w) ? -1 : (VARG(w) * scale + 0.5)); + h = (MISSING(h) ? -1 : (VARG(h) * scale + 0.5)); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + CWIDGET_auto_move_resize(_object, x, y, w, h); + +END_METHOD + + +BEGIN_METHOD(Control_ResizeScaled, GB_FLOAT w; GB_FLOAT h) + + int w, h; + int scale = MAIN_scale; + + w = (int)(VARG(w) * scale + 0.5); + h = (int)(VARG(h) * scale + 0.5); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + CWIDGET_auto_resize(_object, w , h); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Delete) + + //if (WIDGET) + // qDebug("CWIDGET_delete: %p (%p)", THIS, WIDGET); + + CWIDGET_destroy(THIS); + +END_METHOD + + +void CWIDGET_set_visible(CWIDGET *_object, bool v) +{ + bool arrange = false; + + THIS->flag.visible = v; + + if (!THIS->flag.resized) + return; + + if (THIS->flag.visible) + { + arrange = !WIDGET->isVisible(); + QWIDGET(_object)->show(); + if (GB.Is(THIS, CLASS_Container)) + CCONTAINER_arrange(THIS); + } + else + { + arrange = !WIDGET->isHidden(); + QWIDGET(_object)->hide(); + } + + if (arrange && !THIS->flag.ignore) + arrange_parent(THIS); +} + + + +BEGIN_PROPERTY(Control_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_is_visible(THIS)); + else + { + CWIDGET_set_visible(THIS, VPROP(GB_BOOLEAN)); + CWIDGET_check_visibility(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Control_Show) + + CWIDGET_set_visible(THIS, true); + CWIDGET_check_visibility(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Hide) + + CWIDGET_set_visible(THIS, false); + CWIDGET_check_visibility(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Raise) + + QWIDGET(_object)->raise(); + arrange_parent(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Lower) + + QWIDGET(_object)->lower(); + arrange_parent(THIS); + +END_METHOD + + +BEGIN_METHOD(Control_Move_under, GB_OBJECT control) + + CWIDGET *ob = (CWIDGET *)VARG(control); + + if (GB.CheckObject(ob)) + return; + + WIDGET->stackUnder(ob->widget); + +END_METHOD + + +void *CWIDGET_get_next_previous(void *_object, bool next) +{ + QWidget *parent; + QObjectList children; + int i; + void *current; + + parent = WIDGET->parentWidget(); + if (!parent) + return NULL; + + children = WIDGET->parentWidget()->children(); + i = children.indexOf(WIDGET); + for(;;) + { + if (next) + { + i++; + if (i >= children.count()) + return NULL; + } + else + { + i--; + if (i < 0) + return NULL; + } + + current = CWidget::getRealExisting(children.at(i)); + if (current) + return current; + } +} + +BEGIN_PROPERTY(Control_Next) + + if (READ_PROPERTY) + { + GB.ReturnObject(CWIDGET_get_next_previous(THIS, true)); + } + else + { + CWIDGET *ob = (CWIDGET *)VPROP(GB_OBJECT); + + if (!ob) + WIDGET->raise(); + else + { + if (GB.CheckObject(ob)) + return; + + WIDGET->stackUnder(ob->widget); + } + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Previous) + + if (READ_PROPERTY) + { + GB.ReturnObject(CWIDGET_get_next_previous(THIS, false)); + } + else + { + CWIDGET *ob = (CWIDGET *)VPROP(GB_OBJECT); + + if (!ob) + { + WIDGET->lower(); + } + else + { + if (GB.CheckObject(ob)) + return; + + ob = (CWIDGET *)CWIDGET_get_next_previous(ob, true); + if (ob) + WIDGET->stackUnder(QWIDGET(ob)); + } + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Control_Refresh) //, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + QWIDGET(_object)->update(); + if (THIS->flag.scrollview) + get_viewport(WIDGET)->update(); + +END_METHOD + + +void CWIDGET_set_focus(void *_object) +{ + CWINDOW *win; + + HANDLE_PROXY(_object); + + win = CWidget::getTopLevel(THIS); + + if (win->opened && QWIDGET(win)->isVisible()) + { + if (qobject_cast(WIDGET) || qobject_cast(WIDGET)) + WIDGET->setFocus(Qt::MouseFocusReason); + else + WIDGET->setFocus(Qt::TabFocusReason); + //WIDGET->window()->setAttribute(Qt::WA_KeyboardFocusChange, false); + } + else if ((CWIDGET *)win != THIS) + { + //qDebug("delayed focus on %s for %s", THIS->name, ((CWIDGET *)win)->name); + GB.Unref(POINTER(&win->focus)); + win->focus = THIS; + GB.Ref(THIS); + } +} + +BEGIN_METHOD_VOID(Control_SetFocus) + + CWIDGET_set_focus(THIS); + +END_METHOD + + +BEGIN_PROPERTY(Control_Tag) + + if (READ_PROPERTY) + { + if (THIS_EXT) + GB.ReturnVariant(&THIS_EXT->tag); + else + { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } + } + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&(ENSURE_EXT(THIS)->tag))); + +END_METHOD + + +BEGIN_PROPERTY(Control_Mouse) + + QWidget *wid; + + HANDLE_PROXY(_object); + + wid = QWIDGET(_object); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS_EXT ? THIS_EXT->mouse : CURSOR_DEFAULT); + else + { + int mouse = VPROP(GB_INTEGER); + + if (mouse != CURSOR_DEFAULT || THIS_EXT) + ENSURE_EXT(THIS)->mouse = mouse; + + set_mouse(wid, mouse, THIS_EXT ? THIS_EXT->cursor : NULL); + } + +END_METHOD + + +BEGIN_PROPERTY(Control_Cursor) + + HANDLE_PROXY(_object); + + if (READ_PROPERTY) + GB.ReturnObject(THIS_EXT ? THIS_EXT->cursor : NULL); + else + { + GB.StoreObject(PROP(GB_OBJECT), &(ENSURE_EXT(THIS)->cursor)); + set_mouse(WIDGET, CURSOR_CUSTOM, THIS_EXT->cursor); + ENSURE_EXT(THIS)->mouse = CURSOR_CUSTOM; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_NoTabFocus) + + HANDLE_PROXY(_object); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.no_tab_focus); + else + THIS->flag.no_tab_focus = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Direction) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->flag.direction); + else + { + int dir = VPROP(GB_INTEGER); + if (dir < 0 || dir > 2) dir = 0; + THIS->flag.direction = dir; + update_direction(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_RightToLeft) + + GB.ReturnBoolean(WIDGET->isRightToLeft()); + +END_PROPERTY + + +static QWidget *get_color_widget(CWIDGET *_object) +{ + QWidget *view = get_viewport(WIDGET); + if (view) + return view; + + return WIDGET; +} + +QT_COLOR_FUNC CWIDGET_after_set_color(QT_COLOR_FUNC func) +{ + QT_COLOR_FUNC old = _after_set_color; + _after_set_color = func; + return old; +} + + +void CWIDGET_reset_color(CWIDGET *_object) +{ + GB_COLOR fg, bg; + QPalette palette; + QWidget *w; + + HANDLE_PROXY(_object); + //qDebug("reset_color: %s", THIS->name); + //qDebug("set_color: (%s %p) bg = %08X (%d) fg = %08X (%d)", GB.GetClassName(THIS), THIS, THIS->bg, w->backgroundRole(), THIS->fg, w->foregroundRole()); + + w = get_color_widget(THIS); + + if (!THIS_EXT || (THIS_EXT->bg == COLOR_DEFAULT && THIS_EXT->fg == COLOR_DEFAULT)) + { + w->setPalette(QPalette()); + w->setAutoFillBackground(THIS->flag.autoFillBackground); //!THIS->flag.noBackground && THIS->flag.fillBackground); + } + else + { + bg = THIS_EXT->bg; + fg = THIS_EXT->fg; + + if (GB.Is(THIS, CLASS_TextArea)) + { + palette = QPalette(); + + if (bg != COLOR_DEFAULT) + { + palette.setColor(QPalette::Base, TO_QCOLOR(bg)); + palette.setColor(QPalette::Window, TO_QCOLOR(bg)); + palette.setColor(QPalette::Button, TO_QCOLOR(bg)); + } + + if (fg != COLOR_DEFAULT) + { + palette.setColor(QPalette::Text, TO_QCOLOR(fg)); + palette.setColor(QPalette::WindowText, TO_QCOLOR(fg)); + palette.setColor(QPalette::ButtonText, TO_QCOLOR(fg)); + } + + w->setPalette(palette); + + CTEXTAREA_set_foreground(THIS); + } + else + { + palette = QPalette(); + + if (bg != COLOR_DEFAULT) + { + /*if (GB.Is(THIS, CLASS_Container)) + { + palette.setColor(QPalette::Base, TO_QCOLOR(bg)); + palette.setColor(QPalette::Window, TO_QCOLOR(bg)); + palette.setColor(QPalette::Button, TO_QCOLOR(bg)); + } + else*/ + palette.setColor(w->backgroundRole(), TO_QCOLOR(bg)); + w->setAutoFillBackground(!THIS->flag.noBackground && (THIS->flag.fillBackground || w->backgroundRole() == QPalette::Window)); + } + else + w->setAutoFillBackground(THIS->flag.autoFillBackground); + + if (fg != COLOR_DEFAULT) + { + //if (GB.Is(THIS, CLASS_Container)) + { + palette.setColor(QPalette::Text, TO_QCOLOR(fg)); + palette.setColor(QPalette::WindowText, TO_QCOLOR(fg)); + palette.setColor(QPalette::ButtonText, TO_QCOLOR(fg)); + } + //else + //palette.setColor(w->foregroundRole(), TO_QCOLOR(fg)); + } + + w->setPalette(palette); + } + + } + + //w->setAutoFillBackground(THIS->bg != COLOR_DEFAULT); + + + if (_after_set_color) + (*_after_set_color)(THIS); + + if (!GB.Is(THIS, CLASS_Container)) + return; + + if (GB.Is(THIS, CLASS_Window)) + CWINDOW_define_mask((CWINDOW *)THIS); +} + + +void CWIDGET_set_color(CWIDGET *_object, int bg, int fg, bool handle_proxy) +{ + if (handle_proxy) { HANDLE_PROXY(_object); } + + ENSURE_EXT(THIS); + THIS_EXT->bg = bg; + THIS_EXT->fg = fg; + + CWIDGET_reset_color(THIS); +} + + +GB_COLOR CWIDGET_get_background(CWIDGET *_object, bool handle_proxy) +{ + if (handle_proxy) { HANDLE_PROXY(_object); } + + return THIS_EXT ? THIS_EXT->bg : COLOR_DEFAULT; +} + + +GB_COLOR CWIDGET_get_real_background(CWIDGET *_object) +{ + GB_COLOR bg = CWIDGET_get_background(THIS); + + if (bg != COLOR_DEFAULT) + return bg; + + return WIDGET->palette().color(WIDGET->backgroundRole()).rgb() & 0xFFFFFF; + + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + + if (parent) + return CWIDGET_get_real_background(parent); + else + return QApplication::palette().color(QPalette::Window).rgb() & 0xFFFFFF; +} + + +GB_COLOR CWIDGET_get_foreground(CWIDGET *_object, bool handle_proxy) +{ + if (handle_proxy) { HANDLE_PROXY(_object); } + + return THIS_EXT ? THIS_EXT->fg : COLOR_DEFAULT; +} + + +GB_COLOR CWIDGET_get_real_foreground(CWIDGET *_object) +{ + GB_COLOR fg = CWIDGET_get_foreground(THIS); + + if (fg != COLOR_DEFAULT) + return fg; + + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + + if (parent) + return CWIDGET_get_real_foreground(parent); + else + return QApplication::palette().color(QPalette::WindowText).rgb() & 0xFFFFFF; +} + + +BEGIN_PROPERTY(Control_Background) + + if (THIS_EXT && THIS_EXT->proxy) + { + if (READ_PROPERTY) + GB.GetProperty(THIS_EXT->proxy, "Background"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(THIS_EXT->proxy, "Background", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CWIDGET_get_background(THIS)); + else + { + GB_COLOR col = VPROP(GB_INTEGER); + if (col != CWIDGET_get_background(THIS)) + CWIDGET_set_color(THIS, col, CWIDGET_get_foreground(THIS)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Foreground) + + if (THIS_EXT && THIS_EXT->proxy) + { + if (READ_PROPERTY) + GB.GetProperty(THIS_EXT->proxy, "Foreground"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(THIS_EXT->proxy, "Foreground", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CWIDGET_get_foreground(THIS)); + else + { + GB_COLOR col = VPROP(GB_INTEGER); + if (col != CWIDGET_get_foreground(THIS)) + CWIDGET_set_color(THIS, CWIDGET_get_background(THIS), col); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Parent) + + GB.ReturnObject(CWIDGET_get_parent_container(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control__Parent) + + GB.ReturnObject(CWIDGET_get_parent(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Window) + + GB.ReturnObject(CWidget::getWindow(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Id) + + GB.ReturnInteger((int)WIDGET->winId()); + +END_PROPERTY + + +/*static QString remove_ampersand(const QString &s) +{ + QString r; + uint i; + + for (i = 0; i < s.length(); i++) + { + if (s[i] == '&') + { + i++; + if (i < s.length()) + r += s[i]; + } + else + { + r += s[i]; + } + } + + return r; +}*/ + + +BEGIN_PROPERTY(Control_Tooltip) + + //QWidget *w; + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->toolTip()); + else + { + QString tip = QSTRING_PROP(); + if (THIS->flag.inside) + { + if (tip.isEmpty()) + QToolTip::hideText(); + else if (QToolTip::isVisible()) + { + QToolTip::hideText(); + QToolTip::showText(QCursor::pos(), tip, WIDGET); + } + } + WIDGET->setToolTip(tip); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Name) + + if (READ_PROPERTY) + GB.ReturnString(THIS->name); + else + CWIDGET_set_name(THIS, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Action) + + char *current = THIS_EXT ? THIS_EXT->action : NULL; + + if (READ_PROPERTY) + GB.ReturnString(current); + else + { + char *action = PLENGTH() ? GB.NewString(PSTRING(), PLENGTH()) : NULL; + + CACTION_register(THIS, current, action); + + if (THIS_EXT) + GB.FreeString(&THIS_EXT->action); + + if (action) + ENSURE_EXT(THIS)->action = action; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Proxy) + + if (READ_PROPERTY) + GB.ReturnObject(THIS_EXT ? THIS_EXT->proxy : NULL); + else + CWIDGET_register_proxy(THIS, VPROP(GB_OBJECT)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS_EXT ? THIS_EXT->popup : NULL); + else + GB.StoreString(PROP(GB_STRING), &(ENSURE_EXT(THIS)->popup)); + +END_PROPERTY + + +/*BEGIN_METHOD_VOID(Control_Screenshot) + + GB.ReturnObject(CPICTURE_grab(QWIDGET(_object))); + +END_METHOD*/ + + +BEGIN_METHOD(Control_Drag, GB_VARIANT data; GB_STRING format) + + static GB_FUNCTION func; + static bool init = FALSE; + + if (!init) + { + GB.GetFunction(&func, (void *)GB.FindClass("Drag"), "_call", NULL, NULL); + init = TRUE; + } + + GB.Push(2, GB_T_OBJECT, THIS, GB_T_VARIANT, &VARG(data)); + if (MISSING(format)) + { + GB.Call(&func, 2, FALSE); + } + else + { + GB.Push(1, GB_T_STRING, STRING(format), LENGTH(format)); + GB.Call(&func, 3, FALSE); + } + +END_METHOD + + +BEGIN_METHOD(Control_Reparent, GB_OBJECT container; GB_INTEGER x; GB_INTEGER y) + + QPoint p(WIDGET->pos()); + bool show; + + if (!MISSING(x) && !MISSING(y)) + { + p.setX(VARG(x)); + p.setY(VARG(y)); + } + + if (GB.CheckObject(VARG(container))) + return; + + show = CWIDGET_is_visible(THIS); + CWIDGET_set_visible(THIS, false); + WIDGET->setParent(QCONTAINER(VARG(container))); + WIDGET->move(p); + CCONTAINER_insert_child(THIS); + CWIDGET_set_visible(THIS, show); + +END_METHOD + + +BEGIN_PROPERTY(Control_Drop) + + HANDLE_PROXY(_object); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.drop); + else + { + THIS->flag.drop = VPROP(GB_BOOLEAN); + if (THIS->flag.scrollview) + get_viewport(WIDGET)->setAcceptDrops(VPROP(GB_BOOLEAN)); + else + WIDGET->setAcceptDrops(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + +static bool has_tracking(CWIDGET *_object) +{ + HANDLE_PROXY(_object); + return THIS->flag.tracking; +} + +BEGIN_PROPERTY(Control_Tracking) + + HANDLE_PROXY(_object); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.tracking); + else + { + if (VPROP(GB_BOOLEAN) != THIS->flag.tracking) + { + THIS->flag.tracking = VPROP(GB_BOOLEAN); + if (THIS->flag.tracking) + { + THIS->flag.old_tracking = WIDGET->hasMouseTracking(); + WIDGET->setMouseTracking(true); + } + else + { + WIDGET->setMouseTracking(THIS->flag.old_tracking); + } + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_border_full) + + QFrame *wid = (QFrame *)QWIDGET(_object); + int border, lw; + + if (READ_PROPERTY) + { + if (wid->frameStyle() == (QFrame::Box + QFrame::Plain) && wid->lineWidth() == 1) + border = BORDER_PLAIN; + else if (wid->frameStyle() == (QFrame::StyledPanel + QFrame::Sunken)) + border = BORDER_SUNKEN; + else if (wid->frameStyle() == (QFrame::StyledPanel + QFrame::Raised)) + border = BORDER_RAISED; + else if (wid->frameStyle() == (QFrame::StyledPanel + QFrame::Plain)) + border = BORDER_ETCHED; + else + border = BORDER_NONE; + + GB.ReturnInteger(border); + } + else + { + switch (VPROP(GB_INTEGER)) + { + case BORDER_PLAIN: border = QFrame::Box + QFrame::Plain; lw = 1; break; + case BORDER_SUNKEN: border = QFrame::StyledPanel + QFrame::Sunken; lw = 2; break; + case BORDER_RAISED: border = QFrame::StyledPanel + QFrame::Raised; lw = 2; break; + case BORDER_ETCHED: border = QFrame::StyledPanel + QFrame::Plain; lw = 2; break; + default: border = QFrame::NoFrame; lw = 0; break; + } + + wid->setFrameStyle(border); + wid->setLineWidth(lw); + wid->update(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_border_simple) + + QFrame *wid = (QFrame *)QWIDGET(_object); + + if (READ_PROPERTY) + { + GB.ReturnBoolean(wid->frameStyle() != QFrame::NoFrame); + } + else + { + //qDebug("frameStyle = %d", wid->frameStyle()); + + if (VPROP(GB_BOOLEAN)) + { + wid->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + //wid->setFrameStyle(QFrame::LineEditPanel); + wid->setLineWidth(2); + } + else + { + wid->setFrameStyle(QFrame::NoFrame); + wid->setLineWidth(0); + } + + //qDebug("--> %s %d %d %d %d", THIS->name, wid->contentsRect().x(), wid->contentsRect().y(), wid->contentsRect().width(), wid->contentsRect().height()); + //wid->style()->polish(wid); + wid->update(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_scrollbar) + + QAbstractScrollArea *wid = qobject_cast(WIDGET); + //Q3ScrollView *sw = qobject_cast(WIDGET); + int scroll; + + if (wid) + { + if (READ_PROPERTY) + { + scroll = 0; + if (wid->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded) + scroll += 1; + if (wid->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + wid->setHorizontalScrollBarPolicy( (scroll & 1) ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff); + wid->setVerticalScrollBarPolicy( (scroll & 2) ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff); + } + } + /*else if (sw) + { + if (READ_PROPERTY) + { + scroll = 0; + if (sw->hScrollBarMode() == Q3ScrollView::Auto) + scroll += 1; + if (sw->vScrollBarMode() == Q3ScrollView::Auto) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + sw->setHScrollBarMode( (scroll & 1) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + sw->setVScrollBarMode( (scroll & 2) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + } + }*/ + +END_PROPERTY + +void CWIDGET_grab(CWIDGET *_object) +{ + QEventLoop eventLoop; + QEventLoop *old; + + if (THIS->flag.grab) + return; + + THIS->flag.grab = true; + WIDGET->grabMouse(WIDGET->cursor()); + WIDGET->grabKeyboard(); + + old = MyApplication::eventLoop; + MyApplication::eventLoop = &eventLoop; + eventLoop.exec(); + MyApplication::eventLoop = old; + + WIDGET->releaseMouse(); + WIDGET->releaseKeyboard(); + THIS->flag.grab = false; + +} + +BEGIN_METHOD_VOID(Control_Grab) + + CWIDGET_grab(THIS); + +END_METHOD + + +/* Classe CWidget */ + +CWidget CWidget::manager; +QHash CWidget::dict; +bool CWidget::real; + +#if 0 +bool haveChildren; + +void CWidget::installFilter(QObject *o) +{ + QObjectList *children; + QObject *child; + + children = (QObjectList *)(o->children()); + + o->installEventFilter(&manager); + + if (!children) + return; + + child = children->first(); + while (child) + { + if (child->isWidgetType()) + { + haveChildren = true; + CWidget::installFilter(child); + } + + child = children->next(); + } +} + +void CWidget::removeFilter(QObject *o) +{ + QObjectList *children = (QObjectList *)(o->children()); + QObject *child; + + if (!o->isWidgetType()) + return; + + o->removeEventFilter(&manager); + + if (!children) + return; + + child = children->first(); + while (child) + { + CWidget::removeFilter(child); + child = children->next(); + } +} +#endif + +void CWidget::removeFocusPolicy(QWidget *w) +{ + QObjectList children; + int i; + QObject *child; + + w->clearFocus(); + w->setFocusPolicy(Qt::NoFocus); + + children = w->children(); + + for (i = 0; i < children.count(); i++) + { + child = children.at(i); + + if (child->isWidgetType()) + CWidget::removeFocusPolicy((QWidget *)child); + } +} + + +void CWidget::add(QObject *o, void *object, bool no_filter) +{ + //if (!no_filter) + QObject::connect(o, SIGNAL(destroyed()), &manager, SLOT(destroy())); + + dict.insert(o, (CWIDGET *)object); + + /* + if (!no_filter) + { + haveChildren = false; + CWidget::installFilter(o); + if (haveChildren) + CWIDGET_set_flag(object, WF_NO_EVENT); + } + */ + + GB.Ref(object); +} + +CWIDGET *CWidget::get(QObject *o) +{ + CWIDGET *ob; + + real = true; + + while (o) + { + ob = dict[o]; + if (ob) + return ob; + if (((QWidget *)o)->isWindow()) + return NULL; + + o = o->parent(); + real = false; + } + + return NULL; +} + +CWIDGET *CWidget::getRealExisting(QObject *o) +{ + CWIDGET *_object = dict[o]; + + if (THIS && THIS->flag.deleted) + _object = NULL; + + return _object; +} + + +CWIDGET *CWidget::getDesign(QObject *o) +{ + CWIDGET *ob; + + if (!o->isWidgetType()) + return NULL; + + real = true; + + while (o) + { + ob = dict[o]; + if (ob && !ob->flag.design_ignore) + return ob; + if (((QWidget *)o)->isWindow()) + return NULL; + + o = o->parent(); + real = false; + } + + return NULL; +} + +QWidget *CWidget::getContainerWidget(CCONTAINER *object) +{ + if (GB.CheckObject(object)) + GB.Propagate(); + + if (object->container == NULL) + { + GB.Error("Null container"); + GB.Propagate(); + } + + //fprintf(stderr, "container: (%p %s)\n", object, object->widget.name); + + return (object->container); +} + +CWINDOW *CWidget::getWindow(CWIDGET *ob) +{ + //QWidget *p = w->parentWidget(); + for(;;) + { + if (GB.Is(ob, CLASS_Window)) // && ((CWINDOW *)ob)->window) + break; + + ob = CWidget::get(QWIDGET(ob)->parentWidget()); + if (!ob) + break; + } + + return (CWINDOW *)ob; +} + + +CWINDOW *CWidget::getTopLevel(CWIDGET *ob) +{ + //QWidget *p = w->parentWidget(); + for(;;) + { + if (GB.Is(ob, CLASS_Window) && ((CWINDOW *)ob)->toplevel) + break; + + ob = CWidget::get(QWIDGET(ob)->parentWidget()); + if (!ob) + break; + } + + return (CWINDOW *)ob; +} + + +#if 0 +void CWidget::setName(CWIDGET *object, const char *name) +{ + QWidget *w = QWIDGET(object); + CTOPLEVEL *top = (CTOPLEVEL *)CWidget::get(w->topLevelWidget()); + + if (QWIDGET(top) == w) + return; + + if (w->name() != NULL) + { + /*qDebug("- %s", w->name());*/ + top->dict->remove(w->name()); + } + + if (name != NULL) + { + top->dict->insert((const char *)name, object); + w->setName(name); + /*qDebug("+ %s", w->name());*/ + } +} +#endif + +#define CLEAN_POINTER(_ptr) if ((_ptr) == THIS) _ptr = NULL + +void CWidget::destroy() +{ + QWidget *w = (QWidget *)sender(); + CWIDGET *_object = CWidget::get(w); + CWINDOW *win; + + if (!THIS) + return; + + /*fprintf(stderr, "CWidget::destroy: (%s %p) %s / proxy = %p / proxy_for = %p\n", GB.GetClassName(THIS), THIS, THIS->name, + THIS_EXT ? THIS_EXT->proxy : NULL, THIS_EXT ? THIS_EXT->proxy_for : NULL);*/ + + if (!_post_check_hovered) + { + CWIDGET *top = (CWIDGET *)CWidget::getTopLevel(THIS); + if (top == THIS) + top = NULL; + _post_check_hovered = true; + _post_check_hovered_window = top; + GB.Post((void (*)())post_check_hovered, (intptr_t)0); + } + + CLEAN_POINTER(_hovered); + CLEAN_POINTER(_official_hovered); + CLEAN_POINTER(_post_check_hovered_window); + CLEAN_POINTER(CWIDGET_active_control); + CLEAN_POINTER(CWIDGET_previous_control); + CLEAN_POINTER(CWIDGET_hovered); + CLEAN_POINTER(_old_active_control); +#if QT5 + CLEAN_POINTER(_last_entered); +#endif + + win = CWINDOW_Current; + while (win) + { + CLEAN_POINTER(win->save_focus); + win = win->previous; + } + + if (THIS_EXT) + { + if (THIS_EXT->proxy) + EXT(THIS_EXT->proxy)->proxy_for = NULL; + if (THIS_EXT->proxy_for) + EXT(THIS_EXT->proxy_for)->proxy = NULL; + + CACTION_register(THIS, THIS_EXT->action, NULL); + GB.FreeString(&THIS_EXT->action); + + if (THIS_EXT->container_for) + { + ((CCONTAINER *)THIS_EXT->container_for)->container = ((CWIDGET *)THIS_EXT->container_for)->widget; + THIS_EXT->container_for = NULL; + } + + GB.Unref(POINTER(&THIS_EXT->cursor)); + GB.FreeString(&THIS_EXT->popup); + GB.StoreVariant(NULL, &THIS_EXT->tag); + GB.Free(POINTER(&THIS->ext)); + } + + CWIDGET_set_name(THIS, 0); + + dict.remove(w); + + QWIDGET(THIS) = NULL; + GB.Unref(POINTER(&THIS->font)); + + //qDebug(">> CWidget::destroy %p (%p) :%p:%ld #2", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //if (!CWIDGET_test_flag(ob, WF_NODETACH)) + GB.Detach(THIS); + + GB.Unref(POINTER(&_object)); +} + +void CWidget::each(void (*func)(CWIDGET *)) +{ + QHashIterator i(dict); + while (i.hasNext()) + { + i.next(); + (*func)(i.value()); + } +} + +/*static void post_dblclick_event(void *control) +{ + GB.Raise(control, EVENT_DblClick, 0); + GB.Unref(&control); +}*/ + +static void post_focus_change(void *) +{ + CWIDGET *current, *control; + + //fprintf(stderr, "post_focus_change: %d %d\n", !_focus_change, _doing_focus_change); + + if (!_focus_change || _doing_focus_change) + return; + + _doing_focus_change = true; + + for(;;) + { + current = CWIDGET_active_control; + if (current == _old_active_control) + break; + + control = _old_active_control; + //if (control) fprintf(stderr, "check focus out %s\n", control->name); + while (control) + { + //fprintf(stderr, "post_focus_change: %s lost focus\n", control->name); + GB.Raise(control, EVENT_LostFocus, 0); + control = (CWIDGET *)(EXT(control) ? EXT(control)->proxy_for : NULL); + } + + _old_active_control = current; + CWINDOW_activate(current); + + control = current; + //if (control) fprintf(stderr, "check focus in %s\n", control->name); + while (control) + { + //fprintf(stderr, "post_focus_change: %s got focus\n", control->name); + GB.Raise(control, EVENT_GotFocus, 0); + control = (CWIDGET *)(EXT(control) ? EXT(control)->proxy_for : NULL); + } + } + + _focus_change = false; + _doing_focus_change = false; + + //fprintf(stderr, "post_focus_change: END\n"); +} + +static void handle_focus_change() +{ + if (_focus_change) + return; + + _focus_change = TRUE; + GB.Post((void (*)())post_focus_change, (intptr_t)NULL); +} + +void CWIDGET_finish_focus(void) +{ + post_focus_change(NULL); +} + +void CWIDGET_handle_focus(CWIDGET *control, bool on) +{ + if (on == (CWIDGET_active_control == control)) + return; + + //fprintf(stderr, "CWIDGET_handle_focus: %s %d / %d\n", control->name, on, _focus_change); + + if (CWIDGET_active_control && !_focus_change) + CWIDGET_previous_control = CWIDGET_active_control; + CWIDGET_active_control = on ? control : NULL; + handle_focus_change(); +} + +static bool raise_key_event_to_parent_window(CWIDGET *control, int event) +{ + for(;;) + { + if (!control || control->flag.deleted || !QWIDGET(control)) + break; + + control = (CWIDGET *)CWIDGET_get_parent(control); + + if (!control || control->flag.deleted || !QWIDGET(control)) + break; + + control = (CWIDGET *)CWidget::getWindow(control); + if (GB.Raise(control, event, 0)) + return true; + } + + return false; +} + +bool CWidget::eventFilter(QObject *widget, QEvent *event) +{ + CWIDGET *control; + int event_id; + int type = event->type(); + bool real; + bool design; + bool original; + bool cancel; + QPoint p; + void *jump; + bool parent_got_it; + double timer; + + CCONTROL_last_event_type = type; + + //if (widget->isA("MyMainWindow")) + // getDesignDebug(widget); + switch (type) + { + case QEvent::Enter: + jump = &&__ENTER; break; + case QEvent::Leave: + jump = &&__LEAVE; break; + case QEvent::FocusIn: + jump = &&__FOCUS_IN; break; + case QEvent::FocusOut: + jump = &&__FOCUS_OUT; break; + case QEvent::ContextMenu: + jump = &&__CONTEXT_MENU; break; + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonRelease: + jump = &&__MOUSE; break; + case QEvent::KeyPress: + case QEvent::KeyRelease: + jump = &&__KEY; break; + case QEvent::Shortcut: + jump = &&_DESIGN; break; + case QEvent::InputMethod: + jump = &&__INPUT_METHOD; break; + case QEvent::Wheel: + jump = &&__MOUSE_WHEEL; break; + case QEvent::DragEnter: + jump = &&__DRAG_ENTER; break; + case QEvent::DragMove: + jump = &&__DRAG_MOVE; break; + case QEvent::Drop: + jump = &&__DROP; break; + case QEvent::DragLeave: + jump = &&__DRAG_LEAVE; break; + case QEvent::DeferredDelete: + control = CWidget::getDesign(widget); + if (!control || control->flag.deleted) + { + QObject::eventFilter(widget, event); + return false; + } + else + goto _STANDARD; + case QEvent::TabletMove: + case QEvent::TabletPress: + case QEvent::TabletRelease: + jump = &&__TABLET; break; + default: + goto _STANDARD; + } + + control = CWidget::getDesign(widget); + //for(;;) + //{ + if (!control || GB.Is(control, CLASS_Menu)) + goto _STANDARD; + // if (control->widget->isEnabled()) + // break; + // control = (CWIDGET *)CWIDGET_get_parent(control); + //} + + real = CWidget::real; + design = CWIDGET_is_design(control); //CWIDGET_test_flag(control, WF_DESIGN); // && !GB.Is(control, CLASS_Container); + original = event->spontaneous(); + + goto *jump; + + __ENTER: + { +#ifndef QT5 + QWidget *popup = qApp->activePopupWidget(); +#endif + + //qDebug("enter %p %s real = %d inside = %d", widget, control->name, real, control->flag.inside); + + if (real) + { +#ifdef QT5 + CWIDGET_enter(control); +#else + if (!popup || CWidget::getReal(popup)) + CWIDGET_enter(control); + else if (_enter_leave_set) + insert_enter_leave_event(control, true); +#endif + } + + goto __NEXT; + } + + __LEAVE: + { +#ifndef QT5 + QWidget *popup = qApp->activePopupWidget(); +#endif + + //qDebug("leave %p %s real = %d inside = %d", widget, control->name, real, control->flag.inside); + + if (real) + { +#ifdef QT5 + CWIDGET_leave(control); +#else + if (!popup || CWidget::getReal(popup)) + CWIDGET_leave(control); + else if (_enter_leave_set) + insert_enter_leave_event(control, false); +#endif + } + + goto __NEXT; + } + + __FOCUS_IN: + { + CWIDGET_handle_focus(control, true); + goto __NEXT; + } + + __FOCUS_OUT: + { + CWIDGET_handle_focus(control, false); + goto __NEXT; + } + + __CONTEXT_MENU: + { + while (EXT(control) && EXT(control)->proxy_for) + control = (CWIDGET *)(EXT(control)->proxy_for); + + __MENU_TRY_PROXY: + + // if (real && GB.CanRaise(control, EVENT_Menu)) + //qDebug("Menu event! %p %d", control, EVENT_Menu); + if (GB.CanRaise(control, EVENT_Menu)) + { + int old = MENU_popup_count; + + ((QContextMenuEvent *)event)->accept(); + + if (GB.Raise(control, EVENT_Menu, 0) || MENU_popup_count != old) + return true; + //else + // goto __NEXT; + } + + if (EXT(control) && EXT(control)->popup) + { + CWINDOW *window = CWidget::getWindow(control); + CMENU *menu = CWindow::findMenu(window, EXT(control)->popup); + if (menu) + CMENU_popup(menu, QCursor::pos()); + return true; + } + + if (EXT(control) && EXT(control)->proxy) + { + control = (CWIDGET *)(EXT(control)->proxy); + goto __MENU_TRY_PROXY; + } + + goto __NEXT; + } + + __MOUSE: + { + QMouseEvent *mevent = (QMouseEvent *)event; + + if (!original) + goto _DESIGN; + + /*if (type == QEvent::MouseButtonPress) + { + qDebug("mouse event on [%s %s %p] (%s %p) %s%s%s", widget->metaObject()->className(), qPrintable(widget->objectName()), widget, + control ? GB.GetClassName(control) : "-", control, real ? "REAL " : "", design ? "DESIGN " : "", original ? "ORIGINAL ": ""); + //getDesignDebug(widget); + }*/ + + if (!real) + { + CWIDGET *cont = CWidget::get(widget); + if (cont->flag.scrollview) + { + if (qobject_cast(widget)) + goto _STANDARD; + /*if (widget != get_viewport(QWIDGET(cont))) + { + if (!widget->objectName().isNull()) + goto _STANDARD; + }*/ + } + } + + if (type == QEvent::MouseMove) + { + // Ignore spurious move events + + static int last_move_x = -1; + static int last_move_y = -1; + + if (last_move_x == mevent->globalX() && last_move_y == mevent->globalY()) + { + //fprintf(stderr, "same mouse move! %d %d\n", last_move_x, last_move_y); + goto _STANDARD; + } + + last_move_x = mevent->globalX(); + last_move_y = mevent->globalY(); + } + + if (type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick) + { + GB.GetTime(&timer, TRUE); + if (((timer - MOUSE_timer) * 1000) < QApplication::doubleClickInterval() + && abs(mevent->globalX() - MOUSE_click_x) < MAIN_scale + && abs(mevent->globalY() - MOUSE_click_y) < MAIN_scale) + MOUSE_click_count++; + else + { + MOUSE_click_x = mevent->globalX(); + MOUSE_click_y = mevent->globalY(); + MOUSE_click_count = 1; + } + MOUSE_timer = timer; + } + + //while (control->proxy_for) + // control = (CWIDGET *)control->proxy_for; + + __MOUSE_TRY_PROXY: + + if (!design && !QWIDGET(control)->isEnabled()) + goto __NEXT; + + p.setX(mevent->globalX()); + p.setY(mevent->globalY()); + p = QWIDGET(control)->mapFromGlobal(p); + + switch(type) + { + case QEvent::MouseButtonPress: + event_id = EVENT_MouseDown; + //state = mevent->buttons(); + MOUSE_info.sx = p.x(); + MOUSE_info.sy = p.y(); + CMOUSE_set_control(control); + break; + + case QEvent::MouseButtonDblClick: + event_id = EVENT_DblClick; + break; + + case QEvent::MouseButtonRelease: + event_id = EVENT_MouseUp; + CMOUSE_set_control(NULL); + break; + + default: + event_id = EVENT_MouseMove; + if (mevent->buttons() == Qt::NoButton && !has_tracking(control)) + goto _DESIGN; + } + + /* GB.Raise() can free the control, so we must reference it as we may raise two successive events now */ + GB.Ref(control); + cancel = false; + + if (GB.CanRaise(control, event_id) || (event_id == EVENT_DblClick && GB.CanRaise(control, EVENT_MouseDown))) + { + /*if (!design && CWIDGET_test_flag(control, WF_SCROLLVIEW)) + { + if (widget != ((QScrollView *)QWIDGET(control))->viewport() + && widget->name(0)) + { + qDebug("cancel"); + goto _DESIGN; + } + }*/ + + CMOUSE_clear(true); + MOUSE_info.x = p.x(); + MOUSE_info.y = p.y(); + MOUSE_info.screenX = mevent->globalX(); + MOUSE_info.screenY = mevent->globalY(); + MOUSE_info.button = mevent->button(); + MOUSE_info.state = mevent->buttons(); + MOUSE_info.modifier = mevent->modifiers(); + + if (event_id == EVENT_DblClick) + cancel = GB.Raise(control, EVENT_MouseDown, 0); //, GB_T_INTEGER, p.x(), GB_T_INTEGER, p.y(), GB_T_INTEGER, state); + + if (!cancel) + cancel = GB.Raise(control, event_id, 0); //, GB_T_INTEGER, p.x(), GB_T_INTEGER, p.y(), GB_T_INTEGER, state); + + CMOUSE_clear(false); + + /*if (CDRAG_dragging) + return true;*/ + } + + if (event_id == EVENT_MouseMove && !cancel && (mevent->buttons() != Qt::NoButton) && GB.CanRaise(control, EVENT_MouseDrag) && !CDRAG_dragging + && ((abs(p.x() - MOUSE_info.sx) + abs(p.y() - MOUSE_info.sy)) > 8)) // QApplication::startDragDistance())) + { + /*if (!design && CWIDGET_test_flag(control, WF_SCROLLVIEW)) + { + if (widget != ((QScrollView *)QWIDGET(control))->viewport() + && widget->name(0)) + { + goto _DESIGN; + } + }*/ + + CMOUSE_clear(true); + MOUSE_info.x = p.x(); + MOUSE_info.y = p.y(); + MOUSE_info.screenX = mevent->globalX(); + MOUSE_info.screenY = mevent->globalY(); + MOUSE_info.button = mevent->button(); + MOUSE_info.state = mevent->buttons(); + MOUSE_info.modifier = mevent->modifiers(); + + //qDebug("MouseDrag: %s", control->name); + cancel = GB.Raise(control, EVENT_MouseDrag, 0); + + CMOUSE_clear(false); + } + + GB.Unref(POINTER(&control)); + + if (!control) + goto __MOUSE_RETURN_TRUE; + + if (control->flag.grab && event_id == EVENT_MouseUp) + MyApplication::eventLoop->exit(); + + if (cancel) + goto __MOUSE_RETURN_TRUE; + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __MOUSE_TRY_PROXY; + } + + CMOUSE_reset_translate(); + goto __NEXT; + + __MOUSE_RETURN_TRUE: + + CMOUSE_reset_translate(); + return true; + } + + __TABLET: + { + QTabletEvent *tevent = (QTabletEvent *)event; + + if (!original) + goto _DESIGN; + + if (!real) + { + CWIDGET *cont = CWidget::get(widget); + if (cont->flag.scrollview) + { + if (qobject_cast(widget)) + goto _STANDARD; + /*if (widget != get_viewport(QWIDGET(cont))) + { + if (!widget->objectName().isNull()) + goto _STANDARD; + }*/ + } + } + + if (!control->flag.use_tablet) + goto __NEXT; + + __TABLET_TRY_PROXY: + + p.setX(tevent->globalX()); + p.setY(tevent->globalY()); + p = QWIDGET(control)->mapFromGlobal(p); + + if (type == QEvent::TabletPress) + { + //qDebug("MouseDown on %p (%s %p) %s%s", widget, control ? GB.GetClassName(control) : "-", control, real ? "REAL " : "", design ? "DESIGN " : ""); + + event_id = EVENT_MouseDown; + //state = mevent->buttons(); + + //MOUSE_info.sx = p.x(); + //MOUSE_info.sy = p.y(); + + control->flag.tablet_pressed = true; + //qDebug("MouseEvent: %d %d", mevent->x(), mevent->y()); + } + else if (type == QEvent::TabletMove) + { + //if (!control->flag.tracking && !control->flag.tablet_pressed) + // return false; + + event_id = EVENT_MouseMove; + } + else //if (type == QEvent::TabletRelease) + { + event_id = EVENT_MouseUp; + //state = mevent->buttons(); + } + + //if (event_id == EVENT_MouseMove && mevent->buttons() == Qt::NoButton && !QWIDGET(control)->hasMouseTracking()) + // goto _DESIGN; + + + cancel = false; + + if (GB.CanRaise(control, event_id)) + { + //MOUSE_info.x = p.x(); + //MOUSE_info.y = p.y(); + //POINTER_info.screenX = tevent->globalX(); + //POINTER_info.screenY = tevent->globalY(); + //MOUSE_info.modifier = tevent->modifiers(); +#ifdef QT5 + POINTER_info.tx = tevent->globalPosF().x(); + POINTER_info.ty = tevent->globalPosF().y(); +#else + POINTER_info.tx = tevent->hiResGlobalX(); + POINTER_info.ty = tevent->hiResGlobalY(); +#endif + POINTER_info.pressure = tevent->pressure(); + POINTER_info.rotation = tevent->rotation(); + POINTER_info.xtilt = tevent->xTilt(); + POINTER_info.ytilt = tevent->yTilt(); + + switch(tevent->pointerType()) + { + case QTabletEvent::Pen: POINTER_info.type = POINTER_PEN; break; + case QTabletEvent::Eraser: POINTER_info.type = POINTER_ERASER; break; + case QTabletEvent::Cursor: POINTER_info.type = POINTER_CURSOR; break; + default: POINTER_info.type = POINTER_MOUSE; + } + + //cancel = GB.Raise(control, event_id, 0); + + //CMOUSE_clear(false); + } + + //if (control->flag.grab && event_id == EVENT_MouseUp) + // MyApplication::eventLoop->exit(); + + if (event_id == EVENT_MouseUp) + control->flag.tablet_pressed = false; + + //if (cancel) + // return true; + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __TABLET_TRY_PROXY; + } + + CMOUSE_reset_translate(); + return false; // We fill the information, and then expect Qt to generate a Mouse event from the Tablet event + } + + __KEY: + { + QKeyEvent *kevent = (QKeyEvent *)event; + + if (MAIN_key_debug) + { + qDebug(QT_NAME ": %s: real = %d original = %d no_keyboard = %d", + (type == QEvent::KeyRelease ? "KeyRelease" : + (type == QEvent::KeyPress ? "KeyPress" : "?")), + real, original, control->flag.no_keyboard); + } + + #if QT_VERSION <= 0x030005 + if (!real || !original) + goto _DESIGN; + #endif + + if (control->flag.no_keyboard) + goto _DESIGN; + + event_id = (type == QEvent::KeyRelease) ? EVENT_KeyRelease : EVENT_KeyPress; + cancel = false; + parent_got_it = false; + + #if QT_VERSION > 0x030005 + if (!original && type != QEvent::InputMethod) + goto _DESIGN; //_ACCEL; + #endif + + if (type == QEvent::KeyRelease && kevent->isAutoRepeat()) + goto __NEXT; + + if (MAIN_key_debug) + { + qDebug(QT_NAME ": (%s %s) -> %d `%s' %s", + GB.GetClassName(control), control->name, + kevent->key(), (const char *)kevent->text().toLatin1(), kevent->isAutoRepeat() ? "AR" : "--"); + } + //qDebug("CWidget::eventFilter: KeyPress on %s %p", GB.GetClassName(control), control); + + __KEY_TRY_PROXY: + + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + CKEY_info.text = NEW_STRING(kevent->text()); + CKEY_info.state = kevent->modifiers(); + CKEY_info.code = kevent->key(); + CKEY_info.release = type == QEvent::KeyRelease; + + #ifndef NO_X_WINDOW + { + #ifdef QT5 + int last = PLATFORM.GetLastKeyCode(); + #else + int last = MAIN_x11_last_key_code; + #endif + if (type == QEvent::KeyPress && CKEY_info.code) + _x11_to_qt_keycode.insert(last, CKEY_info.code); + else if (type == QEvent::KeyRelease && CKEY_info.code == 0) + { + if (_x11_to_qt_keycode.contains(last)) + { + CKEY_info.code = _x11_to_qt_keycode[last]; + _x11_to_qt_keycode.remove(last); + } + } + } + #endif + + GB.Ref(control); + + if (!parent_got_it) + { + parent_got_it = true; + if (!cancel) + cancel = raise_key_event_to_parent_window(control, event_id); + } + + if (!cancel) + cancel = GB.Raise(control, event_id, 0); + + GB.Unref(POINTER(&control)); + + CKEY_clear(false); + + if ((cancel && (type != QEvent::KeyRelease)) || !control) + return true; + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __KEY_TRY_PROXY; + } + + if (control->flag.grab && event_id == EVENT_KeyPress && kevent->key() == Qt::Key_Escape) + MyApplication::eventLoop->exit(); + + goto __NEXT; + } + + __INPUT_METHOD: + { + QInputMethodEvent *imevent = (QInputMethodEvent *)event; + + if (MAIN_key_debug) + { + qDebug(QT_NAME ": InputMethod: real = %d original = %d no_keyboard = %d", + real, original, control->flag.no_keyboard); + } + + #if QT_VERSION <= 0x030005 + if (!real || !original) + goto _DESIGN; + #endif + + if (!imevent->commitString().isEmpty()) + { + if (MAIN_key_debug) + { + qDebug(QT_NAME ": (%s %s) -> `%s'", + GB.GetClassName(control), control->name, + (const char *)imevent->commitString().toUtf8()); + } + + event_id = EVENT_KeyPress; + cancel = false; + + __IM_TRY_PROXY: + + if (GB.CanRaise(control, event_id)) + { + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + //qDebug("IMEnd: %s", imevent->text().latin1()); + CKEY_info.text = NEW_STRING(imevent->commitString()); + CKEY_info.state = Qt::KeyboardModifiers(); + CKEY_info.code = 0; + + if (EXT(control) && EXT(control)->proxy_for) + cancel = GB.Raise(EXT(control)->proxy_for, event_id, 0); + if (!cancel) + cancel = GB.Raise(control, event_id, 0); + + CKEY_clear(false); + + if (cancel) + return true; + } + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __IM_TRY_PROXY; + } + } + + goto __NEXT; + } + + __MOUSE_WHEEL: + { + QWheelEvent *ev = (QWheelEvent *)event; + bool eat_wheel; + + //qDebug("Event on %p %s%s%s", widget, + // real ? "REAL " : "", design ? "DESIGN " : "", child ? "CHILD " : ""); + + if (!original) + goto _DESIGN; + + eat_wheel = control->flag.wheel; + + __MOUSE_WHEEL_TRY_PROXY: + + //fprintf(stderr, "wheel on %p %s\n", control, control->name); + + if (design || QWIDGET(control)->isEnabled()) + { + if (GB.CanRaise(control, EVENT_MouseWheel)) + { + // Automatic focus for wheel events + CWIDGET_set_focus(control); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + p.setX(ev->position().x()); + p.setY(ev->position().y()); +#else + p.setX(ev->x()); + p.setY(ev->y()); +#endif + + p = ((QWidget *)widget)->mapTo(QWIDGET(control), p); + + CMOUSE_clear(true); + MOUSE_info.x = p.x(); + MOUSE_info.y = p.y(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + MOUSE_info.screenX = ev->globalPosition().x(); + MOUSE_info.screenY = ev->globalPosition().y(); +#else + MOUSE_info.screenX = ev->globalX(); + MOUSE_info.screenY = ev->globalY(); +#endif + MOUSE_info.state = ev->buttons(); + MOUSE_info.modifier = ev->modifiers(); + +#ifdef QT5 + QPoint delta = ev->angleDelta(); + if (delta.x()) + { + MOUSE_info.orientation = Qt::Horizontal; + MOUSE_info.delta = delta.x(); + cancel = GB.Raise(control, EVENT_MouseWheel, 0); + } + if (delta.y()) + { + MOUSE_info.orientation = Qt::Vertical; + MOUSE_info.delta = delta.y(); + cancel = GB.Raise(control, EVENT_MouseWheel, 0); + } +#else + MOUSE_info.orientation = ev->orientation(); + MOUSE_info.delta = ev->delta(); + cancel = GB.Raise(control, EVENT_MouseWheel, 0); +#endif + + + CMOUSE_clear(false); + + if (cancel) + { + event->accept(); + return true; + } + } + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __MOUSE_WHEEL_TRY_PROXY; + } + } + + if (!eat_wheel) + { + control = (CWIDGET *)CWIDGET_get_parent(control); + if (control) + goto __MOUSE_WHEEL_TRY_PROXY; + } + + goto __NEXT; + } + + __DRAG_ENTER: + { + if (!control->flag.drop) + goto __NEXT; + + for(;;) + { + if (CDRAG_drag_enter((QWidget *)widget, control, (QDropEvent *)event)) + { + if (!((QDropEvent *)event)->isAccepted()) + CDRAG_hide_frame(control); + return true; + } + + if (!EXT(control) || !EXT(control)->proxy_for) + break; + + control = (CWIDGET *)(EXT(control)->proxy_for); + } + + goto __NEXT; + } + + __DRAG_MOVE: + { + if (!control->flag.drop) + goto __NEXT; + + for(;;) + { + if (GB.CanRaise(control, EVENT_DragMove)) + { + if (CDRAG_drag_move(QWIDGET(control), control, (QDropEvent *)event)) + { + if (!((QDropEvent *)event)->isAccepted()) + CDRAG_hide_frame(control); + break; + } + } + + if (!EXT(control) || !EXT(control)->proxy_for) + break; + + control = (CWIDGET *)(EXT(control)->proxy_for); + } + + if (GB.CanRaise(control, EVENT_Drop)) + return true; + + goto __NEXT; + } + + __DRAG_LEAVE: + { + if (!control->flag.drop) + goto __NEXT; + + for(;;) + { + CDRAG_drag_leave(control); + + if (!EXT(control) || !EXT(control)->proxy_for) + break; + + control = (CWIDGET *)(EXT(control)->proxy_for); + } + goto __NEXT; + } + + __DROP: + { + if (!control->flag.drop) + goto __NEXT; + + for(;;) + { + CDRAG_drag_leave(control); + if (CDRAG_drag_drop((QWidget *)widget, control, (QDropEvent *)event)) + return true; + + if (!EXT(control) || !EXT(control)->proxy_for) + break; + + control = (CWIDGET *)(EXT(control)->proxy_for); + } + + goto __NEXT; + } + + __NEXT: + + if (!control || control->flag.deleted) + { + QObject::eventFilter(widget, event); + return (type != QEvent::DeferredDelete); + } + + /*if (CWIDGET_check(control)) + { + qDebug("CWidget::eventFilter: %p was destroyed", control); + return true; + }*/ + +_DESIGN: + + if (design) + { + if ((type == QEvent::MouseButtonPress) + || (type == QEvent::MouseButtonRelease) + || (type == QEvent::MouseButtonDblClick) + || (type == QEvent::MouseMove) + || (type == QEvent::Wheel) + || (type == QEvent::ContextMenu) + || (type == QEvent::KeyPress) + || (type == QEvent::KeyRelease) + || (type == QEvent::InputMethod) + || (type == QEvent::Shortcut) + || (type == QEvent::Enter) + || (type == QEvent::Leave) + || (type == QEvent::FocusIn) + || (type == QEvent::FocusOut) + || (type == QEvent::DragEnter) + || (type == QEvent::DragMove) + || (type == QEvent::DragLeave) + || (type == QEvent::Drop) + || (type == QEvent::TabletMove) + || (type == QEvent::TabletPress) + || (type == QEvent::TabletRelease) + ) + return true; + } + +_STANDARD: + + return QObject::eventFilter(widget, event); // standard event processing +} + +/** Action *****************************************************************/ + +#define HAS_ACTION(_control) ((CWIDGET *)(_control))->flag.has_action +#define SET_ACTION(_control, _flag) (((CWIDGET *)(_control))->flag.has_action = (_flag)) + +#include "gb.form.action.h" + +#if 0 +static void gray_image(QImage &img) +{ + uchar *b(img.bits()); + uchar *g(img.bits() + 1); + uchar *r(img.bits() + 2); + + uchar * end(img.bits() + img.numBytes()); + + while (b != end) { + + *b = *g = *r = 0x80 | (((*r + *b) >> 1) + *g) >> 2; // (r + b + g) / 3 + + b += 4; + g += 4; + r += 4; + } +} +#endif + +void CWIDGET_iconset(QIcon &icon, const QPixmap &pixmap, int size) +{ + QImage img; + //QPixmap disabled; + QPixmap normal; + + if (pixmap.isNull()) + return; + + if (size > 0) + { + img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); + size = ((size + 1) & ~3); + img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + normal = QPixmap::fromImage(img); + } + else + normal = pixmap; + + icon = QIcon(normal); + + /*gray_image(img); + + disabled.convertFromImage(img); + icon.setPixmap(disabled, QIcon::Small, QIcon::Disabled);*/ +} + + +GB_DESC CControlDesc[] = +{ + GB_DECLARE("Control", sizeof(CCONTROL)), GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(CWIDGET_check), + + GB_METHOD("_new", NULL, Control_new, NULL), + GB_METHOD("_free", NULL, Control_Delete, NULL), + + GB_METHOD("Move", NULL, Control_Move, "(X)i(Y)i[(Width)i(Height)i]"), + GB_METHOD("Resize", NULL, Control_Resize, "(Width)i(Height)i"), + + GB_METHOD("MoveScaled", NULL, Control_MoveScaled, "(X)f(Y)f[(Width)f(Height)f]"), + GB_METHOD("ResizeScaled", NULL, Control_ResizeScaled, "(Width)f(Height)f"), + + GB_METHOD("Delete", NULL, Control_Delete, NULL), + GB_METHOD("Show", NULL, Control_Show, NULL), + GB_METHOD("Hide", NULL, Control_Hide, NULL), + + GB_METHOD("Raise", NULL, Control_Raise, NULL), + GB_METHOD("Lower", NULL, Control_Lower, NULL), + + GB_PROPERTY("Next", "Control", Control_Next), + GB_PROPERTY("Previous", "Control", Control_Previous), + + GB_METHOD("SetFocus", NULL, Control_SetFocus, NULL), + GB_METHOD("Refresh", NULL, Control_Refresh, NULL), + GB_METHOD("Drag", "Control", Control_Drag, "(Data)v[(Format)s]"), + GB_METHOD("Grab", NULL, Control_Grab, NULL), + + GB_METHOD("Reparent", NULL, Control_Reparent, "(Parent)Container;[(X)i(Y)i]"), + + GB_PROPERTY("X", "i", Control_X), + GB_PROPERTY("Y", "i", Control_Y), + GB_PROPERTY_READ("ScreenX", "i", Control_ScreenX), + GB_PROPERTY_READ("ScreenY", "i", Control_ScreenY), + GB_PROPERTY("W", "i", Control_Width), + GB_PROPERTY("H", "i", Control_Height), + GB_PROPERTY("Left", "i", Control_X), + GB_PROPERTY("Top", "i", Control_Y), + GB_PROPERTY("Width", "i", Control_Width), + GB_PROPERTY("Height", "i", Control_Height), + + GB_PROPERTY("Visible", "b", Control_Visible), + GB_PROPERTY("Enabled", "b", Control_Enabled), + GB_PROPERTY_READ("HasFocus", "b", Control_HasFocus), + GB_PROPERTY_READ("Hovered", "b", Control_Hovered), + + GB_PROPERTY("Expand", "b", Control_Expand), + GB_PROPERTY("Ignore", "b", Control_Ignore), + + GB_PROPERTY("Font", "Font", Control_Font), + GB_PROPERTY("Background", "i", Control_Background), + GB_PROPERTY("Foreground", "i", Control_Foreground), + + GB_PROPERTY("Design", "b", Control_Design), + GB_PROPERTY("Name", "s", Control_Name), + GB_PROPERTY("Tag", "v", Control_Tag), + GB_PROPERTY("Tracking", "b", Control_Tracking), + GB_PROPERTY("Mouse", "i", Control_Mouse), + GB_PROPERTY("Cursor", "Cursor", Control_Cursor), + GB_PROPERTY("Tooltip", "s", Control_Tooltip), + GB_PROPERTY("Drop", "b", Control_Drop), + GB_PROPERTY("Action", "s", Control_Action), + GB_PROPERTY("PopupMenu", "s", Control_PopupMenu), + GB_PROPERTY("Proxy", "Control", Control_Proxy), + GB_PROPERTY("NoTabFocus", "b", Control_NoTabFocus), + GB_PROPERTY("Direction", "i", Control_Direction), + GB_PROPERTY_READ("RightToLeft", "b", Control_RightToLeft), + + GB_PROPERTY_READ("Parent", "Container", Control_Parent), + GB_PROPERTY_READ("_Parent", "Container", Control__Parent), + GB_PROPERTY_READ("Window", "Window", Control_Window), + GB_PROPERTY_READ("Id", "i", Control_Id), + GB_PROPERTY_READ("Handle", "i", Control_Id), + + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("GotFocus", NULL, NULL, &EVENT_GotFocus), + GB_EVENT("LostFocus", NULL, NULL, &EVENT_LostFocus), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPress), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyRelease), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDrag", NULL, NULL, &EVENT_MouseDrag), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("MouseWheel", NULL, NULL, &EVENT_MouseWheel), + GB_EVENT("DblClick", NULL, NULL, &EVENT_DblClick), + GB_EVENT("Menu", NULL, NULL, &EVENT_Menu), + GB_EVENT("Drag", NULL, NULL, &EVENT_Drag), + GB_EVENT("DragMove", NULL, NULL, &EVENT_DragMove), + GB_EVENT("Drop", NULL, NULL, &EVENT_Drop), + GB_EVENT("DragLeave", NULL, NULL, &EVENT_DragLeave), + + CONTROL_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.qt4/src/CWidget.h b/gb.qt4/src/CWidget.h new file mode 100644 index 00000000..befc74ed --- /dev/null +++ b/gb.qt4/src/CWidget.h @@ -0,0 +1,337 @@ +/*************************************************************************** + + CWidget.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWIDGET_H +#define __CWIDGET_H + +#include "main.h" +#include "share/gb.form.properties.h" + +#include +#include +#include +#include +#include +#include + +typedef + struct { + GB_COLOR fg; + GB_COLOR bg; + GB_VARIANT_VALUE tag; + void *cursor; + char *popup; + void *proxy; + void *proxy_for; + char *action; + void *container_for; + char mouse; + } + CWIDGET_EXT; + +typedef + struct CWIDGET { + GB_BASE ob; + QWidget *widget; + void *ext; + struct { + unsigned deleted : 1; + unsigned scrollview : 1; // inherits QScrollView + unsigned expand : 1; + unsigned ignore : 1; + unsigned notified : 1; + unsigned visible : 1; + unsigned autoFillBackground : 1; + unsigned fillBackground : 1; + + unsigned noBackground : 1; + unsigned shown : 1; // for containers + unsigned tracking : 1; + unsigned old_tracking : 1; + unsigned grab : 1; + unsigned dragging: 1; + unsigned no_tab_focus : 1; + unsigned inside : 1; + + unsigned inside_later : 1; + unsigned use_tablet : 1; + unsigned no_keyboard : 1; + unsigned tablet_pressed : 1; + unsigned has_action : 1; + unsigned drop : 1; + unsigned resized : 1; + unsigned wheel : 1; // eat wheel events + + unsigned design : 1; + unsigned design_ignore : 1; + unsigned no_design : 1; + unsigned orientation : 1; // for scrollbars & similar widgets + unsigned inverted : 1; // if layout has been inverted + unsigned direction : 2; // text direction + } flag; + char *name; + void *font; + } + CWIDGET; // BEWARE: gb.qt.h MUST be updated accordingly! + +typedef + CWIDGET CCONTROL; + +typedef + struct { + CWIDGET widget; + QWidget *container; + unsigned mode : 4; + unsigned user : 1; + unsigned locked : 1; + unsigned margin : 1; + unsigned spacing : 1; + unsigned padding : 8; + unsigned indent : 1; + unsigned centered : 1; + unsigned dirty : 1; + unsigned autoresize : 1; + unsigned invert : 1; + unsigned paint : 1; + unsigned _reserved: 10; + } + CCONTAINER_ARRANGEMENT; + +typedef + struct { + CWIDGET widget; + QWidget *container; + int32_t arrangement; + } + CCONTAINER; + +#ifndef __CWIDGET_CPP + +extern GB_DESC CControlDesc[]; +extern CWIDGET *CWIDGET_active_control; +extern CWIDGET *CWIDGET_previous_control; +extern CWIDGET *CWIDGET_hovered; + +extern int CCONTROL_last_event_type; + +#else + +#define THIS (((CWIDGET *)_object)) +#define THIS_EXT ((CWIDGET_EXT *)((CWIDGET *)_object)->ext) +#define WIDGET QWIDGET(_object) + +#endif + +#define QWIDGET(object) (((CWIDGET *)object)->widget) +#define QCONTAINER(_ob) CWidget::getContainerWidget((CCONTAINER *)_ob) + +DECLARE_METHOD(Control_Delete); +DECLARE_METHOD(Control_Refresh); +DECLARE_PROPERTY(Control_Tag); +DECLARE_PROPERTY(CWIDGET_border_full); +DECLARE_PROPERTY(CWIDGET_border_simple); +DECLARE_PROPERTY(CWIDGET_scrollbar); +DECLARE_PROPERTY(Control_Background); +DECLARE_PROPERTY(Control_Design); +DECLARE_PROPERTY(Control_Enabled); +DECLARE_PROPERTY(Control_Font); +DECLARE_PROPERTY(Control_Action); +DECLARE_PROPERTY(Control_Mouse); + +#define RAISE_EVENT(_event) \ +{ \ + GET_SENDER(); \ +\ + if (_object == NULL) \ + return; \ +\ + GB.Raise(_object, _event, 0); \ +} + +#define RAISE_EVENT_ACTION(_event) \ +{ \ + GET_SENDER(); \ +\ + if (_object == NULL) \ + return; \ +\ + CWIDGET_raise_event_action(_object, _event); \ +} + + +/*#define ALIGN_MASK (Qt::AlignLeft | Qt::AlignRight | Qt::AlignTop | Qt::AlignBottom | Qt::AlignCenter) +#define ALIGN_HMASK (Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter) +#define ALIGN_VMASK (Qt::AlignTop | Qt::Bottom | Qt::AlignVCenter)*/ + +#define ALIGN_HMASK (Qt::AlignHorizontal_Mask) +#define ALIGN_VMASK (Qt::AlignVertical_Mask) +#define ALIGN_MASK (ALIGN_HMASK | ALIGN_VMASK) + +#define SET_WIDGET(_ob, _wid) (((CWIDGET *)_ob)->widget = (_wid)) +#define CLEAR_WIDGET(_ob) SET_WIDGET(_ob, 0) + +#define EMBED_WAIT 0 +#define EMBED_OK 1 +#define EMBED_ERROR 2 + +void CWIDGET_new(QWidget *w, void *_object, bool no_show = false, bool no_filter = false, bool no_tag = false); +void CWIDGET_init_name(CWIDGET *_object); +void CWIDGET_set_name(CWIDGET *_object, const char *name); +#define CWIDGET_get_name(_object) (((CWIDGET *)_object)->name) +int CWIDGET_check(void *object); + +QString CWIDGET_Utf8ToQString(GB_STRING *str); + +void CWIDGET_destroy(CWIDGET *_object); + +void CWIDGET_iconset(QIcon &icon, const QPixmap &p, int size = 0); + +void CWIDGET_set_color(CWIDGET *_object, int bg, int fg, bool handle_proxy = false); +void CWIDGET_reset_color(CWIDGET *_object); +QT_COLOR_FUNC CWIDGET_after_set_color(QT_COLOR_FUNC func); +GB_COLOR CWIDGET_get_background(CWIDGET *_object, bool handle_proxy = false); +GB_COLOR CWIDGET_get_foreground(CWIDGET *_object, bool handle_proxy = false); +GB_COLOR CWIDGET_get_real_background(CWIDGET *_object); +GB_COLOR CWIDGET_get_real_foreground(CWIDGET *_object); + +void *CWIDGET_get_real_font(CWIDGET *_object); + +void *CWIDGET_get_parent(void *_object); +int CWIDGET_get_handle(void *_object); +bool CWIDGET_is_visible(void *_object); +void CWIDGET_set_visible(CWIDGET *_object, bool v); + +void CWIDGET_grab(CWIDGET *_object); + +void CWIDGET_move(void *_object, int x, int y); +void CWIDGET_resize(void *_object, int w, int h); +void CWIDGET_move_resize(void *_object, int x, int y, int w, int h); +void CWIDGET_auto_resize(void *_object, int w, int h); + +void CWIDGET_handle_focus(CWIDGET *control, bool on); +void CWIDGET_finish_focus(void); + +void CWIDGET_register_proxy(void *_object, void *proxy); +bool CWIDGET_container_for(void *_object, void *container_for); + +#ifdef QT5 +#define CWIDGET_enter_popup() ((void *)NULL) +void CWIDGET_leave_popup(void *); +#else +void *CWIDGET_enter_popup(); +void CWIDGET_leave_popup(void *save); +#endif + +void CACTION_register(void *control, const char *old, const char *key); +void CACTION_raise(void *control); +void CWIDGET_raise_event_action(void *control, int event); + +bool CWIDGET_get_allow_focus(void *_object); +void CWIDGET_set_allow_focus(void *_object, bool f); + +bool CWIDGET_is_design(void *_object); + +void CWIDGET_check_visibility(CWIDGET *_object); +void CWIDGET_check_hovered(); + +void CWIDGET_set_design(CWIDGET *_object, bool ignore = false); + +void CWIDGET_set_inverted(void *_object, bool v); + +void *CWIDGET_get_next_previous(void *_object, bool next); +void CWIDGET_set_focus(void *_object); +void *CWIDGET_get_next_focus(void *_object); +void *CWIDGET_get_previous_focus(void *_object); +bool CWIDGET_has_no_tab_focus(void *_object); + +#ifndef DO_NOT_DECLARE_EVENTS +#ifndef __CWIDGET_CPP +extern +#endif +int +EVENT_MouseDown, +EVENT_MouseUp, +EVENT_MouseMove, +EVENT_MouseDrag, +EVENT_MouseWheel, +EVENT_DblClick, +EVENT_KeyPress, +EVENT_KeyRelease, +EVENT_Enter, +EVENT_Leave, +EVENT_GotFocus, +EVENT_LostFocus, +EVENT_Menu, +EVENT_Drag, +EVENT_DragMove, +EVENT_Drop, +EVENT_DragLeave; +#endif + +struct CWINDOW; + +class CWidget : public QObject +{ + Q_OBJECT + +public: + + static CWidget manager; + + static void add(QObject *, void *, bool no_filter); + static CWIDGET *get(QObject *); + static CWIDGET *getReal(QObject *o) { return dict[o]; } + static CWIDGET *getRealExisting(QObject *); + static CWIDGET *getDesign(QObject *); + + static QWidget *getContainerWidget(CCONTAINER *object); + + static CWINDOW *getWindow(CWIDGET *object); + static CWINDOW *getTopLevel(CWIDGET *object); + + static void each(void (*func)(CWIDGET *)); + + //static void setName(CWIDGET *, const char *); + //static void installFilter(QObject *); + //static void removeFilter(QObject *); + + //static const char *getProperties(const void *klass); + //static void setProperties(const void *klass, const char *prop); + + static void removeFocusPolicy(QWidget *); + +public slots: + + void destroy(void); + +protected: + + bool eventFilter(QObject *, QEvent *); + +private: + + static bool real; + static QHash dict; +}; + +#endif diff --git a/gb.qt4/src/CWindow.cpp b/gb.qt4/src/CWindow.cpp new file mode 100644 index 00000000..21fb849e --- /dev/null +++ b/gb.qt4/src/CWindow.cpp @@ -0,0 +1,3192 @@ +/*************************************************************************** + + CWindow.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWINDOW_CPP + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +//Added by qt3to4: +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +#ifndef NO_X_WINDOW +#ifndef QT5 +#include +#include +#include +#endif +#endif + +#ifdef QT5 +#include +#endif + +#include "gambas.h" + +#include "CWidget.h" +#include "CMouse.h" +#include "CMenu.h" +#include "CKey.h" +#include "CDraw.h" +#include "CWindow.h" + +#ifndef NO_X_WINDOW + #ifndef QT5 +#include "x11.h" +#undef FontChange + #endif +#endif + +#ifdef NO_X_WINDOW +enum +{ + _NET_WM_WINDOW_TYPE_NORMAL, + _NET_WM_WINDOW_TYPE_DESKTOP, + _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_MENU, + _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DND +}; +#endif + +//#define DEBUG_WINDOW 1 + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Deactivate); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +DECLARE_EVENT(EVENT_Title); +DECLARE_EVENT(EVENT_Icon); +DECLARE_EVENT(EVENT_Font); +DECLARE_EVENT(EVENT_State); + +DECLARE_METHOD(Window_Show); + +CWINDOW *CWINDOW_Main = 0; +int CWINDOW_MainDesktop = -1; +CWINDOW *CWINDOW_Current = 0; +CWINDOW *CWINDOW_LastActive = 0; +CWINDOW *CWINDOW_Active = 0; + +#ifndef QT5 +int CWINDOW_Embedder = 0; +bool CWINDOW_Embedded = false; +static int CWINDOW_EmbedState = 0; +#endif + +// Fix a QT little boring visual bug on menubars +#if 0 +void CWINDOW_fix_menubar(CWINDOW *window) +{ + if (window && window->menuBar) + { + QWidget *save = qApp->focusWidget(); + window->menuBar->setFocus(); + QKeyEvent e(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); + qApp->sendEvent(window->menuBar, &e); + if (save) + save->setFocus(); + } +} +#endif + +/*---- Utility routines --------------------------------------------------------------*/ + +static void clear_mask(CWINDOW *_object) +{ + WINDOW->clearMask(); + + if (THIS->toplevel) + { + #ifndef NO_X_WINDOW + bool v = !WINDOW->isHidden() && WINDOW->isVisible(); + //WINDOW->setBorder(WINDOW->hasBorder(), true); + //WINDOW->setResizable(WINDOW->isResizable(), true); + if (v && THIS->reallyMasked) + { + #ifdef QT5 + PLATFORM.Window.Remap(WINDOW); + #else + X11_window_remap(WINDOW->effectiveWinId()); + #endif + WINDOW->initProperties(PROP_ALL); + } + #endif + } +} + +void CWINDOW_define_mask(CWINDOW *_object) +{ + QPixmap background; + QColor c; + QPalette palette; + + //qDebug("define_mask: (%s %p) picture = %p masked = %d", GB.GetClassName(THIS), THIS, THIS->picture, THIS->masked); + + //if (THIS->embedded) + // return; + + if (THIS->picture) + background = *(THIS->picture->pixmap); + + if (background.isNull()) + { + clear_mask(THIS); + THIS->reallyMasked = false; + THIS->container->setPixmap(0); + //THIS->container->setPalette(WINDOW->palette()); + } + else + { + if (THIS->masked && background.hasAlpha()) + { + THIS->reallyMasked = true; + WINDOW->setBetterMask(background); + } + else + { + clear_mask(THIS); + THIS->reallyMasked = false; + } + + THIS->container->setPixmap(THIS->picture->pixmap); + } + + THIS->container->update(); +} + +static bool emit_open_event(void *_object) +{ + //if (THIS->opening) + // return true; + + if (THIS->opened) + return false; + + THIS->closed = false; + THIS->opened = true; + + #if DEBUG_WINDOW + qDebug("emit_open_event: %s %p", GB.GetClassName(THIS), THIS); + #endif + //THIS->opening = true; + //WINDOW->configure(); + GB.Raise(THIS, EVENT_Open, 0); + //THIS->opening = false; + if (THIS->closed) + { + #if DEBUG_WINDOW + qDebug("emit_open_event: %s %p [CANCELED]", GB.GetClassName(THIS), THIS); + #endif + THIS->opened = false; + return true; + } + + THIS->opened = true; + THIS->hidden = false; + return false; +} + +static void handle_focus(CWINDOW *_object) +{ + if (THIS->focus) + { + //qDebug("handle_focus on %s", THIS->focus->name); + CWIDGET_set_focus(THIS->focus); + GB.Unref(POINTER(&THIS->focus)); + THIS->focus = NULL; + } +} + +static void raise_resize_event(void *_object) +{ + if (WINDOW->width() != THIS->last_resize_w || WINDOW->height() != THIS->last_resize_h) + { + THIS->last_resize_w = WINDOW->width(); + THIS->last_resize_h = WINDOW->height(); + GB.Raise(THIS, EVENT_Resize, 0); + } +} + +static void post_show_event(void *_object) +{ + GB.Raise(THIS, EVENT_Move, 0); + raise_resize_event(THIS); + handle_focus(THIS); +} + +static void reparent_window(CWINDOW *_object, void *parent, bool move, int x = 0, int y = 0) +{ + QPoint p; + QWidget *newParentWidget; + bool moved = THIS->moved; + + if (move) + { + p.setX(x); + p.setY(y); + moved = true; + } + else if (THIS->toplevel) + { + p.setX(THIS->x); + p.setY(THIS->y); + } + else + p = WIDGET->pos(); + + if (!parent) + newParentWidget = 0; + else + { + if (GB.CheckObject(parent)) + return; + newParentWidget = QCONTAINER(parent); + } + + if (newParentWidget != WINDOW->parentWidget()) + { + //qDebug("reparent_window: -> %s %p", parent ? ((CWIDGET *)parent)->name : "", parent); + WINDOW->doReparent(newParentWidget, p); + } + else + CWIDGET_move(THIS, p.x(), p.y()); + + THIS->moved = moved; +} + +void CWINDOW_ensure_active_window() +{ + void *_object = CWINDOW_Active; + + if (THIS) + WINDOW->activate(); +} + + +static void show_later(CWINDOW *_object) +{ + /* If the user has explicitely hidden the window since the posting of this routines + then do nothing + */ + + //qDebug("show_later %s %p: hidden = %d", GB.GetClassName(THIS), THIS, THIS->hidden); + if (!THIS->hidden && WIDGET) + { + if (!emit_open_event(THIS)) + CWIDGET_set_visible((CWIDGET *)THIS, true); + } + GB.Unref(POINTER(&_object)); +} + + +void CWINDOW_move_resize(void *_object, int x, int y, int w, int h) +{ + bool move, resize; + + move = x != THIS->x || y != THIS->y || !THIS->moved; + + if (w < 0) + w = THIS->w; + + if (h < 0) + h = THIS->h; + + resize = w != THIS->w || h != THIS->h || !THIS->resized; + + if (!move && !resize) + return; + + THIS->x = x; + THIS->y = y; + THIS->w = w; + THIS->h = h; + + if (!THIS->moved && (x || y)) + THIS->moved = TRUE; + + if (move) + WINDOW->move(x, y); + + if (resize) + { + bool resizable = false; + + if (WINDOW->isTopLevel() && !WINDOW->isResizable()) + { + resizable = true; + WINDOW->setResizable(true); + } + + WINDOW->resize(w, h); + + THIS->resized = TRUE; + if (THIS->default_minw <= 0 && THIS->default_minh <= 0) + { + THIS->default_minw = w; + THIS->default_minh = h; + } + + if (resizable) + WINDOW->setResizable(false); + + WINDOW->configure(); + } +} + +//-- Window --------------------------------------------------------------- + +BEGIN_METHOD(Window_new, GB_OBJECT parent) + + MyMainWindow *win = 0; + MyContainer *container; +#ifndef NO_X_WINDOW +#ifndef QT5 + QX11EmbedWidget *client = 0; +#endif +#endif + const char *name = GB.GetClassName(THIS); + + //THIS->widget.flag.fillBackground = true; + + if (MISSING(parent) || !VARG(parent)) + { +#ifndef NO_X_WINDOW +#ifndef QT5 + if (CWINDOW_Embedder && !CWINDOW_Embedded) + { + client = new QX11EmbedWidget; + + win = new MyMainWindow(client, name, true); + container = new MyContainer(win); + container->raise(); + THIS->embedded = true; + THIS->toplevel = false; + THIS->xembed = true; + } + else +#endif +#endif + { + //win = new MyMainWindow(CWINDOW_Main ? (MyMainWindow *)QWIDGET(CWINDOW_Main) : 0, name); + win = new MyMainWindow(0, name); + container = new MyContainer(win); + container->raise(); + THIS->embedded = false; + THIS->toplevel = true; + THIS->xembed = false; + } + } + else + { + if (GB.Conv((GB_VALUE *)(void *)ARG(parent), (GB_TYPE)CLASS_Container)) + return; + + //frame = new MyEmbeddedWindow(QCONTAINER(VARG(parent))); + //frame->setName(name); + //container = frame; + //THIS->embedded = true; + //THIS->toplevel = false; + //container->installEventFilter(&CWindow::manager); + //CWIDGET_new(frame, (void *)_object, NULL); + + win = new MyMainWindow(QCONTAINER(VARG(parent)), name, true); + container = new MyContainer(win); + container->raise(); + THIS->embedded = true; + THIS->toplevel = false; + THIS->xembed = false; + } + /*else + { + GB.Error("The parent of a Window must be a Container or a Workspace"); + return; + }*/ + + THIS->container = container; + CWIDGET_new(win, (CWIDGET *)THIS, true); + THIS->widget.flag.resized = TRUE; + + if (win) + { + win->_object = THIS; + win->installEventFilter(&CWindow::manager); + } + + if (THIS->toplevel || THIS->xembed) + { + CWindow::insertTopLevel(THIS); + + /*if (CWINDOW_Main == 0) + { + #if DEBUG_WINDOW + qDebug("CWINDOW_Main -> %p", THIS); + #endif + CWINDOW_Main = THIS; + }*/ + } + +#ifndef NO_X_WINDOW +#ifndef QT5 + if (THIS->xembed) + { + CWINDOW_Embedded = true; + + QObject::connect(XEMBED, SIGNAL(embedded()), &CWindow::manager, SLOT(embedded())); + QObject::connect(XEMBED, SIGNAL(containerClosed()), &CWindow::manager, SLOT(closed())); + QObject::connect(XEMBED, SIGNAL(error(int)), &CWindow::manager, SLOT(error())); + + //qDebug("XEMBED: EmbedInto %ld", CWINDOW_Embedder); + XEMBED->embedInto(CWINDOW_Embedder); + //qDebug("XEMBED: show"); + //XEMBED->show(); + //define_mask(THIS); + + for(;;) + { + MAIN_process_events(); + if (CWINDOW_EmbedState) + break; + usleep(10000); + } + + //qDebug("XEMBED: EmbedState: %d", CWINDOW_EmbedState); + + if (CWINDOW_EmbedState == EMBED_ERROR) + { + CWINDOW_Embedded = false; + CWINDOW_Embedder = 0; + GB.Error("Embedding has failed"); + } + } +#endif +#endif + + #if 1 + if (THIS->embedded && !THIS->xembed) + { + /* ### This can call post_show_event() directly, whereas the function is not terminated */ + #if DEBUG_WINDOW + qDebug("post show_later %s %p", GB.GetClassName(THIS), THIS); + #endif + GB.Ref(THIS); + //show_later(THIS); + GB.Post((void (*)())show_later, (intptr_t)THIS); + //WIDGET->show(); + } + //else + // THIS->hidden = TRUE; + #endif + + THIS->showMenuBar = true; + +END_METHOD + + +BEGIN_METHOD_VOID(CFORM_new) + + if (!GB.Parent(_object)) + GB.Attach(_object, _object, "Form"); + + CWIDGET_set_name((CWIDGET *)THIS, GB.GetClassName((void *)THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Form_Main) + + CWINDOW *form = (CWINDOW *)GB.AutoCreate(GB.GetClass(NULL), 0); + + if (!form->hidden) + Window_Show(form, NULL); + +END_METHOD + + +BEGIN_METHOD(Form_Load, GB_OBJECT parent) + + //qDebug("Form_Load"); + reparent_window((CWINDOW *)GB.AutoCreate(GB.GetClass(NULL), 0), VARGOPT(parent, 0), false); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_free) + + //qDebug("Window_free"); + + GB.StoreObject(NULL, POINTER(&(THIS->icon))); + GB.StoreObject(NULL, POINTER(&(THIS->picture))); + GB.Unref(POINTER(&THIS->focus)); + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_next) + + int index = ENUM(int); + + if (index >= CWindow::list.count()) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(CWindow::list.at(index)); + ENUM(int) = index + 1; + +END_METHOD + + +BEGIN_PROPERTY(CWINDOW_count) + + GB.ReturnInteger(CWindow::list.count()); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_get_from_id, GB_INTEGER id) + + QWidget *wid = QWidget::find(VARG(id)); + + //qDebug("id = %d wid = %p", PARAM(id), wid); + + if (wid != 0 && wid->isWindow()) + { + //qDebug("-> %p", CWidget::getReal(wid)); + GB.ReturnObject(CWidget::getReal(wid)); + } + else + { + //qDebug("-> %p", 0); + GB.ReturnNull(); + } + +END_METHOD + + +static bool do_close(CWINDOW *_object, int ret, bool destroyed = false) +{ + bool closed; + + #if DEBUG_WINDOW + qDebug("do_close: (%s %p) %d %d", GB.GetClassName(THIS), THIS, THIS->closing, THIS->closed); + #endif + + if (THIS->closing || THIS->closed) // || WIDGET->isHidden()) + return false; + + if (!THIS->toplevel) + { + //qDebug("Close event: %s %p opened = %d", GB.GetClassName(THIS), THIS, THIS->opened); + if (THIS->opened) + { + THIS->closing = true; + closed = !GB.Raise(THIS, EVENT_Close, 0); + THIS->closing = false; + } + else + closed = true; + + if (destroyed || closed) + { + THIS->closed = true; + THIS->opened = false; + } + + if (closed) + { + WIDGET->hide(); + if (!THIS->persistent) + CWIDGET_destroy((CWIDGET *)THIS); + } + } + else + { + if (!THIS->opened) + { + #if DEBUG_WINDOW + qDebug("send close event"); + #endif + QCloseEvent e; + QApplication::sendEvent(WINDOW, &e); + closed = e.isAccepted(); + } + else + { + #if DEBUG_WINDOW + qDebug("call WINDOW->close()"); + #endif + closed = WINDOW->close(); + } + #if DEBUG_WINDOW + qDebug("--> closed = %d", closed); + #endif + } + + if (closed) + THIS->ret = ret; + + return (!closed); +} + +BEGIN_METHOD(Window_Close, GB_INTEGER ret) + + int ret = VARGOPT(ret, 0); + + GB.ReturnBoolean(do_close(THIS, ret)); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_Raise) + + if (!THIS->toplevel) + { + if (!WIDGET->isVisible()) + CWIDGET_set_visible((CWIDGET *)THIS, true); + WIDGET->raise(); + } + else + { + if (!WINDOW->isVisible()) + WINDOW->showActivate(); + else + WINDOW->raise(); + } + +END_METHOD + +static bool check_opened(CWINDOW *_object, bool modal) +{ + if (THIS->toplevel && THIS->opened) + { + if (modal || THIS->modal) + { + GB.Error("Window is already opened"); + return TRUE; + } + } + + return FALSE; +} + +BEGIN_METHOD_VOID(Window_Show) + + if (check_opened(THIS, FALSE)) + return; + + if (emit_open_event(THIS)) + return; + + if (!THIS->toplevel) + { + CWIDGET_set_visible((CWIDGET *)THIS, true); + CWIDGET_check_visibility((CWIDGET *)THIS); +#ifndef NO_X_WINDOW +#ifndef QT5 + if (THIS->xembed) + XEMBED->show(); +#endif +#endif + post_show_event(THIS); + } + else + { + WINDOW->showActivate(); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Window_Hide) + + THIS->hidden = true; + + if (THIS->toplevel && THIS->modal) + { + do_close(THIS, 0); + //THIS->widget.flag.visible = false; + } + else + CWIDGET_set_visible((CWIDGET *)THIS, false); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_ShowModal) + + if (check_opened(THIS, TRUE)) + return; + + if (!THIS->toplevel) + { + GB.Error("The window is not top-level"); + return; + } + + THIS->ret = 0; + THIS->modal = TRUE; + + if (!emit_open_event(THIS)) + WINDOW->showModal(); + + THIS->modal = FALSE; + + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_METHOD(Window_ShowPopup, GB_INTEGER x; GB_INTEGER y) + + QPoint pos; + + if (check_opened(THIS, TRUE)) + return; + + if (MISSING(x) || MISSING(y)) + pos = QCursor::pos(); + else + pos = QPoint(VARG(x), VARG(y)); + + THIS->ret = 0; + + if (THIS->toplevel) + { + THIS->modal = THIS->popup = TRUE; + if (!emit_open_event(THIS)) + WINDOW->showPopup(pos); + THIS->modal = THIS->popup = FALSE; + } + + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_PROPERTY(Window_Modal) + + if (THIS->toplevel) + GB.ReturnBoolean(THIS->modal); + else + GB.ReturnBoolean(false); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_TopLevel) + + GB.ReturnBoolean(THIS->toplevel); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Persistent) + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(true); + } + else + { + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->persistent); + else + THIS->persistent = VPROP(GB_BOOLEAN); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->windowTitle()); + else + { + QString s = QSTRING_PROP(); + THIS->title = s.length() > 0; + WIDGET->setWindowTitle(s); + GB.Raise(THIS, EVENT_Title, 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Border) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->hasBorder()); + else + WINDOW->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isResizable()); + else + WINDOW->setResizable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Icon) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->icon); + else + { + SET_PIXMAP(WIDGET->setWindowIcon, &(THIS->icon), PROP(GB_OBJECT)); + GB.Raise(THIS, EVENT_Icon, 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + CPICTURE *new_pict = (CPICTURE *)VPROP(GB_OBJECT); + + if (new_pict != THIS->picture) + { + CPICTURE *old = THIS->picture; + GB.Ref(new_pict); + THIS->picture = new_pict; + //CWINDOW_define_mask(THIS); + CWIDGET_reset_color((CWIDGET *)THIS); + GB.Unref(POINTER(&old)); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Mask) + + /*if (THIS->embedded) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + } + else*/ + { + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->masked); + else + { + bool new_masked = VPROP(GB_BOOLEAN); + + if (new_masked != THIS->masked) + { + THIS->masked = new_masked; + //CWINDOW_define_mask(THIS); + CWIDGET_reset_color((CWIDGET *)THIS); + } + } + } + +END_PROPERTY + + +static void manage_window_state(void *_object, void *_param, Qt::WindowState state) +{ + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + } + else + { + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->getState() & state); + else + { + if (VPROP(GB_BOOLEAN)) + WINDOW->setState(WINDOW->getState() | state); + else + WINDOW->setState(WINDOW->getState() & ~state); + } + } +} + +BEGIN_PROPERTY(Window_Minimized) + + manage_window_state(_object, _param, Qt::WindowMinimized); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Maximized) + + manage_window_state(_object, _param, Qt::WindowMaximized); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_FullScreen) + + manage_window_state(_object, _param, Qt::WindowFullScreen); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Stacking) + + int p; + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnInteger(0); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->stacking); + } + else + { + p = VPROP(GB_INTEGER); + if (p >= 0 && p <= 2) + { + THIS->stacking = p; + WINDOW->initProperties(PROP_STACKING); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_TopOnly) + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->stacking == 1); + } + else + { + THIS->stacking = VPROP(GB_BOOLEAN) ? 1 : 0; + WINDOW->initProperties(PROP_STACKING); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_SkipTaskbar) + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->skipTaskbar); + } + else + { + THIS->skipTaskbar = VPROP(GB_BOOLEAN); + WINDOW->initProperties(PROP_SKIP_TASKBAR); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Sticky) + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + return; + } + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->sticky); + else + { + THIS->sticky = VPROP(GB_BOOLEAN); + WINDOW->initProperties(PROP_STICKY); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Utility) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isUtility()); //WINDOW->getType() == _NET_WM_WINDOW_TYPE_UTILITY); + else + { + //WINDOW->setType(VPROP(GB_BOOLEAN) ? _NET_WM_WINDOW_TYPE_UTILITY : _NET_WM_WINDOW_TYPE_NORMAL); + WINDOW->setUtility(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Window_Center) + + if (!THIS->toplevel) + return; + + WINDOW->center(); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_Delete) + + //qDebug("Window_Delete %p", THIS); + + do_close(THIS, 0); + + if (THIS->toplevel) + THIS->persistent = false; + + CWIDGET_destroy((CWIDGET *)THIS); + +END_METHOD + + +BEGIN_PROPERTY(Window_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WINDOW->isHidden()); + else + { + bool show = !!VPROP(GB_BOOLEAN); + + THIS->hidden = !show; + + if (show == WINDOW->isHidden()) + { + if (show) + Window_Show(_object, _param); + else + Window_Hide(_object, _param); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Controls_Count) + + QList list = WINDOW->findChildren(); + int i; + int n = 0; + CWIDGET *control; + + for (i = 0; i < list.count(); i++) + { + control = CWidget::getReal(list.at(i)); + if (control && !CWIDGET_check(control)) + n++; + } + + GB.ReturnInteger(n); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Window_Controls_next) + + QList children = WINDOW->findChildren(); + CWIDGET *control; + int index; + + index = ENUM(int); + + control = NULL; + + do + { + if (index >= children.count()) + { + GB.StopEnum(); + return; + } + + control = CWidget::getReal(children.at(index)); + index++; + } + while (!control || CWIDGET_check(control)); + + ENUM(int) = index; + GB.ReturnObject(control); + +END_PROPERTY + + +BEGIN_METHOD(Window_Reparent, GB_OBJECT container; GB_INTEGER x; GB_INTEGER y) + + //qDebug("Window_Reparent"); + reparent_window(THIS, VARG(container), !MISSING(x) && !MISSING(y), VARG(x), VARG(y)); + +END_METHOD + + +BEGIN_METHOD(Window_Controls_get, GB_STRING name) + + CWIDGET *control = WINDOW->names[GB.ToZeroString(ARG(name))]; + + if (!control || CWIDGET_check(control)) + GB.ReturnNull(); + else + GB.ReturnObject(control); + +END_METHOD + + +BEGIN_PROPERTY(Window_Closed) + + GB.ReturnBoolean(!THIS->opened); + +END_PROPERTY + +/***************************************************************************/ + +BEGIN_PROPERTY(CWINDOW_menu_count) + + if (THIS->menuBar) + GB.ReturnInteger(THIS->menuBar->actions().count()); + else + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CWINDOW_menu_next) + + int index; + + if (!THIS->menuBar) + { + GB.StopEnum(); + return; + } + + index = ENUM(int); + + if (index >= THIS->menuBar->actions().count()) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menuBar->actions().at(index)]); + + ENUM(int) = index + 1; + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_menu_get, GB_INTEGER index) + + int index = VARG(index); + + if (!THIS->menuBar || index < 0 || index >= THIS->menuBar->actions().count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menuBar->actions().at(index)]); + +END_PROPERTY + + +static bool are_menus_visible(void *_object) +{ + return !THIS->hideMenuBar && THIS->showMenuBar; +} + + +static void set_menus_visible(void *_object, bool v) +{ + THIS->showMenuBar = v; + WINDOW->configure(); +} + +BEGIN_PROPERTY(CWINDOW_menu_visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(are_menus_visible(THIS)); + else + set_menus_visible(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_menu_show) + + set_menus_visible(THIS, true); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_menu_hide) + + set_menus_visible(THIS, false); + +END_METHOD + +BEGIN_PROPERTY(Window_Opacity) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->windowOpacity() * 100); + else + { + double opacity = VPROP(GB_INTEGER) / 100.0; + + if (opacity < 0.0) + opacity = 0.0; + else if (opacity > 1.0) + opacity = 1.0; + + WIDGET->setWindowOpacity(opacity); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Screen) + + GB.ReturnInteger(QApplication::desktop()->screenNumber(WIDGET)); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Transparent) + + bool trans = WINDOW->testAttribute(Qt::WA_TranslucentBackground); + + if (READ_PROPERTY) + GB.ReturnBoolean(trans); + else + { + bool new_trans = VPROP(GB_BOOLEAN); + if (trans == new_trans) + return; + + if (!new_trans) + { + GB.Error("Transparent property cannot be reset"); + return; + } + + WINDOW->setAttribute(Qt::WA_TranslucentBackground, true); + THIS->container->setPaintBackgroundColor(true); + THIS->widget.flag.noBackground = true; + CWIDGET_reset_color((CWIDGET *)THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_TakeFocus) + + if (READ_PROPERTY) + GB.ReturnBoolean(!THIS->noTakeFocus); + else + THIS->noTakeFocus = !VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_METHOD_VOID(Window_Activate) + + if (THIS->toplevel && WINDOW->isVisible() && !WINDOW->isHidden()) + WINDOW->activate(); + +END_METHOD + +BEGIN_PROPERTY(Window_MinWidth) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->minw); + else + { + THIS->minw = Max(0, VPROP(GB_INTEGER)); + WINDOW->setGeometryHints(); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_MinHeight) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->minh); + else + { + THIS->minh = Max(0, VPROP(GB_INTEGER)); + WINDOW->setGeometryHints(); + } + +END_PROPERTY + + +/***************************************************************************/ + +GB_DESC CWindowMenusDesc[] = +{ + GB_DECLARE(".Window.Menus", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", CWINDOW_menu_next, NULL), + GB_METHOD("_get", "Menu", CWINDOW_menu_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", CWINDOW_menu_count), + GB_METHOD("Show", NULL, CWINDOW_menu_show, NULL), + GB_METHOD("Hide", NULL, CWINDOW_menu_hide, NULL), + GB_PROPERTY("Visible", "b", CWINDOW_menu_visible), + + GB_END_DECLARE +}; + +GB_DESC CWindowControlsDesc[] = +{ + GB_DECLARE(".Window.Controls", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Control", Window_Controls_next, NULL), + GB_METHOD("_get", "Control", Window_Controls_get, "(Name)s"), + GB_PROPERTY_READ("Count", "i", Window_Controls_Count), + + GB_END_DECLARE +}; + +#if 0 +GB_DESC CWindowTypeDesc[] = +{ + GB_DECLARE("WindowType", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Normal", "i", _NET_WM_WINDOW_TYPE_NORMAL), + GB_CONSTANT("Dock", "i", _NET_WM_WINDOW_TYPE_DOCK), + GB_CONSTANT("Toolbar", "i", _NET_WM_WINDOW_TYPE_TOOLBAR), + GB_CONSTANT("Menu", "i", _NET_WM_WINDOW_TYPE_MENU), + GB_CONSTANT("Utility", "i", _NET_WM_WINDOW_TYPE_UTILITY), + GB_CONSTANT("Splash", "i", _NET_WM_WINDOW_TYPE_SPLASH), + GB_CONSTANT("Dialog", "i", _NET_WM_WINDOW_TYPE_DIALOG), + GB_CONSTANT("DropDownMenu", "i", _NET_WM_WINDOW_TYPE_DROPDOWN_MENU), + GB_CONSTANT("PopupMenu", "i", _NET_WM_WINDOW_TYPE_POPUP_MENU), + GB_CONSTANT("Tooltip", "i", _NET_WM_WINDOW_TYPE_TOOLTIP), + GB_CONSTANT("Notification", "i", _NET_WM_WINDOW_TYPE_NOTIFICATION), + GB_CONSTANT("Combo", "i", _NET_WM_WINDOW_TYPE_COMBO), + GB_CONSTANT("DragAndDrop", "i", _NET_WM_WINDOW_TYPE_DND), + GB_CONSTANT("Desktop", "i", _NET_WM_WINDOW_TYPE_DESKTOP), + + GB_END_DECLARE +}; +#endif + +GB_DESC CWindowDesc[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), GB_INHERITS("Container"), + + GB_CONSTANT("Normal", "i", 0), + GB_CONSTANT("Above", "i", 1), + GB_CONSTANT("Below", "i", 2), + + GB_METHOD("_new", NULL, Window_new, "[(Parent)Control;]"), + GB_METHOD("_free", NULL, Window_free, NULL), + GB_METHOD("_get", "Control", Window_Controls_get, "(Name)s"), + + GB_METHOD("Close", "b", Window_Close, "[(Return)i]"), + GB_METHOD("Raise", NULL, Window_Raise, NULL), + GB_METHOD("Show", NULL, Window_Show, NULL), + GB_METHOD("Hide", NULL, Window_Hide, NULL), + GB_METHOD("ShowModal", "i", Window_ShowModal, NULL), + GB_METHOD("ShowDialog", "i", Window_ShowModal, NULL), + GB_METHOD("ShowPopup", "i", Window_ShowPopup, "[(X)i(Y)i]"), + GB_METHOD("Center", NULL, Window_Center, NULL), + GB_METHOD("Activate", NULL, Window_Activate, NULL), + + GB_PROPERTY_READ("Modal", "b", Window_Modal), + GB_PROPERTY_READ("TopLevel", "b", Window_TopLevel), + GB_PROPERTY_READ("Closed", "b", Window_Closed), + + GB_METHOD("Delete", NULL, Window_Delete, NULL), + + GB_METHOD("Reparent", NULL, Window_Reparent, "(Container)Container;[(X)i(Y)i]"), + + GB_PROPERTY("Persistent", "b", Window_Persistent), + GB_PROPERTY("Text", "s", Window_Text), + GB_PROPERTY("Title", "s", Window_Text), + GB_PROPERTY("Caption", "s", Window_Text), + GB_PROPERTY("Icon", "Picture", Window_Icon), + GB_PROPERTY("Picture", "Picture", Window_Picture), + GB_PROPERTY("Mask", "b", Window_Mask), + GB_PROPERTY("Minimized", "b", Window_Minimized), + GB_PROPERTY("Maximized", "b", Window_Maximized), + GB_PROPERTY("FullScreen", "b", Window_FullScreen), + GB_PROPERTY("TopOnly", "b", Window_TopOnly), + GB_PROPERTY("Stacking", "i", Window_Stacking), + GB_PROPERTY("Sticky", "b", Window_Sticky), + GB_PROPERTY("SkipTaskbar", "b", Window_SkipTaskbar), + GB_PROPERTY("Visible", "b", Window_Visible), + GB_PROPERTY("Opacity", "i", Window_Opacity), + GB_PROPERTY("Transparent", "b", Window_Transparent), + GB_PROPERTY("TakeFocus", "b", Window_TakeFocus), + + GB_PROPERTY("MinWidth", "i", Window_MinWidth), + GB_PROPERTY("MinHeight", "i", Window_MinHeight), + GB_PROPERTY("MinW", "i", Window_MinWidth), + GB_PROPERTY("MinH", "i", Window_MinHeight), + + ARRANGEMENT_PROPERTIES, + + //GB_PROPERTY("Type", "i", CWINDOW_type), + GB_PROPERTY("Utility", "b", Window_Utility), + GB_PROPERTY("Border", "b", Window_Border), + GB_PROPERTY("Resizable", "b", Window_Resizable), + + GB_PROPERTY_READ("Screen", "i", Window_Screen), + + GB_PROPERTY_SELF("Menus", ".Window.Menus"), + GB_PROPERTY_SELF("Controls", ".Window.Controls"), + + WINDOW_DESCRIPTION, + + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + GB_EVENT("Title", NULL, NULL, &EVENT_Title), + GB_EVENT("Icon", NULL, NULL, &EVENT_Icon), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + GB_EVENT("State", NULL, NULL, &EVENT_State), + + //GB_INTERFACE("Draw", &DRAW_Interface), + + GB_END_DECLARE +}; + + +GB_DESC CWindowsDesc[] = +{ + GB_DECLARE("Windows", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_next", "Window", CWINDOW_next, NULL), + GB_STATIC_METHOD("_get", "Window", CWINDOW_get_from_id, "(Id)i"), + GB_STATIC_PROPERTY_READ("Count", "i", CWINDOW_count), + + GB_END_DECLARE +}; + + +GB_DESC CFormDesc[] = +{ + GB_DECLARE("Form", sizeof(CFORM)), GB_INHERITS("Window"), + GB_AUTO_CREATABLE(), + + GB_STATIC_METHOD("Main", NULL, Form_Main, NULL), + GB_STATIC_METHOD("Load", NULL, Form_Load, "[(Parent)Control;]"), + GB_METHOD("_new", NULL, CFORM_new, NULL), + + FORM_DESCRIPTION, + + GB_END_DECLARE +}; + + +/*************************************************************************** + + MyMainWindow + +***************************************************************************/ + +MyMainWindow::MyMainWindow(QWidget *parent, const char *name, bool embedded) : + QWidget::QWidget(parent, embedded ? Qt::Widget : Qt::Window) +{ + sg = 0; + _border = true; + _resizable = true; + _deleted = false; + //_type = _NET_WM_WINDOW_TYPE_NORMAL; + _enterLoop = false; + _utility = false; + _state = windowState(); + _screen = -1; + + //setAttribute(Qt::WA_KeyCompression, true); + //setAttribute(Qt::WA_InputMethodEnabled, true); + setAttribute(Qt::WA_QuitOnClose, false); + setAttribute(Qt::WA_StaticContents, true); + setObjectName(name); + setFocusPolicy(Qt::NoFocus); + + resize(1, 1); + _activate = false; +} + + +MyMainWindow::~MyMainWindow() +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + + #if DEBUG_WINDOW + qDebug("~MyMainWindow: %s %s %p", GB.GetClassName(THIS), THIS->widget.name, THIS); + #endif + + do_close(THIS, 0, true); + + if (CWINDOW_Active == THIS) + CWINDOW_Active = 0; + + if (CWINDOW_LastActive == THIS) + CWINDOW_LastActive = 0; + + if (sg) + delete sg; + + GB.Detach(THIS); + + if (THIS->menuBar) + { + //CMenu::unrefChildren(THIS->menuBar); + //qDebug("delete menuBar"); + QMenuBar *menuBar = THIS->menuBar; + THIS->menuBar = 0; + delete menuBar; + } + + CWindow::removeTopLevel(THIS); + + _deleted = true; + + //qDebug("~MyMainWindow %p (end)", this); +} + + +void MyMainWindow::showEvent(QShowEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + + //qDebug("showEvent: %s\n", GB.GetClassName(THIS)); + + emit_open_event(THIS); + + //CWINDOW_fix_menubar(THIS); + + if (_activate) + { + //qDebug("showEvent: activate: %s", THIS->widget.name); + raise(); + //setFocus(); + activate(); + _activate = false; + } + + QWidget::showEvent(e); +} + + +void MyMainWindow::initProperties(int which) +{ + CWIDGET *_object = CWidget::get(this); + + if (!THIS->toplevel) // || effectiveWinId() == 0) + return; + + if (!THIS->title && _border) + setWindowTitle(TO_QSTRING(GB.Application.Title())); + + #ifdef QT5 + QT_WINDOW_PROP prop; + + prop.stacking = THIS->stacking; + prop.skipTaskbar = THIS->skipTaskbar; + prop.border = _border; + prop.sticky = THIS->sticky; + + PLATFORM.Window.SetProperties(this, which, &prop); + #else + if (effectiveWinId() == 0) + return; + X11_flush(); + + if (which & (PROP_STACKING | PROP_SKIP_TASKBAR)) + { + X11_window_change_begin(effectiveWinId(), isVisible()); + + if (which & PROP_STACKING) + { + X11_window_change_property(X11_atom_net_wm_state_above, THIS->stacking == 1); + X11_window_change_property(X11_atom_net_wm_state_stays_on_top, THIS->stacking == 1); + X11_window_change_property(X11_atom_net_wm_state_below, THIS->stacking == 2); + } + if (which & PROP_SKIP_TASKBAR) + X11_window_change_property(X11_atom_net_wm_state_skip_taskbar, THIS->skipTaskbar); + + X11_window_change_end(); + } + + //if (which == PROP_ALL) + // X11_set_window_type(effectiveWinId(), _type); + + if (which & PROP_BORDER) + X11_set_window_decorated(effectiveWinId(), _border); + + if (which & PROP_STICKY) + X11_window_set_desktop(effectiveWinId(), isVisible(), THIS->sticky ? 0xFFFFFFFF : X11_get_current_desktop()); + + X11_flush(); + #endif +} + +void MyMainWindow::setEventLoop() +{ + if (!THIS->closed) + THIS->loopLevel = CWINDOW_Current ? CWINDOW_Current->loopLevel : 0; +} + +void MyMainWindow::setGeometryHints() +{ + CWIDGET *_object = CWidget::get(this); + int minw, minh; + + if (!THIS->toplevel) + { + setMinimumSize(0, 0); + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + return; + } + + minw = THIS->minw; + minh = THIS->minh; + + if (_resizable) + { + if (isModal() || isUtility()) + { + if (!minw && !minh) + { + minw = THIS->default_minw; + minh = THIS->default_minh; + } + } + setMinimumSize(minw, minh); + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + } + else + { + setMinimumSize(width(), height()); + setMaximumSize(width(), height()); + } +} + +void MyMainWindow::present(QWidget *parent) +{ + /*CWIDGET *_object = CWidget::get(this); + CWIDGET *_parent = parent ? CWidget::get(parent) : 0; + + qDebug("present: %p %s: parent = %p %s", THIS, _object->name, _parent, _parent ? _parent->name : "");*/ + + if (parent) + _screen = QApplication::desktop()->screenNumber(parent); + else + _screen = -1; + + if (!isVisible()) + { + //X11_window_startup(WINDOW->effectiveWinId(), THIS->x, THIS->y, THIS->w, THIS->h); + + setGeometryHints(); + + setAttribute(Qt::WA_ShowWithoutActivating, THIS->noTakeFocus); + +#ifdef QT5 + if (MAIN_platform_is_wayland) + initProperties(PROP_ALL); +#else + if (effectiveWinId() == 0) + { + createWinId(); + } + initProperties(PROP_ALL); +#endif + + if (getState() & Qt::WindowMinimized) + showMinimized(); + else if (getState() & Qt::WindowFullScreen) + showFullScreen(); + else if (getState() & Qt::WindowMaximized) + showMaximized(); + else + show(); + +#ifdef QT5 + if (!MAIN_platform_is_wayland) + { + //qDebug("createWinId: %p", (void *)effectiveWinId()); + if (THIS->noTakeFocus) + PLATFORM.Window.SetUserTime(this, 0); + initProperties(PROP_ALL); + if (THIS->noTakeFocus) + PLATFORM.Window.SetUserTime(this, 0); + } +#else + initProperties(PROP_SKIP_TASKBAR); +#endif + + /*if (isUtility() && _resizable) + setSizeGrip(true); + else + setSizeGrip(false);*/ + } + else + { + //_activate = true; + + if (getState() & Qt::WindowMinimized) + { + setState(windowState() & ~Qt::WindowMinimized); + //qDebug("_activate set #2"); + } + } + + if (!THIS->noTakeFocus) // && (parent || hasBorder())) + activate(); + + if (parent) + { + #ifdef QT5 + PLATFORM.Window.SetTransientFor(this, parent); + #else + X11_set_transient_for(effectiveWinId(), parent->effectiveWinId()); + #endif + } + + raise(); +} + +void MyMainWindow::showActivate(QWidget *transient) +{ + QWidget *newParentWidget = 0; + + //qDebug("showActivate: %s %d", THIS->widget.name, isToolbar()); + + // Reparent the window if, for example, there is an already modal window displayed + + if (CWINDOW_Current && THIS != CWINDOW_Current) + { + newParentWidget = CWINDOW_Current->widget.widget; + + if (!isVisible()) + { + if (newParentWidget && parentWidget() != newParentWidget) + { + doReparent(newParentWidget, pos()); + } + } + } + + //qDebug("showActivate %p", _object); + + //CWIDGET_clear_flag(THIS, WF_CLOSED); + + if (isUtility()) + { + if (!newParentWidget && CWINDOW_Main && THIS != CWINDOW_Main) + newParentWidget = CWidget::getTopLevel((CWIDGET *)CWINDOW_Main)->widget.widget; + } + + present(newParentWidget); + setEventLoop(); +} + +void on_error_show_modal(MODAL_INFO *info) +{ + #ifdef DEBUG_WINDOW + qDebug("on_error_show_modal"); + #endif + + // info->that can be NULL if the dialog is destroyed during the event loop + + if (info->that) + info->that->_enterLoop = false; + + MyApplication::eventLoop->exit(); + + GB.Debug.LeaveEventLoop(); + + MyApplication::eventLoop = info->old; + CWINDOW_Current = info->save; + + if (info->that && info->that->isPersistent()) + { + info->that->setSizeGrip(false); + info->that->setWindowModality(Qt::NonModal); + info->that->setWindowFlags(Qt::Window | info->flags); + } + + CWIDGET_leave_popup(info->save_popup); +} + +void MyMainWindow::doShowModal(bool popup, const QPoint *pos) +{ + CWIDGET *_object = CWidget::get(this); + CWINDOW *parent; + bool persistent = THIS->persistent; + //QPoint p = pos(); + QEventLoop eventLoop; + GB_ERROR_HANDLER handler; + MODAL_INFO info; + + CWIDGET_finish_focus(); + CMOUSE_finish_event(); + + info.that = this; + info.old = MyApplication::eventLoop; + info.save = CWINDOW_Current; + info.save_popup = popup ? CWIDGET_enter_popup() : NULL; + info.flags = windowFlags() & ~Qt::WindowType_Mask; + + setWindowModality(Qt::ApplicationModal); + + _enterLoop = false; // Do not call exitLoop() if we do not entered the loop yet! + + if (popup) + { + setWindowFlags(Qt::Popup | info.flags); + + move(0, 0); + move(*pos); + setFocus(); + show(); + raise(); + } + else + { + if (_resizable && _border) + setSizeGrip(true); + + parent = CWINDOW_Current; + if (!parent) + parent = CWINDOW_Active; + + if (parent) + setParent(CWidget::getTopLevel((CWIDGET *)parent)->widget.widget, Qt::Window | info.flags); + + present(parent ? CWidget::getTopLevel((CWIDGET *)parent)->widget.widget : 0); + } + + //fprintf(stderr, "set event loop to %p\n", &eventLoop); + MyApplication::eventLoop = &eventLoop; + + setEventLoop(); + + THIS->loopLevel++; + + THIS->save_focus = CWIDGET_active_control; + THIS->previous = CWINDOW_Current; + CWINDOW_Current = THIS; + + _enterLoop = true; + + GB.Debug.EnterEventLoop(); + + handler.handler = (GB_CALLBACK)on_error_show_modal; + handler.arg1 = (intptr_t)&info; + + GB.OnErrorBegin(&handler); + + //fprintf(stderr, "event loop <---- %p\n",CWINDOW_Current); + eventLoop.exec(); + //fprintf(stderr, "event loop ---->%p\n", info.save); + + GB.OnErrorEnd(&handler); + + GB.Debug.LeaveEventLoop(); + + //eventLoop.processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + MyApplication::eventLoop = info.old; + CWINDOW_Current = info.save; + + if (persistent) + { + setSizeGrip(false); + setWindowModality(Qt::NonModal); + setWindowFlags(Qt::Window | info.flags); + } + + if (popup) + CWIDGET_leave_popup(info.save_popup); + + CWINDOW_ensure_active_window(); + + if (THIS->save_focus) + { + CWIDGET_set_focus(THIS->save_focus); + THIS->save_focus = NULL; + } +} + +#if 0 +void MyMainWindow::showModal(void) +{ + //Qt::WindowFlags flags = windowFlags() & ~Qt::WindowType_Mask; + CWIDGET *_object = CWidget::get(this); + CWINDOW *parent; + bool persistent = CWIDGET_test_flag(THIS, WF_PERSISTENT); + //QPoint p = pos(); + QEventLoop eventLoop; + GB_ERROR_HANDLER handler; + MODAL_INFO info; + + if (THIS->modal) + return; + + CWIDGET_finish_focus(); + + info.that = this; + info.old = MyApplication::eventLoop; + info.save = CWINDOW_Current; + + MyApplication::eventLoop = &eventLoop; + + setWindowModality(Qt::ApplicationModal); + + if (_resizable && _border) + { + setMinimumSize(THIS->minw, THIS->minh); + setSizeGrip(true); + } + + _enterLoop = false; // Do not call exitLoop() if we do not entered the loop yet! + + parent = CWINDOW_Current; + if (!parent) + { + parent = CWINDOW_Main; + if (!parent) + parent = CWINDOW_Active; + } + + present(parent ? CWidget::getTopLevel((CWIDGET *)parent)->widget.widget : 0); + setEventLoop(); + + THIS->loopLevel++; + CWINDOW_Current = THIS; + + _enterLoop = true; + + GB.Debug.EnterEventLoop(); + + handler.handler = (GB_CALLBACK)on_error_show_modal; + handler.arg1 = (intptr_t)&info; + + GB.OnErrorBegin(&handler); + + eventLoop.exec(); + + GB.OnErrorEnd(&handler); + + GB.Debug.LeaveEventLoop(); + + //eventLoop.processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + MyApplication::eventLoop = info.old; + CWINDOW_Current = info.save; + + if (persistent) + { + setSizeGrip(false); + setWindowModality(Qt::NonModal); + } + + CWINDOW_ensure_active_window(); +} + +void MyMainWindow::showPopup(QPoint &pos) +{ + Qt::WindowFlags flags = windowFlags() & ~Qt::WindowType_Mask; + CWIDGET *_object = CWidget::get(this); + bool persistent = CWIDGET_test_flag(THIS, WF_PERSISTENT); + CWINDOW *save = CWINDOW_Current; + void *save_popup; + QEventLoop eventLoop; + QEventLoop *old; + + if (THIS->modal) + return; + + CWIDGET_finish_focus(); + + setWindowFlags(Qt::Popup | flags); + setWindowModality(Qt::ApplicationModal); + THIS->popup = true; + + /*if (_resizable && _border) + { + setMinimumSize(THIS->minw, THIS->minh); + setSizeGrip(true); + }*/ + + _enterLoop = false; // Do not call exitLoop() if we do not entered the loop yet! + + move(0, 0); + move(pos); + setFocus(); + show(); + raise(); + setEventLoop(); + //QTimer::singleShot(50, this, SLOT(activateLater())); + + THIS->loopLevel++; + CWINDOW_Current = THIS; + + //handle_focus(THIS); + //activateWindow(); + + save_popup = CWIDGET_enter_popup(); + + _enterLoop = true; + + old = MyApplication::eventLoop; + MyApplication::eventLoop = &eventLoop; + GB.Debug.EnterEventLoop(); + eventLoop.exec(); + GB.Debug.LeaveEventLoop(); + MyApplication::eventLoop = old; + //eventLoop.exec(); + //eventLoop.processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + CWINDOW_Current = save; + + if (persistent) + { + setWindowModality(Qt::NonModal); + setWindowFlags(Qt::Window | flags); + THIS->popup = false; + } + + CWIDGET_leave_popup(save_popup); + + //CWIDGET_check_hovered(); +} +#endif + +#if 0 +void MyMainWindow::setTool(bool t) +{ + WFlags f = getWFlags(); + + if (t) + f |= WStyle_Tool | WStyle_Customize; + else + f &= ~WStyle_Tool; + + doReparent(CWINDOW_Main ? (MyMainWindow *)QWIDGET(CWINDOW_Main) : 0, f, pos()); +} +#endif + +void MyMainWindow::moveSizeGrip() +{ + CWINDOW *window; + QWidget *cont; + + if (sg == 0) + return; + + window = (CWINDOW *)CWidget::get(this); + cont = window->container; + + if (qApp->isRightToLeft()) + sg->move(cont->rect().bottomLeft() - sg->rect().bottomLeft()); + else + sg->move(cont->rect().bottomRight() - sg->rect().bottomRight()); +} + +void MyMainWindow::setSizeGrip(bool on) +{ + if (on == (sg != 0)) + return; + + if (!on) + { + delete sg; + sg = 0; + } + else //if (!parentWidget()) + { + sg = new QSizeGrip(((CWINDOW *)CWidget::get(this))->container); + sg->adjustSize(); + moveSizeGrip(); + sg->lower(); + //if (paletteBackgroundPixmap()) + // sg->setBackgroundOrigin(QWidget::AncestorOrigin); + sg->show(); + } +} + +void MyMainWindow::setBorder(bool b) +{ + if (_border == b) + return; + + _border = b; + + if (!isWindow()) + return; + +#ifdef QT5 + + bool visible = isVisible(); + void *_object = CWidget::get(this);; + + if (_border) + setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); + else + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + + if (visible) + { + show(); + move(THIS->x, THIS->y); + } + +#else + + initProperties(PROP_BORDER); + if (effectiveWinId()) + X11_window_remap(effectiveWinId()); + doReparent(parentWidget(), pos()); + +#endif +} + +void MyMainWindow::setResizable(bool b) +{ + if (_resizable == b) + return; + + _resizable = b; + if (!isWindow()) + return; + doReparent(parentWidget(), pos()); +} + +void MyMainWindow::setUtility(bool b) +{ + if (_utility == b) + return; + + _utility = b; + + doReparent(parentWidget(), pos()); +} + +#if 0 +#ifdef NO_X_WINDOW +#else +int MyMainWindow::getType() +{ + if (!isWindow()) + return 0; + else + return _type; + //return X11_get_window_type(effectiveWinId()); +} + +void MyMainWindow::setType(int type) +{ + if (!isWindow()) + return; + X11_set_window_type(effectiveWinId(), type); + _type = type; +} +#endif +#endif + +void MyMainWindow::moveEvent(QMoveEvent *e) +{ + CWIDGET *_object = CWidget::getReal(this); + + //qDebug("Move: (%s %p) %d %d", GB.GetClassName(THIS), THIS, e->pos().x(), e->pos().y()); + + QWidget::moveEvent(e); + + //qDebug("Move (pos %d %d) (oldPos %d %d)", e->pos().x(), e->pos().y(), e->oldPos().x(), e->oldPos().y()); + //qDebug(" (geom %d %d) (fgeom %d %d)", geometry().x(), geometry().y(), frameGeometry().x(), frameGeometry().y()); + //qDebug(" Visible = %s Hidden = %s", (isVisible() ? "Yes" : "No"), (isHidden() ? "Yes" : "No")); + //qDebug(" Flags = 0x%s State = 0x%s", QString::number(getWFlags(), 16).latin1(), QString::number(getWState(), 16).latin1()); + + //if (CWIDGET_test_flag(ob, WF_IGNORE_MOVE)) + + //if (THIS->embedded) + // return; + + if (THIS->toplevel) + { + if (hasBorder() && !THIS->reallyMasked) + if (geometry().x() == frameGeometry().x() && geometry().y() == frameGeometry().y()) + return; + + if (!isHidden()) + { + THIS->x = x(); + THIS->y = y(); + //qDebug("moveEvent: x= %d y = %d", x(), y()); + } + } + + //qDebug("moveEvent %ld %ld isHidden:%s shown:%s ", THIS->x, THIS->y, isHidden() ? "1" : "0", shown ? "1" : "0"); + + if (THIS->opened) + GB.Raise(THIS, EVENT_Move, 0); +} + + + +/* +static void post_resize_event(CWINDOW *_object) +{ + qDebug("post resize: %d %d", THIS->w, THIS->h); + WINDOW->resize(THIS->w, THIS->h); + GB.Unref(POINTER(&_object)); +} +*/ + +void MyMainWindow::resizeEvent(QResizeEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::getReal(this); + //int w, h; + + //qDebug("Resize %p: %d %d <- %d %d", _object, e->size().width(), e->size().height(), e->oldSize().width(), e->oldSize().height()); + + //QMainWindow::resizeEvent(e); + + configure(); + + if (sg) + moveSizeGrip(); + + if (!isHidden()) + { + THIS->w = THIS->container->width(); + THIS->h = THIS->container->height(); + if (isTopLevel()) + CCONTAINER_arrange(THIS); + } + +#ifndef NO_X_WINDOW +#ifndef QT5 + if (THIS->xembed) + XEMBED->resize(THIS->w, THIS->h); +#endif +#endif + + //qDebug("resizeEvent %ld %ld isHidden:%s shown:%s ", THIS->w, THIS->h, isHidden() ? "1" : "0", shown ? "1" : "0"); + //qDebug("THIS->h = %ld THIS->container->height() = %ld height() = %ld", THIS->h, THIS->container->height(), height()); + + if (THIS->opened && (e->spontaneous() || parentWidget())) + raise_resize_event(THIS); +} + + +void MyMainWindow::keyPressEvent(QKeyEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::getReal(this); + QPushButton *test = 0; + CWIDGET *ob; + + e->ignore(); + + //qDebug("MyMainWindow::keyPressEvent: (%p '%s' %s)", this, this ? this->caption().latin1() : 0, GB.GetClassName(CWidget::get(this))); + + if ((e->modifiers() == Qt::NoModifier || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter ))) + { + switch (e->key()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + + test = THIS->defaultButton; + break; + + case Qt::Key_Escape: + + test = THIS->cancelButton; + break; + } + + if (!test) + return; + + ob = CWidget::get(test); + if (!ob) + return; + + if (CWIDGET_is_design(ob)) + return; + + if (!test->isVisible() || !test->isEnabled()) + return; + + test->setFocus(); + test->animateClick(); + e->accept(); + } +} + + +bool CWINDOW_close_all(bool main) +{ + QList list(CWindow::list); + CWINDOW *win; + int i; + bool ret = false; + + #if DEBUG_WINDOW + qDebug("<<< CLOSE ALL"); + #endif + + for (i = 0; i < list.count(); i++) + { + win = list.at(i); + if (win != CWINDOW_Main && do_close(win, 0)) + { + ret = true; + break; + } + } + + if (main && CWINDOW_Main) + ret = do_close(CWINDOW_Main, 0); + + #if DEBUG_WINDOW + qDebug(">>> CLOSE ALL"); + #endif + + return ret; +} + +void CWINDOW_delete_all(bool main) +{ + QList list(CWindow::list); + CWINDOW *win; + int i; + + #if DEBUG_WINDOW + qDebug("<<< DELETE ALL"); + #endif + + for (i = 0; i < list.count(); i++) + { + win = CWindow::list.at(i); + if (win != CWINDOW_Main) + { + //qDebug("destroy window %s", GB.GetClassName(win)); + CWIDGET_destroy((CWIDGET *)win); + } + } + + if (main && CWINDOW_Main) + { + //qDebug("destroy main window %s", GB.GetClassName(CWINDOW_Main)); + CWIDGET_destroy((CWIDGET *)CWINDOW_Main); + } + + #if DEBUG_WINDOW + qDebug("DELETE ALL >>>"); + #endif + + //qApp->eventLoop()->processEvents(QEventLoop::AllEvents); +} + +bool CWINDOW_must_quit() +{ + CWINDOW *win; + int i; + + for (i = 0; i < CWindow::list.count(); i++) + { + win = CWindow::list.at(i); + if (win->opened) + return false; + } + + return true; +} + + +void MyMainWindow::closeEvent(QCloseEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + bool cancel = false; + //bool modal; + + //fprintf(stderr, "closeEvent(): THIS = %p loopLevel = %d CWINDOW_Current = %p\n", THIS, THIS->loopLevel, CWINDOW_Current); + + e->ignore(); + + #if DEBUG_WINDOW + qDebug("closeEvent: CWINDOW_Current = %p / %d <-> %p / %d", CWINDOW_Current, CWINDOW_Current ? CWINDOW_Current->loopLevel : -1, THIS, THIS->loopLevel); + #endif + + if (THIS->opened) + { + // If a window is not opened, then it can be closed whatever the loop level is + if (CWINDOW_Current && (THIS->loopLevel != CWINDOW_Current->loopLevel)) + { + goto IGNORE; + } + + //qDebug("THIS->opened = %d: %p: %s", THIS->opened, THIS, GB.GetClassName(THIS)); + THIS->closing = true; + //qDebug("Close event: %s %p", GB.GetClassName(THIS), THIS); + cancel = GB.Raise(THIS, EVENT_Close, 0); + THIS->closing = false; + } + + if (!cancel && THIS == CWINDOW_Main) + { + if (CWINDOW_close_all(false)) + cancel = true; + } + + if (cancel) + goto IGNORE; + + //modal = isModal(); //testWFlags(Qt::WShowModal); // && THIS->opened; + + THIS->closed = true; + //qApp->sendEvent(WIDGET, new QEvent(EVENT_CLOSE)); + + if (CWINDOW_LastActive == THIS) + { + //GB.Unref(POINTER(&CWINDOW_LastActive)); + CWINDOW_LastActive = NULL; + //qDebug("CWINDOW_LastActive = 0"); + } + + if (THIS == CWINDOW_Active) + CWINDOW_activate(NULL); + + if (!THIS->persistent) + { + if (CWINDOW_Main == THIS) + { + CWINDOW_delete_all(false); + #if DEBUG_WINDOW + qDebug("CWINDOW_Main -> NULL"); + #endif + CWINDOW_Main = NULL; + } + + CWIDGET_destroy((CWIDGET *)THIS); + } + + e->accept(); + + if (THIS->modal && _enterLoop) + { + _enterLoop = false; + MyApplication::eventLoop->exit(); + } + + #if DEBUG_WINDOW + qDebug("THIS->opened <- false: %p: %s", THIS, GB.GetClassName(THIS)); + #endif + THIS->opened = false; + MAIN_check_quit(); + + return; + +IGNORE: + + THIS->closed = false; + e->ignore(); +} + + +bool MyMainWindow::isPersistent(void) +{ + return !testAttribute(Qt::WA_DeleteOnClose); //testWFlags(WDestructiveClose); +} + + +void MyMainWindow::setPersistent(bool pers) +{ + setAttribute(Qt::WA_DeleteOnClose, !pers); +} + +void MyMainWindow::doReparent(QWidget *parent, const QPoint &pos) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + QIcon icon; + bool old_toplevel; + bool hidden; + bool reparented = false; + Qt::WindowFlags f = windowFlags(); + #ifndef NO_X_WINDOW + bool active = qApp->activeWindow() == this; + #endif + + icon = windowIcon(); + + old_toplevel = THIS->toplevel; + THIS->toplevel = !parent || parent->isWindow(); + THIS->embedded = !THIS->toplevel; + + f &= ~Qt::WindowType_Mask; + + if (THIS->toplevel) + { + if (_utility) + f |= Qt::Dialog; + else + f |= Qt::Window; + + if (!old_toplevel) + CWindow::insertTopLevel(THIS); + } + else + { + if (old_toplevel) + { + THIS->toplevel = true; + CWindow::removeTopLevel(THIS); + THIS->toplevel = false; + } + } + + //qDebug("doReparent: %s %p: visible = %d opened = %d hidden = %d isVisible = %d isHidden = %d shown = %d", + // THIS->widget.name, THIS, THIS->widget.flag.visible, THIS->opened, THIS->hidden, isVisible(), isHidden(), THIS->widget.flag.shown); + + //if (!THIS->hidden) showIt = true; + //hide(); + + hidden = THIS->hidden || !WIDGET->isVisible(); + if (parent != parentWidget() || f != windowFlags()) + { + reparented = true; + setParent(parent, f); + //qDebug("setParent %d", f != windowFlags()); + } + + move(pos); + //qDebug("doReparent: (%s %p) (%d %d) -> (%d %d)", GB.GetClassName(THIS), THIS, pos.x(), pos.y(), WIDGET->x(), WIDGET->y()); + + if (!THIS->embedded) + { + #ifndef NO_X_WINDOW + initProperties(PROP_ALL); + if (active && hasBorder()) + activate(); + #endif + + setWindowIcon(icon); + } + + if (!_resizable && _border && isWindow()) + { + setMinimumSize(width(), height()); + setMaximumSize(width(), height()); + } + else + { + setMinimumSize(0, 0); + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + } + + /*if (parentWidget()) + qDebug("doReparent (%s %p): new parent = (%s %p)", THIS->widget.name, THIS, CWidget::get(parentWidget())->name, CWidget::get(parentWidget())); + else + qDebug("doReparent (%s %p): new parent = 0", THIS->widget.name, THIS);*/ + + if (reparented) + { + if (!hidden) + Window_Show(THIS, NULL); + } +} + +int MyMainWindow::currentScreen() const +{ +#ifdef QT5 + QList screenList; + QScreen* primaryScreen; +#endif + + if (_screen >= 0) + return _screen; + + if (CWINDOW_Active) + return QApplication::desktop()->screenNumber(CWINDOW_Active->widget.widget); + else if (CWINDOW_Main) + return QApplication::desktop()->screenNumber(CWINDOW_Main->widget.widget); + else +#ifndef QT5 + return QApplication::desktop()->primaryScreen(); +#else + { + primaryScreen = QGuiApplication::primaryScreen(); + screenList = QGuiApplication::screens(); + return screenList.indexOf(primaryScreen); + } +#endif +} + +void MyMainWindow::center() +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + QPoint p; + QRect r; + +#ifdef QT5 + r = QGuiApplication::screens().at(currentScreen())->availableGeometry(); +#else + r = QApplication::desktop()->availableGeometry(currentScreen()); +#endif + + CWIDGET_move(THIS, r.x() + (r.width() - width()) / 2, r.y() + (r.height() - height()) / 2); +} + +void MyMainWindow::configure() +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + QMenuBar *menuBar = THIS->menuBar; + bool arrange = false; + QRect geom; + + //qDebug("THIS->menuBar = %p menuBar() = %p", THIS->menuBar, menuBar()); + + if (menuBar && THIS->showMenuBar && !THIS->hideMenuBar) + { + int h = menuBar->sizeHint().height(); + + if (h == 0) + h = menuBar->height(); + + menuBar->show(); + geom = QRect(0, h, this->width(), this->height() - h); + + if (THIS->container->geometry() != geom) + { + arrange = true; + THIS->container->setGeometry(geom); + } + menuBar->setGeometry(0, 0, this->width(), h); + } + else + { + if (menuBar) + { + menuBar->move(0, -menuBar->height()); + menuBar->lower(); + } + + geom = QRect(0, 0, this->width(), this->height()); + + if (THIS->container->geometry() != geom) + { + arrange = true; + THIS->container->setGeometry(geom); + } + + THIS->container->raise(); + } + + if (arrange) + { + CCONTAINER_arrange(THIS); + CMENU_update_menubar(THIS); + } + + //qDebug(">>> THIS->menuBar = %p menuBar() = %p", THIS->menuBar, menuBar()); + + //qDebug("configure: %p (%d %d %d %d)", THIS, ((QFrame *)(THIS->container))->contentsRect().x(), ((QFrame *)(THIS->container))->contentsRect().y(), ((QFrame *)(THIS->container))->contentsRect().width(), ((QFrame *)(THIS->container))->contentsRect().height()); +} + + +void MyMainWindow::setName(const char *name, CWIDGET *control) +{ + if (_deleted) + return; + + names.remove(name); + if (control) + names.insert(name, control); +} + +void MyMainWindow::resize(int w, int h) +{ + bool save = _resizable; + + if (!_resizable && _border) + setResizable(true); + + QWidget::resize(w, h); + + if (_resizable != save) + setResizable(save); +} + +void MyMainWindow::setGeometry(int x, int y, int w, int h) +{ + bool save = _resizable; + + if (!_resizable && _border) + setResizable(true); + + QWidget::setGeometry(x, y, w, h); + + if (_resizable != save) + setResizable(save); +} + + +void MyMainWindow::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + + if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange) + { + void *_object = CWidget::get(this); + configure(); + GB.Raise(THIS, EVENT_Font, 0); + } + else if (e->type() == QEvent::WindowStateChange) + { + GB.Raise(CWidget::get(this), EVENT_State, 0); + } +} + +Qt::WindowStates MyMainWindow::getState() const +{ + return isVisible() ? windowState() : _state; +} + +void MyMainWindow::setState(Qt::WindowStates state) +{ + if (isVisible()) + setWindowState(state); + else + _state = state; +} + +void MyMainWindow::setVisible(bool visible) +{ + if (!visible) + setAttribute(Qt::WA_NoMouseReplay); + QWidget::setVisible(visible); +} + +void MyMainWindow::setBetterMask(QPixmap &bg) +{ + if (!bg.hasAlphaChannel()) + return; + + static const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + const QImage img = bg.toImage(); + const QImage image = (img.depth() < 32 ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) : img); + const int w = image.width(); + const int h = image.height(); + + QImage mask(w, h, QImage::Format_MonoLSB); + if (mask.isNull()) // allocation failed + return; + + mask.setColorCount(2); + mask.setColor(0, QColor(Qt::color0).rgba()); + mask.setColor(1, QColor(Qt::color1).rgba()); + + const int bpl = mask.bytesPerLine(); + + for (int y = 0; y < h; ++y) { + const QRgb *src = reinterpret_cast(image.scanLine(y)); + uchar *dest = mask.scanLine(y); + memset(dest, 0, bpl); + for (int x = 0; x < w; ++x) { + if (qAlpha(*src) >= 128) + dest[x >> 3] |= qt_pixmap_bit_mask[x & 7]; + ++src; + } + } + + setMask(QBitmap::fromImage(mask)); +} + +void MyMainWindow::activate(void) +{ +#ifdef QT5 + PLATFORM.Window.Activate(this); +#else + activateWindow(); +#endif +} + +bool MyMainWindow::focusNextPrevChild(bool next) +{ + void *current, *initial; + QWidget *w; + + current = CWidget::getRealExisting(focusWidget()); + if (!current) + return QWidget::focusNextPrevChild(next); + + //uint focus_flag = QGuiApplication::styleHints()->tabFocusBehavior() == Qt::TabFocusAllControls ? Qt::TabFocus : Qt::StrongFocus; + + initial = current; + + for(;;) + { + current = next ? CWIDGET_get_next_focus(current) : CWIDGET_get_previous_focus(current); + if (!current || current == initial) + return QWidget::focusNextPrevChild(next); + + //fprintf(stderr, "focusNextPrevChild: %s / %d\n", ((CWIDGET *)current)->name, CWIDGET_has_no_tab_focus(current)); + + if (CWIDGET_has_no_tab_focus(current)) + continue; + + w = QWIDGET(current); + if (w->isVisible() && w->isEnabled() && (w->focusPolicy() & Qt::TabFocus)) + { + CWIDGET_set_focus(current); + return true; + } + } + +} + +/*************************************************************************** + + CWindow + +***************************************************************************/ + +CWindow CWindow::manager; +int CWindow::count = 0; +QList CWindow::list; + +/*static void post_activate_event(void *ob) +{ + GB.Raise(ob, EVENT_Activate, 0); + GB.Unref(&ob); +} + +static void post_deactivate_event(void *ob) +{ + GB.Raise(ob, EVENT_Deactivate, 0); + GB.Unref(&ob); +}*/ + +void CWINDOW_activate(CWIDGET *ob) +{ + CWINDOW *active; + + /*if (ob) + { + fprintf(stderr, "CWINDOW_activate: %s %s\n", GB.GetClassName(ob), ob->name); + + CWIDGET *parent = ob; + for(;;) + { + parent = (CWIDGET *)CWIDGET_get_parent(parent); + if (!parent) + break; + fprintf(stderr, " --> %s %s\n", GB.GetClassName(parent), parent->name); + } + } + else + fprintf(stderr, "CWINDOW_activate: null\n");*/ + + if (ob) + { + active = CWidget::getWindow(ob); + for(;;) + { + if (active->toplevel) + break; + if (GB.CanRaise(active, EVENT_Activate)) + break; + active = CWidget::getWindow(CWidget::get(QWIDGET(active)->parentWidget())); + } + } + else + active = 0; + + if (active == CWINDOW_Active) + return; + + //qDebug("CWINDOW_activate: (%s %p): (%s %p) -> (%s %p)", ob ? GB.GetClassName(ob) : "", ob, CWINDOW_Active ? GB.GetClassName(CWINDOW_Active) : "", CWINDOW_Active, active ? GB.GetClassName(active) : "", active); + + if (CWINDOW_Active) + { + GB.Raise(CWINDOW_Active, EVENT_Deactivate, 0); + CWINDOW_Active = NULL; + } + + if (active) + { + //fprintf(stderr, "Activate: %s\n", GB.GetClassName(active)); + GB.Raise(active, EVENT_Activate, 0); + } + + CWINDOW_Active = active; + + CWIDGET_check_hovered(); +} + +void CWINDOW_set_default_button(CWINDOW *win, QPushButton *button, bool on) +{ + if (on) + { + if (win->defaultButton) + win->defaultButton->setDefault(false); + + win->defaultButton = button; + button->setDefault(true); + } + else + { + if (win->defaultButton == button) + { + button->setDefault(false); + win->defaultButton = 0; + } + } +} + +void CWINDOW_set_cancel_button(CWINDOW *win, QPushButton *button, bool on) +{ + //qDebug("CWINDOW_set_cancel_button: (%s %p) (%s %p) %d", GB.GetClassName(win), win, GB.GetClassName(CWidget::get(button)), CWidget::get(button), on); + if (on) + { + win->cancelButton = button; + } + else + { + if (button == win->cancelButton) + win->cancelButton = 0; + } +} + + +bool CWindow::eventFilter(QObject *o, QEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(o); + + if (THIS && !THIS->widget.flag.deleted) + { + if (e->type() == QEvent::Show) // && !e->spontaneous()) + { + MyMainWindow *w = (MyMainWindow *)o; + + THIS->noHideEvent = false; + + if (THIS->toplevel && !THIS->popup && !THIS->moved) + w->center(); + + //handle_focus(THIS); + emit_open_event(THIS); + + //qDebug("eventFilter: Show: %s %d (%d) focus = %p", GB.GetClassName(THIS), !WINDOW->isHidden(), e->spontaneous(), THIS->focus); + + post_show_event(THIS); + //CWINDOW_define_mask(THIS); + + GB.Raise(THIS, EVENT_Show, 0); + if (!e->spontaneous()) + CACTION_raise(THIS); + } + else if (e->type() == QEvent::Hide) // && !e->spontaneous()) + { + //fprintf(stderr, "Hide: %p %s %d (%d)\n", o, GB.GetClassName(THIS), WINDOW->isHidden(), e->spontaneous()); + if (!THIS->noHideEvent) + { + THIS->noHideEvent = true; + GB.Raise(THIS, EVENT_Hide, 0); + if (!e->spontaneous()) + CACTION_raise(THIS); + } + } + } + + return QObject::eventFilter(o, e); // standard event processing +} + +#ifdef QT5 +void CWindow::error(void) +{ +} + +void CWindow::embedded(void) +{ +} + +void CWindow::closed(void) +{ +} +#else +void CWindow::error(void) +{ + //CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::error %p -> %p", sender(), THIS); + CWINDOW_EmbedState = EMBED_ERROR; +} + +void CWindow::embedded(void) +{ + //CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::embedded %p -> %p", sender(), THIS); + CWINDOW_EmbedState = EMBED_OK; +} + +void CWindow::closed(void) +{ + //CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::closed %p -> %p", sender(), THIS); + //CWIDGET_destroy(CWidget::getReal((QObject *)sender())); + delete sender(); +} +#endif + +void CWindow::destroy(void) +{ + CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::destroy %p -> %p", sender(), THIS); + + if (THIS) + { + do_close(THIS, 0, true); + CWindow::removeTopLevel(THIS); + } + +#ifndef QT5 + CWINDOW_EmbedState = EMBED_WAIT; + CWINDOW_Embedded = false; + CWINDOW_Embedder = 0; +#endif +} + +void CWindow::insertTopLevel(CWINDOW *_object) +{ + if (!THIS->toplevel) + return; + + list.append(THIS); + count = list.count(); + + #if DEBUG_WINDOW + qDebug("insertTopLevel: count = %d (%p %s %s)", count, _object, THIS->widget.name, THIS->embedded ? "E" : "W"); + #endif +} + +void CWindow::removeTopLevel(CWINDOW *_object) +{ + if (!THIS->toplevel) + return; + + list.removeAll(THIS); + count = list.count(); + + #if DEBUG_WINDOW + qDebug("removeTopLevel: count = %d (%p %s %s)", count, THIS, THIS->widget.name, THIS->embedded ? "E" : "W"); + #endif + + MAIN_check_quit(); +} + +CMENU *CWindow::findMenu(CWINDOW *_object, const char *name) +{ + int i; + CMENU *menu; + CWIDGET *parent; + + for(;;) + { + if (THIS->menuBar) + { + for (i = 0; i < THIS->menuBar->actions().count(); i++) + { + menu = CMenu::dict[THIS->menuBar->actions().at(i)]; + if (!menu) + continue; + if (!strcasecmp(menu->widget.name, name)) + return menu; + } + } + + parent = (CWIDGET *)CWIDGET_get_parent(THIS); + if (!parent) + break; + _object = CWidget::getWindow(parent); + if (!THIS) + break; + } + + return NULL; +} + diff --git a/gb.qt4/src/CWindow.h b/gb.qt4/src/CWindow.h new file mode 100644 index 00000000..2ef21b9e --- /dev/null +++ b/gb.qt4/src/CWindow.h @@ -0,0 +1,291 @@ +/*************************************************************************** + + CWindow.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWINDOW_H +#define __CWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "CContainer.h" +#include "CMenu.h" +#include "CPicture.h" + +//#define DEBUG_WINDOW 1 + +typedef + struct CWINDOW { + CWIDGET widget; + MyContainer *container; + CARRANGEMENT arrangement; + QMenuBar *menuBar; + CPICTURE *icon; + CPICTURE *picture; + CWIDGET *focus; + CWIDGET *save_focus; + struct CWINDOW *previous; + QPushButton *defaultButton; + QPushButton *cancelButton; + int ret; + int loopLevel; + + int x; + int y; + int w; + int h; + int minw; + int minh; + int default_minw; + int default_minh; + int last_resize_w; + int last_resize_h; + + unsigned toplevel : 1; + unsigned persistent : 1; + unsigned closed : 1; + unsigned embedded : 1; + unsigned xembed : 1; + unsigned stacking : 2; + unsigned skipTaskbar : 1; + + unsigned masked : 1; + unsigned reallyMasked : 1; + unsigned opened : 1; + unsigned hidden : 1; + unsigned toolbar : 1; + unsigned minsize : 1; + unsigned title : 1; + unsigned stateChange : 1; + + unsigned closing : 1; + unsigned hideMenuBar : 1; + unsigned showMenuBar : 1; + unsigned sticky : 1; + unsigned noTakeFocus : 1; + unsigned moved : 1; + unsigned resized : 1; + unsigned popup : 1; + + unsigned modal : 1; + unsigned noHideEvent : 1; + } + CWINDOW; + +typedef + struct { + CWINDOW window; + } + CFORM; + + +#ifndef __CWINDOW_CPP + +extern GB_DESC CWindowDesc[]; +extern GB_DESC CWindowMenusDesc[]; +extern GB_DESC CWindowControlsDesc[]; +//extern GB_DESC CWindowTypeDesc[]; +extern GB_DESC CWindowsDesc[]; +extern GB_DESC CFormDesc[]; + +extern CWINDOW *CWINDOW_Main; +extern int CWINDOW_MainDesktop; +extern CWINDOW *CWINDOW_Current; +extern CWINDOW *CWINDOW_Active; +extern CWINDOW *CWINDOW_LastActive; +#ifndef QT5 +extern int CWINDOW_Embedder; +extern bool CWINDOW_Embedded; +#endif + +#else + +#define THIS ((CWINDOW *)_object) +#define WIDGET ((QWidget *)(((CWIDGET *)_object)->widget)) +#define WINDOW ((MyMainWindow *)WIDGET) + +#ifndef QT5 +#define XEMBED ((QX11EmbedWidget *)(WIDGET->parent())) +#endif + +#endif + +class CWindow : public QObject +{ + Q_OBJECT + +public: + + static QList list; + + static CWindow manager; + static int count; + + static void insertTopLevel(CWINDOW *_object); + static void removeTopLevel(CWINDOW *_object); + static CMENU *findMenu(CWINDOW *_object, const char *name); + +protected: + + bool eventFilter(QObject *, QEvent *); + +public slots: + + void error(void); + void embedded(void); + void closed(void); + void destroy(void); +}; + +class MyMainWindow; + +typedef + struct { + QPointer that; + Qt::WindowFlags flags; + QEventLoop *old; + CWINDOW *save; + void *save_popup; + } + MODAL_INFO; + +#ifndef QT5 +enum { PROP_ALL = -1, PROP_STACKING = 1, PROP_SKIP_TASKBAR = 2, PROP_BORDER = 4, PROP_STICKY = 8 }; +#endif + +class MyMainWindow : public QWidget +{ + Q_OBJECT + +private: + + QSizeGrip *sg; + QMenuBar *mb; + bool _activate; + bool _border; + bool _resizable; + bool _deleted; + bool _enterLoop; + bool _utility; + //int _type; + Qt::WindowStates _state; + int _screen; + + void doShowModal(bool popup, const QPoint *pos = NULL); + +protected: + + virtual void showEvent(QShowEvent *); + virtual void resizeEvent(QResizeEvent *); + virtual void moveEvent(QMoveEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual void closeEvent(QCloseEvent *); + virtual void changeEvent(QEvent *); + + //bool eventFilter(QObject *, QEvent *); + +public: + + enum { BorderNone = 0, BorderFixed = 1, BorderResizable = 2 }; + QHash names; + void *_object; + + explicit MyMainWindow(QWidget *parent, const char *name, bool embedded = false); + ~MyMainWindow(); + + virtual void setVisible(bool visible); + + void initProperties(int which); + void present(QWidget *parent = 0); + void showActivate(QWidget *parent = 0); + //void activateLater() { _activate = true; } + void showModal() { doShowModal(false); } + void showPopup(QPoint &pos) { doShowModal(true, &pos); } + void setEventLoop(); + //bool isModal() { return testWFlags(WShowModal); } + void doReparent(QWidget *w, const QPoint &p); + + bool hasBorder(void) const { return _border; } + void setBorder(bool); + + bool isResizable(void) const { return _resizable; } + void setResizable(bool); + + void activate(void); + + bool isUtility(void) const { return _utility; } + void setUtility(bool b); + + void setSizeGrip(bool); + void moveSizeGrip(); + + bool isPersistent(void); + void setPersistent(bool); + + void center(); + void configure(); + + void setName(const char *, CWIDGET *); + + void setState(Qt::WindowStates state); + Qt::WindowStates getState() const; + + void setBetterMask(QPixmap &bg); + + int currentScreen() const; + + virtual void resize(int w, int h); + virtual void setGeometry(int x, int y, int w, int h); + + void setGeometryHints(); + + friend void on_error_show_modal(MODAL_INFO *info); + + virtual bool focusNextPrevChild(bool next); +}; + + +//void CWINDOW_set_top_only(QWidget *w, bool top); +void CWINDOW_activate(CWIDGET *ob); +void CWINDOW_set_default_button(CWINDOW *win, QPushButton *button, bool on); +void CWINDOW_set_cancel_button(CWINDOW *win, QPushButton *button, bool on); +void CWINDOW_define_mask(CWINDOW *_object); +void CWINDOW_ensure_active_window(); +bool CWINDOW_must_quit(); +bool CWINDOW_close_all(bool main); +void CWINDOW_delete_all(bool main); +void CWINDOW_move_resize(void *_object, int x, int y, int w, int h); + +#endif diff --git a/gb.qt4/src/Makefile.am b/gb.qt4/src/Makefile.am new file mode 100644 index 00000000..5df73f8e --- /dev/null +++ b/gb.qt4/src/Makefile.am @@ -0,0 +1,57 @@ +COMPONENT = gb.qt4 +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +SUBDIRS = . @QTEXT_DIR@ @QTWEBKIT_DIR@ @QTWEBVIEW_DIR@ @QTOPENGL_DIR@ +EXTRA_DIST = trayicon.xpm + +gblib_LTLIBRARIES = gb.qt4.la + +gb_qt4_la_LIBADD = @THREAD_LIB@ @QT_LIB@ +gb_qt4_la_LDFLAGS = -module @LD_FLAGS@ @QT_LDFLAGS@ +gb_qt4_la_CXXFLAGS = @THREAD_INC@ -DGB_QT_COMPONENT $(AM_CXXFLAGS) +gb_qt4_la_CPPFLAGS = @QT_INC@ -I$(top_srcdir)/share/ + +gb_qt4_la_SOURCES = \ + x11.h x11.c \ + gb.qt.h main.h main_moc.cpp main.cpp \ + CFont.h CFont.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CWidget.h CWidget_moc.cpp CWidget.cpp \ + CWindow.h CWindow_moc.cpp CWindow.cpp \ + CButton.h CButton_moc.cpp CButton.cpp \ + CContainer.h CContainer_moc.cpp CContainer.cpp \ + CTextBox.h CTextBox_moc.cpp CTextBox.cpp \ + CMenu.h CMenu_moc.cpp CMenu.cpp \ + CMouse.h CMouse_moc.cpp CMouse.cpp \ + CKey.h CKey_moc.cpp CKey.cpp \ + CColor.h CColor_moc.cpp CColor.cpp \ + CConst.h CConst.cpp \ + CCheckBox.h CCheckBox_moc.cpp CCheckBox.cpp \ + CPanel.h CPanel_moc.cpp CPanel.cpp \ + CRadioButton.h CRadioButton_moc.cpp CRadioButton.cpp \ + CTextArea.h CTextArea_moc.cpp CTextArea.cpp \ + CTabStrip.h CTabStrip_moc.cpp CTabStrip.cpp \ + CDialog.h CDialog_moc.cpp CDialog.cpp \ + CPicture.h CPicture_moc.cpp CPicture.cpp \ + CImage.h CImage_moc.cpp CImage.cpp \ + canimation.h canimation_moc.cpp canimation.cpp \ + CClipboard.h CClipboard_moc.cpp CClipboard.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CWatch.h CWatch_moc.cpp CWatch.cpp \ + CDrawingArea.h CDrawingArea_moc.cpp CDrawingArea.cpp \ + CSlider.h CSlider_moc.cpp CSlider.cpp \ + CScrollBar.h CScrollBar_moc.cpp CScrollBar.cpp \ + ctrayicon.h ctrayicon_moc.cpp ctrayicon.cpp \ + CWatcher.h CWatcher_moc.cpp CWatcher.cpp \ + cprinter.h cprinter_moc.cpp cprinter.cpp \ + csvgimage.h csvgimage_moc.cpp csvgimage.cpp \ + fix_style.h fix_style.cpp \ + CEmbedder.h CEmbedder.cpp + +if XWINDOW +gb_qt4_la_SOURCES += CEmbedder_moc.cpp +endif + diff --git a/gb.qt4/src/canimation.cpp b/gb.qt4/src/canimation.cpp new file mode 100644 index 00000000..2bf7a18a --- /dev/null +++ b/gb.qt4/src/canimation.cpp @@ -0,0 +1,189 @@ +/*************************************************************************** + + canimation.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CANIMATION_CPP + +#include "main.h" + +#include "CImage.h" +#include "canimation.h" + +DECLARE_EVENT(EVENT_CHANGE); + +static QHash dict; + +static void free_movie(void *_object) +{ + if (!MOVIE) + return; + + dict.remove(MOVIE); + + delete MOVIE; + THIS->movie = NULL; + + THIS->buffer->close(); + delete THIS->buffer; + + THIS->data->clear(); + delete THIS->data; + + GB.ReleaseFile(THIS->addr, THIS->len); +} + +static void *load_movie(char *path, int len_path) +{ + void *_object; + char *addr; + int len; + + if (GB.LoadFile(path, len_path, &addr, &len)) + return NULL; + + _object = GB.Create(GB.FindClass("Animation"), NULL, NULL); + + THIS->addr = addr; + THIS->len = len; + THIS->data = new QByteArray(); + *THIS->data = QByteArray::fromRawData((const char *)THIS->addr, THIS->len); + THIS->buffer = new QBuffer(THIS->data); + THIS->buffer->open(QIODevice::ReadOnly); + THIS->movie = new QMovie(THIS->buffer); + dict.insert(MOVIE, THIS); + + QObject::connect(THIS->movie, SIGNAL(frameChanged(int)), &CAnimationManager::manager, SLOT(change())); + + return THIS; +} + + +/*BEGIN_METHOD_(Animation_new, GB_STRING path) + + if (load_movie(THIS, STRING(path), LENGTH(path))) + return; + + QObject::connect(MOVIE, SIGNAL(frameChanged(int)), &CAnimationManager::manager, SLOT(change())); + +END_METHOD*/ + +BEGIN_METHOD_VOID(Animation_free) + + free_movie(THIS); + +END_METHOD + +BEGIN_METHOD(Animation_Load, GB_STRING path) + + GB.ReturnObject(load_movie(STRING(path), LENGTH(path))); + +END_METHOD + +/*BEGIN_PROPERTY(CMOVIEBOX_path) + + if (READ_PROPERTY) + GB.ReturnString(THIS->path); + else + { + bool playing = false; + + if (THIS->movie) + playing = THIS->movie->state() == QMovie::Running; + else + playing = FALSE; + + if (load_movie(THIS, PSTRING(), PLENGTH())) + return; + + if (!playing && THIS->movie) + THIS->movie->setPaused(true); + } + +END_PROPERTY*/ + + +BEGIN_PROPERTY(Animation_Playing) + + GB.ReturnBoolean(MOVIE->state() == QMovie::Running); + +END_PROPERTY + +BEGIN_METHOD_VOID(Animation_Play) + + MOVIE->setPaused(false); + +END_METHOD + +BEGIN_METHOD_VOID(Animation_Pause) + + MOVIE->setPaused(true); + +END_METHOD + +BEGIN_METHOD_VOID(Animation_Stop) + + MOVIE->stop(); + +END_METHOD + +BEGIN_PROPERTY(Animation_Image) + + QImage *image = new QImage; + + *image = MOVIE->currentImage(); + GB.ReturnObject(CIMAGE_create(image)); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +GB_DESC AnimationDesc[] = +{ + GB_DECLARE("Animation", sizeof(CANIMATION)), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, Animation_new, "(Path)s"), + GB_METHOD("_free", NULL, Animation_free, NULL), + + GB_STATIC_METHOD("Load", "Animation", Animation_Load, "(Path)s"), + + GB_PROPERTY_READ("Playing", "b", Animation_Playing), + GB_PROPERTY_READ("Image", "Image", Animation_Image), + + GB_METHOD("Play", NULL, Animation_Play, NULL), + GB_METHOD("Stop", NULL, Animation_Stop, NULL), + GB_METHOD("Pause", NULL, Animation_Pause, NULL), + + GB_EVENT("Change", NULL, EVENT_CHANGE, NULL), + + GB_END_DECLARE +}; + +//------------------------------------------------------------------------- + +CAnimationManager CAnimationManager::manager; + +void CAnimationManager::change(void) +{ + void *_object = dict[sender()]; + GB.Raise(THIS, EVENT_CHANGE, 0); +} diff --git a/gb.qt4/src/canimation.h b/gb.qt4/src/canimation.h new file mode 100644 index 00000000..681b9293 --- /dev/null +++ b/gb.qt4/src/canimation.h @@ -0,0 +1,65 @@ +/*************************************************************************** + + canimation.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CANIMATION_H +#define __CANIMATION_H + +#include "gambas.h" + +#include +#include + +#ifndef __CANIMATION_CPP +extern GB_DESC AnimationDesc[]; +#else + +#define THIS ((CANIMATION *)_object) +#define MOVIE (((CANIMATION *)_object)->movie) + +#endif + +typedef + struct { + GB_BASE ob; + QByteArray *data; + QBuffer *buffer; + QMovie *movie; + char *addr; + int len; + } + CANIMATION; + +class CAnimationManager : public QObject +{ + Q_OBJECT + +public: + + static CAnimationManager manager; + +public slots: + + void change(void); +}; + +#endif diff --git a/gb.qt4/src/cpaint_impl.cpp b/gb.qt4/src/cpaint_impl.cpp new file mode 100644 index 00000000..1f145046 --- /dev/null +++ b/gb.qt4/src/cpaint_impl.cpp @@ -0,0 +1,1671 @@ +/*************************************************************************** + + cpaint_impl.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPAINT_IMPL_CPP + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif +#include + +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CFont.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CPicture.h" +#include "CImage.h" +#include "CDrawingArea.h" +#include "CContainer.h" +#include "CColor.h" +#include "CDraw.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "cpaint_impl.h" + +/*class ClipInfo +{ +public: + QPainterPath *path; + GB_RECT *rect; + + ClipInfo() { path = NULL; rect = NULL; } + ~ClipInfo() { delete path; delete rect; } +};*/ + +typedef + struct { + QPainter *painter; + QPainterPath *path; + int fillRule; + QTransform *init; + float bx, by; + //QPainterPath *clip; + //GB_RECT *clipRect; + //QList *clipStack; + } + QT_PAINT_EXTRA; + +#define EXTRA(d) ((QT_PAINT_EXTRA *)d->extra) + +#define COLOR_TO_INT(color) ((color).rgba() ^ 0xFF000000) +#define MASK_COLOR(col) ((col & 0xFF000000) ? Qt::color0 : Qt::color1) + +#define PAINTER(d) EXTRA(d)->painter +#define PATH(d) EXTRA(d)->path +//#define CLIP(d) EXTRA(d)->clip + +static bool _internal_paint = false; + +static inline qreal to_deg(float angle) +{ + return (qreal)(angle * 180 / M_PI); +} + +static bool init_painting(GB_PAINT *d, QPaintDevice *device) +{ + QPen pen; + GB_COLOR col; + + d->area.width = device->width(); + d->area.height = device->height(); + d->resolutionX = device->physicalDpiX(); + d->resolutionY = device->physicalDpiY(); + + if (!PAINTER(d)) + { + if (device->paintingActive()) + { + GB.Error("Device already being painted"); + return TRUE; + } + + EXTRA(d)->painter = new QPainter(device); + } + +#ifndef QT5 + MyPaintEngine *engine = (MyPaintEngine *)device->paintEngine(); + engine->patchFeatures(); +#endif + + //EXTRA(d)->path = NULL; + //EXTRA(d)->clip = NULL; + //EXTRA(d)->clipRect = NULL; + + EXTRA(d)->init = new QTransform(); + *(EXTRA(d)->init) = PAINTER(d)->worldTransform(); + + PAINTER(d)->setRenderHints(QPainter::Antialiasing, true); + PAINTER(d)->setRenderHints(QPainter::TextAntialiasing, true); + PAINTER(d)->setRenderHints(QPainter::SmoothPixmapTransform, true); + PAINTER(d)->setLayoutDirection(Qt::LayoutDirectionAuto); + + if (GB.Is(d->device, CLASS_Control)) + col = CWIDGET_get_real_foreground((CWIDGET *)d->device); + else + col = 0; + + pen = PAINTER(d)->pen(); + pen.setCapStyle(Qt::FlatCap); + pen.setJoinStyle(Qt::MiterJoin); + pen.setMiterLimit(10.0); + pen.setWidthF(1.0); + pen.setColor(CCOLOR_make(col)); + PAINTER(d)->setPen(pen); + + PAINTER(d)->setBrush(CCOLOR_make(col)); + + return FALSE; +} + +static QColor get_color(GB_PAINT *d, GB_COLOR col) +{ + if (col == GB_COLOR_DEFAULT) + { + if (GB.Is(d->device, CLASS_Control)) + col = CWIDGET_get_real_foreground((CWIDGET *)d->device); + else + col = 0xFFFFFF; + } + + return CCOLOR_make(col); +} + +/*static void begin_clipping(GB_PAINT *d) +{ + if (CLIP(d)) + { + GB_RECT *r; + QTransform save = PAINTER(d)->worldTransform(); + PAINTER(d)->resetTransform(); + r = EXTRA(d)->clipRect; + if (r) + PAINTER(d)->setClipRect(r->x, r->y, r->w, r->h); + else + PAINTER(d)->setClipPath(*CLIP(d)); + PAINTER(d)->setWorldTransform(save); + } +} + +static void end_clipping(GB_PAINT *d) +{ + if (CLIP(d)) + PAINTER(d)->setClipping(false); +}*/ + +#define begin_clipping(_d) +#define end_clipping(_d) + +//--------------------------------------------------------------------------- + +static int Begin(GB_PAINT *d) +{ + void *device = d->device; + QPaintDevice *target = NULL; + + if (GB.Is(device, CLASS_Picture)) + { + QPixmap *pixmap = ((CPICTURE *)device)->pixmap; + + if (pixmap->isNull()) + { + GB.Error("Bad picture"); + return TRUE; + } + + target = pixmap; + } + else if (GB.Is(device, CLASS_Image)) + { + QImage *image = CIMAGE_get((CIMAGE *)device); + + if (image->isNull()) + { + GB.Error("Bad image"); + return TRUE; + } + + target = image; + } + else if (GB.Is(device, CLASS_DrawingArea)) + { + MyDrawingArea *wid; + + wid = (MyDrawingArea *)(((CWIDGET *)device)->widget); + + if (wid->isCached()) + target = wid->getBackgroundPixmap(); + else if (wid->cache) + target = wid->cache; + else + { + if (!wid->inDrawEvent()) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + + target = wid; + } + + wid->drawn++; + + if (init_painting(d, target)) + return TRUE; + + d->area.width = wid->width(); + d->area.height = wid->height(); + return FALSE; + } + else if (GB.Is(device, CLASS_UserControl)) + { + MyContainer *wid; + + wid = (MyContainer *)(((CWIDGET *)device)->widget); + + if (!_internal_paint) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + + target = wid; + + if (init_painting(d, target)) + return TRUE; + + d->area.width = wid->width(); + d->area.height = wid->height(); + return FALSE; + } + else if (GB.Is(device, CLASS_Printer)) + { + CPRINTER *printer = (CPRINTER *)device; + + if (!printer->printing) + { + GB.Error("Printer is not printing"); + return TRUE; + } + + target = printer->printer; + + if (init_painting(d, target)) + return TRUE; + // Paint.W / $hPrinter.PaperWidth * 25.4 / $hPrinter.Resolution + QSizeF size = CPRINTER_get_page_size(printer); + //qDebug("d->area.width = %g / paper width = %g / resolution = %d", d->area.width, (floor((double)size.width() * 1E6) / 1E6), printer->printer->resolution()); + d->fontScale = 25.4 * d->area.width / (floor((double)size.width() * 1E6) / 1E6) / printer->printer->resolution(); + + return FALSE; + } + else if (GB.Is(device, CLASS_SvgImage)) + { + CSVGIMAGE *svgimage = (CSVGIMAGE *)device; + target = SVGIMAGE_begin(svgimage, &EXTRA(d)->painter); + if (!target) + { + GB.Error("SvgImage size is not defined"); + return TRUE; + } + } + + return init_painting(d, target); +} + +static void End(GB_PAINT *d) +{ + void *device = d->device; + QT_PAINT_EXTRA *dx = EXTRA(d); + + if (GB.Is(device, CLASS_DrawingArea)) + { + MyDrawingArea *wid; + + wid = (MyDrawingArea *)(((CWIDGET *)device)->widget); + + if (wid) + { + if (wid->isCached()) + wid->refreshBackground(); + + wid->drawn--; + } + } + else if (GB.Is(device, CLASS_SvgImage)) + { + PAINTER(d)->end(); // ?? + } + + /*if (dx->clipStack) + { + while (!dx->clipStack->isEmpty()) + delete dx->clipStack->takeLast(); + delete dx->clipStack; + }*/ + + delete dx->init; + delete dx->path; + //delete dx->clip; + delete dx->painter; +} + +static void Save(GB_PAINT *d) +{ + //QT_PAINT_EXTRA *dx = EXTRA(d); + //ClipInfo *ci; + + PAINTER(d)->save(); + + /*if (!dx->clipStack) + dx->clipStack = new QList; + + ci = new ClipInfo; + if (dx->clip) + ci->path = new QPainterPath(*dx->clip); + if (dx->clipRect) + { + ci->rect = new GB_RECT; + *ci->rect = *dx->clipRect; + } + + dx->clipStack->append(ci);*/ +} + +static void Restore(GB_PAINT *d) +{ + //QT_PAINT_EXTRA *dx = EXTRA(d); + + PAINTER(d)->restore(); + + /*if (dx->clipStack && !dx->clipStack->isEmpty()) + { + ClipInfo *ci = dx->clipStack->takeLast(); + + delete dx->clip; + dx->clip = ci->path ? new QPainterPath(*(ci->path)) : NULL; + + delete dx->clipRect; + if (ci->rect) + { + dx->clipRect = new GB_RECT; + *dx->clipRect = *ci->rect; + } + else + dx->clipRect = NULL; + + delete ci; + }*/ +} + +static void Antialias(GB_PAINT *d, int set, int *antialias) +{ + if (set) + PAINTER(d)->setRenderHint(QPainter::Antialiasing, *antialias); + else + *antialias = PAINTER(d)->testRenderHint(QPainter::Antialiasing) ? 1 : 0; +} + +static void set_painter_font(QPainter *p, const QFont &f) +{ + QFont pf; + + pf.setFamily(f.family()); + pf.setPointSizeF(f.pointSizeF()); + pf.setWeight(f.weight()); + pf.setStyle(f.style()); + pf.setUnderline(f.underline()); + pf.setStrikeOut(f.strikeOut()); + + p->setFont(pf); + + // Strange bug of QT. Sometimes the font does not apply (cf. DrawTextShadow) + + /*if (pf != p->font()) + { + fprintf(stderr, "set_painter_font: %s / %s\n", TO_UTF8(f.toString()), TO_UTF8(p->font().toString())); + QFont f2; + f2.fromString(f.toString()); + p->setFont(f2); + }*/ +} + +static void apply_font(QFont &font, void *object = 0) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + QFont f = font; + + if (d->fontScale != 1) + f.setPointSizeF(f.pointSizeF() * d->fontScale); + + set_painter_font(PAINTER(d), f); +} + +static void Font(GB_PAINT *d, int set, GB_FONT *font) +{ + QFont f; + + if (set) + { + if (*font) + f = QFont(*((CFONT *)(*font))->font); + else if ((GB.Is(d->device, CLASS_DrawingArea) || GB.Is(d->device, CLASS_UserControl))) + f = (((CWIDGET *)d->device)->widget)->font(); + + apply_font(f); + } + else + { + f = PAINTER(d)->font(); + if (d->fontScale != 1) + f.setPointSizeF(f.pointSizeF() / d->fontScale); + *font = CFONT_create(f, apply_font); + } +} + +static void init_path(GB_PAINT *d) +{ + switch (EXTRA(d)->fillRule) + { + case GB_PAINT_FILL_RULE_WINDING: + PATH(d)->setFillRule(Qt::WindingFill); + break; + case GB_PAINT_FILL_RULE_EVEN_ODD: + default: + PATH(d)->setFillRule(Qt::OddEvenFill); + } +} + +#define CHECK_PATH(_d) \ + if (!PATH(_d)) \ + return; \ + else \ + init_path(_d); + +#define PRESERVE_PATH(_d, _p) \ + if (!(_p)) \ + { \ + delete PATH(_d); \ + EXTRA(_d)->path = 0; \ + } + +#define CREATE_PATH(_d) \ + if (!PATH(_d)) \ + EXTRA(_d)->path = new QPainterPath(); + +/*static void delete_clip_rect(GB_PAINT *d) +{ + if (EXTRA(d)->clipRect) + { + delete EXTRA(d)->clipRect; + EXTRA(d)->clipRect = NULL; + } +}*/ + +static void Clip(GB_PAINT *d, int preserve) +{ + CHECK_PATH(d); + + PAINTER(d)->setClipPath(*PATH(d), PAINTER(d)->hasClipping() ? Qt::IntersectClip : Qt::ReplaceClip); + + /*QPainterPath path = PAINTER(d)->worldTransform().map(*PATH(d)); + + if (CLIP(d)) + path = CLIP(d)->intersected(path); + + delete EXTRA(d)->clip; + EXTRA(d)->clip = new QPainterPath(path); + delete_clip_rect(d);*/ + + PRESERVE_PATH(d, preserve); +} + +static void ResetClip(GB_PAINT *d) +{ + /*delete CLIP(d); + EXTRA(d)->clip = 0; + delete_clip_rect(d);*/ + PAINTER(d)->setClipping(false); +} + +static void get_path_extents(QPainterPath *path, GB_EXTENTS *ext, const QTransform &transform) +{ + if (!path) + { + ext->x1 = ext->x2 = ext->y1 = ext->y2 = 0.0; + return; + } + + QRectF rect = transform.inverted().mapRect(path->boundingRect()); + + ext->x1 = (float)rect.left(); + ext->y1 = (float)rect.top(); + ext->x2 = (float)rect.right(); + ext->y2 = (float)rect.bottom(); +} + +static void ClipExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + /*GB_RECT *rect = EXTRA(d)->clipRect; + if (rect) + { + ext->x1 = (float)rect->x; + ext->y1 = (float)rect->y; + ext->x2 = (float)(rect->x + rect->w); + ext->y2 = (float)(rect->y + rect->h); + } + else*/ + QPainterPath path = PAINTER(d)->clipPath(); + get_path_extents(&path, ext, QTransform()); //PAINTER(d)->transform()); +} + +static void Fill(GB_PAINT *d, int preserve) +{ + CHECK_PATH(d); + + //if (!CLIP(d)) + PAINTER(d)->fillPath(*PATH(d), PAINTER(d)->brush()); + /*else + { + QPainterPath path = PAINTER(d)->worldTransform().inverted().map(*CLIP(d)); + path = path.intersected(*PATH(d)); + PAINTER(d)->fillPath(path, PAINTER(d)->brush()); + }*/ + + PRESERVE_PATH(d, preserve); +} + +static void Stroke(GB_PAINT *d, int preserve) +{ + CHECK_PATH(d); + + if (PAINTER(d)->pen().widthF() > 0.0) + { + //if (!CLIP(d)) + PAINTER(d)->strokePath(*PATH(d), PAINTER(d)->pen()); + /*else + { + QPainterPathStroker stroker; + QPen pen = PAINTER(d)->pen(); + + stroker.setCapStyle(pen.capStyle()); + stroker.setDashOffset(pen.dashOffset()); + stroker.setDashPattern(pen.dashPattern()); + stroker.setJoinStyle(pen.joinStyle()); + stroker.setMiterLimit(pen.miterLimit()); + stroker.setWidth(pen.widthF()); + + QPainterPath path = PAINTER(d)->worldTransform().inverted().map(*CLIP(d)); + path = path.intersected(stroker.createStroke(*PATH(d))); + PAINTER(d)->fillPath(path, PAINTER(d)->brush()); + }*/ + } + + PRESERVE_PATH(d, preserve); +} + +static void PathExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + get_path_extents(PATH(d), ext, PAINTER(d)->transform()); +} + +static int PathContains(GB_PAINT *d, float x, float y) +{ + if (!PATH(d)) + return FALSE; + + QPointF point((qreal)x, (qreal)y); + return PATH(d)->contains(point); +} + +static void PathOutline(GB_PAINT *d, GB_PAINT_OUTLINE_CB cb) +{ + QPainterPath *p = PATH(d); + int i, j; + + if (!p) + return; + + QList qoutline = p->toSubpathPolygons(QTransform()); + + for (i = 0; i < qoutline.count(); i++) + { + QPolygonF qpolygon = qoutline.at(i); + + for (j = 0; j < qpolygon.count(); j++) + { + QPointF qpoint = qpolygon.at(j); + (*cb)(j == 0 ? GB_PAINT_PATH_MOVE : GB_PAINT_PATH_LINE, qpoint.x(), qpoint.y()); + } + } +} + +#define DASH_ZERO 0.0009765625 + +static void Dash(GB_PAINT *d, int set, float **dashes, int *count) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + if (!*count) + pen.setStyle(Qt::SolidLine); + else + { + QVector dv; + qreal d = 0; + + for (int i = 0; i < *count; i++) + { + d = (*dashes)[i]; + if (d == 0.0) + d = DASH_ZERO; + dv << (qreal)d; + } + + if (*count == 1) + dv << (qreal)d; + + pen.setStyle(Qt::CustomDashLine); + pen.setDashPattern(dv); + } + PAINTER(d)->setPen(pen); + } + else + { + if (pen.style() == Qt::CustomDashLine) + { + QVector dv = pen.dashPattern(); + float d; + *count = dv.count(); + GB.Alloc(POINTER(dashes), sizeof(float) * *count); + for (int i = 0; i < *count; i++) + { + d = (float)dv[i]; + if (d <= DASH_ZERO) + d = 0.0; + (*dashes)[i] = d; + } + } + else + { + *count = 0; + *dashes = NULL; + } + } +} + +static void DashOffset(GB_PAINT *d, int set, float *offset) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + pen.setDashOffset((qreal)*offset); + PAINTER(d)->setPen(pen); + } + else + { + *offset = (float)pen.dashOffset(); + } +} + + +static void FillRule(GB_PAINT *d, int set, int *value) +{ + if (set) + { + EXTRA(d)->fillRule = *value; + } + else + *value = EXTRA(d)->fillRule; +} + +static void FillStyle(GB_PAINT *d, int set, int *style) +{ + /*if (set) + { + EXTRA(d)->fillRule = *value; + } + else + *value = EXTRA(d)->fillRule;*/ +} + +static void LineCap(GB_PAINT *d, int set, int *value) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + switch (*value) + { + case GB_PAINT_LINE_CAP_ROUND: + pen.setCapStyle(Qt::RoundCap); break; + case GB_PAINT_LINE_CAP_SQUARE: + pen.setCapStyle(Qt::SquareCap); break; + case GB_PAINT_LINE_CAP_BUTT: + default: + pen.setCapStyle(Qt::FlatCap); + } + PAINTER(d)->setPen(pen); + } + else + { + switch (pen.capStyle()) + { + case Qt::RoundCap: *value = GB_PAINT_LINE_CAP_ROUND; break; + case Qt::SquareCap: *value = GB_PAINT_LINE_CAP_SQUARE; break; + case Qt::FlatCap: default: *value = GB_PAINT_LINE_CAP_BUTT; + } + } +} + +static void LineJoin(GB_PAINT *d, int set, int *value) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + switch (*value) + { + case GB_PAINT_LINE_JOIN_ROUND: + pen.setJoinStyle(Qt::RoundJoin); break; + case GB_PAINT_LINE_JOIN_BEVEL: + pen.setJoinStyle(Qt::BevelJoin); break; + case GB_PAINT_LINE_JOIN_MITER: + default: + pen.setJoinStyle(Qt::MiterJoin); + } + PAINTER(d)->setPen(pen); + } + else + { + switch (pen.joinStyle()) + { + case Qt::RoundJoin: *value = GB_PAINT_LINE_JOIN_ROUND; break; + case Qt::BevelJoin: *value = GB_PAINT_LINE_JOIN_BEVEL; break; + case Qt::MiterJoin: default: *value = GB_PAINT_LINE_JOIN_MITER; + } + } +} + +static void LineWidth(GB_PAINT *d, int set, float *value) +{ + QPen pen = PAINTER(d)->pen(); + if (set) + { + pen.setWidthF((qreal)*value); + PAINTER(d)->setPen(pen); + } + else + *value = (float)pen.widthF(); +} + +static void MiterLimit(GB_PAINT *d, int set, float *value) +{ + QPen pen = PAINTER(d)->pen(); + if (set) + { + pen.setMiterLimit((qreal)*value); + PAINTER(d)->setPen(pen); + } + else + *value = (float)pen.miterLimit(); +} + + +static void Operator(GB_PAINT *d, int set, int *value) +{ + QPainter::CompositionMode mode; + + if (set) + { + switch (*value) + { + case GB_PAINT_OPERATOR_CLEAR: mode = QPainter::CompositionMode_Clear; break; + case GB_PAINT_OPERATOR_SOURCE: mode = QPainter::CompositionMode_Source; break; + case GB_PAINT_OPERATOR_IN: mode = QPainter::CompositionMode_SourceIn; break; + case GB_PAINT_OPERATOR_OUT: mode = QPainter::CompositionMode_SourceOut; break; + case GB_PAINT_OPERATOR_ATOP: mode = QPainter::CompositionMode_SourceAtop; break; + case GB_PAINT_OPERATOR_DEST: mode = QPainter::CompositionMode_Destination; break; + case GB_PAINT_OPERATOR_DEST_OVER: mode = QPainter::CompositionMode_DestinationOver; break; + case GB_PAINT_OPERATOR_DEST_IN: mode = QPainter::CompositionMode_DestinationIn; break; + case GB_PAINT_OPERATOR_DEST_OUT: mode = QPainter::CompositionMode_DestinationOut; break; + case GB_PAINT_OPERATOR_DEST_ATOP: mode = QPainter::CompositionMode_DestinationAtop; break; + case GB_PAINT_OPERATOR_XOR: mode = QPainter::CompositionMode_Xor; break; + case GB_PAINT_OPERATOR_ADD: mode = QPainter::CompositionMode_Plus; break; + case GB_PAINT_OPERATOR_SATURATE: mode = QPainter::CompositionMode_Multiply; break; + case GB_PAINT_OPERATOR_OVER: default: mode = QPainter::CompositionMode_SourceOver; + } + PAINTER(d)->setCompositionMode(mode); + } + else + { + switch (PAINTER(d)->compositionMode()) + { + case QPainter::CompositionMode_Clear: *value = GB_PAINT_OPERATOR_CLEAR; break; + case QPainter::CompositionMode_Source: *value = GB_PAINT_OPERATOR_SOURCE; break; + case QPainter::CompositionMode_SourceIn: *value = GB_PAINT_OPERATOR_IN; break; + case QPainter::CompositionMode_SourceOut: *value = GB_PAINT_OPERATOR_OUT; break; + case QPainter::CompositionMode_SourceAtop: *value = GB_PAINT_OPERATOR_ATOP; break; + case QPainter::CompositionMode_Destination: *value = GB_PAINT_OPERATOR_DEST; break; + case QPainter::CompositionMode_DestinationOver: *value = GB_PAINT_OPERATOR_DEST_OVER; break; + case QPainter::CompositionMode_DestinationIn: *value = GB_PAINT_OPERATOR_DEST_IN; break; + case QPainter::CompositionMode_DestinationOut: *value = GB_PAINT_OPERATOR_DEST_OUT; break; + case QPainter::CompositionMode_DestinationAtop: *value = GB_PAINT_OPERATOR_DEST_ATOP; break; + case QPainter::CompositionMode_Xor: *value = GB_PAINT_OPERATOR_XOR; break; + case QPainter::CompositionMode_Plus: *value = GB_PAINT_OPERATOR_ADD; break; + case QPainter::CompositionMode_Multiply: *value = GB_PAINT_OPERATOR_SATURATE; break; + case QPainter::CompositionMode_SourceOver: default: *value = GB_PAINT_OPERATOR_OVER; + } + } +} + + +static void NewPath(GB_PAINT *d) +{ + PRESERVE_PATH(d, FALSE); +} + +static void ClosePath(GB_PAINT *d) +{ + CHECK_PATH(d); + PATH(d)->closeSubpath(); +} + + +static void Arc(GB_PAINT *d, float xc, float yc, float radius, float angle, float length, bool pie) +{ + CREATE_PATH(d); + + QRectF rect; + rect.setCoords((qreal)(xc - radius), (qreal)(yc - radius), (qreal)(xc + radius), (qreal)(yc + radius)); + + angle = - angle; + length = - length; + + if (pie) + PATH(d)->moveTo(xc, yc); + else + PATH(d)->arcMoveTo(rect, to_deg(angle)); + + PATH(d)->arcTo(rect, to_deg(angle), to_deg(length)); + + if (pie) + PATH(d)->closeSubpath(); +} + +static void Ellipse(GB_PAINT *d, float x, float y, float width, float height, float angle, float length, bool pie) +{ + CREATE_PATH(d); + + QRectF rect; + rect.setCoords((qreal)x, (qreal)y, (qreal)x + width, (qreal)y + height); + + angle = - angle; + length = - length; + + if (pie) + PATH(d)->moveTo(x + width / 2, y + height / 2); + else + PATH(d)->arcMoveTo(rect, to_deg(angle)); + + PATH(d)->arcTo(rect, to_deg(angle), to_deg(length)); + if (pie) + //PATH(d)->lineTo(x + width / 2, y + height / 2); + PATH(d)->closeSubpath(); +} + +static void Rectangle(GB_PAINT *d, float x, float y, float width, float height) +{ + CREATE_PATH(d); + PATH(d)->addRect((qreal)x, (qreal)y, (qreal)width, (qreal)height); +} + +static void ClipRect(GB_PAINT *d, int x, int y, int w, int h) +{ + //GB_RECT *rect; + ResetClip(d); + Rectangle(d, x, y, w, h); + Clip(d, FALSE); + + /*rect = new GB_RECT; + rect->x = x; + rect->y = y; + rect->w = w; + rect->h = h; + EXTRA(d)->clipRect = rect;*/ +} + +static void GetCurrentPoint(GB_PAINT *d, float *x, float *y) +{ + if (!PATH(d)) + { + *x = 0; + *y = 0; + return; + } + + QPointF pt = PATH(d)->currentPosition(); + *x = (float)pt.x(); + *y = (float)pt.y(); +} + +static void MoveTo(GB_PAINT *d, float x, float y) +{ + CREATE_PATH(d); + PATH(d)->moveTo((qreal)x, (qreal)y); +} + +static void LineTo(GB_PAINT *d, float x, float y) +{ + CREATE_PATH(d); + PATH(d)->lineTo((qreal)x, (qreal)y); +} + +static void CurveTo(GB_PAINT *d, float x1, float y1, float x2, float y2, float x3, float y3) +{ + CREATE_PATH(d); + PATH(d)->cubicTo(QPointF((qreal)x1, (qreal)y1), QPointF((qreal)x2, (qreal)y2), QPointF((qreal)x3, (qreal)y3)); +} + +static QStringList text_sl; +static QVector text_w; +static float text_line; + +static float get_text_width(QPainter *p, QString &s) +{ + float w, width = 0; + int i; + QFontMetricsF fm(p->font()); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + text_sl = s.split('\n', Qt::KeepEmptyParts); +#else + text_sl = s.split('\n', QString::KeepEmptyParts); +#endif + + text_w.resize(text_sl.count()); + + for (i = 0; i < (int)text_sl.count(); i++) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + w = fm.horizontalAdvance(text_sl[i]); +#else + w = fm.width(text_sl[i]); +#endif + if (w > width) width = w; + text_w[i] = w; + } + + return width; +} + +static int get_text_height(QPainter *p, QString &s) +{ + QFontMetricsF fm(p->font()); + text_line = fm.height(); + return text_line * (1 + s.count('\n')); +} + +static QPainterPath *_draw_path; +static float _draw_x, _draw_y; + +static void draw_text(GB_PAINT *d, bool rich, const char *text, int len, float w, float h, int align, bool draw) +{ + QPointF pos; + + GetCurrentPoint(d, &_draw_x, &_draw_y); + + if (w < 0 && h < 0) + _draw_y -= PAINTER(d)->fontMetrics().ascent(); + + if (draw) + { + begin_clipping(d); + + if (rich) + DRAW_rich_text(PAINTER(d), QString::fromUtf8(text, len), _draw_x, _draw_y, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + else + DRAW_text(PAINTER(d), QString::fromUtf8(text, len), _draw_x, _draw_y, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + + end_clipping(d); + } + else + { + CREATE_PATH(d); + + _draw_path = PATH(d); + + MyPaintDevice device; + QPainter p(&device); + + set_painter_font(&p, PAINTER(d)->font()); + p.setPen(PAINTER(d)->pen()); + p.setBrush(PAINTER(d)->brush()); + + if (rich) + DRAW_rich_text(&p, QString::fromUtf8(text, len), 0, 0, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + else + DRAW_text(&p, QString::fromUtf8(text, len), 0, 0, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + + p.end(); + _draw_path = NULL; + } +} + +static void Text(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, false, text, len, w, h, align, draw); +} + +static void RichText(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, true, text, len, w, h, align, draw); +} + +static void get_text_extents(GB_PAINT *d, bool rich, const char *text, int len, GB_EXTENTS *ext, float width) +{ + QPainterPath path; + MyPaintDevice device; + QPainter p(&device); + QFont f = PAINTER(d)->font(); + + set_painter_font(&p, f); + + _draw_path = &path; + GetCurrentPoint(d, &_draw_x, &_draw_y); + _draw_y -= PAINTER(d)->fontMetrics().ascent(); + + if (rich) + DRAW_rich_text(&p, QString::fromUtf8(text, len), 0, 0, width, -1, CCONST_alignment(ALIGN_TOP_NORMAL, ALIGN_TOP_NORMAL, true)); + else + DRAW_text(&p, QString::fromUtf8(text, len), 0, 0, -1, -1, CCONST_alignment(ALIGN_TOP_NORMAL, ALIGN_TOP_NORMAL, true)); + + p.end(); + + get_path_extents(&path, ext, QTransform()); + _draw_path = NULL; +} + +static void TextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext) +{ + get_text_extents(d, false, text, len, ext, -1); +} + +static void RichTextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext, float width) +{ + get_text_extents(d, true, text, len, ext, width); +} + +static void TextSize(GB_PAINT *d, const char *text, int len, float *w, float *h) +{ + if (len == 0) + { + if (w) *w = 0; + if (h) *h = 0; + return; + } + + QString s = QString::fromUtf8((const char *)text, len); + if (w) *w = get_text_width(PAINTER(d), s); + if (h) *h = get_text_height(PAINTER(d), s); +} + +static void RichTextSize(GB_PAINT *d, const char *text, int len, float sw, float *w, float *h) +{ + QTextDocument rt; + + DRAW_init_rich_text(&rt, PAINTER(d)->font()); + + rt.setHtml(QString::fromUtf8((const char *)text, len)); + + if (sw > 0) + rt.setTextWidth(sw); + + *w = rt.idealWidth(); + *h = rt.size().height(); +} + +static void Matrix(GB_PAINT *d, int set, GB_TRANSFORM matrix) +{ + QTransform *t = (QTransform *)matrix; + + if (set) + { + if (t) + PAINTER(d)->setWorldTransform(*t); + else + PAINTER(d)->setWorldTransform(*EXTRA(d)->init); + } + else + *t = PAINTER(d)->worldTransform(); +} + + +static void SetBrush(GB_PAINT *d, GB_BRUSH brush) +{ + QBrush *b = (QBrush *)brush; + PAINTER(d)->setBrush(*b); + + QPen p = PAINTER(d)->pen(); + p.setBrush(*b); + PAINTER(d)->setPen(p); + //PAINTER(d)->setBrushOrigin(QPointF((qreal)x, (qreal)y)); +} + +static void BrushOrigin(GB_PAINT *d, int set, float *x, float *y) +{ + if (set) + { + EXTRA(d)->bx = *x; + EXTRA(d)->by = *y; + PAINTER(d)->setBrushOrigin(*x, *y); + } + else + { + *x = EXTRA(d)->bx; + *y = EXTRA(d)->by; + } +} + +static void Background(GB_PAINT *d, int set, GB_COLOR *color) +{ + if (set) + { + QBrush b(get_color(d, *color)); + SetBrush(d, (GB_BRUSH)&b); + } + else + { + *color = COLOR_TO_INT(PAINTER(d)->brush().color()); + } +} + + +static void Invert(GB_PAINT *d, int set, int *invert) +{ + if (set) + { + #if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) + PAINTER(d)->setCompositionMode(*invert ? QPainter::RasterOp_SourceXorDestination : QPainter::CompositionMode_SourceOver); + #else + fprintf(stderr, "gb.qt4: warning: Draw.Invert needs at least Qt 4.5\n"); + #endif + } + else + { + #if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) + *invert = PAINTER(d)->compositionMode() == QPainter::RasterOp_SourceXorDestination; //QPainter::CompositionMode_Xor; + #else + *invert = FALSE; + #endif + } +} + +static void DrawImage(GB_PAINT *d, GB_IMAGE image, float x, float y, float w, float h, float opacity, GB_RECT *source) +{ + QImage *img = CIMAGE_get((CIMAGE *)image); + QRectF rect(x, y, w, h); + + begin_clipping(d); + + PAINTER(d)->setOpacity(opacity); + + if (source) + { + bool smooth = PAINTER(d)->testRenderHint(QPainter::SmoothPixmapTransform); + + if (w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + PAINTER(d)->setRenderHint(QPainter::SmoothPixmapTransform, false); + + QRectF srect(source->x, source->y, source->w, source->h); + PAINTER(d)->drawImage(rect, *img, srect); + + PAINTER(d)->setRenderHint(QPainter::SmoothPixmapTransform, smooth); + } + else + PAINTER(d)->drawImage(rect, *img); + + PAINTER(d)->setOpacity(1.0); + + end_clipping(d); +} + +static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source) +{ + QPixmap *pix = ((CPICTURE *)picture)->pixmap; + QRectF rect(x, y, w, h); + QRectF srect; + + if (source) + srect = QRectF(source->x, source->y, source->w, source->h); + else + srect = QRectF(0, 0, pix->width(), pix->height()); + + begin_clipping(d); + + PAINTER(d)->drawPixmap(rect, *pix, srect); + + end_clipping(d); +} + +static void GetPictureInfo(GB_PAINT *d, GB_PICTURE picture, GB_PICTURE_INFO *info) +{ + QPixmap *p = ((CPICTURE *)picture)->pixmap; + + info->width = p->width(); + info->height = p->height(); +} + +static void FillRect(GB_PAINT *d, float x, float y, float w, float h, GB_COLOR color) +{ + begin_clipping(d); + PAINTER(d)->fillRect(x, y, w, h, get_color(d, color)); + end_clipping(d); +} + +static void BrushFree(GB_BRUSH brush) +{ + delete (QBrush *)brush; +} + +static void BrushColor(GB_BRUSH *brush, GB_COLOR color) +{ + QBrush *br = new QBrush(CCOLOR_make(color)); + *brush = (GB_BRUSH)br; +} + +static void BrushImage(GB_BRUSH *brush, GB_IMAGE image) +{ + QImage img(*CIMAGE_get((CIMAGE *)image)); + + img.detach(); + QBrush *br = new QBrush(img); + *brush = (GB_BRUSH)br; +} + +static void BrushLinearGradient(GB_BRUSH *brush, float x0, float y0, float x1, float y1, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + QLinearGradient gradient; + int i; + + gradient.setStart((qreal)x0, (qreal)y0); + gradient.setFinalStop((qreal)x1, (qreal)y1); + + for (i = 0; i < nstop; i++) + gradient.setColorAt((qreal)positions[i], CCOLOR_make(colors[i])); + + switch (extend) + { + case GB_PAINT_EXTEND_REPEAT: + gradient.setSpread(QGradient::RepeatSpread); break; + case GB_PAINT_EXTEND_REFLECT: + gradient.setSpread(QGradient::ReflectSpread); break; + case GB_PAINT_EXTEND_PAD: + default: + gradient.setSpread(QGradient::PadSpread); + } + + QBrush *br = new QBrush(gradient); + *brush = br; +} + +static void BrushRadialGradient(GB_BRUSH *brush, float cx, float cy, float r, float fx, float fy, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + QRadialGradient gradient; + int i; + + gradient.setCenter((qreal)cx, (qreal)cy); + gradient.setRadius((qreal)r); + gradient.setFocalPoint((qreal)fx, (qreal)fy); + + for (i = 0; i < nstop; i++) + gradient.setColorAt((qreal)positions[i], CCOLOR_make(colors[i])); + + switch (extend) + { + case GB_PAINT_EXTEND_REPEAT: + gradient.setSpread(QGradient::RepeatSpread); break; + case GB_PAINT_EXTEND_REFLECT: + gradient.setSpread(QGradient::ReflectSpread); break; + case GB_PAINT_EXTEND_PAD: + default: + gradient.setSpread(QGradient::PadSpread); + } + + QBrush *br = new QBrush(gradient); + *brush = br; +} + +static void BrushMatrix(GB_BRUSH brush, int set, GB_TRANSFORM matrix) +{ + QBrush *b = (QBrush *)brush; + QTransform *t = (QTransform *)matrix; + + if (set) + { + if (t) + b->setTransform(*t); + else + b->setTransform(QTransform()); + } + else + *t = b->transform(); +} + +static void TransformCreate(GB_TRANSFORM *matrix) +{ + *matrix = (GB_TRANSFORM)(new QTransform()); +} + +static void TransformCopy(GB_TRANSFORM *matrix, GB_TRANSFORM copy) +{ + *matrix = (GB_TRANSFORM)(new QTransform(*(QTransform *)copy)); +} + +static void TransformDelete(GB_TRANSFORM *matrix) +{ + delete (QTransform *)*matrix; + *matrix = 0; +} + +static void TransformInit(GB_TRANSFORM matrix, float xx, float yx, float xy, float yy, float x0, float y0) +{ + QTransform *t = (QTransform *)matrix; + QMatrix m((qreal)xx, (qreal)yx, (qreal)xy, (qreal)yy, (qreal)x0, (qreal)y0); + QTransform t2(m); + *t = t2; +} + +static void TransformTranslate(GB_TRANSFORM matrix, float tx, float ty) +{ + QTransform *t = (QTransform *)matrix; + t->translate((qreal)tx, (qreal)ty); +} + +static void TransformScale(GB_TRANSFORM matrix, float sx, float sy) +{ + QTransform *t = (QTransform *)matrix; + t->scale((qreal)sx, (qreal)sy); +} + +static void TransformRotate(GB_TRANSFORM matrix, float angle) +{ + QTransform *t = (QTransform *)matrix; + t->rotateRadians(-angle); +} + +static int TransformInvert(GB_TRANSFORM matrix) +{ + QTransform *t = (QTransform *)matrix; + bool inv; + QTransform it = t->inverted(&inv); + if (inv) + { + *t = it; + return FALSE; + } + else + return TRUE; +} + +static void TransformMultiply(GB_TRANSFORM matrix, GB_TRANSFORM matrix2) +{ + QTransform *t = (QTransform *)matrix; + QTransform *t2 = (QTransform *)matrix2; + + *t = *t * *t2; +} + +static void TransformMap(GB_TRANSFORM matrix, double *x, double *y) +{ + qreal xx, yy; + + xx = *x; + yy = *y; + + ((QTransform *)matrix)->map(xx, yy, &xx, &yy); + + *x = xx; + *y = yy; +} + + +GB_PAINT_DESC PAINT_Interface = +{ + // Size of the GB_PAINT structure extra data + sizeof(QT_PAINT_EXTRA), + Begin, + End, + Save, + Restore, + Antialias, + Font, + Background, + Invert, + Clip, + ResetClip, + ClipExtents, + ClipRect, + Fill, + Stroke, + PathExtents, + PathContains, + PathOutline, + Dash, + DashOffset, + FillRule, + FillStyle, + LineCap, + LineJoin, + LineWidth, + MiterLimit, + Operator, + NewPath, + ClosePath, + Arc, + Ellipse, + Rectangle, + GetCurrentPoint, + MoveTo, + LineTo, + CurveTo, + Text, + TextExtents, + TextSize, + RichText, + RichTextExtents, + RichTextSize, + Matrix, + SetBrush, + BrushOrigin, + DrawImage, + DrawPicture, + GetPictureInfo, + FillRect, + { + BrushFree, + BrushColor, + BrushImage, + BrushLinearGradient, + BrushRadialGradient, + BrushMatrix, + } +}; + +GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface = +{ + TransformCreate, + TransformCopy, + TransformDelete, + TransformInit, + TransformTranslate, + TransformScale, + TransformRotate, + TransformInvert, + TransformMultiply, + TransformMap +}; + +void PAINT_begin(void *device) +{ + _internal_paint = true; + DRAW.Paint.Begin(device); + _internal_paint = false; +} + +void PAINT_end() +{ + DRAW.Paint.End(); +} + +QPainter *PAINT_get_current() +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + return d ? PAINTER(d) : NULL; +} + +void PAINT_get_current_point(float *x, float *y) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (!d) + return; + GetCurrentPoint(d, x, y); +} + +void PAINT_clip(int x, int y, int w, int h) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + ClipRect(d, x, y, w, h); +} + +/*************************************************************************/ + +MyPaintEngine::MyPaintEngine() : QPaintEngine() {} +MyPaintEngine::~MyPaintEngine() {} + +void MyPaintEngine::patchFeatures() +{ + if (type() == PostScript || type() == Pdf) + { + QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures; + f &= (QPaintEngine::PorterDuff | QPaintEngine::PerspectiveTransform + | QPaintEngine::ObjectBoundingModeGradients + | QPaintEngine::LinearGradientFill + | QPaintEngine::RadialGradientFill + | QPaintEngine::ConicalGradientFill); + //qWarning("warning: patching current paint engine"); + gccaps = f; //PerspectiveTransform; + } +} + +bool MyPaintEngine::begin(QPaintDevice *pdev) +{ + setActive(true); + return true; +} + +bool MyPaintEngine::end() +{ + setActive(false); + return true; +} + +void MyPaintEngine::updateState(const QPaintEngineState &state) +{ + //qDebug("MyPaintEngine::updateState: %04X", (int)state.state()); +} + +void MyPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + //qDebug("MyPaintEngine::drawRects"); +} + +void MyPaintEngine::drawLines(const QLineF *lines, int lineCount) +{ + //qDebug("MyPaintEngine::drawLines"); +} + +void MyPaintEngine::drawEllipse(const QRectF &r) +{ + //qDebug("MyPaintEngine::drawEllipse"); +} + +void MyPaintEngine::drawPath(const QPainterPath &path) +{ + //qDebug("MyPaintEngine::drawPath"); +} + +void MyPaintEngine::drawPoints(const QPointF *points, int pointCount) +{ + //qDebug("MyPaintEngine::drawPoints"); +} + +void MyPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + //qDebug("MyPaintEngine::drawPolygon"); +} + + +void MyPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + //qDebug("MyPaintEngine::drawPixmap"); +} + +void MyPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) +{ + //qDebug("MyPaintEngine::drawTiledPixmap"); +} + +void MyPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags) +{ + //qDebug("MyPaintEngine::drawImage"); +} + + +//QPoint MyPaintEngine::coordinateOffset() const; + +MyPaintEngine::Type MyPaintEngine::type() const +{ + return QPaintEngine::User; +} + +void MyPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) +{ + //qDebug("MyPaintEngine::drawTextItem: %g %g [%s] '%s'", p.x() + _draw_x, p.y() + _draw_y, (const char *)textItem.font().toString().toUtf8(), (const char *)textItem.text().toUtf8()); + //_draw_path->moveTo(p.x() + _draw_x, p.y() + _draw_y); + _draw_path->addText(p.x() + _draw_x + painter()->worldTransform().dx(), p.y() + _draw_y + painter()->worldTransform().dy(), textItem.font(), textItem.text()); +} + +/*************************************************************************/ + +MyPaintEngine MyPaintDevice::engine; + +MyPaintDevice::MyPaintDevice() : QPaintDevice() +{ +} + +QPaintEngine *MyPaintDevice::paintEngine() const +{ + return &engine; +} + +int MyPaintDevice::metric(PaintDeviceMetric m) const +{ + QPaintDevice *d = PAINT_get_current()->device(); + + switch(m) + { + case PdmWidth: return d->width(); + case PdmHeight: return d->height(); + case PdmWidthMM: return d->widthMM(); + case PdmHeightMM: return d->heightMM(); + #if QT_VERSION <= QT_VERSION_CHECK(4, 6, 0) + case PdmNumColors: return d->numColors(); + #else + case PdmNumColors: return d->colorCount(); + #endif + case PdmDepth: return d->depth(); + case PdmDpiX: return d->logicalDpiX(); + case PdmDpiY: return d->logicalDpiY(); + case PdmPhysicalDpiX: return d->physicalDpiX(); + case PdmPhysicalDpiY: return d->physicalDpiY(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + case PdmDevicePixelRatio: return d->devicePixelRatio(); + case PdmDevicePixelRatioScaled: return d->devicePixelRatioFScale(); +#endif + default: return 0; + } +} diff --git a/gb.qt4/src/cpaint_impl.h b/gb.qt4/src/cpaint_impl.h new file mode 100644 index 00000000..d5d2ef6b --- /dev/null +++ b/gb.qt4/src/cpaint_impl.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + cpaint_impl.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPAINT_IMPL_H +#define __CPAINT_IMPL_H + +#include "gambas.h" +#include "gb.paint.h" + +#include +#include +#include +#include +#include + +#ifndef __CPAINT_IMPL_C + +extern GB_PAINT_DESC PAINT_Interface; +extern GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface; + +#endif + +void PAINT_begin(void *device); +void PAINT_end(); +QPainter *PAINT_get_current(); +void PAINT_get_current_point(float *x, float *y); +void PAINT_clip(int x, int y, int w, int h); + +class MyPaintEngine: public QPaintEngine +{ +public: + explicit MyPaintEngine(); + virtual ~MyPaintEngine(); + + virtual bool begin(QPaintDevice *pdev); + virtual bool end(); + + virtual void updateState(const QPaintEngineState &state); + + virtual void drawRects(const QRectF *rects, int rectCount); + virtual void drawLines(const QLineF *lines, int lineCount); + virtual void drawEllipse(const QRectF &r); + virtual void drawPath(const QPainterPath &path); + virtual void drawPoints(const QPointF *points, int pointCount); + virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); + virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); + virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor); + + //virtual QPoint coordinateOffset() const; + + virtual Type type() const; + + void patchFeatures(); +}; + +class MyPaintDevice: public QPaintDevice +{ +public: + explicit MyPaintDevice(); + virtual QPaintEngine *paintEngine() const; + +protected: + virtual int metric(PaintDeviceMetric m) const; + +private: + static MyPaintEngine engine; +}; + + +#endif diff --git a/gb.qt4/src/cprinter.cpp b/gb.qt4/src/cprinter.cpp new file mode 100644 index 00000000..7c6a664f --- /dev/null +++ b/gb.qt4/src/cprinter.cpp @@ -0,0 +1,576 @@ +/*************************************************************************** + + cprinter.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPRINTER_CPP + +#include +#include +#include + +#include "gb.form.print.h" +#include "gb.form.properties.h" +#include "cpaint_impl.h" +#include "cprinter.h" + +DECLARE_EVENT(EVENT_Begin); +DECLARE_EVENT(EVENT_End); +DECLARE_EVENT(EVENT_Paginate); +DECLARE_EVENT(EVENT_Draw); + +#if QT5 + +#define COPY_COUNT copyCount +#define SET_COPY_COUNT(_count) setCopyCount(_count) +#define ORIENTATION pageLayout().orientation +#define SET_ORIENTATION setPageOrientation +#define PAGE_FORMAT() pageLayout().pageSize().id() +#define SET_PAGE_FORMAT(_format) setPageSize(QPageSize(_format)) +#define PAGE_SIZE(_unit) pageLayout().fullRect((QPageLayout::Unit)_unit).size() +#define SET_PAGE_SIZE(_size, _unit) setPageSize(QPageSize(_size, _unit)) + +#define PRINTER_ORIENTATION QPageLayout::Orientation +#define PRINTER_PORTRAIT QPageLayout::Portrait +#define PRINTER_LANDSCAPE QPageLayout::Landscape +#define PRINTER_PAGE_FORMAT QPageSize::PageSizeId + +#define PAGE_FORMAT_ID(_format) QPageSize::_format +#define PAGE_UNIT(_unit) QPageSize::_unit + +#else + +#define COPY_COUNT numCopies +#define SET_COPY_COUNT(_count) setNumCopies(_count) +#define ORIENTATION orientation +#define SET_ORIENTATION setOrientation +#define PAGE_FORMAT paperSize +#define SET_PAGE_FORMAT(_format) setPaperSize(_format) +#define PAGE_SIZE(_unit) paperSize(_unit) +#define SET_PAGE_SIZE(_size, _unit) setPaperSize(_size, _unit) + +#define PRINTER_ORIENTATION QPrinter::Orientation +#define PRINTER_PORTRAIT QPrinter::Portrait +#define PRINTER_LANDSCAPE QPrinter::Landscape +#define PRINTER_PAGE_FORMAT QPrinter::PaperSize + +#define PAGE_FORMAT_ID(_format) QPrinter::_format +#define PAGE_UNIT(_unit) QPrinter::_unit + +#endif + +QSizeF CPRINTER_get_page_size(CPRINTER *_object) +{ + return PRINTER->PAGE_SIZE(PAGE_UNIT(Millimeter)); +} + +static bool configure_printer(CPRINTER *_object) +{ + QPrinter *printer = THIS->printer; + + QPrintDialog dialog(printer, qApp->activeWindow()); + return (dialog.exec() != QDialog::Accepted); +} + +static bool run_printer(CPRINTER *_object) +{ + QEventLoop loop; + bool ret = true; + int page; + int firstPage, lastPage; + bool reverse; + int num_copies_out, num_copies_in, repeat_out, repeat_in; + + THIS->cancel = false; + + THIS->printing = true; + PAINT_begin(THIS); + + GB.Raise(THIS, EVENT_Begin, 0); + + if (GB.CanRaise(THIS, EVENT_Paginate)) + { + while (!THIS->cancel && !THIS->page_count_set) + { + GB.Raise(THIS, EVENT_Paginate, 0); + loop.processEvents(); + } + } + + if (THIS->cancel) + goto __CANCEL; + + if (PRINTER->fromPage() == 0) + { + firstPage = 1; + lastPage = THIS->page_count; + } + else if (PRINTER->toPage() == 0) + { + firstPage = PRINTER->fromPage(); + lastPage = THIS->page_count; + } + else + { + firstPage = PRINTER->fromPage(); + lastPage = PRINTER->toPage(); + } + + if (firstPage > THIS->page_count) + goto __CANCEL; + if (lastPage > THIS->page_count) + lastPage = THIS->page_count; + + reverse = PRINTER->pageOrder() == QPrinter::LastPageFirst; + if (PRINTER->collateCopies()) + { + num_copies_out = PRINTER->COPY_COUNT(); // Always return 1 if the driver manages it + num_copies_in = 1; + } + else + { + num_copies_out = 1; + num_copies_in = PRINTER->COPY_COUNT(); // Always return 1 if the driver manages it + } + + for(repeat_out = 0; repeat_out < num_copies_out; repeat_out++) + { + for (page = firstPage; page <= lastPage; page++) + { + for(repeat_in = 0; repeat_in < num_copies_in; repeat_in++) + { + loop.processEvents(); + if (THIS->cancel) + goto __CANCEL; + + if (reverse) + THIS->page = firstPage + lastPage - page; + else + THIS->page = page; + + GB.Raise(THIS, EVENT_Draw, 0); + + if (page != lastPage) + PRINTER->newPage(); + } + } + } + +__CANCEL: + + GB.Raise(THIS, EVENT_End, 0); + + PAINT_end(); + + THIS->page_count_set = false; + + THIS->printing = false; + + return ret; +} + +static void update_duplex(CPRINTER *_object) +{ + QPrinter::DuplexMode duplex; + + switch(THIS->duplex) + { + case GB_PRINT_DUPLEX_HORIZONTAL: + duplex = PRINTER->ORIENTATION() == PRINTER_PORTRAIT ? QPrinter::DuplexShortSide : QPrinter::DuplexLongSide; + break; + case GB_PRINT_DUPLEX_VERTICAL: + duplex = PRINTER->ORIENTATION() == PRINTER_PORTRAIT ? QPrinter::DuplexLongSide : QPrinter::DuplexShortSide; + break; + case GB_PRINT_SIMPLEX: + default: + duplex = QPrinter::DuplexNone; + } + + PRINTER->setDuplex(duplex); +} + +BEGIN_METHOD_VOID(Printer_new) + + if (MAIN_CHECK_INIT()) + return; + + THIS->printer = new QPrinter(QPrinter::HighResolution); + THIS->page_count = 1; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_free) + + delete THIS->printer; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Print) + + GB.ReturnBoolean(run_printer(THIS)); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Configure) + + GB.ReturnBoolean(configure_printer(THIS)); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Cancel) + + THIS->cancel = true; + PRINTER->abort(); + +END_METHOD + +BEGIN_PROPERTY(Printer_Count) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->page_count); + else + { + THIS->page_count = VPROP(GB_INTEGER); + THIS->page_count_set = true; + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Page) + + GB.ReturnInteger(THIS->page); + +END_PROPERTY + +BEGIN_METHOD(Printer_SetPage, GB_INTEGER page) + + THIS->page = VARG(page); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_IsCountSet) + + GB.ReturnBoolean(THIS->page_count_set); + THIS->page_count_set = FALSE; + +END_METHOD + +BEGIN_PROPERTY(Printer_Name) + + if (READ_PROPERTY) + RETURN_NEW_STRING(PRINTER->printerName()); + else + PRINTER->setPrinterName(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Orientation) + + if (READ_PROPERTY) + { + switch(PRINTER->ORIENTATION()) + { + case PRINTER_LANDSCAPE: GB.ReturnInteger(GB_PRINT_LANDSCAPE); break; + case PRINTER_PORTRAIT: default: GB.ReturnInteger(GB_PRINT_PORTRAIT); + } + } + else + { + PRINTER_ORIENTATION orient; + + switch (VPROP(GB_INTEGER)) + { + case GB_PRINT_LANDSCAPE: orient = PRINTER_LANDSCAPE; break; + case GB_PRINT_PORTRAIT: default: orient = PRINTER_PORTRAIT; break; + } + + PRINTER->SET_ORIENTATION(orient); + update_duplex(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Paper) + + if (READ_PROPERTY) + { + int val; + + switch(PRINTER->PAGE_FORMAT()) + { + case PAGE_FORMAT_ID(A3): val = GB_PRINT_A3; break; + case PAGE_FORMAT_ID(A4): val = GB_PRINT_A4; break; + case PAGE_FORMAT_ID(A5): val = GB_PRINT_A5; break; + case PAGE_FORMAT_ID(B5): val = GB_PRINT_B5; break; + case PAGE_FORMAT_ID(Letter): val = GB_PRINT_LETTER; break; + case PAGE_FORMAT_ID(Executive): val = GB_PRINT_EXECUTIVE; break; + case PAGE_FORMAT_ID(Legal): val = GB_PRINT_LEGAL; break; + default: val = GB_PRINT_CUSTOM; + } + + GB.ReturnInteger(val); + } + else + { + PRINTER_PAGE_FORMAT paper; + + switch(VPROP(GB_INTEGER)) + { + case GB_PRINT_A3: paper = PAGE_FORMAT_ID(A3); break; + case GB_PRINT_A5: paper = PAGE_FORMAT_ID(A5); break; + case GB_PRINT_B5: paper = PAGE_FORMAT_ID(B5); break; + case GB_PRINT_LETTER: paper = PAGE_FORMAT_ID(Letter); break; + case GB_PRINT_EXECUTIVE: paper = PAGE_FORMAT_ID(Executive); break; + case GB_PRINT_LEGAL: paper = PAGE_FORMAT_ID(Legal); break; + case GB_PRINT_A4: default: paper = PAGE_FORMAT_ID(A4); + } + + PRINTER->SET_PAGE_FORMAT(paper); + + fprintf(stderr, "format = %d -> %d\n", VPROP(GB_INTEGER), (int)PRINTER->PAGE_FORMAT()); + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperWidth) + + QSizeF size = CPRINTER_get_page_size(THIS); + + if (READ_PROPERTY) + GB.ReturnFloat(floor((double)size.width() * 1E6) / 1E6); + else + { + qreal width = (qreal)VPROP(GB_FLOAT); + if (width != size.width()) + { + size.setWidth(width); + PRINTER->SET_PAGE_SIZE(size, PAGE_UNIT(Millimeter)); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperHeight) + + QSizeF size = CPRINTER_get_page_size(THIS); + + if (READ_PROPERTY) + GB.ReturnFloat(floor((double)size.height() * 1E6) / 1E6); + else + { + qreal height = (qreal)VPROP(GB_FLOAT); + if (height != size.height()) + { + size.setHeight(height); + PRINTER->SET_PAGE_SIZE(size, PAGE_UNIT(Millimeter)); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_CollateCopies) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->collateCopies()); + else + PRINTER->setCollateCopies(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_ReverseOrder) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->pageOrder() == QPrinter::LastPageFirst); + else + PRINTER->setPageOrder(VPROP(GB_BOOLEAN) ? QPrinter::LastPageFirst : QPrinter::FirstPageFirst); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Duplex) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->duplex); + else + { + THIS->duplex = VPROP(GB_INTEGER); + update_duplex(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_GrayScale) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->colorMode() == QPrinter::GrayScale); + else + PRINTER->setColorMode(VPROP(GB_BOOLEAN) ? QPrinter::GrayScale : QPrinter::Color); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_NumCopies) + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + if (!PRINTER->supportsMultipleCopies()) + return; +#endif + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->COPY_COUNT()); + else + PRINTER->SET_COPY_COUNT(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Resolution) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->resolution()); + else + PRINTER->setResolution(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FirstPage) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->fromPage()); + else + PRINTER->setFromTo(VPROP(GB_INTEGER), PRINTER->toPage()); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_LastPage) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->toPage()); + else + PRINTER->setFromTo(PRINTER->fromPage(), VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FullPage) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->fullPage()); + else + PRINTER->setFullPage(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_OutputFile) + + if (READ_PROPERTY) + RETURN_NEW_STRING(PRINTER->outputFileName()); + else + PRINTER->setOutputFileName(TO_QSTRING(GB.FileName(PSTRING(), PLENGTH()))); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Default) + + QPrinterInfo info = QPrinterInfo::defaultPrinter(); + + if (info.isNull()) + GB.ReturnNull(); + else + RETURN_NEW_STRING(info.printerName()); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_List) + + GB_ARRAY array; + QList list = QPrinterInfo::availablePrinters(); + + GB.Array.New(&array, GB_T_STRING, list.length()); + for (int i = 0; i < list.length(); i++) + *((char **)GB.Array.Get(array, i)) = NEW_STRING(list.at(i).printerName()); + + GB.ReturnObject(array); + +END_PROPERTY + +GB_DESC PrinterDesc[] = +{ + GB_DECLARE("Printer", sizeof(CPRINTER)), + + GB_STATIC_PROPERTY_READ("Default", "s", Printer_Default), + GB_STATIC_PROPERTY_READ("List", "String[]", Printer_List), + + GB_CONSTANT("Portrait", "i", GB_PRINT_PORTRAIT), + GB_CONSTANT("Landscape", "i", GB_PRINT_LANDSCAPE), + //GB_CONSTANT("ReversePortrait", "i", GB_PRINT_REVERSE_PORTRAIT), + //GB_CONSTANT("ReverseLandscape", "i", GB_PRINT_REVERSE_LANDSCAPE), + + GB_CONSTANT("Custom", "i", GB_PRINT_CUSTOM), + GB_CONSTANT("A3", "i", GB_PRINT_A3), + GB_CONSTANT("A4", "i", GB_PRINT_A4), + GB_CONSTANT("A5", "i", GB_PRINT_A5), + GB_CONSTANT("B5", "i", GB_PRINT_B5), + GB_CONSTANT("Letter", "i", GB_PRINT_LETTER), + GB_CONSTANT("Executive", "i", GB_PRINT_EXECUTIVE), + GB_CONSTANT("Legal", "i", GB_PRINT_LEGAL), + + GB_CONSTANT("Simplex", "i", GB_PRINT_SIMPLEX), + GB_CONSTANT("Horizontal", "i", GB_PRINT_DUPLEX_HORIZONTAL), + GB_CONSTANT("Vertical", "i", GB_PRINT_DUPLEX_VERTICAL), + + GB_METHOD("_new", NULL, Printer_new, NULL), + GB_METHOD("_free", NULL, Printer_free, NULL), + + GB_METHOD("Print", "b", Printer_Print, NULL), + GB_METHOD("_SetPage", NULL, Printer_SetPage, "(Page)i"), + GB_METHOD("_IsCountSet", "b", Printer_IsCountSet, NULL), + + GB_METHOD("Configure", "b", Printer_Configure, NULL), + GB_METHOD("Cancel", NULL, Printer_Cancel, NULL), + + GB_PROPERTY("Count", "i", Printer_Count), + GB_PROPERTY_READ("Page", "i", Printer_Page), + + GB_PROPERTY("Name", "s", Printer_Name), + GB_PROPERTY("Orientation", "i", Printer_Orientation), + GB_PROPERTY("Paper", "i", Printer_Paper), + GB_PROPERTY("PaperWidth", "f", Printer_PaperWidth), + GB_PROPERTY("PaperHeight", "f", Printer_PaperHeight), + GB_PROPERTY("CollateCopies", "b", Printer_CollateCopies), + GB_PROPERTY("ReverseOrder", "b", Printer_ReverseOrder), + GB_PROPERTY("Duplex", "i", Printer_Duplex), + GB_PROPERTY("GrayScale", "b", Printer_GrayScale), + GB_PROPERTY("NumCopies", "i", Printer_NumCopies), + GB_PROPERTY("CopyCount", "i", Printer_NumCopies), + GB_PROPERTY("Resolution", "i", Printer_Resolution), + GB_PROPERTY("FirstPage", "i", Printer_FirstPage), + GB_PROPERTY("LastPage", "i", Printer_LastPage), + GB_PROPERTY("FullPage", "b", Printer_FullPage), + GB_PROPERTY("OutputFile", "s", Printer_OutputFile), + + GB_EVENT("Begin", NULL, NULL, &EVENT_Begin), + GB_EVENT("End", NULL, NULL, &EVENT_End), + GB_EVENT("Paginate", NULL, NULL, &EVENT_Paginate), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + + GB_INTERFACE("Paint", &PAINT_Interface), + + PRINTER_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/cprinter.h b/gb.qt4/src/cprinter.h new file mode 100644 index 00000000..4807d111 --- /dev/null +++ b/gb.qt4/src/cprinter.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + cprinter.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPRINTER_H +#define __CPRINTER_H + +#include "main.h" + +#include + +typedef + struct { + GB_BASE ob; + QPrinter *printer; + int page; + int page_count; + int duplex; + bool cancel; + bool page_count_set; + bool printing; + } + CPRINTER; + +#ifndef __CPRINTER_CPP + +extern GB_DESC PrinterDesc[]; + +#else + +#define THIS ((CPRINTER *)_object) +#define PRINTER (THIS->printer) + +#endif + +QSizeF CPRINTER_get_page_size(CPRINTER *_object); + +#endif diff --git a/gb.qt4/src/csvgimage.cpp b/gb.qt4/src/csvgimage.cpp new file mode 100644 index 00000000..d4d6ac61 --- /dev/null +++ b/gb.qt4/src/csvgimage.cpp @@ -0,0 +1,274 @@ +/*************************************************************************** + + csvgimage.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSVGIMAGE_CPP + +#include "main.h" +#include "gambas.h" +#include "cpaint_impl.h" +#include "csvgimage.h" + +#include + +#define MM_TO_PT(_mm) ((_mm) * 72 / 25.4) +#define PT_TO_MM(_pt) ((_pt) / 72 * 25.4) + +static void release(CSVGIMAGE *_object) +{ + if (RENDERER) + { + delete RENDERER; + RENDERER = NULL; + } + + if (GENERATOR) + { + delete GENERATOR; + THIS->generator = NULL; + unlink(THIS->file); + GB.FreeString(&THIS->file); + } + + THIS->width = THIS->height = 0; +} + +QSvgGenerator *SVGIMAGE_begin(CSVGIMAGE *_object, QPainter **painter) +{ + if (!GENERATOR) + { + if (THIS->width <= 0 || THIS->height <= 0) + { + GB.Error("SvgImage size is not defined"); + return NULL; + } + + THIS->file = GB.NewZeroString(GB.TempFile(NULL)); + THIS->generator = new QSvgGenerator(); + GENERATOR->setSize(QSize(THIS->width, THIS->height)); + //GENERATOR->setViewBox(QRectF(0, 0, THIS->width, THIS->height)); + GENERATOR->setFileName(THIS->file); + + if (RENDERER) + { + *painter = new QPainter(GENERATOR); + RENDERER->render(*painter, QRectF(0, 0, THIS->width, THIS->height)); + } + else + *painter = NULL; + } + + return GENERATOR; +} + +BEGIN_METHOD(SvgImage_new, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARGOPT(width, 0); + THIS->height = VARGOPT(height, 0); + +END_METHOD + +BEGIN_METHOD_VOID(SvgImage_free) + + release(THIS); + +END_METHOD + +BEGIN_PROPERTY(SvgImage_Width) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->width); + else + THIS->width = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_PROPERTY(SvgImage_Height) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->height); + else + THIS->height = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_METHOD(SvgImage_Resize, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARG(width); + THIS->height = VARG(height); + +END_METHOD + +#ifdef QT5 +static void myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &) +#else +static void myMessageHandler(QtMsgType, const char *) +#endif +{ +} + +static const char *load_file(CSVGIMAGE *_object, const char *path, int len_path) +{ + QSvgRenderer *renderer; + QByteArray data; + char *addr; + int len; + const char *error = NULL; + + if (GB.LoadFile(path, len_path, &addr, &len)) + return "Unable to load SVG file"; + + data = QByteArray::fromRawData(addr, len); + +#ifdef QT5 + qInstallMessageHandler(myMessageHandler); +#else + qInstallMsgHandler(myMessageHandler); +#endif + renderer = new QSvgRenderer(data); +#ifdef QT5 + qInstallMessageHandler(0); +#else + qInstallMsgHandler(0); +#endif + if (!renderer->isValid()) + { + error = "Unable to load SVG file: unable to create renderer"; + goto __RETURN; + } + + release(THIS); + THIS->renderer = renderer; + THIS->width = renderer->defaultSize().width(); + THIS->height = renderer->defaultSize().height(); + + renderer = NULL; + +__RETURN: + + if (renderer) + delete renderer; + + GB.ReleaseFile(addr, len); + return error; +} + +BEGIN_METHOD(SvgImage_Load, GB_STRING path) + + CSVGIMAGE *svgimage; + const char *err; + + svgimage = (CSVGIMAGE *)GB.New(CLASS_SvgImage, NULL, NULL); + + err = load_file(svgimage, STRING(path), LENGTH(path)); + if (err) + { + GB.Unref(POINTER(&svgimage)); + GB.Error(err); + return; + } + + GB.ReturnObject(svgimage); + +END_METHOD + +BEGIN_METHOD(SvgImage_Paint, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + QPainter *painter = PAINT_get_current(); + float xc, yc; + const char *err; + + if (!painter) + return; + + if (THIS->file) + { + err = load_file(THIS, THIS->file, GB.StringLength(THIS->file)); + if (err) + { + GB.Error(err); + return; + } + } + + if (!RENDERER) + return; + + if (THIS->width <= 0 || THIS->height <= 0) + return; + + PAINT_get_current_point(&xc, &yc); + RENDERER->render(painter, QRectF(VARGOPT(x, xc), VARGOPT(y, yc), VARGOPT(w, THIS->width), VARGOPT(h, THIS->height))); + +END_METHOD + +BEGIN_METHOD(SvgImage_Save, GB_STRING file) + + if (!THIS->file) + { + QPainter *painter; + if (!SVGIMAGE_begin(THIS, &painter)) + { + GB.Error("Void image"); + return; + } + if (painter) + delete painter; + } + + if (GB.CopyFile(THIS->file, GB.FileName(STRING(file), LENGTH(file)))) + return; + + load_file(THIS, THIS->file, GB.StringLength(THIS->file)); + +END_METHOD + +BEGIN_METHOD_VOID(SvgImage_Clear) + + release(THIS); + +END_METHOD + +GB_DESC SvgImageDesc[] = +{ + GB_DECLARE("SvgImage", sizeof(CSVGIMAGE)), + + GB_METHOD("_new", NULL, SvgImage_new, "[(Width)f(Height)f]"), + GB_METHOD("_free", NULL, SvgImage_free, NULL), + + GB_PROPERTY("Width", "f", SvgImage_Width), + GB_PROPERTY("W", "f", SvgImage_Width), + GB_PROPERTY("Height", "f", SvgImage_Height), + GB_PROPERTY("H", "f", SvgImage_Height), + GB_METHOD("Resize", NULL, SvgImage_Resize, "(Width)f(Height)f"), + + GB_STATIC_METHOD("Load", "SvgImage", SvgImage_Load, "(Path)s"), + GB_METHOD("Save", NULL, SvgImage_Save, "(Path)s"), + GB_METHOD("Paint", NULL, SvgImage_Paint, "[(X)f(Y)f(Width)f(Height)f]"), + + GB_METHOD("Clear", NULL, SvgImage_Clear, NULL), + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/csvgimage.h b/gb.qt4/src/csvgimage.h new file mode 100644 index 00000000..34ff1685 --- /dev/null +++ b/gb.qt4/src/csvgimage.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + csvgimage.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSVGIMAGE_H +#define __CSVGIMAGE_H + +#include "gambas.h" +#include +#include + +typedef + struct + { + GB_BASE ob; + QSvgGenerator *generator; + QSvgRenderer *renderer; + char *file; + double width; + double height; + } + CSVGIMAGE; + +#ifndef __CSVGIMAGE_CPP + +extern GB_DESC SvgImageDesc[]; + +#else + +#define THIS OBJECT(CSVGIMAGE) +#define GENERATOR THIS->generator +#define RENDERER THIS->renderer + +#endif + +QSvgGenerator *SVGIMAGE_begin(CSVGIMAGE *_object, QPainter **painter); + +#endif diff --git a/gb.qt4/src/ctrayicon.cpp b/gb.qt4/src/ctrayicon.cpp new file mode 100644 index 00000000..914a490a --- /dev/null +++ b/gb.qt4/src/ctrayicon.cpp @@ -0,0 +1,462 @@ +/*************************************************************************** + + ctrayicon.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTRAYICON_CPP + +#include "gambas.h" +#include "main.h" + +#include +#include + +#include "gb.form.properties.h" +#include "ctrayicon.h" + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_MiddleClick); +DECLARE_EVENT(EVENT_Scroll); + +static QList _list; +static QPixmap *_default_trayicon = NULL; + +#include "gb.form.trayicon.large.h" + +//--------------------------------------------------------------------------- + +static void destroy_trayicon(CTRAYICON *_object) +{ + if (TRAYICON) + { + TRAYICON->deleteLater(); + THIS->indicator = NULL; + QT_PreventQuit(false); + } +} + +static void delete_all(void) +{ + CTRAYICON *_object, *last = 0; + int i; + + GB.StopAllEnum((void *)GB.FindClass("TrayIcons")); + + i = 0; + for (;;) + { + if (i >= _list.count()) + break; + + _object = _list.at(i); + + if (_object == last) + { + i++; + continue; + } + + last = _object; + destroy_trayicon(THIS); + GB.Unref(POINTER(&_object)); + } + + _list.clear(); +} + +static void define_tooltip(CTRAYICON *_object) +{ + if (!TRAYICON) + return; + + TRAYICON->setToolTip(TO_QSTRING(THIS->tooltip)); +} + +static void define_icon(CTRAYICON *_object) +{ + if (!TRAYICON) + return; + + if (THIS->icon) + TRAYICON->setIcon(*THIS->icon->pixmap); + else + { + if (!_default_trayicon) + { + _default_trayicon = new QPixmap; + _default_trayicon->loadFromData(_default_trayicon_data, sizeof(_default_trayicon_data), "PNG"); + } + + TRAYICON->setIcon(*_default_trayicon); + } +} + +static void define_menu(CTRAYICON *_object) +{ + QMenu *menu; + + if (!TRAYICON) + return; + + menu = NULL; + + if (THIS->popup) + menu = QT_FindMenu(GB.Parent(THIS), THIS->popup); + + TRAYICON->setContextMenu(menu); +} + +static CTRAYICON *find_trayicon(const QObject *o) +{ + int i; + CTRAYICON *_object; + + for (i = 0; i < _list.count(); i++) + { + _object = _list.at(i); + if (TRAYICON && THIS->indicator == o) + return THIS; + } + + return NULL; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(TrayIcon_new) + + THIS->tag.type = GB_T_NULL; + _list.append(THIS); + GB.Ref(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcon_free) + + //qDebug("TrayIcon_free"); + + _list.removeAt(_list.indexOf(THIS)); + + GB.StoreObject(NULL, POINTER(&THIS->icon)); + GB.FreeString(&THIS->tooltip); + GB.FreeString(&THIS->popup); + GB.StoreVariant(NULL, &THIS->tag); + + destroy_trayicon(THIS); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcon_Picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->icon); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->icon)); + define_icon(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TrayIcon_Text) + + if (READ_PROPERTY) + GB.ReturnString(THIS->tooltip); + else + { + GB.StoreString(PROP(GB_STRING), &(THIS->tooltip)); + define_tooltip(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(TrayIcon_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS->popup); + else + { + GB.StoreString(PROP(GB_STRING), &(THIS->popup)); + define_menu(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(TrayIcon_Show) + + if (!TRAYICON) + { + QSystemTrayIcon *indicator = new QSystemTrayIcon(); + + QObject::connect(indicator, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), &TrayIconManager::manager, SLOT(activated(QSystemTrayIcon::ActivationReason))); + indicator->installEventFilter(&TrayIconManager::manager); + + THIS->indicator = indicator; + QT_PreventQuit(true); + + define_tooltip(THIS); + define_icon(THIS); + define_menu(THIS); + + TRAYICON->show(); + } + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcon_Hide) + + destroy_trayicon(THIS); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcon_Visible) + + if (READ_PROPERTY) + { + GB.ReturnBoolean(TRAYICON != NULL); + return; + } + + if (VPROP(GB_BOOLEAN)) + TrayIcon_Show(_object, _param); + else + TrayIcon_Hide(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(TrayIcon_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->tag); + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcons_next) + + int index; + + index = ENUM(int); + + if (index >= _list.count()) + { + GB.StopEnum(); + return; + } + + ENUM(int) = index + 1; + + GB.ReturnObject(_list.at(index)); + +END_METHOD + + +BEGIN_METHOD(TrayIcons_get, GB_INTEGER index) + + int index = (uint)VARG(index); + + if (index >= _list.count()) + { + GB.Error("Bad index"); + return; + } + + GB.ReturnObject(_list.at(index)); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcons_Count) + + GB.ReturnInteger(_list.count()); + +END_PROPERTY + +BEGIN_METHOD_VOID(TrayIcon_unknown) + + static char prop[32]; + char *name = GB.GetUnknown(); + + if (strcasecmp(name, "ScreenX") == 0 || strcasecmp(name, "ScreenY") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(QT_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(0); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else if (strcasecmp(name, "W") == 0 || strcasecmp(name, "Width") == 0 || strcasecmp(name, "H") == 0 || strcasecmp(name, "Height") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(QT_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(24); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + } + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcon_exit) + + if (_default_trayicon) + delete _default_trayicon; + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcons_DeleteAll) + + delete_all(); + +END_METHOD + +//--------------------------------------------------------------------------- + +TrayIconManager TrayIconManager::manager; + +void TrayIconManager::activated(QSystemTrayIcon::ActivationReason reason) +{ + CTRAYICON *_object = find_trayicon(sender()); + if (THIS) + { + //qDebug("reason = %d", (int)reason); + switch(reason) + { + //case QSystemTrayIcon::DoubleClick: + // GB.Raise(THIS, EVENT_TrayIconDblClick, 0); + // break; + + case QSystemTrayIcon::Trigger: + GB.Raise(THIS, EVENT_Click, 0); + break; + + case QSystemTrayIcon::MiddleClick: + GB.Raise(THIS, EVENT_MiddleClick, 0); + break; + + default: + break; + } + } +} + +bool TrayIconManager::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Wheel) + { + CTRAYICON *_object = find_trayicon(o); + if (THIS) + { + bool cancel = true; + QWheelEvent *ev = (QWheelEvent *)e; + +#ifdef QT5 + if (ev->angleDelta().x()) + cancel = GB.Raise(THIS, EVENT_Scroll, 2, GB_T_FLOAT, ev->angleDelta().x() / 120.0, GB_T_INTEGER, false); + if (ev->angleDelta().y()) + cancel = GB.Raise(THIS, EVENT_Scroll, 2, GB_T_FLOAT, ev->angleDelta().y() / 120.0, GB_T_INTEGER, true); +#else + cancel = GB.Raise(THIS, EVENT_Scroll, 2, GB_T_FLOAT, ev->delta() / 120.0, GB_T_INTEGER, ev->orientation() == Qt::Vertical); +#endif + + if (cancel) + return true; + } + } + + return QObject::eventFilter(o, e); +} + +//--------------------------------------------------------------------------- + +GB_DESC TrayIconsDesc[] = +{ + GB_DECLARE_STATIC("TrayIcons"), + + GB_STATIC_METHOD("_next", "TrayIcon", TrayIcons_next, NULL), + GB_STATIC_METHOD("_get", "TrayIcon", TrayIcons_get, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", TrayIcons_Count), + GB_STATIC_METHOD("DeleteAll", NULL, TrayIcons_DeleteAll, NULL), + + GB_END_DECLARE +}; + + +GB_DESC TrayIconDesc[] = +{ + GB_DECLARE("TrayIcon", sizeof(CTRAYICON)), + + GB_STATIC_METHOD("_exit", NULL, TrayIcon_exit, NULL), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_METHOD("_new", NULL, TrayIcon_new, NULL), + GB_METHOD("_free", NULL, TrayIcon_free, NULL), + + GB_METHOD("Show", NULL, TrayIcon_Show, NULL), + GB_METHOD("Hide", NULL, TrayIcon_Hide, NULL), + GB_METHOD("Delete", NULL, TrayIcon_Hide, NULL), + + GB_PROPERTY("Picture", "Picture", TrayIcon_Picture), + GB_PROPERTY("Icon", "Picture", TrayIcon_Picture), + GB_PROPERTY("Visible", "b", TrayIcon_Visible), + + GB_PROPERTY("Text", "s", TrayIcon_Text), + GB_PROPERTY("PopupMenu", "s", TrayIcon_PopupMenu), + GB_PROPERTY("Tooltip", "s", TrayIcon_Text), + GB_PROPERTY("Tag", "v", TrayIcon_Tag), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("MiddleClick", NULL, NULL, &EVENT_MiddleClick), + GB_EVENT("Scroll", NULL, "(Delta)f(Orientation)i", &EVENT_Scroll), + + GB_METHOD("_unknown", "v", TrayIcon_unknown, "."), + + TRAYICON_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/ctrayicon.h b/gb.qt4/src/ctrayicon.h new file mode 100644 index 00000000..f54f011e --- /dev/null +++ b/gb.qt4/src/ctrayicon.h @@ -0,0 +1,71 @@ +/*************************************************************************** + + ctrayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTRAYICON_H +#define __CTRAYICON_H + +#include + +#include "gambas.h" +#include "main.h" + +#include "CPicture.h" + +#ifndef __CTRAYICON_CPP +extern GB_DESC TrayIconsDesc[]; +extern GB_DESC TrayIconDesc[]; +#else + +#define THIS ((CTRAYICON *)_object) +#define TRAYICON (THIS->indicator) + +#endif + +void TRAYICON_close_all(void); + +typedef + struct { + GB_BASE ob; + QSystemTrayIcon *indicator; + GB_VARIANT_VALUE tag; + CPICTURE *icon; + char *tooltip; + char *popup; + } + CTRAYICON; + +class TrayIconManager : public QObject +{ + Q_OBJECT + +public: + + static TrayIconManager manager; + + virtual bool eventFilter(QObject *o, QEvent *e); + +public slots: + + void activated(QSystemTrayIcon::ActivationReason); +}; +#endif diff --git a/gb.qt4/src/desktop.c b/gb.qt4/src/desktop.c new file mode 100644 index 00000000..3f510d86 --- /dev/null +++ b/gb.qt4/src/desktop.c @@ -0,0 +1,119 @@ +/*************************************************************************** + + desktop.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DESKTOP_C + +#include "desktop.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const GB_INTERFACE *GB_PTR; +#define GB (*GB_PTR) + +#ifdef __cplusplus +} +#endif + +static bool _desktop_done = FALSE; +static char _desktop[32]; + +static const char *calc_desktop_type() +{ + char *env; + + env = getenv("KDE_FULL_SESSION"); + if (env && strcasecmp(env, "true") == 0) + { + env = getenv("KDE_SESSION_VERSION"); + if (env) + { + if (strcmp(env, "4") == 0) + return "KDE4"; + if (strcmp(env, "5") == 0) + return "KDE5"; + } + return "KDE"; + } + + env = getenv("XDG_CURRENT_DESKTOP"); + if (env && *env && strlen(env) < sizeof(_desktop)) + { + if (env[0] == 'X' && env[1] == '-') + env += 2; + + return env; + } + + env = getenv("GNOME_DESKTOP_SESSION_ID"); + if (env && *env) + return "GNOME"; + + env = getenv("MATE_DESKTOP_SESSION_ID"); + if (env && *env) + return "MATE"; + + env = getenv("E_BIN_DIR"); + if (env && *env) + { + env = getenv("E_LIB_DIR"); + if (env && *env) + return "ENLIGHTENMENT"; + } + + env = getenv("WMAKER_BIN_NAME"); + if (env && *env) + return "WINDOWMAKER"; + + env = getenv("DESKTOP_SESSION"); + if (env && strcasecmp(env, "XFCE") == 0) + return "XFCE"; + + env = getenv("XDG_MENU_PREFIX"); + if (env && strncasecmp(env, "XFCE", 4) == 0) + return "XFCE"; + + env = getenv("XDG_DATA_DIRS"); + if (env && strstr(env, "/xfce")) + return "XFCE"; + + return ""; +} + +const char *DESKTOP_get_type() +{ + const char *type; + char *p; + + if (!_desktop_done) + { + type = calc_desktop_type(); + p = _desktop; + + while ((*p++ = toupper(*type++))); + _desktop_done = TRUE; + } + + return _desktop; +} diff --git a/gb.qt4/src/desktop.h b/gb.qt4/src/desktop.h new file mode 100644 index 00000000..786f4762 --- /dev/null +++ b/gb.qt4/src/desktop.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + desktop.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DESKTOP_H +#define __DESKTOP_H + +#include "gambas.h" +#include "gb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const char *DESKTOP_get_type(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gb.qt4/src/ext/CDial.cpp b/gb.qt4/src/ext/CDial.cpp new file mode 100644 index 00000000..74e086b0 --- /dev/null +++ b/gb.qt4/src/ext/CDial.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** + + CDial.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDIAL_CPP + +#include "main.h" +#include "gambas.h" + +#include "CDial.h" + +DECLARE_EVENT(EVENT_Change); +//DECLARE_EVENT(EVENT_Activate); +//DECLARE_EVENT(EVENT_Deactivate); + +BEGIN_METHOD(CDIAL_new, GB_OBJECT parent) + + QDial *wid = new QDial(QT.GetContainer(VARG(parent))); + + QT.InitWidget(wid, _object, false); + //QT.SetBackgroundRole(_object, QColorGroup::Base); + + QObject::connect(wid, SIGNAL(valueChanged(int)), &CDial::manager, SLOT(event_change())); + //QObject::connect(wid, SIGNAL(dialPressed()), &CDial::manager, + //SLOT(event_activate())); + //QObject::connect(wid, SIGNAL(dialMoved(int)), &CDial::manager, + //SLOT(event_change())); + //QObject::connect(wid, SIGNAL(dialReleased()), &CDial::manager, + //SLOT(event_deactivate())); + + wid->setMinimum(0); + wid->setMaximum(100); + wid->setSingleStep(1); + wid->setPageStep(10); + wid->setNotchesVisible(true); + + wid->show(); + +END_METHOD + + +BEGIN_PROPERTY(CDIAL_tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasTracking()); + else + WIDGET->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_notchsize) + + GB.ReturnInteger(WIDGET->notchSize()); + +END_PROPERTY + +/* +BEGIN_PROPERTY(CDIAL_notchtarget) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->notchTarget()); + else + WIDGET->setNotchTarget(PROPERTY(GB_INTEGER)); + +END_PROPERTY +*/ + +BEGIN_PROPERTY(CDIAL_wrapping) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->wrapping()); + else + WIDGET->setWrapping(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_value) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->value()); + else + WIDGET->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_notchesvisible) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->notchesVisible()); + else + WIDGET->setNotchesVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_minval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->minimum()); + else + WIDGET->setMinimum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_maxval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->maximum()); + else + WIDGET->setMaximum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_linestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->singleStep()); + else + { + int step = VPROP(GB_INTEGER); + if (step > 0) + WIDGET->setSingleStep(step); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_pagestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->pageStep()); + else + { + int step = VPROP(GB_INTEGER); + if (step > 0) + WIDGET->setPageStep(step); + } + +END_PROPERTY + + +/*************************************************************************** + + class CDial + +***************************************************************************/ + +CDial CDial::manager; + +void CDial::event_change(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_Change, 0); +} + + + +GB_DESC CDialDesc[] = +{ + GB_DECLARE("Dial", sizeof(CDIAL)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CDIAL_new, "(Parent)Container;"), + + //GB_PROPERTY("Tracking", "b", CDIAL_tracking), + //GB_PROPERTY_READ("NotchSize", "i", CDIAL_notchsize), + GB_PROPERTY("Value", "i", CDIAL_value), + //GB_PROPERTY("MarkGap", "i", CDIAL_notchtarget), + GB_PROPERTY("Mark", "b", CDIAL_notchesvisible), + GB_PROPERTY("MinValue", "i", CDIAL_minval), + GB_PROPERTY("MaxValue", "i", CDIAL_maxval), + GB_PROPERTY("Step", "i", CDIAL_linestep), + GB_PROPERTY("Wrap", "b", CDIAL_wrapping), + GB_PROPERTY("PageStep", "i", CDIAL_pagestep), + + DIAL_DESCRIPTION, + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/ext/CDial.h b/gb.qt4/src/ext/CDial.h new file mode 100644 index 00000000..bccd7816 --- /dev/null +++ b/gb.qt4/src/ext/CDial.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + CDial.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDIAL_H +#define __CDIAL_H + +#include +#include + +#include "gambas.h" +#include "../gb.qt.h" + +#ifndef __CDIAL_CPP + +extern GB_DESC CDialDesc[]; + +#else + +#define THIS ((CDIAL *)_object) +#define WIDGET ((QDial *)((QT_WIDGET *)_object)->widget) + +#endif + +typedef + struct { + QT_WIDGET widget; + } + CDIAL; + +class CDial : public QObject +{ + Q_OBJECT + +public: + + static CDial manager; + +public slots: + + void event_change(void); +}; + +#endif diff --git a/gb.qt4/src/ext/CEditor.cpp b/gb.qt4/src/ext/CEditor.cpp new file mode 100644 index 00000000..db5de439 --- /dev/null +++ b/gb.qt4/src/ext/CEditor.cpp @@ -0,0 +1,1865 @@ +/*************************************************************************** + + CEditor.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CEDITOR_CPP + +#include "main.h" + +#include +#include +#include + +#include "gdocument.h" + +#include "CEditor.h" + +#define MAX_CONSOLE_WIDTH 256 + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); +DECLARE_EVENT(EVENT_Scroll); +DECLARE_EVENT(EVENT_Highlight); +DECLARE_EVENT(EVENT_Margin); + +enum { LIMIT_NONE, LIMIT_LINE, LIMIT_BLEND, LIMIT_BACKGROUND }; + +enum +{ + HIGHLIGHT_CUSTOM = GDocument::Custom, + HIGHLIGHT_HTML, + HIGHLIGHT_CSS, + HIGHLIGHT_WEBPAGE, + HIGHLIGHT_DIFF, + HIGHLIGHT_JAVASCRIPT, + HIGHLIGHT_C, + HIGHLIGHT_CPLUSPLUS, + HIGHLIGHT_SQL +}; + +typedef + struct { + int style; + const char *name; + } + HIGHLIGHT_NAME; + +typedef + struct + { + const char *cmd; + void *label; + bool select; + } + COMMAND; + +static HIGHLIGHT_NAME _highlight_name[] = { + { HIGHLIGHT_HTML, "_DoHtml" }, + { HIGHLIGHT_CSS, "_DoCss" }, + { HIGHLIGHT_WEBPAGE, "_DoWebpage" }, + { HIGHLIGHT_DIFF, "_DoDiff" }, + { HIGHLIGHT_JAVASCRIPT, "_DoJavascript" }, + { HIGHLIGHT_C, "_DoC" }, + { HIGHLIGHT_CPLUSPLUS, "_DoCPlusPlus" }, + { HIGHLIGHT_SQL, "_DoSQL" }, + { HIGHLIGHT_CUSTOM, NULL } +}; + + +static int _x1, _y1, _x2, _y2; +static int _style; + +static int _highlight_state = 0; +static bool _highlight_alternate = false; +static int _highlight_tag = 0; +static bool _highlight_show_limit = false; +static QString _highlight_text = ""; +static GHighlightArray *_highlight_data = NULL; +static int _highlight_line = -1; + +static QT_PICTURE _breakpoint_picture = 0; +static QT_PICTURE _bookmark_picture = 0; + +static void highlightCustom(GEditor *master, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *data, bool &proc) +{ + void *_object = QT.GetObject((QWidget *)master); + + _highlight_state = state; + _highlight_alternate = alternate; + _highlight_tag = tag; + _highlight_text = s.getString(); + _highlight_show_limit = proc; + _highlight_data = data; + _highlight_line = line; + + GB.NewArray(_highlight_data, sizeof(GHighlight), 0); + + if (DOC->getHighlightMode() == GDocument::Custom) + GB.Raise(THIS, EVENT_Highlight, 0); + else + GB.Call(&THIS->highlight, 0, FALSE); + + state = _highlight_state; + alternate = _highlight_alternate; + tag = _highlight_tag; + s = GString(_highlight_text); + proc = _highlight_show_limit; + _highlight_data = 0; +} + +/**************************************************************************** + + Highlight + +****************************************************************************/ + +BEGIN_PROPERTY(Highlight_Line) + + GB.ReturnInteger(_highlight_line); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_State) + + if (READ_PROPERTY) + GB.ReturnInteger(_highlight_state); + else + _highlight_state = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_AlternateState) + + if (READ_PROPERTY) + GB.ReturnBoolean(_highlight_alternate); + else + _highlight_alternate = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Tag) + + if (READ_PROPERTY) + GB.ReturnInteger(_highlight_tag); + else + _highlight_tag = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_ShowLimit) + + if (READ_PROPERTY) + GB.ReturnBoolean(_highlight_show_limit); + else + _highlight_show_limit = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(_highlight_text); + else + _highlight_text = QSTRING_PROP(); + +END_PROPERTY + +BEGIN_METHOD(Highlight_Add, GB_INTEGER state; GB_INTEGER len) + + GHighlight *h; + + if (!_highlight_data) + return; + + int count = GB.Count(*_highlight_data) - 1; + int state = VARG(state); + int len = VARGOPT(len, 1); + + if (len < 1) + return; + + if (count < 0 || (*_highlight_data)[count].state != (uint)state || (*_highlight_data)[count].alternate != _highlight_alternate || ((*_highlight_data)[count].len + len) > HIGHLIGHT_LEN_MAX) + { + count++; + h = (GHighlight *)GB.Add(_highlight_data); + h->state = state; + h->alternate = _highlight_alternate; + h->len = len; + } + else + (*_highlight_data)[count].len += len; + +END_METHOD + +/**************************************************************************** + + Editor + +****************************************************************************/ + +BEGIN_PROPERTY(Editor_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(DOC->getText().getString()); + else + DOC->setText(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_Length) + + GB.ReturnInteger(DOC->getLength()); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_EndOfLine) + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->getEndOfLine()); + else + DOC->setEndOfLine(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD_VOID(Editor_HighlightAll) + + DOC->colorizeAll(); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_tab_length) + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->getTabWidth()); + else + DOC->setTabWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CEDITOR_reset) + + DOC->reset(true); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_read_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->isReadOnly()); + else + DOC->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_overwrite) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->getInsertMode()); + else + WIDGET->setInsertMode(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CEDITOR_clear) + + DOC->clear(); + +END_METHOD + +BEGIN_METHOD(CEDITOR_insert, GB_STRING str; GB_INTEGER y; GB_INTEGER x) + + if (MISSING(y) || MISSING(x)) + WIDGET->insert(QSTRING_ARG(str)); + else + DOC->insert(VARG(y), VARG(x), QSTRING_ARG(str)); + +END_METHOD + +static void print_newline(void *_object) +{ + int line, col; + + WIDGET->getCursor(&line, &col); + + if (line < (DOC->numLines() - 1)) + WIDGET->cursorGoto(line + 1, WIDGET->getColumn(), false); + else + { + WIDGET->cursorGoto(line, DOC->lineLength(line), false); + WIDGET->insert("\n"); + } + + if (THIS->terminal) + WIDGET->cursorGoto(WIDGET->getLine(), col, false); + + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); +} + +static void print_text(void *_object, const char *str, int lstr, bool esc = false) +{ + QString s = QString::fromUtf8(str, lstr); + int line, col; + uint i, len; + + //fprintf(stderr, "-> %.*s\n", lstr, str); + + WIDGET->getCursor(&line, &col); + /*if (col == 0) + { + DOC->remove(line, 0, line, DOC->lineLength(line)); + WIDGET->cursorGoto(line, 0, false); + }*/ + +// if (col < DOC->lineLength(line)) +// { +// end = col + s.length(); +// if (end > DOC->lineLength(line)) +// end = DOC->lineLength(line); +// DOC->remove(line, col, line, end); +// } + + if (!esc) + { + i = 0; + for (;;) + { + if (col == MAX_CONSOLE_WIDTH) + { + print_newline(THIS); + col = 0; + } + len = s.length() - i; + if ((col + len) >= MAX_CONSOLE_WIDTH) + len = MAX_CONSOLE_WIDTH - col; + DOC->remove(WIDGET->getLine(), col, WIDGET->getLine(), col + len); + WIDGET->insert(s.mid(i, len)); + i += len; + if (i >= (uint)s.length()) + break; + col += len; + } + } + else + { + if (col >= MAX_CONSOLE_WIDTH) + print_newline(THIS); + DOC->remove(WIDGET->getLine(), col, WIDGET->getLine(), col + s.length()); + WIDGET->insert(s); + } + + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + //BREAKPOINT(); +} + +static int ansi_read_integer(const char *str, int len, int def, int *pos) +{ + int value = 0; + int n = 0; + uchar c; + + str += *pos; + len -= *pos; + + while (len > 0) + { + c = str[n]; + if (c < '0' || c > '9') + break; + value = value * 10 + c - '0'; + len--; + n++; + if (n > 6) + { + value = -1; + break; + } + } + + if (n == 0) + value = def; + + while (len > 0) + { + c = str[n]; + len--; + n++; + if (c == ';' || c < '0' || c > '9') + break; + } + + *pos += n; + return value; +} + +static int ansi_process(void *_object, const char *str, int len) +{ + uchar c; + int n, m, l, pos; + bool print; + + if (len == 0) + return 0; + + c = *str; + if (c == '[' || c == ']' || c == '(' || c == ')') + { + for (l = 0; l < len; l++) + { + c = str[l]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + break; + } + } + else + l = 0; + + if (l >= len) + return 0; + + print = false; + + if (*str == '[') + { + pos = 1; + + switch(str[l]) + { + case 'A': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(-n, 0, false); + break; + + case 'B': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(n, 0, false); + break; + + case 'C': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(0, n, false); + break; + + case 'D': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(0, -n, false); + break; + + case 'G': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorGoto(WIDGET->getLine(), n - 1, false); + break; + + case 'd': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorGoto(n - 1, WIDGET->getColumn(), false); + break; + + case 'H': case 'f': + n = ansi_read_integer(str, l, 1, &pos); + m = ansi_read_integer(str, l, 1, &pos); + + while (DOC->numLines() < n) + DOC->insertLine(DOC->numLines()); + + WIDGET->cursorGoto(n - 1, m - 1, false); + THIS->terminal = TRUE; + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + break; + + case 'J': + n = ansi_read_integer(str, l, 1, &pos); + switch(n) + { + case 0: WIDGET->clearDocument(false, true); break; + case 1: WIDGET->clearDocument(true, false); break; + case 2: WIDGET->clearDocument(true, true); break; + } + + case 'K': + n = ansi_read_integer(str, l, 0, &pos); + switch (n) + { + case 0: WIDGET->clearLine(false, true); break; + case 1: WIDGET->clearLine(true, false); break; + case 2: WIDGET->clearLine(true, true); break; + } + break; + + case 's': + WIDGET->saveCursor(); + break; + + case 'u': + WIDGET->restoreCursor(); + break; + + case 'X': + n = ansi_read_integer(str, l, 1, &pos); + WIDGET->clearAfter(n); + break; + + default: + print = FALSE; + } + } + + if (print) + { + fprintf(stderr, "ESC "); + for (n = 0; n <= l; n++) + { + uchar c = str[n]; + + if (c < 32) + fprintf(stderr, "\\x%02X", c); + else + fputc(c, stderr); + } + fputc('\n', stderr); + + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + //BREAKPOINT(); + } + + return l; +} + +BEGIN_METHOD(CEDITOR_print, GB_STRING str; GB_INTEGER y; GB_INTEGER x) + + char *str = STRING(str); + int len = LENGTH(str); + int i, j; + unsigned char c; + + DOC->begin(); + + if (!MISSING(y) && !MISSING(x)) + WIDGET->cursorGoto(VARG(y), VARG(x), false); + + j = 0; + for (i = 0; i < len; i++) + { + //if (s[i].latin1() < 32) + // qDebug("%d", s[i].latin1()); + + c = str[i]; + if (c < 32) + { + if (i > j) + print_text(THIS, &str[j], i - j); + + j = i + 1; + + /*if (c != 27) + fprintf(stderr, "\\x%02X\n", c);*/ + + if (c == '\t') + { + WIDGET->insert("\t"); + } + else if (c == '\r') + { + WIDGET->cursorGoto(WIDGET->getLine(), 0, false); + } + else if (c == '\n') + { + print_newline(THIS); + } + else if (c == '\f') // CTRL+L + { + DOC->clear(); + } + else if (c == '\a') + { + WIDGET->flash(); + } + else if (c == '\b') + { + WIDGET->cursorRelGoto(0, -1, false); //WIDGET->getLine(), WIDGET->getColumn() - 1, false); + } + else if (c == 27) + { + i++; + i += ansi_process(THIS, &str[i], len - i); + j = i + 1; + i--; + } + else + { + QString tmp; + tmp.sprintf("^%c", c + 64); + print_text(THIS, tmp, 2, true); + } + } + } + + if (i > j) + print_text(THIS, &str[j], i - j); + + DOC->end(); + +END_METHOD + + +BEGIN_METHOD(CEDITOR_remove, GB_INTEGER y; GB_INTEGER x; GB_INTEGER y2; GB_INTEGER x2) + + DOC->remove(VARG(y), VARG(x), VARG(y2), VARG(x2)); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_undo) + + WIDGET->undo(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_redo) + + WIDGET->redo(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_begin) + + DOC->begin(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_end) + + DOC->end(); + +END_METHOD + +BEGIN_METHOD(CEDITOR_find_next_breakpoint, GB_INTEGER line) + + int line = VARG(line); + + GB.Deprecated("gb.qt4.ext", "Editor.FindNextBreakpoint", "Editor.Breakpoints"); + + for(;;) + { + if (line >= DOC->numLines()) + { + GB.ReturnInteger(-1); + break; + } + + if (DOC->getLineFlag(line, GLine::BreakpointFlag)) + { + GB.ReturnInteger(line); + break; + } + + line++; + } + +END_METHOD + +static void return_flagged_lines(CEDITOR *_object, int flag) +{ + GB_ARRAY array; + int i; + + GB.Array.New(&array, GB_T_INTEGER, 0); + + for (i = 0; i < DOC->numLines(); i++) + { + if (DOC->getLineFlag(i, flag)) + *((int *)GB.Array.Add(array)) = i; + } + + GB.ReturnObject(array); +} + +static void set_flagged_lines(CEDITOR *_object, int flag, GB_ARRAY array) +{ + int i, line; + + if (GB.CheckObject(array)) + return; + + for (i = 0; i < DOC->numLines(); i++) + { + if (DOC->getLineFlag(i, flag)) + DOC->setLineFlag(i, flag, false); + } + + for (i = 0; i < GB.Array.Count(array); i++) + { + line = *((int *)GB.Array.Get(array, i)); + DOC->setLineFlag(line, flag, true); + } +} + +BEGIN_PROPERTY(Editor_Breakpoints) + + if (READ_PROPERTY) + return_flagged_lines(THIS, GLine::BreakpointFlag); + else + set_flagged_lines(THIS, GLine::BreakpointFlag, VPROP(GB_OBJECT)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_Bookmarks) + + if (READ_PROPERTY) + return_flagged_lines(THIS, GLine::BookmarkFlag); + else + set_flagged_lines(THIS, GLine::BookmarkFlag, VPROP(GB_OBJECT)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_CurrentLine) + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->currentLine()); + else + DOC->setCurrentLine(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_find_next_limit, GB_INTEGER line) + + GB.ReturnInteger(DOC->getNextLimit(VARG(line))); + +END_METHOD + +BEGIN_METHOD(CEDITOR_find_next_word, GB_STRING word; GB_INTEGER line) + + int line = VARG(line); + QString word = QSTRING_ARG(word); + QString s; + + for(;;) + { + if (line >= DOC->numLines()) + { + GB.ReturnInteger(-1); + break; + } + + s = DOC->getLine(line).getString(); + if (s.find(word, 0, false) >= 0) + { + GB.ReturnInteger(line); + break; + } + + line++; + } + +END_METHOD + + +BEGIN_PROPERTY(CEDITOR_highlight) + + HIGHLIGHT_NAME *p; + int mode; + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->getHighlightMode()); + else + { + mode = VPROP(GB_INTEGER); + + if (mode == GDocument::Gambas) + { + if (MAIN_load_eval_component()) + { + GB.Error("Cannot load Gambas syntax highlighter"); + return; + } + } + else if (mode > GDocument::Custom) + { + if (GB.Component.Load("gb.eval.highlight")) + { + GB.Error("Cannot load Gambas custom syntax highlighter component"); + return; + } + + p = _highlight_name; + while (p->name) + { + if (p->style == mode) + { + if (GB.GetFunction(&THIS->highlight, (void *)GB.FindClass("Highlight"), p->name, "", "") == 0) + { + p = 0; + break; + } + } + p++; + } + + if (p) + mode = GDocument::Custom; + } + + DOC->setHighlightMode(mode, highlightCustom); + } + +END_PROPERTY + + +/**************************************************************************** + + Editor.Selection + +****************************************************************************/ + +BEGIN_PROPERTY(CEDITOR_selected) + + GB.ReturnBoolean(WIDGET->hasSelection()); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel) + + if (WIDGET->hasSelection()) + WIDGET->getSelection(&_y1, &_x1, &_y2, &_x2); + else + _x1 = _y1 = _x2 = _y2 = -1; + + RETURN_SELF(); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_text) + + RETURN_NEW_STRING(WIDGET->getSelectedText().getString()); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_start_line) + + GB.ReturnInteger(_y1); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_start_column) + + GB.ReturnInteger(_x1); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_end_line) + + GB.ReturnInteger(_y2); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_end_column) + + GB.ReturnInteger(_x2); + +END_PROPERTY + +BEGIN_METHOD_VOID(CEDITOR_sel_hide) + + WIDGET->hideSelection(); + +END_METHOD + + + +/**************************************************************************** + + Editor.Lines + +****************************************************************************/ + +BEGIN_PROPERTY(CEDITOR_lines_count) + + GB.ReturnInteger((int)DOC->numLines()); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_lines_get, GB_INTEGER line) + + int line = VARG(line); + + if (line < 0 || line >= DOC->numLines()) + GB.ReturnNull(); + else + { + THIS->line = line; + RETURN_SELF(); + } + +END_METHOD + +BEGIN_METHOD_VOID(Editor_ExpandAll) + + WIDGET->unfoldAll(); + +END_METHOD + +BEGIN_METHOD_VOID(Editor_CollapseAll) + + WIDGET->foldAll(); + +END_METHOD + +/**************************************************************************** + + Editor.Line + +****************************************************************************/ + +BEGIN_PROPERTY(CEDITOR_line_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(DOC->getLine(THIS->line).getString()); + else + { + GString s = GString(QSTRING_PROP()); + DOC->setLine(THIS->line, s); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line_length) + + GB.ReturnInteger(DOC->lineLength(THIS->line)); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line_expanded) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WIDGET->isFolded(THIS->line)); + else + { + if (VPROP(GB_BOOLEAN)) + WIDGET->unfoldLine(THIS->line); + else + WIDGET->foldLine(THIS->line); + } + +END_PROPERTY + +BEGIN_PROPERTY(EditorLine_Limit) + + GB.ReturnBoolean(DOC->hasLimit(THIS->line)); + +END_PROPERTY + +/*BEGIN_PROPERTY(EditorLine_Edited) + + GB.ReturnBoolean(DOC->isLineEditedSomewhere(THIS->line)); + +END_PROPERTY*/ + +BEGIN_METHOD_VOID(CEDITOR_line_get_initial_state) + + uint state; + int tag; + bool alternate; + + DOC->getState(THIS->line, true, state, tag, alternate); + + _highlight_state = state; + _highlight_tag = tag; + _highlight_alternate = alternate; + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_line_refresh) + + DOC->invalidate(THIS->line); + DOC->colorize(THIS->line); + WIDGET->updateLine(THIS->line); + +END_METHOD + +BEGIN_PROPERTY(EditorLine_Bookmark) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->getLineFlag(THIS->line, GLine::BookmarkFlag)); + else + DOC->setLineFlag(THIS->line, GLine::BookmarkFlag, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(EditorLine_Breakpoint) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->getLineFlag(THIS->line, GLine::BreakpointFlag)); + else + DOC->setLineFlag(THIS->line, GLine::BreakpointFlag, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_line_purge, GB_BOOLEAN comment; GB_BOOLEAN string; GB_STRING replace) + + bool comment = VARGOPT(comment, FALSE); + bool string = VARGOPT(string, FALSE); + GString line, result; + uint i, state; + GString replace; + + if (MISSING(replace)) + replace = " "; + else + replace = QSTRING_ARG(replace); + + line = DOC->getLine(THIS->line); + + for (i = 0; i < line.length(); i++) + { + state = DOC->getCharState(THIS->line, i); + if ((!string && state == GLine::String) || (!comment && (state == GLine::Comment || state == GLine::Help))) + result += replace; + else + result += line.at(i); + } + + RETURN_NEW_STRING(result.getString()); + +END_METHOD + + + +/**************************************************************************** + + Editor (again) + +****************************************************************************/ + +BEGIN_METHOD(CEDITOR_new, GB_OBJECT parent) + + GEditor *wid = new GEditor(QT.GetContainer(VARG(parent))); + + QObject::connect(wid, SIGNAL(cursorMoved()), &CEditor::manager, SLOT(moved())); + QObject::connect(wid, SIGNAL(textChanged()), &CEditor::manager, SLOT(changed())); + QObject::connect(wid, SIGNAL(marginDoubleClicked(int)), &CEditor::manager, SLOT(marginDoubleClicked(int))); + QObject::connect(wid, SIGNAL(contentsMoving(int, int)), &CEditor::manager, SLOT(scrolled(int, int))); + + QT.InitWidget(wid, _object, true); + QT.SetWheelFlag(_object); + + THIS->line = -1; + + wid->show(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_free) + + GB.Unref((void **)&THIS->view); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_exit) + + GB.Unref((void **)&_breakpoint_picture); + GB.Unref((void **)&_bookmark_picture); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_view) + + if (READ_PROPERTY) + { + if (THIS->view) + GB.ReturnObject(THIS->view); + else + RETURN_SELF(); + } + else + { + GB.StoreObject(PROP(GB_OBJECT), (void **)&THIS->view); + if (THIS->view && THIS->view != THIS) + { + GEditor *view = (GEditor *)((QT_WIDGET *)THIS->view)->widget; + WIDGET->setDocument(view->getDocument()); + } + else + { + WIDGET->setDocument(0); + GB.StoreObject(NULL, (void **)&THIS->view); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line) + + int line, col; + + WIDGET->getCursor(&line, &col); + GB.ReturnInteger(line); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_column) + + int line, col; + + WIDGET->getCursor(&line, &col); + GB.ReturnInteger(col); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_goto, GB_INTEGER line; GB_INTEGER col; GB_BOOLEAN center) + + if (VARGOPT(center, FALSE)) + WIDGET->cursorCenter(); + WIDGET->cursorGoto(VARG(line), VARG(col), false); + +END_METHOD + +BEGIN_METHOD(CEDITOR_flags_get, GB_INTEGER flag) + + GB.ReturnBoolean(WIDGET->getFlag(VARG(flag))); + +END_METHOD + +BEGIN_METHOD(CEDITOR_flags_set, GB_BOOLEAN value; GB_INTEGER flag) + + WIDGET->setFlag(VARG(flag), VARG(value)); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_indent) + + WIDGET->tab(false); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_unindent) + + WIDGET->tab(true); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_copy) + + WIDGET->copy(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_cut) + + WIDGET->cut(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_paste) + + WIDGET->paste(); + +END_METHOD + +BEGIN_METHOD(CEDITOR_to_pos_x, GB_INTEGER x; GB_INTEGER y) + + int line, col; + int px, py; + + WIDGET->getCursor(&line, &col); + WIDGET->cursorToPos(VARGOPT(y, line), VARG(x), &px, &py); + GB.ReturnInteger(px); + +END_METHOD + +BEGIN_METHOD(CEDITOR_pos_to_line, GB_INTEGER y) + + int line = WIDGET->posToLine(VARG(y)); + + if (WIDGET->isPosOutside()) + GB.ReturnInteger(-1); + else + GB.ReturnInteger(line); + +END_METHOD + +BEGIN_METHOD(CEDITOR_pos_to_column, GB_INTEGER x; GB_INTEGER y) + + int line, col; + + if (WIDGET->posToCursor(VARG(x), VARG(y), &line, &col)) + GB.ReturnInteger(-1); + else + GB.ReturnInteger(col); + +END_METHOD + +BEGIN_METHOD(CEDITOR_styles_get, GB_INTEGER style) + + _style = VARG(style); + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_style_color) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnInteger(style.color.rgb() & 0xFFFFFF); + else + { + style.color = QColor(VPROP(GB_INTEGER) & 0xFFFFFF); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_background) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + { + if (style.background) + GB.ReturnInteger(style.backgroundColor.rgb() & 0xFFFFFF); + else + GB.ReturnInteger(-1); + } + else + { + if (VPROP(GB_INTEGER) == -1) + style.background = false; + else + { + style.background = true; + style.backgroundColor = QColor(VPROP(GB_INTEGER) & 0xFFFFFF); + } + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_bold) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnBoolean(style.bold); + else + { + style.bold = VPROP(GB_BOOLEAN); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_underline) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnBoolean(style.underline); + else + { + style.underline = VPROP(GB_BOOLEAN); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_italic) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnBoolean(style.italic); + else + { + style.italic = VPROP(GB_BOOLEAN); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + + +BEGIN_METHOD(CEDITOR_select, GB_INTEGER y1; GB_INTEGER x1; GB_INTEGER y2; GB_INTEGER x2) + + WIDGET->cursorGoto(VARG(y1), VARG(x1), false); + WIDGET->cursorGoto(VARG(y2), VARG(x2), true); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_select_all) + + WIDGET->selectAll(); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_cursor_x) + + int x, y, px, py; + + WIDGET->getCursor(&y, &x); + WIDGET->cursorToPos(y, x, &px, &py); + GB.ReturnInteger(px); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_cursor_y) + + int x, y, px, py; + + WIDGET->getCursor(&y, &x); + WIDGET->cursorToPos(y, x, &px, &py); + GB.ReturnInteger(py); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line_height) + + GB.ReturnInteger(WIDGET->getLineHeight()); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_char_width) + + GB.ReturnInteger(WIDGET->getCharWidth('m')); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_BreakpointPicture) + + if (READ_PROPERTY) + GB.ReturnObject(_breakpoint_picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), (void **)&_breakpoint_picture); + GEditor::setBreakpointPixmap(QT.GetPixmap(_breakpoint_picture)); + } + +END_PROPERTY + +BEGIN_PROPERTY(Editor_BookmarkPicture) + + if (READ_PROPERTY) + GB.ReturnObject(_bookmark_picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), (void **)&_bookmark_picture); + GEditor::setBookmarkPixmap(QT.GetPixmap(_bookmark_picture)); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_keywords_ucase) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->isKeywordsUseUpperCase()); + else + DOC->setKeywordsUseUpperCase(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_show_string, GB_STRING str; GB_BOOLEAN ignoreCase) + + GString s; + + if (!MISSING(str)) + s = GString(QSTRING_ARG(str)); + + WIDGET->showString(s, VARGOPT(ignoreCase, false)); + +END_METHOD + +/*BEGIN_METHOD(CEDITOR_show_word, GB_INTEGER line; GB_INTEGER col; GB_INTEGER len) + + int line = VARGOPT(line, -1); + int col = VARGOPT(col, -1); + int len = VARGOPT(len, -1); + + WIDGET->showWord(line, col, len); + +END_METHOD*/ + +BEGIN_PROPERTY(CEDITOR_border) + + //QT.BorderProperty(_object, _param); + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasBorder()); + else + WIDGET->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_ScrollBar) + + int scroll; + + if (READ_PROPERTY) + { + scroll = 0; + if (WIDGET->hScrollBarMode() == Q3ScrollView::Auto) + scroll += 1; + if (WIDGET->vScrollBarMode() == Q3ScrollView::Auto) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + WIDGET->setHScrollBarMode((scroll & 1) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + WIDGET->setVScrollBarMode((scroll & 2) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + } + +END_PROPERTY + +BEGIN_PROPERTY(Editor_ScrollX) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->horizontalScrollBar()->value()); + else + WIDGET->horizontalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_ScrollY) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->verticalScrollBar()->value()); + else + WIDGET->verticalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD(Editor_Scroll, GB_INTEGER x; GB_INTEGER y) + + WIDGET->horizontalScrollBar()->setValue(VARG(x)); + WIDGET->verticalScrollBar()->setValue(VARG(y)); + +END_METHOD + +BEGIN_PROPERTY(Editor_LineOffset) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->lineOffset()); + else + WIDGET->setLineOffset(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_Mouse) + + QT.MouseProperty(_object, _param); + + if (!READ_PROPERTY) + WIDGET->saveMouseCursor(); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(Editor_Command, GB_STRING command) + + static COMMAND CommandList[] = + { + { "move-left", &&__MOVE_LEFT }, + { "move-left-word", &&__MOVE_LEFT_WORD }, + { "select-left", &&__MOVE_LEFT, TRUE }, + { "select-left-word", &&__MOVE_LEFT_WORD, TRUE }, + { "move-right", &&__MOVE_RIGHT }, + { "move-right-word", &&__MOVE_RIGHT_WORD }, + { "select-right", &&__MOVE_RIGHT, TRUE }, + { "select-right-word", &&__MOVE_RIGHT_WORD, TRUE }, + { "move-up", &&__MOVE_UP }, + { "move-right-word", &&__MOVE_RIGHT_WORD }, + { "select-right", &&__MOVE_RIGHT, TRUE }, + { "select-right-word", &&__MOVE_RIGHT_WORD, TRUE }, + }; + +END_METHOD +#endif + +#if 0 +BEGIN_PROPERTY(Editor_ShowLimits) + + int limit; + + if (READ_PROPERTY) + { + if (!WIDGET->getFlag(GEditor::ShowProcedureLimits)) + limit = LIMIT_NONE; + else if (WIDGET->getFlag(GEditor::BlendedProcedureLimits)) + limit = LIMIT_BLEND; + else if (WIDGET->getFlags(GEditor::ChangeBackgroundAtLimit)) + limit = LIMIT_BACKGROUND; + else + limit = LIMIT_LINE; + + GB.ReturnInteger(limit); + } + else + { + limit = VPROP(GB_INTEGER); + + WIDGET->setFlag(GEditor::ShowProcedureLimits, limit != LIMIT_NONE); + switch(limit) + { + case LIMIT_BLEND: + WIDGET->setFlag(GEditor::BlendedProcedureLimits, true); + WIDGET->setFlag(GEditor::ChangeBackgroundAtLimit, false); + break; + case LIMIT_BACKGROUND: + WIDGET->setFlag(GEditor::BlendedProcedureLimits, false); + WIDGET->setFlag(GEditor::ChangeBackgroundAtLimit, true); + break; + default: + WIDGET->setFlag(GEditor::BlendedProcedureLimits, false); + WIDGET->setFlag(GEditor::ChangeBackgroundAtLimit, false); + break; + } + } + +END_PROPERTY + +#define IMPLEMENT_FLAG_PROPERTY(_name, _constant) \ +BEGIN_PROPERTY(Editor_##_name) \ +\ + if (READ_PROPERTY) \ + GB.ReturnBoolean(WIDGET->getFlag(GEditor::_constant)); \ + else \ + WIDGET->setFlag(GEditor::_constant, VPROP(GB_BOOLEAN)); \ +\ +END_PROPERTY + +IMPLEMENT_FLAG_PROPERTY(ShowLimits, ShowProcedureLimits) + + GB_CONSTANT("ShowLimits", "i", GEditor::ShowProcedureLimits), + GB_CONSTANT("BlendedLimits", "i", GEditor::BlendedProcedureLimits), + GB_CONSTANT("BackgroundLimits", "i", GEditor::ChangeBackgroundAtLimit), + //GB_CONSTANT("DrawWithRelief", "i", GEditor::DrawWithRelief), + GB_CONSTANT("ShowModifiedLines", "i", GEditor::ShowModifiedLines), + GB_CONSTANT("ShowCurrentLine", "i", GEditor::ShowCurrentLine), + GB_CONSTANT("ShowLineNumbers", "i", GEditor::ShowLineNumbers), + GB_CONSTANT("HighlightBraces", "i", GEditor::HighlightBraces), + GB_CONSTANT("HighlightImmediately", "i", GEditor::HighlightImmediately), + GB_CONSTANT("ShowDots", "i", GEditor::ShowDots), + //GB_CONSTANT("ShowCursorPosition", "i", GEditor::ShowCursorPosition), + GB_CONSTANT("HideMargin", "i", GEditor::HideMargin), + Gb_CONSTANT("BlinkCursor", "i", GEditor::BlinkCursor), +#endif + +/***************************************************************************/ + +GB_DESC CHighlightDesc[] = +{ + GB_DECLARE("Highlight", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", GDocument::None), + GB_CONSTANT("Gambas", "i", GDocument::Gambas), + GB_CONSTANT("Custom", "i", GDocument::Custom), + GB_CONSTANT("HTML", "i", HIGHLIGHT_HTML), + GB_CONSTANT("CSS", "i", HIGHLIGHT_CSS), + GB_CONSTANT("WebPage", "i", HIGHLIGHT_WEBPAGE), + GB_CONSTANT("Diff", "i", HIGHLIGHT_DIFF), + GB_CONSTANT("JavaScript", "i", HIGHLIGHT_JAVASCRIPT), + GB_CONSTANT("C", "i", HIGHLIGHT_C), + GB_CONSTANT("CPlusPlus", "i", HIGHLIGHT_CPLUSPLUS), + GB_CONSTANT("SQL", "i", HIGHLIGHT_SQL), + + GB_CONSTANT("Background", "i", HIGHLIGHT_BACKGROUND), + GB_CONSTANT("Normal", "i", HIGHLIGHT_NORMAL), + GB_CONSTANT("Keyword", "i", HIGHLIGHT_KEYWORD), + GB_CONSTANT("Function", "i", HIGHLIGHT_SUBR), + GB_CONSTANT("Operator", "i", HIGHLIGHT_OPERATOR), + GB_CONSTANT("Symbol", "i", HIGHLIGHT_SYMBOL), + GB_CONSTANT("Number", "i", HIGHLIGHT_NUMBER), + GB_CONSTANT("String", "i", HIGHLIGHT_STRING), + GB_CONSTANT("Comment", "i", HIGHLIGHT_COMMENT), + GB_CONSTANT("Breakpoint", "i", HIGHLIGHT_BREAKPOINT), + GB_CONSTANT("Current", "i", HIGHLIGHT_CURRENT), + GB_CONSTANT("DataType", "i", HIGHLIGHT_DATATYPE), + GB_CONSTANT("Selection", "i", HIGHLIGHT_SELECTION), + GB_CONSTANT("Highlight", "i", HIGHLIGHT_HIGHLIGHT), + GB_CONSTANT("CurrentLine", "i", HIGHLIGHT_LINE), + GB_CONSTANT("Error", "i", HIGHLIGHT_ERROR), + GB_CONSTANT("Help", "i", HIGHLIGHT_HELP), + GB_CONSTANT("Preprocessor", "i", HIGHLIGHT_PREPROCESSOR), + GB_CONSTANT("Alternate", "i", HIGHLIGHT_ALTERNATE), + + GB_STATIC_PROPERTY_READ("Line", "i", Highlight_Line), + GB_STATIC_PROPERTY("State", "i", Highlight_State), + GB_STATIC_PROPERTY("Tag", "i", Highlight_Tag), + GB_STATIC_PROPERTY("ShowLimit", "b", Highlight_ShowLimit), + GB_STATIC_PROPERTY("Text", "s", Highlight_Text), + GB_STATIC_PROPERTY("AlternateState", "b", Highlight_AlternateState), + GB_STATIC_METHOD("Add", NULL, Highlight_Add, "(State)i[(Count)i]"), + + GB_END_DECLARE +}; + + +GB_DESC CEditorSelectionDesc[] = +{ + GB_DECLARE(".Editor.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Text", "s", CEDITOR_sel_text), + GB_PROPERTY_READ("StartLine", "i", CEDITOR_sel_start_line), + GB_PROPERTY_READ("StartColumn", "i", CEDITOR_sel_start_column), + GB_PROPERTY_READ("EndLine", "i", CEDITOR_sel_end_line), + GB_PROPERTY_READ("EndColumn", "i", CEDITOR_sel_end_column), + + GB_METHOD("Hide", NULL, CEDITOR_sel_hide, NULL), + + GB_END_DECLARE +}; + +GB_DESC CEditorLineDesc[] = +{ + GB_DECLARE(".Editor.Line", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CEDITOR_line_text), + GB_PROPERTY_READ("Length", "i", CEDITOR_line_length), + GB_PROPERTY("Expanded", "b", CEDITOR_line_expanded), + GB_PROPERTY("Bookmark", "b", EditorLine_Bookmark), + GB_PROPERTY("Breakpoint", "b", EditorLine_Breakpoint), + GB_PROPERTY_READ("Limit", "b", EditorLine_Limit), + //GB_PROPERTY_READ("Edited", "b", EditorLine_Edited), + GB_METHOD("GetInitialState", NULL, CEDITOR_line_get_initial_state, NULL), + GB_METHOD("Refresh", NULL, CEDITOR_line_refresh, NULL), + GB_METHOD("Purge", "s", CEDITOR_line_purge, "[(Comment)b(String)b(Replace)s]"), + + GB_END_DECLARE +}; + +GB_DESC CEditorLinesDesc[] = +{ + GB_DECLARE(".Editor.Lines", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Editor.Line", CEDITOR_lines_get, "(Line)i"), + GB_PROPERTY_READ("Count", "i", CEDITOR_lines_count), + + GB_END_DECLARE +}; + +GB_DESC CEditorStyleDesc[] = +{ + GB_DECLARE(".Editor.Style", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Color", "i", CEDITOR_style_color), + GB_PROPERTY("Background", "i", CEDITOR_style_background), + GB_PROPERTY("Foreground", "i", CEDITOR_style_color), + GB_PROPERTY("Bold", "b", CEDITOR_style_bold), + GB_PROPERTY("Italic", "b", CEDITOR_style_italic), + GB_PROPERTY("Underline", "b", CEDITOR_style_underline), + + GB_END_DECLARE +}; + +GB_DESC CEditorStylesDesc[] = +{ + GB_DECLARE(".Editor.Styles", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Editor.Style", CEDITOR_styles_get, "(Style)i"), + + GB_END_DECLARE +}; + +GB_DESC CEditorFlagsDesc[] = +{ + GB_DECLARE(".Editor.Flags", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "b", CEDITOR_flags_get, "(Flag)i"), + GB_METHOD("_put", NULL, CEDITOR_flags_set, "(Value)b(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC CEditorDesc[] = +{ + GB_DECLARE("Editor", sizeof(CEDITOR)), GB_INHERITS("Control"), + + GB_CONSTANT("ShowLimits", "i", GEditor::ShowProcedureLimits), + GB_CONSTANT("BlendedLimits", "i", GEditor::BlendedProcedureLimits), + GB_CONSTANT("BackgroundLimits", "i", GEditor::ChangeBackgroundAtLimit), + GB_CONSTANT("ShowModifiedLines", "i", GEditor::ShowModifiedLines), + GB_CONSTANT("ShowCurrentLine", "i", GEditor::ShowCurrentLine), + GB_CONSTANT("ShowLineNumbers", "i", GEditor::ShowLineNumbers), + GB_CONSTANT("HighlightBraces", "i", GEditor::HighlightBraces), + GB_CONSTANT("HighlightImmediately", "i", GEditor::HighlightImmediately), + GB_CONSTANT("ShowDots", "i", GEditor::ShowDots), + GB_CONSTANT("HideMargin", "i", GEditor::HideMargin), + GB_CONSTANT("NoFolding", "i", GEditor::NoFolding), + GB_CONSTANT("AlwaysShowCursor", "i", GEditor::AlwaysShowCursor), + + GB_METHOD("_new", NULL, CEDITOR_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CEDITOR_free, NULL), + GB_STATIC_METHOD("_exit", NULL, CEDITOR_exit, NULL), + + GB_STATIC_PROPERTY("BreakpointPicture", "Picture", Editor_BreakpointPicture), + GB_STATIC_PROPERTY("BookmarkPicture", "Picture", Editor_BookmarkPicture), + + GB_PROPERTY("View", "Editor", CEDITOR_view), + + GB_PROPERTY_READ("Line", "i", CEDITOR_line), + GB_PROPERTY_READ("Column", "i", CEDITOR_column), + GB_METHOD("Goto", NULL, CEDITOR_goto, "(Line)i(Column)i[(Center)b]"), + + GB_PROPERTY_SELF("Flags", ".Editor.Flags"), + + GB_CONSTANT("NoLimit", "i", LIMIT_NONE), + GB_CONSTANT("LineLimit", "i", LIMIT_LINE), + GB_CONSTANT("BlendLimit","i", LIMIT_BLEND), + GB_CONSTANT("BackgroundLimit", "i", LIMIT_BACKGROUND), + + /*GB_PROPERTY("ShowLimits", "i", Editor_ShowLimits), + GB_PROPERTY("ShowModifiedLines", "b", Editor_ShowModifiedLines), + GB_PROPERTY("ShowCurrentLine", "b", Editor_ShowCurrentLines), + GB_PROPERTY("ShowLineNumbers", "b", Editor_ShowLineNumbers), + GB_PROPERTY("HighlightBraces", "b", Editor_HighlightBraces), + GB_PROPERTY("HighlightImmediately", "b", Editor_HighlightImmediately), + GB_PROPERTY("ShowDots", "b", Editor_ShowDots), + GB_PROPERTY("HideMargin", "b", Editor_HideMargin), + GB_PROPERTY("BlinkCursor", "b", Editor_BlinkCursor),*/ + + GB_METHOD("Indent", NULL, CEDITOR_indent, NULL), + GB_METHOD("Unindent", NULL, CEDITOR_unindent, NULL), + GB_METHOD("Copy", NULL, CEDITOR_copy, NULL), + GB_METHOD("Cut", NULL, CEDITOR_cut, NULL), + GB_METHOD("Paste", NULL, CEDITOR_paste, NULL), + GB_METHOD("Undo", NULL, CEDITOR_undo, NULL), + GB_METHOD("Redo", NULL, CEDITOR_redo, NULL), + + //GB_METHOD("Command", NULL, Editor_command, "(Command)s"), + + GB_PROPERTY_SELF("Styles", ".Editor.Styles"), + + GB_METHOD("Select", NULL, CEDITOR_select, "(StartLine)i(StartColumn)i(EndLine)i(EndColumn)i"), + GB_METHOD("SelectAll", NULL, CEDITOR_select_all, NULL), + + GB_PROPERTY_READ("CursorX", "i", CEDITOR_cursor_x), + GB_PROPERTY_READ("CursorY", "i", CEDITOR_cursor_y), + GB_METHOD("ToPosX", "i", CEDITOR_to_pos_x, "(Column)i[(Row)i]"), + GB_METHOD("PosToLine","i", CEDITOR_pos_to_line, "(Y)i"), + GB_METHOD("PosToColumn","i", CEDITOR_pos_to_column, "(X)i(Y)i"), + GB_PROPERTY_READ("LineHeight", "i", CEDITOR_line_height), + GB_PROPERTY_READ("CharWidth", "i", CEDITOR_char_width), + + GB_PROPERTY("Border", "b", CEDITOR_border), + GB_PROPERTY("ScrollBar", "i", Editor_ScrollBar), + //GB_PROPERTY("Background", "i", CEDITOR_background), + + GB_METHOD("ExpandAll", NULL, Editor_ExpandAll, NULL), + GB_METHOD("CollapseAll", NULL, Editor_CollapseAll, NULL), + + GB_METHOD("HighlightAll", NULL, Editor_HighlightAll, NULL), + GB_PROPERTY("Text", "s", Editor_Text), + GB_PROPERTY_READ("Length", "i", Editor_Length), + GB_PROPERTY("EndOfLine", "i", Editor_EndOfLine), + + GB_PROPERTY("Highlight", "i", CEDITOR_highlight), + GB_PROPERTY("KeywordsUseUpperCase", "b", CEDITOR_keywords_ucase), + GB_METHOD("ShowString", NULL, CEDITOR_show_string, "[(String)s(IgnoreCase)b]"), + //GB_METHOD("ShowWord", NULL, CEDITOR_show_word, "[(Line)i(Column)i(Length)i]"), + + GB_PROPERTY("TabSize", "i", CEDITOR_tab_length), + GB_PROPERTY("LineNumberOffset", "i", Editor_LineOffset), + GB_METHOD("Reset", NULL, CEDITOR_reset, NULL), + + GB_PROPERTY("ReadOnly", "b", CEDITOR_read_only), + GB_PROPERTY("Overwrite", "b", CEDITOR_overwrite), + + GB_METHOD("Clear", NULL, CEDITOR_clear, NULL), + + GB_METHOD("Insert", NULL, CEDITOR_insert, "(Text)s[(Line)i(Column)i]"), + GB_METHOD("Print", NULL, CEDITOR_print, "(Text)s[(Line)i(Column)i]"), + GB_METHOD("Remove", NULL, CEDITOR_remove, "(Line)i(Column)i(EndLine)i(EndColumn)i"), + GB_METHOD("Begin", NULL, CEDITOR_begin, NULL), + GB_METHOD("End", NULL, CEDITOR_end, NULL), + + GB_PROPERTY_READ("Selected", "b", CEDITOR_selected), + GB_PROPERTY_READ("Selection", ".Editor.Selection", CEDITOR_sel), + GB_PROPERTY_SELF("Lines", ".Editor.Lines"), + + GB_METHOD("FindNextBreakpoint", "i", CEDITOR_find_next_breakpoint, "(Line)i"), + GB_METHOD("FindNextWord", "i", CEDITOR_find_next_word, "(Word)s(Line)i"), + GB_METHOD("FindNextLimit", "i", CEDITOR_find_next_limit, "(Line)i"), + //GB_METHOD("FindPreviousLimit", "i", CEDITOR_find_next_limit, "(Line)i"), + + GB_PROPERTY("Breakpoints", "Integer[]", Editor_Breakpoints), + GB_PROPERTY("Bookmarks", "Integer[]", Editor_Bookmarks), + GB_PROPERTY("CurrentLine", "i", Editor_CurrentLine), + + GB_PROPERTY("ScrollX", "i", Editor_ScrollX), + GB_PROPERTY("ScrollY", "i", Editor_ScrollY), + GB_METHOD("Scroll", NULL, Editor_Scroll, "(X)i(Y)i"), + + GB_PROPERTY("Mouse", "i", Editor_Mouse), + + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + GB_EVENT("Scroll", NULL, NULL, &EVENT_Scroll), + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Highlight", NULL, NULL, &EVENT_Highlight), + GB_EVENT("Margin", NULL, "(LineNumber)i", &EVENT_Margin), + + EDITOR_DESCRIPTION, + + GB_END_DECLARE +}; + + +CEditor CEditor::manager; + +void CEditor::changed(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_Change, 0); +} + +static void post_event(void *_object, int event) +{ + GB.Raise(THIS, event, 0); + GB.Unref(&_object); +} + +void CEditor::moved(void) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Ref(THIS); + GB.Post2((GB_CALLBACK)post_event, (intptr_t)THIS, EVENT_Cursor); +} + +void CEditor::scrolled(int, int) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Ref(THIS); + GB.Post2((GB_CALLBACK)post_event, (intptr_t)THIS, EVENT_Scroll); +} + +void CEditor::marginDoubleClicked(int line) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_Margin, 1, GB_T_INTEGER, line); +} + diff --git a/gb.qt4/src/ext/CEditor.h b/gb.qt4/src/ext/CEditor.h new file mode 100644 index 00000000..3eed533a --- /dev/null +++ b/gb.qt4/src/ext/CEditor.h @@ -0,0 +1,80 @@ +/*************************************************************************** + + CEditor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CEDITOR_H +#define __CEDITOR_H + +#include "gambas.h" +#include "../gb.qt.h" + +#include "gdocument.h" +#include "gview.h" + +#ifndef __CEDITOR_CPP + +extern GB_DESC CHighlightDesc[]; +extern GB_DESC CEditorLineDesc[]; +extern GB_DESC CEditorLinesDesc[]; +extern GB_DESC CEditorSelectionDesc[]; +extern GB_DESC CEditorStyleDesc[]; +extern GB_DESC CEditorStylesDesc[]; +extern GB_DESC CEditorFlagsDesc[]; +extern GB_DESC CEditorDesc[]; + +#else + +#define THIS ((CEDITOR *)_object) +#define WIDGET ((GEditor *)((QT_WIDGET *)_object)->widget) +#define DOC (WIDGET->getDocument()) +#define MANAGER &CEditor::manager + +#endif + +typedef + struct { + QT_WIDGET widget; + void *view; + int line; + GB_FUNCTION highlight; + bool terminal; + } + CEDITOR; + +class CEditor : public QObject +{ + Q_OBJECT + +public: + + static CEditor manager; + +public slots: + + void changed(void); + void moved(void); + void scrolled(int, int); + //void marginClicked(int); + void marginDoubleClicked(int); +}; + +#endif diff --git a/gb.qt4/src/ext/CLCDNumber.cpp b/gb.qt4/src/ext/CLCDNumber.cpp new file mode 100644 index 00000000..21e22a37 --- /dev/null +++ b/gb.qt4/src/ext/CLCDNumber.cpp @@ -0,0 +1,178 @@ +/*************************************************************************** + + CLCDNumber.cpp + + (c) 2002-2003 Nigel Gerrard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CLCDNUMBER_CPP + +#include "main.h" + +#include +#include + +#include "gambas.h" + +#include "CLCDNumber.h" + +/* #define DEBUG_CLCDNUMBER */ + + +BEGIN_METHOD(CLCDNUMBER_new, GB_OBJECT parent) + + QLCDNumber *wid = new QLCDNumber(QT.GetContainer(VARG(parent))); + + QT.InitWidget(wid, _object, false); + //QT.SetBackgroundRole(_object, QColorGroup::Base); + + wid->setFrameStyle(QFrame::NoFrame); + + wid->show(); + +END_METHOD + +BEGIN_PROPERTY(CLCDNUMBER_value) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->value()); + else + WIDGET->display(VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_digits) + + if (READ_PROPERTY) + #if (HAVE_QTEXT_COMPONENT) + GB.ReturnInteger(WIDGET->numDigits()); + #else + GB.ReturnInteger(WIDGET->digitCount()); + #endif + else + { + int n = VPROP(GB_INTEGER); + + if (n < 1) + n = 1; + else if (n > 32) + n = 32; + + #if (HAVE_QTEXT_COMPONENT) + WIDGET->setNumDigits(n); + #else + WIDGET->setDigitCount(n); + #endif + /* Increasing the number of digits does not redisplay value */ + WIDGET->repaint(); + WIDGET->display(WIDGET->value()); + } + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_decimalpoint) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->smallDecimalPoint()); + else + { + WIDGET->setSmallDecimalPoint(VPROP(GB_BOOLEAN)); + WIDGET->repaint(); + WIDGET->display(WIDGET->value()); /* Solves issue where decimal disappears */ + } + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_overflow) + + GB.ReturnBoolean(WIDGET->checkOverflow(WIDGET->value())); + +END_PROPERTY + + +BEGIN_PROPERTY(CLCDNUMBER_mode) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->mode()); + else + { + switch (VPROP(GB_INTEGER)) + { + case QLCDNumber::Hex: WIDGET->setHexMode(); break; + case QLCDNumber::Dec: WIDGET->setDecMode(); break; + //case QLCDNumber::OCT: WIDGET->setOctMode(); break; + case QLCDNumber::Bin: WIDGET->setBinMode(); break; + } + WIDGET->repaint(); + WIDGET->display(WIDGET->value()); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CLCDNUMBER_segmentStyle) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->segmentStyle()); + else + { + switch VPROP(GB_INTEGER) + { + case QLCDNumber::Outline: WIDGET->setSegmentStyle(QLCDNumber::Outline); break; + case QLCDNumber::Filled: WIDGET->setSegmentStyle(QLCDNumber::Filled); break; + case QLCDNumber::Flat: WIDGET->setSegmentStyle(QLCDNumber::Flat); break; + } + } + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_border) + + QT.FullBorderProperty(_object, _param); + +END_PROPERTY + + +GB_DESC CLCDNumberDesc[] = +{ + GB_DECLARE("LCDNumber", sizeof(CLCDNUMBER)), GB_INHERITS("Control"), + + GB_CONSTANT("Hexadecimal","i", QLCDNumber::Hex), + GB_CONSTANT("Decimal","i", QLCDNumber::Dec), + //GB_CONSTANT("Octal","i", QLCDNumber::OCT), + GB_CONSTANT("Binary","i", QLCDNumber::Bin), + + GB_CONSTANT("Outline","i", QLCDNumber::Outline), + GB_CONSTANT("Filled","i", QLCDNumber::Filled), + GB_CONSTANT("Flat","i", QLCDNumber::Flat), + + GB_METHOD("_new", NULL, CLCDNUMBER_new, "(Parent)Container;"), + + GB_PROPERTY("Value", "f", CLCDNUMBER_value), + GB_PROPERTY("Mode", "i", CLCDNUMBER_mode), + GB_PROPERTY("SmallDecimalPoint", "b", CLCDNUMBER_decimalpoint), + GB_PROPERTY_READ("Overflow", "b", CLCDNUMBER_overflow), + GB_PROPERTY("Digits", "i", CLCDNUMBER_digits), + GB_PROPERTY("Border", "i", CLCDNUMBER_border), + GB_PROPERTY("Style", "i", CLCDNUMBER_segmentStyle), + + LCDNUMBER_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/ext/CLCDNumber.h b/gb.qt4/src/ext/CLCDNumber.h new file mode 100644 index 00000000..a3c7b87d --- /dev/null +++ b/gb.qt4/src/ext/CLCDNumber.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + CLCDNumber.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CLCDNUMBER_H +#define __CLCDNUMBER_H + +#include "gambas.h" +#include "../gb.qt.h" + +#ifndef __CLCDNUMBER_C +extern GB_DESC CLCDNumberDesc[]; +#endif + +typedef + struct { + QT_WIDGET widget; + } + CLCDNUMBER; + +#define THIS ((CLCDNUMBER *)_object) +#define WIDGET ((QLCDNumber *)((QT_WIDGET *)_object)->widget) + +#endif diff --git a/gb.qt4/src/ext/CTextEdit.cpp b/gb.qt4/src/ext/CTextEdit.cpp new file mode 100644 index 00000000..16f49e15 --- /dev/null +++ b/gb.qt4/src/ext/CTextEdit.cpp @@ -0,0 +1,735 @@ +/*************************************************************************** + + CTextEdit.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTEDIT_CPP + +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" +#include "../CConst.h" +#include "../CFont.h" +#include "CTextEdit.h" + + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); + +#if 0 +static void to_pos(Q3TextEdit *wid, int par, int car, int *pos) +{ + int i, l; + int p = 0; + + for (i = 0; i < par; i++) + { + l = wid->paragraphLength(i); + if (l < 0) + break; + p += l + 1; + } + + *pos = p + car; +} + + +static void from_pos(Q3TextEdit *wid, int pos, int *par, int *car) +{ + int i; + int l; + + for (i = 0; i <= wid->paragraphs(); i++) + { + l = wid->paragraphLength(i); + if (l < 0) + { + pos = wid->length(); + i--; + break; + } + if (pos <= l) + break; + pos -= l + 1; + } + + *par = i; + *car = pos; +} + + +static void look_pos(Q3TextEdit *wid, int *line, int *col) +{ + if (*line == -1) + *line = wid->paragraphs(); + + if (*col == -1) + *col = wid->paragraphLength(*line); +} + + +static void get_selection(Q3TextEdit *wid, int *start, int *length) +{ + int pStart, iStart, pEnd, iEnd; + int posEnd; + + wid->getSelection(&pStart, &iStart, &pEnd, &iEnd); + + if (pStart < 0) + { + wid->getCursorPosition(&pStart, &iStart); + to_pos(wid, pStart, iStart, start); + *length = 0; + return; + } + + to_pos(wid, pStart, iStart, start); + to_pos(wid, pEnd, iEnd, &posEnd); + + *length = posEnd - *start; +} +#endif + +static int get_length(void *_object) +{ + if (THIS->length < 0) + { + QTextBlock block = WIDGET->document()->begin(); + int len = 0; + + while (block.isValid()) + { + len += block.length(); + block = block.next(); + } + + THIS->length = len - 1; + } + + return THIS->length; +} + +static int get_column(void *_object) +{ + QTextCursor cursor = WIDGET->textCursor(); + return cursor.position() - cursor.block().position(); +} + +static void to_pos(QTextEdit *wid, int par, int car, int *pos) +{ + QTextCursor cursor = wid->textCursor(); + QTextBlock block = cursor.block(); + int p = 0; + + while (par) + { + if (!block.isValid()) + break; + p += block.length() + 1; + block = block.next(); + par--; + } + + *pos = p + car; +} + + +static void from_pos(void *_object, int pos, int *par, int *car) +{ + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + *par = cursor.blockNumber(); + *car = cursor.position() - cursor.block().position(); +} + + +static void get_selection(QTextEdit *wid, int *start, int *length) +{ + QTextCursor cursor = wid->textCursor(); + + *start = cursor.selectionStart(); + *length = cursor.selectionEnd() - *start; +} + + +/** MyTextEdit *************************************************************/ + +MyTextEdit::MyTextEdit(QWidget *parent) : QTextEdit(parent) +{ + setTabChangesFocus(true); + //viewport()->setMouseTracking(true); +} + +MyTextEdit::~MyTextEdit() +{ +} + +void MyTextEdit::emitLinkClicked(const QString &s) +{ + //d->textOrSourceChanged = FALSE; + emit linkClicked( s ); + //if ( !d->textOrSourceChanged ) + // setSource( s ); +} + +/** TextArea ***************************************************************/ + +BEGIN_PROPERTY(CTEXTAREA_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->document()->toPlainText()); + else + { + WIDGET->document()->setPlainText(QSTRING_PROP()); + //THIS->length = -1; + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_rich_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->document()->toHtml("utf-8")); + else + WIDGET->document()->setHtml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_length) + + GB.ReturnInteger(get_length(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_read_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isReadOnly()); + else + WIDGET->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->lineWrapMode() != QTextEdit::NoWrap); + else + WIDGET->setLineWrapMode(VPROP(GB_BOOLEAN) ? QTextEdit::WidgetWidth : QTextEdit::NoWrap); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_column) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + //GB.ReturnInteger(WIDGET->textCursor().columnNumber()); + GB.ReturnInteger(get_column(THIS)); + else + { + int col = VPROP(GB_INTEGER); + + if (col <= 0) + cursor.movePosition(QTextCursor::QTextCursor::StartOfBlock); + else if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_line) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + GB.ReturnInteger(cursor.blockNumber()); + else + { + int col = get_column(THIS); + int line = VPROP(GB_INTEGER); + + if (line < 0) + cursor.movePosition(QTextCursor::Start); + else if (line >= WIDGET->document()->blockCount()) + cursor.movePosition(QTextCursor::End); + else + { + cursor.setPosition(WIDGET->document()->findBlockByNumber(line).position()); + if (col > 0) + { + if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + } + } + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_pos) + + if (READ_PROPERTY) + { + GB.ReturnInteger(WIDGET->textCursor().position()); + } + else + { + int pos = VPROP(GB_INTEGER); + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(CTEXTAREA_clear) + + WIDGET->clear(); + +END_METHOD + +BEGIN_METHOD(CTEXTAREA_insert, GB_STRING text) + + WIDGET->textCursor().insertText(QSTRING_ARG(text)); + +END_METHOD + + +/** .TextEdit.Selection ****************************************************/ + +BEGIN_PROPERTY(CTEXTAREA_sel_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->textCursor().selection().toPlainText()); + else + WIDGET->textCursor().insertText(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_sel_rich_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->textCursor().selection().toHtml()); + else + WIDGET->textCursor().insertFragment(QTextDocumentFragment::fromHtml(QSTRING_PROP())); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_sel_length) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(length); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_sel_start) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(start); + +END_PROPERTY + +BEGIN_METHOD_VOID(CTEXTAREA_sel_clear) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.clearSelection(); + WIDGET->setTextCursor(cursor); + +END_METHOD + +BEGIN_PROPERTY(CTEXTAREA_selected) + + GB.ReturnBoolean(WIDGET->textCursor().hasSelection()); + +END_PROPERTY + +BEGIN_METHOD(CTEXTAREA_sel_select, GB_INTEGER start; GB_INTEGER length) + + if (MISSING(start) && MISSING(length)) + WIDGET->textCursor().select(QTextCursor::Document); + else if (!MISSING(start) && !MISSING(length)) + { + QTextCursor cursor = WIDGET->textCursor(); + + cursor.setPosition(VARG(start)); + cursor.setPosition(VARG(start) + VARG(length), QTextCursor::KeepAnchor); + + WIDGET->setTextCursor(cursor); + } + +END_METHOD + +BEGIN_METHOD_VOID(CTEXTAREA_sel_all) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.select(QTextCursor::Document); + WIDGET->setTextCursor(cursor); + +END_METHOD + +/***************************************************************************/ + +BEGIN_METHOD(CTEXTAREA_to_pos, GB_INTEGER line; GB_INTEGER col) + + int pos; + + to_pos(WIDGET, VARG(line), VARG(col), &pos); + + GB.ReturnInteger(pos); + +END_METHOD + +BEGIN_METHOD(CTEXTAREA_to_line, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(line); + +END_METHOD + +BEGIN_METHOD(CTEXTAREA_to_col, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(col); + +END_METHOD + +BEGIN_METHOD_VOID(CTEXTAREA_copy) + + WIDGET->copy(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_cut) + + WIDGET->cut(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_paste) + + WIDGET->paste(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_undo) + + WIDGET->undo(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_redo) + + WIDGET->redo(); + +END_METHOD + +BEGIN_METHOD_VOID(CTEXTAREA_ensure_visible) + + WIDGET->ensureCursorVisible(); + +END_METHOD + +BEGIN_PROPERTY(CTEXTAREA_border) + + QT.BorderProperty(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_scrollbar) + + QT.ScrollBarProperty(_object, _param); + +END_PROPERTY + + +/** TextEdit ***************************************************************/ + +BEGIN_METHOD(CTEXTEDIT_new, GB_OBJECT parent) + + QTextEdit *wid = new QTextEdit(QT.GetContainer(VARG(parent))); + + QObject::connect(wid, SIGNAL(textChanged()), &CTextArea::manager, SLOT(changed())); + QObject::connect(wid, SIGNAL(cursorPositionChanged()), &CTextArea::manager, SLOT(cursor())); + + wid->setLineWrapMode(QTextEdit::NoWrap); + + QT.InitWidget(wid, _object, true); + QT.SetWheelFlag(_object); + + THIS->length = -1; + +END_METHOD + +BEGIN_PROPERTY(CTEXTEDIT_scroll_x) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->horizontalScrollBar()->value()); + else + WIDGET->horizontalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_scroll_y) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->verticalScrollBar()->value()); + else + WIDGET->verticalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_width) + + if (WIDGET->document()->isEmpty()) + GB.ReturnInteger(0); + else + GB.ReturnInteger(WIDGET->document()->documentLayout()->documentSize().toSize().width()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_height) + + if (WIDGET->document()->isEmpty()) + GB.ReturnInteger(0); + else + GB.ReturnInteger(WIDGET->document()->documentLayout()->documentSize().toSize().height()); + +END_PROPERTY + +/***************************************************************************/ + +BEGIN_PROPERTY(CTEXTEDIT_format_alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(QT.Alignment(WIDGET->alignment() + Qt::AlignVCenter, ALIGN_NORMAL, false)); + else + WIDGET->setAlignment((Qt::Alignment)(QT.Alignment(VPROP(GB_INTEGER), ALIGN_NORMAL, true) & Qt::AlignHorizontal_Mask)); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CTEXTEDIT_format_position) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->verticalAlignment()); + else + WIDGET->setVerticalAlignment((QTextEdit::VerticalAlignment)VPROP(GB_INTEGER)); + +END_PROPERTY*/ + +static void set_font(QFont &font, void *_object = 0) +{ + WIDGET->setCurrentFont(font); +} + +BEGIN_PROPERTY(CTEXTEDIT_format_font) + + if (READ_PROPERTY) + GB.ReturnObject(QT.CreateFont(WIDGET->currentFont(), set_font, _object)); + else + QT.SetFont(set_font, VPROP(GB_OBJECT), THIS); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTEDIT_format_color) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->textColor().rgb() & 0xFFFFFF); + else + WIDGET->setTextColor(QColor((QRgb)VPROP(GB_INTEGER))); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_background) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->textBackgroundColor().rgb() & 0xFFFFFF); + else + WIDGET->setTextBackgroundColor(QColor((QRgb)VPROP(GB_INTEGER))); + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC CTextEditFormatDesc[] = +{ + GB_DECLARE(".TextEdit.Format", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Alignment", "i", CTEXTEDIT_format_alignment), + //GB_PROPERTY("Position", "i", CTEXTEDIT_format_position), + GB_PROPERTY("Font", "Font", CTEXTEDIT_format_font), + GB_PROPERTY("Color", "i", CTEXTEDIT_format_color), + GB_PROPERTY("Background", "i", CTEXTEDIT_format_background), + + GB_END_DECLARE +}; + +GB_DESC CTextEditSelectionDesc[] = +{ + GB_DECLARE(".TextEdit.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTAREA_sel_text), + GB_PROPERTY("RichText", "s", CTEXTAREA_sel_rich_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTAREA_sel_start), + GB_METHOD("Hide", NULL, CTEXTAREA_sel_clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC CTextEditDesc[] = +{ + GB_DECLARE("TextEdit", sizeof(CTEXTEDIT)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTEXTEDIT_new, "(Parent)Container;"), + + //GB_CONSTANT("Normal", "i", QTextEdit::AlignNormal), + //GB_CONSTANT("SubScript", "i", QTextEdit::AlignSubScript), + //GB_CONSTANT("SuperScript", "i", QTextEdit::AlignSuperScript), + + GB_PROPERTY("ReadOnly", "b", CTEXTAREA_read_only), + + GB_METHOD("Clear", NULL, CTEXTAREA_clear, NULL), + + GB_PROPERTY("Text", "s", CTEXTAREA_text), + GB_PROPERTY("RichText", "s", CTEXTAREA_rich_text), + GB_METHOD("Insert", NULL, CTEXTAREA_insert, "(Text)s"), + GB_METHOD("InsertRichText", NULL, CTEXTAREA_sel_rich_text, "(Text)s"), + + GB_PROPERTY("Paragraph", "i", CTEXTAREA_line), + GB_PROPERTY("Index", "i", CTEXTAREA_column), + GB_PROPERTY("Pos", "i", CTEXTAREA_pos), + //GB_PROPERTY_READ("Line", "i", CTEXTEDIT_line), + + GB_METHOD("ToPos", "i", CTEXTAREA_to_pos, "(Paragraph)i(Index)i"), + GB_METHOD("ToParagraph", "i", CTEXTAREA_to_line, "(Pos)i"), + GB_METHOD("ToIndex", "i", CTEXTAREA_to_col, "(Pos)i"), + + GB_METHOD("EnsureVisible", NULL, CTEXTAREA_ensure_visible, NULL), + + GB_PROPERTY_SELF("Selection", ".TextEdit.Selection"), + GB_METHOD("Select", NULL, CTEXTAREA_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, CTEXTAREA_sel_all, NULL), + GB_METHOD("Unselect", NULL, CTEXTAREA_sel_clear, NULL), + GB_PROPERTY_READ("Selected", "b", CTEXTAREA_selected), + + GB_METHOD("Copy", NULL, CTEXTAREA_copy, NULL), + GB_METHOD("Cut", NULL, CTEXTAREA_cut, NULL), + GB_METHOD("Paste", NULL, CTEXTAREA_paste, NULL), + GB_METHOD("Undo", NULL, CTEXTAREA_undo, NULL), + GB_METHOD("Redo", NULL, CTEXTAREA_redo, NULL), + + GB_PROPERTY("Border", "b", CTEXTAREA_border), + GB_PROPERTY("ScrollBar", "i", CTEXTAREA_scrollbar), + GB_PROPERTY("Wrap", "b", CTEXTAREA_wrap), + + GB_PROPERTY("ScrollX", "i", CTEXTEDIT_scroll_x), + GB_PROPERTY("ScrollY", "i", CTEXTEDIT_scroll_y), + + GB_PROPERTY_READ("TextWidth", "i", CTEXTEDIT_text_width), + GB_PROPERTY_READ("TextHeight", "i", CTEXTEDIT_text_height), + + GB_PROPERTY_SELF("Format", ".TextEdit.Format"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + //GB_EVENT("Link", NULL, "(Path)s", &EVENT_Link), + + TEXTEDIT_DESCRIPTION, + + GB_END_DECLARE +}; + + +/** CTextArea **************************************************************/ + +CTextArea CTextArea::manager; + +void CTextArea::changed(void) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Raise(THIS, EVENT_Change, 0); +} + +void CTextArea::cursor(void) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Raise(THIS, EVENT_Cursor, 0); +} + +#if 0 +void CTextArea::link(const QString &path) +{ + void *_object = QT.GetObject((QWidget *)sender()); + + //THIS_EDIT->change = false; + + GB.Raise(_object, EVENT_Link, 1, + GB_T_STRING, TO_UTF8(path), 0); + + /*if (!THIS_EDIT->change) + { + // This cancels the click on the link (see qt source code) + WIDGET->setSource(WIDGET->source()); + }*/ +} +#endif diff --git a/gb.qt4/src/ext/CTextEdit.h b/gb.qt4/src/ext/CTextEdit.h new file mode 100644 index 00000000..37ef79ce --- /dev/null +++ b/gb.qt4/src/ext/CTextEdit.h @@ -0,0 +1,86 @@ +/*************************************************************************** + + CTextEdit.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTEDIT_H +#define __CTEXTEDIT_H + +#include + +#include "gambas.h" +#include "../gb.qt.h" + +#ifndef __CTEXTEDIT_CPP +extern GB_DESC CTextEditSelectionDesc[]; +extern GB_DESC CTextEditFormatDesc[]; +extern GB_DESC CTextEditDesc[]; +#else + +#define WIDGET ((QTextEdit *)((QT_WIDGET *)_object)->widget) +#define MYTEXTEDIT ((MyTextEdit *)((QT_WIDGET *)_object)->widget) +#define THIS ((CTEXTEDIT *)_object) + +#endif + +typedef + struct { + QT_WIDGET widget; + int length; + bool change; + } + CTEXTEDIT; + +class MyTextEdit: public QTextEdit +{ + Q_OBJECT + +public: + MyTextEdit(QWidget *parent = 0); + ~MyTextEdit(); + +signals: + //void highlighted(const QString&); + void linkClicked(const QString&); + +private: + //void popupDetail( const QString& contents, const QPoint& pos ); + bool linksEnabled() const { return true; } + //void emitHighlighted( const QString &s ); + void emitLinkClicked( const QString &s ); +}; + +class CTextArea : public QObject +{ + Q_OBJECT + +public: + + static CTextArea manager; + +public slots: + + void changed(void); + void cursor(void); + //void link(const QString &); +}; + +#endif diff --git a/gb.qt4/src/ext/Makefile.am b/gb.qt4/src/ext/Makefile.am new file mode 100644 index 00000000..07c4afb6 --- /dev/null +++ b/gb.qt4/src/ext/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.qt4.ext +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt4.ext.la + +gb_qt4_ext_la_LIBADD = @QTEXT_LIB@ +gb_qt4_ext_la_LDFLAGS = -module @LD_FLAGS@ @QTEXT_LDFLAGS@ +gb_qt4_ext_la_CPPFLAGS = -DQT3_SUPPORT @QTEXT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt4_ext_la_SOURCES = \ + main.h main.cpp \ + CLCDNumber.h CLCDNumber_moc.cpp CLCDNumber.cpp \ + CDial.h CDial_moc.cpp CDial.cpp \ + garray.h garray.cpp \ + gstring.h gstring.cpp \ + gdocument.h gdocument.cpp \ + gview.h gview_moc.cpp gview.cpp \ + CEditor.h CEditor_moc.cpp CEditor.cpp \ + CTextEdit.h CTextEdit_moc.cpp CTextEdit.cpp diff --git a/gb.qt4/src/ext/garray.cpp b/gb.qt4/src/ext/garray.cpp new file mode 100644 index 00000000..0e0cc52c --- /dev/null +++ b/gb.qt4/src/ext/garray.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + + garray.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_ARRAY_CPP + +#include "garray.h" + +void GArrayImpl::clear() +{ + GB.FreeArray((void **)(void *)&array); + GB.NewArray((void **)(void *)&array, sizeof(void *), 0); + _count = 0; +} + +void *GArrayImpl::take() +{ + void *d; + + if (count() == 0) + return 0; + + d = array[count() - 1]; + GB.Remove((void **)(void *)&array, count() - 1, 1); + _count--; + return d; +} + +void GArrayImpl::insert(uint i, const void *d) +{ + GB.Insert((void **)(void *)&array, i, 1); + array[i] = (void *)d; + _count++; +} + + +void GArrayImpl::remove(uint i) +{ + GB.Remove((void **)(void *)&array, i, 1); + _count--; +} + +int GArrayImpl::find(const void *d) +{ + int i; + + for (i = 0; i < (int)count(); i++) + if (array[i] == d) + return i; + + return (-1); +} diff --git a/gb.qt4/src/ext/garray.h b/gb.qt4/src/ext/garray.h new file mode 100644 index 00000000..7d2fa1e9 --- /dev/null +++ b/gb.qt4/src/ext/garray.h @@ -0,0 +1,112 @@ +/*************************************************************************** + + garray.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GARRAY_H +#define __GARRAY_H + +#include "gb_common.h" +#include "gambas.h" + +extern "C" GB_INTERFACE GB; + +class GArrayImpl +{ +protected: + void **array; + bool autoDelete; + uint pos; + uint _count; + +public: + GArrayImpl() { GB.NewArray((void **)(void *)&array, sizeof(void *), 0); autoDelete = false; _count = 0; } + ~GArrayImpl() { GB.FreeArray((void **)(void *)&array); } + + uint count() const { return _count; } + void setAutoDelete(bool a) { autoDelete = a; } + void *last() const { return count() ? array[count() - 1] : 0; } + void append(const void *d) { *((const void **)GB.Add((void **)(void *)&array)) = d; _count++; } + void clear(); + void *take(); + void *at(uint i) const { return array[i]; } + void insert(uint i, const void *d); + void remove(uint i); + int find(const void *d); + void *first() { pos = 0; return next(); } + void *next() { return (pos >= count()) ? 0 : array[pos++]; } +}; + +template +class GArray : GArrayImpl +{ +public: + GArray() {} + ~GArray() { clear(); } + + uint count() const { return GArrayImpl::count(); } + bool isEmpty() const { return GArrayImpl::count() == 0; } + void setAutoDelete(bool a) { GArrayImpl::setAutoDelete(a); } + type *last() const { return (type *)GArrayImpl::last(); } + void append(const type *d) { GArrayImpl::append(d); } + + void clear(); + type *take() { return (type *)GArrayImpl::take(); } + type *at(uint i) const { return (type *)GArrayImpl::at(i); } + void insert(uint i, const type *d) { GArrayImpl::insert(i, d); } + void remove(uint i); + int find(const type *d) { return GArrayImpl::find(d); } + void removeRef(const type *d); + + type *first() { return (type *)GArrayImpl::first(); } + type *next() { return (type *)GArrayImpl::next(); } +}; + + +template inline void GArray::clear() +{ + if (autoDelete) + { + for (uint i = 0; i < count(); i++) + delete (type *)array[i]; + } + + GArrayImpl::clear(); +} + +template inline void GArray::remove(uint i) +{ + if (autoDelete) + delete (type *)array[i]; + + GArrayImpl::remove(i); +} + + +template inline void GArray::removeRef(const type *d) +{ + int i = find(d); + if (i >= 0) + remove((uint )i); +} + + +#endif diff --git a/gb.qt4/src/ext/gb.qt4.ext.component b/gb.qt4/src/ext/gb.qt4.ext.component new file mode 100644 index 00000000..4a54be62 --- /dev/null +++ b/gb.qt4/src/ext/gb.qt4.ext.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.qt4.ext +Author=Nigel Gerrard,Benoît Minisini +Require=gb.qt4 diff --git a/gb.qt4/src/ext/gdocument.cpp b/gb.qt4/src/ext/gdocument.cpp new file mode 100644 index 00000000..20525656 --- /dev/null +++ b/gb.qt4/src/ext/gdocument.cpp @@ -0,0 +1,1786 @@ +/*************************************************************************** + + gdocument.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_DOCUMENT_CPP + +#include + +#include "main.h" +#include "gview.h" +#include "gdocument.h" + +//#define DEBUG_UNDO 1 + +#define GMAX(x, y) ((x) > (y) ? (x) : (y)) +#define GMIN(x, y) ((x) < (y) ? (x) : (y)) + +#define COLORIZE_FROM(_y) \ + if (colorizeFrom > (_y)) \ + { \ + colorizeFrom = (_y); \ + /*fprintf(stderr, "colorizeFrom -> %d\n", (_y));*/ \ + } + +/**---- GLine -----------------------------------------------------------*/ + +GLine::GLine() +{ + s = ""; + state = Normal; + alternate = false; + modified = false; + changed = false; + saved = false; + flag = 0; + tag = 0; + proc = false; + unicode = false; + highlight = NULL; + nobreak = false; + baptized = false; + tab = false; +} + +GLine::~GLine() +{ + GB.FreeArray(&highlight); +} + +void GLine::insert(uint pos, const GString &text) +{ + s.insert(pos, text); + if (text.hasUnicode()) + unicode = true; +} + +bool GLine::isEmpty() const +{ + uint i; + + for (i = 0; i < s.length(); i++) + { + if (!s.isSpace(i)) + return false; + } + + return true; +} + +/**---- GCommand -----------------------------------------------------------*/ + +struct GCommandDocument +{ +public: + GEditor *view; + int cx; + int cy; + int sx; + int sy; + int sx2; + int sy2; + + GCommandDocument() { } + GCommandDocument(GDocument *doc); + void process(GDocument *doc) const; + void print() const; + bool equals(GCommandDocument *o) const; +}; + +GCommandDocument::GCommandDocument(GDocument *doc) +{ + view = doc->currentView(); + + view->getCursor(&cy, &cx); + + if (doc->hasSelection()) + doc->getSelection(&sy, &sx, &sy2, &sx2, false); // TODO: store insertMode + else + sx = sy = sx2 = sy2 = -1; +} + +void GCommandDocument::process(GDocument *doc) const +{ + //qDebug("goto %d %d", cx, cy); + view->cursorGoto(cy, cx, false); + if (sx >= 0) + { + //qDebug("select %d %d %d %d", sx, sy, sx2, sy2); + doc->startSelection(view, sy, sx); + doc->endSelection(sy2, sx2); + } +} + +void GCommandDocument::print() const +{ + qDebug("- %d %d [%d %d %d %d]", cx, cy, sx, sy, sx2, sy2); +} + +bool GCommandDocument::equals(GCommandDocument *o) const +{ + return view == o->view && cx == o->cx && cy == o->cy + && sx == o->sx && sy == o->sy && sx2 == o->sx2 && sy2 == o->sy2; +} + +class GCommand +{ +public: + enum Type + { + None, Begin, End, Move, Insert, Delete, Indent, Unindent + }; + GCommandDocument info; + + virtual ~GCommand() { } + virtual Type type() const { return None; } + virtual int nest() const { return 0; } + virtual void print() const { } + virtual bool merge(GCommand *) const { return false; } + virtual void process(GDocument *doc, bool undo) const { } + virtual bool linked() const { return false; } + virtual bool remove(GCommand *) const { return false; } +}; + +class GBeginCommand: public GCommand +{ +public: + bool _linked; + + GBeginCommand(GCommandDocument *info, bool linked = false) { _linked = linked; this->info = *info; } + Type type() const { return Begin; } + void print() const { qDebug("Begin"); info.print(); } + int nest() const { return 1; } + bool linked() const { return _linked; } + void process(GDocument *doc, bool undo) const { info.process(doc); } +}; + +class GEndCommand: public GCommand +{ +public: + bool _linked; + + GEndCommand(bool linked = false) { _linked = linked; } + Type type() const { return End; } + void print() const { qDebug("End"); } + int nest() const { return -1; } + bool linked() const { return _linked; } + bool remove(GCommand *o) const { return (o->type() == Begin); } +}; + +class GDeleteCommand: public GCommand +{ +public: + int x, y, x2, y2; + GString str; + + GDeleteCommand(GCommandDocument *info, int y, int x, int y2, int x2, const GString &str) + { + this->x = x; this->y = y; this->x2 = x2; this->y2 = y2; this->str = str; + this->info = *info; + } + + Type type() const { return Delete; } + void print() const { qDebug("Delete: (%d %d)-(%d %d): '%s'", x, y, x2, y2, TO_UTF8(str.getString())); info.print(); } + + bool merge(GCommand *c) const + { + if (c->type() != type()) + return false; + + GDeleteCommand *o = (GDeleteCommand *)c; + + if (info.view != o->info.view) + return false; + + if (x2 != o->x || y2 != o->y || o->y != o->y2) + return false; + + o->str.prepend(str); + o->x = x; + o->y = y; + //o->info = info; + return true; + } + + void process(GDocument *doc, bool undo) const + { + if (undo) + doc->insert(y, x, str); + else + doc->remove(y, x, y2, x2); + + info.process(doc); + } +}; + +class GInsertCommand: public GDeleteCommand +{ +public: + GInsertCommand(GCommandDocument *info, int y, int x, int y2, int x2, const GString &str): + GDeleteCommand(info, y, x, y2, x2, str) {} + Type type() const { return Insert; } + void print() const { qDebug("Insert: (%d %d)-(%d %d): '%s'", x, y, x2, y2, TO_UTF8(str.getString())); info.print(); } + + bool merge(GCommand *c) const + { + if (c->type() != type()) + return false; + + if (str.length() && str.isNewLine(0)) + return false; + + GInsertCommand *o = (GInsertCommand *)c; + + if (o->info.view != info.view) + return false; + + if (o->str.length() && o->str.isNewLine(str.length() - 1)) + return false; + + if (x != o->x2 || y != o->y2) + return false; + + o->str += str; + o->x2 = x2; + o->y2 = y2; + return true; + } + + void process(GDocument *doc, bool undo) const + { + GDeleteCommand::process(doc, !undo); + } +}; + + +/**---- GDocument -----------------------------------------------------------*/ + +#define FOR_EACH_VIEW(_view) \ + for (GEditor *_view = views.first(); _view ; _view = views.next()) + +void GDocument::init() +{ + selector = NULL; + colorizeFrom = 0; + _disableColorize = 0; + _currentLine = -1; +} + +GDocument::GDocument() +{ + oldCount = 0; + blockUndo = false; + tabWidth = 2; + readOnly = false; + highlightMode = None; + keywordsUseUpperCase = false; + _currentView = NULL; + setEndOfLine(GB_EOL_UNIX); + + //lines = new GArray; + lines.setAutoDelete(true); + + undoList.setAutoDelete(true); + redoList.setAutoDelete(true); + + clear(); + //setText(""); //GString("Gambas\nAlmost\nMeans\nBASIC!\n")); +} + +GDocument::~GDocument() +{ +} + +void GDocument::clear() +{ + uint i; + + clearUndo(); + lines.clear(); + lines.append(new GLine); + init(); + + updateViews(); + + for (i = 0; i < views.count(); i++) + views.at(i)->reset(); +} + + +void GDocument::reset(bool saved) +{ + if (saved) + { + for (uint i = 0; i < lines.count(); i++) + { + lines.at(i)->saved |= lines.at(i)->changed; + lines.at(i)->changed = false; + } + } + else + { + for (uint i = 0; i < lines.count(); i++) + lines.at(i)->saved = lines.at(i)->changed = false; + } + + updateViews(); +} + +GString GDocument::getText() +{ + GString tmp; + + if (lines.count()) + { + for (uint i = 0; i < (lines.count() - 1); i++) + { + tmp += lines.at(i)->s; + tmp += _eol; + } + + tmp += lines.at(lines.count() - 1)->s; + + updateViews(); + } + + return tmp; +} + +GString GDocument::getSelectedText(bool insertMode) +{ + GString tmp, line; + int x1, y1, x2, y2, i; + + if (lines.count() && hasSelection()) + { + getSelection(&y1, &x1, &y2, &x2, insertMode); + + if (insertMode) + { + for (i = y1; i <= y2; i++) + { + line = lines.at(i)->s.mid(x1, x2 - x1); + while ((int)line.length() < (x2 - x1)) + line.append(' '); + + tmp += line; + if (i < y2) + tmp += _eol; + } + } + else + { + if (y1 == y2) + tmp = lines.at(y1)->s.mid(x1, x2 - x1); + else + { + tmp = lines.at(y1)->s.mid(x1); + tmp += _eol; + for (i = y1 + 1; i < y2; i++) + { + tmp += lines.at(i)->s; + tmp += _eol; + } + + tmp += lines.at(y2)->s.left(x2); + } + } + } + + return tmp; +} + +int GDocument::getIndent(int y, bool *empty) +{ + int i; + GString s = lines.at(y)->s; + bool e = true; + + for (i = 0; i < (int)s.length(); i++) + { + if (!s.isSpace(i)) + { + e = false; + break; + } + } + + if (empty) + *empty = e; + + return i; +} + +void GDocument::modifyLine(GLine *l, int y) +{ + //if (!l->modified) + // fprintf(stderr, "modifyLine: %d\n", y); + l->modified = l->changed = true; + updateLineWidth(y); + COLORIZE_FROM(y); +} + +void GDocument::updateLineWidth(int y) +{ + FOR_EACH_VIEW(v) + { + v->updateWidth(y); + } +} + +void GDocument::insertLine(int y) +{ + lines.insert(y, new GLine()); + modifyLine(lines.at(y), y); + FOR_EACH_VIEW(v) { v->lineInserted(y); } +} + +void GDocument::insert(int y, int x, const GString &text, bool doNotMove) +{ + int pos; + int len; + int pos2; + int xs, ys; + GLine *l; + int n = 1; + int nl = 0; + GString rest; + int yy; + int i, ns; + GCommandDocument info(this); + + if (readOnly || text.length() == 0) + { + xAfter = x; + yAfter = y; + return; + } + + FOR_EACH_VIEW(v) + { + v->nx = v->x; + v->ny = v->y; + } + + disableColorize(); + + while (y >= (int)lines.count()) + { + yy = (int)lines.count(); + insertLine(yy); + nl++; + } + + l = lines.at(y); + + ns = x - (int)l->s.length(); + if (ns > 0) + { + GString str; + + for (i = 0; i < ns; i++) + str.append(' '); + + insert(y, x - ns, str, doNotMove); + } + + xs = x; + ys = y; + pos = 0; + + for(;;) + { + pos2 = text.findNextLine(pos, len); + + l = lines.at(y); + + if (len > 0) + { + l->insert(x, text.mid(pos, len)); + modifyLine(l, y); + + //maxLength = GMAX(maxLength, (int)l->s.length()); + //updateLineWidth(y); + + FOR_EACH_VIEW(v) + { + if (v->ny == y && v->nx >= x) + v->nx += len; + } + + x += len; + } + + pos = pos2; + + if (pos == 0) + break; + + if (x < (int)l->s.length()) + { + rest = l->s.mid(x); + + l->s.remove(x, rest.length()); + modifyLine(l, y); + } + + FOR_EACH_VIEW(v) + { + if (v->ny >= y) + v->ny++; + } + + y++; + + insertLine(y); + nl++; + + n = -1; + x = 0; + + } + + if (n < 0 && rest.length()) + { + l->insert(x, rest); + modifyLine(l, y); + //maxLength = GMAX(maxLength, (int)l->s.length()); + //updateLineWidth(y); + } + + FOR_EACH_VIEW(v) + { + v->foldInsert(ys, nl); + } + + begin(); + addUndo(new GInsertCommand(&info, ys, xs, y, x, text)); + enableColorize(); + end(); + + updateViews(ys, n); + + yAfter = y; + xAfter = x; + + emitTextChanged(); + + if (!doNotMove) + { + FOR_EACH_VIEW(v) + { + v->cursorGoto(v->ny, v->nx, FALSE); + } + } +} + +void GDocument::removeLine(int y) +{ + lines.remove(y); + COLORIZE_FROM(y); + FOR_EACH_VIEW(v) { v->lineRemoved(y); } +} + +void GDocument::remove(int y1, int x1, int y2, int x2) +{ + GLine *l, *l2; + GString text, rest; + int y; + GCommandDocument info(this); + + yAfter = y1; + xAfter = x1; + + if (readOnly) + return; + + FOR_EACH_VIEW(v) + { + v->nx = v->x; + v->ny = v->y; + } + + disableColorize(); + + l = lines.at(y1); + + if (y1 == y2) + { + if (x2 >= x1 && x1 < lineLength(y1)) + { + text = l->s.mid(x1, x2 - x1); + + l->s.remove(x1, x2 - x1); + if (text.hasUnicode()) + l->unicode = l->s.hasUnicode(); + + modifyLine(l, y1); + + FOR_EACH_VIEW(v) + { + v->foldRemove(y1); + if (v->ny == y1 && v->nx > x1) + v->nx = GMAX(x1, v->nx - (x2 - x1)); + } + + updateViews(y1); + } + } + else + { + l2 = lines.at(y2); + text = l->s.mid(x1) + _eol; + rest = l2->s.left(x2); + + if (x1 < (int)l->s.length() || x2 < (int)l2->s.length()) + { + l->s = l->s.left(x1) + lines.at(y2)->s.mid(x2); + l->state = 0; // force highlighting of next line. + modifyLine(l, y1); + } + else + updateLineWidth(y1); + + for (y = y1 + 1; y < y2; y++) + text += lines.at(y)->s + _eol; + text += rest; + + for (y = y1 + 1; y <= y2; y++) + { + removeLine(y1 + 1); + } + + FOR_EACH_VIEW(v) + { + v->foldRemove(y1 + 1, y2); + if (v->ny > y1) + { + v->ny = GMAX(y1, v->ny - (y2 - y1)); + if (v->ny == y1) + v->nx = x1; + } + else if (v->ny == y1 && v->nx > x1) + v->nx = x1; + } + + updateViews(y1, -1); + } + + begin(); + addUndo(new GDeleteCommand(&info, y1, x1, y2, x2, text)); + enableColorize(); + end(); + + FOR_EACH_VIEW(v) + { + v->cursorGoto(v->ny, v->nx, false); + } + + emitTextChanged(); +} + +void GDocument::setText(const GString &text) +{ + bool oldReadOnly = readOnly; + int mode; + uint l; + + readOnly = false; + blockUndo = true; + + clear(); + clearUndo(); + + mode = GB_EOL_UNIX; + + l = text.length(); + for (uint i = 0; i < l; i++) + { + char c = text.at(i); + if (c == '\n') + { + break; + } + if (c == '\r') + { + if ((i < (l - 1)) && text.at(i + 1) == '\n') + mode = GB_EOL_WINDOWS; + else + mode = GB_EOL_MAC; + break; + } + } + + setEndOfLine(mode); + + undoLevel++; // Prevent textChanged emission + insert(0, 0, text, true); + reset(false); + undoLevel--; + + blockUndo = false; + readOnly = oldReadOnly; + + FOR_EACH_VIEW(v) + { + v->cursorGoto(0, 0, false); + } + + emitTextChanged(); +} + +void GDocument::unsubscribe(GEditor *view) +{ + //qDebug("unsubscribe %p -> %p", view, this); + views.removeRef(view); + + if (views.count() == 0) + delete this; + else + { + if (_currentView == view) + _currentView = views.at(0); + } +} + +void GDocument::subscribe(GEditor *view) +{ + //qDebug("subscribe %p -> %p", view, this); + views.removeRef(view); + views.append(view); + view->setNumRows(lines.count()); + view->updateContents(); + + if (!_currentView) + _currentView = view; +} + +void GDocument::updateViews(int row, int count) +{ + uint i; + + if (lines.count() > oldCount) + { + oldCount = lines.count(); + FOR_EACH_VIEW(v) + { + v->setNumRows(oldCount); + v->updateHeight(); + } + } + + /*if (maxLength != oldMaxLength) + { + oldMaxLength = maxLength; + FOR_EACH_VIEW(v) + v->updateLength(); + }*/ + + if (row < 0) + { + row = 0; + count = oldCount; + } + else if (count < 0) + { + count = oldCount - row; + } + + count = GMIN((int)oldCount - row, count); + + if (((row + count) < numLines()) && lines.at(row + count)->proc) + count++; + + FOR_EACH_VIEW(v) + { + for (i = row; i < (uint)(row + count); i++) + { + v->updateLine(i); + } + } + + if (lines.count() < oldCount) + { + oldCount = lines.count(); + FOR_EACH_VIEW(v) + { + v->setNumRows(oldCount); + v->updateHeight(); + } + } + + FOR_EACH_VIEW(v) + v->checkMatching(); +} + + +void GDocument::getSelection(int *y1, int *x1, int *y2, int *x2, bool insertMode) +{ + if (!selector) + return; + + if (ys2 >= numLines()) + { + ys2 = numLines() - 1; + if (!insertMode) + xs2 = lineLength(ys2); + } + + if (ys >= numLines()) + { + ys = numLines() - 1; + xs = lineLength(ys); + } + + if (ys2 > ys || (ys2 == ys && xs2 > xs)) + { + *y1 = ys; + *y2 = ys2; + if (x1) *x1 = xs; + if (x2) *x2 = xs2; + } + else + { + *y1 = ys2; + *y2 = ys; + if (x1) *x1 = xs2; + if (x2) *x2 = xs; + } + + if (!insertMode) + { + *x1 = QMIN(*x1, lineLength(*y1)); + *x2 = QMIN(*x2, lineLength(*y2)); + } +} + +void GDocument::startSelection(GEditor *view, int y, int x) +{ + hideSelection(); + ys = y; + xs = x; + ys2 = ys; + xs2 = xs; + selector = view; + updateViews(y); +} + +void GDocument::endSelection(int y, int x) +{ + int y1a, y2a, y1b, y2b; + + getSelection(&y1a, NULL, &y2a, NULL, true); + ys2 = y; + xs2 = x; + getSelection(&y1b, NULL, &y2b, NULL, true); + + /*if (y1a == y1b) + updateViews(GMIN(y2a, y2b), GMAX(y2a, y2b) - GMIN(y2a, y2b) + 1); + else if (y2a == y2b) + updateViews(GMIN(y1a, y1b), GMAX(y1a, y1b) - GMIN(y1a, y1b) + 1); + else*/ + updateViews(GMIN(y1a, y1b), GMAX(y2a, y2b) - GMIN(y1a, y1b) + 1); + + updateViews(y); +} + +void GDocument::hideSelection() +{ + int y1, y2; + + if (!selector) + return; + + getSelection(&y1, NULL, &y2, NULL, true); + selector = NULL; + updateViews(y1, y2 - y1 + 1); +} + +void GDocument::eraseSelection(bool insertMode) +{ + int y1, y2, x1, x2, i; + + if (!selector) + return; + + begin(); + getSelection(&y1, &x1, &y2, &x2, false); + selector = NULL; + if (!insertMode) + remove(y1, x1, y2, x2); + else + { + for (i = y1; i <= y2; i++) + remove(i, x1, i, x2); + } + end(); +} + +void GDocument::clearUndo() +{ + undoList.clear(); + redoList.clear(); + undoLevel = 0; +} + +void GDocument::addUndo(GCommand *c) +{ + if (blockUndo) + return; + + #if DEBUG_UNDO + fprintf(stderr, "addUndo: "); + c->print(); + #endif + + if (!undoList.isEmpty()) + { + if (c->merge(undoList.last())) + { + #if DEBUG_UNDO + qDebug(" MERGE"); + #endif + delete c; + return; + } + else if (c->remove(undoList.last())) + { + #if DEBUG_UNDO + qDebug(" REMOVE"); + #endif + delete c; + delete undoList.take(); + return; + } + } + + if (c->type() == GCommand::End && undoList.count() >= 2 && undoList.at(undoList.count() - 2)->type() == GCommand::Begin) + { + GCommand *o = undoList.take(); + GCommand *begin = undoList.take(); + #if DEBUG_UNDO + qDebug(" NO BEGIN / END"); + #endif + o->info = begin->info; + delete begin; + delete c; + addUndo(o); + return; + /*else + { + undoList.append(begin); + undoList.append(o); + }*/ + } + + undoList.append(c); + + if (!redoList.isEmpty()) + { + redoList.clear(); + //emit redoAvailable(false); + } +} + +void GDocument::begin(bool linked) +{ + if (undoLevel == 0) + textHasChanged = false; + undoLevel++; + if (!blockUndo && (undoLevel == 1)) + { + GCommandDocument info(this); + addUndo(new GBeginCommand(&info, linked)); + } +} + +void GDocument::end(bool linked) +{ + undoLevel--; + if (!blockUndo && undoLevel == 0) addUndo(new GEndCommand(linked)); + if (undoLevel == 0 && textHasChanged) + emitTextChanged(); +} + +void GDocument::addRedo(GCommand * c) +{ + if (blockUndo) + return; + + redoList.append(c); +} + +bool GDocument::undo() +{ + int nest; + + if (undoList.isEmpty() || isReadOnly() || blockUndo) + return true; + + disableColorize(); + + blockUndo = true; + + nest = 0; + begin(); + + #ifdef DEBUG_UNDO + qDebug("BEGIN UNDO"); + #endif + + for(;;) + { + GCommand *c = undoList.take(); + if (!c) + break; + #ifdef DEBUG_UNDO + c->print(); + #endif + c->process(this, true); + nest += c->nest(); + //if (d->undoList.isEmpty()) + // emit undoAvailable(false); + redoList.append(c); + if (!nest && !c->linked()) + break; + } + + end(); + + #ifdef DEBUG_UNDO + qDebug("END UNDO: %d", nest); + #endif + + blockUndo = false; + + enableColorize(); + + return false; +} + +bool GDocument::redo() +{ + int nest; + + if (redoList.isEmpty() || isReadOnly() || blockUndo) + return true; + + disableColorize(); + + blockUndo = true; + + nest = 0; + begin(); + + #ifdef DEBUG_UNDO + qDebug("BEGIN REDO"); + #endif + + for(;;) + { + GCommand *c = redoList.take(); + if (!c) + break; + #ifdef DEBUG_UNDO + c->print(); + #endif + c->process(this, false); + nest += c->nest(); + + /*if (d->redoList.isEmpty()) + emit redoAvailable(false); + if (d->undoList.isEmpty()) + emit undoAvailable(true);*/ + undoList.append(c); + if (!nest && !c->linked()) + break; + } + + end(); + + #ifdef DEBUG_UNDO + qDebug("END REDO: %d", nest); + #endif + + blockUndo = false; + + enableColorize(); + + return false; +} + +int GDocument::wordLeft(int y, int x, bool word) +{ + int xw = x; + GString s = lines.at(y)->s; + + if (!word) + { + while (xw > 0 && s.isSpace(xw - 1)) + xw--; + } + + if (xw > 0) + { + if (s.isWordChar(xw - 1)) + { + for(;;) + { + xw--; + if (xw <= 0 || !s.isWordChar(xw - 1)) + break; + } + } + else if (!word) + { + for(;;) + { + xw--; + if (xw <= 0 || s.isWordChar(xw - 1) || s.isSpace(xw - 1)) + break; + } + } + } + + return xw; +} + +int GDocument::wordRight(int y, int x, bool word) +{ + int xw = x; + GString s = lines.at(y)->s; + int len = s.length(); + + if (xw < len) + { + if (s.isWordChar(xw)) + { + for(;;) + { + xw++; + if (xw >= len || !s.isWordChar(xw)) + break; + } + } + else if (!word) + { + for(;;) + { + xw++; + if (xw >= len || s.isWordChar(xw) || s.isSpace(xw)) + break; + } + } + } + + if (!word) + { + while (xw < len && s.isSpace(xw)) + xw++; + } + + return xw; +} + +int GDocument::getLength() const +{ + uint i, len; + + if (lines.count()) + return 0; + + len = 0; + + for (i = 0; i < lines.count(); i++) + len += lines.at(i)->s.length() + 1; + + return (len - 1); +} + + +GString GDocument::getLine(int y) const +{ + GString tmp; + + if (y >= 0 || y < (int)lines.count()) + tmp = lines.at(y)->s; + + return tmp; +} + +void GDocument::setLine(int y, GString & str) +{ + if (y < 0 || y >= (int)lines.count()) + return; + + begin(); + if (lines.at(y)->s.length()) + remove(y, 0, y, lines.at(y)->s.length()); + if (str.length()) + insert(y, 0, str); + end(); + updateViews(y); +} + +bool GDocument::getLineFlag(int y, int f) const +{ + if (y >= 0 && y < (int)lines.count()) + return lines.at(y)->flag & (1 << f); + else + return false; +} + +void GDocument::setLineFlag(int y, int f, bool b) +{ + if (y < 0 || y >= (int)lines.count()) + return; + + if (b) + lines.at(y)->flag |= (1 << f); + else + lines.at(y)->flag &= ~(1 << f); + + updateViews(y); +} + +int GDocument::convState(int state) +{ + switch(state) + { + case RT_END: return GLine::Normal; + case RT_RESERVED: return GLine::Keyword; + case RT_IDENTIFIER: return GLine::Symbol; + case RT_CLASS: return GLine::Datatype; + case RT_NUMBER: return GLine::Number; + case RT_STRING: return GLine::String; + case RT_SUBR: return GLine::Subr; + case RT_COMMENT: return GLine::Comment; + case RT_OPERATOR: return GLine::Operator; + case RT_DATATYPE: return GLine::Datatype; + case RT_ERROR: return GLine::Error; + case RT_HELP: return GLine::Help; + case RT_PREPROCESSOR: return GLine::Preprocessor; + default: return GLine::Normal; + } +} + +void GDocument::highlightGambas(GEditor *editor, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *data, bool &proc) +{ + const char *src; + EVAL_ANALYZE result; + int i; + + src = TO_UTF8(s.getString()); + EVAL.Analyze(src, LAST_UTF8_LENGTH(), state == GLine::Comment ? RT_COMMENT : RT_END, &result, TRUE); + + GB.NewArray(data, sizeof(GHighlight), result.len); + + for (i = 0; i < result.len; i++) + { + //qDebug("state = %d -> %d len = %d", result.color[i].state, QEditor::convState[result.color[i].state], result.color[i].len); + (*data)[i].state = convState(result.color[i].state); + (*data)[i].alternate = result.color[i].alternate; + (*data)[i].len = result.color[i].len; + } + + s = result.str; + GB.FreeString(&result.str); + + proc = result.proc; + state = convState(result.state); +} + +#if 0 +bool GDocument::highlightTest(GEditor *, uint &state, GString &s, GHighlightArray &data, bool &proc) +{ + QMemArray d(s.length()); + uint i; + int j; + uint nstate = 0; + + if (s.length() == 0) + { + proc = false; + return false; + } + + for (i = 0; i < s.length(); i++) + { + nstate = state; + + if (state == GLine::Normal) + { + if (s[i] == '/' && i < (s.length() - 1) && s[i + 1] == '*') + state = nstate = GLine::Comment; + } + else if (state == GLine::Comment) + { + if (s[i] == '/' && i > 0 && s[i - 1] == '*') + nstate = GLine::Normal; + } + + d[i] = state; + state = nstate; + } + + j = -1; + data.resize(s.length()); + + for (i = 0; i < s.length(); i++) + { + if (i == 0 || d[i] != data[j].state) + { + j++; + data[j].state = d[i]; + data[j].len = 1; + } + else + data[j].len++; + } + + if (j >= 0) + data.resize(j + 1); + + proc = false; + + return false; //qstrcmp((const char *)cs, result.str); +} +#endif + +void GDocument::invalidate(int y) +{ + if (y >= 0 && y < numLines()) + { + lines.at(y)->modified = true; + COLORIZE_FROM(y); + } +} + +void GDocument::colorize(int y, bool force) +{ + GLine *l; + //bool modif; + bool proc; + GString old; + uint state; + bool alternate; + int tag; + bool changed = false; + bool updateAll = false; + int yy; + int updateFrom; + bool undo; + + if (highlightMode == None || highlightCallback == NULL) + return; + + if (y < 0) + return; + + if (force) + { + if (colorizeFrom > y) + colorizeFrom = y; + } + + if (_disableColorize) + { + if (_disableColorizeStart < 0) + _disableColorizeStart = y; + else if (_disableColorizeStart > y) + _disableColorizeStart = y; + return; + } + + if (y < colorizeFrom) + return; + + yy = colorizeFrom; + updateFrom = -1; + + undo = false; + + while (yy < numLines()) + { + l = lines.at(yy); + + if (!l->modified) // || ( isLineEditedSomewhere(yy))) + { + if (yy < y) + { + yy++; + continue; + } + else + break; + } + + if (updateFrom < 0) + updateFrom = yy; + //modif = false; + + if (!force && l->baptized && isLineEditedSomewhere(yy)) + { + yy++; + continue; + } + + //fprintf(stderr, "colorize %d\n", yy); + + getState(yy, false, state, tag, alternate); + + if (l->s.length()) + { + GCommandDocument info(this); + + old = l->s; + GB.FreeArray(&l->highlight); + proc = l->proc; + (*highlightCallback)(views.first(), yy, state, alternate, tag, l->s, &l->highlight, proc); + + updateAll |= proc != l->proc; + l->proc = proc; + + if (old != l->s) + { + if (!undo) + { + undo = true; + begin(); + } + addUndo(new GDeleteCommand(&info, yy, 0, yy, old.length(), old)); + if (l->s.length()) + addUndo(new GInsertCommand(&info, yy, 0, yy, l->s.length(), l->s)); + + //maxLength = GMAX(maxLength, (int)l->s.length()); + updateLineWidth(yy); + //qDebug("colorize: %d has changed: '%s' -> '%s'", y, old.utf8(), l->s.utf8()); + l->changed = true; + changed = true; + } + } + else + { + GB.FreeArray(&l->highlight); + updateAll |= l->proc; + l->proc = false; + } + + if (yy == 0) + l->proc = true; + + //fprintf(stderr, "unmodifyLine: %d\n", yy); + l->modified = false; + l->baptized = true; + + state &= 0x1F; + tag &= 0xFFFF; + + if (l->state == state && l->tag == tag && l->alternate == alternate) + { + if (yy < y) + { + yy++; + continue; + } + else + break; + } + + l->state = state; + l->alternate = alternate; + l->tag = tag; + + yy++; + if (yy < numLines()) + { + //if (!lines.at(yy)->modified) + // fprintf(stderr, "modifyLine: %d\n", yy); + lines.at(yy)->modified = true; + } + } + + if (undo) + end(); + + //if (yedit < 0 || yy < yedit) + colorizeFrom = yy + 1; + //else + // colorizeFrom = yedit; + + //fprintf(stderr, "colorizeFrom -> %d\n", colorizeFrom); + + if (changed) + emitTextChanged(); + + if (updateFrom >= 0) + updateViews(updateFrom, yy - updateFrom + 1); + + if (updateAll) + updateContents(); +} + +void GDocument::colorizeAll() +{ + int y; + + if (highlightMode == None) + return; + + FOR_EACH_VIEW(v) + { + //if (v->viewport()->hasFocus()) + v->leaveCurrentLine(); + } + + for (y = colorizeFrom; y < numLines(); y++) + colorize(y); +} + +#if 0 +void GDocument::baptizeUntil(int y) +{ + /*bool busy = (y - baptismLimit) > 256; + + if (busy) + qApp->setOverrideCursor(Qt::waitCursor);*/ + + while (baptismLimit <= y) + { + colorize(baptismLimit); + baptismLimit++; + } + + /*if (busy) + qApp->restoreOverrideCursor();*/ +} +#endif + +void GDocument::invalidateHighlight() +{ + int i; + + for (i = 0; i < numLines(); i++) + lines.at(i)->modified = true; + + colorizeFrom = 0; + + updateMargin(); + updateViews(); +} + +void GDocument::setHighlightMode(int mode, GHighlightCallback cb) +{ + highlightMode = mode; + + if (mode == Gambas) + highlightCallback = GDocument::highlightGambas; + else + highlightCallback = cb; + + invalidateHighlight(); +} + +void GDocument::setKeywordsUseUpperCase(bool v) +{ + if (v == keywordsUseUpperCase) + return; + + keywordsUseUpperCase = v; + invalidateHighlight(); +} + +void GDocument::emitTextChanged() +{ + if (undoLevel > 0) + { + textHasChanged = true; + return; + } + + FOR_EACH_VIEW(v) + v->docTextChanged(); +} + + +int GDocument::getNextLimit(int y) +{ + for(;;) + { + y++; + if (y >= numLines()) + return (-1); + if (hasLimit(y)) + return y; + } +} + +int GDocument::getPreviousLimit(int y) +{ + for(;;) + { + y--; + if (y < 0) + return (-1); + if (y == 0 || hasLimit(y)) + return y; + } +} + +void GDocument::getState(int y, bool col, uint &state, int &tag, bool &alternate) +{ + if (y == 0) + { + state = GLine::Normal; + alternate = false; + tag = 0; + } + else + { + if (col) + colorize(y - 1); + GLine *l = lines.at(y - 1); + state = l->state; + alternate = l->alternate; + tag = l->tag; + } +} + +int GDocument::getCharState(int y, int x) +{ + GLine *l; + int i; + GHighlight *hl; + + l = lines.at(y); + if (l->modified) + { + if (x < 0 || x > (int)l->s.length()) + return GLine::Background; + else + return GLine::Normal; + } + + for (i = 0; i < GB.Count(l->highlight); i++) + { + hl = &l->highlight[i]; + if (x < hl->len) + return hl->state; + x -= hl->len; + } + + return GLine::Background; +} + +bool GDocument::isLineEditedSomewhere(int y) +{ + //fprintf(stderr, "isLineEditedSomewhere %d ?\n", y); + if (!lines.at(y)->modified) + { + //fprintf(stderr, "--> false (not modified)\n"); + return false; + } + + FOR_EACH_VIEW(v) + { + //fprintf(stderr, "check with %d\n", v->y); + if (v->y == y && !v->getFlag(GEditor::HighlightImmediately)) + { + //fprintf(stderr, "--> true\n"); + return true; + } + } + + //fprintf(stderr, "--> false (no view matches)\n"); + return false; +} + +int GDocument::getLimitIndex(int y) +{ + int i = 0; + + if (y >= numLines()) + y = numLines() - 1; + + if (y < 0) + return -1; + + while (y > 0) + { + if (lines.at(y)->proc) + i++; + y--; + } + + return i; +} + +void GDocument::updateMargin() +{ + FOR_EACH_VIEW(v) + { + v->updateMargin(); + } +} + +void GDocument::disableColorize() +{ + if (_disableColorize == 0) + _disableColorizeStart = -1; + _disableColorize++; +} + +void GDocument::enableColorize() +{ + _disableColorize--; + if (_disableColorize == 0 && _disableColorizeStart >= 0) + { + colorize(_disableColorizeStart); + _disableColorizeStart = -1; + } +} + +void GDocument::updateContents() +{ + FOR_EACH_VIEW(v) + { + v->updateContents(); + } +} + +void GDocument::setCurrentLine(int y) +{ + _currentLine = y; + updateContents(); +} + +void GDocument::setEndOfLine(int mode) +{ + _eol_mode = mode; + + switch (mode) + { + case GB_EOL_WINDOWS: _eol = GString("\r\n"); break; + case GB_EOL_MAC: _eol = GString("\r"); break; + default: _eol = GString("\n"); break; + } +} diff --git a/gb.qt4/src/ext/gdocument.h b/gb.qt4/src/ext/gdocument.h new file mode 100644 index 00000000..955ce7cf --- /dev/null +++ b/gb.qt4/src/ext/gdocument.h @@ -0,0 +1,238 @@ +/*************************************************************************** + + gdocument.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDOCUMENT_H +#define __GDOCUMENT_H + +#include "garray.h" +#include "gstring.h" + +struct GHighlight +{ + unsigned state : 5; + unsigned alternate : 1; + unsigned len : 10; +}; + +#define HIGHLIGHT_LEN_MAX 1023 + +typedef + GHighlight *GHighlightArray; + +class GLine +{ +public: + + enum + { + Background = 0, Normal, Keyword, Subr, + Operator, Symbol, Number, String, + Comment, Breakpoint, Current, Datatype, + Selection, Highlight, Line, Error, + Help, Preprocessor, + NUM_STATE + }; + + enum + { + BookmarkFlag = 0, + BreakpointFlag = 1 + }; + + GString s; + GHighlightArray highlight; + unsigned state : 5; + unsigned alternate : 1; + unsigned modified : 1; + unsigned changed : 1; + unsigned saved : 1; + unsigned flag : 2; + unsigned proc : 1; + unsigned unicode : 1; + unsigned tab : 1; + unsigned baptized : 1; + unsigned nobreak : 1; + signed tag : 16; + + GLine(); + ~GLine(); + void insert(uint pos, const GString &text); + bool isEmpty() const; +}; + +class GCommand; +class GEditor; + +typedef + void (*GHighlightCallback)(GEditor *master, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *highlight, bool &proc); + + +class GDocument +{ +private: + + static int convState(int state); + + GArray undoList; + GArray redoList; + int undoLevel; + int highlightMode; + GHighlightCallback highlightCallback; + uint oldCount; + GEditor *selector; + GEditor *_currentView; + GString _eol; + int xs, ys, xs2, ys2; + int tabWidth; + int colorizeFrom; + int _disableColorize; + int _disableColorizeStart; + int _currentLine; + unsigned _eol_mode : 2; + unsigned readOnly : 1; + unsigned blockUndo : 1; + unsigned keywordsUseUpperCase : 1; + unsigned textHasChanged : 1; + + void init(); + void clearUndo(); + void addUndo(GCommand *); + void addRedo(GCommand *); + void updateViews(int row = -1, int count = 1); + void updateMargin(); + void updateContents(); + +public: + + enum GHighlightMode + { + None = 0, + Gambas = 1, + Custom = 2 + }; + + static void highlightGambas(GEditor *master, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *data, bool &proc); + + GArray lines; + GArray views; + int xAfter, yAfter; + + GDocument(); + ~GDocument(); + + void clear(); + void reset(bool saved); + + void setCurrentView(GEditor *view) { _currentView = view; } + GEditor *currentView() const { return _currentView; } + + GString getText(); + void setText(const GString & text); + int getLength() const; + + int getEndOfLine() const { return _eol_mode; } + void setEndOfLine(int mode); + GString getEndOfLineText() const { return _eol; } + + void subscribe(GEditor *view); + void unsubscribe(GEditor *view); + + void setReadOnly(bool ro) { readOnly = ro; }; + bool isReadOnly() const { return readOnly; } + + void setTabWidth(int tw) { tabWidth = tw; } + int getTabWidth() const { return tabWidth; } + + GString getLine(int y) const; + void setLine(int y, GString & str); + + int currentLine() const { return _currentLine; } + void setCurrentLine(int y); + + bool getLineFlag(int y, int f) const; + void setLineFlag(int y, int f, bool b); + + bool isLineEditedSomewhere(int y); + + int lineLength(int y) const { return lines.at(y)->s.length(); } + int numLines() const { return lines.count(); } + + bool hasLimit(int y) { colorize(y); return lines.at(y)->proc; } + int getLimitIndex(int y); + + void getState(int y, bool colorize, uint &state, int &tag, bool &alternate); + int getCharState(int y, int x); + + int getNextLimit(int y); + int getPreviousLimit(int y); + + void insert(int y, int x, const GString &str, bool doNotMove = false); + void remove(int y, int x, int y2, int x2); + + bool undo(); + bool redo(); + void begin(bool linked = false); + void end(bool linked = false); + + void setHighlightMode(int mode, GHighlightCallback cb = 0); + int getHighlightMode() const { return highlightMode; } + + void setKeywordsUseUpperCase(bool v); + bool isKeywordsUseUpperCase() const { return keywordsUseUpperCase; } + + void colorize(int y, bool force = false); + void colorizeAll(); + void invalidate(int y); + + int getIndent(int y, bool *empty = NULL); + int wordLeft(int y, int x, bool word = false); + int wordRight(int y, int x, bool word = false); + + bool hasSelection() { return selector != NULL && (ys != ys2 || xs != xs2); } + bool hasSelection(GEditor *view) { return hasSelection() && selector == view; } + void getSelection(int *y1, int *x1, int *y2, int *x2, bool insertMode); + GString getSelectedText(bool insertMode); + + void startSelection(GEditor *view, int y, int x); + void endSelection(int y, int x); + void hideSelection(); + void eraseSelection(bool insertMode); + + bool insideUndo() const { return undoLevel; } + + void emitTextChanged(); + + void insertLine(int y); + void removeLine(int y); + +private: + + void checkSelection(); + void updateLineWidth(int y); + void modifyLine(GLine *l, int y); + void disableColorize(); + void enableColorize(); + void invalidateHighlight(); +}; + +#endif diff --git a/gb.qt4/src/ext/gstring.cpp b/gb.qt4/src/ext/gstring.cpp new file mode 100644 index 00000000..52282132 --- /dev/null +++ b/gb.qt4/src/ext/gstring.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + + gstring.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_ARRAY_CPP + +#include "garray.h" +#include "gstring.h" + +bool GString::hasUnicode() const +{ + for (uint i = 0; i < (uint)s.length(); i++) + { + int c = s[i].unicode(); + if (!isStandardChar(c)) + return true; + } + + return false; +} + +int GString::findNextLine(int index, int &len) const +{ + uint l = s.length(); + + for (uint i = index; i < l; i++) + { + int c = s[i].unicode(); + if (c == '\n') + { + len = i - index; + return i + 1; + } + if (c == '\r') + { + if ((i < (l - 1)) && s[i + 1].unicode() == '\n') + { + len = i - index; + return i + 2; + } + else + { + len = i - index; + return i + 1; + } + } + } + + len = l - index; + return 0; +} \ No newline at end of file diff --git a/gb.qt4/src/ext/gstring.h b/gb.qt4/src/ext/gstring.h new file mode 100644 index 00000000..67c2623e --- /dev/null +++ b/gb.qt4/src/ext/gstring.h @@ -0,0 +1,275 @@ +/*************************************************************************** + + gstring.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSTRING_H +#define __GSTRING_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.qt.h" + +#include + +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; + +class GString +{ +private: + QString s; + +public: + GString() { s = ""; } + ~GString() {} + GString(const QString &str); + GString(const GString &str); + QString getString() const { return s; } + uint length() const { return s.length(); } + GString &operator=(const GString &str); + GString &operator=(const QString &str); + GString &operator=(const char *utf8); + GString &operator+=(const GString &str); + GString &operator+=(const char *utf8); + GString &operator+=(const char c); + GString left(uint len) const; + GString right(uint len) const; + GString mid(uint index, uint len = 0xffffffff) const; + GString &remove(uint index, uint len); + GString &append(const GString &str); + GString &append(char c); + GString &prepend(const GString &str); + GString &prepend(char c); + GString &insert(uint index, const GString &str); + bool isNewLine(uint pos) const; + bool isSpace(uint pos) const; + bool isWordChar(uint pos) const; + int indexOf(char c, int index = 0) const; + int indexOf(const GString &str, int index = 0, bool cs = true) const; + char at(uint pos) const; + GString lower() const; + GString upper() const; + bool hasUnicode() const; + bool hasTab() const { return indexOf('\t') >= 0; } + void clear() { s.clear(); } + int findNextLine(int index, int &len) const; + + static bool isStandardChar(ushort c); +}; + +inline GString::GString(const GString &str) +{ + s = str.getString(); +} + +inline GString::GString(const QString &str) +{ + s = str; +} + +inline GString &GString::operator=(const GString &str) +{ + s = str.getString(); + return *this; +} + +inline GString &GString::operator=(const QString &str) +{ + s = str; + return *this; +} + +inline GString &GString::operator=(const char *utf8) +{ + s = TO_QSTRING(utf8); + return *this; +} + +inline GString &GString::operator+=(const GString &str) +{ + s += str.getString(); + return *this; +} + +inline GString &GString::operator+=(const char *utf8) +{ + s += TO_QSTRING(utf8); + return *this; +} + +inline GString &GString::operator+=(const char c) +{ + s += c; + return *this; +} + +inline GString GString::left(uint len) const +{ + return GString(s.left(len)); +} + +inline GString GString::right(uint len) const +{ + return GString(s.right(len)); +} + +inline GString GString::mid(uint index, uint len) const +{ + return GString(s.mid(index, len)); +} + +inline GString GString::lower() const +{ + QString sl = s; + int i; + + for (i = 0; i < s.length(); i++) + sl[i] = tolower(s[i].latin1()); + + return GString(sl); +} + +inline GString GString::upper() const +{ + QString sl = s; + int i; + + for (i = 0; i < s.length(); i++) + sl[i] = toupper(s[i].latin1()); + + return GString(sl); +} + + +inline GString &GString::remove(uint index, uint len) +{ + s.remove(index, len); + return *this; +} + +inline GString &GString::insert(uint index, const GString &str) +{ + s.insert(index, str.getString()); + return *this; +} + +inline GString &GString::append(const GString &str) +{ + s.append(str.getString()); + return *this; +} + +inline GString &GString::append(char c) +{ + s.append(c); + return *this; +} + +inline GString &GString::prepend(const GString &str) +{ + s.prepend(str.getString()); + return *this; +} + +inline GString &GString::prepend(char c) +{ + s.prepend(c); + return *this; +} + +inline int GString::indexOf(char c, int index) const +{ + return s.indexOf(c, index); +} + +inline int GString::indexOf(const GString &str, int index, bool cs) const +{ + return s.indexOf(str.getString(), index, cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +} + +inline bool GString::isSpace(uint pos) const +{ + if (pos >= (uint)s.length()) + return false; + return s[pos].isSpace(); +} + +inline bool GString::isNewLine(uint pos) const +{ + if (pos >= (uint)s.length()) + return false; + return s[pos] == '\n'; +} + +inline bool GString::isWordChar(uint pos) const +{ + if (pos >= (uint)s.length()) + return false; + QChar c = s[pos]; + return c.isLetterOrNumber() || c == '_' || c == '$'; +} + +inline bool operator==(const GString &s1, const GString &s2) +{ + return s1.getString() == s2.getString(); +} + +inline bool operator!=(const GString &s1, const GString &s2) +{ + return s1.getString() != s2.getString(); +} + +inline const GString operator+(const GString &s1, const GString &s2) +{ + GString tmp(s1); + tmp += s2; + return tmp; +} + +inline char GString::at(uint pos) const +{ + if (pos >= (uint)s.length()) + return 0; + return s[pos].latin1(); +} + +inline const GString operator+(const GString &s1, const char c) +{ + GString tmp(s1); + tmp += c; + return tmp; +} + +inline const GString operator+(const char c, const GString &str) +{ + GString tmp; + tmp += c; + tmp += str; + return tmp; +} + +inline bool GString::isStandardChar(ushort c) +{ + return !(c < 32 || (c >= 127 && c < 160) || c == 173 || c > 255); +} + +#endif diff --git a/gb.qt4/src/ext/gview.cpp b/gb.qt4/src/ext/gview.cpp new file mode 100644 index 00000000..1ba24fb5 --- /dev/null +++ b/gb.qt4/src/ext/gview.cpp @@ -0,0 +1,3447 @@ +/*************************************************************************** + + gview.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_VIEW_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "gview.h" + +#define ROUND_WIDTH(_w) ((int)((_w) + 0.4999)) + +#if 0 +static const char *breakpoint_xpm[] = +{ +"8 8 3 1", +" c None", +". c #FF7F7F", +"+ c #FF0000", +" .++++. ", +".++++++.", +"++++++++", +"++++++++", +"++++++++", +"++++++++", +".++++++.", +" .++++. " +}; +#endif + +static QColor defaultColors[GLine::NUM_STATE] = +{ + Qt::white, + Qt::black, + QColor(0x00, 0x80, 0xFF), + Qt::blue, + Qt::blue, + Qt::black, + Qt::red, + Qt::magenta, + Qt::gray, + QColor(0xD0, 0x00, 0x00), + QColor(0x00, 0x80, 0xFF), + QColor(0x00, 0x80, 0xFF), + QColor(0xC0, 0xC0, 0xFF), + QColor(0xFF, 0xFF, 0x00), + QColor(0xE8, 0xE8, 0xF8), + Qt::red, + Qt::gray, + Qt::green +}; + + +/**---- GEditor -----------------------------------------------------------*/ + +QPixmap *GEditor::_cache = 0; +QPixmap *GEditor::_breakpoint = 0; +QPixmap *GEditor::_bookmark = 0; +int GEditor::count = 0; +QStyle *GEditor::_style = 0; + +QImage *_blend_pattern = 0; + +void GEditor::reset() +{ + x = y = xx = 0; + x1m = x2m = 0; + y1m = y2m = -1; + lastx = -1; + _cursor = false; + lineNumberLength = 0; + center = false; + largestLine = 0; + flashed = false; + painting = false; + _showRow = -1; + _showCol = 0; + _showLen = 0; + _posOutside = false; + _checkCache = true; + _ensureCursorVisibleLater = false; + _save_x = _save_y = 0; + + foldClear(); +} + +GEditor::GEditor(QWidget *parent) + : Q3ScrollView(parent), + fm(font()) +{ + int i; + //QStyle *oldStyle; + + if (count == 0) + { + _cache = new QPixmap(); + _style = new QWindowsStyle; + } + + count++; + + setKeyCompression(true); + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); + + _border = true; + + setMouseTracking(true); + viewport()->setMouseTracking(true); + viewport()->setCursor(Qt::ibeamCursor); + saveCursor(); + viewport()->setBackgroundRole(QPalette::Base); + viewport()->setPaletteBackgroundColor(defaultColors[GLine::Background]); + viewport()->setFocusProxy(this); + + //oldStyle = style(); + //Q3ScrollView::setStyle(_style); + //viewport()->setStyle(_style); + //Q3ScrollView::setStyle(oldStyle); + ensurePolished(); + updateViewportAttributes(); + + margin = 0; + doc = 0; + _showStringIgnoreCase = false; + _insertMode = false; + _cellw = _cellh = 0; + _oddLine = false; + _dblclick = false; + _firstLineNumber = 0; + + for (i = 0; i < GLine::NUM_STATE; i++) + { + styles[i].color = defaultColors[i]; + styles[i].bold = i == GLine::Keyword || i == GLine::Help; + styles[i].italic = i == GLine::Comment; + styles[i].underline = i == GLine::Error; + if (i == GLine::Comment || i == GLine::Help) + { + styles[i].background = true; + styles[i].backgroundColor = QColor(0xE8, 0xE8, 0xE8); + } + else + styles[i].background = false; + } + + flags = 1 << ShowDots; + + reset(); + + setDocument(NULL); + + setFont(QFont("monospace", QApplication::font().pointSize())); + + blinkTimer = new QTimer(this); + connect(blinkTimer, SIGNAL(timeout()), this, SLOT(blinkTimerTimeout())); + //startBlink(); + + scrollTimer = new QTimer(this); + connect(scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimerTimeout())); + + //connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(baptizeVisible(int, int))); +} + +GEditor::~GEditor() +{ + doc->unsubscribe(this); + count--; + if (count == 0) + { + delete _cache; + delete _breakpoint; + delete _bookmark; + _cache = 0; + _breakpoint = 0; + _bookmark = 0; + delete _style; + } +} + +void GEditor::setBreakpointPixmap(QPixmap *p) +{ + if (!_breakpoint) + _breakpoint = new QPixmap; + + *_breakpoint = *p; +} + +void GEditor::setBookmarkPixmap(QPixmap *p) +{ + if (!_bookmark) + _bookmark = new QPixmap; + + *_bookmark = *p; +} + +void GEditor::setDocument(GDocument *d) +{ + if (doc) + { + doc->unsubscribe(this); + //disconnect(doc, 0, this, 0); + } + + doc = d; + if (!doc) + doc = new GDocument; + + doc->subscribe(this); + findLargestLine(); + //connect(doc, SIGNAL(textChanged()), this, SLOT(docTextChanged())); +} + +int GEditor::getStringWidth(const QString &s, int len, bool unicode) const +{ + int i; + ushort c; + double w = 0; + int us = -1, ul = 0; + + //if (len < 0) + // len = s.length(); + + if (len == 0) + return 0; + + if (_sameWidth && !unicode) + return ROUND_WIDTH(len * _sameWidth); + + for (i = 0; i < len; i++) + { + c = s.at(i).unicode(); + if (c != 9 && !GString::isStandardChar(c)) + { + if (us < 0) + { + us = i; + ul = 0; + } + ul++; + } + else + { + if (us >= 0) + { + w += fm.width(s.mid(us), ul); + us = -1; + } + if (c == 9) + w = (ROUND_WIDTH(w) + _tabWidth) / _tabWidth * _tabWidth; + else + w += _charWidth[c]; + } + } + + if (us >= 0) + w += fm.width(s.mid(us), ul); + + //qDebug("getStringWidth: %g / %d (%g %d)", w, fm.width(s, len), _sameWidth, unicode); + + return w; +} + +void GEditor::updateCache() +{ + //int nw = QMAX(cache->width(), QMIN(visibleWidth(), _cellw)); + //int nh = QMAX(cache->height(), QMIN(visibleHeight(), _cellh)); + int nw = QMAX(_cache->width(), ROUND_WIDTH(visibleWidth() + _charWidth['m'] * 2)); + int nh = QMAX(_cache->height(), visibleHeight() + _cellh); + + if (nw > 0 && nh > 0 && (nw != _cache->width() || nh != _cache->height())) + _cache->resize(nw, nh); + _checkCache = false; +} + +int GEditor::lineWidth(int y) const +{ + // Add 2 or a character width so that we see the cursor at end of line + GLine *l = doc->lines.at(y); + return margin + getStringWidth(l->s.getString(), l->s.length(), l->unicode) + (_insertMode ? _charWidth['W'] : 2); +} + + +int GEditor::lineWidth(int y, int len) +{ + int ns; + + if (len <= 0) + return margin; + else + { + GLine *l = doc->lines.at(y); + QString s = l->s.getString(); + ns = QMAX(0, len - s.length()); + len = QMIN(len, s.length()); + + /*if (y != lineWidthCacheY) + { + lineWidthCacheY = y; + lineWidthCache.clear(); + //qDebug("y = %d", y); + } + + int lw = lineWidthCache[len]; + if (!lw) + { + //qDebug("lineWidthCache: %d", len); + lw = getStringWidth(s, len); //fm.width(s, len); + lineWidthCache.insert(len, lw); + }*/ + + int lw = getStringWidth(s, len, l->unicode); + lw += margin; + if (ns) lw += ns * _charWidth[' ']; + return lw; + } +} + +int GEditor::findLargestLine() +{ + int w, mw = 0; + + //qDebug("search largest line..."); + + for (int y = 0; y < doc->numLines(); y++) + { + w = lineWidth(y); + if (w > mw) + { + mw = w; + largestLine = y; + } + } + + return mw; +} + +void GEditor::updateWidth(int y) +{ + int w; + + if (largestLine < 0 || largestLine >= numLines()) + { + findLargestLine(); + y = -1; + } + + if (y < 0) + { + w = lineWidth(largestLine); + goto UPDATE_WIDTH; + } + else + { + w = lineWidth(y); + + //qDebug("contentsWidth() = %d _cellw = %d", contentsWidth(), _cellw); + //setCellWidth(QMAX(visibleWidth(), margin + charWidth * doc->getMaxLineLength() + 2)); + + if (w > _cellw) + { + largestLine = y; + goto UPDATE_WIDTH; + } + else if (w < _cellw && y == largestLine) + { + w = findLargestLine(); + goto UPDATE_WIDTH; + } + } + + return; + +UPDATE_WIDTH: + + w = QMAX(visibleWidth(), w); + if (w != _cellw) + { + _cellw = w; + updateViewport(); + } +} + +void GEditor::updateHeight() +{ + _cellh = fm.ascent() + fm.descent() + 3; + //updateCache(); + _checkCache = true; + + updateViewport(); +} + +void GEditor::redrawContents() +{ + updateContents(); + //if (contentsHeight() < visibleHeight()) + // repaintContents(contentsX(), contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight() + contentsX(), true); +} + +static double *get_char_width_table(QFontMetrics &fm, QFont &font) +{ + static QHash cache; + + int i; + QString c, fd; + double *cw; + + fd = font.toString(); + + if (cache.contains(fd)) + return cache[fd]; + + //qDebug("get_char_width_table: %s", (const char *)fd.toUtf8()); + + cw = new double[256]; + + for (i = 0; i < 256; i++) + { + c = QChar(i); + c = c.repeated(64); + cw[i] = (double)fm.width(c) / 64; + //qDebug("_charWidth[%d] = %g", i, _charWidth[i]); + } + + cache.insert(fd, cw); + return cw; +} + +void GEditor::updateFont() +{ + QFont f; + int i; + QString c; + + normalFont = QFont(font()); + normalFont.setKerning(false); + italicFont = QFont(font()); + italicFont.setKerning(false); + italicFont.setItalic(true); + + fm = QFontMetrics(normalFont); + _ytext = fm.ascent() + 1; + + _charWidth = get_char_width_table(fm, normalFont); + + _sameWidth = _charWidth[' ']; + _tabWidth = _charWidth['m'] * 8; + //_charWidth[9] = _sameWidth * 8; + + for (i = 33; i < 127; i++) + { + if (_charWidth[i] != _sameWidth) + { + //qDebug("[%d] = %g!", i, _charWidth[i]); + _sameWidth = 0; + break; + } + } + + if (_sameWidth) + { + for (i = 160; i < 256; i++) + { + if (i == 173) + continue; + if (_charWidth[i] != _sameWidth) + { + //qDebug("[%d] = %g!", i, _charWidth[i]); + _sameWidth = 0; + break; + } + } + } + + //qDebug("%p: sameWidth = %g tabWidth = %d", this, _sameWidth, _tabWidth); + + if (_sameWidth) + { + QString t = "AbCdEfGh01#@WwmM"; + t = t.repeated(4); + + _sameWidth = (double)fm.width(t) / t.length(); + //qDebug("_sameWidth -> %g", _sameWidth); + } + + updateMargin(); + updateWidth(); + updateHeight(); + updateContents(); +} + +void GEditor::changeEvent(QEvent *e) +{ + Q3ScrollView::changeEvent(e); + if (e->type() == QEvent::FontChange) + updateFont(); +} + +static int find_last_non_space(const QString &s) +{ + int i; + ushort c; + + for (i = s.length() - 1; i >= 0; i--) + { + c = s[i].unicode(); + if (c > 32 || c == 9) + return i; + } + return -1; +} + +void GEditor::paintDottedSpaces(QPainter &p, int row, int ps, int ls) +{ + QPoint pa[ls]; + int i, y; + double x, w; + + w = _charWidth[' ']; + x = lineWidth(row, ps) + w / 2; + y = _cellh / 2; + for (i = 0; i < ls; i++) + { + pa[i].setX(ROUND_WIDTH(x)); + pa[i].setY(y); + x += w; + } + + p.setOpacity(0.5); + p.drawPoints(pa, i); + p.setOpacity(1); +} + +static QColor merge_color(const QColor &ca, const QColor &cb) +{ + int r, g, b; + + if (cb.value() >= 128) + { + r = ca.red() * cb.red() / 255; + g = ca.green() * cb.green() / 255; + b = ca.blue() * cb.blue() / 255; + } + else + { + r = ca.red() * (255-cb.red()) / 255; + g = ca.green() * (255-cb.green()) / 255; + b = ca.blue() * (255-cb.blue()) / 255; + } + + return QColor(r, g, b); +} + +/*static QColor blend_color(const QColor &ca, const QColor &cb) +{ + return QColor((ca.red() + cb.red()) / 2, (ca.green() + cb.green()) / 2, (ca.blue() + cb.blue()) / 2); +}*/ + +static QColor calc_color(QColor ca, QColor cb, QColor cd) +{ + if (!ca.isValid() && !cb.isValid()) + return cd; + + if (!ca.isValid()) + return cb; + + if (!cb.isValid()) + return ca; + + return QColor((ca.red() + cb.red()) / 2, (ca.green() + cb.green()) / 2, (ca.blue() + cb.blue()) / 2); +} + +void GEditor::drawTextWithTab(QPainter &p, int sx, int x, int y, const QString &s) +{ + int tp, tnp, yt = y; + + tp = 0; + + for(;;) //while (pos < s.length()) + { + tnp = s.indexOf('\t', tp); + if (tnp < 0) + { + p.drawText(x, y, s.mid(tp)); + break; + } + + if (tnp > tp) + { + p.drawText(x, y, s.mid(tp, tnp - tp)); + x += fm.width(s.mid(tp, tnp - tp)); + } + p.setOpacity(0.5); + p.drawLine(x, yt - 2, x, yt); + p.drawLine(x + 1, yt, x + 2, yt); + p.setOpacity(1); + x = sx + ((x - sx) + _tabWidth) / _tabWidth * _tabWidth; + tp = tnp + 1; + } +} + +static void highlight_text(QPainter &p, int x, int x2, int h, QColor color, QColor) +{ + //int i, j; + + //p.setPen(color); + + /*for (i = -1; i <= 1; i++) + for (j = -1; j <= 1; j++) + p.drawText(x + i, y + j, s);*/ + //color.setAlpha(192); + //p.setCompositionMode(QPainter::CompositionMode_Overlay); + p.fillRect(x, 0, x2 - x, h, color); + //p.setCompositionMode(QPainter::CompositionMode_SourceOver); + //border.setAlpha(128); + //p.setPen(QColor(border)); + //p.drawRect(x, 0, x2 - x, h); +} + +void GEditor::paintText(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int xs1, int xs2, int row, QColor &fbg) +{ + int i; + int len, style, pos; + bool alternate; + QString sd; + GHighlightStyle *st; + bool italic = false; + int nx; + int ps; + QColor bg; + bool draw_bg; + bool show_dots = getFlag(ShowDots); + int xs = x; + + if (l->s.length() == 0) + { + if (l->alternate) + { + nx = _cellw; + + int x1 = x; + int x2 = nx; + int xd1 = xs1; + int xd2 = xs2; + + if (xd1 < x1) + xd1 = x1; + if (xd2 > x2) + xd2 = x2; + + p.fillRect(x, 0, nx - x, h, merge_color(_altBackground, fbg)); + + if (xd2 > xd1) + p.fillRect(xd1, 0, xd2 - xd1, h, styles[GLine::Selection].color); + } + + return; + } + + p.setFont(normalFont); + + pos = 0; + ps = find_last_non_space(l->s.getString()) + 1; + + for (i = 0; i < GB.Count(l->highlight); i++) + { + if (pos > (xmin + lmax)) + break; + + len = l->highlight[i].len; + nx = lineWidth(row, pos + len); + //w = len * charWidth; + + if ((pos + len) >= xmin) + { + style = l->highlight[i].state; + alternate = l->highlight[i].alternate; + st = &styles[style]; + draw_bg = false; + if (st->background) + { + draw_bg = true; + bg = st->backgroundColor; + } + else if (alternate) + { + draw_bg = true; + bg = _altBackground; + if (i == (GB.Count(l->highlight) - 1) && l->alternate) + nx = _cellw; + } + + if (style == GLine::Keyword && doc->isKeywordsUseUpperCase()) + sd = l->s.mid(pos, len).upper().getString(); + else + sd = l->s.mid(pos, len).getString(); + + if (draw_bg) + { + int x1 = x; + int x2 = nx; + int xd1 = xs1; + int xd2 = xs2; + + if (xd1 < x1) + xd1 = x1; + if (xd2 > x2) + xd2 = x2; + + p.fillRect(x, 0, nx - x, h, merge_color(bg, fbg)); + + if (xd2 > xd1) + p.fillRect(xd1, 0, xd2 - xd1, h, styles[GLine::Selection].color); + } + + // Highlight braces + if (getFlag(HighlightBraces)) + { + if (row == y1m && x1m >= 0 && x1m >= pos && x1m < (pos + len)) + highlight_text(p, lineWidth(y1m, x1m), lineWidth(y1m, x1m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + if (row == y2m && x2m >= 0 && x2m >= pos && x2m < (pos + len)) + highlight_text(p, lineWidth(y2m, x2m), lineWidth(y2m, x2m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + } + + if (ps >= 0 && pos >= ps) + { + if (show_dots) + paintDottedSpaces(p, row, pos, QMIN(xmin + lmax - pos, sd.length())); + } + else + { + if (st->italic != italic) + { + italic = st->italic; + p.setFont(italic ? italicFont : normalFont); + } + + if (st->bold) + { + QColor c = st->color; + c.setAlpha(128); + p.setPen(c); + drawTextWithTab(p, xs, x + 1, y, sd); + } + + p.setPen(st->color); + drawTextWithTab(p, xs, x, y, sd); + } + + if (st->underline) + { + p.setOpacity(0.5); + p.drawLine(x, y + 2, nx - 1, y + 2); + p.setOpacity(1); + } + } + + pos += len; + x = nx; + } + + if (pos < (int)l->s.length() && pos < (xmin + lmax)) + { + p.setPen(styles[GLine::Normal].color); + //p.drawText(x, y, l->s.mid(pos).getString()); + drawTextWithTab(p, xs, x, y, l->s.mid(pos).getString()); + if (ps >= 0 && pos >= ps && show_dots) + paintDottedSpaces(p, row, pos, QMIN(xmin + lmax - pos, (int)l->s.length() - pos)); + } +} + +void GEditor::paintShowString(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int row) +{ + int pos; + QString sd; + int nx; + int ps; + QColor bg; + + bg = styles[GLine::Highlight].color; + + if (_showString.length()) + { + pos = 0; + for(;;) + { + if (pos >= (int)l->s.length()) + break; + pos = l->s.indexOf(_showString, pos, !_showStringIgnoreCase); + if (pos < 0) + break; + ps = lineWidth(row, pos); + //if (ps > (xmin + lmax)) + // break; + nx = lineWidth(row, pos + _showString.length()); + p.fillRect(ps, 0, nx - ps, h, bg); + pos += _showString.length(); + } + } + + if (row == _showRow && _showLen > 0 && _showCol >= 0 && _showCol < (int)l->s.length()) + { + ps = lineWidth(row, _showCol); + nx = lineWidth(row, QMIN((int)l->s.length(), _showCol + _showLen)); + p.fillRect(ps, 0, nx - ps, h, bg); + } +} + +static void make_blend(QImage *pix, QColor start) //, bool loop = false) +{ + double r, g, b, a, da; + int n = pix->height(); + int i; + QPainter p; + + pix->fill(0); + + r = start.red(); + g = start.green(); + b = start.blue(); + + if (n == 0) + n = 1; + + a = 0; + da = (128 / ((n + 1) / 2) - 1); + + p.begin(pix); + + for (i = 0; i < ((n + 1) / 2); i++) + { + QBrush brush(QColor((int)r, (int)g, (int)b, qMin(255,(int)a))); + p.fillRect(0, i, pix->width(), 1, brush); + p.fillRect(0, n - i - 1, pix->width(), 1, brush); + a += da; + } + + p.end(); +} + +/*static QColor blend_color(QColor a, QColor b, QColor r) +{ + #define f(c) ((a.c()*r.c() + (255 - a.c())*(255 - r.c())) * b.c() / 255 / 255) + return QColor(f(red), f(green), f(blue)); +}*/ + +void GEditor::paintCell(QPainter &p, int row, int) +{ + QRect ur; + GLine *l; + int x1, y1, x2, y2, xs1, xs2; + QColor color, a, b, c; + int xmin, lmax; + int i; + int realRow; + bool folded; + bool highlight; + bool odd; + bool drawSep; + int ysep, wsep; + + ur = QRect(0, row * _cellh, _cellw, _cellh); + contentsToViewport(ur.x(), ur.y(), x1, y1); + ur.setRect(-x1, -y1, ur.width(), ur.height()); + + if (row < 0) + realRow = row; + else + realRow = viewToReal(row); + + if (realRow < 0 || realRow >= numLines()) + { + QColor color = viewport()->paletteBackgroundColor(); + if (flashed) + color = QColor(QRgb(color.rgb() ^ 0x00FFFFFF)); + p.fillRect(0, 0, _cellw, _cellh, color); + return; + } + + // Colorize as soon as possible + highlight = (doc->getHighlightMode() != GDocument::None) && (getFlag(HighlightImmediately) || !doc->isLineEditedSomewhere(realRow)); //(l->modified && realRow == y && !getFlag(HighlightCurrent))); + if (highlight) + { + painting = true; + doc->colorize(realRow); + painting = false; + } + + l = doc->lines.at(realRow); + + folded = l->proc && isFolded(realRow); + + drawSep = false; + ysep = 0; + if (realRow > 0) + { + int nextRow = viewToReal(row + 1); + + if (l->isEmpty() && nextRow < numLines()) + { + GLine *l2 = doc->lines.at(nextRow); + if (l2->proc && !isFolded(nextRow)) + { + drawSep = true; + ysep = _cellh / 2; + } + } + else if (l->proc && (folded || !doc->lines.at(viewToReal(row - 1))->isEmpty())) + drawSep = true; + } + + if (getFlag(ChangeBackgroundAtLimit)) //) && doc->getHighlightMode() != GDocument::None) + { + if (l->proc) + _oddLine = !_oddLine; + odd = _oddLine; + //drawSep = false; + } + else + odd = false; + + //xmin = (ur.left() - margin) / charWidth; + xmin = posToColumn(realRow, 0) - 1; + if (xmin < 0) + xmin = 0; + //lmax = 2 + visibleWidth() / charWidth; + lmax = 2 + posToColumn(realRow, visibleWidth()) - xmin; + //if (row == 0) + // qDebug("%d: %d %d %d %d (%d %d)", row, ur.left(), ur.top(), ur.width(), ur.height(), xmin, lmax); + + // Line background + if (realRow == doc->currentLine()) //l->flag & (1 << GLine::CurrentFlag)) + a = styles[GLine::Current].color; + + if (getFlag(ShowCurrentLine) && realRow == y) + b = styles[GLine::Line].color; + + //QPainter p(cache); + + if (isEnabled()) + color = calc_color(a, b, odd ? _oddBackground : styles[GLine::Background].color); + else + color = palette().color(QPalette::Disabled, QPalette::Base); + + p.fillRect(0, 0, _cellw, _cellh, color); + + if (drawSep && getFlag(ChangeBackgroundAtLimit) && isEnabled()) + { + if (ysep) + { + //int xsep = qMax(0, margin - qMin(_cellh, 12) - contentsX()); + p.fillRect(0, ysep, visibleWidth(), _cellh - ysep, !odd ? _oddBackground : styles[GLine::Background].color); + } + + drawSep = false; + } + + p.setFont(normalFont); + //p.setFont(painter->font()); + //p.translate(-ur.left(), 0); + + p.translate(-contentsX(), 0); + + // Show string + if ((_showRow == realRow || _showString.length())) + paintShowString(p, l, margin, _ytext, xmin, lmax, _cellh, realRow); + + // Selection background + xs1 = 0; + xs2 = 0; + if (doc->hasSelection()) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + + if (realRow >= y1 && realRow <= y2 && !(realRow == y2 && x2 == 0)) + { + if (_insertMode) + { + x1 = lineWidth(y1, x1); + x2 = lineWidth(y2, x2); + } + else + { + if (realRow > y1 || x1 == 0) + x1 = margin; + else + x1 = lineWidth(y1, x1); + + if (realRow < y2) + x2 = _cellw + 1; + else + x2 = lineWidth(y2, x2); + } + + p.fillRect(x1, 0, x2 - x1, _cellh, styles[GLine::Selection].color); + xs1 = x1; + xs2 = x2; + } + } + + // Procedure separation + + if (drawSep) + { + int xsep = 0; //qMax(0, margin - qMin(_cellh, 12) - contentsX()); + + p.translate(contentsX(), 0); + + if (getFlag(BlendedProcedureLimits)) + { + if (!_blend_pattern) + { + _blend_pattern = new QImage(64, _cellh / 2, QImage::Format_ARGB32_Premultiplied); + make_blend(_blend_pattern, styles[GLine::Selection].color); + } + + wsep = visibleWidth(); + + for (i = xsep; i < wsep; i += _blend_pattern->width()) + p.drawImage(i, ysep, *_blend_pattern, 0, 0, QMIN(wsep - i, _blend_pattern->width()), _cellh - ysep); + + if (xs1 && xs2) + p.fillRect(x1, 0, x2 - x1, _cellh, styles[GLine::Selection].color); + //p.drawTiledImage(0, ysep, visibleWidth(), _cellh - ysep, pattern); + } + else if (getFlag(ChangeBackgroundAtLimit)) + { + if (ysep) + { + p.fillRect(0, ysep, visibleWidth(), _cellh - ysep, !odd ? _oddBackground : styles[GLine::Background].color); + if (xs1 && xs2) + p.fillRect(x1, 0, x2 - x1, _cellh, styles[GLine::Selection].color); + } + } + else if (getFlag(ShowProcedureLimits)) + { + //QBrush brush(styles[GLine::Selection].color, Qt::Dense4Pattern); + //p.fillRect(0, 0, cache->width(), 1, brush); + p.setPen(styles[GLine::Selection].color); + p.setOpacity(0.5); + p.drawLine(xsep, ysep, visibleWidth() - 1, ysep); + p.setOpacity(1); + } + + p.translate(-contentsX(), 0); + } + + // Margin + if (!getFlag(HideMargin) && (margin > ur.left())) + { + //if (!l->flag) + // p.fillRect(0, 0, margin, _cellh, color); //styles[GLine::Background].color); + + if (getFlag(ShowModifiedLines)) + { + if (l->changed || l->saved) + { + int w = qMax(2, margin - qMin(_cellh, 12) - 1); + p.setOpacity(0.5); + p.fillRect(0, 0, w, _cellh, styles[l->changed ? GLine::Breakpoint : GLine::Highlight].color); + p.setOpacity(1); + } + //if (l->modified) + // p.fillRect((margin - 2) / 2, _cellh / 2, 1, 1, styles[GLine::Breakpoint].color); + } + //else + // p.fillRect(0, 0, margin - 2, _cellh, odd ? _oddBackground : styles[GLine::Background].color); + /*else if (getFlag(ShowCurrentLine)) + p.fillRect(0, 0, margin - 2, _cellh, styles[GLine::Line].color);*/ + + //x1 = 0; + + if (getFlag(ShowLineNumbers) && (!drawSep || ysep == 0)) + { + int n = realRow + 1 + _firstLineNumber; + if ((n % 10) == 0 || (l->proc && folded)) + p.setPen(styles[GLine::Normal].color); + else + p.setPen(styles[GLine::Selection].color); + p.drawText(2, _ytext, QString::number(n).rightJustify(lineNumberLength)); + } + } + + // Line text + if (!highlight) + { + // Highlight braces + if (getFlag(HighlightBraces)) + { + if (realRow == y1m && x1m >= 0) + highlight_text(p, lineWidth(y1m, x1m), lineWidth(y1m, x1m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + if (realRow == y2m && x2m >= 0) + highlight_text(p, lineWidth(y2m, x2m), lineWidth(y2m, x2m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + } + + if (l->s.length()) + { + p.setPen(styles[GLine::Normal].color); + //p.drawText(margin + xmin * charWidth, fm.ascent() + 1, l->s.getString().mid(xmin, lmax)); + //p.drawText(QRect(lineWidth(realRow, xmin), 0, visibleWidth(), _cellh), l->s.getString().mid(xmin, lmax), textOption); + //p.drawText(lineWidth(realRow, xmin), fm.ascent(), l->s.getString().mid(xmin, lmax)); + drawTextWithTab(p, lineWidth(realRow, 0), lineWidth(realRow, xmin), _ytext, l->s.getString().mid(xmin, lmax)); + if (getFlag(ShowDots)) + { + i = find_last_non_space(l->s.getString()) + 1; + if (i >= 0 && i < (xmin + lmax)) + paintDottedSpaces(p, realRow, i, QMIN(xmin + lmax, (int)l->s.length()) - i); + } + } + } + else + { + paintText(p, l, margin, _ytext, xmin, lmax, _cellh, xs1, xs2, realRow, color); + } + + // Folding symbol + if (!getFlag(NoFolding) && margin && l->proc) + { + QPalette pal; + QStyleOption opt; + int w; + + pal.setColor(QColorGroup::Foreground, styles[GLine::Normal].color); + w = qMin(_cellh, 12); + //opt.rect = QRect(margin - 12, 0, 12, 12); + opt.rect = QRect(margin - w - 2, 0, w, _cellh); + opt.palette = pal; + opt.state |= QStyle::State_Enabled; + + style()->drawPrimitive(folded ? QStyle::PE_IndicatorArrowRight : QStyle::PE_IndicatorArrowDown, &opt, &p); + } + + // Breakpoint symbol + if ((l->flag & (1 << GLine::BreakpointFlag)) && _breakpoint && !_breakpoint->isNull()) + { + //p.fillRect(margin - 10, 0, 8, _cellh, styles[GLine::Breakpoint].color); + //updateBreakpoint(styles[GLine::Background].color.rgb(), styles[GLine::Breakpoint].color.rgb()); + p.drawPixmap(margin - _breakpoint->width() - 2, (_cellh - _breakpoint->height()) / 2, *_breakpoint); + } + + // Bookmark symbol + if ((l->flag & (1 << GLine::BookmarkFlag)) && _bookmark && !_bookmark->isNull()) + { + //p.fillRect(margin - 10, 0, 8, _cellh, styles[GLine::Breakpoint].color); + //updateBreakpoint(styles[GLine::Background].color.rgb(), styles[GLine::Breakpoint].color.rgb()); + p.drawPixmap(margin - _bookmark->width() - 2, (_cellh - _bookmark->height()) / 2, *_bookmark); + } + + // Text cursor + if (realRow == y && (_cursor || getFlag(AlwaysShowCursor))) + { + QColor color = styles[GLine::Normal].color; + int xc = lineWidth(realRow, x); + int wc = 2; + //p.fillRect(QMIN((int)l->s.length(), x) * charWidth + margin, 0, 1, _cellh, styles[GLine::Normal].color); + if (_insertMode) + { + wc = lineWidth(realRow, x + 1) - xc; + color.setAlpha(80); + } + else + color.setAlpha(160); + + p.fillRect(xc, 0, wc, _cellh, color); + } + + p.translate(contentsX(), 0); + + // Flash + if (flashed) + { + //p.setCompositionMode(QPainter::CompositionMode_Xor); + p.fillRect(0, 0, visibleWidth(), _cellh, Qt::black); + } + + + //if (cache->width() < visibleWidth()) + // qDebug("cache->width() = %d < visibleWidth() = %d", cache->width(), visibleWidth()); + //painter->drawPixmap(ur.left(), 0, *cache, 0, 0, cache->width(), cache->height()); //, _cellw, _cellh); +} + + +void GEditor::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + int rowfirst = rowAt(cy); + int rowlast = rowAt(cy + ch - 1); + + if (getFlag(ChangeBackgroundAtLimit) && rowfirst > 0) + _oddLine = doc->getLimitIndex(viewToReal(rowfirst - 1)) & 1; + else + _oddLine = true; + + if (_checkCache) + updateCache(); + + QPainter pc(_cache); + + // Go through the rows + for (int r = rowfirst; r <= rowlast; ++r) + { + paintCell(pc, r, 0); + pc.translate(0, _cellh); + } + + pc.end(); + + //ur = QRect(0, row * _cellh, _cellw, _cellh); + //qDebug("drawContents: %d %d %d %d : %d %d . %d : %d %d", cx, cy, cw, ch, contentsX(), contentsY(), rowfirst * _cellh, viewport()->x(), viewport()->y()); + //p->setClipRect(cx, cy, cw, ch); + p->drawPixmap(contentsX(), rowfirst * _cellh, *_cache, 0, 0, _cellw, _cellh * (rowlast - rowfirst + 1)); //, _cellw, _cellh); + + //qDebug("%d %d", contentsX(), contentsY()); + //p->setPen(styles[GLine::Normal].color); + //p->drawText(contentsX(), contentsY() + 16, QString::number(y) + " : " + QString::number(x)); + + if (_blend_pattern) + { + delete _blend_pattern; + _blend_pattern = 0; + } +} + +void GEditor::checkMatching() +{ + static GString look("()[]{}"); + + GString str; + char c, match, search; + int pos, len; + bool backward; + int old_y1m, old_y2m, old_x1m, old_x2m; + int level; + bool ignore; + + old_y1m = y1m; + old_y2m = y2m; + old_x1m = x1m; + old_x2m = x2m; + + if (y < 0 || y >= (int)numLines()) + goto __NOT_FOUND; + + str = doc->lines.at(y)->s; + len = str.length(); + + pos = -1; + + if (x > 0) + { + c = str.at(x - 1); + pos = look.indexOf(c); + x1m = x - 1; + } + + if (pos < 0 && x < len) + { + c = str.at(x); + pos = look.indexOf(c); + x1m = x; + } + + if (pos < 0) + goto __NOT_FOUND; + + y1m = y; + + backward = pos & 1; + match = c; + search = look.at(pos + (backward ? -1 : 1)); + + //fprintf(stderr, "find '%c' at pos %d. Searching %s for '%c'\n", match, x1m, backward ? "backward" : "forward", search); + + y2m = y1m; + x2m = x1m; + level = 0; + ignore = false; + + for(;;) + { + if (backward) + { + x2m--; + if (x2m < 0) + { + y2m--; + if (y2m < 0) + goto __NOT_FOUND; + str = doc->lines.at(y2m)->s; + len = str.length(); + x2m = len; + continue; + } + } + else + { + x2m++; + if (x2m >= len) + { + y2m++; + if (y2m >= (int)numLines()) + goto __NOT_FOUND; + str = doc->lines.at(y2m)->s; + len = str.length(); + x2m = -1; + continue; + } + } + + c = str.at(x2m); + + if (ignore) + { + if (c == '"' && (x2m == 0 || str.at(x2m - 1) != '\\')) + ignore = false; + continue; + } + + if (c == search) + { + if (level <= 0) + goto __FOUND; + level--; + } + else if (c == match) + { + level++; + } + else if (c == '"') + { + ignore = true; + } + } + +__NOT_FOUND: + + y1m = y2m = -1; + x1m = x2m = -1; + +__FOUND: + + if (y1m != old_y1m || y2m != old_y2m || x1m != old_x1m || x2m != old_x2m) + { + if (old_y1m >= 0) updateLine(old_y1m); + if (old_y2m >= 0) updateLine(old_y2m); + if (y1m >= 0) updateLine(y1m); + if (y2m >= 0) updateLine(y2m); + //fprintf(stderr, "y1m = %d x1m = %d y2m = %d x2m = %d\n", y1m, x1m, y2m, x2m); + } +} + +#if 0 +void GEditor::oldCheckMatching() +{ + + + GString str = doc->lines.at(y)->s; + int len = (int)str.length(); + + char c, cr; + int pos, d, l, bx, by, bx2; + char s[len]; + bool ignore; + int i; + + bx = -1; + bx2 = -1; + by = -1; + ignore = false; + + if (len == 0 || x > len) + goto __OK; + + pos = -1; + + for (i = 0; i < len; i++) + { + c = str.at(i); + + if (ignore) + { + if (c == '\\') + { + s[i] = c; + i++; + s[i] = (i < len) ? str.at(i) : 0; + continue; + } + + if (c == '"') + ignore = false; + else + c = ' '; + } + else + { + if (c == '"') + ignore = true; + } + + s[i] = c; + } + + c = 0; + + if (x > 0 && x <= len) + { + bx2 = x - 1; + c = s[bx2]; + pos = look.find(c); + } + + if (pos < 0 && x < len) + { + bx2 = x; + c = s[bx2]; + pos = look.find(c); + } + + if (c && pos >= 0) + { + d = (pos & 1) ? -1 : 1; + cr = look.at(pos + d); + + pos = bx2; + l = 0; + for(;;) + { + pos += d; + if (pos < 0 || pos >= len) + break; + + if (s[pos] == c) + l++; + else if (s[pos] == cr) + { + l--; + if (l < 0) + { + bx = pos; + by = y; + break; + } + } + } + } + +__OK: + + if (by != ym || bx != x1m || bx2 != x2m) + { + if (ym >= 0) + updateLine(ym); + ym = by; + x1m = bx; + x2m = bx2; + if (ym >= 0) + updateLine(ym); + } +} +#endif + +void GEditor::leaveCurrentLine() +{ + if (y < 0 || y >= numLines()) + return; + + doc->colorize(y, true); + + if (!_insertMode && x > lineLength(y)) + x = lineLength(y); +} + + +bool GEditor::cursorGoto(int ny, int nx, bool mark) +{ + bool setxx; + bool change = false; + + if (!mark) + { + if (doc->hasSelection()) + doc->hideSelection(); + } + + setxx = xx != nx; + + if (ny == y) + { + if (nx < 0 && ny > 0) + { + ny = viewToReal(realToView(y) - 1); + nx = lineLength(ny); + } + else if (!_insertMode && nx > lineLength(ny) && ny < (numLines() - 1)) + { + ny = viewToReal(realToView(y) + 1); + if (ny >= numLines()) + ny = y; + else + nx = 0; + } + } + + if (ny < 0) + { + nx = QMAX(0, nx); + ny = 0; + } + else if (ny >= numLines()) + { + ny = numLines() - 1; + nx = lineLength(ny); + } + else + ny = checkFolded(ny); + + if (nx < 0) + nx = 0; + else + { + int xmax = _insertMode ? QMAX((int)(_cellw / _charWidth[' ']) + 1, lineLength(largestLine)) : lineLength(ny); + + if (nx > xmax) + nx = xmax; + } + + if (ny != y) + leaveCurrentLine(); + + if (y != ny || x != nx) + { + int oy = y; + + if (mark) + { + if (!doc->hasSelection(this)) + doc->startSelection(this, y, x); + } + + y = ny; + x = nx; + if (setxx) + xx = x; + + updateLine(oy); + + if (hasFocus()) + startBlink(); + else + updateLine(y); + + ensureCursorVisible(); + //QTimer::singleShot(0, this, SLOT(ensureCursorVisible())); + + change = true; + + if (mark) + doc->endSelection(y, x); + + if (oy != y && !doc->insideUndo()) + _cutBuffer.clear(); + + emit cursorMoved(); + } + else if (center) + ensureCursorVisible(); + + checkMatching(); + + return change; +} + +void GEditor::insert(QString text) +{ + doc->begin(); + doc->eraseSelection(_insertMode); + unfoldLine(y); + doc->insert(y, x, text); + doc->end(); + cursorGoto(doc->yAfter, doc->xAfter, false); +} + +void GEditor::cursorLeft(bool shift, bool ctrl) +{ + if (ctrl && x > 0) + { + int nx = doc->wordLeft(y, x); + cursorGoto(y, nx, shift); + } + else + cursorGoto(y, x - 1, shift); +} + + +void GEditor::cursorRight(bool shift, bool ctrl) +{ + if (ctrl && x < lineLength(y)) + { + int nx = doc->wordRight(y, x); + cursorGoto(y, nx, shift); + } + else + cursorGoto(y, x + 1, shift); +} + + +void GEditor::cursorUp(bool shift, bool ctrl, bool alt) +{ + if (alt) + { + if (ctrl) + movePreviousSameIndent(shift); + else + { + QString str; + int x1, y1, x2, y2; + bool hasSelection; + + hasSelection = doc->hasSelection(); + + if (hasSelection) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + if (x2) y2++; + } + else + { + y1 = y; + y2 = y + 1; + x1 = x; + } + + if (y1 > 0) + { + str = doc->lines.at(y1 - 1)->s.getString() + '\n'; + + doc->begin(); + doc->remove(y1 - 1, 0, y1, 0); + doc->insert(y2 - 1, 0, str); + if (hasSelection) + { + cursorGoto(y1 - 1, 0, false); + doc->startSelection(this, y1 - 1, 0); + doc->endSelection(y2 - 1, 0); + } + doc->end(); + } + } + } + else if (ctrl) + { + int yl = doc->getPreviousLimit(y); + if (yl >= 0) + cursorGoto(yl, xx, shift); + } + else + cursorGoto(viewToReal(realToView(y) - 1), xx, shift); +} + + +void GEditor::cursorDown(bool shift, bool ctrl, bool alt) +{ + if (alt) + { + if (ctrl) + moveNextSameIndent(shift); + else + { + QString str; + int x1, y1, x2, y2; + bool hasSelection; + + hasSelection = doc->hasSelection(); + + if (hasSelection) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + if (x2) y2++; + } + else + { + y1 = y; + y2 = y + 1; + x1 = x; + } + + if (y2 < (numLines() - 1)) + { + str = doc->lines.at(y2)->s.getString() + '\n'; + + doc->begin(); + doc->remove(y2, 0, y2 + 1, 0); + doc->insert(y1, 0, str); + if (hasSelection) + { + cursorGoto(y2 + 1, 0, false); + doc->startSelection(this, y1 + 1, 0); + doc->endSelection(y2 + 1, 0); + } + doc->end(); + } + } + } + else if (!ctrl) + cursorGoto(QMIN(numLines() - 1, viewToReal(realToView(y) + 1)), xx, shift); + else + { + int yl = doc->getNextLimit(y); + + if (yl >= 0) + cursorGoto(yl, xx, shift); + else + cursorGoto(numLines(), 0, shift); + } +} + +void GEditor::movePreviousSameIndent(bool shift) +{ + int indent = doc->getIndent(y); + int i2, y2; + + for(y2 = y - 1; y2 >= 0; y2--) + { + i2 = doc->getIndent(y2); + if (i2 == indent && i2 < doc->lineLength(y2)) + { + cursorGoto(y2, x, shift); + break; + } + } +} + +void GEditor::cursorHome(bool shift, bool ctrl, bool alt) +{ + if (ctrl) + cursorGoto(0, 0, shift); + else + { + int indent = doc->getIndent(y); + if (x == indent) + cursorGoto(y, 0, shift); + else + cursorGoto(y, indent, shift); + } +} + +void GEditor::moveNextSameIndent(bool shift) +{ + int indent = doc->getIndent(y); + int i2, y2; + + for(y2 = y + 1; y2 < numLines(); y2++) + { + i2 = doc->getIndent(y2); + if (i2 == indent && i2 < doc->lineLength(y2)) + { + cursorGoto(y2, x, shift); + break; + } + } +} + +void GEditor::cursorEnd(bool shift, bool ctrl, bool alt) +{ + if (ctrl) + cursorGoto(numLines(), 0, shift); + else + cursorGoto(y, lineLength(y), shift); +} + +void GEditor::cursorPageUp(bool shift, bool alt) +{ + int page = visibleHeight() / _cellh; + cursorGoto(viewToReal(realToView(y) - page), 0, shift); +} + +void GEditor::cursorPageDown(bool shift, bool alt) +{ + int page = visibleHeight() / _cellh; + cursorGoto(viewToReal(realToView(y) + page), 0, shift); +} + +void GEditor::newLine() +{ + doc->begin(); + doc->eraseSelection(_insertMode); + doc->insert(y, x, '\n' + doc->lines.at(y)->s.left(QMIN(x, doc->getIndent(y)))); + cursorGoto(doc->yAfter, doc->xAfter, false); + doc->end(); +} + +void GEditor::del(bool ctrl) +{ + if (doc->hasSelection()) + { + doc->eraseSelection(_insertMode); + return; + } + + doc->begin(); + + if (x >= lineLength(y)) + { + if (y < (numLines() - 1)) + { + if (_insertMode) + doc->insert(y, x, QString(), TRUE); + doc->remove(y, x, y + 1, 0); + } + } + else + { + if (ctrl && x < lineLength(y)) + { + int nx = doc->wordRight(y, x); + doc->remove(y, x, y, nx); + } + else + doc->remove(y, x, y, x + 1); + } + + doc->end(); +} + +void GEditor::backspace(bool ctrl) +{ + int indent; + int yy; + bool empty; + + if (doc->hasSelection()) + { + doc->eraseSelection(_insertMode); + return; + } + + doc->begin(); + + indent = doc->getIndent(y); + + if (x > 0 && x <= indent) + { + yy = y; + indent = 0; + while (yy > 0) + { + yy--; + indent = doc->getIndent(yy, &empty); + if (!empty && x > indent) + break; + } + cursorGoto(y, indent, true); + del(false); + } + else + { + if (ctrl && x > 0) + { + int nx = doc->wordLeft(y, x); + doc->remove(y, nx, y, x); + } + else + { + if (cursorGoto(y, x - 1, false)) + del(false); + } + } + + doc->end(); +} + +void GEditor::selectCurrentLine() +{ + cursorGoto(y, 0, FALSE); + cursorGoto(y + 1, 0, TRUE); +} + +void GEditor::deleteCurrentLine() +{ + bool im; + + if (doc->hasSelection()) + { + doc->eraseSelection(_insertMode); + return; + } + + im = _insertMode; + _insertMode = FALSE; + doc->begin(); + selectCurrentLine(); + del(FALSE); + doc->end(); + _insertMode = im; +} + +void GEditor::clearLine(bool before, bool after) +{ + int ox = x; + + doc->begin(); + + if (before) + { + QString ins; + + doc->remove(y, 0, y, x + 1); + ins.fill(' ', ox + 1); + doc->insert(y, 0, ins); + x = ox; + } + + if (after) + { + doc->remove(y, x, y, doc->lineLength(y) -1); + } + + doc->end(); +} + +void GEditor::clearAfter(int n) +{ + int ox = x; + QString ins; + + doc->begin(); + doc->remove(y, x, y, x + n); + ins.fill(' ', n); + doc->insert(y, ox, ins); + x = ox; + doc->end(); +} + +void GEditor::clearDocument(bool before, bool after) +{ + int i; + int ox = x; + + doc->begin(); + + if (before && after) + { + for (i = 0; i < doc->numLines(); i++) + doc->remove(i, 0, i, doc->lineLength(i)); + } + else if (before) + { + QString ins; + + for (i = 0; i < y; i++) + doc->remove(i, 0, i, doc->lineLength(i)); + + doc->remove(y, 0, y, x); + ins.fill(' ', ox); + doc->insert(y, 0, ins); + x = ox; + } + else if (after) + { + doc->remove(y, x, y, doc->lineLength(y) -1); + + for (i = y + 1; i < doc->numLines(); i++) + doc->remove(i, 0, i, doc->lineLength(i)); + } + + x = ox; + doc->end(); +} + +void GEditor::tab(bool back) +{ + QString ins; + int y1, x1, y2, x2, yy; + int indent, i; + bool empty; + int tw = doc->getTabWidth(); + + if (!doc->hasSelection()) + { + if (!back) + { + ins.fill(' ', tw - (x % tw)); + insert(ins); + return; + } + + doc->startSelection(this, y, 0); + doc->endSelection(y + 1, 0); + } + + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + doc->startSelection(this, y1, 0); + if (x2) + y2++; + doc->endSelection(y2, 0); + + indent = 65536; + for (yy = y1; yy < y2; yy++) + { + i = doc->getIndent(yy, &empty); + if (!empty) + indent = QMIN(indent, i); + } + + if (back && indent <= 0) + return; + + doc->begin(); + + if (!back) + { + ins.fill(' ', tw - (indent % tw)); + + for (yy = y1; yy < y2; yy++) + { + doc->insert(yy, 0, ins); + doc->colorize(yy); + } + } + else + { + indent %= tw; + if (indent == 0) + indent = tw; + + ins.fill(' ', indent); + + for (yy = y1; yy < y2; yy++) + { + if (doc->lines.at(yy)->s.left(indent) == ins) + { + doc->remove(yy, 0, yy, indent); + doc->colorize(yy); + } + } + } + + doc->startSelection(this, y1, 0); + doc->endSelection(y2, 0); + + doc->end(); +} + + +void GEditor::copy(bool mouse) +{ + if (!doc->hasSelection()) + return; + + QString text = doc->getSelectedText(_insertMode).getString(); + QApplication::clipboard()->setText(text, mouse ? QClipboard::Selection : QClipboard::Clipboard); +} + +void GEditor::cut() +{ + if (!doc->hasSelection()) + { + doc->begin(); + selectCurrentLine(); + _cutBuffer += doc->getSelectedText(_insertMode); + QApplication::clipboard()->setText(_cutBuffer.getString(), QClipboard::Clipboard); + doc->eraseSelection(_insertMode); + doc->end(); + return; + } + + copy(false); + doc->eraseSelection(_insertMode); +} + +void GEditor::paste(bool mouse) +{ + QString text; + GString gtext; + QString subType("plain"); + int i, i2, xs, len; + QString tab; + + text = QApplication::clipboard()->text(subType, mouse ? QClipboard::Selection : QClipboard::Clipboard); + + if (text.length() == 0) + return; + + tab.fill(' ', doc->getTabWidth()); + text.replace("\t", tab); + + for (i = 0; i < text.length(); i++) + { + if ((text[i] < ' ' || text[i].isSpace()) && text[i] != '\n' && text[i] != '\r') + text[i] = ' '; + } + + if (_insertMode) + { + gtext = text; + doc->begin(); + i = 0; + while (i < (int)text.length()) + { + i2 = gtext.findNextLine(i, len); + xs = x; + insert(text.mid(i, len)); + x = xs; + y++; + if (y >= doc->numLines()) + insert("\n"); + i = i2; + } + doc->end(); + } + else + insert(text); +} + +void GEditor::undo() +{ + doc->undo(); + //cursorGoto(doc->yAfter, doc->xAfter, false); +} + +void GEditor::redo() +{ + doc->redo(); + //cursorGoto(doc->yAfter, doc->xAfter, false); +} + +void GEditor::selectAll() +{ + cursorGoto(0, 0, false); + cursorGoto(numLines(), 0, true); +} + +void GEditor::keyPressEvent(QKeyEvent *e) +{ + bool shift = e->state() & Qt::ShiftButton; + bool ctrl = e->state() & Qt::ControlButton; + bool alt = e->state() & Qt::AltButton; + + e->accept(); + + if (doc->isReadOnly()) + { + switch (e->key()) + { + case Qt::Key_Left: + cursorLeft(shift, ctrl); return; + case Qt::Key_Right: + cursorRight(shift, ctrl); return; + case Qt::Key_Up: + cursorUp(shift, ctrl, false); return; + case Qt::Key_Down: + cursorDown(shift, ctrl, false); return; + case Qt::Key_Home: + cursorHome(shift, ctrl, alt); return; + case Qt::Key_End: + cursorEnd(shift, ctrl, alt); return; + case Qt::Key_Prior: + cursorPageUp(shift, alt); return; + case Qt::Key_Next: + cursorPageDown(shift, alt); return; + case Qt::Key_Return: + if (ctrl) + expand(shift); + return; + } + + if (!ctrl) + goto __IGNORE; + + switch(e->key()) + { + case Qt::Key_C: + copy(); return; + case Qt::Key_A: + selectAll(); return; + } + } + else + { + QString t = e->text(); + if (t.length() && (t.at(0).isPrint() || (t.at(0).unicode() == 9 && ctrl)) + && e->key() != Qt::Key_Return + && e->key() != Qt::Key_Enter + && e->key() != Qt::Key_Delete + && e->key() != Qt::Key_Backspace) + { + if (_insertMode) + { + doc->begin(); + del(false); + insert(t); + doc->end(); + } + else + insert(t); + return; + } + + switch (e->key()) + { + case Qt::Key_Left: + cursorLeft(shift, ctrl); return; + case Qt::Key_Right: + cursorRight(shift, ctrl); return; + case Qt::Key_Up: + cursorUp(shift, ctrl, alt); return; + case Qt::Key_Down: + cursorDown(shift, ctrl, alt); return; + case Qt::Key_Enter: + newLine(); return; + case Qt::Key_Return: + if (ctrl) + expand(shift); + else + newLine(); + return; + case Qt::Key_Home: + cursorHome(shift, ctrl, alt); return; + case Qt::Key_End: + cursorEnd(shift, ctrl, alt); return; + case Qt::Key_Backspace: + backspace(ctrl); return; + case Qt::Key_Delete: + del(ctrl); return; + case Qt::Key_Prior: + cursorPageUp(shift, alt); return; + case Qt::Key_Next: + cursorPageDown(shift, alt); return; + case Qt::Key_Tab: + tab(false); return; + case Qt::Key_BackTab: + tab(true); return; + case Qt::Key_Insert: + setInsertMode(!_insertMode); + } + + if (!ctrl) + goto __IGNORE; + + switch(e->key()) + { + case Qt::Key_C: + copy(); return; + case Qt::Key_X: + cut(); return; + case Qt::Key_V: + paste(); return; + case Qt::Key_Z: + undo(); return; + case Qt::Key_Y: + redo(); return; + case Qt::Key_A: + selectAll(); return; + case Qt::Key_D: + deleteCurrentLine(); return; + } + } + +__IGNORE: + + e->ignore(); +} + + +int GEditor::posToColumn(int y, int px) +{ + int i = -1, lw; + int len = doc->lineLength(y); + QString s = doc->lines.at(y)->s.getString(); + int d, f; + + if (px < margin || px >= visibleWidth()) + _posOutside = true; + + if (len == 0) + return (px - margin) / _charWidth[' ']; + + px += contentsX(); + + d = 0; + f = len; + while (f > d) + { + if (i < 0) + i = px / _charWidth['m']; + else + i = (d + f) / 2; + + lw = lineWidth(y, i); + if (px < lw) + { + f = i; + continue; + } + + /*if (_sameWidth) + lw += _sameWidth; + else*/ + lw = lineWidth(y, i + 1); //+= getStringWidth(s.mid(i, 1)); //fm.width(s[i]); + if (px >= lw) + { + d = i + 1; + continue; + } + + d = i; + break; + } + + _posOutside = d > len; + return d; +} + + +int GEditor::posToLine(int py) +{ + int ny; + + ny = rowAt(contentsY() + py); + _posOutside = true; + if (ny < 0) + ny = 0; + else if (ny >= visibleLines()) + ny = visibleLines() - 1; + else + _posOutside = false; + + return viewToReal(ny); +} + +bool GEditor::posToCursor(int px, int py, int *y, int *x) +{ + int nx, ny; + bool outside; + + ny = posToLine(py); + outside = _posOutside; + nx = posToColumn(ny, px); + if (_insertMode) + nx = QMAX(0, nx); //QMIN(nx, lineLength(ny))); + else + nx = QMAX(0, QMIN(nx, lineLength(ny))); + + *y = ny; + *x = nx; + + return outside || _posOutside; +} + +void GEditor::cursorToPos(int y, int x, int *px, int *py) +{ + int npx, npy; + + y = realToView(y); + + npy = y * _cellh - contentsY(); + //npx = x * charWidth - contentsX() + margin; + npx = lineWidth(y, x) - contentsX(); + + *py = npy; + *px = npx; +} + +bool GEditor::updateCursor() +{ + if (contentsX() + lastx >= margin) + { + viewport()->setCursor(_saveCursor); + return false; + } + else + { + viewport()->setCursor(Qt::ArrowCursor); + return true; + } +} + +void GEditor::mousePressEvent(QMouseEvent *e) +{ + bool shift = e->state() & Qt::ShiftButton; + int nx, ny; + //stopAutoScroll(); + //d->dnd_startpos = e->pos(); + + if (e->button() != Qt::LeftButton && e->button() != Qt::MidButton) + return; + + posToCursor(e->pos().x(), e->pos().y(), &ny, &nx); + + lastx = e->pos().x(); + left = updateCursor(); + + if (!left) + cursorGoto(ny, nx, shift); +} + +void GEditor::mouseMoveEvent(QMouseEvent *e) +{ + if ((e->buttons() & Qt::MouseButtonMask) == Qt::LeftButton) + { + if (left) + { + //ly = posToLine(e->pos().y()); + if (!scrollTimer->isActive()) + cursorGoto(posToLine(e->pos().y()), 0, false); + } + + if (!scrollTimer->isActive()) + { + stopBlink(); + scrollTimer->start(25, false); + } + } + + lastx = e->pos().x(); + left = updateCursor(); +} + +void GEditor::mouseDoubleClickEvent(QMouseEvent *e) +{ + int ny; + + _dblclick = true; + + if (left) + { + ny = posToLine(e->pos().y()); + if (!getFlag(NoFolding) && doc->lines.at(ny)->proc) + { + if (isFolded(ny)) + foldAll(); + else + unfoldAll(); + } + emit marginDoubleClicked(ny); + return; + } + + if (e->button() == Qt::LeftButton && !(e->state() & Qt::ShiftButton)) + { + int xl, xr; + + xl = doc->wordLeft(y, x, true); + xr = doc->wordRight(y, x, true); + + if (xr > xl) + { + doc->hideSelection(); + cursorGoto(y, xl, false); + cursorGoto(y, xr, true); + copy(true); + } + } +} + +void GEditor::mouseReleaseEvent(QMouseEvent *e) +{ + if (scrollTimer->isActive()) + { + scrollTimer->stop(); + startBlink(); + copy(true); + } + else + { + if (left && !_dblclick) + { + int ny = posToLine(e->pos().y()); + if (!getFlag(NoFolding) && doc->lines.at(ny)->proc) + { + if (isFolded(ny)) + unfoldLine(ny); + else + foldLine(ny); + } + emit marginClicked(ny); + } + + if (e->button() == Qt::MidButton) + paste(true); + } + + _dblclick = false; +} + + +void GEditor::resizeEvent(QResizeEvent *e) +{ + Q3ScrollView::resizeEvent(e); + //baptizeVisible(); + updateWidth(); +} + +void GEditor::viewportResizeEvent(QResizeEvent *e) +{ + Q3ScrollView::viewportResizeEvent(e); + updateWidth(); + if (!_ensureCursorVisibleLater) + { + _ensureCursorVisibleLater = true; + QTimer::singleShot(0, this, SLOT(ensureCursorVisible())); + } + _checkCache = true; +} + +bool GEditor::isCursorVisible() +{ + int px, py; + + cursorToPos(y, x, &px, &py); + + return !(px < margin || px > (visibleWidth() - QMAX(2, margin)) || py < 0 || py > (visibleHeight() - _cellh)); +} + +void GEditor::ensureCursorVisible() +{ + int xx, yy; + + if (!isUpdatesEnabled() || !isVisible()) + return; + + xx = lineWidth(y, x); // + _charWidth['m'] / 2 + yy = realToView(y) * _cellh + _cellh / 2; + + //fprintf(stderr, "%p: xx = %d yy = %d vw = %d vh = %d center = %d contentX() = %d", this, xx, yy, visibleWidth(), visibleHeight(), center, contentsX()); + + if (xx >= visibleWidth() || contentsX() > 0) + ensureVisible(xx, yy, margin + 2, center ? (visibleHeight() / 2) : _cellh); + else + ensureVisible(0, yy, margin + 2, center ? (visibleHeight() / 2) : _cellh); + + //fprintf(stderr, " -> %d\n", contentsX()); + + center = false; + _ensureCursorVisibleLater = false; +} + +void GEditor::startBlink() +{ + blinkTimer->start(QApplication::cursorFlashTime() / 2, false); + _cursor = true; + updateLine(y); +} + +void GEditor::stopBlink() +{ + blinkTimer->stop(); + _cursor = false; + updateLine(y); +} + +void GEditor::blinkTimerTimeout() +{ + if (!doc->isReadOnly()) + { + _cursor = !_cursor; + updateLine(y); + } +} + +void GEditor::focusInEvent(QFocusEvent *e) +{ + startBlink(); + //ensureCursorVisible(); + Q3ScrollView::focusInEvent(e); + doc->setCurrentView(this); +} + +void GEditor::focusOutEvent(QFocusEvent *e) +{ + stopBlink(); + //leaveCurrentLine(); + Q3ScrollView::focusOutEvent(e); +} + + +void GEditor::scrollTimerTimeout() +{ + int ny, nx; + QPoint pos = mapFromGlobal(QCursor::pos()); + + posToCursor(pos.x(), pos.y(), &ny, &nx); + cursorGoto(ny, nx, true); +} + +void GEditor::setStyle(int index, GHighlightStyle *style) +{ + int sat; + int gray; + + if (index < 0 || index >= GLine::NUM_STATE) + return; + + styles[index] = *style; + + if (index == GLine::Background) + { + viewport()->setPaletteBackgroundColor(style->color); + redrawContents(); + } + else + updateContents(); + + if (index == GLine::Background) + { + _oddBackground = style->color; + + sat = _oddBackground.saturation(); + gray = 128 + (_oddBackground.value() - 128) * 0.8; + + _altBackground = QColor(gray, gray, gray); + + if (_oddBackground.value() > 127) + _oddBackground.setHsv(_oddBackground.hue(), sat, _oddBackground.value() - 16); + else + _oddBackground.setHsv(_oddBackground.hue(), sat, _oddBackground.value() + 16); + } +} + +void GEditor::setNumRows(int n) +{ + _nrows = realToView(n - 1) + 1; + //Q3GridView::setNumRows(n); + updateViewport(); + updateContents(); + //Q3ScrollView::updateScrollBars(); + //if (contentsHeight() < visibleHeight()) + // repaintContents(contentsX(), contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight() + contentsX(), true); + //if (contentsHeight() < visibleHeight()) + //repaintContents(true); +} + +void GEditor::getStyle(int index, GHighlightStyle *style) const +{ + if (index < 0 || index >= GLine::NUM_STATE) + index = GLine::Background; + + *style = styles[index]; +} + +void GEditor::setFlag(int f, bool v) +{ + if (v) + flags |= (1 << f); + else + flags &= ~(1 << f); + + if (getFlag(NoFolding)) + unfoldAll(); + + updateMargin(); + updateContents(); +} + +void GEditor::updateMargin() +{ + int charWidth = _charWidth['m']; + int nm = 1, lnl = 0; + + if (!getFlag(HideMargin)) + { + nm = 2; + + //if (doc->getHighlightMode() != GDocument::None) + { + int bw = 8; + + if (_breakpoint && !_breakpoint->isNull()) + bw = qMax(bw, _breakpoint->width() + 2); + + if (_bookmark && !_bookmark->isNull()) + bw = qMax(bw, _bookmark->width() + 2); + + nm += bw; + } + //else if (getFlag(ShowLineNumbers)) + // nm += 4; + + if (getFlag(ShowLineNumbers)) + { + int cnt = numLines() + _firstLineNumber; + + while (cnt) + { + nm += charWidth; + lnl++; + cnt /= 10; + } + + nm += 4; + } + + if (getFlag(ShowModifiedLines) && nm < 6) + nm = 6; + } + + if (nm != margin) + { + margin = nm; + lineNumberLength = lnl; + updateContents(); + updateCursor(); + } +} + +void GEditor::docTextChangedLater() +{ + emit textChanged(); +} + +void GEditor::docTextChanged() +{ + if (painting) + QTimer::singleShot(0, this, SLOT(docTextChangedLater())); + else + docTextChangedLater(); +} + +void GEditor::inputMethodEvent(QInputMethodEvent *e) +{ + //qDebug("inputMethodEvent: %s\n", (const char *)e->commitString().toUtf8()); + + if (doc->isReadOnly()) + { + e->ignore(); + return; + } + + insert(e->commitString()); + + #if 0 + int priorState = d->undoState; + d->removeSelectedText(); + + int c = d->cursor; // cursor position after insertion of commit string + if (e->replacementStart() <= 0) + c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength()); + + d->cursor += e->replacementStart(); + + // insert commit string + if (e->replacementLength()) { + d->selstart = d->cursor; + d->selend = d->selstart + e->replacementLength(); + d->removeSelectedText(); + } + if (!e->commitString().isEmpty()) + d->insert(e->commitString()); + + d->cursor = c; + + d->textLayout.setPreeditArea(d->cursor, e->preeditString()); + d->preeditCursor = e->preeditString().length(); + d->hideCursor = false; + QList formats; + for (int i = 0; i < e->attributes().size(); ++i) { + const QInputMethodEvent::Attribute &a = e->attributes().at(i); + if (a.type == QInputMethodEvent::Cursor) { + d->preeditCursor = a.start; + d->hideCursor = !a.length; + } else if (a.type == QInputMethodEvent::TextFormat) { + QTextCharFormat f = qvariant_cast(a.value).toCharFormat(); + if (f.isValid()) { + QTextLayout::FormatRange o; + o.start = a.start + d->cursor; + o.length = a.length; + o.format = f; + formats.append(o); + } + } + } + d->textLayout.setAdditionalFormats(formats); + d->updateTextLayout(); + update(); + if (!e->commitString().isEmpty()) + d->emitCursorPositionChanged(); + d->finishChange(priorState); +#ifndef QT_NO_COMPLETER + if (!e->commitString().isEmpty()) + d->complete(Qt::Key_unknown); +#endif + #endif +} + +QVariant GEditor::inputMethodQuery(Qt::InputMethodQuery property) const +{ + //qDebug("inputMethodQuery: %d\n", (int)property); + GEditor *that = (GEditor *)this; + + switch(property) + { + case Qt::ImMicroFocus: + { + int cx, cy, px, py; + + getCursor(&cy, &cx); + that->cursorToPos(cy, cx, &px, &py); + return QRect(px, py, 1, _cellh); + } + case Qt::ImFont: + return font(); + case Qt::ImCursorPosition: + return QVariant(x); + case Qt::ImSurroundingText: + return QVariant(doc->getLine(y).getString()); + case Qt::ImCurrentSelection: + return QVariant(QString()); + case Qt::ImAnchorPosition: + return QVariant(x); + default: + return QVariant(); + } +} + + +bool GEditor::focusNextPrevChild(bool) +{ + return false; +} + +static bool is_power_of_ten(int n) +{ + for(;;) + { + if (n % 10) + return false; + n /= 10; + if (n == 1) + return true; + } +} + +void GEditor::lineInserted(int y) +{ + if (largestLine >= y) + largestLine++; + + if (getFlag(ShowLineNumbers) && is_power_of_ten(numLines())) + updateMargin(); +} + +void GEditor::lineRemoved(int y) +{ + if (largestLine == y) + updateWidth(y); + else if (largestLine > y) + largestLine--; + + if (getFlag(ShowLineNumbers) && is_power_of_ten(numLines() + 1)) + updateMargin(); +} + +void GEditor::flash() +{ + if (flashed) + return; + + flashed = true; + setPaletteBackgroundColor(QColor(QRgb(styles[GLine::Background].color.rgb() ^ 0xFFFFFF))); + redrawContents(); + QTimer::singleShot(50, this, SLOT(unflash())); +} + +void GEditor::unflash() +{ + flashed = false; + setPaletteBackgroundColor(styles[GLine::Background].color); + redrawContents(); +} + +#if 0 +static void dump_fold(GArray &fold) +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + fprintf(stderr, "[%d %d] ", fp->start, fp->end); + } + + fprintf(stderr, "\n"); +} +#endif + +int GEditor::checkCursor(int y) +{ + uint i; + GFoldedProc *fp; + int ny; + + ny = y; + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (y > fp->start && y <= fp->end) + { + ny = fp->start; + break; + } + } + + return ny; +} + +void GEditor::foldLine(int row, bool no_refresh) +{ + uint i; + int pos; + int start, end; + GFoldedProc *fp; + int ny; + + if (getFlag(NoFolding)) + return; + + if (!doc->hasLimit(row)) + row = doc->getPreviousLimit(row); + + if (row < 0 || row >= numLines()) + return; + + //fprintf(stderr, "foldLine %d\n", row); + + start = row; + end = doc->getNextLimit(row); + if (end < 0) + end = numLines() - 1; + else + end--; + /*else + { + while (end > start) + { + end--; + l = doc->lines.at(end); + //qDebug("[%d] state = %d %d", end, l->highlight ? l->highlight[0].state : -1, l->highlight ? l->highlight[0].len : -1); + if (!l->highlight || (l->highlight[0].state != GLine::Comment && l->highlight[0].state != GLine::Help)) + break; + } + }*/ + + pos = -1; + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (end >= fp->start && start <= fp->end) + return; + if (pos < 0 && start < fp->start) + pos = i; + } + + fp = new GFoldedProc; + fp->start = start; + fp->end = end; + + if (pos < 0) + fold.append(fp); + else + fold.insert(pos, fp); + + //dump_fold(fold); + + ny = checkCursor(y); + if (ny != y) + cursorGoto(ny, x, false); + + if (!no_refresh) + { + setNumRows(numLines()); + //redrawContents(); + } +} + +void GEditor::unfoldLine(int row) +{ + uint i; + GFoldedProc *fp; + + //fprintf(stderr, "unfoldLine %d\n", row); + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row >= fp->start && row <= fp->end) + { + fold.remove(i); + //dump_fold(fold); + setNumRows(numLines()); + //redrawContents(); + return; + } + } + +} + +int GEditor::viewToReal(int row) const +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row <= fp->start) + break; + if (fp->end < (numLines() - 1)) + row += fp->end - fp->start; + else + row = numLines(); + } + + return row; +} + +int GEditor::realToView(int row) const +{ + uint i; + GFoldedProc *fp; + int y; + + //fprintf(stderr, "realToView: %d -> ", row); + + y = row; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row < fp->start) + continue; + if (row <= fp->end) + y -= row - fp->start; + else + y -= fp->end - fp->start; + } + + //fprintf(stderr, "%d\n", y); + + return y; +} + +int GEditor::visibleLines() const +{ + int n; + uint i; + GFoldedProc *fp; + + n = numLines(); + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + n -= fp->end - fp->start; + } + + return n; +} + +bool GEditor::isFolded(int row) +{ + uint i; + GFoldedProc *fp; + int d, f; + + d = 0; + f = fold.count(); + + while (f > d) + { + i = (f + d) / 2; + fp = fold.at(i); + if (fp->start == row) + return true; + else if (fp->start < row) + d = i + 1; + else + f = i; + } + + return false; +} + +/*bool GEditor::insideFolded(int row) +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row > fp->start && row <= fp->end) + return true; + } + + return false; +}*/ + +int GEditor::checkFolded(int row) +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row <= fp->end) + { + if (row > fp->start) + return fp->start; + else + break; + } + } + + return row; +} + +void GEditor::foldAll() +{ + int row; + + if (getFlag(NoFolding)) + return; + + row = 0; + for(;;) + { + foldLine(row, true); + row = doc->getNextLimit(row); + if (row < 0) + break; + } + + setNumRows(numLines()); + //updateContents(); +} + +void GEditor::unfoldAll() +{ + foldClear(); + setNumRows(numLines()); + ensureCursorVisible(); + //redrawContents(); +} + + +void GEditor::foldRemove(int y1, int y2) +{ + uint i; + GFoldedProc *fp; + int n; + + if (getFlag(NoFolding)) + return; + + if (y2 < 0) + { + unfoldLine(y1); + return; + } + + n = y2 - y1 + 1; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (y2 < fp->start) + { + fp->start -= n; + fp->end -= n; + } + else if (y1 > fp->end) + continue; + else + { + fold.remove(i); + i--; + } + } +} + + +void GEditor::foldInsert(int y, int n) +{ + uint i; + GFoldedProc *fp; + + if (getFlag(NoFolding)) + return; + + if (n == 0) + { + unfoldLine(y); + return; + } + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (fp->start > y) + { + fp->start += n; + fp->end += n; + } + else if (fp->end >= y) + { + fp->end += n; + fold.remove(i); //unfoldLine(fp->start); + i--; + } + } +} + +void GEditor::showString(GString s, bool ignoreCase) +{ + _showString = s; + _showStringIgnoreCase = ignoreCase; + updateContents(); +} + +void GEditor::showWord(int y, int x, int len) +{ + _showRow = y; + _showCol = x; + _showLen = len; + updateLine(y); +} + +void GEditor::updateLine(int y) +{ + QRect r(0, realToView(y) * _cellh, _cellw, _cellh); + updateContents(r); +} + +#if 0 +void GEditor::paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch) +{ + if ((_nrows * _cellh) >= contentsHeight()) + return; + + contentsToViewport(cx, cy, cx, cy); + QRegion reg(QRect(cx, cy, cw, ch)); + QSize size(_cellw, _nrows * _cellh); + reg = reg.subtracted(QRect(contentsToViewport(QPoint(0, 0)), size)); + + // And draw the rectangles (transformed as needed) + QVector r = reg.rects(); + for (int i = 0; i < (int)r.count(); ++i) + p->fillRect(r[i], viewport()->paletteBackgroundColor()); +} + +void GEditor::viewportPaintEvent(QPaintEvent *e) +{ + QString info; + QRect rect; + QColor color; + + Q3ScrollView::viewportPaintEvent(e); + + if (getFlag(ShowCursorPosition)) + { + getInfo(&rect, &info); + if (rect.intersects(e->rect())) + { + QPainter p(viewport()); + color = styles[GLine::Current].color; + color.setAlpha(128); + p.fillRect(rect, color); + //rect.translate(0, 2); + p.setPen(styles[GLine::Normal].color); + p.drawText(rect, Qt::AlignCenter, info); + /*color = styles[GLine::Normal].color; + color.setAlpha(128); + p.setPen(color); + //rect.translate(0, -2); + p.drawRect(rect);*/ + } + } +} +#endif + +void GEditor::updateViewport() +{ + int vw, vh; + + /*vw = contentsRect().width(); + vh = contentsRect().height(); + + if (doc) + { + vw = QMAX(_cellw, vw); + vh = QMAX(_cellh * _nrows, vh); + //qDebug("updateViewport: h = %d vh = %d", _cellh * numLines(), contentsRect().height()); + }*/ + + vw = QMAX(visibleWidth(), _cellw); + vh = QMAX(visibleHeight(), _cellh * _nrows); + + if (vw != contentsWidth() || vh != contentsHeight()) + Q3ScrollView::resizeContents(vw, vh); + + //updateCache(); + _checkCache = true; +} + +void GEditor::resizeContents(int w, int h) +{ + updateViewport(); +} + +void GEditor::setInsertMode(bool mode) +{ + int x1, y1, x2, y2, i; + + if (mode !=_insertMode) + { + _insertMode = mode; + if (!_insertMode) + x = QMIN(x, lineLength(y)); + + if (doc->hasSelection()) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + x = x2; y = y2; + + for (i = y1; i <= y2; i++) + updateLine(i); + } + else + updateLine(y); + } +} + +void GEditor::updateViewportAttributes() +{ + bool b; + + //if (!::strcmp(style()->metaObject()->className(), "Oxygen::Style")) + b = !hasBorder(); + //else + // b = true; + + viewport()->setAttribute(Qt::WA_NoSystemBackground, b); + viewport()->setAttribute(Qt::WA_PaintOnScreen, b); +} + +void GEditor::setBorder(bool b) +{ + if (_border == b) + return; + + style()->unpolish(this); + setFrameStyle(b ? StyledPanel + Sunken : NoFrame); + style()->polish(this); + updateViewportAttributes(); +} + +void GEditor::setLineOffset(int l) +{ + _firstLineNumber = l; + update(); +} + +void GEditor::expand(bool shift) +{ + bool e; + + e = isFolded(y); + + if (shift) + { + if (e) + unfoldAll(); + else + foldAll(); + } + else + { + if (e) + unfoldLine(y); + else + foldLine(y); + } +} + +void GEditor::saveMouseCursor() +{ + _saveCursor = viewport()->cursor(); +} + +void GEditor::saveCursor() +{ + _save_x = x; + _save_y = y; +} + +void GEditor::restoreCursor() +{ + cursorGoto(_save_x, _save_y, false); +} + +bool GEditor::cursorRelGoto(int dy, int dx, bool mark) +{ + return cursorGoto(QMAX(0, y + dy), QMAX(0, x + dx), mark); +} diff --git a/gb.qt4/src/ext/gview.h b/gb.qt4/src/ext/gview.h new file mode 100644 index 00000000..cd9b91fd --- /dev/null +++ b/gb.qt4/src/ext/gview.h @@ -0,0 +1,317 @@ +/*************************************************************************** + + gview.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GVIEW_H +#define __GVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gdocument.h" +#include "../gb.qt.h" + +struct GHighlightStyle +{ + QColor color; + QColor backgroundColor; + bool bold; + bool italic; + bool underline; + bool background; +}; + +struct GFoldedProc +{ + int start; + int end; +}; + +class GEditor; + +class GEditor : public Q3ScrollView +{ + Q_OBJECT + + friend class GDocument; + +private: + + static QPixmap *_cache; + static QPixmap *_breakpoint; + static QPixmap *_bookmark; + static QStyle *_style; + static int count; + + GDocument *doc; + QFontMetrics fm; + int _ytext; + int largestLine; + int x, y, xx; + int nx, ny; + bool _cursor; + QTimer *blinkTimer; + QTimer *scrollTimer; + int x1m, x2m, y1m, y2m; + int margin; + int lineNumberLength; + bool center; + bool flashed; + bool painting; + GString _showString; + bool _showStringIgnoreCase; + int _showRow, _showCol, _showLen; + bool _posOutside; + int _cellw, _cellh; + int _nrows; + bool _insertMode; + double *_charWidth; + double _sameWidth; + int _tabWidth; + bool _oddLine; + QColor _altBackground; + QColor _oddBackground; + bool _checkCache; + bool _border; + bool _ensureCursorVisibleLater; + int _firstLineNumber; + QCursor _saveCursor; + GString _cutBuffer; + int _save_x, _save_y; + + int lastx; + bool left; + bool _dblclick; + GArray fold; + + QFont normalFont; + QFont italicFont; + GHighlightStyle styles[GLine::NUM_STATE]; + int flags; + + int lineLength(int y) const { return doc->lineLength(y); } + int numLines() const { return doc->numLines(); } + int visibleLines() const; + void startBlink(); + void stopBlink(); + bool updateCursor(); + + int lineWidth(int y) const; + int lineWidth(int y, int len); + void updateWidth(int y = -1); + void updateMargin(); + void updateHeight(); + void updateCache(); + + void lineInserted(int y); + void lineRemoved(int y); + int findLargestLine(); + + void drawTextWithTab(QPainter &p, int sx, int x, int y, const QString &s); + void paintText(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int xs1, int xs2, int row, QColor &); + void paintShowString(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int row); + void paintDottedSpaces(QPainter &p, int row, int ps, int ls); + //void paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch); + + void docTextChanged(); + void redrawContents(); + + int viewToReal(int row) const; + int realToView(int row) const; + int checkCursor(int y); + bool isCursorVisible(); + + void updateViewport(); + void updateFont(); + + int getStringWidth(const QString &s, int len, bool unicode) const; + + void updateViewportAttributes(); + +private slots: + + void blinkTimerTimeout(); + void scrollTimerTimeout(); + //void baptizeVisible(); + //void baptizeVisible(int x, int y); + void unflash(); + void docTextChangedLater(); + void ensureCursorVisible(); + +protected: + + virtual void paintCell(QPainter &, int row, int); + virtual void changeEvent(QEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void resizeEvent(QResizeEvent *e); + virtual void focusInEvent(QFocusEvent *); + virtual void focusOutEvent(QFocusEvent *); + virtual bool focusNextPrevChild(bool); + virtual void inputMethodEvent(QInputMethodEvent *e); + virtual void drawContents(QPainter *p, int cx, int cy, int cw, int ch); + virtual void viewportResizeEvent(QResizeEvent *e); + +public: + + enum Flag + { + ShowProcedureLimits = 1, + DrawWithRelief = 2, + ShowModifiedLines = 3, + ShowCurrentLine = 4, + ShowLineNumbers = 5, + HighlightBraces = 6, + HighlightImmediately = 7, + BlendedProcedureLimits = 8, + ShowDots = 9, + ShowCursorPosition = 10, + ChangeBackgroundAtLimit = 11, + HideMargin = 12, + BlinkCursor = 13, + NoFolding = 14, + AlwaysShowCursor = 15 + }; + + static void setBreakpointPixmap(QPixmap *p); + static void setBookmarkPixmap(QPixmap *p); + + GEditor(QWidget *parent); + ~GEditor(); + void reset(); + + virtual QVariant inputMethodQuery(Qt::InputMethodQuery property) const; + + void setDocument(GDocument *doc); + GDocument *getDocument() const { return doc; } + + void getCursor(int *yc, int *xc) const { *yc = y; *xc = x; } + int getLine() const { return y; } + int getColumn() const { return x; } + void insert(QString text); + bool cursorGoto(int ny, int nx, bool mark); + bool cursorRelGoto(int dy, int dx, bool mark); + void cursorCenter() { center = true; } + void cursorLeft(bool shift, bool ctrl); + void cursorRight(bool shift, bool ctrl); + void cursorUp(bool shift, bool ctrl, bool alt); + void cursorDown(bool shift, bool ctrl, bool alt); + void cursorPageUp(bool shift, bool alt); + void cursorPageDown(bool shift, bool alt); + void cursorHome(bool shift, bool ctrl, bool alt); + void cursorEnd(bool shift, bool ctrl, bool alt); + void newLine(); + void backspace(bool ctrl); + void del(bool ctrl); + void copy(bool mouse); + void copy() { copy(false); } + void cut(); + void paste(bool mouse); + void paste() { paste(false); } + void undo(); + void redo(); + void tab(bool back); + void selectAll(); + void movePreviousSameIndent(bool shift); + void moveNextSameIndent(bool shift); + void expand(bool shift); + void selectCurrentLine(); + void deleteCurrentLine(); + + void clearLine(bool before, bool after); + void clearAfter(int nchar); + void clearDocument(bool before, bool after); + void saveCursor(); + void restoreCursor(); + + bool getInsertMode() const { return _insertMode; } + void setInsertMode(bool mode); + + void setStyle(int index, GHighlightStyle *style); + void getStyle(int index, GHighlightStyle *style) const; + bool getFlag(int f) const { return flags & (1 << f); } + void setFlag(int f, bool v); + bool hasBorder() const { return _border; } + void setBorder(bool b); + void setLineOffset(int l); + + int rowAt(int y) const { return y / _cellh; } + int getLineHeight() const { return _cellh; } + int getCharWidth(unsigned char c) const { return _charWidth[c]; } + void cursorToPos(int y, int x, int *px, int *py); + bool isPosOutside() const { return _posOutside; } + int posToLine(int py); + int posToColumn(int y, int px); + bool posToCursor(int px, int py, int *y, int *x); + int lastVisibleRow(int y) const { return rowAt(y + visibleHeight() - 1); } + int lastVisibleRow() const { return lastVisibleRow(contentsY()); } + void updateLine(int y); + void setNumRows(int); + void leaveCurrentLine(); + int lineOffset() const { return _firstLineNumber; } + + virtual void resizeContents(int w, int h); + + void checkMatching(); + void flash(); + void showString(GString s, bool ignoreCase); + void showWord(int y, int x, int len); + + void foldClear() { fold.clear(); } + void foldLine(int row, bool no_refresh = false); + void foldAll(); + void unfoldAll(); + void unfoldLine(int row); + bool isFolded(int row); + //bool insideFolded(int row); + int checkFolded(int row); + void foldRemove(int y1, int y2 = -1); + void foldInsert(int y, int n); + + bool hasSelection() { return doc->hasSelection(); } + void getSelection(int *y1, int *x1, int *y2, int *x2) { return doc->getSelection(y1, x1, y2, x2, _insertMode); } + GString getSelectedText() { return doc->getSelectedText(_insertMode); } + void hideSelection() { doc->hideSelection(); } + + void saveMouseCursor(); + +signals: + + void cursorMoved(); + void textChanged(); + void marginClicked(int); + void marginDoubleClicked(int); +}; + +#endif diff --git a/gb.qt4/src/ext/main.cpp b/gb.qt4/src/ext/main.cpp new file mode 100644 index 00000000..efd4a201 --- /dev/null +++ b/gb.qt4/src/ext/main.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +/* +#include +#include +#include +*/ + +#include "main.h" + +#include "CLCDNumber.h" +#include "CDial.h" +#include "CEditor.h" +#include "CTextEdit.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; +EVAL_INTERFACE EVAL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CLCDNumberDesc, + + CDialDesc, + + CHighlightDesc, + CEditorLineDesc, + CEditorLinesDesc, + CEditorSelectionDesc, + CEditorStyleDesc, + CEditorStylesDesc, + CEditorFlagsDesc, + CEditorDesc, + + CTextEditSelectionDesc, + CTextEditFormatDesc, + CTextEditDesc, + + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.qt4", QT_INTERFACE_VERSION, &QT); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + +bool MAIN_load_eval_component(void) +{ + if (GB.Component.Load("gb.eval")) + return true; + + GB.GetInterface("gb.eval", EVAL_INTERFACE_VERSION, &EVAL); + return false; +} + diff --git a/gb.qt4/src/ext/main.h b/gb.qt4/src/ext/main.h new file mode 100644 index 00000000..4a2d8676 --- /dev/null +++ b/gb.qt4/src/ext/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.draw.h" +#include "gb.eval.h" +#include "gb.form.properties.h" + +#ifndef __MAIN_C +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +extern "C" EVAL_INTERFACE EVAL; +#endif + +bool MAIN_load_eval_component(void); + +#endif diff --git a/gb.qt4/src/fix_style.cpp b/gb.qt4/src/fix_style.cpp new file mode 100644 index 00000000..01c92b87 --- /dev/null +++ b/gb.qt4/src/fix_style.cpp @@ -0,0 +1,237 @@ +/*************************************************************************** + + fix_stylecpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __FIX_STYLE_CPP + +#include +#include +#include +#include + +#include "gb_common.h" +#include "CStyle.h" +#include "fix_style.h" + +//------------------------------------------------------------------------- + +void FixStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter * painter, const QWidget * widget) const +{ + QStyleOptionButton newOption; + + if (element == CE_CheckBoxLabel || element == CE_RadioButtonLabel) + { + newOption = *(QStyleOptionButton *)option; + newOption.direction = qApp->layoutDirection(); + option = &newOption; + } + + QProxyStyle::drawControl(element, option, painter, widget); +} + +//------------------------------------------------------------------------- + +QFontMetrics *FixBreezeStyle::fm = NULL; + +void FixBreezeStyle::fixFontMetrics(QStyleOption *option) +{ + if (!fm) + { + QFont f = qApp->font(); + f.setPointSize(1); + fm = new QFontMetrics(f); + } + + option->fontMetrics = *fm; +} + +QRect FixBreezeStyle::subControlRect(ComplexControl element, const QStyleOptionComplex* option, SubControl subControl, const QWidget* widget) const +{ + /*if (element == CC_ComboBox) + { + if (subControl == SC_ComboBoxEditField) + { + const QStyleOptionComboBox *comboBoxOption( qstyleoption_cast(option)); + const bool editable( comboBoxOption->editable ); + const bool flat( editable && !comboBoxOption->frame ); + QRect rect(option->rect); + QRect labelRect; + + const int frameWidth(pixelMetric(PM_ComboBoxFrameWidth, option, widget)); + labelRect = QRect( + rect.left(), rect.top(), + rect.width() - 20, //Metrics::MenuButton_IndicatorWidth, + rect.height() ); + + // remove margins + if (!flat) + { + if (CSTYLE_fix_breeze) + labelRect.adjust(frameWidth, 2, 0, -2 ); + else if (CSTYLE_fix_oxygen) + labelRect.adjust(frameWidth, 4, 0, -4 ); + } + + return visualRect( option, labelRect ); + } + } + else*/ + if (element == CC_Slider) + { + const QStyleOptionSlider *sliderOption( qstyleoption_cast( option ) ); + const bool horizontal( sliderOption->orientation == Qt::Horizontal ); + + QRect result(QProxyStyle::subControlRect(element, option, subControl, widget)); + + if (horizontal) + result.moveTop((widget->height() - result.height()) / 2); + else + result.moveLeft((widget->width() - result.width()) / 2); + + return result; + } + + return QProxyStyle::subControlRect(element, option, subControl, widget); +} + +QRect FixBreezeStyle::subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const +{ + if (element == SE_LineEditContents) + { + const QStyleOptionFrame* frameOption(qstyleoption_cast(option)); + + const bool flat( frameOption->lineWidth == 0 ); + if(flat) + return option->rect; + + QRect rect( option->rect ); + + const int frameWidth(pixelMetric(PM_DefaultFrameWidth, option, widget)); + rect.adjust(frameWidth, 2, -frameWidth, -2); + + return rect; + } + + return QProxyStyle::subElementRect(element, option, widget); +} + +void FixBreezeStyle::drawPrimitive( PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget ) const +{ + if (element == PE_FrameLineEdit) + { + QStyleOption newOption = *option; + fixFontMetrics(&newOption); + //qDebug("PE_FrameLineEdit: %d / %d", option->fontMetrics.height(), option->rect.height()); + QProxyStyle::drawPrimitive(element, &newOption, painter, widget); + return; + } + + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void FixBreezeStyle::drawComplexControl(ComplexControl element, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + /*if (element == CC_SpinBox) + { + QStyleOptionSpinBox newOption; + const QStyleOptionSpinBox *spinBoxOption( qstyleoption_cast( option ) ); + + if (option->subControls & SC_SpinBoxFrame) + { + if (spinBoxOption->frame) + { + if( option->subControls & SC_SpinBoxFrame ) + { + newOption = *spinBoxOption; + newOption.subControls &= ~SC_SpinBoxFrame; + option = &newOption; + + drawPrimitive( PE_FrameLineEdit, option, painter, widget ); + } + } + } + } + else + if (element == CC_ComboBox) + { + QStyleOptionComboBox newOption; + const QStyleOptionComboBox* comboBoxOption( qstyleoption_cast( option ) ); + + if (option->subControls & SC_ComboBoxFrame) + { + if (comboBoxOption->editable) + { + if (comboBoxOption->frame) + { + newOption = *comboBoxOption; + newOption.subControls &= ~SC_ComboBoxFrame; + + drawPrimitive(PE_FrameLineEdit, &newOption, painter, widget ); + QProxyStyle::drawComplexControl(element, &newOption, painter, widget); + return; + } + } + } + } + else*/ + if (element == CC_Slider) + { + //QStyleOptionSlider newOption; + const QStyleOptionSlider *sliderOption( qstyleoption_cast( option ) ); + const bool horizontal( sliderOption->orientation == Qt::Horizontal ); + + if (!(sliderOption->subControls & SC_SliderTickmarks)) + { + QRect handle(QProxyStyle::subControlRect(element, option, SC_SliderHandle, widget)); + //newOption = *sliderOption; + //option = &newOption; + + painter->save(); + if (horizontal) + painter->translate(0, (widget->height() - handle.height()) / 2); + else + painter->translate((option->rect.width() - handle.width()) / 2, 0); + //newOption.rect = QRect(newOption.rect.x(), (newOption.rect.height() - handle.height()) / 2, newOption.rect.width(), handle.height()); + + QProxyStyle::drawComplexControl(element, option, painter, widget); + painter->restore(); + return; + } + } + + QProxyStyle::drawComplexControl(element, option, painter, widget); +} + +void FixBreezeStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter * painter, const QWidget * widget) const +{ + if (element == CE_PushButtonBevel) + { + QStyleOptionButton newOption = *(QStyleOptionButton *)option; + newOption.iconSize = QSize(0, 0); + option = &newOption; + QProxyStyle::drawControl(element, option, painter, widget); + return; + } + + FixStyle::drawControl(element, option, painter, widget); +} + diff --git a/gb.qt4/src/fix_style.h b/gb.qt4/src/fix_style.h new file mode 100644 index 00000000..3ffcf503 --- /dev/null +++ b/gb.qt4/src/fix_style.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + fix_style.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __FIX_STYLE_H +#define __FIX_STYLE_H + +#include +#include + +class FixStyle : public QProxyStyle +{ +public: + + void drawControl(ControlElement, const QStyleOption *, QPainter *, const QWidget *) const; +}; + +class FixBreezeStyle : public FixStyle +{ +public: + + QRect subControlRect(ComplexControl, const QStyleOptionComplex*, SubControl, const QWidget*) const; + QRect subElementRect(SubElement, const QStyleOption*, const QWidget*) const; + + void drawComplexControl(ComplexControl, const QStyleOptionComplex*, QPainter*, const QWidget*) const; + void drawPrimitive(PrimitiveElement, const QStyleOption*, QPainter*, const QWidget*) const; + void drawControl(ControlElement, const QStyleOption *, QPainter *, const QWidget *) const; + + QRect visualRect(const QStyleOption* opt, const QRect& subRect) const + { return QProxyStyle::visualRect(opt->direction, opt->rect, subRect); } + + QRect centerRect(const QRect &rect, int width, int height) const + { return QRect(rect.left() + (rect.width() - width)/2, rect.top() + (rect.height() - height)/2, width, height); } + +private: + static QFontMetrics *fm; + static void fixFontMetrics(QStyleOption *); +}; + + +#endif + + diff --git a/gb.qt4/src/gb.qt.h b/gb.qt4/src/gb.qt.h new file mode 100644 index 00000000..5d02e61d --- /dev/null +++ b/gb.qt4/src/gb.qt.h @@ -0,0 +1,177 @@ +/*************************************************************************** + + gb.qt.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_QT_H +#define __GB_QT_H + +#include "gambas.h" +#include "gb.image.h" + +#ifdef OS_MACOSX +//#define NO_X_WINDOW 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QT_INTERFACE_VERSION 1 + +#if QT_VERSION >= 0x050000 +#define QT5 1 +#define QT_NAME "gb.qt5" +#else +#define QT_NAME "gb.qt4" +#endif + +#define TO_QSTRING(_str) (QString::fromUtf8((const char *)(_str))) + +#ifdef DO_NOT_USE_QT_INTERFACE + + #define TO_UTF8(_str) QT_ToUtf8(_str) + #define LAST_UTF8_LENGTH() QT_GetLastUtf8Length() + #define NEW_STRING(_str) QT_NewString(_str) + #define RETURN_NEW_STRING(_str) QT_ReturnNewString(_str) + #define GET_SENDER() void *_object = QT_GetObject((QWidget*)sender()) + +#else + + #define TO_UTF8(_str) QT.ToUtf8(_str) + #define LAST_UTF8_LENGTH() QT.GetLastUtf8Length() + #define NEW_STRING(_str) QT.NewString(_str) + #define RETURN_NEW_STRING(_str) QT.ReturnNewString(_str) + #define GET_SENDER() void *_object = QT.GetObject((QWidget*)sender()) + +#endif + +#define QSTRING_ARG(_arg) (QString::fromUtf8((const char *)(VARG(_arg).addr + VARG(_arg).start), VARG(_arg).len)) +#define QSTRING_PROP() (QString::fromUtf8((const char *)(VPROP(GB_STRING).addr + VPROP(GB_STRING).start), VPROP(GB_STRING).len)) + +#define TO_QCOLOR(_col) QColor::fromRgba((QRgb)((_col) ^ 0xFF000000)) + +typedef + struct { + GB_BASE ob; + QWidget *widget; + void *_r0; + unsigned short flag; + unsigned short _r1; + int _r2; + void *_r3; + void *_r4; + } + QT_WIDGET; + +typedef + struct { + QT_WIDGET widget; + QWidget *container; + int arrangement; + } + QT_CONTAINER; + +typedef + struct { + GB_BASE ob; + QFont *font; + void *func; + void *object; + } + QT_FONT; + +typedef + void *QT_PICTURE; + +typedef + void *QT_IMAGE; + +typedef + struct { + QPainter *p; + QPainter *pm; + QBitmap *mask; + int fg; + int fillColor; + } + QT_DRAW_EXTRA; + +typedef + void (*QT_FONT_FUNC)(QFont &, void *); + +typedef + void (*QT_COLOR_FUNC)(void *); + +typedef + struct { + intptr_t version; + void (*InitEventLoop)(void); + void (*Init)(void); + void (*InitWidget)(QWidget *, void *, int); + void (*SetWheelFlag)(void *); + void *(*GetObject)(QWidget *); + QWidget *(*GetContainer)(void *); + void (*BorderProperty)(void *, void *); + void (*FullBorderProperty)(void *, void *); + void (*ScrollBarProperty)(void *, void *); + void (*FontProperty)(void *, void *); + QT_FONT *(*CreateFont)(const QFont &, QT_FONT_FUNC, void *); + void (*SetFont)(QT_FONT_FUNC, void*, void *); + QT_PICTURE (*CreatePicture)(const QPixmap &); + //QMimeSourceFactory *(*MimeSourceFactory)(void); + QPixmap *(*GetPixmap)(QT_PICTURE); + //QT_IMAGE (*CreateImage)(const QImage &); + const char *(*ToUtf8)(const QString &); + int (*GetLastUtf8Length)(); + char *(*NewString)(const QString &); + void (*ReturnNewString)(const QString &); + bool (*EventFilter)(QEvent *); + bool (*Notify)(void *, bool); + int (*Alignment)(int, int, bool); + void (*Link)(QObject *, void *); + void *(*GetLink)(QObject *); + QPainter *(*GetCurrentPainter)(); + uint (*GetBackgroundColor)(void *); + void (*MouseProperty)(void *, void *); + QT_COLOR_FUNC (*AfterSetColor)(QT_COLOR_FUNC); + int (*GetDesktopScale)(void); + void *_null; + } + QT_INTERFACE; + + +#define QT_WIDGET_PROPERTIES "*" + +#define QT_EVENT_FIRST ((QEvent::Type)(QEvent::User + 10)) + +#endif + diff --git a/gb.qt4/src/gb.qt4.component b/gb.qt4/src/gb.qt4.component new file mode 100644 index 00000000..a07eb600 --- /dev/null +++ b/gb.qt4/src/gb.qt4.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.qt4 +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form diff --git a/gb.qt4/src/main.cpp b/gb.qt4/src/main.cpp new file mode 100644 index 00000000..e219e99d --- /dev/null +++ b/gb.qt4/src/main.cpp @@ -0,0 +1,1599 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb.image.h" +#include "gb.qt.h" +#include "gb.form.font.h" + +#include "CFont.h" +#include "CScreen.h" +#include "CStyle.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CButton.h" +#include "CContainer.h" +#include "CTextBox.h" +#include "CTextArea.h" +#include "CMenu.h" +#include "CPanel.h" +#include "CMouse.h" +#include "CKey.h" +#include "CColor.h" +#include "CConst.h" +#include "CCheckBox.h" +#include "CRadioButton.h" +#include "CTabStrip.h" +#include "CDialog.h" +#include "CPicture.h" +#include "CImage.h" +#include "canimation.h" +#include "CClipboard.h" +#include "CDraw.h" +#include "CWatch.h" +#include "CDrawingArea.h" +#include "CSlider.h" +#include "CScrollBar.h" +#include "CWatcher.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "cpaint_impl.h" +#include "ctrayicon.h" + +#include +#ifndef QT5 +#include "CEmbedder.h" +#endif + +#include "desktop.h" +#include "x11.h" + +#ifdef QT5 +#include +#include +#include +#endif + +#include "fix_style.h" +#include "main.h" + +/*#define DEBUG*/ + +extern "C" { +const GB_INTERFACE *GB_PTR EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; +} + +int MAIN_in_wait = 0; +int MAIN_in_message_box = 0; +int MAIN_loop_level = 0; +int MAIN_scale = 6; +#ifndef NO_X_WINDOW +int MAIN_x11_last_key_code = 0; +#endif +bool MAIN_debug_busy = false; +bool MAIN_init = false; +bool MAIN_key_debug = false; +bool MAIN_right_to_left = false; + +GB_CLASS CLASS_Control; +GB_CLASS CLASS_Container; +GB_CLASS CLASS_ContainerChildren; +GB_CLASS CLASS_UserControl; +GB_CLASS CLASS_UserContainer; +GB_CLASS CLASS_TabStrip; +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Menu; +GB_CLASS CLASS_Picture; +GB_CLASS CLASS_Drawing; +GB_CLASS CLASS_DrawingArea; +GB_CLASS CLASS_Printer; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_SvgImage; +GB_CLASS CLASS_TextArea; + +static bool in_event_loop = false; +static int _no_destroy = 0; +static QTranslator *_translator = NULL; +static bool _application_keypress = false; +static GB_FUNCTION _application_keypress_func; +static bool _check_quit_posted = false; +static int _prevent_quit = 0; + +#ifndef NO_X_WINDOW +static int (*_x11_event_filter)(XEvent *) = 0; +#endif + +static QHash _link_map; + +static QPointer MAIN_mouseGrabber = 0; +static QPointer MAIN_keyboardGrabber = 0; + +static QByteArray _utf8_buffer[UTF8_NBUF]; +static int _utf8_count = 0; +static int _utf8_length = 0; + +static void QT_Init(void); + +#ifdef QT5 + +static QtMessageHandler _previousMessageHandler; + +static void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg ) +{ + //fprintf(stderr, "---- `%s'\n", QT_ToUtf8(msg)); + + if (msg == "QXcbClipboard: SelectionRequest too old") + return; + + if (msg.startsWith("QXcbConnection: ") && msg.contains("(TranslateCoords)")) + return; + + _previousMessageHandler(type, context, msg); +} +#endif + +//static MyApplication *myApp; + +/*************************************************************************** + + MyMimeSourceFactory + + Create a QMimeSourceFactory to handle files stored in an archive + +***************************************************************************/ + +#if 0 +class MyMimeSourceFactory: public Q3MimeSourceFactory +{ +public: + + MyMimeSourceFactory(); + + virtual const QMimeSource* data(const QString& abs_name) const; + +private: + + QMap extensions; +}; + + +MyMimeSourceFactory::MyMimeSourceFactory() +{ + extensions.replace("htm", "text/html;charset=UTF-8"); + extensions.replace("html", "text/html;charset=UTF-8"); + extensions.replace("txt", "text/plain"); + extensions.replace("xml", "text/xml;charset=UTF-8"); + extensions.replace("jpg", "image/jpeg"); + extensions.replace("png", "image/png"); + extensions.replace("gif", "image/gif"); +} + + +const QMimeSource* MyMimeSourceFactory::data(const QString& abs_name) const +{ + char *addr; + int len; + Q3StoredDrag* sr = 0; + char *path; + + //qDebug("MyMimeSourceFactory::data: %s", (char *)abs_name.latin1()); + + path = (char *)abs_name.latin1(); + + if (true) //abs_name[0] != '/') + { + if (GB.LoadFile(path, 0, &addr, &len)) + GB.Error(NULL); + else + { + QByteArray ba; + ba.setRawData((const char *)addr, len); + + QFileInfo fi(abs_name); + QString e = fi.extension(FALSE); + Q3CString mimetype = "text/html"; //"application/octet-stream"; + + const char* imgfmt; + + if ( extensions.contains(e) ) + mimetype = extensions[e].latin1(); + else + { + QBuffer buffer(&ba); + + buffer.open(QIODevice::ReadOnly); + if (( imgfmt = QImageReader::imageFormat( &buffer ) ) ) + mimetype = Q3CString("image/")+Q3CString(imgfmt).lower(); + buffer.close(); + } + + sr = new Q3StoredDrag( mimetype ); + sr->setEncodedData( ba ); + + ba.resetRawData((const char*)addr, len); + + //qDebug("MimeSource: %s %s", abs_name.latin1(), (const char *)mimetype); + + GB.ReleaseFile(addr, len); + } + } + + return sr; +} + +static MyMimeSourceFactory myMimeSourceFactory; +#endif + +#if 0 +/*************************************************************************** + + MyAbstractEventDispatcher + + Manage window deletion + +***************************************************************************/ + +class MyAbstractEventDispatcher : public QAbstractEventDispatcher +{ +public: + MyAbstractEventDispatcher(); + virtual bool processEvents(QEventLoop::ProcessEventsFlags flags); +}; + +MyAbstractEventDispatcher::MyAbstractEventDispatcher() +: QAbstractEventDispatcher() +{ +} + +bool MyAbstractEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + bool ret; + CWIDGET **ptr; + CWIDGET *ob; + + MAIN_loop_level++; + ret = QAbstractEventDispatcher::processEvents(flags); + MAIN_loop_level--; + + for(;;) + { + ptr = &CWIDGET_destroy_list; + + for(;;) + { + ob = *ptr; + if (!ob) + return ret; + + //if (MAIN_loop_level <= ob->level && !ob->flag.notified) + if (!ob->flag.notified) + { + //qDebug("delete: %s %p", GB.GetClassName(ob), ob); + //qDebug(">> delete %p (%p) :%p:%ld", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //*ptr = ob->next; + delete ob->widget; + break; + //GB.Unref(POINTER(&ob)); + //qDebug(" delete %p (%p) :%p:%ld #2", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //qDebug("<< delete %p (%p)", ob, ob->widget); + } + else + { + //qDebug("cannot delete: %s %p", GB.GetClassName(ob), ob); + ptr = &ob->next; + } + } + } + //return ret; +} +#endif + +void MAIN_process_events(void) +{ + _no_destroy++; + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + _no_destroy--; +} + +void MAIN_init_error() +{ + GB.Error("GUI is not initialized"); +} + +/** MyApplication **********************************************************/ + +bool MyApplication::_tooltip_disable = false; +int MyApplication::_event_filter = 0; +QEventLoop *MyApplication::eventLoop = 0; + +MyApplication::MyApplication(int &argc, char **argv) +: QApplication(argc, argv) +{ + if (isSessionRestored()) + { + bool ok; + int desktop; + + if (argc >= 2 && ::strcmp(argv[argc - 2], "-session-desktop") == 0) + { + desktop = QString(argv[argc - 1]).toInt(&ok); + if (ok) + CWINDOW_MainDesktop = desktop; + + //qDebug("session desktop: %d", CWINDOW_MainDesktop); + + argc -= 2; + } + } + + QObject::connect(this, SIGNAL(commitDataRequest(QSessionManager &)), SLOT(commitDataRequested(QSessionManager &))); +} + +void MyApplication::initClipboard() +{ + QObject::connect(clipboard(), SIGNAL(changed(QClipboard::Mode)), qApp, SLOT(clipboardHasChanged(QClipboard::Mode))); +} + +void MyApplication::clipboardHasChanged(QClipboard::Mode m) +{ + CLIPBOARD_has_changed(m); +} + +static bool QT_EventFilter(QEvent *e) +{ + bool cancel; + + if (!_application_keypress) + return false; + + if (e->type() == QEvent::KeyPress) + { + QKeyEvent *kevent = (QKeyEvent *)e; + + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + CKEY_info.text = GB.NewZeroString(QT_ToUtf8(kevent->text())); + CKEY_info.state = kevent->modifiers(); + CKEY_info.code = kevent->key(); + + } + else if (e->type() == QEvent::InputMethod) + { + QInputMethodEvent *imevent = (QInputMethodEvent *)e; + + if (!imevent->commitString().isEmpty()) + { + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + //qDebug("IMEnd: %s", imevent->text().latin1()); + CKEY_info.text = GB.NewZeroString(QT_ToUtf8(imevent->commitString())); + CKEY_info.state = Qt::KeyboardModifiers(); + CKEY_info.code = 0; + } + } + + GB.Call(&_application_keypress_func, 0, FALSE); + cancel = GB.Stopped(); + + CKEY_clear(false); + + return cancel; +} + +static bool QT_Notify(CWIDGET *object, bool value) +{ + bool old = object->flag.notified; + //qDebug("QT_Notify: %s %p %d", GB.GetClassName(object), object, value); + object->flag.notified = value; + return old; +} + +bool MyApplication::eventFilter(QObject *o, QEvent *e) +{ + if (o->isWidgetType()) + { + if ((e->spontaneous() && e->type() == QEvent::KeyPress) || e->type() == QEvent::InputMethod) + { + if (QT_EventFilter(e)) + return true; + } + else if (e->type() == QEvent::ToolTip) + { + if (_tooltip_disable) + return true; + } + else + { + QWidget *widget = (QWidget *)o; + CWIDGET *control; + + if (widget->isWindow()) + { + if (e->type() == QEvent::WindowActivate) + { + control = CWidget::getReal(widget); + //qDebug("WindowActivate: %p %s", widget, control ? control->name : "NULL"); + if (control) + CWIDGET_handle_focus(control, true); + else + CWINDOW_activate(NULL); + } + else if (e->type() == QEvent::WindowDeactivate) + { + control = CWidget::getReal(widget); + //qDebug("WindowDeactivate: %p %s", widget, control ? control->name : "NULL"); + if (control) + CWIDGET_handle_focus(control, false); + } + } + } + } + + return QApplication::eventFilter(o, e); +} + +/*bool MyApplication::notify(QObject *o, QEvent *e) +{ + if (o->isWidgetType()) + { + CWIDGET *ob = CWidget::get(o); + bool old, res; + + if (ob) + { + old = QT_Notify(ob, true); + res = QApplication::notify(o, e); + QT_Notify(ob, old); + return res; + } + } + + return QApplication::notify(o, e); +}*/ + +void MyApplication::setEventFilter(bool set) +{ + if (set) + { + _event_filter++; + if (_event_filter == 1) + qApp->installEventFilter(qApp); + } + else + { + _event_filter--; + if (_event_filter == 0) + qApp->removeEventFilter(qApp); + } +} + +void MyApplication::setTooltipEnabled(bool b) +{ + b = !b; + if (b == _tooltip_disable) + return; + + _tooltip_disable = b; + setEventFilter(b); +} + +void MyApplication::commitDataRequested(QSessionManager &session) +{ + QStringList cmd; + + if (CAPPLICATION_Restart) + { + int i; + char **str; + + str = (char **)GB.Array.Get(CAPPLICATION_Restart, 0); + for (i = 0; i < GB.Array.Count(CAPPLICATION_Restart); i++) + { + if (str[i]) + cmd += str[i]; + else + cmd += ""; + } + } + else + cmd += arguments().at(0); + + cmd += "-session"; + cmd += sessionId(); + + if (CWINDOW_Main) + { + cmd += "-session-desktop"; + cmd += QString::number(X11_window_get_desktop(CWINDOW_Main->widget.widget->winId())); + /*cmd += "-session-data"; + cmd += QString::number(CWINDOW_Main->x) + "," + + QString::number(CWINDOW_Main->y) + "," + + QString::number(CWINDOW_Main->w) + "," + + QString::number(CWINDOW_Main->h) + "," + + QString::number(QApplication::desktop()->screenNumber(CWINDOW_Main->widget.widget));*/ + } + + session.setRestartCommand(cmd); +} + +//--------------------------------------------------------------------------- + +static void x11_set_event_filter(int (*filter)(XEvent *)) +{ + _x11_event_filter = filter; +} + +#ifdef QT5 + +class MyNativeEventFilter: public QAbstractNativeEventFilter +{ +public: + + static MyNativeEventFilter manager; + + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) + { + xcb_generic_event_t *ev = static_cast(message); + int type = ev->response_type & ~0x80; + + switch(type) + { + case XCB_KEY_PRESS: + case XCB_KEY_RELEASE: + MAIN_x11_last_key_code = ((xcb_key_press_event_t *)ev)->detail; + break; + } + + if (_x11_event_filter) + { + XEvent xev; + + CLEAR(&xev); + xev.xany.type = type; + xev.xany.display = QX11Info::display(); + xev.xany.send_event = ev->response_type & 0x80 ? 1 : 0; + + switch (type) + { + //case XCB_KEY_PRESS: + //case XCB_KEY_RELEASE: + case XCB_EXPOSE: + { + xcb_expose_event_t *e = (xcb_expose_event_t *)ev; + xev.xexpose.window = e->window; + xev.xexpose.x = e->x; + xev.xexpose.y = e->y; + xev.xexpose.width = e->width; + xev.xexpose.height = e->height; + xev.xexpose.count = e->count; + break; + } + + case XCB_VISIBILITY_NOTIFY: + { + xcb_visibility_notify_event_t *e = (xcb_visibility_notify_event_t *)ev; + xev.xvisibility.window = e->window; + xev.xvisibility.state = e->state; + break; + } + + case XCB_DESTROY_NOTIFY: + { + xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *)ev; + xev.xdestroywindow.event = e->event; + xev.xdestroywindow.window = e->window; + break; + } + + case XCB_MAP_NOTIFY: + { + xcb_map_notify_event_t *e = (xcb_map_notify_event_t *)ev; + xev.xmap.event = e->event; + xev.xmap.window = e->window; + xev.xmap.override_redirect = e->override_redirect; + break; + } + + case XCB_UNMAP_NOTIFY: + { + xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *)ev; + xev.xunmap.event = e->event; + xev.xunmap.window = e->window; + xev.xunmap.from_configure = e->from_configure; + break; + } + + case XCB_REPARENT_NOTIFY: + { + xcb_reparent_notify_event_t *e = (xcb_reparent_notify_event_t *)ev; + xev.xreparent.event = e->event; + xev.xreparent.window = e->window; + xev.xreparent.parent = e->parent; + xev.xreparent.x = e->x; + xev.xreparent.y = e->y; + xev.xreparent.override_redirect = e->override_redirect; + break; + } + + case XCB_CONFIGURE_NOTIFY: + { + xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *)ev; + xev.xconfigure.event = e->event; + xev.xconfigure.window = e->window; + xev.xconfigure.x = e->x; + xev.xconfigure.y = e->y; + xev.xconfigure.width = e->width; + xev.xconfigure.height = e->height; + xev.xconfigure.border_width = e->border_width; + xev.xconfigure.override_redirect = e->override_redirect; + break; + } + + case XCB_PROPERTY_NOTIFY: + { + xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)ev; + xev.xproperty.window = e->window; + xev.xproperty.atom = e->atom; + xev.xproperty.time = e->time; + xev.xproperty.state = e->state; + break; + } + + case XCB_SELECTION_CLEAR: + { + xcb_selection_clear_event_t *e = (xcb_selection_clear_event_t *)ev; + xev.xselectionclear.window = e->owner; + xev.xselectionclear.selection = e->selection; + xev.xselectionclear.time = e->time; + break; + } + + case XCB_SELECTION_REQUEST: + { + xcb_selection_request_event_t *e = (xcb_selection_request_event_t *)ev; + xev.xselectionrequest.owner = e->owner; + xev.xselectionrequest.requestor = e->requestor; + xev.xselectionrequest.selection = e->selection; + xev.xselectionrequest.target = e->target; + xev.xselectionrequest.property = e->property; + xev.xselectionrequest.time = e->time; + break; + } + + case XCB_SELECTION_NOTIFY: + { + xcb_selection_notify_event_t *e = (xcb_selection_notify_event_t *)ev; + xev.xselection.requestor = e->requestor; + xev.xselection.selection = e->selection; + xev.xselection.target = e->target; + xev.xselection.property = e->property; + xev.xselection.time = e->time; + break; + } + + case XCB_CLIENT_MESSAGE: + { + xcb_client_message_event_t *e = (xcb_client_message_event_t *)ev; + xev.xclient.window = e->window; + xev.xclient.message_type = e->type; + xev.xclient.format = e->format; + xev.xclient.data.l[0] = e->data.data32[0]; + xev.xclient.data.l[1] = e->data.data32[1]; + xev.xclient.data.l[2] = e->data.data32[2]; + xev.xclient.data.l[3] = e->data.data32[3]; + xev.xclient.data.l[4] = e->data.data32[4]; + break; + } + + default: + qDebug("gb.qt5: warning: unhandled xcb event: %d", type); + return false; + } + + return (*_x11_event_filter)(&xev) != 0; + } + + return false; + } +}; + +MyNativeEventFilter MyNativeEventFilter::manager; + +#else + +bool MyApplication::x11EventFilter(XEvent *e) +{ + // Workaround for input methods that void the key code of KeyRelease eventFilter + if (e->type == XKeyPress) + MAIN_x11_last_key_code = e->xkey.keycode; + else if (e->type == XKeyRelease) + MAIN_x11_last_key_code = e->xkey.keycode; + + if (_x11_event_filter) + return (*_x11_event_filter)(e); + + return false; +} + +#endif + +//--------------------------------------------------------------------------- + +MyTimer::MyTimer(GB_TIMER *t) : QObject(0) +{ + timer = t; + id = startTimer(t->delay); +} + +MyTimer::~MyTimer() +{ + killTimer(id); +} + +void MyTimer::timerEvent(QTimerEvent *e) +{ + if (timer) + GB.RaiseTimer(timer); +} + +//--------------------------------------------------------------------------- + +static void release_grab() +{ + MAIN_mouseGrabber = QWidget::mouseGrabber(); + MAIN_keyboardGrabber = QWidget::keyboardGrabber(); + + if (MAIN_mouseGrabber) + { + //qDebug("releaseMouse"); + MAIN_mouseGrabber->releaseMouse(); + } + if (MAIN_keyboardGrabber) + { + //qDebug("releaseKeyboard"); + MAIN_keyboardGrabber->releaseKeyboard(); + } + + #ifndef NO_X_WINDOW + if (qApp->activePopupWidget()) + { + XUngrabPointer(QX11Info::display(), CurrentTime); + XFlush(QX11Info::display()); + } + #endif +} + + +static void unrelease_grab() +{ + if (MAIN_mouseGrabber) + { + //qDebug("grabMouse"); + MAIN_mouseGrabber->grabMouse(); + MAIN_mouseGrabber = 0; + } + + if (MAIN_keyboardGrabber) + { + //qDebug("grabKeyboard"); + MAIN_keyboardGrabber->grabKeyboard(); + MAIN_keyboardGrabber = 0; + } +} + +static bool must_quit(void) +{ + #if DEBUG_WINDOW + qDebug("must_quit: Window = %d Watch = %d in_event_loop = %d MAIN_in_message_box = %d _prevent_quit = %d", CWindow::count, CWatch::count, in_event_loop, MAIN_in_message_box, _prevent_quit); + #endif + return CWINDOW_must_quit() && CWatch::count == 0 && in_event_loop && MAIN_in_message_box == 0 && _prevent_quit == 0 && !GB.HasActiveTimer(); +} + +static void check_quit_now(intptr_t param) +{ + static bool exit_called = false; + + if (must_quit() && !exit_called) + { + if (QApplication::instance()) + { + GB_FUNCTION func; + + if (GB.ExistClass("TrayIcons")) + { + if (!GB.GetFunction(&func, (void *)GB.FindClass("TrayIcons"), "DeleteAll", NULL, NULL)) + GB.Call(&func, 0, FALSE); + } + +#ifndef QT5 + qApp->syncX(); +#endif + qApp->exit(); + exit_called = true; + } + } + else + _check_quit_posted = false; +} + +void MAIN_check_quit(void) +{ + if (_check_quit_posted) + return; + + GB.Post((GB_CALLBACK)check_quit_now, 0); + _check_quit_posted = true; +} + +void MAIN_update_scale(const QFont &font) +{ + MAIN_scale = GET_DESKTOP_SCALE(font.pointSize(), QX11Info::appDpiY()); +} + +static void QT_InitEventLoop(void) +{ +} + +//extern void qt_x11_set_global_double_buffer(bool); + + +static bool try_to_load_translation(QString &locale) +{ + // QLocale::system().name() + return _translator->load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); +/*#ifdef QT5 + return (!_translator->load(QString("qt_") + locale, QString(getenv("QTDIR")) + "/translations") + && !_translator->load(QString("qt_") + locale, QString("/usr/lib/qt5/translations")) + && !_translator->load(QString("qt_") + locale, QString("/usr/share/qt5/translations"))); +#else + return (!_translator->load(QString("qt_") + locale, QString(getenv("QTDIR")) + "/translations") + && !_translator->load(QString("qt_") + locale, QString("/usr/lib/qt4/translations")) + && !_translator->load(QString("qt_") + locale, QString("/usr/share/qt4/translations"))); +#endif*/ +} + +static void init_lang(char *lang, bool rtl) +{ + int pos; + QString locale(lang); + + MAIN_right_to_left = rtl; + + pos = locale.lastIndexOf("."); + if (pos >= 0) locale = locale.left(pos); + + if (_translator) + { + qApp->removeTranslator(_translator); + delete _translator; + _translator = NULL; + } + + _translator = new QTranslator(); + + if (!try_to_load_translation(locale)) + goto __INSTALL_TRANSLATOR; + + pos = locale.lastIndexOf("_"); + if (pos >= 0) + { + locale = locale.left(pos); + if (!try_to_load_translation(locale)) + goto __INSTALL_TRANSLATOR; + } + + delete _translator; + _translator = NULL; + + //if (strcmp(lang, "C")) + // qDebug("gb.qt4: warning: unable to load Qt translation: %s", lang); + + goto __SET_DIRECTION; + +__INSTALL_TRANSLATOR: + qApp->installTranslator(_translator); + +__SET_DIRECTION: + qApp->setLayoutDirection(rtl ? Qt::RightToLeft : Qt::LeftToRight); +} + +static void hook_lang(char *lang, int rtl) +{ + if (!qApp) + return; + + init_lang(lang, rtl); + + //locale = QTextCodec::locale(); +} + +#if 0 +static int (*_old_handler)(Display *d, XErrorEvent *e) = NULL; + +static int X11_error_handler(Display *d, XErrorEvent *e) +{ + qDebug("X11 ERROR"); + //BREAKPOINT(); + + if (_old_handler) + return (*_old_handler)(d, e); + else + return 0; +} +#endif + +static void *_old_hook_main; + +static void hook_main(int *argc, char ***argv) +{ + char *env; + + env = getenv("GB_X11_INIT_THREADS"); + if (env && atoi(env)) + XInitThreads(); + + new MyApplication(*argc, *argv); + + QT_Init(); + init_lang(GB.System.Language(), GB.System.IsRightToLeft()); + + MAIN_init = true; + + //_old_handler = XSetErrorHandler(X11_error_handler); + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + + +static void hook_quit() +{ + GB_FUNCTION func; + + CWINDOW_close_all(true); + CWINDOW_delete_all(true); + CMOUSE_set_control(NULL); + + qApp->sendPostedEvents(); //processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + qApp->sendPostedEvents(0, QEvent::DeferredDelete); + + if (!GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_Quit", NULL, NULL)) + GB.Call(&func, 0, FALSE); +} + + +static void hook_loop() +{ + //qDebug("**** ENTERING EVENT LOOP"); + + qApp->sendPostedEvents(); + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + in_event_loop = true; + + if (!must_quit()) + qApp->exec(); + else + MAIN_check_quit(); + + hook_quit(); +} + + +static void hook_wait(int duration) +{ + static bool _warning = FALSE; + + if (MyDrawingArea::inAnyDrawEvent()) + { + GB.Error("Wait is forbidden during a repaint event"); + return; + } + + if (CKEY_is_valid() && duration != -1) + { + if (!_warning) + { + fprintf(stderr, QT_NAME ": warning: calling the event loop during a keyboard event handler is ignored\n"); + _warning = TRUE; + } + return; + } + + MAIN_in_wait++; + + if (duration >= 0) + qApp->processEvents(QEventLoop::AllEvents, duration); + else if (duration == -1) + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + else if (duration == -2) + qApp->processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents); + + MAIN_in_wait--; +} + + +static void hook_timer(GB_TIMER *timer, bool on) +{ + if (timer->id) + { + MyTimer *t = (MyTimer *)(timer->id); + t->clearTimer(); + t->deleteLater(); + timer->id = 0; + } + + if (on) + timer->id = (intptr_t)(new MyTimer(timer)); + else + MAIN_check_quit(); +} + + +static void hook_watch(int fd, int type, void *callback, intptr_t param) +{ + CWatch::watch(fd, type, (GB_WATCH_CALLBACK)callback, param); +} + + +static void hook_post(void) +{ + static MyPostCheck check; + + //qDebug("hook_post ?"); + + if (MyPostCheck::in_check) + return; + + //qDebug("hook_post !"); + + MyPostCheck::in_check = true; + QTimer::singleShot(0, &check, SLOT(check())); +} + + +static bool hook_error(int code, char *error, char *where, bool in_event_loop) +{ + QString msg; + int ret; + + qApp->restoreOverrideCursor(); + while (qApp->activePopupWidget()) + delete qApp->activePopupWidget(); + CWatch::stop(); + + msg = "This application has raised an unexpected
    error and must abort.


    "; + + if (code > 0) + { + msg = msg + "[%1] %2.

    %3"; + msg = msg.arg(code).arg(TO_QSTRING(error)).arg(where); + } + else + { + msg = msg + "%1.

    %2"; + msg = msg.arg(TO_QSTRING(error)).arg(where); + } + + release_grab(); + MAIN_in_message_box++; + ret = QMessageBox::critical(0, TO_QSTRING(GB.Application.Name()), msg, in_event_loop ? QMessageBox::Close | QMessageBox::Ignore : QMessageBox::Ok); + MAIN_in_message_box--; + unrelease_grab(); + MAIN_check_quit(); + + return ret == QMessageBox::Ignore; +} + +static void QT_Init(void) +{ + static bool init = false; + QFont f; + char *env; + bool fix_style; + + if (init) + return; + + //qApp->setAttribute(Qt::AA_ImmediateWidgetCreation); + + X11_init(QX11Info::display(), QX11Info::appRootWindow()); + +#ifdef QT5 + _previousMessageHandler = qInstallMessageHandler(myMessageHandler); +#endif + + /*QX11Info::setAppDpiX(0, 92); + QX11Info::setAppDpiY(0, 92);*/ + + /*fcntl(ConnectionNumber(qt_xdisplay()), F_SETFD, FD_CLOEXEC);*/ + + fix_style = false; + + if (::strcmp(qApp->style()->metaObject()->className(), "Breeze::Style") == 0) + { + env = getenv("GB_QT_NO_BREEZE_FIX"); + if (!env || atoi(env) == 0) + { + CSTYLE_fix_breeze = TRUE; + qApp->setStyle(new FixBreezeStyle); + fix_style = true; + } + } + else if (::strcmp(qApp->style()->metaObject()->className(), "Oxygen::Style") == 0) + { + env = getenv("GB_QT_NO_OXYGEN_FIX"); + if (!env || atoi(env) == 0) + { + CSTYLE_fix_oxygen = TRUE; + qApp->setStyle(new FixBreezeStyle); + fix_style = true; + } + } + + if (!fix_style) + qApp->setStyle(new FixStyle); + + MAIN_update_scale(qApp->desktop()->font()); + + qApp->installEventFilter(&CWidget::manager); +#ifdef QT5 + qApp->installNativeEventFilter(&MyNativeEventFilter::manager); +#endif + + MyApplication::setEventFilter(true); + + if (GB.GetFunction(&_application_keypress_func, (void *)GB.Application.StartupClass(), "Application_KeyPress", "", "") == 0) + { + _application_keypress = true; + MyApplication::setEventFilter(true); + } + + //qt_x11_set_global_double_buffer(false); + + qApp->setQuitOnLastWindowClosed(false); + + MyApplication::initClipboard(); + + env = getenv("GB_QT_KEY_DEBUG"); + if (env && atoi(env) != 0) + MAIN_key_debug = TRUE; + + GB.Hook(GB_HOOK_WAIT, (void *)hook_wait); + GB.Hook(GB_HOOK_TIMER, (void *)hook_timer); + GB.Hook(GB_HOOK_WATCH, (void *)hook_watch); + GB.Hook(GB_HOOK_POST, (void *)hook_post); + GB.Hook(GB_HOOK_LOOP, (void *)hook_loop); + + init = true; +} + + +static void QT_InitWidget(QWidget *widget, void *object, int fill_bg) +{ + ((CWIDGET *)object)->flag.fillBackground = fill_bg; + CWIDGET_new(widget, object); +} + +static void QT_SetWheelFlag(void *object) +{ + ((CWIDGET *)object)->flag.wheel = true; +} + +void *QT_GetObject(QWidget *widget) +{ + return CWidget::get((QObject *)widget); +} + +static QWidget *QT_GetContainer(void *object) +{ + return QCONTAINER(object); +} + +/*static bool QT_IsDestroyed(void *object) +{ + return CWIDGET_test_flag(object, WF_DELETED); +}*/ + +static QPixmap *QT_GetPixmap(CPICTURE *pict) +{ + return pict->pixmap; +} + +const char *QT_ToUtf8(const QString &str) +{ + const char *res; + + _utf8_buffer[_utf8_count] = str.toUtf8(); + res = _utf8_buffer[_utf8_count].data(); + _utf8_length = _utf8_buffer[_utf8_count].length(); + _utf8_count++; + if (_utf8_count >= UTF8_NBUF) + _utf8_count = 0; + + return res; +} + +int QT_GetLastUtf8Length() +{ + return _utf8_length; +} + +char *QT_NewString(const QString &str) +{ + const char *res = QT_ToUtf8(str); + return GB.NewString(res, _utf8_length); +} + +void QT_ReturnNewString(const QString &str) +{ + const char *res = QT_ToUtf8(str); + GB.ReturnNewString(res, _utf8_length); +} + +static void *QT_CreatePicture(const QPixmap &p) +{ + return CPICTURE_create(&p); +} + +/*static void *QT_CreateImage(const Image &p) +{ + return CIMAGE_create(&p); +}*/ + +void MyApplication::linkDestroyed(QObject *qobject) +{ + void *object = _link_map.value(qobject, 0); + _link_map.remove(qobject); + if (object) + GB.Unref(POINTER(&object)); +} + +void QT_Link(QObject *qobject, void *object) +{ + _link_map.insert(qobject, object); + QObject::connect(qobject, SIGNAL(destroyed(QObject *)), qApp, SLOT(linkDestroyed(QObject *))); + GB.Ref(object); +} + +void *QT_GetLink(QObject *qobject) +{ + return _link_map.value(qobject, 0); +} + +void QT_PreventQuit(bool inc) +{ + if (inc) + _prevent_quit++; + else + { + _prevent_quit--; + MAIN_check_quit(); + } +} + +QMenu *QT_FindMenu(void *parent, const char *name) +{ + CMENU *menu = NULL; + + if (parent && GB.Is(parent, CLASS_Control)) + { + CWINDOW *window = CWidget::getWindow((CWIDGET *)parent); + menu = CWindow::findMenu(window, name); + } + + return menu ? menu->menu : NULL; +} + +static void declare_tray_icon() +{ + GB.Component.Declare(TrayIconDesc); + GB.Component.Declare(TrayIconsDesc); +} + +static int QT_GetDesktopScale(void) +{ + return MAIN_scale; +} + +extern "C" { + +GB_DESC *GB_CLASSES[] EXPORT = +{ + BorderDesc, CColorDesc, + AlignDesc, ArrangeDesc, ScrollDesc, CKeyDesc, SelectDesc, DirectionDesc, + CImageDesc, CPictureDesc, AnimationDesc, + CFontDesc, CFontsDesc, + CMouseDesc, CCursorDesc, CPointerDesc, + CClipboardDesc, CDragDesc, + StyleDesc, ScreenDesc, ScreensDesc, DesktopDesc, + ApplicationDesc, + CControlDesc, ContainerChildrenDesc, ContainerDesc, + UserControlDesc, UserContainerDesc, + CMenuChildrenDesc, CMenuDesc, + CButtonDesc, CToggleButtonDesc, CToolButtonDesc, + CCheckBoxDesc, CRadioButtonDesc, + CTextBoxSelectionDesc, CTextBoxDesc, + CTextAreaSelectionDesc, CTextAreaDesc, + CPanelDesc, CHBoxDesc, CVBoxDesc, CHPanelDesc, CVPanelDesc, + CTabStripContainerChildrenDesc, CTabStripContainerDesc, CTabStripDesc, + CDrawingAreaDesc, + SliderDesc, ScrollBarDesc, + CWindowMenusDesc, CWindowControlsDesc, CWindowDesc, CWindowsDesc, CFormDesc, + CDialogDesc, +#ifndef QT5 + CEmbedderDesc, +#endif + CWatcherDesc, + PrinterDesc, + SvgImageDesc, + NULL +}; + +#ifdef QT5 +void *GB_QT5_1[] EXPORT = +#else +void *GB_QT4_1[] EXPORT = +#endif +{ + (void *)1, + + (void *)QT_InitEventLoop, + (void *)QT_Init, + (void *)QT_InitWidget, + (void *)QT_SetWheelFlag, + (void *)QT_GetObject, + (void *)QT_GetContainer, + (void *)CWIDGET_border_simple, + (void *)CWIDGET_border_full, + (void *)CWIDGET_scrollbar, + (void *)Control_Font, + (void *)CFONT_create, + (void *)CFONT_set, + (void *)QT_CreatePicture, + //(void *)QT_MimeSourceFactory, + (void *)QT_GetPixmap, + //(void *)QT_CreateImage, + (void *)QT_ToUtf8, + (void *)QT_GetLastUtf8Length, + (void *)QT_NewString, + (void *)QT_ReturnNewString, + (void *)QT_EventFilter, + (void *)QT_Notify, + (void *)CCONST_alignment, + (void *)QT_Link, + (void *)QT_GetLink, + (void *)PAINT_get_current, + (void *)CWIDGET_get_background, + (void *)Control_Mouse, + (void *)CWIDGET_after_set_color, + (void *)QT_GetDesktopScale, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.draw,gb.gui.base"; + +int EXPORT GB_INIT(void) +{ + char *env; + + // Do not disable GLib support + + /*env = getenv("KDE_FULL_SESSION"); + if (env && !strcasecmp(env, "true")) + putenv((char *)"QT_NO_GLIB=1");*/ + + env = getenv("GB_GUI_BUSY"); + if (env && atoi(env)) + MAIN_debug_busy = true; + + //putenv((char *)"QT_SLOW_TOPLEVEL_RESIZE=1"); + + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + GB.Hook(GB_HOOK_QUIT, (void *)hook_quit); + GB.Hook(GB_HOOK_ERROR, (void *)hook_error); + GB.Hook(GB_HOOK_LANG, (void *)hook_lang); + + GB.Component.Load("gb.draw"); + GB.Component.Load("gb.image"); + GB.Component.Load("gb.gui.base"); + + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + IMAGE.SetDefaultFormat(GB_IMAGE_BGRP); + + DRAW_init(); + + CLASS_Control = GB.FindClass("Control"); + CLASS_Container = GB.FindClass("Container"); + CLASS_ContainerChildren = GB.FindClass("ContainerChildren"); + CLASS_UserControl = GB.FindClass("UserControl"); + CLASS_UserContainer = GB.FindClass("UserContainer"); + CLASS_TabStrip = GB.FindClass("TabStrip"); + CLASS_Window = GB.FindClass("Window"); + CLASS_Menu = GB.FindClass("Menu"); + CLASS_Picture = GB.FindClass("Picture"); + CLASS_Drawing = GB.FindClass("Drawing"); + CLASS_DrawingArea = GB.FindClass("DrawingArea"); + CLASS_Printer = GB.FindClass("Printer"); + CLASS_Image = GB.FindClass("Image"); + CLASS_SvgImage = GB.FindClass("SvgImage"); + CLASS_TextArea = GB.FindClass("TextArea"); + + QT_InitEventLoop(); + + #ifdef OS_CYGWIN + return 1; + #else + return 0; + #endif +} + +void EXPORT GB_EXIT() +{ + #ifndef NO_X_WINDOW + X11_exit(); + #endif + //qApp->setStyle("windows"); + delete qApp; +} + +#ifndef NO_X_WINDOW +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)QX11Info::display(); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)QX11Info::appRootWindow(); + return TRUE; + } + else if (!strcasecmp(key, "SET_EVENT_FILTER")) + { + *value = (void *)x11_set_event_filter; + return TRUE; + } + else if (!strcasecmp(key, "GET_HANDLE")) + { + *value = (void *)CWIDGET_get_handle; + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)QX11Info::appTime(); + return TRUE; + } + else if (!strcasecmp(key, "DECLARE_TRAYICON")) + { + *value = (void *)declare_tray_icon; + return TRUE; + } + else + return FALSE; +} +#endif + +/*#ifndef NO_X_WINDOW +extern Time qt_x_time; +#endif*/ + +static void activate_main_window(intptr_t value) +{ + CWINDOW *active; + + active = CWINDOW_Active; + if (!active) active = CWINDOW_LastActive; + + if (!active) + return; + + MyMainWindow *win = (MyMainWindow *)active->widget.widget; + if (win && !win->isWindow()) + win = (MyMainWindow *)win->window(); + if (win) + { + /*#ifndef NO_X_WINDOW + qt_x_time = CurrentTime; + #endif*/ + win->raise(); + win->activateWindow(); + } +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + if (!qApp) + return; + + switch(signal) + { + case GB_SIGNAL_DEBUG_BREAK: + release_grab(); + break; + + case GB_SIGNAL_DEBUG_FORWARD: + //while (qApp->activePopupWidget()) + // delete qApp->activePopupWidget(); +#ifndef QT5 + qApp->syncX(); +#endif + break; + + case GB_SIGNAL_DEBUG_CONTINUE: + GB.Post((GB_CALLBACK)activate_main_window, 0); + unrelease_grab(); + break; + } +} + +} // extern "C" + +/* class MyPostCheck */ + +bool MyPostCheck::in_check = false; + +void MyPostCheck::check(void) +{ + //qDebug("MyPostCheck::check"); + in_check = false; + GB.CheckPost(); +} + diff --git a/gb.qt4/src/main.h b/gb.qt4/src/main.h new file mode 100644 index 00000000..86595dbe --- /dev/null +++ b/gb.qt4/src/main.h @@ -0,0 +1,170 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" + +#define DO_NOT_USE_QT_INTERFACE +#include "gb.qt.h" +#include "gb.image.h" +#include "gb.geom.h" + +#ifndef __MAIN_CPP +extern "C" const GB_INTERFACE *GB_PTR; +extern "C" IMAGE_INTERFACE IMAGE; +extern "C" GEOM_INTERFACE GEOM; + +extern int MAIN_in_wait; +extern int MAIN_in_message_box; +extern int MAIN_loop_level; +extern int MAIN_scale; +extern bool MAIN_debug_busy; +extern bool MAIN_init; +extern bool MAIN_key_debug; +extern bool MAIN_right_to_left; +#ifndef NO_X_WINDOW +extern int MAIN_x11_last_key_code; +#define MAIN_platform "x11" +#else +#define MAIN_platform "" +#endif + +extern GB_CLASS CLASS_Control; +extern GB_CLASS CLASS_Container; +extern GB_CLASS CLASS_ContainerChildren; +extern GB_CLASS CLASS_UserControl; +extern GB_CLASS CLASS_UserContainer; +extern GB_CLASS CLASS_TabStrip; +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Menu; +extern GB_CLASS CLASS_Picture; +extern GB_CLASS CLASS_Drawing; +extern GB_CLASS CLASS_DrawingArea; +extern GB_CLASS CLASS_ScrollArea; +extern GB_CLASS CLASS_Printer; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_SvgImage; +extern GB_CLASS CLASS_TextArea; + +#endif + +#define GB (*GB_PTR) + +class MyPostCheck: public QObject +{ + Q_OBJECT + +public: + + static bool in_check; + +public slots: + + void check(void); +}; + +class MyApplication: public QApplication +{ + Q_OBJECT + +public: + + MyApplication(int &argc, char **argv); +#ifndef QT5 + virtual bool x11EventFilter(XEvent *e); +#endif + virtual bool eventFilter(QObject *o, QEvent *e); + //virtual bool notify(QObject *o, QEvent *e); + + static void setEventFilter(bool set); + + static bool isTooltipEnabled() { return !_tooltip_disable; } + static void setTooltipEnabled(bool b); + + static void initClipboard(); + + static QEventLoop *eventLoop; + +public slots: + + void linkDestroyed(QObject *); + void clipboardHasChanged(QClipboard::Mode); + void commitDataRequested(QSessionManager &); + +private: + static bool _tooltip_disable; + static int _event_filter; +}; + +class MyTimer : public QObject +{ +public: + + MyTimer(GB_TIMER *timer); + ~MyTimer(); + void clearTimer() { timer = 0; } + +protected: + + void timerEvent(QTimerEvent *); + +private: + + GB_TIMER *timer; + intptr_t id; +}; + + +#define UTF8_NBUF 4 + +void MAIN_check_quit(void); +void MAIN_update_scale(const QFont &); +void MAIN_process_events(void); +void MAIN_init_error(void); + +#define MAIN_CHECK_INIT() (MAIN_init ? 0 : (MAIN_init_error(), 1)) + +const char *QT_ToUtf8(const QString &str); +int QT_GetLastUtf8Length(); +char *QT_NewString(const QString &str); +void QT_ReturnNewString(const QString &str); +void QT_RegisterAction(void *object, const char *key, int on); +void QT_RaiseAction(const char *key); +void *QT_GetObject(QWidget *); +void QT_Link(QObject *, void *); +void *QT_GetLink(QObject *); +void QT_PreventQuit(bool inc); +QMenu *QT_FindMenu(void *parent, const char *name); + +#endif diff --git a/gb.qt4/src/opengl/CGLarea.cpp b/gb.qt4/src/opengl/CGLarea.cpp new file mode 100644 index 00000000..1385db30 --- /dev/null +++ b/gb.qt4/src/opengl/CGLarea.cpp @@ -0,0 +1,202 @@ +/*************************************************************************** + + CGLarea.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLAREA_CPP + +#include "gb.image.h" +#include "CGLarea.h" + +//#include "gl.h" +//#include + + +static int glWidgetCount = 0; +static QGLWidget *sharedWidget = 0; + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +static void makeSharing() +{ + if (sharedWidget) + return; + + sharedWidget = new QGLWidget(); + //qDebug("make_sharing: %p", sharedWidget); +} + +BEGIN_METHOD_VOID(CGLAREA_exit) + + //qDebug("CGLAREA_exit: %p", sharedWidget); + delete sharedWidget; + sharedWidget = 0; + +END_METHOD + +BEGIN_METHOD(CGLAREA_new, GB_OBJECT parent) + + if (!QGLFormat::hasOpenGL()) + { + GB.Error( "This system has no OpenGL support"); + return; + } + + makeSharing(); + GLarea *area = new GLarea(QT.GetContainer(VARG(parent)), THIS, sharedWidget); + glWidgetCount++; + + QT.InitWidget(area, _object, false); + area->show(); + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_free) + + glWidgetCount--; + + // clean up the shared datas between GLwidgets if there is no more CGLarea + if (glWidgetCount <= 0) + { + delete sharedWidget; + sharedWidget = 0; + makeSharing(); + glWidgetCount = 0; + } + + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_update) + + WIDGET->updateGL(); + +END_METHOD + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_select) + + WIDGET->makeCurrent(); + // really needed ? + GL.Init(); + +END_METHOD +#endif + +BEGIN_METHOD(CGLAREA_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + QString text; + int x, y; + #ifdef GL_LIGHTING + GLboolean _LIGHTING = glIsEnabled(GL_LIGHTING); + #endif + GLboolean _TEXTURE_2D = glIsEnabled(GL_TEXTURE_2D); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glDisable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glDisable(GL_TEXTURE_2D); + + text = QSTRING_ARG(text); + x = VARG(x); + y = VARG(y); + + WIDGET->renderText(x, y, text, WIDGET->font()); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glEnable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glEnable(GL_TEXTURE_2D); + +END_METHOD + +/**************************************************************************/ + +GB_DESC CGlareaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + GB_STATIC_METHOD("_exit", NULL, CGLAREA_exit, NULL), + + GB_METHOD("_new", NULL, CGLAREA_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CGLAREA_free, NULL), + //GB_METHOD("Update", NULL, CGLAREA_update, NULL), + GB_METHOD("Refresh", NULL, CGLAREA_update, NULL), + //GB_METHOD("Select", NULL, CGLAREA_select, NULL), + //GB_METHOD("Text", NULL, CGLAREA_text, "(Text)s(X)i(Y)i"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; + +/* class GLarea */ + +GLarea::GLarea(QWidget *parent,CGLAREA *object, QGLWidget *sharing): QGLWidget(parent, sharing) +{ + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); + _area = object; +}; + + +void GLarea::initializeGL() +{ + GL.Init(); + GB.Raise(_area, EVENT_Open, 0); +} + +void GLarea::paintGL() +{ + static bool CleanupOnFirstShow = 0; + + uint color; + if (!CleanupOnFirstShow) + { + // clear to avoid garbage + color = QT.GetBackgroundColor(_area); + if (color == GB_COLOR_DEFAULT) + color = 0; + + qglClearColor(QColor((QRgb)color)); + glClear(GL_COLOR_BUFFER_BIT); + + CleanupOnFirstShow = true; + } + + GB.Raise(_area, EVENT_Draw, 0); +} + +void GLarea::resizeGL(int w, int h) +{ + GB.Raise(_area, EVENT_Resize, 0); +} + diff --git a/gb.qt4/src/opengl/CGLarea.h b/gb.qt4/src/opengl/CGLarea.h new file mode 100644 index 00000000..e824042b --- /dev/null +++ b/gb.qt4/src/opengl/CGLarea.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CGLarea.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLAREA_H +#define __CGLAREA_H + +#include "main.h" + +#include + +typedef + struct { + QT_WIDGET widget; + } + CGLAREA; + +#ifndef __CGLAREA_CPP +extern GB_DESC CGlareaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((QT_WIDGET *)_object)->widget) + +#endif /* __CGLAREA_CPP */ + +class GLarea : public QGLWidget +{ +Q_OBJECT +public: + GLarea(QWidget *parent,CGLAREA *object, QGLWidget *sharing = 0); + ~GLarea() {}; +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int w, int h); +private: + CGLAREA *_area; +}; + +#endif diff --git a/gb.qt4/src/opengl/Makefile.am b/gb.qt4/src/opengl/Makefile.am new file mode 100644 index 00000000..6e9a597b --- /dev/null +++ b/gb.qt4/src/opengl/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.qt4.opengl +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt4.opengl.la + +gb_qt4_opengl_la_LIBADD = @QTOPENGL_LIB@ +gb_qt4_opengl_la_LDFLAGS = -module @LD_FLAGS@ @QTOPENGL_LDFLAGS@ +gb_qt4_opengl_la_CPPFLAGS = @QTOPENGL_INC@ + +gb_qt4_opengl_la_SOURCES = \ + main.h main.cpp \ + CGLarea.h CGLarea_moc.cpp CGLarea.cpp + diff --git a/gb.qt4/src/opengl/gb.qt4.opengl.component b/gb.qt4/src/opengl/gb.qt4.opengl.component new file mode 100644 index 00000000..8738f7c1 --- /dev/null +++ b/gb.qt4/src/opengl/gb.qt4.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Laurent Carlier,Benoît Minisini +Require=gb.qt4,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Stable diff --git a/gb.qt4/src/opengl/main.cpp b/gb.qt4/src/opengl/main.cpp new file mode 100644 index 00000000..b0aad7b7 --- /dev/null +++ b/gb.qt4/src/opengl/main.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + + main.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "main.h" +#include "CGLarea.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; +GL_INTERFACE GL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CGlareaDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface(QT_NAME, QT_INTERFACE_VERSION, &QT); + GB.GetInterface("gb.opengl", GL_INTERFACE_VERSION, &GL); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/gb.qt4/src/opengl/main.h b/gb.qt4/src/opengl/main.h new file mode 100644 index 00000000..26349161 --- /dev/null +++ b/gb.qt4/src/opengl/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.gl.h" + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern QT_INTERFACE QT; +extern GL_INTERFACE GL; +#endif + +#endif diff --git a/gb.qt4/src/trayicon.xpm b/gb.qt4/src/trayicon.xpm new file mode 100644 index 00000000..7a764cf4 --- /dev/null +++ b/gb.qt4/src/trayicon.xpm @@ -0,0 +1,163 @@ +/* XPM */ +static char * _default_trayicon[] = { +"24 24 136 2", +" c None", +". c #4167C6", +"+ c #446ECF", +"@ c #4975D8", +"# c #4E7DDF", +"$ c #5586E7", +"% c #314F9E", +"& c #2D4A91", +"* c #3153A9", +"= c #304F9B", +"- c #365AB7", +"; c #3C64C5", +"> c #446FD3", +", c #4D7DE1", +"' c #5A8EED", +") c #2D4371", +"! c #46628B", +"~ c #688CB7", +"{ c #5D7FAA", +"] c #668AB6", +"^ c #3B5CA7", +"/ c #3556A2", +"( c #365BB8", +"_ c #3F67C9", +": c #4977DC", +"< c #568AEB", +"[ c #33496F", +"} c #749CC7", +"| c #80AAD7", +"1 c #81ADD9", +"2 c #82AED9", +"3 c #81ACD8", +"4 c #7AA4D0", +"5 c #729BCA", +"6 c #3457AE", +"7 c #476AB0", +"8 c #577CB9", +"9 c #4870C1", +"0 c #5486E6", +"a c #7BA4D0", +"b c #7DA8D5", +"c c #7AA5D3", +"d c #80ABD8", +"e c #82AEDA", +"f c #83AFDA", +"g c #7EA8D6", +"h c #668CC1", +"i c #7EA9D5", +"j c #81ADD8", +"k c #8CB5DC", +"l c #9FBDDC", +"m c #BBD7ED", +"n c #9AAEC3", +"o c #79A2D1", +"p c #77A1D1", +"q c #5278BB", +"r c #3D5DA1", +"s c #6F97C8", +"t c #587DBA", +"u c #7EA9D6", +"v c #82ADDA", +"w c #81ACD9", +"x c #7FAAD6", +"y c #6F91B9", +"z c #6A85A0", +"A c #9DC4E5", +"B c #849AA4", +"C c #1E1F20", +"D c #6189C5", +"E c #335195", +"F c #395BA9", +"G c #4667A2", +"H c #4E72B6", +"I c #2E4E9F", +"J c #729ACA", +"K c #7FAAD7", +"L c #83AFDB", +"M c #7CA6D3", +"N c #202B38", +"O c #020202", +"P c #495B6D", +"Q c #C4E6FB", +"R c #718186", +"S c #4A6EB5", +"T c #3E5E9E", +"U c #587DBC", +"V c #78A1CF", +"W c #466AB6", +"X c #5F84BB", +"Y c #78A0CE", +"Z c #7FA9D6", +"` c #24313F", +" . c #324256", +".. c #97C1E6", +"+. c #C7E9FC", +"@. c #2F4E98", +"#. c #719ACA", +"$. c #6990C2", +"%. c #81ABD6", +"&. c #78A0CA", +"*. c #5E7DA0", +"=. c #77A0CB", +"-. c #80ABD7", +";. c #93BEE4", +">. c #C4E5FA", +",. c #4B6DA9", +"'. c #6F97C5", +"). c #84AFDA", +"!. c #82ADD9", +"~. c #8AB5DE", +"{. c #B0D6F1", +"]. c #719ACC", +"^. c #7197C3", +"/. c #85B0DB", +"(. c #84B0DB", +"_. c #91BCE2", +":. c #638DD3", +"<. c #799DDB", +"[. c #4A6EC2", +"}. c #718AB8", +"|. c #8DA6CA", +"1. c #92ACD1", +"2. c #C4D9EE", +"3. c #AEC6E7", +"4. c #38539B", +"5. c #405A99", +"6. c #7D95BC", +"7. c #9EB6D7", +"8. c #C3D8EE", +"9. c #5771AD", +"0. c #25418F", +"a. c #4863A1", +"b. c #C5D9EF", +"c. c #6384CA", +"d. c #3D65C5", +"e. c #395AB0", +" ", +" ", +" . + @ # $ ", +" % & * = - ; > , ' ", +" ) ! ~ { ] ^ / ( _ : < ", +" [ } | 1 2 3 4 5 6 7 8 9 0 ", +" a b c d 1 e f g h i j k l m n ", +" o p q r s t u v w f v x y z A B C ", +" D E F G H I J K f L e M N O P Q R ", +" S T U V W X Y u f L f Z ` O ...+. ", +" @.#.$. %.e L L e &.*.=.-.;.>. ", +" ,.'. ).L L f e e e !.~.{. ", +" ].^. /.(.L L f f f f _. ", +" b ", +" -. ", +" ", +" :. ", +" <.[.}.|.1. ", +" 2.3.4.5.6.7. ", +" 8.9.0.a. ", +" b.c.d.e. ", +" ", +" ", +" "}; diff --git a/gb.qt4/src/webkit/Makefile.am b/gb.qt4/src/webkit/Makefile.am new file mode 100644 index 00000000..4eac9e0f --- /dev/null +++ b/gb.qt4/src/webkit/Makefile.am @@ -0,0 +1,19 @@ +COMPONENT = gb.qt4.webkit +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt4.webkit.la + +gb_qt4_webkit_la_LIBADD = @QTWEBKIT_LIB@ +gb_qt4_webkit_la_LDFLAGS = -module @LD_FLAGS@ @QTWEBKIT_LDFLAGS@ +gb_qt4_webkit_la_CPPFLAGS = @QTWEBKIT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt4_webkit_la_SOURCES = \ + main.h main.cpp \ + cwebsettings.h cwebsettings.cpp \ + cwebframe.h cwebframe.cpp cwebframe_moc.cpp \ + cwebelement.h cwebelement.cpp \ + cwebview.h cwebview.cpp cwebview_moc.cpp \ + ccookiejar.h ccookiejar.cpp ccookiejar_moc.cpp \ + cwebhittest.h cwebhittest.cpp \ + cwebdownload.h cwebdownload.cpp cwebdownload_moc.cpp diff --git a/gb.qt4/src/webkit/ccookiejar.cpp b/gb.qt4/src/webkit/ccookiejar.cpp new file mode 100644 index 00000000..374fd77d --- /dev/null +++ b/gb.qt4/src/webkit/ccookiejar.cpp @@ -0,0 +1,188 @@ +/*************************************************************************** + + ccookiejar.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOOKIEJAR_CPP + +#include "ccookiejar.h" + +#include + +BEGIN_PROPERTY(Cookie_Domain) + + if (READ_PROPERTY) + RETURN_NEW_STRING(COOKIE->domain()); + else + COOKIE->setDomain(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_ExpirationDate) + + GB_DATE date; + GB_DATE_SERIAL ds; + QDateTime d; + + if (READ_PROPERTY) + { + if (COOKIE->isSessionCookie()) + { + GB.ReturnDate(NULL); + return; + } + + d = COOKIE->expirationDate(); + + ds.year = d.date().year(); + ds.month = d.date().month(); + ds.day = d.date().day(); + ds.hour = d.time().hour(); + ds.min = d.time().minute(); + ds.sec = d.time().second(); + ds.msec = d.time().msec(); + + GB.MakeDate(&ds, &date); + GB.ReturnDate(&date); + } + else + { + ds = *GB.SplitDate(PROP(GB_DATE)); + d = QDateTime(QDate(ds.year, ds.month, ds.day), QTime(ds.hour, ds.min, ds.sec, ds.msec)); + COOKIE->setExpirationDate(d); + } + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_HttpOnly) + + if (READ_PROPERTY) + GB.ReturnBoolean(COOKIE->isHttpOnly()); + else + COOKIE->setHttpOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Secure) + + if (READ_PROPERTY) + GB.ReturnBoolean(COOKIE->isSecure()); + else + COOKIE->setSecure(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Session) + + if (READ_PROPERTY) + GB.ReturnBoolean(COOKIE->isSessionCookie()); + else + { + QDateTime d; + COOKIE->setExpirationDate(d); + } + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Name) + + if (READ_PROPERTY) + { + QByteArray ba = COOKIE->name(); + GB.ReturnNewString((const char *)ba, ba.size()); + } + else + { + COOKIE->setName(QByteArray(PSTRING(), PLENGTH())); + } + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(COOKIE->path()); + else + COOKIE->setPath(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Value) + + if (READ_PROPERTY) + { + QByteArray ba = COOKIE->value(); + GB.ReturnNewString((const char *)ba, ba.size()); + } + else + { + COOKIE->setValue(QByteArray(PSTRING(), PLENGTH())); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(Cookie_new) + + COOKIE = new QNetworkCookie; + +END_METHOD + +BEGIN_METHOD_VOID(Cookie_free) + + delete COOKIE; + +END_METHOD + +GB_DESC CookieDesc[] = +{ + GB_DECLARE("Cookie", sizeof(CCOOKIE)), + + GB_METHOD("_new", NULL, Cookie_new, NULL), + GB_METHOD("_free", NULL, Cookie_free, NULL), + + GB_PROPERTY("Domain", "s", Cookie_Domain), + GB_PROPERTY("ExpirationDate", "d", Cookie_ExpirationDate), + GB_PROPERTY("HttpOnly", "b", Cookie_HttpOnly), + GB_PROPERTY("Secure", "b", Cookie_Secure), + GB_PROPERTY("Session", "b", Cookie_Session), + GB_PROPERTY("Name", "s", Cookie_Name), + GB_PROPERTY("Path", "s", Cookie_Path), + GB_PROPERTY("Value", "s", Cookie_Value), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +CCOOKIE *WEB_create_cookie(const QNetworkCookie &cookie) +{ + CCOOKIE *_object = (CCOOKIE *)GB.New(GB.FindClass("Cookie"), NULL, NULL); + *(THIS_COOKIE->cookie) = cookie; + + return THIS_COOKIE; +} + +/***************************************************************************/ + +MyCookieJar::MyCookieJar(QObject *parent) : QNetworkCookieJar(parent) +{ +} + diff --git a/gb.qt4/src/webkit/ccookiejar.h b/gb.qt4/src/webkit/ccookiejar.h new file mode 100644 index 00000000..c4908ac2 --- /dev/null +++ b/gb.qt4/src/webkit/ccookiejar.h @@ -0,0 +1,77 @@ +/*************************************************************************** + + ccookiejar.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOOKIEJAR_H +#define __CCOOKIEJAR_H + +#include +#include +#include + +#include "main.h" + +#ifndef __CCOOKIEJAR_CPP + +extern GB_DESC CookieDesc[]; +//extern GB_DESC CCookieJarDesc[]; + +#else + +#define THIS_COOKIE ((CCOOKIE *)_object) +#define COOKIE (THIS_COOKIE->cookie) + +#endif + +class MyCookieJar : public QNetworkCookieJar +{ + Q_OBJECT + +public: + + MyCookieJar(QObject *parent = 0); + + QList allCookies () const { return QNetworkCookieJar::allCookies(); } + void setAllCookies(const QList &cookieList) { QNetworkCookieJar::setAllCookies(cookieList); } + + //virtual QList cookiesForUrl(const QUrl & url) const; + //virtual bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); +}; + +typedef + struct + { + GB_BASE ob; + MyCookieJar *jar; + } + CCOOKIEJAR; + +typedef + struct { + GB_BASE ob; + QNetworkCookie *cookie; + } + CCOOKIE; + +CCOOKIE *WEB_create_cookie(const QNetworkCookie &cookie); + +#endif diff --git a/gb.qt4/src/webkit/control/webview.png b/gb.qt4/src/webkit/control/webview.png new file mode 100644 index 0000000000000000000000000000000000000000..7b9ef3e26d3d764a819d70db6792a28351728d25 GIT binary patch literal 1160 zcmV;31b6$1P)31nLr6`z z@x}`iyfH?U3sEEiAt*&4K;^3kX;0hJ!fAWDyEEg(E|k_DI8-c7@?fr?vY| zATgUoJ)@R>A3_Vo2~7}cmenR$nsxp=0^?vN@SD>VYmKvA0 zmZaI-mLXZCwpG1$d<6Df);v)I>lX-twwx;ZytucIFV2oq<2ax#m#&9=(p_TR-Xceb z!h1vDmxAT5F@pzO7`6BszC2svtDZ5+A?!X~qOYXEC@=y>aiwH<^T!h3^alSf1-}m& z8XTd?6@sKt^bH^WJiw8Hj}^fxg;+od+}evUVGO%YP4IBWp}ns1J}W*M3!zj7Z7sgG zT=z8}9U9 zf4Iu=0Ur!t1+&%`R~Qx3jn3QGQ6oB^W^doLOe*Fs)b2b zE==QM5CR`XYQR;3%%RFII`z#0~6Y%*}YCEHj~4wj9grs8Gf51a@{L@owW&Zu>c2(Wuzbks>LU{XHRjx&!=>8PR`z>h6-7{X7v0&SRb|Om zSVYK9{Z;GTvlS3AE+g6H%F4vOuxvX^(f-cz9Ksy{bK-mw;_+Jpg{~HFRIm z6vr*DE9`tN&GIIPwpqCRS-Ijh&5AY6_r}rR_y2 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBDOWNLOAD_CPP + +#include + +#include "cwebdownload.h" + +/***************************************************************************/ + +static CWEBDOWNLOAD *get_download(QNetworkReply *reply) +{ + CWEBDOWNLOAD *download = 0; + sscanf(TO_UTF8(reply->objectName()), "gb-download-%p", &download); + return download; +} + +static void set_error(CWEBDOWNLOAD *_object, const char *error) +{ + GB.FreeString(&THIS->error); + if (error) THIS->error = GB.NewZeroString(error); +} + +static void abort_download(CWEBDOWNLOAD *_object, const char *error) +{ + REPLY->abort(); + if (error) + { + set_error(THIS, error); + THIS->status = STATUS_ERROR; + } +} + +/***************************************************************************/ + +static CWEBDOWNLOAD **_downloads = NULL; + +int WEB_get_downloads_count() +{ + if (_downloads) + return GB.Count(_downloads); + else + return 0; +} + +#define get_downloads_count WEB_get_downloads_count + +static int find_download(CWEBDOWNLOAD *download) +{ + int i; + + for (i = 0; i < get_downloads_count(); i++) + { + if (_downloads[i] == download) + return i; + } + + return (-1); +} + +void WEB_remove_download(CWEBDOWNLOAD *_object) +{ + int index; + + abort_download(THIS, NULL); + + index = find_download(THIS); + + if (index >= 0) + { + GB.Unref(POINTER(&_downloads[index])); + GB.Remove(&_downloads, index, 1); + } +} + +/***************************************************************************/ + +BEGIN_PROPERTY(WebDownload_Status) + + GB.ReturnInteger(THIS->status); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Url) + + RETURN_NEW_STRING(REPLY->url().toString()); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Path) + + if (READ_PROPERTY) + GB.ReturnString(THIS->path); + else + GB.StoreString(PROP(GB_STRING), &THIS->path); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Size) + + qulonglong size = 0; + + size = REPLY->header(QNetworkRequest::ContentLengthHeader).toULongLong(); + + GB.ReturnLong(size); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_ErrorText) + + GB.ReturnString(THIS->error); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Progress) + + GB.ReturnFloat(THIS->progress); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebDownload_Cancel) + + abort_download(THIS, NULL); + +END_METHOD + +BEGIN_METHOD_VOID(WebDownload_Delete) + + WEB_remove_download(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(WebDownload_free) + + if (THIS->reply) + THIS->reply->abort(); + + delete THIS->output; + THIS->reply->deleteLater(); + set_error(THIS, NULL); + GB.FreeString(&THIS->path); + +END_METHOD + +GB_DESC WebDownloadDesc[] = +{ + GB_DECLARE("WebDownload", sizeof(CWEBDOWNLOAD)), + + GB_METHOD("_free", NULL, WebDownload_free, NULL), + + GB_CONSTANT("Created", "i", STATUS_CREATED), + GB_CONSTANT("Downloading", "i", STATUS_DOWNLOADING), + GB_CONSTANT("Finished", "i", STATUS_FINISHED), + GB_CONSTANT("Cancelled", "i", STATUS_CANCELLED), + GB_CONSTANT("Error", "i", STATUS_ERROR), + + GB_PROPERTY("Path", "s", WebDownload_Path), + GB_PROPERTY_READ("Url", "s", WebDownload_Url), + GB_PROPERTY_READ("Size", "l", WebDownload_Size), + GB_PROPERTY_READ("Progress", "f", WebDownload_Progress), + GB_PROPERTY_READ("Status", "i", WebDownload_Status), + GB_PROPERTY_READ("ErrorText", "s", WebDownload_ErrorText), + + GB_METHOD("Cancel", NULL, WebDownload_Cancel, NULL), + GB_METHOD("Delete", NULL, WebDownload_Delete, NULL), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +BEGIN_PROPERTY(WebDownloads_Count) + + GB.ReturnInteger(get_downloads_count()); + +END_PROPERTY + +BEGIN_METHOD(WebDownloads_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= get_downloads_count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(_downloads[index]); + +END_METHOD + +BEGIN_METHOD(WebDownloads_Remove, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= get_downloads_count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + WEB_remove_download(_downloads[index]); + +END_METHOD + +BEGIN_METHOD(WebDownloads_Find, GB_OBJECT download) + + CWEBDOWNLOAD *download = (CWEBDOWNLOAD *)VARG(download); + + GB.ReturnInteger(find_download(download)); + +END_METHOD + +BEGIN_METHOD_VOID(WebDownloads_exit) + + while (get_downloads_count()) + WEB_remove_download(_downloads[0]); + + GB.FreeArray(&_downloads); + +END_METHOD + +GB_DESC WebDownloadsDesc[] = +{ + GB_DECLARE("WebDownloads", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, WebDownloads_exit, NULL), + GB_STATIC_PROPERTY_READ("Count", "i", WebDownloads_Count), + GB_STATIC_METHOD("_get", "WebDownload", WebDownloads_get, "(Index)i"), + GB_STATIC_METHOD("Remove", NULL, WebDownloads_Remove, "(Index)i"), + GB_STATIC_METHOD("Find", "i", WebDownloads_Find, "(Download)WebDownload"), + + GB_END_DECLARE +}; + + +/***************************************************************************/ + +CWEBDOWNLOAD *WEB_create_download(QNetworkReply *reply) +{ + CWEBDOWNLOAD *_object; + char name[32]; + int index; + + _object = (CWEBDOWNLOAD *)GB.New(GB.FindClass("WebDownload"), NULL, NULL); + THIS->reply = reply; + reply->setParent(0); + sprintf(name, "gb-download-%p", THIS); + reply->setObjectName(name); + + QObject::connect(reply, SIGNAL(readyRead()), &CWebDownload::manager, SLOT(readyRead())); + QObject::connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &CWebDownload::manager, SLOT(error(QNetworkReply::NetworkError))); + QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), &CWebDownload::manager, SLOT(downloadProgress(qint64,qint64))); + QObject::connect(reply, SIGNAL(finished()), &CWebDownload::manager, SLOT(finished())); + + index = get_downloads_count(); + + if (!_downloads) + GB.NewArray(&_downloads, sizeof(*_downloads), 1); + else + GB.Add(&_downloads); + + _downloads[index] = THIS; + GB.Ref(THIS); + + return THIS; +} + +/***************************************************************************/ + +CWebDownload CWebDownload::manager; + +#define GET_DOWNLOAD() \ + QNetworkReply *reply = (QNetworkReply *)sender(); \ + CWEBDOWNLOAD *_object = get_download(reply); + +void CWebDownload::readyRead() +{ + GET_DOWNLOAD(); + + if (!THIS->path) + { + //abort_download(THIS, "No file name specified"); + return; + } + + if (!THIS->output) + { + THIS->output = new QFile(TO_QSTRING(THIS->path)); + + if (!THIS->output->open(QIODevice::WriteOnly)) + { + char *error = NULL; + error = GB.AddString(error, "Unable to save file: ", 0); + error = GB.AddString(error, TO_UTF8(THIS->output->errorString()), 0); + abort_download(THIS, error); + return; + } + } + + if (THIS->output->write(reply->readAll()) < 0) + { + abort_download(THIS, TO_UTF8(THIS->output->errorString())); + return; + } + + THIS->status = STATUS_DOWNLOADING; +} + +void CWebDownload::error(QNetworkReply::NetworkError code) +{ + GET_DOWNLOAD(); + + if (code == QNetworkReply::OperationCanceledError) + { + THIS->status = STATUS_CANCELLED; + GB.FreeString(&THIS->error); + } + else + { + THIS->status = STATUS_ERROR; + if (!THIS->error) + THIS->error = GB.NewZeroString(TO_UTF8(reply->errorString())); + } +} + +void CWebDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + GET_DOWNLOAD(); + + if (bytesTotal < 0) + THIS->progress = 0; + else + THIS->progress = (double)bytesReceived / bytesTotal; +} + +void CWebDownload::finished() +{ + GET_DOWNLOAD(); + + if (THIS->status == STATUS_DOWNLOADING) + { + THIS->output->close(); + THIS->status = STATUS_FINISHED; + } + + THIS->progress = 1; +} diff --git a/gb.qt4/src/webkit/cwebdownload.h b/gb.qt4/src/webkit/cwebdownload.h new file mode 100644 index 00000000..13689c6a --- /dev/null +++ b/gb.qt4/src/webkit/cwebdownload.h @@ -0,0 +1,89 @@ +/*************************************************************************** + + cwebdownload.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBDOWNLOAD_H +#define __CWEBDOWNLOAD_H + +#include +#include + +#include "main.h" + +#ifndef __CWEBDOWNLOAD_CPP + +extern GB_DESC WebDownloadDesc[]; +extern GB_DESC WebDownloadsDesc[]; + +#else + +#define THIS ((CWEBDOWNLOAD *)_object) +#define REPLY (THIS->reply) + +#endif + +enum { + STATUS_CREATED, + STATUS_DOWNLOADING, + STATUS_ERROR, + STATUS_CANCELLED, + STATUS_FINISHED +}; + +typedef + struct + { + GB_BASE ob; + QNetworkReply *reply; + int status; + char *path; + char *error; + double progress; + QFile *output; + } + CWEBDOWNLOAD; + +class CWebDownload : public QObject +{ + Q_OBJECT + +public: + + static CWebDownload manager; + +public slots: + + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void error(QNetworkReply::NetworkError code); + void finished(); + void readyRead(); + //void metaDataChanged () + //void sslErrors ( const QList & errors ) + //void uploadProgress(qint64 bytesSent, qint64 bytesTotal); +}; + + +CWEBDOWNLOAD *WEB_create_download(QNetworkReply *reply); +int WEB_get_downloads_count(); +void WEB_remove_download(CWEBDOWNLOAD *_object); + +#endif diff --git a/gb.qt4/src/webkit/cwebelement.cpp b/gb.qt4/src/webkit/cwebelement.cpp new file mode 100644 index 00000000..c740bf23 --- /dev/null +++ b/gb.qt4/src/webkit/cwebelement.cpp @@ -0,0 +1,399 @@ +/*************************************************************************** + + cwebelement.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBELEMENT_CPP + +#include "cwebframe.h" +#include "cwebelement.h" + +typedef + struct { + GB_BASE ob; + int x, y, w, h; + } + CRECT; + +CWEBELEMENT *CWEBELEMENT_create(const QWebElement &elt) +{ + void *_object; + + if (elt.isNull()) + return NULL; + + _object = GB.New(GB.FindClass("WebElement"), 0, 0); + ELT = new QWebElement(elt); + //qDebug("create WebElement %p / %p (%ld)", THIS, ELT, THIS->ob.ref); + return THIS; +} + +static int check_element(void *_object) +{ + return !ELT || ELT->isNull(); +} + +//--------------------------------------------------------------------------- + +/*BEGIN_METHOD_VOID(WebElement_new) + + ELT = new QWebElement; + +END_METHOD*/ + +BEGIN_METHOD_VOID(WebElement_free) + + //qDebug("WebElement_free: %p / %p", THIS, ELT); + delete ELT; + +END_METHOD + +BEGIN_PROPERTY(WebElement_HTML) + + if (READ_PROPERTY) + RETURN_NEW_STRING(ELT->toOuterXml()); + else + ELT->setOuterXml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_InnerHTML) + + if (READ_PROPERTY) + RETURN_NEW_STRING(ELT->toInnerXml()); + else + ELT->setInnerXml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Frame) + + GB.ReturnObject(CWEBFRAME_get(ELT->webFrame())); + +END_PROPERTY + +BEGIN_METHOD(WebElement_Eval, GB_STRING javascript) + + QVariant result = ELT->evaluateJavaScript(QSTRING_ARG(javascript)); + MAIN_return_qvariant(result); + +END_METHOD + +BEGIN_PROPERTY(WebElement_Child) + + GB.ReturnObject(CWEBELEMENT_create(ELT->firstChild())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Last) + + GB.ReturnObject(CWEBELEMENT_create(ELT->lastChild())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Next) + + GB.ReturnObject(CWEBELEMENT_create(ELT->nextSibling())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Previous) + + GB.ReturnObject(CWEBELEMENT_create(ELT->previousSibling())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Parent) + + GB.ReturnObject(CWEBELEMENT_create(ELT->parent())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Document) + + GB.ReturnObject(CWEBELEMENT_create(ELT->document())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_HasFocus) + + GB.ReturnBoolean(ELT->hasFocus()); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebElement_SetFocus) + + ELT->setFocus(); + +END_METHOD + +BEGIN_METHOD(WebElement_FindFirst, GB_STRING selector) + + GB.ReturnObject(CWEBELEMENT_create(ELT->findFirst(QSTRING_ARG(selector)))); + +END_METHOD + +BEGIN_METHOD(WebElement_FindAll, GB_STRING selector) + + GB_ARRAY array; + int i; + QWebElementCollection result = ELT->findAll(QSTRING_ARG(selector)); + CWEBELEMENT *elt; + + GB.Array.New(&array, GB.FindClass("WebElement"), result.count()); + + for (i = 0; i < result.count(); i++) + { + elt = CWEBELEMENT_create(result.at(i)); + GB.Ref(elt); + *((CWEBELEMENT **)GB.Array.Get(array, i)) = elt; + } + + GB.ReturnObject(array); + +END_METHOD + +BEGIN_PROPERTY(WebElement_Tag) + + RETURN_NEW_STRING(ELT->tagName()); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Classes) + + GB_ARRAY array; + int i; + QStringList classes = ELT->classes(); + + GB.Array.New(&array, GB_T_STRING, classes.count()); + for (i = 0; i < classes.count(); i++) + *((const char **)GB.Array.Get(array, i)) = NEW_STRING(classes.at(i)); + + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_METHOD(WebElement_AddClass, GB_STRING klass) + + ELT->addClass(QSTRING_ARG(klass)); + +END_METHOD + +BEGIN_METHOD(WebElement_RemoveClass, GB_STRING klass) + + ELT->removeClass(QSTRING_ARG(klass)); + +END_METHOD + +BEGIN_METHOD(WebElement_get, GB_STRING attr) + + RETURN_NEW_STRING(ELT->attribute(QSTRING_ARG(attr))); + +END_METHOD + +BEGIN_METHOD(WebElement_put, GB_STRING value; GB_STRING attr) + + ELT->setAttribute(QSTRING_ARG(attr), QSTRING_ARG(value)); + +END_METHOD + +BEGIN_METHOD(WebElement_HasAttribute, GB_STRING attr) + + GB.ReturnBoolean(ELT->hasAttribute(QSTRING_ARG(attr))); + +END_METHOD + +BEGIN_METHOD(WebElement_RemoveAttribute, GB_STRING attr) + + ELT->removeAttribute(QSTRING_ARG(attr)); + +END_METHOD + +BEGIN_PROPERTY(WebElement_Geometry) + + QRect r = ELT->geometry(); + + GB.Push(4, GB_T_INTEGER, r.x(), GB_T_INTEGER, r.y(), GB_T_INTEGER, r.width(), GB_T_INTEGER, r.height()); + GB.ReturnObject(GB.New(GB.FindClass("Rect"), 0, (void *)4)); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebElement_Delete) + + ELT->removeFromDocument(); + +END_METHOD + +BEGIN_METHOD(WebElement_Add, GB_STRING markup) + + ELT->appendInside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_AddFirst, GB_STRING markup) + + ELT->prependInside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_AddBefore, GB_STRING markup) + + ELT->prependOutside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_AddAfter, GB_STRING markup) + + ELT->appendOutside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_Enclose, GB_STRING markup) + + ELT->encloseContentsWith(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_Replace, GB_STRING markup) + + ELT->replace(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD_VOID(WebElement_Clear) + + ELT->removeAllChildren(); + +END_METHOD + +BEGIN_METHOD(WebElement_Paint, GB_OBJECT clip) + + QPainter *painter = QT.GetCurrentPainter(); + + if (!painter) + return; + + #if QT_VERSION >= 0x040800 + + if (MISSING(clip)) + ELT->render(painter); + else + { + CRECT *rect = (CRECT *)VARG(clip); + QRect clip(rect->x, rect->y, rect->w, rect->h); + ELT->render(painter, clip); + } + + #else + + ELT->render(painter); + + #endif + +END_METHOD + + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(WebElementStyle_get, GB_STRING property) + + RETURN_NEW_STRING(ELT->styleProperty(QSTRING_ARG(property), QWebElement::InlineStyle)); + +END_METHOD + +BEGIN_METHOD(WebElementStyle_put, GB_STRING value; GB_STRING property) + + ELT->setStyleProperty(QSTRING_ARG(property), QSTRING_ARG(value)); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC WebElementStyleDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebElement.Style"), + GB_HOOK_CHECK(check_element), + + GB_METHOD("_get", "s", WebElementStyle_get, "(Property)s"), + GB_METHOD("_put", NULL, WebElementStyle_put, "(Value)s(Property)s"), + + GB_END_DECLARE +}; + +GB_DESC WebElementDesc[] = +{ + GB_DECLARE("WebElement", sizeof(CWEBELEMENT)), + GB_HOOK_CHECK(check_element), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, WebElement_new, NULL), + GB_METHOD("_free", NULL, WebElement_free, NULL), + + GB_PROPERTY("HTML", "s", WebElement_HTML), + GB_PROPERTY("InnerHTML", "s", WebElement_InnerHTML), + GB_PROPERTY_READ("Frame", "WebFrame", WebElement_Frame), + + GB_PROPERTY_READ("Tag", "s", WebElement_Tag), + + GB_PROPERTY_READ("Classes", "String[]", WebElement_Classes), + GB_METHOD("AddClass", NULL, WebElement_AddClass, "(Class)s"), + GB_METHOD("RemoveClass", NULL, WebElement_RemoveClass, "(Class)s"), + + GB_PROPERTY_READ("Child", "WebElement", WebElement_Child), + GB_PROPERTY_READ("First", "WebElement", WebElement_Child), + GB_PROPERTY_READ("Last", "WebElement", WebElement_Last), + GB_PROPERTY_READ("Next", "WebElement", WebElement_Next), + GB_PROPERTY_READ("Previous", "WebElement", WebElement_Previous), + GB_PROPERTY_READ("Parent", "WebElement", WebElement_Parent), + GB_PROPERTY_READ("Document", "WebElement", WebElement_Document), + + GB_PROPERTY_READ("HasFocus", "b", WebElement_HasFocus), + GB_METHOD("SetFocus", NULL, WebElement_SetFocus, NULL), + + GB_METHOD("Eval", "v", WebElement_Eval, "(JavaScript)s"), + + GB_METHOD("FindFirst", "WebElement", WebElement_FindFirst, "(Selector)s"), + GB_METHOD("FindAll", "WebElement[]", WebElement_FindAll, "(Selector)s"), + + GB_METHOD("_get", "s", WebElement_get, "(Attribute)s"), + GB_METHOD("_put", NULL, WebElement_put, "(Value)s(Attribute)s"), + GB_METHOD("HasAttribute", "b", WebElement_HasAttribute, "(Attribute)s"), + GB_METHOD("RemoveAttribute", NULL, WebElement_RemoveAttribute, "(Attribute)s"), + + GB_PROPERTY_READ("Geometry", "Rect", WebElement_Geometry), + + GB_METHOD("Delete", NULL, WebElement_Delete, NULL), + GB_METHOD("Clear", NULL, WebElement_Clear, NULL), + + GB_METHOD("Add", NULL, WebElement_Add, "(Markup)s"), + GB_METHOD("AddFirst", NULL, WebElement_AddFirst, "(Markup)s"), + GB_METHOD("AddBefore", NULL, WebElement_AddBefore, "(Markup)s"), + GB_METHOD("AddAfter", NULL, WebElement_AddAfter, "(Markup)s"), + GB_METHOD("Enclose", NULL, WebElement_Enclose, "(Markup)s"), + GB_METHOD("Replace", NULL, WebElement_Replace, "(Markup)s"), + + GB_METHOD("Paint", NULL, WebElement_Paint, "[(Clip)Rect;]"), + + GB_PROPERTY_SELF("Style", ".WebElement.Style"), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/webkit/cwebelement.h b/gb.qt4/src/webkit/cwebelement.h new file mode 100644 index 00000000..7b5139dc --- /dev/null +++ b/gb.qt4/src/webkit/cwebelement.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + cwebelement.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBELEMENT_H +#define __CWEBELEMENT_H + +#include "main.h" +#include + +#ifndef __CWEBELEMENT_CPP + +extern GB_DESC WebElementStyleDesc[]; +extern GB_DESC WebElementDesc[]; + +#else + +#define THIS ((CWEBELEMENT *)_object) +#define ELT THIS->elt + +#endif + +typedef + struct + { + GB_BASE ob; + QWebElement *elt; + } + CWEBELEMENT; + +CWEBELEMENT *CWEBELEMENT_create(const QWebElement &elt); + +#endif diff --git a/gb.qt4/src/webkit/cwebframe.cpp b/gb.qt4/src/webkit/cwebframe.cpp new file mode 100644 index 00000000..430bfabc --- /dev/null +++ b/gb.qt4/src/webkit/cwebframe.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + + cwebframe.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBFRAME_CPP + +#include +#include +#include +#include +#include + +#include "cwebelement.h" +#include "cwebframe.h" +#include "../cprinter.h" + +CWEBFRAME *CWEBFRAME_get(QWebFrame *frame) +{ + void *_object; + + if (!frame) return 0; + + _object = QT.GetLink(frame); + + if (!_object) + { + _object = GB.New(GB.FindClass("WebFrame"), 0, 0); + //qDebug("create WebFrame %p", _object); + QT.Link(frame, _object); + THIS->frame = frame; + } + + return (CWEBFRAME *)_object; +} + +void CWEBFRAME_eval(QWebFrame *frame, const QString &javascript) +{ + QVariant result = frame->evaluateJavaScript(javascript); + MAIN_return_qvariant(result); +} + +BEGIN_METHOD_VOID(WebFrame_free) + + //qDebug("WebFrame_free: %p", THIS); + +END_METHOD + +BEGIN_PROPERTY(WebFrame_Name) + + RETURN_NEW_STRING(FRAME->frameName()); + +END_PROPERTY + +BEGIN_PROPERTY(WebFrame_Parent) + + GB.ReturnObject(CWEBFRAME_get(FRAME->parentFrame())); + +END_PROPERTY + +BEGIN_PROPERTY(WebFrame_Url) + + if (READ_PROPERTY) + RETURN_NEW_STRING(FRAME->url().toString()); + else + FRAME->setUrl(QUrl(QSTRING_PROP())); + +END_PROPERTY + + +BEGIN_PROPERTY(WebFrameChildren_Count) + + GB.ReturnInteger(FRAME->childFrames().count()); + +END_PROPERTY + +BEGIN_METHOD(WebFrameChildren_get, GB_INTEGER index) + + int index = VARG(index); + QList children = FRAME->childFrames(); + + if (index < 0 || index >= children.count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CWEBFRAME_get(children.at(index))); + +END_METHOD + +BEGIN_METHOD(WebFrame_Print, GB_OBJECT printer) + + CPRINTER *printer = (CPRINTER *)VARG(printer); + + if (GB.CheckObject(printer)) + return; + + FRAME->print(printer->printer); + +END_METHOD + +BEGIN_PROPERTY(WebFrame_HTML) + + RETURN_NEW_STRING(FRAME->toHtml()); + +END_PROPERTY + +BEGIN_PROPERTY(WebFrame_Text) + + RETURN_NEW_STRING(FRAME->toPlainText()); + +END_PROPERTY + +BEGIN_METHOD(WebFrame_EvalJavaScript, GB_STRING javascript) + + CWEBFRAME_eval(FRAME, QSTRING_ARG(javascript)); + +END_METHOD + +BEGIN_PROPERTY(WebFrame_Document) + + GB.ReturnObject(CWEBELEMENT_create(FRAME->documentElement())); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC WebFrameChildrenDesc[] = +{ + GB_DECLARE(".WebFrame.Children", sizeof(CWEBFRAME)), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", WebFrameChildren_Count), + GB_METHOD("_get", "WebFrame", WebFrameChildren_get, "(Index)i"), + + GB_END_DECLARE +}; + +GB_DESC WebFrameDesc[] = +{ + GB_DECLARE("WebFrame", sizeof(CWEBFRAME)), + + GB_METHOD("_free", NULL, WebFrame_free, NULL), + + GB_PROPERTY_READ("Name", "s", WebFrame_Name), + GB_PROPERTY_SELF("Children", ".WebFrame.Children"), + GB_PROPERTY_READ("Parent", "WebFrame", WebFrame_Parent), + GB_PROPERTY("Url", "s", WebFrame_Url), + GB_METHOD("Print", NULL, WebFrame_Print, "(Printer)Printer;"), + GB_PROPERTY_READ("HTML", "s", WebFrame_HTML), + GB_PROPERTY_READ("Text", "s", WebFrame_Text), + GB_METHOD("Eval", "v", WebFrame_EvalJavaScript, "(JavaScript)s"), + GB_PROPERTY_READ("Document", "WebElement", WebFrame_Document), + + GB_END_DECLARE +}; + + + diff --git a/gb.qt4/src/webkit/cwebframe.h b/gb.qt4/src/webkit/cwebframe.h new file mode 100644 index 00000000..ca24c93c --- /dev/null +++ b/gb.qt4/src/webkit/cwebframe.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + cwebframe.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBFRAME_H +#define __CWEBFRAME_H + +#include "main.h" + +#include +#include +#include + +#ifndef __CWEBFRAME_CPP + +extern GB_DESC WebFrameChildrenDesc[]; +extern GB_DESC WebFrameDesc[]; + +#else + +#define THIS ((CWEBFRAME *)_object) +#define FRAME (THIS->frame) + +#endif + +typedef + struct + { + GB_BASE ob; + QWebFrame *frame; + } + CWEBFRAME; + +CWEBFRAME *CWEBFRAME_get(QWebFrame *frame); +void CWEBFRAME_eval(QWebFrame *frame, const QString &javascript); + +#endif diff --git a/gb.qt4/src/webkit/cwebhittest.cpp b/gb.qt4/src/webkit/cwebhittest.cpp new file mode 100644 index 00000000..1479f01d --- /dev/null +++ b/gb.qt4/src/webkit/cwebhittest.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + + cwebhittest.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBHITTEST_CPP + +#include "cwebelement.h" +#include "cwebhittest.h" + +BEGIN_PROPERTY(WebHitTest_Document) + + GB.ReturnBoolean(RESULT->linkUrl().isEmpty() && RESULT->imageUrl().isEmpty()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Link) + + GB.ReturnBoolean(!RESULT->linkUrl().isEmpty()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Image) + + GB.ReturnBoolean(!RESULT->imageUrl().isEmpty()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Selected) + + GB.ReturnBoolean(RESULT->isContentSelected()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Editable) + + GB.ReturnBoolean(RESULT->isContentEditable()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Url) + + QUrl url; + + url = RESULT->linkUrl(); + if (url.isEmpty()) + url = RESULT->imageUrl(); + + RETURN_NEW_STRING(url.toString()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Html) + + QWebElement element; + element = RESULT->element(); + + RETURN_NEW_STRING(element.toOuterXml()); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebHitTest_free) + + delete RESULT; + +END_METHOD + +BEGIN_PROPERTY(WebHitTest_Element) + + GB.ReturnObject(CWEBELEMENT_create(RESULT->element())); + +END_PROPERTY + +GB_DESC WebHitTestDesc[] = +{ + GB_DECLARE("WebHitTest", sizeof(CWEBHITTEST)), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, WebHitTest_new, NULL), + GB_METHOD("_free", NULL, WebHitTest_free, NULL), + + GB_PROPERTY_READ("Document", "b", WebHitTest_Document), + GB_PROPERTY_READ("Link", "b", WebHitTest_Link), + GB_PROPERTY_READ("Image", "b", WebHitTest_Image), + //GB_PROPERTY_READ("Media", "b", WebHitTest_Media), + GB_PROPERTY_READ("Selected", "b", WebHitTest_Selected), + GB_PROPERTY_READ("Editable", "b", WebHitTest_Editable), + GB_PROPERTY_READ("Url", "s", WebHitTest_Url), + GB_PROPERTY_READ("HTML", "s", WebHitTest_Html), + GB_PROPERTY_READ("Element", "WebElement", WebHitTest_Element), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +CWEBHITTEST *WEB_create_hit_test(const QWebHitTestResult &result) +{ + CWEBHITTEST *_object = (CWEBHITTEST *)GB.New(GB.FindClass("WebHitTest"), NULL, NULL); + RESULT = new QWebHitTestResult; + *RESULT = result; + + return THIS; +} diff --git a/gb.qt4/src/webkit/cwebhittest.h b/gb.qt4/src/webkit/cwebhittest.h new file mode 100644 index 00000000..0585b987 --- /dev/null +++ b/gb.qt4/src/webkit/cwebhittest.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + cwebhittest.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBHITTEST_H +#define __CWEBHITTEST_H + +#include "main.h" + +#include +#include +#include + +#ifndef __CWEBHITTEST_CPP + +extern GB_DESC WebHitTestDesc[]; + +#else + +#define THIS ((CWEBHITTEST *)_object) +#define RESULT (THIS->result) + +#endif + +typedef + struct + { + GB_BASE ob; + QWebHitTestResult *result; + } + CWEBHITTEST; + +CWEBHITTEST *WEB_create_hit_test(const QWebHitTestResult &result); + +#endif diff --git a/gb.qt4/src/webkit/cwebsettings.cpp b/gb.qt4/src/webkit/cwebsettings.cpp new file mode 100644 index 00000000..11c8f6f8 --- /dev/null +++ b/gb.qt4/src/webkit/cwebsettings.cpp @@ -0,0 +1,489 @@ +/*************************************************************************** + + cwebsettings.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBSETTINGS_CPP + +#include +#include + +#include +#include + +#include "cwebview.h" +#include "cwebsettings.h" + +//static QNetworkDiskCache *_cache = 0; +static bool _cache_enabled = false; +static char *_cache_path = 0; + +static void set_cache(bool on) +{ + QNetworkDiskCache *cache; + + if (!_cache_path) + return; + + _cache_enabled = on; + + if (on) + { + cache = new QNetworkDiskCache(0); + cache->setCacheDirectory(TO_QSTRING(_cache_path)); + WEBVIEW_get_network_manager()->setCache(cache); + } + else + WEBVIEW_get_network_manager()->setCache(0); +} + + +static QWebSettings *get_settings(void *_object) +{ + if (!_object) + return QWebSettings::globalSettings(); + else + return WEBVIEW->settings(); +} + +/***************************************************************************/ + +static void handle_font_family(QWebSettings::FontFamily font, void *_object, void *_param) +{ + if (READ_PROPERTY) + RETURN_NEW_STRING(get_settings(_object)->fontFamily(font)); + else + get_settings(_object)->setFontFamily(font, QSTRING_PROP()); +} + +BEGIN_PROPERTY(WebSettingsFonts_CursiveFont) + + handle_font_family(QWebSettings::CursiveFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_FantasyFont) + + handle_font_family(QWebSettings::FantasyFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_FixedFont) + + handle_font_family(QWebSettings::FixedFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_SansSerifFont) + + handle_font_family(QWebSettings::SansSerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_SerifFont) + + handle_font_family(QWebSettings::SerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_StandardFont) + + handle_font_family(QWebSettings::StandardFont, _object, _param); + +END_METHOD + +static void handle_font_size(QWebSettings::FontSize size, void *_object, void *_param) +{ + if (READ_PROPERTY) + GB.ReturnInteger(get_settings(_object)->fontSize(size) - 3); + else + get_settings(_object)->setFontSize(size, VPROP(GB_INTEGER) + 3); +} + +BEGIN_PROPERTY(WebSettingsFonts_DefaultFixedFontSize) + + handle_font_size(QWebSettings::DefaultFixedFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_DefaultFontSize) + + handle_font_size(QWebSettings::DefaultFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_MinimumFontSize) + + handle_font_size(QWebSettings::MinimumFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_MinimumLogicalFontSize) + + handle_font_size(QWebSettings::MinimumLogicalFontSize, _object, _param); + +END_PROPERTY + + +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettingsIconDatabase_Clear) + + QWebSettings::clearIconDatabase(); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsIconDatabase_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(QWebSettings::iconDatabasePath()); + else + QWebSettings::setIconDatabasePath(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_METHOD(WebSettingsIconDatabase_get, GB_STRING url) + + QIcon icon; + QSize size; + QSize max_size; + + icon = QWebSettings::iconForUrl(QSTRING_ARG(url)); + if (icon.isNull()) + { + GB.ReturnNull(); + return; + } + + foreach(size, icon.availableSizes()) + { + if ((size.width() * size.height()) > (max_size.width() * max_size.height())) + max_size = size; + } + + GB.ReturnObject(QT.CreatePicture(icon.pixmap(max_size))); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsCache_Path) + + if (READ_PROPERTY) + GB.ReturnString(_cache_path); + else + { + char *path = GB.FileName(PSTRING(), PLENGTH()); + QString qpath = QString(path); + QString root = QString(GB.System.Home()); + + if (root.at(root.length() - 1) != '/') + root += '/'; + root += ".cache/"; + if (!qpath.startsWith(root)) + { + GB.Error("Cache directory must be located inside ~/.cache"); + return; + } + + GB.FreeString(&_cache_path); + _cache_path = GB.NewZeroString(path); + set_cache(_cache_enabled); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsCache_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(_cache_enabled); + else + set_cache(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +static int _clear_error; +static char *_clear_path; + +static void remove_file(const char *path) +{ + if (rmdir(path) == 0) + return; + + if (errno == ENOTDIR) + { + if (unlink(path) == 0) + return; + } + + if (_clear_error == 0) + { + _clear_error = errno; + _clear_path = GB.NewZeroString(path); + } +} + +BEGIN_METHOD_VOID(WebSettingsCache_Clear) + + if (!_cache_path || !*_cache_path) + return; + + _clear_error = 0; + GB.BrowseDirectory(_cache_path, NULL, remove_file); + + if (_clear_error) + { + GB.Error("Unable to remove '&1': &2", _clear_path, strerror(_clear_error)); + GB.FreeString(&_clear_path); + } + + set_cache(_cache_enabled); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_METHOD(WebSettings_get, GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + QWebSettings::WebAttribute flag(QWebSettings::WebAttribute(VARG(flag))); + + GB.ReturnBoolean(settings->testAttribute(flag)); + +END_METHOD + +BEGIN_METHOD(WebSettings_put, GB_BOOLEAN value; GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + QWebSettings::WebAttribute flag(QWebSettings::WebAttribute(VARG(flag))); + + settings->setAttribute(flag, VARG(value)); + +END_METHOD + +BEGIN_METHOD(WebSettings_Reset, GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + QWebSettings::WebAttribute flag(QWebSettings::WebAttribute(VARG(flag))); + + settings->resetAttribute(flag); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettings_exit) + + GB.FreeString(&_cache_path); + +END_METHOD + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsProxy_Host) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.hostName()); + else + { + proxy.setHostName(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_User) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.user()); + else + { + proxy.setUser(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Password) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.password()); + else + { + proxy.setPassword(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Port) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.port()); + else + { + proxy.setPort(VPROP(GB_INTEGER)); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Type) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.type()); + else + { + int type = VPROP(GB_INTEGER); + if (type == QNetworkProxy::DefaultProxy || type == QNetworkProxy::NoProxy || type == QNetworkProxy::Socks5Proxy || type == QNetworkProxy::HttpProxy) + { + proxy.setType((QNetworkProxy::ProxyType)type); + nam->setProxy(proxy); + } + } + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC WebViewSettingsDesc[] = +{ + GB_DECLARE(".WebView.Settings", 0), + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + GB_METHOD("Reset", NULL, WebSettings_Reset, "(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsFontsDesc[] = +{ + GB_DECLARE(".WebSettings.Fonts", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("StandardFont", "s", WebSettingsFonts_StandardFont), + GB_STATIC_PROPERTY("FixedFont", "s", WebSettingsFonts_FixedFont), + GB_STATIC_PROPERTY("SerifFont", "s", WebSettingsFonts_SerifFont), + GB_STATIC_PROPERTY("SansSerifFont", "s", WebSettingsFonts_SansSerifFont), + GB_STATIC_PROPERTY("CursiveFont", "s", WebSettingsFonts_CursiveFont), + GB_STATIC_PROPERTY("FantasyFont", "s", WebSettingsFonts_FantasyFont), + + GB_STATIC_PROPERTY("MinimumFontSize", "i", WebSettingsFonts_MinimumFontSize), + GB_STATIC_PROPERTY("MinimumLogicalFontSize", "i", WebSettingsFonts_MinimumLogicalFontSize), + GB_STATIC_PROPERTY("DefaultFontSize", "i", WebSettingsFonts_DefaultFontSize), + GB_STATIC_PROPERTY("DefaultFixedFontSize", "i", WebSettingsFonts_DefaultFixedFontSize), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsIconDatabaseDesc[] = +{ + GB_DECLARE(".WebSettings.IconDatabase", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Clear", NULL, WebSettingsIconDatabase_Clear, NULL), + GB_STATIC_PROPERTY("Path", "s", WebSettingsIconDatabase_Path), + GB_STATIC_METHOD("_get", "Picture", WebSettingsIconDatabase_get, "(Url)s"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsCacheDesc[] = +{ + GB_DECLARE(".WebSettings.Cache", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Enabled", "b", WebSettingsCache_Enabled), + GB_STATIC_PROPERTY("Path", "s", WebSettingsCache_Path), + GB_STATIC_METHOD("Clear", NULL, WebSettingsCache_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsProxyDesc[] = +{ + GB_DECLARE(".WebSettings.Proxy", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Type", "i", WebSettingsProxy_Type), + GB_STATIC_PROPERTY("Host", "s", WebSettingsProxy_Host), + GB_STATIC_PROPERTY("Port", "i", WebSettingsProxy_Port), + GB_STATIC_PROPERTY("User", "s", WebSettingsProxy_User), + GB_STATIC_PROPERTY("Password", "s", WebSettingsProxy_Password), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsDesc[] = +{ + GB_DECLARE("WebSettings", 0), + + GB_CONSTANT("AutoLoadImages", "i", QWebSettings::AutoLoadImages), + GB_CONSTANT("JavascriptEnabled", "i", QWebSettings::JavascriptEnabled), + GB_CONSTANT("JavaEnabled", "i", QWebSettings::JavaEnabled), + GB_CONSTANT("PluginsEnabled", "i", QWebSettings::PluginsEnabled), + GB_CONSTANT("PrivateBrowsingEnabled", "i", QWebSettings::PrivateBrowsingEnabled), + GB_CONSTANT("JavascriptCanOpenWindows", "i", QWebSettings::JavascriptCanOpenWindows), + GB_CONSTANT("JavascriptCanAccessClipboard", "i", QWebSettings::JavascriptCanAccessClipboard), + GB_CONSTANT("DeveloperExtrasEnabled", "i", QWebSettings::DeveloperExtrasEnabled), + GB_CONSTANT("LinksIncludedInFocusChain", "i", QWebSettings::LinksIncludedInFocusChain), + GB_CONSTANT("ZoomTextOnly", "i", QWebSettings::ZoomTextOnly), + GB_CONSTANT("PrintElementBackgrounds", "i", QWebSettings::PrintElementBackgrounds), + GB_CONSTANT("OfflineStorageDatabaseEnabled", "i", QWebSettings::OfflineStorageDatabaseEnabled), + GB_CONSTANT("OfflineWebApplicationCacheEnabled", "i", QWebSettings::OfflineWebApplicationCacheEnabled), + GB_CONSTANT("LocalStorageDatabaseEnabled", "i", QWebSettings::LocalStorageDatabaseEnabled), + + GB_CONSTANT("DefaultProxy", "i", QNetworkProxy::DefaultProxy), + GB_CONSTANT("NoProxy", "i", QNetworkProxy::NoProxy), + GB_CONSTANT("Socks5Proxy", "i", QNetworkProxy::Socks5Proxy), + GB_CONSTANT("HttpProxy", "i", QNetworkProxy::HttpProxy), + + GB_STATIC_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + GB_STATIC_PROPERTY_SELF("IconDatabase", ".WebSettings.IconDatabase"), + GB_STATIC_PROPERTY_SELF("Cache", ".WebSettings.Cache"), + GB_STATIC_PROPERTY_SELF("Proxy", ".WebSettings.Proxy"), + + GB_STATIC_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_STATIC_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + GB_STATIC_METHOD("_exit", NULL, WebSettings_exit, NULL), + +GB_END_DECLARE +}; + +/***************************************************************************/ + + diff --git a/gb.qt4/src/webkit/cwebsettings.h b/gb.qt4/src/webkit/cwebsettings.h new file mode 100644 index 00000000..aeb7db16 --- /dev/null +++ b/gb.qt4/src/webkit/cwebsettings.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + cwebsettings.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBSETTINGS_H +#define __CWEBSETTINGS_H + +#include "main.h" + +#include +#include +#include + +#ifndef __CWEBSETTINGS_CPP + +extern GB_DESC WebViewSettingsDesc[]; +extern GB_DESC WebSettingsFontsDesc[]; +extern GB_DESC WebSettingsIconDatabaseDesc[]; +extern GB_DESC WebSettingsCacheDesc[]; +extern GB_DESC WebSettingsProxyDesc[]; +extern GB_DESC WebSettingsDesc[]; + +#else + +#define WEBVIEW ((QWebView *)((QT_WIDGET *)_object)->widget) + +#endif + +//void WEBSETTINGS_set_cache(QWebView *view, bool on); + +#endif diff --git a/gb.qt4/src/webkit/cwebview.cpp b/gb.qt4/src/webkit/cwebview.cpp new file mode 100644 index 00000000..b4db0ac5 --- /dev/null +++ b/gb.qt4/src/webkit/cwebview.cpp @@ -0,0 +1,943 @@ +/*************************************************************************** + + cwebview.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBVIEW_CPP + +#include +#include +#include +#include +#include +#include + +#include "ccookiejar.h" +#include "cwebsettings.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebhittest.h" +#include "cwebview.h" + +/*typedef + struct { + const char *name; + QWebPage::WebAction action; + } + WEBVIEW_ACTION;*/ + +DECLARE_EVENT(EVENT_CLICK); +DECLARE_EVENT(EVENT_LINK); +DECLARE_EVENT(EVENT_PROGRESS); +DECLARE_EVENT(EVENT_LOAD); +DECLARE_EVENT(EVENT_ERROR); +DECLARE_EVENT(EVENT_ICON); +DECLARE_EVENT(EVENT_TITLE); +DECLARE_EVENT(EVENT_SELECT); +DECLARE_EVENT(EVENT_STATUS); +DECLARE_EVENT(EVENT_NEW_WINDOW); +DECLARE_EVENT(EVENT_NEW_FRAME); +DECLARE_EVENT(EVENT_AUTH); +DECLARE_EVENT(EVENT_DOWNLOAD); + +#define HISTORY (WIDGET->history()) + + +static QNetworkAccessManager *_network_access_manager = 0; +static CWEBVIEW *_network_access_manager_view = 0; +static QT_COLOR_FUNC _old_after_set_color; +static bool _ignore_png_warnings = false; + +/* +static WEBVIEW_ACTION _actions[] = +{ + { "OpenLink", QWebPage::OpenLink }, + { "OpenLinkInNewWindow", QWebPage::OpenLinkInNewWindow }, + { "OpenFrameInNewWindow", QWebPage::OpenFrameInNewWindow }, + { "DownloadLinkToDisk", QWebPage::DownloadLinkToDisk }, + { "CopyLinkToClipboard", QWebPage::CopyLinkToClipboard }, + { "OpenImageInNewWindow", QWebPage::OpenImageInNewWindow }, + { "DownloadImageToDisk", QWebPage::DownloadImageToDisk }, + { "CopyImageToClipboard", QWebPage::CopyImageToClipboard }, + { "Back", QWebPage::Back }, + { "Forward", QWebPage::Forward }, + { "Stop", QWebPage::Stop }, + { "StopScheduledPageRefresh", QWebPage::StopScheduledPageRefresh }, + { "Reload", QWebPage::Reload }, + { "ReloadAndBypassCache", QWebPage::ReloadAndBypassCache }, + { "Cut", QWebPage::Cut }, + { "Copy", QWebPage::Copy }, + { "Paste", QWebPage::Paste }, + { "Undo", QWebPage::Undo }, + { "Redo", QWebPage::Redo }, + { "MoveToNextChar", QWebPage::MoveToNextChar }, + { "MoveToPreviousChar", QWebPage::MoveToPreviousChar }, + { "MoveToNextWord", QWebPage::MoveToNextWord }, + { "MoveToPreviousWord", QWebPage::MoveToPreviousWord }, + { "MoveToNextLine", QWebPage::MoveToNextLine }, + { "MoveToPreviousLine", QWebPage::MoveToPreviousLine }, + { "MoveToStartOfLine", QWebPage::MoveToStartOfLine }, + { "MoveToEndOfLine", QWebPage::MoveToEndOfLine }, + { "MoveToStartOfBlock", QWebPage::MoveToStartOfBlock }, + { "MoveToEndOfBlock", QWebPage::MoveToEndOfBlock }, + { "MoveToStartOfDocument", QWebPage::MoveToStartOfDocument }, + { "MoveToEndOfDocument", QWebPage::MoveToEndOfDocument }, + { "SelectNextChar", QWebPage::SelectNextChar }, + { "SelectPreviousChar", QWebPage::SelectPreviousChar }, + { "SelectNextWord", QWebPage::SelectNextWord }, + { "SelectPreviousWord", QWebPage::SelectPreviousWord }, + { "SelectNextLine", QWebPage::SelectNextLine }, + { "SelectPreviousLine", QWebPage::SelectPreviousLine }, + { "SelectStartOfLine", QWebPage::SelectStartOfLine }, + { "SelectEndOfLine", QWebPage::SelectEndOfLine }, + { "SelectStartOfBlock", QWebPage::SelectStartOfBlock }, + { "SelectEndOfBlock", QWebPage::SelectEndOfBlock }, + { "SelectStartOfDocument", QWebPage::SelectStartOfDocument }, + { "SelectEndOfDocument", QWebPage::SelectEndOfDocument }, + { "DeleteStartOfWord", QWebPage::DeleteStartOfWord }, + { "DeleteEndOfWord", QWebPage::DeleteEndOfWord }, + { "SetTextDirectionDefault", QWebPage::SetTextDirectionDefault }, + { "SetTextDirectionLeftToRight", QWebPage::SetTextDirectionLeftToRight }, + { "SetTextDirectionRightToLeft", QWebPage::SetTextDirectionRightToLeft }, + { "ToggleBold", QWebPage::ToggleBold }, + { "ToggleItalic", QWebPage::ToggleItalic }, + { "ToggleUnderline", QWebPage::ToggleUnderline }, + { "InspectElement", QWebPage::InspectElement }, + { "InsertParagraphSeparator", QWebPage::InsertParagraphSeparator }, + { "InsertLineSeparator", QWebPage::InsertLineSeparator }, + { "SelectAll", QWebPage::SelectAll }, + { "PasteAndMatchStyle", QWebPage::PasteAndMatchStyle }, + { "RemoveFormat", QWebPage::RemoveFormat }, + { "ToggleStrikethrough", QWebPage::ToggleStrikethrough }, + { "ToggleSubscript", QWebPage::ToggleSubscript }, + { "ToggleSuperscript", QWebPage::ToggleSuperscript }, + { "InsertUnorderedList", QWebPage::InsertUnorderedList }, + { "InsertOrderedList", QWebPage::InsertOrderedList }, + { "Indent", QWebPage::Indent }, + { "Unindent", QWebPage::Outdent }, + { "AlignCenter", QWebPage::AlignCenter }, + { "AlignJustified", QWebPage::AlignJustified }, + { "AlignLeft", QWebPage::AlignLeft }, + { "AlignRight", QWebPage::AlignRight }, + { NULL, QWebPage::NoWebAction } +}; +*/ + +QNetworkAccessManager *WEBVIEW_get_network_manager() +{ + if (!_network_access_manager) + { + _network_access_manager = new QNetworkAccessManager(); + _network_access_manager->setCookieJar(new MyCookieJar); + } + + return _network_access_manager; +} + +/* +static QWebPage::WebAction get_action(const char *name) +{ + WEBVIEW_ACTION *p; + + for (p = _actions; p->name; p++) + { + if (!strcasecmp(p->name, name)) + return p->action; + } + + return QWebPage::NoWebAction; +} +*/ + +static void after_set_color(void *_object) +{ + if (!GB.Is(THIS, CLASS_WebView)) + { + if (_old_after_set_color) + (*_old_after_set_color)(THIS); + return; + } + + if (QT.GetBackgroundColor(THIS) == GB_COLOR_DEFAULT) + { + QPalette palette = WIDGET->palette(); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, true); + } + else + { + qDebug("after_set_color"); + QPalette palette = WIDGET->palette(); + palette.setBrush(QPalette::Base, Qt::transparent); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, false); + } +} + +static void stop_view(void *_object) +{ + //fprintf(stderr, "stop_view\n"); + THIS->stopping = TRUE; + WIDGET->stop(); + THIS->stopping = FALSE; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(WebView_new, GB_OBJECT parent) + + int fd_save = -1; + + if (!_ignore_png_warnings) + { + int fd = ::open("/dev/null", O_RDWR); + fd_save = ::dup(2); + ::dup2(fd, 2); + ::close(fd); + } + + MyWebView *wid = new MyWebView(QT.GetContainer(VARG(parent))); + + if (!_ignore_png_warnings) + { + ::dup2(fd_save, 2); + ::close(fd_save); + _ignore_png_warnings = true; + } + + QT.InitWidget(wid, _object, false); + QT.SetWheelFlag(_object); + + WEBVIEW_get_network_manager(); + + wid->page()->setNetworkAccessManager(_network_access_manager); + wid->page()->setForwardUnsupportedContent(true); + + //QObject::connect(wid, SIGNAL(linkClicked(const QUrl &)), &CWebView::manager, SLOT(linkClicked(const QUrl &))); + QObject::connect(wid, SIGNAL(loadFinished(bool)), &CWebView::manager, SLOT(loadFinished(bool))); + QObject::connect(wid, SIGNAL(loadProgress(int)), &CWebView::manager, SLOT(loadProgress(int))); + QObject::connect(wid, SIGNAL(loadStarted()), &CWebView::manager, SLOT(loadStarted())); + QObject::connect(wid, SIGNAL(selectionChanged()), &CWebView::manager, SLOT(selectionChanged())); + QObject::connect(wid, SIGNAL(statusBarMessage(const QString &)), &CWebView::manager, SLOT(statusBarMessage(const QString &))); + QObject::connect(wid, SIGNAL(titleChanged(const QString &)), &CWebView::manager, SLOT(titleChanged(const QString &))); + + QObject::connect(wid->page(), SIGNAL(linkHovered(const QString &, const QString &, const QString &)), &CWebView::manager, + SLOT(linkHovered(const QString &, const QString &, const QString &))); + QObject::connect(wid->page(), SIGNAL(frameCreated(QWebFrame *)), &CWebView::manager, SLOT(frameCreated(QWebFrame *))); + QObject::connect(wid->page(), SIGNAL(downloadRequested(QNetworkRequest)), &CWebView::manager, SLOT(downloadRequested(QNetworkRequest))); + QObject::connect(wid->page(), SIGNAL(unsupportedContent(QNetworkReply*)), &CWebView::manager, SLOT(handleUnsupportedContent(QNetworkReply*))); + + QObject::connect(wid, SIGNAL(iconChanged()), &CWebView::manager, SLOT(iconChanged())); + QObject::connect(wid->page()->mainFrame(), SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + + QObject::connect(wid->page()->networkAccessManager(), SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)), &CWebView::manager, + SLOT(authenticationRequired(QNetworkReply *, QAuthenticator *))); +END_METHOD + +BEGIN_METHOD_VOID(WebView_free) + + if (_network_access_manager_view == THIS) + _network_access_manager_view = 0; + + GB.FreeString(&THIS->status); + GB.FreeString(&THIS->userAgent); + GB.Unref(POINTER(&THIS->icon)); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_init) + + _old_after_set_color = QT.AfterSetColor(after_set_color); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_exit) + + delete _network_access_manager; + +END_METHOD + +BEGIN_PROPERTY(WebView_Url) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->url().toString()); + else + { + stop_view(THIS); + WIDGET->setUrl(QSTRING_PROP()); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebView_HTML) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->page()->mainFrame()->toHtml()); + else + WIDGET->setHtml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Text) + + RETURN_NEW_STRING(WIDGET->page()->mainFrame()->toPlainText()); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Icon) + + if (!THIS->icon) + { + QIcon icon = WIDGET->icon(); + + if (icon.isNull()) + icon = QWebSettings::iconForUrl(WIDGET->url()); + + if (!icon.isNull()) + { + THIS->icon = QT.CreatePicture(icon.pixmap(16, 16)); + GB.Ref(THIS->icon); + } + } + + GB.ReturnObject(THIS->icon); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_SelectedText) + + RETURN_NEW_STRING(WIDGET->selectedText()); + +END_PROPERTY + +#if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) +BEGIN_PROPERTY(WebView_Zoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->zoomFactor()); + else + WIDGET->setZoomFactor(VPROP(GB_FLOAT)); + +END_PROPERTY +#endif + +BEGIN_PROPERTY(WebView_TextZoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->textSizeMultiplier()); + else + WIDGET->setTextSizeMultiplier(VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Title) + + RETURN_NEW_STRING(WIDGET->title()); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebView_Back) + + WIDGET->back(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Forward) + + WIDGET->forward(); + +END_METHOD + +BEGIN_METHOD(WebView_Reload, GB_BOOLEAN bypass) + + bool bypass = VARGOPT(bypass, false); + stop_view(THIS); + if (bypass) + WIDGET->page()->triggerAction(QWebPage::ReloadAndBypassCache); + else + WIDGET->reload(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Stop) + + stop_view(THIS); + +END_METHOD + +BEGIN_PROPERTY(WebView_NewView) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->new_view); + else + GB.StoreObject(PROP(GB_OBJECT), &THIS->new_view); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Progress) + + GB.ReturnFloat(THIS->progress); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Status) + + if (READ_PROPERTY) + GB.ReturnString(THIS->status); + else + GB.StoreString(PROP(GB_STRING), &THIS->status); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Frame) + + GB.ReturnObject(CWEBFRAME_get(WIDGET->page()->mainFrame())); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Current) + + GB.ReturnObject(CWEBFRAME_get(WIDGET->page()->currentFrame())); + +END_PROPERTY + + +BEGIN_PROPERTY(WebViewAuth_Url) + + if (THIS->reply) + RETURN_NEW_STRING(THIS->reply->url().toString()); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_Realm) + + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->realm()); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_User) + + if (READ_PROPERTY) + { + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->user()); + else + GB.ReturnVoidString(); + } + else + { + if (THIS->authenticator) + THIS->authenticator->setUser(QSTRING_PROP()); + else + GB.Error("No authentication required"); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_Password) + + if (READ_PROPERTY) + { + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->password()); + else + GB.ReturnVoidString(); + } + else + { + if (THIS->authenticator) + THIS->authenticator->setPassword(QSTRING_PROP()); + else + GB.Error("No authentication required"); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Cookies) + + MyCookieJar *cookieJar = static_cast(_network_access_manager->cookieJar()); + QList list; + GB_ARRAY cookies; + int i; + + if (READ_PROPERTY) + { + list = cookieJar->allCookies(); + + GB.Array.New(POINTER(&cookies), GB.FindClass("Cookie"), list.count()); + + for (i = 0; i < list.count(); i++) + { + CCOOKIE *cookie = WEB_create_cookie(list.at(i)); + *((CCOOKIE **)(GB.Array.Get(cookies, i))) = cookie; + GB.Ref(cookie); + } + + GB.ReturnObject(cookies); + } + else + { + // TODO what todo? + cookies = VPROP(GB_OBJECT); + if (GB.CheckObject(cookies)) + return; + + for (i = 0; i < GB.Array.Count(cookies); i++) + { + CCOOKIE *cookie = *((CCOOKIE **)GB.Array.Get(cookies, i)); + if (GB.CheckObject(cookie)) + continue; + list.append(*(cookie->cookie)); + } + + cookieJar->setAllCookies(list); + } + +END_PROPERTY + +BEGIN_METHOD(WebView_HitTest, GB_INTEGER X; GB_INTEGER Y) + + GB.ReturnObject(WEB_create_hit_test(WIDGET->page()->mainFrame()->hitTestContent(QPoint(VARG(X), VARG(Y))))); + +END_METHOD + +BEGIN_METHOD(WebView_FindText, GB_STRING text; GB_BOOLEAN backward; GB_BOOLEAN case_sensitive; GB_BOOLEAN wrap) + + QString text; + QWebPage::FindFlags flags = 0; + + if (!MISSING(text)) + text = QSTRING_ARG(text); + + if (VARGOPT(backward, false)) flags |= QWebPage::FindBackward; + if (VARGOPT(case_sensitive, false)) flags |= QWebPage::FindCaseSensitively; + if (VARGOPT(wrap, false)) flags |= QWebPage::FindWrapsAroundDocument; + //if (VARGOPT(highlight, false)) flags |= QWebPage::HighlightAllOccurrences; + + GB.ReturnBoolean(!WIDGET->findText(text, flags)); + +END_METHOD + +BEGIN_PROPERTY(WebView_Editable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->page()->isContentEditable()); + else + WIDGET->page()->setContentEditable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(WebView_Exec, GB_STRING action; GB_VARIANT arg) + + QWebPage::WebAction action = get_action(GB.ToZeroString(ARG(action))); + + if (action == QWebPage::NoWebAction) + { + GB.Error("Unknown action"); + return; + } + + if (MISSING(arg)) + { + WIDGET->page()->triggerAction(action); + } + else + { + if (GB.Conv((GB_VALUE *)ARG(arg), GB_T_BOOLEAN)) + return; + + WIDGET->page()->triggerAction(action, ((GB_BOOLEAN *)ARG(arg))->value); + } + +END_METHOD +#endif + +/* +BEGIN_METHOD(WebView_CanExec, GB_STRING action) + + QWebPage::Action action = get_action(GB.ToZeroString(ARG(action))); + + if (action == QWebPage::NoWebAction) + { + GB.Error("Unknown action"); + return; + } + +END_METHOD +*/ + +BEGIN_METHOD(WebView_Eval, GB_STRING javascript) + + CWEBFRAME_eval(WIDGET->page()->currentFrame(), QSTRING_ARG(javascript)); + +END_METHOD + +BEGIN_PROPERTY(WebView_UserAgent) + + if (READ_PROPERTY) + GB.ReturnString(THIS->userAgent); + else + GB.StoreString(PROP(GB_STRING), &THIS->userAgent); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Document) + + GB.ReturnObject(CWEBELEMENT_create(WIDGET->page()->mainFrame()->documentElement())); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(WebViewHistory_Count) + + GB.ReturnInteger(HISTORY->count()); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_Max) + + GB.ReturnInteger(HISTORY->count() - 1); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_Index) + + if (READ_PROPERTY) + GB.ReturnInteger(HISTORY->currentItemIndex()); + else + { + int index = VPROP(GB_INTEGER); + if (index < 0 || index >= HISTORY->count()) + { + GB.Error(GB_ERR_ARG); + return; + } + HISTORY->goToItem(HISTORY->itemAt(index)); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_MaxSize) + + if (READ_PROPERTY) + GB.ReturnInteger(HISTORY->maximumItemCount()); + else + HISTORY->setMaximumItemCount(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebViewHistory_Clear) + + int max = HISTORY->maximumItemCount(); + HISTORY->setMaximumItemCount(0); + HISTORY->setMaximumItemCount(max); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC WebViewAuthDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Auth"), + + GB_PROPERTY_READ("Url", "s", WebViewAuth_Url), + GB_PROPERTY_READ("Realm", "s", WebViewAuth_Realm), + GB_PROPERTY("User", "s", WebViewAuth_User), + GB_PROPERTY("Password", "s", WebViewAuth_Password), + + GB_END_DECLARE +}; + +GB_DESC WebViewHistoryDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History"), + + GB_PROPERTY_READ("Count", "i", WebViewHistory_Count), + GB_PROPERTY_READ("Max", "i", WebViewHistory_Max), + GB_PROPERTY("Index", "i", WebViewHistory_Index), + GB_PROPERTY("MaxSize", "i", WebViewHistory_MaxSize), + GB_METHOD("Clear", NULL, WebViewHistory_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewDesc[] = +{ + GB_DECLARE("WebView", sizeof(CWEBVIEW)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, WebView_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, WebView_free, NULL), + GB_METHOD("_init", NULL, WebView_init, NULL), + GB_METHOD("_exit", NULL, WebView_exit, NULL), + + GB_PROPERTY("Url", "s", WebView_Url), + GB_PROPERTY("Status", "s", WebView_Status), + + GB_PROPERTY("HTML", "s", WebView_HTML), + GB_PROPERTY_READ("Text", "s", WebView_Text), + GB_PROPERTY_READ("Icon", "Picture", WebView_Icon), + GB_PROPERTY_READ("SelectedText", "s", WebView_SelectedText), + GB_PROPERTY_READ("Progress", "f", WebView_Progress), + #if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) + GB_PROPERTY("Zoom", "f", WebView_Zoom), + #else + GB_PROPERTY("Zoom", "f", WebView_TextZoom), + #endif + GB_PROPERTY("TextZoom", "f", WebView_TextZoom), + GB_PROPERTY_READ("Title", "s", WebView_Title), + + GB_PROPERTY_READ("Frame", "WebFrame", WebView_Frame), + GB_PROPERTY_READ("Current", "WebFrame", WebView_Current), + GB_PROPERTY_READ("Document", "WebElement", WebView_Document), + + GB_PROPERTY_SELF("Settings", ".WebView.Settings"), + GB_PROPERTY_SELF("Auth", ".WebView.Auth"), + GB_PROPERTY_SELF("History", ".WebView.History"), + + GB_METHOD("Back", NULL, WebView_Back, NULL), + GB_METHOD("Forward", NULL, WebView_Forward, NULL), + GB_METHOD("Reload", NULL, WebView_Reload, "[(BypassCache)b]"), + GB_METHOD("Stop", NULL, WebView_Stop, NULL), + + GB_PROPERTY("NewView", "WebView", WebView_NewView), + + GB_PROPERTY("Cookies", "Cookie[]", WebView_Cookies), + + GB_METHOD("HitTest", "WebHitTest", WebView_HitTest, "(X)i(Y)i"), + GB_METHOD("FindText", "b", WebView_FindText, "[(Text)s(Backward)b(CaseSensitive)b(Wrap)b]"), + + GB_PROPERTY("Editable", "b", WebView_Editable), + //GB_METHOD("Exec", NULL, WebView_Exec, "(Action)s[(Argument)v]"), + //GB_METHOD("CanExec", "b", WebView_CanExec, "(Action)s"), + GB_METHOD("Eval", "v", WebView_Eval, "(JavaScript)s"), + + GB_PROPERTY("UserAgent", "s", WebView_UserAgent), + + GB_CONSTANT("_Properties", "s", "*,Url,Editable"), + GB_CONSTANT("_Group", "s", "View"), + + GB_EVENT("Click", NULL, "(Frame)WebFrame", &EVENT_CLICK), + GB_EVENT("Link", NULL, "(Url)s", &EVENT_LINK), + GB_EVENT("Progress", NULL, NULL, &EVENT_PROGRESS), + GB_EVENT("Load", NULL, NULL, &EVENT_LOAD), + GB_EVENT("Error", NULL, NULL, &EVENT_ERROR), + GB_EVENT("Icon", NULL, NULL, &EVENT_ICON), + GB_EVENT("Title", NULL, NULL, &EVENT_TITLE), + GB_EVENT("Select", NULL, NULL, &EVENT_SELECT), + GB_EVENT("Status", NULL, NULL, &EVENT_STATUS), + GB_EVENT("NewWindow", NULL, "(Modal)b", &EVENT_NEW_WINDOW), + GB_EVENT("NewFrame", NULL, "(Frame)WebFrame", &EVENT_NEW_FRAME), + GB_EVENT("Auth", NULL, NULL, &EVENT_AUTH), + GB_EVENT("Download", NULL, "(Download)WebDownload", &EVENT_DOWNLOAD), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +MyWebPage::MyWebPage(QObject *parent) : QWebPage(parent) +{ +} + +QString MyWebPage::userAgentForUrl(const QUrl& url) const +{ + MyWebView *view = (MyWebView *)parent(); + void *_object = QT.GetObject(view); + + if (THIS->userAgent) + return (const char *)THIS->userAgent; + else + return QWebPage::userAgentForUrl(url); +}; + + +MyWebView::MyWebView(QWidget *parent) : QWebView(parent) +{ + //settings()->setFontFamily(QWebSettings::FixedFont, "monospace"); + setPage(new MyWebPage(this)); +} + +QWebView *MyWebView::createWindow(QWebPage::WebWindowType type) +{ + void *_object = QT.GetObject(this); + QWebView *new_view; + + GB.Raise(THIS, EVENT_NEW_WINDOW, 1, GB_T_BOOLEAN, type == QWebPage::WebModalDialog); + + if (!THIS->new_view) + return 0; + + new_view = (QWebView *)(((CWEBVIEW *)THIS->new_view)->widget.widget); + GB.Unref(POINTER(&THIS->new_view)); + THIS->new_view = 0; + return new_view; +} + +/***************************************************************************/ + +CWebView CWebView::manager; + +// void CWebView::linkClicked(const QUrl &url) +// { +// GET_SENDER(); +// WIDGET->page()->currentFrame()->setUrl(url); +// //WIDGET->setUrl(url); +// GB.Raise(THIS, EVENT_CLICK, 0); +// } + +void CWebView::loadFinished(bool ok) +{ + GET_SENDER(); + + //fprintf(stderr, "loadFinished %d (%d)\n", ok, THIS->stopping); + + THIS->progress = 1; + + if (ok) + GB.Raise(THIS, EVENT_LOAD, 0); + else if (!THIS->stopping) + GB.RaiseLater(THIS, EVENT_ERROR); +} + +void CWebView::loadProgress(int progress) +{ + GET_SENDER(); + + double v = progress / 100.0; + if (THIS->progress == v) + return; + + THIS->progress = v; + GB.RaiseLater(THIS, EVENT_PROGRESS); +} + +void CWebView::loadStarted() +{ + GET_SENDER(); + + //fprintf(stderr, "loadStarted\n"); + + THIS->progress = 0; + _network_access_manager_view = THIS; + GB.RaiseLater(THIS, EVENT_PROGRESS); +} + +void CWebView::selectionChanged() +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_SELECT, 0); +} + +void CWebView::statusBarMessage(const QString &text) +{ + GET_SENDER(); + GB.FreeString(&THIS->status); + THIS->status = NEW_STRING(text); + GB.Raise(THIS, EVENT_STATUS, 0); +} + +void CWebView::titleChanged(const QString &title) +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_TITLE, 0); +} + +void CWebView::linkHovered(const QString &link, const QString &title, const QString &textContent) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + const char *str = TO_UTF8(link); + GB.Raise(THIS, EVENT_LINK, 1, GB_T_STRING, str, LAST_UTF8_LENGTH()); +} + +void CWebView::frameCreated(QWebFrame *frame) +{ + QObject::connect(frame, SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + GB.Raise(THIS, EVENT_NEW_FRAME, 1, GB_T_OBJECT, CWEBFRAME_get(frame)); +} + +void CWebView::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) +{ + //qDebug("CWebView::authenticationRequired: %p", _network_access_manager_view); + + void *_object = _network_access_manager_view; //QT.GetObject((QWidget *)((QNetworkAccessManager*)sender())->parent()); + if (!THIS) + return; + + THIS->reply = reply; + THIS->authenticator = authenticator; + + GB.Raise(THIS, EVENT_AUTH, 0); + + THIS->reply = 0; + THIS->authenticator = 0; +} + +void CWebView::iconChanged() +{ + //void *_object = QT.GetObject(((QWebFrame *)sender())->page()->view()); + GET_SENDER(); + GB.Unref(POINTER(&THIS->icon)); + THIS->icon = NULL; + GB.RaiseLater(THIS, EVENT_ICON); +} + +void CWebView::urlChanged(const QUrl &) +{ + QWebFrame *frame = (QWebFrame *)sender(); + //fprintf(stderr, "urlChanged: %p\n", frame); + void *_object = QT.GetObject(frame->page()->view()); + GB.Raise(THIS, EVENT_CLICK, 1, GB_T_OBJECT, CWEBFRAME_get(frame)); +} + +void CWebView::downloadRequested(const QNetworkRequest &request) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + download = WEB_create_download(_network_access_manager->get(request)); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); +} + +void CWebView::handleUnsupportedContent(QNetworkReply *reply) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + if (reply->error() == QNetworkReply::NoError) + { + download = WEB_create_download(reply); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); + } + else + delete reply; +} diff --git a/gb.qt4/src/webkit/cwebview.h b/gb.qt4/src/webkit/cwebview.h new file mode 100644 index 00000000..82461200 --- /dev/null +++ b/gb.qt4/src/webkit/cwebview.h @@ -0,0 +1,122 @@ +/*************************************************************************** + + cwebview.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBVIEW_H +#define __CWEBVIEW_H + +#include +#include +#include +#include +#include +#include +#include + +#include "cwebdownload.h" +#include "main.h" + +#ifndef __CWEBVIEW_CPP + +extern GB_DESC WebViewAuthDesc[]; +extern GB_DESC WebViewHistoryDesc[]; +extern GB_DESC WebViewDownloadsDesc[]; +extern GB_DESC WebViewDesc[]; + +#else + +#define THIS ((CWEBVIEW *)_object) +#define WIDGET ((MyWebView *)((QT_WIDGET *)_object)->widget) + +#endif + +class MyWebPage : public QWebPage +{ + Q_OBJECT + +public: + + MyWebPage(QObject *parent); + +protected: + + virtual QString userAgentForUrl(const QUrl& url) const; +}; + +class MyWebView : public QWebView +{ + Q_OBJECT + +public: + + MyWebView(QWidget *parent); + +protected: + + virtual QWebView *createWindow(QWebPage::WebWindowType type); +}; + +typedef + struct + { + QT_WIDGET widget; + void *new_view; + double progress; + char *status; + QT_PICTURE icon; + QNetworkReply *reply; + QAuthenticator *authenticator; + char *userAgent; + unsigned stopping : 1; + } + CWEBVIEW; + +class CWebView : public QObject +{ + Q_OBJECT + +public: + + static CWebView manager; + +public slots: + + void iconChanged(); + //void linkClicked(const QUrl &url); + void loadFinished(bool ok); + void loadProgress(int progress); + void loadStarted(); + void selectionChanged(); + void statusBarMessage(const QString &text); + void titleChanged(const QString &title); + void linkHovered(const QString &link, const QString &title, const QString &textContent); + //void downloadRequested(const QNetworkRequest &); + void frameCreated(QWebFrame *); + void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + void urlChanged(const QUrl &); + void downloadRequested(const QNetworkRequest &); + void handleUnsupportedContent(QNetworkReply*); +}; + +QNetworkAccessManager *WEBVIEW_get_network_manager(); + +#endif diff --git a/gb.qt4/src/webkit/gb.qt4.webkit.component b/gb.qt4/src/webkit/gb.qt4.webkit.component new file mode 100644 index 00000000..ba4504f0 --- /dev/null +++ b/gb.qt4/src/webkit/gb.qt4.webkit.component @@ -0,0 +1,10 @@ +[Component] +Key=gb.qt4.webkit +Author=Benoît Minisini +Type=Form +Require=gb.qt4 +State=Stable + +[Form] +Control=WebView + diff --git a/gb.qt4/src/webkit/main.cpp b/gb.qt4/src/webkit/main.cpp new file mode 100644 index 00000000..b75f5945 --- /dev/null +++ b/gb.qt4/src/webkit/main.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include + +#include + +#include "ccookiejar.h" +#include "cwebhittest.h" +#include "cwebsettings.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebdownload.h" +#include "cwebview.h" +#include "main.h" + +GB_CLASS CLASS_WebView; + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + WebDownloadDesc, + WebDownloadsDesc, + WebHitTestDesc, + CookieDesc, + WebSettingsIconDatabaseDesc, + WebSettingsCacheDesc, + WebSettingsFontsDesc, + WebSettingsProxyDesc, + WebSettingsDesc, + WebElementStyleDesc, + WebElementDesc, + WebFrameChildrenDesc, + WebFrameDesc, + WebViewSettingsDesc, + WebViewAuthDesc, + WebViewHistoryDesc, + WebViewDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ +#ifdef QT5 + GB.GetInterface("gb.qt5", QT_INTERFACE_VERSION, &QT); +#else + GB.GetInterface("gb.qt4", QT_INTERFACE_VERSION, &QT); +#endif + CLASS_WebView = GB.FindClass("WebView"); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + +void MAIN_return_qvariant(const QVariant &result) +{ + GB_DATE date; + GB_DATE_SERIAL ds; + QDateTime qdate; + + switch (result.type()) + { + case QVariant::Bool: + GB.ReturnBoolean(result.toBool()); + break; + + case QVariant::Date: + case QVariant::DateTime: + qdate = result.toDateTime(); + ds.year = qdate.date().year(); + ds.month = qdate.date().month(); + ds.day = qdate.date().day(); + ds.hour = qdate.time().hour(); + ds.min = qdate.time().minute(); + ds.sec = qdate.time().second(); + ds.msec = qdate.time().msec(); + GB.MakeDate(&ds, &date); + GB.ReturnDate(&date); + break; + + case QVariant::Double: + GB.ReturnFloat(result.toDouble()); + break; + + case QVariant::Int: + case QVariant::UInt: + GB.ReturnInteger(result.toInt()); + break; + + case QVariant::LongLong: + case QVariant::ULongLong: + GB.ReturnLong(result.toLongLong()); + break; + + case QVariant::String: + RETURN_NEW_STRING(result.toString()); + break; + + // TODO: Handle these three datatypes + case QVariant::Hash: + case QVariant::List: + case QVariant::RegExp: + default: + GB.ReturnNull(); + break; + } + + GB.ReturnConvVariant(); +} + diff --git a/gb.qt4/src/webkit/main.h b/gb.qt4/src/webkit/main.h new file mode 100644 index 00000000..65677e90 --- /dev/null +++ b/gb.qt4/src/webkit/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.qt.h" + +#include + +#ifndef __MAIN_CPP +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +extern GB_CLASS CLASS_WebView; +#endif + +void MAIN_return_qvariant(const QVariant &result); + +#endif diff --git a/gb.qt4/src/webview/Makefile.am b/gb.qt4/src/webview/Makefile.am new file mode 100644 index 00000000..bf65524f --- /dev/null +++ b/gb.qt4/src/webview/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.qt4.webview +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt4.webview.la + +gb_qt4_webview_la_LIBADD = @QTWEBVIEW_LIB@ +gb_qt4_webview_la_LDFLAGS = -module @LD_FLAGS@ @QTWEBVIEW_LDFLAGS@ +gb_qt4_webview_la_CPPFLAGS = @QTWEBVIEW_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt4_webview_la_SOURCES = \ + main.h main.cpp \ + c_websettings.h c_websettings.cpp \ + c_webview.h c_webview.cpp c_webview_moc.cpp \ + jsonwriter.h jsonwriter.cpp + diff --git a/gb.qt4/src/webview/c_websettings.cpp b/gb.qt4/src/webview/c_websettings.cpp new file mode 100644 index 00000000..fb2d8f34 --- /dev/null +++ b/gb.qt4/src/webview/c_websettings.cpp @@ -0,0 +1,500 @@ +/*************************************************************************** + + c_websettings.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WEBSETTINGS_CPP + +#include +#include + +#include "c_webview.h" +#include "c_websettings.h" + +//static QNetworkDiskCache *_cache = 0; +/*static bool _cache_enabled = false; +static char *_cache_path = 0; + +static void set_cache(bool on) +{ + QNetworkDiskCache *cache; + + if (!_cache_path) + return; + + _cache_enabled = on; + + if (on) + { + cache = new QNetworkDiskCache(0); + cache->setCacheDirectory(TO_QSTRING(_cache_path)); + WEBVIEW_get_network_manager()->setCache(cache); + } + else + WEBVIEW_get_network_manager()->setCache(0); +}*/ + + +static QWebSettings *get_settings(void *_object) +{ + if (GB.Is(_object, GB.FindClass("WebSettings"))) + return QWebSettings::globalSettings(); + else + return WEBVIEW->settings(); +} + +//------------------------------------------------------------------------- + +static void handle_font_family(QWebSettings::FontFamily font, void *_object, void *_param) +{ + if (READ_PROPERTY) + RETURN_NEW_STRING(get_settings(_object)->fontFamily(font)); + else + get_settings(_object)->setFontFamily(font, QSTRING_PROP()); +} + +BEGIN_PROPERTY(WebSettingsFonts_Default) + + handle_font_family(QWebSettings::StandardFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Fixed) + + handle_font_family(QWebSettings::FixedFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Serif) + + handle_font_family(QWebSettings::SerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_SansSerif) + + handle_font_family(QWebSettings::SansSerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Cursive) + + handle_font_family(QWebSettings::CursiveFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Fantasy) + + handle_font_family(QWebSettings::FantasyFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Pictograph) + + handle_font_family(QWebSettings::FantasyFont, _object, _param); + +END_METHOD + +static void handle_font_size(QWebSettings::FontSize size, void *_object, void *_param) +{ + if (READ_PROPERTY) + GB.ReturnInteger(get_settings(_object)->fontSize(size) * 76 / 92); + else + get_settings(_object)->setFontSize(size, VPROP(GB_INTEGER) * 92 / 76); +} + +BEGIN_PROPERTY(WebSettingsFonts_DefaultSize) + + handle_font_size(QWebSettings::DefaultFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_DefaultFixedSize) + + handle_font_size(QWebSettings::DefaultFixedFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_MinimumSize) + + handle_font_size(QWebSettings::MinimumFontSize, _object, _param); + +END_PROPERTY + +/*BEGIN_PROPERTY(WebSettingsFonts_MinimumLogicalFontSize) + + handle_font_size(QWebSettings::MinimumLogicalFontSize, _object, _param); + +END_PROPERTY*/ + +#if 0 +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettingsIconDatabase_Clear) + + QWebSettings::clearIconDatabase(); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsIconDatabase_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(QWebSettings::iconDatabasePath()); + else + QWebSettings::setIconDatabasePath(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_METHOD(WebSettingsIconDatabase_get, GB_STRING url) + + QIcon icon; + QSize size; + QSize max_size; + + icon = QWebSettings::iconForUrl(QSTRING_ARG(url)); + if (icon.isNull()) + { + GB.ReturnNull(); + return; + } + + foreach(size, icon.availableSizes()) + { + if ((size.width() * size.height()) > (max_size.width() * max_size.height())) + max_size = size; + } + + GB.ReturnObject(QT.CreatePicture(icon.pixmap(max_size))); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsCache_Path) + + if (READ_PROPERTY) + GB.ReturnString(_cache_path); + else + { + char *path = GB.FileName(PSTRING(), PLENGTH()); + QString qpath = QString(path); + QString root = QString(GB.System.Home()); + + if (root.at(root.length() - 1) != '/') + root += '/'; + root += ".cache/"; + if (!qpath.startsWith(root)) + { + GB.Error("Cache directory must be located inside ~/.cache"); + return; + } + + GB.FreeString(&_cache_path); + _cache_path = GB.NewZeroString(path); + set_cache(_cache_enabled); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsCache_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(_cache_enabled); + else + set_cache(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +static int _clear_error; +static char *_clear_path; + +static void remove_file(const char *path) +{ + if (rmdir(path) == 0) + return; + + if (errno == ENOTDIR) + { + if (unlink(path) == 0) + return; + } + + if (_clear_error == 0) + { + _clear_error = errno; + _clear_path = GB.NewZeroString(path); + } +} + +BEGIN_METHOD_VOID(WebSettingsCache_Clear) + + if (!_cache_path || !*_cache_path) + return; + + _clear_error = 0; + GB.BrowseDirectory(_cache_path, NULL, remove_file); + + if (_clear_error) + { + GB.Error("Unable to remove '&1': &2", _clear_path, strerror(_clear_error)); + GB.FreeString(&_clear_path); + } + + set_cache(_cache_enabled); + +END_METHOD +#endif + +//------------------------------------------------------------------------- + +BEGIN_METHOD(WebSettings_get, GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + int flag = VARG(flag); + + if (flag >= 0) + GB.ReturnBoolean(settings->testAttribute((QWebSettings::WebAttribute)flag)); + else + GB.ReturnBoolean(FALSE); + +END_METHOD + +BEGIN_METHOD(WebSettings_put, GB_BOOLEAN value; GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + int flag = VARG(flag); + + if (flag >= 0) + settings->setAttribute((QWebSettings::WebAttribute)flag, VARG(value)); + +END_METHOD + +/*BEGIN_METHOD(WebSettings_Reset, GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + int flag = VARG(flag); + + if (flag >= 0) + settings->resetAttribute((QWebSettings::WebAttribute)flag); + +END_METHOD*/ + +#if 0 +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettings_exit) + + GB.FreeString(&_cache_path); + +END_METHOD + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsProxy_Host) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.hostName()); + else + { + proxy.setHostName(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_User) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.user()); + else + { + proxy.setUser(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Password) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.password()); + else + { + proxy.setPassword(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Port) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.port()); + else + { + proxy.setPort(VPROP(GB_INTEGER)); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Type) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.type()); + else + { + int type = VPROP(GB_INTEGER); + if (type == QNetworkProxy::DefaultProxy || type == QNetworkProxy::NoProxy || type == QNetworkProxy::Socks5Proxy || type == QNetworkProxy::HttpProxy) + { + proxy.setType((QNetworkProxy::ProxyType)type); + nam->setProxy(proxy); + } + } + +END_PROPERTY +#endif + +/***************************************************************************/ + +#if 0 +GB_DESC WebSettingsIconDatabaseDesc[] = +{ + GB_DECLARE(".WebSettings.IconDatabase", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Clear", NULL, WebSettingsIconDatabase_Clear, NULL), + GB_STATIC_PROPERTY("Path", "s", WebSettingsIconDatabase_Path), + GB_STATIC_METHOD("_get", "Picture", WebSettingsIconDatabase_get, "(Url)s"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsCacheDesc[] = +{ + GB_DECLARE(".WebSettings.Cache", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Enabled", "b", WebSettingsCache_Enabled), + GB_STATIC_PROPERTY("Path", "s", WebSettingsCache_Path), + GB_STATIC_METHOD("Clear", NULL, WebSettingsCache_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsProxyDesc[] = +{ + GB_DECLARE(".WebSettings.Proxy", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Type", "i", WebSettingsProxy_Type), + GB_STATIC_PROPERTY("Host", "s", WebSettingsProxy_Host), + GB_STATIC_PROPERTY("Port", "i", WebSettingsProxy_Port), + GB_STATIC_PROPERTY("User", "s", WebSettingsProxy_User), + GB_STATIC_PROPERTY("Password", "s", WebSettingsProxy_Password), + + GB_END_DECLARE +}; +#endif + +GB_DESC WebSettingsFontsDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebSettings.Fonts"), + + GB_PROPERTY("Default", "s", WebSettingsFonts_Default), + GB_PROPERTY("Fixed", "s", WebSettingsFonts_Fixed), + GB_PROPERTY("Serif", "s", WebSettingsFonts_Serif), + GB_PROPERTY("SansSerif", "s", WebSettingsFonts_SansSerif), + GB_PROPERTY("Cursive", "s", WebSettingsFonts_Cursive), + GB_PROPERTY("Fantasy", "s", WebSettingsFonts_Fantasy), + GB_PROPERTY("Pictograph", "s", WebSettingsFonts_Pictograph), + + GB_PROPERTY("MinimumSize", "i", WebSettingsFonts_MinimumSize), + GB_PROPERTY("DefaultSize", "i", WebSettingsFonts_DefaultSize), + GB_PROPERTY("DefaultFixedSize", "i", WebSettingsFonts_DefaultFixedSize), + + GB_END_DECLARE +}; + +GB_DESC WebViewSettingsDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Settings"), + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + GB_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsDesc[] = +{ + GB_DECLARE("WebSettings", sizeof(CWEBSETTINGS)), GB_AUTO_CREATABLE(), GB_NOT_CREATABLE(), + + GB_CONSTANT("AutoLoadImages", "i", QWebSettings::AutoLoadImages), + GB_CONSTANT("Javascript", "i", QWebSettings::JavascriptEnabled), + GB_CONSTANT("JavascriptCanOpenWindows", "i", QWebSettings::JavascriptCanOpenWindows), + GB_CONSTANT("JavascriptCanAccessClipboard", "i", QWebSettings::JavascriptCanAccessClipboard), + GB_CONSTANT("LocalStorage", "i", QWebSettings::LocalStorageEnabled), + GB_CONSTANT("SpatialNavigation", "i", QWebSettings::SpatialNavigationEnabled), + GB_CONSTANT("LocalContentCanAccessFileUrls", "i", QWebSettings::LocalContentCanAccessFileUrls), + GB_CONSTANT("HyperlinkAuditing", "i", QWebSettings::HyperlinkAuditingEnabled), + GB_CONSTANT("Plugins", "i", QWebSettings::PluginsEnabled), + GB_CONSTANT("FullScreenSupport", "i", -1), + GB_CONSTANT("WebGL", "i", -1), + GB_CONSTANT("Accelerated2dCanvas", "i", -1), + GB_CONSTANT("PrintElementBackgrounds", "i", -1), + GB_CONSTANT("PlaybackRequiresUserGesture", "i", -1), + GB_CONSTANT("DnsPrefetch", "i", QWebSettings::DnsPrefetchEnabled), + + /*GB_CONSTANT("DefaultProxy", "i", QNetworkProxy::DefaultProxy), + GB_CONSTANT("NoProxy", "i", QNetworkProxy::NoProxy), + GB_CONSTANT("Socks5Proxy", "i", QNetworkProxy::Socks5Proxy), + GB_CONSTANT("HttpProxy", "i", QNetworkProxy::HttpProxy),*/ + + GB_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + /*GB_STATIC_PROPERTY_SELF("IconDatabase", ".WebSettings.IconDatabase"), + GB_STATIC_PROPERTY_SELF("Cache", ".WebSettings.Cache"), + GB_STATIC_PROPERTY_SELF("Proxy", ".WebSettings.Proxy"),*/ + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + //GB_STATIC_METHOD("_exit", NULL, WebSettings_exit, NULL), + +GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/webview/c_websettings.h b/gb.qt4/src/webview/c_websettings.h new file mode 100644 index 00000000..46df8aec --- /dev/null +++ b/gb.qt4/src/webview/c_websettings.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + c_websettings.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WEBSETTINGS_H +#define __C_WEBSETTINGS_H + +#include "main.h" + +#include +#include +#include + +typedef + struct { + GB_BASE ob; + } + CWEBSETTINGS; + +#ifndef __C_WEBSETTINGS_CPP + +/*extern GB_DESC WebSettingsIconDatabaseDesc[]; +extern GB_DESC WebSettingsCacheDesc[]; +extern GB_DESC WebSettingsProxyDesc[];*/ +extern GB_DESC WebViewSettingsDesc[]; +extern GB_DESC WebSettingsDesc[]; +extern GB_DESC WebSettingsFontsDesc[]; + +#else + +#define WEBVIEW ((QWebView *)((QT_WIDGET *)_object)->widget) + +#endif + +//void WEBSETTINGS_set_cache(QWebView *view, bool on); + +#endif diff --git a/gb.qt4/src/webview/c_webview.cpp b/gb.qt4/src/webview/c_webview.cpp new file mode 100644 index 00000000..60ee646d --- /dev/null +++ b/gb.qt4/src/webview/c_webview.cpp @@ -0,0 +1,751 @@ +/*************************************************************************** + + c_webview.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WEBVIEW_CPP + +#include +#include +#include +#include +#include +#include +#include + +#include "jsonwriter.h" + +/*#include "ccookiejar.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebhittest.h"*/ +#include "c_websettings.h" +#include "c_webview.h" + +#define HISTORY (WIDGET->history()) + +//--------------------------------------------------------------------------- + +DECLARE_EVENT(EVENT_TITLE); +DECLARE_EVENT(EVENT_URL); +DECLARE_EVENT(EVENT_ICON); +DECLARE_EVENT(EVENT_START); +DECLARE_EVENT(EVENT_PROGRESS); +DECLARE_EVENT(EVENT_FINISH); +DECLARE_EVENT(EVENT_ERROR); +DECLARE_EVENT(EVENT_LINK); +DECLARE_EVENT(EVENT_NEW_VIEW); + +static int EVENT_MENU = -1; + +//static QNetworkAccessManager *_network_access_manager = 0; +static CWEBVIEW *_network_access_manager_view = 0; +//static QT_COLOR_FUNC _old_after_set_color; +static bool _ignore_png_warnings = false; + + +/*QNetworkAccessManager *WEBVIEW_get_network_manager() +{ + if (!_network_access_manager) + { + _network_access_manager = new QNetworkAccessManager(); + _network_access_manager->setCookieJar(new MyCookieJar); + } + + return _network_access_manager; +}*/ + +/* +static QWebPage::WebAction get_action(const char *name) +{ + WEBVIEW_ACTION *p; + + for (p = _actions; p->name; p++) + { + if (!strcasecmp(p->name, name)) + return p->action; + } + + return QWebPage::NoWebAction; +} +*/ + +/*static void after_set_color(void *_object) +{ + if (!GB.Is(THIS, CLASS_WebView)) + { + if (_old_after_set_color) + (*_old_after_set_color)(THIS); + return; + } + + if (QT.GetBackgroundColor(THIS) == GB_COLOR_DEFAULT) + { + QPalette palette = WIDGET->palette(); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, true); + } + else + { + qDebug("after_set_color"); + QPalette palette = WIDGET->palette(); + palette.setBrush(QPalette::Base, Qt::transparent); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, false); + } +}*/ + +static void stop_view(void *_object) +{ + //fprintf(stderr, "stop_view\n"); + THIS->stopping = TRUE; + WIDGET->stop(); + THIS->stopping = FALSE; + THIS->cancel = FALSE; +} + +static void set_link(void *_object, const QString &link) +{ + GB.FreeString(&THIS->link); + THIS->link = QT.NewString(link); +} + +static void update_language(void *_object) +{ + // TODO: it seems that Qt4 QWebView does not support setting the language. +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(WebView_new, GB_OBJECT parent) + + int fd_save = -1; + + if (!_ignore_png_warnings) + { + int fd = ::open("/dev/null", O_RDWR); + fd_save = ::dup(2); + ::dup2(fd, 2); + ::close(fd); + } + + MyWebView *wid = new MyWebView(QT.GetContainer(VARG(parent))); + + if (!_ignore_png_warnings) + { + ::dup2(fd_save, 2); + ::close(fd_save); + _ignore_png_warnings = true; + + //_old_after_set_color = QT.AfterSetColor(after_set_color); + QWebSettings::globalSettings()->setFontFamily(QWebSettings::FixedFont, "monospace"); + } + + QT.InitWidget(wid, _object, false); + QT.SetWheelFlag(_object); + + //WEBVIEW_get_network_manager(); + + //wid->page()->setNetworkAccessManager(_network_access_manager); + wid->page()->setForwardUnsupportedContent(true); + + QObject::connect(wid, SIGNAL(loadFinished(bool)), &CWebView::manager, SLOT(loadFinished(bool))); + QObject::connect(wid, SIGNAL(loadProgress(int)), &CWebView::manager, SLOT(loadProgress(int))); + QObject::connect(wid, SIGNAL(loadStarted()), &CWebView::manager, SLOT(loadStarted())); + //QObject::connect(wid, SIGNAL(selectionChanged()), &CWebView::manager, SLOT(selectionChanged())); + //QObject::connect(wid, SIGNAL(statusBarMessage(const QString &)), &CWebView::manager, SLOT(statusBarMessage(const QString &))); + QObject::connect(wid, SIGNAL(titleChanged(const QString &)), &CWebView::manager, SLOT(titleChanged(const QString &))); + + //QObject::connect(wid->page(), SIGNAL(frameCreated(QWebFrame *)), &CWebView::manager, SLOT(frameCreated(QWebFrame *))); + //QObject::connect(wid->page(), SIGNAL(downloadRequested(QNetworkRequest)), &CWebView::manager, SLOT(downloadRequested(QNetworkRequest))); + //QObject::connect(wid->page(), SIGNAL(unsupportedContent(QNetworkReply*)), &CWebView::manager, SLOT(handleUnsupportedContent(QNetworkReply*))); + + QObject::connect(wid, SIGNAL(iconChanged()), &CWebView::manager, SLOT(iconChanged())); + QObject::connect(wid->page(), SIGNAL(linkHovered(const QString &, const QString &, const QString &)), &CWebView::manager, + SLOT(linkHovered(const QString &, const QString &, const QString &))); + QObject::connect(wid->page()->mainFrame(), SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + + /*QObject::connect(wid->page()->networkAccessManager(), SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)), &CWebView::manager, + SLOT(authenticationRequired(QNetworkReply *, QAuthenticator *)));*/ + + update_language(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_free) + + if (_network_access_manager_view == THIS) + _network_access_manager_view = 0; + + //GB.FreeString(&THIS->status); + //GB.FreeString(&THIS->userAgent); + GB.FreeString(&THIS->link); + GB.FreeString(&THIS->language); + GB.Unref(POINTER(&THIS->icon)); + GB.Unref(POINTER(&THIS->new_view)); + +END_METHOD + +/*BEGIN_METHOD_VOID(WebView_exit) + + delete _network_access_manager; + +END_METHOD*/ + +BEGIN_PROPERTY(WebView_Url) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->url().toString()); + else + { + QString url = QSTRING_PROP(); + stop_view(THIS); + set_link(THIS, url); + WIDGET->setUrl(url); + } + +END_PROPERTY + +BEGIN_METHOD(WebView_SetHtml, GB_STRING html; GB_STRING root) + + if (!MISSING(root)) + { + QUrl url(QSTRING_ARG(root)); + WIDGET->setHtml(QSTRING_ARG(html), url); + } + else + WIDGET->setHtml(QSTRING_ARG(html)); + +END_METHOD + +BEGIN_PROPERTY(WebView_Icon) + + if (!THIS->icon) + { + QIcon icon = WIDGET->icon(); + + if (icon.isNull()) + icon = QWebSettings::iconForUrl(WIDGET->url()); + + if (!icon.isNull()) + { + THIS->icon = QT.CreatePicture(icon.pixmap(16, 16)); + GB.Ref(THIS->icon); + } + } + + GB.ReturnObject(THIS->icon); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Zoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->zoomFactor()); + else + WIDGET->setZoomFactor(VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Title) + + RETURN_NEW_STRING(WIDGET->title()); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebView_Back) + + WIDGET->back(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Forward) + + WIDGET->forward(); + +END_METHOD + +BEGIN_METHOD(WebView_Reload, GB_BOOLEAN bypass) + + bool bypass = VARGOPT(bypass, false); + stop_view(THIS); + if (bypass) + WIDGET->page()->triggerAction(QWebPage::ReloadAndBypassCache); + else + WIDGET->reload(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Stop) + + stop_view(THIS); + +END_METHOD + +BEGIN_PROPERTY(WebView_Progress) + + GB.ReturnFloat(THIS->progress / 100.0); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_NewView) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->new_view); + else + GB.StoreObject(PROP(GB_OBJECT), &THIS->new_view); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Link) + + GB.ReturnString(THIS->link); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebView_Clear) + + delete WIDGET->page(); + WIDGET->setPage(new QWebPage(WIDGET)); + QObject::connect(WIDGET->page(), SIGNAL(linkHovered(const QString &, const QString &, const QString &)), &CWebView::manager, + SLOT(linkHovered(const QString &, const QString &, const QString &))); + QObject::connect(WIDGET->page()->mainFrame(), SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + +END_METHOD + +BEGIN_METHOD(WebView_ExecJavascript, GB_STRING script) + + if (LENGTH(script) == 0) + return; + + QVariant result = WIDGET->page()->currentFrame()->evaluateJavaScript(QSTRING_ARG(script)); + QByteArray json; + + JSONWRITER_valueToJson(result, json); + + GB.ReturnNewString(json.constData(), json.size()); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_GetHtml) + + RETURN_NEW_STRING(WIDGET->page()->mainFrame()->toHtml()); + +END_METHOD + +BEGIN_PROPERTY(WebView_Language) + + if (READ_PROPERTY) + GB.ReturnString(THIS->language); + else + { + GB.StoreString(PROP(GB_STRING), &THIS->language); + update_language(THIS); + } + +END_PROPERTY + +//------------------------------------------------------------------------- + +static QWebHistoryItem get_item(QWebHistory *history, int index) +{ + if (index == 0) + return history->currentItem(); + + QList list; + + if (index > 0) + list = history->forwardItems(history->count()); + else + { + list = history->backItems(history->count()); + index = (-index); + } + + return list.at(index); +} + +BEGIN_PROPERTY(WebViewHistoryItem_Title) + + QWebHistoryItem item = get_item(HISTORY, THIS->history); + + if (item.isValid()) + RETURN_NEW_STRING(item.title()); + else + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistoryItem_Url) + + QWebHistoryItem item = get_item(HISTORY, THIS->history); + + if (item.isValid()) + RETURN_NEW_STRING(item.url().toString()); + else + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebViewHistoryItem_GoTo) + + QWebHistoryItem item = get_item(HISTORY, THIS->history); + + if (item.isValid()) + HISTORY->goToItem(item); + +END_METHOD + +BEGIN_METHOD_VOID(WebViewHistory_Clear) + + HISTORY->clear(); + +END_METHOD + +BEGIN_METHOD(WebViewHistory_get, GB_INTEGER index) + + int index = VARG(index); + + if (index > 0 && index > HISTORY->forwardItems(HISTORY->count()).count()) + GB.ReturnNull(); + else if (index < 0 && (-index) > HISTORY->backItems(HISTORY->count()).count()) + GB.ReturnNull(); + else + { + THIS->history = index; + RETURN_SELF(); + } + +END_METHOD + +BEGIN_PROPERTY(WebViewHistory_CanGoBack) + + GB.ReturnBoolean(HISTORY->canGoBack()); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_CanGoForward) + + GB.ReturnBoolean(HISTORY->canGoForward()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +class MyCookieJar : public QNetworkCookieJar +{ + Q_OBJECT + +public: + + MyCookieJar(QObject *parent = 0); + + QList allCookies () const { return QNetworkCookieJar::allCookies(); } + void setAllCookies(const QList &cookieList) { QNetworkCookieJar::setAllCookies(cookieList); } + + //virtual QList cookiesForUrl(const QUrl & url) const; + //virtual bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); +}; + +BEGIN_METHOD_VOID(WebView_Cookies_Clear) + + QList list; + static_cast(WIDGET->page()->networkAccessManager()->cookieJar())->setAllCookies(list); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC WebViewHistoryItemDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History.Item"), + + GB_PROPERTY_READ("Title", "s", WebViewHistoryItem_Title), + GB_PROPERTY_READ("Url", "s", WebViewHistoryItem_Url), + GB_METHOD("GoTo", NULL, WebViewHistoryItem_GoTo, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewHistoryDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History"), + + GB_METHOD("Clear", NULL, WebViewHistory_Clear, NULL), + GB_METHOD("_get", ".WebView.History.Item", WebViewHistory_get, "(Index)i"), + GB_PROPERTY_READ("CanGoBack", "b", WebViewHistory_CanGoBack), + GB_PROPERTY_READ("CanGoForward", "b", WebViewHistory_CanGoForward), + + GB_END_DECLARE +}; + +GB_DESC WebViewCookiesDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Cookies"), + + GB_METHOD("Clear", NULL, WebView_Cookies_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewDesc[] = +{ + GB_DECLARE("WebView", sizeof(CWEBVIEW)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, WebView_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, WebView_free, NULL), + + GB_PROPERTY("Url", "s", WebView_Url), + GB_PROPERTY("Title", "s", WebView_Title), + GB_PROPERTY("Zoom", "f", WebView_Zoom), + GB_PROPERTY_READ("Icon", "Picture", WebView_Icon), + GB_PROPERTY_READ("Progress", "f", WebView_Progress), + GB_PROPERTY("NewView", "WebView", WebView_NewView), + GB_PROPERTY_READ("Link", "s", WebView_Link), + GB_PROPERTY("Language", "s", WebView_Language), + + GB_METHOD("SetHtml", NULL, WebView_SetHtml, "(Html)s[(Root)s]"), + GB_METHOD("GetHtml", "s", WebView_GetHtml, NULL), + + GB_METHOD("Clear", NULL, WebView_Clear, NULL), + + GB_METHOD("Back", NULL, WebView_Back, NULL), + GB_METHOD("Forward", NULL, WebView_Forward, NULL), + GB_METHOD("Reload", NULL, WebView_Reload, "[(BypassCache)b]"), + GB_METHOD("Stop", NULL, WebView_Stop, NULL), + + GB_METHOD("ExecJavascript", "s", WebView_ExecJavascript, "(Javascript)s"), + + GB_PROPERTY_SELF("History", ".WebView.History"), + GB_PROPERTY_SELF("Settings", ".WebView.Settings"), + GB_PROPERTY_SELF("Cookies", ".WebView.Cookies"), + + GB_CONSTANT("_Properties", "s", "*,Url,Zoom=1"), + GB_CONSTANT("_Group", "s", "View"), + + GB_EVENT("Title", NULL, NULL, &EVENT_TITLE), + GB_EVENT("Url", NULL, NULL, &EVENT_URL), + GB_EVENT("Icon", NULL, NULL, &EVENT_ICON), + GB_EVENT("Start", NULL, NULL, &EVENT_START), + GB_EVENT("Progress", NULL, NULL, &EVENT_PROGRESS), + GB_EVENT("Finish", NULL, NULL, &EVENT_FINISH), + GB_EVENT("Error", NULL, NULL, &EVENT_ERROR), + GB_EVENT("Link", NULL, NULL, &EVENT_LINK), + GB_EVENT("NewView", NULL, NULL, &EVENT_NEW_VIEW), + + GB_END_DECLARE +}; + +//------------------------------------------------------------------------- + +MyWebPage::MyWebPage(QObject *parent) : QWebPage(parent) +{ +} + +/*QString MyWebPage::userAgentForUrl(const QUrl& url) const +{ + MyWebView *view = (MyWebView *)parent(); + void *_object = QT.GetObject(view); + + if (THIS->userAgent) + return (const char *)THIS->userAgent; + else + return QWebPage::userAgentForUrl(url); +};*/ + + +MyWebView::MyWebView(QWidget *parent) : QWebView(parent) +{ + //settings()->setFontFamily(QWebSettings::FixedFont, "monospace"); + setPage(new MyWebPage(this)); +} + +QWebView *MyWebView::createWindow(QWebPage::WebWindowType type) +{ + void *_object = QT.GetObject(this); + QWebView *new_view; + + if (GB.Raise(THIS, EVENT_NEW_VIEW, 0)) + return NULL; + + if (!THIS->new_view) + return NULL; + + new_view = (QWebView *)(((CWEBVIEW *)THIS->new_view)->widget.widget); + GB.Unref(POINTER(&THIS->new_view)); + THIS->new_view = NULL; + return new_view; +} + +void MyWebView::contextMenuEvent(QContextMenuEvent *event) +{ + void *_object = QT.GetObject(this); + + if (EVENT_MENU < 0) + EVENT_MENU = GB.GetEvent(GB.GetClass(THIS), "Menu"); + + if (!GB.CanRaise(THIS, EVENT_MENU)) + QWebView::contextMenuEvent(event); + + event->accept(); +} + +/***************************************************************************/ + +CWebView CWebView::manager; + +void CWebView::loadFinished(bool ok) +{ + GET_SENDER(); + + //fprintf(stderr, "loadFinished %d (%d)\n", ok, THIS->stopping); + + THIS->progress = 100; + + if (ok) + GB.Raise(THIS, EVENT_FINISH, 0); + else if (!THIS->stopping) + GB.Raise(THIS, EVENT_ERROR, 0); + + GB.FreeString(&THIS->link); +} + +void CWebView::loadProgress(int progress) +{ + GET_SENDER(); + + if (THIS->cancel || THIS->progress == progress) + return; + + THIS->progress = progress; + GB.Raise(THIS, EVENT_PROGRESS, 0); +} + +void CWebView::loadStarted() +{ + GET_SENDER(); + + THIS->progress = 0; + _network_access_manager_view = THIS; + THIS->cancel = GB.Raise(THIS, EVENT_START, 0); + if (THIS->cancel) + stop_view(THIS); + else + GB.Raise(THIS, EVENT_PROGRESS, 0); +} + +/*void CWebView::selectionChanged() +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_SELECT, 0); +} + +void CWebView::statusBarMessage(const QString &text) +{ + GET_SENDER(); + GB.FreeString(&THIS->status); + THIS->status = NEW_STRING(text); + GB.Raise(THIS, EVENT_STATUS, 0); +}*/ + +void CWebView::titleChanged(const QString &title) +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_TITLE, 0); +} + +void CWebView::linkHovered(const QString &link, const QString &title, const QString &textContent) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + + if (THIS) + { + set_link(THIS, link); + GB.Raise(THIS, EVENT_LINK, 0); + } +} + +/*void CWebView::frameCreated(QWebFrame *frame) +{ + QObject::connect(frame, SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + GB.Raise(THIS, EVENT_NEW_FRAME, 1, GB_T_OBJECT, CWEBFRAME_get(frame)); +} + +void CWebView::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) +{ + //qDebug("CWebView::authenticationRequired: %p", _network_access_manager_view); + + void *_object = _network_access_manager_view; //QT.GetObject((QWidget *)((QNetworkAccessManager*)sender())->parent()); + if (!THIS) + return; + + THIS->reply = reply; + THIS->authenticator = authenticator; + + GB.Raise(THIS, EVENT_AUTH, 0); + + THIS->reply = 0; + THIS->authenticator = 0; +}*/ + +void CWebView::iconChanged() +{ + GET_SENDER(); + GB.Unref(POINTER(&THIS->icon)); + THIS->icon = NULL; + GB.RaiseLater(THIS, EVENT_ICON); +} + +void CWebView::urlChanged(const QUrl &) +{ + QWebFrame *frame = (QWebFrame *)sender(); + void *_object = QT.GetObject(frame->page()->view()); + //GB.Raise(THIS, EVENT_CLICK, 1, GB_T_OBJECT, CWEBFRAME_get(frame)); + GB.Raise(THIS, EVENT_URL, 0); +} + +/*void CWebView::downloadRequested(const QNetworkRequest &request) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + download = WEB_create_download(_network_access_manager->get(request)); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); +} + +void CWebView::handleUnsupportedContent(QNetworkReply *reply) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + if (reply->error() == QNetworkReply::NoError) + { + download = WEB_create_download(reply); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); + } + else + delete reply; +}*/ diff --git a/gb.qt4/src/webview/c_webview.h b/gb.qt4/src/webview/c_webview.h new file mode 100644 index 00000000..d23ba950 --- /dev/null +++ b/gb.qt4/src/webview/c_webview.h @@ -0,0 +1,121 @@ +/*************************************************************************** + + c_webview.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WEBVIEW_H +#define __C_WEBVIEW_H + +#include +#include +#include +#include +#include +#include +#include + +//#include "cwebdownload.h" +#include "main.h" + +#ifndef __C_WEBVIEW_CPP + +extern GB_DESC WebViewDesc[]; +extern GB_DESC WebViewHistoryDesc[]; +extern GB_DESC WebViewHistoryItemDesc[]; +extern GB_DESC WebViewCookiesDesc[]; + +#else + +#define THIS ((CWEBVIEW *)_object) +#define WIDGET ((MyWebView *)((QT_WIDGET *)_object)->widget) + +#endif + +class MyWebPage : public QWebPage +{ + Q_OBJECT + +public: + + MyWebPage(QObject *parent); + +/*protected: + + virtual QString userAgentForUrl(const QUrl& url) const;*/ +}; + +class MyWebView : public QWebView +{ + Q_OBJECT + +public: + + MyWebView(QWidget *parent); + +protected: + + virtual QWebView *createWindow(QWebPage::WebWindowType type); + virtual void contextMenuEvent(QContextMenuEvent *event); +}; + +typedef + struct + { + QT_WIDGET widget; + QT_PICTURE icon; + void *new_view; + char *link; + char *language; + int history; + int progress; + unsigned cancel : 1; + unsigned stopping : 1; + } + CWEBVIEW; + +class CWebView : public QObject +{ + Q_OBJECT + +public: + + static CWebView manager; + +public slots: + + void iconChanged(); + void loadFinished(bool ok); + void loadProgress(int progress); + void loadStarted(); + //void selectionChanged(); + //void statusBarMessage(const QString &text); + void titleChanged(const QString &title); + void linkHovered(const QString &link, const QString &title, const QString &textContent); + //void frameCreated(QWebFrame *); + //void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + void urlChanged(const QUrl &); + //void downloadRequested(const QNetworkRequest &); + //void handleUnsupportedContent(QNetworkReply*); +}; + +//QNetworkAccessManager *WEBVIEW_get_network_manager(); + +#endif diff --git a/gb.qt4/src/webview/gb.qt4.webview.component b/gb.qt4/src/webview/gb.qt4.webview.component new file mode 100644 index 00000000..ba4504f0 --- /dev/null +++ b/gb.qt4/src/webview/gb.qt4.webview.component @@ -0,0 +1,10 @@ +[Component] +Key=gb.qt4.webkit +Author=Benoît Minisini +Type=Form +Require=gb.qt4 +State=Stable + +[Form] +Control=WebView + diff --git a/gb.qt4/src/webview/jsonwriter.cpp b/gb.qt4/src/webview/jsonwriter.cpp new file mode 100644 index 00000000..baf8d708 --- /dev/null +++ b/gb.qt4/src/webview/jsonwriter.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + + jsonwriter.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + + +#include +#include +#include + +#include "jsonwriter.h" + +static inline uchar hexa_digit(uchar c) +{ + return (c < 0xa ? '0' + c : 'a' + c - 0xa); +} + +static QByteArray escapedString(const QString &s) +{ + QByteArray utf8 = s.toUtf8(); + QByteArray ba; + int i; + + for (i = 0; i < utf8.size(); i++) + { + uchar c = utf8.at(i); + if (c < 0x20 || c == 0x22 || c == 0x5C) + { + ba += '\\'; + switch (c) + { + case 0x22: ba += '"'; break; + case 0x5c: ba += '\\'; break; + case 0x8: ba += 'b'; break; + case 0xc: ba += 'f'; break; + case 0xa: ba += 'n'; break; + case 0xd: ba += 'r'; break; + case 0x9: ba += 't'; break; + default: + ba += "u00"; + ba += hexa_digit(c >> 4); + ba += hexa_digit(c & 0xf); + } + } + ba += c; + } + + return ba; +} + +static void arrayContentToJson(const QVariantList &v, QByteArray &json) +{ + uint i; + uint size = v.size(); + + if (size == 0) + return; + + size--; + for(i = 0; i < size; i++) + { + JSONWRITER_valueToJson(v.at(i), json); + json += ','; + } + + JSONWRITER_valueToJson(v.at(i), json); +} + +static void objectContentToJson(const QVariantMap &v, QByteArray &json) +{ + uint size = v.size(); + bool comma; + + if (size == 0) + return; + + comma = false; + QMapIterator i(v); + while (i.hasNext()) + { + i.next(); + if (comma) + json += ','; + else + comma = true; + json += '"'; + json += escapedString(i.key()); + json += "\":"; + JSONWRITER_valueToJson(i.value(), json); + } +} + +void JSONWRITER_valueToJson(const QVariant &v, QByteArray &json) +{ + if (v.isNull()) + { + json += "null"; + return; + } + + switch (v.type()) + { + case QVariant::Bool: + json += v.toBool() ? "true" : "false"; + break; + case QVariant::ULongLong: + json += QByteArray::number(v.value()); + case QVariant::UInt: + json += QByteArray::number(v.value()); + case QVariant::Double: + { + const double d = v.toDouble(); + if (std::isnan(d) || std::isinf(d)) + { + json += "null"; + } + else + { + QString str = QByteArray::number(d, 'g', 12); + /*if (!str.contains( '.' ) && !str.contains('e')) + str += ".0";*/ + json += str; + } + break; + } + case QVariant::String: + json += '"'; + json += escapedString(v.toString()); + json += '"'; + break; + case QVariant::List: + json += '['; + arrayContentToJson(v.toList(), json); + json += ']'; + break; + case QVariant::Map: + json += '{'; + objectContentToJson(v.toMap(), json); + json += '}'; + break; + default: + json += "undefined"; + } +} diff --git a/gb.qt4/src/webview/jsonwriter.h b/gb.qt4/src/webview/jsonwriter.h new file mode 100644 index 00000000..84174c45 --- /dev/null +++ b/gb.qt4/src/webview/jsonwriter.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + jsonwriter.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JSONWRITER_H +#define __JSONWRITER_H + +void JSONWRITER_valueToJson(const QVariant &v, QByteArray &json); + +#endif diff --git a/gb.qt4/src/webview/main.cpp b/gb.qt4/src/webview/main.cpp new file mode 100644 index 00000000..174d3935 --- /dev/null +++ b/gb.qt4/src/webview/main.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** + + main.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include + +#include + +/*#include "ccookiejar.h" +#include "cwebhittest.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebdownload.h"*/ +#include "c_websettings.h" +#include "c_webview.h" +#include "main.h" + +GB_CLASS CLASS_WebView; + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + /*WebDownloadDesc, + WebDownloadsDesc, + WebHitTestDesc, + CookieDesc, + WebSettingsIconDatabaseDesc, + WebSettingsCacheDesc, + WebSettingsProxyDesc, + WebSettingsDesc, + WebElementStyleDesc, + WebElementDesc, + WebFrameChildrenDesc, + WebFrameDesc, + WebViewAuthDesc,*/ + WebSettingsFontsDesc, + WebSettingsDesc, + WebViewSettingsDesc, + WebViewHistoryItemDesc, + WebViewHistoryDesc, + WebViewCookiesDesc, + WebViewDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.qt4", QT_INTERFACE_VERSION, &QT); + CLASS_WebView = GB.FindClass("WebView"); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + +void MAIN_return_qvariant(const QVariant &result) +{ + GB_DATE date; + GB_DATE_SERIAL ds; + QDateTime qdate; + + switch (result.type()) + { + case QVariant::Bool: + GB.ReturnBoolean(result.toBool()); + break; + + case QVariant::Date: + case QVariant::DateTime: + qdate = result.toDateTime(); + ds.year = qdate.date().year(); + ds.month = qdate.date().month(); + ds.day = qdate.date().day(); + ds.hour = qdate.time().hour(); + ds.min = qdate.time().minute(); + ds.sec = qdate.time().second(); + ds.msec = qdate.time().msec(); + GB.MakeDate(&ds, &date); + GB.ReturnDate(&date); + break; + + case QVariant::Double: + GB.ReturnFloat(result.toDouble()); + break; + + case QVariant::Int: + case QVariant::UInt: + GB.ReturnInteger(result.toInt()); + break; + + case QVariant::LongLong: + case QVariant::ULongLong: + GB.ReturnLong(result.toLongLong()); + break; + + case QVariant::String: + RETURN_NEW_STRING(result.toString()); + break; + + // TODO: Handle these three datatypes + case QVariant::Hash: + case QVariant::List: + case QVariant::RegExp: + default: + GB.ReturnNull(); + break; + } + + GB.ReturnConvVariant(); +} + diff --git a/gb.qt4/src/webview/main.h b/gb.qt4/src/webview/main.h new file mode 100644 index 00000000..2de1c90a --- /dev/null +++ b/gb.qt4/src/webview/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.qt.h" + +#include + +#ifndef __MAIN_CPP +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +extern GB_CLASS CLASS_WebView; +#endif + +void MAIN_return_qvariant(const QVariant &result); + +#endif diff --git a/gb.qt4/src/x11.c b/gb.qt4/src/x11.c new file mode 100644 index 00000000..7515b0cd --- /dev/null +++ b/gb.qt4/src/x11.c @@ -0,0 +1,801 @@ +/*************************************************************************** + + x11.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __X11_C + +#include +#include +#include +#include +#include +#include + +#include "x11.h" + +extern const GB_INTERFACE *GB_PTR; +#define GB (*GB_PTR) + +#define MAX_WINDOW_PROP 16 + +Atom X11_atom_net_wm_state; +Atom X11_atom_net_wm_state_above; +Atom X11_atom_net_wm_state_below; +Atom X11_atom_net_wm_state_stays_on_top; +Atom X11_atom_net_wm_state_skip_taskbar; +Atom X11_atom_net_wm_window_type; +Atom X11_atom_net_wm_window_type_normal; +Atom X11_atom_net_wm_window_type_utility; +Atom X11_atom_net_wm_desktop; +Atom X11_atom_net_wm_user_time; +Atom X11_atom_net_current_desktop; +Atom X11_atom_net_workarea = None; +Atom X11_atom_motif_wm_hints = None; +Atom X11_atom_net_system_tray = None; + +Atom X11_atom_net_supported; +Atom *_supported = NULL; + +static Display *_display; +static Window _root; + +static bool _atom_init = FALSE; + +typedef + struct { + char *name; + Atom atom; + } + X11_ATOM; + +typedef + struct { + int count; + Atom atoms[MAX_WINDOW_PROP]; + bool changed; + } + X11_PROPERTY; + +static Window _window = 0; +static bool _window_visible; +static X11_PROPERTY _window_prop; +static X11_PROPERTY _window_save[2]; + +static char *_property_value = NULL; + +static X11_ATOM _atoms[] = +{ + {"_NET_WM_WINDOW_TYPE_NORMAL"}, + {"_NET_WM_WINDOW_TYPE_DESKTOP"}, + {"_NET_WM_WINDOW_TYPE_DOCK"}, + {"_NET_WM_WINDOW_TYPE_TOOLBAR"}, + {"_NET_WM_WINDOW_TYPE_MENU"}, + {"_NET_WM_WINDOW_TYPE_UTILITY"}, + {"_NET_WM_WINDOW_TYPE_SPLASH"}, + {"_NET_WM_WINDOW_TYPE_DIALOG"}, + {"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"}, + {"_NET_WM_WINDOW_TYPE_POPUP_MENU"}, + {"_NET_WM_WINDOW_TYPE_TOOLTIP"}, + {"_NET_WM_WINDOW_TYPE_NOTIFICATION"}, + {"_NET_WM_WINDOW_TYPE_COMBO"}, + {"_NET_WM_WINDOW_TYPE_DND"}, + {NULL} +}; + +typedef + struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } + MwmHints; + +static void init_atoms() +{ + if (_atom_init) + return; + + X11_atom_net_current_desktop = XInternAtom(_display, "_NET_CURRENT_DESKTOP", True); + + X11_atom_net_wm_state = XInternAtom(_display, "_NET_WM_STATE", True); + X11_atom_net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", True); + X11_atom_net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", True); + X11_atom_net_wm_state_stays_on_top = XInternAtom(_display, "_NET_WM_STATE_STAYS_ON_TOP", True); + X11_atom_net_wm_state_skip_taskbar = XInternAtom(_display, "_NET_WM_STATE_SKIP_TASKBAR", True); + X11_atom_net_wm_desktop = XInternAtom(_display, "_NET_WM_DESKTOP", True); + X11_atom_net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", True); + X11_atom_net_wm_window_type_normal = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_NORMAL", True); + X11_atom_net_wm_window_type_utility = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_UTILITY", True); + X11_atom_net_wm_user_time = XInternAtom(_display, "_NET_WM_USER_TIME", True); + + X11_atom_net_supported = XInternAtom(_display, "_NET_SUPPORTED", True); + + _atom_init = TRUE; +} + +static Atom get_atom(int index) +{ + X11_ATOM *a = &_atoms[index]; + + if (!a->atom) + a->atom = XInternAtom(_display, a->name, True); + + return a->atom; +} + +static int find_atom(Atom atom) +{ + int i = 0; + X11_ATOM *p = _atoms; + + while (p->name) + { + if (!p->atom) + p->atom = XInternAtom(_display, p->name, True); + if (p->atom == atom) + return i; + p++; + i++; + } + return (-1); +} + +#define PROPERTY_START_READ 1024 +#define PROPERTY_NEXT_READ 1024 + +#if 0 + // On 64 bits OS, format 32 are actually long, i.e. int padded to 64 bits! + #if OS_64BITS + if (format == 32) + { + int *p = (int *)_property_value; + for (i = 0; i < count; i++) + p[i] = *((long *)data + i); + } + else + #endif + { + memcpy(_property_value, (char *)data, count * size); + } +#endif + +void X11_flush() +{ + XFlush(_display); +} + +char *X11_get_property(Window wid, Atom prop, Atom *type, int *format, int *pcount) +{ + uchar *data; + unsigned long count; + unsigned long after; + unsigned long offset; + int size, offset_size; + + *pcount = 0; + + if (XGetWindowProperty(_display, wid, prop, 0, PROPERTY_START_READ / sizeof(int32_t), + False, AnyPropertyType, type, format, + &count, &after, &data) != Success) + return NULL; + + *pcount += count; + + size = *format == 32 ? sizeof(long) : ( *format == 16 ? sizeof(short) : 1 ); + offset_size = *format == 32 ? sizeof(int32_t) : ( *format == 16 ? sizeof(short) : 1 ); + + //fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld\n", *format, size, count, after); + + if (_property_value) + GB.FreeString(&_property_value); + _property_value = GB.NewString((char *)data, count * size); + XFree(data); + + offset = count * offset_size / sizeof(int32_t); + + while (after) + { + //fprintf(stderr, "X11_get_property: offset = %ld read = %ld\n", offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t)); + + if (XGetWindowProperty(_display, wid, prop, offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t), + False, AnyPropertyType, type, format, + &count, &after, &data) != Success) + return NULL; + + *pcount += count; + offset += count * offset_size / sizeof(int32_t); + + //fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld next offset = %ld\n", *format, size, count, after, offset); + + _property_value = GB.AddString(_property_value, (char *)data, count * size); + XFree(data); + } + + return _property_value; +} + +static char *get_property(Window wid, Atom prop, int *count) +{ + Atom type; + int format; + + return X11_get_property(wid, prop, &type, &format, count); +} + +static void load_window_state(Window win, Atom prop) +{ + int length; + char *data; + + _window_prop.count = 0; + _window_prop.changed = FALSE; + + //get_property(win, X11_atom_net_wm_state, MAX_WINDOW_STATE * sizeof(Atom), &data, &length); + data = get_property(win, prop, &length); + + if (length > MAX_WINDOW_PROP) + length = MAX_WINDOW_PROP; + + _window_prop.count = length; + if (data) + memcpy(_window_prop.atoms, data, length * sizeof(Atom)); +} + +static void save_window_state(Window win, Atom prop) +{ + if (_window_prop.changed) + { + //fprintf(stderr, "XChangeProperty: %ld %ld\n", (long)win, (long)prop); + XChangeProperty(_display, win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)_window_prop.atoms, _window_prop.count); + } +} + +static bool has_window_state(Atom prop) +{ + int i; + + for (i = 0; i < _window_prop.count; i++) + { + if (_window_prop.atoms[i] == prop) + return TRUE; + } + + return FALSE; +} + +static void set_window_state(Atom prop) +{ + if (has_window_state(prop)) + return; + + if (_window_prop.count == MAX_WINDOW_PROP) + { + fprintf(stderr, "X11: set_window_state: Too many properties in window\n"); + return; + } + + _window_prop.atoms[_window_prop.count++] = prop; + _window_prop.changed = TRUE; +} + +static void clear_window_state(Atom prop) +{ + int i; + + for (i = 0; i < _window_prop.count; i++) + { + if (_window_prop.atoms[i] == prop) + { + _window_prop.count--; + + for (; i < _window_prop.count; i++) + _window_prop.atoms[i] = _window_prop.atoms[i + 1]; + + _window_prop.changed = TRUE; + return; + } + } +} + + +static void init_net_supported() +{ + char *data; + int count; + + if (_supported) + GB.FreeArray(&_supported); + + data = get_property(_root, X11_atom_net_supported, &count); + if (!data) + return; + + GB.NewArray(&_supported, sizeof(Atom), count); + memcpy(_supported, data, sizeof(Atom) * count); +} + + +void X11_init(Display *display, Window root) +{ + _display = display; + _root = root; + init_atoms(); + init_net_supported(); +} + +void X11_exit() +{ + if (_supported) + GB.FreeArray(&_supported); + if (_property_value) + GB.FreeString(&_property_value); +} + + +void X11_window_change_property(Atom property, bool set) +{ + XEvent e; + + if (_window_visible) + { + e.xclient.type = ClientMessage; + e.xclient.message_type = X11_atom_net_wm_state; + e.xclient.display = _display; + e.xclient.window = _window; + e.xclient.format = 32; + e.xclient.data.l[0] = set ? 1 : 0; + e.xclient.data.l[1] = property; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + + XSendEvent(_display, _root, False, (SubstructureRedirectMask | SubstructureNotifyMask), &e); + } + else + { + if (set) + set_window_state(property); + else + clear_window_state(property); + } +} + +void X11_window_change_begin(Window window, bool visible) +{ + _window = window; + _window_visible = visible; + + if (!visible) + load_window_state(window, X11_atom_net_wm_state); +} + +void X11_window_change_end() +{ + if (!_window_visible) + save_window_state(_window, X11_atom_net_wm_state); + + XFlush(_display); + _window = (Window)0; +} + + +bool X11_window_has_property(Window window, Atom property) +{ + load_window_state(window, X11_atom_net_wm_state); + return has_window_state(property); +} + +void X11_sync(void) +{ + XSync(_display, False); +} + +void X11_window_save_properties(Window window) +{ + load_window_state(window, X11_atom_net_wm_state); + _window_save[0] = _window_prop; + //load_window_state(window, X11_atom_net_wm_window_type); + //_window_save[1] = _window_prop; +} + +void X11_window_restore_properties(Window window) +{ + _window_prop = _window_save[0]; + save_window_state(window, X11_atom_net_wm_state); + //_window_prop = _window_save[1]; + //save_window_state(window, X11_atom_net_wm_window_type); +} + +void X11_window_startup(Window window, int x, int y, int w, int h) +{ + XSizeHints s; + + s.flags = USPosition | PPosition | USSize | PSize; + + s.x = x; + s.y = y; + s.width = w; + s.height = h; + + XSetWMNormalHints(_display, window, &s); +} + +// ### Do not forget to call XFree() on window_list once finished with it + +void X11_find_windows(Window **window_list, int *count) +{ + static Atom _net_client_list = 0; + + if (!_net_client_list) + _net_client_list = XInternAtom(_display, "_NET_CLIENT_LIST", True); + + *window_list = (Window *)get_property(_root, _net_client_list, count); +} + +// ### Do not forget to call XFree() on result once finished with it + +void X11_get_window_title(Window window, char **result, int *length) +{ + int l; + *result = get_property(window, XA_WM_NAME, &l); + *length = l; +} + +void X11_get_window_class(Window window, char **result, int *length) +{ + int l; + *result = get_property(window, XA_WM_CLASS, &l); + *length = l; +} + +void X11_get_window_role(Window window, char **result, int *length) +{ + static Atom wm_window_role = (Atom)0; + int l; + + if (!wm_window_role) + wm_window_role = XInternAtom(_display, "WM_WINDOW_ROLE", True); + + *result = get_property(window, wm_window_role, &l); + *length = l; +} + + +// Makes a tool window + +void X11_set_window_tool(Window window, int tool, Window parent) +{ + load_window_state(window, X11_atom_net_wm_window_type); + + if (tool) + { + set_window_state(X11_atom_net_wm_window_type_utility); + clear_window_state(X11_atom_net_wm_window_type_normal); + if (parent) + XSetTransientForHint(_display, window, parent); + } + else + { + clear_window_state(X11_atom_net_wm_window_type_utility); + set_window_state(X11_atom_net_wm_window_type_normal); + } + + save_window_state(window, X11_atom_net_wm_window_type); +} + +int X11_get_window_tool(Window window) +{ + load_window_state(window, X11_atom_net_wm_window_type); + return has_window_state(X11_atom_net_wm_window_type_utility); +} + + +// Set window desktop + +void X11_window_set_desktop(Window window, bool visible, int desktop) +{ + XEvent e; + long mask = (SubstructureRedirectMask | SubstructureNotifyMask); + + if (visible) + { + e.xclient.type = ClientMessage; + e.xclient.message_type = X11_atom_net_wm_desktop; + e.xclient.display = _display; + e.xclient.window = window; + e.xclient.format = 32; + e.xclient.data.l[0] = desktop; + e.xclient.data.l[1] = 1; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + + XSendEvent(_display, _root, False, mask, &e); + } + else + { + XChangeProperty(_display, window, X11_atom_net_wm_desktop, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&desktop, 1); + XFlush(_display); + } +} + + +int X11_window_get_desktop(Window window) +{ + int length; + char *data = NULL; + int desktop = 0; + + data = get_property(window, X11_atom_net_wm_desktop, &length); + if (data) + desktop = *((int *)data); + + return desktop; +} + +int X11_get_current_desktop() +{ + int length; + char *data; + int desktop = 0; + + data = get_property(_root, X11_atom_net_current_desktop, &length); + if (data) + desktop = *((int *)data); + + return desktop; +} + +int X11_get_window_type(Window window) +{ + int index; + + load_window_state(window, X11_atom_net_wm_window_type); + index = find_atom(_window_prop.atoms[0]); + if (index < 0) + return _NET_WM_WINDOW_TYPE_NORMAL; + else + return index; +} + +void X11_set_window_type(Window window, int type) +{ + _window_prop.count = 1; + _window_prop.atoms[0] = get_atom(type); + save_window_state(window, X11_atom_net_wm_window_type); +} + +void X11_set_transient_for(Window window, Window parent) +{ + XSetTransientForHint(_display, window, parent); +} + +void X11_set_window_decorated(Window window, bool decorated) +{ + // Motif structures + + MwmHints *hints; + MwmHints new_hints; + + uchar *data; + Atom type; + int format; + ulong nitems; + ulong bytes_after; + + if (X11_atom_motif_wm_hints == None) + X11_atom_motif_wm_hints = XInternAtom(_display, "_MOTIF_WM_HINTS", True); + + XGetWindowProperty (_display, window, + X11_atom_motif_wm_hints, 0, sizeof(MwmHints)/sizeof(long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type == None) + { + hints = &new_hints; + hints->flags = 0; + hints->functions = 0; + hints->input_mode = 0; + hints->status = 0; + hints->decorations = 0; + } + else + hints = (MwmHints *)data; + + hints->flags |= (1 << 1); + hints->decorations = decorated ? 1 : 0; + + XChangeProperty(_display, window, + X11_atom_motif_wm_hints, X11_atom_motif_wm_hints, 32, PropModeReplace, + (uchar *)hints, sizeof (MwmHints)/sizeof(long)); + + if (hints != &new_hints) + XFree(hints); + + XFlush(_display); +} + +void X11_window_remap(Window window) +{ + XWithdrawWindow(_display, window, DefaultScreen(_display)); + XUnmapWindow(_display, window); + XMapWindow(_display, window); + XFlush(_display); +} + +void X11_window_activate(Window window) +{ + XSetInputFocus(_display, window, RevertToParent, CurrentTime); + XFlush(_display); +} + +bool X11_get_available_geometry(int screen, int *x, int *y, int *w, int *h) +{ + if (X11_atom_net_workarea == None) + X11_atom_net_workarea = XInternAtom(_display, "_NET_WORKAREA", True); + + Atom ret; + int format, e; + unsigned char *data = NULL; + unsigned long nitems, after; + bool err = TRUE; + + e = XGetWindowProperty(_display, RootWindow(_display, screen), + X11_atom_net_workarea, 0, 4, False, XA_CARDINAL, + &ret, &format, &nitems, &after, &data); + + if (e == Success && ret == XA_CARDINAL && format == 32 && nitems == 4) + { + long *workarea = (long *)data; + *x = workarea[0]; + *y = workarea[1]; + *w = workarea[2]; + *h = workarea[3]; + err = FALSE; + } + + if (data) + XFree(data); + + return err; +} + +bool X11_is_supported_by_WM(Atom atom) +{ + int i; + + if (_supported) + { + for (i = 0; i < GB.Count(_supported); i++) + { + if (_supported[i] == atom) + return TRUE; + } + } + + return FALSE; +} + +bool X11_send_move_resize_event(Window window, int x, int y, int w, int h) +{ + static Atom _net_moveresize_window = None; + XEvent e; + + if (!_net_moveresize_window) + _net_moveresize_window = XInternAtom(_display, "_NET_MOVERESIZE_WINDOW", True); + + if (!X11_is_supported_by_WM(_net_moveresize_window)) + return TRUE; + + e.xclient.type = ClientMessage; + e.xclient.message_type = _net_moveresize_window; + e.xclient.display = _display; + e.xclient.window = window; + e.xclient.format = 32; + e.xclient.data.l[0] = StaticGravity | 1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12; + e.xclient.data.l[1] = x; + e.xclient.data.l[2] = y; + e.xclient.data.l[3] = w; + e.xclient.data.l[4] = h; + XSendEvent(_display, _root, FALSE, (SubstructureNotifyMask | SubstructureRedirectMask), &e); + + return FALSE; +} + +static Atom get_net_system_tray(void) +{ + char buf[64]; + + if (X11_atom_net_system_tray == None) + { + sprintf(buf, "_NET_SYSTEM_TRAY_S%d", XScreenNumberOfScreen(DefaultScreenOfDisplay(_display))); + X11_atom_net_system_tray = XInternAtom(_display, buf, 0); + } + + return X11_atom_net_system_tray; +} + +Window X11_get_system_tray() +{ + return XGetSelectionOwner(_display, get_net_system_tray()); +} + +#if 0 +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define OPCODE "_NET_SYSTEM_TRAY_OPCODE" + +bool X11_window_dock(Window window) +{ + Window xmanager=None; + XClientMessageEvent ev; + Atom OpCodeAtom; + + XGrabServer(_display); + + xmanager = X11_get_system_tray(); + if (xmanager != None) + XSelectInput(_display, xmanager, StructureNotifyMask); + + XUngrabServer(_display); + XFlush(_display); + + if (xmanager == None) + return TRUE; + + /*********************************************** + Dock Tray Icon + ************************************************/ + + OpCodeAtom = XInternAtom(_display, OPCODE, 0); + + ev.type = ClientMessage; + ev.window = xmanager; + ev.message_type = OpCodeAtom; + ev.format = 32; + ev.data.l[0] = 0; + ev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; + ev.data.l[2] = window; + ev.data.l[3] = 0; + ev.data.l[4] = 0; + + XSendEvent(_display, xmanager, 0, NoEventMask, (XEvent *)&ev); + XSync(_display, 0); + usleep(10000); + + return FALSE; +} +#endif + +void X11_window_set_user_time(Window win, int timestamp) +{ + ulong timestamp_long = (long)timestamp; + + XChangeProperty(_display, win, X11_atom_net_wm_user_time, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)×tamp_long, 1); + XFlush(_display); +} diff --git a/gb.qt4/src/x11.h b/gb.qt4/src/x11.h new file mode 100644 index 00000000..740fc102 --- /dev/null +++ b/gb.qt4/src/x11.h @@ -0,0 +1,135 @@ +/*************************************************************************** + + x11.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __X11_H +#define __X11_H + +#include +#include +#include + +#ifdef QT_VERSION +//#include +const int XFocusIn = FocusIn; +#undef FocusIn +const int XFocusOut = FocusOut; +#undef FocusOut +const int XKeyPress = KeyPress; +#undef KeyPress +const int XKeyRelease = KeyRelease; +#undef KeyRelease +#endif + +#include "gambas.h" +#include "gb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __X11_C +EXTERN Atom X11_atom_net_wm_state; +EXTERN Atom X11_atom_net_wm_state_above; +EXTERN Atom X11_atom_net_wm_state_below; +EXTERN Atom X11_atom_net_wm_state_stays_on_top; +EXTERN Atom X11_atom_net_wm_state_skip_taskbar; +EXTERN Atom X11_atom_net_wm_window_type; +EXTERN Atom X11_atom_net_wm_desktop; +#endif + +typedef + struct { + char *title; + char *klass; + char *role; + } + X11_WINDOW_INFO; + +enum +{ + _NET_WM_WINDOW_TYPE_NORMAL, + _NET_WM_WINDOW_TYPE_DESKTOP, + _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_MENU, + _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DND +}; + +void X11_init(Display *display, Window root); +void X11_exit(); +void X11_sync(void); + +/* Functions to deal with the _NET_WM_STATE and _NET_WM_TYPE property */ +void X11_window_change_property(Atom property, bool set); +void X11_window_change_begin(Window window, bool visible); +void X11_window_change_end(); + +bool X11_window_has_property(Window window, Atom property); +void X11_window_save_properties(Window window); +void X11_window_restore_properties(Window window); +/* Function to dock a window in the system tray */ +bool X11_window_dock(Window window); +Window X11_get_system_tray(); +/* Function to define startup position hints for a window being shown */ +void X11_window_startup(Window window, int x, int y, int w, int h); +/* Functions to search for a specific top-level window */ +void X11_find_windows(Window **window_list, int *count); +void X11_get_window_title(Window window, char **result, int *length); +void X11_get_window_class(Window window, char **result, int *length); +void X11_get_window_role(Window window, char **result, int *length); +/* Function to make a tool window */ +void X11_set_window_tool(Window window, int tool, Window parent); +int X11_get_window_tool(Window window); +void X11_window_set_desktop(Window window, bool visible, int desktop); +int X11_window_get_desktop(Window window); +void X11_window_set_user_time(Window win, int timestamp); + +int X11_get_current_desktop(); + +int X11_get_window_type(Window window); +void X11_set_window_type(Window window, int type); +void X11_set_transient_for(Window window, Window parent); +void X11_set_window_decorated(Window window, bool decorated); +void X11_window_remap(Window window); +void X11_window_activate(Window window); +bool X11_get_available_geometry(int screen, int *x, int *y, int *w, int *h); + +char *X11_get_property(Window wid, Atom prop, Atom *type, int *format, int *pcount); +bool X11_is_supported_by_WM(Atom atom); +bool X11_send_move_resize_event(Window window, int x, int y, int w, int h); + +void X11_flush(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gb.qt5/AUTHORS b/gb.qt5/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/COPYING b/gb.qt5/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.qt5/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.qt5/ChangeLog b/gb.qt5/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/INSTALL b/gb.qt5/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.qt5/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.qt5/Makefile.am b/gb.qt5/Makefile.am new file mode 100644 index 00000000..11b5fd4c --- /dev/null +++ b/gb.qt5/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @QT5_DIR@ +EXTRA_DIST = reconf share gb*.h gambas.h diff --git a/gb.qt5/NEWS b/gb.qt5/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/README b/gb.qt5/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/acinclude.m4 b/gb.qt5/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.qt5/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.qt5/component.am b/gb.qt5/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.qt5/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.qt5/configure.ac b/gb.qt5/configure.ac new file mode 100644 index 00000000..4f2701b4 --- /dev/null +++ b/gb.qt5/configure.ac @@ -0,0 +1,73 @@ +dnl ---- configure.ac for gb.qt5 + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-qt5],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.qt5) +LT_INIT +PKG_PROG_PKG_CONFIG + +GB_CHECK_XWINDOW +AM_CONDITIONAL(XWINDOW, test x"$have_x" = xyes) + +GB_COMPONENT_PKG_CONFIG( + qt5, QT5, gb.qt5, [src], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets Qt5Svg Qt5PrintSupport +) + +GB_COMPONENT_PKG_CONFIG( + qt5x11, QT5X11, gb.qt5.x11, [x11], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets Qt5X11Extras x11-xcb +) + +GB_COMPONENT_PKG_CONFIG( + qt5wayland, QT5WAYLAND, gb.qt5.wayland, [wayland], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets +) + +GB_COMPONENT_PKG_CONFIG( + qt5webkit, QT5WEBKIT, gb.qt5.webkit, [webkit], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets Qt5Network Qt5Xml Qt5WebKit Qt5WebKitWidgets Qt5PrintSupport +) + +GB_COMPONENT_PKG_CONFIG( + qt5webview, QT5WEBVIEW, gb.qt5.webview, [webview], + 'Qt5Core >= 5.5.0' Qt5WebEngineWidgets +) + +$PKG_CONFIG --atleast-version 5.4.0 Qt5Core +if test $? != 0; then + AC_DEFINE_UNQUOTED(QT_OLD_OPENGL, 1, Use deprecated OpenGL widget) + QT_OPENGL_SUPPORT=Qt5OpenGL + GB_MESSAGE([Qt version is <= 5.3, using deprecated OpenGL widget]) +fi + +AM_CONDITIONAL(QT_OLD_OPENGL, [test x$QT_OPENGL_SUPPORT != x]) + +GB_COMPONENT_PKG_CONFIG( + qt5opengl, QT5OPENGL, gb.qt5.opengl, [opengl], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets $QT_OPENGL_SUPPORT gl x11 +) + +GB_COMPONENT_PKG_CONFIG( + qt5ext, QT5EXT, gb.qt5.ext, [ext], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets x11 +) + +MOC=`$PKG_CONFIG --variable=host_bins Qt5Core`/moc +AC_SUBST(MOC) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +src/x11/Makefile \ +src/wayland/Makefile \ +src/webkit/Makefile \ +src/webview/Makefile \ +src/opengl/Makefile \ +src/ext/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.qt5/gambas.h b/gb.qt5/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.qt5/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.qt5/gb.draw.h b/gb.qt5/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.qt5/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.qt5/gb.eval.h b/gb.qt5/gb.eval.h new file mode 120000 index 00000000..33d332f0 --- /dev/null +++ b/gb.qt5/gb.eval.h @@ -0,0 +1 @@ +../main/lib/eval/gb.eval.h \ No newline at end of file diff --git a/gb.qt5/gb.geom.h b/gb.qt5/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.qt5/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.qt5/gb.gl.h b/gb.qt5/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.qt5/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.qt5/gb.image.h b/gb.qt5/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.qt5/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.qt5/gb.paint.h b/gb.qt5/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.qt5/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.qt5/gb.qt.am b/gb.qt5/gb.qt.am new file mode 100644 index 00000000..a475f791 --- /dev/null +++ b/gb.qt5/gb.qt.am @@ -0,0 +1,6 @@ +CLEANFILES = *_moc.cpp + +.h_moc.cpp: + $(MOC) -o $@ $< + +SUFFIXES = .h _moc.cpp \ No newline at end of file diff --git a/gb.qt5/gb_common.h b/gb.qt5/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.qt5/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.qt5/m4 b/gb.qt5/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.qt5/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.qt5/reconf b/gb.qt5/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.qt5/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.qt5/share b/gb.qt5/share new file mode 120000 index 00000000..638490a4 --- /dev/null +++ b/gb.qt5/share @@ -0,0 +1 @@ +../gb.qt4/share \ No newline at end of file diff --git a/gb.qt5/src/CButton.cpp b/gb.qt5/src/CButton.cpp new file mode 120000 index 00000000..edde5955 --- /dev/null +++ b/gb.qt5/src/CButton.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CButton.cpp \ No newline at end of file diff --git a/gb.qt5/src/CButton.h b/gb.qt5/src/CButton.h new file mode 120000 index 00000000..cd3da8da --- /dev/null +++ b/gb.qt5/src/CButton.h @@ -0,0 +1 @@ +../../gb.qt4/src/CButton.h \ No newline at end of file diff --git a/gb.qt5/src/CCheckBox.cpp b/gb.qt5/src/CCheckBox.cpp new file mode 120000 index 00000000..7b3905ec --- /dev/null +++ b/gb.qt5/src/CCheckBox.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CCheckBox.cpp \ No newline at end of file diff --git a/gb.qt5/src/CCheckBox.h b/gb.qt5/src/CCheckBox.h new file mode 120000 index 00000000..681844c2 --- /dev/null +++ b/gb.qt5/src/CCheckBox.h @@ -0,0 +1 @@ +../../gb.qt4/src/CCheckBox.h \ No newline at end of file diff --git a/gb.qt5/src/CClipboard.cpp b/gb.qt5/src/CClipboard.cpp new file mode 120000 index 00000000..467a4897 --- /dev/null +++ b/gb.qt5/src/CClipboard.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CClipboard.cpp \ No newline at end of file diff --git a/gb.qt5/src/CClipboard.h b/gb.qt5/src/CClipboard.h new file mode 120000 index 00000000..1f21021e --- /dev/null +++ b/gb.qt5/src/CClipboard.h @@ -0,0 +1 @@ +../../gb.qt4/src/CClipboard.h \ No newline at end of file diff --git a/gb.qt5/src/CColor.cpp b/gb.qt5/src/CColor.cpp new file mode 120000 index 00000000..5b5d22c1 --- /dev/null +++ b/gb.qt5/src/CColor.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CColor.cpp \ No newline at end of file diff --git a/gb.qt5/src/CColor.h b/gb.qt5/src/CColor.h new file mode 120000 index 00000000..f78de373 --- /dev/null +++ b/gb.qt5/src/CColor.h @@ -0,0 +1 @@ +../../gb.qt4/src/CColor.h \ No newline at end of file diff --git a/gb.qt5/src/CConst.cpp b/gb.qt5/src/CConst.cpp new file mode 120000 index 00000000..9b497bd3 --- /dev/null +++ b/gb.qt5/src/CConst.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CConst.cpp \ No newline at end of file diff --git a/gb.qt5/src/CConst.h b/gb.qt5/src/CConst.h new file mode 120000 index 00000000..49486004 --- /dev/null +++ b/gb.qt5/src/CConst.h @@ -0,0 +1 @@ +../../gb.qt4/src/CConst.h \ No newline at end of file diff --git a/gb.qt5/src/CContainer.cpp b/gb.qt5/src/CContainer.cpp new file mode 120000 index 00000000..147be167 --- /dev/null +++ b/gb.qt5/src/CContainer.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CContainer.cpp \ No newline at end of file diff --git a/gb.qt5/src/CContainer.h b/gb.qt5/src/CContainer.h new file mode 120000 index 00000000..b65b2660 --- /dev/null +++ b/gb.qt5/src/CContainer.h @@ -0,0 +1 @@ +../../gb.qt4/src/CContainer.h \ No newline at end of file diff --git a/gb.qt5/src/CDialog.cpp b/gb.qt5/src/CDialog.cpp new file mode 120000 index 00000000..bd7f78dc --- /dev/null +++ b/gb.qt5/src/CDialog.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CDialog.cpp \ No newline at end of file diff --git a/gb.qt5/src/CDialog.h b/gb.qt5/src/CDialog.h new file mode 120000 index 00000000..f49264bf --- /dev/null +++ b/gb.qt5/src/CDialog.h @@ -0,0 +1 @@ +../../gb.qt4/src/CDialog.h \ No newline at end of file diff --git a/gb.qt5/src/CDraw.cpp b/gb.qt5/src/CDraw.cpp new file mode 120000 index 00000000..9f5bfa3c --- /dev/null +++ b/gb.qt5/src/CDraw.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CDraw.cpp \ No newline at end of file diff --git a/gb.qt5/src/CDraw.h b/gb.qt5/src/CDraw.h new file mode 120000 index 00000000..f1c19158 --- /dev/null +++ b/gb.qt5/src/CDraw.h @@ -0,0 +1 @@ +../../gb.qt4/src/CDraw.h \ No newline at end of file diff --git a/gb.qt5/src/CDrawingArea.cpp b/gb.qt5/src/CDrawingArea.cpp new file mode 120000 index 00000000..ac971bd6 --- /dev/null +++ b/gb.qt5/src/CDrawingArea.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CDrawingArea.cpp \ No newline at end of file diff --git a/gb.qt5/src/CDrawingArea.h b/gb.qt5/src/CDrawingArea.h new file mode 120000 index 00000000..833d414d --- /dev/null +++ b/gb.qt5/src/CDrawingArea.h @@ -0,0 +1 @@ +../../gb.qt4/src/CDrawingArea.h \ No newline at end of file diff --git a/gb.qt5/src/CFont.cpp b/gb.qt5/src/CFont.cpp new file mode 120000 index 00000000..a55857bf --- /dev/null +++ b/gb.qt5/src/CFont.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CFont.cpp \ No newline at end of file diff --git a/gb.qt5/src/CFont.h b/gb.qt5/src/CFont.h new file mode 120000 index 00000000..8a5df5ae --- /dev/null +++ b/gb.qt5/src/CFont.h @@ -0,0 +1 @@ +../../gb.qt4/src/CFont.h \ No newline at end of file diff --git a/gb.qt5/src/CImage.cpp b/gb.qt5/src/CImage.cpp new file mode 120000 index 00000000..a0845ff7 --- /dev/null +++ b/gb.qt5/src/CImage.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CImage.cpp \ No newline at end of file diff --git a/gb.qt5/src/CImage.h b/gb.qt5/src/CImage.h new file mode 120000 index 00000000..4cf3932c --- /dev/null +++ b/gb.qt5/src/CImage.h @@ -0,0 +1 @@ +../../gb.qt4/src/CImage.h \ No newline at end of file diff --git a/gb.qt5/src/CKey.cpp b/gb.qt5/src/CKey.cpp new file mode 120000 index 00000000..bc762ca7 --- /dev/null +++ b/gb.qt5/src/CKey.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CKey.cpp \ No newline at end of file diff --git a/gb.qt5/src/CKey.h b/gb.qt5/src/CKey.h new file mode 120000 index 00000000..52a533bb --- /dev/null +++ b/gb.qt5/src/CKey.h @@ -0,0 +1 @@ +../../gb.qt4/src/CKey.h \ No newline at end of file diff --git a/gb.qt5/src/CMenu.cpp b/gb.qt5/src/CMenu.cpp new file mode 120000 index 00000000..ee26dbde --- /dev/null +++ b/gb.qt5/src/CMenu.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CMenu.cpp \ No newline at end of file diff --git a/gb.qt5/src/CMenu.h b/gb.qt5/src/CMenu.h new file mode 120000 index 00000000..2a70ae26 --- /dev/null +++ b/gb.qt5/src/CMenu.h @@ -0,0 +1 @@ +../../gb.qt4/src/CMenu.h \ No newline at end of file diff --git a/gb.qt5/src/CMouse.cpp b/gb.qt5/src/CMouse.cpp new file mode 120000 index 00000000..1b102882 --- /dev/null +++ b/gb.qt5/src/CMouse.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CMouse.cpp \ No newline at end of file diff --git a/gb.qt5/src/CMouse.h b/gb.qt5/src/CMouse.h new file mode 120000 index 00000000..23047135 --- /dev/null +++ b/gb.qt5/src/CMouse.h @@ -0,0 +1 @@ +../../gb.qt4/src/CMouse.h \ No newline at end of file diff --git a/gb.qt5/src/CPanel.cpp b/gb.qt5/src/CPanel.cpp new file mode 120000 index 00000000..65e453c7 --- /dev/null +++ b/gb.qt5/src/CPanel.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CPanel.cpp \ No newline at end of file diff --git a/gb.qt5/src/CPanel.h b/gb.qt5/src/CPanel.h new file mode 120000 index 00000000..40eaad65 --- /dev/null +++ b/gb.qt5/src/CPanel.h @@ -0,0 +1 @@ +../../gb.qt4/src/CPanel.h \ No newline at end of file diff --git a/gb.qt5/src/CPicture.cpp b/gb.qt5/src/CPicture.cpp new file mode 120000 index 00000000..16533dbd --- /dev/null +++ b/gb.qt5/src/CPicture.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CPicture.cpp \ No newline at end of file diff --git a/gb.qt5/src/CPicture.h b/gb.qt5/src/CPicture.h new file mode 120000 index 00000000..2e73612e --- /dev/null +++ b/gb.qt5/src/CPicture.h @@ -0,0 +1 @@ +../../gb.qt4/src/CPicture.h \ No newline at end of file diff --git a/gb.qt5/src/CRadioButton.cpp b/gb.qt5/src/CRadioButton.cpp new file mode 120000 index 00000000..a3382401 --- /dev/null +++ b/gb.qt5/src/CRadioButton.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CRadioButton.cpp \ No newline at end of file diff --git a/gb.qt5/src/CRadioButton.h b/gb.qt5/src/CRadioButton.h new file mode 120000 index 00000000..89361f82 --- /dev/null +++ b/gb.qt5/src/CRadioButton.h @@ -0,0 +1 @@ +../../gb.qt4/src/CRadioButton.h \ No newline at end of file diff --git a/gb.qt5/src/CScreen.cpp b/gb.qt5/src/CScreen.cpp new file mode 120000 index 00000000..eab9f080 --- /dev/null +++ b/gb.qt5/src/CScreen.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CScreen.cpp \ No newline at end of file diff --git a/gb.qt5/src/CScreen.h b/gb.qt5/src/CScreen.h new file mode 120000 index 00000000..0de19f1e --- /dev/null +++ b/gb.qt5/src/CScreen.h @@ -0,0 +1 @@ +../../gb.qt4/src/CScreen.h \ No newline at end of file diff --git a/gb.qt5/src/CScrollBar.cpp b/gb.qt5/src/CScrollBar.cpp new file mode 120000 index 00000000..649da75b --- /dev/null +++ b/gb.qt5/src/CScrollBar.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CScrollBar.cpp \ No newline at end of file diff --git a/gb.qt5/src/CScrollBar.h b/gb.qt5/src/CScrollBar.h new file mode 120000 index 00000000..2648c351 --- /dev/null +++ b/gb.qt5/src/CScrollBar.h @@ -0,0 +1 @@ +../../gb.qt4/src/CScrollBar.h \ No newline at end of file diff --git a/gb.qt5/src/CSlider.cpp b/gb.qt5/src/CSlider.cpp new file mode 120000 index 00000000..9d9ca5ea --- /dev/null +++ b/gb.qt5/src/CSlider.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CSlider.cpp \ No newline at end of file diff --git a/gb.qt5/src/CSlider.h b/gb.qt5/src/CSlider.h new file mode 120000 index 00000000..06bff278 --- /dev/null +++ b/gb.qt5/src/CSlider.h @@ -0,0 +1 @@ +../../gb.qt4/src/CSlider.h \ No newline at end of file diff --git a/gb.qt5/src/CStyle.cpp b/gb.qt5/src/CStyle.cpp new file mode 120000 index 00000000..dd2e6d6a --- /dev/null +++ b/gb.qt5/src/CStyle.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CStyle.cpp \ No newline at end of file diff --git a/gb.qt5/src/CStyle.h b/gb.qt5/src/CStyle.h new file mode 120000 index 00000000..2c4565c3 --- /dev/null +++ b/gb.qt5/src/CStyle.h @@ -0,0 +1 @@ +../../gb.qt4/src/CStyle.h \ No newline at end of file diff --git a/gb.qt5/src/CTabStrip.cpp b/gb.qt5/src/CTabStrip.cpp new file mode 120000 index 00000000..7ad3c567 --- /dev/null +++ b/gb.qt5/src/CTabStrip.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CTabStrip.cpp \ No newline at end of file diff --git a/gb.qt5/src/CTabStrip.h b/gb.qt5/src/CTabStrip.h new file mode 120000 index 00000000..a90b4589 --- /dev/null +++ b/gb.qt5/src/CTabStrip.h @@ -0,0 +1 @@ +../../gb.qt4/src/CTabStrip.h \ No newline at end of file diff --git a/gb.qt5/src/CTextArea.cpp b/gb.qt5/src/CTextArea.cpp new file mode 120000 index 00000000..0e1762e1 --- /dev/null +++ b/gb.qt5/src/CTextArea.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CTextArea.cpp \ No newline at end of file diff --git a/gb.qt5/src/CTextArea.h b/gb.qt5/src/CTextArea.h new file mode 120000 index 00000000..0bbccc6a --- /dev/null +++ b/gb.qt5/src/CTextArea.h @@ -0,0 +1 @@ +../../gb.qt4/src/CTextArea.h \ No newline at end of file diff --git a/gb.qt5/src/CTextBox.cpp b/gb.qt5/src/CTextBox.cpp new file mode 120000 index 00000000..1a71c919 --- /dev/null +++ b/gb.qt5/src/CTextBox.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CTextBox.cpp \ No newline at end of file diff --git a/gb.qt5/src/CTextBox.h b/gb.qt5/src/CTextBox.h new file mode 120000 index 00000000..a39a4c8f --- /dev/null +++ b/gb.qt5/src/CTextBox.h @@ -0,0 +1 @@ +../../gb.qt4/src/CTextBox.h \ No newline at end of file diff --git a/gb.qt5/src/CWatch.cpp b/gb.qt5/src/CWatch.cpp new file mode 120000 index 00000000..8f71615d --- /dev/null +++ b/gb.qt5/src/CWatch.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWatch.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWatch.h b/gb.qt5/src/CWatch.h new file mode 120000 index 00000000..1d432e66 --- /dev/null +++ b/gb.qt5/src/CWatch.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWatch.h \ No newline at end of file diff --git a/gb.qt5/src/CWatcher.cpp b/gb.qt5/src/CWatcher.cpp new file mode 120000 index 00000000..2333e2fe --- /dev/null +++ b/gb.qt5/src/CWatcher.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWatcher.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWatcher.h b/gb.qt5/src/CWatcher.h new file mode 120000 index 00000000..eb51173e --- /dev/null +++ b/gb.qt5/src/CWatcher.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWatcher.h \ No newline at end of file diff --git a/gb.qt5/src/CWidget.cpp b/gb.qt5/src/CWidget.cpp new file mode 120000 index 00000000..f7eb671b --- /dev/null +++ b/gb.qt5/src/CWidget.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWidget.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWidget.h b/gb.qt5/src/CWidget.h new file mode 120000 index 00000000..224b6ce2 --- /dev/null +++ b/gb.qt5/src/CWidget.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWidget.h \ No newline at end of file diff --git a/gb.qt5/src/CWindow.cpp b/gb.qt5/src/CWindow.cpp new file mode 120000 index 00000000..7f8071c6 --- /dev/null +++ b/gb.qt5/src/CWindow.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWindow.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWindow.h b/gb.qt5/src/CWindow.h new file mode 120000 index 00000000..1124e0ff --- /dev/null +++ b/gb.qt5/src/CWindow.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWindow.h \ No newline at end of file diff --git a/gb.qt5/src/Makefile.am b/gb.qt5/src/Makefile.am new file mode 100644 index 00000000..34888c34 --- /dev/null +++ b/gb.qt5/src/Makefile.am @@ -0,0 +1,50 @@ +COMPONENT = gb.qt5 +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +SUBDIRS = . @QT5X11_DIR@ @QT5WAYLAND_DIR@ @QT5WEBKIT_DIR@ @QT5WEBVIEW_DIR@ @QT5OPENGL_DIR@ @QT5EXT_DIR@ + +gblib_LTLIBRARIES = gb.qt5.la + +gb_qt5_la_LIBADD = @THREAD_LIB@ @QT5_LIB@ +gb_qt5_la_LDFLAGS = -module @LD_FLAGS@ @QT5_LDFLAGS@ +gb_qt5_la_CXXFLAGS = @THREAD_INC@ -DGB_QT_COMPONENT $(AM_CXXFLAGS) -std=c++11 +gb_qt5_la_CPPFLAGS = @QT5_INC@ -I$(top_srcdir)/share/ + +gb_qt5_la_SOURCES = \ + gb.qt.h main.h main_moc.cpp main.cpp \ + CFont.h CFont.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CWidget.h CWidget_moc.cpp CWidget.cpp \ + CWindow.h CWindow_moc.cpp CWindow.cpp \ + CButton.h CButton_moc.cpp CButton.cpp \ + CContainer.h CContainer_moc.cpp CContainer.cpp \ + CTextBox.h CTextBox_moc.cpp CTextBox.cpp \ + CMenu.h CMenu_moc.cpp CMenu.cpp \ + CMouse.h CMouse_moc.cpp CMouse.cpp \ + CKey.h CKey_moc.cpp CKey.cpp \ + CColor.h CColor_moc.cpp CColor.cpp \ + CConst.h CConst.cpp \ + CCheckBox.h CCheckBox_moc.cpp CCheckBox.cpp \ + CPanel.h CPanel_moc.cpp CPanel.cpp \ + CRadioButton.h CRadioButton_moc.cpp CRadioButton.cpp \ + CTextArea.h CTextArea_moc.cpp CTextArea.cpp \ + CTabStrip.h CTabStrip_moc.cpp CTabStrip.cpp \ + CDialog.h CDialog_moc.cpp CDialog.cpp \ + CPicture.h CPicture_moc.cpp CPicture.cpp \ + CImage.h CImage_moc.cpp CImage.cpp \ + canimation.h canimation_moc.cpp canimation.cpp \ + CClipboard.h CClipboard_moc.cpp CClipboard.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CWatch.h CWatch_moc.cpp CWatch.cpp \ + CDrawingArea.h CDrawingArea_moc.cpp CDrawingArea.cpp \ + CSlider.h CSlider_moc.cpp CSlider.cpp \ + CScrollBar.h CScrollBar_moc.cpp CScrollBar.cpp \ + ctrayicon.h ctrayicon_moc.cpp ctrayicon.cpp \ + CWatcher.h CWatcher_moc.cpp CWatcher.cpp \ + cprinter.h cprinter_moc.cpp cprinter.cpp \ + csvgimage.h csvgimage_moc.cpp csvgimage.cpp \ + fix_style.h fix_style.cpp + diff --git a/gb.qt5/src/canimation.cpp b/gb.qt5/src/canimation.cpp new file mode 120000 index 00000000..d6b2b7d5 --- /dev/null +++ b/gb.qt5/src/canimation.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/canimation.cpp \ No newline at end of file diff --git a/gb.qt5/src/canimation.h b/gb.qt5/src/canimation.h new file mode 120000 index 00000000..e60dfd9c --- /dev/null +++ b/gb.qt5/src/canimation.h @@ -0,0 +1 @@ +../../gb.qt4/src/canimation.h \ No newline at end of file diff --git a/gb.qt5/src/cpaint_impl.cpp b/gb.qt5/src/cpaint_impl.cpp new file mode 120000 index 00000000..52abf3ae --- /dev/null +++ b/gb.qt5/src/cpaint_impl.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/cpaint_impl.cpp \ No newline at end of file diff --git a/gb.qt5/src/cpaint_impl.h b/gb.qt5/src/cpaint_impl.h new file mode 120000 index 00000000..32338433 --- /dev/null +++ b/gb.qt5/src/cpaint_impl.h @@ -0,0 +1 @@ +../../gb.qt4/src/cpaint_impl.h \ No newline at end of file diff --git a/gb.qt5/src/cprinter.cpp b/gb.qt5/src/cprinter.cpp new file mode 120000 index 00000000..99340598 --- /dev/null +++ b/gb.qt5/src/cprinter.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/cprinter.cpp \ No newline at end of file diff --git a/gb.qt5/src/cprinter.h b/gb.qt5/src/cprinter.h new file mode 120000 index 00000000..8c4a778d --- /dev/null +++ b/gb.qt5/src/cprinter.h @@ -0,0 +1 @@ +../../gb.qt4/src/cprinter.h \ No newline at end of file diff --git a/gb.qt5/src/csvgimage.cpp b/gb.qt5/src/csvgimage.cpp new file mode 120000 index 00000000..57a2c5a2 --- /dev/null +++ b/gb.qt5/src/csvgimage.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/csvgimage.cpp \ No newline at end of file diff --git a/gb.qt5/src/csvgimage.h b/gb.qt5/src/csvgimage.h new file mode 120000 index 00000000..6dddd772 --- /dev/null +++ b/gb.qt5/src/csvgimage.h @@ -0,0 +1 @@ +../../gb.qt4/src/csvgimage.h \ No newline at end of file diff --git a/gb.qt5/src/ctrayicon.cpp b/gb.qt5/src/ctrayicon.cpp new file mode 120000 index 00000000..90841e21 --- /dev/null +++ b/gb.qt5/src/ctrayicon.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/ctrayicon.cpp \ No newline at end of file diff --git a/gb.qt5/src/ctrayicon.h b/gb.qt5/src/ctrayicon.h new file mode 120000 index 00000000..64e8e147 --- /dev/null +++ b/gb.qt5/src/ctrayicon.h @@ -0,0 +1 @@ +../../gb.qt4/src/ctrayicon.h \ No newline at end of file diff --git a/gb.qt5/src/ext/CDial.cpp b/gb.qt5/src/ext/CDial.cpp new file mode 120000 index 00000000..a2e816fa --- /dev/null +++ b/gb.qt5/src/ext/CDial.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CDial.cpp \ No newline at end of file diff --git a/gb.qt5/src/ext/CDial.h b/gb.qt5/src/ext/CDial.h new file mode 120000 index 00000000..e9982c63 --- /dev/null +++ b/gb.qt5/src/ext/CDial.h @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CDial.h \ No newline at end of file diff --git a/gb.qt5/src/ext/CLCDNumber.cpp b/gb.qt5/src/ext/CLCDNumber.cpp new file mode 120000 index 00000000..fbf45409 --- /dev/null +++ b/gb.qt5/src/ext/CLCDNumber.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CLCDNumber.cpp \ No newline at end of file diff --git a/gb.qt5/src/ext/CLCDNumber.h b/gb.qt5/src/ext/CLCDNumber.h new file mode 120000 index 00000000..d4a7dc78 --- /dev/null +++ b/gb.qt5/src/ext/CLCDNumber.h @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CLCDNumber.h \ No newline at end of file diff --git a/gb.qt5/src/ext/CTextEdit.cpp b/gb.qt5/src/ext/CTextEdit.cpp new file mode 120000 index 00000000..adb78061 --- /dev/null +++ b/gb.qt5/src/ext/CTextEdit.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CTextEdit.cpp \ No newline at end of file diff --git a/gb.qt5/src/ext/CTextEdit.h b/gb.qt5/src/ext/CTextEdit.h new file mode 120000 index 00000000..bc66b6fe --- /dev/null +++ b/gb.qt5/src/ext/CTextEdit.h @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CTextEdit.h \ No newline at end of file diff --git a/gb.qt5/src/ext/Makefile.am b/gb.qt5/src/ext/Makefile.am new file mode 100644 index 00000000..40636741 --- /dev/null +++ b/gb.qt5/src/ext/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.qt5.ext +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.ext.la + +gb_qt5_ext_la_LIBADD = @QT5EXT_LIB@ +gb_qt5_ext_la_LDFLAGS = -module @LD_FLAGS@ @QT5EXT_LDFLAGS@ +gb_qt5_ext_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_ext_la_CPPFLAGS = @QT5EXT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt5_ext_la_SOURCES = \ + main.h main.cpp \ + CLCDNumber.h CLCDNumber_moc.cpp CLCDNumber.cpp \ + CDial.h CDial_moc.cpp CDial.cpp \ + CTextEdit.h CTextEdit_moc.cpp CTextEdit.cpp diff --git a/gb.qt5/src/ext/gb.qt5.ext.component b/gb.qt5/src/ext/gb.qt5.ext.component new file mode 100644 index 00000000..41fc6724 --- /dev/null +++ b/gb.qt5/src/ext/gb.qt5.ext.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.qt5.ext +Author=Benoît Minisini +Require=gb.qt5 diff --git a/gb.qt5/src/ext/main.cpp b/gb.qt5/src/ext/main.cpp new file mode 100644 index 00000000..0357c3e3 --- /dev/null +++ b/gb.qt5/src/ext/main.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "main.h" + +#include "CLCDNumber.h" +#include "CDial.h" +#include "CTextEdit.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CLCDNumberDesc, + + CDialDesc, + + CTextEditSelectionDesc, + CTextEditFormatDesc, + CTextEditDesc, + + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.qt5", QT_INTERFACE_VERSION, &QT); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + diff --git a/gb.qt5/src/ext/main.h b/gb.qt5/src/ext/main.h new file mode 100644 index 00000000..de333803 --- /dev/null +++ b/gb.qt5/src/ext/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.form.properties.h" + +#ifndef __MAIN_C +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +#endif + +#endif diff --git a/gb.qt5/src/fix_style.cpp b/gb.qt5/src/fix_style.cpp new file mode 120000 index 00000000..75b22dd6 --- /dev/null +++ b/gb.qt5/src/fix_style.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/fix_style.cpp \ No newline at end of file diff --git a/gb.qt5/src/fix_style.h b/gb.qt5/src/fix_style.h new file mode 120000 index 00000000..a77befc4 --- /dev/null +++ b/gb.qt5/src/fix_style.h @@ -0,0 +1 @@ +../../gb.qt4/src/fix_style.h \ No newline at end of file diff --git a/gb.qt5/src/gb.qt.h b/gb.qt5/src/gb.qt.h new file mode 120000 index 00000000..ef5a2ece --- /dev/null +++ b/gb.qt5/src/gb.qt.h @@ -0,0 +1 @@ +../../gb.qt4/src/gb.qt.h \ No newline at end of file diff --git a/gb.qt5/src/gb.qt.platform.h b/gb.qt5/src/gb.qt.platform.h new file mode 100644 index 00000000..2ee5d9ec --- /dev/null +++ b/gb.qt5/src/gb.qt.platform.h @@ -0,0 +1,80 @@ +/*************************************************************************** + + gb.qt.platform.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_QT_PLATFORM_H +#define __GB_QT_PLATFORM_H + +#include +#include +#include + +// Gambas QT platform component interface + +#define QT_PLATFORM_INTERFACE_VERSION 1 + +typedef + struct { + unsigned stacking : 2; + unsigned skipTaskbar : 1; + unsigned border: 1; + unsigned sticky: 1; + } QT_WINDOW_PROP; + +enum { + PROP_ALL = -1, + PROP_STACKING = 1, + PROP_SKIP_TASKBAR = 2, + PROP_BORDER = 4, + PROP_STICKY = 8 +}; + +typedef + struct { + intptr_t version; + + void (*Init)(void); + void (*Exit)(void); + + void (*ReleaseGrab)(void); + void (*UnreleaseGrab)(void); + int (*GetLastKeyCode)(void); + + struct { + int (*GetResolutionX)(void); + int (*GetResolutionY)(void); + void (*Screenshot)(QPixmap *pixmap, int x, int y, int w, int h); + } Desktop; + + struct { + int (*GetVirtualDesktop)(QWidget *window); + void (*SetVirtualDesktop)(QWidget *window, bool visible, int desktop); + void (*Remap)(QWidget *window); + void (*SetProperties)(QWidget *window, int which, QT_WINDOW_PROP *prop); + void (*SetUserTime)(QWidget *window, int timestamp); + void (*SetTransientFor)(QWidget *window, QWidget *parent); + void (*Activate)(QWidget *window); + } Window; + } + QT_PLATFORM_INTERFACE; + +#endif diff --git a/gb.qt5/src/gb.qt5.component b/gb.qt5/src/gb.qt5.component new file mode 100644 index 00000000..2dda0f44 --- /dev/null +++ b/gb.qt5/src/gb.qt5.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.qt5 +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form diff --git a/gb.qt5/src/main.cpp b/gb.qt5/src/main.cpp new file mode 100644 index 00000000..0a81ba45 --- /dev/null +++ b/gb.qt5/src/main.cpp @@ -0,0 +1,1332 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb.image.h" +#include "gb.form.font.h" + +#include "CFont.h" +#include "CScreen.h" +#include "CStyle.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CButton.h" +#include "CContainer.h" +#include "CTextBox.h" +#include "CTextArea.h" +#include "CMenu.h" +#include "CPanel.h" +#include "CMouse.h" +#include "CKey.h" +#include "CColor.h" +#include "CConst.h" +#include "CCheckBox.h" +#include "CRadioButton.h" +#include "CTabStrip.h" +#include "CDialog.h" +#include "CPicture.h" +#include "CImage.h" +#include "canimation.h" +#include "CClipboard.h" +#include "CDraw.h" +#include "CWatch.h" +#include "CDrawingArea.h" +#include "CSlider.h" +#include "CScrollBar.h" +#include "CWatcher.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "cpaint_impl.h" +#include "ctrayicon.h" + +#include "gb.qt.platform.h" + +#include "fix_style.h" +#include "main.h" + +/*#define DEBUG*/ + +extern "C" { +const GB_INTERFACE *GB_PTR EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; +QT_PLATFORM_INTERFACE PLATFORM EXPORT; +} + +int MAIN_in_wait = 0; +int MAIN_in_message_box = 0; +int MAIN_loop_level = 0; +int MAIN_scale = 6; +bool MAIN_debug_busy = false; +bool MAIN_init = false; +bool MAIN_key_debug = false; +bool MAIN_right_to_left = false; +const char *MAIN_platform = NULL; +bool MAIN_platform_is_wayland = false; + +GB_CLASS CLASS_Control; +GB_CLASS CLASS_Container; +GB_CLASS CLASS_ContainerChildren; +GB_CLASS CLASS_UserControl; +GB_CLASS CLASS_UserContainer; +GB_CLASS CLASS_TabStrip; +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Menu; +GB_CLASS CLASS_Picture; +GB_CLASS CLASS_Drawing; +GB_CLASS CLASS_DrawingArea; +GB_CLASS CLASS_Printer; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_SvgImage; +GB_CLASS CLASS_TextArea; + +static bool in_event_loop = false; +static int _no_destroy = 0; +static QTranslator *_translator = NULL; +static bool _application_keypress = false; +static GB_FUNCTION _application_keypress_func; +static bool _check_quit_posted = false; +static int _prevent_quit = 0; + +static QHash _link_map; + + +static QByteArray _utf8_buffer[UTF8_NBUF]; +static int _utf8_count = 0; +static int _utf8_length = 0; + +static void QT_Init(void); + +static QtMessageHandler _previousMessageHandler; + +static void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg ) +{ + //fprintf(stderr, "---- `%s'\n", QT_ToUtf8(msg)); + + if (msg == "QXcbClipboard: SelectionRequest too old") + return; + + if (msg.startsWith("QXcbConnection: ") && msg.contains("(TranslateCoords)")) + return; + + _previousMessageHandler(type, context, msg); +} + +//static MyApplication *myApp; + +/*************************************************************************** + + MyMimeSourceFactory + + Create a QMimeSourceFactory to handle files stored in an archive + +***************************************************************************/ + +#if 0 +class MyMimeSourceFactory: public Q3MimeSourceFactory +{ +public: + + MyMimeSourceFactory(); + + virtual const QMimeSource* data(const QString& abs_name) const; + +private: + + QMap extensions; +}; + + +MyMimeSourceFactory::MyMimeSourceFactory() +{ + extensions.replace("htm", "text/html;charset=UTF-8"); + extensions.replace("html", "text/html;charset=UTF-8"); + extensions.replace("txt", "text/plain"); + extensions.replace("xml", "text/xml;charset=UTF-8"); + extensions.replace("jpg", "image/jpeg"); + extensions.replace("png", "image/png"); + extensions.replace("gif", "image/gif"); +} + + +const QMimeSource* MyMimeSourceFactory::data(const QString& abs_name) const +{ + char *addr; + int len; + Q3StoredDrag* sr = 0; + char *path; + + //qDebug("MyMimeSourceFactory::data: %s", (char *)abs_name.latin1()); + + path = (char *)abs_name.latin1(); + + if (true) //abs_name[0] != '/') + { + if (GB.LoadFile(path, 0, &addr, &len)) + GB.Error(NULL); + else + { + QByteArray ba; + ba.setRawData((const char *)addr, len); + + QFileInfo fi(abs_name); + QString e = fi.extension(FALSE); + Q3CString mimetype = "text/html"; //"application/octet-stream"; + + const char* imgfmt; + + if ( extensions.contains(e) ) + mimetype = extensions[e].latin1(); + else + { + QBuffer buffer(&ba); + + buffer.open(QIODevice::ReadOnly); + if (( imgfmt = QImageReader::imageFormat( &buffer ) ) ) + mimetype = Q3CString("image/")+Q3CString(imgfmt).lower(); + buffer.close(); + } + + sr = new Q3StoredDrag( mimetype ); + sr->setEncodedData( ba ); + + ba.resetRawData((const char*)addr, len); + + //qDebug("MimeSource: %s %s", abs_name.latin1(), (const char *)mimetype); + + GB.ReleaseFile(addr, len); + } + } + + return sr; +} + +static MyMimeSourceFactory myMimeSourceFactory; +#endif + +#if 0 +/*************************************************************************** + + MyAbstractEventDispatcher + + Manage window deletion + +***************************************************************************/ + +class MyAbstractEventDispatcher : public QAbstractEventDispatcher +{ +public: + MyAbstractEventDispatcher(); + virtual bool processEvents(QEventLoop::ProcessEventsFlags flags); +}; + +MyAbstractEventDispatcher::MyAbstractEventDispatcher() +: QAbstractEventDispatcher() +{ +} + +bool MyAbstractEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + bool ret; + CWIDGET **ptr; + CWIDGET *ob; + + MAIN_loop_level++; + ret = QAbstractEventDispatcher::processEvents(flags); + MAIN_loop_level--; + + for(;;) + { + ptr = &CWIDGET_destroy_list; + + for(;;) + { + ob = *ptr; + if (!ob) + return ret; + + //if (MAIN_loop_level <= ob->level && !ob->flag.notified) + if (!ob->flag.notified) + { + //qDebug("delete: %s %p", GB.GetClassName(ob), ob); + //qDebug(">> delete %p (%p) :%p:%ld", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //*ptr = ob->next; + delete ob->widget; + break; + //GB.Unref(POINTER(&ob)); + //qDebug(" delete %p (%p) :%p:%ld #2", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //qDebug("<< delete %p (%p)", ob, ob->widget); + } + else + { + //qDebug("cannot delete: %s %p", GB.GetClassName(ob), ob); + ptr = &ob->next; + } + } + } + //return ret; +} +#endif + +void MAIN_process_events(void) +{ + _no_destroy++; + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + _no_destroy--; +} + +void MAIN_init_error() +{ + GB.Error("GUI is not initialized"); +} + +/** MyApplication **********************************************************/ + +bool MyApplication::_tooltip_disable = false; +int MyApplication::_event_filter = 0; +QEventLoop *MyApplication::eventLoop = 0; + +MyApplication::MyApplication(int &argc, char **argv) +: QApplication(argc, argv) +{ + if (isSessionRestored()) + { + bool ok; + int desktop; + + if (argc >= 2 && ::strcmp(argv[argc - 2], "-session-desktop") == 0) + { + desktop = QString(argv[argc - 1]).toInt(&ok); + if (ok) + CWINDOW_MainDesktop = desktop; + + //qDebug("session desktop: %d", CWINDOW_MainDesktop); + + argc -= 2; + } + } + + QObject::connect(this, SIGNAL(commitDataRequest(QSessionManager &)), SLOT(commitDataRequested(QSessionManager &))); +} + +void MyApplication::initClipboard() +{ + QObject::connect(clipboard(), SIGNAL(changed(QClipboard::Mode)), qApp, SLOT(clipboardHasChanged(QClipboard::Mode))); +} + +void MyApplication::clipboardHasChanged(QClipboard::Mode m) +{ + CLIPBOARD_has_changed(m); +} + +static bool QT_EventFilter(QEvent *e) +{ + bool cancel; + + if (!_application_keypress) + return false; + + if (e->type() == QEvent::KeyPress) + { + QKeyEvent *kevent = (QKeyEvent *)e; + + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + CKEY_info.text = GB.NewZeroString(QT_ToUtf8(kevent->text())); + CKEY_info.state = kevent->modifiers(); + CKEY_info.code = kevent->key(); + + } + else if (e->type() == QEvent::InputMethod) + { + QInputMethodEvent *imevent = (QInputMethodEvent *)e; + + if (!imevent->commitString().isEmpty()) + { + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + //qDebug("IMEnd: %s", imevent->text().latin1()); + CKEY_info.text = GB.NewZeroString(QT_ToUtf8(imevent->commitString())); + CKEY_info.state = Qt::KeyboardModifiers(); + CKEY_info.code = 0; + } + } + + GB.Call(&_application_keypress_func, 0, FALSE); + cancel = GB.Stopped(); + + CKEY_clear(false); + + return cancel; +} + +static bool QT_Notify(CWIDGET *object, bool value) +{ + bool old = object->flag.notified; + //qDebug("QT_Notify: %s %p %d", GB.GetClassName(object), object, value); + object->flag.notified = value; + return old; +} + +bool MyApplication::eventFilter(QObject *o, QEvent *e) +{ + if (o->isWidgetType()) + { + if ((e->spontaneous() && e->type() == QEvent::KeyPress) || e->type() == QEvent::InputMethod) + { + if (QT_EventFilter(e)) + return true; + } + else if (e->type() == QEvent::ToolTip) + { + if (_tooltip_disable) + return true; + } + else + { + QWidget *widget = (QWidget *)o; + CWIDGET *control; + + if (widget->isWindow()) + { + if (e->type() == QEvent::WindowActivate) + { + control = CWidget::getReal(widget); + //qDebug("WindowActivate: %p %s", widget, control ? control->name : "NULL"); + if (control) + CWIDGET_handle_focus(control, true); + else + CWINDOW_activate(NULL); + } + else if (e->type() == QEvent::WindowDeactivate) + { + control = CWidget::getReal(widget); + //qDebug("WindowDeactivate: %p %s", widget, control ? control->name : "NULL"); + if (control) + CWIDGET_handle_focus(control, false); + } + } + } + } + + return QApplication::eventFilter(o, e); +} + +/*bool MyApplication::notify(QObject *o, QEvent *e) +{ + if (o->isWidgetType()) + { + CWIDGET *ob = CWidget::get(o); + bool old, res; + + if (ob) + { + old = QT_Notify(ob, true); + res = QApplication::notify(o, e); + QT_Notify(ob, old); + return res; + } + } + + return QApplication::notify(o, e); +}*/ + +void MyApplication::setEventFilter(bool set) +{ + if (set) + { + _event_filter++; + if (_event_filter == 1) + qApp->installEventFilter(qApp); + } + else + { + _event_filter--; + if (_event_filter == 0) + qApp->removeEventFilter(qApp); + } +} + +void MyApplication::setTooltipEnabled(bool b) +{ + b = !b; + if (b == _tooltip_disable) + return; + + _tooltip_disable = b; + setEventFilter(b); +} + +void MyApplication::commitDataRequested(QSessionManager &session) +{ + QStringList cmd; + + if (CAPPLICATION_Restart) + { + int i; + char **str; + + str = (char **)GB.Array.Get(CAPPLICATION_Restart, 0); + for (i = 0; i < GB.Array.Count(CAPPLICATION_Restart); i++) + { + if (str[i]) + cmd += str[i]; + else + cmd += ""; + } + } + else + cmd += arguments().at(0); + + cmd += "-session"; + cmd += sessionId(); + + if (CWINDOW_Main) + { + cmd += "-session-desktop"; + cmd += QString::number(PLATFORM.Window.GetVirtualDesktop(CWINDOW_Main->widget.widget)); + /*cmd += "-session-data"; + cmd += QString::number(CWINDOW_Main->x) + "," + + QString::number(CWINDOW_Main->y) + "," + + QString::number(CWINDOW_Main->w) + "," + + QString::number(CWINDOW_Main->h) + "," + + QString::number(QApplication::desktop()->screenNumber(CWINDOW_Main->widget.widget));*/ + } + + session.setRestartCommand(cmd); +} + + +//--------------------------------------------------------------------------- + +MyTimer::MyTimer(GB_TIMER *t) : QObject(0) +{ + timer = t; + id = startTimer(t->delay); +} + +MyTimer::~MyTimer() +{ + killTimer(id); +} + +void MyTimer::timerEvent(QTimerEvent *e) +{ + if (timer) + GB.RaiseTimer(timer); +} + +//--------------------------------------------------------------------------- + +static bool must_quit(void) +{ + #if DEBUG_WINDOW + qDebug("must_quit: Window = %d Watch = %d in_event_loop = %d MAIN_in_message_box = %d _prevent_quit = %d", CWindow::count, CWatch::count, in_event_loop, MAIN_in_message_box, _prevent_quit); + #endif + return CWINDOW_must_quit() && CWatch::count == 0 && in_event_loop && MAIN_in_message_box == 0 && _prevent_quit == 0 && !GB.HasActiveTimer(); +} + +static void check_quit_now(intptr_t param) +{ + static bool exit_called = false; + + if (must_quit() && !exit_called) + { + if (QApplication::instance()) + { + GB_FUNCTION func; + + if (GB.ExistClass("TrayIcons")) + { + if (!GB.GetFunction(&func, (void *)GB.FindClass("TrayIcons"), "DeleteAll", NULL, NULL)) + GB.Call(&func, 0, FALSE); + } + + qApp->exit(); + exit_called = true; + } + } + else + _check_quit_posted = false; +} + +void MAIN_check_quit(void) +{ + if (_check_quit_posted) + return; + + GB.Post((GB_CALLBACK)check_quit_now, 0); + _check_quit_posted = true; +} + +void MAIN_update_scale(const QFont &font) +{ + MAIN_scale = GET_DESKTOP_SCALE(font.pointSize(), PLATFORM.Desktop.GetResolutionY()); +} + +static void QT_InitEventLoop(void) +{ +} + +//extern void qt_x11_set_global_double_buffer(bool); + + +static bool try_to_load_translation(QString &locale) +{ + // QLocale::system().name() + return _translator->load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); +/*#ifdef QT5 + return (!_translator->load(QString("qt_") + locale, QString(getenv("QTDIR")) + "/translations") + && !_translator->load(QString("qt_") + locale, QString("/usr/lib/qt5/translations")) + && !_translator->load(QString("qt_") + locale, QString("/usr/share/qt5/translations"))); +#else + return (!_translator->load(QString("qt_") + locale, QString(getenv("QTDIR")) + "/translations") + && !_translator->load(QString("qt_") + locale, QString("/usr/lib/qt4/translations")) + && !_translator->load(QString("qt_") + locale, QString("/usr/share/qt4/translations"))); +#endif*/ +} + +static void init_lang(char *lang, bool rtl) +{ + int pos; + QString locale(lang); + + MAIN_right_to_left = rtl; + + pos = locale.lastIndexOf("."); + if (pos >= 0) locale = locale.left(pos); + + if (_translator) + { + qApp->removeTranslator(_translator); + delete _translator; + _translator = NULL; + } + + _translator = new QTranslator(); + + if (!try_to_load_translation(locale)) + goto __INSTALL_TRANSLATOR; + + pos = locale.lastIndexOf("_"); + if (pos >= 0) + { + locale = locale.left(pos); + if (!try_to_load_translation(locale)) + goto __INSTALL_TRANSLATOR; + } + + delete _translator; + _translator = NULL; + + //if (strcmp(lang, "C")) + // qDebug("gb.qt4: warning: unable to load Qt translation: %s", lang); + + goto __SET_DIRECTION; + +__INSTALL_TRANSLATOR: + qApp->installTranslator(_translator); + +__SET_DIRECTION: + qApp->setLayoutDirection(rtl ? Qt::RightToLeft : Qt::LeftToRight); +} + +static void hook_lang(char *lang, int rtl) +{ + if (!qApp) + return; + + init_lang(lang, rtl); + + //locale = QTextCodec::locale(); +} + +#if 0 +static int (*_old_handler)(Display *d, XErrorEvent *e) = NULL; + +static int X11_error_handler(Display *d, XErrorEvent *e) +{ + qDebug("X11 ERROR"); + //BREAKPOINT(); + + if (_old_handler) + return (*_old_handler)(d, e); + else + return 0; +} +#endif + +static void *_old_hook_main; + +static void hook_main(int *argc, char ***argv) +{ + QString platform; + const char *comp; + char *env; + + env = getenv("GB_GUI_PLATFORM"); + if (env && *env) + { + if (!strcasecmp(env, "X11")) + putenv((char *)"QT_QPA_PLATFORM=xcb"); + else if (!strcasecmp(env, "WAYLAND")) + putenv((char *)"QT_QPA_PLATFORM=wayland"); + else + fprintf(stderr, QT_NAME ": warning: unknown platform: %s\n", env); + } + + new MyApplication(*argc, *argv); + + platform = qApp->platformName(); + + if (platform == "wayland") + { + comp = "gb.qt5.wayland"; + MAIN_platform = "wayland"; + MAIN_platform_is_wayland = true; + } + else if (platform == "xcb") + { + comp = "gb.qt5.x11"; + MAIN_platform = "x11"; + } + else + { + fprintf(stderr, QT_NAME ": error: unsupported platform: %s\n", TO_UTF8(qApp->platformName())); + ::abort(); + } + + GB.Component.Load(comp); + GB.GetInterface(comp, QT_PLATFORM_INTERFACE_VERSION, &PLATFORM); + + QT_Init(); + init_lang(GB.System.Language(), GB.System.IsRightToLeft()); + + MAIN_init = true; + + //_old_handler = XSetErrorHandler(X11_error_handler); + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + + +static void hook_quit() +{ + GB_FUNCTION func; + + CWINDOW_close_all(true); + CWINDOW_delete_all(true); + CMOUSE_set_control(NULL); + + qApp->sendPostedEvents(); //processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + qApp->sendPostedEvents(0, QEvent::DeferredDelete); + + if (!GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_Quit", NULL, NULL)) + GB.Call(&func, 0, FALSE); +} + + +static void hook_loop() +{ + //qDebug("**** ENTERING EVENT LOOP"); + + qApp->sendPostedEvents(); + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + in_event_loop = true; + + if (!must_quit()) + qApp->exec(); + else + MAIN_check_quit(); + + hook_quit(); +} + + +static void hook_wait(int duration) +{ + static bool _warning = FALSE; + + if (MyDrawingArea::inAnyDrawEvent()) + { + GB.Error("Wait is forbidden during a repaint event"); + return; + } + + if (CKEY_is_valid() && duration != -1) + { + if (!_warning) + { + fprintf(stderr, QT_NAME ": warning: calling the event loop during a keyboard event handler is ignored\n"); + _warning = TRUE; + } + return; + } + + MAIN_in_wait++; + + if (duration >= 0) + qApp->processEvents(QEventLoop::AllEvents, duration); + else if (duration == -1) + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + else if (duration == -2) + qApp->processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents); + + MAIN_in_wait--; +} + + +static void hook_timer(GB_TIMER *timer, bool on) +{ + if (timer->id) + { + MyTimer *t = (MyTimer *)(timer->id); + t->clearTimer(); + t->deleteLater(); + timer->id = 0; + } + + if (on) + timer->id = (intptr_t)(new MyTimer(timer)); + else + MAIN_check_quit(); +} + + +static void hook_watch(int fd, int type, void *callback, intptr_t param) +{ + CWatch::watch(fd, type, (GB_WATCH_CALLBACK)callback, param); +} + + +static void hook_post(void) +{ + static MyPostCheck check; + + //qDebug("hook_post ?"); + + if (MyPostCheck::in_check) + return; + + //qDebug("hook_post !"); + + MyPostCheck::in_check = true; + QTimer::singleShot(0, &check, SLOT(check())); +} + + +static bool hook_error(int code, char *error, char *where, bool in_event_loop) +{ + QString msg; + int ret; + + qApp->restoreOverrideCursor(); + while (qApp->activePopupWidget()) + delete qApp->activePopupWidget(); + CWatch::stop(); + + msg = "This application has raised an unexpected
    error and must abort.


    "; + + if (code > 0) + { + msg = msg + "[%1] %2.

    %3"; + msg = msg.arg(code).arg(TO_QSTRING(error)).arg(where); + } + else + { + msg = msg + "%1.

    %2"; + msg = msg.arg(TO_QSTRING(error)).arg(where); + } + + PLATFORM.ReleaseGrab(); + MAIN_in_message_box++; + ret = QMessageBox::critical(0, TO_QSTRING(GB.Application.Name()), msg, in_event_loop ? QMessageBox::Close | QMessageBox::Ignore : QMessageBox::Ok); + MAIN_in_message_box--; + PLATFORM.UnreleaseGrab(); + MAIN_check_quit(); + + return ret == QMessageBox::Ignore; +} + +static void QT_Init(void) +{ + static bool init = false; + QFont f; + char *env; + bool fix_style; + + if (init) + return; + + //qApp->setAttribute(Qt::AA_ImmediateWidgetCreation); + + PLATFORM.Init(); + + _previousMessageHandler = qInstallMessageHandler(myMessageHandler); + + /*QX11Info::setAppDpiX(0, 92); + QX11Info::setAppDpiY(0, 92);*/ + + /*fcntl(ConnectionNumber(qt_xdisplay()), F_SETFD, FD_CLOEXEC);*/ + #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + qApp->setDesktopFileName(TO_QSTRING(GB.Application.Name())); + #endif + + fix_style = false; + + if (::strcmp(qApp->style()->metaObject()->className(), "Breeze::Style") == 0) + { + env = getenv("GB_QT_NO_BREEZE_FIX"); + if (!env || atoi(env) == 0) + { + CSTYLE_fix_breeze = TRUE; + qApp->setStyle(new FixBreezeStyle); + fix_style = true; + } + } + else if (::strcmp(qApp->style()->metaObject()->className(), "Oxygen::Style") == 0) + { + env = getenv("GB_QT_NO_OXYGEN_FIX"); + if (!env || atoi(env) == 0) + { + CSTYLE_fix_oxygen = TRUE; + qApp->setStyle(new FixBreezeStyle); + fix_style = true; + } + } + + if (!fix_style) + qApp->setStyle(new FixStyle); + + MAIN_update_scale(qApp->desktop()->font()); + + qApp->installEventFilter(&CWidget::manager); + + MyApplication::setEventFilter(true); + + if (GB.GetFunction(&_application_keypress_func, (void *)GB.Application.StartupClass(), "Application_KeyPress", "", "") == 0) + { + _application_keypress = true; + MyApplication::setEventFilter(true); + } + + //qt_x11_set_global_double_buffer(false); + + qApp->setQuitOnLastWindowClosed(false); + + MyApplication::initClipboard(); + + env = getenv("GB_QT_KEY_DEBUG"); + if (env && atoi(env) != 0) + MAIN_key_debug = TRUE; + + GB.Hook(GB_HOOK_WAIT, (void *)hook_wait); + GB.Hook(GB_HOOK_TIMER, (void *)hook_timer); + GB.Hook(GB_HOOK_WATCH, (void *)hook_watch); + GB.Hook(GB_HOOK_POST, (void *)hook_post); + GB.Hook(GB_HOOK_LOOP, (void *)hook_loop); + + init = true; +} + + +static void QT_InitWidget(QWidget *widget, void *object, int fill_bg) +{ + ((CWIDGET *)object)->flag.fillBackground = fill_bg; + CWIDGET_new(widget, object); +} + +static void QT_SetWheelFlag(void *object) +{ + ((CWIDGET *)object)->flag.wheel = true; +} + +void *QT_GetObject(QWidget *widget) +{ + return CWidget::get((QObject *)widget); +} + +static QWidget *QT_GetContainer(void *object) +{ + return QCONTAINER(object); +} + +/*static bool QT_IsDestroyed(void *object) +{ + return CWIDGET_test_flag(object, WF_DELETED); +}*/ + +static QPixmap *QT_GetPixmap(CPICTURE *pict) +{ + return pict->pixmap; +} + +const char *QT_ToUtf8(const QString &str) +{ + const char *res; + + _utf8_buffer[_utf8_count] = str.toUtf8(); + res = _utf8_buffer[_utf8_count].data(); + _utf8_length = _utf8_buffer[_utf8_count].length(); + _utf8_count++; + if (_utf8_count >= UTF8_NBUF) + _utf8_count = 0; + + return res; +} + +int QT_GetLastUtf8Length() +{ + return _utf8_length; +} + +char *QT_NewString(const QString &str) +{ + const char *res = QT_ToUtf8(str); + return GB.NewString(res, _utf8_length); +} + +void QT_ReturnNewString(const QString &str) +{ + const char *res = QT_ToUtf8(str); + GB.ReturnNewString(res, _utf8_length); +} + +static void *QT_CreatePicture(const QPixmap &p) +{ + return CPICTURE_create(&p); +} + +void MyApplication::linkDestroyed(QObject *qobject) +{ + void *object = _link_map.value(qobject, 0); + _link_map.remove(qobject); + if (object) + GB.Unref(POINTER(&object)); +} + +void QT_Link(QObject *qobject, void *object) +{ + _link_map.insert(qobject, object); + QObject::connect(qobject, SIGNAL(destroyed(QObject *)), qApp, SLOT(linkDestroyed(QObject *))); + GB.Ref(object); +} + +void *QT_GetLink(QObject *qobject) +{ + return _link_map.value(qobject, 0); +} + +void QT_PreventQuit(bool inc) +{ + if (inc) + _prevent_quit++; + else + { + _prevent_quit--; + MAIN_check_quit(); + } +} + +QMenu *QT_FindMenu(void *parent, const char *name) +{ + CMENU *menu = NULL; + + if (parent && GB.Is(parent, CLASS_Control)) + { + CWINDOW *window = CWidget::getWindow((CWIDGET *)parent); + menu = CWindow::findMenu(window, name); + } + + return menu ? menu->menu : NULL; +} + +static void declare_tray_icon() +{ + GB.Component.Declare(TrayIconDesc); + GB.Component.Declare(TrayIconsDesc); +} + +static int QT_GetDesktopScale(void) +{ + return MAIN_scale; +} + +extern "C" { + +GB_DESC *GB_CLASSES[] EXPORT = +{ + BorderDesc, CColorDesc, + AlignDesc, ArrangeDesc, ScrollDesc, CKeyDesc, SelectDesc, DirectionDesc, + CImageDesc, CPictureDesc, AnimationDesc, + CFontDesc, CFontsDesc, + CMouseDesc, CCursorDesc, CPointerDesc, + CClipboardDesc, CDragDesc, + StyleDesc, ScreenDesc, ScreensDesc, DesktopDesc, + ApplicationDesc, + CControlDesc, ContainerChildrenDesc, ContainerDesc, + UserControlDesc, UserContainerDesc, + CMenuChildrenDesc, CMenuDesc, + CButtonDesc, CToggleButtonDesc, CToolButtonDesc, + CCheckBoxDesc, CRadioButtonDesc, + CTextBoxSelectionDesc, CTextBoxDesc, + CTextAreaSelectionDesc, CTextAreaDesc, + CPanelDesc, CHBoxDesc, CVBoxDesc, CHPanelDesc, CVPanelDesc, + CTabStripContainerChildrenDesc, CTabStripContainerDesc, CTabStripDesc, + CDrawingAreaDesc, + SliderDesc, ScrollBarDesc, + CWindowMenusDesc, CWindowControlsDesc, CWindowDesc, CWindowsDesc, CFormDesc, + CDialogDesc, + CWatcherDesc, + PrinterDesc, + SvgImageDesc, + NULL +}; + +void *GB_QT5_1[] EXPORT = +{ + (void *)1, + + (void *)QT_InitEventLoop, + (void *)QT_Init, + (void *)QT_InitWidget, + (void *)QT_SetWheelFlag, + (void *)QT_GetObject, + (void *)QT_GetContainer, + (void *)CWIDGET_border_simple, + (void *)CWIDGET_border_full, + (void *)CWIDGET_scrollbar, + (void *)Control_Font, + (void *)CFONT_create, + (void *)CFONT_set, + (void *)QT_CreatePicture, + //(void *)QT_MimeSourceFactory, + (void *)QT_GetPixmap, + (void *)QT_ToUtf8, + (void *)QT_GetLastUtf8Length, + (void *)QT_NewString, + (void *)QT_ReturnNewString, + (void *)QT_EventFilter, + (void *)QT_Notify, + (void *)CCONST_alignment, + (void *)QT_Link, + (void *)QT_GetLink, + (void *)PAINT_get_current, + (void *)CWIDGET_get_background, + (void *)Control_Mouse, + (void *)CWIDGET_after_set_color, + (void *)QT_GetDesktopScale, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.draw,gb.gui.base"; + +int EXPORT GB_INIT(void) +{ + char *env; + + // Do not disable GLib support + + /*env = getenv("KDE_FULL_SESSION"); + if (env && !strcasecmp(env, "true")) + putenv((char *)"QT_NO_GLIB=1");*/ + + env = getenv("GB_GUI_BUSY"); + if (env && atoi(env)) + MAIN_debug_busy = true; + + //putenv((char *)"QT_SLOW_TOPLEVEL_RESIZE=1"); + + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + GB.Hook(GB_HOOK_QUIT, (void *)hook_quit); + GB.Hook(GB_HOOK_ERROR, (void *)hook_error); + GB.Hook(GB_HOOK_LANG, (void *)hook_lang); + + GB.Component.Load("gb.draw"); + GB.Component.Load("gb.image"); + GB.Component.Load("gb.gui.base"); + + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + IMAGE.SetDefaultFormat(GB_IMAGE_BGRP); + + DRAW_init(); + + CLASS_Control = GB.FindClass("Control"); + CLASS_Container = GB.FindClass("Container"); + CLASS_ContainerChildren = GB.FindClass("ContainerChildren"); + CLASS_UserControl = GB.FindClass("UserControl"); + CLASS_UserContainer = GB.FindClass("UserContainer"); + CLASS_TabStrip = GB.FindClass("TabStrip"); + CLASS_Window = GB.FindClass("Window"); + CLASS_Menu = GB.FindClass("Menu"); + CLASS_Picture = GB.FindClass("Picture"); + CLASS_Drawing = GB.FindClass("Drawing"); + CLASS_DrawingArea = GB.FindClass("DrawingArea"); + CLASS_Printer = GB.FindClass("Printer"); + CLASS_Image = GB.FindClass("Image"); + CLASS_SvgImage = GB.FindClass("SvgImage"); + CLASS_TextArea = GB.FindClass("TextArea"); + + QT_InitEventLoop(); + + #ifdef OS_CYGWIN + return 1; + #else + return 0; + #endif +} + +void EXPORT GB_EXIT() +{ + if (qApp) + { + PLATFORM.Exit(); + delete qApp; + } +} + +#ifndef NO_X_WINDOW +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "DECLARE_TRAYICON")) + { + *value = (void *)declare_tray_icon; + return TRUE; + } + else if (!strcasecmp(key, "GET_HANDLE")) + { + *value = (void *)CWIDGET_get_handle; + return TRUE; + } + else + return FALSE; +} +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +void EXPORT GB_FORK(void) +{ + delete QThreadPool::globalInstance(); +} +#endif + +/*#ifndef NO_X_WINDOW +extern Time qt_x_time; +#endif*/ + +static void activate_main_window(intptr_t value) +{ + CWINDOW *active; + + active = CWINDOW_Active; + if (!active) active = CWINDOW_LastActive; + + if (!active) + return; + + MyMainWindow *win = (MyMainWindow *)active->widget.widget; + if (win && !win->isWindow()) + win = (MyMainWindow *)win->window(); + if (win) + { + /*#ifndef NO_X_WINDOW + qt_x_time = CurrentTime; + #endif*/ + win->raise(); + win->activateWindow(); + } +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + if (!qApp) + return; + + switch(signal) + { + case GB_SIGNAL_DEBUG_BREAK: + PLATFORM.ReleaseGrab(); + break; + + case GB_SIGNAL_DEBUG_FORWARD: + //while (qApp->activePopupWidget()) + // delete qApp->activePopupWidget(); + break; + + case GB_SIGNAL_DEBUG_CONTINUE: + GB.Post((GB_CALLBACK)activate_main_window, 0); + PLATFORM.UnreleaseGrab(); + break; + } +} + +} // extern "C" + +/* class MyPostCheck */ + +bool MyPostCheck::in_check = false; + +void MyPostCheck::check(void) +{ + //qDebug("MyPostCheck::check"); + in_check = false; + GB.CheckPost(); +} + diff --git a/gb.qt5/src/main.h b/gb.qt5/src/main.h new file mode 100644 index 00000000..9956b609 --- /dev/null +++ b/gb.qt5/src/main.h @@ -0,0 +1,165 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" + +#define DO_NOT_USE_QT_INTERFACE +#include "gb.qt.h" +#include "gb.qt.platform.h" +#include "gb.image.h" +#include "gb.geom.h" + +#ifndef __MAIN_CPP +extern "C" const GB_INTERFACE *GB_PTR; +extern "C" IMAGE_INTERFACE IMAGE; +extern "C" GEOM_INTERFACE GEOM; +extern "C" QT_PLATFORM_INTERFACE PLATFORM; + +extern int MAIN_in_wait; +extern int MAIN_in_message_box; +extern int MAIN_loop_level; +extern int MAIN_scale; +extern bool MAIN_debug_busy; +extern bool MAIN_init; +extern bool MAIN_key_debug; +extern bool MAIN_right_to_left; +extern const char *MAIN_platform; +extern bool MAIN_platform_is_wayland; + +extern GB_CLASS CLASS_Control; +extern GB_CLASS CLASS_Container; +extern GB_CLASS CLASS_ContainerChildren; +extern GB_CLASS CLASS_UserControl; +extern GB_CLASS CLASS_UserContainer; +extern GB_CLASS CLASS_TabStrip; +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Menu; +extern GB_CLASS CLASS_Picture; +extern GB_CLASS CLASS_Drawing; +extern GB_CLASS CLASS_DrawingArea; +extern GB_CLASS CLASS_ScrollArea; +extern GB_CLASS CLASS_Printer; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_SvgImage; +extern GB_CLASS CLASS_TextArea; + +#endif + +#define GB (*GB_PTR) + +class MyPostCheck: public QObject +{ + Q_OBJECT + +public: + + static bool in_check; + +public slots: + + void check(void); +}; + +class MyApplication: public QApplication +{ + Q_OBJECT + +public: + + MyApplication(int &argc, char **argv); + virtual bool eventFilter(QObject *o, QEvent *e); + //virtual bool notify(QObject *o, QEvent *e); + + static void setEventFilter(bool set); + + static bool isTooltipEnabled() { return !_tooltip_disable; } + static void setTooltipEnabled(bool b); + + static void initClipboard(); + + static QEventLoop *eventLoop; + +public slots: + + void linkDestroyed(QObject *); + void clipboardHasChanged(QClipboard::Mode); + void commitDataRequested(QSessionManager &); + +private: + static bool _tooltip_disable; + static int _event_filter; +}; + +class MyTimer : public QObject +{ +public: + + MyTimer(GB_TIMER *timer); + ~MyTimer(); + void clearTimer() { timer = 0; } + +protected: + + void timerEvent(QTimerEvent *); + +private: + + GB_TIMER *timer; + intptr_t id; +}; + + +#define UTF8_NBUF 4 + +void MAIN_check_quit(void); +void MAIN_update_scale(const QFont &); +void MAIN_process_events(void); +void MAIN_init_error(void); + +#define MAIN_CHECK_INIT() (MAIN_init ? 0 : (MAIN_init_error(), 1)) + +const char *QT_ToUtf8(const QString &str); +int QT_GetLastUtf8Length(); +char *QT_NewString(const QString &str); +void QT_ReturnNewString(const QString &str); +void QT_RegisterAction(void *object, const char *key, int on); +void QT_RaiseAction(const char *key); +void *QT_GetObject(QWidget *); +void QT_Link(QObject *, void *); +void *QT_GetLink(QObject *); +void QT_PreventQuit(bool inc); +QMenu *QT_FindMenu(void *parent, const char *name); + +#endif diff --git a/gb.qt5/src/opengl/CGLarea.cpp b/gb.qt5/src/opengl/CGLarea.cpp new file mode 100644 index 00000000..6597837f --- /dev/null +++ b/gb.qt5/src/opengl/CGLarea.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + + CGLarea.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLAREA_CPP + +#include "gb.image.h" + +#include "CGLarea.h" + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +BEGIN_METHOD(CGLAREA_new, GB_OBJECT parent) + + /*if (!QGLFormat::hasOpenGL()) + { + GB.Error( "This system has no OpenGL support"); + return; + }*/ + + GLarea *area = new GLarea(QT.GetContainer(VARG(parent)), THIS); + QT.InitWidget(area, _object, false); + area->show(); + +END_METHOD + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_update) + + WIDGET->updateGL(); + +END_METHOD +#endif + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_select) + + WIDGET->makeCurrent(); + // really needed ? + GL.Init(); + +END_METHOD +#endif + +#if 0 +BEGIN_METHOD(CGLAREA_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + QString text; + int x, y; + #ifdef GL_LIGHTING + GLboolean _LIGHTING = glIsEnabled(GL_LIGHTING); + #endif + GLboolean _TEXTURE_2D = glIsEnabled(GL_TEXTURE_2D); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glDisable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glDisable(GL_TEXTURE_2D); + + text = QSTRING_ARG(text); + x = VARG(x); + y = VARG(y); + + WIDGET->renderText(x, y, text, WIDGET->font()); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glEnable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glEnable(GL_TEXTURE_2D); + +END_METHOD +#endif + +/**************************************************************************/ + +GB_DESC CGlareaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CGLAREA_new, "(Parent)Container;"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; + +/* class GLarea */ + +GLarea::GLarea(QWidget *parent, CGLAREA *object): QOpenGLWidget(parent) +{ + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); + _area = object; +}; + + +void GLarea::initializeGL() +{ + GL.Init(); + GB.Raise(_area, EVENT_Open, 0); +} + +void GLarea::paintGL() +{ + /*static bool CleanupOnFirstShow = 0; + + uint color; + + if (!CleanupOnFirstShow) + { + // clear to avoid garbage + color = QT.GetBackgroundColor(_area); + if (color == GB_COLOR_DEFAULT) + color = 0; + + qglClearColor(QColor((QRgb)color)); + glClear(GL_COLOR_BUFFER_BIT); + + CleanupOnFirstShow = true; + }*/ + + GB.Raise(_area, EVENT_Draw, 0); +} + +void GLarea::resizeGL(int w, int h) +{ + GB.Raise(_area, EVENT_Resize, 0); +} + diff --git a/gb.qt5/src/opengl/CGLarea.h b/gb.qt5/src/opengl/CGLarea.h new file mode 100644 index 00000000..c294ca46 --- /dev/null +++ b/gb.qt5/src/opengl/CGLarea.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CGLarea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLAREA_H +#define __CGLAREA_H + +#include + +#include "main.h" + +typedef + struct { + QT_WIDGET widget; + } + CGLAREA; + +#ifndef __CGLAREA_CPP +extern GB_DESC CGlareaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((QT_WIDGET *)_object)->widget) + +#endif /* __CGLAREA_CPP */ + +class GLarea : public QOpenGLWidget +{ +Q_OBJECT +public: + GLarea(QWidget *parent, CGLAREA *object); + ~GLarea() {}; +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int w, int h); +private: + CGLAREA *_area; +}; + +#endif diff --git a/gb.qt5/src/opengl/COldGLarea.cpp b/gb.qt5/src/opengl/COldGLarea.cpp new file mode 100644 index 00000000..1b3d3cde --- /dev/null +++ b/gb.qt5/src/opengl/COldGLarea.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** + + CGLarea.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLAREA_CPP + +#include "gb.image.h" +#include "COldGLarea.h" + +//#include "gl.h" + + +//#include + + +static int glWidgetCount = 0; +static QGLWidget *sharedWidget = 0; + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +static void makeSharing() +{ + if (sharedWidget) + return; + + sharedWidget = new QGLWidget(); + //qDebug("make_sharing: %p", sharedWidget); +} + +BEGIN_METHOD_VOID(CGLAREA_exit) + + //qDebug("CGLAREA_exit: %p", sharedWidget); + delete sharedWidget; + sharedWidget = 0; + +END_METHOD + +BEGIN_METHOD(CGLAREA_new, GB_OBJECT parent) + + if (!QGLFormat::hasOpenGL()) + { + GB.Error( "This system has no OpenGL support"); + return; + } + + makeSharing(); + GLarea *area = new GLarea(QT.GetContainer(VARG(parent)), THIS, sharedWidget); + glWidgetCount++; + + QT.InitWidget(area, _object, false); + area->show(); + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_free) + + glWidgetCount--; + + // clean up the shared datas between GLwidgets if there is no more CGLarea + if (glWidgetCount <= 0) + { + delete sharedWidget; + sharedWidget = 0; + makeSharing(); + glWidgetCount = 0; + } + + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_update) + + WIDGET->updateGL(); + +END_METHOD + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_select) + + WIDGET->makeCurrent(); + // really needed ? + GL.Init(); + +END_METHOD +#endif + +BEGIN_METHOD(CGLAREA_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + QString text; + int x, y; + #ifdef GL_LIGHTING + GLboolean _LIGHTING = glIsEnabled(GL_LIGHTING); + #endif + GLboolean _TEXTURE_2D = glIsEnabled(GL_TEXTURE_2D); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glDisable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glDisable(GL_TEXTURE_2D); + + text = QSTRING_ARG(text); + x = VARG(x); + y = VARG(y); + + WIDGET->renderText(x, y, text, WIDGET->font()); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glEnable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glEnable(GL_TEXTURE_2D); + +END_METHOD + +/**************************************************************************/ + +GB_DESC CGlareaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + GB_STATIC_METHOD("_exit", NULL, CGLAREA_exit, NULL), + + GB_METHOD("_new", NULL, CGLAREA_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CGLAREA_free, NULL), + //GB_METHOD("Update", NULL, CGLAREA_update, NULL), + GB_METHOD("Refresh", NULL, CGLAREA_update, NULL), + //GB_METHOD("Select", NULL, CGLAREA_select, NULL), + //GB_METHOD("Text", NULL, CGLAREA_text, "(Text)s(X)i(Y)i"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; + +/* class GLarea */ + +GLarea::GLarea(QWidget *parent, CGLAREA *object, QGLWidget *sharing): QGLWidget(parent, sharing) +{ + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); +_area = object; +}; + + +void GLarea::initializeGL() +{ + GL.Init(); + GB.Raise(_area, EVENT_Open, 0); +} + +void GLarea::paintGL() +{ + /*static bool CleanupOnFirstShow = 0; + + uint color; + if (!CleanupOnFirstShow) + { + // clear to avoid garbage + color = QT.GetBackgroundColor(_area); + if (color == GB_COLOR_DEFAULT) + color = 0; + + qglClearColor(QColor((QRgb)color)); + glClear(GL_COLOR_BUFFER_BIT); + + CleanupOnFirstShow = true; + }*/ + + GB.Raise(_area, EVENT_Draw, 0); +} + +void GLarea::resizeGL(int w, int h) +{ + GB.Raise(_area, EVENT_Resize, 0); +} + diff --git a/gb.qt5/src/opengl/COldGLarea.h b/gb.qt5/src/opengl/COldGLarea.h new file mode 100644 index 00000000..88cd1c2e --- /dev/null +++ b/gb.qt5/src/opengl/COldGLarea.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CGLarea.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLAREA_H +#define __CGLAREA_H + +#include "main.h" + +#include + +typedef + struct { + QT_WIDGET widget; + } + CGLAREA; + +#ifndef __CGLAREA_CPP +extern GB_DESC CGlareaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((QT_WIDGET *)_object)->widget) + +#endif /* __CGLAREA_CPP */ + +class GLarea : public QGLWidget +{ +Q_OBJECT +public: + GLarea(QWidget *parent, CGLAREA *object, QGLWidget *sharing = 0); + ~GLarea() {}; +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int w, int h); +private: + CGLAREA *_area; +}; + +#endif diff --git a/gb.qt5/src/opengl/Makefile.am b/gb.qt5/src/opengl/Makefile.am new file mode 100644 index 00000000..5fcca25e --- /dev/null +++ b/gb.qt5/src/opengl/Makefile.am @@ -0,0 +1,23 @@ +COMPONENT = gb.qt5.opengl +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.opengl.la + +gb_qt5_opengl_la_LIBADD = @QT5OPENGL_LIB@ @QT5OPENGL_LIB@ +gb_qt5_opengl_la_LDFLAGS = -module @LD_FLAGS@ @QT5OPENGL_LDFLAGS@ +gb_qt5_opengl_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_opengl_la_CPPFLAGS = @QT5OPENGL_INC@ + +gb_qt5_opengl_la_SOURCES = main.h main.cpp + +if QT_OLD_OPENGL +gb_qt5_opengl_la_SOURCES += COldGLarea_moc.cpp COldGLarea.h COldGLarea.cpp +else +gb_qt5_opengl_la_SOURCES += CGLarea_moc.cpp CGLarea.h CGLarea.cpp +endif + + + + + diff --git a/gb.qt5/src/opengl/gb.qt5.opengl.component b/gb.qt5/src/opengl/gb.qt5.opengl.component new file mode 100644 index 00000000..cba9aa62 --- /dev/null +++ b/gb.qt5/src/opengl/gb.qt5.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Laurent Carlier,Benoît Minisini +Require=gb.qt5,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Stable diff --git a/gb.qt5/src/opengl/main.cpp b/gb.qt5/src/opengl/main.cpp new file mode 100644 index 00000000..8eff0422 --- /dev/null +++ b/gb.qt5/src/opengl/main.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + + main.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "main.h" + +#if QT_OLD_OPENGL +#include "COldGLarea.h" +#else +#include "CGLarea.h" +#endif + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; +GL_INTERFACE GL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CGlareaDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface(QT_NAME, QT_INTERFACE_VERSION, &QT); + GB.GetInterface("gb.opengl", GL_INTERFACE_VERSION, &GL); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/gb.qt5/src/opengl/main.h b/gb.qt5/src/opengl/main.h new file mode 100644 index 00000000..26349161 --- /dev/null +++ b/gb.qt5/src/opengl/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.gl.h" + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern QT_INTERFACE QT; +extern GL_INTERFACE GL; +#endif + +#endif diff --git a/gb.qt5/src/trayicon.xpm b/gb.qt5/src/trayicon.xpm new file mode 120000 index 00000000..065f8f44 --- /dev/null +++ b/gb.qt5/src/trayicon.xpm @@ -0,0 +1 @@ +../../gb.qt4/src/trayicon.xpm \ No newline at end of file diff --git a/gb.qt5/src/wayland/Makefile.am b/gb.qt5/src/wayland/Makefile.am new file mode 100644 index 00000000..2f9b9174 --- /dev/null +++ b/gb.qt5/src/wayland/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.qt5.wayland +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.wayland.la + +gb_qt5_wayland_la_LIBADD = @QT5WAYLAND_LIB@ +gb_qt5_wayland_la_LDFLAGS = -module @LD_FLAGS@ @QT5WAYLAND_LDFLAGS@ +gb_qt5_wayland_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_wayland_la_CPPFLAGS = @QT5WAYLAND_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt5_wayland_la_SOURCES = \ + main.h main.cpp diff --git a/gb.qt5/src/wayland/gb.qt5.wayland.component b/gb.qt5/src/wayland/gb.qt5.wayland.component new file mode 100644 index 00000000..58584e3c --- /dev/null +++ b/gb.qt5/src/wayland/gb.qt5.wayland.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.qt5 +Hidden=True + + diff --git a/gb.qt5/src/wayland/main.cpp b/gb.qt5/src/wayland/main.cpp new file mode 100644 index 00000000..0c02906b --- /dev/null +++ b/gb.qt5/src/wayland/main.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + + main.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include +#include +#include +#include + +static QPointer _mouseGrabber = 0; +static QPointer _keyboardGrabber = 0; + +//------------------------------------------------------------------------- + +static void platform_init(void) +{ +} + +static void platform_exit(void) +{ +} + +//------------------------------------------------------------------------- + +static void release_grab() +{ + _mouseGrabber = QWidget::mouseGrabber(); + _keyboardGrabber = QWidget::keyboardGrabber(); + + if (_mouseGrabber) + { + //qDebug("releaseMouse"); + _mouseGrabber->releaseMouse(); + } + if (_keyboardGrabber) + { + //qDebug("releaseKeyboard"); + _keyboardGrabber->releaseKeyboard(); + } + + /*#ifndef NO_X_WINDOW + if (qApp->activePopupWidget()) + { + XUngrabPointer(QX11Info::display(), CurrentTime); + XFlush(QX11Info::display()); + } + #endif*/ +} + +static void unrelease_grab() +{ + if (_mouseGrabber) + { + //qDebug("grabMouse"); + _mouseGrabber->grabMouse(); + _mouseGrabber = 0; + } + + if (_keyboardGrabber) + { + //qDebug("grabKeyboard"); + _keyboardGrabber->grabKeyboard(); + _keyboardGrabber = 0; + } +} + +static int get_last_key_code(void) +{ + return 0; +} + +//------------------------------------------------------------------------- + +static int desktop_get_resolution_x(void) +{ + return 96; +} + +static int desktop_get_resolution_y(void) +{ + return 96; +} + +static void desktop_screenshot(QPixmap *pixmap, int x, int y, int w, int h) +{ + //*pixmap = QPixmap::grabWindow(QX11Info::appRootWindow(), x, y, w, h); +} + +//------------------------------------------------------------------------- + +static int window_get_virtual_desktop(QWidget *window) +{ + return 0; //X11_window_get_desktop(window->winId()); +} + +static void window_set_virtual_desktop(QWidget *window, bool visible, int desktop) +{ + //X11_window_set_desktop(window->winId(), window->isVisible(), desktop); +} + +static void window_remap(QWidget *window) +{ + //X11_window_remap(window->effectiveWinId()); +} + +static void window_set_properties(QWidget *window, int which, QT_WINDOW_PROP *prop) +{ + static bool warn_stacking = false; + + Qt::WindowFlags flags = window->windowFlags(); + bool visible = window->isVisible(); + + if (prop->stacking && !warn_stacking) + { + fprintf(stderr, "gb.qt5.wayland: warning: 'Window.Stacking' property is not supported on wayland\n"); + warn_stacking = true; + } + + if (prop->stacking == 1) + flags |= Qt::WindowStaysOnTopHint; + else + flags &= ~Qt::WindowStaysOnTopHint; + + if (prop->stacking == 2) + flags |= Qt::WindowStaysOnBottomHint; + else + flags &= ~Qt::WindowStaysOnBottomHint; + + if (!prop->border) + flags |= Qt::FramelessWindowHint; + else + flags &= ~Qt::FramelessWindowHint; + + window->setWindowFlags(flags); + if (visible) + window->show(); +} + +static void window_set_user_time(QWidget *window, int timestamp) +{ + //X11_window_set_user_time(window->effectiveWinId(), 0); +} + +static void window_set_transient_for(QWidget *window, QWidget *parent) +{ +#if QT_VERSION >= 0x051300 + window->windowHandle()->setTransientParent(parent->windowHandle()); +#endif +} + +static void window_activate(QWidget *win) +{ + win->windowHandle()->alert(0); +} + +//------------------------------------------------------------------------- + +extern "C" { + +const GB_INTERFACE *GB_PTR EXPORT; + +void *GB_QT5_WAYLAND_1[] EXPORT = { + + (void *)QT_PLATFORM_INTERFACE_VERSION, + + (void *)platform_init, + (void *)platform_exit, + + (void *)release_grab, + (void *)unrelease_grab, + (void *)get_last_key_code, + + (void *)desktop_get_resolution_x, + (void *)desktop_get_resolution_y, + (void *)desktop_screenshot, + + (void *)window_get_virtual_desktop, + (void *)window_set_virtual_desktop, + (void *)window_remap, + (void *)window_set_properties, + (void *)window_set_user_time, + (void *)window_set_transient_for, + (void *)window_activate, + + NULL + }; + + +int EXPORT GB_INFO(const char *key, void **value) +{ + /*if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)QX11Info::display(); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)QX11Info::appRootWindow(); + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)QX11Info::appTime(); + return TRUE; + } + else*/ + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + diff --git a/gb.qt5/src/wayland/main.h b/gb.qt5/src/wayland/main.h new file mode 100644 index 00000000..7b881e3f --- /dev/null +++ b/gb.qt5/src/wayland/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" +//#include "../gb.qt.h" +#include "../gb.qt.platform.h" + +#ifndef __MAIN_C +extern "C" const GB_INTERFACE *GB_PTR; +#endif +#define GB (*GB_PTR) + +#endif diff --git a/gb.qt5/src/webkit/Makefile.am b/gb.qt5/src/webkit/Makefile.am new file mode 100644 index 00000000..ec9c3011 --- /dev/null +++ b/gb.qt5/src/webkit/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.qt5.webkit +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.webkit.la + +gb_qt5_webkit_la_LIBADD = @QT5WEBKIT_LIB@ +gb_qt5_webkit_la_LDFLAGS = -module @LD_FLAGS@ @QT5WEBKIT_LDFLAGS@ +gb_qt5_webkit_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_webkit_la_CPPFLAGS = @QT5WEBKIT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt5_webkit_la_SOURCES = \ + main.h main.cpp \ + cwebsettings.h cwebsettings.cpp \ + cwebframe.h cwebframe.cpp cwebframe_moc.cpp \ + cwebelement.h cwebelement.cpp \ + cwebview.h cwebview.cpp cwebview_moc.cpp \ + ccookiejar.h ccookiejar.cpp ccookiejar_moc.cpp \ + cwebhittest.h cwebhittest.cpp \ + cwebdownload.h cwebdownload.cpp cwebdownload_moc.cpp diff --git a/gb.qt5/src/webkit/ccookiejar.cpp b/gb.qt5/src/webkit/ccookiejar.cpp new file mode 120000 index 00000000..b5ea8b4f --- /dev/null +++ b/gb.qt5/src/webkit/ccookiejar.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/webkit/ccookiejar.cpp \ No newline at end of file diff --git a/gb.qt5/src/webkit/ccookiejar.h b/gb.qt5/src/webkit/ccookiejar.h new file mode 120000 index 00000000..93f3e108 --- /dev/null +++ b/gb.qt5/src/webkit/ccookiejar.h @@ -0,0 +1 @@ +../../../gb.qt4/src/webkit/ccookiejar.h \ No newline at end of file diff --git a/gb.qt5/src/webkit/control/webview.png b/gb.qt5/src/webkit/control/webview.png new file mode 100644 index 0000000000000000000000000000000000000000..3f8789b0026559711b4a8386dd471255ac8118ef GIT binary patch literal 1572 zcmV+<2HW|GP)LiI!86h!ZiUd+r z1QG+psBKy(G*wYlnz~6H$94RQJUT; zajeK?ERIeObNuKiqa(%tIr~@uTHn7wDaDmHZgYKh9XI2G5Nuc5cwT_l2d#XgC}8C? zlm_zjjg*)?G|bp!A8yK~-UujEviOO?rC+|m{QTyB?C2T5FeF-QUjE~CqF57j6{hQ8 zyB5-vU`m8-qAi2%-4@Mu#9B3Ed8@_j+y*yqZIZ9#u(D}h`qf|YyygRI;ei&s_WCWB zS3Q&v)LIdrJvc}q@8SnBi|Y+Gy^!KSfkeQ{T8-Olb-W;^9wuyhA$Qh1R`1pMU~ZF_ z|8V`2S>Sm-Z@#sNn|AR0E+@Y@!PrO2FH5QkvL_+eHE4Nu$t@aM^-uvs!ez--XG^I+0eS-xO1nnpx>?-z;m3ijiAUE#p z^3j%0Xc+7SF^#SU$E2e*k%EbXqYOSZjE~9de_QAs;O|!#Szd2YZFJbI1?<*CkOFB+ zT-Tznn4;bcSlemh$BM)>P?m{sY|OMnECq=axLKD{B}Zkb#G7y5=^bExVTT0x$GdC% z>(&;r(u7fhA9k@U$wsZk>UM*t#!Eai)rXaFz_pRCg|sb%ZDKek`GU*XP#)VhxU*W{ z6JXCqRBv?s68US@P(j#i}bI*J>cK7Bcdb|T)GB-C{ zxG9^N0~Puz8I)t9Yy)ANNK29kNuiL&aZKjsH(6d-r@m8Xppxkw;KcE9Of&|XLdGVD z5=JU1s*z%`7V)hkLp*zI1Z3 zcMG09GRBe54e;JlgTRl7?1*LYgnM@WP_T_Bm*PF~O?Lw!EX-FiRsnHzge{k$UVfF@4N@JQPFZ|?docjC#1w-RF z7HC-5Xz|WUon{oXzhE((GwDki`0Wl}!$&BM<=RN4nJ7B^;)T=n_vg|26O3ai1zKzR z`g8pJXQ%k~Oh4r)U?OKTRZcUQGRWJKv@Pf=_^1}*`5jy-unmdyLyiu){QBZ^OdlQr zK#0A^@dH;fA?`y|`trQ^)30#kl{tR@>H=%q4JyQ}^r!G@0e5Qwt#(A+3s|1pU?^yD z_8XJ@@XTQC7)%y8n^7_VJj=IZfz+j&5 zP51Nllg~0SQ33wD);%`B$98kd)Tu-vIhR2`V_!~`@hsDrGEp7 WE}(cmn$(m40000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WEBSETTINGS_CPP + +#include +#include + +#include "c_webview.h" +#include "c_websettings.h" + +//static QNetworkDiskCache *_cache = 0; +/*static bool _cache_enabled = false; +static char *_cache_path = 0; + +static void set_cache(bool on) +{ + QNetworkDiskCache *cache; + + if (!_cache_path) + return; + + _cache_enabled = on; + + if (on) + { + cache = new QNetworkDiskCache(0); + cache->setCacheDirectory(TO_QSTRING(_cache_path)); + WEBVIEW_get_network_manager()->setCache(cache); + } + else + WEBVIEW_get_network_manager()->setCache(0); +}*/ + + +static QWebEngineSettings *get_settings(void *_object) +{ + if (GB.Is(_object, GB.FindClass("WebSettings"))) + return QWebEngineSettings::defaultSettings(); + else + return WEBVIEW->page()->profile()->settings(); +} + +//------------------------------------------------------------------------- + +static void handle_font_family(QWebEngineSettings::FontFamily font, void *_object, void *_param) +{ + if (READ_PROPERTY) + RETURN_NEW_STRING(get_settings(_object)->fontFamily(font)); + else + get_settings(_object)->setFontFamily(font, QSTRING_PROP()); +} + +BEGIN_PROPERTY(WebSettingsFonts_Default) + + handle_font_family(QWebEngineSettings::StandardFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Fixed) + + handle_font_family(QWebEngineSettings::FixedFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Serif) + + handle_font_family(QWebEngineSettings::SerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_SansSerif) + + handle_font_family(QWebEngineSettings::SansSerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Cursive) + + handle_font_family(QWebEngineSettings::CursiveFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Fantasy) + + handle_font_family(QWebEngineSettings::FantasyFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_Pictograph) + + handle_font_family(QWebEngineSettings::PictographFont, _object, _param); + +END_METHOD + +static void handle_font_size(QWebEngineSettings::FontSize size, void *_object, void *_param) +{ + if (READ_PROPERTY) + GB.ReturnInteger(get_settings(_object)->fontSize(size) * 72 / 96); + else + get_settings(_object)->setFontSize(size, VPROP(GB_INTEGER) * 96 / 72); +} + +BEGIN_PROPERTY(WebSettingsFonts_DefaultSize) + + handle_font_size(QWebEngineSettings::DefaultFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_DefaultFixedSize) + + handle_font_size(QWebEngineSettings::DefaultFixedFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_MinimumSize) + + handle_font_size(QWebEngineSettings::MinimumFontSize, _object, _param); + +END_PROPERTY + +/*BEGIN_PROPERTY(WebSettingsFonts_MinimumLogicalFontSize) + + handle_font_size(QWebEngineSettings::MinimumLogicalFontSize, _object, _param); + +END_PROPERTY*/ + +#if 0 +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettingsIconDatabase_Clear) + + QWebEngineSettings::clearIconDatabase(); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsIconDatabase_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(QWebEngineSettings::iconDatabasePath()); + else + QWebEngineSettings::setIconDatabasePath(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_METHOD(WebSettingsIconDatabase_get, GB_STRING url) + + QIcon icon; + QSize size; + QSize max_size; + + icon = QWebEngineSettings::iconForUrl(QSTRING_ARG(url)); + if (icon.isNull()) + { + GB.ReturnNull(); + return; + } + + foreach(size, icon.availableSizes()) + { + if ((size.width() * size.height()) > (max_size.width() * max_size.height())) + max_size = size; + } + + GB.ReturnObject(QT.CreatePicture(icon.pixmap(max_size))); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsCache_Path) + + if (READ_PROPERTY) + GB.ReturnString(_cache_path); + else + { + char *path = GB.FileName(PSTRING(), PLENGTH()); + QString qpath = QString(path); + QString root = QString(GB.System.Home()); + + if (root.at(root.length() - 1) != '/') + root += '/'; + root += ".cache/"; + if (!qpath.startsWith(root)) + { + GB.Error("Cache directory must be located inside ~/.cache"); + return; + } + + GB.FreeString(&_cache_path); + _cache_path = GB.NewZeroString(path); + set_cache(_cache_enabled); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsCache_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(_cache_enabled); + else + set_cache(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +static int _clear_error; +static char *_clear_path; + +static void remove_file(const char *path) +{ + if (rmdir(path) == 0) + return; + + if (errno == ENOTDIR) + { + if (unlink(path) == 0) + return; + } + + if (_clear_error == 0) + { + _clear_error = errno; + _clear_path = GB.NewZeroString(path); + } +} + +BEGIN_METHOD_VOID(WebSettingsCache_Clear) + + if (!_cache_path || !*_cache_path) + return; + + _clear_error = 0; + GB.BrowseDirectory(_cache_path, NULL, remove_file); + + if (_clear_error) + { + GB.Error("Unable to remove '&1': &2", _clear_path, strerror(_clear_error)); + GB.FreeString(&_clear_path); + } + + set_cache(_cache_enabled); + +END_METHOD +#endif + +//------------------------------------------------------------------------- + +BEGIN_METHOD(WebSettings_get, GB_INTEGER flag) + + QWebEngineSettings *settings = get_settings(_object); + int flag = VARG(flag); + + if (flag >= 0) + GB.ReturnBoolean(settings->testAttribute((QWebEngineSettings::WebAttribute)flag)); + else + GB.ReturnBoolean(FALSE); + +END_METHOD + +BEGIN_METHOD(WebSettings_put, GB_BOOLEAN value; GB_INTEGER flag) + + QWebEngineSettings *settings = get_settings(_object); + int flag = VARG(flag); + + if (flag >= 0) + settings->setAttribute((QWebEngineSettings::WebAttribute)flag, VARG(value)); + +END_METHOD + +/*BEGIN_METHOD(WebSettings_Reset, GB_INTEGER flag) + + QWebEngineSettings *settings = get_settings(_object); + int flag = VARG(flag); + + if (flag >= 0) + settings->resetAttribute((QWebEngineSettings::WebAttribute)flag); + +END_METHOD*/ + +#if 0 +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettings_exit) + + GB.FreeString(&_cache_path); + +END_METHOD + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsProxy_Host) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.hostName()); + else + { + proxy.setHostName(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_User) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.user()); + else + { + proxy.setUser(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Password) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.password()); + else + { + proxy.setPassword(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Port) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.port()); + else + { + proxy.setPort(VPROP(GB_INTEGER)); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Type) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.type()); + else + { + int type = VPROP(GB_INTEGER); + if (type == QNetworkProxy::DefaultProxy || type == QNetworkProxy::NoProxy || type == QNetworkProxy::Socks5Proxy || type == QNetworkProxy::HttpProxy) + { + proxy.setType((QNetworkProxy::ProxyType)type); + nam->setProxy(proxy); + } + } + +END_PROPERTY +#endif + +/***************************************************************************/ + +#if 0 +GB_DESC WebSettingsIconDatabaseDesc[] = +{ + GB_DECLARE(".WebSettings.IconDatabase", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Clear", NULL, WebSettingsIconDatabase_Clear, NULL), + GB_STATIC_PROPERTY("Path", "s", WebSettingsIconDatabase_Path), + GB_STATIC_METHOD("_get", "Picture", WebSettingsIconDatabase_get, "(Url)s"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsCacheDesc[] = +{ + GB_DECLARE(".WebSettings.Cache", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Enabled", "b", WebSettingsCache_Enabled), + GB_STATIC_PROPERTY("Path", "s", WebSettingsCache_Path), + GB_STATIC_METHOD("Clear", NULL, WebSettingsCache_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsProxyDesc[] = +{ + GB_DECLARE(".WebSettings.Proxy", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Type", "i", WebSettingsProxy_Type), + GB_STATIC_PROPERTY("Host", "s", WebSettingsProxy_Host), + GB_STATIC_PROPERTY("Port", "i", WebSettingsProxy_Port), + GB_STATIC_PROPERTY("User", "s", WebSettingsProxy_User), + GB_STATIC_PROPERTY("Password", "s", WebSettingsProxy_Password), + + GB_END_DECLARE +}; +#endif + +GB_DESC WebSettingsFontsDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebSettings.Fonts"), + + GB_PROPERTY("Default", "s", WebSettingsFonts_Default), + GB_PROPERTY("Fixed", "s", WebSettingsFonts_Fixed), + GB_PROPERTY("Serif", "s", WebSettingsFonts_Serif), + GB_PROPERTY("SansSerif", "s", WebSettingsFonts_SansSerif), + GB_PROPERTY("Cursive", "s", WebSettingsFonts_Cursive), + GB_PROPERTY("Fantasy", "s", WebSettingsFonts_Fantasy), + GB_PROPERTY("Pictograph", "s", WebSettingsFonts_Pictograph), + + GB_PROPERTY("MinimumSize", "i", WebSettingsFonts_MinimumSize), + GB_PROPERTY("DefaultSize", "i", WebSettingsFonts_DefaultSize), + GB_PROPERTY("DefaultFixedSize", "i", WebSettingsFonts_DefaultFixedSize), + + GB_END_DECLARE +}; + +GB_DESC WebViewSettingsDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Settings"), + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + GB_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsDesc[] = +{ + GB_DECLARE("WebSettings", sizeof(CWEBSETTINGS)), GB_AUTO_CREATABLE(), GB_NOT_CREATABLE(), + + GB_CONSTANT("AutoLoadImages", "i", QWebEngineSettings::AutoLoadImages), + GB_CONSTANT("Javascript", "i", QWebEngineSettings::JavascriptEnabled), + GB_CONSTANT("JavascriptCanOpenWindows", "i", QWebEngineSettings::JavascriptCanOpenWindows), + GB_CONSTANT("JavascriptCanAccessClipboard", "i", QWebEngineSettings::JavascriptCanAccessClipboard), + //GB_CONSTANT("LinksIncludedInFocusChain", "i", QWebEngineSettings::LinksIncludedInFocusChain), + GB_CONSTANT("LocalStorage", "i", QWebEngineSettings::LocalStorageEnabled), + //GB_CONSTANT("LocalContentCanAccessRemoteUrls", "i", QWebEngineSettings::LocalContentCanAccessRemoteUrls), + GB_CONSTANT("SpatialNavigation", "i", QWebEngineSettings::SpatialNavigationEnabled), + GB_CONSTANT("LocalContentCanAccessFileUrls", "i", QWebEngineSettings::LocalContentCanAccessFileUrls), + GB_CONSTANT("HyperlinkAuditing", "i", QWebEngineSettings::HyperlinkAuditingEnabled), + //GB_CONSTANT("ScrollAnimatorEnabled", "i", QWebEngineSettings::ScrollAnimatorEnabled), + //GB_CONSTANT("ErrorPageEnabled", "i", QWebEngineSettings::ErrorPageEnabled), +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + GB_CONSTANT("Plugins", "i", QWebEngineSettings::PluginsEnabled), + GB_CONSTANT("FullScreenSupport", "i", QWebEngineSettings::FullScreenSupportEnabled), +#else + GB_CONSTANT("Plugins", "i", -1), + GB_CONSTANT("FullScreenSupport", "i", -1), +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + //GB_CONSTANT("ScreenCaptureEnabled", "i", QWebEngineSettings::ScreenCaptureEnabled), + GB_CONSTANT("WebGL", "i", QWebEngineSettings::WebGLEnabled), + GB_CONSTANT("Accelerated2dCanvas", "i", QWebEngineSettings::Accelerated2dCanvasEnabled), + //GB_CONSTANT("AutoLoadIconsForPage", "i", QWebEngineSettings::AutoLoadIconsForPage), + //GB_CONSTANT("TouchIconsEnabled", "i", QWebEngineSettings::TouchIconsEnabled), +#else + //GB_CONSTANT("ScreenCaptureEnabled", "i", -1), + GB_CONSTANT("WebGL", "i", -1), + GB_CONSTANT("Accelerated2dCanvas", "i", -1), + //GB_CONSTANT("AutoLoadIconsForPage", "i", -1), + //GB_CONSTANT("TouchIconsEnabled", "i", -1), +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) + //GB_CONSTANT("FocusOnNavigationEnabled", "i", QWebEngineSettings::FocusOnNavigationEnabled), + GB_CONSTANT("PrintElementBackgrounds", "i", QWebEngineSettings::PrintElementBackgrounds), + //GB_CONSTANT("AllowRunningInsecureContent", "i", QWebEngineSettings::AllowRunningInsecureContent), +#else + //GB_CONSTANT("FocusOnNavigationEnabled", "i", -1), + GB_CONSTANT("PrintElementBackgrounds", "i", -1), + //GB_CONSTANT("AllowRunningInsecureContent", "i", -1), +#endif +/*#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + GB_CONSTANT("AllowGeolocationOnInsecureOrigins", "i", QWebEngineSettings::AllowGeolocationOnInsecureOrigins), +#else + GB_CONSTANT("AllowGeolocationOnInsecureOrigins", "i", -1), +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + GB_CONSTANT("AllowWindowActivationFromJavaScript", "i", QWebEngineSettings::AllowWindowActivationFromJavaScript), + GB_CONSTANT("ShowScrollBars", "i", QWebEngineSettings::ShowScrollBars), +#else + GB_CONSTANT("AllowWindowActivationFromJavaScript", "i", -1), + GB_CONSTANT("ShowScrollBars", "i", -1), +#endif*/ +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + GB_CONSTANT("PlaybackRequiresUserGesture", "i", QWebEngineSettings::PlaybackRequiresUserGesture), + //GB_CONSTANT("JavascriptCanPaste", "i", QWebEngineSettings::JavascriptCanPaste), + //GB_CONSTANT("WebRTCPublicInterfacesOnly", "i", QWebEngineSettings::WebRTCPublicInterfacesOnly), +#else + GB_CONSTANT("PlaybackRequiresUserGesture", "i", -1), + //GB_CONSTANT("JavascriptCanPaste", "i", -1), + //GB_CONSTANT("WebRTCPublicInterfacesOnly", "i", -1), +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + GB_CONSTANT("DnsPrefetch", "i", QWebEngineSettings::DnsPrefetchEnabled), +#else + GB_CONSTANT("DnsPrefetch", "i", -1), +#endif +/*#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + GB_CONSTANT("PdfViewerEnabled", "i", QWebEngineSettings::PdfViewerEnabled), +#else + GB_CONSTANT("PdfViewerEnabled", "i", -1), +#endif*/ + + /*GB_CONSTANT("DefaultProxy", "i", QNetworkProxy::DefaultProxy), + GB_CONSTANT("NoProxy", "i", QNetworkProxy::NoProxy), + GB_CONSTANT("Socks5Proxy", "i", QNetworkProxy::Socks5Proxy), + GB_CONSTANT("HttpProxy", "i", QNetworkProxy::HttpProxy),*/ + + GB_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + /*GB_STATIC_PROPERTY_SELF("IconDatabase", ".WebSettings.IconDatabase"), + GB_STATIC_PROPERTY_SELF("Cache", ".WebSettings.Cache"), + GB_STATIC_PROPERTY_SELF("Proxy", ".WebSettings.Proxy"),*/ + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + //GB_STATIC_METHOD("_exit", NULL, WebSettings_exit, NULL), + +GB_END_DECLARE +}; + + diff --git a/gb.qt5/src/webview/c_websettings.h b/gb.qt5/src/webview/c_websettings.h new file mode 100644 index 00000000..bca29f36 --- /dev/null +++ b/gb.qt5/src/webview/c_websettings.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + c_websettings.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WEBSETTINGS_H +#define __C_WEBSETTINGS_H + +#include "main.h" + +#include +#include +#include + +typedef + struct { + GB_BASE ob; + } + CWEBSETTINGS; + +#ifndef __C_WEBSETTINGS_CPP + +/*extern GB_DESC WebSettingsIconDatabaseDesc[]; +extern GB_DESC WebSettingsCacheDesc[]; +extern GB_DESC WebSettingsProxyDesc[];*/ +extern GB_DESC WebViewSettingsDesc[]; +extern GB_DESC WebSettingsDesc[]; +extern GB_DESC WebSettingsFontsDesc[]; + +#else + +#define WEBVIEW ((QWebEngineView *)((QT_WIDGET *)_object)->widget) + +#endif + +//void WEBSETTINGS_set_cache(QWebView *view, bool on); + +#endif diff --git a/gb.qt5/src/webview/c_webview.cpp b/gb.qt5/src/webview/c_webview.cpp new file mode 100644 index 00000000..02d93c79 --- /dev/null +++ b/gb.qt5/src/webview/c_webview.cpp @@ -0,0 +1,1268 @@ +/*************************************************************************** + + c_webview.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WEBVIEW_CPP + +/*#include +#include +#include +#include +#include +#include */ +#include +#include +#include +#include +#include +#include + +/*#include "ccookiejar.h" +#include "cwebsettings.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebhittest.h"*/ +#include "c_webview.h" + +#define HISTORY (WIDGET->history()) + +static bool _cb_exited = FALSE; +static volatile bool _cb_running = FALSE; +static bool _cb_error = FALSE; +static char *_cb_result = NULL; + +/*typedef + struct { + const char *name; + QWebPage::WebAction action; + } + WEBVIEW_ACTION;*/ + +//--------------------------------------------------------------------------- + +DECLARE_EVENT(EVENT_TITLE); +DECLARE_EVENT(EVENT_URL); +DECLARE_EVENT(EVENT_ICON); +DECLARE_EVENT(EVENT_START); +DECLARE_EVENT(EVENT_PROGRESS); +DECLARE_EVENT(EVENT_FINISH); +DECLARE_EVENT(EVENT_ERROR); +DECLARE_EVENT(EVENT_LINK); +DECLARE_EVENT(EVENT_NEW_VIEW); + +static int EVENT_MENU = -1; +/*DECLARE_EVENT(EVENT_CLICK); +DECLARE_EVENT(EVENT_LINK); +DECLARE_EVENT(EVENT_NEW_FRAME); +DECLARE_EVENT(EVENT_AUTH); +DECLARE_EVENT(EVENT_DOWNLOAD);*/ + + + +/* +static QNetworkAccessManager *_network_access_manager = 0; +static CWEBVIEW *_network_access_manager_view = 0; +static QT_COLOR_FUNC _old_after_set_color; +static bool _ignore_png_warnings = false;*/ + +/* +static WEBVIEW_ACTION _actions[] = +{ + { "OpenLink", QWebPage::OpenLink }, + { "OpenLinkInNewWindow", QWebPage::OpenLinkInNewWindow }, + { "OpenFrameInNewWindow", QWebPage::OpenFrameInNewWindow }, + { "DownloadLinkToDisk", QWebPage::DownloadLinkToDisk }, + { "CopyLinkToClipboard", QWebPage::CopyLinkToClipboard }, + { "OpenImageInNewWindow", QWebPage::OpenImageInNewWindow }, + { "DownloadImageToDisk", QWebPage::DownloadImageToDisk }, + { "CopyImageToClipboard", QWebPage::CopyImageToClipboard }, + { "Back", QWebPage::Back }, + { "Forward", QWebPage::Forward }, + { "Stop", QWebPage::Stop }, + { "StopScheduledPageRefresh", QWebPage::StopScheduledPageRefresh }, + { "Reload", QWebPage::Reload }, + { "ReloadAndBypassCache", QWebPage::ReloadAndBypassCache }, + { "Cut", QWebPage::Cut }, + { "Copy", QWebPage::Copy }, + { "Paste", QWebPage::Paste }, + { "Undo", QWebPage::Undo }, + { "Redo", QWebPage::Redo }, + { "MoveToNextChar", QWebPage::MoveToNextChar }, + { "MoveToPreviousChar", QWebPage::MoveToPreviousChar }, + { "MoveToNextWord", QWebPage::MoveToNextWord }, + { "MoveToPreviousWord", QWebPage::MoveToPreviousWord }, + { "MoveToNextLine", QWebPage::MoveToNextLine }, + { "MoveToPreviousLine", QWebPage::MoveToPreviousLine }, + { "MoveToStartOfLine", QWebPage::MoveToStartOfLine }, + { "MoveToEndOfLine", QWebPage::MoveToEndOfLine }, + { "MoveToStartOfBlock", QWebPage::MoveToStartOfBlock }, + { "MoveToEndOfBlock", QWebPage::MoveToEndOfBlock }, + { "MoveToStartOfDocument", QWebPage::MoveToStartOfDocument }, + { "MoveToEndOfDocument", QWebPage::MoveToEndOfDocument }, + { "SelectNextChar", QWebPage::SelectNextChar }, + { "SelectPreviousChar", QWebPage::SelectPreviousChar }, + { "SelectNextWord", QWebPage::SelectNextWord }, + { "SelectPreviousWord", QWebPage::SelectPreviousWord }, + { "SelectNextLine", QWebPage::SelectNextLine }, + { "SelectPreviousLine", QWebPage::SelectPreviousLine }, + { "SelectStartOfLine", QWebPage::SelectStartOfLine }, + { "SelectEndOfLine", QWebPage::SelectEndOfLine }, + { "SelectStartOfBlock", QWebPage::SelectStartOfBlock }, + { "SelectEndOfBlock", QWebPage::SelectEndOfBlock }, + { "SelectStartOfDocument", QWebPage::SelectStartOfDocument }, + { "SelectEndOfDocument", QWebPage::SelectEndOfDocument }, + { "DeleteStartOfWord", QWebPage::DeleteStartOfWord }, + { "DeleteEndOfWord", QWebPage::DeleteEndOfWord }, + { "SetTextDirectionDefault", QWebPage::SetTextDirectionDefault }, + { "SetTextDirectionLeftToRight", QWebPage::SetTextDirectionLeftToRight }, + { "SetTextDirectionRightToLeft", QWebPage::SetTextDirectionRightToLeft }, + { "ToggleBold", QWebPage::ToggleBold }, + { "ToggleItalic", QWebPage::ToggleItalic }, + { "ToggleUnderline", QWebPage::ToggleUnderline }, + { "InspectElement", QWebPage::InspectElement }, + { "InsertParagraphSeparator", QWebPage::InsertParagraphSeparator }, + { "InsertLineSeparator", QWebPage::InsertLineSeparator }, + { "SelectAll", QWebPage::SelectAll }, + { "PasteAndMatchStyle", QWebPage::PasteAndMatchStyle }, + { "RemoveFormat", QWebPage::RemoveFormat }, + { "ToggleStrikethrough", QWebPage::ToggleStrikethrough }, + { "ToggleSubscript", QWebPage::ToggleSubscript }, + { "ToggleSuperscript", QWebPage::ToggleSuperscript }, + { "InsertUnorderedList", QWebPage::InsertUnorderedList }, + { "InsertOrderedList", QWebPage::InsertOrderedList }, + { "Indent", QWebPage::Indent }, + { "Unindent", QWebPage::Outdent }, + { "AlignCenter", QWebPage::AlignCenter }, + { "AlignJustified", QWebPage::AlignJustified }, + { "AlignLeft", QWebPage::AlignLeft }, + { "AlignRight", QWebPage::AlignRight }, + { NULL, QWebPage::NoWebAction } +}; +*/ + +#if 0 +QNetworkAccessManager *WEBVIEW_get_network_manager() +{ + if (!_network_access_manager) + { + _network_access_manager = new QNetworkAccessManager(); + _network_access_manager->setCookieJar(new MyCookieJar); + } + + return _network_access_manager; +} + +/* +static QWebPage::WebAction get_action(const char *name) +{ + WEBVIEW_ACTION *p; + + for (p = _actions; p->name; p++) + { + if (!strcasecmp(p->name, name)) + return p->action; + } + + return QWebPage::NoWebAction; +} +*/ + +static void after_set_color(void *_object) +{ + if (!GB.Is(THIS, CLASS_WebView)) + { + if (_old_after_set_color) + (*_old_after_set_color)(THIS); + return; + } + + if (QT.GetBackgroundColor(THIS) == GB_COLOR_DEFAULT) + { + QPalette palette = WIDGET->palette(); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, true); + } + else + { + qDebug("after_set_color"); + QPalette palette = WIDGET->palette(); + palette.setBrush(QPalette::Base, Qt::transparent); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, false); + } +} + +static void stop_view(void *_object) +{ + //fprintf(stderr, "stop_view\n"); + THIS->stopping = TRUE; + WIDGET->stop(); + THIS->stopping = FALSE; +} +#endif + + +static void set_link(void *_object, const QString &link) +{ + GB.FreeString(&THIS->link); + THIS->link = QT.NewString(link); +} + +static bool start_callback() +{ + if (_cb_running) + { + GB.Error("Pending asynchronous method"); + return true; + } + + _cb_running = true; + return false; +} + +static void run_callback(const char *error) +{ + while(_cb_running) + GB.Wait(-1); + + if (_cb_error) + { + GB.Error(error); + GB.FreeString(&_cb_result); + } + else + { + GB.ReturnString(GB.FreeStringLater(_cb_result)); + _cb_result = NULL; + } + + _cb_error = FALSE; +} + +static void cb_javascript_finished(const QVariant &result) +{ + if (_cb_exited) + return; + + QVariantList value; + value.append(result); + + QByteArray array = QJsonDocument::fromVariant(value).toJson(QJsonDocument::Compact); + + if (array.size() > 2) + _cb_result = GB.NewString(array.constData() + 1, array.size() - 2); + + _cb_running = FALSE; +} + +static void cb_html_finished(const QString &result) +{ + if (_cb_exited) + return; + + _cb_result = QT.NewString(result); + + _cb_running = FALSE; +} + +static void update_language(void *_object) +{ + QString lang; + + if (THIS->language && *THIS->language) + lang = TO_QSTRING(THIS->language); + else + lang = TO_QSTRING(GB.System.Language()).replace("_", "-"); + + WIDGET->page()->profile()->setHttpAcceptLanguage(lang); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(WebView_exit) + + _cb_exited = TRUE; + GB.FreeString(&_cb_result); + +END_METHOD + +BEGIN_METHOD(WebView_new, GB_OBJECT parent) + + /*int fd_save = -1; + + if (!_ignore_png_warnings) + { + int fd = ::open("/dev/null", O_RDWR); + fd_save = ::dup(2); + ::dup2(fd, 2); + ::close(fd); + }*/ + + MyWebEngineView *wid = new MyWebEngineView(QT.GetContainer(VARG(parent))); + + //wid->setAttribute(Qt::WA_NativeWindow, true); + + /*if (!_ignore_png_warnings) + { + ::dup2(fd_save, 2); + ::close(fd_save); + _ignore_png_warnings = true; + }*/ + + QT.InitWidget(wid, _object, false); + QT.SetWheelFlag(_object); + + //WEBVIEW_get_network_manager(); + + //wid->page()->setNetworkAccessManager(_network_access_manager); + //wid->page()->setForwardUnsupportedContent(true); + + QObject::connect(wid, SIGNAL(iconChanged(const QIcon &)), &WebViewSignalManager::manager, SLOT(iconChanged())); + QObject::connect(wid, SIGNAL(titleChanged(const QString &)), &WebViewSignalManager::manager, SLOT(titleChanged())); + QObject::connect(wid, SIGNAL(urlChanged(const QUrl &)), &WebViewSignalManager::manager, SLOT(urlChanged())); + QObject::connect(wid, SIGNAL(loadStarted()), &WebViewSignalManager::manager, SLOT(loadStarted())); + QObject::connect(wid, SIGNAL(loadProgress(int)), &WebViewSignalManager::manager, SLOT(loadProgress(int))); + QObject::connect(wid, SIGNAL(loadFinished(bool)), &WebViewSignalManager::manager, SLOT(loadFinished(bool))); + //QObject::connect(wid, SIGNAL(destroyed()), &WebViewSignalManager::manager, SLOT(destroy())); + + wid->clearPage(false); + update_language(THIS); + + //QObject::connect(wid, SIGNAL(linkClicked(const QUrl &)), &CWebView::manager, SLOT(linkClicked(const QUrl &))); +#if 0 + QObject::connect(wid, SIGNAL(selectionChanged()), &CWebView::manager, SLOT(selectionChanged())); + QObject::connect(wid, SIGNAL(statusBarMessage(const QString &)), &CWebView::manager, SLOT(statusBarMessage(const QString &))); + + QObject::connect(wid->page(), SIGNAL(frameCreated(QWebFrame *)), &CWebView::manager, SLOT(frameCreated(QWebFrame *))); + QObject::connect(wid->page(), SIGNAL(downloadRequested(QNetworkRequest)), &CWebView::manager, SLOT(downloadRequested(QNetworkRequest))); + QObject::connect(wid->page(), SIGNAL(unsupportedContent(QNetworkReply*)), &CWebView::manager, SLOT(handleUnsupportedContent(QNetworkReply*))); + + QObject::connect(wid->page()->mainFrame(), SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + + QObject::connect(wid->page()->networkAccessManager(), SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)), &CWebView::manager, + SLOT(authenticationRequired(QNetworkReply *, QAuthenticator *))); +#endif + +END_METHOD + +BEGIN_METHOD_VOID(WebView_free) + + GB.FreeString(&THIS->link); + GB.FreeString(&THIS->language); + + GB.Unref(POINTER(&THIS->icon)); + GB.Unref(POINTER(&THIS->new_view)); + +END_METHOD + + +/*BEGIN_METHOD_VOID(WebView_exit) + + delete _network_access_manager; + +END_METHOD*/ + +BEGIN_PROPERTY(WebView_Url) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->url().toString()); + else + { + QString url = QSTRING_PROP(); + set_link(THIS, url); + THIS->cancel = false; + WIDGET->setUrl(url); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Title) + + RETURN_NEW_STRING(WIDGET->title()); + +END_PROPERTY + +BEGIN_METHOD(WebView_SetHtml, GB_STRING html; GB_STRING root) + + if (!MISSING(root)) + { + QUrl url(QSTRING_ARG(root)); + WIDGET->setHtml(QSTRING_ARG(html), url); + } + else + WIDGET->setHtml(QSTRING_ARG(html)); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Back) + + WIDGET->back(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Forward) + + WIDGET->forward(); + +END_METHOD + +BEGIN_METHOD(WebView_Reload, GB_BOOLEAN bypass) + + bool bypass = VARGOPT(bypass, false); + //stop_view(THIS); + if (bypass) + WIDGET->page()->triggerAction(QWebEnginePage::ReloadAndBypassCache); + else + WIDGET->reload(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Stop) + + WIDGET->stop(); + +END_METHOD + +BEGIN_PROPERTY(WebView_Zoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->zoomFactor()); + else + WIDGET->setZoomFactor(VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Icon) + + if (!THIS->icon) + { + QIcon icon = WIDGET->icon(); + + if (!icon.isNull()) + { + int size = QT.GetDesktopScale() * 2; + THIS->icon = QT.CreatePicture(icon.pixmap(size, size)); + GB.Ref(THIS->icon); + } + } + + GB.ReturnObject(THIS->icon); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Progress) + + GB.ReturnFloat(THIS->progress / 100.0); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_NewView) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->new_view); + else + GB.StoreObject(PROP(GB_OBJECT), &THIS->new_view); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Link) + + GB.ReturnString(THIS->link); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebView_Clear) + + WIDGET->clearPage(true); + +END_METHOD + +BEGIN_METHOD(WebView_ExecJavascript, GB_STRING script) + + if (LENGTH(script) == 0) + return; + + if (start_callback()) + return; + + WIDGET->page()->runJavaScript(QSTRING_ARG(script), cb_javascript_finished); + + run_callback("Javascript error"); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_GetHtml) + + if (start_callback()) + return; + + WIDGET->page()->toHtml(cb_html_finished); + + run_callback("Unable to retrieve HTML contents"); + +END_METHOD + +BEGIN_PROPERTY(WebView_Language) + + if (READ_PROPERTY) + GB.ReturnString(THIS->language); + else + { + GB.StoreString(PROP(GB_STRING), &THIS->language); + update_language(THIS); + } + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(WebView_HTML) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->page()->mainFrame()->toHtml()); + else + WIDGET->setHtml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Text) + + RETURN_NEW_STRING(WIDGET->page()->mainFrame()->toPlainText()); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_SelectedText) + + RETURN_NEW_STRING(WIDGET->selectedText()); + +END_PROPERTY + +#if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) +BEGIN_PROPERTY(WebView_Zoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->zoomFactor()); + else + WIDGET->setZoomFactor(VPROP(GB_FLOAT)); + +END_PROPERTY +#endif + +BEGIN_PROPERTY(WebView_TextZoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->textSizeMultiplier()); + else + WIDGET->setTextSizeMultiplier(VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Status) + + if (READ_PROPERTY) + GB.ReturnString(THIS->status); + else + GB.StoreString(PROP(GB_STRING), &THIS->status); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Frame) + + GB.ReturnObject(CWEBFRAME_get(WIDGET->page()->mainFrame())); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Current) + + GB.ReturnObject(CWEBFRAME_get(WIDGET->page()->currentFrame())); + +END_PROPERTY + + +BEGIN_PROPERTY(WebViewAuth_Url) + + if (THIS->reply) + RETURN_NEW_STRING(THIS->reply->url().toString()); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_Realm) + + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->realm()); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_User) + + if (READ_PROPERTY) + { + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->user()); + else + GB.ReturnVoidString(); + } + else + { + if (THIS->authenticator) + THIS->authenticator->setUser(QSTRING_PROP()); + else + GB.Error("No authentication required"); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_Password) + + if (READ_PROPERTY) + { + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->password()); + else + GB.ReturnVoidString(); + } + else + { + if (THIS->authenticator) + THIS->authenticator->setPassword(QSTRING_PROP()); + else + GB.Error("No authentication required"); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Cookies) + + MyCookieJar *cookieJar = static_cast(_network_access_manager->cookieJar()); + QList list; + GB_ARRAY cookies; + int i; + + if (READ_PROPERTY) + { + list = cookieJar->allCookies(); + + GB.Array.New(POINTER(&cookies), GB.FindClass("Cookie"), list.count()); + + for (i = 0; i < list.count(); i++) + { + CCOOKIE *cookie = WEB_create_cookie(list.at(i)); + *((CCOOKIE **)(GB.Array.Get(cookies, i))) = cookie; + GB.Ref(cookie); + } + + GB.ReturnObject(cookies); + } + else + { + // TODO what todo? + cookies = VPROP(GB_OBJECT); + if (GB.CheckObject(cookies)) + return; + + for (i = 0; i < GB.Array.Count(cookies); i++) + { + CCOOKIE *cookie = *((CCOOKIE **)GB.Array.Get(cookies, i)); + if (GB.CheckObject(cookie)) + continue; + list.append(*(cookie->cookie)); + } + + cookieJar->setAllCookies(list); + } + +END_PROPERTY + +BEGIN_METHOD(WebView_HitTest, GB_INTEGER X; GB_INTEGER Y) + + GB.ReturnObject(WEB_create_hit_test(WIDGET->page()->mainFrame()->hitTestContent(QPoint(VARG(X), VARG(Y))))); + +END_METHOD + +BEGIN_METHOD(WebView_FindText, GB_STRING text; GB_BOOLEAN backward; GB_BOOLEAN case_sensitive; GB_BOOLEAN wrap) + + QString text; + QWebPage::FindFlags flags = 0; + + if (!MISSING(text)) + text = QSTRING_ARG(text); + + if (VARGOPT(backward, false)) flags |= QWebPage::FindBackward; + if (VARGOPT(case_sensitive, false)) flags |= QWebPage::FindCaseSensitively; + if (VARGOPT(wrap, false)) flags |= QWebPage::FindWrapsAroundDocument; + //if (VARGOPT(highlight, false)) flags |= QWebPage::HighlightAllOccurrences; + + GB.ReturnBoolean(!WIDGET->findText(text, flags)); + +END_METHOD + +BEGIN_PROPERTY(WebView_Editable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->page()->isContentEditable()); + else + WIDGET->page()->setContentEditable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(WebView_Exec, GB_STRING action; GB_VARIANT arg) + + QWebPage::WebAction action = get_action(GB.ToZeroString(ARG(action))); + + if (action == QWebPage::NoWebAction) + { + GB.Error("Unknown action"); + return; + } + + if (MISSING(arg)) + { + WIDGET->page()->triggerAction(action); + } + else + { + if (GB.Conv((GB_VALUE *)ARG(arg), GB_T_BOOLEAN)) + return; + + WIDGET->page()->triggerAction(action, ((GB_BOOLEAN *)ARG(arg))->value); + } + +END_METHOD +#endif + +/* +BEGIN_METHOD(WebView_CanExec, GB_STRING action) + + QWebPage::Action action = get_action(GB.ToZeroString(ARG(action))); + + if (action == QWebPage::NoWebAction) + { + GB.Error("Unknown action"); + return; + } + +END_METHOD +*/ + +BEGIN_METHOD(WebView_Eval, GB_STRING javascript) + + CWEBFRAME_eval(WIDGET->page()->currentFrame(), QSTRING_ARG(javascript)); + +END_METHOD + +BEGIN_PROPERTY(WebView_UserAgent) + + if (READ_PROPERTY) + GB.ReturnString(THIS->userAgent); + else + GB.StoreString(PROP(GB_STRING), &THIS->userAgent); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Document) + + GB.ReturnObject(CWEBELEMENT_create(WIDGET->page()->mainFrame()->documentElement())); + +END_PROPERTY +#endif + +//------------------------------------------------------------------------- + +static QWebEngineHistoryItem get_item(QWebEngineHistory *history, int index) +{ + return history->itemAt(history->currentItemIndex() + index); +} + +BEGIN_PROPERTY(WebView_History_Item_Title) + + QWebEngineHistoryItem item = get_item(HISTORY, THIS->history); + + if (item.isValid()) + RETURN_NEW_STRING(item.title()); + else + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_History_Item_Url) + + QWebEngineHistoryItem item = get_item(HISTORY, THIS->history); + + if (item.isValid()) + RETURN_NEW_STRING(item.url().toString()); + else + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebView_History_Item_GoTo) + + QWebEngineHistoryItem item = get_item(HISTORY, THIS->history); + + if (item.isValid()) + HISTORY->goToItem(item); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_History_Clear) + + HISTORY->clear(); + +END_METHOD + +BEGIN_METHOD(WebView_History_get, GB_INTEGER index) + + int index = VARG(index); + + index += HISTORY->currentItemIndex(); + if (index < 0 || index >= HISTORY->count()) + GB.ReturnNull(); + else + { + THIS->history = index; + RETURN_SELF(); + } + +END_METHOD + +BEGIN_PROPERTY(WebView_History_CanGoBack) + + GB.ReturnBoolean(HISTORY->canGoBack()); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_History_CanGoForward) + + GB.ReturnBoolean(HISTORY->canGoForward()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(WebView_Cookies_Clear) + + WIDGET->page()->profile()->cookieStore()->deleteAllCookies(); + +END_METHOD + +//------------------------------------------------------------------------- + +#if 0 +GB_DESC WebViewAuthDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Auth"), + + GB_PROPERTY_READ("Url", "s", WebViewAuth_Url), + GB_PROPERTY_READ("Realm", "s", WebViewAuth_Realm), + GB_PROPERTY("User", "s", WebViewAuth_User), + GB_PROPERTY("Password", "s", WebViewAuth_Password), + + GB_END_DECLARE +}; + +GB_DESC WebViewDesc[] = +{ + GB_DECLARE("WebView", sizeof(CWEBVIEW)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, WebView_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, WebView_free, NULL), + GB_METHOD("_init", NULL, WebView_init, NULL), + GB_METHOD("_exit", NULL, WebView_exit, NULL), + + GB_PROPERTY("Status", "s", WebView_Status), + + GB_PROPERTY_READ("SelectedText", "s", WebView_SelectedText), + #if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) + GB_PROPERTY("Zoom", "f", WebView_Zoom), + #else + GB_PROPERTY("Zoom", "f", WebView_TextZoom), + #endif + GB_PROPERTY("TextZoom", "f", WebView_TextZoom), + GB_PROPERTY_READ("Title", "s", WebView_Title), + + GB_PROPERTY_READ("Frame", "WebFrame", WebView_Frame), + GB_PROPERTY_READ("Current", "WebFrame", WebView_Current), + GB_PROPERTY_READ("Document", "WebElement", WebView_Document), + + GB_PROPERTY_SELF("Auth", ".WebView.Auth"), + GB_PROPERTY_SELF("History", ".WebView.History"), + + GB_PROPERTY("NewView", "WebView", WebView_NewView), + + GB_PROPERTY("Cookies", "Cookie[]", WebView_Cookies), + + GB_METHOD("HitTest", "WebHitTest", WebView_HitTest, "(X)i(Y)i"), + GB_METHOD("FindText", "b", WebView_FindText, "[(Text)s(Backward)b(CaseSensitive)b(Wrap)b]"), + + GB_PROPERTY("Editable", "b", WebView_Editable), + //GB_METHOD("Exec", NULL, WebView_Exec, "(Action)s[(Argument)v]"), + //GB_METHOD("CanExec", "b", WebView_CanExec, "(Action)s"), + GB_METHOD("Eval", "v", WebView_Eval, "(JavaScript)s"), + + GB_PROPERTY("UserAgent", "s", WebView_UserAgent), + + GB_CONSTANT("_Properties", "s", "*,Url,Editable"), + GB_CONSTANT("_Group", "s", "View"), + + GB_EVENT("Click", NULL, "(Frame)WebFrame", &EVENT_CLICK), + GB_EVENT("Link", NULL, "(Url)s", &EVENT_LINK), + GB_EVENT("Progress", NULL, NULL, &EVENT_PROGRESS), + GB_EVENT("Load", NULL, NULL, &EVENT_LOAD), + GB_EVENT("Error", NULL, NULL, &EVENT_ERROR), + GB_EVENT("Icon", NULL, NULL, &EVENT_ICON), + GB_EVENT("Title", NULL, NULL, &EVENT_TITLE), + GB_EVENT("Select", NULL, NULL, &EVENT_SELECT), + GB_EVENT("Status", NULL, NULL, &EVENT_STATUS), + GB_EVENT("NewWindow", NULL, "(Modal)b", &EVENT_NEW_WINDOW), + GB_EVENT("NewFrame", NULL, "(Frame)WebFrame", &EVENT_NEW_FRAME), + GB_EVENT("Auth", NULL, NULL, &EVENT_AUTH), + GB_EVENT("Download", NULL, "(Download)WebDownload", &EVENT_DOWNLOAD), + + GB_END_DECLARE +}; +#endif + +GB_DESC WebViewHistoryItemDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History.Item"), + + GB_PROPERTY_READ("Title", "s", WebView_History_Item_Title), + GB_PROPERTY_READ("Url", "s", WebView_History_Item_Url), + GB_METHOD("GoTo", NULL, WebView_History_Item_GoTo, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewHistoryDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History"), + + GB_METHOD("Clear", NULL, WebView_History_Clear, NULL), + GB_METHOD("_get", ".WebView.History.Item", WebView_History_get, "(Index)i"), + GB_PROPERTY_READ("CanGoBack", "b", WebView_History_CanGoBack), + GB_PROPERTY_READ("CanGoForward", "b", WebView_History_CanGoForward), + + GB_END_DECLARE +}; + +GB_DESC WebViewCookiesDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Cookies"), + + GB_METHOD("Clear", NULL, WebView_Cookies_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewDesc[] = +{ + GB_DECLARE("WebView", sizeof(CWEBVIEW)), GB_INHERITS("Control"), + + GB_STATIC_METHOD("_exit", NULL, WebView_exit, NULL), + + GB_METHOD("_new", NULL, WebView_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, WebView_free, NULL), + + GB_PROPERTY("Url", "s", WebView_Url), + GB_PROPERTY("Title", "s", WebView_Title), + GB_PROPERTY("Zoom", "f", WebView_Zoom), + GB_PROPERTY_READ("Icon", "Picture", WebView_Icon), + GB_PROPERTY_READ("Progress", "f", WebView_Progress), + GB_PROPERTY("NewView", "WebView", WebView_NewView), + GB_PROPERTY_READ("Link", "s", WebView_Link), + GB_PROPERTY("Language", "s", WebView_Language), + + GB_METHOD("SetHtml", NULL, WebView_SetHtml, "(Html)s[(Root)s]"), + GB_METHOD("GetHtml", "s", WebView_GetHtml, NULL), + + GB_METHOD("Clear", NULL, WebView_Clear, NULL), + + GB_METHOD("Back", NULL, WebView_Back, NULL), + GB_METHOD("Forward", NULL, WebView_Forward, NULL), + GB_METHOD("Reload", NULL, WebView_Reload, "[(BypassCache)b]"), + GB_METHOD("Stop", NULL, WebView_Stop, NULL), + + GB_METHOD("ExecJavascript", "s", WebView_ExecJavascript, "(Javascript)s"), + + GB_PROPERTY_SELF("History", ".WebView.History"), + GB_PROPERTY_SELF("Settings", ".WebView.Settings"), + GB_PROPERTY_SELF("Cookies", ".WebView.Cookies"), + + GB_CONSTANT("_Properties", "s", "*,Url,Zoom=1"), + GB_CONSTANT("_Group", "s", "View"), + + GB_EVENT("Title", NULL, NULL, &EVENT_TITLE), + GB_EVENT("Url", NULL, NULL, &EVENT_URL), + GB_EVENT("Icon", NULL, NULL, &EVENT_ICON), + GB_EVENT("Start", NULL, NULL, &EVENT_START), + GB_EVENT("Progress", NULL, NULL, &EVENT_PROGRESS), + GB_EVENT("Finish", NULL, NULL, &EVENT_FINISH), + GB_EVENT("Error", NULL, NULL, &EVENT_ERROR), + GB_EVENT("Link", NULL, NULL, &EVENT_LINK), + GB_EVENT("NewView", NULL, NULL, &EVENT_NEW_VIEW), + + GB_END_DECLARE +}; + +//------------------------------------------------------------------------- + +MyWebEngineView::MyWebEngineView(QWidget *parent) : QWebEngineView(parent) +{ + profile = new QWebEngineProfile(this); + //settings()->setFontFamily(QWebSettings::FixedFont, "monospace"); + //setPage(new MyWebPage(profile, this)); +} + +MyWebEngineView::~MyWebEngineView() +{ + delete page(); + delete profile; +} + +void MyWebEngineView::clearPage(bool destroy) +{ + QPointer p; + if (destroy) + p = page(); + + setPage(new MyWebPage(profile, this)); + QObject::connect(page(), SIGNAL(linkHovered(const QString &)), &WebViewSignalManager::manager, SLOT(linkHovered(const QString &))); + + if (destroy && p) + delete p; +} + +QWebEngineView *MyWebEngineView::createWindow(QWebEnginePage::WebWindowType type) +{ + void *_object = QT.GetObject(this); + QWebEngineView *new_view; + + if (GB.Raise(THIS, EVENT_NEW_VIEW, 0)) + return NULL; + + if (!THIS->new_view) + return NULL; + + new_view = (QWebEngineView *)(((CWEBVIEW *)THIS->new_view)->widget.widget); + GB.Unref(POINTER(&THIS->new_view)); + THIS->new_view = NULL; + return new_view; +} + +void MyWebEngineView::contextMenuEvent(QContextMenuEvent *event) +{ + void *_object = QT.GetObject(this); + + if (EVENT_MENU < 0) + EVENT_MENU = GB.GetEvent(GB.GetClass(THIS), "Menu"); + + if (!GB.CanRaise(THIS, EVENT_MENU)) + QWebEngineView::contextMenuEvent(event); + + event->accept(); +} + + +//------------------------------------------------------------------------- + +MyWebPage::MyWebPage(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent) +{ +} + +bool MyWebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) +{ + QWidget *wid = view(); + void *_object; + + if (!wid) + return true; + + _object = QT.GetObject(wid); + + //fprintf(stderr, "acceptNavigationRequest: cancel = %d / %s\n", THIS->cancel, TO_UTF8(url.toString())); + + if (THIS->cancel) + { + THIS->cancel = FALSE; + return false; + } + + return true; +} + +//------------------------------------------------------------------------- + +WebViewSignalManager WebViewSignalManager::manager; + +void WebViewSignalManager::iconChanged() +{ + GET_SENDER(); + GB.Unref(POINTER(&THIS->icon)); + THIS->icon = NULL; + GB.Raise(THIS, EVENT_ICON, 0); +} + +void WebViewSignalManager::titleChanged() +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_TITLE, 0); +} + +void WebViewSignalManager::urlChanged() +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_URL, 0); +} + +void WebViewSignalManager::linkHovered(const QString &link) +{ + void *_object = QT.GetObject(((QWebEnginePage*)sender())->view()); + + if (THIS) + { + set_link(THIS, link); + GB.Raise(THIS, EVENT_LINK, 0); + } +} + +void WebViewSignalManager::loadStarted() +{ + GET_SENDER(); + + //fprintf(stderr, "loadStarted\n"); + THIS->progress = 0; + THIS->cancel = GB.Raise(THIS, EVENT_START, 0); + if (!THIS->cancel) + GB.Raise(THIS, EVENT_PROGRESS, 0); +} + +void WebViewSignalManager::loadProgress(int progress) +{ + GET_SENDER(); + + if (THIS->cancel || THIS->progress == progress) + return; + + THIS->progress = progress; + GB.Raise(THIS, EVENT_PROGRESS, 0); + if (progress == 100) + GB.Raise(THIS, EVENT_FINISH, 0); +} + +void WebViewSignalManager::loadFinished(bool ok) +{ + GET_SENDER(); + + if (ok) + { + if (THIS->progress < 100) + { + THIS->progress = 100; + GB.Raise(THIS, EVENT_FINISH, 0); + } + } + else //if (!THIS->stopping) + GB.Raise(THIS, EVENT_ERROR, 0); + + GB.FreeString(&THIS->link); +} + + +#if 0 + +// void CWebView::linkClicked(const QUrl &url) +// { +// GET_SENDER(); +// WIDGET->page()->currentFrame()->setUrl(url); +// //WIDGET->setUrl(url); +// GB.Raise(THIS, EVENT_CLICK, 0); +// } + +void CWebView::selectionChanged() +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_SELECT, 0); +} + +void CWebView::statusBarMessage(const QString &text) +{ + GET_SENDER(); + GB.FreeString(&THIS->status); + THIS->status = NEW_STRING(text); + GB.Raise(THIS, EVENT_STATUS, 0); +} + +void CWebView::frameCreated(QWebFrame *frame) +{ + QObject::connect(frame, SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + GB.Raise(THIS, EVENT_NEW_FRAME, 1, GB_T_OBJECT, CWEBFRAME_get(frame)); +} + +void CWebView::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) +{ + //qDebug("CWebView::authenticationRequired: %p", _network_access_manager_view); + + void *_object = _network_access_manager_view; //QT.GetObject((QWidget *)((QNetworkAccessManager*)sender())->parent()); + if (!THIS) + return; + + THIS->reply = reply; + THIS->authenticator = authenticator; + + GB.Raise(THIS, EVENT_AUTH, 0); + + THIS->reply = 0; + THIS->authenticator = 0; +} + +void CWebView::downloadRequested(const QNetworkRequest &request) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + download = WEB_create_download(_network_access_manager->get(request)); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); +} + +void CWebView::handleUnsupportedContent(QNetworkReply *reply) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + if (reply->error() == QNetworkReply::NoError) + { + download = WEB_create_download(reply); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); + } + else + delete reply; +} +#endif diff --git a/gb.qt5/src/webview/c_webview.h b/gb.qt5/src/webview/c_webview.h new file mode 100644 index 00000000..3f112b7c --- /dev/null +++ b/gb.qt5/src/webview/c_webview.h @@ -0,0 +1,129 @@ +/*************************************************************************** + + c_webview.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WEBVIEW_H +#define __C_WEBVIEW_H + +#include +#include +#include +#include + +//#include "cwebdownload.h" +#include "main.h" + +#ifndef __C_WEBVIEW_CPP + +/*extern GB_DESC WebViewAuthDesc[]; +extern GB_DESC WebViewDownloadsDesc[];*/ +extern GB_DESC WebViewDesc[]; +extern GB_DESC WebViewCookiesDesc[]; +extern GB_DESC WebViewHistoryDesc[]; +extern GB_DESC WebViewHistoryItemDesc[]; + +#else + +#define THIS ((CWEBVIEW *)_object) +#define WIDGET ((MyWebEngineView *)((QT_WIDGET *)_object)->widget) + +#endif + +typedef + struct + { + QT_WIDGET widget; + QT_PICTURE icon; + void *new_view; + char *link; + int history; + int progress; + char *language; + unsigned cancel : 1; + } + CWEBVIEW; + +class MyWebPage : public QWebEnginePage +{ + Q_OBJECT + +public: + + MyWebPage(QWebEngineProfile *profile, QObject *parent); + +protected: + + virtual bool acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame); +}; + +class MyWebEngineView : public QWebEngineView +{ + Q_OBJECT + +public: + + MyWebEngineView(QWidget *parent); + ~MyWebEngineView(); + void clearPage(bool destroy); + +protected: + + virtual void contextMenuEvent(QContextMenuEvent *event); + virtual QWebEngineView *createWindow(QWebEnginePage::WebWindowType type); + + +private: + + QWebEngineProfile *profile; +}; + +class WebViewSignalManager : public QObject +{ + Q_OBJECT + +public: + + static WebViewSignalManager manager; + +public slots: + + void iconChanged(); + void titleChanged(); + void urlChanged(); + void linkHovered(const QString &link); + void loadStarted(); + void loadProgress(int progress); + void loadFinished(bool ok); + + /* + void selectionChanged(); + void statusBarMessage(const QString &text); + //void downloadRequested(const QNetworkRequest &); + void frameCreated(QWebFrame *); + void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + void downloadRequested(const QNetworkRequest &); + void handleUnsupportedContent(QNetworkReply*);*/ +}; + +//QNetworkAccessManager *WEBVIEW_get_network_manager(); + +#endif diff --git a/gb.qt5/src/webview/gb.qt5.webview.component b/gb.qt5/src/webview/gb.qt5.webview.component new file mode 100644 index 00000000..5360b31c --- /dev/null +++ b/gb.qt5/src/webview/gb.qt5.webview.component @@ -0,0 +1,5 @@ +[Component] +Author=Benoît Minisini +Require=gb.qt5 +State=Stable + diff --git a/gb.qt5/src/webview/main.cpp b/gb.qt5/src/webview/main.cpp new file mode 100644 index 00000000..d9545946 --- /dev/null +++ b/gb.qt5/src/webview/main.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** + + main.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include + +#include + +/*#include "ccookiejar.h" +#include "cwebhittest.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebdownload.h"*/ +#include "c_websettings.h" +#include "c_webview.h" +#include "main.h" + +GB_CLASS CLASS_WebView; + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + /*WebDownloadDesc, + WebDownloadsDesc, + WebHitTestDesc, + CookieDesc, + WebSettingsIconDatabaseDesc, + WebSettingsCacheDesc, + WebSettingsProxyDesc, + WebSettingsDesc, + WebElementStyleDesc, + WebElementDesc, + WebFrameChildrenDesc, + WebFrameDesc, + WebViewAuthDesc,*/ + WebSettingsFontsDesc, + WebSettingsDesc, + WebViewSettingsDesc, + WebViewHistoryItemDesc, + WebViewHistoryDesc, + WebViewCookiesDesc, + WebViewDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.qt5", QT_INTERFACE_VERSION, &QT); + CLASS_WebView = GB.FindClass("WebView"); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + +void MAIN_return_qvariant(const QVariant &result) +{ + GB_DATE date; + GB_DATE_SERIAL ds; + QDateTime qdate; + + switch (result.type()) + { + case QVariant::Bool: + GB.ReturnBoolean(result.toBool()); + break; + + case QVariant::Date: + case QVariant::DateTime: + qdate = result.toDateTime(); + ds.year = qdate.date().year(); + ds.month = qdate.date().month(); + ds.day = qdate.date().day(); + ds.hour = qdate.time().hour(); + ds.min = qdate.time().minute(); + ds.sec = qdate.time().second(); + ds.msec = qdate.time().msec(); + GB.MakeDate(&ds, &date); + GB.ReturnDate(&date); + break; + + case QVariant::Double: + GB.ReturnFloat(result.toDouble()); + break; + + case QVariant::Int: + case QVariant::UInt: + GB.ReturnInteger(result.toInt()); + break; + + case QVariant::LongLong: + case QVariant::ULongLong: + GB.ReturnLong(result.toLongLong()); + break; + + case QVariant::String: + RETURN_NEW_STRING(result.toString()); + break; + + // TODO: Handle these three datatypes + case QVariant::Hash: + case QVariant::List: + case QVariant::RegExp: + default: + GB.ReturnNull(); + break; + } + + GB.ReturnConvVariant(); +} + diff --git a/gb.qt5/src/webview/main.h b/gb.qt5/src/webview/main.h new file mode 100644 index 00000000..2de1c90a --- /dev/null +++ b/gb.qt5/src/webview/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.qt.h" + +#include + +#ifndef __MAIN_CPP +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +extern GB_CLASS CLASS_WebView; +#endif + +void MAIN_return_qvariant(const QVariant &result); + +#endif diff --git a/gb.qt5/src/x11/Makefile.am b/gb.qt5/src/x11/Makefile.am new file mode 100644 index 00000000..eb14a9c7 --- /dev/null +++ b/gb.qt5/src/x11/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.qt5.x11 +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.x11.la + +gb_qt5_x11_la_LIBADD = @QT5X11_LIB@ +gb_qt5_x11_la_LDFLAGS = -module @LD_FLAGS@ @QT5X11_LDFLAGS@ +gb_qt5_x11_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_x11_la_CPPFLAGS = @QT5X11_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt5_x11_la_SOURCES = \ + main.h main.cpp \ + x11.h x11.c diff --git a/gb.qt5/src/x11/gb.qt5.x11.component b/gb.qt5/src/x11/gb.qt5.x11.component new file mode 100644 index 00000000..58584e3c --- /dev/null +++ b/gb.qt5/src/x11/gb.qt5.x11.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.qt5 +Hidden=True + + diff --git a/gb.qt5/src/x11/main.cpp b/gb.qt5/src/x11/main.cpp new file mode 100644 index 00000000..5a431d75 --- /dev/null +++ b/gb.qt5/src/x11/main.cpp @@ -0,0 +1,441 @@ +/*************************************************************************** + + main.cpp + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include +#include +#include +#include + +#include +#include +#include "x11.h" + +static int _x11_last_key_code = 0; +static int (*_x11_event_filter)(XEvent *) = 0; +static QPointer _mouseGrabber = 0; +static QPointer _keyboardGrabber = 0; + +//------------------------------------------------------------------------- + +class MyNativeEventFilter: public QAbstractNativeEventFilter +{ +public: + + static MyNativeEventFilter manager; + + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) + { + xcb_generic_event_t *ev = static_cast(message); + int type = ev->response_type & ~0x80; + + switch(type) + { + case XCB_KEY_PRESS: + case XCB_KEY_RELEASE: + _x11_last_key_code = ((xcb_key_press_event_t *)ev)->detail; + break; + } + + if (_x11_event_filter) + { + XEvent xev; + + CLEAR(&xev); + xev.xany.type = type; + xev.xany.display = QX11Info::display(); + xev.xany.send_event = ev->response_type & 0x80 ? 1 : 0; + + switch (type) + { + //case XCB_KEY_PRESS: + //case XCB_KEY_RELEASE: + case XCB_EXPOSE: + { + xcb_expose_event_t *e = (xcb_expose_event_t *)ev; + xev.xexpose.window = e->window; + xev.xexpose.x = e->x; + xev.xexpose.y = e->y; + xev.xexpose.width = e->width; + xev.xexpose.height = e->height; + xev.xexpose.count = e->count; + break; + } + + case XCB_VISIBILITY_NOTIFY: + { + xcb_visibility_notify_event_t *e = (xcb_visibility_notify_event_t *)ev; + xev.xvisibility.window = e->window; + xev.xvisibility.state = e->state; + break; + } + + case XCB_DESTROY_NOTIFY: + { + xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *)ev; + xev.xdestroywindow.event = e->event; + xev.xdestroywindow.window = e->window; + break; + } + + case XCB_MAP_NOTIFY: + { + xcb_map_notify_event_t *e = (xcb_map_notify_event_t *)ev; + xev.xmap.event = e->event; + xev.xmap.window = e->window; + xev.xmap.override_redirect = e->override_redirect; + break; + } + + case XCB_UNMAP_NOTIFY: + { + xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *)ev; + xev.xunmap.event = e->event; + xev.xunmap.window = e->window; + xev.xunmap.from_configure = e->from_configure; + break; + } + + case XCB_REPARENT_NOTIFY: + { + xcb_reparent_notify_event_t *e = (xcb_reparent_notify_event_t *)ev; + xev.xreparent.event = e->event; + xev.xreparent.window = e->window; + xev.xreparent.parent = e->parent; + xev.xreparent.x = e->x; + xev.xreparent.y = e->y; + xev.xreparent.override_redirect = e->override_redirect; + break; + } + + case XCB_CONFIGURE_NOTIFY: + { + xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *)ev; + xev.xconfigure.event = e->event; + xev.xconfigure.window = e->window; + xev.xconfigure.x = e->x; + xev.xconfigure.y = e->y; + xev.xconfigure.width = e->width; + xev.xconfigure.height = e->height; + xev.xconfigure.border_width = e->border_width; + xev.xconfigure.override_redirect = e->override_redirect; + break; + } + + case XCB_PROPERTY_NOTIFY: + { + xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)ev; + xev.xproperty.window = e->window; + xev.xproperty.atom = e->atom; + xev.xproperty.time = e->time; + xev.xproperty.state = e->state; + break; + } + + case XCB_SELECTION_CLEAR: + { + xcb_selection_clear_event_t *e = (xcb_selection_clear_event_t *)ev; + xev.xselectionclear.window = e->owner; + xev.xselectionclear.selection = e->selection; + xev.xselectionclear.time = e->time; + break; + } + + case XCB_SELECTION_REQUEST: + { + xcb_selection_request_event_t *e = (xcb_selection_request_event_t *)ev; + xev.xselectionrequest.owner = e->owner; + xev.xselectionrequest.requestor = e->requestor; + xev.xselectionrequest.selection = e->selection; + xev.xselectionrequest.target = e->target; + xev.xselectionrequest.property = e->property; + xev.xselectionrequest.time = e->time; + break; + } + + case XCB_SELECTION_NOTIFY: + { + xcb_selection_notify_event_t *e = (xcb_selection_notify_event_t *)ev; + xev.xselection.requestor = e->requestor; + xev.xselection.selection = e->selection; + xev.xselection.target = e->target; + xev.xselection.property = e->property; + xev.xselection.time = e->time; + break; + } + + case XCB_CLIENT_MESSAGE: + { + xcb_client_message_event_t *e = (xcb_client_message_event_t *)ev; + xev.xclient.window = e->window; + xev.xclient.message_type = e->type; + xev.xclient.format = e->format; + xev.xclient.data.l[0] = e->data.data32[0]; + xev.xclient.data.l[1] = e->data.data32[1]; + xev.xclient.data.l[2] = e->data.data32[2]; + xev.xclient.data.l[3] = e->data.data32[3]; + xev.xclient.data.l[4] = e->data.data32[4]; + break; + } + + default: + qDebug("gb.qt5: warning: unhandled xcb event: %d", type); + return false; + } + + return (*_x11_event_filter)(&xev) != 0; + } + + return false; + } +}; + +MyNativeEventFilter MyNativeEventFilter::manager; + +//------------------------------------------------------------------------- + +static void platform_init(void) +{ + char *env; + + env = getenv("GB_X11_INIT_THREADS"); + if (env && atoi(env)) + XInitThreads(); + + X11_init(QX11Info::display(), QX11Info::appRootWindow()); + + qApp->installNativeEventFilter(&MyNativeEventFilter::manager); +} + +static void platform_exit(void) +{ + X11_exit(); +} + +//------------------------------------------------------------------------- + +static void release_grab() +{ + _mouseGrabber = QWidget::mouseGrabber(); + _keyboardGrabber = QWidget::keyboardGrabber(); + + if (_mouseGrabber) + { + //qDebug("releaseMouse"); + _mouseGrabber->releaseMouse(); + } + if (_keyboardGrabber) + { + //qDebug("releaseKeyboard"); + _keyboardGrabber->releaseKeyboard(); + } + + #ifndef NO_X_WINDOW + if (qApp->activePopupWidget()) + { + XUngrabPointer(QX11Info::display(), CurrentTime); + XFlush(QX11Info::display()); + } + #endif +} + +static void unrelease_grab() +{ + if (_mouseGrabber) + { + //qDebug("grabMouse"); + _mouseGrabber->grabMouse(); + _mouseGrabber = 0; + } + + if (_keyboardGrabber) + { + //qDebug("grabKeyboard"); + _keyboardGrabber->grabKeyboard(); + _keyboardGrabber = 0; + } +} + +static int get_last_key_code(void) +{ + return _x11_last_key_code; +} + +//------------------------------------------------------------------------- + +static int desktop_get_resolution_x(void) +{ + return QX11Info::appDpiX(); +} + +static int desktop_get_resolution_y(void) +{ + return QX11Info::appDpiY(); +} + +static void desktop_screenshot(QPixmap *pixmap, int x, int y, int w, int h) +{ + *pixmap = qApp->primaryScreen()->grabWindow(QX11Info::appRootWindow(), x, y, w, h); +} + +//------------------------------------------------------------------------- + +static int window_get_virtual_desktop(QWidget *window) +{ + return X11_window_get_desktop(window->winId()); +} + +static void window_set_virtual_desktop(QWidget *window, bool visible, int desktop) +{ + X11_window_set_desktop(window->winId(), window->isVisible(), desktop); +} + +static void window_remap(QWidget *window) +{ + X11_window_remap(window->effectiveWinId()); +} + +static void window_set_properties(QWidget *window, int which, QT_WINDOW_PROP *prop) +{ + X11_flush(); + + if (which & (PROP_STACKING | PROP_SKIP_TASKBAR)) + { + X11_window_change_begin(window->effectiveWinId(), window->isVisible()); + + if (which & PROP_STACKING) + { + X11_window_change_property(X11_atom_net_wm_state_above, prop->stacking == 1); + X11_window_change_property(X11_atom_net_wm_state_stays_on_top, prop->stacking == 1); + X11_window_change_property(X11_atom_net_wm_state_below, prop->stacking == 2); + } + if (which & PROP_SKIP_TASKBAR) + X11_window_change_property(X11_atom_net_wm_state_skip_taskbar, prop->skipTaskbar); + + X11_window_change_end(); + } + + if (which & PROP_BORDER) + X11_set_window_decorated(window->effectiveWinId(), prop->border); + + if (which & PROP_STICKY) + X11_window_set_desktop(window->effectiveWinId(), window->isVisible(), prop->sticky ? 0xFFFFFFFF : X11_get_current_desktop()); + + X11_flush(); +} + +static void window_set_user_time(QWidget *window, int timestamp) +{ + X11_window_set_user_time(window->effectiveWinId(), 0); +} + +static void window_set_transient_for(QWidget *window, QWidget *parent) +{ + X11_set_transient_for(window->effectiveWinId(), parent->effectiveWinId()); +} + +static void window_activate(QWidget *window) +{ + window->activateWindow(); +} + +//------------------------------------------------------------------------- + +static void x11_set_event_filter(int (*filter)(XEvent *)) +{ + _x11_event_filter = filter; +} + +//------------------------------------------------------------------------- + +extern "C" { + +const GB_INTERFACE *GB_PTR EXPORT; + +void *GB_QT5_X11_1[] EXPORT = { + + (void *)QT_PLATFORM_INTERFACE_VERSION, + + (void *)platform_init, + (void *)platform_exit, + + (void *)release_grab, + (void *)unrelease_grab, + (void *)get_last_key_code, + + (void *)desktop_get_resolution_x, + (void *)desktop_get_resolution_y, + (void *)desktop_screenshot, + + (void *)window_get_virtual_desktop, + (void *)window_set_virtual_desktop, + (void *)window_remap, + (void *)window_set_properties, + (void *)window_set_user_time, + (void *)window_set_transient_for, + (void *)window_activate, + + NULL + }; + + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)QX11Info::display(); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)QX11Info::appRootWindow(); + return TRUE; + } + else if (!strcasecmp(key, "SET_EVENT_FILTER")) + { + *value = (void *)x11_set_event_filter; + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)QX11Info::appTime(); + return TRUE; + } + else + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + diff --git a/gb.qt5/src/x11/main.h b/gb.qt5/src/x11/main.h new file mode 100644 index 00000000..1220c09e --- /dev/null +++ b/gb.qt5/src/x11/main.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" +//#include "../gb.qt.h" +#include "../gb.qt.platform.h" + +#ifndef __MAIN_C +extern "C" const GB_INTERFACE *GB_PTR; +#endif +#define GB (*GB_PTR) + +#endif diff --git a/gb.qt5/src/x11/x11.c b/gb.qt5/src/x11/x11.c new file mode 120000 index 00000000..68e60e76 --- /dev/null +++ b/gb.qt5/src/x11/x11.c @@ -0,0 +1 @@ +../../../gb.qt4/src/x11.c \ No newline at end of file diff --git a/gb.qt5/src/x11/x11.h b/gb.qt5/src/x11/x11.h new file mode 120000 index 00000000..966df48f --- /dev/null +++ b/gb.qt5/src/x11/x11.h @@ -0,0 +1 @@ +../../../gb.qt4/src/x11.h \ No newline at end of file diff --git a/gb.sdl.sound/AUTHORS b/gb.sdl.sound/AUTHORS new file mode 100644 index 00000000..81dc5cd6 --- /dev/null +++ b/gb.sdl.sound/AUTHORS @@ -0,0 +1,5 @@ +Codes/ideas/contributions (in alphabetic order): + +Laurent Carlier +Ahmad Kahmal +Benoît Minisini diff --git a/gb.sdl.sound/COPYING b/gb.sdl.sound/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.sdl.sound/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.sdl.sound/ChangeLog b/gb.sdl.sound/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl.sound/INSTALL b/gb.sdl.sound/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.sdl.sound/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.sdl.sound/Makefile.am b/gb.sdl.sound/Makefile.am new file mode 100644 index 00000000..4611a218 --- /dev/null +++ b/gb.sdl.sound/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SDLSOUND_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.sdl.sound/NEWS b/gb.sdl.sound/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl.sound/README b/gb.sdl.sound/README new file mode 100644 index 00000000..e2a7ea07 --- /dev/null +++ b/gb.sdl.sound/README @@ -0,0 +1,10 @@ +Welcome to the "SDL SOUND component for gambas !" +------------------------------------------------- + +Following versions are known to work + +Currently SDL component needs are : + - SDL 1.2.8 with cdrom support + - SDL mixer 1.2.6 + +Have fun :-) diff --git a/gb.sdl.sound/acinclude.m4 b/gb.sdl.sound/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.sdl.sound/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.sdl.sound/component.am b/gb.sdl.sound/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.sdl.sound/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.sdl.sound/configure.ac b/gb.sdl.sound/configure.ac new file mode 100644 index 00000000..e1fdcea1 --- /dev/null +++ b/gb.sdl.sound/configure.ac @@ -0,0 +1,24 @@ +dnl ---- configure.ac for gb.sdl + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-sdl-sound],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.sdl.sound) +LT_INIT + +dnl TODO: add a version test in GB_COMPONENT! +GB_PATH_SDL(1.2.8, [] , [touch DISABLED]) + +dnl check for SDL libs +GB_COMPONENT_PKG_CONFIG( + sdlsound, SDLSOUND, gb.sdl.sound, [src], + SDL_mixer) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.sdl.sound/gambas.h b/gb.sdl.sound/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.sdl.sound/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.sdl.sound/gb_common.h b/gb.sdl.sound/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.sdl.sound/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.sdl.sound/m4 b/gb.sdl.sound/m4 new file mode 120000 index 00000000..038bb10a --- /dev/null +++ b/gb.sdl.sound/m4 @@ -0,0 +1 @@ +../gb.sdl/m4 \ No newline at end of file diff --git a/gb.sdl.sound/reconf b/gb.sdl.sound/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.sdl.sound/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.sdl.sound/src/Makefile.am b/gb.sdl.sound/src/Makefile.am new file mode 100644 index 00000000..06250363 --- /dev/null +++ b/gb.sdl.sound/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.sdl.sound +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.sdl.sound.la + +gb_sdl_sound_la_LIBADD = @SDLSOUND_LIB@ @MATH_LIB@ +gb_sdl_sound_la_LDFLAGS = -module @LD_FLAGS@ @SDLSOUND_LDFLAGS@ +gb_sdl_sound_la_CPPFLAGS = @SDLSOUND_INC@ + +gb_sdl_sound_la_SOURCES = \ + main.h main.c \ + sound.h sound.c \ + cdrom.h cdrom.c diff --git a/gb.sdl.sound/src/cdrom.c b/gb.sdl.sound/src/cdrom.c new file mode 100644 index 00000000..63ff41d0 --- /dev/null +++ b/gb.sdl.sound/src/cdrom.c @@ -0,0 +1,491 @@ +/*************************************************************************** + + cdrom.c + + (c) 2004,2005 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDROM_C + +#include "config.h" + +#include "main.h" +#include "cdrom.h" + +/* CDROM volume control */ +#ifdef OS_LINUX +#include +#include +#include +#endif + +#ifdef OS_BSD /* Is this the good headers ? */ +#include +#include +#include +#endif + +#include +#include "SDL.h" + + +/*************************************************************************** + + CDROMS - detect available CDROMs + +***************************************************************************/ + +BEGIN_METHOD(CDROMS_get, GB_INTEGER index) + + int numdrives = SDL_CDNumDrives(); + + if (numdrives==0) + { + GB.Error("no CDROM found !"); + return; + } + + if (VARG(index)>numdrives) + { + GB.Error("CDROM &1 not available !", VARG(index)); + return; + } + + GB.ReturnConstZeroString(SDL_CDName(VARG(index)-1)); + +END_METHOD + +BEGIN_PROPERTY(CDROMS_count) + + GB.ReturnInteger(SDL_CDNumDrives()); + +END_METHOD + +/*************************************************************************** + + Track - managing a Track + +***************************************************************************/ + +BEGIN_METHOD(TRACK_play, GB_INTEGER start; GB_INTEGER length) + + int track = THIS->index; + int start = VARGOPT(start,0); + int length = VARGOPT(length,0); + + if (CDROM->track[track-1].type!=SDL_AUDIO_TRACK) + return; + + if ((track>CDROM->numtracks) || !track) + return; + + if (start>((CDROM->track[track-1].length)/CD_FPS)) + return; + + if ((start+length)>((CDROM->track[track-1].length)/CD_FPS)) + length = 0; + + if (length==0) + { + if (SDL_CDPlayTracks(CDROM, track-1, start*CD_FPS, 1, 0)==-1) + GB.Error(SDL_GetError()); + } + else + { + if (SDL_CDPlayTracks(CDROM, track-1, start*CD_FPS, 0, length*CD_FPS)==-1) + GB.Error(SDL_GetError()); + } + +END_METHOD + +BEGIN_PROPERTY(TRACK_length) + + if ((THIS->index>CDROM->numtracks) || (CDROM->track[(THIS->index)-1].type!=SDL_AUDIO_TRACK)) + GB.ReturnInteger(0); + else + GB.ReturnInteger((CDROM->track[(THIS->index)-1].length)/CD_FPS); + +END_PROPERTY + +BEGIN_PROPERTY(TRACK_position) + + if (CDROM->cur_track!=((THIS->index)-1)) + GB.ReturnInteger(0); + else + GB.ReturnInteger((CDROM->cur_frame)/CD_FPS); + +END_PROPERTY + +BEGIN_PROPERTY(TRACK_playable) + + if (THIS->index>CDROM->numtracks) + GB.ReturnBoolean(0); + + if (CDROM->track[(THIS->index)-1].type==SDL_AUDIO_TRACK) + GB.ReturnBoolean(1); + else + GB.ReturnBoolean(0); + +END_PROPERTY + +/*************************************************************************** + + Tracks - managing the Tracks + +***************************************************************************/ + +BEGIN_METHOD(TRACKS_get, GB_INTEGER index) + + CDstatus status = SDL_CDStatus(CDROM); + long index = VARG(index); + + if (!CD_INDRIVE(status)) + { + GB.Error("CDROM not available !"); + return; + } + + THIS->index = index; + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(TRACKS_count) + + SDL_CDStatus(CDROM); + GB.ReturnInteger(CDROM->numtracks); + +END_PROPERTY + +BEGIN_PROPERTY(TRACKS_current) + + CDstatus status = SDL_CDStatus(CDROM); + + if (!CD_INDRIVE(status)) + GB.ReturnInteger(0); + else + GB.ReturnInteger(CDROM->cur_track+1); + +END_PROPERTY + +/*************************************************************************** + + CDrom - use a detected CDROM + +***************************************************************************/ + +BEGIN_METHOD(CDROM_new, GB_INTEGER index) + + int numdrives = SDL_CDNumDrives(); + int index = VARGOPT(index, 0); + + if (numdrives==0) + { + GB.Error("no CDROM found !"); + return; + } + + if (!MISSING(index)) + { + CDROM = SDL_CDOpen(index); + THIS->id = index; + + if (!CDROM) + GB.Error(SDL_GetError()); + + return; + } + else + { + CDROM = SDL_CDOpen(0); + THIS->id = 0; + + if (!CDROM) + GB.Error(SDL_GetError()); + + return; + } + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_free) + + SDL_CDStop(CDROM); + SDL_CDClose(CDROM); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_eject) + + if (SDL_CDEject(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_stop) + + if (SDL_CDStop(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_pause) + + if (SDL_CDPause(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_resume) + + if (SDL_CDResume(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD(CDROM_play, GB_INTEGER start; GB_INTEGER tracks) + + CDstatus status = SDL_CDStatus(CDROM); + int start = VARGOPT(start,1); + int tracks = VARGOPT(tracks,0); + + if (status == CD_ERROR) + GB.Error(SDL_GetError()); + + if (!CD_INDRIVE(status)) + return; + + if (start>CDROM->numtracks) + return; + + if ((start+(tracks-1))>CDROM->numtracks) + tracks = (CDROM->numtracks)-start; + + if (SDL_CDPlayTracks(CDROM, start-1, 0, tracks, 0)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_PROPERTY(CDROM_ready) + + CDstatus status = SDL_CDStatus(CDROM); + + GB.ReturnBoolean(CD_INDRIVE(status)); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_paused) + + CDstatus status = SDL_CDStatus(CDROM); + + if (status == CD_ERROR) + GB.Error(SDL_GetError()); + + GB.ReturnBoolean(status == CD_PAUSED); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_playing) + + CDstatus status = SDL_CDStatus(CDROM); + + GB.ReturnBoolean(status == CD_PLAYING); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_stopped) + + CDstatus status = SDL_CDStatus(CDROM); + + GB.ReturnBoolean(status == CD_STOPPED); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_length) + + CDstatus status = SDL_CDStatus(CDROM); + int i, length = 0; + + if (!CD_INDRIVE(status)) + { + GB.ReturnInteger(0); + } + else + { + for (i=0;i<((CDROM->numtracks)-1);i++) + { + if (CDROM->track[i].type==SDL_AUDIO_TRACK) + length = length + (CDROM->track[i].length); + } + + GB.ReturnInteger(length/CD_FPS); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_position) + + CDstatus status = SDL_CDStatus(CDROM); + int i, length =0; + + if (!CD_INDRIVE(status)) + GB.ReturnInteger(0); + else + { + for (i=0;i<(CDROM->cur_track);i++) + { + if (CDROM->track[i].type==SDL_AUDIO_TRACK) + length = length + (CDROM->track[i].length); + } + + GB.ReturnInteger(((CDROM->cur_frame)+length)/CD_FPS); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_volume) + +#ifdef OS_LINUX + + struct cdrom_volctrl vol; + + if (READ_PROPERTY) + { + ioctl(CDROM->id, CDROMVOLREAD, (void *) &vol); + GB.ReturnInteger(vol.channel0); + } + else + { + int volume = VPROP(GB_INTEGER); + + if (volume<0) + volume = 0; + + if (volume>255) + volume = 255; + + vol.channel0 = volume; + vol.channel1 = volume; + ioctl(CDROM->id, CDROMVOLCTRL,(void *) &vol); + } + +#endif + +#ifdef OS_BSD + + struct ioc_vol vol; + + if (READ_PROPERTY) + { + ioctl(CDROM->id, CDIOCGETVOL, &vol); + GB.ReturnInteger(vol.vol[0]); + } + else + { + int volume = VPROP(GB_INTEGER); + + if (volume <0) + volume = 0; + if (volume>255) + volume = 255; + + vol.vol[0] = volume; + vol.vol[1] = volume; + vol.vol[2] = 0; + vol.vol[3] = 0; + ioctl(CDROM->id, CDIOCSETVOL, &vol); + } + +#endif + +#if !defined(OS_LINUX) && !defined(OS_BSD) + + if (!READ_PROPERTY) + GB.ReturnInteger(0); + +#endif + +END_PROPERTY + +/**************************************************************************/ + +GB_DESC Cquerycdrom[] = +{ + GB_DECLARE("CDRoms", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", "s", CDROMS_get, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", CDROMS_count), + + GB_END_DECLARE +}; + +GB_DESC Ctrack[] = +{ + GB_DECLARE(".Track",0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Play", NULL, TRACK_play, "[(Start)i(Length)i]"), + + GB_PROPERTY_READ("Length", "i", TRACK_length), + GB_PROPERTY_READ("Position", "i", TRACK_position), + GB_PROPERTY_READ("Playable", "b", TRACK_playable), + + GB_END_DECLARE +}; + +GB_DESC Ctracks[] = +{ + GB_DECLARE(".Tracks",0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Track", TRACKS_get, "(Index)i"), + + GB_PROPERTY_READ("Count", "i", TRACKS_count), + GB_PROPERTY_READ("Current", "i", TRACKS_current), + + GB_END_DECLARE +}; + +GB_DESC Ccdrom[] = +{ + GB_DECLARE("CDRom", sizeof(CCDROM)), + + GB_METHOD("_new", NULL, CDROM_new, "[(Index)i]"), + GB_METHOD("_free", NULL, CDROM_free, NULL), + + /* cdrom commands */ + GB_METHOD("Eject", NULL, CDROM_eject, NULL), + GB_METHOD("Stop", NULL, CDROM_stop, NULL), + GB_METHOD("Pause", NULL, CDROM_pause, NULL), + GB_METHOD("Resume", NULL, CDROM_resume, NULL), + GB_METHOD("Play", NULL, CDROM_play, "[(StartTrack)i(NbrTracks)i]"), + + /* cdrom status */ + GB_PROPERTY_READ("Ready", "b", CDROM_ready), + GB_PROPERTY_READ("Paused", "b", CDROM_paused), + GB_PROPERTY_READ("Playing", "b", CDROM_playing), + GB_PROPERTY_READ("Stopped", "b", CDROM_stopped), + GB_PROPERTY_READ("Length", "i", CDROM_length), + GB_PROPERTY_READ("Position", "i", CDROM_position), + + GB_PROPERTY("Volume", "i", CDROM_volume), + + /* Track management */ + GB_PROPERTY_SELF("Tracks", ".Tracks"), + + GB_END_DECLARE +}; diff --git a/gb.sdl.sound/src/cdrom.h b/gb.sdl.sound/src/cdrom.h new file mode 100644 index 00000000..0751a3aa --- /dev/null +++ b/gb.sdl.sound/src/cdrom.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + cdrom.h + + (c) 2004,2005 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDROM_H +#define __CDROM_H + +#include "gambas.h" + +#include "SDL.h" + +typedef + struct { + GB_BASE ob; + SDL_CD *cdrom; + int id; + long index; + } + CCDROM; + +#ifndef __CDROM_C +extern GB_DESC Cquerycdrom[]; +extern GB_DESC Ctrack[]; +extern GB_DESC Ctracks[]; +extern GB_DESC Ccdrom[]; +#else + +#define CDROM ((CCDROM *)_object)->cdrom +#define THIS ((CCDROM *)_object) + +#endif /* __CDROM_C */ + +#endif /* __CDROM_H */ diff --git a/gb.sdl.sound/src/gb.sdl.sound.component b/gb.sdl.sound/src/gb.sdl.sound.component new file mode 100644 index 00000000..13426a30 --- /dev/null +++ b/gb.sdl.sound/src/gb.sdl.sound.component @@ -0,0 +1,9 @@ +[Component] +Key=gb.sdl.sound +Name=Sound library based on SDL +Name[fr]=Bibliothèque sonore basée sur SDL +Name[pl]=Biblioteka dźwięku bazująca na SDL +Name[es]=Biblioteca de sonido basada en SDL +Name[tr]=SDL tabanlı ses kitaplığı +Author=Benoît Minisini,Ahmad Kahmal,Laurent Carlier +State=Deprecated diff --git a/gb.sdl.sound/src/main.c b/gb.sdl.sound/src/main.c new file mode 100644 index 00000000..2f6ade06 --- /dev/null +++ b/gb.sdl.sound/src/main.c @@ -0,0 +1,97 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "sound.h" +#include "cdrom.h" +#include "main.h" + +#include "SDL.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CSoundDesc, + CMusicDesc, + CChannelsDesc, + CChannelDesc, + //CMusic, + Cquerycdrom, + Ctracks, Ctrack, + Ccdrom, + + NULL +}; + + +int EXPORT GB_INIT(void) +{ + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if video is defined, sdl was init by gb.sdl component ! + if (sysInit & SDL_INIT_VIDEO) + { + if (SDL_InitSubSystem(SDL_INIT_AUDIO | SDL_INIT_CDROM)<0) + { + GB.Error(SDL_GetError()); + return 0; + } + } + else + { + if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_CDROM | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE)<0) + { + GB.Error(SDL_GetError()); + return 0; + } + } + + SOUND_init(); + + return -1; +} + + +void EXPORT GB_EXIT() +{ + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + SOUND_exit(); + + // if video is defined, gb.sdl component still not closed ! + if (sysInit & SDL_INIT_VIDEO) + SDL_QuitSubSystem(SDL_INIT_AUDIO | SDL_INIT_CDROM); + else + SDL_Quit(); + +} + + + + diff --git a/gb.sdl.sound/src/main.h b/gb.sdl.sound/src/main.h new file mode 100644 index 00000000..f7ea835e --- /dev/null +++ b/gb.sdl.sound/src/main.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.sdl.sound/src/sound.c b/gb.sdl.sound/src/sound.c new file mode 100644 index 00000000..d378fb08 --- /dev/null +++ b/gb.sdl.sound/src/sound.c @@ -0,0 +1,604 @@ +/*************************************************************************** + + sound.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __SOUND_C + +#include + +#include "gb_common.h" + +#include "sound.h" +#include "main.h" + +static SOUND_INFO info = { 0 }; + +static CCHANNEL *channel_cache[MAX_CHANNEL] = { 0 }; +static int channel_count; + +static double music_ref_time = 0; +static double music_ref_pos = 0; + +static int _ch_pipe[2]; +static int _ch_playing = 0; +static int _init = 0; + +#if 0 +static void musicDone() +{ +/* This is the function that we told SDL_Mixer to call when the music + was finished. In our case, we're going to simply unload the music + as though the player wanted it stopped. In other applications, a + different music file might be loaded and played. */ + + /* BM: You cannot raise an event from a static class at the moment. */ + + printf("The music stopped playing NOW\n"); //This should raise a gambas event! +} +#endif + +static void set_audio_properties() +{ + /* We're going to be requesting certain things from our audio + device, so we set them up beforehand */ + + //Of course this should all come from gambas properties! + info.rate = 44100; //could be: 22050; + info.format = MIX_DEFAULT_FORMAT; //16-bit stereo + info.channels = 2; //The only one that opens on my machine! SB_PCI128 + // BM: This is stereo. It does not matter. + info.buffers = 4096; + +} + +static void free_channel(CCHANNEL *ch) +{ + if (!ch->sound) + return; + + GB.Unref(POINTER(&ch->sound)); + ch->sound = NULL; + ch->free = FALSE; + + _ch_playing--; + if (_ch_playing == 0) + GB.Watch(_ch_pipe[0], GB_WATCH_NONE, (void *)0, 0); +} + +static void free_finished_channels(void) +{ + int i; + CCHANNEL *ch; + char foo; + + if (read(_ch_pipe[0], &foo, 1) != 1) + return; + + for (i = 0; i < MAX_CHANNEL; i++) + { + ch = channel_cache[i]; + if (ch && ch->free) + free_channel(ch); + } +} + +static void channel_finished(int channel) +{ + CCHANNEL *ch = channel_cache[channel]; + char foo = 0; + + if (!ch) + return; + + /*printf("channel_finished: %p\n", ch->sound);*/ + + // TODO: do not use GB.Post(), because we are not in the main thread. Write to a pipe and watch it in the main thread + ch->free = write(_ch_pipe[1], &foo, 1) == 1; +} + +static int play_channel(int channel, CSOUND *sound, int loops, int fadein) +{ + _ch_playing++; + if (_ch_playing == 1) + GB.Watch(_ch_pipe[0], GB_WATCH_READ , (void *)free_finished_channels,0); + + if (fadein > 0) + return Mix_FadeInChannel(channel, sound->chunk, loops, fadein); + else + return Mix_PlayChannel(channel, sound->chunk, loops); +} + +static bool start_sound_engine() +{ + /* This is where we open up our audio device. Mix_OpenAudio takes + as its parameters the audio format we'd /like/ to have. */ + if(Mix_OpenAudio(info.rate, info.format, info.channels, info.buffers)) + { + GB.Error("Unable to open audio"); + return TRUE; + } + + if (pipe(_ch_pipe)) + { + GB.Error("Unable to initialize channel pipe"); + return TRUE; + } + + Mix_QuerySpec(&info.rate, &info.format, &info.channels); + //fprintf(stderr, "Mix_QuerySpec: %d %d %d\n", info.rate, info.format, info.channels); + + channel_count = Mix_AllocateChannels(-1); + + Mix_ChannelFinished(channel_finished); + + return FALSE; +} + + +static void free_music(void) +{ + if (!info.music) + return; + + Mix_HaltMusic(); + Mix_RewindMusic(); + Mix_FreeMusic(info.music); + info.music = NULL; +} + + +static void stop_sound_engine() +{ + if (_ch_playing) + GB.Watch(_ch_pipe[0], GB_WATCH_NONE, (void *)0, 0); + close(_ch_pipe[0]); + close(_ch_pipe[1]); + free_music(); + Mix_CloseAudio(); +} + +void SOUND_init(void) +{ + _init++; + if (_init > 1) + return; + + set_audio_properties(); //Fill audio structures with gambas properties + start_sound_engine(); //Start the sound engine +} + + +void SOUND_exit(void) +{ + _init--; + if (_init > 0) + return; + + stop_sound_engine(); +} + + +static void return_channel(int channel, CSOUND *sound) +{ + CCHANNEL *ch = NULL; + + if (channel < 0 || channel >= channel_count) + { + if (sound) + GB.Unref(POINTER(&sound)); + + GB.ReturnNull(); + return; + } + + ch = channel_cache[channel]; + if (!ch) + { + ch = GB.New(GB.FindClass("Channel"), NULL, NULL); + channel_cache[channel] = ch; + ch->channel = channel; + GB.Ref(ch); + } + + free_channel(ch); + if (sound) + ch->sound = sound; + + GB.ReturnObject(ch); +} + + +static double volume_from_sdl(int vol) +{ + return log(1 + (M_E - 1) * (double)vol / MIX_MAX_VOLUME); +} + +static int volume_to_sdl(double vol) +{ + return (exp(vol) - 1) / (M_E - 1) * MIX_MAX_VOLUME; +} + +/*************************************************************************** + + Sound + +***************************************************************************/ + +#define THIS ((CSOUND *)_object) + + +BEGIN_METHOD(CSOUND_new, GB_STRING file) + + char *addr; + int len; + + if (GB.LoadFile(STRING(file), LENGTH(file), &addr, &len)) + return; + + THIS->chunk = Mix_LoadWAV_RW(SDL_RWFromMem(addr, len), TRUE); + + GB.ReleaseFile(addr, len); + + if (!THIS->chunk) + GB.Error(Mix_GetError()); + +END_METHOD + + +BEGIN_METHOD_VOID(CSOUND_free) + + Mix_FreeChunk(THIS->chunk); + THIS->chunk = NULL; + +END_METHOD + + +BEGIN_METHOD(CSOUND_play, GB_INTEGER loops; GB_FLOAT fadein) + + int loops = VARGOPT(loops, 0); + int channel; + + GB.Ref(THIS); + channel = play_channel(-1, THIS, loops, MISSING(fadein) ? 0 : (int)(VARG(fadein) * 1000)); + return_channel(channel, THIS); + +END_METHOD + + +GB_DESC CSoundDesc[] = +{ + GB_DECLARE("Sound", sizeof(CSOUND)), + + //GB_STATIC_METHOD("_init", NULL, CSOUND_init, NULL), + //GB_STATIC_METHOD("_exit", NULL, CSOUND_exit, NULL), + + GB_METHOD("_new", NULL, CSOUND_new, "(File)s"), + GB_METHOD("_free", NULL, CSOUND_free, NULL), + + //GB_PROPERTY("Volume", "e", CSOUND_volume), + + GB_METHOD("Play", "Channel", CSOUND_play, "[(Loops)i(FadeIn)f]"), + + GB_END_DECLARE +}; + +/*************************************************************************** + + Channel + +***************************************************************************/ + +#undef THIS +#define THIS ((CCHANNEL *)_object) + + +BEGIN_METHOD(CCHANNEL_get, GB_INTEGER index) + + return_channel(VARG(index), NULL); + +END_METHOD + + +BEGIN_PROPERTY(CCHANNEL_count) + + int nchan; + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_AllocateChannels(-1)); + else + { + nchan = VPROP(GB_INTEGER); + if (nchan < 0) + nchan = 0; + else if (nchan >= MAX_CHANNEL) + nchan = MAX_CHANNEL; + + Mix_AllocateChannels(nchan); + channel_count = Mix_AllocateChannels(-1); + } + +END_PROPERTY + + +BEGIN_METHOD(CCHANNEL_play, GB_OBJECT sound; GB_INTEGER loops; GB_FLOAT fadein) + + CSOUND *sound; + + if (Mix_Paused(THIS->channel)) + Mix_Resume(THIS->channel); + + sound = VARGOPT(sound, NULL); + if (!sound) + return; + + /*printf("Ref %p\n", sound);*/ + GB.Ref(sound); + THIS->sound = sound; + play_channel(THIS->channel, sound, VARGOPT(loops, 0), MISSING(fadein) ? 0 : (int)(VARG(fadein) * 1000)); + +END_METHOD + + +BEGIN_METHOD_VOID(CCHANNEL_pause) + + Mix_Pause(THIS->channel); + +END_METHOD + + +BEGIN_METHOD(CCHANNEL_stop, GB_FLOAT fadeout) + + if (MISSING(fadeout)) + Mix_HaltChannel(THIS->channel); + else + Mix_FadeOutChannel(THIS->channel, (int)(VARG(fadeout) * 1000)); + +END_METHOD + + +BEGIN_METHOD_VOID(CCHANNEL_exit) + + int i; + CCHANNEL *ch; + + for (i = 0; i < MAX_CHANNEL; i++) + { + ch = channel_cache[i]; + if (!ch) + continue; + + free_channel(ch); + GB.Unref(POINTER(&ch)); + } + +END_METHOD + + +BEGIN_PROPERTY(CCHANNEL_volume) + + int channel; + + channel = THIS ? THIS->channel : -1; + + if (READ_PROPERTY) + GB.ReturnFloat(volume_from_sdl(Mix_Volume(channel, -1))); + else + Mix_Volume(channel, volume_to_sdl(VPROP(GB_FLOAT))); + +END_PROPERTY + + + +GB_DESC CChannelDesc[] = +{ + GB_DECLARE("Channel", sizeof(CCHANNEL)), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_exit", NULL, CCHANNEL_exit, NULL), + //GB_STATIC_PROPERTY("Volume", "e", CCHANNEL_volume), + GB_METHOD("Play", NULL, CCHANNEL_play, "[(Sound)Sound;(Loops)i(FadeIn)f]"), + GB_METHOD("Pause", NULL, CCHANNEL_pause, NULL), + GB_METHOD("Stop", NULL, CCHANNEL_stop, "[(FadeOut)f]"), + + GB_PROPERTY("Volume", "f", CCHANNEL_volume), + + GB_END_DECLARE +}; + +GB_DESC CChannelsDesc[] = +{ + GB_DECLARE("Channels", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_get", "Channel", CCHANNEL_get, "(Index)i"), + //GB_STATIC_METHOD("_next", "Channel", CCHANNEL_next, NULL), + + GB_STATIC_PROPERTY("Count", "i", CCHANNEL_count), + GB_STATIC_PROPERTY("Volume", "f", CCHANNEL_volume), + + GB_END_DECLARE +}; + +/*************************************************************************** + + Music + +***************************************************************************/ + +static double get_music_pos(void) +{ + double time; + + if (Mix_PlayingMusic()) + { + if (!Mix_PausedMusic()) + { + GB.GetTime(&time, FALSE); + return music_ref_pos + time - music_ref_time; + } + else + return music_ref_pos; + } + else + return 0; +} + + +BEGIN_METHOD(CMUSIC_load, GB_STRING file) + + free_music(); + + /* Note that the music cannot be stored inside the project ! */ + + info.music = Mix_LoadMUS(GB.RealFileName(STRING(file), LENGTH(file))); + if (!info.music) + GB.Error(Mix_GetError()); + + music_ref_pos = 0; + music_ref_time = 0; + +END_METHOD + + +BEGIN_METHOD(CMUSIC_play, GB_INTEGER loops; GB_FLOAT fadein) + + double fadevalue=0; + + if (!info.music) + return; + + GB.GetTime(&music_ref_time, FALSE); + + if (Mix_PausedMusic()) + { + Mix_ResumeMusic(); + return; + } + + /* We want to know when our music has stopped playing so we + can free it up and set 'music' back to NULL. SDL_Mixer + provides us with a callback routine we can use to do + exactly that */ + + /*Mix_HookMusicFinished(musicDone);*/ + + //The 'Looping' param should be optional in gambas, default=0. Don't know how? + //BM Now do you know ? ;-) + + fadevalue = VARGOPT(fadein, 0) * 1000; + // if fadevalue is too small -> music doesn't want to play ! + if (fadevalue<100) + { + fadevalue=0; + } + + Mix_FadeInMusic(info.music, VARGOPT(loops, 1), fadevalue); + +END_METHOD + + +BEGIN_METHOD_VOID(CMUSIC_pause) + + music_ref_pos = get_music_pos(); + Mix_PauseMusic(); + +END_METHOD + + +BEGIN_METHOD(CMUSIC_stop, GB_FLOAT fadeout) + + if (MISSING(fadeout)) + Mix_HaltMusic(); + else + Mix_FadeOutMusic(VARG(fadeout)*1000); + + music_ref_pos = 0; + +END_METHOD + +BEGIN_PROPERTY(CMUSIC_pos) + + double pos; + + if (READ_PROPERTY) + { + GB.ReturnFloat(get_music_pos()); + } + else + { + pos = VPROP(GB_FLOAT); + Mix_RewindMusic(); + if (Mix_SetMusicPosition(pos) == 0) + music_ref_pos = pos; + else + music_ref_pos = 0; + GB.GetTime(&music_ref_time, FALSE); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CMUSIC_volume) + + if (READ_PROPERTY) + GB.ReturnFloat(volume_from_sdl(Mix_VolumeMusic(-1))); + else + Mix_VolumeMusic(volume_to_sdl(VPROP(GB_FLOAT))); + +END_PROPERTY + + +BEGIN_PROPERTY(Music_State) + + if (Mix_PlayingMusic()) + { + if (Mix_PausedMusic()) + GB.ReturnInteger(2); + else + GB.ReturnInteger(1); + } + else + GB.ReturnInteger(0); + +END_PROPERTY + +GB_DESC CMusicDesc[] = +{ + GB_DECLARE("Music", 0), + + //GB_STATIC_METHOD("_init", NULL, CSOUND_init, NULL), + //GB_STATIC_METHOD("_exit", NULL, CSOUND_exit, NULL), + + GB_STATIC_METHOD("Load", NULL, CMUSIC_load, "(File)s"), + GB_STATIC_METHOD("Play", NULL, CMUSIC_play, "[(Loops)i(FadeIn)f]"), + GB_STATIC_METHOD("Pause", NULL, CMUSIC_pause, NULL), + GB_STATIC_METHOD("Stop", NULL, CMUSIC_stop, "[(FadeOut)f]"), + + GB_STATIC_PROPERTY("Volume", "f", CMUSIC_volume), + GB_STATIC_PROPERTY("Pos", "f", CMUSIC_pos), + + GB_STATIC_PROPERTY_READ("State", "i", Music_State), + + GB_CONSTANT("Stopped", "i", 0), + GB_CONSTANT("Playing", "i", 1), + GB_CONSTANT("Paused", "i", 2), + + GB_END_DECLARE +}; diff --git a/gb.sdl.sound/src/sound.h b/gb.sdl.sound/src/sound.h new file mode 100644 index 00000000..6f642eb9 --- /dev/null +++ b/gb.sdl.sound/src/sound.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + sound.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SOUND_H +#define __SOUND_H + +#include "gambas.h" + +#include "SDL.h" +#include "SDL_mixer.h" + +#ifndef __SOUND_C +extern GB_DESC CSoundDesc[]; +extern GB_DESC CMusicDesc[]; +extern GB_DESC CChannelDesc[]; +extern GB_DESC CChannelsDesc[]; +#else + +typedef + struct { + Mix_Music *music; + int rate; + Uint16 format; + int channels; + int buffers; + } + SOUND_INFO; + +#endif + +#define MAX_CHANNEL 64 + +typedef + struct { + GB_BASE ob; + Mix_Chunk *chunk; + } + CSOUND; + +typedef + struct { + GB_BASE ob; + int channel; + CSOUND *sound; + char free; + } + CCHANNEL; + +void SOUND_init(void); +void SOUND_exit(void); + +#endif /* __SOUND_H */ diff --git a/gb.sdl/AUTHORS b/gb.sdl/AUTHORS new file mode 100644 index 00000000..27dce17c --- /dev/null +++ b/gb.sdl/AUTHORS @@ -0,0 +1,6 @@ +Codes/ideas/contributions (in alphabetic order): + +Fabien Bodard +Daniel Campos FernànDez +Laurent Carlier +Benoît Minisini diff --git a/gb.sdl/COPYING b/gb.sdl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.sdl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.sdl/ChangeLog b/gb.sdl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl/INSTALL b/gb.sdl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.sdl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.sdl/Makefile.am b/gb.sdl/Makefile.am new file mode 100644 index 00000000..29bfebd9 --- /dev/null +++ b/gb.sdl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SDL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.sdl/NEWS b/gb.sdl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl/README b/gb.sdl/README new file mode 100644 index 00000000..9070dfba --- /dev/null +++ b/gb.sdl/README @@ -0,0 +1,12 @@ +Welcome to the "SDL component for gambas !" +------------------------------------------- + +Following versions are known to work + +Currently SDL component needs are : + - SDL 1.2.8 + - SDL image 1.2.4 + - OpenGL support + - Xcursor support (provide with Xfree 4.x/xorg) + +Have fun :-) diff --git a/gb.sdl/acinclude.m4 b/gb.sdl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.sdl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.sdl/component.am b/gb.sdl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.sdl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.sdl/configure.ac b/gb.sdl/configure.ac new file mode 100644 index 00000000..4c3208b7 --- /dev/null +++ b/gb.sdl/configure.ac @@ -0,0 +1,49 @@ +dnl ---- configure.ac for gb.sdl + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-sdl],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.sdl) +LT_INIT + +GB_CHECK_XWINDOW + +dnl TODO: add a version test in GB_COMPONENT! +dnl GB_PATH_SDL([1.2.8], [], [touch DISABLED]) + +dnl check Xcursor support +dnl AC_CHECK_LIB(Xcursor, XcursorLibraryLoadCursor, +dnl [echo -n +dnl LDFLAGS="$LDFLAGS -lXcursor"], +dnl [AC_MSG_WARN([libXcursor not found. Check 'config.log' for more details.]) +dnl touch DISABLED], +dnl $X_LIBS) + +dnl check for SDL libs +dnl GB_COMPONENT( +dnl sdl, +dnl SDL, +dnl [SDL], +dnl [src], +dnl [GB_FIND(SDL_opengl.h SDL.h SDL_ttf.h, `sdl-config --prefix`, include/SDL)], +dnl [GB_FIND(libSDL_ttf.$SHLIBEXT libGLEW.$SHLIBEXT, /usr /usr/X11R6 /usr/local `sdl-config --prefix`, lib)], +dnl [$X_LIBS -lSDL_ttf -lGLEW -lXcursor], +dnl [$SDL_CFLAGS]) + +GB_COMPONENT_SEARCH( + sdl, SDL, gb.sdl, [src], + 'sdl > 1.2.8' SDL_ttf glew xcursor x11, + [GB_FIND(GL/glew.h SDL_ttf.h X11/Xcursor/Xcursor.h, /usr /usr/local `sdl-config --prefix`, include include/SDL)], + [GB_FIND(libSDL_ttf.$SHLIBEXT libGLEW.$SHLIBEXT libXcursor.$SHLIBEXT, /usr /usr/local `sdl-config --prefix`, lib)], + [$X_LIBS -lSDL_ttf -lGLEW -lXcursor], + [$SDL_CFLAGS] +) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.sdl/gambas.h b/gb.sdl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.sdl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.sdl/gb.image.h b/gb.sdl/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.sdl/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.sdl/gb_common.h b/gb.sdl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.sdl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.sdl/m4 b/gb.sdl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.sdl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.sdl/reconf b/gb.sdl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.sdl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.sdl/src/Cconst.cpp b/gb.sdl/src/Cconst.cpp new file mode 100644 index 00000000..b38f5637 --- /dev/null +++ b/gb.sdl/src/Cconst.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + + Cconst.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONST_CPP + +#include "gambas.h" + +#include "main.h" +#include "Cconst.h" +#include "SDL_h.h" + +GB_DESC CLine[] = +{ + GB_DECLARE("Line", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SDL::NoLine), + GB_CONSTANT("Solid", "i", SDL::SolidLine), + GB_CONSTANT("Dash", "i", SDL::DashLine), + GB_CONSTANT("Dot", "i", SDL::DotLine), + GB_CONSTANT("DashDot", "i", SDL::DashDotLine), + GB_CONSTANT("DashDotDot", "i", SDL::DashDotDotLine), + + GB_END_DECLARE +}; + +GB_DESC CFill[] = +{ + GB_DECLARE("Fill", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SDL::NoFill), + GB_CONSTANT("Solid", "i", SDL::SolidFill), + GB_CONSTANT("Dense94", "i", SDL::Dense1Fill), + GB_CONSTANT("Dense88", "i", SDL::Dense2Fill), + GB_CONSTANT("Dense63", "i", SDL::Dense3Fill), + GB_CONSTANT("Dense50", "i", SDL::Dense4Fill), + GB_CONSTANT("Dense37", "i", SDL::Dense5Fill), + GB_CONSTANT("Dense12", "i", SDL::Dense6Fill), + GB_CONSTANT("Dense6", "i", SDL::Dense7Fill), + + GB_CONSTANT("Horizontal", "i", SDL::HorizontalFill), + GB_CONSTANT("Vertical", "i", SDL::VerticalFill), + GB_CONSTANT("Cross", "i", SDL::CrossFill), + GB_CONSTANT("BackDiagonal", "i", SDL::BackDiagFill), + GB_CONSTANT("Diagonal", "i", SDL::DiagFill), + GB_CONSTANT("CrossDiagonal", "i", SDL::DiagCrossFill), + + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cconst.h b/gb.sdl/src/Cconst.h new file mode 100644 index 00000000..7f7d9e26 --- /dev/null +++ b/gb.sdl/src/Cconst.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + Cconst.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONST_H +#define __CCONST_H + +#include "gambas.h" + +#ifndef __CCONST_CPP +extern GB_DESC CLine[]; +extern GB_DESC CFill[]; +#endif + +#endif diff --git a/gb.sdl/src/Cdesktop.cpp b/gb.sdl/src/Cdesktop.cpp new file mode 100644 index 00000000..72b9172c --- /dev/null +++ b/gb.sdl/src/Cdesktop.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + + Cdesktop.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDESKTOP_CPP + +#include "Cdesktop.h" +#include "SDLapp.h" + +BEGIN_PROPERTY(CDESKTOP_width) + + GB.ReturnInteger(SDLapp->DesktopWidth()); + +END_PROPERTY + +BEGIN_PROPERTY(CDESKTOP_height) + + GB.ReturnInteger(SDLapp->DesktopHeight()); + +END_PROPERTY + +GB_DESC CDesktop[] = +{ + GB_DECLARE("Desktop", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("W", "i", CDESKTOP_width), + GB_STATIC_PROPERTY_READ("H", "i", CDESKTOP_height), + GB_STATIC_PROPERTY_READ("Width", "i", CDESKTOP_width), + GB_STATIC_PROPERTY_READ("Height", "i", CDESKTOP_height), + + GB_END_DECLARE +}; diff --git a/gb.sdl/src/Cdesktop.h b/gb.sdl/src/Cdesktop.h new file mode 100644 index 00000000..f518967f --- /dev/null +++ b/gb.sdl/src/Cdesktop.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + Cdesktop.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDESKTOP_H +#define __CDESKTOP_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CDESKTOP_CPP +extern GB_DESC CDesktop[]; +#endif + +#endif diff --git a/gb.sdl/src/Cdraw.cpp b/gb.sdl/src/Cdraw.cpp new file mode 100644 index 00000000..8d457869 --- /dev/null +++ b/gb.sdl/src/Cdraw.cpp @@ -0,0 +1,367 @@ +/*************************************************************************** + + Cdraw.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_CPP + +#include "main.h" +#include "Cdraw.h" +#include "Cwindow.h" +#include "Cimage.h" + +#define DRAW_STACK_MAX 8 + +static CDRAW draw_stack[DRAW_STACK_MAX]; +static CDRAW *draw_current = 0; +static CFONT *_default_font = NULL; + +#define THIS (draw_current) +#define GFX (THIS->graphic) +#define FONT (THIS->font) +#define WINDOWID(object) ((CWINDOW *)object)->id +#define IMAGEID(object) (CIMAGE_get((CIMAGE *)object)) + +/**************************************************************************/ + +static CFONT *get_default_font() +{ + if (!_default_font) + { + _default_font = (CFONT *)GB.New(GB.FindClass("Font"), NULL, NULL); + GB.Ref(_default_font); + } + + return _default_font; +} + +static bool check_graphic(void) +{ + if (THIS == NULL) + { + GB.Error("No device"); + return true; + } + else + return false; +} + +#define CHECK_GRAPHIC() if (check_graphic()) return + +void DRAW_begin(void *device) +{ + if (THIS >= &draw_stack[DRAW_STACK_MAX - 1]) { + GB.Error("Too many nested drawings"); + return; + } + + if (GB.CheckObject(device)) + return; + + if (THIS == 0) + THIS = draw_stack; + else + THIS++; + + THIS->backcolor = 0x00000000; + THIS->forecolor = 0x00FFFFFF; + + if (FONT) + GB.Unref(POINTER(&FONT)); + + FONT = get_default_font(); + GB.Ref(FONT); + + if (GB.Is(device, CLASS_Window)) { + THIS->device = device; + THIS->graphic = new SDLgfx(WINDOWID(device)); + GB.Ref(THIS->device); + return; + } +#if 0 + if (GB.Is(device, CLASS_Image)) + { + THIS->device = device; + THIS->graphic = new SDLgfx(IMAGEID(device)); + GB.Ref(THIS->device); + return; + } +#endif + GB.Error("Device not supported !"); +} + + void DRAW_end() +{ + if (!THIS) + return; + + delete THIS->graphic; + GB.Unref(POINTER(&THIS->device)); + THIS->device = 0; + + if (FONT) + GB.Unref(POINTER(&FONT)); + + FONT = 0; + + if (THIS == draw_stack) + THIS = 0; + else + THIS--; +} + +BEGIN_METHOD_VOID(Draw_exit) + + if (_default_font) + GB.Unref(POINTER(&_default_font)); + +END_METHOD + +BEGIN_METHOD(CDRAW_begin, GB_OBJECT device) + + void *device = VARG(device); + DRAW_begin(device); + +END_METHOD + +BEGIN_METHOD_VOID(CDRAW_end) + + DRAW_end(); + +END_METHOD + +BEGIN_METHOD(CDRAW_rotate, GB_FLOAT angle) + + CHECK_GRAPHIC(); + + GFX->Rotate(VARG(angle)); + +END_METHOD + +BEGIN_METHOD(CDRAW_scale, GB_FLOAT x; GB_FLOAT y) + + CHECK_GRAPHIC(); + + GFX->Scale(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(CDRAW_point, GB_INTEGER x; GB_INTEGER y) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawPixel(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(CDRAW_line, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawLine(VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(CDRAW_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawRect(VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD(CDRAW_ellipse, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawEllipse(VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD(CDRAW_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + int scale; + + CHECK_GRAPHIC(); + + if (GB.CheckObject(THIS->font)) + return; + + SDLsurface *txt = FONT->font->RenderText(STRING(text), LENGTH(text)); + if (!txt) + return; + +/* if (THIS->backcolor != 0) + { + int fill = GFX->GetFillStyle(); + GFX->SetFillStyle(SDL::SolidFill); + GFX->SetColor(THIS->backcolor); + GFX->DrawRect(VARG(x), VARG(y), txt->GetWidth(), txt->GetHeight()); + GFX->SetFillStyle(fill); + }*/ + + GFX->SetColor(THIS->forecolor); + + scale = FONT->font->GetScale(); + + if (scale == 1) + GFX->Blit(txt, VARG(x), VARG(y)); + else + GFX->Blit(txt, VARG(x), VARG(y), 0, 0, -1, -1, txt->GetWidth() * scale, txt->GetHeight() * scale, true); + + txt->Unref(); + +END_METHOD + +BEGIN_METHOD(CDRAW_image, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; + GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CHECK_GRAPHIC(); + + CIMAGE *image = (CIMAGE *) VARG(image); + + if (!image) + return; + + GFX->SetColor(THIS->forecolor); + GFX->Blit(IMAGEID(image), VARG(x), VARG(y), VARGOPT(srcx,0), VARGOPT(srcy,0), + VARGOPT(srcw,-1), VARGOPT(srch,-1), VARGOPT(width,-1), VARGOPT(height,-1)); + +END_METHOD + +BEGIN_PROPERTY(CDRAW_defaultfont) + + CHECK_GRAPHIC(); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_font) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnObject(FONT); + else + { + if (FONT) + GB.Unref(POINTER(&FONT)); + + FONT = (CFONT *) VPROP(GB_OBJECT); + GB.Ref(FONT); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_linestyle) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(GFX->GetLineStyle()); + else + GFX->SetLineStyle(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_linewidth) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(GFX->GetLineWidth()); + else + GFX->SetLineWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_fillstyle) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(GFX->GetFillStyle()); + else + GFX->SetFillStyle(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_background) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->backcolor); + else + THIS->backcolor = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_foreground) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->forecolor); + else + THIS->forecolor = VPROP(GB_INTEGER); + +END_PROPERTY + +/**************************************************************************/ + +GB_DESC CDraw[] = +{ + GB_DECLARE("Draw", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_exit", NULL, Draw_exit, NULL), + + GB_STATIC_METHOD("Begin", NULL, CDRAW_begin, "(Device)o"), + GB_STATIC_METHOD("End", NULL, CDRAW_end, NULL), + + GB_STATIC_METHOD("Rotate", NULL, CDRAW_rotate, "(Angle)f"), + GB_STATIC_METHOD("Scale", NULL, CDRAW_scale, "(X)f(Y)f"), + + GB_STATIC_METHOD("Point", NULL, CDRAW_point, "(X)i(Y)i"), + GB_STATIC_METHOD("Line", NULL, CDRAW_line, "(X1)i(Y1)i(X2)i(Y2)i"), + GB_STATIC_METHOD("Rect", NULL, CDRAW_rect, "(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("Ellipse", NULL, CDRAW_ellipse, "(X)i(Y)i(Width)i(Height)i"), + + GB_STATIC_METHOD("Text", NULL, CDRAW_text, "(text)s(X)i(Y)i"), + GB_STATIC_METHOD("Image", NULL, CDRAW_image, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + +// GB_STATIC_PROPERTY("DefaultFont", "Font", CDRAW_defaultfont), + GB_STATIC_PROPERTY("Font", "Font", CDRAW_font), + + GB_STATIC_PROPERTY("LineStyle", "i", CDRAW_linestyle), + GB_STATIC_PROPERTY("LineWidth", "i", CDRAW_linewidth), + GB_STATIC_PROPERTY("FillStyle", "i", CDRAW_fillstyle), + GB_STATIC_PROPERTY("Background", "i", CDRAW_background), + GB_STATIC_PROPERTY("BackColor", "i", CDRAW_background), + GB_STATIC_PROPERTY("Foreground", "i", CDRAW_foreground), + GB_STATIC_PROPERTY("ForeColor", "i", CDRAW_foreground), + + GB_END_DECLARE +}; diff --git a/gb.sdl/src/Cdraw.h b/gb.sdl/src/Cdraw.h new file mode 100644 index 00000000..da5c1cae --- /dev/null +++ b/gb.sdl/src/Cdraw.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + Cdraw.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "main.h" +#include "SDLgfx.h" +#include "Cfont.h" + +typedef + struct { + void *device; + SDLgfx *graphic; + CFONT *font; + Uint32 forecolor; + Uint32 backcolor; + } + CDRAW; + +#ifndef __CDRAW_CPP +extern GB_DESC CDraw[]; +#endif /* __CDRAW_CPP */ + +void DRAW_begin(void *device); +void DRAW_end(void ); + +#endif /* __CDRAW_H */ + diff --git a/gb.sdl/src/Cfont.cpp b/gb.sdl/src/Cfont.cpp new file mode 100644 index 00000000..60e5cb58 --- /dev/null +++ b/gb.sdl/src/Cfont.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + + Cfont.cpp + + (c) 2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFONT_CPP + +#include "Cfont.h" +#include "Cimage.h" +#if 0 +static StringList FontList; + +static void init_font_list(void ) +{ + if (FontList.empty()) + FontList = SDLfont::GetFontList(); +} + +BEGIN_METHOD_VOID(CFONTS_next) + + std::string s; + + uint *index = (uint *)GB.GetEnum(); + + if (*index == 0) + init_font_list(); + + if (*index >= FontList.size()) + GB.StopEnum(); + else + { + s = FontList[*index]; + GB.ReturnNewZeroString(s.c_str()); + (*index)++; + } + +END_METHOD + +BEGIN_PROPERTY(CFONTS_count) + + init_font_list(); + GB.ReturnInteger(FontList.size()); + +END_PROPERTY +#endif +BEGIN_METHOD(CFONT_load, GB_STRING path) + + CFONT *font; + font = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + font->font = new SDLfont(GB.RealFileName(STRING(path), LENGTH(path))); + GB.ReturnObject(font); + +END_METHOD + +BEGIN_METHOD(CFONT_width, GB_STRING text) + + int width, height; + FONT->SizeText(STRING(text), LENGTH(text), &width, &height); + GB.ReturnInteger(width); + +END_METHOD + +BEGIN_METHOD(CFONT_height, GB_STRING text) + + int width, height; + FONT->SizeText(STRING(text), LENGTH(text), &width, &height); + GB.ReturnInteger(height); + +END_METHOD + +BEGIN_METHOD(CFONT_image, GB_STRING text) + + CIMAGE *img; + + SDLsurface *txt = FONT->RenderText(STRING(text), LENGTH(text)); + if (!txt) + { + GB.ReturnNull(); + return; + } + + img = CIMAGE_create(txt); + + GB.ReturnObject(img); + +END_METHOD + +BEGIN_METHOD_VOID(CFONT_new) + + THIS->font = new SDLfont(); + +END_METHOD + +BEGIN_METHOD_VOID(CFONT_free) + + if (FONT) + delete (FONT); + +END_METHOD + +BEGIN_PROPERTY(CFONT_name) + + GB.ReturnNewZeroString(FONT->GetFontName()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_size) + + if (READ_PROPERTY) + GB.ReturnInteger(FONT->GetFontSize()); + else + FONT->SetFontSize(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_bold) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontBold()); + else + FONT->SetFontBold(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_italic) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontItalic()); + else + FONT->SetFontItalic(VPROP(GB_BOOLEAN)); + +END_PROPERTY +/* +BEGIN_PROPERTY(CFONT_strikeout) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontStrikeout()); + else + FONT->SetFontStrikeout(VPROP(GB_BOOLEAN)); + +END_PROPERTY +*/ +BEGIN_PROPERTY(CFONT_underline) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontUnderline()); + else + FONT->SetFontUnderline(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_ascent) + + GB.ReturnInteger(FONT->GetFontAscent()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_descent) + + GB.ReturnInteger(FONT->GetFontDescent()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_fixed) + + GB.ReturnBoolean(FONT->IsFontFixed()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_scalable) + + GB.ReturnBoolean(FONT->IsFontScalable()); + +END_PROPERTY + +BEGIN_PROPERTY(Font_DefaultFontSize) + + GB.ReturnInteger(FONT->GetDefaultFontSize()); + +END_PROPERTY + +#if 0 +GB_DESC CFonts[] = +{ + GB_DECLARE("Fonts", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_next", "s", CFONTS_next, NULL), + GB_STATIC_PROPERTY_READ("Count", "i", CFONTS_count), + + GB_END_DECLARE +}; +#endif +GB_DESC CFont[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), + + GB_STATIC_METHOD("Load", "Font", CFONT_load, "(Path)s"), + + GB_METHOD("_new", NULL, CFONT_new, NULL), + GB_METHOD("_free", NULL, CFONT_free, NULL), + + GB_PROPERTY("Size", "i", CFONT_size), + GB_PROPERTY("Bold", "b", CFONT_bold), + GB_PROPERTY("Italic", "b", CFONT_italic), +// GB_PROPERTY("StrikeOut", "b", CFONT_strikeout), + GB_PROPERTY("Underline", "b", CFONT_underline), + + GB_PROPERTY_READ("Name", "s", CFONT_name), + GB_PROPERTY_READ("Ascent", "i", CFONT_ascent), + GB_PROPERTY_READ("Descent", "i", CFONT_descent), + GB_PROPERTY_READ("Fixed", "b", CFONT_fixed), + GB_PROPERTY_READ("Scalable", "b", CFONT_scalable), + + GB_METHOD("Width", "i", CFONT_width, "(Text)s"), + GB_METHOD("Height", "i", CFONT_height, "(Text)s"), + GB_METHOD("Image", "Image", CFONT_image, "(Text)s"), + + GB_STATIC_PROPERTY_READ("DefaultFontSize", "i", Font_DefaultFontSize), +/* + GB_PROPERTY("Grade", "i", CFONT_grade), +*/ + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cfont.h b/gb.sdl/src/Cfont.h new file mode 100644 index 00000000..7fb05038 --- /dev/null +++ b/gb.sdl/src/Cfont.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + Cfont.h + + (c) 2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFONT_H +#define __CFONT_H + +#include "SDLfont.h" + +#include "main.h" + +#ifndef __CFONT_CPP +extern GB_DESC CFont[]; +//extern GB_DESC CFonts[]; +#else + +#define THIS ((CFONT *)_object) +#define FONT ((CFONT *)_object)->font +#endif /* __CFONT_CPP */ + +typedef + struct { + GB_BASE ob; + SDLfont *font; + } + CFONT; + +#endif /* __CFONT_H */ diff --git a/gb.sdl/src/Cimage.cpp b/gb.sdl/src/Cimage.cpp new file mode 100644 index 00000000..4387ec92 --- /dev/null +++ b/gb.sdl/src/Cimage.cpp @@ -0,0 +1,160 @@ +/*************************************************************************** + + Cimage.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include "Cimage.h" +#include "SDLtexture.h" + +#include +#include + +static void free_image(GB_IMG *img, void *image) +{ + ((SDLsurface *)image)->Unref(); +} + +static void check_modified(GB_IMG *img) +{ + // Vérifie le flag modified + if (img->modified) + { + // Synchronize l'image image->texture + + // Réinitialise le flag modified + img->modified = false; + } +} + +static void *temp_image(GB_IMG *img) +{ + SDLsurface *image; + + if (!img->data) + image = new SDLsurface(); + else + { + // Pas besoin de faire de synchro image->texture, vu qu'on crée une nouvelle surface ? + image = new SDLsurface((char *)img->data, img->width, img->height); + } + + image->SetAlphaBuffer(true); + return image; +} + +static void sync_image(GB_IMG *image) +{ + // Synchronize l'image texture->image + + // Puis mets le flag de synchro à false + image->sync = false; +} + +static GB_IMG_OWNER _image_owner = { + "gb.sdl", + DEFAULT_IMAGE_FORMAT, + free_image, + free_image, + temp_image, + sync_image, + }; + +SDLsurface *CIMAGE_get(CIMAGE *_object) +{ + GB_IMG *img = THIS_IMAGE; + + // Si ce n'est pas nécessaire de le faire systématiquement chaque fois qu'on a besoin de l'image, + // alors ne pas le faire ici, mais explicitement où c'est vraiment nécessaire. + check_modified(img); + return (SDLsurface *)IMAGE.Check(img, &_image_owner); +} + +#define check_image CIMAGE_get + +static void take_image(CIMAGE *_object, SDLsurface *image) +{ + IMAGE.Take(THIS_IMAGE, &_image_owner, image, image->width(), image->height(), image->data()); +} + +CIMAGE *CIMAGE_create(SDLsurface *image) +{ + CIMAGE *img; + + img = (CIMAGE *)GB.New(CLASS_Image, NULL, NULL); + + if (image) + { + (image->GetTexture())->Sync(); + take_image(img, image); + } + else + take_image(img, new SDLsurface()); + + return img; +} + +CIMAGE *CIMAGE_create_from_window(SDLwindow *window, int x, int y, int w, int h) +{ + GB_IMG *img; + uchar *line, *top, *bottom; + uint size; + + if (w < 0) + w = window->GetWidth(); + + if (h < 0) + h = window->GetHeight(); + + if (w <= 0 || h <= 0) + return NULL; + + img = IMAGE.Create(w, h, GB_IMAGE_RGBA, NULL); + glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img->data); + + size = img->width * sizeof(uint); + GB.Alloc(POINTER(&line), size); + top = img->data; + bottom = img->data + img->height * size; + + for (y = 0; y < img->height / 2; y++) + { + bottom -= size; + memcpy(line, top, size); + memcpy(top, bottom, size); + memcpy(bottom, line, size); + top += size; + } + + GB.Free(POINTER(&line)); + + return (CIMAGE *)img; +} + +/***************************************************************************/ + +GB_DESC CImage[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_END_DECLARE +}; diff --git a/gb.sdl/src/Cimage.h b/gb.sdl/src/Cimage.h new file mode 100644 index 00000000..023c1603 --- /dev/null +++ b/gb.sdl/src/Cimage.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + Cimage.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "main.h" +#include "SDLsurface.h" +#include "SDLwindow.h" + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_ARGB +#else +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_BGRA +#endif + + +typedef + struct { + GB_IMG img; + } + CIMAGE; + +#ifndef __CIMAGE_CPP +extern GB_DESC CImage[]; +#else + +#define THIS ((CIMAGE *)_object) +#define THIS_IMAGE (&THIS->img) +#define IMAGEID ((SDLsurface *)GB_IMG_HANDLE(&THIS->img)) + +#endif /* __CIMAGE_CPP */ + +SDLsurface *CIMAGE_get(CIMAGE *); +CIMAGE *CIMAGE_create(SDLsurface *); +CIMAGE *CIMAGE_create_from_window(SDLwindow *window, int x, int y, int w, int h); + +#endif /* __CIMAGE_H */ diff --git a/gb.sdl/src/Cjoystick.cpp b/gb.sdl/src/Cjoystick.cpp new file mode 100644 index 00000000..a2e076b6 --- /dev/null +++ b/gb.sdl/src/Cjoystick.cpp @@ -0,0 +1,296 @@ +/*************************************************************************** + + Cjoystick.cpp + + (c) 2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CJOYSTICK_CPP + +#include "Cjoystick.h" + +#include +#include + +// store joysticks infos +typedef + struct { + Uint8 Axes; + Uint8 Balls; + Uint8 Buttons; + Uint8 Hats; + std::string Name; + } + JOY_info; + +static std::map joyinfos; +static std::map joyobjects; +static int joyindex = 0; + +CJOY_INFO CJOY_info = { 0 }; + +#define CHECK_VALID() \ + if (!CJOY_info.valid) \ + { \ + GB.Error("No joystick event data"); \ + return; \ + } + +static void filljoyinfos() +{ + + int numjoy = SDL_NumJoysticks(); + JOY_info myinfo; + + if (!numjoy) + return; + + for (int i=0; i=numjoy || index<0) + { + GB.Error("Joystick &1 not available !", VARG(index)); + return; + } + + joyindex = index; + + if (!joyinfos.size()) + filljoyinfos(); + + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(JOYSTICKS_count) + + GB.ReturnInteger(SDL_NumJoysticks()); + +END_METHOD + +BEGIN_PROPERTY(JOYSTICK_device) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.device); + +END_METHOD + +BEGIN_PROPERTY(JOYSTICK_id) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.id); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_axisvalue) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value1); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_hatvalue) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value1); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_ballx) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value1); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_bally) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value2); + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC CJoyInfos[] = +{ + GB_DECLARE(".Joystick", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Enable", "b", JOYINFOS_enable), + + GB_STATIC_PROPERTY_READ("Axes", "i", JOYINFOS_numofaxes), + GB_STATIC_PROPERTY_READ("Balls", "i", JOYINFOS_numofballs), + GB_STATIC_PROPERTY_READ("Buttons", "i", JOYINFOS_numofbuts), + GB_STATIC_PROPERTY_READ("Hats", "i", JOYINFOS_numofhats), + GB_STATIC_PROPERTY_READ("Name", "s", JOYINFOS_name), + + GB_END_DECLARE +}; + +GB_DESC CQueryJoys[] = +{ + GB_DECLARE("Joysticks", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", ".Joystick", JOYSTICKS_get, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", JOYSTICKS_count), + + GB_END_DECLARE +}; + +GB_DESC CJoystick[] = +{ + GB_DECLARE("Joystick", 0), GB_VIRTUAL_CLASS(), + +// TODO close all opened joysticks on exit ? +// GB_STATIC_METHOD("_exit", NULL, JOYSTICK_exit, NULL), + + GB_STATIC_PROPERTY_READ("Device", "i", JOYSTICK_device), + GB_STATIC_PROPERTY_READ("Id", "i", JOYSTICK_id), + GB_STATIC_PROPERTY_READ("Axis", "i", JOYSTICK_axisvalue), + GB_STATIC_PROPERTY_READ("Hat", "i", JOYSTICK_hatvalue), + GB_STATIC_PROPERTY_READ("BallX", "i", JOYSTICK_ballx), + GB_STATIC_PROPERTY_READ("BallY", "i", JOYSTICK_bally), + + GB_CONSTANT("LeftUp", "i", SDL_HAT_LEFTUP), + GB_CONSTANT("Left", "i", SDL_HAT_LEFT), + GB_CONSTANT("LeftDown", "i", SDL_HAT_LEFTDOWN), + GB_CONSTANT("Up", "i", SDL_HAT_UP), + GB_CONSTANT("Centered", "i", SDL_HAT_CENTERED), + GB_CONSTANT("Down", "i", SDL_HAT_DOWN), + GB_CONSTANT("RightUp", "i", SDL_HAT_RIGHTUP), + GB_CONSTANT("Right", "i", SDL_HAT_RIGHT), + GB_CONSTANT("RightDown", "i", SDL_HAT_RIGHTDOWN), + + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cjoystick.h b/gb.sdl/src/Cjoystick.h new file mode 100644 index 00000000..29efee38 --- /dev/null +++ b/gb.sdl/src/Cjoystick.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + Cjoystick.h + + (c) 2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CJOYSTICK_H +#define __CJOYSTICK_H + +#include "main.h" +#include "SDL.h" + +typedef + struct { + bool valid; + Uint8 device; /* Joystick that raised the event */ + int id; /* Can be axis, ball, hat, button id */ + Sint16 value1; /* Can be value for axis, hat; xrel for ball */ + Sint16 value2; /* yrel value for ball */ + } + CJOY_INFO; + +#ifndef __CJOYSTICK_CPP +extern GB_DESC CJoyInfos[]; +extern GB_DESC CQueryJoys[]; +extern GB_DESC CJoystick[]; +extern CJOY_INFO CJOY_info; +#else + +#define JOYSTICK ((CJOYSTICK *)_object)->joy +#define THIS ((CJOYSTICK *)_object) + +#endif /* __CJOYSTICK_CPP */ +#endif /* __CJOYSTCIK_H */ + diff --git a/gb.sdl/src/Ckey.cpp b/gb.sdl/src/Ckey.cpp new file mode 100644 index 00000000..e73de8a9 --- /dev/null +++ b/gb.sdl/src/Ckey.cpp @@ -0,0 +1,235 @@ +/*************************************************************************** + + Ckey.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CKEY_CPP + +#include "gambas.h" +#include "main.h" + +#include "Ckey.h" + +#include "SDL.h" + +CKEY_INFO CKEY_info = { 0 }; + +static bool _key_repeat = false; + +#define CHECK_VALID() \ + if (CKEY_info.valid <= 0) \ + { \ + GB.Error("No keyboard event data"); \ + return; \ + } + +/***************************************************************************/ + +BEGIN_METHOD(CKEY_get, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + int code = 0; + + if (key[0] && !key[1] && !(key[0] & 0x80)) + { + GB.ReturnInteger(key[0]); + return; + } + else + { + for (code = 1; code <= 255; code++) + { + if (!strcasecmp(SDL_GetKeyName((SDLKey)code), key)) + { + GB.ReturnInteger(code); + return; + } + } + } + + GB.ReturnInteger(0); + +END_METHOD + +BEGIN_PROPERTY(CKEY_code) + + CHECK_VALID(); + GB.ReturnInteger(CKEY_info.code); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_state) + + CHECK_VALID(); + GB.ReturnInteger(CKEY_info.state); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_shift) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_SHIFT); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_control) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_CTRL); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_alt) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_ALT); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_meta) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_META); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_normal) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state != 0); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_text) + + CHECK_VALID(); + GB.ReturnNewZeroString(CKEY_info.text); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Repeat) + + if (READ_PROPERTY) + GB.ReturnBoolean(_key_repeat); + else + { + _key_repeat = VPROP(GB_BOOLEAN); + SDL_EnableKeyRepeat(_key_repeat ? SDL_DEFAULT_REPEAT_DELAY : 0, SDL_DEFAULT_REPEAT_INTERVAL); + } + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC CKey[] = +{ + GB_DECLARE("Key", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Repeat", "b", Key_Repeat), + + GB_STATIC_METHOD("_get", "i", CKEY_get, "(Key)s"), + + GB_STATIC_PROPERTY_READ("Code", "i", CKEY_code), + GB_STATIC_PROPERTY_READ("State", "i", CKEY_state), + GB_STATIC_PROPERTY_READ("Shift", "b", CKEY_shift), + GB_STATIC_PROPERTY_READ("Control", "b", CKEY_control), + GB_STATIC_PROPERTY_READ("Alt", "b", CKEY_alt), + GB_STATIC_PROPERTY_READ("Meta", "b", CKEY_meta), + GB_STATIC_PROPERTY_READ("Normal", "b", CKEY_normal), + GB_STATIC_PROPERTY_READ("Text", "s", CKEY_text), + + GB_CONSTANT("Backspace", "i", SDLK_BACKSPACE), + GB_CONSTANT("Tab", "i", SDLK_TAB), + //GB_CONSTANT("Clear", "i", SDLK_CLEAR = 12, + GB_CONSTANT("Return", "i", SDLK_RETURN), + GB_CONSTANT("Pause", "i", SDLK_PAUSE), + GB_CONSTANT("Escape", "i", SDLK_ESCAPE), + GB_CONSTANT("Esc", "i", SDLK_ESCAPE), + GB_CONSTANT("Space", "i", SDLK_SPACE), + GB_CONSTANT("Delete", "i", SDLK_DELETE), + GB_CONSTANT("KP0", "i", SDLK_KP0), + GB_CONSTANT("KP1", "i", SDLK_KP1), + GB_CONSTANT("KP2", "i", SDLK_KP2), + GB_CONSTANT("KP3", "i", SDLK_KP3), + GB_CONSTANT("KP4", "i", SDLK_KP4), + GB_CONSTANT("KP5", "i", SDLK_KP5), + GB_CONSTANT("KP6", "i", SDLK_KP6), + GB_CONSTANT("KP7", "i", SDLK_KP7), + GB_CONSTANT("KP8", "i", SDLK_KP8), + GB_CONSTANT("KP9", "i", SDLK_KP9), + GB_CONSTANT("KPPeriod", "i", SDLK_KP_PERIOD), + GB_CONSTANT("KPDivide", "i", SDLK_KP_DIVIDE), + GB_CONSTANT("KPMultiply", "i", SDLK_KP_MULTIPLY), + GB_CONSTANT("KPMinus", "i", SDLK_KP_MINUS), + GB_CONSTANT("KPPlus", "i", SDLK_KP_PLUS), + GB_CONSTANT("KPEnter", "i", SDLK_KP_ENTER), + GB_CONSTANT("KPEquals", "i", SDLK_KP_EQUALS), + GB_CONSTANT("Up", "i", SDLK_UP), + GB_CONSTANT("Down", "i", SDLK_DOWN), + GB_CONSTANT("Right", "i", SDLK_RIGHT), + GB_CONSTANT("Left", "i", SDLK_LEFT), + GB_CONSTANT("Insert", "i", SDLK_INSERT), + GB_CONSTANT("Home", "i", SDLK_HOME), + GB_CONSTANT("End", "i", SDLK_END), + GB_CONSTANT("PageUp", "i", SDLK_PAGEUP), + GB_CONSTANT("PageDown", "i", SDLK_PAGEDOWN), + GB_CONSTANT("F1", "i", SDLK_F1), + GB_CONSTANT("F2", "i", SDLK_F2), + GB_CONSTANT("F3", "i", SDLK_F3), + GB_CONSTANT("F4", "i", SDLK_F4), + GB_CONSTANT("F5", "i", SDLK_F5), + GB_CONSTANT("F6", "i", SDLK_F6), + GB_CONSTANT("F7", "i", SDLK_F7), + GB_CONSTANT("F8", "i", SDLK_F8), + GB_CONSTANT("F9", "i", SDLK_F9), + GB_CONSTANT("F10", "i", SDLK_F10), + GB_CONSTANT("F11", "i", SDLK_F11), + GB_CONSTANT("F12", "i", SDLK_F12), + GB_CONSTANT("F13", "i", SDLK_F13), + GB_CONSTANT("F14", "i", SDLK_F14), + GB_CONSTANT("F15", "i", SDLK_F15), + GB_CONSTANT("NumLock", "i", SDLK_NUMLOCK), + GB_CONSTANT("CapsLock", "i", SDLK_CAPSLOCK), + GB_CONSTANT("ScrollLock", "i", SDLK_SCROLLOCK), + GB_CONSTANT("RightShift", "i", SDLK_RSHIFT), + GB_CONSTANT("LeftShift", "i", SDLK_LSHIFT), + GB_CONSTANT("RightControl", "i", SDLK_RCTRL), + GB_CONSTANT("LeftControl", "i", SDLK_LCTRL), + GB_CONSTANT("RightAlt", "i", SDLK_RALT), + GB_CONSTANT("LeftAlt", "i", SDLK_LALT), + GB_CONSTANT("RightMeta", "i", SDLK_RMETA), + GB_CONSTANT("LeftMeta", "i", SDLK_LMETA), + GB_CONSTANT("RightSuper", "i", SDLK_RSUPER), + GB_CONSTANT("LeftSuper", "i", SDLK_LSUPER), + GB_CONSTANT("AltGr", "i", SDLK_MODE), + GB_CONSTANT("Compose", "i", SDLK_COMPOSE), + GB_CONSTANT("Help", "i", SDLK_HELP), + GB_CONSTANT("Print", "i", SDLK_PRINT), + GB_CONSTANT("SysReq", "i", SDLK_SYSREQ), + GB_CONSTANT("Break", "i", SDLK_BREAK), + GB_CONSTANT("Menu", "i", SDLK_MENU), + GB_CONSTANT("Power", "i", SDLK_POWER), + GB_CONSTANT("Euro", "i", SDLK_EURO), + GB_CONSTANT("Undo", "i", SDLK_UNDO), + + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Ckey.h b/gb.sdl/src/Ckey.h new file mode 100644 index 00000000..2763c408 --- /dev/null +++ b/gb.sdl/src/Ckey.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + Ckey.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CKEY_H +#define __CKEY_H + +#include "gambas.h" +#include "main.h" + +#include "SDL.h" +#include "SDL_syswm.h" +//#include "X11/keysym.h" + +typedef + struct { + int valid; + int code; // SDL virtual keysym + int state; // modifier state + //int cancel; + char text[4]; // UTF-8 text + } + CKEY_INFO; + +#ifndef __CKEY_CPP +extern GB_DESC CKey[]; +extern CKEY_INFO CKEY_info; +#endif /* __CKEY_CPP */ + +#endif /* __CKEY_H */ + diff --git a/gb.sdl/src/Cmouse.cpp b/gb.sdl/src/Cmouse.cpp new file mode 100644 index 00000000..8ffcb47b --- /dev/null +++ b/gb.sdl/src/Cmouse.cpp @@ -0,0 +1,319 @@ +/*************************************************************************** + + Cmouse.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOUSE_CPP + +#include "gambas.h" +#include "main.h" + +#include "Cmouse.h" +#include "Cimage.h" + +#include "SDL_h.h" +#include "SDLapp.h" + +CMOUSE_INFO CMOUSE_info = { 0 }; + +#define CHECK_VALID() \ + if (!CMOUSE_info.valid) \ + { \ + GB.Error("No mouse event data"); \ + return; \ + } + +/***************************************************************************/ + +#if 0 +BEGIN_METHOD(CURSOR_new, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y) + +/* CIMAGE *img = (CIMAGE *)VARG(image); + + THIS->x = VARGOPT(x, -1); + THIS->y = VARGOPT(y, -1); + + if (GB.CheckObject(img)) + return; + + if (THIS->x < 0 || THIS->x >= img->id->GetWidth()) + THIS->x = -1; + + if (THIS->y < 0 || THIS->y >= img->id->GetHeight()) + THIS->y = -1; + + THIS->cursor = new SDLcursor(); + THIS->cursor->SetCursor(img->id, THIS->x, THIS->y); +*/ +END_METHOD + + +BEGIN_METHOD_VOID(CURSOR_delete) + +// delete THIS->cursor; + +END_METHOD + +BEGIN_PROPERTY(CURSOR_x) + + GB.ReturnInteger(THIS->x); + +END_PROPERTY + + +BEGIN_PROPERTY(CURSOR_y) + + GB.ReturnInteger(THIS->y); + +END_PROPERTY +#endif + +BEGIN_METHOD(CMOUSE_move, GB_INTEGER x; GB_INTEGER y) + + SDL_WarpMouse(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_PROPERTY(CMOUSE_screenx) + + int x, y, toto; + Window tata; + unsigned int mask; + + SDLapp->LockX11(); + XQueryPointer(SDLapp->X11appDisplay(), SDLapp->X11appRootWin(), &tata, &tata, &x,&y, &toto,&toto, &mask); + SDLapp->UnlockX11(); + + GB.ReturnInteger(x); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_screeny) + + int x, y, toto; + Window tata; + unsigned int mask; + + SDLapp->LockX11(); + XQueryPointer(SDLapp->X11appDisplay(), SDLapp->X11appRootWin(), &tata, &tata, &x,&y, &toto,&toto, &mask); + SDLapp->UnlockX11(); + + GB.ReturnInteger(y); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_x) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.x); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_y) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.y); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_startx) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.x - CMOUSE_info.relx); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_starty) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.y - CMOUSE_info.rely); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_left) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_LEFT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_right) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_RIGHT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_middle) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_MIDDLE); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_wheelup) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_WHEELUP); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_wheeldown) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_WHEELDOWN); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_button) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.state); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_shift) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_SHIFT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_control) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_CTRL); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_alt) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_ALT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_meta) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_META); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_normal) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod < KMOD_NUM); + +END_PROPERTY + +BEGIN_METHOD_VOID(Mouse_Show) + + SDL_ShowCursor(SDL_ENABLE); + +END_METHOD + +BEGIN_METHOD_VOID(Mouse_Hide) + + SDL_ShowCursor(SDL_DISABLE); + +END_METHOD + +BEGIN_PROPERTY(Mouse_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE); + else + SDL_ShowCursor(VPROP(GB_BOOLEAN) ? SDL_ENABLE : SDL_DISABLE); + +END_PROPERTY + +/***************************************************************************/ +/* +GB_DESC CCursor[] = +{ + GB_DECLARE("Cursor", sizeof(CCURSOR)), + + GB_METHOD("_new", NULL, CURSOR_new, "(Image)Image;[(X)i(Y)i]"), + GB_METHOD("_free", NULL, CURSOR_delete, NULL), + + GB_PROPERTY_READ("X", "i", CURSOR_x), + GB_PROPERTY_READ("Y", "i", CURSOR_y), + + GB_END_DECLARE +}; +*/ +GB_DESC CMouse[] = +{ + GB_DECLARE("Mouse", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Move", NULL, CMOUSE_move, "(X)i(Y)i"), + + GB_STATIC_METHOD("Show", NULL, Mouse_Show, NULL), + GB_STATIC_METHOD("Hide", NULL, Mouse_Hide, NULL), + GB_STATIC_PROPERTY("Visible", "b", Mouse_Visible), + + GB_STATIC_PROPERTY_READ("ScreenX", "i", CMOUSE_screenx), + GB_STATIC_PROPERTY_READ("ScreenY", "i", CMOUSE_screeny), + GB_STATIC_PROPERTY_READ("StartX", "i", CMOUSE_startx), + GB_STATIC_PROPERTY_READ("StartY", "i", CMOUSE_starty), + GB_STATIC_PROPERTY_READ("X", "i", CMOUSE_x), + GB_STATIC_PROPERTY_READ("Y", "i", CMOUSE_y), + + GB_STATIC_PROPERTY_READ("Left", "b", CMOUSE_left), + GB_STATIC_PROPERTY_READ("Right", "b", CMOUSE_right), + GB_STATIC_PROPERTY_READ("Middle", "b", CMOUSE_middle), + GB_STATIC_PROPERTY_READ("WheelUp", "b", CMOUSE_wheelup), + GB_STATIC_PROPERTY_READ("WheelDown", "b", CMOUSE_wheeldown), + GB_STATIC_PROPERTY_READ("Button", "i", CMOUSE_button), + GB_STATIC_PROPERTY_READ("Shift", "b", CMOUSE_shift), + GB_STATIC_PROPERTY_READ("Control", "b", CMOUSE_control), + GB_STATIC_PROPERTY_READ("Alt", "b", CMOUSE_alt), + GB_STATIC_PROPERTY_READ("Meta", "b", CMOUSE_meta), + GB_STATIC_PROPERTY_READ("Normal", "b", CMOUSE_normal), +/* + GB_CONSTANT("Default", "i", SDL::DefaultCursor), + GB_CONSTANT("Custom", "i", SDL::CustomCursor), + GB_CONSTANT("Blank", "i", SDL::BlankCursor), + GB_CONSTANT("Arrow", "i", SDL::ArrowCursor), + GB_CONSTANT("Cross", "i", SDL::CrossCursor), + GB_CONSTANT("Wait", "i", SDL::WaitCursor), + GB_CONSTANT("Text", "i", SDL::TextCursor), + GB_CONSTANT("SizeAll", "i", SDL::SizeAllCursor), + GB_CONSTANT("SizeH", "i", SDL::SizeHorCursor), + GB_CONSTANT("SizeV", "i", SDL::SizeVerCursor), + GB_CONSTANT("SizeN", "i", SDL::SizeVerCursor), + GB_CONSTANT("SizeS", "i", SDL::SizeVerCursor), + GB_CONSTANT("SizeW", "i", SDL::SizeHorCursor), + GB_CONSTANT("SizeE", "i", SDL::SizeHorCursor), + GB_CONSTANT("SizeNW", "i", SDL::SizeFDiagCursor), + GB_CONSTANT("SizeSE", "i", SDL::SizeFDiagCursor), + GB_CONSTANT("SizeNE", "i", SDL::SizeBDiagCursor), + GB_CONSTANT("SizeSW", "i", SDL::SizeBDiagCursor), + GB_CONSTANT("SizeNWSE", "i", SDL::SizeFDiagCursor), + GB_CONSTANT("SizeNESW", "i", SDL::SizeBDiagCursor), + GB_CONSTANT("SplitH", "i", SDL::SplitHCursor), + GB_CONSTANT("SplitV", "i", SDL::SplitVCursor), + GB_CONSTANT("Pointing", "i", SDL::PointingHandCursor), +*/ + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cmouse.h b/gb.sdl/src/Cmouse.h new file mode 100644 index 00000000..45bb5f75 --- /dev/null +++ b/gb.sdl/src/Cmouse.h @@ -0,0 +1,65 @@ +/*************************************************************************** + + Cmouse.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOUSE_H +#define __CMOUSE_H + +#include "gambas.h" +#include "main.h" + +#include "SDL.h" +#include "SDLcursor.h" + +typedef + struct { + bool valid; + int x; + int y; + int relx; + int rely; + int state; + SDLMod keymod; + } + CMOUSE_INFO; + +#ifndef __CMOUSE_CPP +extern GB_DESC CMouse[]; +//extern GB_DESC CCursor[]; +extern CMOUSE_INFO CMOUSE_info; +#else + +#define THIS ((CCURSOR *)_object) + +#endif /* __CMOUSE_CPP */ + +typedef + struct _CCURSOR { + GB_BASE ob; + int x; + int y; + SDLcursor *cursor; + } + CCURSOR; + +#endif /* __CMOUSE_H */ + diff --git a/gb.sdl/src/Cwindow.cpp b/gb.sdl/src/Cwindow.cpp new file mode 100644 index 00000000..07006b29 --- /dev/null +++ b/gb.sdl/src/Cwindow.cpp @@ -0,0 +1,548 @@ +/*************************************************************************** + + Cwindow.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWINDOW_CPP + +#include + +#include "Cwindow.h" +#include "Cjoystick.h" +#include "Ckey.h" +#include "Cmouse.h" +#include "Cdraw.h" +#include "Cimage.h" + +#include "SDL.h" + +#define THIS ((CWINDOW *)_object) +#define WINDOWID ((CWINDOW *)_object)->id +// number of frames before counting FPS +#define FRAMECOUNT 100 + +// events +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Deactivate); +DECLARE_EVENT(EVENT_Enter); +DECLARE_EVENT(EVENT_JoyAxisMotion); +DECLARE_EVENT(EVENT_JoyBallMotion); +DECLARE_EVENT(EVENT_JoyButtonPressed); +DECLARE_EVENT(EVENT_JoyButtonReleased); +DECLARE_EVENT(EVENT_JoyHatMotion); +DECLARE_EVENT(EVENT_Leave); +DECLARE_EVENT(EVENT_Refresh); +DECLARE_EVENT(EVENT_KeyPressed); +DECLARE_EVENT(EVENT_KeyReleased); +DECLARE_EVENT(EVENT_MouseMove); +DECLARE_EVENT(EVENT_MouseDown); +DECLARE_EVENT(EVENT_MouseUp); +DECLARE_EVENT(EVENT_Open); + +/***************************************************************************/ + +BEGIN_METHOD(CWINDOW_new, GB_BOOLEAN openGL) + + WINDOWID = new myWin(THIS); + WINDOWID->SetTitle(GB.Application.Name()); + THIS->openGL = VARGOPT(openGL, false); + THIS->lastTime = SDL_GetTicks(); + THIS->startTime = THIS->lastTime; + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_free) + + GB.StoreObject(NULL, POINTER(&(THIS->cursor))); + delete WINDOWID; + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_show) + + WINDOWID->Show(); + WINDOWID->Refresh(); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_close) + + WINDOWID->Quit(); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_clear) + + WINDOWID->Clear(); + +END_METHOD + +BEGIN_METHOD(CWINDOW_fill, GB_INTEGER color;) + + WINDOWID->Clear(VARG(color)); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_refresh) + + WINDOWID->Refresh(); + +END_METHOD + +BEGIN_PROPERTY(CWINDOW_framerate) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->currentFPS); + else + { + double val = VPROP(GB_FLOAT); + + if (val < 0) + return; + + THIS->FPSLimit = val ? 1000.0 / val : 0; + THIS->lastTime = SDL_GetTicks(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(WINDOWID->GetTitle()); + else + WINDOWID->SetTitle(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_fullscreen) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOWID->IsFullScreen()); + else + WINDOWID->SetFullScreen(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOWID->IsResizable()); + else + WINDOWID->SetResizable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_mouse) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOWID->GetCursorShape()); + else + WINDOWID->SetCursorShape(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->tracking); + else + THIS->tracking = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_cursor) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->cursor); + else + { +/* + CCURSOR *curs = (CCURSOR *)VPROP(GB_OBJECT); + WINDOWID->SetCursor(curs->cursor); + return; +*/ + } + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_width) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOWID->GetWidth()); + else + THIS->id->SetWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_height) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOWID->GetHeight()); + else + WINDOWID->SetHeight(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_shown) + + GB.ReturnBoolean(WINDOWID->IsShown()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_id) + + GB.ReturnInteger(WINDOWID->Id()); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Grabbed) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOWID->IsInputGrabbed()); + else + WINDOWID->GrabInput(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(Window_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + GB.ReturnObject(CIMAGE_create_from_window(WINDOWID, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, -1), VARGOPT(h, -1))); + +END_METHOD + +/***************************************************************************/ + +GB_DESC CWindow[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), + + GB_METHOD("_new", NULL, CWINDOW_new, "[(OpenGL)b]"), + GB_METHOD("_free", NULL, CWINDOW_free, NULL), + + GB_METHOD("Show", NULL, CWINDOW_show, NULL), + GB_METHOD("Close", NULL, CWINDOW_close, NULL), + GB_METHOD("Clear", NULL, CWINDOW_clear, NULL), + GB_METHOD("Fill", NULL, CWINDOW_fill, "(Color)i"), + GB_METHOD("Refresh", NULL, CWINDOW_refresh, NULL), + GB_METHOD("Update", NULL, CWINDOW_refresh, NULL), + + GB_PROPERTY("Caption", "s", CWINDOW_text), +// GB_PROPERTY("Cursor", "Cursor;", CWINDOW_cursor), + GB_PROPERTY("Framerate", "f", CWINDOW_framerate), + GB_PROPERTY("FullScreen", "b", CWINDOW_fullscreen), + GB_PROPERTY("Height", "i", CWINDOW_height), + GB_PROPERTY("Mouse", "i", CWINDOW_mouse), + GB_PROPERTY("Text", "s", CWINDOW_text), + GB_PROPERTY("Title", "s", CWINDOW_text), + GB_PROPERTY("Tracking", "b", CWINDOW_tracking), + GB_PROPERTY("Resizable", "b", CWINDOW_resizable), + GB_PROPERTY("Width", "i", CWINDOW_width), + + GB_PROPERTY_READ("Handle", "i", CWINDOW_id), + GB_PROPERTY_READ("Id", "i", CWINDOW_id), + GB_PROPERTY_READ("Shown", "b", CWINDOW_shown), + GB_PROPERTY("Grabbed", "b", Window_Grabbed), + + GB_METHOD("Screenshot", "Image", Window_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_EVENT("Close", "b", NULL, &EVENT_Close), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("JoyAxisMove", NULL, NULL, &EVENT_JoyAxisMotion), + GB_EVENT("JoyBallMove", NULL, NULL, &EVENT_JoyBallMotion), + GB_EVENT("JoyButtonPress", NULL, NULL, &EVENT_JoyButtonPressed), + GB_EVENT("JoyButtonRelease", NULL, NULL, &EVENT_JoyButtonReleased), + GB_EVENT("JoyHatMove", NULL, NULL, &EVENT_JoyHatMotion), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("Draw", "b", NULL, &EVENT_Refresh), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPressed), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyReleased), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +#define WINDOW(object) ((CWINDOW *)object) + +void myWin::Resize(void) +{ + GB.Raise(hWindow, EVENT_Resize, 0); +} + +void myWin::GotFocus(void) +{ + GB.Raise(hWindow, EVENT_Activate, 0); +} + +void myWin::LostFocus(void) +{ + GB.Raise(hWindow, EVENT_Deactivate, 0); +} + +void myWin::MouseEnter(void) +{ + GB.Raise(hWindow, EVENT_Enter, 0); +} + +void myWin::MouseLeave(void) +{ + GB.Raise(hWindow, EVENT_Leave, 0); +} + +void myWin::Quit(void) +{ + bool cancel = GB.Raise(hWindow, EVENT_Close, 0); + + if (!cancel) + this->Close(); +} + +void myWin::Update(void) +{ + Uint32 ticks, diff; + + // no refresh event + if (!GB.CanRaise(hWindow, EVENT_Refresh)) + { + SDL_Delay(1); + return; + } + + ticks = SDL_GetTicks(); + + // framerate limitation + if (WINDOW(hWindow)->FPSLimit > 0) + { + double d = WINDOW(hWindow)->lastTime + WINDOW(hWindow)->FPSLimit; + + //fprintf(stderr, "%d %g %g %d\n", ticks, d, WINDOW(hWindow)->lastTime, d < ticks); + + if (d > ticks) + { + SDL_Delay(1); + return; + } + + WINDOW(hWindow)->lastTime = d; + } + + DRAW_begin(hWindow); + bool cancel = GB.Raise(hWindow, EVENT_Refresh, 0); + DRAW_end(); + + // user doesn't want to refresh + if (cancel) + { + //SDL_Delay(1); + return; + } + else + this->Refresh(); + + // calculate the framerate + /*if (WINDOW(hWindow)->countFrames >= FRAMECOUNT) + { + double value = (ticks - WINDOW(hWindow)->startTime) / FRAMECOUNT; + + if (value > 0) + WINDOW(hWindow)->currentFPS = Uint32(1000 / value + 0.5); + else + WINDOW(hWindow)->currentFPS = 0; + + WINDOW(hWindow)->countFrames = 0; + WINDOW(hWindow)->startTime = ticks; + } + else + WINDOW(hWindow)->countFrames++;*/ + + WINDOW(hWindow)->countFrames++; + + diff = ticks - WINDOW(hWindow)->startTime; + if (diff > 1000) + { + WINDOW(hWindow)->currentFPS = WINDOW(hWindow)->countFrames; + WINDOW(hWindow)->countFrames = 0; + WINDOW(hWindow)->startTime += 1000; + } +} + +void myWin::Open(void) +{ + if (!(((CWINDOW *)hWindow)->openGL)) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, this->GetWidth(), this->GetHeight()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, GLdouble(this->GetWidth()), GLdouble(this->GetHeight()), 0.0f, -1, 1); + glMatrixMode(GL_MODELVIEW); + } + + if (GB.CanRaise(hWindow, EVENT_Open)) + GB.Raise(hWindow, EVENT_Open,0); + + if ((((CWINDOW *)hWindow)->openGL)) + { + if (GB.CanRaise(hWindow, EVENT_Resize)) + GB.Raise(hWindow, EVENT_Resize,0); + } +} + +void myWin::JoyEvent(SDL_Event& event) +{ + CJOY_info.valid = true; + switch(event.type) + { + case SDL_JOYAXISMOTION: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jaxis.axis; + CJOY_info.value1 = event.jaxis.value; + CJOY_info.value2 = 0; + GB.Raise(hWindow, EVENT_JoyAxisMotion, 0); + break; + } + case SDL_JOYHATMOTION: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jhat.hat; + CJOY_info.value1 = event.jhat.value; + CJOY_info.value2 = 0; + GB.Raise(hWindow, EVENT_JoyHatMotion, 0); + break; + } + case SDL_JOYBALLMOTION: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jball.ball; + CJOY_info.value1 = event.jball.xrel; + CJOY_info.value2 = event.jball.yrel; + GB.Raise(hWindow, EVENT_JoyBallMotion, 0); + break; + } + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jbutton.button; + CJOY_info.value1 = 0; + CJOY_info.value2 = 0; + if (event.jbutton.state == SDL_PRESSED) + GB.Raise(hWindow, EVENT_JoyButtonPressed, 0); + else + GB.Raise(hWindow, EVENT_JoyButtonReleased, 0); + break; + } + default: + break; + } + + CJOY_info.valid = false; +} + +static void convert_unicode_to_utf8(Uint16 unicode, char *buffer) +{ + if (unicode < 0x80) + { + buffer[0] = unicode; + buffer[1] = 0; + } + else if (unicode < 0x800) + { + buffer[0] = 0xC0 | (unicode >> 6); + buffer[1] = 0x80 | (unicode & 0x3F); + buffer[2] = 0; + } + else + { + buffer[0] = 0xE0 | (unicode >> 12); + buffer[1] = 0x80 | ((unicode >> 6) & 0x3F); + buffer[2] = 0x80 | (unicode & 0x3F); + buffer[3] = 0; + } +} + +void myWin::KeyEvent(SDL_KeyboardEvent *keyEvent, int eventType) +{ + CKEY_info.valid++; + + CKEY_info.code = keyEvent->keysym.sym; + CKEY_info.state = keyEvent->keysym.mod; + convert_unicode_to_utf8(keyEvent->keysym.unicode, CKEY_info.text); + + //SDLapp->LockX11(); + //CKEY_info.code = XKeycodeToKeysym(SDLapp->X11appDisplay(), keyEvent->keysym.scancode, 0); + //SDLapp->UnlockX11(); + //CKEY_info.state = keyEvent->keysym.mod; + + if (eventType == SDL_KEYDOWN) + GB.Raise(hWindow, EVENT_KeyPressed,0); + else + GB.Raise(hWindow, EVENT_KeyReleased,0); + + CKEY_info.valid--; +} + +void myWin::MouseButtonEvent(SDL_MouseButtonEvent *mouseEvent) +{ + CMOUSE_info.valid = true; + CMOUSE_info.x = mouseEvent->x; + CMOUSE_info.y = mouseEvent->y; + CMOUSE_info.state = mouseEvent->button; + CMOUSE_info.keymod = SDL_GetModState(); + + if (mouseEvent->type == SDL_MOUSEBUTTONDOWN) + GB.Raise(hWindow, EVENT_MouseDown,0); + else + GB.Raise(hWindow, EVENT_MouseUp,0); + + CMOUSE_info.valid = false; + +} + +void myWin::MouseMotionEvent(SDL_MouseMotionEvent *mouseEvent) +{ + CMOUSE_info.relx = mouseEvent->xrel; + CMOUSE_info.rely = mouseEvent->yrel; + + // do not raise event if no mouse button are pressed/released && tracking is not set + if ((!mouseEvent->state) && (!(((CWINDOW *)hWindow)->tracking))) + return; + + CMOUSE_info.valid = true; + CMOUSE_info.x = mouseEvent->x; + CMOUSE_info.y = mouseEvent->y; + CMOUSE_info.state = mouseEvent->state; + CMOUSE_info.keymod = SDL_GetModState(); + GB.Raise(hWindow, EVENT_MouseMove,0); + CMOUSE_info.valid = false; +} diff --git a/gb.sdl/src/Cwindow.h b/gb.sdl/src/Cwindow.h new file mode 100644 index 00000000..7c3138a9 --- /dev/null +++ b/gb.sdl/src/Cwindow.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + Cwindow.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWINDOW_H +#define __CWINDOW_H + +#include "gambas.h" +#include "main.h" +#include "SDLwindow.h" +#include "Cfont.h" +#include "Cmouse.h" + +class myWin : public SDLwindow +{ +public: + myWin(void *win):SDLwindow() { hWindow = win; }; + ~myWin() {}; + + void Quit(void ); + void Resize(void ); + void GotFocus(void ); + void LostFocus(void ); + void MouseEnter(void ); + void MouseLeave(void ); + void Update(void ); + void Open(void ); + void JoyEvent(SDL_Event& ); + void KeyEvent(SDL_KeyboardEvent* , int); + void MouseButtonEvent(SDL_MouseButtonEvent* ); + void MouseMotionEvent(SDL_MouseMotionEvent* ); +private: + void *hWindow; +}; + +typedef + struct { + GB_BASE ob; + CCURSOR *cursor; + + myWin *id; + bool openGL; + bool tracking; + // framerate control + double FPSLimit; // duration of a frame in milliseconds, if 0 -> no framerate limit + double lastTime; + // framerate count + Uint32 startTime; + Uint32 countFrames; + double currentFPS; + } + CWINDOW; + +#ifndef __CWINDOW_CPP +extern GB_DESC CWindow[]; +#endif /* __CWINDOW_CPP */ + +#endif /* __CWINDOW_H */ + diff --git a/gb.sdl/src/Makefile.am b/gb.sdl/src/Makefile.am new file mode 100644 index 00000000..d40414ed --- /dev/null +++ b/gb.sdl/src/Makefile.am @@ -0,0 +1,34 @@ +COMPONENT = gb.sdl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.sdl.la + +gb_sdl_la_LIBADD = @SDL_LIB@ +gb_sdl_la_LDFLAGS = -module @LD_FLAGS@ @SDL_LDFLAGS@ +gb_sdl_la_CXXFLAGS = $(AM_CXXFLAGS) -DDATA_DIR=\"$(DESTDIR)$(gbdatadir)/$(COMPONENT)\" +gb_sdl_la_CPPFLAGS = @SDL_INC@ + +gb_sdl_la_SOURCES = \ + SDL_h.h \ + SDLapp.h SDLapp.cpp \ + SDLcore.h SDLcore.cpp \ + SDLdebug.h SDLdebug.cpp \ + SDLerror.h SDLerror.cpp \ + SDLfont.h SDLfont.cpp \ + SDLgfx.h SDLgfx.cpp \ + SDLcursor.h SDLcursor.cpp \ + SDLosrender.h SDLosrender.cpp \ + SDLsurface.h SDLsurface.cpp \ + SDLtexture.h SDLtexture.cpp \ + SDLwindow.h SDLwindow.cpp \ + Cconst.h Cconst.cpp \ + Cdesktop.h Cdesktop.cpp \ + Cdraw.h Cdraw.cpp \ + Cfont.h Cfont.cpp \ + Cimage.h Cimage.cpp \ + Cjoystick.h Cjoystick.cpp \ + Ckey.h Ckey.cpp \ + Cmouse.h Cmouse.cpp \ + Cwindow.h Cwindow.cpp \ + default_font.h \ + main.h main.cpp diff --git a/gb.sdl/src/SDL_h.h b/gb.sdl/src/SDL_h.h new file mode 100644 index 00000000..62795dfa --- /dev/null +++ b/gb.sdl/src/SDL_h.h @@ -0,0 +1,84 @@ +/*************************************************************************** + + SDL_h.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDL_H_H +#define __SDL_H_H + +#include +#include +#include +#include + +#include "SDLapp.h" +#include "SDLerror.h" +#include "SDLdebug.h" + +namespace SDL +{ + // constant values for drawing + // line + enum LineStyle { + NoLine = 0, + SolidLine, + DashLine, + DotLine, + DashDotLine, + DashDotDotLine}; + // filling + enum FillStyle { + NoFill = 0, + SolidFill, + VerticalFill, + HorizontalFill, + CrossFill, + BackDiagFill, + DiagFill, + DiagCrossFill, + Dense1Fill, + Dense2Fill, + Dense3Fill, + Dense4Fill, + Dense5Fill, + Dense6Fill, + Dense7Fill}; + // cursor shapes + enum CursorsShape { + CustomCursor = -3, + DefaultCursor = -2, + BlankCursor = -1, + ArrowCursor = XC_left_ptr, + CrossCursor = XC_crosshair, + WaitCursor = XC_watch, + PointingHandCursor = XC_hand2, + SizeAllCursor = XC_fleur, + SizeHorCursor = XC_sb_h_double_arrow, + SizeVerCursor = XC_sb_v_double_arrow, + SizeFDiagCursor = XC_bottom_right_corner, + SizeBDiagCursor = XC_top_right_corner, + SplitHCursor = XC_sb_h_double_arrow, + SplitVCursor = XC_sb_v_double_arrow, + TextCursor = XC_xterm}; +} + +#endif /* __SDL_H_H */ + diff --git a/gb.sdl/src/SDLapp.cpp b/gb.sdl/src/SDLapp.cpp new file mode 100644 index 00000000..44398cfd --- /dev/null +++ b/gb.sdl/src/SDLapp.cpp @@ -0,0 +1,266 @@ +/*************************************************************************** + + SDLapp.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLapp.h" + +#include "SDLcore.h" +#include "SDLwindow.h" +#include "SDL_ttf.h" + +#include +#include + +int SDLapplication::AppCount = 0; +int SDLapplication::LockX11Count = 0; + +SDLapplication *SDLapp; + +SDLapplication::SDLapplication(int &argc, char **argv) +{ + // init is already done ! + if (SDLapplication::AppCount) + { + SDLapplication::AppCount++; + return; + } + + std::string sMsg = "Failed to init: "; + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if audio is defined, sdl was init by gb.sdl.sound component ! + if (sysInit & SDL_INIT_AUDIO) + { + if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)<0) + { + sMsg =+ SDL_GetError(); + goto _error; + } + } + else + { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE | SDL_INIT_JOYSTICK)<0) + { + sMsg =+ SDL_GetError(); + goto _error; + } + } + + if (TTF_Init()<0) + { + sMsg =+ TTF_GetError(); + goto _error; + } + + SDLapp = this; + + SDL_EnableUNICODE(1); + + SDLcore::Init(); + SDLdebug::Init(); + + return; + +_error: + std::cout << sMsg << std::endl; + exit (-1); +} + +SDLapplication::~SDLapplication() +{ + // stop SDL only if it's the last ~SDLapplication call + if (SDLapplication::AppCount>1) + { + SDLapplication::AppCount--; + return; + } + + TTF_Quit(); + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if audio is defined, gb.sdl.audio component still not closed ! + if (sysInit & SDL_INIT_AUDIO) + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); + else + SDL_Quit(); +} + +static int poll_event(SDL_Event *event, bool no_input) +{ + uint mask; + + SDL_PumpEvents(); + + mask = SDL_ALLEVENTS; + if (no_input) + mask ^= SDL_KEYEVENTMASK | SDL_MOUSEEVENTMASK | SDL_JOYEVENTMASK | SDL_QUITMASK; + + /* We can't return -1, just return 0 (no event) on error */ + if ( SDL_PeepEvents(event, 1, SDL_GETEVENT, mask) <= 0 ) + return 0; + return 1; +} + + +void SDLapplication::ManageEvents(bool no_input) +{ + SDL_Event event; + + while (poll_event(&event, no_input)) + { + if (!this->HaveWindows()) + break; + + switch(event.type) + { + case SDL_QUIT: + SDLcore::GetWindow()->Quit(); + break; + case SDL_ACTIVEEVENT: + if (event.active.state==SDL_APPINPUTFOCUS) + { + if (event.active.gain) + SDLcore::GetWindow()->GotFocus(); + else + SDLcore::GetWindow()->LostFocus(); + } + if (event.active.state==SDL_APPMOUSEFOCUS) + { + if (event.active.gain) + SDLcore::GetWindow()->MouseEnter(); + else + SDLcore::GetWindow()->MouseLeave(); + } + break; + case SDL_VIDEORESIZE: + SDLcore::GetWindow()->SetWidth(event.resize.w); + SDLcore::GetWindow()->SetHeight(event.resize.h); + SDLcore::GetWindow()->Resize(); + + // take care if window is closed during resize + if (this->HaveWindows()) + SDLcore::GetWindow()->Show(); + break; + case SDL_JOYAXISMOTION: + case SDL_JOYHATMOTION: + case SDL_JOYBALLMOTION: + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + // Only raise joysticks events if window has input keyboard focus + if (SDL_GetAppState() & SDL_APPINPUTFOCUS) + SDLcore::GetWindow()->JoyEvent(event); + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + SDLcore::GetWindow()->KeyEvent(&event.key, event.type); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + SDLcore::GetWindow()->MouseButtonEvent(&event.button); + break; + case SDL_MOUSEMOTION: + SDLcore::GetWindow()->MouseMotionEvent(&event.motion); + break; + default: + break; + } + } + + if (this->HaveWindows()) // take care if window wasn't close during events + SDLcore::GetWindow()->Update(); + +} + +bool SDLapplication::HaveWindows() +{ + return (SDLcore::GetWindow() ? true : false); +} + +int SDLapplication::DesktopWidth() +{ + LockX11(); + int Width = XDisplayWidth(display, DefaultScreen(display)); + UnlockX11(); + + return (Width); +} + +int SDLapplication::DesktopHeight() +{ + LockX11(); + int Height = XDisplayHeight(display, DefaultScreen(display)); + UnlockX11(); + + return (Height); +} + +Window SDLapplication::X11appRootWin() +{ + LockX11(); + Window win = XDefaultRootWindow(display); + UnlockX11(); + + return (win); +} + +Window SDLapplication::CurrentWin() +{ + LockX11(); + // refresh window variable ;) + UnlockX11(); + + return (window); +} + +Display *SDLapplication::X11appDisplay() +{ + LockX11(); + // refresh display variable ;) + UnlockX11(); + + return (display); +} + +void SDLapplication::LockX11() +{ + SDLapplication::LockX11Count++; + SDL_VERSION(&info.version); + SDL_GetWMInfo(&info); + + if (SDLapplication::LockX11Count==1) + info.info.x11.lock_func(); + + display = info.info.x11.display; + window = info.info.x11.window; +} + +void SDLapplication::UnlockX11() +{ + SDLapplication::LockX11Count--; + + if (SDLapplication::LockX11Count>1) + return; + + SDLapplication::LockX11Count = 0; + info.info.x11.unlock_func(); +} + diff --git a/gb.sdl/src/SDLapp.h b/gb.sdl/src/SDLapp.h new file mode 100644 index 00000000..026f2054 --- /dev/null +++ b/gb.sdl/src/SDLapp.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + SDLapp.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLAPP_H +#define __SDLAPP_H + +#include "SDL_h.h" + +class SDLapplication +{ +public: + SDLapplication(int &argc, char **argv); + virtual ~SDLapplication(); + + void ManageEvents(bool no_input = false); + bool HaveWindows(void ); + + int DesktopWidth(void ); + int DesktopHeight(void ); + Window X11appRootWin(void ); + Window CurrentWin(void ); + Display* X11appDisplay(void ); + + virtual void ManageError(const char* ) = 0; + // needed if calling X11 funcs ! + void LockX11(void ); + void UnlockX11(void ); + +private: + // datas + static int AppCount; + static int LockX11Count; + SDL_SysWMinfo info; + Display *display; + Window window; +}; + +extern SDLapplication *SDLapp; + +#endif /* __SDLAPP_H */ + diff --git a/gb.sdl/src/SDLcore.cpp b/gb.sdl/src/SDLcore.cpp new file mode 100644 index 00000000..6ab75e2b --- /dev/null +++ b/gb.sdl/src/SDLcore.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + + SDLcore.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLcore.h" + +#include "SDLwindow.h" +#include "SDLapp.h" + +const SDL_VideoInfo *SDLcore::hVideoInfo = NULL; +SDLwindow *SDLcore::hWindow = NULL; + +void SDLcore::RegisterWindow(SDLwindow *window) +{ + if (window) + { + if (hWindow) + hWindow->Close(); + } + + hWindow = window; +} + +void SDLcore::Init(void) +{ + hVideoInfo = SDL_GetVideoInfo(); +} + +void SDLcore::RaiseError(std::string error) +{ + if (SDLapp) + SDLapp->ManageError(error.c_str()); +} diff --git a/gb.sdl/src/SDLcore.h b/gb.sdl/src/SDLcore.h new file mode 100644 index 00000000..aeefda00 --- /dev/null +++ b/gb.sdl/src/SDLcore.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + SDLcore.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLCORE_H +#define __SDLCORE_H + +#include "SDL_h.h" + +#include + +class SDLwindow; +class SDLapplication; + +class SDLcore +{ +public: + static void Init(void); + + // this window will receive the events + static void RegisterWindow(SDLwindow* ); + static SDLwindow* GetWindow(void ) { return (hWindow); }; + static void RaiseError(std::string ); +private: + static const SDL_VideoInfo* hVideoInfo; + // shown window (for events) + static SDLwindow* hWindow; +}; + +#endif /* __SDLCORE_H */ + diff --git a/gb.sdl/src/SDLcursor.cpp b/gb.sdl/src/SDLcursor.cpp new file mode 100644 index 00000000..a24f5268 --- /dev/null +++ b/gb.sdl/src/SDLcursor.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** + + SDLcursor.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLcursor.h" +#include "SDLcore.h" + +#include + +SDLcursor::SDLcursor() +{ + Display *myDisplay = SDLapp->X11appDisplay(); + hCursor = XcursorLibraryLoadCursor(myDisplay, XcursorGetTheme(myDisplay)); + hShape = SDL::DefaultCursor; + hImgCursor = NULL; +} + +SDLcursor::SDLcursor(const SDLcursor& curs) +{ + hCursor = curs.hCursor; + hShape = curs.hShape; + hImgCursor = NULL; + + if (curs.hImgCursor) + { + std::cout << curs.hImgCursor->width << " " << curs.hImgCursor->height << std::endl; + hImgCursor = XcursorImageCreate(curs.hImgCursor->width, curs.hImgCursor->height); + memcpy (hImgCursor->pixels, curs.hImgCursor->pixels, hImgCursor->width * hImgCursor->height * 4); + } + +} + +SDLcursor::~SDLcursor() +{ + if (hImgCursor) + XcursorImageDestroy(hImgCursor); +} + +void SDLcursor::Show(Window w) +{ + Cursor cursor = hCursor; + int shape = hShape; + Display *myDisplay = SDLapp->X11appDisplay(); + + if (hShape == SDL::BlankCursor) + { + SDL_ShowCursor(SDL_DISABLE); + return; + } + + if (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE) + SDL_ShowCursor(SDL_ENABLE); + + + if (shape == SDL::DefaultCursor) + shape = SDL::ArrowCursor; + + SDLapp->LockX11(); + if (shape != SDL::CustomCursor) + cursor = XcursorShapeLoadCursor(myDisplay, shape); + else + cursor = XcursorImageLoadCursor(myDisplay, hImgCursor); + + XDefineCursor(myDisplay, w, cursor); + SDLapp->UnlockX11(); + +} + +void SDLcursor::SetShape(int shape) +{ + if (hShape == shape) + return; + + if (hShape == SDL::CustomCursor && !hImgCursor) + return; + + hShape = shape; + +} + +void SDLcursor::SetCursor(SDLsurface *image, int xhot, int yhot) +{ + if (image->IsNull()) { + hShape = SDL::BlankCursor; + return; + } + + if (hImgCursor) + XcursorImageDestroy(hImgCursor); + + hImgCursor = XcursorImageCreate(image->GetWidth(), image->GetHeight()); + + if ((xhot < 0)) + xhot = 0; + if (((unsigned int)xhot > hImgCursor->width)) + xhot = hImgCursor->width; + if ((yhot < 0)) + yhot = 0; + if (((unsigned int)yhot > hImgCursor->height)) + yhot = hImgCursor->height; + + memcpy (hImgCursor->pixels, image->GetData(), image->GetWidth() * image->GetHeight() * 4); + hImgCursor->xhot = xhot; + hImgCursor->yhot = yhot; + hShape = SDL::CustomCursor; +} + diff --git a/gb.sdl/src/SDLcursor.h b/gb.sdl/src/SDLcursor.h new file mode 100644 index 00000000..2054b205 --- /dev/null +++ b/gb.sdl/src/SDLcursor.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + SDLcursor.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLCURSOR_H +#define __SDLCURSOR_H + +#include "SDL_h.h" +#include "SDLsurface.h" + +#include +#include + +class SDLcursor +{ +public: + SDLcursor(); + SDLcursor(const SDLcursor& cursor); + ~SDLcursor(); + + void Show(Window ); + void SetShape(int ); + void SetCursor(SDLsurface* image, int xhot = -1, int yhot = -1); + + int GetShape(void ) { return hShape; } + +private: + Cursor hCursor; + int hShape; + XcursorImage *hImgCursor; +}; + +#endif /* SDLCURSOR_H */ diff --git a/gb.sdl/src/SDLdebug.cpp b/gb.sdl/src/SDLdebug.cpp new file mode 100644 index 00000000..4ca0f8ce --- /dev/null +++ b/gb.sdl/src/SDLdebug.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + + SDLdebug.cpp + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLdebug.h" + +#include +#include +#include + +#include +#include +#include + +std::string DebugString = ""; + +void SDLdebug::Init() +{ + char* debug = std::getenv("DEBUG_GB_SDL"); + + if (debug != NULL) + DebugString = debug; +} + +void SDLdebug::Print(const char* message, ...) +{ + std::string msg; + const char *p; + std::va_list list; + va_start(list, message); + + if (DebugString.empty()) + return; + + for (p = message ; *p ; p++) + { + std::stringstream res; + + if (*p != '%') + { + msg.push_back(*p); + continue; + } + + p++; + switch(*p) + { + int val; + char* strval; + + //long + case 'd' : + val = va_arg(list, long); + res << val; + break; + //hex + case 'h' : + val = va_arg(list, long); + res << std::hex << val; + break; + //boolean + case 'b' : + val = va_arg(list, int); + (val) ? res << "True": res << "False"; + break; + //char string + case 's' : + strval = va_arg(list, char*); + res << strval; + break; + default : + res << "%" << *p; + } + msg = msg.append(res.str()); + } + + va_end(list); + std::cerr << "==GB.SDL== " << msg << std::endl; +} diff --git a/gb.sdl/src/SDLdebug.h b/gb.sdl/src/SDLdebug.h new file mode 100644 index 00000000..6c791eab --- /dev/null +++ b/gb.sdl/src/SDLdebug.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + SDLdebug.h + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLDEBUG_H +#define __SDLDEBUG_H + +namespace SDLdebug { + +void Init(void ); +void Print(const char* message, ...); + +} + +#endif /* __SDLDEBUG_H */ \ No newline at end of file diff --git a/gb.sdl/src/SDLerror.cpp b/gb.sdl/src/SDLerror.cpp new file mode 100644 index 00000000..ed7b10d1 --- /dev/null +++ b/gb.sdl/src/SDLerror.cpp @@ -0,0 +1,30 @@ +/*************************************************************************** + + SDLerror.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLerror.h" +#include "SDLcore.h" + +void SDLerror::RaiseError(std::string error) +{ + SDLcore::RaiseError(error); +} diff --git a/gb.sdl/src/SDLerror.h b/gb.sdl/src/SDLerror.h new file mode 100644 index 00000000..0efc6c61 --- /dev/null +++ b/gb.sdl/src/SDLerror.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + SDLerror.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLERROR_H +#define __SDLERROR_H + +#include + +#define COMP_WARN "gb.sdl warning: &1", +#define COMP_ERR "gb.sdl error: &1", +#define COMP_INFO "gb.sdl info: &1", + +class SDLerror +{ +public: + static void RaiseError(std::string ); +// static void RaiseWarning(std::string ); +}; + +#endif /* __SDLERROR_H */ + diff --git a/gb.sdl/src/SDLfont.cpp b/gb.sdl/src/SDLfont.cpp new file mode 100644 index 00000000..54409e2a --- /dev/null +++ b/gb.sdl/src/SDLfont.cpp @@ -0,0 +1,586 @@ +/*************************************************************************** + + SDLfont.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLfont.h" +#include "SDLapp.h" +#include "gb_common.h" +#include "main.h" +#include +#include + +#include "default_font.h" + +#if 0 +typedef struct { + std::string name; + std::string realname; + std::string foundry; + std::string path; + } fontdesc; + +static std::vector fontDB; +static StringList _FontList; + +#define DEFAULT_DPI 72 /* Default DPI size in SDL_TTF */ + +inline bool cmp_db_nocase(const fontdesc x, const fontdesc y) +{ + std::string a = x.name, b = y.name; + transform(a.begin(), a.end(), a.begin(), tolower); + transform(b.begin(), b.end(), b.begin(), tolower); + return (b>a) ? 1 : 0; +} + +StringList SDLfont::GetFontList(void ) +{ + return _FontList; +} + +void SDLfont::Init() +{ + Display *disp = XOpenDisplay(NULL); + int scr = XDefaultScreen(disp); + int i; + + XftFontSet *FntnameSet = XftListFonts(disp, scr, 0, + XFT_FAMILY, NULL); + + // Get the fonts name + for (i = 0; i < FntnameSet->nfont; i++) { + char *name[255]; + char *foundry[255]; + fontdesc font; + unsigned int j; + + XftResult res = XftPatternGetString(FntnameSet->fonts[i], XFT_FAMILY, 0, name); + + if (res!=XftResultMatch) + continue; + + XftFontSet *Fntdetail = XftListFonts(disp, scr, + XFT_FAMILY, XftTypeString, name[0], 0, + XFT_FOUNDRY, NULL); + j = Fntdetail->nfont; + + if (j>1) { + while (j) { + XftPatternGetString(Fntdetail->fonts[j-1], XFT_FOUNDRY, 0, foundry); + font.name = font.realname = name[0]; + font.foundry = foundry[0]; + font.name = font.name + " [" +font.foundry+ "]"; + fontDB.push_back(font); + j--; + } + } + else { + font.name = font.realname = name[0]; + XftPatternGetString(Fntdetail->fonts[0], XFT_FOUNDRY, 0, foundry); + font.foundry = foundry[0]; + fontDB.push_back(font); + } + XFree(Fntdetail); + } + + std::sort(fontDB.begin(), fontDB.end(), cmp_db_nocase); + XFree(FntnameSet); + + std::string font = ""; + i = 0; + while (ifonts[0], XFT_FILE, 0, res); + fontDB[i].path = res[0]; + _FontList.push_back(fontDB[i].name); + XFree(Fntdetail); + i++; + } + + XCloseDisplay(disp); +} +#endif + +#define UNICODE_INVALID 0xFFFFFFFFU + +static const char _char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define utf8_get_char_length(_c) ((int)_char_length[(unsigned char)(_c)]) + +static int utf8_get_length(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + int ulen; + int i; + + ulen = 0; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + +static uint utf8_to_unicode(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + uint unicode; + + switch (len) + { + case 2: + unicode = (str[1] & 0x3F) + ((str[0] & 0x1F) << 6); + if (unicode < 0x80) + goto _INVALID; + break; + + case 3: + unicode = (str[2] & 0x3F) + ((str[1] & 0x3F) << 6) + ((str[0] & 0xF) << 12); + if (unicode < 0x800) + goto _INVALID; + break; + + case 4: + unicode = (str[3] & 0x3F) + ((str[2] & 0x3F) << 6) + ((str[1] & 0x3F) << 12) + ((str[0] & 0x7) << 18); + if (unicode < 0x10000) + goto _INVALID; + break; + + case 5: + unicode = (str[4] & 0x3F) + ((str[3] & 0x3F) << 6) + ((str[2] & 0x3F) << 12) + ((str[1] & 0x3F) << 18) + ((str[0] & 0x3) << 24); + if (unicode < 0x200000) + goto _INVALID; + break; + + case 6: + unicode = (str[5] & 0x3F) + ((str[4] & 0x3F) << 6) + ((str[3] & 0x3F) << 12) + ((str[2] & 0x3F) << 18) + ((str[1] & 0x3F) << 24) + ((str[0] & 0x1) << 30); + if (unicode < 0x4000000) + goto _INVALID; + break; + + default: + unicode = str[0]; + break; + } + + return unicode; + +_INVALID: + + return UNICODE_INVALID; +} + + +static void render_default_font(uint *dest, int size, const char *text, int len) +{ + static void *jump[] = { &&__0, &&__1, &&__2, &&__3, &&__4, &&__5, &&__6, &&__7, &&__8, &&__9, &&__A, &&__B, &&__C, &&__D, &&__E, &&__F }; + static void *jump2[] = { &&__00, &&__10, &&__20, &&__30, &&__40, &&__50, &&__60, &&__70, &&__80, &&__90, &&__A0, &&__B0, &&__C0, &&__D0, &&__E0, &&__F0 }; + + int lc; + const char *src; + uint *p; + int y; + uchar line; + uint code; + uint col = 0xFFFFFFFF; + + size *= DEFAULT_FONT_WIDTH; + + for(;;) + { + if (!*text) + break; + + lc = utf8_get_char_length(*text); + code = utf8_to_unicode(text, lc); + text += lc; + + if (code >= 33 && code <= 126) + src = _default_font_33_126 + DEFAULT_FONT_HEIGHT * (code - 33); + else if (code >= 160 && code <= 687) + src = _default_font_160_687 + DEFAULT_FONT_HEIGHT * (code - 160); + else + src = NULL; + + if (src) + { + p = dest; + + for (y = 0; y < DEFAULT_FONT_HEIGHT; y++) + { + line = *src++; + if (!line) + { + p += size; + continue; + } + + goto *jump[line & 0xF]; + + __1: p[0] = col; goto __0; + __2: p[1] = col; goto __0; + __3: p[0] = p[1] = col; goto __0; + __4: p[2] = col; goto __0; + __5: p[0] = p[2] = col; goto __0; + __6: p[1] = p[2] = col; goto __0; + __7: p[0] = p[1] = p[2] = col; goto __0; + __8: p[3] = col; goto __0; + __9: p[0] = p[3] = col; goto __0; + __A: p[1] = p[3] = col; goto __0; + __B: p[0] = p[1] = p[3] = col; goto __0; + __C: p[2] = p[3] = col; goto __0; + __D: p[0] = p[2] = p[3] = col; goto __0; + __E: p[1] = p[2] = p[3] = col; goto __0; + __F: p[0] = p[1] = p[2] = p[3] = col; goto __0; + __0: + + goto *jump2[line >> 4]; + + __10: p[4] = col; goto __00; + __20: p[5] = col; goto __00; + __30: p[4] = p[5] = col; goto __00; + __40: p[6] = col; goto __00; + __50: p[4] = p[6] = col; goto __00; + __60: p[5] = p[6] = col; goto __00; + __70: p[4] = p[5] = p[6] = col; goto __00; + __80: p[7] = col; goto __00; + __90: p[4] = p[7] = col; goto __00; + __A0: p[5] = p[7] = col; goto __00; + __B0: p[4] = p[5] = p[7] = col; goto __00; + __C0: p[6] = p[7] = col; goto __00; + __D0: p[4] = p[6] = p[7] = col; goto __00; + __E0: p[5] = p[6] = p[7] = col; goto __00; + __F0: p[4] = p[5] = p[6] = p[7] = col; goto __00; + __00: + + p += size; + } + } + + dest += DEFAULT_FONT_WIDTH; + } +} + + +//------------------------------------------------------------------------- + +SDLfont::SDLfont(const char *fontfile) +{ + hfontsize = DEFAULT_FONT_HEIGHT; + hSDLfont = 0; + _last_surface = 0; + _last_text = 0; + + /*if (!fontfile) + { + hfontname = GB.System.Path(); + hfontname += "/share/gambas" GAMBAS_VERSION_STRING "/gb.sdl/" DEFAULT_FONT; + } + else + hfontname = fontfile;*/ + + if (fontfile) + { + hfontname = fontfile; + OpenFont(hfontname.c_str()); + } +} + +SDLfont::~SDLfont() +{ + GB.FreeString(&_last_text); + if (_last_surface) + _last_surface->Unref(); + if (hSDLfont) + TTF_CloseFont(hSDLfont); +} + +void SDLfont::OpenFont(const char* file) +{ + if (hSDLfont) + TTF_CloseFont(hSDLfont); + + hSDLfont = TTF_OpenFont(file, hfontsize); + + if (hSDLfont == NULL) + SDLerror::RaiseError(TTF_GetError()); +} +#if 0 +void SDLfont::SetFontName(char* name) +{ + std::string font = name; + int i=0; + + while (i= 1024) + return NULL; + + if (_last_surface) + { + if (len == GB.StringLength(_last_text) && strncmp(text, _last_text, len) == 0) + { + _last_surface->Ref(); + return _last_surface; + } + } + + if (hSDLfont) + { + SDL_Color fg = {0xFF, 0xFF, 0xFF}; + + result = TTF_RenderUTF8_Blended(hSDLfont, GB.TempString(text, len), fg); + } + else + { + int size = utf8_get_length(text, len); + + result = SDL_CreateRGBSurface(SDL_SWSURFACE, size * DEFAULT_FONT_WIDTH, DEFAULT_FONT_HEIGHT, 32, + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + #else + 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); + #endif + + if (SDL_MUSTLOCK(result)) + SDL_LockSurface(result); + render_default_font((uint *)result->pixels, size, text, len); + if (SDL_MUSTLOCK(result)) + SDL_UnlockSurface(result); + } + + GB.FreeString(&_last_text); + _last_text = GB.NewString(text, len); + + if (_last_surface) + _last_surface->Unref(); + _last_surface = new SDLsurface(result); + _last_surface->Ref(); + + return _last_surface; +} + +int SDLfont::GetDefaultFontSize() +{ + return DEFAULT_FONT_HEIGHT; +} diff --git a/gb.sdl/src/SDLfont.h b/gb.sdl/src/SDLfont.h new file mode 100644 index 00000000..f14d1b51 --- /dev/null +++ b/gb.sdl/src/SDLfont.h @@ -0,0 +1,95 @@ +/*************************************************************************** + + SDLfont.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLFONT_H +#define __SDLFONT_H + +#if 0 +#include +#include +#endif + +#include "SDL_ttf.h" + +#if 0 +#include +#include +#endif + +#include "SDLsurface.h" +#if 0 +typedef std::vector StringList; + +enum _fonttype { + SDLTTF_font = 1, + X_font}; +#endif +class SDLfont +{ +public: + SDLfont(const char* fontfile = 0); + ~SDLfont(); + +// static StringList GetFontList(void ); +// static void Init(void ); + +// void SetFontName(char* name); + void SetFontSize(int size); + void SetFontBold(bool state); + void SetFontItalic(bool state); + void SetFontStrikeout(bool state); + void SetFontUnderline(bool state); + + const char* GetFontName(void ); + int GetFontSize(void ) { return hfontsize; }; + int GetFontAscent(void ); + int GetFontDescent(void ); + + bool IsFontFixed(void ); + bool IsFontBold(void ); + bool IsFontItalic(void ); + bool IsFontStrikeout(void ); + bool IsFontUnderline(void ); + bool IsFontScalable(void ); + + void SizeText(const char* text, int len, int *width, int *height); + SDLsurface* RenderText(const char *text, int len); + + int GetScale(); + static int GetDefaultFontSize(); + +private: + void OpenFont(const char* file); + + SDLsurface *_last_surface; + char *_last_text; + + int hfontsize; + std::string hfontname; + int hSDLfontstyle; + + /* SDL_TTF font */ + TTF_Font *hSDLfont; +}; + +#endif /* _SDLFONT_H */ diff --git a/gb.sdl/src/SDLgfx.cpp b/gb.sdl/src/SDLgfx.cpp new file mode 100644 index 00000000..a6d57672 --- /dev/null +++ b/gb.sdl/src/SDLgfx.cpp @@ -0,0 +1,533 @@ +/*************************************************************************** + + SDLgfx.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLgfx.h" +#include "SDLerror.h" +#include "SDLcore.h" +#include "SDLapp.h" +#include "SDLwindow.h" +#include "SDLsurface.h" +#include "SDLtexture.h" + +#include +#include + +// for ellipses +#define PI 3.14159265359 + +// debug +// #define DEBUG_GFX + +// fill patterns +static GLubyte VertPattern[] = {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}; + +static GLubyte HoriPattern[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static GLubyte CrosPattern[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}; + +static GLubyte BdiaPattern[] = {0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, + 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x80, 0x80, 0x80, 0x80, + 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, + 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, + 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, + 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, + 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01}; + +static GLubyte DiaPattern[] = {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, + 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, + 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, + 0x80, 0x80, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, + 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, + 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, + 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80}; + +static GLubyte DiaCPattern[] = {0x81, 0x81, 0x81, 0x81, 0x42, 0x42, 0x42, 0x42, 0x24, 0x24, 0x24, 0x24, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24, 0x24, + 0x42, 0x42, 0x42, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x42, 0x42, 0x42, 0x42, 0x24, 0x24, 0x24, 0x24, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24, 0x24, 0x42, 0x42, 0x42, 0x42, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x42, 0x42, 0x42, + 0x24, 0x24, 0x24, 0x24, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x24, 0x24, 0x24, 0x24, 0x42, 0x42, 0x42, 0x42, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x42, 0x42, 0x42, 0x42, 0x24, 0x24, 0x24, 0x24, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24, 0x24, + 0x42, 0x42, 0x42, 0x42, 0x81, 0x81, 0x81, 0x81}; + +static GLubyte Dns1Pattern[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +static GLubyte Dns2Pattern[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF}; + +static GLubyte Dns3Pattern[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, + 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, + 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, + 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, + 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, + 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, + 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, + 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, + 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, + 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, + 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA}; + +static GLubyte Dns4Pattern[] = {0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA}; + +static GLubyte Dns5Pattern[] = {0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, + 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, + 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, + 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, + 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, + 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, + 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, + 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, + 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, + 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, + 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11}; + +static GLubyte Dns6Pattern[] = {0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00}; + +static GLubyte Dns7Pattern[] = {0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static void SetLinePattern(int value) +{ + GLushort pattern = 0xFFFF; + + if (value == SDL::SolidLine) + return; + if (value == SDL::DotLine) + pattern = 0xCCCC; + if (value == SDL::DashLine) + pattern = 0xAAAA; + if (value == SDL::DashDotLine) + pattern = 0xE4E4; + if (value == SDL::DashDotDotLine) + pattern = 0xF98C; + + glEnable(GL_LINE_STIPPLE); + glLineStipple(2, pattern); +} + +static void SetFillPattern(int value) +{ + if (!value) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + else + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + if (value <= SDL::SolidFill) + return; + + glEnable(GL_POLYGON_STIPPLE); + + if (value == SDL::VerticalFill) + glPolygonStipple(VertPattern); + + if (value == SDL::HorizontalFill) + glPolygonStipple(HoriPattern); + + if (value == SDL::CrossFill) + glPolygonStipple(CrosPattern); + + if (value == SDL::BackDiagFill) + glPolygonStipple(BdiaPattern); + + if (value == SDL::DiagFill) + glPolygonStipple(DiaPattern); + + if (value == SDL::DiagCrossFill) + glPolygonStipple(DiaCPattern); + + if (value == SDL::Dense1Fill) + glPolygonStipple(Dns1Pattern); + + if (value == SDL::Dense2Fill) + glPolygonStipple(Dns2Pattern); + + if (value == SDL::Dense3Fill) + glPolygonStipple(Dns3Pattern); + + if (value == SDL::Dense4Fill) + glPolygonStipple(Dns4Pattern); + + if (value == SDL::Dense5Fill) + glPolygonStipple(Dns5Pattern); + + if (value == SDL::Dense6Fill) + glPolygonStipple(Dns6Pattern); + + if (value == SDL::Dense7Fill) + glPolygonStipple(Dns7Pattern); +} + +SDLgfx::SDLgfx(SDLwindow *window) +{ + hTex = NULL; + resetGfx(); +} + +SDLgfx::SDLgfx(SDLsurface *surface) +{ + if (!SDLcore::GetWindow()) + { + SDLerror::RaiseError("Window need to be opened first !"); + return; + } + + hTex = surface->GetTexture(); + resetGfx(); +} + +void SDLgfx::resetGfx(void) +{ + hLine = SDL::SolidLine; + hLineWidth = 1; + hFill = SDL::NoFill; + rotx = roty = rotz = 0; + scalex = scaley = 1.0f; +} + +void SDLgfx::SetColor(Uint32 color) +{ + glColor4f((GLfloat((color >> 16) & 0xFF)/255), (GLfloat((color >> 8) & 0xFF)/255), (GLfloat(color & 0xFF)/255), + (GLfloat(~(color >> 24) & 0xFF)/255)); +} + +void SDLgfx::SetLineStyle(int style) +{ + if (style>SDL::DashDotDotLine) + style = SDL::DashDotDotLine; + + hLine = style; +} + +void SDLgfx::SetFillStyle(int style) +{ + if (style>SDL::Dense7Fill) + style = SDL::Dense7Fill; + + hFill = style; +} + +void SDLgfx::Clear(void) +{ + SetContext(); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void SDLgfx::DrawPixel(int x, int y) +{ + SetContext(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_POINTS); + glVertex2i(x, y); + glEnd(); + + glPopAttrib(); +} + +void SDLgfx::DrawLine(int x1, int y1, int x2, int y2) +{ + if (!hLine) // SDLgfx::NoLine + return; + + SetContext(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + SetLinePattern(hLine); + glLineWidth(GLfloat(hLineWidth)); + + glBegin(GL_LINES); + glVertex2i(x1, y1); + glVertex2i(x2, y2); + glEnd(); + + glPopAttrib(); +} + +void SDLgfx::DrawRect(int x, int y, int w, int h) +{ + if (!hFill && !hLine) + return; + + SetContext(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + SetFillPattern(hFill); + + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x+w, y); + glVertex2i(x+w, y+h); + glVertex2i(x, y+h); + glEnd(); + + if (hFill>SDL::SolidFill) + { + SetFillPattern(SDL::NoFill); + SetLinePattern(hLine); + glLineWidth(GLfloat(hLineWidth)); + + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x+w, y); + glVertex2i(x+w, y+h); + glVertex2i(x, y+h); + glEnd(); + } + + SetFillPattern(SDL::SolidFill); + glPopAttrib(); +} + +void SDLgfx::DrawEllipse(int x, int y, int w, int h) +{ + if (!hFill && !hLine) + return; + + SetContext(); + + double angle; + double step = 2 * PI / 360; + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTranslatef(x, y, 0.0f); + SetFillPattern(hFill); + + glBegin(GL_POLYGON); + for (angle=0; angle < 2 * PI; angle += step) + glVertex2d(w * cos(angle), h * sin(angle)); + glEnd(); + + if (hFill>SDL::SolidFill) + { + SetFillPattern(SDL::NoFill); + SetLinePattern(hLine); + glLineWidth(GLfloat(hLineWidth)); + glBegin(GL_POLYGON); + for (angle=0; angle < 2 * PI; angle += step) + glVertex2d(w * cos(angle), h * sin(angle)); + glEnd(); + } + + SetFillPattern(SDL::SolidFill); + glPopAttrib(); + glLoadIdentity(); +} + +void SDLgfx::Blit(SDLsurface *surface, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight, int width, int height, bool no_filter) +{ + if ((srcX > surface->GetWidth()) || (srcY > surface->GetHeight())) + return; + + if (!surface->GetWidth() || !surface->GetHeight()) + return; + + SDL_Surface *destsurf = GetDestSurface(); + + if ((x > destsurf->w) || (y > destsurf->h)) + return; + + SetContext(); + texinfo info; + + glPushAttrib(GL_ENABLE_BIT); + SDLtexture *texture = surface->GetTexture(); + texture->GetAsTexture(&info); + + GLfloat myWidth = 0, myHeight = 0; + + if ((srcHeight<0) || ((srcY + srcHeight) > surface->GetHeight())) + myHeight = surface->GetHeight() - srcY; + else + myHeight = srcHeight; + + if ((srcWidth<0) || ((srcX + srcWidth) > surface->GetWidth())) + myWidth = surface->GetWidth() - srcX; + else + myWidth = srcWidth; + + GLdouble myTexX, myTexY, myTexHeight, myTexWidth; + + myTexX = ((srcX * info.Width) / surface->GetWidth()); + myTexY = ((srcY * info.Height) / surface->GetHeight()); + myTexWidth = (((srcX + myWidth)* info.Width) / surface->GetWidth()); + myTexHeight = (((srcY + myHeight)* info.Height) / surface->GetHeight()); + + if (width != -1) + myWidth = width; + + if (height != -1) + myHeight = height; + + myWidth = myWidth / 2; + myHeight = myHeight / 2; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, info.Index); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, no_filter ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, no_filter ? GL_NEAREST : GL_LINEAR); + + glTranslatef(myWidth + x, myHeight + y, 0.0f); + glRotatef(rotz, 0, 0, 1); + glScalef(scalex, scaley, 0.0f); + + glBegin(GL_QUADS); + glTexCoord2d(myTexX, myTexY); + glVertex2f(-myWidth, -myHeight); + + glTexCoord2d(myTexX, myTexHeight); + glVertex2f(-myWidth, myHeight); + + glTexCoord2d(myTexWidth, myTexHeight); + glVertex2f(myWidth, myHeight); + + glTexCoord2d(myTexWidth, myTexY); + glVertex2f(myWidth, -myHeight); + glEnd(); + + glPopAttrib(); + glLoadIdentity(); +} + +void SDLgfx::SetContext() +{ + if (!hTex) + SDLcore::GetWindow()->Select(); + else + hTex->Select(); +} + +SDL_Surface *SDLgfx::GetDestSurface() +{ + if (!hTex) + return SDLcore::GetWindow()->GetSdlSurface(); + else + return hTex->GetSurface()->GetSdlSurface(); +} + diff --git a/gb.sdl/src/SDLgfx.h b/gb.sdl/src/SDLgfx.h new file mode 100644 index 00000000..78b11692 --- /dev/null +++ b/gb.sdl/src/SDLgfx.h @@ -0,0 +1,76 @@ +/*************************************************************************** + + SDLgfx.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLGFX_H +#define __SDLGFX_H + +#include "SDL_h.h" + +class SDLwindow; +class SDLsurface; +class SDLtexture; + +class SDLgfx +{ +public: + SDLgfx(SDLwindow* window); + SDLgfx(SDLsurface* surface); + ~SDLgfx() {}; + + void resetGfx(void ); + void SetColor(Uint32 color); + void Scale(GLfloat x, GLfloat y) { scalex = x; scaley = y; } + void Rotate(GLfloat z) { rotz = z; } + + int GetLineStyle(void ) { return hLine; } + int GetLineWidth(void ) { return hLineWidth; } + int GetFillStyle(void ) { return hFill; } + void SetLineStyle(int style); + void SetLineWidth(int width) { hLineWidth = width; } + void SetFillStyle(int style); + + void Clear(void); + void Blit(SDLsurface* s, int x, int y, int srcX = 0, int srcY = 0, int srcWidth = -1, int srcHeight = -1, int width = -1, int height = -1, bool no_filter = false); + void DrawPixel(int x, int y); + void DrawLine(int x1, int y1, int x2, int y2); + void DrawRect(int x, int y, int w, int h); + void DrawEllipse(int x, int y, int w, int h); + +private: + // hTex is null if we are drawing on a window ! + SDLtexture* hTex; + + void SetContext(void ); + SDL_Surface* GetDestSurface(void ); + + // lines and fills + int hLine, hLineWidth; + int hFill; + + // rotations & scaling + GLfloat rotx, roty, rotz; + GLfloat scalex, scaley; +}; + +#endif /* __SDLGFX_H */ + diff --git a/gb.sdl/src/SDLosrender.cpp b/gb.sdl/src/SDLosrender.cpp new file mode 100644 index 00000000..3f2993d6 --- /dev/null +++ b/gb.sdl/src/SDLosrender.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + + SDLosrender.cpp + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLosrender.h" + +#include + +/****** FBO render ******/ +bool FBOrender::hBinded = false; + +FBOrender::FBOrender() +{ + glGenFramebuffersEXT(1, &hFbo); +} + +FBOrender::~FBOrender() +{ + if (hFbo) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDeleteFramebuffersEXT(1, &hFbo); + } +} + +void FBOrender::Bind(GLuint texid) +{ + GLenum status; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, hFbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texid, 0); + + status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + std::cerr << "FBO can't be completed : "<< std::hex << status << std::endl; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, hFbo); + + FBOrender::hBinded = true; + std::cout << "FBO: binding " << hFbo << " with tex " << texid << std::endl; +} + +void FBOrender::Unbind(void ) +{ + if (!FBOrender::hBinded) + return; + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + std::cout << "FBO: unbinding " << std::endl; + FBOrender::hBinded = false; +} + +bool FBOrender::Check(void ) +{ + return (GLEW_ARB_framebuffer_object || GLEW_EXT_framebuffer_object); +} diff --git a/gb.sdl/src/SDLosrender.h b/gb.sdl/src/SDLosrender.h new file mode 100644 index 00000000..726359d3 --- /dev/null +++ b/gb.sdl/src/SDLosrender.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + SDLosrender.h + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLOSRENDER_H +#define __SDLOSRENDER_H + +#include + +class FBOrender +{ +public: + FBOrender(); + ~FBOrender(); + + void Bind(GLuint ); + + static void Unbind(void ); + static bool Check(void ); +private: + GLuint hFbo; + static bool hBinded; +}; + +#endif /* __SDLOSRENDER_H */ + diff --git a/gb.sdl/src/SDLsurface.cpp b/gb.sdl/src/SDLsurface.cpp new file mode 100644 index 00000000..caba3bed --- /dev/null +++ b/gb.sdl/src/SDLsurface.cpp @@ -0,0 +1,284 @@ +/*************************************************************************** + + SDLsurface.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include + +#include "SDLsurface.h" +#include "SDLcore.h" +#include "SDLtexture.h" + +// debugging +// #define DEBUGSURFACE + +SDLsurface::SDLsurface() +{ + hTexture = new SDLtexture(this); + hSurface = 0; + ref = 1; +} + +SDLsurface::SDLsurface(char *data, int width, int height) +{ + hTexture = new SDLtexture(this); + ref = 1; + + hSurface = SDL_CreateRGBSurfaceFrom((void *)data, width, height, 32, width * sizeof(int), +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); +#else + 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); +#endif + + if (!hSurface) + SDLcore::RaiseError(SDL_GetError()); + else + hTexture->ToLoad(); +} + +SDLsurface::SDLsurface(const SDLsurface& surf) +{ +#define cpySurf surf.hSurface + + ref = 1; + hTexture = new SDLtexture(this); + hSurface = 0; + + Create(cpySurf->w, cpySurf->h, cpySurf->format->BitsPerPixel); + + if (!hSurface->w || !hSurface->h) + return; // no surface to copy + + // now we are copying the surface + Uint32 saved_flags; + Uint8 saved_alpha; + + saved_flags = cpySurf->flags & (SDL_SRCALPHA | SDL_RLEACCELOK); + saved_alpha = cpySurf->format->alpha; + + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) + SDL_SetAlpha(cpySurf, 0, 0); + + /* Copy into the new surface */ + surf.hTexture->Sync(); + SDL_BlitSurface(cpySurf, NULL, hSurface, NULL); + + /* Restore the alpha blending attributes & set same attributes to new surface */ + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) + { + SDL_SetAlpha(cpySurf, saved_flags, saved_alpha); + SDL_SetAlpha(hSurface, saved_flags, saved_alpha); + } + +#undef cpySurf +} + +SDLsurface::SDLsurface(SDL_Surface* surf) +{ + ref = 1; + hTexture = new SDLtexture(this); + hSurface = surf; + hTexture->ToLoad(); +} + +SDLsurface::SDLsurface(int Width, int Height) +{ + ref = 1; + hTexture = new SDLtexture(this); + hSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32, + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + if (!hSurface) + SDLcore::RaiseError(SDL_GetError()); + + hTexture->ToLoad(); +} + +SDLsurface::~SDLsurface() +{ + if (hSurface) + SDL_FreeSurface(hSurface); + + if (hTexture) + delete hTexture; +} + +void SDLsurface::Create(int Width, int Height, int Depth) +{ + SDL_Surface *pSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, Depth, + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + if (!pSurface) + SDLcore::RaiseError(SDL_GetError()); + else + { + if (hSurface) + SDL_FreeSurface(hSurface); + + hSurface = pSurface; + } + hTexture->ToLoad(); +} +/* +void SDLsurface::LoadFromMem(char *addr, long len) +{ + SDL_Surface *surface = IMG_Load_RW(SDL_RWFromMem(addr, len), true); + + if (!surface) + SDLcore::RaiseError(SDL_GetError()); + + if (hSurface) + SDL_FreeSurface(hSurface); + + hSurface = surface; + hTexture->ToLoad(); + +#ifdef DEBUGSURFACE + if (hSurface->flags & SDL_SRCALPHA) + std::cout << "SDLsurface::LoadFromMem : alpha layer detected" << std::endl; +#endif +} +*/ +int SDLsurface::GetWidth() +{ + if (hSurface) + return (hSurface->w); + else + return (0); +} + +int SDLsurface::GetHeight() +{ + if (hSurface) + return (hSurface->h); + else + return (0); +} + +int SDLsurface::GetDepth() +{ + if (hSurface) + return (hSurface->format->BitsPerPixel); + else + return (0); +} + +void* SDLsurface::GetData() +{ + if (hSurface) + return (hSurface->pixels); + else + return (0); +} + +void SDLsurface::SetAlphaBuffer(bool choice) +{ + if (!hSurface) + return; + + Uint32 myflags = 0; + + if (choice) + myflags = SDL_SRCALPHA; + + if ((SDL_SetAlpha(hSurface, myflags, SDL_ALPHA_OPAQUE))<0) + SDLcore::RaiseError(SDL_GetError()); +} + +void SDLsurface::ConvertDepth(int depth) +{ + if (!hSurface) + return; + + if (hSurface->format->BitsPerPixel == depth) + return; + +#ifdef DEBUGSURFACE + std::cout << "SDLsurface::ConvertDepth : converting from " << + int(hSurface->format->BitsPerPixel) << " to " << depth << std::endl; +#endif + + SDL_Surface *tmpSurf = SDL_CreateRGBSurface(hSurface->flags, 1, 1, depth, + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + + if (!tmpSurf) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + SDL_Surface *surface = SDL_ConvertSurface(hSurface, tmpSurf->format, tmpSurf->flags); + + if (!surface) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + SDL_FreeSurface(tmpSurf); + SDL_FreeSurface(hSurface); + + hSurface = surface; + hTexture->ToLoad(); +} + +void SDLsurface::Fill(Uint32 color) +{ + if (!hSurface) + return; + + SDL_FillRect(hSurface, NULL, color); + hTexture->ToLoad(); +} + +void SDLsurface::Resize(int width, int height) +{ + if (!hSurface) + return; + + Uint32 saved_flags = hSurface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK);; + Uint8 saved_alpha = hSurface->format->alpha; + + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) + SDL_SetAlpha(hSurface, 0, 0); + + SDL_Surface *tmpSurf = SDL_CreateRGBSurface (SDL_SWSURFACE, width, height, + hSurface->format->BitsPerPixel, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + if (!tmpSurf) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + /* Copy the surface into the GL texture image */ + SDL_BlitSurface(hSurface, NULL, tmpSurf, NULL); + + /* Restore the alpha blending attributes */ + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) + SDL_SetAlpha(tmpSurf, saved_flags, saved_alpha); + + SDL_FreeSurface(hSurface); + hSurface = tmpSurf; + hTexture->ToLoad(); +} diff --git a/gb.sdl/src/SDLsurface.h b/gb.sdl/src/SDLsurface.h new file mode 100644 index 00000000..d03fa8c6 --- /dev/null +++ b/gb.sdl/src/SDLsurface.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + SDLsurface.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLSURFACE_H +#define __SDLSURFACE_H + +#include "SDL_h.h" + +class SDLtexture; + +class SDLsurface +{ +public: + SDLsurface(); + SDLsurface(const SDLsurface& surf); + SDLsurface(SDL_Surface* surf); /* SDLsurface will free surf automaticly */ + SDLsurface(int Width, int Height); + ~SDLsurface(); + + void Ref() { ref++; } + void Unref() { ref--; if (ref <= 0) delete this; } + + void Create(int Width, int Height, int Depth = 0); +// void LoadFromMem(char* addr, long len); + + int GetWidth(void ); + int GetHeight(void ); + int GetDepth(void ); + void* GetData(void ); + + SDL_Surface* GetSdlSurface(void ) { return hSurface; } + SDLtexture* GetTexture(void ) { return hTexture; }; + + void SetAlphaBuffer(bool ); + void ConvertDepth(int ); + void Fill(Uint32 color = 0); + void Resize(int width, int height); + + bool IsNull(void ) { return (hSurface ? true: false); }; + + // Compatibility // + SDLsurface(char *data, int width, int height); + int width(void) { return GetWidth(); } + int height(void) { return GetHeight(); } + unsigned char *data(void) { return (unsigned char *)GetData(); } + +private: + int ref; + SDLtexture *hTexture; + SDL_Surface *hSurface; +}; + +#endif /* __SDLSURFACE_H */ + diff --git a/gb.sdl/src/SDLtexture.cpp b/gb.sdl/src/SDLtexture.cpp new file mode 100644 index 00000000..4a3bdb89 --- /dev/null +++ b/gb.sdl/src/SDLtexture.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + + SDLtexture.cpp + + (c) 2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* manage textures and context switching if needed */ + +#include "SDLsurface.h" +#include "SDLtexture.h" +#include "SDLcore.h" + +#include +#include +#include + +SDLtexture::SDLtexture(SDLsurface *surface) +{ + hSurface = surface; + hTexinfo = new texinfo; + hTexinfo->Index = 0; + hRenderBuffer = 0; +} + +SDLtexture::~SDLtexture() +{ + if (hTexinfo->Index) + glDeleteTextures(1, &(hTexinfo->Index)); + + if (hRenderBuffer) + delete hRenderBuffer; + + delete hTexinfo; +} + +void SDLtexture::init() +{ + FBOrender::Check(); +} + +void SDLtexture::Select() +{ + if (!FBOrender::Check()) + SDLcore::RaiseError("Unable to draw on the texture, FBO not supported!"); + + /* Make sure our texture is properly loaded/synced */ + this->GetAsTexture(NULL); + + if (!hRenderBuffer) + hRenderBuffer = new FBOrender(); + + hRenderBuffer->Bind(hTexinfo->Index); + +/* glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, hSurface->GetWidth(), hSurface->GetHeight());*/ +} + +void SDLtexture::Unselect() +{ + FBOrender::Unbind(); +} + +void SDLtexture::Sync() +{ +} + +void SDLtexture::GetAsTexture(texinfo *tex) +{ + if (!hTexinfo->Index) + { + glGenTextures(1, &(hTexinfo->Index)); + hTexinfo->State = TEXTURE_TO_UPLOAD; + } + + if (hTexinfo->State & TEXTURE_TO_UPLOAD) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, hTexinfo->Index); + + SDL_Surface *image; + + if (!GLEW_ARB_texture_non_power_of_two) + { + SDL_Surface *origin = hSurface->GetSdlSurface(); + + /* Use the surface width and height expanded to powers of 2 */ + int w = this->GetPowerOfTwo(origin->w); + int h = this->GetPowerOfTwo(origin->h); + hTexinfo->Width = GLdouble(origin->w) / w; /* Max X */ + hTexinfo->Height = GLdouble(origin->h) / h; /* Max Y */ + + image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, +// #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */ +// 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 +// #else +// 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF +// #endif + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF + ); + + if (!image) + { + std::cerr << __FILE__ << ":" << __LINE__ << ": Failed to create SDL_Surface() !" << std::endl; + return; + } + + /* Save the alpha blending attributes */ + Uint32 saved_flags = origin->flags & (SDL_SRCALPHA | SDL_RLEACCELOK); + Uint8 saved_alpha = origin->format->alpha; + + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) + SDL_SetAlpha(origin, 0, 0); + + /* Copy the surface into the GL texture image */ + SDL_BlitSurface(origin, NULL, image, NULL); + + /* Restore the alpha blending attributes */ + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) + SDL_SetAlpha(origin, saved_flags, saved_alpha); + } + else + { + hTexinfo->Width = 1.0; + hTexinfo->Height = 1.0; + image = hSurface->GetSdlSurface(); + } + +// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_BGRA, + GL_UNSIGNED_BYTE, image->pixels); + /* Use of CLAMP_TO_EDGE, without it give a black line around the texture + with DRI radeon drivers */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (!GLEW_ARB_texture_non_power_of_two) + SDL_FreeSurface(image); /* No longer needed */ + + hTexinfo->State = TEXTURE_OK; + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + } + + if (tex) + std::memcpy(tex, hTexinfo, sizeof(texinfo)); +} + +int SDLtexture::GetPowerOfTwo(int size) +{ + int value = 1; + + while ( value < size ) + value <<= 1; + + return value; +} + diff --git a/gb.sdl/src/SDLtexture.h b/gb.sdl/src/SDLtexture.h new file mode 100644 index 00000000..ca939ec9 --- /dev/null +++ b/gb.sdl/src/SDLtexture.h @@ -0,0 +1,73 @@ +/*************************************************************************** + + SDLtexture.h + + (c) 2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* manage transparently window/surfaces as texture */ + +#ifndef __SDLTEXTURE_H +#define __SDLTEXTURE_H + +#include "SDL_h.h" +#include "SDLosrender.h" + +// internal texture status values +#define TEXTURE_OK (0) +#define TEXTURE_TO_UPLOAD (1) +#define TEXTURE_TO_DOWNLOAD (2) + +class SDLwindow; +class SDLsurface; + +typedef struct { + GLuint Index; + GLdouble Width, Height; + GLint RealWidth, RealHeight; + Uint8 State; + } + texinfo; + +class SDLtexture +{ +public: + SDLtexture(SDLsurface* surface); + ~SDLtexture(); + + static void init(void ); + void Select(void ); + static void Unselect(void ); + SDLsurface* GetSurface(void ) { return hSurface; }; + void Sync(void ); + + void ToLoad(void ) { hTexinfo->State = TEXTURE_TO_UPLOAD; }; + + Uint8 GetStatus(void ) { return hTexinfo->State; }; + void GetAsTexture(texinfo *); + + static int GetPowerOfTwo(int size); +private: + SDLsurface *hSurface; + texinfo *hTexinfo; + FBOrender *hRenderBuffer; +}; + +#endif /* __SDLTEXTURE_H */ + diff --git a/gb.sdl/src/SDLwindow.cpp b/gb.sdl/src/SDLwindow.cpp new file mode 100644 index 00000000..70223e2e --- /dev/null +++ b/gb.sdl/src/SDLwindow.cpp @@ -0,0 +1,276 @@ +/*************************************************************************** + + SDLwindow.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLwindow.h" +#include "SDLcore.h" +#include "SDLtexture.h" + +#include + +SDLwindow::SDLwindow(void ) +{ + hSurface = 0; + hCursor = new SDLcursor(); + hX = hY = 0; + hWidth = 640; + hHeight = 480; + hFullScreen = false; + hResizable = true; + hTitle = "Gambas SDL application"; +} + +SDLwindow::~SDLwindow() +{ + this->Close(); + delete hCursor; +} + +void SDLwindow::Show() +{ + Uint32 myFlags = (SDL_ASYNCBLIT | SDL_DOUBLEBUF | SDL_OPENGL); + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 ); + + if (hFullScreen) + myFlags = myFlags | SDL_FULLSCREEN; + + if (hResizable) + myFlags = myFlags | SDL_RESIZABLE; + + hSurface = SDL_SetVideoMode(hWidth, hHeight , 0, myFlags); + + if (!hSurface) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + GLenum err = glewInit(); + if (GLEW_OK != err) + { + /* Problem: glewInit failed, something is seriously wrong. */ + SDLcore::RaiseError((char *)glewGetErrorString(err)); + return; + } + + hCtx = glXGetCurrentContext(); + hDrw = glXGetCurrentDrawable(); + hDpy = glXGetCurrentDisplay(); + + /* Set our custom cursor */ + hCursor->Show(SDLapp->CurrentWin()); + + SDL_WM_SetCaption(hTitle.c_str(), hTitle.c_str()); + + if (SDLcore::GetWindow() != this) + SDLcore::RegisterWindow(this); + +// GL::init(); + SDLtexture::init(); + + Clear(); + Open(); + +} + +void SDLwindow::Close() +{ + if (!hSurface) + return; + + GrabInput(false); + SDLcore::RegisterWindow(NULL); + hSurface = NULL; +} + +void SDLwindow::Refresh() +{ + if (!hSurface) + return; + + SDL_GL_SwapBuffers(); +} + +void SDLwindow::Select(void ) +{ + if (!hSurface) + return; + + if ((glXGetCurrentContext()!=hCtx) && (glXGetCurrentDrawable()!=hDrw)) + { + std::cout << "Set window current with glXMakeCurrent()" << std::endl; + glXMakeCurrent(hDpy, hDrw, hCtx); + } + else + { + SDLtexture::Unselect(); +/* this->Open();*/ + } +} + +void SDLwindow::Clear(Uint32 color) +{ + if (!hSurface) + return; + + glClearColor((GLfloat((color >> 16) & 0xFF)/255), (GLfloat((color >> 8) & 0xFF)/255), + (GLfloat((color & 0xFF)/255)), (GLfloat(~(color >> 24) & 0xFF)/255)); + glClear(GL_COLOR_BUFFER_BIT); + +} + +Uint32 SDLwindow::Id(void ) +{ + if (!hSurface) + return 0; + + return (SDLapp->CurrentWin()); +} + +int SDLwindow::GetWidth() +{ + if (hSurface) + return (hSurface->w); + else + return (hWidth); +} + +int SDLwindow::GetHeight() +{ + if (hSurface) + return (hSurface->h); + else + return (hHeight); +} + +int SDLwindow::GetDepth() +{ + if (hSurface) + return (hSurface->format->BitsPerPixel); + else + return (0); +} + +void* SDLwindow::GetData() +{ + if (hSurface) + return (hSurface->pixels); + else + return (0); +} + +void SDLwindow::SetCursorShape(int shape) +{ + if (!hCursor) + return; + + hCursor->SetShape(shape); + + if (this->IsShown()) + hCursor->Show(SDLapp->CurrentWin()); +} + +void SDLwindow::SetWidth(int width) +{ + hWidth = width; + + if (hSurface) + this->Show(); +} + +void SDLwindow::SetHeight(int height) +{ + hHeight = height; + + if (hSurface) + this->Show(); +} + +void SDLwindow::SetFullScreen(bool choice) +{ + if ((choice && !hFullScreen) || (!choice && hFullScreen)) + { + if (hSurface) + { + if (!SDL_WM_ToggleFullScreen(hSurface)) + SDLcore::RaiseError(SDL_GetError()); + } + hFullScreen = !hFullScreen; + } +} + +void SDLwindow::SetResizable(bool choice) +{ + if (!hSurface) + { + hResizable = choice; + return; + } + + if (((hSurface->flags & SDL_RESIZABLE) && !choice) || (!(hSurface->flags & SDL_RESIZABLE) && choice)) + { + hResizable = choice; + this->Show(); + } +} + +void SDLwindow::SetTitle(char *title) +{ + hTitle = title; + + if (hSurface) + SDL_WM_SetCaption(title, title); +} +/* +void SDLwindow::SetCursor(SDLcursor *cursor) +{ + if (hCursor) + delete hCursor; + + SDLcursor *curs=new SDLcursor(*cursor); + hCursor = curs; + + if (this->IsShown()) + hCursor->Show(); +} +*/ +bool SDLwindow::IsShown() +{ + if (!hSurface) + return (false); + + if (SDLcore::GetWindow() == this) + return (true); + else + return (false); +} + +void SDLwindow::GrabInput(bool grab) +{ + SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF); +} + +bool SDLwindow::IsInputGrabbed(void) const +{ + return SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON; +} diff --git a/gb.sdl/src/SDLwindow.h b/gb.sdl/src/SDLwindow.h new file mode 100644 index 00000000..0c77831f --- /dev/null +++ b/gb.sdl/src/SDLwindow.h @@ -0,0 +1,105 @@ +/*************************************************************************** + + SDLwindow.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLWINDOW_H +#define __SDLWINDOW_H + +#include "SDL_h.h" +#include "SDLcursor.h" + +#include + +class SDLwindow +{ +public: + SDLwindow(void ); + virtual ~SDLwindow(); + + void Show(void ); + void Close(void ); + void Refresh(); + void Select(void ); + void Clear(Uint32 color = 0); + Uint32 Id(void ); + + int GetWidth(void ); + int GetHeight(void ); + int GetDepth(void ); + void* GetData(void ); + + void Lock(void) { if (hSurface) SDL_LockSurface(hSurface); } + void Unlock(void) { if (hSurface) SDL_UnlockSurface(hSurface); } + + const char* GetTitle(void ) { return (hTitle.c_str()); }; + SDL_Surface* GetSdlSurface(void ) { return (hSurface); }; + SDLcursor* GetCursor(void ) { return (hCursor); } + int GetCursorShape(void ) { return (hCursor->GetShape()); } + + void SetX(int ); + void SetY(int ); + void SetWidth(int ); + void SetHeight(int ); + void SetFullScreen(bool ); + void SetResizable(bool ); + void SetTitle(char* ); + + void SetCursor(SDLcursor* ); + void SetCursorShape(int ); + + bool IsFullScreen(void ) {return (hFullScreen); }; + bool IsResizable(void ) { return (hResizable); }; + bool IsShown(void ); + + void GrabInput(bool grab); + bool IsInputGrabbed(void) const; + + // events :) + virtual void Quit(void ) = 0; + virtual void Resize(void ) = 0; + virtual void GotFocus(void ) = 0; + virtual void LostFocus(void ) = 0; + virtual void MouseEnter(void ) = 0; + virtual void MouseLeave(void ) = 0; + virtual void Update(void ) = 0; + virtual void JoyEvent(SDL_Event& ) = 0; + virtual void KeyEvent(SDL_KeyboardEvent* , int ) = 0; + virtual void MouseButtonEvent(SDL_MouseButtonEvent* ) = 0; + virtual void MouseMotionEvent(SDL_MouseMotionEvent* ) = 0; + virtual void Open(void ) =0; + +private: + SDL_Surface *hSurface; + SDLcursor *hCursor; + int hX, hY; + int hWidth, hHeight; + bool hFullScreen; + bool hResizable; + std::string hTitle; + // context and drawable (for GlxMakeCurrent) + GLXContext hCtx; + GLXDrawable hDrw; + Display *hDpy; +}; + +#endif /* __SDLWINDOW_H */ + diff --git a/gb.sdl/src/default_font.h b/gb.sdl/src/default_font.h new file mode 100644 index 00000000..daaa86af --- /dev/null +++ b/gb.sdl/src/default_font.h @@ -0,0 +1,632 @@ +#define DEFAULT_FONT_WIDTH 7 +#define DEFAULT_FONT_HEIGHT 13 +#define DEFAULT_FONT_ASCENT 10 +#define DEFAULT_FONT_DESCENT 3 + +const char _default_font_33_126[] = { + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x1C, 0x28, 0x28, 0x1E, 0x08, 0x08, 0x00, + 0x00, 0x02, 0x15, 0x12, 0x08, 0x08, 0x04, 0x04, 0x12, 0x2A, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x0C, 0x04, 0x2A, 0x11, 0x11, 0x2E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00, + 0x00, 0x04, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x0C, 0x0A, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x18, 0x14, 0x12, 0x11, 0x3F, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x10, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x39, 0x25, 0x25, 0x25, 0x19, 0x02, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x00, + 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1C, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x04, 0x04, 0x18, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x00, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x00, + 0x00, 0x24, 0x2A, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const char _default_font_160_687[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x3C, 0x08, 0x08, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x04, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x02, 0x04, 0x0A, 0x12, 0x14, 0x08, 0x10, 0x0E, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x59, 0x45, 0x45, 0x45, 0x59, 0x22, 0x1C, 0x00, 0x00, + 0x0C, 0x10, 0x1C, 0x12, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x09, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x4D, 0x55, 0x4D, 0x55, 0x55, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x10, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x3C, 0x2E, 0x2E, 0x2E, 0x2C, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x06, + 0x04, 0x06, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x24, 0x12, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x1C, 0x20, 0x18, 0x04, 0x3C, + 0x07, 0x08, 0x06, 0x08, 0x07, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x04, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x04, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x04, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x04, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1A, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x0C, 0x30, 0x18, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x14, 0x08, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x70, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x10, 0x60, + 0x14, 0x08, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x22, 0x1C, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x06, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x7F, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0F, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x30, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x08, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x37, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x24, 0x24, 0x00, 0x36, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x20, 0x18, + 0x10, 0x28, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x10, 0x28, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x10, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x0C, + 0x14, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x1A, 0x1A, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x34, 0x34, 0x04, 0x04, 0x04, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x06, 0x03, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x18, 0x0C, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x08, 0x06, + 0x14, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x02, 0x02, 0x01, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x39, 0x09, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x29, 0x29, 0x19, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x0C, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x0C, + 0x14, 0x08, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x0E, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x07, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x25, 0x24, 0x24, 0x1C, 0x24, 0x24, 0x24, 0x1C, 0x00, 0x00, + 0x00, 0x1E, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x03, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x0E, 0x15, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x14, 0x0C, 0x00, 0x00, + 0x00, 0x3C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x14, 0x08, 0x10, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, + 0x00, 0x40, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x06, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x60, 0x10, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x02, 0x62, 0x12, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x1E, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x34, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x04, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x26, 0x20, 0x20, + 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x20, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x08, + 0x00, 0x00, 0x3E, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x3E, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x18, 0x20, 0x20, 0x1E, 0x01, 0x06, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x1E, 0x02, 0x04, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x28, 0x10, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x13, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x14, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1F, 0x00, 0x00, + 0x00, 0x20, 0x21, 0x01, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2F, 0x20, 0x18, + 0x00, 0x20, 0x26, 0x04, 0x34, 0x24, 0x24, 0x24, 0x24, 0x24, 0x2E, 0x20, 0x18, + 0x00, 0x00, 0x29, 0x29, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x19, 0x00, 0x00, + 0x00, 0x20, 0x29, 0x09, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x00, 0x20, 0x20, 0x00, 0x37, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x72, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x72, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x08, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x02, 0x02, + 0x04, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x28, 0x28, 0x3E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x08, 0x24, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x10, 0x08, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x08, 0x04, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x06, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x14, 0x2C, 0x18, 0x08, 0x08, + 0x00, 0x00, 0x03, 0x05, 0x05, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1D, 0x08, 0x08, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x08, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x10, 0x1C, 0x32, 0x32, 0x2A, 0x3E, 0x2A, 0x2A, 0x26, 0x26, 0x04, 0x00, + 0x00, 0x10, 0x3C, 0x12, 0x12, 0x0A, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x3C, 0x12, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x28, 0x18, 0x08, 0x0C, 0x0A, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x04, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x04, 0x18, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x27, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x14, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x3E, 0x12, 0x12, 0x0A, 0x1E, 0x0A, 0x0A, 0x06, 0x3E, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1C, 0x32, 0x2A, 0x3E, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1F, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x0F, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1A, 0x26, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x40, + 0x40, 0x20, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x18, 0x24, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x10, 0x0C, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x40, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x14, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x18, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x2C, 0x2A, 0x1A, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x0C, 0x0A, 0x3C, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x03, 0x02, 0x3E, 0x22, 0x12, 0x0A, 0x12, 0x22, 0x22, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x09, 0x09, 0x39, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x02, 0x0C, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x20, 0x18, 0x10, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3C, 0x0A, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x10, 0x10, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x34, 0x4A, 0x3E, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x3C, 0x22, 0x5C, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x10, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2C, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x3C, 0x12, 0x0C, + 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x25, 0x15, 0x25, 0x25, 0x26, 0x20, 0x18, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x35, 0x5D, 0x3E, 0x10, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x11, 0x21, 0x21, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x21, 0x11, 0x17, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E, 0x10, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x09, 0x19, 0x29, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x06, 0x01, 0x1D, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x20, 0x10, + 0x00, 0x00, 0x03, 0x02, 0x32, 0x0A, 0x0A, 0x12, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x02, 0x3A, 0x22, 0x12, 0x12, 0x12, 0x0A, 0x3C, 0x00, 0x00, + 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, +}; diff --git a/gb.sdl/src/gb.sdl.component b/gb.sdl/src/gb.sdl.component new file mode 100644 index 00000000..5de72c24 --- /dev/null +++ b/gb.sdl/src/gb.sdl.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.sdl +Author=Laurent Carlier +Implements=EventLoop,OpenGLViewer +Requires=gb.image,gb.image.io +State=Deprecated diff --git a/gb.sdl/src/main.cpp b/gb.sdl/src/main.cpp new file mode 100644 index 00000000..c8abfbd8 --- /dev/null +++ b/gb.sdl/src/main.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + + main.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "gambas.h" +#include "main.h" + +#include +#include "SDL_h.h" +#include "SDL_syswm.h" + +#include "SDLapp.h" +#include "SDLcore.h" + +#include "Cconst.h" +#include "Cdesktop.h" +#include "Cjoystick.h" +#include "Ckey.h" +#include "Cmouse.h" +#include "Cimage.h" +#include "Cdraw.h" +#include "Cwindow.h" +#include "Cfont.h" + +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_Font; + +class mySDLapp : public SDLapplication +{ +public: + mySDLapp(int &argc, char **argv):SDLapplication(argc, argv) {}; + virtual ~mySDLapp() {}; + + virtual void ManageError(const char *myError); +}; + +mySDLapp *myApp = NULL; + +static void my_main(int *argc, char **argv); +static int my_loop(void ); +static void my_wait(int duration); + +extern "C" +{ + GB_INTERFACE GB EXPORT; + IMAGE_INTERFACE IMAGE EXPORT; + + GB_DESC *GB_CLASSES[] EXPORT = + { + CLine, CFill, + CFont, + CDesktop, + CJoyInfos, CQueryJoys, CJoystick, + CKey, + CMouse, // CCursor, + CImage, + CDraw, + CWindow, + + NULL + }; + + int EXPORT GB_INIT(void) + { + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + IMAGE.SetDefaultFormat(GB_IMAGE_BGRA); + + GB.Hook(GB_HOOK_MAIN, (void *)my_main); + GB.Hook(GB_HOOK_LOOP, (void *)my_loop); + GB.Hook(GB_HOOK_WAIT, (void *)my_wait); + + CLASS_Window = GB.FindClass("Window"); + CLASS_Image = GB.FindClass("Image"); + CLASS_Font = GB.FindClass("Font"); + + return -1; + } + + void EXPORT GB_EXIT() + { + delete myApp; + } + + void EXPORT GB_SIGNAL(int signal, void *param) + { + static bool wasFullscreen = false; + + if (!SDLcore::GetWindow()) + return; + + if ((signal == GB_SIGNAL_DEBUG_BREAK) || (signal == GB_SIGNAL_DEBUG_CONTINUE)) + { + if (SDLcore::GetWindow()->IsFullScreen()) + { + wasFullscreen = true; + SDLcore::GetWindow()->SetFullScreen(false); + } + } + + if (signal == GB_SIGNAL_DEBUG_CONTINUE) + { + if (wasFullscreen) + SDLcore::GetWindow()->SetFullScreen(true); + } + } +} + +void mySDLapp::ManageError(const char *myError) +{ + GB.Error(COMP_ERR myError); +} + +static void my_main(int *argc, char **argv) +{ + myApp = new mySDLapp(*argc, argv); +} + +static int my_loop() +{ + while(myApp->HaveWindows()) + { + myApp->ManageEvents(); + GB.Loop(10); + } + + return 1; +} + +static void my_wait(int duration) +{ + myApp->ManageEvents(duration == 0); + GB.Loop(10); +} diff --git a/gb.sdl/src/main.h b/gb.sdl/src/main.h new file mode 100644 index 00000000..4552f2b6 --- /dev/null +++ b/gb.sdl/src/main.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + main.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.image.h" + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; + +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_Font; +#endif + + +#endif /* __MAIN_H */ + diff --git a/gb.sdl2/AUTHORS b/gb.sdl2/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/COPYING b/gb.sdl2/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.sdl2/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.sdl2/ChangeLog b/gb.sdl2/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/INSTALL b/gb.sdl2/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.sdl2/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.sdl2/Makefile.am b/gb.sdl2/Makefile.am new file mode 100644 index 00000000..f81e7575 --- /dev/null +++ b/gb.sdl2/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SDL2_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.sdl2/NEWS b/gb.sdl2/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/README b/gb.sdl2/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/acinclude.m4 b/gb.sdl2/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.sdl2/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.sdl2/component.am b/gb.sdl2/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.sdl2/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.sdl2/configure.ac b/gb.sdl2/configure.ac new file mode 100644 index 00000000..19bcfa02 --- /dev/null +++ b/gb.sdl2/configure.ac @@ -0,0 +1,27 @@ +dnl ---- configure.ac for gb.sdl + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-sdl2],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.sdl2) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + sdl2, SDL2, gb.sdl2, [src], + 'sdl2 >= 2.0.2' 'SDL2_image >= 2.0.0' 'SDL2_ttf >= 2.0.12' +) + +GB_COMPONENT_PKG_CONFIG( + sdl2audio, SDL2AUDIO, gb.sdl2.audio, [audio], + 'sdl2 >= 2.0.2' 'SDL2_mixer >= 2.0.0' +) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +src/audio/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.sdl2/gambas.h b/gb.sdl2/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.sdl2/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.sdl2/gb.geom.h b/gb.sdl2/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.sdl2/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.sdl2/gb.image.h b/gb.sdl2/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.sdl2/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.sdl2/gb_common.h b/gb.sdl2/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.sdl2/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.sdl2/gb_list.h b/gb.sdl2/gb_list.h new file mode 120000 index 00000000..cfd35489 --- /dev/null +++ b/gb.sdl2/gb_list.h @@ -0,0 +1 @@ +../main/share/gb_list.h \ No newline at end of file diff --git a/gb.sdl2/gb_list_temp.h b/gb.sdl2/gb_list_temp.h new file mode 120000 index 00000000..0bd26c39 --- /dev/null +++ b/gb.sdl2/gb_list_temp.h @@ -0,0 +1 @@ +../main/share/gb_list_temp.h \ No newline at end of file diff --git a/gb.sdl2/m4 b/gb.sdl2/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.sdl2/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.sdl2/reconf b/gb.sdl2/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.sdl2/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.sdl2/src/Makefile.am b/gb.sdl2/src/Makefile.am new file mode 100644 index 00000000..0b44a74c --- /dev/null +++ b/gb.sdl2/src/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.sdl2 +include $(top_srcdir)/component.am + +SUBDIRS = . @SDL2AUDIO_DIR@ + +gblib_LTLIBRARIES = gb.sdl2.la + +gb_sdl2_la_LIBADD = @SDL2_LIB@ libfont.la +gb_sdl2_la_LDFLAGS = -module @LD_FLAGS@ @SDL2_LDFLAGS@ +gb_sdl2_la_CFLAGS = $(AM_CFLAGS) +gb_sdl2_la_CPPFLAGS = @SDL2_INC@ + +gb_sdl2_la_SOURCES = \ + c_image.h c_image.c \ + c_window.h c_window.c \ + c_draw.h c_draw.c \ + c_mouse.h c_mouse.c \ + c_key.h c_key.c \ + c_font.h c_font.c \ + main.h main.c + +noinst_LTLIBRARIES = libfont.la + +libfont_la_CPPFLAGS = @SDL2_INC@ +libfont_la_CFLAGS = $(AM_CFLAGS_OPT) + +libfont_la_SOURCES = \ + default_font_data.h default_font.h default_font.c + diff --git a/gb.sdl2/src/audio/Makefile.am b/gb.sdl2/src/audio/Makefile.am new file mode 100644 index 00000000..d8b375b9 --- /dev/null +++ b/gb.sdl2/src/audio/Makefile.am @@ -0,0 +1,15 @@ +COMPONENT = gb.sdl2.audio +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.sdl2.audio.la + +gb_sdl2_audio_la_LIBADD = @SDL2AUDIO_LIB@ +gb_sdl2_audio_la_LDFLAGS = -module @LD_FLAGS@ @SDL2AUDIO_LDFLAGS@ +gb_sdl2_audio_la_CFLAGS = $(AM_CFLAGS) +gb_sdl2_audio_la_CPPFLAGS = @SDL2AUDIO_INC@ + +gb_sdl2_audio_la_SOURCES = \ + c_sound.h c_sound.c \ + c_music.h c_music.c \ + c_channel.h c_channel.c \ + main.h main.c diff --git a/gb.sdl2/src/audio/c_channel.c b/gb.sdl2/src/audio/c_channel.c new file mode 100644 index 00000000..2b9d3d18 --- /dev/null +++ b/gb.sdl2/src/audio/c_channel.c @@ -0,0 +1,411 @@ +/*************************************************************************** + + c_channel.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_CHANNEL_C + +#include "c_channel.h" + +#define THIS ((CCHANNEL *)_object) + +DECLARE_EVENT(EVENT_Finish); + +static CCHANNEL *_cache[MAX_CHANNEL] = { 0 }; +static int _count = 0; + +static int _pipe[2]; +static int _pipe_usage = 0; + +//------------------------------------------------------------------------- + +static void free_channel(CCHANNEL *ch) +{ + if (!ch->sound) + return; + + GB.Unref(POINTER(&ch->sound)); + ch->sound = NULL; + ch->free = FALSE; + + _pipe_usage--; + if (_pipe_usage == 0) + { + //fprintf(stderr, "stop watch\n"); + GB.Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + } +} + +static void free_finished_channel(void) +{ + //int i; + CCHANNEL *ch; + char channel; + + if (read(_pipe[0], &channel, 1) != 1) + return; + + ch = _cache[(int)channel]; + if (ch) + { + if (ch->free) + free_channel(ch); + //fprintf(stderr, "raise finish %d\n", (int)channel); + GB.Raise(ch, EVENT_Finish, 0); + } + + /*for (i = 0; i < MAX_CHANNEL; i++) + { + ch = channel_cache[i]; + if (ch && ch->free) + free_channel(ch); + }*/ +} + +static void channel_finished_cb(int channel) +{ + CCHANNEL *ch = _cache[channel]; + char buf = (char)channel; + + if (!ch) + return; + + ch->free = (write(_pipe[1], &buf, 1) == 1); + //fprintf(stderr, "finish %d (%d)\n", channel, ch->free); +} + +static int find_free_channel(void) +{ + int i; + + for (i = 0; i < MAX_CHANNEL; i++) + { + if (!_cache[i]) + return i; + } + + return -1; +} + +void CHANNEL_return(int channel, CSOUND *sound) +{ + CCHANNEL *ch = NULL; + + + if (channel < 0 || channel >= _count) + { + if (sound) + GB.Unref(POINTER(&sound)); + + GB.ReturnNull(); + return; + } + + CHECK_AUDIO(); + + ch = _cache[channel]; + if (!ch) + { + ch = GB.New(CLASS_Channel, NULL, NULL); + _cache[channel] = ch; + ch->channel = channel; + //GB.Ref(ch); + } + + //free_channel(ch); + if (sound) + { + GB.Unref(POINTER(&ch->sound)); + ch->sound = sound; + } + + GB.ReturnObject(ch); +} + +bool CHANNEL_init(void) +{ + if (pipe(_pipe)) + { + GB.Error("Unable to initialize channel pipe"); + return TRUE; + } + + _count = Mix_AllocateChannels(-1); + + Mix_ChannelFinished(channel_finished_cb); + return FALSE; +} + +int CHANNEL_play_sound(int channel, CSOUND *sound, int loops, int fadein) +{ + _pipe_usage++; + if (_pipe_usage == 1) + { + //fprintf(stderr, "watch pipe\n"); + GB.Watch(_pipe[0], GB_WATCH_READ, (void *)free_finished_channel, 0); + } + + if (fadein > 0) + return Mix_FadeInChannel(channel, sound->chunk, loops, fadein); + else + return Mix_PlayChannel(channel, sound->chunk, loops); +} + +void CHANNEL_exit() +{ + int i; + CCHANNEL *ch; + + Mix_HaltChannel(-1); + + for (i = 0; i < MAX_CHANNEL; i++) + { + ch = _cache[i]; + if (!ch) + continue; + + free_channel(ch); + GB.Unref(POINTER(&_cache[i])); + } + + if (_pipe_usage) + { + GB.Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + _pipe_usage = 0; + } + + close(_pipe[0]); + close(_pipe[1]); +} + +static void update_channel_effect(CCHANNEL *_object) +{ + if (Mix_SetPosition(THIS->channel, THIS->angle, THIS->distance) == 0) + GB.Error("Unable to set effect: &1", Mix_GetError()); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Channels_get, GB_INTEGER index) + + CHANNEL_return(VARG(index), NULL); + +END_METHOD + +BEGIN_PROPERTY(Channels_Count) + + int nchan; + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_AllocateChannels(-1)); + else + { + nchan = VPROP(GB_INTEGER); + if (nchan < 0 || nchan > MAX_CHANNEL) + { + GB.Error(GB_ERR_ARG); + return; + } + + Mix_AllocateChannels(nchan); + _count = Mix_AllocateChannels(-1); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channels_Volume) + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_Volume(-1, -1)); + else + Mix_Volume(-1, VPROP(GB_INTEGER)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Channel_new) + + int channel = find_free_channel(); + + if (channel < 0) + { + GB.Error("No more channel available"); + return; + } + + THIS->channel = channel; + _cache[channel] = THIS; + GB.Ref(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Channel_exit) + + CHANNEL_exit(); + +END_METHOD + +BEGIN_METHOD(Channel_Play, GB_OBJECT sound; GB_INTEGER loops; GB_FLOAT fadein) + + CSOUND *sound; + + if (Mix_Paused(THIS->channel)) + Mix_Resume(THIS->channel); + + sound = VARGOPT(sound, NULL); + if (!sound) + return; + + while (THIS->sound) + { + Mix_HaltChannel(THIS->channel); + GB.Loop(10); + } + + GB.Ref(sound); + THIS->sound = sound; + CHANNEL_play_sound(THIS->channel, sound, VARGOPT(loops, 0), (int)(VARGOPT(fadein, 0) * 1000)); + +END_METHOD + + +BEGIN_METHOD_VOID(Channel_Pause) + + Mix_Pause(THIS->channel); + +END_METHOD + + +BEGIN_METHOD(Channel_Stop, GB_FLOAT fadeout) + + if (MISSING(fadeout)) + Mix_HaltChannel(THIS->channel); + else + Mix_FadeOutChannel(THIS->channel, (int)(VARG(fadeout) * 1000)); + +END_METHOD + +BEGIN_PROPERTY(Channel_Volume) + + int channel = THIS->channel; + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_Volume(channel, -1)); + else + Mix_Volume(channel, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Distance) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->distance); + else + { + int d = VPROP(GB_INTEGER); + + if (d < 0 || d > 255) + { + GB.Error(GB_ERR_ARG); + return; + } + + THIS->distance = d; + update_channel_effect(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Angle) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->angle); + else + { + THIS->angle = VPROP(GB_INTEGER); + update_channel_effect(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Reverse) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->reverse); + else + { + bool v = VPROP(GB_BOOLEAN); + if (Mix_SetReverseStereo(THIS->channel, v)) + THIS->reverse = v; + else + GB.Error(Mix_GetError()); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Index) + + GB.ReturnInteger(THIS->channel); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC ChannelsDesc[] = +{ + GB_DECLARE_STATIC("Channels"), + + GB_STATIC_METHOD("_get", "Channel", Channels_get, "(Index)i"), + //GB_STATIC_METHOD("_next", "Channel", CCHANNEL_next, NULL), + + GB_STATIC_PROPERTY("Count", "i", Channels_Count), + GB_STATIC_PROPERTY("Volume", "i", Channels_Volume), + + GB_END_DECLARE +}; + +GB_DESC ChannelDesc[] = +{ + GB_DECLARE("Channel", sizeof(CCHANNEL)), + + GB_METHOD("_new", NULL, Channel_new, NULL), + + GB_STATIC_METHOD("_exit", NULL, Channel_exit, NULL), + GB_METHOD("Play", NULL, Channel_Play, "[(Sound)Sound;(Loops)i(FadeIn)f]"), + GB_METHOD("Pause", NULL, Channel_Pause, NULL), + GB_METHOD("Stop", NULL, Channel_Stop, "[(FadeOut)f]"), + + GB_PROPERTY_READ("Index", "i", Channel_Index), + GB_PROPERTY("Volume", "i", Channel_Volume), + GB_PROPERTY("Distance", "i", Channel_Distance), + GB_PROPERTY("Angle", "i", Channel_Angle), + GB_PROPERTY("Reverse", "b", Channel_Reverse), + + GB_EVENT("Finish", NULL, NULL, &EVENT_Finish), + + GB_END_DECLARE +}; + diff --git a/gb.sdl2/src/audio/c_channel.h b/gb.sdl2/src/audio/c_channel.h new file mode 100644 index 00000000..9bde0953 --- /dev/null +++ b/gb.sdl2/src/audio/c_channel.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + c_channel.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_CHANNEL_H +#define __C_CHANNEL_H + +#include "main.h" +#include "c_sound.h" + +typedef + struct { + GB_BASE ob; + int channel; + CSOUND *sound; + uchar distance; + ushort angle; + unsigned reverse : 1; + unsigned free : 1; + } + CCHANNEL; + +#ifndef __C_CHANNEL_C +extern GB_DESC ChannelDesc[]; +extern GB_DESC ChannelsDesc[]; +#endif + +#define MAX_CHANNEL 64 + +bool CHANNEL_init(void); +void CHANNEL_exit(void); +int CHANNEL_play_sound(int channel, CSOUND *sound, int loops, int fadein); +void CHANNEL_return(int channel, CSOUND *sound); + +#endif /* __C_CHANNEL_H */ + diff --git a/gb.sdl2/src/audio/c_music.c b/gb.sdl2/src/audio/c_music.c new file mode 100644 index 00000000..c2a47b2b --- /dev/null +++ b/gb.sdl2/src/audio/c_music.c @@ -0,0 +1,241 @@ +/*************************************************************************** + + c_music.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MUSIC_C + +#include "c_music.h" + +static Mix_Music *_music = NULL; +static double _ref_time = 0; +static double _ref_pos = 0; +static int _volume = MIX_MAX_VOLUME; + +//------------------------------------------------------------------------- + +static double get_music_pos(void) +{ + double time; + + if (Mix_PlayingMusic()) + { + if (!Mix_PausedMusic()) + { + GB.GetTime(&time, FALSE); + return _ref_pos + time - _ref_time; + } + else + return _ref_pos; + } + else + return 0; +} + +void MUSIC_exit(void) +{ + if (!_music) + return; + + Mix_HaltMusic(); + Mix_RewindMusic(); + Mix_FreeMusic(_music); + _music = NULL; +} + +static void update_volume(void) +{ + if (!Mix_PlayingMusic()) + return; + + Mix_VolumeMusic(_volume); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Music_Load, GB_STRING path) + + CHECK_AUDIO(); + + MUSIC_exit(); + + // Note that the music cannot be stored inside the project. + + _music = Mix_LoadMUS(GB.RealFileName(STRING(path), LENGTH(path))); + if (!_music) + { + GB.Error(Mix_GetError()); + return; + } + + _ref_pos = 0; + _ref_time = 0; + +END_METHOD + +BEGIN_METHOD(Music_Play, GB_INTEGER loops; GB_FLOAT fadein) + + double fadevalue=0; + + CHECK_AUDIO(); + + if (!_music) + return; + + GB.GetTime(&_ref_time, FALSE); + + if (Mix_PausedMusic()) + { + Mix_ResumeMusic(); + return; + } + + fadevalue = VARGOPT(fadein, 0) * 1000; + + // if fadevalue is too small the music doesn't want to play. + if (fadevalue < 100) + fadevalue = 0; + + Mix_FadeInMusic(_music, VARGOPT(loops, 1), fadevalue); + update_volume(); + +END_METHOD + +BEGIN_METHOD_VOID(Music_Pause) + + CHECK_AUDIO(); + + _ref_pos = get_music_pos(); + Mix_PauseMusic(); + +END_METHOD + +BEGIN_METHOD(Music_Stop, GB_FLOAT fadeout) + + CHECK_AUDIO(); + + if (MISSING(fadeout)) + Mix_HaltMusic(); + else + Mix_FadeOutMusic(VARG(fadeout) * 1000); + + _ref_pos = 0; + +END_METHOD + +BEGIN_PROPERTY(Music_Pos) + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnFloat(get_music_pos()); + else + { + double pos; + + if (!_music) + return; + + if (Mix_GetMusicType(_music) == MUS_MOD) + { + GB.Error("Seeking is not supported on MOD files"); + return; + } + + pos = VPROP(GB_FLOAT); + Mix_RewindMusic(); + if (Mix_SetMusicPosition(pos) == 0) + _ref_pos = pos; + else + _ref_pos = 0; + GB.GetTime(&_ref_time, FALSE); + } + +END_PROPERTY + +BEGIN_PROPERTY(Music_Volume) + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnInteger(_volume); + else + { + _volume = VPROP(GB_INTEGER); + if (_volume < 0) + _volume = 0; + else if (_volume > MIX_MAX_VOLUME) + _volume = MIX_MAX_VOLUME; + + update_volume(); + } + +END_PROPERTY + +BEGIN_PROPERTY(Music_State) + + CHECK_AUDIO(); + + if (Mix_PlayingMusic()) + { + if (Mix_PausedMusic()) + GB.ReturnInteger(2); + else + GB.ReturnInteger(1); + } + else + GB.ReturnInteger(0); + +END_PROPERTY + +BEGIN_PROPERTY(Music_SoundFontPath) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(Mix_GetSoundFonts()); + else + Mix_SetSoundFonts(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC MusicDesc[] = +{ + GB_DECLARE_STATIC("Music"), + + GB_STATIC_PROPERTY("SoundFontPath", "s", Music_SoundFontPath), + + GB_STATIC_METHOD("Load", NULL, Music_Load, "(File)s"), + GB_STATIC_METHOD("Play", NULL, Music_Play, "[(Loops)i(FadeIn)f]"), + GB_STATIC_METHOD("Pause", NULL, Music_Pause, NULL), + GB_STATIC_METHOD("Stop", NULL, Music_Stop, "[(FadeOut)f]"), + + GB_STATIC_PROPERTY("Volume", "i", Music_Volume), + GB_STATIC_PROPERTY("Pos", "f", Music_Pos), + + GB_STATIC_PROPERTY_READ("State", "i", Music_State), + + GB_CONSTANT("Stopped", "i", 0), + GB_CONSTANT("Playing", "i", 1), + GB_CONSTANT("Paused", "i", 2), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/audio/c_music.h b/gb.sdl2/src/audio/c_music.h new file mode 100644 index 00000000..f5048310 --- /dev/null +++ b/gb.sdl2/src/audio/c_music.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + c_music.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MUSIC_H +#define __C_MUSIC_H + +#include "main.h" + +#ifndef __C_MUSIC_C +extern GB_DESC MusicDesc[]; +#endif + +void MUSIC_exit(void); + +#endif /* __C_MUSIC_H */ + diff --git a/gb.sdl2/src/audio/c_sound.c b/gb.sdl2/src/audio/c_sound.c new file mode 100644 index 00000000..3598d123 --- /dev/null +++ b/gb.sdl2/src/audio/c_sound.c @@ -0,0 +1,141 @@ +/*************************************************************************** + + c_sound.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_SOUND_C + +#include "c_channel.h" +#include "c_sound.h" + +#define THIS ((CSOUND *)_object) + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Sound_Frequency) + + if (READ_PROPERTY) + GB.ReturnInteger(AUDIO_frequency); + else + { + if (AUDIO_initialized) + GB.Error("Read-only property. Audio has been initialized"); + else + AUDIO_frequency = VPROP(GB_INTEGER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Sound_BufferSize) + + if (READ_PROPERTY) + GB.ReturnInteger(AUDIO_buffer_size); + else + { + if (AUDIO_initialized) + GB.Error("Read-only property. Audio has been initialized"); + else + AUDIO_buffer_size = VPROP(GB_INTEGER); + } + +END_PROPERTY + +BEGIN_METHOD(Sound_Load, GB_STRING path) + + char *addr; + int len; + Mix_Chunk *chunk; + CSOUND *sound; + + CHECK_AUDIO(); + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + chunk = Mix_LoadWAV_RW(SDL_RWFromMem(addr, len), TRUE); + GB.ReleaseFile(addr, len); + + if (!chunk) + { + GB.Error(Mix_GetError()); + return; + } + + sound = (CSOUND *)GB.New(CLASS_Sound, NULL, NULL); + sound->chunk = chunk; + GB.ReturnObject(sound); + +END_METHOD + +BEGIN_METHOD_VOID(Sound_free) + + Mix_FreeChunk(THIS->chunk); + THIS->chunk = NULL; + +END_METHOD + +BEGIN_PROPERTY(Sound_Volume) + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_VolumeChunk(THIS->chunk, -1)); + else + { + int vol = VPROP(GB_INTEGER); + if (vol < 0 || vol > MIX_MAX_VOLUME) + GB.Error(GB_ERR_ARG); + else + Mix_VolumeChunk(THIS->chunk, vol); + } + +END_PROPERTY + +BEGIN_METHOD(Sound_Play, GB_INTEGER loops; GB_FLOAT fadein) + + int loops = VARGOPT(loops, 0); + int channel; + + GB.Ref(THIS); + channel = CHANNEL_play_sound(-1, THIS, loops, MISSING(fadein) ? 0 : (int)(VARG(fadein) * 1000)); + CHANNEL_return(channel, THIS); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC SoundDesc[] = +{ + GB_DECLARE("Sound", sizeof(CSOUND)), GB_NOT_CREATABLE(), + + GB_CONSTANT("MaxVolume", "i", MIX_MAX_VOLUME), + + GB_STATIC_PROPERTY("Frequency", "i", Sound_Frequency), + GB_STATIC_PROPERTY("BufferSize", "i", Sound_BufferSize), + + GB_STATIC_METHOD("Load", "Sound", Sound_Load, "(Path)s"), + + GB_METHOD("_free", NULL, Sound_free, NULL), + + GB_PROPERTY("Volume", "i", Sound_Volume), + + GB_METHOD("Play", "Channel", Sound_Play, "[(Loops)i(FadeIn)f]"), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/audio/c_sound.h b/gb.sdl2/src/audio/c_sound.h new file mode 100644 index 00000000..52741c25 --- /dev/null +++ b/gb.sdl2/src/audio/c_sound.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + c_sound.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_SOUND_H +#define __C_SOUND_H + +#include "main.h" + +typedef + struct { + GB_BASE ob; + Mix_Chunk *chunk; + } + CSOUND; + +#ifndef __C_SOUND_C +extern GB_DESC SoundDesc[]; +#endif + +#endif /* __C_SOUND_H */ + diff --git a/gb.sdl2/src/audio/gb.sdl2.audio.component b/gb.sdl2/src/audio/gb.sdl2.audio.component new file mode 100644 index 00000000..4c09377b --- /dev/null +++ b/gb.sdl2/src/audio/gb.sdl2.audio.component @@ -0,0 +1,2 @@ +[Component] +Key=gb.sdl2.audio diff --git a/gb.sdl2/src/audio/main.c b/gb.sdl2/src/audio/main.c new file mode 100644 index 00000000..5654ddf4 --- /dev/null +++ b/gb.sdl2/src/audio/main.c @@ -0,0 +1,159 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "main.h" +#include "c_channel.h" +#include "c_music.h" +#include "c_sound.h" + +GB_INTERFACE GB EXPORT; + +GB_CLASS CLASS_Sound; +GB_CLASS CLASS_Channel; + +int AUDIO_frequency = 44100; +int AUDIO_buffer_size = 4096; +bool AUDIO_initialized = FALSE; + +//------------------------------------------------------------------------- + +static void init_mixer(int flag) +{ + if ((Mix_Init(flag) & flag) != flag) + fprintf(stderr, "gb.sdl2.audio: warning: %s\n", Mix_GetError()); +} + +bool AUDIO_init() +{ + Uint16 format; + int channels; + + if (AUDIO_initialized) + return FALSE; + + init_mixer(MIX_INIT_MP3); + init_mixer(MIX_INIT_OGG); + init_mixer(MIX_INIT_MOD); + init_mixer(MIX_INIT_FLAC); +#ifdef MIX_INIT_FLUIDSYNTH + init_mixer(MIX_INIT_FLUIDSYNTH); +#endif +#ifdef MIX_INIT_MID + init_mixer(MIX_INIT_MID); +#endif + + if (Mix_OpenAudio(AUDIO_frequency, MIX_DEFAULT_FORMAT, 2, AUDIO_buffer_size)) + { + GB.Error("Unable to initialize mixer"); + return TRUE; + } + + Mix_QuerySpec(&AUDIO_frequency, &format, &channels); + //fprintf(stderr, "AUDIO_init: %d %d %d\n", AUDIO_frequency, format, channels); + + if (CHANNEL_init()) + return TRUE; + + AUDIO_initialized = TRUE; + return FALSE; +} + +static void AUDIO_exit() +{ + if (!AUDIO_initialized) + return; + + // Don't free Gambas objects from GB_EXIT! + // CHANNEL_exit(); + MUSIC_exit(); + Mix_CloseAudio(); + + while (Mix_Init(0)) + Mix_Quit(); +} + +static void init_sdl() +{ + uint init = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if video is defined, SDL was initialized by gb.sdl2 component ! + if (init & SDL_INIT_VIDEO) + { + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) + goto __ERROR; + } + else + { + if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) + goto __ERROR; + } + + return; + +__ERROR: + + fprintf(stderr, "gb.sdl2.audio: unable to initialize SDL: %s\n", SDL_GetError()); + abort(); +} + +static void exit_sdl() +{ + uint init = SDL_WasInit(SDL_INIT_EVERYTHING); + + AUDIO_exit(); + + // if video is defined, gb.sdl2 component still not closed ! + if (init & SDL_INIT_VIDEO) + SDL_QuitSubSystem(SDL_INIT_AUDIO); + else + SDL_Quit(); +} + +//------------------------------------------------------------------------- + +GB_DESC *GB_CLASSES[] EXPORT = +{ + SoundDesc, + ChannelDesc, + ChannelsDesc, + MusicDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + CLASS_Sound = GB.FindClass("Sound"); + CLASS_Channel = GB.FindClass("Channel"); + + init_sdl(); + + return -1; +} + +void EXPORT GB_EXIT() +{ + exit_sdl(); +} diff --git a/gb.sdl2/src/audio/main.h b/gb.sdl2/src/audio/main.h new file mode 100644 index 00000000..32bd8a6e --- /dev/null +++ b/gb.sdl2/src/audio/main.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb_list.h" + +#include "SDL.h" +#include "SDL_mixer.h" + +#ifndef __MAIN_C + +extern GB_INTERFACE GB; + +extern GB_CLASS CLASS_Sound; +extern GB_CLASS CLASS_Channel; + +extern bool AUDIO_initialized; +extern int AUDIO_frequency; +extern int AUDIO_buffer_size; + +#endif + +bool AUDIO_init(void); + +#define CHECK_AUDIO() if (!AUDIO_initialized && AUDIO_init()) return + +#endif /* __MAIN_H */ + diff --git a/gb.sdl2/src/c_draw.c b/gb.sdl2/src/c_draw.c new file mode 100644 index 00000000..2315f309 --- /dev/null +++ b/gb.sdl2/src/c_draw.c @@ -0,0 +1,423 @@ +/*************************************************************************** + + c_draw.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_DRAW_C + +#include "c_window.h" +#include "c_image.h" +#include "c_font.h" +#include "c_draw.h" + +#define DRAW_STACK_MAX 8 +static CDRAW _draw_stack[DRAW_STACK_MAX]; +static CDRAW *_draw_current = NULL; +static CFONT *_default_font = NULL; + +#define THIS _draw_current +#define RENDERER _draw_current->renderer + +static bool check_device(void) +{ + if (THIS == NULL) + { + GB.Error("No device"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_DEVICE() if (check_device()) return + +static CFONT *get_default_font() +{ + if (!_default_font) + { + _default_font = FONT_create(); + GB.Ref(_default_font); + } + + return _default_font; +} + +/*static GB_COLOR get_background() +{ + uchar r, g, b, a; + SDL_GetRenderDrawColor(RENDERER, &r, &g, &b, &a); + return GB_COLOR_MAKE(r, g, b, a); +}*/ + +static void set_background(GB_COLOR col) +{ + uchar r, g, b, a; + + GB_COLOR_SPLIT(col, r, g, b, a); + SDL_SetRenderDrawColor(RENDERER, r, g, b, a); + + if (a != 255) + SDL_SetRenderDrawBlendMode(RENDERER, SDL_BLENDMODE_BLEND); + else + SDL_SetRenderDrawBlendMode(RENDERER, SDL_BLENDMODE_NONE); +} + +void DRAW_begin(void *device) +{ + if (THIS >= &_draw_stack[DRAW_STACK_MAX - 1]) + { + GB.Error("Too many nested drawings"); + return; + } + + if (GB.CheckObject(device)) + return; + + if (THIS == 0) + THIS = _draw_stack; + else + THIS++; + + THIS->font = get_default_font(); + GB.Ref(THIS->font); + + if (GB.Is(device, CLASS_Window)) + { + THIS->device = device; + THIS->renderer = ((CWINDOW *)device)->renderer; + GB.Ref(THIS->device); + THIS->color = (GB_COLOR)0xFFFFFF; + return; + } + + GB.Error("Unsupported device"); +} + +void DRAW_end(void) +{ + if (!THIS) + return; + + GB.Unref(POINTER(&THIS->device)); + THIS->device = NULL; + GB.Unref(POINTER(&THIS->font)); + THIS->font = NULL; + + if (THIS == _draw_stack) + THIS = NULL; + else + THIS--; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Draw_exit) + + if (_default_font) + GB.Unref(POINTER(&_default_font)); + +END_METHOD + +BEGIN_METHOD(Draw_Begin, GB_OBJECT device) + + void *device = VARG(device); + DRAW_begin(device); + +END_METHOD + +BEGIN_METHOD_VOID(Draw_End) + + CHECK_DEVICE(); + DRAW_end(); + +END_METHOD + +BEGIN_METHOD(Draw_Clear, GB_INTEGER col) + + CHECK_DEVICE(); + + set_background(VARGOPT(col, 0)); + SDL_RenderClear(RENDERER); + +END_METHOD + +BEGIN_PROPERTY(Draw_Background) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->color); + else + THIS->color = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_METHOD(Draw_Rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER col) + + SDL_Rect rect; + + CHECK_DEVICE(); + + rect.x = VARG(x); + rect.y = VARG(y); + rect.w = VARG(w); + rect.h = VARG(h); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawRect(RENDERER, &rect); + +END_METHOD + +BEGIN_METHOD(Draw_FillRect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER col) + + SDL_Rect rect; + + CHECK_DEVICE(); + + rect.x = VARG(x); + rect.y = VARG(y); + rect.w = VARG(w); + rect.h = VARG(h); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderFillRect(RENDERER, &rect); + +END_METHOD + +BEGIN_METHOD(Draw_Line, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2; GB_INTEGER col) + + CHECK_DEVICE(); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawLine(RENDERER, VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(Draw_Point, GB_INTEGER x; GB_INTEGER y; GB_INTEGER col) + + CHECK_DEVICE(); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawPoint(RENDERER, VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(Draw_Points, GB_OBJECT points; GB_INTEGER col) + + GB_ARRAY points; + uint n; + + CHECK_DEVICE(); + + points = (GB_ARRAY)VARG(points); + if (GB.CheckObject(points)) + return; + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawPoints(RENDERER, (SDL_Point *)GB.Array.Get(points, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_Lines, GB_OBJECT points; GB_INTEGER col) + + GB_ARRAY points; + uint n; + + CHECK_DEVICE(); + + points = (GB_ARRAY)VARG(points); + if (GB.CheckObject(points)) + return; + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawLines(RENDERER, (SDL_Point *)GB.Array.Get(points, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_Rects, GB_OBJECT rects; GB_INTEGER col) + + GB_ARRAY rects; + uint n; + + CHECK_DEVICE(); + + rects = (GB_ARRAY)VARG(rects); + if (GB.CheckObject(rects)) + return; + + n = GB.Array.Count(rects) / 4; + if (n == 0) + return; + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawRects(RENDERER, (SDL_Rect *)GB.Array.Get(rects, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_FillRects, GB_OBJECT rects; GB_INTEGER col) + + GB_ARRAY rects; + uint n; + + CHECK_DEVICE(); + + rects = (GB_ARRAY)VARG(rects); + if (GB.CheckObject(rects)) + return; + + n = GB.Array.Count(rects) / 4; + if (n == 0) + return; + + if (!MISSING(col)) set_background(VARG(col)); + SDL_RenderFillRects(RENDERER, (SDL_Rect *)GB.Array.Get(rects, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_Image, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_OBJECT src; GB_FLOAT opacity; GB_FLOAT angle) + + CIMAGE *image; + SDL_Texture *texture; + GEOM_RECT *src; + SDL_Rect *rect; + SDL_Rect dest; + + CHECK_DEVICE(); + + image = VARG(image); + if (GB.CheckObject(image)) + return; + + texture = IMAGE_get_texture(image, (CWINDOW *)THIS->device); + + dest.x = VARG(x); + dest.y = VARG(y); + dest.w = VARGOPT(w, image->img.width); + dest.h = VARGOPT(h, image->img.height); + + src = VARGOPT(src, NULL); + if (src) + rect = (SDL_Rect *)&src->x; + else + rect = NULL; + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + + if (MISSING(opacity) && MISSING(angle)) + SDL_RenderCopy(RENDERER, texture, rect, &dest); + else + { + SDL_SetTextureAlphaMod(texture, VARGOPT(opacity, 1.0) * 255); + SDL_RenderCopyEx(RENDERER, texture, rect, &dest, VARGOPT(angle, 0.0), NULL, SDL_FLIP_NONE); + SDL_SetTextureAlphaMod(texture, 255); + } + +END_METHOD + +BEGIN_PROPERTY(Draw_Font) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(THIS->font); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->font)); + if (!THIS->font) + { + THIS->font = get_default_font(); + GB.Ref(THIS->font); + } + } + +END_PROPERTY + +BEGIN_METHOD(Draw_Text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + SDL_Image *image; + SDL_Texture *texture; + SDL_Rect dest; + uint r, g, b, a; + + CHECK_DEVICE(); + + if (!LENGTH(text)) + return; + + dest.x = VARG(x); + dest.y = VARG(y); + + image = FONT_render_text(THIS->font, (CWINDOW *)THIS->device, STRING(text), LENGTH(text), &dest.w, &dest.h); + if (!image) + return; + + texture = SDL_GetTextureFromImage(image, (CWINDOW *)THIS->device, FALSE); + if (image->surface) + { + SDL_FreeSurface(image->surface); + image->surface = NULL; + } + + GB_COLOR_SPLIT(THIS->color, r, g, b, a); + SDL_SetTextureColorMod(texture, r, g, b); + SDL_SetTextureAlphaMod(texture, a); + + SDL_RenderCopy(RENDERER, texture, NULL, &dest); + +END_METHOD + +//------------------------------------------------------------------------- + + +GB_DESC DrawDesc[] = +{ + GB_DECLARE_STATIC("Draw"), + + GB_STATIC_METHOD("_exit", NULL, Draw_exit, NULL), + + GB_STATIC_METHOD("Begin", NULL, Draw_Begin, "(Device)o"), + GB_STATIC_METHOD("End", NULL, Draw_End, NULL), + + GB_STATIC_METHOD("Clear", NULL, Draw_Clear, "[(Color)i]"), + GB_STATIC_METHOD("Rect", NULL, Draw_Rect, "(X)i(Y)i(Width)i(Height)i[(Color)i]"), + GB_STATIC_METHOD("Rects", NULL, Draw_Rects, "(Rectangles)Integer[];[(Color)i]"), + GB_STATIC_METHOD("FillRect", NULL, Draw_FillRect, "(X)i(Y)i(Width)i(Height)i[(Color)i]"), + GB_STATIC_METHOD("FillRects", NULL, Draw_FillRects, "(Rectangles)Integer[];[(Color)i]"), + GB_STATIC_METHOD("Line", NULL, Draw_Line, "(X1)i(Y1)i(X2)i(Y2)i[(Color)i]"), + GB_STATIC_METHOD("Lines", NULL, Draw_Lines, "(Lines)Integer[];[(Color)i]"), + GB_STATIC_METHOD("Point", NULL, Draw_Point, "(X)i(Y)i[(Color)i]"), + GB_STATIC_METHOD("Points", NULL, Draw_Points, "(Points)Integer[];[(Color)i]"), + GB_STATIC_METHOD("Image", NULL, Draw_Image, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(Source)Rect;(Opacity)f(Angle)f"), + GB_STATIC_METHOD("Text", NULL, Draw_Text, "(Text)s(X)i(Y)i"), + + GB_STATIC_PROPERTY("Background", "i", Draw_Background), + GB_STATIC_PROPERTY("Font", "Font", Draw_Font), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_draw.h b/gb.sdl2/src/c_draw.h new file mode 100644 index 00000000..73f94441 --- /dev/null +++ b/gb.sdl2/src/c_draw.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + c_draw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_DRAW_H +#define __C_DRAW_H + +#include "main.h" +#include "c_font.h" + +typedef + struct { + void *device; + SDL_Renderer *renderer; + CFONT *font; + GB_COLOR color; + } + CDRAW; + +#ifndef __C_DRAW_C + +extern GB_DESC DrawDesc[]; + +void DRAW_begin(void *device); +void DRAW_end(); + +#endif + +#endif /* __C_DRAW_H */ + diff --git a/gb.sdl2/src/c_font.c b/gb.sdl2/src/c_font.c new file mode 100644 index 00000000..327c182b --- /dev/null +++ b/gb.sdl2/src/c_font.c @@ -0,0 +1,527 @@ +/*************************************************************************** + + c_font.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_FONT_C + +#include "default_font.h" +#include "c_font.h" + +typedef + struct { + LIST list; + char *path; + char *name; + } + FONT_LOAD; + +static FONT_LOAD *_font_list = NULL; + +#define THIS ((CFONT *)_object) + +static void create_cache(CFONT *_object) +{ + if (!THIS->cache) + GB.HashTable.New(&THIS->cache, GB_COMP_BINARY); +} + +CFONT *FONT_create() +{ + CFONT *font = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + create_cache(font); + return font; +} + +static FONT_LOAD *find_font(const char *name) +{ + FONT_LOAD *p; + + LIST_for_each(p, _font_list) + { + if (strcasecmp(name, p->name) == 0) + return p; + } + + return NULL; +} + +static bool load_font(char *path, char *name) +{ + FONT_LOAD *load; + char *addr; + int len; + + if (GB.LoadFile(path, strlen(path), &addr, &len)) + return TRUE; + + GB.ReleaseFile(addr, len); + + if (!name || !*name) + { + char *p = strrchr(path, '/'); + if (p) + name = p + 1; + else + name = path; + + p = strrchr(name, '.'); + if (p) + name = GB.TempString(name, p - name); + } + + if (find_font(name)) + { + GB.Error("Font already exists"); + return TRUE; + } + + GB.AllocZero(POINTER(&load), sizeof(FONT_LOAD)); + load->path = GB.NewZeroString(path); + load->name = GB.NewZeroString(name); + LIST_insert(&_font_list, load, &load->list); + + return FALSE; +} + +static void release_cache(CFONT *_object) +{ + GB.HashTable.Enum(THIS->cache, (GB_HASHTABLE_ENUM_FUNC)SDL_FreeImage); + GB.HashTable.Free(&THIS->cache); +} + +static bool init_font() +{ + if (!TTF_WasInit() && TTF_Init()) + { + GB.Error("Unable to initialize TTF library: &1", TTF_GetError()); + return TRUE; + } + else + return FALSE; +} + +static bool check_font(CFONT *_object) +{ + FONT_LOAD *font; + int style; + char *addr; + int len; + + if (!THIS->dirty) + return FALSE; + + if (THIS->font) + { + if (init_font()) + return TRUE; + TTF_CloseFont(THIS->font); + THIS->font = NULL; + } + + release_cache(THIS); + create_cache(THIS); + + if (!THIS->name) + { + THIS->dirty = FALSE; + return FALSE; + } + + font = find_font(THIS->name); + if (!font) + { + GB.Error("Unknown font: &1", THIS->name); + return TRUE; + } + + if (GB.LoadFile(font->path, strlen(font->path), &addr, &len)) + return TRUE; + + if (init_font()) + return TRUE; + + THIS->font = TTF_OpenFontRW(SDL_RWFromConstMem(addr, len), TRUE, THIS->size); + if (!THIS->font) + { + GB.Error("Unable to load font: &1: &2", THIS->name, TTF_GetError()); + return TRUE; + } + + style = TTF_STYLE_NORMAL; + if (THIS->bold) style |= TTF_STYLE_BOLD; + if (THIS->italic) style |= TTF_STYLE_ITALIC; + TTF_SetFontStyle(THIS->font, style); + + THIS->dirty = FALSE; + + return FALSE; +} + +static int get_font_ascent(void *_object) +{ + if (THIS->font) + return TTF_FontAscent(THIS->font); + else + return DEFAULT_FONT_ASCENT * THIS->size / DEFAULT_FONT_HEIGHT; +} + +static int get_font_descent(void *_object) +{ + if (THIS->font) + return TTF_FontDescent(THIS->font); + else + return DEFAULT_FONT_DESCENT * THIS->size / DEFAULT_FONT_HEIGHT; +} + +static void get_text_size(void *_object, const char *text, int *width, int *height) +{ + if (!text || !*text) + { + *width = 0; + *height = get_font_ascent(THIS) + get_font_descent(THIS); + return; + } + + if (THIS->font) + { + TTF_SizeUTF8(THIS->font, text, width, height); + return; + } + + *width = UTF8_get_length(text, strlen(text)) * DEFAULT_FONT_WIDTH * THIS->size / DEFAULT_FONT_HEIGHT; + *height = THIS->size; +} + + +SDL_Image *FONT_render_text(CFONT *_object, CWINDOW *window, char *text, int len, int *w, int *h) +{ + SDL_Surface *surface; + SDL_Image *image; + bool exist; + + if (check_font(THIS)) + return NULL; + + exist = !GB.HashTable.Get(THIS->cache, text, len, POINTER(&image)); + + if (exist) + { + if (image->window == window) + { + SDL_QueryTexture(image->texture, NULL, NULL, w, h); + if (!THIS->name) + { + *w = *w * THIS->size / DEFAULT_FONT_HEIGHT; + *h = *h * THIS->size / DEFAULT_FONT_HEIGHT; + } + + return image; + } + + SDL_FreeImage(image); + GB.HashTable.Remove(THIS->cache, text, len); + } + + if (THIS->name) + { + SDL_Color color = { 0xFF, 0xFF, 0xFF, 0xFF }; + char c = text[len]; + if (c) text[len] = 0; + surface = TTF_RenderUTF8_Blended(THIS->font, text, color); + if (c) text[len] = c; + *w = surface->w; + *h = surface->h; + } + else + { + int size = UTF8_get_length(text, len); + + surface = SDL_CreateRGBSurface(0, size * DEFAULT_FONT_WIDTH, DEFAULT_FONT_HEIGHT, 32, RMASK, GMASK, BMASK, AMASK); + + if (SDL_MUSTLOCK(surface)) + SDL_LockSurface(surface); + + FONT_render_default((uint *)surface->pixels, size, text, len); + + if (SDL_MUSTLOCK(surface)) + SDL_UnlockSurface(surface); + + *w = surface->w * THIS->size / DEFAULT_FONT_HEIGHT; + *h = surface->h * THIS->size / DEFAULT_FONT_HEIGHT; + } + + image = SDL_CreateImage(surface); + + if (GB.HashTable.Count(THIS->cache) >= 128) + { + release_cache(THIS); + create_cache(THIS); + } + + GB.HashTable.Add(THIS->cache, text, len, image); + return image; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Font_exit) + + FONT_LOAD *next; + + while (_font_list) + { + next = _font_list->list.next; + GB.FreeString(&_font_list->path); + GB.FreeString(&_font_list->name); + GB.Free(POINTER(&_font_list)); + _font_list = next; + } + +END_METHOD + +BEGIN_METHOD(Font_Load, GB_STRING path; GB_STRING name) + + char *path = GB.ToZeroString(ARG(path)); + char *name; + + if (MISSING(name)) + name = NULL; + else + name = GB.ToZeroString(ARG(name)); + + load_font(path, name); + +END_METHOD + +BEGIN_METHOD(Font_get, GB_STRING font) + + CFONT *font; + char *desc = GB.ToZeroString(ARG(font)); + char *elt; + int val; + bool bold = FALSE; + bool italic = FALSE; + int size = 10; + char *name = NULL; + + for (elt = strtok(desc, ","); elt; elt = strtok(NULL, ",")) + { + if (strcasecmp(elt, "bold") == 0) + { + bold = TRUE; + continue; + } + + if (strcasecmp(elt, "italic") == 0) + { + italic = TRUE; + continue; + } + + val = atoi(elt); + if (val) + { + size = val; + continue; + } + + if (name) + { + GB.Error("Font name defined twice"); + goto ERROR; + } + + name = GB.NewZeroString(elt); + } + + if (size < 1 || size > 1024) + { + GB.Error("Incorrect font size"); + goto ERROR; + } + + font = FONT_create(); + font->name = name; + font->bold = bold; + font->italic = italic; + font->size = size; + font->dirty = TRUE; + + GB.ReturnObject(font); + return; + +ERROR: + + GB.FreeString(&name); + return; + +END_METHOD + +BEGIN_METHOD_VOID(Font_free) + + release_cache(THIS); + + if (THIS->font) + TTF_CloseFont(THIS->font); + + GB.FreeString(&THIS->name); + +END_METHOD + +BEGIN_PROPERTY(Font_Size) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->size); + else + { + int size = VPROP(GB_INTEGER); + if (size < 1 || size > 1024) + GB.Error("Incorrect font size"); + else if (THIS->size != size) + { + THIS->size = size; + if (THIS->name) + THIS->dirty = TRUE; + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Bold) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->bold); + else if (THIS->bold != VPROP(GB_BOOLEAN)) + { + THIS->bold = VPROP(GB_BOOLEAN); + if (THIS->font && !THIS->dirty) + TTF_SetFontStyle(THIS->font, (TTF_GetFontStyle(THIS->font) ^ TTF_STYLE_BOLD)); + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Italic) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->italic); + else if (THIS->italic != VPROP(GB_BOOLEAN)) + { + THIS->italic = VPROP(GB_BOOLEAN); + if (THIS->font && !THIS->dirty) + TTF_SetFontStyle(THIS->font, (TTF_GetFontStyle(THIS->font) ^ TTF_STYLE_ITALIC)); + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Name) + + if (READ_PROPERTY) + GB.ReturnString(THIS->name); + else + { + GB.StoreString(PROP(GB_STRING), &THIS->name); + THIS->dirty = TRUE; + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Ascent) + + if (check_font(THIS)) + return; + + GB.ReturnInteger(TTF_FontAscent(THIS->font)); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Descent) + + if (check_font(THIS)) + return; + + GB.ReturnInteger(TTF_FontDescent(THIS->font)); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Fixed) + + if (check_font(THIS)) + return; + + GB.ReturnBoolean(TTF_FontFaceIsFixedWidth(THIS->font)); + +END_PROPERTY + +BEGIN_METHOD(Font_TextWidth, GB_STRING text) + + int w, h; + + if (check_font(THIS)) + return; + + get_text_size(THIS, GB.ToZeroString(ARG(text)), &w, &h); + GB.ReturnInteger(w); + +END_METHOD + +BEGIN_METHOD(Font_TextHeight, GB_STRING text) + + int w, h; + + if (check_font(THIS)) + return; + + get_text_size(THIS, GB.ToZeroString(ARG(text)), &w, &h); + GB.ReturnInteger(h); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC FontDesc[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), GB_NOT_CREATABLE(), + + GB_CONSTANT("DefaultHeight", "i", DEFAULT_FONT_HEIGHT), + + GB_STATIC_METHOD("Load", NULL, Font_Load, "(Path)s[(Name)s]"), + GB_STATIC_METHOD("_get", "Font", Font_get, "(Font)s"), + GB_STATIC_METHOD("_exit", NULL, Font_exit, NULL), + + GB_METHOD("_free", NULL, Font_free, NULL), + + GB_PROPERTY("Size", "i", Font_Size), + GB_PROPERTY("Bold", "b", Font_Bold), + GB_PROPERTY("Italic", "b", Font_Italic), + //GB_PROPERTY("Underline", "b", Font_Underline), + + GB_PROPERTY_READ("Name", "s", Font_Name), + GB_PROPERTY_READ("Ascent", "i", Font_Ascent), + GB_PROPERTY_READ("Descent", "i", Font_Descent), + GB_PROPERTY_READ("Fixed", "b", Font_Fixed), + + GB_METHOD("TextWidth", "i", Font_TextWidth, "(Text)s"), + GB_METHOD("TextHeight", "i", Font_TextHeight, "(Text)s"), + /*GB_METHOD("GetImage", "Image", Font_GetImage, "(Text)s"),*/ + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_font.h b/gb.sdl2/src/c_font.h new file mode 100644 index 00000000..b3ae9b49 --- /dev/null +++ b/gb.sdl2/src/c_font.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + c_font.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_FONT_H +#define __C_FONT_H + +#include "main.h" +#include "c_window.h" +#include "c_image.h" + +typedef + struct { + GB_BASE ob; + TTF_Font *font; + char *name; + int size; + GB_HASHTABLE cache; + unsigned bold : 1; + unsigned italic : 1; + unsigned dirty : 1; + } + CFONT; + +#ifndef __C_FONT_C +extern GB_DESC FontDesc[]; +#endif + +CFONT *FONT_create(); +SDL_Image *FONT_render_text(CFONT *_object, CWINDOW *window, char *text, int len, int *w, int *h); + +#endif /* __C_FONT_H */ + diff --git a/gb.sdl2/src/c_image.c b/gb.sdl2/src/c_image.c new file mode 100644 index 00000000..5fa9c1c2 --- /dev/null +++ b/gb.sdl2/src/c_image.c @@ -0,0 +1,214 @@ +/*************************************************************************** + + c_image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMAGE_C + +#include "c_image.h" + +#define THIS ((CIMAGE *)_object) +#define THIS_IMAGE (&THIS->img) + +SDL_Image *SDL_CreateImage(SDL_Surface *surface) +{ + SDL_Image *image; + + GB.Alloc(POINTER(&image), sizeof(SDL_Image)); + + image->texture = NULL; + image->window = NULL; + image->surface = surface; + + return image; +} + +void SDL_FreeImage(SDL_Image *image) +{ + if (image->texture) + { + SDL_DestroyTexture(image->texture); + image->texture = NULL; + GB.Unref(POINTER(&image->window)); + } + + if (image->surface) + { + SDL_FreeSurface(image->surface); + image->surface = NULL; + } + + GB.Free(POINTER(&image)); +} + +static void free_image(GB_IMG *img, void *image) +{ + SDL_FreeImage((SDL_Image *)image); +} + +static void *temp_image(GB_IMG *img) +{ + SDL_Surface *surface = NULL; + + if (img && img->data) + surface = SDL_CreateRGBSurfaceFrom(img->data, img->width, img->height, 32, img->width * sizeof(int), RMASK, GMASK, BMASK, AMASK); + + return SDL_CreateImage(surface); +} + +/*static void sync_image(GB_IMG *image) +{ + // Synchronize l'image texture->image + + // Puis mets le flag de synchro à false + image->sync = false; +}*/ + +static GB_IMG_OWNER _image_owner = { + "gb.sdl2", + DEFAULT_IMAGE_FORMAT, + free_image, + free_image, + temp_image, + NULL, //sync_image, + }; + +SDL_Image *IMAGE_get(CIMAGE *_object) +{ + return (SDL_Image *)IMAGE.Check(THIS_IMAGE, &_image_owner); +} + +#define check_image IMAGE_get + +static void take_image(CIMAGE *_object, SDL_Image *image) +{ + if (image && image->surface) + IMAGE.Take(THIS_IMAGE, &_image_owner, image, image->surface->w, image->surface->h, image->surface->pixels); + else + IMAGE.Take(THIS_IMAGE, &_image_owner, image, 0, 0, NULL); +} + +CIMAGE *IMAGE_create(SDL_Image *image) +{ + CIMAGE *img; + + img = (CIMAGE *)GB.New(CLASS_Image, NULL, NULL); + take_image(img, image); + return img; +} + +SDL_Texture *SDL_GetTextureFromImage(SDL_Image *image, CWINDOW *window, bool modified) +{ + if (image->texture) + { + if (image->window != window) + { + SDL_DestroyTexture(image->texture); + GB.Unref(POINTER(&image->window)); + image->texture = NULL; + } + else if (modified) + { + SDL_DestroyTexture(image->texture); + image->texture = NULL; + } + } + + if (!image->texture) + { + image->texture = SDL_CreateTextureFromSurface(window->renderer, image->surface); + //SDL_SetTextureBlendMode(image->texture, SDL_BLENDMODE_BLEND); + image->window = window; + GB.Ref(window); + } + + return image->texture; +} + +SDL_Texture *IMAGE_get_texture(CIMAGE *_object, CWINDOW *window) +{ + SDL_Texture *texture = SDL_GetTextureFromImage(IMAGE_get(THIS), window, THIS->img.modified); + THIS->img.modified = FALSE; + return texture; +} + +CIMAGE *IMAGE_create_from_window(CWINDOW *window, int x, int y, int w, int h) +{ + SDL_Surface *surface; + SDL_Rect rect = { x, y, w, h }; + + surface = SDL_CreateRGBSurface(0, w, h, 32, RMASK, GMASK, BMASK, AMASK); + SDL_RenderReadPixels(window->renderer, &rect, DEFAULT_SDL_IMAGE_FORMAT, surface->pixels, surface->pitch); + return IMAGE_create(SDL_CreateImage(surface)); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Image_Load, GB_STRING path) + + char *addr; + int len; + SDL_Surface *surface; + SDL_Surface *csurface; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + surface = IMG_Load_RW(SDL_RWFromConstMem(addr, len), TRUE); + GB.ReleaseFile(addr, len); + + if (!surface) + { + GB.Error("Unable to load image: &1", IMG_GetError()); + return; + } + + if (surface->format->format != DEFAULT_SDL_IMAGE_FORMAT) + { + csurface = SDL_ConvertSurfaceFormat(surface, DEFAULT_SDL_IMAGE_FORMAT, 0); + SDL_FreeSurface(surface); + surface = csurface; + } + + GB.ReturnObject(IMAGE_create(SDL_CreateImage(surface))); + +END_METHOD + +BEGIN_METHOD(Image_Save, GB_STRING path) + + char *path = GB.FileName(STRING(path), LENGTH(path)); + + if (SDL_SaveBMP(IMAGE_get(THIS)->surface, path)) + RAISE_ERROR("Unable to save image: &1"); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC ImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_METHOD("Save", NULL, Image_Save, "(Path)s"), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_image.h b/gb.sdl2/src/c_image.h new file mode 100644 index 00000000..801c201f --- /dev/null +++ b/gb.sdl2/src/c_image.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + c_image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMAGE_H +#define __C_IMAGE_H + +#include "main.h" +#include "c_window.h" + +typedef + struct { + SDL_Surface *surface; + SDL_Texture *texture; + CWINDOW *window; + } + SDL_Image; + +typedef + struct { + GB_IMG img; + } + CIMAGE; + +#ifndef __C_IMAGE_C +extern GB_DESC ImageDesc[]; +#endif + +CIMAGE *IMAGE_create(SDL_Image *image); +SDL_Texture *IMAGE_get_texture(CIMAGE *_object, CWINDOW *window); +CIMAGE *IMAGE_create_from_window(CWINDOW *window, int x, int y, int w, int h); + +SDL_Image *SDL_CreateImage(SDL_Surface *surface); +void SDL_FreeImage(SDL_Image *image); +SDL_Texture *SDL_GetTextureFromImage(SDL_Image *image, CWINDOW *window, bool modified); + +#endif /* __C_IMAGE_H */ + diff --git a/gb.sdl2/src/c_key.c b/gb.sdl2/src/c_key.c new file mode 100644 index 00000000..de4b53f3 --- /dev/null +++ b/gb.sdl2/src/c_key.c @@ -0,0 +1,344 @@ +/*************************************************************************** + + c_key.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_KEY_C + +#include "c_window.h" +#include "c_key.h" + +static SDL_Event *_current = NULL; +static bool _text_event = FALSE; + +//------------------------------------------------------------------------- + +static void update_event() +{ + if (!_current) + return; + + _text_event = _current->type == SDL_TEXTINPUT; +} + +SDL_Event *KEY_enter_event(SDL_Event *event) +{ + SDL_Event *old = _current; + _current = event; + update_event(); + return old; +} + +void KEY_leave_event(SDL_Event *event) +{ + _current = event; + update_event(); +} + +static bool check_event(void) +{ + if (!_current) + { + GB.Error("No keyboard event"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_EVENT() if (check_event()) return + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Key_get, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + int code = 0; + + if (*key) + { + if (!key[1] && ((uchar)key[0] < 127)) + { + GB.ReturnInteger(key[0]); + return; + } + else + { + for (code = 127; code <= 255; code++) + { + if (!strcasecmp(SDL_GetKeyName((SDL_Keycode)code), key)) + { + GB.ReturnInteger(code); + return; + } + } + } + } + + GB.ReturnInteger(0); + +END_METHOD + +BEGIN_PROPERTY(Key_Code) + + CHECK_EVENT(); + GB.ReturnInteger(_text_event ? 0 : _current->key.keysym.sym); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shift) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_SHIFT); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Control) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_CTRL); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Alt) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_ALT); + +END_PROPERTY + +BEGIN_PROPERTY(Key_AltGr) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_MODE); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Meta) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_GUI); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Normal) + + CHECK_EVENT(); + GB.ReturnBoolean(((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & (KMOD_CTRL | KMOD_ALT | KMOD_MODE | KMOD_GUI)) == 0); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Text) + + CHECK_EVENT(); + if (!_text_event) + GB.ReturnNull(); + else + GB.ReturnNewZeroString(_current->text.text); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Repeat) + + CHECK_EVENT(); + GB.ReturnBoolean(_text_event ? FALSE : _current->key.repeat); + +END_PROPERTY + + +static int convert_keycode_to_scancode(int code) +{ + if (code & SDLK_SCANCODE_MASK) + return code & ~SDLK_SCANCODE_MASK; + + switch(code) + { + case SDLK_RETURN: return SDL_SCANCODE_RETURN; + case SDLK_ESCAPE: return SDL_SCANCODE_ESCAPE; + case SDLK_BACKSPACE: return SDL_SCANCODE_BACKSPACE; + case SDLK_TAB: return SDL_SCANCODE_TAB; + case SDLK_SPACE: return SDL_SCANCODE_SPACE; + case SDLK_COMMA: return SDL_SCANCODE_COMMA; + case SDLK_MINUS: return SDL_SCANCODE_MINUS; + case SDLK_PERIOD: return SDL_SCANCODE_PERIOD; + case SDLK_SLASH: return SDL_SCANCODE_SLASH; + case SDLK_0: return SDL_SCANCODE_0; + case SDLK_1: return SDL_SCANCODE_1; + case SDLK_2: return SDL_SCANCODE_2; + case SDLK_3: return SDL_SCANCODE_3; + case SDLK_4: return SDL_SCANCODE_4; + case SDLK_5: return SDL_SCANCODE_5; + case SDLK_6: return SDL_SCANCODE_6; + case SDLK_7: return SDL_SCANCODE_7; + case SDLK_8: return SDL_SCANCODE_8; + case SDLK_9: return SDL_SCANCODE_9; + case SDLK_SEMICOLON: return SDL_SCANCODE_SEMICOLON; + case SDLK_EQUALS: return SDL_SCANCODE_EQUALS; + case SDLK_LEFTBRACKET: return SDL_SCANCODE_LEFTBRACKET; + case SDLK_BACKSLASH: return SDL_SCANCODE_BACKSLASH; + case SDLK_RIGHTBRACKET: return SDL_SCANCODE_RIGHTBRACKET; + case SDLK_a: return SDL_SCANCODE_A; + case SDLK_b: return SDL_SCANCODE_B; + case SDLK_c: return SDL_SCANCODE_C; + case SDLK_d: return SDL_SCANCODE_D; + case SDLK_e: return SDL_SCANCODE_E; + case SDLK_f: return SDL_SCANCODE_F; + case SDLK_g: return SDL_SCANCODE_G; + case SDLK_h: return SDL_SCANCODE_H; + case SDLK_i: return SDL_SCANCODE_I; + case SDLK_j: return SDL_SCANCODE_J; + case SDLK_k: return SDL_SCANCODE_K; + case SDLK_l: return SDL_SCANCODE_L; + case SDLK_m: return SDL_SCANCODE_M; + case SDLK_n: return SDL_SCANCODE_N; + case SDLK_o: return SDL_SCANCODE_O; + case SDLK_p: return SDL_SCANCODE_P; + case SDLK_q: return SDL_SCANCODE_Q; + case SDLK_r: return SDL_SCANCODE_R; + case SDLK_s: return SDL_SCANCODE_S; + case SDLK_t: return SDL_SCANCODE_T; + case SDLK_u: return SDL_SCANCODE_U; + case SDLK_v: return SDL_SCANCODE_V; + case SDLK_w: return SDL_SCANCODE_W; + case SDLK_x: return SDL_SCANCODE_X; + case SDLK_y: return SDL_SCANCODE_Y; + case SDLK_z: return SDL_SCANCODE_Z; + + default: return SDL_SCANCODE_UNKNOWN; + } +} + + +BEGIN_METHOD(Key_IsPressed, GB_INTEGER key; GB_BOOLEAN ignore) + + int code = VARG(key); + bool pressed = FALSE; + + if (VARGOPT(ignore, FALSE)) + code = convert_keycode_to_scancode(code); + else + code = SDL_GetScancodeFromKey(code); + + if (code != SDL_SCANCODE_UNKNOWN) + { + int count; + const Uint8 *keystate = SDL_GetKeyboardState(&count); + if (code >= 0 && code < count) + pressed = keystate[code]; + } + + GB.ReturnBoolean(pressed); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC KeyDesc[] = +{ + GB_DECLARE_STATIC("Key"), + + GB_STATIC_METHOD("_get", "i", Key_get, "(Key)s"), + + GB_STATIC_PROPERTY_READ("Code", "i", Key_Code), + GB_STATIC_PROPERTY_READ("Shift", "b", Key_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Key_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Key_Alt), + GB_STATIC_PROPERTY_READ("AltGr", "b", Key_AltGr), + GB_STATIC_PROPERTY_READ("Meta", "b", Key_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Key_Normal), + GB_STATIC_PROPERTY_READ("Text", "s", Key_Text), + GB_STATIC_PROPERTY("Repeat", "b", Key_Repeat), + + GB_STATIC_METHOD("IsPressed", "b", Key_IsPressed, "(Key)i[(IgnoreLayout)b]"), + + GB_CONSTANT("Backspace", "i", SDLK_BACKSPACE), + GB_CONSTANT("Tab", "i", SDLK_TAB), + GB_CONSTANT("Return", "i", SDLK_RETURN), + GB_CONSTANT("Pause", "i", SDLK_PAUSE), + GB_CONSTANT("Escape", "i", SDLK_ESCAPE), + GB_CONSTANT("Esc", "i", SDLK_ESCAPE), + GB_CONSTANT("Space", "i", SDLK_SPACE), + GB_CONSTANT("Delete", "i", SDLK_DELETE), + GB_CONSTANT("Num0", "i", SDLK_KP_0), + GB_CONSTANT("Num1", "i", SDLK_KP_1), + GB_CONSTANT("Num2", "i", SDLK_KP_2), + GB_CONSTANT("Num3", "i", SDLK_KP_3), + GB_CONSTANT("Num4", "i", SDLK_KP_4), + GB_CONSTANT("Num5", "i", SDLK_KP_5), + GB_CONSTANT("Num6", "i", SDLK_KP_6), + GB_CONSTANT("Num7", "i", SDLK_KP_7), + GB_CONSTANT("Num8", "i", SDLK_KP_8), + GB_CONSTANT("Num9", "i", SDLK_KP_9), + GB_CONSTANT("NumPeriod", "i", SDLK_KP_PERIOD), + GB_CONSTANT("NumDivide", "i", SDLK_KP_DIVIDE), + GB_CONSTANT("NumMultiply", "i", SDLK_KP_MULTIPLY), + GB_CONSTANT("NumMinus", "i", SDLK_KP_MINUS), + GB_CONSTANT("NumPlus", "i", SDLK_KP_PLUS), + GB_CONSTANT("NumEnter", "i", SDLK_KP_ENTER), + //GB_CONSTANT("NumEquals", "i", SDLK_KP_EQUALS), + GB_CONSTANT("Up", "i", SDLK_UP), + GB_CONSTANT("Down", "i", SDLK_DOWN), + GB_CONSTANT("Right", "i", SDLK_RIGHT), + GB_CONSTANT("Left", "i", SDLK_LEFT), + GB_CONSTANT("Insert", "i", SDLK_INSERT), + GB_CONSTANT("Home", "i", SDLK_HOME), + GB_CONSTANT("End", "i", SDLK_END), + GB_CONSTANT("PageUp", "i", SDLK_PAGEUP), + GB_CONSTANT("PageDown", "i", SDLK_PAGEDOWN), + GB_CONSTANT("F1", "i", SDLK_F1), + GB_CONSTANT("F2", "i", SDLK_F2), + GB_CONSTANT("F3", "i", SDLK_F3), + GB_CONSTANT("F4", "i", SDLK_F4), + GB_CONSTANT("F5", "i", SDLK_F5), + GB_CONSTANT("F6", "i", SDLK_F6), + GB_CONSTANT("F7", "i", SDLK_F7), + GB_CONSTANT("F8", "i", SDLK_F8), + GB_CONSTANT("F9", "i", SDLK_F9), + GB_CONSTANT("F10", "i", SDLK_F10), + GB_CONSTANT("F11", "i", SDLK_F11), + GB_CONSTANT("F12", "i", SDLK_F12), + GB_CONSTANT("F13", "i", SDLK_F13), + GB_CONSTANT("F14", "i", SDLK_F14), + GB_CONSTANT("F15", "i", SDLK_F15), + GB_CONSTANT("NumLock", "i", SDLK_NUMLOCKCLEAR), + GB_CONSTANT("CapsLock", "i", SDLK_CAPSLOCK), + GB_CONSTANT("ScrollLock", "i", SDLK_SCROLLLOCK), + GB_CONSTANT("RightShift", "i", SDLK_RSHIFT), + GB_CONSTANT("LeftShift", "i", SDLK_LSHIFT), + GB_CONSTANT("RightControl", "i", SDLK_RCTRL), + GB_CONSTANT("LeftControl", "i", SDLK_LCTRL), + GB_CONSTANT("RightAlt", "i", SDLK_RALT), + GB_CONSTANT("LeftAlt", "i", SDLK_LALT), + GB_CONSTANT("RightMeta", "i", SDLK_RGUI), + GB_CONSTANT("LeftMeta", "i", SDLK_LGUI), + GB_CONSTANT("AltGrKey", "i", SDLK_MODE), + //GB_CONSTANT("Compose", "i", SDLK_COMPOSE), + //GB_CONSTANT("Help", "i", SDLK_HELP), + //GB_CONSTANT("Print", "i", SDLK_PRINT), + GB_CONSTANT("SysReq", "i", SDLK_SYSREQ), + //GB_CONSTANT("Break", "i", SDLK_BREAK), + GB_CONSTANT("Menu", "i", SDLK_MENU), + //GB_CONSTANT("Power", "i", SDLK_POWER), + //GB_CONSTANT("Euro", "i", SDLK_EURO), + //GB_CONSTANT("Undo", "i", SDLK_UNDO), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_key.h b/gb.sdl2/src/c_key.h new file mode 100644 index 00000000..0a8f7148 --- /dev/null +++ b/gb.sdl2/src/c_key.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + c_key.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_KEY_H +#define __C_KEY_H + +#include "main.h" + +#ifndef __C_KEY_C +extern GB_DESC KeyDesc[]; +#endif + +SDL_Event *KEY_enter_event(SDL_Event *event); +void KEY_leave_event(SDL_Event *event); + +#endif /* __C_KEY_H */ + diff --git a/gb.sdl2/src/c_mouse.c b/gb.sdl2/src/c_mouse.c new file mode 100644 index 00000000..b24e32a6 --- /dev/null +++ b/gb.sdl2/src/c_mouse.c @@ -0,0 +1,276 @@ +/*************************************************************************** + + c_mouse.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MOUSE_C + +#include "c_window.h" +#include "c_mouse.h" + +static SDL_Event *_current = NULL; +static MOUSE_INFO *_info = NULL; + +//------------------------------------------------------------------------- + +static void update_event() +{ + MOUSE_INFO *info; + CWINDOW *window; + + _info = NULL; + + if (!_current) + return; + + window = WINDOW_get_from_event(_current); + if (!window) + return; + + _info = info = &window->mouse; + + switch (_current->type) + { + case SDL_MOUSEMOTION: + info->x = _current->motion.x; + info->y = _current->motion.y; + info->wheel_x = 0; + info->wheel_y = 0; + info->state = _current->motion.state; + info->button = 0; + break; + + case SDL_MOUSEWHEEL: + info->wheel_x = _current->wheel.x; + info->wheel_y = _current->wheel.y; + info->state = SDL_GetMouseState(&info->x, &info->y); + info->button = 0; +#if SDL_VERSION_ATLEAST(2,0,4) + if (_current->wheel.direction == SDL_MOUSEWHEEL_FLIPPED) + { + info->wheel_x = (- info->wheel_x); + info->wheel_y = (- info->wheel_y); + } +#endif + break; + + case SDL_MOUSEBUTTONDOWN: + info->x = _current->button.x; + info->y = _current->button.y; + info->wheel_x = 0; + info->wheel_y = 0; + info->state = SDL_GetMouseState(NULL, NULL); + info->button = _current->button.button; + info->start_x = info->x; + info->start_y = info->y; + break; + + case SDL_MOUSEBUTTONUP: + info->x = _current->button.x; + info->y = _current->button.y; + info->wheel_x = 0; + info->wheel_y = 0; + info->state = SDL_GetMouseState(NULL, NULL); + info->button = _current->button.button; + break; + } +} + +SDL_Event *MOUSE_enter_event(SDL_Event *event) +{ + SDL_Event *old = _current; + _current = event; + update_event(); + return old; +} + +void MOUSE_leave_event(SDL_Event *event) +{ + _current = event; + update_event(); +} + +static bool check_event(void) +{ + if (!_info) + { + GB.Error("No mouse event"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_EVENT() if (check_event()) return + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Mouse_X) + + CHECK_EVENT(); + GB.ReturnInteger(_info->x); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Y) + + CHECK_EVENT(); + GB.ReturnInteger(_info->y); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_WheelX) + + CHECK_EVENT(); + GB.ReturnInteger(_info->wheel_x); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_WheelY) + + CHECK_EVENT(); + GB.ReturnInteger(_info->wheel_y); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartX) + + GB.ReturnInteger(_info->start_x); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartY) + + GB.ReturnInteger(_info->start_y); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Left) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->button ? _info->button == SDL_BUTTON_LEFT : _info->state & SDL_BUTTON_LMASK); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Middle) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->button ? _info->button == SDL_BUTTON_MIDDLE : _info->state & SDL_BUTTON_MMASK); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Right) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->button ? _info->button == SDL_BUTTON_RIGHT : _info->state & SDL_BUTTON_RMASK); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Button) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->state); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(Mouse_Move, GB_INTEGER x; GB_INTEGER y; GB_OBJECT window) + + CWINDOW *window = (CWINDOW *)VARGOPT(window, NULL); + + if (!window) + { +#if SDL_VERSION_ATLEAST(2,0,4) + SDL_WarpMouseGlobal(VARG(x), VARG(y)); +#else + fprintf(stderr, "gb.sdl2: warning: global mouse warp is not supported.\n"); +#endif + } + else + SDL_WarpMouseInWindow(window->window, VARG(x), VARG(y)); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Mouse_Show) + + SDL_ShowCursor(SDL_ENABLE); + +END_METHOD + +BEGIN_METHOD_VOID(Mouse_Hide) + + SDL_ShowCursor(SDL_DISABLE); + +END_METHOD + +BEGIN_PROPERTY(Mouse_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE); + else + SDL_ShowCursor(VPROP(GB_BOOLEAN) ? SDL_ENABLE : SDL_DISABLE); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Relative) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_GetRelativeMouseMode()); + else + SDL_SetRelativeMouseMode(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC MouseDesc[] = +{ + GB_DECLARE_STATIC("Mouse"), + + //GB_STATIC_METHOD("Move", NULL, Mouse_Move, "(X)i(Y)i[(Window)Window]"), + + GB_STATIC_METHOD("Show", NULL, Mouse_Show, NULL), + GB_STATIC_METHOD("Hide", NULL, Mouse_Hide, NULL), + GB_STATIC_PROPERTY("Visible", "b", Mouse_Visible), + GB_STATIC_PROPERTY("Relative", "b", Mouse_Relative), + + //GB_STATIC_PROPERTY_READ("ScreenX", "i", Mouse_ScreenX), + //GB_STATIC_PROPERTY_READ("ScreenY", "i", Mouse_ScreenY), + GB_STATIC_PROPERTY_READ("StartX", "i", Mouse_StartX), + GB_STATIC_PROPERTY_READ("StartY", "i", Mouse_StartY), + GB_STATIC_PROPERTY_READ("X", "i", Mouse_X), + GB_STATIC_PROPERTY_READ("Y", "i", Mouse_Y), + GB_STATIC_PROPERTY_READ("WheelX", "i", Mouse_WheelX), + GB_STATIC_PROPERTY_READ("WheelY", "i", Mouse_WheelY), + + GB_STATIC_PROPERTY_READ("Left", "b", Mouse_Left), + GB_STATIC_PROPERTY_READ("Right", "b", Mouse_Right), + GB_STATIC_PROPERTY_READ("Middle", "b", Mouse_Middle), + GB_STATIC_PROPERTY_READ("Button", "i", Mouse_Button), + //GB_STATIC_PROPERTY_READ("Shift", "b", Mouse_Shift), + //GB_STATIC_PROPERTY_READ("Control", "b", Mouse_Control), + //GB_STATIC_PROPERTY_READ("Alt", "b", Mouse_Alt), + //GB_STATIC_PROPERTY_READ("Meta", "b", Mouse_Meta), + //GB_STATIC_PROPERTY_READ("Normal", "b", Mouse_Normal), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_mouse.h b/gb.sdl2/src/c_mouse.h new file mode 100644 index 00000000..2f119943 --- /dev/null +++ b/gb.sdl2/src/c_mouse.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + c_mouse.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MOUSE_H +#define __C_MOUSE_H + +#include "main.h" + +#ifndef __C_MOUSE_C +extern GB_DESC MouseDesc[]; +#endif + +typedef + struct { + int x; + int y; + int wheel_x; + int wheel_y; + int state; + int button; + int start_x; + int start_y; + } + MOUSE_INFO; + +SDL_Event *MOUSE_enter_event(SDL_Event *event); +void MOUSE_leave_event(SDL_Event *event); + +#endif /* __C_MOUSE_H */ + diff --git a/gb.sdl2/src/c_window.c b/gb.sdl2/src/c_window.c new file mode 100644 index 00000000..bca919b1 --- /dev/null +++ b/gb.sdl2/src/c_window.c @@ -0,0 +1,670 @@ +/*************************************************************************** + + c_window.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WINDOW_C + +#include "c_draw.h" +#include "c_image.h" +#include "c_window.h" + +#define THIS ((CWINDOW *)_object) +#define WINDOW THIS->window + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Enter); +DECLARE_EVENT(EVENT_Leave); +DECLARE_EVENT(EVENT_GotFocus); +DECLARE_EVENT(EVENT_LostFocus); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_KeyPress); +DECLARE_EVENT(EVENT_KeyRelease); +DECLARE_EVENT(EVENT_Text); +DECLARE_EVENT(EVENT_MouseMove); +DECLARE_EVENT(EVENT_MouseDown); +DECLARE_EVENT(EVENT_MouseUp); +DECLARE_EVENT(EVENT_MouseWheel); + +CWINDOW *WINDOW_list = NULL; +static int _id = 0; + +static void update_geometry(void *_object) +{ + if (!THIS->opened) + return; + + if (THIS->fullscreen) + { + SDL_SetWindowFullscreen(WINDOW, SDL_WINDOW_FULLSCREEN_DESKTOP); + if (!THIS->opengl) + SDL_RenderSetLogicalSize(THIS->renderer, THIS->width, THIS->height); + THIS->clear = TRUE; + } + else + { + SDL_SetWindowFullscreen(WINDOW, 0); + SDL_SetWindowPosition(WINDOW, THIS->x, THIS->y); + + if (!THIS->resizable) + { + SDL_SetWindowMinimumSize(WINDOW, THIS->width, THIS->height); + SDL_SetWindowMaximumSize(WINDOW, THIS->width, THIS->height); + } + SDL_SetWindowSize(WINDOW, THIS->width, THIS->height); + + GB.Raise(THIS, EVENT_Resize, 0); + } +} + +static void open_window(void *_object) +{ + if (THIS->opened) + return; + + if (GB.Raise(THIS, EVENT_Open, 0)) + return; + + THIS->opened = TRUE; + GB.Ref(THIS); + LIST_insert(&WINDOW_list, THIS, &THIS->list); + + SDL_ShowWindow(WINDOW); + update_geometry(THIS); + + /*if (!THIS->opengl) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, this->GetWidth(), this->GetHeight()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, GLdouble(this->GetWidth()), GLdouble(this->GetHeight()), 0.0f, -1, 1); + glMatrixMode(GL_MODELVIEW); + }*/ + + /*if (THIS->opengl) + { + if (GB.CanRaise(hWindow, EVENT_Resize)) + GB.Raise(hWindow, EVENT_Resize,0); + }*/ +} + + +static void close_window(void *_object) +{ + if (!THIS->opened) + return; + + if (GB.Raise(THIS, EVENT_Close, 0)) + return; + + LIST_remove(&WINDOW_list, THIS, &THIS->list); + SDL_HideWindow(WINDOW); + THIS->opened = FALSE; + GB.Unref(POINTER(&_object)); +} + +static void raise_mouse_event(CWINDOW *_object, SDL_Event *event, int type) +{ + SDL_Event *old; + + if (!GB.CanRaise(THIS, type)) + return; + + old = MOUSE_enter_event(event); + GB.Raise(THIS, type, 0); + MOUSE_leave_event(old); +} + +static void raise_keyboard_event(CWINDOW *_object, SDL_Event *event, int type) +{ + SDL_Event *old; + + if (!GB.CanRaise(THIS, type)) + return; + + old = KEY_enter_event(event); + GB.Raise(THIS, type, 0); + KEY_leave_event(old); +} + +CWINDOW *WINDOW_get_from_event(SDL_Event *event) +{ + SDL_Window *window = SDL_GetWindowFromID(event->window.windowID); + return SDL_GetWindowData(window, "gambas-object"); +} + +void WINDOW_handle_event(SDL_Event *event) +{ + CWINDOW *_object; + + _object = WINDOW_get_from_event(event); + if (!THIS) + return; + + switch(event->type) + { + case SDL_WINDOWEVENT: + switch(event->window.event) + { + case SDL_WINDOWEVENT_SHOWN: + GB.Raise(THIS, EVENT_Show, 0); + break; + case SDL_WINDOWEVENT_HIDDEN: + GB.Raise(THIS, EVENT_Hide, 0); + break; + /*case SDL_WINDOWEVENT_EXPOSED:*/ + case SDL_WINDOWEVENT_MOVED: + THIS->x = event->window.data1; + THIS->y = event->window.data2; + GB.Raise(THIS, EVENT_Move, 0); + break; + case SDL_WINDOWEVENT_RESIZED: + THIS->width = event->window.data1; + THIS->height = event->window.data2; + GB.Raise(THIS, EVENT_Resize, 0); + break; + /*case SDL_WINDOWEVENT_MINIMIZED: + SDL_Log("Window %d minimized", event->window.windowID); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + SDL_Log("Window %d maximized", event->window.windowID); + break; + case SDL_WINDOWEVENT_RESTORED: + SDL_Log("Window %d restored", event->window.windowID); + break;*/ + case SDL_WINDOWEVENT_ENTER: + GB.Raise(THIS, EVENT_Enter, 0); + break; + case SDL_WINDOWEVENT_LEAVE: + GB.Raise(THIS, EVENT_Leave, 0); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + GB.Raise(THIS, EVENT_GotFocus, 0); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + GB.Raise(THIS, EVENT_LostFocus, 0); + break; + case SDL_WINDOWEVENT_CLOSE: + close_window(THIS); + break; + } + break; + + case SDL_MOUSEBUTTONDOWN: + raise_mouse_event(THIS, event, EVENT_MouseDown); + break; + case SDL_MOUSEBUTTONUP: + raise_mouse_event(THIS, event, EVENT_MouseUp); + break; + case SDL_MOUSEMOTION: + raise_mouse_event(THIS, event, EVENT_MouseMove); + break; + case SDL_MOUSEWHEEL: + raise_mouse_event(THIS, event, EVENT_MouseWheel); + break; + case SDL_KEYDOWN: + raise_keyboard_event(THIS, event, EVENT_KeyPress); + break; + case SDL_KEYUP: + raise_keyboard_event(THIS, event, EVENT_KeyRelease); + break; + case SDL_TEXTINPUT: + raise_keyboard_event(THIS, event, EVENT_Text); + break; + } +} + +void WINDOW_update(void) +{ + CWINDOW *_object; + uint current_time; + uint diff; + bool at_least_one = FALSE; + + current_time = SDL_GetTicks(); + + LIST_for_each(_object, WINDOW_list) + { + if (!GB.CanRaise(THIS, EVENT_Draw)) + continue; + + if (THIS->frame_time > 0) + { + double d = THIS->last_time + THIS->frame_time; + if (d > current_time) + continue; + THIS->last_time = d; + } + + if (THIS->clear) + { + if (!THIS->opengl) + { + SDL_SetRenderDrawColor(THIS->renderer, 0, 0, 0, 255); + SDL_RenderClear(THIS->renderer); + } + THIS->clear = FALSE; + } + + DRAW_begin(THIS); + GB.Raise(THIS, EVENT_Draw, 0); + DRAW_end(); + + if (THIS->opengl) + SDL_GL_SwapWindow(WINDOW); + else + SDL_RenderPresent(THIS->renderer); + + THIS->frame_count++; + THIS->total_frame_count++; + + if (THIS->start_time == 0) + THIS->start_time = current_time; + else + { + diff = current_time - THIS->start_time; + if (diff > 1000) + { + THIS->frame_rate = THIS->frame_count; + THIS->frame_count = 0; + THIS->start_time += 1000; + } + } + + at_least_one = TRUE; + } + + if (!at_least_one) + SDL_Delay(1); +} + +static void init_opengl(void) +{ + static bool _init = FALSE; + + if (_init) + return; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + _init = TRUE; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Window_new, GB_BOOLEAN opengl) + + int flag; + + _id++; + THIS->id = _id; + + THIS->opengl = VARGOPT(opengl, FALSE); + THIS->fullscreen = FALSE; + THIS->width = 640; + THIS->height = 400; + + flag = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; + + if (THIS->opengl) + { + init_opengl(); + flag |= SDL_WINDOW_OPENGL; + } + + THIS->window = SDL_CreateWindow(GB.Application.Title(), 0, 0, THIS->width, THIS->height, flag); + if (!WINDOW) + { + RAISE_ERROR("Unable to create window"); + return; + } + + if (THIS->opengl) + { + THIS->context = SDL_GL_CreateContext(WINDOW); + if (!THIS->context) + { + RAISE_ERROR("Unable to create OpenGL context"); + return; + } + } + else + { + THIS->renderer = SDL_CreateRenderer(WINDOW, -1, SDL_RENDERER_ACCELERATED); + if (!THIS->renderer) + { + RAISE_ERROR("Unable to create renderer"); + return; + } + } + + SDL_SetWindowData(WINDOW, "gambas-object", THIS); + + SDL_SetWindowMinimumSize(WINDOW, THIS->width, THIS->height); + SDL_SetWindowMaximumSize(WINDOW, THIS->width, THIS->height); + +END_METHOD + +BEGIN_METHOD_VOID(Window_free) + + if (THIS->context) + SDL_GL_DeleteContext(THIS->context); + if (THIS->renderer) + SDL_DestroyRenderer(THIS->renderer); + SDL_DestroyWindow(WINDOW); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Show) + + open_window(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Hide) + + SDL_HideWindow(WINDOW); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Close) + + close_window(THIS); + +END_METHOD + +BEGIN_PROPERTY(Window_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_GetWindowFlags(WINDOW) & SDL_WINDOW_SHOWN); + else + { + if (VPROP(GB_BOOLEAN)) + open_window(THIS); + else + SDL_HideWindow(WINDOW); + } + +END_PROPERTY + +BEGIN_METHOD(Window_Move, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + int w = VARGOPT(width, -1); + int h = VARGOPT(height, -1); + + THIS->x = VARG(x); + THIS->y = VARG(y); + if (w > 0) THIS->width = w; + if (h > 0) THIS->height = h; + + update_geometry(THIS); + +END_METHOD + +BEGIN_METHOD(Window_Resize, GB_INTEGER width; GB_INTEGER height) + + int w = VARG(width); + int h = VARG(height); + + if (w > 0) THIS->width = w; + if (h > 0) THIS->height = h; + + update_geometry(THIS); + +END_METHOD + +BEGIN_PROPERTY(Window_X) + + GB.ReturnInteger(THIS->x); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Y) + + GB.ReturnInteger(THIS->y); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Width) + + GB.ReturnInteger(THIS->width); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Height) + + GB.ReturnInteger(THIS->height); + +END_PROPERTY + +BEGIN_PROPERTY(Window_FullScreen) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->fullscreen); + else + { + bool f = VPROP(GB_BOOLEAN); + + if (THIS->fullscreen != f) + { + THIS->fullscreen = f; + if (f) + { + THIS->save_x = THIS->x; + THIS->save_y = THIS->y; + THIS->save_width = THIS->width; + THIS->save_height = THIS->height; + } + else + { + THIS->x = THIS->save_x; + THIS->y = THIS->save_y; + THIS->width = THIS->save_width; + THIS->height = THIS->save_height; + } + update_geometry(THIS); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_FrameRate) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->frame_rate); + else + { + double val = VPROP(GB_FLOAT); + + if (val < 0) + return; + + THIS->frame_time = val ? 1000.0 / val : 0; + THIS->last_time = SDL_GetTicks(); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_FrameCount) + + GB.ReturnInteger(THIS->total_frame_count); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(SDL_GetWindowTitle(WINDOW)); + else + SDL_SetWindowTitle(WINDOW, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_METHOD(Window_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + GB.ReturnObject(IMAGE_create_from_window(THIS, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, THIS->width), VARGOPT(h, THIS->height))); + +END_METHOD + +BEGIN_PROPERTY(Window_Resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->resizable); + else + { + bool v = VPROP(GB_BOOLEAN); + if (v == THIS->resizable) + return; + THIS->resizable = v; + + if (v) + { + SDL_SetWindowMinimumSize(WINDOW, 1, 1); + SDL_SetWindowMaximumSize(WINDOW, 2048, 2048); + } + else + { + SDL_SetWindowMinimumSize(WINDOW, THIS->width, THIS->height); + SDL_SetWindowMaximumSize(WINDOW, THIS->width, THIS->height); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Handle) + + GB.ReturnInteger(SDL_GetWindowID(WINDOW)); + +END_PROPERTY + +// Does not work +#if 0 +BEGIN_PROPERTY(Window_Border) + + if (READ_PROPERTY) + GB.ReturnBoolean((SDL_GetWindowFlags(WINDOW) & SDL_WINDOW_BORDERLESS) == 0); + else + { + SDL_SetWindowBordered(WINDOW, VPROP(GB_BOOLEAN)); + SDL_ShowWindow(WINDOW); + } + +END_PROPERTY +#endif + +BEGIN_PROPERTY(Window_Grabbed) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_GetWindowGrab(WINDOW)); + else + SDL_SetWindowGrab(WINDOW, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC WindowDesc[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), + + GB_METHOD("_new", NULL, Window_new, "[(OpenGL)b]"), + GB_METHOD("_free", NULL, Window_free, NULL), + + GB_METHOD("Show", NULL, Window_Show, NULL), + GB_METHOD("Hide", NULL, Window_Hide, NULL), + GB_METHOD("Close", NULL, Window_Close, NULL), + GB_METHOD("Move", NULL, Window_Move, "(X)i(Y)i[(Width)i(Height)i]"), + GB_METHOD("Resize", NULL, Window_Resize, "(Width)i(Height)i"), + GB_METHOD("Screenshot", "Image", Window_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_PROPERTY("Visible", "b", Window_Visible), + GB_PROPERTY("FullScreen", "b", Window_FullScreen), + //GB_PROPERTY("Border", "b", Window_Border), + GB_PROPERTY("Resizable", "b", Window_Resizable), + + GB_PROPERTY_READ("X", "i", Window_X), + GB_PROPERTY_READ("Y", "i", Window_Y), + GB_PROPERTY_READ("W", "i", Window_Width), + GB_PROPERTY_READ("H", "i", Window_Height), + GB_PROPERTY_READ("Width", "i", Window_Width), + GB_PROPERTY_READ("Height", "i", Window_Height), + + GB_PROPERTY_READ("Handle", "i", Window_Handle), + + GB_PROPERTY("FrameRate", "f", Window_FrameRate), + GB_PROPERTY_READ("FrameCount", "i", Window_FrameCount), + + GB_PROPERTY("Text", "s", Window_Text), + GB_PROPERTY("Title", "s", Window_Text), + + GB_PROPERTY("Grabbed", "b", Window_Grabbed), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("GotFocus", NULL, NULL, &EVENT_GotFocus), + GB_EVENT("LostFocus", NULL, NULL, &EVENT_LostFocus), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPress), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyRelease), + GB_EVENT("Text", NULL, NULL, &EVENT_Text), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("MouseWheel", NULL, NULL, &EVENT_MouseWheel), + +/* + GB_METHOD("Clear", NULL, CWINDOW_clear, NULL), + GB_METHOD("Fill", NULL, CWINDOW_fill, "(Color)i"), + GB_METHOD("Refresh", NULL, CWINDOW_refresh, NULL), + GB_METHOD("Update", NULL, CWINDOW_refresh, NULL), + + GB_PROPERTY("Tracking", "b", CWINDOW_tracking), + GB_PROPERTY("Resizable", "b", CWINDOW_resizable), + + GB_PROPERTY_READ("Handle", "i", CWINDOW_id), + GB_PROPERTY_READ("Id", "i", CWINDOW_id), + + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("JoyAxisMove", NULL, NULL, &EVENT_JoyAxisMotion), + GB_EVENT("JoyBallMove", NULL, NULL, &EVENT_JoyBallMotion), + GB_EVENT("JoyButtonPress", NULL, NULL, &EVENT_JoyButtonPressed), + GB_EVENT("JoyButtonRelease", NULL, NULL, &EVENT_JoyButtonReleased), + GB_EVENT("JoyHatMove", NULL, NULL, &EVENT_JoyHatMotion), +*/ + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_window.h b/gb.sdl2/src/c_window.h new file mode 100644 index 00000000..72c6194d --- /dev/null +++ b/gb.sdl2/src/c_window.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + c_window.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WINDOW_H +#define __C_WINDOW_H + +#include "main.h" +#include "c_mouse.h" +#include "c_key.h" + +typedef + struct CWINDOW { + GB_BASE ob; + LIST list; + SDL_Window *window; + SDL_Renderer *renderer; + SDL_GLContext context; + int id; + int x; + int y; + int width; + int height; + int save_x, save_y, save_width, save_height; + uint start_time; + uint frame_count; + uint total_frame_count; + double last_time; + double frame_time; + double frame_rate; + MOUSE_INFO mouse; + unsigned opengl : 1; + unsigned opened : 1; + unsigned fullscreen : 1; + unsigned clear : 1; + unsigned resizable : 1; + } + CWINDOW; + +#ifndef __C_WINDOW_C + +extern GB_DESC WindowDesc[]; + +extern CWINDOW *WINDOW_list; + +CWINDOW *WINDOW_get_from_event(SDL_Event *event); +void WINDOW_handle_event(SDL_Event *event); +void WINDOW_update(); + +#endif + +#endif /* __C_WINDOW_H */ + diff --git a/gb.sdl2/src/default_font.c b/gb.sdl2/src/default_font.c new file mode 100644 index 00000000..eec1fb35 --- /dev/null +++ b/gb.sdl2/src/default_font.c @@ -0,0 +1,202 @@ +/*************************************************************************** + + default_font.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DEFAULT_FONT_C + +#include "default_font.h" +#include "default_font_data.h" + +#define UNICODE_INVALID 0xFFFFFFFFU + +static const char _char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define utf8_get_char_length(_c) ((int)_char_length[(unsigned char)(_c)]) + +int UTF8_get_length(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + int ulen; + int i; + + ulen = 0; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + +static uint utf8_to_unicode(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + uint unicode; + + switch (len) + { + case 2: + unicode = (str[1] & 0x3F) + ((str[0] & 0x1F) << 6); + if (unicode < 0x80) + goto _INVALID; + break; + + case 3: + unicode = (str[2] & 0x3F) + ((str[1] & 0x3F) << 6) + ((str[0] & 0xF) << 12); + if (unicode < 0x800) + goto _INVALID; + break; + + case 4: + unicode = (str[3] & 0x3F) + ((str[2] & 0x3F) << 6) + ((str[1] & 0x3F) << 12) + ((str[0] & 0x7) << 18); + if (unicode < 0x10000) + goto _INVALID; + break; + + case 5: + unicode = (str[4] & 0x3F) + ((str[3] & 0x3F) << 6) + ((str[2] & 0x3F) << 12) + ((str[1] & 0x3F) << 18) + ((str[0] & 0x3) << 24); + if (unicode < 0x200000) + goto _INVALID; + break; + + case 6: + unicode = (str[5] & 0x3F) + ((str[4] & 0x3F) << 6) + ((str[3] & 0x3F) << 12) + ((str[2] & 0x3F) << 18) + ((str[1] & 0x3F) << 24) + ((str[0] & 0x1) << 30); + if (unicode < 0x4000000) + goto _INVALID; + break; + + default: + unicode = str[0]; + break; + } + + return unicode; + +_INVALID: + + return UNICODE_INVALID; +} + + +void FONT_render_default(uint *dest, int size, const char *text, int len) +{ + static void *jump[] = { &&__0, &&__1, &&__2, &&__3, &&__4, &&__5, &&__6, &&__7, &&__8, &&__9, &&__A, &&__B, &&__C, &&__D, &&__E, &&__F }; + static void *jump2[] = { &&__00, &&__10, &&__20, &&__30, &&__40, &&__50, &&__60, &&__70, &&__80, &&__90, &&__A0, &&__B0, &&__C0, &&__D0, &&__E0, &&__F0 }; + + int lc; + const char *src; + uint *p; + int y; + uchar line; + uint code; + const uint col = 0xFFFFFFFF; + + size *= DEFAULT_FONT_WIDTH; + + for(;;) + { + if (!*text) + break; + + lc = utf8_get_char_length(*text); + code = utf8_to_unicode(text, lc); + text += lc; + + if (code >= 33 && code <= 126) + src = _default_font_33_126 + DEFAULT_FONT_HEIGHT * (code - 33); + else if (code >= 160 && code <= 687) + src = _default_font_160_687 + DEFAULT_FONT_HEIGHT * (code - 160); + else if (code >= 880 && code <= 1319) + src = _default_font_880_1319 + DEFAULT_FONT_HEIGHT * (code - 880); + else + src = NULL; + + if (src) + { + p = dest; + + for (y = 0; y < DEFAULT_FONT_HEIGHT; y++) + { + line = *src++; + if (!line) + { + p += size; + continue; + } + + goto *jump[line & 0xF]; + + __1: p[0] = col; goto __0; + __2: p[1] = col; goto __0; + __3: p[0] = p[1] = col; goto __0; + __4: p[2] = col; goto __0; + __5: p[0] = p[2] = col; goto __0; + __6: p[1] = p[2] = col; goto __0; + __7: p[0] = p[1] = p[2] = col; goto __0; + __8: p[3] = col; goto __0; + __9: p[0] = p[3] = col; goto __0; + __A: p[1] = p[3] = col; goto __0; + __B: p[0] = p[1] = p[3] = col; goto __0; + __C: p[2] = p[3] = col; goto __0; + __D: p[0] = p[2] = p[3] = col; goto __0; + __E: p[1] = p[2] = p[3] = col; goto __0; + __F: p[0] = p[1] = p[2] = p[3] = col; goto __0; + __0: + + goto *jump2[line >> 4]; + + __10: p[4] = col; goto __00; + __20: p[5] = col; goto __00; + __30: p[4] = p[5] = col; goto __00; + __40: p[6] = col; goto __00; + __50: p[4] = p[6] = col; goto __00; + __60: p[5] = p[6] = col; goto __00; + __70: p[4] = p[5] = p[6] = col; goto __00; + __80: p[7] = col; goto __00; + __90: p[4] = p[7] = col; goto __00; + __A0: p[5] = p[7] = col; goto __00; + __B0: p[4] = p[5] = p[7] = col; goto __00; + __C0: p[6] = p[7] = col; goto __00; + __D0: p[4] = p[6] = p[7] = col; goto __00; + __E0: p[5] = p[6] = p[7] = col; goto __00; + __F0: p[4] = p[5] = p[6] = p[7] = col; goto __00; + __00: + + p += size; + } + } + + dest += DEFAULT_FONT_WIDTH; + } +} diff --git a/gb.sdl2/src/default_font.h b/gb.sdl2/src/default_font.h new file mode 100644 index 00000000..40364eb2 --- /dev/null +++ b/gb.sdl2/src/default_font.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + default_font.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DEFAULT_FONT_H +#define __DEFAULT_FONT_H + +#include "main.h" + +#define DEFAULT_FONT_WIDTH 7 +#define DEFAULT_FONT_HEIGHT 13 +#define DEFAULT_FONT_ASCENT 10 +#define DEFAULT_FONT_DESCENT 3 + +int UTF8_get_length(const char *sstr, int len); +void FONT_render_default(uint *dest, int size, const char *text, int len); + +#endif /* __DEFAULT_FONT_H */ + diff --git a/gb.sdl2/src/default_font_data.h b/gb.sdl2/src/default_font_data.h new file mode 100644 index 00000000..33d47305 --- /dev/null +++ b/gb.sdl2/src/default_font_data.h @@ -0,0 +1,1075 @@ +#define DEFAULT_FONT_WIDTH 7 +#define DEFAULT_FONT_HEIGHT 13 +#define DEFAULT_FONT_ASCENT 10 +#define DEFAULT_FONT_DESCENT 3 + +const char _default_font_33_126[] = { + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x1C, 0x28, 0x28, 0x1E, 0x08, 0x08, 0x00, + 0x00, 0x02, 0x05, 0x25, 0x12, 0x08, 0x04, 0x12, 0x29, 0x28, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x0C, 0x04, 0x2A, 0x11, 0x11, 0x2E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00, + 0x00, 0x04, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x0C, 0x0A, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x18, 0x14, 0x12, 0x11, 0x3F, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x10, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x20, 0x10, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x39, 0x25, 0x25, 0x25, 0x19, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x00, + 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1C, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x04, 0x04, 0x18, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x00, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x00, + 0x00, 0x24, 0x2A, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const char _default_font_160_687[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x3C, 0x08, 0x08, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x04, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x02, 0x04, 0x0A, 0x12, 0x14, 0x08, 0x10, 0x0E, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x59, 0x45, 0x45, 0x45, 0x59, 0x22, 0x1C, 0x00, 0x00, + 0x0C, 0x10, 0x1C, 0x12, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x09, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x4D, 0x55, 0x4D, 0x55, 0x55, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x10, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x3C, 0x2E, 0x2E, 0x2E, 0x2C, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x06, + 0x04, 0x06, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x24, 0x12, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x1C, 0x20, 0x18, 0x04, 0x3C, + 0x07, 0x08, 0x06, 0x08, 0x07, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x04, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x04, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x04, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x04, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x04, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1A, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x0C, 0x30, 0x18, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x14, 0x08, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x70, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x10, 0x60, + 0x14, 0x08, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x22, 0x1C, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x06, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x7F, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0F, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x30, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x08, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x37, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x24, 0x24, 0x00, 0x36, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x20, 0x18, + 0x10, 0x28, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x10, 0x28, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x10, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x0C, + 0x14, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x1A, 0x1A, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x34, 0x34, 0x04, 0x04, 0x04, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x06, 0x03, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x18, 0x0C, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x08, 0x06, + 0x14, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x02, 0x02, 0x01, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x39, 0x09, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x29, 0x29, 0x19, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x0C, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x0C, + 0x14, 0x08, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x0E, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x07, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x25, 0x24, 0x24, 0x1C, 0x24, 0x24, 0x24, 0x1C, 0x00, 0x00, + 0x00, 0x1E, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x03, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x0E, 0x15, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x14, 0x0C, 0x00, 0x00, + 0x00, 0x3C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x14, 0x08, 0x10, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, + 0x00, 0x40, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x06, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x60, 0x10, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x02, 0x62, 0x12, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x1E, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x34, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x04, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x26, 0x20, 0x20, + 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x20, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x08, + 0x00, 0x00, 0x3E, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x3E, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x18, 0x20, 0x20, 0x1E, 0x01, 0x06, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x1E, 0x02, 0x04, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x28, 0x10, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x13, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x14, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1F, 0x00, 0x00, + 0x00, 0x20, 0x21, 0x01, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2F, 0x20, 0x18, + 0x00, 0x20, 0x26, 0x04, 0x34, 0x24, 0x24, 0x24, 0x24, 0x24, 0x2E, 0x20, 0x18, + 0x00, 0x00, 0x29, 0x29, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x19, 0x00, 0x00, + 0x00, 0x20, 0x29, 0x09, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x00, 0x20, 0x20, 0x00, 0x37, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x72, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x72, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x14, 0x08, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x02, 0x02, + 0x04, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x28, 0x28, 0x3E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x08, 0x24, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x10, 0x08, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x08, 0x04, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x06, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x14, 0x2C, 0x18, 0x08, 0x08, + 0x00, 0x00, 0x03, 0x05, 0x05, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1D, 0x08, 0x08, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x08, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x10, 0x1C, 0x32, 0x32, 0x2A, 0x3E, 0x2A, 0x2A, 0x26, 0x26, 0x04, 0x00, + 0x00, 0x10, 0x3C, 0x12, 0x12, 0x0A, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x3C, 0x12, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x28, 0x18, 0x08, 0x0C, 0x0A, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x04, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x04, 0x18, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x27, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x14, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x3E, 0x12, 0x12, 0x0A, 0x1E, 0x0A, 0x0A, 0x06, 0x3E, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1C, 0x32, 0x2A, 0x3E, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1F, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x0F, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1A, 0x26, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x40, + 0x40, 0x20, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x18, 0x24, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x10, 0x0C, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x40, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x14, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x18, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x2C, 0x2A, 0x1A, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x0C, 0x0A, 0x3C, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x03, 0x02, 0x3E, 0x22, 0x12, 0x0A, 0x12, 0x22, 0x22, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x34, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x34, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x09, 0x09, 0x39, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x02, 0x0C, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x20, 0x18, 0x10, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3C, 0x0A, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x10, 0x10, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x34, 0x4A, 0x3E, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x3C, 0x22, 0x5C, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x10, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2C, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x3C, 0x12, 0x0C, + 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x25, 0x15, 0x25, 0x25, 0x26, 0x20, 0x18, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x35, 0x5D, 0x3E, 0x10, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x11, 0x21, 0x21, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x21, 0x11, 0x17, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E, 0x10, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x09, 0x19, 0x29, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x06, 0x01, 0x1D, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x20, 0x10, + 0x00, 0x00, 0x03, 0x02, 0x32, 0x0A, 0x0A, 0x12, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x02, 0x3A, 0x22, 0x12, 0x12, 0x12, 0x0A, 0x3C, 0x00, 0x00, + 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, +}; + +const char _default_font_880_1319[] = { + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x2A, 0x2A, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x2A, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x2A, 0x26, 0x26, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x12, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x24, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x08, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x02, 0x01, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x02, 0x01, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x02, 0x01, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x02, 0x01, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x02, 0x01, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x14, 0x36, 0x00, 0x00, + 0x12, 0x08, 0x24, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x14, 0x14, 0x14, 0x22, 0x22, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x14, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x14, 0x36, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x10, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x12, 0x08, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x04, 0x04, + 0x00, 0x00, 0x1C, 0x22, 0x02, 0x0C, 0x12, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x12, 0x1E, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x14, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x12, 0x1E, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x02, 0x01, 0x20, 0x12, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x00, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x14, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x18, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x02, 0x04, 0x24, 0x34, 0x2A, 0x16, 0x12, 0x10, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x02, 0x02, 0x3E, 0x20, 0x20, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x24, 0x24, 0x24, 0x30, 0x28, 0x28, 0x28, 0x28, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x10, 0x18, 0x24, 0x32, 0x28, 0x20, 0x20, + 0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x34, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x34, 0x20, 0x18, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x12, 0x12, 0x3C, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x04, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x38, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x02, 0x02, 0x0E, 0x12, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x3E, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x08, 0x08, 0x3E, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x07, 0x02, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1A, 0x1A, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x2C, 0x2C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x04, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x10, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x0A, 0x0A, 0x0A, 0x1A, 0x2A, 0x2A, 0x2A, 0x19, 0x00, 0x00, + 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x1F, 0x29, 0x29, 0x29, 0x19, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x04, 0x08, 0x22, 0x32, 0x32, 0x2A, 0x2A, 0x26, 0x26, 0x22, 0x22, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x08, 0x08, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x14, 0x14, 0x14, 0x14, 0x12, 0x12, 0x11, 0x3F, 0x21, 0x21, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x2A, 0x26, 0x26, 0x22, 0x22, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x7E, 0x40, 0x40, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x7E, 0x40, 0x40, + 0x00, 0x00, 0x03, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x32, 0x22, 0x26, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x25, 0x25, 0x25, 0x27, 0x25, 0x25, 0x25, 0x19, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x14, 0x14, 0x14, 0x12, 0x12, 0x3F, 0x21, 0x21, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x24, 0x24, 0x24, 0x24, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x7E, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x7E, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x22, 0x26, 0x2A, 0x2A, 0x2A, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x1F, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x10, 0x08, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0A, 0x1A, 0x2A, 0x2A, 0x2A, 0x19, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x1F, 0x29, 0x29, 0x29, 0x19, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0F, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x08, 0x08, + 0x00, 0x00, 0x14, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0F, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x0F, 0x02, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x39, 0x05, 0x05, 0x05, 0x1F, 0x05, 0x05, 0x05, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x39, 0x05, 0x05, 0x1F, 0x05, 0x05, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x14, 0x14, 0x22, 0x3E, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x3E, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x11, 0x29, 0x29, 0x47, 0x7D, 0x55, 0x55, 0x55, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x29, 0x45, 0x7F, 0x55, 0x55, 0x55, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x22, 0x14, 0x14, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x22, 0x14, 0x08, 0x1C, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x7D, 0x45, 0x29, 0x29, 0x17, 0x39, 0x55, 0x55, 0x55, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7D, 0x45, 0x29, 0x17, 0x39, 0x55, 0x55, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x20, 0x1C, 0x02, 0x1C, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1C, 0x02, 0x1C, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x61, 0x11, 0x11, 0x0A, 0x0A, 0x0A, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x11, 0x11, 0x0A, 0x0A, 0x04, 0x04, 0x00, 0x00, + 0x05, 0x0A, 0x60, 0x11, 0x11, 0x0A, 0x0A, 0x0A, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x0A, 0x14, 0x00, 0x61, 0x11, 0x11, 0x0A, 0x0A, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x05, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x3A, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x3A, 0x20, 0x1C, + 0x00, 0x08, 0x1C, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x1C, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x22, 0x22, 0x22, 0x2A, 0x1C, 0x08, 0x00, + 0x1C, 0x22, 0x08, 0x2A, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x08, 0x2A, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x1C, 0x08, 0x2A, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x1C, 0x08, 0x08, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0C, 0x18, 0x06, 0x0C, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x0F, 0x00, 0x00, 0x3E, 0x36, 0x00, 0x00, 0x07, 0x0F, 0x0C, 0x00, + 0x06, 0x07, 0x03, 0x00, 0x06, 0x1E, 0x1E, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, + 0x22, 0x1C, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x2A, 0x26, 0x26, 0x22, 0x40, 0x20, + 0x00, 0x22, 0x1C, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x40, 0x20, + 0x00, 0x00, 0x02, 0x07, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x07, 0x02, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x2A, 0x1E, 0x12, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x1E, 0x12, 0x02, + 0x20, 0x20, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x0F, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x02, 0x0F, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x2A, 0x6A, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x6A, 0x40, 0x40, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x21, 0x21, 0x15, 0x0D, 0x07, 0x0D, 0x15, 0x21, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x21, 0x15, 0x0D, 0x07, 0x0D, 0x15, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x17, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x17, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x23, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x62, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x62, 0x40, 0x40, + 0x00, 0x00, 0x62, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x62, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x0A, 0x0A, 0x0A, 0x1A, 0x2A, 0x2A, 0x2A, 0x2A, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0A, 0x1A, 0x2A, 0x2A, 0x2A, 0x2A, 0x20, 0x10, + 0x00, 0x00, 0x18, 0x04, 0x12, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x04, 0x12, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x10, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x08, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x10, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x10, 0x10, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x62, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x62, 0x40, 0x40, + 0x00, 0x00, 0x2E, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x7C, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x24, 0x24, 0x24, 0x24, 0x24, 0x7C, 0x40, 0x40, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x60, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x60, 0x40, 0x40, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x3C, 0x28, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x2A, 0x3C, 0x28, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x23, 0x3E, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x23, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x23, 0x3E, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x23, 0x3E, 0x02, 0x02, 0x3C, 0x08, 0x08, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x20, 0x18, + 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x40, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x24, 0x24, 0x24, 0x24, 0x22, 0x40, 0x20, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x20, 0x18, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x40, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x40, 0x20, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x30, 0x10, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x30, 0x10, 0x10, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x22, 0x40, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x40, 0x20, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x28, 0x28, 0x3E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x2A, 0x2A, 0x2A, 0x1C, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x14, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x1C, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x2A, 0x26, 0x26, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00, + 0x14, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x2A, 0x26, 0x26, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x28, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x28, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x1C, + 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x06, 0x04, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x06, 0x04, 0x04, + 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x26, 0x2A, 0x2A, 0x2A, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x26, 0x2A, 0x2A, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1F, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x02, 0x1F, 0x02, 0x02, 0x02, 0x02, 0x01, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x14, 0x14, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x3E, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x11, 0x11, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x28, 0x2E, 0x29, 0x29, 0x29, 0x29, 0x29, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x28, 0x2E, 0x29, 0x29, 0x29, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x08, 0x28, 0x26, 0x28, 0x28, 0x28, 0x28, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x28, 0x26, 0x28, 0x28, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x10, 0x10, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x30, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x10, 0x0C, 0x10, 0x10, 0x30, 0x20, 0x20, + 0x00, 0x00, 0x0E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x0A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x04, 0x04, 0x24, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x04, 0x04, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x20, 0x10, + 0x00, 0x00, 0x2E, 0x2A, 0x2A, 0x2A, 0x12, 0x2A, 0x2A, 0x2A, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x2A, 0x2A, 0x12, 0x2A, 0x2A, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x2A, 0x1A, 0x1A, 0x0E, 0x12, 0x12, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x26, 0x2A, 0x1A, 0x0A, 0x1A, 0x2A, 0x26, 0x02, 0x02, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x3E, 0x0C, 0x0A, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x29, 0x29, 0x3E, 0x0C, 0x0A, 0x31, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x20, 0x20, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x12, 0x2A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x12, 0x2A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x0A, 0x0A, 0x0A, 0x1A, 0x2A, 0x2A, 0x2A, 0x29, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0A, 0x0A, 0x1A, 0x2A, 0x2A, 0x29, 0x20, 0x18, + 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x0A, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x1E, 0x2A, 0x2A, 0x2A, 0x20, 0x18, + 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x62, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x62, 0x40, 0x40, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x62, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x62, 0x40, 0x40, +}; diff --git a/gb.sdl2/src/gb.sdl2.component b/gb.sdl2/src/gb.sdl2.component new file mode 100644 index 00000000..8076ceea --- /dev/null +++ b/gb.sdl2/src/gb.sdl2.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.sdl2 +Implements=EventLoop,OpenGLViewer,ImageIO +Requires=gb.image +State=1 diff --git a/gb.sdl2/src/main.c b/gb.sdl2/src/main.c new file mode 100644 index 00000000..e943f9fd --- /dev/null +++ b/gb.sdl2/src/main.c @@ -0,0 +1,251 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +// Lazyfoo! + +#include "gambas.h" +#include "main.h" +#include "c_image.h" +#include "c_draw.h" +#include "c_window.h" +#include "c_mouse.h" +#include "c_font.h" + +#include "gb_list_temp.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; + +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_Font; + +//------------------------------------------------------------------------- + +static void init_sdl() +{ + uint init = SDL_WasInit(SDL_INIT_EVERYTHING); + const char *error; + + // if audio is defined, sdl was init by gb.sdl2.audio component ! + if (init & SDL_INIT_AUDIO) + { + if (SDL_InitSubSystem(SDL_INIT_VIDEO)) // | SDL_INIT_JOYSTICK)) + { + error = SDL_GetError(); + goto __ERROR; + } + } + else + { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) // | SDL_INIT_JOYSTICK)) + { + error = SDL_GetError(); + goto __ERROR; + } + } + + if (IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG) != (IMG_INIT_JPG | IMG_INIT_PNG)) + { + error = IMG_GetError(); + goto __ERROR; + } + + return; + +__ERROR: + + fprintf(stderr, "gb.sdl2: unable to initialize SDL2: %s\n", error); + abort(); +} + +static void exit_sdl() +{ + uint init; + + if (TTF_WasInit()) + TTF_Quit(); + + IMG_Quit(); + + init = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if audio is defined, gb.sdl2.audio component still not closed ! + if (init & SDL_INIT_AUDIO) + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); + else + SDL_Quit(); +} + +static bool event_loop() +{ + SDL_Event event; + bool ret = FALSE; + + while (SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_QUIT: + ret = TRUE; + break; + case SDL_WINDOWEVENT: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEMOTION: + case SDL_MOUSEWHEEL: + case SDL_KEYDOWN: + case SDL_KEYUP: + case SDL_TEXTINPUT: + WINDOW_handle_event(&event); + ret = TRUE; + break; + } + } + + return ret; +} + +static void my_main(int *argc, char **argv) +{ + char *env; + const char *driver = NULL; + + env = getenv("GB_GUI_PLATFORM"); + if (env && *env) + { + if (!strcasecmp(env, "wayland")) + driver = "SDL_VIDEODRIVER=wayland"; + else if (!strcasecmp(env, "x11")) + driver = "SDL_VIDEODRIVER=x11"; + else + fprintf(stderr, "gb.sdl2: warning: unsupported platform: %s\n", env); + } + + if (!driver) + { + if (getenv("WAYLAND_DISPLAY")) + putenv("SDL_VIDEODRIVER=wayland"); + } + else + putenv((char *)driver); + + init_sdl(); + + CLASS_Window = GB.FindClass("Window"); + CLASS_Image = GB.FindClass("Image"); + CLASS_Font = GB.FindClass("Font"); +} + +static int my_loop() +{ + for(;;) + { + if (!GB.Loop(10) && !WINDOW_list) + break; + event_loop(); + WINDOW_update(); + } + + return 1; +} + +static void my_wait(int duration) +{ + if (duration >= 0) + { + GB.Loop(10); + event_loop(); + WINDOW_update(); + } + else if (duration == -1) + { + GB.Loop(10); + WINDOW_update(); + } + else if (duration == -2) + { + while (!GB.Loop(10) && !event_loop()) + WINDOW_update(); + } +} + +//------------------------------------------------------------------------- + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ImageDesc, + DrawDesc, + WindowDesc, + KeyDesc, + MouseDesc, + FontDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + GB.Component.Load("gb.image"); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + IMAGE.SetDefaultFormat(GB_IMAGE_BGRA); + + GB.Hook(GB_HOOK_MAIN, (void *)my_main); + GB.Hook(GB_HOOK_LOOP, (void *)my_loop); + GB.Hook(GB_HOOK_WAIT, (void *)my_wait); + + return -1; +} + +void EXPORT GB_EXIT() +{ + exit_sdl(); +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + //static bool wasFullscreen = false; + + //if (!SDLcore::GetWindow()) + // return; + + if ((signal == GB_SIGNAL_DEBUG_BREAK) || (signal == GB_SIGNAL_DEBUG_CONTINUE)) + { + /*if (SDLcore::GetWindow()->IsFullScreen()) + { + wasFullscreen = true; + SDLcore::GetWindow()->SetFullScreen(false); + }*/ + } + + if (signal == GB_SIGNAL_DEBUG_CONTINUE) + { + /*if (wasFullscreen) + SDLcore::GetWindow()->SetFullScreen(true);*/ + } +} + diff --git a/gb.sdl2/src/main.h b/gb.sdl2/src/main.h new file mode 100644 index 00000000..669bdd7f --- /dev/null +++ b/gb.sdl2/src/main.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb_list.h" + +#include "gb.geom.h" +#include "gb.image.h" + +#include "SDL.h" +#include "SDL_image.h" +#include "SDL_ttf.h" +#include "SDL_opengl.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +extern GEOM_INTERFACE GEOM; + +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_Font; +#endif + +#define RAISE_ERROR(_msg) GB.Error(_msg ": &1", SDL_GetError()); + +#define SAME_COLORS(_col1, _col2) ((_col1)->r == (_col2)->r && (_col1)->g== (_col2)->g && (_col1)->b == (_col2)->b && (_col1)->a == (_col2)->a) + +#define SDL_COLOR_TO_UINT(_color) ((_color)->b | ((_color)->g << 8) | ((_color)->r << 16) | ((_color)->a << 24)) + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_ARGB +#define DEFAULT_SDL_IMAGE_FORMAT SDL_PIXELFORMAT_BGRA8888 + +#define BMASK 0xFF000000 +#define GMASK 0x00FF0000 +#define RMASK 0x0000FF00 +#define AMASK 0x000000FF + +#else + +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_BGRA +#define DEFAULT_SDL_IMAGE_FORMAT SDL_PIXELFORMAT_ARGB8888 + +#define BMASK 0x000000FF +#define GMASK 0x0000FF00 +#define RMASK 0x00FF0000 +#define AMASK 0xFF000000 + +#endif + +#endif /* __MAIN_H */ + diff --git a/gb.v4l/AUTHORS b/gb.v4l/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.v4l/COPYING b/gb.v4l/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.v4l/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.v4l/ChangeLog b/gb.v4l/ChangeLog new file mode 100644 index 00000000..86f0c1cb --- /dev/null +++ b/gb.v4l/ChangeLog @@ -0,0 +1,241 @@ +0.4.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.94) + +* Fixed a "segmentation fault", when changing user name or password in both HttpClient and FtpClient + +0.4.2 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* FtpClient external interface stabilized. + +* Finished 'Curl' class code, and code sharing between 'HttpClient' and 'FtpClient'. + +0.4.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* Added a new class 'Curl', that is the base for the rest of classes in this component + +* Now HttpClient and FtpClient inherits from 'Curl', so they share a lot of code + +0.4.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* HttpClient : some code improvements so now it is faster,smaller and wastes less memory. + +* Added 'FtpClient' class. + +0.3.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + + * Added two new properties to Socket class. They are read/write, the first is called 'Port'. if + value is zero (Net.Local), connection will try to stablish a Local socket, else a TCP connection + will be stablished. The second, 'HostOrPath' can be a Host or a local path. + + * Socket constructor has now no parameters. + + * Conect method has now two optional parameters. The first can override 'HostOrPath' property, and the + second overrides 'Port' property + +0.3.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + +* Added code to let the IDE show icons for all net classes. +* Removed lots of code to make the component lighter and faster. + +0.2.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.90) + +* Fixed a bug in UdpClient : segmentation fault when reading or writing data +* Fixed a bug in UDPServerClient example : trying to use CLOSE when UdpClient is not active +* defined before to allow compile on FreeBSD + +0.2.2 (ALPHA - compiles on Gambas 0.81) + +* 'NetCode' and 'AdvancedCode' classes changed to 'Net' +* Proxy properties from 'HttpClient' has been added into a new class called 'Proxy' +* 'ReturnCode' and 'ReturnString' properties are now called 'Code' and 'Reason' +* This version should compile now using libcurl 7.10.3, 7.10.4, 7.10.5, 7.10.6, 7.10.7, 7.10.8 and 7.11.0 +* now is after in all files to allow compile it on FreeBSD + +0.2.1 (ALPHA - compiles on Gambas 0.81) + +* Examples Updated +* New HttpClient interface defined +* 'AdvancedCode' class provides constants for 'Net.Advanced' component + +0.2.0 (ALPHA - compiles on Gambas 0.80) + +* Examples updated +* Modifications in configuration scripts to detect libcurl +* 'NetCode' provide the constants needed to work with all network classes +* 'Net advanced' includes not : CHttpClient, and will include other classes using libcurl +* 'Net' includes now : Socket, SerialPort,ServerSocket,UdpSocket,DnsClient and NetCode, + that is, basic networking stuff +* 'Net' component splitted it two components: 'Net' and 'Advanced net' + +0.1.4 (STABLE - Gambas 0.80) + +BM - Changes to allow the component compile on systems without MSG_NOSIGNAL flag + +0.1.3 (Gambas 0.74) + +BM - 20 Dec 2003 - Let component compile with gcc 2.95 + +0.1.2 + +* Added HTTP proxy support for 'HttpClient' class + +0.1.1 (Gambas 0.73) + +* Corrected bug in 'HttpClient' class that didn't convert correctly + document query to HTTP codification +* Added support for Solaris + +0.1.0 + +* Added 'HttpClient.Local' constant as sinonym of 'HttpClient.Unix' +* Changed 'HttpClient.Inet' constant to 'HttpClient.Internet' + +0.1.0pre7 + +* Using sys/un.h instead of linux/un.h in 'Socket' and 'ServerSocket' + classes +* Added option 'SO_REUSEADDR' to socket in 'ServerSocket' class +* 'ServerSocket' example fixed +* Documentation fixed + + +0.1.0pre6 + +* Memory allocation bug fixed in 'Socket' class +* Memory allocation bug fixed in 'SerialPort' class +* Lots of internal code reorganization +* Some memory optimizations in 'Socket' and 'ServerSocket' +* UDPServerClient example fixed +* ServerSocket example fixed + +0.1.0pre5 + +* Constant names changed in all classes to be more simple +* Parameters in methods and events does not include its type + as a prefix now +* 'ConnectUnix' and 'ConnectSocket' methods merged in one + method : 'Connect' +* 'ServerSocket' 'SocketType' property changed to 'Type' +* 'ConnectionRequest' event in 'ServerSocket' changed to 'Connection' +* 'Accept' method from 'ServerSocket' does not take any parameter + now +* 'HostFound' events from 'Socket' and 'HttpClient' changed to 'Found' +* Datagram class now inherits from '.Stream' +* Removed 'DataPacket' class +* New properties 'SourceHost', 'SourcePort', 'TargetHost', 'TargetPort' + in 'Datagram' class +* New method 'Peek' in 'Datagram' class +* Removed methods 'Stop','Receive' and 'Send' from 'Datagram' class +* 'Start' method from 'Datagram' Changed to 'Bind' +* 'Datagram' class changed its name to 'UdpSocket' +* Documentation updated +* Examples updated + + + +0.1.0pre4 + +* Removed Close() method from 'Socket' and 'SerialPort' classes, + translated to standard stream methods +* 'LookingHostIP' constant in 'Socket' and 'HttpClient' classes, + changed to 'LookingUpHostIP' +* Examples updated +* Documentation updated + + +0.1.0pre3 + + +* 'Accept' method from 'ServerSocket' changed its way to + act. Now it returns a new 'Socket' and accpets an optional + Event Handler +* Old method 'Receive' from HttpClient, splitted + in two new methods : 'Receive' and 'Peek' +* Now 'Socket' class inherits from '.Stream' +* Now 'SerialPort' class inherits from 'Stream' +* Removed 'Send' and 'Receive' methods from 'Socket' +* Removed 'Send' and 'Receive' methods from 'SerialPort' +* Added 'Peek' method to 'Socket' +* Adaptations from generic stream methods to 'Socket' characteristics +* Adaptations from generic stream methods to 'SerialPort' characteristics +* Documentation updated +* Examples updated + + +0.1.0pre2 + +* Added 0.0.17 = 0.1.0pre1 to CHANGELOG file +* 'SocketError' event from Socket and Datagram now is called 'Error' +* 'Error' codes are now negative values in Status property +* 'Error' events from classes which support it, now takes + zero parameters +* References to class names removed from 'GB.Error()' messages +* Class 'ClientSocket' changed its name to 'Socket' +* 'RemoteHostIP' and 'LocalHostIP' properties from 'Socket' + class changed its name to 'RemoteHost' and 'LocalHost' +* Constants from ServerSocket changed its name "TypeTCP"->"iNet", + "TypeUnix"->"Unix" +* 'DataAvailable' event changed its name to 'Read' in all classes + which supports it. +* 'Wait' method from ServerSocket changed to 'Pause' +* 'Path' property from 'Socket' class now returns IP:Port when + connected using TCP sockets. +* 'CloseSocket' method from 'Socket' class changed to 'Close' +* 'Socket' Class has a new constructor. You can both use no + parameters, or pass a string as parameter, which can be : + 'HostName:Port' or 'HostIP:Port' for TCP connections, or + 'Absolute_Path' for Unix connections. +* 'ServerSocket' Class has a new constructor. You can both use no + parameters, or pass a string and a number as parameter, which can be : + ':Port' for TCP connections, or 'Absolute_Path' for Unix connections. +* 'Datagram' Class has a new constructor. You can both use no + parameters, or pass an integer as Port value to start its + work. +* 'MaxConn' parameter from 'Listen' method if ServerSocket class + now is optional. +* 'SendData' and 'ReceiveData' changed to 'Send' and 'Receive' in all + classes containing that methods. + + +0.0.17 = 0.1.0pre1 + +* Removed prefix 'Is' in constats beginning with that prefix. +* 'GetData' method changed its name to 'ReceiveData' is some + classes +* 'Connected' event from 'ClientSocket' changed its name + to 'Connect'. +* Examples updated. +* HttpClient class finished. +* HttpClient documentation added. + + +0.0.16 + +* Added CHANGELOG file. +* Changed component name from 'networking' to 'net'. +* Changed 'sData' property from DataPacket to 'Data'. +* Documentation updated for DataPacket class. +* Changed example names. +* Constant names and values have changed in ClientSocket, some + new constants have been added. +* ConnectSocket and ConnectUnix methods from ClientSocket + have changed, now they not return any value, as error + codes are managed by "SocketError" event. +* Documentation updated for ClientSocket class. +* Example "ClientSocket" updated. +* DnsClient has two new constants. +* Documentation updated for DnsClient class. +* Some ServerSocket constants have changed its name. +* New Constants added to ServerSocket. +* A new static property 'UnixMaxPath' added to ServerSocket. +* ServerSocket 'Listen' method does not return any value now. +* ServerSocket example updated. +* Documentation updated for ServerSocket class. +* Datagram class has new constants. +* Datagram 'Start' method does not return any value now. +* Datagram 'SocketError' event implemented. +* Documentation updated for Datagram class. +* Example "UDPServerClient" updated. +* SerialPort 'GetData' method now is called 'ReceiveData'. +* Added constants to SerialPort class. +* Documentation updated for SerialPort class. +* SerialPort Example updated. diff --git a/gb.v4l/INSTALL b/gb.v4l/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.v4l/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.v4l/Makefile.am b/gb.v4l/Makefile.am new file mode 100644 index 00000000..547eb54e --- /dev/null +++ b/gb.v4l/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @V4L_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h orig diff --git a/gb.v4l/NEWS b/gb.v4l/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.v4l/README b/gb.v4l/README new file mode 100644 index 00000000..23bf9803 --- /dev/null +++ b/gb.v4l/README @@ -0,0 +1,4 @@ +This code is partially based in the code written and released under GPL by +Nick Andrew , for the "video-capture" program. +You can find the original sources of that program in the "orig" folder +in these sources. diff --git a/gb.v4l/acinclude.m4 b/gb.v4l/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.v4l/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.v4l/component.am b/gb.v4l/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.v4l/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.v4l/configure.ac b/gb.v4l/configure.ac new file mode 100644 index 00000000..5f295dd2 --- /dev/null +++ b/gb.v4l/configure.ac @@ -0,0 +1,26 @@ +dnl ---- configure.ac for gb.v4l + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-v4l],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.v4l) +LT_INIT + +GB_COMPONENT_PKG_CONFIG( + v4lconvert, V4LCONVERT, gb.v4l, [src], + libv4lconvert) + +GB_COMPONENT( + v4l, V4L, gb.v4l, [src], + [GB_FIND(png.h jpeglib.h linux/videodev2.h, $prefix /usr/local/lib /usr/local /usr/lib /usr, include)], + [GB_FIND(libpng.$SHLIBEXT libjpeg.$SHLIBEXT, $prefix /usr/local /usr, lib)], + [$C_LIB -ljpeg -lpng]) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/gb.v4l/gambas.h b/gb.v4l/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.v4l/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.v4l/gb.image.h b/gb.v4l/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.v4l/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.v4l/gb_common.h b/gb.v4l/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.v4l/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.v4l/m4 b/gb.v4l/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.v4l/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.v4l/orig/video-capture-0.2.tar.gz b/gb.v4l/orig/video-capture-0.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..8e1a3213088432479dee03cf4a708d60783a232d GIT binary patch literal 10049 zcmV-HC%)JpiwFR&yeKg^y|Y2 zsQ@KH(r5wG)A{c2-dk1L5X3e~`pud6oS6ox>ej8h-(MBE)Iac%W%KX8;9s-VY(CxJ z|E}3=wthKirvHn7t^L;CcgF{IbI= z$K-eQNjSB|+=0nwmci?j>{la88NP?MH*rI^?%CRTx7QzBT%Vl;wl%GXk!{x<9v(Jk zy5E@3=lH)mihoCUOrOv3WyAB`_uL4BMvmjN7Z>f9y_22uSM4{Q!A1A^-)=fDwTpB4 z*naQvv8L&^eOSuP^tsLTfFG9X@{^|3<7cUDCX6c?WC-p2P?1ur*1@vs<3F_050%UI z-#b+ndA6&YS}AlRW5SYk#PdU~ZO-Dl`CjkW{I58>TXucu9kZ($LEY9u>vhSy?A(RLpf%a_j?GECNr&q zjKQn_FlM8B$Vg84t6lLg`4ejXvTIQMf4VCEx0<_qN&J8M^Z>{2w_4xg|JV4_7-MZF zqbUnrHutTNGhMh16RyWbzCPtl`VfvD!vp-T4YpRdsgaGN8R`7x8 zAsazhXMxAL$tqTz*V*oY9MuT8;X0-ybJM=4);0We?#>+%7a9I-v)@|3XPp_+lJa6$ z33wVZKI8r(oB${Btli&JffJKlsDD6CLu<-y-3wqt_%@(LuV5ErJ+xdWS$6ODTMp|v zxX2Zl!Owvb7<_=;#sl}42Tt56r!hF_%=H$&HJ*g5a#m%#VEZ*1$J&(G*fAQ}uenrr zO0uwm(6@$B2;z$%Ov5H7oQ2$<1|T>3^7866yNQOjWv~m-nPZ4G`|v6wwOD;<_(;#T zMxgM3{R4&>dRRApu8|dl++o87((v`b(0?6I@9HLu1XX!36W65|u*-kU_Y8gdvl2_4 z^*Bjo5{BMkgAzCmh4~HQsvEL7EExqH?1Gfy0>eA*Xe6x+6$A_D()32YJB^J}rEH)h z?6bx|GOP0(vY%LCip6NJ)tp>Wjp_nFs2UHeMm~PKjE~WsVyn&*20fl1tj* z#Er%i=7MT{wOqjX9Tg{J2d#r0 z&N*45un&5XGZNv6ZzovsO!JyMV9Y`xMdKsK6K$B5JfU? zknaA4lZZl?NsFlODHfu&#|1RIV5 zwT~RDsBKt}$OITIVTkF13-g^}G#YVo0x2u7)diVVm>1N9C4+Q{EYV62 zzy%Z6!q*PJ3#EI_Nx}FK81c!H!R(pPvcZFR2c8c!!Jx!EX&tZHeb#@~VK=weFK^qI z?5uso&adP6sZ0uq*eaKR^LyTufvl(^mqTsW^=c_LVbnh(2#08~0jdUUzD8<7B@k0C zxE6#XJ(OXXF;K7vI>s&~U=>>KN3$+hQ(xbEWA?0?XVG#PG##|T!1{v+H4%6W?uR>?|NeJ8ZfDn=~LHa)>*9ii~nJI2l+zMTeRqBy=ns zdm3S=A!7#)^;mzv8rizfA@b`YELaBROqs*+FBLwSbUB|0`CK>2l4W$1p*=-iXjoU2 z4A+Oa?LqiNVda*CnfbgHyC^zBRxcRHC;E)|7`te0A1L{NW$9zoFfL4S7|a#Tlq!k3 zQrHWuVMtAC&8fZ+nZf5^sQ3W0&Q3VNT~ZG7lZ}{>ZtqBk0$U2KvBN67KCXkFxbifL ze=3atHzE?Kh(%i=xuK9ma!Bo$Plc*biq)8Hj7tv5T9yhTepn7#nlffqp-lr%I@D=y z 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 X{5(I;&-3&A{MzS#Tj9>~0H6Q>_ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + +Collection of conversion routines from various sources .. +All provided under GPL or "as-is" licenses. + +*/ + +#include +#include + +int convert_rgb_to_yuv_pixel(int r, int g, int b) +{ + unsigned int pixel32 = 0; + unsigned char *pixel = (unsigned char *)&pixel32; + int y, u, v; + + y = 0.299 * (r - 128) + 0.587 * (g - 128) + 0.114 * (b - 128) + 128; + u = - 0.147 * (r - 128) - 0.289 * (g - 128) + 0.436 * (b - 128) + 128; + v = 0.615 * (r - 128) - 0.515 * (g - 128) - 0.100 * (b - 128) + 128; + + if(y > 255) y = 255; + if(u > 255) u = 255; + if(v > 255) v = 255; + if(y < 0) y = 0; + if(u < 0) u = 0; + if(v < 0) v = 0; + + pixel[0] = y; + pixel[1] = u; + pixel[2] = v; + + return pixel32; +} + + +int convert_rgb_to_yuv_buffer(unsigned char *rgb, unsigned char *yuv, unsigned int width, unsigned int height) +{ + unsigned int in, out = 0; + unsigned int pixel32; + int y0, u0, v0, y1, u1, v1; + + for(in = 0; in < width * height * 3; in += 6) { + pixel32 = convert_rgb_to_yuv_pixel(rgb[in], rgb[in + 1], rgb[in + 2]); + y0 = (pixel32 & 0x000000ff); + u0 = (pixel32 & 0x0000ff00) >> 8; + v0 = (pixel32 & 0x00ff0000) >> 16; + + pixel32 = convert_rgb_to_yuv_pixel(rgb[in + 3], rgb[in + 4], rgb[in + 5]); + y1 = (pixel32 & 0x000000ff); + u1 = (pixel32 & 0x0000ff00) >> 8; + v1 = (pixel32 & 0x00ff0000) >> 16; + + yuv[out++] = y0; + yuv[out++] = (u0 + u1) / 2; + yuv[out++] = y1; + yuv[out++] = (v0 + v1) / 2; + } + + return 0; +} + +int convert_yuv_to_rgb_pixel(int y, int u, int v) +{ + unsigned int pixel32 = 0; + unsigned char *pixel = (unsigned char *)&pixel32; + int r, g, b; + + r = y + (1.370705 * (v-128)); + g = y - (0.698001 * (v-128)) - (0.337633 * (u-128)); + b = y + (1.732446 * (u-128)); + + if(r > 255) r = 255; + if(g > 255) g = 255; + if(b > 255) b = 255; + if(r < 0) r = 0; + if(g < 0) g = 0; + if(b < 0) b = 0; + + pixel[0] = r * 220 / 256; + pixel[1] = g * 220 / 256; + pixel[2] = b * 220 / 256; + + return pixel32; +} + + +int convert_yuv_to_rgb_buffer(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height) +{ + unsigned int in, out = 0; + unsigned int pixel_16; + unsigned char pixel_24[3]; + unsigned int pixel32; + int y0, u, y1, v; + + for(in = 0; in < width * height * 2; in += 4) { + pixel_16 = + yuv[in + 3] << 24 | + yuv[in + 2] << 16 | + yuv[in + 1] << 8 | + yuv[in + 0]; + + y0 = (pixel_16 & 0x000000ff); + u = (pixel_16 & 0x0000ff00) >> 8; + y1 = (pixel_16 & 0x00ff0000) >> 16; + v = (pixel_16 & 0xff000000) >> 24; + + pixel32 = convert_yuv_to_rgb_pixel(y0, u, v); + pixel_24[0] = (pixel32 & 0x000000ff); + pixel_24[1] = (pixel32 & 0x0000ff00) >> 8; + pixel_24[2] = (pixel32 & 0x00ff0000) >> 16; + + rgb[out++] = pixel_24[0]; + rgb[out++] = pixel_24[1]; + rgb[out++] = pixel_24[2]; + + pixel32 = convert_yuv_to_rgb_pixel(y1, u, v); + pixel_24[0] = (pixel32 & 0x000000ff); + pixel_24[1] = (pixel32 & 0x0000ff00) >> 8; + pixel_24[2] = (pixel32 & 0x00ff0000) >> 16; + + rgb[out++] = pixel_24[0]; + rgb[out++] = pixel_24[1]; + rgb[out++] = pixel_24[2]; + } + + return 0; +} + +static inline void move_420_block (int yTL, int yTR, int yBL, int yBR, int u, + int v, int rowPixels, unsigned char *rgb, + int bits); + +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) + +void +yuv420p_to_rgb (unsigned char *image, unsigned char *image2, int x, int y, int z) { + const int numpix = x * y; + const int bytes = z; /* (z*8) >> 3; */ + int i, j, y00, y01, y10, y11, u, v; + unsigned char *pY = image; + unsigned char *pU = pY + numpix; + unsigned char *pV = pU + numpix / 4; + + for (j = 0; j <= y - 2; j += 2) { + for (i = 0; i <= x - 2; i += 2) { + y00 = *pY; + y01 = *(pY + 1); + y10 = *(pY + x); + y11 = *(pY + x + 1); + u = (*pU++) - 128; + v = (*pV++) - 128; + + move_420_block (y00, y01, y10, y11, u, v, x, image2, z * 8); + + pY += 2; + image2 += 2 * bytes; + } + pY += x; + image2 += x * bytes; + } +} + +void move_420_block (int yTL, int yTR, int yBL, int yBR, int u, int v, + int rowPixels, unsigned char *rgb, int bits) +{ + const int rvScale = 91881; + const int guScale = -22553; + const int gvScale = -46801; + const int buScale = 116129; + const int yScale = 65536; + int r, g, b; + + g = guScale * u + gvScale * v; + if (1) { + r = buScale * u; + b = rvScale * v; + } else { + r = rvScale * v; + b = buScale * u; + } + + yTL *= yScale; + yTR *= yScale; + yBL *= yScale; + yBR *= yScale; + + if (bits == 24) { + /* Write out top two pixels */ + rgb[0] = LIMIT (b + yTL); + rgb[1] = LIMIT (g + yTL); + rgb[2] = LIMIT (r + yTL); + + rgb[3] = LIMIT (b + yTR); + rgb[4] = LIMIT (g + yTR); + rgb[5] = LIMIT (r + yTR); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 3 * rowPixels; + rgb[0] = LIMIT (b + yBL); + rgb[1] = LIMIT (g + yBL); + rgb[2] = LIMIT (r + yBL); + + rgb[3] = LIMIT (b + yBR); + rgb[4] = LIMIT (g + yBR); + rgb[5] = LIMIT (r + yBR); + } else if (bits == 16) { + /* Write out top two pixels */ + rgb[0] = ((LIMIT (b + yTL) >> 3) & 0x1F) + | ((LIMIT (g + yTL) << 3) & 0xE0); + rgb[1] = ((LIMIT (g + yTL) >> 5) & 0x07) + | (LIMIT (r + yTL) & 0xF8); + + rgb[2] = ((LIMIT (b + yTR) >> 3) & 0x1F) + | ((LIMIT (g + yTR) << 3) & 0xE0); + rgb[3] = ((LIMIT (g + yTR) >> 5) & 0x07) + | (LIMIT (r + yTR) & 0xF8); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 2 * rowPixels; + + rgb[0] = ((LIMIT (b + yBL) >> 3) & 0x1F) + | ((LIMIT (g + yBL) << 3) & 0xE0); + rgb[1] = ((LIMIT (g + yBL) >> 5) & 0x07) + | (LIMIT (r + yBL) & 0xF8); + + rgb[2] = ((LIMIT (b + yBR) >> 3) & 0x1F) + | ((LIMIT (g + yBR) << 3) & 0xE0); + rgb[3] = ((LIMIT (g + yBR) >> 5) & 0x07) + | (LIMIT (r + yBR) & 0xF8); + } +} + diff --git a/gb.v4l/src/CWebcam.c b/gb.v4l/src/CWebcam.c new file mode 100644 index 00000000..cb86c0b0 --- /dev/null +++ b/gb.v4l/src/CWebcam.c @@ -0,0 +1,1906 @@ +/*************************************************************************** + + CWebcam.c + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBCAM_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif + +#ifdef EXTERN +#undef EXTERN +#endif + +#ifdef INLINE +#undef INLINE +#endif + +#include "main.h" +#include "CWebcam.h" + +#define DEF_WIDTH 320 +#define DEF_HEIGHT 240 +// default colour depth (changing this will break everything) +#define DEF_DEPTH 3 + +#define FMT_UNKNOWN 0 +#define FMT_PPM 1 +#define FMT_PNG 2 +#define FMT_JPEG 3 +#define FMT_DEFAULT FMT_PNG + +#define IN_TV 0 +#define IN_COMPOSITE1 1 +#define IN_COMPOSITE2 2 +#define IN_SVIDEO 3 +#define IN_DEFAULT IN_COMPOSITE1 + +#define NORM_PAL 0 +#define NORM_NTSC 1 +#define NORM_SECAM 2 +#define NORM_AUTO 3 +#define NORM_DEFAULT NORM_PAL + +#define QUAL_DEFAULT 80 + +GB_STREAM_DESC VideoStream = +{ + .open = Video_stream_open, + .close = Video_stream_close, + .read = Video_stream_read, + .write = Video_stream_write, + .seek = Video_stream_seek, + .tell = Video_stream_tell, + .flush = Video_stream_flush, + .eof = Video_stream_eof, + .lof = Video_stream_lof, + .handle = Video_stream_handle +}; + +extern bool gv4l2_debug_mode; // ++ + +/*********************************************************************************** + + Camera setup + +************************************************************************************/ + +static int vd_ioctl(video_device_t *vd, int cmd, void *arg) +{ + return ioctl(vd->dev, cmd, arg); +} + +static void vd_close(video_device_t *vd) +{ + if (vd->frame_buffer) + { + if (vd->use_mmap) munmap(vd->frame_buffer, vd->vmbuf.size); + else GB.Free(POINTER(&vd->frame_buffer)); + } + close(vd->dev); +} + +static video_device_t *vd_setup(int width, int height, int depth, int dev) +{ + video_device_t *vd; + + GB.Alloc(POINTER(&vd),sizeof(video_device_t)); + + vd->width = width; + vd->height = height; + vd->depth = depth; + vd->buffer_size = width * height * depth; + vd->dev = dev; + vd->use_mmap = 0; + vd->capturing = 0; + vd->frame_buffer=NULL; + + return vd; +} + +static int vd_get_capabilities(video_device_t *vd) +{ + if (vd_ioctl(vd, VIDIOCGCAP, &vd->vcap)) return 0; + + if (!(vd->vcap.type & VID_TYPE_CAPTURE)) vd->use_mmap = 0; + else vd->use_mmap = 1; + + if (vd->width > vd->vcap.maxwidth) vd->width=vd->vcap.maxwidth; + if (vd->width < vd->vcap.minwidth) vd->width=vd->vcap.minwidth; + if (vd->height > vd->vcap.maxheight) vd->height=vd->vcap.maxheight; + if (vd->height < vd->vcap.minheight) vd->height=vd->vcap.minheight; + + return 1; +} + + +// -- int vd_setup_capture_mode(video_device_t *vd) + +static int vd_setup_capture_mode(CWEBCAM *_object) // ++ +{ + video_device_t *vd = DEVICE; + + if (!vd_get_capabilities(vd)) return 0; + + // See if we can use mmap (to avoid copying data around) + // VIDIOCGMBUF tells us how many frames it is going to buffer + // for us, and we have to use them all!!! ??? + if (vd_ioctl(vd, VIDIOCGMBUF, &vd->vmbuf)) + { + if (vd->use_mmap) + { + if (vd->frame_buffer) + { + munmap(vd->frame_buffer, vd->vmbuf.size); + vd->frame_buffer=NULL; + } + vd->use_mmap = 0; + } + + // Issue VIDIOCGWIN to tell the driver what geometry we + // expect from read() + if (!vd_ioctl(vd, VIDIOCGWIN, &vd->vwin)) + { + vd->vwin.width = vd->width; + vd->vwin.height = vd->height; + if (vd_ioctl(vd, VIDIOCSWIN, &vd->vwin)) return 0; + if (vd_ioctl(vd, VIDIOCSWIN, &vd->vwin)) return 0; + vd->buffer_size = vd->height * vd->width; + } + + if (vd->frame_buffer) GB.Free(POINTER(&vd->frame_buffer)); + if (THIS->frame) GB.Free(POINTER(&THIS->frame)); // ++ + GB.Alloc(POINTER(&vd->frame_buffer),vd->buffer_size); + GB.Alloc(POINTER(&THIS->frame),vd->height * vd->width * 4); // ++ + return 1; + } + + // mmap is okay! + if (!vd->use_mmap) + { + if (vd->frame_buffer) GB.Free(POINTER(&vd->frame_buffer)); + vd->use_mmap = 1; + } + + vd->frame_buffer = mmap(0, vd->vmbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->dev, 0); + vd->vmmap.format = VIDEO_PALETTE_RGB24; // KLUDGE ... + vd->vmmap.frame = 0; // Start at frame 0 + vd->vmmap.width = vd->width; + vd->vmmap.height = vd->height; + + if (THIS->frame) GB.Free(POINTER(&THIS->frame)); // ++ + GB.Alloc(&THIS->frame, vd->height * vd->width * 4); // ++ + + ioctl(vd->dev, VIDIOCGPICT, &vd->videopict); //++ Recover camera palette + vd->vmmap.format = vd->videopict.palette; //++ Save for future ref + return 1; +} + +static int vd_setup_video_source(video_device_t *vd, int input, int norm) { + + vd->vchan.channel = input; // Query desired channel + + if (vd_ioctl(vd, VIDIOCGCHAN, &vd->vchan)) return 0; + + // Now set the channel and the norm for this channel + + vd->vchan.norm = norm; + if (vd_ioctl(vd, VIDIOCSCHAN, &vd->vchan)) return 0; + + // KLUDGE ... the API leaves colour settings and tuning undefined + // after a channel change + return 1; +} + +/*********************************************************************************** + + Image capture + +************************************************************************************/ + +static void put_image_jpeg(char *image, int width, int height, int quality, int frame,FILE *fd) +{ + int y, x, line_width; + JSAMPROW row_ptr[1]; + struct jpeg_compress_struct cjpeg; + struct jpeg_error_mgr jerr; + char *line; + + GB.Alloc( POINTER(&line) ,width * 3); + if (!line) + return; + cjpeg.err = jpeg_std_error(&jerr); + jpeg_create_compress (&cjpeg); + cjpeg.image_width = width; + cjpeg.image_height= height; + cjpeg.input_components = 3; + cjpeg.in_color_space = JCS_RGB; + jpeg_set_defaults (&cjpeg); + + jpeg_set_quality (&cjpeg, quality, TRUE); + cjpeg.dct_method = JDCT_FASTEST; + jpeg_stdio_dest (&cjpeg, fd); + + jpeg_start_compress (&cjpeg, TRUE); + + row_ptr[0] = (JSAMPROW)line; + line_width = width * 3; + for ( y = 0; y < height; y++) { + for (x = 0; x < line_width; x+=3) { + line[x] = image[x+2]; + line[x+1] = image[x+1]; + line[x+2] = image[x]; + } + jpeg_write_scanlines (&cjpeg, row_ptr, 1); + image += line_width; + } + jpeg_finish_compress (&cjpeg); + jpeg_destroy_compress (&cjpeg); + GB.Free( POINTER(&line) ); + +} + +static void put_image_png(char *image, int width, int height, int frame, FILE *fd) +{ + int y; + char *p; + png_infop info_ptr; + png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (!png_ptr) + return; + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) + return; + + png_init_io (png_ptr, fd); + png_set_IHDR (png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_bgr (png_ptr); + png_write_info (png_ptr, info_ptr); + p = image; + for (y = 0; y < height; y++) { + png_write_row (png_ptr, (unsigned char*)p); + p+=width*3; + } + png_write_end (png_ptr, info_ptr); +} + +/* + * write ppm image to stdout + */ +#define WRITE_MODE "w" + +static void put_image_ppm_buffer(char *image, int width, int height, int frame, int *len,void *_object) +{ + int x; + int htot=width*height; + unsigned char *p = (unsigned char *)image; + unsigned char *bp; + + *len=(3 * htot)+15; + if (!THIS->membuf) GB.Alloc(POINTER(&THIS->membuf),((*len)*sizeof(unsigned char*))); + sprintf((char*)THIS->membuf, "P6\n%d %d\n%d\n", width, height, 255); + bp=THIS->membuf+strlen((const char*)THIS->membuf); + for (x = 0; x < htot; x++) { + *bp++ = p[2]; + *bp++ = p[1]; + *bp++ = p[0]; + p += 3; + } +} + +static void put_image_ppm(char *image, int width, int height, int binary, int frame,FILE *out_fp) +{ + int x, y, ls=0; + unsigned char *p = (unsigned char *)image; + + if (!binary) + { + fprintf(out_fp, "P3\n%d %d\n%d\n", width, height, 255); + for (x = 0; x < width; x++) + { + for (y = 0; y < height; y++) + { + fprintf(out_fp, "%03d %03d %03d ", p[2], p[1], p[0]); + p += 3; + if (ls++ > 4) + { + fprintf(out_fp, "\n"); + ls = 0; + } + } + } + fprintf(out_fp, "\n"); + } + else + { + unsigned char buff[3 * width * height]; + unsigned char *bp = buff; + + fprintf(out_fp, "P6\n%d %d\n%d\n", width, height, 255); + for (x = 0; x < width * height; x++) { + *bp++ = p[2]; + *bp++ = p[1]; + *bp++ = p[0]; + p += 3; + } + fwrite(buff, width * height, 3, out_fp); + } +} + +//unsigned char * vd_get_image(video_device_t *vd) + +static unsigned char *vd_get_image(CWEBCAM *_object) +{ + int len; + video_device_t *vd; + + vd = DEVICE; + + if (vd->use_mmap) { + if (!vd->capturing) { + + int i; + // Queue requests to capture successive frames + for (i = 0; i < vd->vmbuf.frames; ++i) { + vd->vmmap.frame = i; + if(vd_ioctl(vd, VIDIOCMCAPTURE, &vd->vmmap)) + return 0; + } + // And start reading from zero + vd->vmmap.frame = 0; + vd->capturing = 1; + } + // VIDIOCSYNC causes the driver to block until the specified + // frame is completely received + if (ioctl(vd->dev, VIDIOCSYNC, &vd->vmmap.frame)) return 0; + gv4l1_process_image (THIS,vd->frame_buffer + vd->vmbuf.offsets[vd->vmmap.frame]); + + //vd_post_process(vd,vd->frame_buffer + vd->vmbuf.offsets[vd->vmmap.frame]); + return THIS->frame; + + // Return the buffer, cause it should contain an image + //return vd->frame_buffer + vd->vmbuf.offsets[vd->vmmap.frame]; + } + + // Otherwise, we have to read the right number of bytes + + len = read(vd->dev, vd->frame_buffer, vd->buffer_size); + if (len <= 0) { + return 0; + } + + if (len != vd->buffer_size) return 0; + + return vd->frame_buffer; +} + +static int vd_image_done(video_device_t *vd) +{ + if (vd->use_mmap) + { + // vd->vmmap.frame contains the index of the recently-used buffer + // So tell the driver to reuse this one for the next frame + + if (ioctl(vd->dev, VIDIOCMCAPTURE, &vd->vmmap)) return 0; + + // Now cycle the frame number, so we sync the next frame + if (++vd->vmmap.frame >= vd->vmbuf.frames) { + vd->vmmap.frame = 0; + } + } + + return 1; +} + +/*********************************************************************************** + +Stream interface + +************************************************************************************/ + +static int fill_buffer(void *_object) +{ + char *buf; + int w,h; + + // -- buf=(char*)vd_get_image(DEVICE); + buf=(char*)vd_get_image(THIS); // ++ + if (!buf) return -1; + w=DEVICE->vmmap.width; + h=DEVICE->vmmap.height; + vd_image_done(DEVICE); + put_image_ppm_buffer (buf,w,h,0,&THIS->gotframe,_object); + THIS->posframe=0; + return 0; +} + +int Video_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) + if ( fill_buffer(_object) ) return -1; + + if ((len+THIS->posframe)>THIS->gotframe) return -1; + memcpy (buffer,THIS->membuf+THIS->posframe,len); + THIS->posframe+=len; + return 0; +} + +int Video_stream_eof(GB_STREAM *stream) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) return 0; + + if (THIS->gotframe<=THIS->posframe) return -1; + return 0; +} + +int Video_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) + if ( fill_buffer(_object) ) return -1; + + *len=(long long)THIS->gotframe; + return 0; +} + +int Video_stream_seek(GB_STREAM *stream, int64_t pos, int whence) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) + if ( fill_buffer(_object) ) return -1; + + if (pos<0) return -1; + THIS->posframe=pos; + return 0; +} + +int Video_stream_tell(GB_STREAM *stream, int64_t *pos) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + *pos=(long long)THIS->posframe; + return 0; + +} + +int Video_stream_flush(GB_STREAM *stream) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + THIS->gotframe=0; + THIS->posframe=0; + return 0; +} + +int Video_stream_open(GB_STREAM *stream, const char *path, int mode, void *data) +{ + return -1; +} + +int Video_stream_close(GB_STREAM *stream) +{ + return -1; +} + +int Video_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + return -1; +} + +int Video_stream_handle(GB_STREAM *stream) +{ + return 0; +} + +/*********************************************************************************** + + Gambas interface + +************************************************************************************/ + +int CWEBCAM_check(void *_object) +{ + //if((!DEVICE)&&(!THIS->is_v4l2)) return TRUE; + if(!THIS->device) return TRUE; // ++ V4L2 + return FALSE; +} + +static void handle_min(void *_object, int min) +{ + GB.ReturnInteger(THIS->is_v4l2 ? min : 0); +} + +static void handle_max(void *_object, int max) +{ + GB.ReturnInteger(THIS->is_v4l2 ? max : 65535); +} + +static void handle_default(void *_object, int min, int max, int def) +{ + if (!THIS->is_v4l2) + GB.ReturnInteger(32767); + else + { + if (!def) + GB.ReturnInteger((max - min) / 2); + else + GB.ReturnInteger(def); + } +} + +BEGIN_PROPERTY(VideoDevice_Contrast) + + if (!THIS->is_v4l2 ) + { + vd_ioctl(DEVICE, VIDIOCGPICT, &DEVICE->videopict); + + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.contrast); + else + { + DEVICE->videopict.contrast=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_contrast(THIS, -1)); + else + gv4l2_contrast(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ContrastMax) + + handle_max(THIS, THIS->contrast_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ContrastMin) + + handle_min(THIS, THIS->contrast_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ContrastDefault) + + handle_default(THIS, THIS->contrast_min, THIS->contrast_max, THIS->contrast_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Color) + + if( !THIS->is_v4l2 ) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.colour); + else + { + DEVICE->videopict.colour=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_color(THIS, -1)); + else + gv4l2_color(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ColorMax) + + handle_max(THIS, THIS->color_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ColorMin) + + handle_min(THIS, THIS->color_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ColorDefault) + + handle_default(THIS, THIS->color_min, THIS->color_max, THIS->color_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Whiteness) + + if (!THIS->is_v4l2 ) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.whiteness>>8); + else + { + DEVICE->videopict.whiteness=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_whiteness(THIS, -1)); + else + gv4l2_whiteness(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_WhitenessMax) + + handle_max(THIS, THIS->whiteness_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_WhitenessMin) + + handle_min(THIS, THIS->whiteness_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_WhitenessDefault) + + handle_default(THIS, THIS->whiteness_min, THIS->whiteness_max, THIS->whiteness_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Hue) + + if( !THIS->is_v4l2 ) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.hue>>8); + else + { + DEVICE->videopict.hue=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_hue(THIS, -1)); + else + gv4l2_hue(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_HueMax) + + handle_max(THIS, THIS->hue_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_HueMin) + + handle_min(THIS, THIS->whiteness_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_HueDefault) + + handle_default(THIS, THIS->hue_min, THIS->hue_max, THIS->hue_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Brightness) + + if (!THIS->is_v4l2) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.brightness); + else + { + DEVICE->videopict.brightness=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_brightness(THIS, -1)); + else + gv4l2_brightness(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_BrightnessMax) + + handle_max(THIS, THIS->bright_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_BrightnessMin) + + handle_min(THIS, THIS->bright_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_BrightnessDefault) + + handle_default(THIS, THIS->bright_min, THIS->bright_max, THIS->bright_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Width) + + if (THIS->is_v4l2) + GB.ReturnInteger(THIS->fmt.fmt.pix.width); + else + GB.ReturnInteger(DEVICE->width); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Height) + + if (THIS->is_v4l2) + GB.ReturnInteger(THIS->fmt.fmt.pix.height); + else + GB.ReturnInteger(DEVICE->height); + +END_PROPERTY + + +BEGIN_METHOD(VideoDevice_new, GB_STRING Device; GB_INTEGER Compat) + + struct video_tuner vtuner; + VIDEO_STREAM *str; + + // ++ V4L2 + // + // Open the device + // + THIS->device = GB.NewString(STRING(Device), LENGTH(Device)); + + THIS->io = gv4l2_open_device(THIS->device); + if (THIS->io == -1) + { + GB.Error("Unable to open device"); + return; + } + + switch (VARGOPT(Compat, 0)) + { + case MODE_ANY: + THIS->is_v4l2 = gv4l2_available( THIS ); + break; + case MODE_V4L: + THIS->is_v4l2 = 0; + break; + case MODE_V4L2: + THIS->is_v4l2 = 1; + break; + default: + GB.Error("Invalid mode flag"); + goto __ERROR; + } + + if (THIS->is_v4l2 ) + { + gv4l2_debug("Device is V4L2!"); + // + // Initialise the device + // + if(!gv4l2_init_device(THIS,DEF_WIDTH,DEF_HEIGHT)) + { + GB.Error("Unable to initialise the device"); + goto __ERROR; + } + // + THIS->stream.desc=&VideoStream; + str = (VIDEO_STREAM*)POINTER(&THIS->stream); + str->handle = (void*)THIS; + // + gv4l2_start_capture(THIS); + return; + } + + gv4l2_debug("Device is V4L!"); + // mydev=open (GB.FileName(STRING(Device),LENGTH(Device)),O_RDWR); + // if (mydev==-1) + // { + // GB.Error("Unable to open device"); + // return; + //} + // -- V4L2 + + DEVICE = vd_setup(DEF_WIDTH,DEF_HEIGHT,DEF_DEPTH,THIS->io); + +//-- if (!vd_setup_capture_mode(DEVICE)) + if (!vd_setup_capture_mode(THIS)) // ++ + { + GB.Free(POINTER(&DEVICE)); + GB.Error("Unable to setup capture mode"); + goto __ERROR; + } + + vd_setup_video_source(DEVICE,IN_DEFAULT,NORM_DEFAULT); + + // -- GB.Alloc(POINTER(&THIS->device),sizeof(char)*(LENGTH(Device)+1)); + // -- strcpy(THIS->device,STRING(Device)); + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)) DEVICE->Freq2=1; + + THIS->stream.desc=&VideoStream; + str=(VIDEO_STREAM*)POINTER(&THIS->stream); + str->handle=(void*)THIS; + return; + +__ERROR: + + close(THIS->io); + +END_METHOD + + +BEGIN_METHOD_VOID(VideoDevice_free) + + // ++ V4L2 + GB.FreeString(&THIS->device); + if (THIS->frame) + GB.Free(POINTER(&THIS->frame)); + + if (THIS->is_v4l2) + { + gv4l2_stop_capture( THIS ); + gv4l2_uninit_device( THIS ); + gv4l2_close_device( THIS->io ); + return; + } + + // --if (THIS->device) GB.Free(POINTER(&THIS->device)); + // -- V4L2 + + if (THIS->membuf) GB.Free(POINTER(&THIS->membuf)); + + if (DEVICE) + { + vd_close(DEVICE); + GB.Free(POINTER(&DEVICE)); + } + +END_METHOD + + +BEGIN_METHOD(VideoDevice_Resize, GB_INTEGER Width; GB_INTEGER Height) + + struct video_tuner vtuner; + int w=VARG(Width); + int h=VARG(Height); + int mydev; + int norm; + int channel; + int colour,hue,whiteness,contrast,brightness; + + // ++ V4L2 + if( THIS->is_v4l2 ) { + gv4l2_resize( THIS , VARG(Width) , VARG(Height) ); + return; + } + // -- V4L2 + + if (hvcap.minheight) h=DEVICE->vcap.minheight; + if (h>DEVICE->vcap.maxheight) h=DEVICE->vcap.maxheight; + if (wvcap.minwidth) w=DEVICE->vcap.minwidth; + if (w>DEVICE->vcap.maxwidth) w=DEVICE->vcap.maxwidth; + + if ( (w==DEVICE->width) && (h==DEVICE->height) ) return; + + norm=DEVICE->vchan.norm; + channel=DEVICE->vchan.channel; + + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + hue=DEVICE->videopict.hue; + contrast=DEVICE->videopict.contrast; + brightness=DEVICE->videopict.brightness; + colour=DEVICE->videopict.colour; + whiteness=DEVICE->videopict.whiteness; + + if (THIS->membuf) GB.Free(POINTER(&THIS->membuf)); + vd_close(DEVICE); + GB.Free(POINTER(&DEVICE)); + + mydev=open(THIS->device,O_RDWR); + if (mydev==-1) + { + GB.Error("Unable to open device"); + return; + } + DEVICE=vd_setup(w,h,DEF_DEPTH,mydev); + +//-- if (!vd_setup_capture_mode(DEVICE)) + if (!vd_setup_capture_mode(THIS)) // ++ + { + close(mydev); + GB.Free(POINTER(&DEVICE)); + GB.Error("Unable to setup capture mode"); + return; + } + + vd_setup_video_source(DEVICE,channel,norm); + + DEVICE->videopict.hue=hue; + DEVICE->videopict.contrast=contrast; + DEVICE->videopict.brightness=brightness; + DEVICE->videopict.colour=colour; + DEVICE->videopict.whiteness=whiteness; + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)) DEVICE->Freq2=1; + +END_METHOD + + +BEGIN_PROPERTY(VideoDevice_Source) +/* +http://www.linuxtv.org/downloads/video4linux/API/V4L2_API/spec/index.html +*/ +// video_device_t *vd = DEVICE; +// struct video_tuner vtuner; + + int Source=0,Norm=0; + + if( THIS->is_v4l2 ) { + gv4l2_debug("'Source' not currently implemented for V4L2"); + +// BM: What is that all? I comment everything... + return; + } + +#if 0 + if (READ_PROPERTY) + { +/* +Example 1-1. Information about the current video input +struct v4l2_input input; +int index; + +if (-1 == ioctl (fd, VIDIOC_G_INPUT, &index)) { + perror ("VIDIOC_G_INPUT"); + exit (EXIT_FAILURE); +} + +memset (&input, 0, sizeof (input)); +input.index = index; + +if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUMINPUT"); + exit (EXIT_FAILURE); +} + +printf ("Current input: %s\n", input.name); +*/ + + struct v4l2_input input; + int index; + index=0; +//--------------------------vvvv +/* + if (-1 == vd_ioctl (DEVICE, VIDIOC_G_INPUT, &index)) { + // perror ("VIDIOC_G_INPUT"); + // exit (EXIT_FAILURE); + } +*/ +//-------------------------^^^^^ + +/* + memset (&input, 0, sizeof (input)); + input.index = index; + + if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUMINPUT"); + exit (EXIT_FAILURE); + } + printf ("Current input: %s\n", input.name; +*/ + + // if (!vd_ioctl(DEVICE, VIDIOCGCHAN, &DEVICE->vchan)) + // { +//--------------------------vvvv +/* +*/ +//-------------------------^^^^^ + switch (index)//DEVICE->vchan.channel) + { + case IN_TV: Source=0; break; + case IN_COMPOSITE1: Source=1; break; + case IN_COMPOSITE2: Source=2; break; + case IN_SVIDEO: Source=3; break; + } + gv4l2_debug("Source=" + Source); + +/* +Example 1-6. Listing the video standards supported by the current input +struct v4l2_input input; +struct v4l2_standard standard; + +memset (&input, 0, sizeof (input)); + +if (-1 == ioctl (fd, VIDIOC_G_INPUT, &input.index)) { + perror ("VIDIOC_G_INPUT"); + exit (EXIT_FAILURE); +} + +if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUM_INPUT"); + exit (EXIT_FAILURE); +} + +printf ("Current input %s supports:\n", input.name); + +memset (&standard, 0, sizeof (standard)); +standard.index = 0; + +while (0 == ioctl (fd, VIDIOC_ENUMSTD, &standard)) { + if (standard.id & input.std) + printf ("%s\n", standard.name); + + standard.index++; +} + +// EINVAL indicates the end of the enumeration, which cannot be +// empty unless this device falls under the USB exception. + +if (errno != EINVAL || standard.index == 0) { + perror ("VIDIOC_ENUMSTD"); + exit (EXIT_FAILURE); +} + */ +//dbl //struct v4l2_input input; +//nu struct v4l2_standard standard; + int std; + std=0; + std=NORM_SECAM; +//--------------------------vvvv +/* + if (-1 == vd_ioctl (DEVICE, VIDIOC_G_STD, &std)) { + // perror ("VIDIOC_G_INPUT"); + // exit (EXIT_FAILURE); + } +*/ +//-------------------------^^^^^ + switch(std) //DEVICE->vchan.norm) + { + case NORM_PAL: Norm=0; break; + case NORM_NTSC: Norm=4; break; + case NORM_SECAM: Norm=8; break; + case NORM_AUTO: Norm=12; break; + } + + //printf ("Current input: %lu\n",Source); + //printf ("Current norm: %lu\n", Norm); + + GB.ReturnInteger(Source+Norm); + + return; + } + +// property write starts here ********************************************* + Source=VPROP(GB_INTEGER) & 3; + Norm=(VPROP(GB_INTEGER)>>2) & 3; +gv4l2_debug("Source write2" ); + +/* +Example 1-2. Switching to the first video input +int index; + +index = 0; + +if (-1 == ioctl (fd, VIDIOC_S_INPUT, &index)) { + perror ("VIDIOC_S_INPUT"); + exit (EXIT_FAILURE); +} +*/ + +// vd_setup_video_source(DEVICE,channel,norm); +// vd_setup_video_source(DEVICE,Source,Norm); + +//vd_setup_video_source(video_device_t *vd, int input, int norm); +//vd_setup_video_source(DEVICE, Source, Norm); + + + +//--------------------------vvvv +/* + int index; + index = 2;// Source; //0 + if (!vd_ioctl (DEVICE, VIDIOC_S_INPUT, &DEVICE->vchan.channel)) { + gv4l2_debug ("VIDIOC_S_INPUT2"); + // exit (EXIT_FAILURE); + } + +*/ +//-------------------------^^^^^ + +/* +Example 1-7. Selecting a new video standard +struct v4l2_input input; +v4l2_std_id std_id; + +memset (&input, 0, sizeof (input)); + +if (-1 == ioctl (fd, VIDIOC_G_INPUT, &input.index)) { + perror ("VIDIOC_G_INPUT"); + exit (EXIT_FAILURE); +} + +if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUM_INPUT"); + exit (EXIT_FAILURE); +} + +if (0 == (input.std & V4L2_STD_PAL_BG)) { + fprintf (stderr, "Oops. B/G PAL is not supported.\n"); + exit (EXIT_FAILURE); +} + +// Note this is also supposed to work when only B +// or G/PAL is supported + +std_id = V4L2_STD_PAL_BG; + +if (-1 == ioctl (fd, VIDIOC_S_STD, &std_id)) { + perror ("VIDIOC_S_STD"); + exit (EXIT_FAILURE); +} + +*/ + int std_id; + std_id = Norm; //V4L2_STD_PAL_BG; + +//--------------------------vvvv +/* + if (!vd_ioctl (DEVICE, VIDIOC_S_STD, &std_id)) { + gv4l2_debug ("VIDIOC_S_STD2"); + // exit (EXIT_FAILURE); + } +*/ +//-------------------------^^^^^ + gv4l2_debug ("VIDIOC_S_done2"); + + return; + } // end v4l2 + + gv4l2_debug("'Source' now for V4L1"); +#endif + + if (READ_PROPERTY) + { + if (!vd_ioctl(DEVICE, VIDIOCGCHAN, &DEVICE->vchan)) + { + switch (DEVICE->vchan.channel) + { + case IN_TV: Source=0; break; + case IN_COMPOSITE1: Source=1; break; + case IN_COMPOSITE2: Source=2; break; + case IN_SVIDEO: Source=3; break; + } + switch(DEVICE->vchan.norm) + { + case NORM_PAL: Norm=0; break; + case NORM_NTSC: Norm=4; break; + case NORM_SECAM: Norm=8; break; + case NORM_AUTO: Norm=12; break; + } + } + GB.ReturnInteger(Source+Norm); + return; + } + + Source=VPROP(GB_INTEGER) & 3; + Norm=(VPROP(GB_INTEGER)>>2) & 3; + + switch( Source ) + { + case 0: Source=IN_TV; break; + case 1: Source=IN_COMPOSITE1; break; + case 2: Source=IN_COMPOSITE2; break; + case 3: Source=IN_SVIDEO; break; + } + + switch( Norm ) + { + case 0: Norm=NORM_PAL; break; + case 1: Norm=NORM_NTSC; break; + case 2: Norm=NORM_SECAM; break; + case 3: Norm=NORM_AUTO; break; + } + + vd_setup_video_source(DEVICE,Source,Norm); + +END_METHOD +// +//============================================================================= +// +// VideoDevice_Debug() +// +BEGIN_PROPERTY(VideoDevice_Debug) + + if (READ_PROPERTY) + { + GB.ReturnBoolean( gv4l2_debug_mode ); + return; + } + gv4l2_debug_mode = VPROP(GB_BOOLEAN); + +END_PROPERTY + +// +//============================================================================= +// +// cwebcam_image +// +// Raw "get_image" routine that can be used elsewhere regardless of the +// version of V4L2 in play. Necessary refactoring I'm afraid ... +// +int cwebcam_image(CWEBCAM *_object) +{ + if( THIS->is_v4l2 ) + { + if( !gv4l2_read_frame(THIS)) return 0; + THIS->w=THIS->fmt.fmt.pix.width; + THIS->h=THIS->fmt.fmt.pix.height; + } + else + { + if( !vd_get_image(THIS)) return 0; + THIS->w = DEVICE->vmmap.width; + THIS->h = DEVICE->vmmap.height; + vd_image_done(DEVICE); + } + return 1; +} + +// +// VideoDevice_Image() +// +// Hopefully you will agree, that not only is the raw _image routine +// required, but the resulting code is much nicer .. :) +// +BEGIN_PROPERTY(VideoDevice_Image) + + if (!cwebcam_image(THIS)) + { + GB.Error("Unable to get image"); + return; + } + + GB.ReturnObject(IMAGE.Create(THIS->w, THIS->h, THIS->format, THIS->frame)); +/* + // Ok, this lot has been refactored, sorry + // Once I got to "save" it became more efficient .. + + // -- GB_IMAGE ret=NULL; + unsigned char *buf; + int w, h; + + // ++ V4L2 + if( THIS->is_v4l2 ) { + + if( !gv4l2_read_frame( THIS )) + { + GB.Error("Unable to get image"); + GB.ReturnNull(); + return; + } + w=THIS->fmt.fmt.pix.width; + h=THIS->fmt.fmt.pix.height; + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_BGR, THIS->frame)); + return; + } + // -- V4L2 + + // -- buf = (unsigned char*)vd_get_image(DEVICE); + buf = (unsigned char*)vd_get_image(THIS); // ++ + if (!buf) + { + GB.Error("Unable to get image"); + GB.ReturnNull(); + return; + } + + w = DEVICE->vmmap.width; + h = DEVICE->vmmap.height; + vd_image_done(DEVICE); + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_BGR, buf)); +*/ + +END_PROPERTY + +BEGIN_METHOD(VideoDevice_Save,GB_STRING File; GB_INTEGER Quality;) + + char *File; + char *ext=NULL; + long b; + FILE *fd; + // -- char *buf; + int format=2; + int quality=80; + // -- int w,h; + + File=GB.FileName(STRING(File),LENGTH(File)); + + if (!File) + { + GB.Error("Unable to open file for writing"); + return; + } + + if (!MISSING(Quality)) + { + quality=VARG(Quality); + if (quality<0) quality=0; + if (quality>100) quality=100; + } + + format = 0; + + for (b=strlen(File)-1;b>=0;b--) + if (File[b]=='.') { ext=File+b+1; break; } + + if (ext) + { + if (!strcasecmp(ext,"jpeg")) format=3; + else if (!strcasecmp(ext,"jpg")) format=3; + else if (!strcasecmp(ext,"png")) format=2; + else if (!strcasecmp(ext,"ppm")) format=1; + } + + if (!format) + { + GB.Error("Unknown format (jpeg|jpg|png|ppm)"); + return; + } + + fd=fopen(File, "w"); + if (!fd) + { + GB.Error("Unable to open file for writing"); + return; + } + +/* V4L2 Refactoring + + // -- buf=(char*)vd_get_image(DEVICE); + buf=(char*)vd_get_image(THIS); // ++ + if (!buf) + { + fclose(fd); + GB.Error("Unable to get image"); + return; + } + + w=DEVICE->vmmap.width; + h=DEVICE->vmmap.height; + + switch(format) + { + case 1: put_image_ppm (buf,w,h,quality,0,fd); break; + case 2: put_image_png (buf,w,h,0,fd); break; + case 3: put_image_jpeg (buf,w,h,quality,0,fd); break; + } +*/ + + // + // V4L2 ++ + // + if( !cwebcam_image(THIS) ) { + fclose(fd); + GB.Error("Unable to get image"); + return; + } + switch(format) + { + case 1: + put_image_ppm (THIS->frame,THIS->w,THIS->h,quality,0,fd); + break; + case 2: + put_image_png (THIS->frame,THIS->w,THIS->h,0,fd); + break; + case 3: + put_image_jpeg(THIS->frame,THIS->w,THIS->h,quality,0,fd); + break; + } + // + // V4L2 -- + // + + fclose(fd); + // -- (Ooops!) vd_image_done(DEVICE); + +END_METHOD + +/************************************************************************** + +Features + +***************************************************************************/ +void return_array(char *array,long mmax) +{ + int bucle; + int max=mmax; + + for (bucle=0;bucleis_v4l2 ) + GB.ReturnNewZeroString(THIS->device); + else return_array(DEVICE->vcap.name,32); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Driver) + + struct v4l2_capability vcap; + int dev; + + if( THIS->is_v4l2 ) + dev = THIS->io; + else dev = DEVICE->dev; + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + return_array((char*)vcap.driver,16); + + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Bus) + + struct v4l2_capability vcap; + + int dev; + + if( THIS->is_v4l2 ) + dev = THIS->io; + else dev = DEVICE->dev; + + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + + return_array((char*)vcap.bus_info,32); + + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Card) + + struct v4l2_capability vcap; + + int dev; + + if( THIS->is_v4l2 ) { + return_array((char*)THIS->cap.card,32); + return; + } + dev = DEVICE->dev; + + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + return_array((char*)vcap.driver,16); + + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Version) + + char arr[12]; + struct v4l2_capability vcap; + + int dev; + + if( THIS->is_v4l2 ) + dev = THIS->io; + else dev = DEVICE->dev; + + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + + sprintf (arr,"%u.%u.%u",(vcap.version>>16)&0xFF,(vcap.version>> 8)&0xFF,vcap.version&0xFF); + GB.ReturnNewZeroString(arr); + + +END_PROPERTY + + + +BEGIN_PROPERTY(VideoDevice_MaxWidth) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("maxWidth not implemented in V4l2"); + GB.ReturnInteger(1024); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.maxwidth); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_MinWidth) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("minWidth not implemented in V4l2"); + GB.ReturnInteger(0); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.minwidth); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_MaxHeight) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("maxHeight not implemented in V4l2"); + GB.ReturnInteger(768); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.maxheight); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_MinHeight) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("minHeight not implemented in V4l2"); + GB.ReturnInteger(0); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.minheight); + +END_PROPERTY + +/************************************************************************** + +TV tuner + +***************************************************************************/ + +BEGIN_PROPERTY(CTUNER_name) + + struct video_tuner vtuner; + long bucle,mmax=32; + char * tuner = "'tuner' not currently implemented on V4L2"; + + if( THIS->is_v4l2 ) { + GB.ReturnNewZeroString(tuner); + return; + } + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + GB.ReturnVoidString(); + return; + } + + for (bucle=0;bucle<32;bucle++) + { + if (vtuner.name[bucle]==0) break; + mmax--; + } + + GB.ReturnNewString(vtuner.name,32-mmax); + +END_PROPERTY + +BEGIN_PROPERTY(CTUNER_signal) + + struct video_tuner vtuner; + + if( THIS->is_v4l2 ) { + GB.ReturnInteger(0); + return; + } + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(vtuner.signal); + +END_PROPERTY + +BEGIN_PROPERTY(CTUNER_low) + + struct video_tuner vtuner; + struct v4l2_frequency vfreq; + + if( THIS->is_v4l2 ) { + GB.ReturnBoolean(0); + return; + } + + if (DEVICE->Freq2) + { + if (READ_PROPERTY) + { + if (vd_ioctl (DEVICE, VIDIOC_G_FREQUENCY, &vfreq)!=0) + { + GB.ReturnBoolean(0); + return; + } + GB.ReturnBoolean(vfreq.type & V4L2_TUNER_CAP_LOW); + return; + } + } + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + GB.ReturnBoolean(0); + return; + } + GB.ReturnBoolean(vtuner.flags & VIDEO_TUNER_LOW); + + +END_PROPERTY + + +BEGIN_PROPERTY(CTUNER_frequency) + + struct video_tuner vtuner; + struct v4l2_frequency vfreq; + + if( THIS->is_v4l2 ) { + GB.ReturnInteger(0); + return; + } + + if (DEVICE->Freq2) + { + if (READ_PROPERTY) + { + if (vd_ioctl (DEVICE, VIDIOC_G_FREQUENCY, &vfreq)!=0) + { + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(vfreq.frequency); + return; + } + + if (vd_ioctl (DEVICE, VIDIOC_G_FREQUENCY, &vfreq)!=0) return; + vfreq.frequency=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOC_S_FREQUENCY, &vfreq); + return; + } + + if (READ_PROPERTY) + { + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(vtuner.signal); + return; + } + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) return; + vtuner.signal=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSTUNER, &vtuner); + +END_PROPERTY + +/*GB_DESC CFeaturesDesc[] = +{ + GB_DECLARE(".VideoDevice.Features", 0), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Name","s",VideoDevice_Name), + GB_PROPERTY_READ("Driver","s",VideoDevice_Driver), + GB_PROPERTY_READ("Bus","s",VideoDevice_Bus), + GB_PROPERTY_READ("Card","s",VideoDevice_Card), // ++ V4L2 + GB_PROPERTY_READ("Version","s",VideoDevice_Version), + GB_PROPERTY_READ("MaxWidth","i",VideoDevice_MaxWidth), + GB_PROPERTY_READ("MinWidth","i",VideoDevice_MinWidth), + GB_PROPERTY_READ("MaxHeight","i",VideoDevice_MaxHeight), + GB_PROPERTY_READ("MinHeight","i",VideoDevice_MinHeight), + + GB_END_DECLARE +};*/ + +GB_DESC CTunerDesc[] = +{ + GB_DECLARE(".VideoDevice.Tuner", 0), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Name","s",CTUNER_name), + GB_PROPERTY_READ("Quality","i",CTUNER_signal), + GB_PROPERTY_READ("Low","b",CTUNER_low), + + GB_PROPERTY("Frequency","i",CTUNER_frequency), + + GB_END_DECLARE +}; + +GB_DESC CWebcamDesc[] = +{ + + GB_DECLARE("VideoDevice", sizeof(CWEBCAM)), + GB_INHERITS("Stream"), + + GB_HOOK_CHECK(CWEBCAM_check), + + GB_CONSTANT("Hz","i",1), + GB_CONSTANT("Khz","i",0), + + GB_CONSTANT("Pal","i",0),//NORM_PAL), + GB_CONSTANT("Ntsc","i",4),//NORM_NTSC), + GB_CONSTANT("Secam","i",8),//NORM_SECAM), + GB_CONSTANT("Auto","i",12),//NORM_AUTO), + + GB_CONSTANT("Tv","i",0), //IN_TV), + GB_CONSTANT("Composite1","i",1), //IN_COMPOSITE1), + GB_CONSTANT("Composite2","i",2), //IN_COMPOSITE2), + GB_CONSTANT("SVideo","i",3), //IN_SVIDEO), + + GB_CONSTANT("Any", "i", MODE_ANY), + GB_CONSTANT("V4L", "i", MODE_V4L), // ++ force V4L1 + GB_CONSTANT("V4L2", "i", MODE_V4L2), // ++ force V4L2 + + GB_METHOD("_new",NULL,VideoDevice_new,"(Device)s[(Mode)i]"), + GB_METHOD("_free",NULL,VideoDevice_free,NULL), + + //GB_PROPERTY_SELF("Features",".VideoDevice.Features"), + + GB_PROPERTY_SELF("Tuner",".VideoDevice.Tuner"), + GB_PROPERTY("Source","i",VideoDevice_Source), + + GB_PROPERTY_READ("Width","i",VideoDevice_Width), + GB_PROPERTY_READ("Height","i",VideoDevice_Height), + + GB_PROPERTY("Contrast","i",VideoDevice_Contrast), + GB_PROPERTY_READ("ContrastMax","i",VideoDevice_ContrastMax), + GB_PROPERTY_READ("ContrastMin","i",VideoDevice_ContrastMin), + GB_PROPERTY_READ("ContrastDefault","i",VideoDevice_ContrastDefault), + GB_PROPERTY("Color","i",VideoDevice_Color), + GB_PROPERTY_READ("ColorMax","i",VideoDevice_ColorMax), + GB_PROPERTY_READ("ColorMin","i",VideoDevice_ColorMin), + GB_PROPERTY_READ("ColorDefault","i",VideoDevice_ColorDefault), + GB_PROPERTY("Whiteness","i",VideoDevice_Whiteness), + GB_PROPERTY_READ("WhitenessMax","i",VideoDevice_WhitenessMax), + GB_PROPERTY_READ("WhitenessMin","i",VideoDevice_WhitenessMin), + GB_PROPERTY_READ("WhitenessDefault","i",VideoDevice_WhitenessDefault), + GB_PROPERTY("Bright","i",VideoDevice_Brightness), + GB_PROPERTY_READ("BrightMax","i",VideoDevice_BrightnessMax), + GB_PROPERTY_READ("BrightMin","i",VideoDevice_BrightnessMin), + GB_PROPERTY_READ("BrightDefault","i",VideoDevice_BrightnessDefault), + GB_PROPERTY("Hue","i",VideoDevice_Hue), + GB_PROPERTY_READ("HueMax","i",VideoDevice_HueMax), + GB_PROPERTY_READ("HueMin","i",VideoDevice_HueMin), + GB_PROPERTY_READ("HueDefault","i",VideoDevice_HueDefault), + + GB_PROPERTY_READ("Name","s",VideoDevice_Name), + GB_PROPERTY_READ("Driver","s",VideoDevice_Driver), + GB_PROPERTY_READ("Bus","s",VideoDevice_Bus), + GB_PROPERTY_READ("Card","s",VideoDevice_Card), // ++ V4L2 + GB_PROPERTY_READ("Version","s",VideoDevice_Version), + GB_PROPERTY_READ("MaxWidth","i",VideoDevice_MaxWidth), + GB_PROPERTY_READ("MinWidth","i",VideoDevice_MinWidth), + GB_PROPERTY_READ("MaxHeight","i",VideoDevice_MaxHeight), + GB_PROPERTY_READ("MinHeight","i",VideoDevice_MinHeight), + //GB_PROPERTY_READ("MaxFrameRate","i",CFEATURES_maxRate), + + GB_PROPERTY("Image","Image",VideoDevice_Image), + GB_PROPERTY("Debug","b",VideoDevice_Debug), + + GB_METHOD("Resize",NULL,VideoDevice_Resize,"(Width)i(Height)i"), + GB_METHOD("Save",NULL,VideoDevice_Save,"(File)s[(Quality)i]"), + + GB_END_DECLARE +}; + + diff --git a/gb.v4l/src/CWebcam.h b/gb.v4l/src/CWebcam.h new file mode 100644 index 00000000..4bd0e501 --- /dev/null +++ b/gb.v4l/src/CWebcam.h @@ -0,0 +1,197 @@ +/*************************************************************************** + + CWebcam.h + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBCAM_H +#define __CWEBCAM_H + +#include +#include +#include +#include "config.h" +#include +#include + +#ifdef OS_LINUX + #include + #include "videodev.h" +#else + #include +#endif + +#include "gambas.h" + +#ifndef __CWEBCAM_C + +extern GB_DESC CWebcamDesc[]; +extern GB_DESC CTunerDesc[]; +extern GB_STREAM_DESC VideoStream; + +#else + +#define THIS ((CWEBCAM *)_object) +#define DEVICE (THIS->dev) +// ++ V4L2 +#define MCLEAR(x) memset (&(x), 0, sizeof (x)) +#define MODE_ANY 0 +#define MODE_V4L 1 +#define MODE_V4L2 2 +// -- +#endif + +typedef + struct video_device + { + int width; + int height; + int depth; // colour depth + int buffer_size; // always width * height * depth + int use_mmap; // mmap() available for capturing a frame + int capturing; // our device is capturing frames for us + + struct video_capability vcap; + struct video_channel vchan; + struct video_mbuf vmbuf; + struct video_mmap vmmap; + struct video_window vwin; + struct video_picture videopict; + + unsigned char *frame_buffer; // not the video memory, but one image + int dev; // fd of the physical device + int Freq2; + } + video_device_t; + + +typedef + struct + { + GB_STREAM_BASE stream; + void *handle; + } + VIDEO_STREAM; + +// ++ V4L2 +typedef + struct gv4l2_buffer + { + void* start; + size_t length; + } + gv4l2_buffer_t; +//-- + +typedef + struct + { + GB_BASE ob; + GB_STREAM stream; + + char *device; + video_device_t *dev; + unsigned char *membuf; + int gotframe; + int posframe; + + // ++ YUYV->RGB conversion + void *frame; // "current" frame buffer + //-- + // ++ V4L2 + // + // There is some duplication here but we really don't want to use + // the v4l video_device_t structure ... + // + struct v4l2_capability cap; + struct v4l2_cropcap cropcap; + struct v4l2_crop crop; + struct v4l2_format fmt; + struct gv4l2_buffer *buffers; + // + int is_v4l2; // which version is this dev + int io; // raw device handle for V2 + int use_mmap; // is MMAP available + int buffer_count; // number of buffers + int w, h; // "current" dimensions + int format; // gb.image format + // + int bright_max; + int hue_max; + int contrast_max; + int whiteness_max; + int color_max; + // + int bright_min; + int hue_min; + int contrast_min; + int whiteness_min; + int color_min; + // + int bright_def; + int hue_def; + int contrast_def; + int whiteness_def; + int color_def; + // -- + struct v4lconvert_data *convert; + } + CWEBCAM; + + +int Video_stream_read(GB_STREAM *stream, char *buffer, int len); +int Video_stream_write(GB_STREAM *stream, char *buffer, int len); +int Video_stream_eof(GB_STREAM *stream); +int Video_stream_lof(GB_STREAM *stream, int64_t *len); +int Video_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int Video_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int Video_stream_tell(GB_STREAM *stream, int64_t *pos); +int Video_stream_flush(GB_STREAM *stream); +int Video_stream_close(GB_STREAM *stream); +int Video_stream_handle(GB_STREAM *stream); + + +// ++ YUYV->RGB conversion +int convert_yuv_to_rgb_buffer(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height); +void yuv420p_to_rgb (unsigned char *image, unsigned char *image2, int x, int y, int z); +// -- + +// ++ V4L2 +int gv4l2_available(CWEBCAM * _object); +void gv4l2_debug(const char *s); +int gv4l2_xioctl( int fd,int request,void * arg); +int gv4l2_open_device( char* name ); +void gv4l2_close_device( int id ); +int gv4l2_init_device(CWEBCAM * _object , int width , int height ); +int gv4l2_start_capture(CWEBCAM * _object); +int gv4l2_stop_capture(CWEBCAM * _object); +void gv4l2_uninit_device(CWEBCAM * _object); +void gv4l1_process_image (CWEBCAM * _object, void *start); +void gv4l2_process_image (CWEBCAM * _object, void *start); +int gv4l2_read_frame( CWEBCAM * _object ); +int gv4l2_resize( CWEBCAM * _object , int width , int height ); +int gv4l2_hue( CWEBCAM * _object , int hue ); +int gv4l2_brightness( CWEBCAM * _object , int hue ); +int gv4l2_contrast( CWEBCAM * _object , int value ); +int gv4l2_color( CWEBCAM * _object , int value ); +int gv4l2_whiteness( CWEBCAM * _object , int value ); +// -- V4L2 + +#endif diff --git a/gb.v4l/src/Makefile.am b/gb.v4l/src/Makefile.am new file mode 100644 index 00000000..f8318f13 --- /dev/null +++ b/gb.v4l/src/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.v4l +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.v4l.la + +gb_v4l_la_LIBADD = @V4L_LIB@ @V4LCONVERT_LIB@ +gb_v4l_la_LDFLAGS = -module @LD_FLAGS@ @V4L_LDFLAGS@ @V4LCONVERT_LDFLAGS@ +gb_v4l_la_CPPFLAGS = @V4L_INC@ @V4LCONVERT_INC@ + +gb_v4l_la_SOURCES = main.h main.c CWebcam.h CWebcam.c gv4l2.c CConverters.c videodev.h + + + + diff --git a/gb.v4l/src/gb.v4l.component b/gb.v4l/src/gb.v4l.component new file mode 100644 index 00000000..af796e9e --- /dev/null +++ b/gb.v4l/src/gb.v4l.component @@ -0,0 +1,10 @@ +[Component] +Key=gb.v4l +Name[fr]=Composant de capture vidéo +Name[es]=Componente para captura de vídeo +Name[tr]=Video yakalama bileşeni +Author=Daniel Campos Fernández,Gareth Bult +Requires=gb.image +State=Deprecated + + diff --git a/gb.v4l/src/gv4l2.c b/gb.v4l/src/gv4l2.c new file mode 100644 index 00000000..2afe4deb --- /dev/null +++ b/gb.v4l/src/gv4l2.c @@ -0,0 +1,803 @@ +/*************************************************************************** + + gv4l2.c + + (C) 2009 Gareth Bult + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBCAM_C + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif + +#include "main.h" +#include "CWebcam.h" + +#ifdef OS_LINUX + #include +#else + #include +#endif + +// +bool gv4l2_debug_mode = TRUE; +// +//============================================================================= +// +// gv4l2_available() +// +// Test for V4L2 availability +// +int gv4l2_available(CWEBCAM * _object) +{ + char dummy[256]; + + return(!(ioctl( THIS->io , VIDIOC_QUERYCAP , dummy ) == -1 )); +} +//============================================================================= +// +// v4l2_debug( string ) +// +// Debugging routine for V4L2 +// +void gv4l2_debug(const char *s) +{ + if (!gv4l2_debug_mode) return; + fprintf(stderr, "gb.v4l: v4l2: %s: %s\n", s, strerror(errno)); +} + +//============================================================================= +// +// xioctl( fd,request,arg ) +// +// Local swapper for ioctl to repeat on EINTR's +// +int gv4l2_xioctl( int fd,int request,void * arg) +{ + int r; + + do r = ioctl (fd, request, arg); + while (-1 == r && EINTR == errno); + return r; +} +// +//============================================================================= +// +// gv4l2_hue( THIS, value ) +// +void gv4l2_camera_setup(CWEBCAM *_object,int id,int *min,int *max,int *def) +{ + struct v4l2_queryctrl query; + + memset (&query, 0, sizeof (query)); + query.id = id; + + if(gv4l2_xioctl(THIS->io,VIDIOC_QUERYCTRL,&query)==-1)return; + + //printf("Name=%s,Min=%d,Max=%d,Value=%d\n", + // query.name, + // query.minimum, + // query.maximum, + // query.default_value); + //fflush(stdout); + + *max = query.maximum; + *min = query.minimum; + *def = query.default_value; +} +// +int gv4l2_camera_get( CWEBCAM * _object , int id , int value ) +{ + struct v4l2_control control; + int command; + int result; + + memset (&control, 0, sizeof (control)); + control.id = id; + control.value = value; + + if( value != -1 ) + command = VIDIOC_S_CTRL; + else command = VIDIOC_G_CTRL; + + result = gv4l2_xioctl (THIS->io, command , &control); + + if( result == -1 ) return result; + return control.value; +} +// +void gv4l2_hue_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_HUE , &THIS->hue_min , &THIS->hue_max ,&THIS->hue_def); +} +// +int gv4l2_hue( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_HUE , value ); +} +// +//============================================================================= +// +// gv4l2_brightness( THIS, value ) +// +void gv4l2_brightness_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS, + V4L2_CID_BRIGHTNESS , &THIS->bright_min ,&THIS->bright_max,&THIS->bright_def); +} + +int gv4l2_brightness( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_BRIGHTNESS , value ); +} +// +//============================================================================= +// +// gv4l2_contrast( THIS, value ) +// +void gv4l2_contrast_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_CONTRAST, &THIS->contrast_min , &THIS->contrast_max,&THIS->contrast_def); +} + +int gv4l2_contrast( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_CONTRAST , value ); +} +// +//============================================================================= +// +// gv4l2_color( THIS, value ) +// +void gv4l2_color_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_SATURATION, &THIS->color_min, &THIS->color_max,&THIS->color_def ); +} + +int gv4l2_color( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_SATURATION , value ); +} +// +//============================================================================= +// +// gv4l2_color( THIS, value ) +// +void gv4l2_whiteness_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_WHITENESS , &THIS->whiteness_min,&THIS->whiteness_max,&THIS->whiteness_def); +} +// +int gv4l2_whiteness( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_WHITENESS , value ); +} +//============================================================================= +// +// +//============================================================================= +// +// v4l2_open_device( device_name ) +// +// Open the raw device (typically /dev/video?) , note that we're not +// using non-blocking mode as (a) it's not needed given we're recovering +// specific frames and (b) camera's are often "not ready" so it would +// require retries under Gambas. +// +// FIXME:: what happens when you unplug a camera when active ?? +// +int gv4l2_open_device( char* name ) +{ + struct stat file_info; + int status; + + // + // See if the file is there ... + // + status = stat(name,&file_info); + if( status == -1 ) { + gv4l2_debug("failed to stat device"); + return status; + } + // + // Make sure it's a character device (/dev/video?) + // + if( !S_ISCHR(file_info.st_mode) ) { + gv4l2_debug("not a character device"); + return status; + } + // + // Finally, try to open the file .. + // + return open( name,O_RDWR /* |O_NONBLOCK */ ,0 ); +} +//============================================================================ +// +// v4l2_close_device( id ) +// +// Close the device, got to be done and can't get much easier ! ;-) +// +void gv4l2_close_device( int id ) +{ + if( close( id ) == -1 ) { + gv4l2_debug("error closing device"); + } +} +//============================================================================ +// +// v4l2_init_device( THIS , Width , Height ) +// +// Initialise the device and associated data structures, this is the most +// complex operation in the code and has to cope with it's own MMAP +// handling whereas V4L did a lot of this for us. +// +// FIXME:: test the READ interface, I only use MMAP cameras ... +// +int gv4l2_init_device(CWEBCAM * _object , int width , int height ) +{ + unsigned int min; + static unsigned int n_buffers = 0; + int save; + + if ( gv4l2_xioctl (THIS->io, VIDIOC_QUERYCAP, &THIS->cap) == -1 ) { + gv4l2_debug("VIDIOC_QUERYCAP error"); + return 0; + } + + if (!(THIS->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + gv4l2_debug("not video capture device"); + return 0; + } + // + // We need to choose which IO method to use, well try MMAP and + // if that fails, fall back to READ + // + if (!(THIS->cap.capabilities & V4L2_CAP_STREAMING)) { + // + // No MMAP support! + // + THIS->use_mmap = 0; + if (!(THIS->cap.capabilities & V4L2_CAP_READWRITE)) { + gv4l2_debug("device does not support mmap or read"); + return 0; + } + } + else + THIS->use_mmap = 1; + + MCLEAR(THIS->cropcap); + THIS->cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (!gv4l2_xioctl (THIS->io, VIDIOC_CROPCAP, &THIS->cropcap)) { + THIS->crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + THIS->crop.c = THIS->cropcap.defrect; + + if ( gv4l2_xioctl (THIS->io, VIDIOC_S_CROP, &THIS->crop) == -1 ) + { + if( errno == EINVAL ) { + gv4l2_debug("cropping not supported"); + } + } + } + + MCLEAR(THIS->fmt); + THIS->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if( gv4l2_xioctl( THIS->io, VIDIOC_G_FMT, &THIS->fmt ) == -1 ) { + gv4l2_debug("Unable to get Video formats"); + return 0; + } + + THIS->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + THIS->fmt.fmt.pix.width = width; + THIS->fmt.fmt.pix.height = height; + THIS->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + + save = THIS->fmt.fmt.pix.pixelformat; + + // + // Camera format should be picked up from VIDIOC_G_FMT above + // FIXME:: do cameras support multiple formats and so we want + // to be able to pick the format?? + // + // Try the supported formats; + // a. YUYV + // b. YUV420 + // c. revert to whatever the camera was set to + // + THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + if ( gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt) == -1) { + gv4l2_debug("VIDIOC_S_FMT, can't set YUYV, trying YUV 420"); + THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + if ( gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt) == -1) { + gv4l2_debug("VIDIOC_S_FMT, can't set YUV420, defaulting "); + THIS->fmt.fmt.pix.pixelformat = save; + } + } + + // BM: Final image gb.image format + THIS->format = GB_IMAGE_BGR; //IMAGE.GetDefaultFormat(); + + // BM: Conversion structure + THIS->convert = v4lconvert_create(THIS->io); + + //THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + + //if ( gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt) == -1) { + // gv4l2_debug("VIDIOC_S_FMT, unable to set format"); + // return 0; + //} + // THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + // gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt); + + /* Note VIDIOC_S_FMT may change width and height. */ + + /* Buggy driver paranoia. */ + min = THIS->fmt.fmt.pix.width * 2; + if (THIS->fmt.fmt.pix.bytesperline < min) + THIS->fmt.fmt.pix.bytesperline = min; + min = THIS->fmt.fmt.pix.bytesperline * THIS->fmt.fmt.pix.height; + if (THIS->fmt.fmt.pix.sizeimage < min) + THIS->fmt.fmt.pix.sizeimage = min; + + GB.Alloc(&THIS->frame, THIS->fmt.fmt.pix.width * THIS->fmt.fmt.pix.height * (GB_IMAGE_FMT_IS_24_BITS(THIS->format) ? 3 : 4)); + + gv4l2_brightness_setup( THIS ); + gv4l2_contrast_setup( THIS ); + gv4l2_color_setup( THIS ); + gv4l2_whiteness_setup( THIS ); + gv4l2_hue_setup( THIS ); + + if( !THIS->use_mmap ) { + GB.Alloc( POINTER(&THIS->buffers) ,sizeof(*THIS->buffers)); + if( !THIS->buffers ) { + gv4l2_debug("Failed to allocate buffer space"); + return 0; + } + THIS->buffers[0].length = THIS->fmt.fmt.pix.sizeimage; + GB.Alloc( POINTER(&THIS->buffers[0].start),THIS->fmt.fmt.pix.sizeimage); + if( !THIS->buffers[0].start ) { + gv4l2_debug("Failed to allocate buffer space"); + return 0; + } + return 1; + } + // We don't support USERPTR in Gambas (!) + // So now we initialise MMAP + // + struct v4l2_requestbuffers req; + + MCLEAR(req); + req.count = 2; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + + if ( gv4l2_xioctl (THIS->io, VIDIOC_REQBUFS, &req) == -1 ) { + gv4l2_debug("mmap not supported or error"); + return 0; + } + if (req.count < 2) { + gv4l2_debug("not enough memory for mmap"); + return 0; + } + GB.Alloc ( POINTER(&THIS->buffers),req.count * sizeof (*THIS->buffers)); + if (!THIS->buffers) { + gv4l2_debug("not memory for mmap"); + return 0; + } + THIS->buffer_count = req.count; + for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { + struct v4l2_buffer buf; + + MCLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + + if( gv4l2_xioctl (THIS->io, VIDIOC_QUERYBUF, &buf) == -1 ) { + gv4l2_debug("VIDIOC_QUERYBUF"); + return 0; + } + + THIS->buffers[n_buffers].length = buf.length; + THIS->buffers[n_buffers].start = + mmap (NULL /* start anywhere */, + buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, + THIS->io, buf.m.offset); + + if (MAP_FAILED == THIS->buffers[n_buffers].start) { + gv4l2_debug("mmap failed"); + return 0; + } + } + return 1; +} + +//============================================================================= +// +// v4l2_start_capture() +// +// Start capture mode, this should turn on the little green light on your +// camera. +// +// FIXME:: make sure we check the return status on this call +// +int gv4l2_start_capture(CWEBCAM * _object) +{ + int i; + enum v4l2_buf_type type; + // + gv4l2_debug("Capture ON"); + // + // Nothing to do unless we're using MMAP + // + if( !THIS->use_mmap) return 1; + // + for( i=0; ibuffer_count; i++ ) { + struct v4l2_buffer buf; + + MCLEAR (buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if( gv4l2_xioctl( THIS->io, VIDIOC_QBUF, &buf) == -1 ) { + gv4l2_debug("VIDIOC_QBUF error starting capture"); + return 0; + } + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if( gv4l2_xioctl( THIS->io, VIDIOC_STREAMON, &type) == -1 ) { + gv4l2_debug("VIDIOC_STREAMON error starting capture"); + return 0; + } + return 1; +} +//============================================================================= +// +// v4l2_stop_capture() +// +// Stop Capturing on device (turn little green light off!) +// +// FIXME:: check return status on this call! +// +int gv4l2_stop_capture(CWEBCAM * _object) +{ + enum v4l2_buf_type type; + + if( !THIS->use_mmap) return 1; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if( gv4l2_xioctl( THIS->io, VIDIOC_STREAMOFF, &type) == -1) + { + gv4l2_debug("VIDIOC_STREAMOFF error"); + return 0; + } + return 1; +} +//============================================================================= +// +// gv4l2_uninit_device(THIS) +// +// Uninitialise the device and free all the associated memory +// +void gv4l2_uninit_device(CWEBCAM * _object) +{ + unsigned int i; + + GB.Free( POINTER(&THIS->frame) ); + + v4lconvert_destroy(THIS->convert); + + if( !THIS->use_mmap) { + GB.Free ( POINTER(&THIS->buffers[0].start)); + GB.Free ( POINTER(&THIS->buffers)); + return; + } + for (i = 0; i < THIS->buffer_count; ++i ) + if(munmap(THIS->buffers[i].start,THIS->buffers[i].length)==-1) + gv4l2_debug("MUNMAP Error"); + + GB.Free ( POINTER(&THIS->buffers)); +} +//============================================================================= +// +// g4vl_process_image(THIS,start) +// +// Process the image found in start and dump the resulting RGB frame into +// our local frame buffer (THIS->frame). Width, Height and Image size can +// all be found in THIS->fmt.fmt +// +// FIXME:: there are lots of formats, I can *only* test YUYV. +// I'm *assuming* RGB32 is "raw" mode (no conversion) +// Do "other" RGB formats work without conversion? +// What other conversion routines do we need? +// Will BM be moving any/all of these to Image/Picture objects? +// +void gv4l1_process_image (CWEBCAM * _object, void *start) +{ + int format,w,h; + long size; + + format = THIS->dev->videopict.palette; + w = THIS->dev->width; + h = THIS->dev->height; + size = THIS->dev->buffer_size; + + switch(format) + { + case VIDEO_PALETTE_YUV411P: gv4l2_debug("YUV411P"); break; + case VIDEO_PALETTE_YUV420P: + //gv4l2_debug("YUV420P"); + case VIDEO_PALETTE_YUV420: + //gv4l2_debug("YUV420"); + yuv420p_to_rgb (start,THIS->frame,w,h,3); + return; + case VIDEO_PALETTE_YUYV: + //gv4l2_debug("YUYV"); + convert_yuv_to_rgb_buffer(start,THIS->frame,w,h); + return; + + case VIDEO_PALETTE_GREY: gv4l2_debug("GREY"); break; + case VIDEO_PALETTE_HI240: gv4l2_debug("HI240"); break; + case VIDEO_PALETTE_RGB565: gv4l2_debug("RGB5656"); break; + case VIDEO_PALETTE_RGB24: gv4l2_debug("RGB24"); break; + case VIDEO_PALETTE_RGB32: /* DEFAULT */ break; + case VIDEO_PALETTE_RGB555: gv4l2_debug("RGB555"); break; + case VIDEO_PALETTE_UYVY: gv4l2_debug("UYVY"); break; + case VIDEO_PALETTE_YUV411: gv4l2_debug("YUV411"); break; + case VIDEO_PALETTE_RAW: gv4l2_debug("RAW"); break; + case VIDEO_PALETTE_YUV422P: gv4l2_debug("YUV422P"); break; + case VIDEO_PALETTE_YUV410P: gv4l2_debug("YUV410P"); break; + case VIDEO_PALETTE_COMPONENT: gv4l2_debug("COMPONENT");break; + + default: + gv4l2_debug("Frame in unknown format"); + break; + } + memcpy(THIS->frame,start,size); +} +// +// v4l2 version (!) +// +void gv4l2_process_image (CWEBCAM * _object, void *start) +{ + struct v4l2_format dest = THIS->fmt; + + if (THIS->format != GB_IMAGE_BGR) + gv4l2_debug("Destination format not supported"); + + dest.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + dest.fmt.pix.sizeimage = THIS->fmt.fmt.pix.width * THIS->fmt.fmt.pix.height * 3; + + if (v4lconvert_convert(THIS->convert, &THIS->fmt, &dest, start, THIS->fmt.fmt.pix.sizeimage, THIS->frame, dest.fmt.pix.sizeimage) != dest.fmt.pix.sizeimage) + gv4l2_debug("Unable to convert webcam image to BGR24"); +} + +#if 0 + switch(format) + { +/* + For adding new formats use the #ifdef method. + It is not safe to asume every videodev2.h has the V4L2_PIX_FMT_ + V4L2_PIX_FMT_Y16 was not aviable in Ubuntu 8.04 but exists in Ubuntu 8.10 + Some other where also missing + Add by:R.Onstenk 22-feb-2009 +*/ + case V4L2_PIX_FMT_RGB332: gv4l2_debug("RGB332"); break; + case V4L2_PIX_FMT_RGB444: gv4l2_debug("RGB444"); break; + case V4L2_PIX_FMT_RGB555: gv4l2_debug("RGB555"); break; + case V4L2_PIX_FMT_RGB565: gv4l2_debug("YRGB565"); break; + case V4L2_PIX_FMT_RGB555X: gv4l2_debug("YRGB555X");break; + case V4L2_PIX_FMT_RGB565X: gv4l2_debug("RGB565X"); break; + case V4L2_PIX_FMT_BGR24: gv4l2_debug("BGR24"); break; + case V4L2_PIX_FMT_RGB24: gv4l2_debug("RGB24"); break; + case V4L2_PIX_FMT_BGR32: gv4l2_debug("BGR32"); break; + case V4L2_PIX_FMT_RGB32: /* DEFAULT - NO CONV */ break; + case V4L2_PIX_FMT_GREY: gv4l2_debug("GREY"); break; + +#ifdef V4L2_PIX_FMT_Y16 + case V4L2_PIX_FMT_Y16: gv4l2_debug("Y16"); break; +#endif + case V4L2_PIX_FMT_PAL8: gv4l2_debug("PAL8"); break; + case V4L2_PIX_FMT_YVU410: gv4l2_debug("YVU410"); break; + case V4L2_PIX_FMT_YVU420: gv4l2_debug("YVU420"); break; + + case V4L2_PIX_FMT_YUV420: + //gv4l2_debug("YUV420"); + yuv420p_to_rgb (start,THIS->frame,w,h,3); + return; + case V4L2_PIX_FMT_YUYV: + //gv4l2_debug("YUYV"); + convert_yuv_to_rgb_buffer(start,THIS->frame,w,h); + return; + + case V4L2_PIX_FMT_UYVY: gv4l2_debug("UYVY"); break; + case V4L2_PIX_FMT_YUV422P: gv4l2_debug("YUV422P"); break; + case V4L2_PIX_FMT_YUV411P: gv4l2_debug("YUV411P"); break; + case V4L2_PIX_FMT_Y41P: gv4l2_debug("Y41P"); break; + case V4L2_PIX_FMT_YUV444: gv4l2_debug("YUV444"); break; + case V4L2_PIX_FMT_YUV555: gv4l2_debug("YUV555"); break; + case V4L2_PIX_FMT_YUV565: gv4l2_debug("YUV565"); break; + case V4L2_PIX_FMT_YUV32: gv4l2_debug("YUV32"); break; + case V4L2_PIX_FMT_NV12: gv4l2_debug("NV12"); break; + case V4L2_PIX_FMT_NV21: gv4l2_debug("NV21"); break; + case V4L2_PIX_FMT_YUV410: gv4l2_debug("YUV410"); break; + case V4L2_PIX_FMT_YYUV: gv4l2_debug("YYUV"); break; + case V4L2_PIX_FMT_HI240: gv4l2_debug("HI240"); break; + case V4L2_PIX_FMT_HM12: gv4l2_debug("HM12"); break; + case V4L2_PIX_FMT_SBGGR8: gv4l2_debug("SBGGR8"); break; +#ifdef V4L2_PIX_FMT_SGBRG8 + case V4L2_PIX_FMT_SGBRG8: gv4l2_debug("SBGRG8"); break; +#endif +#ifdef V4L2_PIX_FMT_SBGGR16 + case V4L2_PIX_FMT_SBGGR16: gv4l2_debug("SBGGR16"); break; +#endif + + case V4L2_PIX_FMT_MJPEG: gv4l2_debug("MJPEG"); break; + case V4L2_PIX_FMT_JPEG: gv4l2_debug("JPEG"); break; + case V4L2_PIX_FMT_DV: gv4l2_debug("DV"); break; + case V4L2_PIX_FMT_MPEG: gv4l2_debug("MPEG"); break; + case V4L2_PIX_FMT_WNVA: gv4l2_debug("WNVA"); break; + case V4L2_PIX_FMT_SN9C10X: gv4l2_debug("SN9C10X"); break; + case V4L2_PIX_FMT_PWC1: gv4l2_debug("PWC1"); break; + case V4L2_PIX_FMT_PWC2: gv4l2_debug("PWC2"); break; + case V4L2_PIX_FMT_ET61X251: gv4l2_debug("ET61X251");break; +#ifdef V4L2_PIX_FMT_SPCA501 + case V4L2_PIX_FMT_SPCA501: gv4l2_debug("SPCA501"); break; +#endif +#ifdef V4L2_PIX_FMT_SPCA505 + case V4L2_PIX_FMT_SPCA505: gv4l2_debug("SPCA505"); break; +#endif +#ifdef V4L2_PIX_FMT_SPCA508 + case V4L2_PIX_FMT_SPCA508: gv4l2_debug("SPCA508"); break; +#endif +#ifdef V4L2_PIX_FMT_SPCA561 + case V4L2_PIX_FMT_SPCA561: gv4l2_debug("SPCA561"); break; +#endif +#ifdef V4L2_PIX_FMT_PAC207 + case V4L2_PIX_FMT_PAC207: gv4l2_debug("PAC207"); break; +#endif +#ifdef V4L2_PIX_FMT_PJPG + case V4L2_PIX_FMT_PJPG: gv4l2_debug("PJPG"); break; +#endif +#ifdef V4L2_PIX_FMT_YVYU + case V4L2_PIX_FMT_YVYU: gv4l2_debug("YVYU"); break; +#endif + + default: + gv4l2_debug("Frame in unknown format. Format:0x" + format); + break; + } + memcpy(THIS->frame,start,size); +} +#endif + +//============================================================================= +// +// gv4l2_read_frame( THIS ) +// +// Read a frame from the camera / video device +// +// FIXME:: test non mmap mode! +// +int gv4l2_read_frame( CWEBCAM * _object ) +{ + struct v4l2_buffer buf; + + if( !THIS->use_mmap ) { + gv4l2_debug("Using READ interface"); + if( read (THIS->io, THIS->buffers[0].start, THIS->buffers[0].length) == -1) { + switch (errno) { + case EAGAIN: + return 0; + case EIO: + /* Could ignore EIO, see spec. */ + /* fall through */ + default: + gv4l2_debug("READ ERROR"); + } + } + gv4l2_process_image (THIS,THIS->buffers[0].start); + return 1; + } + // + // This is the MMAP based read code + // + MCLEAR (buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + if( gv4l2_xioctl( THIS->io, VIDIOC_DQBUF, &buf) == -1 ) { + gv4l2_debug("DQBUF Error"); + switch (errno) { + case EAGAIN: + gv4l2_debug("EAGAIN"); + return 0; + case EIO: + /* Could ignore EIO, see spec. */ + /* fall through */ + default: + gv4l2_debug("VIDIOC_DQBUF READ ERROR"); + } + } + assert (buf.index < THIS->buffer_count); + gv4l2_process_image (THIS,THIS->buffers[buf.index].start); + + if( gv4l2_xioctl( THIS->io, VIDIOC_QBUF, &buf) == -1 ) { + gv4l2_debug("VIDIOC_QBUF READ ERROR"); + return 0; + } + return 1; +} +//============================================================================= +// +// gv4l2_resize( THIS , Width , Height ) +// +// Resize the display. +// Going to cheat a little here, easy way is to completely deactivate +// and let it start up with a new width and height .. :) +// +int gv4l2_resize( CWEBCAM * _object , int width , int height ) +{ + if(! gv4l2_stop_capture( THIS ) ) { + GB.Error("Failed to stop capturing on device"); + return 0; + } + gv4l2_uninit_device( THIS ); + + if( close(THIS->io ) == -1 ) { + gv4l2_debug("error closing device"); + } + + if ( !gv4l2_open_device( THIS->device )){ + GB.Error("Unable to reopen the device"); + return 0; + } + + if( !gv4l2_init_device(THIS , width , height ) ) { + GB.Error("Unable to initialise the device"); + return 0; + } + + gv4l2_start_capture( THIS ); + return 1; +} diff --git a/gb.v4l/src/main.c b/gb.v4l/src/main.c new file mode 100644 index 00000000..8d55c4f6 --- /dev/null +++ b/gb.v4l/src/main.c @@ -0,0 +1,50 @@ +/*************************************************************************** + + main.c + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include + +#include "CWebcam.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CWebcamDesc, + CTunerDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.v4l/src/main.h b/gb.v4l/src/main.h new file mode 100644 index 00000000..8679e385 --- /dev/null +++ b/gb.v4l/src/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif diff --git a/gb.v4l/src/videodev.h b/gb.v4l/src/videodev.h new file mode 100644 index 00000000..71876c7e --- /dev/null +++ b/gb.v4l/src/videodev.h @@ -0,0 +1,343 @@ +/*************************************************************************** + + videodev.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + * Video for Linux version 1 - OBSOLETE + * + * Header file for v4l1 drivers and applications, for + * Linux kernels 2.2.x or 2.4.x. + * + * Provides header for legacy drivers and applications + * + * See http://linuxtv.org for more info + * + */ +#ifndef __LINUX_VIDEODEV_H +#define __LINUX_VIDEODEV_H + +#include +#include +#include + + +#define VID_TYPE_CAPTURE 1 /* Can capture */ +#define VID_TYPE_TUNER 2 /* Can tune */ +#define VID_TYPE_TELETEXT 4 /* Does teletext */ +#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ +#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ +#define VID_TYPE_CLIPPING 32 /* Can clip */ +#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ +#define VID_TYPE_SCALES 128 /* Scalable */ +#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ +#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ +#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ +#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ +#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ +#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ + +struct video_capability +{ + char name[32]; + int type; + int channels; /* Num channels */ + int audios; /* Num audio devices */ + int maxwidth; /* Supported width */ + int maxheight; /* And height */ + int minwidth; /* Supported width */ + int minheight; /* And height */ +}; + + +struct video_channel +{ + int channel; + char name[32]; + int tuners; + __u32 flags; +#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ +#define VIDEO_VC_AUDIO 2 /* Channel has audio */ + __u16 type; +#define VIDEO_TYPE_TV 1 +#define VIDEO_TYPE_CAMERA 2 + __u16 norm; /* Norm set by channel */ +}; + +struct video_tuner +{ + int tuner; + char name[32]; + unsigned long rangelow, rangehigh; /* Tuner range */ + __u32 flags; +#define VIDEO_TUNER_PAL 1 +#define VIDEO_TUNER_NTSC 2 +#define VIDEO_TUNER_SECAM 4 +#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ +#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ +#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ +#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ +#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ + __u16 mode; /* PAL/NTSC/SECAM/OTHER */ +#define VIDEO_MODE_PAL 0 +#define VIDEO_MODE_NTSC 1 +#define VIDEO_MODE_SECAM 2 +#define VIDEO_MODE_AUTO 3 + __u16 signal; /* Signal strength 16bit scale */ +}; + +struct video_picture +{ + __u16 brightness; + __u16 hue; + __u16 colour; + __u16 contrast; + __u16 whiteness; /* Black and white only */ + __u16 depth; /* Capture depth */ + __u16 palette; /* Palette in use */ +#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ +#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ +#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ +#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ +#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ +#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ +#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ +#define VIDEO_PALETTE_YUYV 8 +#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ +#define VIDEO_PALETTE_YUV420 10 +#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ +#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ +#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ +#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ +#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ +#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ +#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ +#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ +}; + +struct video_audio +{ + int audio; /* Audio channel */ + __u16 volume; /* If settable */ + __u16 bass, treble; + __u32 flags; +#define VIDEO_AUDIO_MUTE 1 +#define VIDEO_AUDIO_MUTABLE 2 +#define VIDEO_AUDIO_VOLUME 4 +#define VIDEO_AUDIO_BASS 8 +#define VIDEO_AUDIO_TREBLE 16 +#define VIDEO_AUDIO_BALANCE 32 + char name[16]; +#define VIDEO_SOUND_MONO 1 +#define VIDEO_SOUND_STEREO 2 +#define VIDEO_SOUND_LANG1 4 +#define VIDEO_SOUND_LANG2 8 + __u16 mode; + __u16 balance; /* Stereo balance */ + __u16 step; /* Step actual volume uses */ +}; + +struct video_clip +{ + __s32 x,y; + __s32 width, height; + struct video_clip *next; /* For user use/driver use only */ +}; + +struct video_window +{ + __u32 x,y; /* Position of window */ + __u32 width,height; /* Its size */ + __u32 chromakey; + __u32 flags; + struct video_clip *clips; /* Set only */ + int clipcount; +#define VIDEO_WINDOW_INTERLACE 1 +#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ +#define VIDEO_CLIP_BITMAP -1 +/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ +#define VIDEO_CLIPMAP_SIZE (128 * 625) +}; + +struct video_capture +{ + __u32 x,y; /* Offsets into image */ + __u32 width, height; /* Area to capture */ + __u16 decimation; /* Decimation divider */ + __u16 flags; /* Flags for capture */ +#define VIDEO_CAPTURE_ODD 0 /* Temporal */ +#define VIDEO_CAPTURE_EVEN 1 +}; + +struct video_buffer +{ + void *base; + int height,width; + int depth; + int bytesperline; +}; + +struct video_mmap +{ + unsigned int frame; /* Frame (0 - n) for double buffer */ + int height,width; + unsigned int format; /* should be VIDEO_PALETTE_* */ +}; + +struct video_key +{ + __u8 key[8]; + __u32 flags; +}; + +struct video_mbuf +{ + int size; /* Total memory to map */ + int frames; /* Frames */ + int offsets[VIDEO_MAX_FRAME]; +}; + +#define VIDEO_NO_UNIT (-1) + +struct video_unit +{ + int video; /* Video minor */ + int vbi; /* VBI minor */ + int radio; /* Radio minor */ + int audio; /* Audio minor */ + int teletext; /* Teletext minor */ +}; + +struct vbi_format { + __u32 sampling_rate; /* in Hz */ + __u32 samples_per_line; + __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ + __s32 start[2]; /* starting line for each frame */ + __u32 count[2]; /* count of lines for each frame */ + __u32 flags; +#define VBI_UNSYNC 1 /* can distingues between top/bottom field */ +#define VBI_INTERLACED 2 /* lines are interlaced */ +}; + +/* video_info is biased towards hardware mpeg encode/decode */ +/* but it could apply generically to any hardware compressor/decompressor */ +struct video_info +{ + __u32 frame_count; /* frames output since decode/encode began */ + __u32 h_size; /* current unscaled horizontal size */ + __u32 v_size; /* current unscaled veritcal size */ + __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ + __u32 picture_type; /* current picture type */ + __u32 temporal_reference; /* current temporal reference */ + __u8 user_data[256]; /* user data last found in compressed stream */ + /* user_data[0] contains user data flags, user_data[1] has count */ +}; + +/* generic structure for setting playback modes */ +struct video_play_mode +{ + int mode; + int p1; + int p2; +}; + +/* for loading microcode / fpga programming */ +struct video_code +{ + char loadwhat[16]; /* name or tag of file being passed */ + int datasize; + __u8 *data; +}; + +#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ +#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ +#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ +#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ +#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ +#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ +#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ +#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ +#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ +#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ +#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ +#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ +#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ +#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ +#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ +#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ +#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ +#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ +#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ +#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ +#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ +#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ +#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ +#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ +#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ +#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ +#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ +#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ +#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ + + +#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ + +/* VIDIOCSWRITEMODE */ +#define VID_WRITE_MPEG_AUD 0 +#define VID_WRITE_MPEG_VID 1 +#define VID_WRITE_OSD 2 +#define VID_WRITE_TTX 3 +#define VID_WRITE_CC 4 +#define VID_WRITE_MJPEG 5 + +/* VIDIOCSPLAYMODE */ +#define VID_PLAY_VID_OUT_MODE 0 + /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ +#define VID_PLAY_GENLOCK 1 + /* p1: 0 = OFF, 1 = ON */ + /* p2: GENLOCK FINE DELAY value */ +#define VID_PLAY_NORMAL 2 +#define VID_PLAY_PAUSE 3 +#define VID_PLAY_SINGLE_FRAME 4 +#define VID_PLAY_FAST_FORWARD 5 +#define VID_PLAY_SLOW_MOTION 6 +#define VID_PLAY_IMMEDIATE_NORMAL 7 +#define VID_PLAY_SWITCH_CHANNELS 8 +#define VID_PLAY_FREEZE_FRAME 9 +#define VID_PLAY_STILL_MODE 10 +#define VID_PLAY_MASTER_MODE 11 + /* p1: see below */ +#define VID_PLAY_MASTER_NONE 1 +#define VID_PLAY_MASTER_VIDEO 2 +#define VID_PLAY_MASTER_AUDIO 3 +#define VID_PLAY_ACTIVE_SCANLINES 12 + /* p1 = first active; p2 = last active */ +#define VID_PLAY_RESET 13 +#define VID_PLAY_END_MARK 14 + + +#endif /* __LINUX_VIDEODEV_H */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/gb.xml/AUTHORS b/gb.xml/AUTHORS new file mode 100755 index 00000000..e69de29b diff --git a/gb.xml/COPYING b/gb.xml/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.xml/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.xml/ChangeLog b/gb.xml/ChangeLog new file mode 100755 index 00000000..e69de29b diff --git a/gb.xml/INSTALL b/gb.xml/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.xml/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.xml/Makefile.am b/gb.xml/Makefile.am new file mode 100755 index 00000000..3765f66a --- /dev/null +++ b/gb.xml/Makefile.am @@ -0,0 +1,4 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @XML_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h + diff --git a/gb.xml/NEWS b/gb.xml/NEWS new file mode 100755 index 00000000..e69de29b diff --git a/gb.xml/README b/gb.xml/README new file mode 100755 index 00000000..53f792ab --- /dev/null +++ b/gb.xml/README @@ -0,0 +1,7 @@ +TODO : +GLrasterisation : +glBitmap() +glPolygonStipple() +glGetPolygonStipple() + +Waiting for texture implementation under components (needed to test !) diff --git a/gb.xml/TODO b/gb.xml/TODO new file mode 100644 index 00000000..1e9c72e1 --- /dev/null +++ b/gb.xml/TODO @@ -0,0 +1,8 @@ + +- Support "linked-nodes" (not urgent) + +- Make MANY tests with valgrind/gdb/whatyouwant , running many different projects, so that there isn't ANY bug or memory leak. + +- Check for any possible performance improvement + +- Improve my English. :/ \ No newline at end of file diff --git a/gb.xml/acinclude.m4 b/gb.xml/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.xml/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.xml/component.am b/gb.xml/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.xml/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.xml/configure.ac b/gb.xml/configure.ac new file mode 100644 index 00000000..557cec03 --- /dev/null +++ b/gb.xml/configure.ac @@ -0,0 +1,38 @@ +dnl ---- configure.ac for gb.xml + +m4_include([../version.m4]) +AC_INIT([gambas3-gb-xml],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.xml) +LT_INIT + +GB_COMPONENT( + xml, XML, gb.xml, [src], + [], + [], + [] +) + +GB_COMPONENT( + xmlhtml, XMLHTML, gb.xml.html, [html], + [], + [], + [] +) + +GB_COMPONENT_PKG_CONFIG( + xmlxslt, XMLXSLT, gb.xml.xslt, [xslt], + libxml-2.0 libxslt +) + +AC_CONFIG_FILES([\ +Makefile \ +src/Makefile \ +src/html/Makefile \ +src/xslt/Makefile \ +src/rpc/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES \ No newline at end of file diff --git a/gb.xml/gambas.h b/gb.xml/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.xml/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.xml/gb_common.h b/gb.xml/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.xml/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.xml/m4 b/gb.xml/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.xml/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.xml/reconf b/gb.xml/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.xml/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.xml/src/.directory b/gb.xml/src/.directory new file mode 100755 index 00000000..22f35d7f --- /dev/null +++ b/gb.xml/src/.directory @@ -0,0 +1,6 @@ +[Dolphin] +Timestamp=2012,2,8,18,41,28 +Version=2 + +[Settings] +ShowDotFiles=true diff --git a/gb.xml/src/CDocument.cpp b/gb.xml/src/CDocument.cpp new file mode 100644 index 00000000..1a8e3477 --- /dev/null +++ b/gb.xml/src/CDocument.cpp @@ -0,0 +1,238 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CDocument.h" +#include "CNode.h" + +#include "node.h" +#include "document.h" +#include "element.h" +#include "utils.h" +#include "serializer.h" + +#define GBTHIS (static_cast(_object)) +#define THIS ((Document*)(static_cast(_object)->node)) + +BEGIN_METHOD(CDocument_new, GB_STRING fileName) + +if(XMLNode_NoInstanciate()) return; + +bool isHtml = false; + +if(GB.Is(_object, GB.FindClass("HtmlDocument")))//Called as inherited HtmlDocument constructor +{ + if(CheckHtmlInterface()) + { + isHtml = true; + } +} + +try +{ + if(!MISSING(fileName)) + { + if(isHtml) + { + GBTHIS->node = (Node*)XMLDocument_NewFromFile(STRING(fileName), LENGTH(fileName), HTMLDocumentType); + } + else + { + GBTHIS->node = (Node*)XMLDocument_NewFromFile(STRING(fileName), LENGTH(fileName)); + } + } + else + { + if(isHtml) + { + GBTHIS->node = (Node*)HTML.HtmlDocument_New(); + } + else + { + GBTHIS->node = (Node*)XMLDocument_New(); + } + } + + THIS->GBObject = (CDocument*)(_object); + +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + GB.ReturnNull(); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD_VOID(CDocument_free) + +XMLNode_Free(GBTHIS->node); + +END_METHOD + +BEGIN_METHOD(CDocument_fromString, GB_STRING content) + +try +{ + XMLDocument_SetContent(THIS, STRING(content), LENGTH(content)); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD(CDocument_tostring, GB_BOOLEAN indent) + + char *str = 0; + size_t len = 0; + GBserializeNode((Node*)THIS, str, len, VARG(indent) == -1 ? 0 : -1); + + GB.ReturnString(str); + +END_METHOD + +BEGIN_PROPERTY(CDocument_content) + +if(READ_PROPERTY) +{ + char *str = 0; + size_t len = 0; + + GBserializeNode((Node*)THIS, str, len); + + GB.ReturnString(str); +} +else +{ + try + { + XMLDocument_SetContent(THIS, PSTRING(), PLENGTH()); + } + catch(XMLParseException &e) + { + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); + } +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_root) + +if(READ_PROPERTY) +{ + XML_ReturnNode((Node*)(THIS->root)); +} +else +{ + XMLDocument_SetRoot(THIS, ((Element*)(VPROPOBJ(CNode)->node))); +} + +END_PROPERTY + +BEGIN_METHOD(CDocument_open, GB_STRING fileName) + +try +{ + XMLDocument_Open(THIS, STRING(fileName), LENGTH(fileName)); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD(CDocument_save, GB_STRING fileName; GB_BOOLEAN indent) + +XMLDocument_Save(THIS, GB.ToZeroString(ARG(fileName)), VARG(indent)); + +END_METHOD + +BEGIN_METHOD(CDocument_getElementsByTagName, GB_STRING tagName; GB_INTEGER mode; GB_INTEGER depth;) + +GB_ARRAY array; + +XMLNode_getGBChildrenByTagName(THIS, STRING(tagName), LENGTH(tagName), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CDocument_getElementsByNamespace, GB_STRING name; GB_INTEGER mode; GB_INTEGER depth;) + +GB_ARRAY array; + +XMLNode_getGBChildrenByTagName(THIS, STRING(name), LENGTH(name), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_PROPERTY(CDocument_getAll) + +GB_ARRAY array; + +XMLNode_getGBAllChildren(THIS, &array); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CDocument_createElement, GB_STRING tagName) + +Element *elmt = XMLElement_New(STRING(tagName), LENGTH(tagName)); + +XML_ReturnNode(elmt); + +END_METHOD + +GB_DESC CDocumentDesc[] = +{ + GB_DECLARE("XmlDocument", sizeof(CDocument)), + + GB_METHOD("_new", NULL, CDocument_new, "[(FileName)s]"), + GB_METHOD("_free", NULL, CDocument_free, NULL), + + GB_METHOD("CreateElement", "XmlElement", CDocument_createElement, "(TagName)s"), + GB_PROPERTY("Root", "XmlElement", CDocument_root), + GB_PROPERTY_READ("All", "XmlNode[]", CDocument_getAll), + GB_METHOD("GetElementsByTagName", "XmlElement[]", CDocument_getElementsByTagName, "(TagName)s[(Mode)i(Depth)i]"), + GB_METHOD("GetElementsByNamespace", "XmlElement[]", CDocument_getElementsByNamespace, "(Namespace)s[(Mode)i(Depth)i]"), + + + + GB_PROPERTY("Content", "s", CDocument_content), + GB_METHOD("FromString", NULL, CDocument_fromString, "(Data)s"), + GB_METHOD("HtmlFromString", NULL, CDocument_fromString, "(Data)s"), + GB_METHOD("ToString", "s", CDocument_tostring, "[(Indent)b]"), + + GB_METHOD("Open", NULL, CDocument_open, "(FileName)s"), + GB_METHOD("Save", NULL, CDocument_save, "(FileName)s[(Indent)b]"), + GB_METHOD("Write", NULL, CDocument_save, "(FileName)s[(Indent)b]"), + + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CDocument.h b/gb.xml/src/CDocument.h new file mode 100644 index 00000000..10f0bc5c --- /dev/null +++ b/gb.xml/src/CDocument.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CDOCUMENT_H +#define CDOCUMENT_H + +#include "gbinterface.h" + +extern GB_DESC CDocumentDesc[]; + +#endif // CDOCUMENT_H diff --git a/gb.xml/src/CElement.cpp b/gb.xml/src/CElement.cpp new file mode 100644 index 00000000..8dc3a248 --- /dev/null +++ b/gb.xml/src/CElement.cpp @@ -0,0 +1,411 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CElement.h" + +#include "node.h" +#include "element.h" +#include "textnode.h" +#include "parser.h" + + +#include + +#define THIS ((Element*)(static_cast(_object)->node)) +#define THISNODE (static_cast(_object)->node) + +BEGIN_METHOD(CElement_new, GB_STRING tagName) + +if(XMLNode_NoInstanciate()) return; +if(!MISSING(tagName)) +{ + THISNODE = XMLElement_New(STRING(tagName), LENGTH(tagName)); +} +else +{ +THISNODE = XMLElement_New(); +} + THIS->GBObject = static_cast(_object); + +END_METHOD + +BEGIN_PROPERTY(CElement_tagName) + +if(READ_PROPERTY) +{ + if(!THIS->tagName || !THIS->lenTagName) + { + GB.ReturnNull(); + return; + } + else + { + GB.ReturnNewString(THIS->tagName, THIS->lenTagName); + } +} +else +{ + XMLElement_SetTagName(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CElement_prefix) + +if(READ_PROPERTY) +{ + if(!THIS->prefix || !THIS->lenPrefix) + { + GB.ReturnNull(); + return; + } + GB.ReturnNewString(THIS->prefix, THIS->lenPrefix); +} +else +{ + XMLElement_SetPrefix(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_METHOD(CElement_appendChild, GB_OBJECT newChild) + + if(!VARGOBJ(CNode, newChild)) + { + GB.Error("Null object"); + return; + } + XMLNode_appendChild(THIS, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_getAttribute, GB_STRING attrName; GB_INTEGER mode) + +Attribute *attr = XMLElement_GetAttribute(THIS, STRING(attrName), LENGTH(attrName), VARG(mode)); + +if(attr && attr->attrValue && attr->lenAttrValue) +{ + GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); +} +else +{ + GB.ReturnNull(); +} + +END_METHOD + +BEGIN_METHOD(CElement_removeAttribute, GB_STRING attrName) + + XMLElement_RemoveAttribute(THIS, STRING(attrName), LENGTH(attrName)); + +END_METHOD + +BEGIN_METHOD(CElement_setAttribute, GB_STRING attrName; GB_STRING attrValue) + + XMLElement_SetAttribute(THIS, STRING(attrName), LENGTH(attrName), + STRING(attrValue), LENGTH(attrValue)); + +END_METHOD + +BEGIN_METHOD(CElement_appendFromText, GB_STRING data; GB_VALUE param[0]) + +try +{ + if(GB.NParam() > 0) + { + XMLNode_substAppendFromText(THIS, STRING(data), LENGTH(data), ARG(param[0]), GB.NParam()); + } + else + { + XMLNode_appendFromText(THIS, STRING(data), LENGTH(data)); + } +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} +END_METHOD + +BEGIN_METHOD(CElement_appendChildren, GB_OBJECT children) + +GB_ARRAY array = reinterpret_cast(VARG(children)); + +for(int i = 0; i < GB.Array.Count(array); i++) +{ + XMLNode_appendChild(THIS, (*(reinterpret_cast(GB.Array.Get(array, i))))->node); +} + +END_METHOD + +BEGIN_METHOD(CElement_prependChild, GB_OBJECT newChild) + +XMLNode_prependChild(THIS, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_removeChild, GB_OBJECT oldChild) + +XMLNode_removeChild(THIS, VARGOBJ(CNode, oldChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_replaceChild, GB_OBJECT oldChild; GB_OBJECT newChild) + +XMLNode_replaceChild(THIS, VARGOBJ(CNode, oldChild)->node, + VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_insertAfter, GB_OBJECT child; GB_OBJECT newChild) + +XMLNode_insertAfter(THIS, VARGOBJ(CNode, child)->node, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_insertBefore, GB_OBJECT child; GB_OBJECT newChild) + +XMLNode_insertBefore(THIS, VARGOBJ(CNode, child)->node, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_appendText, GB_STRING data) + +XMLNode_appendText(THIS, STRING(data), LENGTH(data)); + +END_METHOD + +BEGIN_METHOD_VOID(CElement_clearChildren) + +XMLNode_clearChildren(THIS); + +END_METHOD + +BEGIN_PROPERTY(CElement_previousElement) + +XML_ReturnNode(XMLNode_previousElement(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_nextElement) + +XML_ReturnNode(XMLNode_nextElement(THIS)); + +END_PROPERTY + +BEGIN_METHOD(CElement_isAttributeSet, GB_STRING name) + +GB.ReturnBoolean((bool)(XMLElement_GetAttribute(THIS, STRING(name), LENGTH(name)))); + +END_METHOD + +BEGIN_PROPERTY(CElement_allChildNodes) + +GB_ARRAY array; +XMLNode_getGBAllChildren(THIS, &array); + +GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_firstChildElement) + +XML_ReturnNode(XMLNode_firstChildElement(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_lastChildElement) + +XML_ReturnNode(XMLNode_lastChildElement(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_childElements) + +GB_ARRAY array; +XMLNode_getGBChildElements(THIS, &array); +GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_METHOD(CElement_fromText, GB_STRING data) + +GB_ARRAY array; +try +{ + GBparseXML(STRING(data), LENGTH(data), &array); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByTagName, GB_STRING tagName; GB_INTEGER mode ; GB_INTEGER depth;) + +GB_ARRAY array; +XMLNode_getGBChildrenByTagName(THIS, STRING(tagName), LENGTH(tagName), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByNamespace, GB_STRING name; GB_INTEGER mode ; GB_INTEGER depth;) + +GB_ARRAY array; +XMLNode_getGBChildrenByNamespace(THIS, STRING(name), LENGTH(name), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByAttributeValue, GB_STRING name; GB_STRING value; GB_INTEGER mode ; GB_INTEGER depth;) + +GB_ARRAY array; +XMLNode_getGBChildrenByAttributeValue(THIS, STRING(name), LENGTH(name), + STRING(value), LENGTH(value), + &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_PROPERTY(CElement_firstChild) + +XML_ReturnNode(THIS->firstChild); + +END_METHOD + +BEGIN_PROPERTY(CElement_lastChild) + +XML_ReturnNode(THIS->lastChild); + +END_PROPERTY + +BEGIN_METHOD(CElement_normalizeAttributeContent, GB_STRING data) + +if(!LENGTH(data)) +{ + GB.ReturnNull(); + return; +} + +char *escapedData; size_t lenEscapedData; + +XMLText_escapeAttributeContent(STRING(data), LENGTH(data), escapedData, lenEscapedData); + +GB.ReturnNewString(escapedData, lenEscapedData); + +if(escapedData != STRING(data)) free(escapedData); + +END_METHOD + + + +GB_DESC CElementDesc[] = +{ + GB_DECLARE("XmlElement", sizeof(CNode)), GB_INHERITS("XmlNode"), + + GB_METHOD("_new", NULL, CElement_new, "[(TagName)s]"), + + GB_METHOD("AppendChild", NULL, CElement_appendChild, "(NewChild)XmlNode"), + GB_METHOD("AppendChildren", NULL, CElement_appendChildren, "(NewChildren)XmlNode[]"), + GB_METHOD("PrependChild", NULL, CElement_prependChild, "(NewChild)XmlNode"), + GB_METHOD("InsertAfter", NULL, CElement_insertAfter, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("InsertBefore", NULL, CElement_insertBefore, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("RemoveChild", NULL, CElement_removeChild, "(OldChild)XmlNode"), + GB_METHOD("ReplaceChild", NULL, CElement_replaceChild, "(OldChild)XmlNode;(NewChild)XmlNode"), + GB_METHOD("ClearChildren", NULL, CElement_clearChildren, ""), + + GB_METHOD("AppendText", NULL, CElement_appendText, "(Data)s"), + GB_METHOD("AppendFromText", NULL, CElement_appendFromText, "(Data)s(Arguments)."), + + GB_METHOD("GetAttribute", "s", CElement_getAttribute, "(Name)s[(Mode)i]"), + GB_METHOD("RemoveAttribute", NULL, CElement_removeAttribute, "(Name)s"), + GB_METHOD("SetAttribute", NULL, CElement_setAttribute, "(Name)s(Value)s"), + + GB_METHOD("IsAttributeSet", "b", CElement_isAttributeSet, "(Name)s"), + + GB_PROPERTY_READ("ChildElements", "XmlElement[]", CElement_childElements), + GB_PROPERTY_READ("AllChildNodes", "XmlNode[]", CElement_allChildNodes), + GB_PROPERTY_READ("FirstChild", "XmlNode", CElement_firstChild), + GB_PROPERTY_READ("LastChild", "XmlNode", CElement_lastChild), + GB_PROPERTY_READ("FirstChildElement", "XmlElement", CElement_firstChildElement), + GB_PROPERTY_READ("LastChildElement", "XmlElement", CElement_lastChildElement), + + GB_PROPERTY("TagName", "s", CElement_tagName), + GB_PROPERTY("Prefix", "s", CElement_prefix), + GB_PROPERTY("PreviousElement", "XmlElement", CElement_previousElement), + GB_PROPERTY("NextElement", "XmlElement", CElement_nextElement), + + GB_METHOD("GetChildrenByNamespace", "XmlElement[]", CElement_getChildrenByNamespace, "(Namespace)s[(Mode)i(Depth)i]"), + GB_METHOD("GetChildrenByTagName", "XmlElement[]", CElement_getChildrenByTagName, "(TagName)s[(Mode)i(Depth)i]"), + GB_METHOD("GetChildrenByAttributeValue", "XmlElement[]", CElement_getChildrenByAttributeValue, "(Attribute)s(Value)s[(Mode)i(Depth)i]"), + + GB_STATIC_METHOD("FromText", "XmlNode[]", CElement_fromText, "(Data)s"), + GB_STATIC_METHOD("NormalizeAttributeContent", "s", CElement_normalizeAttributeContent, "(Data)s"), + + + GB_END_DECLARE +}; + +/* +GB_DESC CElementDesc[] = +{ + GB_DECLARE("XmlElement", sizeof(CElement)), GB_INHERITS("XmlNode"), + GB_METHOD("_new", NULL, CElement_new, "[(TagName)s]"), + GB_METHOD("_free", NULL, CElement_free, ""), + GB_METHOD("AppendChild", NULL, CElement_AppendChild, "(NewChild)XmlNode"), + GB_METHOD("AppendChildren", NULL, CElement_AppendChildren, "(NewChildren)XmlNode[]"), + GB_METHOD("PrependChild", NULL, CElement_prependChild, "(NewChild)XmlNode"), + GB_METHOD("InsertAfter", NULL, CElement_insertAfter, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("InsertBefore", NULL, CElement_insertBefore, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("AppendText", NULL, CElement_AppendText, "(NewText)s"), + GB_METHOD("AppendFromText", NULL, CElement_appendFromText, "(Data)s"), + GB_METHOD("RemoveChild", NULL, CElement_removeChild, "(OldChild)XmlNode"), + GB_METHOD("ReplaceChild", NULL, CElement_replaceChild, "(OldChild)XmlNode;(NewChild)XmlNode"), + GB_METHOD("NewElement", NULL, CElement_newElement, "(Name)s[(Value)s]"), + + GB_PROPERTY("TagName", "s", CElement_tagName), + + GB_PROPERTY_READ("PreviousSibling", "XmlElement", CElement_previousSibling), + GB_PROPERTY_READ("NextSibling", "XmlElement", CElement_nextSibling), + + GB_METHOD("GetAttribute", "s", CElement_getAttribute, "(Name)s"), + GB_METHOD("SetAttribute", NULL, CElement_setAttribute, "(Name)s(Value)s"), + GB_METHOD("NewAttribute", NULL, CElement_setAttribute, "(Name)s(Value)s"), + GB_METHOD("IsAttributeSet", "b", CElement_isAttributeSet, "(Name)s"), + + GB_PROPERTY_READ("ChildNodes", "XmlNode[]", CElement_childNodes), + GB_PROPERTY_READ("Children", "XmlNode[]", CElement_childNodes), + GB_PROPERTY_READ("ChildElements", "XmlElement[]", CElement_childElements), + GB_PROPERTY_READ("AllChildNodes", "XmlNode[]", CElement_allChildNodes), + GB_PROPERTY_READ("FirstChildElement", "XmlElement", CElement_firstChildElement), + GB_PROPERTY_READ("LastChildElement", "XmlElement", CElement_lastChildElement), + + GB_METHOD("MatchXPathFilter", "b", CElement_matchXPathFilter ,"(Filter)s"), + GB_METHOD("GetChildrenByTagName", "XmlElement[]", CElement_getChildrenByTagName, "(TagName)s[(Depth)i]"), + GB_METHOD("GetChildrenByAttributeValue", "XmlElement[]", CElement_getChildrenByAttributeValue, "(Attribute)s(Value)s[(Depth)i]"), + + GB_STATIC_METHOD("FromText", "XmlNode[]", CElement_fromText, "(Data)s"), + + GB_END_DECLARE +};*/ diff --git a/gb.xml/src/CElement.h b/gb.xml/src/CElement.h new file mode 100644 index 00000000..958980e6 --- /dev/null +++ b/gb.xml/src/CElement.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CELEMENT_H +#define CELEMENT_H + +#include "gbinterface.h" + +extern GB_DESC CElementDesc[]; + +#endif // CELEMENT_H diff --git a/gb.xml/src/CExplorer.cpp b/gb.xml/src/CExplorer.cpp new file mode 100644 index 00000000..16c08e55 --- /dev/null +++ b/gb.xml/src/CExplorer.cpp @@ -0,0 +1,154 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CExplorer.h" +#include "explorer.h" + +#include "node.h" +#include "document.h" +#include "utils.h" + +#undef THIS +#define THIS (static_cast(_object)->explorer) + +BEGIN_METHOD(CExplorerReadFlags_get, GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0) +{ + GB.ReturnBoolean(false); + return; +} +GB.ReturnBoolean(THIS->flags[flag]); + +END_METHOD + +BEGIN_METHOD(CExplorerReadFlags_put, GB_BOOLEAN value; GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0 || flag == READ_ERR_EOF) return; +THIS->flags[flag] = VARG(value); + +END_METHOD + +BEGIN_PROPERTY(CExplorer_Node) + +XML_ReturnNode(THIS->curNode); + +END_PROPERTY + +BEGIN_METHOD_VOID(CExplorer_Read) + +GB.ReturnInteger(THIS->Read()); + +END_METHOD + +BEGIN_METHOD(CExplorer_new, GB_OBJECT doc) + +THIS = new Explorer; + +if(!MISSING(doc)) +{ + THIS->Load((Document*)(VARGOBJ(CDocument, doc)->node)); +} + +END_METHOD + +BEGIN_METHOD_VOID(CExplorer_free) + +delete THIS; + +END_METHOD + +BEGIN_METHOD(CExplorer_open, GB_STRING path) + +try +{ + Document *doc = XMLDocument_NewFromFile(STRING(path), LENGTH(path)); + THIS->Load(doc); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + + +END_METHOD + +BEGIN_PROPERTY(CExplorer_eof) + +GB.ReturnBoolean(THIS->eof); + +END_PROPERTY + +BEGIN_PROPERTY(CExplorer_state) + +GB.ReturnInteger(THIS->state); + +END_PROPERTY + +BEGIN_METHOD(CExplorer_load, GB_OBJECT doc) + +THIS->Load((Document*)(VARGOBJ(CDocument, doc)->node)); + +END_METHOD + +BEGIN_PROPERTY(CExplorer_document) + +if(READ_PROPERTY) +{ + XML_ReturnNode(THIS->loadedDocument); +} +else +{ + THIS->Load((Document*)(VPROPOBJ(CDocument)->node)); +} + +END_PROPERTY + +GB_DESC CExplorerReadFlagsDesc[] = +{ + GB_DECLARE(".XmlExplorerReadFlags", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "b", CExplorerReadFlags_get, "(Flag)i"), + GB_METHOD("_put", "b", CExplorerReadFlags_put, "(Value)b(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC CExplorerDesc[] = +{ + GB_DECLARE("XmlExplorer", sizeof(CExplorer)), + + GB_METHOD("_new", NULL, CExplorer_new, "[(Document)XmlDocument;]"), + GB_METHOD("_free", NULL, CExplorer_free, ""), + GB_METHOD("Load", NULL, CExplorer_load, "(Document)XmlDocument"), + GB_PROPERTY("Document", "XMLDocument", CExplorer_document), + GB_PROPERTY_SELF("ReadFlags", ".XmlExplorerReadFlags"), + GB_PROPERTY_READ("Node", "XmlNode", CExplorer_Node), + GB_PROPERTY_READ("Eof", "b", CExplorer_eof), + GB_PROPERTY_READ("State", "i", CExplorer_state), + GB_METHOD("Read", "i", CExplorer_Read, ""), + GB_METHOD("Open", NULL, CExplorer_open, "(Path)s"), + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CExplorer.h b/gb.xml/src/CExplorer.h new file mode 100644 index 00000000..308f306f --- /dev/null +++ b/gb.xml/src/CExplorer.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CEXPLORER_H +#define CEXPLORER_H + +#include "gbinterface.h" + +class Explorer; + +struct CExplorer +{ + GB_BASE ob; + Explorer *explorer; +}; + +extern GB_DESC CExplorerDesc[]; + +#endif // CEXPLORER_H diff --git a/gb.xml/src/CNode.cpp b/gb.xml/src/CNode.cpp new file mode 100644 index 00000000..527942f5 --- /dev/null +++ b/gb.xml/src/CNode.cpp @@ -0,0 +1,438 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CNode.h" +#include "node.h" +#include "document.h" +#include "element.h" +#include "textnode.h" +#include "serializer.h" +#include + +#define THISOBJ ((CNode*)_object) +#define THIS (static_cast(_object)->node) + +#define NODE_BASE 0 +#define NODE_ELEMENT 1 +#define NODE_TEXT 2 +#define NODE_COMMENT 3 +#define NODE_CDATA 4 +#define NODE_ATTRIBUTE 5 +#define NODE_DOCUMENT 6 + +BEGIN_METHOD_VOID(CNode_new) + +if(XMLNode_NoInstanciate()) return; + + +END_METHOD + +BEGIN_METHOD_VOID(CNode_free) + +XMLNode_DestroyGBObject(THIS); + +END_METHOD + +BEGIN_METHOD(CNode_tostring, GB_BOOLEAN indent) + + char *str = 0; + size_t len = 0; + + GBserializeNode(THIS, str, len, VARG(indent) ? 0 : -1); + + GB.ReturnString(str); + +END_METHOD + +BEGIN_PROPERTY(CNode_type) + +switch(THIS->type) +{ + case Node::ElementNode: + GB.ReturnInteger(NODE_ELEMENT);break; + case Node::Comment: + GB.ReturnInteger(NODE_COMMENT);break; + case Node::NodeText: + GB.ReturnInteger(NODE_TEXT);break; + case Node::CDATA: + GB.ReturnInteger(NODE_CDATA);break; + default: + GB.ReturnInteger(NODE_BASE); +} + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isElement) + +GB.ReturnBoolean(THIS->type == Node::ElementNode); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isText) + +GB.ReturnBoolean(THIS->type == Node::NodeText); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isComment) + +GB.ReturnBoolean(THIS->type == Node::Comment); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isCDATA) + +GB.ReturnBoolean(THIS->type == Node::CDATA); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_element) + +XML_ReturnNode(THIS); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_ownerDocument) + +XML_ReturnNode((Node*)XMLNode_GetOwnerDocument((Node*)THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_parent) + +XML_ReturnNode((Node*)(THIS->parent)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_previous) + +XML_ReturnNode((Node*)(THIS->previousNode)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_next) + +XML_ReturnNode((Node*)(THIS->nextNode)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_textContent) + + +if(READ_PROPERTY) +{ + char *data; size_t len; + GBGetXMLTextContent(THIS, data, len); + GB.ReturnString(data); +} +else +{ + XMLNode_setTextContent(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CNode_name) + +if(!READ_PROPERTY) +{ + if(THIS->type == Node::ElementNode) + { + XMLElement_SetTagName((Element*)THIS, PSTRING(), PLENGTH()); + } + return; +} + +switch (THIS->type) +{ + case Node::ElementNode: + GB.ReturnNewString(((Element*)THIS)->tagName, ((Element*)THIS)->lenTagName);break; + case Node::NodeText: + GB.ReturnNewZeroString("#text");break; + case Node::Comment: + GB.ReturnNewZeroString("#comment");break; + case Node::CDATA: + GB.ReturnNewZeroString("#cdata");break; + case Node::AttributeNode: + GB.ReturnNewString(((Attribute*)THIS)->attrName, ((Attribute*)THIS)->lenAttrName);break; + default: + GB.ReturnNewZeroString(""); +} + +END_PROPERTY + + +BEGIN_METHOD(CNode_newElement, GB_STRING name; GB_STRING value) + +if(!SUPPORT_CHILDREN(THIS)) return; +Element *elmt = XMLElement_New(STRING(name), LENGTH(name)); +if(!MISSING(value)) XMLElement_SetTextContent(elmt, STRING(value), LENGTH(value)); +XMLNode_appendChild(THIS, elmt); + +END_METHOD + +BEGIN_METHOD(CNode_setAttribute, GB_STRING attr; GB_STRING val) + +if(THIS->type != Node::ElementNode) return; + +XMLElement_SetAttribute(((Element*)THIS), STRING(attr), LENGTH(attr), + STRING(val), LENGTH(val)); + +END_METHOD + + +BEGIN_METHOD(CElementAttributes_get, GB_STRING name) + +if(THIS->type != Node::ElementNode) return; + +Attribute *attr = XMLElement_GetAttribute((Element*)THIS, STRING(name), LENGTH(name)); + +if(attr && attr->attrValue && attr->lenAttrValue) +{ + GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); +} +else +{ + GB.ReturnNull(); +} + +END_METHOD + +BEGIN_METHOD(CElementAttributes_put, GB_STRING value; GB_STRING name) + +if(THIS->type != Node::ElementNode) return; + +XMLElement_SetAttribute((Element*)THIS, STRING(name), LENGTH(name), + STRING(value), LENGTH(value)); + +END_METHOD + +BEGIN_PROPERTY(CElementAttributes_count) + +if(THIS->type != Node::ElementNode) +{ + GB.ReturnInteger(0); + return; +} + +if(READ_PROPERTY) +{ + GB.ReturnInteger(((Element*)THIS)->attributeCount); +} + +END_PROPERTY + +BEGIN_METHOD_VOID(CElementAttributes_next) + +if(THIS->type != Node::ElementNode) {GB.StopEnum(); return;} + +Attribute *attr = *reinterpret_cast((GB.GetEnum())); +if(attr == 0) +{ + attr = ((Element*)THIS)->firstAttribute; + *reinterpret_cast(GB.GetEnum()) = attr; +} +else +{ + attr = (Attribute*)attr->nextNode; + *reinterpret_cast(GB.GetEnum()) = attr; +} + +THISOBJ->curAttrEnum = attr; + +if(attr == 0) {GB.StopEnum(); return;} + + +XML_ReturnNode(attr); + + +END_METHOD + + +BEGIN_PROPERTY(CElementAttributes_name) + +if(!THISOBJ->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + +if(!THISOBJ->curAttrEnum->attrName || !THISOBJ->curAttrEnum->lenAttrName) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THISOBJ->curAttrEnum->attrName, THISOBJ->curAttrEnum->lenAttrName); + +END_PROPERTY + +BEGIN_PROPERTY(CElementAttributes_value) + +if(!THISOBJ->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + +if(!THISOBJ->curAttrEnum->attrValue || !THISOBJ->curAttrEnum->lenAttrValue) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THISOBJ->curAttrEnum->attrValue, THISOBJ->curAttrEnum->lenAttrValue); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_childNodes) + +GB_ARRAY array; +XMLNode_getGBChildren(THIS, &array); + +GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_METHOD(CNode_getUserData, GB_STRING key) + +GB_VARIANT *value = XMLNode_getUserData(THIS, STRING(key), LENGTH(key)); + +if(value) +{ + GB.ReturnVariant(&(value->value)); + delete value; +} +else +{ + GB.ReturnNull(); +} + +END_METHOD + +BEGIN_METHOD(CNode_setUserData, GB_STRING key; GB_VARIANT value) + +XMLNode_addUserData(THIS, STRING(key), LENGTH(key), ARG(value)); + +END_METHOD + +BEGIN_METHOD(CNode_escapeContent, GB_STRING data) + +if(!LENGTH(data)) +{ + GB.ReturnNull(); + return; +} + +char *escapedData; size_t lenEscapedData; + +XMLText_escapeContent(STRING(data), LENGTH(data), escapedData, lenEscapedData); + +GB.ReturnNewString(escapedData, lenEscapedData); + +if(escapedData != STRING(data)) free(escapedData); + +END_METHOD + +BEGIN_METHOD(CNode_unEscapeContent, GB_STRING data) + +if(!LENGTH(data)) +{ + GB.ReturnNull(); + return; +} + +char *unescapedData; size_t lenUnEscapedData; + +XMLText_unEscapeContent(STRING(data), LENGTH(data), unescapedData, lenUnEscapedData); + +GB.ReturnNewString(unescapedData, lenUnEscapedData); + +if(unescapedData != STRING(data)) free(unescapedData); + +END_METHOD + +GB_DESC CElementAttributeNodeDesc[] = +{ + GB_DECLARE("_XmlAttrNode", sizeof(CNode)), GB_INHERITS("XmlNode"), + + GB_END_DECLARE +}; + +GB_DESC CElementAttributesDesc[] = +{ + GB_DECLARE(".XmlElementAttributes", 0), GB_VIRTUAL_CLASS(), + GB_METHOD("_get", "s", CElementAttributes_get, "(Name)s"), + GB_METHOD("_put", NULL, CElementAttributes_put, "(Value)s(Name)s"), + GB_METHOD("_next", "XmlNode", CElementAttributes_next, ""), + GB_PROPERTY_READ("Count", "i", CElementAttributes_count), + GB_PROPERTY_READ("Name", "s", CElementAttributes_name), + GB_PROPERTY_READ("Value", "s", CElementAttributes_value), + GB_END_DECLARE +}; + +GB_DESC CNodeDesc[] = +{ + GB_DECLARE("XmlNode", sizeof(CNode)), GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, CNode_new, ""), + GB_METHOD("_free", NULL, CNode_free, ""), + + GB_CONSTANT("ElementNode", "i", NODE_ELEMENT), + GB_CONSTANT("TextNode", "i", NODE_TEXT), + GB_CONSTANT("CommentNode", "i", NODE_COMMENT), + GB_CONSTANT("CDATASectionNode", "i", NODE_CDATA), + GB_CONSTANT("AttributeNode", "i", NODE_ATTRIBUTE), + + GB_PROPERTY_READ("Type", "i", CNode_type), + GB_PROPERTY_READ("IsElement", "b", CNode_isElement), + GB_PROPERTY_READ("IsText", "b", CNode_isText), + GB_PROPERTY_READ("IsComment", "b", CNode_isComment), + GB_PROPERTY_READ("IsCDATA", "b", CNode_isCDATA), + + GB_PROPERTY_READ("Element", "XmlElement", CNode_element), + GB_PROPERTY_READ("OwnerDocument", "XmlDocument", CNode_ownerDocument), + GB_PROPERTY_READ("ChildNodes", "XmlNode[]", CNode_childNodes), + GB_PROPERTY_READ("Children", "XmlNode[]", CNode_childNodes), + GB_PROPERTY_READ("Parent", "XmlElement", CNode_parent), + GB_PROPERTY_READ("Previous", "XmlNode", CNode_previous), + GB_PROPERTY_READ("Next", "XmlNode", CNode_next), + GB_PROPERTY_READ("PreviousSibling", "XmlNode", CNode_previous), + GB_PROPERTY_READ("NextSibling", "XmlNode", CNode_next), + + GB_METHOD("ToString", "s", CNode_tostring, "[(Indent)b]"), + GB_METHOD("GetUserData", "v", CNode_getUserData, "(Key)s"), + GB_METHOD("SetUserData", NULL, CNode_setUserData, "(Key)s(Value)v"), + GB_PROPERTY("TextContent", "s", CNode_textContent), + GB_PROPERTY("Value", "s", CNode_textContent), + GB_PROPERTY("Name", "s", CNode_name), + + GB_STATIC_METHOD("Serialize", "s", CNode_escapeContent, "(Data)s"), + GB_STATIC_METHOD("Deserialize", "s", CNode_unEscapeContent, "(Data)s"), + + GB_METHOD("NewElement", NULL, CNode_newElement, "(Name)s[(Value)s]"), + GB_METHOD("NewAttribute", NULL, CNode_setAttribute, "(Name)s(Value)s"), + GB_PROPERTY_SELF("Attributes", ".XmlElementAttributes"), + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CNode.h b/gb.xml/src/CNode.h new file mode 100644 index 00000000..ac08e51c --- /dev/null +++ b/gb.xml/src/CNode.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CNODE_H +#define CNODE_H + +#include "gbinterface.h" + +extern GB_DESC CNodeDesc[]; +extern GB_DESC CElementAttributesDesc[]; +extern GB_DESC CElementAttributeNodeDesc[]; + +#endif // CNODE_H diff --git a/gb.xml/src/CReader.cpp b/gb.xml/src/CReader.cpp new file mode 100644 index 00000000..ca0b9381 --- /dev/null +++ b/gb.xml/src/CReader.cpp @@ -0,0 +1,435 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CReader.h" +#include "reader.h" +#include "element.h" +#include "serializer.h" +#include "utils.h" + +#undef THIS +#define THIS (static_cast(_object)->reader) + +BEGIN_METHOD_VOID(CReader_new) + +THIS = new Reader; + +END_METHOD + +BEGIN_METHOD_VOID(CReader_free) + +delete THIS; + +END_METHOD + +BEGIN_METHOD(CReader_ReadChar, GB_STRING car) + +if(!LENGTH(car)) return; +try +{ + GB.ReturnInteger(THIS->ReadChar(STRING(car)[0])); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD_VOID(CReader_Close) + +THIS->ClearReader(); + +END_METHOD + +BEGIN_PROPERTY(CReader_keepData) + +if(READ_PROPERTY) +{ + GB.ReturnBoolean(THIS->keepMemory); +} +else +{ + THIS->keepMemory = VPROP(GB_BOOLEAN); + if(THIS->keepMemory && THIS->foundNode) + { + //TODO : + //THIS->storedElements->push_back(THIS->foundNode); + //GB.Ref(THIS->foundNode); + } +} + +END_PROPERTY + +BEGIN_PROPERTY(CReader_pos) + +if(!READ_PROPERTY) return; + +GB.ReturnInteger(THIS->pos); + +END_PROPERTY + +BEGIN_METHOD_VOID(CReaderNodeAttr_next) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.StopEnum(); return; +} + +if(THIS->foundNode->type != Node::ElementNode) +{ + GB.StopEnum(); return; +} + +Attribute *attr = *reinterpret_cast((GB.GetEnum())); +if(attr == 0) +{ + attr = ((Element*)(THIS->foundNode))->firstAttribute; + *reinterpret_cast(GB.GetEnum()) = attr; + (THIS->depth)++; +} +else +{ + attr = (Attribute*)attr->nextNode; + *reinterpret_cast(GB.GetEnum()) = attr; +} + +if(attr == 0) {GB.StopEnum(); THIS->curAttrEnum = 0; (THIS->depth)--; return;} + +THIS->curAttrEnum = attr; + +if((attr->attrValue && attr->lenAttrValue)) +{ + GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); +} +else +{ + GB.ReturnNewZeroString(0); +} + +END_METHOD + +BEGIN_METHOD(CReaderNodeAttr_get, GB_STRING name) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + return; +} + +if(!THIS->foundNode->type == Node::ElementNode) return; + +Attribute *attr = XMLElement_GetAttribute((Element*)(THIS->foundNode), STRING(name), LENGTH(name)); + +if (!attr) +{ + GB.Error("No such attribute"); + return; +} + +GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); + +END_METHOD + +BEGIN_METHOD(CReaderNodeAttr_Exist, GB_STRING name) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + return; +} + +if(!THIS->foundNode->type == Node::ElementNode) return; + +Attribute *attr = XMLElement_GetAttribute((Element*)(THIS->foundNode), STRING(name), LENGTH(name)); + +GB.ReturnBoolean(!!attr); + +END_METHOD + +BEGIN_PROPERTY(CReaderNodeAttr_count) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.ReturnInteger(0); + return; +} + +if(THIS->foundNode->type == Node::ElementNode) +{ + GB.ReturnInteger(((Element*)(THIS->foundNode))->attributeCount); +} +else +{ +GB.ReturnInteger(0); +} + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNodeAttr_name) + +if(!THIS->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + + +if(!THIS->curAttrEnum->attrName || !THIS->curAttrEnum->lenAttrName) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THIS->curAttrEnum->attrName, THIS->curAttrEnum->lenAttrName); + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNodeAttr_value) + +if(!THIS->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + +if(!THIS->curAttrEnum->attrValue || !THIS->curAttrEnum->lenAttrValue) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THIS->curAttrEnum->attrValue, THIS->curAttrEnum->lenAttrValue); + +END_PROPERTY + +BEGIN_METHOD(CReaderReadFlags_get, GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0) return; +GB.ReturnBoolean(THIS->flags[flag]); + +END_METHOD + +BEGIN_METHOD(CReaderReadFlags_put, GB_BOOLEAN value; GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0 || flag == READ_ERR_EOF) return; +THIS->flags[flag] = VARG(value); + +END_METHOD + +BEGIN_PROPERTY(CReaderNode_type) + +GB.ReturnInteger(THIS->state); +END_PROPERTY + +BEGIN_PROPERTY(CReaderNode_Value) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.ReturnNull(); + return; +} + + +if(THIS->curAttrEnum) +{ + if((THIS->curAttrEnum->attrValue && THIS->curAttrEnum->lenAttrValue)) + { + GB.ReturnNewString(THIS->curAttrEnum->attrValue, THIS->curAttrEnum->lenAttrValue); + } + else + { + GB.ReturnNewZeroString(0); + } + return; +} + +char *data; size_t len; +GBGetXMLTextContent(THIS->foundNode, data, len); +GB.ReturnString(data); + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNode_Name) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.ReturnNull(); + return; +} + +if(THIS->curAttrEnum) +{ + GB.ReturnNewString(THIS->curAttrEnum->attrName, THIS->curAttrEnum->lenAttrName); + return; +} + +switch (THIS->foundNode->type) +{ + case Node::ElementNode: + GB.ReturnNewString(((Element*)(THIS->foundNode))->tagName, + ((Element*)(THIS->foundNode))->lenTagName); break; + case Node::NodeText: + GB.ReturnNewZeroString("#text");break; + case Node::Comment: + GB.ReturnNewZeroString("#comment");break; + case Node::CDATA: + GB.ReturnNewZeroString("#cdata");break; + default: + GB.ReturnNull(); +} + + +END_PROPERTY + +BEGIN_PROPERTY(CReader_Depth) + +if(THIS->depth < 0) +{ + GB.ReturnInteger(0); + return; +} +GB.ReturnInteger(THIS->depth); + +END_PROPERTY + +BEGIN_PROPERTY(CReader_storedNodes) + +if(!READ_PROPERTY) return; + +GB.ReturnObject(0); + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNode_IsEmptyElement) + +if(!THIS->foundNode) +{ + GB.ReturnBoolean(false); + return; +} + +if(!THIS->foundNode->type != Node::ElementNode) +{ + GB.ReturnBoolean(false); + return; +} + +GB.ReturnBoolean(THIS->waitClosingElmt); + +END_PROPERTY + + + +GB_DESC CReaderNodeTypeDesc[] = +{ + GB_DECLARE("XmlReaderNodeType", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", 0), + GB_CONSTANT("Element", "i", NODE_ELEMENT), + GB_CONSTANT("Attribute", "i", READ_ATTRIBUTE), + GB_CONSTANT("Text", "i", NODE_TEXT), + GB_CONSTANT("CDATA", "i", NODE_CDATA), + GB_CONSTANT("EntityReference", "i", 0), + GB_CONSTANT("Entity", "i", 0), + GB_CONSTANT("ProcessingInstruction", "i", 0), + GB_CONSTANT("Comment", "i", NODE_COMMENT), + GB_CONSTANT("Document", "i", 0), + GB_CONSTANT("DocumentType", "i", 0), + GB_CONSTANT("DocumentFragment", "i", 0), + GB_CONSTANT("Notation", "i", 0), + GB_CONSTANT("Whitespace", "i",0), + GB_CONSTANT("SignificantWhitespace", "i",0), + GB_CONSTANT("EndElement", "i", READ_END_CUR_ELEMENT), + GB_CONSTANT("EndStream", "i", READ_ERR_EOF), + GB_CONSTANT("EndEntity", "i", 0), + GB_CONSTANT("XmlDeclaration", "i",0), + + GB_END_DECLARE +}; + +GB_DESC CReaderReadFlagsDesc[] = +{ + GB_DECLARE(".XmlReaderReadFlags", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "b", CReaderReadFlags_get, "(Flag)i"), + GB_METHOD("_put", NULL, CReaderReadFlags_put, "(Value)b(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC CReaderNodeAttributesDesc[] = +{ + GB_DECLARE(".XmlReader.Node.Attributes", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "s", CReaderNodeAttr_get, "(Name)s"), + GB_METHOD("_next", "s", CReaderNodeAttr_next, ""), + GB_METHOD("Exist", "b", CReaderNodeAttr_Exist, "(Name)s"), + GB_PROPERTY_READ("Count", "i", CReaderNodeAttr_count), + GB_PROPERTY_READ("Name", "i", CReaderNodeAttr_name), + GB_PROPERTY_READ("Value", "i", CReaderNodeAttr_value), + + GB_END_DECLARE +}; + +GB_DESC CReaderNodeDesc[] = +{ + GB_DECLARE(".XmlReader.Node", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_SELF("Attributes", ".XmlReader.Node.Attributes"), + //GB_PROPERTY_READ("BaseUri","s",CRNODE_BaseUri), + GB_PROPERTY_READ("Depth","i",CReader_Depth), + //GB_PROPERTY_READ("IsDefault","b",CRNODE_IsDefault), + GB_PROPERTY_READ("IsEmptyElement","b",CReaderNode_IsEmptyElement), + //GB_PROPERTY_READ("LocalName","s",CRNODE_LocalName), + GB_PROPERTY_READ("Name", "s", CReaderNode_Name), + //GB_PROPERTY_READ("NamespaceUri", "s", CRNODE_NamespaceUri), + //GB_PROPERTY_READ("Prefix", "s", CRNODE_Prefix), + //GB_PROPERTY_READ("QuoteChar", "s", CRNODE_QuoteChar), + GB_PROPERTY_READ("Type","i",CReaderNode_type), + GB_PROPERTY_READ("Value", "s", CReaderNode_Value), + //GB_PROPERTY_READ("XmlLang", "s", CRNODE_XmlLang), + + GB_END_DECLARE +}; + +GB_DESC CReaderDesc[] = +{ + GB_DECLARE("_XmlReader", sizeof(CReader)), + + GB_METHOD("_new", NULL, CReader_new, ""), + GB_METHOD("_free", NULL, CReader_free, ""), + GB_METHOD("_ReadChar", "i", CReader_ReadChar, "(Char)s"), + GB_METHOD("_Close", "i", CReader_Close, ""), + + GB_PROPERTY("KeepData", "b", CReader_keepData), + GB_PROPERTY_READ("Pos", "i", CReader_pos), + GB_PROPERTY_SELF("Node", ".XmlReader.Node"), + GB_PROPERTY_READ("StoredNodes", "XmlNode[]", CReader_storedNodes), + GB_PROPERTY_READ("Depth","i",CReader_Depth), + + GB_PROPERTY_SELF("ReadFlags", ".XmlReaderReadFlags"), + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CReader.h b/gb.xml/src/CReader.h new file mode 100644 index 00000000..f92bb71f --- /dev/null +++ b/gb.xml/src/CReader.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CXMLREADER_H +#define CXMLREADER_H + +#include "gbinterface.h" + +class Reader; + +typedef struct CReader +{ + GB_BASE ob; + Reader *reader; +} CReader; + +extern GB_DESC CReaderDesc[]; +extern GB_DESC CReaderNodeDesc[]; +extern GB_DESC CReaderNodeTypeDesc[]; +extern GB_DESC CReaderNodeAttributesDesc[]; +extern GB_DESC CReaderReadFlagsDesc[]; + + +#endif // CXMLREADER_H diff --git a/gb.xml/src/CTextNode.cpp b/gb.xml/src/CTextNode.cpp new file mode 100644 index 00000000..9c94d9f6 --- /dev/null +++ b/gb.xml/src/CTextNode.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CTextNode.h" +#include "textnode.h" + +#include "node.h" + +#define THISNODE (static_cast(_object)->node) + +BEGIN_METHOD(CTextNode_new, GB_STRING content) + + +if(XMLNode_NoInstanciate()) return; +if(GB.Is(_object, GB.FindClass("XmlCommentNode")))//Called as inherited Comment constructor +{ + if(!MISSING(content)) + { + THISNODE = XMLComment_New(STRING(content), LENGTH(content)); + } + else + { + THISNODE = XMLComment_New(); + } +} +else if(GB.Is(_object, GB.FindClass("XmlCDATANode")))//Called as inherited CDATA constructor +{ + if(!MISSING(content)) + { + THISNODE = XMLCDATA_New(STRING(content), LENGTH(content)); + } + else + { + THISNODE = XMLCDATA_New(); + } +} +else +{ + if(!MISSING(content)) + { + THISNODE = XMLTextNode_New(STRING(content), LENGTH(content)); + } + else + { + THISNODE = XMLTextNode_New(); + } +} + + THISNODE->GBObject = static_cast(_object); + +END_METHOD + + +GB_DESC CTextNodeDesc[] = +{ + GB_DECLARE("XmlTextNode", sizeof(CNode)), GB_INHERITS("XmlNode"), + + GB_METHOD("_new", NULL, CTextNode_new, "[(Content)s]"), + + GB_END_DECLARE +}; + +GB_DESC CCommentNodeDesc[] = +{ + GB_DECLARE("XmlCommentNode", sizeof(CNode)), GB_INHERITS("XmlTextNode"), + + GB_END_DECLARE +}; + +GB_DESC CCDATANodeDesc[] = +{ + GB_DECLARE("XmlCDATANode", sizeof(CNode)), GB_INHERITS("XmlTextNode"), + + GB_END_DECLARE +}; + diff --git a/gb.xml/src/CTextNode.h b/gb.xml/src/CTextNode.h new file mode 100644 index 00000000..1bdf2b24 --- /dev/null +++ b/gb.xml/src/CTextNode.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CTEXTNODE_H +#define CTEXTNODE_H + +#include "gbinterface.h" + +extern GB_DESC CTextNodeDesc[]; +extern GB_DESC CCommentNodeDesc[]; +extern GB_DESC CCDATANodeDesc[]; + +#endif // CTEXTNODE_H diff --git a/gb.xml/src/Makefile.am b/gb.xml/src/Makefile.am new file mode 100644 index 00000000..5ab447e7 --- /dev/null +++ b/gb.xml/src/Makefile.am @@ -0,0 +1,18 @@ +COMPONENT = gb.xml +include $(top_srcdir)/component.am + +SUBDIRS = . @XMLXSLT_DIR@ rpc @XMLHTML_DIR@ +gblib_LTLIBRARIES = gb.xml.la + +gb_xml_la_LIBADD = @XML_LIB@ +gb_xml_la_LDFLAGS = -module @LD_FLAGS@ @XML_LDFLAGS@ +gb_xml_la_CPPFLAGS = @XML_INC@ +gb_xml_la_CXXFLAGS = $(AM_CXXFLAGS) -fexceptions -fvisibility=default + +gb_xml_la_SOURCES = main.cpp main.h utils.cpp utils.h\ +serializer.h serializer.cpp parser.h parser.cpp\ +document.cpp document.h node.cpp node.h textnode.cpp textnode.h element.cpp element.h \ +CDocument.cpp CDocument.h CNode.cpp CNode.h CElement.h CElement.cpp CTextNode.h CTextNode.cpp \ +CReader.h CReader.cpp reader.cpp reader.h \ +CExplorer.h CExplorer.cpp explorer.cpp explorer.h \ +gb.xml.h gbinterface.h diff --git a/gb.xml/src/document.cpp b/gb.xml/src/document.cpp new file mode 100644 index 00000000..fd458af8 --- /dev/null +++ b/gb.xml/src/document.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "document.h" + +#include "node.h" +#include "element.h" +#include "utils.h" +#include "CDocument.h" +#include "parser.h" +#include "serializer.h" + +#include +#include + +Document* XMLDocument_New() +{ + Document *newDoc = (Document*)malloc(sizeof(Document)); + + XMLNode_Init((Node*)newDoc, Node::DocumentNode); + + newDoc->root = XMLElement_New("xml", 3); + newDoc->root->parentDocument = newDoc; + newDoc->parentDocument = newDoc; + newDoc->docType = XMLDocumentType; + XMLNode_appendChild((Node*)newDoc, newDoc->root); + + return newDoc; + +} + +Document* XMLDocument_NewFromFile(const char *fileName, const size_t lenFileName, const DocumentType docType) +{ + Document *newDoc = (Document*)malloc(sizeof(Document)); + + XMLNode_Init((Node*)newDoc, Node::DocumentNode); + + newDoc->root = 0; + newDoc->parentDocument = newDoc; + newDoc->docType = docType; + + try + { + XMLDocument_Open(newDoc, fileName, lenFileName); + } + catch(XMLParseException ex) + { + XMLDocument_Release(newDoc); + throw ex; + } + + return newDoc; +} + +void XMLDocument_Release(Document *doc) +{ + XMLNode_clearChildren((Node*)doc); + free(doc); +} + +/***** Node tree *****/ +void XMLDocument_SetRoot(Document *doc, Element *newRoot) +{ + if(!doc->root) + { + XMLNode_appendChild(doc, newRoot); + } + else + { + XMLNode_replaceChild(doc, doc->root, newRoot); + } + doc->root = newRoot; +} + +/***** Document loading *****/ +void XMLDocument_Open(Document *doc, const char *fileName, const size_t lenFileName) +{ + char *content; int len; + + if(GB.LoadFile(fileName, lenFileName, &content, &len)) + { + GB.Error("Error loading file."); + GB.Propagate(); + return; + } + + + XMLDocument_SetContent(doc, content, len); + +} + +void XMLDocument_SetContent(Document *doc, const char *content, const size_t len) +{ + const char *posStart = 0, *posEnd = 0; + + if(doc->docType == XMLDocumentType) + { + //On cherche le début du prologue XML + posStart = (const char*)memchrs(content, len, "", 2); + posEnd += 2; + } + } + else + { + //On cherche le début du prologue XML + posStart = strcasestr(content, "', len - (posStart - content)); + + if(posEnd) + { + posEnd += 1; + //HTML5 ? () + doc->docType = (posEnd - posStart == 98) ? XHTMLDocumentType : HTMLDocumentType; + if(doc->docType == HTMLDocumentType) doc->docType = (!memcmp(posStart, "html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"", 98)) ? XHTMLDocumentType : HTMLDocumentType; + } + + } + + } + + Node** elements = 0; + size_t elementCount = 0; + + if(posEnd) + { + elements = parse(posEnd, len - (posEnd - content), &elementCount, doc->docType); + } + else + { + elements = parse(content, len, &elementCount, doc->docType); + } + + Node *newRoot = 0; + Node *node = 0; + + XMLNode_clearChildren((Node*)doc); + doc->root = 0; + for(size_t i = 0; i < elementCount; i++) + { + node = elements[i]; + if(node->type == Node::ElementNode) + { + if(!newRoot) + { + newRoot = node; + } + else + { + if(doc->docType == XMLDocumentType)//Strict document + { + throw XMLParseException_New("Extra root element", 0, 0, 0); + } + } + + } + XMLNode_appendChild((Node*)doc, node); + + } + + + free(elements); + if(newRoot) doc->root = (Element*)newRoot; +} + + + +void XMLDocument_Save(Document *doc, const char *fileName, bool indent) +{ + FILE *newFile = fopen(fileName, "w"); + + if(!newFile) + { + GB.Error("Cannot open file"); + GB.Propagate(); + return; + } + + char *data = 0; + size_t lenData = 0; + serializeNode(doc, data, lenData, indent ? 0 : -1); + data = (char*)realloc(data, lenData + 1); + data[lenData] = 0; + + fputs(data, newFile); + fclose(newFile); + free(data); + +} diff --git a/gb.xml/src/document.h b/gb.xml/src/document.h new file mode 100644 index 00000000..a81e03b9 --- /dev/null +++ b/gb.xml/src/document.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef DOCUMENT_H +#define DOCUMENT_H + +#include "main.h" +#include "utils.h" + +Document* XMLDocument_New(); +Document* XMLDocument_NewFromFile(const char *fileName, const size_t lenFileName, const DocumentType docType = XMLDocumentType); +void XMLDocument_Release(Document *doc); + +void XMLDocument_Open(Document *doc, const char *fileName, const size_t lenFileName); +void XMLDocument_SetContent(Document *doc, const char *content, const size_t len); +void XMLDocument_Save(Document *doc, const char *fileName, bool indent = false); + +void XMLDocument_SetRoot(Document *doc, Element *newRoot); + +#endif // DOCUMENT_H diff --git a/gb.xml/src/element.cpp b/gb.xml/src/element.cpp new file mode 100644 index 00000000..51b119cd --- /dev/null +++ b/gb.xml/src/element.cpp @@ -0,0 +1,335 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "element.h" + +#include "node.h" +#include "utils.h" +#include "textnode.h" + +#include +#include + +/*************************************** Element ***************************************/ + +Element* XMLElement_New() +{ + Element *newElement = (Element*)malloc(sizeof(Element)); + memset(newElement, 0, sizeof(Element)); + XMLNode_Init(newElement, Node::ElementNode); + return newElement; +} + +Element* XMLElement_New(const char *ntagName, size_t nlenTagName) +{ + Element *newElement = XMLElement_New(); + XMLElement_SetTagName(newElement, ntagName, nlenTagName); + return newElement; +} + +void XMLElement_Free(Element *elmt) +{ + //Releasing tag name + if(elmt->tagName) free(elmt->tagName); + free(elmt->prefix); + free(elmt->localName); + + //Releasing children + XMLNode_clearChildren(elmt); + + //Releasing attributes + if(elmt->firstAttribute) + { + for(Attribute *attr = (Attribute*)(elmt->firstAttribute->nextNode); attr != 0; attr = (Attribute*)(attr->nextNode)) + { + XMLAttribute_Free((Attribute*)(attr->previousNode)); + } + XMLAttribute_Free(elmt->lastAttribute); + } + free(elmt); +} + + + +/***** TagName *****/ + +void XMLElement_SetTagName(Element *elmt, const char *ntagName, size_t nlenTagName) +{ + elmt->lenTagName = nlenTagName; + elmt->tagName = (char*)realloc(elmt->tagName, sizeof(char) * elmt->lenTagName); + memcpy(elmt->tagName, ntagName, nlenTagName); + XMLElement_RefreshPrefix(elmt); + +} + +void XMLElement_SetPrefix(Element *elmt, const char *nprefix, size_t nlenPrefix) +{ + if(nlenPrefix) + { + elmt->tagName = (char*)realloc(elmt->tagName, nlenPrefix + elmt->lenLocalName + 1); + memcpy(elmt->tagName, nprefix, nlenPrefix); + *(elmt->tagName + nlenPrefix) = ':'; + memcpy(elmt->tagName + nlenPrefix + 1, elmt->localName, elmt->lenLocalName); + } + else if(elmt->lenPrefix) + { + elmt->tagName = (char*)realloc(elmt->tagName, elmt->lenLocalName); + memcpy(elmt->tagName, elmt->localName, elmt->lenLocalName); + } + + + elmt->lenPrefix = nlenPrefix; + elmt->prefix = (char*)realloc(elmt->prefix, nlenPrefix); + if(nlenPrefix) memcpy(elmt->prefix, nprefix, nlenPrefix); +} + +void XMLElement_RefreshPrefix(Element *elmt) +{ + if(!elmt->lenTagName) + { + free(elmt->localName); + elmt->localName = 0; + elmt->lenLocalName = 0; + free(elmt->prefix); + elmt->prefix = 0; + elmt->lenPrefix = 0; + return; + } + char* pos = (char*)memrchr(elmt->tagName, ':', elmt->lenTagName);//Prefix + if(pos) + { + elmt->lenLocalName = (elmt->tagName + elmt->lenTagName) - (pos + 1); + elmt->lenPrefix = pos - elmt->tagName; + elmt->localName = (char*)realloc(elmt->localName, elmt->lenLocalName); + elmt->prefix = (char*)realloc(elmt->prefix, elmt->lenPrefix); + memcpy(elmt->prefix, elmt->tagName, elmt->lenPrefix); + memcpy(elmt->localName, pos + 1, elmt->lenLocalName); + } + else + { + elmt->lenLocalName = elmt->lenTagName; + elmt->localName = (char*)realloc(elmt->localName, sizeof(char) * elmt->lenTagName); + memcpy(elmt->localName, elmt->tagName, elmt->lenTagName); + free(elmt->prefix); + elmt->prefix = 0; + elmt->lenPrefix = 0; + } +} + +/***** Attributes *****/ + +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName) +{ + elmt->attributeCount++; + Attribute *newAttribute = XMLAttribute_New(nattrName, nlenAttrName); + newAttribute->parent = elmt; + if(!elmt->lastAttribute)//No attribute + { + elmt->firstAttribute = newAttribute; + elmt->lastAttribute = elmt->firstAttribute; + elmt->lastAttribute->previousNode = 0; + elmt->lastAttribute->nextNode = 0; + return newAttribute; + } + + newAttribute->previousNode = elmt->lastAttribute; + elmt->lastAttribute->nextNode = newAttribute; + elmt->lastAttribute = newAttribute; + elmt->lastAttribute->nextNode = 0; + return newAttribute; +} + +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal) +{ + elmt->attributeCount++; + Attribute *newAttribute = XMLAttribute_New(nattrName, nlenAttrName, + nattrVal, nlenAttrVal); + newAttribute->parent = elmt; + if(!elmt->lastAttribute)//No attribute + { + elmt->firstAttribute = newAttribute; + elmt->lastAttribute = elmt->firstAttribute; + elmt->lastAttribute->previousNode = 0; + elmt->lastAttribute->nextNode = 0; + return newAttribute; + } + + newAttribute->previousNode = elmt->lastAttribute; + elmt->lastAttribute->nextNode = newAttribute; + elmt->lastAttribute = newAttribute; + elmt->lastAttribute->nextNode = 0; + return newAttribute; +} + +Attribute* XMLElement_GetAttribute(const Element *elmt, const char *nattrName, const size_t nlenAttrName, const int mode) +{ + for(Attribute *attr = elmt->firstAttribute; attr != 0; attr = (Attribute*)(attr->nextNode)) + { + if(GB_MatchString(attr->attrName, attr->lenAttrName, nattrName, nlenAttrName, mode)) + return attr; + } + return 0; +} + +void XMLElement_SetAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal) +{ + Attribute *attr = XMLElement_GetAttribute(elmt, nattrName, nlenAttrName); + if(!attr) + { + XMLElement_AddAttribute(elmt, nattrName, nlenAttrName, nattrVal, nlenAttrVal); + } + else + { + XMLAttribute_SetValue(attr, nattrVal, nlenAttrVal); + } +} + + + +bool XMLElement_AttributeContains(const Element *elmt, const char *attrName, size_t lenAttrName, const char *value, size_t lenValue) +{ + Attribute *attr = XMLElement_GetAttribute(elmt, attrName, lenAttrName); + if(!attr) return false; + char *pos = attr->attrValue; + size_t left = attr->lenAttrValue; + + while (1) { + if (!memcmp(value, pos, lenValue)) return true; + pos = (char*)memchr(pos, ' ', left); + if (!pos) break; + pos++; + left = attr->lenAttrValue - (pos - attr->attrValue); + } + + return false; +} + +void XMLElement_RemoveAttribute(Element *elmt, const char *attrName, size_t lenAttrName) +{ + XMLElement_RemoveAttribute(elmt, XMLElement_GetAttribute(elmt, attrName,lenAttrName)); +} + +void XMLElement_RemoveAttribute(Element *elmt, Attribute *attr) +{ + if(!attr) return; + if(attr->parent != elmt) return; + if(attr == elmt->firstAttribute) elmt->firstAttribute = (Attribute*)(attr->nextNode); + if(attr == elmt->lastAttribute) elmt->lastAttribute = (Attribute*)(attr->previousNode); + if(attr->nextNode) attr->nextNode->previousNode = attr->previousNode; + if(attr->previousNode) attr->previousNode->nextNode = attr->nextNode; + elmt->attributeCount--; + XMLAttribute_Free(attr); +} + +void XMLElement_SetTextContent(Element *elmt, const char *content, size_t lenContent) +{ + if(!lenContent) return; + + XMLNode_clearChildren(elmt); + + TextNode *newChild = XMLTextNode_New(content, lenContent); + + XMLNode_appendChild(elmt, newChild); + +} + +/*************************************** Attribute ***************************************/ + +Attribute* XMLAttribute_New() +{ + Attribute *newAttr = (Attribute*)malloc(sizeof(Attribute)); + XMLNode_Init(newAttr, Node::AttributeNode); + newAttr->attrName = 0; + newAttr->attrValue = 0; + newAttr->lenAttrName = 0; + newAttr->lenAttrValue = 0; + return newAttr; +} + +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName) +{ + Attribute *newAttr = (Attribute*)malloc(sizeof(Attribute)); + XMLNode_Init(newAttr, Node::AttributeNode); + newAttr->attrValue = 0; + newAttr->lenAttrValue = 0; + + newAttr->lenAttrName = nlenAttrName; + newAttr->attrName = (char*)malloc(sizeof(char)*nlenAttrName); + memcpy(newAttr->attrName, nattrName, nlenAttrName); + return newAttr; +} + +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal) +{ + Attribute *newAttr = (Attribute*)malloc(sizeof(Attribute)); + XMLNode_Init(newAttr, Node::AttributeNode); + + newAttr->lenAttrName = nlenAttrName; + newAttr->lenAttrValue = nlenAttrVal; + + newAttr->attrName = (char*)malloc(sizeof(char)*(nlenAttrName)); + memcpy(newAttr->attrName, nattrName, nlenAttrName); + + if(nattrVal && nlenAttrVal) + { + newAttr->attrValue = (char*)malloc(nlenAttrVal); + memcpy(newAttr->attrValue, nattrVal, nlenAttrVal); + } + else + { + newAttr->attrValue = 0; + newAttr->lenAttrValue = 0; + } + + return newAttr; +} + +void XMLAttribute_Free(Attribute *attr) +{ + if(attr->attrName) free(attr->attrName); + if(attr->attrValue) free(attr->attrValue); + + free(attr); + attr = 0; +} + +void XMLAttribute_SetName(Attribute *attr, const char *nattrName, const size_t nlenAttrName) +{ + attr->lenAttrName = nlenAttrName; + attr->attrName = (char*)realloc(attr->attrName, sizeof(char) * attr->lenAttrName); + memcpy(attr->attrName, nattrName, attr->lenAttrName); +} + +void XMLAttribute_SetValue(Attribute *attr, const char *nattrVal, const size_t nlenAttrVal) +{ + attr->lenAttrValue = nlenAttrVal; + if((!nlenAttrVal) && attr->attrValue) + { + free(attr->attrValue); + attr->attrValue = 0; + return; + } + attr->attrValue = (char*)realloc(attr->attrValue, sizeof(char) * attr->lenAttrValue); + memcpy(attr->attrValue, nattrVal, attr->lenAttrValue); +} diff --git a/gb.xml/src/element.h b/gb.xml/src/element.h new file mode 100644 index 00000000..f017158c --- /dev/null +++ b/gb.xml/src/element.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef ELEMENT_H +#define ELEMENT_H + +#include "gb.xml.h" + + +Attribute* XMLAttribute_New(); +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName); +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + +void XMLAttribute_Free(Attribute *attr); + +void XMLAttribute_SetName(Attribute *attr, const char *nattrName, const size_t nlenAttrName); +void XMLAttribute_SetValue(Attribute *attr, const char *nattrVal, const size_t nlenAttrVal); + +Element* XMLElement_New(); +Element* XMLElement_New(const char *ntagName, size_t nlenTagName); +void XMLElement_Free(Element *elmt); + +void XMLElement_SetTagName(Element *elmt, const char *ntagName, size_t nlenTagName); +void XMLElement_SetPrefix(Element *elmt, const char *nprefix, size_t nlenPrefix); +void XMLElement_RefreshPrefix(Element *elmt); + +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName); +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + +Attribute* XMLElement_GetAttribute(const Element *elmt, const char *nattrName, const size_t nlenAttrName, const int mode = GB_STRCOMP_BINARY); + +void XMLElement_SetAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + +bool XMLElement_AttributeContains(const Element *elmt, const char *attrName, size_t lenAttrName, const char *value, size_t lenValue); + +void XMLElement_RemoveAttribute(Element *elmt, const char *attrName, size_t lenAttrName); +void XMLElement_RemoveAttribute(Element *elmt, Attribute *attr); + +void XMLElement_SetTextContent(Element *elmt, const char *content, size_t lenContent); + + +#endif // ELEMENT_H diff --git a/gb.xml/src/explorer.cpp b/gb.xml/src/explorer.cpp new file mode 100644 index 00000000..5774b64a --- /dev/null +++ b/gb.xml/src/explorer.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "explorer.h" + +#include "node.h" +#include "document.h" +#include "element.h" +#include "gbinterface.h" +#include + + + +Explorer::Explorer() : loadedDocument(0) +{ + Init(); +} + +Explorer::~Explorer() +{ + Clear(); + delete[] flags; +} + +void Explorer::Init() +{ + this->flags = new bool[FLAGS_COUNT]; + memset(this->flags, false, FLAGS_COUNT); + this->flags[NODE_ELEMENT] = true; + this->flags[NODE_TEXT] = true; + this->flags[NODE_COMMENT] = true; + this->flags[NODE_CDATA] = true; + this->flags[READ_END_CUR_ELEMENT] = true; + this->flags[READ_ERR_EOF] = true; + Clear(); +} + +void Explorer::Load(Document *doc) +{ + Clear(); + loadedDocument = doc; + CNode *obj = XMLNode_GetGBObject(loadedDocument); + GB.Ref(obj); + //GB.Ref(doc); + + Read(); + +} + +void Explorer::Clear() +{ + if(loadedDocument) + { + CNode *obj = XMLNode_GetGBObject(loadedDocument); + GB.Unref(POINTER(&obj)); + } + loadedDocument = 0; + curNode = 0; + this->eof = false; + this->endElement = false; +} + +int Explorer::MoveNext() +{ + if(eof) return READ_ERR_EOF; + if(!loadedDocument) + { + GB.Error("No document loaded"); + GB.Propagate(); + return READ_ERR_EOF; + } + if(!curNode)//Début du document + { + curNode = (Node*)(loadedDocument->root); + return NODE_ELEMENT; + } + //Premier enfant + else if(curNode->type == Node::ElementNode && ((Element*)curNode)->childCount > 0 && !endElement) + { + curNode = (((Element*)curNode)->firstChild); + return curNode->type; + } + //Si plus d'enfants, frère suivant + else + { + Node *nextNode = curNode->nextNode; + endElement = false; + if(nextNode) + { + curNode = nextNode; + return nextNode->type; + } + //si plus d'enfants ni de frère, on remonte + else if(curNode->parent && curNode != loadedDocument->root && curNode->parent != loadedDocument) + { + curNode = curNode->parent; + endElement = true; + return READ_END_CUR_ELEMENT; + } + else//Plus de parent = fin du document + { + this->eof = true; + return READ_ERR_EOF; + } + + } + + return 0;//Hautement probablement impossible + +} + +unsigned char Explorer::Read() +{ + do + { + state = MoveNext(); + } while(!this->flags[state]); + + return state; +} diff --git a/gb.xml/src/explorer.h b/gb.xml/src/explorer.h new file mode 100644 index 00000000..5adc1c65 --- /dev/null +++ b/gb.xml/src/explorer.h @@ -0,0 +1,65 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef EXPLORER_H +#define EXPLORER_H + +/* ===== Constantes de retour de Read() ===== */ + +#define NODE_ELEMENT 1//Retourné au début d'un élément +#define NODE_TEXT 2//Fin d'un nœud texte +#define NODE_COMMENT 3//Fin d'un commentaire +#define NODE_CDATA 4//Fin d'un CDATA +#define NODE_ATTRIBUTE 5 +#define READ_END_CUR_ELEMENT 6//Fin d'un élément +#define READ_ERR_EOF 7//Fin du document +#define READ_ATTRIBUTE 8//Lecture d'un attribut +#define FLAGS_COUNT 9 + +class Document; +class Node; + + +/* Explorer : explore un document déjà chargé en mémoire.*/ +class Explorer +{ +public: + Explorer(); + ~Explorer(); + + void Load(Document *doc); + + + void Init();//Intitialise le lecteur + void Clear();//Réinitialise le lecteur + int MoveNext();//Va au nœud suivant + unsigned char Read();//Continue la lecture du document + bool *flags;//Flags de lecture + bool endElement;//Si on vient de finir la lecture de l'élément courant + bool eof; + Document *loadedDocument;//Document actuellement chargé + Node *curNode; + + unsigned char state; +}; + + +#endif // EXPLORER_H diff --git a/gb.xml/src/gb.xml.component b/gb.xml/src/gb.xml.component new file mode 100755 index 00000000..eb2afb8b --- /dev/null +++ b/gb.xml/src/gb.xml.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.xml +Author=Adrien Prokopowicz +State=Stable +Implements=XML \ No newline at end of file diff --git a/gb.xml/src/gb.xml.h b/gb.xml/src/gb.xml.h new file mode 100644 index 00000000..8f8566af --- /dev/null +++ b/gb.xml/src/gb.xml.h @@ -0,0 +1,204 @@ +#ifndef GB_XML_H +#define GB_XML_H + +#include + +#define GB_STRCOMP_BINARY 0 +#define GB_STRCOMP_NOCASE 1 +#define GB_STRCOMP_LANG 2 +#define GB_STRCOMP_LIKE 4 +#define GB_STRCOMP_NATURAL 8 + +struct Document; +struct CNode; + +typedef void* GB_ARRAY; + +#if 0 +#include + +using namespace std; + +#define DEBUG std::cerr << "XMLDBG (" << __FILE__ << ":" <<__LINE__ << ") :" +#define DEBUGH DEBUG << endl + +#endif + +typedef struct Node +{ + enum Type {ElementNode, NodeText, Comment, CDATA, AttributeNode, DocumentNode, HTMLDocumentNode}; + + //Node children + Node *firstChild; + Node *lastChild; + size_t childCount; + + //Node tree + Document *parentDocument; + Node *parent; + Node *nextNode; + Node *previousNode; + + //Node Type + Type type; + + //Gambas object + CNode *GBObject; + + //User data + void *userData; //GB_COLLECTION = void* (cf. gambas.h) +}Node; + +typedef struct Attribute : public Node +{ + char *attrName; + char *attrValue; + + size_t lenAttrName; + size_t lenAttrValue; + +}Attribute; + +typedef struct Element : public Node +{ + //Tag Name + char *tagName; + size_t lenTagName; + + char *prefix; + size_t lenPrefix; + + char* localName; + size_t lenLocalName; + + Attribute *firstAttribute; + Attribute *lastAttribute; + size_t attributeCount; + +}Element; + +typedef enum {XMLDocumentType, HTMLDocumentType, XHTMLDocumentType} DocumentType ; + +typedef struct Document : public Node +{ + Element *root; + DocumentType docType; +}Document; + +typedef struct TextNode : public Node +{ + char *content; + size_t lenContent; + + char *escapedContent; + size_t lenEscapedContent; + +}TextNode; + +typedef TextNode CommentNode; + +typedef TextNode CDATANode; + +typedef struct XMLParseException +{ + char *near; + size_t lenNear; + + size_t line; + size_t column; + + char *errorWhat; +}XMLParseException; + +//Gambas XML component interface + +#define XML_INTERFACE_VERSION 1 + +typedef struct +{ + void* version; + //Converts the node to its string representation + void (*SerializeXMLNode)(Node *node, char *&output, size_t &len, int indent); + void (*GBSerializeXMLNode)(Node *node, char *&output, size_t &len, int indent); + Node** (*ParseXML)(char const *data, const size_t lendata, size_t *nodeCount); + void (*GBGetXMLTextContent)(Node *node, char *&output, size_t &len); + + //Content escaping/normalization + void (*XMLText_escapeContent)(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + void (*XMLText_unEscapeContent)(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + void (*XMLText_escapeAttributeContent)(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + + //XMLNode Interface + CNode* (*XMLNode_GetGBObject)(Node *node); + Element* (*XMLNode_getFirstChildByAttributeValue)(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, const int mode, const int depth); + void (*XMLNode_getGBChildrenByAttributeValue)(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode, const int depth); + + Element* (*XMLNode_firstChildElement)(Node *node); + Element* (*XMLNode_lastChildElement)(Node *node); + Element* (*XMLNode_previousElement)(const Node *node); + void (*XMLNode_appendChild)(Node *node, Node *newChild); + Element** (*XMLNode_getChildrenByTagName)(Node *node, const char *ctagName, const size_t clenTagName, size_t &lenArray, const int depth); + Element* (*XMLNode_getFirstChildByTagName)(const Node *node, const char *ctagName, const size_t clenTagName, const int depth); + + void (*XMLNode_setTextContent)(Node *node, const char *content, const size_t lenContent); + + + //XMLTextNode Interface + TextNode* (*XMLTextNode_New)(const char *ncontent, const size_t nlen); + void (*XMLTextNode_setEscapedTextContent)(TextNode *node, const char *ncontent, const size_t nlen); + + void (*XMLTextNode_checkEscapedContent)(TextNode *node); + + //XMLDocument Interface + Document* (*XMLDocument_New)(); + Document* (*XMLDocument_NewFromFile)(const char *fileName, const size_t lenFileName, const DocumentType docType); + void (*XMLDocument_SetContent)(Document *doc, const char *content, size_t len); + + //XMLElement Interface + Element* (*XMLElement_New)(const char *ntagName, size_t nlenTagName); + void (*XMLElement_SetTagName)(Element *elmt, const char *ntagName, size_t nlenTagName); + bool (*XMLElement_AttributeContains)(const Element *elmt, const char *attrName, size_t lenAttrName, const char *value, size_t lenValue); + + Attribute* (*XMLElement_AddAttribute)(Element *elmt, const char *ntagName, size_t nlenTagName, + const char *nattrVal, const size_t nlenAttrVal); + Attribute* (*XMLElement_GetAttribute)(const Element *elmt, const char *nattrName, const size_t nlenAttrName, const int mode); + void (*XMLElement_SetAttribute)(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + + void (*XMLElement_RemoveAttribute)(Element *elmt, Attribute *attr); + //XMLComment Interface + CommentNode* (*XMLComment_New)(const char *ncontent, const size_t nlen); + + //XMLCDATA Interface + CDATANode* (*XMLCDATA_New)(const char *ncontent, const size_t nlen); + + + + void (*ReturnNode)(Node *node); + + + + //Various utils + void (*Trim)(const char* &str, size_t &len); + bool (*isNameStartChar)(const wchar_t car); + bool (*isNameChar)(const wchar_t car); + const void* (*memchrs)(const char *source, size_t lensource, const char *comp, size_t lencomp); + bool (*GB_MatchString)(const char *str, size_t lenStr, const char *pattern, size_t lenPattern, int mode); + wchar_t (*nextUTF8Char)(const char *&data, size_t len); + bool (*isWhiteSpace)(const wchar_t s); + + void (*ThrowXMLParseException)(const char* nerror, const char *text, const size_t lenText, const char *posFailed); + +#if defined(OS_MACOSX) || defined(__APPLE__) + void* (*memrchr)(const char *s, int c, size_t n); +#endif + + void *_null; + +}XML_INTERFACE; + + +#endif // GB_XML_H diff --git a/gb.xml/src/gb.xml/.component b/gb.xml/src/gb.xml/.component new file mode 100644 index 00000000..b4845468 --- /dev/null +++ b/gb.xml/src/gb.xml/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.xml +Version=3.16.90 +State=1 diff --git a/gb.xml/src/gb.xml/.directory b/gb.xml/src/gb.xml/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.xml/src/gb.xml/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.xml/src/gb.xml/.icon.png b/gb.xml/src/gb.xml/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eedc7d8863854cb8646b25127e14db92ddf35ed5 GIT binary patch literal 10933 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

    0 And Me.ReadFlags[$state]) Or $posBuffer = $lenBuffer + + If $posBuffer = $lenBuffer Then Return XmlReaderNodeType.EndStream + Return $state + + Endif + + If Not $stream Then Return 0 + If Eof($stream) Then Return XmlReaderNodeType.EndStream + + Do + sBuf = Read #$stream, 1 + $state = Me._ReadChar(sBuf) + Loop Until ($state <> 0 And Me.ReadFlags[$state]) Or Eof($stream) + + If Eof($stream) And $state = 0 Then Return XmlReaderNodeType.EndStream + Return $state + +End + +Public Sub Open(path As String) + + Dim fichier As File + If Left(Path) <> "/" Then Path = ".." &/ Path + fichier = Open path For Input + Me.InputStream = fichier + +End + +Public Sub Close() + + If $stream Then + Close #$stream + Endif + + $posBuffer = 0 + Me._Close() + +End + + + +Private Function Eof_Read() As Boolean + + If $DataBuffer Then Return $posBuffer = $lenBuffer + If Not $stream Then Return False + Return Eof($stream) + +End + +Private Function State_Read() As Integer + + Return $state + +End diff --git a/gb.xml/src/gb.xml/.src/XmlWriter.class b/gb.xml/src/gb.xml/.src/XmlWriter.class new file mode 100644 index 00000000..3643a6ed --- /dev/null +++ b/gb.xml/src/gb.xml/.src/XmlWriter.class @@ -0,0 +1,241 @@ +' Gambas class file + +Export + +Property Read Data As String +Property Read DTD As _XmlWriterDTD +Property OutputStream As Stream + +Private $stream As Stream ''The stream the XmlWriter is currently writing into +Private $indent As Boolean ''If the XmlWriter must indent its output +Private $sData As String ''The data of the recently closed stream buffer + +'Internal state +Private $lastWasBlock As Boolean = False '' If the last instruction was a block, then the current must write a line break before +Private $bTagOpen As Boolean = False ''If a tag is currently open +Private $bStringStream As Boolean ''If we are working on our own internal string stream +Private $aElementsPile As New String[] ''The pile of all the currently open elements + +Public Sub Open(Optional fileName As String, Optional Indent As Boolean = $indent, Optional Encoding As String = "UTF-8") + + If fileName Then + If Left(fileName) <> "/" Then fileName = ".." &/ fileName + $stream = Open fileName For Output Create + Else If Not $stream + $stream = Open String For Write + $bStringStream = True + Endif + + $indent = Indent + + Print #$stream, "" + +End + +Public Sub Flush() + + CheckStream() + Flush #$stream + +End + +Public Sub Element(TagName As String, Optional Value As String, Optional Prefix As String, Optional URI As String) + + PrintIndent() + + If Prefix Then TagName = Prefix & ":" & TagName + Print #$stream, "<"; TagName; + + If URI Then PrintXmlNsAttribute(Prefix, URI) + + If Value Then + Print #$stream, ">"; XmlNode.Serialize(Value); " "; + Else + Print #$stream, " />"; + Endif + +End + +Public Sub StartElement(TagName As String, Optional Attributes As String[], Optional Prefix As String, Optional URI As String) + + Dim i As Integer = 0 + + PrintIndent() + + If Prefix Then TagName = Prefix & ":" & TagName + + Print #$stream, "<" & TagName; + + If URI Then PrintXmlNsAttribute(Prefix, URI) + + If Attributes + If (Attributes.Count Mod 2) Then Attributes.Push("") + For i = 0 To Attributes.Max Step 2 + Print #$stream, " "; Attributes[i]; "=\""; Attributes[i + 1]; "\""; + Next + Endif + + $bTagOpen = True + $lastWasBlock = True + $aElementsPile.Push(TagName) + +End + +Public Sub EndElement() + + Dim tag As String + + If Not $aElementsPile.Count Then Return + + tag = $aElementsPile.Pop() + + If $bTagOpen Then 'On ferme le tag précédent + Print #$stream, " />"; + $bTagOpen = False + Else + If $lastWasBlock Then PrintIndent() + Print #$stream, ""; + Endif + + $lastWasBlock = True + +End + +Public Sub Attribute(Name As String, Value As String, Optional Prefix As String, Optional URI As String) + + Dim sData As String + If Not $bTagOpen Then Error.Raise("Writing attribute with no open tag") + If Prefix Then + Name = Prefix & ":" & Name + Endif + + sData = " " & Name & "=\"" & XmlElement.NormalizeAttributeContent(Value) & "\"" + + If URI Then PrintXmlNsAttribute(Prefix, URI) + + Print #$stream, sData; + +End + +Public Sub Text(sText As String) + + CloseTags() + Print #$stream, XmlNode.Serialize(sText); + + $lastWasBlock = False + +End + +Public Sub Comment(sComment As String) + + PrintIndent() + Print #$stream, ""; + + $lastWasBlock = True + +End + +Public Sub CDATA(data As String) + + PrintIndent() + Print #$stream, ""; + + $lastWasBlock = True +End + +Public Sub PI(Target As String, Content As String) + + PrintIndent() + Print #$stream, ""; + + $lastWasBlock = True +End + +Public Function EndDocument() As String + + CheckStream() + + While $aElementsPile.Count + Me.EndElement() + Wend + + Try Flush #$stream + + If $bStringStream Then + $sData = Null + Try $sData = Close $stream + Return $sData + Endif + +End + +Public Sub Close() As String + + EndDocument() + If Not $bStringStream Then Close $stream + Return $sData + +End + +'Utils + +Private Sub CheckStream() + + If Not $stream Then Error.Raise("No output stream") + +End + +Private Sub CloseTags() + + CheckStream() + If $bTagOpen Then + Print #$stream, ">"; + $bTagOpen = False + Endif + +End + +Private Sub PrintIndent() + + CloseTags() + If $indent Then + If $lastWasBlock Then Print #$stream + Print #$stream, String$($aElementsPile.Count, " "); + Endif + +End + +Private Sub PrintXmlNsAttribute(Prefix As String, URI As String) + + Print #$stream, " xmlns"; If(Prefix, ":" & Prefix, ""); "=\""; URI; "\""; + +End + +'Properties + +Private Function Data_Read() As String + + Return $sData + +End + +Private Function OutputStream_Read() As Stream + + Return $stream + +End + +Private Sub OutputStream_Write(Value As Stream) + + $bStringStream = $bStringStream And $stream = Value + $stream = Value + +End + +Private Function DTD_Read() As _XmlWriterDTD + + _XmlWriterDTD._$writer = Me + Return _XmlWriterDTD + +End + diff --git a/gb.xml/src/gb.xml/.src/_XmlWriterDTD.class b/gb.xml/src/gb.xml/.src/_XmlWriterDTD.class new file mode 100644 index 00000000..ff1bc3c5 --- /dev/null +++ b/gb.xml/src/gb.xml/.src/_XmlWriterDTD.class @@ -0,0 +1,8 @@ +' Gambas class file + +Export +Create Static + +Public _$writer As XmlWriter + + diff --git a/gb.xml/src/gb.xml/text.xml b/gb.xml/src/gb.xml/text.xml new file mode 100644 index 00000000..df9b1695 --- /dev/null +++ b/gb.xml/src/gb.xml/text.xml @@ -0,0 +1,2 @@ + +HelloBouh diff --git a/gb.xml/src/gbinterface.h b/gb.xml/src/gbinterface.h new file mode 100644 index 00000000..cfec0b15 --- /dev/null +++ b/gb.xml/src/gbinterface.h @@ -0,0 +1,27 @@ +#ifndef GBINTERFACE_H +#define GBINTERFACE_H + +#include "gambas.h" + +extern "C" GB_INTERFACE GB; + +#define VARGOBJ(_type, _ob) ((_type*)VARG(_ob)) +#define VPROPOBJ(_type) ((_type*)VPROP(GB_OBJECT)) +#define STRINGOPT(_str, _repl, _lenrepl) MISSING(_str) ? _repl : STRING(_str),\ + MISSING(_str) ? _lenrepl : LENGTH(_str) + + + +struct Node; +struct Attribute; + +typedef struct CNode +{ + GB_BASE ob; + Node *node; + Attribute *curAttrEnum; +}CNode; + +typedef CNode CDocument; + +#endif // GBINTERFACE_H diff --git a/gb.xml/src/html/CHTMLDocument.cpp b/gb.xml/src/html/CHTMLDocument.cpp new file mode 100644 index 00000000..c4b0d8fb --- /dev/null +++ b/gb.xml/src/html/CHTMLDocument.cpp @@ -0,0 +1,236 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CHTMLDocument.h" +#include "htmldocument.h" +#include "htmlelement.h" + +/*========== Document */ + +#define THIS ((Document*)(static_cast(_object)->node)) + +BEGIN_METHOD_VOID(CDocument_new) + +END_METHOD + +BEGIN_METHOD_VOID(CDocument_free) + + + +END_METHOD + +BEGIN_PROPERTY(CDocument_Html5) + +if(READ_PROPERTY) +{ + GB.ReturnBoolean(THIS->docType == HTMLDocumentType); +} +else +{ + HtmlDocument_SetHTML(THIS, VPROP(GB_BOOLEAN)); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_Title) + +if(READ_PROPERTY) +{ + char *title; size_t lenTitle; + XML.GBGetXMLTextContent(HtmlDocument_GetTitle(THIS), title, lenTitle); + GB.ReturnString(title); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetTitle(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_favicon) + +if(READ_PROPERTY) +{ + char *favicon; size_t lenFavicon; + XML.GBGetXMLTextContent(HtmlDocument_GetFavicon(THIS), favicon, lenFavicon); + GB.ReturnString(favicon); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetFavicon(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_lang) + +if(READ_PROPERTY) +{ + char *lang; size_t lenLang; + XML.GBGetXMLTextContent(HtmlDocument_GetLang(THIS), lang, lenLang); + GB.ReturnString(lang); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetLang(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_base) + +if(READ_PROPERTY) +{ + char *base; size_t lenBase; + XML.GBGetXMLTextContent(HtmlDocument_GetBase(THIS), base, lenBase); + GB.ReturnString(base); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetBase(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_root) + +XML.ReturnNode(THIS->root); + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_head) + +XML.ReturnNode(HtmlDocument_GetHead(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_body) + +XML.ReturnNode(HtmlDocument_GetBody(THIS)); + +END_PROPERTY + +BEGIN_METHOD(CDocument_getElementById, GB_STRING id; GB_INTEGER depth) + +XML.ReturnNode(HtmlDocument_GetElementById(THIS, STRING(id), LENGTH(id), VARGOPT(depth, -1))); + +END_METHOD + +BEGIN_METHOD(CDocument_getElementsByClassName, GB_STRING className; GB_INTEGER depth) + +if(LENGTH(className) <= 0) return; +GB_ARRAY array; +HtmlDocument_GetElementsByClassName(THIS, STRING(className), LENGTH(className), &array, VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CDocumentStyleSheets_add, GB_STRING path; GB_STRING media) + +HtmlDocument_AddStyleSheet(THIS, STRING(path), LENGTH(path), STRINGOPT(media, "screen", 6)); + +END_METHOD + +BEGIN_METHOD(CDocumentStyleSheets_addIfNotIE, GB_STRING path; GB_STRING media) + +HtmlDocument_AddStyleSheetIfNotIE(THIS, STRING(path), LENGTH(path), STRINGOPT(media, "screen", 6)); + +END_METHOD + +BEGIN_METHOD(CDocumentStyleSheets_addIfIE, GB_STRING path; GB_STRING cond; GB_STRING media) + +HtmlDocument_AddStyleSheetIfIE(THIS, STRING(path), LENGTH(path), + STRINGOPT(cond, "IE", 2), STRINGOPT(media, "screen", 6)); + +END_METHOD + +BEGIN_METHOD(CDocumentScripts_add, GB_STRING path) + +HtmlDocument_AddScript(THIS, STRING(path), LENGTH(path)); + +END_METHOD + +BEGIN_METHOD(CDocumentScripts_addIfNotIE, GB_STRING path) + +HtmlDocument_AddScriptIfNotIE(THIS, STRING(path), LENGTH(path)); + +END_METHOD + +BEGIN_METHOD(CDocumentScripts_addIfIE, GB_STRING path; GB_STRING cond) + +HtmlDocument_AddScriptIfIE(THIS, STRING(path), LENGTH(path), STRINGOPT(cond, "IE", 2)); + +END_METHOD + +GB_DESC CDocumentStyleSheetsDesc[] = +{ + GB_DECLARE(".HtmlDocumentStyleSheets", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Add", NULL, CDocumentStyleSheets_add, "(Source)s[(Media)s]"), + GB_METHOD("AddIfIE", NULL, CDocumentStyleSheets_addIfIE, "(Source)s[(Condition)s(Media)s]"), + GB_METHOD("AddIfNotIE", NULL, CDocumentStyleSheets_addIfNotIE, "(Source)s[(Media)s]"), + + GB_END_DECLARE +}; + +GB_DESC CDocumentScriptsDesc[] = +{ + GB_DECLARE(".HtmlDocumentScripts", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Add", NULL, CDocumentScripts_add, "(Source)s"), + GB_METHOD("AddIfIE", NULL, CDocumentScripts_addIfIE, "(Source)s[(Condition)s]"), + GB_METHOD("AddIfNotIE", NULL, CDocumentScripts_addIfNotIE, "(Source)s"), + + GB_END_DECLARE +}; + +GB_DESC CDocumentDesc[] = +{ + GB_DECLARE("HtmlDocument", sizeof(CDocument)), GB_INHERITS("XmlDocument"), + + GB_METHOD("_new", NULL, CDocument_new, ""), + GB_METHOD("_free", NULL, CDocument_free, ""), + + GB_PROPERTY("Html5", "b", CDocument_Html5), + + GB_PROPERTY("Title", "s", CDocument_Title), + GB_PROPERTY("Favicon", "s", CDocument_favicon), + GB_PROPERTY("Lang", "s", CDocument_lang), + GB_PROPERTY("Base", "s", CDocument_base), + GB_PROPERTY_READ("Head", "XmlElement", CDocument_head), + GB_PROPERTY_READ("Body", "XmlElement", CDocument_body), + + //GB_METHOD("FromString", NULL, CDocument_fromString, "(Data)s"), + //GB_METHOD("HtmlFromString", NULL, CDocument_fromString, "(Data)s"), + + GB_PROPERTY_SELF("StyleSheets", ".HtmlDocumentStyleSheets"), + GB_PROPERTY_SELF("Scripts", ".HtmlDocumentScripts"), + + GB_METHOD("GetElementById", "XmlElement", CDocument_getElementById, "(Id)s[(Depth)i]"), + GB_METHOD("GetElementsByClassName", "XmlElement[]", CDocument_getElementsByClassName, "(ClassName)s[(Depth)i]"), + + + GB_END_DECLARE +}; diff --git a/gb.xml/src/html/CHTMLDocument.h b/gb.xml/src/html/CHTMLDocument.h new file mode 100644 index 00000000..956a9e6b --- /dev/null +++ b/gb.xml/src/html/CHTMLDocument.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HCDOCUMENT_H +#define HCDOCUMENT_H + +#include "htmlmain.h" +#include "../gbinterface.h" + +#ifndef CLASSES_CPP +extern GB_DESC CDocumentDesc[]; +extern GB_DESC CDocumentStyleSheetsDesc[]; +extern GB_DESC CDocumentScriptsDesc[]; +#endif + +#endif diff --git a/gb.xml/src/html/CHTMLElement.cpp b/gb.xml/src/html/CHTMLElement.cpp new file mode 100644 index 00000000..c89288a7 --- /dev/null +++ b/gb.xml/src/html/CHTMLElement.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "htmlelement.h" +#include "cssfilter.h" +#include "../gbinterface.h" + +/*========== Element */ + +#define THIS ((Element*)(static_cast(_object)->node)) +#define THISNODE (static_cast(_object)->node) + +BEGIN_PROPERTY(CElement_id) + +if(READ_PROPERTY) +{ + Attribute *id = HTMLElement_GetId(THIS); + if(id) + { + GB.ReturnNewString(id->attrValue, id->lenAttrValue); + } + else + { + GB.ReturnNull(); + } +} +else +{ + HTMLElement_SetId(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CElement_className) + +if(READ_PROPERTY) +{ + Attribute *className = HTMLElement_GetClassName(THIS); + if(className) + { + GB.ReturnNewString(className->attrValue, className->lenAttrValue); + } + else + { + GB.ReturnNull(); + } +} +else +{ + HTMLElement_SetClassName(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_METHOD(CElement_matchFilter, GB_STRING filter) + +GB.ReturnBoolean(HTMLElement_MatchFilter(THIS, STRING(filter), LENGTH(filter))); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByFilter, GB_STRING filter; GB_INTEGER depth) + +GB_ARRAY array; + +HTMLElement_GetGBChildrenByFilter(THIS, STRING(filter), LENGTH(filter), &array, VARGOPT(depth, -1)); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildById, GB_STRING id; GB_INTEGER depth) + +XML.ReturnNode(HTMLElement_GetChildById(THIS, STRING(id), LENGTH(id), VARGOPT(depth, -1))); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByClassName, GB_STRING className; GB_INTEGER depth) + + GB_ARRAY array; + + HTMLElement_GetGBChildrenByClassName(THIS, STRING(className), LENGTH(className), &array, VARGOPT(depth, -1)); + + GB.ReturnObject(array); + +END_METHOD + +GB_DESC CElementDesc[] = +{ + GB_DECLARE("XmlElement", sizeof(CNode)), + + GB_PROPERTY("Id", "s", CElement_id), + GB_PROPERTY("ClassName", "s", CElement_className), + + GB_METHOD("MatchFilter", "b", CElement_matchFilter, "(Filter)s"), + GB_METHOD("GetChildrenByFilter", "XmlElement[]", CElement_getChildrenByFilter, "(Filter)s[(Depth)i]"), + + GB_METHOD("GetChildById", "XmlElement", CElement_getChildById, "(Id)s[(Depth)i]"), + GB_METHOD("GetChildrenByClassName", "XmlElement[]", CElement_getChildrenByClassName, "(ClassName)s[(Depth)i]"), + + + + GB_END_DECLARE +}; diff --git a/gb.xml/src/html/CHTMLElement.h b/gb.xml/src/html/CHTMLElement.h new file mode 100644 index 00000000..1a9eab5c --- /dev/null +++ b/gb.xml/src/html/CHTMLElement.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HCELEMENT_H +#define HCELEMENT_H + +#include "htmlmain.h" +#include "../gbinterface.h" + +#ifndef __HELEMENT_CPP +extern GB_DESC CElementDesc[]; +#endif + +#endif diff --git a/gb.xml/src/html/Makefile.am b/gb.xml/src/html/Makefile.am new file mode 100644 index 00000000..f571657c --- /dev/null +++ b/gb.xml/src/html/Makefile.am @@ -0,0 +1,19 @@ +COMPONENT = gb.xml.html +include $(top_srcdir)/component.am +gblib_LTLIBRARIES = gb.xml.html.la + +##gb_xml_html_la_LIBADD = @XMLHTML_LIB@ @XML_LIB@ -l:gb.xml.so +##gb_xml_html_la_LDFLAGS = -module @LD_FLAGS@ @XMLHTML_LDFLAGS@ -L$(srcdir)/../.libs +gb_xml_html_la_LIBADD = @XMLHTML_LIB@ @XML_LIB@ +gb_xml_html_la_LDFLAGS = -module @LD_FLAGS@ @XMLHTML_LDFLAGS@ +gb_xml_html_la_CPPFLAGS = @XMLHTML_INC@ +gb_xml_html_la_CXXFLAGS = $(AM_CXXFLAGS) -fexceptions + +gb_xml_html_la_SOURCES = *.h \ +htmlmain.cpp \ +htmldocument.cpp htmlelement.cpp \ +CHTMLDocument.cpp CHTMLElement.cpp \ +htmlserializer.cpp \ +cssfilter.cpp \ +htmlparser.cpp \ +gb.xml.html.h diff --git a/gb.xml/src/html/cssfilter.cpp b/gb.xml/src/html/cssfilter.cpp new file mode 100644 index 00000000..35c856f3 --- /dev/null +++ b/gb.xml/src/html/cssfilter.cpp @@ -0,0 +1,195 @@ +#include "cssfilter.h" +#include "htmlmain.h" +#include "htmlelement.h" +#include + +bool HTMLElement_MatchSubFilter(const Element *elmt, const char *filter, size_t lenFilter); + +bool HTMLElement_MatchFilter(const Element *elmt, const char *filter, size_t lenFilter) +{ + if(!lenFilter) return true; + XML.Trim(filter, lenFilter); + char *pos; + + pos = (char*)memrchr(filter, ',', lenFilter); + if(pos) + { + return HTMLElement_MatchFilter(elmt, filter, (pos - filter)) || + HTMLElement_MatchFilter(elmt, pos, lenFilter - (pos + 1 - filter)); + } + + pos = (char*)memrchr(filter, '>', lenFilter); + if(pos) + { + Element *parent = (Element*)(elmt->parent); + if(!parent) return false; + return HTMLElement_MatchFilter(parent, filter, (pos - filter)) && + HTMLElement_MatchFilter(elmt, pos, lenFilter - (pos + 1 - filter)); + } + + pos = (char*)memrchr(filter, '+', lenFilter); + if(pos) + { + Element *previous = XML.XMLNode_previousElement(elmt); + if(!previous) return false; + return HTMLElement_MatchFilter(previous, filter, (pos - filter)) && + HTMLElement_MatchFilter(previous, pos, lenFilter - (pos + 1 - filter)); + } + + pos = (char*)memrchr(filter, ' ', lenFilter); + if(pos) + { + if(!HTMLElement_MatchFilter(elmt, pos, lenFilter - (pos + 1 - filter))) return false; + for(Node *parent = elmt->parent; parent != 0; parent = parent->parent)//TODO: does not support non-element parents + { + if(parent->type == Node::ElementNode) + { + if(HTMLElement_MatchFilter((Element*)parent, filter, (pos - filter))) return true; + } + } + + return false; + } + + return HTMLElement_MatchSubFilter(elmt, filter, lenFilter); +} + +bool HTMLElement_MatchSubFilter(const Element *elmt, const char *filter, size_t lenFilter) +{ + if(!lenFilter) return true; + XML.Trim(filter, lenFilter); + if(!lenFilter) return true; + + char s = 0; + char const *pos = 0; + + for(pos = filter + 1; pos < filter + lenFilter; ++pos) + { + if(!XML.isNameChar(*pos))//Something else that a name + { + break; + } + } + + bool cond = (pos != filter + lenFilter);//If there is something else to check + + s = *filter; + if(s == '*')//Universal selector + { + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == ':')//Pseudo-class + { + size_t lenSubStr = pos - filter; + + if(lenSubStr == 11 && !memcmp(filter, "first-child", 11)) + { + if(!elmt->parent) return false; + if(XML.XMLNode_firstChildElement(elmt->parent) != elmt) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + else if(lenSubStr == 10 && !memcmp(filter, "last-child", 10)) + { + if(!elmt->parent) return false; + if(XML.XMLNode_lastChildElement(elmt->parent) != elmt) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + return false; + } + if(XML.isNameStartChar(s))//Tag Name + { + if(!(elmt->lenTagName + filter == pos)) return false;//lenTagName == pos - filter + if(memcmp(elmt->tagName, filter, elmt->lenTagName)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + else if(s == '#')//ID + { + Attribute *id = HTMLElement_GetId(elmt); + if(!id) return false; + + if(!(id->lenAttrValue + filter + 1 == pos)) return false; + if(memcmp(filter + 1, id->attrValue, pos - (filter + 1))) return false; + if(cond) return (HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter))); + return true; + } + else if(s == '.')//ClassName + { + if(!HTMLElement_HasClassName(elmt, filter + 1, pos - (filter + 1))) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - (filter + 1))); + return true; + } + else if(s == '[')//Attribut + { + //Syntax : [foo="bar"] + char const *endPos = (char*)memchr(filter, ']', lenFilter);//On cherche le crochet fermant + + endPos = endPos ? endPos : filter + lenFilter - 1; + pos = (endPos + 1); + cond = (pos < filter + lenFilter); + + char *equalPos = (char*)memchr(filter, '=', lenFilter);//On cherche le signe égal + + if(equalPos)//Si trouvé + { + s = *(equalPos - 1);//Le signe avant le signe égal + char const *attrName = filter + 1; size_t lenAttrName = (equalPos - filter - 1); + char const *attrValue = equalPos + 2; size_t lenAttrValue = (endPos - equalPos - 3); + if(s == '~')// ~= Comparison + { + if(!XML.XMLElement_AttributeContains(elmt, attrName, lenAttrName - 1, attrValue, lenAttrValue)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == '^')// ^= Comparison + { + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName - 1, 0); + if(!attr) return false; + if(attr->lenAttrValue < lenAttrValue) return false; + if(memcmp(attr->attrValue, attrValue, lenAttrValue)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == '$')// $= Comparison + { + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName - 1, 0); + if(!attr) return false; + if(attr->lenAttrValue < lenAttrValue) return false; + if(memcmp(attr->attrValue + attr->lenAttrValue - lenAttrValue, attrValue, lenAttrValue)) return false; + if(cond) HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == '*')// *= Comparison + { + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName - 1, 0); + if(!attr) return false; + if(attr->lenAttrValue < lenAttrValue) return false; + if(!XML.memchrs(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName, 0); + if(!attr) return false; + if(attr->lenAttrValue != lenAttrValue) return false; + if(memcmp(attr->attrValue, attrValue, lenAttrValue)) return false; + + //Valeur de l'attribut + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + + Attribute *attr = XML.XMLElement_GetAttribute(elmt, filter + 1, endPos - filter - 1, 0); + if(!attr) return false; + //Si l'attribut est défini + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + + } + + return false; + +} diff --git a/gb.xml/src/html/cssfilter.h b/gb.xml/src/html/cssfilter.h new file mode 100644 index 00000000..0afbd29f --- /dev/null +++ b/gb.xml/src/html/cssfilter.h @@ -0,0 +1,8 @@ +#ifndef CSSFILTER_H +#define CSSFILTER_H + +#include "../gb.xml.h" + +bool HTMLElement_MatchFilter(const Element *elmt, const char *filter, size_t lenFilter); + +#endif // CSSFILTER_H diff --git a/gb.xml/src/html/gb.xml.html.component b/gb.xml/src/html/gb.xml.html.component new file mode 100755 index 00000000..c236ecc4 --- /dev/null +++ b/gb.xml/src/html/gb.xml.html.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.xml.html +Author=Adrien Prokopowicz +State=Stable +Require=gb.xml \ No newline at end of file diff --git a/gb.xml/src/html/gb.xml.html.h b/gb.xml/src/html/gb.xml.html.h new file mode 100644 index 00000000..225ff7a6 --- /dev/null +++ b/gb.xml/src/html/gb.xml.html.h @@ -0,0 +1,26 @@ +#ifndef GB_XML_HTML_H +#define GB_XML_HTML_H + +#include "../gb.xml.h" + +#define XML_HTML_INTERFACE_VERSION 1 + +typedef struct +{ + int version; + //Converts the node to its string representation + void (*serializeHTMLNode)(Node *node, char *&output, size_t &len, int indent); + void (*GBserializeHTMLNode)(Node *node, char *&output, size_t &len, int indent); + + //Parser + Node** (*parseHTML)(char const *data, const size_t lendata, size_t *nodeCount); + void (*GBparseHTML)(char const *data, const size_t lendata, GB_ARRAY *array); + + //HtmlDocument Interface + Document* (*HtmlDocument_New)(); + Document* (*HtmlDocument_NewFromFile)(const char *fileName, const size_t lenFileName); + void *_null; + +}XML_HTML_INTERFACE; + +#endif // GB_XML_HTML_H diff --git a/gb.xml/src/html/htmldocument.cpp b/gb.xml/src/html/htmldocument.cpp new file mode 100644 index 00000000..8b70876f --- /dev/null +++ b/gb.xml/src/html/htmldocument.cpp @@ -0,0 +1,317 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + + +#include "htmlelement.h" +#include "htmldocument.h" +#include +#include + +Element* GetElement(Node *parent, const char *tagName, const size_t lenTagName); +Attribute* GetAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName); + +void UpdateMetaCharset(Document *doc, bool html5); + +Document *HtmlDocument_New() +{ + Document *newDoc = XML.XMLDocument_New(); + newDoc->docType = XHTMLDocumentType; + newDoc->root->parentDocument = newDoc; + + XML.XMLElement_SetTagName(newDoc->root, "html", 4); + + Element *head = XML.XMLElement_New("head", 4); + XML.XMLNode_appendChild(newDoc->root, head); + + Element *body = XML.XMLElement_New("body", 4); + XML.XMLNode_appendChild(newDoc->root, body); + + //Meta utf-8 + Element *meta = XML.XMLElement_New("meta", 4); + + XML.XMLElement_AddAttribute(meta, "http-equiv", 10, "Content-Type", 12); + XML.XMLElement_AddAttribute(meta, "content", 7,"text/html; charset=utf-8", 24); + XML.XMLNode_appendChild(head, meta); + + //Title + Element *title = XML.XMLElement_New("title", 5); + XML.XMLNode_appendChild(head, title); + + return newDoc; +} + +Document* HtmlDocument_NewFromFile(const char *fileName, const size_t lenFileName) +{ + return XML.XMLDocument_NewFromFile(fileName, lenFileName, XHTMLDocumentType); +} + +void HtmlDocument_SetHTML(Document *doc, const bool isHtml) +{ + UpdateMetaCharset(doc, isHtml); + if(isHtml) + { + doc->docType = HTMLDocumentType; + } + else + { + doc->docType = XHTMLDocumentType; + } +} + +Element* HtmlDocument_GetHead(Document *doc) +{ + return GetElement(doc->root, "head", 4); +} + +Element* HtmlDocument_GetBody(Document *doc) +{ + return GetElement(doc->root, "body", 4); +} + +Element* HtmlDocument_GetTitle(Document *doc) +{ + return GetElement(HtmlDocument_GetHead(doc), "title", 5); +} + +Attribute* HtmlDocument_GetFavicon(Document *doc) +{ + Element *head = HtmlDocument_GetHead(doc); + Element **elmts; size_t lenElmts; + elmts = XML.XMLNode_getChildrenByTagName(head, "link", 4, lenElmts, 2); + Element *elmt; + + Attribute *attr; + + for(unsigned int i = 0; i < lenElmts; i++) + { + attr = XML.XMLElement_GetAttribute(elmts[i], "rel", 3, 0); + if(attr->lenAttrValue == 4) + { + if(!memcmp(attr->attrValue, "icon", 4)) + { + elmt = elmts[i]; + free(elmts); + return XML.XMLElement_GetAttribute(elmt, "href", 4, 0); + } + } + } + + free(elmts); + elmt = XML.XMLElement_New("link", 4); + XML.XMLElement_AddAttribute(elmt, "rel", 3, "icon", 4); + XML.XMLNode_appendChild(head, elmt); + return GetAttribute(elmt, "href", 4); +} + + +Attribute* HtmlDocument_GetBase(Document *doc) +{ + return GetAttribute(GetElement(HtmlDocument_GetHead(doc), "base", 4), "href", 4); +} + +Attribute* HtmlDocument_GetLang(Document *doc) +{ + return GetAttribute(doc->root, "lang", 4); +} + +void HtmlDocument_AddStyleSheet(Document *doc, const char *src, size_t lenSrc, + const char *media, size_t lenMedia) +{ + Element *elmt = XML.XMLElement_New("link", 4); + XML.XMLElement_AddAttribute(elmt, "rel", 3, "stylesheet", 10); + XML.XMLElement_AddAttribute(elmt, "href", 4, src, lenSrc); + XML.XMLElement_AddAttribute(elmt, "type", 4, "text/css", 8); + XML.XMLElement_AddAttribute(elmt, "media", 5, media, lenMedia); + XML.XMLNode_appendChild(HtmlDocument_GetHead(doc),elmt); +} + +void HtmlDocument_AddStyleSheetIfIE(Document *doc, const char *src, size_t lenSrc, + const char *cond, size_t lenCond, + const char *media, size_t lenMedia) +{ +//[if +cond+]><", 10); + XML.XMLNode_appendChild(head,comment); + HtmlDocument_AddStyleSheet(doc, src, lenSrc, media, lenMedia); + comment = XML.XMLComment_New("><", 10); + XML.XMLNode_appendChild(head,comment); + + HtmlDocument_AddScript(doc, src, lenSrc); + + comment = XML.XMLComment_New(">docType == (html5 ? HTMLDocumentType : XHTMLDocumentType)) return; + + //Looking for the meta charset element + size_t lenMetas; + Element **metas = XML.XMLNode_getChildrenByTagName(HtmlDocument_GetHead(doc), "meta", 4, lenMetas, 2); + Element *meta = 0; + Element *tmeta = 0; + Attribute *attr; + + for(size_t i = 0; i < lenMetas; i++) + { + tmeta = metas[i]; + if(doc->docType == XHTMLDocumentType) + { + attr = XML.XMLElement_GetAttribute(tmeta, "http-equiv", 10, 0); + if(!attr) continue; + if(!XML.GB_MatchString(attr->attrValue, attr->lenAttrValue, "Content-Type", 12, 0)) continue; + XML.XMLElement_RemoveAttribute(tmeta, attr); + attr = XML.XMLElement_GetAttribute(tmeta, "content", 7, 0); + if(!attr) continue; + if(!XML.GB_MatchString(attr->attrValue, attr->lenAttrValue, "text/html; charset=utf-8", 24, 0)) continue; + XML.XMLElement_RemoveAttribute(tmeta, attr); + meta = tmeta; + break; + } + else + { + attr = XML.XMLElement_GetAttribute(metas[i], "charset", 7, 0); + if(!attr) continue; + if(!XML.GB_MatchString(attr->attrValue, attr->lenAttrValue, "utf-8", 5, 0)) continue; + XML.XMLElement_RemoveAttribute(metas[i], attr); + + meta = metas[i]; + break; + } + } + free(metas); + + if(!meta) + { + meta = XML.XMLElement_New("meta", 4); + XML.XMLNode_appendChild(HtmlDocument_GetHead(doc), meta); + } + + if(html5) + { + XML.XMLElement_AddAttribute(meta, "charset", 7, "utf-8", 5); + } + else + { + XML.XMLElement_AddAttribute(meta, "http-equiv", 11, "Content-Type", 12); + XML.XMLElement_AddAttribute(meta, "content", 7, "text/html; charset=utf-8", 25); + } + +} + diff --git a/gb.xml/src/html/htmldocument.h b/gb.xml/src/html/htmldocument.h new file mode 100644 index 00000000..839f12be --- /dev/null +++ b/gb.xml/src/html/htmldocument.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HDOCUMENT_H +#define HDOCUMENT_H + +#include "htmlmain.h" + +Document* HtmlDocument_New(); +Document* HtmlDocument_NewFromFile(const char *fileName, const size_t lenFileName); +Element* HtmlDocument_GetBody(Document *doc); +Element* HtmlDocument_GetHead(Document *doc); + +Element* HtmlDocument_GetElementById(Document *doc, const char *id, const size_t lenId, int depth = -1); +void HtmlDocument_GetElementsByClassName(Document *doc, const char *className, const size_t lenClassName, GB_ARRAY *array, int depth = -1); + +Element* HtmlDocument_GetTitle(Document *doc); +Attribute* HtmlDocument_GetFavicon(Document *doc); +Attribute* HtmlDocument_GetBase(Document *doc); +Attribute* HtmlDocument_GetLang(Document *doc); + +void HtmlDocument_SetHTML(Document *doc, const bool isHtml); + +void HtmlDocument_AddStyleSheet(Document *doc, const char *src, size_t lenSrc, + const char *media, size_t lenMedia); + +void HtmlDocument_AddStyleSheetIfIE(Document *doc, const char *src, size_t lenSrc, + const char *cond, size_t lenCond, + const char *media, size_t lenMedia); + +void HtmlDocument_AddStyleSheetIfNotIE(Document *doc, const char *src, size_t lenSrc, + const char *media, size_t lenMedia); + +void HtmlDocument_AddScript(Document *doc, const char *src, size_t lenSrc); + + +void HtmlDocument_AddScriptIfIE(Document *doc, const char *src, size_t lenSrc, + const char *cond, size_t lenCond); + +void HtmlDocument_AddScriptIfNotIE(Document *doc, const char *src, size_t lenSrc); + + +#endif diff --git a/gb.xml/src/html/htmlelement.cpp b/gb.xml/src/html/htmlelement.cpp new file mode 100644 index 00000000..095f1444 --- /dev/null +++ b/gb.xml/src/html/htmlelement.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "htmlelement.h" +#include "cssfilter.h" +#include "../gbinterface.h" + +void HTMLElement_AddGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth = -1); + +bool HTMLElement_HasClassName(const Element *elmt, const char *className, size_t lenClassName) +{ + return XML.XMLElement_AttributeContains(elmt, "class", 5, className, lenClassName); +} + +Element* HTMLElement_GetChildById(Element *elmt, char *id, size_t lenId, int depth) +{ + return XML.XMLNode_getFirstChildByAttributeValue(elmt, "id", 2, id, lenId, 0, depth); +} + +void HTMLElement_GetGBChildrenByClassName(Element *elmt, char* className, size_t lenClassName, GB_ARRAY *array, int depth) +{ + XML.XMLNode_getGBChildrenByAttributeValue(elmt, "class", 5, className, lenClassName, array, 0, depth); +} + +void HTMLElement_SetId(Element *elmt, const char *value, size_t len) +{ + XML.XMLElement_SetAttribute(elmt, "id", 2, value, len); +} + +void HTMLElement_SetClassName(Element *elmt, const char *value, size_t len) +{ + XML.XMLElement_SetAttribute(elmt, "class", 5, value, len); +} + +Attribute* HTMLElement_GetClassName(const Element *elmt) +{ + return XML.XMLElement_GetAttribute(elmt, "class", 5, 0); +} + +Attribute* HTMLElement_GetId(const Element *elmt) +{ + return XML.XMLElement_GetAttribute(elmt, "id", 2, 0); +} + +void HTMLElement_GetGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + HTMLElement_AddGBChildrenByFilter(elmt, filter, lenFilter, array, depth); +} + +void HTMLElement_AddGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth) +{ + if(depth == 0) return; + for(Node *node = elmt->firstChild; node != 0; node = node->nextNode) + { + if(node->type == Node::ElementNode) + { + if(HTMLElement_MatchFilter((Element*)node, filter, lenFilter)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XML.XMLNode_GetGBObject(node); + GB.Ref(node->GBObject); + } + HTMLElement_AddGBChildrenByFilter((Element*)(node), filter, lenFilter, array, depth - 1); + } + } +} + +const size_t lenSingleElements[] = {2 , 3 ,4 ,5 ,4 ,4 ,2 ,7 ,5 ,2 ,5 ,4 ,2 ,6 ,5 ,3}; +const char* singleElements[] = {"br", "img", "meta", "input", "area", "base", "co", "command", "embed", "hr", "keygen", "link", "param", "source", "track", "wbr"}; +#define COUNT_SINGLEELEMENTS 16 + + +bool HTMLElement_IsSingle(Element *elmt) +{ + for(int i = 0; i < COUNT_SINGLEELEMENTS; i++) + { + if(elmt->lenTagName == lenSingleElements[i]) + { + if(!strncasecmp(singleElements[i], elmt->tagName, elmt->lenTagName)) + { + return true; + } + } + } + return false; +} diff --git a/gb.xml/src/html/htmlelement.h b/gb.xml/src/html/htmlelement.h new file mode 100644 index 00000000..5e201c90 --- /dev/null +++ b/gb.xml/src/html/htmlelement.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HELEMENT_H +#define HELEMENT_H + +#include "htmlmain.h" + +Attribute* HTMLElement_GetClassName(const Element *elmt); +bool HTMLElement_HasClassName(const Element *elmt, const char *className, const size_t lenClassName); +void HTMLElement_SetClassName(Element *elmt, const char *className, const size_t lenClassName); + +Attribute* HTMLElement_GetId(const Element *elmt); +void HTMLElement_SetId(Element *elmt, const char* value, size_t len); + +void HTMLElement_GetGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth = -1); + +Element* HTMLElement_GetChildById(Element *elmt, char *id, size_t lenId, int depth = -1); +void HTMLElement_GetGBChildrenByClassName(Element *elmt, char* className, size_t lenClassName, GB_ARRAY *array, int depth = -1); + +bool HTMLElement_IsSingle(Element *elmt); + + +#endif diff --git a/gb.xml/src/html/htmlmain.cpp b/gb.xml/src/html/htmlmain.cpp new file mode 100644 index 00000000..c892c84d --- /dev/null +++ b/gb.xml/src/html/htmlmain.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __HMAIN_CPP +#include "htmlmain.h" +#include "CHTMLElement.h" +#include "CHTMLDocument.h" +#include "gb.xml.html.h" +#include "htmlserializer.h" +#include "htmlparser.h" +#include "htmldocument.h" + +GB_INTERFACE GB EXPORT; +XML_INTERFACE XML EXPORT; + +extern "C"{ +GB_DESC *GB_CLASSES[] EXPORT = +{ + CDocumentDesc, CDocumentStyleSheetsDesc, CDocumentScriptsDesc, CElementDesc, 0 +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.xml", XML_INTERFACE_VERSION, &XML); + return -1; +} + +void EXPORT GB_EXIT() +{ + +} + +void *GB_XML_HTML_1[] EXPORT = +{ + (void*)XML_HTML_INTERFACE_VERSION, + (void *)serializeHTMLNode, + (void *)GBserializeHTMLNode, + (void *)parseHTML, + (void *)GBparseHTML, + (void *)HtmlDocument_New, + (void *)HtmlDocument_NewFromFile, + NULL +}; + +} diff --git a/gb.xml/src/html/htmlmain.h b/gb.xml/src/html/htmlmain.h new file mode 100644 index 00000000..47f68c01 --- /dev/null +++ b/gb.xml/src/html/htmlmain.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HMAIN_H +#define HMAIN_H + +#include "../gb.xml.h" + +#ifndef __HMAIN_CPP +extern "C" XML_INTERFACE XML; +#endif + +#if defined(OS_MACOSX) || defined(__APPLE__) +#define memrchr(s, c, n) XML.memrchr(s, c, n) +#endif + +#endif diff --git a/gb.xml/src/html/htmlparser.cpp b/gb.xml/src/html/htmlparser.cpp new file mode 100644 index 00000000..8e26d053 --- /dev/null +++ b/gb.xml/src/html/htmlparser.cpp @@ -0,0 +1,342 @@ +#include "htmlparser.h" +#include "htmlelement.h" +#include "../gbinterface.h" +#include +#include "../main.h" + +/***** Parser *****/ + +void GBparseHTML(const char *data, const size_t lendata, GB_ARRAY *array) +{ + size_t nodeCount; + size_t i = 0; + Node **nodes = parseHTML(data, lendata, &nodeCount); + GB.Array.New(array, GB.FindClass("XmlNode"), nodeCount); + + for(i = 0; i < nodeCount; ++i) + { + *(reinterpret_cast((GB.Array.Get(*array, i)))) = XML.XMLNode_GetGBObject(nodes[i]); + GB.Ref(nodes[i]->GBObject); + } + + free(nodes); +} + +//Ajoute 'elmt' à la liste +#define APPEND(_elmt) if(curElement == 0)\ +{\ + (*nodeCount)++;\ + elements = (Node**)realloc(elements, sizeof(Node*) * (*nodeCount));\ + elements[(*nodeCount) - 1] = _elmt;\ +}\ +else \ +{\ + XML.XMLNode_appendChild(curElement, _elmt); \ +} + +Node** parseHTML(char const *data, const size_t lendata, size_t *nodeCount)// XML.ThrowXMLParseException) +{ + *nodeCount = 0; + if(!lendata || !data) return 0; //Empty ? + + const char *endData = data + lendata; + + Node **elements = 0;//Elements to return + Element *curElement = 0;//Current element + + + char s = 0;//Current byte (value) + char const *pos = data;//Current byte (position) + wchar_t ws = 0;//Current character (value) + + char *tag = 0;//First '<' character found + + while(pos < endData)//Start + { + tag = (char*)memchr(pos, '<', endData - pos);//On cherche un début de tag + + if(tag && (tag - pos) != 0)//On ajoute le texte, s'il existe + { + //Checking length + char const *textpos = pos; + size_t textlen = tag - pos; + //XML.Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XML.XMLTextNode_New("", 0); + XML.XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + + if(!tag) + { + if(pos < endData)//Il reste du texte + { + //Checking length + char const *textpos = pos; + size_t textlen = endData - pos; + //XML.Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XML.XMLTextNode_New("", 0); + XML.XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + break; + } + + tag++; + pos = tag;//On avance au caractère trouvé + + //On analyse le contenu du tag + ws = XML.nextUTF8Char(pos, endData - pos);//On prend le premier caractère + + if(!XML.isNameStartChar(ws))//Ce n'est pas un tagName, il y a quelque chose ... + { + if(ws == '/')//C'est un élément de fin + { + Element *oldCurElement = curElement; + while(curElement) + { + if((endData) >= pos + curElement->lenTagName) + { + if(!(strncasecmp(pos, curElement->tagName, curElement->lenTagName))) + { + break; + } + } + curElement = (Element*)(curElement->parent); + } + + if(!curElement) + { + curElement = oldCurElement; + } + else + { + pos += curElement->lenTagName; + curElement = (Element*)(curElement->parent); + } + + tag = (char*)memchr(pos, '>', endData - pos);//On cherche la fin du tag + if(tag) pos = tag + 1;//On avance à la fin du tag + + continue; + + } + else if(ws == '!')//Ce serait un commentaire ou un CDATA + { + if(memcmp(pos, "--", 2) == 0)//C'est bien un commentaire + { + pos += 2;//On va au début du contenu du commentaire + tag = (char*)XML.memchrs(pos, endData - pos, "-->", 3); + if(!tag)//Commentaire sans fin + { + //ERREUR : NEVER-ENDING COMMENT + XML.ThrowXMLParseException("Never-ending comment", + data, lendata, pos - 1); + } + + CommentNode *comment = XML.XMLComment_New("", 0); + XML.XMLTextNode_setEscapedTextContent(comment, pos, tag - pos); + APPEND(comment); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "[CDATA[", 7) == 0)//C'est un CDATA + { + pos += 7;//On va au début du contenu du cdata + tag = (char*)XML.memchrs(pos, endData - pos, "]]>", 3); + if(!tag)//Cdata sans fin + { + //ERREUR : UNENDED CDATA + XML.ThrowXMLParseException("Never-ending CDATA", + data, lendata, pos - 1); + } + + CDATANode *cdata = XML.XMLCDATA_New("", 0); + XML.XMLTextNode_setEscapedTextContent(cdata, pos, tag - pos); + APPEND(cdata); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "DOCTYPE", 7) == 0)//Doctypes are silently ignored for the moment + { + pos += 7; + tag = (char*)memchr(pos, '>', endData - pos); + if(!tag)//Doctype sans fin + { + XML.ThrowXMLParseException("Never-ending DOCTYPE", + data, lendata, pos - 1); + } + + pos = tag + 1; + continue; + } + else// ... ? + { + //ERREUR : INVALID TAG + XML.ThrowXMLParseException("Invalid Tag", + data, lendata, pos - 1); + } + } + else if(ws == '?')//Processing Instruction //TODO : add the PI API + { + tag = (char*)XML.memchrs(pos, endData - pos, "?>", 2);//Looking for the end of the PI + if(!tag)//Endless PI + { + XML.ThrowXMLParseException("Never-ending Processing instruction", + data, lendata, pos - 1); + } + + pos = tag + 2; + continue; + + } + else// ... ? + { + //ERREUR : INVALID TAG + XML.ThrowXMLParseException("Invalid Tag", + data, lendata, pos - 1); + } + }//Si tout va bien, on a un nouvel élément + else + { + while(XML.isNameChar(XML.nextUTF8Char(pos, endData - pos)))//On cherche le tagName + { + if(pos > endData) + { + //ERREUR : NEVER-ENDING TAG + XML.ThrowXMLParseException("Never-ending tag", + data, lendata, pos - 1); + } + } + pos--; + + Element *elmt = XML.XMLElement_New(tag, pos - tag); + APPEND(elmt); + curElement = elmt; + s = *pos; + + while(pos < endData)//On gère le contenu de l'élément (attributs) + { + if(s == '>') + { + if(HTMLElement_IsSingle(curElement)) + { + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + } + else if(XML.GB_MatchString(curElement->tagName, curElement->lenTagName, "script", 6, 1)) + { + pos ++;//On va au début du contenu du script + + char *scriptTag = (char*)malloc(sizeof(char) * (curElement->lenTagName) + 3); + scriptTag[0] = '<'; scriptTag[1] = '/'; scriptTag[curElement->lenTagName + 2] = '>'; + memcpy(scriptTag + 2, curElement->tagName, curElement->lenTagName); + + tag = (char*)XML.memchrs(pos, endData - pos, scriptTag, curElement->lenTagName + 3); + + free(scriptTag); + + if(!tag)//Script sans fin + { + XML.ThrowXMLParseException("Never-ending Script", + data, lendata, pos - 1); + } + + TextNode *scriptcontent = XML.XMLTextNode_New("", 0); + XML.XMLTextNode_setEscapedTextContent(scriptcontent, pos, tag - pos); + APPEND(scriptcontent); + pos = tag + curElement->lenTagName + 2; + curElement = (Element*)(curElement->parent);//Pas d'autres enfants, on remonte + } + break;//Fin de l'élément + } + if(s == '/') //Élément auto-fermant + { + pos++; + + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + + if(XML.isNameStartChar(s))//Début d'attribut + { + const char *attrNamestart = pos; + while(XML.isNameChar(XML.nextUTF8Char(pos, endData - pos)) && pos < endData){}//On parcourt le nom d'attribut + pos--; + const char *attrNameEnd = pos; + s = *pos; + while(XML.isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + if(s != '=') + { + XML.XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, "", 0); + if(s == '>') break;//Fin de l'élément + else if (s == '/')//Élément auto-fermant + { + pos++; + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + else + { + /*//ERREUR : INVALID TAG + XML.ThrowXMLParseException("Invalid tag", + data, lendata, pos - 1);*/ + //Tag not ended correctly ? ... + pos = (char*)memchr(pos, '>', endData - pos); + break; + } + } + + pos++; s = *pos; + + while(XML.isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + char delimiter = s; + if(delimiter != '"' && delimiter != '\'') + { + /*//ERREUR : EXPECTED ATTRIBUTE DELIMITER + XML.ThrowXMLParseException("Expected attribute delimiter", + data, lendata, pos - 1);*/ + //Seeking to the next white space or tag-end + while(!(XML.isWhiteSpace(s)) && pos < endData && s != '>'){pos++; s = *pos;} + delimiter = s; + const char* delimiterPos = (char*)memchr(pos, delimiter, endData - pos); + + XML.XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, + pos, delimiterPos - pos); + pos = delimiterPos; + if(delimiter == '>') pos = delimiterPos - 1; + } + else + { + pos++; + const char* delimiterPos = (char*)memchr(pos, delimiter, endData - pos); + if(!delimiterPos) delimiterPos = endData; + + XML.XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, + pos, delimiterPos - pos); + pos = delimiterPos; + } + + + + + + } + + pos++; s = *pos; + } + + } + pos++; + + } + + return elements; + +} diff --git a/gb.xml/src/html/htmlparser.h b/gb.xml/src/html/htmlparser.h new file mode 100644 index 00000000..0e8065fe --- /dev/null +++ b/gb.xml/src/html/htmlparser.h @@ -0,0 +1,9 @@ +#ifndef HTMLPARSER_H +#define HTMLPARSER_H + +#include "htmlmain.h" + +void GBparseHTML(char const *data, const size_t lendata, GB_ARRAY *array); +Node** parseHTML(char const *data, const size_t lendata, size_t *nodeCount);// throw(XMLParseException); + +#endif // HTMLPARSER_H diff --git a/gb.xml/src/html/htmlserializer.cpp b/gb.xml/src/html/htmlserializer.cpp new file mode 100644 index 00000000..5d46cdde --- /dev/null +++ b/gb.xml/src/html/htmlserializer.cpp @@ -0,0 +1,239 @@ +#include "htmlserializer.h" +#include "htmlelement.h" +#include "../gbinterface.h" +#include + +void addStringLen(Node *node, size_t &len, int indent = -1);//Calculates the node's string representation length, and adds it to len (recursive) +void addString(Node *node, char *&data, int indent = -1);//Puts the string represenetation into data, and increments it (recursive) + + +void serializeHTMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = (char*)malloc(sizeof(char) * (len)); + addString(node, output, indent); + output -= len; + +} + +void GBserializeHTMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = GB.TempString(0, len); + addString(node, output, indent); + output -= len; +} + +void addStringLen(Node *node, size_t &len, int indent) +{ + switch (node->type) + { + case Node::DocumentNode: + if(((Document*)node)->docType == HTMLDocumentType) + { + len += 15 + (indent >= 0 ? 1 : 0); + } + else + { + len += 109 + (indent >= 0 ? 1 : 0); + } + //Content + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + '>' \n + // + children + (indent) '" \n + // Or, singlElement : + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + ' />' \n + if(HTMLElement_IsSingle((Element*)node)) + { + len += (4 + ((Element*)node)->lenTagName); + if(indent >= 0) len += indent + 1; + } + else + { + len += (5 + ((((Element*)node)->lenTagName) * 2)); + if(indent >= 0) len += indent * 2 + 2; + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent + 1 : -1); + } + } + + for(Attribute *attr = (Attribute*)(((Element*)node)->firstAttribute); attr != 0; attr = (Attribute*)(attr->nextNode)) + { + len += 4 + attr->lenAttrName + attr->lenAttrValue; + } + break; + case Node::NodeText: + + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + len += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) len += indent + 1; + break; + + case Node::Comment: + + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenEscapedContent + 7; + if(indent >= 0) len += indent + 1; + break; + case Node::CDATA: + + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenContent + 12; + if(indent) len += indent + 1; + break; + + default: + break; + } +} + + +#define ADD(_car) *data = _car; data++; +void addString(Node *node, char *&data, int indent) +{ + bool single; + switch (node->type) + { + case Node::DocumentNode: + if(((Document*)node)->docType == HTMLDocumentType) + { + memcpy(data, "", 15); + data += 15; + } + else + { + memcpy(data, "", 109); + data += 109; + } + if(indent >= 0) + { + ADD('\n') + } + //Content + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + //char *content = data; + single = HTMLElement_IsSingle((Element*)node); + + //Opening tag + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + ADD('<'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + + //Attributes + for(Attribute *attr = (Attribute*)((Element*)node)->firstAttribute; attr != 0; attr = (Attribute*)(attr->nextNode)) + { + ADD(' '); + memcpy(data, attr->attrName, attr->lenAttrName); data += attr->lenAttrName; + + ADD('='); + ADD('"'); + memcpy(data, attr->attrValue, attr->lenAttrValue); data += attr->lenAttrValue; + ADD('"'); + } + + if(single) + { + ADD(' '); + ADD('/'); + } + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + if(!single) + { + + //Content + for(Node *child = ((Element*)node)->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent + 1 : -1); + } + + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + + //Ending Tag + ADD('<'); + ADD('/'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + } + break; + case Node::NodeText: + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + + memcpy(data, ((TextNode*)node)->escapedContent, ((TextNode*)node)->lenEscapedContent); + data += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::Comment: + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::CDATA: + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "content, ((CDATANode*)node)->lenContent); + data += ((CDATANode*)node)->lenContent; + memcpy(data, "]]>", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + default: + break; + } +} diff --git a/gb.xml/src/html/htmlserializer.h b/gb.xml/src/html/htmlserializer.h new file mode 100644 index 00000000..ffe8f250 --- /dev/null +++ b/gb.xml/src/html/htmlserializer.h @@ -0,0 +1,10 @@ +#ifndef SERIALIZER_H +#define SERIALIZER_H + +#include "htmlmain.h" + +void serializeHTMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Converts the node to its string representation +void GBserializeHTMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Same as above, but returns a Gambas String directly + + +#endif // SERIALIZER_H diff --git a/gb.xml/src/main.cpp b/gb.xml/src/main.cpp new file mode 100644 index 00000000..56f8fbfa --- /dev/null +++ b/gb.xml/src/main.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CNode.h" +#include "CElement.h" +#include "CTextNode.h" +#include "CDocument.h" +#include "CExplorer.h" +#include "CReader.h" + +#include "node.h" +#include "document.h" +#include "element.h" +#include "textnode.h" +#include "serializer.h" +#include "parser.h" + +#include "gambas.h" +#include "html/gb.xml.html.h" + +GB_INTERFACE GB EXPORT; +XML_HTML_INTERFACE HTML EXPORT; + +extern "C" +{ + + GB_DESC *GB_CLASSES[] EXPORT = + { + CNodeDesc, CElementDesc, CTextNodeDesc, CCommentNodeDesc, CCDATANodeDesc, CElementAttributesDesc, CElementAttributeNodeDesc, + CDocumentDesc, + CExplorerDesc, + CReaderDesc, CReaderNodeAttributesDesc, CReaderNodeDesc, CReaderNodeTypeDesc, CReaderReadFlagsDesc, 0 + }; + + void *GB_XML_1[] EXPORT = + { + (void *)XML_INTERFACE_VERSION, + (void *)serializeXMLNode, + (void *)GBserializeXMLNode, + (void *)parseXML, + (void *)GBGetXMLTextContent, + + (void *)XMLText_escapeContent, + (void *)XMLText_unEscapeContent, + (void *)XMLText_escapeAttributeContent, + + (void *)XMLNode_GetGBObject, + (void *)XMLNode_getFirstChildByAttributeValue, + (void *)XMLNode_getGBChildrenByAttributeValue, + (void *)XMLNode_firstChildElement, + (void *)XMLNode_lastChildElement, + (void *)XMLNode_previousElement, + (void *)XMLNode_appendChild, + (void *)XMLNode_getChildrenByTagName, + (void *)XMLNode_getFirstChildByTagName, + (void *)XMLNode_setTextContent, + + (void *)static_cast(XMLTextNode_New), + (void *)XMLTextNode_setEscapedTextContent, + (void *)XMLTextNode_checkEscapedContent, + + (void *) XMLDocument_New, + (void *) XMLDocument_NewFromFile, + (void *)XMLDocument_SetContent, + + (void *) static_cast(XMLElement_New), + (void *)XMLElement_SetTagName, + (void *)XMLElement_AttributeContains, + (void *) static_cast(XMLElement_AddAttribute), + (void *)XMLElement_GetAttribute, + (void *)XMLElement_SetAttribute, + (void *) static_cast(XMLElement_RemoveAttribute), + (void *) static_cast(XMLComment_New), + + (void *) static_cast(XMLCDATA_New), + + (void *)XML_ReturnNode, + + (void *)Trim, + (void *)isNameStartChar, + (void *)isNameChar, + (void *)memchrs, + (void *)GB_MatchString, + (void *)nextUTF8Char, + (void *) static_cast(isWhiteSpace), + + (void *)ThrowXMLParseException, + + #if defined(OS_MACOSX) || defined(__APPLE__) + (void*)memrchr, + #endif + + NULL + }; + + int EXPORT GB_INIT(void) + { + memset(&HTML, 0, sizeof(XML_HTML_INTERFACE)); + return -1; + } + + void EXPORT GB_EXIT() + { + + } +} + +bool CheckHtmlInterface() +{ + if((int)HTML.version == XML_HTML_INTERFACE_VERSION) + { + return true; + } + if(GB.ExistClass("HtmlDocument")) + { + GB.GetInterface("gb.xml.html", XML_HTML_INTERFACE_VERSION, &HTML); + return true; + } + return false; +} + diff --git a/gb.xml/src/main.h b/gb.xml/src/main.h new file mode 100644 index 00000000..c1825fa4 --- /dev/null +++ b/gb.xml/src/main.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef MAIN_H +#define MAIN_H + +#include "gb.xml.h" +#include "html/gb.xml.html.h" + +extern "C" XML_HTML_INTERFACE HTML; + +bool CheckHtmlInterface(); + +#endif // MAIN_H diff --git a/gb.xml/src/node.cpp b/gb.xml/src/node.cpp new file mode 100644 index 00000000..0961b169 --- /dev/null +++ b/gb.xml/src/node.cpp @@ -0,0 +1,646 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "node.h" +#include "element.h" +#include "textnode.h" +#include "parser.h" +#include "document.h" +#include "CNode.h" +#include "utils.h" + +#include +#include + +bool Node_NoInstanciate = false;//If true, newly-created Gambas objects won't instanciate a new node + +void XMLNode_Init(Node *node, Node::Type nodeType) +{ + memset(node, 0, sizeof(Node)); + node->type = nodeType; +} + +void XMLNode_Free(Node *&node)//TODO: Handle per-node type freeing +{ + //fprintf(stderr, "XMLNode_Free: %p\n", node); + + if(!node) return; + if(node->userData) + { + GB.Unref(POINTER(&(node->userData))); + node->userData = 0; + } + switch(node->type) + { + case Node::ElementNode: + XMLElement_Free((Element*)node); + break; + case Node::DocumentNode: + XMLDocument_Release((Document*)node); + break; + case Node::NodeText: + case Node::CDATA: + case Node::Comment: + XMLTextNode_Free((TextNode*)node); + break; + default: + return; + break; + } + node = 0; +} + +CNode* XMLNode_GetGBObject(Node *node) +{ + if(!node->GBObject) + { + XMLNode_NewGBObject(node); + } + return node->GBObject; +} + +void XMLNode_NewGBObject(Node *node) +{ + Node_NoInstanciate = true; + switch(node->type) + { + case Node::ElementNode: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlElement"), 0, 0); + break; + case Node::CDATA: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlCDataNode"), 0, 0); + break; + case Node::Comment: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlCommentNode"), 0, 0); + break; + case Node::NodeText: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlTextNode"), 0, 0); + break; + case Node::DocumentNode: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlDocument"), 0, 0); + break; + case Node::AttributeNode: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlNode"), 0, 0); + break; + default: + fprintf(stderr, "FATAL : tried to create a Gambas object with invalid type."); + exit(EXIT_FAILURE); + break; + } + node->GBObject->node = node; + Node_NoInstanciate = false; +} + +void XMLNode_DestroyGBObject(Node *&node) +{ + if((!node->parent) && (!node->parentDocument)) + { + XMLNode_Free(node); + } + else + { + node->GBObject = 0; + } +} + +void XMLNode_DestroyParent(Node *node) +{ + if(!node->GBObject) + { + XMLNode_Free(node); + } + else + { + node->parent = 0; + node->parentDocument = 0; + } +} + +/***** Node tree *****/ + +Document* XMLNode_GetOwnerDocument(Node *node) +{ + if(node->type == Node::DocumentNode || node->type == Node::HTMLDocumentNode) { + return (Document*) node; + } + + while(node->parent && !node->parentDocument) + node = (Node*)(node->parent); + return node->parentDocument; +} + +void XMLNode_getGBChildren(Node *node, GB_ARRAY *array) +{ + GB.Array.New(array, GB.FindClass("XmlNode"), node->childCount); + if(!(SUPPORT_CHILDREN(node))) return; + int i = 0; + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + *(reinterpret_cast((GB.Array.Get(*array, i)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + ++i; + } +} + +/***** Node tree *****/ +void XMLNode_appendChild(Node *node, Node *newChild) +{ + if (newChild->parent) + { + GB.Error("Node already has a parent"); + return; + } + + (node->childCount)++; + if(!(node->lastChild))//No child + { + node->firstChild = newChild; + node->lastChild = newChild; + node->lastChild->previousNode = 0; + node->lastChild->nextNode = 0; + newChild->parent = node; + return; + } + + newChild->previousNode = node->lastChild; + node->lastChild->nextNode = newChild; + node->lastChild = newChild; + node->lastChild->nextNode = 0; + newChild->parent = node; + +} + +void XMLNode_prependChild(Node *node, Node *newChild) +{ + node->childCount++; + if(!node->lastChild)//No child + { + node->firstChild = newChild; + node->lastChild = node->firstChild; + node->lastChild->previousNode = 0; + node->lastChild->nextNode = 0; + newChild->parent = node; + return; + } + + newChild->nextNode = node->firstChild; + node->firstChild->previousNode = newChild; + node->firstChild = newChild; + node->firstChild->previousNode = 0; + newChild->parent = node; +} + +void XMLNode_removeKeepChild(Node *node, Node *child) +{ + if(child == node->firstChild) node->firstChild = child->nextNode; + if(child == node->lastChild) node->lastChild = child->previousNode; + if(child->nextNode) child->nextNode->previousNode = child->previousNode; + if(child->previousNode) child->previousNode->nextNode = child->nextNode; + node->childCount--; +} + +void XMLNode_removeChild(Node *node, Node *child) +{ + XMLNode_removeKeepChild(node, child); + XMLNode_DestroyParent(child); +} + +GB_VALUE *aft_args; +int aft_argsCount; + +void XMLNode_appendFromTextSubstCallback(int index, char* *str, int *len) +{ + if(index < 1 || index > aft_argsCount) return; + size_t nlen; + + XML_Format(&(aft_args[index - 1]), *str, nlen); + *len = (int)nlen; + +} + +void XMLNode_substAppendFromText(Node *node, const char *data, const size_t lenData, GB_VALUE *args, int argsCount) +{ + char *newData; + size_t lenNewData; + + aft_args = args; + aft_argsCount = argsCount; + + newData = GB.SubstString(data, lenData, XMLNode_appendFromTextSubstCallback); + lenNewData = GB.StringLength(newData); + + XMLNode_appendFromText(node, newData, lenNewData); +} + +void XMLNode_appendFromText(Node *node, const char *data, const size_t lenData) +{ + size_t nodeCount = 0; + Document *parentDoc = XMLNode_GetOwnerDocument(node); + + Node **nodes = parse(data, lenData, &nodeCount, parentDoc ? parentDoc->docType : XMLDocumentType); + for(size_t i = 0; i < nodeCount; i++) + { + XMLNode_appendChild(node, nodes[i]); + } + free(nodes); +} + +void XMLNode_addGBChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, GB_ARRAY *array, const int mode, const int depth) +{ + if(depth == 0 || depth == 1) return; + + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(tNode->type != Node::ElementNode) continue; + + if(GB_MatchString(((Element*)tNode)->tagName, ((Element*)tNode)->lenTagName, compTagName, compLenTagName, mode)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } + + XMLNode_addGBChildrenByTagName(tNode, compTagName, compLenTagName, array, mode, depth - 1); + } +} + +void XMLNode_addGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode, const int depth) +{ + if(depth == 0 || depth == 1) return; + + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(tNode->type != Node::ElementNode) continue; + + if(GB_MatchString(((Element*)tNode)->tagName, ((Element*)tNode)->lenTagName, cnamespace, lenNamespace, mode)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } + + XMLNode_addGBChildrenByNamespace(tNode, cnamespace, lenNamespace, array, mode, depth - 1); + } +} + +void XMLNode_addGBAllChildren(Node *node, GB_ARRAY *array) +{ + if(SUPPORT_CHILDREN(node)) + { + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + XMLNode_addGBAllChildren(tNode, array); + } + } + +} + +void XMLNode_getGBChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, GB_ARRAY *array, const int mode, const int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + XMLNode_addGBChildrenByTagName(node, ctagName, clenTagName, array, mode, depth); +} + +void XMLNode_getGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode, const int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + XMLNode_addGBChildrenByNamespace(node, cnamespace, lenNamespace, array, mode, depth); +} + +void XMLNode_getGBAllChildren(Node *node, GB_ARRAY *array) +{ + GB.Array.New(array, GB.FindClass("XmlNode"), 0); + XMLNode_addGBAllChildren(node, array); +} + +void XMLNode_getGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode, const int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + XMLNode_addGBChildrenByAttributeValue(node, attrName, lenAttrName, attrValue, lenAttrValue, array, mode, depth); +} + +void XMLNode_addGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode, const int depth) +{ + if(depth == 0 || depth == 1) return; + + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(tNode->type != Node::ElementNode) continue; + Attribute *attr = XMLElement_GetAttribute((Element*)tNode, attrName, lenAttrName); + if(attr) + { + if(GB_MatchString(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue, mode)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } + } + + XMLNode_addGBChildrenByAttributeValue(tNode, attrName, lenAttrName, attrValue, lenAttrValue, array, mode, depth - 1); + } +} + +void XMLNode_getGBChildElements(Node *node, GB_ARRAY *array) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(!SUPPORT_CHILDREN(tNode)) continue; + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } +} + +void XMLNode_addChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, Element** &array, size_t &lenArray, const int depth) +{ + if(depth == 0) return; + if(node->type == Node::ElementNode) + { + if(compLenTagName == ((Element*)node)->lenTagName) + { + if(memcmp(compTagName, ((Element*)node)->tagName, compLenTagName) == 0) + { + array = (Element**)realloc(array, sizeof(Element*) * (lenArray + 1)); + array[lenArray] = ((Element*)node); + ++lenArray; + } + } + } + if(depth == 1) return; + + if(SUPPORT_CHILDREN(node)) + { + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + XMLNode_addChildrenByTagName(tNode, compTagName, compLenTagName, array, lenArray,depth - 1); + } + } +} + +Element** XMLNode_getChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, size_t &lenArray, const int depth) +{ + lenArray = 0; + Element **array = 0; + XMLNode_addChildrenByTagName(node, ctagName, clenTagName, array, lenArray, depth); + return array; +} + +Element* XMLNode_getFirstChildByTagName(const Node *node, const char *ctagName, const size_t clenTagName, const int depth) +{ + if(depth == 0) return 0; + if(node->type == Node::ElementNode) + { + if(((Element*)node)->lenTagName == clenTagName) + { + if(!memcmp(((Element*)node)->tagName, ctagName, clenTagName)) return ((Element*)node); + } + } + if(depth == 1) return 0; + if(!SUPPORT_CHILDREN(node)) return 0; + Element *elmt = 0; + for(Node *it = node->firstChild; it != 0; it = it->nextNode) + { + if((it)->type == Node::ElementNode) + { + elmt = XMLNode_getFirstChildByTagName(it, ctagName, clenTagName, depth - 1); + if(elmt) return elmt; + } + } + return 0; +} + +Element* XMLNode_firstChildElement(Node *node) +{ + Node *child = node->firstChild; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->nextNode; + } + + return 0; +} + +Element* XMLNode_lastChildElement(Node *node) +{ + Node *child = node->lastChild; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->previousNode; + } + + return 0; +} + +Element* XMLNode_nextElement(Node *node) +{ + Node *child = node->nextNode; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->nextNode; + } + + return 0; +} + +Element* XMLNode_previousElement(const Node *node) +{ + Node *child = node->previousNode; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->previousNode; + } + + return 0; +} + +bool XMLNode_insertAfter(Node *node, Node *child, Node *newChild) +{ + if(child->parent != node) return false; + newChild->nextNode = child->nextNode; + newChild->previousNode = child; + if(child->nextNode) + { + child->nextNode->previousNode = newChild; + } + if(child == node->lastChild) + { + node->lastChild = newChild; + } + child->nextNode = newChild; + newChild->parent = node; + node->childCount++; + return true; +} + +bool XMLNode_insertBefore(Node *node, Node *child, Node *newChild) +{ + if(child->parent != node) return false; + newChild->nextNode = child; + newChild->previousNode = child->previousNode; + if(child->previousNode) + { + child->previousNode->nextNode = newChild; + } + if(child == node->firstChild) + { + node->firstChild = newChild; + } + child->previousNode = newChild; + newChild->parent = node; + node->childCount++; + return true; +} + +void XMLNode_replaceChild(Node *node, Node *oldChild, Node *newChild) +{ + if(XMLNode_insertBefore(node, oldChild, newChild)) + XMLNode_removeChild(node, oldChild); +} + + +void XMLNode_appendText(Node *node, const char *data, const size_t lenData) +{ + if(node->lastChild && node->lastChild->type == Node::NodeText) + { + TextNode *text = (TextNode*)node->lastChild; + text->content = (char*)realloc(text->content, lenData + text->lenContent); + memcpy(text->content + text->lenContent, data, lenData); + text->lenContent += lenData; + } + else + { + TextNode *text = XMLTextNode_New(data, lenData); + XMLNode_appendChild(node, text); + } +} + +void XMLNode_clearChildren(Node *node) +{ + if(node->childCount == 0) return; + Node* prevChild = 0; + Node* child = 0; + //fprintf(stderr, "XMLNode_clearChildren: <<< node = %p\n", node); + for(child = node->firstChild->nextNode; child != 0; child = child->nextNode) + { + //fprintf(stderr, "XMLNode_clearChildren: child = %p\n", child); + prevChild = child->previousNode; + prevChild->nextNode = 0; + prevChild->previousNode = 0; + XMLNode_DestroyParent(prevChild); + } + node->lastChild->nextNode = 0; + node->lastChild->previousNode = 0; + XMLNode_DestroyParent(node->lastChild); + + node->childCount = 0; + node->lastChild = 0; + node->firstChild = 0; + //fprintf(stderr, "XMLNode_clearChildren: >>> node = %p\n", node); +} + +void XMLNode_setTextContent(Node *node, const char *content, const size_t lenContent) +{ + switch(node->type) + { + case Node::ElementNode: + XMLElement_SetTextContent((Element*)node, content, lenContent); + break; + case Node::AttributeNode: + XMLAttribute_SetValue((Attribute*)node, content, lenContent); + default: + return; + } +} + +GB_VARIANT* XMLNode_getUserData(Node *node, const char *key, const size_t lenkey) +{ + if(!node->userData) return 0; + GB_VARIANT *srcValue = new GB_VARIANT; + if (GB.Collection.Get(node->userData, key, lenkey, srcValue)) return 0; + return srcValue; +} + +void XMLNode_addUserData(Node *node, const char *key, const size_t lenkey, GB_VARIANT *value) +{ + if(!node->userData) + { + GB.Collection.New(POINTER(&(node->userData)), GB_COMP_BINARY); + } + + GB.Collection.Set(node->userData, key, lenkey, value); +} + + +bool XMLNode_NoInstanciate() +{ + return Node_NoInstanciate; +} + +void XML_ReturnNode(Node *node) +{ + if(!node) + { + GB.ReturnNull(); return; + } + if(!(node->GBObject)) + { + XMLNode_NewGBObject(node); + } + GB.ReturnObject(node->GBObject); +} + + +Element *XMLNode_getFirstChildByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, const char *attrValue, const size_t lenAttrValue, const int mode, const int depth) +{ + if(depth == 0) return 0; + + if(SUPPORT_CHILDREN(node)) + { + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + if(child->type == Node::ElementNode) + { + Element *elmt; + Attribute *attr; + attr = XMLElement_GetAttribute((Element*)child, attrName, lenAttrName); + if(attr) + { + if(GB_MatchString(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue, mode)) + { + return (Element*)child; + } + } + elmt = XMLNode_getFirstChildByAttributeValue(child, attrName, lenAttrName, attrValue, lenAttrValue, mode, depth - 1); + if(elmt) return elmt; + } + } + } + + return 0; +} diff --git a/gb.xml/src/node.h b/gb.xml/src/node.h new file mode 100644 index 00000000..0a0406bd --- /dev/null +++ b/gb.xml/src/node.h @@ -0,0 +1,98 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef NODE_H +#define NODE_H + +#include "gb.xml.h" + +#define SUPPORT_CHILDREN(__node) ((__node->type == Node::ElementNode) || (__node->type == Node::DocumentNode)) + +class Element; +class TextNode; +class Document; + +struct CNode; + +void XMLNode_Init(Node* node, Node::Type nodeType); +void XMLNode_Free(Node* &node); + +void XMLNode_DestroyGBObject(Node* &node); +void XMLNode_DestroyParent(Node *node); + +void XMLNode_NewGBObject(Node *node); +CNode* XMLNode_GetGBObject(Node *node); + +//Node tree +void XMLNode_getGBChildren(Node *node, GB_ARRAY *array); +Document* XMLNode_GetOwnerDocument(Node *node); +void XMLNode_appendChild(Node *node, Node *newChild); +void XMLNode_prependChild(Node *node, Node *newChild); +void XMLNode_removeChild(Node *node, Node *child); +void XMLNode_removeKeepChild(Node *node, Node *child); +void XMLNode_replaceChild(Node *node, Node *oldChild, Node *newChild); +bool XMLNode_insertAfter(Node *node, Node *child, Node *newChild); +bool XMLNode_insertBefore(Node *node, Node *child, Node *newChild); +void XMLNode_appendText(Node *node, const char *data, const size_t lenData); +void XMLNode_clearChildren(Node *node); +void XMLNode_appendFromText(Node *node, const char *data, const size_t lenData); + +//Searching elements +void XMLNode_getGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_addGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_getGBChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_addGBChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_getGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_addGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +Element* XMLNode_getFirstChildByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +Element** XMLNode_getChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, size_t &lenArray, const int depth = -1); +Element* XMLNode_getFirstChildByTagName(const Node *node, const char *ctagName, const size_t clenTagName, const int depth = -1); +void XMLNode_addChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, Element** &array, size_t &lenArray, const int depth = -1); +void XMLNode_getGBAllChildren(Node *node, GB_ARRAY *array); +void XMLNode_addGBAllChildren(Node *node, GB_ARRAY *array); +void XMLNode_getGBChildElements(Node *node, GB_ARRAY *array); + +Element* XMLNode_firstChildElement(Node *node); +Element* XMLNode_lastChildElement(Node *node); +Element* XMLNode_previousElement(const Node *node); +Element* XMLNode_nextElement(Node *node); + +void XMLNode_setTextContent(Node *node, const char *content, const size_t lenContent);//Sets the plain text conent of a node + + + +bool XMLNode_NoInstanciate(); + +void XML_ReturnNode(Node *node); + +#endif // NODE_H + +#if !defined(NODE_GBINTERFACE) && defined(GBINTERFACE_H) +#define NODE_GBINTERFACE +void XMLNode_substAppendFromText(Node *node, const char *data, const size_t lenData, GB_VALUE *args, int argsCount); +GB_VARIANT *XMLNode_getUserData(Node *node, const char *key, const size_t lenkey); +void XMLNode_addUserData(Node *node, const char *key, const size_t lenkey, GB_VARIANT *value); +#endif diff --git a/gb.xml/src/parser.cpp b/gb.xml/src/parser.cpp new file mode 100644 index 00000000..ac3dcc0c --- /dev/null +++ b/gb.xml/src/parser.cpp @@ -0,0 +1,341 @@ +#include "parser.h" + +#include "node.h" +#include "element.h" +#include "textnode.h" +#include "gbinterface.h" +#include + + +/***** Parser *****/ + +void GBparse(const char *data, const size_t lendata, GB_ARRAY *array, DocumentType docType) +{ + if(docType == HTMLDocumentType || docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + HTML.GBparseHTML(data, lendata, array); + return; + } + } + + GBparseXML(data, lendata, array); +} + +Node** parse(char const *data, const size_t lendata, size_t *nodeCount, DocumentType docType) +{ + if(docType == HTMLDocumentType || docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + return HTML.parseHTML(data, lendata, nodeCount); + } + } + + return parseXML(data, lendata, nodeCount); +} + +void GBparseXML(const char *data, const size_t lendata, GB_ARRAY *array) +{ + size_t nodeCount; + size_t i = 0; + Node **nodes = parseXML(data, lendata, &nodeCount); + GB.Array.New(array, GB.FindClass("XmlNode"), nodeCount); + + for(i = 0; i < nodeCount; ++i) + { + *(reinterpret_cast((GB.Array.Get(*array, i)))) = XMLNode_GetGBObject(nodes[i]); + GB.Ref(nodes[i]->GBObject); + } + + free(nodes); +} + +//Ajoute 'elmt' à la liste +#define APPEND(_elmt) if(curElement == 0)\ +{\ + (*nodeCount)++;\ + elements = (Node**)realloc(elements, sizeof(Node*) * (*nodeCount));\ + elements[(*nodeCount) - 1] = _elmt;\ +}\ +else \ +{\ + XMLNode_appendChild(curElement, _elmt); \ +} + +void parser_cleanup(Node **elements, size_t *nodeCount) +{ + for(size_t i = *nodeCount; i --> 0;) + { + XMLNode_Free(elements[i]); + } + free(elements); +} + +#define THROW(_ex) parser_cleanup(elements, nodeCount); throw(_ex) + +Node** parseXML(char const *data, const size_t lendata, size_t *nodeCount) +{ + *nodeCount = 0; + if(!lendata || !data) return 0; //Empty ? + + const char *endData = data + lendata; + + Node **elements = 0;//Elements to return + Element *curElement = 0;//Current element + + + char s = 0;//Current byte (value) + char const *pos = data;//Current byte (position) + wchar_t ws = 0;//Current character (value) + + char *tag = 0;//First '<' character found + + while(pos < endData)//Start + { + tag = (char*)memchr(pos, '<', endData - pos);//On cherche un début de tag + if(tag && (tag - pos) != 0)//On ajoute le texte, s'il existe + { + //Checking length + char const *textpos = pos; + size_t textlen = tag - pos; + //Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XMLTextNode_New(); + XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + + if(!tag) + { + if(pos < endData)//Il reste du texte + { + //Checking length + char const *textpos = pos; + size_t textlen = endData - pos; + //Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XMLTextNode_New(); + XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + break; + } + + tag++; + pos = tag;//On avance au caractère trouvé + + //On analyse le contenu du tag + ws = nextUTF8Char(pos, endData - pos);//On prend le premier caractère + + if(!isNameStartChar(ws))//Ce n'est pas un tagName, il y a quelque chose ... + { + if(ws == '/')//C'est un élément de fin + { + if(!curElement)//Pas d'élément courant + { + //ERREUR : CLOSING TAG WHEREAS NONE IS OPEN + THROW(XMLParseException_New("Closing tag whereas none is open", + data, lendata, pos - 1)); + + } + if((endData) < pos + curElement->lenTagName)//Impossible que les tags correspondent + { + //ERREUR : TAG MISMATCH + THROW(XMLParseException_New("Tag mismatch", + data, lendata, pos - 1)); + } + //Les tags ne correspondent pas + else if(memcmp(pos, curElement->tagName, curElement->lenTagName) != 0) + { + //ERREUR : TAG MISMATCH + THROW(XMLParseException_New("Tag mismatch", + data, lendata, pos - 1)); + } + else//Les tags correspondent, on remonte + { + pos += curElement->lenTagName; + curElement = (Element*)(curElement->parent); + tag = (char*)memchr(pos, '>', endData - pos);//On cherche la fin du tag + if (!tag) + { + THROW(XMLParseException_New("Never-ending tag", data, lendata, pos - 1)); + } + pos = tag + 1;//On avance à la fin du tag + + continue; + } + } + else if(ws == '!')//Ce serait un commentaire ou un CDATA + { + if(memcmp(pos, "--", 2) == 0)//C'est bien un commentaire + { + pos += 2;//On va au début du contenu du commentaire + tag = (char*)memchrs(pos, endData - pos, "-->", 3); + if(!tag)//Commentaire sans fin + { + //ERREUR : NEVER-ENDING COMMENT + THROW(XMLParseException_New("Never-ending comment", + data, lendata, pos - 1)); + } + + CommentNode *comment = XMLComment_New(); + XMLTextNode_setEscapedTextContent(comment, pos, tag - pos); + APPEND(comment); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "[CDATA[", 7) == 0)//C'est un CDATA + { + pos += 7;//On va au début du contenu du cdata + tag = (char*)memchrs(pos, endData - pos, "]]>", 3); + if(!tag)//Cdata sans fin + { + //ERREUR : UNENDED CDATA + THROW(XMLParseException_New("Never-ending CDATA", + data, lendata, pos - 1)); + } + + CDATANode *cdata = XMLCDATA_New(); + XMLTextNode_setEscapedTextContent(cdata, pos, tag - pos); + APPEND(cdata); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "DOCTYPE", 7) == 0)//Doctypes are silently ignored for the moment + { + pos += 7; + tag = (char*)memchr(pos, '>', endData - pos); + if(!tag)//Doctype sans fin + { + THROW(XMLParseException_New("Never-ending DOCTYPE", + data, lendata, pos - 1)); + } + + pos = tag + 1; + continue; + } + else// ... ? + { + //ERREUR : INVALID TAG + THROW(XMLParseException_New("Invalid Tag", + data, lendata, pos - 1)); + } + } + else if(ws == '?')//Processing Instruction //TODO : add the PI API + { + tag = (char*)memchrs(pos, endData - pos, "?>", 2);//Looking for the end of the PI + if(!tag)//Endless PI + { + THROW(XMLParseException_New("Never-ending Processing instruction", + data, lendata, pos - 1)); + } + + pos = tag + 2; + continue; + + } + else// ... ? + { + //ERREUR : INVALID TAG + THROW(XMLParseException_New("Invalid Tag", + data, lendata, pos - 1)); + } + }//Si tout va bien, on a un nouvel élément + else + { + while(isNameChar(nextUTF8Char(pos, endData - pos)))//On cherche le tagName + { + if(pos > endData) + { + //ERREUR : NEVER-ENDING TAG + THROW(XMLParseException_New("Never-ending tag", + data, lendata, pos - 1)); + } + } + pos--; + + Element *elmt = XMLElement_New(tag, pos - tag); + APPEND(elmt); + curElement = elmt; + s = *pos; + + while(pos < endData)//On gère le contenu de l'élément (attributs) + { + if(s == '>') break;//Fin de l'élément + if(s == '/') //Élément auto-fermant + { + pos++; + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + + if(isNameStartChar(s))//Début d'attribut + { + const char *attrNamestart = pos; + while(isNameChar(nextUTF8Char(pos, endData - pos)) && pos < endData){}//On parcourt le nom d'attribut + pos--; + const char *attrNameEnd = pos; + s = *pos; + while(isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + if(s != '=') + { + XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart); + if(s == '>') break;//Fin de l'élément + else if (s == '/')//Élément auto-fermant + { + pos++; + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + else + { + //ERREUR : INVALID TAG + THROW(XMLParseException_New("Invalid tag", + data, lendata, pos - 1)); + } + } + + pos++; s = *pos; + + while(isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + char delimiter = s; + if(delimiter != '"' && delimiter != '\'') + { + //ERREUR : EXPECTED ATTRIBUTE DELIMITER + THROW(XMLParseException_New("Expected attribute delimiter", + data, lendata, pos - 1)); + } + pos++; + + char* delimiterPos = (char*)memchr(pos, delimiter, endData - pos); + + if(!delimiterPos) + { + THROW(XMLParseException_New("Never-ending attribute value", + data, lendata, pos - 1)); + } + + XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, + pos, delimiterPos - pos); + pos = delimiterPos; + + } + + pos++; s = *pos; + } + + } + pos++; + } + + return elements; + +} diff --git a/gb.xml/src/parser.h b/gb.xml/src/parser.h new file mode 100644 index 00000000..da95c25a --- /dev/null +++ b/gb.xml/src/parser.h @@ -0,0 +1,14 @@ +#ifndef PARSER_H +#define PARSER_H + +#include "main.h" +#include "utils.h" + +void GBparse(const char *data, const size_t lendata, GB_ARRAY *array, DocumentType docType); +Node** parse(char const *data, const size_t lendata, size_t *nodeCount, DocumentType docType); + +void GBparseXML(char const *data, const size_t lendata, GB_ARRAY *array); +Node** parseXML(char const *data, const size_t lendata, size_t *nodeCount); + + +#endif // PARSER_H diff --git a/gb.xml/src/reader.cpp b/gb.xml/src/reader.cpp new file mode 100644 index 00000000..87d2e006 --- /dev/null +++ b/gb.xml/src/reader.cpp @@ -0,0 +1,499 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "reader.h" + +#include "node.h" +#include "utils.h" +#include "element.h" +#include "document.h" +#include "textnode.h" + +#include +#include + +#define DELETE(_ob) if(_ob) {delete _ob; _ob = 0;} +#define FREE(_ob) if(_ob) {free(_ob); _ob = 0;} +#define UNREF(_ob) if(_ob) GB.Unref(POINTER(&(_ob))) +#define DESTROYPARENT(_ob) if(_ob) {XMLNode_DestroyParent(_ob); _ob = 0;} + +void Reader::ClearReader() +{ + //UNREF(foundNode); + //UNREF(curNode); + this->keepMemory = false; + this->pos = 0; + this->depth = -1; + this->inTag = false; + this->inTagName = false; + this->inAttr = false; + this->inAttrName = false; + this->inAttrVal = false; + this->inNewTag = false; + this->specialTagLevel = 0; + this->inEndTag = false; + this->inXMLProlog = false; + this->inCommentTag = false; + this->inCDATATag = false; + this->inComment = false; + this->inCDATA = false; + this->waitClosingElmt = false; + this->specialTagLevel = 0; + this->state = 0; + + if(curNode != foundNode) + { + DESTROYPARENT(curNode); + } + else + { + curNode = 0; + } + DESTROYPARENT(foundNode); + curElmt = 0; + storedDocument = 0; + FREE(attrName); + lenAttrName = 0; + FREE(attrVal); + lenAttrVal = 0; + FREE(content); + lenContent = 0; + + if(storedElements) + { + + /*for(vector::iterator it = storedElements->begin(); it != storedElements->end(); ++it) + { + GB.Unref(POINTER(&(*it))); + } + this->storedElements->clear();*/ + } + + curAttrEnum = 0; + +} + +void Reader::InitReader() +{ + attrName = 0; + attrVal = 0; + content = 0; + storedDocument = 0; + storedElements = 0; + curNode = 0; + foundNode = 0; + + ClearReader(); + + this->flags[NODE_ELEMENT] = true; + this->flags[NODE_TEXT] = true; + this->flags[NODE_COMMENT] = true; + this->flags[NODE_CDATA] = true; + this->flags[NODE_ATTRIBUTE] = false; + this->flags[READ_ATTRIBUTE] = false; + + this->flags[READ_END_CUR_ELEMENT] = true; + this->flags[READ_ERR_EOF] = true; + FREE(storedElements); + +} + +void Reader::DestroyReader() +{ + ClearReader(); +} + +int Reader::ReadChar(char car) +{ + #define APPEND(elmt) if(curElmt == 0){}\ + else {XMLNode_appendChild(curElmt, elmt);} + + ++(this->pos); + + if(waitClosingElmt) + { + if(car != '>') return 0; + waitClosingElmt = false; + depth--; +// this->state = READ_END_CUR_ELEMENT; + return 0; + } + + if(car == '<' && !inComment && !inCDATA)//Début de tag + { + if(inTag)//Si on est déjà dans un tag + { + throw XMLParseException_New("Invalid tag Name", pos); + } + inNewTag = true; + inTagName = true; + if(curNode && curNode->type == Node::NodeText) //Si il y avait du texte avant + { + DESTROYPARENT(foundNode); + foundNode = curNode; + if(keepMemory) + { + APPEND(foundNode); + } + //const char *trimmedText = curNode->toTextNode()->content; + //size_t lenTrimmedText = curNode->toTextNode()->lenContent; + + //Trim(trimmedText, lenTrimmedText); + + XMLTextNode_TrimContent((TextNode*)curNode); + + curNode = 0; + this->state = NODE_TEXT; + return NODE_TEXT; + } + } + else if(car == '>' && inTag && !inEndTag && !inComment && !inCDATA)//Fin de tag (de nouvel élément) + { + DESTROYPARENT(foundNode); + //UNREF(foundNode); + foundNode = curNode;//On a trouvé un élément complet + //curNode = 0; + //GB.Ref(foundNode); + inTag = false; + depth++; + if(keepMemory) + { + APPEND(foundNode); + curElmt = ((Element*)foundNode); + } + if(attrName && attrVal) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, + attrVal, lenAttrVal); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + FREE(attrVal); lenAttrVal = 0; inAttrVal = false; + } + else if(attrName) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, "", 0); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + } + this->state = NODE_ELEMENT; + return NODE_ELEMENT; + } + else if(isWhiteSpace(car) && inTag && inTagName && !inComment && !inCDATA)// Fin de tagName + { + inTagName = false; + XMLElement_RefreshPrefix((Element*)curNode); + } + else if(isNameStartChar(car) && inTag && !inTagName && !inEndTag && !inAttrVal && !inAttrName && !inComment && !inCDATA)//Début de nom d'attribut + { + if(attrName && attrVal) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, + attrVal, lenAttrVal); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + FREE(attrVal); lenAttrVal = 0; inAttrVal = false; + } + else if(attrName) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, "", 0); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + } + inAttr = true; + inAttrName = true; + attrName = (char*)malloc(1); + *attrName = car; + lenAttrName = 1; + } + else if(car == '=' && inAttrName && !inComment && !inCDATA)//Fin du nom d'attribut + { + inAttrName = false; + } + else if((car == '\'' || car == '"') && inAttr && !inAttrVal && !inComment && !inCDATA)//Début de valeur d'attribut + { + inAttrVal = true; + attrVal = 0; + } + else if((car == '\'' || car == '"') && inAttr && inAttrVal && !inComment && !inCDATA)//Fin de valeur d'attribut + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, + attrVal, lenAttrVal); + FREE(attrName); lenAttrName = 0; + FREE(attrVal); lenAttrVal = 0; + inAttr = false; + inAttrVal = false; + this->state = READ_ATTRIBUTE; + return READ_ATTRIBUTE; + } + else if(car == '/' && inTag && !inAttrVal && !inComment && !inCDATA)//Self-closed element + { + inTag = false; + inTagName = false; + inEndTag = false; + if(curElmt) curElmt = (Element*)(curElmt->parent); + FREE(content); lenContent = 0; + //depth--; + waitClosingElmt = true; + DESTROYPARENT(foundNode); + foundNode = curNode; + XMLElement_RefreshPrefix((Element*)curNode); + this->state = NODE_ELEMENT; + depth++; + return NODE_ELEMENT; + } + else if(car == '/' && inNewTag && !inComment && !inCDATA)//C'est un tag de fin + { + inEndTag = true; + inNewTag = false; + inTag = true; + } + else if(car == '>' && inEndTag && !inComment && !inCDATA)//La fin d'un tag de fin + { + inTag = false; + inEndTag = false; + if(curElmt && lenContent == curElmt->lenTagName) + { + if(memcmp(curElmt->tagName, content, lenContent)) + curElmt = (Element*)(curElmt->parent); + } + FREE(content); lenContent = 0; + depth--; + this->state = READ_END_CUR_ELEMENT; + return READ_END_CUR_ELEMENT; + } + else if(inEndTag)//Tag de fin + { + if(!content) + { + content = (char*)malloc(1); + content[0] = car; + lenContent = 1; + } + else + { + content = (char*)realloc(content, lenContent + 1); + content[lenContent] = car; + ++lenContent; + } + + } + else if(inNewTag && car == '!' )//Premier caractère de commentaire + { + specialTagLevel = COMMENT_TAG_STARTCHAR_1; + inCommentTag = true; + inNewTag = false; + inTag = false; + } + //Caractère de début de CDATA + else if(inCommentTag && car == '[' && specialTagLevel == COMMENT_TAG_STARTCHAR_1) + { + specialTagLevel = CDATA_TAG_STARTCHAR_2; + inCommentTag = false; + inCDATATag = true; + } + //Caractère de CDATA + else if(inCDATATag && specialTagLevel >= CDATA_TAG_STARTCHAR_2 && specialTagLevel < CDATA_TAG_STARTCHAR_8 + && (car == '[' || car == 'C' || car == 'D' || car == 'A' || car == 'T')) + { + ++specialTagLevel; + if(specialTagLevel == CDATA_TAG_STARTCHAR_8) + { + inCDATATag = false; + inCDATA = true; + curNode = XMLCDATA_New(); + } + } + //Caractère "]" de fin de CDATA + else if(curNode && curNode->type == Node::CDATA && car == ']') + { + ++specialTagLevel; + if(specialTagLevel > CDATA_TAG_ENDCHAR_2)//On est allés un peu trop loin, il y a des ] en trop + { + --specialTagLevel; + char *&textContent = ((TextNode*)curNode)->content; + size_t &lenTextContent = ((TextNode*)curNode)->lenContent; + textContent = (char*)realloc(textContent, lenTextContent + 1); + textContent[lenTextContent] = car; + ++lenTextContent; + } + } + //Fin du CDATA + else if(curNode && curNode->type == Node::CDATA && car == '>' && specialTagLevel == CDATA_TAG_ENDCHAR_2) + { + specialTagLevel = 0; + inTag = false; + DESTROYPARENT(foundNode); + //UNREF(foundNode); + foundNode = curNode; + inCDATA = false; + if(keepMemory) + { + APPEND(foundNode); + } + curNode = 0; + this->state = NODE_CDATA; + return NODE_CDATA; + } + //Caractère "-" de début de commentaire + else if(inCommentTag && car == '-' && specialTagLevel >= COMMENT_TAG_STARTCHAR_1 && specialTagLevel < COMMENT_TAG_STARTCHAR_3 && !inComment && !inCDATA) + { + ++specialTagLevel; + if (specialTagLevel == COMMENT_TAG_STARTCHAR_3)//Le tag +#define COMMENT_TAG_STARTCHAR_1 1 //Caractère ! +#define COMMENT_TAG_STARTCHAR_2 2 //Caractère - +#define COMMENT_TAG_STARTCHAR_3 3 //Caractère - (2e) +#define COMMENT_TAG_ENDCHAR_1 4 //Caractère - +#define COMMENT_TAG_ENDCHAR_2 5 //Caractère - (2e) + + +//Prologue +#define PROLOG_TAG_ENDCHAR 6//Caractère ? + +//CDATA +#define CDATA_TAG_STARTCHAR_2 7 //Catactère [ +#define CDATA_TAG_STARTCHAR_3 8 //Catactère [ (2e) +#define CDATA_TAG_STARTCHAR_4 9 //Catactère C +#define CDATA_TAG_STARTCHAR_5 10 //Catactère D +#define CDATA_TAG_STARTCHAR_6 11 //Catactère A +#define CDATA_TAG_STARTCHAR_7 12 //Catactère T +#define CDATA_TAG_STARTCHAR_8 13 //Catactère A +#define CDATA_TAG_ENDCHAR_1 14 //Catactère ] +#define CDATA_TAG_ENDCHAR_2 15 //Catactère ] (2e) + +class Document; +class Node; +class Element; +class Attribute; + +class Reader +{ +public : + + Reader(){InitReader();} + ~Reader(){ClearReader();} + + Document *storedDocument;//Document stocké + Node *curNode;//Nœud en cours de lecture + Node *foundNode;//Noœud dont on vient de finir la lecture + Element *curElmt;//Nœud en cours de lecture + bool keepMemory;//Si on doit garder toute l'arborescence en mémoire + int pos;//Position dans le flux + bool inTag;//Si on est en train de lire un tag incomplet + bool inAttr;//Si on est en train de lire un attribut + bool inAttrName;//Si on est en train de lire un nom d'attribut + bool inAttrVal;//Si on est en train de lire une valeur d'attribut + bool inEndTag;//Si on est dans un tag de fin + bool inNewTag;//Si on est dans un nouveau tag + bool inTagName;//Si on est dans le nom d'un tag + bool waitClosingElmt;//Si on attend la fermeture d'un tag (auto-fermant) + bool inCommentTag;//Si on est dans un tag commentaire + bool inCDATATag;//Si on est dans un tag de CDATA + bool inXMLProlog;//Si on est dans un prologue xml + unsigned char specialTagLevel;//Niveau de lecture d'un tag spécial + bool inComment;//Si on est dans un commentaire + bool inCDATA;//Si on est dans un CDATA + int depth;//Profondeur du nœud courant + char *attrName;//Nom de l'attribut en cours de lecture + size_t lenAttrName; + char *attrVal;//Valeur de l'attribut en cours de lecture + size_t lenAttrVal; + char *content;//Contenu du nœud texte en cours de lecture + size_t lenContent; + + char state; + + Node* *storedElements; + size_t lenStoredElements; + + void InitReader();//Intitialise le lecteur + int ReadChar(char car);//Lit un caractère + void ClearReader();//Réinitialise le lecteur + void DestroyReader();//Détruit le lecteur + bool flags[FLAGS_COUNT];//Flags de lecture + + Attribute *curAttrEnum; +}; + +#endif diff --git a/gb.xml/src/rpc/Makefile.am b/gb.xml/src/rpc/Makefile.am new file mode 100644 index 00000000..466d8101 --- /dev/null +++ b/gb.xml/src/rpc/Makefile.am @@ -0,0 +1,2 @@ +COMPONENT = gb.xml.rpc +include $(top_srcdir)/component.am diff --git a/gb.xml/src/rpc/gb.xml.rpc.component b/gb.xml/src/rpc/gb.xml.rpc.component new file mode 120000 index 00000000..1331526c --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc.component @@ -0,0 +1 @@ +gb.xml.rpc/.component \ No newline at end of file diff --git a/gb.xml/src/rpc/gb.xml.rpc/.component b/gb.xml/src/rpc/gb.xml.rpc/.component new file mode 100644 index 00000000..4e44f5a8 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.xml.rpc +Version=3.10.90 +Needs=XML diff --git a/gb.xml/src/rpc/gb.xml.rpc/.directory b/gb.xml/src/rpc/gb.xml.rpc/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.xml/src/rpc/gb.xml.rpc/.icon.png b/gb.xml/src/rpc/gb.xml.rpc/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 7 Then + Error.Raise("Invalid data type") + Return + End If + + Values.Add(Null) + dTypes.Add(dataType) + Try Me[Values.Count - 1] = Value + If Error Then + sErr = Error.Text + Values.Pop() + dTypes.Pop() + Error.Raise(sErr) + End If + +End + +Public Function Pop() As Variant + + If Values.Count = 0 Then + Error.Raise("Bad argument") + Return Null + End If + + dTypes.Pop() + Return Values.Pop() + +End + +Public Sub Push(Value As Variant, dataType As Integer) + + Add(Value, dataType) + +End + + + +Public Sub Clear() + + Values.Clear() + dTypes.Clear() + +End + +Public Function Copy() As RpcArray + + Dim hRes As New RpcArray + Dim Bucle As Integer + + For Bucle = 0 To Values.Count - 1 + hRes.Add(Values[Bucle], dTypes[Bucle]) + Next + + Return hRes + +End + + + +Public Sub Reverse() + + Values.Reverse() + dTypes.Reverse() + +End + + +Public Sub Remove(Index As Integer) + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return + End If + + Values.Remove(Index) + dTypes.Remove(Index) + +End + +Public Function Datatype(Index As Integer) As Integer + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return dTypes[Index] + +End + +Public Sub _New() + + Values = New Variant[] + dTypes = New Integer[] + +End + +Public Function _Get(Index As Integer) As Variant + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return Null + End If + + Return Values[Index] + +End + +Public Sub _Put(Data As Variant, Index As Integer) + + Dim pSt As RpcStruct + Dim pAr As RpcArray + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return + End If + + Select Case dTypes[Index] + + Case XmlRpc.xInteger + Try Values[Index] = CInt(Data) + If Error Then + Error.Raise("Bad integer value") + Return + End If + + Case XmlRpc.xBoolean + Try Values[Index] = CBool(Data) + If Error Then + Error.Raise("Bad boolean value") + Return + End If + + Case XmlRpc.xDouble + Try Values[Index] = CFloat(Data) + If Error Then + Error.Raise("Bad float value") + Return + End If + + Case XmlRpc.xString + Try Values[Index] = CStr(Data) + If Error Then + Error.Raise("Bad string value") + Return + End If + + Case XmlRpc.xBase64 + Try Values[Index] = CStr(Data) + If Error Then + Error.Raise("Bad string value") + Return + End If + + Case XmlRpc.xDate + Try Values[Index] = CDate(Data) + If Error Then + Error.Raise("Bad date value") + Return + End If + + Case XmlRpc.xStruct + If Data = Null Then + Error.Raise("Bad RpcStruct value") + Return + End If + Try pSt = Data + If Error Then + Error.Raise("Bad RpcStruct value") + Return + End If + Values[Index] = PSt + + Case XmlRpc.xArray + If Data = Null Then + Error.Raise("Bad RpcArray value") + Return + End If + Try pAr = Data + If Error Then + Error.Raise("Bad RpcArray value") + Return + End If + Values[Index] = pAr + + End Select + +End + + + + + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class new file mode 100644 index 00000000..9e3be261 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class @@ -0,0 +1,29 @@ +'*************************************************************************** +' +' RpcAtom.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +PUBLIC Type AS Integer +PUBLIC Data AS Variant diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class new file mode 100644 index 00000000..6f09df2e --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class @@ -0,0 +1,372 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcClient.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private Method As RpcFunction +Private hHttp As HPost +Private hMode As Integer +Private sUrl As String +Private isFault As Boolean + +Public Const offLine As Integer = 0 +Public Const httpSync As Integer = 1 +Public Const httpAsync As Integer = 2 + +Property Mode As Integer +Property URL As String +Property Read RpcMethod As RpcFunction + +Event Reply(Data As Variant) +Event BadReply(Code As String) + + +Sub URL_Write(Vl As String) + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + If hHttp Then hHttp = Null + sURL = Trim(Vl) + +End + +Function URL_Read() As String + + Return sURL + +End + + + +Sub Mode_Write(Vl As Integer) + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + If vl >= 0 And vl < 3 Then + hMode = vl + End If + + If vl = 0 Then hHttp = Null + +End + +Function Mode_Read() As Integer + + Return hMode + +End + +Function RpcMethod_Read() As RpcFunction + + Return Method + +End + + +Public Sub _New(remoteFunction As RpcFunction) + + hMode = RpcClient.httpSync + If remoteFunction = Null Then Error.Raise("Invalid RpcFunction object") + Method = remoteFunction + +End + +Private Function testXML(sCad As String) As Integer + + Dim Xml As New XmlReader + + Try Xml.FromString(sCad) + If Error Then Return 1 + + Do While Not Xml.Eof + Try Xml.Read() + + If Error Then Return 1 + + + If Not Xml.Eof Then + If Xml.Node.Type = XmlReaderNodeType.Element Then + Select Case Xml.Node.Name + Case "methodResponse" + Case "fault" + Case "params" + Case "param" + Case "i4" + Case "int" + Case "boolean" + Case "string" + Case "double" + Case "dateTime.iso8601" + Case "base64" + Case "struct" + Case "array" + Case "data" + Case "member" + Case "value" + Case "name" + Default + Return 2 + End Select + End If + End If + + Loop + + Return 0 + +End + +Public Function EvalReply(sCad As String) As Variant + + Dim hAtom As RpcAtom + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + Try hAtom = extractReply(sCad) + If Error Then + Error.Raise(Error.Text) + Return Null + End If + + If isFault Then + '//TODO ERROR + Error.Raise("server") + Return Null + End If + + If hAtom = Null Then + If Method.ReturnType = Null Then Return Null + Error.Raise("Invalid return type, wanted VOID, got " & XmlRpc.ToString(hAtom.Type)) + End If + + If Method.ReturnType <> hAtom.Type Then + Error.Raise("Invalid return type, wanted " & XmlRpc.ToString(Method.ReturnType) & ", got " & XmlRpc.ToString(hAtom.Type)) + Return + End If + + Return hAtom.Data + +End + + +Private Function extractReply(sCad As String) As RpcAtom + + Dim Xml As New XmlReader + Dim hAtom As RpcAtom + + isFault = False + Select Case testXML(sCad) + Case 1 + Error.Raise("Invalid XML data") + Return Null + Case 2 + Error.Raise("Invalid XML-RPC format") + Return Null + End Select + + Xml.FromString(sCad) + If Tools.Find(Xml, "methodResponse") = False Then + Error.Raise("Invalid XML-RPC format") + Return Null + End If + + Do While True + If Not Xml.Eof Then Xml.Read() + If Xml.Eof Then Break + If Xml.Node.Type = XmlReaderNodeType.Element Then + + Select Case Xml.Node.Name + Case "params" + Do While True + Xml.Read() + Select Case Xml.Node.Type + Case XmlReaderNodeType.EndElement + Return Null + Case XmlReaderNodeType.Element + If Xml.Node.Name = "param" Then + Xml.Read() + hAtom = Tools.GetParam(Xml) + If hAtom = Null Then + Error.Raise("Invalid XML-RPC format") + Return Null + End If + Return hAtom + Else + Error.Raise("Invalid XML-RPC format") + Return Null + End If + End Select + + Loop + + Case "fault" + isFault = True + Default + Break + End Select + + End If + Loop + + Error.Raise("Invalid XML-RPC format") + Return Null + +End + +Public Sub hHttp_GotData(Data As String) + + Dim Dt As Variant + + If Last = Null Then + Error.Raise("Invalid method call") + Return + End If + + If Me.Mode = RpcClient.httpAsync Then + Try Dt = EvalReply(Data) + If Error Then + Raise BadReply(Error.Text) + Return + End If + Raise Reply(Dt) + + End If + +End + +Public Sub hHttp_GotError() + + If Last = Null Then + Error.Raise("Invalid method call") + Return + End If + + If Me.Mode = RpcClient.httpAsync Then Raise BadReply("Unable to contact server, or bad reply from server") + +End + + +Public Function Call(Data As Variant[]) As Variant + + Dim Xml As New XmlWriter + Dim Bucle As Integer + Dim sCad As String + Dim sData As String + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + If Method = Null Then + Error.Raise("Invalid RpcFunction object") + Return + End If + + If Data = Null Then + If Method.Count <> 0 Then + Error.Raise("Wrong parameters number") + Return + End If + Else + If Method.Count <> Data.Count Then + Error.Raise("Wrong parameters number") + Return + End If + End If + + Xml.Open("") + Xml.StartElement("methodCall") + Xml.Element("methodName", Method.MethodName) + Xml.StartElement("params") + For Bucle = 0 To Method.Count - 1 + + Xml.StartElement("param") + If Not Tools.AddValue(Xml, Data[Bucle], Method[Bucle]) Then + Xml.EndDocument() + Error.Raise("Invalid parameter " & Bucle + 1 & " Type : " & XmlRpc.ToString(Method[Bucle])) + Return + End If + Xml.EndElement() + + Next + Xml.EndElement() + + If hMode = RpcClient.offline Then Return Xml.EndDocument() + + If sURL = "" Then + Error.Raise("Invalid URL") + Return + End If + + If hHttp = Null Then hHttp = New HPost(sURL) As "hHttp" + + If hMode = RpcClient.httpSync Then + hHttp.Mode = False + Else + hHttp.Mode = True + End If + + sData = Xml.EndDocument() + + If Right(sData) <> "\n" Then sData &= "\n" + + sData = Replace(sData, "\n", "\r\n") + + sCad = hHttp.PostData(sData) + + If hMode = RpcClient.httpSync Then + If sCad = "" Then + Error.Raise("Unable to contact server, or bad reply from server") + Return Null + End If + Return EvalReply(sCad) + End If + + Return Null + +End + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class new file mode 100644 index 00000000..49fc9eb4 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class @@ -0,0 +1,125 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcFunction.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private dTypes As Integer[] +Private rType As Integer +Private mName As String +Private Hlp As String + +Property Read MethodName As String +Property Read Count As Integer +Property Read ReturnType As Integer +Property Help As String + +Function Help_read() As String + + Return Hlp + +End + +Sub Help_Write(sCad As String) + + Hlp = sCad + +End + + + +Function MethodName_Read() As String + + Return mName + +End + +Function Count_Read() As Integer + + Return dTypes.Count + +End + + +Public Function _get(Index As Integer) As Integer + + Return dTypes[Index] + +End + + + +Function ReturnType_Read() As Integer + + Return rType + +End + + + +Public Sub _New(Name As String, DataTypes As Integer[], Ret As Integer) + + Dim Ok As Boolean + Dim Bucle As Integer + + Ok = True + + If DataTypes <> Null Then + + For Bucle = 0 To Datatypes.Count - 1 + + If Datatypes[Bucle] < 0 Or Datatypes[Bucle] > 7 Then + Ok = False + Break + End If + + Next + + End If + + If Ret < 0 Or Ret > 8 Then Ok = False + + If Not Ok Then + + Error.Raise("Invalid data types") + + Else + + If DataTypes <> Null Then + dTypes = DataTypes.Copy() + Else + dTypes = New Integer[] + End If + + rType = Ret + mName = Name + + End If + + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class new file mode 100644 index 00000000..e31fdc9e --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class @@ -0,0 +1,357 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcServer.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private Methods As Object[] +Private miniSrv As MiniServer +Private CurrIndex As Integer + +Property Read Count As Integer +Property Read Listening As Boolean + +Event RemoteCall(Method As String, Params As Variant[]) + +Function Count_Read() As Integer + + Return Methods.Count() + +End + +Function Listening_Read() As Boolean + + If miniSrv Then Return True + Return False + +End + + +Public Sub Unregister(methodName As String) + + Dim Bucle As Integer + + If CurrIndex <> - 1 Then + Error.Raise("Unable to Unregister a function in a RemoteCall event") + Return + End If + + For Bucle = 0 To Methods.Count - 1 + + If Methods[Bucle].MethodName = MethodName Then + + Methods[Bucle] = Null + Methods.Remove(Bucle) + Return + + End If + + Next + +End + + +Public Sub Register(remoteFunction As RpcFunction) + + Dim Bucle As Integer + + If remoteFunction.MethodName = "system.listMethods" Then + Error.Raise("system.listMethods is a reserved keyword") + Return + End If + + If remoteFunction.MethodName = "system.methodSignature" Then + Error.Raise("system.methodSignature is a reserved keyword") + Return + End If + + If remoteFunction.MethodName = "system.methodHelp" Then + Error.Raise("system.methodHelp is a reserved keyword") + Return + End If + + If remoteFunction = Null Then + Error.Raise("Null function") + Return + End If + + For Bucle = 0 To Methods.Count - 1 + + If Methods[Bucle] = remoteFunction Then + Error.Raise("Function already registered") + Return + End If + + If Methods[Bucle].methodName = remoteFunction.methodName Then + Error.Raise("Function already registered") + Return + End If + + Next + + Methods.Add(remoteFunction) + + + +End + +Public Sub Listen(Port As Integer, MaxConn As Integer) + + If Port < 1 Or Port > 65535 Then + Error.Raise("Invalid port number") + Return + End If + + If MaxConn < 0 Then + Error.Raise("Invalid maximum connections number") + Return + End If + + If miniSrv Then miniSrv = Null + miniSrv = New MiniServer As "miniSrv" + Try miniSrv.Listen(Port, MaxConn) + If Error Then + miniSrv = Null + Error.Raise("Unable to listen at port " & Port) + End If + +End + +Public Sub Stop() + + If miniSrv Then + miniSrv.Close() + miniSrv = Null + End If + +End + + +Public Sub SetReply(Data As Variant) + + Dim Xml As New XmlWriter + + If CurrIndex = - 1 Then + Error.Raise("No remote function available") + Return + End If + + Xml.Open("", True) + Xml.StartElement("methodResponse") + Xml.StartElement("params") + Xml.StartElement("param") + If Not Tools.AddValue(Xml, Data, Methods[CurrIndex].ReturnType) Then + miniSrv.SetReply(tools.FaultReply(8, "internal server error")) + Error.Raise("Invalid type conversion") + Return + End If + + miniSrv.SetReply(Xml.EndDocument()) + + + + +End + + +Public Sub miniSrv_ProcessData(Data As String) + + Dim Xml As New XmlReader + Dim hPar As New Variant[] + Dim hP As RpcAtom + Dim Counter As Integer + + Xml = New XmlReader + Xml.FromString(Data) + + tools.Find(Xml, "methodCall") + Try Xml.Read() + If Not tools.Find(Xml, "params") Then + miniSrv.SetReply(tools.FaultReply(4, "params field not found")) + CurrIndex = - 1 + Return + End If + + Try Xml.Read() + + Do While True + + 'If Xml.Node.Type = XmlReaderNodeType.EndElement Then Break + If Not Tools.Find(Xml, "param") Then Break + hP = Tools.GetParam(Xml) + If hP = Null Then + miniSrv.SetReply(tools.FaultReply(5, "malformed parameters")) + CurrIndex = - 1 + Return + End If + + hPar.Add(hP.Data) + If Methods[CurrIndex].Count < hPar.Count Then + miniSrv.SetReply(tools.FaultReply(6, "too many parameters")) + CurrIndex = - 1 + Return + End If + If hP.Type <> Methods[CurrIndex][Counter] Then + miniSrv.SetReply(tools.FaultReply(7, "wrong parameter type")) + CurrIndex = - 1 + Return + End If + + Counter = Counter + 1 + + Loop + + miniSrv.SetReply(tools.FaultReply(9, "Unknown error")) + + If methods[CurrIndex].MethodName = "system.listMethods" Then + System_ListMethods() + Else If methods[CurrIndex].MethodName = "system.methodHelp" Then + System_methodHelp(hPar[0]) + Else If methods[CurrIndex].MethodName = "system.methodSignature" Then + System_methodSignature(hPar[0]) + Else + Raise RemoteCall(Methods[CurrIndex].MethodName, hPar) + End If + + CurrIndex = - 1 +End + + +Public Sub miniSrv_GotData(Data As String) + + Dim Xml As New XmlReader + Dim Bucle As Integer + + Xml = New XmlReader + Xml.FromString(Data) + + If Not tools.Find(Xml, "methodCall") Then + Stop Event + Return + End If + Try Xml.Read() + If Not tools.Find(Xml, "methodName") Then + Stop Event + Return + End If + Do While True + Try Xml.Read() + If Xml.Node.Type = XmlReaderNodetype.Element Then + Stop Event + Return + End If + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + Stop Event + Return + End If + If Xml.Node.Type = XmlReaderNodeType.Text Then + + For Bucle = 0 To Methods.Count - 1 + If Methods[Bucle].methodName = Xml.Node.Value Then + CurrIndex = bucle + Return + End If + Next + + Stop Event + Return + + End If + Loop + +End + +Public Sub _New() + + Dim Rpc As RpcFunction + + CurrIndex = - 1 + Methods = New Object[] + + Rpc = New RpcFunction("system.listMethods", Null, XmlRpc.xArray) + Methods.Add(Rpc) + Rpc = New RpcFunction("system.methodHelp", [XmlRpc.xString], XmlRpc.xString) + Methods.Add(Rpc) + Rpc = New RpcFunction("system.methodSignature", [XmlRpc.xString], XmlRpc.xArray) + Methods.Add(Rpc) + + +End + +Private Sub System_ListMethods() + + Dim Arr As New RpcArray + Dim Bucle As Integer + + For Bucle = 0 To Methods.Count - 1 + Arr.Add(Methods[Bucle].MethodName, XmlRpc.xString) + Next + + SetReply(Arr) + +End + +Private Sub System_methodHelp(Data As String) + + Dim Bucle As Integer + + Data = Trim(Data) + + For Bucle = 0 To Methods.Count - 1 + If Methods[Bucle].MethodName = Data Then + SetReply(Methods[Bucle].Help) + Return + End If + Next + + miniSrv.SetReply(tools.FaultReply("2", "Unknown method")) + +End + +Private Sub System_methodSignature(Data As String) + + Dim Bucle As Integer + Dim B2 As Integer + Dim xArr As New RpcArray + + Data = Trim(Data) + + For Bucle = 0 To Methods.Count - 1 + If Methods[Bucle].MethodName = Data Then + xArr.Add(XmlRpc.ToString(Methods[Bucle].ReturnType), XmlRpc.xString) + For B2 = 0 To Methods[Bucle].Count - 1 + xArr.Add(XmlRpc.ToString(Methods[Bucle][B2]), XmlRpc.xString) + Next + SetReply(xArr) + Return + End If + Next + + miniSrv.SetReply(tools.FaultReply("2", "Unknown method")) + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class new file mode 100644 index 00000000..5d72d96c --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class @@ -0,0 +1,151 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcStruct.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private Names As String[] +Private Values As Variant[] +Private dTypes As Integer[] + +Property Read Count As Integer + +Function Count_Read() As Integer + + Return Names.Count + +End + + +Public Sub _New() + + Names = New String[] + Values = New Variant[] + dTypes = New Integer[] + +End + +Public Sub Add(Name As String, Value As Variant, dataType As Integer) + + If datatype < 0 Or datatype > 7 Then + Error.Raise("Invalid data type") + Return + End If + + Names.Add(Name) + Values.Add(Value) + dTypes.Add(dataType) + +End + +Public Sub Clear() + + Names.Clear() + Values.Clear() + dTypes.Clear() + +End + +Public Function Copy() As RpcStruct + + Dim hRes As New RpcStruct + Dim Bucle As Integer + + For Bucle = 0 To Names.Count - 1 + + hRes.Add(Names[Bucle], Values[Bucle], dTypes[Bucle]) + + Next + + Return hRes + +End + +Public Function Key(Index As Integer) As String + + If Index < 0 Or Index >= Names.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return Names[Index] + +End + + +Public Sub Reverse() + + Names.Reverse() + Values.Reverse() + dTypes.Reverse() + +End + + +Public Sub Remove(Index As String) + + Dim Ind As Integer + + Ind = Names.Find(Index) + If Ind = - 1 Then + Error.Raise("Invalid Index") + Return + End If + + Names.Remove(Ind) + Values.Remove(Ind) + dTypes.Remove(Ind) + +End + +Public Function Datatype(Index As Integer) As Integer + + If Index < 0 Or Index >= Names.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return dTypes[Index] + +End + + +Public Function Value(Index As Integer) As Variant + + If Index < 0 Or Index >= Names.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return Values[Index] + +End + + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class new file mode 100644 index 00000000..addeaf59 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class @@ -0,0 +1,101 @@ +'*************************************************************************** +' +' RpcType.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +EXPORT + +'*********************************************** +' Data types for XML-RPC calls +'*********************************************** +STATIC PUBLIC CONST xInteger AS Integer = 0 +STATIC PUBLIC CONST xBoolean AS Integer = 1 +STATIC PUBLIC CONST xDouble AS Integer = 2 +STATIC PUBLIC CONST xString AS Integer = 3 +STATIC PUBLIC CONST xBase64 AS Integer = 4 +STATIC PUBLIC CONST xDate AS Integer = 5 +STATIC PUBLIC CONST xStruct AS Integer = 6 +STATIC PUBLIC CONST xArray AS Integer = 7 +STATIC PUBLIC CONST xVoid AS Integer = 8 + +STATIC PUBLIC FUNCTION ToType(type AS String) AS Integer + + + SELECT CASE type + + CASE "int" + RETURN RpcType.xInteger + CASE "i4" + RETURN RpcType.xInteger + CASE "boolean" + RETURN RpcType.xBoolean + CASE "double" + RETURN RpcType.xDouble + CASE "string" + RETURN RpcType.xString + CASE "base64" + RETURN RpcType.xBase64 + CASE "dateTime.iso8601" + RETURN RpcType.xDate + CASE "struct" + RETURN RpcType.xStruct + CASE "array" + RETURN RpcType.xArray + + END SELECT + + RETURN -1 + +END + +STATIC PUBLIC FUNCTION ToString(type AS Integer) AS String + + SELECT CASE type + + CASE RpcType.xInteger + RETURN "int" + CASE RpcType.xBoolean + RETURN "boolean" + CASE RpcType.xDouble + RETURN "double" + CASE RpcType.xString + RETURN "string" + CASE RpcType.xBase64 + RETURN "base64" + CASE RpcType.xDate + RETURN "dateTime.iso8601" + CASE RpcType.xStruct + RETURN "struct" + CASE RpcType.xArray + RETURN "array" + + END SELECT + + RETURN "" + +END + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class new file mode 100644 index 00000000..ed19c296 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class @@ -0,0 +1,94 @@ +' Gambas class file + +' CXMLRPC.class +' Built-in XML-RPC server. + +Property HTTPPort As Integer +Property MaxConn As Integer + +Private iHTTPPort As Integer +Private iMaxConn As Integer +Private bXMLRPCDebug As Boolean + +Public hXMLRPC As RpcServer + +Public Function Connect() As Boolean + + Dim hRpcFunc As RpcFunction + + hXMLRPC = New RpcServer As "hXMLRPC" + + hRpcFunc = New RpcFunction("calendar.getplanning", Null, XmlRpc.xArray) + hRpcFunc.Help = ("set global variable param1 to param2.") + hXMLRPC.Register(hRpcFunc) + + Try hXMLRPC.Listen(9009, 10) + If Not hXMLRPC.Listening Then + Return False + Endif + Return True + +End + +' shutdown our xmlrpc server +Public Sub Disconnect() + + hXMLRPC.Stop() + +End + +Public Sub hXMLRPC_RemoteCall(sName As String, sData As Variant[]) + + Dim vValue As Variant + Dim aArray As New RpcArray + Dim sParam As String + Dim iParam As Integer + + 'Main.hXMLRPC.hXMLRPC.SetReply(aArray) + + Print "Get a RemoteCall for method " & sName + + Select Case sName + Case "calendar.getplanning" + hXMLRPC.SetReply(aArray) + Default + Return + End Select + +End + +Private Function HTTPPort_Read() As Integer + + Return iHTTPPort + +End + +Private Sub HTTPPort_Write(Value As Integer) + + iHTTPPort = Value + +End + +Private Function MaxConn_Read() As Integer + + Return iMaxConn + +End + +Private Sub MaxConn_Write(Value As Integer) + + iMaxConn = Value + +End + +Private Function XMLRPCDebug_Read() As Boolean + + Return bXMLRPCDebug + +End + +Private Sub XMLRPCDebug_Write(Value As Boolean) + + bXMLRPCDebug = Value + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module new file mode 100644 index 00000000..179d17f0 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module @@ -0,0 +1,63 @@ +' Gambas module file + +Private Srv As RpcServer + + + + +Private Sub GetStateName(Number As Integer) + + Srv.SetReply("Alabama") + +End + +Private Sub Ping() + + Srv.SetReply(Now()) + +End + + +Public Sub Srv_RemoteCall(Name As String, Data As Variant[]) + + Select Case Name + Case "pepe" + Print Data.Count + Case "examples.ping" + Ping() + Case "examples.getStateName" + GetStateName(Data[0]) + Default + Return + End Select + +End + + +' Public Sub Main() +' +' Dim Bucle As Integer +' Dim B2 As Integer +' Dim Rpc As RpcFunction +' Dim myFunc As RpcClient +' Dim mySig As RpcClient +' Dim Arr As New RpcArray +' Dim Arr2 As RpcArray +' Dim hVar As New Variant[] +' +' +' Rpc = New RpcFunction("queryTitle", [XmlRpc.xString], XmlRpc.xArray) +' mySig = New RpcClient(Rpc) +' mySig.URL = "http://127.0.0.1:9000" +' mySig.Mode = RpcClient.httpAsync +' +' End + +Public Sub Main() + + Dim hRpcServer As RpcServer + + hRpcServer = New RpcServer As "RpcServer" + hRpcServer.Listen(9009, 1) + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module new file mode 100644 index 00000000..8a2e7073 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module @@ -0,0 +1,61 @@ +' Gambas module file + +' Gambas module file + +Public hXMLRPC As CXMLRPC +Public tMainSleep As Timer + +Public Function GetPlanning() + + Dim fRpc As RpcFunction + Dim cRpc As RpcClient + Dim vVar As New Variant[] + Dim bOk As Boolean + + fRpc = New RpcFunction("calendar.getplanning", Null, XmlRpc.xArray) + cRpc = New RpcClient(fRpc) + cRpc.URL = "http://127.0.0.1:9009" + + Try bOk = cRpc.Call(Null) + If Error Then Print "Error posting" + If bOk Then + Print "Successful post" + Else + Print "Error: There was a problem setting" + Endif + +End + +'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +' gets triggered by sleep timer, triggers main loop +'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Public Sub tSleep_Timer() + + ' Check_for_Action() + Wait 0.01 +End + + +Public Sub Main() + + Dim X As Integer + + ' main loop sleep timer + tMainSleep = New Timer As "tSleep" + tMainSleep.Delay = 1000 + 'tMainSleep.Enabled = True + + hXMLRPC = New CXMLRPC + + ' start xmlrpc server + If hXMLRPC.Connect() Then + Print "XML-RPC listening on port 9009" + Else + Print "XML-RPC failed to start on port 9009" + End If + + For X = 1 To 100000 Step 1 + Print "XXX=" & X + GetPlanning() + Next +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module b/gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module new file mode 100644 index 00000000..ec16e7f7 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module @@ -0,0 +1,338 @@ +' Gambas module file + +Public Sub ExitNode(Xml As XmlReader) + + Do While True + + If Xml.Eof Then Return + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then ExitNode(Xml) + If Xml.Node.Type = XmlReaderNodetype.EndElement Then Return + + + Loop + +End + + +Public Function Find(Xml As XmlReader, Node As String) As Boolean + + Do While True + + If Xml.Eof Then Return False + + If Xml.Node.Type = XmlReaderNodeType.Element Then + + If Xml.Node.Name = Node Then Return True + ExitNode(Xml) + + End If + + If Not Xml.Eof Then Xml.Read() + + Loop + +End + +Public Function FindText(Xml As XmlReader) As Boolean + + Do While True + + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Text Then Return True + If Xml.Node.Type = XmlReaderNodeType.EndElement Then Return False + If Xml.Node.Type = XmlReaderNodeType.Element Then Return False + + Loop + +End + + +Public Function GetParam(Xml As XmlReader) As RpcAtom + + Dim hAtom As New RpcAtom + Dim Name As String + Dim hDate As Date + Dim sDate As String + Dim bAtom As RpcAtom + + If Not (Xml.Node.Type = XmlReaderNodetype.Element And Xml.Node.Name = "value") Then + Try Xml.Read() + End If + + If Not Find(Xml, "value") Then Return Null + + Do While True + Try Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + hAtom.Data = "" + hAtom.Type = XmlRpc.xString + Return hAtom + End If + If Xml.Node.Type = XmlReaderNodeType.Text Then + hAtom.Data = Xml.Node.Value + hAtom.Type = XmlRpc.xString + ExitNode(Xml) + Return hAtom + End If + If Xml.Node.Type = XmlReaderNodeType.Element Then Break + Loop + + Select Case Xml.Node.Name + + Case "i4" + hAtom.Type = XmlRpc.xInteger + If Not FindText(Xml) Then Return Null + Try hAtom.Data = CInt(Xml.Node.Value) + If Error Then Return Null + + Case "int" + hAtom.Type = XmlRpc.xInteger + If Not FindText(Xml) Then Return Null + Try hAtom.Data = CInt(Xml.Node.Value) + If Error Then Return Null + + Case "double" + hAtom.Type = XmlRpc.xDouble + If Not FindText(Xml) Then Return Null + Try hAtom.Data = CFloat(Xml.Node.Value) + If Error Then Return Null + + Case "boolean" + hAtom.Type = XmlRpc.xBoolean + If Not FindText(Xml) Then Return Null + If Trim(Xml.Node.Value) = "0" Then + hAtom.Data = False + Else If Trim(Xml.Node.Value) = "1" Then + hAtom.Data = True + Else + Return Null + End If + + Case "string" + hAtom.Type = XmlRpc.xString + If Not FindText(Xml) Then + hAtom.Data = "" + Else + hAtom.Data = Xml.Node.Value + End If + + Case "base64" + hAtom.Type = XmlRpc.xBase64 + If Not FindText(Xml) Then Return Null + Try hAtom.Data = XmlReader.Decode(Xml.Node.Value, "base64") + If Error Then Return Null + + Case "dateTime.iso8601" + hAtom.Type = XmlRpc.xDate + If Not FindText(Xml) Then Return Null + sDate = Trim(Xml.Node.Value) + If Len(sDate) = 8 Then + Try hDate = Date(Left(sDate, 4), Mid(sDate, 5, 2), Mid(sDate, 7, 2)) + If Error Then Return Null + hAtom.Data = hDate + Else If Len(sDate) = 17 Then + Try hDate = Date(Left(sDate, 4), Mid(sDate, 5, 2), Mid(sDate, 7, 2), Mid(sDate, 10, 2), Mid(sDate, 13, 2), Mid(sDate, 16, 2)) + If Error Then Return Null + hAtom.Data = hDate + Else + Return Null + End If + + Case "array" + hAtom.Type = XmlRpc.xArray + hAtom.Data = New RpcArray + Do While True + + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then + + If Xml.Node.Name <> "data" Then Return Null + Do While True + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then + If Xml.Node.Name <> "value" Then Return Null + bAtom = Tools.GetParam(Xml) + If bAtom = Null Then Return Null + hAtom.Data.Add(bAtom.Data, bAtom.Type) + End If + + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + + 'If Xml.Node.Name = "data" Then + If Xml.Eof Then + ExitNode(Xml) + Xml.Read() + Return hAtom + End If + + End If + Loop + End If + + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + ExitNode(Xml) + Xml.Read() + Return hAtom + End If + + Loop + + Case "struct" + hAtom.Type = XmlRpc.xStruct + hAtom.Data = New RpcStruct + Do While True + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then + + If Xml.Node.Name <> "member" Then Return Null + bAtom = Null + Name = "" + Do While True + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + If bAtom = Null Or Name = "" Then Return Null + hAtom.Data.Add(Name, bAtom.Data, bAtom.Type) + Break + End If + If Xml.Node.Type = XmlReaderNodeType.Element Then + Select Case Xml.Node.Name + Case "name" + If Not FindText(Xml) Then Return Null + Name = Xml.Node.Value + ExitNode(Xml) + Case "value" + bAtom = GetParam(Xml) + If Not bAtom Then Return Null + Default + Return Null + End Select + End If + Loop + + End If + + If Xml.Node.Type = XmlReaderNodetype.EndElement Then + + If Xml.Node.Name = "struct" Then + ExitNode(Xml) + Xml.Read() + Return hAtom + End If + + End If + + Loop + + Default + Return Null + End Select + + ExitNode(Xml) + Return hAtom + +End + +Public Function AddValue(Xml As XmlWriter, Data As Variant, Type As Integer) As Boolean + + Dim xBool As Boolean + Dim xBase64 As String + Dim xDate As Date + Dim xArr As RpcArray + Dim xStr As RpcStruct + Dim Bucle As Integer + + Xml.StartElement("value") + Select Case Type + Case XmlRpc.xInteger + Try Xml.Element("int", CInt(Data)) + If Error Then Return False + + Case XmlRpc.xBoolean + Try xBool = CBool(Data) + If Error Then Return False + If xBool Then + Xml.Element("boolean", "1") + Else + Xml.Element("boolean", "0") + End If + + Case XmlRpc.xDouble + Try Xml.Element("double", CFloat(Data)) + If Error Then Return False + + Case Xmlrpc.xString + Try Xml.Element("string", CStr(Data)) + If Error Then Return False + + Case XmlRpc.xBase64 + Try xBase64 = CStr(Data) + If Error Then Return False + Xml.StartElement("base64") + Try Xml.Base64(xBase64) + If Error Then Return False + Xml.EndElement() + + Case XmlRpc.xDate + Try xDate = CDate(Data) + If Error Then Return False + Xml.Element("dateTime.iso8601", Format(xDate, "yyyymmdd") & "T" & Format(xDate, "hh:nn:ss")) + + Case XmlRpc.xArray + Try xArr = Data + If Error Then Return False + Xml.StartElement("array") + Xml.StartElement("data") + For Bucle = 0 To xArr.Count - 1 + If Not AddValue(Xml, xArr[Bucle], xArr.Datatype(Bucle)) Then Return False + Next + Xml.EndElement() + Xml.EndElement() + + Case XmlRpc.xStruct + Try xStr = Data + If Error Then Return False + Xml.StartElement("struct") + For Bucle = 0 To xStr.Count - 1 + Xml.StartElement("member") + Xml.Element("name", xStr.Key(Bucle)) + If Not AddValue(Xml, xStr.Value(Bucle), xStr.Datatype(Bucle)) Then Return False + Xml.EndElement() + Next + Xml.EndElement() + + End Select + Xml.EndElement() + Return True + +End + + + +Public Function FaultReply(Code As Integer, Reason As String) As String + + Dim Xml As New XmlWriter + + Xml.Open("", True) + Xml.StartElement("methodResponse") + Xml.StartElement("fault") + Xml.StartElement("value") + Xml.StartElement("struct") + + Xml.StartElement("member") + Xml.Element("name", "faultCode") + Xml.StartElement("value") + Xml.Element("int", Code) + Xml.EndElement() + Xml.EndElement() + + Xml.StartElement("member") + Xml.Element("name", "faultString") + Xml.StartElement("value") + Xml.Element("string", Reason) + + Return Xml.EndDocument() + + +End + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class b/gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class new file mode 100644 index 00000000..8a1eb08f --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class @@ -0,0 +1,101 @@ +'*************************************************************************** +' +' XmlRpc.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +EXPORT + +'*********************************************** +' Data types for XML-RPC calls +'*********************************************** +STATIC PUBLIC CONST xInteger AS Integer = 0 +STATIC PUBLIC CONST xBoolean AS Integer = 1 +STATIC PUBLIC CONST xDouble AS Integer = 2 +STATIC PUBLIC CONST xString AS Integer = 3 +STATIC PUBLIC CONST xBase64 AS Integer = 4 +STATIC PUBLIC CONST xDate AS Integer = 5 +STATIC PUBLIC CONST xStruct AS Integer = 6 +STATIC PUBLIC CONST xArray AS Integer = 7 +STATIC PUBLIC CONST xVoid AS Integer = 8 + +STATIC PUBLIC FUNCTION ToType(type AS String) AS Integer + + + SELECT CASE type + + CASE "int" + RETURN XmlRpc.xInteger + CASE "i4" + RETURN XmlRpc.xInteger + CASE "boolean" + RETURN XmlRpc.xBoolean + CASE "double" + RETURN XmlRpc.xDouble + CASE "string" + RETURN XmlRpc.xString + CASE "base64" + RETURN XmlRpc.xBase64 + CASE "dateTime.iso8601" + RETURN XmlRpc.xDate + CASE "struct" + RETURN XmlRpc.xStruct + CASE "array" + RETURN XmlRpc.xArray + + END SELECT + + RETURN -1 + +END + +STATIC PUBLIC FUNCTION ToString(type AS Integer) AS String + + SELECT CASE type + + CASE XmlRpc.xInteger + RETURN "int" + CASE XmlRpc.xBoolean + RETURN "boolean" + CASE XmlRpc.xDouble + RETURN "double" + CASE XmlRpc.xString + RETURN "string" + CASE XmlRpc.xBase64 + RETURN "base64" + CASE XmlRpc.xDate + RETURN "dateTime.iso8601" + CASE XmlRpc.xStruct + RETURN "struct" + CASE XmlRpc.xArray + RETURN "array" + + END SELECT + + RETURN "" + +END + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class b/gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class new file mode 100644 index 00000000..01732d92 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class @@ -0,0 +1,94 @@ +' Gambas class file + +Public Http As HttpClient +Private Buffer As String +Public Mode As Boolean + +Event GotData(Data As String) + +Event GotError() + +Public Sub Http_Read() + + Dim sCad As String + + If Mode = False Then Return + + If Lof(Http) Then + Try Read #Http, sCad, Lof(Http) + If Not Error Then Buffer = Buffer & sCad + End If + +End + +Public Sub Http_Finished() + + Dim sCad As String + + If Mode = False Then Return + + If Lof(Http) Then + Try Read #Http, sCad, Lof(Http) + If Not Error Then Buffer = Buffer & sCad + End If + + Raise GotData(Buffer) + + +End + +Public Sub Http_Error() + + If Mode = False Then Return + + Try Close #Http + + Raise GotError() + +End + + + +Public Function PostData(Data As String) As String + + Dim sCad As String + + Buffer = "" + sCad = "" + + Http.Async = Mode + Http.Post("text/xml", Data) + + If Mode = False Then + + Do While Http.Status > 0 + + 'Wait 0.001 + Wait 0.01 + + Loop + + If Http.Status < 0 Then Return "" + If Http.Code <> 200 Then Return "" + + If Lof(Http) Then + + Read #Http, sCad, Lof(Http) + + End If + + sCad = Trim(sCad) + Return sCad + + End If + +End + + +Public Sub _new(sUrl As String) + + Http = New HttpClient As "Http" + + Http.URL = sUrl + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class b/gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class new file mode 100644 index 00000000..a31d7d98 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class @@ -0,0 +1,465 @@ +' Gambas class file + +'*************************************************************************** +' +' miniServer.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Private Http As ServerSocket +Private SockColl As Collection + +Private hReply As String + +Event GotData(Data As String) +Event ProcessData(Data As String) + +Private Sub hError(hS As Socket, hErr As String) + + hS.Begin + hS.EndOfLine = gb.Windows + Print #hS, "HTTP/1.1 " & hErr + Print #hS, "Server: Gambas XML-RPC Server" + Print #hS, "Connection: close" + Print #hS, "Content-Type: text/html; charset=iso-8859-1" + Print #hS, "" + Print #hS, "" + Print #hS, "" + Print #hS, "" & hErr & "" + Print #hS, "" + Print #hS, "

    Bad Request

    " + Print #hS, hErr & ".

    " + Print #hS, "


    " + Print #hS, "
    Gambas XML-RPC Server
    " + Print #hS, "" + Print #hS, "" + Print #hS, "" + + Try hS.Send + +Catch + ' Catch any errors with a closed stream + +End + + + +Private Sub RemoveSocket(hS As Socket) + + Dim cCol As Collection + + For Each cCol In SockColl + If hS = cCol["socket"] Then + Try Close #hS + SockColl.Remove(cCol["id"]) + Break + End If + + Next + +End + +Private Function FirstTest(Xml As XmlReader) As Boolean + + Do While Not Xml.Eof + + Try Xml.Read() + If Error Then Return False + If Xml.Eof Then Return True + If Xml.Node.Type = XmlReaderNodeType.Element Then + + Select Case Xml.Node.Name + + Case "methodCall" + Case "methodName" + Case "params" + Case "param" + Case "i4" + Case "int" + Case "boolean" + Case "string" + Case "double" + Case "dateTime.iso8601" + Case "base64" + Case "struct" + Case "array" + Case "data" + Case "member" + Case "value" + Case "name" + Default + Return False + + End Select + + End If + + Loop + + Return True + +End + + +Public Sub SetReply(sCad As String) + + hReply = sCad + +End + + +Private Sub ProcessQuery(hS As Socket, Buf As String) + + Dim hBuf As String[] + Dim Bucle As Integer + Dim Point As Integer + Dim Resul As String + Dim sReply As String + Dim hBol As Boolean + Dim Xml As XmlReader + + Point = - 1 + hBuf = Split(Buf, Chr(13)) + + For Bucle = 0 To hBuf.Count - 1 + hBuf[Bucle] = Replace(hBuf[Bucle], Chr(10), "") + Next + + For Bucle = 0 To hBuf.Count - 1 + If hBuf[Bucle] = "" Then + Point = Bucle + Break + End If + Next + + If Point = - 1 Then + hError(hS, "400 Bad Request") + RemoveSocket(hS) + Return + End If + + For Bucle = Point + 1 To hBuf.Count - 1 + Resul = Resul & hBuf[Bucle] + If Bucle < (hBuf.Count - 1) Then Resul = Resul & "\n" + Next + + Xml = New XmlReader + Try Xml.FromString(Trim(Resul)) + If Error Then + Xml = Null + hError(hS, "400 Bad Request") + RemoveSocket(Hs) + Return + End If + + sReply = "OK" + + If Not FirstTest(Xml) Then + sReply = Tools.FaultReply("1", "Malformed XML-RPC document") + Else + hBol = Raise GotData(Resul) + If hBol Then + sReply = Tools.FaultReply("2", "Unknown method") + Else + sReply = Tools.FaultReply("3", "Can not perform desired request") + hReply = "" + Raise ProcessData(Resul) + If hReply <> "" Then sReply = hReply + End If + End If + + hS.Begin + hS.EndOfLine = gb.Windows + Print #hS, "HTTP/1.1 200 OK" + Print #hS, "Connection: close" + Print #hS, "Content-Length: " & Len(sReply) + Print #hS, "Content-Type: text/xml" + Print #hS, "Server: Gambas RPC Server" + Print #hS, "" + Write #hS, sReply, Len(sReply) + Try hS.Send + +Finally + + RemoveSocket(hS) + +Catch + ' Catch any errors with a closed stream + +End + + +Public Sub Socket_Read() + + Dim Buf As String + Dim sCad As String + Dim Bucle As Integer + Dim hS As Socket + Dim cCount As Integer + Dim cLen As Integer + Dim cWait As Integer = 1 + Dim cCol As Collection + + For Each cCol In SockColl + If cCol["socket"] = Last Then + hS = Last + Break + Endif + Next + + If hS = Null Then Return + + If cCol["buffer"] = "" Then + If Lof(hS) >= 5 Then + + Try Read #hS, Buf, Lof(hS) + cCol["buffer"] &= Buf + If Left(cCol["buffer"], 5) <> "POST " Then + hError(hS, "405 Method Not Allowed") + RemoveSocket(hS) + Return + End If + Else + ' We expect at LEAST something like "POST /" + RemoveSocket(hS) + Return + End If + Else + Try Read #hS, Buf, Lof(hS) + cCol["buffer"] &= Buf + + ' Don't continue, because this is the second (or more) iteration to read the data + ' The first iteration will handle the complete request + Return + End If + + If cCol["protocol"] = - 1 Then + If InStr(cCol["buffer"], Chr(13)) > 0 Then + Buf = Trim(Left(cCol["buffer"], InStr(cCol["buffer"], Chr(13)) - 1)) + Buf = Right(Buf, 8) + If Buf = "HTTP/1.1" Then + cCol["protocol"] = 1 + Else If Buf = "HTTP/1.0" Then + cCol["protocol"] = 0 + Else + hError(hS, "505 HTTP Version Not Supported") + RemoveSocket(hS) + Return + End If + + Else + If Len(cCol["buffer"]) > 4096 Then + hError(hS, "413 Request Entity Too Large") + RemoveSocket(hS) + Return + End If + End If + End If + + ' It is possible the HTTP headers are send in separate TCP packets, we will wait a maximum of 1000msec + For cCount = cWait To 199 Step 1 + If hS = Null Then Return + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) Then Break + Wait 0.005 + Next + + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) Then + If cCol["type"] = 0 Then + Buf = Left(cCol["buffer"], InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10))) + + If InStr(UCase(Buf), "CONTENT-TYPE:") > 0 Then + sCad = Mid(Buf, InStr(UCase(Buf), "CONTENT-TYPE:") + 13) + sCad = Trim(Left(sCad, InStr(sCad, Chr(13)))) + If LCase(sCad) <> "text/xml" Then + hError(hS, "415 Unsupported Media Type") + RemoveSocket(hS) + Return + Else + cCol["type"] = 1 + End If + Else + hError(hS, "415 Unsupported Media Type") + RemoveSocket(hS) + Return + End If + + If InStr(UCase(Buf), "CONTENT-LENGTH:") > 0 Then + sCad = Mid(Buf, InStr(UCase(Buf), "CONTENT-LENGTH:") + 15) + sCad = Trim(Left(sCad, InStr(sCad, Chr(13)))) + Try cCol["length"] = CInt(sCad) + If Error Then + hError(hS, "411 Length Required") + RemoveSocket(hS) + Return + End If + Else + hError(hS, "411 Length Required") + RemoveSocket(hS) + Return + End If + + ' Check for "Expect: 100-continue" option & HTTP/1.1 + If cCol["protocol"] = 1 And InStr(UCase(Buf), "EXPECT: 100-CONTINUE") > 0 Then + ' Do a special check if no body is received - only then reply + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) + 3 = Len(cCol["buffer"]) Then + hS.Begin + hS.EndOfLine = gb.Windows + Print #hS, "HTTP/1.1 100 Continue" + Print #hS, "" + Try hS.Send + End If + End If + + End If + End If + + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) Then + + If cCol["length"] = - 1 Or cCol["type"] <> 1 Then + hError(hS, "406 Not Acceptable") + RemoveSocket(hS) + Return + End If + + For cCount = cWait To 200 Step 1 + If hS = Null Then Return + cLen = Len(cCol["buffer"]) - InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) - 3 + If cLen >= cCol["length"] Then Break + Wait 0.005 + Next + + ' HTTP/1.0 = looseless checking, some older clients generation wrong length + ' HTTP/1.1 = strict checking + If (cCol["protocol"] = 1 And cCol["length"] = cLen) Or (cCol["protocol"] = 0 And cCol["length"] <= cLen) Then + ProcessQuery(hS, cCol["buffer"]) + Return + Else + + If Len(cCol["buffer"]) > 4096 Then + hError(hS, "413 Request Entity Too Large") + RemoveSocket(hS) + Return + Else + ' If size doesn't match - we give an internal error + hError(hS, "500 Internal Server Error") + RemoveSocket(hS) + Return + End If + + End If + + Else + If Len(cCol["buffer"]) > 4096 Then + hError(hS, "413 Request Entity Too Large") + RemoveSocket(hS) + Return + End If + End If + + ' Always give a response + ' We wait long enough for the information = 1000msec + hError(hS, "408 Request Timeout") + RemoveSocket(hS) + +End + + +Public Sub Http_Connection(RemoteHost As String) + + Dim SockCollEntry As New Collection + Dim iId As Integer + + Randomize + Do + iId = Int(Rnd(1, 32768)) + Loop Until Not SockColl.Exist(iId) + + SockCollEntry.Add("", "buffer") + SockCollEntry.Add(Http.Accept(), "socket") + SockCollEntry.Add(-1, "protocol") + SockCollEntry.Add(-1, "length") + SockCollEntry.Add(0, "type") + SockCollEntry.Add(CStr(iId), "id") + + SockColl.Add(SockCollEntry, CStr(iId)) + +End + + +Public Sub Close() + + Dim cCol As Collection + + For Each cCol In SockColl + RemoveSocket(cCol["socket"]) + Next + + Try Http.Close() + Http = Null + +End + + +Public Sub Listen(Port As Integer, MaxConn As Integer) + + If Not Http Then + Http = New ServerSocket As "Http" + Else + If Http.Status <> 0 Then Http.Close() + End If + + Try Http.Port = Port + If Error Then + Error.Raise("Invalid TCP port") + Return + End If + + If MaxConn > 0 Then + Try Http.Listen(MaxConn) + Else + Try Http.Listen() + End If + + If Error Then Error.Raise("Unable to listen at port " & Port) + If Http.Status < 0 Then Error.Raise("Unable to listen at port " & Port) + + +End + + +Public Sub _New() + + SockColl = New Collection + +End + + +Public Sub _Free() + + Me.Close() + +End + + + diff --git a/gb.xml/src/serializer.cpp b/gb.xml/src/serializer.cpp new file mode 100644 index 00000000..0e598715 --- /dev/null +++ b/gb.xml/src/serializer.cpp @@ -0,0 +1,351 @@ +#include "serializer.h" + +#include "utils.h" +#include "node.h" +#include "textnode.h" + +#include "gbinterface.h" + +#include + +void serializeNode(Node *node, char *&output, size_t &len, int indent) +{ + Document *parentDocument = XMLNode_GetOwnerDocument(node); + if(parentDocument) + { + if(parentDocument->docType == HTMLDocumentType || parentDocument->docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + HTML.serializeHTMLNode(node, output, len, indent); + return; + } + else + { + //DEBUG << "WARNING : HTML Serializer not found" << endl; + } + } + } + + serializeXMLNode(node, output, len, indent); +} + +void GBserializeNode(Node *node, char *&output, size_t &len, int indent) +{ + Document *parentDocument = XMLNode_GetOwnerDocument(node); + if(parentDocument) + { + if(parentDocument->docType == HTMLDocumentType || parentDocument->docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + HTML.GBserializeHTMLNode(node, output, len, indent); + return; + } + else + { + //DEBUG << "WARNING : HTML Serializer not found" << endl; + } + } + } + + GBserializeXMLNode(node, output, len, indent); +} + + +/***** String output *****/ + +void addStringLen(Node *node, size_t &len, int indent = -1);//Calculates the node's string representation length, and adds it to len (recursive) +void addString(Node *node, char *&data, int indent = -1);//Puts the string represenetation into data, and increments it (recursive) + +void serializeXMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = (char*)malloc(sizeof(char) * (len)); + addString(node, output, indent); + output -= len; + +} + +void GBserializeXMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = GB.TempString(0, len); + addString(node, output, indent); + output -= len; +} + +void addStringLen(Node *node, size_t &len, int indent) +{ + switch (node->type) + { + case Node::DocumentNode: + len += 38 + (indent >= 0 ? 1 : 0);// root->addStringLen(len, indent); + //Content + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + '>' \n + // + children + (indent) '" \n + // Or, singlElement : + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + ' />' \n + /*if(((Element*)node)->isSingle()) + { + len += (4 + ((Element*)node)->lenTagName); + if(indent >= 0) len += indent + 1; + } + else + {*/ + len += (5 + ((((Element*)node)->lenTagName) * 2)); + if(indent >= 0) len += indent * 2 + 2; + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent + 1 : -1); + } + //} + + for(Attribute *attr = (Attribute*)(((Element*)node)->firstAttribute); attr != 0; attr = (Attribute*)(attr->nextNode)) + { + len += 4 + attr->lenAttrName + attr->lenAttrValue; + } + break; + case Node::NodeText: + + XMLTextNode_checkEscapedContent((TextNode*)node); + len += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) len += indent + 1; + break; + + case Node::Comment: + + XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenEscapedContent + 7; + if(indent >= 0) len += indent + 1; + break; + case Node::CDATA: + + XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenContent + 12; + if(indent) len += indent + 1; + break; + + default: + break; + } +} + + +#define ADD(_car) *data = _car; data++; +void addString(Node *node, char *&data, int indent) +{ + //bool single; + switch (node->type) + { + case Node::DocumentNode: + memcpy(data, "", 38); + data += 38; + if(indent >= 0) + { + *data = '\n'; + ++data; + } + //Content + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + //char *content = data; + //single = ((Element*)node)->isSingle(); + + //Opening tag + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + ADD('<'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + + //Attributes + for(Attribute *attr = (Attribute*)((Element*)node)->firstAttribute; attr != 0; attr = (Attribute*)(attr->nextNode)) + { + ADD(' '); + memcpy(data, attr->attrName, attr->lenAttrName); data += attr->lenAttrName; + + ADD('='); + ADD('"'); + memcpy(data, attr->attrValue, attr->lenAttrValue); data += attr->lenAttrValue; + ADD('"'); + } + + /*if(single) + { + ADD(CHAR_SPACE); + ADD(CHAR_SLASH); + }*/ + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + //if(!single) + { + + //Content + for(Node *child = ((Element*)node)->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent + 1 : -1); + } + + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + + //Ending Tag + ADD('<'); + ADD('/'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + } + break; + case Node::NodeText: + XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + + memcpy(data, ((TextNode*)node)->escapedContent, ((TextNode*)node)->lenEscapedContent); + data += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::Comment: + XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::CDATA: + XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "content, ((CDATANode*)node)->lenContent); + data += ((CDATANode*)node)->lenContent; + memcpy(data, "]]>", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + default: + break; + } +} + +/***** Text Content *****/ +void addTextContentLen(Node *node, size_t &len); +void addTextContent(Node *node, char *&data); + +void GetXMLTextContent(Node *node, char *&output, size_t &len) +{ + len = 0; + addTextContentLen(node, len); + output = (char*)malloc(sizeof(char) * (len)); + addTextContent(node, output); + output -= len; +} + +void GBGetXMLTextContent(Node *node, char *&output, size_t &len) +{ + len = 0; + addTextContentLen(node, len); + output = GB.TempString(0, len); + addTextContent(node, output); + output -= len; +} + +void addTextContentLen(Node *node, size_t &len) +{ + if(!node) return; + switch(node->type) + { + case Node::DocumentNode: + case Node::ElementNode: + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addTextContentLen(child, len); + } + break; + case Node::NodeText: + case Node::Comment: + case Node::CDATA: + XMLTextNode_checkContent((TextNode*)node); + len += ((TextNode*)node)->lenContent; + break; + case Node::AttributeNode: + len += ((Attribute*)node)->lenAttrValue; + break; + default: + break; + } +} + +void addTextContent(Node *node, char *&data) +{ + if(!node) return; + switch(node->type) + { + case Node::DocumentNode: + case Node::ElementNode: + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addTextContent(child, data); + } + break; + case Node::NodeText: + case Node::Comment: + case Node::CDATA: + memcpy(data, ((TextNode*)node)->content, ((TextNode*)node)->lenContent); + data += ((TextNode*)node)->lenContent; + break; + case Node::AttributeNode: + memcpy(data, ((Attribute*)node)->attrValue, ((Attribute*)node)->lenAttrValue); + data += ((Attribute*)node)->lenAttrValue; + break; + default: + break; + } +} diff --git a/gb.xml/src/serializer.h b/gb.xml/src/serializer.h new file mode 100644 index 00000000..37a52e56 --- /dev/null +++ b/gb.xml/src/serializer.h @@ -0,0 +1,18 @@ +#ifndef SERIALIZER_H +#define SERIALIZER_H + +#include "gb.xml.h" + +//Switches if HTML enabled +void serializeNode(Node *node, char *&output, size_t &len, int indent = -1); +void GBserializeNode(Node *node, char *&output, size_t &len, int indent = -1); + +//String output +void serializeXMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Converts the node to its string representation +void GBserializeXMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Same as above, but returns a Gambas String directly + +//Text content +void GetXMLTextContent(Node *node, char *&output, size_t &len); +void GBGetXMLTextContent(Node *node, char *&output, size_t &len); + +#endif // SERIALIZER_H diff --git a/gb.xml/src/textnode.cpp b/gb.xml/src/textnode.cpp new file mode 100644 index 00000000..5d5fa344 --- /dev/null +++ b/gb.xml/src/textnode.cpp @@ -0,0 +1,336 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "textnode.h" +#include "utils.h" +#include "node.h" + +#include +#include + +/*************************************** TextNode ***************************************/ + +TextNode* XMLTextNode_New() +{ + TextNode *newTextNode = (TextNode*)malloc(sizeof(TextNode)); + XMLNode_Init(newTextNode, Node::NodeText); + newTextNode->content = 0; + newTextNode->lenContent = 0; + newTextNode->escapedContent = 0; + newTextNode->lenEscapedContent = 0; + + return newTextNode; +} + +TextNode* XMLTextNode_New(const char *ncontent, const size_t nlen) +{ + TextNode *newTextNode = (TextNode*)malloc(sizeof(TextNode)); + XMLNode_Init(newTextNode, Node::NodeText); + newTextNode->content = 0; + newTextNode->lenContent = 0; + newTextNode->escapedContent = 0; + newTextNode->lenEscapedContent = 0; + newTextNode->lenContent = nlen; + if(!newTextNode->lenContent) + { + newTextNode->content = 0; + return newTextNode; + } + newTextNode->content = (char*)malloc(sizeof(char) * nlen + 1); + memcpy(newTextNode->content, ncontent, nlen); + newTextNode->content[nlen] = 0; + + return newTextNode; +} + +void XMLTextNode_Free(TextNode *node) +{ + if(node->escapedContent && node->escapedContent != node->content) free(node->escapedContent); + if(node->content) free(node->content); + free(node); +} + +void XMLText_escapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst) +{ + dst = (char*)src; + lenDst = lenSrc; + if(!lenSrc || !src) return; + char *posFound = strpbrk (dst, "<>&\""); + while (posFound != 0) + { + if(dst == src)//dst not allocated yet + { + dst = (char*)malloc(lenSrc + 1); + lenDst = lenSrc + 1; + dst[lenSrc] = 0; + memcpy(dst, src, lenSrc); + posFound = ((posFound - src) + dst); + } + switch(*posFound) + { + case '<'://< + *posFound = '&'; + ++posFound; + insertString(dst, lenDst , "lt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + case '>': //> + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "gt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + + case '&': //& + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "amp;", 4, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + case '"': //" + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "quot;", 5, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + default: + //posFound = strpbrk (posFound + 1,"<>&"); + break; + } + + + } + + if(dst != src) --lenDst; +} + +void XMLText_escapeAttributeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst) +{ + dst = (char*)src; + lenDst = lenSrc; + if(!lenSrc || !src) return; + char *posFound = strpbrk (dst, "<>&\"\n"); + while (posFound != 0) + { + if(dst == src)//dst not allocated yet + { + dst = (char*)malloc(lenSrc + 1); + lenDst = lenSrc + 1; + dst[lenSrc] = 0; + memcpy(dst, src, lenSrc); + posFound = ((posFound - src) + dst); + } + switch(*posFound) + { + case '<'://< + *posFound = '&'; + ++posFound; + insertString(dst, lenDst , "lt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + case '>': //> + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "gt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + + case '&': //& + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "amp;", 4, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + case '"': //" + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "quot;", 5, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + case '\n': + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "#10;", 4, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + default: + //posFound = strpbrk (posFound + 1,"<>&"); + break; + } + + + } + + if(dst != src) --lenDst; +} + +void XMLText_unEscapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst) +{ + dst = (char*)malloc(lenSrc); + lenDst = lenSrc; + memcpy(dst, src, lenSrc); + char *posFound = (char*)memchr(dst, '&', lenDst); + + while(((posFound != 0) && ((posFound + 3) < lenDst + dst)))//(posFound - dst) < lenDst - 3 + { + if(memcmp(posFound + 1, "lt;", 3) == 0)// < < + { + *posFound = '<'; + //.......dst=========posFound!===posFound+x|======dst+lenDst............ + //lenCut = (pos2 - pos1) = (dst + lenDst) - (posFound + x) + memmove(posFound + 1, posFound + 4, (dst + lenDst) - (posFound + 4)); + lenDst -= 3; + posFound -= 3; + } + else if(memcmp(posFound + 1, "gt;", 3) == 0)// > > + { + *posFound = '>'; + memmove(posFound + 1, posFound + 4, (dst + lenDst) - (posFound + 4)); + lenDst -= 3; + posFound -= 3; + } + else if(((posFound + 4) < lenDst + dst) && memcmp(posFound + 1, "amp;", 4) == 0)// & & + { + memmove(posFound + 1, posFound + 5, (dst + lenDst) - (posFound + 5)); + lenDst -= 4; + posFound -= 4; + } + else if(((posFound + 5) < lenDst + dst) && memcmp(posFound + 1, "quot;", 5) == 0)// "&" " + { + *posFound = '"'; + memmove(posFound + 1, posFound + 6, (dst + lenDst) - (posFound + 6)); + lenDst -= 5; + posFound -= 5; + } + if(posFound + 1 >= dst + lenDst) break; + posFound = (char*)memchr(posFound + 1, '&', lenDst - (posFound + 1 - dst)); + //cond = ((posFound != 0) && ((posFound + 3) < lenDst + dst)); + } +} + +void XMLTextNode_checkEscapedContent(TextNode *node) +{ + if(!(node->escapedContent) && node->content) + { + XMLText_escapeContent(node->content, node->lenContent, node->escapedContent, node->lenEscapedContent); + } +} + +void XMLTextNode_checkContent(TextNode *node) +{ + if(node->escapedContent && !(node->content)) + { + XMLText_unEscapeContent(node->escapedContent, node->lenEscapedContent, node->content, node->lenContent); + } +} + +void XMLTextNode_setEscapedTextContent(TextNode *node, const char *ncontent, const size_t nlen) +{ + node->escapedContent = (char*)realloc(node->escapedContent, sizeof(char)*nlen); + node->lenEscapedContent = nlen; + memcpy(node->escapedContent, ncontent, nlen); +} + +/***** Text Content *****/ + +void XMLTextNode_TrimContent(TextNode *node) +{ + const char *oldcontent = node->content; + Trim(oldcontent, node->lenContent); + memmove(node->content, oldcontent, node->lenContent); + node->content = (char*)realloc(node->content, sizeof(char)*node->lenContent); +} + +void XMLTextNode_setTextContent(TextNode *node, const char *ncontent, const size_t nlen) +{ + node->content = (char*)realloc(node->content, sizeof(char)*nlen + 1); + node->lenContent = nlen; + memcpy(node->content, ncontent, nlen); + node->content[node->lenContent] = 0; +} + +void XMLTextNode_appendTextContent(TextNode *node, const char *content, const size_t len) +{ + size_t newLen = node->lenContent + len; + node->content = (char*)realloc(node->content, newLen); + memcpy(node->content + node->lenContent, content, len); + node->lenContent = newLen; +} + +/*************************************** Comment ***************************************/ + +CommentNode* XMLComment_New() +{ + CommentNode *newComment = XMLTextNode_New(); + newComment->type = Node::Comment; + return newComment; +} + + +CommentNode* XMLComment_New(const char *ncontent, const size_t nlen) +{ + CommentNode *newComment = XMLTextNode_New(ncontent, nlen); + newComment->type = Node::Comment; + return newComment; +} + +/*************************************** CDATA ***************************************/ + +CDATANode* XMLCDATA_New() +{ + CommentNode *newComment = XMLTextNode_New(); + newComment->type = Node::CDATA; + return newComment; +} + +CDATANode* XMLCDATA_New(const char *ncontent, const size_t nlen) +{ + CommentNode *newComment = XMLTextNode_New(ncontent, nlen); + newComment->type = Node::CDATA; + return newComment; +} + + +bool XML_isTextNode(Node *node) +{ + switch(node->type) + { + case Node::NodeText: + case Node::Comment: + case Node::CDATA: + return true; + default: + return false; + } +} diff --git a/gb.xml/src/textnode.h b/gb.xml/src/textnode.h new file mode 100644 index 00000000..cc7c33dc --- /dev/null +++ b/gb.xml/src/textnode.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef TEXTNODE_H +#define TEXTNODE_H + +#include "main.h" + +void XMLText_unEscapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); +void XMLText_escapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); +void XMLText_escapeAttributeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + +TextNode* XMLTextNode_New(); +TextNode* XMLTextNode_New(const char *ncontent, const size_t nlen); +void XMLTextNode_Free(TextNode *node); + +void XMLTextNode_appendTextContent(TextNode *node, const char *content, const size_t len); +void XMLTextNode_checkEscapedContent(TextNode *node); +void XMLTextNode_checkContent(TextNode *node); +void XMLTextNode_setEscapedTextContent(TextNode *node, const char *ncontent, const size_t nlen); + +void XMLTextNode_TrimContent(TextNode *node); + +bool XML_isTextNode(Node *node); + + +CommentNode* XMLComment_New(); +CommentNode* XMLComment_New(const char *ncontent, const size_t nlen); + +CDATANode* XMLCDATA_New(); +CDATANode* XMLCDATA_New(const char *ncontent, const size_t nlen); + +#endif // TEXTNODE_H diff --git a/gb.xml/src/utils.cpp b/gb.xml/src/utils.cpp new file mode 100644 index 00000000..e57ac2a1 --- /dev/null +++ b/gb.xml/src/utils.cpp @@ -0,0 +1,372 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "utils.h" +#include "gbinterface.h" +#include "gb_common.h" +#include "textnode.h" + +#include +#include +#include + +#if defined(OS_MACOSX) +void *memrchr(const char *s, int c, size_t n) +{ + const char *start=s,*end=(s+n-1); + + while(end>=start) + { + if(*end==c) + return (void *)end; + else + end--; + } + + return NULL; +} +#endif + +wchar_t nextUTF8Char(const char *&data, size_t len) +{ + unsigned char c = *data; + if (c <= 0x7f){//first byte + data++; + return (wchar_t)c; + } + else if (c <= 0xdf && c >= 0xbf && 1 < len){//2byte sequence start + wchar_t c2 = (unsigned char)data[1]; + data += 2; + return (((c & 0x1f) << 6) | (c2 & 0x3f)); + } + else if (c <= 0xef && c >= 0xbf && 2 < len){//3byte sequence start + wchar_t c2 = (unsigned char)data[1]; + wchar_t c3 = (unsigned char)data[2]; + data += 3; + return (((((c & 0x1f) << 6) | (c2 & 0x3f)) << 6)| (c3 & 0x3f)); + //w = c & 0x0f; + } + else if (c <= 0xf7 && c >= 0xbf && 3 < len){//4byte sequence start + wchar_t c2 = (unsigned char)data[1]; + wchar_t c3 = (unsigned char)data[2]; + wchar_t c4 = (unsigned char)data[3]; + data += 4; + return (((((((c & 0x1f) << 6) | (c2 & 0x3f)) << 6)| (c3 & 0x3f)) << 6) | (c4 & 0x3f)); + //w = c & 0x07; + } + else + { + return CHAR_ERROR; + } +} + + +/* http://www.w3.org/TR/REC-xml/#NT-NameStartChar + + NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | + [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | + [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] + */ + +#define INTER(min, max) (car >= min && car <= max) +#define CAR(c) (car == c) + +bool isNameStartChar(const wchar_t car) +{ + if(INTER('a', 'z')) return true; + + return CAR(':') || INTER('A', 'Z') || CAR('_') || + INTER(0xC0, 0xD6) || INTER(0xD8, 0xF6) || INTER(0xF8, 0x2FF) || + INTER(0x370, 0x37D) || INTER(0x37F, 0x1FFF) || INTER(0x200C, 0x200D) || + INTER(0x2070, 0x218F) || INTER(0x2C00, 0x2FEF) || INTER(0x3001, 0xD7FF) || + INTER(0xF900, 0xFDCF) || INTER(0xFDF0, 0xFFFD) || INTER(0x10000, 0xEFFFF); + + +} + +/* http://www.w3.org/TR/REC-xml/#NT-NameChar + + NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] + + */ + +bool isNameChar(const wchar_t car) +{ + if(INTER('a', 'z')) return true; + if(isNameStartChar(car)) return true; + return CAR('-') || CAR('.') || INTER('0', '9') || + (car == 0xB7) || INTER(0x0300, 0x036F) || INTER(0x203F, 0x2040); +} +/* http://www.w3.org/TR/REC-xml/#NT-S + + S ::= (#x20 | #x9 | #xD | #xA)+ + + */ +bool isWhiteSpace(const wchar_t s) +{ + const wchar_t car = s; + + return (car == 0x20) || (car == 0x9) || (car == 0xD) || (car == 0xA); +} + +bool isWhiteSpace(const char s) +{ + const char car = s; + + return (car == 0x20) || (car == 0x9) || (car == 0xD) || (car == 0xA); +} + +const void* memchrs(const char *source, size_t lensource, const char *comp, size_t lencomp) +{ + const char *pos = source - 1; + do + { + pos = (char*)(memchr((void*)(pos + 1), ((comp))[0], lensource - (pos - source) - 1)); + if(!pos) return 0; + if(pos + lencomp > source + lensource) return 0; + if(memcmp(pos, comp, lencomp) != 0) continue; + return pos; + + }while(1); +} + +const void* memrchrs(const char *source, size_t lensource, const char *comp, size_t lencomp) +{ + char *pos = (char*)source; + do + { + pos = (char*)(memrchr(pos, ((char*)(comp))[lencomp - 1], lensource - (pos - source))); + if(!pos) return 0; + if(pos - lencomp < source) return 0; + if(memcmp(pos - lencomp, comp, lencomp) != 0) continue; + return pos; + }while(1); +} + +void Trim(const char *&str, size_t &len) +{ + while(isWhiteSpace(*str) && len) + { + ++str; + --len; + } + + if(!len) return; + + while(isWhiteSpace(*(str + len - 1)) && len > 0) + --len; +} + +void insertString(char *&src, size_t &lenSrc, const char *insert, size_t lenInsert, char *&posInsert) +{ + size_t iPosInsert = posInsert - src; + lenSrc += lenInsert; + src = (char*)realloc(src, lenSrc); + posInsert = src + iPosInsert; + memmove(posInsert + lenInsert, posInsert, lenSrc - lenInsert - iPosInsert); + memcpy(posInsert, insert, lenInsert); +} + +bool GB_MatchString(const char *str, size_t lenStr, const char *pattern, size_t lenPattern, int mode) +{ + if(mode == GB_STRCOMP_NOCASE || mode == GB_STRCOMP_LANG + GB_STRCOMP_NOCASE) + { + if(lenStr != lenPattern) return false; + return strncasecmp(str, pattern, lenPattern) == 0; + } + else if(mode == GB_STRCOMP_LIKE) + { + return GB.MatchString(pattern, lenPattern, str, lenStr) == true; + } + else + { + if(lenStr != lenPattern) return false; + return memcmp(str, pattern, lenPattern) == 0; + } +} + + +void XML_Format(GB_VALUE *value, char* &dst, size_t &lenDst) +{ +#define RETURN(__str) dst = (char*)malloc(sizeof(char) * lenDst); memcpy(dst, __str, lenDst); return; +#define RETURNLEN(__str, __len) lenDst = __len; RETURN(__str); + static char buffer[32]; + if(value->type == GB_T_VARIANT) + GB.Conv(value, ((GB_VARIANT *)value)->value.type); + + if(value->type == GB_T_DATE) + GB.Conv(value, GB_T_STRING); + + switch(value->type) + { + case GB_T_BOOLEAN: + + if (VALUE((GB_BOOLEAN *)value)) + { + RETURNLEN("True", 4); + } + else + { + RETURNLEN("False", 5); + } + + case GB_T_BYTE: + case GB_T_SHORT: + case GB_T_INTEGER: + lenDst = sprintf(buffer, "%d", VALUE((GB_INTEGER *)value)); + RETURN(buffer); + break; + + case GB_T_LONG: + lenDst = sprintf(buffer, "%" PRId64, VALUE((GB_LONG *)value)); + break; + + case GB_T_STRING: + case GB_T_CSTRING: + + XMLText_escapeContent(VALUE((GB_STRING *)value).addr + VALUE((GB_STRING *)value).start, VALUE((GB_STRING *)value).len, dst, lenDst); + return; + + case GB_T_FLOAT: + int lendst; + GB.NumberToString(0, VALUE((GB_FLOAT *)value), NULL, &dst, &lendst); + lenDst = (size_t) lendst; + return; + case GB_T_NULL: + RETURNLEN("Null", 4) + break; + + default: + fprintf(stderr, "gb.xml: XML_Format: unsupported datatype: %d\n", (int)value->type); + dst = 0; + lenDst = 0; + return; + } +#undef RETURN +#undef RETURNLEN +} + +/************************************ Error Management ************************************/ + +void XMLParseException_AnalyzeText(XMLParseException *ex, const char *text, const size_t lenText, const char *posFailed) throw(); + +void ThrowXMLParseException(const char* nerror, const char *text, const size_t lenText, const char *posFailed) +{ + throw XMLParseException_New(nerror, text, lenText, posFailed); +} + +XMLParseException XMLParseException_New()//Void initializer +{ + XMLParseException exception; + memset(&exception, 0, sizeof(XMLParseException)); + exception.line = 1; + exception.column = 1; + return exception; +} + +XMLParseException XMLParseException_New(const char *nerror, size_t posFailed) +{ + XMLParseException exception = XMLParseException_New(); + size_t lenError; + char *error; + + lenError = strlen(nerror) + 1; + error = (char*) malloc(lenError); + memcpy(error, nerror, lenError); + + exception.errorWhat = (char*)malloc(37 + lenError); + sprintf(exception.errorWhat, "Parse error : %s !\n Position %zu", error, (size_t)posFailed); + exception.errorWhat[36 + lenError] = 0; + + free(error); + + return exception; +} + +XMLParseException XMLParseException_New(const char *nerror, const char *data, const size_t lenData, const char *posFailed) throw() +{ + XMLParseException exception = XMLParseException_New(); + size_t lenError; + + lenError = strlen(nerror) + 1; + + //Parse error : (errorText) !\n Line 123456789 , Column 123456789 : \n (near) + + if(posFailed == 0) + { + exception.errorWhat = (char*)malloc(17 + lenError); + sprintf(exception.errorWhat, "Parse error : %s !", nerror); + exception.errorWhat[16 + lenError] = 0; + return exception; + } + else if(!data || !lenData) + { + exception.errorWhat = (char*)malloc(37 + lenError); + sprintf(exception.errorWhat, "Parse error : %s !\n Position %zu", nerror, (size_t)posFailed); + exception.errorWhat[36 + lenError] = 0; + return exception; + } + + if(posFailed > data + lenData || posFailed < data) return exception; + XMLParseException_AnalyzeText(&exception, data, lenData, posFailed); + + + exception.errorWhat = (char*)malloc(61 + lenError + exception.lenNear); + memset(exception.errorWhat, 0, 61 + lenError + exception.lenNear); + sprintf(exception.errorWhat, "Parse error : %s !\n Line %zu , Column %zu : \n %s", nerror, exception.line, exception.column, exception.near); + exception.errorWhat[60 + lenError + exception.lenNear] = 0; + + return exception; +} + +void XMLParseException_Cleanup(XMLParseException *ex) +{ + if(ex->errorWhat) free(ex->errorWhat); + if(ex->near) free(ex->near); +} + +void XMLParseException_AnalyzeText(XMLParseException *ex, const char *text, const size_t lenText, const char *posFailed) throw() +{ + for(const char *pos = text; pos < posFailed; ++pos) + { + ++ex->column; + if(*pos == '\n') + { + ex->column = 1; + ++ex->line; + } + else if(*pos == '\r') + { + if(*(pos + 1) == '\n') ++pos; + ex->column = 1; + ++ex->line; + } + } + + ex->lenNear = text + lenText <= posFailed + 20 ? text + lenText - posFailed : 20; + if(ex->lenNear == 0) return; + ex->near = (char*)malloc(ex->lenNear + 1); + memcpy(ex->near, posFailed, ex->lenNear); + ex->near[ex->lenNear] = 0; +} + + + diff --git a/gb.xml/src/utils.h b/gb.xml/src/utils.h new file mode 100644 index 00000000..27709850 --- /dev/null +++ b/gb.xml/src/utils.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef UTILS_H +#define UTILS_H + +#include "main.h" + +#define CHAR_ERROR 0xFFFD // � + +#if defined(OS_MACOSX) +#include +void *memrchr(const char *s, int c, size_t n); +#endif + +wchar_t nextUTF8Char(const char *&data, size_t len); +const void* memchrs(const char *source, size_t lensource, const char *comp, size_t lencomp); +const void* memrchrs(const char *source, size_t lensource, const char *comp, size_t lencomp); + + +bool isNameStartChar(const wchar_t car); +bool isNameChar(const wchar_t car); +bool isWhiteSpace(const wchar_t s); +bool isWhiteSpace(const char s); + +void Trim(const char* &str, size_t &len); +void insertString(char *&src, size_t &lenSrc, const char *insert, size_t lenInsert, char *&posInsert); + +bool GB_MatchString(const char *str, size_t lenStr, const char *pattern, size_t lenPattern, int mode = GB_STRCOMP_BINARY); + +XMLParseException XMLParseException_New(const char *nerror, size_t posFailed); +XMLParseException XMLParseException_New(const char *nerror, const char *data, const size_t lenData, const char *posFailed) throw(); +void XMLParseException_Cleanup(XMLParseException *ex); +void ThrowXMLParseException(const char* nerror, const char *text, const size_t lenText, const char *posFailed); + + +#endif // UTILS_H + +#if !defined(UTILS_GBINTERFACE) && defined(GBINTERFACE_H) +#define UTILS_GBINTERFACE + +void XML_Format(GB_VALUE *value, char* &dst, size_t &lenDst); + +#endif diff --git a/gb.xml/src/xslt/CXSLT.cpp b/gb.xml/src/xslt/CXSLT.cpp new file mode 100644 index 00000000..b9570f95 --- /dev/null +++ b/gb.xml/src/xslt/CXSLT.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** + + CXSLT.c + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ +#define __CXSLT_C +#include "../gb.xml.h" +#include "CXSLT.h" + +extern GB_INTERFACE GB; +extern XML_INTERFACE XML; + +#include +#include +#include +#include +#include +#include +#include + +void XSLT_Transform(Document* doc, Document* stylesheet,char* &outDocument, size_t &outDocumentLen) +{ + if(!doc->childCount) throw XSLTException("Void document"); + if(!stylesheet->childCount) throw XSLTException("Void style sheet"); + + xsltStylesheetPtr sheet = 0; + + char *StyleSheetOutput = NULL; + size_t StyleSheetLen = 0; + + XML.SerializeXMLNode((Node*)stylesheet, StyleSheetOutput, StyleSheetLen, -1); + + StyleSheetOutput =(char*)realloc(StyleSheetOutput, StyleSheetLen + 1); + StyleSheetOutput[StyleSheetLen] = 0; + + + xmlDoc *xmlStyleSheet = xmlParseDoc((xmlChar*)(StyleSheetOutput)); + + free(StyleSheetOutput); + + if(!(sheet=xsltParseStylesheetDoc(xmlStyleSheet))) throw XSLTException("Invalid style sheet"); + + char *DocumentOutput; + size_t DocumentLen; + + + XML.SerializeXMLNode((Node*)doc, DocumentOutput, DocumentLen, -1); + + DocumentOutput =(char*)realloc(DocumentOutput, DocumentLen + 1); + DocumentOutput[DocumentLen] = 0; + + + xmlDoc *xmlInputDoc = xmlParseDoc((xmlChar*)(DocumentOutput)); + if(!xmlInputDoc) throw XSLTException("Unable to parse input document"); + + free(DocumentOutput); + + xmlDoc *xmlOutDoc; + + xmlOutDoc = xsltApplyStylesheet(sheet, xmlInputDoc, NULL); + if (!xmlOutDoc) throw XSLTException("Unable to apply style sheet"); + + int size; + xmlDocDumpFormatMemoryEnc(xmlOutDoc ,(xmlChar**)&outDocument, &size, "UTF-8", 1); + outDocumentLen = size; + + xsltFreeStylesheet(sheet); + xmlFreeDoc(xmlOutDoc); + + xmlFreeDoc(xmlInputDoc); +} + +BEGIN_METHOD(CXSLT_Transform,GB_OBJECT inputDoc;GB_OBJECT inputStyleSheet) + +if (GB.CheckObject(VARGOBJ(CDocument,inputDoc))) return; +if (GB.CheckObject(VARGOBJ(CDocument,inputStyleSheet))) return; + + Document *doc = (Document*)(VARGOBJ(CDocument,inputDoc)->node), + *stylesheet = (Document*)(VARGOBJ(CDocument,inputStyleSheet)->node); + + char *buffer = 0; + size_t size; + + try + { + XSLT_Transform(doc, stylesheet, buffer, size); + } + catch(XSLTException &ex) + { + GB.Error(ex.what()); + GB.ReturnNull(); + return; + } + + Document *outDoc = XML.XMLDocument_New(); + + try + { + XML.XMLDocument_SetContent(outDoc, (char*)(buffer),size); + } + catch(XMLParseException e) + { + std::cerr << "XSLT Warning : error when parsing output document : " << e.errorWhat << std::endl; + } + + + free(buffer); + XML.ReturnNode(outDoc); + + +END_METHOD + +BEGIN_METHOD(CXSLT_TransformToString,GB_OBJECT inputDoc;GB_OBJECT inputStyleSheet) + +if (GB.CheckObject(VARGOBJ(CDocument,inputDoc))) return; +if (GB.CheckObject(VARGOBJ(CDocument,inputStyleSheet))) return; + + Document *doc = (Document*)(VARGOBJ(CDocument,inputDoc)->node), + *stylesheet = (Document*)(VARGOBJ(CDocument,inputStyleSheet)->node); + + char *buffer = 0; + size_t size; + + try + { + XSLT_Transform(doc, stylesheet, buffer, size); + } + catch(XSLTException &ex) + { + GB.Error(ex.what()); + GB.ReturnNull(); + return; + } + + GB.ReturnNewString(buffer, size); + + free(buffer); + +END_METHOD + + + + +GB_DESC CXsltDesc[] = +{ + GB_DECLARE("Xslt", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD ("Transform","XmlDocument",CXSLT_Transform,"(Document)XmlDocument;(StyleSheet)XmlDocument;"), + GB_STATIC_METHOD ("TransformToString","s",CXSLT_TransformToString,"(Document)XmlDocument;(StyleSheet)XmlDocument;"), + + GB_END_DECLARE +}; + + +XSLTException::XSLTException(const char *error) throw() +{ + this->error = error; +} + +const char *XSLTException::what() +{ + return this->error; +} diff --git a/gb.xml/src/xslt/CXSLT.h b/gb.xml/src/xslt/CXSLT.h new file mode 100644 index 00000000..ae686d16 --- /dev/null +++ b/gb.xml/src/xslt/CXSLT.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + CXSLT.h + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXSLT_H +#define __CXSLT_H + +#include "../gbinterface.h" + +#ifndef __CXSLT_C +extern GB_DESC CXsltDesc[]; +#endif + +class XSLTException +{ +public: + XSLTException(const char *error) throw(); + const char* what(); +private: + const char *error; + +}; + +#endif diff --git a/gb.xml/src/xslt/Makefile.am b/gb.xml/src/xslt/Makefile.am new file mode 100755 index 00000000..98f0bedb --- /dev/null +++ b/gb.xml/src/xslt/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.xml.xslt +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.xml.xslt.la + +gb_xml_xslt_la_LIBADD = @XMLXSLT_LIB@ @XML_LIB@ +gb_xml_xslt_la_LDFLAGS = -module @LD_FLAGS@ @XML_LDFLAGS@ @XMLXSLT_LDFLAGS@ +gb_xml_xslt_la_CPPFLAGS = @XMLXSLT_INC@ +gb_xml_xslt_la_CXXFLAGS = $(AM_CXXFLAGS) -fexceptions + +gb_xml_xslt_la_SOURCES = main.cpp CXSLT.h CXSLT.cpp diff --git a/gb.xml/src/xslt/gb.xml.xslt.component b/gb.xml/src/xslt/gb.xml.xslt.component new file mode 100755 index 00000000..8a40225b --- /dev/null +++ b/gb.xml/src/xslt/gb.xml.xslt.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.xml.xslt +Author=Daniel Campos Fernández +Require=gb.xml +State=Stable + diff --git a/gb.xml/src/xslt/main.cpp b/gb.xml/src/xslt/main.cpp new file mode 100644 index 00000000..d805d96e --- /dev/null +++ b/gb.xml/src/xslt/main.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + + main.c + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CXSLT.h" +#include "../gb.xml.h" + +GB_INTERFACE GB EXPORT; +XML_INTERFACE XML EXPORT; + +extern "C" +{ + GB_DESC *GB_CLASSES[] EXPORT = + { + CXsltDesc ,0 + }; + + int EXPORT GB_INIT(void) + { + GB.GetInterface("gb.xml", XML_INTERFACE_VERSION, &XML); + return -1; + } + + void EXPORT GB_EXIT() + { + + } +} diff --git a/gb.xml/src/xslt/main.h b/gb.xml/src/xslt/main.h new file mode 100644 index 00000000..c8e619b5 --- /dev/null +++ b/gb.xml/src/xslt/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __XSLTMAIN_H +#define __XSLTMAIN_H + +#include "../main.h" +#include +#include +#include +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.xml/src/xslt/xslt.pro b/gb.xml/src/xslt/xslt.pro new file mode 100644 index 00000000..61584fa6 --- /dev/null +++ b/gb.xml/src/xslt/xslt.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Fri Apr 13 23:39:19 2012 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += CXSLT.h +SOURCES += \ + main.cpp \ + CXSLT.cpp diff --git a/logo/gambas-ide.svg b/logo/gambas-ide.svg new file mode 100644 index 00000000..9133c355 --- /dev/null +++ b/logo/gambas-ide.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo/gambas.svg b/logo/gambas.svg new file mode 100644 index 00000000..523873c6 --- /dev/null +++ b/logo/gambas.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4 new file mode 100644 index 00000000..9f8c5055 --- /dev/null +++ b/m4/ax_compare_version.m4 @@ -0,0 +1,176 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# +# DESCRIPTION +# +# This macro compares two version strings. Due to the various number of +# minor-version numbers that can exist, and the fact that string +# comparisons are not compatible with numeric comparisons, this is not +# necessarily trivial to do in a autoconf script. This macro makes doing +# these comparisons easy. +# +# The six basic comparisons are available, as well as checking equality +# limited to a certain number of minor-version levels. +# +# The operator OP determines what type of comparison to do, and can be one +# of: +# +# eq - equal (test A == B) +# ne - not equal (test A != B) +# le - less than or equal (test A <= B) +# ge - greater than or equal (test A >= B) +# lt - less than (test A < B) +# gt - greater than (test A > B) +# +# Additionally, the eq and ne operator can have a number after it to limit +# the test to that number of minor versions. +# +# eq0 - equal up to the length of the shorter version +# ne0 - not equal up to the length of the shorter version +# eqN - equal up to N sub-version levels +# neN - not equal up to N sub-version levels +# +# When the condition is true, shell commands ACTION-IF-TRUE are run, +# otherwise shell commands ACTION-IF-FALSE are run. The environment +# variable 'ax_compare_version' is always set to either 'true' or 'false' +# as well. +# +# Examples: +# +# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) +# +# would both be true. +# +# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) +# +# would both be false. +# +# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) +# +# would be true because it is only comparing two minor versions. +# +# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) +# +# would be true because it is only comparing the lesser number of minor +# versions of the two values. +# +# Note: The characters that separate the version numbers do not matter. An +# empty string is the same as version 0. OP is evaluated by autoconf, not +# configure, so must be a string, not a variable. +# +# The author would like to acknowledge Guido Draheim whose advice about +# the m4_case and m4_ifvaln functions make this macro only include the +# portions necessary to perform the specific comparison specified by the +# OP argument in the final configure script. +# +# LICENSE +# +# Copyright (c) 2008 Tim Toolan +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +dnl ######################################################################### +AC_DEFUN([AX_COMPARE_VERSION], [ + AC_REQUIRE([AC_PROG_AWK]) + + # Used to indicate true or false condition + ax_compare_version=false + + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + AS_VAR_PUSHDEF([A],[ax_compare_version_A]) + A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + AS_VAR_PUSHDEF([B],[ax_compare_version_B]) + B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary + dnl # then the first line is used to determine if the condition is true. + dnl # The sed right after the echo is to remove any indented white space. + m4_case(m4_tolower($2), + [lt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [gt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [le],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ], + [ge],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ],[ + dnl Split the operator from the subversion count if present. + m4_bmatch(m4_substr($2,2), + [0],[ + # A count of zero means use the length of the shorter version. + # Determine the number of characters in A and B. + ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` + ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` + + # Set A to no more than B's length and B to no more than A's length. + A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` + B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` + ], + [[0-9]+],[ + # A count greater than zero means use only that many subversions + A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + ], + [.+],[ + m4_warn([syntax], [illegal OP numeric parameter: $2]) + ],[]) + + # Pad zeros at end of numbers to make same length. + ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" + B="$B`echo $A | sed 's/./0/g'`" + A="$ax_compare_version_tmp_A" + + # Check for equality or inequality as necessary. + m4_case(m4_tolower(m4_substr($2,0,2)), + [eq],[ + test "x$A" = "x$B" && ax_compare_version=true + ], + [ne],[ + test "x$A" != "x$B" && ax_compare_version=true + ],[ + m4_warn([syntax], [illegal OP parameter: $2]) + ]) + ]) + + AS_VAR_POPDEF([A])dnl + AS_VAR_POPDEF([B])dnl + + dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. + if test "$ax_compare_version" = "true" ; then + m4_ifvaln([$4],[$4],[:])dnl + m4_ifvaln([$5],[else $5])dnl + fi +]) dnl AX_COMPARE_VERSION diff --git a/m4/gb_cflags_gcc_option.m4 b/m4/gb_cflags_gcc_option.m4 new file mode 100644 index 00000000..966ed6b8 --- /dev/null +++ b/m4/gb_cflags_gcc_option.m4 @@ -0,0 +1,222 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_cflags_gcc_option.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]]) +# +# DESCRIPTION +# +# AX_CFLAGS_GCC_OPTION(-fomit-frame-pointer) would show a message like +# "checking CFLAGS for gcc -fomit-frame-pointer ... yes" and add the +# optionflag to CFLAGS if it is understood. You can override the +# shellvar-default of CFLAGS of course. The order of arguments stems from +# the explicit macros like AX_CFLAGS_WARN_ALL. +# +# The cousin AX_CXXFLAGS_GCC_OPTION would check for an option to add to +# CXXFLAGS - and it uses the autoconf setup for C++ instead of C (since it +# is possible to use different compilers for C and C++). +# +# The macro is a lot simpler than any special AX_CFLAGS_* macro (or +# ax_cxx_rtti.m4 macro) but allows to check for arbitrary options. +# However, if you use this macro in a few places, it would be great if you +# would make up a new function-macro and submit it to the ac-archive. +# +# - $1 option-to-check-for : required ("-option" as non-value) +# - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case) +# - $3 action-if-found : add value to shellvariable +# - $4 action-if-not-found : nothing +# +# Note: in earlier versions, $1-$2 were swapped. We try to detect the +# situation and accept a $2=~/-/ as being the old option-to-check-for. +# +# There are other variants that emerged from the original macro variant +# which did just test an option to be possibly added. However, some +# compilers accept an option silently, or possibly for just another option +# that was not intended. Therefore, we have to do a generic test for a +# compiler family. For gcc we check "-pedantic" being accepted which is +# also understood by compilers who just want to be compatible with gcc +# even when not being made from gcc sources. +# +# See also: AX_CFLAGS_SUN_OPTION, AX_CFLAGS_HPUX_OPTION, +# AX_CFLAGS_AIX_OPTION, and AX_CFLAGS_IRIX_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 12 + +AC_DEFUN([GB_CFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG([C]) + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($2,$2,-option)" dnl GCC + "-pedantic % m4_ifval($2,$2,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([GB_CXXFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cxxflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG([C++]) + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($2,$2,-option)" dnl GCC + "-pedantic % m4_ifval($2,$2,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl ------------------------------------------------------------------------- + +AC_DEFUN([GB_CFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG([C]) + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC + "-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([GB_CXXFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cxxflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG([C++]) + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC + "-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break],[]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +AC_DEFUN([GB_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[GB_CFLAGS_GCC_OPTION_NEW($@)],[GB_CFLAGS_GCC_OPTION_OLD($@)])]) + +AC_DEFUN([GB_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[GB_CXXFLAGS_GCC_OPTION_NEW($@)],[GB_CXXFLAGS_GCC_OPTION_OLD($@)])]) + diff --git a/m4/gb_httpd.m4 b/m4/gb_httpd.m4 new file mode 100644 index 00000000..180dfcde --- /dev/null +++ b/m4/gb_httpd.m4 @@ -0,0 +1,176 @@ +dnl +dnl Improved version of AC_CHECK_LIB +dnl +dnl Thanks to John Hawkinson (jhawk@mit.edu) +dnl +dnl usage: +dnl +dnl GB_AC_LBL_CHECK_LIB(LIBRARY, FUNCTION [, ACTION-IF-FOUND [, +dnl ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]]) +dnl +dnl results: +dnl +dnl LIBS +dnl + +define(GB_AC_LBL_CHECK_LIB, +[AC_MSG_CHECKING([for $2 in -l$1]) +dnl Use a cache variable name containing both the library and function name, +dnl because the test really is for library $1 defining function $2, not +dnl just for library $1. Separate tests with the same $1 and different $2's +dnl may have different results. +ac_lib_var=`echo $1['_']$2['_']$5 | sed 'y%./+- %__p__%'` +AC_CACHE_VAL(ac_cv_lbl_lib_$ac_lib_var, +[ac_save_LIBS="$LIBS" +LIBS="-l$1 $5 $LIBS" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[dnl +ifelse($2, main, , dnl Avoid conflicting decl of main. +/* Override any gcc2 internal prototype to avoid an error. */ +ifelse(_AC_LANG_CURRENT, CPLUSPLUS, #ifdef __cplusplus +extern "C" +#endif +)dnl +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $2(); +)]], [[$2()]])],[eval "ac_cv_lbl_lib_$ac_lib_var=yes"],[eval "ac_cv_lbl_lib_$ac_lib_var=no"]) +LIBS="$ac_save_LIBS" +])dnl +if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$3], , +[changequote(, )dnl + ac_tr_lib=HAVE_LIB`echo $1 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` +changequote([, ])dnl + AC_DEFINE_UNQUOTED($ac_tr_lib, [], []) + LIBS="-l$1 $LIBS" +], [$3]) +else + AC_MSG_RESULT(no) +ifelse([$4], , , [$4 +])dnl +fi +]) + +dnl +dnl GB_AC_LBL_LIBRARY_NET +dnl +dnl This test is for network applications that need socket() and +dnl gethostbyname() -ish functions. Under Solaris, those applications +dnl need to link with "-lsocket -lnsl". Under IRIX, they need to link +dnl with "-lnsl" but should *not* link with "-lsocket" because +dnl libsocket.a breaks a number of things (for instance: +dnl gethostbyname() under IRIX 5.2, and snoop sockets under most +dnl versions of IRIX). +dnl +dnl Unfortunately, many application developers are not aware of this, +dnl and mistakenly write tests that cause -lsocket to be used under +dnl IRIX. It is also easy to write tests that cause -lnsl to be used +dnl under operating systems where neither are necessary (or useful), +dnl such as SunOS 4.1.4, which uses -lnsl for TLI. +dnl +dnl This test exists so that every application developer does not test +dnl this in a different, and subtly broken fashion. + +dnl It has been argued that this test should be broken up into two +dnl seperate tests, one for the resolver libraries, and one for the +dnl libraries necessary for using Sockets API. Unfortunately, the two +dnl are carefully intertwined and allowing the autoconf user to use +dnl them independantly potentially results in unfortunate ordering +dnl dependancies -- as such, such component macros would have to +dnl carefully use indirection and be aware if the other components were +dnl executed. Since other autoconf macros do not go to this trouble, +dnl and almost no applications use sockets without the resolver, this +dnl complexity has not been implemented. +dnl +dnl The check for libresolv is in case you are attempting to link +dnl statically and happen to have a libresolv.a lying around (and no +dnl libnsl.a). +dnl +AC_DEFUN([GB_AC_LBL_LIBRARY_NET], [ + # Most operating systems have gethostbyname() in the default searched + # libraries (i.e. libc): + AC_CHECK_FUNC(gethostbyname, , + # Some OSes (eg. Solaris) place it in libnsl: + GB_AC_LBL_CHECK_LIB(nsl, gethostbyname, , + # Some strange OSes (SINIX) have it in libsocket: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, , + # Unfortunately libsocket sometimes depends on libnsl. + # AC_CHECK_LIB's API is essentially broken so the + # following ugliness is necessary: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, + LIBS="-lsocket -lnsl $LIBS", + AC_CHECK_LIB(resolv, gethostbyname), + -lnsl)))) + AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket, , + GB_AC_LBL_CHECK_LIB(socket, socket, LIBS="-lsocket -lnsl $LIBS", , + -lnsl))) + # DLPI needs putmsg under HPUX so test for -lstr while we're at it + AC_CHECK_LIB(str, putmsg) + ]) + +dnl +dnl Checks to see if struct tm has the BSD tm_gmtoff member +dnl +dnl usage: +dnl +dnl GB_AC_ACME_TM_GMTOFF +dnl +dnl results: +dnl +dnl HAVE_TM_GMTOFF (defined) +dnl +AC_DEFUN([GB_AC_ACME_TM_GMTOFF], + [AC_MSG_CHECKING(if struct tm has tm_gmtoff member) + AC_CACHE_VAL(ac_cv_acme_tm_has_tm_gmtoff, + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +# include +# include ]], [[u_int i = sizeof(((struct tm *)0)->tm_gmtoff)]])],[ac_cv_acme_tm_has_tm_gmtoff=yes],[ac_cv_acme_tm_has_tm_gmtoff=no])) + AC_MSG_RESULT($ac_cv_acme_tm_has_tm_gmtoff) + if test $ac_cv_acme_tm_has_tm_gmtoff = yes ; then + AC_DEFINE([HAVE_TM_GMTOFF], [], [if struct tm has the BSD tm_gmtoff member]) + fi]) + +dnl +dnl Checks to see if int64_t exists +dnl +dnl usage: +dnl +dnl GB_AC_ACME_INT64T +dnl +dnl results: +dnl +dnl HAVE_INT64T (defined) +dnl +AC_DEFUN([GB_AC_ACME_INT64T], + [AC_MSG_CHECKING(if int64_t exists) + AC_CACHE_VAL(ac_cv_acme_int64_t, + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +# include ]], [[int64_t i64]])],[ac_cv_acme_int64_t=yes],[ac_cv_acme_int64_t=no])) + AC_MSG_RESULT($ac_cv_acme_int64_t) + if test $ac_cv_acme_int64_t = yes ; then + AC_DEFINE([HAVE_INT64T], [], [if int64_t exists]) + fi]) + +dnl +dnl Checks to see if socklen_t exists +dnl +dnl usage: +dnl +dnl GB_AC_ACME_SOCKLENT +dnl +dnl results: +dnl +dnl HAVE_SOCKLENT (defined) +dnl +AC_DEFUN([GB_AC_ACME_SOCKLENT], + [AC_MSG_CHECKING(if socklen_t exists) + AC_CACHE_VAL(ac_cv_acme_socklen_t, + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +# include +# include ]], [[socklen_t slen]])],[ac_cv_acme_socklen_t=yes],[ac_cv_acme_socklen_t=no])) + AC_MSG_RESULT($ac_cv_acme_socklen_t) + if test $ac_cv_acme_socklen_t = yes ; then + AC_DEFINE([HAVE_SOCKLENT], [], [if socklen_t exists]) + fi]) diff --git a/m4/gb_sdl.m4 b/m4/gb_sdl.m4 new file mode 100644 index 00000000..469cc0ed --- /dev/null +++ b/m4/gb_sdl.m4 @@ -0,0 +1,183 @@ +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +#serial 1 + +dnl GB_PATH_SDL is only a rename for AM_PATH_SDL, grab from sdl-1.2.13 release + +dnl GB_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN([GB_PATH_SDL], +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + if test x$sdl_exec_prefix != x ; then + sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + if test "x$prefix" != xNONE; then + PATH="$prefix/bin:$prefix/usr/bin:$PATH" + fi + AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH]) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdl_config_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdl_config_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_CXXFLAGS="$CXXFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +#include +#include "SDL.h" + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +]])],[],[no_sdl=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include "SDL.h" + +int main(int argc, char *argv[]) +{ return 0; } +#undef main +#define main K_and_R_C_main +]], [[ return 0; ]])],[ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],[ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) diff --git a/main/AUTHORS b/main/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/main/COPYING b/main/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/main/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/main/ChangeLog b/main/ChangeLog new file mode 120000 index 00000000..22ec9b8a --- /dev/null +++ b/main/ChangeLog @@ -0,0 +1 @@ +../ChangeLog \ No newline at end of file diff --git a/main/INSTALL b/main/INSTALL new file mode 100644 index 00000000..64d33306 --- /dev/null +++ b/main/INSTALL @@ -0,0 +1,231 @@ + +REQUIREMENTS +============ + +Read the IMPORTANT NOTES in the README file. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + + +Gambas Options +============== + + --with-intl-includes where the internationalization headers are located. + --with-intl-libraries where the internationalization libraries are located. + --with-qt-includes where the QT component headers are located. + --with-qt-libraries where the QT component libraries are located. + --with-kde-includes where the KDE 3.x component headers are located. + --with-kde-libraries where the KDE 3.x component libraries are located. + --with-net-includes where the Networking component headers are located. + --with-net-libraries where the Networking component libraries are located. + --with-postgresql-includes where the PostgreSQL driver headers are located. + --with-postgresql-libraries where the PostgreSQL driver libraries are located. + --with-mysql-includes where the MySQL driver headers are located. + --with-mysql-libraries where the MySQL driver libraries are located. + --with-sdl-includes where the SDL component headers are located. + --with-sdl-libraries where the SDL component libraries are located. + +--disable-debug Remove debug information from binary files. + +--enable-optimization Enable optimization during compilation. + +--disable-preloading Disable the preloading of components. + +--disable-qt-component Do not compile the QT component. + + +Component options +================= + +XXX is a component or library name: + + intl internationalization library + kde KDE component + mysql MySQL driver + net Network component + postgresql PostgreSQL driver + qt QT component + sdl SDL component + +--with-XXX-libraries Where the libraries are located. + +--with-XXX-includes Where the headers are located. + +Use these options if the configure script cannot detect the +location of librairies and/or headers. + +The components or libraries that are not detected are automatically disables, +and then not compiled. + +That's all ! Good luck... diff --git a/main/Makefile.am b/main/Makefile.am new file mode 100644 index 00000000..cb4d9231 --- /dev/null +++ b/main/Makefile.am @@ -0,0 +1,76 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = gbc gbx . lib share man +EXTRA_DIST = TODO reconf tools spec README mime gb.*.h + +install-exec-local: + @rm -f $(srcdir)/../warnings.log + + @if test "x$(ROOT)" != "x"; then \ + echo "[Installing with ROOT=$(ROOT)]"; \ + fi + @if test "x$(DESTDIR)" != "x"; then \ + echo "[Installing with DESTDIR=$(DESTDIR)]"; \ + ROOT=$DESTDIR; \ + fi + + @echo "Installing runtime symbolic link" + @$(LN_S) -f gbx$(GAMBAS_VERSION) $(DESTDIR)$(bindir)/gbr$(GAMBAS_VERSION) || true + + @if test x"$(XDG_UTILS)" != x; then \ + echo "Registering Gambas executable mimetype"; \ + xdg-icon-resource install --novendor --context mimetypes --size 256 $(srcdir)/mime/application-x-gambas3.png application-x-gambas3; \ + xdg-icon-resource install --novendor --context mimetypes --size 48 $(srcdir)/mime/application-x-gambas3-48.png application-x-gambas3; \ + xdg-mime install $(srcdir)/mime/application-x-gambas3.xml; \ + fi + + @echo "Installing 'gb' component..." + @$(INSTALL) -d $(DESTDIR)$(gbdatadir)/info + @$(INSTALL) lib/gb.component $(DESTDIR)$(gblibdir) + @$(DESTDIR)$(bindir)/gbi$(GAMBAS_VERSION) -r $(DESTDIR)$(prefix) gb + @rm -f $(DESTDIR)$(gblibdir)/gb.la + @rm -f $(DESTDIR)$(gblibdir)/gb.so* + + @echo "Installing compiler tools..." + @(cd $(srcdir)/tools; d=`pwd`; \ + for p in gb*; do \ + echo "Compiling $$p..."; cd $$d/$$p; \ + $(DESTDIR)$(bindir)/gbc$(GAMBAS_VERSION) -ag -r $(DESTDIR)$(prefix); \ + if test $$? -eq 0; then \ + $(DESTDIR)$(bindir)/gba$(GAMBAS_VERSION); \ + rm -rf .gambas; \ + echo "Installing $$p..."; \ + $(INSTALL) $$p.gambas $(DESTDIR)$(bindir); \ + $(LN_S) -f $$p.gambas $(DESTDIR)$(bindir)/$$p || true; \ + else \ + echo "|| Unable to compile $$p" >> ../../../warnings.log; \ + fi \ + done) + +uninstall-local: + @echo "Uninstalling compiler tools..." + @(cd $(srcdir)/tools; for p in gb*; do rm -f $(DESTDIR)$(bindir)/$$p.gambas $(DESTDIR)$(bindir)/$$p; done) + + @echo "Uninstalling 'gb' component..." + @rm -f $(DESTDIR)$(gblibdir)/gb.component + @rm -rf $(DESTDIR)$(gbdatadir)/info/gb.info + @rm -rf $(DESTDIR)$(gbdatadir)/info/gb.list + + @if test x"$(XDG_UTILS)" != x; then \ + echo "Unregistering Gambas executable mimetype"; \ + xdg-mime uninstall $(srcdir)/mime/application-x-gambas3.xml; \ + xdg-icon-resource uninstall --context mimetypes --size 256 application-x-gambas3; \ + fi + + @echo "Uninstalling runtime symbolic link" + @rm -f $(DESTDIR)$(bindir)/gbr$(GAMBAS_VERSION) + + @rmdir $(DESTDIR)$(gbdatadir)/info >/dev/null 2>&1 || true; + @rmdir $(DESTDIR)$(gbdatadir)/control >/dev/null 2>&1 || true; + @rmdir $(DESTDIR)$(gbdatadir) >/dev/null 2>&1 || true; + +dist-hook: + @rm -f $(distdir)/trunk_version.h + @(cd $(distdir)/tools; \ + rm -rf `find . -name ".gambas" -o -name ".action" -o -name ".lock" -o -name ".xvpics" -o -name "*~" -o -name "*.out" -o -name "*.pot" -o -name "*.gambas" -o -name "core*" -o -name ".kdbg*" -o -name ".svn"`;) + + diff --git a/main/NEWS b/main/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/main/README b/main/README new file mode 100644 index 00000000..e69de29b diff --git a/main/TODO b/main/TODO new file mode 100644 index 00000000..e69de29b diff --git a/main/acinclude.m4 b/main/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/main/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/main/component.am b/main/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/main/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/main/configure.ac b/main/configure.ac new file mode 100644 index 00000000..39054bad --- /dev/null +++ b/main/configure.ac @@ -0,0 +1,155 @@ +dnl ---- configure.ac for main programs + +m4_include([../version.m4]) +AC_INIT([gambas3-main],[GB_VERSION],[GB_MAIL],[],[GB_URL]) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT([main]) +GB_TRUNK_VERSION +GB_MATH_FUNC +LT_INIT([dlopen win32-dll]) + +AM_PROG_CC_C_O + +CFLAGS=$gb_save_cflags +CXXFLAGS=$gb_save_cxxflags + +if test "x${GAMBAS_CONFIG_FAILURE}" != "x"; then + SAVE_GAMBAS_CONFIG_FAILURE=${GAMBAS_CONFIG_FAILURE} + GAMBAS_CONFIG_FAILURE='' +fi + +dnl ---- Check for internationalization library + +GB_COMPONENT( + intl, INTL, libintl, [], + [GB_FIND(libintl.h, /opt/local /usr/local /usr, include)], + [GB_FIND(libintl.$SHLIBEXT, /opt/local /usr/local /usr /, lib)], + [-lintl], + [], + [Cannot find 'libintl' library. It may be located inside the system C library, so you can ignore that warning...]) + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +dnl ---- Check for charset conversion library + +GB_COMPONENT( + conv, CONV, libiconv, [], + [GB_FIND(iconv.h, /opt/local /usr/local /usr, include)], + [GB_FIND(libiconv.$SHLIBEXT, /opt/local /usr/local /usr, lib)], + [-liconv], + [], + [Cannot find 'libiconv' library. It may be merged inside the system C library, so you can ignore that warning...]) + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +dnl ---- Check for gettext library + +if test "x$GETTEXT_LIB" != x; then + +GB_COMPONENT( + gettext, GETTEXT, libgettextlib, [], + [], + [GB_FIND(libgettextlib.$SHLIBEXT, /opt/local /usr/local /usr, lib)], + [$GETTEXT_LIB], + [], + [Cannot find 'libgettextlib' library. It may be merged inside the system C library, so you can ignore that warning...]) + +fi + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +dnl ---- Check for ffi library + +GB_COMPONENT_SEARCH( + ffi, FFI, libffi, [], + libffi, + [GB_FIND(ffi.h, /usr/local /usr/local/lib/64 /usr/local/lib /usr /usr/lib64 /usr/lib /usr/lib/gcc/*/*, include ffi/include)], + [GB_FIND(libffi.$SHLIBEXT, /usr/local /usr /usr/lib/gcc/*/*, lib .)], + [-lffi]) + +if test -z "$FFI_LIB"; then + GB_COMPONENT( + ffi, FFI, libffi, [], + [GB_FIND(ffi.h, /opt/local /usr/local /usr/local/lib /usr /usr/lib /usr/lib/gcc/*/*, include ffi/include)], + [GB_FIND(libffi.$SHLIBEXT, /usr/local /usr, lib)], + [-lffi]) +fi + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +if test "x${SAVE_GAMBAS_CONFIG_FAILURE}" != "x"; then + GAMBAS_CONFIG_FAILURE=${SAVE_GAMBAS_CONFIG_FAILURE} + SAVE_GAMBAS_CONFIG_FAILURE='' +fi + +dnl ---- Check for the Linux inotify headers + +GB_COMPONENT( + inotify, INOTIFY, gb.inotify, [inotify], + [GB_FIND(sys/inotify.h, /usr/local/lib /usr/local /usr/lib /usr, include)], + [GB_FIND(, /usr/local /usr, lib)], + [$GB_INOTIFY_LIB]) + +dnl ---- We do not use libtool to load shared libraries anymore! + +AC_DEFINE(DONT_USE_LTDL, 1, [Do not use libtool to load shared libraries]) +if test "$SYSTEM" != "OPENBSD" && test "$SYSTEM" != "FREEBSD" && test "$SYSTEM" != "NETBSD"; then + DL_LIB="-ldl" +else + DL_LIB="" +fi + +AC_SUBST(DL_LIB) + +dnl ---- Check for Portland scripts + +AC_CHECK_PROGS(XDG_UTILS, [xdg-mime xdg-icon-resource], []) + +dnl ---- Create makefiles + +AC_CONFIG_FILES([\ +Makefile \ +share/Makefile \ +man/Makefile \ +gbc/Makefile \ +gbx/Makefile \ +lib/Makefile \ +lib/debug/Makefile \ +lib/eval/Makefile \ +lib/db/Makefile \ +lib/vb/Makefile \ +lib/compress/Makefile \ +lib/option/Makefile \ +lib/geom/Makefile \ +lib/draw/Makefile \ +lib/gui/Makefile \ +lib/gui.opengl/Makefile \ +lib/gui.webview/Makefile \ +lib/gui.qt/Makefile \ +lib/gui.qt.webkit/Makefile \ +lib/gui.qt.opengl/Makefile \ +lib/gui.trayicon/Makefile \ +lib/image/Makefile \ +lib/image.effect/Makefile \ +lib/signal/Makefile \ +lib/term/Makefile \ +lib/complex/Makefile \ +lib/data/Makefile \ +lib/clipper/Makefile \ +lib/inotify/Makefile \ +lib/jit/Makefile \ +lib/test/Makefile \ +lib/hash/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/main/gb.pcre.h b/main/gb.pcre.h new file mode 120000 index 00000000..6d534635 --- /dev/null +++ b/main/gb.pcre.h @@ -0,0 +1 @@ +../gb.pcre/src/gb.pcre.h \ No newline at end of file diff --git a/main/gbc/Makefile.am b/main/gbc/Makefile.am new file mode 100644 index 00000000..acfd337a --- /dev/null +++ b/main/gbc/Makefile.am @@ -0,0 +1,72 @@ +AM_CFLAGS += -I$(top_srcdir)/share +AM_CFLAGS_OPT += -I$(top_srcdir)/share + +bin_PROGRAMS = gbc3 gba3 gbi3 +noinst_PROGRAMS = gbcm3 +##noinst_LTLIBRARIES = libgbcopt.la + +##libgbcopt_la_CFLAGS= -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS_OPT) + +gbc3_LDADD = @C_LIB@ @MATH_LIB@ +gbc3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS_OPT) + +gbi3_LDADD = @C_LIB@ @DL_LIB@ +gbi3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS_OPT) + +gba3_LDADD = @C_LIB@ +gba3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS_OPT) + +gbcm3_LDADD = @C_LIB@ + +gbc3_SOURCES = \ + gb_error.h gb_error.c \ + gb_alloc.c gb_array.c \ + gbc_class.h gbc_class.c \ + gbc_read.h gbc_read.c \ + gbc_preprocess.h gbc_preprocess.c \ + gbc_type.h gbc_type.c \ + gbc_compile.h gbc_compile.c \ + gbc_header.h gbc_header.c \ + gbc_help.h gbc_help.c \ + gbc_output.h gbc_output.c \ + gbc_trans.h gbc_trans_expr.c gbc_trans_tree.c gbc_trans_ctrl.c gbc_trans_subr.c gbc_trans_const.c \ + gbc_reserved.c \ + gb_buffer.c \ + gbc_dump.c gbc_code.c gbc_trans.c gbc_trans_code.c \ + gbc_pcode.c \ + gb_file.h gb_file.c \ + gbc_form.h gbc_form.c gbc_form_webpage.c \ + gb_str.h gb_str.c \ + gbc_chown.h gbc_chown.c \ + gb_common.c \ + gbc_arch.c \ + gb_table.c \ + gbc.c + +gba3_SOURCES = \ + gb_error.h gb_error.c \ + gb_alloc.c gb_array.c \ + gb_table.c \ + gb_str.h gb_str.c \ + gb_file.h gb_file.c \ + gbc_archive.h gbc_archive.c \ + gbc_chown.h gbc_chown.c \ + gb_common.c \ + gbc_arch.c \ + gba.c + +gbi3_SOURCES= \ + gb_error.h gb_error.c \ + gb_alloc.c gb_array.c \ + gb_str.h gb_str.c \ + gb_file.h gb_file.c \ + gb_table.c \ + gb_common.c \ + gbc_arch.c \ + gbi.c + +##libgbcopt_la_SOURCES = \ +## gb_table.c + +gbcm3_SOURCES = \ + gbc_reserved_make.c diff --git a/main/gbc/gb_alloc.c b/main/gbc/gb_alloc.c new file mode 100644 index 00000000..a634b72a --- /dev/null +++ b/main/gbc/gb_alloc.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_alloc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_alloc_temp.h" diff --git a/main/gbc/gb_array.c b/main/gbc/gb_array.c new file mode 100644 index 00000000..6c300c86 --- /dev/null +++ b/main/gbc/gb_array.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_array_temp.h" + diff --git a/main/gbc/gb_buffer.c b/main/gbc/gb_buffer.c new file mode 100644 index 00000000..84269fde --- /dev/null +++ b/main/gbc/gb_buffer.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_buffer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_buffer_temp.h" + diff --git a/main/gbc/gb_common.c b/main/gbc/gb_common.c new file mode 100644 index 00000000..f906f663 --- /dev/null +++ b/main/gbc/gb_common.c @@ -0,0 +1,32 @@ +/*************************************************************************** + + gb_common.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_C + +#include "gb_common_case_temp.h" +#include "gb_common_buffer_temp.h" +#include "gb_common_swap_temp.h" + +void COMMON_init(void) +{ +} diff --git a/main/gbc/gb_error.c b/main/gbc/gb_error.c new file mode 100644 index 00000000..186c4e5c --- /dev/null +++ b/main/gbc/gb_error.c @@ -0,0 +1,289 @@ +/*************************************************************************** + + gb_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ERROR_C + +#include "gb_common.h" +#include + +#include "gb_buffer.h" +#include "gb_error.h" + + +ERROR_INFO ERROR_info; +bool ERROR_translate = FALSE; + +static const char *_message[] = +{ + /* 0 E_UNKNOWN */ "Unknown error", + /* 1 E_MEMORY */ "Out of memory", + /* 2 E_OPEN */ "Cannot open file: &1: &2", + /* 3 E_READ */ "Cannot read file: &1", + /* 4 E_SYNTAX */ "Syntax error", + /* 5 E_UNEXPECTED */ "Unexpected &1", + /* 6 E_EXPECTED */ "&1 expected", + /* 7 E_MISSING */ "Missing &1", + /* 8 E_SYNTAX_MISSING */ "Syntax error. Missing &1", + /* 9 E_TOOLONG */ "File name is too long", + /* 10 E_BADARG */ "Bad argument", + NULL +}; + +ERROR_CONTEXT *ERROR_current = NULL; + + +#if 0 +void ERROR_enter(ERROR_CONTEXT *err) +{ + CLEAR(err); + err->prev = ERROR_current; + ERROR_current = err; +} + + +void ERROR_leave(ERROR_CONTEXT *err) +{ + if (err->prev != ERROR_LEAVE_DONE) + { + /*ERROR_panic("ERROR_leave already done");*/ + + ERROR_current = err->prev; + err->prev = ERROR_LEAVE_DONE; + } +} +#endif + +char *ERROR_get(void) +{ + /* + if (code > 0 && code < 256) + return strerror(code); + else + return ERROR_Message[code - 256]; + */ + return strerror(errno); +} + + +void _add_char(uchar c, int *n) +{ + if (*n >= MAX_ERROR_MSG) + return; + + ERROR_info.msg[(*n)++] = c; +} + +void _add_string(const char *s, int *n) +{ + while (*s) + { + _add_char(*s, n); + s++; + } +} + +void ERROR_define(const char *pattern, const char *arg[]) +{ + int i, n; + uchar c; + bool subst; + + if ((intptr_t)pattern > 0 && (intptr_t)pattern < 256) + { + ERROR_info.code = (int)(intptr_t)pattern; + pattern = _message[(int)(intptr_t)pattern]; + } + else + ERROR_info.code = -1; + + n = 0; + + subst = FALSE; + + if (ERROR_translate) + { + int nsubst = 0; + + _add_string(pattern, &n); + + for (;;) + { + c = *pattern++; + if (c == 0) + break; + + if (subst) + { + if (c >= '1' && c <= '4') + { + c -= '0'; + if (c > nsubst) nsubst = c; + } + subst = FALSE; + } + else + { + if (c == '&') + subst = TRUE; + } + } + + for (i = 0; i < nsubst; i++) + { + _add_char('\t', &n); + _add_string(arg[i], &n); + } + } + else + { + for (;;) + { + c = *pattern++; + if (c == 0) + break; + + if (subst) + { + if (c >= '1' && c <= '4') + _add_string(arg[c - '1'], &n); + else + { + _add_char('&', &n); + _add_char(c, &n); + } + subst = FALSE; + } + else + { + if (c == '&') + subst = TRUE; + else + _add_char(c, &n); + } + } + } + + _add_char(0, &n); +} + +void PROPAGATE() +{ + ERROR_CONTEXT *err; + + /* + if (_must_free_index) + { + for (i = 0; i < _must_free_index; i++) + OBJECT_UNREF(&_must_free[i], "PROPAGATE"); + + _must_free_index = 0; + } + */ + + if (ERROR_current == NULL) + ERROR_panic("Cannot propagate error. No error handler."); + + err = ERROR_current; + ERROR_leave(ERROR_current); + longjmp(err->env, 1); +} + +void THROW(const char *code, ...) +{ + va_list args; + int i; + const char *arg[4]; + + va_start(args, code); + + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, const char *); + + ERROR_define(code, arg); + PROPAGATE(); +} + +void ERROR_panic(const char *error, ...) +{ + va_list args; + + va_start(args, error); + + fflush(NULL); + + fprintf(stderr, "\n" + "** INTERNAL ERROR **\n" + ); + vfprintf(stderr, error, args); + fprintf(stderr, "\n"); + if (ERROR_info.code) + { + fprintf(stderr, "\n"); + ERROR_print(); + fprintf(stderr, "\n"); + } + fprintf(stderr, "** Program aborting... Sorry... :-(\n\n"); + abort(); +} + + +void ERROR_print_at(FILE *where) +{ + fprintf(where, "%s\n", ERROR_info.msg); +} + +void ERROR_print(void) +{ + ERROR_print_at(stderr); +} + +void TRACE_where(void) +{ +} + +void ERROR_warning(const char *warning, ...) +{ + va_list args; + + va_start(args, warning); + + fflush(NULL); + + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": warning: "); + vfprintf(stderr, warning, args); + putc('\n', stderr); +} + +void ERROR_fail(const char *error, ...) +{ + va_list args; + + va_start(args, error); + + fflush(NULL); + + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": "); + vfprintf(stderr, error, args); + putc('\n', stderr); + exit(1); +} diff --git a/main/gbc/gb_error.h b/main/gbc/gb_error.h new file mode 100644 index 00000000..dfcfb7ce --- /dev/null +++ b/main/gbc/gb_error.h @@ -0,0 +1,129 @@ +/*************************************************************************** + + gb_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_H +#define __GB_ERROR_H + +#include +#include + +#include "gb_limit.h" + +#define E_UNKNOWN ((const char *)0) +#define E_MEMORY ((const char *)1) +#define E_OPEN ((const char *)2) +#define E_READ ((const char *)3) +#define E_SYNTAX ((const char *)4) +#define E_UNEXPECTED ((const char *)5) +#define E_EXPECTED ((const char *)6) +#define E_MISSING ((const char *)7) +#define E_SYNTAX_MISSING ((const char *)8) +#define E_TOOLONG ((const char *)9) +#define E_BADARG ((const char *)10) + +typedef + struct { + int code; + char msg[MAX_ERROR_MSG + 1]; + } + ERROR_INFO; + +typedef + struct _ERROR { + struct _ERROR *prev; + int code; + jmp_buf env; + } + ERROR_CONTEXT; + +#ifndef __GB_ERROR_C +EXTERN ERROR_INFO ERROR_info; +EXTERN bool ERROR_translate; +EXTERN ERROR_CONTEXT *ERROR_current; +#endif + + +#define ERROR_LEAVE_DONE ((ERROR_CONTEXT *)-1) + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_CONTEXT *__err = &__err_context; \ + ERROR_enter(__err); \ + __err->code = setjmp(__err->env); \ + if (__err->code == 0) + +#define FINALLY + +#define CATCH \ + if (__err->code != 0 && ((__err->code = 0), TRUE)) + +#define CATCH_GET(get_it) \ + if (__err->code != 0 && ((get_it = __err->code), TRUE) && ((__err->code = 0), TRUE)) + +#define END_TRY \ + if (__err->code == 0) \ + ERROR_leave(__err); \ + else \ + PROPAGATE(); \ + } \ + } + +#define ERROR_enter(_err) \ +do { \ + _err->prev = ERROR_current; \ + _err->code = 0; \ + ERROR_current = _err; \ +} while(0) + +#define ERROR_leave(_err) \ +do { \ + if (_err->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_CONTEXT *_prev = _err->prev; \ + _err->prev = ERROR_LEAVE_DONE; \ + ERROR_current = _prev; \ + } \ +} while(0) + +#define ERROR_clear() (ERROR_info.code = 0) + +char *ERROR_get(void); + +void ERROR_define(const char *pattern, const char *arg[]); + +void PROPAGATE() NORETURN; +void THROW(const char *code, ...) NORETURN; +void THROW_SYSTEM(int err, const char *path); + +void ERROR_panic(const char *error, ...) NORETURN; + +void ERROR_print(void); +void ERROR_print_at(FILE *where); +void ERROR_warning(const char *warning, ...); +void ERROR_fail(const char *error, ...) NORETURN; + +/*PUBLIC void ERROR_must_free(void *object);*/ + +#endif diff --git a/main/gbc/gb_file.c b/main/gbc/gb_file.c new file mode 100644 index 00000000..4e451258 --- /dev/null +++ b/main/gbc/gb_file.c @@ -0,0 +1,29 @@ +/*************************************************************************** + + gb_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_str.h" + +#include "gb_file_temp.h" diff --git a/main/gbc/gb_file.h b/main/gbc/gb_file.h new file mode 100644 index 00000000..33cd4df9 --- /dev/null +++ b/main/gbc/gb_file.h @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_file.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_file_share.h" diff --git a/main/gbc/gb_str.c b/main/gbc/gb_str.c new file mode 100644 index 00000000..ecd1e271 --- /dev/null +++ b/main/gbc/gb_str.c @@ -0,0 +1,199 @@ +/*************************************************************************** + + gb_str.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_STR_C + +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_alloc.h" + +#include "gb_str.h" + +static char *_free_later = NULL; +static char *_last_str = NULL; +static int _last_len = 0; + + +void STR_vadd(char **str, const char *fmt, va_list args) +{ + va_list copy; + int len, add; + char *new; + + va_copy(copy, args); + add = vsnprintf(NULL, 0, fmt, args); + + if (*str) + len = (*str == _last_str ? _last_len : strlen(*str)); + else + len = 0; + + ALLOC(&new, len + add + 1); + if (*str) strcpy(new, *str); + + vsprintf(&new[len], fmt, copy); + va_end(copy); + + *str = new; + + _last_str = new; + _last_len = len + add; +} + + +void STR_add(char **str, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + STR_vadd(str, fmt, args); + va_end(args); +} + + +char *STR_copy_len(const char *str, int len) +{ + char *cpy; + + ALLOC(&cpy, len + 1); + memcpy(cpy, str, len + 1); + return cpy; +} + + +char *STR_copy(const char *str) +{ + return STR_copy_len(str, strlen(str)); +} + + +static char *str_add(char *d, const char *s) +{ + for(;;) + { + if ((*d = *s) == 0) + break; + + d++; + s++; + } + + return d; +} + + +char *STR_cat(const char *str, ...) +{ + va_list args; + char *cpy; + char *p; + int len = 0; + + va_start(args, str); + + p = (char *)str; + while (p) + { + len += strlen(p); + p = va_arg(args, char *); + } + + va_end(args); + + ALLOC(&cpy, len + 1); + p = cpy; + + va_start(args, str); + + while (str) + { + p = str_add(p, str); + str = va_arg(args, char *); + } + + va_end(args); + + return cpy; +} + + +char *STR_upper(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = toupper(*p); + p++; + } + + return s; +} + + +char *STR_lower(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = tolower(*p); + p++; + } + + return s; +} + + +char *STR_free_later(char *str) +{ + if (_free_later) + STR_free(_free_later); + _free_later = str; + return str; +} + + +char *STR_print(const char *fmt, ...) +{ + va_list args; + char *str = NULL; + + va_start(args, fmt); + STR_vadd(&str, fmt, args); + va_end(args); + return str; +} + diff --git a/main/gbc/gb_str.h b/main/gbc/gb_str.h new file mode 100644 index 00000000..4042ad4a --- /dev/null +++ b/main/gbc/gb_str.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb_str.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_STR_H +#define __GB_STR_H + +#include "gb_alloc.h" + +char *STR_copy(const char *str); +char *STR_copy_len(const char *str, int len); +char *STR_cat(const char *str, ...); +char *STR_upper(const char *str); +char *STR_lower(const char *str); + +#define STR_free(_str) IFREE(_str) +char *STR_free_later(char *str); + +void STR_add(char **str, const char *fmt, ...); +char *STR_print(const char *fmt, ...); +void STR_vadd(char **str, const char *fmt, va_list args); + +#endif diff --git a/main/gbc/gb_table.c b/main/gbc/gb_table.c new file mode 100644 index 00000000..8888f66d --- /dev/null +++ b/main/gbc/gb_table.c @@ -0,0 +1,26 @@ +/*************************************************************************** + + gb_table.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common_case.h" +#include "gb_table_temp.h" + diff --git a/main/gbc/gba.c b/main/gbc/gba.c new file mode 100644 index 00000000..f32eaa87 --- /dev/null +++ b/main/gbc/gba.c @@ -0,0 +1,487 @@ +/*************************************************************************** + + gba.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBA_C + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_array.h" +#include "gb_common_buffer.h" + +#include "gbc_archive.h" + +#if HAVE_GETOPT_LONG +static struct option Long_options[] = +{ + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'h' }, + { "swap", 0, NULL, 's' }, + { "verbose", 0, NULL, 'v' }, + { "output", 1, NULL, 'o' }, + { "extract", 1, NULL, 'x' }, + { 0 } +}; +#endif + +static char **path_list; +static int path_current; + +static const char *allowed_hidden_files[] = { ".gambas", ".info", ".list", ".test", ".lang", ".action", ".connection", ".component", ".public", NULL }; +//static const char *remove_ext_root[] = { "module", "class", "form", "gambas", NULL }; +static const char *remove_ext_lang[] = { "pot", "po", NULL }; + +static bool _extract = FALSE; +static char *_extract_file = NULL; + +static bool _list_all = FALSE; + +static char *_archive; + +static void print_version() +{ + #ifdef TRUNK_VERSION + printf(VERSION " " TRUNK_VERSION "\n"); + #else /* no TRUNK_VERSION */ + printf(VERSION "\n"); + #endif +} + +static void print_title() +{ + printf("\nGambas archiver version "); + print_version(); +} + +static void get_arguments(int argc, char **argv) +{ + int opt; + #if HAVE_GETOPT_LONG + int index = 0; + #endif + + for(;;) + { + #if HAVE_GETOPT_LONG + opt = getopt_long(argc, argv, "vVLhso:x:l:", Long_options, &index); + #else + opt = getopt(argc, argv, "vVLhso:x:l:"); + #endif + + if (opt < 0) break; + + switch (opt) + { + case 'V': + print_version(); + exit(0); + + case 'v': + ARCH_verbose = TRUE; + break; + + case 's': + ARCH_swap = TRUE; + break; + + case 'o': + ARCH_define_output(optarg); + break; + + case 'x': + _archive = optarg; + _extract = TRUE; + break; + + case 'l': + _archive = optarg; + _list_all = TRUE; + break; + + case 'L': + print_version(); + printf(COPYRIGHT); + exit(0); + + case 'h': case '?': + print_title(); + printf( + "\nCreate a standalone one-file executable from a Gambas project.\n" + "\n gba" GAMBAS_VERSION_STRING " [options] []\n" + "\nExtract a specific file from a Gambas executable (-x option).\n" + "\n gba" GAMBAS_VERSION_STRING " -x \n" + "\nList all files included in a Gambas executable (-l option).\n" + "\n gba" GAMBAS_VERSION_STRING " -l \n\n" + "Options:" + #if HAVE_GETOPT_LONG + "\n\n" + " -h --help display this help\n" + " -L --license display license\n" + " -o --output=ARCHIVE archive path [/.gambas]\n" + " -s --swap swap endianness\n" + " -v --verbose verbose output\n" + " -V --version display version\n" + " -x --extract=ARCHIVE extract a specific file from the archive\n" + " -l --list=ARCHIVE list archive files\n" + #else + " (no long options on this system)\n\n" + " -h display this help\n" + " -L display license\n" + " -o=ARCHIVE archive path [/.gambas]\n" + " -s swap endianness\n" + " -v verbose output\n" + " -V display version\n" + " -x=ARCHIVE extract a specific file from the archive\n" + " -l=ARCHIVE list archie files\n" + #endif + "\n" + ); + + exit(0); + + default: + exit(1); + + } + } + + if (optind < (argc - 1)) + { + fprintf(stderr, "gba: too many arguments.\n"); + exit(1); + } + + if (_extract) + { + if (optind == argc) + { + fprintf(stderr, "gba: not enough arguments.\n"); + exit(1); + } + _extract_file = argv[optind]; + } + else if (_list_all) + { + // everything is ok + } + else + { + if (optind == argc) + ARCH_define_project(NULL); + else + ARCH_define_project(argv[optind]); + + if (!FILE_exist(ARCH_project)) + { + fprintf(stderr, "gba: project file not found: %s\n", ARCH_project); + exit(1); + } + } +} + + +static void print_resolved_path_rec(ARCH *arch, int n) +{ + static char buffer[16]; + + const char *name = arch->symbol[n].sym.name; + int len = arch->symbol[n].sym.len; + int nrec; + const char *p = NULL; + + if (*name == '/') + { + p = index(name, ':'); + if (p) + { + nrec = p - name - 1; + if (nrec > 0 && nrec < 16) + { + strncpy(buffer, &name[1], nrec); + buffer[nrec] = 0; + nrec = atoi(buffer); + if (nrec > 0 && nrec < arch->header.n_symbol) + { + print_resolved_path_rec(arch, nrec); + putchar('/'); + } + } + } + } + + if (p) + fwrite(p + 1, sizeof(char), len - (p - name) - 1, stdout); + else + fwrite(name, sizeof(char), len, stdout); +} + + +static void print_resolved_path(ARCH *arch, int n) +{ + print_resolved_path_rec(arch, n); + putchar('\n'); +} + + +static void path_add(const char *path) +{ + *((char **)ARRAY_add(&path_list)) = STR_copy(path); +} + + +static void path_init(const char *first) +{ + ARRAY_create(&path_list); + + if (*first) + FILE_chdir(first); + + path_add(FILE_get_current_dir()); + + path_current = 0; +} + + +static void path_exit(void) +{ + ARRAY_delete(&path_list); +} + + +static int path_count(void) +{ + return ARRAY_count(path_list); +} + + +int main(int argc, char **argv) +{ + const char *path; + int nfile; + struct dirent **filelist; + struct dirent *dirent; + char *file_name; + const char *file; + struct stat info; + const char *ext; + int len; + const char **p; + int len_prefix; + const char **remove_ext; + ARCH *arch; + ARCH_FIND find; + int i; + + get_arguments(argc, argv); + COMMON_init(); + + TRY + { + if (_extract) // Extract a file from an archive + { + arch = ARCH_open(_archive); + + if (ARCH_find(arch, _extract_file, 0, &find)) + fprintf(stderr, "gba: file not found in archive\n"); + else + fwrite(&arch->addr[find.pos], sizeof(char), find.len, stdout); + + ARCH_close(arch); + } + else if (_list_all) + { + arch = ARCH_open(_archive); + + for (i = 0; i < arch->header.n_symbol; i++) + print_resolved_path(arch, i); + + ARCH_close(arch); + } + else // Create an archive + { + ARCH_init(); + + file = FILE_get_dir(ARCH_project); + len_prefix = strlen(file); + path_init(file); + + /* .startup and .project file always first ! */ + + path = FILE_cat(FILE_get_dir(ARCH_project), ".startup", NULL); + if (FILE_exist(path)) ARCH_add_file(path); + + path = FILE_cat(FILE_get_dir(ARCH_project), ".project", NULL); + if (FILE_exist(path)) ARCH_add_file(path); + + for(;;) + { + if (path_current >= path_count()) + break; + + path = path_list[path_current++]; + + if (chdir(path) != 0) + { + fprintf(stderr, "gba: warning: cannot change to directory: %s\n", path); + goto _NEXT_PATH; + } + + filelist = NULL; + nfile = scandir(path, &filelist, NULL, alphasort); + + if (nfile < 0) + { + fprintf(stderr, "gba: warning: cannot scan directory: %s\n", path); + goto _NEXT_PATH; + } + + for (i = 0; i < nfile; i++) + { + dirent = filelist[i]; + + file_name = dirent->d_name; + len = strlen(file_name); + + if (*file_name == '.') + { + for (p = allowed_hidden_files; *p; p++) + { + if (strcmp(file_name, *p) == 0) + break; + } + + if (*p == NULL) + continue; + } + + if (file_name[len - 1] == '~') + continue; + + //if (strcmp(file_name, ARCH_project_name) == 0) + // continue; + + if ((len == 4) && (strncmp(file_name, "core", 4) == 0)) + continue; + + if ((len > 5) && (strncmp(file_name, "core.", 5) == 0)) + continue; + + if ((len > 7) && (strncmp(file_name, "vgcore.", 5) == 0)) + continue; + + if ((len > 10) && (strncmp(file_name, "callgrind.", 5) == 0)) + continue; + + file = FILE_cat(path, file_name, NULL); + + // Do not put the archive file inside itself. + if (!strcmp(file, ARCH_output)) + continue; + + if (stat(file_name, &info)) + { + fprintf(stderr, "gba: warning: cannot stat file: %s\n", file); + continue; + } + + if (S_ISDIR(info.st_mode)) + { + if (strcmp(file_name, "CVS") == 0) + continue; + + path_add(file); + ARCH_add_file(file); + } + else + { + ext = FILE_get_ext(file_name); + + //printf("path = %s\n", &path[len_prefix]); + + //if (path[len_prefix] == 0) + // remove_ext = remove_ext_root; + if (strcmp(&path[len_prefix], "/.lang") == 0) + remove_ext = remove_ext_lang; + else + remove_ext = 0; + + if (remove_ext) + { + for (p = remove_ext; *p; p++) + { + if (strcasecmp(ext, *p) == 0) + break; + } + + if (*p != NULL) + continue; + } + + if (strcmp(ext, "gambas") == 0) + continue; + + ARCH_add_file(file); + } + + free(dirent); + } + + _NEXT_PATH: + if (filelist != NULL) + free(filelist); + + FREE((char **)&path); + } + + path_exit(); + + ARCH_exit(); + /*MEM_check();*/ + } + } + CATCH + { + fflush(NULL); + fprintf(stderr, "gba: ERROR: "); + ERROR_print(); + exit(1); + } + END_TRY + + return 0; +} + diff --git a/main/gbc/gbc.c b/main/gbc/gbc.c new file mode 100644 index 00000000..ac7d71a9 --- /dev/null +++ b/main/gbc/gbc.c @@ -0,0 +1,864 @@ +/*************************************************************************** + + gbc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_C + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_common_buffer.h" +#include "gb_system.h" + +#include "gbc_compile.h" + +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gbc_form.h" +#include "gbc_trans.h" +#include "gbc_header.h" +#include "gbc_output.h" + +#include "gb_system_temp.h" + +typedef + void (*BACKGROUND_TASK)(const char *); + +typedef + struct { + const char *option; + bool *variable; + } + COMPILER_FLAGS; + +static bool main_debug = FALSE; +static bool main_exec = FALSE; +static bool main_compile_all = FALSE; +static bool main_trans = FALSE; +static bool main_warnings = FALSE; +static bool main_swap = FALSE; + +static bool _opt_no_old_read_syntax = FALSE; +static bool _opt_check_prefix = FALSE; +static bool _opt_public_module = FALSE; +static bool _opt_public_control = FALSE; + +static char *_convert_form = NULL; + +#if HAVE_GETOPT_LONG +static struct option _long_options[] = +{ + { "debug", 0, NULL, 'g' }, + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'h' }, + { "license", 0, NULL, 'L' }, + { "verbose", 0, NULL, 'v' }, + { "translate", 0, NULL, 't' }, + { "swap", 0, NULL, 's' }, + { "all", 0, NULL, 'a' }, + { "translate-errors", 0, NULL, 'e' }, + { "warnings", 0, NULL, 'w' }, + + { "jobs", 1, NULL, 'j' }, + { "root", 1, NULL, 'r' }, + { "default-namespace", 1, NULL, 'n' }, + { "form", 1, NULL, 'F' }, + + //{ "no-old-read-write-syntax", 0, &_opt_no_old_read_syntax, 1 }, + //{ "check-prefix", 0, &_opt_check_prefix, 1}, + + { 0 } +}; +#endif + +static COMPILER_FLAGS _compiler_flags[] = { + { "no-old-read-write-syntax", &_opt_no_old_read_syntax }, + { "check-prefix", &_opt_check_prefix }, + { "public-module", &_opt_public_module }, + { "public-control", &_opt_public_control }, + { NULL } +}; + +//static char *main_class_file = NULL; + +static char **_files = NULL; +static bool make_test = FALSE; + +static uint _ntask_max = 0; +static uint _ntask = 0; +static bool _child = FALSE; + +static void print_version() +{ + #ifdef TRUNK_VERSION + printf(VERSION " " TRUNK_VERSION "\n"); + #else /* no TRUNK_VERSION */ + printf(VERSION "\n"); + #endif +} + +static void print_title() +{ + printf("\nGambas compiler version "); + print_version(); +} + +static void get_arguments(int argc, char **argv) +{ + const char *dir; + int opt; + #if HAVE_GETOPT_LONG + int index = 0; + #endif + COMPILER_FLAGS *cf; + + for(;;) + { + #if HAVE_GETOPT_LONG + opt = getopt_long(argc, argv, "gxvaVhj:Lwtser:f:n:F:", _long_options, &index); + #else + opt = getopt(argc, argv, "gxvaVhj:Lwtser:f:n:F:"); + #endif + if (opt < 0) break; + + switch (opt) + { + case 'V': + + print_version(); + exit(0); + + case 'g': + + main_debug = TRUE; + break; + + case 'x': + + main_exec = TRUE; + break; + + case 'v': + + COMP_verbose = TRUE; + break; + + case 'a': + + main_compile_all = TRUE; + break; + + case 't': + + main_trans = TRUE; + break; + + case 'w': + + main_warnings = TRUE; + break; + + case 's': + + main_swap = TRUE; + break; + + case 'r': + + if (COMP_root) + ERROR_fail("option '-r' already specified."); + COMP_root = STR_copy(optarg); + break; + + case 'e': + + ERROR_translate = TRUE; + break; + + case 'L': + + print_title(); + printf(COPYRIGHT); + exit(0); + + case 'h': case '?': + + print_title(); + + printf( + "\nCompile Gambas projects into architecture-independent bytecode.\n" + "\n gbc" GAMBAS_VERSION_STRING " [options] []\n" + "\nConvert a form file into Gambas code.\n" + "\n gbc" GAMBAS_VERSION_STRING " -F
    \n\n" + "Options:" + #if HAVE_GETOPT_LONG + "\n\n" + " -a --all compile all files\n" + " -e --translate-errors display translatable error messages\n" + " -F --form convert a form file into code and print it\n" + " -g --debug add debugging information\n" + " -h --help display this help\n" + " -j --jobs number of background jobs (default: %d)\n" + " -L --license display license\n" + " -n --default-namespace default namespace for exported classes\n" + " -r --root gives the gambas installation directory\n" + " -s --swap swap endianness\n" + " -t --translate output translation files and compile them if needed\n" + " -v --verbose verbose output\n" + " -V --version display version\n" + " -w --warnings display warnings\n" + " -x --exec executable mode (define the 'Exec' preprocessor constant and remove assertions)\n" + "\nCompiler flags:\n\n" + " -f check-prefix check the prefix of variables if warnings are enable\n" + " -f public-module module symbols are public by default\n" + " -f public-control form controls are public\n" + #else + " (no long options on this system)\n\n" + " -a compile all files\n" + " -e display translatable error messages\n" + " -F convert a form file into code and print it\n" + " -g add debugging information\n" + " -h display this help\n" + " -j number of background jobs (default: %d)\n" + " -L display license\n" + " -n default namespace for exported classes\n" + " -r gives the gambas installation directory\n" + " -s swap endianness\n" + " -t output translation files and compile them if needed\n" + " -v verbose output\n" + " -V display version\n" + " -w display warnings\n" + " -x executable mode (define the 'Exec' preprocessor constant and remove assertions)\n" + "\nCompiler flags:\n\n" + " -f check-prefix check the prefix of variables if warnings are enable\n" + " -f public-module module symbols are public by default\n" + " -f public-control form controls are public\n" + #endif + "\n", + SYSTEM_get_cpu_count()); + + exit(0); + + case 'j': + + _ntask_max = atoi(optarg); + if (_ntask_max < 0 || _ntask_max > 16) + ERROR_fail("Incorrect number of jobs."); + break; + + case 'f': + + cf = _compiler_flags; + while (cf->option) + { + if (strcmp(optarg, cf->option) == 0) + { + *cf->variable = TRUE; + break; + } + cf++; + } + + break; + + case 'n': + + if (COMP_default_namespace) + ERROR_fail("option '-n' already specified."); + COMP_default_namespace = STR_copy(optarg); + break; + + case 'F': + + if (_convert_form) + ERROR_fail("option '-F' already specified."); + _convert_form = STR_copy(optarg); + break; + + default: + exit(1); + + } + } + + if (optind < (argc - 1)) + ERROR_fail("too many arguments"); + + if (_convert_form) + return; + + if (optind < argc) + FILE_chdir(argv[optind]); + + dir = FILE_get_current_dir(); + if (!dir) + ERROR_fail("no current directory"); + + COMP_dir = STR_copy(dir); + COMP_project = STR_copy(FILE_cat(COMP_dir, ".project", NULL)); + + if (!FILE_exist(COMP_project)) + ERROR_fail("project file not found: %s", COMP_project); +} + + +static void compile_file(const char *file) +{ + int i, lock; + time_t time_src, time_form, time_pot, time_output; + char *source; + + COMPILE_begin(file, main_trans, main_debug); + + if (!main_compile_all) + { + if (FILE_exist(JOB->output)) + { + time_src = FILE_get_time(JOB->name); + time_output = FILE_get_time(JOB->output); + + if (JOB->form) + time_form = FILE_get_time(JOB->form); + else + time_form = time_src; + + if (main_trans) + time_pot = FILE_get_time(JOB->tname); + else + time_pot = time_src; + + if (time_src <= time_output && time_src <= time_pot && time_form <= time_output) + goto _FIN; + } + } + + COMPILE_alloc(); + + JOB->exec = main_exec; + JOB->warnings = main_warnings; + JOB->check_prefix = main_warnings && _opt_check_prefix; + JOB->swap = main_swap; + JOB->public_module = _opt_public_module; + JOB->no_old_read_syntax = _opt_no_old_read_syntax; + //JOB->class_file = main_class_file; + + if (COMP_verbose) + { + fputc('\n', stderr); + for (i = 1; i <= 9; i++) + fprintf(stderr, "--------"); + fprintf(stderr, "\nCompiling %s...\n", FILE_get_name(JOB->name)); + } + + JOB->first_line = 1; + + if (JOB->form) + { + JOB->first_line = FORM_FIRST_LINE; + BUFFER_add(&JOB->source, "#Line " FORM_FIRST_LINE_STRING "\n", -1); + + BUFFER_create(&source); + BUFFER_load_file(&source, JOB->form); + BUFFER_add(&source, "\n\0", 2); + + FORM_set_target(&JOB->source); + + switch (JOB->family->type) + { + case FORM_WEBPAGE: + FORM_webpage(source); + break; + + case FORM_NORMAL: + default: + FORM_do(source, FALSE, _opt_public_control); + break; + } + + BUFFER_delete(&source); + + BUFFER_add(&JOB->source, "#Line 1\n", -1); + } + + COMPILE_load(); + BUFFER_add(&JOB->source, "\n\0", 2); + + JOB->step = JOB_STEP_READ; + READ_do(); + + #ifdef DEBUG + TABLE_print(JOB->class->table, TRUE); + #endif + + JOB->step = JOB_STEP_CODE; + HEADER_do(); + TRANS_code(); + + #ifdef DEBUG + TABLE_print(JOB->class->string, FALSE); + #endif + + JOB->step = JOB_STEP_OUTPUT; + OUTPUT_do(main_swap); + + lock = COMPILE_lock_file(".gbc.lock"); + CLASS_export(); + COMPILE_unlock_file(lock); + + COMPILE_free(); + +_FIN: + COMPILE_end(); +} + + +static void wait_for_task(void) +{ + int status; + pid_t pid; + + if (COMP_verbose) + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": wait for tasks...\n"); + + pid = wait(&status); + + if (pid < 0) + ERROR_fail("wait() fails: &1", strerror(errno)); + + if (!WIFEXITED(status)) + { + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": the background job %d has crashed with signal %d", pid, WTERMSIG(status)); + exit(status); + } + + if (WEXITSTATUS(status)) + { + while (_ntask > 0) + { + wait(&status); + _ntask--; + } + exit(1); + } + + if (COMP_verbose) + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": end task %d\n", pid); + _ntask--; +} + + +static void wait_for_all_task(void) +{ + if (_ntask_max > 1) + { + while (_ntask > 0) + wait_for_task(); + } +} + + +/*static void kill_tasks(void) +{ + if (_ntask > 0) + { + if (COMP_verbose) + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": kill pending tasks\n"); + kill(-getpid(), SIGTERM); + } +}*/ + +static void run_task(BACKGROUND_TASK func, const char *arg) +{ + pid_t pid; + + if (_ntask_max <= 1) + { + (*func)(arg); + return; + } + + while (_ntask >= _ntask_max) + wait_for_task(); + + pid = fork(); + + if (pid < 0) + THROW("Failed to run child process: &1", strerror(errno)); + + if (pid == 0) + { + _child = TRUE; + _ntask = 0; + + if (setpgid(0, getppid()) < 0) + ERROR_fail("[%d] setpgid to %d failed: %s", getpid(), getppid(), strerror(errno)); + + (*func)(arg); + exit(0); + } + else + { + if (COMP_verbose) + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": start task: [%d] '%s'\n", pid, arg); + _ntask++; + } +} + + +static int compare_path(char **a, char **b) +{ + return strcmp(*a, *b); +} + +/*static bool check_cvs_directory(const char *dir) +{ + int len; + char *buffer; + struct stat info; + + len = strlen(dir); + buffer = alloca(len + 32); + strcpy(buffer, dir); + + strcpy(&buffer[len], "Root"); + if (stat(buffer, &info)) + return FALSE; + + strcpy(&buffer[len], "Entries"); + if (stat(buffer, &info)) + return FALSE; + + strcpy(&buffer[len], "Repository"); + if (stat(buffer, &info)) + return FALSE; + + return TRUE; +}*/ + +static void fill_files(const char *root, bool recursive) +{ + DIR *dir; + char *path; + struct dirent *dirent; + char *file_name; + const char *file; + struct stat info; + const char *ext; + + path = STR_copy(root); + + dir = opendir(path); + if (!dir) + ERROR_fail("cannot browse directory: %s", path); + + while ((dirent = readdir(dir)) != NULL) + { + file_name = dirent->d_name; + if (*file_name == '.') + continue; + + file = FILE_cat(path, file_name, NULL); + + if (stat(file, &info)) + { + ERROR_warning("cannot stat file: %s", file); + continue; + } + + if (S_ISDIR(info.st_mode)) + { + if (recursive) + { + if (*file_name == 'C') + { + if (strcmp(file_name, "CVS") == 0) // && check_cvs_directory(file)) + continue; + if (strcmp(file_name, "CVSROOT") == 0) + continue; + } + fill_files(file, TRUE); + } + } + else + { + ext = FILE_get_ext(file); + + if ((strcmp(ext, "module") == 0) + || (strcmp(ext, "class") == 0)) + { + *((char **)ARRAY_add(&_files)) = STR_copy(file); + } + else if (strcmp(ext, "test") == 0) + { + *((char **)ARRAY_add(&_files)) = STR_copy(file); + make_test = TRUE; + } + } + } + + closedir(dir); + STR_free(path); +} + + +static void init_files(const char *first) +{ + bool recursive; + const char *name; + const char *ext; + int i, n; + bool has_test; + + ARRAY_create(&_files); + + if (*first) + FILE_chdir(first); + + recursive = chdir(".src") == 0; + fill_files(FILE_get_current_dir(), recursive); + if (recursive) FILE_chdir(".."); + + // Sort paths + n = ARRAY_count(_files); + qsort(_files, n, sizeof(*_files), (int (*)(const void *, const void *))compare_path); + + // Add the classes to the list of classes + has_test = FALSE; + for (i = 0; i < n; i++) + { + if (!has_test) + { + ext = FILE_get_ext(_files[i]); + if (strcmp(ext, "test") == 0) + { + has_test = TRUE; + COMPILE_add_component("gb.test"); + } + } + name = FILE_get_basename(_files[i]); + COMPILE_add_class(name, strlen(name)); + } + + // End the list of classes + COMPILE_end_class(); +} + + +static void exit_files(void) +{ + int i; + + for (i = 0; i < ARRAY_count(_files); i++) + STR_free(_files[i]); + + ARRAY_delete(&_files); +} + + +static void compile_lang(const char *file_po) +{ + const char *file_log; + char *file_mo; + time_t time_po, time_mo; + char *cmd; + int ret; + + time_po = FILE_get_time(file_po); + + if (time_po == ((time_t)-1)) + return; + + file_mo = (char *)FILE_set_ext(file_po, "mo"); + + if (!main_compile_all) + { + time_mo = FILE_get_time(file_mo); + if (time_mo >= time_po) + return; + } + + file_mo = STR_copy(file_mo); + file_log = FILE_set_ext(file_po, "log"); + unlink(file_log); + unlink(file_mo); + + // Shell "msgfmt -o " & Shell$(sPath) & " " & Shell(sTrans) Wait + if (COMP_verbose) + { + cmd = STR_print("msgfmt -o %s %s 2>&1", file_mo, file_po); + fprintf(stderr, "running: %s\n", cmd); + } + else + cmd = STR_print("msgfmt -o %s %s > %s 2>&1", file_mo, file_po, file_log); + + ret = system(cmd); + + if (!WIFEXITED(ret) || WEXITSTATUS(ret)) + ERROR_warning("unable to compile translation file with 'msgfmt': %s", file_po); + + if (FILE_get_size(file_log) == 0) + unlink(file_log); + + STR_free(cmd); + STR_free(file_mo); +} + + +static void compile_all_lang(void) +{ + DIR *dir; + char *path; + struct dirent *dirent; + char *file_name; + int i; + char c; + + path = STR_copy(FILE_cat(COMP_dir, ".lang", NULL)); + FILE_chdir(path); + + dir = opendir("."); + if (!dir) + { + ERROR_warning("cannot browse directory: %s", path); + return; + } + + while ((dirent = readdir(dir)) != NULL) + { + file_name = dirent->d_name; + if (*file_name == '.') + continue; + + if (strcmp(FILE_get_ext(file_name), "po")) + continue; + + for (i = 0; i < strlen(file_name); i++) + { + c = file_name[i]; + if (c == '.') + break; + if (!isalnum(c)) + continue; + } + + run_task(compile_lang, file_name); + } + + wait_for_all_task(); + + closedir(dir); + STR_free(path); +} + + +int main(int argc, char **argv) +{ + int i; + + MEMORY_init(); + COMMON_init(); + + TRY + { + get_arguments(argc, argv); + + if (_convert_form) + { + FORM_convert(_convert_form); + STR_free(_convert_form); + } + else + { + if (_ntask_max == 0) + _ntask_max = SYSTEM_get_cpu_count() + 1; + + if (_ntask_max >= 2) + { + if (setpgid(0, 0)) + ERROR_fail("setpgid() fails: %s", strerror(errno)); + } + + COMPILE_init(); + COMP_do_not_lock = FALSE; + + // Remove information files if we are compiling everything + + if (main_compile_all) + { + if (COMP_verbose) + fputs("Removing .info and .list files", stderr); + FILE_chdir(COMP_dir); + FILE_unlink(".info"); + FILE_unlink(".list"); + } + + init_files(COMP_dir); + + for (i = 0; i < ARRAY_count(_files); i++) + run_task(compile_file, _files[i]); + + wait_for_all_task(); + + COMPILE_remove_lock(".gbc.lock"); + COMPILE_remove_lock(".gbc.stderr"); + + exit_files(); + + if (main_trans) + compile_all_lang(); + + COMPILE_exit(_ntask_max == 1); + FILE_exit(); + + puts("OK"); + } + } + CATCH + { + //wait_for_all_task(); + + fflush(NULL); + + COMPILE_print(MSG_ERROR, -1, NULL); + ERROR_print(); + exit(1); + } + END_TRY + + return 0; +} + diff --git a/main/gbc/gbc_arch.c b/main/gbc/gbc_arch.c new file mode 100644 index 00000000..729c88c6 --- /dev/null +++ b/main/gbc/gbc_arch.c @@ -0,0 +1,43 @@ +/*************************************************************************** + + gbc_arch.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_ARCH_C + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" + +#include "gb_arch_temp.h" diff --git a/main/gbc/gbc_archive.c b/main/gbc/gbc_archive.c new file mode 100644 index 00000000..d2cf1231 --- /dev/null +++ b/main/gbc/gbc_archive.c @@ -0,0 +1,444 @@ +/*************************************************************************** + + gbc_archive.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_ARCHIVE_C + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_magic.h" +#include "gb_common_swap.h" +#include "gbc_chown.h" +#include "gbc_archive.h" + +/*#define DEBUG*/ + +#define TEMP_EXEC ".temp.gambas" + +char *ARCH_project; +char *ARCH_project_name; +char *ARCH_output = NULL; +bool ARCH_verbose = FALSE; +bool ARCH_swap = FALSE; + +static int arch_dir_pos; +static TABLE *arch_table; +static FILE *arch_file = NULL; + +#define ARCH_BUFFER_SIZE 4096 +static char *arch_buffer; + +static int pos_start; + +static void write_int(uint val) +{ + if (ARCH_swap) + SWAP_int((int *)&val); + if (fwrite(&val, sizeof(uint), 1, arch_file) < 1) + THROW("Write error"); +} + + +static void write_short(ushort val) +{ + if (ARCH_swap) + SWAP_short((short *)&val); + if (fwrite(&val, sizeof(ushort), 1, arch_file) < 1) + THROW("Write error"); +} + + +static long get_pos(void) +{ + long pos = ftell(arch_file); + if (pos < 0) + THROW("Unable to get file position"); + return (int)pos; // No archive file greater then 2 Go! +} + + +static void write_int_at(int pos, uint val) +{ + int old_pos = get_pos(); + + fseek(arch_file, pos, SEEK_SET); + write_int(val); + fseek(arch_file, old_pos, SEEK_SET); +} + + +static void write_string(const char *str, int len) +{ + if (fwrite(str, sizeof(char), len, arch_file) < len) + THROW("Write error"); +} + + +static void make_executable(void) +{ + const char *err; + struct stat info; + + FILE_chdir(FILE_get_dir(ARCH_output)); + + // If we cannot make the archive executable, just print a warning. gbs creates an executable cache + // inside /tmp, and /tmp may be mounted with the "noexec" flag. + + if (stat(TEMP_EXEC, &info) || chmod(TEMP_EXEC, info.st_mode | S_IXUSR | S_IXGRP | S_IXOTH)) + fprintf(stderr, "gba: warning: cannot change executable permissions\n"); + + FILE_set_owner(TEMP_EXEC, FILE_cat(FILE_get_dir(ARCH_project), ".project", NULL)); + + if (FILE_exist(ARCH_output) && unlink(ARCH_output)) + { + err = "Cannot remove previous executable"; + goto __ERROR; + } + + if (rename(TEMP_EXEC, ARCH_output)) + { + err = "Cannot create executable"; + goto __ERROR; + } + + return; + +__ERROR: + + THROW("Cannot make executable: &1: &2", err, strerror(errno)); +} + + +void ARCH_define_output(const char *path) +{ + STR_free(ARCH_output); + + if (path && *path != '/') + path = FILE_cat(FILE_get_current_dir(), path, NULL); + else if (!path) + path = ""; + + ARCH_output = STR_copy(path); +} + +static void print_separation() +{ + printf("---------------------------------------- ------------------------------ -------- -----\n"); +} + +void ARCH_define_project(const char *project) +{ + char *name; + char *dir; + const char *path; + + if (project == NULL) + project = FILE_get_current_dir(); + + FILE_chdir(project); + dir = STR_copy(FILE_get_current_dir()); + + arch_dir_pos = strlen(dir) + 1; + + path = FILE_cat(dir, ".startup", NULL); + if (FILE_exist(path)) + ARCH_project = STR_copy(path); + else + ARCH_project = STR_copy(FILE_cat(dir, ".project", NULL)); + + name = STR_copy(FILE_get_name(dir)); + + /*ARCH_project_name = STR_copy(FILE_set_ext(name, NULL));*/ + ARCH_project_name = STR_copy(name); + + if (!ARCH_output) + ARCH_define_output(strcat((char *)FILE_cat(dir, ARCH_project_name, NULL), ".gambas")); + + STR_free(name); + STR_free(dir); +} + + +void ARCH_init(void) +{ + TABLE_create(&arch_table, sizeof(ARCH_SYMBOL), TF_NORMAL); + + ALLOC(&arch_buffer, ARCH_BUFFER_SIZE); + + arch_file = fopen(FILE_cat(FILE_get_dir(ARCH_output), TEMP_EXEC, NULL), "w"); + if (arch_file == NULL) + THROW("Cannot create temporary archive file: &1", ARCH_output); + + fputs("#! /usr/bin/env gbr" GAMBAS_VERSION_STRING "\n", arch_file); + + while (get_pos() < 31) + fprintf(arch_file, " "); + fprintf(arch_file, "\n"); + + write_int(ARCH_MAGIC); + write_int(ARCH_VERSION); + + if (ARCH_verbose) + { + print_separation(); + printf("%-40.40s %-30.30s %8s %5s\n", "Path", "Abbrev. path","Size","Index"); + print_separation(); + } + + pos_start = get_pos(); + write_int(0); + write_int(0); + write_int(0); + write_int(0); + + write_int_at(pos_start, get_pos()); +} + + +#if ARCH_VERSION == 2 +static void compress_file_name(const char *src, int lsrc, char **dst, int *ldst) +{ + char *p; + static char tpath[PATH_MAX]; + char tpath2[PATH_MAX]; + int len; + int ind; + + strncpy(tpath, src, lsrc); + tpath[lsrc] = 0; + len = lsrc; + + if (ARCH_verbose) + printf("%-40.40s", tpath); + + for(;;) + { + p = index(tpath + 1, '/'); + if (!p) + break; + + if (!TABLE_find_symbol(arch_table, tpath, p - tpath, &ind)) + { + *p = 0; + THROW("&1: not in archive", tpath); + } + + len = snprintf(tpath2, sizeof(tpath2), "/%d:%s", ind, p + 1); + strcpy(tpath, tpath2); + } + + if (ARCH_verbose) + printf(" %-30.30s", tpath); + + *dst = tpath; + *ldst = len; +} +#endif + +void ARCH_exit(void) +{ + int i; + ARCH_SYMBOL *sym; + int pos_str; + + /* Write strings */ + + write_int_at(pos_start + sizeof(int), get_pos()); + + pos_str = 0; + + for (i = 0; i < TABLE_count(arch_table); i++) + { + sym = (ARCH_SYMBOL *)TABLE_get_symbol(arch_table, i); + write_string(sym->sym.name, sym->sym.len); + } + + /* Write file names */ + + write_int_at(pos_start + sizeof(int) * 2, get_pos()); + + write_int_at(pos_start + sizeof(int) * 3, TABLE_count(arch_table)); + + for (i = 0; i < TABLE_count(arch_table); i++) + { + sym = (ARCH_SYMBOL *)TABLE_get_symbol(arch_table, i); + //write_short((ushort)i); + write_int(pos_str); + write_int(sym->sym.len); + write_int(sym->pos); + write_int(sym->len); + + pos_str += sym->sym.len; + } + + for (i = 0; i < TABLE_count(arch_table); i++) + write_short(arch_table->sort[i]); + + /* Close file */ + + fclose(arch_file); + + make_executable(); + + if (ARCH_verbose) + { + print_separation(); + printf("Writing archive format version %d:\n%s\n", ARCH_VERSION,ARCH_output); + } + + /* Free everything */ + + for (i = 0; i < TABLE_count(arch_table); i++) + STR_free(TABLE_get_symbol(arch_table, i)->name); + + TABLE_delete(&arch_table); + + STR_free(ARCH_output); + STR_free(ARCH_project); + STR_free(ARCH_project_name); + + FREE(&arch_buffer); +} + + +int ARCH_add_file(const char *path) +{ + char *rel_path; + ARCH_SYMBOL *sym; + FILE *file; + struct stat info; + int len, len_read; + + int ind; + + #if ARCH_VERSION == 2 + compress_file_name(&path[arch_dir_pos], strlen(&path[arch_dir_pos]), &rel_path, &len); + rel_path = STR_copy(rel_path); + #else + rel_path = STR_copy(&path[arch_dir_pos]); + len = strlen(rel_path); + #endif + + ind = TABLE_add_symbol(arch_table, rel_path, len); + sym = (ARCH_SYMBOL *)TABLE_get_symbol(arch_table, ind); + sym->pos = get_pos(); + + file = fopen(path, "r"); + if (file == NULL) + THROW("Cannot open file: &1", path); + + fstat(fileno(file), &info); + + if (S_ISDIR(info.st_mode)) + { + sym->pos = -1; + sym->len = 0; + fclose(file); + if (ARCH_verbose) + printf(" %8s", "-"); + } + else + { + sym->len = info.st_size; + + len = sym->len; + while (len > 0) + { + len_read = fread(arch_buffer, 1, ARCH_BUFFER_SIZE, file); + if (len_read > 0) + fwrite(arch_buffer, 1, len_read, arch_file); + + if (len_read < ARCH_BUFFER_SIZE) + { + if (ferror(file)) + THROW("Cannot read file: &1: &2", path, strerror(errno)); + else + break; + } + } + + fclose(file); + + if (ARCH_verbose) + printf(" %8d", sym->len); + } + + if (ARCH_verbose) + printf(" %5d\n", ind); + + return ind; +} + +#if 0 +void ARCH_browse(ARCH *a, void (*found)(const char *path, int64_t size)) +{ + int i; + ARCH_SYMBOL *asym; + SYMBOL *sym; + char *path; + char *temp; + int size; + int ip; + + for (i = 0; i < a->header.n_symbol; i++) + { + asym = &a->symbol[i]; + sym = &asym->sym; + + size = asym->len; + + path = STR_copy_len(sym->name, sym->len); + for(;;) + { + if (*path != '/') + break; + + ip = atoi(&path[1]); + sym = &a->symbol[ip].sym; + + temp = path; + path = STR_copy_len(sym->name, sym->len); + if (path[sym->len - 1] != '/') + STRING_add(&path, "/", 1); + STRING_add(&path, strchr(temp, ':') + 1, 0); + STRING_free(&temp); + } + + (*found)(path, size); + STR_free(path); + } +} +#endif diff --git a/main/gbc/gbc_archive.h b/main/gbc/gbc_archive.h new file mode 100644 index 00000000..2a6fee49 --- /dev/null +++ b/main/gbc/gbc_archive.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gbc_archive.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_ARCHIVE_H +#define __GBC_ARCHIVE_H + +#include "gb_alloc.h" +#include "gb_limit.h" +#include "gb_table.h" +#include "gb_magic.h" +#include "gb_arch.h" + +#ifndef __GBC_ARCHIVE_C + +EXTERN char *ARCH_project; +EXTERN char *ARCH_project_name; +EXTERN char *ARCH_output; +EXTERN bool ARCH_verbose; +EXTERN bool ARCH_swap; + +#endif + +void ARCH_init(void); +void ARCH_exit(void); +void ARCH_define_project(const char *project); +void ARCH_define_output(const char *path); +int ARCH_add_file(const char *path); + +#endif diff --git a/main/gbc/gbc_chown.c b/main/gbc/gbc_chown.c new file mode 100644 index 00000000..9ed63a3a --- /dev/null +++ b/main/gbc/gbc_chown.c @@ -0,0 +1,52 @@ +/*************************************************************************** + + gbc_chown.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_CHOWN_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_chown.h" + +void FILE_set_owner(const char *path, const char *template) +{ + struct stat info; + + if (geteuid()) + return; + + if (stat(template, &info)) + goto __ERROR; + + if (chown(path, info.st_uid, info.st_gid)) + goto __ERROR; + + return; + +__ERROR: + unlink(path); + THROW("Cannot set file owner: &1: &2", path, strerror(errno)); +} diff --git a/main/gbc/gbc_chown.h b/main/gbc/gbc_chown.h new file mode 100644 index 00000000..2d924cfe --- /dev/null +++ b/main/gbc/gbc_chown.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + gbc_chown.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_CHOWN_H +#define __GBC_CHOWN_H + +void FILE_set_owner(const char *path, const char *template); + +#endif diff --git a/main/gbc/gbc_class.c b/main/gbc/gbc_class.c new file mode 100644 index 00000000..9e0e960c --- /dev/null +++ b/main/gbc/gbc_class.c @@ -0,0 +1,1063 @@ +/*************************************************************************** + + gbc_class.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_CLASS_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" + +#include "gb_table.h" +#include "gbc_compile.h" +#include "gb_code.h" + +static int _array_class[17]; + +void CLASS_create(CLASS **result) +{ + CLASS *class; + TRANS_FUNC func; + + ALLOC_ZERO(&class, sizeof(CLASS)); + + ARRAY_create_inc(&class->function, 256); + ARRAY_create(&class->event); + ARRAY_create(&class->prop); + ARRAY_create(&class->ext_func); + ARRAY_create_inc(&class->constant, 256); + ARRAY_create(&class->class); + ARRAY_create_inc(&class->unknown, 256); + ARRAY_create_inc(&class->stat, 256); + ARRAY_create_inc(&class->dyn, 256); + ARRAY_create(&class->array); + ARRAY_create(&class->structure); + ARRAY_create(&class->names); + + TABLE_create_inc(&class->table, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE, 1024); + TABLE_create_inc(&class->string, sizeof(SYMBOL), TF_NORMAL, 1024); + + CLEAR(&func); + TYPE_clear(&func.type); + + func.index = CLASS_add_symbol(class, "@init"); + CLASS_add_function(class, &func); + + func.index = CLASS_add_symbol(class, "@new"); + CLASS_add_function(class, &func); + + class->name = STR_copy(FILE_set_ext(FILE_get_name(JOB->name), NULL)); + class->parent = NO_SYMBOL; + + memset(_array_class, 0, sizeof(_array_class)); + + *result = class; +} + + +static void delete_function(FUNCTION *func) +{ + ARRAY_delete(&func->local); + ARRAY_delete(&func->stat); + if (func->code) + FREE(&func->code); + + if (JOB->debug) + ARRAY_delete(&func->pos_line); + + if (func->param) + FREE(&func->param); +} + + +static void delete_event(EVENT *event) +{ + if (event->param) + FREE(&event->param); +} + + +static void delete_extfunc(EXTFUNC *extfunc) +{ + if (extfunc->param) + FREE(&extfunc->param); +} + + +static void delete_structure(CLASS_STRUCT *structure) +{ + if (structure->field) + ARRAY_delete(&structure->field); +} + + +void CLASS_delete(CLASS **class) +{ + int i; + + if (!*class) + return; + + CLASS_set_current_function(NULL); + + TABLE_delete(&((*class)->table)); + TABLE_delete(&((*class)->string)); + + for (i = 0; i < ARRAY_count((*class)->function); i++) + delete_function(&((*class)->function[i])); + + for (i = 0; i < ARRAY_count((*class)->event); i++) + delete_event(&((*class)->event[i])); + + for (i = 0; i < ARRAY_count((*class)->ext_func); i++) + delete_extfunc(&((*class)->ext_func[i])); + + for (i = 0; i < ARRAY_count((*class)->structure); i++) + delete_structure(&((*class)->structure[i])); + + for (i = 0; i < ARRAY_count((*class)->names); i++) + FREE(&((*class)->names[i])); + + ARRAY_delete(&((*class)->function)); + ARRAY_delete(&((*class)->event)); + ARRAY_delete(&((*class)->prop)); + ARRAY_delete(&((*class)->ext_func)); + ARRAY_delete(&((*class)->constant)); + ARRAY_delete(&((*class)->class)); + ARRAY_delete(&((*class)->unknown)); + ARRAY_delete(&((*class)->stat)); + ARRAY_delete(&((*class)->dyn)); + ARRAY_delete(&((*class)->array)); + ARRAY_delete(&((*class)->structure)); + ARRAY_delete(&((*class)->names)); + + if ((*class)->name) STR_free((*class)->name); + if ((*class)->export_name) STR_free((*class)->export_name); + + FREE(class); +} + + +CLASS_SYMBOL *CLASS_declare(CLASS *class, int index, int type, bool global) +{ + const char *name; + CLASS_SYMBOL *sym; + + sym = CLASS_get_symbol(class, index); + + if ((global && !TYPE_is_null(sym->global.type)) + || (!global && !TYPE_is_null(sym->local.type))) // || !TYPE_is_null(sym->global.type)))) + { + char name[sym->symbol.len + 1]; + memcpy(name, sym->symbol.name, sym->symbol.len); + name[sym->symbol.len] = 0; + THROW("'&1' already declared", name); + } + + if (type == TK_VARIABLE && sym->class && JOB->class->class[sym->class - 1].has_static) + { + name = SYMBOL_get_name(&sym->symbol); + if (global) + COMPILE_print(MSG_WARNING, -1, "class name hidden by global declaration: &1", name); + else + COMPILE_print(MSG_WARNING, -1, "class name hidden by local declaration: &1", name); + } + + if (!global && !TYPE_is_null(sym->global.type)) + { + name = SYMBOL_get_name(&sym->symbol); + + switch (TYPE_get_kind(sym->global.type)) + { + case TK_VARIABLE: + COMPILE_print(MSG_WARNING, -1, "global variable hidden by local declaration: &1", name); + break; + + case TK_FUNCTION: + COMPILE_print(MSG_WARNING, -1, "function hidden by local declaration: &1", name); + break; + + case TK_EXTERN: + COMPILE_print(MSG_WARNING, -1, "extern function hidden by local declaration: &1", name); + break; + + case TK_CONST: + COMPILE_print(MSG_WARNING, -1, "constant hidden by local declaration: &1", name); + break; + } + } + + if (global) + sym->global.line = JOB->line; + else + sym->local.line = JOB->line; + + return sym; +} + + +void CLASS_check_unused_global(CLASS *class) +{ + CLASS_SYMBOL *sym; + int i; + TYPE type; + + for (i = 0; i < TABLE_count(class->table); i++) + { + sym = CLASS_get_symbol(class, i); + type = sym->global.type; + + if (sym->global_no_warning) + continue; + + if (sym->global_used && sym->global_assigned) + continue; + + if (TYPE_is_null(type) || TYPE_is_public(type)) + continue; + + if (!sym->global_used) + { + if (TYPE_get_kind(type) == TK_VARIABLE) + COMPILE_print(MSG_WARNING, sym->global.line, "unused global variable: &1", SYMBOL_get_name(&sym->symbol)); + else if (TYPE_get_kind(type) == TK_FUNCTION) + COMPILE_print(MSG_WARNING, class->function[sym->global.value].line - 1, "unused function: &1", SYMBOL_get_name(&sym->symbol)); + else if (TYPE_get_kind(type) == TK_EXTERN) + COMPILE_print(MSG_WARNING, sym->global.line, "unused extern function: &1", SYMBOL_get_name(&sym->symbol)); + } + else + { + if (TYPE_get_kind(type) == TK_VARIABLE) + COMPILE_print(MSG_WARNING, sym->global.line, "uninitialized global variable: &1", SYMBOL_get_name(&sym->symbol)); + } + } +} + + +int CLASS_add_function(CLASS *class, TRANS_FUNC *decl) +{ + FUNCTION *func; + int i; + CLASS_SYMBOL *sym; + PARAM *param; + int count; + + count = ARRAY_count(class->function); + if (count >= MAX_CLASS_FUNCTION) + THROW("Too many functions"); + + func = ARRAY_add_void(&class->function); + TYPE_clear(&func->type); + func->nparam = 0; + func->name = NO_SYMBOL; + + ARRAY_create(&func->local); + ARRAY_create(&func->stat); + //ARRAY_create_inc(&func->code, 512); + func->code = NULL; + func->ncode = 0; + func->ncode_max = 0; + + if (JOB->debug) + ARRAY_create(&func->pos_line); + + if (decl->index != NO_SYMBOL) + { + sym = CLASS_declare(class, decl->index, TK_FUNCTION, TRUE); + sym->global.type = decl->type; + sym->global.value = count; + sym->global_no_warning = decl->no_warning; + } + + if (TYPE_is_static(decl->type)) + class->has_static = TRUE; + + func->nparam = decl->nparam; + + if (decl->nparam) + { + ALLOC(&func->param, decl->nparam * sizeof(PARAM)); + + for (i = 0; i < decl->nparam; i++) + func->param[i] = decl->param[i]; + } + + func->type = decl->type; + func->start = decl->start; + func->line = decl->line; + func->name = decl->index; + func->last_code = CODE_NO_POS; + func->last_code2 = CODE_NO_POS; + func->stack = 8; // Some stack may be needed for initialization functions + func->finally = 0; + func->catch = 0; + func->npmin = -1; + func->vararg = decl->vararg; + func->fast = decl->fast; + func->unsafe = decl->unsafe; + + // Function startup + + CODE_begin_function(func); + JOB->func = func; + + // Byref check at function startup + + if (decl->byref) + CODE_byref(decl->byref); + + // Optional parameters + + for (i = 0; i < func->nparam; i++) + { + param = &func->param[i]; + if (param->optional == NULL) + continue; + + if (func->npmin < 0) + func->npmin = i; + + TRANS_init_optional(param); + CODE_pop_optional(i - func->nparam); + } + + if (func->npmin < 0) + func->npmin = func->nparam; + + return count; +} + + +void CLASS_add_event(CLASS *class, TRANS_EVENT *decl) +{ + EVENT *event; + int i; + CLASS_SYMBOL *sym; + int count; + + count = ARRAY_count(class->event); + if (count >= MAX_CLASS_EVENT) + THROW("Too many events"); + + event = ARRAY_add_void(&class->event); + TYPE_clear(&event->type); + event->nparam = 0; + event->name = NO_SYMBOL; + + if (!decl) return; + + sym = CLASS_declare(class, decl->index, TK_EVENT, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->event) - 1; + + event->nparam = decl->nparam; + + if (event->nparam) + { + ALLOC_ZERO(&event->param, decl->nparam * sizeof(PARAM)); + + for (i = 0; i < decl->nparam; i++) + { + event->param[i].type = decl->param[i].type; + event->param[i].index = decl->param[i].index; + } + } + + event->type = decl->type; + event->name = decl->index; +} + + +void CLASS_add_property(CLASS *class, TRANS_PROPERTY *decl) +{ + PROPERTY *prop; + CLASS_SYMBOL *sym; + int index; + int i; + + index = ARRAY_count(class->prop); + prop = ARRAY_add_void(&class->prop); + TYPE_clear(&prop->type); + prop->name = NO_SYMBOL; + prop->read = decl->write_only ? 0 : -1; + prop->write = decl->read_only ? 0 : -1; + prop->synonymous = -1; + + sym = CLASS_declare(class, decl->index, TK_PROPERTY, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->prop) - 1; + + if (TYPE_is_static(decl->type)) + class->has_static = TRUE; + + prop->type = decl->type; + prop->name = decl->index; + prop->line = decl->line; + prop->comment = decl->comment; + prop->use = decl->use; + + for (i = 0; i < decl->nsynonymous; i++) + { + prop = ARRAY_add_void(&class->prop); + TYPE_clear(&prop->type); + prop->name = NO_SYMBOL; + prop->read = decl->write_only ? 0 : -1; + prop->write = decl->read_only ? 0 : -1; + prop->synonymous = index; + + sym = CLASS_declare(class, decl->synonymous[i], TK_PROPERTY, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->prop) - 1; + + prop->type = decl->type; + prop->name = decl->synonymous[i]; + prop->line = decl->line; + prop->comment = decl->comment; + } + +} + + +void CLASS_add_extern(CLASS *class, TRANS_EXTERN *decl) +{ + EXTFUNC *extfunc; + int i; + CLASS_SYMBOL *sym; + int count; + + count = ARRAY_count(class->ext_func); + if (count >= MAX_CLASS_EXTERN) + THROW("Too many external functions"); + + extfunc = ARRAY_add_void(&class->ext_func); + TYPE_clear(&extfunc->type); + extfunc->nparam = 0; + extfunc->vararg = FALSE; + extfunc->name = NO_SYMBOL; + + if (!decl) return; + + sym = CLASS_declare(class, decl->index, TK_EXTERN, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->ext_func) - 1; + + extfunc->nparam = decl->nparam; + extfunc->vararg = decl->vararg; + + if (extfunc->nparam) + { + ALLOC(&extfunc->param, decl->nparam * sizeof(PARAM)); + + for (i = 0; i < decl->nparam; i++) + { + extfunc->param[i].type = decl->param[i].type; + extfunc->param[i].index = decl->param[i].index; + } + } + + extfunc->type = decl->type; + extfunc->name = decl->index; + extfunc->library = decl->library; + extfunc->alias = decl->alias; +} + + +int CLASS_add_constant(CLASS *class, TRANS_DECL *decl) +{ + CONSTANT *desc; + int num; + + num = ARRAY_count(class->constant); + if (num >= MAX_CLASS_CONST) + THROW("Too many constants"); + + desc = ARRAY_add(&class->constant); + desc->type = decl->type; + desc->index = decl->index; + + desc->value = decl->value; + if (TYPE_get_id(decl->type) == T_LONG) + desc->lvalue = decl->lvalue; + desc->is_integer = decl->is_integer; + + desc->line = JOB->line; + + return num; +} + + +static int add_class(CLASS *class, int index, bool used, bool exported) +{ + int num; + CLASS_REF *desc; + CLASS_SYMBOL *sym = CLASS_get_symbol(class, index); + + num = sym->class - 1; + if (num < 0) + { + num = ARRAY_count(class->class); + if (num >= MAX_CLASS_CLASS) + THROW("Too many different classes used"); + + desc = ARRAY_add_void(&class->class); + desc->index = index; + + sym->class = num + 1; + + if (COMP_verbose) + fprintf(stderr, "Adding class %.*s %s%s\n", sym->symbol.len, sym->symbol.name, used ? "" : "Unused ", exported ? "Exported" : ""); + + JOB->class->class[num].exported = exported; + } + + if (used != JOB->class->class[num].used) + { + if (COMP_verbose) + fprintf(stderr, "Switching class %.*s to %s\n", sym->symbol.len, sym->symbol.name, used ? "Used" : "Unused"); + + JOB->class->class[num].used = used; + } + + return num; +} + +int CLASS_add_class(CLASS *class, int index) +{ + return add_class(class, index, TRUE, FALSE); +} + +int CLASS_add_class_unused(CLASS *class, int index) +{ + return add_class(class, index, FALSE, FALSE); +} + +int CLASS_add_class_exported(CLASS *class, int index) +{ + return add_class(class, index, TRUE, TRUE); +} + +int CLASS_add_class_exported_unused(CLASS *class, int index) +{ + return add_class(class, index, FALSE, TRUE); +} + +bool CLASS_exist_class(CLASS *class, int index) +{ + return CLASS_get_symbol(class, index)->class > 0; +} + +static char *CLASS_add_name(CLASS *class, const char *name, int len) +{ + char *ret; + + ALLOC(&ret, len + 1); + memcpy(ret, name, len); + ret[len] = 0; + + *((char **)ARRAY_add(&class->names)) = ret; + return ret; +} + +int CLASS_get_array_class(CLASS *class, int type, int value) +{ + static char *names[] = { + NULL, "Boolean[]", "Byte[]", "Short[]", "Integer[]", "Long[]", "Single[]", "Float[]", + "Date[]", "String[]", "String[]", "Pointer[]", "Variant[]", NULL, NULL, NULL, "Object[]" + }; + + int index; + //CLASS_REF *cref; + + if (value < 0) + { + if (type <= T_VOID || type > T_OBJECT) + ERROR_panic("Bad native array class"); + + index = _array_class[type]; + + if (index == 0) + { + if (!TABLE_find_symbol(class->table, names[type], strlen(names[type]), &index)) + index = CLASS_add_symbol(class, names[type]); + index = CLASS_add_class_exported(class, index); + _array_class[type] = index; + } + } + else + { + CLASS_SYMBOL *sym = CLASS_get_symbol(class, class->class[value].index); + int len = sym->symbol.len; + char name[len + 2]; + + memcpy(name, sym->symbol.name, len); + memcpy(&name[len], "[]", 2); + + if (!TABLE_find_symbol(class->table, name, len + 2, &index)) + { + char *name_alloc = CLASS_add_name(class, name, len + 2); + index = TABLE_add_symbol(class->table, name_alloc, len + 2); + } + + if (class->class[value].exported) + index = CLASS_add_class_exported(JOB->class, index); + else + index = CLASS_add_class(JOB->class, index); + } + + JOB->class->class[index].type = TYPE_make(type, value, 0); + + /*cref = &class->class[index]; + if (TYPE_is_null(cref->array)) + { + cref->array.t.id = type; + cref->array.t.value = value; + }*/ + + return index; +} + + +int CLASS_add_unknown(CLASS *class, int index) +{ + int num; + int *desc; + CLASS_SYMBOL *sym = CLASS_get_symbol(class, index); + + num = sym->unknown - 1; + if (num < 0) + { + num = ARRAY_count(class->unknown); + if (num >= MAX_CLASS_UNKNOWN) + THROW("Too many unknown symbols"); + + desc = ARRAY_add(&class->unknown); + *desc = index; + + sym->unknown = num + 1; + } + + return num; +} + +int CLASS_add_array(CLASS *class, TRANS_ARRAY *array) +{ + CLASS_ARRAY *desc; + int num; + int i; + + num = ARRAY_count(class->array); + if (num >= MAX_CLASS_ARRAY) + THROW("Too many array declarations"); + + desc = ARRAY_add(&class->array); + desc->type = array->type; + desc->ndim = array->ndim; + for (i = 0; i < desc->ndim; i++) + desc->dim[i] = array->dim[i]; + + return num; +} + + +FUNCTION *CLASS_set_current_function(FUNCTION *func) +{ + JOB->func = func; + return CODE_set_function(func); +} + + +void CLASS_add_static_declaration(CLASS *class, int index, TYPE type, CLASS_SYMBOL *sym, bool local) +{ + VARIABLE *var; + int count = ARRAY_count(class->stat); + + if (count >= MAX_CLASS_SYMBOL) + THROW("Too many static variables"); + + if (local) + sym->local.value = count; + else + sym->global.value = count; + + var = ARRAY_add(&class->stat); + + var->type = type; + var->index = index; + + class->has_static = TRUE; +} + + +void CLASS_init_global_declaration(CLASS *class, TRANS_DECL *decl, CLASS_SYMBOL *sym, bool local) +{ + FUNCTION *prev_func; + bool is_static; + + if (!TRANS_has_init_var(decl)) + return; + + is_static = TYPE_is_static(decl->type); + prev_func = CLASS_set_current_init_function(class, is_static ? FUNC_INIT_STATIC : FUNC_INIT_DYNAMIC); + + FUNCTION_add_all_pos_line(); + TRANS_init_var(decl); + if (local) + { + CODE_pop_global(sym->local.value, is_static); + sym->local_assigned = TRUE; + } + else + { + CODE_pop_global(sym->global.value, is_static); + sym->global_assigned = TRUE; + } + + CLASS_set_current_function(prev_func); +} + +void CLASS_add_declaration(CLASS *class, TRANS_DECL *decl) +{ + CLASS_SYMBOL *sym; + VARIABLE *var; + int count; + bool save_warnings = FALSE; + + if (decl->no_warning) + { + save_warnings = JOB->warnings; + JOB->warnings = FALSE; + } + + sym = CLASS_declare(class, decl->index, TYPE_get_kind(decl->type), TRUE); + + if (decl->no_warning) + { + JOB->warnings = save_warnings; + } + + sym->global.type = decl->type; + + if (TYPE_get_kind(decl->type) == TK_CONST) + { + sym->global.value = CLASS_add_constant(class, decl); + class->has_static = TRUE; + } + else if (TYPE_is_static(decl->type)) + { + CLASS_add_static_declaration(class, decl->index, decl->type, sym, FALSE); + CLASS_init_global_declaration(class, decl, sym, FALSE); + } + else + { + count = ARRAY_count(class->dyn); + if (count >= MAX_CLASS_SYMBOL) + THROW("Too many dynamic variables"); + + sym->global.value = count; + var = ARRAY_add(&class->dyn); + + var->type = decl->type; + var->index = decl->index; + + CLASS_init_global_declaration(class, decl, sym, FALSE); + + /*CLASS_begin_init_function(class, FUNC_INIT_DYNAMIC); + + if (TRANS_has_init_var(decl)) + { + FUNCTION_add_all_pos_line(); + TRANS_init_var(decl); + CODE_pop_global(sym->global.value, FALSE); + sym->global_assigned = TRUE; + }*/ + } + + CLASS_check_variable_prefix(sym, FALSE); +} + +// Don't do that! The order of variables must be kept. + +void CLASS_sort_declaration(CLASS *class) +{ + //reorder_decl(class, class->stat, "static"); + //reorder_decl(class, class->dyn, "dynamic"); +} + + +int CLASS_add_symbol(CLASS *class, const char *name) +{ + int index; + + index = TABLE_add_symbol(class->table, name, strlen(name)); + return index; +} + + +void FUNCTION_add_last_pos_line(void) +{ + int current_pos; + + if (!JOB->debug || JOB->nobreak) + return; + + current_pos = CODE_get_current_pos(); + *ARRAY_add(&JOB->func->pos_line) = current_pos; +} + +void FUNCTION_add_all_pos_line(void) +{ + int line; + int current_pos; + + if (!JOB->debug || JOB->nobreak) + return; + + line = JOB->func->line + ARRAY_count(JOB->func->pos_line) - 1; + current_pos = CODE_get_current_pos(); + + //fprintf(stderr, "FUNCTION_add_all_pos_line: line = %d JOB->line = %d\n", line, JOB->line); + + while (line < JOB->line) + { + *ARRAY_add(&JOB->func->pos_line) = current_pos; + line++; + } +} + +char *FUNCTION_get_fake_name(int func) +{ + static char buf[6]; + + snprintf(buf, sizeof(buf), "$%d", func); + return buf; +} + + +static int check_one_property_func(CLASS *class, PROPERTY *prop, bool write) +{ + CLASS_SYMBOL *sym; + char *name; + //char *name_alloc; + bool is_static; + FUNCTION *func; + int index; + int value = 0; + + JOB->line = prop->line; + JOB->current = NULL; + + is_static = TYPE_is_static(prop->type); + + name = STR_copy(TABLE_get_symbol_name_suffix(class->table, prop->name, write ? "_Write" : "_Read")); + + if (!TABLE_find_symbol(class->table, name, strlen(name), &index)) + { + TRANS_FUNC decl; + + if (prop->use == 0) + THROW("&1 is not declared", name); + + CLEAR(&decl); + //name_alloc = CLASS_add_name(class, name, strlen(name)); + decl.index = index = NO_SYMBOL; //CLASS_add_symbol(class, name_alloc); + + if (write) + { + decl.nparam = 1; + decl.param[0].type = prop->type; + decl.param[0].index = NO_SYMBOL; + } + else + { + decl.type = prop->type; + TYPE_clear_flag(&decl.type, TF_PUBLIC); + } + + decl.line = prop->line; + + TYPE_set_kind(&decl.type, TK_FUNCTION); + if (is_static) + TYPE_set_flag(&decl.type, TF_STATIC); + + value = CLASS_add_function(JOB->class, &decl); + JOB->nobreak = TRUE; + + sym = CLASS_get_symbol(JOB->class, prop->use); + + if (write) + { + sym->global_assigned = TRUE; + CODE_push_local(-1); + CODE_pop_global(sym->global.value, is_static); + CODE_return(2); + } + else + { + sym->global_used = TRUE; + CODE_push_global(sym->global.value, is_static, FALSE); + CODE_return(1); + } + + FUNCTION_add_last_pos_line(); + JOB->func->stack = CODE_stack_usage; + JOB->nobreak = FALSE; + } + + if (index != NO_SYMBOL) + { + sym = (CLASS_SYMBOL *)TABLE_get_symbol(class->table, index); + + if (TYPE_get_kind(sym->global.type) != TK_FUNCTION) + THROW("&1 is declared but is not a function", name); + + func = &class->function[sym->global.value]; + JOB->line = func->line; + + if (TYPE_is_public(sym->global.type)) + THROW("A property implementation cannot be public"); + + if (is_static != TYPE_is_static(sym->global.type)) + { + if (is_static) + THROW("&1 must be static", name); + else + THROW("&1 cannot be static", name); + } + + if (write) + { + if (TYPE_get_id(func->type) != T_VOID) + goto _BAD_SIGNATURE; + + if (func->nparam != 1 || func->npmin != 1) + goto _BAD_SIGNATURE; + + if (!TYPE_compare(&func->param[0].type, &prop->type)) + goto _BAD_SIGNATURE; + } + else + { + if (!TYPE_compare(&func->type, &prop->type)) + goto _BAD_SIGNATURE; + + if (func->nparam != 0 || func->npmin != 0) + goto _BAD_SIGNATURE; + } + + sym->global_used = TRUE; + value = sym->global.value; + } + + STR_free(name); + return value; + +_BAD_SIGNATURE: + + THROW("&1 declaration does not match", name); +} + + +void CLASS_check_properties(CLASS *class) +{ + int i; + PROPERTY *prop; + + for (i = 0; i < ARRAY_count(class->prop); i++) + { + prop = &class->prop[i]; + if (prop->synonymous >= 0) + { + prop->read = class->prop[prop->synonymous].read; + prop->write = class->prop[prop->synonymous].write; + } + else + { + // TODO: if COMPILER_version < 0x3180000, and if there is no prop->read, then + // replace it by a function that raises an error. + prop->read = prop->read ? check_one_property_func(class, prop, FALSE) : NO_SYMBOL; + prop->write = prop->write ? check_one_property_func(class, prop, TRUE) : NO_SYMBOL; + } + } +} + + +void CLASS_check_variable_prefix(CLASS_SYMBOL *sym, bool local) +{ + char *name = sym->symbol.name; + int len = sym->symbol.len; + int len_prefix; + char *p; + TYPE type; + + if (!JOB->check_prefix) + return; + + type = local ? sym->local.type : sym->global.type; + if (TYPE_is_null(type)) + return; + + if (len >= 1 && *name == '$') + { + name++; + len--; + } + + if (len <= 1) + return; + + len_prefix = 0; + p = name; + while (len > 0 && islower(*p)) + { + p++; + len--; + len_prefix++; + } + + if (TYPE_check_prefix(type, name, len_prefix)) + COMPILE_print(MSG_WARNING, -1, "variable prefix does not match its datatype: &1", SYMBOL_get_name(&sym->symbol)); +} + + +char *CLASS_get_export_name(CLASS *class) +{ + if (!class->exported) + return NULL; + + if (class->export_name) + return class->export_name; + + return class->name; +} diff --git a/main/gbc/gbc_class.h b/main/gbc/gbc_class.h new file mode 100644 index 00000000..01da7e72 --- /dev/null +++ b/main/gbc/gbc_class.h @@ -0,0 +1,283 @@ +/*************************************************************************** + + gbc_class.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_CLASS_H +#define __GBC_CLASS_H + +#include "gbc_type.h" +#include "gb_table.h" +#include "gbc_trans.h" + +#define FUNC_INIT_STATIC 0 +#define FUNC_INIT_DYNAMIC 1 +#define FUNC_INIT_MAX 1 + +typedef + struct { + TYPE type; + int value; + int line; + } + CLASS_SYMBOL_INFO; + +typedef + struct { + SYMBOL symbol; + CLASS_SYMBOL_INFO global; + CLASS_SYMBOL_INFO local; + int class; + int unknown; + unsigned global_used : 1; + unsigned global_assigned : 1; + unsigned global_no_warning : 1; + unsigned local_used : 1; + unsigned local_assigned : 1; + unsigned _reserved : 27; + } + CLASS_SYMBOL; + +typedef + TRANS_PARAM PARAM; + +typedef + struct { + TYPE type; + int index; + } + VARIABLE; + +typedef + struct { + int num; + int param; + } + VARIABLE_INIT; + +typedef + struct { + TYPE type; + int index; + int value; + int line; + int64_t lvalue; + unsigned is_integer : 1; + } + CONSTANT; + +typedef + struct { + TYPE type; // Return value datatype + int name; // Function name index in class symbol table + + char nparam; // Maximum number of arguments + char npmin; // Minimum number of arguments + unsigned vararg : 1; // If this function accepts extra arguments + unsigned fast : 1; // If this function is jit compiled + unsigned unsafe : 1; // If this function is unsafe + unsigned use_is_missing : 1; // If this function uses IsMissing() + unsigned no_debug : 1; // Do not output deubgging information + unsigned _reserved : 11; + short nlocal; // Local variable count + short nctrl; // Control structure variable count + + uint64_t byref; // Byref mask + PARAM *local; // Datatypes of local variables + PARAM *param; // Datatypes of arguments + PARAM *stat; // Datatypes of static local variables + + PATTERN *start; // Starts compilation from there + + int line; // ...which is this line + short stack; // Needed stack + short _reserved2; + + ushort *code; // Compile bytecode + + short *pos_line; // Bytecode position of each code line + + ushort ncode; // Number of instructions + ushort ncode_max; // Size of the bytecode allocation + + ushort last_code; // Last compiled bytecode position + ushort last_code2; // Last last compiled bytecode position + ushort finally; // FINALLY position + ushort catch; // CATCH position + + short code_stack_usage; // save state of code generation + short code_stack; + } + FUNCTION; + +typedef + struct { + TYPE type; // Return value datatype + int name; // Function name index in class symbol table + PARAM *param; // Argument list + short nparam; // Number of arguments + short _reserved; + } + EVENT; + +typedef + struct { + TYPE type; // Return value datatype + int name; // Function name index in class symbol table + PARAM *param; // Argument list + short nparam; // Number of arguments + unsigned vararg : 1; // Variable number of arguments + unsigned _reserved : 15; + int library; // Library name index + int alias; // Real function name index + } + EXTFUNC; + +typedef + struct { + TYPE type; // Property datatype + int name; // Property name index + int line; // The line where the property is declared + int comment; // Property string description, added to datatype + int synonymous; // Synonymous property index (-1 if not a synonymous) + int use; // Associated global private variable + short read; // Read function + short write; // Write function + } + PROPERTY; + +typedef + struct { + TYPE type; // Array datatype + int ndim; // Number of dimensions + int dim[MAX_ARRAY_DIM]; // Dimensions bounds + } + CLASS_ARRAY; + +typedef + struct { + int index; // Structure name + int nfield; // Number of structure fields + VARIABLE *field; // Structure fields + } + CLASS_STRUCT; + +typedef + struct { + int index; // class name + unsigned used : 1; + unsigned exported : 1; + unsigned structure : 1; + unsigned has_static : 1; + unsigned is_collection : 1; // if the class is Collection (for JIT) + unsigned _reserved : 20; + TYPE type; // if the class is an array, the type of the array contents (for JIT) + } + CLASS_REF; + +typedef + struct { + TABLE *table; // symbol table + TABLE *string; // strings table + char *name; // class name + short parent; // parent class + unsigned exported : 1; // class is exported + unsigned autocreate : 1; // class is auto-creatable + unsigned optional : 1; // class is optional + unsigned nocreate : 1; // class cannot be instantiated + unsigned all_fast : 1; // all methods have the Fast option (JIT) + unsigned all_unsafe : 1; // all methods are unsafe + unsigned has_static : 1; // has static methods, properties or variables + unsigned has_fast : 1; // has at least one fast method + unsigned _reserved : 8; + VARIABLE *stat; // static variables + VARIABLE *dyn; // dynamic variables + CONSTANT *constant; // constants + CLASS_REF *class; // classes + int *unknown; // unknown symbols + FUNCTION *function; // functions + int size_stat; // static variables total size + int size_dyn; // dynamic variables total size + EVENT *event; // events + PROPERTY *prop; // properties + EXTFUNC *ext_func; // extern functions + CLASS_ARRAY *array; // array definitions + CLASS_STRUCT *structure; // structs definitions + char **names; // when some symbols must be created like object arrays + char *export_name; // export name of the class + } + CLASS; + + +#define CLASS_get_symbol(class, ind) ((CLASS_SYMBOL *)TABLE_get_symbol((class)->table, ind)) + +#define FUNCTION_is_procedure(func) (TYPE_get_id((func)->type) == T_VOID) +#define FUNCTION_is_static(func) (TYPE_is_static((func)->type)) + + +void CLASS_create(CLASS **result); +void CLASS_delete(CLASS **class); + +CLASS_SYMBOL *CLASS_declare(CLASS *class, int index, int type, bool global); +void CLASS_check_unused_global(CLASS *class); + +FUNCTION *CLASS_set_current_function(FUNCTION *func); +#define CLASS_set_current_init_function(_class, _type) CLASS_set_current_function(&(_class)->function[_type]) + +int CLASS_add_function(CLASS *class, TRANS_FUNC *decl); +void CLASS_add_event(CLASS *class, TRANS_EVENT *decl); +void CLASS_add_property(CLASS *class, TRANS_PROPERTY *prop); +void CLASS_add_extern(CLASS *class, TRANS_EXTERN *decl); + +void CLASS_add_declaration(CLASS *class, TRANS_DECL *decl); +void CLASS_add_static_declaration(CLASS *class, int index, TYPE type, CLASS_SYMBOL *sym, bool local); +void CLASS_init_global_declaration(CLASS *class, TRANS_DECL *decl, CLASS_SYMBOL *sym, bool local); + +int CLASS_add_constant(CLASS *class, TRANS_DECL *decl); +int CLASS_add_class(CLASS *class, int index); +int CLASS_add_class_unused(CLASS *class, int index); +int CLASS_add_class_exported(CLASS *class, int index); +int CLASS_add_class_exported_unused(CLASS *class, int index); +bool CLASS_exist_class(CLASS *class, int index); +int CLASS_add_unknown(CLASS *class, int index); +int CLASS_add_array(CLASS *class, TRANS_ARRAY *array); + +int CLASS_get_array_class(CLASS *class, int type, int value); + +void FUNCTION_add_last_pos_line(void); +void FUNCTION_add_all_pos_line(void); +char *FUNCTION_get_fake_name(int func); + +int CLASS_add_symbol(CLASS *class, const char *name); + +void CLASS_sort_declaration(CLASS *class); +void CLASS_check_properties(CLASS *class); + +void CLASS_check_variable_prefix(CLASS_SYMBOL *sym, bool local); + +char *CLASS_get_export_name(CLASS *class); + +// gbc_dump.c + +void CLASS_dump(void); +void CLASS_export(void); + +#endif diff --git a/main/gbc/gbc_code.c b/main/gbc/gbc_code.c new file mode 100644 index 00000000..014e0077 --- /dev/null +++ b/main/gbc/gbc_code.c @@ -0,0 +1,37 @@ +/*************************************************************************** + + gbc_code.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_CODE_C +#define __GB_CODE_C + +#define PROJECT_COMP +#define CODE_DUMP + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gb_code.h" +#include "gb_limit.h" + +#include "gb_code_temp.h" + diff --git a/main/gbc/gbc_compile.c b/main/gbc/gbc_compile.c new file mode 100644 index 00000000..f4c2b76e --- /dev/null +++ b/main/gbc/gbc_compile.c @@ -0,0 +1,823 @@ +/*************************************************************************** + + gbc_compile.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_COMPILE_C + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_component.h" + +#include "gbc_compile.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gbc_trans.h" +#include "gbc_header.h" +#include "gb_code.h" +#include "gbc_output.h" +#include "gbc_form.h" +#include "gbc_chown.h" +#include "gb_arch.h" + +/*#define DEBUG*/ + +bool COMP_verbose = FALSE; + +char *COMP_root = NULL; +char *COMP_dir; +char *COMP_project; +char *COMP_project_name; +char *COMP_info_path; +char *COMP_lib_path; +char *COMP_classes = NULL; +COMPILE COMP_current; +uint COMP_version = GAMBAS_PCODE_VERSION; +char *COMP_default_namespace = NULL; +bool COMP_do_not_lock = TRUE; + +#define STARTUP_MAX_LINE 256 + +const FORM_FAMILY COMP_form_families[] = +{ + { "form", FORM_NORMAL }, + { "report", FORM_NORMAL }, + { "webpage", FORM_WEBPAGE }, + { "webform", FORM_NORMAL }, + { "termform", FORM_NORMAL }, + { NULL } +}; + +static bool read_line(FILE *f, char *dir, int max) +{ + char *p; + int c; + + p = dir; + + for(;;) + { + max--; + + c = fgetc(f); + if (c == EOF) + return TRUE; + + if (c == '\n' || max == 0) + { + *p = 0; + return FALSE; + } + + *p++ = (char)c; + } +} + +static void add_memory_list(char *p, int size) +{ + char *pe = p + size; + char *p2; + int len; + + for(;;) + { + if (p >= pe) + break; + + p2 = p; + while (p2 < pe && *p2 != '\n') + p2++; + + if (p2 >= pe) + break; + + len = p2 - p; + /*if (len > 2 && p[len - 1] == '?') + len--;*/ + + COMPILE_add_class(p, len); + + p = p2 + 1; + } +} + +static bool add_file_list(const char *path) +{ + FILE *f; + char line[256]; + int len; + + f = fopen(path, "r"); + + if (!f) + return TRUE; + + for(;;) + { + if (read_line(f, line, sizeof(line))) + break; + + len = strlen(line); + /*if (len > 2 && line[len - 1] == '?') + len--;*/ + + COMPILE_add_class(line, len); + } + + fclose(f); + return FALSE; +} + +static void add_library_list_file(const char *path, bool ref) +{ + ARCH *arch; + ARCH_FIND find; + const char *name; + char *rpath = NULL; + bool is_file = FALSE; + + if (*path == ':') + { + name = &path[1]; + + rpath = STR_cat(FILE_cat(COMP_lib_path, name, NULL), ".gambas", NULL); + if (!FILE_exist(rpath)) + { + STR_free(rpath); + rpath = STR_cat(FILE_cat(COMP_root, "lib/gambas" GAMBAS_VERSION_STRING, name, NULL), ".gambas", NULL); + } + + if (!FILE_exist(rpath)) + path = NULL; + else + path = rpath; + } + else + { + name = path; + if (!FILE_exist(path)) + path = NULL; + /* + { + is_file = TRUE; + path = FILE_cat(FILE_get_dir(path), ".list", NULL); + if (!FILE_exist(path)) + path = NULL; + } + */ + } + + if (path) + { + if (is_file) + { + add_file_list(path); + } + else + { + arch = ARCH_open(path); + + if (!ARCH_find(arch, ".list", 0, &find)) + add_memory_list(&arch->addr[find.pos], find.len); + + ARCH_close(arch); + } + } + else + ERROR_warning((ref ? "cannot find reference: %s" : "cannot find library: %s"), name); + + if (rpath) + STR_free(rpath); +} + + +void COMPILE_add_component(const char *name) +{ + char *path; + + if (COMP_verbose) + fprintf(stderr, "Loading information from component '%s'\n", name); + + path = (char *)FILE_cat(COMP_info_path, name, NULL); + strcat(path, ".list"); + + if (add_file_list(path)) + { + // Do not raise an error if a component self-reference is not found + if (strcmp(name, COMP_project_name)) + //fprintf(stderr, "warning: cannot read component list file: %s.list\n", name); + THROW("Component not found: &1", name); + return; + } +} + + +static FILE *open_project_file() +{ + FILE *fp = fopen(COMP_project, "r"); + if (!fp) + THROW(E_OPEN, COMP_project); + return fp; +} + +static bool line_begins_with(const char *line, const char *key, int len) +{ + if (len < 0) + len = strlen(key); + + return strncmp(line, key, len) == 0; +} + +static void startup_print(FILE *fs, const char *key, const char *def) +{ + FILE *fp; + char line[256]; + int len = strlen(key); + bool print = FALSE; + + fp = open_project_file(); + + for(;;) + { + if (read_line(fp, line, sizeof(line))) + break; + + if (line_begins_with(line, key, len)) + { + fprintf(fs, "%s\n", &line[len]); + print = TRUE; + } + } + + fclose(fp); + + if (!print && def) + fprintf(fs, "%s\n", def); +} + +static char *find_version_in_file(void) +{ + char *dir, *pdir; + FILE *fv; + char line[256]; + const char *path; + int len; + + dir = STR_copy(COMP_project); + + for(;;) + { + pdir = STR_copy(FILE_get_dir(dir)); + STR_free(dir); + dir = pdir; + + if (dir[0] == '/' && dir[1] == 0) + { + STR_free(dir); + return NULL; + } + + path = FILE_cat(dir, "VERSION", NULL); + + if (FILE_exist(path)) + { + STR_free(dir); + break; + } + } + + fv = fopen(path, "r"); + if (!fv) + return NULL; + + len = fread(line, 1, sizeof(line) - 1, fv); + while (len > 0 && isspace(line[len - 1])) + len--; + line[len] = 0; + return STR_copy(line); +} + +static void startup_print_version(FILE *fs) +{ + FILE *fp; + char line[256]; + char *version = NULL; + char *branch = NULL; + bool add_branch = FALSE; + + fp = open_project_file(); + + for(;;) + { + if (read_line(fp, line, sizeof(line))) + break; + + if (line_begins_with(line, "Version=", 8)) + { + version = STR_copy(&line[8]); + branch = index(version, ' '); + } + else if (line_begins_with(line, "VersionFile=", 12)) + { + if (line[12] == '1') + { + version = find_version_in_file(); + add_branch = TRUE; + } + } + } + + fclose(fp); + + if (version) + { + fputs(version, fs); + if (add_branch && branch) + fputs(branch, fs); + fputc('\n', fs); + STR_free(version); + } + else + fputs("0.0.0\n", fs); +} + +static void create_startup_file() +{ + const char *name; + FILE *fs; + + name = FILE_cat(FILE_get_dir(COMP_project), ".startup", NULL); + fs = fopen(name, "w"); + if (!fs) + THROW("Cannot create .startup file"); + + // Do that now, otherwise file buffer can be erased + FILE_set_owner(name, COMP_project); + + startup_print(fs, "Startup=", ""); + startup_print(fs, "Title=", ""); + startup_print(fs, "Stack=", "0"); + startup_print(fs, "StackTrace=", "0"); + + startup_print_version(fs); + + fputc('\n', fs); + startup_print(fs, "Component=", NULL); + startup_print(fs, "Library=", NULL); + fputc('\n', fs); + + if (fclose(fs)) + THROW("Cannot create .startup file"); +} + +#undef isdigit + +static int read_version_digits(const char **pstr) +{ + const char *p = *pstr; + int n; + int i; + + if (!isdigit(*p)) + return -1; + + n = 0; + + for (i = 0; i < 4; i++) + { + n = (n << 4) + *p++ - '0'; + if (!isdigit(*p)) + break; + } + + *pstr = p; + return n; +} + +static void init_version(void) +{ + const char *ver; + int n, v; + + ver = getenv("GB_PCODE_VERSION"); + if (ver && *ver) + { + v = 0; + n = read_version_digits(&ver); + if (n <= 0 || n > GAMBAS_VERSION) + return; + + v = n << 24; + + if (*ver++ != '.') + return; + + n = read_version_digits(&ver); + if (n < 0 || n > 0x99) + return; + + v |= n << 16; + + if (*ver++ == '.') + { + n = read_version_digits(&ver); + if (n > 0) + { + if (n > 0x9999) + return; + + v |= n; + } + } + + COMP_version = v; + } +} + +void COMPILE_init(void) +{ + FILE *fp; + char line[256]; + char *env; + + RESERVED_init(); + + if (!COMP_root) + COMP_root = STR_copy(FILE_get_dir(FILE_get_dir(FILE_find_gambas()))); + + // Component directory + + COMP_info_path = STR_copy(FILE_cat(COMP_root, "share/gambas" GAMBAS_VERSION_STRING "/info", NULL)); + + // Local libraries directory + + env = getenv("XDG_DATA_HOME"); + if (env && *env) + COMP_lib_path = STR_copy(FILE_cat(env, "gambas3/lib", NULL)); + else + COMP_lib_path = STR_copy(FILE_cat(FILE_get_home(), ".local/share/gambas3/lib", NULL)); + + // Project name + + COMP_project_name = STR_copy(FILE_get_name(FILE_get_dir(COMP_project))); + + // Bytecode version + + init_version(); + + // Project classes + + BUFFER_create(&COMP_classes); + + COMPILE_add_component("gb"); + + fp = open_project_file(); + + for(;;) + { + if (read_line(fp, line, sizeof(line))) + break; + + /*printf("%s\n", line);*/ + + if (strncmp(line, "Component=", 10) == 0) + COMPILE_add_component(&line[10]); + else if (strncmp(line, "Library=", 8) == 0) + add_library_list_file(&line[8], FALSE); + else if (strncmp(line, "Reference=", 10) == 0) + add_library_list_file(&line[10], TRUE); + } + + fclose(fp); + + // Add local ".list" file + // Not possible at the moment. + // add_file_list(FILE_cat(FILE_get_dir(COMP_project), ".list", NULL)); + + // Startup file + + create_startup_file(); + + // Adds a separator to make the difference between classes from components + // (they must be searched in the global symbol table) and classes from the + // project (they must be searched in the project symbol table) + + COMPILE_add_class("-", 1); + + /* + dir = opendir(FILE_get_dir(COMP_project)); + if (dir) + { + while ((dirent = readdir(dir)) != NULL) + { + name = dirent->d_name; + if (*name == '.') + continue; + + if ((strcasecmp(FILE_get_ext(name), "module") == 0) + || (strcasecmp(FILE_get_ext(name), "class") == 0)) + { + name = FILE_get_basename(name); + BUFFER_add(&COMP_classes, name, strlen(name)); + BUFFER_add(&COMP_classes, "\n", 1); + } + } + + closedir(dir); + } + + BUFFER_add(&COMP_classes, "\n", 1); + */ +} + + +void COMPILE_begin(const char *file, bool trans, bool debug) +{ + CLEAR(JOB); + + JOB->name = STR_copy(file); + JOB->debug = debug; + JOB->form = FORM_get_file_family(JOB->name, &JOB->family); + JOB->output = OUTPUT_get_file(JOB->name); + + if (trans) + { + JOB->trans = TRUE; + JOB->tname = OUTPUT_get_trans_file(JOB->name); + } +} + + +void COMPILE_alloc() +{ + struct stat info; + off_t size; + + BUFFER_create(&JOB->source); + CLASS_create(&JOB->class); + + JOB->default_library = NO_SYMBOL; + + size = 0; + + if (stat(JOB->name, &info)) + ERROR_warning("cannot stat file: %s", JOB->name); + else + size += info.st_size; + + if (JOB->form) + { + if (stat(JOB->form, &info)) + ERROR_warning("cannot stat file: %s", JOB->form); + else + size += info.st_size * 2; + } + + ALLOC(&JOB->pattern, sizeof(PATTERN) * (16 + size)); + ALLOC(&JOB->pattern_pos, sizeof(int) * (16 + size)); + JOB->pattern_count = 0; + + JOB->current = NULL; +} + + +void COMPILE_load(void) +{ + if (BUFFER_load_file(&JOB->source, JOB->name)) + THROW("Cannot load source file: &1", strerror(errno)); + + //BUFFER_add(&JOB->source, "\n", 1); +} + + +void COMPILE_free(void) +{ + CLASS_delete(&JOB->class); + BUFFER_delete(&JOB->source); + FREE(&JOB->pattern); + FREE(&JOB->pattern_pos); + + if (JOB->help) + ARRAY_delete(&JOB->help); +} + +void COMPILE_end(void) +{ + STR_free(JOB->name); + STR_free(JOB->form); + STR_free(JOB->output); + if (JOB->trans) + STR_free(JOB->tname); + if (JOB->help) + STR_free(JOB->hname); +} + + +void COMPILE_exit(bool can_dump_count) +{ + /*if (COMP_verbose && can_dump_count) + PCODE_dump_count(stdout);*/ + + RESERVED_exit(); + BUFFER_delete(&COMP_classes); + STR_free(COMP_project_name); + STR_free(COMP_project); + STR_free(COMP_info_path); + STR_free(COMP_dir); + STR_free(COMP_root); + STR_free(COMP_default_namespace); +} + +static void add_class(const char *name, int len) +{ + unsigned char clen = (unsigned char)len; + + if (clen != len) + ERROR_panic("Class name is too long"); + + //fprintf(stderr, "add_class: %.*s\n", len, name); + + BUFFER_add(&COMP_classes, &clen, 1); + BUFFER_add(&COMP_classes, name, len); +} + +void COMPILE_add_class(const char *name, int len) +{ + char *p; + + p = memchr(name, ' ', len); + if (!p) + add_class(name, len); + else + add_class(name, p - name); +} + +void COMPILE_end_class(void) +{ + unsigned char clen = 0; + BUFFER_add(&COMP_classes, &clen, 1); +} + +int COMPILE_lock_file(const char *name) +{ + const char *path; + int fd; + + if (COMP_do_not_lock) + return -1; + + path = FILE_cat(COMP_dir, name, NULL); + + fd = open(path, O_CREAT | O_WRONLY | O_CLOEXEC, 0666); + if (fd < 0) + goto __ERROR; + if (lockf(fd, F_LOCK, 0) < 0) + goto __ERROR; + + return fd; + +__ERROR: + + ERROR_fail("unable to lock file: %s: %s", path, strerror(errno)); +} + + +void COMPILE_unlock_file(int fd) +{ + if (!COMP_do_not_lock) + close(fd); +} + + +void COMPILE_remove_lock(const char *name) +{ + const char *path; + + if (COMP_do_not_lock) + return; + + path = FILE_cat(COMP_dir, name, NULL); + if (FILE_exist(path)) + FILE_unlink(path); +} + + +void COMPILE_print(int type, int line, const char *msg, ...) +{ + int i; + va_list args; + const char *arg[4]; + int col = -1; + int lock; + + if (!JOB->warnings && type == MSG_WARNING) + return; + + lock = COMPILE_lock_file(".gbc.stderr"); + + va_start(args, msg); + + if (line < 0) + { + if (JOB->step == JOB_STEP_READ) + { + line = JOB->line; + col = READ_get_column(); + } + else if (JOB->step == JOB_STEP_TREE) + { + col = TRANS_get_column(&line); + } + else if (JOB->step == JOB_STEP_CODE) + { + line = JOB->line; + if (JOB->current) + col = COMPILE_get_column(JOB->current); + } + } + + if (JOB->name) + { + const char *name = FILE_get_name(JOB->name); + if (line) + { + if (line > JOB->max_line && JOB->form) + { + name = FILE_get_name(JOB->form); + fprintf(stderr, "%s:%d: ", name, line - FORM_FIRST_LINE + 1); + } + else + { + if (col >= 0) + fprintf(stderr, "%s:%d:%d: ", name, line, col + 1); + else + fprintf(stderr, "%s:%d: ", name, line); + } + } + else + fprintf(stderr, "%s: ", name); + } + else + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": "); + + fprintf(stderr, "%s: ", type ? "warning" : "error"); + + if (msg) + { + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, const char *); + + ERROR_define(msg, arg); + fputs(ERROR_info.msg, stderr); + putc('\n', stderr); + } + + COMPILE_unlock_file(lock); + + va_end(args); +} + + +void COMPILE_create_file(FILE **fw, const char *file) +{ + if (!*fw) + { + *fw = fopen(file, "w"); + if (!*fw) + THROW("Cannot create file: &1: &2", FILE_cat(FILE_get_dir(COMP_project), file, NULL), strerror(errno)); + } +} + + diff --git a/main/gbc/gbc_compile.h b/main/gbc/gbc_compile.h new file mode 100644 index 00000000..10aae477 --- /dev/null +++ b/main/gbc/gbc_compile.h @@ -0,0 +1,150 @@ +/*************************************************************************** + + gbc_compile.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef _COMPILE_H +#define _COMPILE_H + +#include + +#include "gb_array.h" +#include "gb_buffer.h" +#include "gb_table.h" +#include "gb_alloc.h" + +#include "gb_limit.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gbc_form.h" + +#include "gbc_class.h" + +enum { + MSG_ERROR = 0, + MSG_WARNING = 1 +}; + +enum { + JOB_STEP_NONE = 0, + JOB_STEP_READ = 1, + JOB_STEP_CODE = 2, + JOB_STEP_TREE = 3, + JOB_STEP_OUTPUT = 4 +}; + +typedef + struct { + char *name; // source file name + int line; // current line number + int first_line; // first line to compile + int max_line; // maximum line number + char *source; // source file contents + unsigned step : 3; // compiler step (JOB_STEP_*) + unsigned debug : 1; // if debugging information must be generated + unsigned trans : 1; // if translation files must be generated + unsigned is_module : 1; // if the source file is a module + unsigned is_form : 1; // if the source is a class form + unsigned is_test : 1; // if the source is a test module + unsigned declared : 1; // ? + unsigned nobreak : 1; // no breakpoint + unsigned exported : 1; // there are some exported class + unsigned swap : 1; // endianness must be swapped + unsigned public_module : 1; // modules symbols are public by default + unsigned trans_error : 1; // display error messages in a translatable form + unsigned no_old_read_syntax : 1; // do not compile the old read syntax + unsigned exec : 1; // we are compiling for an executable + unsigned warnings : 1; // if warnings must be printed + unsigned check_prefix : 1; // check if variable prefix matches its datatype + unsigned _reserved : 14; // reserved + char *output; // output file + PATTERN *pattern; // lexical analyze + int pattern_count; // number of patterns + int *pattern_pos; // position of a pattern in its line + PATTERN *current; // current pattern + PATTERN *end; // last pattern + FUNCTION *func; // current function being compiled + CLASS *class; // current class being compiled + char *form; // form file name + const FORM_FAMILY *family; // form file family + char *tname; // translation file name + int default_library; // default library name for extern declarations + const char **help; // help comments array + int help_first_line; // line of the first help comment + char *hname; // help file name + PATTERN *column; // where to search the current column + } + COMPILE; + +#ifndef __GBC_COMPILE_C + +EXTERN bool COMP_verbose; +EXTERN COMPILE COMP_current; +EXTERN char *COMP_root; +EXTERN char *COMP_dir; +EXTERN char *COMP_project; +EXTERN char *COMP_project_name; +EXTERN char *COMP_info_path; +EXTERN FORM_FAMILY COMP_form_families[]; +EXTERN uint COMP_version; +EXTERN char *COMP_default_namespace; +EXTERN char *COMP_classes; +EXTERN bool COMP_do_not_lock; + +#endif + +#define JOB (&COMP_current) + +void COMPILE_init(void); +void COMPILE_load(void); +void COMPILE_exit(bool can_dump_count); +void COMPILE_begin(const char *file, bool trans, bool debug); +void COMPILE_alloc(); +void COMPILE_free(); +void COMPILE_end(void); +void COMPILE_export_class(char *name); +void COMPILE_add_class(const char *name, int len); +void COMPILE_end_class(void); +void COMPILE_print(int type, int line, const char *msg, ...); +void COMPILE_create_file(FILE **fw, const char *file); +void COMPILE_add_component(const char *name); + +int COMPILE_lock_file(const char *name); +void COMPILE_unlock_file(int fd); +void COMPILE_remove_lock(const char *name); + +#define COMPILE_get_column(_pattern) (JOB->pattern_pos[(_pattern) - JOB->pattern]) + +#define COMPILE_enum_class_start(_name, _len) \ +({ \ + _name = COMP_classes; \ + _len = *_name; \ + _name++; \ +}) + +#define COMPILE_enum_class_next(_name, _len) \ +({ \ + _name += _name[-1]; \ + _len = *_name; \ + _name++; \ +}) + +#endif diff --git a/main/gbc/gbc_dump.c b/main/gbc/gbc_dump.c new file mode 100644 index 00000000..0cd79336 --- /dev/null +++ b/main/gbc/gbc_dump.c @@ -0,0 +1,900 @@ +/*************************************************************************** + + gbc_dump.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_DUMP_C + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_error.h" + +#include "gb_table.h" +#include "gb_str.h" +#include "gbc_compile.h" +#include "gb_code.h" +#include "gb_file.h" +#include "gbc_chown.h" +#include "gbc_help.h" +#include "gbc_output.h" + +static FILE *_finfo; +static char *_buffer = NULL; +static int _buffer_ptr = 0; + +static char *_list_buffer = NULL; +static TABLE *_list_table = NULL; + +static const char *get_name(int index) +{ + return TABLE_get_symbol_name(JOB->class->table, index); +} + +static void get_string(int index, const char **str, int *len) +{ + SYMBOL *sym; + + if (index == VOID_STRING_INDEX) + { + *str = ""; + *len = 0; + } + else + { + sym = TABLE_get_symbol(JOB->class->string, index); + *str = sym->name; + *len = sym->len; + } +} + +static void print_quoted(FILE *file, const char *str, int len) +{ + unsigned char c; + + fputc('"', file); + while (len--) + { + c = *str++; + if (c == '\n') + fputs("\\n", file); + else if (c == '\t') + fputs("\\t", file); + else if (c == 0) + fputs("\\0", file); + else if (c == '"') + fputs("\\\"", file); + else + fputc(c, file); + } + fputc('"', file); +} + +static void dump_name(int index) +{ + printf("%s", get_name(index)); +} + + +static void dump_type(TYPE type, bool as) +{ + int value; + TYPE_ID id; + CLASS_ARRAY *array; + int i; + + id = TYPE_get_id(type); + value = TYPE_get_value(type); + + if (id == T_ARRAY) + { + array = &JOB->class->array[value]; + + printf("["); + for (i = 0; i < array->ndim; i++) + { + if (i > 0) + printf(","); + printf("%d", array->dim[i]); + } + printf("] As "); + dump_type(array->type, FALSE); + } + else if (id == T_OBJECT && value >= 0) + { + if (as) + printf(" As "); + dump_name(JOB->class->class[value].index); + } + else + { + if (as) + printf(" As "); + printf("%s", TYPE_get_desc(type)); + } +} + + +static void dump_function(FUNCTION *func) +{ + int i; + + //printf("<%lld> ", func->byref); + + printf("("); + + for (i = 0; i < func->nparam; i++) + { + if (i > 0) printf(", "); + + if (i >= func->npmin) + printf("Optional "); + + if (func->byref & (1LL << i)) + printf("ByRef "); + + dump_name(func->param[i].index); + dump_type(func->param[i].type, TRUE); + } + + if (func->vararg) + { + if (func->nparam > 0) + printf(", "); + printf("..."); + } + + printf(")"); +} + + + +void CLASS_dump(void) +{ + int i; + TYPE type; + CLASS_SYMBOL *sym; + CLASS *class = JOB->class; + + if (JOB->is_module) + printf("MODULE "); + else if (JOB->is_form) + printf("FORM "); + else + printf("CLASS "); + + printf("%s\n", class->name); + + if (class->parent != NO_SYMBOL) + { + printf("CLASS INHERITS "); + dump_name(class->class[class->parent].index); + putchar('\n'); + } + + if (class->exported) + printf("EXPORT\n"); + + if (class->autocreate) + printf("CREATE\n"); + + if (class->optional) + printf("OPTIONAL\n"); + + putchar('\n'); + + printf("Static size : %d octets\n", class->size_stat); + printf("Dynamic size : %d octets\n\n", class->size_dyn); + + for (i = 0; i < TABLE_count(class->table); i++) + { + sym = CLASS_get_symbol(class, i); + type = sym->global.type; + if (TYPE_is_null(type)) + continue; + + if (TYPE_is_static(type)) printf("Static "); + if (TYPE_is_public(type)) printf("Public "); else printf("Private "); + + switch(TYPE_get_kind(type)) + { + case TK_VARIABLE: + + dump_name(i); + dump_type(type, TRUE); + break; + + case TK_FUNCTION: + + if (TYPE_get_id(type) == T_VOID) + printf("Procedure "); + else + printf("Function "); + + dump_name(i); + dump_function(&class->function[sym->global.value]); + + break; + + case TK_CONST: + + printf("Const "); + dump_name(i); + dump_type(type, TRUE); + printf(" = "); + dump_name(class->constant[sym->global.value].index); + + break; + + case TK_PROPERTY: + + printf("Property "); + if (class->prop[sym->global.value].write == NO_SYMBOL) + printf("Read "); + dump_name(i); + dump_type(type, TRUE); + break; + + case TK_EVENT: + + printf("Event "); + dump_name(i); + break; + + case TK_UNKNOWN: printf("Unknown "); break; + case TK_EXTERN: printf("Extern "); break; + case TK_LABEL: printf("Label "); break; + } + + putchar('\n'); + + /* + if (TYPE_get_kind(type) == TK_FUNCTION) + { + func = &class->function[value]; + printf(" L:%ld", func->line); + } + else if (TYPE_get_kind(type) == TK_EVENT) + func = (FUNCTION *)&class->event[value]; + else if (TYPE_get_kind(type) == TK_EXTERN) + { + func = (FUNCTION *)&class->ext_func[value]; + printf(" in %s", TABLE_get_symbol_name(class->table, class->ext_func[value].library)); + } + */ + } + + putchar('\n'); +} + +static void export_newline(void) +{ + fputc('\n', _finfo); +} + +static void export_type(TYPE type, bool scomma) +{ + int value; + int index; + TYPE_ID id; + + id = TYPE_get_id(type); + value = TYPE_get_value(type); + + if (id == T_OBJECT && value >= 0) + { + fprintf(_finfo, "%s", get_name(JOB->class->class[value].index)); + if (scomma) + fputc(';', _finfo); + } + else if (id == T_ARRAY) + { + type = JOB->class->array[value].type; + index = CLASS_get_array_class(JOB->class, TYPE_get_id(type), TYPE_get_value(type)); + fprintf(_finfo, "%s", get_name(JOB->class->class[index].index)); + if (scomma) + fputc(';', _finfo); + } + // TODO: Manage T_STRUCT + else + fprintf(_finfo, "%s", TYPE_get_short_desc(type)); +} + + +static void export_signature(int nparam, int npmin, PARAM *param, bool vararg) +{ + int i; + PARAM *p; + + for (i = 0; i < nparam; i++) + { + p = ¶m[i]; + + if (i == npmin) + fprintf(_finfo, "["); + + fprintf(_finfo, "(%s%s)", (p->byref ? "&" : ""), get_name(p->index)); + export_type(p->type, TRUE); + } + + if (npmin < nparam) + fprintf(_finfo, "]"); + + if (vararg) + fprintf(_finfo, "."); + + export_newline(); +} + + +static void close_file_and_rename(FILE *f, const char *file, const char *dest) +{ + if (f) + { + fclose(f); + FILE_unlink(dest); + FILE_rename(file, dest); + FILE_set_owner(dest, COMP_project); + } + else + { + FILE_unlink(file); + FILE_unlink(dest); + } +} + + +static bool exist_bytecode_file(char *name) +{ + char *output = OUTPUT_get_file(name); + bool exist = FILE_exist(output); + STR_free(output); + return exist; +} + + +static void read_line(char **line, int *len) +{ + int l; + char c; + int lmax; + + *line = NULL; + *len = 0; + + if (!_buffer) + return; + + lmax = BUFFER_length(_buffer); + if (_buffer_ptr >= lmax) + return; + + *line = &_buffer[_buffer_ptr]; + + l = 0; + for(;;) + { + if (_buffer_ptr >= lmax) + break; + c = _buffer[_buffer_ptr++]; + if (c == '\n') + { + _buffer[_buffer_ptr - 1] = 0; + break; + } + l++; + } + + *len = l; +} + + +static bool load_file(const char *name) +{ + _buffer_ptr = 0; + BUFFER_create(&_buffer); + if (BUFFER_load_file(&_buffer, name)) + { + BUFFER_delete(&_buffer); + return TRUE; + } + else + return FALSE; +} + + +static void class_update_exported(CLASS *class) +{ + FILE *fw = NULL; + char *name; + int len; + bool inserted = FALSE; + bool optional; + bool has_static; + int cmp; + char *file_name = NULL; + char *export_name; + + if (load_file(".list") && !class->exported) + return; + + for(;;) + { + read_line(&name, &len); + optional = FALSE; + has_static = FALSE; + + if (!name) + cmp = 1; + else + { + file_name = memchr(name, ' ', len); + if (file_name) + { + len = file_name - name; + *file_name++ = 0; + } + else + file_name = name; + + if (name[len - 1] == '?') + { + optional = TRUE; + name[len - 1] = 0; + len--; + } + + if (name[len - 1] == '!') + { + has_static = TRUE; + name[len - 1] = 0; + len--; + } + + cmp = strcmp(file_name, class->name); + } + + if (cmp == 0) + { + if (COMP_verbose) + printf("Remove '%s' from .list file\n", name); + continue; + } + else if ((cmp > 0) && class->exported && !inserted) + { + export_name = CLASS_get_export_name(class); + + COMPILE_create_file(&fw, ".list#"); + if (COMP_verbose) + printf("Insert '%s%s' into .list file\n", export_name, class->optional ? "?" : ""); + fputs(export_name, fw); + if (class->has_static && COMP_version >= 0x03060090) + fputc('!', fw); + if (class->optional) + fputc('?', fw); + if (class->export_name) + { + fputc(' ', fw); + fputs(class->name, fw); + } + fputc('\n', fw); + inserted = TRUE; + } + + if (file_name != name) + TABLE_add_symbol(_list_table, name, len); + + if (!name) + break; + + if (exist_bytecode_file(file_name)) + { + if (COMP_verbose) + printf("Copy '%s' in .list file\n", name); + + COMPILE_create_file(&fw, ".list#"); + fputs(name, fw); + if (has_static && COMP_version >= 0x03060090) + fputc('!', fw); + if (optional) + fputc('?', fw); + if (file_name != name) + { + fputc(' ', fw); + fputs(file_name, fw); + } + fputc('\n', fw); + } + else + { + if (COMP_verbose) + printf("Remove '%s' from .list file\n", name); + } + } + + _list_buffer = _buffer; + _buffer = NULL; + + close_file_and_rename(fw, ".list#", ".list"); +} + + +static void insert_class_info(CLASS *class, FILE *fw) +{ + int i; + TYPE type; + CLASS_SYMBOL *sym; + char kind; + int val; + FUNCTION *func; + EVENT *event; + EXTFUNC *extfunc; + CONSTANT *cst; + int line; + const char *str; + int len; + const char *export_name; + + export_name = CLASS_get_export_name(class); + + if (COMP_verbose) + printf("Insert '%s' information into .info file\n", export_name); + + _finfo = fw; + + fprintf(_finfo, "#%s\n", export_name); + + if (class->parent != NO_SYMBOL) + fprintf(_finfo, "%s", get_name(JOB->class->class[class->parent].index)); + export_newline(); + + if (!class->nocreate) + fprintf(_finfo, "C"); + if (class->autocreate) + fprintf(_finfo, "A"); + if (class->optional) + fprintf(_finfo, "O"); + export_newline(); + + HELP_search_and_print_for_class(_finfo); + + for (i = 0; i < TABLE_count(class->table); i++) + { + sym = CLASS_get_symbol(class, i); + type = sym->global.type; + line = sym->global.line; + + if (TYPE_is_null(type)) + continue; + if (!TYPE_is_public(type)) + continue; + + switch(TYPE_get_kind(type)) + { + case TK_CONST: + + kind = 'C'; + break; + + case TK_FUNCTION: + + kind = 'm'; + break; + + case TK_PROPERTY: + + if (class->prop[sym->global.value].write == NO_SYMBOL) + kind = 'r'; + else + kind = 'p'; + break; + + case TK_VARIABLE: + + kind = 'v'; + break; + + case TK_EVENT: + + kind = ':'; + break; + + case TK_EXTERN: + + kind = 'X'; + break; + + default: + continue; + } + + fprintf(_finfo, "%s\n", get_name(i)); + fprintf(_finfo, "%c\n", TYPE_is_static(type) ? toupper(kind) : kind); + + export_type(type, FALSE); + + if (kind == 'r' || kind == 'p') + { + val = class->prop[sym->global.value].comment; + if (val != NO_SYMBOL) + { + get_string(val, &str, &len); + fprintf(_finfo, "%.*s", len, str); + } + } + + export_newline(); + + switch(kind) + { + case 'C': + cst = &class->constant[sym->global.value]; + + switch(TYPE_get_id(type)) + { + case T_BOOLEAN: + if (cst->value) + fputc('T', _finfo); + fputc('\n', _finfo); + break; + + case T_BYTE: + case T_SHORT: + case T_INTEGER: + fprintf(_finfo, "%d\n", cst->value); + break; + + case T_LONG: + fprintf(_finfo, "%" PRId64 "\n", cst->lvalue); + break; + + case T_SINGLE: + case T_FLOAT: + fprintf(_finfo, "%s\n", get_name(cst->value)); + break; + + case T_STRING: + get_string(cst->value, &str, &len); + print_quoted(_finfo, str, len); + export_newline(); + break; + + default: + export_newline(); + break; + } + break; + + case 'm': + func = &class->function[sym->global.value]; + export_signature(func->nparam, func->npmin, func->param, func->vararg); + line = func->line - 1; + break; + + case ':': + event = &class->event[sym->global.value]; + export_signature(event->nparam, event->nparam, event->param, FALSE); + break; + + case 'X': + extfunc = &class->ext_func[sym->global.value]; + export_signature(extfunc->nparam, extfunc->nparam, extfunc->param, FALSE); + break; + + default: + export_newline(); + } + + HELP_search_and_print(_finfo, line); + } +} + +#if 0 +static char *OUTPUT_get_help_file(const char *file) +{ + char *output; + char *p; + //char *dir; + char *name; + + //dir = STR_copy(FILE_get_dir(file)); + name = STR_copy(FILE_get_name(file)); + + for (p = name; *p; p++) + { + if (*p == '.') + { + *p = 0; + break; + } + + *p = toupper(*p); + } + + output = ".help"; + if (mkdir(output, 0777) == 0) + FILE_set_owner(output, COMP_project); + + output = STR_copy(FILE_cat(output, name, NULL)); + + //STR_free(dir); + STR_free(name); + + return output; +} + +static void output_help(void) +{ + FILE *file; + int i; + + JOB->hname = OUTPUT_get_help_file(JOB->name); + + if (!JOB->help) + { + FILE_unlink(JOB->hname); + return; + } + + file = fopen(JOB->hname, "w"); + + if (!file) + THROW("Cannot create file: &1", JOB->hname); + + for (i = 0; i < ARRAY_count(JOB->help); i++) + { + if (!JOB->help[i]) + continue; + + fprintf(stderr, "[%d] = %.*s\n", i + JOB->help_first_line, get_help_comment_length(JOB->help[i]), JOB->help[i]); + } + + fclose(file); + FILE_set_owner(JOB->hname, COMP_project); +} +#endif + + +void CLASS_export(void) +{ + FILE *fw = NULL; + char *line; + int len; + bool inserted = FALSE; + CLASS *class = JOB->class; + int cmp; + const char *msg; + char *export_name; + int index; + SYMBOL *sym; + char *name; + char *file_name; + + if (chdir(FILE_get_dir(COMP_project))) + { + msg = "Cannot change directory"; + goto __ERROR; + } + + TABLE_create(&_list_table, sizeof(SYMBOL), TF_IGNORE_CASE); + + if (class->exported) + export_name = CLASS_get_export_name(class); + else + export_name = class->name; + + class_update_exported(class); + + load_file(".info"); + + read_line(&line, &len); + + for(;;) + { + if (!line) + cmp = 1; + else + cmp = strcmp(&line[1], export_name); + + if (cmp == 0) + { + if (COMP_verbose) + printf("Remove '%s' information from .info file\n", export_name); + + for(;;) + { + read_line(&line, &len); + if (!line || *line == '#') + break; + } + + continue; + } + + if (cmp > 0 && class->exported && !inserted) + { + COMPILE_create_file(&fw, ".info#"); + insert_class_info(class, fw); + inserted = TRUE; + } + + if (!line) + break; + + // copying class information + + name = &line[1]; + if (TABLE_find_symbol(_list_table, name, strlen(name), &index)) + { + sym = TABLE_get_symbol(_list_table, index); + file_name = &sym->name[sym->len]; + while (!*file_name) + file_name++; + } + else + file_name = name; + + if (exist_bytecode_file(file_name)) + { + if (COMP_verbose) + printf("Copy '%s' information in .info file\n", &line[1]); + for(;;) + { + COMPILE_create_file(&fw, ".info#"); + fputs(line, fw); + fputc('\n', fw); + read_line(&line, &len); + if (!line || *line == '#') + break; + } + } + else + { + if (COMP_verbose) + printf("Remove '%s' information from .info file\n", &line[1]); + for(;;) + { + read_line(&line, &len); + if (!line || *line == '#') + break; + } + } + } + + if (_buffer) + BUFFER_delete(&_buffer); + if (_list_buffer) + BUFFER_delete(&_list_buffer); + + TABLE_delete(&_list_table); + + close_file_and_rename(fw, ".info#", ".info"); + return; + +__ERROR: + + THROW("Cannot create class information: &1: &2", msg, strerror(errno)); +} + diff --git a/main/gbc/gbc_form.c b/main/gbc/gbc_form.c new file mode 100644 index 00000000..0aab619f --- /dev/null +++ b/main/gbc/gbc_form.c @@ -0,0 +1,597 @@ +/*************************************************************************** + + gbc_form.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_FORM_C + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_file.h" +#include "gb_str.h" +#include "gbc_compile.h" +#include "gbc_chown.h" +#include "gbc_form.h" + +/*#define DEBUG*/ + +static char *_source; +static const char *_current; + +static FORM_PARENT form_parent[MAX_FORM_PARENT]; +static int form_parent_level; + +static bool _no_trim = FALSE; + +static char **_target = NULL; +static bool _convert = FALSE; + + +void FORM_print_len(const char *buffer, int len) +{ + if (COMP_verbose) + printf("%.*s", len, buffer); + + BUFFER_add(_target, buffer, len); +} + + +void FORM_print(const char *buffer) +{ + if (COMP_verbose) + printf("%s", buffer); + + BUFFER_add(_target, buffer, strlen(buffer)); +} + + +void FORM_print_char(char c) +{ + if (COMP_verbose) + putchar(c); + + BUFFER_add_char(_target, c); +} + + +void FORM_print_indent() +{ + int i; + + if (!_convert) + return; + + for (i = 2; i < form_parent_level; i++) + BUFFER_add_char(_target, '\t'); +} + +static void print_fmt(const char *before, const char *word, int len, const char *after) +{ + FORM_print(before); + FORM_print_len(word, len); + FORM_print(after); +} + + +static void print_var(const char *before, const char *word, int len, const char *after) +{ + FORM_print(before); + if (!word) + FORM_print_len("Me", 2); + else + { + FORM_print_char('{'); + FORM_print_len(word, len); + FORM_print_char('}'); + } + FORM_print(after); +} + + +static bool read_line(const char **str, int *len) +{ + const char *start, *end; + unsigned char car; + int l; + + if (_no_trim) + { + car = *_current; + if (!car) + return TRUE; + } + else + { + for(;;) + { + car = *_current; + if (!car) + return TRUE; + if (car > ' ') + break; + + _current++; + } + } + + start = _current; + for(;;) + { + car = *_current++; + if (car == '\n') // || car =='\r' || !car) + break; + //if (car > ' ') + // nospace = _current; + } + + end = _current; + l = (int)(end - start); + + while (l > 0 && (uchar)end[-1] <= ' ') + { + end--; + l--; + } + + *str = start; + *len = l; + return FALSE; +} + + +static char *get_word(const char **str, int *len) +{ + char car; + const char *pos; + + *len = 0; + + for(;;) + { + car = **str; + if (car == '\n') + return NULL; + if (!isspace(car)) + break; + (*str)++; + } + + pos = *str; + + for(;;) + { + car = **str; + if (isspace(car)) + break; + (*str)++; + (*len)++; + } + + return (char *)pos; +} + + +static void parent_enter(const char *str, int len) +{ + if (form_parent_level >= MAX_FORM_PARENT) + THROW("&1: too many nested containers", JOB->form); + + form_parent[form_parent_level].name = (char *)str; + form_parent[form_parent_level].len = len; + form_parent_level++; +} + + +static void parent_leave(void) +{ + if (form_parent_level == 0) + THROW("&1: syntax error", JOB->form); + + form_parent_level--; +} + + +static void parent_get(char **str, int *len, int add) +{ + if (form_parent_level < add) + { + *str = NULL; + *len = 0; + } + else + { + FORM_PARENT *fp = &form_parent[form_parent_level - add]; + *str = fp->name; + *len = fp->len; + } +} + + +static void get_container(char **str, int *len) +{ + parent_get(str, len, 2); +} + +static void get_current(char **str, int *len) +{ + parent_get(str, len, 1); +} + +static void save_action(bool delete) +{ + FILE *file; + const char *path; + char *name; + const char *line; + int len; + + path = FILE_cat(FILE_get_dir(COMP_project), ".action", NULL); + mkdir(path, 0777); + FILE_set_owner(path, COMP_project); + + name = STR_copy(FILE_set_ext(FILE_get_name(JOB->form), "action")); + path = FILE_cat(FILE_get_dir(COMP_project), ".action", name, NULL); + + if (delete) + { + if (FILE_exist(path)) + { + if (COMP_verbose) + printf("Deleting action file %s\n", path); + + FILE_unlink(path); + } + } + else + { + if (COMP_verbose) + printf("Writing action file %s\n", path); + + file = fopen(path, "w"); + if (!file) + THROW("Cannot create action file: &1", path); + + fputs("# Gambas Action File 3.0\n", file); + + _no_trim = TRUE; + while (!read_line(&line, &len)) + { + fwrite(line, sizeof(char), len, file); + putc('\n', file); + } + _no_trim = FALSE; + + if (fclose(file)) + THROW("Cannot create action file: &1", path); + + FILE_set_owner(path, COMP_project); + } + + STR_free(name); +} + +char *FORM_get_file_family(const char *file, const FORM_FAMILY **family) +{ + char *form; + const FORM_FAMILY *p; + + if (strcmp(FILE_get_ext(file), "class")) + return NULL; + + p = COMP_form_families; + + while (p->ext) + { + form = STR_copy(FILE_set_ext(file, p->ext)); + if (FILE_exist(form)) + break; + STR_free(form); + form = NULL; + p++; + } + + if (form) *family= p; + return form; +} + + +void FORM_do(char *source, bool convert, bool ctrl_public) +{ + const char *line; + char *word; + char *win = NULL; + char *twin = NULL; + + int len, len_twin; + const char *pos_rewind; + bool virtual; + bool public; + bool action; + + if (!convert && JOB->form == NULL) + return; + + _source = source; + _current = _source; + _convert = convert; + + /* version */ + + if (!convert) + { + if (read_line(&line, &len)) + goto _ERROR; + + if (strncasecmp(line, "# Gambas Form File 3.0", len)) + THROW("Bad form file version"); + } + + pos_rewind = _current; + form_parent_level = 0; + + //FORM_print("@SECTION\n"); + + while (!read_line(&line, &len)) + { + if (len == 0 || *line == '#' || *line == '\'') + continue; + + if (*line == '{') + { + form_parent_level++; + line++; + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (win != NULL) + { + if (word[0] == '!') + { + word++; + len--; + public = TRUE; + } + else + public = FALSE; + + if (ctrl_public || public) + FORM_print("Public"); + else + FORM_print("Private"); + + //print_fmt(" {%.*s} ", len, word); + print_fmt(" {", word, len, "} "); + } + + if (win == NULL) + { + win = word; + } + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (twin != NULL) + { + if (*word == '#') + { + word++; + len--; + } + //print_fmt("AS %.*s\n", len, word); + print_fmt("As ", word, len, "\n"); + } + + if (twin == NULL) + { + twin = word; + len_twin = len; + + //print_fmt("INHERITS %.*s\n\n", len_twin, twin); + if (!convert) + print_fmt("Inherits ", twin, len_twin, "\n\n"); + } + } + else if (*line == '}') + { + form_parent_level--; + if (form_parent_level <= 0) + break; + } + } + + if (!convert) + FORM_print("\nPrivate Sub {@load}()\n\n"); + + _current = pos_rewind; + form_parent_level = 0; + + while (!read_line(&line, &len)) + { + if (len == 0 || *line == '#' || *line == '\'') + continue; + + if (*line == '{') + { + line++; + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (form_parent_level == 0) + { + parent_enter(NULL, 0); + if (!convert) FORM_print("With Me\n"); + } + else + { + //print_fmt(" {%.*s} = NEW ", len, word); + if (word[0] == '!') + { + word++; + len--; + } + + if (convert) + FORM_print_char('\n'); + + parent_enter(word, len); + FORM_print_indent(); + print_fmt("{", word, len, "} = New "); + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (*word == '#') + { + virtual = TRUE; + word++; + len--; + } + else + virtual = FALSE; + + //print_fmt("%.*s", len, word); + FORM_print_len(word, len); + + if (!virtual) + { + get_container(&word, &len); + print_var("(", word, len, ")"); + } + + word = get_word(&line, &len); + if (word == NULL) + get_current(&word, &len); + + //print_fmt(" AS \"%.*s\"\n", len, word); + print_fmt(" As \"", word, len, "\"\n"); + + FORM_print_indent(); + get_current(&word, &len); + print_var("With ", word, len, "\n"); + } + } + else if (*line == '}') + { + if (!convert || form_parent_level > 1) + { + FORM_print_indent(); + FORM_print("End With\n"); + } + parent_leave(); + if (form_parent_level == 0) + break; + } + else + { + /*get_current(&word, &len_word);*/ + /*FORM_print(" %.*s", len_word, word);*/ + FORM_print_indent(); + FORM_print_len("\t.", 2); + // Fix incorrect use of unsigned color constants by the IDE form editor + if (line[len - 1] == '&') + len--; + FORM_print_len(line, len); + FORM_print("\n"); + } + } + + if (form_parent_level > 0) + goto _ERROR; + + if (!convert) + { + FORM_print("\nEnd\n\n"); + + // Create or delete the action file if needed + + action = FALSE; + + while (!read_line(&line, &len)) + { + if (!strncasecmp(line, "# Gambas Action File 3.0", len)) + { + save_action(FALSE); + action = TRUE; + break; + } + } + + if (!action) + save_action(TRUE); + } + + return; + +_ERROR: + + if (!convert) + THROW("&1: syntax error in form file", JOB->form); + else + THROW("&1: syntax error in form data"); +} + + +void FORM_set_target(char **target) +{ + _target = target; +} + +void FORM_convert(const char *path) +{ + char *source; + char *target; + + BUFFER_create(&source); + BUFFER_add(&source, "{ Me *\n", 7); + if (BUFFER_load_file(&source, path)) + THROW("Cannot load file: &1", path); + BUFFER_add(&source, "}\n\n\0", 4); + + BUFFER_create(&target); + + FORM_set_target(&target); + FORM_do(source, TRUE, FALSE); + BUFFER_add_char(&target, '\0'); + + puts(target); + + BUFFER_delete(&source); + BUFFER_delete(&target); +} diff --git a/main/gbc/gbc_form.h b/main/gbc/gbc_form.h new file mode 100644 index 00000000..5e67e893 --- /dev/null +++ b/main/gbc/gbc_form.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + gbc_form.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_FORM_H +#define __GBC_FORM_H + +enum { + FORM_NORMAL = 0, + FORM_WEBPAGE = 1 +}; + +typedef + struct { + char *ext; + int type; + } + FORM_FAMILY; + +typedef + struct { + char *name; + int len; + } + FORM_PARENT; + +#define FORM_FIRST_LINE 100000 +#define FORM_FIRST_LINE_STRING "100000" + +void FORM_set_target(char **target); +void FORM_do(char *source, bool convert, bool ctrl_public); +char *FORM_get_file_family(const char *file, const FORM_FAMILY **family); +void FORM_print_len(const char *buffer, int len); +void FORM_print(const char *buffer); +void FORM_print_char(char c); +void FORM_convert(const char *path); + +void FORM_webpage(char *source); + +#endif diff --git a/main/gbc/gbc_form_webpage.c b/main/gbc/gbc_form_webpage.c new file mode 100644 index 00000000..fe174c59 --- /dev/null +++ b/main/gbc/gbc_form_webpage.c @@ -0,0 +1,526 @@ +/*************************************************************************** + + gbc_form_webpage.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_FORM_WEBPAGE_C + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_file.h" +#include "gb_str.h" +#include "gbc_compile.h" +#include "gbc_chown.h" +#include "gbc_form.h" + +/*#define DEBUG*/ + +enum { TYPE_CODE, TYPE_HTML, TYPE_COMMENT, TYPE_MARKUP, TYPE_ARG, TYPE_ROOT }; + +static const char *_start; + +static void print_quoted_string(const char *str, int len) +{ + int i; + char buf[8]; + uchar c; + + if (len == 0) + return; + + FORM_print_char('"'); + + for (i = 0; i < len; i++) + { + c = (uchar)str[i]; + //if (c >= ' ' && c <= 126 && c != '\\' && c != '"') + if (c >= ' ' && c != '\\' && c != '"') + FORM_print_char(c); + else + { + FORM_print_char('\\'); + if (c == '\n') + c = 'n'; + else if (c == '\r') + c = 'r'; + else if (c == '\t') + c = 't'; + else if (!(c == '"' || c == '\\')) + { + snprintf(buf, sizeof(buf), "x%02X", c); + FORM_print_len(buf, 3); + continue; + } + FORM_print_char(c); + } + } + + FORM_print_char('"'); +} + +static void print_lower_len(const char *str, int len) +{ + int i; + + for (i = 0; i < len; i++) + FORM_print_char(tolower(str[i])); +} + +static void flush_html(const char *end) +{ + if (_start == end) + return; + + FORM_print_len("Print ", 6); + if (end[-1] == '\n') + { + print_quoted_string(_start, end - _start - 1); + FORM_print_char('\n'); + } + else + { + print_quoted_string(_start, end - _start); + FORM_print_len(";\n", 2); + } + + _start = end; +} + +static bool check_contents(const char *str, int len) +{ + if (len < 4) + return FALSE; + + if (str[0] == '-' && str[1] == '-' && str[len - 1] == '-' && str[len - 2] == '-') + return TRUE; + else + return FALSE; +} + +static int print_code(const char *str, int len) +{ + char c; + const char *start = str; + + //fprintf(stderr, "'%.*s'\n", len, str); + + while (len > 0) + { + c = *str++; + len--; + + if (c == '"') + { + while (len > 0) + { + c = *str++; + len--; + if (c == '"') + break; + if (c == '\\' && *str) + { + str++; + len--; + } + } + } + else if (c == '%' && *str == '>') + { + len = str - start - 1; + FORM_print_len(start, len); + return len + 2; + } + } + + THROW("&1: syntax error in form file", JOB->form); +} + +static void print_markup(const char *str, int len) +{ + int l; + char c; + const char *p; + bool comma; + bool quote; + bool end; + + if (len <= 0) + return; + + p = str; + while (len > 0) + { + c = *str++; + len--; + if (c == ' ') + break; + } + + l = str - p - (c == ' '); + + if (*p == '/') + { + end = TRUE; + p++; + l--; + } + else + end = FALSE; + + if (l <= 0) + THROW(E_SYNTAX); + + FORM_print_len(p, l); + + if (end) + { + FORM_print(".__RenderEnd()\n"); + return; + } + + FORM_print(".__Render("); + + if (len > 0) + { + FORM_print_char('['); + + comma = FALSE; + + while (len > 0) + { + while (len > 0 && *str == ' ') + { + str++; + len--; + } + + if (len == 0) + break; + + p = str; + while (len > 0) + { + c = *str++; + len--; + if (c == '=' || c == ' ') + break; + } + + if (comma) + FORM_print(", "); + + FORM_print_char('"'); + l = str - p - (c == '='); + print_lower_len(p, l); + FORM_print("\": "); + + comma = TRUE; + + if (len <= 0 || c == ' ') + { + FORM_print("True"); + continue; + } + + p = str; + + // attr=<% ... %> + if (len >= 3 && *str == '<' && str[1] == '%' && str[2] == '=') + { + str += 3; + len -= 3; + FORM_print_char('('); + l = print_code(str, len); + FORM_print_char(')'); + str += l; + len -= l; + //fprintf(stderr, "-> '%.*s'\n", len, str); + continue; + } + else if (*str == '"') + { + str++; + len--; + quote = TRUE; + } + else + { + quote = FALSE; + } + + while (len > 0) + { + c = *str++; + len--; + if (quote) + { + if (len > 0 && c == '\\') + { + str++; + len--; + } + else if (c == '"') + break; + } + else + { + if (c == ' ') + break; + } + } + + if (!quote && str == p) + FORM_print("True"); + else + { + if (!quote) + FORM_print_char('"'); + FORM_print_len(p, str - p); + if (!quote) + FORM_print_char('"'); + } + } + + FORM_print_char(']'); + } + + FORM_print(")\n"); +} + +void FORM_webpage(char *source) +{ + char type = TYPE_CODE; + char c; + const char *p; + int line; + char buf[16]; + bool has_contents = FALSE; + + line = FORM_FIRST_LINE; + + FORM_print("Inherits WebPage\n\n"); + FORM_print("Public Sub _Render(Optional (_Arg) As Collection = New Collection)\n\n"); + + p = source; + +__PRINT: + + _start = p; + for(;;) + { + c = *p++; + if (!c) + { + flush_html(p - 1); + goto __END; + } + + if (c == '<') + { + if (*p == '%') + { + flush_html(p - 1); + + if (p[1] == '/' && p[2] == '%' && p[3] == '>') + { + p += 4; + type = TYPE_ROOT; + } + else + { + p++; + type = TYPE_CODE; + } + + goto __CODE; + } + else if (*p == '<') + { + flush_html(p - 1); + p++; + type = TYPE_MARKUP; + goto __CODE; + } + } + + if (c == '\n') + { + line++; + if ((p - _start) >= 256) + flush_html(p); + } + } + +__CODE: + + FORM_print("#Line "); + sprintf(buf, "%d", line); + FORM_print(buf); + FORM_print_char('\n'); + + if (type == TYPE_CODE) + { + if (*p == '=') + { + type = TYPE_HTML; + p++; + } + else if (*p == '!') + { + type = TYPE_ARG; + p++; + } + else if (*p == '-' && p[1] == '-') + { + type = TYPE_COMMENT; + p += 2; + } + else + type = TYPE_CODE; + } + else if (type == TYPE_ROOT) + { + FORM_print("Print Html$(Application.Root);\n"); + goto __PRINT; + } + + _start = p; + +__END_STRING: + + for(;;) + { + c = *p++; + if (!c) + goto __ERROR; + if (c == '"') + goto __STRING; + + if (c == '\n') + { + line++; + continue; + } + + if (c == '%' && *p == '>' && type != TYPE_MARKUP) + { + switch (type) + { + case TYPE_CODE: + FORM_print_len(_start, p - _start - 1); + FORM_print_char('\n'); + break; + + case TYPE_HTML: + + if ((p - _start) == 1) + { + FORM_print("Print Html$(Application.Root);\n"); + } + else + { + FORM_print("Print Html$("); + FORM_print_len(_start, p - _start - 1); + FORM_print(");\n"); + } + break; + + case TYPE_ARG: + FORM_print("Print _Arg!"); + print_lower_len(_start, p - _start - 1); + FORM_print(";\n"); + break; + + case TYPE_COMMENT: + default: + break; + } + + p++; + if (*p == '\n') + { + line++; + p++; + } + goto __PRINT; + } + else if (c == '>' && *p == '>' && type == TYPE_MARKUP) + { + if (check_contents(_start, p - _start - 1)) + { + if (has_contents) + THROW("Contents already declared"); + has_contents = TRUE; + + FORM_print("\nEnd\n\n"); + FORM_print("Public Sub _RenderEnd()\n\n"); + } + else + { + print_markup(_start, p - _start - 1); + FORM_print_char('\n'); + } + + p++; + if (*p == '\n') + { + line++; + p++; + } + goto __PRINT; + } + } + +__STRING: + + for(;;) + { + c = *p++; + if (!c) + goto __ERROR; + if (c == '"') + goto __END_STRING; + if (c == '\\' && *p) + p++; + } + +__END: + + FORM_print("\nEnd\n\n"); + return; + +__ERROR: + + THROW("&1: syntax error in form file", JOB->form); +} + diff --git a/main/gbc/gbc_header.c b/main/gbc/gbc_header.c new file mode 100644 index 00000000..d04b402c --- /dev/null +++ b/main/gbc/gbc_header.c @@ -0,0 +1,1219 @@ +/*************************************************************************** + + gbc_header.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_HEADER_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_table.h" + +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gbc_header.h" +#include "gb_code.h" + +/*#define DEBUG*/ + + +static void check_public_private(PATTERN **look, bool *is_public) +{ + *is_public = JOB->is_module && JOB->public_module; + + if (PATTERN_is(**look, RS_PUBLIC)) + { + *is_public = TRUE; + (*look)++; + } + else if (PATTERN_is(**look, RS_PRIVATE)) + { + *is_public = FALSE; + (*look)++; + } +} + +static PATTERN *jump_expression(PATTERN *look) +{ + int niv = 0; + + for(;;) + { + if (PATTERN_is_newline(*look)) + break; + + if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR)) + { + niv++; + } + else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR)) + { + if (niv > 0) + niv--; + else + break; + } + else if (niv == 0) + { + if (PATTERN_is(*look, RS_COMMA)) + break; + } + + look++; + } + + return look; +} + +static void analyze_function_desc(TRANS_FUNC *func, int flag) +{ + PATTERN *look = JOB->current; + TRANS_PARAM *param; + //bool is_output; + bool is_optional = FALSE; + TRANS_DECL ttyp; + uint64_t byref_mask = 1; + + if (PATTERN_is(*look, RS_LBRA)) + { + func->no_warning = TRUE; + look++; + } + else + func->no_warning = FALSE; + + if (!PATTERN_is_identifier(*look)) + THROW("Syntax error. Invalid identifier in function name"); + + func->index = PATTERN_index(*look); + TYPE_clear(&func->type); + look++; + + if (flag & HF_EVENT) + func->index = TABLE_copy_symbol_with_prefix(JOB->class->table, func->index, ':'); + + if (func->no_warning) + { + if (!PATTERN_is(*look, RS_RBRA)) + THROW(E_MISSING, "')'"); + look++; + } + + func->nparam = 0; + func->byref = 0; + func->vararg = FALSE; + + if ((flag & HF_VOID) && PATTERN_is_newline(*look)) + { + JOB->current = ++look; + return; + } + + if (!PATTERN_is(*look, RS_LBRA)) + THROW_UNEXPECTED(look); + look++; + + for(;;) + { + if (func->nparam >= MAX_PARAM_FUNC) + THROW("Too many arguments"); + + param = &func->param[func->nparam]; + CLEAR(param); + + if (PATTERN_is(*look, RS_RBRA)) + { + look++; + break; + } + + if (func->nparam > 0) + { + if (!PATTERN_is(*look, RS_COMMA)) + THROW(E_SYNTAX_MISSING, "',' or ')'"); + look++; + } + + if (PATTERN_is(*look, RS_3PTS) && !(flag & HF_NO_3PTS)) + { + look++; + if (!PATTERN_is(*look, RS_RBRA)) + THROW("Syntax error. '...' must be the last argument"); //, get_num_desc(func->nparam + 1)); + look++; + func->vararg = TRUE; + break; + } + + //is_output = FALSE; + + if (!(flag & HF_NO_OPT)) + { + if (PATTERN_is(*look, RS_OPTIONAL)) + { + look++; + is_optional = TRUE; + } + } + + if (PATTERN_is(*look, RS_AT) || PATTERN_is(*look, RS_BYREF)) + { + param->byref = TRUE; + func->byref |= byref_mask; + look++; + } + + if (PATTERN_is(*look, RS_LBRA)) + { + param->ignore = TRUE; + look++; + } + + if (!PATTERN_is_identifier(*look)) + THROW("Syntax error. The &1 argument is not a valid identifier", TRANS_get_num_desc(func->nparam + 1)); + + param->index = PATTERN_index(*look); + look++; + + if (param->ignore) + { + if (!PATTERN_is(*look, RS_RBRA)) + THROW(E_MISSING, "')'"); + look++; + } + + JOB->current = look; + + if (!TRANS_type(TT_NOTHING, &ttyp)) + THROW("Syntax error. Invalid type description of &1 argument", TRANS_get_num_desc(func->nparam + 1)); + + param->type = ttyp.type; + + /* + if (is_output) + TYPE_set_flag(¶m->type, TF_OUTPUT); + */ + + look = JOB->current; + + if (is_optional) + { + param->optional = look; + look = jump_expression(look); + JOB->current = look; + } + + func->nparam++; + byref_mask <<= 1; + } + + JOB->current = look; +} + + +static void header_module_type(void) +{ + const char *ext; + const FORM_FAMILY *p; + + /*JOB->class->name = STR_copy(FILE_get_name(JOB->name));*/ + + ext = FILE_get_ext(JOB->name); + + JOB->is_test = FALSE; + JOB->is_form = FALSE; + + if (strcasecmp(ext, "module") == 0) + { + JOB->is_module = TRUE; + } + else if (strcasecmp(ext, "test") == 0) + { + JOB->is_module = TRUE; + JOB->is_test = TRUE; + } + else if (strcasecmp(ext, "class") == 0) + { + JOB->is_module = FALSE; + } + else + { + p = COMP_form_families; + while (p->ext) + { + if (strcasecmp(ext, p->ext) == 0) + { + JOB->is_module = FALSE; + JOB->is_form = TRUE; + break; + } + p++; + } + + if (!p->ext) + THROW("Unknown file extension"); + } + + JOB->declared = TRUE; +} + + +static bool header_event(TRANS_EVENT *event) +{ + PATTERN *look = JOB->current; + //TRANS_DECL ttyp; + + if (!PATTERN_is(*look, RS_EVENT)) + return FALSE; + + CLEAR(event); + + if (JOB->is_module) + THROW("A module cannot raise events"); + + JOB->current++; + analyze_function_desc((TRANS_FUNC *)event, HF_VOID + HF_NO_BYREF + HF_NO_3PTS + HF_NO_OPT); + + /*if (PATTERN_is(*JOB->current, RS_AS)) + { + if (!TRANS_type(TT_CAN_SQUARE, &ttyp)) + THROW("Syntax error in return type"); + event->type = ttyp.type; + }*/ + + TYPE_set_kind(&event->type, TK_EVENT); + TYPE_set_flag(&event->type, TF_PUBLIC); + + return TRUE; +} + + +static bool header_property(TRANS_PROPERTY *prop) +{ + TRANS_DECL ptype = { 0 }; + PATTERN *look = JOB->current; + bool is_static = FALSE; + bool is_public = TRUE; + + CLEAR(prop); + + /* static */ + + if (JOB->is_module) + { + is_static = TRUE; + } + else if (PATTERN_is(*look, RS_STATIC)) + { + is_static = TRUE; + look++; + } + + /* public */ + + if (PATTERN_is(*look, RS_PUBLIC) || PATTERN_is(*look, RS_PRIVATE)) + { + is_public = PATTERN_is(*look, RS_PUBLIC); + look++; + } + + if (!PATTERN_is(*look, RS_PROPERTY)) + return FALSE; + look++; + JOB->current = look; + + if (!is_public) + THROW("A property must be public"); + + // Read-only or write-only property + + prop->read_only = prop->write_only = FALSE; + + if (TRANS_is(RS_READ)) + prop->read_only = TRUE; + else if (COMP_version >= 0x03180000 && TRANS_is(RS_WRITE)) + prop->write_only = TRUE; + + // Property name + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected for property name"); + + prop->index = PATTERN_index(*JOB->current); + JOB->current++; + + prop->nsynonymous = 0; + + while (TRANS_is(RS_COMMA)) + { + if (prop->nsynonymous == 3) + THROW("Too many property synonymous"); + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected for property synonymous"); + prop->synonymous[prop->nsynonymous++] = PATTERN_index(*JOB->current); + JOB->current++; + } + + if (!TRANS_type(TT_NOTHING, &ptype)) + THROW("Syntax error. Invalid property type"); + + prop->type = ptype.type; + prop->line = JOB->line; + + TYPE_set_kind(&prop->type, TK_PROPERTY); + if (is_static) + TYPE_set_flag(&prop->type, TF_STATIC); + TYPE_set_flag(&prop->type, TF_PUBLIC); + + // property with variable + + if (TRANS_is(RS_USE)) + { + TRANS_DECL decl = ptype; + + TYPE_set_kind(&decl.type, TK_VARIABLE); + if (is_static) + TYPE_set_flag(&decl.type, TF_STATIC); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected for property variable"); + + prop->use = PATTERN_index(*JOB->current); + JOB->current++; + + decl.index = prop->use; + + if (TRANS_is(RS_EQUAL)) + decl.init = JOB->current; + + CLASS_add_declaration(JOB->class, &decl); + } + + if (PATTERN_is_string(*JOB->current)) + { + prop->comment = PATTERN_index(*JOB->current); + JOB->current++; + } + else + prop->comment = NO_SYMBOL; + + return TRUE; +} + + +static bool header_extern(TRANS_EXTERN *trans) +{ + PATTERN *look = JOB->current; + TRANS_DECL ttyp; + int index; + bool is_public; + + check_public_private(&look, &is_public); + + if (!PATTERN_is(*look, RS_EXTERN)) + return FALSE; + look++; + + CLEAR(trans); + + JOB->current = look; + analyze_function_desc((TRANS_FUNC *)trans, HF_NO_BYREF); + + if (PATTERN_is(*JOB->current, RS_AS)) + { + if (!TRANS_type(TT_NOTHING, &ttyp)) + THROW("Syntax error in return type"); + trans->type = ttyp.type; + } + + if (TRANS_is(RS_IN)) + { + if (!PATTERN_is_string(*JOB->current)) + THROW("Library name must be a string"); + + index = PATTERN_index(*JOB->current); + JOB->current++; + } + else + { + if (JOB->default_library == NO_SYMBOL) + THROW(E_MISSING, "IN"); + + index = JOB->default_library; + } + + trans->library = index; + + if (TRANS_is(RS_EXEC)) + { + if (!PATTERN_is_string(*JOB->current)) + THROW("Alias name must be a string"); + + trans->alias = PATTERN_index(*JOB->current); + JOB->current++; + } + else + trans->alias = NO_SYMBOL; + + TYPE_set_kind(&trans->type, TK_EXTERN); + TYPE_set_flag(&trans->type, TF_STATIC); + if (is_public) TYPE_set_flag(&trans->type, TF_PUBLIC); + + return TRUE; +} + +static bool header_class(TRANS_DECL *decl) +{ + if (!TRANS_is(RS_CLASS)) + return FALSE; + + TRANS_want_class(); + + CLEAR(decl); + decl->index = PATTERN_index(*JOB->current); + JOB->current++; + + return TRUE; +} + +static bool header_declaration(TRANS_DECL *decl) +{ + PATTERN *look = JOB->current; + PATTERN *save; + bool is_static = FALSE; + bool is_public = FALSE; + bool is_const = FALSE; + bool no_warning = FALSE; + + /* static ! */ + + if (JOB->is_module) + { + is_static = TRUE; + } + else if (PATTERN_is(*look, RS_STATIC)) + { + is_static = TRUE; + //has_static = TRUE; + look++; + } + + check_public_private(&look, &is_public); + + /* const ? */ + + is_const = FALSE; + + if (PATTERN_is(*look, RS_CONST)) + { + //if (has_static) + // THROW("Unexpected &1", "STATIC"); + + is_const = TRUE; + look++; + } + + if (PATTERN_is(*look, RS_LBRA)) + { + no_warning = TRUE; + look++; + } + + if (!PATTERN_is_identifier(*look)) + return FALSE; + + CLEAR(decl); + + decl->index = PATTERN_index(*look); + look++; + + if (no_warning) + { + if (!PATTERN_is(*look, RS_RBRA)) + return FALSE; + look++; + decl->no_warning = TRUE; + } + + save = JOB->current; + JOB->current = look; + + if (!TRANS_type((!is_const ? TT_CAN_ARRAY | TT_CAN_EMBED : 0) | TT_CAN_NEW, decl)) + { + JOB->current = save; + return FALSE; + } + + if (is_static) TYPE_set_flag(&decl->type, TF_STATIC); + if (is_public) TYPE_set_flag(&decl->type, TF_PUBLIC); + if (is_const) + TYPE_set_kind(&decl->type, TK_CONST); + else + TYPE_set_kind(&decl->type, TK_VARIABLE); + + if (is_const) + { + if (!decl->init) + THROW(E_SYNTAX_MISSING, "'='"); + + JOB->current = decl->init; + TRANS_get_constant_value(decl); + } + + //JOB->current = look; + + return TRUE; +} + + +static bool header_enumeration(TRANS_DECL *decl) +{ + PATTERN *look = JOB->current; + bool is_public; + int value = 0; + + check_public_private(&look, &is_public); + + if (!PATTERN_is(*look, RS_ENUM)) + return FALSE; + look++; + + JOB->current = look; + + for(;;) + { + TRANS_newline(); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected"); + + CLEAR(decl); + + decl->index = PATTERN_index(*JOB->current); + JOB->current++; + + decl->type = TYPE_make_simple(T_INTEGER); + + if (is_public) TYPE_set_flag(&decl->type, TF_PUBLIC); + TYPE_set_kind(&decl->type, TK_CONST); + + if (TRANS_is(RS_EQUAL)) + { + TRANS_get_constant_value(decl); + value = decl->value + 1; + } + else + { + decl->value = value; + value++; + } + + CLASS_add_declaration(JOB->class, decl); + + if (TRANS_newline()) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + THROW_UNEXPECTED(JOB->current); + JOB->current++; + } + + return TRUE; +} + + +static bool header_function(TRANS_FUNC *func) +{ + static HEADER_SPECIAL spec[] = + { + { "_init", HS_PUBLIC + HS_STATIC + HS_PROCEDURE + HS_NOPARAM }, + { "_exit", HS_PUBLIC + HS_STATIC + HS_PROCEDURE + HS_NOPARAM}, + { "_new", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE }, + { "_free", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE + HS_NOPARAM }, + { "_call", HS_PUBLIC }, + { "_get", HS_PUBLIC + HS_FUNCTION }, + { "_put", HS_PUBLIC + HS_PROCEDURE + HS_PUT }, + { "_next", HS_PUBLIC + HS_NOPARAM }, + { "_property", HS_PUBLIC + HS_NOPARAM + HS_FUNCTION + HS_PROPERTY }, + { "_unknown", HS_PUBLIC + HS_UNKNOWN }, + { "_compare", HS_PUBLIC + HS_DYNAMIC + HS_FUNCTION + HS_COMPARE }, + { "_attach", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE + HS_ATTACH }, + { NULL, 0 } + }; + + PATTERN *look = JOB->current; + PATTERN pat; + TRANS_DECL ttyp; + SYMBOL *sym; + HEADER_SPECIAL *hsp; + + bool is_proc = FALSE; + bool is_static = FALSE; + bool is_public = FALSE; + bool is_fast = FALSE; + bool is_unsafe = FALSE; + + // fast ? + + if (PATTERN_is(*look, RS_FAST)) + { + is_fast = TRUE; + look++; + + if (PATTERN_is(*look, RS_UNSAFE)) + { + is_unsafe = TRUE; + look++; + } + } + + // static ? + + if (JOB->is_module) + { + is_static = TRUE; + } + else if (PATTERN_is(*look, RS_STATIC)) + { + is_static = TRUE; + look++; + } + + // public ou static ? + + is_public = JOB->is_module && JOB->public_module; + + if (PATTERN_is(*look, RS_PUBLIC)) + { + is_public = TRUE; + look++; + } + else if (PATTERN_is(*look, RS_PRIVATE)) + { + is_public = FALSE; + look++; + } + + if (PATTERN_is(*look, RS_PROCEDURE) || PATTERN_is(*look, RS_SUB)) + is_proc = TRUE; + else if (!PATTERN_is(*look, RS_FUNCTION)) + return FALSE; + look++; + + //CLEAR(func); + + JOB->current = look; + analyze_function_desc(func, HF_NORMAL); + + if (PATTERN_is(*JOB->current, RS_AS)) + { + if (!TRANS_type(TT_NOTHING, &ttyp)) + THROW("Syntax error. Invalid return type"); + func->type = ttyp.type; + is_proc = FALSE; + } + + TYPE_set_kind(&func->type, TK_FUNCTION); + if (is_static) TYPE_set_flag(&func->type, TF_STATIC); + if (is_public) TYPE_set_flag(&func->type, TF_PUBLIC); + + func->fast = is_fast || JOB->class->all_fast; + if (func->fast) + JOB->class->has_fast = TRUE; + + func->unsafe = is_unsafe || JOB->class->all_unsafe; + + // Check special methods + + sym = TABLE_get_symbol(JOB->class->table, func->index); + + if (*sym->name == '_') + { + for (hsp = spec; hsp->name; hsp++) + { + if (sym->len != strlen(hsp->name)) + continue; + if (strncmp(sym->name, hsp->name, sym->len)) + continue; + + if (hsp->flag == HS_ERROR) + THROW("The special method &1 cannot be implemented", hsp->name); + + if ((hsp->flag & HS_PUBLIC) && !is_public) + THROW("The special method &1 must be public", hsp->name); + + if ((hsp->flag & HS_STATIC) && !is_static) + THROW("The special method &1 must be static", hsp->name); + + if ((hsp->flag & HS_DYNAMIC) && is_static) + THROW("The special method &1 cannot be static", hsp->name); + + if ((hsp->flag & HS_PROCEDURE) && !is_proc) + THROW("The special method &1 cannot be a function", hsp->name); + + if ((hsp->flag & HS_FUNCTION) && is_proc) + THROW("The special method &1 must be a function", hsp->name); + + if ((hsp->flag & HS_NOPARAM) && func->nparam > 0) + THROW("The special method &1 takes no arguments", hsp->name); + + if (hsp->flag & HS_PUT) + { + if (func->nparam < 1) + THROW("The special method &1 must take at least one argument", hsp->name); + } + + if (hsp->flag & HS_UNKNOWN) + { + if (func->nparam > 0 || !func->vararg) + THROW("The special method &1 must take a variable number of arguments only", hsp->name); + } + + if (hsp->flag & HS_PROPERTY) + { + if (TYPE_get_id(func->type) != T_BOOLEAN) + THROW("The special method &1 must return a boolean", hsp->name); + } + + if (hsp->flag & HS_COMPARE) + { + if (func->type.t.id != T_INTEGER) + THROW("The special method must return an integer"); + if (func->nparam != 1) + THROW("The special method must take exactly one argument"); + } + + if (hsp->flag & HS_ATTACH) + { + if (func->nparam != 2) + THROW("The special method must take exactly two arguments"); + if (func->param[0].type.t.id != T_OBJECT || func->param[1].type.t.id != T_STRING) + THROW("The special method signature is incorrect"); + } + + break; + } + } + + + // We ignore function body + + if (!PATTERN_is_newline(*(JOB->current))) + THROW("Syntax error at function declaration"); + + func->line = JOB->line = PATTERN_index(*(JOB->current)) + 1; + func->start = JOB->current + 1; + + look = JOB->current; + for(;;) + { + pat = *look; + + if (PATTERN_is_newline(pat)) + { + JOB->line = PATTERN_index(pat) + 1; + pat = look[1]; + + if (PATTERN_is(pat, RS_END)) + { + if (PATTERN_is_newline(look[2])) + { + look += 2; + break; + } + if (TRANS_is_end_function(is_proc, &look[2])) + { + look += 3; + break; + } + else + { + if (is_proc && PATTERN_is(look[2], RS_FUNCTION)) + THROW(E_EXPECTED, "END SUB"); + else if (!is_proc && PATTERN_is(look[2], RS_SUB)) + THROW(E_EXPECTED, "END FUNCTION"); + } + } + else if (PATTERN_is_end(pat)) + THROW(E_MISSING, "END"); + } + + look++; + } + + JOB->current = look; + return TRUE; +} + + +static bool header_inherits(void) +{ + int index; + + if (!PATTERN_is(*JOB->current, RS_INHERITS)) + return FALSE; + + /*{ + if (!(PATTERN_is(JOB->current[0], RS_CLASS) + && PATTERN_is(JOB->current[1], RS_INHERITS))) + return FALSE; + JOB->current++; + }*/ + + JOB->current++; + + TRANS_want_class(); + + if (JOB->class->parent != NO_SYMBOL) + THROW("Class already has a parent"); + + index = PATTERN_index(*JOB->current); + + if (strcasecmp(TABLE_get_symbol_name(JOB->class->table, index), JOB->class->name) == 0) + THROW("Cannot inherit itself"); + + JOB->class->parent = CLASS_add_class(JOB->class, index); + /*printf("JOB->class->parent = %d\n", JOB->class->parent);*/ + + JOB->current++; + return TRUE; +} + + +static bool header_option(void) +{ + if (TRANS_is(RS_EXPORT)) + { + if (JOB->class->exported) + THROW("Class is already exported"); + + JOB->class->exported = TRUE; + + if (TRANS_is(RS_OPTIONAL)) + JOB->class->optional = TRUE; + + if (TRANS_is(RS_AS)) + { + TRANS_want_class(); + JOB->class->export_name = STR_copy(TABLE_get_symbol_name(JOB->class->table, PATTERN_index(*JOB->current))); + JOB->current++; + } + else if (TRANS_is(RS_TO)) + { + if (TRANS_is(RS_DEFAULT)) + { + JOB->class->export_name = NULL; + } + else + { + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected for namespace"); + JOB->class->export_name = STR_cat(TABLE_get_symbol_name(JOB->class->table, PATTERN_index(*JOB->current)), ":", JOB->class->name, NULL); + JOB->current++; + } + } + else if (COMP_default_namespace) + { + JOB->class->export_name = STR_cat(COMP_default_namespace, ":", JOB->class->name, NULL); + } + + return TRUE; + } + + if (TRANS_is(RS_CREATE)) + { + if (PATTERN_is_newline(JOB->current[0]) || PATTERN_is(JOB->current[0], RS_STATIC)) + { + JOB->class->autocreate = TRUE; + TRANS_ignore(RS_STATIC); + return TRUE; + } + else if (TRANS_is(RS_PRIVATE)) + { + JOB->class->nocreate = TRUE; + return TRUE; + } + } + + if (TRANS_is(RS_FAST)) + { + JOB->class->all_fast = TRUE; + JOB->class->has_fast = TRUE; + + if (TRANS_is(RS_UNSAFE)) + JOB->class->all_unsafe = TRUE; + + return TRUE; + } + + return FALSE; +} + + +static bool header_library(void) +{ + if (!TRANS_is(RS_LIBRARY)) + return FALSE; + + if (!PATTERN_is_string(*JOB->current)) + THROW("Extern library name must be a string"); + + JOB->default_library = PATTERN_index(*JOB->current); + JOB->current++; + return TRUE; +} + + +static bool header_structure(void) +{ + PATTERN *look = JOB->current; + bool is_public; + CLASS_STRUCT *structure; + VARIABLE *field; + TRANS_DECL decl; + int nfield; + int index; + + check_public_private(&look, &is_public); + + if (!PATTERN_is(*look, RS_STRUCT)) + return FALSE; + look++; + JOB->current = look; + + if (!is_public) + THROW("Structures must be public"); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. STRUCT must be followed by an identifier"); + + structure = ARRAY_add_void(&JOB->class->structure); + ARRAY_create(&structure->field); + nfield = 0; + + structure->index = PATTERN_index(*JOB->current); + index = CLASS_add_class_exported(JOB->class, structure->index); + JOB->class->class[index].structure = TRUE; + //fprintf(stderr, "Set structure flag to %s\n", TABLE_get_symbol_name(JOB->class->table, structure->index)); + +//TABLE_copy_symbol_with_prefix(JOB->class->table, structure->index, '.', NULL, &structure->index); + + JOB->current++; + TRANS_want_newline(); + + for(;;) + { + do + { + if (PATTERN_is_end(*JOB->current)) // || PATTERN_is_command(*JOB->current)) + THROW ("Missing END STRUCT"); + } + while (TRANS_newline()); + + if (PATTERN_is(*JOB->current, RS_END) && PATTERN_is(JOB->current[1], RS_STRUCT)) + { + if (nfield == 0) + THROW ("Syntax error. A structure must have one field at least."); + + JOB->current += 2; + + TRANS_want_newline(); + break; + } + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. The &1 field is not a valid identifier", TRANS_get_num_desc(nfield + 1)); + + field = ARRAY_add(&structure->field); + field->index = PATTERN_index(*JOB->current); + + + JOB->current++; + + CLEAR(&decl); + if (!TRANS_type(TT_CAN_ARRAY | TT_CAN_EMBED, &decl)) + THROW("Syntax error. Invalid type description of &1 field", TRANS_get_num_desc(nfield + 1)); + + TRANS_want_newline(); + + field->type = decl.type; + + nfield++; + } + + structure->nfield = nfield; + + return TRUE; +} + +static bool header_use(void) +{ + if (!TRANS_is(RS_USE)) + return FALSE; + + CLASS_set_current_init_function(JOB->class, FUNC_INIT_STATIC); + + for(;;) + { + if (!PATTERN_is_string(*JOB->current)) + THROW("Component name must be a string"); + + TRANS_string(*JOB->current); + TRANS_subr(TS_SUBR_USE, 1); + CODE_drop(); + + JOB->current++; + + if (!TRANS_is(RS_COMMA)) + break; + } + + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + return TRUE; +} + + +static void check_class_header() +{ + while (TRUE) //(JOB->current < JOB->end) + { + if (PATTERN_is_end(*JOB->current)) + break; + + if (TRANS_newline()) + continue; + + if (header_option()) + continue; + + if (header_inherits()) + continue; + + break; + } +} + +void HEADER_do(void) +{ + union { + TRANS_DECL decl; + TRANS_FUNC func; + TRANS_EVENT event; + TRANS_EXTERN ext; + TRANS_PROPERTY prop; + } trans; + + TRANS_reset(); + + header_module_type(); + + if (JOB->line == 1) + check_class_header(); + + while (TRUE) //JOB->current < JOB->end) + { + if (PATTERN_is_end(*JOB->current)) + break; + + if (TRANS_newline()) + { + if (JOB->line == 1) + check_class_header(); + + continue; + } + + if (header_function(&trans.func)) + { + CLASS_add_function(JOB->class, &trans.func); + continue; + } + + if (header_event(&trans.event)) + { + CLASS_add_event(JOB->class, &trans.event); + continue; + } + + if (header_property(&trans.prop)) + { + CLASS_add_property(JOB->class, &trans.prop); + continue; + } + + if (header_extern(&trans.ext)) + { + CLASS_add_extern(JOB->class, &trans.ext); + continue; + } + + if (header_enumeration(&trans.decl)) + continue; + + if (header_declaration(&trans.decl)) + { + CLASS_add_declaration(JOB->class, &trans.decl); + continue; + } + + if (header_structure()) + continue; + + if (header_class(&trans.decl)) + { + CLASS_add_class_exported(JOB->class, trans.decl.index); + continue; + } + + if (header_inherits()) + continue; + + if (header_library()) + continue; + + if (header_use()) + continue; + + /*if (PATTERN_is_command(*JOB->current)) + { + JOB->current++; + continue; + }*/ + + THROW_UNEXPECTED(JOB->current); + } + + // Sort class declaration to avoid alignment problems. + // This should be useless now, as it is done again by the interpreter + // when loading the class. + + CLASS_sort_declaration(JOB->class); + + if (COMP_verbose) + CLASS_dump(); +} diff --git a/main/gbc/gbc_header.h b/main/gbc/gbc_header.h new file mode 100644 index 00000000..1a60b4d9 --- /dev/null +++ b/main/gbc/gbc_header.h @@ -0,0 +1,69 @@ +/*************************************************************************** + + gbc_header.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_HEADER_H +#define __GBC_HEADER_H + +#include "gbc_type.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gb_limit.h" +#include "gbc_trans.h" + + +enum { + HF_NORMAL = 0, + HF_NO_3PTS = 1, + HF_EVENT = 2, + HF_NO_OPT = 4, + HF_VOID = 8, + HF_NO_BYREF = 16 + }; + +enum { + HS_ERROR = 0, + HS_PUBLIC = 1 << 0, + HS_STATIC = 1 << 1, + HS_DYNAMIC = 1 << 2, + HS_PROCEDURE = 1 << 3, + HS_FUNCTION = 1 << 4, + HS_PUT = 1 << 5, + HS_UNKNOWN = 1 << 6, + HS_PROPERTY = 1 << 7, + HS_NOPARAM = 1 << 8, + HS_COMPARE = 1 << 9, + HS_ATTACH = 1 << 10 + }; + +typedef + struct { + char *name; + int flag; + } + HEADER_SPECIAL; + + +void HEADER_do(void); + +#endif + diff --git a/main/gbc/gbc_help.c b/main/gbc/gbc_help.c new file mode 100644 index 00000000..2d93891c --- /dev/null +++ b/main/gbc/gbc_help.c @@ -0,0 +1,182 @@ +/*************************************************************************** + + gbc_help.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_HELP_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_file.h" + +#include "gbc_compile.h" +#include "gbc_help.h" + +//#define DEBUG_ME 1 + +static bool get_help_at(int i, const char **help, int *len) +{ + const char *p; + int n = 0; + + if (i < 0 || i >= ARRAY_count(JOB->help)) + return TRUE; + + p = JOB->help[i]; + if (!p) + return TRUE; + + if (HELP_is_void_line(p)) + { + *help = p; + n = 0; + } + else + { + while (*p == '\'') + p++; + if (*p == ' ') + p++; + *help = p; + while (*p++ != '\n') + n++; + } + + *len = n; + return FALSE; +} + +void HELP_add_at_current_line(const char *help) +{ + int count = ARRAY_count(JOB->help); + int new_max; + + if (!count) + { + if (HELP_is_void_line(help)) + return; + + ARRAY_create_inc(&JOB->help, 256); + + JOB->help_first_line = JOB->line; + } + else + { + new_max = JOB->line - JOB->help_first_line; + + if (new_max < count) // Already added! + return; + + if (new_max > count) + ARRAY_add_many_void(&JOB->help, new_max - count); + } + + *ARRAY_add(&JOB->help) = help; + + #if DEBUG_ME + int len; + get_help_at(JOB->line - JOB->help_first_line, &help, &len); + fprintf(stderr, "add help comment: %d %p %d: %.*s\n", JOB->line, help, ARRAY_count(JOB->help), len, help); + #endif +} + +void HELP_search_and_print(FILE *file, int line) +{ + const char *help; + int len, i, ii; + + if (!JOB->help) + return; + + #if DEBUG_ME + fprintf(stderr, "HELP_search_and_print: %s: %d\n", FILE_get_name(JOB->name), line); + #endif + + if (line >= FORM_FIRST_LINE) + return; + + i = line - JOB->help_first_line; + + if (i < 0) + return; + + if (JOB->help[i]) + { + if (!get_help_at(i, &help, &len)) + { + #if DEBUG_ME + fprintf(stderr, "[%d] '' %.*s\n", line, len, help); + #endif + fprintf(file, "'%.*s\n", len, help); + JOB->help[i] = NULL; + } + } + else + { + ii = i; + i--; + while (i >= 0) + { + if (!JOB->help[i] || HELP_is_for_class(JOB->help[i])) + break; + i--; + } + + for (i++; i < ii; i++) + { + if (HELP_is_void_line(JOB->help[i])) + continue; + + if (get_help_at(i, &help, &len)) + continue; + + #if DEBUG_ME + fprintf(stderr, "{%d} ''%.*s\n", line + i - ii, len, help); + #endif + fprintf(file, "'%.*s\n", len, help); + } + } +} + +void HELP_search_and_print_for_class(FILE *file) +{ + int i; + const char *help; + int len; + + if (!JOB->help) + return; + + for (i = 0; i < ARRAY_count(JOB->help); i++) + { + if (!JOB->help[i] || !HELP_is_for_class(JOB->help[i])) + break; + + if (get_help_at(i, &help, &len)) + break; + + #if DEBUG_ME + fprintf(stderr, "[%d] '''%.*s\n", JOB->help_first_line + i, len, help); + #endif + fprintf(file, "'%.*s\n", len, help); + } +} + diff --git a/main/gbc/gbc_help.h b/main/gbc/gbc_help.h new file mode 100644 index 00000000..6220fbd0 --- /dev/null +++ b/main/gbc/gbc_help.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gbc_help.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_HELP_H +#define __GBC_HELP_H + +#include + +#ifndef __GBC_HELP_C +#endif + +void HELP_add_at_current_line(const char *help); +void HELP_search_and_print(FILE *file, int line); +void HELP_search_and_print_for_class(FILE *file); + +#define HELP_is_help_comment(_ptr) ((_ptr)[0] == '\'' && (isspace((_ptr)[1]) || ((_ptr)[1] == '\'' && isspace((_ptr)[2])))) +#define HELP_is_for_class(_help) ((_help)[0] == '\'' && (_help)[1] == '\'') +#define HELP_is_void_line(_help) ((_help)[0] == '\n' && (_help)[1] == 0) + +#endif diff --git a/main/gbc/gbc_output.c b/main/gbc/gbc_output.c new file mode 100644 index 00000000..0c902900 --- /dev/null +++ b/main/gbc/gbc_output.c @@ -0,0 +1,1424 @@ +/*************************************************************************** + + gbc_output.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_OUTPUT_C + +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_common_swap.h" +#include "gb_magic.h" +#include "gbc_chown.h" +#include "gbc_compile.h" +#include "gbc_form.h" +#include "gbc_output.h" + + +//#define DEBUG +//#define DEBUG_MORE + +static CLASS *_class; + +static int StringAddr; +static TABLE *StringTable; +static int NSection; +static int PosStartSection; +static int SizeSection; + +static bool _swap; + +static FILE *_file; + +static int _pos; +static char _buffer[OUTPUT_BUFFER_SIZE + 16]; +static const char * const _mbuffer = &_buffer[OUTPUT_BUFFER_SIZE]; +static char *_pbuffer; + +static OUTPUT_CHANGE *_change = NULL; + +static char *_const_buffer = NULL; + +static const char *get_symbol_name(TABLE *table, int index, int *len) +{ + if (index < 0 || index >= ARRAY_count(table->symbol)) + { + *len = 1; + return "?"; + } + else + { + SYMBOL *sym = TABLE_get_symbol(table, index); + *len = sym->len; + return sym->name; + } +} + +static void output_init(void) +{ + TABLE_create(&StringTable, sizeof(OUTPUT_SYMBOL), TF_NORMAL); + StringAddr = 0; + NSection = 0; + _pos = 0; + _pbuffer = _buffer; + ARRAY_create(&_change); + BUFFER_create(&_const_buffer); +} + + +static void output_exit(void) +{ + TABLE_delete(&StringTable); + ARRAY_delete(&_change); + BUFFER_delete(&_const_buffer); +} + + +static int get_string(const char *string, int len) +{ + OUTPUT_SYMBOL *sym; + int index; + bool new; + + if (len < 0) + len = strlen(string); + + new = !TABLE_add_symbol_exist(StringTable, string, len, &index); + sym = (OUTPUT_SYMBOL *)TABLE_get_symbol(StringTable, index); + + if (new) + { + sym->value = StringAddr; + StringAddr += len + 1; + } + + #ifdef DEBUG + printf("'%.*s' -> %ld\n", len, string, sym->value); + #endif + + return sym->value; +} + +#define get_pos() (_pos) + +static void flush_buffer(void) +{ + size_t len = _pbuffer - _buffer; + + if (len <= 0) + return; + + if (fwrite(_buffer, sizeof(char), len, _file) != len) + THROW("Write error"); + + _pbuffer = _buffer; +} + + +static void write_byte(unsigned char val) +{ + #ifdef DEBUG_MORE + printf("%ld : b %u 0x%X\n", get_pos(), val, val); + #endif + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *_pbuffer++ = val; + _pos++; + + /*if (fwrite(&val, sizeof(char), 1, _file) != 1) + THROW("Write error");*/ +} + + +static void write_short(ushort val) +{ + #ifdef DEBUG_MORE + printf("%ld : h %u 0x%X\n", get_pos(), val, val); + #endif + + if (_swap) + SWAP_short((short *)&val); + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *((ushort *)_pbuffer) = val; + _pbuffer += sizeof(val); + _pos += sizeof(val); +} + + +static void write_int(uint val) +{ + #ifdef DEBUG_MORE + printf("%ld : i %lu 0x%lX\n", get_pos(), val, val); + #endif + + if (_swap) + SWAP_int((int *)&val); + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *((uint *)_pbuffer) = val; + _pbuffer += sizeof(val); + _pos += sizeof(val); +} + + +static void write_int64(uint64_t val) +{ + #ifdef DEBUG_MORE + printf("%ld : l %llu 0x%llX\n", get_pos(), val, val); + #endif + + if (_swap) + SWAP_int64((int64_t *)&val); + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *((uint64_t *)_pbuffer) = val; + _pbuffer += sizeof(val); + _pos += sizeof(val); +} + +static void write_string(const char *str, int len) +{ + #ifdef DEBUG_MORE + printf("%ld : s \"%.*s\"\n", get_pos(), len, str); + #endif + + if (&_pbuffer[len] > _mbuffer) + flush_buffer(); + + if (&_pbuffer[len] <= _mbuffer) + { + memcpy(_pbuffer, str, len); + _pbuffer += len; + *_pbuffer++ = 0; + _pos += len + 1; + return; + } + + if (fwrite(str, sizeof(char), len, _file) != len) + THROW("Write error"); + + _pos += len; + write_byte(0); +} + + +static void write_buffer(void *str, int len) +{ + #ifdef DEBUG_MORE + printf("%ld : buffer %ld octets\n", get_pos(), len); + #endif + + if (len == 0) + return; + + if (&_pbuffer[len] > _mbuffer) + flush_buffer(); + + if (&_pbuffer[len] <= _mbuffer) + { + memcpy(_pbuffer, str, len); + _pbuffer += len; + _pos += len; + return; + } + + flush_buffer(); + + if (fwrite(str, sizeof(char), len, _file) != len) + THROW("Write error"); + + _pos += len; +} + + +static void write_pad(void) +{ + while (get_pos() & 0x3) + write_byte(0); +} + + +static void write_type(TYPE type) +{ + write_byte(type.t.flag); + write_byte(type.t.id); + write_short(type.t.value); +} + + +static void add_change(off_t pos, uint val) +{ + int prev = get_pos(); + char *ppos; + OUTPUT_CHANGE *change; + + ppos = &_buffer[pos - (prev - (_pbuffer - _buffer))]; + if (ppos >= _buffer && ppos < (_pbuffer - sizeof(uint))) + { + *((uint *)ppos) = val; + return; + } + + change = ARRAY_add(&_change); + change->pos = pos; + change->val = val; +} + +static void begin_section(const char *name, int size) +{ + NSection++; + #ifdef DEBUG + printf("Section #%d : %s\n", NSection, name); + #endif + + if (size) + { + PosStartSection = get_pos(); + write_int(0); + } + else + PosStartSection = 0; + + SizeSection = size; +} + + +static void end_section(void) +{ + int len; + + if (PosStartSection) + { + write_pad(); + len = get_pos() - PosStartSection - sizeof(int); + add_change(PosStartSection, len); + + #ifdef DEBUG + printf("==> %ld %s\n", len / SizeSection, (SizeSection == 1) ? "bytes" : "elements"); + if (len % SizeSection) + printf("*** remain %ld bytes\n", len % SizeSection); + #endif + } +} + + +static void output_header(void) +{ + begin_section("Header", 0); + + /* magic */ + write_int(OUTPUT_MAGIC); + /* version */ + write_int(COMP_version); + /* endianness */ + write_int(OUTPUT_ENDIAN); + /* flag */ + if (JOB->debug) + write_int(1); + else + write_int(0); + + end_section(); +} + + +static void output_class(void) +{ + short flag; + begin_section("_class", 1); + + // Parent class + write_short(_class->parent); + + // Class flags + flag = 0; + if (_class->exported) flag |= 1; + if (_class->autocreate) flag |= 2; + if (_class->optional) flag |= 4; + if (_class->nocreate) flag |= 8; + if (_class->has_fast) flag |= 16; + if (JOB->is_test) flag |= 32; + + write_short(flag); + + // Static size + write_int(_class->size_stat); + + // Dynamic size + write_int(_class->size_dyn); + + // Number of structures + write_short(ARRAY_count(_class->structure)); + + // reserved + write_short(0); + + end_section(); +} + + +static void output_desc(void) +{ + int i, n, nn = 0; + CLASS_SYMBOL *csym; + TYPE type; + short out_type; + + n = TABLE_count(_class->table); + + begin_section("Description", 6 * sizeof(int)); + + for (i = 0; i < n; i++) + { + csym = (CLASS_SYMBOL *)TABLE_get_symbol_sort(_class->table, i); + //csym = (CLASS_SYMBOL *)TABLE_get_symbol(_class->table, csym->symbol.sort); + + type = csym->global.type; + + if (TYPE_is_public(type)) + { + nn++; + /* name */ + write_int(get_string(csym->symbol.name, csym->symbol.len)); + /* datatype */ + write_type(csym->global.type); + + switch (TYPE_get_kind(type)) + { + case TK_VARIABLE: + + /* offset */ + write_int(csym->global.value); + /* read */ + write_int(0); + /* write */ + write_int(0); + + if (TYPE_is_static(type)) + out_type = CD_STATIC_VARIABLE_ID; + else + out_type = CD_VARIABLE_ID; + + break; + + case TK_PROPERTY: + + /* read */ + write_int(_class->prop[csym->global.value].read); + /* write */ + write_int(_class->prop[csym->global.value].write); + /* flag */ + write_int(0); + + if (TYPE_is_static(type)) + out_type = CD_STATIC_PROPERTY_ID; + else + out_type = CD_PROPERTY_ID; + + break; + + case TK_CONST: + + /* param */ + write_int(csym->global.value); + /* read */ + write_int(0); + /* write */ + write_int(0); + + out_type = CD_CONSTANT_ID; + + break; + + case TK_FUNCTION: + + /* exec */ + write_int(csym->global.value); + /* signature */ + write_int(0); + /* nparam */ + write_int(0); + + if (TYPE_is_static(type)) + out_type = CD_STATIC_METHOD_ID; + else + out_type = CD_METHOD_ID; + + break; + + case TK_EVENT: + + /* exec */ + write_int(csym->global.value); + /* signature */ + write_int(0); + /* nparam */ + write_int(0); + + out_type = CD_EVENT_ID; + + break; + + case TK_EXTERN: + + /* exec */ + write_int(csym->global.value); + /* signature */ + write_int(0); + /* nparam */ + write_int(0); + + out_type = CD_EXTERN_ID; + + break; + + default: + + ERROR_panic("output_desc: unknown symbol type"); + continue; + } + + /* type de symbole */ + write_int(out_type); + } + } + + end_section(); +} + + +static void output_constant(void) +{ + int i, n; + CONSTANT *constant; + SYMBOL *sym; + //TABLE *table; + + n = ARRAY_count(_class->constant); + + begin_section("Constants", 3 * sizeof(int)); + + for (i = 0; i < n; i++) + { + constant = &_class->constant[i]; + + /* type */ + write_type(constant->type); + /* value */ + switch (TYPE_get_id(constant->type)) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: + + write_int(constant->value); + write_int(0); + break; + + case T_LONG: + + write_int64(constant->lvalue); + break; + + case T_SINGLE: case T_FLOAT: + + if (constant->is_integer) + { + char buffer[8]; + int len; + offset_t pos; + + len = sprintf(buffer, "%d", constant->value); + + pos = BUFFER_add(&_const_buffer, buffer, len); + //fprintf(stderr, "output_constant: integer: %.*s\n", len, buffer); + write_int(get_string(&_const_buffer[pos], len)); + write_int(len); + } + else + { + sym = TABLE_get_symbol(_class->table, constant->value); + //fprintf(stderr, "output_constant: float: %.*s\n", sym->len, sym->name); + write_int(get_string(sym->name, sym->len)); + write_int(sym->len); + } + break; + + case T_STRING: case T_CSTRING: + + if (constant->value == VOID_STRING_INDEX) + { + write_int(0); + write_int(0); + } + else + { + sym = TABLE_get_symbol(_class->string, constant->value); + write_int(get_string(sym->name, sym->len)); + write_int(sym->len); + } + break; + } + } + + end_section(); +} + + +static void output_class_ref(void) +{ + int i, n; + SYMBOL *sym; + CLASS_REF *ref; + + n = ARRAY_count(_class->class); + + begin_section("External classes", sizeof(int)); + + for (i = 0; i < n; i++) + { + ref = &_class->class[i]; + sym = TABLE_get_symbol(_class->table, ref->index); + if (ref->used) + { + if (ref->exported) + write_int(-get_string(sym->name, sym->len)); + else + write_int(get_string(sym->name, sym->len)); + } + else + { + if (COMP_verbose) + printf("Ignoring class %.*s\n", sym->len, sym->name); + write_int(-1); + } + } + + end_section(); +} + + +static void output_unknown_ref(void) +{ + int i, n; + SYMBOL *sym; + + n = ARRAY_count(_class->unknown); + + begin_section("External symbols", sizeof(int)); + + for (i = 0; i < n; i++) + { + sym = TABLE_get_symbol(_class->table, _class->unknown[i]); + write_int(get_string(sym->name, sym->len)); + } + + end_section(); +} + + +static void output_static(void) +{ + int i, n; + VARIABLE *var; + + n = ARRAY_count(_class->stat); + + begin_section("Static variables", 2 * sizeof(int)); + + for (i = 0; i < n; i++) + { + var = &_class->stat[i]; + + /* type */ + write_type(var->type); + /* addr */ + write_int(0); + } + + end_section(); +} + + +static void output_dynamic(void) +{ + int i, n; + VARIABLE *var; + + n = ARRAY_count(_class->dyn); + + begin_section("Dynamic variables", 2 * sizeof(int)); + + for (i = 0; i < n; i++) + { + var = &_class->dyn[i]; + + /* type */ + write_type(var->type); + /* addr */ + write_int(0); + } + + end_section(); +} + + +static void output_event(void) +{ + int i, n; + EVENT *event; + SYMBOL *sym; + + n = ARRAY_count(_class->event); + + begin_section("Events", 4 * sizeof(int)); + + for (i = 0; i < n; i++) + { + event = &_class->event[i]; + + /* type */ + write_type(event->type); + /* n_param */ + write_short(event->nparam); + /* reserved */ + write_short(0); + /* desc_param */ + write_int(0); + /* name */ + sym = TABLE_get_symbol(_class->table, event->name); + write_int(get_string(sym->name, sym->len)); + } + + end_section(); +} + + +static void output_extern(void) +{ + int i, n; + EXTFUNC *ext; + SYMBOL *sym; + + n = ARRAY_count(_class->ext_func); + + begin_section("Extern functions", 5 * sizeof(int)); + + for (i = 0; i < n; i++) + { + ext = &_class->ext_func[i]; + + /* type */ + write_type(ext->type); + /* n_param */ + write_short(ext->nparam); + /* vararg */ + write_byte(ext->vararg); + /* reserved */ + write_byte(0); + /* desc_param */ + write_int(0); + /* name */ + /*sym = TABLE_get_symbol(_class->table, ext->name); + write_int(get_string(sym->name, sym->len));*/ + /* alias name */ + if (ext->alias == NO_SYMBOL) + sym = TABLE_get_symbol(_class->table, ext->name); + else + sym = TABLE_get_symbol(_class->string, ext->alias); + write_int(get_string(sym->name, sym->len)); + /* library name */ + sym = TABLE_get_symbol(_class->string, ext->library); + write_int(get_string(sym->name, sym->len)); + } + + end_section(); +} + + +static void output_method(void) +{ + int i, n; + FUNCTION *func; + uchar flag; + /*SYMBOL *sym;*/ + + n = ARRAY_count(_class->function); + + begin_section("Methods", 8 * sizeof(int)); + + for (i = 0; i < n; i++) + { + func = &_class->function[i]; + + write_type(func->type); + write_byte(func->nparam); + write_byte(func->npmin); + write_byte(func->vararg); + + flag = func->fast; + if (func->use_is_missing) + flag += 2; + if (func->unsafe) + flag += 4; + + write_byte(flag); + + write_short(func->nlocal); + write_short(func->nctrl); + write_short(func->stack); + + /* gestion d'erreur */ + if (func->catch && func->finally) + write_short(Min(func->finally, func->catch)); + else if (func->catch) + write_short(func->catch); + else + write_short(func->finally); + + /* addr_code */ + write_int(func->ncode); + /* desc_param */ + write_int(0); + /* desc_local */ + write_int(0); + /* debug_info */ + write_int(0); + + } + + end_section(); +} + + +static void output_param_local(void) +{ + int i, j; + FUNCTION *func; + EVENT *event; + EXTFUNC *ext; + PARAM *param; + + begin_section("Parameters", sizeof(int)); + + for (i = 0; i < ARRAY_count(_class->function); i++) + { + func = &_class->function[i]; + + for (j = 0; j < func->nparam; j++) + { + param = &func->param[j]; + write_type(param->type); + } + for (j = 0; j < func->nlocal; j++) + { + param = &func->local[j + func->nparam]; + write_type(param->type); + } + } + + for (i = 0; i < ARRAY_count(_class->event); i++) + { + event = &_class->event[i]; + + for (j = 0; j < event->nparam; j++) + { + param = &event->param[j]; + + /* type */ + write_type(param->type); + } + } + + for (i = 0; i < ARRAY_count(_class->ext_func); i++) + { + ext = &_class->ext_func[i]; + + for (j = 0; j < ext->nparam; j++) + { + param = &ext->param[j]; + + /* type */ + write_type(param->type); + } + } + + end_section(); +} + + +static void output_array(void) +{ + int i, j, p; + CLASS_ARRAY *array; + + begin_section("Arrays", sizeof(int)); + + p = ARRAY_count(_class->array) * sizeof(int); + + for (i = 0; i < ARRAY_count(_class->array); i++) + { + array = &_class->array[i]; + write_int(p); + p += sizeof(int) + array->ndim * sizeof(int); + } + + for (i = 0; i < ARRAY_count(_class->array); i++) + { + array = &_class->array[i]; + + write_type(array->type); + + for (j = 0; j < array->ndim; j++) + { + p = array->dim[j]; + if (j == (array->ndim - 1)) + p = (-p); + + write_int(p); + } + } + + end_section(); +} + + +static void output_structure(void) +{ + int i, j; + CLASS_STRUCT *structure; + VARIABLE *field; + SYMBOL *sym; + + for (i = 0; i < ARRAY_count(_class->structure); i++) + { + structure = &_class->structure[i]; + + begin_section("Structure", sizeof(int)); + + // Structure name + sym = TABLE_get_symbol(_class->table, structure->index); + write_int(get_string(sym->name, sym->len)); + + for (j = 0; j < structure->nfield; j++) + { + field = &structure->field[j]; + + // Field name + sym = TABLE_get_symbol(_class->table, field->index); + write_int(get_string(sym->name, sym->len)); + + // Field datatype + write_type(field->type); + } + + end_section(); + } +} + +static void output_code() +{ + int i, j; + int n; + FUNCTION *func; + + for (i = 0; i < ARRAY_count(_class->function); i++) + { + func = &_class->function[i]; + + n = func->ncode; + + begin_section("Code", sizeof(short)); + if (_swap) + { + for (j = 0; j < n; j++) + write_short(func->code[j]); + } + else + write_buffer(func->code, n * sizeof(short)); + end_section(); + } +} + + +static void output_debug_global() +{ + int i, nn = 0; + CLASS_SYMBOL *csym; + TYPE type; + + begin_section("Global symbol table", 4 * sizeof(int)); + + for (i = 0; i < TABLE_count(_class->table); i++) + { + csym = (CLASS_SYMBOL *)TABLE_get_symbol_sort(_class->table, i); + //csym = (CLASS_SYMBOL *)TABLE_get_symbol(_class->table, csym->symbol.sort); + + type = csym->global.type; + switch (TYPE_get_kind(type)) + { + case TK_VARIABLE: + case TK_FUNCTION: + case TK_PROPERTY: + case TK_EXTERN: + case TK_CONST: + + nn++; + /* name */ + write_int(get_string(csym->symbol.name, csym->symbol.len)); + /* len */ + write_int(csym->symbol.len); + /* type */ + write_type(csym->global.type); + /* value */ + write_int(csym->global.value); + + break; + + default: + /* ignore */ + break; + } + } + + end_section(); + + begin_section("Global symbol table sort", sizeof(ushort)); + + for (i = 0; i < nn; i++) + write_short(i); + + end_section(); +} + + +static void output_debug_method() +{ + int i, j, n; + OUTPUT_SYMBOL *osym; + PARAM *param; + FUNCTION *func; + TABLE *table; + int index; + const char *name; + int len; + + begin_section("Debug method info", 5 * sizeof(int)); + + for (i = 0; i < ARRAY_count(_class->function); i++) + { + func = &_class->function[i]; + + if (func->pos_line != NULL && func->line < FORM_FIRST_LINE && func->name != NO_SYMBOL) + { + /* line */ + write_short(func->line); + write_short(ARRAY_count(func->pos_line)); + /* pos_line */ + write_int(0); + /* nom */ + name = get_symbol_name(_class->table, func->name, &len); + write_int(get_string(name, len)); + /* local symbols */ + write_int(0); + /* n_local */ + write_short(0); + /* reserved */ + write_short(0); + } + else + { + func->no_debug = TRUE; + write_short(0); + write_short(0); + write_int(0); + write_int(0); + write_int(0); + write_short(0); + write_short(0); + } + } + + end_section(); + + for (i = 0; i < ARRAY_count(_class->function); i++) + { + func = &_class->function[i]; + + begin_section("Debug method lines", sizeof(short)); + + if (!func->no_debug) + { + n = func->pos_line ? ARRAY_count(func->pos_line) : 0; + + if (_swap) + { + for (j = 0; j < n; j++) + write_short(func->pos_line[j]); + } + else + write_buffer(func->pos_line, n * sizeof(short)); + } + + end_section(); + } + + for (i = 0; i < ARRAY_count(_class->function); i++) + { + func = &_class->function[i]; + + begin_section("Debug method local symbols", sizeof(int) * 3); + + if (!func->no_debug) + { + TABLE_create(&table, sizeof(OUTPUT_SYMBOL), TF_IGNORE_CASE); + + //for (j = 0; j < func->nlocal + func->nparam; j++) + for (j = 0; j < ARRAY_count(func->local); j++) + { + param = &func->local[j]; + + name = get_symbol_name(_class->table, param->index, &len); + index = TABLE_add_symbol(table, name, len); + osym = (OUTPUT_SYMBOL *)TABLE_get_symbol(table, index); + osym->value = param->value;/*TYPE_long(param->type);*/ + } + + for (j = 0; j < ARRAY_count(func->stat); j++) + { + param = &func->stat[j]; + + name = get_symbol_name(_class->table, param->index, &len); + index = TABLE_add_symbol(table, name, len); + osym = (OUTPUT_SYMBOL *)TABLE_get_symbol(table, index); + osym->value = param->value;/*TYPE_long(param->type);*/ + } + + for (j = 0; j < TABLE_count(table); j++) + { + param = &func->local[j]; + osym = (OUTPUT_SYMBOL *)TABLE_get_symbol(table, j); + + /* name */ + write_int(get_string(osym->sym.name, osym->sym.len)); + /* len */ + write_int(osym->sym.len); + /* value */ + write_int(osym->value); + + /*printf("%.*s %ld\n", osym->sym.len, osym->sym.name, osym->value);*/ + } + + // We actually do not use the table sort, so do not output it! + + TABLE_delete(&table); + } + + end_section(); + } +} + + +static void output_debug_filename(void) +{ + begin_section("Debug file name", 1); + + // file name is ignored, don't put it in the file anymore + + /* + path = (char *)FILE_get_name(JOB->name); + + n = strlen(path); + write_buffer(path, n); + */ + + end_section(); +} + + +static void output_string(void) +{ + int i; + SYMBOL *sym; + + begin_section("Strings", 1); + + for (i = 0; i < TABLE_count(StringTable); i++) + { + sym = TABLE_get_symbol(StringTable, i); + write_string(sym->name, sym->len); + } + + end_section(); +} + + +char *OUTPUT_get_file(const char *file) +{ + char *output; + char *p; + char *name; + + name = STR_copy(FILE_get_name(file)); + + for (p = name; *p; p++) + { + if (*p == '.') + { + *p = 0; + break; + } + + *p = toupper(*p); + } + + output = ".gambas"; + if (mkdir(output, 0777) == 0) + FILE_set_owner(output, COMP_project); + + output = STR_copy(FILE_cat(output, name, NULL)); + + //STR_free(dir); + STR_free(name); + + return output; +} + + +char *OUTPUT_get_trans_file(const char *file) +{ + char *output; + //char *dir; + char *name; + + //dir = STR_copy(FILE_get_dir(file)); + name = STR_copy(FILE_get_name(file)); + + output = ".lang"; //(char *)FILE_cat(dir, ".lang", NULL); + if (mkdir(output, 0777) == 0) + FILE_set_owner(output, COMP_project); + + output = (char *)FILE_cat(".lang", name, NULL); + output = STR_copy(FILE_set_ext(output, "pot")); + + //STR_free(dir); + STR_free(name); + + return output; +} + +static void output_translation(void) +{ + FILE *file; + int i, j, n; + CONSTANT *constant; + SYMBOL *sym; + unsigned char c; + time_t t; + struct tm *now; + + /*printf("Generating %s\n", JOB->tname);*/ + + if (!JOB->trans) + { + JOB->tname = OUTPUT_get_trans_file(JOB->name); + FILE_unlink(JOB->tname); + return; + } + + file = fopen(JOB->tname, "w"); + if (!file) + THROW("Cannot create file: &1", JOB->tname); + + fprintf(file, "# %s\n# Generated by the Gambas " GAMBAS_FULL_VERSION_STRING " compiler\n\n", JOB->name); + + fprintf(file, + "#, fuzzy\n" + "msgid \"\"\n" + "msgstr \"\"\n" + "\"Project-Id-Version: $(PACKAGE) $(VERSION)\\n\"\n"); + + t = time(NULL); + now = gmtime(&t); + + fprintf(file, "\"POT-Creation-Date: %04d-%02d-%02d %02d:%02d UTC\\n\"\n", + now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min); + + // "\"PO-Revision-Date: $(DATE)\\n\"\n" // YEAR-MO-DA HO:MI+ZONE + fprintf(file, + "\"MIME-Version: 1.0\\n\"\n" + "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" + "\"Content-Transfer-Encoding: 8bit\\n\"\n\n"); + + n = ARRAY_count(_class->constant); + + for (i = 0; i < n; i++) + { + constant = &_class->constant[i]; + if (TYPE_get_id(constant->type) != T_CSTRING) + continue; + + sym = TABLE_get_symbol(_class->string, constant->value); + if (sym->len == 0) + continue; + + for (j = 0; j < sym->len; j++) + { + c = sym->name[j]; + if (c > ' ') + break; + } + + if (j >= sym->len) + continue; + + if (constant->line < FORM_FIRST_LINE) + fprintf(file, "#: %s:%d\n", FILE_get_name(JOB->name), constant->line); + else + fprintf(file, "#: %s:%d\n", FILE_get_name(JOB->form), constant->line - FORM_FIRST_LINE); + + fprintf(file, "msgid \""); + + for (j = 0; j < sym->len; j++) + { + c = sym->name[j]; + if (c == '\n') + fprintf(file, "\\n"); + else if (c == '\t') + fprintf(file, "\\t"); + else if (c == '\r') + fprintf(file, "\\r"); + else if (c == '\\') + fprintf(file, "\\\\"); + else if (c == '"') + fprintf(file, "\\\""); + else + fputc(c, file); + } + + fprintf(file, "\"\n"); + fprintf(file, "msgstr \"\"\n\n"); + + sym->len = 0; /* Horrible hack for not writing the same string twice */ + } + + fclose(file); + FILE_set_owner(JOB->tname, COMP_project); +} + + +static void output_finalize(void) +{ + int i; + + flush_buffer(); + + for (i = 0; i < ARRAY_count(_change); i++) + { + fseek(_file, _change[i].pos, SEEK_SET); + if (fwrite(&_change[i].val, sizeof(int), 1, _file) != 1) + THROW("Write error"); + } +} + + +void OUTPUT_do(bool swap) +{ + const char *name; + + _swap = swap; + + output_init(); + + // The first string is always the class name + get_string(JOB->class->name, strlen(JOB->class->name)); + + name = JOB->output; + + #ifdef DEBUG + printf("Output to %s\n", name); + #endif + + _file = fopen(name, "w"); + if (!_file) + THROW("Cannot create file: &1", name); + + _class = JOB->class; + + #ifdef DEBUG + printf("pos = %lu\n", get_pos()); + #endif + + output_header(); + output_class(); + output_desc(); + output_constant(); + output_class_ref(); + output_unknown_ref(); + output_static(); + output_dynamic(); + output_event(); + output_extern(); + output_method(); + output_param_local(); + output_array(); + output_structure(); + output_code(); + + if (JOB->debug) + { + output_debug_global(); + output_debug_method(); + output_debug_filename(); + } + + output_string(); + + output_finalize(); + + fclose(_file); + FILE_set_owner(JOB->output, COMP_project); + + output_exit(); + + // Translations + output_translation(); +} diff --git a/main/gbc/gbc_output.h b/main/gbc/gbc_output.h new file mode 100644 index 00000000..56b2a356 --- /dev/null +++ b/main/gbc/gbc_output.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + gbc_output.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_OUTPUT_H +#define __GBC_OUTPUT_H + +#include "gb_table.h" +#include "gb_class_desc_common.h" + +typedef + struct { + SYMBOL sym; + int value; + } + OUTPUT_SYMBOL; + +typedef + struct { + off_t pos; + uint val; + } + OUTPUT_CHANGE; + +#define OUTPUT_BUFFER_SIZE 16384 + +void OUTPUT_do(bool swap); +char *OUTPUT_get_file(const char *file); +char *OUTPUT_get_trans_file(const char *file); + +#endif + + diff --git a/main/gbc/gbc_pcode.c b/main/gbc/gbc_pcode.c new file mode 100644 index 00000000..2072dcf7 --- /dev/null +++ b/main/gbc/gbc_pcode.c @@ -0,0 +1,28 @@ +/*************************************************************************** + + gbc_pcode.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_PCODE_C + +#define PROJECT_COMP + +#include "gb_pcode_temp.h" diff --git a/main/gbc/gbc_preprocess.c b/main/gbc/gbc_preprocess.c new file mode 100644 index 00000000..b8735290 --- /dev/null +++ b/main/gbc/gbc_preprocess.c @@ -0,0 +1,316 @@ +/*************************************************************************** + + gbc_preprocess.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_PREPROCESS_C + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_table.h" + +#include "gbc_compile.h" +#include "gbc_read.h" +#include "gbc_trans.h" +#include "gbc_preprocess.h" + +typedef + struct { + bool ignore; + int ignore_level; + } + PREP_STATE; + +#define MAX_LEVEL 16 + +int PREP_next_line; + +static PREP_STATE _stack[MAX_LEVEL]; + +static int _level; +static bool _ignore; +static int _ignore_level; +static PATTERN *_current; + + +static int get_expression(void); + +static bool is_current(int res) +{ + if (PATTERN_is(*_current, res)) + { + _current++; + return TRUE; + } + else + return FALSE; +} + +static bool compare_value(const char *value) +{ + PATTERN op; + SYMBOL *sym; + char version[8]; + int n, major, minor; + int diff; + + op = *_current++; + if (!PATTERN_is_reserved(op)) + THROW("Missing operator"); + + if (!PATTERN_is_string(*_current)) + THROW("String expected"); + + sym = TABLE_get_symbol(JOB->class->string, PATTERN_index(*_current)); + _current++; + + if (value) + { + if (strlen(value) != sym->len) + diff = 1; + else + diff = strncasecmp(value, sym->name, sym->len); + + switch (PATTERN_index(op)) + { + case RS_EQUAL: return diff == 0; + case RS_NE: return diff != 0; + default: THROW("Equality or inequality operator expected"); + } + } + else + { + if (sym->len < 1 || sym->len >= sizeof(version)) + THROW("Bad version string"); + + memcpy(version, sym->name, sym->len); + version[sym->len] = 0; + n = sscanf(version, "%d.%d", &major, &minor); + if (n == 0) + THROW("Bad version string"); + else if (n == 1) + minor = 0; + + diff = GAMBAS_VERSION - major; + if (!diff) + diff = GAMBAS_MINOR_VERSION - minor; + + switch (PATTERN_index(op)) + { + case RS_EQUAL: return diff == 0; + case RS_NE: return diff != 0; + case RS_GT: return diff > 0; + case RS_LT: return diff < 0; + case RS_GE: return diff >= 0; + case RS_LE: return diff <= 0; + default: THROW("Comparison operator expected"); + } + } +} + +#define compare_symbol(_symbol, _name, _len) ((strlen(_symbol) == _len) && !strncasecmp((_symbol), (_name), (_len))) + +static int get_symbol(const char *name, int len) +{ + if (compare_symbol("system", name, len)) + return compare_value(SYSTEM); + + if (compare_symbol("arch", name, len) || compare_symbol("architecture", name, len)) + return compare_value(ARCHITECTURE); + + if (compare_symbol("version", name, len) || compare_symbol("gambas", name, len)) + return compare_value(NULL); + + /*if (compare_symbol("debug", name, len)) + return JOB->debug;*/ + + /*if (compare_symbol("true", name, len)) + return TRUE; + + if (compare_symbol("false", name, len)) + return FALSE;*/ + + return FALSE; +} + +static int get_value(void) +{ + int value; + SYMBOL *sym; + + if (is_current(RS_LBRA)) + { + value = get_expression(); + if (!is_current(RS_RBRA)) + THROW("Missing right brace"); + return value; + } + + if (is_current(RS_NOT)) + return !get_value(); + + if (PATTERN_is_identifier(*_current)) + { + sym = TABLE_get_symbol(JOB->class->table, PATTERN_index(*_current)); + _current++; + value = get_symbol(sym->name, sym->len); + return value; + } + else if (is_current(RS_FALSE)) + return 0; + else if (is_current(RS_TRUE)) + return 1; + else if (is_current(RS_DEBUG)) + return JOB->debug; + else if (is_current(RS_EXEC)) + return JOB->exec; + + return 0; +} + +static int get_expression(void) +{ + int value; + + for(;;) + { + value = get_value(); + + if (is_current(RS_AND)) + { + if (!value) + return FALSE; + + continue; + } + + if (is_current(RS_OR)) + { + if (value) + return TRUE; + + continue; + } + + if (PATTERN_is_newline(*_current) || PATTERN_is(*_current, RS_RBRA)) + return value; + + THROW(E_SYNTAX); + } +} + +void PREP_init(void) +{ + _level = 0; + _ignore = FALSE; + _current = NULL; +} + +void PREP_exit(void) +{ + if (_level) + THROW("Missing #Endif"); +} + +int PREP_analyze(PATTERN *line) +{ + bool test; + + if (PATTERN_is(*line, RS_P_IF)) + { + if (_level >= MAX_LEVEL) + THROW("Too many imbricated #If...#Endif"); + + _stack[_level].ignore = _ignore; + _stack[_level].ignore_level = _ignore_level; + + line++; + _level++; + + if (!_ignore) + { + _current = line; + if (!get_expression()) + { + _ignore = TRUE; + _ignore_level = _level; + } + } + } + else if (PATTERN_is(*line, RS_P_ELSE)) + { + //bool else_if = FALSE; + + if (!_level) + THROW_UNEXPECTED(line); + + test = TRUE; + line++; + + if (!_ignore) + { + _ignore = TRUE; + _ignore_level = _level - 1; + } + else + { + if (_level == _ignore_level) + { + if (PATTERN_is(*line, RS_IF)) + { + line++; + + _current = line; + test = get_expression(); + + //else_if = TRUE; + } + } + + if (_level == _ignore_level) + _ignore = !test; + } + } + else if (PATTERN_is(*line, RS_P_ENDIF)) + { + if (!_level) + THROW_UNEXPECTED(line); + + _level--; + _ignore = _stack[_level].ignore; + _ignore_level = _stack[_level].ignore_level; + } + else if (PATTERN_is(*line, RS_P_LINE)) + { + line++; + if (!PATTERN_is_integer(*line)) + THROW_UNEXPECTED(line); + + PREP_next_line = PATTERN_index(*line) - 1; + return PREP_LINE; + } + // else if (PATTERN_is(*line, RS_P_CONST)) TODO + else + THROW(E_SYNTAX); + + return _ignore ? PREP_IGNORE : PREP_CONTINUE; +} diff --git a/main/gbc/gbc_preprocess.h b/main/gbc/gbc_preprocess.h new file mode 100644 index 00000000..6ca816c7 --- /dev/null +++ b/main/gbc/gbc_preprocess.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gbc_preprocess.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_PREPROCESS_H +#define __GBC_PREPROCESS_H + +enum { PREP_CONTINUE, PREP_IGNORE, PREP_LINE }; + +#ifndef __GBC_PREPROCESS_C +extern int PREP_next_line; +#endif + +void PREP_init(void); +void PREP_exit(void); +int PREP_analyze(PATTERN *line); + +#endif diff --git a/main/gbc/gbc_read.c b/main/gbc/gbc_read.c new file mode 100644 index 00000000..b5a00267 --- /dev/null +++ b/main/gbc/gbc_read.c @@ -0,0 +1,901 @@ +/*************************************************************************** + + gbc_read.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_READ_C + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_table.h" +#include "gb_file.h" + +#include "gbc_compile.h" +#include "gbc_class.h" +#include "gbc_preprocess.h" +#include "gbc_help.h" +#include "gbc_read.h" + +//#define DEBUG + +static bool is_init = FALSE; +static COMPILE *comp; +static const char *source_ptr; +static const char *_line_start; +static int source_length; +static bool _begin_line = FALSE; +static bool _no_quote = FALSE; + +static bool _prep = FALSE; +static int _prep_index; + +static PATTERN _last_pattern; + +static char ident_car[256]; +static char first_car[256]; +static char noop_car[256]; +static char canres_car[256]; + +enum +{ + GOTO_BREAK, + GOTO_SPACE, + GOTO_NEWLINE, + GOTO_COMMENT, + GOTO_STRING, + GOTO_IDENT, + GOTO_QUOTED_IDENT, + GOTO_ERROR, + GOTO_SHARP, + GOTO_NUMBER, + GOTO_NUMBER_OR_OPERATOR, + GOTO_OPERATOR +}; + + +static void READ_init(void) +{ + unsigned char i; + + JOB->line = 1; + JOB->max_line = FORM_FIRST_LINE - 1; + + if (!is_init) + { + for (i = 0; i < 255; i++) + { + ident_car[i] = (i != 0) && ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9') || strchr("$_?@", i)); + noop_car[i] = ident_car[i] || (i >= '0' && i <= '9') || i <= ' '; + canres_car[i] = (i != ':') && (i != '.') && (i != '!') && (i != '('); + + if (i == 0) + first_car[i] = GOTO_BREAK; + else if (i == '\n') + first_car[i] = GOTO_NEWLINE; + else if (i <= ' ') + first_car[i] = GOTO_SPACE; + else if (i == '\'') + first_car[i] = GOTO_COMMENT; + else if (i == '"') + first_car[i] = GOTO_STRING; + else if (i == '#') + first_car[i] = GOTO_SHARP; + else if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == '$' || i == '_') + first_car[i] = GOTO_IDENT; + else if (i == '{') + first_car[i] = GOTO_QUOTED_IDENT; + else if (i >= '0' && i <= '9') + first_car[i] = GOTO_NUMBER; + else if (i >= 127) + first_car[i] = GOTO_ERROR; + else if (i == '+' || i == '-' || i == '&') + first_car[i] = GOTO_NUMBER_OR_OPERATOR; + else + first_car[i] = GOTO_OPERATOR; + } + + is_init = TRUE; + } +} + + +static void READ_exit(void) +{ + char *name = NULL; + int len; + int index; + bool local; + bool has_static; + char c; + + local = FALSE; + + COMPILE_enum_class_start(name, len); + + while (len) + { + if (len == 1 && *name == '-') + { + local = TRUE; + } + else if (*name != '.') + { + has_static = FALSE; + + for(;;) + { + c = name[len - 1]; + if (c == '!') + { + has_static = TRUE; + len--; + } + else if (c == '?') + len--; + else + break; + } + + if (TABLE_find_symbol(JOB->class->table, name, len, &index)) + { + if (local) + index = CLASS_add_class_unused(JOB->class, index); + else + index = CLASS_add_class_exported_unused(JOB->class, index); + + JOB->class->class[index].has_static = has_static; + } + } + + COMPILE_enum_class_next(name, len); + } + + if (COMP_verbose) + printf("\n"); +} + +static int get_utf8_length(const char *str, int len) +{ + int ulen = 0; + int i; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + + +int READ_get_column() +{ + const char *p = source_ptr; + int col = 0; + + while (p > comp->source) + { + if (*p == '\n') + { + p++; + break; + } + p--; + col++; + } + + return get_utf8_length(p, col + 1); +} + + +char *READ_get_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + const char *str; + const char *before = _no_quote ? "" : "'"; + const char *after = _no_quote ? "" : "'"; + + switch(type) + { + case RT_RESERVED: + str = COMP_res_info[index].name; //TABLE_get_symbol_name(COMP_res_table, index); + if (ispunct(*str)) + snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", before, str, after); + else + strcpy(COMMON_buffer, str); + break; + + case RT_INTEGER: + snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%d%s", before, PATTERN_signed_index(*pattern), after); + break; + + case RT_NUMBER: + case RT_IDENTIFIER: + case RT_CLASS: + snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", before, TABLE_get_symbol_name(JOB->class->table, index), after); + break; + + case RT_STRING: + case RT_TSTRING: + if (_no_quote) + snprintf(COMMON_buffer, COMMON_BUF_MAX, "\"%s\"", TABLE_get_symbol_name(JOB->class->string, index)); + else + strcpy(COMMON_buffer, "string"); + break; + + /*case RT_COMMAND: + snprintf(COMMON_buffer, COMMON_BUF_MAX, "#%d", index); + break;*/ + + case RT_NEWLINE: + strcpy(COMMON_buffer, "end of line"); + break; + + case RT_END: + strcpy(COMMON_buffer, "end of file"); + break; + + case RT_SUBR: + //snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", bafore, COMP_subr_info[index].name, after); + strcpy(COMMON_buffer, COMP_subr_info[index].name); + break; + + default: + sprintf(COMMON_buffer, "%s?%02X.%02X.%d?%s", before, PATTERN_type(*pattern), PATTERN_flag(*pattern), (int)PATTERN_index(*pattern), after); + } + + return COMMON_buffer; +} + +void THROW_UNEXPECTED(PATTERN *pattern) +{ + switch (PATTERN_type(*pattern)) + { + case RT_NEWLINE: case RT_END: + THROW("Unexpected end of line"); + case RT_STRING: case RT_TSTRING: + THROW("Unexpected string"); + default: + THROW("Unexpected &1", READ_get_pattern(pattern)); + } +} + +void READ_dump_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + + /*pos = (int)(pattern - JOB->pattern); + if (pos < 0 || pos >= JOB->pattern_count) + return; + + printf("%d ", pos);*/ + + if (PATTERN_flag(*pattern) & RT_FIRST) + printf("!"); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_POINT) + printf("."); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_CLASS) + printf("C"); + else + printf(" "); + + printf(" "); + + _no_quote = TRUE; + + if (type == RT_RESERVED) + printf("RESERVED %s\n", READ_get_pattern(pattern)); + else if (type == RT_INTEGER) + printf("INTEGER %s\n", READ_get_pattern(pattern)); + else if (type == RT_NUMBER) + printf("NUMBER %s\n", READ_get_pattern(pattern)); + else if (type == RT_IDENTIFIER) + printf("IDENTIFIER %s\n", READ_get_pattern(pattern)); + else if (type == RT_CLASS) + printf("CLASS %s\n", READ_get_pattern(pattern)); + else if (type == RT_STRING) + printf("STRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_TSTRING) + printf("TSTRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_NEWLINE) + printf("NEWLINE (%d)\n", index); + else if (type == RT_END) + printf("END\n"); + else if (type == RT_PARAM) + printf("PARAM %d\n", index); + else if (type == RT_SUBR) + printf("SUBR %s\n", READ_get_pattern(pattern)); + /*else if (type == RT_COMMAND) + printf("COMMAND %d\n", index);*/ + else + printf("? %d\n", index); + + _no_quote = FALSE; +} + + +#if 0 +static inline unsigned char get_char_offset(int offset) +{ + offset += source_ptr; + + if (offset >= source_length || offset < 0) + return 0; + else + return (unsigned char)(comp->source[offset]); +} +#endif + +#if 0 +static unsigned char get_char(void) +{ + return get_char_offset(0); +} +#endif + +#define get_char_offset(_offset) ((unsigned char)source_ptr[(_offset)]) +#define get_char() ((unsigned char)(*source_ptr)) + + +static unsigned char next_char(void) +{ + source_ptr++; + return get_char(); +} + + +#ifdef DEBUG + +static void add_pattern_no_dump(int type, int index) +{ + comp->pattern[comp->pattern_count] = _last_pattern = PATTERN_make(type, index); + comp->pattern_pos[comp->pattern_count] = source_ptr - _line_start; + comp->pattern_count++; +} + +static void add_pattern(int type, int index) +{ + add_pattern_no_dump(type, index); + printf("% 4d ", comp->pattern_pos[comp->pattern_count - 1]); + READ_dump_pattern(&comp->pattern[comp->pattern_count - 1]); +} + +#else + +#define add_pattern_no_dump add_pattern + +//#define add_pattern(_type, _index) comp->pattern[comp->pattern_count++] = PATTERN_make((_type), (_index)); + +static inline void add_pattern(int type, int index) +{ + comp->pattern[comp->pattern_count] = _last_pattern = PATTERN_make(type, index); + comp->pattern_pos[comp->pattern_count] = source_ptr - _line_start; + comp->pattern_count++; +} + +#endif + +static PATTERN get_last_last_pattern() +{ + if (comp->pattern_count > 1) + return comp->pattern[comp->pattern_count - 2]; + else + return NULL_PATTERN; +} + +#define get_last_pattern() (_last_pattern) +//(comp->pattern[comp->pattern_count - 1]) + +static void jump_to_next_prep(void) +{ + unsigned char car; + const char *line_start; + + for (;;) + { + line_start = source_ptr; + + for(;;) + { + car = get_char(); + if (!car) + return; + if (car == '\n' || !car || !isspace(car)) + break; + source_ptr++; + } + + if (car == '#') + { + source_ptr = line_start; + return; + } + + for(;;) + { + car = get_char(); + if (!car) + return; + source_ptr++; + if (car == '\n') + break; + } + + add_pattern(RT_NEWLINE, comp->line); + comp->line++; + } +} + +INLINE static void add_newline() +{ + int action = PREP_CONTINUE; + + if (_prep) + { + int line = comp->line; + + add_pattern_no_dump(RT_NEWLINE, comp->line); + action = PREP_analyze(&comp->pattern[_prep_index]); + _prep = FALSE; + + comp->pattern_count = _prep_index; + comp->line = line; + } + + if (action == PREP_LINE) + comp->line = PREP_next_line; + + // Void lines must act as void help comments + if (comp->line > 0 && PATTERN_is_newline(get_last_pattern())) + HELP_add_at_current_line("\n"); + + add_pattern(RT_NEWLINE, comp->line); + comp->line++; + + if (action == PREP_IGNORE) + jump_to_next_prep(); +} + +static void add_end() +{ + add_pattern(RT_END, 0); + comp->line++; +} + + +#include "gbc_read_temp.h" + + +static void add_quoted_identifier(void) +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + PATTERN last_pattern; + + last_pattern = get_last_pattern(); + + type = RT_IDENTIFIER; + + start = source_ptr; + len = 0; + + for(;;) + { + car = next_char(); + if (!ident_car[car]) + break; + len++; + } + + if (car != '}') + THROW("Missing '}'"); + + if (len == 0) + THROW("Void identifier"); + + source_ptr++; + start++; + + if (PATTERN_is(last_pattern, RS_EVENT) || PATTERN_is(last_pattern, RS_RAISE)) + { + start--; + len++; + *((char *)start) = ':'; + } + + if (PATTERN_is(last_pattern, RS_EXCL)) + { + index = TABLE_add_symbol(comp->class->string, start, len); + type = RT_STRING; + } + else + index = TABLE_add_symbol(comp->class->table, start, len); + + add_pattern(type, index); +} + + +static void add_operator() +{ + unsigned char car; + const char *start; + const char *end; + int len; + int op = NO_SYMBOL; + int index; + + start = source_ptr; + end = start; + len = 1; + + for(;;) + { + source_ptr++; + + index = RESERVED_find_word(start, len); + if (index >= 0) + { + op = index; + end = source_ptr; + } + + car = get_char(); + //if (!isascii(car) || !ispunct(car)) + if (noop_car[car]) + break; + len++; + } + + source_ptr = end; + + if (op < 0) + THROW("Unknown operator"); + + add_pattern(RT_RESERVED, op); +} + + +static int xdigit_val(unsigned char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + else + return (-1); +} + + +static void add_string() +{ + unsigned char car; + const char *start; + int len; + int index; + ushort newline; + bool jump; + char *p; + const char *end; + int i; + + start = end = source_ptr; + len = 0; + newline = 0; + jump = FALSE; + p = (char *)source_ptr; + + for(;;) + { + source_ptr++; + car = get_char(); + + if (jump) + { + if (car == '\n') + newline++; + else if (car == '"') + jump = FALSE; + else if (!car || !isspace(car)) + break; + } + else + { + p++; + len++; + + if (car == '\n') + THROW("Non terminated string"); + + if (car == '\\') + { + source_ptr++; + car = get_char(); + + if (car == 'n') + *p = '\n'; + else if (car == 't') + *p = '\t'; + else if (car == 'r') + *p = '\r'; + else if (car == 'b') + *p = '\b'; + else if (car == 'v') + *p = '\v'; + else if (car == 'f') + *p = '\f'; + else if (car == 'e') + *p = '\x1B'; + else if (car == '0') + *p = 0; + else if (car == '\"' || car == '\'' || car == '\\') + *p = car; + else + { + if (car == 'x') + { + i = xdigit_val(get_char_offset(1)); + if (i >= 0) + { + car = i; + i = xdigit_val(get_char_offset(2)); + if (i >= 0) + { + car = (car << 4) | (uchar)i; + *p = car; + source_ptr += 2; + continue; + } + } + } + + THROW("Bad character constant in string"); + } + } + else if (car == '"') + { + p--; + len--; + end = source_ptr; + jump = TRUE; + comp->line += newline; + newline = 0; + } + else + *p = car; + } + } + + if (len > 0) + { + index = TABLE_add_symbol(comp->class->string, start + 1, len); + add_pattern(RT_STRING, index); + } + else + add_pattern(RT_STRING, VOID_STRING_INDEX); + + source_ptr = end + 1; + //for (i = 0; i < newline; i++) + // add_newline(); +} + + +#if 0 +static void add_command() +{ + unsigned char car; + const char *start; + int len; + + start = source_ptr; + len = 0; + + for(;;) + { + source_ptr++; + car = get_char(); + if (car == '\n' || !car) + break; + len++; + } + + if (len) + { + //TABLE_add_symbol(comp->class->string, start + 1, len, NULL, &index); + if (len == 7 && !strncasecmp(start + 1, "SECTION", 7)) + add_pattern(RT_COMMAND, RC_SECTION); + } + + add_newline(); +} +#endif + +void READ_do(void) +{ + static const void *jump_char[12] = + { + &&__BREAK, + &&__SPACE, + &&__NEWLINE, + &&__COMMENT, + &&__STRING, + &&__IDENT, + &&__QUOTED_IDENT, + &&__ERROR, + &&__SHARP, + &&__NUMBER, + &&__NUMBER_OR_OPERATOR, + &&__OPERATOR + }; + + unsigned char car; + + comp = JOB; + + READ_init(); + PREP_init(); + + //add_pattern(RT_NEWLINE, 0); + + source_ptr = comp->source; + source_length = BUFFER_length(comp->source); + _line_start = source_ptr; + _begin_line = TRUE; + _prep = FALSE; + _last_pattern = NULL_PATTERN; + + //while (source_ptr < source_length) + for(;;) + { + car = get_char(); + goto *jump_char[(int)first_car[car]]; + + __ERROR: + + THROW("Syntax error"); + + __SPACE: + + source_ptr++; + continue; + + __NEWLINE: + + source_ptr++; + add_newline(); + _begin_line = TRUE; + _line_start = source_ptr; + continue; + + __COMMENT: + + source_ptr++; + + if (HELP_is_help_comment(source_ptr)) + HELP_add_at_current_line(source_ptr); + + car = get_char(); + while (car != '\n') + { + source_ptr++; + car = get_char(); + } + + _begin_line = FALSE; + continue; + + __STRING: + + add_string(); + _begin_line = FALSE; + continue; + + __IDENT: + + add_identifier(); + _begin_line = FALSE; + continue; + + __QUOTED_IDENT: + + add_quoted_identifier(); + _begin_line = FALSE; + continue; + + __SHARP: + + if (_begin_line) + { + _prep = TRUE; + _prep_index = comp->pattern_count; + + add_identifier(); + _begin_line = FALSE; + continue; + } + else + goto __OPERATOR; + + __NUMBER_OR_OPERATOR: + + if (add_number()) + goto __OPERATOR; + + _begin_line = FALSE; + continue; + + __NUMBER: + + add_number(); + _begin_line = FALSE; + continue; + + __OPERATOR: + + add_operator(); + _begin_line = FALSE; + continue; + } + +__BREAK: + + // We add end markers to simplify the compiler job, when it needs to look + // at many patterns in one shot. + + add_newline(); + + JOB->max_line = JOB->line; + + add_end(); + add_end(); + add_end(); + add_end(); + + PREP_exit(); + READ_exit(); +} + diff --git a/main/gbc/gbc_read.h b/main/gbc/gbc_read.h new file mode 100644 index 00000000..1e5da70e --- /dev/null +++ b/main/gbc/gbc_read.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + gbc_read.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_READ_H +#define __GBC_READ_H + +#include "gbc_read_common.h" + +#include + +#undef isdigit +#define isdigit(_c) ((_c) >= '0' && (_c) <= '9') +#undef isspace +#define isspace(_c) (((uchar)(_c)) <= ' ') + +void READ_do(void); +void READ_dump_pattern(PATTERN *pattern); +char *READ_get_pattern(PATTERN *pattern); +int READ_get_column(); + +void THROW_UNEXPECTED(PATTERN *pattern) NORETURN; + +#endif diff --git a/main/gbc/gbc_reserved.c b/main/gbc/gbc_reserved.c new file mode 100644 index 00000000..c8d74e1a --- /dev/null +++ b/main/gbc/gbc_reserved.c @@ -0,0 +1,27 @@ +/*************************************************************************** + + gbc_reserved.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_RESERVED_C + +#define PROJECT_COMP +#include "gb_reserved_temp.h" diff --git a/main/gbc/gbc_reserved_make.c b/main/gbc/gbc_reserved_make.c new file mode 100644 index 00000000..01bb4b31 --- /dev/null +++ b/main/gbc/gbc_reserved_make.c @@ -0,0 +1,469 @@ +/*************************************************************************** + + gbc_reserved_make.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_RESERVED_MAKE_C + +#define PROJECT_COMP + +#include +#include + +#include "gb_common.h" +#include "gb_reserved.h" +#include "gb_type_common.h" +#include "gb_pcode.h" +#include "gb_reserved_keyword.h" + +#define HASH_SIZE 109 + +/*uint hash(const char *key, int len) +{ + int i; + uint h = 0; + for (i = 0; i < len; i++) + h = (h << 4) + (h ^ (tolower(key[i]))); + + return h % HASH_SIZE; +}*/ + +char buffer[1024]; + +char *read_line(FILE *f) +{ + return fgets(buffer, sizeof(buffer), f); +} + +void read_write_until(FILE *in, FILE *out, char *until) +{ + char *line; + + for(;;) + { + line = read_line(in); + if (!line) + break; + if (out) fputs(line, out); + while (*line && ((unsigned char)*line <= ' ')) + line++; + if (!strncmp(line, until, strlen(until))) + break; + } +} + +#if 0 +int main(int argc, char **argv) +{ + COMP_INFO *info; + SUBR_INFO *subr; + int len; + int i, p, n; + uint h; + char c; + char last[16]; + char next[16]; + FILE *in, *out; + + in = fopen("../share/gb_reserved_temp.h", "r"); + if (!in) + { + fprintf(stderr, "unable to open '../share/gb_reserved_temp.h': %s\n", strerror(errno)); + exit(1); + } + + out = fopen("../share/gb_reserved_temp.h.tmp", "w"); + + read_write_until(in, out, "int RESERVED_find_word(const char *word, int len)"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 0; h < 12; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02d, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + //printf(" goto *jump[h %% %d];\n\n", HASH_SIZE); + fprintf(out, "\tgoto *jump[len];\n\n"); + + fprintf(out, "__00:\n__01:\n\treturn -1;\n"); + + for (h = 2; h < 12; h++) + { + fprintf(out, "__%02d:\n", h); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (info = &COMP_res_info[1], i = 1; info->name; info++, i++) + { + len = strlen(info->name); + if (len != h) + continue; + + if (strcmp(info->name, last) <= 0) + continue; + + if (*next == 0 || strcmp(info->name, next) <= 0) + { + strcpy(next, info->name); + n = i; + } + } + + if (n < 0) + break; + + info = &COMP_res_info[n]; + len = strlen(info->name); + + strcpy(last, info->name); + + fprintf(out, "\tif ("); + for (p = 0; p < len; p++) + { + if (p) + fprintf(out, " && "); + c = info->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(info->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, info->name[p]); + } + /*printf(" if (!strncmp(\""); + for (p = 0; p < len; p++) + { + c = info->name[p]; + if (c == '\\') + printf("\\\\"); + else + putchar(tolower(info->name[p])); + } + printf("\", word, %d)", len);*/ + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + read_write_until(in, NULL, "}"); + read_write_until(in, NULL, "}"); + fprintf(out, "}\n"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 0; h < 12; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02d, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + fprintf(out, "\tgoto *jump[len];\n\n"); + + fprintf(out, "__00:\n__01:\n\treturn -1;\n"); + + for (h = 2; h < 12; h++) + { + fprintf(out, "__%02d:\n", h); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (subr = &COMP_subr_info[0], i = 0; subr->name; subr++, i++) + { + len = strlen(subr->name); + if (len != h) + continue; + + if (strcmp(subr->name, last) <= 0) + continue; + + if (*next == 0 || strcmp(subr->name, next) <= 0) + { + strcpy(next, subr->name); + n = i; + } + } + + if (n < 0) + break; + + subr = &COMP_subr_info[n]; + len = strlen(subr->name); + + strcpy(last, subr->name); + + fprintf(out, "\tif ("); //COMPARE_%d(\"%s\", word)) return %d;\n", len, len, info->name, i); + for (p = 0; p < len; p++) + { + if (p) + fprintf(out, " && "); + c = subr->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(subr->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, subr->name[p]); + } + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + fprintf(out, "}\n"); + + fclose(in); + fclose(out); + + unlink("../share/gb_reserved_temp.h"); + rename("../share/gb_reserved_temp.h.tmp", "../share/gb_reserved_temp.h"); + + return 0; +} +#endif + +static int compare_name(const char *name1, const char* name2) +{ + int l1 = strlen(name1); + int l2 = strlen(name2); + + if (l1 < l2) + return -1; + if (l1 > l2) + return 1; + return strcasecmp(name1, name2); +} + +int main(int argc, char **argv) +{ + COMP_INFO *info; + SUBR_INFO *subr; + int len; + int i, p, n; + uint h; + char c; + char last[16]; + char next[16]; + FILE *in, *out; + + in = fopen("../share/gb_reserved_temp.h", "r"); + if (!in) + { + fprintf(stderr, "unable to open '../share/gb_reserved_temp.h': %s\n", strerror(errno)); + exit(1); + } + + out = fopen("../share/gb_reserved_temp.h.tmp", "w"); + + read_write_until(in, out, "int RESERVED_find_word(const char *word, int len)"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 32; h < 127; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02X, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + //printf(" goto *jump[h %% %d];\n\n", HASH_SIZE); + fprintf(out, "\tgoto *jump[*word - 32];\n\n"); + + for (h = 32; h < 127; h++) + { + if (islower(h)) + continue; + + fprintf(out, "__%02X:\n", h); + if (isupper(h)) + fprintf(out, "__%02X:\n", tolower(h)); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (info = &COMP_res_info[1], i = 1; info->name; info++, i++) + { + if (tolower(*info->name) != tolower(h) || !info->name[1]) + continue; + + if (compare_name(info->name, last) <= 0) + continue; + + if (*next == 0 || compare_name(info->name, next) <= 0) + { + strcpy(next, info->name); + n = i; + } + } + + if (n < 0) + break; + + info = &COMP_res_info[n]; + len = strlen(info->name); + + strcpy(last, info->name); + + fprintf(out, "\tif (len == %d", len); + for (p = 1; p < len; p++) + { + fprintf(out, " && "); + c = info->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(info->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, info->name[p]); + } + /*printf(" if (!strncmp(\""); + for (p = 0; p < len; p++) + { + c = info->name[p]; + if (c == '\\') + printf("\\\\"); + else + putchar(tolower(info->name[p])); + } + printf("\", word, %d)", len);*/ + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + read_write_until(in, NULL, "}"); + read_write_until(in, NULL, "}"); + fprintf(out, "}\n"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 32; h < 127; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02X, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + fprintf(out, "\tgoto *jump[*word - 32];\n\n"); + + for (h = 32; h < 127; h++) + { + if (islower(h)) + continue; + + fprintf(out, "__%02X:\n", h); + if (isupper(h)) + fprintf(out, "__%02X:\n", tolower(h)); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (subr = &COMP_subr_info[0], i = 0; subr->name; subr++, i++) + { + if (tolower(*subr->name) != tolower(h) || !subr->name[1]) + continue; + + if (compare_name(subr->name, last) <= 0) + continue; + + if (*next == 0 || compare_name(subr->name, next) <= 0) + { + strcpy(next, subr->name); + n = i; + } + } + + if (n < 0) + break; + + subr = &COMP_subr_info[n]; + len = strlen(subr->name); + + strcpy(last, subr->name); + + fprintf(out, "\tif (len == %d", len); + for (p = 1; p < len; p++) + { + fprintf(out, " && "); + c = subr->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(subr->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, subr->name[p]); + } + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + fprintf(out, "}\n"); + + fclose(in); + fclose(out); + + unlink("../share/gb_reserved_temp.h"); + rename("../share/gb_reserved_temp.h.tmp", "../share/gb_reserved_temp.h"); + + return 0; +} diff --git a/main/gbc/gbc_trans.c b/main/gbc/gbc_trans.c new file mode 100644 index 00000000..51ba9dac --- /dev/null +++ b/main/gbc/gbc_trans.c @@ -0,0 +1,861 @@ +/*************************************************************************** + + gbc_trans.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TRANS_C + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" + +#include "gbc_compile.h" +#include "gbc_read.h" +#include "gbc_trans.h" +#include "gb_reserved.h" +#include "gb_code.h" + +#define IS_PURE_INTEGER(_int64_val) ((_int64_val) == ((int)(_int64_val))) + +short TRANS_in_assignment = 0; +short TRANS_in_left_value = 0; +short TRANS_in_try = RS_NONE; + +void TRANS_reset(void) +{ + JOB->line = JOB->first_line; + JOB->current = JOB->pattern; + JOB->end = &(JOB->pattern[JOB->pattern_count]); +} + + +static bool read_integer(char *number, int base, bool minus, int64_t *result) +{ + uint64_t nbr2, nbr; + int d, n; + unsigned char c; + int nmax; + + n = 0; + nbr = 0; + + switch (base) + { + case 2: nmax = 64; break; + case 8: nmax = 21; break; + case 16: nmax = 16; break; + case 10: default: nmax = 19; break; + } + + if (base == 10) + { + c = *number++; + + for(;;) + { + if (isdigit(c)) + d = c - '0'; + else + break; + + n++; + if (n < nmax) + nbr = nbr * 10 + d; + else + { + nbr2 = nbr * 10 + d; + + if ((nbr2 / 10) != nbr || nbr2 > ((uint64_t)LLONG_MAX + minus)) + return TRUE; + + nbr = nbr2; + } + + c = *number++; + if (!c) + break; + } + } + else + { + c = *number++; + + for(;;) + { + if (isdigit(c)) + d = c - '0'; + else if (c >= 'A' && c <='Z') + d = c - 'A' + 10; + else if (c >= 'a' && c <='z') + d = c - 'a' + 10; + else + break; + + if (d >= base) + break; + + n++; + if (n > nmax) + return TRUE; + + nbr = nbr * base + d; + + c = *number++; + if (!c) + break; + } + + if ((c == '&' || c == 'u' || c == 'U') && base != 10) + c = *number++; + else + { + if ((base == 16 && n == 4) || (base == 2 && n == 16)) + { + if (nbr >= 0x8000L && nbr <= 0xFFFFL) + nbr |= INT64_C(0xFFFFFFFFFFFF0000); + } + else if ((base == 16 && n == 8) || (base == 2 && n == 32)) + { + if (nbr >= 0x80000000L && nbr <= 0xFFFFFFFFL) + nbr |= INT64_C(0xFFFFFFFF00000000); + } + } + } + + if (c) + return TRUE; + + if (n == 0) + return TRUE; + + *((int64_t *)result) = nbr; + return FALSE; +} + + +static bool read_float(char *number, double *result) +{ + unsigned char c; + double nint; + double nfrac, n; + int nexp; + bool nexp_minus; + + nint = 0.0; + nfrac = 0.0; + nexp = 0; + nexp_minus = FALSE; + + c = *number++; + + /* Integer part */ + + for(;;) + { + if (c == '.') + { + c = *number++; + break; + } + + if (!c || !isdigit(c)) + return TRUE; + + nint = nint * 10 + (c - '0'); + + c = *number++; + + if (c == 'e' || c == 'E') + break; + + if (!c || isspace(c)) + goto __END; + } + + /* Decimal part */ + + n = 0.1; + for(;;) + { + if (!c || !isdigit(c)) + break; + + nfrac += n * (c - '0'); + n /= 10; + + c = *number++; + } + + /* Exponent */ + + if (c == 'e' || c == 'E') + { + c = *number++; + + if (c == '+' || c == '-') + { + if (c == '-') + nexp_minus = TRUE; + + c = *number++; + } + + if (!c || !isdigit(c)) + return TRUE; + + for(;;) + { + nexp = nexp * 10 + (c - '0'); + if (nexp > DBL_MAX_10_EXP) + return TRUE; + + c = *number++; + if (!c || !isdigit(c)) + break; + } + } + + if (c) + return TRUE; + +__END: + + *result = (nint + nfrac) * pow(10, nexp_minus ? (-nexp) : nexp); + + return FALSE; +} + +bool TRANS_get_number(int index, TRANS_NUMBER *result) +{ + char buffer[68]; + SYMBOL *sym; + char *number; + unsigned char c; + int64_t val = 0; + double dval = 0.0; + int type; + int base = 10; + bool minus = FALSE; + bool complex = FALSE; + + sym = TABLE_get_symbol(JOB->class->table, index); + if (sym->len > 66) + return TRUE; + memcpy(buffer, sym->name, sym->len); + buffer[sym->len] = 0; + number = buffer; + + c = *number++; + + if (c == '+' || c == '-') + { + minus = (c == '-'); + c = *number++; + } + + if (c == '&') + { + c = *number++; + + if (c == 'H' || c == 'h') + { + base = 16; + c = *number++; + } + else if (c == 'X' || c == 'x') + { + base = 2; + c = *number++; + } + else if (c == 'O' || c == 'o') + { + base = 8; + c = *number++; + } + else + base = 16; + } + else if (c == '%') + { + base = 2; + c = *number++; + } + + if (!c) + return TRUE; + + if (c == '-' || c == '+') + return TRUE; + + errno = 0; + number--; + + if (base == 10 && tolower(buffer[sym->len - 1]) == 'i') + { + buffer[sym->len - 1] = 0; + complex = TRUE; + } + + if (!read_integer(number, base, minus, &val)) + { + if (minus) val = (-val); + + if (IS_PURE_INTEGER(val)) + { + type = T_INTEGER; + goto __END; + } + else + { + type = T_LONG; + goto __END; + } + } + + if (base == 10) + { + if (!read_float(number, &dval)) + { + if (minus) dval = (-dval); + type = T_FLOAT; + goto __END; + } + } + + return TRUE; + +__END: + + result->type = type; + result->complex = complex; + + if (type == T_INTEGER) + result->lval = result->ival = val; + else if (type == T_LONG) + result->lval = val; + else + result->dval = dval; + + return FALSE; +} + + +static PATTERN *trans_embedded_array(PATTERN *look, int mode, TRANS_DECL *result) +{ + TRANS_CONST_VALUE *const_value; + int i; + int size; + + if (!(mode & TT_CAN_EMBED)) + { + if (PATTERN_is(*look, RS_LSQR)) + THROW("Embedded arrays are forbidden here"); + return look; + } + + if (!PATTERN_is(*look, RS_LSQR)) + return look; + + look++; + + if (mode & TT_CAN_ARRAY) + { + for (i = 0;; i++) + { + if (i >= MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + JOB->current = look; + const_value = TRANS_const(); + look = JOB->current; + + if (const_value->type != T_INTEGER) + THROW("Bad subscript range"); + + size = const_value->value._integer; + + if (size < 1 || size > (2 << 22)) /* 4 Mo, ca devrait suffire... ;-) */ + THROW("Bad subscript range"); + + result->array.dim[i] = size; + result->array.ndim++; + + if (PATTERN_is(*look, RS_RSQR)) + break; + + if (!PATTERN_is(*look, RS_COMMA)) + THROW(E_MISSING, "','"); + look++; + } + } + + if (!PATTERN_is(*look, RS_RSQR)) + THROW(E_MISSING, "']'"); + + result->is_embedded = TRUE; + + look++; + return look; +} + + +int TRANS_get_class(int index) +{ + int index_array; + //CLASS_REF *cref; + + if (!CLASS_exist_class(JOB->class, index)) + { + CLASS_SYMBOL *sym = CLASS_get_symbol(JOB->class, index); + int i; + char c; + + //fprintf(stderr, "TRANS_get_class: %.*s\n", sym->symbol.len, sym->symbol.name); + + for (i = sym->symbol.len - 1; i >= 0; i--) + { + c = sym->symbol.name[i]; + if (c == '[') + { + //fprintf(stderr, "TRANS_get_class: find %.*s\n", i, sym->symbol.name); + if (TABLE_find_symbol(JOB->class->table, sym->symbol.name, i, &index_array)) + { + index_array = TRANS_get_class(index_array); + + if (JOB->class->class[index_array].exported) + index = CLASS_add_class_exported(JOB->class, index); + else + index = CLASS_add_class(JOB->class, index); + + JOB->class->class[index].type = TYPE_make(T_OBJECT, index_array, 0); + + /*cref = &JOB->class->class[index]; + if (TYPE_is_null(cref->array)) + { + cref->array.t.id = T_OBJECT; + cref->array.t.value = index_array; + }*/ + + return index; + } + } + } + + THROW("Unknown identifier: &1", TABLE_get_symbol_name(JOB->class->table, index)); + } + + return CLASS_add_class(JOB->class, index); +} + +static bool check_structure(int *cindex) +{ + SYMBOL *sym = TABLE_get_symbol(JOB->class->table, JOB->class->class[*cindex].index); + int len = sym->len; + char name[sym->len + 1]; + int index; + bool is_array; + + strncpy(name, sym->name, len); + while (name[len - 1] == ']') + len -= 2; + name[len] = 0; + + if (len < sym->len) + { + if (!TABLE_find_symbol(JOB->class->table, name, len, &index)) + goto __ERROR; + + index = CLASS_add_class(JOB->class, index); + is_array = TRUE; + } + else + { + index = *cindex; + is_array = FALSE; + } + + if (JOB->class->class[index].structure) + { + *cindex = index; + return is_array; + } + +__ERROR: + + THROW("&1 is not a structure", name); +} + + +bool TRANS_type(int mode, TRANS_DECL *result) +{ + PATTERN *look = JOB->current; + short id; + int value; + int flag = 0; + bool is_array; + + /* Do not fill the structure with zeros */ + + TYPE_clear(&result->type); + result->is_new = FALSE; + result->is_embedded = FALSE; + result->init = NULL; + result->array.ndim = 0; + + look = trans_embedded_array(look, mode, result); + + if (!PATTERN_is(*look, RS_AS)) + { + if (mode & TT_DO_NOT_CHECK_AS) + return FALSE; + else + THROW(E_MISSING, "AS"); + } + + look++; + + if (mode & TT_CAN_NEW) + { + if (PATTERN_is(*look, RS_NEW)) + { + if (result->is_embedded) //TYPE_get_id(result->type) == T_ARRAY) + THROW("Cannot mix NEW and embedded array"); + + result->is_new = TRUE; + look++; + result->init = look; + } + } + + if ((mode & TT_CAN_EMBED) && PATTERN_is(*look, RS_STRUCT)) + { + id = T_STRUCT; + look++; + + if (!PATTERN_is_class(*look)) + THROW_UNEXPECTED(look); + + value = TRANS_get_class(PATTERN_index(*look)); + is_array = check_structure(&value); + + if (!is_array) + { + if (result->is_new) + THROW("Cannot mix NEW and embedded structure"); + //if (result->array.ndim > 0) + // THROW("Cannot mix embedded array and embedded structure"); + } + else + THROW("Arrays of structure are not supported"); + + look++; + } + else + { + if (!PATTERN_is_type(*look) && !PATTERN_is_class(*look)) + THROW_UNEXPECTED(look); + + if (PATTERN_is_type(*look)) + { + id = RES_get_type(PATTERN_index(*look)); + value = -1; + } + else + { + id = T_OBJECT; + value = TRANS_get_class(PATTERN_index(*look)); + } + + if (PATTERN_is(look[1], RS_LSQR)) + { + value = CLASS_get_array_class(JOB->class, id, value); + id = T_OBJECT; + + if (!PATTERN_is(look[2], RS_RSQR)) + { + if ((mode & TT_CAN_NEW) && result->is_new) + { + //if (TYPE_get_id(result->type) == T_ARRAY) + // THROW("Cannot mix NEW and static array declaration"); + + //result->is_new = TRUE; + result->init = look; + } + else + THROW("Syntax error"); + } + + while (!PATTERN_is_newline(*look)) + look++; + } + else + { + //if (id == T_OBJECT) + // value = (-1); + look++; + } + } + + if (id == T_VOID) + return FALSE; + + /* + if (result->is_array && result->array.ndim == 0) + result->is_array = FALSE; + */ + + if (result->array.ndim > 0) + { + result->array.type = TYPE_make(id, value, flag); + result->type = TYPE_make(T_ARRAY, CLASS_add_array(JOB->class, &result->array), 0); + } + else if (id == T_STRUCT) + { + result->type = TYPE_make(id, value, flag); + } + else + { + result->type = TYPE_make(id, value, flag); + + if ((mode & TT_CAN_NEW) && !result->is_new && PATTERN_is(*look, RS_EQUAL)) + { + look++; + result->init = look; + while (!PATTERN_is_newline(*look)) + look++; + } + } + + JOB->current = look; + return TRUE; +} + +bool TRANS_check_declaration(void) +{ + PATTERN *look = JOB->current; + + if (!PATTERN_is_identifier(*look)) + return FALSE; + look++; + + if (PATTERN_is(*look, RS_LSQR)) + { + for(;;) + { + look++; + if (PATTERN_is(*look, RS_RSQR)) + break; + if (PATTERN_is_newline(*look)) + return FALSE; + } + look++; + } + + if (!PATTERN_is(*look, RS_AS)) + return FALSE; + + return TRUE; +} + + +void TRANS_get_constant_value(TRANS_DECL *decl) +{ + int index; + TRANS_NUMBER number = {0}; + int type; + PATTERN value; + TRANS_CONST_VALUE *const_value; + + type = TYPE_get_id(decl->type); + + if (type == T_STRING) + { + value = *JOB->current++; + index = PATTERN_index(value); + + if (PATTERN_is(value, RS_LBRA)) + { + value = *JOB->current++; + if (!PATTERN_is_string(value)) + THROW("Constant string expected"); + index = PATTERN_index(value); + value = *JOB->current++; + if (!PATTERN_is(value, RS_RBRA)) + THROW("Missing right brace"); + if (index != VOID_STRING_INDEX) + TYPE_set_id(&decl->type, T_CSTRING); + } + else + { + if (!PATTERN_is_string(value)) + THROW("Constant string expected"); + } + + decl->is_integer = FALSE; + decl->value = index; + } + else + { + switch(type) + { + case T_BOOLEAN: + + const_value = TRANS_const(); + + if (const_value->type == T_INTEGER) + decl->value = const_value->value._integer ? -1 : 0; + else if (const_value->type == T_LONG) + decl->value = const_value->value._long ? -1 : 0; + else + THROW("Type mismatch"); + + decl->is_integer = TRUE; + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: + + const_value = TRANS_const(); + + if (const_value->type == T_INTEGER) + { + if (((type == T_BYTE) && (const_value->value._integer < 0 || const_value->value._integer > 255)) + || ((type == T_SHORT) && (const_value->value._integer < -32768L || const_value->value._integer > 32767L))) + THROW("Out of range"); + + decl->value = const_value->value._integer; + } + else if (const_value->type == T_LONG) + THROW("Out of range"); + else + THROW("Type mismatch"); + + decl->is_integer = TRUE; + break; + + case T_LONG: + + const_value = TRANS_const(); + + decl->is_integer = FALSE; + if (const_value->type == T_INTEGER) + decl->lvalue = const_value->value._integer; + else if (const_value->type == T_LONG) + decl->lvalue = const_value->value._long; + else + THROW("Type mismatch"); + + break; + + case T_FLOAT: case T_SINGLE: + + value = *JOB->current++; + index = PATTERN_index(value); + + if (PATTERN_is_integer(value)) + { + decl->is_integer = TRUE; + index = PATTERN_signed_index(value); + } + else + { + if (TRANS_get_number(index, &number)) + THROW("Type mismatch"); + + if (type == T_SINGLE && !finite((float)number.dval)) + THROW("Out of range"); + + decl->is_integer = FALSE; + } + + decl->value = index; + break; + + default: + + THROW("Bad constant type"); + } + } +} + + +void TRANS_want(int reserved, char *msg) +{ + if (!PATTERN_is(*JOB->current, reserved)) + THROW("Syntax error. &1 expected", msg ? msg : COMP_res_info[reserved].name); + JOB->current++; +} + +void TRANS_want_newline() +{ + if (!TRANS_newline()) + THROW_UNEXPECTED(JOB->current); +} + +void TRANS_want_class() +{ + if (!PATTERN_is_class(*JOB->current)) + THROW("Syntax error. Class name expected"); +} + +bool TRANS_is_end_function(bool is_proc, PATTERN *look) +{ + if (PATTERN_is_newline(*look)) + return TRUE; + + if (is_proc) + return PATTERN_is(*look, RS_PROCEDURE) || PATTERN_is(*look, RS_SUB); + else + return PATTERN_is(*look, RS_FUNCTION); +} + + +char *TRANS_get_num_desc(ushort num) +{ + static const char *num_desc[3] = { "first", "second", "third" }; + static char desc[8]; + + if (num < 1) + return NULL; + + if (ERROR_translate) + { + snprintf(desc, sizeof(desc), "#%d", num); + } + else + { + if (num < 4) + return (char *)num_desc[num - 1]; + + snprintf(desc, sizeof(desc), "%dth", num); + } + + return desc; +} + + diff --git a/main/gbc/gbc_trans.h b/main/gbc/gbc_trans.h new file mode 100644 index 00000000..eeb1ae39 --- /dev/null +++ b/main/gbc/gbc_trans.h @@ -0,0 +1,285 @@ +/*************************************************************************** + + gbc_trans.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_TRANS_H +#define __GBC_TRANS_H + +#include "gbc_type.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gb_limit.h" + +#include "gbc_trans_common.h" + +enum { + TT_NOTHING = 0, + TT_DO_NOT_CHECK_AS = 1, + TT_CAN_EMBED = 2, + TT_CAN_ARRAY = 4, + TT_CAN_NEW = 8 + }; + +enum { + TS_MODE_READ = (1 << 0), + TS_MODE_WRITE = (1 << 1), + TS_MODE_APPEND = (1 << 2), + TS_MODE_CREATE = (1 << 3), + TS_MODE_DIRECT = (1 << 4), + TS_MODE_WATCH = (1 << 6), + TS_MODE_PIPE = (1 << 7), + TS_MODE_MEMORY = (1 << 8), + TS_MODE_STRING = (1 << 9), + TS_MODE_NULL = (1 << 10), + }; + +enum { + TS_EXEC_NONE = 0, + TS_EXEC_READ = (1 << 0), + TS_EXEC_WRITE = (1 << 1), + TS_EXEC_TERM = (1 << 2), + TS_EXEC_STRING = (1 << 3), + TS_EXEC_WAIT = (1 << 4), + TS_EXEC_ERROR = (1 << 5) + }; + +enum { + TS_SUBR_PRINT, + TS_SUBR_INPUT, + TS_SUBR_WRITE, + TS_SUBR_WRITE_BYTES, + TS_SUBR_READ, + TS_SUBR_READ_BYTES, + TS_SUBR_OPEN, + TS_SUBR_CLOSE, + TS_SUBR_SEEK, + TS_SUBR_LINE_INPUT, + TS_SUBR_FLUSH, + TS_SUBR_EXEC, + TS_SUBR_SHELL, + TS_SUBR_WAIT, + TS_SUBR_KILL, + TS_SUBR_MOVE, + TS_SUBR_MKDIR, + TS_SUBR_RMDIR, + TS_SUBR_ARRAY, + TS_SUBR_COLLECTION, + TS_SUBR_COPY, + TS_SUBR_LINK, + TS_SUBR_ERROR, + TS_SUBR_LOCK, + TS_SUBR_UNLOCK, + TS_SUBR_LOCK_WAIT, + TS_SUBR_INPUT_FROM, + TS_SUBR_OUTPUT_TO, + TS_SUBR_DEBUG, + TS_SUBR_SLEEP, + TS_SUBR_RANDOMIZE, + TS_SUBR_ERROR_TO, + TS_SUBR_LEFT, + TS_SUBR_MID, + TS_SUBR_OPEN_MEMORY, + TS_SUBR_CHMOD, + TS_SUBR_CHOWN, + TS_SUBR_CHGRP, + TS_SUBR_USE, + TS_SUBR_CHECK_EXEC, + TS_SUBR_MOVE_KILL, + TS_SUBR_WAIT_DELAY, + TS_SUBR_WAIT_NEXT, + TS_SUBR_PEEK + }; + +enum { + TSO_SUBR_SCAN + }; + +enum { + TS_NONE = -1, + TS_STDIN = 0, + TS_STDOUT = 1, + TS_STDERR = 2 + }; + +#define TS_NO_SUBR ((void (*)())-1) + +typedef + struct { + TYPE_ID type; + union { + int _integer; + int64_t _long; + } value; + } + TRANS_CONST_VALUE; + +#ifndef __GBC_TRANS_C +EXTERN short TRANS_in_assignment; +EXTERN short TRANS_in_left_value; +EXTERN bool TRANS_in_try; +//EXTERN ushort *TRANS_labels; +#endif + +#define TRANS_newline() (PATTERN_is_newline(*JOB->current) ? JOB->line = PATTERN_index(*JOB->current) + 1, JOB->current++, TRUE : FALSE) + +void TRANS_reset(void); +/*PUBLIC bool TRANS_type(bool check_as, bool square, bool array, bool new, TRANS_DECL *result);*/ +bool TRANS_type(int flag, TRANS_DECL *result); +bool TRANS_get_number(int index, TRANS_NUMBER *result); +bool TRANS_check_declaration(void); +void TRANS_get_constant_value(TRANS_DECL *decl); +int TRANS_get_class(int index); + +void TRANS_want(int reserved, char *msg); +void TRANS_want_newline(void); +void TRANS_want_class(void); +//int TRANS_get_class(PATTERN pattern); +bool TRANS_is_end_function(bool is_proc, PATTERN *look); +char *TRANS_get_num_desc(ushort num); + +#define TRANS_is(_reserved) (PATTERN_is(*JOB->current, (_reserved)) ? JOB->current++, TRUE : FALSE) +#define TRANS_ignore(_reserved) (void)TRANS_is(_reserved) + +// gbc_trans_code.c + +void TRANS_code(void); +#define TRANS_has_init_var(_decl) ((_decl)->is_new || (_decl)->init) +bool TRANS_init_var(TRANS_DECL *decl); +void TRANS_statement(void); +void TRANS_init_optional(TRANS_PARAM *param); +//#define TRANS_add_label(_pos) (TRANS_labels ? *ARRAY_add(&TRANS_labels) = (_pos) : 0) +int TRANS_loop_local(bool allow_arg); + +// gbc_trans_expr.c + +void TRANS_expression(bool check); +void TRANS_ignore_expression(void); +bool TRANS_popify_last(bool no_conv); +void TRANS_reference(void); +bool TRANS_affectation(bool dup); +void TRANS_operation(short op, short nparam, bool output, PATTERN previous); +void TRANS_new(void); +TYPE TRANS_variable_get_type(void); +void TRANS_class(int index); +bool TRANS_string(PATTERN pattern); +TYPE TRANS_get_last_type(void); + +// gbc_trans_const.c + +TRANS_CONST_VALUE *TRANS_const(void); + +// gbc_trans_tree.c + +#define RS_UNARY (-1) + +#ifndef __GBC_TRANS_TREE +extern int TRANS_tree_index; +#endif + +//TRANS_TREE *TRANS_tree(bool check_statement); +void TRANS_tree(bool check_statement, TRANS_TREE **result, int *count); +void TRANS_tree_set_index(int index); +int TRANS_get_column(int *line); + +// gbc_trans_ctrl.c + +void TRANS_control_init(void); +void TRANS_control_exit(void); + +void TRANS_if(void); +void TRANS_else(void); +void TRANS_endif(void); +void TRANS_goto(void); +void TRANS_gosub(void); +void TRANS_on_goto_gosub(void); +void TRANS_do(int type); +void TRANS_loop(int type); +void TRANS_select(void); +void TRANS_case(void); +void TRANS_default(void); +void TRANS_end_select(void); +void TRANS_break(void); +void TRANS_continue(void); +void TRANS_return(void); +void TRANS_for(void); +void TRANS_for_each(void); +void TRANS_next(void); +void TRANS_assert(void); +void TRANS_try(void); +void TRANS_finally(void); +void TRANS_catch(void); +void TRANS_label(void); +void TRANS_with(void); +void TRANS_use_with(void); +void TRANS_end_with(void); +void TRANS_raise(void); +void TRANS_stop(void); + +// gbc_trans_subr.c + +void TRANS_subr(int subr, int nparam); + +void TRANS_print(void); +void TRANS_input(void); +void TRANS_read(void); +void TRANS_read_old(void); +void TRANS_peek(void); +void TRANS_write(void); +void TRANS_open(void); +void TRANS_pipe(void); +void TRANS_memory(void); +void TRANS_close(void); +void TRANS_lock(void); +void TRANS_unlock(void); +void TRANS_seek(void); +void TRANS_line_input(void); +void TRANS_flush(void); +void TRANS_quit(void); +void TRANS_exec(void); +void TRANS_shell(void); +void TRANS_wait(void); +void TRANS_sleep(void); +void TRANS_kill(void); +void TRANS_move(void); +void TRANS_chmod(void); +void TRANS_chown(void); +void TRANS_chgrp(void); +void TRANS_inc(void); +void TRANS_dec(void); +void TRANS_swap(void); +void TRANS_mkdir(void); +void TRANS_rmdir(void); +void TRANS_use(void); +void TRANS_copy(void); +void TRANS_link(void); +void TRANS_input_from(void); +void TRANS_output_to(void); +void TRANS_debug(void); +void TRANS_error(void); +void TRANS_scan(void); +void TRANS_randomize(void); +void TRANS_mid(void); +void TRANS_use(void); +void TRANS_poke(void); + +#endif + diff --git a/main/gbc/gbc_trans_code.c b/main/gbc/gbc_trans_code.c new file mode 100644 index 00000000..20db6a88 --- /dev/null +++ b/main/gbc/gbc_trans_code.c @@ -0,0 +1,781 @@ +/*************************************************************************** + + gbc_trans_code.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_CODE_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gb_code.h" +#include "gb_limit.h" + +//#define DEBUG + +//ushort *TRANS_labels = NULL; + +static FUNCTION *_func; + +static CLASS_SYMBOL *add_local(int sym_index, TYPE type, int value, bool used) +{ + CLASS_SYMBOL *sym; + PARAM *loc; + bool warnings = JOB->warnings; + + if (used) + JOB->warnings = FALSE; + sym = CLASS_declare(JOB->class, sym_index, TK_VARIABLE, FALSE); + if (used) + JOB->warnings = warnings; + + if (TYPE_is_static(type)) + { + CLASS_add_static_declaration(JOB->class, sym_index, type, sym, TRUE); + loc = ARRAY_add(&_func->stat); + } + else + { + if (value > MAX_LOCAL_SYMBOL) + THROW("Too many local variables"); + + sym->local.value = value; + sym->local_used = used; + loc = ARRAY_add(&_func->local); + } + + loc->index = sym_index; + loc->type = type; + loc->value = sym->local.value; + sym->local.type = type; + + CLASS_check_variable_prefix(sym, TRUE); + return sym; +} + + +static void create_local_from_param() +{ + int i; + + JOB->line--; // For line number of declaration + + for (i = 0; i < _func->nparam; i++) + { + if (TYPE_get_id(_func->param[i].type) != T_VOID) + add_local(_func->param[i].index, _func->param[i].type, (i - _func->nparam), _func->param[i].ignore); + } + + JOB->line++; +} + +static void check_local(CLASS_SYMBOL *sym) +{ + if (!sym->local_used) + { + if (sym->local.value < 0) + COMPILE_print(MSG_WARNING, sym->local.line, "unused argument: &1", SYMBOL_get_name(&sym->symbol)); + else + COMPILE_print(MSG_WARNING, sym->local.line, "unused variable: &1", SYMBOL_get_name(&sym->symbol)); + } + else if (!sym->local_assigned) + { + if (sym->local.value >= 0) + COMPILE_print(MSG_WARNING, sym->local.line, "uninitialized variable: &1", SYMBOL_get_name(&sym->symbol)); + } + + TYPE_clear(&sym->local.type); +} + +static void remove_local() +{ + int i; + + for (i = 0; i < ARRAY_count(_func->local); i++) + check_local(CLASS_get_symbol(JOB->class, _func->local[i].index)); + + for (i = 0; i < ARRAY_count(_func->stat); i++) + check_local(CLASS_get_symbol(JOB->class, _func->stat[i].index)); +} + + +static bool TRANS_local(void) +{ + int sym_index; + TRANS_DECL decl; + PATTERN *pattern; + CLASS_SYMBOL *sym; + int f; + bool no_warning; + bool save_warnings; + bool is_static; + + if (TRANS_is(RS_DIM)) + is_static = FALSE; + else if (TRANS_is(RS_STATIC)) + is_static = TRUE; + else + return FALSE; + + for(;;) + { + //many = FALSE; + pattern = JOB->current; + + for(;;) + { + no_warning = TRANS_is(RS_LBRA); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + JOB->current++; + + if (no_warning) + { + if (!PATTERN_is(*JOB->current, RS_RBRA)) + THROW("Missing right brace"); + JOB->current++; + } + + if (!TRANS_is(RS_COMMA)) + break; + //many = TRUE; + } + + f = TT_DO_NOT_CHECK_AS | TT_CAN_ARRAY | TT_CAN_NEW; + if (is_static) + f |= TT_CAN_EMBED; + + if (!TRANS_type(f, &decl)) + THROW(E_SYNTAX); + + if (is_static) + TYPE_set_flag(&decl.type, TF_STATIC); + + for(;;) + { + if (PATTERN_is(*pattern, RS_LBRA)) + { + pattern++; + no_warning = TRUE; + save_warnings = JOB->warnings; + JOB->warnings = FALSE; + } + else + no_warning = FALSE; + + sym_index = PATTERN_index(*pattern); + sym = add_local(sym_index, decl.type, _func->nlocal, FALSE); + pattern++; + + if (no_warning) + { + pattern++; + JOB->warnings = save_warnings; + } + + if (is_static) + { + CLASS_init_global_declaration(JOB->class, &decl, sym, TRUE); + + if (COMP_verbose) + printf("STATIC %s AS %s\n", TABLE_get_symbol_name(JOB->class->table, sym_index), TYPE_get_desc(decl.type)); + } + else + { + _func->nlocal++; + + if (TRANS_init_var(&decl)) + { + CODE_push_local_ref(sym->local.value, TYPE_must_ref(sym->local.type)); + TRANS_popify_last(TYPE_get_id(TRANS_get_last_type()) == TYPE_get_id(sym->local.type)); + sym->local_assigned = TRUE; + } + + if (COMP_verbose) + printf("DIM %s AS %s\n", TABLE_get_symbol_name(JOB->class->table, sym_index), TYPE_get_desc(decl.type)); + } + + if (!PATTERN_is(*pattern, RS_COMMA)) + break; + pattern++; + } + + /*if (!many) + { + if (TRANS_init_var(&decl)) + CODE_pop_local(_func->nlocal - 1); + }*/ + + if (!TRANS_is(RS_COMMA)) + break; + } + + return TRUE; +} + + +int TRANS_loop_local(bool allow_arg) +{ + int sym_index; + TRANS_DECL decl; + CLASS_SYMBOL *sym; + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected"); + + sym_index = PATTERN_index(*JOB->current); + JOB->current++; + + sym = CLASS_get_symbol(JOB->class, sym_index); + + if (TRANS_type(TT_DO_NOT_CHECK_AS | TT_CAN_ARRAY, &decl)) + { + if (!TYPE_compare(&sym->local.type, &decl.type)) + { + add_local(sym_index, decl.type, _func->nlocal, FALSE); + _func->nlocal++; + } + } + + if (TYPE_is_null(sym->local.type) || TYPE_is_static(sym->local.type)) + { + if (TYPE_is_null(sym->global.type)) + THROW("Unknown identifier: &1", TABLE_get_symbol_name(JOB->class->table, sym_index)); + else + THROW("Loop variable cannot be global"); + } + + if (!allow_arg && sym->local.value < 0) + THROW("Loop variable cannot be an argument"); + + sym->local_assigned = TRUE; + sym->local_used = TRUE; + + return sym->local.value; +} + + + +void TRANS_stop(void) +{ + if (TRANS_is(RS_EVENT)) + CODE_stop_event(); + else + CODE_stop(); +} + + +void TRANS_statement(void) +{ + static TRANS_STATEMENT statement[] = { + { RS_EXIT, TRANS_break }, + { RS_BREAK, TRANS_break }, + { RS_CONTINUE, TRANS_continue }, + { RS_GOTO, TRANS_goto }, + { RS_RETURN, TRANS_return }, + { RS_PRINT, TRANS_print }, + { RS_INPUT, TRANS_input }, + { RS_WRITE, TRANS_write }, + { RS_READ, TRANS_read_old }, + //{ RS_OPEN, TRANS_open }, + { RS_CLOSE, TRANS_close }, + { RS_SEEK, TRANS_seek }, + { RS_FLUSH, TRANS_flush }, + { RS_STOP, TRANS_stop }, + { RS_QUIT, TRANS_quit }, + { RS_EXEC, TRANS_exec }, + { RS_SHELL, TRANS_shell }, + { RS_WAIT, TRANS_wait }, + { RS_SLEEP, TRANS_sleep }, + { RS_KILL, TRANS_kill }, + { RS_MOVE, TRANS_move }, + { RS_INC, TRANS_inc }, + { RS_DEC, TRANS_dec }, + { RS_SWAP, TRANS_swap }, + { RS_MKDIR, TRANS_mkdir }, + { RS_RMDIR, TRANS_rmdir }, + { RS_COPY, TRANS_copy }, + { RS_RAISE, TRANS_raise }, + { RS_LINK, TRANS_link }, + { RS_LOCK, TRANS_lock }, + { RS_UNLOCK, TRANS_unlock }, + { RS_TRY, TRANS_try }, + { RS_LINE, TRANS_line_input }, + { RS_OUTPUT, TRANS_output_to }, + { RS_DEBUG, TRANS_debug }, + { RS_ASSERT, TRANS_assert }, + { RS_ERROR, TRANS_error }, + { RS_RANDOMIZE, TRANS_randomize }, + { RS_CHMOD, TRANS_chmod }, + { RS_CHOWN, TRANS_chown }, + { RS_CHGRP, TRANS_chgrp }, + { RS_GOSUB, TRANS_gosub }, + { RS_ON, TRANS_on_goto_gosub }, + + { RS_NONE, NULL } + }; + + PATTERN *look = JOB->current; + TRANS_STATEMENT *st; + COMP_INFO *info; + int index; + + if (PATTERN_is_reserved(look[0])) + { + info = &COMP_res_info[PATTERN_index(*look)]; + + if (!info->func) + { + for (st = statement; st->id; st++) + { + if (PATTERN_is(look[0], st->id)) + { + info->func = st->func; + break; + } + } + if (!info->func) + info->func = TS_NO_SUBR; + } + + if (info->func && info->func != TS_NO_SUBR) + { + JOB->current++; + (*info->func)(); + return; + } + } + else if (PATTERN_is_subr(look[0])) + { + index = PATTERN_index(look[0]); + + if (index == SUBR_Mid || index == SUBR_MidS) + { + JOB->current++; + TRANS_mid(); + return; + } + else if (COMP_version >= 0x03180000 && COMP_subr_info[index].opcode == (CODE_POKE - CODE_FIRST_SUBR)) + { + TRANS_poke(); + return; + } + } + + if (!TRANS_affectation(FALSE)) + TRANS_expression(TRUE); +} + + +static void translate_body() +{ + PATTERN *look; + bool is_proc = (TYPE_get_id(_func->type) == T_VOID); + bool test_newline; + //int line = JOB->line - 1; + bool just_got_select = FALSE; + + for(;;) + { + test_newline = TRUE; + CODE_allow_break(); + + FUNCTION_add_all_pos_line(); + + look = JOB->current; + + if (PATTERN_is(look[0], RS_END)) + if (TRANS_is_end_function(is_proc, &look[1])) + break; + + if (TRANS_newline()) + test_newline = FALSE; + else if (!TRANS_local()) + break; + + if (test_newline) + if (!PATTERN_is_newline(*JOB->current)) + THROW_UNEXPECTED(JOB->current); + } + + + TRANS_control_init(); + + for(;;) + { + test_newline = TRUE; + CODE_allow_break(); + + FUNCTION_add_all_pos_line(); + + look = JOB->current; + + if (PATTERN_is(look[0], RS_END)) + if (TRANS_is_end_function(is_proc, &look[1])) + break; + + if (TRANS_newline()) + continue; + + if (just_got_select) + { + if (!PATTERN_is(look[0], RS_CASE) && !PATTERN_is(look[0], RS_DEFAULT)) + THROW("Syntax error. CASE or DEFAULT expected after SELECT"); + just_got_select = FALSE; + } + + if (PATTERN_is(look[0], RS_DIM) || PATTERN_is(look[0], RS_STATIC)) + { + TRANS_local(); + } + else if (PATTERN_is_identifier(look[0]) && PATTERN_is(look[1], RS_COLON)) + { + TRANS_label(); + } + else if (PATTERN_is(look[0], RS_IF)) + { + JOB->current++; + TRANS_if(); + } + else if (PATTERN_is(look[0], RS_ELSE)) + { + JOB->current++; + TRANS_else(); + } + else if ((PATTERN_is(look[0], RS_END) + && PATTERN_is(look[1], RS_IF)) + || PATTERN_is(look[0], RS_ENDIF)) + { + if (PATTERN_is(look[0], RS_END)) + JOB->current += 2; + else + JOB->current++; + + TRANS_endif(); + } + else if (PATTERN_is(look[0], RS_DO)) + { + JOB->current++; + TRANS_do(RS_DO); + } + else if (PATTERN_is(look[0], RS_WHILE)) + { + TRANS_do(RS_WHILE); + } + else if (PATTERN_is(*look, RS_REPEAT)) + { + JOB->current++; + TRANS_do(RS_REPEAT); + } + else if (PATTERN_is(look[0], RS_LOOP)) + { + JOB->current++; + TRANS_loop(RS_LOOP); + } + else if (PATTERN_is(look[0], RS_UNTIL)) + { + TRANS_loop(RS_UNTIL); + } + else if (PATTERN_is(look[0], RS_WEND)) + { + JOB->current++; + TRANS_loop(RS_WEND); + } + else if (PATTERN_is(look[0], RS_FOR)) + { + if (PATTERN_is(look[1], RS_EACH)) + { + JOB->current += 2; + TRANS_for_each(); + } + else + { + JOB->current++; + TRANS_for(); + } + } + else if (PATTERN_is(look[0], RS_NEXT)) + { + JOB->current++; + TRANS_next(); + } + else if (PATTERN_is(look[0], RS_SELECT)) + { + JOB->current++; + TRANS_select(); + just_got_select = TRUE; + } + else if (PATTERN_is(look[0], RS_CASE)) + { + JOB->current++; + if (PATTERN_is(look[1], RS_ELSE)) + { + JOB->current++; + TRANS_default(); + } + else + TRANS_case(); + } + else if (PATTERN_is(look[0], RS_DEFAULT)) + { + JOB->current++; + TRANS_default(); + } + else if (PATTERN_is(look[0], RS_END) + && PATTERN_is(look[1], RS_SELECT)) + { + JOB->current += 2; + TRANS_end_select(); + } + else if (PATTERN_is(look[0], RS_FINALLY)) + { + JOB->current++; + TRANS_finally(); + } + else if (PATTERN_is(look[0], RS_CATCH)) + { + JOB->current++; + TRANS_catch(); + } + else if (PATTERN_is(*look, RS_WITH)) + { + JOB->current++; + TRANS_with(); + } + else if (PATTERN_is(look[0], RS_END) + && PATTERN_is(look[1], RS_WITH)) + { + JOB->current += 2; + TRANS_end_with(); + } + else if (PATTERN_is(look[0], RS_LET)) + { + JOB->current++; + if (!TRANS_affectation(FALSE)) + THROW(E_SYNTAX); + } + else + TRANS_statement(); + + /* + if (next_newline) + { + for(;;) + { + if (PATTERN_is_NEWLINE(*JOB->current) + || PATTERN_is_END(*JOB->current)) + break; + JOB->current++; + } + } + */ + + if (test_newline) + if (!PATTERN_is_newline(*JOB->current)) + THROW_UNEXPECTED(JOB->current); + + } + + TRANS_control_exit(); +} + +static void trans_call(const char *name, int nparam) +{ + CLASS_SYMBOL *sym; + int index; + + if (!TABLE_find_symbol(JOB->class->table, name, strlen(name), &index)) + return; + + sym = (CLASS_SYMBOL *)TABLE_get_symbol(JOB->class->table, index); + + if (TYPE_get_kind(sym->global.type) != TK_FUNCTION) + return; + + sym->global_used = TRUE; + + CODE_push_global(sym->global.value, FALSE, TRUE); + CODE_call(nparam); + CODE_drop(); +} + + +void TRANS_code(void) +{ + int i, j, n; + + // We must compile initialization functions at the end, because static local variables can modify them. + + n = ARRAY_count(JOB->class->function); + for (j = 0; j < n; j++) + { + i = (j + FUNC_INIT_MAX + 1) % n; + + _func = &JOB->class->function[i]; + + CODE_begin_function(_func); + + if (COMP_verbose) + { + printf("Compiling %s()...\n", TABLE_get_symbol_name(JOB->class->table, _func->name)); + if (_func->fast) + printf("Fast\n"); + } + + /* Do not debug implicit or generated functions */ + if (!_func->start || _func->name == NO_SYMBOL || TABLE_get_symbol_name(JOB->class->table, _func->name)[0] == '@') + JOB->nobreak = TRUE; + else + JOB->nobreak = FALSE; + + JOB->line = _func->line; + JOB->current = _func->start; + JOB->func = _func; + + /* fonction implicite ? */ + if (!_func->start) + { + if ((i == FUNC_INIT_DYNAMIC) && (JOB->form != NULL)) + { + //CODE_event(FALSE); + trans_call("@load", 0); + //CODE_event(TRUE); + } + + FUNCTION_add_last_pos_line(); + CODE_op(C_RETURN, 0, 0, TRUE); + if (COMP_verbose) + CODE_dump(_func->code, _func->ncode); + + continue; + } + + create_local_from_param(); + + translate_body(); + + CODE_return(2); // Return from function, ignore Gosub stack + + CODE_end_function(_func); + FUNCTION_add_last_pos_line(); + + _func->stack = _func->nlocal + _func->nctrl + CODE_stack_usage; + + if (COMP_verbose) + { + CODE_dump(_func->code, _func->ncode); + printf("%d local(s) %d control(s) ", _func->nlocal, _func->nctrl); + printf("%d stack\n", _func->stack); + printf("\n"); + } + + remove_local(); + } + + CLASS_check_properties(JOB->class); + CLASS_check_unused_global(JOB->class); + + JOB->func = NULL; +} + + + +bool TRANS_init_var(TRANS_DECL *decl) +{ + int i; + TRANS_ARRAY *array; + //PATTERN *save; + + if (decl->is_new) + { + if (TYPE_is_array(decl->type) && decl->array.ndim > 0) + { + array = &decl->array; + + if (TYPE_is_object(decl->type)) + CODE_push_class(TYPE_get_value(decl->type)); + else + CODE_push_number(TYPE_get_id(decl->type)); + + for (i = 0; i < array->ndim; i++) + CODE_push_number(array->dim[i]); + + CODE_new(array->ndim + 1, TRUE, FALSE); + } + else + { + JOB->current = decl->init; + TRANS_new(); + } + return TRUE; + } + else if (decl->init) + { + JOB->current = decl->init; + TRANS_expression(FALSE); + return TRUE; + } + else + return FALSE; +} + + +/* +void TRANS_init_object() +{ +} +*/ + +void TRANS_init_optional(TRANS_PARAM *param) +{ + PATTERN *look = param->optional; + PATTERN *save; + + if (look == NULL) + return; + + save = JOB->current; + + if (PATTERN_is(*look, RS_COMMA) || PATTERN_is(*look, RS_RBRA)) + { + CODE_push_void(); + } + else + { + if (!PATTERN_is(*look, RS_EQUAL)) + THROW("Syntax error. Invalid optional parameter"); + + look++; + JOB->current = look; + TRANS_expression(FALSE); + + if (!PATTERN_is(*JOB->current, RS_COMMA) && !PATTERN_is(*JOB->current, RS_RBRA)) + THROW("Syntax error. Invalid optional parameter"); + } + + JOB->current = save; +} diff --git a/main/gbc/gbc_trans_const.c b/main/gbc/gbc_trans_const.c new file mode 100644 index 00000000..e5c21898 --- /dev/null +++ b/main/gbc/gbc_trans_const.c @@ -0,0 +1,494 @@ +/*************************************************************************** + + gbc_trans_const.c + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TRANS_CONST_C + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" + +#include "gbc_trans.h" +#include "gb_code.h" + + +//#define DEBUG 1 + +#define MAX_STACK 32 + +TRANS_CONST_VALUE _stack[MAX_STACK]; +int _stack_ptr = 0; + + +static void throw_error(void) +{ + THROW("Constant expression expected"); +} + +static short get_nparam(PATTERN *tree, int count, int *pindex) +{ + PATTERN pattern; + int nparam = 0; + int index = *pindex; + + if (index < count) + { + pattern = tree[index + 1]; + if (PATTERN_is_param(pattern)) + { + index++; + nparam = PATTERN_index(pattern); + } + + while (index < count) + { + pattern = tree[index + 1]; + if (!PATTERN_is_param(pattern)) + break; + index++; + } + + *pindex = index; + } + + return (short)nparam; +} + +static TRANS_CONST_VALUE *push_stack() +{ + if (_stack_ptr >= MAX_STACK) + THROW("Constant expression too complex"); + return &_stack[_stack_ptr++]; +} + + +static TRANS_CONST_VALUE *pop_stack(int nparam) +{ + _stack_ptr -= nparam; + if (_stack_ptr < 0) + ERROR_panic("Bad stack computation"); + return &_stack[_stack_ptr]; +} + + +static void push_integer(int val) +{ + TRANS_CONST_VALUE *value = push_stack(); + value->type = T_INTEGER; + value->value._integer = val; +} + + +static void push_long(int64_t val) +{ + TRANS_CONST_VALUE *value = push_stack(); + + if (val < INT_MIN || val > INT_MAX) + { + value->type = T_LONG; + value->value._long = val; + } + else + { + value->type = T_INTEGER; + value->value._integer = (int)val; + } +} + + +static void push_number(int index) +{ + TRANS_CONST_VALUE *value; + TRANS_NUMBER number; + + if (TRANS_get_number(index, &number)) + THROW(E_SYNTAX); + + value = push_stack(); + + if (number.type == T_INTEGER) + { + value->type = T_INTEGER; + value->value._integer = number.ival; + } + else if (number.type == T_LONG) + { + value->type = T_LONG; + value->value._long = number.lval; + } + else if (number.type == T_FLOAT) + throw_error(); + + if (number.complex) + throw_error(); +} + + +#if 0 +static void push_string(int index, bool trans) +{ + TRANS_DECL decl; + SYMBOL *sym; + int len; + + if (index == VOID_STRING_INDEX) + len = 0; + else + { + sym = TABLE_get_symbol(JOB->class->string, index); + len = sym->len; + } + + if (len == 0) + { + CODE_push_void_string(); + } + else if (len == 1 && !trans) + { + CODE_push_char(*(sym->name)); + } + else + { + //CLEAR(&decl); + + if (trans) + decl.type = TYPE_make_simple(T_CSTRING); + else + decl.type = TYPE_make_simple(T_STRING); + decl.index = NO_SYMBOL; + decl.value = index; + + CODE_push_const(CLASS_add_constant(JOB->class, &decl)); + } + + push_type_id(T_STRING); +} +#endif + + +static void push_identifier(int index, bool point) +{ + CLASS_SYMBOL *sym = CLASS_get_symbol(JOB->class, index); + int type; + CONSTANT *constant; + TRANS_CONST_VALUE *value; + +#if DEBUG + fprintf(stderr, "trans_identifier: %.*s\n", sym->symbol.len, sym->symbol.name); +#endif + + if (point) + { + push_integer(index); + return; + } + + if (!TYPE_is_null(sym->global.type) && TYPE_get_kind(sym->global.type) == TK_CONST) + { + if (!TYPE_is_public(sym->global.type)) + sym->global_used = TRUE; + //fprintf(stderr, "_last_symbol_used = %.*s / global\n", sym->symbol.len, sym->symbol.name); + + constant = &JOB->class->constant[sym->global.value]; + type = TYPE_get_id(constant->type); + if (type >= T_BOOLEAN && type <= T_LONG) + { + push_integer(constant->value); + return; + } + + throw_error(); + } + + if (!TABLE_compare_ignore_case_len(sym->symbol.name, sym->symbol.len, "gb", 2)) + { + value = push_stack(); + value->type = T_OBJECT; + return; + } + + throw_error(); +} + +static void trans_subr(int subr, short nparam) +{ + TRANS_CONST_VALUE *sp; + SUBR_INFO *info = &COMP_subr_info[subr]; + + if (nparam < info->min_param) + THROW("Not enough arguments to &1()", info->name); + else if (nparam > info->max_param) + THROW("Too many arguments to &1()", info->name); + + sp = pop_stack(nparam); + + if (subr == SUBR_SizeOf) + { + static char _sizeof[9] = { 0, 1, 1, 2, 4, 8, 4, 8, 8 }; + int type; + + if (sp->type != T_INTEGER) + THROW(E_BADARG); + + type = sp[0].value._integer; + if (type < T_BOOLEAN) + THROW(E_BADARG); + if (type > T_DATE) + throw_error(); + + push_integer(_sizeof[type]); + return; + } + + throw_error(); +} + + +static void trans_operation(short op, short nparam) +{ + COMP_INFO *info = &COMP_res_info[op]; + int64_t v1 = 0, v2 = 0; + TRANS_CONST_VALUE *sp = pop_stack(nparam); + const char *name; + + if (nparam >= 1) + { + v1 = sp[0].type == T_LONG ? sp[0].value._long : (int64_t)sp[0].value._integer; + if (nparam >= 2) + v2 = sp[1].type == T_LONG ? sp[1].value._long : (int64_t)sp[1].value._integer; + } + + switch (info->value) + { + case OP_PLUS: + + push_long(v1 + v2); + break; + + case OP_MINUS: + + if (nparam == 1) + push_long((- v1)); + else + push_long(v1 - v2); + break; + + case OP_STAR: + + push_long(v1 * v2); + break; + + case OP_SLASH: case OP_DIV: + + push_long(v1 / v2); + break; + + case OP_MOD: + + push_long(v1 % v2); + break; + + case OP_NOT: + + push_long(~v1); + break; + + case OP_AND: + + push_long(v1 & v2); + break; + + case OP_OR: + + push_long(v1 | v2); + break; + + case OP_XOR: + + push_long(v1 ^ v2); + break; + + case OP_SHL: case OP_ASL: + + if (v2 < 0 || v2 > 64) + THROW(E_BADARG); + push_long(((v1 << v2) & 0x7FFFFFFFFFFFFFFFLL) | (v1 & 0x8000000000000000LL)); + break; + + case OP_SHR: case OP_ASR: + + if (v2 < 0 || v2 > 64) + THROW(E_BADARG); + push_long(((v1 >> v2) & 0x7FFFFFFFFFFFFFFFLL) | (v1 & 0x8000000000000000LL)); + break; + + case OP_LSL: + + if (v2 < 0 || v2 > 64) + THROW(E_BADARG); + push_long(v1 << v2); + break; + + case OP_LSR: + + if (v2 < 0 || v2 > 64) + THROW(E_BADARG); + push_long(v1 >> v2); + break; + + case OP_PT: + + if (nparam != 2) + throw_error(); + + if (sp[0].type != T_OBJECT) + throw_error(); + + name = TABLE_get_symbol_name(JOB->class->table, v2); + + if (!strcasecmp(name, "byte") || !strcasecmp(name, "boolean")) + push_integer(1); + else if (!strcasecmp(name, "short")) + push_integer(2); + else if (!strcasecmp(name, "integer")) + push_integer(4); + else if (!strcasecmp(name, "long")) + push_integer(8); + else if (!strcasecmp(name, "single")) + push_integer(4); + else if (!strcasecmp(name, "float")) + push_integer(8); + else if (!strcasecmp(name, "date")) + push_integer(8); + else + throw_error(); + + break; + + default: + throw_error(); + } +} + + +static void trans_const_from_tree(TRANS_TREE *tree, int count) +{ + static void *jump[] = { + &&__CONTINUE, &&__CONTINUE, &&__RESERVED, &&__IDENTIFIER, &&__INTEGER, &&__NUMBER, &&__STRING, &&__TSTRING, &&__CONTINUE, &&__SUBR, &&__CLASS, &&__CONTINUE, &&__CONTINUE + }; + + int i, op; + short nparam; + PATTERN pattern; + + pattern = NULL_PATTERN; + + #if DEBUG + fprintf(stderr, "-------------------------------------------- %d\n", _type_level); + #endif + + i = 0; + + for (i = 0; i < count; i++) + { + TRANS_tree_index = i; + pattern = tree[i]; + + goto *jump[PATTERN_type(pattern)]; + + __INTEGER: + + push_integer(PATTERN_signed_index(pattern)); + continue; + + __NUMBER: + + push_number(PATTERN_index(pattern)); + continue; + + __IDENTIFIER: + + push_identifier(PATTERN_index(pattern), PATTERN_is_point(pattern)); + continue; + + __SUBR: + + nparam = get_nparam(tree, count, &i); + trans_subr(PATTERN_index(pattern), nparam); + continue; + + __CLASS: + __STRING: + __TSTRING: + + throw_error(); + continue; + + __RESERVED: + + if (PATTERN_is(pattern, RS_TRUE)) + { + push_integer(-1); + } + else if (PATTERN_is(pattern, RS_FALSE)) + { + push_integer(0); + } + else + { + op = PATTERN_index(pattern); + nparam = get_nparam(tree, count, &i); + trans_operation((short)op, nparam); + } + + __CONTINUE: + ; + } + + TRANS_tree_index = -1; +} + +TRANS_CONST_VALUE *TRANS_const(void) +{ + TRANS_TREE *tree; + int tree_length; + + _stack_ptr = 0; + + TRANS_tree(FALSE, &tree, &tree_length); + + JOB->step = JOB_STEP_TREE; + trans_const_from_tree(tree, tree_length); + JOB->step = JOB_STEP_CODE; + + //FREE(&tree); + + if (_stack_ptr != 1) + THROW(E_SYNTAX); + + return &_stack[0]; +} diff --git a/main/gbc/gbc_trans_ctrl.c b/main/gbc/gbc_trans_ctrl.c new file mode 100644 index 00000000..a25d13e2 --- /dev/null +++ b/main/gbc/gbc_trans_ctrl.c @@ -0,0 +1,1379 @@ +/*************************************************************************** + + gbc_trans_ctrl.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_CTRL_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gb_code.h" +#include "gb_limit.h" + +/*#define DEBUG*/ +/*#define DEBUG_GOTO*/ + +#define BEGIN_NO_BREAK \ +{ \ + bool nobreak = JOB->nobreak; \ + JOB->nobreak = TRUE; + +#define END_NO_BREAK \ + JOB->nobreak = nobreak; \ +} + +#if defined(__GNUC__) && __GNUC__ == 8 +static volatile int ctrl_level; // Workaround a gcc compiler optimization bug +#else +static int ctrl_level; +#endif +static int ctrl_id; +static int ctrl_local; + +static TRANS_CTRL ctrl_data[MAX_CTRL_LEVEL]; + +static TRANS_CTRL *current_ctrl; + +static TRANS_GOTO *goto_info; +static TRANS_LABEL *label_info; +static short *ctrl_parent; + +static ushort *_relocation = NULL; + +typedef + struct { + ushort src; + ushort dst; + } + JUMP; + +static JUMP *_jumps = NULL; + +static void jump_length(ushort src, ushort dst, bool can_optimize) +{ + JUMP *jump; + + CODE_jump_length(src, dst); + + if (can_optimize) + { + jump = ARRAY_add(&_jumps); + jump->src = src; + jump->dst = dst; + } +} + + +static void control_set_value(int value) +{ + if (ctrl_level <= 0) + return; + + current_ctrl->value = value; +} + + +static int control_get_value(void) +{ + if (ctrl_level <= 0) + return 0; + + return current_ctrl->value; +} + + +static void control_add_relocation() +{ + if (!_relocation) + ARRAY_create(&_relocation); + + *((ushort *)ARRAY_add(&_relocation)) = CODE_get_current_pos(); +} + +static void control_add_pos(ushort **tab_pos, ushort pos) +{ + if (!(*tab_pos)) + ARRAY_create(tab_pos); + + *((ushort *)ARRAY_add(tab_pos)) = pos; +} + + +static void control_add_current_pos(void) +{ + control_add_pos(¤t_ctrl->pos, CODE_get_current_pos()); +} + +static void control_add_current_pos_break() +{ + control_add_pos(¤t_ctrl->pos_break, CODE_get_current_pos()); +} + +static void control_add_current_pos_continue() +{ + control_add_pos(¤t_ctrl->pos_continue, CODE_get_current_pos()); +} + +static void control_add_this_pos(ushort pos) +{ + control_add_pos(¤t_ctrl->pos, pos); +} + +static void control_add_this_pos_continue(ushort pos) +{ + control_add_pos(¤t_ctrl->pos_continue, pos); +} + + +static void control_jump_each_pos_with(ushort *tab_pos) +{ + int i; + + if (!tab_pos) + return; + + for (i = 0; i < ARRAY_count(tab_pos); i++) + jump_length(tab_pos[i], CODE_get_current_pos(), TRUE); +} + + +static void control_jump_each_pos(void) +{ + control_jump_each_pos_with(current_ctrl->pos); +} + + +static TRANS_CTRL *control_get_inner(void) +{ + int level = ctrl_level; + TRANS_CTRL *ctrl_look; + + for(;;) + { + if (!level) + return NULL; + + level--; + ctrl_look = &ctrl_data[level]; + + if ((ctrl_look->type == RS_DO) + || (ctrl_look->type == RS_WHILE) + || (ctrl_look->type == RS_REPEAT) + || (ctrl_look->type == RS_FOR) + || (ctrl_look->type == RS_EACH)) + return ctrl_look; + } +} + + +static TRANS_CTRL *control_get_inner_with(void) +{ + int level = ctrl_level; + TRANS_CTRL *ctrl_look; + + for(;;) + { + if (!level) + return NULL; + + level--; + ctrl_look = &ctrl_data[level]; + + if (ctrl_look->type == RS_WITH) + return ctrl_look; + } +} + + +static void add_goto(int index, int mode) +{ + TRANS_GOTO *info; + + if (goto_info == NULL) + ARRAY_create(&goto_info); + + info = ARRAY_add(&goto_info); + info->index = index; + info->pos = CODE_get_current_pos(); + info->ctrl_id = (ctrl_level == 0) ? 0 : current_ctrl->id; + info->line = JOB->line; + info->gosub = mode == RS_GOSUB; + info->on_goto = mode == RS_NONE; + + #ifdef DEBUG_GOTO + printf("add_goto: ctrl_id = %d (%ld)\n", info->ctrl_id, ARRAY_count(goto_info)); + #endif + + if (mode == RS_GOSUB) + { + //control_add_relocation(); + CODE_gosub(ctrl_local); + } + else if (mode == RS_GOTO) + CODE_jump(); + else + CODE_nop(); +} + + +static void control_enter(int type) +{ + short *parent; + + if (ctrl_level >= MAX_CTRL_LEVEL) + THROW("Too many nested control structures."); + + ctrl_id++; + + current_ctrl = &ctrl_data[ctrl_level]; + + current_ctrl->type = type; + current_ctrl->value = 0; + current_ctrl->pos = NULL; + current_ctrl->state = 0; + current_ctrl->local = ctrl_local; + current_ctrl->id = ctrl_id; + current_ctrl->loop_var = -1; + + parent = ARRAY_add(&ctrl_parent); + + if (ctrl_level == 0) + *parent = 0; + else + *parent = ctrl_data[ctrl_level - 1].id; + + switch (type) + { + case RS_FOR: + case RS_EACH: + ctrl_local += 2; + break; + + case RS_SELECT: + case RS_WITH: + ctrl_local += 1; + break; + } + + JOB->func->nctrl = Max(JOB->func->nctrl, ctrl_local); + if ((JOB->func->nctrl + JOB->func->nlocal) > MAX_LOCAL_SYMBOL) + THROW("Too many local variables"); + + ctrl_level++; +} + + +static void control_leave(void) +{ + control_jump_each_pos_with(current_ctrl->pos_break); + + ARRAY_delete(¤t_ctrl->pos); + ARRAY_delete(¤t_ctrl->pos_break); + ARRAY_delete(¤t_ctrl->pos_continue); + + ctrl_local = current_ctrl->local; + + ctrl_level--; + + if (ctrl_level > 0) + current_ctrl = &ctrl_data[ctrl_level - 1]; + else + current_ctrl = NULL; +} + + +static void control_check(int type, const char *msg1, const char *msg2) +{ + if (ctrl_level <= 0) + THROW(msg1); + + if (current_ctrl->type != type) + THROW(E_UNEXPECTED, msg2); +} + +static void control_check_two(int type1, int type2, const char *msg1, const char *msg2) +{ + if (ctrl_level <= 0) + THROW(msg1); + + if (current_ctrl->type != type1 && current_ctrl->type != type2) + THROW(E_UNEXPECTED, msg2); +} + +static void control_check_loop_var(short var) +{ + int i; + + for (i = 0; i < (ctrl_level - 1); i++) + { + if (ctrl_data[i].loop_var == var) + THROW("Loop variable already in use"); + } + + current_ctrl->loop_var = var; +} + +static void check_try(const char *name) +{ + if (TRANS_in_try) + { + const char *keyword = TRANS_in_try == RS_TRY ? "TRY" : "ASSERT"; + TRANS_in_try = RS_NONE; + if (strcmp(name, keyword)) + THROW("Cannot use &1 with &2", keyword, name); + else + THROW("Cannot use &1 twice", keyword); + } +} + +void TRANS_control_init(void) +{ + ctrl_level = 0; + ctrl_id = 0; + current_ctrl = NULL; + + goto_info = NULL; + + label_info = NULL; + + ctrl_local = 0; //JOB->func->nlocal; + JOB->func->nctrl = 0; + + ARRAY_create(&ctrl_parent); + ARRAY_create(&_jumps); +} + + +void TRANS_control_exit(void) +{ + int i, j; + CLASS_SYMBOL *sym; + int line; + TRANS_LABEL *label; + short id; + ushort *pcode; + JUMP *jump; + + // Relocate locals + + if (_relocation) + { + for (i = 0; i < ARRAY_count(_relocation); i++) + { + pcode = &JOB->func->code[_relocation[i]]; + if (PCODE_is_breakpoint(*pcode)) + pcode++; + + *pcode += JOB->func->nlocal; + + /*fprintf(stderr, "%04d : %04X --> ", _relocation[i], pcode); + pcode = (pcode & 0xFF00) | ((pcode & 0xFF) + JOB->func->nlocal); + fprintf(stderr, "%04X\n", pcode); + JOB->func->code[_relocation[i]] = pcode;*/ + } + + ARRAY_delete(&_relocation); + } + + // Resolve GOTOs + + if (goto_info) + { + line = JOB->line; + + for (i = 0; i < ARRAY_count(goto_info); i++) + { + JOB->line = goto_info[i].line; + /*printf("%d\n", JOB->line);*/ + + sym = CLASS_get_symbol(JOB->class, goto_info[i].index); + + if (TYPE_get_kind(sym->local.type) != TK_LABEL) + THROW("Label '&1' not declared", TABLE_get_symbol_name(JOB->class->table, goto_info[i].index)); + + label = &label_info[sym->local.value]; + + id = goto_info[i].ctrl_id; + + if (goto_info[i].gosub) + { + if (label->ctrl_id) + THROW("Forbidden GOSUB"); + } + else + { + for(;;) + { + if (label->ctrl_id == id) + break; + + if (id == 0) + THROW("Forbidden GOTO"); + + #ifdef DEBUG_GOTO + printf("id = %d ctrl_parent[id - 1] = %d (%ld)\n", id, ctrl_parent[id - 1], ARRAY_count(ctrl_parent)); + #endif + id = ctrl_parent[id - 1]; + } + } + + jump_length(goto_info[i].pos, label->pos, !goto_info[i].on_goto); + } + + JOB->line = line; + } + + // Optimize jumps + + for (i = 0; i < ARRAY_count(_jumps); i++) + { + jump = &_jumps[i]; + + for(j = 1; j <= 4; j++) // avoid infinite loop + { + if (jump->dst >= JOB->func->ncode) + break; + pcode = &JOB->func->code[jump->dst]; + + if (PCODE_is_breakpoint(*pcode)) + { + pcode++; + jump->dst++; + } + + if (!PCODE_is_jump(*pcode)) + break; + + jump->dst += ((short *)pcode)[1] + 2; + CODE_jump_length(jump->src, jump->dst); + } + } + + // Remove previously declared labels + + if (label_info) + { + for (i = 0; i < ARRAY_count(label_info); i++) + { + sym = CLASS_get_symbol(JOB->class, label_info[i].index); + TYPE_clear(&sym->local.type); + } + } + + ARRAY_delete(&goto_info); + ARRAY_delete(&ctrl_parent); + ARRAY_delete(&label_info); + ARRAY_delete(&_jumps); + + // Detect structures still opened + + if (ctrl_level == 0) return; + + switch (ctrl_data[ctrl_level - 1].type) + { + case RS_IF: + THROW(E_MISSING, "ENDIF"); + case RS_FOR: + case RS_EACH: + THROW(E_MISSING, "NEXT"); + case RS_DO: + THROW(E_MISSING, "LOOP"); + case RS_REPEAT: + THROW(E_MISSING, "UNTIL"); + case RS_WHILE: + THROW(E_MISSING, "WEND"); + case RS_SELECT: + THROW(E_MISSING, "END SELECT"); + case RS_WITH: + THROW(E_MISSING, "END WITH"); + } +} + +static ushort trans_jump_if(bool if_true, bool fast) +{ + ushort pos; + + if (CODE_check_jump_not()) + { + if_true = !if_true; + fast = FALSE; + } + + pos = CODE_get_current_pos(); + + CODE_jump_if(if_true, fast); + + return pos; +} + +/*static void trans_endif(void) +{ + if (current_ctrl->state == 0) + jump_length(control_get_value(), CODE_get_current_pos()); + + control_jump_each_pos(); +}*/ + +static void trans_else(void) +{ + BEGIN_NO_BREAK + { + control_add_current_pos_break(); + CODE_jump(); + } + END_NO_BREAK + + control_jump_each_pos_with(current_ctrl->pos_continue); + ARRAY_delete(¤t_ctrl->pos_continue); + //jump_length(control_get_value(), CODE_get_current_pos()); + + current_ctrl->state = 1; +} + +static bool trans_boolean_expr(void) +{ + TRANS_expression(FALSE); + return TYPE_is_boolean(TRANS_get_last_type()); +} + +static void trans_if(void) +{ + bool fast; + + fast = trans_boolean_expr(); + + if (PATTERN_is(*JOB->current, RS_AND)) + { + for(;;) + { + control_add_this_pos_continue(trans_jump_if(FALSE, fast)); + + if (!PATTERN_is(*JOB->current, RS_AND)) + break; + + JOB->current++; + + TRANS_want(RS_IF, "AND IF"); + + fast = trans_boolean_expr(); + } + } + else if (PATTERN_is(*JOB->current, RS_OR)) + { + for(;;) + { + control_add_this_pos(trans_jump_if(TRUE, fast)); + + if (!PATTERN_is(*JOB->current, RS_OR)) + break; + + JOB->current++; + + TRANS_want(RS_IF, "OR IF"); + + fast = trans_boolean_expr(); + } + + control_add_current_pos_continue(); + CODE_jump(); + } + else + { + control_add_this_pos_continue(trans_jump_if(FALSE, fast)); + } + + if (PATTERN_is(*JOB->current, RS_THEN)) + JOB->current++; + else if (!PATTERN_is_newline(*JOB->current)) + THROW(E_EXPECTED, "THEN"); + + control_jump_each_pos(); + ARRAY_delete(¤t_ctrl->pos); +} + +static void trans_else_if(void) +{ + trans_else(); + current_ctrl->state = 0; + trans_if(); +} + + +void TRANS_if(void) +{ + PATTERN *look; + bool has_else; + + control_enter(RS_IF); + + trans_if(); + + if (PATTERN_is_newline(*JOB->current)) + return; + + has_else = FALSE; + look = JOB->current; + for(;;) + { + look++; + if (PATTERN_is_newline(*look)) + break; + if (PATTERN_is(*look, RS_ELSE)) + { + has_else = TRUE; + *look = PATTERN_make(RT_NEWLINE, 0); + break; + } + } + + TRANS_statement(); + + if (has_else) + { + JOB->current++; + trans_else(); + TRANS_statement(); + } + + TRANS_endif(); +} + + +void TRANS_else(void) +{ + control_check(RS_IF, "ELSE without IF", "ELSE"); + + if (current_ctrl->state) + THROW(E_UNEXPECTED, "ELSE"); + + if (TRANS_is(RS_IF)) + trans_else_if(); + else + trans_else(); +} + + +void TRANS_endif(void) +{ + control_check(RS_IF, "ENDIF without IF", "ENDIF"); + control_jump_each_pos_with(current_ctrl->pos_continue); + control_leave(); +} + + +void TRANS_goto(void) +{ + int index; + + check_try("GOTO"); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + + index = PATTERN_index(*JOB->current); + JOB->current++; + + add_goto(index, RS_GOTO); +} + +void TRANS_gosub(void) +{ + int index; + + check_try("GOSUB"); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + + index = PATTERN_index(*JOB->current); + JOB->current++; + + add_goto(index, RS_GOSUB); +} + + +void TRANS_on_goto_gosub(void) +{ + bool gosub; + int pos, n; + int index; + + TRANS_expression(FALSE); + + if (TRANS_is(RS_GOTO)) + gosub = FALSE; + else if (TRANS_is(RS_GOSUB)) + gosub = TRUE; + else + THROW(E_SYNTAX); + + check_try(gosub ? "ON GOSUB" : "ON GOTO"); + + pos = CODE_get_current_pos(); + CODE_nop(); + + for (n = 1;; n++) + { + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + + index = PATTERN_index(*JOB->current); + JOB->current++; + + add_goto(index, RS_NONE); + + if (!TRANS_is(RS_COMMA)) + break; + + if (n == 127) + THROW("Too many labels"); + + TRANS_newline(); + } + + pos = CODE_set_current_pos(pos); + CODE_on(n); + CODE_set_current_pos(pos); + + if (gosub) + { + //control_add_relocation(); + CODE_gosub(ctrl_local); + } + else + CODE_jump(); +} + + +void TRANS_do(int type) +{ + bool is_until; + bool fast; + + control_enter(type); + control_set_value(CODE_get_current_pos()); + + if (type == RS_REPEAT) + return; + + is_until = PATTERN_is(*JOB->current, RS_UNTIL); + + if (PATTERN_is(*JOB->current, RS_WHILE) + || is_until) + { + JOB->current++; + + fast = trans_boolean_expr(); + control_add_this_pos(trans_jump_if(is_until, fast)); + } +} + + +void TRANS_loop(int type) +{ + ushort pos; + bool is_until; + bool fast; + + if (type == RS_LOOP) + control_check(RS_DO, "LOOP without DO", "LOOP"); + else if (type == RS_UNTIL) + control_check(RS_REPEAT, "UNTIL without REPEAT", "UNTIL"); + else if (type == RS_WEND) + control_check(RS_WHILE, "WEND without WHILE", "WEND"); + + control_jump_each_pos_with(current_ctrl->pos_continue); + + is_until = PATTERN_is(*JOB->current, RS_UNTIL); + + if ((type != RS_WEND) && (PATTERN_is(*JOB->current, RS_WHILE) || is_until)) + { + JOB->current++; + + fast = trans_boolean_expr(); + pos = trans_jump_if(!is_until, fast); + + jump_length(pos, control_get_value(), TRUE); + } + else + { + pos = CODE_get_current_pos(); + CODE_jump(); + jump_length(pos, control_get_value(), TRUE); + } + + control_jump_each_pos(); + control_leave(); +} + + +static void trans_select_break(bool do_not_add_pos) +{ + BEGIN_NO_BREAK + { + if (current_ctrl->value) + { + if (!do_not_add_pos) + { + control_add_current_pos(); + CODE_jump(); + } + + jump_length(current_ctrl->value, CODE_get_current_pos(), TRUE); + } + } + END_NO_BREAK +} + + +void TRANS_select(void) +{ + control_enter(RS_SELECT); + + if (PATTERN_is(*JOB->current, RS_CASE)) + JOB->current++; + + TRANS_expression(FALSE); + + control_add_relocation(); + CODE_pop_ctrl(current_ctrl->local); +} + + +void TRANS_case(void) +{ + int i; + short pos; + short local; + bool like; + + control_check(RS_SELECT, "CASE without SELECT", "CASE"); + + if (current_ctrl->state) + THROW("Default case must be the last one"); + + trans_select_break(FALSE); + + local = current_ctrl->local; + + control_enter(RS_CASE); + + like = TRANS_is(RS_LIKE); + + for(i = 0; ; i++) + { + if (i > MAX_CASE_EXPR) + THROW("Too many expressions in CASE"); + + if (PATTERN_is_newline_end(*JOB->current)) + THROW("Unexpected end of line"); + + /*CODE_dup(); + TRANS_expression(FALSE); + CODE_op(C_EQ, 2);*/ + + if (like) + { + control_add_relocation(); + CODE_push_local(local); + TRANS_expression(FALSE); + CODE_op(C_LIKE, 0, 2, TRUE); + } + else if (TRANS_is(RS_TO)) + { + control_add_relocation(); + CODE_push_local(local); + TRANS_expression(FALSE); + CODE_op(C_LE, 0, 2, TRUE); + } + else + { + TRANS_expression(FALSE); + + if (TRANS_is(RS_TO)) + { + control_add_relocation(); + CODE_push_local(local); + CODE_op(C_LE, 0, 2, TRUE); + control_add_relocation(); + CODE_push_local(local); + TRANS_expression(FALSE); + CODE_op(C_LE, 0, 2, TRUE); + CODE_op(C_AND, 0, 2, TRUE); + } + else + { + control_add_relocation(); + CODE_push_local(local); + CODE_op(C_EQ, 0, 2, TRUE); + } + } + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + { + //TRANS_ignore(RS_THEN); + pos = CODE_get_current_pos(); + CODE_jump_if(FALSE, FALSE); + break; + } + + control_add_current_pos(); + CODE_jump_if(TRUE, FALSE); + + JOB->current++; + TRANS_newline(); + } + + control_jump_each_pos(); + control_leave(); + + current_ctrl->value = pos; +} + + +void TRANS_default(void) +{ + control_check(RS_SELECT, "DEFAULT without SELECT", "DEFAULT"); + + if (current_ctrl->state) + THROW("Default case already defined"); + + trans_select_break(FALSE); + + current_ctrl->value = 0; /*CODE_get_current_pos();*/ + current_ctrl->state = 1; +} + + +void TRANS_end_select(void) +{ + control_check(RS_SELECT, "END SELECT without SELECT", "END SELECT"); + + /* + if (current_ctrl->value) + jump_length(current_ctrl->value, CODE_get_current_pos()); + */ + + trans_select_break(TRUE); + + control_jump_each_pos(); + + control_leave(); +} + + +void TRANS_break(void) +{ + TRANS_CTRL *ctrl_inner = control_get_inner(); + + check_try("BREAK"); + + if (!ctrl_inner) + THROW(E_UNEXPECTED, "BREAK"); + + control_add_pos(&ctrl_inner->pos_break, CODE_get_current_pos()); + CODE_jump(); +} + + +void TRANS_continue(void) +{ + TRANS_CTRL *ctrl_inner = control_get_inner(); + + check_try("CONTINUE"); + + if (!ctrl_inner) + THROW(E_UNEXPECTED, "CONTINUE"); + + control_add_pos(&ctrl_inner->pos_continue, CODE_get_current_pos()); + CODE_jump(); +} + + +void TRANS_return(void) +{ + if (FUNCTION_is_procedure(JOB->func)) + { + if (!(PATTERN_is_newline(*JOB->current) || PATTERN_is_end(*JOB->current))) + THROW("Return value datatype not specified in function declaration"); + } + + if (PATTERN_is_newline(*JOB->current) || PATTERN_is_end(*JOB->current)) + CODE_return(0); + else + { + TRANS_expression(FALSE); + CODE_return(1); + } +} + + +void TRANS_for(void) +{ + int local; + bool downto = FALSE; + + control_enter(RS_FOR); + + local = TRANS_loop_local(FALSE); + + TRANS_want(RS_EQUAL, "="); + + TRANS_expression(FALSE); + + CODE_pop_local_noref(local); + + control_check_loop_var(local); + + if (TRANS_is(RS_DOWNTO)) + downto = TRUE; + else + TRANS_want(RS_TO, "TO"); + + TRANS_expression(FALSE); + + if (PATTERN_is(*JOB->current, RS_STEP)) + { + JOB->current++; + TRANS_expression(FALSE); + if (downto) + CODE_op(C_NEG, 0, 1, TRUE); + } + else + { + CODE_push_number(downto ? -1 : 1); + } + + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_UNEXPECTED, READ_get_pattern(JOB->current)); + + control_add_relocation(); + CODE_jump_first(current_ctrl->local); + + control_set_value(CODE_get_current_pos()); + + control_add_current_pos(); + CODE_jump_next(); + + CODE_pop_local_noref(local); +} + + +void TRANS_for_each(void) +{ + PATTERN *iterator = JOB->current; + PATTERN *save; + int local; + bool var = TRUE; + + while (!PATTERN_is(*JOB->current, RS_IN)) + { + if (PATTERN_is_newline_end(*JOB->current)) + { + JOB->current = iterator; + var = FALSE; + break; + } + JOB->current++; + } + + control_enter(RS_EACH); + + if (var) + JOB->current++; + + TRANS_expression(FALSE); + + /*CODE_pop_ctrl(current_ctrl->local);*/ + + control_add_relocation(); + CODE_first(current_ctrl->local); + + control_set_value(CODE_get_current_pos()); + control_add_current_pos(); + + if (var) + { + CODE_next(FALSE); + + save = JOB->current; + JOB->current = iterator; + + local = TRANS_loop_local(TRUE); + CODE_pop_local(local); + + //TRANS_reference(); + + TRANS_want(RS_IN, "IN"); + + JOB->current = save; + } + else + CODE_next(TRUE); + + return; +} + + +void TRANS_next(void) +{ + ushort pos; + + control_check_two(RS_FOR, RS_EACH, "NEXT without FOR", "NEXT"); + + /* + if (current_ctrl->type == RS_FOR) + { + control_jump_each_pos_with(current_ctrl->pos_continue); + + pos = CODE_get_current_pos(); + CODE_jump(); + jump_length(pos, control_get_value()); + + control_jump_each_pos(); + control_leave(); + } + else + { + */ + control_jump_each_pos_with(current_ctrl->pos_continue); + + pos = CODE_get_current_pos(); + CODE_jump(); + jump_length(pos, control_get_value(), TRUE); + + control_jump_each_pos(); + control_leave(); +} + + +void TRANS_assert(void) +{ + ushort pos; + + check_try("ASSERT"); + + if (!JOB->debug || JOB->exec) + CODE_disable(); + + TRANS_in_try = RS_ASSERT; + + TRANS_expression(FALSE); + + if (PATTERN_is(*JOB->current, RS_PRINT) || PATTERN_is(*JOB->current, RS_ERROR)) + { + CODE_dup(); + pos = CODE_get_current_pos(); + CODE_jump_if(TRUE, FALSE); + + TRANS_statement(); + + jump_length(pos, CODE_get_current_pos(), TRUE); + } + + TRANS_subr(TS_SUBR_DEBUG, 1); + CODE_drop(); + + TRANS_in_try = RS_NONE; + + if (!JOB->debug || JOB->exec) + CODE_enable(); +} + + +void TRANS_try(void) +{ + ushort pos; + + check_try("TRY"); + + pos = CODE_get_current_pos(); + CODE_try(); + + TRANS_in_try = RS_TRY; + TRANS_statement(); + TRANS_in_try = RS_NONE; + + jump_length(pos, CODE_get_current_pos(), TRUE); + CODE_end_try(); +} + + +void TRANS_finally(void) +{ + ushort pos = CODE_get_current_pos(); + + if ((JOB->func->finally != 0) + || (JOB->func->catch != 0) + || (pos == 0)) + THROW(E_UNEXPECTED, "FINALLY"); + + JOB->func->finally = pos; +} + + +void TRANS_catch(void) +{ + ushort pos = CODE_get_current_pos(); + + if ((JOB->func->catch != 0) + || (pos == 0)) + THROW(E_UNEXPECTED, "CATCH"); + + CODE_catch(); + + JOB->func->catch = CODE_get_current_pos(); +} + + +void TRANS_label(void) +{ + CLASS_SYMBOL *sym; + int sym_index; + TRANS_LABEL *label; + + sym_index = PATTERN_index(*JOB->current); + JOB->current++; + + sym = CLASS_declare(JOB->class, sym_index, TK_LABEL, FALSE); + + if (label_info == NULL) + ARRAY_create(&label_info); + + sym->local.type = TYPE_make(T_VOID, -1, TK_LABEL); + sym->local.value = ARRAY_count(label_info); + + label = ARRAY_add(&label_info); + + label->index = sym_index; + label->ctrl_id = (ctrl_level == 0) ? 0 : current_ctrl->id; + label->pos = CODE_get_current_pos(); + + #ifdef DEBUG_GOTO + printf("TRANS_label: %.*s ctrl_id = %d\n", sym->symbol.len, sym->symbol.name, label->ctrl_id); + #endif + + /* on saute le ':' */ + JOB->current++; + + /* + // A new set of control stack slots must be used for a GOSUB subroutine + + for (i = 0; i < ARRAY_count(goto_info); i++) + { + if (goto_info[i].gosub && goto_info[i].index == sym_index) + { + ctrl_local = JOB->func->nctrl + JOB->func->nlocal; + return; + } + } + */ +} + + +void TRANS_with(void) +{ + if (!TRANS_affectation(TRUE)) + TRANS_expression(FALSE); + + control_enter(RS_WITH); + + control_add_relocation(); + CODE_pop_ctrl(current_ctrl->local); +} + +void TRANS_use_with(void) +{ + TRANS_CTRL *ctrl_inner = control_get_inner_with(); + + if (ctrl_inner == NULL) + THROW("Syntax error. Point syntax used outside of WITH / END WITH"); + + control_add_relocation(); + CODE_push_local(ctrl_inner->local); +} + + +void TRANS_end_with(void) +{ + control_check(RS_WITH, "END WITH without WITH", "END WITH"); + + control_leave(); +} + + +void TRANS_raise(void) +{ + CLASS_SYMBOL *sym; + int index; + int np; + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error in event name"); + + index = PATTERN_index(*JOB->current); + sym = CLASS_get_symbol(JOB->class, index); + + JOB->current++; + + if (TYPE_get_kind(sym->global.type) == TK_EVENT) + { + if (TYPE_is_static(JOB->func->type)) + THROW("Cannot raise events in static function"); + + CODE_push_event(sym->global.value); + } + else + { + CODE_push_unknown_event(CLASS_add_unknown(JOB->class, index)); + } + + np = 0; + + if (TRANS_is(RS_LBRA)) + { + for (;;) + { + if (TRANS_is(RS_RBRA)) + break; + + if (np > 0) + TRANS_want(RS_COMMA, "comma"); + + if (np >= MAX_PARAM_FUNC) + THROW("Too many arguments"); + + TRANS_expression(FALSE); + np++; + } + } + + CODE_call(np); + + if (!TRANS_in_assignment) + CODE_drop(); +} diff --git a/main/gbc/gbc_trans_expr.c b/main/gbc/gbc_trans_expr.c new file mode 100644 index 00000000..c44072cc --- /dev/null +++ b/main/gbc/gbc_trans_expr.c @@ -0,0 +1,1196 @@ +/*************************************************************************** + + gbc_trans_expr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TRANS_EXPR_C + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" + +#include "gbc_trans.h" +#include "gb_code.h" + +//#define DEBUG 1 + +//static bool _accept_statement = FALSE; + +static bool _must_drop_vargs = FALSE; + +static TYPE _type[MAX_EXPR_LEVEL]; +static int _type_level = 0; +static TYPE _last_type; + +static CLASS_SYMBOL *_last_symbol_used = NULL; +static bool _last_symbol_global = FALSE; + +static void trans_expression(bool check_statement); + +#if DEBUG + +static void _push_type(TYPE type) +{ + if (_type_level >= MAX_EXPR_LEVEL) // should have been detected by TRANS_tree() + THROW("Expression too complex"); + + fprintf(stderr, "level = %d / %d\n", _type_level, MAX_EXPR_LEVEL); + _type[_type_level++] = type; +} + +static void _drop_type(int n) +{ + _type_level -= n; + if (_type_level < 0) + ERROR_panic("Incorrect type analyze"); +} + +#define push_type(_type) fprintf(stderr, "push_type: %d in %s.%d\n", (_type).t.id, __func__, __LINE__), _push_type(_type) +#define push_type_id(_id) fprintf(stderr, "push_type_id: %d in %s.%d\n", (_id), __func__, __LINE__), _push_type(TYPE_make_simple(_id)) +#define pop_type() (fprintf(stderr, "pop_type: in %s.%d\n", __func__, __LINE__),(_type[--_type_level])) +#define drop_type(_n) fprintf(stderr, "drop_type: %d in %s.%d\n", (_n), __func__, __LINE__),_drop_type(_n) +#define get_type(_i, _nparam) (fprintf(stderr, "get_type(%d,%d): %d in %s.%d\n", (_i), (_nparam), (_type[_type_level + (_i) - (_nparam)].l), __func__, __LINE__),(_type[_type_level + (_i) - (_nparam)])) +#define get_type_id(_i, _nparam) (fprintf(stderr, "get_type_id(%d,%d): %d in %s.%d\n", (_i), (_nparam), (_type[_type_level + (_i) - (_nparam)].t.id), __func__, __LINE__),(_type[_type_level + (_i) - (_nparam)].t.id)) +#define dup_type() fprintf(stderr, "dup_type: in %s.%d\n", __func__, __LINE__),push_type(_type[_type_level - 1]) + +#else + +static void push_type(TYPE type) +{ + if (_type_level >= MAX_EXPR_LEVEL) // should have been detected by TRANS_tree() + THROW("Expression too complex"); + + _type[_type_level++] = type; +} + +static void drop_type(int n) +{ + _type_level -= n; + if (_type_level < 0) + ERROR_panic("Incorrect type analyze"); +} + +#define push_type_id(_id) push_type(TYPE_make_simple(_id)) +#define pop_type() (_type[--_type_level]) +#define get_type(_i, _nparam) (_type[_type_level + (_i) - (_nparam)]) +#define get_type_id(_i, _nparam) (get_type(_i, _nparam).t.id) +#define dup_type() push_type(_type[_type_level - 1]) + +#endif + +static short get_nparam(PATTERN *tree, int count, int *pindex) +{ + PATTERN pattern; + int nparam = 0; + int index = *pindex; + + if (index < count) + { + pattern = tree[index + 1]; + if (PATTERN_is_param(pattern)) + { + index++; + nparam = PATTERN_index(pattern); + } + + while (index < count) + { + pattern = tree[index + 1]; + if (!PATTERN_is_param(pattern)) + break; + index++; + } + + *pindex = index; + } + + // Handle the case of a subroutine without parenthesis + return (short)nparam; +} + +static short get_nparam_byref(PATTERN *tree, int count, int *pindex, uint64_t *byref) +{ + PATTERN pattern; + int nparam = 0; + int index = *pindex; + int shift; + + if (index < count) + { + pattern = tree[index + 1]; + if (PATTERN_is_param(pattern)) + { + index++; + nparam = PATTERN_index(pattern); + } + + shift = 0; + *byref = 0; + while (index < count) + { + pattern = tree[index + 1]; + if (!PATTERN_is_param(pattern)) + break; + index++; + *byref |= (uint64_t)PATTERN_index(pattern) << shift; + shift += 16; + } + + *pindex = index; + } + + return (short)nparam; +} + + +static void push_integer(int index) +{ + CODE_push_number(index); + push_type_id(T_INTEGER); +} + +static void push_number(int index) +{ + TRANS_NUMBER number; + TRANS_DECL decl; + + if (TRANS_get_number(index, &number)) + THROW(E_SYNTAX); + + if (number.type == T_INTEGER) + { + CODE_push_number(number.ival); + } + else if (number.type == T_FLOAT && COMP_version >= 0x03180000 && number.dval == (double)(int)number.dval && number.dval >= -128 && number.dval <= 127) + { + CODE_push_float(number.dval); + } + else + { + CLEAR(&decl); + decl.type = TYPE_make_simple(number.type); + decl.index = NO_SYMBOL; + decl.value = index; + if (number.type == T_LONG) + decl.lvalue = number.lval; + CODE_push_const(CLASS_add_constant(JOB->class, &decl)); + } + + if (number.complex) + { + CODE_push_complex(); + push_type_id(T_OBJECT); + } + else + push_type_id(number.type); +} + + +static void push_string(int index, bool trans) +{ + TRANS_DECL decl; + SYMBOL *sym; + int len; + + if (index == VOID_STRING_INDEX) + len = 0; + else + { + sym = TABLE_get_symbol(JOB->class->string, index); + len = sym->len; + } + + if (len == 0) + { + CODE_push_void_string(); + } + else if (len == 1 && !trans) + { + CODE_push_char(*(sym->name)); + } + else + { + //CLEAR(&decl); + + if (trans) + decl.type = TYPE_make_simple(T_CSTRING); + else + decl.type = TYPE_make_simple(T_STRING); + decl.index = NO_SYMBOL; + decl.value = index; + + CODE_push_const(CLASS_add_constant(JOB->class, &decl)); + } + + push_type_id(T_STRING); +} + +bool TRANS_string(PATTERN pattern) +{ + if (!PATTERN_is_string(pattern)) + return TRUE; + push_string(PATTERN_index(pattern), FALSE); + return FALSE; +} + + +static void trans_class(int index) +{ + CODE_push_class(TRANS_get_class(index)); + push_type_id(T_OBJECT); +} + +static void trans_identifier(int index, bool point, PATTERN next) +{ + CLASS_SYMBOL *sym = CLASS_get_symbol(JOB->class, index); + bool is_static; + bool is_func; + int type; + CONSTANT *constant; + +#if DEBUG + fprintf(stderr, "trans_identifier: %.*s\n", sym->symbol.len, sym->symbol.name); +#endif + + _last_symbol_used = NULL; + //fprintf(stderr, "_last_symbol_used = NULL\n"); + + if (!TYPE_is_null(sym->local.type) && !point) + { + if (TYPE_is_static(sym->local.type)) + CODE_push_global(sym->local.value, TRUE, FALSE); + else + CODE_push_local_ref(sym->local.value, TYPE_must_ref(sym->local.type)); + + push_type(sym->local.type); + sym->local_used = TRUE; + _last_symbol_used = sym; + _last_symbol_global = FALSE; + //fprintf(stderr, "_last_symbol_used = %.*s / local\n", sym->symbol.len, sym->symbol.name); + } + else if (!TYPE_is_null(sym->global.type) && !point) + { + type = TYPE_get_kind(sym->global.type); + if (!TYPE_is_public(sym->global.type)) + { + sym->global_used = TRUE; + _last_symbol_used = sym; + _last_symbol_global = TRUE; + //fprintf(stderr, "_last_symbol_used = %.*s / global\n", sym->symbol.len, sym->symbol.name); + } + + if (type == TK_CONST) + { + if (PATTERN_is_point(next)) + goto __CLASS; + + constant = &JOB->class->constant[sym->global.value]; + type = TYPE_get_id(constant->type); + if (type == T_BOOLEAN) + CODE_push_boolean(constant->value); + else if (type == T_INTEGER) + CODE_push_number(constant->value); + else + CODE_push_const(sym->global.value); + + push_type_id(type); + } + else if (type == TK_EXTERN) + { + if (PATTERN_is_point(next)) + goto __CLASS; + + CODE_push_extern(sym->global.value); + push_type(JOB->class->ext_func[sym->global.value].type); + } + /* That breaks some code if the property has the same name as a class! + else if (type == TK_PROPERTY) + { + CODE_push_me(FALSE); + CODE_push_unknown(CLASS_add_unknown(JOB->class, index)); + }*/ + else if (type == TK_EVENT || type == TK_LABEL || type == TK_PROPERTY) + { + goto __CLASS; + } + else /* TK_FUNCTION || TK_VARIABLE */ + { + is_static = TYPE_is_static(sym->global.type); + is_func = type == TK_FUNCTION; + + if (is_func && PATTERN_is_point(next)) + goto __CLASS; + + if (!is_static && TYPE_is_static(JOB->func->type)) + THROW("Dynamic symbols cannot be used in static function"); + + if (is_func && PATTERN_is_point(next)) + goto __CLASS; + else + CODE_push_global(sym->global.value, is_static, is_func); + + if (is_func) + push_type(JOB->class->function[sym->global.value].type); + else if (is_static) + push_type(JOB->class->stat[sym->global.value].type); + else + push_type(JOB->class->dyn[sym->global.value].type); + } + } + else + { + if (point) + { + CODE_push_unknown(CLASS_add_unknown(JOB->class, index)); + push_type_id(T_VOID); + } + else + goto __CLASS; + } + + return; + +__CLASS: + + trans_class(index); +} + + +static void trans_subr(int subr, short nparam) +{ + SUBR_INFO *info = &COMP_subr_info[subr]; + int type; + + if (nparam < info->min_param) + THROW("Not enough arguments to &1()", info->name); + else if (nparam > info->max_param) + THROW("Too many arguments to &1()", info->name); + + if (subr == SUBR_VarPtr) + { + if (CODE_check_varptr()) + THROW("VarPtr() argument must be a dynamic, a static or a local variable"); + } + else if (subr == SUBR_IsMissing) + { + JOB->func->use_is_missing = TRUE; + if (CODE_check_ismissing()) + THROW("IsMissing() requires a function argument"); + } + + CODE_subr(info->opcode, nparam, info->optype, info->max_param == info->min_param); + + type = info->type; + switch(type) + { + case RST_SAME: + type = get_type_id(0, nparam); + break; + + case RST_BCLR: + type = get_type_id(0, nparam); + break; + + case RST_MIN: + type = Max(get_type_id(0, nparam), get_type_id(1, nparam)); + if (type > T_DATE && type != T_VARIANT) + THROW("Number or Date expected"); + } + + if (nparam) + drop_type(nparam); + + push_type_id(type); +} + + +static void trans_operation(short op, short nparam, PATTERN previous) +{ + COMP_INFO *info = &COMP_res_info[op]; + int type = info->type; + int type1, type2; + TYPE ftype; + + switch (info->value) + { + case OP_PT: + if (nparam == 0) + { + TRANS_use_with(); + type = T_OBJECT; + } + else if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + else + { + switch(get_type_id(0, nparam)) + { + case T_OBJECT: + case T_VARIANT: + case T_STRUCT: + case T_STRING: + case T_ARRAY: + break; + + default: + THROW("Not an object"); + } + } + + break; + + case OP_EXCL: + if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + break; + + case OP_LSQR: + CODE_push_array(nparam); + break; + + case OP_RSQR: + TRANS_subr(TS_SUBR_ARRAY, nparam); + if (nparam > MAX_PARAM_OP) + nparam--; + break; + + case OP_COLON: + TRANS_subr(TS_SUBR_COLLECTION, nparam); + if (nparam > MAX_PARAM_OP) + nparam -= 2; + break; + + case OP_MINUS: + if (nparam == 1) + { + CODE_op(C_NEG, 0, nparam, TRUE); + type = RST_SAME; + } + else + CODE_op(info->code, info->subcode, nparam, TRUE); + break; + + case OP_AMP: + if (nparam == 1) + CODE_op(info->code, 1, 2, TRUE); + else + CODE_op(info->code, info->subcode, nparam, FALSE); + break; + + default: + CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN)); + } + + switch(type) + { + case RST_SAME: + ftype = get_type(0, nparam); + break; + + case RST_ADD: + type = Max(get_type_id(0, nparam), get_type_id(1, nparam)); + if (type == T_DATE) + type = T_FLOAT; + ftype = TYPE_make_simple(type); + break; + + case RST_AND: + type1 = get_type_id(0, nparam); + type2 = get_type_id(1, nparam); + + if (type1 != T_VARIANT && type2 != T_VARIANT) + { + if ((type1 > T_BOOLEAN && type1 <= T_LONG) ^ (type2 > T_BOOLEAN && type2 <= T_LONG)) + COMPILE_print(MSG_WARNING, JOB->line, "integer and boolean mixed with `&1' operator", info->name); + } + + type = Max(type1, type2); + + if (type > T_LONG) + { + if (type != T_VARIANT) + type = T_BOOLEAN; + } + + ftype = TYPE_make_simple(type); + break; + + case RST_NOT: + type = get_type_id(0, nparam); + if (type == T_STRING || type == T_OBJECT || type == T_DATE) + type = T_BOOLEAN; + ftype = TYPE_make_simple(type); + break; + + case RST_MOD: + type1 = get_type_id(0, nparam); + type2 = get_type_id(1, nparam); + + if (type1 != T_VARIANT && type2 != T_VARIANT) + { + if (type1 <= T_BOOLEAN || type1 > T_LONG || type2 <= T_BOOLEAN || type2 > T_LONG) + THROW("Type mismatch"); + } + + ftype = TYPE_make_simple(Max(type1, type2)); + break; + + case RST_DIV: + type = Max(get_type_id(0, nparam), get_type_id(1, nparam)); + if (type <= T_FLOAT) + type = T_FLOAT; + else + type = T_VARIANT; + ftype = TYPE_make_simple(type); + break; + + case RST_GET: + ftype = TYPE_make_simple(T_VARIANT); + break; + + case RST_BCLR: + ftype = get_type(0, nparam); + break; + + /*case RST_GET: + ftype = get_type(0, nparam); + if (ftype.t.id == T_OBJECT) + { + ftype = JOB->class->class[ftype.t.value].array; + if (TYPE_is_null(ftype)) + ftype = TYPE_make_simple(T_VARIANT); + } + else + ftype = TYPE_make_simple(T_VARIANT); + + fprintf(stderr, "RST_GET: %s\n", TYPE_get_desc(ftype)); + break;*/ + + default: + if (type > T_OBJECT) + ERROR_panic("Operator type analysis not implemented."); + ftype = TYPE_make_simple(type); + + } + + if (nparam) + drop_type(nparam); + + push_type(ftype); +} + + +static void trans_call(short nparam, uint64_t byref) +{ + if (!byref) + { + CODE_call(nparam); + if (_must_drop_vargs) + { + CODE_end_vargs(); + _must_drop_vargs = FALSE; + } + } + else + { + CODE_call_byref(nparam, byref); + if (_must_drop_vargs) + { + CODE_drop_vargs(); + _must_drop_vargs = FALSE; + } + } + + drop_type(nparam + 1); + + push_type_id(T_VARIANT); +} + +static void trans_expr_from_tree(TRANS_TREE *tree, int count) +{ + static void *jump[] = { + &&__CONTINUE, &&__CONTINUE, &&__RESERVED, &&__IDENTIFIER, &&__INTEGER, &&__NUMBER, &&__STRING, &&__TSTRING, &&__CONTINUE, &&__SUBR, &&__CLASS, &&__CONTINUE, &&__CONTINUE + }; + + int i, op; + short nparam; + PATTERN pattern, next_pattern, prev_pattern; + uint64_t byref = 0; + + pattern = NULL_PATTERN; + + #if DEBUG + fprintf(stderr, "-------------------------------------------- %d\n", _type_level); + #endif + + i = 0; + + for (i = 0; i < count; i++) + { + TRANS_tree_index = i; + prev_pattern = pattern; + pattern = tree[i]; + next_pattern = tree[i + 1]; + + goto *jump[PATTERN_type(pattern)]; + + __INTEGER: + + push_integer(PATTERN_signed_index(pattern)); + continue; + + __NUMBER: + + push_number(PATTERN_index(pattern)); + continue; + + __STRING: + + push_string(PATTERN_index(pattern), FALSE); + continue; + + __TSTRING: + + push_string(PATTERN_index(pattern), TRUE); + continue; + + __IDENTIFIER: + + trans_identifier(PATTERN_index(pattern), PATTERN_is_point(pattern), next_pattern); + continue; + + __CLASS: + + trans_class(PATTERN_index(pattern)); + continue; + + __SUBR: + + nparam = get_nparam(tree, count, &i); + trans_subr(PATTERN_index(pattern), nparam); + continue; + + __RESERVED: + + if (PATTERN_is(pattern, RS_TRUE)) + { + CODE_push_boolean(TRUE); + push_type_id(T_BOOLEAN); + } + else if (PATTERN_is(pattern, RS_FALSE)) + { + CODE_push_boolean(FALSE); + push_type_id(T_BOOLEAN); + } + else if (PATTERN_is(pattern, RS_NULL)) + { + CODE_push_null(); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_ME)) + { + /*if (FUNCTION_is_static(JOB->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_me(FALSE); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_SUPER)) + { + /*if (FUNCTION_is_static(JOB->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_super(FALSE); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_LAST)) + { + CODE_push_last(); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_AT)) + { + if (TRANS_popify_last(FALSE)) + THROW("This expression cannot be passed by reference"); + } + else if (PATTERN_is(pattern, RS_COMMA)) + { + CODE_drop(); + drop_type(1); + } + else if (PATTERN_is(pattern, RS_ERROR)) + { + TRANS_subr(TS_SUBR_ERROR, 0); + push_type_id(T_BOOLEAN); + } + else if (PATTERN_is(pattern, RS_OPTIONAL)) + { + CODE_push_void(); + push_type_id(T_VOID); + } + else if (PATTERN_is(pattern, RS_PINF)) + { + CODE_push_inf(FALSE); + push_type_id(T_FLOAT); + } + else if (PATTERN_is(pattern, RS_MINF)) + { + CODE_push_inf(TRUE); + push_type_id(T_FLOAT); + } + else if (PATTERN_is(pattern, RS_3PTS)) + { + _must_drop_vargs = TRUE; + CODE_push_vargs(); + // push_type_id(T_FLOAT); we push no type + } + else + { + op = PATTERN_index(pattern); + if (op == RS_LBRA) + { + nparam = get_nparam_byref(tree, count, &i, &byref); + trans_call(nparam, byref); + } + else + { + nparam = get_nparam(tree, count, &i); + trans_operation((short)op, nparam, prev_pattern); + } + } + + __CONTINUE: + ; + } + + TRANS_tree_index = -1; + _last_type = pop_type(); +} + + +void TRANS_new(void) +{ + int index; + int i, nparam; + bool array = FALSE; + bool event = FALSE; + //bool collection = FALSE; + bool check_param = FALSE; + + nparam = 0; + + if (PATTERN_is_class(*JOB->current)) + { + index = TRANS_get_class(PATTERN_index(*JOB->current)); + //CLASS_add_class(JOB->class, PATTERN_index(*JOB->current)); + if (PATTERN_is(JOB->current[1], RS_LSQR)) + index = CLASS_get_array_class(JOB->class, T_OBJECT, index); + + CODE_push_class(index); + nparam = 1; + } + else if (PATTERN_is_type(*JOB->current)) + { + if (!PATTERN_is(JOB->current[1], RS_LSQR)) + THROW("Cannot instantiate native types"); + + //CODE_push_number(RES_get_type(PATTERN_index(*JOB->current))); + CODE_push_class(CLASS_get_array_class(JOB->class, RES_get_type(PATTERN_index(*JOB->current)), -1)); + nparam = 1; + } + else if (PATTERN_is(*JOB->current, RS_LBRA)) + { + /* NEW ("Class", ...) */ + nparam = 0; + JOB->current--; + check_param = TRUE; + } + else + THROW(E_SYNTAX); + + JOB->current++; + + if (TRANS_is(RS_LSQR)) + { + //if (collection) + // THROW("Array declaration is forbidden with typed collection"); + + if (!PATTERN_is(*JOB->current, RS_RSQR)) + { + for (i = 0;; i++) + { + if (i > MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*JOB->current, RS_RSQR)) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + THROW(E_MISSING, "']'"); + + JOB->current++; + } + } + + JOB->current++; + array = TRUE; + } + else + { + if (TRANS_is(RS_LBRA)) + { + for(;;) + { + if (nparam > MAX_PARAM_FUNC) + THROW("Too many arguments"); + + if (PATTERN_is(*JOB->current, RS_AT) || PATTERN_is(*JOB->current, RS_BYREF)) + THROW("NEW cannot have arguments passed by reference"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*JOB->current, RS_RBRA)) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + THROW(E_MISSING, "')'"); + + JOB->current++; + } + + JOB->current++; + + if (check_param && nparam == 0) + THROW("Not enough argument to New()"); + } + + if (TRANS_is(RS_AS)) + { + TRANS_expression(FALSE); + nparam++; + event = TRUE; + } + + /* + CODE_call(nparam, FALSE); + CODE_drop(); + */ + } + + //if (collection) + // CODE_new(nparam, TRUE, event); + //else + CODE_new(nparam, array, event); +} + + +static void trans_expression(bool check_statement) +{ + TRANS_TREE *tree; + int tree_length; + + if (!check_statement) + { + if (TRANS_is(RS_NEW)) + { + TRANS_new(); + return; + } + else if (TRANS_is(RS_READ)) + { + TRANS_read(); + return; + } + else if (TRANS_is(RS_PEEK)) + { + TRANS_peek(); + return; + } + } + + TRANS_tree(check_statement, &tree, &tree_length); + + JOB->step = JOB_STEP_TREE; + trans_expr_from_tree(tree, tree_length); + JOB->step = JOB_STEP_CODE; + + //FREE(&tree); + + if (check_statement) + { + /* + if (!CODE_check_statement_last()) + THROW("This expression cannot be a statement"); + */ + + CODE_drop(); + } +} + + +void TRANS_ignore_expression() +{ + TRANS_tree(FALSE, NULL, NULL); +} + + +TYPE TRANS_variable_get_type() +{ + TYPE type = TYPE_make_simple(T_VOID); + TRANS_TREE *tree; + int count; + int index; + CLASS_SYMBOL *sym; + + TRANS_tree(FALSE, &tree, &count); + + if (count == 1 && PATTERN_is_identifier(*tree)) + { + index = PATTERN_index(*tree); + sym = CLASS_get_symbol(JOB->class, index); + + if (!TYPE_is_null(sym->local.type)) + { + return sym->local.type; + } + else if (!TYPE_is_null(sym->global.type)) + { + int type = TYPE_get_kind(sym->global.type); + if (type == TK_VARIABLE) + return sym->global.type; + } + } + + //FREE(&tree); + + return type; +} + + +bool TRANS_popify_last(bool no_conv) +{ + if (!CODE_popify_last(no_conv)) + return TRUE; + + if (_last_symbol_used) + { + if (_last_symbol_global) + _last_symbol_used->global_assigned = TRUE; + else + _last_symbol_used->local_assigned = TRUE; + } + + return FALSE; +} + + +void TRANS_reference(void) +{ + TRANS_expression(FALSE); + if (TRANS_popify_last(FALSE)) + THROW("Invalid assignment"); +} + + +void TRANS_reference_type(TYPE type) +{ + TRANS_expression(FALSE); + if (TRANS_popify_last(TYPE_get_id(_last_type) == TYPE_get_id(type))) + THROW("Invalid assignment"); +} + + +bool TRANS_affectation(bool dup) +{ + static TRANS_STATEMENT statement[] = { + //{ RS_NEW, TRANS_new }, + { RS_OPEN, TRANS_open }, + { RS_CLOSE, TRANS_close }, + { RS_SHELL, TRANS_shell }, + { RS_EXEC, TRANS_exec }, + { RS_RAISE, TRANS_raise }, + { RS_PIPE, TRANS_pipe }, + { RS_LOCK, TRANS_lock }, + { RS_MEMORY, TRANS_memory }, + //{ RS_READ, TRANS_read }, + { RS_NONE, NULL } + }; + + TRANS_STATEMENT *st; + PATTERN *look = JOB->current; + PATTERN *left, *expr, *after; + int niv = 0; + bool equal = FALSE; + bool stat = FALSE; + int op = RS_NONE; + int id = RS_NONE; + + for(;;) + { + if (PATTERN_is_newline(*look)) // || PATTERN_is_end(*look)) + break; + + if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR)) + { + niv++; + } + else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR)) + { + if (niv > 0) + niv--; + } + else if (niv == 0) + { + if (PATTERN_is(*look, RS_EQUAL)) + { + equal = TRUE; + op = RS_NONE; + break; + } + else if (PATTERN_is_reserved(*look) && RES_is_assignment(PATTERN_index(*look))) + { + equal = TRUE; + op = RES_get_assignment_operator(PATTERN_index(*look)); + break; + } + } + + look++; + } + + if (!equal || look == JOB->current) + return FALSE; + + left = JOB->current; + *look++ = PATTERN_make(RT_NEWLINE, 0); + expr = look; + + JOB->current = expr; + + if (op == RS_NONE && (PATTERN_is_reserved(*JOB->current))) + { + if (TRANS_is(RS_NEW)) + { + TRANS_in_assignment++; + TRANS_new(); + TRANS_in_assignment--; + id = RS_NEW; + stat = TRUE; + } + else + { + for (st = statement; st->id; st++) + { + if (TRANS_is(st->id)) + { + id = st->id; + TRANS_in_assignment++; + (*st->func)(); + TRANS_in_assignment--; + stat = TRUE; + break; + } + } + } + } + + if (!stat) + { + TYPE type; + + if (op != RS_NONE) + { + JOB->current = left; + trans_expression(FALSE); + type = _last_type; + } + + JOB->current = expr; + trans_expression(FALSE); + after = JOB->current; + + if (op != RS_NONE) + { + push_type(_last_type); + push_type(type); + /*if (op == RS_AMP && COMP_version >= 0x03150000) + trans_operation(op, 1, NULL_PATTERN); + else*/ + trans_operation(op, 2, NULL_PATTERN); + _last_type = pop_type(); + } + } + + after = JOB->current; + + if (dup) + CODE_dup(); + + if (COMP_version >= 0x03070000) + { + if (id == RS_EXEC || id == RS_SHELL) + CODE_dup(); + } + + JOB->current = left; + TRANS_reference_type(_last_type); + + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + if (COMP_version >= 0x03150000 && op == RS_AMP) + CODE_check_fast_cat(); + + JOB->current = after; + + if (COMP_version >= 0x03070000) + { + if (id == RS_EXEC || id == RS_SHELL) + { + TRANS_subr(TS_SUBR_CHECK_EXEC, 1); + CODE_drop(); + } + } + + return TRUE; +} + + +void TRANS_expression(bool check_statement) +{ + _type_level = 0; + trans_expression(check_statement); +} + + +TYPE TRANS_get_last_type(void) +{ + return _last_type; +} diff --git a/main/gbc/gbc_trans_subr.c b/main/gbc/gbc_trans_subr.c new file mode 100644 index 00000000..6cad02ba --- /dev/null +++ b/main/gbc/gbc_trans_subr.c @@ -0,0 +1,1061 @@ +/*************************************************************************** + + gbc_trans_subr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_SUBR_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gb_code.h" +#include "gb_limit.h" + +/*#define DEBUG*/ + + +typedef + struct { + char *name; + SUBR_INFO *info; + } + TRANS_SUBR_INFO; + + +void TRANS_subr(int subr, int nparam) +{ + static TRANS_SUBR_INFO subr_info[] = + { + { ".Print" }, { ".Input" }, { ".Write" }, { ".WriteBytes" }, { ".Read" }, + { ".ReadBytes" }, { ".Open" }, { ".Close" }, { "Seek" }, { ".LineInput" }, + { ".Flush" }, { ".Exec" }, { ".Shell" }, { ".Wait" }, { ".Kill" }, + { ".Move" }, { ".Mkdir" }, { ".Rmdir" }, { ".Array" }, {".Collection" }, + { ".Copy" }, { ".Link" }, { ".Error" }, { ".Lock" }, { ".Unlock" }, + { ".LockWait" }, { ".InputFrom" }, { ".OutputTo" }, { ".Debug" }, { ".Sleep" }, + { ".Randomize" }, { ".ErrorTo" }, { "Left" }, { "Mid" }, { ".OpenMemory" }, + { ".Chmod" }, { ".Chown" }, { ".Chgrp" }, { ".Use" }, { ".CheckExec" }, + { ".MoveKill" }, { ".WaitDelay" }, { ".WaitNext" }, { ".Peek" } + }; + + TRANS_SUBR_INFO *tsi = &subr_info[subr]; + + if (tsi->info == NULL) + { + tsi->info = SUBR_get(tsi->name); + if (!tsi->info) + ERROR_panic("Unknown intern subroutine: %s", tsi->name); + } + + if (subr == TS_SUBR_ARRAY && nparam > MAX_PARAM_OP) + CODE_subr(tsi->info->opcode, MAX_PARAM_OP + 1, CODE_CALL_VARIANT + MAX_PARAM_OP, FALSE); + else if (subr == TS_SUBR_COLLECTION && nparam > MAX_PARAM_OP) + CODE_subr(tsi->info->opcode, MAX_PARAM_OP, CODE_CALL_VARIANT + MAX_PARAM_OP - 1, FALSE); + else + CODE_subr(tsi->info->opcode, nparam, tsi->info->optype, tsi->info->min_param == tsi->info->max_param); +} + + +static bool trans_stream_check(int default_stream, bool check) +{ + if (TRANS_is(RS_SHARP) || default_stream == TS_NONE) + { + TRANS_expression(FALSE); + + if (check) + { + if (PATTERN_is(*JOB->current, RS_COMMA)) + { + JOB->current++; + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + } + else + { + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + } + } + + return FALSE; + } + else + { + //if (default_stream == TS_NONE) + // THROW("Syntax error. &1 expected", "'#'"); + + CODE_push_number(default_stream); + return TRUE; + } +} + +#define trans_stream(_default_stream) trans_stream_check(_default_stream, TRUE) +#define trans_stream_no_check(_default_stream) trans_stream_check(_default_stream, FALSE) + +static void trans_print_debug() +{ + int nparam = 1; + bool semicolon_or_comma = FALSE; + + for(;;) + { + if (PATTERN_is_newline(*JOB->current)) + break; + + TRANS_expression(FALSE); + nparam++; + semicolon_or_comma = FALSE; + + if (PATTERN_is_newline(*JOB->current)) + break; + + if (TRANS_is(RS_SCOLON)) + { + if (TRANS_is(RS_SCOLON)) + { + CODE_push_char(' '); + nparam++; + } + semicolon_or_comma = TRUE; + } + else if (TRANS_is(RS_COMMA)) + { + CODE_push_char('\t'); + nparam++; + semicolon_or_comma = TRUE; + } + else + THROW(E_SYNTAX); + } + + if (!semicolon_or_comma) + { + CODE_push_char('\n'); + nparam++; + } + + TRANS_subr(TS_SUBR_PRINT, nparam); + CODE_drop(); +} + + +void TRANS_print(void) +{ + trans_stream(TS_STDOUT); + trans_print_debug(); +} + + +void TRANS_debug(void) +{ + if (!JOB->debug) + CODE_disable(); + + TRANS_subr(TS_SUBR_DEBUG, 0); + trans_print_debug(); + + if (!JOB->debug) + CODE_enable(); +} + + +void TRANS_error(void) +{ + if (TRANS_is(RS_TO)) + { + if (TRANS_is(RS_DEFAULT)) + CODE_push_null(); + else + trans_stream(TS_NONE); + + TRANS_subr(TS_SUBR_ERROR_TO, 1); + + if (!TRANS_in_assignment) + CODE_drop(); + } + else + { + CODE_push_number(2); // stderr + trans_print_debug(); + } +} + +static int trans_binary_type(void) +{ + int index; + int nparam = 0; + + if (PATTERN_is_class(*JOB->current)) + { + index = CLASS_add_class(JOB->class, PATTERN_index(*JOB->current)); + if (PATTERN_is(JOB->current[1], RS_LSQR)) + index = CLASS_get_array_class(JOB->class, T_OBJECT, index); + + CODE_push_class(index); + } + else if (PATTERN_is_type(*JOB->current)) + { + if (PATTERN_is(JOB->current[1], RS_LSQR)) + { + index = CLASS_get_array_class(JOB->class, RES_get_type(PATTERN_index(*JOB->current)), -1); + CODE_push_class(index); + } + else + { + index = RES_get_type(PATTERN_index(*JOB->current)); + CODE_push_number(index); + } + } + else + THROW(E_SYNTAX); + + JOB->current++; + nparam++; + + #if 0 + if (TRANS_is(RS_LSQR)) + { + TRANS_expression(FALSE); + nparam++; + TRANS_want(RS_RSQR, NULL); + } + else if (TRANS_is(RS_STAR)) + { + if (!string) + THROW("Syntax error"); + TRANS_expression(FALSE); + nparam++; + } + #endif + + return nparam; +} + + +void TRANS_write(void) +{ + trans_stream(TS_STDOUT); + + TRANS_expression(FALSE); + + if (TRANS_is(RS_AS)) + { + trans_binary_type(); + TRANS_subr(TS_SUBR_WRITE, 3); + } + else + { + if (TRANS_is(RS_COMMA)) + TRANS_expression(FALSE); + else + { + if (JOB->no_old_read_syntax) + THROW("Syntax error. &1 expected", "AS"); + CODE_push_number(-1); + } + + TRANS_subr(TS_SUBR_WRITE_BYTES, 3); + } + + CODE_drop(); +} + + +void TRANS_output_to() +{ + TRANS_want(RS_TO, NULL); + + if (TRANS_is(RS_DEFAULT)) + CODE_push_null(); + else + trans_stream(TS_NONE); + + TRANS_subr(TS_SUBR_OUTPUT_TO, 1); + + if (!TRANS_in_assignment) + CODE_drop(); +} + +void TRANS_input_from() +{ + if (TRANS_is(RS_DEFAULT)) + CODE_push_null(); + else + trans_stream(TS_NONE); + + TRANS_subr(TS_SUBR_INPUT_FROM, 1); + + if (!TRANS_in_assignment) + CODE_drop(); +} + + +void TRANS_input(void) +{ + bool stream = TRUE; + + if (TRANS_is(RS_FROM)) + { + TRANS_input_from(); + return; + } + + trans_stream(TS_STDIN); + + for(;;) + { + TRANS_subr(TS_SUBR_INPUT, (stream ? 1 : 0)); + stream = FALSE; + + TRANS_reference(); + + if (PATTERN_is_newline(*JOB->current)) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA) + && !PATTERN_is(*JOB->current, RS_SCOLON)) + THROW(E_SYNTAX); + + JOB->current++; + } +} + + +void TRANS_read_old(void) +{ + PATTERN *save_var; + PATTERN *save_current; + TYPE type; + + if (JOB->no_old_read_syntax) + THROW(E_UNEXPECTED, "READ"); + + trans_stream(TS_STDIN); + + save_var = JOB->current; + type = TRANS_variable_get_type(); + + if (TRANS_is(RS_COMMA)) + { + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_READ_BYTES, 2); + } + else + { + int id = TYPE_get_id(type); + + CODE_push_number(id); + TRANS_subr(TS_SUBR_READ, 2); + } + + save_current = JOB->current; + JOB->current = save_var; + TRANS_reference(); + JOB->current = save_current; +} + + +void TRANS_read(void) +{ + bool def = trans_stream_no_check(TS_STDIN); + + if (TRANS_is(RS_AS)) + { + trans_binary_type(); + TRANS_subr(TS_SUBR_READ, 2); + } + else + { + if (!def) + TRANS_want(RS_COMMA, NULL); + + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_READ_BYTES, 2); + } +} + + +void TRANS_peek(void) +{ + bool def = trans_stream_no_check(TS_STDIN); + + if (!def) + TRANS_want(RS_COMMA, NULL); + + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_PEEK, 2); +} + + +static void TRANS_open_null(void) +{ + int mode = TS_MODE_READ; + + // file name + + CODE_push_null(); + + // open mode + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + } + + CODE_push_number(mode | TS_MODE_NULL); + + TRANS_subr(TS_SUBR_OPEN, 2); +} + + +static void TRANS_open_string(void) +{ + int mode = TS_MODE_READ | TS_MODE_DIRECT; + + // file name + + if (!PATTERN_is(*JOB->current, RS_FOR) && !PATTERN_is_newline(*JOB->current)) + TRANS_expression(FALSE); + else + CODE_push_null(); + + // open mode + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + } + + CODE_push_number(mode | TS_MODE_STRING); + + TRANS_subr(TS_SUBR_OPEN, 2); +} + + +void TRANS_open(void) +{ + int mode = TS_MODE_READ; + + if (TRANS_is(RS_PIPE)) + { + TRANS_pipe(); + return; + } + else if (TRANS_is(RS_MEMORY)) + { + TRANS_memory(); + return; + } + else if (TRANS_is(RS_STRING)) + { + TRANS_open_string(); + return; + } + else if (TRANS_is(RS_NULL)) + { + TRANS_open_null(); + return; + } + + // file name + + TRANS_expression(FALSE); + + // open mode + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + else if (TRANS_is(RS_INPUT)) + mode |= TS_MODE_READ; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + else if (TRANS_is(RS_OUTPUT)) + mode |= TS_MODE_WRITE; + + if (TRANS_is(RS_CREATE)) + mode |= TS_MODE_CREATE; + else if (TRANS_is(RS_APPEND)) + mode |= TS_MODE_APPEND; + + if (TRANS_is(RS_WATCH)) + mode |= TS_MODE_WATCH; + + /*if (TRANS_is(RS_BIG)) + mode |= TS_MODE_BIG; + else if (TRANS_is(RS_LITTLE)) + mode |= TS_MODE_LITTLE;*/ + + /*JOB->current--; + if (PATTERN_is(*JOB->current, RS_FOR)) + THROW("Syntax error in file open mode"); + JOB->current++;*/ + } + + CODE_push_number(mode); + + TRANS_subr(TS_SUBR_OPEN, 2); +} + + +void TRANS_pipe(void) +{ + int mode = TS_MODE_READ; + + // file name + + TRANS_expression(FALSE); + + // open mode + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + //else if (TRANS_is(RS_INPUT)) + // mode |= TS_MODE_READ; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + //else if (TRANS_is(RS_OUTPUT)) + // mode |= TS_MODE_WRITE; + + if (TRANS_is(RS_WATCH)) + mode |= TS_MODE_WATCH; + } + + CODE_push_number(mode | TS_MODE_PIPE); + + TRANS_subr(TS_SUBR_OPEN, 2); +} + + +void TRANS_memory(void) +{ + int mode = TS_MODE_READ; + + /* Memory address */ + + TRANS_expression(FALSE); + + /* Open mode */ + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + } + + CODE_push_number(mode); + + TRANS_subr(TS_SUBR_OPEN_MEMORY, 2); +} + + +void TRANS_close(void) +{ + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + TRANS_ignore(RS_SHARP); + TRANS_expression(FALSE); + + TRANS_subr(TS_SUBR_CLOSE, 1); + + if (!TRANS_in_assignment) + CODE_drop(); +} + + +void TRANS_lock(void) +{ + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + if (!TRANS_in_assignment) + THROW("Useless LOCK"); + + TRANS_expression(FALSE); + + if (TRANS_is(RS_WAIT)) + { + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_LOCK_WAIT, 2); + } + else + TRANS_subr(TS_SUBR_LOCK, 1); +} + + +void TRANS_unlock(void) +{ + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + TRANS_ignore(RS_SHARP); + TRANS_expression(FALSE); + + TRANS_subr(TS_SUBR_UNLOCK, 1); + CODE_drop(); +} + + +void TRANS_seek(void) +{ + int nparam; + + trans_stream(TS_NONE); + + TRANS_expression(FALSE); + nparam = 2; + + /* + if (TRANS_is(RS_COMMA)) + { + TRANS_expression(FALSE); + nparam++; + } + */ + + TRANS_subr(TS_SUBR_SEEK, nparam); + CODE_drop(); +} + + +void TRANS_line_input(void) +{ + if (TRANS_is(RS_INPUT)) + { + trans_stream(TS_STDIN); + TRANS_subr(TS_SUBR_LINE_INPUT, 1); + TRANS_reference(); + } + else + THROW(E_SYNTAX); +} + + +void TRANS_flush(void) +{ + trans_stream(TS_STDOUT); + TRANS_subr(TS_SUBR_FLUSH, 1); + CODE_drop(); +} + + +void TRANS_quit(void) +{ + if (PATTERN_is_newline(*JOB->current)) + { + CODE_quit(FALSE); + } + else + { + TRANS_expression(FALSE); + CODE_quit(TRUE); + } + +} + +void TRANS_randomize(void) +{ + if (PATTERN_is_newline(*JOB->current)) + { + TRANS_subr(TS_SUBR_RANDOMIZE, 0); + } + else + { + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_RANDOMIZE, 1); + } + + CODE_drop(); +} + +static void trans_exec_shell(bool shell) +{ + int mode = TS_EXEC_NONE; + bool wait; + bool as = TRUE; + PATTERN *dest = NULL; + PATTERN *save; + + TRANS_expression(FALSE); + + if (TRANS_is(RS_WITH)) + TRANS_expression(FALSE); + else + CODE_push_null(); + + wait = TRANS_is(RS_WAIT); + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_EXEC_READ; + if (TRANS_is(RS_WRITE)) + mode |= TS_EXEC_WRITE; + + if (mode == 0) + { + mode |= TS_EXEC_TERM; + if (TRANS_is(RS_INPUT)) + mode |= TS_EXEC_READ; + if (TRANS_is(RS_OUTPUT)) + mode |= TS_EXEC_WRITE; + } + } + else + { + if (TRANS_is(RS_TO)) + { + if (TRANS_in_assignment) + THROW("Syntax error. Cannot use this syntax in assignment"); + + mode = TS_EXEC_STRING; + wait = TRUE; + as = FALSE; + + dest = JOB->current; + TRANS_ignore_expression(); + + if (TRANS_is(RS_WITH) && TRANS_is(RS_ERROR)) + mode += TS_EXEC_ERROR; + } + } + + if (wait) mode |= TS_EXEC_WAIT; + CODE_push_number(mode); + + if (as && TRANS_is(RS_AS)) + TRANS_expression(FALSE); + else + CODE_push_null(); + + TRANS_subr(shell ? TS_SUBR_SHELL : TS_SUBR_EXEC, 4); + + if (dest) + { + save = JOB->current; + JOB->current = dest; + TRANS_reference(); + JOB->current = save; + } + else if (!TRANS_in_assignment) + CODE_drop(); +} + + +void TRANS_exec(void) +{ + trans_exec_shell(FALSE); +} + +void TRANS_shell(void) +{ + trans_exec_shell(TRUE); +} + + +void TRANS_wait(void) +{ + if (TRANS_is(RS_NEXT)) + TRANS_subr(TS_SUBR_WAIT_NEXT, 0); + else if (!PATTERN_is_newline(*JOB->current)) + { + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_WAIT_DELAY, 1); + } + else + TRANS_subr(TS_SUBR_WAIT, 0); + + CODE_drop(); +} + + +void TRANS_sleep(void) +{ + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_SLEEP, 1); + CODE_drop(); +} + + +void TRANS_kill(void) +{ + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_KILL, 1); + CODE_drop(); +} + +void TRANS_move(void) +{ + int subr; + + TRANS_expression(FALSE); + if (TRANS_is(RS_TO)) + subr = TS_SUBR_MOVE; + else if (TRANS_is(RS_DOWNTO) || TRANS_is(RS_KILL)) + subr = TS_SUBR_MOVE_KILL; + else + THROW_UNEXPECTED(JOB->current); + + TRANS_expression(FALSE); + TRANS_subr(subr, 2); + CODE_drop(); +} + +void TRANS_copy(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_COPY, 2); + CODE_drop(); +} + +void TRANS_link(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_LINK, 2); + CODE_drop(); +} + +void TRANS_chmod(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_CHMOD, 2); + CODE_drop(); +} + +void TRANS_chown(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_CHOWN, 2); + CODE_drop(); +} + +void TRANS_chgrp(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_CHGRP, 2); + CODE_drop(); +} + +void TRANS_inc(void) +{ + PATTERN *save = JOB->current; + + TRANS_expression(FALSE); + CODE_push_number(1); + CODE_op(C_ADD, 0, 2, TRUE); + + JOB->current = save; + TRANS_reference(); +} + +void TRANS_dec(void) +{ + PATTERN *save = JOB->current; + + TRANS_expression(FALSE); + CODE_push_number(1); + CODE_op(C_SUB, 0, 2, TRUE); + + JOB->current = save; + TRANS_reference(); +} + + +void TRANS_swap(void) +{ + PATTERN *sa; + PATTERN *sb; + PATTERN *current; + + sa = JOB->current; + TRANS_expression(FALSE); + + TRANS_want(RS_COMMA, "Comma"); + + sb = JOB->current; + TRANS_expression(FALSE); + + current = JOB->current; + + JOB->current = sa; + TRANS_reference(); + + JOB->current = sb; + TRANS_reference(); + + JOB->current = current; +} + +void TRANS_mkdir(void) +{ + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_MKDIR, 1); + CODE_drop(); +} + +void TRANS_rmdir(void) +{ + TRANS_expression(FALSE); + TRANS_subr(TS_SUBR_RMDIR, 1); + CODE_drop(); +} + +void TRANS_mid(void) +{ + PATTERN *str; + PATTERN *pos; + PATTERN *len; + PATTERN *save; + + TRANS_want(RS_LBRA, "Left bracket"); + + str = JOB->current; + TRANS_expression(FALSE); + + TRANS_want(RS_COMMA, "Comma"); + + pos = JOB->current; + TRANS_expression(FALSE); + CODE_push_number(1); + CODE_op(C_SUB, 0, 2, TRUE); + TRANS_subr(TS_SUBR_LEFT, 2); + + if (TRANS_is(RS_COMMA)) + { + len = JOB->current; + TRANS_ignore_expression(); + } + else + { + len = NULL; + } + + TRANS_want(RS_RBRA, "Right bracket"); + TRANS_want(RS_EQUAL, "Equal"); + + TRANS_expression(FALSE); + + save = JOB->current; + + if (len) + { + JOB->current = str; + TRANS_expression(FALSE); + JOB->current = pos; + TRANS_expression(FALSE); + JOB->current = len; + TRANS_expression(FALSE); + CODE_op(C_ADD, 0, 2, TRUE); + TRANS_subr(TS_SUBR_MID, 2); + } + + CODE_op(C_CAT, 0, len ? 3 : 2, FALSE); + + JOB->current = str; + TRANS_reference(); + + JOB->current = save; +} + + +void TRANS_poke(void) +{ + int index; + + index = PATTERN_index(*JOB->current); + JOB->current++; + + TRANS_want(RS_LBRA, "Left bracket"); + TRANS_expression(FALSE); + TRANS_want(RS_RBRA, "Right bracket"); + + TRANS_want(RS_EQUAL, "Equal"); + + TRANS_expression(FALSE); + + CODE_op(C_POKE, COMP_subr_info[index].optype, 2, TRUE); + CODE_drop(); +} + + +#if 0 +void TRANS_scan(void) +{ + PATTERN *save; + int noutput = 0; + + TRANS_expression(FALSE); + TRANS_want(RS_WITH, NULL); + TRANS_expression(FALSE); + + TRANS_want(RS_TO, NULL); + + save = JOB->current; + for(;;) + { + TRANS_expression(FALSE); + noutput++; + if (!TRANS_is(RS_COMMA)) + break; + } + JOB->current = save; + + trans_subr_output(TSO_SUBR_SCAN, 2 + noutput, noutput); + + for(;;) + { + TRANS_reference(); + if (!TRANS_is(RS_COMMA)) + break; + } +} +#endif diff --git a/main/gbc/gbc_trans_tree.c b/main/gbc/gbc_trans_tree.c new file mode 100644 index 00000000..484fc4fb --- /dev/null +++ b/main/gbc/gbc_trans_tree.c @@ -0,0 +1,882 @@ +/*************************************************************************** + + gbc_trans_tree.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TRANS_TREE_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" + +#include "gbc_trans.h" +#include "gb_code.h" +#include "gbc_read.h" + +//#define DEBUG + +#define BYREF_TEST(_byref, _n) (_byref & ((uintptr_t)1 << _n)) +#define BYREF_SET(_byref, _n) _byref |= ((uintptr_t)1 << _n) + +int TRANS_tree_index = -1; + +static short _level; +static PATTERN *_current; +static TRANS_TREE _tree[MAX_EXPR_PATTERN]; +static int _tree_pos[MAX_EXPR_PATTERN]; +static int _tree_length = 0; +static int _tree_line = 0; +static int _tree_current_line = 0; + +static void analyze_expr(short priority, short op_main); +static void analyze_array(); + + +static void THROW_EXPR_TOO_COMPLEX() +{ + THROW("Expression too complex"); +} + +static void inc_level() +{ + _level++; + if (_level > MAX_EXPR_LEVEL) + THROW_EXPR_TOO_COMPLEX(); +} + + +static void dec_level() +{ + _level--; +} + +/*#define add_pattern(_pattern) \ +do { \ + if (_tree_length >= MAX_EXPR_PATTERN) \ + THROW_EXPR_TOO_COMPLEX(); \ + _tree_pos[_tree_length] = COMPILE_get_column(_current); \ + _tree[_tree_length] = (_pattern); \ + _tree_length++; \ +} while (0)*/ + +static inline void add_pattern(PATTERN pattern) +{ + if (_tree_length >= MAX_EXPR_PATTERN) + THROW_EXPR_TOO_COMPLEX(); + _tree_pos[_tree_length] = COMPILE_get_column(_current); + if (JOB->line != _tree_current_line) + { + _tree_current_line = JOB->line; + _tree_pos[_tree_length] = -_tree_pos[_tree_length]; + } + _tree[_tree_length] = pattern; + _tree_length++; +} + + +static void remove_last_pattern() +{ + if (_tree_length == 0) + return; + + _tree_length--; +} + + +static PATTERN get_last_pattern(int dep) +{ + if (_tree_length < dep) + return NULL_PATTERN; + else + return _tree[_tree_length - dep]; +} + + +static void change_last_pattern(int dep, PATTERN pattern) +{ + if (_tree_length < dep) + return; + _tree[_tree_length - dep] = pattern; +} + + +static void check_last_first(int dep) +{ + if (PATTERN_is_identifier(get_last_pattern(dep))) + change_last_pattern(dep, PATTERN_set_flag(get_last_pattern(dep), RT_FIRST)); +} + + +static void add_reserved_pattern(int reserved) +{ + add_pattern(PATTERN_make(RT_RESERVED, reserved)); +} + + +static void add_operator(short op, short nparam) +{ + PATTERN pattern; + + /* + Why this test? + + add_operator() can be called without operator. See: + if (RES_priority(op) < prio) ... + */ + + if (op == RS_NONE || op == RS_UNARY) + return; + + if (op == RS_EXCL) + { + op = RS_LSQR; + nparam = 2; + + check_last_first(2); + } + + pattern = PATTERN_make(RT_RESERVED, op); + + //if (op == RS_LBRA && byref) + // pattern = PATTERN_set_flag(pattern, RT_OUTPUT); + + add_pattern(pattern); + + add_pattern(PATTERN_make(RT_PARAM, nparam)); +} + +static void add_operator_output(short nparam, uint64_t byref) +{ + PATTERN pattern; + + pattern = PATTERN_make(RT_RESERVED, RS_LBRA); + + add_pattern(pattern); + + add_pattern(PATTERN_make(RT_PARAM, nparam)); + + if (byref) + { + while (byref) + { + add_pattern(PATTERN_make(RT_PARAM, byref & 0xFFFF)); + byref >>= 16; + } + } +} + + +static void add_subr(PATTERN subr_pattern, short nparam) +{ + PATTERN pattern; + + //if (has_output) + // subr_pattern = PATTERN_set_flag(subr_pattern, RT_OUTPUT); + + add_pattern(subr_pattern); + + pattern = PATTERN_make(RT_PARAM, nparam); + add_pattern(pattern); +} + +static bool is_statement(void) +{ + PATTERN last; + int count; + + count = _tree_length; + + if (count == 0) + return FALSE; + + count--; + last = _tree[count]; + + while (PATTERN_is_param(last) && (count > 0)) + { + count--; + last = _tree[count]; + } + + if (PATTERN_is(last, RS_PT)) + goto _ADD_BRACE; + + if (PATTERN_is_identifier(last)) + { + if (count == 0) + goto _ADD_BRACE; + + if (count >= 2) + { + last = _tree[count - 1]; + if (PATTERN_is_param(last) && (PATTERN_index(last) == 0) + && PATTERN_is(_tree[count - 2], RS_PT)) + goto _ADD_BRACE; + } + } + else if (PATTERN_is_subr(last) + || PATTERN_is(last, RS_LBRA) + || PATTERN_is(last, RS_RBRA) + || PATTERN_is(last, RS_AT) + || PATTERN_is(last, RS_COMMA)) + return TRUE; + + #ifdef DEBUG + printf("Last = "); + READ_dump_pattern(&last); + printf("%08X %08X %d\n", last, PATTERN_make(RT_RESERVED, RS_LBRA), PATTERN_is(last, RS_LBRA)); + #endif + + return FALSE; + +_ADD_BRACE: + + add_operator(RS_LBRA, 0); + #ifdef DEBUG + printf("Add ()\n"); + #endif + return TRUE; +} + +static void analyze_make_array() +{ + int n = 0; + bool checked = FALSE; + bool collection = FALSE; + + /*if (PATTERN_is(*_current, RS_RSQR)) + { + _current++; + add_pattern(PATTERN_make(RT_RESERVED, RS_NULL)); + return; + }*/ + + if (!PATTERN_is(*_current, RS_RSQR)) + { + for(;;) + { + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + + if (!checked) + { + collection = PATTERN_is(*_current, RS_COLON); + checked = TRUE; + } + + if (collection) + { + if (!PATTERN_is(*_current, RS_COLON)) + THROW(E_MISSING, "':'"); + _current++; + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + } + + if (!PATTERN_is(*_current, RS_COMMA)) + break; + + _current++; + + if (collection) + { + if (n == (MAX_PARAM_OP - 1)) + { + add_operator(RS_COLON, MAX_PARAM_OP + 1); + n = 0; + } + } + else + { + if (n == MAX_PARAM_OP) + { + add_operator(RS_RSQR, MAX_PARAM_OP + 1); + n = 0; + } + } + } + } + + while (PATTERN_is_newline(*_current)) + { + add_pattern(PATTERN_make(RT_NEWLINE, 0)); + JOB->line++; + _current++; + } + + if (!PATTERN_is(*_current, RS_RSQR)) + THROW(E_MISSING, "']'"); + _current++; + + add_operator(collection ? RS_COLON : RS_RSQR, n); +} + + +static void analyze_single(int op) +{ + PATTERN *pattern; + bool jump_newline; + + jump_newline = PATTERN_is_newline(*_current); + if (jump_newline) + { + add_pattern(PATTERN_make(RT_NEWLINE, 0)); + JOB->line++; + _current++; + } + + if (op == RS_PT && !PATTERN_is_identifier(*_current)) + THROW("The '.' operator must be followed by an identifier"); + else if (op == RS_EXCL && !PATTERN_is_string(*_current)) + THROW("The '!' operator must be followed by an identifier"); + + /* ( expr ) */ + + if (PATTERN_is(*_current, RS_LBRA)) + { + int old_length = _tree_length; + PATTERN last; + + _current++; + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*_current, RS_RBRA)) + THROW(E_MISSING, "')'"); + _current++; + + if (_tree_length == (old_length + 1)) + { + last = get_last_pattern(1); + if (PATTERN_is_string(last)) + change_last_pattern(1, PATTERN_make(RT_TSTRING, PATTERN_index(last))); + } + } + + /* [ expr, expr, ... ] */ + + else if (PATTERN_is(*_current, RS_LSQR)) + { + _current++; + analyze_make_array(); + } + + /* - expr | NOT expr */ + + else if (PATTERN_is(*_current, RS_MINUS) || PATTERN_is(*_current, RS_NOT)) + { + pattern = _current; + _current++; + + analyze_expr(RES_priority(RS_NOT), RS_UNARY); + add_operator(PATTERN_index(*pattern), 1); + } + + // . symbol + + else if (PATTERN_is(*_current, RS_PT) && PATTERN_is_identifier(_current[1])) + { + add_operator(PATTERN_index(_current[0]), 0); + add_pattern(PATTERN_set_flag(_current[1], RT_POINT)); + add_operator(PATTERN_index(_current[0]), 2); + _current += 2; + } + + // . [ ... ] + + else if (PATTERN_is(*_current, RS_PT) && PATTERN_is(_current[1], RS_LSQR)) + { + add_operator(PATTERN_index(_current[0]), 0); + //add_pattern(PATTERN_set_flag(RS_RSQR, RT_POINT)); + _current += 2; + analyze_array(); + } + + // ! symbol + + else if (PATTERN_is(*_current, RS_EXCL) && PATTERN_is_string(_current[1])) + { + add_operator(RS_PT, 0); + add_pattern(PATTERN_set_flag(_current[1], RT_POINT)); + add_operator(RS_EXCL, 0); + _current += 2; + } + + /* NULL, TRUE, FALSE, ME, PARENT, LAST, ERROR */ + /* integer, number, string or symbol */ + + else if (PATTERN_is(*_current, RS_NULL) + || PATTERN_is(*_current, RS_ME) + || PATTERN_is(*_current, RS_LAST) + || PATTERN_is(*_current, RS_TRUE) + || PATTERN_is(*_current, RS_FALSE) + || PATTERN_is(*_current, RS_PINF) + || PATTERN_is(*_current, RS_MINF) + || PATTERN_is(*_current, RS_ERROR) + || (!PATTERN_is_reserved(*_current) && !PATTERN_is_newline(*_current) && !PATTERN_is_end(*_current))) + { + add_pattern(*_current); + + if (PATTERN_is_identifier(*_current)) + { + /*if ((op == RS_NONE || op == RS_UNARY) && (PATTERN_is_identifier(*_current))) + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_FIRST));*/ + if (op == RS_PT) + { + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_POINT)); + check_last_first(2); + } + } + + _current++; + } + + else if (PATTERN_is(*_current, RS_SUPER)) + { + add_pattern(*_current); + _current++; + if (!PATTERN_is(*_current, RS_PT) + && !PATTERN_is(*_current, RS_EXCL) + && !PATTERN_is(*_current, RS_LBRA) + && !PATTERN_is(*_current, RS_LSQR)) + THROW("SUPER cannot be used alone"); + } + + else + { + if (jump_newline) + { + _current--; + JOB->line--; + } + + THROW_UNEXPECTED(_current); + } +} + + +static void analyze_call() +{ + static PATTERN *byref_pattern[MAX_PARAM_FUNC]; + + int i, nparam_post = 0; + PATTERN subr_pattern = NULL_PATTERN; + PATTERN last_pattern = get_last_pattern(1); + SUBR_INFO *info; + bool optional = TRUE; + uint64_t byref = 0; + PATTERN *save; + + /* + get_pattern_subr(last_pattern, &subr); + */ + if (PATTERN_is_subr(last_pattern)) + { + subr_pattern = last_pattern; + remove_last_pattern(); + optional = FALSE; + } + else if (PATTERN_is_identifier(last_pattern)) + { + check_last_first(1); + } + else if (PATTERN_is_string(last_pattern) || PATTERN_is_integer(last_pattern) || PATTERN_is_number(last_pattern)) + THROW(E_SYNTAX); + + /* N.B. Le cas où last_pattern = "." n'a pas de test spécifique */ + + if (PATTERN_type(subr_pattern) == RT_SUBR && PATTERN_index(subr_pattern) == SUBR_VarPtr) + { + if (!PATTERN_is_identifier(_current[0]) || !PATTERN_is(_current[1], RS_RBRA)) + THROW("Syntax error. VarPtr() takes only one identifier"); + + add_pattern(*_current); + _current += 2; + add_subr(subr_pattern, 1); + } + else + { + for (;;) + { + if (PATTERN_is(*_current, RS_RBRA)) + { + _current++; + break; + } + + if (nparam_post > 0) + { + if (!PATTERN_is(*_current, RS_COMMA)) + THROW(E_MISSING, "',' or ')'"); + _current++; + } + + #if 0 + if (FALSE) /*(PATTERN_is(*_current, RS_AMP))*/ + { + _current++; + output[nparam_post] = _current; + has_output = TRUE; + } + else + { + output[nparam_post] = NULL; + } + #endif + + if (optional && (PATTERN_is(*_current, RS_COMMA) || PATTERN_is(*_current, RS_RBRA))) + { + add_reserved_pattern(RS_OPTIONAL); + } + else if (optional && PATTERN_is(*_current, RS_3PTS) && PATTERN_is(_current[1], RS_RBRA)) + { + _current++; + add_reserved_pattern(RS_3PTS); + nparam_post--; + } + else + { + if (PATTERN_is(*_current, RS_AT) || PATTERN_is(*_current, RS_BYREF)) + { + _current++; + BYREF_SET(byref, nparam_post); + byref_pattern[nparam_post] = _current; + } + + analyze_expr(0, RS_NONE); + } + + nparam_post++; + + if (nparam_post >= MAX_PARAM_FUNC) + THROW("Too many arguments"); + } + + last_pattern = get_last_pattern(1); + if (PATTERN_is(last_pattern, RS_OPTIONAL)) + THROW("Syntax error. Needless arguments"); + + /* + while (nparam_post > 0) + { + if (get_last_pattern(1) != PATTERN_make(RT_RESERVED, RS_OPTIONAL)) + break; + + remove_last_pattern(); + nparam_post--; + } + */ + + if (PATTERN_is_null(subr_pattern)) + { + add_operator_output(nparam_post, byref); + + save = _current; + + for (i = nparam_post - 1; i >= 0; i--) + { + if (BYREF_TEST(byref, i)) + { + _current = byref_pattern[i]; + analyze_expr(0, RS_NONE); + //if (!is_statement()) + // THROW("The &1 argument cannot be passed by reference", TRANS_get_num_desc(i + 1)); + add_pattern(PATTERN_make(RT_RESERVED, RS_AT)); + } + } + + _current = save; + } + else + { + info = &COMP_subr_info[PATTERN_index(subr_pattern)]; + + if (nparam_post < info->min_param) + THROW("Not enough arguments to &1()", info->name); + else if (nparam_post > info->max_param) + THROW("Too many arguments to &1()", info->name); + else if (byref) + THROW("Subroutine arguments cannot be passed by reference"); + + add_subr(subr_pattern, nparam_post); + } + } + +} + + +static void analyze_array() +{ + int i; + + check_last_first(1); + + for(i = 0; i < MAX_ARRAY_DIM; i++) + { + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*_current, RS_COMMA)) + break; + + _current++; + } + + if (!PATTERN_is(*_current, RS_RSQR)) + THROW(E_MISSING, "',' or ')'"); + _current++; + + add_operator(RS_LSQR, i + 2); +} + + +static void analyze_expr(short priority, short op_main) +{ + short op, op_curr, op_not; + short prio; + short nparam; + + inc_level(); + + op_curr = op_main; + op_not = RS_NONE; + nparam = (op_main == RS_NONE || op_main == RS_UNARY) ? 0 : 1; + + if (PATTERN_is(*_current, RS_NEW)) + THROW("Cannot use NEW operator here"); + +READ_OPERAND: + + //analyze_expr_check_first(op_curr); + + analyze_single(op_curr); + nparam++; + + if (nparam > MAX_PARAM_OP) + THROW("Expression too complex. Too many operands"); + +READ_OPERATOR: + + if (!PATTERN_is_reserved(*_current)) + goto OPERATOR_END; + + op = PATTERN_index(*_current); + + if (!RES_is_operator(op)) + goto OPERATOR_END; + + if (op == RS_AND || op == RS_OR) + if (PATTERN_is(_current[1], RS_IF)) + goto OPERATOR_END; + + _current++; + + if (op == RS_NOT && PATTERN_is_reserved(*_current)) + { + op_not = PATTERN_index(*_current); + if (RES_is_operator(op_not) && RES_can_have_not_before(op_not)) + { + op = op_not + 1; + _current++; + } + } + + if (priority) + prio = priority; + else if (op_curr == RS_NONE) + prio = 0; + else + prio = RES_priority(op_curr); + + if (op_curr == RS_NONE) + { + if (RES_is_binary(op) || RES_is_n_ary(op)) + { + op_curr = op; + goto READ_OPERAND; + } + } + + if (op_curr == op) + { + if (!(RES_is_binary(op) && nparam == 2)) + goto READ_OPERAND; + } + + if (RES_priority(op) > prio) + { + if (op == RS_LSQR) + analyze_array(); + else if (op == RS_LBRA) + analyze_call(); + else + analyze_expr(RES_priority(op), op); + + goto READ_OPERATOR; + } + + if (RES_priority(op) == prio) + { + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + goto READ_OPERATOR; + } + else + { + if (RES_is_only(op_curr) || RES_is_only(op)) + THROW("Ambiguous expression. Please use brackets"); + + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + if (RES_priority(op) < prio) + { + if ((op_main != RS_NONE) || (priority > 0)) + { + add_operator(op_curr, nparam); + _current--; + if (op_not != RS_NONE) + _current--; + goto END; + } + + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else + { + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + dec_level(); + return; + +OPERATOR_END: + + add_operator(op_curr, nparam); + +END: + + dec_level(); + return; + +} + + +void TRANS_tree(bool check_statement, TRANS_TREE **result, int *count) +{ + #ifdef DEBUG + int i; + #endif + + _tree_length = 0; + _tree_line = _tree_current_line = JOB->line; + _current = JOB->current; + _level = 0; + + TRY + { + analyze_expr(0, RS_NONE); + JOB->current = _current; + } + CATCH + { + JOB->current = _current; + PROPAGATE(); + } + END_TRY + + #ifdef DEBUG + printf("\n"); + for (i = 0; i < _tree_length; i++) + { + printf("[% 4d] ", i); + READ_dump_pattern(&_tree[i]); + } + #endif + + if (check_statement && (!is_statement())) + THROW("This expression cannot be a statement"); + + if (result) + { + add_pattern(NULL_PATTERN); + //ALLOC(result, sizeof(PATTERN) * _tree_length); + //memcpy(*result, _tree, sizeof(PATTERN) * _tree_length); + *result = _tree; + *count = _tree_length - 1; + } +} + +int TRANS_get_column(int *pline) +{ + int i; + int line; + int col; + + if (TRANS_tree_index < 0) + { + *pline = -1; + return -1; + } + + line = _tree_line; + col = _tree_pos[0]; + for (i = 1; i <= TRANS_tree_index; i++) + { + col = _tree_pos[i]; + if (col < 0) + line++; + } + + *pline = line; + return abs(col); +} + diff --git a/main/gbc/gbc_type.c b/main/gbc/gbc_type.c new file mode 100644 index 00000000..c6afec54 --- /dev/null +++ b/main/gbc/gbc_type.c @@ -0,0 +1,219 @@ +/*************************************************************************** + + gbc_type.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TYPE_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_type.h" + +#include "gbc_class.h" +#include "gbc_compile.h" + +typedef + struct { + char prefix1; + char prefix2; + } + TYPE_CHECK_PREFIX; + + +const char *TYPE_name[] = +{ + "Void", "Boolean", "Byte", "Short", "Integer", "Long", "Single", "Float", "Date", + "String", "CString", "Variant", "Array", "Pointer", "Class", "Null", + "Object" +}; + + +#if 0 +size_t TYPE_sizeof(TYPE type) +{ + TYPE_ID id = TYPE_get_id(type); + + switch(id) + { + case T_BOOLEAN: + return 1; + + case T_BYTE: + return 1; + + case T_SHORT: + return 2; + + case T_INTEGER: + return 4; + + case T_LONG: + return 8; + + case T_SINGLE: + return 4; + + case T_FLOAT: + return 8; + + case T_DATE: + return 8; + + case T_STRING: + return 4; + + case T_VARIANT: + return 12; + + case T_OBJECT: + return 4; + + case T_POINTER: + return 4; + + case T_ARRAY: + { + int i; + size_t size; + CLASS_ARRAY *array = &JOB->class->array[TYPE_get_value(type)]; + + size = 1; + for (i = 0; i < array->ndim; i++) + size *= array->dim[i]; + + size *= TYPE_sizeof(array->type); + + return (size + 3) & ~3; + } + + default: + ERROR_panic("TYPE_sizeof: bad type id"); + } +} +#endif + + +const char *TYPE_get_short_desc(TYPE type) +{ + static const char *name[] = { + "", "b", "i", "i", "i", "l", "g", "f", + "d", "s", "s", "p", "v", "?", "?", "?", + "o" + }; + + TYPE_ID id; + + id = TYPE_get_id(type); + + if (id == T_ARRAY) + return "?"; + else + return name[id]; +} + + +char *TYPE_get_desc(TYPE type) +{ + static char buf[256]; + + TYPE_ID id; + int value; + CLASS_SYMBOL *sym; + + id = TYPE_get_id(type); + value = TYPE_get_value(type); + + if (id == T_ARRAY) + { + strcpy(buf, TYPE_name[JOB->class->array[value].type.t.id]); + strcat(buf, "[]"); + } + else if (id == T_OBJECT) + { + if (value == -1) + strcpy(buf, "Object"); + else + { + sym = CLASS_get_symbol(JOB->class, JOB->class->class[value].index); + sprintf(buf, "%.*s", sym->symbol.len, sym->symbol.name); + } + } + else + { + strcpy(buf, TYPE_name[id]); + } + + return buf; +} + + +bool TYPE_check_prefix(TYPE type, const char *prefix, int len) +{ + static TYPE_CHECK_PREFIX _check[] = { + { 0 }, // T_VOID + { 'b' }, // T_BOOLEAN + { 'i', 'n' }, // T_BYTE + { 'i', 'n' }, // T_SHORT + { 'i', 'n' }, // T_INTEGER + { 'i', 'n' }, // T_LONG + { 'f' }, // T_SINGLE + { 'f' }, // T_FLOAT + { 'd' }, // T_DATE + { 's' }, // T_STRING + { 's' }, // T_CSTRING + { 'p' }, // T_POINTER + { 'v' }, // T_VARIANT + { 'a' }, // T_ARRAY, + { 0 }, // T_STRUCT, + { 0 } + }; + + if (len == 0) // no prefix + return FALSE; + + if (type.t.id < T_OBJECT) + { + if (len != 1) + return TRUE; + + if (*prefix != _check[type.t.id].prefix1 && *prefix != _check[type.t.id].prefix2) + return TRUE; + + return FALSE; + } + + if (len == 1 && *prefix == 'h') + return FALSE; + + char *class_name = TYPE_get_desc(type); + + if (strcasecmp(class_name, "collection") == 0) + return len != 1 || *prefix != 'c'; + + if (strcasecmp(class_name, "result") == 0) + return len != 1 || *prefix != 'r'; + + return FALSE; +} diff --git a/main/gbc/gbc_type.h b/main/gbc/gbc_type.h new file mode 100644 index 00000000..8a10e246 --- /dev/null +++ b/main/gbc/gbc_type.h @@ -0,0 +1,115 @@ +/*************************************************************************** + + gbc_type.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_TYPE_H +#define __GBC_TYPE_H + +#define PROJECT_COMP +#include "gb_type_common.h" + +/*************************************************** + + Format d'un Type + + F K T T X X X X + + F : Flags (TF_*) + K : Kind (TK_*) + T : Type (T_*) + X : index, pour T_OBJECT, T_ARRAY, T_STRUCT + +***************************************************/ + +/* +typedef + ulong TYPE; +*/ + +typedef + unsigned char TYPE_ID; + +typedef + union { + struct { + unsigned char flag; + TYPE_ID id; + short value; + } t; + int l; + } + TYPE; + +typedef + struct { + int value; + TYPE type; + } + VALUE; + +#ifndef __GBC_TYPE_C +EXTERN char *TYPE_name[]; +#endif + +/*#define TYPE_is_const(type) (((type) >> 24) & TF_CONST)*/ +#define TYPE_is_static(type) ((type).t.flag & TF_STATIC) +#define TYPE_is_public(type) ((type).t.flag & TF_PUBLIC) + +#define TYPE_is_optional(type) ((type).t.flag & TF_OPTIONAL) +/*#define TYPE_is_output(type) (((type) >> 24) & TF_OUTPUT)*/ + +#define TYPE_is_array(type) (TYPE_get_id(type) == T_ARRAY) +#define TYPE_is_object(type) ((TYPE_get_id(type) == T_OBJECT) && (TYPE_get_value(type) >= 0)) +#define TYPE_is_boolean(type) (TYPE_get_id(type) == T_BOOLEAN) +#define TYPE_can_be_long(type) (TYPE_get_id(type) <= T_LONG) + +#define TYPE_get_value(type) ((type).t.value) +#define TYPE_get_kind(type) ((type).t.flag & 0x7) +#define TYPE_get_id(type) ((type).t.id) +#define TYPE_is_null(type) ((type).l == 0) +#define TYPE_is_void(type) ((type).t.id == 0) + +#define TYPE_set_value(type, _value) ((type)->t.value = (_value)) +#define TYPE_set_id(type, _id) ((type)->t.id = (_id)) +#define TYPE_set_kind(type, _kind) ((type)->t.flag &= ~0x7, (type)->t.flag |= (_kind)) +#define TYPE_set_flag(type, _flag) ((type)->t.flag |= (_flag)) +#define TYPE_clear_flag(type, _flag) ((type)->t.flag &= ~(_flag)) +#define TYPE_clear(type) ((type)->l = 0) + +#define TYPE_compare(_t1, _t2) ((_t1)->t.id == (_t2)->t.id && (_t1)->t.value == (_t2)->t.value) + +#define TYPE_make_simple(_id) ({ TYPE _t; _t.t.flag = 0; _t.t.id = (_id); _t.t.value = -1; _t; }) +#define TYPE_make(_id, _value, _flag) ({ TYPE _t; _t.t.flag = (_flag); _t.t.id = (_id); _t.t.value = ((_id) == T_OBJECT || (_id) == T_ARRAY || (_id) == T_STRUCT) ? (_value) : -1; _t; }) + +#define TYPE_must_ref(type) (TYPE_get_id(type) >= T_STRING) + +/*PUBLIC long TYPE_get_class(TYPE type);*/ +//TYPE TYPE_make(TYPE_ID id, short value, int flag); +const char *TYPE_get_short_desc(TYPE type); +//size_t TYPE_sizeof(TYPE_ID type); + +char *TYPE_get_desc(TYPE type); +bool TYPE_check_prefix(TYPE type, const char *prefix, int len); + +#endif + + diff --git a/main/gbc/gbi.c b/main/gbc/gbi.c new file mode 100644 index 00000000..87aabe39 --- /dev/null +++ b/main/gbc/gbi.c @@ -0,0 +1,938 @@ +/*************************************************************************** + + gbi.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBI_C + +#include "config.h" + +#include "gb_limit.h" +#include "gb_common.h" +#include "gb_alloc.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef DONT_USE_LTDL + #define lt_dlinit() (0) + #define lt_dlhandle void * + #define lt_dlopenext(_path) dlopen(_path, RTLD_LAZY) + #define lt_dlsym(_handle, _symbol) dlsym(_handle, _symbol) + #define lt_dlclose(_handle) dlclose(_handle) + #define lt_dlerror() dlerror() +#else + #include +#endif + +#include + +#include "gb_component.h" +#include "gb_file.h" +#include "gb_str.h" +#include "gb_arch.h" +#include "gb_common_swap.h" +#include "gb_array.h" +#include "gb_table.h" +#include "gambas.h" + +static char _root[PATH_MAX + 1] = { 0 }; +static char _lib_path[PATH_MAX + 1]; +static char _info_path[PATH_MAX + 1]; +static char _buffer[PATH_MAX + 16]; +static char _env[PATH_MAX + 32]; + +static FILE *out_info; +static FILE *out_list; +static bool _verbose = FALSE; +static bool _format = FALSE; +static bool _nopreload = FALSE; +static bool _root_set = FALSE; +static bool _analyze = FALSE; +static bool _no_include_warning = TRUE; + +static char **_components = NULL; + +static TABLE *_classes = NULL; + +static bool analyze(const char *comp, bool include); + +#if HAVE_GETOPT_LONG +static struct option LongOptions[] = +{ + { "version", 0, NULL, 'V' }, + { "verbose", 0, NULL, 'v' }, + { "help", 0, NULL, 'h' }, + { "license", 0, NULL, 'L' }, + { "root", 1, NULL, 'r' }, + { 0 } +}; +#endif + + +static int compare_components(char **a, char **b) +{ + return strcmp(*a, *b); +} + +static void print(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + vfprintf(out_info, fmt, args); + + va_end(args); +} + +static void warning(const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "gbi" GAMBAS_VERSION_STRING ": warning: "); + + va_start(args, fmt); + + vfprintf(stderr, fmt, args); + + putc('\n', stderr); + + va_end(args); +} + +static void error(bool must_exit, const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "gbi" GAMBAS_VERSION_STRING ": ERROR: "); + + va_start(args, fmt); + + vfprintf(stderr, fmt, args); + + putc('\n', stderr); + + va_end(args); + + if (must_exit) + exit(1); +} + +// static void error2(const char *msg, const char *msg2, bool must_exit) +// { +// fprintf(stderr, "gbi" GAMBAS_VERSION_STRING ": ERROR: %s: %s%s%s\n", msg, msg2, (errno != 0) ? ": " : "", (errno != 0) ? strerror(errno) : ""); +// if (must_exit) +// exit(1); +// } + + +static void init(void) +{ + const char *path; + char *env; + + /* chemin d'installation de Gambas */ + + if (!_root[0]) + { + const char *dir; + + path = FILE_find_gambas(); + dir = FILE_get_dir(FILE_get_dir(path)); + if (dir) + strncpy(_root, dir, PATH_MAX); + } + + #ifdef OS_64BITS + strcpy(_lib_path, FILE_cat(_root, GAMBAS_LIB64_PATH, NULL)); + if (access(FILE_cat(_lib_path, "gb.component", NULL), F_OK)) + #endif + strcpy(_lib_path, FILE_cat(_root, GAMBAS_LIB_PATH, NULL)); + + strcpy(_info_path, FILE_cat(_root, "share/gambas" GAMBAS_VERSION_STRING "/info", NULL)); + + if (lt_dlinit()) + error(TRUE, "Cannot initialize plug-in management: %s", lt_dlerror()); + + env = getenv("GBI_DEBUG"); + if (env && *env && atoi(env)) + _no_include_warning = FALSE; +} + + +static void newline() +{ + print("\n"); +} + +#if 0 +static bool print_type(const char *type) +{ + switch (*type) + { + case 'b': print("Boolean"); break; + case 'i': print("Integer"); break; + case 's': print("String"); break; + case 'd': print("Date"); break; + case 'f': print("Float"); break; + case 'v': print("Variant"); break; + case 'o': print("Object"); break; + + default: + + while (*type && *type != ';') + { + print("%c", *type); + type++; + } + return TRUE; + } + + return FALSE; +} +#endif + +static void dump_symbol(GB_DESC *desc) +{ + const char *name; + const char *p; + + name = &desc->name[1]; + + print("%s\n", name); + print("%c\n", *desc->name); + if (desc->val1) + print("%s", desc->val1); + newline(); + + if (*desc->name == 'C') + { + switch(*(char *)desc->val1) + { + case 'i': + print("%d", desc->val2); + break; + + case 's': + p = (const char *)desc->val2; + while (*p) + { + if (*p == '\n') + print("\\n"); + else if (*p == '\t') + print("\\t"); + else + print("%c", *p); + p++; + } + break; + + case 'b': + if (desc->val2) + print("T"); + break; + + case 'f': + print("%.15g", desc->val4); + break; + + default: + print("?"); + break; + } + } + else if (desc->val3) + print("%s", (char *)desc->val3); + + newline(); +} + + +static GB_DESC *_sort_symbol; + +static int sort_symbol(const int *a, const int *b) +{ + return strcmp(_sort_symbol[*a].name, _sort_symbol[*b].name); +} + +static void add_class(const char *name, bool has_static) +{ + int index; + + if (out_list) + { + if (!TABLE_add_symbol_exist(_classes, name, strlen(name), &index)) + { + fputs(name, out_list); + if (has_static) + fputc('!', out_list); + fputc('\n', out_list); + } + } +} + +static void analyze_class(GB_DESC *desc) +{ + const char *name = desc->name; + char *parent = NULL; + bool autocreate = FALSE; + bool nocreate = FALSE; + uintptr_t hook; + int nsymbol; + int *sort; + GB_DESC *p; + int i; + bool has_static = FALSE; + + desc++; + + while (desc->name) + { + hook = (uintptr_t)desc->name; + + if (hook == (intptr_t)GB_INHERITS_ID) + parent = (char *)desc->val1; + else if (hook == (intptr_t)GB_NOT_CREATABLE_ID) + nocreate = TRUE; + else if (hook == (intptr_t)GB_AUTO_CREATABLE_ID) + autocreate = TRUE; + else if (hook > 16) + break; + + desc++; + } + + p = desc; + nsymbol = 0; + while (p->name) + { + nsymbol++; + p++; + } + + if (_format) + { + print("CLASS %s\n", name); + if (parent) print("INHERITS %s\n", parent); + if (!nocreate) print("CREATABLE\n"); + if (autocreate) print("AUTOCREATABLE\n"); + } + else + { + print("#%s\n", name); + if (parent) + print("%s", parent); + newline(); + if (!nocreate) + print("C"); + if (autocreate) + print("A"); + newline(); + } + + ALLOC(&sort, sizeof(int) * nsymbol); + for (i = 0; i < nsymbol; i++) + sort[i] = i; + + _sort_symbol = desc; + qsort(sort, nsymbol, sizeof(int), (int (*)(const void *, const void *))sort_symbol); + + for (i = 0; i < nsymbol; i++) + { + p = &desc[sort[i]]; + if (strchr("PCMR", *p->name) && p->name[1] != '_') + has_static = TRUE; + dump_symbol(p); + } + + FREE(&sort); + + if (_format) + newline(); + + add_class(name, has_static); +} + + +static GB_DESC **_sort_desc; + +static int sort_desc(const int *a, const int *b) +{ + return strcmp(_sort_desc[*a]->name, _sort_desc[*b]->name); +} + +static void analyze_include(char *include_list) +{ + char *includes[8]; + int nincludes; + char *include; + int i; + + include_list = STR_copy(include_list); + + if (_verbose) + fprintf(stderr, "Including %s\n", include_list); + + nincludes = 0; + include = strtok(include_list, ","); + while (include && nincludes < 8) + { + includes[nincludes++] = include; + include = strtok(NULL, ","); + } + + for (i = 0; i < nincludes; i++) + { + include = strtok(includes[i], "|"); + while (include) + { + if (!analyze(include, TRUE)) + break; + include = strtok(NULL, "|"); + } + } + + STR_free(include_list); +} + +static void analyze_classes(GB_DESC **desc) +{ + int nclass; + int *sort; + int i; + GB_DESC **p; + + nclass = 0; + + p = desc; + while (*p) + { + nclass++; + p++; + } + + ALLOC(&sort, sizeof(int) * nclass); + for (i = 0; i < nclass; i++) + sort[i] = i; + + _sort_desc = desc; + qsort(sort, nclass, sizeof(int), (int (*)(const void *, const void *))sort_desc); + + for (i = 0; i < nclass; i++) + analyze_class(desc[sort[i]]); + + FREE(&sort); +} + +static bool analyze_native_component(const char *path, bool for_include) +{ + lt_dlhandle lib; + GB_DESC **desc; + GB_DESC **desc_opt; + char **include; + + if (_verbose) + fprintf(stderr, "Loading native component: %s\n", path); + + //lt_dlopen_flag = RTLD_LAZY; /* | RTLD_GLOBAL;*/ + + lib = lt_dlopenext(path); + if (!lib) + { + if (!for_include || _verbose) + error(FALSE, "Cannot load shared library: %s", lt_dlerror()); + return TRUE; + } + + include = lt_dlsym(lib, LIB_INCLUDE); + if (include) + analyze_include(*include); + + desc = lt_dlsym(lib, LIB_CLASS); + desc_opt = lt_dlsym(lib, LIB_OPTIONAL); + + if (desc) + { + analyze_classes(desc); + if (desc_opt) + analyze_classes(desc_opt); + } + else + { + if (_verbose) + warning("cannot find '" LIB_CLASS "' symbol in shared library."); + } + + // Do not close shared libraries, except on openbsd that seems to feel better with + #ifdef OS_OPENBSD + lt_dlclose(lib); + #endif + + return FALSE; +} + + +static bool analyze_gambas_component(const char *path) +{ + ARCH *arch; + ARCH_FIND find; + bool ret = TRUE; + char *buffer; + char *line; + + if (_verbose) + fprintf(stderr, "Loading gambas component: %s\n", path); + + arch = ARCH_open(path); + + if (!ARCH_find(arch, ".component", 0, &find)) + { + ALLOC(&buffer, find.len + 1); + memcpy(buffer, &arch->addr[find.pos], find.len); + buffer[find.len] = 0; + + for (line = strtok(buffer, "\n"); line; line = strtok(NULL, "\n")) + { + if (strncmp(line, "Include=", 8)) + continue; + analyze_include(&line[8]); + break; + } + + FREE(&buffer); + } + + if (ARCH_find(arch, ".info", 0, &find)) + { + warning("'.info' file not found in component archive."); + goto __RETURN; + } + + fwrite(&arch->addr[find.pos], 1, find.len, out_info); + + if (ARCH_find(arch, ".list", 0, &find)) + { + warning("'.list' file not found in component archive."); + goto __RETURN; + } + + ALLOC(&buffer, find.len + 1); + memcpy(buffer, &arch->addr[find.pos], find.len); + buffer[find.len] = 0; + + for (line = strtok(buffer, "\n"); line; line = strtok(NULL, "\n")) + add_class(line, FALSE); + + FREE(&buffer); + + //fwrite(&arch->addr[find.pos], 1, find.len, out_list); + ret = FALSE; + +__RETURN: + + ARCH_close(arch); + return ret; +} + +#if 0 +static void preload(char **argv, char *lib) +{ +#if DO_PRELOADING + if (_nopreload || getenv("GB_PRELOAD") || !lib || !*lib) + return; + + snprintf(_env, sizeof(_env), "LD_PRELOAD=%s", lib); + putenv(_env); + putenv("GB_PRELOAD=1"); + + execvp(argv[0], argv); +#endif +} +#endif + +static bool find_native_component(const char *name) +{ + snprintf(_buffer, sizeof(_buffer), LIB_PATTERN, _lib_path, name); + return (access(_buffer, F_OK) == 0); +} + +static bool analyze(const char *comp, bool include) +{ + bool native, gambas; + char *name; + char *path_list = NULL; + char *path_info = NULL; + bool ok; + + name = STR_copy(comp); + + if (_verbose) + fprintf(stderr, "%s component %s\n", include ? "Including" : "Analyzing", name); + /*else if (!include) + puts(name);*/ + + native = find_native_component(name); + + snprintf(_buffer, sizeof(_buffer), ARCH_PATTERN, _lib_path, name); + gambas = (access(_buffer, F_OK) == 0); + + if (!native && !gambas) + { + if (!include || !_no_include_warning) + warning("component not found: %s", name); + STR_free(name); + return TRUE; + } + + if (!include) + { + path_info = STR_cat(FILE_cat(_info_path, name, NULL), ".info", NULL); + path_list = STR_cat(FILE_cat(_info_path, name, NULL), ".list", NULL); + + out_info = fopen(path_info, "w"); + if (!out_info) + { + error(FALSE, "Cannot write file: %s", path_info); + return TRUE; + } + + out_list = fopen(path_list, "w"); + if (!out_list) + { + error(FALSE, "Cannot write file: %s", path_list); + return TRUE; + } + + TABLE_create(&_classes, sizeof(SYMBOL), TF_IGNORE_CASE); + } + + fflush(stdout); + ok = TRUE; + + if (native) + { + snprintf(_buffer, sizeof(_buffer), LIB_PATTERN, _lib_path, name); + + if (analyze_native_component(_buffer, include)) + ok = FALSE; + } + + if (gambas) + { + snprintf(_buffer, sizeof(_buffer), ARCH_PATTERN, _lib_path, name); + + if (analyze_gambas_component(_buffer)) + if (!native) + ok = FALSE; + } + + if (!include) + { + TABLE_delete(&_classes); + + fclose(out_info); + fclose(out_list); + + if (!ok) + { + FILE_unlink(path_info); + FILE_unlink(path_list); + } + else if (_verbose) + { + fprintf(stderr, "Wrote %s\n", path_info); + fprintf(stderr, "Wrote %s\n", path_list); + } + + STR_free(path_info); + STR_free(path_list); + } + + STR_free(name); + + return !ok; +} + +static void run_myself(const char *path, const char *name) +{ + const char *argv[10]; + int n = 0; + pid_t pid; + int status; + + if (_verbose) + fprintf(stderr, "Running myself for component '%s'...\n\n", name); + + argv[n++] = path; + if (_verbose) + argv[n++] = "-v"; + if (_nopreload) + argv[n++] = "-p"; + if (_root_set) + { + argv[n++] = "-r"; + argv[n++] = _root; + } + argv[n++] = "-a"; + argv[n++] = name; + argv[n] = NULL; + + if (find_native_component(name)) + { + snprintf(_env, sizeof(_env), "LD_PRELOAD=%s", _buffer); + putenv(_env); + } + else + unsetenv("LD_PRELOAD"); + + pid = fork(); + switch (pid) + { + case 0: + execvp(path, (char **)argv); + error(FALSE, "Cannot run sub-process: %s", strerror(errno)); + exit(1); + case -1: + error(FALSE, "Cannot run sub-process: %s", strerror(errno)); + exit(1); + default: + waitpid(pid, &status, 0); + } + + if (_verbose) + fputc('\n', stderr); +} + +static void make_component_list() +{ + DIR *dir; + struct dirent *dirent; + const char *name; + + dir = opendir(_lib_path); + if (dir == NULL) + error(TRUE, "Cannot read directory: %s", _lib_path); + + //save_fd = dup(STDOUT_FILENO); + + ARRAY_create(&_components); + + while ((dirent = readdir(dir)) != NULL) + { + name = dirent->d_name; + if (strcmp(FILE_get_ext(name), "component")) + continue; + name = FILE_get_basename(name); + if (strcmp(name, "gb") == 0) + continue; + *((char **)ARRAY_add(&_components)) = STR_copy(name); + } + + closedir(dir); + + qsort(_components, ARRAY_count(_components), sizeof(*_components), (int (*)(const void *, const void *))compare_components); +} + +static void print_version() +{ + #ifdef TRUNK_VERSION + printf(VERSION " " TRUNK_VERSION "\n"); + #else /* no TRUNK_VERSION */ + printf(VERSION "\n"); + #endif +} + +static void print_title() +{ + printf("\nGambas informer version "); + print_version(); +} + +int main(int argc, char **argv) +{ + int i; + char *name; + int opt; + int ind = 0; + + //dup(STDOUT_FILENO); + + //_verbose = TRUE; + + for(;;) + { + #if HAVE_GETOPT_LONG + opt = getopt_long(argc, argv, "vVhLpar:", LongOptions, &ind); + #else + opt = getopt(argc, argv, "vVhLpar:"); + #endif + if (opt < 0) break; + + switch (opt) + { + case 'V': + print_version(); + + case 'v': + _verbose = TRUE; + break; +#if DO_PRELOADING + case 'p': + _nopreload = TRUE; + break; +#endif + + case 'r': + strncpy(_root, optarg, PATH_MAX); + _root_set = TRUE; + break; + + case 'a': + _analyze = TRUE; + break; + + case 'L': + print_title(); + printf(COPYRIGHT); + exit(0); + + case 'h': + print_title(); + printf( + "\nGenerate component description files.\n" + "\n gbi" GAMBAS_VERSION_STRING " [options] [components]\n" + "\nOptions:" + #if HAVE_GETOPT_LONG + "\n\n" + " -h --help display this help\n" + " -L --license display license\n" + #if DO_PRELOADING + " -p disable preloading\n" + #endif + " -r --root gives the gambas installation directory\n" + " -v verbose output\n" + " -V --version display version\n" + #else + " (no long options on this system)\n\n" + " -h display this help\n" + " -L display license\n" + #if DO_PRELOADING + " -p disable preloading\n" + #endif + " -r gives the gambas installation directory\n" + " -v verbose output\n" + " -V display version\n" + #endif + "\n" + ); + + exit(0); + } + } + + init(); + + if (_analyze) + { + if (_verbose) + { + char *env = getenv("LD_PRELOAD"); + if (env) + fprintf(stderr, "LD_PRELOAD=%s\n", env); + } + + analyze(argv[optind], FALSE); + /*if (strcmp(argv[optind], "gb.qt4") == 0) + analyze("gb.gui", FALSE); + else if (strcmp(argv[optind], "gb.qt4.opengl") == 0) + analyze("gb.gui.opengl", FALSE);*/ + } + else + { + if (optind == argc + #ifdef OS_SOLARIS /* solaris bug ? */ + || optind == 0 + #endif + ) + { + /*preload(argv, + "libqt-mt.so.3 " + "libkdecore.so.4 " + );*/ + + if (_verbose) + { + fprintf(stderr, "Browsing component directory: %s\n", _lib_path); + fprintf(stderr, "*.info files are stored in: %s\n\n", _info_path); + } + + make_component_list(); + + for (i = 0; i < ARRAY_count(_components); i++) + { + name = _components[i]; + run_myself(argv[0], name); + STR_free(name); + } + + ARRAY_delete(&_components); + } + else + { +#if 0 + if (!getenv("GB_PRELOAD")) + { + for (ind = optind; ind < argc; ind++) + { + name = argv[ind]; + /*if (strncmp(name, "gb.qt.kde", 9) == 0) + preload(argv, "libqt-mt.so.3 libkdecore.so.4"); + else if (strcmp(name, "gb.qt") == 0 || strncmp(name, "gb.qt.", 6) == 0) + preload(argv, "libqt-mt.so.3");*/ + } + } +#endif + + for (ind = optind; ind < argc; ind++) + { + name = argv[ind]; + //analyze(name, FALSE); + run_myself(argv[0], name); + } + } + } + + exit(0); +} + diff --git a/main/gbx/Makefile.am b/main/gbx/Makefile.am new file mode 100644 index 00000000..6c0b652f --- /dev/null +++ b/main/gbx/Makefile.am @@ -0,0 +1,98 @@ +AM_CFLAGS += $(GB_CFLAGS_LTO) -I$(top_srcdir)/share @INTL_INC@ @CONV_INC@ @GBX_THREAD_INC@ @FFI_INC@ +AM_CFLAGS_OPT += $(GB_CFLAGS_LTO) -I$(top_srcdir)/share + +bin_PROGRAMS = gbx3 +noinst_LIBRARIES = libgbx.a +gblib_LTLIBRARIES = gb.la + +libgbx_a_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" -Wno-address-of-packed-member -Wno-stringop-truncation $(AM_CFLAGS) + +gbx3_LDADD = @C_LIB@ @GBX_THREAD_LIB@ libgbx.a @MATH_LIB@ @INTL_LIB@ @CONV_LIB@ @GETTEXT_LIB@ @DL_LIB@ @FFI_LIB@ @RT_LIB@ +gbx3_LDFLAGS = @LD_FLAGS@ @GBX_THREAD_LDFLAGS@ @INTL_LDFLAGS@ @CONV_LDFLAGS@ @GETTEXT_LDFLAGS@ @FFI_LDFLAGS@ @RT_LDFLAGS@ +gbx3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" -Wno-address-of-packed-member -Wno-stringop-truncation $(AM_CFLAGS) + +gb_la_LIBADD = @C_LIB@ @GBX_THREAD_LIB@ @MATH_LIB@ @INTL_LIB@ @CONV_LIB@ @GETTEXT_LIB@ @DL_LIB@ @FFI_LIB@ @RT_LIB@ +gb_la_LDFLAGS = -module @LD_FLAGS@ @INTL_LDFLAGS@ @CONV_LDFLAGS@ @GETTEXT_LDFLAGS@ @FFI_LDFLAGS@ @RT_LDFLAGS@ +gb_la_CFLAGS = -DGBX_INFO $(AM_CFLAGS) -Wno-address-of-packed-member -Wno-stringop-truncation -O0 + +libgbx_a_SOURCES = \ + gb_error.h gb_error.c \ + gbx_split.h gbx_split.c \ + gbx_exec_loop.c \ + gb_hash.c \ + gb_common_check.h gb_common.c \ + gbx_exec.h gbx_exec.c gbx_exec_push.c gbx_exec_enum.c gbx_exec_pop.c gbx_exec_operator.c \ + gbx_string.h gbx_string.c \ + gbx_value.h gbx_value.c + +gbx3_SOURCES = \ + gbx_debug.h gbx_debug.c \ + gbx_jit.h gbx_jit.c gb.jit.h \ + gb_alloc.c gb_array.c \ + gbx_stack.h gbx_stack.c \ + gb_buffer.c gbx_replace.c \ + gb_table.c \ + gb_list.c \ + gbx_type.h gbx_type.c \ + gbx_class_desc.h gbx_class.h gbx_class_init.c gbx_class.c gbx_class_native.c \ + gbx_class_load.c gbx_class_load.h \ + gbx_event.h gbx_event.c \ + gb_file.h gb_file.c \ + gbx_stream.h gbx_stream.c gbx_stream_direct.c gbx_stream_lock.c gbx_stream_buffer.c gbx_stream_memory.c gbx_stream_null.c \ + gbx_stream_arch.c gbx_stream_process.c gbx_stream_pipe.c gbx_stream_string.c \ + gbx_project.h gbx_project.c \ + gbx_library.h gbx_library.c \ + gbx_subr.h gbx_subr.c \ + gbx_subr_file.c gbx_subr_string.c gbx_subr_conv.c gbx_subr_time.c gbx_subr_extern.c gbx_subr_misc.c \ + gbx_math.h gbx_math.c \ + gbx_subr_math_temp.h gbx_subr_math.c \ + gbx_subr_test_temp.h gbx_subr_test.c \ + gbx_api.h gbx_api.c \ + gbx_local.h gbx_local.c \ + gbx_regexp.h gbx_regexp.c \ + gbx_archive.h gbx_archive.c \ + gbx_watch.h gbx_watch.c \ + gbx_expression.h gbx_eval.h gbx_eval.c \ + gbx_compare.h gbx_compare.c \ + gbx.c gbx.h \ + gbx_number.h gbx_number.c \ + gbx_object.h gbx_object.c \ + gbx_variant.h \ + gbx_date.h gbx_date.c \ + gbx_c_class.h gbx_c_class.c \ + gbx_c_collection.h gbx_c_collection.c \ + gbx_c_error.h gbx_c_error.c \ + gbx_c_gambas.h gbx_c_gambas.c \ + gbx_c_file.h gbx_c_file.c \ + gbx_c_application.h gbx_c_application.c \ + gbx_c_system.h gbx_c_system.c \ + gbx_c_array.h gbx_c_array.c \ + gbx_c_process.h gbx_c_process.c \ + gbx_c_string.h gbx_c_string.c \ + gbx_c_observer.h gbx_c_observer.c \ + gbx_c_task.h gbx_c_task.c \ + gbx_component.h gbx_component.c \ + gbx_extern.h gbx_extern.c \ + gbx_c_enum.h gbx_c_enum.c \ + gbx_c_timer.h gbx_c_timer.c \ + gbx_struct.h gbx_struct.c \ + gbx_signal.h gbx_signal.c + +gb_la_SOURCES = \ + gbx_info.h \ + gbx_local.h gbx_compare.h gbx_date.h \ + gbx_c_class.h gbx_c_class.c \ + gbx_c_collection.h gbx_c_collection.c \ + gbx_c_error.h gbx_c_error.c \ + gbx_c_gambas.h gbx_c_gambas.c \ + gbx_c_file.h gbx_c_file.c \ + gbx_c_application.h gbx_c_application.c \ + gbx_c_system.h gbx_c_system.c \ + gbx_c_array.h gbx_c_array.c \ + gbx_c_process.h gbx_c_process.c \ + gbx_c_string.h gbx_c_string.c \ + gbx_c_observer.h gbx_c_observer.c \ + gbx_c_enum.h gbx_c_enum.c \ + gbx_c_timer.h gbx_c_timer.c \ + gbx_c_task.h gbx_c_task.c \ + gbx_class_info.c diff --git a/main/gbx/gb.jit.h b/main/gbx/gb.jit.h new file mode 100644 index 00000000..d6dc2089 --- /dev/null +++ b/main/gbx/gb.jit.h @@ -0,0 +1,119 @@ +/*************************************************************************** + + gb.jit.h + + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_JIT_H +#define __GB_JIT_H + +typedef + union { + int type; + struct { int type; double value; } PACKED _float; + struct { int type; float value; } PACKED _single; + struct { int type; int value; } PACKED _integer; + struct { int type; int64_t value; } PACKED _long; + struct { int type; char *addr; int len; } PACKED _string; + struct { int type; int val[2]; } PACKED _swap; + } + PACKED + JIT_CONSTANT; + +typedef + ushort JIT_PCODE; + +typedef + struct { + unsigned char flag; + unsigned char id; + short value; + } + JIT_CTYPE; + +typedef + struct { + void *next; + GB_VALUE *bp; + GB_VALUE *pp; + void *cp; + char *op; + GB_VALUE *ep; + void *fp; + JIT_PCODE *pc; + JIT_PCODE *ec; + JIT_PCODE *et; + GB_VALUE *gp; + } + JIT_CONTEXT; + +typedef + struct { + GB_VALUE **sp; + JIT_CONTEXT *exec; + GB_VALUE *ret; + GB_VALUE **exec_super; + void (*debug)(const char *fmt, ...); + JIT_PCODE *(*get_code)(void *func); + void (*throw)(int code, ...) NORETURN; + void (*throw_type)(GB_TYPE want, GB_TYPE got) NORETURN; + JIT_CONSTANT *(*get_constant)(int index); + void *(*get_class_ref)(int index); + void **subr_table; + const char *char_table; + void *(*unborrow)(GB_VALUE *val); + void (*new)(void); + void (*push_array)(ushort code); + void (*pop_array)(ushort code); + void (*conv)(GB_VALUE *value, GB_TYPE type); + void (*push_unknown)(void); + void (*call_unknown)(ushort *pc, GB_VALUE **psp); + void (*pop_unknown)(void); + void (*enum_first)(ushort code, GB_VALUE *local, GB_VALUE *penum); + bool (*enum_next)(ushort code, GB_VALUE *local, GB_VALUE *penum); + int (*find_symbol)(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix); + void (*load_class)(void *class); + void (*load_class_without_init)(void *class); + void *error_current; + void *error_handler; + void (*error_reset)(void *); + void (*error_set_last)(bool); + void (*set_got_error)(bool); + void **event_last; + void (*push_complex)(void); + void (*push_vargs)(void); + void (*pop_vargs)(void); + void (*exec_quit)(ushort code); + void (*push_unknown_event)(bool); + void *(*get_extern)(void *ext); + const char *(*get_position)(void *cp, void *fp, ushort *pc); + void (*release_many)(GB_VALUE *, int); + void *(*static_struct)(void *ref, GB_CLASS type, char *addr); + void *(*static_array)(void *cp, void *ref, GB_CLASS type, char *addr); + void *(*get_array_class)(void *cp, JIT_CTYPE ctype); + void (*add_string_local)(GB_STRING *str, GB_STRING val); + void (*add_string_global)(char **str, GB_STRING val); + void (*value_class_write)(void *class, GB_VALUE *value, void *addr, JIT_CTYPE ctype); + void (*subr_poke)(ushort code); + void *(*get_object_addr)(void *ob); + } + JIT_INTERFACE; + +#endif diff --git a/main/gbx/gb_alloc.c b/main/gbx/gb_alloc.c new file mode 100644 index 00000000..5e2b9bbd --- /dev/null +++ b/main/gbx/gb_alloc.c @@ -0,0 +1,27 @@ +/*************************************************************************** + + gb_alloc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define PROJECT_EXEC + +#include "gb_alloc_temp.h" + diff --git a/main/gbx/gb_array.c b/main/gbx/gb_array.c new file mode 100644 index 00000000..6c300c86 --- /dev/null +++ b/main/gbx/gb_array.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_array_temp.h" + diff --git a/main/gbx/gb_buffer.c b/main/gbx/gb_buffer.c new file mode 100644 index 00000000..84269fde --- /dev/null +++ b/main/gbx/gb_buffer.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_buffer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_buffer_temp.h" + diff --git a/main/gbx/gb_common.c b/main/gbx/gb_common.c new file mode 100644 index 00000000..cf418328 --- /dev/null +++ b/main/gbx/gb_common.c @@ -0,0 +1,111 @@ +/*************************************************************************** + + gb_common.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_C +#define __COMMON_CHECK_C + +#include "gb_common.h" + +#include "gb_common_check.h" + +#include "gb_common_string_temp.h" +#include "gb_common_case_temp.h" +#include "gb_common_buffer_temp.h" +#include "gb_common_swap_temp.h" + +#include "gbx_signal.h" + +sigjmp_buf CHECK_jump; + +static SIGNAL_HANDLER _SIGSEGV_handler; +static SIGNAL_HANDLER _SIGBUS_handler; + +static int _dummy; +static volatile int _got_error; + + +void COMMON_init(void) +{ +} + + +static void signal_error(int sig, siginfo_t *info, void *context) +{ + SIGNAL_previous(sig == SIGSEGV ? &_SIGSEGV_handler : &_SIGBUS_handler, sig, info, context); + _got_error = TRUE; + siglongjmp(CHECK_jump, 1); +} + +void CHECK_enter(void) +{ + _got_error = FALSE; + SIGNAL_install(&_SIGSEGV_handler, SIGSEGV, signal_error); + SIGNAL_install(&_SIGBUS_handler, SIGBUS, signal_error); +} + +void CHECK_leave(void) +{ + SIGNAL_uninstall(&_SIGSEGV_handler, SIGSEGV); + SIGNAL_uninstall(&_SIGBUS_handler, SIGBUS); +} + +bool CHECK_got_error(void) +{ + return _got_error; +} + +bool CHECK_address(void *ptr, ssize_t len) +{ + offset_t i; + + if (len < 0) + return TRUE; + + CHECK_enter(); + if (sigsetjmp(CHECK_jump, TRUE) == 0) + { + for (i = 0; i < len; i += 1024) + _dummy = ((int *)ptr)[i]; + + _dummy = ((int *)ptr)[len >> 2]; + } + CHECK_leave(); + return _got_error; +} + +bool CHECK_strlen(char *ptr, ssize_t *len) +{ + size_t l = 0; + + CHECK_enter(); + if (sigsetjmp(CHECK_jump, TRUE) == 0) + { + l = strlen(ptr); + *len = l; + } + CHECK_leave(); + if (l != (size_t)(int)l) + _got_error = TRUE; + return _got_error; +} + diff --git a/main/gbx/gb_common_check.h b/main/gbx/gb_common_check.h new file mode 100644 index 00000000..319b9073 --- /dev/null +++ b/main/gbx/gb_common_check.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gb_common_check.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_CHECK_H +#define __GB_COMMON_CHECK_H + +#include +#include + +#ifndef __GB_COMMON_CHECK_C +EXTERN sigjmp_buf CHECK_jump; +#endif + +void CHECK_enter(void); +void CHECK_leave(void); +bool CHECK_got_error(void); +bool CHECK_address(void *ptr, ssize_t len); +bool CHECK_strlen(char *ptr, ssize_t *len); + +#endif diff --git a/main/gbx/gb_error.c b/main/gbx/gb_error.c new file mode 100644 index 00000000..cf366149 --- /dev/null +++ b/main/gbx/gb_error.c @@ -0,0 +1,819 @@ +/*************************************************************************** + + gb_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ERROR_C + +#include "gb_common.h" +#include +#include + +#include "gb_buffer.h" +#include "gbx_debug.h" +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_stack.h" +#include "gbx_project.h" +#include "gbx.h" +#include "gb_error.h" + +//#define DEBUG_ERROR 1 + +ERROR_CONTEXT *ERROR_current = NULL; +ERROR_INFO ERROR_last = { 0 }; +void *ERROR_backtrace = NULL; +ERROR_HANDLER *ERROR_handler = NULL; +#if DEBUG_ERROR +int ERROR_depth = 0; +#endif + +static int _lock = 0; +static char *_print_prefix = NULL; + +static const char *const _message[78] = +{ + /* 0 E_UNKNOWN */ "Unknown error", + /* 1 E_MEMORY */ "Out of memory", + /* 2 E_CLASS */ ".3Cannot load class '&1': &2&3", + /* 3 E_STACK */ "Stack overflow", + /* 4 E_NEPARAM */ "Not enough arguments", + /* 5 E_TMPARAM */ "Too many arguments", + /* 6 E_TYPE */ ".2Type mismatch: wanted &1, got &2 instead", + /* 7 E_OVERFLOW */ "Overflow", + /* 8 E_ILLEGAL */ "Illegal instruction", + /* 9 E_NFUNC */ "Not a function", + /* 10 E_CSTATIC */ ".1Class '&1' is not creatable", + /* 11 E_NSYMBOL */ ".2Unknown symbol '&2' in class '&1'", + /* 12 E_NOBJECT */ "Not an object", + /* 13 E_NULL */ "Null object", + /* 14 E_STATIC */ ".2'&1.&2' is static", + /* 15 E_NREAD */ ".2'&1.&2' is write only", + /* 16 E_NWRITE */ ".2'&1.&2' is read only", + /* 17 E_NPROPERTY */ ".2'&1.&2' is not a property", + /* 18 E_NRETURN */ "No return value", + /* 19 E_MATH */ "Mathematic error", + /* 20 E_ARG */ "Bad argument", + /* 21 E_BOUND */ "Out of bounds", + /* 22 E_NDIM */ "Bad number of dimensions", + /* 23 E_NARRAY */ "Not an array", + /* 24 E_MAIN */ "No startup method", + /* 25 E_NNEW */ "No instantiation method", + /* 26 E_ZERO */ "Division by zero", + /* 27 E_LIBRARY */ ".2Cannot load component '&1': &2", + /* 28 E_EVENT */ ".3Bad event handler in &1.&2(): &3", + /* 29 E_IOBJECT */ "Invalid object", + /* 30 E_ENUM */ "Not an enumeration", + /* 31 E_UCONV */ "Unsupported string conversion", + /* 32 E_CONV */ "Bad string conversion", + /* 33 E_DATE */ "Invalid date", + /* 34 E_BADPATH */ "Invalid path", + /* 35 E_OPEN */ ".2Cannot open file '&1': &2", + /* 36 E_PROJECT */ ".2Bad project file: line &1: &2", + /* 37 E_FULL */ "Device is full", + /* 38 E_EXIST */ "File already exists", /* &1 */ + /* 39 E_EOF */ "End of file", + /* 40 E_FORMAT */ "Bad format string", + /* 41 E_DYNAMIC */ ".2'&1.&2' is not static", + /* 42 E_SYSTEM */ ".2System error #&1: &2", + /* 43 E_ACCESS */ "Access forbidden", + /* 44 E_TOOLONG */ "File name is too long", + /* 45 E_NEXIST */ "File or directory does not exist", /* &1 */ + /* 46 E_DIR */ "File is a directory", /* &1 */ + /* 47 E_READ */ "Read error", + /* 48 E_WRITE */ "Write error", + /* 49 E_NDIR */ ".1Not a directory: &1", + /* 50 E_REGEXP */ ".1Bad regular expression: &1", + /* 51 E_ARCH */ ".2Bad archive: &1: &2", + /* 52 E_REGISTER */ ".1Cannot register class '&1'", + /* 53 E_CLOSED */ "Stream is closed", + /* 54 E_VIRTUAL */ "Bad use of virtual class", + /* 55 E_STOP */ "STOP instruction encountered", + /* 56 E_STRING */ "Too many simultaneous new strings", + /* 57 E_EVAL */ ".1Bad expression: &1", + /* 58 E_LOCK */ "File is locked", + /* 59 E_PARENT */ "No parent class", + /* 60 E_EXTLIB */ ".2Cannot find dynamic library '&1': &2", + /* 61 E_EXTSYM */ ".2Cannot find symbol '&2' in dynamic library '&1'", + /* 62 E_BYREF */ "Argument cannot be passed by reference", + /* 63 E_OVERRIDE */ ".3'&1.&2' is incorrectly overridden in class '&3'", + /* 64 E_NKEY */ "Void key", + /* 65 E_SARRAY */ "Read-only array", + /* 66 E_EXTCB */ ".1Cannot create callback: &1", + /* 67 E_SERIAL */ "Serialization error", + /* 68 E_CHILD */ ".2Cannot run child process: &1&2", + /* 69 E_USER */ "Unknown user or group", + /* 70 E_NEMPTY */ "Directory is not empty", + /* 71 E_UTYPE */ "Unsupported datatype", + /* 72 E_FREEREF */ "Free object referenced", + /* 73 E_ASSERT */ "Assertion failed", + /* 74 E_MARRAY */ "Multidimensional array", + /* 75 E_UCLASS */ ".1Unknown class '&1'", + /* 76 E_SPEC */ ".2Incorrect declaration of symbol '&1' in class '&2'", + /* 77 E_USIZE */ "Unknow stream size" +}; + +static void clear_info(ERROR_INFO *info) +{ + if (!info->code) + return; + + if (info->free) + { + STRING_unref(&info->msg); + info->msg = NULL; + info->free = FALSE; + } + + info->code = 0; + info->msg = NULL; +} + +static void copy_info(ERROR_INFO *src, ERROR_INFO *dst) +{ + clear_info(dst); + *dst = *src; + if (dst->free) + STRING_ref(dst->msg); +} + +static void move_info(ERROR_INFO *src, ERROR_INFO *dst) +{ + clear_info(dst); + *dst = *src; + CLEAR(src); +} + +#if DEBUG_ERROR +void ERROR_debug(const char *msg, ...) +{ + int i; + va_list args; + ERROR_CONTEXT *err; + + va_start(args, msg); + + for (i = 0; i < ERROR_depth; i++) + fprintf(stderr, "- "); + + vfprintf(stderr, msg, args); + + fprintf(stderr, "\t\t\t\t\t\t\t\t\t"); + DEBUG_where(); + err = ERROR_current; + while (err) + { + fprintf(stderr, "[%p] -> ", err); + err = err->prev; + } + fprintf(stderr, "NULL\n"); + + va_end(args); +} +#endif + +void ERROR_lock() +{ + _lock++; +} + + +void ERROR_unlock() +{ + _lock--; +} + +void ERROR_reset(ERROR_INFO *info) +{ + clear_info(info); +} + +static void ERROR_clear() +{ + if (_lock) + { + #if DEBUG_ERROR + ERROR_debug("ERROR_clear: (%p) *LOCKED*\n", ERROR_current); + #endif + return; + } + + #if DEBUG_ERROR + fprintf(stderr, "ERROR_clear: (%p)\n", ERROR_current); + #endif + ERROR_reset(&ERROR_current->info); + STACK_free_backtrace(&ERROR_backtrace); +} + +#if 0 +void ERROR_enter(ERROR_CONTEXT *err) +{ + err->prev = ERROR_current; + err->info.code = 0; + //err->info.free = FALSE; + //err->info.msg = NULL; + //err->info.backtrace = NULL; + + ERROR_current = err; + + #if DEBUG_ERROR + fprintf(stderr, "ERROR_enter: (%p)\n", ERROR_current); + fprintf(stderr, ">> ERROR_enter"); + { + ERROR_CONTEXT *e = err; + while (e) + { + fprintf(stderr, " -> %p", e); + e = e->prev; + } + fprintf(stderr, "\n"); + } + #endif +} +#endif + +#if 0 +void ERROR_leave(ERROR_CONTEXT *err) +{ + if (err->prev == ERROR_LEAVE_DONE) + return; + + #if DEBUG_ERROR + fprintf(stderr, "<< ERROR_leave"); + { + ERROR_CONTEXT *e = err; + while (e) + { + fprintf(stderr, " -> %p", e); + e = e->prev; + } + fprintf(stderr, " : %d %s\n", err->info.code, err->info.msg); + } + #endif + + //if (!err->prev) + // BREAKPOINT(); + + ERROR_current = err->prev; + + if (ERROR_current) + { + #if DEBUG_ERROR + fprintf(stderr, "ERROR_leave: (%p)\n", ERROR_current); + #endif + if (err->info.code) + { + ERROR_reset(&ERROR_current->info); + ERROR_current->info = err->info; + } + } + else + ERROR_reset(&err->info); + + err->prev = ERROR_LEAVE_DONE; + //ERROR_reset(err); +} +#endif + +void ERROR_propagate() +{ + ERROR_HANDLER *ph, *prev; + #if DEBUG_ERROR + ERROR_debug("ERROR_propagate: %p %d %s (ret = %d)\n", ERROR_current, ERROR_current->info.code, ERROR_current->info.msg, ERROR_current->ret); + #endif + + //fprintf(stderr, "ERROR_propagate: %p\n", ERROR_handler); + + if (ERROR_in_catch(ERROR_current)) + ERROR_leave(ERROR_current); + + while (ERROR_handler) + { + ph = ERROR_handler; + if (ERROR_current && ERROR_current->handler == ph) + break; + + //fprintf(stderr, "ERROR_propagate: %p @ %p (%p)\n", ERROR_handler, ERROR_handler->context, ERROR_current); + prev = ph->prev; + (*ph->handler)(ph->arg1, ph->arg2); + ERROR_handler = prev; + } + + longjmp(ERROR_current->env, 1); +} + + + +const char *ERROR_get(void) +{ + /* + if (code > 0 && code < 256) + return strerror(code); + else + return ERROR_Message[code - 256]; + */ + return strerror(errno); +} + +static int get_message_length(const char *pattern, char *arg[], int narg) +{ + int len; + int i; + + len = strlen(pattern) + narg; + for (i = 0; i < narg; i++) + { + if (arg[i]) + len += strlen(arg[i]); + } + + if (!EXEC_debug) + len -= narg * 3; + + return len; +} + +void ERROR_define(const char *pattern, char *arg[]) +{ + uchar c; + char *msg = NULL; + int len; + int narg = 0; + + ERROR_clear(); + + if ((intptr_t)pattern >= 0 && (intptr_t)pattern < 256) + { + ERROR_current->info.code = (int)(intptr_t)pattern; + pattern = _message[(int)(intptr_t)pattern]; + if (*pattern == '.') + { + narg = pattern[1] - '0'; + pattern += 2; + } + } + else if ((intptr_t)pattern == E_ABORT) + { + ERROR_current->info.code = E_ABORT; + pattern = ""; + } + else + { + ERROR_current->info.code = E_CUSTOM; + + if (arg) + { + msg = (char *)pattern; + for (;;) + { + c = *msg++; + if (c == 0) + break; + + if (c == '&') + { + c = *msg++; + if (c >= '1' && c <= '4') + { + c -= '0'; + if (c > narg) + narg = c; + } + } + } + } + } + + if (narg) + { + len = get_message_length(pattern, arg, narg); + if (len) + { + msg = STRING_new(NULL, len); + ERROR_current->info.msg = msg; + ERROR_current->info.free = TRUE; + + if (EXEC_debug) + { + int i; + strcpy(msg, pattern); + msg += strlen(pattern); + for (i = 0; i < narg; i++) + { + *msg++ = '|'; + if (arg[i]) + { + strcpy(msg, arg[i]); + msg += strlen(arg[i]); + } + } + } + else + { + for (;;) + { + c = *pattern++; + if (c == 0) + break; + + if (c == '&') + { + c = *pattern++; + if (c >= '1' && c <= '4') + { + c -= '1'; + if (arg[c]) + { + len = strlen(arg[c]); + memcpy(msg, arg[c], len); + msg += len; + } + } + } + else + *msg++ = c; + } + + *msg = 0; + } + + //fprintf(stderr, "msg: %p '%s'\n", ERROR_current->info.msg, ERROR_current->info.msg); + /*if (strcmp(ERROR_current->info.msg, "Type mismatch: wanted WebView, got Function instead") == 0) + { + BREAKPOINT(); + STRING_watch = ERROR_current->info.msg; + }*/ + } + } + else if (ERROR_current->info.code == E_CUSTOM) + { + if (pattern && *pattern) + { + ERROR_current->info.msg = STRING_new_zero(pattern); + ERROR_current->info.free = TRUE; + } + else + { + ERROR_current->info.msg = (char *)_message[E_UNKNOWN]; + ERROR_current->info.free = FALSE; + } + } + else + { + ERROR_current->info.msg = (char *)pattern; + ERROR_current->info.free = FALSE; + } + + //fprintf(stderr, "ERROR_define: %p %d '%s'\n", ERROR_current, ERROR_current->info.code, ERROR_current->info.msg); + + //STRING_add_char(&ERROR_current->info.msg, 0); + + ERROR_current->info.cp = CP; + ERROR_current->info.fp = FP; + ERROR_current->info.pc = PC; + + #if DEBUG_ERROR + ERROR_debug("ERROR_define: %s\n", ERROR_current->info.msg); + #endif +} + +void THROW(int code, ...) +{ + va_list args; + int i; + char *arg[4]; + + va_start(args, code); + + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, char *); + + ERROR_define((char *)(intptr_t)code, arg); + + va_end(args); + + PROPAGATE(); +} + +void THROW_NULL(void) +{ + THROW(E_NULL); +} + +void THROW_CLASS(void *class, char *arg1, char *arg2) +{ + THROW(E_CLASS, CLASS_get_name((CLASS *)class), arg1, arg2); +} + +void THROW_ILLEGAL(void) +{ + THROW(E_ILLEGAL); +} + +void THROW_STACK(void) +{ + #if DEBUG_STACK + fprintf(stderr, "THROW STACK!\n"); + #endif + THROW(E_STACK); +} + +void THROW_BOUND(void) +{ + THROW(E_BOUND); +} + +void THROW_SYSTEM(int err, const char *path) +{ + char buf[6]; + + switch(err) + { + case ENOENT: + THROW(E_NEXIST, path); + + case EISDIR: + THROW(E_DIR, path); + + case ENOTDIR: + THROW(E_NDIR, path); + + case ENOMEM: + THROW(E_MEMORY); + + case EACCES: + THROW(E_ACCESS); + + case ENAMETOOLONG: + THROW(E_TOOLONG); + + case ENOSPC: + THROW(E_FULL); + + case EEXIST: + THROW(E_EXIST, path); + + default: + sprintf(buf, "%d", err); + THROW(E_SYSTEM, buf, strerror(err)); + } +} + +void THROW_MATH(bool zero) +{ + THROW(zero ? E_ZERO : E_MATH); +} + +void THROW_OVERFLOW_(void) +{ + THROW(E_OVERFLOW); +} + + +void ERROR_fatal(const char *error, ...) +{ + va_list args; + + va_start(args, error); + fputs(EXEC_arch ? "gbr" GAMBAS_VERSION_STRING : "gbx" GAMBAS_VERSION_STRING, stderr); + fputs(": ", stderr); + vfprintf(stderr, error, args); + va_end(args); + putc('\n', stderr); + _exit(1); +} + +void ERROR_panic(const char *error, ...) +{ + va_list args; + + va_start(args, error); + + fflush(NULL); + + fprintf(stderr, "\n** \n** OOPS! INTERNAL ERROR. Program aborting, sorry! :-(\n** "); + vfprintf(stderr, error, args); + + va_end(args); + + fputc('\n', stderr); + + if (ERROR_current->info.code) + { + fprintf(stderr, "** \n"); + _print_prefix = "** "; + ERROR_print(FALSE); + _print_prefix = NULL; + } + fprintf(stderr, "** \n** Please send a bug report to the gambas bugtracker [1] or to the gambas mailing-list [2].\n** [1] http://gambaswiki.org/bugtracker\n** [2] https://lists.gambas-basic.org/listinfo/user\n** \n\n"); + _exit(1); +} + + +static void print_prefix(FILE *where) +{ + if (_print_prefix) fputs(_print_prefix, where); +} + +void ERROR_print_at(FILE *where, bool msgonly, bool newline) +{ + ERROR_INFO *info = &ERROR_current->info; + + if (!info->code) + return; + + print_prefix(where); + + if (!msgonly) + { + if (info->cp && info->fp && info->pc) + fprintf(where, "%s: ", DEBUG_get_position(info->cp, info->fp, info->pc)); + else + fprintf(where, "ERROR: "); + /*if (ERROR_current->info.code > 0 && ERROR_current->info.code < 256) + fprintf(where, "%ld:", ERROR_current->info.code);*/ + if (info->code > 0) + fprintf(where, "#%d: ", info->code); + if (info->msg) + fputs(info->msg, where); + } + else + { + char *p = info->msg; + unsigned char c; + + if (p) + { + while ((c = *p++)) + { + if (c < ' ') c = ' '; + fputc(c, where); + } + } + } + + if (newline) + fputc('\n', where); +} + +bool ERROR_print(bool can_ignore) +{ + static bool lock = FALSE; + bool ignore = FALSE; + + ERROR_print_at(stderr, FALSE, TRUE); + + if (ERROR_backtrace) + { + print_prefix(stderr); + DEBUG_print_backtrace(ERROR_backtrace); + } + + if (EXEC_main_hook_done && !EXEC_debug && EXEC_Hook.error && !lock) + { + lock = TRUE; + GAMBAS_DoNotRaiseEvent = TRUE; + ignore = HOOK(error)(ERROR_current->info.code, ERROR_current->info.msg, DEBUG_get_position(ERROR_current->info.cp, ERROR_current->info.fp, ERROR_current->info.pc), can_ignore); + GAMBAS_DoNotRaiseEvent = !ignore; + lock = FALSE; + } + + return ignore; +} + +void ERROR_save(ERROR_INFO *save, ERROR_INFO *last) +{ + clear_info(save); + *save = ERROR_current->info; + CLEAR(&ERROR_current->info); + + if (last) + { + clear_info(last); + *last = ERROR_last; + CLEAR(&ERROR_last); + } +} + +void ERROR_restore(ERROR_INFO *save, ERROR_INFO *last) +{ + clear_info(&ERROR_current->info); + ERROR_current->info = *save; + CLEAR(save); + + if (last) + { + clear_info(&ERROR_last); + ERROR_last = *last; + CLEAR(last); + } +} + +void ERROR_set_last(bool bt) +{ + copy_info(&ERROR_current->info, &ERROR_last); + + if (bt && !ERROR_backtrace) + ERROR_backtrace = STACK_get_backtrace(); +} + +void ERROR_define_last(void) +{ + copy_info(&ERROR_last, &ERROR_current->info); +} + +void ERROR_warning(const char *warning, ...) +{ + va_list args; + + va_start(args, warning); + + fflush(NULL); + + fprintf(stderr, "gbx" GAMBAS_VERSION_STRING " [%d]: warning: ", getpid()); + vfprintf(stderr, warning, args); + + va_end(args); + + putc('\n', stderr); +} + +/*void ERROR_deprecated(const char *msg) +{ + ERROR_warning("%s: %s is deprecated.", DEBUG_get_current_position(), msg); +}*/ + +void ERROR_exit(void) +{ + ERROR_reset(&ERROR_last); + STACK_free_backtrace(&ERROR_backtrace); +} + +void ERROR_hook(void) +{ + static bool no_rec = FALSE; + + ERROR_INFO save = { 0 }; + ERROR_INFO last = { 0 }; + STACK_BACKTRACE *save_bt = NULL; + CLASS_DESC_METHOD *handle_error; + + if (no_rec) + return; + + if (PROJECT_class && CLASS_is_loaded(PROJECT_class)) + { + handle_error = (CLASS_DESC_METHOD *)CLASS_get_symbol_desc_kind(PROJECT_class, "Application_Error", CD_STATIC_METHOD, 0, T_VOID); + + if (handle_error) + { + no_rec = TRUE; + + copy_info(&ERROR_current->info, &save); + copy_info(&ERROR_last, &last); + + if (ERROR_backtrace) save_bt = STACK_copy_backtrace(ERROR_backtrace); + + TRY + { + EXEC_public_desc(PROJECT_class, NULL, handle_error, 0); + } + CATCH + { + } + END_TRY + + move_info(&save, &ERROR_current->info); + move_info(&last, &ERROR_last); + + STACK_free_backtrace(&ERROR_backtrace); + if (save_bt) + ERROR_backtrace = save_bt; + + no_rec = FALSE; + } + } +} + +const char *ERROR_get_message(void) +{ + return ERROR_current->info.msg; +} diff --git a/main/gbx/gb_error.h b/main/gbx/gb_error.h new file mode 100644 index 00000000..d532fa02 --- /dev/null +++ b/main/gbx/gb_error.h @@ -0,0 +1,196 @@ +/*************************************************************************** + + gb_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_H +#define __GB_ERROR_H + +#include +#include + +#include "gb_common.h" +#include "gb_limit.h" + +//#define DEBUG_ERROR 1 + +#include "gb_error_common.h" + +enum { + E_ABORT = -2, + E_CUSTOM = -1, + E_UNKNOWN = 0, + E_MEMORY, + E_CLASS, + E_STACK, + E_NEPARAM, + E_TMPARAM, + E_TYPE, + E_OVERFLOW, + E_ILLEGAL, + E_NFUNC, + E_CSTATIC, + E_NSYMBOL, + E_NOBJECT, + E_NULL, + E_STATIC, + E_NREAD, + E_NWRITE, + E_NPROPERTY, + E_NRETURN, + E_MATH, + E_ARG, + E_BOUND, + E_NDIM, + E_NARRAY, + E_MAIN, + E_NNEW, + E_ZERO, + E_LIBRARY, + E_EVENT, + E_IOBJECT, + E_ENUM, + E_UCONV, + E_CONV, + E_DATE, + E_BADPATH, + E_OPEN, + E_PROJECT, + E_FULL, + E_EXIST, + E_EOF, + E_FORMAT, + E_DYNAMIC, + E_SYSTEM, + E_ACCESS, + E_TOOLONG, + E_NEXIST, + E_DIR, + E_READ, + E_WRITE, + E_NDIR, + E_REGEXP, + E_ARCH, + E_REGISTER, + E_CLOSED, + E_VIRTUAL, + E_STOP, + E_STRING, + E_EVAL, + E_LOCK, + E_PARENT, + E_EXTLIB, + E_EXTSYM, + E_BYREF, + E_OVERRIDE, + E_VKEY, + E_SARRAY, + E_EXTCB, + E_SERIAL, + E_CHILD, + E_USER, + E_NEMPTY, + E_UTYPE, + E_FREEREF, + E_ASSERT, + E_MARRAY, + E_UCLASS, + E_SPEC, + E_USIZE + }; + +#ifndef __GB_ERROR_C + +EXTERN ERROR_CONTEXT *ERROR_current; +EXTERN ERROR_INFO ERROR_last; +EXTERN ERROR_HANDLER *ERROR_handler; +EXTERN void *ERROR_backtrace; + +#if DEBUG_ERROR +EXTERN int ERROR_depth; + +void ERROR_debug(const char *msg, ...); +#endif + +#endif + +#define ON_ERROR(_handler) \ + { \ + ERROR_HANDLER __handler; \ + __handler.handler = (_handler); \ + __handler.prev = ERROR_handler; \ + __handler.context = ERROR_current; \ + ERROR_handler = &__handler; + //fprintf(stderr, "%s.%d: ERROR_handler -> %p @ %p\n", __FUNCTION__, __LINE__, ERROR_handler, ERROR_current); + +#define ON_ERROR_1(_handler, _arg1) \ + ON_ERROR(_handler) \ + __handler.arg1 = (intptr_t)(_arg1); + +#define ON_ERROR_2(_handler, _arg1, _arg2) \ + ON_ERROR_1(_handler, _arg1) \ + __handler.arg2 = (intptr_t)(_arg2); + +#define END_ERROR \ + ERROR_handler = __handler.prev; \ + } + //fprintf(stderr, "%s.%d: ERROR_handler <- %p @ %p (%p)\n", __FUNCTION__, __LINE__, ERROR_handler, ERROR_handler ? ERROR_handler->context : NULL, ERROR_current); + +const char *ERROR_get(void); + +void ERROR_define(const char *pattern, char *arg[]); + +void ERROR_propagate(void) NORETURN; + +void THROW(int code, ...) NORETURN; +void THROW_SYSTEM(int err, const char *path); +void THROW_NULL(void) NORETURN; +void THROW_ILLEGAL(void) NORETURN; +void THROW_STACK(void) NORETURN; +void THROW_BOUND(void) NORETURN; +void THROW_CLASS(void *class, char *arg1, char *arg2) NORETURN; +void THROW_MATH(bool zero) NORETURN; +void THROW_OVERFLOW_(void) NORETURN; +#define THROW_OVERFLOW() ({ if (EXEC_check_overflow) THROW_OVERFLOW_(); }) +//#define THROW_OVERFLOW THROW_OVERFLOW_ + +void ERROR_fatal(const char *error, ...) NORETURN; +void ERROR_panic(const char *error, ...) NORETURN; +void ERROR_warning(const char *warning, ...); + +bool ERROR_print(bool); +void ERROR_print_at(FILE *where, bool msgonly, bool newline); +void ERROR_hook(void); +const char *ERROR_get_message(void); + +void ERROR_save(ERROR_INFO *save, ERROR_INFO *last); +void ERROR_restore(ERROR_INFO *save, ERROR_INFO *last); + +void ERROR_reset(ERROR_INFO *info); +void ERROR_lock(void); +void ERROR_unlock(void); +void ERROR_set_last(bool bt); +void ERROR_define_last(void); +//void ERROR_deprecated(const char *msg); + +void ERROR_exit(void); + +#endif diff --git a/main/gbx/gb_file.c b/main/gbx/gb_file.c new file mode 100644 index 00000000..91ac9a67 --- /dev/null +++ b/main/gbx/gb_file.c @@ -0,0 +1,38 @@ +/*************************************************************************** + + gb_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_error.h" + +#include "gbx_value.h" +#include "gbx_exec.h" +#include "gbx_project.h" +#include "gambas.h" +#include "gbx_regexp.h" +#include "gbx_string.h" +#include "gbx_archive.h" +#include "gbx_stream.h" + +#define PROJECT_EXEC + +#include "gb_file_temp.h" diff --git a/main/gbx/gb_file.h b/main/gbx/gb_file.h new file mode 100644 index 00000000..784373a9 --- /dev/null +++ b/main/gbx/gb_file.h @@ -0,0 +1,26 @@ +/*************************************************************************** + + gb_file.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define PROJECT_EXEC + +#include "gb_file_share.h" diff --git a/main/gbx/gb_hash.c b/main/gbx/gb_hash.c new file mode 100644 index 00000000..bc940b12 --- /dev/null +++ b/main/gbx/gb_hash.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_hash.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_hash_temp.h" diff --git a/main/gbx/gb_list.c b/main/gbx/gb_list.c new file mode 100644 index 00000000..281dbe19 --- /dev/null +++ b/main/gbx/gb_list.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_list.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_list_temp.h" diff --git a/main/gbx/gb_table.c b/main/gbx/gb_table.c new file mode 100644 index 00000000..a14ceefd --- /dev/null +++ b/main/gbx/gb_table.c @@ -0,0 +1,28 @@ +/*************************************************************************** + + gb_table.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define PROJECT_EXEC + +#include "gb_common_case.h" +#include "gb_table_temp.h" + diff --git a/main/gbx/gbx.c b/main/gbx/gbx.c new file mode 100644 index 00000000..15c8a352 --- /dev/null +++ b/main/gbx/gbx.c @@ -0,0 +1,541 @@ +/*************************************************************************** + + gbx.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C + +#include "config.h" + +//#define USE_PROFILE 1 + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_error.h" + +#include +#include +#include +#include + +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_stack.h" +#include "gbx_debug.h" +#include "gb_file.h" +#include "gbx_component.h" +#include "gbx_project.h" +#include "gbx_local.h" +#include "gbx_watch.h" +#include "gbx_event.h" +#include "gbx_extern.h" +#include "gbx_eval.h" +#include "gbx_subr.h" +#include "gbx_math.h" +#include "gb_common_buffer.h" +#include "gbx_api.h" +#include "gbx_signal.h" +#include "gbx_jit.h" + +#if USE_PROFILE +#include "gbx_profile.h" +#endif + +#include "gbx_c_file.h" +#include "gbx_c_application.h" +#include "gbx.h" + +extern void _exit(int) NORETURN; +FILE *log_file; + +static bool _welcome = FALSE; +static bool _quit_after_main = FALSE; +static bool _eval = FALSE; +static const char *_tests = NULL; + +static void NORETURN do_exit(int ret, bool immediate) +{ + fflush(NULL); + + if (EXEC_debug_hold) + { + if (ret) ERROR_warning("The process returned %d", ret); + sleep(86400); + } + + if (immediate) + _exit(ret); + else + exit(ret); +} + +static void NORETURN my_exit(int ret) +{ + LOCAL_exit(); + COMPONENT_exit(); + EXTERN_exit(); + //fclose(log_file); + do_exit(ret, FALSE); +} + +static void init(const char *file, int argc, char **argv) +{ + COMPONENT_init(); + FILE_init(); + JIT_init(); + EXEC_init(); + CLASS_init(); + WATCH_init(); + MATH_init(); + PROJECT_init(file); + DEBUG_init(); + + LOCAL_init(); + + if (file) + { + PROJECT_load(); + + if (PROJECT_run_httpd) + COMPONENT_exec("gb.httpd", argc, argv); + + PROJECT_load_finish(); + } + else + STACK_init(); + + if (EXEC_debug) + { + DEBUG.Welcome(); + DEBUG.Main(FALSE); + } + _welcome = TRUE; +} + + +void main_exit(bool silent) +{ + silent |= EXEC_task; + + // If the stack has not been initialized because the project could not be started, do it now + if (!SP) + STACK_init(); + + TRY + { + JIT_abort(); + SIGNAL_exit(); + EXTERN_release(); + STREAM_exit(); + OBJECT_exit(); + CFILE_exit(); + + CLASS_clean_up(silent); + + SUBR_exit(); + DEBUG_exit(); + WATCH_exit(); + #if USE_PROFILE + PROFILE_exit(); + #endif + CLASS_exit(); + COMPONENT_exit(); + EXTERN_exit(); + PROJECT_exit(); + LOCAL_exit(); + EVENT_exit(); + FILE_exit(); + STACK_exit(); + JIT_exit(); + ERROR_exit(); + } + CATCH + { + if (!silent) + ERROR_print_at(stderr, _eval, TRUE); + do_exit(1, TRUE); + } + END_TRY + + STRING_exit(); +} + + +void NORETURN MAIN_exit(bool silent, int ret) +{ + main_exit(silent); + do_exit(ret, TRUE); +} + + +static bool is_option(const char *arg, char option) +{ + return (arg[0] == '-' && arg[1] == option && arg[2] == 0); +} + +static bool is_long_option(const char *arg, char option, const char *long_option) +{ + if (is_option(arg, option)) + return TRUE; + else + return (arg[0] == '-' && arg[1] == '-' && !strcmp(&arg[2], long_option)); +} + +static bool is_option_arg(char **argv, int argc, int *i, char option, const char *long_option, const char **param) +{ + if (long_option) + { + if (!is_long_option(argv[*i], option, long_option)) + return FALSE; + } + else + { + if (!is_option(argv[*i], option)) + return FALSE; + } + + if (*i < (argc - 1) && *argv[*i + 1] != '-') + { + *param = argv[*i + 1]; + (*i)++; + } + else + *param = NULL; + + return TRUE; +} + +static void print_version() +{ +#ifdef TRUNK_VERSION + printf(VERSION " " TRUNK_VERSION "\n"); +#else /* no TRUNK_VERSION */ + printf(VERSION "\n"); +#endif +} + +static void print_title() +{ + printf("\nGambas interpreter version "); + print_version(); +} + +int main(int argc, char *argv[]) +{ + CLASS_DESC_METHOD *startup = NULL; + int i, n; + char *file = NULL; + int ret = 0; + const char *redirect_stderr = NULL; + + //char log_path[256]; + //sprintf(log_path, "/tmp/gambas-%d.log", getuid()); + //log_file = freopen(log_path, "w+", stderr); + //fprintf(stderr, "Fichier log Gambas\n"); + + /*struct rlimit rl = { 64000000, 64000000 }; + if (setrlimit(RLIMIT_CORE, &rl)) + perror(strerror(errno));*/ + + MEMORY_init(); + COMMON_init(); + //STRING_init(); + + EXEC_arch = (strcmp(FILE_get_name(argv[0]), "gbr" GAMBAS_VERSION_STRING) == 0); + + if (argc == 2) + { + if (is_long_option(argv[1], 'h', "help")) + { + print_title(); + + if (EXEC_arch) + { + printf( + "\nExecute a Gambas executable.\n" + "\n gbr" GAMBAS_VERSION_STRING " [options] []\n\n" + ); + } + else + { + printf( + "\nExecute a Gambas project.\n" + "\n gbx" GAMBAS_VERSION_STRING " [options] [] [-- ]\n" + "\nEvaluate a Gambas expression.\n" + "\n gbx" GAMBAS_VERSION_STRING " -e \n\n" + ); + } + + printf("Options:\n\n"); + + printf( + " -a override application path\n" + ); + + if (!EXEC_arch) + printf(" -e evaluate an expression\n"); + + printf( + " -g enter debugging mode\n" + " -h --help display this help\n" + " -H --httpd run through an embedded http server\n" + " -j disable just-in-time compiler\n" + " -k do not unload shared libraries\n" + " -L --license display license\n" + " -p activate profiling and debugging mode\n" + " -r redirect standard error output\n" + " -s override startup class\n" + " -t --trace dump the position of each executed line of code\n" + " -T list all test modules\n" + " -T run the specified test modules\n" + " -V --version display version\n" + "\n" + ); + + my_exit(0); + } + else if (is_long_option(argv[1], 'V', "version")) + { + print_version(); + my_exit(0); + } + else if (is_long_option(argv[1], 'L', "license")) + { + print_title(); + printf(COPYRIGHT); + my_exit(0); + } + } + + if (!EXEC_arch && argc >= 2 && is_option(argv[1], 'e')) + { + if (argc < 3) + ERROR_fatal("-e option needs an expression."); + + _eval = TRUE; + + TRY + { + init(NULL, argc, argv); + EVAL_string(argv[2]); + } + CATCH + { + if (ERROR_current->info.code && ERROR_current->info.code != E_ABORT) + ERROR_print_at(stderr, TRUE, TRUE); + MAIN_exit(TRUE, 1); + } + END_TRY + + MAIN_exit(FALSE, 0); + } + + for (i = 1; i < argc; i++) + { + if (is_option(argv[i], 'g')) + { + EXEC_debug = TRUE; + } + else if (is_option_arg(argv, argc, &i, 'p', NULL, &EXEC_profile_path)) + { + EXEC_debug = TRUE; + EXEC_profile = TRUE; + } + else if (is_long_option(argv[i], 't', "trace")) + { + EXEC_trace = TRUE; + } + else if (is_option_arg(argv, argc, &i, 'f', NULL, &EXEC_fifo_name)) + { + EXEC_fifo = TRUE; + } + else if (is_option_arg(argv, argc, &i, 's', NULL, &PROJECT_startup)) + { + continue; + } + else if (is_option_arg(argv, argc, &i, 'r', NULL, &redirect_stderr)) + { + int fd = open(redirect_stderr, O_WRONLY | O_CLOEXEC); + if (fd < 0) + ERROR_fatal("cannot redirect stderr."); + dup2(fd, STDERR_FILENO); + } + else if (is_option(argv[i], 'k')) + { + EXEC_keep_library = TRUE; + } + else if (is_option(argv[i], 'q')) + { + _quit_after_main = TRUE; + } + else if (is_long_option(argv[i], 'H', "httpd")) + { + PROJECT_run_httpd = TRUE; + } + else if (is_option(argv[i], 'j')) + { + JIT_disabled = TRUE; + } + else if (is_option_arg(argv, argc, &i, 'T', "test", &_tests)) + { + PROJECT_run_tests = TRUE; + } + else if (is_option_arg(argv, argc, &i, 'a', NULL, &PROJECT_override)) + { + } + else if (is_option(argv[i], '-')) + { + i++; + break; + } + else + { + if (file) + ERROR_fatal("too many %s.", EXEC_arch ? "executable files" : "project directories"); + + file = argv[i]; + + if (EXEC_arch) + { + i++; + break; + } + } + } + + n = i; + if (!file) + file = "."; + + if (EXEC_arch) + argv[0] = file; + + for (i = 1; i <= (argc - n); i++) + argv[i] = argv[i + n - 1]; + + argc -= n - 1; + + TRY + { + init(file, argc, argv); + + if (!EXEC_arch) + argv[0] = PROJECT_name; + + HOOK(main)(&argc, &argv); + EXEC_main_hook_done = TRUE; + + /* Startup class */ + + CLASS_load(PROJECT_class); + startup = (CLASS_DESC_METHOD *)CLASS_get_symbol_desc_kind(PROJECT_class, "main", CD_STATIC_METHOD, 0, T_ANY); + if (startup == NULL) + THROW(E_MAIN); + + //CAPP_init(); /* needs startup class */ + CFILE_init_watch(); + + PROJECT_argc = argc; + PROJECT_argv = argv; + } + CATCH + { + ERROR_hook(); + + if (EXEC_debug && DEBUG_is_init()) + { + if (!_welcome) + DEBUG.Main(TRUE); + DEBUG.Main(TRUE); + MAIN_exit(FALSE, 0); + } + else + { + if (ERROR->info.code && ERROR->info.code != E_ABORT) + ERROR_print(FALSE); + MAIN_exit(TRUE, 1); + } + } + END_TRY + + TRY + { + if (PROJECT_run_tests) + { + GB_Push(1, T_STRING, _tests, -1); + EXEC_public_desc(PROJECT_class, NULL, startup, 1); + } + else + { + EXEC_public_desc(PROJECT_class, NULL, startup, 0); + } + + if (TYPE_is_boolean(startup->type)) + ret = RP->_boolean.value ? 1 : 0; + else if (TYPE_is_integer(startup->type)) + ret = RP->_integer.value & 0xFF; + + EXEC_release_return_value(); + + if (_quit_after_main) + MAIN_exit(TRUE, 0); + + if (!ret) + { + DEBUG_enter_event_loop(); + HOOK_DEFAULT(loop, WATCH_loop)(); + DEBUG_leave_event_loop(); + EVENT_check_post(); + } + } + CATCH + { + ret = EXEC_quit_value; + + if (ERROR->info.code) + { + if (ERROR->info.code == E_ABORT) + do_exit(ret, TRUE); + + ERROR_hook(); + + if (EXEC_debug) + { + DEBUG.Main(TRUE); + MAIN_exit(TRUE, 0); + } + else + { + ERROR_print(FALSE); + MAIN_exit(TRUE, 1); + } + } + } + END_TRY + + main_exit(FALSE); + + if (!EXEC_task) + MEMORY_exit(); + + do_exit(ret, FALSE); +} + diff --git a/main/gbx/gbx.h b/main/gbx/gbx.h new file mode 100644 index 00000000..90dacdcc --- /dev/null +++ b/main/gbx/gbx.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + gbx.h + + (c) 2000-2019 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_H +#define __GBX_H + +#include "gb_common.h" + +void NORETURN MAIN_exit(bool silent, int ret); + +#endif diff --git a/main/gbx/gbx_api.c b/main/gbx/gbx_api.c new file mode 100644 index 00000000..a1ad79a4 --- /dev/null +++ b/main/gbx/gbx_api.c @@ -0,0 +1,2613 @@ +/*************************************************************************** + + gbx_api.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_API_C + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_buffer.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_list.h" + +#include + +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_stack.h" +#include "gbx_stream.h" +#include "gbx_library.h" +#include "gbx_watch.h" +#include "gbx_project.h" +#include "gbx_eval.h" +#include "gbx_local.h" +#include "gb_hash.h" +#include "gb_file.h" +#include "gbx_number.h" +#include "gbx_object.h" +#include "gbx_string.h" +#include "gbx_date.h" +#include "gbx_regexp.h" +#include "gbx_c_array.h" +#include "gbx_c_timer.h" +#include "gbx_component.h" +#include "gbx_c_gambas.h" +#include "gbx_c_observer.h" +#include "gbx_debug.h" +#include "gbx_c_file.h" +#include "gbx_extern.h" +#include "gbx_compare.h" +#include "gbx_subr.h" +#include "gbx_math.h" +#include "gbx_struct.h" +#include "gbx_signal.h" +#include "gbx_jit.h" +#include "gbx_split.h" +#include "gbx.h" +#include "gambas.h" +#include "gbx_api.h" + +const void *const GAMBAS_Api[] = +{ + (void *)GB_VERSION, + + (void *)GB_GetInterface, + + (void *)GB_Hook, + + (void *)GB_LoadComponent, + (void *)COMPONENT_can_load_library, + (void *)COMPONENT_exist, + (void *)COMPONENT_is_loaded, + (void *)GB_CurrentComponent, + (void *)COMPONENT_get_info, + (void *)COMPONENT_signal, + (void *)LIBRARY_declare_one, + + (void *)GB_Push, + (void *)GB_GetFunction, + (void *)GB_Call, + (void *)GB_GetClassInterface, + (void *)GB_GetProperty, + (void *)GB_SetProperty, + (void *)GB_Serialize, + (void *)GB_UnSerialize, + + (void *)WATCH_one_loop, + (void *)GB_Wait, + (void *)EVENT_post, + (void *)EVENT_post2, + (void *)CTIMER_every, + (void *)GB_RaiseEvent, + (void *)GB_RaiseBegin, + (void *)GB_RaiseEnd, + (void *)EVENT_post_event, + (void *)EVENT_check_post, + (void *)GB_CanRaise, + (void *)GB_GetEvent, + (void *)GB_GetLastEventName, + (void *)CTIMER_raise, + (void *)GB_HasActiveTimer, + (void *)GB_Stopped, + (void *)GB_IsRaiseLocked, + + (void *)GB_NParam, + (void *)GB_Conv, + (void *)GB_GetUnknown, + + (void *)GB_Error, + (void *)GB_HasError, + (void *)GB_GetErrorMessage, + (void *)ERROR_propagate, + (void *)GB_Deprecated, + (void *)GB_OnErrorBegin, + (void *)GB_OnErrorEnd, + + (void *)GB_GetClass, + (void *)GB_GetClassName, + (void *)GB_ExistClass, + (void *)GB_FindClass, + (void *)GB_ExistClassLocal, + (void *)GB_FindClassLocal, + (void *)GB_GetArrayType, + (void *)CLASS_get_array_class, + (void *)CLASS_find_load_from, + (void *)GB_Is, + (void *)GB_Ref, + (void *)GB_Unref, + //(void *)GB_UnrefKeep, + (void *)GB_Detach, + (void *)GB_Attach, + (void *)OBJECT_parent, + (void *)GB_Create, + (void *)GB_New, + (void *)CLASS_auto_create, + (void *)GB_CheckObject, + (void *)OBJECT_is_locked, + + (void *)GB_GetEnum, + (void *)GB_StopEnum, + (void *)GB_BeginEnum, + (void *)GB_EndEnum, + (void *)GB_NextEnum, + (void *)GB_StopAllEnum, + (void *)GB_OnFreeEnum, + + (void *)GB_GetReturnValue, + (void *)GB_Return, + (void *)GB_ReturnInteger, + (void *)GB_ReturnLong, + (void *)GB_ReturnPointer, + (void *)GB_ReturnBoolean, + (void *)GB_ReturnDate, + (void *)GB_ReturnObject, + (void *)GB_ReturnNull, + (void *)GB_ReturnSingle, + (void *)GB_ReturnFloat, + (void *)GB_ReturnVariant, + (void *)GB_ReturnConvVariant, + (void *)GB_ReturnBorrow, + (void *)GB_ReturnRelease, + (void *)GB_ReturnPtr, + (void *)GB_ReturnSelf, + + (void *)GB_ReturnString, + (void *)GB_ReturnVoidString, + (void *)GB_ReturnConstString, + (void *)GB_ReturnConstZeroString, + (void *)GB_ReturnNewString, + (void *)GB_ReturnNewZeroString, + + (void *)STRING_new, + (void *)GB_NewZeroString, + (void *)GB_TempString, + (void *)GB_RefString, + (void *)GB_FreeString, + (void *)STRING_free_later, + (void *)STRING_extend, + (void *)STRING_add, + (void *)STRING_add_char, + (void *)GB_StringLength, + (void *)GB_ToZeroString, + (void *)REGEXP_match, + (void *)NUMBER_from_string, + (void *)GB_NumberToString, + (void *)LOCAL_gettext, + + (void *)STRING_subst, + (void *)STRING_subst_add, + (void *)STRING_subst_add_unquote, + (void *)STRING_make, + (void *)GB_ConvString, + (void *)STRING_conv_file_name, + (void *)GB_RealFileName, + + (void *)GB_LoadFile, + (void *)STREAM_unmap, + (void *)GB_TempDir, + (void *)GB_TempFile, + (void *)GB_CopyFile, + (void *)GB_BrowseProject, + (void *)GB_BrowseDirectory, + (void *)GB_StatFile, + + (void *)GB_Store, + (void *)GB_StoreString, + (void *)GB_StoreObject, + (void *)GB_StoreVariant, + (void *)VALUE_read, + (void *)GB_BorrowValue, + (void *)GB_ReleaseValue, + (void *)COMPARE_variant, + + (void *)GB_SplitDate, + (void *)GB_MakeDate, + (void *)DATE_from_time, + (void *)DATE_timer, + (void *)DATE_from_string, + + (void *)GB_Watch, + + (void *)GB_Eval, + + (void *)GB_Alloc, + (void *)GB_AllocZero, + (void *)GB_Free, + (void *)GB_Realloc, + + (void *)GB_NewArray, + (void *)ARRAY_delete, + (void *)GB_CountArray, + (void *)GB_Add, + (void *)ARRAY_insert_many, + (void *)ARRAY_remove_many, + + (void *)GB_tolower, + (void *)GB_toupper, + (void *)strcasecmp, + (void *)strncasecmp, + + (void *)GB_AppName, + (void *)GB_AppTitle, + (void *)GB_AppVersion, + (void *)GB_AppPath, + (void *)GB_AppStartupClass, + + (void *)GB_SystemCharset, + (void *)LOCAL_get_lang, + (void *)LOCAL_set_lang, + (void *)GB_SystemDomainName, + (void *)GB_IsRightToLeft, + (void *)GB_SystemPath, + (void *)GB_SystemHasForked, + (void *)GB_SystemDebug, + (void *)FILE_get_home, + (void *)DATE_get_timezone, + + (void *)GB_ArrayNew, + (void *)GB_ArrayCount, + (void *)GB_ArrayAdd, + (void *)GB_ArrayGet, + (void *)GB_ArrayType, + (void *)GB_ArraySetReadOnly, + + (void *)GB_CollectionNew, + (void *)GB_CollectionCount, + (void *)GB_CollectionSet, + (void *)GB_CollectionGet, + (void *)GB_CollectionEnum, + + (void *)GB_HashTableNew, + (void *)HASH_TABLE_delete, + (void *)HASH_TABLE_size, + (void *)GB_HashTableAdd, + (void *)GB_HashTableRemove, + (void *)GB_HashTableGet, + (void *)GB_HashTableEnum, + (void *)GB_HashTableFirst, + + (void *)GB_StreamGet, + (void *)GB_StreamSetSwapping, + (void *)GB_StreamSetAvailableNow, + (void *)GB_StreamBlock, + (void *)GB_StreamRead, + (void *)GB_StreamWrite, + (void *)STREAM_get_readable, + (void *)STREAM_eof, + (void *)STREAM_handle, + + (void *)STRING_start_len, + (void *)STRING_end, + (void *)STRING_make, + (void *)STRING_split, + + (void *)DEBUG_get_current_position, + (void *)DEBUG_enter_event_loop, + (void *)DEBUG_leave_event_loop, + + (void *)SIGNAL_register, + (void *)SIGNAL_unregister, + (void *)SIGNAL_must_check, + + (void *)LIST_insert, + (void *)LIST_remove, + + NULL +}; + +const void *const GAMBAS_DebugApi[] = +{ + (void *)GB_DebugGetExec, + (void *)STACK_get_frame, + (void *)ERROR_get_message, + (void *)ERROR_save, + (void *)ERROR_restore, + (void *)VALUE_to_local_string, + (void *)LOCAL_format_date, + (void *)LOCAL_format_number, + (void *)DEBUG_get_value, + (void *)DEBUG_set_value, + (void *)CARRAY_get_value, + (void *)DEBUG_enum_keys, + (void *)CLASS_get_next_sorted_symbol, + (void *)DEBUG_get_object_access_type, + (void *)DEBUG_find_class, + (void *)CARRAY_get_array_bounds, + (void *)GB_DebugBreakOnError, + (void *)DEBUG_enter_eval, + (void *)DEBUG_leave_eval, + (void *)GB_DebugInside, + (void *)GB_DebugHold, + NULL +}; + +const void *const GAMBAS_JitApi[] = +{ + (void *)&SP, + (void *)&EXEC_current, + (void *)&TEMP, + (void *)&EXEC_super, + (void *)JIT_debug, + (void *)JIT_get_code, + (void *)THROW, + (void *)THROW_TYPE, + (void *)JIT_get_constant, + (void *)JIT_get_class_ref, + (void *)EXEC_subr_table, + (void *)STRING_char_table, + (void *)EXEC_unborrow, + (void *)EXEC_new, + (void *)EXEC_push_array, + (void *)EXEC_pop_array, + (void *)VALUE_convert, + (void *)EXEC_push_unknown, + (void *)JIT_call_unknown, + (void *)EXEC_pop_unknown, + (void *)EXEC_enum_first, + (void *)EXEC_enum_next, + (void *)SYMBOL_find, + (void *)JIT_load_class, + (void *)JIT_load_class_without_init, + (void *)&ERROR_current, + (void *)&ERROR_handler, + (void *)ERROR_reset, + (void *)ERROR_set_last, + (void *)EXEC_set_got_error, + (void *)&EVENT_Last, + (void *)EXEC_push_complex, + (void *)EXEC_push_vargs, + (void *)EXEC_drop_vargs, + (void *)EXEC_quit, + (void *)EXEC_push_unknown_event, + (void *)EXTERN_get_addr, + (void *)DEBUG_get_position, + (void *)RELEASE_many, + (void *)CSTRUCT_create_static, + (void *)CARRAY_create_static, + (void *)CARRAY_get_array_class, + (void *)JIT_add_string_local, + (void *)JIT_add_string_global, + (void *)VALUE_class_write, + (void *)SUBR_poke, + (void *)OBJECT_get_addr, + NULL +}; + + +bool GAMBAS_DoNotRaiseEvent = FALSE; +bool GAMBAS_StopEvent = FALSE; +bool GAMBAS_RaiseEventCanPropagate = FALSE; + +static bool _event_stopped = FALSE; +static int _raise_event_level = 0; + +#define CATCH_ERROR \ + bool ret = FALSE; \ + TRY + +#define END_CATCH_ERROR \ + CATCH \ + { \ + ret = TRUE; \ + } \ + END_TRY \ + if (ret) EXEC_set_native_error(TRUE); \ + return ret; + +#define CATCH_ERROR_INT \ + int ret = 0; \ + TRY + +#define END_CATCH_ERROR_INT \ + CATCH \ + { \ + ret = -1; \ + } \ + END_TRY \ + if (ret < 0) EXEC_set_native_error(TRUE); \ + return ret; + +bool GB_GetInterface(const char *name, int version, void *iface) +{ + GB_LoadComponent(name); + + if (LIBRARY_get_interface_by_name(name, version, iface)) + ERROR_panic("Cannot find interface of library '%s'", name); + + return FALSE; +} + + +void *GB_Hook(int type, void *hook) +{ + void *old_hook; + void **phook = (void **)(void *)&EXEC_Hook; + + if ((type < 0) || (type > GB_HOOK_MAX)) + return NULL; + + type--; + old_hook = phook[type]; + if (hook) + phook[type] = hook; + + type++; + if (type == GB_HOOK_LOOP && !old_hook) + { + if (phook[GB_HOOK_WATCH]) + WATCH_transfer_watch(); + if (phook[GB_HOOK_TIMER]) + WATCH_transfer_timer(); + } + + return old_hook; +} + + +bool GB_HasActiveTimer(void) +{ + return CTIMER_active_count > 0; +} + + +bool GB_LoadComponent(const char *name) +{ + CATCH_ERROR + { + COMPONENT *comp = COMPONENT_create(name); + COMPONENT_load(comp); + } + END_CATCH_ERROR +} + + +static void push(int nval, va_list args) +{ + TYPE type; + GB_VARIANT_VALUE *val; + + STACK_check(nval); + + while (nval) + { + type = va_arg(args, int); + SP->type = type; + + switch(type) + { + case T_INTEGER: + case T_BOOLEAN: + SP->_integer.value = va_arg(args, int); + break; + + case T_LONG: + SP->_long.value = va_arg(args, int64_t); + break; + + case T_STRING: + SP->type = T_CSTRING; + SP->_string.addr = va_arg(args, char *); + SP->_string.start = 0; + SP->_string.len = va_arg(args, int); + if (SP->_string.len <= 0 && SP->_string.addr) + SP->_string.len = strlen(SP->_string.addr); + break; + + case T_FLOAT: + SP->_float.value = va_arg(args, double); + break; + + case T_OBJECT: + SP->_object.object = va_arg(args, void *); + OBJECT_REF_CHECK(SP->_object.object); + break; + + case T_VARIANT: + val = va_arg(args, GB_VARIANT_VALUE *); + SP->_variant.vtype = val->type; + SP->_variant.value.data = val->value.data; + BORROW(SP); + break; + + default: + ERROR_panic("GB.Push: unknown datatype"); + break; + } + + SP++; + nval--; + } +} + + +void GB_Push(int nval, ...) +{ + va_list args; + + va_start(args, nval); + push(nval, args); + va_end(args); +} + +static void error(int code, CLASS *class, const char *name) +{ + GB_Error((char *)(intptr_t)code, CLASS_get_name(class), name); +} + +static CLASS_DESC *get_desc(CLASS *class, const char *name) +{ + int index; + + index = CLASS_find_symbol(class, name); + + if (index == NO_SYMBOL) + { + error(E_NSYMBOL, class, name); + return NULL; + } + else + return class->table[index].desc; +} + +void *GB_GetProperty(void *object, const char *name) +{ + CLASS_DESC *desc; + CLASS *class; + char type; + + //if (GB_CheckObject(object)) + // return; + + if (OBJECT_is_class(object)) + { + class = (CLASS *)object; + object = NULL; + } + else + { + class = OBJECT_class(object); + } + + desc = get_desc(class, name); + + if (!desc) + return NULL; + + type = CLASS_DESC_get_type(desc); + + if (type == CD_PROPERTY || type == CD_PROPERTY_READ || type == CD_VARIABLE) + { + if (!object) + { + error(E_DYNAMIC, class, name); + return NULL; + } + } + else if (type == CD_STATIC_PROPERTY || type == CD_STATIC_PROPERTY_READ || type == CD_STATIC_VARIABLE || type == CD_CONSTANT) + { + if (object) + { + error(E_STATIC, class, name); + return NULL; + } + } + else if (type == CD_PROPERTY_WRITE || type == CD_STATIC_PROPERTY_WRITE) + { + error(E_NREAD, class, name); + return NULL; + } + else + { + error(E_NPROPERTY, class, name); + return NULL; + } + + if (type == CD_VARIABLE) + VALUE_read(&TEMP, (char *)object + desc->variable.offset, desc->property.type); + else if (type == CD_STATIC_VARIABLE) + VALUE_read(&TEMP, (char *)class->stat + desc->variable.offset, desc->property.type); + else if (type == CD_CONSTANT) + VALUE_read(&TEMP, (void *)&desc->constant.value, desc->constant.type); + else + { + if (desc->property.native) + { + if (EXEC_call_native(desc->property.read, object, desc->property.type, 0)) + { + EXEC_set_native_error(TRUE); + return NULL; + } + } + else + { + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 0; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.read; + //EXEC.func = &class->load->func[(long)desc->property.read]; + + EXEC_function_keep(); + + EXEC_move_ret_to_temp(); + } + } + + return &TEMP; +} + +bool GB_SetProperty(void *object, const char *name, GB_VALUE *val) +{ + VALUE *value = (VALUE *)val; + CLASS_DESC *desc; + CLASS *class; + char type; + + if (OBJECT_is_class(object)) + { + class = (CLASS *)object; + object = NULL; + } + else + { + class = OBJECT_class(object); + } + + desc = get_desc(class, name); + + if (!desc) + return TRUE; + + type = CLASS_DESC_get_type(desc); + + if (type == CD_PROPERTY || type == CD_VARIABLE || type == CD_PROPERTY_WRITE) + { + if (!object) + { + if (!class->auto_create) + { + error(E_DYNAMIC, class, name); + return TRUE; + } + + object = EXEC_auto_create(class, TRUE); + } + } + else if (type == CD_STATIC_PROPERTY || type == CD_STATIC_VARIABLE || type == CD_STATIC_PROPERTY_WRITE) + { + if (object) + { + error(E_STATIC, class, name); + return TRUE; + } + } + else if (type == CD_PROPERTY_READ || type == CD_STATIC_PROPERTY_READ) + { + error(E_NWRITE, class, name); + return TRUE; + } + else + { + error(E_NPROPERTY, class, name); + return TRUE; + } + + if (type == CD_VARIABLE) + VALUE_write(value, (char *)object + desc->variable.offset, desc->variable.type); + else if (type == CD_STATIC_VARIABLE) + VALUE_write(value, (char *)class->stat + desc->variable.offset, desc->variable.type); + else + { + if (desc->property.native) + { + VALUE_conv(value, desc->property.type); + + if (EXEC_call_native(desc->property.write, object, 0, value)) + { + EXEC_set_native_error(TRUE); + return TRUE; + } + } + else + { + *SP = *value; + BORROW(SP); + SP++; + + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 1; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.write; + //EXEC.func = &class->load->func[(long)desc->property.write]; + + EXEC_function(); + + /*VALUE_write(value, OBJECT_get_prop_addr(object, desc), desc->property.type);*/ + } + } + + return FALSE; +} + + +bool GB_CanRaise(void *object, int event_id) +{ + ushort *event_tab; + int func_id; + COBSERVER *obs; + + if (!object || !OBJECT_has_events(object)) + return FALSE; + + LIST_for_each(obs, OBJECT_event(object)->observer) + { + if (OBJECT_parent(obs) && obs->event && obs->event[event_id]) + return TRUE; + } + + if (!OBJECT_parent(object)) + return FALSE; + + event_tab = OBJECT_event(object)->event; + func_id = event_tab[event_id]; + + return (func_id != 0); +} + +bool GB_IsRaiseLocked(void *object) +{ + COBSERVER *obs; + + if (GAMBAS_DoNotRaiseEvent) + return TRUE; + + if (!object || !OBJECT_has_events(object)) + return TRUE; + + LIST_for_each(obs, OBJECT_event(object)->observer) + { + if (OBJECT_active_parent(obs)) + return FALSE; + } + + return OBJECT_active_parent(object) == NULL; +} + + +static int get_event_func_id(ushort *event_tab, int event_id) +{ + int func_id; + + if (!event_tab) + return 0; + + func_id = event_tab[event_id]; + if (!func_id) + return 0; + + return func_id; +} + +static bool raise_event(OBJECT *observer, void *object, int func_id, int nparam, bool can_propagate) +{ + bool stop_event; + CLASS *class; + CLASS_DESC_METHOD *desc; + void *old_last; + bool result; + uint stack_barrier; + + func_id--; + + if (OBJECT_is_class(observer)) + { + class = (CLASS *)observer; //OBJECT_class(object); + observer = NULL; + } + else + class = OBJECT_class(observer); + + desc = &class->table[func_id].desc->method; + + old_last = EVENT_Last; + EVENT_Last = object; + //OBJECT_REF(object, "raise_event"); + +// if (arg) +// { +// EXEC_dup(nparam); +// } +// else +// { +// va_start(args, nparam); +// push(nparam, args); +// va_end(args); +// } + + stop_event = GAMBAS_StopEvent; + GAMBAS_StopEvent = FALSE; + + stack_barrier = STACK_push_barrier(); + + TRY + { + EXEC_public_desc(class, observer, desc, nparam); + } + CATCH + { + // Errors cannot be propagated outside of an event handler, because + // the event may result from a library signal that do not support + // exceptions (like the glib library). + + if (ERROR->info.code && ERROR->info.code != E_ABORT && !(can_propagate && STACK_has_error_handler())) + { + ERROR_hook(); + + if (EXEC_debug) + { + DEBUG.Main(TRUE); + MAIN_exit(TRUE, 0); + } + else + { + if (!ERROR_print(TRUE)) + MAIN_exit(TRUE, 1); + } + } + else + { + EVENT_Last = old_last; + GAMBAS_StopEvent = stop_event; + STACK_pop_barrier(stack_barrier); + PROPAGATE(); + } + } + END_TRY + + if (RP->type == T_VOID) + result = FALSE; + else + result = RP->_boolean.value != 0; + + if (GAMBAS_StopEvent) + result = TRUE; + + EVENT_Last = old_last; + GAMBAS_StopEvent = stop_event; + STACK_pop_barrier(stack_barrier); + + EXEC_release_return_value(); + + return result; +} + +// If nparam < 0, the args are already on the stack + +static GB_RAISE_HANDLER *_GB_Raise_handler = NULL; + +static void error_GB_Raise(void *object, intptr_t nparam) +{ + RELEASE_MANY(SP, (int)nparam); + OBJECT_UNREF(object); + + _raise_event_level--; + + if (_GB_Raise_handler && _GB_Raise_handler->level == _raise_event_level) + { + (*_GB_Raise_handler->callback)(_GB_Raise_handler->data); + _GB_Raise_handler = _GB_Raise_handler->old; + } +} + +void GB_RaiseBegin(GB_RAISE_HANDLER *handler) +{ + handler->old = _GB_Raise_handler; + handler->level = _raise_event_level; + _GB_Raise_handler = handler; +} + +void GB_RaiseEnd(GB_RAISE_HANDLER *handler) +{ + _GB_Raise_handler = handler->old; +} + +bool GB_RaiseEvent(void *object, int event_id, int nparam, ...) +{ + OBJECT *parent; + int func_id; + bool result; + va_list args; + bool arg; + COBSERVER *obs; + bool can_propagate; + + can_propagate = GAMBAS_RaiseEventCanPropagate; + GAMBAS_RaiseEventCanPropagate = FALSE; + + if (GAMBAS_DoNotRaiseEvent) + return FALSE; + + if (!OBJECT_is_valid(object) || !OBJECT_has_events(object)) + return FALSE; + + OBJECT_REF(object); + + arg = nparam < 0; + nparam = abs(nparam); + + _raise_event_level++; + + ON_ERROR_2(error_GB_Raise, object, nparam) + { + if (!arg) + { + va_start(args, nparam); + push(nparam, args); + va_end(args); + } + + result = FALSE; + + // Observers before + + LIST_for_each(obs, OBJECT_event(object)->observer) + { + parent = OBJECT_active_parent(obs); + if (!parent) + continue; + if (obs->after) + continue; + //if (obs->object != object) + // continue; + + func_id = get_event_func_id(obs->event, event_id); + if (!func_id) + continue; + + if (!OBJECT_is_valid(parent)) + { + OBJECT_detach((OBJECT *)obs); + continue; + } + + EXEC_dup(nparam); + result = raise_event(parent, object, func_id, nparam, can_propagate); + + if (result) + goto __RETURN; + } + + // Parent + + parent = OBJECT_active_parent(object); + if (parent) + { + func_id = get_event_func_id(OBJECT_event(object)->event, event_id); + #if DEBUG_EVENT + CLASS *class = OBJECT_class(object); + fprintf(stderr, "GB_Raise(%p, %d, %s)\n", object, event_id, class->event[event_id].name); + fprintf(stderr, "func_id = %d parent = (%s %p)\n", func_id, parent->class->name, parent); + if (OBJECT_is_locked(parent)) + fprintf(stderr, "parent is locked!\n"); + #endif + if (func_id) + { + if (!OBJECT_is_valid(parent)) + OBJECT_detach(object); + else + { + EXEC_dup(nparam); + result = raise_event(parent, object, func_id, nparam, can_propagate); + if (result) + goto __RETURN; + } + } + } + + // Observers after + + LIST_for_each(obs, OBJECT_event(object)->observer) + { + parent = OBJECT_active_parent(obs); + if (!parent) + continue; + if (!obs->after) + continue; + //if (obs->object != object) + // continue; + + func_id = get_event_func_id(obs->event, event_id); + if (!func_id) + continue; + + if (!OBJECT_is_valid(parent)) + { + OBJECT_detach((OBJECT *)obs); + continue; + } + + EXEC_dup(nparam); + result = raise_event(parent, object, func_id, nparam, can_propagate); + if (result) + goto __RETURN; + } + +__RETURN: + + RELEASE_MANY(SP, nparam); + OBJECT_UNREF(object); + } + END_ERROR + + _raise_event_level--; + + return result; +} + + +bool GB_GetFunction(GB_FUNCTION *func, void *object, const char *name, const char *sign, const char *type) +{ + char len_min, nparam, npvar; + TYPE *tsign; + TYPE tret; + int index; + CLASS *class; + int kind; + CLASS_DESC *desc; + bool error; + const char *err; + + if (OBJECT_is_class(object)) + { + class = (CLASS *)object; + kind = CD_STATIC_METHOD; + } + else + { + class = OBJECT_class(object); + kind = CD_METHOD; + } + + CLASS_load(class); + + index = CLASS_find_symbol(class, name); + if (index == NO_SYMBOL) + { + err = "Symbol not found"; + goto _NOT_FOUND; + } + + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) != kind) + { + err = kind == CD_METHOD ? "Not a method" : "Not a static method"; + goto _NOT_FOUND; + } + + if (sign) + { + TYPE_signature_length(sign, &len_min, &nparam, &npvar); + tsign = NULL; + + if (nparam) + { + ALLOC(&tsign, nparam * sizeof(TYPE)); + tsign = TYPE_transform_signature(&tsign, sign, nparam); + } + + error = TYPE_compare_signature(desc->method.signature, desc->method.npmax, tsign, nparam, FALSE); + + if (nparam) + FREE(&tsign); + + if (error) + { + err = "Parameters do not match"; + goto _NOT_FOUND; + } + } + + if (type) + { + tret = TYPE_from_string(&type); + if (tret != desc->method.type) + { + if (tret == T_VOID) + err = "Must be a procedure"; + else if (desc->method.type == T_VOID) + err = "Must be a function"; + else + err = "Return type does not match"; + + goto _NOT_FOUND; + } + } + + func->object = object; + func->index = index + 1; + + return FALSE; + +_NOT_FOUND: + + GB_Error("Unable to find method &1 in class &2. &3", name, CLASS_get_name(class), err); + func->object = NULL; + func->index = 0; + return TRUE; +} + + +void *GB_GetClassInterface(void *_class, const char *_name) +{ + CLASS_DESC *desc; + int index; + CLASS *class = (CLASS *)_class; + int len = strlen(_name); + char name[len + 4]; + + CLASS_load(class); + + strcpy(name, "_@"); + strcat(name, _name); + + index = CLASS_find_symbol(class, name); + if (index == NO_SYMBOL) + goto __NOT_FOUND; + + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_CONSTANT) + goto __NOT_FOUND; + + if (desc->constant.type != T_POINTER) + goto __NOT_FOUND; + + return desc->constant.value._pointer; + +__NOT_FOUND: + return NULL; +} + + +GB_VALUE *GB_Call(GB_FUNCTION *func, int nparam, int release) +{ + bool stop_event; + CLASS *class; + void *object; + CLASS_DESC_METHOD *desc; + + if (!func || !func->index) + GB_Error("Unknown function call"); + //TEMP.type = GB_T_NULL; + else + { + object = func->object; + if (OBJECT_is_class(object)) + { + class = object; + object = NULL; + } + else + { + class = OBJECT_class(object); + } + + desc = &class->table[func->index - 1].desc->method; + + stop_event = GAMBAS_StopEvent; + EXEC_public_desc(class, object, desc, nparam); + _event_stopped = GAMBAS_StopEvent; + GAMBAS_StopEvent = stop_event; + + if (release) + EXEC_release_return_value(); + else + EXEC_move_ret_to_temp(); + } + + return (GB_VALUE *)&TEMP; +} + + +int GB_GetEvent(void *class, char *name) +{ + CLASS_DESC_EVENT *cd; + + cd = CLASS_get_event_desc((CLASS *)class, name); + if (!cd) + return (-1); + else + return cd->index; +} + + +char *GB_GetLastEventName() +{ + return EVENT_Name; +} + + +bool GB_Stopped(void) +{ + return _event_stopped; +} + + +int GB_NParam(void) +{ + return EXEC_unknown_nparam; +} + + +const char *GB_GetUnknown(void) +{ + return EXEC_unknown_name; +} + + +void GB_OnErrorBegin(GB_ERROR_HANDLER *handler) +{ + handler->prev = ERROR_handler; + handler->context = ERROR_current; + ERROR_handler = (ERROR_HANDLER *)handler; +} + +void GB_OnErrorEnd(GB_ERROR_HANDLER *handler) +{ + ERROR_handler = (ERROR_HANDLER *)handler->prev; +} + +void GB_Error(const char *error, ...) +{ + va_list args; + char *arg[4]; + int i; + + if (!error) + { + EXEC_set_native_error(FALSE); + return; + } + + va_start(args, error); + + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, char *); + + va_end(args); + + ERROR_define(error, arg); + EXEC_set_native_error(TRUE); +} + + +bool GB_HasError() +{ + return EXEC_has_native_error(); +} + + +char *GB_GetErrorMessage() +{ + return ERROR_last.msg; +} + + +void GB_Deprecated(const char *msg, const char *func, const char *repl) +{ + if (EXEC_debug) + { + if (repl) + GB_Error("&1: &2 is deprecated. Use &3 instead", msg, func, repl); + else + GB_Error("&1: &2 is deprecated. Do not use it anymore", msg, func); + } + else + { + DEBUG_where(); + if (repl) + fprintf(stderr, "%s: %s is deprecated. Use %s instead\n", msg, func, repl); + else + fprintf(stderr, "%s: %s is deprecated. Do not use it anymore\n", msg, func); + } +} + +#if DEBUG_REF + +#include + +static void print_stack_backtrace() +{ + void *trace[16]; + char **messages = (char **)NULL; + int i, trace_size = 0; + + trace_size = backtrace(trace, 16); + messages = backtrace_symbols(trace, trace_size); + //printf("[bt] Execution path:\n"); + fprintf(stderr, "[ "); + for (i=0; istop = TRUE; +} + +void GB_OnFreeEnum(void (*cb)(void *)) +{ + EXEC_enum->free = cb; +} + + +void *GB_GetEnum(void) +{ + return (void *)&EXEC_enum->data; +} + + +static void *_enum_object; + +void *GB_BeginEnum(void *enum_object) +{ + CENUM *old = EXEC_enum; + EXEC_enum = NULL; + _enum_object = enum_object; + return old; +} + +void GB_EndEnum(void *old_enum) +{ + EXEC_enum = old_enum; +} + + +bool GB_NextEnum(void) +{ + for(;;) + { + EXEC_enum = CENUM_get_next(EXEC_enum); + if (!EXEC_enum) + return TRUE; + if (EXEC_enum->enum_object == _enum_object && !EXEC_enum->stop) + return FALSE; + } +} + +void GB_StopAllEnum(void *enum_object) +{ + void *save = GB_BeginEnum(enum_object); + while (!GB_NextEnum()) + GB_StopEnum(); + GB_EndEnum(save); +} + +GB_VALUE *GB_GetReturnValue(void) +{ + return (GB_VALUE *)&TEMP; +} + +void GB_Return(GB_TYPE type, ...) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + VALUE *ret = &TEMP; + va_list args; + + va_start(args, type); + + ret->type = type; + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + ret->_integer.value = va_arg(args, int) ? (-1) : 0; + goto __CONV; + +__BYTE: + + ret->_integer.value = va_arg(args, int); + goto __CONV; + +__SHORT: + + ret->_integer.value = va_arg(args, int); + goto __CONV; + +__INTEGER: + + ret->_integer.value = va_arg(args, int); + goto __CONV; + +__LONG: + + ret->_long.value = va_arg(args, int64_t); + goto __CONV; + +__SINGLE: + + ret->_single.value = va_arg(args, double); + goto __CONV; + +__FLOAT: + + ret->_float.value = va_arg(args, double); + goto __CONV; + +__DATE: + + ret->_date.date = va_arg(args, int); + ret->_date.time = va_arg(args, int); + goto __CONV; + +__OBJECT: + + ret->_object.object = va_arg(args, void *); + goto __CONV; + +__POINTER: + + ret->_pointer.value = va_arg(args, void *); + goto __CONV; + +__CLASS: + ret->_class.class = va_arg(args, CLASS *); + goto __CONV; + +__CONV: +__STRING: +__VOID: +__VARIANT: +__FUNCTION: +__NULL: + + va_end(args); + return; +} + + +void GB_ReturnInteger(int val) +{ + TEMP.type = T_INTEGER; + TEMP._integer.value = val; +} + + +void GB_ReturnLong(int64_t val) +{ + TEMP.type = T_LONG; + TEMP._long.value = val; +} + + +void GB_ReturnPointer(void *val) +{ + TEMP.type = T_POINTER; + TEMP._pointer.value = val; +} + + +void GB_ReturnSingle(float val) +{ + TEMP.type = T_SINGLE; + TEMP._single.value = val; +} + +void GB_ReturnFloat(double val) +{ + TEMP.type = T_FLOAT; + TEMP._float.value = val; +} + + +void GB_ReturnDate(GB_DATE *date) +{ + TEMP.type = T_DATE; + if (date) + { + TEMP._date.date = date->value.date; + TEMP._date.time = date->value.time; + } + else + { + TEMP._date.date = 0; + TEMP._date.time = 0; + } +} + + +void GB_ReturnBoolean(int val) +{ + TEMP.type = T_BOOLEAN; + TEMP._boolean.value = val ? -1 : 0; +} + + +void GB_ReturnObject(void *val) +{ + if (val == NULL) + GB_ReturnNull(); + else + { + TEMP.type = T_OBJECT; //OBJECT_class(val); + TEMP._object.object = val; + } +} + + +void GB_ReturnVariant(GB_VARIANT_VALUE *val) +{ + TEMP._variant.type = T_VARIANT; + + if (!val) + { + TEMP._variant.vtype = T_NULL; + } + else + { + //VALUE_read(&TEMP, value, type); + TEMP._variant.vtype = val->type; + if (TEMP._variant.vtype == T_VOID) + TEMP._variant.vtype = T_NULL; + + VARIANT_copy_value(&TEMP._variant, (VARIANT *)val); + } +} + + +void GB_ReturnConvVariant(void) +{ + BORROW(&TEMP); + VALUE_conv(&TEMP, T_VARIANT); + UNBORROW(&TEMP); +} + +void GB_ReturnBorrow(void) +{ + BORROW(&TEMP); +} + +void GB_ReturnRelease(void) +{ + UNBORROW(&TEMP); +} + +void GB_ReturnPtr(GB_TYPE type, void *value) +{ + if (type == T_VOID) + return; + + if (!value) + VALUE_default(&TEMP, type); + else + VALUE_read(&TEMP, value, type); +} + + +void GB_ReturnSelf(void *object) +{ + if (object) + { + TEMP.type = T_OBJECT; + TEMP._object.object = object; + } + else + { + TEMP.type = T_CLASS; + TEMP._class.class = NULL; + } +} + + +char *GB_ToZeroString(GB_STRING *src) +{ + char *str; + + str = STRING_new_temp(src->value.addr + src->value.start, src->value.len); + + if (str == NULL) + return ""; + else + return str; +} + + +void GB_ReturnString(char *str) +{ + TEMP.type = T_STRING; + TEMP._string.addr = str; + TEMP._string.start = 0; + TEMP._string.len = STRING_length(str); + + if (TEMP._string.len == 0) + TEMP._string.addr = 0; +} + + +void GB_ReturnConstString(const char *str, int len) +{ + TEMP.type = T_CSTRING; + TEMP._string.addr = (char *)str; + TEMP._string.start = 0; + TEMP._string.len = len; + + if (TEMP._string.len == 0) + TEMP._string.addr = 0; +} + + +void GB_ReturnConstZeroString(const char *str) +{ + int len; + + if (str) + len = strlen(str); + else + len = 0; + + GB_ReturnConstString(str, len); +} + + +void GB_ReturnNewString(const char *src, int len) +{ + if (len <= 0) + { + if (src && *src) + { + ERROR_warning("please use GB.ReturnNewZeroString() instead of GB.ReturnNewString()"); + len = strlen(src); + } + else + { + GB_ReturnVoidString(); + return; + } + } + + GB_ReturnString(STRING_new_temp(src, len)); +} + + +void GB_ReturnNewZeroString(const char *src) +{ + GB_ReturnString(STRING_new_temp_zero(src)); +} + + +void GB_ReturnNull(void) +{ + VALUE_null(&TEMP); +} + +void GB_ReturnVoidString(void) +{ + STRING_void_value(&TEMP); +} + + +void *GB_GetClass(void *object) +{ + if (object) + return OBJECT_class(object); + else + return EXEC.class; +} + + +char *GB_GetClassName(void *object) +{ + CLASS *class = GB_GetClass(object); + return class == CLASS_Class ? CLASS_get_name((CLASS *)object) : CLASS_get_name(class); +} + + +bool GB_Is(void *object, void *class) +{ + CLASS *ob_class; + + if (!object) + return FALSE; + + ob_class = OBJECT_class(object); + + return ((ob_class == class) || CLASS_inherits(ob_class, class)); +} + + +bool GB_LoadFile(const char *path, int lenp, char **addr, int *len) +{ + bool ret = FALSE; + + //fprintf(stderr, "GB_LoadFile: %.*s\n", lenp ? lenp : strlen(path), path); + + TRY + { + *addr = 0; + *len = 0; + + STREAM_map(STRING_conv_file_name(path, lenp), addr, len); + } + CATCH + { + if (*addr) + STREAM_unmap(*addr, *len); + + ret = TRUE; + } + END_TRY + + if (ret) + EXEC_set_native_error(TRUE); + + return ret; +} + + +bool GB_ExistFile(const char *path) +{ + return FILE_exist(path); +} + + +void GB_Store(GB_TYPE type, GB_VALUE *src, void *dst) +{ + if (src != NULL) + VALUE_write((VALUE *)src, dst, type); + else + VALUE_free(dst, type); +} + + +void GB_StoreString(GB_STRING *src, char **dst) +{ + STRING_unref(dst); + if (src) + { + *dst = STRING_new(src->value.addr + src->value.start, src->value.len); + } + else + *dst = NULL; +} + +void GB_StoreObject(GB_OBJECT *src, void **dst) +{ + void *object; + + if (src) + object = src->value; + else + object = NULL; + + OBJECT_REF_CHECK(object); + OBJECT_UNREF(*dst); + *dst = object; +} + +void GB_StoreVariant(GB_VARIANT *src, void *dst) +{ + /*GB_Store(GB_T_VARIANT, (GB_VALUE *)src, dst);*/ + if (src) + { + VALUE_write((VALUE *)src, dst, T_VARIANT); + + /*VARIANT_keep((VARIANT *)&src->value); + VARIANT_free((VARIANT *)dst); + *((VARIANT *)dst) = *((VARIANT *)&src->value);*/ + } + else + { + VARIANT_free((VARIANT *)dst); + ((VARIANT *)dst)->type = T_NULL; + } +} + +void GB_BorrowValue(GB_VALUE *value) +{ + BORROW((VALUE *)value); +} + +void GB_ReleaseValue(GB_VALUE *value) +{ + RELEASE((VALUE *)value); +} + +void GB_Watch(int fd, int flag, void *callback, intptr_t param) +{ + HOOK_DEFAULT(watch, WATCH_watch)(fd, flag, callback, param); +} + + +void *GB_Create(void *class, const char *name, void *parent) +{ + void *object; + + object = OBJECT_new(class, name, parent); + OBJECT_UNREF_KEEP(object); + + return object; +} + +void *GB_New(void *class, const char *name, void *parent) +{ + if (name && !parent) + { + parent = OP; + if (!parent) + parent = CP; + } + + if (!((CLASS *)class)->no_create) + { + int nparam = 0; + if (!name && parent) + { + nparam = (int)(intptr_t)parent; + parent = NULL; + } + + return OBJECT_create(class, name, parent, nparam); + } + else + return GB_Create(class, name, parent); +} + +bool GB_CheckObject(void *object) +{ + CLASS *class; + + if (object == NULL) + { + GB_Error((char *)E_NULL); + return TRUE; + } + + class = OBJECT_class(object); + + if (class->check && (*(class->check))(object)) + { + GB_Error((char *)E_IOBJECT); + return TRUE; + } + + return FALSE; +} + + +const char *GB_AppName(void) +{ + return PROJECT_name; +} + +const char *GB_AppPath(void) +{ + return PROJECT_path; +} + +const char *GB_AppTitle(void) +{ + return LOCAL_gettext(PROJECT_title); +} + +const char *GB_AppVersion(void) +{ + return PROJECT_version; +} + +void *GB_AppStartupClass(void) +{ + return PROJECT_class; +} + + +void *GB_Eval(void *expr, void *func) +{ + bool err = EVAL_expression((EXPRESSION *)expr, (EVAL_FUNCTION)func); + + EXEC_set_native_error(err); + + if (err) + return NULL; + else + return &TEMP; +} + + +void GB_Alloc(void **addr, int len) +{ + ALLOC(addr, len); +} + +void GB_AllocZero(void **addr, int len) +{ + ALLOC_ZERO(addr, len); +} + +void GB_Free(void **addr) +{ + FREE(addr); +} + +void GB_Realloc(void **addr, int len) +{ + REALLOC(addr, len); +} + + +bool GB_Conv(GB_VALUE *arg, GB_TYPE type) +{ + CATCH_ERROR + { + VALUE_conv((VALUE *)arg, (GB_TYPE)type); + } + END_CATCH_ERROR +} + + +DATE_SERIAL *GB_SplitDate(GB_VALUE *arg) +{ + return DATE_split((VALUE *)arg); +} + +bool GB_MakeDate(DATE_SERIAL *date, GB_DATE *arg) +{ + return DATE_make(date, (VALUE *)arg); +} + + +int GB_StringLength(const char *str) +{ + return STRING_length(str); +} + + +bool GB_NumberToString(int local, double value, const char *format, char **str, int *len) +{ + return + LOCAL_format_number + ( + value, + format ? LF_USER : LF_GENERAL_NUMBER, + format, + format ? strlen(format) : 0, + str, len, local != 0 + ); +} + + +void GB_HashTableNew(GB_HASHTABLE *hash, int mode) +{ + HASH_TABLE_create((HASH_TABLE **)hash, sizeof(void *), mode); +} + +void GB_HashTableAdd(GB_HASHTABLE hash, const char *key, int len, void *data) +{ + if (len <= 0) + len = strlen(key); + + *((void **)HASH_TABLE_insert((HASH_TABLE *)hash, key, len)) = data; +} + +void GB_HashTableRemove(GB_HASHTABLE hash, const char *key, int len) +{ + if (len <= 0) + len = strlen(key); + + HASH_TABLE_remove((HASH_TABLE *)hash, key, len); +} + +bool GB_HashTableGet(GB_HASHTABLE hash, const char *key, int len, void **data) +{ + void **pdata; + + if (len <= 0) + len = strlen(key); + + pdata = (void **)HASH_TABLE_lookup((HASH_TABLE *)hash, key, len, FALSE); + if (pdata) + { + *data = *pdata; + return FALSE; + } + else + return TRUE; +} + +void GB_HashTableEnum(GB_HASHTABLE hash, GB_HASHTABLE_ENUM_FUNC func) +{ + HASH_ENUM iter; + void **pdata; + + CLEAR(&iter); + + for(;;) + { + pdata = (void **)HASH_TABLE_next((HASH_TABLE *)hash, &iter, FALSE); + if (!pdata) + break; + + (*func)(*pdata); + } +} + +bool GB_HashTableFirst(GB_HASHTABLE hash, void **data) +{ + HASH_ENUM iter; + void **pdata; + + CLEAR(&iter); + pdata = (void **)HASH_TABLE_next((HASH_TABLE *)hash, &iter, FALSE); + if (pdata) + { + *data = *pdata; + return FALSE; + } + else + return TRUE; +} + +void GB_NewArray(void *pdata, int size, int count) +{ + ARRAY_create_with_size(pdata, size, 16); + if (count) + ARRAY_add_data(pdata, count, TRUE); +} + + +int GB_CountArray(void *data) +{ + return ARRAY_count(data); +} + + +void *GB_Add(void *pdata) +{ + return ARRAY_add_void_size((char **)pdata); +} + +char *GB_NewZeroString(char *src) +{ + return STRING_new_zero(src); +} + +char *GB_TempString(char *src, int len) +{ + return STRING_new_temp(src, len); +} + +char *GB_RefString(char *str) +{ + STRING_ref(str); + return str; +} + +void GB_FreeString(char **str) +{ + STRING_unref(str); + *str = NULL; +} + +bool GB_ConvString(char **result, const char *str, int len, const char *src, const char *dst) +{ + CATCH_ERROR + { + STRING_conv(result, str, len, src, dst, TRUE); + } + END_CATCH_ERROR +} + + +char *GB_SystemCharset(void) +{ + return LOCAL_is_UTF8 ? "UTF-8" : LOCAL_encoding; +} + +char *GB_SystemDomainName(void) +{ + char host[256], domain[256]; + + if (gethostname(host, sizeof(host) - 1)) + return ""; + + if (getdomainname(domain, sizeof(domain) - 1)) + return ""; + + if ((strlen(host) + strlen(domain)) > COMMON_BUF_MAX) + return ""; + + *COMMON_buffer = 0; + if (*domain && strcmp(domain, "(none)")) + { + strcpy(COMMON_buffer, domain); + strcat(COMMON_buffer, "."); + } + strcat(COMMON_buffer, host); + + return COMMON_buffer; +} + +bool GB_IsRightToLeft(void) +{ + return LOCAL_local.rtl; +} + +char *GB_SystemPath(void) +{ + return PROJECT_exec_path; +} + +/*void GB_StreamInit(GB_STREAM *stream, int fd) +{ + STREAM *s = (STREAM *)stream; + + s->type = &STREAM_direct; + s->direct.fd = fd; +}*/ + +GB_STREAM *GB_StreamGet(void *object) +{ + return (GB_STREAM *)CSTREAM_TO_STREAM(object); +} + +void GB_StreamSetSwapping(GB_STREAM *stream, int v) +{ + ((STREAM *)stream)->common.swap = v; +} + +void GB_StreamSetAvailableNow(GB_STREAM *stream, int v) +{ + ((STREAM *)stream)->common.available_now = v; +} + +bool GB_StreamBlock(GB_STREAM *stream, int block) +{ + STREAM *st = (STREAM *)stream; + bool old = STREAM_is_blocking(st); + STREAM_blocking(st, block); + return old; +} + +int GB_StreamRead(GB_STREAM *stream, void *addr, int len) +{ + CATCH_ERROR_INT + { + if (len < 0) + ret = STREAM_read_max((STREAM *)stream, addr, -len); + else + ret = STREAM_read((STREAM *)stream, addr, len); + } + END_CATCH_ERROR_INT +} + +int GB_StreamWrite(GB_STREAM *stream, void *addr, int len) +{ + CATCH_ERROR_INT + { + STREAM_write((STREAM *)stream, addr, len); + ret = len; + } + END_CATCH_ERROR_INT +} + +int GB_tolower(int c) +{ + return tolower(c); +} + +int GB_toupper(int c) +{ + return toupper(c); +} + +char *GB_TempDir(void) +{ + return FILE_make_temp(NULL, NULL); +} + +char *GB_TempFile(const char *pattern) +{ + int len; + return FILE_make_temp(&len, pattern); +} + +bool GB_CopyFile(const char *src, const char *dst) +{ + CATCH_ERROR + { + FILE_copy(src, dst); + } + END_CATCH_ERROR +} + +#if 0 +int GB_FindFile(const char *dir, int recursive, int follow, void (*found)(const char *)) +{ + int ret = 0; + char *pattern; + int len_pattern; + char *str; + + TRY + { + if (recursive) + FILE_recursive_dir(dir, found, NULL, 0, follow); + else + { + FILE_dir_first(dir, NULL, 0); + while (!FILE_dir_next(&pattern, &len_pattern)) + { + if (!LOCAL_is_UTF8) + { + if (STRING_conv(&str, pattern, len_pattern, LOCAL_encoding, SC_UTF8, FALSE)) + STRING_new(&str, pattern, len_pattern); + else + STRING_ref(str); + } + else + STRING_new(&str, pattern, len_pattern); + + (*found)(str); + STRING_unref(&str); + } + } + } + CATCH + { + ret = 1; + GAMBAS_Error = TRUE; + } + END_TRY; + + return ret; +} +#endif + +bool GB_StatFile(const char *path, GB_FILE_STAT *info, bool follow) +{ + CATCH_ERROR + { + FILE_stat(path, (FILE_STAT *)info, follow); + } + END_CATCH_ERROR +} + +char *GB_RealFileName(const char *name, int len) +{ + char *path = STRING_conv_file_name(name, len); + char *real; + char *temp; + + if (!STREAM_in_archive(path)) + return path; + + temp = FILE_make_temp(NULL, NULL); + real = STRING_new_temp(NULL, strlen(temp) + strlen(path) + strlen("/data/")); + snprintf(real, strlen(temp) + strlen(path) + strlen("/data/") + 1, "%s/data/%s", temp, path); + + if (!FILE_exist(real)) + { + TRY + { + FILE_make_path_dir(real); + FILE_copy(path, real); + } + CATCH + { + real = path; + } + END_TRY + } + + return real; +} + +static GB_BROWSE_PROJECT_CALLBACK _browse_project_func; + +static void project_file_found(const char *path) +{ + FILE_STAT info; + + FILE_stat(path, &info, FALSE); + (*_browse_project_func)(path, info.size); +} + +void GB_BrowseProject(GB_BROWSE_PROJECT_CALLBACK func) +{ + ARCHIVE *arch = NULL; + + ARCHIVE_get_current(&arch); + if (!arch || (arch == ARCHIVE_main && !EXEC_arch)) + { + _browse_project_func = func; + FILE_recursive_dir(PROJECT_path, project_file_found, NULL, 0, FALSE); + } + else + ARCHIVE_browse(arch, func); +} + +void GB_BrowseDirectory(const char *dir, GB_BROWSE_CALLBACK before, GB_BROWSE_CALLBACK after) +{ + FILE_recursive_dir(dir, before, after, 0, FALSE); +} + + +const char *GB_CurrentComponent() +{ + ARCHIVE *arch; + + ARCHIVE_get_current(&arch); + return arch->name ? arch->name : ""; +} + + +void *GB_DebugGetExec(void) +{ + return &EXEC_current; +} + +void GB_DebugBreakOnError(bool b) +{ + EXEC_break_on_error = b; +} + +void GB_DebugInside(bool b) +{ + EXEC_debug_inside = b; +} + +void GB_DebugHold(void) +{ + EXEC_debug_hold = TRUE; +} + +bool GB_SystemDebug(void) +{ + return EXEC_debug; +} + +void GB_SystemHasForked(void) +{ + MATH_init(); + FILE_init(); + LOCAL_init(); + + if (EXEC_profile) + DEBUG.Profile.Cancel(); + EXEC_profile = FALSE; + EXEC_profile_instr = FALSE; + + SIGNAL_has_forked(); +} + +bool GB_ExistClass(const char *name) +{ + return CLASS_look_global(name, strlen(name)) != NULL; +} + + +bool GB_ExistClassLocal(const char *name) +{ + return CLASS_look(name, strlen(name)) != NULL; +} + +void *GB_FindClass(const char *name) +{ + return CLASS_find_global(name); +} + +void *GB_FindClassLocal(const char *name) +{ + return CLASS_find(name); +} + +TYPE GB_GetArrayType(void *klass) +{ + return ((CLASS *)klass)->array_type; +} + +void GB_Wait(int delay) +{ + struct timespec rem; + double wait; + double stop, time; + int duration; + + DEBUG_enter_event_loop(); + + if (delay <= 0) + { + HOOK_DEFAULT(wait, WATCH_wait)(delay); + } + else + { + wait = delay / 1000.0; + + DATE_timer(&stop, FALSE); + stop += wait; + + for(;;) + { + duration = (int)(wait * 1000 + 0.5); + HOOK_DEFAULT(wait, WATCH_wait)(duration); + + if (DATE_timer(&time, FALSE)) + break; + + wait = stop - time; + if (wait <= 0.0) + break; + + rem.tv_sec = 0; + rem.tv_nsec = 1000000; + nanosleep(&rem, &rem); + } + } + + DEBUG_leave_event_loop(); +} + +bool GB_Serialize(const char *path, GB_VALUE *value) +{ + CFILE *cfile; + STREAM stream; + + CATCH_ERROR + { + STREAM_open(&stream, path, GB_ST_CREATE); + cfile = CFILE_create(&stream, GB_ST_CREATE); + OBJECT_REF(cfile); + STREAM_write_type(&cfile->ob.stream, T_VARIANT, (VALUE *)value); + //STREAM_close(&cfile->ob.stream); + OBJECT_UNREF(cfile); + } + END_CATCH_ERROR +} + +bool GB_UnSerialize(const char *path, GB_VALUE *value) +{ + CFILE *cfile; + STREAM stream; + + CATCH_ERROR + { + STREAM_open(&stream, path, GB_ST_READ); + cfile = CFILE_create(&stream, GB_ST_CREATE); + OBJECT_REF(cfile); + STREAM_read_type(&cfile->ob.stream, T_VARIANT, (VALUE *)value); + //STREAM_close(&cfile->ob.stream); + OBJECT_UNREF(cfile); + } + END_CATCH_ERROR +} + diff --git a/main/gbx/gbx_api.h b/main/gbx/gbx_api.h new file mode 100644 index 00000000..eb366b9d --- /dev/null +++ b/main/gbx/gbx_api.h @@ -0,0 +1,242 @@ +/*************************************************************************** + + gbx_api.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_API_H +#define __GBX_API_H + +#include "gambas.h" +#include "gbx_type.h" +#include "gbx_stream.h" +#include "gb_hash.h" +#include "gbx_date.h" + +bool GB_GetInterface(const char *library, int version, void *iface); +void *GB_Hook(int type, void *hook); + +bool GB_LoadComponent(const char *name); +const char *GB_CurrentComponent(void); + +void GB_Wait(int); +void GB_Push(int nval, ...); +bool GB_CanRaise(void *object, int event_id); +bool GB_RaiseEvent(void *object, int event_id, int nparam, ...); +void GB_RaiseBegin(GB_RAISE_HANDLER *handler); +void GB_RaiseEnd(GB_RAISE_HANDLER *handler); +int GB_GetEvent(void *class, char *name); +char *GB_GetLastEventName(void); +bool GB_Stopped(void); +bool GB_IsRaiseLocked(void *object); +bool GB_HasActiveTimer(void); + +int GB_NParam(void); +const char *GB_GetUnknown(void); + +void *GB_GetProperty(void *object, const char *property); +bool GB_SetProperty(void *object, const char *name, GB_VALUE *value); + +bool GB_Serialize(const char *path, GB_VALUE *value); +bool GB_UnSerialize(const char *path, GB_VALUE *value); + +void GB_Error(const char *msg, ...); +bool GB_HasError(void); +char *GB_GetErrorMessage(void); +void GB_Deprecated(const char *msg, const char *func, const char *repl); +void GB_OnErrorBegin(GB_ERROR_HANDLER *handler); +void GB_OnErrorEnd(GB_ERROR_HANDLER *handler); + +void GB_Ref(void *object); +void GB_Unref(void **object); +void GB_UnrefKeep(void **object, int); + +void GB_StopEnum(void); +void *GB_GetEnum(void); +void *GB_BeginEnum(void *); +void GB_EndEnum(void *); +bool GB_NextEnum(void); +void GB_StopAllEnum(void *); +void GB_OnFreeEnum(void (*cb)(void *)); + +GB_VALUE *GB_GetReturnValue(void); +void GB_Return(GB_TYPE type, ...); +void GB_ReturnInteger(int val); +#define GB_ReturnInt GB_ReturnInteger +void GB_ReturnLong(int64_t val); +void GB_ReturnPointer(void *val); +void GB_ReturnBoolean(int val); +void GB_ReturnObject(void *val); +void GB_ReturnNull(void); +void GB_ReturnSingle(float val); +void GB_ReturnFloat(double val); +void GB_ReturnPtr(GB_TYPE type, void *value); +void GB_ReturnDate(GB_DATE *date); +void GB_ReturnSelf(void *object); +void GB_ReturnVariant(GB_VARIANT_VALUE *value); + +void GB_ReturnConvVariant(void); +void GB_ReturnBorrow(void); +void GB_ReturnRelease(void); + +void GB_ReturnString(char *str); +void GB_ReturnVoidString(void); +void GB_ReturnConstString(const char *str, int len); +void GB_ReturnConstZeroString(const char *str); +void GB_ReturnNewString(const char *src, int len); +void GB_ReturnNewZeroString(const char *src); + +void *GB_GetClass(void *object); +char *GB_GetClassName(void *object); +void *GB_FindClass(const char *name); +void *GB_FindClassLocal(const char *name); +bool GB_ExistClass(const char *name); +bool GB_ExistClassLocal(const char *name); +TYPE GB_GetArrayType(void *klass); + +char *GB_ToZeroString(GB_STRING *src); + +bool GB_LoadFile(const char *path, int lenp, char **addr, int *len); +bool GB_ExistFile(const char *path); +//void GB_ReleaseFile(char **addr, int len); +#define GB_ReleaseFile STREAM_unmap +char *GB_RealFileName(const char *path, int len); +char *GB_TempDir(void); +char *GB_TempFile(const char *pattern); +bool GB_CopyFile(const char *src, const char *dst); +//int GB_FindFile(const char *dir, int recursive, int follow, void (*found)(const char *)); +bool GB_StatFile(const char *path, GB_FILE_STAT *info, bool follow); +void GB_BrowseProject(GB_BROWSE_PROJECT_CALLBACK func); +void GB_BrowseDirectory(const char *dir, GB_BROWSE_CALLBACK before, GB_BROWSE_CALLBACK after); + +int GB_IsMissing(int param); + +void GB_Attach(void *object, void *parent, const char *name); +void GB_Detach(void *object); + +void GB_Store(GB_TYPE type, GB_VALUE *src, void *dst); +void GB_StoreString(GB_STRING *src, char **dst); +void GB_StoreObject(GB_OBJECT *object, void **dst); +void GB_StoreVariant(GB_VARIANT *src, void *dst); +void GB_BorrowValue(GB_VALUE *value); +void GB_ReleaseValue(GB_VALUE *value); + +void GB_Watch(int fd, int flag, void *callback, intptr_t param); + +void *GB_Create(void *class_name, const char *name, void *parent); +void *GB_New(void *class_name, const char *name, void *parent); +bool GB_CheckObject(void *object); +bool GB_Is(void *object, void *class); + +bool GB_GetFunction(GB_FUNCTION *func, void *object, const char *name, const char *sign, const char *type); +GB_VALUE *GB_Call(GB_FUNCTION *func, int nparam, int release); +void *GB_GetClassInterface(void *class, const char *name); + +const char *GB_AppName(void); +const char *GB_AppTitle(void); +const char *GB_AppVersion(void); +const char *GB_AppPath(void); +void *GB_AppStartupClass(void); + +char *GB_SystemCharset(void); +char *GB_SystemDomainName(void); +bool GB_IsRightToLeft(void); +char *GB_SystemPath(void); +bool GB_SystemDebug(void); +void GB_SystemHasForked(void); + +void *GB_Eval(void *, void *); + +void GB_ArrayNew(GB_ARRAY *array, TYPE type, int size); +int GB_ArrayCount(GB_ARRAY array); +void *GB_ArrayAdd(GB_ARRAY array); +void *GB_ArrayGet(GB_ARRAY array, int index); +TYPE GB_ArrayType(GB_ARRAY array); +void GB_ArraySetReadOnly(GB_ARRAY array); + +void GB_CollectionNew(GB_COLLECTION *col, int mode); +int GB_CollectionCount(GB_COLLECTION col); +bool GB_CollectionSet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value); +bool GB_CollectionGet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value); +bool GB_CollectionEnum(GB_COLLECTION col, GB_COLLECTION_ITER *iter, GB_VARIANT *value, char **key, int *len); + +void GB_Alloc(void **addr, int len); +void GB_AllocZero(void **addr, int len); +void GB_Free(void **addr); +void GB_Realloc(void **addr, int len); + +char *GB_NewZeroString(char *src); +char *GB_TempString(char *src, int len); +char *GB_RefString(char *str); +void GB_FreeString(char **str); +int GB_StringLength(const char *str); +bool GB_ConvString(char **result, const char *str, int len, const char *src, const char *dst); + +bool GB_Conv(GB_VALUE *, GB_TYPE); + +DATE_SERIAL *GB_SplitDate(GB_VALUE *); +bool GB_MakeDate(DATE_SERIAL *, GB_DATE *); + +bool GB_NumberToString(int flag, double value, const char *format, char **str, int *len); + +void GB_HashTableNew(GB_HASHTABLE *hash, int mode); +void GB_HashTableAdd(GB_HASHTABLE hash, const char *key, int len, void *data); +void GB_HashTableRemove(GB_HASHTABLE hash, const char *key, int len); +bool GB_HashTableGet(GB_HASHTABLE hash, const char *key, int len, void **data); +void GB_HashTableEnum(GB_HASHTABLE hash, GB_HASHTABLE_ENUM_FUNC func); +bool GB_HashTableFirst(GB_HASHTABLE hash, void **data); + +void GB_NewArray(void *pdata, int size, int count); +int GB_CountArray(void *data); +void *GB_Add(void *pdata); + +GB_STREAM *GB_StreamGet(void *); +//void GB_StreamSetBytesRead(GB_STREAM *, int); +void GB_StreamSetSwapping(GB_STREAM *, int); +void GB_StreamSetAvailableNow(GB_STREAM *, int); +bool GB_StreamBlock(GB_STREAM *, int); +int GB_StreamRead(GB_STREAM *stream, void *addr, int len); +int GB_StreamWrite(GB_STREAM *stream, void *addr, int len); + +int GB_tolower(int c); +int GB_toupper(int c); + +void *GB_DebugGetClass(const char *name); +void *GB_DebugGetExec(void); +void GB_DebugBreakOnError(bool); +void GB_DebugInside(bool); +void GB_DebugHold(void); + +#define GB_PrintString PRINT_string + +#define GB_Raise(_object, _event_id, _nparam, ...) (GAMBAS_RaiseEventCanPropagate = TRUE, GB_RaiseEvent(_object, _event_id, _nparam, ## __VA_ARGS__)) + +#ifndef __GBX_API_C +EXTERN void *GAMBAS_Api[]; +EXTERN void *GAMBAS_DebugApi[]; +EXTERN void *GAMBAS_JitApi[]; +EXTERN unsigned int GAMBAS_MissingParam; +EXTERN bool GAMBAS_DoNotRaiseEvent; +EXTERN bool GAMBAS_StopEvent; +EXTERN bool GAMBAS_RaiseEventCanPropagate; +#endif + +#endif + diff --git a/main/gbx/gbx_archive.c b/main/gbx/gbx_archive.c new file mode 100644 index 00000000..ad42515d --- /dev/null +++ b/main/gbx/gbx_archive.c @@ -0,0 +1,798 @@ +/*************************************************************************** + + gbx_archive.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_ARCHIVE_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" + +#include "gbx_string.h" +#include "gbx_stream.h" +#include "gbx_component.h" +#include "gbx_regexp.h" +#include "gbx_exec.h" +#include "gbx_class.h" +#include "gbx_project.h" + +#include "gbx_archive.h" + +#include "gb_arch_temp.h" + +//#define DEBUG_COMP 1 + +// main archive project (used only if gbx is run with -x flag) +ARCHIVE *ARCHIVE_main = NULL; + +// Additional library search directory used in debug mode, normally '~/.config/share/gambas3/lib' +char *ARCHIVE_path = NULL; + +static char *_local_path = NULL; +static ARCHIVE *_archive_list = NULL; +static char *arch_pattern = NULL; +static int arch_index = 0; +static ARCH *arch_dir = NULL; +static char *arch_prefix = NULL; + +ARCHIVE *ARCHIVE_create(const char *name, const char *path) +{ + ARCHIVE *arch; + + ALLOC_ZERO(&arch, sizeof(ARCHIVE)); + + arch->name = name; + arch->path = STRING_new_zero(path); + + arch->domain = STRING_new_zero(name ? name : "gb"); + + arch->translation_loaded = FALSE; + + TABLE_create(&arch->classes, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE); + + LIST_insert(&_archive_list, arch, &arch->list); + + return arch; +} + +static void error_ARCHIVE_load_exported_class(COMPONENT *current) +{ + COMPONENT_current = current; +} + +void ARCHIVE_load_exported_class(ARCHIVE *arch, int pass) +{ + char *buffer; + int len; + char *name; + char *file_name; + CLASS *class; + int i; + COMPONENT *current; + bool optional; + char c; + + if (arch->exported_classes_loaded) + return; + + current = COMPONENT_current; + COMPONENT_current = (COMPONENT *)arch->current_component; + + ON_ERROR_1(error_ARCHIVE_load_exported_class, current) + { + /* COMPONENT_current is set => it will look in the archive */ + + #if DEBUG_COMP + fprintf(stderr, "load_exported_class: %s (component: %s)\n", arch->name, COMPONENT_current ? COMPONENT_current->name : "?"); + #endif + + if (FILE_exist(".list")) + { + if (pass & AR_FIND_ONLY) + { + #if DEBUG_COMP + fprintf(stderr, "\n"); + #endif + + STREAM_load(".list", &buffer, &len); + /* The file must end with a newline !*/ + buffer[len - 1] = 0; + + #if DEBUG_COMP + fprintf(stderr, "-----------\n%s\n----------\n\n", buffer); + #endif + + ARRAY_create(&arch->exported); + + name = strtok(buffer, "\n"); + while (name) + { + #if DEBUG_COMP + fprintf(stderr, "Check %s global\n", name); + #endif + + file_name = strchr(name, ' '); + if (file_name) + *file_name++ = 0; + + len = strlen(name); + optional = FALSE; + + for(;;) + { + c = name[len - 1]; + if (c == '?') + { + optional = TRUE; + len--; + } + else if (c == '!') + len--; + else + break; + } + + name[len] = 0; + + if (!optional || CLASS_look_global(name, len) == NULL) + { + class = CLASS_find_export(file_name, name); + + if (file_name) + { + ALLOC(&class->data, len + 1); + strcpy(class->data, file_name); + class->data[len] = 0; + } + + class = CLASS_check_global(class); + + #if DEBUG_COMP + fprintf(stderr, "Add to load: %p %s\n", class, name); + #endif + class->component = COMPONENT_current; + *((CLASS **)ARRAY_add(&arch->exported)) = class; + } + + name = strtok(NULL, "\n"); + } + + FREE(&buffer); + } + + if (pass & AR_FIND_ONLY) // That way the 'pass' flag is always ignored. + { + #if DEBUG_COMP + fprintf(stderr, "\n"); + #endif + + for (i = 0; i < ARRAY_count(arch->exported); i++) + { + #if DEBUG_COMP + fprintf(stderr, "load %p %s\n", arch->exported[i], arch->exported[i]->name); + #endif + CLASS_load(arch->exported[i]); + } + + ARRAY_delete(&arch->exported); + arch->exported_classes_loaded = TRUE; + } + } + } + END_ERROR + + COMPONENT_current = current; +} + +// Archive path is an absolute path or :/: + +static char *exist_library(const char *dir, const char *name) +{ + char *path; + char *n; + char *p; + + n = STRING_new_zero(name); + + path = (char *)FILE_cat(dir, n, NULL); + if (FILE_exist(path)) + goto __RETURN_PATH; + + n = STRING_add(n, ".gambas", -1); + path = (char *)FILE_cat(dir, n, NULL); + if (FILE_exist(path)) + goto __RETURN_PATH; + + p = rindex(n, ':'); + if (p) + { + strcpy(p, ".gambas"); + path = (char *)FILE_cat(dir, n, NULL); + if (FILE_exist(path)) + goto __RETURN_PATH; + } + + path = NULL; + +__RETURN_PATH: + + STRING_free(&n); + return path; +} + +void ARCHIVE_load(ARCHIVE *arch, bool load_exp) +{ + char *path; + char *dir; + const char *name; + char *env; + + if (arch->path) + { + if (*arch->path == '/' && FILE_exist(arch->path)) + { + path = (char *)arch->path; + } + else + { + if (*arch->path == ':') + { + if (!_local_path) + { + env = getenv("XDG_DATA_HOME"); + if (env && *env) + _local_path = STRING_new_zero(FILE_cat(env, "gambas3/lib", NULL)); + else + _local_path = STRING_new_zero(FILE_cat(FILE_get_home(), ".local/share/gambas3/lib", NULL)); + } + + name = &arch->path[1]; + } + else + name = FILE_get_name(arch->path); + + // For backward-compatibility, library name is searched in the current folder too, without the vendor. + + if (!(path = exist_library(PROJECT_path, FILE_get_name(name)))) + { + if (!_local_path || !(path = exist_library(_local_path, name))) + { + if (!ARCHIVE_path || !(path = exist_library(ARCHIVE_path, name))) + { + if (!(path = exist_library(COMPONENT_path, name))) + { + dir = STRING_new_zero(FILE_cat(PROJECT_exec_path, "bin", NULL)); + path = exist_library(dir, name); + STRING_free(&dir); + } + } + } + } + + if (!path || !FILE_exist(path)) + THROW(E_LIBRARY, arch->name, "cannot find library"); + } + } + else + { + path = FILE_buffer(); + sprintf(path, ARCH_PATTERN, COMPONENT_path, arch->name); + } + + arch->arch = ARCH_open(path); + arch->current_component = COMPONENT_current; + + if (load_exp) + ARCHIVE_load_exported_class(arch, AR_FIND_AND_LOAD); //, dep); +} + + +void ARCHIVE_create_main(const char *path) +{ + ARCHIVE_main = ARCHIVE_create(NULL, NULL); + + if (path) + ARCHIVE_main->arch = ARCH_open(path); + else + ARCHIVE_main->arch = NULL; +} + + +void ARCHIVE_load_main() +{ + ARCHIVE_load_exported_class(ARCHIVE_main, AR_FIND_AND_LOAD); +} + + +void ARCHIVE_delete(ARCHIVE *arch) +{ + int i; + LIST_remove(&_archive_list, arch, &arch->list); + + if (arch->arch) + ARCH_close(arch->arch); + + for (i = 0; i < TABLE_count(arch->classes); i++) + IFREE(TABLE_get_symbol(arch->classes, i)->name); + + TABLE_delete(&arch->classes); + + STRING_free(&arch->domain); + STRING_free(&arch->version); + STRING_free(&arch->path); + + FREE(&arch); +} + + +void ARCHIVE_init(void) +{ +} + + +void ARCHIVE_exit(void) +{ + if (ARCHIVE_main) + ARCHIVE_delete(ARCHIVE_main); + + STRING_free(&arch_pattern); + STRING_free(&arch_prefix); + STRING_free(&ARCHIVE_path); + STRING_free(&_local_path); +} + + +/* ### *parch must be initialized to NULL or a valid archive */ +/* Returns a true archive, never the main archive if we are not running an executable */ +bool ARCHIVE_find_from_path(ARCHIVE **parch, const char **ppath) +{ + int i; + CLASS *class; + const char *path = *ppath; + const char *p; + + if (*parch) + return FALSE; + + if (COMPONENT_current && COMPONENT_current->archive) + *parch = COMPONENT_current->archive; + else if (CP && CP->component && CP->component->archive) + *parch = CP->component->archive; + else + *parch = NULL; + + //fprintf(stderr, "ARCHIVE_find_from_path: %s (%s)\n", *ppath, *parch ? (*parch)->name : "NULL"); + + if (strncmp(path, "./", 2) == 0 && path[2]) + { + path += 2; + p = index(path, '/'); + if (p) + { + int len = p - path; + char name[len + 1]; + COMPONENT *comp; + + strncpy(name, path, len); + name[len] = 0; + + comp = COMPONENT_find(name); + if (comp && comp->archive) + { + *parch = comp->archive; + *ppath = p + 1; + return FALSE; + } + } + } + + if (strncmp(path, ".../", 4) == 0) + { + path += 4; + *parch = NULL; + } + else + { + i = 0; + while (strncmp(path, "../", 3) == 0) + { + path += 3; + if (*parch == NULL || *parch == ARCHIVE_main) + continue; + + while (i < STACK_frame_count) + { + class = STACK_frame[i].cp; + //fprintf(stderr, "[%d] %s / %s\n", i, class ? class->name : "NULL", class && class->component && class->component->archive ? class->component->archive->name : "NULL"); + if (class) + { + if (!class->component) + { + *parch = NULL; + break; + } + if (class->component && class->component->archive != *parch) + { + *parch = class->component->archive; + break; + } + } + + i++; + } + + if (i == STACK_frame_count) + *parch = NULL; + } + } + + if (*parch == NULL && EXEC_arch) + *parch = ARCHIVE_main; + + //fprintf(stderr, "--> '%s' / %s\n", *parch ? (*parch)->name : "(null)", *ppath); + + *ppath = path; + return *parch == NULL; +} + +/* Can return the main archive even if we are not running an executable */ +bool ARCHIVE_get_current(ARCHIVE **parch) +{ + if (COMPONENT_current && COMPONENT_current->archive) + *parch = COMPONENT_current->archive; + else if (CP && CP->component && CP->component->archive) + *parch = CP->component->archive; + else + *parch = ARCHIVE_main; + + return *parch == NULL; +} + + +bool ARCHIVE_get(ARCHIVE *arch, const char **ppath, ARCHIVE_FIND *find) +{ + ARCH_FIND f; + struct stat buf; + + if (!*ppath || **ppath == 0) + return TRUE; + + if (ARCHIVE_find_from_path(&arch, ppath)) + { + // no archive found, we try a lstat + if (!PROJECT_path) + return TRUE; + FILE_chdir(PROJECT_path); + if (stat(*ppath, &buf)) + return TRUE; + + find->pos = S_ISDIR(buf.st_mode) ? -1 : 0; + find->sym = NULL; + find->len = (int)buf.st_size; + find->index = -1; + find->arch = NULL; + return FALSE; + } + + if (!arch->arch) // archive has been created but loading has failed + return TRUE; + + if (ARCH_find(arch->arch, *ppath, 0, &f)) + return TRUE; + + find->sym = f.sym; + find->pos = f.pos; + find->len = f.len; + find->index = f.index; + find->arch = arch; + + return FALSE; +} + + +bool ARCHIVE_exist(ARCHIVE *arch, const char *path) +{ + ARCHIVE_FIND find; + + //if (get_current(&arch, &path)) + // return FALSE; + + return (!ARCHIVE_get(arch, &path, &find)); +} + + +bool ARCHIVE_is_dir(ARCHIVE *arch, const char *path) +{ + ARCHIVE_FIND find; + + if (ARCHIVE_get(arch, &path, &find)) + return FALSE; + + return (find.pos < 0); +} + + +void ARCHIVE_stat(ARCHIVE *arch, const char *path, FILE_STAT *info) +{ + ARCHIVE_FIND find = { 0 }; + struct stat buf; + + //if (get_current(&arch)) + // THROW_SYSTEM(ENOENT, path); + + if (ARCHIVE_get(arch, &path, &find)) + THROW_SYSTEM(ENOENT, path); + + if (find.arch) + fstat(find.arch->arch->fd, &buf); + else + stat(PROJECT_path, &buf); + + info->type = find.pos < 0 ? GB_STAT_DIRECTORY : GB_STAT_FILE; + info->mode = 0400; + + info->size = find.len; + info->atime = (int)buf.st_mtime; + info->mtime = (int)buf.st_mtime; + info->ctime = (int)buf.st_mtime; + info->hidden = (*FILE_get_name(path) == '.'); + info->uid = (int)buf.st_uid; + info->gid = (int)buf.st_gid; +} + + +void ARCHIVE_read(ARCHIVE *arch, int pos, void *buffer, int len) +{ + ARCH_read(arch->arch, pos, buffer, len); +} + + +void ARCHIVE_dir_first(ARCHIVE *arch, const char *path, const char *pattern, int attr) +{ + ARCHIVE_FIND find; + char abs_path[16]; + int abs_len; + + /*if (get_current(&arch, &path)) + { + arch_dir = NULL; + return; + }*/ + + if (ARCHIVE_get(arch, &path, &find)) + { + arch_dir = NULL; + return; + } + + // ?? "." means that we want to browse the archive. + if (!find.sym && !find.arch) // && !(path[0] == '.' && path[1] == 0)) + { + // By calling FILE_dir_first() again with an absolute path, we are sure that next calls to + // FILE_dir_next() will never call ARCHIVE_dir_next(). + FILE_dir_first(FILE_cat(PROJECT_path, path, NULL), pattern, attr); + return; + } + + if (pattern == NULL || !*pattern) + pattern = "*"; + + arch = find.arch; + arch_dir = arch->arch; + arch_index = 0; + + STRING_free(&arch_pattern); + arch_pattern = STRING_new_zero(pattern); + + //if (arch_dir->header.version == 2) + //{ + if (find.index >= 0) + abs_len = sprintf(abs_path, "/%d:", find.index); + else + abs_len = 0; + /*} + else + { + ARCH_get_absolute_path(path, strlen(path), abs_path, &abs_len); + + if (abs_len && abs_path[abs_len - 1] != '/') + { + abs_path[abs_len] = '/'; + abs_len++; + } + }*/ + + STRING_free(&arch_prefix); + if (abs_len) + arch_prefix = STRING_new(abs_path, abs_len); +} + + +bool ARCHIVE_dir_next(char **name, int *len, int attr) +{ + ARCH_SYMBOL *asym; + SYMBOL *sym; + char *s = NULL; + int l = 0; + int lenp; + + /*if (arch_fd < 0) + return FILE_dir_next(name, len);*/ + + if (!arch_dir) + return TRUE; + + lenp = STRING_length(arch_prefix); + + for(;;) + { + if (arch_index >= arch_dir->header.n_symbol) + return TRUE; + + //sym = &arch_dir->symbol[arch_index].sym; + asym = &arch_dir->symbol[arch_dir->sort[arch_index]]; + sym = &asym->sym; + + if (arch_pattern == NULL) + break; + + arch_index++; + + if (attr == GB_STAT_DIRECTORY && (asym->pos >= 0)) + continue; + + if (sym->len < lenp) + continue; + + if (lenp) + { + if (strncmp(sym->name, arch_prefix, lenp)) + continue; + } + else // special case: root directory when header.version == 2 + { + if (!strncmp(sym->name, "/", 1)) + continue; + } + + s = sym->name + lenp; + l = sym->len - lenp; + + if (l < 0) + continue; + + if (memchr(s, '/', l)) + continue; + + if (!REGEXP_match(arch_pattern, STRING_length(arch_pattern), s, l)) + continue; + + break; + } + + *name = s; + *len = l; + return FALSE; +} + + +bool ARCHIVE_check_addr(char *addr) +{ + ARCHIVE *arch; + ARCH *a; + + LIST_for_each(arch, _archive_list) + { + a = arch->arch; + if (a && addr >= a->addr && addr < &a->addr[a->length]) + return FALSE; + } + + return TRUE; +} + +void ARCHIVE_browse(ARCHIVE *arch, void (*found)(const char *path, int64_t size)) +{ + int i; + ARCH_SYMBOL *asym; + SYMBOL *sym; + ARCH *a = arch->arch; + char *path; + char *temp; + int size; + int ip; + + for (i = 0; i < a->header.n_symbol; i++) + { + asym = &a->symbol[i]; + sym = &asym->sym; + + size = asym->len; + + path = STRING_new(sym->name, sym->len); + for(;;) + { + if (*path != '/') + break; + + ip = atoi(&path[1]); + sym = &a->symbol[ip].sym; + + temp = path; + path = STRING_new(sym->name, sym->len); + if (path[sym->len - 1] != '/') + path = STRING_add(path, "/", 1); + path = STRING_add(path, strchr(temp, ':') + 1, 0); + STRING_free(&temp); + } + + (*found)(path, size); + STRING_free(&path); + } +} + +char *ARCHIVE_get_version(ARCHIVE *arch) +{ + COMPONENT *current; + char *buffer; + int len; + int n; + char *line; + + if (!arch->version_loaded) + { + current = COMPONENT_current; + COMPONENT_current = (COMPONENT *)arch->current_component; + + ON_ERROR_1(error_ARCHIVE_load_exported_class, current) + { + STREAM_load(".startup", &buffer, &len); + + n = 0; + line = strtok(buffer, "\n"); + while (line) + { + n++; + if (n == 4) + { + arch->version = STRING_new_zero(line); + break; + } + line = strtok(NULL, "\n"); + } + + FREE(&buffer); + } + END_ERROR + + COMPONENT_current = current; + arch->version_loaded = TRUE; + } + + return arch->version; +} diff --git a/main/gbx/gbx_archive.h b/main/gbx/gbx_archive.h new file mode 100644 index 00000000..b4f3fe88 --- /dev/null +++ b/main/gbx/gbx_archive.h @@ -0,0 +1,103 @@ +/*************************************************************************** + + gbx_archive.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_ARCHIVE_H +#define __GBX_ARCHIVE_H + +#define PROJECT_EXEC + +#include "gb_table.h" +#include "gb_file.h" +#include "gb_arch.h" +#include "gb_list.h" + +// ARCHIVE_load_exported_class() pass argument +#define AR_FIND_ONLY 1 +#define AR_LOAD_ONLY 2 +#define AR_FIND_AND_LOAD 3 + +typedef + struct { + LIST list; + ARCH *arch; + const char *name; + char *version; + char *domain; + TABLE *classes; + char *path; + void *current_component; + struct _CLASS **exported; + void *jit_library; + unsigned translation_loaded : 1; + unsigned exported_classes_loaded : 1; + unsigned version_loaded : 1; + unsigned jit_state : 2; + } + ARCHIVE; + +typedef + struct { + ARCHIVE *arch; + ARCH_SYMBOL *sym; + int index; + int pos; + int len; + } + ARCHIVE_FIND; + +#ifndef __ARCHIVE_C +EXTERN ARCHIVE *ARCHIVE_main; +EXTERN char *ARCHIVE_path; +#endif + +void ARCHIVE_init(void); +void ARCHIVE_exit(void); + +void ARCHIVE_create_main(const char *path); +void ARCHIVE_load_main(void); + +ARCHIVE *ARCHIVE_create(const char *name, const char *path); +void ARCHIVE_delete(ARCHIVE *arch); +void ARCHIVE_load(ARCHIVE *arch, bool load_exp); +void ARCHIVE_load_exported_class(ARCHIVE *arch, int pass); +char *ARCHIVE_get_version(ARCHIVE *arch); + +bool ARCHIVE_get(ARCHIVE *arch, const char **ppath, ARCHIVE_FIND *find); + +void ARCHIVE_read(ARCHIVE *arch, int pos, void *buffer, int len); + +bool ARCHIVE_exist(ARCHIVE *arch, const char *path); +void ARCHIVE_stat(ARCHIVE *arch, const char *path, FILE_STAT *info); +bool ARCHIVE_is_dir(ARCHIVE *arch, const char *path); + +void ARCHIVE_dir_first(ARCHIVE *arch, const char *path, const char *pattern, int attr); +bool ARCHIVE_dir_next(char **name, int *len, int attr); + +bool ARCHIVE_find_from_path(ARCHIVE **parch, const char **ppath); +bool ARCHIVE_get_current(ARCHIVE **parch); + +bool ARCHIVE_check_addr(char *addr); + +void ARCHIVE_browse(ARCHIVE *arch, void (*found)(const char *path, int64_t size)); + +#endif diff --git a/main/gbx/gbx_c_application.c b/main/gbx/gbx_c_application.c new file mode 100644 index 00000000..ca7fd875 --- /dev/null +++ b/main/gbx/gbx_c_application.c @@ -0,0 +1,404 @@ +/*************************************************************************** + + gbx_c_application.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_APPLICATION_C + +#include "gambas.h" +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_project.h" +#include "gbx_local.h" +#include "gbx_event.h" +#include "gbx_string.h" +#include "gbx_exec.h" +#include "gbx_extern.h" +#include "gbx_object.h" + +#include "gbx_c_application.h" + +extern char **environ; + +static bool _daemon = FALSE; + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Application_Path) + + if (PROJECT_override) + GB_ReturnNewZeroString(FILE_get_dir(PROJECT_override)); + else + GB_ReturnString(PROJECT_path); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Name) + + GB_ReturnConstZeroString(PROJECT_name); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Title) + + GB_ReturnConstZeroString(LOCAL_gettext(PROJECT_title)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Handle) + + GB_ReturnInt(getpid()); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ParentHandle) + + GB_ReturnInt(getppid()); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Dir) + + GB_ReturnString(PROJECT_oldcwd); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Version) + + GB_ReturnConstZeroString(PROJECT_version); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Application_Args_Count) + + GB_ReturnInt(PROJECT_argc); + +END_PROPERTY + +BEGIN_PROPERTY(Application_Args_Max) + + GB_ReturnInt(PROJECT_argc - 1); + +END_PROPERTY + + +BEGIN_METHOD(Application_Args_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0) + { + GB_Error((char *)E_ARG); + return; + } + + if (index >= PROJECT_argc) + GB_ReturnVoidString(); + else if (index == 0 && PROJECT_override) + GB_ReturnNewZeroString(FILE_get_name(PROJECT_override)); + else + GB_ReturnConstZeroString(PROJECT_argv[index]); + +END_METHOD + + +BEGIN_METHOD_VOID(Application_Args_next) + + int *index = (int*)GB_GetEnum(); + + if (*index >= PROJECT_argc) + GB_StopEnum(); + else + { + GB_ReturnConstZeroString(PROJECT_argv[*index]); + (*index)++; + } + +END_METHOD + + +BEGIN_PROPERTY(Application_Args_Copy) + + GB_ARRAY array; + int i; + char *arg; + + GB_ArrayNew(&array, GB_T_STRING, PROJECT_argc); + for (i = 0; i < PROJECT_argc; i++) + { + arg = PROJECT_argv[i]; + if (arg && *arg) + *(char **)GB_ArrayGet(array, i) = STRING_new_zero(arg); + } + + GB_ReturnObject(array); + +END_PROPERTY + +/*BEGIN_PROPERTY(Application_Args_Name) + + if (READ_PROPERTY) + GB_ReturnConstZeroString(PROJECT_argv[0]); + else + { + GB_StoreString(PROP(GB_STRING), &PROJECT_argname); + PROJECT_argv[0] = PROJECT_argname; + } + +END_PROPERTY*/ + +//------------------------------------------------------------------------- + +static int get_environ_count() +{ + int n = 0; + + while(environ[n]) + n++; + + return n; +} + +BEGIN_PROPERTY(Application_Env_Count) + + GB_ReturnInteger(get_environ_count()); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Env_Max) + + GB_ReturnInteger(get_environ_count() - 1); + +END_PROPERTY + + +BEGIN_METHOD(Application_Env_get, GB_STRING key) + + GB_ReturnNewZeroString(getenv(GB_ToZeroString(ARG(key)))); + +END_METHOD + + +BEGIN_METHOD(Application_Env_put, GB_STRING value; GB_STRING key) + + setenv(GB_ToZeroString(ARG(key)), GB_ToZeroString(ARG(value)), 1); + +END_METHOD + + +BEGIN_METHOD_VOID(Application_Env_next) + + int *index = (int*)GB_GetEnum(); + char *pos; + char *key; + + if (environ[*index] == NULL) + GB_StopEnum(); + else + { + key = environ[*index]; + pos = strchr(key, '='); + if (!pos) + GB_ReturnVoidString(); + else + GB_ReturnConstString(key, pos - key); + (*index)++; + } + +END_METHOD + +BEGIN_PROPERTY(Application_Env_Copy) + + GB_ARRAY array; + int i, n; + char *arg; + + n = get_environ_count(); + GB_ArrayNew(&array, GB_T_STRING, n - 1); + for (i = 0; i < n; i++) + { + arg = environ[i]; + if (arg && *arg) + *(char **)GB_ArrayGet(array, i) = STRING_new_zero(environ[i]); + } + + GB_ReturnObject(array); + +END_PROPERTY + +//------------------------------------------------------------------------- + +static void init_again(int old_pid) +{ + char old[PATH_MAX]; + + FILE_remove_temp_file(); + snprintf(old, sizeof(old),FILE_TEMP_DIR, getuid(), old_pid); + rename(old, FILE_make_temp(NULL, NULL)); + FILE_chdir(PROJECT_path); +} + +BEGIN_PROPERTY(Application_Daemon) + + int old_pid; + + if (READ_PROPERTY) + GB_ReturnBoolean(_daemon); + else + { + if (!_daemon && VPROP(GB_BOOLEAN)) + { + old_pid = getpid(); + if (daemon(FALSE, FALSE)) + THROW_SYSTEM(errno, NULL); + // Argh! daemon() changes the current process id... + _daemon = TRUE; + init_again(old_pid); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Startup) + + GB_ReturnObject(PROJECT_class); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Priority) + + int pr; + + if (READ_PROPERTY) + { + errno = 0; + pr = getpriority(PRIO_PROCESS, 0); + if (pr == -1 && errno > 0) + THROW_SYSTEM(errno, NULL); + GB_ReturnInteger(pr); + } + else + { + pr = VPROP(GB_INTEGER); + + if (pr < -20) + pr = -20; + else if (pr > 19) + pr = 19; + + if (setpriority(PRIO_PROCESS, 0, VPROP(GB_INTEGER)) < 0) + THROW_SYSTEM(errno, NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Task) + + GB_ReturnBoolean(EXEC_task); + +END_PROPERTY + +#endif + +//------------------------------------------------------------------------- + +GB_DESC NATIVE_AppArgs[] = +{ + GB_DECLARE_STATIC("Args"), + + GB_STATIC_PROPERTY_READ("Count", "i", Application_Args_Count), + GB_STATIC_PROPERTY_READ("Max", "i", Application_Args_Max), + GB_STATIC_PROPERTY_READ("All", "String[]", Application_Args_Copy), + GB_STATIC_METHOD("Copy", "String[]", Application_Args_Copy, NULL), + GB_STATIC_METHOD("_get", "s", Application_Args_get, "(Index)i"), + GB_STATIC_METHOD("_next", "s", Application_Args_next, NULL), + //GB_STATIC_PROPERTY("Name", "s", Application_Args_Name), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_AppEnv[] = +{ + GB_DECLARE_STATIC("Env"), + + GB_STATIC_PROPERTY_READ("Count", "i", Application_Env_Count), + GB_STATIC_PROPERTY_READ("Max", "i", Application_Env_Max), + GB_STATIC_METHOD("Copy", "String[]", Application_Env_Copy, NULL), + GB_STATIC_METHOD("_get", "s", Application_Env_get, "(Key)s"), + GB_STATIC_METHOD("_put", NULL, Application_Env_put, "(Value)s(Key)s"), + GB_STATIC_METHOD("_next", "s", Application_Env_next, NULL), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_App[] = +{ + GB_DECLARE_STATIC("Application"), + + GB_STATIC_PROPERTY_SELF("Args", "Args"), + GB_STATIC_PROPERTY_SELF("Env", "Env"), + GB_STATIC_PROPERTY_READ("Path", "s", Application_Path), + GB_STATIC_PROPERTY_READ("Name", "s", Application_Name), + GB_STATIC_PROPERTY_READ("Title", "s", Application_Title), + GB_STATIC_PROPERTY_READ("Id", "i", Application_Handle), + GB_STATIC_PROPERTY_READ("Handle", "i", Application_Handle), + GB_STATIC_PROPERTY_READ("ParentHandle", "i", Application_ParentHandle), + GB_STATIC_PROPERTY_READ("Version", "s", Application_Version), + GB_STATIC_PROPERTY_READ("Dir", "s", Application_Dir), + GB_STATIC_PROPERTY("Daemon", "b", Application_Daemon), + GB_STATIC_PROPERTY_READ("Startup", "Class", Application_Startup), + GB_STATIC_PROPERTY("Priority", "i", Application_Priority), + GB_STATIC_PROPERTY_READ("Task", "b", Application_Task), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_application.h b/main/gbx/gbx_c_application.h new file mode 100644 index 00000000..916b03f5 --- /dev/null +++ b/main/gbx/gbx_c_application.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gbx_c_application.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_APPLICATION_H +#define __GBX_C_APPLICATION_H + +#include "gambas.h" + +#ifndef __GBX_C_APPLICATION_C +extern GB_DESC NATIVE_AppEnv[]; +extern GB_DESC NATIVE_AppArgs[]; +extern GB_DESC NATIVE_App[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_array.c b/main/gbx/gbx_c_array.c new file mode 100644 index 00000000..dc737ac0 --- /dev/null +++ b/main/gbx/gbx_c_array.c @@ -0,0 +1,2604 @@ +/*************************************************************************** + + gbx_c_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_ARRAY_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_array.h" +#include "gb_limit.h" +#include "gbx_class.h" + +#include "gbx_exec.h" +#include "gbx_date.h" +#include "gbx_variant.h" +#include "gbx_compare.h" +#include "gbx_class.h" +#include "gbx_object.h" +#include "gbx_api.h" +#include "gbx_c_file.h" +#include "gbx_struct.h" +#include "gbx_math.h" +#include "gbx_c_array.h" + +static bool _create_static_array; + + +void *CARRAY_out_of_bounds() +{ + GB_Error((char *)E_BOUND); + return NULL; +} + + +void CARRAY_static_array() +{ + GB_Error((char *)E_SARRAY); +} + + +static void error_multidimensional_array() +{ + GB_Error((char *)E_MARRAY); +} + +#define check_not_static(_object) ((((CARRAY *)_object)->ref) ? CARRAY_static_array(), TRUE : FALSE) +#define check_not_read_only(_object) ((((CARRAY *)_object)->read_only) ? CARRAY_static_array(), TRUE : FALSE) +#define check_not_multi(_object) ((((CARRAY *)_object)->n_dim) ? error_multidimensional_array(), TRUE : FALSE) + + +static bool check_start_length(int count, int *start, int *length) +{ + if (*start < 0) + { + CARRAY_out_of_bounds(); + return TRUE; + } + + if (*start >= count) + *length = 0; + + if (*length == -1) + *length = count - *start; + + if (*length < 0 || (*start + *length) > count) + { + CARRAY_out_of_bounds(); + return TRUE; + } + + return FALSE; +} + + +#define get_dim(_array) (_array->n_dim + 1) + +static int calc_dim(int *dim) +{ + int d; + + if (dim == NULL) + return 1; + else + { + for(d = 0;; d++) + { + if (dim[d] < 0) + return (d + 1); + } + } +} + +static int get_bound(CARRAY *_object, int d) +{ + if (THIS->n_dim) + return abs(THIS->dim[d]); + else + return THIS->count; +} + + +CLASS *CARRAY_get_array_class(CLASS *class, CTYPE ctype) +{ + if (ctype.id == T_NULL) + { + if (TYPE_is_pure_object((TYPE)class)) + return CLASS_get_array_class(class); + + ctype.id = (unsigned char)(TYPE)class; + ctype.value = -1; + } + + switch (ctype.id) + { + case T_BOOLEAN: class = CLASS_BooleanArray; break; + case T_BYTE: class = CLASS_ByteArray; break; + case T_SHORT: class = CLASS_ShortArray; break; + case T_INTEGER: class = CLASS_IntegerArray; break; + case T_LONG: class = CLASS_LongArray; break; + case T_SINGLE: class = CLASS_SingleArray; break; + case T_FLOAT: class = CLASS_FloatArray; break; + case T_STRING: class = CLASS_StringArray; break; + case T_DATE: class = CLASS_DateArray; break; + case T_POINTER: class = CLASS_PointerArray; break; + + case T_OBJECT: + if (ctype.value >= 0) + class = CLASS_get_array_class(class->load->class_ref[ctype.value]); + else + class = CLASS_ObjectArray; + break; + + case TC_STRUCT: + class = CLASS_get_array_of_struct_class(class->load->class_ref[ctype.value]); + break; + + default: + class = CLASS_VariantArray; + break; + } + + return class; +} + + +#define get_data_multi CARRAY_get_data_multi +#define get_data CARRAY_get_data +#define get_data_unsafe CARRAY_get_data_unsafe + +void *CARRAY_get_data_multi(CARRAY *_object, GB_INTEGER *arg, int nparam) +{ + int index; + + //fprintf(stderr, "get_data_multi: nparam = %d\n", nparam); + + nparam++; + + if (THIS->n_dim) + { + int max; + int i; + int d; + + index = 0; + + for (i = 0; i <= THIS->n_dim; i++) + { + if (i > nparam) + break; + + max = abs(THIS->dim[i]); + VALUE_conv_integer((VALUE *)&arg[i]); + d = arg[i].value; + + if (d < 0 || d >= max) + return CARRAY_out_of_bounds(); + + index *= max; + index += d; + } + + if (i != nparam) + { + GB_Error((char *)E_NDIM); + return NULL; + } + } + else + { + if (nparam != 1) + { + GB_Error((char *)E_NDIM); + return NULL; + } + + index = arg->value; + + if ((index < 0) || (index >= THIS->count)) + return CARRAY_out_of_bounds(); + } + + return (void *)((char *)(THIS->data) + index * THIS->size); +} + + +static int get_count(int *dim) +{ + int i, size; + + size = 1; + + for (i = 0;; i++) + { + size *= dim[i]; + if (size < 0) + { + size = (-size); + break; + } + } + + return size; +} + + +static void release_one(CARRAY *_object, int i) +{ + if (THIS->type == T_STRING) + STRING_unref(&(((char **)(THIS->data))[i])); + else if (THIS->type == T_VARIANT) + VARIANT_free(&(((VARIANT *)(THIS->data))[i])); + else if (TYPE_is_object(THIS->type)) + OBJECT_UNREF(((void **)(THIS->data))[i]); +} + + +static void release_static(TYPE type, void *data, int start, int end) +{ + int i; + + if (type == T_STRING) + { + for (i = start; i < end; i++) + STRING_unref(&((char **)data)[i]); + } + else if (type == T_VARIANT) + { + for (i = start; i < end; i++) + VARIANT_free(&((VARIANT *)data)[i]); + } + else if (TYPE_is_object(type)) + { + for (i = start; i < end; i++) + OBJECT_UNREF(((void **)data)[i]); + } +} + + +static void release(CARRAY *_object, int start, int end) +{ + if (end < 0) + end = THIS->count; + + release_static(THIS->type, THIS->data, start, end); +} + + +static void borrow(CARRAY *_object, int start, int end) +{ + int i; + + if (end < 0) + end = THIS->count; + + if (THIS->type == T_STRING) + { + for (i = start; i < end; i++) + STRING_ref(((char **)(THIS->data))[i]); + } + else if (THIS->type == T_VARIANT) + { + for (i = start; i < end; i++) + VARIANT_keep(&((VARIANT *)(THIS->data))[i]); + } + else if (TYPE_is_object(THIS->type)) + { + for (i = start; i < end; i++) + OBJECT_REF_CHECK(((void **)(THIS->data))[i]); + } +} + + +static void clear(CARRAY *_object) +{ + if (check_not_read_only(THIS)) + return; + + release(THIS, 0, -1); + + if (THIS->n_dim) + { + memset(THIS->data, 0, THIS->size * THIS->count); + } + else + { + ARRAY_delete(&THIS->data); + ARRAY_create_with_size(&THIS->data, THIS->size, 8); + THIS->count = 0; + } +} + +static void *insert(CARRAY *_object, int index) +{ + THIS->count++; + return ARRAY_insert(&THIS->data, index); +} + +size_t CARRAY_get_static_size(CLASS *class, CLASS_ARRAY *desc) +{ + uint64_t size = (uint64_t)get_count(desc->dim) * CLASS_sizeof_ctype(class, desc->ctype); + + if (size > INT_MAX) + THROW(E_MEMORY); + + return (size_t)size; +} + +int CARRAY_get_static_count(CLASS_ARRAY *desc) +{ + return get_count(desc->dim); +} + + +CARRAY *CARRAY_create_static(CLASS *class, void *ref, CLASS_ARRAY *desc, void *data) +{ + CARRAY *array; + TYPE type; + CLASS *aclass; + size_t size; + + type = CLASS_ctype_to_type(class, desc->ctype); + _create_static_array = TRUE; + aclass = CARRAY_get_array_class(class, desc->ctype); + array = OBJECT_create_native(aclass, NULL); + _create_static_array = FALSE; + + array->type = type; + array->data = data; + array->ref = ref; + array->count = get_count(desc->dim); + OBJECT_REF(ref); + + if (desc->dim[0] < 0) + array->dim = NULL; + else + { + array->dim = desc->dim; + array->n_dim = calc_dim(desc->dim) - 1; + } + + size = CLASS_sizeof_ctype(class, desc->ctype); + if (size >= (1 << 24)) + THROW(E_MEMORY); + array->size = size; + + return array; +} + + +void CARRAY_release_static(CLASS *class, CLASS_ARRAY *desc, void *data) +{ + int count = get_count(desc->dim); + + if (desc->ctype.id == TC_STRUCT) + { + CLASS *sclass = class->load->class_ref[desc->ctype.value]; + int i; + int size = CLASS_sizeof_ctype(class, desc->ctype); + char *p = (char *)data; + + for (i = 0; i < count; i++) + { + OBJECT_release_static(sclass, sclass->load->dyn, sclass->load->n_dyn, p); + p += size; + } + } + else + release_static(CLASS_ctype_to_type(class, desc->ctype), data, 0, count); +} + + +void CARRAY_get_value(CARRAY *_object, int index, VALUE *value) +{ + VALUE_read(value, get_data(THIS, index), THIS->type); +} + + +int *CARRAY_get_array_bounds(CARRAY *_object) +{ + return THIS->dim; +} + + +static void check_size(CARRAY *_object, int size, int inc) +{ + if (inc > 0) + size = (int)((uint)size + inc - 1) / inc * inc; + + if (size > (INT_MAX / THIS->size) || size < 0) + THROW(E_MEMORY); +} + + +BEGIN_METHOD(Array_new, GB_INTEGER size) + + TYPE type; + CLASS *klass; + int inc; + GB_INTEGER *sizes = ARG(size); + int nsize = GB_NParam() + 1; + int i; + + if (_create_static_array) + return; + + klass = OBJECT_class(THIS); + + type = (TYPE)klass->array_type; + if (!type) + { + GB_Error("Bad array type"); + return; + } + + /*if (TYPE_is_object(type)) + THIS->mode = T_OBJECT; + else + THIS->mode = (int)type;*/ + + //printf("Array_new: type = %d nsize = %d\n", type, nsize); + + THIS->type = type; + THIS->size = TYPE_sizeof_memory(type); + + if (nsize <= 1) + { + int size = VARGOPT(size, 0); + + if (size < 0) + size = 0; + + inc = (size / 8) & ~7; + if (inc < 8) + inc = 8; + else if (inc > 256) + inc = 256; + + if (size) + check_size(THIS, size, inc); + + ARRAY_create_with_size(&THIS->data, THIS->size, inc); + if (size > 0) + ARRAY_add_many_void(&THIS->data, size); + + THIS->count = size; + } + else + { + if (nsize > MAX_ARRAY_DIM) + { + GB_Error((char *)E_NDIM); + return; + } + + uint64_t size = 1; + for (i = 0; i < nsize; i++) + { + VALUE_conv_integer((VALUE *)&sizes[i]); + if (sizes[i].value < 1) + { + GB_Error((char *)E_ARG); + return; + } + size *= sizes[i].value; + check_size(THIS, size, 0); + } + + ALLOC_ZERO(&THIS->dim, nsize * sizeof(int)); + + nsize--; + THIS->n_dim = nsize; + for (i = 0; i < nsize; i++) + THIS->dim[i] = sizes[i].value; + THIS->dim[i] = (- sizes[i].value); + + ARRAY_create_with_size(&THIS->data, THIS->size, 8); + ARRAY_add_many_void(&THIS->data, (int)size); + + THIS->count = size; + } + + +END_METHOD + + +BEGIN_PROPERTY(Array_Type) + + GB_ReturnInteger(THIS->type); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Array_free) + + if (THIS->ref) + { + OBJECT_UNREF(THIS->ref); + return; + } + + release(THIS, 0, -1); + + ARRAY_delete(&THIS->data); + + FREE(&THIS->dim); + +END_METHOD + + +BEGIN_PROPERTY(Array_ReadOnly) + + if (READ_PROPERTY) + GB_ReturnBoolean(THIS->read_only); + else + { + bool read_only = VPROP(GB_BOOLEAN); + if (THIS->read_only == read_only) + return; + + if (THIS->read_only) + CARRAY_static_array(); + else + THIS->read_only = TRUE; + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Array_Clear) + + clear(THIS); + +END_METHOD + + +BEGIN_PROPERTY(Array_Data) + + GB_ReturnPointer(THIS->data); + +END_PROPERTY + + +BEGIN_PROPERTY(Array_Count) + + GB_ReturnInt(THIS->count); + +END_PROPERTY + + +BEGIN_PROPERTY(Array_Max) + + GB_ReturnInt(THIS->count - 1); + +END_PROPERTY + + +BEGIN_PROPERTY(Array_Empty) + + GB_ReturnBoolean(THIS->count == 0); + +END_PROPERTY + + +static bool copy_remove(CARRAY *_object, int start, int length, bool copy, bool remove) +{ + CARRAY *array = NULL; + int count = THIS->count; + void *data; + int i, nsize; + + if (start != 0 || length != -1) + { + if (check_not_multi(THIS)) + return TRUE; + } + + if (remove && check_not_static(THIS) && check_not_read_only(THIS)) + return TRUE; + + if (check_start_length(count, &start, &length)) + return TRUE; + + if (copy) + { + GB_ArrayNew((GB_ARRAY *)POINTER(&array), THIS->type, 0); + + if (length > 0) + { + data = ARRAY_insert_many(&array->data, 0, length); + array->count += length; + memmove(data, get_data(THIS, start), length * THIS->size); + borrow(array, 0, -1); + } + + if (THIS->n_dim) + { + nsize = get_dim(THIS); + ALLOC_ZERO(&array->dim, nsize * sizeof(int)); + + for (i = 0; i < nsize; i++) + array->dim[i] = THIS->dim[i]; + } + } + + if (remove) + { + release(THIS, start, start + length); + ARRAY_remove_many(&THIS->data, start, length); + THIS->count -= length; + } + + if (copy) + GB_ReturnObject(array); + + return FALSE; +} + + +BEGIN_METHOD(Array_Remove, GB_INTEGER index; GB_INTEGER length) + + copy_remove(THIS, VARG(index), VARGOPT(length, 1), FALSE, TRUE); + +END_METHOD + + +BEGIN_METHOD(Array_Copy, GB_INTEGER start; GB_INTEGER length) + + int start, length; + + if (MISSING(start) && MISSING(length)) + { + start = 0; + length = -1; + } + else + { + start = VARGOPT(start, 0); + length = VARGOPT(length, 1); + } + + copy_remove(THIS, start, length, TRUE, FALSE); + +END_METHOD + + +BEGIN_METHOD(Array_Extract, GB_INTEGER start; GB_INTEGER length) + + copy_remove(THIS, VARG(start), VARGOPT(length, 1), TRUE, TRUE); + +END_METHOD + + +void CARRAY_resize(CARRAY *_object, int size) +{ + int count; + + if (size < 0) + { + GB_Error((char *)E_ARG); + return; + } + + count = THIS->count; + if (size == count) + return; + + if (size > count) + { + check_size(THIS, size, DATA_TO_ARRAY(THIS->data)->inc); + ARRAY_add_many_void(&THIS->data, size - count); + } + else + { + release(THIS, size, -1); + ARRAY_remove_many(&THIS->data, size, count - size); + } + + THIS->count = size; +} + + +BEGIN_METHOD(Array_Resize, GB_INTEGER size) + + int size = VARG(size); + + if (check_not_static(THIS) && check_not_multi(THIS) && check_not_read_only(THIS)) + return; + + CARRAY_resize(THIS, size); + +END_METHOD + +/* +BEGIN_METHOD(Array_Swap, GB_INTEGER index; GB_INTEGER index2) + + int size = THIS->size; + int i1, i2; + void *p1, *p2; + + if (check_not_multi(THIS)) + return; + + i1 = VARG(index); + i2 = VARG(index2); + + p1 = get_data(THIS, i1); + if (!p1) + return; + p2 = get_data(THIS, i2); + if (!p2) + return; + + switch(size) + { + case 1: + { + char t = *(char *)p1; + *(char *)p1 = *(char *)p2; + *(char *)p2 = t; + break; + } + + case 2: + { + short t = *(short *)p1; + *(short *)p1 = *(short *)p2; + *(short *)p2 = t; + break; + } + + case 4: + { + int t = *(int *)p1; + *(int *)p1 = *(int *)p2; + *(int *)p2 = t; + break; + } + + case 8: + { + int64_t t = *(char *)p1; + *(char *)p1 = *(char *)p2; + *(char *)p2 = t; + break; + } + + default: + { + unsigned char *p; + unsigned char *q; + unsigned char *const end = (unsigned char *)p1 + size; + + for (p = p1, q = p2; p < end; ++p, ++q ) { + const unsigned char t = *p; + *p = *q; + *q = t; + } + } + } + +END_METHOD +*/ + +BEGIN_METHOD_VOID(Array_Shuffle) + + int count = THIS->count; + int size = THIS->size; + void *p1, *p2; + int i, j; + void *swap; + + if (check_not_read_only(THIS) || count <= 1) + return; + + switch (size) + { + case 1: swap = &&__SWAP_BYTE; break; + case 2: swap = &&__SWAP_SHORT; break; + case 4: swap = &&__SWAP_INT; break; + case 8: swap = &&__SWAP_LONG; break; + default: swap = &&__SWAP_ANY; + } + + for (i = count - 1; i >= 1; i--) + { + j = (int)(rnd() * (i + 1)); + p1 = CARRAY_get_data_unsafe(THIS, i); + p2 = CARRAY_get_data_unsafe(THIS, j); + + goto *swap; + + __SWAP_BYTE: + { + char t = *(char *)p1; + *(char *)p1 = *(char *)p2; + *(char *)p2 = t; + continue; + } + + __SWAP_SHORT: + { + short t = *(short *)p1; + *(short *)p1 = *(short *)p2; + *(short *)p2 = t; + continue; + } + + __SWAP_INT: + { + int t = *(int *)p1; + *(int *)p1 = *(int *)p2; + *(int *)p2 = t; + continue; + } + + __SWAP_LONG: + { + int64_t t = *(int64_t *)p1; + *(int64_t *)p1 = *(int64_t *)p2; + *(int64_t *)p2 = t; + continue; + } + + __SWAP_ANY: + { + unsigned char *p; + unsigned char *q; + unsigned char *const end = (unsigned char *)p1 + size; + + for (p = p1, q = p2; p < end; ++p, ++q ) { + const unsigned char t = *p; + *p = *q; + *q = t; + } + } + } + +END_METHOD + +static void add(CARRAY *_object, GB_VALUE *value, int index) +{ + void *data; + + if (check_not_static(THIS) && check_not_multi(THIS) && check_not_read_only(THIS)) + return; + + data = insert(THIS, index); + GB_Store(THIS->type, value, data); +} + + +#define IMPLEMENT_add(_type, _gtype) \ +BEGIN_METHOD(Array_##_type##_Add, GB_##_gtype value; GB_INTEGER index) \ +\ + add(THIS, (GB_VALUE *)(void *)ARG(value), VARGOPT(index, -1)); \ + \ +END_METHOD \ +\ +BEGIN_METHOD(Array_##_type##_Push, GB_##_gtype value) \ +\ + add(THIS, (GB_VALUE *)(void *)ARG(value), -1); \ + \ +END_METHOD + +IMPLEMENT_add(Integer, INTEGER) +IMPLEMENT_add(Long, LONG) +IMPLEMENT_add(Float, FLOAT) +IMPLEMENT_add(Single, SINGLE) +IMPLEMENT_add(Date, DATE) +IMPLEMENT_add(String, STRING) +IMPLEMENT_add(Object, OBJECT) +IMPLEMENT_add(Variant, VARIANT) + +BEGIN_METHOD(Array_Add, GB_VARIANT value; GB_INTEGER index) + + GB_VALUE *value = (GB_VALUE *)(void *)ARG(value); + GB_Conv(value, THIS->type); + add(THIS, value, VARGOPT(index, -1)); + +END_METHOD + +BEGIN_METHOD(Array_Push, GB_VARIANT value) + + GB_VALUE *value = (GB_VALUE *)(void *)ARG(value); + GB_Conv(value, THIS->type); + add(THIS, value, -1); + +END_METHOD + + +#define IMPLEMENT_put(_type, _gtype) \ +BEGIN_METHOD(Array_##_type##_put, GB_##_gtype value; GB_INTEGER index) \ + \ + if (check_not_read_only(THIS)) \ + return; \ + void *data = get_data_multi(THIS, ARG(index), GB_NParam()); \ + if (!data) return; \ + GB_Store(GB_T_##_gtype, (GB_VALUE *)(void *)ARG(value), data); \ + \ +END_METHOD + +#define IMPLEMENT_put2(_type, _gtype, _gstore) \ +BEGIN_METHOD(Array_##_type##_put, GB_##_gtype value; GB_INTEGER index) \ +\ + if (check_not_read_only(THIS)) \ + return; \ + void *data = get_data_multi(THIS, ARG(index), GB_NParam()); \ + if (!data) return; \ + GB_Store(GB_T_##_gstore, (GB_VALUE *)(void *)ARG(value), data); \ + \ +END_METHOD + +BEGIN_METHOD(Array_put, GB_VARIANT value; GB_INTEGER index) + + GB_VALUE *value; + void *data; + + if (check_not_read_only(THIS)) + return; + + data = get_data_multi(THIS, ARG(index), GB_NParam()); + if (!data) return; + + value = (GB_VALUE *)(void *)ARG(value); + GB_Conv(value, THIS->type); + + GB_Store(THIS->type, value, data); + +END_METHOD + +IMPLEMENT_put(Integer, INTEGER) +IMPLEMENT_put2(Short, INTEGER, SHORT) +IMPLEMENT_put2(Byte, INTEGER, BYTE) +IMPLEMENT_put2(Boolean, INTEGER, BOOLEAN) +IMPLEMENT_put(Long, LONG) +IMPLEMENT_put(Float, FLOAT) +IMPLEMENT_put(Single, SINGLE) +IMPLEMENT_put(Date, DATE) +IMPLEMENT_put(String, STRING) +IMPLEMENT_put(Object, OBJECT) +IMPLEMENT_put(Variant, VARIANT) + + +BEGIN_METHOD(Array_Fill, GB_VALUE value; GB_INTEGER start; GB_INTEGER length) + + int count = THIS->count; + int start = VARGOPT(start, 0); + int length = VARGOPT(length, count); + int i; + void *data; + int size; + + if (check_start_length(count, &start, &length)) + return; + + VALUE_conv((VALUE *)ARG(value), THIS->type); + + data = get_data(THIS, start); + size = THIS->size; + + for (i = 0; i < length; i++) + { + GB_Store(THIS->type, (GB_VALUE *)ARG(value), data); + data += size; + } + +END_METHOD + + +BEGIN_METHOD(Array_Insert, GB_OBJECT array; GB_INTEGER pos) + + int pos = VARGOPT(pos, -1); + CARRAY *array = (CARRAY *)VARG(array); + void *data; + int count; + + if (GB_CheckObject(array)) + return; + + count = array->count; + + if (count > 0) + { + if (check_not_static(THIS) && check_not_multi(THIS)) + { + GB_ReturnNull(); + return; + } + + if (pos < 0) + pos = THIS->count; + + data = ARRAY_insert_many(&THIS->data, pos, count); + THIS->count += count; + borrow(array, 0, -1); + memmove(data, array->data, count * THIS->size); + } + + GB_ReturnObject(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Array_Pop) + + int index = THIS->count - 1; + + if (check_not_static(THIS) && check_not_multi(THIS)) + return; + + if (index < 0) + { + GB_Error((char *)E_ARG); + return; + } + + GB_ReturnPtr(THIS->type, get_data(THIS, index)); + + BORROW(&TEMP); + release_one(THIS, index); + ARRAY_remove(&THIS->data, index); + THIS->count--; + UNBORROW(&TEMP); + +END_METHOD + + +BEGIN_METHOD(Array_get, GB_INTEGER index) + + void *data = get_data_multi(THIS, ARG(index), GB_NParam()); + + if (data) + GB_ReturnPtr(THIS->type, data); + +END_METHOD + + +static void array_first_last(CARRAY *_object, void *_param, bool last) +{ + void *data; + int index = last ? THIS->count - 1 : 0; + + data = get_data(THIS, index); + if (!data) return; + + if (READ_PROPERTY) + { + GB_ReturnPtr(THIS->type, data); + } + else if (!check_not_read_only(THIS)) + { + GB_VALUE *value; + + value = PROP(GB_VALUE); + GB_Conv(value, THIS->type); + + GB_Store(THIS->type, value, data); + } +} + + +BEGIN_PROPERTY(Array_First) + + array_first_last(_object, _param, FALSE); + +END_PROPERTY + + +BEGIN_PROPERTY(Array_Last) + + array_first_last(_object, _param, TRUE); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Array_next) + + int *index = (int *)GB_GetEnum(); + + if (*index >= THIS->count) + GB_StopEnum(); + else + { + GB_ReturnPtr(THIS->type, get_data(THIS, *index)); + (*index)++; + } + +END_METHOD + +BEGIN_METHOD(Array_Sort, GB_INTEGER mode) + + if (THIS->count > 1) + qsort(THIS->data, THIS->count, THIS->size, COMPARE_get_func(THIS->type, VARGOPT(mode, 0))); + + GB_ReturnObject(THIS); + +END_METHOD + +static void *_using_data; +static int _using_size; +static COMPARE_FUNC _using_func; + +static int compare_using(const int *a, const int *b) +{ + return _using_func(_using_data + *a * _using_size, _using_data + *b * _using_size); +} + +BEGIN_METHOD(Array_SortUsing, GB_OBJECT order; GB_INTEGER mode) + + CARRAY *order = VARG(order); + int *sort; + int i, j, k; + void *data; + int count; + int size; + char old[sizeof(VARIANT)]; + + if (!order) + { + GB_Error((char *)E_NULL); + return; + } + + if (check_not_multi(order)) + return; + + if (order->count < THIS->count) + { + CARRAY_out_of_bounds(); + return; + } + + data = THIS->data; + count = THIS->count; + size = THIS->size; + + if (count > 1) + { + ALLOC(&sort, sizeof(int) * count); + for (i = 0; i < count; i++) + sort[i] = i; + + _using_data = order->data; + _using_size = order->size; + _using_func = COMPARE_get_func(order->type, VARGOPT(mode, 0)); + + qsort(sort, count, sizeof(int), (COMPARE_FUNC)compare_using); + + for (i = 0; i < count; i++) + { + j = i; + k = sort[j]; + if (k < 0) + continue; + + while (k != i) + { + memcpy(old, data + j * size, size); + memcpy(data + j * size, data + k * size, size); + memcpy(data + k * size, old, size); + sort[j] = -1; + j = k; + k = sort[k]; + } + + sort[j] = -1; + } + + IFREE(sort); + } + + GB_ReturnObject(THIS); + +END_METHOD + + +#define IS_FIND_SORTED() (EXEC.desc->name[5]) +#define IS_FIND_BYREF_SORTED() (EXEC.desc->name[10]) +#define IS_EXIST_SORTED() (EXEC.desc->name[6]) +#define IS_EXIST_BYREF_SORTED() (EXEC.desc->name[11]) + + +static int find(CARRAY *_object, int mode, void *value, int start, bool sorted) +{ + COMPARE_FUNC compare = COMPARE_get_func(THIS->type, mode); + int i, c; + int count = THIS->count; + + if (start < 0) + start = 0; + if (start >= count) + return (-1); + + if (sorted) + { + while (start < count) + { + i = (start + count) / 2; + c = (*compare)(value, get_data_unsafe(THIS, i)); + if (c < 0) + count = i; + else if (c > 0) + start = i + 1; + else + return i; + } + return (-1 - start); + } + else + { + for (i = start; i < count; i++) + { + if ((*compare)(value, get_data_unsafe(THIS, i)) == 0) + return i; + } + return (-1); + } +} + +#define IMPLEMENT_find_fast(_type, _gtype, _ctype) \ +static int find_##_type(CARRAY *_object, _ctype value, int start, bool sorted) \ +{ \ + int count = THIS->count; \ + _ctype *data; \ + int i; \ + \ + if (start < 0) \ + start = 0; \ + if (start > count) \ + return (-1); \ + \ + data = (_ctype *)THIS->data; \ + \ + if (sorted) \ + { \ + while (start < count) \ + { \ + i = (start + count) / 2; \ + if (value < data[i]) \ + count = i; \ + else if (value > data[i]) \ + start = i + 1; \ + else \ + return i; \ + } \ + return (-1 - start); \ + } \ + else \ + { \ + for (i = start; i < count; i++) \ + { \ + if (data[i] == value) \ + return i; \ + } \ + return (-1); \ + } \ +} \ +\ +BEGIN_METHOD(Array_##_type##_Find, _gtype value; GB_INTEGER start) \ +\ + GB_ReturnInteger(find_##_type(THIS, VARG(value), VARGOPT(start, 0), IS_FIND_SORTED())); \ +\ +END_METHOD \ +BEGIN_METHOD(Array_##_type##_Exist, _gtype value) \ +\ + GB_ReturnBoolean(find_##_type(THIS, VARG(value), 0, IS_EXIST_SORTED()) >= 0); \ +\ +END_METHOD + +#define IMPLEMENT_find(_type, _gtype) \ +BEGIN_METHOD(Array_##_type##_Find, _gtype value; GB_INTEGER start) \ +\ + GB_ReturnInt(find(THIS, 0, &VARG(value), VARGOPT(start, 0), IS_FIND_SORTED())); \ +\ +END_METHOD \ +BEGIN_METHOD(Array_##_type##_Exist, _gtype value) \ +\ + GB_ReturnBoolean(find(THIS, 0, &VARG(value), 0, IS_EXIST_SORTED()) >= 0); \ +\ +END_METHOD + +IMPLEMENT_find_fast(Boolean, GB_BOOLEAN, bool) +IMPLEMENT_find_fast(Byte, GB_INTEGER, uchar) +IMPLEMENT_find_fast(Short, GB_INTEGER, short) +IMPLEMENT_find_fast(Integer, GB_INTEGER, int) +IMPLEMENT_find_fast(Long, GB_LONG, int64_t) +IMPLEMENT_find_fast(Float, GB_FLOAT, double) +IMPLEMENT_find_fast(Single, GB_SINGLE, float) +IMPLEMENT_find(Date, GB_DATE) +IMPLEMENT_find(Variant, GB_VARIANT) + +static int find_object(CARRAY *_object, void *value, int start, bool sorted) +{ + int i, c; + void **data; + int count = THIS->count; + + if (start < 0) + start = 0; + if (start >= count) + return (-1); + + data = (void **)THIS->data; + + if (sorted) + { + while (start < count) + { + i = (start + count) / 2; + c = COMPARE_object(&value, &data[i]); + if (c < 0) + count = i; + else if (c > 0) + start = i + 1; + else + return i; + } + return (-1 - start); + } + else + { + for (i = start; i < count; i++) + { + if (!COMPARE_object(&value, &data[i])) + return i; + } + return (-1); + } +} + +BEGIN_METHOD(Array_Object_Find, GB_OBJECT value; GB_INTEGER start) + + GB_ReturnInt(find_object(THIS, VARG(value), VARGOPT(start, 0), IS_FIND_SORTED())); + +END_METHOD + +BEGIN_METHOD(Array_Object_FindByRef, GB_OBJECT value; GB_INTEGER start) + + #ifdef OS_64BITS + GB_ReturnInt(find_Long(THIS, (int64_t)VARG(value), VARGOPT(start, 0), IS_FIND_BYREF_SORTED())); + #else + GB_ReturnInt(find_Integer(THIS, (int)VARG(value), VARGOPT(start, 0), IS_FIND_BYREF_SORTED())); + #endif + +END_METHOD + +BEGIN_METHOD(Array_Object_Exist, GB_OBJECT value) + + GB_ReturnBoolean(find_object(THIS, VARG(value), 0, IS_EXIST_SORTED()) >= 0); + +END_METHOD + +BEGIN_METHOD(Array_Object_ExistByRef, GB_OBJECT value) + + #ifdef OS_64BITS + GB_ReturnBoolean(find_Long(THIS, (int64_t)VARG(value), 0, IS_EXIST_BYREF_SORTED()) >= 0); + #else + GB_ReturnBoolean(find_Integer(THIS, (int)VARG(value), 0, IS_EXIST_BYREF_SORTED()) >= 0); + #endif + +END_METHOD + + +static int find_string(CARRAY *_object, int mode, const char *value, int len_value, int start, bool sorted) +{ + char **data; + char *str; + int i, c; + int len; + int count = THIS->count; + + //fprintf(stderr, "find_string: %p %d: %.*s | %s\n", THIS, THIS->count, len_value, value, DEBUG_get_current_position()); + + if (start < 0) + start = 0; + if (start >= count) + return (-1); + + data = (char **)THIS->data; + + if (mode == GB_COMP_BINARY) + { + if (sorted) + { + while (start < count) + { + i = (start + count) / 2; + str = data[i]; + len = STRING_length(str); + c = STRING_compare(value, len_value, str, len); + if (c < 0) + count = i; + else if (c > 0) + start = i + 1; + else + return i; + } + return (-1 - start); + } + else + { + for (i = start; i < count; i++) + { + str = data[i]; + len = STRING_length(str); + if (STRING_equal(value, len_value, str, len)) + return i; + } + } + } + else if (mode == GB_COMP_NOCASE) + { + if (sorted) + { + while (start < count) + { + i = (start + count) / 2; + str = data[i]; + len = STRING_length(str); + c = STRING_compare_ignore_case(value, len_value, str, len); + if (c < 0) + count = i; + else if (c > 0) + start = i + 1; + else + return i; + } + return (-1 - start); + } + else + { + for (i = start; i < count; i++) + { + str = data[i]; + len = STRING_length(str); + if (STRING_equal_ignore_case(value, len_value, str, len)) + return i; + } + } + } + else + { + COMPARE_STRING_FUNC compare = COMPARE_get_string_func(mode); + bool nocase = mode & GB_COMP_NOCASE; + + // Beware: order of comparison arguments is important! + + if (sorted) + { + while (start < count) + { + i = (start + count) / 2; + str = data[i]; + len = STRING_length(str); + // pattern must be second, but then the strings and the comparison result are inverted. + c = (*compare)(str, len, value, len_value, nocase, FALSE); + if (c > 0) + count = i; + else if (c < 0) + start = i + 1; + else + return i; + } + return (-1 - start); + } + else + { + for (i = start; i < count; i++) + { + str = data[i]; + len = STRING_length(str); + if ((*compare)(str, len, value, len_value, nocase, FALSE) == 0) + return i; + } + } + } + + return (-1); +} + + +BEGIN_METHOD(Array_String_Find, GB_STRING value; GB_INTEGER mode; GB_INTEGER start) + + GB_ReturnInteger(find_string(THIS, VARGOPT(mode, GB_COMP_BINARY), STRING(value), LENGTH(value), VARGOPT(start, 0), IS_FIND_SORTED())); + +END_METHOD + + +BEGIN_METHOD(Array_String_Exist, GB_STRING value; GB_INTEGER mode) + + GB_ReturnBoolean(find_string(THIS, VARGOPT(mode, GB_COMP_BINARY), STRING(value), LENGTH(value), 0, IS_EXIST_SORTED()) >= 0); + +END_METHOD + + +BEGIN_METHOD(Array_String_join, GB_STRING sep; GB_STRING esc) + + char *sep = ","; + uint lsep = 1; + char *esc = ""; + uint lesc = 0; + char escl, escr; + int i; + char **data = (char **)THIS->data; + char *p, *p2; + int l, max; + + if (!MISSING(sep)) + { + sep = STRING(sep); + lsep = LENGTH(sep); + } + + if (!MISSING(esc)) + { + esc = STRING(esc); + lesc = LENGTH(esc); + + if (lesc == 1) + escl = escr = esc[0]; + else if (lesc >= 2) + { + escl = esc[0]; + escr = esc[1]; + } + } + + if (lesc == 0) + { + max = 0; + for (i = 0; i < THIS->count; i++) + max += STRING_length(data[i]) + lsep; + if (THIS->count) + max -= lsep; + + STRING_start_len(max); + + for (i = 0; i < THIS->count; i++) + { + p = data[i]; + l = STRING_length(data[i]); + + if (i) + STRING_make(sep, lsep); + if (l) + STRING_make(p, l); + } + } + else if (*sep && escr == *sep) + { + STRING_start(); + + for (i = 0; i < THIS->count; i++) + { + p = data[i]; + l = STRING_length(data[i]); + + if (i) + STRING_make(sep, lsep); + + if (l == 0) + continue; + + for(;;) + { + p2 = index(p, escr); + if (p2) + { + STRING_make(p, p2 - p); + STRING_make_char(escl); + STRING_make_char(escr); + p = p2 + 1; + } + else + { + STRING_make(p, l + data[i] - p); + break; + } + } + } + } + else + { + STRING_start(); + + for (i = 0; i < THIS->count; i++) + { + p = data[i]; + l = STRING_length(data[i]); + + if (i) + STRING_make(sep, lsep); + + if (l == 0) + continue; + + STRING_make_char(escl); + for(;;) + { + p2 = index(p, escr); + if (p2) + { + STRING_make(p, p2 - p + 1); + STRING_make_char(escr); + p = p2 + 1; + } + else + { + STRING_make(p, l + data[i] - p); + break; + } + } + STRING_make_char(escr); + } + } + + GB_ReturnString(STRING_end_temp()); + +END_METHOD + + +BEGIN_METHOD_VOID(CARRAY_reverse) + + size_t size; + int count; + char *buffer[16]; + char *pi, *pj; + + count = THIS->count; + if (count > 1) + { + size = THIS->size; + pi = get_data(THIS, 0); + pj = get_data(THIS, count - 1); + + do + { + memcpy(buffer, pi, size); + memcpy(pi, pj, size); + memcpy(pj, buffer, size); + pi += size; + pj -= size; + } + while (pi < pj); + } + + GB_ReturnObject(THIS); + +END_METHOD + + +BEGIN_METHOD(Array_Read, GB_OBJECT stream; GB_INTEGER start; GB_INTEGER length) + + int count = THIS->count; + int start = VARGOPT(start, 0); + int length = VARGOPT(length, count); + void *stream = VARG(stream); + + if (GB_CheckObject(stream)) + return; + + if (check_start_length(count, &start, &length)) + return; + + STREAM_read(CSTREAM_TO_STREAM(stream), get_data(THIS, start), length * THIS->size); + +END_METHOD + + +BEGIN_METHOD(Array_Write, GB_OBJECT stream; GB_INTEGER start; GB_INTEGER length) + + int count = THIS->count; + int start = VARGOPT(start, 0); + int length = VARGOPT(length, count); + void *stream = VARG(stream); + + if (GB_CheckObject(stream)) + return; + + if (check_start_length(count, &start, &length)) + return; + + STREAM_write(CSTREAM_TO_STREAM(stream), get_data(THIS, start), length * THIS->size); + +END_METHOD + + +BEGIN_PROPERTY(Array_Dim) + + GB_ReturnInteger(get_dim(THIS)); + +END_PROPERTY + + +BEGIN_METHOD(Array_Bounds_get, GB_INTEGER index) + + int dim = get_dim(THIS); + int index = VARG(index); + + if (index < 0 || index >= dim) + { + CARRAY_out_of_bounds(); + return; + } + + GB_ReturnInteger(get_bound(THIS, index)); + +END_PROPERTY + +BEGIN_METHOD(Array_Byte_ToString, GB_INTEGER start; GB_INTEGER length) + + int start, length; + int count = THIS->count; + char *p; + + start = VARGOPT(start, 0); + + if (start < 0) + { + CARRAY_out_of_bounds(); + return; + } + + if (start >= count) + { + GB_ReturnVoidString(); + return; + } + + length = VARGOPT(length, -1); + if (length < 0) + { + p = memchr((char *)THIS->data + start, 0, count - start); + if (!p) + length = count - start; + else + length = p - ((char *)THIS->data + start); + } + else + { + if ((start + length) > count) + length = count - start; + } + + GB_ReturnNewString((char *)THIS->data + start, length); + +END_METHOD + + +BEGIN_METHOD(Array_Byte_FromString, GB_STRING string) + + CARRAY *array; + char *string = STRING(string); + int length = LENGTH(string); + + GB_ArrayNew((GB_ARRAY *)POINTER(&array), T_BYTE, length); + memcpy(array->data, string, length); + GB_ReturnObject(array); + +END_METHOD + + +BEGIN_METHOD(ArrayOfStruct_get, GB_INTEGER index) + + void *data = get_data_multi(THIS, ARG(index), GB_NParam()); + + if (data) + GB_ReturnObject(CSTRUCT_create_static(THIS, (CLASS *)THIS->type, data)); + +END_METHOD + +static void array_of_struct_put(CARRAY *_object, void *data, void *object) +{ + int i; + CLASS *class = (CLASS *)THIS->type; + CLASS_DESC *desc; + char *addr; + VALUE temp; + CTYPE ctype; + + for (i = 0; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + if (((CSTRUCT *)object)->ref) + addr = (char *)((CSTATICSTRUCT *)object)->addr + desc->variable.offset; + else + addr = (char *)object + sizeof(CSTRUCT) + desc->variable.offset; + + ctype = desc->variable.ctype; + if (ctype.id == TC_ARRAY || ctype.id == TC_STRUCT) + THROW(E_ILLEGAL); + + VALUE_class_read(desc->variable.class, &temp, (void *)addr, ctype, object); + //BORROW(&temp); + addr = (char *)data + desc->variable.offset; + VALUE_write(&temp, (void *)addr, desc->variable.type); + RELEASE(&temp); + } +} + +BEGIN_METHOD(ArrayOfStruct_put, GB_OBJECT value; GB_INTEGER index) + + void *object = VARG(value); + void *data; + + if (GB_CheckObject(object)) + return; + + data = get_data_multi(THIS, ARG(index), GB_NParam()); + if (!data) + return; + + array_of_struct_put(THIS, data, object); + +END_METHOD + + +static void array_of_struct_first_last(void *_object, void *_param, bool last) +{ + int index = last ? THIS->count - 1 : 0; + + /*if (check_not_multi(THIS)) + return;*/ + + void *data = get_data(THIS, index); + if (!data) return; + + if (READ_PROPERTY) + { + GB_ReturnObject(CSTRUCT_create_static(THIS, (CLASS *)THIS->type, data)); + } + else + { + array_of_struct_put(THIS, data, VPROP(GB_OBJECT)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(ArrayOfStruct_First) + + array_of_struct_first_last(_object, _param, FALSE); + +END_PROPERTY + + +BEGIN_PROPERTY(ArrayOfStruct_Last) + + array_of_struct_first_last(_object, _param, TRUE); + +END_PROPERTY + + +BEGIN_METHOD_VOID(ArrayOfStruct_next) + + int *index = (int *)GB_GetEnum(); + + if (*index >= THIS->count) + GB_StopEnum(); + else + { + GB_ReturnObject(CSTRUCT_create_static(THIS, (CLASS *)THIS->type, get_data(THIS, *index))); + (*index)++; + } + +END_METHOD + +static void error_convert(CARRAY *array) +{ + OBJECT_UNREF(array); +} + +static bool _convert(CARRAY *src, CLASS *class, VALUE *conv) +{ + CARRAY *array; + int i; + void *data; + VALUE temp; + int dim; + + if (!src || !TYPE_is_pure_object((TYPE)class)) + return TRUE; + + CLASS_load(class); // Force creation of array classes + + if (!class->is_array) + return TRUE; + + array = OBJECT_create(class, NULL, NULL, 0); + + if (src->count) + { + ARRAY_add_many_void(&array->data, src->count); + array->count = src->count; + + ON_ERROR_1(error_convert, array) + { + for (i = 0; i < src->count; i++) + { + data = CARRAY_get_data(src, i); + VALUE_read(&temp, data, src->type); + BORROW(&temp); + data = CARRAY_get_data(array, i); + VALUE_write(&temp, data, array->type); + RELEASE(&temp); + } + } + END_ERROR + } + + if (src->n_dim) + { + dim = get_dim(src); + ALLOC(&array->dim, dim * sizeof(int)); + for (i = 0; i < dim; i++) + array->dim[i] = src->dim[i]; + array->n_dim = src->n_dim; + } + + conv->_object.object = array; + return FALSE; +} + +#else + +#include "gbx_c_array.h" + +#define _convert NULL + +#endif /* #ifndef GBX_INFO */ + + +//--------------------------------------------------------------------------- + + +GB_DESC NATIVE_ArrayBounds[] = +{ + GB_DECLARE(".Array.Bounds", sizeof(CARRAY)), GB_NOT_CREATABLE(), + + GB_METHOD("_get", "i", Array_Bounds_get, "(Dimension)i"), + GB_PROPERTY_READ("Count", "i", Array_Dim), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_Array[] = +{ + GB_DECLARE("Array", sizeof(CARRAY)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, Array_free, NULL), + + GB_PROPERTY_READ("Type", "i", Array_Type), + GB_PROPERTY_READ("Count", "i", Array_Count), + GB_PROPERTY_READ("Empty", "b", Array_Empty), + GB_PROPERTY_READ("Max", "i", Array_Max), + GB_PROPERTY_READ("Length", "i", Array_Count), + GB_PROPERTY_READ("Dim", "i", Array_Dim), + GB_PROPERTY_READ("Data", "p", Array_Data), + GB_PROPERTY_SELF("Bounds", ".Array.Bounds"), + + GB_METHOD("Remove", NULL, Array_Remove, "(Index)i[(Length)i]"), + GB_METHOD("Clear", NULL, Array_Clear, NULL), + GB_METHOD("Resize", NULL, Array_Resize, "(Size)i"), + GB_METHOD("Shuffle", NULL, Array_Shuffle, NULL), + + GB_PROPERTY("ReadOnly", "b", Array_ReadOnly), + + GB_INTERFACE("_convert", _convert), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_BooleanArray[] = +{ + GB_DECLARE("Boolean[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)b[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)b"), + GB_METHOD("_put", NULL, Array_Boolean_put, "(Value)b(Index)i."), + GB_METHOD("Find", "i", Array_Boolean_Find, "(Value)b[(Start)i]"), + GB_METHOD("Exist", "b", Array_Boolean_Exist, "(Value)b"), + GB_METHOD("FindSorted", "i", Array_Boolean_Find, "(Value)b[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Boolean_Exist, "(Value)b"), + + GB_METHOD("Pop", "b", Array_Pop, NULL), + GB_METHOD("_get", "b", Array_get, "(Index)i."), + GB_METHOD("_next", "b", Array_next, NULL), + + GB_PROPERTY("First", "b", Array_First), + GB_PROPERTY("Last", "b", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Boolean[]", Array_Insert, "(Array)Boolean[];[(Pos)i]"), + GB_METHOD("Copy", "Boolean[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Boolean[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Boolean[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Boolean[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Boolean[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Boolean[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)b[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_ByteArray[] = +{ + GB_DECLARE("Byte[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)c[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)c"), + GB_METHOD("_put", NULL, Array_Byte_put, "(Value)c(Index)i."), + GB_METHOD("Find", "i", Array_Byte_Find, "(Value)c[(Start)i]"), + GB_METHOD("Exist", "b", Array_Byte_Exist, "(Value)c"), + GB_METHOD("FindSorted", "i", Array_Byte_Find, "(Value)c[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Byte_Exist, "(Value)c"), + + GB_METHOD("Pop", "c", Array_Pop, NULL), + GB_METHOD("_get", "c", Array_get, "(Index)i."), + GB_METHOD("_next", "c", Array_next, NULL), + + GB_PROPERTY("First", "c", Array_First), + GB_PROPERTY("Last", "c", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Byte[]", Array_Insert, "(Array)Byte[];[(Pos)i]"), + GB_METHOD("Copy", "Byte[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Byte[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Byte[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Byte[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Byte[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Byte[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)c[(Start)i(Length)i]"), + + GB_METHOD("ToString", "s", Array_Byte_ToString, "[(Start)i(Length)i]"), + GB_STATIC_METHOD("FromString", "Byte[]", Array_Byte_FromString, "(String)s"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_ShortArray[] = +{ + GB_DECLARE("Short[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)h[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)h"), + GB_METHOD("_put", NULL, Array_Short_put, "(Value)h(Index)i."), + GB_METHOD("Find", "i", Array_Short_Find, "(Value)h[(Start)i]"), + GB_METHOD("Exist", "b", Array_Short_Exist, "(Value)h"), + GB_METHOD("FindSorted", "i", Array_Short_Find, "(Value)h[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Short_Exist, "(Value)h"), + + GB_METHOD("Pop", "h", Array_Pop, NULL), + GB_METHOD("_get", "h", Array_get, "(Index)i."), + GB_METHOD("_next", "h", Array_next, NULL), + + GB_PROPERTY("First", "h", Array_First), + GB_PROPERTY("Last", "h", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Short[]", Array_Insert, "(Array)Short[];[(Pos)i]"), + GB_METHOD("Copy", "Short[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Short[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Short[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Short[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Short[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Short[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)h[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_IntegerArray[] = +{ + GB_DECLARE("Integer[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)i[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)i"), + GB_METHOD("_put", NULL, Array_Integer_put, "(Value)i(Index)i."), + GB_METHOD("Find", "i", Array_Integer_Find, "(Value)i[(Start)i]"), + GB_METHOD("Exist", "b", Array_Integer_Exist, "(Value)i"), + GB_METHOD("FindSorted", "i", Array_Integer_Find, "(Value)i[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Integer_Exist, "(Value)i"), + + GB_METHOD("Pop", "i", Array_Pop, NULL), + GB_METHOD("_get", "i", Array_get, "(Index)i."), + GB_METHOD("_next", "i", Array_next, NULL), + + GB_PROPERTY("First", "i", Array_First), + GB_PROPERTY("Last", "i", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Integer[]", Array_Insert, "(Array)Integer[];[(Pos)i]"), + GB_METHOD("Copy", "Integer[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Integer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Integer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Integer[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Integer[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Integer[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)i[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_LongArray[] = +{ + GB_DECLARE("Long[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Long_Add, "(Value)l[(Index)i]"), + GB_METHOD("Push", NULL, Array_Long_Push, "(Value)l"), + GB_METHOD("_put", NULL, Array_Long_put, "(Value)l(Index)i."), + GB_METHOD("Find", "i", Array_Long_Find, "(Value)l[(Start)i]"), + GB_METHOD("Exist", "b", Array_Long_Exist, "(Value)l"), + GB_METHOD("FindSorted", "i", Array_Long_Find, "(Value)l[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Long_Exist, "(Value)l"), + + GB_METHOD("Pop", "l", Array_Pop, NULL), + GB_METHOD("_get", "l", Array_get, "(Index)i."), + GB_METHOD("_next", "l", Array_next, NULL), + + GB_PROPERTY("First", "l", Array_First), + GB_PROPERTY("Last", "l", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Long[]", Array_Insert, "(Array)Long[];[(Pos)i]"), + + GB_METHOD("Copy", "Long[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Long[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Long[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Long[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Long[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Long[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)l[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + +#ifdef OS_64BITS +#define Array_Pointer_Add Array_Long_Add +#define Array_Pointer_Push Array_Long_Push +#define Array_Pointer_put Array_Long_put +#define Array_Pointer_Find Array_Long_Find +#define Array_Pointer_Exist Array_Long_Exist +#else +#define Array_Pointer_Add Array_Integer_Add +#define Array_Pointer_Push Array_Integer_Push +#define Array_Pointer_put Array_Integer_put +#define Array_Pointer_Find Array_Integer_Find +#define Array_Pointer_Exist Array_Integer_Exist +#endif + +GB_DESC NATIVE_PointerArray[] = +{ + GB_DECLARE("Pointer[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Pointer_Add, "(Value)p[(Index)i]"), + GB_METHOD("Push", NULL, Array_Pointer_Push, "(Value)p"), + GB_METHOD("_put", NULL, Array_Pointer_put, "(Value)p(Index)i."), + GB_METHOD("Find", "i", Array_Pointer_Find, "(Value)p[(Start)i]"), + GB_METHOD("Exist", "b", Array_Pointer_Exist, "(Value)p"), + GB_METHOD("FindSorted", "i", Array_Pointer_Find, "(Value)p[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Pointer_Exist, "(Value)p"), + + GB_METHOD("Pop", "p", Array_Pop, NULL), + GB_METHOD("_get", "p", Array_get, "(Index)i."), + GB_METHOD("_next", "p", Array_next, NULL), + + GB_PROPERTY("First", "p", Array_First), + GB_PROPERTY("Last", "p", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Pointer[]", Array_Insert, "(Array)Pointer[];[(Pos)i]"), + + GB_METHOD("Copy", "Pointer[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Pointer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Pointer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Pointer[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Pointer[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Pointer[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)p[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_StringArray[] = +{ + GB_DECLARE("String[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_String_Add, "(Value)s[(Index)i]"), + GB_METHOD("Push", NULL, Array_String_Push, "(Value)s"), + GB_METHOD("_put", NULL, Array_String_put, "(Value)s(Index)i."), + GB_METHOD("Find", "i", Array_String_Find, "(Value)s[(Mode)i(Start)i]"), + GB_METHOD("Exist", "b", Array_String_Exist, "(Value)s[(Mode)i]"), + GB_METHOD("FindSorted", "i", Array_String_Find, "(Value)s[(Mode)i(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_String_Exist, "(Value)s[(Mode)i]"), + + GB_METHOD("Pop", "s", Array_Pop, NULL), + GB_METHOD("_get", "s", Array_get, "(Index)i."), + GB_METHOD("_next", "s", Array_next, NULL), + + GB_PROPERTY("First", "s", Array_First), + GB_PROPERTY("Last", "s", Array_Last), + + GB_METHOD("Insert", "String[]", Array_Insert, "(Array)String[];[(Pos)i]"), + + GB_METHOD("Join", "s", Array_String_join, "[(Separator)s(Escape)s]"), + + GB_METHOD("Copy", "String[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "String[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "String[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "String[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "String[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "String[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)s[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_FloatArray[] = +{ + GB_DECLARE("Float[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Float_Add, "(Value)f[(Index)i]"), + GB_METHOD("Push", NULL, Array_Float_Push, "(Value)f"), + GB_METHOD("_put", NULL, Array_Float_put, "(Value)f(Index)i."), + GB_METHOD("Find", "i", Array_Float_Find, "(Value)f[(Start)i]"), + GB_METHOD("Exist", "b", Array_Float_Exist, "(Value)f"), + GB_METHOD("FindSorted", "i", Array_Float_Find, "(Value)f[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Float_Exist, "(Value)f"), + + GB_METHOD("Pop", "f", Array_Pop, NULL), + GB_METHOD("_get", "f", Array_get, "(Index)i."), + GB_METHOD("_next", "f", Array_next, NULL), + + GB_PROPERTY("First", "f", Array_First), + GB_PROPERTY("Last", "f", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Float[]", Array_Insert, "(Array)Float[];[(Pos)i]"), + + GB_METHOD("Copy", "Float[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Float[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Float[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Float[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Float[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Float[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)f[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_SingleArray[] = +{ + GB_DECLARE("Single[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Single_Add, "(Value)g[(Index)i]"), + GB_METHOD("Push", NULL, Array_Single_Push, "(Value)g"), + GB_METHOD("_put", NULL, Array_Single_put, "(Value)g(Index)i."), + GB_METHOD("Find", "i", Array_Single_Find, "(Value)g[(Start)i]"), + GB_METHOD("Exist", "b", Array_Single_Exist, "(Value)g"), + GB_METHOD("FindSorted", "i", Array_Single_Find, "(Value)g[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Single_Exist, "(Value)g"), + + GB_METHOD("Pop", "g", Array_Pop, NULL), + GB_METHOD("_get", "g", Array_get, "(Index)i."), + GB_METHOD("_next", "g", Array_next, NULL), + + GB_PROPERTY("First", "g", Array_First), + GB_PROPERTY("Last", "g", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Single[]", Array_Insert, "(Array)Single[];[(Pos)i]"), + + GB_METHOD("Copy", "Single[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Single[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Single[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Single[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Single[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Single[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)g[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_DateArray[] = +{ + GB_DECLARE("Date[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Date_Add, "(Value)d[(Index)i]"), + GB_METHOD("Push", NULL, Array_Date_Push, "(Value)d"), + GB_METHOD("_put", NULL, Array_Date_put, "(Value)d(Index)i."), + GB_METHOD("Find", "i", Array_Date_Find, "(Value)d[(Start)i]"), + GB_METHOD("Exist", "b", Array_Date_Exist, "(Value)d"), + GB_METHOD("FindSorted", "i", Array_Date_Find, "(Value)d[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Date_Exist, "(Value)d"), + + GB_METHOD("Pop", "d", Array_Pop, NULL), + GB_METHOD("_get", "d", Array_get, "(Index)i."), + GB_METHOD("_next", "d", Array_next, NULL), + + GB_PROPERTY("First", "d", Array_First), + GB_PROPERTY("Last", "d", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Date[]", Array_Insert, "(Array)Date[];[(Pos)i]"), + + GB_METHOD("Copy", "Date[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Date[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Date[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Date[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Date[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "Date[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)d[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_ObjectArray[] = +{ + GB_DECLARE("Object[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Object_Add, "(Value)o[(Index)i]"), + GB_METHOD("Push", NULL, Array_Object_Push, "(Value)o"), + GB_METHOD("_put", NULL, Array_Object_put, "(Value)o(Index)i."), + GB_METHOD("Find", "i", Array_Object_Find, "(Value)o[(Start)i]"), + GB_METHOD("FindByRef", "i", Array_Object_FindByRef, "(Value)o[(Start)i]"), + GB_METHOD("Exist", "b", Array_Object_Exist, "(Value)o"), + GB_METHOD("ExistByRef", "b", Array_Object_ExistByRef, "(Value)o"), + GB_METHOD("FindSorted", "i", Array_Object_Find, "(Value)o[(Start)i]"), + GB_METHOD("FindByRefSorted", "i", Array_Object_FindByRef, "(Value)o[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Object_Exist, "(Value)o"), + GB_METHOD("ExistByRefSorted", "b", Array_Object_ExistByRef, "(Value)o"), + + GB_METHOD("Pop", "o", Array_Pop, NULL), + GB_METHOD("_get", "o", Array_get, "(Index)i."), + GB_METHOD("_next", "o", Array_next, NULL), + + GB_PROPERTY("First", "o", Array_First), + GB_PROPERTY("Last", "o", Array_Last), + + GB_METHOD("Insert", "Object[]", Array_Insert, "(Array)Object[];[(Pos)i]"), + + GB_METHOD("Copy", "Object[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Object[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Object[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)o[(Start)i(Length)i]"), + GB_METHOD("Reverse", "Object[]", CARRAY_reverse, NULL), + GB_METHOD("Sort", "Object[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Object[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_VariantArray[] = +{ + GB_DECLARE("Variant[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Variant_Add, "(Value)v[(Index)i]"), + GB_METHOD("Push", NULL, Array_Variant_Push, "(Value)v"), + GB_METHOD("_put", NULL, Array_Variant_put, "(Value)v(Index)i."), + GB_METHOD("Find", "i", Array_Variant_Find, "(Value)v[(Start)i]"), + GB_METHOD("Exist", "b", Array_Variant_Exist, "(Value)v"), + GB_METHOD("FindSorted", "i", Array_Variant_Find, "(Value)v[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Variant_Exist, "(Value)v"), + + GB_METHOD("Pop", "v", Array_Pop, NULL), + GB_METHOD("_get", "v", Array_get, "(Index)i."), + GB_METHOD("_next", "v", Array_next, NULL), + + GB_PROPERTY("First", "v", Array_First), + GB_PROPERTY("Last", "v", Array_Last), + + GB_METHOD("Insert", "Variant[]", Array_Insert, "(Array)Variant[];[(Pos)i]"), + + GB_METHOD("Copy", "Variant[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Variant[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Variant[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)v[(Start)i(Length)i]"), + GB_METHOD("Reverse", "Variant[]", CARRAY_reverse, NULL), + GB_METHOD("Sort", "Variant[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "Variant[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + + GB_END_DECLARE +}; + +// Beware: if this declaration is modified, the ARRAY_TEMPLATE_NDESC constant must be modified accordingly. + +GB_DESC NATIVE_TemplateArray[ARRAY_TEMPLATE_NDESC] = +{ + GB_DECLARE("*[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Object_Add, "(Value)*;[(Index)i]"), + GB_METHOD("Push", NULL, Array_Object_Push, "(Value)*;"), + GB_METHOD("_put", NULL, Array_Object_put, "(Value)*;(Index)i."), + GB_METHOD("Find", "i", Array_Object_Find, "(Value)*;[(Start)i]"), + GB_METHOD("FindByRef", "i", Array_Object_FindByRef, "(Value)*;[(Start)i]"), + GB_METHOD("Exist", "b", Array_Object_Exist, "(Value)*;"), + GB_METHOD("ExistByRef", "b", Array_Object_ExistByRef, "(Value)*;"), + GB_METHOD("FindSorted", "i", Array_Object_Find, "(Value)*;[(Start)i]"), + GB_METHOD("FindByRefSorted", "i", Array_Object_FindByRef, "(Value)*;[(Start)i]"), + GB_METHOD("ExistSorted", "b", Array_Object_Exist, "(Value)*;"), + GB_METHOD("ExistByRefSorted", "b", Array_Object_ExistByRef, "(Value)*;"), + + GB_METHOD("Pop", "*", Array_Pop, NULL), + GB_METHOD("_get", "*", Array_get, "(Index)i."), + GB_METHOD("_next", "*", Array_next, NULL), + + GB_PROPERTY("First", "*", Array_First), + GB_PROPERTY("Last", "*", Array_Last), + + GB_METHOD("Insert", "*", Array_Insert, "(Array)*[];[(Pos)i]"), + + GB_METHOD("Copy", "*[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "*[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "*[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)*;[(Start)i(Length)i]"), + GB_METHOD("Sort", "*[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("SortUsing", "*[]", Array_SortUsing, "(Order)Array;[(Mode)i]"), + GB_METHOD("Reverse", "*[]", CARRAY_reverse, NULL), + + GB_END_DECLARE +}; + +// Beware: if this declaration is modified, the ARRAY_OF_STRUCT_TEMPLATE_NDESC constant must be modified accordingly. + +GB_DESC NATIVE_TemplateArrayOfStruct[ARRAY_OF_STRUCT_TEMPLATE_NDESC] = +{ + GB_DECLARE("$*[]", sizeof(CARRAY)), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + GB_METHOD("_free", NULL, Array_free, NULL), + + GB_PROPERTY_READ("Count", "i", Array_Count), + GB_PROPERTY_READ("Max", "i", Array_Max), + GB_PROPERTY_READ("Length", "i", Array_Count), + GB_PROPERTY_READ("Dim", "i", Array_Dim), + GB_PROPERTY_READ("Data", "p", Array_Data), + GB_PROPERTY_SELF("Bounds", ".Array.Bounds"), + + GB_METHOD("_get", "*", ArrayOfStruct_get, "(Index)i."), + GB_METHOD("_put", NULL, ArrayOfStruct_put, "(Value)*;(Index)i."), + GB_METHOD("_next", "*", ArrayOfStruct_next, NULL), + + GB_PROPERTY("First", "*", ArrayOfStruct_First), + GB_PROPERTY("Last", "*", ArrayOfStruct_Last), + + GB_END_DECLARE +}; + + +#ifndef GBX_INFO + +/* Gambas API */ + +void GB_ArrayNew(GB_ARRAY *array, TYPE type, int size) +{ + int np; + CTYPE ctype; + + if (size > 0) + { + GB_Push(1, GB_T_INTEGER, size); + np = 1; + } + else + { + np = 0; + } + + ctype.id = T_NULL; + *array = OBJECT_create(CARRAY_get_array_class((CLASS *)type, ctype), NULL, NULL, np); +} + +int GB_ArrayCount(GB_ARRAY array) +{ + return ((CARRAY *)array)->count; +} + +void *GB_ArrayAdd(GB_ARRAY array) +{ + return insert((CARRAY *)array, -1); +} + +void *GB_ArrayGet(GB_ARRAY array, int index) +{ + return CARRAY_get_data_unsafe((CARRAY *)array, index); +} + +TYPE GB_ArrayType(GB_ARRAY array) +{ + return ((CARRAY *)array)->type; +} + +void GB_ArrayRemove(GB_ARRAY array, int index) +{ + copy_remove((CARRAY *)array, index, 1, FALSE, TRUE); +} + +void GB_ArraySetReadOnly(GB_ARRAY array) +{ + ((CARRAY *)array)->read_only = TRUE; +} + +#endif diff --git a/main/gbx/gbx_c_array.h b/main/gbx/gbx_c_array.h new file mode 100644 index 00000000..8ea0d0e7 --- /dev/null +++ b/main/gbx/gbx_c_array.h @@ -0,0 +1,134 @@ +/*************************************************************************** + + gbx_c_array.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_ARRAY_H +#define __GBX_C_ARRAY_H + +#ifndef GBX_INFO + +#include "gambas.h" + +#include "gbx_variant.h" +#include "gbx_object.h" +#include "gbx_type.h" +#include "gbx_class.h" + +// Do not forget to modify GB_ARRAY_BASE in gambas.h + +typedef + struct { + OBJECT object; + unsigned size : 24; + unsigned read_only : 1; + unsigned n_dim : 3; + int count; + TYPE type; + void *data; + int *dim; + void *ref; + } + CARRAY; + +#ifndef __GBX_C_ARRAY_C +extern GB_DESC NATIVE_ArrayBounds[]; +extern GB_DESC NATIVE_Array[]; +extern GB_DESC NATIVE_BooleanArray[]; +extern GB_DESC NATIVE_ByteArray[]; +extern GB_DESC NATIVE_ShortArray[]; +extern GB_DESC NATIVE_IntegerArray[]; +extern GB_DESC NATIVE_LongArray[]; +extern GB_DESC NATIVE_PointerArray[]; +extern GB_DESC NATIVE_SingleArray[]; +extern GB_DESC NATIVE_FloatArray[]; +extern GB_DESC NATIVE_StringArray[]; +extern GB_DESC NATIVE_DateArray[]; +extern GB_DESC NATIVE_VariantArray[]; +extern GB_DESC NATIVE_ObjectArray[]; +extern GB_DESC NATIVE_TemplateArray[]; +extern GB_DESC NATIVE_TemplateArrayOfStruct[]; +#else + +#define THIS ((CARRAY *)_object) + +#endif + +void CARRAY_split(CARRAY *_object, const char *str, int lstr, const char *sep, const char *esc, bool no_void, bool keep_esc); +void CARRAY_reverse(void *_object, void *_param); +void CARRAY_get_value(CARRAY *_object, int index, VALUE *value); +#define CARRAY_invert(_array) CARRAY_reverse(_array, NULL) +void *CARRAY_get_data_multi(CARRAY *_object, GB_INTEGER *arg, int nparam); +void CARRAY_static_array(); +void *CARRAY_out_of_bounds(); +CLASS *CARRAY_get_array_class(CLASS *class, CTYPE ctype); +int *CARRAY_get_array_bounds(CARRAY *_object); +void CARRAY_resize(CARRAY *_object, int size); + +CARRAY *CARRAY_create_static(CLASS *class, void *ref, CLASS_ARRAY *desc, void *data); +int CARRAY_get_static_count(CLASS_ARRAY *desc); +size_t CARRAY_get_static_size(CLASS *class, CLASS_ARRAY *desc); +void CARRAY_release_static(CLASS *class, CLASS_ARRAY *desc, void *data); + +#define CARRAY_is_static(_array) ((_array)->ref != NULL) + +#define CARRAY_get_data_unsafe(_array, _index) \ +({ \ + int __index = (_index); \ + CARRAY *__array = (CARRAY *)(_array); \ + (void *)((char *)(__array->data) + __index * __array->size); \ +}) + +#define CARRAY_get_data(_array, _index) \ +({ \ + int __index = (_index); \ + CARRAY *__array = (CARRAY *)(_array); \ + void *__data; \ + if ((__index < 0) || (__index >= __array->count)) \ + __data = CARRAY_out_of_bounds(); \ + else \ + __data = (void *)((char *)(__array->data) + __index * __array->size); \ + __data; \ +}) + +#define CARRAY_get_data_throw(_array, _index) \ +({ \ + int __index = (_index); \ + CARRAY *__array = (CARRAY *)(_array); \ + if ((__index < 0) || (__index >= __array->count)) \ + THROW(E_BOUND); \ + (void *)((char *)(__array->data) + __index * __array->size); \ +}) + +#define CARRAY_check_not_read_only(_object) \ +({ \ + CARRAY *__object = (CARRAY *)(_object); \ + if (__object->read_only) \ + THROW(E_SARRAY); \ +}) + + +#endif // #ifndef __GBX_CLASS_INFO_C + +#define ARRAY_TEMPLATE_NDESC 28 +#define ARRAY_OF_STRUCT_TEMPLATE_NDESC 15 + +#endif diff --git a/main/gbx/gbx_c_class.c b/main/gbx/gbx_c_class.c new file mode 100644 index 00000000..ed983c60 --- /dev/null +++ b/main/gbx/gbx_c_class.c @@ -0,0 +1,1029 @@ +/*************************************************************************** + + gbx_c_class.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_CLASS_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include + +#include "gb_common.h" +#include "gbx_api.h" +#include "gbx_component.h" +#include "gbx_project.h" +#include "gbx_class.h" +#include "gbx_class_desc.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_object.h" +#include "gbx_c_array.h" +#include "gbx_c_observer.h" +#include "gbx_c_class.h" + +static CLASS_DESC_SYMBOL *_current_symbol = NULL; + +static void error(int code, CLASS *class, const char *name) +{ + GB_Error((char *)(intptr_t)code, CLASS_get_name(class), name); +} + +static bool check_null(void *object) +{ + if (!object) + { + GB_Error((char *)E_NULL); + return TRUE; + } + + return FALSE; +} + +//---- Components --------------------------------------------------------- + +BEGIN_METHOD(Components_get, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + COMPONENT *comp; + + comp = COMPONENT_find(name); + GB_ReturnObject(comp); + +END_METHOD + +BEGIN_PROPERTY(Components_Count) + + GB_ReturnInt(COMPONENT_count); + +END_PROPERTY + +BEGIN_METHOD_VOID(Components_next) + + COMPONENT **plib = (COMPONENT **)GB_GetEnum(); + + *plib = COMPONENT_next(*plib); + if (*plib == NULL) + GB_StopEnum(); + else + GB_ReturnObject(*plib); + +END_METHOD + +BEGIN_PROPERTY(Component_Name) + + GB_ReturnConstZeroString(OBJECT(COMPONENT)->name); + +END_PROPERTY + +BEGIN_PROPERTY(Component_Version) + + if (OBJECT(COMPONENT)->user) + GB_ReturnString(ARCHIVE_get_version(OBJECT(COMPONENT)->archive)); + else + GB_ReturnConstZeroString(VERSION); + +END_PROPERTY + +BEGIN_PROPERTY(Component_Library) + + GB_ReturnBoolean(OBJECT(COMPONENT)->user); + +END_PROPERTY + +BEGIN_PROPERTY(Component_Path) + + GB_ReturnString(COMPONENT_path); + +END_PROPERTY + +BEGIN_METHOD(Component_Load, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + COMPONENT *comp; + + comp = COMPONENT_find(name); + if (!comp) + comp = COMPONENT_create(name); + + COMPONENT_load(comp); + + GB_ReturnObject(comp); + +END_METHOD + +BEGIN_METHOD(Component_IsLoaded, GB_STRING name) + + GB_ReturnBoolean(COMPONENT_is_loaded(GB_ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(Component_FindFromPath, GB_STRING path) + + ARCHIVE *arch = NULL; + const char *path = GB_ToZeroString(ARG(path)); + ARCHIVE_find_from_path(&arch, &path); + if (arch) + GB_ReturnNewZeroString(arch->name); + else + GB_ReturnVoidString(); + +END_METHOD + +//---- Classes ------------------------------------------------------------ + +BEGIN_METHOD(Classes_get, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + CLASS *class = NULL; + + if (name != NULL) + class = CLASS_look(name, LENGTH(name)); + + if (class == NULL) + { + GB_Error((char *)E_UCLASS, name); + return; + } + + if (!CLASS_is_loaded(class)) + { + GB_Error("Class is not loaded"); + return; + } + + GB_ReturnObject(class); + +END_METHOD + + +BEGIN_METHOD(Class_Load, GB_STRING name) + + CLASS *class; + + class = CLASS_get(GB_ToZeroString(ARG(name))); + GB_ReturnObject(class); + +END_METHOD + + +BEGIN_METHOD(Class_IsLoaded, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + CLASS *class = NULL; + + if (name != NULL) + class = CLASS_look(name, LENGTH(name)); + + GB_ReturnBoolean(class && CLASS_is_loaded(class)); + +END_METHOD + + +BEGIN_METHOD_VOID(Classes_next) + + CLASS **pcurrent = (CLASS **)GB_GetEnum(); + CLASS *class; + + class = *pcurrent; + + for(;;) + { + if (!class) + class = CLASS_get_list(); + else + class = class->next; + + if (!class) + { + GB_StopEnum(); + break; + } + + if (CLASS_is_loaded(class)) + { + GB_ReturnObject(class); + break; + } + + } + + *pcurrent = class; + +END_METHOD + + +BEGIN_PROPERTY(Classes_count) + + GB_ReturnInt(CLASS_count()); + +END_PROPERTY + + +//---- Class -------------------------------------------------------------- + +BEGIN_PROPERTY(Class_Name) + + GB_ReturnConstZeroString(OBJECT(CLASS)->name); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Count) + + GB_ReturnInt(OBJECT(CLASS)->count); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Hidden) + + GB_ReturnBoolean(*(CLASS_get_name(OBJECT(CLASS))) == '.'); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Native) + + GB_ReturnBoolean(CLASS_is_native(OBJECT(CLASS))); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Component) + + GB_ReturnObject(OBJECT(CLASS)->component); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Parent) + + GB_ReturnObject(OBJECT(CLASS)->parent); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Symbols) + + CLASS *class = OBJECT(CLASS); + GB_ARRAY array; + CLASS_DESC_SYMBOL *cds; + int index = 0; + + GB_ArrayNew(&array, T_STRING, 0); + + for(;;) + { + cds = CLASS_get_next_sorted_symbol(class, &index); + if (!cds) + break; + if (cds->name[0] == '.' && !cds->name[1]) + continue; + + *((char **)GB_ArrayAdd(array)) = STRING_new(cds->name, cds->len); + } + + GB_ReturnObject(array); + +END_PROPERTY + + +BEGIN_METHOD(Class_get, GB_STRING name) + + CLASS *class = OBJECT(CLASS); + CLASS_DESC_SYMBOL *cd = NULL; + const char *name = GB_ToZeroString(ARG(name)); + + if (name && name[0] && (name[0] != '.' || name[1] == 0)) + cd = CLASS_get_symbol(class, name); + + if (!cd) + { + error(E_NSYMBOL, class, name); + return; + } + + _current_symbol = cd; + GB_ReturnObject(class); + +END_METHOD + + +BEGIN_METHOD(Class_Exist, GB_STRING name) + + CLASS *class = OBJECT(CLASS); + const char *name = GB_ToZeroString(ARG(name)); + + GB_ReturnBoolean(name != NULL && CLASS_get_symbol(class, name) != NULL); + +END_METHOD + + +BEGIN_PROPERTY(Class_Instance) + + GB_ReturnObject(OBJECT(CLASS)->instance); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Class_AutoCreate) + + CLASS *class = OBJECT(CLASS); + + if (!class->auto_create) + GB_ReturnNull(); + else + GB_ReturnObject(CLASS_auto_create(class, 0)); + +END_METHOD + + +BEGIN_METHOD(Class_New, GB_OBJECT params) + + CLASS *class = OBJECT(CLASS); + GB_ARRAY params = VARGOPT(params, NULL); + int i, np = 0; + void *object; + + if (params) + np = GB_ArrayCount(params); + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + object = EXEC_create_object(class, np, NULL); + OBJECT_UNREF_KEEP(object); + GB_ReturnObject(object); + +END_METHOD + + +/*BEGIN_PROPERTY(Class_Size) + + GB_ReturnInteger(OBJECT(CLASS)->size); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_DataSize) + + CLASS *class = OBJECT(CLASS); + GB_ReturnInteger(CLASS_sizeof(class) - (class->size - class->off_event)); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_StaticSize) + + GB_ReturnInteger(OBJECT(CLASS)->size_stat); + +END_PROPERTY*/ + + +//---- Symbol ------------------------------------------------------------- + +BEGIN_PROPERTY(Symbol_Name) + + GB_ReturnConstString(_current_symbol->name, _current_symbol->len); + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Kind) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + switch(CLASS_DESC_get_type(cds->desc)) + { + case CD_VARIABLE: + case CD_STATIC_VARIABLE: + GB_ReturnInt(1); + return; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + case CD_PROPERTY_WRITE: + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + case CD_STATIC_PROPERTY_WRITE: + GB_ReturnInt(2); + return; + + case CD_METHOD: + case CD_STATIC_METHOD: + GB_ReturnInt(3); + return; + + case CD_EVENT: + GB_ReturnInt(4); + return; + + case CD_CONSTANT: + GB_ReturnInt(5); + return; + + default: + GB_ReturnInt(0); + return; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Static) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + GB_ReturnBoolean(index(CD_STATIC_LIST, CLASS_DESC_get_type(cds->desc)) != NULL); + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Hidden) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + GB_ReturnBoolean(*cds->name == '_'); + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_ReadOnly) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + switch (CLASS_DESC_get_type(cds->desc)) + { + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + GB_ReturnBoolean(FALSE); + break; + + default: + GB_ReturnBoolean(TRUE); + break; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Type) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + GB_ReturnConstZeroString(TYPE_to_string(cds->desc->property.type)); /* Valable pour tout symbole */ + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Signature) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + char *sign = CLASS_DESC_get_signature(cds->desc); + + if (sign) + { + STRING_free_later(sign); + GB_ReturnString(sign); + } + else + GB_ReturnVoidString(); + +END_METHOD + + +BEGIN_PROPERTY(Symbol_Value) + + CLASS_DESC *desc = _current_symbol->desc; + + if (CLASS_DESC_get_type(desc) != CD_CONSTANT) + { + GB_ReturnVariant(NULL); + return; + } + + if (desc->constant.type == T_STRING) + GB_ReturnConstZeroString(desc->constant.value._string); + else + GB_ReturnPtr(desc->constant.type, (void *)&desc->constant.value); + + GB_ReturnConvVariant(); + +END_PROPERTY + + +//---- Object ------------------------------------------------------------- + +BEGIN_METHOD(Object_GetProperty, GB_OBJECT object; GB_STRING property) + + const char *name; + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + name = GB_ToZeroString(ARG(property)); + + GB_GetProperty(object, name); + GB_ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Object_SetProperty, GB_OBJECT object; GB_STRING property; GB_VARIANT value) + + const char *name; + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + name = GB_ToZeroString(ARG(property)); + + GB_SetProperty(object, name, (GB_VALUE *)ARG(value)); + +END_METHOD + + +BEGIN_METHOD(Object_Attach, GB_OBJECT object; GB_OBJECT parent; GB_STRING name) + + void *object = VARG(object); + void *parent = VARG(parent); + char *name = GB_ToZeroString(ARG(name)); + + if (GB_CheckObject(object)) + return; + + if (!parent || !name || !*name) + { + OBJECT_detach(object); + return; + } + + if (GB_CheckObject(parent)) + return; + + OBJECT_attach(object, parent, name); + + /*if (OBJECT_is(object, CLASS_Observer)) + COBSERVER_attach((COBSERVER *)object, parent, GB_ToZeroString(ARG(name)));*/ + +END_METHOD + + +BEGIN_METHOD(Object_Detach, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + if (OBJECT_is(object, CLASS_Observer)) + COBSERVER_detach((COBSERVER *)object); + + OBJECT_detach(object); + +END_METHOD + + +BEGIN_METHOD(Object_Parent, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + GB_ReturnObject(OBJECT_parent(object)); + +END_METHOD + + +BEGIN_METHOD(Object_Class, GB_OBJECT object) + + void *object = VARG(object); + + if (check_null(object)) + return; + + GB_ReturnObject(OBJECT_class(object)); + +END_METHOD + + +BEGIN_METHOD(Object_Type, GB_OBJECT object) + + void *object = VARG(object); + + if (check_null(object)) + return; + + GB_ReturnConstZeroString(OBJECT_class(object)->name); + +END_METHOD + + +BEGIN_METHOD(Object_Is, GB_OBJECT object; GB_STRING class) + + void *object = VARG(object); + CLASS *class = CLASS_look(STRING(class), LENGTH(class)); + + if (check_null(object)) + return; + + if (!class) + { + GB_ReturnBoolean(FALSE); + return; + } + + GB_ReturnBoolean(OBJECT_class(object) == class || CLASS_inherits(OBJECT_class(object), class)); + +END_METHOD + + +BEGIN_METHOD(Object_Call, GB_OBJECT object; GB_STRING method; GB_OBJECT params) + + int i; + int np = 0; + char *name = GB_ToZeroString(ARG(method)); + void *object = VARG(object); + GB_FUNCTION func; + GB_ARRAY params = VARGOPT(params, NULL); + + if (GB_CheckObject(object)) + return; + + if (GB_GetFunction(&func, object, name, NULL, NULL)) + { + error(E_NSYMBOL, OBJECT_class(object), name); + return; + } + + if (params) + np = GB_ArrayCount(params); + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + GB_Call(&func, np, FALSE); + + if (TEMP.type != T_VOID) + GB_ReturnConvVariant(); + +END_METHOD + +BEGIN_METHOD(Object_Raise, GB_OBJECT object; GB_STRING event; GB_OBJECT params) + + CLASS *class; + CLASS_DESC *desc; + int index; + int i, np; + void *object = VARG(object); + GB_ARRAY params = VARGOPT(params, NULL); + int len = LENGTH(event); + char event[len + 2]; + + if (GB_CheckObject(object)) + return; + + class = OBJECT_class(object); + + event[0] = ':'; + memcpy(&event[1], STRING(event), len); + event[len + 1] = 0; + + index = CLASS_find_symbol(class, event); + if (index == NO_SYMBOL) + goto __UNKNOWN_EVENT; + + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_EVENT) + goto __UNKNOWN_EVENT; + + if (params) + np = GB_ArrayCount(params); + else + np = 0; + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + GB_ReturnBoolean(GB_Raise(object, desc->event.index, -np)); + return; + +__UNKNOWN_EVENT: + + GB_Error("Unknown event"); + +END_METHOD + + +BEGIN_METHOD(Object_IsValid, GB_OBJECT object) + + GB_ReturnBoolean(OBJECT_is_valid(VARG(object))); + +END_METHOD + + +BEGIN_METHOD(Object_Lock, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + OBJECT_lock(object, TRUE); + +END_METHOD + + +BEGIN_METHOD(Object_Unlock, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + OBJECT_lock(object, FALSE); + +END_METHOD + + +BEGIN_METHOD(Object_IsLocked, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + GB_ReturnBoolean(OBJECT_is_locked(object)); + +END_METHOD + + +BEGIN_METHOD(Object_Count, GB_OBJECT object) + + void *object = VARG(object); + + if (check_null(object)) + return; + + // We substract one because the object is referenced on the stack + GB_ReturnInteger(OBJECT_count(object) - 1); + +END_METHOD + + +BEGIN_METHOD(Object_SizeOf, GB_OBJECT object) + + CLASS *class; + void *object = VARG(object); + + if (check_null(object)) + return; + + class = OBJECT_class(object); + if (CLASS_is_array(class) && CARRAY_is_static((CARRAY *)object)) + { + CARRAY *array = (CARRAY *)object; + GB_ReturnInteger(array->count * array->size); + } + else + GB_ReturnInteger(CLASS_sizeof(class)); + +END_METHOD + + +BEGIN_METHOD(Object_New, GB_STRING class; GB_OBJECT params) + + const char *name = GB_ToZeroString(ARG(class)); + CLASS *class = CLASS_find(name); + GB_ARRAY params = VARGOPT(params, NULL); + int i, np = 0; + void *object; + + if (!class) + { + GB_Error((char *)E_UCLASS, name); + return; + } + + if (params) + np = GB_ArrayCount(params); + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + object = EXEC_create_object(class, np, NULL); + OBJECT_UNREF_KEEP(object); + GB_ReturnObject(object); + +END_METHOD + + +BEGIN_PROPERTY(Object_Address) + + GB_ReturnPointer(VPROP(GB_OBJECT)); + +END_PROPERTY + + +BEGIN_METHOD(Object_CanRaise, GB_OBJECT object; GB_STRING name) + + void *object = VARG(object); + CLASS *class; + char *name; + int index; + + if (!object) + { + GB_Error((char *)E_NULL); + return; + } + + class = OBJECT_class(object); + name = GB_ToZeroString(ARG(name)); + index = GB_GetEvent(class, name); + //fprintf(stderr, "Object.CanRaise: %s %d\n", name, index); + if (index < 0) + GB_ReturnBoolean(FALSE); + else + GB_ReturnBoolean(GB_CanRaise(object, index)); + +END_METHOD + +#endif + + +//------------------------------------------------------------------------- + +GB_DESC NATIVE_Symbol[] = +{ + GB_DECLARE_VIRTUAL(".Symbol"), + + GB_PROPERTY_READ("Name", "s", Symbol_Name), + GB_PROPERTY_READ("Kind", "i", Symbol_Kind), + GB_PROPERTY_READ("Type", "s", Symbol_Type), + GB_PROPERTY_READ("ReadOnly", "b", Symbol_ReadOnly), + GB_PROPERTY_READ("Hidden", "b", Symbol_Hidden), + GB_PROPERTY_READ("Signature", "s", Symbol_Signature), + GB_PROPERTY_READ("Static", "b", Symbol_Static), + GB_PROPERTY_READ("Value", "v", Symbol_Value), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Components[] = +{ + GB_DECLARE_STATIC("Components"), + + GB_STATIC_METHOD("_next", "Component", Components_next, NULL), + GB_STATIC_METHOD("_get", "Component", Components_get, "(Name)s"), + GB_STATIC_PROPERTY_READ("Count", "i", Components_Count), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Component[] = +{ + GB_DECLARE("Component", sizeof(COMPONENT)), + GB_NOT_CREATABLE(), + + //GB_STATIC_METHOD("_get", "Component", library_get, "(Name)s"), + GB_STATIC_METHOD("Load", "Component", Component_Load, "(Name)s"), + GB_STATIC_METHOD("IsLoaded", "b", Component_IsLoaded, "(Name)s"), + GB_STATIC_METHOD("FindFromPath", "s", Component_FindFromPath, "(Path)s"), + GB_STATIC_PROPERTY_READ("Path", "s", Component_Path), + + GB_PROPERTY_READ("Name", "s", Component_Name), + GB_PROPERTY_READ("Version", "s", Component_Version), + GB_PROPERTY_READ("Library", "b", Component_Library), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Classes[] = +{ + GB_DECLARE_STATIC("Classes"), + + GB_STATIC_METHOD("_next", "Class", Classes_next, NULL), + GB_STATIC_METHOD("_get", "Class", Classes_get, "(Name)s"), + GB_STATIC_PROPERTY_READ("Count", "i", Classes_count), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Class[] = +{ + GB_DECLARE("Class", sizeof(CLASS)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("Load", "Class", Class_Load, "(Name)s"), + GB_STATIC_METHOD("IsLoaded", "b", Class_IsLoaded, "(Name)s"), + + GB_METHOD("_get", ".Symbol", Class_get, "(Symbol)s"), + + GB_PROPERTY_READ("Name", "s", Class_Name), + GB_PROPERTY_READ("Hidden", "b", Class_Hidden), + GB_PROPERTY_READ("Native", "b", Class_Native), + GB_PROPERTY_READ("Parent", "Class", Class_Parent), + GB_PROPERTY_READ("Component", "Component", Class_Component), + GB_PROPERTY_READ("Count", "i", Class_Count), + GB_PROPERTY_READ("Instance", "o", Class_Instance), + GB_PROPERTY_READ("Symbols", "String[]", Class_Symbols), + GB_METHOD("AutoCreate", "o", Class_AutoCreate, NULL), + GB_METHOD("New", "o", Class_New, "[(Arguments)Array;]"), + GB_METHOD("Exist", "b", Class_Exist, "(Symbol)s"), + /*GB_PROPERTY_READ("Size", "i", Class_Size), + GB_PROPERTY_READ("DataSize", "i", Class_DataSize), + GB_PROPERTY_READ("StaticSize", "i", Class_StaticSize),*/ + + GB_CONSTANT("Variable", "i", 1), + GB_CONSTANT("Property", "i", 2), + GB_CONSTANT("Method", "i", 3), + GB_CONSTANT("Event", "i", 4), + GB_CONSTANT("Constant", "i", 5), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Object[] = +{ + GB_DECLARE_STATIC("Object"), + + GB_STATIC_METHOD("GetProperty", "v", Object_GetProperty, "(Object)o(Property)s"), + GB_STATIC_METHOD("SetProperty", NULL, Object_SetProperty, "(Object)o(Property)s(Value)v"), + GB_STATIC_METHOD("Attach", NULL, Object_Attach, "(Object)o(Parent)o(Name)s"), + GB_STATIC_METHOD("Detach", NULL, Object_Detach, "(Object)o"), + GB_STATIC_METHOD("Class", "Class", Object_Class, "(Object)o"), + GB_STATIC_METHOD("Type", "s", Object_Type, "(Object)o"), + GB_STATIC_METHOD("Is", "b", Object_Is, "(Object)o(Class)s"), + GB_STATIC_METHOD("Parent", "o", Object_Parent, "(Object)o"), + GB_STATIC_METHOD("Call", "v", Object_Call, "(Object)o(Method)s[(Arguments)Array;]"), + GB_STATIC_METHOD("New", "o", Object_New, "(Class)s[(Arguments)Array;]"), + GB_STATIC_METHOD("IsValid", "b", Object_IsValid, "(Object)o"), + GB_STATIC_METHOD("Lock", NULL, Object_Lock, "(Object)o"), + GB_STATIC_METHOD("Unlock", NULL, Object_Unlock, "(Object)o"), + GB_STATIC_METHOD("IsLocked", "b", Object_IsLocked, "(Object)o"), + GB_STATIC_METHOD("Count", "i", Object_Count, "(Object)o"), + GB_STATIC_METHOD("SizeOf", "i", Object_SizeOf, "(Object)o"), + GB_STATIC_METHOD("Address", "p", Object_Address, "(Object)o"), + GB_STATIC_METHOD("CanRaise", "b", Object_CanRaise, "(Object)o(Event)s"), + GB_STATIC_METHOD("Raise", "b", Object_Raise, "(Object)o(Event)s[(Arguments)Array;]"), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_class.h b/main/gbx/gbx_c_class.h new file mode 100644 index 00000000..6194ef56 --- /dev/null +++ b/main/gbx/gbx_c_class.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + gbx_c_class.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_CLASS_H +#define __GBX_C_CLASS_H + +#include "gambas.h" + +#ifndef __GBX_C_CLASS_C +extern GB_DESC NATIVE_Component[]; +extern GB_DESC NATIVE_Components[]; +extern GB_DESC NATIVE_Class[]; +extern GB_DESC NATIVE_Classes[]; +extern GB_DESC NATIVE_Object[]; +extern GB_DESC NATIVE_Symbol[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_collection.c b/main/gbx/gbx_c_collection.c new file mode 100644 index 00000000..284dde1e --- /dev/null +++ b/main/gbx/gbx_c_collection.c @@ -0,0 +1,459 @@ +/*************************************************************************** + + gbx_c_collection.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_COLLECTION_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gb_error.h" + +#include "gbx_variant.h" +#include "gambas.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_object.h" + +#include "gbx_c_collection.h" + + +static void clear(CCOLLECTION *col) +{ + VARIANT *value; + HASH_ENUM iter; + + CLEAR(&iter); + col->locked = TRUE; + + for(;;) + { + value = HASH_TABLE_next(col->hash_table, &iter, FALSE); + if (value == NULL) + break; + + VARIANT_free(value); + value->type = T_NULL; + } + + HASH_TABLE_delete(&col->hash_table); + col->locked = FALSE; +} + +#define get_key(_col, _key, _len, _set_last) HASH_TABLE_lookup((_col)->hash_table, (_key), (_len), _set_last) + +#define add_key(_col, _key, _len) ((_len) == 0 ? (GB_Error((char *)E_VKEY), NULL) : HASH_TABLE_insert(((CCOLLECTION *)(_col))->hash_table, (_key), (_len))) + +static void remove_key(CCOLLECTION *col, const char *key, int len) +{ + void *value; + HASH_NODE *last; + HASH_NODE *save; + void *save_enum; + + if (len == 0) + { + GB_Error((char *)E_VKEY); + return; + } + + save = col->hash_table->last; + + value = HASH_TABLE_lookup(col->hash_table, key, len, TRUE); + + last = col->hash_table->last; + col->hash_table->last = save; + + if (value == NULL) + return; + + if (last) + { + save_enum = GB_BeginEnum(col); + while (!GB_NextEnum()) + { + HASH_ENUM *iter = (HASH_ENUM *)GB_GetEnum(); + if (iter->next == last) + iter->next = iter->next->snext; + } + GB_EndEnum(save_enum); + } + + VARIANT_free((VARIANT *)value); + + if (!col->locked) + HASH_TABLE_remove(col->hash_table, key, len); + else // Prevent the freed variant to be freed twice if the collection is locked (i.e. being destroyed) + ((VARIANT *)value)->type = T_NULL; +} + + +BEGIN_METHOD(Collection_new, GB_INTEGER mode) + + int mode = MISSING(mode) ? 0 : VARG(mode); + + HASH_TABLE_create(&THIS->hash_table, TYPE_sizeof(T_VARIANT), mode); + THIS->last = NULL; + THIS->default_value.type = GB_T_NULL; + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_free) + + clear(THIS); + GB_StoreVariant(NULL, POINTER(&THIS->default_value)); + +END_METHOD + + +BEGIN_PROPERTY(Collection_Count) + + GB_ReturnInt(CCOLLECTION_get_count(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Collection_Empty) + + GB_ReturnBoolean(CCOLLECTION_get_count(THIS) == 0); + +END_PROPERTY + + +BEGIN_PROPERTY(Collection_Key) + + char *key; + int len; + + if (READ_PROPERTY) + { + if (HASH_TABLE_get_last_key(THIS->hash_table, &key, &len)) + GB_ReturnVoidString(); + else + GB_ReturnNewString(key, len); + } + else + HASH_TABLE_set_last_key(THIS->hash_table, PSTRING(), PLENGTH()); + +END_PROPERTY + + +BEGIN_METHOD(Collection_Add, GB_VARIANT value; GB_STRING key) + + void *data; + + data = add_key(THIS, STRING(key), LENGTH(key)); + if (!data) + return; + + GB_StoreVariant(ARG(value), data); + +END_METHOD + + +BEGIN_METHOD(Collection_Exist, GB_STRING key) + + GB_ReturnBoolean(get_key(THIS, STRING(key), LENGTH(key), FALSE) != NULL); + +END_METHOD + + +BEGIN_METHOD(Collection_Remove, GB_STRING key) + + remove_key(THIS, STRING(key), LENGTH(key)); + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_Clear) + + int mode = THIS->hash_table->mode; + + /* Stops all iterators */ + GB_StopAllEnum(THIS); + + clear(THIS); + HASH_TABLE_create(&THIS->hash_table, TYPE_sizeof(T_VARIANT), mode); + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_next) + + void *value; + HASH_TABLE *hash_table = OBJECT(CCOLLECTION)->hash_table; + HASH_ENUM *iter = (HASH_ENUM *)GB_GetEnum(); + + value = HASH_TABLE_next(hash_table, iter, TRUE); + + if (value == NULL) + GB_StopEnum(); + else + GB_ReturnVariant(value); + +END_METHOD + + +BEGIN_METHOD(Collection_get, GB_STRING key) + + void *value = get_key(THIS, STRING(key), LENGTH(key), TRUE); + if (!value) + { + if (THIS->has_default) + value = (void *)&THIS->default_value; + } + + GB_ReturnVariant(value); + +END_METHOD + + +BEGIN_METHOD(Collection_put, GB_VARIANT value; GB_STRING key) + + void *data; + + if (VARIANT_is_null((VARIANT *)&VARG(value))) + remove_key(THIS, STRING(key), LENGTH(key)); + else + { + data = add_key(THIS, STRING(key), LENGTH(key)); + if (!data) + return; + GB_StoreVariant(ARG(value), data); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_Copy) + + GB_COLLECTION col; + GB_COLLECTION_ITER iter;; + GB_VARIANT value; + char *key; + int len; + + GB_CollectionNew(&col, THIS->mode); + + CLEAR(&iter); + + for(;;) + { + if (GB_CollectionEnum(THIS, &iter, &value, &key, &len)) + break; + + GB_CollectionSet(col, key, len, &value); + } + + GB_ReturnObject(col); + +END_METHOD + + +BEGIN_PROPERTY(Collection_Keys) + + GB_ARRAY keys; + GB_COLLECTION_ITER iter;; + char *key; + int len; + int i; + + GB_ArrayNew(&keys, GB_T_STRING, CCOLLECTION_get_count(THIS)); + + CLEAR(&iter); + + for(i = 0;; i++) + { + if (GB_CollectionEnum(THIS, &iter, NULL, &key, &len)) + break; + + *((char **)GB_ArrayGet(keys, i)) = STRING_new(key, len); + } + + GB_ReturnObject(keys); + +END_METHOD + + +BEGIN_PROPERTY(Collection_Default) + + if (READ_PROPERTY) + GB_ReturnVariant(&THIS->default_value); + else + { + GB_StoreVariant(PROP(GB_VARIANT), POINTER(&THIS->default_value)); + THIS->has_default = THIS->default_value.type != GB_T_NULL; + } + +END_PROPERTY + +static void return_node_key(HASH_TABLE *hash_table, HASH_NODE *node) +{ + char *key; + int len; + + HASH_TABLE_get_key(hash_table, node, &key, &len); + if (len) + GB_ReturnNewString(key, len); + else + GB_ReturnNull(); + +} + +BEGIN_PROPERTY(Collection_First) + + return_node_key(THIS->hash_table, THIS->hash_table->sfirst); + +END_PROPERTY + +BEGIN_PROPERTY(Collection_Last) + + return_node_key(THIS->hash_table, THIS->hash_table->slast); + +END_PROPERTY + +#endif + + +GB_DESC NATIVE_Collection[] = +{ + GB_DECLARE("Collection", sizeof(CCOLLECTION)), + + GB_METHOD("_new", NULL, Collection_new, "[(Mode)i]"), + GB_METHOD("_free", NULL, Collection_free, NULL), + + GB_PROPERTY_READ("Count", "i", Collection_Count), + GB_PROPERTY_READ("Empty", "b", Collection_Empty), + GB_PROPERTY_READ("Length", "i", Collection_Count), + GB_PROPERTY_READ("First", "s", Collection_First), + GB_PROPERTY_READ("Last", "s", Collection_Last), + GB_PROPERTY("Key", "s", Collection_Key), + GB_PROPERTY("Default", "v", Collection_Default), + GB_PROPERTY_READ("Keys", "String[]", Collection_Keys), + + GB_METHOD("Add", NULL, Collection_Add, "(Value)v(Key)s"), + GB_METHOD("Exist", "b", Collection_Exist, "(Key)s"), + GB_METHOD("Remove", NULL, Collection_Remove, "(Key)s"), + GB_METHOD("Clear", NULL, Collection_Clear, NULL), + /*GB_METHOD("_first", NULL, collection_first, NULL),*/ + GB_METHOD("_next", "v", Collection_next, NULL), + GB_METHOD("_get", "v", Collection_get, "(Key)s"), + GB_METHOD("_put", NULL, Collection_put, "(Value)v(Key)s"), + GB_METHOD("Copy", "Collection", Collection_Copy, NULL), + + GB_END_DECLARE +}; + +#ifndef GBX_INFO + +void GB_CollectionNew(GB_COLLECTION *col, int mode) +{ + VALUE param; + + param._integer.type = GB_T_INTEGER; + param._integer.value = mode; + + *col = OBJECT_create_native(CLASS_Collection, ¶m); +} + +int GB_CollectionCount(GB_COLLECTION col) +{ + return HASH_TABLE_size(((CCOLLECTION *)col)->hash_table); +} + +bool GB_CollectionSet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value) +{ + VARIANT *data; + + if (VARIANT_is_null((VARIANT *)&value->value)) + remove_key((CCOLLECTION *)col, key, len); + else + { + data = (VARIANT *)add_key((CCOLLECTION *)col, key, len); + if (!data) + return TRUE; + VALUE_write_variant((VALUE *)value, data); + } + return FALSE; +} + +bool GB_CollectionGet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value) +{ + CCOLLECTION *_object = (CCOLLECTION *)col; + VARIANT *var; + bool ret; + + value->type = T_VARIANT; + + var = (VARIANT *)get_key(THIS, key, len, TRUE); + + if (!var) + { + if (!THIS->has_default) + { + value->value.type = GB_T_NULL; + return TRUE; + } + + var = (VARIANT *)&THIS->default_value; + ret = TRUE; + } + else + { + ret = FALSE; + } + + value->value.type = var->type; + value->value.value.data = var->value.data; + return ret; +} + +bool GB_CollectionEnum(GB_COLLECTION col, GB_COLLECTION_ITER *iter, GB_VARIANT *value, char **key, int *len) +{ + VARIANT *val; + HASH_TABLE *hash_table = ((CCOLLECTION *)col)->hash_table; + + if (!key) + { + CLEAR(iter); + return FALSE; + } + + val = HASH_TABLE_next(hash_table, (HASH_ENUM *)iter, TRUE); + if (!val) + return TRUE; + + if (value) + { + value->type = T_VARIANT; + value->value.type = val->type; + value->value.value.data = val->value.data; + } + + HASH_TABLE_get_last_key(hash_table, key, len); + return FALSE; +} + +#endif diff --git a/main/gbx/gbx_c_collection.h b/main/gbx/gbx_c_collection.h new file mode 100644 index 00000000..83e63e8a --- /dev/null +++ b/main/gbx/gbx_c_collection.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + gbx_c_collection.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_COLLECTION_H +#define __GBX_C_COLLECTION_H + +#include "gambas.h" + +#include "gb_hash.h" +#include "gbx_object.h" + +typedef + struct { + int sort; + void *data; + } + CCOL_DESC; + +typedef + struct { + OBJECT object; + HASH_TABLE *hash_table; + HASH_NODE *last; + GB_VARIANT_VALUE default_value; + short mode; + unsigned locked : 1; + unsigned has_default : 1; + } + CCOLLECTION; + +#ifndef __GBX_C_COLLECTION_C + +extern GB_DESC NATIVE_Collection[]; + +#else + +#define THIS ((CCOLLECTION *)_object) + +#endif + +#define CCOLLECTION_get_count(_col) HASH_TABLE_size((_col)->hash_table) + +/*PUBLIC void *CCOLLECTION_new(TYPE type);*/ +/* +void *CCOLLECTION_get_key(CCOLLECTION *col, char *key, int len); +void *CCOLLECTION_add_key(CCOLLECTION *col, char *key, int len); +void CCOLLECTION_remove_key(CCOLLECTION *col, char *key, int len); +*/ + +#endif diff --git a/main/gbx/gbx_c_enum.c b/main/gbx/gbx_c_enum.c new file mode 100644 index 00000000..54f244ab --- /dev/null +++ b/main/gbx/gbx_c_enum.c @@ -0,0 +1,210 @@ +/*************************************************************************** + + gbx_c_enum.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_ENUM_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gbx_class.h" + +#include "gambas.h" +#include "gbx_api.h" + +#include "gbx_exec.h" +#include "gbx_object.h" +#include "gbx_c_enum.h" + +//#define DEBUG_ME + +static CENUM *_enum_list = NULL; + + +CENUM *CENUM_create(void *enum_object) +{ + CENUM *_object; + + _object = OBJECT_new(CLASS_Enum, NULL, NULL); + OBJECT_UNREF_KEEP(_object); + + THIS->enum_object = enum_object; + OBJECT_REF(enum_object); + + LIST_insert(&_enum_list, THIS, &THIS->list); + + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_create: %p <%p>\n", THIS, enum_object); + #endif + + return THIS; +} + +CENUM *CENUM_get_next(CENUM *cenum) +{ + if (!cenum) + return _enum_list; + else + return cenum->list.next; +} + + +BEGIN_METHOD_VOID(CENUM_free) + + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_free: %p <%p>\n", THIS, THIS->enum_object); + #endif + + if (THIS->free) + (*THIS->free)(&THIS->data); + + LIST_remove(&_enum_list, THIS, &THIS->list); + OBJECT_UNREF(THIS->enum_object); + + if (THIS->variant) + GB_StoreVariant(NULL, THIS->data); + +END_METHOD + +static bool check_enum() +{ + if (!EXEC_enum) + { + GB_Error((char *)E_ENUM); //"No current enumeration"); + return TRUE; + } + else + return FALSE; +} + +BEGIN_METHOD_VOID(CENUM_stop) + + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_stop: %p <%p>\n", EXEC_enum, EXEC_enum->enum_object); + #endif + + if (check_enum()) + return; + + EXEC_enum->stop = TRUE; + +END_METHOD + + +BEGIN_PROPERTY(CENUM_index) + + if (check_enum()) + return; + + _object = EXEC_enum; + + if (READ_PROPERTY) + { + if (!THIS->variant) + { + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_index: %p <%p>: -> NULL\n", THIS, THIS->enum_object); + #endif + GB_ReturnVariant(NULL); + } + else + { + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_index: %p <%p>: -> value (%ld)\n", THIS, THIS->enum_object, ((VARIANT *)THIS->data)->type); + #endif + GB_ReturnVariant((GB_VARIANT_VALUE *)THIS->data); + } + } + else + { + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_index: %p <%p>: set: variant = %d\n", THIS, THIS->enum_object, THIS->variant); + #endif + if (!THIS->variant) + ((VARIANT *)THIS->data)->type = T_NULL; + + GB_StoreVariant(PROP(GB_VARIANT), THIS->data); + THIS->variant = TRUE; + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(CENUM_next) + + void *enum_object = OP ? (void *)OP : (void *)CP; + CENUM **pcenum = (CENUM **)GB_GetEnum(); + CENUM *cenum; + + if (*pcenum == NULL) + cenum = _enum_list; + else + cenum = *pcenum; + + for(;;) + { + if (!cenum) + { + GB_StopEnum(); + return; + } + + if (cenum->enum_object == enum_object) + break; + + cenum = cenum->list.next; + } + + *pcenum = cenum->list.next; + EXEC_enum = cenum; + +END_METHOD + +BEGIN_PROPERTY(Enum_Stopped) + + if (check_enum()) + return; + + GB_ReturnBoolean(EXEC_enum->stop); + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Enum[] = +{ + GB_DECLARE("Enum", sizeof(CENUM)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, CENUM_free, NULL), + + GB_STATIC_PROPERTY("Index", "v", CENUM_index), + GB_STATIC_PROPERTY_READ("Stopped", "b", Enum_Stopped), + GB_STATIC_METHOD("Stop", NULL, CENUM_stop, NULL), + + GB_STATIC_METHOD("_next", NULL, CENUM_next, NULL), + + GB_END_DECLARE +}; + + diff --git a/main/gbx/gbx_c_enum.h b/main/gbx/gbx_c_enum.h new file mode 100644 index 00000000..3ab18e5b --- /dev/null +++ b/main/gbx/gbx_c_enum.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + gbx_c_enum.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_ENUM_H +#define __GBX_C_ENUM_H + +#include "gambas.h" +#include "gb_list.h" +#include "gbx_variant.h" + +typedef + struct CENUM { + GB_BASE object; + LIST list; + void *enum_object; + void *data[3]; + void (*free)(void *); + unsigned stop : 1; + unsigned variant : 1; + } + CENUM; + +#ifndef __GBX_C_ENUM_C +extern GB_DESC NATIVE_Enum[]; +#else + +#define THIS ((CENUM *)_object) + +#endif + +CENUM *CENUM_create(void *object); +CENUM *CENUM_get_next(CENUM *); + +#endif diff --git a/main/gbx/gbx_c_error.c b/main/gbx/gbx_c_error.c new file mode 100644 index 00000000..8506206a --- /dev/null +++ b/main/gbx/gbx_c_error.c @@ -0,0 +1,169 @@ +/*************************************************************************** + + gbx_c_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_ERROR_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gbx_class.h" +#include "gbx_debug.h" + +#include "gambas.h" +#include "gbx_api.h" + +#include "gbx_object.h" +#include "gbx_string.h" +#include "gbx_split.h" +#include "gbx_stack.h" +#include "gbx_exec.h" +#include "gbx_c_array.h" + +#include "gbx_c_error.h" + + +BEGIN_PROPERTY(Error_Code) + + GB_ReturnInt(ERROR_last.code); + +END_PROPERTY + +static char **_arg; + +static void get_subst(int np, char **str, int *len) +{ + *str = _arg[np]; + *len = STRING_length(_arg[np]); +} + + +BEGIN_PROPERTY(Error_Text) + + if (ERROR_last.code && ERROR_last.msg) + { + if (EXEC_debug) + { + GB_ARRAY array; + char *result; + + array = STRING_split(ERROR_last.msg, strlen(ERROR_last.msg), "|", 1, NULL, 0, FALSE, FALSE); + _arg = (char **)GB_ArrayGet(array, 0); + + result = STRING_subst(_arg[0], STRING_length(_arg[0]), get_subst); + + OBJECT_UNREF(array); + + GB_ReturnNewZeroString(result); + } + else + { + if (ERROR_last.free) + GB_ReturnString(ERROR_last.msg); + else + GB_ReturnConstZeroString(ERROR_last.msg); + } + } + else + GB_ReturnVoidString(); + +END_PROPERTY + + +BEGIN_PROPERTY(Error_Class) + + if (ERROR_last.code) + GB_ReturnObject(ERROR_last.cp); + else + GB_ReturnNull(); + +END_PROPERTY + + +BEGIN_PROPERTY(Error_Where) + + if (ERROR_last.code) + GB_ReturnNewZeroString(DEBUG_get_position(ERROR_last.cp, ERROR_last.fp, ERROR_last.pc)); + else + GB_ReturnVoidString(); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Error_Clear) + + ERROR_reset(&ERROR_last); + EXEC_got_error = FALSE; + +END_METHOD + + +BEGIN_METHOD(Error_Raise, GB_STRING msg) + + ERROR_define(GB_ToZeroString(ARG(msg)), NULL); + EXEC_set_native_error(TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Error_Propagate) + + if (ERROR_last.code) + { + ERROR_define_last(); + EXEC_set_native_error(TRUE); + } + +END_METHOD + + +BEGIN_PROPERTY(Error_Backtrace) + + if (ERROR_backtrace) + GB_ReturnObject(DEBUG_get_string_array_from_backtrace(ERROR_backtrace)); + else + GB_ReturnNull(); + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Error[] = +{ + GB_DECLARE_STATIC("Error"), + + GB_STATIC_PROPERTY_READ("Code", "i", Error_Code), + GB_STATIC_PROPERTY_READ("Text", "s", Error_Text), + GB_STATIC_PROPERTY_READ("Class", "Class", Error_Class), + GB_STATIC_PROPERTY_READ("Where", "s", Error_Where), + GB_STATIC_PROPERTY_READ("Backtrace", "String[]", Error_Backtrace), + + GB_STATIC_METHOD("Clear", NULL, Error_Clear, NULL), + GB_STATIC_METHOD("Raise", NULL, Error_Raise, "(Message)s"), + GB_STATIC_METHOD("Propagate", NULL, Error_Propagate, NULL), + + GB_END_DECLARE +}; + + diff --git a/main/gbx/gbx_c_error.h b/main/gbx/gbx_c_error.h new file mode 100644 index 00000000..eb906582 --- /dev/null +++ b/main/gbx/gbx_c_error.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + gbx_c_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_ERROR_H +#define __GBX_C_ERROR_H + +#include "gambas.h" + +#ifndef __GBX_C_ERROR_C +extern GB_DESC NATIVE_Error[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_file.c b/main/gbx/gbx_c_file.c new file mode 100644 index 00000000..fb4c4463 --- /dev/null +++ b/main/gbx/gbx_c_file.c @@ -0,0 +1,1212 @@ +/*************************************************************************** + + gbx_c_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_FILE_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include +#include +#include +#include +#include +#ifdef OS_BSD + #include +#else + #include +#endif +#include + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_list.h" +#include "gb_file.h" + +#include "gbx_api.h" +#include "gambas.h" +#include "gbx_class.h" +#include "gbx_stream.h" +#include "gbx_exec.h" +#include "gbx_project.h" +#include "gbx_string.h" +#include "gbx_date.h" +#include "gbx_watch.h" +#include "gbx_signal.h" + +#include "gbx_c_file.h" + +#define STREAM_FD STREAM_handle(THE_STREAM) + +mode_t CFILE_default_dir_auth = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Write); +DECLARE_EVENT(EVENT_Resize); + +static GB_FUNCTION _read_func; + +static char _buffer[16]; + +static bool _term_init = FALSE; +static ushort _term_width = 0; +static ushort _term_height = 0; +static SIGNAL_CALLBACK *_SIGWINCH_callback = NULL; +static GB_FUNCTION _term_resize_func; + +static CFILE *_std[3] = { NULL }; + + +static void callback_read(int fd, int type, CSTREAM *_object) +{ + STREAM *stream = CSTREAM_TO_STREAM(THIS); + int64_t len; + + if (!GB_CanRaise(THIS, EVENT_Read)) + goto __DISABLE_WATCH; + + STREAM_read_ahead(stream); + + if (stream->common.check_read) + { + if (!STREAM_lof_safe(stream, &len) && len == 0) + { + stream->common.eof = TRUE; + goto __DISABLE_WATCH; + } + } + + if (!stream->common.eof) + GB_Raise(THIS, EVENT_Read, 0); + else + WATCH_little_sleep(); + + return; + +__DISABLE_WATCH: + + GB_Watch(fd, GB_WATCH_READ, NULL, (intptr_t)THIS); +} + +static void callback_write(int fd, int type, CSTREAM *_object) +{ + if (GB_CanRaise(THIS, EVENT_Write)) + GB_Raise(THIS, EVENT_Write, 0); + else + GB_Watch(fd, GB_WATCH_WRITE, NULL, (intptr_t)THIS); +} + +static void cb_term_resize(int signum, intptr_t data) +{ + _term_width = _term_height = 0; + if (_std[CFILE_IN]) + GB_Raise(_std[CFILE_IN], EVENT_Resize, 0); +} + +static void watch_stream(CSTREAM *_object, int mode, bool on) +{ + STREAM *stream = &THIS_STREAM->stream; + int fd = STREAM_handle(stream); + + if (mode & GB_ST_READ) + GB_Watch(fd, GB_WATCH_READ, (void *)(on ? callback_read : NULL), (intptr_t)THIS); + + if (mode & GB_ST_WRITE) + GB_Watch(fd, GB_WATCH_WRITE, (void *)(on ? callback_write : NULL), (intptr_t)THIS); +} + +CFILE *CFILE_create(STREAM *stream, int mode) +{ + CFILE *file = OBJECT_new(CLASS_File, NULL, NULL); + OBJECT_UNREF_KEEP(file); + + if (stream) + { + *CSTREAM_TO_STREAM(file) = *stream; + //file->watch_fd = -1; + + if (mode & GB_ST_WATCH) + { + watch_stream(&file->ob, mode, TRUE); + OBJECT_attach((OBJECT *)file, OP ? (OBJECT *)OP : (OBJECT *)CP, "File"); + } + } + + return file; +} + +static CFILE *create_default_stream(FILE *file, int mode) +{ + STREAM stream; + //bool tty = isatty(fileno(file)); + + CLEAR(&stream); + stream.type = &STREAM_buffer; + stream.common.no_read_ahead = TRUE; + stream.common.standard = TRUE; + stream.common.check_read = TRUE; + stream.buffer.file = file; + //stream.direct.fd = fileno(file); + STREAM_check_blocking(&stream); + return (CFILE *)OBJECT_REF(CFILE_create(&stream, mode)); +} + +void CFILE_exit(void) +{ + int i; + + for (i = 0; i <= 2; i++) + OBJECT_UNREF(_std[i]); + + if (_term_init) + SIGNAL_unregister(SIGWINCH, _SIGWINCH_callback); +} + +void CFILE_init_watch(void) +{ + bool has_term_func = GB_GetFunction(&_term_resize_func, PROJECT_class, "Application_Resize", "", "") == 0; + bool has_read_func = GB_GetFunction(&_read_func, PROJECT_class, "Application_Read", "", "") == 0; + + if (has_term_func || has_read_func) + OBJECT_attach((OBJECT *)CFILE_get_standard_stream(CFILE_IN), (OBJECT *)PROJECT_class, "Application"); + + if (has_read_func) + { + //fprintf(stderr, "watch stdin\n"); + //CFILE_in->watch_fd = STDIN_FILENO; + GB_Watch(fileno(stdin), GB_WATCH_READ, (void *)callback_read, (intptr_t)CFILE_get_standard_stream(CFILE_IN)); + } +} + +CFILE *CFILE_get_standard_stream(int num) +{ + if (!_std[num]) + { + switch(num) + { + case CFILE_IN: + _std[CFILE_IN] = create_default_stream(stdin, GB_ST_READ); + break; + + case CFILE_OUT: + _std[CFILE_OUT] = create_default_stream(stdout, GB_ST_WRITE); + break; + + case CFILE_ERR: + _std[CFILE_ERR] = create_default_stream(stderr, GB_ST_WRITE); + break; + } + } + + return _std[num]; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(File_free) + + STREAM_close(&THIS->ob.stream); + +END_METHOD + + +BEGIN_PROPERTY(File_In) + + GB_ReturnObject(CFILE_get_standard_stream(CFILE_IN)); + +END_PROPERTY + + +BEGIN_PROPERTY(File_Out) + + GB_ReturnObject(CFILE_get_standard_stream(CFILE_OUT)); + +END_PROPERTY + + +BEGIN_PROPERTY(File_Err) + + GB_ReturnObject(CFILE_get_standard_stream(CFILE_ERR)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Stat_free) + + STRING_unref(&THIS_STAT->path); + +END_METHOD + + +BEGIN_PROPERTY(Stat_Type) + + GB_ReturnInt(THIS_STAT->info.type); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Path) + + GB_ReturnString(THIS_STAT->path); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Link) + + if (THIS_STAT->info.type == GB_STAT_LINK) + GB_ReturnNewZeroString(FILE_readlink(THIS_STAT->path)); + else + GB_ReturnVoidString(); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Mode) + + GB_ReturnInt(THIS_STAT->info.mode); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Hidden) + + GB_ReturnBoolean(THIS_STAT->info.hidden); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Size) + + GB_ReturnLong(THIS_STAT->info.size); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_LastAccess) + + VALUE date; + + DATE_from_time(THIS_STAT->info.atime, 0, &date); + + GB_ReturnDate((GB_DATE *)&date); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_LastChange) + + VALUE date; + + DATE_from_time(THIS_STAT->info.ctime, 0, &date); + + GB_ReturnDate((GB_DATE *)&date); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Time) + + VALUE date; + + DATE_from_time(THIS_STAT->info.mtime, 0, &date); + + GB_ReturnDate((GB_DATE *)&date); + +END_PROPERTY + + +static char *get_file_user(CFILE *_object) +{ + struct passwd *pwd; + uid_t uid = (uid_t)THIS_STAT->info.uid; + + if (uid == 0) + return "root"; + else + { + pwd = getpwuid(uid); + if (!pwd) + { + snprintf(_buffer, sizeof(_buffer), "%d", (int)uid); + return _buffer; + } + else + return pwd->pw_name; + } +} + +BEGIN_PROPERTY(Stat_User) + + GB_ReturnNewZeroString(get_file_user(THIS)); + +END_PROPERTY + + +static char *get_file_group(CFILE *_object) +{ + struct group *grp; + gid_t gid = (gid_t)THIS_STAT->info.gid; + + if (gid == 0) + return "root"; + else + { + grp = getgrgid(gid); + if (!grp) + { + snprintf(_buffer, sizeof(_buffer), "%d", (int)gid); + return _buffer; + } + else + return grp->gr_name; + } +} + +BEGIN_PROPERTY(Stat_Group) + + GB_ReturnNewZeroString(get_file_group(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_SetUID) + + GB_ReturnBoolean(THIS_STAT->info.mode & S_ISUID); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_SetGID) + + GB_ReturnBoolean(THIS_STAT->info.mode & S_ISGID); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Sticky) + + GB_ReturnBoolean(THIS_STAT->info.mode & S_ISVTX); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Auth) + + char *auth = FILE_mode_to_string(THIS_STAT->info.mode); + + GB_ReturnNewString(auth, FILE_buffer_length()); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Device) + + dev_t dev = THIS_STAT->info.device; + int len; + + if (dev == 0) + GB_ReturnNull(); + else + { + len = sprintf(COMMON_buffer, "/%s/%d:%d", THIS_STAT->info.chrdev ? "char" : "block", major(dev), minor(dev)); + GB_ReturnNewString(COMMON_buffer, len); + } + +END_PROPERTY + +//-------------------------------------------------------------------------- + +static void return_perm(CSTAT *_object, int rf, int wf, int xf) +{ + char perm[4]; + char *p; + int mode = THIS_STAT->info.mode; + + p = perm; + + if (mode & rf) *p++ = 'r'; + if (mode & wf) *p++ = 'w'; + if (mode & xf) *p++ = 'x'; + + *p = 0; + + GB_ReturnNewZeroString(perm); +} + + +BEGIN_PROPERTY(StatPerm_User) + + return_perm(THIS_STAT, S_IRUSR, S_IWUSR, S_IXUSR); + +END_PROPERTY + +BEGIN_PROPERTY(StatPerm_Group) + + return_perm(THIS_STAT, S_IRGRP, S_IWGRP, S_IXGRP); + +END_PROPERTY + +BEGIN_PROPERTY(StatPerm_Other) + + return_perm(THIS_STAT, S_IROTH, S_IWOTH, S_IXOTH); + +END_PROPERTY + +BEGIN_METHOD(StatPerm_get, GB_STRING user) + + char *who; + char *user = GB_ToZeroString(ARG(user)); + + who = get_file_user(THIS); + if (strcmp(user, who) == 0) + { + return_perm(THIS_STAT, S_IRUSR, S_IWUSR, S_IXUSR); + return; + } + + who = get_file_group(THIS); + if (strlen(user) > 2 && user[0] == '*' && user[1] == '.' && strcmp(&user[2], who) == 0) + { + return_perm(THIS_STAT, S_IRGRP, S_IWGRP, S_IXGRP); + return; + } + + return_perm(THIS_STAT, S_IROTH, S_IWOTH, S_IXOTH); + +END_METHOD + + +/*---- File path functions --------------------------------------------------*/ + +static char *_dir; +static char *_basename; +static char *_ext; + +static void split_path(char *path) +{ + char *p; + + p = rindex(path, '/'); + if (p) + { + if (p == path) + { + _dir = "/"; + _basename = path + 1; + } + else + { + *p = 0; + _dir = path; + _basename = p + 1; + } + } + else + { + _dir = ""; + _basename = path; + } + + p = rindex(_basename, '.'); + if (p) + { + *p = 0; + _ext = p + 1; + } + else + _ext = ""; +} + +static void return_path(void) +{ + char *tmp = NULL; + int len = strlen(_dir); + char *test; + + if (len) + { + tmp = STRING_add(tmp, _dir, len); + test = _basename ? _basename : _ext; + if (tmp[len - 1] != '/' && *test != '/') + tmp = STRING_add_char(tmp, '/'); + } + + if (_basename && *_basename) + tmp = STRING_add(tmp, _basename, 0); + + if (*_ext) + { + if (*_ext != '.') + tmp = STRING_add_char(tmp, '.'); + + tmp = STRING_add(tmp, _ext, 0); + } + + STRING_extend_end(tmp); + + GB_ReturnString(tmp); +} + +BEGIN_METHOD(File_Dir, GB_STRING path) + + split_path(GB_ToZeroString(ARG(path))); + GB_ReturnNewZeroString(_dir); + +END_METHOD + + +BEGIN_METHOD(File_SetDir, GB_STRING path; GB_STRING new_dir) + + split_path(GB_ToZeroString(ARG(path))); + _dir = GB_ToZeroString(ARG(new_dir)); + return_path(); + +END_METHOD + + +BEGIN_METHOD(File_Name, GB_STRING path) + + char *path; + + //if (LENGTH(path) && STRING(path)[LENGTH(path) - 1] == '/') + // LENGTH(path)--; + + path = GB_ToZeroString(ARG(path)); + GB_ReturnNewZeroString(FILE_get_name(path)); + +END_METHOD + + +BEGIN_METHOD(File_SetName, GB_STRING path; GB_STRING new_name) + + char *path; + + if (LENGTH(path) && STRING(path)[LENGTH(path) - 1] == '/') + LENGTH(path)--; + + path = GB_ToZeroString(ARG(path)); + + split_path(path); + _basename = GB_ToZeroString(ARG(new_name)); + _ext = ""; + return_path(); + +END_METHOD + + +BEGIN_METHOD(File_Ext, GB_STRING path) + + split_path(GB_ToZeroString(ARG(path))); + GB_ReturnNewZeroString(_ext); + +END_METHOD + + +BEGIN_METHOD(File_SetExt, GB_STRING path; GB_STRING new_ext) + + split_path(GB_ToZeroString(ARG(path))); + _ext = GB_ToZeroString(ARG(new_ext)); + return_path(); + +END_METHOD + + +BEGIN_METHOD(File_BaseName, GB_STRING path) + + split_path(GB_ToZeroString(ARG(path))); + GB_ReturnNewZeroString(_basename); + +END_METHOD + + +BEGIN_METHOD(File_SetBaseName, GB_STRING path; GB_STRING new_basename) + + split_path(GB_ToZeroString(ARG(path))); + _basename = GB_ToZeroString(ARG(new_basename)); + return_path(); + +END_METHOD + +static void error_CFILE_load_save(STREAM *stream) +{ + STREAM_close(stream); +} + +BEGIN_METHOD(File_Load, GB_STRING path) + + STREAM stream; + int64_t len; + int rlen; + char *str = NULL; + + STREAM_open(&stream, STRING_conv_file_name(STRING(path), LENGTH(path)), GB_ST_READ); + + ON_ERROR_1(error_CFILE_load_save, &stream) + { + STREAM_lof(&stream, &len); + if (len >> 31) + THROW(E_MEMORY); + + if (len == 0) + { + char buffer[256]; + + str = NULL; + + for(;;) + { + len = STREAM_read_max(&stream, buffer, sizeof(buffer)); + if (len) str = STRING_add(str, buffer, len); + if (len < sizeof(buffer)) + break; + } + } + else + { + rlen = len; + + str = STRING_new(NULL, rlen); + rlen = STREAM_read_max(&stream, str, rlen); + str = STRING_extend(str, rlen); + } + + STREAM_close(&stream); + + STRING_free_later(str); + GB_ReturnString(str); + } + END_ERROR + +END_METHOD + +BEGIN_METHOD(File_Save, GB_STRING path; GB_STRING data) + + STREAM stream; + + STREAM_open(&stream, STRING_conv_file_name(STRING(path), LENGTH(path)), GB_ST_CREATE); + + ON_ERROR_1(error_CFILE_load_save, &stream) + { + STREAM_write(&stream, STRING(data), LENGTH(data)); + STREAM_close(&stream); + } + END_ERROR + +END_METHOD + +BEGIN_METHOD(File_IsRelative, GB_STRING path) + + char *path = STRING(path); + int len = LENGTH(path); + + if (len <= 0) + { + GB_ReturnBoolean(FALSE); + return; + } + + GB_ReturnBoolean(FILE_is_relative(path)); + +END_METHOD + +BEGIN_METHOD(File_IsHidden, GB_STRING path) + + char *path = STRING(path); + int len = LENGTH(path); + int i; + char c; + + for (i = 0; i < len; i++) + { + if (path[i] != '.') + continue; + + if (i > 0 && path[i - 1] != '/') + continue; + + if (i == (len - 1)) + continue; + + c = path[i + 1]; + if (c == '/') + continue; + + if (c == '.') + { + if (i == (len - 2)) + continue; + + if (path[i + 2] == '/') + continue; + } + + GB_ReturnBoolean(TRUE); + return; + } + + GB_ReturnBoolean(FALSE); + +END_METHOD + +BEGIN_METHOD(File_RealPath, GB_STRING path) + + char *path = STRING_conv_file_name(STRING(path), LENGTH(path)); + char *rpath = NULL; + + if (!FILE_is_relative(path)) + { + rpath = realpath(path, NULL); + path = rpath; + } + + GB_ReturnNewZeroString(path); + if (rpath) free(rpath); + +END_METHOD + +BEGIN_PROPERTY(File_DefaultDirAuth) + + if (READ_PROPERTY) + { + char *auth = FILE_mode_to_string(CFILE_default_dir_auth); + GB_ReturnNewString(auth, FILE_buffer_length()); + } + else + { + CFILE_default_dir_auth = FILE_mode_from_string((mode_t)0, GB_ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + + +//-------------------------------------------------------------------------- + +BEGIN_PROPERTY(Stream_Handle) + + GB_ReturnInteger(STREAM_FD); + +END_PROPERTY + + +BEGIN_PROPERTY(Stream_ByteOrder) + + bool endian = EXEC_big_endian; + + if (READ_PROPERTY) + { + if (THE_STREAM->common.swap) + endian = !endian; + + GB_ReturnInteger(endian ? 1 : 0); + } + else + { + bool val = VPROP(GB_INTEGER); + THE_STREAM->common.swap = endian ^ val; + } + +END_PROPERTY + +BEGIN_PROPERTY(Stream_EndOfLine) + + if (READ_PROPERTY) + GB_ReturnInteger(THE_STREAM->common.eol); + else + { + int eol = VPROP(GB_INTEGER); + + if (eol >= 0 && eol <= 2) + THE_STREAM->common.eol = eol; + } + +END_PROPERTY + +BEGIN_METHOD_VOID(Stream_Close) + + STREAM_close(THE_STREAM); + +END_METHOD + +BEGIN_PROPERTY(Stream_EndOfFile) + + GB_ReturnBoolean(THE_STREAM->common.eof); + +END_PROPERTY + +BEGIN_PROPERTY(Stream_Blocking) + + if (READ_PROPERTY) + GB_ReturnBoolean(STREAM_is_blocking(THE_STREAM)); + else + STREAM_blocking(THE_STREAM, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Stream_Tag) + + if (READ_PROPERTY) + GB_ReturnVariant(&THIS_STREAM->tag); + else + GB_StoreVariant(PROP(GB_VARIANT), POINTER(&(THIS_STREAM->tag))); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_free) + + STREAM_close(THE_STREAM); + GB_StoreVariant(NULL, POINTER(&(THIS_STREAM->tag))); + +END_METHOD + +BEGIN_PROPERTY(Stream_Eof) + + GB_ReturnBoolean(STREAM_eof(THE_STREAM)); + +END_PROPERTY + +BEGIN_PROPERTY(Stream_NullTerminatedString) + + if (READ_PROPERTY) + GB_ReturnBoolean(THE_STREAM->common.null_terminated); + else + THE_STREAM->common.null_terminated = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_METHOD(Stream_ReadLine, GB_STRING escape) + + char *escape; + char *str; + + if (MISSING(escape)) + escape = NULL; + else + { + escape = GB_ToZeroString(ARG(escape)); + if (!*escape) + escape = NULL; + } + + str = STREAM_line_input(THE_STREAM, escape); + STRING_free_later(str); + GB_ReturnString(str); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_Begin) + + STREAM_begin(THE_STREAM); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_End) + + STREAM_end(THE_STREAM); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_Cancel) + + STREAM_cancel(THE_STREAM); + +END_METHOD + +BEGIN_PROPERTY(Stream_IsTerm) + + GB_ReturnBoolean(isatty(STREAM_FD)); + +END_PROPERTY + +BEGIN_METHOD(Stream_Watch, GB_INTEGER mode; GB_BOOLEAN on) + + int mode = VARG(mode); + + if (mode != GB_ST_READ && mode != GB_ST_WRITE) + { + GB_Error(GB_ERR_ARG); + return; + } + + watch_stream(THIS_STREAM, mode, VARG(on)); + +END_METHOD + +BEGIN_METHOD_VOID(StreamLines_next) + + char *str; + + if (STREAM_eof(THE_STREAM)) + GB_StopEnum(); + else + { + str = STREAM_line_input(THE_STREAM, NULL); + STRING_free_later(str); + GB_ReturnString(str); + } + +END_METHOD + +//--------------------------------------------------------------------------- + +static void init_term_size(void *_object) +{ + struct winsize winSize; + + if (_term_width == 0 || _term_height == 0) + { + if (ioctl(STREAM_FD, TIOCGWINSZ, (char *)&winSize)) + THROW_SYSTEM(errno, NULL); + + _term_width = winSize.ws_col; + _term_height = winSize.ws_row; + } + + if (!_term_init) + { + _SIGWINCH_callback = SIGNAL_register(SIGWINCH, cb_term_resize, 0); + _term_init = TRUE; + } +} + +BEGIN_PROPERTY(StreamTerm_Name) + + GB_ReturnNewZeroString(ttyname(STREAM_FD)); + +END_PROPERTY + +BEGIN_METHOD(StreamTerm_Resize, GB_INTEGER width; GB_INTEGER height) + + struct winsize winSize = { 0 }; + + winSize.ws_row = (unsigned short)Max(1, Min(65535, VARG(height))); + winSize.ws_col = (unsigned short)Max(1, Min(65535, VARG(width))); + + if (ioctl(STREAM_FD, TIOCSWINSZ, (char *)&winSize)) + THROW_SYSTEM(errno, NULL); + +END_METHOD + +static void handle_term_property(void *_object, void *_param, bool iflag, int flag) +{ + struct termios ttmode; + + if (tcgetattr(STREAM_FD, &ttmode)) + THROW_SYSTEM(errno, ""); + + if (READ_PROPERTY) + GB_ReturnBoolean(((iflag ? ttmode.c_iflag : ttmode.c_lflag) & flag) == flag); + else + { + if (VPROP(GB_BOOLEAN)) + { + if (iflag) + ttmode.c_iflag |= flag; + else + ttmode.c_lflag |= flag; + } + else + { + if (iflag) + ttmode.c_iflag &= ~flag; + else + ttmode.c_lflag &= ~flag; + } + + if (tcsetattr(STREAM_FD, TCSANOW, &ttmode)) + THROW_SYSTEM(errno, ""); + } +} + +BEGIN_PROPERTY(StreamTerm_Echo) + + handle_term_property(_object, _param, FALSE, ECHO); + +END_PROPERTY + +BEGIN_PROPERTY(StreamTerm_FlowControl) + + handle_term_property(_object, _param, TRUE, IXON | IXOFF); + +END_PROPERTY + +BEGIN_PROPERTY(StreamTerm_Width) + + init_term_size(_object); + GB_ReturnInteger(_term_width); + +END_PROPERTY + +BEGIN_PROPERTY(StreamTerm_Height) + + init_term_size(_object); + GB_ReturnInteger(_term_height); + +END_PROPERTY + +#endif + +//--------------------------------------------------------------------------- + +GB_DESC StreamLinesDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stream.Lines"), + + GB_METHOD("_next", "s", StreamLines_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC StreamTermDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stream.Term"), + + GB_PROPERTY_READ("Name", "s", StreamTerm_Name), + GB_PROPERTY("Echo", "b", StreamTerm_Echo), + GB_PROPERTY("FlowControl", "b", StreamTerm_FlowControl), + GB_PROPERTY_READ("Width", "i", StreamTerm_Width), + GB_PROPERTY_READ("W", "i", StreamTerm_Width), + GB_PROPERTY_READ("Height", "i", StreamTerm_Height), + GB_PROPERTY_READ("H", "i", StreamTerm_Height), + GB_METHOD("Resize", NULL, StreamTerm_Resize, "(Width)i(Height)i"), + + GB_END_DECLARE +}; + +GB_DESC StreamDesc[] = +{ + GB_DECLARE("Stream", sizeof(CSTREAM)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, Stream_free, NULL), + + GB_PROPERTY("ByteOrder", "i", Stream_ByteOrder), + GB_PROPERTY_READ("Handle", "i", Stream_Handle), + GB_PROPERTY("EndOfLine", "i", Stream_EndOfLine), + GB_METHOD("Close", NULL, Stream_Close, NULL), + GB_PROPERTY_READ("EndOfFile", "b", Stream_EndOfFile), + GB_PROPERTY("NullTerminatedString", "b", Stream_NullTerminatedString), + GB_PROPERTY("Blocking", "b", Stream_Blocking), + GB_PROPERTY("Tag", "v", Stream_Tag), + GB_METHOD("ReadLine", "s", Stream_ReadLine, "[(Escape)s]"), + GB_PROPERTY_READ("IsTerm", "b", Stream_IsTerm), + + GB_PROPERTY_SELF("Lines", ".Stream.Lines"), + GB_PROPERTY_SELF("Term", ".Stream.Term"), + + GB_METHOD("Begin", NULL, Stream_Begin, NULL), + GB_METHOD("Send", NULL, Stream_End, NULL), + GB_METHOD("Drop", NULL, Stream_Cancel, NULL), + + GB_METHOD("Watch", NULL, Stream_Watch, "(Mode)i(Watch)b"), + + GB_PROPERTY_READ("Eof", "b", Stream_Eof), + + GB_END_DECLARE +}; + + +GB_DESC StatPermDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stat.Perm"), + + GB_METHOD("_get", "s", StatPerm_get, "(UserOrGroup)s"), + GB_PROPERTY_READ("User", "s", StatPerm_User), + GB_PROPERTY_READ("Group", "s", StatPerm_Group), + GB_PROPERTY_READ("Other", "s", StatPerm_Other), + + GB_END_DECLARE +}; + + +GB_DESC StatDesc[] = +{ + GB_DECLARE("Stat", sizeof(CSTAT)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, Stat_free, NULL), + + GB_PROPERTY_READ("Type", "i", Stat_Type), + GB_PROPERTY_READ("Mode", "i", Stat_Mode), + GB_PROPERTY_READ("Hidden", "b", Stat_Hidden), + GB_PROPERTY_READ("Size", "l", Stat_Size), + GB_PROPERTY_READ("Time", "d", Stat_Time), + GB_PROPERTY_READ("LastAccess", "d", Stat_LastAccess), + GB_PROPERTY_READ("LastModified", "d", Stat_Time), + GB_PROPERTY_READ("LastChange", "d", Stat_LastChange), + GB_PROPERTY_READ("User", "s", Stat_User), + GB_PROPERTY_READ("Group", "s", Stat_Group), + GB_PROPERTY_SELF("Perm", ".Stat.Perm"), + GB_PROPERTY_READ("SetGID", "b", Stat_SetGID), + GB_PROPERTY_READ("SetUID", "b", Stat_SetUID), + GB_PROPERTY_READ("Sticky", "b", Stat_Sticky), + GB_PROPERTY_READ("Path", "s", Stat_Path), + GB_PROPERTY_READ("Link", "s", Stat_Link), + GB_PROPERTY_READ("Auth", "s", Stat_Auth), + GB_PROPERTY_READ("Device", "s", Stat_Device), + + GB_END_DECLARE +}; + + +GB_DESC FileDesc[] = +{ + GB_DECLARE("File", sizeof(CFILE)), + GB_INHERITS("Stream"), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, File_free, NULL), + +// GB_STATIC_PROPERTY_READ("Separator", "s", CFILE_separator), + + GB_STATIC_PROPERTY_READ("In", "File", File_In), + GB_STATIC_PROPERTY_READ("Out", "File", File_Out), + GB_STATIC_PROPERTY_READ("Err", "File", File_Err), + + GB_STATIC_METHOD("Dir", "s", File_Dir, "(Path)s"), + GB_STATIC_METHOD("Name", "s", File_Name, "(Path)s"), + GB_STATIC_METHOD("Ext", "s", File_Ext, "(Path)s"), + GB_STATIC_METHOD("BaseName", "s", File_BaseName, "(Path)s"), + + GB_STATIC_METHOD("SetDir", "s", File_SetDir, "(Path)s(NewDir)s"), + GB_STATIC_METHOD("SetName", "s", File_SetName, "(Path)s(NewName)s"), + GB_STATIC_METHOD("SetExt", "s", File_SetExt, "(Path)s(NewExt)s"), + GB_STATIC_METHOD("SetBaseName", "s", File_SetBaseName, "(Path)s(NewBaseName)s"), + + GB_STATIC_METHOD("IsRelative", "b", File_IsRelative, "(Path)s"), + GB_STATIC_METHOD("IsHidden", "b", File_IsHidden, "(Path)s"), + + GB_STATIC_METHOD("Load", "s", File_Load, "(FileName)s"), + GB_STATIC_METHOD("Save", NULL, File_Save, "(FileName)s(Data)s"), + + GB_STATIC_METHOD("RealPath", "s", File_RealPath, "(Path)s"), + + GB_STATIC_PROPERTY("DefaultDirAuth", "s", File_DefaultDirAuth), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Write", NULL, NULL, &EVENT_Write), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_file.h b/main/gbx/gbx_c_file.h new file mode 100644 index 00000000..ff2a2cfd --- /dev/null +++ b/main/gbx/gbx_c_file.h @@ -0,0 +1,86 @@ +/*************************************************************************** + + gbx_c_file.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_FILE_H +#define __GBX_C_FILE_H + +#include "gambas.h" + +#include "gbx_value.h" +#include "gbx_stream.h" +#include "gbx_object.h" +#include "gb_file.h" + +typedef + struct { + OBJECT ob; + STREAM stream; + GB_VARIANT_VALUE tag; + } + CSTREAM; + +typedef + struct { + CSTREAM ob; + } + CFILE; + +typedef + struct { + OBJECT ob; + FILE_STAT info; + char *path; + } + CSTAT; + +#ifndef __GBX_C_FILE_C + +extern GB_DESC StreamLinesDesc[]; +extern GB_DESC StreamTermDesc[]; +extern GB_DESC StreamDesc[]; +extern GB_DESC FileDesc[]; +extern GB_DESC StatDesc[]; +extern GB_DESC StatPermDesc[]; + +extern mode_t CFILE_default_dir_auth; + +#else + +#define THIS ((CFILE *)_object) +#define THIS_STREAM ((CSTREAM *)_object) +#define THIS_STAT ((CSTAT *)_object) +#define THE_STREAM CSTREAM_TO_STREAM(THIS_STREAM) + +#endif + +#define CSTREAM_TO_STREAM(_cstream) (&((CSTREAM *)(void *)(_cstream))->stream) +#define CSTREAM_FROM_STREAM(_stream) ((CSTREAM *)((char *)(_stream) - sizeof(OBJECT))) + +enum { CFILE_IN = 0, CFILE_OUT = 1, CFILE_ERR = 2 }; + +CFILE *CFILE_create(STREAM *stream, int mode); +void CFILE_exit(void); +void CFILE_init_watch(void); +CFILE *CFILE_get_standard_stream(int num); + +#endif diff --git a/main/gbx/gbx_c_gambas.c b/main/gbx/gbx_c_gambas.c new file mode 100644 index 00000000..d0d4fb69 --- /dev/null +++ b/main/gbx/gbx_c_gambas.c @@ -0,0 +1,272 @@ +/*************************************************************************** + + gbx_c_gambas.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_GAMBAS_C + +#include + +#include "gbx_info.h" +#include "gbx_local.h" +#include "gbx_compare.h" +#include "gb_type_common.h" +#include "gb_file.h" +#include "gbx_date.h" +#include "gbx_exec.h" + +#ifndef GBX_INFO + +#include "gb_error.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_event.h" +#include "gbx_c_array.h" +#include "gbx_c_gambas.h" + + +static int get_arg_count(void) +{ + if (DEBUG_inside_eval && DEBUG_info) + { + if (DEBUG_info->fp && DEBUG_info->fp->vararg) + return DEBUG_info->bp - DEBUG_info->pp; + } + else + { + if (FP && FP->vararg) + return BP - PP; + } + return 0; +} + +static VALUE *get_arg(int i) +{ + if (DEBUG_inside_eval && DEBUG_info) + return &DEBUG_info->pp[i]; + else + return &PP[i]; +} + +BEGIN_PROPERTY(Param_Count) + + GB_ReturnInteger(get_arg_count()); + +END_PROPERTY + + +BEGIN_PROPERTY(Param_Max) + + GB_ReturnInteger(get_arg_count() - 1); + +END_PROPERTY + + +BEGIN_METHOD(Param_get, GB_INTEGER index) + + int index = VARG(index); + VALUE *arg; + + if (index < 0 || index >= get_arg_count()) + THROW(E_BOUND); + + arg = get_arg(index); + VALUE_conv(arg, T_VARIANT); + TEMP = *arg; + //VALUE_conv(&TEMP, T_VARIANT); + +END_METHOD + + +BEGIN_PROPERTY(Param_All) + + GB_ARRAY all; + int nparam = get_arg_count(); + int i; + VALUE *arg; + + GB_ArrayNew(POINTER(&all), T_VARIANT, nparam); + + for (i = 0; i < nparam; i++) + { + arg = get_arg(i); + VALUE_conv(arg, T_VARIANT); + GB_StoreVariant((GB_VARIANT *)arg, GB_ArrayGet(all, i)); + } + + GB_ReturnObject(all); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Param_next) + + int *index = (int *)GB_GetEnum(); + + if (*index >= get_arg_count()) + GB_StopEnum(); + else + { + VALUE *arg = get_arg(*index); + VALUE_conv(arg, T_VARIANT); + TEMP = *arg; + (*index)++; + } + +END_METHOD + + +BEGIN_PROPERTY(Param_Name) + + GB_ReturnConstZeroString(EXEC_unknown_name); + +END_PROPERTY + + +/*BEGIN_PROPERTY(Param_Property) + + GB_ReturnBoolean(EXEC_unknown_property); + +END_PROPERTY*/ + +BEGIN_PROPERTY(Param_EventName) + + GB_ReturnConstZeroString(EVENT_Name); + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Param[] = +{ + GB_DECLARE_STATIC("Param"), + + GB_STATIC_PROPERTY_READ("Count", "i", Param_Count), + GB_STATIC_PROPERTY_READ("Max", "i", Param_Max), + GB_STATIC_PROPERTY_READ("All", "Variant[]", Param_All), + + GB_STATIC_PROPERTY_READ("Name", "s", Param_Name), + GB_STATIC_PROPERTY_READ("EventName", "s", Param_EventName), + //GB_STATIC_PROPERTY_READ("Property", "b", Param_Property), + + GB_STATIC_METHOD("_get", "v", Param_get, "(Index)i"), + GB_STATIC_METHOD("_next", "v", Param_next, NULL), + + //GB_STATIC_METHOD("Copy", "Variant[]", CPARAM_copy, "[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Gambas[] = +{ + GB_DECLARE_STATIC("gb"), + + GB_CONSTANT("Binary", "i", GB_COMP_BINARY), + GB_CONSTANT("IgnoreCase", "i", GB_COMP_NOCASE), + GB_CONSTANT("Language", "i", GB_COMP_LANG), + GB_CONSTANT("Like", "i", GB_COMP_LIKE), + GB_CONSTANT("Match", "i", GB_COMP_MATCH), + GB_CONSTANT("Natural", "i", GB_COMP_NATURAL), + + GB_CONSTANT("Ascent", "i", GB_COMP_ASCENT), + GB_CONSTANT("Descent", "i", GB_COMP_DESCENT), + + /* BE CAREFUL ! These constants are used in the compiler */ + GB_CONSTANT("Null", "i", T_NULL), + GB_CONSTANT("Boolean", "i", T_BOOLEAN), + GB_CONSTANT("Byte", "i", T_BYTE), + GB_CONSTANT("Short", "i", T_SHORT), + GB_CONSTANT("Integer", "i", T_INTEGER), + GB_CONSTANT("Long", "i", T_LONG), + GB_CONSTANT("Float", "i", T_FLOAT), + GB_CONSTANT("Single", "i", T_SINGLE), + GB_CONSTANT("Date", "i", T_DATE), + GB_CONSTANT("String", "i" , T_STRING), + GB_CONSTANT("Pointer", "i" , T_POINTER), + GB_CONSTANT("Function", "i" , T_FUNCTION), + GB_CONSTANT("Variant", "i", T_VARIANT), + GB_CONSTANT("Class", "i" , T_CLASS), + GB_CONSTANT("Object", "i", T_OBJECT), + + GB_CONSTANT("File", "i", GB_STAT_FILE), + GB_CONSTANT("Directory", "i", GB_STAT_DIRECTORY), + GB_CONSTANT("Device", "i", GB_STAT_DEVICE), + GB_CONSTANT("Pipe", "i", GB_STAT_PIPE), + GB_CONSTANT("Socket", "i", GB_STAT_SOCKET), + GB_CONSTANT("Link", "i", GB_STAT_LINK), + + GB_CONSTANT("NewLine", "s", "\n"), + GB_CONSTANT("Tab", "s", "\t"), + GB_CONSTANT("Cr", "s", "\r"), + GB_CONSTANT("Lf", "s", "\n"), + GB_CONSTANT("CrLf", "s", "\r\n"), + + GB_CONSTANT("Standard", "i", LF_STANDARD), + GB_CONSTANT("GeneralNumber", "i", LF_GENERAL_NUMBER), + GB_CONSTANT("ShortNumber", "i", LF_SHORT_NUMBER), + GB_CONSTANT("Fixed", "i", LF_FIXED), + GB_CONSTANT("Percent", "i", LF_PERCENT), + GB_CONSTANT("Scientific", "i", LF_SCIENTIFIC), + GB_CONSTANT("Currency", "i", LF_CURRENCY), + GB_CONSTANT("International", "i", LF_INTERNATIONAL), + GB_CONSTANT("GeneralDate", "i", LF_GENERAL_DATE), + GB_CONSTANT("LongDate", "i", LF_LONG_DATE), + GB_CONSTANT("MediumDate", "i", LF_MEDIUM_DATE), + GB_CONSTANT("ShortDate", "i", LF_SHORT_DATE), + GB_CONSTANT("LongTime", "i", LF_LONG_TIME), + GB_CONSTANT("MediumTime", "i", LF_MEDIUM_TIME), + GB_CONSTANT("ShortTime", "i", LF_SHORT_TIME), + + GB_CONSTANT("Read", "i", GB_ST_READ), + GB_CONSTANT("Write", "i", GB_ST_WRITE), + GB_CONSTANT("Exec", "i", GB_ST_EXEC), + GB_CONSTANT("Input", "i", GB_ST_READ + GB_ST_BUFFERED), + GB_CONSTANT("Output", "i", GB_ST_WRITE + GB_ST_BUFFERED), + + GB_CONSTANT("Sunday", "i", 0), + GB_CONSTANT("Monday", "i", 1), + GB_CONSTANT("Tuesday", "i", 2), + GB_CONSTANT("Wednesday", "i", 3), + GB_CONSTANT("Thursday", "i", 4), + GB_CONSTANT("Friday", "i", 5), + GB_CONSTANT("Saturday", "i", 6), + + GB_CONSTANT("Millisecond", "i", DP_MILLISECOND), + GB_CONSTANT("Second", "i", DP_SECOND), + GB_CONSTANT("Minute", "i", DP_MINUTE), + GB_CONSTANT("Hour", "i", DP_HOUR), + GB_CONSTANT("Day", "i", DP_DAY), + GB_CONSTANT("WeekDay", "i", DP_WEEKDAY), + GB_CONSTANT("Week", "i", DP_WEEK), + GB_CONSTANT("Month", "i", DP_MONTH), + GB_CONSTANT("Quarter", "i", DP_QUARTER), + GB_CONSTANT("Year", "i", DP_YEAR), + + GB_CONSTANT("LittleEndian", "i", GB_LITTLE_ENDIAN), + GB_CONSTANT("BigEndian", "i", GB_BIG_ENDIAN), + + GB_CONSTANT("Unix", "i", GB_EOL_UNIX), + GB_CONSTANT("Windows", "i", GB_EOL_WINDOWS), + GB_CONSTANT("Mac", "i", GB_EOL_MAC), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_gambas.h b/main/gbx/gbx_c_gambas.h new file mode 100644 index 00000000..29c4a2e9 --- /dev/null +++ b/main/gbx/gbx_c_gambas.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gbx_c_gambas.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_GAMBAS_H +#define __GBX_C_GAMBAS_H + +#include "gambas.h" +#include "gbx_object.h" + +#ifndef __GBX_C_GAMBAS_C +extern GB_DESC NATIVE_Gambas[]; +extern GB_DESC NATIVE_Param[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_observer.c b/main/gbx/gbx_c_observer.c new file mode 100644 index 00000000..dffde447 --- /dev/null +++ b/main/gbx/gbx_c_observer.c @@ -0,0 +1,164 @@ +/*************************************************************************** + + gbx_c_observer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_OBSERVER_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_event.h" +#include "gbx_c_observer.h" + +//#define DEBUG_ME 1 + +void COBSERVER_lock(COBSERVER *this, bool lock) +{ + if (lock) + this->locked++; + else + this->locked--; +} + +void COBSERVER_attach(COBSERVER *this, void *parent, const char *name) +{ + #if DEBUG_ME + fprintf(stderr, "COBSERVER_attach: %p: %s %p\n", this, parent ? OBJECT_class(parent)->name : "", parent); + #endif + if (this->object && this->event) + EVENT_search(OBJECT_class(this->object), this->event, name, parent); +} + +void COBSERVER_detach(COBSERVER *this) +{ + #if DEBUG_ME + fprintf(stderr, "COBSERVER_detach: %p\n", this); + #endif + + if (this->event) + FREE(&this->event); +} + +BEGIN_METHOD(Observer_new, GB_OBJECT object; GB_BOOLEAN after) + + OBJECT *object; + OBJECT_EVENT *ev; + char *name; + CLASS *class; + void *parent; + + object = (OBJECT *)VARG(object); + if (GB_CheckObject(object)) + return; + + parent = OBJECT_parent(THIS); + + if (!parent) + return; + + class = OBJECT_class(object); + if (class->n_event == 0) + return; + + name = EVENT_Name; + if (!name || !*name) + return; + + #if DEBUG_ME + fprintf(stderr, "Observer_new: %p %d %s (%s %p)\n", THIS, OBJECT_class(object)->n_event, name, GB_GetClassName(parent), parent); + #endif + + ev = OBJECT_event(object); + + THIS->after = VARGOPT(after, FALSE); + + ALLOC_ZERO(&THIS->event, sizeof(ushort) * class->n_event); + + THIS->object = object; + OBJECT_attach((OBJECT *)THIS, parent, name); + COBSERVER_attach(THIS, parent, name); + + LIST_insert((void **)&ev->observer, THIS, &THIS->list); + OBJECT_REF(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Observer_free) + + #if DEBUG_ME + fprintf(stderr, "Observer_free: %p\n", THIS); + #endif + + GB_StoreVariant(NULL, &THIS->tag); + COBSERVER_detach(THIS); + +END_METHOD + +BEGIN_PROPERTY(Observer_Object) + + GB_ReturnObject(THIS->object); + +END_PROPERTY + +BEGIN_PROPERTY(Observer_Tag) + + if (READ_PROPERTY) + GB_ReturnVariant(&THIS->tag); + else + GB_StoreVariant(PROP(GB_VARIANT), &THIS->tag); + +END_PROPERTY + +BEGIN_METHOD_VOID(Observer_Detach) + + OBJECT_EVENT *ev; + + if (!THIS->object) + return; + + ev = OBJECT_event(THIS->object); + LIST_remove((void **)&ev->observer, THIS, &THIS->list); + THIS->object = NULL; + OBJECT_UNREF(_object); + +END_METHOD + +#endif + +GB_DESC NATIVE_Observer[] = +{ + GB_DECLARE("Observer", sizeof(COBSERVER)), + + GB_METHOD("_new", NULL, Observer_new, "(Object)o[(After)b]"), + GB_METHOD("_free", NULL, Observer_free, NULL), + + GB_PROPERTY_READ("Object", "o", Observer_Object), + GB_PROPERTY("Tag", "v", Observer_Tag), + + GB_METHOD("Detach", NULL, Observer_Detach, NULL), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_observer.h b/main/gbx/gbx_c_observer.h new file mode 100644 index 00000000..7458ad2e --- /dev/null +++ b/main/gbx/gbx_c_observer.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + gbx_c_observer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_OBSERVER_H +#define __GBX_C_OBSERVER_H + +#include "gambas.h" +#include "gbx_object.h" +#include "gb_list.h" + +#ifndef __GBX_C_OBSERVER_C +extern GB_DESC NATIVE_Observer[]; +#else +#define THIS ((COBSERVER *)_object) +#endif + +// Note: the interpreter automatically allocates an extra OBJECT_EVENT structure. See CLASS_calc_info(). + +typedef + struct { + OBJECT ob; + LIST list; + ushort *event; + void *object; + GB_VARIANT_VALUE tag; + short locked; + unsigned after : 1; + } + COBSERVER; + +void COBSERVER_attach(COBSERVER *this, void *parent, const char *name); +void COBSERVER_detach(COBSERVER *this); +void COBSERVER_lock(COBSERVER *this, bool lock); + +#define COBSERVER_is_locked(_this) (((COBSERVER *)_this)->locked > 0) + +#endif diff --git a/main/gbx/gbx_c_process.c b/main/gbx/gbx_c_process.c new file mode 100644 index 00000000..99c3d396 --- /dev/null +++ b/main/gbx/gbx_c_process.c @@ -0,0 +1,1276 @@ +/*************************************************************************** + + gbx_c_process.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_PROCESS_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gb_common_buffer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_replace.h" +#include "gb_limit.h" +#include "gb_array.h" +#include "gbx_api.h" +#include "gambas.h" +#include "gbx_stream.h" +#include "gbx_exec.h" +#include "gbx_class.h" +#include "gbx_watch.h" +#include "gbx_project.h" +#include "gbx_c_array.h" +#include "gbx_local.h" +#include "gbx_signal.h" +#include "gbx_event.h" + +#include "gbx_c_process.h" + +//#define DEBUG_ME +//#define DEBUG_CHILD + +char *CPROCESS_shell = NULL; + +extern char **environ; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Error); +DECLARE_EVENT(EVENT_Kill); + +static CPROCESS *_running_process_list = NULL; +static int _running_process = 0; +static int _ignore_process = 0; + +static SIGNAL_CALLBACK *_SIGCHLD_callback; +static bool _init = FALSE; + +static int _last_status = 0; + +static int _last_child_error = 0; +static int _last_child_error_errno = 0; + +static void init_child(void); +static void exit_child(void); +static bool wait_child(CPROCESS *process); + +enum { + CHILD_NO_ERROR, + CHILD_CANNOT_OPEN_TTY, + CHILD_CANNOT_INIT_TTY, + CHILD_CANNOT_PLUG_INPUT, + CHILD_CANNOT_PLUG_OUTPUT, + CHILD_CANNOT_EXEC, +}; + +static const char *const _child_error[] = { + NULL, + "cannot open slave pseudo-terminal: ", + "cannot initialize pseudo-terminal: ", + "cannot plug standard input: ", + "cannot plug standard output and standard error: ", + "cannot run executable: " +}; + +//------------------------------------------------------------------------- + +static void close_fd(int *pfd) +{ + int fd = *pfd; + + if (fd >= 0) + { + #ifdef DEBUG_ME + fprintf(stderr, "unwatch & close: %d\n", fd); + #endif + GB_Watch(fd, GB_WATCH_NONE, NULL, 0); + close(fd); + *pfd = -1; + } +} + +static void add_process_to_running_list(CPROCESS *process) +{ + if (_running_process_list) + _running_process_list->prev = process; + + process->next = _running_process_list; + process->prev = NULL; + + _running_process_list = process; + + process->running = TRUE; + _running_process++; +} + +static void remove_process_from_running_list(CPROCESS *process) +{ + if (process->prev) + process->prev->next = process->next; + + if (process->next) + process->next->prev = process->prev; + + if (process == _running_process_list) + _running_process_list = process->next; + + process->running = FALSE; + + _running_process--; + if (process->ignore) + _ignore_process--; +} + +static void callback_write(int fd, int type, CPROCESS *process) +{ + #ifdef DEBUG_ME + fprintf(stderr, "callback_write: %d %p\n", fd, process); + #endif + + if (process->to_string) + { + int n; + + for(;;) + { + n = read(fd, COMMON_buffer, 256); + if (n >= 0 || errno != EINTR) + break; + } + + if (n > 0) + { + process->result = STRING_add(process->result, COMMON_buffer, n); + return; + } + } + + if (GB_CanRaise(process, EVENT_Read)) + { + if (!STREAM_read_ahead(CSTREAM_TO_STREAM(process))) + { + GB_Raise(process, EVENT_Read, 0); + return; + } + } + + close_fd(&process->out); +} + + +static void callback_error(int fd, int type, CPROCESS *process) +{ + char buffer[256]; + int n; + + #ifdef DEBUG_ME + fprintf(stderr, "callback_error: %d %p\n", fd, process); + #endif + + if (process->to_string && process->with_error) + { + int n; + + for(;;) + { + n = read(fd, COMMON_buffer, 256); + if (n >= 0 || errno != EINTR) + break; + } + + if (n > 0) + { + process->result = STRING_add(process->result, COMMON_buffer, n); + return; + } + } + + if (GB_CanRaise(process, EVENT_Error)) + { + n = read(fd, buffer, sizeof(buffer)); + if (n > 0) + { + GB_Raise(process, EVENT_Error, 1, GB_T_STRING, buffer, n); + return; + } + } + + close_fd(&process->err); +} + + +static void update_stream(CPROCESS *process) +{ + STREAM *stream = &process->ob.stream; + + stream->type = &STREAM_process; + (*stream->type->open)(stream, NULL, 0, process); +} + + +static void init_process(CPROCESS *process) +{ + //process->watch = GB_WATCH_NONE; + process->in = process->out = process->err = -1; + update_stream(process); +} + +static void exit_process(CPROCESS *_object) +{ + #ifdef DEBUG_ME + fprintf(stderr, "exit_process: %p\n", _object); + #endif + + if (THIS->in >= 0) + { + if (THIS->in != THIS->out) + close(THIS->in); + THIS->in = -1; + } + + close_fd(&THIS->out); + close_fd(&THIS->err); + + STREAM_close(&THIS->ob.stream); +} + +static void prepare_child_error(CPROCESS *_object) +{ + int fd; + char path[PATH_MAX]; + + snprintf(path, sizeof(path), FILE_TEMP_DIR "/%d.child", (int)getuid(), (int)getpid(), (int)THIS->pid); + + #ifdef DEBUG_ME + fprintf(stderr, "prepare_child_error: %p: %s\n", _object, path); + #endif + + if (_last_child_error == 0) + { + fd = open(path, O_RDONLY); + if (fd >= 0) + { + if (read(fd, &_last_child_error, sizeof(int)) != sizeof(int) + || read(fd, &_last_child_error_errno, sizeof(int)) != sizeof(int)) + { + _last_child_error = -1; + _last_child_error_errno = 0; + } + + close(fd); + } + + #ifdef DEBUG_ME + fprintf(stderr, "prepare_child_error: error = %d errno = %d\n", _last_child_error, _last_child_error_errno); + #endif + } + + unlink(path); +} + +static void throw_last_child_error() +{ + int child_error, child_errno; + + if (_last_child_error == 0) + return; + + child_error = _last_child_error; + child_errno = _last_child_error_errno; + + _last_child_error = 0; + + #ifdef DEBUG_ME + fprintf(stderr, "throw_last_child_error: %d %d\n", child_error, child_errno); + #endif + + if (child_error < 0) + THROW(E_CHILD, "unknown error", ""); + else + THROW(E_CHILD, _child_error[child_error], strerror(child_errno)); +} + +static void stop_process_after(CPROCESS *_object) +{ + STREAM *stream; + bool do_exit_process = THIS->in >= 0; + + #ifdef DEBUG_ME + fprintf(stderr, "stop_process_after: %p out = %d err = %d\n", _object, THIS->out, THIS->err); + #endif + + if (WIFEXITED(THIS->status) && WEXITSTATUS(THIS->status) == 255) + { + prepare_child_error(THIS); + exit_process(THIS); + //OBJECT_detach((OBJECT *)THIS); + } + + /* Vidage du tampon d'erreur */ + while (THIS->err >= 0) + { + callback_error(THIS->err, 0, THIS); + do_exit_process = TRUE; + } + + /* Vidage du tampon de sortie */ + if (THIS->out >= 0) + { + stream = CSTREAM_TO_STREAM(THIS); + while (THIS->out >= 0 && !STREAM_is_closed(stream)) + callback_write(THIS->out, 0, THIS); + + do_exit_process = TRUE; + } + + if (do_exit_process) + exit_process(THIS); + + /*printf("** stop_process_after\n");*/ + //GB_Unref((void **)&_object); /* Ref du post */ +} + + +static void stop_process(CPROCESS *_object) +{ + if (!THIS->running) + return; + + #ifdef DEBUG_ME + fprintf(stderr, "stop_process: %p\n", THIS); + #endif + + /* Remove from running process list */ + + stop_process_after(THIS); + remove_process_from_running_list(THIS); + + #ifdef DEBUG_ME + fprintf(stderr, "Raising Kill event for %p: parent = %p can raise = %d\n", THIS, OBJECT_parent(THIS), GB_CanRaise(THIS, EVENT_Kill)); + #endif + GB_Raise(THIS, EVENT_Kill, 0); + + OBJECT_detach((OBJECT *)THIS); + OBJECT_UNREF(_object); + + //if (!_running_process_list) + if (_running_process <= _ignore_process) + exit_child(); +} + +static void abort_child(int error) +{ + int fd; + int save_errno; + char path[PATH_MAX]; + + fflush(stdout); + fflush(stderr); + + save_errno = errno; + + #ifdef DEBUG_ME + fprintf(stderr, "abort_child: %d %d\n", error, save_errno); + #endif + + snprintf(path, sizeof(path), FILE_TEMP_DIR "/%d.child", (int)getuid(), (int)getppid(), (int)getpid()); + + fd = open(path, O_CREAT | O_WRONLY, 0600); + if (fd >= 0) + { + write(fd, &error, sizeof(int)) == sizeof(int) + && write(fd, &save_errno, sizeof(int)) == sizeof(int); + close(fd); + } + + _exit(255); +} + +static void init_child_tty(int fd) +{ + struct termios terminal = { 0 }; + struct termios check; + + tcgetattr(fd, &terminal); + + terminal.c_iflag |= IXON | IXOFF | ICRNL; + #ifdef IUTF8 + if (LOCAL_is_UTF8) + terminal.c_iflag |= IUTF8; + #endif + + terminal.c_oflag |= OPOST; + //terminal.c_oflag &= ~ONLCR; + + terminal.c_lflag |= ISIG | ICANON | IEXTEN | ECHO; + ///terminal.c_lflag &= ~ECHO; + + #ifdef DEBUG_CHILD + fprintf(stderr, "init_child_tty: %s\n", isatty(fd) ? ttyname(fd) : "not a tty!"); + #endif + + if (tcsetattr(fd, TCSANOW, &terminal)) + { + #ifdef DEBUG_CHILD + int save_errno = errno; + fprintf(stderr, "init_child_tty: errno = %d\n", errno); + errno = save_errno; + #endif + abort_child(CHILD_CANNOT_INIT_TTY); + } + + tcgetattr(fd, &check); + if (check.c_iflag != terminal.c_iflag || check.c_oflag != terminal.c_oflag || check.c_lflag != terminal.c_lflag) + abort_child(CHILD_CANNOT_INIT_TTY); +} + +const char *CPROCESS_search_program_in_path(char *name) +{ + char *search; + char *p, *p2; + int len; + const char *path; + + if (!name) + return NULL; + + if (strchr(name, '/')) + { + if (access(name, X_OK) == 0) + return name; + else + return NULL; + } + + search = getenv("PATH"); + if (!search || !*search) + search = "/usr/bin:/bin"; + + search = STRING_new_zero(search); + + path = NULL; + + //fprintf(stderr, "search_program_in_path: '%s' in '%s'\n", name, search); + + p = search; + for(;;) + { + p2 = strchr(p, ':'); + if (p2) + len = p2 - p; + else + len = strlen(p); + + if (len > 0) + { + p[len] = 0; + //fprintf(stderr, "trying: %s\n", p); + path = FILE_cat(p, name, NULL); + if (access(path, X_OK) == 0) + break; + path = NULL; + } + + if (!p2) + break; + + p = p2 + 1; + } + + STRING_free(&search); + //fprintf(stderr, "--> %s\n", path); + return path; +} + +static void run_process(CPROCESS *process, int mode, void *cmd, CARRAY *env) +{ + static const char *shell[] = { NULL, "-c", NULL, NULL }; + + int fdin[2], fdout[2], fderr[2]; + pid_t pid; + char **argv; + CARRAY *array; + int i, n; + sigset_t sig, old; + int save_errno; + + // for virtual terminal + int fd_master = -1; + char *slave = NULL; + struct termios termios_master; + const char *exec; + + if (mode & PM_SHELL) + { + #ifdef DEBUG_ME + fprintf(stderr, "run_process %p: %s\n", process, (char *)cmd); + #endif + + argv = (char **)shell; + + if (CPROCESS_shell) + argv[0] = CPROCESS_shell; + else + argv[0] = "/bin/sh"; + + argv[2] = (char *)cmd; + + if (argv[2] == NULL || *argv[2] == 0) + return; + + exec = argv[0]; + + process->process_group = TRUE; + } + else + { + array = (CARRAY *)cmd; + n = ARRAY_count(array->data); + + if (n == 0) + return; + + ALLOC(&argv, sizeof(*argv) * (n + 1)); + memcpy(argv, array->data, sizeof(*argv) * n); + argv[n] = NULL; + + exec = CPROCESS_search_program_in_path(argv[0]); + if (!exec) + { + IFREE(argv); + THROW(E_NEXIST); + } + + for (i = 0; i < n; i++) + { + if (!argv[i]) + argv[i] = ""; + } + + #ifdef DEBUG_ME + { + int i; + + fprintf(stderr, "run_process %p: ", process); + for (i = 0; i < n; i++) + fprintf(stderr, "%s ", argv[i]); + fprintf(stderr, "\n"); + } + #endif + } + + if (mode & PM_STRING) + { + process->to_string = TRUE; + process->with_error = (mode & PM_ERROR) != 0; + process->result = NULL; + mode |= PM_READ; + } + + if (mode & PM_TERM) + { + fd_master = posix_openpt(O_RDWR | O_NOCTTY); + if (fd_master < 0) + goto __ABORT_ERRNO; + + grantpt(fd_master); + unlockpt(fd_master); + slave = ptsname(fd_master); + #ifdef DEBUG_ME + fprintf(stderr, "run_process: slave = %s\n", slave); + #endif + + if (tcgetattr(fd_master, &termios_master)) + goto __ABORT_ERRNO; + + cfmakeraw(&termios_master); + //termios_master.c_lflag &= ~ECHO; + + if (tcsetattr(fd_master, TCSANOW, &termios_master)) + goto __ABORT_ERRNO; + } + else + { + /* Create pipes */ + + if ((mode & PM_WRITE) && pipe(fdin) != 0) + goto __ABORT_ERRNO; + + if ((mode & PM_READ) && (pipe(fdout) != 0 || pipe(fderr) != 0)) + goto __ABORT_ERRNO; + } + + // Adding to the running process list + + add_process_to_running_list(process); + OBJECT_REF(process); + + // Start the SIGCHLD callback + + init_child(); + + // Block SIGCHLD and fork + + sigemptyset(&sig); + + sigaddset(&sig, SIGCHLD); + sigprocmask(SIG_BLOCK, &sig, &old); + + if (mode & PM_SHELL || mode & PM_TERM || env) + pid = fork(); + else + pid = vfork(); + + if (pid == (-1)) + { + stop_process(process); + sigprocmask(SIG_SETMASK, &old, NULL); + goto __ABORT_ERRNO; + } + + // parent process + + if (pid) + { + process->pid = pid; + + #ifdef DEBUG_ME + fprintf(stderr, "fork: pid = %d\n", pid); + #endif + + if (mode & PM_WRITE) + { + if (mode & PM_TERM) + { + process->in = fd_master; + } + else + { + close(fdin[0]); + process->in = fdin[1]; + } + } + + if (mode & PM_READ) + { + if (mode & PM_TERM) + { + process->out = fd_master; + process->err = -1; + } + else + { + close(fdout[1]); + close(fderr[1]); + + process->out = fdout[0]; + process->err = fderr[0]; + } + + #ifdef DEBUG_ME + fprintf(stderr, "watch: out = %d err = %d\n", process->out, process->err); + #endif + GB_Watch(process->out, GB_WATCH_READ, (void *)callback_write, (intptr_t)process); + if (process->err >= 0) + { + fcntl(process->err, F_SETFL, fcntl(process->err, F_GETFL) | O_NONBLOCK); + GB_Watch(process->err, GB_WATCH_READ, (void *)callback_error, (intptr_t)process); + } + } + + if ((mode & PM_SHELL) == 0) + { + FREE(&argv); + } + + sigprocmask(SIG_SETMASK, &old, NULL); + } + else //------------ child process ---------------------------- + { + int fd_slave; + bool pwd; + int ch_i, ch_n; + + sigprocmask(SIG_SETMASK, &old, NULL); + + if (mode & PM_TERM) + { + close(fd_master); + setsid(); + fd_slave = open(slave, O_RDWR); + if (fd_slave < 0) + abort_child(CHILD_CANNOT_OPEN_TTY); + + init_child_tty(fd_slave); + + /*#ifdef DEBUG_ME + fprintf(stderr, "run_process (child): slave = %s isatty = %d\n", slave, isatty(fd_slave)); + #endif*/ + + if (mode & PM_WRITE) + { + if (dup2(fd_slave, STDIN_FILENO) == -1) + abort_child(CHILD_CANNOT_PLUG_INPUT); + } + + if (mode & PM_READ) + { + if ((dup2(fd_slave, STDOUT_FILENO) == -1) + || (dup2(fd_slave, STDERR_FILENO) == -1)) + abort_child(CHILD_CANNOT_PLUG_OUTPUT); + } + + // Strange Linux behaviour ? + // Terminal initialization must be done on STDIN_FILENO after using dup2(). + // If it is done on fd_slave, before using dup2(), it sometimes fails with no error. + + /*if (mode & PM_WRITE) + init_child_tty(STDIN_FILENO); + else if (mode & PM_READ) + init_child_tty(STDOUT_FILENO);*/ + + /*puts("---------------------------------"); + if (stdin_isatty) puts("STDIN is a tty");*/ + /*tcgetattr(STDIN_FILENO, &termios_check); + puts(termios_check.c_lflag & ISIG ? "+ISIG" : "-ISIG"); + //tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_check); + system("stty icanon"); + system("stty -a"); + puts("---------------------------------");*/ + } + else + { + if (mode & PM_WRITE) + { + close(fdin[1]); + + if (dup2(fdin[0], STDIN_FILENO) == -1) + abort_child(CHILD_CANNOT_PLUG_INPUT); + } + + if (mode & PM_READ) + { + close(fdout[0]); + close(fderr[0]); + + if ((dup2(fdout[1], STDOUT_FILENO) == -1) + || (dup2(fderr[1], STDERR_FILENO) == -1)) + abort_child(CHILD_CANNOT_PLUG_OUTPUT); + } + } + + if (mode & PM_SHELL) + setpgid(0, 0); + + pwd = FALSE; + + if (env) + { + char *str; + ch_n = ARRAY_count(env->data); + for (ch_i = 0; ch_i < ch_n; ch_i++) + { + str = ((char **)env->data)[ch_i]; + if (putenv(str)) + ERROR_warning("cannot set environment string: %s", str); + if (strncmp(str, "PWD=", 4) == 0 && chdir(&str[4]) == 0) + pwd = TRUE; + } + } + + // Return to the parent working directory if the PWD environment variable has not been set + if (!pwd) + FILE_chdir(PROJECT_oldcwd); + + execv(exec, (char **)argv); + abort_child(CHILD_CANNOT_EXEC); + } + + update_stream(process); + + #ifdef DEBUG_ME + fprintf(stderr, "run_process: child is OK\n"); + #endif + + return; + +__ABORT_ERRNO: + + save_errno = errno; + stop_process(process); + THROW_SYSTEM(save_errno, NULL); +} + +void CPROCESS_check(void *_object) +{ + #ifdef DEBUG_ME + fprintf(stderr, "CPROCESS_check: %p\n", THIS); + #endif + + usleep(100); + if (wait_child(THIS)) + { + #ifdef DEBUG_ME + fprintf(stderr, "CPROCESS_check: stop process later\n"); + #endif + stop_process_after(THIS); + EVENT_post(stop_process, (intptr_t)THIS); + throw_last_child_error(); + } + + //fprintf(stderr, "CPROCESS_check: %p END\n", THIS); +} + +static bool wait_child(CPROCESS *process) +{ + int status; + int ret; + + #ifdef DEBUG_ME + fprintf(stderr, "wait_child: check process %d\n", process->pid); + #endif + + if ((ret = waitpid(process->pid, &status, WNOHANG)) == process->pid) + { + process->status = status; + process->wait = TRUE; + _last_status = status; + + #ifdef DEBUG_ME + fprintf(stderr, "wait_child: process %d has returned %d\n", process->pid, status); + #endif + + return TRUE; + } + else + { + #ifdef DEBUG_ME + fprintf(stderr, "wait_child: waitpid() has returned %d\n", ret); + #endif + + return FALSE; + } +} + +static void callback_child(int signum, intptr_t data) +{ + CPROCESS *process; + + #ifdef DEBUG_ME + fprintf(stderr, ">> callback_child\n"); + #endif + + for (process = _running_process_list; process; ) + { + if (wait_child(process)) + { + stop_process(process); + process = _running_process_list; + } + else + process = process->next; + } + + throw_last_child_error(); + + #ifdef DEBUG_ME + fprintf(stderr, "<< callback_child\n"); + #endif +} + +void CPROCESS_callback_child(void) +{ + callback_child(SIGCHLD, 0); +} + +static void init_child(void) +{ + if (!_init) + { + #ifdef DEBUG_ME + fprintf(stderr, "init_child()\n"); + #endif + + _SIGCHLD_callback = SIGNAL_register(SIGCHLD, callback_child, 0); + + _init = TRUE; + } + + SIGNAL_check(SIGCHLD); +} + +static void exit_child(void) +{ + if (!_init) + return; + + #ifdef DEBUG_ME + fprintf(stderr, "exit_child()\n"); + #endif + + SIGNAL_unregister(SIGCHLD, _SIGCHLD_callback); + + _init = FALSE; +} + + +static void error_CPROCESS_create(CPROCESS *process) +{ + OBJECT_UNREF(process); +} + +CPROCESS *CPROCESS_create(int mode, void *cmd, char *name, CARRAY *env) +{ + CPROCESS *process; + + process = OBJECT_new(CLASS_Process, name, OP ? (OBJECT *)OP : (OBJECT *)CP); + //fprintf(stderr, "CPROCESS_create: %p\n", process); + + ON_ERROR_1(error_CPROCESS_create, process) + { + init_process(process); + run_process(process, mode, cmd, env); + } + END_ERROR + + OBJECT_UNREF_KEEP(process); + + if (!name || !*name) + STREAM_blocking(CSTREAM_TO_STREAM(process), TRUE); + + return process; +} + +void CPROCESS_wait_for(CPROCESS *process, int timeout) +{ + int ret; + int sigfd; + + #ifdef DEBUG_ME + fprintf(stderr, "Waiting for %d\n", process->pid); + #endif + + // If CPROCESS_check() caught the process end, process->running is not set yet, because + // stop_process() will be raised at the next event loop. So no need to wait for it. + + if (process->wait) + return; + + OBJECT_REF(process); + + ON_ERROR_1(error_CPROCESS_create, process) + { + while (process->running) + { + sigfd = SIGNAL_get_fd(); + #ifdef DEBUG_ME + fprintf(stderr, "Watch process %d (end = %d)\n", process->pid, sigfd); + #endif + SIGNAL_check(SIGCHLD); + ret = WATCH_process(sigfd, process->out, process->err, timeout); + #ifdef DEBUG_ME + fprintf(stderr, "Watch process %d ->%s%s%s%s\n", process->pid, ret & WP_END ? " END" : "", ret & WP_OUTPUT ? " OUTPUT" : "", ret & WP_ERROR ? " ERROR" : "", ret & WP_TIMEOUT ? " TIMEOUT" : ""); + #endif + + if (ret & WP_OUTPUT) + callback_write(process->out, GB_WATCH_READ, process); + + if (ret & WP_ERROR) + callback_error(process->err, GB_WATCH_READ, process); + + if (ret & WP_END) + SIGNAL_raise_callbacks(sigfd, GB_WATCH_READ, 0); + + if (ret & WP_TIMEOUT) + break; + + if (ret == 0) + usleep(1000); + } + } + END_ERROR + + OBJECT_UNREF(process); + + #if 0 + { + sigsuspend(&old); + if (!process->running) + break; + + #ifdef DEBUG_ME + fprintf(stderr, "Waiting: %d\n", process->running); + #endif + + sleep(10); + } + #endif + + /*if (have_sigchld) + sigaddset(&old, SIGCHLD); + sigprocmask(SIG_SETMASK, &old, NULL);*/ + + #ifdef DEBUG_ME + fprintf(stderr, "Waiting for: got it !\n"); + #endif +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Process_exit) + + //fprintf(stderr, "Process_exit\n"); + + while (_running_process_list) + stop_process(_running_process_list); + + exit_child(); + + STRING_free(&CPROCESS_shell); + +END_METHOD + + +BEGIN_METHOD_VOID(Process_new) + + init_process(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Process_free) + + #ifdef DEBUG_ME + fprintf(stderr, "Process_free %p\n", THIS); + #endif + + exit_process(THIS); + +END_METHOD + + +BEGIN_PROPERTY(Process_Id) + + GB_ReturnInt(THIS->pid); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Process_Kill) + + pid_t pid; + + if (!THIS->running) + return; + + if (THIS->process_group) + { + pid = getpgid(THIS->pid); + if (pid < 0) + return; + pid = - pid; + } + else + pid = THIS->pid; + + kill(pid, SIGKILL); + + //CPROCESS_wait_for(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Process_Signal) + + if (!THIS->running) + return; + + /* + printf("Send SIGUSR1 to process %d\n", THIS->pid); + fflush(NULL);*/ + kill(THIS->pid, SIGUSR1); + +END_METHOD + + +#if 0 +BEGIN_METHOD(CPROCESS_send, GB_STRING text) + + if (!THIS->running || THIS->in < 0) + return; + + STREAM_write(&THIS->ob.stream, STRING(text), LENGTH(text)); + +END_METHOD +#endif + +BEGIN_PROPERTY(Process_State) + + if (THIS->running) + GB_ReturnInteger(1); + else + { + if (WIFEXITED(THIS->status)) + GB_ReturnInteger(0); + else + GB_ReturnInteger(2); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Process_Value) + + int status; + + if (THIS->running) + { + GB_ReturnInteger(0); + return; + } + + status = THIS->status; + + if (WIFEXITED(status)) + GB_ReturnInteger(WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + GB_ReturnInteger(WTERMSIG(status)); + else + GB_ReturnInteger(-1); + +END_PROPERTY + +BEGIN_PROPERTY(Process_LastState) + + if (WIFEXITED(_last_status)) + GB_ReturnInteger(0); + else + GB_ReturnInteger(2); + +END_PROPERTY + + +BEGIN_PROPERTY(Process_LastValue) + + int status; + + status = _last_status; + + if (WIFEXITED(status)) + GB_ReturnInteger(WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + GB_ReturnInteger(WTERMSIG(status)); + else + GB_ReturnInteger(-1); + +END_PROPERTY + +BEGIN_METHOD(Process_Wait, GB_FLOAT timeout) + + // FIXME: Does not work if Ignore is set + CPROCESS_wait_for(THIS, (int)(VARGOPT(timeout, 0.0) * 1000)); + +END_METHOD + +BEGIN_PROPERTY(Process_Ignore) + + if (READ_PROPERTY) + GB_ReturnBoolean(THIS->ignore); + else + { + bool ignore = VPROP(GB_BOOLEAN); + if (THIS->ignore != ignore) + { + THIS->ignore = ignore; + if (ignore) + { + _ignore_process++; + if (_running_process <= _ignore_process) + exit_child(); + } + else + { + _ignore_process--; + if (_running_process > _ignore_process) + init_child(); + } + } + } + +END_METHOD + +BEGIN_METHOD_VOID(Process_CloseInput) + + close_fd(&THIS->in); + +END_METHOD + +#endif + +GB_DESC NATIVE_Process[] = +{ + GB_DECLARE("Process", sizeof(CPROCESS)), GB_NOT_CREATABLE(), + GB_INHERITS("Stream"), + + GB_CONSTANT("Stopped", "i", 0), + GB_CONSTANT("Running", "i", 1), + GB_CONSTANT("Crashed", "i", 2), + GB_CONSTANT("Signaled", "i", 2), + + GB_STATIC_PROPERTY_READ("LastState", "i", Process_LastState), + GB_STATIC_PROPERTY_READ("LastValue", "i", Process_LastValue), + + GB_PROPERTY_READ("Id", "i", Process_Id), + GB_PROPERTY_READ("Handle", "i", Process_Id), + GB_PROPERTY_READ("State", "i", Process_State), + GB_PROPERTY_READ("Value", "i", Process_Value), + + GB_STATIC_METHOD("_exit", NULL, Process_exit, NULL), + GB_METHOD("_new", NULL, Process_new, NULL), + GB_METHOD("_free", NULL, Process_free, NULL), + + GB_METHOD("Kill", NULL, Process_Kill, NULL), + GB_METHOD("Signal", NULL, Process_Signal, NULL), + GB_METHOD("Wait", NULL, Process_Wait, "[(Timeout)f]"), + + GB_METHOD("CloseInput", NULL, Process_CloseInput, NULL), + + GB_PROPERTY("Ignore", "b", Process_Ignore), + + /*GB_METHOD("Exec", NULL, Process_Exec, "(Command)String[];[(Mode)i(Environment)String[];]"), + GB_METHOD("Shell", NULL, Process_Shell, "(Command)s;[(Mode)i(Environment)String[];]"),*/ + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Error", NULL, "(Error)s", &EVENT_Error), + GB_EVENT("Kill", NULL, NULL, &EVENT_Kill), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_process.h b/main/gbx/gbx_c_process.h new file mode 100644 index 00000000..5513ec75 --- /dev/null +++ b/main/gbx/gbx_c_process.h @@ -0,0 +1,83 @@ +/*************************************************************************** + + gbx_c_process.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_PROCESS_H +#define __GBX_C_PROCESS_H + +#include +#include + +#include "gambas.h" +#include "gbx_object.h" +#include "gbx_c_file.h" +#include "gbx_c_array.h" + +#ifndef __GBX_C_PROCESS_C +extern GB_DESC NATIVE_Process[]; +extern char *CPROCESS_shell; +#else + +#define THIS ((CPROCESS *)_object) + +#endif + +typedef + struct _CPROCESS { + CSTREAM ob; + struct _CPROCESS *prev; + struct _CPROCESS *next; + pid_t pid; + int in; + int out; + int err; + int status; + unsigned running : 1; + unsigned wait : 1; + unsigned to_string : 1; + unsigned with_error : 1; + unsigned process_group : 1; + unsigned ignore : 1; + unsigned error : 1; + char *result; + GB_VARIANT_VALUE tag; + } + CPROCESS; + +enum +{ + PM_READ = 1, + PM_WRITE = 2, + PM_TERM = 4, + PM_STRING = 8, + PM_WAIT = 16, + PM_ERROR = 32, + PM_SHELL = 128 +}; + +CPROCESS *CPROCESS_create(int mode, void *cmd, char *name, CARRAY *env); +void CPROCESS_wait_for(CPROCESS *process, int timeout); +void CPROCESS_check(void *_object); +const char *CPROCESS_search_program_in_path(char *name); +void CPROCESS_callback_child(void); + +#endif diff --git a/main/gbx/gbx_c_string.c b/main/gbx/gbx_c_string.c new file mode 100644 index 00000000..5eec9835 --- /dev/null +++ b/main/gbx/gbx_c_string.c @@ -0,0 +1,1042 @@ +/*************************************************************************** + + gbx_c_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_STRING_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_buffer.h" + +#include +#include +#include + +#include "gb_error.h" +#include "gb_table.h" +#include "gbx_string.h" +#include "gbx_api.h" +#include "gbx_exec.h" +#include "gbx_subr.h" +#include "gbx_compare.h" +#include "gambas.h" + +#include "gbx_c_string.h" + +//#define DEBUG_CACHE + +const uchar STRING_char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +//----------------------------------------------------------------------------- + +static int utf8_get_length(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + int ulen; + int i; + + ulen = 0; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + +uint STRING_utf8_to_unicode(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + uint unicode; + + switch (len) + { + case 2: + unicode = (str[1] & 0x3F) + ((str[0] & 0x1F) << 6); + if (unicode < 0x80) + goto __INVALID; + break; + + case 3: + unicode = (str[2] & 0x3F) + ((str[1] & 0x3F) << 6) + ((str[0] & 0xF) << 12); + if (unicode < 0x800) + goto __INVALID; + break; + + case 4: + unicode = (str[3] & 0x3F) + ((str[2] & 0x3F) << 6) + ((str[1] & 0x3F) << 12) + ((str[0] & 0x7) << 18); + if (unicode < 0x10000) + goto __INVALID; + break; + + case 5: + unicode = (str[4] & 0x3F) + ((str[3] & 0x3F) << 6) + ((str[2] & 0x3F) << 12) + ((str[1] & 0x3F) << 18) + ((str[0] & 0x3) << 24); + if (unicode < 0x200000) + goto __INVALID; + break; + + case 6: + unicode = (str[5] & 0x3F) + ((str[4] & 0x3F) << 6) + ((str[3] & 0x3F) << 12) + ((str[2] & 0x3F) << 18) + ((str[1] & 0x3F) << 24) + ((str[0] & 0x1) << 30); + if (unicode < 0x4000000) + goto __INVALID; + break; + + default: + unicode = str[0]; + break; + } + + return unicode; + +__INVALID: + + return UNICODE_INVALID; +} + +void STRING_utf8_from_unicode(uint code, char *sstr) +{ + uchar *str = (uchar *)sstr; + + if (code < 0x80) + str[0] = code; + else if (code < 0x800) + { + str[0] = (code >> 6) | 0xC0; + str[1] = (code & 0x3F) | 0x80; + } + else if (code < 0x10000) + { + str[0] = (code >> 12) | 0xE0; + str[1] = ((code >> 6) & 0x3F) | 0x80; + str[2] = (code & 0x3F) | 0x80; + } + else if (code < 0x200000) + { + str[0] = (code >> 18) | 0xF0; + str[1] = ((code >> 12) & 0x3F) | 0x80; + str[2] = ((code >> 6) & 0x3F) | 0x80; + str[3] = (code & 0x3F) | 0x80; + } + else if (code < 0x4000000) + { + str[0] = (code >> 24) | 0xF8; + str[1] = ((code >> 18) & 0x3F) | 0x80; + str[2] = ((code >> 12) & 0x3F) | 0x80; + str[3] = ((code >> 6) & 0x3F) | 0x80; + str[4] = (code & 0x3F) | 0x80; + } + else if (code < 0x80000000) + { + str[0] = (code >> 31) | 0xFC; + str[1] = ((code >> 24) & 0x3F)| 0x80; + str[2] = ((code >> 18) & 0x3F) | 0x80; + str[3] = ((code >> 12) & 0x3F) | 0x80; + str[4] = ((code >> 6) & 0x3F) | 0x80; + str[5] = (code & 0x3F) | 0x80; + } + else + str[0] = 0; +} + +int COMMON_get_unicode_char() +{ + int lc; + uint uc; + + if (COMMON_pos > COMMON_len) + return -1; + + lc = STRING_utf8_get_char_length(*COMMON_get_current()); + if (COMMON_pos + lc > COMMON_len) + return -1; + + uc = STRING_utf8_to_unicode(COMMON_get_current(), lc); + COMMON_pos += lc; + return (int)uc; +} + +//----------------------------------------------------------------------------- + +char *STRING_utf8_current = NULL; +static const char *_utf8_current_start = NULL; +static int _utf8_current_len = 0; + +#define UTF8_MAX_COUNT 256 +#define UTF8_MAX_CACHE 64 + +struct { + ushort pos[UTF8_MAX_COUNT]; + ushort last_pos; + ushort cnext; + int lpos; + int lindex; + int cindex[UTF8_MAX_CACHE]; + int cpos[UTF8_MAX_CACHE]; + } +_utf8 = { { 0 } }; + +static int utf8_get_pos(const char *ref, const char *start, int len, int index) +{ + const uchar *str; + int i, j, pos; + int min_index, min_i; + +#ifdef DEBUG_CACHE + fprintf(stderr, "utf8_get_pos: [%p] %p %d %d\n", ref, start, len, index); +#endif + + if (index <= 0) + return 0; + + if (ref != STRING_utf8_current || start != _utf8_current_start || len != _utf8_current_len) + { + STRING_utf8_current = (char *)ref; + _utf8_current_start = start; + _utf8_current_len = len; +#ifdef DEBUG_CACHE + fprintf(stderr, "current -> %p / %ld\n", STRING_utf8_current, _utf8_current_start - STRING_utf8_current); +#endif + CLEAR(&_utf8); + } + + str = (const uchar *)start; + + if (index < UTF8_MAX_COUNT) + { + if (index <= _utf8.last_pos) + { +#ifdef DEBUG_CACHE + fprintf(stderr, "cached -> %d\n", _utf8.pos[index]); +#endif + pos = _utf8.pos[index]; + if (pos >= len) + pos = len; + return pos; + } + + pos = _utf8.pos[_utf8.last_pos]; + + for(;;) + { + if (pos >= len) + return len; + + pos += STRING_utf8_get_char_length(str[pos]); + _utf8.pos[++_utf8.last_pos] = pos; + + if (index == _utf8.last_pos) + { +#ifdef DEBUG_CACHE + fprintf(stderr, "search -> %d\n", pos); +#endif + return pos; + } + } + } + +#ifdef DEBUG_CACHE + fprintf(stderr, "index = %d\n", index); +#endif + + if (index == _utf8.lindex) + { + pos = _utf8.lpos; + if (pos >= len) + pos = len; + return pos; + } + + min_index = 0; + min_i = -1; + + for (j = 0; j < UTF8_MAX_CACHE; j++) + { + i = (_utf8.cnext + UTF8_MAX_CACHE - j - 1) % UTF8_MAX_CACHE; + + if (_utf8.cindex[i] == 0) + break; + + if ((index >= _utf8.cindex[i]) && (index < (_utf8.cindex[i] + 256))) + { +#ifdef DEBUG_CACHE + fprintf(stderr, "use cache %d (%d)\n", i, _utf8.cindex[i]); +#endif + pos = _utf8.cpos[i]; + j = _utf8.cindex[i]; + goto __CALC_POS; + } + else if (_utf8.cindex[i] > min_index && _utf8.cindex[i] < index) + { + min_index = _utf8.cindex[i]; + min_i = i; + } + } + + j = index & ~0xFF; + + if (min_i < 0) + { + pos = 0; + i = 0; + } + else + { + pos = _utf8.cpos[min_i]; + i = _utf8.cindex[min_i]; + } + +#ifdef DEBUG_CACHE + fprintf(stderr, "add cache %d: %d / %d\n", _utf8.cnext, j, index - i); +#endif + + for (; i < j; i++) + { + if (pos >= len) + { + pos = len; + break; + } + pos += STRING_utf8_get_char_length(str[pos]); + } + + _utf8.cindex[_utf8.cnext] = j; + _utf8.cpos[_utf8.cnext] = pos; + _utf8.cnext = (_utf8.cnext + 1) % UTF8_MAX_CACHE; + +__CALC_POS: + + for (i = j; i < index; i++) + { + if (pos >= len) + { + pos = len; + break; + } + pos += STRING_utf8_get_char_length(str[pos]); + } + + _utf8.lindex = index; + _utf8.lpos = pos; + + return pos; +} + +//----------------------------------------------------------------------------- + +static int byte_to_index(const char *str, int len, int byte) +{ + if (byte <= 0) + return 0; + + if (byte > len) + byte = len; + + return utf8_get_length(str, byte); +} + +static int index_to_byte(const char *ref, const char *str, int len, int index) +{ + if (index <= 0) + return 0; + + return utf8_get_pos(ref, str, len, index - 1) + 1; +} + + +BEGIN_METHOD(String_Pos, GB_STRING str; GB_INTEGER index) + + GB_ReturnInteger(index_to_byte(VARG(str).addr, STRING(str), LENGTH(str), VARG(index))); + +END_METHOD + + +BEGIN_METHOD(String_Len, GB_STRING str) + + GB_ReturnInteger(utf8_get_length(STRING(str), LENGTH(str))); + +END_METHOD + + +BEGIN_METHOD(String_Index, GB_STRING str; GB_INTEGER pos) + + GB_ReturnInteger(byte_to_index(STRING(str), LENGTH(str), VARG(pos))); + +END_METHOD + + +static void String_Mid(ushort code, VALUE *sp) +{ + char *str; + char *ref; + int start, length; + int len, ulen, upos; + bool null; + + SUBR_ENTER(); + + null = SUBR_check_string(PARAM); + + VALUE_conv_integer(&PARAM[1]); + start = PARAM[1]._integer.value - 1; + + if (start < 0) + THROW(E_ARG); + + if (null) + return; + + ref = PARAM->_string.addr; + str = ref + PARAM->_string.start; + len = PARAM->_string.len; + + ulen = utf8_get_pos(ref, str, len, start); + if (ulen >= len) + { + VOID_STRING(PARAM); + return; + } + + PARAM->_string.start += ulen; + //str += ulen; + //len -= ulen; + + if (NPARAM == 2) + { + ulen = len - ulen; + } + else + { + VALUE_conv_integer(&PARAM[2]); + length = PARAM[2]._integer.value; + + if (length < 0) + length += utf8_get_length(str, len) - start; + + if (length == 1) + { + ulen = STRING_utf8_get_char_length(str[ulen]); + } + else + { + upos = utf8_get_pos(ref, str, len, start + length); + if (upos > len) + upos = len; + ulen = upos - ulen; + } + } + + if (ulen <= 0) + { + VOID_STRING(PARAM); + } + else + PARAM->_string.len = ulen; +} + + +static void String_Left(ushort code, VALUE *sp) +{ + int val; + char *ref; + char *str; + int len, ulen; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + ref = PARAM->_string.addr; + str = ref + PARAM->_string.start; + len = PARAM->_string.len; + + if (val < 0) + val += utf8_get_length(str, len); + + ulen = utf8_get_pos(ref, str, len, val); + PARAM->_string.len = ulen; + } +} + + +static void String_Right(ushort code, VALUE *sp) +{ + int val; + char *str; + char *ref; + int len, ulen; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + ref = PARAM->_string.addr; + str = ref + PARAM->_string.start; + len = PARAM->_string.len; + + if (val < 0) + val = (-val); + else + val = utf8_get_length(str, len) - val; + + ulen = utf8_get_pos(ref, str, len, val); + if (ulen > len) + ulen = len; + + PARAM->_string.start += ulen; + PARAM->_string.len -= ulen; + } +} + + +bool STRING_convert_to_unicode(wchar_t **pwstr, int *pwlen, const char *str, int len) +{ + char *result; + int wlen = utf8_get_length(str, len); + int i, lc; + wchar_t *wstr; + + result = STRING_new_temp(NULL, (wlen + 1) * sizeof(wchar_t) - 1); + wstr = (wchar_t *)result; + + for (i = 0; i < wlen; i++) + { + lc = STRING_utf8_get_char_length(*str); + wstr[i] = (wchar_t)STRING_utf8_to_unicode(str, lc); + if (wstr[i] == UNICODE_INVALID) + return TRUE; + str += lc; + } + + wstr[wlen] = 0; + *pwstr = wstr; + *pwlen = wlen; + return FALSE; +} + +static bool convert_to_unicode(wchar_t **wstr, int *wlen, const char *str, int len, bool upper) +{ + wchar_t *wtemp; + int i, l; + + if (len == 0) + { + *wstr = NULL; + *wlen = 0; + return FALSE; + } + + if (STRING_convert_to_unicode(&wtemp, &l, str, len)) + return TRUE; + + if (upper) + { + for (i = 0; i < l; i++) + wtemp[i] = towupper(wtemp[i]); + } + else + { + for (i = 0; i < l; i++) + wtemp[i] = towlower(wtemp[i]); + } + + *wstr = wtemp; + *wlen = l; + return FALSE; +} + +static void convert_string(char *str, int len, bool upper) +{ + char *result; + char *p, *pe; + char c; + int lc; + wchar_t wc; + + if (len <= 0) + { + GB_ReturnVoidString(); + return; + } + + result = STRING_new_temp(str, len); + + p = result; + pe = &result[len]; + + if (upper) + { + while (p < pe) + { + c = *p; + lc = STRING_utf8_get_char_length(c); + + if (lc == 1) + { + *p = toupper(c); + p++; + } + else + { + wc = (wchar_t)STRING_utf8_to_unicode(p, lc); + wc = towupper(wc); + // We suppose that the conversion does not change the number of bytes! + STRING_utf8_from_unicode((uint)wc, p); + //if (STRING_utf8_get_char_length(*p) != lc) + // fprintf(stderr, "convert_string: not the same number of bytes!\n"); + p += lc; + } + } + } + else + { + while (p < pe) + { + c = *p; + lc = STRING_utf8_get_char_length(c); + + if (lc == 1) + { + *p = tolower(c); + p++; + } + else + { + wc = (wchar_t)STRING_utf8_to_unicode(p, lc); + wc = towlower(wc); + // We suppose that the conversion does not change the number of bytes! + STRING_utf8_from_unicode((uint)wc, p); + //if (STRING_utf8_get_char_length(*p) != lc) + // fprintf(stderr, "convert_string: not the same number of bytes!\n"); + p += lc; + } + } + } + + GB_ReturnString(result); +} + + +BEGIN_METHOD(String_Lower, GB_STRING str) + + convert_string(STRING(str), LENGTH(str), FALSE); + +END_METHOD + + +BEGIN_METHOD(String_Upper, GB_STRING str) + + convert_string(STRING(str), LENGTH(str), TRUE); + +END_METHOD + + +BEGIN_METHOD(String_UCaseFirst, GB_STRING str) + + char *str = STRING(str); + int len = LENGTH(str); + char *result; + int lc; + wchar_t wc; + + if (len <= 0) + { + GB_ReturnVoidString(); + return; + } + + result = STRING_new_temp(str, len); + + lc = STRING_utf8_get_char_length(result[0]); + + if (lc == 1) + { + result[0] = toupper(result[0]); + } + else + { + wc = (wchar_t)STRING_utf8_to_unicode(result, lc); + wc = towupper(wc); + // We suppose that the conversion does not change the number of bytes! + STRING_utf8_from_unicode((uint)wc, result); + } + + GB_ReturnString(result); + +END_METHOD + + +BEGIN_METHOD(String_Chr, GB_INTEGER code) + + char temp[8]; + + STRING_utf8_from_unicode(VARG(code), temp); + temp[STRING_utf8_get_char_length(temp[0])] = 0; + GB_ReturnNewZeroString(temp); + +END_METHOD + + +BEGIN_METHOD(String_Code, GB_STRING str; GB_INTEGER index) + + char *str; + int len, index, pos, lc; + + index = VARGOPT(index, 1); + if (index < 1) + { + GB_ReturnInteger(0); + return; + } + + str = STRING(str); + len = LENGTH(str); + + if (index > len) + { + GB_ReturnInteger(0); + return; + } + + pos = utf8_get_pos(VARG(str).addr, str, len, index - 1); + lc = STRING_utf8_get_char_length(str[pos]); + if ((lc + pos) > len) + GB_ReturnInteger(-1); + else + GB_ReturnInteger(STRING_utf8_to_unicode(&str[pos], lc)); + +END_METHOD + +static void string_search(const char *str, const char *ref, int len, const char *pattern, int lenp, int start, bool right, bool nocase) +{ + int pos; + + if (lenp == 0) + goto __ERROR; + + if (start) + start = index_to_byte(ref, str, len, start); + + if (!nocase) + { + pos = STRING_search(str, len, pattern, lenp, start, right, FALSE); + pos = byte_to_index(str, len, pos); + } + else + { + wchar_t *wstr; + int lstr; + wchar_t *wpattern; + int lpattern; + + if (convert_to_unicode(&wstr, &lstr, str, len, TRUE)) + goto __ERROR; + + if (convert_to_unicode(&wpattern, &lpattern, pattern, lenp, TRUE)) + goto __ERROR; + + if (!start) + start = right ? lstr : 1; + + pos = STRING_search((char *)wstr, lstr * sizeof(wchar_t), (char *)wpattern, lpattern * sizeof(wchar_t), 1 + (start - 1) * sizeof(wchar_t), right, FALSE); + if (pos) + pos = (pos - 1) / sizeof(wchar_t) + 1; + } + + GB_ReturnInteger(pos); + return; + +__ERROR: + + GB_ReturnInteger(0); + return; +} + +BEGIN_METHOD(String_Instr, GB_STRING str; GB_STRING pattern; GB_INTEGER start; GB_INTEGER mode) + + string_search(STRING(str), VARG(str).addr, LENGTH(str), STRING(pattern), LENGTH(pattern), VARGOPT(start, 0), FALSE, VARGOPT(mode, GB_COMP_BINARY) == GB_COMP_NOCASE); + +END_METHOD + +BEGIN_METHOD(String_RInstr, GB_STRING str; GB_STRING pattern; GB_INTEGER start; GB_INTEGER mode) + + string_search(STRING(str), VARG(str).addr, LENGTH(str), STRING(pattern), LENGTH(pattern), VARGOPT(start, 0), TRUE, VARGOPT(mode, GB_COMP_BINARY) == GB_COMP_NOCASE); + +END_METHOD + +BEGIN_METHOD(String_Comp, GB_STRING str1; GB_STRING str2; GB_INTEGER mode) + + int mode = VARGOPT(mode, GB_COMP_BINARY) | GB_COMP_LANG; + bool nocase = (mode & GB_COMP_NOCASE) != 0; + + if (mode & GB_COMP_NATURAL) + GB_ReturnInteger(COMPARE_string_natural(STRING(str1), LENGTH(str1), STRING(str2), LENGTH(str2), nocase)); + else + GB_ReturnInteger(COMPARE_string_lang(STRING(str1), LENGTH(str1), STRING(str2), LENGTH(str2), nocase, TRUE)); + +END_METHOD + +#define IS_VALID(_char) \ + ((_char) < 0x110000 && \ + (((_char) & 0xFFFFF800) != 0xD800) && \ + ((_char) < 0xFDD0 || (_char) > 0xFDEF) && \ + ((_char) & 0xFFFE) != 0xFFFE) + +BEGIN_METHOD(String_IsValid, GB_STRING str) + + const uchar *str; + int len, lc; + uint unicode; + bool valid = FALSE; + int i; + + str = (const uchar *)STRING(str); + len = LENGTH(str); + + while (len) + { + lc = STRING_utf8_get_char_length(*str); + len -= lc; + if (len < 0) + goto _INVALID; + + //for (i = 0; i < len; i++) + // fprintf(stderr, "%02X ", str[i]); + + for (i = 1; i < lc; i++) + { + if ((str[i] & 0xC0) != 0x80) + goto _INVALID; + } + + unicode = STRING_utf8_to_unicode((char *)str, lc); + if (unicode == UNICODE_INVALID) + goto _INVALID; + if (!IS_VALID(unicode)) + goto _INVALID; + + str += lc; + } + + valid = TRUE; + +_INVALID: + + GB_ReturnBoolean(valid); + //fprintf(stderr, "\n"); + +END_METHOD + +//----------------------------------------------------------------------------- + +BEGIN_PROPERTY(BoxedString_Len) + + VALUE *val = &SP[-1]; + + VARIANT_undo(val); + int len = val->_string.len; + RELEASE_STRING(val); + GB_ReturnInteger(len); + +END_PROPERTY + +void BoxedString_get(ushort code) +{ + int start; + int len; + bool null; + + code++; + + SUBR_ENTER(); + + null = SUBR_check_string(PARAM); + + VALUE_conv_integer(&PARAM[1]); + start = PARAM[1]._integer.value; + + if (start < 0) + THROW(E_ARG); + + if (null) + goto _SUBR_MID_FIN; + + if (start >= PARAM->_string.len) + { + VOID_STRING(PARAM); + goto _SUBR_MID_FIN; + } + + if (NPARAM == 2) + len = 1; + else + { + VALUE_conv_integer(&PARAM[2]); + len = PARAM[2]._integer.value; + } + + if (len < 0) + len = Max(0, PARAM->_string.len - start + len); + + len = MinMax(len, 0, PARAM->_string.len - start); + + if (len == 0) + { + RELEASE_STRING(PARAM); + PARAM->_string.addr = NULL; + PARAM->_string.start = 0; + } + else + PARAM->_string.start += start; + + PARAM->_string.len = len; + +_SUBR_MID_FIN: + + SP -= NPARAM; + SP++; +} + +#endif // GBX_INFO + +//----------------------------------------------------------------------------- + +GB_DESC StringDesc[] = +{ + GB_DECLARE_STATIC("String"), + + GB_STATIC_METHOD("Len", "i", String_Len, "(String)s"), + + GB_STATIC_FAST_METHOD("Mid", "s", String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Mid$", "s", String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Left", "s", String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Left$", "s", String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right", "s", String_Right, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right$", "s", String_Right, "(String)s[(Length)i]"), + + GB_STATIC_METHOD("Upper", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("Upper$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCaseFirst", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("UCaseFirst$", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("Lower", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("Lower$", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase$", "s", String_Lower, "(String)s"), + + GB_STATIC_METHOD("InStr", "i", String_Instr, "(String)s(Pattern)s[(From)i(Mode)i]"), + GB_STATIC_METHOD("RInStr", "i", String_RInstr, "(String)s(Pattern)s[(From)i(Mode)i]"), + + GB_STATIC_METHOD("Comp", "i", String_Comp, "(String)s(String2)s[(Mode)i]"), + + GB_STATIC_METHOD("Byte", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Pos", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Index", "i", String_Index, "(String)s(Byte)i"), + + GB_STATIC_METHOD("Chr", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Chr$", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Code", "i", String_Code, "(String)s[(Index)i]"), + + GB_STATIC_METHOD("IsValid", "b", String_IsValid, "(String)s"), + + GB_END_DECLARE +}; + +//----------------------------------------------------------------------------- + +GB_DESC BoxedStringDesc[] = +{ + GB_DECLARE_STATIC("_BoxedString"), + + GB_STATIC_PROPERTY_READ("Len", "i", BoxedString_Len), + + GB_STATIC_FAST_METHOD("_get", "s", BoxedString_get, "(Start)i[(Length)i]"), + + //GB_STATIC_FAST_METHOD("_get", "s", _String_get, "(Start)i[(Length)i]"), + + /*GB_STATIC_FAST_METHOD("Mid", "s", _String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Mid$", "s", _String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Left", "s", _String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Left$", "s", _String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right", "s", _String_Right, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right$", "s", _String_Right, "(String)s[(Length)i]"), + + GB_STATIC_METHOD("Upper", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("Upper$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCaseFirst", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("UCaseFirst$", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("Lower", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("Lower$", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase$", "s", String_Lower, "(String)s"), + + GB_STATIC_METHOD("InStr", "i", String_Instr, "(String)s(Pattern)s[(From)i(Mode)i]"), + GB_STATIC_METHOD("RInStr", "i", String_RInstr, "(String)s(Pattern)s[(From)i(Mode)i]"), + + GB_STATIC_METHOD("Comp", "i", String_Comp, "(String)s(String2)s[(Mode)i]"), + + GB_STATIC_METHOD("Byte", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Pos", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Index", "i", String_Index, "(String)s(Byte)i"), + + GB_STATIC_METHOD("Chr", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Chr$", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Code", "i", String_Code, "(String)s[(Index)i]"), + + GB_STATIC_METHOD("IsValid", "b", String_IsValid, "(String)s"),*/ + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_string.h b/main/gbx/gbx_c_string.h new file mode 100644 index 00000000..45b1cc32 --- /dev/null +++ b/main/gbx/gbx_c_string.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + gbx_c_string.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_STRING_H +#define __GBX_C_STRING_H + +#include "gambas.h" + +#ifndef __GBX_C_STRING_C + +EXTERN GB_DESC StringDesc[]; +EXTERN GB_DESC BoxedStringDesc[]; + +EXTERN const char STRING_char_length[]; + +#endif + +#define UNICODE_INVALID 0xFFFFFFFFU + +bool STRING_convert_to_unicode(wchar_t **pwstr, int *pwlen, const char *str, int len); +void STRING_utf8_from_unicode(uint code, char *sstr); +uint STRING_utf8_to_unicode(const char *sstr, int len); + +#define STRING_utf8_get_char_length(_c) ((int)STRING_char_length[(unsigned char)(_c)]) + +int COMMON_get_unicode_char(); + +void BoxedString_get(ushort code); + +#endif diff --git a/main/gbx/gbx_c_system.c b/main/gbx/gbx_c_system.c new file mode 100644 index 00000000..2177e9d7 --- /dev/null +++ b/main/gbx/gbx_c_system.c @@ -0,0 +1,460 @@ +/*************************************************************************** + + gbx_c_system.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_SYSTEM_C + +#include "gambas.h" +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "config.h" + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_system.h" +#include "gb_error.h" +#include "gb_overflow.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_date.h" +#include "gbx_project.h" +#include "gbx_local.h" +#include "gbx_event.h" +#include "gbx_string.h" +#include "gbx_exec.h" +#include "gbx_extern.h" +#include "gbx_object.h" +#include "gbx_jit.h" +#include "gbx_c_process.h" +#include "gbx_c_system.h" + +#include "gb_system_temp.h" + +typedef + struct { + const char *prefix; + int prio; + } + SYSLOG_PREFIX; + +static const SYSLOG_PREFIX _syslog_prefix[] = +{ + { "[error]", LOG_ERR }, + { "[warning]", LOG_WARNING }, + { "[notice]", LOG_NOTICE }, + { "[info]", LOG_INFO }, + { "[debug]", LOG_DEBUG }, + { NULL, 0 } +}; + + +BEGIN_PROPERTY(User_Home) + + GB_ReturnString(FILE_get_home()); + +END_PROPERTY + + +BEGIN_PROPERTY(User_Name) + + struct passwd *info = getpwuid(getuid()); + + if (info) + GB_ReturnNewZeroString(info->pw_name); + else + GB_Error((char *)E_MEMORY); + +END_PROPERTY + + +BEGIN_PROPERTY(User_Id) + + GB_ReturnInteger(getuid()); + +END_PROPERTY + + +BEGIN_PROPERTY(User_Group) + + GB_ReturnInteger(getgid()); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Language) + + if (READ_PROPERTY) + GB_ReturnNewZeroString(LOCAL_get_lang()); + else + LOCAL_set_lang(GB_ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(System_FirstDayOfWeek) + + if (READ_PROPERTY) + GB_ReturnInteger(LOCAL_get_first_day_of_week()); + else + LOCAL_set_first_day_of_week(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Charset) + + if (LOCAL_is_UTF8) + GB_ReturnConstZeroString("UTF-8"); + else + GB_ReturnString(LOCAL_encoding); + +END_PROPERTY + + +BEGIN_PROPERTY(System_RightToLeft) + + GB_ReturnBoolean(LOCAL_local.rtl); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Path) + + GB_ReturnString(PROJECT_exec_path); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Host) + + char buffer[256]; + + gethostname(buffer, 255); + GB_ReturnNewZeroString(buffer); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Domain) + + char buffer[256]; + + if (getdomainname(buffer, 255)) + GB_Error("Unable to retrieve domain name: &1", strerror(errno)); + else + GB_ReturnNewZeroString(buffer); + +END_PROPERTY + + +BEGIN_PROPERTY(System_ByteOrder) + + GB_ReturnInteger(EXEC_big_endian); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Backtrace) + + STACK_BACKTRACE *bt = STACK_get_backtrace(); + + GB_ReturnObject(DEBUG_get_string_array_from_backtrace(bt)); + STACK_free_backtrace(&bt); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Error) + + GB_ReturnInteger(errno); + +END_PROPERTY + + +BEGIN_METHOD(System_GetExternSymbol, GB_STRING library; GB_STRING name) + + char *library = GB_ToZeroString(ARG(library)); + char *name = GB_ToZeroString(ARG(name)); + void *ptr = NULL; + + if (*library && *name) + ptr = EXTERN_get_symbol(library, name); + + GB_ReturnPointer(ptr); + +END_METHOD + + +BEGIN_PROPERTY(System_Shell) + + if (READ_PROPERTY) + { + if (!CPROCESS_shell) + GB_ReturnConstZeroString("/bin/sh"); + else + GB_ReturnString(CPROCESS_shell); + } + else + GB_StoreString(PROP(GB_STRING), &CPROCESS_shell); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Profile) + + if (READ_PROPERTY) + GB_ReturnBoolean(EXEC_profile_instr); + else + EXEC_profile_instr = EXEC_profile && VPROP(GB_BOOLEAN); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Trace) + + if (READ_PROPERTY) + GB_ReturnBoolean(EXEC_trace); + else + EXEC_trace = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +BEGIN_METHOD_VOID(System_Breakpoint) + + BREAKPOINT(); + +END_METHOD + + +BEGIN_PROPERTY(System_TimeZone) + + GB_ReturnInteger(DATE_get_timezone()); + +END_PROPERTY + + +BEGIN_PROPERTY(System_BreakOnError) + + if (READ_PROPERTY) + GB_ReturnBoolean(EXEC_break_on_error); + else if (EXEC_debug) + EXEC_break_on_error = VPROP(GB_BOOLEAN); + +END_METHOD + + +BEGIN_METHOD(System_Log, GB_STRING message) + + static bool _opened = FALSE; + + const SYSLOG_PREFIX *p; + char *msg; + int len; + int lenp; + int prio; + + msg = STRING(message); + len = LENGTH(message); + prio = LOG_INFO; + + for (p = _syslog_prefix; p->prefix; p++) + { + lenp = strlen(p->prefix); + if (len >= (lenp + 2) && strncasecmp(msg, p->prefix, lenp) == 0) + { + msg += lenp + 2; + len -= lenp + 2; + prio = p->prio; + break; + } + } + + while (len > 0 && *msg == ' ') + msg++, len--; + + if (len <= 0) + return; + + if (!_opened) + { + _opened = TRUE; + openlog(PROJECT_name, LOG_PID, LOG_INFO); + } + + syslog(prio, "%.*s", len, msg); + +END_METHOD + + +BEGIN_METHOD(System_Find, GB_STRING program) + + const char *path; + + path = CPROCESS_search_program_in_path(GB_ToZeroString(ARG(program))); + if (!path) + GB_ReturnNull(); + else + GB_ReturnNewZeroString(path); + +END_METHOD + + +BEGIN_METHOD(System_Exist, GB_STRING program) + + GB_ReturnBoolean(CPROCESS_search_program_in_path(GB_ToZeroString(ARG(program))) != NULL); + +END_METHOD + + +BEGIN_METHOD(System_GetFormat, GB_INTEGER format) + + const char *fmt = LOCAL_get_format(&LOCAL_local, VARG(format)); + if (!fmt) + GB_ReturnNull(); + else + GB_ReturnNewZeroString(fmt); + +END_METHOD + + +BEGIN_PROPERTY(System_Cores) + + GB_ReturnInteger(SYSTEM_get_cpu_count()); + +END_PROPERTY + + +BEGIN_PROPERTY(System_IgnoreOverflow) + +#if DO_NOT_CHECK_OVERFLOW + + if (READ_PROPERTY) + GB_ReturnBoolean(TRUE); + else if (!VPROP(GB_BOOLEAN)) + GB_Error("Overflow detection is not supported on this system"); + +#else + + if (READ_PROPERTY) + GB_ReturnBoolean(!EXEC_check_overflow); + else + EXEC_check_overflow = !VPROP(GB_BOOLEAN); + +#endif + +END_PROPERTY + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Jit_Time) + + GB_ReturnFloat(0); + +END_PROPERTY + +BEGIN_PROPERTY(Jit_Enabled) + + if (READ_PROPERTY) + GB_ReturnBoolean(!JIT_disabled); + else + JIT_disabled = !VPROP(GB_BOOLEAN); + +END_PROPERTY + +//------------------------------------------------------------------------- + +#endif + +GB_DESC NATIVE_User[] = +{ + GB_DECLARE_STATIC("User"), + + GB_STATIC_PROPERTY_READ("Name", "s", User_Name), + GB_STATIC_PROPERTY_READ("Id", "i", User_Id), + GB_STATIC_PROPERTY_READ("Group", "i", User_Group), + GB_STATIC_PROPERTY_READ("Home", "s", User_Home), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_System[] = +{ + GB_DECLARE_STATIC("System"), + + GB_STATIC_PROPERTY_READ("Path", "s", System_Path), + GB_CONSTANT("Version", "s", GAMBAS_VERSION_STRING), + GB_CONSTANT("FullVersion", "s", VERSION), + GB_STATIC_PROPERTY_READ("Backtrace", "String[]", System_Backtrace), + GB_STATIC_PROPERTY("BreakOnError", "b", System_BreakOnError), + + GB_STATIC_PROPERTY("Language", "s", System_Language), + GB_STATIC_PROPERTY("FirstDayOfWeek", "i", System_FirstDayOfWeek), + GB_STATIC_PROPERTY("Shell", "s", System_Shell), + GB_STATIC_PROPERTY("Profile", "b", System_Profile), + GB_STATIC_PROPERTY("Trace", "b", System_Trace), + GB_STATIC_PROPERTY("IgnoreOverflow", "b", System_IgnoreOverflow), + + GB_STATIC_PROPERTY_READ("RightToLeft", "b", System_RightToLeft), + GB_STATIC_PROPERTY_READ("Charset", "s", System_Charset), + GB_STATIC_PROPERTY_READ("Host", "s", System_Host), + GB_STATIC_PROPERTY_READ("Domain", "s", System_Domain), + GB_STATIC_PROPERTY_READ("ByteOrder", "i", System_ByteOrder), + GB_STATIC_PROPERTY_READ("Error", "i", System_Error), + GB_STATIC_PROPERTY_READ("TimeZone", "i", System_TimeZone), + GB_STATIC_PROPERTY_READ("Cores", "i", System_Cores), + + GB_CONSTANT("Family", "s", SYSTEM), + GB_CONSTANT("Architecture", "s", ARCHITECTURE), + + GB_STATIC_METHOD("GetExternSymbol", "p", System_GetExternSymbol, "(Library)s(Symbol)s"), + GB_STATIC_METHOD("_Breakpoint", NULL, System_Breakpoint, NULL), + + GB_STATIC_PROPERTY_SELF("User", "User"), + + GB_STATIC_METHOD("Log", NULL, System_Log, "(Message)s"), + + GB_STATIC_METHOD("Exist", "b", System_Exist, "(Program)s"), + GB_STATIC_METHOD("Find", "s", System_Find, "(Program)s"), + + GB_STATIC_METHOD("GetFormat", "s", System_GetFormat, "(Format)i"), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Jit[] = +{ + GB_DECLARE_STATIC("Jit"), + + GB_STATIC_PROPERTY_READ("Time", "f", Jit_Time), + GB_STATIC_PROPERTY("Enabled", "b", Jit_Enabled), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_system.h b/main/gbx/gbx_c_system.h new file mode 100644 index 00000000..6682b52e --- /dev/null +++ b/main/gbx/gbx_c_system.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gbx_c_system.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_SYSTEM_H +#define __GBX_C_SYSTEM_H + +#include "gambas.h" + +#ifndef __GBX_C_SYSTEM_C +extern GB_DESC NATIVE_User[]; +extern GB_DESC NATIVE_System[]; +extern GB_DESC NATIVE_Jit[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_task.c b/main/gbx/gbx_c_task.c new file mode 100644 index 00000000..b329c51b --- /dev/null +++ b/main/gbx/gbx_c_task.c @@ -0,0 +1,768 @@ +/*************************************************************************** + + gbx_c_task.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_TASK_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_list.h" +#include "gb_file.h" +#include "gb_error.h" +#include "gbx_api.h" +#include "gbx_exec.h" +#include "gbx_string.h" +#include "gbx_signal.h" +#include "gbx_event.h" +#include "gbx_component.h" +#include "gbx_watch.h" + +#include "gbx_c_file.h" +#include "gbx_c_task.h" + +//#define DEBUG_ME 1 +#ifdef __CYGWIN__ +#define FIONREAD TIOCINQ +#endif + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Error); +DECLARE_EVENT(EVENT_Kill); + +enum { + CHILD_OK = 0, + CHILD_ERROR = 1, + CHILD_STDOUT = 2, + CHILD_STDERR = 3, + CHILD_RETURN = 4 +}; + +static SIGNAL_CALLBACK *_signal_handler = NULL; +static CTASK *_task_list = NULL; +static int _task_count = 0; + +//------------------------------------------------------------------------- + +static void cleanup_task(CTASK *_object); +static void stop_task(CTASK *_object); +static void close_fd(int *pfd); + +static void has_forked(void) +{ + CTASK *task; + pid_t pid = getpid(); + STREAM *stream; + + FILE_init(); + EXEC_debug = FALSE; + EXEC_task = TRUE; + if (EXEC_profile) + DEBUG.Profile.Cancel(); + EXEC_profile = FALSE; + EXEC_profile_instr = FALSE; + + EXEC_Hook.loop = NULL; + EXEC_Hook.wait = NULL; + EXEC_Hook.timer = NULL; + EXEC_Hook.watch = NULL; + EXEC_Hook.post = NULL; + //EXEC_Hook.quit = NULL; + + stream = CSTREAM_TO_STREAM(CFILE_get_standard_stream(CFILE_OUT)); + + stream->common.eol = 0; + STREAM_blocking(stream, TRUE); + + SIGNAL_has_forked(); + + task = _task_list; + while (task) + { + if (task->pid != pid) + { + close_fd(&task->fd_out); + close_fd(&task->fd_err); + } + + task = task->list.next; + } +} + +static void callback_child(int signum, intptr_t data) +{ + CTASK *_object, *next; + int status; + + #if DEBUG_ME + fprintf(stderr, ">> callback_child\n"); + #endif + + _object = _task_list; + while (_object) + { + next = THIS->list.next; + if (wait4(THIS->pid, &status, WNOHANG, NULL) == THIS->pid) + { + THIS->status = status; + stop_task(THIS); + } + _object = next; + } + + #if DEBUG_ME + fprintf(stderr, "<< callback_child\n"); + #endif +} + +void CTASK_callback_child(void) +{ + callback_child(SIGCHLD, 0); +} + +static int get_readable(int fd) +{ + int len; + + if (ioctl(fd, FIONREAD, &len) < 0 || len <= 0) + return 0; + else + return len; +} + +static bool callback_write(int fd, int type, CTASK *_object) +{ + int len; + char *data; + char *p; + int n; + + //fprintf(stderr, "callback_write: %d %p\n", fd, THIS); + + len = get_readable(fd); + if (len == 0) + return TRUE; + + data = STRING_new(NULL, len); + p = data; + + while (len > 0) + { + n = read(fd, p, len); + if (n < 0) + { + if (errno == EINTR) + continue; + else + break; + } + len -= n; + p += n; + } + + GB_Raise(THIS, EVENT_Read, 1, GB_T_STRING, data, STRING_length(data) - len); + + STRING_free(&data); + return FALSE; +} + + +static int callback_error(int fd, int type, CTASK *_object) +{ + char buffer[256]; + int n; + + //fprintf(stderr, "callback_error: %d %p\n", fd, THIS); + + n = read(fd, buffer, sizeof(buffer)); + + if (n <= 0) + return TRUE; + + GB_Raise(THIS, EVENT_Error, 1, GB_T_STRING, buffer, n); + return FALSE; +} + +static bool create_return_directory(void) +{ + static bool mkdir_done = FALSE; + + char buf[PATH_MAX]; + + if (mkdir_done) + return FALSE; + + sprintf(buf, RETURN_DIR_PATTERN, getuid(), getpid()); + + if (mkdir(buf, S_IRWXU) != 0) + { + GB_Error("Cannot create task return directory"); + return TRUE; + } + + mkdir_done = TRUE; + return FALSE; +} + +static void init_task(void) +{ + _task_count++; + + if (_task_count <= 1) + _signal_handler = SIGNAL_register(SIGCHLD, callback_child, 0); + + SIGNAL_check(SIGCHLD); +} + +static void exit_task(void) +{ + _task_count--; + + if (_task_count > 0) + return; + + SIGNAL_unregister(SIGCHLD, _signal_handler); + _signal_handler = NULL; +} + +static void prepare_task(CTASK *_object) +{ + THIS->fd_out = -1; + THIS->fd_err = -1; +} + +static void exit_child(int ret) +{ + FILE_exit(); + _exit(ret); +} + +static bool start_task(CTASK *_object) +{ + const char *err = NULL; + pid_t pid; + sigset_t sig, old; + GB_FUNCTION func; + int fd_out[2], fd_err[2]; + bool has_read, has_error; + GB_VALUE *ret; + char buf[PATH_MAX]; + FILE *f; + + if (EXEC_task) + return TRUE; + + if (THIS->stopped) + { + cleanup_task(THIS); + return TRUE; + } + + init_task(); + + LIST_insert(&_task_list, THIS, &THIS->list); + + // Create pipes + + has_read = GB_CanRaise(THIS, EVENT_Read); + has_error = GB_CanRaise(THIS, EVENT_Error); + + if (has_read && pipe(fd_out) != 0) + goto __ERROR; + + if (has_error && pipe(fd_err) != 0) + goto __ERROR; + + COMPONENT_before_fork(); + + // Block SIGCHLD + + sigemptyset(&sig); + + sigaddset(&sig, SIGCHLD); + sigprocmask(SIG_BLOCK, &sig, &old); + + pid = fork(); + if (pid == (-1)) + { + stop_task(THIS); + sigprocmask(SIG_SETMASK, &old, NULL); + goto __ERROR; + } + + if (pid) + { + #if DEBUG_ME + fprintf(stderr, "start_task: %p %d\n", THIS, pid); + #endif + THIS->pid = pid; + + if (has_read) + { + close(fd_out[1]); + THIS->fd_out = fd_out[0]; + + GB_Watch(THIS->fd_out, GB_WATCH_READ, (void *)callback_write, (intptr_t)THIS); + } + + if (has_error) + { + close(fd_err[1]); + THIS->fd_err = fd_err[0]; + + fcntl(THIS->fd_err, F_SETFL, fcntl(THIS->fd_err, F_GETFL) | O_NONBLOCK); + GB_Watch(THIS->fd_err, GB_WATCH_READ, (void *)callback_error, (intptr_t)THIS); + } + + sigprocmask(SIG_SETMASK, &old, NULL); + } + else // child task + { + THIS->child = TRUE; + THIS->pid = getpid(); + + sigprocmask(SIG_SETMASK, &old, NULL); + + if (has_read) + { + close(fd_out[0]); + + if (dup2(fd_out[1], STDOUT_FILENO) == -1) + exit_child(CHILD_STDOUT); + + setlinebuf(stdout); + } + /*else + close(CHILD_STDOUT);*/ + + if (has_error) + { + close(fd_err[0]); + + if (dup2(fd_err[1], STDERR_FILENO) == -1) + exit_child(CHILD_STDERR); + + setlinebuf(stderr); + } + /*else + close(CHILD_STDERR);*/ + + has_forked(); // After the redirection + + GB_GetFunction(&func, THIS, "Main", "", NULL); + + TRY + { + ret = GB_Call(&func, 0, FALSE); + if (ret->type != GB_T_VOID) + { + sprintf(buf, RETURN_FILE_PATTERN, getuid(), getppid(), getpid()); + #if DEBUG_ME + fprintf(stderr, "serialize to: %s\n", buf); + #endif + GB_ReturnConvVariant(); + if (GB_Serialize(buf, ret)) + { + #if DEBUG_ME + fprintf(stderr, "gb.task: serialization has failed\n"); + #endif + exit_child(CHILD_RETURN); + } + } + } + CATCH + { + if (ERROR_current->info.code && ERROR_current->info.code != E_ABORT) + { + sprintf(buf, RETURN_FILE_PATTERN, getuid(), getppid(), getpid()); + f = fopen(buf, "w+"); + if (f) + { + ERROR_print_at(f, FALSE, FALSE); + fclose(f); + } + + exit_child(CHILD_ERROR); + } + } + END_TRY + + exit_child(CHILD_OK); + } + + return FALSE; + +__ERROR: + + // TODO: as the routine is posted, nobody will see the error! + if (!err) + err = strerror(errno); + fprintf(stderr, "gb.task: cannot run task: %s\n", err); + GB_Error("Cannot run task: &1", err); + + return TRUE; +} + +static void close_fd(int *pfd) +{ + int fd = *pfd; + + if (fd >= 0) + { + GB_Watch(fd, GB_WATCH_NONE, NULL, 0); + close(fd); + *pfd = -1; + } +} + +static bool get_return_value(CTASK *_object, bool cleanup) +{ + char path[PATH_MAX]; + GB_VALUE value; + bool fail = FALSE; + struct stat info; + char *err = NULL; + int fd; + int n; + + sprintf(path, RETURN_FILE_PATTERN, getuid(), getpid(), THIS->pid); + + if (!cleanup) + { + switch (THIS->status) + { + case CHILD_OK: + + #if DEBUG_ME + fprintf(stderr,"unserialize from: %s\n", path); + #endif + if (!THIS->got_value) + { + if (stat(path, &info)) + { + GB_Error((char *)E_NRETURN); + return TRUE; + } + + fail = GB_UnSerialize(path, &value); + if (!fail) + GB_StoreVariant(&value._variant, &THIS->ret); + THIS->got_value = TRUE; + } + break; + + case CHILD_ERROR: + + if (stat(path, &info)) + { + fail = TRUE; + } + else + { + err = STRING_new_temp(NULL, info.st_size); + + fd = open(path, O_RDONLY); + if (fd < 0) + { + fail = TRUE; + break; + } + else + { + for(;;) + { + n = read(fd, err, info.st_size); + if (n == info.st_size || errno != EINTR) + break; + } + close(fd); + + if (n == info.st_size) + GB_Error("Task has failed: &1", err); + else + fail = TRUE; + } + } + + if (fail) + GB_Error("Unable to get task error"); + + break; + } + } + + unlink(path); + + return fail; +} + +static void cleanup_task(CTASK *_object) +{ + //printf("cleanup task %p\n", THIS); fflush(stdout); + + OBJECT_UNREF(_object); +} + +static void stop_task(CTASK *_object) +{ + int len; + GB_RAISE_HANDLER handler; + + #if DEBUG_ME + fprintf(stderr, "stop_task: %p %d\n", THIS, THIS->pid); + #endif + + THIS->stopped = TRUE; + + // Remove task temporary files + FILE_remove_temp_file_pid(THIS->pid); + + // Flush standard error + if (THIS->fd_err >= 0) + while (callback_error(THIS->fd_err, 0, THIS) == 0); + + // Flush standard output + if (THIS->fd_out >= 0) + { + for(;;) + { + len = get_readable(THIS->fd_out); + if (len <= 0) + break; + + if (callback_write(THIS->fd_out, 0, THIS)) + break; + } + } + + close_fd(&THIS->fd_out); + close_fd(&THIS->fd_err); + + LIST_remove(&_task_list, THIS, &THIS->list); + exit_task(); + + //printf("Kill event...\n"); fflush(stdout); + + if (GB_CanRaise(THIS, EVENT_Kill)) + { + handler.callback = (GB_CALLBACK)cleanup_task; + handler.data = (intptr_t)THIS; + + GB_RaiseBegin(&handler); + GB_Raise(THIS, EVENT_Kill, 0); + GB_RaiseEnd(&handler); + } + + cleanup_task(THIS); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Task_new) + + GB_FUNCTION func; + + THIS->ret.type = GB_T_NULL; + + if (EXEC_task) + { + GB_Error("A task cannot create other tasks"); + return; + } + + if (create_return_directory()) + return; + + if (GB_GetFunction(&func, THIS, "Main", "", NULL)) + return; + + if (_task_count > MAX_TASK) + { + GB_Error("Too many tasks"); + return; + } + + prepare_task(THIS); + + OBJECT_REF(THIS); + EVENT_post((GB_CALLBACK)start_task, (intptr_t)THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Task_free) + + get_return_value(THIS, TRUE); + GB_StoreVariant(NULL, &THIS->ret); + +END_METHOD + +BEGIN_PROPERTY(Task_Handle) + + GB_ReturnInteger(THIS->pid); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Task_Stop) + + if (THIS->pid > 0) + kill(THIS->pid, SIGKILL); + else + THIS->stopped = TRUE; + +END_METHOD + +static void error_Task_Wait(CTASK *task) +{ + OBJECT_UNREF(task); +} + +BEGIN_METHOD(Task_Wait, GB_FLOAT timeout) + + int ret; + int sigfd; + int timeout; + + timeout = (int)(VARGOPT(timeout, 0.0) * 1000); + + OBJECT_REF(THIS); + + //fprintf(stderr, "Task_Wait: %p\n", THIS); + + ON_ERROR_1(error_Task_Wait, THIS) + { + GB_Wait(-1); + + while (!THIS->stopped) + { + sigfd = SIGNAL_get_fd(); + SIGNAL_check(SIGCHLD); + ret = WATCH_process(sigfd, THIS->fd_out, THIS->fd_err, timeout); + + if (ret & WP_OUTPUT) + callback_write(THIS->fd_out, GB_WATCH_READ, THIS); + + if (ret & WP_ERROR) + callback_error(THIS->fd_err, GB_WATCH_READ, THIS); + + if (ret & WP_END) + SIGNAL_raise_callbacks(sigfd, GB_WATCH_READ, 0); + + if (ret & WP_TIMEOUT) + break; + + if (ret == 0) + usleep(1000); + } + } + END_ERROR + + OBJECT_UNREF(_object); + +END_METHOD + +BEGIN_PROPERTY(Task_Value) + + if (!THIS->child && THIS->stopped) + { + if (WIFEXITED(THIS->status)) + { + switch (WEXITSTATUS(THIS->status)) + { + case CHILD_OK: + + if (get_return_value(THIS, FALSE)) + break; + GB_ReturnVariant(&THIS->ret); + return; + + case CHILD_STDOUT: + + GB_Error("Unable to redirect task standard output"); + return; + + case CHILD_STDERR: + + GB_Error("Unable to redirect task error output"); + return; + + case CHILD_RETURN: + + GB_Error("Unable to serialize task return value"); + return; + + case CHILD_ERROR: + + get_return_value(THIS, FALSE); + break; + } + } + else if (WIFSIGNALED(THIS->status)) + { + GB_Error("Task has aborted: &1", strsignal(WTERMSIG(THIS->status))); + return; + } + } + + GB_ReturnNull(); + GB_ReturnConvVariant(); + +END_PROPERTY + +BEGIN_PROPERTY(Task_Running) + + GB_ReturnBoolean(!THIS->stopped); + +END_PROPERTY + +//------------------------------------------------------------------------- + +#endif + +GB_DESC TaskDesc[] = +{ + GB_DECLARE("Task", sizeof(CTASK)), GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, Task_new, NULL), + GB_METHOD("_free", NULL, Task_free, NULL), + + GB_PROPERTY_READ("Handle", "i", Task_Handle), + GB_PROPERTY_READ("Value", "v", Task_Value), + GB_PROPERTY_READ("Running", "b", Task_Running), + + GB_METHOD("Stop", NULL, Task_Stop, NULL), + GB_METHOD("Kill", NULL, Task_Stop, NULL), + GB_METHOD("Wait", NULL, Task_Wait, "[(Timeout)f]"), + + GB_EVENT("Read", NULL, "(Data)s", &EVENT_Read), + GB_EVENT("Error", NULL, "(Data)s", &EVENT_Error), + GB_EVENT("Kill", NULL, NULL, &EVENT_Kill), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_task.h b/main/gbx/gbx_c_task.h new file mode 100644 index 00000000..81e25a1c --- /dev/null +++ b/main/gbx/gbx_c_task.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + gbx_c_task.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_TASK_H +#define __GBX_C_TASK_H + +#include +#include + +#include "gambas.h" +#include "gb_file.h" +#include "gb_list.h" + +#ifndef __GBX_C_TASK_C +extern GB_DESC TaskDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + LIST list; + GB_VARIANT_VALUE ret; + pid_t pid; + int fd_out; + int fd_err; + int status; + volatile sig_atomic_t stopped; + unsigned child : 1; + unsigned got_value : 1; + } + CTASK; + +#define THIS ((CTASK *)_object) + +#define RETURN_DIR_PATTERN FILE_TEMP_DIR "/task" +#define RETURN_FILE_PATTERN FILE_TEMP_DIR "/task/%d" + +#define MAX_TASK 256 + +void CTASK_callback_child(void); + +#endif diff --git a/main/gbx/gbx_c_timer.c b/main/gbx/gbx_c_timer.c new file mode 100644 index 00000000..f0abe075 --- /dev/null +++ b/main/gbx/gbx_c_timer.c @@ -0,0 +1,262 @@ +/*************************************************************************** + + gbx_c_timer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_TIMER_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gbx_watch.h" +#include "gbx_api.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_c_timer.h" + +#define EXT(_ob) ((CTIMER_EXT *)((CTIMER *)_ob)->ext) + +int CTIMER_active_count = 0; + + +DECLARE_EVENT(EVENT_Timer); + + +static void enable_timer(CTIMER *_object, bool on) +{ + if (on == (THIS->id != 0)) + return; + + HOOK_DEFAULT(timer, WATCH_timer)((GB_TIMER *)THIS, on); + + if (on && (THIS->id == 0)) + GB_Error("Too many active timers"); + + if (!THIS->ignore) + { + CTIMER_active_count += on ? 1 : -1; + /*if (!on) + fprintf(stderr, "disable_timer: %d -> %d\n", THIS->delay, CTIMER_active_count);*/ + } +} + + +CTIMER *CTIMER_every(int delay, GB_TIMER_CALLBACK callback, intptr_t param) +{ + CTIMER *timer; + + timer = OBJECT_create(CLASS_Timer, NULL, NULL, 0); + OBJECT_REF(timer); + + timer->delay = delay; + timer->task = EXEC_task; + timer->ignore = TRUE; + + ALLOC_ZERO(&timer->ext, sizeof(CTIMER_EXT)); + EXT(timer)->callback = callback; + EXT(timer)->tag = param; + + enable_timer(timer, TRUE); + + return timer; +} + + +void CTIMER_raise(void *_object) +{ + if (THIS->task == EXEC_task) + { + if (THIS_EXT && THIS_EXT->callback) + { + if (!(*(THIS_EXT->callback))(THIS_EXT->tag)) + return; + } + else + { + void *parent = OBJECT_parent(THIS); + if (parent && OBJECT_is_valid(parent) && !GB_Raise(THIS, EVENT_Timer, 0)) + return; + } + } + + enable_timer(THIS, FALSE); +} + + +BEGIN_METHOD(Timer_new, GB_INTEGER delay) + + int delay; + THIS->id = 0; + + delay = VARGOPT(delay, -1); + if (delay < 0) + delay = 1000; + + THIS->task = EXEC_task; + THIS->delay = delay; + if (!MISSING(delay)) + enable_timer(THIS, TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_free) + + enable_timer(THIS, FALSE); + + if (THIS->ext) + IFREE(THIS->ext); + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_Start) + + enable_timer(THIS, TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_Stop) + + enable_timer(THIS, FALSE); + THIS->triggered = FALSE; + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_Restart) + + enable_timer(THIS, FALSE); + enable_timer(THIS, TRUE); + +END_METHOD + + +BEGIN_PROPERTY(Timer_Enabled) + + if (READ_PROPERTY) + GB_ReturnBoolean(THIS->id != 0); + else + enable_timer(THIS, !!VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Timer_Delay) + + if (READ_PROPERTY) + GB_ReturnInteger(THIS->delay); + else + { + int delay = VPROP(GB_INTEGER); + bool enabled = THIS->id != 0; + + if (delay < 0 || delay >= (1<<30)) + { + GB_Error(GB_ERR_ARG); + return; + } + + if (enabled) + HOOK_DEFAULT(timer, WATCH_timer)((GB_TIMER *)THIS, FALSE); + + THIS->delay = delay; + + if (enabled) + HOOK_DEFAULT(timer, WATCH_timer)((GB_TIMER *)THIS, TRUE); + } + +END_PROPERTY + +static void trigger_timer(void *_object) +{ + if (THIS->triggered) + { + THIS->triggered = FALSE; + GB_Raise(THIS, EVENT_Timer, 0); + } + + OBJECT_UNREF(_object); +} + +BEGIN_METHOD_VOID(Timer_Trigger) + + if (THIS->triggered) + return; + + THIS->triggered = TRUE; + OBJECT_REF(THIS); + EVENT_post(trigger_timer, (intptr_t)THIS); + +END_METHOD + +BEGIN_PROPERTY(Timer_Ignore) + + if (READ_PROPERTY) + GB_ReturnBoolean(THIS->ignore); + else + { + bool v = VPROP(GB_BOOLEAN); + + if (THIS->ignore == v) + return; + + THIS->ignore = v; + if (THIS->id) + CTIMER_active_count += THIS->ignore ? -1 : 1; + } + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Timer[] = +{ + GB_DECLARE("Timer", sizeof(CTIMER)), + + GB_METHOD("_new", NULL, Timer_new, "[(Delay)i]"), + GB_METHOD("_free", NULL, Timer_free, NULL), + + GB_PROPERTY("Enabled", "b", Timer_Enabled), + GB_PROPERTY("Delay", "i", Timer_Delay), + GB_PROPERTY("Ignore", "b", Timer_Ignore), + //GB_PROPERTY_READ("Timeout", "f", Timer_Timeout), + + GB_METHOD("Start", NULL, Timer_Start, NULL), + GB_METHOD("Stop", NULL, Timer_Stop, NULL), + GB_METHOD("Restart", NULL, Timer_Restart, NULL), + GB_METHOD("Trigger", NULL, Timer_Trigger, NULL), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Special"), + GB_CONSTANT("_Properties", "s", "Enabled,Delay{Range:0;86400000;10;ms}=1000,Ignore"), + GB_CONSTANT("_DefaultEvent", "s", "Timer"), + + GB_EVENT("Timer", NULL, NULL, &EVENT_Timer), + + GB_END_DECLARE +}; + + diff --git a/main/gbx/gbx_c_timer.h b/main/gbx/gbx_c_timer.h new file mode 100644 index 00000000..e140b46f --- /dev/null +++ b/main/gbx/gbx_c_timer.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + gbx_c_timer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_TIMER_H +#define __GBX_C_TIMER_H + +#include "gambas.h" +#include "gbx_variant.h" +#include "gb_list.h" + +typedef + struct { + GB_TIMER_CALLBACK callback; + intptr_t tag; + } + CTIMER_EXT; + +typedef GB_TIMER CTIMER; + +#ifndef __GBX_C_TIMER_C + +extern GB_DESC NATIVE_Timer[]; + +extern int CTIMER_active_count; + +#else + +#define THIS ((CTIMER *)_object) +#define THIS_EXT ((CTIMER_EXT *)THIS->ext) + +#endif + +void CTIMER_raise(void *_object); +CTIMER *CTIMER_every(int delay, GB_TIMER_CALLBACK callback, intptr_t param); + +#endif diff --git a/main/gbx/gbx_class.c b/main/gbx/gbx_class.c new file mode 100644 index 00000000..2e50d8ae --- /dev/null +++ b/main/gbx/gbx_class.c @@ -0,0 +1,1648 @@ +/*************************************************************************** + + gbx_class.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include + +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_type.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gb_magic.h" +#include "gbx_stream.h" +#include "gbx_struct.h" +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_number.h" +#include "gbx_c_array.h" +#include "gbx_jit.h" + +#include "gambas.h" + +#include "gbx_class.h" + +//#define DEBUG_COMP 1 +//#define DEBUG_LOAD 1 +//#define DEBUG_DESC 1 + +// We are exiting... +bool CLASS_exiting = FALSE; + +// Global class table +static TABLE _global_table; +// List of all classes +static CLASS *_classes = NULL; +// First created class (must be 'Class' class) +static CLASS *_first = NULL; +// If set, do not create a new class, but register that class in the global table +static CLASS *_global_class = NULL; + + +#ifdef DEBUG +static CLASS *Class; +#endif + + +void CLASS_init(void) +{ + TABLE_create_static(&_global_table, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE); + + CLASS_init_native(); +} + + +TABLE *CLASS_get_table(void) +{ + return &_global_table; +} + +CLASS *CLASS_get_list(void) +{ + return _classes; +} + +static void exit_class(CLASS *class, bool native) +{ + //CLASS_DESC *desc; + + /*if (!class->ready && !CLASS_is_virtual(class)) + printf("WARNING: class %s was never loaded.\n", class->name);*/ + + #if DEBUG_LOAD + fprintf(stderr, "Exiting class %s...\n", class->name); + #endif + + /* Est-ce que _exit marche pour les classes gambas ? */ + + if (CLASS_is_native(class) != native) + return; + + if (!CLASS_is_loaded(class)) + return; + + EXEC_public(class, NULL, "_exit", 0); +} + +static void unload_class(CLASS *class) +{ + #if DEBUG_LOAD + fprintf(stderr, "Unloading class %s...\n", class->name); + #endif + + if (class->is_struct) + { + FREE(&class->load->dyn); + if (class->debug) + FREE(&class->load->global); + FREE(&class->load); + FREE(&class->data); + } + else if (!CLASS_is_native(class)) + { + #ifdef OS_64BITS + + if (class->load) + { + FREE(&class->load->desc); + FREE(&class->load->cst); + FREE(&class->load->class_ref); + FREE(&class->load->unknown); + FREE(&class->load->event); + FREE(&class->load->ext); + FREE(&class->load->local); + FREE(&class->load->array); + + if (class->debug) + { + int i; + FUNCTION *func; + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + FREE(&func->debug->local); + } + + FREE(&class->load->global); + FREE(&class->load->debug); + } + + FREE(&class->load->func); + } + + #endif + + if (class->load) + FREE(&class->load->prof); + + FREE(&class->load); + //if (!class->mmapped) + FREE(&class->data); + } + else + { + FREE(&class->signature); + FREE(&class->data); + } + + if (class->free_event) + FREE(&class->event); + + FREE(&class->stat); + + FREE(&class->table); + FREE(&class->sort); + + class->ready = FALSE; + class->loaded = FALSE; +} + +#define SWAP_FIELD(_v, _s, _d, _f) (_v = _s->_f, _s->_f = _d->_f, _d->_f = _v) + +static CLASS *class_keep_global(CLASS *class) +{ + CLASS_DESC_SYMBOL *cds; + CLASS *parent; + const char *name; + char *old_name; + CLASS *old_class; + int len; + CLASS swap; + char *swap_name; + //bool swap_free_name; + int nprefix; + int i; + + name = class->name; + len = strlen(name); + + parent = class->parent; + if (parent) + { + for (nprefix = 0;; nprefix++) + { + if (parent->name[nprefix] != '^') + break; + } + nprefix++; + } + else + nprefix = 1; + + ALLOC(&old_name, len + nprefix + 1); + for (i = 0; i < nprefix; i++) + old_name[i] = '^'; + strcpy(&old_name[i], name); + + old_class = CLASS_find_global(old_name); + + FREE(&old_name); + + swap = *class; + *class = *old_class; + *old_class = swap; + + SWAP_FIELD(swap_name, class, old_class, name); + //SWAP_FIELD(swap_free_name, class, old_class, free_name); + SWAP_FIELD(parent, class, old_class, next); + SWAP_FIELD(i, class, old_class, count); + SWAP_FIELD(i, class, old_class, ref); + + for (i = 0; i < old_class->n_desc; i++) + { + cds = &old_class->table[i]; + if (cds->desc && cds->desc->method.class == class) + cds->desc->method.class = old_class; + } + + CLASS_inheritance(class, old_class); + return class; +} + +CLASS *class_replace_global(CLASS *class) +{ + char *old_name; + CLASS *new_class; + char *swap_name; + //bool swap_free_name; + int index; + CLASS_SYMBOL *csym; + const char *name = class->name; + int len = strlen(class->name); + + if (!CLASS_is_loaded(class)) + return class; + + if (class->count == 0) + return class_keep_global(class); + + //fprintf(stderr, "class_replace_global: %s already has %d objects!\n", class->name, class->count); + + ALLOC(&old_name, len + 2); + old_name[0] = '^'; + strcpy(&old_name[1], name); + + new_class = CLASS_find_global(old_name); + + if (TABLE_find_symbol(&_global_table, name, len, &index)) + { + csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index); + csym->class = new_class; + } + + if (TABLE_find_symbol(&_global_table, old_name, len + 1, &index)) + { + csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index); + csym->class = class; + } + + FREE(&old_name); + + SWAP_FIELD(swap_name, class, new_class, name); + //SWAP_FIELD(swap_free_name, class, new_class, free_name); + + CLASS_inheritance(new_class, class); + + CLASS_update_global(class, new_class); + + //fprintf(stderr, "class_replace_global: %s -> %s\n", class->name, new_class->name); + + return new_class; +} + +static void release_class(CLASS *class) +{ + #if DEBUG_LOAD + fprintf(stderr, "release_class: %s\n", class->name); + #endif + OBJECT_release(class, NULL); + class->exit = TRUE; +} + +void CLASS_clean_up(bool silent) +{ + int n, nc, nb; + //CLASS_SYMBOL *csym; + CLASS *class; + + #if DEBUG_LOAD + fprintf(stderr, "\n------------------- CLASS_clean_up -------------------\n\n"); + #endif + + #if DEBUG_LOAD + fprintf(stderr, "Freeing auto-creatable objects...\n"); + #endif + + CLASS_exiting = TRUE; + + // Free automatic instances + + for (class = _classes; class; class = class->next) + { + if (class->instance) + { + #if DEBUG_LOAD + fprintf(stderr, "Freeing instance of %p %s\n", class, class->name); + #endif + OBJECT_UNREF(class->instance); + } + } + + // Count how many classes should be freed + + nc = 0; + + for (class = _classes; class; class = class->next) + { + if (!CLASS_is_native(class) && CLASS_is_loaded(class)) + { + #if DEBUG_LOAD + fprintf(stderr, "Must free: %p (%s) %s [%d]\n", class, class->component ? class->component->name : "-", class->name, class->count); + #endif + nc++; + } + } + + #if DEBUG_LOAD + fprintf(stderr, "Must free %d classes...\n", nc); + #endif + + #if DEBUG_LOAD + fprintf(stderr, "Calling _exit on loaded classes...\n"); + #endif + + for (class = _classes; class; class = class->next) + exit_class(class, FALSE); + + #if DEBUG_LOAD + fprintf(stderr, "Freeing classes with no instance...\n"); + #endif + + // Free classes having no instance + + n = 0; + for(;;) + { + nb = n; + + for (class = _classes; class; class = class->next) + { + if (class->count == 0 && !CLASS_is_native(class) && CLASS_is_loaded(class) && !class->exit) + { + release_class(class); + n++; + } + } + + if (n == nb) // nothing could be done + break; + } + + #if DEBUG_LOAD + fprintf(stderr, "Freeing other classes...\n"); + #endif + + // Remaining objects are circular references + // Everything is forced to be freed + + if (n < nc) + { + if (!silent) + ERROR_warning("circular references detected:"); + + for(;;) + { + nb = n; + + for (class = _classes; class; class = class->next) + { + if (!CLASS_is_native(class) && CLASS_is_loaded(class) && !class->exit && (!class->array_class || class->array_class->count == 0)) // && !class->astruct_class) + { + if (!silent) + fprintf(stderr, "gbx" GAMBAS_VERSION_STRING ": % 5d %s\n", class->count, class->name); + release_class(class); + n++; + } + } + + if (n == nb) // nothing could be done + break; + } + } + + + #if DEBUG_LOAD + fprintf(stderr, "Calling _exit on native classes...\n"); + #endif + + for (class = _classes; class; class = class->next) + exit_class(class, TRUE); +} + + +void CLASS_exit() +{ + CLASS *class, *next; + int i; + + #if DEBUG_LOAD + fprintf(stderr, "Unloading classes...\n"); + #endif + + for (class = _classes; class; class = class->next) + unload_class(class); + + #if DEBUG_LOAD + fprintf(stderr, "Destroying classes...\n"); + #endif + + class = _classes; + while (class) + { + next = class->next; + FREE(&class); + class = next; + } + + for (i = 0; i < TABLE_count(&_global_table); i++) + IFREE(TABLE_get_symbol(&_global_table, i)->name); + + TABLE_delete_static(&_global_table); +} + + +CLASS *CLASS_look_do(const char *name, int len, bool global) +{ + CLASS_SYMBOL *csym; + ARCHIVE *arch = NULL; + int index; + + #if DEBUG_COMP + fprintf(stderr, "CLASS_look: %s in %s", name, global ? "global" : "local"); + #endif + + //if (CP && CP->component && CP->component->archive) + if (!global && !ARCHIVE_get_current(&arch)) + { + if (TABLE_find_symbol(arch->classes, name, len, &index)) + { + #if DEBUG_COMP + fprintf(stderr, " -> in %s\n", arch->name ? arch->name : "main"); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(arch->classes, index); + return csym->class; + } + } + + if (TABLE_find_symbol(&_global_table, name, len, &index)) + { + #if DEBUG_COMP + fprintf(stderr, " -> %d in global\n", index); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index); + return csym->class; + } + else + { + #if DEBUG_COMP + fprintf(stderr, " -> ?\n"); + #endif + return NULL; + } +} + +CLASS *CLASS_find_do(const char *name, bool global) +{ + CLASS_SYMBOL *csym; + CLASS *class; + int index; + int len; + ARCHIVE *arch = NULL; + + if (name == NULL) + name = COMMON_buffer; + + len = strlen(name); + +#if DEBUG_LOAD + fprintf(stderr, "CLASS_find: %s (%d)\n", name, global); +#endif + + class = CLASS_look(name, len); + if (class) + return class; + + //if (CP && CP->component && CP->component->archive) + if (!global && !ARCHIVE_get_current(&arch)) + { + index = TABLE_add_symbol(arch->classes, name, len); + #if DEBUG_LOAD || DEBUG_COMP + fprintf(stderr, "Not found -> creating new one in %s\n", arch->name ? arch->name : "main"); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(arch->classes, index); + } + else + { + global = TRUE; + index = TABLE_add_symbol(&_global_table, name, len); + #if DEBUG_LOAD || DEBUG_COMP + fprintf(stderr, "Not found -> creating new one in global\n"); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index); + } + + if (_global_class) + class = _global_class; + else + { + ALLOC_ZERO(&class, sizeof(CLASS)); + class->next = _classes; + _classes = class; + } + + class->ref++; + csym->class = class; + + ALLOC(&csym->sym.name, len + 1); + strcpy(csym->sym.name, name); + + if (!class->name) + class->name = csym->sym.name; + + // The first class must be the Class class! + if (_first == NULL) + _first = class; + class->class = _first; + + class->global = global; + + if (arch) + class->component = arch->current_component; + + return class; +} + + +int CLASS_count(void) +{ + return TABLE_count(&_global_table); +} + + +CLASS *CLASS_get(const char *name) +{ + CLASS *class = CLASS_find(name); + + if (!CLASS_is_loaded(class) && !class->in_load) + CLASS_load(class); + + return class; +} + + +CLASS *CLASS_find_export(const char *name, const char *global) +{ + CLASS *class; + + if (name) + { + class = CLASS_find(name); + _global_class = class; + CLASS_find_global(global); + _global_class = NULL; + } + else + class = CLASS_find_global(global); + + return class; +} + +bool CLASS_inherits(CLASS *class, CLASS *parent) +{ + for(;;) + { + class = class->parent; + + if (class == NULL) + return FALSE; + + if (class == parent) + return TRUE; + } +} + + +int CLASS_find_symbol_with_prefix(CLASS *class, const char *name, const char *prefix) +{ + return SYMBOL_find(class->table, class->sort, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, name, strlen(name), prefix); +} + + +CLASS_DESC_SYMBOL *CLASS_get_symbol(CLASS *class, const char *name) +{ + int index; + + index = CLASS_find_symbol(class, name); + if (index == NO_SYMBOL) + return NULL; + else + return &class->table[index]; +} + + +CLASS_DESC *CLASS_get_symbol_desc(CLASS *class, const char *name) +{ + CLASS_DESC_SYMBOL *cds = CLASS_get_symbol(class, name); + + if (cds == NULL) + return NULL; + else + return cds->desc; +} + + +static short CLASS_get_symbol_index_kind(CLASS *class, const char *name, int kind, int kind2, TYPE type, bool error) +{ + CLASS_DESC *desc; + int index; + + index = CLASS_find_symbol(class, name); + if (index != NO_SYMBOL) + { + desc = CLASS_get_desc(class, index); + if (desc) + if ((CLASS_DESC_get_type(desc) == kind) || (CLASS_DESC_get_type(desc) == kind2)) + { + if (type == T_ANY || desc->method.type == type) + return (short)index; + + if (error) + THROW(E_SPEC, name, class->name); + } + } + + return (short)NO_SYMBOL; +} + + +CLASS_DESC *CLASS_get_symbol_desc_kind(CLASS *class, const char *name, int kind, int kind2, TYPE type) +{ + short index; + + index = CLASS_get_symbol_index_kind(class, name, kind, kind2, type, TRUE); + if (index == NO_SYMBOL) + return NULL; + else + return CLASS_get_desc(class, index); +} + + +CLASS_DESC_EVENT *CLASS_get_event_desc(CLASS *class, const char *name) +{ + int index; + CLASS_DESC *cd; + + index = CLASS_find_symbol_with_prefix(class, name, ":"); + if (index == NO_SYMBOL) + return NULL; + + cd = class->table[index].desc; + + if (CLASS_DESC_get_type(cd) != CD_EVENT) + return NULL; + + return &cd->event; +} + + +char *CLASS_DESC_get_signature(CLASS_DESC *cd) +{ + char *res = NULL; + TYPE *sign; + int i, n; + TYPE type; + + switch (CLASS_DESC_get_type(cd)) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + sign = cd->method.signature; + n = cd->method.npmax; + break; + + case CD_EVENT: + + sign = cd->event.signature; + n = cd->event.npmax; + break; + + case CD_EXTERN: + + sign = cd->ext.signature; + n = cd->ext.npmax; + break; + + default: + + return NULL; + } + + for (i = 0; i < n; i++) + { + type = sign[i]; + res = STRING_add(res, TYPE_to_string(type), 0); + if (TYPE_is_object(type)) + res = STRING_add_char(res, ';'); + } + + return res; +} + +// NOTE: The _free method can be called during a conversion, so we must save the EXEC structure + +static void error_CLASS_free(void *object, EXEC_GLOBAL *save) +{ + EXEC = *save; + ((OBJECT *)object)->ref = 0; + OBJECT_release(OBJECT_class(object), object); +} + +void CLASS_free(void *object) +{ + CLASS *class = OBJECT_class(object); + EXEC_GLOBAL save; + + if (!class->has_free) + { + OBJECT_release(class, object); + return; + } + + save = EXEC; + + ON_ERROR_2(error_CLASS_free, object, &save) + { + ((OBJECT *)object)->ref = 1; // Prevents anybody from freeing the object! + EXEC_special_inheritance(SPEC_FREE, class, object, 0, TRUE); + if (((OBJECT *)object)->ref != 1) + THROW(E_FREEREF); + error_CLASS_free(object, &save); + } + END_ERROR +} + +#if DEBUG_REF +void CLASS_ref(void *object) +{ + char *name; + + ((OBJECT *)object)->ref++; + + if (OBJECT_class(object) == FREE_MARK) + name = "*ALREADY FREED*"; + else + name = OBJECT_class(object)->name; + + #if DEBUG_MEMORY + fprintf(stderr, "%s: %s: ref(%s <%d>) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, GET_ALLOC_ID(object), ((OBJECT *)object)->ref); + #else + fprintf(stderr, "%s: %s: ref(%s %p) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, object, ((OBJECT *)object)->ref); + #endif + fflush(stdout); +} + +bool CLASS_unref(void *ob, bool can_free) +{ + char *name; + + OBJECT *object = (OBJECT *)ob; + + if (OBJECT_class(object) == FREE_MARK) + name = "*ALREADY FREED*"; + else + name = OBJECT_class(object)->name; + + #if DEBUG_MEMORY + if (object->ref <= 0) + fprintf(stderr, "*** <%d> REF = %ld !\n", GET_ALLOC_ID(object), object->ref); + + fprintf(stderr, "%s: %s: unref(%s <%d>) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, GET_ALLOC_ID(object), object->ref - 1); + #else + if (object->ref <= 0) + fprintf(stderr, "*** %p REF = %ld !\n", object, object->ref); + + fprintf(stderr, "%s, %s: unref(%s %p) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, object, object->ref - 1); + #endif + fflush(stdout); + + /*if (strcmp(OBJECT_class(object)->name, "Class") == 0) + fprintf(stderr, "Class ?\n");*/ + + if ((--(object->ref) <= 0) && can_free) + { + #if DEBUG_MEMORY + fprintf(stderr, "FREE <%d> !\n", GET_ALLOC_ID(object)); + #else + fprintf(stderr, "FREE %p !\n", object); + #endif + CLASS_free(object); + return TRUE; + } + + return FALSE; +} +#endif + +void CLASS_do_nothing() +{ +} + +int CLASS_return_zero() +{ + return 0; +} + +static CLASS *_sorted_class; + +static int partition(CLASS_DESC_SYMBOL *cds, ushort *sym, const int start, const int end) +{ + int pos = start; + short pivot = sym[start]; + int i; + ushort val; + int len; + const char *s1, *s2; + int result; + + for (i = start + 1; i <= end; i++) + { + len = cds[sym[i]].len; + result = len - cds[pivot].len; + + if (result > 0) + continue; + + if (result == 0) + { +#if TABLE_USE_KEY + result = cds[sym[i]].key - cds[pivot].key; + if (result > 0) + continue; + else if (result == 0) +#endif + { + s1 = cds[sym[i]].name; + s2 = cds[pivot].name; + + while (len) + { + result = tolower(*s1++) - tolower(*s2++); + if (result) + break; + len--; + } + + if (result == 0) + { + if (*cds[pivot].name != '.') + ERROR_panic("Symbol '%s' declared twice in class '%s'\n", cds[sym[i]].name, _sorted_class->name); + } + if (result >= 0) + continue; + } + } + + pos++; // incrémente compteur cad la place finale du pivot + //echanger(tableau, compteur, i); // élément positionné + val = sym[pos]; + sym[pos] = sym[i]; + sym[i] = val; + } + + //echanger(tableau, compteur, debut); // le pivot est placé + val = sym[pos]; + sym[pos] = sym[start]; + sym[start] = val; + + return pos; // et sa position est retournée +} + +static void my_qsort(CLASS_DESC_SYMBOL *cds, ushort *sym, const int start, const int end) +{ + if (start < end) // cas d'arrêt pour la récursivité + { + int pivot = partition(cds, sym, start, end); // division du tableau + my_qsort(cds, sym, start, pivot - 1); // trie partie1 + my_qsort(cds, sym, pivot + 1, end); // trie partie2 + } +} + +void CLASS_sort(CLASS *class) +{ + ushort *sym; + ushort i; + + if (!class->n_desc) + return; + + SYMBOL_compute_keys(class->table, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE); + + _sorted_class = class; + + ALLOC(&sym, sizeof(ushort) * class->n_desc); + + for (i = 0; i < class->n_desc; i++) + sym[i] = i; + + //qsort(sym, class->n_desc, sizeof(ushort), (int (*)(const void *, const void *))sort_desc); + my_qsort(class->table, sym, 0, class->n_desc - 1); + + class->sort = sym; + + #if DEBUG_DESC + { + SYMBOL *s; + + fprintf(stderr, "\nSORT %s\n", class->name); + + for (i = 0; i < class->n_desc; i++) + { + s = (SYMBOL *)&class->table[sym[i]]; + + fprintf(stderr, "[%d] %.*s (%d)\n", i, (int)s->len, s->name, sym[i]); + } + fputc('\n', stderr); + } + #endif +} + +void CLASS_inheritance(CLASS *class, CLASS *parent) +{ + if (class->parent != NULL) + THROW_CLASS(class, "Multiple inheritance", ""); + + class->parent = parent; + parent->has_child = TRUE; + + TRY + { + CLASS_load(class->parent); + } + CATCH + { + THROW_CLASS(class, "Cannot load parent class: ", STRING_new_temp_zero(ERROR_current->info.msg)); + } + END_TRY + + if (!class->check) + { + class->check = class->parent->check; + class->must_check = class->parent->must_check; + } + + if (!class->has_free) + class->has_free = class->parent->has_free; + + // CREATE STATIC is inherited, but not CREATE PRIVATE + + if (parent->auto_create) + class->auto_create = TRUE; + + class->is_array = parent->is_array; + + if (!class->array_type) + class->array_type = parent->array_type; + + //fprintf(stderr, "CLASS_inheritance: %s %s\n", class->name, class->auto_create ? "AUTO CREATE" : ""); +} + + + +const char *CLASS_DESC_get_type_name(const CLASS_DESC *desc) +{ + switch (desc->gambas.val3._int[1]) + { + case CD_PROPERTY_ID: return desc->gambas.val2 < 0 ? "r" : desc->gambas.val1 < 0 ? "w" : "p"; + case CD_VARIABLE_ID: return "v"; + case CD_METHOD_ID: return "m"; + case CD_STATIC_PROPERTY_ID: return desc->gambas.val2 < 0 ? "R" : desc->gambas.val1 < 0 ? "W" : "P"; + case CD_STATIC_VARIABLE_ID: return "V"; + case CD_STATIC_METHOD_ID: return "M"; + case CD_CONSTANT_ID: return "C"; + case CD_EVENT_ID: return ":"; + case CD_EXTERN_ID: return "X"; + default: + //fprintf(stderr, "CLASS_DESC_get_type_name: %s: %ld\n", desc->gambas.name, desc->gambas.val4); + return "?"; + } +} + +static bool check_signature(char type, const CLASS_DESC *desc, const CLASS_DESC *pdesc) +{ + TYPE *sd, *sp; + int nsd, nsp; + + if (!TYPE_are_compatible(desc->property.type, pdesc->property.type)) + return TRUE; + + switch (type) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + sd = desc->method.signature; + nsd = desc->method.npmax; + sp = pdesc->method.signature; + nsp = pdesc->method.npmax; + break; + + case CD_EVENT: + + sd = desc->event.signature; + nsd = desc->event.npmax; + sp = pdesc->event.signature; + nsp = pdesc->event.npmax; + break; + + case CD_EXTERN: + + sd = desc->ext.signature; + nsd = desc->ext.npmax; + sp = pdesc->ext.signature; + nsp = pdesc->ext.npmax; + break; + + case CD_VARIABLE: + case CD_STATIC_VARIABLE: + + return TRUE; + + default: + + return FALSE; + } + + return TYPE_compare_signature(sd, nsd, sp, nsp, TRUE); +} + +void CLASS_make_description(CLASS *class, const CLASS_DESC *desc, int n_desc, int *first) +{ + static const char *nonher[] = { "_new", "_free", "_init", "_exit", "_lang", NULL }; + + int ind; + int i, j; + const char *name; + const char **pnonher; + CLASS *parent; + char type, parent_type; + CLASS_DESC_SYMBOL *cds; + bool check; + + #if DEBUG_DESC + fprintf(stderr, "\n---- %s\n", class->name); + #endif + + // Compute number of public descriptions + + class->n_desc = n_desc; + if (class->parent) + class->n_desc += class->parent->n_desc; + + // Make the description symbol table + + if (class->n_desc) + ALLOC(&class->table, sizeof(CLASS_DESC_SYMBOL) * class->n_desc); + + i = 0; + + if (class->parent && class->parent->n_desc) + { + for (j = 0; j < class->parent->n_desc; j++) + { + class->table[i] = class->parent->table[j]; + i++; + } + + for (j = 0; j < n_desc; j++) + { + if (CLASS_is_native(class)) + { + name = &(desc[j].gambas.name[1]); + type = CLASS_DESC_get_type(&desc[j]); + //ptype = (const char*)desc[j].gambas.type; + //fprintf(stderr, "%s -> ", ptype); + //desc[j].gambas.type = TYPE_from_string(&ptype); + //fprintf(stderr, "%p\n", (void *)desc[j].gambas.type); + } + else + { + name = desc[j].gambas.name; + type = *CLASS_DESC_get_type_name(&desc[j]); + } + + //fprintf(stderr, "%s.%s\n", class->name, name); + + // An inherited symbol has two or more entries in the table. Only the first one + // will be used, and so it must point at the new description, not the inherited one. + check = FALSE; + parent = class; + while ((parent = parent->parent)) + { + ind = CLASS_find_symbol(parent, name); + if (ind == NO_SYMBOL) + continue; + + cds = &parent->table[ind]; + + // The parent class public symbols of non-native classes were replaced by the symbol kind returned by CLASS_DESC_get_type_name() + // Only the first inheritance level is tested against signature compatibility + + if (cds->desc && !check) + { + parent_type = CLASS_DESC_get_type(cds->desc); + + if (parent_type != type) + { + #if DEBUG_DESC + fprintf(stderr, "type = '%c' parent_type = '%c'\n", type, parent_type); + #endif + THROW(E_OVERRIDE, CLASS_get_name(parent), cds->name, CLASS_get_name(class)); + } + + if (!CLASS_is_native(class) && strcasecmp(name, "_new")) + { + //fprintf(stderr, "check_signature: %s\n", name); + if (check_signature(type, &desc[j], cds->desc)) + THROW(E_OVERRIDE, CLASS_get_name(parent), cds->name, CLASS_get_name(class)); + } + + check = TRUE; + } + + cds = &class->table[ind]; + + #if DEBUG_DESC + fprintf(stderr, "%s: [%d] (%p %ld) := (%p %ld)\n", name, ind, cds->desc, cds->desc ? cds->desc->gambas.val1 : 0, &desc[j], desc[j].gambas.val1); + #endif + + cds->desc = (CLASS_DESC *)&desc[j]; + cds->name = "."; + cds->len = 1; + + /*if (!desc[j].gambas.val1 && index(CD_CALL_SOMETHING_LIST, type) != NULL) + { + //#if DEBUG_DESC + fprintf(stderr, "CLASS_make_description: '%s.%s' gambas.val1: %ld -> %ld\n", class->name, desc[j].gambas.name, desc[j].gambas.val1, class->parent->table[ind].desc->gambas.val1); + //#endif + ((CLASS_DESC *)desc)[j].gambas.val1 = class->parent->table[ind].desc->gambas.val1; + }*/ + } + } + + for (pnonher = nonher; *pnonher; pnonher++) + { + ind = CLASS_find_symbol(class->parent, *pnonher); + if (ind != NO_SYMBOL) + { + cds = &class->table[ind]; + cds->desc = NULL; + cds->name = "."; + cds->len = 1; + } + } + } + + *first = i; + + for (j = 0; j < n_desc; j++, i++) + { + class->table[i].desc = (CLASS_DESC *)&desc[j]; + name = desc[j].gambas.name; + + /* On saute le caractère de type de symbole */ + if (CLASS_is_native(class)) + { + name++; + if (*name == '!') + name++; + } + + class->table[i].name = (char *)name; + class->table[i].len = strlen(name); + } + + #if DEBUG_DESC + { + CLASS_DESC_SYMBOL *cds; + + for (i = 0; i < class->n_desc; i++) + { + cds = &class->table[i]; + fprintf(stderr, "%d: %.*s %p\n", i, cds->len, cds->name, cds->desc); + } + } + #endif +} + +/* 'all' means that size_dynamic is the entire size of the object, not just the size of the data */ +/* And check if the class is a stream */ + +void CLASS_calc_info(CLASS *class, int n_event, int size_dynamic, bool all, int size_static) +{ + // If the class is native and static, then size_dynamic == 0. But if we want to inherit + // the static class, and make the inherited class dynamic, then class->off_event must + // start after the object header. So we fix size_dynamic accordingly. + + if (all && size_dynamic == 0) + size_dynamic = sizeof(OBJECT); + + if (class->parent) + { + if (all) + class->off_event = size_dynamic; + else + class->off_event = class->parent->off_event + size_dynamic; + + class->n_event = class->parent->n_event + n_event; + } + else + { + if (all) + class->off_event = size_dynamic; + else + class->off_event = sizeof(OBJECT) + size_dynamic; + + class->n_event = n_event; + } + + if (class->n_event) + class->size = class->off_event + sizeof(OBJECT_EVENT) + class->n_event * sizeof(ushort); + else + class->size = class->off_event; + + class->size_stat = size_static; + if (size_static) + ALLOC_ZERO(&class->stat, class->size_stat); + else + class->stat = NULL; + + class->is_stream = (class == CLASS_Stream) || (class->parent && class->parent->is_stream); + + class->is_simple = class->parent == NULL && !class->is_virtual && !class->must_check; + if (class->parent) + class->parent->is_simple = FALSE; +} + + +void CLASS_make_event(CLASS *class, int *first) +{ + int i, ev; + + *first = 0; + + if (class->n_event == 0) + return; + + if (!CLASS_is_native(class)) + { + if (class->parent == NULL) + { + class->event = class->load->event; + class->free_event = FALSE; + return; + } + } + + ALLOC_ZERO(&class->event, sizeof(CLASS_EVENT) * class->n_event); + + if (class->parent != NULL) + { + ev = class->parent->n_event; + + for (i = 0; i < ev; i++) + class->event[i] = class->parent->event[i]; + } + else + ev = 0; + + class->free_event = TRUE; + + *first = ev; +} + + +int CLASS_get_inheritance(CLASS *class, CLASS **her) +{ + int nher = 0; + + while ((nher < MAX_INHERITANCE) && (class != NULL)) + { + *her++ = class; + nher++; + class = class->parent; + } + + return nher; +} + +void *CLASS_auto_create(CLASS *class, int nparam) +{ + void *ob = class->instance; + + //fprintf(stderr, ">>> CLASS_auto_create: %s (%p)\n", class->name, ob); + + // We automatically release invalid automatic instances + + if (ob) + { + if (OBJECT_is_valid(ob)) + { + RELEASE_MANY(SP, nparam); + //fprintf(stderr, "<<< CLASS_auto_create: %s (%p): valid=1\n", class->name, ob); + return ob; + } + + OBJECT_UNREF(class->instance); + class->instance = NULL; + } + + /*fprintf(stderr, "CLASS_auto_create: create %s\n", class->name);*/ + + OBJECT_create_and_set(&class->instance, class, NULL, NULL, nparam); + //ob = class->instance; + //OBJECT_REF(ob, "CLASS_auto_create"); + + //fprintf(stderr, "<<< CLASS_auto_create: %s (%p) valid=%d\n", class->name, ob, OBJECT_is_valid(ob)); + + return class->instance; +} + +//#define SET_OPTIONAL_OPERATOR(_class, _op, _func) if (!CLASS_has_operator(_class, _op)) (_class)->operators[_op] = (EXEC_no_operator##_func) + +void CLASS_search_special(CLASS *class) +{ + static int _operator_strength = 0; + int sym; + int offset; + + class->special[SPEC_NEW] = CLASS_get_symbol_index_kind(class, "_new", CD_METHOD, 0, T_VOID, TRUE); + class->special[SPEC_FREE] = CLASS_get_symbol_index_kind(class, "_free", CD_METHOD, 0, T_VOID, TRUE); + class->special[SPEC_GET] = CLASS_get_symbol_index_kind(class, "_get", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE); + class->special[SPEC_PUT] = CLASS_get_symbol_index_kind(class, "_put", CD_METHOD, CD_STATIC_METHOD, T_VOID, TRUE); + class->special[SPEC_FIRST] = CLASS_get_symbol_index_kind(class, "_first", CD_METHOD, CD_STATIC_METHOD, T_VOID, TRUE); + class->special[SPEC_NEXT] = CLASS_get_symbol_index_kind(class, "_next", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE); + class->special[SPEC_CALL] = CLASS_get_symbol_index_kind(class, "_call", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE); + class->special[SPEC_UNKNOWN] = CLASS_get_symbol_index_kind(class, "_unknown", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE); + class->special[SPEC_PROPERTY] = CLASS_get_symbol_index_kind(class, "_property", CD_METHOD, CD_STATIC_METHOD, T_BOOLEAN, TRUE); + class->special[SPEC_COMPARE] = CLASS_get_symbol_index_kind(class, "_compare", CD_METHOD, 0, T_INTEGER, TRUE); + class->special[SPEC_ATTACH] = CLASS_get_symbol_index_kind(class, "_attach", CD_METHOD, 0, T_VOID, TRUE); + class->special[SPEC_READY] = CLASS_get_symbol_index_kind(class, "_ready", CD_METHOD, 0, T_VOID, TRUE); + class->special[SPEC_READ] = CLASS_get_symbol_index_kind(class, "_read", CD_METHOD, 0, T_VOID, TRUE); + class->special[SPEC_WRITE] = CLASS_get_symbol_index_kind(class, "_write", CD_METHOD, 0, T_VOID, TRUE); + class->special[SPEC_INVALID] = CLASS_get_symbol_index_kind(class, "_invalid", CD_VARIABLE, 0, T_BOOLEAN, TRUE); + + sym = CLASS_get_symbol_index_kind(class, "_@_convert", CD_CONSTANT, 0, T_ANY, FALSE); + if (sym != NO_SYMBOL) + { + class->has_convert = TRUE; + class->convert = CLASS_get_desc(class, sym)->constant.value._pointer; + } + + sym = CLASS_get_symbol_index_kind(class, "_@_operator", CD_CONSTANT, 0, T_ANY, FALSE); + if (sym != NO_SYMBOL) + { + class->has_operators = TRUE; + class->operators = CLASS_get_desc(class, sym)->constant.value._pointer; + _operator_strength++; + CLASS_set_operator_strength(class, _operator_strength); + } + + if (class->special[SPEC_NEXT] != NO_SYMBOL) + class->enum_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_NEXT])) == CD_STATIC_METHOD; + if (class->special[SPEC_UNKNOWN] != NO_SYMBOL) + class->unknown_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_UNKNOWN])) == CD_STATIC_METHOD; + if (class->special[SPEC_PROPERTY] != NO_SYMBOL) + class->property_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_PROPERTY])) == CD_STATIC_METHOD; + if (class->special[SPEC_FREE] != NO_SYMBOL) + class->has_free = TRUE; + + if (class->special[SPEC_INVALID] != NO_SYMBOL) + { + if (class->must_check && class->check != OBJECT_check_valid) + THROW_CLASS(class, "Validation method already implemented", ""); + class->must_check = TRUE; + class->check = OBJECT_check_valid; + offset = CLASS_get_desc(class, class->special[SPEC_INVALID])->variable.offset; + if (offset > 32767) + THROW_CLASS(class, "Validation variable must be declared first", ""); + class->special[SPEC_INVALID] = (short)offset; + } +} + + +CLASS *CLASS_check_global(CLASS *class) +{ + if (CLASS_is_loaded(class)) + { + if (COMPONENT_current && class->component == COMPONENT_current) + ERROR_panic("Class '%s' declared twice in the component '%s'.", CLASS_get_name(class), class->component->name); + + /*if (class->has_child) + THROW(E_CLASS, class->name, "Overriding an already inherited class is forbidden", "");*/ + + class = class_replace_global(class); + } + + return class; +} + + +CLASS_DESC_METHOD *CLASS_get_special_desc(CLASS *class, int spec) +{ + short index = class->special[spec]; + + if (index == NO_SYMBOL) + return NULL; + else + return &CLASS_get_desc(class, index)->method; +} + + +CLASS_DESC_SYMBOL *CLASS_get_next_sorted_symbol(CLASS *class, int *index) +{ + CLASS_DESC_SYMBOL *old = NULL; + CLASS_DESC_SYMBOL *cur; + + for(;;) + { + if (*index >= class->n_desc) + return NULL; + + cur = &class->table[class->sort[*index]]; + if (*index > 0) + old = &class->table[class->sort[*index - 1]]; + + (*index)++; + + if (!cur->desc) + continue; + + if (old && !TABLE_compare_ignore_case(cur->name, cur->len, old->name, old->len)) + continue; + + return cur; + } +} + + +void CLASS_create_array_class(CLASS *class) +{ + void *save = TYPE_joker; + char *name_joker; + GB_DESC *desc; + CLASS *array_type = (CLASS *)class->array_type; + + name_joker = STRING_new(class->name, strlen(class->name) - 2); + + if (!array_type) + { + array_type = class->global ? CLASS_find_global(name_joker) : CLASS_find(name_joker); + class->array_type = (TYPE)array_type; + } + + TYPE_joker = array_type; + array_type->array_class = class; + + ALLOC(&desc, sizeof(GB_DESC) * ARRAY_TEMPLATE_NDESC); + memcpy(desc, NATIVE_TemplateArray, sizeof(GB_DESC) * ARRAY_TEMPLATE_NDESC); + ((CLASS_DESC_GAMBAS *)desc)->name = class->name; + + CLASS_register_class(desc, class); + + class->is_array = TRUE; + class->quick_array = CQA_ARRAY; + class->data = (char *)desc; + + STRING_free(&name_joker); + TYPE_joker = save; +} + + +CLASS *CLASS_get_array_class(CLASS *class) +{ + if (!class->array_class) + { + char name[strlen(class->name) + 3]; + strcpy(name, class->name); + strcat(name, "[]"); + class->array_class = class->global ? CLASS_find_global(name) : CLASS_find(name); + CLASS_create_array_class(class->array_class); + } + + return class->array_class; +} + + +void CLASS_create_array_of_struct_class(CLASS *class) +{ + void *save = TYPE_joker; + char *name_joker; + GB_DESC *desc; + CLASS *array_type = (CLASS *)class->array_type; + + //fprintf(stderr, "CLASS_create_array_class: create %s\n", class->name); + + name_joker = STRING_new(&class->name[1], strlen(class->name) - 3); + + if (!array_type) + { + array_type = class->global ? CLASS_find_global(name_joker) : CLASS_find(name_joker); + class->array_type = (TYPE)array_type; + } + + TYPE_joker = array_type; + array_type->astruct_class = class; + + ALLOC(&desc, sizeof(GB_DESC) * ARRAY_OF_STRUCT_TEMPLATE_NDESC); + memcpy(desc, NATIVE_TemplateArrayOfStruct, sizeof(GB_DESC) * ARRAY_OF_STRUCT_TEMPLATE_NDESC); + ((CLASS_DESC_GAMBAS *)desc)->name = class->name; + + CLASS_register_class(desc, class); + + class->is_array = TRUE; + class->is_array_of_struct = TRUE; + class->data = (char *)desc; + + STRING_free(&name_joker); + TYPE_joker = save; +} + + +CLASS *CLASS_get_array_of_struct_class(CLASS *class) +{ + if (!class->astruct_class) + { + char name[strlen(class->name) + 4]; + sprintf(name, "$%s[]", class->name); + class->astruct_class = class->global ? CLASS_find_global(name) : CLASS_find(name); + CLASS_create_array_of_struct_class(class->astruct_class); + } + + return class->astruct_class; +} + +int CLASS_sizeof(CLASS *class) +{ + if (CLASS_is_struct(class)) + return class->size - sizeof(CSTRUCT); + else + return class->size - sizeof(OBJECT); +} + +char *CLASS_get_name(CLASS *class) +{ + char *name = class->name; + + while (*name == '^') + name++; + + return name; +} + + +CLASS *CLASS_find_load_from(const char *name, const char *from) +{ + COMPONENT *save; + CLASS *save_cp; + COMPONENT *comp = NULL; + CLASS *class; + + comp = COMPONENT_find(from); + + save = COMPONENT_current; + save_cp = CP; + + COMPONENT_current = comp; + CP = NULL; + + class = CLASS_find(name); + + if (!CLASS_is_loaded(class) && !class->in_load) + CLASS_load_without_init(class); + + COMPONENT_current = save; + CP = save_cp; + + return class; +} + + +void CLASS_translation_must_be_reloaded(void) +{ + CLASS *class; + + for (class = _classes; class; class = class->next) + { + if (class->ready && !class->is_native) + EXEC_public(class, NULL, "_lang", 0); + } +} diff --git a/main/gbx/gbx_class.h b/main/gbx/gbx_class.h new file mode 100644 index 00000000..d3cf87ae --- /dev/null +++ b/main/gbx/gbx_class.h @@ -0,0 +1,570 @@ +/*************************************************************************** + + gbx_class.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_CLASS_H +#define __GBX_CLASS_H + +#include "gb_error.h" +#include "gbx_type.h" +#include "gb_table.h" +#include "gbx_class_desc.h" +#include "gbx_component.h" + +#ifndef __GBX_CLASS_C +EXTERN bool CLASS_exiting; +#endif + +typedef + int CLASS_ID; + +typedef + struct { + CTYPE type; + int pos; + } + CLASS_VAR; + +typedef + struct _CLASS *CLASS_REF; + +typedef + char *CLASS_UNKNOWN; + +typedef + union { + int type; + struct { int type; double value; } PACKED _float; + struct { int type; float value; } PACKED _single; + struct { int type; int value; } PACKED _integer; + struct { int type; int64_t value; } PACKED _long; + struct { int type; char *addr; int len; } PACKED _string; + struct { int type; int val[2]; } PACKED _swap; + } + PACKED + CLASS_CONST; + +typedef + struct { + CTYPE type; + } + CLASS_LOCAL; + +typedef + struct { + TYPE type; + } + CLASS_PARAM; + +typedef + struct { + char *name; + int len; + } + PACKED + DEBUG_SYMBOL; + +typedef + struct { + DEBUG_SYMBOL sym; + int value; + } + LOCAL_SYMBOL; + +typedef + struct { + unsigned short line; + unsigned short nline; + unsigned short *pos; + char *name; + LOCAL_SYMBOL *local; + short n_local; + unsigned short index; // Function index in its class. Used by the profiler + } + PACKED + FUNC_DEBUG; + +typedef + struct { + TYPE type; + char n_param; + char npmin; + char vararg; + unsigned fast : 1; + unsigned unsafe : 1; + unsigned fast_linked : 1; + unsigned optional : 1; + unsigned use_is_missing : 1; + unsigned is_static : 1; + unsigned _reserved : 2; + short n_local; + short n_ctrl; + short stack_usage; + short error; + unsigned short *code; + CLASS_PARAM *param; + CLASS_LOCAL *local; + FUNC_DEBUG *debug; + } + PACKED + FUNCTION; + +typedef + struct { + TYPE type; + char n_param; + char npmin; + char vararg; + unsigned char flag; + } + PACKED + FUNCTION_FLAG; + +typedef + struct { + TYPE type; + short n_param; + short _reserved; + CLASS_PARAM *param; + char *name; + } + PACKED + CLASS_EVENT; + +typedef + struct { + TYPE type; + short n_param; + bool vararg; + unsigned loaded : 1; + unsigned _reserved : 7; + CLASS_PARAM *param; + char *alias; + char *library; + } + PACKED + CLASS_EXTERN; + +typedef + struct { + uint magic; + uint version; + uint endian; + uint flag; + } + PACKED + CLASS_HEADER; + +typedef + struct { + short parent; + short flag; + int s_static; + int s_dynamic; + short nstruct; + short _reserved; + } + PACKED + CLASS_INFO; + +typedef + struct { + CTYPE ctype; + int dim[0]; + } + CLASS_ARRAY; + +typedef + CLASS_ARRAY *CLASS_ARRAY_P; + +struct _CLASS; + +typedef + struct { + DEBUG_SYMBOL sym; + CTYPE ctype; + int value; + } + GLOBAL_SYMBOL; + +typedef + struct { + int nfield; + int *desc; + } + CLASS_STRUCT; + +typedef + struct { + short n_cst; + short n_stat; + short n_dyn; + short n_func; + + CLASS_CONST *cst; + CLASS_VAR *stat; + CLASS_VAR *dyn; + FUNCTION *func; + CLASS_EVENT *event; + CLASS_EXTERN *ext; + CLASS_ARRAY **array; + + CLASS_REF *class_ref; + char **unknown; + + GLOBAL_SYMBOL *global; + ushort *sort; + short n_global; + short n_ext; + uint *prof; // Shortcut indexes attributed during profiling. The first one is for the class, the other ones for the functions + #ifdef OS_64BITS + CLASS_DESC *desc; + CLASS_PARAM *local; + FUNC_DEBUG *debug; + #endif + } + PACKED + CLASS_LOAD; + +enum { + CO_EQUAL, CO_EQUALF, CO_EQUALO, + CO_COMP, CO_COMPF, CO_COMPO, + CO_ADD, CO_ADDF, CO_ADDO, + CO_SUB, CO_SUBF, CO_SUBO, + CO_MUL, CO_MULF, CO_MULO, + CO_DIV, CO_DIVF, CO_DIVO, + CO_POW, CO_POWF, CO_POWO, + CO_NEG, CO_ABS, CO_FABS, CO_SGN, + CO_STRENGTH +}; + +typedef + struct _CLASS { // 32b 64b + struct _CLASS *class; // 4 8 Points at the 'Class' class ! + int ref; // 8 12 Reference count + int count; // 12 16 Number of instanciated objects + struct _CLASS *parent; // 16 24 Inherited class + char *name; // 20 32 Class name + + unsigned loaded : 1; // Class is loaded + unsigned ready : 1; // Class is loaded and ready + unsigned debug : 1; // Debugging information ? + unsigned swap : 1; // Class endianness was swapped + unsigned free_event : 1; // Must free class->event + unsigned in_load : 1; // Class being loaded + unsigned exit : 1; // Marker used by CLASS_exit + unsigned auto_create : 1; // Automatically instanciated + + unsigned quick_array : 2; // Array accessor optimization type + unsigned no_create : 1; // Cannot instanciate this class + unsigned is_virtual : 1; // Virtual class (name beginning with a dot) + unsigned enum_static : 1; // If class enumeration is static + unsigned is_stream : 1; // If the class inherits stream + unsigned global : 1; // If the class is in the global table + unsigned error : 1; // Loading or registering the class has failed + + unsigned is_native : 1; // If the class is native (i.e. written in C/C++) + unsigned is_observer : 1; // This is the Observer class + unsigned is_struct : 1; // This class is a structure + unsigned is_array : 1; // This class is an array + unsigned is_array_of_struct : 1; // This class is an array of struct + unsigned init_dynamic : 1; // If there is a special function to call at instanciation + unsigned must_check : 1; // The class has a check function + unsigned has_child : 1; // The class has an inherited child class + + unsigned unknown_static : 1; // If _unknown is static + unsigned property_static : 1; // If _property is static + unsigned has_convert : 1; // If the _convert interface is implemented + unsigned has_operators : 1; // If the _operators interface is implemented + unsigned is_simple : 1; // Class has no parent, no child, is not virtual, and has no 'check' function. + unsigned has_free : 1; // The class has a free function + unsigned is_test : 1; // The class is a test module + unsigned not_3_18 : 1; // 24 36 If bytecode version is strictly older than 3.18 + + short n_desc; // 26 38 number of descriptions + short n_event; // 28 40 number of events + + CLASS_DESC_SYMBOL *table; // 32 48 class description + ushort *sort; // 36 56 table sort + + CLASS_EVENT *event; // 40 64 event description + + int (*check)(); // 44 72 method for checking that an object is valid + + char *data; // 48 80 class file data for loaded class + // or generated description for native class + // or generated description for structures + + CLASS_LOAD *load; // 52 88 information about loaded class + + char *stat; // 56 96 static class data + TYPE *signature; // 60 104 signatures address + char *string; // 64 112 strings address + + uint size_stat; // 68 116 static class size + uint size; // 72 120 dynamic class size + uint off_event; // 76 124 offset of OBJECT_EVENT structure in the object + #ifdef OS_64BITS + uint _reserved2; // 128 + #endif + + short special[16]; // 108 160 special functions index (_new, _free, ...) + + TYPE array_type; // 112 168 datatype of the contents if this class is an array class of objects + struct _CLASS *array_class; // 116 176 array of class + struct _CLASS *astruct_class; // 120 184 array of struct class + + void *instance; // 124 192 automatically created instance + void **operators; // 128 200 arithmetic interface + bool (*convert)(); // 132 208 convert method + + COMPONENT *component; // 136 216 The component the class belongs to + + struct _CLASS *next; // 140 224 next class + } + CLASS; + +typedef + struct { + SYMBOL sym; + CLASS *class; + } + PACKED + CLASS_SYMBOL; + +typedef + enum { + SPEC_NEW, + SPEC_FREE, + SPEC_GET, + SPEC_PUT, + SPEC_FIRST, + SPEC_NEXT, + SPEC_CALL, + SPEC_UNKNOWN, + SPEC_PROPERTY, + SPEC_COMPARE, + SPEC_ATTACH, + SPEC_READY, + SPEC_READ, + SPEC_WRITE, + SPEC_INVALID + } + CLASS_SPECIAL; + +typedef + enum { + CQA_NONE = 0, + CQA_ARRAY = 1, + CQA_COLLECTION = 2, + CQA_STRING = 3 + } + CLASS_QUICK_ARRAY; + +typedef + enum + { + CF_DEBUG = 1 + } + CLASS_FLAG; + +typedef + enum + { + CI_EXPORTED = 1, + CI_AUTO_CREATE = 2, + CI_OPTIONAL = 4, + CI_NO_CREATE = 8, + CI_HAS_FAST = 16, + CI_TEST = 32 + } + CLASS_INFO_FLAG; + +#ifndef __GBX_CLASS_INIT_C +EXTERN CLASS *CLASS_Class; +EXTERN CLASS *CLASS_Enum; +EXTERN CLASS *CLASS_Symbol; + +EXTERN CLASS *CLASS_Array; +EXTERN CLASS *CLASS_Collection; +EXTERN CLASS *CLASS_Stream; +EXTERN CLASS *CLASS_File; +EXTERN CLASS *CLASS_Stat; +EXTERN CLASS *CLASS_AppArgs; +EXTERN CLASS *CLASS_AppEnv; +EXTERN CLASS *CLASS_Application; +EXTERN CLASS *CLASS_Process; +EXTERN CLASS *CLASS_Component; +EXTERN CLASS *CLASS_Observer; +EXTERN CLASS *CLASS_Timer; +EXTERN CLASS *CLASS_BoxedString; + +EXTERN CLASS *CLASS_BooleanArray; +EXTERN CLASS *CLASS_ByteArray; +EXTERN CLASS *CLASS_ShortArray; +EXTERN CLASS *CLASS_IntegerArray; +EXTERN CLASS *CLASS_SingleArray; +EXTERN CLASS *CLASS_FloatArray; +EXTERN CLASS *CLASS_DateArray; +EXTERN CLASS *CLASS_StringArray; +EXTERN CLASS *CLASS_ObjectArray; +EXTERN CLASS *CLASS_VariantArray; +EXTERN CLASS *CLASS_LongArray; +EXTERN CLASS *CLASS_PointerArray; +#endif + + +#define DO_ERROR ((void (*)()) -1) + +#define CLASS_is_native(_class) ((_class)->is_native) +#define CLASS_is_struct(_class) ((_class)->is_struct) +#define CLASS_is_array(_class) ((_class)->is_array) +#define CLASS_is_array_of_struct(_class) ((_class)->is_array_of_struct) + +#define FUNCTION_is_static(func) ((func)->type & TF_STATIC) +//#define FUNCTION_is_native(_desc) (((uintptr_t)(_desc)->exec >> 16) != 0) +#define FUNCTION_is_native(_desc) ((_desc)->native) + +#define FUNC_INIT_STATIC 0 +#define FUNC_INIT_DYNAMIC 1 + + +/* class.c */ + +void CLASS_init(void); +void CLASS_clean_up(bool silent); +void CLASS_exit(void); +CLASS *CLASS_get_list(void); + +CLASS *CLASS_get(const char *name); +int CLASS_count(void); + +char *CLASS_get_name(CLASS *class); + +CLASS_DESC_SYMBOL *CLASS_get_symbol(CLASS *class, const char *name); +CLASS_DESC *CLASS_get_symbol_desc(CLASS *class, const char *name); + +//short CLASS_get_symbol_index_kind(CLASS *class, const char *name, int kind, int kind2); +CLASS_DESC *CLASS_get_symbol_desc_kind(CLASS *class, const char *name, int kind, int kind2, TYPE type); +CLASS_DESC_METHOD *CLASS_get_special_desc(CLASS *class, int spec); + +#define CLASS_get_desc(_class, _index) (((_class)->table[_index].desc)) + +CLASS_DESC_EVENT *CLASS_get_event_desc(CLASS *class, const char *name); + +#define CLASS_find_symbol(_class, _name) SYMBOL_find((_class)->table, (_class)->sort, (_class)->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, (_name), strlen((_name)), NULL) + +int CLASS_find_symbol_with_prefix(CLASS *class, const char *name, const char *prefix); + +CLASS *CLASS_look_do(const char *name, int len, bool global); +CLASS *CLASS_find_do(const char *name, bool global); + +#define CLASS_look(_name, _len) CLASS_look_do(_name, _len, FALSE) +#define CLASS_find(_name) CLASS_find_do(_name, FALSE) + +#define CLASS_look_global(_name, _len) CLASS_look_do(_name, _len, TRUE) +#define CLASS_find_global(_name) CLASS_find_do(_name, TRUE) + +TABLE *CLASS_get_table(void); + +bool CLASS_inherits(CLASS *class, CLASS *parent); + +CLASS *CLASS_replace_global(const char *name); +CLASS *CLASS_check_global(CLASS *class); +CLASS *CLASS_find_export(const char *name, const char *global); + +void CLASS_ref(void *object); +bool CLASS_unref(void *object, bool can_free); +void CLASS_free(void *object); + +int CLASS_get_inheritance(CLASS *class, CLASS **her); + +void CLASS_do_nothing(); +int CLASS_return_zero(); + +void CLASS_sort(CLASS *class); + +void CLASS_inheritance(CLASS *class, CLASS *parent); +void CLASS_make_description(CLASS *class, const CLASS_DESC *desc, int n_desc, int *first); +void CLASS_make_event(CLASS *class, int *first); +void CLASS_calc_info(CLASS *class, int n_event, int size_dynamic, bool all, int size_static); + +void *CLASS_auto_create(CLASS *class, int nparam); + +void CLASS_search_special(CLASS *class); + +CLASS_DESC_SYMBOL *CLASS_get_next_sorted_symbol(CLASS *class, int *index); + +int CLASS_can_be_used_like_an_array(CLASS *class); + +void CLASS_create_array_class(CLASS *class); +CLASS *CLASS_get_array_class(CLASS *class); + +void CLASS_create_array_of_struct_class(CLASS *class); +CLASS *CLASS_get_array_of_struct_class(CLASS *class); + +int CLASS_sizeof(CLASS *class); + +CLASS *CLASS_find_load_from(const char *name, const char *from); + +void CLASS_translation_must_be_reloaded(void); + +/* class_init.c */ + +void CLASS_init_native(void); +void CLASS_update_global(CLASS *old_class, CLASS *new_class); + +/* class_load.c */ + +TYPE CLASS_ctype_to_type(CLASS *class, CTYPE ctype); +int CLASS_sizeof_ctype(CLASS *class, CTYPE ctype); + +void CLASS_load_real(CLASS *class); +#define CLASS_load(_class) \ +({ \ + if (!((_class)->ready)) \ + CLASS_load_real(_class); \ +}) +void CLASS_run_inits(CLASS *class); +void CLASS_load_without_init(CLASS *class); + +/* class_native.c */ + +CLASS *CLASS_register_class(GB_DESC *desc, CLASS *class); +CLASS *CLASS_register(GB_DESC *desc); + +#define SET_IF_NULL(ptr, val) if ((ptr) == NULL) (ptr) = (val) +/* +#define DO_NOTHING(ptr) if ((ptr) == NULL) (ptr) = CLASS_do_nothing +#define RETURN_ZERO(ptr) if ((ptr) == NULL) (ptr) = CLASS_return_zero +*/ + +#define CLASS_is_virtual(class) (class->is_virtual) + +#define CLASS_has_operator(_class, _op) (((void **)(_class)->operators)[_op] != NULL) +#define CLASS_get_operator_strength(_class) (((intptr_t *)(_class)->operators)[CO_STRENGTH]) +#define CLASS_set_operator_strength(_class, _strength) (((intptr_t *)(_class)->operators)[CO_STRENGTH] = (_strength)) + +#define CLASS_is_loaded(_class) ((_class)->loaded) +#define CLASS_is_ready(_class) ((_class)->ready) + +#endif /* _CLASS_H */ diff --git a/main/gbx/gbx_class_desc.h b/main/gbx/gbx_class_desc.h new file mode 100644 index 00000000..8ff0b699 --- /dev/null +++ b/main/gbx/gbx_class_desc.h @@ -0,0 +1,209 @@ +/*************************************************************************** + + gbx_class_desc.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_CLASS_DESC_H +#define __GBX_CLASS_DESC_H + +#include "gb_class_desc_common.h" +#include "gb_table.h" + +#define CD_PROPERTY 'p' +#define CD_PROPERTY_READ 'r' +#define CD_PROPERTY_WRITE 'w' +#define CD_METHOD 'm' +#define CD_CONSTANT 'C' +#define CD_EVENT ':' +#define CD_STATIC_PROPERTY 'P' +#define CD_STATIC_PROPERTY_READ 'R' +#define CD_STATIC_PROPERTY_WRITE 'W' +#define CD_STATIC_METHOD 'M' +#define CD_VARIABLE 'v' +#define CD_STRUCT_FIELD 'f' +#define CD_STATIC_VARIABLE 'V' +#define CD_EXTERN 'X' + +#define CD_STATIC_LIST "PWRMVX" +#define CD_CALL_SOMETHING_LIST "pwrmPRM" + + +typedef + struct { + char *name; + TYPE type; // property datatype + void (*read)(); // read property function + void (*write)(); // write property function + unsigned native : 1; // if the property is native + unsigned _reserved : 7; + char _reserved2[3]; + #ifdef OS_64BITS + int _reserved3; + #endif + struct _CLASS *class; + } + CLASS_DESC_PROPERTY; + +typedef + struct { + char *name; + TYPE type; // variable datatype + int offset; // variable offset in object memory + CTYPE ctype; // variable compilation datatype + intptr_t _reserved; + #ifdef OS_64BITS + intptr_t _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_VARIABLE; + +typedef + struct { + char *name; + TYPE type; // return value datatype + void (*exec)(); // method + TYPE *signature; // signature + char npmin; // minimum number of arguments + char npmax; // maximum number of arguments + char npvar; // if there is a variable number of arguments + unsigned native : 1; // if the method is native + unsigned subr : 1; // static method called like a subr + unsigned _reserved : 6; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_METHOD; + +typedef + struct { + char *name; + TYPE type; // return value datatype - N/A + intptr_t index; // event index + TYPE *signature; // event signature + char npmin; // minimum number of arguments + char npmax; // maximum number of arguments + char npvar; // if there is a variable number of arguments + char _reserved; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_EVENT; + +typedef + struct { + char *name; + TYPE type; // return value datatype + int exec; // extern function index + TYPE *signature; // signature + char npmin; // minimum number of arguments + char npmax; // maximum number of arguments + char npvar; // if there is a variable number of arguments + char _reserved; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_EXTERN; + +typedef + struct { + char *name; + TYPE type; // constant datatype + union { + int _integer; + double _float; + float _single; + char *_string; + int64_t _long; + void *_pointer; + } + value; + unsigned translate : 1; + unsigned _reserved : 31; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_CONSTANT; + +typedef + struct { + char *name; + void (*func)(); + } + CLASS_DESC_HOOK; + +typedef + struct { + char *name; + intptr_t type; + intptr_t val1; + intptr_t val2; + union { + double _double; + intptr_t _int[2]; + } val3; + } + CLASS_DESC_GAMBAS; + + +typedef + union { + CLASS_DESC_PROPERTY property; + CLASS_DESC_VARIABLE variable; + CLASS_DESC_METHOD method; + CLASS_DESC_CONSTANT constant; + CLASS_DESC_EVENT event; + CLASS_DESC_HOOK hook; + CLASS_DESC_GAMBAS gambas; + CLASS_DESC_EXTERN ext; + } + CLASS_DESC; + +typedef + struct { + char *name; + int len; +#if TABLE_USE_KEY + uint key; +#endif + CLASS_DESC *desc; + } + PACKED + CLASS_DESC_SYMBOL; + + +#define CLASS_DESC_get_type(d) (*(d)->gambas.name) +#define CLASS_DESC_is_static_method(d) (CLASS_DESC_get_type((CLASS_DESC *)d) == 'M') +#define CLASS_DESC_SELF (-1) + +char *CLASS_DESC_get_signature(CLASS_DESC *cd); +const char *CLASS_DESC_get_type_name(const CLASS_DESC *desc); + + +#endif diff --git a/main/gbx/gbx_class_info.c b/main/gbx/gbx_class_info.c new file mode 100644 index 00000000..4b62db47 --- /dev/null +++ b/main/gbx/gbx_class_info.c @@ -0,0 +1,448 @@ +/*************************************************************************** + + gbx_class_info.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_INFO_C + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gbx_c_array.h" +#include "gambas.h" + +static GB_DESC NATIVE_GambasLanguage[] = +{ + GB_DECLARE(".", 0), + + GB_METHOD("Left$", "s", NULL, "(String)s[(Length)i]"), + GB_METHOD("Left", "s", NULL, "(String)s[(Length)i]"), + + GB_METHOD("Mid$", "s", NULL, "(String)s[(Pos)i(Length)i]"), + GB_METHOD("Mid", "s", NULL, "(String)s[(Pos)i(Length)i]"), + + GB_METHOD("Right$", "s", NULL, "(String)s[(Length)i]"), + GB_METHOD("Right", "s", NULL, "(String)s[(Length)i]"), + + GB_METHOD("Len", "i", NULL, "(String)s"), + + GB_METHOD("Space$", "s", NULL, "(Length)i"), + GB_METHOD("Space", "s", NULL, "(Length)i"), + + GB_METHOD("String$", "s", NULL, "(Length)i(Pattern)s"), + GB_METHOD("String", "s", NULL, "(Length)i(Pattern)s"), + + GB_METHOD("Trim$", "s", NULL, "(String)s"), + GB_METHOD("Trim", "s", NULL, "(String)s"), + + GB_METHOD("LTrim$", "s", NULL, "(String)s"), + GB_METHOD("LTrim", "s", NULL, "(String)s"), + + GB_METHOD("RTrim$", "s", NULL, "(String)s"), + GB_METHOD("RTrim", "s", NULL, "(String)s"), + + GB_METHOD("Upper$", "s", NULL, "(String)s"), + GB_METHOD("Upper", "s", NULL, "(String)s"), + GB_METHOD("UCase$", "s", NULL, "(String)s"), + GB_METHOD("UCase", "s", NULL, "(String)s"), + + GB_METHOD("Lower$", "s", NULL, "(String)s"), + GB_METHOD("Lower", "s", NULL, "(String)s"), + GB_METHOD("LCase$", "s", NULL, "(String)s"), + GB_METHOD("LCase", "s", NULL, "(String)s"), + + GB_METHOD("Chr$", "s", NULL, "(Code)i"), + GB_METHOD("Chr", "s", NULL, "(Code)i"), + + GB_METHOD("Asc", "i", NULL, "(String)s[(Pos)i]"), + + GB_METHOD("Instr", "i", NULL, "(String)s(Pattern)s[(From)i(Mode)i]"), + GB_METHOD("RInstr", "i", NULL, "(String)s(Pattern)s[(From)i(Mode)i]"), + + GB_METHOD("Subst$", "s", NULL, "(Pattern)s."), + GB_METHOD("Subst", "s", NULL, "(Pattern)s."), + + GB_METHOD("Replace$", "s", NULL, "(String)s(Find)s(Replace)s[(Mode)i]"), + GB_METHOD("Replace", "s", NULL, "(String)s(Find)s(Replace)s[(Mode)i]"), + + GB_METHOD("Split", "String[]", NULL, "(String)s[(Separators)s(Escape)s(IgnoreVoid)b(KeepEscape)b]"), + GB_METHOD("Scan", "String[]", NULL, "(String)s(Pattern)s"), + + GB_METHOD("Comp", "i", NULL, "(String1)s(String2)s[(Mode)i]"), + + GB_METHOD("Conv$", "s", NULL, "(String)s(From)s(To)s"), + GB_METHOD("Conv", "s", NULL, "(String)s(From)s(To)s"), + + GB_METHOD("SConv$", "s", NULL, "(String)s"), + GB_METHOD("SConv", "s", NULL, "(String)s"), + + GB_METHOD("DConv$", "s", NULL, "(String)s"), + GB_METHOD("DConv", "s", NULL, "(String)s"), + + GB_METHOD("Abs", "v", NULL, "(Value)v"), + GB_METHOD("Int", "v", NULL, "(Value)v"), + + GB_METHOD("Frac", "f", NULL, "(Value)f"), + GB_METHOD("Log", "f", NULL, "(Value)f"), + GB_METHOD("Exp", "f", NULL, "(Value)f"), + GB_METHOD("Sqr", "f", NULL, "(Value)f"), + GB_METHOD("Sin", "f", NULL, "(Value)f"), + GB_METHOD("Cos", "f", NULL, "(Value)f"), + GB_METHOD("Tan", "f", NULL, "(Value)f"), + GB_METHOD("Atn", "f", NULL, "(Value)f"), + GB_METHOD("ATan", "f", NULL, "(Value)f"), + GB_METHOD("Asn", "f", NULL, "(Value)f"), + GB_METHOD("ASin", "f", NULL, "(Value)f"), + GB_METHOD("Acs", "f", NULL, "(Value)f"), + GB_METHOD("ACos", "f", NULL, "(Value)f"), + GB_METHOD("Deg", "f", NULL, "(Radians)f"), + GB_METHOD("Rad", "f", NULL, "(Degrees)f"), + GB_METHOD("Log10", "f", NULL, "(Value)f"), + GB_METHOD("Sinh", "f", NULL, "(Value)f"), + GB_METHOD("Cosh", "f", NULL, "(Value)f"), + GB_METHOD("Tanh", "f", NULL, "(Value)f"), + GB_METHOD("Asnh", "f", NULL, "(Value)f"), + GB_METHOD("ASinh", "f", NULL, "(Value)f"), + GB_METHOD("Acsh", "f", NULL, "(Value)f"), + GB_METHOD("ACosh", "f", NULL, "(Value)f"), + GB_METHOD("Atnh", "f", NULL, "(Value)f"), + GB_METHOD("ATanh", "f", NULL, "(Value)f"), + GB_METHOD("Exp2", "f", NULL, "(Value)f"), + GB_METHOD("Exp10", "f", NULL, "(Value)f"), + GB_METHOD("Log2", "f", NULL, "(Value)f"), + GB_METHOD("Cbr", "f", NULL, "(Value)f"), + GB_METHOD("Expm", "f", NULL, "(Value)f"), + GB_METHOD("Logp", "f", NULL, "(Value)f"), + GB_METHOD("Ceil", "f", NULL, "(Value)f"), + GB_METHOD("Floor", "f", NULL, "(Value)f"), + + GB_METHOD("Atan2", "f", NULL, "(X)f(Y)f"), + GB_METHOD("Ang", "f", NULL, "(X)f(Y)f"), + GB_METHOD("Hyp", "f", NULL, "(X)f(Y)f"), + GB_METHOD("Mag", "f", NULL, "(X)f(Y)f"), + + GB_METHOD("Sgn", "i", NULL, "(Value)v"), + GB_METHOD("Fix", "v", NULL, "(Value)v"), + + GB_METHOD("Pi", "f", NULL, "[(Factor)f]"), + + GB_METHOD("Round", "f", NULL, "(Value)f[(Round)i]"), + + GB_METHOD("Rnd", "f", NULL, "[(From)f(To)f]"), + GB_METHOD("Rand", "i", NULL, "(From)i[(To)i]"), + + GB_METHOD("Min", "v", NULL, "(Value)v(Value2)v"), + GB_METHOD("Max", "v", NULL, "(Value)v(Value2)v"), + + GB_METHOD("If", "v", NULL, "(Test)b(True)v(False)v"), + GB_METHOD("IIf", "v", NULL, "(Test)b(True)v(False)v"), + + GB_METHOD("Choose", "v", NULL, "(Select)i[(Value)v.]"), + + GB_METHOD("BClr", "i", NULL, "(Value)i(Bit)i"), + GB_METHOD("BSet", "i", NULL, "(Value)i(Bit)i"), + GB_METHOD("BTst", "i", NULL, "(Value)i(Bit)i"), + GB_METHOD("BChg", "i", NULL, "(Value)i(Bit)i"), + + GB_METHOD("Shl", "i", NULL, "(Value)i(Shift)i"), + GB_METHOD("Shr", "i", NULL, "(Value)i(Shift)i"), + GB_METHOD("Rol", "i", NULL, "(Value)i(Shift)i"), + GB_METHOD("Ror", "i", NULL, "(Value)i(Shift)i"), + + GB_METHOD("IsBoolean", "b", NULL, "(Value)s"), + GB_METHOD("IsInteger", "b", NULL, "(Value)s"), + GB_METHOD("IsLong", "b", NULL, "(Value)s"), + GB_METHOD("IsFloat", "b", NULL, "(Value)s"), + GB_METHOD("IsDate", "b", NULL, "(Value)s"), + GB_METHOD("IsNumber", "b", NULL, "(Value)s"), + GB_METHOD("IsNull", "b", NULL, "(Value)v"), + + GB_METHOD("IsAscii", "b", NULL, "(String)s"), + GB_METHOD("IsLetter", "b", NULL, "(String)s"), + GB_METHOD("IsLower", "b", NULL, "(String)s"), + GB_METHOD("IsUpper", "b", NULL, "(String)s"), + GB_METHOD("IsLCase", "b", NULL, "(String)s"), + GB_METHOD("IsUCase", "b", NULL, "(String)s"), + GB_METHOD("IsDigit", "b", NULL, "(String)s"), + GB_METHOD("IsHexa", "b", NULL, "(String)s"), + GB_METHOD("IsSpace", "b", NULL, "(String)s"), + GB_METHOD("IsBlank", "b", NULL, "(String)s"), + GB_METHOD("IsPunct", "b", NULL, "(String)s"), + + GB_METHOD("TypeOf", "i", NULL, "(Value)v"), + GB_METHOD("SizeOf", "i", NULL, "(Type)i"), + + GB_METHOD("CBool", "b", NULL, "(Value)v"), + GB_METHOD("CBoolean", "b", NULL, "(Value)v"), + GB_METHOD("CByte", "c", NULL, "(Value)v"), + GB_METHOD("CShort", "h", NULL, "(Value)v"), + GB_METHOD("CInt", "i", NULL, "(Value)v"), + GB_METHOD("CInteger", "i", NULL, "(Value)v"), + GB_METHOD("CLong", "l", NULL, "(Value)v"), + GB_METHOD("CSingle", "g", NULL, "(Value)v"), + GB_METHOD("CFloat", "f", NULL, "(Value)v"), + GB_METHOD("CDate", "d", NULL, "(Value)v"), + GB_METHOD("CStr", "s", NULL, "(Value)v"), + GB_METHOD("CString", "s", NULL, "(Value)v"), + GB_METHOD("CPointer", "p", NULL, "(Value)v"), + GB_METHOD("CVariant", "v", NULL, "(Value)v"), + + GB_METHOD("Bin$", "s", NULL, "(Value)v[(Digits)i]"), + GB_METHOD("Bin", "s", NULL, "(Value)v[(Digits)i]"), + + GB_METHOD("Hex$", "s", NULL, "(Value)v[(Digits)i]"), + GB_METHOD("Hex", "s", NULL, "(Value)v[(Digits)i]"), + + GB_METHOD("Val", "v", NULL, "(String)s"), + + GB_METHOD("Str$", "s", NULL, "(Value)v"), + GB_METHOD("Str", "s", NULL, "(Value)v"), + + GB_METHOD("Format$", "s", NULL, "(Value)v[(Format)s]"), + GB_METHOD("Format", "s", NULL, "(Value)v[(Format)s]"), + + GB_METHOD("Timer", "f", NULL, NULL), + GB_METHOD("Now", "d", NULL, NULL), + + GB_METHOD("Year", "i", NULL, "(Date)d"), + GB_METHOD("Month", "i", NULL, "(Date)d"), + GB_METHOD("Day", "i", NULL, "(Date)d"), + GB_METHOD("Hour", "i", NULL, "(Date)d"), + GB_METHOD("Minute", "i", NULL, "(Date)d"), + GB_METHOD("Second", "i", NULL, "(Date)d"), + GB_METHOD("WeekDay", "i", NULL, "(Date)d"), + + GB_METHOD("Date", "d", NULL, "[(DateOrYear)v(Month)i(Day)i(Hour)i(Minute)i(Second)i(MilliSecond)i]"), + GB_METHOD("Time", "d", NULL, "[(DateOrHour)v(Minute)i(Second)i(MilliSecond)i]"), + GB_METHOD("Week", "i", NULL, "[(Date)d(Mode)i(Plain)b]"), + + GB_METHOD("DateAdd", "d", NULL, "(Date)d(Period)i(Count)i"), + GB_METHOD("DateDiff", "i", NULL, "(Date1)d(Date2)d(Period)i"), + + GB_METHOD("Eval", "v", NULL, "(Expression)s[(Context)Collection;]"), + + GB_METHOD("Eof", "b", NULL, "[(File)Stream;]"), + GB_METHOD("Lof", "l", NULL, "[(File)Stream;]"), + GB_METHOD("Seek", "l", NULL, "(File)Stream;"), + + GB_METHOD("Exist", "b", NULL, "(Path)s[(FollowLink)b]"), + GB_METHOD("Stat", "Stat", NULL, "(Path)s[(FollowLink)b]"), + + GB_METHOD("Temp$", "s", NULL, "[(Prefix)s]"), + GB_METHOD("Temp", "s", NULL, "[(Prefix)s]"), + + GB_METHOD("IsDir", "b", NULL, "(Path)s"), + + GB_METHOD("Access", "b", NULL, "(Path)s[(Mode)i]"), + + GB_METHOD("Dir", "String[]", NULL, "(Path)s[(Pattern)s(Filter)i]"), + GB_METHOD("RDir", "String[]", NULL, "(Path)s[(Pattern)s(Filter)i(FollowLink)b]"), + + GB_METHOD("DFree", "l", NULL, "(Path)s"), + + GB_METHOD("Alloc", "p", NULL, "(SizeOrString)v[(Count)i]"), + GB_METHOD("Free", NULL, NULL, "(Pointer)p"), + GB_METHOD("Realloc", "i", NULL, "(Pointer)p(Size)i[(Count)i]"), + GB_METHOD("Str@", "s", NULL, "(Pointer)p"), + GB_METHOD("String@", "s", NULL, "(Pointer)p"), + GB_METHOD("VarPtr", "p", NULL, "(Variable)v"), + + GB_METHOD("MkBool", "s", NULL, "(Value)b"), + GB_METHOD("MkBool$", "s", NULL, "(Value)b"), + GB_METHOD("MkBoolean", "s", NULL, "(Value)b"), + GB_METHOD("MkBoolean$", "s", NULL, "(Value)b"), + GB_METHOD("MkByte", "s", NULL, "(Value)c"), + GB_METHOD("MkByte$", "s", NULL, "(Value)c"), + GB_METHOD("MkShort", "s", NULL, "(Value)h"), + GB_METHOD("MkShort$", "s", NULL, "(Value)h"), + GB_METHOD("MkInt", "s", NULL, "(Value)i"), + GB_METHOD("MkInt$", "s", NULL, "(Value)i"), + GB_METHOD("MkInteger", "s", NULL, "(Value)i"), + GB_METHOD("MkInteger$", "s", NULL, "(Value)i"), + GB_METHOD("MkLong", "s", NULL, "(Value)l"), + GB_METHOD("MkLong$", "s", NULL, "(Value)l"), + GB_METHOD("MkSingle", "s", NULL, "(Value)g"), + GB_METHOD("MkSingle$", "s", NULL, "(Value)g"), + GB_METHOD("MkFloat", "s", NULL, "(Value)f"), + GB_METHOD("MkFloat$", "s", NULL, "(Value)f"), + GB_METHOD("MkDate", "s", NULL, "(Value)d"), + GB_METHOD("MkDate$", "s", NULL, "(Value)d"), + GB_METHOD("MkPointer", "s", NULL, "(Value)p"), + GB_METHOD("MkPointer$", "s", NULL, "(Value)p"), + + GB_METHOD("Swap", "s", NULL, "(String)s[(Endianness)i]"), + GB_METHOD("Swap$", "s", NULL, "(String)s[(Endianness)i]"), + + GB_METHOD("Bool@", "b", NULL, "(Pointer)p"), + GB_METHOD("Boolean@", "b", NULL, "(Pointer)p"), + GB_METHOD("Byte@", "c", NULL, "(Pointer)p"), + GB_METHOD("Short@", "h", NULL, "(Pointer)p"), + GB_METHOD("Int@", "i", NULL, "(Pointer)p"), + GB_METHOD("Integer@", "i", NULL, "(Pointer)p"), + GB_METHOD("Long@", "l", NULL, "(Pointer)p"), + GB_METHOD("Single@", "g", NULL, "(Pointer)p"), + GB_METHOD("Float@", "f", NULL, "(Pointer)p"), + GB_METHOD("Date@", "f", NULL, "(Pointer)p"), + GB_METHOD("Pointer@", "p", NULL, "(Pointer)p"), + + GB_METHOD("Tr", "s", NULL, "(String)s"), + GB_METHOD("Tr$", "s", NULL, "(String)s"), + + GB_METHOD("Quote", "s", NULL, "(String)s"), + GB_METHOD("Quote$", "s", NULL, "(String)s"), + + GB_METHOD("Shell", "s", NULL, "(String)s"), + GB_METHOD("Shell$", "s", NULL, "(String)s"), + + GB_METHOD("Html", "s", NULL, "(String)s"), + GB_METHOD("Html$", "s", NULL, "(String)s"), + + GB_METHOD("Base64", "s", NULL, "(String)s"), + GB_METHOD("Base64$", "s", NULL, "(String)s"), + + GB_METHOD("Url", "s", NULL, "(String)s"), + GB_METHOD("Url$", "s", NULL, "(String)s"), + + GB_METHOD("Unquote", "s", NULL, "(String)s"), + GB_METHOD("Unquote$", "s", NULL, "(String)s"), + + GB_METHOD("UnBase64", "s", NULL, "(String)s"), + GB_METHOD("UnBase64$", "s", NULL, "(String)s"), + + GB_METHOD("FromBase64", "s", NULL, "(String)s"), + GB_METHOD("FromBase64$", "s", NULL, "(String)s"), + + GB_METHOD("FromUrl", "s", NULL, "(String)s"), + GB_METHOD("FromUrl$", "s", NULL, "(String)s"), + + GB_METHOD("Odd", "b", NULL, "(Value)i"), + GB_METHOD("Even", "b", NULL, "(Value)i"), + + GB_METHOD("IsNan", "b", NULL, "(Value)f"), + GB_METHOD("IsInf", "i", NULL, "(Value)f"), + + GB_METHOD("IsMissing", "b", NULL, "(Argument)?"), + + GB_END_DECLARE +}; + + +extern GB_DESC NATIVE_GambasLanguage[]; +extern GB_DESC NATIVE_Gambas[]; +extern GB_DESC NATIVE_Param[]; +extern GB_DESC NATIVE_Enum[]; +extern GB_DESC NATIVE_Symbol[]; +extern GB_DESC NATIVE_Class[]; +extern GB_DESC NATIVE_Classes[]; +extern GB_DESC NATIVE_Component[]; +extern GB_DESC NATIVE_Components[]; +extern GB_DESC NATIVE_Object[]; +extern GB_DESC NATIVE_Collection[]; +extern GB_DESC NATIVE_Error[]; +extern GB_DESC StreamDesc[]; +extern GB_DESC StreamLinesDesc[]; +extern GB_DESC StreamTermDesc[]; +extern GB_DESC StatPermDesc[]; +extern GB_DESC StatDesc[]; +extern GB_DESC FileDesc[]; +extern GB_DESC NATIVE_AppEnv[]; +extern GB_DESC NATIVE_AppArgs[]; +extern GB_DESC NATIVE_App[]; +extern GB_DESC NATIVE_System[]; +extern GB_DESC NATIVE_Jit[]; +extern GB_DESC NATIVE_User[]; +extern GB_DESC NATIVE_ArrayBounds[]; +extern GB_DESC NATIVE_Array[]; +extern GB_DESC NATIVE_Process[]; +extern GB_DESC NATIVE_BooleanArray[]; +extern GB_DESC NATIVE_ByteArray[]; +extern GB_DESC NATIVE_ShortArray[]; +extern GB_DESC NATIVE_IntegerArray[]; +extern GB_DESC NATIVE_SingleArray[]; +extern GB_DESC NATIVE_FloatArray[]; +extern GB_DESC NATIVE_DateArray[]; +extern GB_DESC NATIVE_StringArray[]; +extern GB_DESC NATIVE_ObjectArray[]; +extern GB_DESC NATIVE_VariantArray[]; +extern GB_DESC NATIVE_TemplateArray[]; +extern GB_DESC NATIVE_TemplateArrayOfStruct[]; +extern GB_DESC NATIVE_LongArray[]; +extern GB_DESC NATIVE_PointerArray[]; +extern GB_DESC StringDesc[]; +extern GB_DESC BoxedStringDesc[]; +extern GB_DESC TaskDesc[]; +extern GB_DESC NATIVE_Timer[]; +extern GB_DESC NATIVE_Observer[]; +extern GB_DESC NATIVE_Proxy[]; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NATIVE_GambasLanguage, + NATIVE_Gambas, + NATIVE_Param, + NATIVE_Enum, + NATIVE_Symbol, + NATIVE_Class, + NATIVE_Classes, + NATIVE_Component, + NATIVE_Components, + NATIVE_Object, + NATIVE_Collection, + NATIVE_Error, + StreamLinesDesc, + StreamTermDesc, + StreamDesc, + StatPermDesc, + StatDesc, + FileDesc, + NATIVE_AppEnv, + NATIVE_AppArgs, + NATIVE_App, + NATIVE_System, + NATIVE_Jit, + NATIVE_User, + NATIVE_ArrayBounds, + NATIVE_Array, + NATIVE_Process, + NATIVE_BooleanArray, + NATIVE_ByteArray, + NATIVE_ShortArray, + NATIVE_IntegerArray, + NATIVE_SingleArray, + NATIVE_FloatArray, + NATIVE_DateArray, + NATIVE_StringArray, + NATIVE_ObjectArray, + NATIVE_VariantArray, + NATIVE_TemplateArray, + NATIVE_TemplateArrayOfStruct, + NATIVE_LongArray, + NATIVE_PointerArray, + StringDesc, + BoxedStringDesc, + TaskDesc, + NATIVE_Timer, + NATIVE_Observer, + //NATIVE_Proxy, + NULL +}; + diff --git a/main/gbx/gbx_class_init.c b/main/gbx/gbx_class_init.c new file mode 100644 index 00000000..d4891ca3 --- /dev/null +++ b/main/gbx/gbx_class_init.c @@ -0,0 +1,187 @@ +/*************************************************************************** + + gbx_class_init.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_INIT_C + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include "gbx_component.h" + +#include "gbx_c_gambas.h" +#include "gbx_c_observer.h" +#include "gbx_c_class.h" +#include "gbx_c_error.h" +#include "gbx_c_collection.h" +#include "gbx_c_file.h" +#include "gbx_c_system.h" +#include "gbx_c_application.h" +#include "gbx_c_array.h" +#include "gbx_c_process.h" +#include "gbx_c_string.h" +#include "gbx_c_enum.h" +#include "gbx_c_timer.h" +#include "gbx_c_task.h" + +#include "gbx_class.h" + +CLASS *CLASS_Class = NULL; +CLASS *CLASS_Collection = NULL; +CLASS *CLASS_Symbol = NULL; +CLASS *CLASS_File = NULL; +CLASS *CLASS_Stat = NULL; +CLASS *CLASS_Stream = NULL; +CLASS *CLASS_Application = NULL; +CLASS *CLASS_AppArgs = NULL; +CLASS *CLASS_AppEnv = NULL; +CLASS *CLASS_Process = NULL; +CLASS *CLASS_Component = NULL; +CLASS *CLASS_Observer = NULL; +CLASS *CLASS_Timer = NULL; +CLASS *CLASS_BoxedString = NULL; + +CLASS *CLASS_Array = NULL; +CLASS *CLASS_BooleanArray = NULL; +CLASS *CLASS_ByteArray = NULL; +CLASS *CLASS_ShortArray = NULL; +CLASS *CLASS_IntegerArray = NULL; +CLASS *CLASS_SingleArray = NULL; +CLASS *CLASS_FloatArray = NULL; +CLASS *CLASS_DateArray = NULL; +CLASS *CLASS_StringArray = NULL; +CLASS *CLASS_ObjectArray = NULL; +CLASS *CLASS_VariantArray = NULL; +CLASS *CLASS_LongArray = NULL; +CLASS *CLASS_PointerArray = NULL; + +CLASS *CLASS_String = NULL; +CLASS *CLASS_Enum = NULL; + +typedef + struct { + GB_DESC *desc; + CLASS **class; + int array; + TYPE array_type; + } + CLASS_INIT; + +static const CLASS_INIT _init_list[] = +{ + { NATIVE_Gambas, NULL }, + { NATIVE_Param, NULL }, + { NATIVE_Enum, &CLASS_Enum }, + { NATIVE_Symbol, NULL }, + { NATIVE_Class, NULL }, + { NATIVE_Classes, NULL }, + { NATIVE_Component, NULL }, + { NATIVE_Components, NULL }, + { NATIVE_Object, NULL }, + { NATIVE_Collection, &CLASS_Collection, CQA_COLLECTION }, + { NATIVE_Error, NULL }, + { StreamLinesDesc, NULL }, + { StreamTermDesc, NULL }, + { StreamDesc, &CLASS_Stream }, + { StatPermDesc, NULL }, + { StatDesc, &CLASS_Stat }, + { FileDesc, &CLASS_File }, + { NATIVE_AppEnv, &CLASS_AppEnv }, + { NATIVE_AppArgs, &CLASS_AppArgs }, + { NATIVE_App, &CLASS_Application }, + { NATIVE_Process, &CLASS_Process }, + { NATIVE_System, NULL }, + { NATIVE_Jit, NULL }, + { NATIVE_User, NULL }, + { StringDesc, NULL }, + { BoxedStringDesc, &CLASS_BoxedString, CQA_STRING }, + { TaskDesc, NULL }, + { NATIVE_Timer, &CLASS_Timer }, + { NATIVE_Observer, &CLASS_Observer }, + //{ NATIVE_Proxy, &CLASS_Proxy }, + + { NATIVE_ArrayBounds, NULL }, + { NATIVE_Array, &CLASS_Array }, + { NATIVE_BooleanArray, &CLASS_BooleanArray, CQA_ARRAY, T_BOOLEAN }, + { NATIVE_ByteArray, &CLASS_ByteArray, CQA_ARRAY, T_BYTE }, + { NATIVE_ShortArray, &CLASS_ShortArray, CQA_ARRAY, T_SHORT }, + { NATIVE_IntegerArray, &CLASS_IntegerArray, CQA_ARRAY, T_INTEGER }, + { NATIVE_FloatArray, &CLASS_FloatArray, CQA_ARRAY, T_FLOAT }, + { NATIVE_SingleArray, &CLASS_SingleArray, CQA_ARRAY, T_SINGLE }, + { NATIVE_DateArray, &CLASS_DateArray, CQA_ARRAY, T_DATE }, + { NATIVE_StringArray, &CLASS_StringArray, CQA_ARRAY, T_STRING }, + { NATIVE_ObjectArray, &CLASS_ObjectArray, CQA_ARRAY, T_OBJECT }, + { NATIVE_VariantArray, &CLASS_VariantArray, CQA_ARRAY, T_VARIANT }, + { NATIVE_LongArray, &CLASS_LongArray, CQA_ARRAY, T_LONG }, + { NATIVE_PointerArray, &CLASS_PointerArray, CQA_ARRAY, T_POINTER }, + + { NULL } +}; + + +void CLASS_init_native(void) +{ + const CLASS_INIT *init; + CLASS *class; + + /* NOTE: The 'Class' class must be first in the global class table */ + CLASS_Class = CLASS_find("Class"); + CLASS_Symbol = CLASS_find("Symbol"); + CLASS_Component = CLASS_find("Component"); + CLASS_Stream = CLASS_find("Stream"); + + //LIBRARY_Current = LIBRARY_create(NULL); + + for (init = _init_list; init->desc; init++) + { + class = CLASS_register(init->desc); + if (init->class != NULL) + *init->class = class; + if (init->array) + { + class->quick_array = init->array; + class->array_type = init->array_type; + class->is_array = init->array == CQA_ARRAY; + } + } + + CLASS_Observer->is_observer = TRUE; + CLASS_Observer->size += sizeof(OBJECT_EVENT); + //CLASS_Proxy->is_observer = TRUE; + //CLASS_Proxy->size += sizeof(OBJECT_EVENT); +} + +void CLASS_update_global(CLASS *old_class, CLASS *new_class) +{ + const CLASS_INIT *init; + + for (init = _init_list; init->desc; init++) + { + if (init->class && *init->class == old_class) + { + *init->class = new_class; + return; + } + } +} diff --git a/main/gbx/gbx_class_load.c b/main/gbx/gbx_class_load.c new file mode 100644 index 00000000..62225c97 --- /dev/null +++ b/main/gbx/gbx_class_load.c @@ -0,0 +1,1368 @@ +/*************************************************************************** + + gbx_class_load.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_LOAD_C + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_buffer.h" +#include "gb_common_swap.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include + +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_type.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gb_magic.h" +#include "gbx_stream.h" +#include "gbx_c_array.h" +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_number.h" +#include "gbx_struct.h" +#include "gbx_jit.h" + +#include "gambas.h" + +#include "gbx_class.h" + +//#define DEBUG DEBUG +//#define DEBUG_LOAD 1 +//#define DEBUG_STRUCT 1 + +static bool _load_without_init = FALSE; +static const char *_class_name; +static bool _swap; +static bool _last_ctype_is_static; + +#define _b "\x1" +#define _s "\x2" +#define _i "\x3" +#define _p "\x4" +#define _c "\x5" +#define _t "\x6" + +#ifdef DEBUG +static CLASS *Class; +static int NSection; +#endif + +static void SWAP_type(CTYPE *p) +{ + SWAP_short(&p->value); +} + + +static int align_pos(int pos, int size) +{ + switch (size) + { + case 1: + return pos; + case 2: + return (pos + 1) & ~1; + #ifndef OS_64BITS + case 4: + default: + return (pos + 3) & ~3; + #else + case 4: + return (pos + 3) & ~3; + case 8: + default: + return (pos + 7) & ~7; + #endif + } +} + + +int CLASS_sizeof_ctype(CLASS *class, CTYPE ctype) +{ + size_t size; + + if (ctype.id == TC_ARRAY) + { + CLASS_ARRAY *array = class->load->array[ctype.value]; + size = CARRAY_get_static_size(class, array); + return (size + 3) & ~3; + } + else if (ctype.id == TC_STRUCT) + { + return CSTRUCT_get_size(class->load->class_ref[ctype.value]); + } + else + return TYPE_sizeof_memory(ctype.id); +} + +TYPE CLASS_ctype_to_type(CLASS *class, CTYPE ctype) +{ + if (ctype.id == T_OBJECT && ctype.value >= 0) + return (TYPE)(class->load->class_ref[ctype.value]); + else if (ctype.id == TC_ARRAY) + return (TYPE)CARRAY_get_array_class(class, class->load->array[ctype.value]->ctype); + else if (ctype.id == TC_STRUCT) + return (TYPE)(class->load->class_ref[ctype.value]); + else + return (TYPE)(ctype.id); +} + + +static void conv_ctype(CTYPE *ctype) +{ + //if (ctype->id == TC_POINTER) + // ctype->id = T_POINTER; +} + + +static void conv_type(CLASS *class, void *ptype) +{ + CTYPE ctype = *(CTYPE *)ptype; + + if (_swap) + { + SWAP_int((int *)&ctype); + SWAP_type(&ctype); + } + + _last_ctype_is_static = CTYPE_is_static(ctype); + *((TYPE *)ptype) = CLASS_ctype_to_type(class, ctype); +} + + +static void conv_type_simple(CLASS *class, int *ptype) +{ + CTYPE ctype = *(CTYPE *)ptype; + + if (_swap) + SWAP_int((int *)&ctype); + + *ptype = ctype.id; +} + + +static void check_version(CLASS *class, int loaded) +{ + if (loaded > GAMBAS_PCODE_VERSION) + THROW_CLASS(class, "Bytecode too recent. Please upgrade Gambas.", ""); + if (loaded < GAMBAS_PCODE_VERSION_MIN) + THROW_CLASS(class, "Bytecode too old. Please recompile the project.", ""); + + class->not_3_18 = loaded < 0x3180000; +} + + +static char *get_section(char *sec_name, char **section, short *pcount, const char *desc) +{ + static void *jump_swap[] = { &&__SWAP_END, &&__SWAP_BYTE, &&__SWAP_SHORT, &&__SWAP_INT, &&__SWAP_POINTER, &&__SWAP_CTYPE, &&__SWAP_TYPE }; + static size_t sizeof_32[] = { 0, 1, 2, 4, 4, 4, 4 }; + #ifdef OS_64BITS + static void *jump_trans[] = { &&__TRANS_END, &&__TRANS_BYTE, &&__TRANS_SHORT, &&__TRANS_INT, &&__TRANS_POINTER, &&__TRANS_CTYPE, &&__TRANS_TYPE }; + static size_t sizeof_64[] = { 0, 1, 2, 4, 8, 4, 8 }; + #endif + + char *current = *section + sizeof(int); + int section_size = *((int *)(*section)); + int i; + char *p; + const char *pdesc; + short size; + size_t size_one = 0; + #ifdef OS_64BITS + size_t size_one_64 = 0; + char *alloc = NULL; + char *pa = NULL; + #endif + + if (_swap) + SWAP_int(§ion_size); + + #ifdef DEBUG + NSection++; + fprintf(stderr, "Section #%d %s %08lX %d %d\n", NSection + 1, sec_name, (int)(current - (char *)Class->data), (int)size_one, (int)section_size); + #endif + + *section += section_size + sizeof(int); + + if (desc) + { + pdesc = desc; + while (*pdesc) + { + size_one += sizeof_32[(int)*pdesc]; + #ifdef OS_64BITS + size_one_64 += sizeof_64[(int)*pdesc]; + #endif + pdesc++; + } + + if (section_size % size_one) + THROW(E_CLASS, _class_name, "Bad format in section: ", sec_name); + + size = section_size / size_one; + if (pcount) *pcount = size; + if (!size) + return NULL; + + if (_swap) + { + for (i = 0; i < size; i++) + { + p = current + i * size_one; + pdesc = desc; + + __SWAP_NEXT: + goto *jump_swap[(int)(*pdesc++)]; + + __SWAP_BYTE: + p++; + goto __SWAP_NEXT; + + __SWAP_SHORT: + SWAP_short((short *)p); + p += sizeof(short); + goto __SWAP_NEXT; + + __SWAP_INT: + __SWAP_POINTER: + SWAP_int((int *)p); + p += sizeof(int); + goto __SWAP_NEXT; + + __SWAP_CTYPE: + __SWAP_TYPE: + SWAP_type((CTYPE *)p); + p += sizeof(CTYPE); + goto __SWAP_NEXT; + + __SWAP_END: + continue; + } + } + + #ifdef OS_64BITS + + if (size_one_64 != size_one) + { + ALLOC(&alloc, size_one_64 * size); + + for (i = 0; i < size; i++) + { + p = current + i * size_one; + pa = alloc + i * size_one_64; + pdesc = desc; + + __TRANS_NEXT: + goto *jump_trans[(int)(*pdesc++)]; + + __TRANS_BYTE: + *pa++ = *p++; + goto __TRANS_NEXT; + + __TRANS_SHORT: + *((short *)pa) = *((short *)p); + pa += sizeof(short); + p += sizeof(short); + goto __TRANS_NEXT; + + __TRANS_INT: + __TRANS_CTYPE: + *((int *)pa) = *((int *)p); + pa += sizeof(int); + p += sizeof(int); + goto __TRANS_NEXT; + + __TRANS_TYPE: + *((int *)pa) = *((int *)p); + pa += sizeof(int); + p += sizeof(int); + *((int *)pa) = 0; + pa += sizeof(int); + goto __TRANS_NEXT; + + __TRANS_POINTER: + *((int64_t *)pa) = *((int *)p); + pa += sizeof(int64_t); + p += sizeof(int); + goto __TRANS_NEXT; + + __TRANS_END: + continue; + } + + return alloc; + } + + #endif + + } + + return current; +} + +#define RELOCATE(_ptr) (_ptr = (char *)&class->string[(int)(intptr_t)(_ptr)]) + +static void load_structure(CLASS *class, int *structure, int nfield) +{ + char *name; + char *field; + CLASS *sclass; + int i, pos, size, len; + CTYPE ctype; + CLASS_DESC *desc; + CLASS_VAR *var; + GLOBAL_SYMBOL *global = NULL; + + name = (char *)(intptr_t)(*structure++); + RELOCATE(name); + #if DEBUG_STRUCT + fprintf(stderr, "Loading structure %s\n", name); + #endif + + if (class->global) + sclass = CLASS_find_global(name); + else + sclass = CLASS_find(name); + + if (CLASS_is_loaded(sclass)) + { + if (!sclass->is_struct) + THROW_CLASS(class, "Class already exists: ", name); + + // Check compatibility with previous declaration + + if (sclass->load->n_dyn != nfield) + goto __MISMATCH; + + desc = (CLASS_DESC *)sclass->data; + + for (i = 0; i < nfield; i++) + { + field = (char *)(intptr_t)(*structure++); + RELOCATE(field); + len = strlen(field); + ctype = *((CTYPE *)structure); + structure++; + + if (CLASS_ctype_to_type(class, ctype) != desc[i].variable.type) + goto __MISMATCH; + + if (TABLE_compare_ignore_case_len(field, strlen(field), sclass->table[i].name, sclass->table[i].len)) + goto __MISMATCH; + } + + // OK, they are the same! + return; + } + + sclass->swap = class->swap; + sclass->component = class->component; + sclass->debug = class->debug; + + ALLOC_ZERO(&sclass->load, sizeof(CLASS_LOAD)); + + ALLOC(&var, sizeof(CLASS_VAR) * nfield); + sclass->load->dyn = var; + sclass->load->n_dyn = nfield; + sclass->load->class_ref = class->load->class_ref; + sclass->load->array = class->load->array; + + if (sclass->debug) + { + ALLOC(&global, sizeof(GLOBAL_SYMBOL) * nfield); + sclass->load->global = global; + sclass->load->n_global = nfield; + } + + sclass->n_desc = nfield; + ALLOC(&sclass->table, sizeof(CLASS_DESC_SYMBOL) * nfield); + ALLOC(&desc, sizeof(CLASS_DESC) * nfield); + sclass->data = (char *)desc; + + pos = 0; //sizeof(CSTRUCT); + + for (i = 0; i < nfield; i++) + { + field = (char *)(intptr_t)(*structure++); + RELOCATE(field); + len = strlen(field); + ctype = *((CTYPE *)structure); + structure++; + + size = CLASS_sizeof_ctype(class, ctype); + pos = align_pos(pos, size); + + desc[i].variable.name = "f"; + desc[i].variable.type = CLASS_ctype_to_type(class, ctype); + desc[i].variable.ctype = ctype; + desc[i].variable.offset = pos; // This the position relative to the data, NOT the object! + desc[i].variable.class = sclass; + + var[i].type = ctype; + var[i].pos = pos; + + if (sclass->debug) + { + global[i].sym.name = field; + global[i].sym.len = len; + global[i].ctype = ctype; + global[i].value = i; + } + + #if DEBUG_STRUCT + fprintf(stderr, " %d: %s As %s (%d) pos = %d\n", i, field, TYPE_get_name(desc[i].variable.type), CLASS_sizeof_ctype(class, ctype), pos); + #endif + + sclass->table[i].desc = &desc[i]; + sclass->table[i].name = field; + sclass->table[i].len = len; + + pos += size; //sizeof_ctype(class, var->type); + } + + #ifdef OS_64BITS + size = align_pos(pos, 8); + #else + size = align_pos(pos, 4); + #endif + + size += sizeof(CSTRUCT); + + #if DEBUG_STRUCT + fprintf(stderr, " --> size = %d\n", size); + #endif + + CLASS_calc_info(sclass, 0, size, TRUE, 0); + + CLASS_sort(sclass); + + if (sclass->debug) + sclass->load->sort = sclass->sort; + + CLASS_search_special(sclass); + + sclass->is_struct = TRUE; + + sclass->loaded = TRUE; + sclass->ready = TRUE; + return; + +__MISMATCH: + + THROW_CLASS(class, "Structure is declared elsewhere differently: ", CLASS_get_name(sclass)); +} + + +static void load_and_relocate(CLASS *class, int len_data, CLASS_DESC **pstart, int *pndesc) +{ + char *section; + CLASS_INFO *info; + CLASS_HEADER *header; + CLASS_DESC *start; + CLASS_PARAM *local; + CLASS_EVENT *event; + CLASS_EXTERN *ext; + CLASS_VAR *var; + FUNCTION *func; + FUNC_DEBUG *debug; + int i, j, pos; + int offset; + short n_desc, n_class_ref, n_unknown, n_array, n_struct; + CLASS_STRUCT *structure = NULL; + int size; + char *name; + int len; + uchar flag; + + ALLOC_ZERO(&class->load, sizeof(CLASS_LOAD)); + + /* header */ + + section = class->data; + + header = (CLASS_HEADER *)section; + section += sizeof(CLASS_HEADER); + + class->swap = header->endian != OUTPUT_ENDIAN; + _swap = class->swap; + if (_swap) + fprintf(stderr, "Swapping class %s\n", class->name); + + if (_swap) + { + SWAP_int((int *)&header->magic); + SWAP_int((int *)&header->version); + SWAP_int((int *)&header->flag); + } + + if (header->magic != OUTPUT_MAGIC) + { + int fd; + + fd = open("/tmp/gambas-bad-header.dump", O_CREAT | O_WRONLY, 0666); + if (fd >= 0) + { + if (write(fd, class->data, len_data) != len_data) + fprintf(stderr, "Cannot dump bad class file.\n"); + else + fprintf(stderr, "Bad class file dumped at /tmp/gambas-bad-header.dump\n"); + close(fd); + } + + THROW_CLASS(class, "Bad header", ""); + } + + check_version(class, header->version); + + class->debug = header->flag & CF_DEBUG; + + info = (CLASS_INFO *)get_section("info", §ion, NULL, _s _s _i _i _s _s ); + #ifdef OS_64BITS + class->load->desc = + #endif + start = (CLASS_DESC *)get_section("description", §ion, &n_desc, _p _t _p _p _p _p ); + class->load->cst = (CLASS_CONST *)get_section("constant", §ion, &class->load->n_cst, _i _p _i ); // A special process is needed later + class->load->class_ref = (CLASS **)get_section("reference", §ion, &n_class_ref, _p ); + class->load->unknown = (char **)get_section("unknown", §ion, &n_unknown, _p ); + class->load->stat = (CLASS_VAR *)get_section("static", §ion, &class->load->n_stat, _c _i ); + class->load->dyn = (CLASS_VAR *)get_section("dynamic", §ion, &class->load->n_dyn, _c _i ); + class->load->event = (CLASS_EVENT *)get_section("event", §ion, &class->n_event, _t _s _s _p _p ); + class->load->ext = (CLASS_EXTERN *)get_section("extern", §ion, &class->load->n_ext, _t _s _s _p _p _p ); + class->load->func = (FUNCTION *)get_section("function", §ion, &class->load->n_func, _t _b _b _b _b _s _s _s _s _p _p _p _p ); + #ifdef OS_64BITS + class->load->local = + #endif + local = (CLASS_PARAM *)get_section("local", §ion, NULL, _t); + class->load->array = (CLASS_ARRAY **)get_section("array", §ion, &n_array, _i); // A special process is needed later + + // Structure descriptions + + if (info->nstruct) + { + ALLOC(&structure, sizeof(CLASS_STRUCT) * info->nstruct); + for (i = 0; i < info->nstruct; i++) + { + structure[i].desc = (int *)get_section("structure", §ion, &n_struct, _i); + structure[i].nfield = (n_struct - 1) / 2; + } + } + + // Loading code + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->code = (ushort *)get_section("code", §ion, NULL, _s); + + flag = ((FUNCTION_FLAG *)func)->flag; + + func->fast = (flag & 1) != 0; + func->optional = (func->npmin < func->n_param); + func->use_is_missing = (flag & 2) != 0; + func->unsafe = (flag & 4) != 0; + func->fast_linked = FALSE; + + if (func->use_is_missing) + { + func->stack_usage++; + func->n_ctrl++; + } + + func->_reserved = 0; + } + + /* Creation flags */ + + class->auto_create = (info->flag & CI_AUTO_CREATE) != 0; + class->no_create = (info->flag & CI_NO_CREATE) != 0; + class->is_test = (info->flag & CI_TEST) != 0; + //fprintf(stderr, "%s: info->flag = %d auto_create = %d no_create = %d\n", class->name, info->flag, class->auto_create, class->no_create); + + /* Debugging information */ + + if (class->debug) + { + class->load->global = (GLOBAL_SYMBOL *)get_section("debug global", §ion, &class->load->n_global, _p _i _c _i ); + class->load->sort = (ushort *)get_section("debug global sort", §ion, NULL, _s); + #ifdef OS_64BITS + class->load->debug = + #endif + debug = (FUNC_DEBUG *)get_section("debug method", §ion, NULL, _s _s _p _p _p _s _s ); + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->debug = &debug[i]; + func->debug->index = i; + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->debug->pos = (ushort *)get_section("debug line", §ion, NULL, _s ); + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->debug->local = (LOCAL_SYMBOL *)get_section("debug local", §ion, &func->debug->n_local, _p _i _i ); + } + } + + // Profile information + + if (EXEC_profile) + ALLOC_ZERO(&class->load->prof, sizeof(uint) * (class->debug ? (1 + class->load->n_func) : 1)); + + /* Source file path, ignored now! */ + + if (class->debug) + get_section("debug file name", §ion, NULL, NULL); + + /* Strings */ + + class->string = (char *)get_section("string", §ion, NULL, NULL); + + /* Referenced classes */ + + for (i = 0; i < n_class_ref; i++) + { + offset = (int)(intptr_t)class->load->class_ref[i]; + + // The compiler does not know if an array class is global or not, we must check now. + + if (offset >= 0) + { + name = &class->string[offset]; + len = strlen(name); + + if (len >= 3 && name[len - 2] == '[') + { + do + { + len -= 2; + } + while (len >= 3 && name[len - 2] == '['); + + if (CLASS_look_global(name, len)) + offset = (- offset); + } + } + + { + CLASS *ref; + + if (offset >= 0) + { + ref = CLASS_find(&class->string[offset]); + //fprintf(stderr, "%s: %s -> %p (%s)\n", class->name, &class->string[offset], ref, ref->component ? ref->component->name : "NULL"); + } + else if (offset < -1) + { + ref = CLASS_find_global(&class->string[-offset]); + //fprintf(stderr, "%s: %s -> %p (%s)\n", class->name, &class->string[-offset], ref, ref->component ? ref->component->name : "NULL"); + } + else + ref = 0; //0x31415926; //CLASS_find(&class->string[-offset]); + + class->load->class_ref[i] = ref; + } + } + + /* Datatype conversion */ + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + + conv_type(class, &func->type); + func->is_static = _last_ctype_is_static; + + if (func->n_param > 0) + { + func->param = (CLASS_PARAM *)local; + + for (j = 0; j < func->n_param; j++) + conv_type(class, &func->param[j].type); + + local += func->n_param; + } + + if (func->n_local > 0) + { + func->local = (CLASS_LOCAL *)local; + + #ifdef OS_64BITS + // The local variable descriptions are CTYPE that are 32 bits only. + // We must transform a 64 bits integer array into a 32 bits integer array. + for (j = 0; j < func->n_local; j++) + { + func->local[j] = func->local[j * 2]; + } + #endif + + // As the 'local' section is a mix of CLASS_PARAM and CLASS_LOCAL, + // we swap endianness there and not during get_section() + + if (_swap) + { + for (j = 0; j < func->n_local; j++) + { + SWAP_int((int *)&func->local[j].type); + SWAP_type(&func->local[j].type); + } + } + + for (j = 0; j < func->n_local; j++) + conv_ctype(&func->local[j].type); + + local += func->n_local; + } + } + + /* Events information */ + + for (i = 0; i < class->n_event; i++) + { + event = &class->load->event[i]; + + conv_type(class, &event->type); + + if (event->n_param > 0) + { + event->param = (CLASS_PARAM *)local; + + for (j = 0; j < event->n_param; j++) + conv_type(class, &event->param[j].type); + + local += event->n_param; + } + } + + /* Extern calls information */ + + for (i = 0; i < class->load->n_ext; i++) + { + ext = &class->load->ext[i]; + + conv_type(class, &ext->type); + + if (ext->n_param > 0) + { + ext->param = (CLASS_PARAM *)local; + + for (j = 0; j < ext->n_param; j++) + conv_type(class, &ext->param[j].type); + + local += ext->n_param; + } + } + + /* End of file reached ? */ + + if (section != &class->data[len_data]) + { + /*printf("%d\n", &class->load[BUFFER_length(class->load)] - section);*/ + THROW_CLASS(class, "Unknown section", ""); + } + + + /* Static array definition relocation */ + + if (n_array > 0) + { + #ifdef OS_64BITS + CLASS_ARRAY **array = class->load->array; + n_array = *((int *)array) / sizeof(int); + ALLOC(&class->load->array, sizeof(void *) * n_array); + #else + n_array = *((int *)class->load->array) / sizeof(int); + #endif + + for (i = 0; i < n_array; i++) + { + #ifdef OS_64BITS + class->load->array[i] = (CLASS_ARRAY *)((char *)array + ((int *)array)[i]); + #else + class->load->array[i] = (CLASS_ARRAY *)((char *)class->load->array + ((int *)class->load->array)[i]); + #endif + //conv_type(class, &class->load->array[i]->type); + } + } + + // Create structures (we may need the structure size to compute the variable sizes) + + if (info->nstruct) + { + for (i = 0; i < info->nstruct; i++) + load_structure(class, structure[i].desc, structure[i].nfield); + FREE(&structure); + } + + // Computes and align the position of each static and dynamic variables. + // Computes the total size needed accordingly. + + #ifdef DEBUG + fprintf(stderr, "Compute variable position for %s\n", class->name); + #endif + + pos = 0; + for (i = 0; i < class->load->n_stat; i++) + { + var = &class->load->stat[i]; + conv_ctype(&var->type); + size = CLASS_sizeof_ctype(class, var->type); + pos = align_pos(pos, size); + var->pos = pos; + #ifdef DEBUG + fprintf(stderr, "Static #%d: %d\n", i, var->pos); + #endif + pos += size; + } + #ifdef OS_64BITS + info->s_static = align_pos(pos, 8); + #else + info->s_static = align_pos(pos, 4); + #endif + + pos = 0; + for (i = 0; i < class->load->n_dyn; i++) + { + var = &class->load->dyn[i]; + conv_ctype(&var->type); + size = CLASS_sizeof_ctype(class, var->type); + pos = align_pos(pos, size); + var->pos = pos; + #ifdef DEBUG + fprintf(stderr, "Dynamic #%d: %d\n", i, var->pos); + #endif + pos += size; //sizeof_ctype(class, var->type); + } + #ifdef OS_64BITS + info->s_dynamic = align_pos(pos, 8); + #else + info->s_dynamic = align_pos(pos, 4); + #endif + + /* String relocation */ + + for (i = 0; i < n_desc; i++) + RELOCATE(start[i].gambas.name); + + for (i = 0; i < n_unknown; i++) + RELOCATE(class->load->unknown[i]); + + for (i = 0; i < class->n_event; i++) + RELOCATE(class->load->event[i].name); + + for (i = 0; i < class->load->n_ext; i++) + { + RELOCATE(class->load->ext[i].library); + RELOCATE(class->load->ext[i].alias); + } + + if (class->debug) + { + for (i = 0; i < class->load->n_global; i++) + { + RELOCATE(class->load->global[i].sym.name); + /*conv_type(class, &(class->load->global[i].type));*/ + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + RELOCATE(func->debug->name); + + for (j = 0; j < func->debug->n_local; j++) + RELOCATE(func->debug->local[j].sym.name); + } + } + + /* Inheritance */ + + if (info->parent >= 0) + { + //printf("%s inherits %s\n", class->name, (class->load->class_ref[info->parent])->name); + CLASS_inheritance(class, class->load->class_ref[info->parent]); + } + + /* If there is no dynamic variable, then the class is not instanciable */ + //if (info->s_dynamic == 0) + // class->no_create = TRUE; + + /* Class size and offsets */ + + CLASS_calc_info(class, class->n_event, info->s_dynamic, FALSE, info->s_static); + + *pstart = start; + *pndesc = n_desc; +} + + +static void load_without_inits(CLASS *class) +{ + int i; + FUNCTION *func; + CLASS_DESC *desc; + CLASS_CONST *cc; + CLASS_VAR *var; + int len; + CLASS_EVENT *event; + CLASS_EXTERN *ext; + VALUE value; + int len_data; + int n_desc; + int offset; + int first; + int first_event; + COMPONENT *save; + CLASS_DESC *start; + char kind; + ARCHIVE *arch; + char *file_name; + + //size_t alloc = MEMORY_size; + + if (CLASS_is_loaded(class)) + return; + + if (class->error) + THROW_CLASS(class, "Loading has already failed", ""); + + if (CLASS_exiting) + THROW_CLASS(class, "Program is exiting", ""); + + class->error = TRUE; + + #if DEBUG_LOAD + fprintf(stderr, "Loading class %s (%p)...\n", class->name, class); + #endif + + #ifdef DEBUG + Class = class; + NSection = 0; + #endif + + _class_name = class->name; + + if (class->in_load) + THROW_CLASS(class, "Circular reference", ""); + + if (!class->component) + { + if (CP) + { + class->component = CP->component; + #if DEBUG_COMP + fprintf(stderr, "Load class %s -> component %s from CP\n", class->name, class->component ? class->component->name : "NULL"); + #endif + } + else + { + class->component = COMPONENT_current; + #if DEBUG_COMP + fprintf(stderr, "Load class %s -> component %s from COMPONENT_current\n", class->name, class->component ? class->component->name : "NULL"); + #endif + } + } + + #if DEBUG_COMP + if (class->component) + fprintf(stderr, "Load class %s -> component %s\n", class->name, class->component->name); + else + fprintf(stderr, "Load class %s -> no component\n", class->name); + #endif + + save = COMPONENT_current; + COMPONENT_current = class->component; + #if DEBUG_LOAD + fprintf(stderr, "COMPONENT_current = %s\n", COMPONENT_current ? COMPONENT_current->name : "NULL"); + #endif + + file_name = class->data ? class->data : class->name; + len = strlen(file_name); + + { + char name[len + 9]; + char *p; + + strcpy(name, ".gambas/"); + p = &name[8]; + + for (i = 0; i < len; i++) + *p++ = toupper(file_name[i]); + *p = 0; + + if (class->data) + IFREE(class->data); + + TRY + { + //class->mmapped = !STREAM_map(name, &class->data, &len_data); + STREAM_load(name, &class->data, &len_data); + } + CATCH + { + COMPONENT_current = save; + THROW_CLASS(class, ERROR_last.msg, ""); + } + END_TRY + } + + COMPONENT_current = save; + #if DEBUG_LOAD + fprintf(stderr, "COMPONENT_current = %s\n", COMPONENT_current ? COMPONENT_current->name : "NULL"); + #endif + + class->in_load = TRUE; + + class->init_dynamic = TRUE; + + load_and_relocate(class, len_data, &start, &n_desc); + + // Information on static and dynamic variables + + if (class->parent) + offset = class->parent->off_event; + else + offset = sizeof(OBJECT); + + for (i = 0; i < class->load->n_dyn; i++) + { + var = &class->load->dyn[i]; + var->pos += offset; + } + + // Constant conversion & relocation + + for (i = 0; i < class->load->n_cst; i++) + { + cc = &class->load->cst[i]; + conv_type_simple(class, &(cc->type)); + + switch (cc->type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: + #ifdef OS_64BITS + // Special process for integer constants + cc->_integer.value = (int)(intptr_t)cc->_string.addr; + #endif + break; + + case T_LONG: + #ifdef OS_64BITS + // Special process for long constants: the first 32 bits part of the LONG constant + // has been extended to 64 bits + //cc->_swap.val[0] = (int)(*((int64_t *)(void *)&cc->_string.addr)); + cc->_swap.val[0] = (int)(intptr_t)cc->_string.addr; + cc->_swap.val[1] = cc->_string.len; + #endif + /* + The two 32 bits parts of the LONG value have been already swapped independently. + So we just have to swap the two parts again. + */ + if (_swap) + { + int val; + + val = cc->_swap.val[0]; + cc->_swap.val[0] = cc->_swap.val[1]; + cc->_swap.val[1] = val; + } + break; + + case T_STRING: case T_CSTRING: + if (cc->_string.len) + cc->_string.addr += (intptr_t)class->string; + break; + + case T_FLOAT: case T_SINGLE: + cc->_string.addr += (intptr_t)class->string; + if (NUMBER_from_string(NB_READ_FLOAT, cc->_string.addr, strlen(cc->_string.addr), &value)) + THROW_CLASS(class, "Bad constant", ""); + if (cc->type == T_SINGLE) + cc->_single.value = (float)value._float.value; + else + cc->_float.value = value._float.value; + break; + } + } + + // Event description + + CLASS_make_event(class, &first_event); + + if (class->free_event && class->n_event > first_event) + memcpy(&class->event[first_event], class->load->event, (class->n_event - first_event) * sizeof(CLASS_EVENT)); + + // Class public description + + for (i = 0; i < n_desc; i++) + { + desc = &start[i]; //class->table[i].desc; + + //desc->gambas.name = (char *)CLASS_DESC_get_type_name(desc); + + conv_type(class, &desc->gambas.type); + + kind = *CLASS_DESC_get_type_name(desc); + + if (!desc->gambas.val1 && index(CD_CALL_SOMETHING_LIST, kind) != NULL) + fprintf(stderr, "load_without_inits: '%s.%s' gambas.val1 == 0\n", class->name, desc->gambas.name); + + switch (kind) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + func = &class->load->func[desc->gambas.val1]; + desc->method.exec = (void (*)())desc->gambas.val1; + desc->method.npmin = func->npmin; + desc->method.npmax = func->n_param; + desc->method.npvar = func->vararg; + desc->method.signature = (TYPE *)func->param; + //desc->method.help = NULL; + desc->method.native = FALSE; + + break; + + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + case CD_PROPERTY_WRITE: + case CD_STATIC_PROPERTY_WRITE: + + desc->property.read = (void (*)())desc->gambas.val1; + desc->property.write = (void (*)())desc->gambas.val2; + //if ((intptr_t)desc->property.write == -1L) + // desc->gambas.name = *desc->gambas.name == 'p' ? "r" : "R"; + desc->property.native = FALSE; + + break; + + case CD_VARIABLE: + case CD_STATIC_VARIABLE: + + if (kind == CD_STATIC_VARIABLE) + var = &class->load->stat[desc->gambas.val1]; + else + var = &class->load->dyn[desc->gambas.val1]; + + desc->variable.ctype = var->type; + desc->variable.offset = var->pos; + + break; + + case CD_CONSTANT: + + cc = &class->load->cst[desc->gambas.val1]; + + if (TYPE_is_integer(desc->constant.type)) + desc->constant.value._integer = cc->_integer.value; + else if (desc->constant.type == T_FLOAT) + desc->constant.value._float = cc->_float.value; + else if (desc->constant.type == T_LONG) + desc->constant.value._long = cc->_long.value; + else if (desc->constant.type == T_SINGLE) + desc->constant.value._single = cc->_single.value; + else + { + desc->constant.type = T_CSTRING; + desc->constant.value._string = cc->_string.addr; + desc->constant.translate = (cc->type == T_CSTRING); + } + + break; + + case CD_EVENT: + + //fprintf(stderr, "event %s.%s: %d %d\n", class->name, desc->event.name, first_event, (int)desc->event.index); + + event = &class->load->event[desc->event.index]; + if (class->parent) + desc->event.index += class->parent->n_event; + desc->event.npmin = event->n_param; + desc->event.npmax = event->n_param; + desc->event.signature = (TYPE *)event->param; + //desc->event.help = NULL; + //desc->event.index = first_event++; + break; + + case CD_EXTERN: + + ext = &class->load->ext[desc->gambas.val1]; + desc->ext.npmin = ext->n_param; + desc->ext.npmax = ext->n_param; + desc->ext.npmin = ext->n_param; + desc->ext.signature = (TYPE *)ext->param; + //desc->event.help = NULL; + break; + + default: + + THROW_CLASS(class, "Bad description", ""); + } + } + + // Inheritance + + CLASS_make_description(class, start, n_desc, &first); + + // Transfer symbol kind into symbol name (which is stored in the symbol table now), like native classes + // Define event index + + for (i = 0; i < n_desc; i++) + { + desc = &start[i]; + desc->gambas.name = (char *)CLASS_DESC_get_type_name(desc); + desc->method.class = class; + } + + // Sort the class description + + CLASS_sort(class); + + // Special methods + + CLASS_search_special(class); + + // Class is loaded... + + class->in_load = FALSE; + + // ...and usable ! + + class->loaded = TRUE; + class->error = FALSE; + + // JIT compilation + + arch = class->component ? class->component->archive : NULL; + + if (JIT_can_compile(arch)) + { + for (i = 0; i < class->load->n_func; i++) + { + if (class->load->func[i].fast) + { + JIT_compile(arch); + break; + } + } + } + + // Init breakpoints + + if (EXEC_debug) + DEBUG.InitBreakpoints(class); + + //total += MEMORY_size - alloc; + //printf("%s: %d TOTAL = %d\n", class->name, MEMORY_size - alloc, total); +} + +#if 0 +void CLASS_load_without_init(CLASS *class) +{ + load_without_inits(class, FALSE); + + /* Call the static initializer */ + + EXEC.native = FALSE; + EXEC.class = class; + EXEC.object = NULL; + EXEC.nparam = 0; + EXEC.index = FUNC_INIT_STATIC; + //EXEC.func = &class->load->func[FUNC_INIT_STATIC]; + + EXEC_function(); +} +#endif + +void CLASS_run_inits(CLASS *class) +{ + COMPONENT *current = COMPONENT_current; + COMPONENT_current = NULL; + /* Call the static initializer */ + + EXEC.native = FALSE; + EXEC.class = class; + EXEC.object = NULL; + EXEC.nparam = 0; + EXEC.index = FUNC_INIT_STATIC; + //EXEC.func = &class->load->func[FUNC_INIT_STATIC]; + + EXEC_function(); + + /* _init */ + EXEC_public(class, NULL, "_init", 0); + + EXEC_public(class, NULL, "_lang", 0); + + COMPONENT_current = current; +} + + +void CLASS_load_real(CLASS *class) +{ + bool load_without_init = _load_without_init; + char *name = class->name; + int len = strlen(name); + + if (!CLASS_is_loaded(class)) + { + if (len >= 3 && name[len - 2] == '[' && name[len - 1] == ']' && !class->array_type) + { + CLASS_create_array_class(class); + return; + } + } + + load_without_inits(class); + class->loaded = TRUE; + class->ready = FALSE; + + if (load_without_init) + { + _load_without_init = FALSE; + return; + } + + class->ready = TRUE; + CLASS_run_inits(class); +} + +void CLASS_load_without_init(CLASS *class) +{ + if (CLASS_is_loaded(class)) + return; + + _load_without_init = TRUE; + CLASS_load_real(class); +} diff --git a/main/gbx/gbx_class_load.h b/main/gbx/gbx_class_load.h new file mode 100644 index 00000000..f9d51998 --- /dev/null +++ b/main/gbx/gbx_class_load.h @@ -0,0 +1,208 @@ +/*************************************************************************** + + gbx_class_load.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_CLASS_LOAD_H +#define __GBX_CLASS_LOAD_H + +#include "gb_common.h" + +#ifdef OS_64BITS + +typedef + uint32_t ptr32_t; + +typedef + struct { + ptr32_t name; + int len; + } + PACKED + SYMBOL_32; + +typedef + struct { + CTYPE type; + int pos; + } + PACKED + CLASS_VAR_32; + +typedef + uint32_t TYPE_32; + +typedef + union { + TYPE_32 type; + struct { TYPE_32 type; double value; } PACKED _float; + struct { TYPE_32 type; float value; } PACKED _single; + struct { TYPE_32 type; int value; } PACKED _integer; + struct { TYPE_32 type; int64_t value; } PACKED _long; + struct { TYPE_32 type; ptr32_t addr; int len; } PACKED _string; + struct { TYPE_32 type; int val[2]; } PACKED _swap; + } + PACKED + CLASS_CONST_32; + +typedef + ptr32_t CLASS_REF_32; + +typedef + ptr32_t CLASS_UNKNOWN_32; + +typedef + struct { + CTYPE type; + } + PACKED + CLASS_LOCAL_32; + +typedef + struct { + TYPE_32 type; + } + PACKED + CLASS_PARAM_32; + +typedef + struct { + SYMBOL_32 sym; + int value; + } + LOCAL_SYMBOL_32; + +typedef + struct { + unsigned short line; + unsigned short nline; + unsigned short *pos; + ptr32_t name; + ptr32_t local; // LOCAL_SYMBOL_32 + short n_local; + unsigned short _reserved; + } + PACKED + FUNC_DEBUG_32; + +typedef + struct { + TYPE type; + char n_param; + char npmin; + char vararg; + char flag; + short n_local; + short n_ctrl; + short stack_usage; + short error; + ptr32_t code; // unsigned short * + ptr32_t param; // CLASS_PARAM + ptr32_t local; // CLASS_LOCAL + ptr32_t debug; // FUNC_DEBUG + } + PACKED + FUNCTION_32; + +typedef + struct { + TYPE_32 type; + short n_param; + short _reserved; + ptr32_t param; // CLASS_PARAM + ptr32_t name; + } + PACKED + CLASS_EVENT_32; + +typedef + struct { + TYPE_32 type; + short n_param; + unsigned loaded : 1; + unsigned _reserved : 15; + ptr32_t param; // CLASS_PARAM + ptr32_t alias; + ptr32_t library; + } + PACKED + CLASS_EXTERN_32; + +typedef + struct { + TYPE_32 type; + int dim[0]; + } + CLASS_ARRAY_32; + +typedef + CLASS_ARRAY_32 *CLASS_ARRAY_P_32; + +struct _CLASS; + +typedef + struct { + SYMBOL_32 sym; + CTYPE ctype; + int value; + } + GLOBAL_SYMBOL_32; + +typedef + union { + ptr32_t name; + TYPE_32 type; + ptr32_t val1; + ptr32_t val2; + ptr32_t val3; + ptr32_t val4; + } + PACKED + CLASS_DESC_GAMBAS_32; + +typedef + union { + CLASS_DESC_GAMBAS_32 gambas; + } + PACKED + CLASS_DESC_32; + + +typedef + struct { + CLASS_DESC_32 *desc; + CLASS_CONST_32 *cst; + CLASS_VAR_32 *stat; + CLASS_VAR_32 *dyn; + FUNCTION_32 *func; + CLASS_EVENT_32 *event; + CLASS_EXTERN_32 *ext; + CLASS_ARRAY_32 **array; + struct _CLASS **class_ref; + char **unknown; + GLOBAL_SYMBOL_32 *global; + } + PACKED + CLASS_LOAD_32; + +#endif + +#endif diff --git a/main/gbx/gbx_class_native.c b/main/gbx/gbx_class_native.c new file mode 100644 index 00000000..380de960 --- /dev/null +++ b/main/gbx/gbx_class_native.c @@ -0,0 +1,358 @@ +/*************************************************************************** + + gbx_class_native.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_NATIVE_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include + +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_type.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gb_magic.h" +#include "gbx_stream.h" + +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_number.h" + +#include "gambas.h" +#include "gbx_api.h" + +#include "gbx_class.h" + +//extern long total; + +BEGIN_PROPERTY(class_self_property) + + GB_ReturnSelf(_object); + +END_PROPERTY + +CLASS *CLASS_register_class(GB_DESC *ptr, CLASS *class) +{ + int i, n_desc; + + CLASS_DESC *desc; + CLASS_DESC *start; + CLASS_DESC_GAMBAS *gambas = (CLASS_DESC_GAMBAS *)ptr; + + CLASS_EVENT *event; + const char *ptype; + //const char *type; + int first_event, nsign; + TYPE *sign; + int first; + int size_dynamic; + VALUE value; + + #if DEBUG_LOAD + fprintf(stderr, "Registering native class %s (%p)...\n", class->name, class); + #endif + + if (gambas->type != GB_VERSION) + return NULL; + + if (class->error) + THROW_CLASS(class, "Loading has already failed", ""); + + class->error = TRUE; + + class->is_native = TRUE; + class->load = NULL; + class->data = NULL; + class->component = COMPONENT_current; + + #if DEBUG_COMP + if (class->component) + fprintf(stderr, "class %s -> component %s\n", class->name, class->component->name); + else + fprintf(stderr, "class %s -> no component\n", class->name); + #endif + + class->is_virtual = *class->name == '.'; + + #ifdef OS_64BITS + size_dynamic = (gambas->val1 + 7) & ~7; + #else + size_dynamic = (gambas->val1 + 3) & ~3; + #endif + + class->n_desc = 0; + class->n_event = 0; + nsign = 0; + + /* Read the class global information at the beginning of the description */ + + desc = (CLASS_DESC *)&gambas[1]; + + for (start = NULL; start == NULL; desc++) + { + if (desc->gambas.name == NULL) + { + start = desc; + break; + } + + switch ((intptr_t)desc->gambas.name) + { + case (intptr_t)GB_INHERITS_ID: + CLASS_inheritance(class, CLASS_find((const char *)desc->gambas.type)); + break; + + case (intptr_t)GB_AUTO_CREATABLE_ID: + class->auto_create = TRUE; + break; + + case (intptr_t)GB_NOT_CREATABLE_ID: + class->no_create = TRUE; + break; + + case (intptr_t)GB_VIRTUAL_CLASS_ID: + class->no_create = TRUE; + class->is_virtual = TRUE; + break; + + case (intptr_t)GB_HOOK_CHECK_ID: + class->check = (int (*)())(desc->hook.func); + class->must_check = TRUE; + break; + + default: + start = desc; + break; + } + } + + /* If there is a parent class, and if the size is zero, then inherits the size */ + + if (class->parent && size_dynamic == 0) + size_dynamic = class->parent->size; + + /* Compute the number of symbol description */ + + for(desc = start, n_desc = 0; desc->gambas.name != NULL; desc++, n_desc++); + + /* Description analysis */ + + for (i = 0; i < n_desc; i++) + { + desc = &start[i]; + + ptype = (char *)desc->gambas.type; + desc->gambas.type = TYPE_from_string(&ptype); + + switch (CLASS_DESC_get_type(desc)) + { + case CD_CONSTANT: + + if (desc->constant.type == T_INTEGER) + { + desc->constant.value._integer = (int)desc->constant.value._long; + } + else if (desc->constant.type == T_STRING) + desc->constant.type = T_CSTRING; + else if (desc->constant.type == T_FLOAT || desc->constant.type == T_SINGLE) + { + if (desc->gambas.val1 == 0) + { + value._float.value = desc->gambas.val3._double; + } + else + { + if (NUMBER_from_string(NB_READ_FLOAT, desc->constant.value._string, strlen(desc->constant.value._string), &value)) + THROW_CLASS(class, "Bad constant", ""); + } + + if (desc->constant.type == T_SINGLE) + desc->constant.value._single = (float)value._float.value; + else + desc->constant.value._float = value._float.value; + } + break; + + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + case CD_PROPERTY_WRITE: + case CD_STATIC_PROPERTY_WRITE: + + if ((intptr_t)desc->property.read == CLASS_DESC_SELF) + desc->property.read = (void (*)())class_self_property; + + desc->property.write = desc->property.read; + + desc->property.native = TRUE; + //desc->property.help = (char *)type; + break; + + case CD_METHOD: + case CD_STATIC_METHOD: + + //desc->method.help = (char *)desc->method.signature; + desc->method.native = TRUE; + desc->method.subr = desc->gambas.name[1] == '!'; + + if (desc->method.signature) + { + TYPE_signature_length((char *)desc->method.signature, &desc->method.npmin, &desc->method.npmax, &desc->method.npvar); + nsign += desc->method.npmax; + } + + break; + + case CD_EVENT: + + class->n_event++; + + //desc->event.help = (char *)desc->event.signature; + + if (desc->event.signature) + { + TYPE_signature_length((char *)desc->event.signature, &desc->event.npmin, &desc->event.npmax, &desc->method.npvar); + desc->event.npmin = desc->event.npmax; + nsign += desc->event.npmax; + } + + break; + } + + desc->method.class = class; + } + + CLASS_calc_info(class, class->n_event, size_dynamic, TRUE, 0); + + // Inheritance + + CLASS_make_description(class, start, n_desc, &first); + + CLASS_make_event(class, &first_event); + + // Transfer events and signatures + + if (nsign) + { + ALLOC(&class->signature, sizeof(TYPE) * nsign); + sign = class->signature; + + for (i = first; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + //fprintf(stderr, "[%.*s]\n", class->table[i].len, class->table[i].name); + + switch (CLASS_DESC_get_type(desc)) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + if (desc->method.npmax) + { + desc->method.signature = + TYPE_transform_signature(&sign, (char *)desc->method.signature, desc->method.npmax); + } + break; + + case CD_EVENT: + + if (desc->event.npmax) + { + desc->event.signature = + TYPE_transform_signature(&sign, (char *)desc->event.signature, desc->event.npmax); + } + + break; + } + } + } + + if (class->n_event && (!class->parent || class->n_event > class->parent->n_event)) + { + for (i = first; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_EVENT: + + event = &class->event[first_event]; + event->name = class->table[i].name; + if (desc->event.index) + *((int *)desc->event.index) = first_event; + desc->event.index = first_event; + + event->type = desc->event.type; + event->param = (CLASS_PARAM *)desc->event.signature; + event->n_param = desc->event.npmax; + + first_event++; + + break; + } + } + } + + /* Sort the class description */ + + CLASS_sort(class); + + /* Search for special methods */ + + CLASS_search_special(class); + + /* Class is loaded */ + + class->loaded = TRUE; + class->error = FALSE; + + /* Run the static initializer */ + + EXEC_public(class, NULL, "_init", 0); + + /* Class is ready */ + + class->ready = TRUE; + + //total += MEMORY_size - alloc; + //printf("%s: %d TOTAL = %d\n", class->name, MEMORY_size - alloc, total); + + return class; +} + + +CLASS *CLASS_register(GB_DESC *ptr) +{ + const char *name = ((CLASS_DESC_GAMBAS *)ptr)->name; + return CLASS_register_class(ptr, CLASS_check_global(CLASS_find_global(name))); +} + diff --git a/main/gbx/gbx_compare.c b/main/gbx/gbx_compare.c new file mode 100644 index 00000000..20075b1b --- /dev/null +++ b/main/gbx/gbx_compare.c @@ -0,0 +1,690 @@ +/*************************************************************************** + + gbx_compare.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_COMPARE_C + +#include "gb_common.h" +#include "gb_common_case.h" + +#include +#include +#include + +#include "gbx_type.h" +#include "gbx_compare.h" +#include "gbx_date.h" +#include "gbx_object.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_regexp.h" +#include "gbx_c_string.h" + + +static bool _descent = FALSE; +static int _mode = 0; + +int compare_nothing(void *a, void *b) +{ + return 0; +} + +int compare_integer(int *a, int *b) +{ + int comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + if (_descent) + comp = -comp; + + return comp; +} + +int compare_short(short *a, short *b) +{ + int comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_byte(unsigned char *a, unsigned char *b) +{ + int comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_long(int64_t *a, int64_t *b) +{ + int comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_float(double *a, double *b) +{ + int comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_single(float *a, float *b) +{ + int comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_date(DATE *a, DATE *b) +{ + int comp; + + comp = DATE_comp(a, b); + + return _descent ? (-comp) : comp; +} + +int COMPARE_string_lang(const char *s1, int l1, const char *s2, int l2, bool nocase, bool throw) +{ + wchar_t *t1 = NULL; + wchar_t *t2 = NULL; + int i, cmp; + int lt1, lt2; + + if (l1 < 0) + l1 = s1 ? strlen(s1) : 0; + + if (l2 < 0) + l2 = s2 ? strlen(s2) : 0; + + if (l1 == 0) + { + if (l2 == 0) + return 0; + else + return (-1); + } + else if (l2 == 0) + return 1; + + if (STRING_convert_to_unicode(&t1, <1, s1, l1) + || STRING_convert_to_unicode(&t2, <2, s2, l2)) + { + if (throw) + THROW(E_CONV); + else + goto __FAILED; + } + + if (nocase) + { + for (i = 0; i < lt1; i++) + t1[i] = towlower(t1[i]); + for (i = 0; i < lt2; i++) + t2[i] = towlower(t2[i]); + } + + errno = 0; + cmp = wcscoll(t1, t2); + if (!errno) + return (cmp < 0) ? - 1 : (cmp > 0) ? 1 : 0; + +__FAILED: + + return nocase ? TABLE_compare_ignore_case(s1, l1, s2, l2) : TABLE_compare(s1, l1, s2, l2); +} + +/* + Natural sort order. + Based on the algorithm made by Martin Pol (http://sourcefrog.net/projects/natsort/) + + This software is copyright by Martin Pool, and made available under the same + licence as zlib: + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the use + of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be appreciated but + is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ + +static int strnatcmp_compare_right(const char *a, int la, const char *b, int lb) +{ + int bias = 0; + unsigned char ca, cb; + + /* The longest run of digits wins. That aside, the greatest + value wins, but we can't know that it will until we've scanned + both numbers to know that they have the same magnitude, so we + remember it in BIAS. */ + + for (;; a++, b++, la--, lb--) + { + ca = (la > 0) ? *a : 0; + cb = (lb > 0) ? *b : 0; + + if (!isdigit(ca) && !isdigit(cb)) + return bias; + else if (!isdigit(ca)) + return -1; + else if (!isdigit(cb)) + return +1; + else if (ca < cb) + { + if (!bias) + bias = -1; + } + else if (ca > cb) + { + if (!bias) + bias = +1; + } + else if (!ca) // && !cb) + return bias; + } + + return 0; +} + + +static int strnatcmp_compare_left(const char *a, int la, const char *b, int lb) +{ + unsigned char ca, cb; + + /* Compare two left-aligned numbers: the first to have a + different value wins. */ + for (;; a++, b++, la--, lb--) + { + ca = (la > 0) ? *a : 0; + cb = (lb > 0) ? *b : 0; + + if (!isdigit(ca) && !isdigit(cb)) + return 0; + else if (!isdigit(ca)) + return -1; + else if (!isdigit(cb)) + return +1; + else if (ca < cb) + return -1; + else if (ca > cb) + return +1; + } + + return 0; +} + +int COMPARE_string_natural(const char *a, int la, const char *b, int lb, bool nocase) +{ + int ai, bi, lca, lcb; + unsigned char ca, cb; + int fractional, result; + + ai = bi = 0; + + for(;;) + { + for(;;) + { + if (ai >= la) + { + ca = 0; + break; + } + ca = a[ai]; + if (ca > ' ') + break; + ai++; + } + + for(;;) + { + if (bi >= lb) + { + cb = 0; + break; + } + cb = b[bi]; + if (cb > ' ') + break; + bi++; + } + + /* process run of digits */ + if (ca >= '0' && ca <= '9' && cb >= '0' && cb <= '9') + { + fractional = (ca == '0' || cb == '0'); + + if (fractional) + { + if ((result = strnatcmp_compare_left(a+ai, la-ai, b+bi, lb-bi)) != 0) + return result; + } + else + { + if ((result = strnatcmp_compare_right(a+ai, la-ai, b+bi, lb-bi)) != 0) + return result; + } + } + + if (!ca) + { + if (!cb) + return 0; + else + return -1; + } + else if (!cb) + return 1; + + lca = STRING_utf8_get_char_length(ca); + lcb = STRING_utf8_get_char_length(cb); + if (lca > 1 || lcb > 1) + { + if ((result = COMPARE_string_lang(&a[ai], lca, &b[bi], lcb, nocase, FALSE))) + return result; + ai += lca; + bi += lcb; + } + else + { + if (nocase) + { + ca = tolower(ca); + cb = tolower(cb); + } + + if (ca < cb) + return -1; + else if (ca > cb) + return +1; + ++ai; ++bi; + } + } +} + + +/*#define IMPLEMENT_COMPARE_STRING(_name, _func) \ +int compare_string_##_name(char **pa, char **pb) \ +{ \ + char *a; \ + char *b; \ + int comp; \ + \ + a = *pa; \ + if (!a) \ + a = ""; \ + \ + b = *pb; \ + if (!b) \ + b = ""; \ + \ + comp = _func(a, b); \ + if (_descent) \ + comp = -comp; \ + return comp; \ +} + +IMPLEMENT_COMPARE_STRING(binary, strcmp) +IMPLEMENT_COMPARE_STRING(case, strcasecmp)*/ + +static int compare_string_binary(char **pa, char **pb) +{ + int comp = TABLE_compare(*pa, STRING_length(*pa), *pb, STRING_length(*pb)); + return _descent ? -comp : comp; +} + +static int compare_string_case(char **pa, char **pb) +{ + int comp = TABLE_compare_ignore_case(*pa, STRING_length(*pa), *pb, STRING_length(*pb)); + return _descent ? -comp : comp; +} + +static int compare_string_lang(char **pa, char **pb) +{ + int comp = COMPARE_string_lang(*pa, STRING_length(*pa), *pb, STRING_length(*pb), FALSE, TRUE); + return _descent ? (-comp) : comp; +} + +static int compare_string_lang_case(char **pa, char **pb) +{ + int comp = COMPARE_string_lang(*pa, STRING_length(*pa), *pb, STRING_length(*pb), TRUE, TRUE); + return _descent ? (-comp) : comp; +} + +int COMPARE_string_like(const char *s1, int l1, const char *s2, int l2, bool nocase) +{ + int result; + + if (nocase) + { + if (REGEXP_match_pcre(s2, l2, s1, l1)) + return 0; + } + else + { + if (REGEXP_match(s2, l2, s1, l1)) + return 0; + } + result = TABLE_compare_ignore_case(s1, l1, s2, l2); + return (result < 0) ? -1 : (result > 0) ? 1 : 0; +} + +static int compare_string_like(char **pa, char **pb) +{ + int comp = COMPARE_string_like(*pa, STRING_length(*pa), *pb, STRING_length(*pb), FALSE); + return _descent ? (-comp) : comp; +} + +static int compare_string_match(char **pa, char **pb) +{ + int comp = COMPARE_string_like(*pa, STRING_length(*pa), *pb, STRING_length(*pb), TRUE); + return _descent ? (-comp) : comp; +} + +/*#define IMPLEMENT_COMPARE_STRING_CASE(_name, _nocase), _func \ +static int compare_string_##_name(char **pa, char **pb) \ +{ \ + int la = *pa ? strlen(*pa) : 0; \ + int lb = *pb ? strlen(*pb) : 0; \ + int diff = _func(*pa, la, *pb, lb, _nocase); \ + if (_descent) \ + return (-diff); \ + else \ + return diff; \ +} + +IMPLEMENT_COMPARE_STRING_CASE(like, FALSE, COMPARE_string_like) +IMPLEMENT_COMPARE_STRING_CASE(match, TRUE, COMPARE_string_like) +IMPLEMENT_COMPARE_STRING_CASE(natural, FALSE, COMPARE_string_natural) +IMPLEMENT_COMPARE_STRING_CASE(natural_case, TRUE, COMPARE_string_natural)*/ + +static int compare_string_natural(char **pa, char **pb) +{ + int comp = COMPARE_string_natural(*pa, STRING_length(*pa), *pb, STRING_length(*pb), FALSE); + return _descent ? (-comp) : comp; +} + +static int compare_string_natural_case(char **pa, char **pb) +{ + int comp = COMPARE_string_natural(*pa, STRING_length(*pa), *pb, STRING_length(*pb), TRUE); + return _descent ? (-comp) : comp; +} + + +int COMPARE_object(void **a, void **b) +{ + int comp; + bool desc = _descent; + CLASS *ca, *cb; + + /*{ + STACK_BACKTRACE *bt = STACK_get_backtrace(); + fprintf(stderr, "COMPARE_object\n"); + DEBUG_print_backtrace(bt); + STACK_free_backtrace(&bt); + }*/ + + ca = OBJECT_class_null(*a); + cb = OBJECT_class_null(*b); + + if (ca && cb) + { + if (ca->has_operators && CLASS_has_operator(ca, CO_COMP) && ca == cb) + { + void *func = ca->operators[CO_COMP]; + comp = (*(int (*)(void *, void *))func)(*a, *b); + goto __RETURN; + } + + if (ca->special[SPEC_COMPARE] != NO_SYMBOL) + { + STACK_check(1); + PUSH_OBJECT(cb, *b); + EXEC_special(SPEC_COMPARE, ca, *a, 1, FALSE); + VALUE_conv_integer(&SP[-1]); + SP--; + comp = SP->_integer.value; + goto __RETURN; + } + + if (cb->special[SPEC_COMPARE] != NO_SYMBOL) + { + STACK_check(1); + PUSH_OBJECT(ca, *a); + EXEC_special(SPEC_COMPARE, cb, *b, 1, FALSE); + VALUE_conv_integer(&SP[-1]); + SP--; + comp = (- SP->_integer.value); + goto __RETURN; + } + + _descent = desc; + } + + comp = (*a == *b) ? 0 : (*a > *b) ? 1 : -1; + +__RETURN: + + return desc ? (-comp) : comp; +} + +int COMPARE_variant(VARIANT *a, VARIANT *b) +{ + TYPE type; + VALUE value; + int comp; + + if (a->type == T_NULL) + return b->type == T_NULL ? 0 : -1; + else if (b->type == T_NULL) + return 1; + + if (TYPE_is_object(a->type)) + { + if (TYPE_is_object(b->type)) + return COMPARE_object(&a->value._object, &b->value._object); + else + return 1; + } + else if (TYPE_is_object(b->type)) + return -1; + + if (a->type == b->type) + return (*COMPARE_get_func(a->type, -1))(&a->value, &b->value); + + type = Max(a->type, b->type); + + if (b->type == type) + { + VARIANT *c; + + c = a; + a = b; + b = c; + _descent = !_descent; + } + + value.type = T_VARIANT; + value._variant.vtype = b->type; + value._variant.value.data = b->value.data; + + BORROW(&value); + VALUE_conv(&value, type); + VALUE_conv_variant(&value); + comp = (*COMPARE_get_func(type, -1))(&a->value, &value._variant.value); + RELEASE(&value); + + return comp; +} + +static COMPARE_FUNC _string_func[] = { + /* 0 */ compare_string_binary, + /* 1 */ compare_string_case, + /* 2 */ compare_string_lang, + /* 3 */ compare_string_lang_case, + /* 4 */ compare_string_like, + /* 5 */ compare_string_match, + /* 6 */ compare_string_like, + /* 7 */ compare_string_match, + /* 8 */ compare_string_natural, + /* 9 */ compare_string_natural_case, + /* 10 */ compare_string_natural, + /* 11 */ compare_string_natural_case, + /* 12 */ compare_string_natural, + /* 13 */ compare_string_natural_case, + /* 14 */ compare_string_natural, + /* 15 */ compare_string_natural_case, +}; + + +COMPARE_FUNC COMPARE_get_func(TYPE type, int mode) +{ + if (mode >= 0) + { + _descent = (mode & GB_COMP_DESCENT) != 0; + mode &= GB_COMP_TYPE_MASK; + _mode = mode; + } + + if (type >= T_OBJECT) + return (COMPARE_FUNC)COMPARE_object; + + switch(type) + { + case T_INTEGER: + return (COMPARE_FUNC)compare_integer; + + case T_SHORT: + return (COMPARE_FUNC)compare_short; + + case T_BYTE: + case T_BOOLEAN: + return (COMPARE_FUNC)compare_byte; + + case T_LONG: + return (COMPARE_FUNC)compare_long; + + case T_FLOAT: + return (COMPARE_FUNC)compare_float; + + case T_SINGLE: + return (COMPARE_FUNC)compare_single; + + case T_DATE: + return (COMPARE_FUNC)compare_date; + + case T_STRING: + return _string_func[_mode]; + + case T_POINTER: + #ifdef OS_64BITS + return (COMPARE_FUNC)compare_long; + #else + return (COMPARE_FUNC)compare_integer; + #endif + + case T_VARIANT: + return (COMPARE_FUNC)COMPARE_variant; + + default: + return (COMPARE_FUNC)compare_nothing; + } +} + +COMPARE_STRING_FUNC COMPARE_get_string_func(int mode) +{ + mode &= GB_COMP_TYPE_MASK; + + if (mode == GB_COMP_BINARY) + return (COMPARE_STRING_FUNC)STRING_compare; + else if (mode == GB_COMP_NOCASE) + return (COMPARE_STRING_FUNC)STRING_compare_ignore_case; + else + { + if (mode & GB_COMP_NATURAL) + return (COMPARE_STRING_FUNC)COMPARE_string_natural; + else if (mode & GB_COMP_LIKE) + return (COMPARE_STRING_FUNC)COMPARE_string_like; + else if (mode & GB_COMP_LANG) + return (COMPARE_STRING_FUNC)COMPARE_string_lang; + else + THROW(E_ARG); + } +} diff --git a/main/gbx/gbx_compare.h b/main/gbx/gbx_compare.h new file mode 100644 index 00000000..34f16a31 --- /dev/null +++ b/main/gbx/gbx_compare.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + gbx_compare.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_COMPARE_H +#define __GBX_COMPARE_H + +#ifndef GBX_INFO +#include "gbx_type.h" +#include "gbx_variant.h" +#endif + +#define GB_COMP_BINARY 0 +#define GB_COMP_NOCASE 1 +#define GB_COMP_LANG 2 +#define GB_COMP_LIKE 4 +#define GB_COMP_MATCH 5 +#define GB_COMP_NATURAL 8 + +#define GB_COMP_TYPE_MASK 15 + +#define GB_COMP_ASCENT 0 +#define GB_COMP_DESCENT 16 + +#ifndef GBX_INFO + +typedef + int (*COMPARE_FUNC)(); + +typedef + int (*COMPARE_STRING_FUNC)(); + +COMPARE_FUNC COMPARE_get_func(TYPE type, int mode); +COMPARE_STRING_FUNC COMPARE_get_string_func(int mode); + +int COMPARE_object(void **a, void **b); +int COMPARE_string_lang(const char *s1, int l1, const char *s2, int l2, bool nocase, bool throw); +int COMPARE_string_like(const char *s1, int l1, const char *s2, int l2, bool nocase); +int COMPARE_string_natural(const char *a, int la, const char *b, int lb, bool nocase); +int COMPARE_variant(VARIANT *a, VARIANT *b); + +#endif + +#endif diff --git a/main/gbx/gbx_component.c b/main/gbx/gbx_component.c new file mode 100644 index 00000000..a64fc0e4 --- /dev/null +++ b/main/gbx/gbx_component.c @@ -0,0 +1,449 @@ +/*************************************************************************** + + gbx_component.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_COMPONENT_C + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_replace.h" +#include "gb_file.h" +#include "gbx_debug.h" + +#include +#include +#include +#include +#include + +#include "gb_error.h" + +#include "gbx_class.h" +#include "gbx_exec.h" + +#include "gbx_local.h" +#include "gbx_archive.h" +#include "gbx_library.h" +#include "gbx_project.h" + +#include "gbx_component.h" + + +//#define DEBUG_COMP 1 +//#define DEBUG_PRELOAD + +COMPONENT *COMPONENT_current = NULL; +//COMPONENT *COMPONENT_main; +int COMPONENT_count = 0; +char *COMPONENT_path; + +static COMPONENT *_component_list = NULL; +static COMPONENT *_component_load = NULL; + +static bool _load_all = FALSE; + +void COMPONENT_init(void) +{ + LIBRARY_init(); + ARCHIVE_init(); +} + + +void COMPONENT_exit(void) +{ + COMPONENT *comp; + int order; + int max_order = 0; + + _component_load = NULL; + + LIST_for_each(comp, _component_list) + { + if (comp->order > max_order) + max_order = comp->order; + } + + // if order < 0, the component is not unloaded + + for (order = 0; order <= max_order; order++) + { + LIST_for_each(comp, _component_list) + { + if (comp->order == order) + COMPONENT_unload(comp); + } + } + + /*LIST_for_each(comp, _component_list) + { + if (comp->loaded) + COMPONENT_unload(comp); + }*/ + + while (_component_list) + COMPONENT_delete(_component_list); + + LIBRARY_exit(); + ARCHIVE_exit(); + + STRING_free(&COMPONENT_path); +} + + + +void COMPONENT_load_all(void) +{ + COMPONENT *comp; + + if (EXEC_debug) + { + COMPONENT_create("gb.eval"); + COMPONENT_create("gb.debug"); + } + + if (PROJECT_run_tests) + { + COMPONENT_create("gb.test"); + } + + _load_all = TRUE; + + LIST_for_each(comp, _component_list) + { + COMPONENT_load(comp); + } + + _load_all = FALSE; +} + + +void COMPONENT_load_all_finish(void) +{ + COMPONENT *comp; + + LIST_for_each_name(comp, _component_load, load) + { + ARCHIVE_load_exported_class(comp->archive, AR_FIND_ONLY); + } + LIST_for_each_name(comp, _component_load, load) + { + ARCHIVE_load_exported_class(comp->archive, AR_LOAD_ONLY); + } + + LIST_for_each(comp, _component_list) + { + if (comp->library) + LIBRARY_after_init(comp->library); + } +} + + +COMPONENT *COMPONENT_find(const char *name) +{ + COMPONENT *comp; + + /* A null name is the main archive */ + if (!name) + return NULL; + + LIST_for_each(comp, _component_list) + { + if (strcmp(comp->name, name) == 0) + return comp; + } + + return NULL; +} + +bool COMPONENT_exist(const char *name) +{ + return COMPONENT_find(name) != NULL; +} + +bool COMPONENT_can_load_library(const char *name) +{ + char *path; + + path = FILE_buffer(); + sprintf(path, LIB_PATTERN, COMPONENT_path, name); + return FILE_exist(path); +} + +COMPONENT *COMPONENT_create(const char *name) +{ + COMPONENT *comp; + char *path = NULL; + bool can_archive; + bool user_library = FALSE; + bool same_name_as_project = FALSE; + char *p = NULL; + + if (*name == '/' || *name == ':') // user library + { + user_library = TRUE; + path = (char *)name; + if (*name == ':') + { + name = STRING_new_temp_zero(name + 1); + p = index(name, '/'); + if (p) + *p = '.'; + p = rindex(name, ':'); + if (p) + *p = 0; + } + else + name = FILE_get_basename(name); + } + + comp = COMPONENT_find(name); + if (comp) + return comp; + + ALLOC_ZERO(&comp, sizeof(COMPONENT)); + + comp->class = CLASS_Component; + comp->ref = 1; + + comp->name = STRING_new_zero(name); + + if (p) + *p = ':'; + + if (user_library) + { + comp->archive = ARCHIVE_create(comp->name, path); + comp->user = TRUE; + } + else + { + // Don't load the archive if it has the same name as the project + + if (PROJECT_name) + same_name_as_project = strcmp(comp->name, PROJECT_name) == 0; + + can_archive = !same_name_as_project; + + // System wide component + + path = FILE_buffer(); + sprintf(path, LIB_PATTERN, COMPONENT_path, name); + //fprintf(stderr, "COMPONENT_create: %s\n", path); + + if (FILE_exist(path)) + comp->library = LIBRARY_create(comp->name); + + if (can_archive) + { + path = FILE_buffer(); + sprintf(path, ARCH_PATTERN, COMPONENT_path, name); + + if (FILE_exist(path)) + comp->archive = ARCHIVE_create(comp->name, NULL); + } + } + + //fprintf(stderr, "insert %s\n", comp->name); + LIST_insert(&_component_list, comp, &comp->list); + COMPONENT_count++; + + if (!comp->library && !comp->archive && !same_name_as_project) + { + COMPONENT_delete(comp); + THROW(E_LIBRARY, name, "cannot find component"); + } + + return comp; +} + + +void COMPONENT_delete(COMPONENT *comp) +{ + COMPONENT_unload(comp); + LIST_remove(&_component_list, comp, &comp->list); + COMPONENT_count--; + + if (comp->library) + LIBRARY_delete(comp->library); + + if (comp->archive) + ARCHIVE_delete(comp->archive); + + STRING_free(&comp->name); + + FREE(&comp); +} + +static void error_COMPONENT_load(COMPONENT *current) +{ + COMPONENT_current = current; +} + +void COMPONENT_load(COMPONENT *comp) +{ + COMPONENT *current; + + if (comp->loaded || comp->loading) + return; + + #if DEBUG_COMP + fprintf(stderr, "Loading component %s\n", comp->name); + #endif + + comp->loading = TRUE; + + current = COMPONENT_current; + COMPONENT_current = comp; + + ON_ERROR_1(error_COMPONENT_load, current) + { + if (comp->library) + { + comp->order = LIBRARY_load(comp->library); + comp->library->persistent = _load_all; + } + + if (comp->archive) + { + if (_load_all) + { + //fprintf(stderr, "load later: %s\n", comp->name); + LIST_insert(&_component_load, comp, &comp->load); + } + + ARCHIVE_load(comp->archive, !_load_all); + } + } + END_ERROR + + comp->loading = FALSE; + comp->loaded = TRUE; + COMPONENT_current = current; +} + + +void COMPONENT_unload(COMPONENT *comp) +{ + if (!comp->loaded) + return; + + #if DEBUG_COMP + fprintf(stderr, "Unloading component %s [%d]\n", comp->name, comp->order); + #endif + + if (comp->library) + LIBRARY_unload(comp->library); + + /* Do not exist yet */ + //if (comp->archive) + // ARCHIVE_unload(comp->archive); + + comp->loaded = FALSE; +} + + +COMPONENT *COMPONENT_next(COMPONENT *comp) +{ + if (comp) + return (COMPONENT *)(comp->list.next); + else + return _component_list; +} + + +void COMPONENT_translation_must_be_reloaded(void) +{ + COMPONENT *comp; + + LIST_for_each(comp, _component_list) + { + if (comp->archive) + comp->archive->translation_loaded = FALSE; + } + + if (ARCHIVE_main) + ARCHIVE_main->translation_loaded = FALSE; +} + + +void COMPONENT_signal(int signal, void *param) +{ + COMPONENT *comp; + + LIST_for_each(comp, _component_list) + { + if (comp->library && comp->library->signal) + (*comp->library->signal)(signal, param); + } +} + +bool COMPONENT_get_info(const char *key, void **value) +{ + COMPONENT *comp; + + LIST_for_each(comp, _component_list) + { + if (comp->library && comp->library->info) + if ((*comp->library->info)(key, value)) + return FALSE; + } + + return TRUE; +} + +void COMPONENT_exec(const char *name, int argc, char **argv) +{ + COMPONENT *comp; + + comp = COMPONENT_create(name); + + COMPONENT_load(comp); + + if (comp->library) + LIBRARY_exec(comp->library, argc, argv); +} + +bool COMPONENT_is_loaded(const char *name) +{ + COMPONENT *comp; + + comp = COMPONENT_find(name); + return comp && comp->loaded; +} + +void COMPONENT_before_fork(void) +{ + COMPONENT *comp; + + LIST_for_each(comp, _component_list) + { + if (comp->library) + LIBRARY_before_fork(comp->library); + } +} diff --git a/main/gbx/gbx_component.h b/main/gbx/gbx_component.h new file mode 100644 index 00000000..09db9629 --- /dev/null +++ b/main/gbx/gbx_component.h @@ -0,0 +1,91 @@ +/*************************************************************************** + + gbx_component.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_COMPONENT_H +#define __GBX_COMPONENT_H + +#include "gambas.h" +#include "gb_list.h" +#include "gb_component.h" + +#include "gbx_library.h" +#include "gbx_archive.h" + +typedef + struct _COMPONENT { + void *class; + intptr_t ref; + LIST list; + LIST load; + char *name; + LIBRARY *library; + ARCHIVE *archive; + unsigned order : 8; + unsigned loaded : 1; + unsigned user : 1; // user library + unsigned warning : 1; // Set when the bytecode warning was displayed by the class loader for this component + unsigned loading : 1; // component is being loaded + unsigned _reserved : 20; + } + COMPONENT; + +#ifndef __GBX_COMPONENT_C +EXTERN char *COMPONENT_path; +EXTERN COMPONENT *COMPONENT_current; +EXTERN int COMPONENT_count; +//EXTERN COMPONENT *COMPONENT_main; +#endif + +void COMPONENT_init(void); +void COMPONENT_exit(void); + +COMPONENT *COMPONENT_create(const char *name); +void COMPONENT_delete(COMPONENT *comp); + +COMPONENT *COMPONENT_find(const char *name); +bool COMPONENT_exist(const char *name); +bool COMPONENT_can_load_library(const char *name); + +void COMPONENT_load(COMPONENT *comp); +void COMPONENT_unload(COMPONENT *comp); + +void COMPONENT_load_all(void); +void COMPONENT_load_all_finish(void); + +bool COMPONENT_is_loaded(const char *name); + +COMPONENT *COMPONENT_next(COMPONENT *comp); + +void COMPONENT_translation_must_be_reloaded(void); + +void COMPONENT_signal(int signal, void *param); + +#define COMPONENT_is_library(comp) ((comp)->library != NULL) + +bool COMPONENT_get_info(const char *key, void **value); + +void COMPONENT_exec(const char *name, int argc, char **argv); + +void COMPONENT_before_fork(void); + +#endif diff --git a/main/gbx/gbx_date.c b/main/gbx/gbx_date.c new file mode 100644 index 00000000..07c0302d --- /dev/null +++ b/main/gbx/gbx_date.c @@ -0,0 +1,1028 @@ +/*************************************************************************** + + gbx_date.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DATE_C + +#include "gb_common.h" +#include "gb_common_buffer.h" + +#include +#include +#include +#include + +#include "gb_error.h" +#include "gbx_value.h" +#include "gbx_local.h" +#include "gbx_number.h" +#include "gbx_c_string.h" +#include "gbx_math.h" + +#include "gbx_date.h" + +//#define DEBUG_DATE + +static const char days_in_months[2][13] = +{ /* error, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */ +}; + +static const short days_in_year[2][14] = +{ /* 0, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } /* leap year */ +}; + +static double _start_time; + + +// Returns 1 for a leap year, 0 else + +static int date_is_leap_year(short year) +{ + if (year < 0) + year += 8001; + + if ((((year % 4) == 0) && ((year % 100) != 0)) || (year % 400) == 0) + return 1; + else + return 0; +} + + +static bool date_is_valid(DATE_SERIAL *date) +{ + return ((date->year == 0 + || ((date->month >= 1) && (date->month <= 12) & + (date->year >= DATE_YEAR_MIN) && (date->year <= DATE_YEAR_MAX) && (date->year != 0) && + (date->day >= 1) && (date->day <= days_in_months[date_is_leap_year(date->year)][(short)date->month]))) + && (date->hour >= 0) && (date->hour <= 23) && (date->min >= 0) && (date->min <= 59) + && (date->sec >= 0) && (date->sec <= 59)); +} + + +static short date_to_julian_year(short year) +{ + if (year < 0) + return year - DATE_YEAR_MIN; + else + return year - DATE_YEAR_MIN - 1; +} + + +static short date_from_julian_year(short year) +{ + if (year < (-DATE_YEAR_MIN)) + return year + DATE_YEAR_MIN; + else + return year + DATE_YEAR_MIN + 1; +} + +static double get_monotonic_timer(void) +{ +#ifdef HAVE_MONOTONIC_CLOCK + struct timespec tv; + #if OS_LINUX && defined(CLOCK_MONOTONIC_RAW) + if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) == 0) + #else + if (clock_gettime(CLOCK_MONOTONIC, &tv) == 0) + #endif + return (double)tv.tv_sec + (double)tv.tv_nsec / 1E9; +#else + struct timeval tv; + if (gettimeofday(&tv, NULL) == 0) + return (double)tv.tv_sec + (double)tv.tv_usec / 1E6; +#endif + return 0.0; +} + +void DATE_init(void) +{ + _start_time = get_monotonic_timer(); +} + + +void DATE_init_local(void) +{ + // Prevent glibc for calling stat("/etc/localtime") again and again... + if (!getenv("TZ")) + putenv("TZ=:/etc/localtime"); + + tzset(); +} + + +DATE_SERIAL *DATE_split_local(VALUE *value, bool local) +{ + static int last_nday, last_nmsec; + static DATE_SERIAL last_date = { 0 }; + + int nday, nmsec; + int A, B, C, D, E, M; + + nday = value->_date.date; + nmsec = value->_date.time; + + if (local && nday > 0) + nmsec -= DATE_get_timezone() * 1000; + + if (nmsec < 0) + { + nday--; + nmsec += 86400000; + } + else if (nmsec >= 86400000) + { + nday++; + nmsec -= 86400000; + } + + if (last_nmsec != nmsec) + { + last_nmsec = nmsec; + + last_date.msec = nmsec % 1000; + nmsec /= 1000; + last_date.sec = nmsec % 60; + nmsec /= 60; + last_date.min = nmsec % 60; + nmsec /= 60; + last_date.hour = nmsec; + } + + if (last_nday != nday) + { + last_nday = nday; + + /*nday += DATE_NDAY_BC;*/ + if (nday <= 0) + { + last_date.month = 0; + last_date.day = 0; + last_date.year = 0; + last_date.weekday = 0; + } + else + { + A = nday - 58 - 1; + B = (4 * (A + 36524))/ 146097 - 1; + C = A - (146097 * B)/4; + D = (4 * (C + 365)) / 1461 - 1; + E = C - ((1461 * D) / 4); + M = (5 * (E - 1) + 2) / 153; + + last_date.month = M + 3 - (12 * (M / 10)); + last_date.day = E - (153 * M + 2)/5; + last_date.year = 100 * B + D + (M / 10) /*- 4800*/ ; + last_date.year = date_from_julian_year(last_date.year); + last_date.weekday = (nday - 1) % 7; + } + } + + //fprintf(stderr, "DATE_split: %d %d %d / %d %d %d . %d\n", last_date.year, last_date.month, last_date.day, last_date.hour, last_date.min, last_date.sec, last_date.msec); + + return &last_date; +} + + +static bool make_date(DATE_SERIAL *date, VALUE *val) +{ + short year; + int nday; + + if (!date_is_valid(date)) + return TRUE; + + if (date->year == 0) + { + nday = 0; /*(-DATE_NDAY_BC - 1);*/ + } + else + { + year = date_to_julian_year(date->year); + + nday = year * 365; + if (year > 1) + { + year--; + nday += (year >>= 2); + nday -= (year /= 25); + nday += year >> 2; + } + + nday += days_in_year[date_is_leap_year(date->year)][(short)date->month] + date->day; + + /*nday -= DATE_NDAY_BC;*/ + } + + val->_date.date = nday; + val->_date.time = (((date->hour * 60) + date->min) * 60 + date->sec) * 1000 + date->msec; + + val->type = T_DATE; + + return FALSE; + +} + +static void add_timezone(VALUE *val, int timezone) +{ + val->_date.time += timezone * 1000; + + if (val->_date.time < 0) + { + val->_date.date--; + val->_date.time += 86400000; + } + else if (val->_date.time >= 86400000) + { + val->_date.date++; + val->_date.time -= 86400000; + } +} + + +bool DATE_make_local(DATE_SERIAL *date, VALUE *val, bool local) +{ + if (make_date(date, val)) + return TRUE; + + if (local && date->year) + add_timezone(val, DATE_get_timezone()); + + return FALSE; +} + + +static int get_current_year(void) +{ + struct tm *tm; + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + THROW(E_DATE); + + tm = localtime((time_t *)&tv.tv_sec); + return tm->tm_year + 1900; +} + + +void DATE_from_time(time_t time, int usec, VALUE *val) +{ + static struct tm tm; + static time_t last_time = (time_t)-1; + + DATE_SERIAL date; + + if (time != last_time) + { + localtime_r(&time, &tm); + last_time = time; + } + + date.year = tm.tm_year + 1900; + date.month = tm.tm_mon + 1; + date.day = tm.tm_mday; + date.hour = tm.tm_hour; + date.min = tm.tm_min; + date.sec = tm.tm_sec; + date.msec = usec / 1000; + + if (DATE_make(&date, val)) + VALUE_default(val, T_DATE); +} + + +void DATE_now(VALUE *val) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL)) + VALUE_default(val, T_DATE); + else + { + //fprintf(stderr, "DATE_now: %d %d\n", tv.tv_sec, tv.tv_usec); + DATE_from_time((time_t)tv.tv_sec, tv.tv_usec, val); + } +} + + +int DATE_to_string(char *buffer, VALUE *value) +{ + DATE_SERIAL *date; + int len; + + if (value->_date.date == 0 && value->_date.time == 0) + return 0; + + date = DATE_split_local(value, FALSE); + + if (value->_date.date == 0) + len = sprintf(buffer,"%02d:%02d:%02d", date->hour, date->min, date->sec); + else if ((date->hour | date->min | date->sec | date->msec) == 0) + len = sprintf(buffer,"%02d/%02d/%04d", date->month, date->day, date->year); + else + len = sprintf(buffer,"%02d/%02d/%04d %02d:%02d:%02d", date->month, date->day, date->year, date->hour, date->min, date->sec); + + if (date->msec) + { + len += sprintf(&buffer[len], ".%03d", date->msec); + while (buffer[len - 1] == '0') + len--; + buffer[len] = 0; + } + + return len; +} + + +static bool read_integer(int *number, bool *zero) +{ + int nbr = 0; + int nbr2; + int c; + bool minus = FALSE; + + c = COMMON_get_char(); + + if (c == '-') + { + minus = TRUE; + c = COMMON_get_char(); + } + else if (c == '+') + c = COMMON_get_char(); + + if ((c < 0) || !isdigit(c)) + return TRUE; + + if (zero) + *zero = (c == '0'); + + for(;;) + { + nbr2 = nbr * 10 + (c - '0'); + if (nbr2 < nbr) + return TRUE; + nbr = nbr2; + + c = COMMON_look_char(); + if ((c < 0) || !isdigit(c)) + break; + + COMMON_pos++; + } + + if (minus) + nbr = (-nbr); + + *number = nbr; + return FALSE; +} + + +static bool read_msec(int *number) +{ + int nbr = 0; + int nbr2; + int c; + int i; + + c = COMMON_get_char(); + + if ((c < 0) || !isdigit(c)) + return TRUE; + + i = 0; + for(;;) + { + i++; + nbr2 = nbr * 10 + (c - '0'); + if (nbr2 < nbr) + return TRUE; + nbr = nbr2; + + if (i == 3) + break; + + c = COMMON_look_char(); + if ((c < 0) || !isdigit(c)) + break; + + COMMON_pos++; + } + + for (; i < 3; i++) + nbr *= 10; + + *number = nbr; + return FALSE; +} + + +static void set_date(DATE_SERIAL *date, int which, int value, bool zero) +{ + if (which == LO_YEAR) + { + if (!zero && value >= 0 && value <= 99) + { + if (value > 30) + value += 1900; + else + value += 2000; + } + date->year = value; + } + else if (which == LO_MONTH) + date->month = value; + else if (which == LO_DAY) + date->day = value; +} + + +static void set_time(DATE_SERIAL *date, int which, int value) +{ + if (which == LO_HOUR) + date->hour = value; + else if (which == LO_MINUTE) + date->min = value; + else if (which == LO_SECOND) + date->sec = value; +} + + +static bool read_timezone(int *timezone) +{ + int c; + int nbr; + bool neg; + int save_pos = COMMON_pos; + + if (!COMMON_has_string("UTC", 3) && !COMMON_has_string("GMT", 3)) + goto __ERROR; + + COMMON_pos += 3; + *timezone = 0; + + c = COMMON_look_char(); + if (c < 0) + return TRUE; + + if (c != '+' && c != '-') + goto __ERROR; + + neg = c == '-'; + COMMON_pos++; + + if (read_integer(&nbr, NULL)) + goto __ERROR; + + if (nbr < 0 || nbr > 24) + goto __ERROR; + + *timezone = nbr * 3600; + + c = COMMON_look_char(); + if (c < 0) + goto __OK; + + if (c != ':') + goto __ERROR; + + COMMON_pos++; + + nbr = 0; + c = COMMON_look_char(); + if (c < 0 || !isdigit(c)) + goto __ERROR; + nbr += (c - '0') * 10; + COMMON_pos++; + + c = COMMON_look_char(); + if (c < 0 || !isdigit(c)) + goto __ERROR; + nbr += c - '0'; + + if (nbr > 59) + goto __ERROR; + + COMMON_pos++; + + *timezone += nbr * 60; + +__OK: + + if (!neg) + *timezone = (- *timezone); + + return TRUE; + +__ERROR: + + COMMON_pos = save_pos; + return FALSE; +} + +bool DATE_from_string(const char *str, int len, VALUE *val, bool local) +{ + DATE_SERIAL date; + LOCAL_INFO *info = LOCAL_get(local); + int nbr, nbr2; + int c, i; + bool has_date = FALSE; + bool zero, zero2; + bool has_timezone = FALSE; + int timezone = 0; + + if (!len) + { + DATE_void_value(val); + return FALSE; + } + + CLEAR(&date); + + COMMON_buffer_init(str, len); + COMMON_jump_space(); + + if (read_integer(&nbr, &zero)) + return TRUE; + + c = COMMON_get_unicode_char(); + + if (c == info->date_sep[info->date_order[0]]) + { + has_date = TRUE; + + if (read_integer(&nbr2, &zero2)) + return TRUE; + + c = COMMON_get_unicode_char(); + + if (c == info->date_sep[info->date_order[1]]) + { + set_date(&date, info->date_order[0], nbr, zero); + set_date(&date, info->date_order[1], nbr2, zero2); + + if (read_integer(&nbr, &zero)) + { + if (!info->date_tail_sep) + return TRUE; + else + goto _OK; + } + + if (info->date_sep[info->date_order[2]]) + { + c = COMMON_get_unicode_char(); + if (c > 0 && c != info->date_sep[info->date_order[2]]) + return TRUE; + } + + set_date(&date, info->date_order[2], nbr, zero); + } + else if ((c < 0) || isspace(c)) + { + i = 0; + + set_date(&date, LO_YEAR, get_current_year(), TRUE); + + if (info->date_order[i] == LO_YEAR) i++; + set_date(&date, info->date_order[i], nbr, zero); i++; + + if (info->date_order[i] == LO_YEAR) i++; + set_date(&date, info->date_order[i], nbr2, zero2); + } + + COMMON_jump_space(); + + c = COMMON_look_char(); + if (c < 0) + goto _OK; + + if (read_integer(&nbr, NULL)) + return TRUE; + + c = COMMON_get_unicode_char(); + } + + if (c == info->time_sep[info->time_order[0]]) + { + if (read_integer(&nbr2, NULL)) + return TRUE; + + c = COMMON_get_unicode_char(); + + if (c == info->time_sep[info->time_order[1]]) + { + set_time(&date, info->time_order[0], nbr); + set_time(&date, info->time_order[1], nbr2); + + if (read_integer(&nbr, NULL)) + { + if (!info->time_tail_sep) + return TRUE; + else + goto _OK; + } + + set_time(&date, info->time_order[2], nbr); + + c = COMMON_look_char(); + if (c == '.') // msec separator + { + COMMON_pos++; + if (read_msec(&nbr)) + return TRUE; + date.msec = nbr; + } + + if (info->time_sep[info->time_order[2]]) + { + c = COMMON_get_unicode_char(); + if (c > 0 && c != info->time_sep[info->time_order[2]]) + return TRUE; + } + } + else if ((c < 0) || isspace(c)) + { + i = 0; + + if (info->time_order[i] == LO_SECOND) i++; + set_time(&date, info->time_order[i], nbr); i++; + + if (info->time_order[i] == LO_SECOND) i++; + set_time(&date, info->time_order[i], nbr2); + } + + COMMON_jump_space(); + + if (has_date) + { + has_timezone = read_timezone(&timezone); + + if (has_timezone) + COMMON_jump_space(); + } + + c = COMMON_get_char(); + if (c < 0) + goto _OK; + } + + return TRUE; + +_OK: + + if (make_date(&date, val)) + return TRUE; + + if (date.year) + { + if (has_timezone) + add_timezone(val, timezone); + else if (local) + add_timezone(val, DATE_get_timezone()); + } + + if (!has_date) + val->_date.date = 0; + + return FALSE; +} + + +int DATE_comp(DATE *date1, DATE *date2) +{ + if (date1->date < date2->date) + return (-1); + + if (date1->date > date2->date) + return 1; + + if (date1->time < date2->time) + return (-1); + + if (date1->time > date2->time) + return 1; + + return 0; +} + + +int DATE_comp_value(VALUE *date1, VALUE *date2) +{ + return DATE_comp((DATE *)&date1->_date.date, (DATE *)&date2->_date.date); +} + + +double DATE_to_double(struct timeval *time, int from_start) +{ + double result = (double)time->tv_sec + (double)time->tv_usec / 1E6; + + if (from_start) + result -= _start_time; + + return result; +} + + +bool DATE_timer(double *result, int from_start) +{ + *result = get_monotonic_timer(); + + if (*result == 0.0) + return TRUE; + + if (from_start) + *result -= _start_time; + + return FALSE; +} + + +void DATE_add(VALUE *date, int period, int val) +{ + int64_t add_time = 0; + int64_t add_date = 0; + int64_t fix_date; + int64_t new_time; + int64_t new_date; + DATE_SERIAL ds; + int y, m, d; + + switch(period) + { + case DP_MILLISECOND: + add_date = val / 86400000; + add_time = val % 86400000 ; + goto __ADD_DATE_TIME; + + case DP_SECOND: + add_date = val / 86400; + add_time = (val % 86400) * 1000; + goto __ADD_DATE_TIME; + + case DP_MINUTE: + add_date = val / 1440; + add_time = (val % 1440) * 60000; + goto __ADD_DATE_TIME; + + case DP_HOUR: + add_date = val / 24; + add_time = (val % 24) * 3600000; + goto __ADD_DATE_TIME; + + case DP_DAY: + add_date = val; + goto __ADD_DATE_TIME; + + case DP_WEEK: + add_date = val * 7; + goto __ADD_DATE_TIME; + + default: + break; + } + + ds = *DATE_split(date); + + switch(period) + { + case DP_WEEKDAY: + add_date = (val / 5) * 7; + ds.weekday += val % 5; + if (ds.weekday > 5) + add_date += 2; + else if (ds.weekday < 1) + add_date -= 2; + add_date += val % 5; + goto __ADD_DATE_TIME; + + case DP_QUARTER: + val *= 3; + /* continue; */ + + case DP_MONTH: + y = ((ds.year * 12) + (ds.month - 1) + val) / 12; + m = ((ds.month - 1) + val) % 12; + if (m < 0) m += 12; + m++; + d = days_in_months[date_is_leap_year(y)][m]; + d = ds.day > d ? d : ds.day; + ds.day = d; + ds.month = m; + ds.year = y; + goto __MAKE_DATE; + + case DP_YEAR: + ds.year += val; + if (ds.month == 2 && ds.day == 29) + { + d = days_in_months[date_is_leap_year(ds.year)][ds.month]; + if (ds.day > d) + ds.day = d; + } + goto __MAKE_DATE; + + default: + THROW(E_ARG); + } + +__ADD_DATE_TIME: + + new_time = date->_date.time + add_time; + new_date = date->_date.date + add_date; + + if (new_time >= 86400000) + { + fix_date = new_time / 86400000; + new_time -= fix_date * 86400000; + new_date += fix_date; + } + else if (new_time < 0) + { + fix_date = (-new_time + 86400000 - 1) / 86400000; + new_time += fix_date * 86400000; + new_date -= fix_date; + } + + if (new_time < INT32_MIN || new_time > INT32_MAX || new_date < 0 || new_date > INT32_MAX) + THROW(E_OVERFLOW); + + date->_date.date = (int)new_date; + date->_date.time = (int)new_time; + return; + +__MAKE_DATE: + + if (DATE_make(&ds, date)) + THROW(E_DATE); + return; +} + + +int DATE_diff(VALUE *date1, VALUE *date2, int period) +{ + int64_t diff = 0; + int sdiff; + DATE_SERIAL ds1 = {0}; + DATE_SERIAL ds2 = {0}; + bool neg; + + switch (period) + { + case DP_DAY: + case DP_WEEK: + diff = date1->_date.date - date2->_date.date; + if (diff) + { + sdiff = lsgn(date1->_date.time - date2->_date.time); + if (sdiff != lsgn(diff)) + diff += sdiff; + } + break; + + case DP_MILLISECOND: + case DP_SECOND: + case DP_MINUTE: + case DP_HOUR: + diff = date1->_date.date - date2->_date.date; + diff = diff * 86400000 + (date1->_date.time - date2->_date.time); + break; + + case DP_MONTH: + case DP_QUARTER: + case DP_YEAR: + ds1 = *DATE_split(date1); + ds2 = *DATE_split(date2); + break; + + case DP_WEEKDAY: + diff = date1->_date.date - date2->_date.date; + if (diff) + { + sdiff = lsgn(date1->_date.time - date2->_date.time); + if (sdiff != lsgn(diff)) + diff += sdiff; + } + ds1 = *DATE_split(date1); + ds2 = *DATE_split(date2); + break; + + default: + THROW(E_ARG); + } + + switch (period) + { + case DP_DAY: + break; + + case DP_WEEK: + diff /= 7; + break; + + case DP_SECOND: + diff /= 1000; + break; + + case DP_MINUTE: + diff /= 60000; + break; + + case DP_HOUR: + diff /= 3600000; + break; + + case DP_WEEKDAY: + + neg = (diff < 0); + if (neg) + { + int swap; + diff = (-diff); + swap = ds1.weekday; + ds1.weekday = ds2.weekday; + ds2.weekday = swap; + } + + diff = diff / 7 * 5; + + /* last day is not included ! */ + while (ds2.weekday != ds1.weekday) + { + if (ds2.weekday > 0 && ds2.weekday < 6) + diff++; + ds2.weekday++; + if (ds2.weekday == 7) + ds2.weekday = 0; + } + + if (neg) + diff = (-diff); + + break; + + case DP_MONTH: + diff = (ds1.year - ds2.year) * 12 + ds1.month - ds2.month; + break; + + case DP_QUARTER: + diff = (ds1.year - ds2.year) * 4 + (ds1.month - ds2.month) / 3; + break; + + case DP_YEAR: + diff = ds1.year - ds2.year; + break; + + case DP_MILLISECOND: + break; + } + + if (diff < INT32_MIN || diff > INT32_MAX) + THROW(E_OVERFLOW); + + return diff; +} + + +// Beware: System.TimeZone is what must be added to go to UTC +// So if the shell command `date` displays "UTC+2", then that +// function must return -7200! + +int DATE_get_timezone(void) +{ + static time_t last = (time_t)0; + static int tz = 0; + + time_t t = time(NULL); + if ((t - last) >= 600) + { + struct tm *tm = localtime(&t); + tz = -tm->tm_gmtoff; + last = t; + } + + return tz; +} diff --git a/main/gbx/gbx_date.h b/main/gbx/gbx_date.h new file mode 100644 index 00000000..fd79eb7a --- /dev/null +++ b/main/gbx/gbx_date.h @@ -0,0 +1,109 @@ +/*************************************************************************** + + gbx_date.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_DATE_H +#define __GBX_DATE_H + +#ifndef GBX_INFO + +#include + +#include "gbx_value.h" + +typedef + struct { + int year; + int month; + int day; + int hour; + int min; + int sec; + int weekday; + int msec; + } + DATE_SERIAL; + +#ifndef __DATE_DECLARED +#define __DATE_DECLARED +typedef + struct { + int date; + int time; + } + DATE; +#endif + +#endif + +enum { + DP_MILLISECOND = 1, + DP_SECOND = 2, + DP_MINUTE = 3, + DP_HOUR = 4, + DP_DAY = 5, + DP_WEEK = 6, + DP_WEEKDAY = 7, + DP_MONTH = 8, + DP_QUARTER = 9, + DP_YEAR = 10, + }; + +#ifndef GBX_INFO + +#define DATE_YEAR_MIN -4801 +#define DATE_YEAR_MAX 9999 + +#define DATE_NDAY_BC 1753350 + +#define DATE_SERIAL_has_no_date(_date) ((_date)->year == 0) +#define DATE_SERIAL_has_no_time(_date) ((_date)->hour == 0 && (_date)->min == 0 && (_date)->sec == 0 && (_date)->msec == 0) + +#define DATE_void_value(_value) ((_value)->type = T_DATE, (_value)->_date.time = (_value)->_date.date = 0) + +void DATE_init(void); +void DATE_init_local(void); +int DATE_get_timezone(void); + +DATE_SERIAL *DATE_split_local(VALUE *value, bool local); +#define DATE_split(_value) DATE_split_local(_value, TRUE) + +bool DATE_make_local(DATE_SERIAL *date, VALUE *val, bool local); +#define DATE_make(_date, _value) DATE_make_local(_date, _value, TRUE) + +void DATE_from_time(time_t time, int usec, VALUE *val); +void DATE_now(VALUE *val); + +int DATE_to_string(char *buffer, VALUE *value); +bool DATE_from_string(const char *str, int len, VALUE *val, bool local); +int DATE_comp_value(VALUE *date1, VALUE *date2); +int DATE_comp(DATE *date1, DATE *date2); + +double DATE_to_double(struct timeval *time, int from_start); +bool DATE_timer(double *result, int from_start); + +void DATE_add(VALUE *date, int period, int val); +int DATE_diff(VALUE *date1, VALUE *date2, int period); + +#endif + +#endif diff --git a/main/gbx/gbx_debug.c b/main/gbx/gbx_debug.c new file mode 100644 index 00000000..68dd8688 --- /dev/null +++ b/main/gbx/gbx_debug.c @@ -0,0 +1,762 @@ +/*************************************************************************** + + gbx_debug.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_DEBUG_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include + +#include "gb_buffer.h" +#include "gb_array.h" +#include "gb_error.h" +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_c_array.h" +#include "gbx_project.h" +#include "gbx_stack.h" +#include "gbx_subr.h" +#include "gbx_jit.h" + +#include "gbx_debug.h" + +DEBUG_INTERFACE DEBUG; +DEBUG_INFO *DEBUG_info = NULL; +int DEBUG_inside_eval = 0; + +static bool calc_line_from_position(CLASS *class, FUNCTION *func, PCODE *addr, ushort *line) +{ + int i; + ushort pos = addr - JIT_get_code(func); + ushort *post; + + if (func->debug) + { + post = func->debug->pos; + for (i = 0; i < (func->debug->nline - 1); i++) + { + if (pos >= post[i] && pos < post[i + 1]) + { + *line = i + func->debug->line; + return FALSE; + } + } + + /*printf("pos = %d addr=%p func->code=%p\n", pos, addr, func->code);*/ + } + + return TRUE; +} + +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc) +{ +#if DEBUG_MEMORY + static char buffer[256]; + const int buffer_size = sizeof(buffer); +#else + char *buffer = COMMON_buffer; + const int buffer_size = COMMON_BUF_MAX; +#endif + + ushort line = 0; + + if (!cp || !pc) + return "?"; + + if (fp != NULL && fp->debug) + calc_line_from_position(cp, fp, pc, &line); + + if (cp->component) + { + snprintf(buffer, buffer_size, "[%s].%s.%s.%d", + cp->component->name, cp->name, + (fp && fp->debug) ? fp->debug->name : "?", + line); + } + else + { + snprintf(buffer, buffer_size, "%s.%s.%d", + cp->name, + (fp && fp->debug) ? fp->debug->name : "?", + line); + } + + return buffer; +} + + +const char *DEBUG_get_current_position(void) +{ + return DEBUG_get_position(CP, FP, PC); +} + + +void DEBUG_init(void) +{ + const char *dir; + const char *fifo_name; + int pid; + int fd_lock; + int n; + + if (!EXEC_debug) + { + sprintf(COMMON_buffer, DEBUG_WAIT_LINK, PROJECT_name); + + dir = FILE_readlink(COMMON_buffer); + if (!dir) + return; + + for (n = DEBUG_WAIT_IGNORE_MAX; n >= 1; n--) + { + sprintf(COMMON_buffer, DEBUG_WAIT_IGNORE, PROJECT_name, n); + if (unlink(COMMON_buffer) == 0) + return; + } + + sprintf(COMMON_buffer, DEBUG_WAIT_LINK, PROJECT_name); + + if (unlink(COMMON_buffer)) + return; + + pid = atoi(FILE_get_name(dir)); + if (!pid) + return; + + sprintf(COMMON_buffer, DEBUG_FIFO_PATTERN, getuid(), pid, "lock"); + fd_lock = open(COMMON_buffer, O_CREAT | O_WRONLY | O_CLOEXEC, 0666); + if (fd_lock < 1) + return; + + STREAM_lock_all_fd(fd_lock); // On program end, that file will be automatically closed, and the lock released. + + EXEC_debug = TRUE; + EXEC_fifo = TRUE; + + sprintf(COMMON_buffer, DEBUG_FIFO_PATTERN, getuid(), pid, ""); + fifo_name = COMMON_buffer; + } + else + fifo_name = EXEC_fifo_name; + + COMPONENT_load(COMPONENT_create("gb.debug")); + LIBRARY_get_interface_by_name("gb.debug", DEBUG_INTERFACE_VERSION, &DEBUG); + + DEBUG_info = DEBUG.Init((GB_DEBUG_INTERFACE *)(void *)GAMBAS_DebugApi, EXEC_fifo, fifo_name); + + if (!DEBUG_info) + ERROR_panic("Cannot initialize debug mode"); + + if (EXEC_profile) + { + EXEC_profile_instr = TRUE; + DEBUG.Profile.Init(EXEC_profile_path); + } +} + + +void DEBUG_exit(void) +{ + if (!EXEC_debug || !DEBUG_is_init()) + return; + + DEBUG.Exit(); + if (EXEC_profile) + DEBUG.Profile.Exit(); +} + +void DEBUG_enter_event_loop(void) +{ + if (EXEC_profile) + DEBUG.Profile.Begin(NULL, NULL); +} + +void DEBUG_leave_event_loop(void) +{ + if (EXEC_profile) + DEBUG.Profile.End(NULL, NULL); +} + +void DEBUG_where(void) +{ + //static bool breakpoint = FALSE; + const char *where = DEBUG_get_current_position(); + /*if (!breakpoint && !strcmp(where, "FForm._new.97")) + { + breakpoint = TRUE; + BREAKPOINT(); + }*/ + fprintf(stderr, "%s: ", where); +} + + +bool DEBUG_get_value(const char *sym, int len, GB_VARIANT *ret) +{ + int i; + VALUE value; + LOCAL_SYMBOL *lp; + GLOBAL_SYMBOL *gp; + CLASS_VAR *var; + char *addr; + CLASS *class; + void *ref; + + if (DEBUG_info->fp) + { + int n_local = DEBUG_info->fp->n_param + DEBUG_info->fp->n_local; + + for (i = 0; i < DEBUG_info->fp->debug->n_local; i++) + { + lp = &DEBUG_info->fp->debug->local[i]; + if (len == lp->sym.len && strncasecmp(sym, lp->sym.name, len) == 0) + { + if (i >= n_local) + { + var = &DEBUG_info->cp->load->stat[lp->value]; + addr = (char *)DEBUG_info->cp->stat + var->pos; + ref = DEBUG_info->cp; + VALUE_class_read(DEBUG_info->cp, &value, addr, var->type, ref); + goto __FOUND_NO_BORROW; + } + else if (lp->value >= 0) + value = DEBUG_info->bp[lp->value]; + else + value = DEBUG_info->pp[lp->value]; + goto __FOUND; + } + } + } + + if (DEBUG_info->cp) + { + for (i = 0; i < DEBUG_info->cp->load->n_global; i++) + { + gp = &DEBUG_info->cp->load->global[i]; + if (len != gp->sym.len || strncasecmp(sym, gp->sym.name, len) != 0) + continue; + + if (CTYPE_get_kind(gp->ctype) == TK_VARIABLE) + { + if (!CTYPE_is_static(gp->ctype)) + { + if (!DEBUG_info->op) + continue; + var = &DEBUG_info->cp->load->dyn[gp->value]; + addr = (char *)DEBUG_info->op + var->pos; + ref = DEBUG_info->op; + } + else + { + var = &DEBUG_info->cp->load->stat[gp->value]; + addr = (char *)DEBUG_info->cp->stat + var->pos; + ref = DEBUG_info->cp; + } + + VALUE_class_read(DEBUG_info->cp, &value, addr, var->type, ref); + goto __FOUND_NO_BORROW; + } + else if (CTYPE_get_kind(gp->ctype) == TK_CONST) + { + VALUE_class_constant(DEBUG_info->cp, &value, gp->value); + goto __FOUND; + } + } + } + + //class = CLASS_look_global(sym, len); + class = CLASS_look(sym, len); + if (class) + { + if (class->auto_create && class->instance) + { + value._object.class = class; + value._object.object = class->instance; + value._object.super = NULL; + } + else + { + value.type = T_CLASS; + value._class.class = class; + value._class.super = NULL; + } + goto __FOUND; + } + + return TRUE; + +__FOUND: + + BORROW(&value); + +__FOUND_NO_BORROW: + + VALUE_conv_variant(&value); + UNBORROW(&value); + + *((VALUE *)ret) = value; + return FALSE; +} + +int DEBUG_set_value(const char *sym, int len, VALUE *value) +{ + int i; + LOCAL_SYMBOL *lp; + GLOBAL_SYMBOL *gp; + CLASS_VAR *var; + char *addr; + VALUE *where; + bool ret = GB_DEBUG_SET_OK; + + TRY + { + if (DEBUG_info->fp) + { + for (i = 0; i < DEBUG_info->fp->debug->n_local; i++) + { + lp = &DEBUG_info->fp->debug->local[i]; + if (len == lp->sym.len && strncasecmp(sym, lp->sym.name, len) == 0) + { + if (lp->value >= 0) + where = &DEBUG_info->bp[lp->value]; + else + where = &DEBUG_info->pp[lp->value]; + VALUE_conv(value, where->type); + RELEASE(where); + *where = *value; + BORROW(where); + goto __FOUND; + } + } + } + + if (DEBUG_info->cp) + { + for (i = 0; i < DEBUG_info->cp->load->n_global; i++) + { + gp = &DEBUG_info->cp->load->global[i]; + if (len != gp->sym.len || strncasecmp(sym, gp->sym.name, len) != 0) + continue; + + if (CTYPE_get_kind(gp->ctype) == TK_VARIABLE) + { + if (!CTYPE_is_static(gp->ctype) && DEBUG_info->op) + { + var = &DEBUG_info->cp->load->dyn[gp->value]; + addr = (char *)DEBUG_info->op + var->pos; + } + else + { + var = &DEBUG_info->cp->load->stat[gp->value]; + addr = (char *)DEBUG_info->cp->stat + var->pos; + } + + VALUE_class_write(DEBUG_info->cp, value, addr, var->type); + goto __FOUND; + } + } + } + + ret = GB_DEBUG_SET_READ_ONLY; + +__FOUND: + + 0; + } + CATCH + { + ret = GB_DEBUG_SET_ERROR; + } + END_TRY + + if (ret == GB_DEBUG_SET_ERROR) + EXEC_set_native_error(TRUE); + + return ret; +} + +int DEBUG_get_object_access_type(void *object, CLASS *class, int *count) +{ + CLASS_DESC *desc; + CLASS_DESC_METHOD *dm; + char type; + int access = GB_DEBUG_ACCESS_NORMAL; + + //fprintf(stderr, "DEBUG_can_be_used_like_an_array: %p %s ?\n", object, class->name); + + if (!object) + goto __NORMAL; + + if (class == CLASS_Class || OBJECT_is_class(object)) + { + class = (CLASS *)object; + object = NULL; + CLASS_load(class); + } + + dm = CLASS_get_special_desc(class, SPEC_GET); + if (!dm) + { + //fprintf(stderr, "No _get method\n"); + goto __NORMAL; + } + + if (dm->npmin != 1 || dm->npmax != 1) + { + //fprintf(stderr, "No _get(Arg AS Integer) method\n"); + goto __NORMAL; + } + + if (*dm->signature == T_INTEGER) + access = GB_DEBUG_ACCESS_ARRAY; + else if (*dm->signature == T_STRING) + access = GB_DEBUG_ACCESS_COLLECTION; + else + goto __NORMAL; + + desc = CLASS_get_symbol_desc(class, "Count"); + if (!desc) + goto __NORMAL; + + type = CLASS_DESC_get_type(desc); + + // The two only possible cases: + // A static read-only property, and object == NULL + // or a dynamic read-only property, and object != NULL + + if (!((type == CD_PROPERTY_READ && object) || (type == CD_STATIC_PROPERTY_READ && !object))) + goto __NORMAL; + + TRY + { + if (desc->property.native) + { + if (EXEC_call_native(desc->property.read, object, desc->property.type, 0)) + access = GB_DEBUG_ACCESS_NORMAL; + } + else + { + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 0; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.read; + //EXEC.func = &class->load->func[(int)desc->property.read]; + + EXEC_function_keep(); + EXEC_move_ret_to_temp(); + } + + if (access != GB_DEBUG_ACCESS_NORMAL) + { + VALUE_conv_integer(&TEMP); + *count = TEMP._integer.value; + } + } + CATCH + { + access = GB_DEBUG_ACCESS_NORMAL; + } + END_TRY + + + // For collection-like objects, check _next and Key property + + if (access == GB_DEBUG_ACCESS_COLLECTION) + { + dm = CLASS_get_special_desc(class, SPEC_NEXT); + if (!dm) + goto __NORMAL; + + desc = CLASS_get_symbol_desc(class, "Key"); + if (!desc) + goto __NORMAL; + + type = CLASS_DESC_get_type(desc); + if (type != CD_PROPERTY_READ && type != CD_PROPERTY && type != CD_STATIC_PROPERTY_READ && type != CD_STATIC_PROPERTY && type != CD_VARIABLE && type != CD_STATIC_VARIABLE) + goto __NORMAL; + } + + return access; + +__NORMAL: + return GB_DEBUG_ACCESS_NORMAL; +} + +void DEBUG_print_backtrace(STACK_BACKTRACE *bt) +{ + int i, n; + bool stop; + STACK_BACKTRACE *end; + //STACK_CONTEXT *sc = (STACK_CONTEXT *)(STACK_base + STACK_size) - err->bt_count; + + //fprintf(stderr, "0: %s\n", DEBUG_get_position(err->cp, err->fp, err->pc)); + for (i = 0, n = 0, stop = FALSE; !stop; i++) + { + //fprintf(stderr, "%d: %s\n", i, DEBUG_get_position(bt[i].cp, bt[i].fp, bt[i].pc)); + if (STACK_backtrace_is_end(&bt[i])) + { + stop = TRUE; + end = &bt[i]; + STACK_backtrace_clear_end(end); + } + if (bt[i].pc) + { + n++; + fprintf(stderr, "%s ", DEBUG_get_position(bt[i].cp, bt[i].fp, bt[i].pc)); + } + } + fputc('\n', stderr); + + STACK_backtrace_set_end(end); +} + + +void DEBUG_print_current_backtrace(void) +{ + STACK_BACKTRACE *bt = STACK_get_backtrace(); + if (bt) + { + DEBUG_print_backtrace(bt); + STACK_free_backtrace(&bt); + } +} + + +GB_ARRAY DEBUG_get_string_array_from_backtrace(STACK_BACKTRACE *bt) +{ + GB_ARRAY array; + int i, n, size; + STACK_BACKTRACE *end; + + for (i = 0, size = 0;; i++) + { + if (bt[i].pc) + size++; + if (STACK_backtrace_is_end(&bt[i])) + { + end = &bt[i]; + STACK_backtrace_clear_end(end); + break; + } + } + + GB_ArrayNew(&array, GB_T_STRING, size); + //*((char **)GB_ArrayGet(array, 0)) = STRING_new_zero(DEBUG_get_position(err->cp, err->fp, err->pc)); + + for (i = 0, n = 0; n < size; i++) + { + if (!bt[i].pc) + continue; + *((char **)GB_ArrayGet(array, n)) = STRING_new_zero(DEBUG_get_position(bt[i].cp, bt[i].fp, bt[i].pc)); + n++; + } + + STACK_backtrace_set_end(end); + return array; +} + +GB_CLASS DEBUG_find_class(const char *comp_name, const char *class_name) +{ + CLASS *class; + COMPONENT *save_comp; + CLASS *save_class; + + if (comp_name) + { + save_comp = COMPONENT_current; + COMPONENT_current = COMPONENT_find(comp_name); + } + else + { + save_class = CP; + CP = NULL; + } + + /*// As the startup class is automatically exported, this is the only way for the debugger to find it. + if (PROJECT_class && !strcmp(name, PROJECT_class->name)) + return (GB_CLASS)PROJECT_class;*/ + + class = CLASS_find(class_name); + + if (comp_name) + COMPONENT_current = save_comp; + else + CP = save_class; + + return (GB_CLASS)class; +} + +void DEBUG_enum_keys(void *object, GB_DEBUG_ENUM_CB cb) +{ + CENUM *old; + CENUM *cenum = NULL; + CLASS *class; + bool err = FALSE; + char *str; + int len; + char *save_key; + int save_lkey; + GB_VALUE value; + void *prop_ob; + + old = EXEC_enum; + + class = OBJECT_class(object); + if (class == CLASS_Class) + { + class = (CLASS *)object; + object = NULL; + } + + prop_ob = object ? object : class; + + TRY + { + GB_GetProperty(prop_ob, "Key"); + SUBR_get_string_len(&TEMP, &save_key, &save_lkey); + (*cb)(save_key, save_lkey); + + cenum = CENUM_create(prop_ob); + + EXEC_enum = cenum; + EXEC_special(SPEC_FIRST, class, object, 0, TRUE); + EXEC_enum = old; + + if (cenum->stop) + { + err = TRUE; + goto __END; + } + + for(;;) + { + EXEC_enum = cenum; + err = EXEC_special(SPEC_NEXT, class, object, 0, TRUE); + EXEC_enum = old; + + if (err || cenum->stop) + { + err = TRUE; + break; + } + + GB_GetProperty(prop_ob, "Key"); + + SUBR_get_string_len(&TEMP, &str, &len); + (*cb)(str, len); + } + + __END: + + value.type = GB_T_CSTRING; + value._string.value.addr = save_key; + value._string.value.start = 0; + value._string.value.len = save_lkey; + GB_SetProperty(prop_ob, "Key", &value); + + if (err) + OBJECT_UNREF(cenum); + } + CATCH + { + OBJECT_UNREF(cenum); + } + END_TRY +} + +void DEBUG_enter_eval() +{ + DEBUG_inside_eval++; +} + +void DEBUG_leave_eval() +{ + DEBUG_inside_eval--; +} + +void DEBUG_breakpoint(ushort code) +{ + if (EXEC_trace) + { + double timer; + int i; + + DATE_timer(&timer, TRUE); + fprintf(stderr, "[%d.%06d] ", (int)timer, (int)(timer * 1000000) % 1000000); + for (i = 0; i < STACK_frame_count; i++) + fputs(". ", stderr); + fputs(DEBUG_get_current_position(), stderr); + fputc('\n', stderr); + fflush(stderr); + } + + if (EXEC_debug) + { + /*TC = PC + 1; + TP = SP;*/ + + //fprintf(stderr, "%s %d\n", DEBUG_get_current_position(), DEBUG_info->watch); + + if (CP && (EXEC_debug_inside || !CP->component)) + { + if (EXEC_profile_instr) + DEBUG.Profile.Add(CP, FP, PC); + + if (DEBUG_info->watch) + { + if (DEBUG.CheckWatches()) + return; + } + + code = (uchar)code; + + if (code == 0) + { + if (!DEBUG_info->stop) + return; + + // Return from (void stack) + if (DEBUG_info->leave) + { + if (STACK_get_current()->pc) + return; + if (FP == DEBUG_info->fp) + return; + if (BP > DEBUG_info->bp) + return; + } + // Forward or Return From + else if (DEBUG_info->fp != NULL) + { + if (BP > DEBUG_info->bp) + return; + } + // otherwise, Next + } + + DEBUG.Breakpoint(code); + } + } +} diff --git a/main/gbx/gbx_debug.h b/main/gbx/gbx_debug.h new file mode 100644 index 00000000..ae0653bd --- /dev/null +++ b/main/gbx/gbx_debug.h @@ -0,0 +1,90 @@ +/*************************************************************************** + + gbx_debug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_DEBUG_H +#define __GBX_DEBUG_H + +#include "gbx_class.h" +#include "gbx_stack.h" +#include "gb_pcode.h" +#include "../lib/debug/gb.debug.h" + +/* Object referencement debugging */ +#define DEBUG_REF 0 +/* String referencement debugging */ +#define DEBUG_STRING 0 +/* Bytecode debugging */ +#define DEBUG_PCODE 0 +/* Class loading debugging */ +#define DEBUG_LOAD 0 +/* Event debugging */ +#define DEBUG_EVENT 0 +/* Component management debugging */ +#define DEBUG_COMP 0 +/* Stream opening debugging */ +#define DEBUG_STREAM 0 + +#ifndef __GBX_DEBUG_C + +EXTERN DEBUG_INTERFACE DEBUG; +EXTERN DEBUG_INFO *DEBUG_info; +EXTERN int DEBUG_inside_eval; + +#endif + +#define DEBUG_is_init() (DEBUG_info != NULL) + +void DEBUG_init(void); +void DEBUG_exit(void); + +void DEBUG_breakpoint(ushort code); + +void DEBUG_enter_event_loop(void); +void DEBUG_leave_event_loop(void); + +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc); +const char *DEBUG_get_current_position(void); +void DEBUG_where(void); + +bool DEBUG_get_value(const char *sym, int len, GB_VARIANT *ret); +int DEBUG_set_value(const char *sym, int len, VALUE *value); +int DEBUG_get_object_access_type(void *object, CLASS *class, int *count); +GB_CLASS DEBUG_find_class(const char *comp_name, const char *class_name); +void DEBUG_enum_keys(void *object, GB_DEBUG_ENUM_CB cb); + +void DEBUG_print_backtrace(STACK_BACKTRACE *bt); +void DEBUG_print_current_backtrace(void); +GB_ARRAY DEBUG_get_string_array_from_backtrace(STACK_BACKTRACE *bt); + +void DEBUG_enter_eval(void); +void DEBUG_leave_eval(void); + +#define PROFILE_ENTER_FUNCTION() \ + if (EXEC_profile && CP && (EXEC_debug_inside || CP->component == NULL)) \ + DEBUG.Profile.Begin(CP, FP); \ + +#define PROFILE_LEAVE_FUNCTION() \ + if (EXEC_profile && CP && (EXEC_debug_inside || CP->component == NULL)) \ + DEBUG.Profile.End(CP, FP); \ + +#endif diff --git a/main/gbx/gbx_eval.c b/main/gbx/gbx_eval.c new file mode 100644 index 00000000..d3d89150 --- /dev/null +++ b/main/gbx/gbx_eval.c @@ -0,0 +1,162 @@ +/*************************************************************************** + + gbx_eval.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EVAL_C + +#include "gb_common.h" + +#include "gb_array.h" +#include "gbx_string.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gbx_eval.h" + +#include "gbx_c_collection.h" +#include "gbx_api.h" + +bool EVAL_debug = FALSE; + +static EXPRESSION *EVAL; + +static void EVAL_enter() +{ + STACK_push_frame(&EXEC_current, EVAL->func.stack_usage); + + BP = SP; + PP = SP; + FP = &EVAL->func; + PC = EVAL->func.code; + + OP = NULL; + CP = &EVAL->exec_class; + EXEC_check_bytecode(); + + //AP = ARCH_from_class(CP); + + EP = NULL; + EC = NULL; + GP = NULL; + + RP->type = T_VOID; + + PROFILE_ENTER_FUNCTION(); +} + +static void error_EVAL_exec(void) +{ + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); +} + +static void EVAL_exec() +{ + // We need to push a void frame, because EXEC_leave looks at *PC to know if a return value is expected + STACK_push_frame(&EXEC_current, 0); + + PC = NULL; + GP = NULL; + + ON_ERROR(error_EVAL_exec) + { + EVAL_enter(); + } + END_ERROR + + EXEC_function_loop(); + EXEC_move_ret_to_temp(); +} + +bool EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION func) +{ + int i; + EVAL_SYMBOL *sym; + bool debug; + bool error; + int nvar; + EXPRESSION *save_eval; + /*HASH_TABLE *hash_table; + char *name; + CCOL_ENUM enum_state;*/ + + save_eval = EVAL; + EVAL = expr; + + #ifdef DEBUG + fprintf(stderr, "EVAL: %s\n", EVAL->source); + #endif + + STACK_enable_for_eval(); + debug = EXEC_debug; + EXEC_debug = FALSE; + error = FALSE; + + nvar = EVAL->nvar; + + STACK_check(nvar); + + if (expr->custom) + nvar--; + + for (i = 0; i < nvar; i++) + { + SP->type = T_VARIANT; + SP->_variant.vtype = T_NULL; + SP++; + + sym = (EVAL_SYMBOL *)TABLE_get_symbol(EVAL->table, EVAL->var[EVAL->nvar - i - 1]); + if ((*func)(sym->sym.name, sym->sym.len, (GB_VARIANT *)&SP[-1])) + { + GB_Error("Unknown symbol"); + error = TRUE; + goto __LEAVE; + } + + BORROW(SP - 1); + } + + if (expr->custom) + { + SP->type = T_OBJECT; + SP->_object.object = expr->parent; + OBJECT_REF_CHECK(expr->parent); + SP++; + } + + TRY + { + EVAL_exec(); + } + CATCH + { + error = TRUE; + } + END_TRY + +__LEAVE: + + STACK_disable_for_eval(); + EXEC_debug = debug; + EVAL = save_eval; + return error; +} diff --git a/main/gbx/gbx_eval.h b/main/gbx/gbx_eval.h new file mode 100644 index 00000000..57b6e42f --- /dev/null +++ b/main/gbx/gbx_eval.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + gbx_eval.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EVAL_H +#define __GBX_EVAL_H + +#include "gb_table.h" +#include "gbx_class.h" +#include "gb_reserved.h" +#include "gbx_c_collection.h" + +#include "../lib/eval/gb.eval.h" +#include "gbx_expression.h" + +#ifndef __GBX_EVAL_C +EXTERN bool EVAL_debug; +#endif + +bool EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION func); + +#endif diff --git a/main/gbx/gbx_event.c b/main/gbx/gbx_event.c new file mode 100644 index 00000000..e753cc87 --- /dev/null +++ b/main/gbx/gbx_event.c @@ -0,0 +1,274 @@ +/*************************************************************************** + + gbx_event.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EVENT_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_signal.h" + +#include "gbx_event.h" + +//#define DEBUG_ME 1 + +void *EVENT_Last = NULL; + +char *EVENT_PreviousName = NULL; +char *EVENT_Name = NULL; + +static EVENT_POST *_post_list = NULL; + + +static void check_event_method(CLASS *class, const char *name, CLASS_DESC_METHOD *desc, CLASS_EVENT *event) +{ + int n = event->n_param; + + if (n < desc->npmin) + { + /*printf("event->n_param = %d desc->npmin = %d desc->npmax = %d\n", event->n_param, desc->npmin, desc->npmax);*/ + THROW(E_EVENT, CLASS_get_name(class), name, "Too many arguments"); + } + + if (n > desc->npmax) + { + if (!desc->npvar) + THROW(E_EVENT, CLASS_get_name(class), name, "Not enough arguments"); + + n = desc->npmax; + } + + if (TYPE_compare_signature(desc->signature, n, (TYPE *)event->param, n, FALSE)) + THROW(E_EVENT, CLASS_get_name(class), name, "Type mismatch"); + + if (desc->type != T_VOID) + THROW(E_EVENT, CLASS_get_name(class), name, "Not a procedure"); +} + + +void EVENT_search(CLASS *class, ushort *event, const char *name, OBJECT *parent) +{ + //ushort *event; + //ushort *self; + CLASS *class_parent; + int i, index; + const char *event_name; + CLASS_DESC *desc; + int kind; + int len_name, len_event_name; + char *func_name = COMMON_buffer; + char *pevent_name; + + #if DEBUG_ME + fprintf(stderr, "EVENT_search: class = %s name = %s parent = %p\n", class->name, name, parent); + #endif + + //event = OBJECT_event(object)->event; + //self = OBJECT_event(object)->self; + + if (class->n_event <= 0) + return; + + len_name = name ? strlen(name) : 0; + + if (len_name == 0 || len_name >= (COMMON_BUF_MAX - 4)) + { + memset(event, 0, class->n_event * sizeof(ushort)); + return; + } + + kind = parent->class == CLASS_Class ? CD_STATIC_METHOD : CD_METHOD; + + if (kind == CD_STATIC_METHOD) + class_parent = (CLASS *)parent; + else + class_parent = parent->class; + + strcpy(func_name, name); + pevent_name = &func_name[len_name]; + *pevent_name++ = '_'; + + for (i = 0; i < class->n_event; i++) + { + event[i] = 0; + + event_name = class->event[i].name; + if (*event_name == ':') + event_name++; + + len_event_name = strlen(event_name); + if ((len_name + len_event_name + 1) >= COMMON_BUF_MAX) + continue; + + strcpy(pevent_name, event_name); + index = CLASS_find_symbol(class_parent, func_name); + + if (index != NO_SYMBOL) + { + desc = class_parent->table[index].desc; + if (CLASS_DESC_get_type(desc) == kind) + { + #if DEBUG_ME + fprintf(stderr, "EVENT_search: FOUND [%d] %s.%s index = %d parent = %s.%p\n", i, class_parent->name, func_name, index, parent->class->name, parent); + #endif + check_event_method(class_parent, func_name, &desc->method, &class->event[i]); + event[i] = index + 1; + } + } + + #if 0 + snprintf(func_name, COMMON_BUF_MAX, "ME_%s", event_name); + index = CLASS_find_symbol(class, func_name); + + if (index != NO_SYMBOL) + { + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) == CD_METHOD) + { + #if DEBUG_ME + printf("EVENT_search: FOUND [%d] %s.%s index = %d parent = %s.%p\n", i, class->name, func_name, index, parent->class->name, parent); + #endif + check_event_method(class, func_name, &desc->method, &class->event[i]); + if (!self) + { + ALLOC_ZERO(&self, sizeof(ushort) * class->n_event, "EVENT_search"); + OBJECT_event(object)->self = self; + } + self[i] = index + 1; + } + } + #endif + } +} + + + +void EVENT_exit() +{ + EVENT_POST *ep; + + while (_post_list) + { + ep = _post_list; + _post_list = _post_list->list.next; + FREE(&ep); + } + + STRING_free(&EVENT_Name); +} + + +static void post(void (*func)(), int nparam, intptr_t param, intptr_t param2) +{ + EVENT_POST *ep; + + /*printf("EVENT_post\n"); + fflush(NULL);*/ + + ALLOC(&ep, sizeof(EVENT_POST)); + + ep->func = func; + ep->nparam = nparam; + ep->param = param; + ep->param2 = param2; + + LIST_insert(&_post_list, ep, &ep->list); + + HOOK(post)(); +} + + +void EVENT_post(void (*func)(), intptr_t param) +{ + post(func, 1, param, 0); +} + +void EVENT_post2(void (*func)(), intptr_t param, intptr_t param2) +{ + post(func, 2, param, param2); +} + +static void post_event(void *object, int event) +{ + GB_Raise(object, event, 0); + OBJECT_UNREF(object); +} + +void EVENT_post_event(void *object, int event) +{ + OBJECT_REF(object); + post((void (*)())post_event, 2, (intptr_t)object, (intptr_t)event); +} + + +bool EVENT_check_post(void) +{ + EVENT_POST *ep; + EVENT_POST save; + bool ret = FALSE; + + #ifdef DEBUG_POST + fprintf(stderr, "EVENT_check_post: START\n"); + #endif + + SIGNAL_check(SIGCHLD); + + while (_post_list) + { + ret = TRUE; + ep = _post_list; + save = *ep; + LIST_remove(&_post_list, _post_list, &_post_list->list); + + FREE(&ep); + + if (save.nparam == 1) + (*save.func)(save.param); + else + (*save.func)(save.param, save.param2); + } + + /*norec = FALSE;*/ + + #ifdef DEBUG_POST + fprintf(stderr, "EVENT_check_post: END\n"); + #endif + + return ret; +} + + +char *EVENT_enter_name(const char *name) +{ + char *save = EVENT_PreviousName; + EVENT_PreviousName = EVENT_Name; + EVENT_Name = (char *)name; + return save; +} + +void EVENT_leave_name(const char *save) +{ + EVENT_Name = EVENT_PreviousName; + EVENT_PreviousName = (char *)save; +} diff --git a/main/gbx/gbx_event.h b/main/gbx/gbx_event.h new file mode 100644 index 00000000..580e7e72 --- /dev/null +++ b/main/gbx/gbx_event.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + gbx_event.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EVENT_H +#define __GBX_EVENT_H + +#include "gbx_class.h" +#include "gbx_object.h" +#include "gb_list.h" + + +#ifndef __GBX_EVENT_C +extern void *EVENT_Last; +extern char *EVENT_PreviousName; +extern char *EVENT_Name; +#endif + +typedef + struct _EVENT_POST { + LIST list; + void (*func)(); + int nparam; + intptr_t param; + intptr_t param2; + } + EVENT_POST; + + +void EVENT_search(CLASS *class, ushort *event, const char *name, OBJECT *parent); +void EVENT_post(void (*func)(), intptr_t param); +void EVENT_post2(void (*func)(), intptr_t param, intptr_t param2); +bool EVENT_check_post(void); +void EVENT_init(void); +void EVENT_exit(void); +void EVENT_post_event(void *object, int event); +char *EVENT_enter_name(const char *name); +void EVENT_leave_name(const char *save); + +#endif diff --git a/main/gbx/gbx_exec.c b/main/gbx/gbx_exec.c new file mode 100644 index 00000000..96e41aa3 --- /dev/null +++ b/main/gbx/gbx_exec.c @@ -0,0 +1,2060 @@ +/*************************************************************************** + + gbx_exec.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXEC_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_overflow.h" +#include "gbx_type.h" + +#include +#include + +#include "gb_limit.h" +#include "gbx_subr.h" +#include "gbx_stack.h" +#include "gbx_debug.h" +#include "gbx_event.h" +#include "gbx_string.h" +#include "gbx_date.h" + +#include "gbx_c_collection.h" + +#include "gbx_api.h" +#include "gbx_jit.h" +#include "gbx_exec.h" + +//#define DEBUG_STACK 1 +//#define SHOW_FUNCTION 1 + +STACK_CONTEXT EXEC_current = { 0 }; // Current virtual machine state +VALUE *SP = NULL; // Stack pointer +VALUE TEMP; // Temporary storage or return value of a native function +VALUE RET; // Return value of a gambas function +VALUE *EXEC_super = NULL; // SUPER was used for this stack pointer +CENUM *EXEC_enum; // Current iterator + +EXEC_FLAG FLAG = { 0 }; + +const char *EXEC_profile_path = NULL; // profile file path +const char *EXEC_fifo_name = NULL; // fifo name +EXEC_HOOK EXEC_Hook = { NULL }; +EXEC_GLOBAL EXEC; +uint64_t EXEC_byref = 0; + +unsigned char EXEC_quit_value = 0; // interpreter return value + +#if 0 +bool EXEC_big_endian; // CPU endianness +bool EXEC_debug_inside = FALSE; // debug inside components +bool EXEC_debug_hold = FALSE; // hold execution at program end +bool EXEC_task = FALSE; // I am a background task +bool EXEC_profile = FALSE; // profiling mode +bool EXEC_profile_instr = FALSE; // profiling mode at instruction level +bool EXEC_trace = FALSE; // tracing mode +bool EXEC_arch = FALSE; // executing an archive +bool EXEC_fifo = FALSE; // debugging through a fifo +bool EXEC_keep_library = FALSE; // do not unload libraries +bool EXEC_main_hook_done = FALSE; +bool EXEC_break_on_error = FALSE; // if we must break into the debugger as soon as there is an error. +bool EXEC_in_event_loop = FALSE; // if we are in the event loop +#if DO_NOT_CHECK_OVERFLOW +#else +bool EXEC_check_overflow = TRUE; // if we should check for overflow +#endif +#endif + +const char EXEC_should_borrow[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 0, 0, 1 }; + +const char *EXEC_unknown_name; +bool EXEC_unknown_property; +char EXEC_unknown_nparam; + +void EXEC_init(void) +{ + union { + char _string[4]; + uint _int; + } + test; + + PC = NULL; + BP = NULL; + OP = NULL; + CP = NULL; + RP->type = T_VOID; + + test._string[0] = 0xAA; + test._string[1] = 0xBB; + test._string[2] = 0xCC; + test._string[3] = 0xDD; + + FLAG.big_endian = test._int == 0xAABBCCDDL; + + /*if (EXEC_big_endian) + ERROR_warning("CPU is big endian");*/ + + FLAG.check_overflow = TRUE; + + DATE_init(); + + EXEC_init_bytecode_check(); +} + + +void EXEC_borrow(TYPE type, VALUE *value) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + goto *jump[type]; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_ref(value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_REF_CHECK(value->_variant.value._object); + return; + +__FUNCTION: + OBJECT_REF_CHECK(value->_function.object); + return; + +__STRING: + STRING_ref(value->_string.addr); + +__NONE: + return; +} + + +void EXEC_unborrow(VALUE *value) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + TYPE type = value->type; + + if (TYPE_is_object(type)) + { + OBJECT_UNREF_KEEP(value->_object.object); + return; + } + + goto *jump[type]; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_unref_keep(&value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_UNREF_KEEP(value->_variant.value._object); + return; + +__FUNCTION: + OBJECT_UNREF_KEEP(value->_function.object); + return; + +__STRING: + STRING_unref_keep(&value->_string.addr); + +__NONE: + return; +} + + +void EXEC_release(TYPE type, VALUE *value) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + goto *jump[type]; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_unref(&value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_UNREF(value->_variant.value._object); + return; + +__FUNCTION: + OBJECT_UNREF(value->_function.object); + return; + +__STRING: + STRING_unref(&value->_string.addr); + +__NONE: + return; +} + +void RELEASE_many(VALUE *value, int n) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + TYPE type; + + while (n) + { + n--; + value--; + + type = value->type; + + if (TYPE_is_object(type)) + { + OBJECT_UNREF(value->_object.object); + continue; + } + + goto *jump[type]; + + __VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_unref(&value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_UNREF(value->_variant.value._object); + continue; + + __FUNCTION: + OBJECT_UNREF(value->_function.object); + continue; + + __STRING: + STRING_unref(&value->_string.addr); + + __NONE: + continue; + } +} + +#if 0 +void DUMP(VALUE *value) +{ + static void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__VARIANT, &&__ARRAY, &&__NONE, &&__FUNCTION, &&__NONE, &&__NONE + }; + + TYPE type = value->type; + + printf("type = %p / ", (void *)type); + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__STRING: + printf("STRING %p\n", value->_string.addr); + return; + +__OBJECT: + if (value->_object.object) + { + printf("OBJECT (%p)\n", value->_object.object); + printf("-> %s\n", OBJECT_class(value->_object.object)->name); + } + else + printf("OBJECT (NULL)\n"); + return; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + printf("STRING %p\n", *((char **)value->_variant.value)); + else if (TYPE_is_object(value->_variant.vtype)) + printf("OBJECT (%s %p)\n", OBJECT_class(*((void **)value->_variant.value))->name, *((void **)value->_variant.value)); + return; + +__FUNCTION: + printf("FUNCTION %s (%s %p)\n", value->_function.class->name, OBJECT_class(value->_function.object)->name, value->_function.object); + return; + +__ARRAY: + printf("ARRAY\n"); + return; + +__NONE: + printf("\n"); + return; +} +#endif + +void EXEC_release_return_value(void) +{ + RELEASE(RP); + RP->type = T_VOID; +} + + +#define print_register() \ + fprintf(stderr, "| SP = %d BP = %d FP = %p PC = %p EC = %p\n", (int)(SP - (VALUE *)STACK_base), (int)(BP - (VALUE *)STACK_base), FP, PC, EC) + +static ushort exec_enter_can_quick(void) +{ + int i; + FUNCTION *func; + int nparam = EXEC.nparam; + + func = &EXEC.class->load->func[EXEC.index]; + + /* check number of arguments */ + + if (func->npmin < func->n_param || nparam != func->n_param || func->vararg) + return C_CALL_SLOW; + + /* check arguments type */ + + for (i = 0; i < nparam; i++) + { + if (SP[i - nparam].type != func->param[i].type) + return C_CALL_SLOW; + } + + return C_CALL_QUICK; +} + +static void init_local_var(CLASS *class, FUNCTION *func) +{ + static const void *jump[] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, + &&__OBJECT + }; + + CLASS_LOCAL *local; + int n; + CTYPE ctype; + VALUE *value; + + local = func->local; + value = SP; + + for (n = func->n_local; n; n--, value++, local++) + { + ctype = local->type; + + value->type = ctype.id; + goto *jump[ctype.id]; + + __BOOLEAN: + __BYTE: + __SHORT: + __INTEGER: + + value->_integer.value = 0; + continue; + + __LONG: + + value->_long.value = 0; + continue; + + __SINGLE: + value->_single.value = 0; + continue; + + __FLOAT: + value->_float.value = 0; + continue; + + __STRING: + value->_string.addr = NULL; + value->_string.start = 0; + value->_string.len = 0; + continue; + + __VARIANT: + value->_variant.vtype = T_NULL; + continue; + + __POINTER: + value->_pointer.value = NULL; + continue; + + __DATE: + value->_date.date = 0; + value->_date.time = 0; + continue; + + __VOID: + continue; + + __OBJECT: + if (ctype.value >= 0) + value->type = (TYPE)class->load->class_ref[ctype.value]; + value->_object.object = NULL; + continue; + + __FUNCTION: + __CLASS: + __NULL: + ERROR_panic("VALUE_default: Unknown default type"); + } + + SP = value; +} + +// EXEC.nparam must be set to the amount of stack that must be freed if an exception is raised during EXEC_enter() + +void EXEC_enter(void) +{ + int i; + FUNCTION *func; // = EXEC.func; + bool optional; + int nparam = EXEC.nparam; + void *object = EXEC.object; + CLASS *class = EXEC.class; + int64_t optargs; + + #if DEBUG_STACK + fprintf(stderr, "\n| >> EXEC_enter(%s, %d, %d)\n", EXEC.class->name, EXEC.index, nparam); + print_register(); + #endif + + func = &class->load->func[EXEC.index]; + #if DEBUG_STACK + if (func->debug) + fprintf(stderr, " | >> %s\n", func->debug->name); + #endif + + #if SHOW_FUNCTION + if (func->debug) + fprintf(stderr, "%s.%s\n", EXEC.class->name, func->debug->name); + #endif + + optional = func->optional; + + // Check number of arguments + + if (nparam < func->npmin) + THROW(E_NEPARAM); + else if (nparam > func->n_param && !func->vararg) + THROW(E_TMPARAM); + + // Mandatory arguments + + for (i = 0; i < func->npmin; i++) + VALUE_conv(SP - nparam + i, func->param[i].type); + + if (optional) + { + optargs = 0; + + // Optional arguments + + for (i = func->npmin; i < Min(func->n_param, nparam); i++) + { + if (SP[- nparam + i].type == T_VOID) + { + optargs |= (1 << i); + SP[- nparam + i]._void.ptype = func->param[i].type; + } + else + VALUE_conv(SP - nparam + i, func->param[i].type); + } + + // Missing optional arguments + + if (nparam < func->n_param) + { + STACK_check(func->n_param - nparam); + + for (i = nparam; i < func->n_param; i++) + { + optargs |= (1 << i); + SP->type = T_VOID; + SP->_void.ptype = func->param[i].type; + SP++; + } + + //EXEC.nparam = func->n_param; + nparam = func->n_param; + } + } + + // Save context & check stack + + STACK_push_frame(&EXEC_current, func->stack_usage); + + // Enter function + + BP = SP; + if (func->vararg) + PP = SP - (nparam - func->n_param); + else + PP = SP; + FP = func; + PC = func->code; + OP = object; + CP = class; + EXEC_check_bytecode(); + EP = NULL; + GP = NULL; + + PROFILE_ENTER_FUNCTION(); + + if (func->error) + { + #if DEBUG_ERROR + printf("EXEC_enter: EC = PC + %d\n", func->error); + #endif + EC = PC + func->error; + } + else + EC = NULL; + + // Reference the object so that it is not destroyed during the function call + OBJECT_REF_CHECK(OP); + + // Local variables initialization + + if (func->n_local > 0) + init_local_var(class, func); + + // Control variables initialization + + if (func->n_ctrl > 0) + { + for (i = 0; i < func->n_ctrl; i++) + { + SP->type = T_VOID; + SP++; + } + + // Optional argument map + if (optional && func->use_is_missing) + { + SP--; + SP->type = T_LONG; + SP->_long.value = optargs; + SP++; + } + } + + RP->type = T_VOID; + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_enter()\n"); + print_register(); + #endif +} + + +void EXEC_enter_check(bool defined) +{ + ushort mode = defined ? exec_enter_can_quick() : C_CALL_SLOW; + + *PC = (*PC & 0xFF) | mode; + + switch(mode) + { + case C_CALL_QUICK: EXEC_enter_quick(); break; + //case C_CALL_EASY: EXEC_enter_easy(); break; + default: EXEC_enter(); break; + } +} + + +void EXEC_enter_quick(void) +{ + int i; + FUNCTION *func;; + void *object = EXEC.object; + CLASS *class = EXEC.class; + + #if DEBUG_STACK + fprintf(stderr, "\n| >> EXEC_enter_quick(%s, %d, %d)\n", EXEC.class->name, EXEC.index, EXEC.nparam); + print_register(); + #endif + + func = &class->load->func[EXEC.index]; + #if DEBUG_STACK + if (func->debug) + fprintf(stderr, " | >> %s\n", func->debug->name); + #endif + + #if SHOW_FUNCTION + if (func->debug) + fprintf(stderr, "%s.%s\n", EXEC.class->name, func->debug->name); + #endif + + /* save context & check stack */ + + STACK_push_frame(&EXEC_current, func->stack_usage); + + /* enter function */ + + BP = SP; + PP = SP; + FP = func; + PC = func->code; + OP = object; + CP = class; + EXEC_check_bytecode(); + EP = NULL; + GP = NULL; + + if (func->error) + EC = PC + func->error; + else + EC = NULL; + + PROFILE_ENTER_FUNCTION(); + + /* reference the object so that it is not destroyed during the function call */ + OBJECT_REF_CHECK(OP); + + /* local variables initialization */ + + if (func->n_local != 0) + init_local_var(class, func); + + /* control variables initialization */ + + if (func->n_ctrl != 0) + { + for (i = 0; i < func->n_ctrl; i++) + { + SP->type = T_VOID; + SP++; + } + } + + RP->type = T_VOID; + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_enter()\n"); + print_register(); + #endif +} + +static int exec_leave_byref(ushort *pc, int nparam) +{ + int nbyref; + ushort *pc_func; + int nbyref_func; + VALUE *xp, *pp; + int i, n, bit; + int nb; + + pc++; + nbyref = 1 + (*pc & 0xF); + pc_func = FP->code; + + if (!PCODE_is(*pc_func, C_BYREF)) + return 0; + + nbyref_func = 1 + (*pc_func & 0xF); + if (nbyref_func < nbyref) + return 0; + + for (i = 1; i <= nbyref; i++) + { + if (pc[i] & ~pc_func[i]) + return 0; + } + + xp = PP - nparam; + pp = xp; + + for (i = 0, n = 0, nb = 0; i < nparam; i++) + { + bit = i & 15; + if (bit == 0) + n++; + + if (n <= nbyref && (pc[n] & (1 << bit))) + { + xp[nb] = *pp; + nb++; + } + else + { + RELEASE(pp); + } + pp++; + } + + pc--; + + n = SP - PP; + RELEASE_MANY(SP, n); + SP -= nparam; + + SP += nb; + OBJECT_UNREF(OP); + SP -= nb; + + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); + + PC += nbyref + 1; + return nb; +} + +#if 0 +#define EXEC_LEAVE_BYREF() \ + pc++; \ + nbyref = 1 + (*pc & 0xF); \ + pc_func = FP->code; \ + \ + if (!PCODE_is(*pc_func, C_BYREF)) \ + goto __LEAVE_NORMAL; \ + \ + nbyref_func = 1 + (*pc_func & 0xF); \ + if (nbyref_func < nbyref) \ + goto __LEAVE_NORMAL; \ + \ + for (i = 1; i <= nbyref; i++) \ + { \ + if (pc[i] & ~pc_func[i]) \ + goto __LEAVE_NORMAL; \ + } \ + \ + xp = PP - nparam; \ + pp = xp; \ + \ + for (i = 0, n = 0; i < nparam; i++) \ + { \ + bit = i & 15; \ + if (bit == 0) \ + n++; \ + \ + if (n <= nbyref && (pc[n] & (1 << bit))) \ + { \ + xp[nb] = *pp; \ + nb++; \ + } \ + else \ + { \ + RELEASE(pp); \ + } \ + pp++; \ + } \ + \ + pc--; \ + n = SP - PP; \ + RELEASE_MANY(SP, n); \ + SP -= nparam; \ + SP += nb; \ + OBJECT_UNREF(OP); \ + SP -= nb; \ + PROFILE_LEAVE_FUNCTION(); \ + STACK_pop_frame(&EXEC_current); \ + PC += nbyref + 1; \ + goto __RETURN_VALUE; +#endif + +void EXEC_leave_drop() +{ + int nparam; + //VALUE ret; + ushort *pc; + int n, nb; + +#if DEBUG_STACK + fprintf(stderr, "| >> EXEC_leave\n"); + print_register(); +#endif + + /* Save the return value. It can be erased by OBJECT_UNREF() */ + + //ret = *RP; + EXEC_release_return_value(); + + //VALUE_copy(&ret, RP); + + pc = STACK_get_previous_pc(); + nparam = FP->n_param; + + /* ByRef arguments management */ + + nb = (pc && PCODE_is(pc[1], C_BYREF)) ? exec_leave_byref(pc, nparam) : 0; + if (nb == 0) + { + n = nparam + (SP - PP); + RELEASE_MANY(SP, n); + + OBJECT_UNREF(OP); + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); + } + + SP += nb; + +#if DEBUG_STACK + fprintf(stderr, "| << EXEC_leave()\n"); + print_register(); + fprintf(stderr, "\n"); +#endif + return; +} + +void EXEC_leave_keep() +{ + VALUE ret; + int nparam; + ushort *pc; + int n, nb; + +#if DEBUG_STACK + fprintf(stderr, "| >> EXEC_leave\n"); + print_register(); +#endif + + // RP may be indirectly freed by OBJECT_UNREF() + VALUE_copy(&ret, RP); + + pc = STACK_get_previous_pc(); + nparam = FP->n_param; + + // ByRef arguments management + + nb = (pc && PCODE_is(pc[1], C_BYREF)) ? exec_leave_byref(pc, nparam) : 0; + if (nb == 0) + { + n = nparam + (SP - PP); + RELEASE_MANY(SP, n); + + OBJECT_UNREF(OP); + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); + } + + if (pc) + { + if (SP[-1].type == T_FUNCTION) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + COPY_VALUE(SP, &ret); + RP->type = T_VOID; + if (PCODE_is_variant(*PC) && SP->type != T_VOID) + VALUE_conv_variant(SP); + SP++; + } + else + { + VALUE_copy(RP, &ret); + } + + SP += nb; + +#if DEBUG_STACK + fprintf(stderr, "| << EXEC_leave()\n"); + print_register(); + fprintf(stderr, "\n"); +#endif + return; +} + +static void error_EXEC_function_real(void) +{ + RELEASE_MANY(SP, EXEC.nparam); + STACK_pop_frame(&EXEC_current); +} + +void EXEC_function_real() +{ + EXEC.func = &EXEC.class->load->func[EXEC.index]; + if (EXEC.func->fast && !JIT_exec(FALSE)) + return; + + // We need to push a void frame, because EXEC_leave looks at *PC to know if a return value is expected + STACK_push_frame(&EXEC_current, 0); + + PC = NULL; + GP = NULL; + + ON_ERROR(error_EXEC_function_real) + { + EXEC_enter(); + } + END_ERROR + + EXEC_function_loop(); +} + +void EXEC_function_loop() +{ + bool retry = FALSE; + + if (PC != NULL) + { + do + { + TRY + { + EXEC_loop(); + retry = FALSE; + } + CATCH + { + // QUIT was called + if (ERROR->info.code == E_ABORT) + { + #if DEBUG_ERROR + fprintf(stderr, "#0 QUIT\n"); + #endif + ERROR_lock(); + while (PC != NULL) + EXEC_leave_drop(); + ERROR_unlock(); + + //STACK_pop_frame(&EXEC_current); + PROPAGATE(); + } + + if (EXEC_break_on_error && EXEC_debug) + DEBUG.Main(TRUE); + + if (ERROR->info.code == E_ASSERT) + { + EP = NULL; + EC = NULL; + goto __IGNORE_TRY_CATCH; + } + + // Are we in a TRY? + if (EP && EC) + { + #if DEBUG_ERROR + fprintf(stderr, "#1 EP = %d SP = %d\n", EP - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); + fprintf(stderr, "TRY\n"); + #endif + ERROR_set_last(FALSE); + + // No need to unwind the Gosub stack until the TRY stack position, because TRY GOSUB is forbidden + /*while (GP > EP) + { + ... + }*/ + + // The stack is popped until reaching the stack position before the TRY + while (SP > EP) + POP(); + + // We go directly to the END TRY + PC = EC; + EP = NULL; + retry = TRUE; + goto __CONTINUE; + } + + // Is there a CATCH in the function? + if (EC != NULL) + { + #if DEBUG_ERROR + fprintf(stderr, "#2 EC = %p\n", EC); + fprintf(stderr, "CATCH\n"); + #endif + + ERROR_set_last(TRUE); + + // The stack is popped until reaching the stack position at the function start + while (SP > (BP + FP->n_local + FP->n_ctrl)) + POP(); + + // Reset the Gosub stack pointer as all Gosub control variables saved have been released + GP = NULL; + + PC = EC; + EC = NULL; + retry = TRUE; + goto __CONTINUE; + } + + __IGNORE_TRY_CATCH: + + // There is no error handler in the function + + #if DEBUG_ERROR + fprintf(stderr, "#3\n"); + fprintf(stderr, "NOTHING\n"); + #endif + //ERROR_INFO save = { 0 }; + //ERROR_save(&save); + + ERROR_set_last(TRUE); + + if (EXEC_debug && !FP->fast_linked && !STACK_has_error_handler()) + { + ERROR_hook(); + + for(;;) + DEBUG.Main(TRUE); + } + else + { + // We leave stack frames until we find either: + // - A stack frame that has an error handler. + // - A void stack frame created by EXEC_enter_real() + + // First we leave stack frames for JIT functions + // on top of the stack. We have just propagated + // past these some lines below. + + /*ERROR_lock(); + while (PC != NULL && EC == NULL && FP->fast) + EXEC_leave_drop(); + ERROR_unlock();*/ + + // We can only leave stack frames for non-JIT functions. + ERROR_lock(); + while (PC != NULL && EC == NULL && !FP->fast_linked) + EXEC_leave_drop(); + ERROR_unlock(); + + if (FP && FP->fast_linked) + PROPAGATE(); + + // If we got the void stack frame, then we remove it and raise the error again + if (PC == NULL) + { + STACK_pop_frame(&EXEC_current); + PROPAGATE(); + } + + + // We have a TRY too, handle it. + if (EP != NULL) + { + #if DEBUG_ERROR + fprintf(stderr, "#4 EP = %d SP = %d\n", EP - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); + #endif + + ERROR_lock(); + while (SP > EP) + POP(); + ERROR_unlock(); + + EP = NULL; + /* On va directement sur le END TRY */ + } + + // Now we can handle the CATCH + + // The stack is popped until reaching the stack position at the function start + ERROR_lock(); + #if DEBUG_ERROR + DEBUG_where(); + fprintf(stderr, "#5 BP + local + ctrl = %d SP = %d\n", BP + FP->n_local + FP->n_ctrl - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); + #endif + while (SP > (BP + FP->n_local + FP->n_ctrl)) + POP(); + ERROR_unlock(); + + PC = EC; + EC = NULL; + + retry = TRUE; + } + + //ERROR_restore(&save); + //ERROR_set_last(); + + __CONTINUE: + + while (SP < EXEC_super) + EXEC_super = ((VALUE *)EXEC_super)->_object.super; + } + END_TRY + + #if DEBUG_ERROR + if (retry) + fprintf(stderr, "RETRY %p\n", PC); + #endif + } + while (retry); + } + + if (PC == NULL) + STACK_pop_frame(&EXEC_current); +} + + +static ushort exec_native_can_quick(void) +{ + CLASS_DESC_METHOD *desc = EXEC.desc; + int i; + int nparam = EXEC.nparam; + + /* check number of arguments */ + + //fprintf(stderr, "exec_native_can_quick: %s.%s(%d) npmin:%d npmax:%d npvar:%d\n", desc->class->name, desc->name, nparam, desc->npmin, desc->npmax, desc->npvar); + + if (desc->npmin < desc->npmax || nparam != desc->npmin || desc->npvar) + return C_CALL_SLOW; + + /* check arguments type */ + + for (i = 0; i < nparam; i++) + { + if (SP[i - nparam].type != desc->signature[i]) + return C_CALL_SLOW; + } + + return C_CALL_QUICK; +} + +void EXEC_native_check(bool defined) +{ + ushort mode = defined ? exec_native_can_quick() : C_CALL_SLOW; + + *PC = (*PC & 0xFF) | mode; + + switch(mode) + { + case C_CALL_QUICK: EXEC_native_quick(); break; + //case C_CALL_EASY: EXEC_native_easy(); break; + default: EXEC_native(); break; + } +} + +#define EXEC_call_native_inline(_exec, _object, _type, _param) \ +({ \ + EXEC_set_native_error(FALSE); \ + (*(_exec))((_object), (void *)(_param)); \ + \ + if (EXEC_has_native_error()) \ + { \ + EXEC_set_native_error(FALSE); \ + error = TRUE; \ + } \ + else \ + { \ + TYPE __type = (_type); \ + if (TYPE_is_pure_object(__type) && TEMP.type != T_NULL && TEMP.type != T_VOID) \ + { \ + if (TEMP.type == T_CLASS) \ + TEMP._class.class = (CLASS *)__type; \ + else \ + TEMP.type = __type; \ + \ + error = FALSE; \ + } \ + else \ + error = FALSE; \ + } \ +}) + +bool EXEC_call_native(void (*exec)(), void *object, TYPE type, VALUE *param) +{ + bool error; + + EXEC_call_native_inline(exec, object, type, param); + return error; +} + +void EXEC_native_quick(void) +{ + CLASS_DESC_METHOD *desc = EXEC.desc; + int nparam = EXEC.nparam; + + bool error; + //void *free_later; + VALUE ret; + + EXEC_call_native_inline(desc->exec, EXEC.object, desc->type, &SP[-nparam]); + COPY_VALUE(&ret, &TEMP); + + if (error) + { + RELEASE_MANY(SP, nparam); + POP(); + PROPAGATE(); + } + + if (desc->type == T_VOID) + { + RELEASE_MANY(SP, nparam); + + SP--; + OBJECT_UNREF(SP->_function.object); + + SP->type = T_VOID; + SP->_void.ptype = T_NULL; + SP++; + } + else + { + BORROW(&ret); + RELEASE_MANY(SP, nparam); + + SP--; + OBJECT_UNREF(SP->_function.object); + COPY_VALUE(SP, &ret); + SP++; + } + + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_native: %s (%p)\n", desc->name, &desc); + #endif +} + + +static void error_EXEC_native(intptr_t nparam) +{ + RELEASE_MANY(SP, (int)nparam); +} + +void EXEC_native(void) +{ + CLASS_DESC_METHOD *desc = EXEC.desc; + int nparam = EXEC.nparam; + void *object = EXEC.object; + bool use_stack = EXEC.use_stack; + + int i, n, nm; + VALUE *value; + TYPE *sign; + bool error; + VALUE ret; + + #if DEBUG_STACK + fprintf(stderr, "| >> EXEC_native: %s.%s (%p)\n", EXEC.class->name, desc->name, &desc); + #endif + + ON_ERROR_1(error_EXEC_native, nparam) + { + n = desc->npmin; + nm = desc->npmax; + + if (nparam < n) + THROW(E_NEPARAM); + + if (!desc->npvar) + { + if (nparam > nm) + THROW(E_TMPARAM); + + value = &SP[-nparam]; + sign = desc->signature; + + for (i = 0; i < n; i++, value++, sign++) + VALUE_conv(value, *sign); + + if (n < nm) + { + for (; i < nparam; i++, value++, sign++) + { + if (value->type != T_VOID) + VALUE_conv(value, *sign); + } + + n = nm - nparam; + + /*if (STACK_check(n)) + { + STACK_RELOCATE(value); + }*/ + + SP += n; + nparam = nm; + + for (; i < nparam; i++, value++) + value->type = T_VOID; + } + } + else + { + value = &SP[-nparam]; + sign = desc->signature; + + for (i = 0; i < n; i++, value++, sign++) + VALUE_conv(value, *sign); + + nm = desc->npmax; + if (n < nm) + { + if (nparam < nm) + { + for (; i < nparam; i++, value++, sign++) + { + if (value->type != T_VOID) + VALUE_conv(value, *sign); + } + + n = nm - nparam; + + /*if (STACK_check(n)) + { + STACK_RELOCATE(value); + }*/ + + SP += n; + nparam = nm; + + for (; i < nparam; i++, value++) + value->type = T_VOID; + } + else + { + n = desc->npmax; + for (; i < n; i++, value++, sign++) + { + if (value->type != T_VOID) + VALUE_conv(value, *sign); + } + } + } + + EXEC_unknown_nparam = nparam <= nm ? 0 : nparam - nm; + + for (; i < nparam; i++, value++) + VARIANT_undo(value); + + //printf("EXEC_native: nparvar = %d\n", EXEC.nparvar); + } + } + END_ERROR + + EXEC_call_native_inline(desc->exec, object, desc->type, &SP[-nparam]); + COPY_VALUE(&ret, &TEMP); + + if (error) + { + RELEASE_MANY(SP, nparam); + + if (use_stack) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + PROPAGATE(); + } + + // If the function description is on the stack + + if (desc->type == T_VOID) + { + RELEASE_MANY(SP, nparam); + + if (use_stack) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + SP->type = T_VOID; + SP->_void.ptype = T_NULL; + SP++; + //UNBORROW(&ret); + } + else + { + BORROW(&ret); + RELEASE_MANY(SP, nparam); + + if (use_stack) + { + SP--; + OBJECT_UNREF(SP->_function.object); + + if (PCODE_is_variant(*PC) && ret.type != T_VOID) + VALUE_conv_variant(&ret); + } + + COPY_VALUE(SP, &ret); + SP++; + } + + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_native: %s (%p)\n", desc->name, &desc); + #endif +} + + +CLASS *EXEC_object_real(VALUE *val) +{ + CLASS *class; + OBJECT *object; + + object = val->_object.object; + class = val->_object.class; + + if (!object) + { + /* A null object and a virtual class means that we want to pass a static class */ + if (!class->is_virtual) + THROW_NULL(); + CLASS_load(class); + goto __RETURN; + } + + if (val == EXEC_super) + EXEC_super = val->_object.super; + else if (!class->is_virtual) + class = object->class; + + //CLASS_load(class); If we have an object, the class is necessarily loaded. + + if (class->must_check && (*(class->check))(object)) + THROW(E_IOBJECT); + +__RETURN: + + return class; +} + +CLASS *EXEC_object_variant(VALUE *val, OBJECT **pobject) +{ + CLASS *class; + OBJECT *object; + + if (TYPE_is_pure_object(val->_variant.vtype)) + { + object = val->_variant.value._object; + if (!object) + goto __NULL; + class = (CLASS *)val->_variant.vtype; + if (!class->is_virtual) + class = object->class; /* Virtual dispatching */ + goto __CHECK; + } + else if (val->_variant.vtype == T_OBJECT) + { + object = val->_variant.value._object; + if (!object) + goto __NULL; + class = object->class; + goto __CHECK; + } + else if (val->_variant.vtype == T_STRING || val->_variant.vtype == T_CSTRING) + { + *pobject = NULL; + return CLASS_BoxedString; + } + else + goto __ERROR; + +__ERROR: + + THROW(E_NOBJECT); + +__NULL: + + THROW_NULL(); + +__CHECK: + + //CLASS_load(class); //If we have an object, the class is not necessarily loaded? + + if (class->must_check && (*(class->check))(object)) + THROW(E_IOBJECT); + + *pobject = object; + + return class; +} + +bool EXEC_object_other(VALUE *val, CLASS **pclass, OBJECT **pobject) +{ + static const void *jump[] = { + &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, + &&__STRING, &&__CSTRING, &&__ERROR, &&__ERROR, &&__FUNCTION, &&__CLASS, &&__NULL, + &&__OBJECT, + }; + + CLASS *class; + OBJECT *object; + bool defined; + + goto *jump[val->type]; + +__FUNCTION: + + if (val->_function.kind == FUNCTION_UNKNOWN) + { + EXEC_unknown_property = TRUE; + EXEC_unknown_name = CP->load->unknown[val->_function.index]; + + EXEC_special(SPEC_UNKNOWN, val->_function.class, val->_function.object, 0, FALSE); + + object = val->_function.object; + OBJECT_UNREF(object); + + SP--; + //*val = *SP; + COPY_VALUE(val, SP); + EXEC_object(val, pclass, pobject); + return FALSE; // Could be TRUE, but always returning FALSE allows optimizations in quick array management + } + else + goto __ERROR; + +__CLASS: + + class = val->_class.class; + object = NULL; + defined = TRUE; + + if (val == EXEC_super) + { + EXEC_super = val->_class.super; + //*class = (*class)->parent; + if (class == NULL) + THROW(E_PARENT); + } + + CLASS_load(class); + goto __RETURN; + +__OBJECT: + + object = val->_object.object; + if (!object) + goto __NULL; + class = object->class; + defined = FALSE; + + goto __CHECK; + +__STRING: +__CSTRING: + + *pclass = CLASS_BoxedString; + *pobject = NULL; + return TRUE; + +__ERROR: + + THROW(E_NOBJECT); + +__NULL: + + THROW_NULL(); + +__CHECK: + + //CLASS_load(class); If we have an object, the class is necessarily loaded. + + if (class->must_check && (*(class->check))(object)) + THROW(E_IOBJECT); + +__RETURN: + + *pclass = class; + *pobject = object; + + return defined; +} + + +void EXEC_public_desc(CLASS *class, void *object, CLASS_DESC_METHOD *desc, int nparam) +{ + EXEC.object = object; + EXEC.nparam = nparam; /*desc->npmin;*/ + + if (FUNCTION_is_native(desc)) + { + EXEC.class = class; // EXEC_native() does not need the real class, except the GB.GetClass(NULL) API used by Form.Main. + EXEC.native = TRUE; + EXEC.use_stack = FALSE; + EXEC.desc = desc; + EXEC_native(); + SP--; + *RP = *SP; + SP->type = T_VOID; + } + else + { + EXEC.class = desc->class; // EXEC_function_real() needs the effective class, because the method can be an inherited one! + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->exec; + EXEC_function_keep(); + } +} + + +void EXEC_public(CLASS *class, void *object, const char *name, int nparam) +{ + CLASS_DESC *desc; + + desc = CLASS_get_symbol_desc_kind(class, name, (object != NULL) ? CD_METHOD : CD_STATIC_METHOD, 0, T_VOID); + + if (desc == NULL) + return; + + EXEC_public_desc(class, object, &desc->method, nparam); + EXEC_release_return_value(); +} + + +bool EXEC_special(int special, CLASS *class, void *object, int nparam, bool drop) +{ + CLASS_DESC *desc; + short index = class->special[special]; + + if (index == NO_SYMBOL) + return TRUE; + + desc = CLASS_get_desc(class, index); + + if (CLASS_DESC_get_type(desc) == CD_STATIC_METHOD) + { + if (object != NULL) + return TRUE; + } + else + { + if (object == NULL) + { + if (class->auto_create) + object = EXEC_auto_create(class, FALSE); + + if (object == NULL) + THROW(E_NOBJECT); + } + } + + EXEC.class = desc->method.class; + EXEC.object = object; + EXEC.nparam = nparam; + + /*printf("<< EXEC_spec: SP = %d\n", SP - (VALUE *)STACK_base); + save_SP = SP;*/ + + if (FUNCTION_is_native(&desc->method)) + { + if (desc->method.subr) + { + ((EXEC_FUNC_CODE_SP)(EXEC.class->table[index].desc->method.exec))(nparam, SP); + } + else + { + EXEC.desc = &desc->method; + EXEC.use_stack = FALSE; + EXEC.native = TRUE; + EXEC_native(); + } + + if (drop) + POP(); + } + else + { + //EXEC.func = &class->load->func[(long)desc->method.exec] + EXEC.index = (int)(intptr_t)desc->method.exec; + EXEC.native = FALSE; + if (drop) + EXEC_function(); + else + { + EXEC_function_keep(); + //*SP++ = *RP; + COPY_VALUE(SP, RP); + SP++; + RP->type = T_VOID; + } + } + + /*printf(">> EXEC_spec: SP = %d\n", SP - (VALUE *)STACK_base); + if (SP != save_SP) + printf("**** SP should be %d\n", save_SP - (VALUE *)STACK_base);*/ + + return FALSE; +} + + +/* + The highest parent method is called first, but get only the parameters + not consumed by the child methods. +*/ + +void EXEC_special_inheritance(int special, CLASS *class, OBJECT *object, int nparam, bool drop) +{ + CLASS *her[MAX_INHERITANCE]; + int nher; + int i, np, npopt, nparam_opt, save_nparam; + CLASS_DESC *desc; + short index; + int arg, opt; + VALUE *base; + + if (!class->parent) + { + if (special == SPEC_NEW && class->init_dynamic) + { + EXEC.class = class; + EXEC.object = object; + EXEC.index = FUNC_INIT_DYNAMIC; + EXEC.native = FALSE; + EXEC.nparam = 0; + EXEC_function(); + } + + if (EXEC_special(special, class, object, nparam, drop)) + { + if (nparam) + THROW(E_TMPARAM); + } + + return; + } + + nher = CLASS_get_inheritance(class, her); + + for(i = 0, np = 0; i < nher; i++) + { + class = her[i]; + + index = class->special[special]; + if (index == NO_SYMBOL) + continue; + desc = CLASS_get_desc(class, index); //class->special[special]; + np += desc->method.npmin; + } + + if (np > nparam) + THROW(E_NEPARAM); + + save_nparam = nparam; + arg = - nparam; + opt = arg + np; + nparam_opt = nparam - np; + nparam = np; + + // nparam is now the number of mandatory arguments + // naram_opt is the number of optional arguments + + for(;;) + { + nher--; + if (nher < 0) + break; + class = her[nher]; + + if (special == SPEC_NEW) + { + if (class->init_dynamic) + { + EXEC.class = class; + EXEC.object = object; + EXEC.index = FUNC_INIT_DYNAMIC; + //EXEC.func = &class->load->func[FUNC_INIT_DYNAMIC]; + EXEC.native = FALSE; + EXEC.nparam = 0; + + EXEC_function(); + } + } + + index = class->special[special]; + if (index == NO_SYMBOL) + continue; + + desc = CLASS_get_desc(class, index); // class->special[special]; + + if (nher) + { + np = desc->method.npmin; + if (np > nparam) np = nparam; + nparam -= np; + + npopt = desc->method.npmax - desc->method.npmin; + if (npopt > nparam_opt) npopt = nparam_opt; + nparam_opt -= npopt; + } + else + { + np = nparam; + npopt = nparam_opt; + } + + if (np > 0 || npopt > 0) + { + STACK_check(np + npopt); + + base = SP; + + for (i = 0; i < np; i++) + { + *SP++ = base[arg]; + base[arg].type = T_NULL; + arg++; + } + + for (i = 0; i < npopt; i++) + { + *SP++ = base[opt]; + base[opt].type = T_NULL; + opt++; + } + } + + EXEC_special(special, class, object, np + npopt, drop); + } + + SP -= save_nparam; +} + +void *EXEC_create_object(CLASS *class, int np, char *event) +{ + void *object; + + CLASS_load(class); + + if (class->no_create) + THROW(E_CSTATIC, CLASS_get_name(class)); + + object = OBJECT_new(class, event, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); + + TRY + { + OBJECT_lock(object, TRUE); + EXEC_special_inheritance(SPEC_NEW, class, object, np, TRUE); + OBJECT_lock(object, FALSE); + EXEC_special(SPEC_READY, class, object, 0, TRUE); + } + CATCH + { + // _free() methods should not be called, but we must + OBJECT_UNREF(object); + PROPAGATE(); + } + END_TRY + + return object; +} + + +void EXEC_new(ushort code) +{ + CLASS *class; + int np; + bool event; + void *object; + char *name = NULL; + char *cname = NULL; + char *save; + + np = code & 0xFF; + event = np & CODE_NEW_EVENT; + np &= 0x3F; + + /* Instanciation */ + + SP -= np; + + if (SP->type == T_CLASS) + { + class = SP->_class.class; + //if (class->override != NULL) + // class = class->override; + } + else if (TYPE_is_string(SP->type)) + { + cname = STRING_copy_from_value_temp(SP); + class = CLASS_find(cname); + RELEASE_STRING(SP); + SP->type = T_NULL; + } + else + THROW_TYPE(T_STRING, SP->type); + + SP += np; + + //printf("**** NEW %s\n", class->name); + CLASS_load(class); + + if (class->no_create) + THROW(E_CSTATIC, CLASS_get_name(class)); + + if (event) + { + SP--; + + if (!TYPE_is_string(SP->type)) + THROW_TYPE(T_STRING, SP->type); + + name = STRING_copy_from_value_temp(SP); + //printf("**** name %s\n", class->name); + + STRING_ref(name); + SP++; + object = OBJECT_new(class, name, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); + SP--; + STRING_unref(&name); + + RELEASE_STRING(SP); + + np -= 2; + } + else + { + object = OBJECT_new(class, name, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); + np--; + } + + save = EVENT_enter_name(name); + + TRY + { + OBJECT_lock(object, TRUE); + EXEC_special_inheritance(SPEC_NEW, class, object, np, TRUE); + OBJECT_lock(object, FALSE); + EVENT_leave_name(save); + + SP--; /* class */ + EXEC_special(SPEC_READY, class, object, 0, TRUE); + + SP->_object.class = class; + SP->_object.object = object; + SP++; + } + CATCH + { + EVENT_leave_name(save); + // _free() methods should not be called, but we must + OBJECT_UNREF(object); + //(*class->free)(class, object); + SP--; /* class */ + SP->type = T_NULL; + SP++; + PROPAGATE(); + } + END_TRY +} + + +void EXEC_do_quit(void) +{ + GAMBAS_DoNotRaiseEvent = TRUE; + + JIT_abort(); + HOOK(quit)(); + + THROW(E_ABORT); +} + +void *EXEC_auto_create(CLASS *class, bool ref) +{ + void *object; + + object = CLASS_auto_create(class, 0); /* object is checked by CLASS_auto_create */ + + /*if (class->must_check && (*(class->check))(object)) + THROW(E_IOBJECT);*/ + + if (ref) + OBJECT_REF(object); + + return object; +} + +void EXEC_dup(int n) +{ + VALUE *src; + + STACK_check(n); + + src = SP - n; + while (n > 0) + { + BORROW(src); + *SP++ = *src++; + n--; + } +} + +void EXEC_push_complex(void) +{ + static void *(*func)(double) = NULL; + void *ob; + + SP--; + if (SP->type < T_INTEGER || SP->type > T_FLOAT) + THROW_ILLEGAL(); + SP++; + + if (!func) + { + if (COMPONENT_get_info("PUSH_COMPLEX", POINTER(&func))) + { + COMPONENT_load(COMPONENT_create("gb.complex")); + if (COMPONENT_get_info("PUSH_COMPLEX", POINTER(&func))) + THROW(E_MATH); + } + } + + SP--; + VALUE_conv_float(SP); + ob = (*func)(SP->_float.value); + SP->_object.object = ob; + SP->_object.class = OBJECT_class(ob); + OBJECT_REF(ob); + SP++; +} + +void EXEC_push_vargs(void) +{ + int i; + int nargs = (FP && FP->vararg) ? BP - PP : 0; + + if (nargs == 0) + return; + + STACK_check(nargs); + + for (i = 0; i < nargs; i++) + { + *SP = PP[i]; + PUSH(); + } + + PC[1] += nargs; +} + +void EXEC_end_vargs(void) +{ + int nargs = (FP && FP->vararg) ? BP - PP : 0; + PC[-1] -= nargs; +} + +void EXEC_drop_vargs(void) +{ + int nargs = (FP && FP->vararg) ? BP - PP : 0; + RELEASE_MANY(SP, nargs); + PC[-1] -= nargs; +} + +void EXEC_set_got_error(bool err) +{ + EXEC_got_error = err; +} diff --git a/main/gbx/gbx_exec.h b/main/gbx/gbx_exec.h new file mode 100644 index 00000000..0e25761e --- /dev/null +++ b/main/gbx/gbx_exec.h @@ -0,0 +1,442 @@ +/*************************************************************************** + + gbx_exec.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EXEC_H +#define __GBX_EXEC_H + +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_overflow.h" +#include "gbx_class.h" +#include "gbx_value.h" +#include "gb_pcode.h" +#include "gbx_stack.h" + +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" + +#include "gbx_c_enum.h" + +enum { GB_LITTLE_ENDIAN, GB_BIG_ENDIAN }; + +typedef + void (*EXEC_FUNC)(); + +typedef + void (*EXEC_FUNC_CODE)(ushort); + +typedef + void (*EXEC_FUNC_CODE_SP)(ushort, VALUE *); + +typedef + struct { + unsigned debug : 1; // running in debugging mode + unsigned got_error : 1; // if a native function has returned an error + unsigned debug_inside : 1; // debug inside components + unsigned debug_hold : 1; // hold execution at program end + unsigned task : 1; // I am a background task + unsigned profile : 1; // profiling mode + unsigned profile_instr : 1; // profiling mode at instruction level + unsigned trace : 1; // tracing mode + unsigned arch : 1; // executing an archive + unsigned fifo : 1; // debugging through a fifo + unsigned keep_library : 1; // do not unload libraries + unsigned main_hook_done : 1; // the main hook has been run + unsigned break_on_error : 1; // if we must break into the debugger as soon as there is an error. + unsigned in_event_loop : 1; // if we are in the event loop + unsigned check_overflow : 1; // if we should check for overflow + unsigned big_endian : 1; // if the CPU is big endian + } + EXEC_FLAG; + +typedef + struct { + CLASS *class; + OBJECT *object; + CLASS_DESC_METHOD *desc; + FUNCTION *func; + int index; + char nparam; + bool native; + bool use_stack; + } + EXEC_GLOBAL; + +typedef + struct { + void (*main)(); + void (*loop)(); + void (*wait)(); + void (*timer)(); + void (*lang)(); + void (*watch)(); + void (*post)(); + void (*quit)(); + bool (*error)(); + double (*timeout)(); + } + EXEC_HOOK; + +enum { + OP_NOTHING = 0, + OP_OBJECT_FLOAT, + OP_FLOAT_OBJECT, + OP_OBJECT_OTHER, + OP_OTHER_OBJECT, + OP_OBJECT_OBJECT + }; + + +#ifndef __GBX_EXEC_C + +extern STACK_CONTEXT EXEC_current; +extern VALUE *SP; +extern VALUE TEMP; +extern VALUE RET; + +extern VALUE *EXEC_super; + +/* +extern bool EXEC_debug; +extern bool EXEC_got_error; +extern bool EXEC_debug_inside; +extern bool EXEC_debug_hold; +extern bool EXEC_task; +extern bool EXEC_profile; +extern bool EXEC_trace; +extern bool EXEC_profile_instr; +extern bool EXEC_arch; +extern bool EXEC_fifo; +extern bool EXEC_keep_library; +extern bool EXEC_break_on_error; +extern bool EXEC_in_event_loop; +#if DO_NOT_CHECK_OVERFLOW +#else +extern bool EXEC_check_overflow; +#endif +extern bool EXEC_big_endian; +extern bool EXEC_main_hook_done; +*/ + +extern const char *EXEC_fifo_name; +extern const char *EXEC_profile_path; + +extern EXEC_HOOK EXEC_Hook; + +extern CENUM *EXEC_enum; + +extern const char EXEC_should_borrow[]; + +extern const char *EXEC_unknown_name; +extern char EXEC_unknown_nparam; +extern uchar EXEC_quit_value; + +extern EXEC_GLOBAL EXEC; +extern EXEC_FLAG FLAG; + +extern const void *EXEC_subr_table[]; + +#endif + +#define EXEC_debug FLAG.debug +#define EXEC_got_error FLAG.got_error +#define EXEC_debug_inside FLAG.debug_inside +#define EXEC_profile FLAG.profile +#define EXEC_fifo FLAG.fifo +#define EXEC_arch FLAG.arch +#define EXEC_profile_instr FLAG.profile_instr +#define EXEC_trace FLAG.trace +#define EXEC_big_endian FLAG.big_endian +#define EXEC_break_on_error FLAG.break_on_error +#define EXEC_debug_hold FLAG.debug_hold +#define EXEC_task FLAG.task +#define EXEC_keep_library FLAG.keep_library +#define EXEC_main_hook_done FLAG.main_hook_done +#define EXEC_check_overflow FLAG.check_overflow + +// Local variables base pointer +#define BP EXEC_current.bp +// Arguments base pointer +#define PP EXEC_current.pp +// Current class +#define CP EXEC_current.cp +// Current object +#define OP EXEC_current.op +// Save stack pointer for a TRY +#define EP EXEC_current.ep +// Current function +#define FP EXEC_current.fp +// Program counter +#define PC EXEC_current.pc +// Where to go if there is an error +#define EC EXEC_current.ec +// Save register for TRY +#define ET EXEC_current.et +// GoSub stack +#define GP EXEC_current.gp + +// Function return value pointer +#define RP (&RET) + +#define HOOK(func) (!EXEC_Hook.func) ? 0 : (*EXEC_Hook.func) +#define HOOK_DEFAULT(func, def) (*((!EXEC_Hook.func) ? def : EXEC_Hook.func)) + +#define GET_NPARAM(var) short var = code & 0x3F +#define GET_PARAM(var, nparam) VALUE *var = &SP[-nparam] + +void EXEC_init(void); + +void EXEC_enter_check(bool defined); +void EXEC_enter(void); +void EXEC_enter_quick(void); +void EXEC_leave_keep(); +void EXEC_leave_drop(); +void EXEC_loop(void); +void EXEC_init_bytecode_check(void); +void EXEC_check_bytecode(void); + +#define EXEC_object_2(_val, _pclass, _pobject) \ +({ \ + VALUE *v = (_val); \ + \ + *(_pobject) = v->_object.object; \ + \ + if (v->_object.class->is_simple) \ + { \ + if (!*(_pobject)) \ + THROW_NULL(); \ + \ + *(_pclass) = OBJECT_class(v->_object.object); \ + } \ + else \ + *(_pclass) = EXEC_object_real(_val); \ +}) + +#define EXEC_object(_val, _pclass, _pobject) \ + ((TYPE_is_pure_object((_val)->type)) ? EXEC_object_2(_val, _pclass, _pobject), TRUE : \ + TYPE_is_variant((_val)->type) ? (*(_pclass) = EXEC_object_variant(_val, _pobject)), FALSE : \ + EXEC_object_other(_val, _pclass, _pobject)) + +#define EXEC_object_fast(_val, _pclass, _pobject) \ +({ \ + if ((_val)->type != T_CLASS) \ + EXEC_object_real(_val, _pclass, _pobject); \ + else \ + EXEC_object_other(_val, _pclass, _pobject); \ +}) + +#define EXEC_object_array(_val, _vclass, _vobject) \ + _vobject = (_val)->_object.object; \ + if (!(_vobject)) \ + THROW_NULL(); \ + _vclass = (_vobject)->class; \ + if ((_val) == EXEC_super) \ + EXEC_super = (_val)->_object.super; + +CLASS *EXEC_object_real(VALUE *val); +CLASS *EXEC_object_variant(VALUE *val, OBJECT **pobject); +bool EXEC_object_other(VALUE *val, CLASS **pclass, OBJECT **pobject); + +void *EXEC_auto_create(CLASS *class, bool ref); + +bool EXEC_call_native(void (*exec)(), void *object, TYPE type, VALUE *param); +void EXEC_native_check(bool defined); +void EXEC_native_quick(void); +void EXEC_native(void); +void EXEC_function_real(void); +void EXEC_function_loop(void); + +#define EXEC_function() EXEC_function_real(), EXEC_release_return_value() +#define EXEC_function_keep() EXEC_function_real() + +void EXEC_public(CLASS *class, void *object, const char *name, int nparam); +void EXEC_public_desc(CLASS *class, void *object, CLASS_DESC_METHOD *desc, int nparam); + +bool EXEC_special(int special, CLASS *class, void *object, int nparam, bool drop); + +void EXEC_special_inheritance(int special, CLASS *class, OBJECT *object, int nparam, bool drop); + +void EXEC_nop(void); + +void EXEC_push_unknown(void); +int EXEC_push_unknown_event(bool unknown); +//void EXEC_push_special(void); + +void EXEC_pop_unknown(void); + +void EXEC_enum_first(PCODE code, VALUE *local, VALUE *penum); +bool EXEC_enum_next(PCODE code, VALUE *local, VALUE *penum); + +void *EXEC_create_object(CLASS *class, int np, char *event); +void EXEC_new(ushort code); + +void EXEC_release_return_value(void); +void EXEC_do_quit(void); + +void EXEC_dup(int n); + +void EXEC_borrow(TYPE type, VALUE *val); +void EXEC_unborrow(VALUE *val); +void EXEC_release(TYPE type, VALUE *val); +void RELEASE_many(VALUE *val, int n); + +void EXEC_push_complex(void); + +void EXEC_push_vargs(void); +void EXEC_end_vargs(void); +void EXEC_drop_vargs(void); + +#define BORROW(_value) \ +do { \ + VALUE *_v = (_value); \ + TYPE type = _v->type; \ + if (TYPE_is_object(type)) \ + { \ + OBJECT_REF_CHECK(_v->_object.object); \ + } \ + else if (EXEC_should_borrow[type]) \ + { \ + if (type == T_STRING) \ + STRING_ref(_v->_string.addr); \ + else \ + EXEC_borrow(type, _v); \ + } \ +} while (0) + +#define RELEASE(_value) \ +do { \ + VALUE *_v = (_value); \ + TYPE type = _v->type; \ + if (TYPE_is_object(type)) \ + { \ + OBJECT_UNREF(_v->_object.object); \ + } \ + else if (EXEC_should_borrow[type]) \ + { \ + if (type == T_STRING) \ + STRING_unref(&_v->_string.addr); \ + else \ + EXEC_release(type, _v); \ + } \ +} while (0) + +#if DEBUG_REF + +#define UNBORROW(_value) \ +do { \ + fprintf(stderr, "@%s\n", OBJECT_where_am_i(__FILE__, __LINE__, __func__)); \ + EXEC_unborrow(_value); \ +} while (0) + +#else + +#define UNBORROW EXEC_unborrow + +#endif + +#define RELEASE_STRING(_value) \ +do { \ + if ((_value)->type == T_STRING) STRING_unref(&(_value)->_string.addr); \ +} while(0) + +#define RELEASE_OBJECT(_value) \ +do { \ + VALUE *_v = (_value); \ + OBJECT_UNREF(_v->_object.object); \ +} while (0) + +#define RELEASE_VARIANT(_value) \ +do { \ + VALUE *_v = (_value); \ + if (_v->_variant.vtype == T_STRING) \ + STRING_unref(&_v->_variant.value._string); \ + else if (TYPE_is_object(_v->_variant.vtype)) \ + OBJECT_UNREF(_v->_variant.value._object); \ +} while (0) + +#define RELEASE_MANY(_val, _n) \ +do { \ + if (_n) \ + { \ + if ((_n) == 1) \ + { \ + _val--; \ + RELEASE((_val)); \ + } \ + else \ + { \ + RELEASE_many((_val), (_n)); \ + _val -= (_n); \ + } \ + } \ +} while (0) + +#define PUSH() \ +do { \ + BORROW(SP); \ + SP++; \ +} while (0) + +#define POP() \ +do { \ + SP--; \ + RELEASE(SP); \ +} while (0) + +#define PUSH_OBJECT(__class, __object) \ +({ \ + SP->_object.class = (__class); \ + SP->_object.object = OBJECT_REF(__object); \ + SP++; \ +}) + +#define COPY_VALUE(_dst, _src) VALUE_copy(_dst, _src) + +#define EXEC_set_native_error(_err) (ERROR_current->info.native = (_err)) +#define EXEC_has_native_error() (ERROR_current->info.native) + +#define EXEC_move_ret_to_temp() \ +({ \ + TEMP = *RP; \ + UNBORROW(RP); \ + RP->type = T_VOID; \ +}) + +bool EXEC_check_operator_single(VALUE *P1, uchar op); +int EXEC_check_operator(VALUE *P1, VALUE *P2, uchar op); +void EXEC_operator(uchar what, uchar op, VALUE *P1, VALUE *P2); +void EXEC_operator_object_add_quick(VALUE *P1, double val); +int EXEC_comparator(uchar what, uchar op, VALUE *P1, VALUE *P2); +void EXEC_operator_object_sgn(VALUE *P1); +void EXEC_operator_object_fabs(VALUE *P1); +void EXEC_operator_object_single(uchar op, VALUE *P1); + +void EXEC_quit(ushort code); + +void EXEC_push_array(ushort code); +void EXEC_pop_array(ushort code); + +void EXEC_set_got_error(bool err); + +#endif /* */ diff --git a/main/gbx/gbx_exec_enum.c b/main/gbx/gbx_exec_enum.c new file mode 100644 index 00000000..791ee0c8 --- /dev/null +++ b/main/gbx/gbx_exec_enum.c @@ -0,0 +1,97 @@ +/*************************************************************************** + + gbx_exec_enum.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_limit.h" +#include "gbx_c_enum.h" +#include "gbx_exec.h" + + +/* EXEC_object() ne doit pas faire d'auto-create, car sinon + il renvoie un objet référencé */ + +void EXEC_enum_first(PCODE code, VALUE *local, VALUE *penum) +{ + OBJECT *object; + CLASS *class; + CENUM *old = EXEC_enum; + CENUM *cenum; + + EXEC_object(local, &class, &object); + + if (!object && class->auto_create && !class->enum_static) + object = EXEC_auto_create(class, FALSE); + + cenum = CENUM_create(object ? (void *)object : (void *)class); + + RELEASE(penum); + penum->_object.class = OBJECT_class(cenum); + penum->_object.object = cenum; + OBJECT_REF(cenum); + + EXEC_enum = cenum; + EXEC_special(SPEC_FIRST, class, object, 0, TRUE); + EXEC_enum = old; +} + + +bool EXEC_enum_next(PCODE code, VALUE *local, VALUE *penum) +{ + OBJECT *object; + CLASS *class; + bool defined; + bool drop = (code & 1); + bool err; + CENUM *old = EXEC_enum; + CENUM *cenum; + + defined = EXEC_object(local, &class, &object); + cenum = (CENUM *)penum->_object.object; + if (!cenum) + return TRUE; + + if (cenum->stop) + goto __STOP; + + EXEC_enum = cenum; + err = EXEC_special(SPEC_NEXT, class, object, 0, FALSE); + EXEC_enum = old; + + if (err) + THROW(E_ENUM); + + if (!defined && !drop && !cenum->stop) + VALUE_conv_variant(&SP[-1]); + + if (drop || cenum->stop) + POP(); + + if (!cenum->stop) + return FALSE; + +__STOP: + + OBJECT_UNREF(cenum); + penum->_object.object = NULL; + return TRUE; +} diff --git a/main/gbx/gbx_exec_loop.c b/main/gbx/gbx_exec_loop.c new file mode 100644 index 00000000..38117cdd --- /dev/null +++ b/main/gbx/gbx_exec_loop.c @@ -0,0 +1,4781 @@ +/*************************************************************************** + + gbx_exec_loop.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXEC_LOOP_C + +#include "gb_common.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gb_overflow.h" + +#include "gbx_type.h" +#include "gbx_debug.h" + +#include "gbx_subr.h" +#include "gb_pcode.h" +#include "gbx_stack.h" +#include "gbx_event.h" +#include "gbx_value.h" +#include "gbx_local.h" +#include "gbx_string.h" +#include "gbx_api.h" +#include "gbx_archive.h" +#include "gbx_extern.h" +#include "gbx_exec.h" +#include "gbx_subr.h" +#include "gbx_math.h" +#include "gbx_c_array.h" +#include "gbx_c_string.h" +#include "gbx_struct.h" +#include "gbx_variant.h" +#include "gbx_jit.h" + +//#define DEBUG_PCODE 1 + +#if DEBUG_PCODE +#define PROJECT_EXEC +#include "gb_pcode_temp.h" +#endif + +#define SUBR_beep EXEC_ILLEGAL +//#define _SUBR_POKE _NEXT + +#define GET_XXX() (((signed short)(code << 4)) >> 4) +#define GET_UXX() (code & 0xFFF) +#define GET_7XX() (code & 0x7FF) +#define GET_XX() ((signed char)code) +#define GET_UX() ((unsigned char)code) +#define GET_3X() (code & 0x3F) +#define GET_0X() (code & 0xF) +#define TEST_XX() (code & 1) + +//static void _SUBR_comp(ushort code); +NOINLINE static void _SUBR_compe(ushort code); +//static void _SUBR_compi(ushort code); +NOINLINE static void _SUBR_add(ushort code); +NOINLINE static void _SUBR_sub(ushort code); +NOINLINE static void _SUBR_mul(ushort code); +NOINLINE static void _SUBR_div(ushort code); + +#define my_VALUE_class_read VALUE_class_read_inline + +NOINLINE static void SUBR_left(ushort code); +NOINLINE static void SUBR_right(ushort code); +NOINLINE static void SUBR_mid(ushort code); +NOINLINE static void SUBR_len(void); + +//---- Subroutine dispatch table -------------------------------------------- + +const void *EXEC_subr_table[] = +{ + /* 00 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 10 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 20 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* 28 */ _SUBR_compe, _SUBR_compe, NULL, NULL, + /* 2C */ NULL, NULL, SUBR_near, SUBR_case, + /* 30 */ _SUBR_add, _SUBR_sub, _SUBR_mul, _SUBR_div, + /* 34 */ SUBR_neg, SUBR_quo, SUBR_rem, SUBR_pow, + /* 38 */ SUBR_and_, SUBR_and_, SUBR_and_, SUBR_not, + /* 3C */ SUBR_cat, SUBR_like, SUBR_file, SUBR_is, + + SUBR_left, /* 00 40 */ + SUBR_mid, /* 01 41 */ + SUBR_right, /* 02 42 */ + NULL, /* 03 43 */ + SUBR_space, /* 04 44 */ + SUBR_string, /* 05 45 */ + SUBR_trim, /* 06 46 */ + SUBR_upper, /* 07 47 */ + SUBR_hex_bin, /* 08 48 */ + SUBR_chr, /* 09 49 */ + SUBR_asc, /* 10 4A */ + SUBR_instr, /* 11 4B */ + SUBR_instr, /* 12 4C */ + SUBR_subst, /* 13 4D */ + SUBR_replace, /* 14 4E */ + SUBR_split, /* 15 4F */ + SUBR_scan, /* 16 50 */ + SUBR_strcomp, /* 17 51 */ + SUBR_iconv, /* 18 52 */ + SUBR_sconv, /* 19 53 */ + SUBR_abs, /* 20 54 */ + SUBR_int, /* 21 55 */ + SUBR_fix, /* 22 56 */ + SUBR_sgn, /* 23 57 */ + SUBR_math, /* 24 58 */ + SUBR_pi, /* 25 59 */ + SUBR_round, /* 26 5A */ + SUBR_randomize, /* 27 5B */ + SUBR_rnd, /* 28 5C */ + SUBR_min_max, /* 29 5D */ + SUBR_min_max, /* 30 5E */ + SUBR_if, /* 31 5F */ + SUBR_choose, /* 32 60 */ + SUBR_array, /* 33 61 */ + SUBR_math2, /* 34 62 */ + SUBR_is_chr, /* 35 63 */ + SUBR_bit, /* 36 64 */ + SUBR_is_type, /* 37 65 */ + SUBR_type, /* 38 66 */ + NULL, /* 39 67 */ + SUBR_hex_bin, /* 40 68 */ + SUBR_hex_bin, /* 41 69 */ + SUBR_val, /* 42 6A */ + SUBR_str, /* 43 6B */ + SUBR_format, /* 44 6C */ + SUBR_timer, /* 45 6D */ + SUBR_now, /* 46 6E */ + SUBR_year, /* 47 6F */ + SUBR_week, /* 48 70 */ + SUBR_date, /* 49 71 */ + SUBR_time, /* 50 72 */ + SUBR_date_op, /* 51 73 */ + SUBR_eval, /* 52 74 */ + SUBR_error, /* 53 75 */ + SUBR_debug, /* 54 76 */ + SUBR_wait, /* 55 77 */ + SUBR_open, /* 56 78 */ + SUBR_close, /* 57 79 */ + SUBR_input, /* 58 7A */ + SUBR_linput, /* 59 7B */ + SUBR_print, /* 60 7C */ + SUBR_read, /* 61 7D */ + SUBR_write, /* 62 7E */ + SUBR_flush, /* 63 7F */ + SUBR_lock, /* 64 80 */ + SUBR_inp_out, /* 65 81 */ + SUBR_eof, /* 66 82 */ + SUBR_lof, /* 67 83 */ + SUBR_seek, /* 68 84 */ + SUBR_kill, /* 69 85 */ + SUBR_mkdir, /* 70 86 */ // deprecated -> Even() & Odd() + SUBR_rmdir, /* 71 87 */ // deprecated + SUBR_move, /* 72 88 */ + SUBR_swap, /* 73 89 */ // support for Copy() + SUBR_link, /* 74 8A */ // deprecated -> IsNan() & IsInf() + SUBR_exist, /* 75 8B */ + SUBR_access, /* 76 8C */ + SUBR_stat, /* 77 8D */ + SUBR_dfree, /* 78 8E */ + SUBR_temp, /* 79 8F */ + SUBR_isdir, /* 80 90 */ + SUBR_dir, /* 81 91 */ + SUBR_rdir, /* 82 92 */ + SUBR_exec, /* 83 93 */ + SUBR_alloc, /* 84 94 */ + SUBR_free, /* 85 95 */ + SUBR_realloc, /* 86 96 */ + SUBR_strptr, /* 87 97 */ + SUBR_sleep, /* 88 98 */ + SUBR_varptr, /* 89 99 */ + SUBR_collection, /* 90 9A */ + SUBR_tr, /* 91 9B */ + SUBR_quote, /* 92 9C */ + SUBR_unquote, /* 93 9D */ + SUBR_make, /* 94 9E */ + SUBR_peek, /* 95 9F */ +}; + +//---- Main interpreter loop ------------------------------------------------ + +/*static void my_VALUE_class_read(CLASS *class, VALUE *value, char *addr, CTYPE ctype, void *ref) +{ + VALUE_class_read_inline(class, value, addr, ctype, ref); +}*/ + +static const void **_sb_jump_table; +static const void **_sb_jump_table_3_18_AXXX; +static const void **_sb_jump_table_3_18_FXXX; +static bool _sb_not_3_18 = FALSE; + +void EXEC_init_bytecode_check() +{ + ushort opcode = C_RETURN + 3; + PC = &opcode; + EXEC_loop(); +} + +void EXEC_check_bytecode() +{ + int i; + + if (!CP) + return; + + //fprintf(stderr, "EXEC_check_bytecode: %s / %d\n", CP->name, CP->not_3_18); + + if (CP->not_3_18 == _sb_not_3_18) + return; + + _sb_not_3_18 = !_sb_not_3_18; + //fprintf(stderr, "switch bytecode to %s\n", _sb_not_3_18 ? "< 3.18" : "3.18"); + + if (_sb_not_3_18) + { + for (i = 0xA1; i <= 0xAE; i++) + _sb_jump_table[i] = _sb_jump_table[0xA0]; + for (i = 0xF1; i <= 0xFE; i++) + _sb_jump_table[i] = _sb_jump_table[0xF0]; + } + else + { + for(i = 1; i <= 14; i++) + { + _sb_jump_table[0xA0 + i] = _sb_jump_table_3_18_AXXX[i]; + _sb_jump_table[0xF0 + i] = _sb_jump_table_3_18_FXXX[i]; + } + } +} + +INLINE static void _pop_ctrl(int ind) +{ + VALUE *val = &BP[ind]; + RELEASE(val); + SP--; + *val = *SP; +} + +NOINLINE static void _push_event(int ind) +{ + if (ind >= 0xFE) + ind = EXEC_push_unknown_event(ind & 1); + else if (CP->parent) + ind += CP->parent->n_event; + + SP->type = T_FUNCTION; + SP->_function.kind = FUNCTION_EVENT; + SP->_function.index = ind; + SP->_function.defined = FALSE; + SP->_function.class = NULL; + SP->_function.object = NULL; + SP++; +} + +NOINLINE static void _push_extern(int ind) +{ + SP->type = T_FUNCTION; + SP->_function.class = CP; + SP->_function.object = NULL; + SP->_function.kind = FUNCTION_EXTERN; + SP->_function.index = ind; + SP->_function.defined = TRUE; + SP++; +} + +NOINLINE static void _pop_optional(int ind) +{ + VALUE *val = &PP[ind]; + + if (val->type == T_VOID) + { + if (SP[-1].type == T_VOID) + VALUE_default(&SP[-1], val->_void.ptype); + else + VALUE_conv(&SP[-1], val->_void.ptype); + + SP--; + *val = *SP; + } + else + POP(); +} + +NOINLINE static void _push_me(ushort code) +{ + if (GET_UX() & 1) + { + if (DEBUG_info) + { + if (DEBUG_info->op) + { + SP->_object.class = DEBUG_info->cp; + SP->_object.object = DEBUG_info->op; + } + else if (DEBUG_info->cp) + { + SP->type = T_CLASS; + SP->_class.class = DEBUG_info->cp; + } + } + else + VALUE_null(SP); + } + else + { + if (OP) + { + SP->_object.class = CP; + SP->_object.object = OP; + } + /*else if (CP->auto_create) + { + OP = EXEC_auto_create(CP, FALSE); + SP->_object.class = CP; + SP->_object.object = OP; + OP = NULL; + }*/ + else + { + SP->type = T_CLASS; + SP->_class.class = CP; + } + } + + if (GET_UX() & 2) + { + // The used class must be in the stack, because it is tested by exec_push && exec_pop + if (OP) + { + SP->_object.class = SP->_object.class->parent; + SP->_object.super = EXEC_super; + } + else + { + SP->_class.class = SP->_class.class->parent; + SP->_class.super = EXEC_super; + } + + EXEC_super = SP; + + //fprintf(stderr, "%s\n", DEBUG_get_current_position()); + //BREAKPOINT(); + } + + PUSH(); +} + +NOINLINE static bool _push_misc(ushort code) +{ + static const void *_jump[] = + { &&__PUSH_NULL, &&__PUSH_VOID, &&__PUSH_FALSE, &&__PUSH_TRUE, &&__PUSH_LAST, &&__PUSH_STRING, &&__PUSH_PINF, &&__PUSH_MINF, &&__PUSH_COMPLEX, + &&__PUSH_VARGS, &&__PUSH_DROP_VARGS, &&__JIT_RETURN, &&__PUSH_END_VARGS }; + //, &&__POP_LAST }; + + goto *_jump[GET_UX()]; + +__PUSH_NULL: + + VALUE_null(SP); + SP++; + return FALSE; + +__PUSH_VOID: + + SP->type = T_VOID; + SP++; + return FALSE; + +__PUSH_FALSE: + + SP->type = T_BOOLEAN; + SP->_integer.value = 0; + SP++; + return FALSE; + +__PUSH_TRUE: + + SP->type = T_BOOLEAN; + SP->_integer.value = -1; + SP++; + return FALSE; + +__PUSH_LAST: + + SP->type = T_OBJECT; + SP->_object.object = EVENT_Last; + OBJECT_REF_CHECK(EVENT_Last); + SP++; + return FALSE; + +__PUSH_STRING: + + SP->type = T_CSTRING; + SP->_string.addr = ""; // NULL + SP->_string.start = SP->_string.len = 0; + SP++; + return FALSE; + +__PUSH_PINF: + + SP->type = T_FLOAT; + SP->_float.value = INFINITY; + SP++; + return FALSE; + +__PUSH_MINF: + + SP->type = T_FLOAT; + SP->_float.value = -INFINITY; + SP++; + return FALSE; + +__PUSH_COMPLEX: + + EXEC_push_complex(); + return FALSE; + +__PUSH_VARGS: + + EXEC_push_vargs(); + return FALSE; + +__PUSH_DROP_VARGS: + + EXEC_drop_vargs(); + return FALSE; + +__JIT_RETURN: + return TRUE; + +__PUSH_END_VARGS: + + EXEC_end_vargs(); + return FALSE; + +/*__POP_LAST: + + VALUE_conv(&SP[-1], T_OBJECT); + OBJECT_UNREF(EVENT_Last); + SP--; + EVENT_Last = SP->_object.object; + goto _NEXT;*/ +} + +void EXEC_loop(void) +{ + static const void *jump_table[256] = + { + /* 00 NOP */ &&_NEXT, + /* 01 PUSH LOCAL */ &&_PUSH_LOCAL, + /* 02 PUSH PARAM */ &&_PUSH_PARAM, + /* 03 PUSH ARRAY */ &&_PUSH_ARRAY, + /* 04 PUSH UNKNOWN */ &&_PUSH_UNKNOWN, + /* 05 PUSH EXTERN */ &&_PUSH_EXTERN, + /* 06 BYREF */ &&_BYREF, + /* 07 PUSH EVENT */ &&_PUSH_EVENT, + /* 08 QUIT */ &&_QUIT, + /* 09 POP LOCAL */ &&_POP_LOCAL, + /* 0A POP PARAM */ &&_POP_PARAM, + /* 0B POP ARRAY */ &&_POP_ARRAY, + /* 0C POP UNKNOWN */ &&_POP_UNKNOWN, + /* 0D POP OPTIONAL */ &&_POP_OPTIONAL, + /* 0E POP CTRL */ &&_POP_CTRL, + /* 0F BREAK */ &&_BREAK, + /* 10 RETURN */ &&_RETURN, + /* 11 PUSH SHORT */ &&_PUSH_SHORT, + /* 12 PUSH INTEGER */ &&_PUSH_INTEGER, + /* 13 PUSH CHAR */ &&_PUSH_CHAR, + /* 14 PUSH MISC */ &&_PUSH_MISC, + /* 15 PUSH ME */ &&_PUSH_ME, + /* 16 TRY */ &&_TRY, + /* 17 END TRY */ &&_END_TRY, + /* 18 CATCH */ &&_CATCH, + /* 19 DUP */ &&_DUP, + /* 1A DROP */ &&_DROP, + /* 1B NEW */ &&_NEW, + /* 1C CALL */ &&_CALL, + /* 1D CALL QUICK */ &&_CALL_QUICK, + /* 1E CALL EASY */ &&_CALL_SLOW, + /* 1F ON */ &&_ON_GOTO_GOSUB, + /* 20 JUMP */ &&_JUMP, + /* 21 JUMP IF TRUE */ &&_JUMP_IF_TRUE, + /* 22 JUMP IF FALSE */ &&_JUMP_IF_FALSE, + /* 23 GOSUB */ &&_GOSUB, + /* 24 JUMP FIRST */ &&_JUMP_FIRST, + /* 25 JUMP NEXT */ &&_JUMP_NEXT, + /* 26 FIRST */ &&_ENUM_FIRST, + /* 27 NEXT */ &&_ENUM_NEXT, + /* 28 = */ &&_SUBR_COMPE, + /* 29 <> */ &&_SUBR_COMPN, + /* 2A > */ &&_SUBR_COMPGT, + /* 2B <= */ &&_SUBR_COMPLE, + /* 2C < */ &&_SUBR_COMPLT, + /* 2D >= */ &&_SUBR_COMPGE, + /* 2E == */ &&_SUBR, + /* 2F CASE */ &&_SUBR_CODE, + /* 30 + */ &&_SUBR_ADD, + /* 31 - */ &&_SUBR_SUB, + /* 32 * */ &&_SUBR_MUL, + /* 33 / */ &&_SUBR_DIV, + /* 34 NEG */ &&_SUBR_CODE, + /* 35 \ */ &&_SUBR_CODE, + /* 36 MOD */ &&_SUBR_CODE, + /* 37 ^ */ &&_SUBR_CODE, + /* 38 AND */ &&_SUBR_CODE, + /* 39 OR */ &&_SUBR_CODE, + /* 3A XOR */ &&_SUBR_CODE, + /* 3B NOT */ &&_SUBR_CODE, + /* 3C & */ &&_SUBR_CODE, + /* 3D LIKE */ &&_SUBR_CODE, + /* 3E &/ */ &&_SUBR_CODE, + /* 3F Is */ &&_SUBR_CODE, + /* 40 Left$ */ &&_SUBR_LEFT, + /* 41 Mid$ */ &&_SUBR_MID, + /* 42 Right$ */ &&_SUBR_RIGHT, + /* 43 Len */ &&_SUBR_LEN, + /* 44 Space$ */ &&_SUBR, + /* 45 String$ */ &&_SUBR, + /* 46 Trim$ */ &&_SUBR_CODE, + /* 47 UCase$ */ &&_SUBR_CODE, + /* 48 Oct$ */ &&_SUBR_CODE, + /* 49 Chr$ */ &&_SUBR, + /* 4A Asc */ &&_SUBR_CODE, + /* 4B InStr */ &&_SUBR_CODE, + /* 4C RInStr */ &&_SUBR_CODE, + /* 4D Subst$ */ &&_SUBR_CODE, + /* 4E Replace$ */ &&_SUBR_CODE, + /* 4F Split */ &&_SUBR_CODE, + /* 50 Scan */ &&_SUBR, + /* 51 Comp */ &&_SUBR_CODE, + /* 52 Conv */ &&_SUBR, + /* 53 DConv */ &&_SUBR_CODE, + /* 54 Abs */ &&_SUBR_CODE, + /* 55 Int */ &&_SUBR_CODE, + /* 56 Fix */ &&_SUBR_CODE, + /* 57 Sgn */ &&_SUBR_CODE, + /* 58 Frac... */ &&_SUBR_CODE, + /* 59 Pi */ &&_SUBR_CODE, + /* 5A Round */ &&_SUBR_CODE, + /* 5B Randomize */ &&_SUBR_CODE, + /* 5C Rnd */ &&_SUBR_CODE, + /* 5D Min */ &&_SUBR_CODE, + /* 5E Max */ &&_SUBR_CODE, + /* 5F IIf */ &&_SUBR_CODE, + /* 60 Choose */ &&_SUBR_CODE, + /* 61 Array */ &&_SUBR_CODE, + /* 62 ATan2... */ &&_SUBR_CODE, + /* 63 IsAscii... */ &&_SUBR_CODE, + /* 64 BClr... */ &&_SUBR_CODE, + /* 65 IsBoolean... */ &&_SUBR_CODE, + /* 66 TypeOf */ &&_SUBR_CODE, + /* 67 CBool... */ &&_SUBR_CONV, + /* 68 Bin$ */ &&_SUBR_CODE, + /* 69 Hex$ */ &&_SUBR_CODE, + /* 6A Val */ &&_SUBR, + /* 6B Str */ &&_SUBR, + /* 6C Format */ &&_SUBR_CODE, + /* 6D Timer */ &&_SUBR, + /* 6E Now */ &&_SUBR, + /* 6F Year... */ &&_SUBR_CODE, + /* 70 Week */ &&_SUBR_CODE, + /* 71 Date */ &&_SUBR_CODE, + /* 72 Time... */ &&_SUBR_CODE, + /* 73 DateAdd... */ &&_SUBR_CODE, + /* 74 Eval */ &&_SUBR_CODE, + /* 75 Error */ &&_SUBR, + /* 76 Debug */ &&_SUBR_CODE, + /* 77 Wait */ &&_SUBR_CODE, + /* 78 Open */ &&_SUBR_CODE, + /* 79 Close */ &&_SUBR, + /* 7A Input */ &&_SUBR_CODE, + /* 7B LineInput */ &&_SUBR, + /* 7C Print */ &&_SUBR_CODE, + /* 7D Read */ &&_SUBR_CODE, + /* 7E Write */ &&_SUBR_CODE, + /* 7F Flush */ &&_SUBR, + /* 80 Lock... */ &&_SUBR_CODE, + /* 81 InputFrom... */ &&_SUBR_CODE, + /* 82 Eof */ &&_SUBR_CODE, + /* 83 Lof */ &&_SUBR_CODE, + /* 84 Seek */ &&_SUBR_CODE, + /* 85 Kill */ &&_SUBR_CODE, + /* 86 Mkdir */ &&_SUBR_CODE, + /* 87 Rmdir */ &&_SUBR_CODE, + /* 88 Move */ &&_SUBR_CODE, + /* 89 Copy */ &&_SUBR_CODE, + /* 8A Link */ &&_SUBR_CODE, + /* 8B Exist */ &&_SUBR_CODE, + /* 8C Access */ &&_SUBR_CODE, + /* 8D Stat */ &&_SUBR_CODE, + /* 8E Dfree */ &&_SUBR, + /* 8F Temp$ */ &&_SUBR_CODE, + /* 90 IsDir */ &&_SUBR, + /* 91 Dir */ &&_SUBR_CODE, + /* 92 RDir */ &&_SUBR_CODE, + /* 93 Exec... */ &&_SUBR_CODE, + /* 94 Alloc */ &&_SUBR_CODE, + /* 95 Free */ &&_SUBR, + /* 96 Realloc */ &&_SUBR_CODE, + /* 97 StrPtr */ &&_SUBR_CODE, + /* 98 Sleep... */ &&_SUBR_CODE, + /* 99 VarPtr */ &&_SUBR_CODE, + /* 9A Collection */ &&_SUBR_CODE, + /* 9B Tr$ */ &&_SUBR, + /* 9C Quote$... */ &&_SUBR_CODE, + /* 9D Unquote$... */ &&_SUBR_CODE, + /* 9E MkInt$... */ &&_SUBR_CODE, + /* 9F Byte@... */ &&_SUBR_CODE, + /* A0 ADD QUICK */ &&_ADD_QUICK, + /* A1 ADD QUICK */ &&_PUSH_ARRAY_NATIVE_INTEGER, + /* A2 ADD QUICK */ &&_POP_ARRAY_NATIVE_INTEGER, + /* A3 ADD QUICK */ &&_PUSH_ARRAY_NATIVE_FLOAT, + /* A4 ADD QUICK */ &&_POP_ARRAY_NATIVE_FLOAT, + /* A5 ADD QUICK */ &&_ADD_QUICK, + /* A6 ADD QUICK */ &&_ADD_QUICK, + /* A7 ADD QUICK */ &&_ADD_INTEGER, + /* A8 ADD QUICK */ &&_ADD_FLOAT, + /* A9 ADD QUICK */ &&_SUB_INTEGER, + /* AA ADD QUICK */ &&_SUB_FLOAT, + /* AB ADD QUICK */ &&_MUL_INTEGER, + /* AC ADD QUICK */ &&_MUL_FLOAT, + /* AD ADD QUICK */ &&_DIV_INTEGER, + /* AE ADD QUICK */ &&_DIV_FLOAT, + /* AF ADD QUICK */ &&_ADD_QUICK, + /* B0 PUSH CLASS */ &&_PUSH_CLASS, + /* B1 PUSH CLASS */ &&_PUSH_CLASS, + /* B2 PUSH CLASS */ &&_PUSH_CLASS, + /* B3 PUSH CLASS */ &&_PUSH_CLASS, + /* B4 PUSH CLASS */ &&_PUSH_CLASS, + /* B5 PUSH CLASS */ &&_PUSH_CLASS, + /* B6 PUSH CLASS */ &&_PUSH_CLASS, + /* B7 PUSH CLASS */ &&_PUSH_CLASS, + /* B8 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* B9 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BA PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BB PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BC PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BD PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BE PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BF PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* C0 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C1 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C2 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C3 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C4 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C5 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C6 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C7 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C8 PUSH STATIC */ &&_PUSH_STATIC, + /* C9 PUSH STATIC */ &&_PUSH_STATIC, + /* CA PUSH STATIC */ &&_PUSH_STATIC, + /* CB PUSH STATIC */ &&_PUSH_STATIC, + /* CC PUSH STATIC */ &&_PUSH_STATIC, + /* CD PUSH STATIC */ &&_PUSH_STATIC, + /* CE PUSH STATIC */ &&_PUSH_STATIC, + /* CF PUSH STATIC */ &&_PUSH_STATIC, + /* D0 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D1 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D2 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D3 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D4 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D5 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D6 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D7 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D8 POP STATIC */ &&_POP_STATIC, + /* D9 POP STATIC */ &&_POP_STATIC, + /* DA POP STATIC */ &&_POP_STATIC, + /* DB POP STATIC */ &&_POP_STATIC, + /* DC POP STATIC */ &&_POP_STATIC, + /* DD POP STATIC */ &&_POP_STATIC, + /* DE POP STATIC */ &&_POP_STATIC, + /* DF POP STATIC */ &&_POP_STATIC, + /* E0 PUSH CONST */ &&_PUSH_CONST, + /* E1 PUSH CONST */ &&_PUSH_CONST, + /* E2 PUSH CONST */ &&_PUSH_CONST, + /* E3 PUSH CONST */ &&_PUSH_CONST, + /* E4 PUSH CONST */ &&_PUSH_CONST, + /* E5 PUSH CONST */ &&_PUSH_CONST, + /* E6 PUSH CONST */ &&_PUSH_CONST, + /* E7 PUSH CONST */ &&_PUSH_CONST, + /* E8 PUSH CONST */ &&_PUSH_CONST, + /* E9 PUSH CONST */ &&_PUSH_CONST, + /* EA PUSH CONST */ &&_PUSH_CONST, + /* EB PUSH CONST */ &&_PUSH_CONST, + /* EC PUSH CONST */ &&_PUSH_CONST, + /* ED PUSH CONST */ &&_PUSH_CONST, + /* EE PUSH CONST */ &&_PUSH_CONST, + /* EF PUSH CONST */ &&_PUSH_CONST_EX, + /* F0 PUSH QUICK */ &&_PUSH_QUICK, + /* F1 PUSH QUICK */ &&_PUSH_LOCAL_NOREF, + /* F2 PUSH QUICK */ &&_PUSH_PARAM_NOREF, + /* F3 PUSH QUICK */ &&_JUMP_IF_TRUE_FAST, + /* F4 PUSH QUICK */ &&_JUMP_IF_FALSE_FAST, + /* F5 PUSH QUICK */ &&_PUSH_VARIABLE, + /* F6 PUSH QUICK */ &&_POP_VARIABLE, + /* F7 PUSH QUICK */ &&_PUSH_FLOAT, + /* F8 PUSH QUICK */ &&_SUBR_POKE, + /* F9 PUSH QUICK */ &&_POP_LOCAL_NOREF, + /* FA PUSH QUICK */ &&_POP_PARAM_NOREF, + /* FB PUSH QUICK */ &&_POP_LOCAL_FAST, + /* FC PUSH QUICK */ &&_POP_PARAM_FAST, + /* FD PUSH QUICK */ &&_PUSH_QUICK, + /* FE PUSH QUICK */ &&_JUMP_NEXT_INTEGER, + /* FF PUSH QUICK */ &&_PUSH_QUICK + }; + + static const void *jump_table_3_18_AXXX[] = { + /* A0 ADD QUICK */ &&_ADD_QUICK, + /* A1 ADD QUICK */ &&_PUSH_ARRAY_NATIVE_INTEGER, + /* A2 ADD QUICK */ &&_POP_ARRAY_NATIVE_INTEGER, + /* A3 ADD QUICK */ &&_PUSH_ARRAY_NATIVE_FLOAT, + /* A4 ADD QUICK */ &&_POP_ARRAY_NATIVE_FLOAT, + /* A5 ADD QUICK */ &&_ADD_QUICK, + /* A6 ADD QUICK */ &&_ADD_QUICK, + /* A7 ADD QUICK */ &&_ADD_INTEGER, + /* A8 ADD QUICK */ &&_ADD_FLOAT, + /* A9 ADD QUICK */ &&_SUB_INTEGER, + /* AA ADD QUICK */ &&_SUB_FLOAT, + /* AB ADD QUICK */ &&_MUL_INTEGER, + /* AC ADD QUICK */ &&_MUL_FLOAT, + /* AD ADD QUICK */ &&_DIV_INTEGER, + /* AE ADD QUICK */ &&_DIV_FLOAT, + /* AF ADD QUICK */ &&_ADD_QUICK + }; + + static const void *jump_table_3_18_FXXX[] = { + /* F0 PUSH QUICK */ &&_PUSH_QUICK, + /* F1 PUSH QUICK */ &&_PUSH_LOCAL_NOREF, + /* F2 PUSH QUICK */ &&_PUSH_PARAM_NOREF, + /* F3 PUSH QUICK */ &&_JUMP_IF_TRUE_FAST, + /* F4 PUSH QUICK */ &&_JUMP_IF_FALSE_FAST, + /* F5 PUSH QUICK */ &&_PUSH_VARIABLE, + /* F6 PUSH QUICK */ &&_POP_VARIABLE, + /* F7 PUSH QUICK */ &&_PUSH_FLOAT, + /* F8 PUSH QUICK */ &&_SUBR_POKE, + /* F9 PUSH QUICK */ &&_POP_LOCAL_NOREF, + /* FA PUSH QUICK */ &&_POP_PARAM_NOREF, + /* FB PUSH QUICK */ &&_POP_LOCAL_FAST, + /* FC PUSH QUICK */ &&_POP_PARAM_FAST, + /* FD PUSH QUICK */ &&_PUSH_QUICK, + /* FE PUSH QUICK */ &&_JUMP_NEXT_INTEGER, + /* FF PUSH QUICK */ &&_PUSH_QUICK + }; + + int NO_WARNING(ind); + ushort code; + VALUE *NO_WARNING(val); + +/*-----------------------------------------------*/ + +_MAIN: + +#if 0 + { + FILE *f; + + f = fopen("/var/log/thttpd/pcode.log", "a"); + if (f) + { + fprintf(f, "%s: ", DEBUG_get_current_position()); + if (*PC >> 8) + PCODE_dump(f, PC - FP->code, PC); + else + fprintf(f, "\n"); + fclose(f); + } + } +#endif + +#if DEBUG_PCODE + DEBUG_where(); + fprintf(stderr, "[%4d %3ld] ", (int)(intptr_t)(SP - (VALUE *)STACK_base), SP - PP); + if (FP) + PCODE_dump(stderr, PC - FP->code, PC); + else + fprintf(stderr, "?\n"); + fflush(stderr); +#endif + + code = *PC; + goto *jump_table[code >> 8]; + +/*-----------------------------------------------*/ + +_SUBR_CODE: + + //fprintf(stderr, "gbx3: %02X: %s\n", (code >> 8), DEBUG_get_current_position()); + (*(EXEC_FUNC_CODE)EXEC_subr_table[code >> 8])(code); + + //if (PCODE_is_void(code)) + // POP(); + +/*-----------------------------------------------*/ + +_NEXT: + + code = *(++PC); + +#if 0 + { + FILE *f; + + f = fopen("/var/log/thttpd/pcode.log", "a"); + if (f) + { + fprintf(f, "%s: ", DEBUG_get_current_position()); + if (*PC >> 8) + PCODE_dump(f, PC - FP->code, PC); + else + fprintf(f, "\n"); + fclose(f); + } + } +#endif + +#if DEBUG_PCODE + DEBUG_where(); + fprintf(stderr, "[%4d %3ld] ", (int)(intptr_t)(SP - (VALUE *)STACK_base), SP - PP); + if (FP) + PCODE_dump(stderr, PC - FP->code, PC); + else + fprintf(stderr, "?\n"); + fflush(stderr); +#endif + + goto *jump_table[code >> 8]; + +/*-----------------------------------------------*/ + +_SUBR: + + (*(EXEC_FUNC)EXEC_subr_table[code >> 8])(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_LOCAL: + + *SP = BP[GET_XX()]; + PUSH(); + goto _NEXT; + +_PUSH_LOCAL_NOREF: + + *SP++ = BP[GET_XX()]; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_PARAM: + + *SP = PP[GET_XX()]; + PUSH(); + goto _NEXT; + +_PUSH_PARAM_NOREF: + + *SP++ = PP[GET_XX()]; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_ARRAY: + + EXEC_push_array(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_UNKNOWN: + + EXEC_push_unknown(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_EVENT: + + /* + The function called by raising an event is different at each call, + but the signature remains the same, so optimizing the next CALL + instruction with CALL QUICK is safe. + + The only problem is when pushing a 'NULL' function, i.e. a function + that does nothing, because there is no handler for this event. + Then CALL QUICK must know how to handle these functions. + */ + + _push_event(GET_UX()); + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_LOCAL: + + val = &BP[GET_XX()]; + VALUE_conv(&SP[-1], val->type); + RELEASE(val); + SP--; + *val = *SP; + + goto _NEXT; + +_POP_LOCAL_NOREF: + + val = &BP[GET_XX()]; + VALUE_conv(&SP[-1], val->type); + SP--; + *val = *SP; + + goto _NEXT; + +_POP_LOCAL_FAST: + + SP--; + BP[GET_XX()] = *SP; + + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_PARAM: + + val = &PP[GET_XX()]; + VALUE_conv(&SP[-1], val->type); + RELEASE(val); + SP--; + *val = *SP; + goto _NEXT; + +_POP_PARAM_NOREF: + + val = &PP[GET_XX()]; + VALUE_conv(&SP[-1], val->type); + SP--; + *val = *SP; + goto _NEXT; + +_POP_PARAM_FAST: + + SP--; + PP[GET_XX()] = *SP; + + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_CTRL: + + _pop_ctrl(GET_XX()); + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_ARRAY: + + EXEC_pop_array(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_UNKNOWN: + + EXEC_pop_unknown(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_OPTIONAL: + + _pop_optional(GET_XX()); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_SHORT: + + SP->type = T_INTEGER; + PC++; + SP->_integer.value = *((short *)PC); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_INTEGER: + + SP->type = T_INTEGER; + PC++; + SP->_integer.value = PC[0] | ((uint)PC[1] << 16); + SP++; + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_PUSH_CHAR: + + STRING_char_value(SP, GET_UX()); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_ME: + + _push_me(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_MISC: + + if (_push_misc(code)) + return; + goto _NEXT; + + +/*-----------------------------------------------*/ + +_DUP: + + *SP = SP[-1]; + PUSH(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_DROP: + + POP(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_NEW: + + EXEC_new(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_ON_GOTO_GOSUB: + + { + int n, m; + + m = GET_XX(); + SP--; + VALUE_conv_integer(SP); + n = SP->_integer.value; + if (n < 0 || n >= m) + PC += m + 3; + else + { + PC[m + 2] = PC[n + 1] - (m - n) - 2; + PC += m + 1; + } + goto _MAIN; + } + +/*-----------------------------------------------*/ + +_GOSUB: + + { + STACK_check(1 + FP->stack_usage - FP->n_local); + + SP->type = T_VOID; + SP->_void.value[0] = (intptr_t)PC; + SP->_void.value[1] = (intptr_t)GP; + + GP = SP; + + SP++; + + val = &BP[FP->n_local]; + for (ind = 0; ind < FP->n_ctrl; ind++) + { + *SP++ = val[ind]; + val[ind].type = T_NULL; + } + } + +/*-----------------------------------------------*/ + +_JUMP: + + PC += (signed short)PC[1] + 2; + goto _MAIN; + + +/*-----------------------------------------------*/ + +_JUMP_IF_TRUE: + + VALUE_convert_boolean(&SP[-1]); + +_JUMP_IF_TRUE_FAST: + + SP--; + if (SP->_boolean.value & 1) + PC += (signed short)PC[1]; + + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_JUMP_IF_FALSE: + + VALUE_convert_boolean(&SP[-1]); + +_JUMP_IF_FALSE_FAST: + + SP--; + if ((SP->_boolean.value & 1) == 0) + PC += (signed short)PC[1]; + + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_RETURN: + + { + static const void *return_jump[] = { &&__RETURN_GOSUB, &&__RETURN_VALUE, &&__RETURN_VOID, &&__INIT_BYTECODE_CHECK }; + + goto *return_jump[GET_UX()]; + + __RETURN_GOSUB: + + if (!GP) + goto __RETURN_VOID; + + val = &BP[FP->n_local]; + GP++; + + for (ind = 0; ind < FP->n_ctrl; ind++) + { + RELEASE(&val[ind]); + val[ind] = GP[ind]; + } + + GP--; + + SP = GP; + PC = (PCODE *)GP->_void.value[0] + 2; + GP = (VALUE *)GP->_void.value[1]; + + goto _MAIN; + + __RETURN_VALUE: + + VALUE_conv(&SP[-1], FP->type); + SP--; + *RP = *SP; + + goto __RETURN_LEAVE; + + __RETURN_VOID: + + VALUE_default(RP, FP->type); + + __RETURN_LEAVE: + + EXEC_leave_keep(); + + if (!PC) + return; + + goto _NEXT; + + __INIT_BYTECODE_CHECK: + + _sb_jump_table = jump_table; + _sb_jump_table_3_18_AXXX = jump_table_3_18_AXXX; + _sb_jump_table_3_18_FXXX = jump_table_3_18_FXXX; + return; + } + +/*-----------------------------------------------*/ + +_CALL: + + { + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE, &&__CALL_PRIVATE, &&__CALL_PUBLIC, + &&__CALL_EVENT, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + &&__CALL_SUBR + }; + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + if (!TYPE_is_function(val->type)) + { + bool defined = EXEC_object(val, &EXEC.class, (OBJECT **)&EXEC.object); + + val->type = T_FUNCTION; + val->_function.kind = FUNCTION_CALL; + val->_function.defined = defined; + val->_function.class = EXEC.class; + val->_function.object = EXEC.object; + //goto _CALL; + } + else + { + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + } + + EXEC.nparam = ind; + EXEC.use_stack = TRUE; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + goto *call_jump[(int)val->_function.kind]; + + __CALL_NULL: + + while (ind > 0) + { + POP(); + ind--; + } + + POP(); + + //if (!PCODE_is_void(code)) + { + /*VALUE_default(SP, (TYPE)(val->_function.function));*/ + VALUE_null(SP); + SP++; + } + + goto _NEXT; + + __CALL_NATIVE: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + //EXEC.use_stack = TRUE; + + goto __EXEC_NATIVE; + + __CALL_PRIVATE: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + goto __EXEC_ENTER; + + __CALL_PUBLIC: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + goto __EXEC_ENTER; + + __EXEC_ENTER: + + if (EXEC.func->fast && !JIT_exec(TRUE)) + { + goto _NEXT; + } + else + { + EXEC_enter_check(val->_function.defined); + goto _MAIN;; + } + + __EXEC_NATIVE: + + EXEC_native_check(val->_function.defined); + goto _NEXT; + + __CALL_EVENT: + + //if (OP && !strcmp(OBJECT_class(OP)->name, "Workspace")) + // BREAKPOINT(); + ind = GB_Raise(OP, val->_function.index, (-EXEC.nparam)); + + POP(); // function + + //if (!PCODE_is_void(code)) + { + SP->type = T_BOOLEAN; + SP->_boolean.value = ind ? -1 : 0; + SP++; + } + + //EVENT_Last = old_last; + + goto _NEXT; + + __CALL_UNKNOWN: + + EXEC_unknown_name = CP->load->unknown[val->_function.index]; + EXEC.desc = CLASS_get_special_desc(EXEC.class, SPEC_UNKNOWN); + //EXEC.use_stack = TRUE; + goto __CALL_SPEC; + + __CALL_CALL: + + EXEC.desc = CLASS_get_special_desc(EXEC.class, SPEC_CALL); + + if (EXEC.desc) + { + if (!CLASS_DESC_is_static_method(EXEC.desc) && !EXEC.object) + { + if (!EXEC.class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(EXEC.class), $("_call")); + + EXEC.object = EXEC_auto_create(EXEC.class, FALSE); + EXEC.nparam = ind; + } + + goto __CALL_SPEC; + } + + if (!EXEC.object && EXEC.nparam == 1 && !EXEC.class->is_virtual) + { + SP[-2] = SP[-1]; + SP--; + VALUE_conv_object(SP - 1, (TYPE)EXEC.class); + goto _NEXT; + } + + __CALL_SPEC: + + if (!EXEC.desc) + THROW(E_NFUNC); + + EXEC.native = FUNCTION_is_native(EXEC.desc); + + if (EXEC.native) + { + EXEC_native(); + goto _NEXT; + } + else + { + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + if (EXEC.func->fast && !JIT_exec(TRUE)) + { + goto _NEXT; + } + else + { + EXEC_enter(); + goto _MAIN;; + } + } + + __CALL_EXTERN: + + EXEC.index = val->_function.index; + EXTERN_call(); + goto _NEXT; + + __CALL_SUBR: + + ind = GET_3X(); + ((EXEC_FUNC_CODE_SP)(EXEC.class->table[val->_function.index].desc->method.exec))(ind, SP); + SP -= ind; + SP[-1] = SP[0]; + goto _NEXT; + } + +/*-----------------------------------------------*/ + +_CALL_QUICK: + + { + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE_Q, &&__CALL_PRIVATE_Q, &&__CALL_PUBLIC_Q, + &&__CALL_EVENT, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + &&__CALL_SUBR + }; + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + EXEC.nparam = ind; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + //if (call_jump[(int)val->_function.kind] == 0) + // fprintf(stderr, "val->_function.kind = %d ?\n", val->_function.kind); + + goto *call_jump[(int)val->_function.kind]; + + __CALL_PRIVATE_Q: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER_Q; + + __CALL_PUBLIC_Q: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + + __EXEC_ENTER_Q: + + EXEC_enter_quick(); + goto _MAIN;; + + __CALL_NATIVE_Q: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + + EXEC_native_quick(); + goto _NEXT; + } + +/*-----------------------------------------------*/ + +#if 0 +_CALL_EASY: +{ + static const void *call_jump[] = + { &&__CALL_NULL, &&__CALL_NATIVE_E, &&__CALL_PRIVATE_E, &&__CALL_PUBLIC_E }; + + VALUE * NO_WARNING(val); + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + EXEC.nparam = ind; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + //if (call_jump[(int)val->_function.kind] == 0) + // fprintf(stderr, "val->_function.kind = %d ?\n", val->_function.kind); + + goto *call_jump[(int)val->_function.kind]; + +__CALL_PRIVATE_E: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER_E; + +__CALL_PUBLIC_E: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + +__EXEC_ENTER_E: + + EXEC_enter_easy(); + goto _MAIN; + +__CALL_NATIVE_E: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + + EXEC_native_easy(); + goto _NEXT; +} +#endif + +/*-----------------------------------------------*/ + +_CALL_SLOW: + + { + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE_S, &&__CALL_PRIVATE_S, &&__CALL_PUBLIC_S, + &&__CALL_EVENT, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + &&__CALL_SUBR + }; + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + EXEC.nparam = ind; + EXEC.use_stack = TRUE; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + goto *call_jump[(int)val->_function.kind]; + + __CALL_PRIVATE_S: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER_S; + + __CALL_PUBLIC_S: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + + __EXEC_ENTER_S: + + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + if (EXEC.func->fast && !JIT_exec(TRUE)) + { + goto _NEXT; + } + else + { + EXEC_enter(); + goto _MAIN;; + } + + __CALL_NATIVE_S: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + + EXEC_native(); + goto _NEXT; + } + +/*-----------------------------------------------*/ + +_JUMP_FIRST: + + { + static const void *const jn_jump[] = + { + NULL, &&_JN_INTEGER_INC, &&_JN_BYTE, &&_JN_SHORT, &&_JN_INTEGER_DEC, &&_JN_LONG, &&_JN_SINGLE, &&_JN_FLOAT + }; + + VALUE * NO_WARNING(inc); + VALUE * NO_WARNING(end); + TYPE type; + + ind = GET_XX(); + + end = &BP[ind]; + inc = &BP[ind + 1]; + val = &BP[PC[3] & 0xFF]; + + type = val->type; + + if (type < T_BYTE || type > T_FLOAT) + THROW(E_TYPE, "Number", TYPE_get_name(type)); + + if (type > T_INTEGER) + VALUE_conv(&SP[-1], type); + else + VALUE_conv_integer(&SP[-1]); + + VALUE_conv(&SP[-2], type); + + _pop_ctrl(ind + 1); + _pop_ctrl(ind); + + // loop mode is stored in the inc type. It must be strictly lower than T_STRING + + if (type == T_INTEGER && inc->_integer.value == 1 && !CP->not_3_18) + { + PC++; + *PC = C_JUMP_NEXT_INTEGER | ind; + goto _JN_INTEGER_TEST_INC; + } + else + { + if (type == T_INTEGER && inc->_integer.value > 0) + type = 1; + + inc->type = type; + + PC++; + *PC |= ind; + + if (type <= T_INTEGER) + { + if (inc->_integer.value < 0) + goto _JN_INTEGER_TEST_DEC; + else + goto _JN_INTEGER_TEST_INC; + } + else if (type == T_LONG) + goto _JN_LONG_TEST; + else if (type == T_SINGLE) + goto _JN_SINGLE_TEST; + else //if (type == T_FLOAT) + goto _JN_FLOAT_TEST; + } + +/*-----------------------------------------------*/ + +_JUMP_NEXT_INTEGER: + + end = &BP[GET_XX()]; + val = &BP[PC[2] & 0xFF]; + + val->_integer.value++; + + if (val->_integer.value <= end->_integer.value) + PC += 3; + else + PC += (signed short)PC[1] + 2; + + goto _MAIN; + +/*-----------------------------------------------*/ + +_JUMP_NEXT: + + end = &BP[GET_XX()]; + inc = end + 1; + val = &BP[PC[2] & 0xFF]; + + goto *jn_jump[inc->type]; + + _JN_BYTE: + val->_integer.value = (unsigned char)(val->_integer.value + inc->_integer.value); + if (inc->_integer.value < 0) + goto _JN_INTEGER_TEST_DEC; + else + goto _JN_INTEGER_TEST_INC; + + _JN_SHORT: + val->_integer.value = (short)(val->_integer.value + inc->_integer.value); + if (inc->_integer.value < 0) + goto _JN_INTEGER_TEST_DEC; + else + goto _JN_INTEGER_TEST_INC; + + _JN_INTEGER_INC: + val->_integer.value += inc->_integer.value; + + _JN_INTEGER_TEST_INC: + if (val->_integer.value <= end->_integer.value) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_INTEGER_DEC: + val->_integer.value += inc->_integer.value; + + _JN_INTEGER_TEST_DEC: + if (val->_integer.value >= end->_integer.value) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_LONG: + val->_long.value += inc->_long.value; + + _JN_LONG_TEST: + if ((inc->_long.value > 0 && val->_long.value <= end->_long.value) + || (inc->_long.value < 0 && val->_long.value >= end->_long.value)) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_SINGLE: + val->_single.value += inc->_single.value; + + _JN_SINGLE_TEST: + if ((inc->_single.value > 0 && val->_single.value <= end->_single.value) + || (inc->_single.value < 0 && val->_single.value >= end->_single.value)) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_FLOAT: + val->_float.value += inc->_float.value; + + _JN_FLOAT_TEST: + if ((inc->_float.value > 0 && val->_float.value <= end->_float.value) + || (inc->_float.value < 0 && val->_float.value >= end->_float.value)) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_END: + PC += (signed short)PC[1] + 2; + goto _MAIN; + } + +/*-----------------------------------------------*/ + +_ENUM_FIRST: + + ind = GET_XX(); + _pop_ctrl(ind); + EXEC_enum_first(code, &BP[ind], &BP[ind + 1]); + goto _NEXT; + +/*-----------------------------------------------*/ + +_ENUM_NEXT: + + ind = PC[-1] & 0xFF; + if (EXEC_enum_next(code, &BP[ind], &BP[ind + 1])) + goto _JUMP; + else + { + PC += 2; + goto _MAIN; + } + +/*-----------------------------------------------*/ + +_PUSH_CLASS: + + SP->type = T_CLASS; + SP->_class.class = CP->load->class_ref[GET_7XX()];; + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_FUNCTION: + + /*ind = GET_7XX();*/ + + SP->type = T_FUNCTION; + SP->_function.class = CP; + SP->_function.object = OP; + SP->_function.kind = FUNCTION_PRIVATE; + SP->_function.index = GET_7XX(); + SP->_function.defined = TRUE; + + OBJECT_REF_CHECK(OP); + SP++; + + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_EXTERN: + + _push_extern(GET_UX()); + goto _NEXT; + +/*-----------------------------------------------*/ + + { + CLASS_VAR *NO_WARNING(var); + char *NO_WARNING(addr); + void *NO_WARNING(ref); + +_PUSH_DYNAMIC: + + var = &CP->load->dyn[GET_7XX()]; + + //if (OP == NULL) + // THROW_ILLEGAL(); + + ref = OP; + addr = &OP[var->pos]; + goto __READ; + +_PUSH_STATIC: + + var = &CP->load->stat[GET_7XX()]; + addr = (char *)CP->stat + var->pos; + ref = CP; + goto __READ; + +__READ: + + my_VALUE_class_read(CP, SP, addr, var->type, ref, PDS); + SP++; + goto _NEXT; + + +_POP_DYNAMIC: + + var = &CP->load->dyn[GET_7XX()]; + + //if (OP == NULL) + // THROW_ILLEGAL(); + + addr = &OP[var->pos]; + goto __WRITE; + +_POP_STATIC: + + var = &CP->load->stat[GET_7XX()]; + addr = (char *)CP->stat + var->pos; + goto __WRITE; + +__WRITE: + + VALUE_class_write(CP, &SP[-1], addr, var->type); + POP(); + + goto _NEXT; + + } + +/*-----------------------------------------------*/ + +_PUSH_CONST_EX: + + PC++; + ind = *PC; + goto _PUSH_CONSTANT; + +/*-----------------------------------------------*/ + +_PUSH_CONST: + + ind = GET_UXX(); + +_PUSH_CONSTANT: + + VALUE_class_constant_inline(CP, SP, ind); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_QUICK: + + SP->type = T_INTEGER; + SP->_integer.value = GET_XXX(); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_FLOAT: + + SP->type = T_FLOAT; + SP->_float.value = GET_XX(); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_ADD_QUICK: + + { + static void *_aq_jump[] = { + &&__AQ_VOID, &&__AQ_BOOLEAN, &&__AQ_BYTE, &&__AQ_SHORT, &&__AQ_INTEGER, &&__AQ_LONG, &&__AQ_SINGLE, &&__AQ_FLOAT, + &&__AQ_DATE, &&__AQ_STRING, &&__AQ_STRING, &&__AQ_POINTER, &&__AQ_VARIANT, &&__AQ_BOOLEAN, &&__AQ_BOOLEAN, &&__AQ_BOOLEAN + }; + + TYPE NO_WARNING(type); + void * NO_WARNING(jump_end); + + val = SP - 1; + + jump_end = &&_NEXT; + type = val->type; + ind = GET_XXX(); + + __AQ_JUMP: + + if (TYPE_is_object(type)) + goto __AQ_OBJECT; + else + goto *_aq_jump[type]; + + __AQ_VOID: + + THROW(E_NRETURN); + +#if DO_NOT_CHECK_OVERFLOW + + __AQ_BYTE: + + val->_integer.value = (uchar)(val->_integer.value + ind); + goto *jump_end; + + __AQ_SHORT: + + val->_integer.value = (short)(val->_integer.value + ind); + goto *jump_end; + + __AQ_INTEGER: + + val->_integer.value += ind; + goto *jump_end; + + __AQ_LONG: + + val->_long.value += (int64_t)ind; + goto *jump_end; + +#else + + __AQ_BYTE: + + { + uchar result; + + if (__builtin_add_overflow((uchar)val->_integer.value, ind, &result)) + THROW_OVERFLOW(); + val->_integer.value = result; + goto *jump_end; + } + + __AQ_SHORT: + + { + short result; + + if (__builtin_add_overflow((short)val->_integer.value, (short)ind, &result)) + THROW_OVERFLOW(); + val->_integer.value = result; + goto *jump_end; + } + + __AQ_INTEGER: + + if (__builtin_sadd_overflow(val->_integer.value, ind, &val->_integer.value)) + THROW_OVERFLOW(); + goto *jump_end; + + __AQ_LONG: + + if (__builtin_saddl_overflow(val->_long.value, (int64_t)ind, &val->_long.value)) + THROW_OVERFLOW(); + goto *jump_end; + +#endif + + __AQ_SINGLE: + + val->_single.value += (float)ind; + goto *jump_end; + + __AQ_DATE: + __AQ_STRING: + + VALUE_conv_float(val); + + __AQ_FLOAT: + + val->_float.value += (double)ind; + goto *jump_end; + + __AQ_POINTER: + + val->_pointer.value += ind; + goto *jump_end; + + __AQ_VARIANT: + + jump_end = &&__AQ_VARIANT_END; + VARIANT_undo(val); + type = val->type; + goto __AQ_JUMP; + + __AQ_OBJECT: + + if (EXEC_check_operator_single(val, CO_ADDF)) + { + EXEC_operator_object_add_quick(val, ind); + goto *jump_end; + } + + __AQ_BOOLEAN: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + + __AQ_VARIANT_END: + + VALUE_conv_variant(val); + goto _NEXT; + } + +/*-----------------------------------------------*/ + +_PUSH_ARRAY_NATIVE_INTEGER: + + { + CARRAY *array; + + val = &SP[-2]; + array = (CARRAY *)val->_object.object; + + if (!array) + THROW_NULL(); + + //VALUE_conv_integer(&SP[-1]); + ind = val[1]._integer.value; + + if (ind < 0 || ind >= array->count) + THROW_BOUND(); + + val->_integer.value = ((int *)(array->data))[ind]; + val->type = GB_T_INTEGER; + + OBJECT_UNREF(array); + SP--; + goto _NEXT; + } + +_PUSH_ARRAY_NATIVE_FLOAT: + + { + CARRAY *array; + + val = &SP[-2]; + array = (CARRAY *)val->_object.object; + + if (!array) + THROW_NULL(); + + //VALUE_conv_integer(&SP[-1]); + ind = val[1]._integer.value; + + if (ind < 0 || ind >= array->count) + THROW_BOUND(); + + val->_float.value = ((double *)(array->data))[ind]; + val->type = GB_T_FLOAT; + + OBJECT_UNREF(array); + SP--; + goto _NEXT; + } + +#if 0 +_PUSH_ARRAY_NATIVE_COLLECTION: + + { + GB_COLLECTION col; + + val = &SP[-2]; + col = (GB_COLLECTION)val->_object.object; + + if (!col) + THROW_NULL(); + + VALUE_conv_string(&val[1]); + GB_CollectionGet(col, val[1]._string.addr + val[1]._string.start, val[1]._string.len, (GB_VARIANT *)&SP[-2]); + + RELEASE_STRING(&val[1]); + SP--; + BORROW(&SP[-1]); + OBJECT_UNREF(col); + goto _NEXT; + } +#endif + +/*-----------------------------------------------*/ + +_POP_ARRAY_NATIVE_INTEGER: + + { + CARRAY *array; + + val = &SP[-2]; + array = (CARRAY *)val->_object.object; + + if (!array) + THROW_NULL(); + + CARRAY_check_not_read_only(array); + + ind = val[1]._integer.value; + if (ind < 0 || ind >= array->count) + THROW_BOUND(); + + ((int *)(array->data))[ind] = val[-1]._integer.value; + + OBJECT_UNREF(array); + SP -= 3; + goto _NEXT; + } + +_POP_ARRAY_NATIVE_FLOAT: + + { + CARRAY *array; + + val = &SP[-2]; + array = (CARRAY *)val->_object.object; + + if (!array) + THROW_NULL(); + + CARRAY_check_not_read_only(array); + + //VALUE_conv_float(&SP[-3]); + //VALUE_conv_integer(&SP[-1]); + + ind = val[1]._integer.value; + if (ind < 0 || ind >= array->count) + THROW_BOUND(); + + ((double *)(array->data))[ind] = SP[-3]._float.value; + + OBJECT_UNREF(array); + SP -= 3; + goto _NEXT; + } + +#if 0 +_POP_ARRAY_NATIVE_COLLECTION: + + { + GB_COLLECTION col; + + val = &SP[-2]; + col = (GB_COLLECTION)val->_object.object; + if (!col) + THROW_NULL(); + + VALUE_conv_variant(&val[-1]); + VALUE_conv_string(&val[1]); + + if (GB_CollectionSet((GB_COLLECTION)col, val[1]._string.addr + val[1]._string.start, val[1]._string.len, (GB_VARIANT *)&SP[-3])) + PROPAGATE(); + + RELEASE_STRING(&val[1]); + OBJECT_UNREF(col); + RELEASE_VARIANT(&val[-1]); + SP -= 3; + goto _NEXT; + } +#endif + +/*-----------------------------------------------*/ + +_ADD_INTEGER: + + SP--; +#if DO_NOT_CHECK_OVERFLOW + SP[-1]._integer.value += SP->_integer.value; +#else + if (__builtin_sadd_overflow(SP[-1]._integer.value, SP->_integer.value, &SP[-1]._integer.value)) + THROW_OVERFLOW(); +#endif + goto _NEXT; + +_ADD_FLOAT: + + SP--; + SP[-1]._float.value += SP->_float.value; + goto _NEXT; + +_SUB_INTEGER: + + SP--; +#if DO_NOT_CHECK_OVERFLOW + SP[-1]._integer.value -= SP->_integer.value; +#else + if (__builtin_ssub_overflow(SP[-1]._integer.value, SP->_integer.value, &SP[-1]._integer.value)) + THROW_OVERFLOW(); +#endif + goto _NEXT; + +_SUB_FLOAT: + + SP--; + SP[-1]._float.value -= SP->_float.value; + goto _NEXT; + +_MUL_INTEGER: + + SP--; +#if DO_NOT_CHECK_OVERFLOW + SP[-1]._integer.value *= SP->_integer.value; +#else + if (__builtin_smul_overflow(SP[-1]._integer.value, SP->_integer.value, &SP[-1]._integer.value)) + THROW_OVERFLOW(); +#endif + goto _NEXT; + +_MUL_FLOAT: + + SP--; + SP[-1]._float.value *= SP->_float.value; + goto _NEXT; + +_DIV_INTEGER: + + SP--; + VALUE_conv_float(&SP[-1]); + VALUE_conv_float(SP); + SP[-1]._float.value /= SP->_float.value; + if (!isfinite(SP[-1]._float.value)) + THROW_MATH(SP->_float.value == 0); + goto _NEXT; + +_DIV_FLOAT: + + SP--; + SP[-1]._float.value /= SP->_float.value; + if (!isfinite(SP[-1]._float.value)) + THROW_MATH(SP->_float.value == 0); + goto _NEXT; + +/*-----------------------------------------------*/ + +_TRY: + + EP = SP; + ET = EC; + EC = PC + (signed short)PC[1] + 2; + + #if DEBUG_ERROR + fprintf(stderr, "exec TRY %p\n", EC); + #endif + + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_END_TRY: + + #if DEBUG_ERROR + fprintf(stderr, "exec END TRY %p\n", PC); + #endif + + // If EP was reset to null, then an error occurred + EXEC_got_error = (EP == NULL); + EP = NULL; + EC = ET; + ET = NULL; + goto _NEXT; + +/*-----------------------------------------------*/ + +_CATCH: + + if (EC == NULL) + goto _NEXT; + else + goto __RETURN_VOID; + +/*-----------------------------------------------*/ + +_BREAK: + + if (!EXEC_trace && !EXEC_debug) + *PC = C_NOP; + else + DEBUG_breakpoint(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_QUIT: + + EXEC_quit(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_BYREF: + + if (PC == FP->code) + { + PC += GET_UX() + 2; + goto _MAIN; + } + + THROW(E_BYREF); + +/*-----------------------------------------------*/ + +_SUBR_COMPN: + + _SUBR_compe(code); + goto _NEXT; + +_SUBR_COMPE: + + _SUBR_compe(code); + goto _NEXT; + +#if 0 + { + static void *jump[] = { + &&__SC_VARIANT, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG, &&__SC_SINGLE, &&__SC_FLOAT, + &&__SC_DATE, &&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT, &&__SC_OBJECT_FLOAT, &&__SC_FLOAT_OBJECT, &&__SC_OBJECT_OTHER, &&__SC_OTHER_OBJECT, &&__SC_OBJECT_OBJECT, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG_NC, &&__SC_SINGLE_NC, &&__SC_FLOAT_NC, + &&__SC_DATE_NC, &&__SC_STRING_NC, &&__SC_STRING_NC, &&__SC_POINTER_NC, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT + }; + + char NO_WARNING(result); + VALUE *NO_WARNING(P1); + VALUE *NO_WARNING(P2); + +_SUBR_COMPN: + + result = 1; + goto _SUBR_COMP; + +_SUBR_COMPE: + + result = 0; + +_SUBR_COMP: + + P1 = SP - 2; + P2 = SP - 1; + + goto *jump[code & 0x3F]; + + __SC_BOOLEAN: + __SC_BYTE: + __SC_SHORT: + __SC_INTEGER: + + result ^= P1->_integer.value == P2->_integer.value; + goto __SC_END; + + __SC_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + __SC_LONG_NC: + + result ^= P1->_long.value == P2->_long.value; + goto __SC_END; + + __SC_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + __SC_DATE_NC: + + result ^= DATE_comp_value(P1, P2) == 0; + goto __SC_END; + + __SC_NULL: + + if (P2->type == T_NULL) + { + result ^= VALUE_is_null(P1); + goto __SC_END_RELEASE; + } + else if (P1->type == T_NULL) + { + result ^= VALUE_is_null(P2); + goto __SC_END_RELEASE; + } + + __SC_STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + __SC_STRING_NC: + + if (P1->_string.len == P2->_string.len) + result ^= STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __SC_END; + + __SC_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + __SC_SINGLE_NC: + + result ^= P1->_single.value == P2->_single.value; + goto __SC_END; + + __SC_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + __SC_FLOAT_NC: + + result ^= P1->_float.value == P2->_float.value; + goto __SC_END; + + __SC_POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + __SC_POINTER_NC: + + result ^= P1->_pointer.value == P2->_pointer.value; + goto __SC_END; + + __SC_OBJECT: + + result ^= OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SC_END_RELEASE; + + __SC_OBJECT_FLOAT: + + result ^= EXEC_comparator(OP_OBJECT_FLOAT, CO_EQUALF, P1, P2); + goto __SC_END; + + __SC_FLOAT_OBJECT: + + result ^= EXEC_comparator(OP_FLOAT_OBJECT, CO_EQUALF, P1, P2); + goto __SC_END; + + __SC_OBJECT_OTHER: + + result ^= EXEC_comparator(OP_OBJECT_OTHER, CO_EQUALO, P1, P2); + goto __SC_END; + + __SC_OTHER_OBJECT: + + result ^= EXEC_comparator(OP_OTHER_OBJECT, CO_EQUALO, P1, P2); + goto __SC_END; + + __SC_OBJECT_OBJECT: + + result ^= EXEC_comparator(OP_OBJECT_OBJECT, CO_EQUAL, P1, P2); + goto __SC_END; + + __SC_VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + code = EXEC_check_operator(P1, P2, CO_EQUAL); + if (code) + { + code += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= code; + goto *jump[code]; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + { + if (P1->type == P2->type) + *PC |= 0x20; + *PC |= type; + } + + goto *jump[type]; + } + + __SC_ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + + __SC_END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + + __SC_END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = -result; + SP--; + goto _NEXT; + } +#endif + +/*-----------------------------------------------*/ + + { + char NO_WARNING(result); + VALUE *NO_WARNING(P1); + VALUE *NO_WARNING(P2); + + static void *jump[] = { + &&__SCI_VARIANT, &&__SCI_BOOLEAN, &&__SCI_BYTE, &&__SCI_SHORT, &&__SCI_INTEGER, &&__SCI_LONG, &&__SCI_SINGLE, &&__SCI_FLOAT, + &&__SCI_DATE, &&__SCI_STRING, &&__SCI_STRING, &&__SCI_POINTER, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_NULL, + &&__SCI_OBJECT, &&__SCI_OBJECT_FLOAT, &&__SCI_FLOAT_OBJECT, &&__SCI_OBJECT_OTHER, &&__SCI_OTHER_OBJECT, &&__SCI_OBJECT_OBJECT, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, &&__SCI_BOOLEAN, &&__SCI_BYTE, &&__SCI_SHORT, &&__SCI_INTEGER, &&__SCI_LONG_NC, &&__SCI_SINGLE_NC, &&__SCI_FLOAT_NC, + &&__SCI_DATE_NC, &&__SCI_STRING_NC, &&__SCI_STRING_NC, &&__SCI_POINTER_NC, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_NULL, + &&__SCI_OBJECT + }; + +_SUBR_COMPGT: + + P1 = SP - 2; + P2 = SP - 1; + result = 0; + goto _SUBR_COMPI; + +_SUBR_COMPLT: + + P1 = SP - 1; + P2 = SP - 2; + result = 0; + goto _SUBR_COMPI; + +_SUBR_COMPLE: + + P1 = SP - 2; + P2 = SP - 1; + result = 1; + goto _SUBR_COMPI; + +_SUBR_COMPGE: + + P1 = SP - 1; + P2 = SP - 2; + result = 1; + goto _SUBR_COMPI; + +_SUBR_COMPI: + + goto *jump[code & 0x3F]; + + __SCI_BOOLEAN: + __SCI_BYTE: + __SCI_SHORT: + __SCI_INTEGER: + + result ^= P1->_integer.value > P2->_integer.value; + goto __SCI_END; + + __SCI_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + __SCI_LONG_NC: + + result ^= P1->_long.value > P2->_long.value; + goto __SCI_END; + + __SCI_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + __SCI_DATE_NC: + + result ^= DATE_comp_value(P1, P2) > 0; + goto __SCI_END; + + __SCI_NULL: + __SCI_STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + __SCI_STRING_NC: + + result ^= STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) > 0; + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __SCI_END; + + __SCI_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + __SCI_SINGLE_NC: + + result ^= P1->_single.value > P2->_single.value; + goto __SCI_END; + + __SCI_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + __SCI_FLOAT_NC: + + result ^= P1->_float.value > P2->_float.value; + goto __SCI_END; + + __SCI_POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + __SCI_POINTER_NC: + + result ^= P1->_pointer.value > P2->_pointer.value; + goto __SCI_END; + + __SCI_OBJECT: + + result ^= OBJECT_comp_value(P1, P2) > 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SCI_END_RELEASE; + + __SCI_OBJECT_FLOAT: + + result ^= EXEC_comparator(OP_OBJECT_FLOAT, CO_COMPF, P1, P2) > 0; + goto __SCI_END; + + __SCI_FLOAT_OBJECT: + + result ^= EXEC_comparator(OP_FLOAT_OBJECT, CO_COMPF, P1, P2) > 0; + goto __SCI_END; + + __SCI_OBJECT_OTHER: + + result ^= EXEC_comparator(OP_OBJECT_OTHER, CO_COMPO, P1, P2) > 0; + goto __SCI_END; + + __SCI_OTHER_OBJECT: + + result ^= EXEC_comparator(OP_OTHER_OBJECT, CO_COMPO, P1, P2) > 0; + goto __SCI_END; + + __SCI_OBJECT_OBJECT: + + result ^= EXEC_comparator(OP_OBJECT_OBJECT, CO_COMP, P1, P2) > 0; + goto __SCI_END; + + __SCI_VARIANT: + + { + bool variant = FALSE; + int op; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + op = EXEC_check_operator(P1, P2, CO_COMP); + if (op) + { + op += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= op; + goto *jump[op]; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW_TYPE(typem, type); + } + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + { + if (P1->type == P2->type) + *PC |= 0x20; + *PC |= type; + } + + goto *jump[type]; + } + + __SCI_ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + + __SCI_END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + + __SCI_END: + + SP -= 2; + SP->type = T_BOOLEAN; + SP->_boolean.value = -result; + SP++; + goto _NEXT; + } + + +/*-----------------------------------------------*/ + +_PUSH_VARIABLE: + + { + void *NO_WARNING(object); + CLASS_DESC *NO_WARNING(desc); + + val = &SP[-1]; + object = val->_object.object; + if (!object) + THROW_NULL(); + desc = val->_object.class->table[PC[1]].desc; + my_VALUE_class_read(desc->variable.class, val, (char *)object + desc->variable.offset, desc->variable.ctype, object, PV); + //BORROW(&SP[-1]); + OBJECT_UNREF(object); + } + goto _NEXT; + +_POP_VARIABLE: + + { + void *object = SP[-1]._object.object; + if (!object) + THROW_NULL(); + CLASS_DESC *desc = SP[-1]._object.class->table[PC[1]].desc; + VALUE_write(&SP[-2], (char *)object + desc->variable.offset, desc->variable.type); + RELEASE(&SP[-2]); + OBJECT_UNREF(object); + SP -= 2; + } + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_CONV: + + VALUE_conv(SP - 1, code & 0x3F); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_LEFT: + + SUBR_left(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_RIGHT: + + SUBR_right(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_MID: + + SUBR_mid(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_LEN: + + SUBR_len(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_ADD: + + _SUBR_add(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_SUB: + + _SUBR_sub(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_MUL: + + _SUBR_mul(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_DIV: + + _SUBR_div(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_POKE: + + SUBR_poke(code); + goto _NEXT; + +} + + + +#if 0 +static void _SUBR_compn(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT, + &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT + }; + + //static void *test[] = { &&__EQ, &&__NE, &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + goto *jump[code & 0x1F]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value == P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value == P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2) == 0; + goto __END; + +__NULL: + + if (P2->type == T_NULL) + { + result = VALUE_is_null(P1); + goto __END_RELEASE; + } + else if (P1->type == T_NULL) + { + result = VALUE_is_null(P2); + goto __END_RELEASE; + } + +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0; + else + result = STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + result = P1->_single.value == P2->_single.value; + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value == P2->_float.value; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value == P2->_pointer.value; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__OBJECT_FLOAT: + + result = EXEC_comparator(OP_OBJECT_FLOAT, CO_EQUALF, P1, P2); + goto __END; + +__FLOAT_OBJECT: + + result = EXEC_comparator(OP_FLOAT_OBJECT, CO_EQUALF, P1, P2); + goto __END; + +__OBJECT_OTHER: + + result = EXEC_comparator(OP_OBJECT_OTHER, CO_EQUALO, P1, P2); + goto __END; + +__OTHER_OBJECT: + + result = EXEC_comparator(OP_OTHER_OBJECT, CO_EQUALO, P1, P2); + goto __END; + +__OBJECT_OBJECT: + + result = EXEC_comparator(OP_OBJECT_OBJECT, CO_EQUAL, P1, P2); + goto __END; + +__VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + code = EXEC_check_operator(P1, P2, CO_EQUAL); + if (code) + { + code += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= code; + goto *jump[code]; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + P1->_boolean.value = result - 1; // ? 0 : -1; +} +#endif + +#define MANAGE_VARIANT_OBJECT(_func, _op, _opcode) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + if (P1->type == P2->type) \ + { \ + *PC |= 0x10; \ + if (!CP->not_3_18) \ + { \ + if (type == T_INTEGER) \ + *PC = _opcode##_INTEGER; \ + else if (type == T_FLOAT) \ + *PC = _opcode##_FLOAT; \ + } \ + } \ + goto *jump[type]; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + code += T_DATE; \ + if (!(P1->type == T_OBJECT || P2->type == T_OBJECT)) \ + *PC |= code; \ + goto *jump[code]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + (_func)(code + T_DATE); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + +#define MANAGE_VARIANT_POINTER_OBJECT(_func, _op, _opcode) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + *PC |= type; \ + if (P1->type == P2->type) \ + { \ + *PC |= 0x10; \ + if (!CP->not_3_18) \ + { \ + if (type == T_INTEGER) \ + *PC = _opcode##_INTEGER; \ + else if (type == T_FLOAT) \ + *PC = _opcode##_FLOAT; \ + } \ + } \ + goto *jump[type]; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + code += T_POINTER; \ + if (P1->type != T_OBJECT && P2->type != T_OBJECT) \ + *PC |= code; \ + goto *jump[code]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + (_func)(code + T_POINTER); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + + +NOINLINE static void _SUBR_add(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, NULL, NULL, &&__POINTER, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, + &&__OBJECT, && __BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG_NC, &&__SINGLE_NC, &&__FLOAT_NC, + &&__DATE, NULL, NULL, &&__POINTER_NC + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value | P2->_integer.value; SP--; return; + +#if DO_NOT_CHECK_OVERFLOW + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value + P2->_integer.value); SP--; return; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value + P2->_integer.value); SP--; return; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value += P2->_integer.value; SP--; return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + P1->_long.value += P2->_long.value; SP--; return; + +#else + +__BYTE: + + { + uchar result; + + if (__builtin_add_overflow((uchar)P1->_integer.value, (uchar)P2->_integer.value, &result)) + THROW_OVERFLOW(); + + P1->_integer.value = result; + P1->type = T_BYTE; + SP--; + return; + } + +__SHORT: + + { + short result; + + if (__builtin_add_overflow((short)P1->_integer.value, (short)P2->_integer.value, &result)) + THROW_OVERFLOW(); + + P1->_integer.value = result; + P1->type = T_SHORT; + SP--; + return; + } + +__INTEGER: + + if (__builtin_sadd_overflow(P1->_integer.value, P2->_integer.value, &P1->_integer.value)) + THROW_OVERFLOW(); + P1->type = T_INTEGER; + SP--; + return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + if (__builtin_saddl_overflow(P1->_long.value, P2->_long.value, &P1->_long.value)) + THROW_OVERFLOW(); + SP--; + return; + +#endif + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value += P2->_single.value; SP--; return; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value += P2->_float.value; SP--; return; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + +__POINTER_NC: + + P1->_pointer.value += (intptr_t)P2->_pointer.value; SP--; return; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_ADDF, P1, P2); SP--; return; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_ADDF, P1, P2); SP--; return; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_ADDO, P1, P2); SP--; return; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_ADDO, P1, P2); SP--; return; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_ADD, P1, P2); SP--; return; + +__VARIANT: + + MANAGE_VARIANT_POINTER_OBJECT(_SUBR_add, CO_ADD, C_ADD); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +NOINLINE static void _SUBR_sub(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, NULL, NULL, &&__POINTER, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, + &&__OBJECT, && __BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG_NC, &&__SINGLE_NC, &&__FLOAT_NC, + &&__DATE, NULL, NULL, &&__POINTER_NC + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value ^ P2->_integer.value; SP--; return; + +#if DO_NOT_CHECK_OVERFLOW + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value - P2->_integer.value); SP--; return; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value - P2->_integer.value); SP--; return; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value -= P2->_integer.value; SP--; return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + P1->_long.value -= P2->_long.value; SP--; return; + +#else + +__BYTE: + + { + uchar result; + + if (__builtin_sub_overflow((uchar)P1->_integer.value, (uchar)P2->_integer.value, &result)) + THROW_OVERFLOW(); + + P1->_integer.value = result; + P1->type = T_BYTE; + SP--; + return; + } + +__SHORT: + + { + short result; + + if (__builtin_sub_overflow((short)P1->_integer.value, (short)P2->_integer.value, &result)) + THROW_OVERFLOW(); + + P1->_integer.value = result; + P1->type = T_SHORT; + SP--; + return; + } + +__INTEGER: + + if (__builtin_ssub_overflow(P1->_integer.value, P2->_integer.value, &P1->_integer.value)) + THROW_OVERFLOW(); + P1->type = T_INTEGER; + SP--; + return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + if (__builtin_ssubl_overflow(P1->_long.value, P2->_long.value, &P1->_long.value)) + THROW_OVERFLOW(); + SP--; + return; + +#endif + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value -= P2->_single.value; SP--; return; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value -= P2->_float.value; SP--; return; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + +__POINTER_NC: + + P1->_pointer.value -= (intptr_t)P2->_pointer.value; SP--; return; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_SUBF, P1, P2); SP--; return; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_SUBF, P1, P2); SP--; return; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_SUBO, P1, P2); SP--; return; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_SUBO, P1, P2); SP--; return; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_SUB, P1, P2); SP--; return; + +__VARIANT: + + MANAGE_VARIANT_POINTER_OBJECT(_SUBR_sub, CO_SUB, C_SUB); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +NOINLINE static void _SUBR_mul(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__ERROR, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT, NULL, NULL, + NULL, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG_NC, &&__SINGLE_NC, &&__FLOAT_NC, &&__ERROR, + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value & P2->_integer.value; SP--; return; + +#if DO_NOT_CHECK_OVERFLOW + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value * P2->_integer.value); SP--; return; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value * P2->_integer.value); SP--; return; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value *= P2->_integer.value; SP--; return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + P1->_long.value *= P2->_long.value; SP--; return; + +#else + +__BYTE: + + { + uchar result; + + if (__builtin_mul_overflow((uchar)P1->_integer.value, (uchar)P2->_integer.value, &result)) + THROW_OVERFLOW(); + + P1->_integer.value = result; + P1->type = T_BYTE; + SP--; + return; + } + +__SHORT: + + { + short result; + + if (__builtin_mul_overflow((short)P1->_integer.value, (short)P2->_integer.value, &result)) + THROW_OVERFLOW(); + + P1->_integer.value = result; + P1->type = T_SHORT; + SP--; + return; + } + +__INTEGER: + + if (__builtin_smul_overflow(P1->_integer.value, P2->_integer.value, &P1->_integer.value)) + THROW_OVERFLOW(); + P1->type = T_INTEGER; + SP--; + return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + if (__builtin_smull_overflow(P1->_long.value, P2->_long.value, &P1->_long.value)) + THROW_OVERFLOW(); + SP--; + return; + +#endif + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value *= P2->_single.value; SP--; return; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value *= P2->_float.value; SP--; return; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_MULF, P1, P2); SP--; return; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_MULF, P1, P2); SP--; return; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_MULO, P1, P2); SP--; return; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_MULO, P1, P2); SP--; return; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_MUL, P1, P2); SP--; return; + +__VARIANT: + + MANAGE_VARIANT_OBJECT(_SUBR_mul, CO_MUL, C_MUL); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +NOINLINE static void _SUBR_div(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__ERROR, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT, NULL, NULL, + NULL, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE_NC, &&__FLOAT_NC, &&__ERROR, + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value /= P2->_float.value; + if (isfinite(P1->_float.value)) + { + SP--; + return; + } + + THROW_MATH(P2->_float.value == 0); + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value /= P2->_single.value; + if (isfinite(P1->_single.value)) + { + SP--; + return; + } + + THROW_MATH(P2->_single.value == 0); + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_DIVF, P1, P2); + goto __CHECK_OBJECT; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_DIVF, P1, P2); + goto __CHECK_OBJECT; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_DIVO, P1, P2); + goto __CHECK_OBJECT; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_DIVO, P1, P2); + goto __CHECK_OBJECT; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_DIV, P1, P2); + goto __CHECK_OBJECT; + +__VARIANT: + + MANAGE_VARIANT_OBJECT(_SUBR_div, CO_DIV, C_DIV); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__CHECK_OBJECT: + + if (P1->_object.object == NULL) + THROW(E_ZERO); + SP--; +} + +NOINLINE static void _SUBR_compe(ushort code) +{ + static void *jump[] = { + &&__SC_VARIANT, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG, &&__SC_SINGLE, &&__SC_FLOAT, + &&__SC_DATE, &&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT, &&__SC_OBJECT_FLOAT, &&__SC_FLOAT_OBJECT, &&__SC_OBJECT_OTHER, &&__SC_OTHER_OBJECT, &&__SC_OBJECT_OBJECT, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG_NC, &&__SC_SINGLE_NC, &&__SC_FLOAT_NC, + &&__SC_DATE_NC, &&__SC_STRING_NC, &&__SC_STRING_NC, &&__SC_POINTER_NC, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT + }; + + char result = code >= C_NE; + VALUE *P1; + VALUE *P2; + + P1 = SP - 2; + P2 = SP - 1; + + goto *jump[code & 0x3F]; + +__SC_BOOLEAN: +__SC_BYTE: +__SC_SHORT: +__SC_INTEGER: + + result ^= P1->_integer.value == P2->_integer.value; + goto __SC_END; + +__SC_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__SC_LONG_NC: + + result ^= P1->_long.value == P2->_long.value; + goto __SC_END; + +__SC_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + +__SC_DATE_NC: + + result ^= DATE_comp_value(P1, P2) == 0; + goto __SC_END; + +__SC_NULL: + + if (P2->type == T_NULL) + { + result ^= VALUE_is_null(P1); + goto __SC_END_RELEASE; + } + else if (P1->type == T_NULL) + { + result ^= VALUE_is_null(P2); + goto __SC_END_RELEASE; + } + +__SC_STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + +__SC_STRING_NC: + + if (P1->_string.len == P2->_string.len) + result ^= STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __SC_END; + +__SC_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SC_SINGLE_NC: + + result ^= P1->_single.value == P2->_single.value; + goto __SC_END; + +__SC_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__SC_FLOAT_NC: + + result ^= P1->_float.value == P2->_float.value; + goto __SC_END; + +__SC_POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + +__SC_POINTER_NC: + + result ^= P1->_pointer.value == P2->_pointer.value; + goto __SC_END; + +__SC_OBJECT: + + result ^= OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SC_END_RELEASE; + +__SC_OBJECT_FLOAT: + + result ^= EXEC_comparator(OP_OBJECT_FLOAT, CO_EQUALF, P1, P2); + goto __SC_END; + +__SC_FLOAT_OBJECT: + + result ^= EXEC_comparator(OP_FLOAT_OBJECT, CO_EQUALF, P1, P2); + goto __SC_END; + +__SC_OBJECT_OTHER: + + result ^= EXEC_comparator(OP_OBJECT_OTHER, CO_EQUALO, P1, P2); + goto __SC_END; + +__SC_OTHER_OBJECT: + + result ^= EXEC_comparator(OP_OTHER_OBJECT, CO_EQUALO, P1, P2); + goto __SC_END; + +__SC_OBJECT_OBJECT: + + result ^= EXEC_comparator(OP_OBJECT_OBJECT, CO_EQUAL, P1, P2); + goto __SC_END; + +__SC_VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + code = EXEC_check_operator(P1, P2, CO_EQUAL); + if (code) + { + code += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= code; + goto *jump[code]; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW_TYPE(T_OBJECT, Min(P1->type, P2->type)); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + { + if (P1->type == P2->type) + *PC |= 0x20; + *PC |= type; + } + + goto *jump[type]; + } + +__SC_ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + +__SC_END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__SC_END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = -result; + SP--; +} + +#if 0 +static void _SUBR_compi(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT, + &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT + }; + + static void *test[] = { &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + TYPE type; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value > P2->_integer.value ? 1 : P1->_integer.value < P2->_integer.value ? -1 : 0; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value > P2->_long.value ? 1 : P1->_long.value < P2->_long.value ? -1 : 0; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2); + goto __END; + +__NULL: +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + result = STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + result = P1->_single.value > P2->_single.value ? 1 : P1->_single.value < P2->_single.value ? -1 : 0; + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value > P2->_float.value ? 1 : P1->_float.value < P2->_float.value ? -1 : 0; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value > P2->_pointer.value ? 1 : P1->_pointer.value < P2->_pointer.value ? -1 : 0; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2); + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__OBJECT_FLOAT: + + result = EXEC_comparator(OP_OBJECT_FLOAT, CO_COMPF, P1, P2); + goto __END; + +__FLOAT_OBJECT: + + result = EXEC_comparator(OP_FLOAT_OBJECT, CO_COMPF, P1, P2); + goto __END; + +__OBJECT_OTHER: + + result = EXEC_comparator(OP_OBJECT_OTHER, CO_COMPO, P1, P2); + goto __END; + +__OTHER_OBJECT: + + result = EXEC_comparator(OP_OTHER_OBJECT, CO_COMPO, P1, P2); + goto __END; + +__OBJECT_OBJECT: + + result = EXEC_comparator(OP_OBJECT_OBJECT, CO_COMP, P1, P2); + goto __END; + +__VARIANT: + + { + bool variant = FALSE; + int op; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + op = EXEC_check_operator(P1, P2, CO_COMP); + if (op) + { + op += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= op; + goto *jump[op]; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + goto *test[(code >> 8) - (C_GT >> 8)]; + +__GT: + P1->_boolean.value = result > 0 ? -1 : 0; + return; + +__GE: + P1->_boolean.value = result >= 0 ? -1 : 0; + return; + +__LT: + P1->_boolean.value = result < 0 ? -1 : 0; + return; + +__LE: + P1->_boolean.value = result <= 0 ? -1 : 0; + return; +} +#endif + +void EXEC_push_array(ushort code) +{ + static const void *jump[] = { + &&__PUSH_GENERIC, &&__PUSH_GENERIC, &&__PUSH_GENERIC, &&__PUSH_GENERIC, + &&__PUSH_ARRAY, &&__PUSH_ARRAY, &&__PUSH_ARRAY, &&__PUSH_ARRAY, + &&__PUSH_NATIVE_ARRAY, NULL, &&__PUSH_NATIVE_ARRAY_SIMPLE, &&__PUSH_NATIVE_ARRAY_SIMPLE, + &&__PUSH_NATIVE_COLLECTION, &&__PUSH_NATIVE_ARRAY_INTEGER, &&__PUSH_NATIVE_ARRAY_FLOAT, &&__PUSH_NATIVE_STRING + }; + + CLASS *class; + OBJECT *object; + ushort np; + int i; + void *NO_WARNING(data); + bool defined; + VALUE *NO_WARNING(val); + uint fast; + CARRAY *array; + + goto *jump[(code & 0xF0) >> 4]; + +__PUSH_GENERIC: + + np = GET_3X(); + val = &SP[-np]; + np--; + + defined = EXEC_object(val, &class, &object); + + if (class->quick_array == CQA_STRING) + { + if (np < 1) + THROW(E_NEPARAM); + else if (np > 2) + THROW(E_TMPARAM); + + if (defined) + *PC = (*PC & 0xFF00) | (0xF1 + np); + + goto __PUSH_NATIVE_STRING; + } + + fast = 0x41 + np; + + if (defined) + { + if (class->quick_array == CQA_ARRAY) + { + if (np == 1) + { + array = (CARRAY *)object; + if (array->type == GB_T_INTEGER) + { + if (!CP->not_3_18 && SP[-1].type == T_INTEGER) + { + *PC = C_PUSH_ARRAY_NATIVE_INTEGER; + goto __PUSH_ARRAY_2; + } + + fast = 0xD0; + } + else if (array->type == GB_T_FLOAT) + { + if (!CP->not_3_18 && SP[-1].type == T_INTEGER) + { + *PC = C_PUSH_ARRAY_NATIVE_FLOAT; + goto __PUSH_ARRAY_2; + } + else + { + fast = 0xE0; + } + } + else if (TYPE_is_object(array->type)) + fast = 0xB0; + else + fast = 0xA0 + array->type; + } + else + { + if (np > MAX_ARRAY_DIM) + THROW(E_TMPARAM); + + fast = 0x81 + np; + } + } + else if (class->quick_array == CQA_COLLECTION) + { + if (np < 1) + THROW(E_NEPARAM); + else if (np > 1) + THROW(E_TMPARAM); + + if (TRUE) //CP->not_3_18) + { + fast = 0xC0; + } + else + { + *PC = C_PUSH_ARRAY_NATIVE_COLLECTION; + goto __PUSH_ARRAY_2; + } + } + else + { + // Check the symbol existance, but *not virtually* + if (object && !VALUE_is_super(val)) + { + CLASS *nvclass = val->_object.class; + + if (nvclass->special[SPEC_GET] == NO_SYMBOL) + THROW(E_NARRAY, CLASS_get_name(nvclass)); + } + } + } + + *PC = (*PC & 0xFF00) | fast; + + goto __PUSH_ARRAY_2; + +__PUSH_NATIVE_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + np--; + EXEC_object_array(val, class, object); + + for (i = 1; i <= np; i++) + VALUE_conv_integer(&val[i]); + + data = CARRAY_get_data_multi((CARRAY *)object, (GB_INTEGER *)&val[1], np - 1); + if (!data) + PROPAGATE(); + + VALUE_read(val, data, ((CARRAY *)object)->type); + goto __PUSH_NATIVE_END; + +__PUSH_NATIVE_COLLECTION: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + + VALUE_conv_string(&val[1]); + //fprintf(stderr, "GB_CollectionGet: %p '%.*s'\n", val[1]._string.addr, val[1]._string.len, val[1]._string.addr + val[1]._string.start); + GB_CollectionGet((GB_COLLECTION)object, val[1]._string.addr + val[1]._string.start, val[1]._string.len, (GB_VARIANT *)val); + + RELEASE_STRING(&val[1]); + goto __PUSH_NATIVE_END; + +__PUSH_NATIVE_STRING: + + BoxedString_get(GET_0X() - 1); + return; + +__PUSH_NATIVE_ARRAY_SIMPLE: + + val = &SP[-2]; + np = code & 0x1F; + + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + VALUE_conv_integer(&val[1]); + + data = CARRAY_get_data_throw(array, val[1]._integer.value); + + VALUE_read_inline_type(val, data, np, array->type, __PUSH_NATIVE_END_NOREF, __PUSH_NATIVE_END); + +__PUSH_NATIVE_ARRAY_INTEGER: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + VALUE_conv_integer(&val[1]); + i = val[1]._integer.value; + + if (i < 0 || i >= array->count) + THROW_BOUND(); + + val->_integer.value = ((int *)(array->data))[i]; + val->type = GB_T_INTEGER; + goto __PUSH_NATIVE_END_NOREF; + +__PUSH_NATIVE_ARRAY_FLOAT: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + VALUE_conv_integer(&val[1]); + i = val[1]._integer.value; + + if (i < 0 || i >= array->count) + THROW_BOUND(); + + val->_float.value = ((double *)(array->data))[i]; + val->type = GB_T_FLOAT; + goto __PUSH_NATIVE_END_NOREF; + +__PUSH_NATIVE_END_NOREF: + + SP = val + 1; + OBJECT_UNREF(object); + return; + +__PUSH_NATIVE_END: + + SP = val; + PUSH(); + OBJECT_UNREF(object); + return; + +__PUSH_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + np--; + defined = EXEC_object(val, &class, &object); + +__PUSH_ARRAY_2: + + if (EXEC_special(SPEC_GET, class, object, np, FALSE)) + THROW(E_NARRAY, CLASS_get_name(class)); + + OBJECT_UNREF(object); + SP--; + //SP[-1] = SP[0]; + VALUE_copy(&SP[-1], &SP[0]); + + if (!defined) + VALUE_conv_variant(&SP[-1]); +} + +void EXEC_pop_array(ushort code) +{ + static const void *jump[] = { + &&__POP_GENERIC, &&__POP_GENERIC, &&__POP_GENERIC, &&__POP_GENERIC, + &&__POP_ARRAY, &&__POP_ARRAY, &&__POP_ARRAY, &&__POP_ARRAY, + &&__POP_NATIVE_ARRAY, NULL, &&__POP_NATIVE_ARRAY_SIMPLE, &&__POP_NATIVE_ARRAY_SIMPLE, + &&__POP_NATIVE_COLLECTION, &&__POP_NATIVE_ARRAY_INTEGER, &&__POP_NATIVE_ARRAY_FLOAT, NULL + }; + + CLASS *class; + OBJECT *object; + ushort np; + int i; + void *data; + bool defined; + VALUE *NO_WARNING(val); + VALUE swap; + int fast; + CARRAY *array; + + goto *jump[(code & 0xF0) >> 4]; + +__POP_GENERIC: + + np = GET_3X(); + val = &SP[-np]; + + defined = EXEC_object(val, &class, &object); + + fast = 0x40 + np; + + if (defined) + { + if (class->quick_array == CQA_ARRAY) + { + if (np == 2) + { + array = (CARRAY *)object; + if (array->type == GB_T_INTEGER) + { + if (!CP->not_3_18 && SP[-1].type == T_INTEGER && SP[-3].type == T_INTEGER) + { + *PC = C_POP_ARRAY_NATIVE_INTEGER; + goto __POP_ARRAY_2; + } + else + { + fast = 0xD0; + } + } + else if (array->type == GB_T_FLOAT) + { + if (!CP->not_3_18 && SP[-1].type == T_INTEGER && SP[-3].type == T_FLOAT) + { + *PC = C_POP_ARRAY_NATIVE_FLOAT; + goto __POP_ARRAY_2; + } + else + { + fast = 0xE0; + } + } + else if (TYPE_is_object(array->type)) + fast = 0xB0; + else + fast = 0xA0 + array->type; + } + else + { + if (np > (MAX_ARRAY_DIM + 1)) + THROW(E_TMPARAM); + + fast = 0x80 + np; + } + } + else if (class->quick_array == CQA_COLLECTION) + { + if (np < 2) + THROW(E_NEPARAM); + else if (np > 2) + THROW(E_TMPARAM); + + if (TRUE) //CP->not_3_18) + { + fast = 0xC0; + } + else + { + *PC = C_POP_ARRAY_NATIVE_COLLECTION; + goto __POP_ARRAY_2; + } + } + else + { + // Check the symbol existance, but *not virtually* + if (object && !VALUE_is_super(val)) + { + CLASS *nvclass = val->_object.class; + + if (nvclass->special[SPEC_PUT] == NO_SYMBOL) + THROW(E_NARRAY, CLASS_get_name(nvclass)); + } + } + } + + *PC = (*PC & 0xFF00) | fast; + + goto __POP_ARRAY_2; + +__POP_NATIVE_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + CARRAY_check_not_read_only(array); + + for (i = 1; i < np; i++) + VALUE_conv_integer(&val[i]); + + data = CARRAY_get_data_multi((CARRAY *)object, (GB_INTEGER *)&val[1], np - 2); + if (data == NULL) + PROPAGATE(); + + VALUE_write(&val[-1], data, array->type); + goto __POP_NATIVE_END; + +__POP_NATIVE_COLLECTION: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + + VALUE_conv_variant(&val[-1]); + VALUE_conv_string(&val[1]); + + if (GB_CollectionSet((GB_COLLECTION)object, val[1]._string.addr + val[1]._string.start, val[1]._string.len, (GB_VARIANT *)&val[-1])) + PROPAGATE(); + + RELEASE_STRING(&val[1]); + OBJECT_UNREF(object) + RELEASE_VARIANT(&val[-1]); + SP -= 3; + return; + +__POP_NATIVE_ARRAY_SIMPLE: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + CARRAY_check_not_read_only(array); + + VALUE_conv_integer(&val[1]); + + data = CARRAY_get_data(array, val[1]._integer.value); + if (data == NULL) + PROPAGATE(); + + VALUE_write(&val[-1], data, array->type); + goto __POP_NATIVE_END; + +__POP_NATIVE_ARRAY_INTEGER: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + CARRAY_check_not_read_only(array); + + VALUE_conv_integer(&val[-1]); + VALUE_conv_integer(&val[1]); + + i = val[1]._integer.value; + if (i < 0 || i >= array->count) + THROW_BOUND(); + + ((int *)(array->data))[i] = val[-1]._integer.value; + goto __POP_NATIVE_FAST_END; + +__POP_NATIVE_ARRAY_FLOAT: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + CARRAY_check_not_read_only(array); + + VALUE_conv_float(&val[-1]); + VALUE_conv_integer(&val[1]); + + i = val[1]._integer.value; + if (i < 0 || i >= array->count) + THROW_BOUND(); + + ((double *)(array->data))[i] = val[-1]._float.value; + +__POP_NATIVE_FAST_END: + + SP = val + 1; + OBJECT_UNREF(object); + SP -= 2; + return; + +__POP_NATIVE_END: + + SP = val + 1; + OBJECT_UNREF(object); + RELEASE(SP - 2); + SP -= 2; + //OBJECT_UNREF(object); + return; + +__POP_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + + defined = EXEC_object(val, &class, &object); + +__POP_ARRAY_2: + + /* swap object and value to be inserted */ + VALUE_copy(&swap, &val[0]); + VALUE_copy(&val[0], &val[-1]); + VALUE_copy(&val[-1], &swap); + + if (EXEC_special(SPEC_PUT, class, object, np, TRUE)) + THROW(E_NARRAY, CLASS_get_name(class)); + + POP(); /* free the object */ +} + + +void EXEC_quit(ushort code) +{ + switch(code & 3) + { + case 0: // QUIT + EXEC_do_quit(); + break; + + case 1: // STOP + if (EXEC_debug && CP) // && CP->component == COMPONENT_main) + DEBUG.Breakpoint(0); + break; + + case 2: // STOP EVENT + GAMBAS_StopEvent = TRUE; + break; + + case 3: // QUIT + VALUE_conv(&SP[-1], T_BYTE); + SP--; + EXEC_quit_value = (uchar)SP->_integer.value; + EXEC_do_quit(); + break; + } +} + + +NOINLINE static void SUBR_left(ushort code) +{ + int val; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + if (val < 0) + val += PARAM->_string.len; + + PARAM->_string.len = MinMax(val, 0, PARAM->_string.len); + } + + SP -= NPARAM; + SP++; +} + +NOINLINE static void SUBR_mid(ushort code) +{ + int start; + int len; + bool null; + + SUBR_ENTER(); + + null = SUBR_check_string(PARAM); + + VALUE_conv_integer(&PARAM[1]); + start = PARAM[1]._integer.value - 1; + + if (start < 0) + THROW(E_ARG); + + if (null) + goto _SUBR_MID_FIN; + + if (start >= PARAM->_string.len) + { + VOID_STRING(PARAM); + goto _SUBR_MID_FIN; + } + + if (NPARAM == 2) + len = PARAM->_string.len; + else + { + VALUE_conv_integer(&PARAM[2]); + len = PARAM[2]._integer.value; + } + + if (len < 0) + len = Max(0, PARAM->_string.len - start + len); + + len = MinMax(len, 0, PARAM->_string.len - start); + + if (len == 0) + { + RELEASE_STRING(PARAM); + PARAM->_string.addr = NULL; + PARAM->_string.start = 0; + } + else + PARAM->_string.start += start; + + PARAM->_string.len = len; + +_SUBR_MID_FIN: + + SP -= NPARAM; + SP++; +} + +NOINLINE static void SUBR_right(ushort code) +{ + int val; + int new_len; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + if (val < 0) + val += PARAM->_string.len; + + new_len = MinMax(val, 0, PARAM->_string.len); + + PARAM->_string.start += PARAM->_string.len - new_len; + PARAM->_string.len = new_len; + } + + SP -= NPARAM; + SP++; +} + + +NOINLINE static void SUBR_len(void) +{ + int len; + + SUBR_GET_PARAM(1); + + if (SUBR_check_string(PARAM)) + len = 0; + else + len = PARAM->_string.len; + + RELEASE_STRING(PARAM); + + PARAM->type = T_INTEGER; + PARAM->_integer.value = len; +} diff --git a/main/gbx/gbx_exec_operator.c b/main/gbx/gbx_exec_operator.c new file mode 100644 index 00000000..5666ba3f --- /dev/null +++ b/main/gbx/gbx_exec_operator.c @@ -0,0 +1,368 @@ +/*************************************************************************** + + gbx_exec_operator.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXEC_OPERATOR_C + +#include "gb_common.h" +#include "gbx_type.h" +#include "gbx_api.h" +#include "gbx_exec.h" + +typedef + void *(*FUNC_O_OF)(void *, double, bool); + +typedef + void *(*FUNC_O_OO)(void *, void *, bool); + +typedef + int (*FUNC_I_OF)(void *, double, bool); + +typedef + int (*FUNC_I_OO)(void *, void *, bool); + +typedef + void *(*FUNC_O_O)(void *); + +typedef + int (*FUNC_I_O)(void *); + +typedef + double (*FUNC_F_O)(void *); + +static void raise_error(void *o1, void *o2) +{ + if (o2 && OBJECT_class(o2) == OBJECT_class(o1)) + GB_Error((char *)E_TYPE, "Number", TYPE_get_name((TYPE)OBJECT_class(o1))); + else + GB_Error((char *)E_TYPE, TYPE_get_name((TYPE)OBJECT_class(o1)), o2 ? TYPE_get_name((TYPE)OBJECT_class(o2)) : "Number"); +} + +bool EXEC_check_operator_single(VALUE *P1, uchar op) +{ + return (TYPE_is_object(P1->type) && P1->_object.object && OBJECT_class(P1->_object.object)->has_operators + && CLASS_has_operator(OBJECT_class(P1->_object.object), op)); +} + +int EXEC_check_operator(VALUE *P1, VALUE *P2, uchar op) +{ + CLASS *class1, *class2; + + if (TYPE_is_number(P1->type) && TYPE_is_object(P2->type)) + { + if (P2->_object.object && OBJECT_class(P2->_object.object)->has_operators && CLASS_has_operator(OBJECT_class(P2->_object.object), op + 1)) + { + //*dynamic = P2->type == T_OBJECT; + return OP_FLOAT_OBJECT; + } + } + else if (TYPE_is_number(P2->type) && TYPE_is_object(P1->type)) + { + if (P1->_object.object && OBJECT_class(P1->_object.object)->has_operators && CLASS_has_operator(OBJECT_class(P1->_object.object), op + 1)) + { + //*dynamic = P1->type == T_OBJECT; + return OP_OBJECT_FLOAT; + } + } + else if (TYPE_are_objects(P1->type, P2->type) && OBJECT_are_not_null(P1->_object.object, P2->_object.object)) + { + class1 = OBJECT_class(P1->_object.object); + class2 = OBJECT_class(P2->_object.object); + + //*dynamic = P1->type == T_OBJECT || P2->type = T_OBJECT; + + if (class1->has_operators) + { + if (class1 == class2 && CLASS_has_operator(class1, op)) + return OP_OBJECT_OBJECT; + + if (class2->has_operators) + { + if (CLASS_get_operator_strength(class1) > CLASS_get_operator_strength(class2) && CLASS_has_operator(class1, op + 2)) + return OP_OBJECT_OTHER; + else if (CLASS_has_operator(class2, op + 2)) + return OP_OTHER_OBJECT; + } + else if (CLASS_has_operator(class1, op)) + return OP_OBJECT_OTHER; + } + else if (class2->has_operators && CLASS_has_operator(class2, op + 2)) + return OP_OTHER_OBJECT; + } + + return OP_NOTHING; +} + +void EXEC_operator(uchar what, uchar op, VALUE *P1, VALUE *P2) +{ + static void *jump[] = { NULL, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT }; + + void *func; + void *result; + bool invert; + void *o1, *o2; + + goto *jump[what]; + +__OBJECT_FLOAT: + + o1 = P1->_object.object; + if (!o1) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + VALUE_conv_float(P2); + result = (*(FUNC_O_OF)func)(o1, P2->_float.value, FALSE); + OBJECT_REF_CHECK(result); + OBJECT_UNREF(o1); + + if (!result) + { + if (op != CO_DIVF) + raise_error(o1, NULL); + } + + goto __END; + +__FLOAT_OBJECT: + + o1 = P2->_object.object; + if (!o1) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + VALUE_conv_float(P1); + result = (*(FUNC_O_OF)func)(o1, P1->_float.value, TRUE); + OBJECT_REF_CHECK(result); + P1->_object.class = P2->_object.class; + OBJECT_UNREF(o1); + + if (!result && !EXEC_has_native_error()) + raise_error(o1, NULL); + + goto __END; + +__OTHER_OBJECT: + + o2 = P1->_object.object; + o1 = P2->_object.object; + + invert = TRUE; + goto __OTHER; + +__OBJECT_OTHER: +__OBJECT_OBJECT: + + o1 = P1->_object.object; + o2 = P2->_object.object; + + invert = FALSE; + goto __OTHER; + +__OTHER: + + if (!OBJECT_are_not_null(o1, o2)) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + result = (*(FUNC_O_OO)func)(o1, o2, invert); + OBJECT_REF_CHECK(result); + OBJECT_UNREF(o1); + OBJECT_UNREF(o2); + + if (!result && !EXEC_has_native_error()) + raise_error(o1, o2); + +__END: + + P1->_object.object = result; + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + +void EXEC_operator_object_add_quick(VALUE *P1, double val) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[CO_ADDF]; + void *result = (*(FUNC_O_OF)func)(P1->_object.object, val, FALSE); + OBJECT_REF_CHECK(result); + OBJECT_UNREF(P1->_object.object); + P1->_object.object = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + + +int EXEC_comparator(uchar what, uchar op, VALUE *P1, VALUE *P2) +{ + static void *jump[] = { NULL, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT }; + + void *func; + int result; + bool invert; + void *o1, *o2; + + goto *jump[what]; + +__OBJECT_FLOAT: + + o1 = P1->_object.object; + if (!o1) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + VALUE_conv_float(P2); + result = (*(FUNC_I_OF)func)(o1, P2->_float.value, FALSE); + OBJECT_UNREF(o1); + + if (result < (-1)) + raise_error(o1, NULL); + + goto __END; + +__FLOAT_OBJECT: + + o2 = P2->_object.object; + if (!o2) + THROW_NULL(); + + func = OBJECT_class(o2)->operators[op]; + VALUE_conv_float(P1); + result = (*(FUNC_I_OF)func)(o2, P1->_float.value, TRUE); + OBJECT_UNREF(o2); + + if (result < (-1)) + raise_error(o2, NULL); + + goto __END; + +__OTHER_OBJECT: + + o2 = P1->_object.object; + o1 = P2->_object.object; + invert = TRUE; + goto __OTHER; + +__OBJECT_OTHER: +__OBJECT_OBJECT: + + o1 = P1->_object.object; + o2 = P2->_object.object; + invert = FALSE; + goto __OTHER; + +__OTHER: + + if (!OBJECT_are_not_null(o1, o2)) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + result = (*(FUNC_I_OO)func)(o1, o2, invert); + OBJECT_UNREF(o1); + OBJECT_UNREF(o2); + //result = !!result; // result != 0; + + if (result < (-1)) + raise_error(o1, o2); + +__END: + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } + + return result; +} + +void EXEC_operator_object_sgn(VALUE *P1) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[CO_SGN]; + int result = (*(FUNC_I_O)func)(P1->_object.object); + OBJECT_UNREF(P1->_object.object); + P1->type = T_INTEGER; + P1->_integer.value = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + +void EXEC_operator_object_fabs(VALUE *P1) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[CO_FABS]; + double result = (*(FUNC_F_O)func)(P1->_object.object); + OBJECT_UNREF(P1->_object.object); + P1->type = T_FLOAT; + P1->_float.value = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + +void EXEC_operator_object_single(uchar op, VALUE *P1) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[op]; + void *result = (*(FUNC_O_O)func)(P1->_object.object); + OBJECT_REF_CHECK(result); + OBJECT_UNREF(P1->_object.object); + P1->_object.object = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} diff --git a/main/gbx/gbx_exec_pop.c b/main/gbx/gbx_exec_pop.c new file mode 100644 index 00000000..00189db8 --- /dev/null +++ b/main/gbx/gbx_exec_pop.c @@ -0,0 +1,304 @@ +/*************************************************************************** + + gbx_exec_pop.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_limit.h" +#include "gbx_exec.h" + +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_c_array.h" +#include "gbx_c_collection.h" +#include "gbx_api.h" +#include "gbx_struct.h" + + +void EXEC_pop_unknown(void) +{ + static void *jump[] = { + /* 0 */ &&_POP_GENERIC, + /* 1 */ &&_POP_VARIABLE, + /* 2 */ &&_POP_STATIC_VARIABLE, + /* 3 */ &&_POP_PROPERTY, + /* 4 */ &&_POP_VARIABLE_AUTO, + /* 5 */ &&_POP_PROPERTY_AUTO, + /* 6 */ &&_POP_STRUCT_FIELD + }; + + const char *name; + int index; + CLASS_DESC *desc; + CLASS *class; + OBJECT *object; + char *addr; + bool defined; + VALUE *val; + + defined = EXEC_object(&SP[-1], &class, &object); + + /*printf("> exec_pop_unknown: SP = %p -> %p\n", SP, SP->_string.addr);*/ + + goto *jump[*PC & 0xF]; + + +_POP_GENERIC: + + name = CP->load->unknown[PC[1]]; + + // The first time we access a symbol, we must not be virtual to find it + val = &SP[-1]; + if (defined && object && !VALUE_is_super(val)) + index = CLASS_find_symbol(val->_object.class, name); + else + index = CLASS_find_symbol(class, name); + + if (index == NO_SYMBOL) + { + if (class->special[SPEC_UNKNOWN] == NO_SYMBOL) + { + if (defined && object && !VALUE_is_super(val)) + class = val->_object.class; + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + goto _POP_UNKNOWN_PROPERTY; + } + + desc = class->table[index].desc; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_CONSTANT: + + THROW(E_NPROPERTY, CLASS_get_name(class), name); + + case CD_VARIABLE: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 4; + } + else + { + if (defined) + { + if (!CP->not_3_18 && TYPE_is_pure_object(val->type) && !VALUE_is_super(val) && class->is_simple) + *PC = C_POP_VARIABLE; + else + *PC |= 1; + } + } + + if (defined) + PC[1] = index; + + goto _POP_VARIABLE_2; + + case CD_STRUCT_FIELD: + + if (object == NULL) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + + if (defined) + { + *PC |= 6; + PC[1] = index; + } + + /*if (desc->variable.ctype.id == TC_STRUCT || desc->variable.ctype.id == TC_ARRAY) + THROW(E_NWRITE, CLASS_get_name(class), name);*/ + + goto _POP_STRUCT_FIELD_2; + + case CD_STATIC_VARIABLE: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) + { + *PC |= 2; + PC[1] = index; + } + + goto _POP_STATIC_VARIABLE_2; + + case CD_PROPERTY: + case CD_PROPERTY_WRITE: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 5; + } + else + { + if (defined) *PC |= 3; + } + + if (defined) + PC[1] = index; + + goto _POP_PROPERTY_2; + + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_WRITE: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) *PC |= 3; + + if (defined) + PC[1] = index; + + goto _POP_PROPERTY_2; + + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + + THROW(E_NWRITE, CLASS_get_name(class), name); + + case CD_METHOD: + case CD_STATIC_METHOD: + + THROW(E_NPROPERTY, CLASS_get_name(class), name); + + default: + + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + + +_POP_VARIABLE_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_POP_VARIABLE: + + desc = class->table[PC[1]].desc; + +_POP_VARIABLE_2: + + addr = (char *)object + desc->variable.offset; + VALUE_write(&SP[-2], (void *)addr, desc->variable.type); + goto _FIN; + + +_POP_STATIC_VARIABLE: + + desc = class->table[PC[1]].desc; + +_POP_STATIC_VARIABLE_2: + + addr = (char *)desc->variable.class->stat + desc->variable.offset; + VALUE_write(&SP[-2], (void *)addr, desc->variable.type); + goto _FIN; + + +_POP_STRUCT_FIELD: + + desc = class->table[PC[1]].desc; + +_POP_STRUCT_FIELD_2: + + if (((CSTRUCT *)object)->ref) + addr = (char *)((CSTATICSTRUCT *)object)->addr + desc->variable.offset; + else + addr = (char *)object + sizeof(CSTRUCT) + desc->variable.offset; + + VALUE_class_write(class, &SP[-2], addr, desc->variable.ctype); + //VALUE_write(&SP[-2], (void *)addr, desc->variable.type); + goto _FIN; + + +_POP_PROPERTY_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_POP_PROPERTY: + + desc = class->table[PC[1]].desc; + +_POP_PROPERTY_2: + + VALUE_conv(&SP[-2], desc->property.type); + + if (desc->property.native) + { + if (EXEC_call_native(desc->property.write, object, 0, &SP[-2])) + PROPAGATE(); + } + else + { + *SP = SP[-2]; + PUSH(); + + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 1; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.write; + + EXEC_function(); + } + + goto _FIN; + +_POP_UNKNOWN_PROPERTY: + + if (class->special[SPEC_PROPERTY] == NO_SYMBOL) + goto _NOT_A_PROPERTY; + + EXEC_unknown_name = name; + if (EXEC_special(SPEC_PROPERTY, class, class->property_static ? NULL : object, 0, FALSE)) + goto _NOT_A_PROPERTY; + + VALUE_conv_boolean(&SP[-1]); + SP--; + if (!SP->_boolean.value) + goto _NOT_A_PROPERTY; + + EXEC_unknown_name = name; + + *SP = SP[-2]; + PUSH(); + + EXEC_special(SPEC_UNKNOWN, class, class->unknown_static ? NULL : object, 1, TRUE); + goto _FIN; + +_NOT_A_PROPERTY: + + THROW(E_NPROPERTY, CLASS_get_name(class), name); + +_FIN: + + RELEASE(&SP[-2]); + OBJECT_UNREF(object); + SP -= 2; + PC++; +} diff --git a/main/gbx/gbx_exec_push.c b/main/gbx/gbx_exec_push.c new file mode 100644 index 00000000..bc75d414 --- /dev/null +++ b/main/gbx/gbx_exec_push.c @@ -0,0 +1,590 @@ +/*************************************************************************** + + gbx_exec_push.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_limit.h" +#include "gbx_exec.h" +#include "gb_pcode.h" +#include "gbx_api.h" + +#include "gbx_string.h" +#include "gbx_c_array.h" +#include "gbx_c_collection.h" +#include "gbx_api.h" +#include "gbx_struct.h" +#include "gbx_local.h" + +void EXEC_push_unknown(void) +{ + static void *jump[] = { + &&_PUSH_GENERIC, // 0 + &&_PUSH_CONSTANT, // 1 + &&_PUSH_VARIABLE, // 2 + &&_PUSH_STATIC_VARIABLE, // 3 + &&_PUSH_PROPERTY, // 4 + &&_PUSH_METHOD, // 5 + &&_PUSH_STATIC_METHOD, // 6 + &&_PUSH_VARIABLE_AUTO, // 7 + &&_PUSH_PROPERTY_AUTO, // 8 + &&_PUSH_METHOD_AUTO, // 9 + &&_PUSH_EXTERN, // 10 + &&_PUSH_STRUCT_FIELD, // 11 + &&_PUSH_CONST_STRING, // 12 + }; + + const char *name; + int index; + CLASS_DESC *desc; + CLASS *class; + OBJECT *object; + void *ref; + char *addr; + bool defined; + VALUE *val; + + // EXEC_object can change *PC by calling EXEC_push_unknown() recursively + // So don't store *PC anywhere + + defined = EXEC_object(&SP[-1], &class, &object); + + goto *jump[*PC & 0xF]; + + +_PUSH_GENERIC: + + name = CP->load->unknown[PC[1]]; + + // The first time we access a symbol, we must not be virtual to find it + val = &SP[-1]; + if (defined && object && !VALUE_is_super(val)) + index = CLASS_find_symbol(val->_object.class, name); + else + index = CLASS_find_symbol(class, name); + + if (index == NO_SYMBOL) + { + //index = CLASS_find_symbol(class, name); + + if (class->special[SPEC_UNKNOWN] == NO_SYMBOL) + { + if (defined && object && !VALUE_is_super(val)) + class = val->_object.class; + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + + if (class->unknown_static && object) + THROW(E_STATIC, CLASS_get_name(class), name); + else if (!class->unknown_static && object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + } + + index = CLASS_find_symbol(class, name); + if (index != NO_SYMBOL) + THROW(E_NSYMBOL, CLASS_get_name(val->_object.class), name); + + if (class->special[SPEC_PROPERTY] != NO_SYMBOL) + { + EXEC_unknown_name = name; + if (!EXEC_special(SPEC_PROPERTY, class, class->property_static ? NULL : object, 0, FALSE)) + { + VALUE_conv_boolean(&SP[-1]); + SP--; + if (SP->_boolean.value) + { + //SP--; Keep the object on the stack so that it is automatically freed if an error is raised + EXEC_special(SPEC_UNKNOWN, class, class->unknown_static ? NULL : object, 0, FALSE); + VALUE_conv_variant(&SP[-1]); + SP--; + SP[-1] = *SP; + OBJECT_UNREF(object); + goto _FIN; + } + } + } + + goto _PUSH_UNKNOWN_METHOD; + } + + desc = class->table[index].desc; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_CONSTANT: + + if (TYPE_is_string(desc->constant.type)) + { + if (defined) + { + *PC |= 12; + PC[1] = index; + } + + goto _PUSH_CONST_STRING_2; + } + else + { + if (defined) + { + if ((PC[-1] & 0xF800) == C_PUSH_CLASS) + { + if (desc->constant.type == T_BOOLEAN) + { + PC[-1] = C_PUSH_MISC | (desc->constant.value._integer ? CPM_TRUE : CPM_FALSE); + PC[0] = C_NOP; + PC[1] = C_NOP; + goto _PUSH_CONSTANT_2; + } + else if (desc->constant.type == T_BYTE || desc->constant.type == T_SHORT) + { + PC[-1] = C_PUSH_INTEGER; + PC[0] = desc->constant.value._integer; + PC[1] = (CODE_CONV << 8) | desc->constant.type; + goto _PUSH_CONSTANT_2; + } + else if (desc->constant.type == T_INTEGER) + { + PC[-1] = C_PUSH_LONG; + PC[0] = desc->constant.value._integer & 0xFFFF; + PC[1] = desc->constant.value._integer >> 16; + goto _PUSH_CONSTANT_2; + } + } + + *PC |= 1; + + PC[1] = index; + } + + goto _PUSH_CONSTANT_2; + } + + case CD_VARIABLE: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 7; + } + else + { + if (defined) + { + if (!CP->not_3_18 && TYPE_is_pure_object(val->type) && !VALUE_is_super(val) && class->is_simple) + *PC = C_PUSH_VARIABLE; + else + *PC |= 2; + } + } + + if (defined) + PC[1] = index; + + goto _PUSH_VARIABLE_2; + + case CD_STRUCT_FIELD: + + if (object == NULL) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + + if (defined) + { + *PC |= 11; + PC[1] = index; + } + + goto _PUSH_STRUCT_FIELD_2; + + case CD_STATIC_VARIABLE: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) + *PC |= 3; + + if (defined) + PC[1] = index; + + goto _PUSH_STATIC_VARIABLE_2; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 8; + } + else + { + if (defined) *PC |= 4; + } + + if (defined) + PC[1] = index; + + goto _PUSH_PROPERTY_2; + + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) *PC |= 4; + + if (defined) + PC[1] = index; + + goto _PUSH_PROPERTY_2; + + case CD_METHOD: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 9; + } + else + { + if (defined) *PC |= 5; + } + + if (defined) + PC[1] = index; + + goto _PUSH_METHOD_2; + + case CD_STATIC_METHOD: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + if (defined) *PC |= 6; + + if (defined) + PC[1] = index; + + goto _PUSH_METHOD_2; + + case CD_EXTERN: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + if (defined) *PC |= 10; + + if (defined) + PC[1] = index; + + goto _PUSH_EXTERN_2; + + case CD_PROPERTY_WRITE: + case CD_STATIC_PROPERTY_WRITE: + + THROW(E_NREAD, CLASS_get_name(class), name); + + default: + + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + + +_PUSH_CONSTANT: + + desc = class->table[PC[1]].desc; + +_PUSH_CONSTANT_2: + + VALUE_read(&SP[-1], (void *)&desc->constant.value, desc->constant.type); + goto _FIN_DEFINED; + + +_PUSH_CONST_STRING: + + desc = class->table[PC[1]].desc; + +_PUSH_CONST_STRING_2: + + SP--; + SP->type = T_CSTRING; + + if (!desc->constant.value._string) + { + SP->_string.addr = NULL; + SP->_string.len = 0; + } + else + { + if (desc->constant.translate) + SP->_string.addr = (char *)LOCAL_gettext(desc->constant.value._string); + else + SP->_string.addr = (char *)desc->constant.value._string; + + SP->_string.len = strlen(SP->_string.addr); + } + + SP->_string.start = 0; + SP++; + goto _FIN_DEFINED_NO_BORROW; + + +_PUSH_VARIABLE_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_PUSH_VARIABLE: + + desc = class->table[PC[1]].desc; + +_PUSH_VARIABLE_2: + + addr = (char *)object + desc->variable.offset; + ref = object; + goto _READ_VARIABLE; + + +_PUSH_STATIC_VARIABLE: + + desc = class->table[PC[1]].desc; + +_PUSH_STATIC_VARIABLE_2: + + addr = (char *)desc->variable.class->stat + desc->variable.offset; + ref = desc->variable.class; + goto _READ_VARIABLE; + + +_PUSH_STRUCT_FIELD: + + desc = class->table[PC[1]].desc; + +_PUSH_STRUCT_FIELD_2: + + if (((CSTRUCT *)object)->ref) + addr = (char *)((CSTATICSTRUCT *)object)->addr + desc->variable.offset; + else + addr = (char *)object + sizeof(CSTRUCT) + desc->variable.offset; + + ref = object; + goto _READ_VARIABLE; + + +_READ_VARIABLE: + + VALUE_class_read(desc->variable.class, &SP[-1], (void *)addr, desc->variable.ctype, ref); + goto _FIN_DEFINED_NO_BORROW; + + +_PUSH_PROPERTY_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_PUSH_PROPERTY: + + desc = class->table[PC[1]].desc; + +_PUSH_PROPERTY_2: + + if (desc->property.native) + { + if (EXEC_call_native(desc->property.read, object, desc->property.type, NULL)) + PROPAGATE(); + + //SP[-1] = TEMP; + VALUE_copy(&SP[-1], &TEMP); + goto _FIN_DEFINED; + } + else + { + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 0; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.read; + + EXEC_function_keep(); + + //SP[-1] = *RP; + VALUE_copy(&SP[-1], RP); + RP->type = T_VOID; + goto _FIN_DEFINED_NO_BORROW; + } + + +_PUSH_STATIC_METHOD: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + goto _PUSH_METHOD; + +_PUSH_METHOD_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_PUSH_METHOD: + + index = PC[1]; + desc = class->table[index].desc; + +_PUSH_METHOD_2: + + //printf("PUSH_METHOD: %d %s\n", index, desc->method.name); + + SP--; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + /*SP->_function.function = (int)&desc->method;*/ + + if (FUNCTION_is_native(&desc->method)) + { + if (desc->method.subr) + SP->_function.kind = FUNCTION_SUBR; + else + SP->_function.kind = FUNCTION_NATIVE; + } + else + SP->_function.kind = FUNCTION_PUBLIC; + + SP->_function.index = index; + + SP->_function.defined = defined; + + SP++; + + goto _FIN; + + +_PUSH_EXTERN: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + index = PC[1]; + desc = class->table[index].desc; + +_PUSH_EXTERN_2: + + //printf("PUSH_METHOD: %d %s\n", index, desc->method.name); + + SP--; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + SP->_function.kind = FUNCTION_EXTERN; + SP->_function.index = (int)desc->ext.exec; + SP->_function.defined = defined; + SP++; + + goto _FIN; + +_PUSH_UNKNOWN_METHOD: + + SP--; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + SP->_function.kind = FUNCTION_UNKNOWN; + SP->_function.index = PC[1]; + SP->_function.defined = FALSE; + SP++; + + //OBJECT_REF(&object); + + goto _FIN; + +_FIN_DEFINED: + + BORROW(&SP[-1]); + +_FIN_DEFINED_NO_BORROW: + + if (!defined) + VALUE_conv_variant(&SP[-1]); + + // SP[-1] was the object and it has been erased. So we must unref it manually, + // unless we are calling a static method (see above) + OBJECT_UNREF(object); + +_FIN: + + PC++; +} + +int EXEC_push_unknown_event(bool unknown) +{ + int index; + CLASS_DESC *desc; + const char *name; + + if (unknown) + { + name = CP->load->unknown[PC[1]]; + // The ':' is already in the name, thanks to the compiler. + index = CLASS_find_symbol(CP, name); + if (index == NO_SYMBOL) + THROW(E_DYNAMIC, CLASS_get_name(CP), name); + + desc = CP->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_EVENT) + THROW(E_DYNAMIC, CLASS_get_name(CP), name); + + PC[1] = index; + PC[0] &= ~1; + } + else + { + desc = CP->table[PC[1]].desc; + } + + index = desc->event.index; + + //if (desc->event.class->parent) + // index += desc->event.class->parent->n_event; + + return index; +} + diff --git a/main/gbx/gbx_expression.h b/main/gbx/gbx_expression.h new file mode 100644 index 00000000..166c92c3 --- /dev/null +++ b/main/gbx/gbx_expression.h @@ -0,0 +1,75 @@ +/*************************************************************************** + + gbx_expression.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EXPRESSION_H +#define __GBX_EXPRESSION_H + +#include "gb_table.h" +#include "gbx_class.h" + +typedef + struct { + void *parent; + char *source; + int len; + PATTERN *pattern; + int pattern_count; + PATTERN *current; + PATTERN *tree; + CLASS exec_class; + CLASS_LOAD class_load; + FUNCTION func; + CLASS_CONST *cst; + ushort *code; + ushort ncode; + ushort ncode_max; + TABLE *table; + TABLE *string; + /*TABLE *variable;*/ + CLASS **class; + char **unknown; + int *var; + short nvar; + short last_code; + short last_code2; + short assign_code; + int stack_usage; + void *op; + char *error; + unsigned analyze : 1; + unsigned rewrite : 1; + unsigned comment : 1; + unsigned custom : 1; + //unsigned _reserved : 12; + } + //PACKED + EXPRESSION; + +typedef + struct { + SYMBOL sym; + int local; + } + EVAL_SYMBOL; + +#endif diff --git a/main/gbx/gbx_extern.c b/main/gbx/gbx_extern.c new file mode 100644 index 00000000..ecf5317f --- /dev/null +++ b/main/gbx/gbx_extern.c @@ -0,0 +1,761 @@ +/*************************************************************************** + + gbx_extern.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXTERN_C + +#include "config.h" +#include "gb_common.h" + +#ifdef HAVE_FFI_COMPONENT + +#include + +#include "gb_common_buffer.h" +#include "gb_table.h" +#include "gb_hash.h" +#include "gbx_type.h" +#include "gbx_value.h" +#include "gbx_class_desc.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_c_array.h" +#include "gbx_struct.h" +#include "gbx_extern.h" + +typedef + struct { + SYMBOL sym; + lt_dlhandle handle; + } + EXTERN_SYMBOL; + +typedef + struct { + ffi_cif cif; + ffi_type **types; + ffi_type *rtype; + } + EXTERN_CIF; + +typedef + struct EXTERN_CALLBACK { + struct EXTERN_CALLBACK *next; + EXEC_GLOBAL exec; + void *closure; + void *code; + int nparam; + TYPE *sign; + TYPE ret; + EXTERN_CIF info; + } + EXTERN_CALLBACK; + +typedef + struct EXTERN_FUNC { + struct EXTERN_FUNC *next; + char *alias; + void *call; + EXTERN_CIF info; + } + EXTERN_FUNC; + +static TABLE *_table = NULL; +//static EXTERN_CALLBACK *_callbacks = NULL; +static HASH_TABLE *_callbacks = NULL; +static EXTERN_FUNC *_functions = NULL; + +static ffi_type *_to_ffi_type[17] = { + &ffi_type_void, &ffi_type_sint32, &ffi_type_sint32, &ffi_type_sint32, + &ffi_type_sint32, &ffi_type_sint64, &ffi_type_float, &ffi_type_double, + &ffi_type_void, &ffi_type_pointer, &ffi_type_pointer, &ffi_type_pointer, + &ffi_type_void, &ffi_type_void, &ffi_type_void, &ffi_type_pointer, + &ffi_type_pointer + }; + + +static void prepare_cif(EXTERN_CIF *info, int nsign, TYPE *sign, TYPE ret, int nparam, VALUE *value) +{ + int i; + TYPE t; + + info->types = NULL; + + if (nparam > 0) + { + ALLOC(&info->types, sizeof(ffi_type *) * nparam); + + for (i = 0; i < nparam; i++) + { + if (i < nsign) + t = sign[i]; + else + t = value[i].type; + + if (TYPE_is_object(t)) + t = T_OBJECT; + + info->types[i] = _to_ffi_type[t]; + } + } + + if (TYPE_is_object(ret)) + t = T_OBJECT; + else + t = ret; + + info->rtype = _to_ffi_type[t]; + + if (ffi_prep_cif(&info->cif, FFI_DEFAULT_ABI, nparam, info->rtype, info->types) != FFI_OK) + THROW(E_EXTCB, "Unable to prepare function description"); +} + + +static lt_dlhandle get_library(const char *name) +{ + EXTERN_SYMBOL *esym; + char *p; + int index; + + if (!_table) + TABLE_create(&_table, sizeof(EXTERN_SYMBOL), TF_NORMAL); + + index = TABLE_add_symbol(_table, name, strlen(name)); + esym = (EXTERN_SYMBOL *)TABLE_get_symbol(_table, index); + if (!esym->handle) + { + /* !!! Must add the suffix !!! */ + + p = strrchr(name, ':'); + if (!p) + sprintf(COMMON_buffer, "%s." SHARED_LIBRARY_EXT, name); + else + sprintf(COMMON_buffer, "%.*s." SHARED_LIBRARY_EXT ".%s", (int)(p - name), name, p + 1); + + name = COMMON_buffer; + + #ifndef DONT_USE_LTDL + /* no more available in libltld ? + lt_dlopen_flag = RTLD_LAZY; + */ + esym->handle = lt_dlopenext(name); + #else + esym->handle = dlopen(name, RTLD_LAZY); + #endif + + if (esym->handle == NULL) + { + name = GB_RealFileName(name, strlen(name)); + #ifndef DONT_USE_LTDL + esym->handle = lt_dlopenext(name); + #else + esym->handle = dlopen(name, RTLD_LAZY); + #endif + } + + if (esym->handle == NULL) + THROW(E_EXTLIB, name, lt_dlerror()); + + //fprintf(stderr, "%s loaded.\n", name); + } + + return esym->handle; +} + +void *EXTERN_get_symbol(const char *library, const char *symbol) +{ + lt_dlhandle handle = get_library(library); + return lt_dlsym(handle, symbol); +} + +static EXTERN_FUNC *get_function(CLASS_EXTERN *ext) +{ + EXTERN_FUNC *func; + void *call; + lt_dlhandle handle; + + if (ext->loaded) + return (EXTERN_FUNC *)ext->alias; + + handle = get_library(ext->library); + call = lt_dlsym(handle, ext->alias); + + if (call == NULL) + THROW(E_EXTSYM, ext->library, ext->alias); + + ALLOC_ZERO(&func, sizeof(EXTERN_FUNC)); + func->next = _functions; + func->alias = ext->alias; + _functions = func; + + func->call = call; + + if (!ext->vararg) + prepare_cif(&func->info, ext->n_param, (TYPE *)ext->param, ext->type, ext->n_param, NULL); + + //ext->library = (char *)handle; + ext->alias = (char *)func; + ext->loaded = TRUE; + + return func; +} + +void *EXTERN_get_addr(CLASS_EXTERN *ext) +{ + EXTERN_FUNC *func = get_function(ext); + return func->call; +} + + +/* + EXEC.class : the class + EXEC.index : the extern function index + EXEC.nparam : the number of parameters to the call + EXEC.drop : if the return value should be dropped. +*/ + +void EXTERN_call(void) +{ + static const void *jump[17] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT + }; + static const int use_temp[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, sizeof(char *), sizeof(char *), 0, 0, 0, 0, 0, sizeof(void *) }; + static char temp[16 * sizeof(void *)]; + static void *null = 0; + + CLASS_EXTERN *ext = &EXEC.class->load->ext[EXEC.index]; + EXTERN_FUNC *func; + EXTERN_CIF cif; + bool vararg; + int nparam = EXEC.nparam; + void *args[nparam]; + TYPE *sign; + VALUE *value; + char *tmp = NULL; + char *next_tmp; + int i, t; + union { + int _integer; + float _single; + double _float; + char *_string; + int64_t _long; + void *_pointer; + } + rvalue; + + if (!ext->loaded) + { + if (nparam < ext->n_param) + THROW(E_NEPARAM); + if (!ext->vararg && nparam > ext->n_param) + THROW(E_TMPARAM); + } + + func = get_function(ext); + sign = (TYPE *)ext->param; + vararg = ext->vararg; + value = &SP[-nparam]; + next_tmp = temp; + + for (i = 0; i < nparam; i++) + { + if (i < ext->n_param) + VALUE_conv(&value[i], sign[i]); + else + VARIANT_undo(&value[i]); + } + + if (vararg) + prepare_cif(&cif, ext->n_param, (TYPE *)ext->param, ext->type, nparam, value); + + for (i = 0; i < nparam; i++, value++, sign++) + { + if (TYPE_is_object(value->type)) + t = T_OBJECT; + else + t = (int)value->type; + + if (use_temp[t]) + { + tmp = next_tmp; + if ((tmp + use_temp[t]) > &temp[sizeof(temp)]) + THROW(E_TMPARAM); + args[i] = tmp; + next_tmp = tmp + use_temp[t]; + } + goto *jump[t]; + + __BOOLEAN: + __BYTE: + __SHORT: + __INTEGER: + args[i] = &value->_integer.value; + continue; + + __LONG: + args[i] = &value->_long.value; + continue; + + __SINGLE: + args[i] = &value->_single.value; + continue; + + __FLOAT: + args[i] = &value->_float.value; + continue; + + __STRING: + *((char **)tmp) = (char *)(value->_string.addr + value->_string.start); + continue; + + __OBJECT: + *((void **)tmp) = OBJECT_get_addr(value->_object.object); + continue; + + __POINTER: + args[i] = &value->_pointer.value; + continue; + + __NULL: + args[i] = &null; + continue; + + __DATE: + __VARIANT: + __VOID: + __CLASS: + __FUNCTION: + + THROW(E_UTYPE); + } + + if (vararg) + { + ffi_call(&cif.cif, func->call, &rvalue, args); + FREE(&cif.types); + } + else + ffi_call(&func->info.cif, func->call, &rvalue, args); + + + switch (ext->type) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + GB_ReturnInteger(rvalue._integer); + break; + + case T_LONG: + GB_ReturnLong(rvalue._long); + break; + + case T_SINGLE: + GB_ReturnSingle(rvalue._single); + break; + + case T_FLOAT: + GB_ReturnFloat(rvalue._float); + break; + + case T_STRING: + GB_ReturnConstZeroString(rvalue._string); + break; + + case T_POINTER: + GB_ReturnPointer(rvalue._pointer); + break; + + case T_VOID: + default: + + if (TYPE_is_pure_object(ext->type)) + { + CLASS *class = (CLASS *)(ext->type); + if (CLASS_is_struct(class)) + { + GB_ReturnObject(CSTRUCT_create_static(STRUCT_CONST, class, rvalue._pointer)); + break; + } + } + + TEMP.type = T_VOID; + break; + } + + while (nparam) + { + nparam--; + POP(); + } + + POP(); /* extern function */ + + /* from EXEC_native() */ + + BORROW(&TEMP); + VALUE_conv(&TEMP, ext->type); + *SP = TEMP; + SP++; +} + +void EXTERN_release(void) +{ + EXTERN_CALLBACK *cb; + HASH_ENUM iter; + + if (!_callbacks) + return; + + CLEAR(&iter); + + for(;;) + { + cb = HASH_TABLE_next(_callbacks, &iter, FALSE); + if (!cb) + break; + if (cb->exec.object) + { + OBJECT_UNREF(cb->exec.object); + cb->exec.object = NULL; + } + } +} + +void EXTERN_exit(void) +{ + int i; + EXTERN_SYMBOL *esym; + EXTERN_CALLBACK *cb; + EXTERN_FUNC *func; + HASH_ENUM iter; + + if (_table) + { + for (i = 0; i < TABLE_count(_table); i++) + { + esym = (EXTERN_SYMBOL *)TABLE_get_symbol(_table, i); + if (esym->handle) + lt_dlclose(esym->handle); + } + + TABLE_delete(&_table); + } + + if (_callbacks) + { + CLEAR(&iter); + + for(;;) + { + cb = HASH_TABLE_next(_callbacks, &iter, FALSE); + if (!cb) + break; + if (cb->exec.object) + OBJECT_UNREF(cb->exec.object); + FREE(&cb->info.types); + ffi_closure_free(cb->closure); + } + + HASH_TABLE_delete(&_callbacks); + } + + while (_functions) + { + func = _functions; + _functions = func->next; + + FREE(&func->info.types); + FREE(&func); + } +} + +static void callback(ffi_cif *cif, void *result, void **args, void *user_data) +{ + static const void *jump[17] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT + }; + + EXTERN_CALLBACK *cb = (EXTERN_CALLBACK *)user_data; + //VALUE_FUNCTION *value = &cb->func; + int i; + VALUE *arg; + + STACK_check(cb->nparam); + + for (i = 0; i < cb->nparam; i++) + { + arg = SP++; + arg->type = cb->sign[i]; + + if (TYPE_is_object(cb->sign[i])) + goto __OBJECT; + else + goto *jump[cb->sign[i]]; + + __BOOLEAN: + arg->_integer.value = *((char *)args[i]) ? -1 : 0; + continue; + + __BYTE: + arg->_integer.value = *((char *)args[i]); + continue; + + __SHORT: + arg->_integer.value = *((short *)args[i]); + continue; + + __INTEGER: + arg->_integer.value = *((int *)args[i]); + continue; + + __LONG: + arg->_long.value = *((int64_t *)args[i]); + continue; + + __SINGLE: + arg->_single.value = *((float *)args[i]); + continue; + + __FLOAT: + arg->_float.value = *((double *)args[i]); + continue; + + __STRING: + arg->type = T_CSTRING; + arg->_string.addr = *((char **)args[i]); + arg->_string.start = 0; + arg->_string.len = arg->_string.addr ? strlen(arg->_string.addr) : 0; + continue; + + __OBJECT: + arg->_object.object = *((void **)args[i]); + continue; + + __POINTER: + arg->_pointer.value = *((void **)args[i]); + continue; + + __NULL: + __DATE: + __VARIANT: + __VOID: + __CLASS: + __FUNCTION: + VALUE_null(arg); + } + + EXEC = cb->exec; + + if (!EXEC.native) + { + EXEC_function_keep(); + + // Do that later, within a TRY/CATCH: VALUE_conv(RP, cb->ret); + + switch (cb->ret) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + *((ffi_arg *)result) = RP->_integer.value; + break; + + case T_LONG: + *((int64_t *)result) = RP->_long.value; + break; + + case T_SINGLE: + *((float *)result) = RP->_single.value; + break; + + case T_FLOAT: + *((double *)result) = RP->_float.value; + break; + + case T_STRING: + if (!RP->_string.len) + *((char **)result) = NULL; + else + *((char **)result) = RP->_string.addr + RP->_string.start; + break; + + case T_OBJECT: + *((void **)result) = RP->_object.object; + break; + + case T_POINTER: + *((void **)result) = RP->_pointer.value; + break; + + default: + break; + } + + EXEC_release_return_value(); + } +} + + +static void prepare_cif_from_gambas(EXTERN_CALLBACK *cb, FUNCTION *func) +{ + if (func->npmin != func->n_param || func->vararg) + THROW(E_EXTCB, "The function must take a fixed number of arguments"); + + cb->nparam = func->npmin; + cb->sign = (TYPE *)func->param; + cb->ret = func->type; +} + +/*static void prepare_cif_from_native(EXTERN_CALLBACK *cb, CLASS_DESC_METHOD *desc) +{ + THROW(E_EXTCB, "Not implemented yet"); +}*/ + +void *EXTERN_make_callback(VALUE_FUNCTION *value) +{ + EXEC_GLOBAL exec = { 0 }; + FUNCTION *func; + EXTERN_CALLBACK *cb; + union { + char key[sizeof(void *)]; + void *addr; + } + cb_key; + + if (value->kind == FUNCTION_EXTERN) + { + CLASS_EXTERN *ext = &value->class->load->ext[value->index]; + return get_function(ext)->call; + } + + if (!_callbacks) + HASH_TABLE_create(&_callbacks, sizeof(EXTERN_CALLBACK), HF_NORMAL); + + //ALLOC(&cb, sizeof(EXTERN_CALLBACK), "EXTERN_make_callback"); + + // See gbx_exec_loop.c, at the _CALL label, to understand the following. + + if (value->kind == FUNCTION_PRIVATE) + { + exec.object = value->object; + exec.class = value->class; + exec.native = FALSE; + exec.index = value->index; + } + else if (value->kind == FUNCTION_PUBLIC) + { + exec.object = value->object; + exec.native = FALSE; + exec.desc = &value->class->table[value->index].desc->method; + exec.index = (int)(intptr_t)(exec.desc->exec); + exec.class = exec.desc->class; + } + /*else if (value->kind == FUNCTION_NATIVE) + { + cb->exec.object = value->object; + cb->exec.class = value->class; + cb->exec.native = TRUE; + cb->exec.index = value->index; + cb->exec.desc = &value->class->table[value->index].desc->method; + //cb->desc = &value->class->table[value->index].desc->method; + + prepare_cif_from_native(cb, cb->exec.desc); + }*/ + else + THROW(E_EXTCB, "Not supported"); + + func = &exec.class->load->func[exec.index]; + cb_key.addr = func; + + cb = (EXTERN_CALLBACK *)HASH_TABLE_insert(_callbacks, cb_key.key, sizeof(void *)); + if (cb->code) + { + OBJECT_UNREF(value->object); + return cb->code; + } + + // Do not reference value->_function.object, as it has been already referenced + // when put on the stack in exec_loop.c + + /*if (value->object) + { + fprintf(stderr, "EXTERN_make_callback: ref: %p\n", value->object); + OBJECT_REF(value->object); + }*/ + + cb->exec = exec; + prepare_cif_from_gambas(cb, func); + + cb->exec.nparam = cb->nparam; + + prepare_cif(&cb->info, cb->nparam, cb->sign, cb->ret, cb->nparam, NULL); + + cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->code); + + if (ffi_prep_closure_loc(cb->closure, &cb->info.cif, callback, cb, cb->code) != FFI_OK) + THROW(E_EXTCB, "Unable to create closure"); + + return cb->code; +} + +#else /* HAVE_FFI_COMPONENT */ + +#include "gbx_value.h" +#include "gbx_extern.h" + +void EXTERN_call(void) +{ + THROW_ILLEGAL(); +} + +void EXTERN_release(void) +{ +} + +void EXTERN_exit(void) +{ +} + +void *EXTERN_make_callback(VALUE_FUNCTION *value) +{ + return NULL; +} + +void *EXTERN_get_symbol(const char *library, const char *symbol) +{ + return NULL; +} + +void *EXTERN_get_addr(CLASS_EXTERN *ext) +{ + return NULL; +} + +#endif + diff --git a/main/gbx/gbx_extern.h b/main/gbx/gbx_extern.h new file mode 100644 index 00000000..7cc4b4ad --- /dev/null +++ b/main/gbx/gbx_extern.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gbx_extern.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EXTERN_H +#define __GBX_EXTERN_H + +#include "config.h" + +#include + +#ifndef DONT_USE_LTDL + #include +#else + #define lt_dlsym dlsym + #define lt_dlclose dlclose + #define lt_dlerror dlerror + #define lt_dlhandle void * +#endif + +#include "gbx_value.h" + +void EXTERN_release(void); +void EXTERN_exit(void); +void EXTERN_call(void); +void *EXTERN_make_callback(VALUE_FUNCTION *value); +void *EXTERN_get_symbol(const char *library, const char *symbol); +void *EXTERN_get_addr(CLASS_EXTERN *ext); + +#endif diff --git a/main/gbx/gbx_info.h b/main/gbx/gbx_info.h new file mode 100644 index 00000000..90de12f7 --- /dev/null +++ b/main/gbx/gbx_info.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + gbx_info.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_INFO_H +#define __GBX_INFO_H + +#include "gb_common.h" +#include "gambas.h" + +#ifdef GBX_INFO + +#undef GB_DECLARE +#define GB_DECLARE(name, size) { name, (intptr_t)GB_VERSION, 0 } + +#undef GB_HOOK_NEW +#define GB_HOOK_NEW(hook) { GB_HOOK_NEW_ID, 0 } + +#undef GB_HOOK_FREE +#define GB_HOOK_FREE(hook) { GB_HOOK_FREE_ID, 0 } + +#undef GB_HOOK_CHECK +#define GB_HOOK_CHECK(hook) { GB_HOOK_CHECK_ID, 0 } + +#undef GB_PROPERTY +#define GB_PROPERTY(symbol, type, proc) { "p" symbol, (intptr_t)type, 0 } + +#undef GB_PROPERTY_READ +#define GB_PROPERTY_READ(symbol, type, proc) { "r" symbol, (intptr_t)type, 0 } + +#undef GB_METHOD +#define GB_METHOD(symbol, type, exec, signature) { "m" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#undef GB_EVENT +#define GB_EVENT(symbol, type, signature, id) { "::" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#undef GB_STATIC_PROPERTY +#define GB_STATIC_PROPERTY(symbol, type, proc) { "P" symbol, (intptr_t)type, 0 } + +#undef GB_STATIC_PROPERTY_READ +#define GB_STATIC_PROPERTY_READ(symbol, type, proc) { "R" symbol, (intptr_t)type, 0 } + +#undef GB_STATIC_METHOD +#define GB_STATIC_METHOD(symbol, type, exec, signature) { "M" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#undef GB_STATIC_FAST_METHOD +#define GB_STATIC_FAST_METHOD(symbol, type, exec, signature) { "M" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#endif + +#endif diff --git a/main/gbx/gbx_jit.c b/main/gbx/gbx_jit.c new file mode 100644 index 00000000..83f147c2 --- /dev/null +++ b/main/gbx/gbx_jit.c @@ -0,0 +1,500 @@ +/*************************************************************************** + + gbx_jit.c + + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_JIT_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gbx_component.h" +#include "gbx_exec.h" +#include "gbx_object.h" +#include "gbx_api.h" +#include "gbx_jit.h" + +typedef + struct { + JIT_FUNC addr; + PCODE *code; + } + JIT_FUNCTION; + +bool JIT_disabled = FALSE; + +static bool _component_loaded = FALSE; +static GB_FUNCTION _jit_compile_func; +static GB_FUNCTION _jit_wait_func; + +static char _jit_state = JIT_NOT_COMPILED; +static void *_jit_library = NULL; + +static JIT_FUNCTION *_jit_func = NULL; + +static bool _debug = FALSE; + +static void find_method(GB_FUNCTION *func, const char *method, const char *sign, const char *type) +{ + if (GB_GetFunction(func, CLASS_find_global("Jit"), method, sign, type)) + ERROR_panic("Unable to find JIT.&1() method", method); +} + +void JIT_init(void) +{ + char *var = getenv("GB_NO_JIT"); + if (var && var[0] && !(var[0] == '0' && var[1] == 0)) + JIT_disabled = TRUE; +} + +void JIT_abort(void) +{ + GB_FUNCTION func; + + if (!_component_loaded || JIT_disabled) + return; + + find_method(&func, "_Abort", NULL, NULL); + GB_Call(&func, 0, FALSE); + GB_Wait(-1); +} + +void JIT_exit(void) +{ + ARRAY_delete(&_jit_func); +} + +static int get_state(ARCHIVE *arch) +{ + return arch ? arch->jit_state : _jit_state; +} + +bool JIT_can_compile(ARCHIVE *arch) +{ + return get_state(arch) == JIT_NOT_COMPILED; +} + +void JIT_compile(ARCHIVE *arch) +{ + COMPONENT *current; + + if (JIT_disabled) + return; + + if (arch ? arch->jit_library : _jit_library) + return; + + if (!_component_loaded) + { + char *var; + GB_FUNCTION init_func; + GB_VALUE *ret; + + _component_loaded = TRUE; + + var = getenv("GB_JIT_DEBUG"); + if (var && var[0] && !(var[0] == '0' && var[1] == 0)) + _debug = TRUE; + + if (_debug) + fprintf(stderr, "gbx3: loading gb.jit component\n"); + + COMPONENT_load(COMPONENT_create("gb.jit")); + + find_method(&init_func, "_Search", NULL, NULL); + + ret = GB_Call(&init_func, 0, FALSE); + if (ret->_boolean.value) + { + JIT_disabled = TRUE; + fprintf(stderr, "gbx3: no compiler found. JIT is disabled.\n"); + return; + } + + find_method(&_jit_compile_func, "_Compile", "s", "b"); + find_method(&_jit_wait_func, "_Wait", "s", "s"); + } + + arch ? (arch->jit_state = JIT_COMPILING) : (_jit_state = JIT_COMPILING); + + current = COMPONENT_current; + COMPONENT_current = NULL; + + GB_Push(1, T_STRING, arch ? arch->name : "", -1); + GB_Call(&_jit_compile_func, 1, FALSE); + + COMPONENT_current = current; +} + +static bool wait_for_compilation(ARCHIVE *arch) +{ + COMPONENT *current; + void *lib; + void **iface; + GB_VALUE *ret; + char *path; + + if (JIT_disabled) + return TRUE; + + if (arch ? arch->jit_library : _jit_library) + return FALSE; + + current = COMPONENT_current; + COMPONENT_current = NULL; + + GB_Push(1, T_STRING, arch ? arch->name : "", -1); + ret = GB_Call(&_jit_wait_func, 1, FALSE); + + COMPONENT_current = current; + + arch ? (arch->jit_state = JIT_COMPILED) : (_jit_state = JIT_COMPILED); + + path = GB_ToZeroString((GB_STRING *)ret); + if (!*path) + ERROR_panic("Unable to compile JIT source file"); + + //fprintf(stderr, "gbx3: shared jit library is: %s\n", path); + + lib = dlopen(path, RTLD_NOW); + if (!lib) + ERROR_panic("Unable to load JIT library: %s", dlerror()); + + if (arch) + arch->jit_library = lib; + else + _jit_library = lib; + + iface = dlsym(lib, "GB_PTR"); + if (iface) *((void **)iface) = &GAMBAS_Api; + + iface = dlsym(lib, "JIT_PTR"); + if (iface) *((void **)iface) = &GAMBAS_JitApi; + + return FALSE; +} + +static bool create_function(CLASS *class, int index) +{ + ARCHIVE *arch; + FUNCTION *func; + JIT_FUNCTION *jit; + void *lib; + void *addr; + int i; + int len; + char *name; + int jit_index; + char c; + + arch = class->component ? class->component->archive : NULL; + + if (!class->loaded) + return TRUE; + + /*if (get_state(arch) == JIT_NOT_COMPILED) // don't use JIT if we are compiling the corresponding archive + return TRUE;*/ + + if (wait_for_compilation(arch)) + { + for (i = 0; i < class->load->n_func; i++) + class->load->func[i].fast = FALSE; + return TRUE; + } + + func = &class->load->func[index]; + + if (!arch) + lib = _jit_library; + else + lib = arch->jit_library; + + name = class->name; + while (*name == '^') + name++; + + len = sprintf(COMMON_buffer, "jit_%s_%d", name, index); + + for (i = 0; i < len; i++) + { + c = tolower(COMMON_buffer[i]); + if (c == ':') + c = '$'; + COMMON_buffer[i] = c; + } + + addr = dlsym(lib, COMMON_buffer); + if (!addr) + { + func->fast = FALSE; + return TRUE; + } + + if (_debug && func->debug) + fprintf(stderr, "gbx3: loading jit function: %s.%s\n", class->name, func->debug->name); + + if (!_jit_func) + ARRAY_create(&_jit_func); + + jit_index = ARRAY_count(_jit_func); + jit = (JIT_FUNCTION *)ARRAY_add(&_jit_func); + + jit->addr = addr; + jit->code = func->code; + + func->code = (PCODE *)(intptr_t)jit_index; + func->fast_linked = TRUE; + + return FALSE; +} + + +bool JIT_exec(bool ret_on_stack) +{ + VALUE *sp = SP; + JIT_FUNCTION *jit; + CLASS *class = EXEC.class; + void *object = EXEC.object; + char nparam = EXEC.nparam; + VALUE ret; + FUNCTION *func = EXEC.func; + + if (JIT_disabled) + return TRUE; + + if (nparam < func->npmin) + THROW(E_NEPARAM); + else if (nparam > func->n_param && !func->vararg) + THROW(E_TMPARAM); + + if (!func->fast_linked) + { + if (create_function(class, EXEC.index)) + return TRUE; + } + + STACK_push_frame(&EXEC_current, func->stack_usage); + + CP = class; + EXEC_check_bytecode(); + OP = object; + FP = func; + EC = NULL; + + jit = &_jit_func[(intptr_t)func->code]; + + PROFILE_ENTER_FUNCTION(); + + TRY + { + (*(jit->addr))(nparam); + } + CATCH + { + PROFILE_LEAVE_FUNCTION(); + if (SP != sp) + ERROR_panic("Stack mismatch in JIT function (SP %+ld)\n", SP - sp); + RELEASE_MANY(SP, nparam); + STACK_pop_frame(&EXEC_current); + PROPAGATE(); + } + END_TRY + + PROFILE_LEAVE_FUNCTION(); + + if (SP != sp) + ERROR_panic("Stack mismatch in JIT function (SP %+ld)\n", SP - sp); + + if (func->type != T_VOID) + { + ret = TEMP; + BORROW(&ret); + } + else + ret.type = T_VOID; + + RELEASE_MANY(SP, nparam); + + STACK_pop_frame(&EXEC_current); + + if (ret_on_stack) + { + if (SP[-1].type == T_FUNCTION) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + *SP++ = ret; + ret.type = T_VOID; + } + else + RET = ret; + + return FALSE; +} + +PCODE *JIT_get_code(FUNCTION *func) +{ + if (func->fast_linked) + return _jit_func[(intptr_t)func->code].code; + else + return func->code; +} + +void *JIT_get_class_ref(int index) +{ + return CP->load->class_ref[index]; +} + +CLASS_CONST *JIT_get_constant(int index) +{ + return &CP->load->cst[index]; +} + +void JIT_debug(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +typedef + struct { + PCODE *pc; + VALUE **psp; + PCODE code; + } + JIT_call_unknown_ERROR; + +static void error_JIT_call_unknown(JIT_call_unknown_ERROR *save) +{ + save->pc[1] = (PCODE)save->code; + *save->psp = SP; +} + +void JIT_call_unknown(PCODE *pc, VALUE **psp) +{ + JIT_call_unknown_ERROR save; + + PC = pc; + SP = *psp; + + save.pc = pc; + save.psp = psp; + save.code = pc[1]; + + pc[1] = 0x140B; + + ON_ERROR_1(error_JIT_call_unknown, &save) + { + EXEC_function_loop(); + } + END_ERROR + + error_JIT_call_unknown(&save); +} + +void JIT_load_class(CLASS *class) +{ + CLASS_load(class); +} + +void JIT_load_class_without_init(CLASS *class) +{ + TRY + { + CLASS_load_without_init(class); + } + CATCH + { + class->error = FALSE; + } + END_TRY +} + +void JIT_add_string_local(GB_STRING *str, GB_STRING val) +{ + int len; + char *add; + int len_add; + + add = val.value.addr + val.value.start; + len_add = val.value.len; + len = str->value.len; + + if (len_add == 0) + return; + + if (len && STRING_from_ptr(str->value.addr)->ref == 1 && str->value.start == 0) + { + str->value.addr = STRING_add(str->value.addr, add, len_add); + } + else + { + char *str_new = STRING_new(NULL, len + len_add); + if (len) memcpy(str_new, str->value.addr + str->value.start, len); + memcpy(&str_new[len], add, len_add); + if (str->type == T_STRING) + STRING_unref(&str->value.addr); + else + str->type = T_STRING; + str->value.addr = str_new; + } + + str->value.len += len_add; + + if (val.type == T_STRING) STRING_unref(&val.value.addr); +} + +void JIT_add_string_global(char **pstr, GB_STRING val) +{ + char *str; + int len; + char *add; + int len_add; + + add = val.value.addr + val.value.start; + len_add = val.value.len; + + if (len_add == 0) + return; + + str = *pstr; + len = STRING_length(str); + + if (len && STRING_from_ptr(str)->ref == 1) + *pstr = STRING_add(str, add, len_add); + else + { + char *str_new = STRING_new(NULL, len + len_add); + if (len) memcpy(str_new, str, len); + memcpy(&str_new[len], add, len_add); + STRING_unref(pstr); + *pstr = str_new; + } + + if (val.type == T_STRING) STRING_unref(&val.value.addr); +} + + diff --git a/main/gbx/gbx_jit.h b/main/gbx/gbx_jit.h new file mode 100644 index 00000000..998b2f2f --- /dev/null +++ b/main/gbx/gbx_jit.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + gbx_jit.h + + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_JIT_H +#define __GBX_JIT_H + +#include "gbx_class.h" +#include "gbx_type.h" +#include "gbx_value.h" +#include "gbx_stack.h" +#include "gbx_object.h" +#include "gbx_exec.h" + +typedef + void (*JIT_FUNC)(uchar nparam); + +enum { JIT_NOT_COMPILED = 0, JIT_COMPILING = 1, JIT_COMPILED = 2}; + +#ifndef __GBX_JIT_C +extern bool JIT_disabled; +#endif + +void JIT_compile(ARCHIVE *arch); +void JIT_debug(const char *fmt, ...); +bool JIT_exec(bool ret_on_stack); +PCODE *JIT_get_code(FUNCTION *func); +CLASS_CONST *JIT_get_constant(int index); +void *JIT_get_class_ref(int index); +void JIT_call_unknown(PCODE *pc, VALUE **psp); + +void JIT_abort(void); +void JIT_init(void); +void JIT_exit(void); + +bool JIT_can_compile(ARCHIVE *arch); + +void JIT_load_class(CLASS *class); +void JIT_load_class_without_init(CLASS *class); + +void JIT_add_string_local(GB_STRING *src, GB_STRING val); +void JIT_add_string_global(char **pstr, GB_STRING val); + +#endif diff --git a/main/gbx/gbx_library.c b/main/gbx/gbx_library.c new file mode 100644 index 00000000..db35b92b --- /dev/null +++ b/main/gbx/gbx_library.c @@ -0,0 +1,375 @@ +/*************************************************************************** + + gbx_library.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_LIBRARY_C + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_replace.h" +#include "gb_magic.h" + +#include +#include +#include +#include +#include + +#include "gb_error.h" + +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_stack.h" +#include "gb_file.h" +#include "gbx_archive.h" +#include "gbx_project.h" +#include "gbx_api.h" +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_component.h" +#include "gb.jit.h" + +#include "gbx_library.h" + +//#define DEBUG +//#define DEBUG_PRELOAD + +// Maximum size of a project or startup file +// This avoids reading too much in the archive file! + +#define MAX_SIZE 2048 + +int _bytes_read = 0; + +#if 0 +static lt_ptr library_malloc(size_t size) +{ + lt_ptr ptr; + ALLOC(&ptr, size, "library_malloc"); + printf("library_malloc: %d -> %p\n", size, ptr); + return ptr; +} + + +static void library_free(lt_ptr ptr) +{ + printf("library_free -> %p\n", ptr); + FREE(&ptr, "library_free"); +} +#endif + +static void *get_symbol(LIBRARY *lib, const char *symbol, bool err) +{ + void *sym; + + sym = lt_dlsym(lib->handle, symbol); + if (sym == NULL && err) + { + strcpy(COMMON_buffer, lt_dlerror()); + lt_dlclose(lib->handle); + lib->handle = NULL; + THROW(E_LIBRARY, lib->name, COMMON_buffer); + } + + return sym; +} + + +static void copy_interface(void **src, void **dst) +{ + for(;;) + { + if (!*src) + return; + + *dst++ = *src++; + } +} + + +void LIBRARY_init(void) +{ + /*if (putenv("LD_BIND_NOW=true")) + ERROR_panic("Cannot set LD_BIND_NOW: &1", strerror(errno)); + + if (putenv("KDE_MALLOC=0")) + ERROR_panic("Cannot set KDE_MALLOC: &1", strerror(errno));*/ + + /*lt_dlmalloc = library_malloc; + lt_dlfree = library_free;*/ + + #ifndef DONT_USE_LTDL + if (lt_dlinit()) + ERROR_panic("Cannot initialize plug-in management: %s", lt_dlerror()); + #endif +} + + +void LIBRARY_exit(void) +{ + #ifndef DONT_USE_LTDL + lt_dlexit(); + #endif +} + + +void LIBRARY_get_interface(LIBRARY *lib, int version, void *iface) +{ + char symbol[32]; + int i, len; + char c; + + len = strlen(lib->name); + for (i = 0; i < len; i++) + { + c = toupper(lib->name[i]); + if (!isalnum((unsigned char)c)) + c = '_'; + + symbol[i] = c; + } + + sprintf(&symbol[len], "_%d", version); + + copy_interface((void **)get_symbol(lib, symbol, TRUE), iface); +} + + +bool LIBRARY_get_interface_by_name(const char *name, int version, void *iface) +{ + COMPONENT *comp; + + comp = COMPONENT_find(name); + if (!comp || !comp->library) + return TRUE; + + LIBRARY_get_interface(comp->library, version, iface); + return FALSE; +} + + + +LIBRARY *LIBRARY_create(const char *name) +{ + LIBRARY *lib; + + ALLOC_ZERO(&lib, sizeof(LIBRARY)); + + lib->handle = NULL; + lib->name = name; + + /*if (name) + { + lib->persistent = FALSE; + lib->preload = FALSE; + } + else + { + lib->persistent = TRUE; + lib->preload = TRUE; + }*/ + + return lib; +} + + +void LIBRARY_delete(LIBRARY *lib) +{ + LIBRARY_unload(lib); + FREE(&lib); +} + + +static void init_interface(LIBRARY *lib, const char *sym, const char *sym_ptr, void **api) +{ + void **iface; + + iface = get_symbol(lib, sym_ptr, FALSE); + if (iface) + { + *((void **)iface) = api; + return; + } + + iface = get_symbol(lib, sym, FALSE); + if (iface) + copy_interface(api, iface); +} + + +int LIBRARY_load(LIBRARY *lib) +{ + int (*func)(); + GB_DESC **desc; + char *path; + int order = 0; + + if (lib->handle) + return 0; + +#ifdef DEBUG + clock_t t = clock(); + fprintf(stderr, "Loading library %s\n", lib->name); +#endif + + path = FILE_buffer(); + sprintf(path, LIB_PATTERN, COMPONENT_path, lib->name); + + //if (!FILE_exist(path)) + // sprintf(path, LIB_PATTERN, COMPONENT_user_path, lib->name); + + #ifndef DONT_USE_LTDL + /* no more available in libltld ? + lt_dlopen_flag = RTLD_LAZY; + */ + lib->handle = lt_dlopenext(path); + #else + lib->handle = dlopen(path, RTLD_LAZY); + #endif + + if (lib->handle == NULL) + THROW(E_LIBRARY, lib->name, lt_dlerror()); + + func = get_symbol(lib, LIB_INIT, TRUE); + + /* Interface de Gambas */ + + init_interface(lib, LIB_GAMBAS, LIB_GAMBAS "_PTR", GAMBAS_Api); + init_interface(lib, LIB_JIT, LIB_JIT "_PTR", GAMBAS_JitApi); + + /* Signal function */ + lib->signal = (void(*)())get_symbol(lib, LIB_SIGNAL, FALSE); + lib->info = (int(*)())get_symbol(lib, LIB_INFO, FALSE); + + /* Initialisation */ + order = (*func)(); + + /* Déclaration des classes */ + desc = get_symbol(lib, LIB_CLASS, FALSE); + if (desc) + LIBRARY_declare(desc); + +#ifdef DEBUG + fprintf(stderr, "Library %s loaded ", lib->name); + fprintf(stderr, "in %g s\n", ((double)(clock() - t) / CLOCKS_PER_SEC)); +#endif + + return order; +} + +void LIBRARY_after_init(LIBRARY *lib) +{ + void (*func)(); + + func = get_symbol(lib, LIB_AFTER_INIT, FALSE); + if (func) (*func)(); +} + +void LIBRARY_exec(LIBRARY *lib, int argc, char **argv) +{ + void (*func)(); + + func = get_symbol(lib, LIB_MAIN, FALSE); + if (func) + (*func)(argc, argv); +} + +void LIBRARY_declare_one(GB_DESC *desc) +{ + CLASS_find_global(desc->name); + if (CLASS_register(desc) == NULL) + THROW(E_REGISTER, desc->name); +} + +void LIBRARY_declare(GB_DESC **desc) +{ + GB_DESC **p; + + p = desc; + while (*p != NULL) + { + CLASS_find_global((*p)->name); + p++; + } + + p = desc; + while (*p != NULL) + { + if (CLASS_register(*p) == NULL) + THROW(E_REGISTER, (*p)->name); + + p++; + } +} + + +void LIBRARY_unload(LIBRARY *lib) +{ + void (*gambas_exit)(); + + if (lib->handle == NULL) + return; + + // Do not free pre-loaded classes + + gambas_exit = lt_dlsym(lib->handle, LIB_EXIT); + if (gambas_exit != NULL) + (*gambas_exit)(); + + if (lib->persistent) + { + gambas_exit = lt_dlsym(lib->handle, "_fini"); + if (gambas_exit != NULL) + (*gambas_exit)(); + } + else + { + lt_dlclose(lib->handle); + } + + lib->handle = NULL; + +#ifdef DEBUG + printf("Unloading library %s\n", lib->name); +#endif +} + + +void LIBRARY_before_fork(LIBRARY *lib) +{ + void (*func)(); + + if (lib->handle) + { + func = get_symbol(lib, LIB_FORK, FALSE); + if (func) (*func)(); + } +} + diff --git a/main/gbx/gbx_library.h b/main/gbx/gbx_library.h new file mode 100644 index 00000000..fa89d3c2 --- /dev/null +++ b/main/gbx/gbx_library.h @@ -0,0 +1,79 @@ +/*************************************************************************** + + gbx_library.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_LIBRARY_H +#define __GBX_LIBRARY_H + +#include "gambas.h" +#include "gb_list.h" +#include "gb_component.h" + +#include + +#ifndef DONT_USE_LTDL + #include +#else + #define lt_dlsym dlsym + #define lt_dlclose dlclose + #define lt_dlerror dlerror +#endif + +typedef + struct _LIBRARY { +#ifdef USE_LTDL + lt_dlhandle handle; +#else + void *handle; +#endif + const char *name; + void (*signal)(); + int (*info)(); + unsigned persistent : 1; + unsigned _reserved: 13; + } + LIBRARY; + +#ifndef __GBX_LIBRARY_C +#endif + +void LIBRARY_init(void); +void LIBRARY_exit(void); + +LIBRARY *LIBRARY_create(const char *path); +void LIBRARY_delete(LIBRARY *lib); + +int LIBRARY_load(LIBRARY *lib); +void LIBRARY_unload(LIBRARY *lib); + +void LIBRARY_declare(GB_DESC **desc); +void LIBRARY_declare_one(GB_DESC *desc); + +bool LIBRARY_get_interface_by_name(const char *name, int version, void *iface); +void LIBRARY_get_interface(LIBRARY *lib, int version, void *iface); + +void LIBRARY_exec(LIBRARY *lib, int argc, char **argv); +void LIBRARY_after_init(LIBRARY *lib); + +void LIBRARY_before_fork(LIBRARY *lib); + +#endif diff --git a/main/gbx/gbx_local.c b/main/gbx/gbx_local.c new file mode 100644 index 00000000..698eb76c --- /dev/null +++ b/main/gbx/gbx_local.c @@ -0,0 +1,2226 @@ +/*************************************************************************** + + gbx_local.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_LOCAL_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" + +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gbx_math.h" +#include "gbx_date.h" +#include "gbx_string.h" +#include "gbx_c_string.h" +#include "gbx_api.h" +#include "gb_file.h" +#include "gbx_component.h" +#include "gbx_exec.h" +#include "gbx_archive.h" + +#include "gbx_local.h" + +//#define DEBUG_LANG +//#define DEBUG_DATE + +static void add_string(const char *src, int len, bool quote); +static void add_string_thousand(const char *src, int len, int *before); + +/* System encoding*/ +char *LOCAL_encoding = NULL; + +/* If system encoding is UTF-8 */ +bool LOCAL_is_UTF8; + +/* Default 'C' localization */ +LOCAL_INFO LOCAL_default = { + '.', '.', + NULL, 0, NULL, 0, + 3, 3, + 0, + { 0, 0, '/', '/' }, + { 0, ':', ':', 0 }, + "", + "", + { LO_MONTH, LO_DAY, LO_YEAR }, + { LO_HOUR, LO_MINUTE, LO_SECOND }, + "dddd mmmm d yyyy", + "mmm d yyyy", + "mm/dd/yyyy", + "hh:nn:ss", + "hh:nn AM/PM", + "hh:nn", + "mm/dd/yyyy hh:nn:ss", + "mm/dd/yyyy hh:nn:ss", + "(#,##0.##)", + "(#,##0.##)", + "True", 4, + "False", 5, + FALSE + }; + +/* User language localization */ +LOCAL_INFO LOCAL_local = { 0 }; + +// First day of weekday +char LOCAL_first_day_of_week = -1; + +static char *_rtl_lang[] = { "ar", "fa", "he", NULL }; + +static bool _translation_loaded = FALSE; + +static LOCAL_INFO *local_current; + +static char env_LC_ALL[MAX_LANG + sizeof "LC_ALL" + 2]; +static char env_LANGUAGE[MAX_LANG + sizeof "LANGUAGE" + 2]; +static char env_LANG[MAX_LANG + sizeof "LANG" + 2]; + +static bool _currency; + +static char *_lang = NULL; + +extern char **environ; +static char **_environ; + +enum { PAD_DEFAULT, PAD_NONE, PAD_ZERO, PAD_SPACE }; +enum { LAST_NONE, LAST_DATE, LAST_TIME }; + +#define add_currency_flag(_flag) (LOCAL_local.currency_flag <<= 1, LOCAL_local.currency_flag |= (!!(_flag))) +#define test_currency_flag(_negative, _space, _before, _intl) (!!(LOCAL_local.currency_flag & (1 << ((!!_negative) + ((!!_before) << 1) + ((!!_intl) << 2))))) + +static void init_currency_flag(struct lconv *info) +{ +#ifndef OS_BSD + add_currency_flag(info->int_n_cs_precedes); // 7 + add_currency_flag(info->int_p_cs_precedes); // 6 +#else + add_currency_flag(info->n_cs_precedes); // 7 + add_currency_flag(info->p_cs_precedes); // 6 +#endif + add_currency_flag(1); // 5 + add_currency_flag(1); // 4 + add_currency_flag(info->n_cs_precedes); // 3 + add_currency_flag(info->p_cs_precedes); // 2 + add_currency_flag(info->n_sep_by_space); // 1 + add_currency_flag(info->p_sep_by_space); // 0 +} + +static bool is_currency_before(bool negative, bool intl) +{ + return test_currency_flag(negative, FALSE, TRUE, intl); +} + +static bool is_currency_space(bool negative, bool intl) +{ + //fprintf(stderr, "%02X & %02X\n", LOCAL_local.currency_flag, (1 << ((!!negative) + ((!!0) << 1) + ((!!intl) << 2)))); + return test_currency_flag(negative, TRUE, FALSE, intl); +} + +static void my_setenv(const char *name, const char *value, char *ptr) +{ + snprintf(ptr, MAX_LANG + strlen(name) + 2, "%s=%s", name, value); + putenv(ptr); +} + +static void begin(void) +{ + COMMON_buffer_init(COMMON_buffer, COMMON_BUF_MAX - 4); +} + +static void end(char **str, int *len) +{ + *(COMMON_get_current()) = 0; + *str = COMMON_buffer; + *len = COMMON_pos; +} + +static void add_thousand_sep(int *before) +{ + int group; + const char *thsep; + int lthsep; + + if (before == NULL) + return; + + thsep = _currency ? local_current->currency_thousand_sep : local_current->thousand_sep; + lthsep = _currency ? local_current->len_currency_thousand_sep : local_current->len_thousand_sep; + + if (thsep && thsep) + { + group = _currency ? local_current->currency_group_size : local_current->group_size; + + if (group > 0 && (*before > 1) && ((*before - 1) == (((*before - 1) / group) * group))) + { + if (COMMON_pos > 0 && (COMMON_get_current()[-1] == ' ')) + COMMON_put_char(' '); + else + add_string(thsep, lthsep, FALSE); + } + } + + (*before)--; +} + +static void add_string(const char *src, int len, bool quote) +{ + char c; + + if (len <= 0) + len = strlen(src); + + while (len > 0) + { + c = *src++; + len--; + + if (quote && c == '\\') + { + if (len > 0) + { + c = *src++; + len--; + } + else + break; + } + + COMMON_put_char(c); + } +} + +static void add_string_thousand(const char *src, int len, int *before) +{ + char c; + + if (len <= 0) + len = strlen(src); + + while (len > 0) + { + c = *src++; + len--; + + COMMON_put_char(c); + + add_thousand_sep(before); + } +} + +static void add_unicode(uint unicode) +{ + char str[8]; + int len; + + if (unicode == 0) + return; + + STRING_utf8_from_unicode(unicode, str); + len = STRING_utf8_get_char_length(*str); + + if (COMMON_pos >= len && strncmp(&COMMON_buffer[COMMON_pos - len], str, len) == 0) + return; + + add_string(str, len, FALSE); +} + +static void add_currency(const char *sym) +{ + const char *p = sym; + char c; + + for(;;) + { + c = *p++; + if (c == 0) + return; + if (c != ' ') + COMMON_put_char(c); + } +} + +static void add_digit_char(char c, int count, int *before) +{ + while (count > 0) + { + COMMON_put_char(c); + count--; + + add_thousand_sep(before); + } +} + +static void add_zero(int count, int *before) +{ + add_digit_char('0', count, before); +} + + +static void add_sign(char mode, int sign, bool after) +{ + if (after && mode != '(') + return; + + if (sign < 0) + { + if (mode == '(') + COMMON_put_char(after ? ')' : '('); + else + COMMON_put_char('-'); + } + else if (mode != 0 && mode != '(') + { + if (sign > 0) + COMMON_put_char(mode); + else + COMMON_put_char(' '); + } +} + + +static char *get_languages(void) +{ + char *lang; + char *lang_list = NULL; + char *src; + char *p; + + lang = STRING_new_temp_zero(LOCAL_get_lang()); + p = index(lang, '.'); + if (p) *p = 0; + + lang_list = STRING_add_zero(lang_list, lang); + lang_list = STRING_add_char(lang_list, ':'); + + src = index(lang, '_'); + if (src) + { + *src = 0; + lang_list = STRING_add_zero(lang_list, lang); + lang_list = STRING_add_char(lang_list, ':'); + } + + lang_list = STRING_add_zero(lang_list, lang); + lang_list = STRING_add(lang_list, "_*", 2); + + #ifdef DEBUG_LANG + fprintf(stderr, "Languages = %s\n", lang_list); + #endif + + return lang_list; +} + + +static void free_local_info(void) +{ + STRING_free(&LOCAL_local.standard_date); + STRING_free(&LOCAL_local.long_date); + STRING_free(&LOCAL_local.medium_date); + STRING_free(&LOCAL_local.short_date); + STRING_free(&LOCAL_local.long_time); + STRING_free(&LOCAL_local.medium_time); + STRING_free(&LOCAL_local.short_time); + STRING_free(&LOCAL_local.general_date); + STRING_free(&LOCAL_local.general_currency); + STRING_free(&LOCAL_local.intl_currency); + STRING_free(&LOCAL_local.true_str); + STRING_free(&LOCAL_local.false_str); + CLEAR(&LOCAL_local); +} + +static const char *fix_separator(const char *str) +{ + if (!str || !*str) + return " "; + + if ((uchar)str[0] == 0xC2 && (uchar)str[1] == 0xA0 && str[2] == 0) + return " "; + + if ((uchar)str[0] == 0xE2 && (uchar)str[1] == 0x80 && (uchar)str[2] == 0xAF && str[3] == 0) + return " "; + + return str[1] ? "_" : str; +} + +#define FORMAT_ADD(_dst, _str) (LOCAL_local._dst = STRING_add_zero(LOCAL_local._dst, (_str))) +#define FORMAT_ADD_CHAR(_dst, _char) (LOCAL_local._dst = STRING_add_char(LOCAL_local._dst, (_char))) + +static void format_add_len(char **pdst, const char *str, int len) +{ + if (len == 1 && isalpha(*str)) + *pdst = STRING_add_char(*pdst, '\\'); + *pdst = STRING_add(*pdst, str, len); +} + +#define FORMAT_ADD_LEN(_dst, _str, _len) (format_add_len(&LOCAL_local._dst, (_str), (_len))) + +static void format_add_sep(char **pdst, const char *str, char sep) +{ + if (*pdst) + *pdst = STRING_add_char(*pdst, sep); + *pdst = STRING_add_zero(*pdst, str); +} + +#define FORMAT_ADD_SEP(_dst, _str, _sep) format_add_sep(&LOCAL_local._dst, (_str), (_sep)) + +static bool order_add(uchar *p, uchar type) +{ + int i; + + for (i = 0; i < 3; i++) + { + if (p[i] == type) + return FALSE; + else if (p[i] == 0) + { + p[i] = type; + return TRUE; + } + } + + return FALSE; +} + +#define ORDER_DATE_ADD(_type) order_add(LOCAL_local.date_order, (_type)) +#define ORDER_TIME_ADD(_type) order_add(LOCAL_local.time_order, (_type)) + +static char *get_strftime(int which) +{ + char *fmt = NULL; + char *p = nl_langinfo(which); + char c; + + for(;;) + { + c = *p++; + if (!c) + break; + if (c != '%') + { + fmt = STRING_add_char(fmt, c); + continue; + } + + c = *p++; + if (c == 'D') + fmt = STRING_add_zero(fmt, "%m/%d/%y"); + else if (c == 'F') + fmt = STRING_add_zero(fmt, "%Y-%m-%d"); + else if (c == 'R') + fmt = STRING_add_zero(fmt, "%H:%M"); + else if (c == 'r') + fmt = STRING_add_zero(fmt, "%I:%M:%S %p"); + else if (c == 'T') + fmt = STRING_add_zero(fmt, "%H:%M:%S"); + else + { + fmt = STRING_add_char(fmt, '%'); + fmt = STRING_add_char(fmt, c); + } + } + + #ifdef DEBUG_DATE + fprintf(stderr, "fmt = %s\n", fmt); + #endif + return fmt; +} + +static void fill_local_info(void) +{ + struct lconv *info; + char *p; + char *fmt; + char c; + char pad; + char last; + uchar last_elt; + char *codeset; + const char *lang; + int len; + int last_long_date; + + free_local_info(); + + /* local encoding */ + + if (!LOCAL_is_UTF8) + STRING_free(&LOCAL_encoding); + + codeset = nl_langinfo(CODESET); + if (!codeset || !*codeset) + codeset = "US-ASCII"; + + LOCAL_is_UTF8 = (strcasecmp(codeset, "UTF-8") == 0); + if (LOCAL_is_UTF8) + LOCAL_encoding = SC_UTF8; + else + LOCAL_encoding = STRING_new_zero(codeset); + + #ifdef DEBUG_LANG + fprintf(stderr, "LOCAL_encoding = %s\n", LOCAL_encoding == SC_UTF8 ? "UTF-8" : LOCAL_encoding); + #endif + + /* Numeric information */ + + info = localeconv(); + + //fprintf(stderr, "'%s' '%s' %d %d\n", info->thousands_sep, info->mon_thousands_sep, *info->thousands_sep, *info->grouping); + //fprintf(stderr, "'%s' '%s'\n", nl_langinfo(THOUSANDS_SEP), nl_langinfo(MON_THOUSANDS_SEP)); + + LOCAL_local.decimal_point = *(info->decimal_point); + LOCAL_local.thousand_sep = fix_separator(info->thousands_sep); + LOCAL_local.len_thousand_sep = strlen(LOCAL_local.thousand_sep); + + LOCAL_local.group_size = *(info->grouping); + if (LOCAL_local.group_size == 0) + LOCAL_local.group_size = 3; + + /*LOCAL_local.currency_symbol = STRING_conv_to_UTF8(info->currency_symbol, 0); + STRING_ref(LOCAL_local.currency_symbol); + LOCAL_local.intl_currency_symbol = STRING_conv_to_UTF8(info->int_curr_symbol, 0); + STRING_ref(LOCAL_local.intl_currency_symbol);*/ + + // Date & time format + + #ifdef DEBUG_DATE + fprintf(stderr, "D_T_FMT: %s\n", nl_langinfo(D_T_FMT)); + fprintf(stderr, "D_FMT: %s\n", nl_langinfo(D_FMT)); + fprintf(stderr, "T_FMT: %s\n", nl_langinfo(T_FMT)); + #endif + + // gb.GeneralDate / gb.LongDate + + p = fmt = get_strftime(D_T_FMT); + last = LAST_NONE; + last_elt = 0; + last_long_date = 0; + + for(;;) + { + c = *p++; + if (!c) + break; + + if (c == '%') + { + c = *p++; + if (c != '%') + { + pad = PAD_DEFAULT; + + while (c && !isalpha(c)) + { + if (c == '-') + pad = PAD_NONE; + else if (c == '_') + pad = PAD_SPACE; + else if (c == '0') + pad = PAD_ZERO; + c = *p++; + } + + if (c == 'E' || c == 'O') + c = *p++; + + switch (c) + { + case 'a': case 'A': + FORMAT_ADD(general_date, (c == 'a') ? "ddd" : "dddd"); + FORMAT_ADD(long_date, "dddd"); + last = LAST_DATE; + break; + + case 'b': case 'h': case 'B': + FORMAT_ADD(general_date, (c == 'b') ? "mmm" : "mmmm"); + FORMAT_ADD(long_date, "mmmm"); + last = LAST_DATE; + break; + + case 'd': case 'e': + if (pad == PAD_DEFAULT) + pad = (c == 'e') ? PAD_NONE : PAD_ZERO; + FORMAT_ADD(general_date, (pad == PAD_ZERO) ? "dd" : "d"); + FORMAT_ADD(long_date, (pad == PAD_ZERO) ? "dd" : "d"); + last = LAST_DATE; + break; + + case 'H': case 'I': case 'k': case 'l': + if (pad == PAD_DEFAULT) + pad = (c == 'k' || c == 'l') ? PAD_NONE : PAD_ZERO; + FORMAT_ADD(general_date, (pad == PAD_ZERO) ? "hh" : "h"); + last = LAST_TIME; + break; + + case 'm': + if (pad == PAD_DEFAULT) + pad = PAD_ZERO; + FORMAT_ADD(general_date, (pad == PAD_ZERO) ? "mm" : "m"); + FORMAT_ADD(long_date, (pad == PAD_ZERO) ? "mm" : "m"); + last = LAST_DATE; + break; + + case 'M': + if (pad == PAD_DEFAULT) + pad = PAD_ZERO; + FORMAT_ADD(general_date, (pad == PAD_ZERO) ? "nn" : "n"); + last = LAST_TIME; + break; + + case 'P': case 'p': + FORMAT_ADD(general_date, (c == 'P') ? "am/pm" : "AM/PM"); + last = LAST_TIME; + break; + + case 'S': + if (pad == PAD_DEFAULT) + pad = PAD_ZERO; + FORMAT_ADD(general_date, (pad == PAD_ZERO) ? "ss" : "s"); + last = LAST_TIME; + break; + + case 'Y': case 'y': + FORMAT_ADD(general_date, "yyyy"); + FORMAT_ADD(long_date, "yyyy"); + last = LAST_DATE; + break; + + case 'z': + FORMAT_ADD(general_date, "t"); + FORMAT_ADD(long_date, "t"); + last = LAST_DATE; + break; + + case 'Z': + FORMAT_ADD(general_date, "tt"); + FORMAT_ADD(long_date, "t"); + last = LAST_DATE; + break; + } + + if (last == LAST_DATE) + last_long_date = STRING_length(LOCAL_local.long_date); + + continue; + } + } + + len = STRING_utf8_get_char_length(c); + p--; + + FORMAT_ADD_LEN(general_date, p, len); + + if (last == LAST_DATE) + FORMAT_ADD_LEN(long_date, p, len); + + p += len; + } + + STRING_free(&fmt); + + // Remove final separators of long_date + + fmt = LOCAL_local.long_date; + LOCAL_local.long_date = STRING_new(fmt, last_long_date); + STRING_free(&fmt); + + // Other date formats + + p = fmt = get_strftime(D_FMT); + last = LAST_NONE; + last_elt = 0; + + for(;;) + { + c = *p++; + if (!c) + break; + + if (c == '%') + { + c = *p++; + if (c != '%') + { + pad = PAD_DEFAULT; + + while (c && !isalpha(c)) + { + if (c == '-') + pad = PAD_NONE; + else if (c == '_') + pad = PAD_SPACE; + else if (c == '0') + pad = PAD_ZERO; + c = *p++; + } + + if (c == 'E' || c == 'O') + c = *p++; + + switch (c) + { + case 'a': case 'A': + if (ORDER_DATE_ADD(LO_DAY)) + { + FORMAT_ADD_SEP(medium_date, "dd", '/'); + FORMAT_ADD_SEP(short_date, "d", '/'); + last = LAST_DATE; + last_elt = LO_DAY; + } + break; + + case 'b': case 'h': case 'B': + if (ORDER_DATE_ADD(LO_MONTH)) + { + FORMAT_ADD_SEP(medium_date, "mm", '/'); + FORMAT_ADD_SEP(short_date, "m", '/'); + last = LAST_DATE; + last_elt = LO_MONTH; + } + break; + + case 'd': case 'e': + if (pad == PAD_DEFAULT) + pad = (c == 'e') ? PAD_NONE : PAD_ZERO; + if (ORDER_DATE_ADD(LO_DAY)) + { + FORMAT_ADD_SEP(medium_date, "dd", '/'); + FORMAT_ADD_SEP(short_date, "d", '/'); + last = LAST_DATE; + last_elt = LO_DAY; + } + break; + + case 'm': + if (pad == PAD_DEFAULT) + pad = PAD_ZERO; + if (ORDER_DATE_ADD(LO_MONTH)) + { + FORMAT_ADD_SEP(medium_date, "mm", '/'); + FORMAT_ADD_SEP(short_date, "m", '/'); + last = LAST_DATE; + last_elt = LO_MONTH; + } + break; + + case 'Y': case 'y': + FORMAT_ADD_SEP(medium_date, "yyyy", '/'); + FORMAT_ADD_SEP(short_date, "yyyy", '/'); + if (ORDER_DATE_ADD(LO_YEAR)) + { + last = LAST_DATE; + last_elt = LO_YEAR; + } + break; + + default: + last = LAST_NONE; + } + + if (last == LAST_DATE) + LOCAL_local.date_tail_sep = FALSE; + + continue; + } + } + + len = STRING_utf8_get_char_length(c); + p--; + + if (last == LAST_DATE) + { + if (LOCAL_local.date_sep[last_elt] == 0) + LOCAL_local.date_sep[last_elt] = STRING_utf8_to_unicode(p, len); + LOCAL_local.date_tail_sep = c != ' '; + } + + p += len; + } + + if (LOCAL_local.date_tail_sep) + { + FORMAT_ADD_CHAR(medium_date, '/'); + FORMAT_ADD_CHAR(short_date, '/'); + } + + LOCAL_local.date_many_sep = LOCAL_local.date_sep[LOCAL_local.date_order[0]] != LOCAL_local.date_sep[LOCAL_local.date_order[1]]; + + STRING_free(&fmt); + + // Other time formats + + p = fmt = get_strftime(T_FMT); + last = LAST_NONE; + last_elt = 0; + + for(;;) + { + c = *p++; + if (!c) + break; + + if (c == '%') + { + c = *p++; + if (c != '%') + { + pad = PAD_DEFAULT; + + while (c && !isalpha(c)) + { + if (c == '-') + pad = PAD_NONE; + else if (c == '_') + pad = PAD_SPACE; + else if (c == '0') + pad = PAD_ZERO; + c = *p++; + } + + if (c == 'E' || c == 'O') + c = *p++; + + switch (c) + { + case 'H': case 'I': case 'k': case 'l': + if (pad == PAD_DEFAULT) + pad = (c == 'k' || c == 'l') ? PAD_NONE : PAD_ZERO; + if (ORDER_TIME_ADD(LO_HOUR)) + { + FORMAT_ADD_SEP(long_time, "hh", ':'); + FORMAT_ADD_SEP(medium_time, (pad == PAD_ZERO) ? "hh" : "h", ':'); + FORMAT_ADD_SEP(short_time, "h", ':'); + last = LAST_TIME; + last_elt = LO_HOUR; + } + break; + + case 'M': + if (pad == PAD_DEFAULT) + pad = PAD_ZERO; + if (ORDER_TIME_ADD(LO_MINUTE)) + { + FORMAT_ADD_SEP(long_time, "nn", ':'); + FORMAT_ADD_SEP(medium_time, (pad == PAD_ZERO) ? "nn" : "n", ':'); + FORMAT_ADD_SEP(short_time, "nn", ':'); + last = LAST_TIME; + last_elt = LO_MINUTE; + } + break; + + case 'P': case 'p': + FORMAT_ADD_SEP(medium_time, (c == 'P') ? "am/pm" : "AM/PM", ' '); + break; + + case 'S': + if (pad == PAD_DEFAULT) + pad = PAD_ZERO; + if (ORDER_TIME_ADD(LO_SECOND)) + { + FORMAT_ADD_SEP(long_time, "ss", ':'); + FORMAT_ADD_SEP(medium_time, (pad == PAD_ZERO) ? "ss" : "s", ':'); + last = LAST_TIME; + last_elt = LO_SECOND; + } + break; + + default: + last = LAST_NONE; + } + + if (last == LAST_TIME) + LOCAL_local.time_tail_sep = FALSE; + + continue; + } + } + + len = STRING_utf8_get_char_length(c); + p--; + + if (last == LAST_TIME) + { + if (LOCAL_local.time_sep[last_elt] == 0) + LOCAL_local.time_sep[last_elt] = STRING_utf8_to_unicode(p, len); + LOCAL_local.time_tail_sep = c != ' '; + } + + p += len; + } + + if (LOCAL_local.time_tail_sep) + { + FORMAT_ADD_CHAR(long_time, ':'); + FORMAT_ADD_CHAR(medium_time, ':'); + FORMAT_ADD_CHAR(short_time, ':'); + } + + LOCAL_local.time_many_sep = LOCAL_local.time_sep[LOCAL_local.time_order[0]] != LOCAL_local.time_sep[LOCAL_local.time_order[1]]; + + STRING_free(&fmt); + + LOCAL_local.standard_date = STRING_copy(LOCAL_local.medium_date); + LOCAL_local.standard_date = STRING_add_char(LOCAL_local.standard_date, ' '); + LOCAL_local.standard_date = STRING_add_string(LOCAL_local.standard_date, LOCAL_local.long_time); + + #ifdef DEBUG_DATE + fprintf(stderr, "date_tail_sep = %d\n", LOCAL_local.date_tail_sep); + fprintf(stderr, "time_tail_sep = %d\n\n", LOCAL_local.time_tail_sep); + + fprintf(stderr, "general_date: '%s'\n", LOCAL_local.general_date); + fprintf(stderr, "long_date: '%s'\n", LOCAL_local.long_date); + fprintf(stderr, "medium_date: '%s'\n", LOCAL_local.medium_date); + fprintf(stderr, "short_date: '%s'\n", LOCAL_local.short_date); + fprintf(stderr, "long_time: '%s'\n", LOCAL_local.long_time); + fprintf(stderr, "medium_time: '%s'\n", LOCAL_local.medium_time); + fprintf(stderr, "short_time: '%s'\n", LOCAL_local.short_time); + #endif + + // Fix missing seconds + + /*if (!got_second) + { + *tp++ = LO_SECOND; + stradd_sep(long_time, "ss", ':'); + }*/ + + // Fix the french date separator + + lang = LOCAL_get_lang(); + if (strcmp(lang, "fr") == 0 || strncmp(lang, "fr_", 3) == 0) + LOCAL_local.date_sep[LO_DAY] = LOCAL_local.date_sep[LO_MONTH] = '/'; + + /*stradd_sep(general_date, LOCAL_local.long_time, ' '); + am_pm = nl_langinfo(AM_STR); + if (am_pm && *am_pm) + { + am_pm = nl_langinfo(PM_STR); + if (am_pm && *am_pm) + { + stradd_sep(medium_time, "AM/PM", ' '); + } + }*/ + + // Currency format + + LOCAL_local.currency_thousand_sep = fix_separator(info->mon_thousands_sep); + LOCAL_local.len_currency_thousand_sep = strlen(LOCAL_local.currency_thousand_sep); + + LOCAL_local.currency_group_size = *(info->mon_grouping); + if (LOCAL_local.currency_group_size == 0) + LOCAL_local.currency_group_size = 3; + + LOCAL_local.currency_decimal_point = *(info->mon_decimal_point); + LOCAL_local.currency_symbol = info->currency_symbol; + LOCAL_local.intl_currency_symbol = info->int_curr_symbol; + + LOCAL_local.general_currency = STRING_new_zero("($,0."); + LOCAL_local.general_currency = STRING_add(LOCAL_local.general_currency, "########", Min(8, info->frac_digits)); + LOCAL_local.general_currency = STRING_add_char(LOCAL_local.general_currency, ')'); + + LOCAL_local.intl_currency = STRING_new_zero("($$,0."); + LOCAL_local.intl_currency = STRING_add(LOCAL_local.intl_currency, "########", Min(8, info->int_frac_digits)); + LOCAL_local.intl_currency = STRING_add_char(LOCAL_local.intl_currency, ')'); + + init_currency_flag(info); + + LOCAL_local.true_str = STRING_new_zero(LOCAL_gettext(LOCAL_default.true_str)); + LOCAL_local.len_true_str = STRING_length(LOCAL_local.true_str); + LOCAL_local.false_str = STRING_new_zero(LOCAL_gettext(LOCAL_default.false_str)); + LOCAL_local.len_false_str = STRING_length(LOCAL_local.false_str); +} + + +void LOCAL_init(void) +{ + _environ = environ; + LOCAL_set_lang(NULL); +} + +void LOCAL_exit(void) +{ + if (environ == _environ) + { + unsetenv("LANG"); + unsetenv("LC_ALL"); + unsetenv("LANGUAGE"); + } + + if (!LOCAL_is_UTF8) + STRING_free(&LOCAL_encoding); + + STRING_free(&_lang); + free_local_info(); +} + + +const char *LOCAL_get_lang(void) +{ + char *lang; + + if (!_lang) + { + lang = getenv("LC_ALL"); + if (!lang || !*lang) + lang = getenv("LANG"); + if (!lang || !*lang) + lang = "C"; + _lang = STRING_new_zero(lang); + } + + return _lang; +} + +void LOCAL_set_lang(const char *lang) +{ + char **l; + int rtl; + char *var; + + if (lang && (strlen(lang) > MAX_LANG)) + THROW(E_ARG); + + #ifdef DEBUG_LANG + fprintf(stderr, "******** LOCAL_set_lang: %s\n", lang ? lang : "(null)"); + #endif + + if (lang && *lang) + { + my_setenv("LANG", lang, env_LANG); + my_setenv("LC_ALL", lang, env_LC_ALL); + } + + STRING_free(&_lang); + lang = LOCAL_get_lang(); + + #ifdef DEBUG_LANG + fprintf(stderr, "lang = %s\n", lang); + #endif + + my_setenv("LANG", lang, env_LANG); + my_setenv("LC_ALL", lang, env_LC_ALL); + + if (getenv("LANGUAGE")) + my_setenv("LANGUAGE", lang, env_LANGUAGE); + + if (setlocale(LC_ALL, "")) + { + _translation_loaded = FALSE; + COMPONENT_translation_must_be_reloaded(); + CLASS_translation_must_be_reloaded(); + } + else + { + char *err = strerror(errno); + ERROR_warning("cannot switch to language '%s': %s. Did you install the corresponding locale packages?", lang ? lang : LOCAL_get_lang(), err); + setlocale(LC_ALL, "C"); + } + + DATE_init_local(); + fill_local_info(); + + /* If language is right to left written */ + + rtl = FALSE; + for (l = _rtl_lang; *l; l++) + { + if (strncmp(*l, lang, 2) == 0) + { + rtl = TRUE; + break; + } + } + + var = getenv("GB_REVERSE"); + if (var && !(var[0] == '0' && var[1] == 0)) + rtl = !rtl; + + HOOK(lang)(lang, rtl); + LOCAL_local.rtl = rtl; +} + + +static int int_to_string(uint64_t nbr, char **addr) +{ + static char buf[32]; + char *ptr; + int digit, len; + bool neg; + + len = 0; + ptr = &buf[sizeof(buf)]; + + if (nbr == 0) + { + ptr -= 2; + *addr = ptr; + ptr[0] = '0'; + ptr[1] = 0; + return 1; + } + + neg = (nbr & (1LL << 63)) != 0; + + if (neg) + nbr = 1 + ~nbr; + + while (nbr > 0) + { + digit = nbr % 10; + nbr /= 10; + + ptr--; + len++; + + *ptr = '0' + digit; + } + + if (neg) + { + len++; + ptr--; + *ptr = '-'; + } + + *addr = ptr; + return len; +} + +const char *LOCAL_get_format(LOCAL_INFO *info, int type) +{ + switch(type) + { + case LF_GENERAL_NUMBER: return "0.###############E@#"; + case LF_SHORT_NUMBER: return "0.#######E@#"; + case LF_FIXED: return "0.00"; + case LF_PERCENT: return "###%"; + case LF_SCIENTIFIC: return "0.################E+0"; + case LF_CURRENCY: return info->general_currency; + case LF_INTERNATIONAL: return info->intl_currency; + case LF_GENERAL_DATE: return info->general_date; + case LF_LONG_DATE: return info->long_date; + case LF_MEDIUM_DATE: return info->medium_date; + case LF_SHORT_DATE: return info->short_date; + case LF_LONG_TIME: return info->long_time; + case LF_MEDIUM_TIME: return info->medium_time; + case LF_SHORT_TIME: return info->short_time; + default: return NULL; + } +} + +bool LOCAL_format_number(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, bool local) +{ + int i; + unsigned char c; + int n; + char *buf_addr; + int pos; + int thousand; + int *thousand_ptr; + char sign; + bool comma; + bool point; + int before, before_zero; + int after, after_zero; + char exponent; + int exp_zero; + bool exp_sign; + + double fabs_number; + int number_sign; + uint64_t mantisse; + uint64_t power; + double number_mant; + int number_exp; + int number_real_exp; + int ndigit; + int pos_first_digit; + + bool intl_currency; + + if (local) + local_current = &LOCAL_local; + else + local_current = &LOCAL_default; + + switch(fmt_type) + { + case LF_USER: + break; + + case LF_STANDARD: + fmt_type = LF_GENERAL_NUMBER; + // continue + + default: + fmt = LOCAL_get_format(local_current, fmt_type); + if (!fmt) + return TRUE; + } + + if (len_fmt == 0) + len_fmt = strlen(fmt); + + if (len_fmt >= COMMON_BUF_MAX) + return TRUE; + + sign = 0; + comma = FALSE; + before = 0; + before_zero = 0; + point = FALSE; + after = 0; + after_zero = 0; + exponent = 0; + exp_sign = FALSE; + exp_zero = 0; + _currency = FALSE; + intl_currency = FALSE; + fabs_number = fabs(number); + + begin(); + + // Search for the first formatting character + + for (i = 0; i < len_fmt; i++) + { + c = fmt[i]; + if (c == '\\') + { + i++; + continue; + } + + if (c == '-' || c == '+' || c == '#' || c == '0' || c == '.' || c == ',' || c == '(' || c == '$') + break; + } + + if (i >= len_fmt) + return TRUE; + + pos = i; + if (pos > 0) + add_string(fmt, pos, TRUE); + + // Search if there is a percent format character + + for (i = pos; i < len_fmt; i++) + { + c = fmt[i]; + if (c == '\\') + { + i++; + continue; + } + + if (c == '%') + { + number *= 100; + break; + } + } + + // specify the sign + + if (fmt[pos] == '-') + { + sign = ' '; + pos++; + } + else if (fmt[pos] == '+') + { + sign = '+'; + pos++; + } + else if (fmt[pos] == '(') + { + sign = '('; + pos++; + } + + if (pos >= len_fmt) + return TRUE; + + // currency + + if (fmt[pos] == '$') + { + _currency = TRUE; + pos++; + + if (fmt[pos] == '$') + { + intl_currency = TRUE; + pos++; + } + } + + // decimal digits + + for(; pos < len_fmt; pos++) + { + c = fmt[pos]; + + if (c == ',') + { + comma = TRUE; + continue; + } + + if (c == '#' || c == '0') + { + before++; + if (c == '0' || before_zero > 0) + before_zero++; + continue; + } + + break; + } + + if (before > 0 && before_zero == 0) + before_zero = 1; + + if (pos >= len_fmt) + goto _FORMAT; + + // decimal point + + if (fmt[pos] != '.') + goto _FORMAT; + + pos++; + point = TRUE; + + if (pos >= len_fmt) + goto _FORMAT; + + // digits after decimal point + + for(; pos < len_fmt; pos++) + { + c = fmt[pos]; + + if (c == '#' || c == '0') + { + after++; + if (c == '0') + after_zero = after; + continue; + } + + break; + } + + if (pos >= len_fmt) + goto _FORMAT; + + // exponent + + if (fmt[pos] == 'e' || fmt[pos] == 'E') + { + bool exp_optional = FALSE; + + exponent = fmt[pos]; + + pos++; + if (pos >= len_fmt) + return TRUE; + + if (fmt[pos] == '-' || fmt[pos] == '+' || fmt[pos] == '@') + { + exp_sign = TRUE; + exp_optional = fmt[pos] == '@'; + pos++; + } + + if (pos >= len_fmt) + return TRUE; + + for(; pos < len_fmt; pos++) + { + c = fmt[pos]; + + if (c == '#' || c == '0') + { + if (c == '0' || exp_zero > 0) + exp_zero++; + continue; + } + + break; + } + + if (exp_optional) + { + if (number == 0.0 || (fabs_number > 1E-4 && fabs_number < 1E10)) + exponent = 0; + } + } + +_FORMAT: + + if (before == 0 && after == 0) + return TRUE; + + // sign + + number_sign = fsgn(number); + + add_sign(sign, number_sign, FALSE); + + // currency (before) + + if (_currency && is_currency_before(number_sign < 0, intl_currency)) + { + add_currency(intl_currency ? local_current->intl_currency_symbol : local_current->currency_symbol); + if (is_currency_space(number_sign < 0, intl_currency)) + COMMON_put_char(' '); + } + + // We note where the first digit will be printed + + pos_first_digit = COMMON_pos; + + // the number + + if (isfinite(number)) + { + number_mant = frexp10(fabs(number), &number_exp); + ndigit = after; + + if (!exponent) + ndigit += number_exp; + else + ndigit++; + + ndigit = MinMax(ndigit, 0, MAX_FLOAT_DIGIT); + //fprintf(stderr, "number_mant = %.24g number_exp = %d ndigit = %d\n", number_mant, number_exp, ndigit); + + power = pow10_uint64_p(ndigit + 1); + + mantisse = number_mant * power; + if ((mantisse % 10) >= 5) + mantisse += 10; + + //fprintf(stderr, "-> power = %" PRId64 " mantisse = %" PRId64 "\n", power, mantisse); + + if (mantisse >= power) + { + ndigit = int_to_string(mantisse, &buf_addr); + buf_addr--; + buf_addr[0] = buf_addr[1]; + buf_addr[1] = '.'; + ndigit++; + } + else + { + ndigit = int_to_string(mantisse, &buf_addr); + buf_addr -= 2; + buf_addr[0] = '0'; + buf_addr[1] = '.'; + ndigit += 2; + } + + ndigit--; + buf_addr[ndigit] = 0; + + // 0.0 <= number_mant < 1.0 + + //number_exp++; /* simplifie les choses */ + + number_real_exp = number_exp; + if (exponent) + number_exp = number != 0.0; + + // should return "0[.]...", or "1[.]..." if the number is rounded up. + + if (buf_addr[0] == '1') // the number has been rounded up. + { + if (exponent) + number_real_exp++; + else + number_exp++; + } + + if (ndigit > 1) // so there is a point + { + if (buf_addr[0] == '0') + { + buf_addr += 2; + ndigit -= 2; + } + else + { + buf_addr[1] = buf_addr[0]; + ndigit--; + buf_addr++; + } + + while (ndigit > 0 && buf_addr[ndigit - 1] == '0') + ndigit--; + } + + // digits before point + + thousand = Max(before, Max(before_zero, number_exp)); + thousand_ptr = comma ? &thousand : NULL; + + if (number_exp > 0) + { + add_digit_char(' ', before - Max(before_zero, number_exp), thousand_ptr); + add_zero(before_zero - number_exp, thousand_ptr); + + add_string_thousand(buf_addr, Min(number_exp, ndigit), thousand_ptr); + + if (number_exp > ndigit) + add_zero(number_exp - ndigit, thousand_ptr); + } + else + { + add_digit_char(' ', before - before_zero, thousand_ptr); + add_zero(before_zero, thousand_ptr); + } + + // decimal point + + if (point) + COMMON_put_char(local_current->decimal_point); + + // digits after the decimal point + + if ((ndigit - number_exp) > 0) + { + if (number_exp < 0) + { + n = Min(after, (- number_exp)); + if (n == after) + { + add_zero(after_zero, NULL); + goto _EXPONENT; + } + else + { + add_zero(n, NULL); + after -= n; + after_zero -= n; + } + } + + if (number_exp > 0) + { + buf_addr += number_exp; + ndigit -= number_exp; + } + + n = Min(ndigit, after); + if (n > 0) + { + add_string(buf_addr, n, FALSE); + after -= n; + after_zero -= n; + } + + if (after_zero > 0) + add_zero(after_zero, NULL); + } + else + add_zero(after_zero, NULL); + + _EXPONENT: + + // The decimal point is removed if it is located at the end + + COMMON_pos--; + if (COMMON_look_char() != local_current->decimal_point) + COMMON_pos++; + + // exponent + + if (exponent != 0) // && number != 0.0) + { + number_real_exp--; + + COMMON_put_char(exponent); + if (exp_sign && number_real_exp >= 0) + COMMON_put_char('+'); + if (number_real_exp < 0) + { + COMMON_put_char('-'); + number_real_exp = (- number_real_exp); + } + n = int_to_string(number_real_exp, &buf_addr); + while (exp_zero > n) + { + COMMON_put_char('0'); + exp_zero--; + } + add_string(buf_addr, n, FALSE); + } + } + else // isfinite + { + if (isnan(number)) + add_string("NaN", 3, FALSE); + else if (isinf(number)) + add_string("Inf", 3, FALSE); + } + + // currency (after) + + if (_currency && !is_currency_before(number_sign < 0, intl_currency)) + { + if (is_currency_space(number_sign < 0, intl_currency)) + COMMON_put_char(' '); + add_currency(intl_currency ? local_current->intl_currency_symbol : local_current->currency_symbol); + } + + // The last format brace is ignored + + if (sign == '(' && fmt[pos] == ')') + pos++; + + // The sign after + + add_sign(sign, number_sign, TRUE); + + // print at least a zero + + if (COMMON_pos == pos_first_digit) + COMMON_put_char('0'); + + // format suffix + + if (pos < len_fmt) + add_string(&fmt[pos], len_fmt - pos, TRUE); + + // return the result + + end(str, len_str); + return FALSE; +} + +static void add_strftime(const char *format, struct tm *tm) +{ + int n; + + n = strftime(COMMON_get_current(), COMMON_get_size_left(), format, tm); + COMMON_pos += n; +} + + +static void add_number(int value, int pad) +{ + static char temp[8] = { 0 }; + int i, n; + bool minus = FALSE; + + if (value < 0) + { + value = (-value); + minus = TRUE; + } + + n = 0; + for (i = 7; i >= 0; i--) + { + n++; + if (value < 10) + { + temp[i] = value + '0'; + value = 0; + if (n >= pad) + break; + } + else + { + temp[i] = (value % 10) + '0'; + value /= 10; + } + } + + if (minus) + { + i--; + temp[i] = '-'; + n++; + } + + add_string(&temp[i], n, FALSE); +} + +static bool add_date_time_token(DATE_SERIAL *date, char token, int count) +{ + struct tm tm = {0}; + char buf[8]; + int n; + bool date_token; + + date_token = token == 'd' || token == 'm' || token == 'y'; + + if ((date_token && DATE_SERIAL_has_no_date(date))) // || (!date_token && DATE_SERIAL_has_no_time(date))) + return TRUE; + + switch (token) + { + case 'd': + + if (count <= 2) + { + add_number(date->day, (count == 1 ? 0 : 2)); + } + else if (count >= 3) + { + tm.tm_wday = date->weekday; + add_strftime(count == 3 ? "%a" : "%A", &tm); + } + break; + + case 'm': + + if (count <= 2) + { + add_number(date->month, (count == 1 ? 0 : 2)); + } + else if (count >= 3) + { + tm.tm_mon = date->month - 1; + add_strftime(count == 3 ? "%b" : "%B", &tm); + } + break; + + case 'y': + + if (count <= 2 && date->year >= 1939 && date->year <= 2038) + add_number(date->year - (date->year >= 2000 ? 2000 : 1900), 2); + else + add_number(date->year, (count == 1 ? 0 : count)); + break; + + case 'h': + + add_number(date->hour, (count == 1 ? 0 : 2)); + break; + + case 'n': + + add_number(date->min, (count == 1 ? 0 : 2)); + break; + + case 's': + + add_number(date->sec, (count == 1 ? 0 : 2)); + break; + + case 'u': + + if (date->msec || count == 2) + { + if (count >= 2) + add_number(date->msec, 3); + else + { + n = snprintf(buf, sizeof(buf), "%03d", date->msec); + while (buf[n - 1] == '0') + n--; + buf[n] = 0; + add_string(buf, n, FALSE); + } + } + break; + + case 't': + + if (count <= 2) + { + time_t t = time(NULL); + localtime_r(&t, &tm); + add_strftime(count == 2 ? "%z" : "%Z", &tm); + } + break; + } + + return FALSE; +} + + +static uint find_first_separator(uint psep[4]) +{ + int i; + + for (i = 1; i <= 3; i++) + { + if (psep[i]) + return psep[i]; + } + + return 0; +} + + +static void add_date_separator(char token) +{ + uchar index; + uint sep; + + switch (token) + { + case 'y': index = LO_YEAR; break; + case 'm': index = LO_MONTH; break; + case 'd': index = LO_DAY; break; + default: return; + } + + sep = local_current->date_sep[index]; + if (!sep && !local_current->date_many_sep) + sep = find_first_separator(local_current->date_sep); + if (sep) add_unicode(sep); +} + + +static void add_time_separator(char token) +{ + uchar index; + uint sep; + + switch (token) + { + case 'h': index = LO_HOUR; break; + case 'n': index = LO_MINUTE; break; + case 's': index = LO_SECOND; break; + default: return; + } + + sep = local_current->time_sep[index]; + if (!sep && !local_current->time_many_sep) + sep = find_first_separator(local_current->time_sep); + if (sep) add_unicode(sep); +} + + +bool LOCAL_format_date(const DATE_SERIAL *date, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str) +{ + DATE_SERIAL vdate; + char c; + int pos; + int pos_ampm = -1; + struct tm date_tm; + char token; + int token_count; + bool quote; + + local_current = &LOCAL_local; + vdate = *date; + + switch(fmt_type) + { + case LF_USER: + break; + + case LF_STANDARD: + fmt = local_current->standard_date; + break; + + case LF_GENERAL_DATE: + if (date->year == 0) + { + if (date->hour == 0 && date->min == 0 && date->sec == 0) + { + *str = NULL; + *len_str = 0; + return FALSE; + } + fmt = local_current->long_time; + } + else + fmt = local_current->general_date; + break; + + default: + fmt = LOCAL_get_format(local_current, fmt_type); + if (!fmt) + return TRUE; + } + + if (len_fmt == 0) + len_fmt = strlen(fmt); + + if (len_fmt >= COMMON_BUF_MAX) + return TRUE; + + // looking for AM/PM + + for (pos = 0; pos < len_fmt - 4; pos++) + { + if (fmt[pos] == '\\') + { + pos++; + continue; + } + + if (strncasecmp(&fmt[pos], "am/pm", 5) == 0) + { + pos_ampm = pos; + if (vdate.hour > 12) + vdate.hour -= 12; + else if (vdate.hour == 0) + vdate.hour = 12; + break; + } + } + + // formatting + + begin(); + + token = 0; + token_count = 0; + quote = FALSE; + + for (pos = 0; pos < len_fmt; pos++) + { + c = fmt[pos]; + + if (c == '\\') + { + pos++; + c = fmt[pos]; + if (!c) + break; + c = 1; // something not null that cannot be a token + quote = TRUE; + } + + if (c == token) + { + token_count++; + continue; + } + + if (token) + add_date_time_token(&vdate, token, token_count); + + if (c == 'd' || c == 'm' || c == 'y' || c == 'h' || c == 'n' || c == 's' || c == 'u' || c == 't') + { + token = c; + token_count = 1; + continue; + } + + if (c == '/') + { + if (!DATE_SERIAL_has_no_date(date)) + add_date_separator(token); + } + else if (c == ':') + add_time_separator(token); + else if (pos == pos_ampm) + { + date_tm.tm_sec = date->sec; + date_tm.tm_min = date->min; + date_tm.tm_hour = date->hour; + date_tm.tm_mday = 1; + date_tm.tm_mon = 0; + date_tm.tm_year = 0; + + add_strftime((c == 'a' ? "%P" : "%p"), &date_tm); + + pos += 4; + } + else + { + if (c != ' ' || token == 0 || COMMON_pos > 0) + COMMON_put_char(quote ? fmt[pos] : c); + } + + token = 0; + } + + if (token) + add_date_time_token(&vdate, token, token_count); + + // return the result + + end(str, len_str); + return FALSE; +} + + +static void LOCAL_load_translation(ARCHIVE *arch) +{ + char *domain = NULL; + char *lang_list; + char *lang; + char *src; + char *test; + char c; + FILE *file; + char *addr; + int len; + COMPONENT *save = COMPONENT_current; + char *path; + const char *dir; + char *dst; + + /* We must force GB_LoadFile() to look in our archive, because all translation + files of one language have the same path! + */ + + if (arch) + { + domain = arch->domain; + COMPONENT_current = COMPONENT_find(arch->name); + } + + if (!domain) + domain = "gb"; + + #ifdef DEBUG_LANG + fprintf(stderr, "LOCAL_load_translation: %s / domain = %s\n", arch ? arch->name : "NULL", domain); + #endif + + lang_list = get_languages(); + + lang = strtok(lang_list, ":"); + + for(;;) + { + if (!lang) + break; + + if (*lang) + { + test = STRING_new_zero(lang); + src = test; + + for(;;) + { + c = *src; + if (c == 0 || c == '_') + break; + *src = tolower(c); + src++; + } + + test = STRING_add(test, ".mo", 3); + + dir = arch ? ".lang" : ".../.lang"; + + FILE_dir_first(dir, test, 0); + STRING_free_real(test); + + if (!FILE_dir_next(&path, &len)) + { + dst = STRING_new_zero(dir); + dst = STRING_add_char(dst, '/'); + dst = STRING_add(dst, path, len); + break; + } + + /*if (!arch) + dst = FILE_cat("...", ".lang", test, NULL); + else + dst = FILE_cat(".lang", test, NULL); + + dst = FILE_set_ext(dst, "mo"); + + #ifdef DEBUG_LANG + fprintf(stderr, "trying %s\n", dst); + #endif + + if (FILE_exist(dst)) + break;*/ + } + + lang = strtok(NULL, ":"); + } + + if (!lang) + { + #ifdef DEBUG_LANG + fprintf(stderr, "No translation\n"); + #endif + goto __NOTRANS; + } + + #ifdef DEBUG_LANG + fprintf(stderr, "Loading %s\n", dst); + #endif + + STRING_free_later(dst); + if (GB_LoadFile(dst, 0, &addr, &len)) + { + #ifdef DEBUG_LANG + fprintf(stderr, "Cannot load %s\n", dst); + #endif + goto __ERROR; + } + + // These temporary files have predictable names because they + // are *.mo files read by the gettext system. + + dir = FILE_cat(FILE_make_temp(NULL, NULL), "tr", NULL); + mkdir(dir, S_IRWXU); + + dir = FILE_cat(FILE_make_temp(NULL, NULL), "tr", LOCAL_get_lang(), NULL); + mkdir(dir, S_IRWXU); + + dir = FILE_cat(dir, "LC_MESSAGES", NULL); + mkdir(dir, S_IRWXU); + + dir = FILE_cat(dir, domain, NULL); + strcat((char *)dir, ".mo"); + + unlink(dir); + + #ifdef DEBUG_LANG + fprintf(stderr, "Writing to %s (%d bytes)\n", dir, len); + #endif + + // No need to test previous system calls as the failure will be detected now + + file = fopen(dir, "w"); + if (file) + { + fwrite(addr, len, 1, file); + fclose(file); + } + + GB_ReleaseFile(addr, len); + +__ERROR: + + // If the *.mo was not copied, then the following functions will failed + + #ifdef DEBUG_LANG + + fprintf(stderr, "bindtextdomain: %s\n", bindtextdomain(domain, FILE_cat(FILE_make_temp(NULL, NULL), "tr", NULL))); + fprintf(stderr, "bind_textdomain_codeset: %s\n", bind_textdomain_codeset(domain, "UTF-8")); + if (!arch) + fprintf(stderr, "textdomain: %s\n", textdomain(domain)); + + #else + + bindtextdomain(domain, FILE_cat(FILE_make_temp(NULL, NULL), "tr", NULL)); + #ifdef OS_SOLARIS + fprintf(stderr, "Warning: bind_textdomain_codeset() unavailable.\n"); + #else + bind_textdomain_codeset(domain, "UTF-8"); + #endif + if (!arch) + textdomain(domain); /* default domain */ + + #endif + +__NOTRANS: + + STRING_free(&lang_list); + + if (arch) + arch->translation_loaded = TRUE; + else + _translation_loaded = TRUE; + + COMPONENT_current = save; +} + + +const char *LOCAL_gettext(const char *msgid) +{ + const char *tr = msgid; + ARCHIVE *arch = NULL; + + /* + If LOCAL_gettext() is called, then we are in the context of + the archive, so the translation loaded will be the good one. + */ + + if (!msgid) + return ""; + + if (!ARCHIVE_get_current(&arch)) + { + #ifdef DEBUG_LANG + fprintf(stderr, "dgettext(\"%s\", \"%s\")\n", arch->domain, msgid); + #endif + if (!arch->translation_loaded) + LOCAL_load_translation(arch); + tr = dgettext(arch->domain, msgid); + } + + if (tr == msgid) + { + #ifdef DEBUG_LANG + fprintf(stderr, "dgettext(\"%s\", \"%s\")\n", "gb", msgid); + #endif + if (!_translation_loaded) + LOCAL_load_translation(NULL); + tr = dgettext("gb", msgid); + //tr = gettext(msgid); + } + + /*printf("tr: %s -> %s\n", msgid, tr);*/ + + if (!tr || tr[0] == 0 || (tr[0] == '-' && (tr[1] == 0 || (tr[1] == '\n' && tr[2] == 0)))) + tr = msgid; + + #ifdef DEBUG_LANG + fprintf(stderr, "--> \"%s\"\n", tr); + #endif + + return tr; +} + +int LOCAL_get_first_day_of_week() +{ + const char *lang; + + if (LOCAL_first_day_of_week >= 0) + return LOCAL_first_day_of_week; + + lang = LOCAL_get_lang(); + + if (strcmp(lang, "en") == 0 || strncmp(lang, "en_", 3) == 0 || strcmp(lang, "C") == 0) + return 0; + else + return 1; +} + +void LOCAL_set_first_day_of_week(char day) +{ + if (day >= -1 && day <= 6) + LOCAL_first_day_of_week = day; +} diff --git a/main/gbx/gbx_local.h b/main/gbx/gbx_local.h new file mode 100644 index 00000000..36e106bc --- /dev/null +++ b/main/gbx/gbx_local.h @@ -0,0 +1,129 @@ +/*************************************************************************** + + gbx_local.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_LOCAL_H +#define __GBX_LOCAL_H + +#ifndef GBX_INFO + +#include +#include "gbx_date.h" +#include "gbx_archive.h" + +#endif + +enum { + LF_USER, + LF_STANDARD, + LF_GENERAL_NUMBER, + LF_SHORT_NUMBER, + LF_FIXED, + LF_PERCENT, + LF_SCIENTIFIC, + LF_CURRENCY, + LF_INTERNATIONAL, + LF_GENERAL_DATE, + LF_LONG_DATE, + LF_MEDIUM_DATE, + LF_SHORT_DATE, + LF_LONG_TIME, + LF_MEDIUM_TIME, + LF_SHORT_TIME, + LF_MAX + }; + +#ifndef GBX_INFO + +enum { + LO_HOUR = 1, + LO_MINUTE = 2, + LO_SECOND = 3, + LO_YEAR = 1, + LO_MONTH = 2, + LO_DAY = 3 + }; + +typedef + struct { + char decimal_point; + char currency_decimal_point; + const char *thousand_sep; + uchar len_thousand_sep; + const char *currency_thousand_sep; + uchar len_currency_thousand_sep; + char group_size; + char currency_group_size; + unsigned char currency_flag; + uint date_sep[4]; + uint time_sep[4]; + const char *currency_symbol; + const char *intl_currency_symbol; + uchar date_order[4]; + uchar time_order[4]; + char *long_date; + char *medium_date; + char *short_date; + char *long_time; + char *medium_time; + char *short_time; + char *general_date; + char *standard_date; + char *general_currency; + char *intl_currency; + char *true_str; + uchar len_true_str; + char *false_str; + uchar len_false_str; + unsigned rtl : 1; + unsigned date_many_sep : 1; + unsigned date_tail_sep : 1; + unsigned time_many_sep : 1; + unsigned time_tail_sep : 1; + } + LOCAL_INFO; + +#ifndef __GBX_LOCAL_C +EXTERN LOCAL_INFO LOCAL_default, LOCAL_local; +EXTERN char *LOCAL_encoding; +EXTERN bool LOCAL_is_UTF8; +EXTERN char LOCAL_first_day_of_week; +#endif + + +void LOCAL_init(void); +void LOCAL_exit(void); +bool LOCAL_format_number(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, bool local); +bool LOCAL_format_date(const DATE_SERIAL *date, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str); +const char *LOCAL_get_lang(void); +void LOCAL_set_lang(const char *lang); +const char *LOCAL_gettext(const char *msgid); +//void LOCAL_load_translation(ARCHIVE *arch); +int LOCAL_get_first_day_of_week(); +void LOCAL_set_first_day_of_week(char day); +const char *LOCAL_get_format(LOCAL_INFO *info, int type); + +#define LOCAL_get(_local) ((_local) ? &LOCAL_local : &LOCAL_default) + +#endif + +#endif diff --git a/main/gbx/gbx_math.c b/main/gbx/gbx_math.c new file mode 100644 index 00000000..c955b3b4 --- /dev/null +++ b/main/gbx/gbx_math.c @@ -0,0 +1,242 @@ +/*************************************************************************** + + gbx_math.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_MATH_C + +#include "gb_common.h" + +#include +#include +#include + +#include "gb_hash.h" +#include "gbx_math.h" + +const double MATH_pow10_double[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; +static const uint _pow10_uint[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + +/* This is a twisted generalized feedback shift register + that generates pseudo-random numbers. + + Source code from the paper of Yann Guidon + in March edition of GNU/Linux Magazine France +*/ + +#define CRC32_STD 0x04C11DB7 +#define GFSR_SIZE 15 + +static uint GFSR_table[GFSR_SIZE]; +static uint GFSR_temp; +static uint GFSR_index; + +static void GFSR_init(uint seed) +{ + int i = 0, j; + uint t = seed; + + if (t == 0) + t = (uint)-1; + + do + { + t ^= (t >> 5) ^ (t << 1); + j = t >> 31; + t <<= 1; + if (j) + t ^= CRC32_STD; + GFSR_table[i++] = t; + } + while (i < GFSR_SIZE); + + GFSR_temp = GFSR_table[GFSR_SIZE - 1]; + GFSR_index = 0; +} + +static uint GFSR_random(void) +{ + int t; + + GFSR_temp ^= GFSR_table[GFSR_index]; + + t = GFSR_temp; + GFSR_temp <<= 1; + if (t < 0) + GFSR_temp ^= CRC32_STD; + + GFSR_table[GFSR_index] = GFSR_temp; + + if (++GFSR_index >= GFSR_SIZE) + GFSR_index = 0; + + return GFSR_temp; +} + +double frac(double x) +{ + x = fabs(x); + return x - floor(x); +} + +int lsgn(int x) +{ + return ((x > 0) ? 1 : ((x < 0) ? (-1) : 0)); +} + +int llsgn(int64_t x) +{ + return ((x > 0) ? 1 : ((x < 0) ? (-1) : 0)); +} + +/*int64_t llabs(int64_t x) +{ + return ((x < 0) ? (-x) : x); +}*/ + +int fsgn(double x) +{ + return ((x > 0) ? 1 : ((x < 0) ? (-1) : 0)); +} + +float fixf(float x) +{ + if (x >= 0) + return floorf(x); + else + return -floorf(fabsf(x)); +} + +double fix(double x) +{ + if (x >= 0) + return floor(x); + else + return -floor(fabs(x)); +} + +double frexp10(double x, int *exp) +{ + int p; + + if (x == 0.0) + { + *exp = 0; + return x; + } + + p = (int)log10(x); + x /= pow10(p); + + if (x >= 1) + { + x /= 10; + p++; + } + + *exp = p; + return x; +} + + +void randomize(bool set, uint seed) +{ + struct timeval tv; + + if (!set && gettimeofday(&tv, NULL) == 0) + seed = 0xD1C2B3A4 + (tv.tv_sec << 20) + tv.tv_usec; + + GFSR_init(seed); +} + + +double rnd(void) +{ + /*seed = 16807L * (seed % 127773L) - 2836L * (seed / 127773L); + if (seed <= 0) seed += 2147483647; + + return (double)seed / 2147483648.0;*/ + + uint64_t val; + + val = GFSR_random(); + val <<= 32; + val |= GFSR_random(); + + return (double)val / 18446744073709551616.0; //0xFFFFFFFFFFFFFFFFULL; +} + +#ifndef HAVE_EXP10 +double exp10(double x) +{ + return pow(10, x); +} +#endif + +#ifndef HAVE_LOG2 +double log2(double x) +{ + return log(x) / M_LN2; +} +#endif + +#ifndef HAVE_EXP2 +double exp2(double x) +{ + return pow(2, x); +} +#endif + +static int nbits(uint n) +{ + uint c; + for (c = 0; n; c++) + n &= n - 1; + return c; +} + +void MATH_init(void) +{ + uint seed; + + randomize(FALSE, 0); + + // Internet condom + do + seed = GFSR_random(); + while ((seed & 1) == 0 || nbits(seed) < 16); + + HASH_seed = seed; +} + +uint64_t pow10_uint64_p(int n) +{ + uint64_t v = 1; + + while (n > 8) + { + v *= 100000000; + n -= 8; + } + v *= _pow10_uint[n]; + return v; +} + diff --git a/main/gbx/gbx_math.h b/main/gbx/gbx_math.h new file mode 100644 index 00000000..3d003d27 --- /dev/null +++ b/main/gbx/gbx_math.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + gbx_math.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_MATH_H +#define __GBX_MATH_H + +#include "config.h" + +#ifdef OS_BSD +#undef HAVE_EXP10 +#endif + +#ifndef __GBX_MATH_C +extern const double MATH_pow10_double[]; +#endif + +void MATH_init(void); + +int lsgn(int x); +int llsgn(int64_t x); +//int64_t llabs(int64_t x); + +double frac(double x); +int fsgn(double x); +float fixf(float x); +double fix(double x); +double frexp10(double x, int *exp); + +#define pow10(_n) (((_n) >= 0 && (_n) <= 9) ? MATH_pow10_double[_n] : (((_n) < 0 && (_n) >= -9) ? (1.0 / MATH_pow10_double[-(_n)]) : exp10(_n))) +//#define mulpow10(_v, _n) (((_n) >= 0 && (_n) <= 9) ? ((_v) * MATH_pow10_double[_n]) : (((_n) < 0 && (_n) >= -9) ? ((_v) / MATH_pow10[-(_n)]) : ((_v) * exp10(_n)))) + +uint64_t pow10_uint64_p(int n); + +void randomize(bool set, uint seed); +double rnd(void); + +#define deg(_x) ((_x) * 180 / M_PI) +#define rad(_x) ((_x) * M_PI / 180) + +#ifndef HAVE_EXP10 +double exp10(double x); +#endif + +#ifndef HAVE_LOG2 +double log2(double x); +#endif + +#ifndef HAVE_EXP2 +double exp2(double x); +#endif + +#endif diff --git a/main/gbx/gbx_number.c b/main/gbx/gbx_number.c new file mode 100644 index 00000000..3393637c --- /dev/null +++ b/main/gbx/gbx_number.c @@ -0,0 +1,601 @@ +/*************************************************************************** + + gbx_number.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_NUMBER_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include +#include +#include + +#include "gbx_type.h" +#include "gb_common_buffer.h" +#include "gbx_local.h" +#include "gbx_math.h" +#include "gbx_string.h" +#include "gbx_number.h" + +#define buffer_init COMMON_buffer_init +#define get_char COMMON_get_char +#define last_char COMMON_last_char +#define look_char COMMON_look_char +#define put_char COMMON_put_char +#define jump_space COMMON_jump_space +#define get_current COMMON_get_current +#define buffer_pos COMMON_pos +#define get_size_left COMMON_get_size_left +#define has_string COMMON_has_string + +#define IS_PURE_INTEGER(_int64_val) ((_int64_val) == ((int)(_int64_val))) + +static uint64_t _pow_10[18] = { + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000 +}; + + +static bool read_integer(int base, bool minus, int64_t *result, bool local) +{ + uint64_t nbr2, nbr; + int d, n, c, nmax; + const char *thsep; + int lthsep; + int ndigit_thsep; + bool first_thsep; + + thsep = LOCAL_get(local)->thousand_sep; + lthsep = LOCAL_get(local)->len_thousand_sep; + ndigit_thsep = 0; + first_thsep = FALSE; + + n = 0; + nbr = 0; + + switch (base) + { + case 2: nmax = 64; break; + case 8: nmax = 21; break; + case 16: nmax = 16; break; + case 10: default: nmax = 19; break; + } + + c = last_char(); + + if (base == 10) + { + for(;;) + { + if (local) + { + COMMON_pos--; + + if (has_string(thsep, lthsep) && (ndigit_thsep == 3 || (!first_thsep && ndigit_thsep >= 1 && ndigit_thsep <= 3))) + { + COMMON_pos += lthsep; + c = get_char(); + first_thsep = TRUE; + ndigit_thsep = 0; + } + else + COMMON_pos++; + } + + if (c >= '0' && c <= '9') + { + d = c - '0'; + if (local) + ndigit_thsep++; + } + else + break; + + n++; + if (n < nmax) + { + nbr = nbr * 10 + d; + } + else + { + nbr2 = nbr * 10 + d; + + if ((nbr2 / base) != nbr || nbr2 > ((uint64_t)LLONG_MAX + minus)) + return TRUE; + + nbr = nbr2; + } + + c = get_char(); + if (c < 0) + break; + } + + c = last_char(); + + if (local && first_thsep && ndigit_thsep != 3) + return TRUE; + } + else + { + for(;;) + { + if (c >= '0' && c <= '9') + d = c - '0'; + else if (c >= 'A' && c <='Z') + d = c - 'A' + 10; + else if (c >= 'a' && c <='z') + d = c - 'a' + 10; + else + break; + + if (d >= base) + break; + + n++; + if (n > nmax) + return TRUE; + + nbr = nbr * base + d; + + c = get_char(); + if (c < 0) + break; + } + + c = last_char(); + + if ((c == '&' || c == 'u' || c == 'U') && base != 10) + c = get_char(); + else + { + if ((base == 16 && n == 4) || (base == 2 && n == 16)) + { + if (nbr >= 0x8000L && nbr <= 0xFFFFL) + nbr |= INT64_C(0xFFFFFFFFFFFF0000); + } + else if ((base == 16 && n == 8) || (base == 2 && n == 32)) + { + if (nbr >= 0x80000000L && nbr <= 0xFFFFFFFFL) + nbr |= INT64_C(0xFFFFFFFF00000000); + } + } + } + + if (c > 0 && !isspace(c)) + return TRUE; + + if (n == 0) + return TRUE; + + *((int64_t *)result) = nbr; + return FALSE; +} + + +static bool read_float(double *result, bool local) +{ + LOCAL_INFO *local_info; + char point; + const char *thsep; + int lthsep; + int ndigit_thsep; + bool first_thsep; + int c, n; + + uint64_t mantisse, mantisse_int; + int ndigit_frac, ndigit_frac_zero; + bool frac; + bool frac_null; + bool nozero; + + int nexp; + bool nexp_minus; + + local_info = LOCAL_get(local); + point = local_info->decimal_point; + thsep = local_info->thousand_sep; + lthsep = local_info->len_thousand_sep; + ndigit_thsep = 0; + first_thsep = FALSE; + + c = last_char(); + + n = 0; + mantisse = 0; + mantisse_int = 0; + frac = FALSE; + frac_null = TRUE; + ndigit_frac = 0; + ndigit_frac_zero = 0; + nexp = 0; + nexp_minus = FALSE; + nozero = FALSE; + + // Integer part + + for(;;) + { + if (c == point) + { + c = get_char(); + frac = TRUE; + mantisse_int = mantisse; + break; + } + + if (local) + { + COMMON_pos--; + + if (has_string(thsep, lthsep) && (ndigit_thsep == 3 || (!first_thsep && ndigit_thsep >= 1 && ndigit_thsep <= 3))) + { + COMMON_pos += lthsep; + first_thsep = TRUE; + ndigit_thsep = 0; + c = get_char(); + } + else + COMMON_pos++; + } + + if (!isdigit(c) || (c < 0)) + break; + + if (c != '0') + nozero = TRUE; + + if (nozero) + n++; + + if (n > MAX_FLOAT_DIGIT) + { + if (n == (MAX_FLOAT_DIGIT + 1) && (c >= '5')) + mantisse++; + ndigit_frac--; // ??? + c = get_char(); + continue; + } + + if (c == '0') + mantisse *= 10; + else + mantisse = mantisse * 10 + (c - '0'); + + if (local) + ndigit_thsep++; + + c = get_char(); + + if (c == 'e' || c == 'E') + break; + + if (c < 0) + goto __END; + } + + // Decimal part + + for(;;) + { + if (c == point) + break; + + if (!isdigit(c) || (c < 0)) + break; + + if (c != '0') + nozero = TRUE; + + if (nozero) + n++; + + if (n > MAX_FLOAT_DIGIT) + { + if (n == (MAX_FLOAT_DIGIT + 1) && (c >= '5')) + mantisse++; + if (!frac) + ndigit_frac--; + c = get_char(); + continue; + } + + if (c == '0') + ndigit_frac_zero++; + else + { + frac_null = FALSE; + ndigit_frac += ndigit_frac_zero + 1; + mantisse = mantisse * _pow_10[ndigit_frac_zero] + (c - '0'); + ndigit_frac_zero = 0; + } + + c = get_char(); + + if (c == 'e' || c == 'E') + break; + + if (c < 0) + goto __END; + } + + // Exponant + + if (c == 'e' || c == 'E') + { + c = get_char(); + + if (c == '+' || c == '-') + { + if (c == '-') + nexp_minus = TRUE; + + c = get_char(); + } + + if (!isdigit(c) || (c < 0)) + return TRUE; + + for(;;) + { + nexp = nexp * 10 + (c - '0'); + if (nexp > DBL_MAX_10_EXP) + return TRUE; + + c = get_char(); + if (!isdigit(c) || (c < 0)) + break; + } + + if (nexp_minus) + nexp = (-nexp); + } + + if (c >= 0 && !isspace(c)) + return TRUE; + +__END: + + if (local && first_thsep && ndigit_thsep != 3) + return TRUE; + + if (frac && frac_null) + mantisse = mantisse_int; + else + nexp -= ndigit_frac; + + //fprintf(stderr, "%.24g %d\n", (double)mantisse, nexp); + //*result = mulpow10((double)mantisse, nexp); + *result = (double)mantisse * pow10(nexp); + + return FALSE; +} + + +bool NUMBER_from_string(int option, const char *str, int len, VALUE *value) +{ + int c; + int64_t val = 0; + double dval = 0.0; + TYPE type; + int base = 10; + bool minus = FALSE; + int pos; + + buffer_init(str, len); + + jump_space(); + + c = get_char(); + + if (c == '+' || c == '-') + { + minus = (c == '-'); + c = get_char(); + } + + if (option & NB_READ_INT_LONG) + { + if (option & NB_READ_HEX_BIN) + { + if (c == '&') + { + c = get_char(); + + if (c == 'H' || c == 'h') + { + base = 16; + c = get_char(); + } + else if (c == 'O' || c == 'o') + { + base = 8; + c = get_char(); + } + else if (c == 'X' || c == 'x') + { + base = 2; + c = get_char(); + } + else + base = 16; + } + else if (c == '%') + { + base = 2; + c = get_char(); + } + } + } + + if (c < 0) + return TRUE; + + if (c == '-' || c == '+') + return TRUE; + + errno = 0; + pos = COMMON_pos - 1; + + if (!read_integer(base, minus, &val, (option & NB_LOCAL) != 0)) + { + if (minus) val = (-val); + + if ((option & NB_READ_INTEGER) && IS_PURE_INTEGER(val)) + { + type = T_INTEGER; + goto __END; + } + else if ((option & NB_READ_LONG)) + { + type = T_LONG; + goto __END; + } + else if ((option & NB_READ_FLOAT) && base == 10) + { + type = T_FLOAT; + dval = (double)val; + goto __END; + } + } + + if ((option & NB_READ_FLOAT) && base == 10) + { + COMMON_pos = pos; + get_char(); + if (!read_float(&dval, (option & NB_LOCAL) != 0)) + { + if (minus) dval = (-dval); + type = T_FLOAT; + goto __END; + } + } + + return TRUE; + +__END: + + if (last_char() >= 0) //(c >= 0 && !isspace(c)) + return TRUE; + + value->type = type; + + if (type == T_INTEGER) + value->_integer.value = val; + else if (type == T_LONG) + value->_long.value = val; + else + value->_float.value = dval; + + //fprintf(stderr, "return FALSE\n"); + return FALSE; +} + + +void NUMBER_int_to_string(uint64_t nbr, int prec, int base, VALUE *value) +{ + char *ptr; + char *src; + int digit, len; + bool neg; + + len = 0; + ptr = &COMMON_buffer[COMMON_BUF_MAX]; + + if (nbr == 0 && prec == 0) + { + STRING_char_value(value, '0'); + return; + } + + neg = (nbr & (1LL << 63)) != 0; + + if (base == 10 && neg) + nbr = 1 + ~nbr; + + while (nbr > 0) + { + digit = nbr % base; + nbr /= base; + + ptr--; + len++; + + if (digit < 10) + *ptr = '0' + digit; + else + *ptr = 'A' + digit - 10; + } + + if (neg) + { + if (prec) + { + ptr += len - prec; + len = prec; + } + + if (base == 10) + { + len++; + ptr--; + *ptr = '-'; + } + + STRING_new_temp_value(value, NULL, len); + src = value->_string.addr; + + memcpy(src, ptr, len); + } + else + { + STRING_new_temp_value(value, NULL, Max(len, prec)); + src = value->_string.addr; + + while (prec > len) + { + *src++ = '0'; + prec--; + } + + memcpy(src, ptr, len); + } +} + diff --git a/main/gbx/gbx_number.h b/main/gbx/gbx_number.h new file mode 100644 index 00000000..c299bdd8 --- /dev/null +++ b/main/gbx/gbx_number.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + gbx_number.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_NUMBER_H +#define __GBX_NUMBER_H + +#include "gbx_type.h" +#include "gbx_value.h" + +enum { + NB_READ_NOTHING = 0, + NB_READ_INTEGER = 1, + NB_READ_LONG = 2, + NB_READ_INT_LONG = 3, + NB_READ_FLOAT = 4, + NB_READ_ALL = 7, + NB_READ_HEX_BIN = 8, + NB_LOCAL = 16 + }; + + +bool NUMBER_from_string(int option, const char *str, int len, VALUE *value); +void NUMBER_int_to_string(uint64_t nbr, int prec, int base, VALUE *value); + +#endif /* */ diff --git a/main/gbx/gbx_object.c b/main/gbx/gbx_object.c new file mode 100644 index 00000000..0ea48277 --- /dev/null +++ b/main/gbx/gbx_object.c @@ -0,0 +1,549 @@ +/*************************************************************************** + + gbx_object.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __OBJECT_C + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_list.h" +#include "gbx_class.h" +#include "gbx_event.h" +#include "gbx_exec.h" +#include "gbx_compare.h" +#include "gbx_c_observer.h" +#include "gbx_c_array.h" +#include "gbx_struct.h" +#include "gbx_object.h" + +#if DEBUG_REF +const char *OBJECT_ref_where = 0; +#endif + +void **OBJECT_set_pointer = NULL; + +static OBJECT *_event_object_list = NULL; + +#if DEBUG_REF +char *OBJECT_where_am_i(const char *file, int line, const char *func) +{ + static char buffer[256]; + + snprintf(buffer, sizeof(buffer), "[%s] %s:%d", file, func, line); + return buffer; +} +#endif + +void *OBJECT_new(CLASS *class, const char *name, OBJECT *parent) +{ + OBJECT *object; + + ALLOC_ZERO(&object, class->size); + + object->class = class; + #if DEBUG_REF + object->ref = 0; + OBJECT_REF(object); + #else + object->ref = 1; + #endif + + class->count++; + + OBJECT_attach(object, parent, name); + + return object; +} + + +#if 0 +static void dump_attach(char *title) +{ + void *ob; + + fprintf(stderr, ">>>> %s: ", title); + for (ob = _event_object_list; ob; ob = OBJECT_event(ob)->next) + fprintf(stderr, "%p -> ", ob); + fprintf(stderr, "(nil)\n"); + +} +#endif + +static void call_attach_special_method(CLASS *class, void *ob, void *parent, const char *name) +{ + STACK_check(2); + + SP->_object.class = OBJECT_class(parent); + SP->_object.object = parent; + PUSH(); + + if (name) + { + SP->type = T_CSTRING; + SP->_string.addr = (char *)name; + SP->_string.start = 0; + SP->_string.len = strlen(name); + } + else + VALUE_null(SP); + + SP++; + + EXEC_special(SPEC_ATTACH, class, ob, 2, TRUE); +} + +static void insert_object(OBJECT *ob, OBJECT_EVENT *ev) +{ + ev->next = _event_object_list; + ev->prev = NULL; + + if (_event_object_list) + OBJECT_event(_event_object_list)->prev = ob; + + _event_object_list = ob; +} + +static void remove_object(OBJECT *ob, OBJECT_EVENT *ev) +{ + if (ev->prev) + OBJECT_event(ev->prev)->next = ev->next; + + if (ev->next) + OBJECT_event(ev->next)->prev = ev->prev; + + if (ob == _event_object_list) + _event_object_list = ev->next; + + ev->prev = NULL; + ev->next = NULL; +} + +void OBJECT_detach(OBJECT *ob) +{ + CLASS *class = OBJECT_class(ob); + OBJECT *parent; + OBJECT_EVENT *ev; + + if (!class->is_observer && class->n_event == 0) + return; + + ev = (OBJECT_EVENT *)((char *)ob + class->off_event); + + //if (!ev->parent) + // return; + + // Do not free the observers there + + remove_object(ob, ev); + + //dump_attach("OBJECT_detach"); + + /* Avoids an infinite recursion, if freeing the parent implies freeing the object */ + parent = ev->parent; + + if (parent) + { + ev->parent = NULL; + + if (class->special[SPEC_ATTACH] != NO_SYMBOL) + call_attach_special_method(class, ob, parent, NULL); + + #if DEBUG_EVENT || DEBUG_REF + fprintf(stderr, "OBJECT_detach : Detach (%s %p) from (%s %p)\n", + ob->class->name, ob, parent->class->name, parent); + #endif + OBJECT_UNREF(parent); + } +} + +static void remove_observers(OBJECT *ob) +{ + CLASS *class = OBJECT_class(ob); + OBJECT_EVENT *ev; + COBSERVER *obs, *next; + + //fprintf(stderr, "Remove observers: %s %p\n", class->name, ob); + + if (!class->is_observer && class->n_event == 0) + return; + + ev = (OBJECT_EVENT *)((char *)ob + class->off_event); + obs = ev->observer; + ev->observer = NULL; + + while (obs) + { + next = obs->list.next; + #if DEBUG_EVENT + fprintf(stderr, "Remove observer %p %d: %p: %p\n", obs, (int)obs->ob.ref, ob, obs->object); + #endif + obs->object = NULL; + OBJECT_UNREF(obs); + obs = next; + } + + //ev->observer = NULL; +} + +void OBJECT_attach(OBJECT *ob, OBJECT *parent, const char *name) +{ + CLASS *class = OBJECT_class(ob); + OBJECT_EVENT *ev; + + if (!name || !*name) + return; + + if (!class->is_observer && class->n_event == 0) + return; + + OBJECT_detach(ob); + + ev = (OBJECT_EVENT *)((char *)ob + class->off_event); + ev->parent = parent; + + #if DEBUG_EVENT || DEBUG_REF + fprintf(stderr, "OBJECT_attach : Attach (%s %p) to (%s %p) as %s\n", + ob->class->name, ob, parent->class->name, parent, name); + #endif + + OBJECT_REF(parent); + + EVENT_search(class, ev->event, name, parent); + + insert_object(ob, ev); + + if (class->special[SPEC_ATTACH] != NO_SYMBOL) + call_attach_special_method(class, ob, parent, name); + + //dump_attach("OBJECT_attach"); +} + + +bool OBJECT_comp_value(VALUE *ob1, VALUE *ob2) +{ + if (ob1->type == T_NULL && ob2->type == T_NULL) + return FALSE; + else if (ob1->type == T_NULL) + return ob2->_object.object != NULL; + else if (ob2->type == T_NULL) + return ob1->_object.object != NULL; + else + return COMPARE_object(&ob1->_object.object, &ob2->_object.object); +} + +void OBJECT_release_static(CLASS *class, CLASS_VAR *var, int nelt, char *data) +{ + static void *jump[17] = { + &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, + &&__STRING, &&__NEXT, &&__NEXT, &&__VARIANT, &&__ARRAY, &&__STRUCT, &&__NEXT, &&__OBJECT + }; + + CTYPE type; + + while (nelt--) + { +#if TRACE_MEMORY + if (var->type.id == T_STRING || var->type.id == T_OBJECT) + fprintf(stderr, "release_static: %s [%d] trying %p\n", class->name, i, (*(void **)&data[var->pos])); +#endif + + type = var->type; + goto *jump[type.id]; + + __STRING: + STRING_unref((char **)&data[var->pos]); + goto __NEXT; + + __OBJECT: + OBJECT_UNREF(*((void **)&data[var->pos])); + goto __NEXT; + + __VARIANT: + VARIANT_free((VARIANT *)&data[var->pos]); + goto __NEXT; + + __ARRAY: + CARRAY_release_static(class, class->load->array[type.value], &data[var->pos]); + goto __NEXT; + + __STRUCT: + { + CLASS *sclass = class->load->class_ref[type.value]; + OBJECT_release_static(sclass, sclass->load->dyn, sclass->load->n_dyn, &data[var->pos]); + } + + __NEXT: + var++; + } +} + +static void release(CLASS *class, OBJECT *ob) +{ + CLASS_VAR *var; + int nelt; + char *data; + + if (class->parent != NULL && ob) + release(class->parent, ob); + + if (CLASS_is_native(class)) + return; + + if (ob == NULL) + { + var = class->load->stat; + nelt = class->load->n_stat; + data = class->stat; + } + else + { + if (CLASS_is_struct(class)) + { + if (((CSTRUCT *)ob)->ref) + { + CSTRUCT_release((CSTRUCT *)ob); + return; + } + data = (char *)ob + sizeof(CSTRUCT); + } + else + data = (char *)ob; + + var = class->load->dyn; + nelt = class->load->n_dyn; + } + + OBJECT_release_static(class, var, nelt, data); +} + + +void OBJECT_release(CLASS *class, OBJECT *ob) +{ +#if TRACE_MEMORY + printf("> OBJECT_release %s %p\n", class->name, ob); +#endif + + if (ob) + { + ob->ref = 1; // Prevents anybody from freeing the object! + OBJECT_detach(ob); + remove_observers(ob); + ob->ref = 0; + } + + release(class, ob); + + if (ob) + { + class->count--; + + #if DEBUG_REF + ob->class = FREE_MARK; + #endif + + IFREE(ob); + } + +#if TRACE_MEMORY + printf("< OBJECT_release %s %p\n", class->name, ob); +#endif +} + + +void OBJECT_exit(void) +{ + #if DEBUG_LOAD + fprintf(stderr, "------------ OBJECT_exit - BEGIN---------\n"); + #endif + while (_event_object_list) + OBJECT_detach(_event_object_list); + #if DEBUG_LOAD + fprintf(stderr, "------------ OBJECT_exit - END ----------\n"); + #endif +} + +static void error_OBJECT_create(const char *name, void *object) +{ + OBJECT_UNREF_KEEP(object); + EVENT_leave_name(name); +} + +void *OBJECT_create(CLASS *class, const char *name, void *parent, int nparam) +{ + void *object; + const char *save; + + // The "no create" flag only concerns users of NEW + //if (class->no_create) + // THROW(E_CSTATIC, CLASS_get_name(class)); + + ON_ERROR_2(error_OBJECT_create, save = EVENT_enter_name(name), object = OBJECT_new(class, name, parent)) + { + if (OBJECT_set_pointer) + { + *OBJECT_set_pointer = object; + OBJECT_REF(object); + OBJECT_set_pointer = NULL; + } + + OBJECT_lock(object, TRUE); + EXEC_special_inheritance(SPEC_NEW, class, object, nparam, TRUE); + OBJECT_lock(object, FALSE); + + EXEC_special(SPEC_READY, class, object, 0, TRUE); + + error_OBJECT_create(save, object); + } + END_ERROR + + return object; +} + + +/* FIXME: The _new methods are called differently from EXEC_special_inheritance */ + +void *OBJECT_create_native(CLASS *class, VALUE *param) +{ + CLASS_DESC *desc; + short index; + void *object; + + object = OBJECT_new(class, NULL, NULL); + + for(;;) + { + index = class->special[SPEC_NEW]; + if (index != NO_SYMBOL) + { + desc = CLASS_get_desc(class, index); + EXEC_call_native(desc->method.exec, object, desc->method.type, param); + } + class = class->parent; + if (!class) + break; + } + + EXEC_special(SPEC_READY, OBJECT_class(object), object, 0, TRUE); + OBJECT_UNREF_KEEP(object); + return object; +} + +void OBJECT_lock(OBJECT *object, bool lock) +{ + CLASS *class; + OBJECT_EVENT *ev; + + if (!object) + return; + + class = object->class; + + if (class->is_observer) + { + COBSERVER_lock((COBSERVER *)object, lock); + return; + } + + if (class->n_event == 0) + return; + + ev = (OBJECT_EVENT *)((char *)object + class->off_event); + + if (lock) + ev->locked++; + else + ev->locked--; +} + + +bool OBJECT_is_locked(OBJECT *object) +{ + CLASS *class; + + if (!object) + return FALSE; + + class = object->class; + + if (class->is_observer) + return COBSERVER_is_locked((COBSERVER *)object); + + if (class->n_event == 0) + return FALSE; + + return OBJECT_event(object)->locked > 0; +} + + +OBJECT *OBJECT_parent(void *object) +{ + CLASS *class = OBJECT_class(object); + + if (!class->is_observer && class->n_event == 0) + return NULL; + + //return ((OBJECT *)((intptr_t)OBJECT_event(object)->parent & ~1)); + return OBJECT_event(object)->parent; +} + + +OBJECT *OBJECT_active_parent(void *object) +{ + OBJECT *parent = OBJECT_parent(object); + + if (!parent || OBJECT_is_locked((OBJECT *)object) || OBJECT_is_locked(parent)) + return NULL; + + return parent; +} + +int OBJECT_check_valid(void *object) +{ + return *((char *)object + OBJECT_class(object)->special[SPEC_INVALID]); +} + +void *OBJECT_get_addr(void *ob) +{ + CLASS *class; + void *addr; + + if (!ob) + return NULL; + + class = OBJECT_class(ob); + + if (class == CLASS_Class && !CLASS_is_native((CLASS *)ob)) + addr = ((CLASS *)ob)->stat; + else if (CLASS_is_array(class)) + addr = ((CARRAY *)ob)->data; + else if (CLASS_is_struct(class)) + { + if (((CSTRUCT *)ob)->ref) + addr = (char *)((CSTATICSTRUCT *)ob)->addr; + else + addr = (char *)ob + sizeof(CSTRUCT); + } + else + addr = (char *)ob + sizeof(OBJECT); + + return addr; +} diff --git a/main/gbx/gbx_object.h b/main/gbx/gbx_object.h new file mode 100644 index 00000000..f25f8e66 --- /dev/null +++ b/main/gbx/gbx_object.h @@ -0,0 +1,253 @@ +/*************************************************************************** + + gbx_object.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __OBJECT_H +#define __OBJECT_H + +#include "gbx_debug.h" +#include "gbx_value.h" +#include "gbx_class.h" + +#ifndef __OBJECT_C +EXTERN void **OBJECT_set_pointer; +#endif + +typedef + struct { + CLASS *class; + intptr_t ref; + } + OBJECT; + +typedef + struct { + OBJECT *parent; + OBJECT *next; + OBJECT *prev; + void *observer; + short locked; + ushort event[0]; + } + PACKED + OBJECT_EVENT; + +#define OBJECT_event(_object) ((OBJECT_EVENT *)((intptr_t *)_object + ((OBJECT *)(_object))->class->off_event / sizeof(intptr_t))) +#define OBJECT_is(_object, _class) (OBJECT_class(_object) == _class) +#define OBJECT_is_class(_object) OBJECT_is(_object, CLASS_Class) +#define OBJECT_class(_object) (((OBJECT *)_object)->class) +#define OBJECT_class_null(_object) ((_object) ? ((OBJECT *)_object)->class : NULL) +#define OBJECT_count(_object) (((OBJECT *)_object)->ref) + +#define OBJECT_are_null(_o1, _o2) (((intptr_t)(_o1) | (intptr_t)(_o2)) == 0) +#define OBJECT_are_not_null(_o1, _o2) ((_o1) && (_o2)) + +void *OBJECT_new(CLASS *class, const char *name, OBJECT *parent); +void OBJECT_attach(OBJECT *ob, OBJECT *parent, const char *name); +void OBJECT_detach(OBJECT *ob); +void OBJECT_release(CLASS *class, OBJECT *ob); +void OBJECT_release_static(CLASS *class, CLASS_VAR *var, int nelt, char *data); +//void OBJECT_free(CLASS *class, OBJECT *ob); +void OBJECT_lock(OBJECT *ob, bool block); +bool OBJECT_is_locked(OBJECT *ob); + +//void *OBJECT_alloc(CLASS *class, size_t size); +bool OBJECT_comp_value(VALUE *ob1, VALUE *ob2); + +void OBJECT_exit(void); +void *OBJECT_create(CLASS *class, const char *name, void *parent, int nparam); +void *OBJECT_create_native(CLASS *class, VALUE *param); + +#define OBJECT_create_and_set(_ptr, _class, _name, _parent, _nparam) \ + ((OBJECT_set_pointer = (_ptr)), \ + OBJECT_create((_class), (_name), (_parent), (_nparam))) + +#define OBJECT_is_valid(_object) ((_object) && !(((OBJECT *)_object)->class->check && (*((OBJECT *)_object)->class->check)(_object))) +#define OBJECT_has_events(_object) (((OBJECT *)_object)->class->n_event != 0) + +OBJECT *OBJECT_parent(void *object); +OBJECT *OBJECT_active_parent(void *object); + +int OBJECT_check_valid(void *object); + +void *OBJECT_get_addr(void *ob); + +/* +static INLINE CLASS *OBJECT_class(void *object) +{ + if (object) + return ((OBJECT *)object)->class; + else + return object; +} +*/ + +/*#define DEBUG_REF 1*/ + +#if DEBUG_REF + +#if OS_64BITS + #define FREE_MARK ((CLASS *)0x2323232323232323LL) +#else + #define FREE_MARK ((CLASS *)0x23232323) +#endif + +EXTERN const char *OBJECT_ref_where; + +char *OBJECT_where_am_i(const char *file, int line, const char *func); + +#define OBJECT_ref(_object) \ +({ \ + OBJECT *_ob = (OBJECT *)(_object); \ + if (OBJECT_class(_ob) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, _ob); \ + fflush(NULL); \ + BREAKPOINT(); \ + } \ + CLASS_ref(_ob); \ + (void *)_ob; \ +}) + + +#define OBJECT_ref_check(_object) \ +{ \ + if (_object) OBJECT_ref(_object); \ +} + + +#define OBJECT_unref(_object) \ +{ \ + if (_object) \ + { \ + if (OBJECT_class(_object) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, (_object)); \ + fflush(NULL); \ + BREAKPOINT(); \ + } \ + if (CLASS_unref(_object, TRUE)) \ + _object = NULL; \ + } \ +} + +#define OBJECT_just_unref(_object) \ +{ \ + if (OBJECT_class(_object) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, (_object)); \ + fflush(NULL); \ + BREAKPOINT(); \ + } \ + CLASS_unref(_object, TRUE); \ +} + + +#define OBJECT_unref_keep(_object) \ +{ \ + if (_object) \ + { \ + if (OBJECT_class(_object) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, (_object)); \ + fflush(NULL); \ + } \ + CLASS_unref(_object, FALSE); \ + } \ +} + +#define OBJECT_REF(_ob) ({ OBJECT_ref_where = OBJECT_where_am_i(__FILE__, __LINE__, __func__); OBJECT_ref(_ob); }) +#define OBJECT_REF_CHECK(_ob) { OBJECT_ref_where = OBJECT_where_am_i(__FILE__, __LINE__, __func__); OBJECT_ref_check(_ob); } +#define OBJECT_UNREF(_ob) { OBJECT_ref_where = OBJECT_where_am_i(__FILE__, __LINE__, __func__); OBJECT_unref(_ob); } +#define OBJECT_UNREF_KEEP(_ob) { OBJECT_ref_where = OBJECT_where_am_i(__FILE__, __LINE__, __func__); OBJECT_unref_keep(_ob); } + +#else /* DEBUG_REF */ + +#define OBJECT_ref(_object) \ +({ \ + OBJECT *_ob = (OBJECT *)(_object); \ + _ob->ref++; \ + (void *)_ob; \ +}) + +#define OBJECT_ref_check(_object) \ +{ \ + if (_object) OBJECT_ref(_object); \ +} + +#define OBJECT_unref(_object) \ +{ \ + if (_object) \ + { \ + if ((--((OBJECT *)(_object))->ref) <= 0) \ + { \ + void *temp = (void *)(_object); \ + _object = NULL; \ + CLASS_free(temp); \ + } \ + } \ +} + +#define OBJECT_just_unref(_object) \ +{ \ + if ((--((OBJECT *)(_object))->ref) == 0) \ + { \ + void *temp = (void *)(_object); \ + _object = NULL; \ + CLASS_free(temp); \ + } \ +} + +#define OBJECT_unref_keep(_object) \ +{ \ + if (_object) \ + --((OBJECT *)(_object))->ref; \ +} + + +#define OBJECT_REF_CHECK(_ob) OBJECT_ref_check(_ob) +#define OBJECT_REF(_ob) OBJECT_ref(_ob) +#define OBJECT_UNREF(_ob) OBJECT_unref(_ob) +#define OBJECT_UNREF_KEEP(_ob) OBJECT_unref_keep(_ob) + +#endif /* DEBUG_REF */ + +#define OBJECT_get_var_addr(_object, _desc) ((void *)((char *)(_object) + (_desc)->variable.offset)) + + +#define OBJECT_null(_value, _class) \ +({ \ + VALUE *__value = (_value); \ + __value->_object.class = (_class); \ + __value->_object.object = NULL; \ +}) + + +#define OBJECT_put(_value, _ob) \ +({ \ + VALUE *__value = (_value); \ + void *__object = (void *)(_ob); \ + __value->_object.class = OBJECT_class(__object); \ + __value->_object.object = __object; \ +}) + +#endif diff --git a/main/gbx/gbx_project.c b/main/gbx/gbx_project.c new file mode 100644 index 00000000..28ac4954 --- /dev/null +++ b/main/gbx/gbx_project.c @@ -0,0 +1,389 @@ +/*************************************************************************** + + gbx_project.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_PROJECT_C + +#include "config.h" + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_alloc.h" +#include "gb_error.h" + +#include + +#include "gb_limit.h" +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_stream.h" +#include "gbx_archive.h" +#include "gbx_exec.h" +#include "gbx_stack.h" +#include "gb_component.h" +#include "gbx_component.h" + +#include "gbx_project.h" + +char *PROJECT_path = NULL; +char *PROJECT_exec_path = NULL; +char *PROJECT_name = NULL; +char *PROJECT_title = NULL; +const char *PROJECT_startup = NULL; +char *PROJECT_version = NULL; +CLASS *PROJECT_class = NULL; + +int PROJECT_argc = 0; +char **PROJECT_argv = NULL; +//char *PROJECT_argname = NULL; + +char *PROJECT_oldcwd = NULL; + +bool PROJECT_run_httpd = FALSE; +bool PROJECT_run_tests = FALSE; + +const char *PROJECT_override = NULL; + +static char *project_buffer; + +//static char *project_ptr; +static int project_line; + +static const char *_last_component = NULL; + +static void raise_error(const char *msg) +{ + char line[16]; + + snprintf(line, sizeof(line), "%d", project_line); + THROW(E_PROJECT, line, msg); +} + + +static void project_title(char *name, int len) +{ + name[len] = 0; + PROJECT_title = name; +} + + +static void project_version(char *name, int len) +{ + name[len] = 0; + PROJECT_version = name; +} + + +static void project_component(char *name, int len) +{ + _last_component = name; + + name[len] = 0; + + COMPONENT_create(name); + + // If 'gb.httpd' is set explicitely, then always run through it. + + if (strcmp(name, "gb.httpd") == 0) + PROJECT_run_httpd = TRUE; + + _last_component = NULL; +} + + +static void project_startup(char *name, int len) +{ + if (PROJECT_startup) + return; + + if (len == 0) + raise_error("Project startup class name is void"); + + name[len] = 0; + PROJECT_startup = name; +} + + +static void project_library_path(char *name, int len) +{ + if (!EXEC_debug) + { + ARCHIVE_path = STRING_new_zero(STRING_conv_file_name(name, len)); + if (*name != '/') + { + name = STRING_new_zero(FILE_cat(PROJECT_path, ARCHIVE_path, NULL)); + STRING_free(&ARCHIVE_path); + ARCHIVE_path = name; + } + } +} + +static void check_after_analyze() +{ + if (!PROJECT_name || PROJECT_name[0] == 0) + raise_error("No project name"); + + if (!PROJECT_startup || PROJECT_startup[0] == 0) + raise_error("No startup class"); + + if (!PROJECT_title || PROJECT_title[0] == 0) + PROJECT_title = PROJECT_name; +} + +static bool get_line(char **addr, const char *end, char **start, int *len) +{ + char *p = *addr; + + if (p >= end) + return FALSE; + + while (p < end && *p && *p != '\n') + p++; + + *start = *addr; + *len = p - *start; + *addr = p + 1; + + return (*len > 0); +} + +void PROJECT_analyze_startup(char *addr, int len, PROJECT_COMPONENT_CALLBACK cb) +{ + char *end = &addr[len]; + char *p; + int l, i; + + if (!cb) + { + if (get_line(&addr, end, &p, &l)) + project_startup(p, l); + if (get_line(&addr, end, &p, &l)) + project_title(p, l); + get_line(&addr, end, &p, &l); // Deprecated "Stack" + get_line(&addr, end, &p, &l); // Deprecated "StackTrace" + if (get_line(&addr, end, &p, &l)) + project_version(p, l); + } + else + { + for (i = 1; i <= 5; i++) + get_line(&addr, end, &p, &l); + } + + if (get_line(&addr, end, &p, &l)) + { + project_library_path(p, l); + while (get_line(&addr, end, &p, &l)); + } + + if (!cb) + { + while (get_line(&addr, end, &p, &l)) + project_component(p, l); + + check_after_analyze(); + } + else + { + while (get_line(&addr, end, &p, &l)) + { + p[l] = 0; + (*cb)(p); + } + } +} + +void PROJECT_init(const char *file) +{ + int len; + const char *path; + + /* Save the working directory */ + + PROJECT_oldcwd = STRING_new_zero(FILE_getcwd(NULL)); + + /* Gambas installation path */ + + path = FILE_find_gambas(); + + PROJECT_exec_path = STRING_new_zero(FILE_get_dir(FILE_get_dir(path))); + + /* Component paths */ + + #ifdef OS_64BITS + COMPONENT_path = STRING_new_zero(FILE_cat(PROJECT_exec_path, GAMBAS_LIB64_PATH, NULL)); + if (access(COMPONENT_path, F_OK)) + { + STRING_free(&COMPONENT_path); + COMPONENT_path = STRING_new_zero(FILE_cat(PROJECT_exec_path, GAMBAS_LIB_PATH, NULL)); + } + #else + COMPONENT_path = STRING_new_zero(FILE_cat(PROJECT_exec_path, GAMBAS_LIB_PATH, NULL)); + #endif + + //STRING_new(&COMPONENT_user_path, FILE_cat(PROJECT_get_home(), ".local", GAMBAS_LIB_PATH, NULL), 0); + + /* Project path & name*/ + + if (!file) + { + // "gbx3 -e" case + PROJECT_path = STRING_new("", 0); + PROJECT_name = STRING_new("", 0); + return; + } + + if (*file == '.' && file[1] == '/') + file += 2; + + if (EXEC_arch) + { + if (FILE_is_relative(file)) + { + path = FILE_getcwd(file); + if (path == NULL) + goto _PANIC; + } + else + path = file; + + path = FILE_get_dir(path); + FILE_chdir(path); + } + else + { + if (FILE_is_absolute(file)) + { + path = file; + } + else + { + path = FILE_getcwd(file); + if (path == NULL) + goto _PANIC; + + if (!chdir(path)) + { + path = FILE_getcwd(NULL); + if (path == NULL) + goto _PANIC; + } + } + } + + len = strlen(path); + + while (len > 1) + { + if (path[len - 1] != '/') + break; + + len--; + /*path[len] = 0;*/ + } + + PROJECT_path = STRING_new(path, len); + + FILE_chdir(PROJECT_path); + + /* Project name */ + + if (EXEC_arch) + PROJECT_name = STRING_new_zero(FILE_get_basename(file)); + else + PROJECT_name = STRING_new_zero(FILE_get_name(PROJECT_path)); + + /* Main archive creation */ + + ARCHIVE_create_main(EXEC_arch ? FILE_get_name(file) : NULL); + + return; + +_PANIC: + ERROR_panic("Cannot initialize project: %s", strerror(errno)); +} + + +void PROJECT_load() +{ + const char *file; + int len; + + /* Project file analyze */ + + STACK_init(); + + if (EXEC_arch) + file = ".startup"; + else + file = FILE_cat(PROJECT_path, ".startup", NULL); + + TRY + { + STREAM_load(file, &project_buffer, &len); + } + CATCH + { + ERROR_fatal("unable to find startup file"); + } + END_TRY + + TRY + { + PROJECT_analyze_startup(project_buffer, len, NULL); + } + CATCH + { + if (_last_component) + ERROR_fatal("unable to load component: %s", _last_component); + else + ERROR_fatal("unable to analyze startup file"); + } + END_TRY + + // Loads all component + COMPONENT_load_all(); +} + +void PROJECT_load_finish(void) +{ + // Load exported class of components written in Gambas + COMPONENT_load_all_finish(); + + // Loads main archive + ARCHIVE_load_main(); + + // Startup class + PROJECT_class = CLASS_find(PROJECT_run_tests ? "Test" : PROJECT_startup); +} + +void PROJECT_exit(void) +{ + if (project_buffer) + FREE(&project_buffer); + + //STRING_free(&PROJECT_argname); + STRING_free(&PROJECT_name); + STRING_free(&PROJECT_path); + STRING_free(&PROJECT_oldcwd); + STRING_free(&PROJECT_exec_path); +} diff --git a/main/gbx/gbx_project.h b/main/gbx/gbx_project.h new file mode 100644 index 00000000..aa2ed93a --- /dev/null +++ b/main/gbx/gbx_project.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + gbx_project.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_PROJECT_H +#define __GBX_PROJECT_H + +#include "gbx_class.h" + +typedef + struct { + char *command; + void (*func)(); + } + PROJECT_COMMAND; + +typedef + void (*PROJECT_COMPONENT_CALLBACK)(char *); + +#ifndef __GBX_PROJECT_C +EXTERN char *PROJECT_path; +EXTERN char *PROJECT_exec_path; +EXTERN char *PROJECT_name; +EXTERN char *PROJECT_title; +EXTERN char *PROJECT_version; +EXTERN const char *PROJECT_startup; +EXTERN CLASS *PROJECT_class; +EXTERN int PROJECT_argc; +EXTERN char **PROJECT_argv; +//EXTERN char *PROJECT_argname; +EXTERN char *PROJECT_oldcwd; +EXTERN char *PROJECT_user_home; +EXTERN bool PROJECT_run_httpd; +EXTERN bool PROJECT_run_tests; +EXTERN const char *PROJECT_override; +#endif + +void PROJECT_init(const char *file); +void PROJECT_load(void); +void PROJECT_load_finish(void); +void PROJECT_exit(void); +void PROJECT_analyze_startup(char *addr, int len, PROJECT_COMPONENT_CALLBACK cb); + +#endif diff --git a/main/gbx/gbx_regexp.c b/main/gbx/gbx_regexp.c new file mode 100644 index 00000000..f56647bf --- /dev/null +++ b/main/gbx/gbx_regexp.c @@ -0,0 +1,304 @@ +/*************************************************************************** + + gbx_regexp.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_REGEXP_C + +#include "gb_common.h" +#include "gb_common_case.h" + +#include + +#include "gb_alloc.h" +#include "gb_array.h" +#include "gb_error.h" +#include "gbx_c_array.h" +#include "gbx_api.h" +#include "gb.pcre.h" + +#include "gbx_regexp.h" + + +static REGEXP_SCAN_FUNC _scan_cb = NULL; +static CARRAY *_scan_array; + +static PCRE_INTERFACE PCRE; + +static void init_pcre() +{ + static bool init = FALSE; + + if (init) + return; + + COMPONENT_load(COMPONENT_create("gb.pcre")); + LIBRARY_get_interface_by_name("gb.pcre", PCRE_INTERFACE_VERSION, &PCRE); + init = TRUE; +} + +bool REGEXP_match(const char *pattern, int len_pattern, const char *string, int len_string) +{ + unsigned char cp; + unsigned char cs; + + #define _next_pattern() (cp = *pattern++, len_pattern--) + #define _next_string(void) (cs = *string++, len_string--) + + /*if (len_pattern == 0 || len_string == 0) + return FALSE;*/ + + for(;;) + { + if (len_pattern == 0) + return (len_string == 0); + + _next_pattern(); + + if (cp == '*') + { + const char *p; + + if (len_pattern == 0) + { + if (_scan_cb) + (*_scan_cb)(string, len_string); + return TRUE; + } + + p = string; + + for(;;) + { + if (REGEXP_match(pattern, len_pattern, string, len_string)) + { + if (_scan_cb) + (*_scan_cb)(p, string - p); + return TRUE; + } + if (len_string == 0) + return FALSE; + _next_string(); + } + return FALSE; + } + + if (len_string == 0) + return FALSE; /*end || (len_pattern == 0);*/ + + _next_string(); + + if (cp == '?') + continue; + + if (cp == '[' && len_pattern > 0) + { + bool not = FALSE; + bool in = FALSE; + unsigned char cb = 0; + + _next_pattern(); + if (cp == '^') + { + not = TRUE; + _next_pattern(); + } + + if (cp == cs) + { + in = TRUE; + _next_pattern(); + } + else + { + for(;;) + { + if (cp == '-' && len_pattern > 1 && cb && cb != '-') + { + _next_pattern(); + if (cb <= cs && cs <= cp) + { + in = TRUE; + break; + } + cb = 0; + } + else if (cp == cs) + { + in = TRUE; + break; + } + else + cb = cp; + + _next_pattern(); + if (cp == ']') + break; + } + } + + for(;;) + { + if (cp == ']') + break; + if (len_pattern == 0) + THROW(E_REGEXP, "Missing ']'"); + _next_pattern(); + } + + if (in ^ not) + continue; + + return FALSE; + } + + if (cp == ' ') + { + if (cs > ' ') + return FALSE; + + while (len_string && cs <= ' ') + _next_string(); + + if (cs > ' ') + { + string--; + len_string++; + } + + while (len_pattern && cp == ' ') + _next_pattern(); + + if (cp != ' ') + { + pattern--; + len_pattern++; + } + + continue; + } + + if (cp == '{') + { + const char *save_string; + int save_len_string; + const char *save_pattern; + int save_len_pattern; + + string--; len_string++; + save_string = string; + save_len_string = len_string; + + NEXT_SUB_PATTERN: + + for(;;) + { + if (len_pattern == 0) + goto MISSING_BRACE; + _next_pattern(); + if (cp == ',' || cp == '}') + break; + _next_string(); + if (tolower(cp) != tolower(cs)) + break; + } + + if (cp == ',' || cp == '}') + { + save_pattern = pattern - 1; + save_len_pattern = len_pattern + 1; + + while (cp != '}') + { + if (len_pattern == 0) + goto MISSING_BRACE; + _next_pattern(); + } + + if (REGEXP_match(pattern, len_pattern, string, len_string)) + return TRUE; + + pattern = save_pattern; + len_pattern = save_len_pattern; + _next_pattern(); + } + + while (cp != ',') + { + if (cp == '}') + return FALSE; + + if (len_pattern == 0) + goto MISSING_BRACE; + _next_pattern(); + } + + string = save_string; + len_string = save_len_string; + + goto NEXT_SUB_PATTERN; + } + + if (cp == '\\') + { + if (len_pattern == 0) + THROW(E_REGEXP, "Trailing backslash"); + _next_pattern(); + } + + if (tolower(cp) != tolower(cs)) + return FALSE; + } + +MISSING_BRACE: + + THROW(E_REGEXP, "Missing '}'"); +} + + +static void add_string(const char *str, int len) +{ + char **p = (char **)GB_ArrayAdd((GB_ARRAY)_scan_array); + if (len) + *p = STRING_new(str, len); +} + + +bool REGEXP_scan(CARRAY *array, const char *pattern, int len_pattern, const char *string, int len_string) +{ + bool match; + + _scan_cb = add_string; + _scan_array = array; + match = REGEXP_match(pattern, len_pattern, string, len_string); + CARRAY_reverse(array, NULL); + _scan_cb = NULL; + _scan_array = NULL; + + return match; +} + +bool REGEXP_match_pcre(const char *pattern, int len_pattern, const char *string, int len_string) +{ + init_pcre(); + return PCRE.Match(string, len_string, pattern, len_pattern, 0, 0); +} + diff --git a/main/gbx/gbx_regexp.h b/main/gbx/gbx_regexp.h new file mode 100644 index 00000000..df938739 --- /dev/null +++ b/main/gbx/gbx_regexp.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gbx_regexp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_REGEXP_H +#define __GBX_REGEXP_H + +#include "gbx_c_array.h" + +typedef + void (*REGEXP_SCAN_FUNC)(const char *, int); + +bool REGEXP_match(const char *pattern, int len_pattern, const char *string, int len_string); +bool REGEXP_match_pcre(const char *pattern, int len_pattern, const char *string, int len_string); +bool REGEXP_scan(CARRAY *array, const char *pattern, int len_pattern, const char *string, int len_string); + +#endif + diff --git a/main/gbx/gbx_replace.c b/main/gbx/gbx_replace.c new file mode 100644 index 00000000..11673326 --- /dev/null +++ b/main/gbx/gbx_replace.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gbx_replace.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_replace_temp.h" + diff --git a/main/gbx/gbx_signal.c b/main/gbx/gbx_signal.c new file mode 100644 index 00000000..5a22552e --- /dev/null +++ b/main/gbx/gbx_signal.c @@ -0,0 +1,494 @@ +/*************************************************************************** + + gbx_signal.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SIGNAL_C + +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_array.h" +#include "gbx_api.h" +#include "gbx_c_process.h" +#include "gbx_c_task.h" +#include "gbx_signal.h" + +//#define DEBUG_ME 1 + +uint64_t SIGNAL_check_mask = 0; + +static SIGNAL_HANDLER *_handlers = NULL; +static int _pipe[2] = { -1, -1 }; +static volatile int _count = 0; +static int _raising_callback = 0; + +enum { CB_NONE = 0, CB_HANDLER = 1, CB_ACTION = 2 }; + +static int get_callback(struct sigaction *action, void (**callback)()) +{ + if (action->sa_handler != SIG_DFL && action->sa_handler != SIG_IGN) + { + if (action->sa_flags & SA_SIGINFO) + { + *callback = (void *)action->sa_sigaction; + return CB_ACTION; + } + else + { + *callback = (void *)action->sa_handler; + return CB_HANDLER; + } + } + else + { + *callback = NULL; + return CB_NONE; + } +} + +void SIGNAL_install(SIGNAL_HANDLER *handler, int signum, void (*callback)(int, siginfo_t *, void *)) +{ + struct sigaction action; + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_install: %d %p\n", signum, callback); + #endif + + handler->signum = signum; + + action.sa_flags = SA_SIGINFO; + // According to manpage, the emitting signal is blocked by default. + sigemptyset(&action.sa_mask); + action.sa_sigaction = callback; + + if (sigaction(signum, NULL, &handler->old_action) != 0 || sigaction(signum, &action, NULL) != 0) + ERROR_panic("Cannot install signal handler: %s", strerror(errno)); +} + +void SIGNAL_uninstall(SIGNAL_HANDLER *handler, int signum) +{ + #if DEBUG_ME + fprintf(stderr, "SIGNAL_uninstall: %d\n", signum); + #endif + + if (sigaction(signum, &handler->old_action, NULL) != 0) + ERROR_panic("Cannot uninstall signal handler"); + + while (handler->callbacks) + SIGNAL_unregister(handler->signum, handler->callbacks); +} + +void SIGNAL_previous(SIGNAL_HANDLER *handler, int signum, siginfo_t *info, void *context) +{ + void (*cb)(); + + switch (get_callback(&handler->old_action, &cb)) + { + case CB_ACTION: + (*cb)(signum, info, context); + break; + + case CB_HANDLER: + (*cb)(signum); + break; + + default: + ; /* do nothing */ + } +} + +static SIGNAL_HANDLER *find_handler(int signum) +{ + int i; + + for (i = 0; i < ARRAY_count(_handlers); i++) + { + if (_handlers[i].signum == signum) + return &_handlers[i]; + } + + return NULL; +} + +static SIGNAL_HANDLER *add_handler(void) +{ + if (!_handlers) + ARRAY_create_inc(&_handlers, 1); + + return ARRAY_add_void(&_handlers); +} + +static void handle_signal(int signum, siginfo_t *info, void *context) +{ + char buffer; + int save_errno; + static volatile int64_t lock = 0; + + if (lock & (1 << signum)) + return; + + save_errno = errno; + lock |= 1 << signum; + + #if DEBUG_ME + char digit; + write(2, "[SIGNAL:", 8); + digit = '0' + (signum / 10); + write(2, &digit, 1); + digit = '0' + (signum % 10); + write(2, &digit, 1); + write(2, "]\n", 2); + #endif + + if (_count) + { + buffer = signum; + for(;;) + { + if (write(_pipe[1], &buffer, 1) == 1) + break; + + if (errno != EINTR) + { + ERROR_warning("cannot write signal #%d into signal pipe: %s", signum, strerror(errno)); + break; + } + } + } + + SIGNAL_previous(find_handler(signum), signum, info, context); + + errno = save_errno; + lock &= ~(1 << signum); +} + +static bool _must_purge_callbacks = FALSE; +static int _purge_signum; +static SIGNAL_HANDLER *_purge_handler; + +static void purge_callbacks(void) +{ + SIGNAL_CALLBACK *cb, *next_cb; + + _raising_callback--; + if (_raising_callback) + return; + + #if DEBUG_ME + fprintf(stderr, ">> purge_callbacks\n"); + #endif + + while (_must_purge_callbacks) + { + _must_purge_callbacks = FALSE; + + cb = _purge_handler->callbacks; + while (cb) + { + #if DEBUG_ME + fprintf(stderr, "purge_callbacks: cb = %p\n", cb); + #endif + next_cb = cb->next; + + if (!cb->callback) + SIGNAL_unregister(_purge_signum, cb); + + cb = next_cb; + } + } + + #if DEBUG_ME + fprintf(stderr, "<< purge_callbacks\n"); + #endif +} + +void SIGNAL_raise_callbacks(int fd, int type, void *data) +{ + SIGNAL_HANDLER *handler; + SIGNAL_CALLBACK *cb; + char signum; + int ret; + + /*old = signal(SIGCHLD, signal_child);*/ + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: fd = %d blocking = %d\n", fd, (fcntl(fd, F_GETFL) & O_NONBLOCK) == 0); + #endif + + for(;;) + { + ret = read(fd, &signum, 1); + if (ret != 1) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: read -> %d / errno = %d\n", ret, errno); + #endif + return; + } + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: signum = %d\n", signum); + #endif + + handler = find_handler(signum); + if (!handler) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: no handler\n"); + #endif + return; + } + + #if DEBUG_ME + fprintf(stderr, ">> SIGNAL_raise_callbacks (%d)\n", _raising_callback); + #endif + + _raising_callback++; + _purge_signum = signum; + _purge_handler = handler; + + ON_ERROR(purge_callbacks) + { + cb = handler->callbacks; + while (cb) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: cb = %p cb->callback = %p\n", cb, cb->callback); + #endif + if (cb->callback) + (*cb->callback)((int)signum, cb->data); + + cb = cb->next; + } + } + END_ERROR + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: purge_callbacks\n"); + #endif + purge_callbacks(); + + #if DEBUG_ME + fprintf(stderr, "<< SIGNAL_raise_callbacks (%d)\n", _raising_callback); + #endif + } +} + +static void create_pipe(void) +{ + if (pipe(_pipe) != 0) + ERROR_panic("Cannot create signal handler pipes: %s", strerror(errno)); + + if (_pipe[0] == 0) + BREAKPOINT(); + + fcntl(_pipe[0], F_SETFD, FD_CLOEXEC); + fcntl(_pipe[1], F_SETFD, FD_CLOEXEC); + // Allows to read the signal pipe without blocking + fcntl(_pipe[0], F_SETFL, fcntl(_pipe[0], F_GETFL) | O_NONBLOCK); + + GB_Watch(_pipe[0], GB_WATCH_READ, (void *)SIGNAL_raise_callbacks, 0); + + #if DEBUG_ME + fprintf(stderr, "create_pipe: fd = %d\n", _pipe[0]); + #endif +} + +static void delete_pipe(void) +{ + #if DEBUG_ME + fprintf(stderr, "delete_pipe: fd = %d\n", _pipe[0]); + #endif + GB_Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + close(_pipe[0]); + close(_pipe[1]); + _pipe[0] = -1; + _pipe[1] = -1; +} + +SIGNAL_CALLBACK *SIGNAL_register(int signum, void (*callback)(int, intptr_t), intptr_t data) +{ + SIGNAL_HANDLER *handler; + SIGNAL_CALLBACK *cb; + + if (!_count) + create_pipe(); + + _count++; + + handler = find_handler(signum); + #if DEBUG_ME + fprintf(stderr, "SIGNAL_register: find_handler(%d) -> %p\n", signum, handler); + #endif + + if (!handler) + { + handler = add_handler(); + SIGNAL_install(handler, signum, handle_signal); + } + + ALLOC(&cb, sizeof(SIGNAL_CALLBACK)); + + cb->prev = NULL; + cb->next = handler->callbacks; + cb->callback = callback; + cb->data = data; + + if (cb->next) + cb->next->prev = cb; + handler->callbacks = cb; + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_register: %d -> %p (%p)\n", signum, cb, cb->callback); + #endif + + #if DEBUG_ME + fprintf(stderr, "handler->callbacks %p:", handler); + SIGNAL_CALLBACK *save = cb; + cb = handler->callbacks; + while (cb) + { + fprintf(stderr, " -> %p (%p)", cb, cb->callback); + cb = cb->next; + } + fprintf(stderr, "\n"); + cb = save; + #endif + + return cb; +} + +void SIGNAL_unregister(int signum, SIGNAL_CALLBACK *cb) +{ + SIGNAL_HANDLER *handler = find_handler(signum); + + if (!handler) + return; + + if (_raising_callback) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_unregister: disable %d %p (%p)\n", signum, cb, cb->callback); + #endif + cb->callback = NULL; + _must_purge_callbacks = TRUE; + return; + } + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_unregister: remove %d %p (%p)\n", signum, cb, cb->callback); + #endif + + if (cb->prev) + cb->prev->next = cb->next; + + if (cb->next) + cb->next->prev = cb->prev; + + if (cb == handler->callbacks) + handler->callbacks = cb->next; + + IFREE(cb); + + _count--; + + if (_count == 0) + delete_pipe(); + + #if DEBUG_ME + fprintf(stderr, "handler->callbacks %p:", handler); + cb = handler->callbacks; + while (cb) + { + fprintf(stderr, " -> %p (%p)", cb, cb->callback); + cb = cb->next; + } + fprintf(stderr, "\n"); + #endif +} + +void SIGNAL_exit(void) +{ + int i; + SIGNAL_HANDLER *handler; + + if (_handlers) + { + _raising_callback = 0; + for (i = 0; i < ARRAY_count(_handlers); i++) + { + handler = &_handlers[i]; + SIGNAL_uninstall(handler, handler->signum); + } + + ARRAY_delete(&_handlers); + } +} + +int SIGNAL_get_fd(void) +{ + return _pipe[0]; +} + +void SIGNAL_has_forked(void) +{ + if (!_count) + return; + + GB_Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + close(_pipe[0]); + close(_pipe[1]); + create_pipe(); +} + +void SIGNAL_do_check(int signum) +{ + struct sigaction action; + SIGNAL_HANDLER *handler = find_handler(signum); + void (*cb)(); + + if (!handler) + return; + + sigaction(signum, NULL, &action); + get_callback(&action, &cb); + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_check: %d -> %d (action.sa_sigaction = %p)\n", signum, cb == handle_signal, cb); + if (cb != handle_signal) + BREAKPOINT(); + #endif + + if (cb == handle_signal) + return; + + SIGNAL_install(handler, signum, handle_signal); + + if (signum == SIGCHLD) + { + CPROCESS_callback_child(); + CTASK_callback_child(); + } +} + +void SIGNAL_must_check(int signum) +{ + SIGNAL_check_mask |= (1 << signum); +} diff --git a/main/gbx/gbx_signal.h b/main/gbx/gbx_signal.h new file mode 100644 index 00000000..4fec7309 --- /dev/null +++ b/main/gbx/gbx_signal.h @@ -0,0 +1,69 @@ +/*************************************************************************** + + gbx_signal.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_SIGNAL_H +#define __GBX_SIGNAL_H + +#include +#include + +#ifndef __GBX_SIGNAL_C +extern uint64_t SIGNAL_check_mask; +#endif + +typedef + struct SIGNAL_CALLBACK { + struct SIGNAL_CALLBACK *prev; + struct SIGNAL_CALLBACK *next; + void (*callback)(int, intptr_t); + intptr_t data; + } + SIGNAL_CALLBACK; + +typedef + struct { + int signum; + struct sigaction old_action; + SIGNAL_CALLBACK *callbacks; + } + SIGNAL_HANDLER; + +void SIGNAL_install(SIGNAL_HANDLER *handler, int signum, void (*callback)(int, siginfo_t *, void *)); +void SIGNAL_uninstall(SIGNAL_HANDLER *handler, int signum); +void SIGNAL_previous(SIGNAL_HANDLER *handler, int signum, siginfo_t *info, void *context); +void SIGNAL_do_check(int signum); +void SIGNAL_must_check(int signum); + +#define SIGNAL_check(_signum) { if (SIGNAL_check_mask & (1 << (_signum))) SIGNAL_do_check(_signum); } + +SIGNAL_CALLBACK *SIGNAL_register(int signum, void (*callback)(int, intptr_t), intptr_t data); +void SIGNAL_unregister(int signum, SIGNAL_CALLBACK *cb); + +int SIGNAL_get_fd(void); +void SIGNAL_raise_callbacks(int fd, int type, void *data); +void SIGNAL_exit(void); +void SIGNAL_has_forked(void); + +#endif + + diff --git a/main/gbx/gbx_split.c b/main/gbx/gbx_split.c new file mode 100644 index 00000000..0559224f --- /dev/null +++ b/main/gbx/gbx_split.c @@ -0,0 +1,243 @@ +/*************************************************************************** + + gbx_split.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SPLIT_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_array.h" +#include "gbx_c_array.h" + +static CARRAY *_array; +static bool _novoid; +static char *_entry; +static const char *_ptr; +static int _lptr; + +static void add_char_real(const char *p) +{ + if (_lptr) + { + int old_len = STRING_length(_entry); + + _entry = STRING_extend(_entry, old_len + _lptr); + memcpy(&_entry[old_len], _ptr, _lptr); + _entry[old_len + _lptr] = 0; + } + + _ptr = p; + _lptr = p ? 1 : 0; +} + +#define add_char(_p) \ +({ \ + if ((_p) && (_p) == (_ptr + _lptr)) \ + _lptr++; \ + else \ + add_char_real(_p); \ +}) + +static void add_entry() +{ + add_char_real(NULL); + + if (!_entry) + { + if (!_novoid) + ARRAY_add_void((char ***)&_array->data); + } + else + { + *((char **)ARRAY_add((char ***)&_array->data)) = _entry; + _entry = NULL; + } + + //fprintf(stderr, "** add_entry\n"); +} + +static void split_fast(CARRAY *array, const char *str, int lstr, const char *sep, int lsep, bool no_void) +{ + const char *ptr = NULL; + int lptr = 0; + + #define add_entry_fast() \ + ({ \ + if (lptr) \ + { \ + *((char **)ARRAY_add((char ***)&array->data)) = STRING_new(ptr, lptr); \ + lptr = 0; \ + } \ + else if (!no_void) \ + { \ + ARRAY_add_void((char ***)&array->data); \ + } \ + }) + + if (lsep == 1) + { + char csep = sep[0]; + + while (lstr--) + { + if (*str == csep) + { + add_entry_fast(); + } + else + { + if (!lptr) ptr = str; + lptr++; + } + + str++; + } + } + else + { + while (lstr--) + { + if (memchr(sep, *str, lsep)) + { + add_entry_fast(); + } + else + { + if (!lptr) ptr = str; + lptr++; + } + + str++; + } + } + + add_entry_fast(); +} + +CARRAY *STRING_split(const char *str, int lstr, const char *sep, int lsep, const char *esc, int lesc, bool no_void, bool keep_esc) +{ + CARRAY *array; + int i; + char c; + bool escape; + char escl, escr; + + array = OBJECT_create(CLASS_StringArray, NULL, NULL, 0); + if (lstr == 0) + return array; + + if (sep == NULL || lsep == 0) + { + sep = ","; + lsep = 1; + } + + if (esc == NULL || lesc == 0) + { + split_fast(array, str, lstr, sep, lsep, no_void); + } + else + { + _array = array; + _entry = NULL; + _novoid = no_void; + _ptr = NULL; + _lptr = 0; + + escl = esc[0]; + if (lesc >= 2) + escr = esc[1]; + else + escr = escl; + + if (escr == *sep) + { + for (i = 0; i < lstr; i++) + { + c = *str; + + if (c == escl) + { + i++; + str++; + if (i < lstr) + add_char(str); + } + else if (c == *sep || (lsep > 1 && memchr(&sep[1], c, lsep - 1))) + { + add_entry(); + } + else + add_char(str); + + str++; + } + } + else + { + escape = FALSE; + + for (i = 0; i < lstr; i++) + { + c = *str; + + if (escape) + { + if (c != escr) + add_char(str); + else if ((i < (lstr - 1)) && str[1] == escr) + { + add_char(str); + str++; + i++; + } + else + { + escape = FALSE; + if (keep_esc) + add_char(str); + } + } + else if (c == escl) + { + escape = TRUE; + if (keep_esc) + add_char(str); + } + else if (c == *sep || (lsep > 1 && memchr(&sep[1], c, lsep - 1))) + { + add_entry(); + } + else + add_char(str); + + str++; + } + } + + add_entry(); + } + + array->count = ARRAY_count(array->data); + + return array; +} diff --git a/main/gbx/gbx_split.h b/main/gbx/gbx_split.h new file mode 100644 index 00000000..8ced963f --- /dev/null +++ b/main/gbx/gbx_split.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + gbx_split.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_SPLIT_H +#define __GBX_SPLIT_H + +#include "gbx_c_array.h" + +CARRAY *STRING_split(const char *str, int lstr, const char *sep, int lsep, const char *esc, int lesc, bool no_void, bool keep_esc); + +#endif \ No newline at end of file diff --git a/main/gbx/gbx_stack.c b/main/gbx/gbx_stack.c new file mode 100644 index 00000000..4e23df15 --- /dev/null +++ b/main/gbx/gbx_stack.c @@ -0,0 +1,187 @@ +/*************************************************************************** + + gbx_stack.c + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_STACK_C + +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gbx_exec.h" +#include "gb_error.h" +#include "gbx_string.h" +#include "gbx_stack.h" + +char *STACK_base = NULL; +size_t STACK_size; +char *STACK_limit = NULL; +STACK_CONTEXT *STACK_frame; +uint STACK_frame_count; +uint STACK_frame_barrier; + +uintptr_t STACK_process_stack_limit; + +void STACK_init(void) +{ + int stack; + struct rlimit limit; + uintptr_t max; + + // Get the maximum stack size allowed + if (getrlimit(RLIMIT_STACK, &limit)) + ERROR_panic("Cannot get stack size limit"); + + if (limit.rlim_cur == RLIM_INFINITY) + max = 64 << 20; // 64 Mb if there is no limit. + else + max = (uintptr_t)limit.rlim_cur; + + STACK_size = max - sizeof(VALUE) * 256; // some security + #if DEBUG_STACK + fprintf(stderr, "STACK_size = %ld\n", STACK_size); + #endif + + STACK_base = mmap(NULL, STACK_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + + //fprintf(stderr, "Stack = %p %ld\n", STACK_base, STACK_size); + + STACK_process_stack_limit = (uintptr_t)&stack - max + 65536; + + STACK_limit = STACK_base + STACK_size; + STACK_frame = (STACK_CONTEXT *)STACK_limit; + STACK_frame_count = 0; + STACK_frame_barrier = 0; + STACK_limit -= STACK_FOR_EVAL * sizeof(VALUE); + + SP = (VALUE *)STACK_base; +} + + +void STACK_exit(void) +{ + if (STACK_base) + { + munmap(STACK_base, STACK_size); + STACK_base = NULL; + } +} + +#if DEBUG_STACK +bool STACK_check(int need) +{ + static VALUE *old = NULL; + + fprintf(stderr, "STACK_check: SP = %d need = %d limit = %d\n", (int)(((char *)SP - STACK_base) / sizeof(VALUE)), need, (int)((STACK_limit - STACK_base) / sizeof(VALUE))); + + if (SP > old) + { + fprintf(stderr, "**** STACK_check: -> %ld bytes\n", ((char *)SP - STACK_base)); + old = SP; + } + + if (((char *)(SP + need) + sizeof(STACK_CONTEXT)) >= STACK_limit) + { + THROW_STACK(); + return TRUE; + } + else + return FALSE; +} +#endif + +bool STACK_has_error_handler(void) +{ + uint i; + STACK_CONTEXT *sc; + uint b = STACK_frame_count - STACK_frame_barrier; + + for (i = 0; i < b; i++) + { + sc = &STACK_frame[i]; + if (sc->ec || sc->ep) + return TRUE; + } + + return FALSE; +} + +STACK_CONTEXT *STACK_get_frame(uint frame) +{ + if (frame >= 0 && frame < STACK_frame_count) + return &STACK_frame[frame]; + else + return NULL; +} + +STACK_BACKTRACE *STACK_get_backtrace(void) +{ + STACK_BACKTRACE *bt, *pbt; + uint i; + + if (STACK_frame_count == 0) + return NULL; + + ALLOC(&bt, sizeof(STACK_BACKTRACE) * (1 + STACK_frame_count)); + + bt->cp = CP; + bt->fp = FP; + bt->pc = PC; + + for (i = 0, pbt = &bt[1]; i < STACK_frame_count; i++, pbt++) + { + pbt->cp = STACK_frame[i].cp; + pbt->fp = STACK_frame[i].fp; + pbt->pc = STACK_frame[i].pc; + } + + // Mark the end of the backtrace + pbt--; + STACK_backtrace_set_end(pbt); + + return bt; +} + +STACK_BACKTRACE *STACK_copy_backtrace(STACK_BACKTRACE *bt) +{ + STACK_BACKTRACE *copy; + int i, n; + + for (i = 0;; i++) + { + if (STACK_backtrace_is_end(&bt[i])) + { + n = i + 1; + break; + } + } + + ALLOC(©, sizeof(STACK_BACKTRACE) * n); + + for (i = 0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_STACK_H +#define __GBX_STACK_H + +#include "gbx_value.h" +#include "gb_pcode.h" + +//#define DEBUG_STACK 1 + +typedef + struct { + void *cp; + void *fp; + void *pc; + } + STACK_BACKTRACE; + +typedef + struct _stack_context { + struct _stack_context *next; + VALUE *bp; // local variables + VALUE *pp; // local parameters + CLASS *cp; // current class + char *op; // current object + VALUE *ep; // error pointer + FUNCTION *fp; // current function + PCODE *pc; // instruction + PCODE *ec; // instruction if error + PCODE *et; // TRY save + VALUE *gp; // GOSUB stack pointer + } + STACK_CONTEXT; + +#ifndef __GBX_STACK_C + +EXTERN char *STACK_base; +EXTERN size_t STACK_size; +EXTERN char *STACK_limit; +//EXTERN size_t STACK_relocate; + +EXTERN uint STACK_frame_count; +EXTERN STACK_CONTEXT *STACK_frame; + +EXTERN uintptr_t STACK_process_stack_limit; + +EXTERN uint STACK_frame_barrier; + +#endif + +#define STACK_FOR_EVAL 16 + +void STACK_init(void); +void STACK_exit(void); + +#if DEBUG_STACK +bool STACK_check(int need); +#else + +#define STACK_check(_need) \ +do { \ + if (((char *)(SP + (_need)) + sizeof(STACK_CONTEXT)) >= STACK_limit) \ + THROW_STACK(); \ + } \ +while (0); + +#endif + +bool STACK_has_error_handler(void); + +STACK_BACKTRACE *STACK_get_backtrace(void); +STACK_BACKTRACE *STACK_copy_backtrace(STACK_BACKTRACE *bt); +#define STACK_free_backtrace(_backtrace) FREE(_backtrace) +#define STACK_backtrace_is_end(_bt) ((((intptr_t)((_bt)->cp)) & 1) != 0) +#define STACK_backtrace_set_end(_bt) ((_bt)->cp = (void *)(((intptr_t)((_bt)->cp)) | 1)) +#define STACK_backtrace_clear_end(_bt) ((_bt)->cp = (void *)(((intptr_t)((_bt)->cp)) & ~1)) + + +STACK_CONTEXT *STACK_get_frame(uint frame); + +#define STACK_get_previous_pc() ((STACK_frame_count == 0) ? NULL : STACK_frame->pc) + +#define STACK_get_current() ((STACK_frame_count > 0) ? STACK_frame : NULL) + +#define STACK_copy(_dst, _src) *(_dst) = *(_src) + +#define STACK_push_frame(_context, _need) \ +({ \ + int stack; \ + if ((uintptr_t)&stack < STACK_process_stack_limit) \ + THROW_STACK(); \ + \ + STACK_check(_need); \ + \ + STACK_frame--; \ + \ + STACK_copy(STACK_frame, _context); \ + \ + STACK_frame_count++; \ + STACK_limit -= sizeof(STACK_CONTEXT); \ +}) + +#define STACK_pop_frame(_context) \ +({ \ + STACK_copy(_context, STACK_frame); \ + STACK_frame++; \ + STACK_frame_count--; \ + STACK_limit += sizeof(STACK_CONTEXT); \ + EXEC_check_bytecode(); \ +}) + +#define STACK_enable_for_eval() STACK_limit += STACK_FOR_EVAL * sizeof(VALUE) +#define STACK_disable_for_eval() STACK_limit -= STACK_FOR_EVAL * sizeof(VALUE) + +#define STACK_push_barrier() \ +({ \ + int old = STACK_frame_barrier; \ + STACK_frame_barrier = STACK_frame_count + 1; \ + old; \ +}) + +#define STACK_pop_barrier(_old) (STACK_frame_barrier = (_old)) + +#endif diff --git a/main/gbx/gbx_stream.c b/main/gbx/gbx_stream.c new file mode 100644 index 00000000..97ceba83 --- /dev/null +++ b/main/gbx/gbx_stream.c @@ -0,0 +1,2227 @@ +/*************************************************************************** + + gbx_stream.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_STREAM_C + +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common_buffer.h" +#include "gb_common_swap.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" +#include "gbx_exec.h" +#include "gbx_project.h" +#include "gb_file.h" +#include "gambas.h" +#include "gbx_regexp.h" +#include "gbx_api.h" +#include "gbx_string.h" +#include "gbx_watch.h" +#include "gbx_c_array.h" +#include "gbx_c_collection.h" +#include "gbx_struct.h" +#include "gbx_c_file.h" +#include "gbx_stream.h" + +#define EXTRA(_stream) ((_stream)->common.extra) + +static CFILE *_temp_stream = NULL; +static STREAM *_temp_save = NULL; +static int _temp_level; + +#if DEBUG_STREAM +static unsigned char _tag = 0; +static int _nopen = 0; +#endif + +static void THROW_SERIAL(void) +{ + THROW(E_SERIAL); +} + +void STREAM_exit(void) +{ +#if DEBUG_STREAM + if (_nopen) + ERROR_warning("%d streams yet opened", _nopen); +#endif + OBJECT_UNREF(_temp_stream); +} + +static STREAM_EXTRA *ENSURE_EXTRA(STREAM *stream) +{ + if (!stream->common.extra) + ALLOC_ZERO(&stream->common.extra, sizeof(STREAM_EXTRA)); + return stream->common.extra; +} + +static void wait_for_fd_ready_to_read(int fd) +{ + if (fd >= 0) + WATCH_process(fd, -1, -1, 0); +} + +bool STREAM_in_archive(const char *path) +{ + ARCHIVE *arch; + + if (FILE_is_relative(path)) + { + ARCHIVE_get_current(&arch); + if (arch->name || EXEC_arch) // || !FILE_exist_real(path)) - Why that test ? + return TRUE; + } + + return FALSE; +} + +bool STREAM_get_readable(STREAM *stream, int *len) +{ + int fd; + off_t off; + off_t end; + + fd = STREAM_handle(stream); + if (fd < 0) + return TRUE; + +//_IOCTL: + + #ifdef FIONREAD + + if (!stream->common.no_fionread) + { + if (ioctl(fd, FIONREAD, len) >= 0) + return FALSE; + + stream->common.no_fionread = TRUE; + } + + #endif + +//_LSEEK: + + if (!stream->common.no_lseek) + { + off = lseek(fd, 0, SEEK_CUR); + if (off >= 0) + { + end = lseek(fd, 0, SEEK_END); + if (end >= 0) + { + *len = (int)(end - off); + + off = lseek(fd, off, SEEK_SET); + if (off >= 0) + return FALSE; + } + } + + stream->common.no_lseek = TRUE; + } + + //fprintf(stderr, "STREAM_get_readable: lseek: %d\n", *len); + return TRUE; +} + +bool STREAM_default_eof(STREAM *stream) +{ + int fd; + int ilen; + + fd = STREAM_handle(stream); + if (fd < 0) + return TRUE; + + if (STREAM_is_blocking(stream) && !stream->common.available_now) + wait_for_fd_ready_to_read(STREAM_handle(stream)); + + if (!STREAM_get_readable(stream, &ilen)) + return (ilen == 0); + + return FALSE; // Unable to get the remaining size. +} + +// STREAM_open *MUST* initialize completely the stream structure + +void STREAM_open(STREAM *stream, const char *path, int mode) +{ + STREAM_CLASS *sclass; + int fd; + + stream->type = NULL; + + if (mode & GB_ST_PIPE) + sclass = &STREAM_pipe; + else if (mode & GB_ST_MEMORY) + sclass = &STREAM_memory; + else if (mode & GB_ST_STRING) + sclass = &STREAM_string; + else if (mode & GB_ST_LOCK) + sclass = &STREAM_lock; + else if (mode & GB_ST_NULL) + sclass = &STREAM_null; + else + { + // ".99" is used for opening a file descriptor in direct mode + + if (FILE_is_relative(path) && !(((mode & GB_ST_BUFFERED) == 0) && path[0] == '.' && isdigit(path[1]))) + { + ARCHIVE *arch = NULL; + const char *tpath = path; + + /*ARCHIVE *arch = NULL; + + if (strncmp(path, "../", 3)) + ARCHIVE_get_current(&arch); + else if (!EXEC_arch) + path += 3; + + if ((arch && arch->name) || EXEC_arch) // || !FILE_exist_real(path)) - Why that test ? + { + sclass = &STREAM_arch; + goto _OPEN; + }*/ + + if ((mode & GB_ST_ACCESS) != GB_ST_READ || mode & GB_ST_PIPE) + THROW(E_ACCESS); + + if (!ARCHIVE_find_from_path(&arch, &tpath)) + { + sclass = &STREAM_arch; + goto __OPEN; + } + + path = tpath; + } + + if (mode & GB_ST_BUFFERED) + sclass = &STREAM_buffer; + else + sclass = &STREAM_direct; + } + +__OPEN: + + stream->common.mode = mode; + stream->common.swap = FALSE; + stream->common.eol = 0; + stream->common.eof = FALSE; + stream->common.extra = NULL; + stream->common.no_fionread = FALSE; + stream->common.no_lseek = FALSE; + stream->common.standard = FALSE; + stream->common.blocking = TRUE; + stream->common.available_now = FALSE; + stream->common.redirected = FALSE; + stream->common.no_read_ahead = FALSE; + stream->common.null_terminated = FALSE; + stream->common.check_read = FALSE; + + if ((*(sclass->open))(stream, path, mode, NULL)) + THROW_SYSTEM(errno, path); + + stream->type = sclass; + + fd = STREAM_handle(stream); + if (fd >= 0) + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | O_CLOEXEC); + + #if DEBUG_STREAM + _tag++; + stream->common.tag = _tag; + _nopen++; + fprintf(stderr, "Open %p [%d] (%d)\n", stream, _tag, _nopen); + #endif +} + +static void release_buffer(STREAM_EXTRA *extra) +{ + if (extra && extra->buffer) + { + #if DEBUG_STREAM + fprintf(stderr, "Stream %p [%d]: Free buffer\n", stream, stream->common.tag); + #endif + FREE(&extra->buffer); + extra->buffer_pos = 0; + extra->buffer_len = 0; + } +} + +static void release_unread(STREAM_EXTRA *extra) +{ + if (extra && extra->unread) + { + FREE(&extra->unread); + extra->unread_pos = 0; + extra->unread_len = 0; + } +} + +void STREAM_release(STREAM *stream) +{ + STREAM_EXTRA *extra = EXTRA(stream); + + STREAM_cancel(stream); + release_buffer(extra); + release_unread(extra); + + if (extra) + { + ARRAY_delete(&extra->read_objects); + HASH_TABLE_delete(&extra->write_objects); + IFREE(extra); + stream->common.extra = NULL; + } +} + +static void stop_watching(STREAM *stream, int mode) +{ + int fd = STREAM_handle(stream); + if (fd >= 0) + GB_Watch(fd, mode, NULL, 0); +} + +void STREAM_close(STREAM *stream) +{ + STREAM_release(stream); + + if (!stream->type) + return; + + stop_watching(stream, GB_WATCH_NONE); + + if (!stream->common.standard) + { + if ((*(stream->type->close))(stream)) + { + if (errno != EBADF && errno != EINPROGRESS && errno != EAGAIN) + THROW_SYSTEM(errno, ""); + } + } + + stream->type = NULL; + + #if DEBUG_STREAM + _nopen--; + fprintf(stderr, "Close %p [%d] (%d)\n", stream, stream->common.tag, _nopen); + #endif +} + + +void STREAM_flush(STREAM *stream) +{ + STREAM_end(stream); + + if (!stream->type) + THROW(E_CLOSED); + + (*(stream->type->flush))(stream); +} + +static int read_unread(STREAM_EXTRA *extra, void *addr, int len) +{ + int l = extra->unread_len - extra->unread_pos; + if (l > len) + l = len; + + if (l > 0) + { + memcpy(addr, extra->unread + extra->unread_pos, l); + extra->unread_pos += l; + if (extra->unread_pos >= extra->unread_len) + release_unread(extra); + return l; + } + else + return 0; +} + +static int read_buffer(STREAM_EXTRA *extra, void *addr, int len) +{ + int l = extra->buffer_len - extra->buffer_pos; + if (l > len) + l = len; + if (l > 0) + { + memcpy(addr, extra->buffer + extra->buffer_pos, l); + extra->buffer_pos += l; + return l; + } + else + return 0; +} + +int STREAM_read(STREAM *stream, void *addr, int len) +{ + int eff = 0; + int n; + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (len <= 0) + return 0; + + if (EXTRA(stream)) + { + if (EXTRA(stream)->unread) + { + eff = read_unread(EXTRA(stream), addr, len); + addr += eff; + len -= eff; + + return eff; + } + + if (len > 0 && EXTRA(stream)->buffer) + { + eff = read_buffer(EXTRA(stream), addr, len); + addr += eff; + len -= eff; + } + } + + while (len > 0) + { + n = (*(stream->type->read))(stream, addr, len); + + if ((n <= 0) && errno != EINTR) + { + stop_watching(stream, GB_WATCH_READ); + + if (n == 0) + THROW(E_EOF); + + switch(errno) + { + case 0: + case EAGAIN: + THROW(E_EOF); + case EIO: + THROW(E_READ); + default: + THROW_SYSTEM(errno, NULL); + } + } + + eff += n; + addr += n; + len -= n; + } + + return eff; +} + +void STREAM_peek(STREAM *stream, void *addr, int len) +{ + STREAM_EXTRA *extra = ENSURE_EXTRA(stream); + + release_unread(extra); + + STREAM_read(stream, addr, len); + + ALLOC(&extra->unread, len); + extra->unread_len = len; + extra->unread_pos = 0; + memcpy(extra->unread, addr, len); +} + +#if 0 +char STREAM_getchar(STREAM *stream) +{ + char c = 0; + bool ret; + + if (!stream->type) + THROW(E_CLOSED); + + if (stream->common.buffer && stream->common.buffer_pos < stream->common.buffer_len) + return stream->common.buffer[stream->common.buffer_pos++]; + + for(;;) + { + if (stream->type->getchar) + ret = (*(stream->type->getchar))(stream, &c); + else + ret = (*(stream->type->read))(stream, &c, 1); + + if (ret == 1) + break; + + if (errno == EINTR) + continue; + + stop_watching(stream, GB_WATCH_READ); + + switch(errno) + { + case 0: + case EAGAIN: + THROW(E_EOF); + case EIO: + THROW(E_READ); + default: + THROW_SYSTEM(errno, NULL); + } + } + + return c; +} +#endif + + +int STREAM_read_max(STREAM *stream, void *addr, int len) +{ + int eff = 0; + int n; + int flags, handle; + int save_errno; + + if (!stream->type) + THROW(E_CLOSED); + + if (len <= 0) + return 0; + + if (EXTRA(stream)) + { + if (EXTRA(stream)->unread) + { + eff = read_unread(EXTRA(stream), addr, len); + addr += eff; + len -= eff; + + return eff; + } + + if (len > 0 && EXTRA(stream)->buffer) + { + eff = read_buffer(EXTRA(stream), addr, len); + addr += eff; + len -= eff; + } + } + + while (len > 0) + { + if (stream->common.available_now) + { + n = (*(stream->type->read))(stream, addr, len); + } + else + { + handle = STREAM_handle(stream); + flags = fcntl(handle, F_GETFL); + if ((flags & O_NONBLOCK) == 0) + { + wait_for_fd_ready_to_read(handle); + fcntl(handle, F_SETFL, flags | O_NONBLOCK); + } + + errno = 0; + n = (*(stream->type->read))(stream, addr, len); + save_errno = errno; + + if ((flags & O_NONBLOCK) == 0) + fcntl(handle, F_SETFL, flags); + + errno = save_errno; + } + + if (n > 0) + { + eff += n; + break; + } + + if (n <= 0 && errno != EINTR) + { + if (n == 0 || errno == 0) + { + stop_watching(stream, GB_WATCH_READ); + return eff; + } + + switch(errno) + { + case EAGAIN: + case EIO: + return eff; + default: + THROW_SYSTEM(errno, NULL); + } + } + + addr += n; + len -= n; + } + + return eff; +} + + +void STREAM_write(STREAM *stream, void *addr, int len) +{ + int n; + + if (STREAM_is_closed_for_writing(stream)) + THROW(E_CLOSED); + + if (len <= 0) + return; + + if (stream->common.redirected) + stream = EXTRA(stream)->redirect; + + do + { + n = ((*(stream->type->write))(stream, addr, len)); + + if (n <= 0 && errno != EINTR) + { + switch(errno) + { + case 0: + case EIO: + THROW(E_WRITE); + default: + THROW_SYSTEM(errno, NULL); + } + } + + addr += n; + len -= n; + + if (STREAM_is_closed_for_writing(stream)) + THROW(E_CLOSED); + } + while (len > 0); +} + + +void STREAM_write_zeros(STREAM *stream, int len) +{ + static const char buffer[32] = { 0 }; + int lenw; + + while (len > 0) + { + lenw = Min(len, sizeof(buffer)); + STREAM_write(stream, (void *)buffer, lenw); + len -= lenw; + } +} + + +void STREAM_write_eol(STREAM *stream) +{ + if (STREAM_is_closed_for_writing(stream)) + THROW(E_CLOSED); + + switch(stream->common.eol) + { + case ST_EOL_UNIX: STREAM_write(stream, "\n", 1); break; + case ST_EOL_WINDOWS: STREAM_write(stream, "\r\n", 2); break; + case ST_EOL_MAC: STREAM_write(stream, "\r", 1); break; + } +} + + +int64_t STREAM_tell(STREAM *stream) +{ + int64_t pos; + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (stream->type->tell(stream, &pos)) + /*THROW(E_SEEK, ERROR_get());*/ + THROW_SYSTEM(errno, NULL); + + return pos; +} + + +void STREAM_seek(STREAM *stream, int64_t pos, int whence) +{ + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (stream->type->seek(stream, pos, whence)) + { + switch(errno) + { + case EINVAL: + THROW(E_ARG); + default: + THROW_SYSTEM(errno, NULL); + } + } + + release_buffer(EXTRA(stream)); +} + +static int fill_buffer(STREAM *stream, char *addr, bool do_not_wait_ready) +{ + int n; + int flags, fd; + int len; + int eff = 0; + + fd = STREAM_handle(stream); + len = STREAM_BUFFER_SIZE; + + while (len > 0) + { + if (stream->common.available_now) + n = (*(stream->type->read))(stream, addr, len); + else + { + if (!do_not_wait_ready) + { + wait_for_fd_ready_to_read(fd); + do_not_wait_ready = TRUE; + } + + flags = fcntl(fd, F_GETFL); + if ((flags & O_NONBLOCK) == 0) + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + n = (*(stream->type->read))(stream, addr, len); + + if ((flags & O_NONBLOCK) == 0) + { + int save_errno = errno; + fcntl(fd, F_SETFL, flags); + errno = save_errno; + } + } + + if (n == 0) + return eff; + + if (n > 0) + eff += n; + + if (n < 0 && errno != EINTR) + { + switch(errno) + { + case EAGAIN: + case EINPROGRESS: + case EIO: + return eff; + default: + THROW_SYSTEM(errno, NULL); + } + } + + addr += n; + len -= n; + } + + return eff; +} + +bool STREAM_read_ahead(STREAM *stream) +{ + int eff; + STREAM_EXTRA *extra = EXTRA(stream); + + if (stream->common.no_read_ahead) + return FALSE; + + if (extra && extra->buffer && extra->buffer_pos < extra->buffer_len) + return FALSE; + + extra = ENSURE_EXTRA(stream); + + if (!extra->buffer) + ALLOC(&extra->buffer, STREAM_BUFFER_SIZE); + + eff = fill_buffer(stream, extra->buffer, TRUE); + + extra->buffer_pos = 0; + extra->buffer_len = eff; + + if (eff == 0) + { + stream->common.eof = TRUE; + return TRUE; + } + else + { + stream->common.eof = FALSE; + return FALSE; + } +} + + +static char *input(STREAM *stream, bool line, char *escape) +{ + unsigned char mode; + int len = 0; + int start; + unsigned char c = 0, lc = 0; + void *test; + char *buffer; + int buffer_len; + int buffer_pos; + char *addr; + char ec; + bool inside_escape = FALSE; + STREAM_EXTRA *extra; + + addr = NULL; + + mode = 2; + ec = escape ? *escape : 0; + + if (!line) + test = &&__TEST_INPUT; + else + { + switch(stream->common.eol) + { + case GB_EOL_WINDOWS: test = &&__TEST_WINDOWS; break; + case GB_EOL_MAC: test = &&__TEST_MAC; break; + default: test = NULL; mode = ec ? 1 : 0; break; + } + } + + stream->common.eof = FALSE; + + if (!STREAM_is_blocking(stream) && STREAM_eof(stream)) + THROW(E_EOF); + + extra = ENSURE_EXTRA(stream); + + buffer = extra->buffer; + buffer_len = extra->buffer_len; + buffer_pos = extra->buffer_pos; + + if (!buffer) + { + #if DEBUG_STREAM + fprintf(stderr, "Stream %p [%d]: Alloc buffer\n", stream, stream->common.tag); + #endif + ALLOC(&buffer, STREAM_BUFFER_SIZE); + buffer_pos = 0; + buffer_len = 0; + } + + start = buffer_pos; + + for(;;) + { + if (mode == 0) + { + while (buffer_pos < buffer_len) + { + c = buffer[buffer_pos++]; //STREAM_getchar(stream); + + if (c == '\n') + { + len = buffer_pos - start - 1; + if (buffer_pos) + lc = buffer[buffer_pos - 1]; + if (lc == '\r') + len--; + goto __FINISH; + } + } + + len = buffer_len - start; + } + else if (mode == 1) + { + while (buffer_pos < buffer_len) + { + c = buffer[buffer_pos++]; //STREAM_getchar(stream); + + if (c == ec) + inside_escape = !inside_escape; + else if (!inside_escape) + { + if (c == '\n') + { + len = buffer_pos - start - 1; + if (buffer_pos) + lc = buffer[buffer_pos - 1]; + if (lc == '\r') + len--; + goto __FINISH; + } + } + } + + len = buffer_pos - start; + } + else + { + while (buffer_pos < buffer_len) + { + c = buffer[buffer_pos++]; //STREAM_getchar(stream); + + if (ec && c == ec) + { + inside_escape = !inside_escape; + continue; + } + + if (inside_escape) + continue; + + goto *test; + + __TEST_INPUT: + + if (c <= ' ') + { + len = buffer_pos - start - 1; + goto __FINISH; + } + else + continue; + + __TEST_MAC: + + if (c == '\r') + { + len = buffer_pos - start - 1; + goto __FINISH; + } + else + continue; + + __TEST_WINDOWS: + + if ((lc == '\r') && (c == '\n')) + { + len = buffer_pos - start - 2; + goto __FINISH; + } + else + { + lc = c; + continue; + } + } + + len = buffer_pos - start; + } + + lc = c; + + if (len) + { + if (!addr) + addr = STRING_new(buffer + start, len); + else + addr = STRING_add(addr, buffer + start, len); + len = 0; + } + + extra->buffer = buffer; + extra->buffer_pos = buffer_pos; + extra->buffer_len = buffer_len; + + buffer_pos = 0; + buffer_len = fill_buffer(stream, buffer, FALSE); + + if (!buffer_len) + { + stream->common.eof = TRUE; + break; + } + + start = 0; + } + +__FINISH: + + if (len > 0) + { + if (!addr) + addr = STRING_new(buffer + start, len); + else + addr = STRING_add(addr, buffer + start, len); + } + else if (len < 0) + addr = STRING_extend(addr, STRING_length(addr) + len); + + extra->buffer = buffer; + extra->buffer_pos = buffer_pos; + extra->buffer_len = buffer_len; + + return addr; +} + + +char *STREAM_line_input(STREAM *stream, char *escape) +{ + return input(stream, TRUE, escape); +} + + +char *STREAM_input(STREAM *stream) +{ + return input(stream, FALSE, NULL); +} + + +static int read_length(STREAM *stream) +{ + union + { + unsigned char _data[4]; + short _short; + int _int; + } + buffer; + + int len = 0; + + STREAM_read(stream, buffer._data, 1); + + switch (buffer._data[0] >> 6) + { + case 0: + case 1: + len = buffer._data[0]; + break; + + case 2: + STREAM_read(stream, &buffer._data[1], 1); + buffer._data[0] &= 0x3F; + + if (!EXEC_big_endian) + SWAP_short(&buffer._short); + + len = buffer._short; + break; + + case 3: + STREAM_read(stream, &buffer._data[1], 3); + buffer._data[0] &= 0x3F; + + if (!EXEC_big_endian) + SWAP_int(&buffer._int); + + len = buffer._int; + break; + } + + return len; +} + +static STREAM *enter_temp_stream(STREAM *stream) +{ + if (stream != CSTREAM_TO_STREAM(_temp_stream)) + { + _temp_save = stream; + _temp_level = 0; + + OBJECT_UNREF(_temp_stream); + _temp_stream = OBJECT_new(CLASS_File, NULL, NULL); + STREAM_open(CSTREAM_TO_STREAM(_temp_stream), NULL, GB_ST_STRING | GB_ST_WRITE); + } + + _temp_level++; + + return CSTREAM_TO_STREAM(_temp_stream); +} + +static STREAM *leave_temp_stream(void) +{ + STREAM *stream = CSTREAM_TO_STREAM(_temp_stream); + + _temp_level--; + if (_temp_level > 0) + return stream; + + STREAM_write(_temp_save, stream->string.buffer, STRING_length(stream->string.buffer)); + STREAM_close(stream); + OBJECT_UNREF(_temp_stream); + return _temp_save; +} + +static void read_structure(STREAM *stream, CLASS *class, char *base); + +static void read_value_ctype(STREAM *stream, CLASS *class, CTYPE ctype, void *addr) +{ + TYPE type; + VALUE temp; + + type = (TYPE)ctype.id; + if (type == T_OBJECT && ctype.value >= 0) + { + class = class->load->class_ref[ctype.value]; + if (CLASS_is_struct(class)) + { + CSTRUCT *structure = (CSTRUCT *)OBJECT_create(class, NULL, NULL, 0); + OBJECT_REF(structure); + read_structure(stream, class, (char *)structure + sizeof(CSTRUCT)); + *((void **)addr) = structure; + return; + } + } + else if (type == TC_STRUCT) + { + class = class->load->class_ref[ctype.value]; + read_structure(stream, class, addr); + return; + } + + STREAM_read_type(stream, type, &temp); + VALUE_class_write(class, &temp, addr, ctype); +} + +static CLASS *read_class(STREAM *stream, TYPE type) +{ + char *name = COMMON_buffer; + unsigned char len; + CLASS *class; + + STREAM_read(stream, &len, 1); + STREAM_read(stream, name, len); + name[len] = 0; + + class = CLASS_get(name); + if (!class) + THROW_SERIAL(); + + if (TYPE_is_pure_object(type)) + { + if ((CLASS *)type != class) + THROW_SERIAL(); + } + + return class; +} + +static void read_structure(STREAM *stream, CLASS *class, char *base) +{ + int i, n; + CLASS_DESC *desc; + char *addr; + CTYPE ctype; + + for (n = 0; n < class->n_desc; n++) + { + desc = class->table[n].desc; + ctype = desc->variable.ctype; + addr = base + desc->variable.offset; + + if (ctype.id == TC_STRUCT) + { + read_structure(stream, class->load->class_ref[ctype.value], addr); + } + else if (ctype.id == TC_ARRAY) + { + CLASS_ARRAY *adesc = class->load->array[ctype.value]; + int size = CLASS_sizeof_ctype(class, adesc->ctype); + + for (i = 0; i < CARRAY_get_static_count(adesc); i++) + { + read_value_ctype(stream, desc->variable.class, adesc->ctype, addr); + addr += size; + } + } + else + { + read_value_ctype(stream, desc->variable.class, ctype, addr); + } + } +} + +static void register_read_object(STREAM *stream, void *object) +{ + STREAM_EXTRA *extra = ENSURE_EXTRA(stream); + + if (!extra->read_objects) + ARRAY_create(&extra->read_objects); + + //fprintf(stderr, "register_read_object: %p -> %d\n", object, ARRAY_count(extra->read_objects)); + *(void **)ARRAY_add(&extra->read_objects) = object; +} + +static void error_STREAM_read_type(void *object) +{ + OBJECT_UNREF(object); +} + +void STREAM_read_type(STREAM *stream, TYPE type, VALUE *value) +{ + bool variant; + int len; + + union + { + unsigned char _byte; + short _short; + int _int; + float _single; + unsigned char _data[4]; + } + buffer; + + variant = (type == T_VARIANT); + + if (variant || TYPE_is_object(type)) + { + if (TYPE_is_pure_object(type) && CLASS_is_struct((CLASS *)type)) + { + CLASS *class = (CLASS *)type; + CSTRUCT *structure = (CSTRUCT *)OBJECT_create(class, NULL, NULL, 0); + + read_structure(stream, class, (char *)structure + sizeof(CSTRUCT)); + + value->_object.class = class; + value->_object.object = structure; + + return; + } + + STREAM_read(stream, &buffer._byte, 1); + + if (buffer._byte == 0) + { + value->type = T_VARIANT; + value->_variant.vtype = T_NULL; + return; + } + + if (buffer._byte == '@') + { + void *object; + STREAM_EXTRA *extra = EXTRA(stream); + int id; + + if (!extra) + THROW(E_SERIAL); + + STREAM_read(stream, &id, sizeof(int)); + id--; + if (id < 0 || id > ARRAY_count(extra->read_objects)) + THROW(E_SERIAL); + + object = extra->read_objects[id]; + //fprintf(stderr, "find: %d -> %p\n", id, object); + value->_object.object = object; + value->type = (TYPE)OBJECT_class(object); + + if (value->type != type) + { + OBJECT_REF(object); + VALUE_convert(value, type); + UNBORROW(value); + } + + return; + } + + if (buffer._byte == 'A' || buffer._byte == 'a') + { + CLASS *class; + CARRAY *array; + int size, i; + VALUE temp; + void *data; + TYPE atype = T_VOID; + + if (buffer._byte == 'a' || variant) + { + class = read_class(stream, type); + + atype = class->array_type; + } + else if (TYPE_is_pure_object(type)) + { + atype = ((CLASS *)type)->array_type; + } + else if (TYPE_is_object(type)) + { + atype = T_VARIANT; + } + + if (TYPE_is_void(atype)) + THROW_SERIAL(); + + if (TYPE_is_pure_object(atype)) + CLASS_load((CLASS *)atype); + + STREAM_read(stream, &buffer._byte, 1); // Compatibility with old format + if (atype == T_VARIANT) + atype = (TYPE)buffer._byte; + + size = read_length(stream); + + GB_ArrayNew((GB_ARRAY *)&array, atype, size); + register_read_object(stream, array); + + for (i = 0; i < size; i++) + { + data = CARRAY_get_data(array, i); + STREAM_read_type(stream, atype, &temp); + VALUE_write(&temp, data, atype); + //RELEASE(&temp); + } + + value->type = (TYPE)OBJECT_class(array); + value->_object.object = array; + + if (value->type != type) + { + OBJECT_REF(array); + VALUE_convert(value, type); + UNBORROW(value); + } + return; + } + + if (buffer._byte == 'c' || buffer._byte == 'C') + { + GB_COLLECTION col; + int size, i; + VALUE temp; + char *key; + char tkey[32]; + int len; + + size = read_length(stream); + + GB_CollectionNew(&col, buffer._byte == 'c'); + register_read_object(stream, col); + + for (i = 0; i < size; i++) + { + len = read_length(stream); + if (len < sizeof(tkey)) + key = tkey; + else + key = STRING_new(NULL, len); + STREAM_read(stream, key, len); + STREAM_read_type(stream, T_VARIANT, &temp); + GB_CollectionSet(col, key, len, (GB_VARIANT *)&temp); + //RELEASE(&temp); + if (len >= 32) + STRING_free_real(key); + } + + value->type = (TYPE)OBJECT_class(col); + value->_object.object = col; + + if (value->type != type) + { + OBJECT_REF(col); + VALUE_convert(value, type); + UNBORROW(value); + } + return; + } + + if (buffer._byte == 'O' || buffer._byte == 'o') + { + CLASS *class = NULL; + void *object; + void *cstream; + + if (buffer._byte == 'o' || variant) + class = read_class(stream, type); + else + class = (CLASS *)type; + + object = OBJECT_REF(OBJECT_create(class, NULL, NULL, 0)); + register_read_object(stream, object); + + cstream = CSTREAM_FROM_STREAM(stream); + + STACK_check(1); + PUSH_OBJECT(OBJECT_class(cstream), cstream); + + ON_ERROR_1(error_STREAM_read_type, object) + { + if (EXEC_special(SPEC_READ, class, object, 1, TRUE)) + THROW_SERIAL(); + } + END_ERROR + + OBJECT_UNREF_KEEP(object); + + value->type = (TYPE)class; + value->_object.object = object; + if (value->type != type) + { + OBJECT_REF(object); + VALUE_convert(value, type); + UNBORROW(value); + } + return; + } + + if (variant) + type = buffer._byte; + } + + value->type = type; + + switch (type) + { + case T_BOOLEAN: + + STREAM_read(stream, &buffer._byte, 1); + value->_integer.value = (buffer._byte != 0) ? (-1) : 0; + break; + + case T_BYTE: + + STREAM_read(stream, &buffer._byte, 1); + value->_integer.value = buffer._byte; + break; + + case T_SHORT: + + STREAM_read(stream, &buffer._short, sizeof(short)); + if (stream->common.swap) + SWAP_short(&buffer._short); + value->_integer.value = buffer._short; + break; + + case T_INTEGER: + + STREAM_read(stream, &value->_integer.value, sizeof(int)); + if (stream->common.swap) + SWAP_int(&value->_integer.value); + break; + + case T_LONG: + + STREAM_read(stream, &value->_long.value, sizeof(int64_t)); + if (stream->common.swap) + SWAP_int64(&value->_long.value); + break; + + case T_POINTER: + + STREAM_read(stream, &value->_pointer.value, sizeof(void *)); + if (stream->common.swap) + SWAP_pointer(&value->_pointer.value); + break; + + case T_SINGLE: + + STREAM_read(stream, &buffer._single, sizeof(float)); + if (stream->common.swap) + SWAP_float(&buffer._single); + value->_single.value = buffer._single; + break; + + case T_FLOAT: + + STREAM_read(stream, &value->_float.value, sizeof(double)); + if (stream->common.swap) + SWAP_double(&value->_float.value); + break; + + case T_DATE: + + STREAM_read(stream, &value->_date.date, sizeof(int)); + STREAM_read(stream, &value->_date.time, sizeof(int)); + if (stream->common.swap) + { + SWAP_int(&value->_date.date); + SWAP_int(&value->_date.time); + } + break; + + case T_CSTRING: + value->type = T_STRING; + // continue + + case T_STRING: + + if (stream->common.null_terminated) + { + if (stream->type == &STREAM_memory) + { + ssize_t slen; + if (CHECK_strlen(stream->memory.addr + stream->memory.pos, &slen)) + THROW(E_READ); + len = (int)slen; + } + else + { + len = -1; + } + } + else + len = read_length(stream); + + if (len > 0) + { + STRING_new_temp_value(value, NULL, len); + STREAM_read(stream, value->_string.addr, len); + } + else if (len < 0) + { + char *str = NULL; + + for(len = 0;; len++) + { + STREAM_read(stream, &buffer._byte, 1); + if (buffer._byte == 0) + break; + str = STRING_add_char(str, buffer._byte); + } + STRING_free_later(str); + + value->_string.addr = str; + value->_string.len = len; + value->_string.start = 0; + value->type = T_STRING; + } + else + { + STRING_void_value(value); + } + + if (stream->common.null_terminated) + STREAM_seek(stream, SEEK_CUR, 1); + + break; + + case T_NULL: + + break; + + default: + + THROW_SERIAL(); + } + + if (variant) + VALUE_convert_variant(value); +} + +static void write_length(STREAM *stream, int len) +{ + union + { + unsigned char _byte; + short _short; + int _int; + } + buffer; + + if (len < 0x80) + { + buffer._byte = (unsigned char)len; + STREAM_write(stream, &buffer._byte, 1); + } + else if (len < 0x4000) + { + buffer._short = (short)len | 0x8000; + if (!EXEC_big_endian) + SWAP_short(&buffer._short); + STREAM_write(stream, &buffer._short, sizeof(short)); + } + else + { + buffer._int = len | 0xC0000000; + if (!EXEC_big_endian) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + } +} + +void STREAM_write_type(STREAM *stream, TYPE type, VALUE *value) +{ + union + { + unsigned char _byte; + short _short; + int _int; + int64_t _long; + float _single; + double _float; + unsigned char _data[8]; + void *_pointer; + } + buffer; + + CLASS *class; + void *object; + void *structure; + bool variant; + STREAM_EXTRA *extra; + int *pid; + + if (type == T_VARIANT) + { + variant = TRUE; + VARIANT_undo(value); + type = value->type; + } + else + variant = FALSE; + + if (!TYPE_is_object(type)) + { + if (variant) + { + buffer._byte = (unsigned char)type; + STREAM_write(stream, &buffer._byte, 1); + } + + switch (type) + { + case T_BOOLEAN: + + buffer._byte = value->_integer.value ? 0xFF : 0; + STREAM_write(stream, &buffer._byte, 1); + break; + + case T_BYTE: + + buffer._byte = (unsigned char)value->_integer.value; + STREAM_write(stream, &buffer._byte, 1); + break; + + case T_SHORT: + + buffer._short = (short)value->_integer.value; + if (stream->common.swap) + SWAP_short(&buffer._short); + STREAM_write(stream, &buffer._short, sizeof(short)); + break; + + case T_INTEGER: + + buffer._int = value->_integer.value; + if (stream->common.swap) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + break; + + case T_LONG: + + buffer._long = value->_long.value; + if (stream->common.swap) + SWAP_int64(&buffer._long); + STREAM_write(stream, &buffer._long, sizeof(int64_t)); + break; + + case T_POINTER: + + buffer._pointer = value->_pointer.value; + if (stream->common.swap) + SWAP_pointer(&buffer._pointer); + STREAM_write(stream, &buffer._pointer, sizeof(void *)); + break; + + case T_SINGLE: + + buffer._single = value->_single.value; + if (stream->common.swap) + SWAP_float(&buffer._single); + STREAM_write(stream, &buffer._single, sizeof(float)); + break; + + case T_FLOAT: + + buffer._float = value->_float.value; + if (stream->common.swap) + SWAP_double(&buffer._float); + STREAM_write(stream, &buffer._float, sizeof(double)); + break; + + case T_DATE: + + buffer._int = value->_date.date; + if (stream->common.swap) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + + buffer._int = value->_date.time; + if (stream->common.swap) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + + break; + + case T_STRING: + case T_CSTRING: + + if (stream->common.null_terminated) + { + STREAM_write(stream, value->_string.addr + value->_string.start, value->_string.len); + buffer._byte = 0; + STREAM_write(stream, &buffer._byte, 1); + } + else + { + write_length(stream, value->_string.len); + STREAM_write(stream, value->_string.addr + value->_string.start, value->_string.len); + } + break; + + case T_NULL: + + break; + + default: + + THROW_SERIAL(); + } + + return; + } + + object = value->_object.object; + + if (!object) + { + buffer._byte = 0; + STREAM_write(stream, &buffer._byte, 1); + return; + } + + extra = ENSURE_EXTRA(stream); + if (!extra->write_objects) + HASH_TABLE_create(&extra->write_objects, sizeof(int), HF_NORMAL); + + pid = (int *)HASH_TABLE_lookup(extra->write_objects, (const char *)&object, sizeof(uintptr_t), FALSE); + if (pid) + { + buffer._byte = '@'; + STREAM_write(stream, &buffer._byte, 1); + buffer._int = *pid; + STREAM_write(stream, &buffer._int, sizeof(int)); + return; + } + else + { + pid = HASH_TABLE_insert(extra->write_objects, (const char *)&object, sizeof(uintptr_t)); + *pid = HASH_TABLE_size(extra->write_objects); + } + + class = OBJECT_class(object); + + if (class->special[SPEC_WRITE] != NO_SYMBOL) + { + CSTREAM *ob; + + if (variant || type == T_OBJECT) + { + char *name = class->name; + int len = strlen(name); + + buffer._byte = 'o'; + STREAM_write(stream, &buffer._byte, 1); + buffer._byte = (unsigned char)len; + STREAM_write(stream, &buffer._byte, 1); + + STREAM_write(stream, name, len); + } + else + { + buffer._byte = 'O'; + STREAM_write(stream, &buffer._byte, 1); + } + + ob = CSTREAM_FROM_STREAM(stream); + STACK_check(1); + PUSH_OBJECT(OBJECT_class(ob), ob); + EXEC_special(SPEC_WRITE, class, object, 1, TRUE); + + return; + } + + if (class->quick_array == CQA_ARRAY || class->is_array_of_struct) + { + CARRAY *array = (CARRAY *)object; + VALUE temp; + void *data; + int i; + + if (OBJECT_is_locked((OBJECT *)array)) + THROW_SERIAL(); + OBJECT_lock((OBJECT *)array, TRUE); + + if (!array->ref) + { + if (variant || type == T_OBJECT) + { + char *name = class->name; + int len = strlen(name); + + buffer._byte = 'a'; + STREAM_write(stream, &buffer._byte, 1); + + buffer._byte = (unsigned char)len; + STREAM_write(stream, &buffer._byte, 1); + STREAM_write(stream, name, len); + } + else + { + buffer._byte = 'A'; + STREAM_write(stream, &buffer._byte, 1); + } + + if (TYPE_is_pure_object(array->type)) + buffer._byte = T_OBJECT; + else + buffer._byte = (uchar)array->type; + STREAM_write(stream, &buffer._byte, 1); // Compatibility with old format + + write_length(stream, array->count); + } + + if (class->is_array_of_struct) + { + for (i = 0; i < array->count; i++) + { + data = CARRAY_get_data(array, i); + structure = CSTRUCT_create_static(array, (CLASS *)array->type, data); + temp._object.class = OBJECT_class(structure); + temp._object.object = structure; + OBJECT_REF(structure); + STREAM_write_type(stream, T_OBJECT, &temp); + OBJECT_UNREF(structure); + } + } + else + { + for (i = 0; i < array->count; i++) + { + data = CARRAY_get_data(array, i); + VALUE_read(&temp, data, array->type); + STREAM_write_type(stream, array->type, &temp); + } + } + + OBJECT_lock((OBJECT *)array, FALSE); + return; + } + + if (class->quick_array == CQA_COLLECTION) + { + CCOLLECTION *col = (CCOLLECTION *)object; + GB_COLLECTION_ITER iter; + char *key = NULL; + int len; + VALUE temp; + + if (OBJECT_is_locked((OBJECT *)col)) + THROW_SERIAL(); + OBJECT_lock((OBJECT *)col, TRUE); + + buffer._byte = col->mode ? 'c' : 'C'; + STREAM_write(stream, &buffer._byte, 1); + + write_length(stream, CCOLLECTION_get_count(col)); + + GB_CollectionEnum(col, &iter, (GB_VARIANT *)&temp, NULL, NULL); + while (!GB_CollectionEnum(col, &iter, (GB_VARIANT *)&temp, &key, &len)) + { + write_length(stream, len); + STREAM_write(stream, key, len); + STREAM_write_type(stream, T_VARIANT, &temp); + } + + OBJECT_lock((OBJECT *)col, FALSE); + return; + } + + if (class->is_struct) + { + CSTRUCT *structure = (CSTRUCT *)object; + int i; + CLASS_DESC *desc; + VALUE temp; + char *addr; + + stream = enter_temp_stream(stream); + + if (OBJECT_is_locked((OBJECT *)structure)) + THROW_SERIAL(); + OBJECT_lock((OBJECT *)structure, TRUE); + + for (i = 0; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + if (structure->ref) + addr = (char *)((CSTATICSTRUCT *)structure)->addr + desc->variable.offset; + else + addr = (char *)structure + sizeof(CSTRUCT) + desc->variable.offset; + + VALUE_class_read(desc->variable.class, &temp, (void *)addr, desc->variable.ctype, (void *)structure); + //BORROW(&temp); + STREAM_write_type(stream, temp.type, &temp); + RELEASE(&temp); + } + + stream = leave_temp_stream(); + + OBJECT_lock((OBJECT *)structure, FALSE); + return; + } + + THROW_SERIAL(); +} + + +void STREAM_load(const char *path, char **buffer, int *rlen) +{ + STREAM stream; + int64_t len; + + STREAM_open(&stream, path, GB_ST_READ); + STREAM_lof(&stream, &len); + + if (len >> 31) + THROW(E_MEMORY); + + *rlen = len; + + ALLOC(buffer, *rlen); + + STREAM_read(&stream, *buffer, *rlen); + STREAM_close(&stream); +} + + +bool STREAM_map(const char *path, char **paddr, int *plen) +{ + STREAM stream; + int fd; + struct stat info; + void *addr; + size_t len; + bool ret = TRUE; + + STREAM_open(&stream, path, GB_ST_READ); + + if (stream.type == &STREAM_arch) + { + *paddr = (char *)stream.arch.arch->arch->addr + stream.arch.start; + *plen = stream.arch.size; + ret = FALSE; + goto __RETURN; + } + + fd = STREAM_handle(&stream); + if (fd < 0) + goto __RETURN; + + if (fstat(fd, &info) < 0) + goto __RETURN; + + len = info.st_size; + addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) + goto __RETURN; + + *paddr = addr; + *plen = len; + ret = FALSE; + +__RETURN: + + STREAM_close(&stream); + return ret; +} + +void STREAM_unmap(char *addr, int len) +{ + if (addr && len > 0 && ARCHIVE_check_addr(addr)) + { + munmap(addr, len); + } +} + + +int STREAM_handle(STREAM *stream) +{ + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (stream->type->handle) + return (*stream->type->handle)(stream); + else + return (-1); +} + +int STREAM_lock_all_fd(int fd) +{ + int64_t pos; + + pos = lseek(fd, 0, SEEK_CUR); + if (pos < 0) + return errno; + + if (lseek(fd, 0, SEEK_SET) < 0) + return errno; + + #ifdef F_TLOCK + + if (lockf(fd, F_TLOCK, 0)) + return errno; + + #else + + return ENOTSUP; + + #endif + + if (lseek(fd, pos, SEEK_SET) < 0) + return errno; + + return 0; +} + +bool STREAM_lock_all(STREAM *stream) +{ + int fd; + int err; + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + fd = STREAM_handle(stream); + if (fd < 0) + return TRUE; + + err = STREAM_lock_all_fd(fd); + + if (err == 0) + return FALSE; + if (err == EAGAIN) + return TRUE; + + THROW_SYSTEM(err, NULL); + return TRUE; +} + + +bool STREAM_lof_safe(STREAM *stream, int64_t *len) +{ + int ilen; + STREAM_EXTRA *extra; + + *len = 0; + + extra = EXTRA(stream); + if (extra && extra->unread) + { + *len = extra->unread_len - extra->unread_pos; + if (*len > 0) + return FALSE; + } + + if (stream->type->lof) + { + if (!(*(stream->type->lof))(stream, len)) + goto ADD_BUFFER; + } + + if (STREAM_get_readable(stream, &ilen)) + return TRUE; + + *len = ilen; + +ADD_BUFFER: + + if (extra) + { + if (extra->buffer) + *len += extra->buffer_len - extra->buffer_pos; + } + + return FALSE; +} + + +void STREAM_lof(STREAM *stream, int64_t *len) +{ + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (STREAM_lof_safe(stream, len)) + THROW(E_USIZE); +} + + +bool STREAM_eof(STREAM *stream) +{ + STREAM_EXTRA *extra = EXTRA(stream); + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (extra) + { + if (extra->unread && extra->unread_pos < extra->unread_len) + return FALSE; + if (extra->buffer && extra->buffer_pos < extra->buffer_len) + return FALSE; + } + + if (stream->common.eof) + return TRUE; + + if (stream->type->eof) + return ((*(stream->type->eof))(stream)); + else + return STREAM_default_eof(stream); +} + + +#if 0 +int STREAM_read_direct(int fd, char *buffer, int len) +{ + ssize_t eff_read; + ssize_t len_read; + + while (len > 0) + { + len_read = Min(len, MAX_IO); + eff_read = read(fd, buffer, len_read); + + if (eff_read > 0) + { + STREAM_eff_read += eff_read; + len -= eff_read; + buffer += eff_read; + } + + if (eff_read < len_read) + { + if (eff_read == 0) + errno = 0; + if (eff_read <= 0 && errno != EINTR) + return TRUE; + } + } + + return FALSE; +} + +int STREAM_write_direct(int fd, char *buffer, int len) +{ + ssize_t eff_write; + ssize_t len_write; + + while (len > 0) + { + len_write = Min(len, MAX_IO); + eff_write = write(fd, buffer, len_write); + + if (eff_write < len_write) + { + if (eff_write <= 0 && errno != EINTR) + return TRUE; + } + + len -= eff_write; + buffer += eff_write; + } + + return FALSE; +} +#endif + +void STREAM_blocking(STREAM *stream, bool block) +{ + int fd = STREAM_handle(stream); + + if (fd < 0) + return; + + stream->common.blocking = block; + + if (block) + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); + else + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); +} + +void STREAM_check_blocking(STREAM *stream) +{ + int fd = STREAM_handle(stream); + + stream->common.blocking = (fd < 0) ? TRUE : ((fcntl(fd, F_GETFL) & O_NONBLOCK) == 0); +} + +void STREAM_cancel(STREAM *stream) +{ + if (!stream->common.redirected) + return; + + STREAM_close(EXTRA(stream)->redirect); + FREE(&EXTRA(stream)->redirect); + stream->common.redirected = FALSE; +} + +void STREAM_begin(STREAM *stream) +{ + STREAM_cancel(stream); + + if (!stream->common.redirected) + { + STREAM_EXTRA *extra = ENSURE_EXTRA(stream); + ALLOC_ZERO(&extra->redirect, sizeof(STREAM)); + STREAM_open(extra->redirect, NULL, GB_ST_STRING | GB_ST_WRITE); + } + + stream->common.redirected = TRUE; +} + +void STREAM_end(STREAM *stream) +{ + STREAM_EXTRA *extra = EXTRA(stream); + + if (!stream->common.redirected) + return; + + stream->common.redirected = FALSE; + STREAM_write(stream, extra->redirect->string.buffer, STRING_length(extra->redirect->string.buffer)); + stream->common.redirected = TRUE; + STREAM_cancel(stream); +} + diff --git a/main/gbx/gbx_stream.h b/main/gbx/gbx_stream.h new file mode 100644 index 00000000..84f3de13 --- /dev/null +++ b/main/gbx/gbx_stream.h @@ -0,0 +1,288 @@ +/*************************************************************************** + + gbx_stream.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_STREAM_H +#define __GBX_STREAM_H + +#include "gb_hash.h" +#include "gbx_value.h" +#include "gbx_archive.h" + +union STREAM; + +typedef + struct STREAM_CLASS { + int (*open)(union STREAM *stream, const char *path, int mode, void *data); + int (*close)(union STREAM *stream); + int (*read)(union STREAM *stream, char *buffer, int len); + int (*write)(union STREAM *stream, char *buffer, int len); + int (*seek)(union STREAM *stream, int64_t pos, int whence); + int (*tell)(union STREAM *stream, int64_t *pos); + int (*flush)(union STREAM *stream); + int (*eof)(union STREAM *stream); + int (*lof)(union STREAM *stream, int64_t *len); + int (*handle)(union STREAM *stream); + } + STREAM_CLASS; + +typedef + struct { + union STREAM *redirect; + char *buffer; + short buffer_pos; + short buffer_len; + char *unread; + int unread_pos; + int unread_len; + void **read_objects; + HASH_TABLE *write_objects; + } + STREAM_EXTRA; + +typedef + struct { + STREAM_CLASS *type; + short mode; + unsigned swap : 1; + unsigned eol : 2; + unsigned eof : 1; + unsigned no_fionread : 1; + unsigned no_lseek : 1; + unsigned available_now : 1; + unsigned standard : 1; + unsigned blocking : 1; + unsigned redirected : 1; + unsigned no_read_ahead : 1; + unsigned null_terminated : 1; + unsigned check_read : 1; + unsigned _reserved : 2; + #if __WORDSIZE == 64 + unsigned _reserved2 : 32; + #endif + STREAM_EXTRA *extra; + #if DEBUG_STREAM + int tag; + #endif + } + STREAM_COMMON; + +typedef + struct { + STREAM_COMMON common; + int _reserved[6]; + } + STREAM_RESERVED; + +typedef + struct { + STREAM_COMMON common; + int64_t size; + int fd; + unsigned watch : 1; + unsigned has_size : 1; + unsigned use_size : 1; + } + STREAM_DIRECT; + +typedef + struct { + STREAM_COMMON common; + int fd; + unsigned can_write : 1; + } + STREAM_PIPE; + +typedef + struct { + STREAM_COMMON common; + FILE *file; + } + STREAM_BUFFER; + +typedef + struct { + STREAM_COMMON common; + void *addr; + intptr_t pos; + } + STREAM_MEMORY; + +typedef + struct { + STREAM_COMMON common; + ARCHIVE *arch; + int size; + int start; + int pos; + } + STREAM_ARCH; + +typedef + struct { + STREAM_COMMON common; + void *process; + } + STREAM_PROCESS; + +typedef + struct { + STREAM_COMMON common; + char *buffer; + int pos; + int size; + } + STREAM_STRING; + +typedef + struct { + STREAM_COMMON common; + intptr_t pos; + } + STREAM_NULL; + +typedef + union STREAM { + STREAM_CLASS *type; + STREAM_COMMON common; + STREAM_RESERVED _reserved; + STREAM_DIRECT direct; + STREAM_BUFFER buffer; + STREAM_PIPE pipe; + STREAM_MEMORY memory; + STREAM_ARCH arch; + STREAM_PROCESS process; + STREAM_STRING string; + STREAM_NULL null; + } + STREAM; + +/*enum { + STO_READ = (1 << 0), + STO_WRITE = (1 << 1), + STO_READ_WRITE = STO_READ + STO_WRITE, + STO_MODE = 0x3, + STO_APPEND = (1 << 2), + STO_CREATE = (1 << 3), + STO_ACCESS = 0xF, + STO_DIRECT = (1 << 4), + STO_LOCK = (1 << 5), + STO_WATCH = (1 << 6), + STO_PIPE = (1 << 7), + STO_MEMORY = (1 << 8), + STO_STRING = (1 << 9), + STO_NULL = (1 << 10) + };*/ + +enum { + ST_EOL_UNIX = 0, + ST_EOL_WINDOWS = 1, + ST_EOL_MAC = 2 + }; + +//EXTERN int STREAM_eff_read; + +#ifndef __STREAM_IMPL_C + +EXTERN STREAM_CLASS STREAM_direct; +EXTERN STREAM_CLASS STREAM_lock; +EXTERN STREAM_CLASS STREAM_buffer; +EXTERN STREAM_CLASS STREAM_pipe; +EXTERN STREAM_CLASS STREAM_memory; +EXTERN STREAM_CLASS STREAM_arch; +EXTERN STREAM_CLASS STREAM_process; +EXTERN STREAM_CLASS STREAM_string; +EXTERN STREAM_CLASS STREAM_null; + +#else + +#define DECLARE_STREAM(stream) \ +STREAM_CLASS stream = \ +{ \ + (void *)stream_open, \ + (void *)stream_close, \ + (void *)stream_read, \ + (void *)stream_write, \ + (void *)stream_seek, \ + (void *)stream_tell, \ + (void *)stream_flush, \ + (void *)stream_eof, \ + (void *)stream_lof, \ + (void *)stream_handle \ +} + +#endif + +#define STREAM_BUFFER_SIZE 1024 + +bool STREAM_in_archive(const char *path); + +void STREAM_open(STREAM *stream, const char *path, int mode); + +void STREAM_release(STREAM *stream); +void STREAM_close(STREAM *stream); +void STREAM_write(STREAM *stream, void *addr, int len); +char *STREAM_line_input(STREAM *stream, char *escape); +char *STREAM_input(STREAM *stream); +int64_t STREAM_tell(STREAM *stream); +void STREAM_seek(STREAM *stream, int64_t pos, int whence); +int STREAM_read(STREAM *stream, void *addr, int len); +void STREAM_peek(STREAM *stream, void *addr, int len); +int STREAM_read_max(STREAM *stream, void *addr, int len); +bool STREAM_read_ahead(STREAM *stream); +//char STREAM_getchar(STREAM *stream); +void STREAM_read_type(STREAM *stream, TYPE type, VALUE *value); +void STREAM_write(STREAM *stream, void *addr, int len); +void STREAM_write_zeros(STREAM *stream, int len); +void STREAM_write_type(STREAM *stream, TYPE type, VALUE *value); +void STREAM_write_eol(STREAM *stream); +void STREAM_flush(STREAM *stream); +int STREAM_handle(STREAM *stream); +bool STREAM_lof_safe(STREAM *stream, int64_t *len); +void STREAM_lof(STREAM *stream, int64_t *len); +bool STREAM_eof(STREAM *stream); +bool STREAM_default_eof(STREAM *stream); + +void STREAM_load(const char *path, char **buffer, int *len); + +bool STREAM_map(const char *path, char **paddr, int *plen); +void STREAM_unmap(char *addr, int len); + +int STREAM_lock_all_fd(int fd); +bool STREAM_lock_all(STREAM *stream); + +#define STREAM_is_closed(_stream) ((_stream)->type == NULL) +#define STREAM_is_closed_for_writing(_stream) (STREAM_is_closed(_stream) && !(_stream)->common.redirected) + +void STREAM_blocking(STREAM *stream, bool block); +#define STREAM_is_blocking(_stream) ((_stream)->common.blocking) +void STREAM_check_blocking(STREAM *stream); + +bool STREAM_get_readable(STREAM *stream, int *len); + +void STREAM_exit(void); + +void STREAM_begin(STREAM *stream); +void STREAM_cancel(STREAM *stream); +void STREAM_end(STREAM *stream); + +#endif diff --git a/main/gbx/gbx_stream_arch.c b/main/gbx/gbx_stream_arch.c new file mode 100644 index 00000000..6a9ac138 --- /dev/null +++ b/main/gbx/gbx_stream_arch.c @@ -0,0 +1,165 @@ +/*************************************************************************** + + gbx_stream_arch.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_archive.h" +#include "gbx_stream.h" + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + ARCHIVE_FIND find; + + if (ARCHIVE_get(NULL, &path, &find)) + { + errno = ENOENT; + return TRUE; + } + + if (find.pos < 0) + { + errno = EISDIR; + return TRUE; + } + + if ((mode & GB_ST_ACCESS) != GB_ST_READ) + { + errno = EACCES; + return TRUE; + } + + stream->common.available_now = TRUE; + stream->arch.arch = find.arch; + stream->arch.size = find.len; + stream->arch.start = find.pos; + stream->arch.pos = 0; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + int max; + + max = stream->arch.size - stream->arch.pos; + + if (len > max) + { + len = max; + errno = 0; + } + + ARCHIVE_read(stream->arch.arch, stream->arch.start + stream->arch.pos, buffer, len); + stream->arch.pos += len; + return len; +} + + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return -1; +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + int64_t new_pos; + + switch(whence) + { + case SEEK_SET: + new_pos = pos; + break; + + case SEEK_CUR: + new_pos = stream->arch.pos + pos; + break; + + case SEEK_END: + new_pos = stream->arch.size - pos; + break; + + default: + return TRUE; + } + + if (new_pos < 0 || new_pos > stream->arch.size) + return TRUE; + + stream->arch.pos = (int)new_pos; + return FALSE; +} + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = stream->arch.pos; + return FALSE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + return (stream->arch.pos >= stream->arch.size); +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + *len = stream->arch.size; + return FALSE; +} + +static int stream_handle(STREAM *stream) +{ + return -1; +} + + +DECLARE_STREAM(STREAM_arch); + diff --git a/main/gbx/gbx_stream_buffer.c b/main/gbx/gbx_stream_buffer.c new file mode 100644 index 00000000..d45300cc --- /dev/null +++ b/main/gbx/gbx_stream_buffer.c @@ -0,0 +1,217 @@ +/*************************************************************************** + + gbx_stream_buffer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_exec.h" +#include "gbx_stream.h" + + +#define FD (stream->buffer.file) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + FILE *file; + char *fmode; + struct stat info; + int fd; + + if (mode & GB_ST_CREATE) + fmode = "w+"; + else if (mode & GB_ST_APPEND) + fmode = "a+"; + else if (mode & GB_ST_WRITE) + fmode = "r+"; + else + fmode = "r"; + + file = fopen(path, fmode); + if (file == NULL) + return TRUE; + + fd = fileno(file); + + if (fstat(fd, &info) < 0) + { + fclose(file); + return TRUE; + } + + if (S_ISDIR(info.st_mode)) + { + fclose(file); + errno = EISDIR; + return TRUE; + } + + if (!S_ISREG(info.st_mode)) + { + stream->common.available_now = FALSE; + stream->common.no_read_ahead = TRUE; + } + else + stream->common.available_now = TRUE; + + + //fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + + FD = file; + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + FILE *f = FD; + + if (!f) + return TRUE; + + FD = NULL; + + return fclose(f) < 0; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + int eff; + + if (!FD) + return 0; + + eff = (int)fread(buffer, 1, len, FD); + if (eff < len) + { + if (ferror(FD) == 0) + errno = 0; + } + + return eff; +} + + +static int stream_flush(STREAM *stream) +{ + if (!FD) + return TRUE; + + return (fflush(FD) != 0); +} + + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + int eff; + + if (!FD) + return 0; + + eff = (int)fwrite(buffer, 1, len, FD); + if (eff < len) + { + if (ferror(FD) == 0) + errno = 0; + } + + return eff; +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + if (!FD) + return TRUE; + + return (fseek(FD, (off_t)pos, whence) != 0); +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + if (!FD) + return TRUE; + + *pos = (int64_t)ftell(FD); + return (*pos < 0); +} + + +static int stream_eof(STREAM *stream) +{ + int c; + + if (!FD || feof(FD) || stream->common.eof) + return TRUE; + + if (stream->common.no_read_ahead) + return FALSE; + + c = fgetc(FD); + if (c == EOF) + return TRUE; + + ungetc(c, FD); + return FALSE; +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + struct stat info; + + if (!stream->common.available_now) + return TRUE; + + if (!FD || fstat(fileno(FD), &info) < 0) + return TRUE; + + *len = info.st_size; + return FALSE; +} + + +static int stream_handle(STREAM *stream) +{ + if (FD) + return fileno(FD); + else + return -1; +} + + + +DECLARE_STREAM(STREAM_buffer); diff --git a/main/gbx/gbx_stream_direct.c b/main/gbx/gbx_stream_direct.c new file mode 100644 index 00000000..328eefe1 --- /dev/null +++ b/main/gbx/gbx_stream_direct.c @@ -0,0 +1,230 @@ +/*************************************************************************** + + gbx_stream_direct.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" +#include "gbx_number.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +#define FD (stream->direct.fd) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + int fd; + struct stat info; + int fmode, omode; + VALUE val; + + if (mode & GB_ST_CREATE) + { + fmode = O_CREAT | O_TRUNC; // | O_EXCL; + mode |= GB_ST_WRITE; + } + else if (mode & GB_ST_APPEND) + fmode = O_APPEND | O_CREAT; + else + fmode = 0; + + switch (mode & GB_ST_MODE) + { + case GB_ST_READ: fmode |= O_RDONLY; break; + case GB_ST_WRITE: fmode |= O_WRONLY; break; + case GB_ST_READ_WRITE: fmode |= O_RDWR; break; + default: fmode |= O_RDONLY; + } + + stream->direct.has_size = FALSE; + stream->direct.use_size = FALSE; + + if (path[0] == '.' && isdigit(path[1])) + { + if ((mode & GB_ST_CREATE) || (mode & GB_ST_APPEND)) + THROW(E_ACCESS); + + if (NUMBER_from_string(NB_READ_INTEGER, &path[1], strlen(path) - 1, &val) || val._integer.value < 0) + { + errno = ENOENT; + return TRUE; + } + + fd = val._integer.value; + omode = fcntl(fd, F_GETFL, NULL); + if (omode < 0) + return TRUE; + + if (((mode & GB_ST_MODE) == GB_ST_READ && (omode & O_ACCMODE) == O_WRONLY) + || ((mode & GB_ST_MODE) == GB_ST_WRITE && (omode & O_ACCMODE) == O_RDONLY) + || ((mode & GB_ST_MODE) == GB_ST_READ_WRITE && (omode & O_ACCMODE) != O_RDWR)) + THROW(E_ACCESS); + + stream->direct.watch = TRUE; + stream->common.no_read_ahead = TRUE; + } + else + { + stream->direct.watch = FALSE; + + fd = open(path, fmode, 0666); + if (fd < 0) + return TRUE; + + if (fstat(fd, &info) < 0) + { + close(fd); + return TRUE; + } + + if (S_ISDIR(info.st_mode)) + { + close(fd); + errno = EISDIR; + return TRUE; + } + + if (!S_ISREG(info.st_mode)) + { + stream->common.available_now = FALSE; + stream->common.no_read_ahead = TRUE; + stream->direct.has_size = TRUE; + fcntl(fd, F_SETFL, O_NONBLOCK); + } + else + { + stream->common.available_now = TRUE; + } + } + + FD = fd; + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (!stream->direct.watch) + { + if (close(FD) < 0) + return TRUE; + } + + FD = -1; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return read(FD, buffer, len); +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return write(FD, buffer, len); +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + return (lseek(FD, pos, whence) < 0); +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = lseek(FD, 0, SEEK_CUR); + return (*pos < 0); +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + struct stat info; + off_t pos; + + if (!stream->direct.has_size) + { + if (fstat(FD, &info) == 0) + { + stream->direct.use_size = TRUE; + stream->direct.size = info.st_size; + } + + stream->direct.has_size = TRUE; + } + + if (stream->direct.use_size && !stream->common.no_lseek) + { + pos = lseek(FD, 0, SEEK_CUR); + if (pos >= 0) + return pos >= stream->direct.size; + + stream->common.no_lseek = TRUE; + } + + return STREAM_default_eof(stream); +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + struct stat info; + + if (!stream->common.available_now) + return TRUE; + + if (fstat(FD, &info) < 0) + return TRUE; + + *len = info.st_size; + return FALSE; +} + + +static int stream_handle(STREAM *stream) +{ + return FD; +} + + +DECLARE_STREAM(STREAM_direct); + diff --git a/main/gbx/gbx_stream_lock.c b/main/gbx/gbx_stream_lock.c new file mode 100644 index 00000000..3ac9b33d --- /dev/null +++ b/main/gbx/gbx_stream_lock.c @@ -0,0 +1,109 @@ +/*************************************************************************** + + gbx_stream_lock.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" +#include "gbx_number.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +#define FD (stream->direct.fd) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + int fd; + + stream->direct.watch = FALSE; + + fd = open(path, O_CREAT | O_WRONLY | O_CLOEXEC, 0666); + if (fd < 0) + return TRUE; + + FD = fd; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (close(FD) < 0) + return TRUE; + + FD = -1; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return 0; +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return 0; +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + return TRUE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + return TRUE; +} + + +static int stream_flush(STREAM *stream) +{ + return TRUE; +} + +#define stream_eof NULL +#define stream_lof NULL + +static int stream_handle(STREAM *stream) +{ + return FD; +} + + +DECLARE_STREAM(STREAM_lock); + diff --git a/main/gbx/gbx_stream_memory.c b/main/gbx/gbx_stream_memory.c new file mode 100644 index 00000000..80e4bfcc --- /dev/null +++ b/main/gbx/gbx_stream_memory.c @@ -0,0 +1,187 @@ +/*************************************************************************** + + gbx_stream_memory.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + stream->memory.addr = (char *)path; + + if (stream->memory.addr == NULL) + { + stream->type = NULL; + THROW(E_ARG); + } + + stream->memory.pos = 0; + + stream->common.available_now = TRUE; + stream->common.null_terminated = TRUE; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + /*if (munmap(stream->memory.addr, stream->memory.size) != 0) + return TRUE;*/ + + stream->memory.addr = NULL; + //stream->memory.size = 0; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + /*if ((stream->common.mode & GB_ST_READ) == 0) + THROW(E_ACCESS);*/ + + CHECK_enter(); + + if (sigsetjmp(CHECK_jump, TRUE) == 0) + memmove(buffer, stream->memory.addr + stream->memory.pos, len); + + CHECK_leave(); + + if (CHECK_got_error()) + { + errno = EIO; + return -1; + } + else + { + stream->memory.pos += len; + return len; + } +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + if ((stream->common.mode & GB_ST_WRITE) == 0) + THROW(E_ACCESS); + + CHECK_enter(); + + if (sigsetjmp(CHECK_jump, TRUE) == 0) + memmove(stream->memory.addr + stream->memory.pos, buffer, len); + + CHECK_leave(); + + if (CHECK_got_error()) + { + errno = EIO; + return -1; + } + else + { + stream->memory.pos += len; + return len; + } +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + int64_t new_pos; + + switch(whence) + { + case SEEK_SET: + new_pos = pos; + break; + + case SEEK_CUR: + new_pos = stream->memory.pos + pos; + break; + + case SEEK_END: + return TRUE; + + default: + return TRUE; + } + + if (new_pos < 0) + return TRUE; + + stream->memory.pos = new_pos; + return FALSE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = (int64_t)stream->memory.pos; + return FALSE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + //return (stream->memory.pos >= stream->memory.size); + return FALSE; +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + //*len = stream->memory.size; + //return FALSE; + *len = 0; + return TRUE; +} + + +static int stream_handle(STREAM *stream) +{ + return -1; +} + + +DECLARE_STREAM(STREAM_memory); diff --git a/main/gbx/gbx_stream_null.c b/main/gbx/gbx_stream_null.c new file mode 100644 index 00000000..0dcd903c --- /dev/null +++ b/main/gbx/gbx_stream_null.c @@ -0,0 +1,117 @@ +/*************************************************************************** + + gbx_stream_null.c + + (c) 2000-2019 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gbx_stream.h" + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + stream->null.pos = 0; + stream->common.available_now = TRUE; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return 0; +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + stream->null.pos += len; + return len; +} + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + int64_t new_pos; + + switch(whence) + { + case SEEK_SET: + new_pos = pos; + break; + + case SEEK_CUR: + new_pos = stream->null.pos + pos; + break; + + case SEEK_END: + return TRUE; + + default: + return TRUE; + } + + if (new_pos < 0) + return TRUE; + + stream->null.pos = new_pos; + return FALSE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = (int64_t)stream->null.pos; + return FALSE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + return TRUE; +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + *len = 0; + return TRUE; +} + + +static int stream_handle(STREAM *stream) +{ + return -1; +} + + +DECLARE_STREAM(STREAM_null); diff --git a/main/gbx/gbx_stream_pipe.c b/main/gbx/gbx_stream_pipe.c new file mode 100644 index 00000000..0cd41c11 --- /dev/null +++ b/main/gbx/gbx_stream_pipe.c @@ -0,0 +1,137 @@ +/*************************************************************************** + + gbx_stream_pipe.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +#define FD (stream->direct.fd) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + int fd; + int fmode; + + RESTART_SYSCALL(mkfifo(path, 0666)) + { + if (errno != EEXIST) + return TRUE; + } + + fmode = 0; + + switch (mode & GB_ST_MODE) + { + case GB_ST_READ: fmode |= O_RDONLY; break; + case GB_ST_WRITE: fmode |= O_WRONLY; break; + case GB_ST_READ_WRITE: fmode |= O_RDWR; break; + default: fmode |= O_RDWR; + } + + RESTART_SYSCALL(fd = open(path, fmode | O_NONBLOCK)) + return TRUE; + + if ((mode & GB_ST_MODE) == GB_ST_READ) + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); + + stream->pipe.can_write = ((mode & GB_ST_MODE) & GB_ST_WRITE) != 0; + stream->common.check_read = TRUE; + + FD = fd; + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (close(FD) < 0) + return TRUE; + + FD = -1; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return read(FD, buffer, len); +} + + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + if (!stream->pipe.can_write) + { + errno = EBADF; + return -1; + } + + return write(FD, buffer, len); +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + return TRUE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + return TRUE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +#define stream_eof NULL + +#define stream_lof NULL + + +static int stream_handle(STREAM *stream) +{ + return FD; +} + + +DECLARE_STREAM(STREAM_pipe); + diff --git a/main/gbx/gbx_stream_process.c b/main/gbx/gbx_stream_process.c new file mode 100644 index 00000000..28491075 --- /dev/null +++ b/main/gbx/gbx_stream_process.c @@ -0,0 +1,119 @@ +/*************************************************************************** + + gbx_stream_process.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_c_process.h" + +#include "gbx_stream.h" + + +#define FDR ((CPROCESS *)stream->process.process)->out +#define FDW ((CPROCESS *)stream->process.process)->in + + +static int stream_open(STREAM *stream, const char *path, int mode, CPROCESS *process) +{ + stream->process.process = process; + stream->common.check_read = TRUE; + STREAM_blocking(stream, FALSE); + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (FDW >= 0) + { + if (close(FDW) < 0) + return TRUE; + + FDW = -1; + } + + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return read(FDR, buffer, len); +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return write(FDW, buffer, len); +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + if (FDR < 0) + return TRUE; + + return (lseek(FDR, pos, whence) < 0); +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + if (FDR < 0) + return TRUE; + + *pos = lseek(FDR, 0, SEEK_CUR); + return (*pos < 0); +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +#define stream_eof NULL + + +#define stream_lof NULL + + +static int stream_handle(STREAM *stream) +{ + return FDR; +} + + +DECLARE_STREAM(STREAM_process); + diff --git a/main/gbx/gbx_stream_string.c b/main/gbx/gbx_stream_string.c new file mode 100644 index 00000000..f1fce480 --- /dev/null +++ b/main/gbx/gbx_stream_string.c @@ -0,0 +1,140 @@ +/*************************************************************************** + + gbx_stream_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gbx_string.h" +#include "gbx_stream.h" + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + stream->string.buffer = NULL; + stream->common.available_now = TRUE; + stream->string.size = 0; + stream->string.pos = 0; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + STRING_unref(&stream->string.buffer); + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + int max; + + max = stream->string.size - stream->string.pos; + + if (len > max) + { + len = max; + errno = 0; + } + + if (len > 0) + memcpy(buffer, stream->string.buffer + stream->string.pos, len); + + stream->string.pos += len; + return len; +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + if ((stream->common.mode & GB_ST_WRITE) == 0) + THROW(E_ACCESS); + + stream->string.buffer = STRING_add(stream->string.buffer, buffer, len); + + stream->string.size += len; + stream->string.pos = stream->string.size; + + return len; +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + int ipos = (int)pos; + + if (pos != (int64_t)ipos || ipos < 0 || ipos > stream->string.size) + return TRUE; + + stream->string.pos = ipos; + return FALSE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = (int64_t)stream->string.pos; + return FALSE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + return (stream->string.pos >= stream->string.size); +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + *len = STRING_length(stream->string.buffer); + return FALSE; +} + + +static int stream_handle(STREAM *stream) +{ + return -1; +} + + +DECLARE_STREAM(STREAM_string); diff --git a/main/gbx/gbx_string.c b/main/gbx/gbx_string.c new file mode 100644 index 00000000..8e941211 --- /dev/null +++ b/main/gbx/gbx_string.c @@ -0,0 +1,1416 @@ +/*************************************************************************** + + gbx_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STRING_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_common_string.h" + +#include "gb_error.h" +#include "gbx_value.h" +#include "gbx_debug.h" +#include "gbx_local.h" +#include "gbx_project.h" +#include "gbx_exec.h" + +#include +#include +#include +#include + +#include "gbx_string.h" + +//#define DEBUG_ME + +#if DEBUG_STRING +#define DEBUG_ME +#endif + +#if DEBUG_STRING +char *STRING_watch = NULL; +#endif + +/*#ifdef DEBUG_ME +extern FILE *MEMORY_log; +#undef stderr +#define stderr MEMORY_log +static void print_where() +{ + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); +} +#define DEBUG_where print_where +#endif*/ + +#if DEBUG_MEMORY + +static void *_my_malloc(int size) +{ + void *ptr; + ALLOC(&ptr, size); + return ptr; +} + +static void _my_free(void *ptr) +{ + IFREE(ptr); +} + +static void *_my_realloc(void *ptr, int size) +{ + REALLOC(&ptr, size); + return ptr; +} +#else + +#define _my_malloc my_malloc +#define _my_free my_free +#define _my_realloc my_realloc + +#endif + +#define STRING_last_count 32 +static char *STRING_last[STRING_last_count] = { 0 }; + +const char STRING_char_table[512] = +"\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0A\x00\x0B\x00\x0C\x00\x0D\x00\x0E\x00\x0F\x00" +"\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1A\x00\x1B\x00\x1C\x00\x1D\x00\x1E\x00\x1F\x00" +"\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2A\x00\x2B\x00\x2C\x00\x2D\x00\x2E\x00\x2F\x00" +"\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3A\x00\x3B\x00\x3C\x00\x3D\x00\x3E\x00\x3F\x00" +"\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4A\x00\x4B\x00\x4C\x00\x4D\x00\x4E\x00\x4F\x00" +"\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5A\x00\x5B\x00\x5C\x00\x5D\x00\x5E\x00\x5F\x00" +"\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6A\x00\x6B\x00\x6C\x00\x6D\x00\x6E\x00\x6F\x00" +"\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7A\x00\x7B\x00\x7C\x00\x7D\x00\x7E\x00\x7F\x00" +"\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8A\x00\x8B\x00\x8C\x00\x8D\x00\x8E\x00\x8F\x00" +"\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9A\x00\x9B\x00\x9C\x00\x9D\x00\x9E\x00\x9F\x00" +"\xA0\x00\xA1\x00\xA2\x00\xA3\x00\xA4\x00\xA5\x00\xA6\x00\xA7\x00\xA8\x00\xA9\x00\xAA\x00\xAB\x00\xAC\x00\xAD\x00\xAE\x00\xAF\x00" +"\xB0\x00\xB1\x00\xB2\x00\xB3\x00\xB4\x00\xB5\x00\xB6\x00\xB7\x00\xB8\x00\xB9\x00\xBA\x00\xBB\x00\xBC\x00\xBD\x00\xBE\x00\xBF\x00" +"\xC0\x00\xC1\x00\xC2\x00\xC3\x00\xC4\x00\xC5\x00\xC6\x00\xC7\x00\xC8\x00\xC9\x00\xCA\x00\xCB\x00\xCC\x00\xCD\x00\xCE\x00\xCF\x00" +"\xD0\x00\xD1\x00\xD2\x00\xD3\x00\xD4\x00\xD5\x00\xD6\x00\xD7\x00\xD8\x00\xD9\x00\xDA\x00\xDB\x00\xDC\x00\xDD\x00\xDE\x00\xDF\x00" +"\xE0\x00\xE1\x00\xE2\x00\xE3\x00\xE4\x00\xE5\x00\xE6\x00\xE7\x00\xE8\x00\xE9\x00\xEA\x00\xEB\x00\xEC\x00\xED\x00\xEE\x00\xEF\x00" +"\xF0\x00\xF1\x00\xF2\x00\xF3\x00\xF4\x00\xF5\x00\xF6\x00\xF7\x00\xF8\x00\xF9\x00\xFA\x00\xFB\x00\xFC\x00\xFD\x00\xFE\x00\xFF\x00"; + +static int _index = 0; + +STRING_MAKE STRING_make_buffer; +#define _make STRING_make_buffer +static bool _subst_add_unquote; + +static iconv_t _conv_unicode_utf8 = (iconv_t)-1; +static iconv_t _conv_utf8_unicode = (iconv_t)-1; + + +/**************************************************************************** + + String pool management + +****************************************************************************/ + +#define SIZE_INC 16 +#define SIZE_INC2 256 +#define SIZE_INC3 4096 + +#define POOL_SIZE 16 +#define POOL_MAX 64 + +#define POOL_MAX_LEN (POOL_SIZE * SIZE_INC) + +static STRING *_pool[POOL_SIZE] = { 0 }; +static char _pool_count[POOL_SIZE] = { 0 }; + +static int real_size(int len) +{ + if (len < 256) + return (len + (SIZE_INC - 1)) & ~(SIZE_INC - 1); + else if (len < 4096) + return (len + (SIZE_INC2 - 1)) & ~(SIZE_INC2 - 1); + else + return (len + (SIZE_INC3 - 1)) & ~(SIZE_INC3 - 1); +} + +#define REAL_SIZE(_len) real_size(_len) + +#ifdef DEBUG_ME + +static void dump_pool(int pool) +{ + STRING *str; + + //fprintf(stderr, "pool %d = [ ", pool); + str = _pool[pool]; + while (str) + { + //fprintf(stderr, "%p ", str); + if (((intptr_t)str) & 1) + { + BREAKPOINT(); + } + str = *((STRING **)str); + } + //fprintf(stderr, " ]\n"); +} + +static STRING *alloc_string(int _len) \ +{ \ + STRING *str; \ + int size = REAL_SIZE((_len) + 1 + sizeof(STRING)); \ + int pool = (size / SIZE_INC) - 1; \ + \ + MEMORY_count++; \ + fprintf(stderr, "[%d]\n", MEMORY_count); \ + \ + if (pool < POOL_SIZE && (_pool_count[pool])) \ + { \ + str = _pool[pool]; \ + fprintf(stderr, "alloc_string: (%p) %d bytes from pool %d [%d]\n", str, size, pool, _pool_count[pool]); \ + _pool[pool] = *((STRING **)str); \ + _pool_count[pool]--; \ + } \ + else \ + { \ + str = _my_malloc(size); \ + if (!str) \ + THROW_MEMORY(); \ + } \ + str->len = (_len); \ + str->ref = 1; \ + return str; \ +} + +#else + +#define alloc_string(_len) \ +({ \ + STRING *str; \ + int pool = (((_len) + 1 + sizeof(STRING) + (SIZE_INC - 1)) / SIZE_INC) - 1; \ + \ + MEMORY_count++; \ + \ + if (pool < POOL_SIZE && (_pool_count[pool])) \ + { \ + str = _pool[pool]; \ + _pool[pool] = *((STRING **)str); \ + _pool_count[pool]--; \ + } \ + else \ + { \ + str = _my_malloc(REAL_SIZE((_len) + 1 + sizeof(STRING))); \ + if (!str) \ + THROW_MEMORY(); \ + } \ + str->len = (_len); \ + str->ref = 1; \ + str; \ +}) + +#endif + +extern char *STRING_utf8_current; + +void STRING_free_real(char *ptr) +{ + STRING *str = STRING_from_ptr(ptr); + int size = REAL_SIZE(str->len + 1 + sizeof(STRING)); + int pool = (size / SIZE_INC) - 1; + + if (STRING_utf8_current == ptr) + { + //fprintf(stderr, "free STRING_utf8_current (%p)\n", ptr); + STRING_utf8_current = NULL; + } + + MEMORY_count--; +#ifdef DEBUG_ME + fprintf(stderr, "[%d]\n", MEMORY_count); + + int i; + for (i = 0; i < STRING_last_count; i++) + { + if (STRING_last[i] == ptr) + { + fprintf(stderr, "%p (%p) already free later!\n", str, ptr); + BREAKPOINT(); + } + } +#endif + + if (pool < POOL_SIZE) + { + if (_pool_count[pool] < POOL_MAX) + { + #ifdef DEBUG_ME + fprintf(stderr, "STRING_free_real: (%p / %p) %d bytes to pool %d\n", str, ptr, size, pool); + /*str->ref = 0x87654321; + str->len = 0x87654321;*/ + #endif + *((STRING **)str) = _pool[pool]; + _pool[pool] = str; + _pool_count[pool]++; + + return; + } + } + + _my_free(str); +} + +static STRING *realloc_string(STRING *str, int new_len) +{ + int size; + int new_size; + + if (new_len == str->len) + return str; + + size = REAL_SIZE(str->len + 1 + sizeof(STRING)); + new_size = REAL_SIZE(new_len + 1 + sizeof(STRING)); + + if (new_size != size) + { + if (new_len == 0) + { + STRING_free_real(str->data); + return NULL; + } + else if (size > POOL_MAX_LEN && new_size > POOL_MAX_LEN) + { + str = _my_realloc(str, new_size); + //REALLOC(&str, new_size, "realloc_string"); + } + else + { + STRING *nstr = alloc_string(new_len); + if (new_len < str->len) + memcpy(nstr->data, str->data, new_len); + else + memcpy(nstr->data, str->data, str->len); + if (str->ref == 1) + STRING_free_real(str->data); + else + str->ref--; + str = nstr; + } + } + + str->len = new_len; + return str; +} + +static void clear_pool(void) +{ + int i; + STRING *str, *next; + + for (i = 0; i < POOL_SIZE; i++) + { + #ifdef DEBUG_ME + fprintf(stderr, "clear_pool: clear pool #%d\n", i); + #endif + str = _pool[i]; + while (str) + { + next = *((STRING **)str); + #ifdef DEBUG_ME + fprintf(stderr, "%p\n", str); + #endif + _my_free(str); + str = next; + } + } +} + +/**************************************************************************** + + String routines + +****************************************************************************/ + +char *STRING_new(const char *src, int len) +{ + STRING *str; + char *data; + + if (len == 0) + return NULL; + + //ALLOC(&str, REAL_SIZE(len + 1 + sizeof(STRING)), "STRING_new"); + str = alloc_string(len); + data = str->data; + + if (src) + memcpy(data, src, len); + + data[len] = 0; + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_new %p ( 0 ) \"%.*s\"\n", data, len, src); + fflush(stderr); + #endif + + return data; +} + +char *STRING_free_later(char *ptr) +{ + /*if (NLast >= MAX_LAST_STRING) + THROW(E_STRING);*/ + + //static int nfl = 0; + + if (ptr) + { + //nfl++; + //fprintf(stderr, "% 8d % 6d\n", nfl, STRING_length(ptr)); + + #ifdef DEBUG_ME + if (STRING_last[_index]) + { + DEBUG_where(); + fprintf(stderr, "STRING_free_later: release temp: %p '%s'\n", STRING_last[_index], STRING_last[_index]); + fflush(stderr); + } + #endif + + //if (STRING_last[_index] && STRING_length(STRING_last[_index]) >= 1024) + // fprintf(stderr, "STRING_free_later: free [%d] %d\n", _index, STRING_length(STRING_last[_index])); + + char *tmp = STRING_last[_index]; + + STRING_last[_index] = ptr; + STRING_unref(&tmp); + + #ifdef DEBUG_ME + fprintf(stderr, "STRING_free_later: post temp: %p '%s'\n", ptr, ptr); + fflush(stderr); + #endif + + //if (STRING_length(ptr) >= 1024) + // fprintf(stderr, "STRING_free_later: [%d] = %d\n", _index, STRING_length(ptr)); + + _index++; + + if (_index >= STRING_last_count) + _index = 0; + + #ifdef DEBUG_ME + int i, j; + fprintf(stderr, "later [ "); + for (i = 0; i < STRING_last_count; i++) + { + if (i == _index) + fprintf(stderr, "^"); + fprintf(stderr, "%p ", STRING_last[i]); + } + fprintf(stderr, "]\n"); + + for (i = 0; i < STRING_last_count; i++) + { + if (STRING_last[i]) + { + for (j = i + 1; j < STRING_last_count; j++) + if (STRING_last[i] == STRING_last[j]) + { + fprintf(stderr, "%p twice !\n", STRING_last[i]); + BREAKPOINT(); + } + } + } + #endif + } + + return ptr; +} + + +int STRING_get_free_index(void) +{ + return _index; +} + + +static void clear_cache(void) +{ + int i; + + for (i = 0; i < STRING_last_count; i++) + { + #ifdef DEBUG_ME + if (STRING_last[i]) + fprintf(stderr, "release temp %p '%s'\n", STRING_last[i], STRING_last[i]); + #endif + STRING_unref(&STRING_last[i]); + STRING_last[i] = NULL; + } + + _index = 0; + + clear_pool(); +} + + +void STRING_exit(void) +{ + clear_cache(); + + #ifdef DEBUG_ME + fprintf(stderr, "STRING_exit\n"); + #endif + + if (_conv_unicode_utf8 != ((iconv_t)-1)) + iconv_close(_conv_unicode_utf8); + if (_conv_utf8_unicode != ((iconv_t)-1)) + iconv_close(_conv_utf8_unicode); +} + + +char *STRING_extend(char *str, int new_len) +{ + STRING *sstr; + + if (!str) + { + sstr = alloc_string(new_len); + #ifdef DEBUG_ME + fprintf(stderr, "STRING_extend: NULL -> %p / %p\n", sstr, sstr->data); + #endif + } + else + { + sstr = realloc_string(STRING_from_ptr(str), new_len); + #ifdef DEBUG_ME + fprintf(stderr, "STRING_extend: %p / %p -> %p / %p\n", STRING_from_ptr(str), str, sstr, sstr->data); + #endif + } + + return sstr ? sstr->data : NULL; +} + + +bool STRING_extend_will_realloc(char *str, int new_len) +{ + STRING *sstr; + int size; + int new_size; + + if (!str) + return new_len != 0; + + sstr = STRING_from_ptr(str); + + if (new_len == sstr->len) + return FALSE; + + size = REAL_SIZE(sstr->len + 1 + sizeof(STRING)); + new_size = REAL_SIZE(new_len + 1 + sizeof(STRING)); + return size != new_size; +} + + +void STRING_new_temp_value(VALUE *value, const char *src, int len) +{ + value->_string.addr = STRING_new_temp(src, len); + value->_string.len = len; //STRING_length(value->_string.addr); + value->_string.start = 0; + value->type = T_STRING; +} + + +void STRING_new_constant_value(VALUE *value, const char *src, int len) +{ + value->_string.addr = (char *)src; + value->_string.len = ((len < 0) ? strlen(src) : len); + value->_string.start = 0; + value->type = T_CSTRING; +} + + +void STRING_void_value(VALUE *value) +{ + value->type = T_CSTRING; + value->_string.addr = NULL; + value->_string.start = 0; + value->_string.len = 0; +} + + +#if DEBUG_STRING + +void STRING_free(char **ptr) +{ + if (*ptr == NULL) + return; + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_free: %p\n", *ptr); + fflush(stderr); + #endif + + STRING_free_real(*ptr); + *ptr = NULL; +} + +void STRING_ref_real(char *ptr) +{ + STRING *str; + + if (ptr == NULL) + return; + + if (ptr == STRING_watch) + BREAKPOINT(); + + str = STRING_from_ptr(ptr); + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_ref: %p ( %d -> %d )\n", ptr, str->ref, str->ref + 1); + if (str->ref < 0 || str->ref > 10000) + { + fprintf(stderr, "*** BAD\n"); + BREAKPOINT(); + } + fflush(stderr); + #endif + + str->ref++; +} + + +void STRING_unref_real(char **ptr) +{ + STRING *str; + + if (*ptr == NULL) + return; + + if (*ptr == STRING_watch) + BREAKPOINT(); + + str = STRING_from_ptr(*ptr); + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_unref: %p ( %d -> %d )\n", *ptr, str->ref, str->ref - 1); + if (str->ref < 1 || str->ref > 10000) + { + fprintf(stderr, "*** BAD\n"); + BREAKPOINT(); + } + fflush(stderr); + #endif + + if ((--str->ref) <= 0) + STRING_free(ptr); +} + +#endif + +void STRING_unref_keep(char **ptr) +{ + STRING *str; + + if (*ptr == NULL) + return; + + str = STRING_from_ptr(*ptr); + if (str->ref > 1) + str->ref--; + else + STRING_free_later(*ptr); +} + +/* The get_param argument index starts at 1, not 0! */ + +#define INDEX_AT 0 +#define INDEX_IGNORE (-1) +#define INDEX_ERROR (-2) + +static int get_param_index(const char *str, int len, uint *pos, int *len_pattern) +{ + uint i; + int index; + bool err; + uchar d; + + i = *pos + 1; + d = str[i]; + if (d == '&') + { + index = INDEX_AT; + } + else if (d >= '1' && d <= '9') + { + index = d - '0'; + } + else if (d == '{') + { + err = FALSE; + index = 0; + for(;;) + { + i++; + if (i >= len) + break; + d = str[i]; + if (d == '}') + break; + if (d >= '0' && d <= '9') + index = index * 10 + d - '0'; + else + err = TRUE; + } + if (err || index < 1 || index >= 64) + index = INDEX_ERROR; + } + else + { + index = INDEX_IGNORE; + } + + if (len_pattern) + *len_pattern = i - *pos + 1; + + *pos = i; + return index; +} + +char *STRING_subst(const char *str, int len, SUBST_FUNC get_param) +{ + uint i; + uchar c; + int np, lenp; + int len_subst; + char *subst; + char *ps; + char *p[64]; + int lp[64]; + + if (!str) + return NULL; + + if (len <= 0) + len = strlen(str); + + // Comment to force a commit because svn unexpectedly lost my log + if (len == 0) + return NULL; + + // Calculate the length + + len_subst = len; + + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == '&') + { + np = get_param_index(str, len, &i, &lenp); + len_subst -= lenp; + + switch (np) + { + case INDEX_AT: + len_subst++; + break; + case INDEX_IGNORE: + len_subst += lenp; + break; + case INDEX_ERROR: + break; + default: + np--; + (*get_param)(np + 1, &p[np], &lp[np]); + if (lp[np] < 0) + lp[np] = strlen(p[np]); + len_subst += lp[np]; + } + } + } + + if (!len_subst) + return NULL; + + subst = STRING_new(NULL, len_subst); + ps = subst; + + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == '&') + { + np = get_param_index(str, len, &i, &lenp); + switch (np) + { + case INDEX_AT: + *ps++ = '&'; + break; + case INDEX_IGNORE: + *ps++ = '&'; + *ps++ = str[i]; + break; + case INDEX_ERROR: + break; + default: + np--; + memcpy(ps, p[np], lp[np]); + ps += lp[np]; + } + } + else + *ps++ = c; + } + + *ps = 0; + return STRING_free_later(subst); +} + +char *STRING_subst_add(const char *str, int len, SUBST_ADD_FUNC add_param) +{ + uint i; + char c, before; + int np; + + if (!str) + return NULL; + + if (len <= 0) + len = strlen(str); + + STRING_start_len(len); + _subst_add_unquote = FALSE; + + before = 0; + + for (i = 0; i < len; i++) + { + c = str[i]; + + if (c == '&') + { + np = get_param_index(str, len, &i, NULL); + switch (np) + { + case INDEX_AT: + STRING_make_char('&'); + break; + case INDEX_IGNORE: + STRING_make_char('&'); + STRING_make_char(str[i]); + break; + case INDEX_ERROR: + break; + default: + (*add_param)(np, before, (i < (len - 1)) ? str[i + 1] : 0); + if (_subst_add_unquote) + { + i++; + _subst_add_unquote = FALSE; + } + } + } + else + STRING_make_char(c); + + before = c; + } + + return STRING_end_temp(); +} + + +void STRING_subst_add_unquote() +{ + if (_subst_add_unquote) + return; + STRING_make_undo_char(); + _subst_add_unquote = TRUE; +} + + +char *STRING_add(char *str, const char *src, int len) +{ + int old_len; + + if (len <= 0 && src != NULL) + len = strlen(src); + + if (len <= 0) + return str; + + if (!str) + return STRING_new(src, len); + + old_len = STRING_length(str); + + str = STRING_extend(str, old_len + len); + if (src) + { + memcpy(&str[old_len], src, len); + str[old_len + len] = 0; + } + + return str; +} + + +char *STRING_add_char(char *str, char c) +{ + int len = STRING_length(str); + char *p; + + str = STRING_extend(str, len + 1); + + p = str + len; + p[0] = c; + p[1] = 0; + + return str; +} + + +void STRING_start_len(int len) +{ + _make.inc = 32; + + if (len == 0) + len = 32; + + _make.buffer = STRING_new(NULL, len); + _make.max = len; + _make.len = 0; + _make.ptr = _make.buffer; + _make.ntemp = 0; +} + + +void STRING_make_dump() +{ + int n = _make.ntemp; + _make.ntemp = 0; + STRING_make(_make.temp, n); +} + + +// len == 0 est possible ! On peut vouloir ajouter une chaîne vide. + +void STRING_make(const char *src, int len) +{ + int pos; + + if (!src) + return; + + if (len < 0) + len = strlen(src); + + if (len <= 0) + return; + + if (_make.ntemp) + STRING_make_dump(); + + _make.len += len; + + if (_make.len >= _make.max) + { + pos = (_make.len - _make.max) * 4; + if (pos < _make.inc) + pos = _make.inc; + else + { + _make.inc = (pos + 31) & ~31; + if (_make.inc > 1024) + _make.inc = 1024; + } + + _make.max += pos; + + //fprintf(stderr, "STRING_extend: %d\n", _max - STRING_length(SUBST_buffer)); + pos = _make.ptr - _make.buffer; + _make.buffer = STRING_extend(_make.buffer, _make.max); + _make.ptr = _make.buffer + pos; + } + + memcpy(_make.ptr, src, len); + _make.ptr += len; +} + +char *STRING_end() +{ + if (_make.ntemp) + STRING_make_dump(); + + if (_make.len > 0) + { + _make.buffer = STRING_extend(_make.buffer, _make.len); + _make.buffer[_make.len] = 0; + } + else + STRING_free(&_make.buffer); + + return _make.buffer; +} + +char *STRING_end_temp() +{ + STRING_end(); + + if (_make.buffer) + STRING_free_later(_make.buffer); + + return _make.buffer; +} + + +/**************************************************************************** + + Charset conversion routines + +****************************************************************************/ + +static iconv_t my_iconv_open(const char *dst, const char *src) +{ + const char *osrc, *odst; + iconv_t *cache; + iconv_t handle; + + osrc = src; + odst = dst; + + if (dst == SC_UNICODE) + dst = EXEC_big_endian ? "UCS-4BE" : "UCS-4LE"; + else if (dst == SC_UTF8) + dst = "UTF-8"; + else if (!dst || *dst == 0) + dst = "ASCII"; + + if (src == SC_UNICODE) + src = EXEC_big_endian ? "UCS-4BE" : "UCS-4LE"; + else if (src == SC_UTF8) + src = "UTF-8"; + else if (!src || *src == 0) + src = "ASCII"; + + if (osrc == SC_UNICODE && odst == SC_UTF8) + cache = &_conv_unicode_utf8; + else if (osrc == SC_UTF8 && odst == SC_UNICODE) + cache = &_conv_utf8_unicode; + else + cache = NULL; + + if (cache && *cache != ((iconv_t)-1)) + return *cache; + + //fprintf(stderr, "iconv_open: %s -> %s\n", src, dst); + handle = iconv_open(dst, src); + if (cache) + *cache = handle; + return handle; +} + + +static void my_iconv_close(iconv_t handle) +{ + if (handle == _conv_unicode_utf8 || handle == _conv_utf8_unicode) + return; + + iconv_close(handle); +} + + +int STRING_conv(char **result, const char *str, int len, const char *src, const char *dst, bool throw) +{ + iconv_t handle; + bool err; + const char *in; + char *out; + size_t in_len; + size_t out_len; + size_t ret; + int errcode = 0; + bool unicode; + + *result = NULL; + + in = str; + in_len = len; + + if (len == 0) + return errcode; + + unicode = (dst == SC_UNICODE); + + handle = my_iconv_open(dst, src); + if (handle == (iconv_t)(-1)) + { + if (errno == EINVAL) + errcode = E_UCONV; + else + errcode = E_CONV; + } + else + { + err = FALSE; + + for(;;) + { + out = COMMON_buffer; + out_len = COMMON_BUF_MAX; + + #if defined(OS_SOLARIS) || defined(OS_BSD) + ret = iconv(handle, &in, &in_len, &out, &out_len); + #else + ret = iconv(handle, (char **)&in, &in_len, &out, &out_len); + #endif + + if (ret != (size_t)(-1) || errno == E2BIG) + *result = STRING_add(*result, COMMON_buffer, COMMON_BUF_MAX - out_len); + + if (ret != (size_t)(-1)) + break; + + if (errno != E2BIG) + { + err = TRUE; + break; + } + } + + my_iconv_close(handle); + + if (unicode) + *result = STRING_add(*result, "\0\0\0", 3); + + STRING_extend_end(*result); + + if (err) + errcode = E_CONV; + } + + if (throw && errcode) + THROW(errcode); + + return errcode; +} + + +char *STRING_conv_to_UTF8(const char *name, int len) +{ + char *result = NULL; + + if (!name) + return ""; + + if (LOCAL_is_UTF8) + { + if (len <= 0) + result = (char *)name; + else + result = STRING_new_temp(name, len); + } + else + { + if (len <= 0) + len = strlen(name); + + STRING_conv(&result, name, len, LOCAL_encoding, SC_UTF8, TRUE); + } + + if (result) + return result; + else + return ""; +} + + +char *STRING_conv_file_name(const char *name, int len) +{ + char *result = NULL; + int pos; + struct passwd *info; + char *dir; + char *user; + int err; + + if (!name) + return ""; + + if (len <= 0) + len = strlen(name); + + if (len > 0 && *name == '~') + { + for (pos = 0; pos < len; pos++) + { + if (name[pos] == '/') + break; + } + + if (pos <= 1) + dir = FILE_get_home(); + else + { + user = STRING_new_temp(&name[1], pos - 1); + info = getpwnam(user); + if (info) + dir = info->pw_dir; + else + dir = NULL; + } + + if (dir) + { + user = STRING_new_zero(dir); + if (pos < len) + user = STRING_add(user, &name[pos], len - pos); + name = user; + len = STRING_length(name); + STRING_free_later(user); + } + } + + if (LOCAL_is_UTF8) + result = STRING_new_temp(name, len); + else + { + err = STRING_conv(&result, name, len, SC_UTF8, LOCAL_encoding, FALSE); + if (err) + result = STRING_new_temp(name, len); + } + //fprintf(stderr, "STRING_conv_file_name: %s\n", result); + + if (result) + return result; + else + return ""; +} + + +// Warning! Returns the index starting from 1, and 0 if no match is found. + +int STRING_search(const char *ps, int ls, const char *pp, int lp, int is, bool right, bool nocase) +{ + int pos, ip, apos; + char *p; + + if (lp > ls) + return 0; + + if (is < 0) + is += ls; + else if (is == 0) + is = right ? ls : 1; + + ls = ls - lp + 1; + + if (is > ls) + { + if (!right) + return 0; + else + is = ls; + } + else if (is < lp) + { + if (right) + return 0; + else if (is < 1) + is = 1; + } + + is--; + + if (lp == 1) + { + char cp = *pp; + + if (nocase && cp != tolower(cp)) + { + cp = tolower(cp); + + if (right) + { + for (; is >= 0; is--) + { + if (tolower(ps[is]) == cp) + return is + 1; + } + } + else + { + for (; is < ls; is++) + { + if (tolower(ps[is]) == cp) + return is + 1; + } + } + } + else + { + if (right) + { + p = memrchr(ps, (uchar)cp, is + 1); + return p ? p - ps + 1 : 0; + + /*for (; is >= 0; is--) + { + if (ps[is] == cp) + return is + 1; + }*/ + } + else + { + p = memchr(ps + is, (uchar)cp, ls - is); + return p ? p - ps + 1 : 0; + + /*for (; is < ls; is++) + { + if (ps[is] == cp) + return is + 1; + }*/ + } + } + + return 0; + } + + pos = 0; + apos = 0; + + if (!nocase) // || pp[0] == tolower(pp[0])) + { + p = memchr(right ? ps : ps + is, (uchar)pp[0], right ? ls : ls - is); + if (!p) + goto __FOUND; + ls -= (int)(p - ps); + is -= (int)(p - ps); + if (is < 0) is = 0; + apos = (int)(p - ps); + ps = p; + + p = memrchr(ps, (uchar)pp[0], right ? is + 1 : ls); + if (!p) + goto __FOUND; + ls = (int)(p - ps + 1); + if (is >= ls) is = ls - 1; + } + + ps += is; + + if (right) + { + if (nocase) + { + for (; is >= 0; is--, ps--) + { + if (tolower(ps[0]) != tolower(pp[0])) + goto __NEXT_RN; + + for (ip = 1; ip < lp; ip++) + { + if (tolower(ps[ip]) != tolower(pp[ip])) + goto __NEXT_RN; + } + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_RN: + ; + } + } + else + { + for (; is >= 0; is--, ps--) + { + if (ps[0] != pp[0]) + goto __NEXT_R; + + for (ip = 1; ip < lp; ip++) + { + if (ps[ip] != pp[ip]) + goto __NEXT_R; + } + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_R: + ; + } + } + } + else + { + if (nocase) + { + for (; is < ls; is++, ps++) + { + if (tolower(ps[0]) != tolower(pp[0])) + goto __NEXT_LN; + + for (ip = 1; ip < lp; ip++) + { + if (tolower(ps[ip]) != tolower(pp[ip])) + goto __NEXT_LN; + } + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_LN: + ; + } + } + else + { + for (; is < ls; is++, ps++) + { + if (ps[0] != pp[0]) + goto __NEXT_L; + + if (memcmp(ps + 1, pp + 1, lp - 1)) + goto __NEXT_L; + + /*for (ip = 1; ip < lp; ip++) + { + if (ps[ip] != pp[ip]) + goto __NEXT_L; + }*/ + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_L: + ; + } + } + } + +__FOUND: + + return pos; +} diff --git a/main/gbx/gbx_string.h b/main/gbx/gbx_string.h new file mode 100644 index 00000000..01b79da3 --- /dev/null +++ b/main/gbx/gbx_string.h @@ -0,0 +1,236 @@ +/*************************************************************************** + + gbx_string.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __STRING_H +#define __STRING_H + +#include "gbx_value.h" +#include "gbx_debug.h" +#include "gb_common_string.h" + +typedef + struct + { + int ref; + int len; + char data[0]; + } + STRING; + +typedef + void (*SUBST_FUNC)(int, char **, int *); + +typedef + void (*SUBST_ADD_FUNC)(int, char, char); + +#define STRING_MAKE_TEMP 32 + +typedef + struct + { + char *buffer; + char *ptr; + int inc; + int len; + int max; + char temp[STRING_MAKE_TEMP]; + int ntemp; + } + STRING_MAKE; + +// NOTE: Defined in gambas.h too +#define SC_UNICODE ((char *)-1) +#define SC_UTF8 ((char *)-2) + +#define STRING_from_ptr(_ptr) ((STRING *)((_ptr) - offsetof(STRING, data))) +#define STRING_length(_ptr) ((_ptr) == NULL ? 0 : STRING_from_ptr(_ptr)->len) + +#ifndef __STRING_C +extern STRING_MAKE STRING_make_buffer; +extern const char STRING_char_table[]; +#endif + +void STRING_init(void); +void STRING_exit(void); +//void STRING_clear_cache(void); + +char *STRING_new(const char *src, int len); +#define STRING_new_zero(_src) \ +({ \ + const char *_s = (_src); \ + STRING_new(_s, _s ? strlen(_s) : 0); \ +}) + +void STRING_free_real(char *ptr); +char *STRING_free_later(char *ptr); +int STRING_get_free_index(void); + +#define STRING_new_temp(_src, _len) STRING_free_later(STRING_new(_src, _len)) +#define STRING_new_temp_zero(_src) STRING_free_later(STRING_new_zero(_src)) +#define STRING_copy(_src) STRING_new((_src), STRING_length(_src)) + +char *STRING_extend(char *str, int new_len); +bool STRING_extend_will_realloc(char *str, int new_len); + +//void STRING_extend_end(char *str); +char *STRING_add(char *str, const char *src, int len); +#define STRING_add_zero(_str, _src) STRING_add((_str), (_src), -1) +char *STRING_add_char(char *str, char c); +#define STRING_add_string(_str, _add) STRING_add((_str), (_add), STRING_length(_add)) + +#define STRING_extend_end(_str) \ +do { \ + if (_str) \ + { \ + (_str)[STRING_length((_str))] = 0; \ + STRING_free_later(_str); \ + } \ +} while(0) + + +#define STRING_copy_from_value(_value) \ +({ \ + char *ptr; \ + if ((_value)->_string.len == 0) \ + ptr = NULL; \ + else if ((_value)->type == T_STRING && (_value)->_string.start == 0 && (_value)->_string.len == STRING_length((_value)->_string.addr)) \ + ptr = (_value)->_string.addr; \ + else \ + ptr = STRING_new(&(_value)->_string.addr[(_value)->_string.start], (_value)->_string.len); \ + ptr; \ +}) + +#define STRING_copy_from_value_temp(_value) \ +({ \ + char *ptr; \ + if ((_value)->_string.len == 0) \ + ptr = NULL; \ + else if ((_value)->type == T_STRING && (_value)->_string.start == 0 && (_value)->_string.len == STRING_length((_value)->_string.addr)) \ + ptr = (_value)->_string.addr; \ + else \ + ptr = STRING_new_temp(&(_value)->_string.addr[(_value)->_string.start], (_value)->_string.len); \ + ptr; \ +}) + + +void STRING_new_temp_value(VALUE *value, const char *src, int len); +void STRING_new_constant_value(VALUE *value, const char *src, int len); + +#define STRING_char_value(_value, _car) \ +do { \ + _value->type = T_CSTRING; \ + _value->_string.addr = (char *)&STRING_char_table[(_car) * 2]; \ + _value->_string.start = 0; \ + _value->_string.len = 1; \ +} while(0) + +void STRING_void_value(VALUE *value); + +char *STRING_subst(const char *str, int len, SUBST_FUNC get_param); +char *STRING_subst_add(const char *str, int len, SUBST_ADD_FUNC add_param); +void STRING_subst_add_unquote(); + +int STRING_conv(char **result, const char *str, int len, const char *src, const char *dst, bool throw); +char *STRING_conv_file_name(const char *name, int len); +char *STRING_conv_to_UTF8(const char *name, int len); + +#if DEBUG_STRING + +#ifndef __STRING_C +extern char *STRING_watch; +#endif + +void STRING_free(char **ptr); +void STRING_ref_real(char *ptr); +void STRING_unref_real(char **ptr); + +#define STRING_ref(_p) fprintf(stderr, "<< %s >> ", __func__), STRING_ref_real(_p) +#define STRING_unref(_p) fprintf(stderr, "<< %s >> ", __func__), STRING_unref_real(_p) + +#else + +#define STRING_free(_p) \ +({ \ + char **pptr = _p; \ + char *ptr = *pptr; \ + if (ptr) \ + { \ + STRING_free_real(ptr); \ + *pptr = NULL; \ + } \ +}) + +#define STRING_ref(_p) \ +({ \ + char *ptr = _p; \ + if (ptr) \ + { \ + STRING_from_ptr(ptr)->ref++; \ + } \ +}) + +#define STRING_unref(_p) \ +({ \ + char **pptr = _p; \ + char *ptr = *pptr; \ + STRING *str; \ + if (ptr) \ + { \ + str = STRING_from_ptr(ptr); \ + if ((--str->ref) <= 0) \ + { \ + STRING_free_real(ptr); \ + *pptr = NULL; \ + } \ + } \ +}) + +#endif + +void STRING_unref_keep(char **ptr); + +int STRING_search(const char *ps, int ls, const char *pp, int lp, int is, bool right, bool nocase); + +void STRING_start_len(int len); +#define STRING_start() STRING_start_len(0) +char *STRING_end(); +char *STRING_end_temp(); +void STRING_make(const char *src, int len); +void STRING_make_dump(); + +#define STRING_make_char(_c) \ +({ \ + if (STRING_make_buffer.ntemp == STRING_MAKE_TEMP) \ + STRING_make_dump(); \ + STRING_make_buffer.temp[STRING_make_buffer.ntemp++] = (_c); \ +}) + +#define STRING_make_undo_char() \ +({ \ + if (STRING_make_buffer.ntemp) \ + STRING_make_buffer.ntemp--; \ + else if (STRING_make_buffer.len > 0) \ + STRING_make_buffer.len--; \ +}) + +#endif diff --git a/main/gbx/gbx_struct.c b/main/gbx/gbx_struct.c new file mode 100644 index 00000000..776f5c9c --- /dev/null +++ b/main/gbx/gbx_struct.c @@ -0,0 +1,59 @@ +/*************************************************************************** + + gbx_struct.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_STRUCT_C + +#include "gb_alloc.h" +#include "gbx_struct.h" + +void *CSTRUCT_create_static(void *ref, CLASS *class, char *addr) +{ + CSTATICSTRUCT *object; + + ALLOC(&object, sizeof(CSTATICSTRUCT)); + + object->ob.class = class; + object->ob.ref = 0; + object->ref = ref; + object->addr = addr; + + class->count++; + + if (ref != STRUCT_CONST) + OBJECT_REF_CHECK(ref); + + //fprintf(stderr, "CSTRUCT_create_static: %s %p ref = %p addr = %p\n", class->name, object, ref, addr); + + return object; +} + +int CSTRUCT_get_size(CLASS *class) +{ + return class->size - sizeof(CSTRUCT); +} + +void CSTRUCT_release(CSTRUCT *ob) +{ + if (ob->ref != STRUCT_CONST) + OBJECT_UNREF(ob->ref); +} diff --git a/main/gbx/gbx_struct.h b/main/gbx/gbx_struct.h new file mode 100644 index 00000000..feccec51 --- /dev/null +++ b/main/gbx/gbx_struct.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + gbx_struct.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_STRUCT_H +#define __GBX_STRUCT_H + +#include "gbx_object.h" +#include "gbx_value.h" + +#define STRUCT_CONST ((void *)-1) + +typedef + struct { + OBJECT ob; + void *ref; + } + CSTRUCT; + +typedef + struct { + OBJECT ob; + void *ref; + char *addr; + } + CSTATICSTRUCT; + +void *CSTRUCT_create_static(void *ref, CLASS *class, char *addr); +int CSTRUCT_get_size(CLASS *class); +void CSTRUCT_release(CSTRUCT *ob); + +#endif diff --git a/main/gbx/gbx_subr.c b/main/gbx/gbx_subr.c new file mode 100644 index 00000000..7d5846d9 --- /dev/null +++ b/main/gbx/gbx_subr.c @@ -0,0 +1,250 @@ +/*************************************************************************** + + gbx_subr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SUBR_C + +#include "gb_common.h" +#include "gbx_subr.h" +#include "gambas.h" +#include "gbx_api.h" + +/*int NPARAM;*/ + +void SUBR_leave_void(int nparam) +{ + RELEASE_MANY(SP, nparam); + + SP->type = T_VOID; + SP++; +} + +void SUBR_leave(int nparam) +{ + BORROW(RP); + + RELEASE_MANY(SP, nparam); + + //*SP++ = *RP; + COPY_VALUE(SP, RP); + SP++; + RP->type = T_VOID; +} + + +bool SUBR_check_string_real(VALUE *param) +{ +__RETRY: + + if (TYPE_is_string(param->type)) + return (param->_string.len == 0); + + if (TYPE_is_null(param->type)) + return TRUE; + + if (param->type == T_VARIANT) + { + VARIANT_undo(param); + goto __RETRY; + } + + THROW_TYPE(T_STRING, param->type); +} + + +void SUBR_check_integer(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (TYPE_is_integer(param->type)) + return; + + if (TYPE_is_long(param->type)) + { + VALUE_convert_integer(param); + return; + } + + THROW_TYPE(T_INTEGER, param->type); +} + + +void SUBR_check_float(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (TYPE_is_number(param->type)) + { + VALUE_conv_float(param); + return; + } + + THROW_TYPE(T_INTEGER, param->type); +} + + +int SUBR_get_integer(VALUE *param) +{ + SUBR_check_integer(param); + return param->_integer.value; +} + + +void *SUBR_get_pointer(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (param->type != T_POINTER) + THROW_TYPE(T_POINTER, param->type); + + return (void *)param->_pointer.value; +} + +void *SUBR_get_pointer_or_string(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (TYPE_is_string(param->type)) + return (void *)(param->_string.addr + param->_string.start); + + if (param->type == T_POINTER) + return (void *)param->_pointer.value; + + THROW_TYPE(T_POINTER, param->type); +} + + +double SUBR_get_float(VALUE *param) +{ + SUBR_check_float(param); + return param->_float.value; +} + + +char *SUBR_get_string(VALUE *param) +{ + if (SUBR_check_string(param)) + return ""; + + return STRING_copy_from_value_temp(param); +} + + +bool SUBR_get_boolean(VALUE *param) +{ + VALUE_conv_boolean(param); + return param->_boolean.value; +} + +static TYPE conv_type(TYPE type) +{ + /*if (type <= T_BYTE) + type = T_BYTE;*/ + if (type == T_CSTRING) // || type == T_NULL) + type = T_STRING; + else if (type == T_CLASS || type == T_FUNCTION) + type = T_VARIANT; + + return type; +} + +TYPE SUBR_check_good_type(VALUE *param, int count) +{ + int i; + TYPE type, type2; + + if (count == 0) + goto __VARIANT; + + type = conv_type(param[0].type); + + if (type == T_VARIANT) + goto __VARIANT; + + if (TYPE_is_value(type)) + { + for (i = 1; i < count; i++) + { + type2 = conv_type(param[i].type); + + if (type2 == type) + continue; + + if (type == T_NULL) + { + if (type2 <= T_FLOAT) + goto __VARIANT; + + type = type2; + continue; + } + + if (type <= T_FLOAT && type2 <= T_FLOAT) + { + if (type2 > type) + type = type2; + continue; + } + + if (type2 == T_NULL) + { + if (type <= T_FLOAT) + goto __VARIANT; + else + continue; + } + + if (TYPE_is_object(type) && TYPE_is_object(type2)) + { + type = T_OBJECT; + continue; + } + + type = T_VARIANT; + break; + } + } + + if (type == T_VOID) + THROW(E_NRETURN); + else if (!TYPE_is_value(type)) + THROW_TYPE(T_VARIANT, type); + + return type; + +__VARIANT: + + return T_VARIANT; +} + +TYPE SUBR_get_type(VALUE *param) +{ + if (param->type == T_INTEGER) + return (TYPE)param->_integer.value; + if (param->type == T_CLASS) + return (TYPE)param->_class.class; + THROW_ILLEGAL(); +} diff --git a/main/gbx/gbx_subr.h b/main/gbx/gbx_subr.h new file mode 100644 index 00000000..9077e8ee --- /dev/null +++ b/main/gbx/gbx_subr.h @@ -0,0 +1,263 @@ +/*************************************************************************** + + gbx_subr.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_SUBR_H +#define __GBX_SUBR_H + +#include "gb_error.h" +#include "gbx_exec.h" + + +typedef + void (*SUBR_FUNC)(VALUE *); + +typedef + double (*MATH_FUNC)(double); + +typedef + double (*MATH_FUNC_2)(double, double); + +/* +#ifndef __GBX_SUBR_C +EXTERN int NPARAM; +#endif +*/ + +#define SUBR_GET_PARAM(nparam) \ + VALUE *PARAM = (SP - nparam); + +#define SUBR_ENTER() \ + int NPARAM = code & 0x3F; \ + SUBR_GET_PARAM(NPARAM); + +#define SUBR_ENTER_PARAM(nparam) \ + const int NPARAM = nparam; \ + SUBR_GET_PARAM(NPARAM); + +#define RETURN RP + +#define SUBR_LEAVE() SUBR_leave(NPARAM) + +/* BORROW(RP); \ + SUBR_leave_void(NPARAM); \ + *SP++ = *RP; \ + RP->type = T_VOID;*/ + +#define SUBR_LEAVE_VOID() SUBR_leave_void(NPARAM); + /*SP->type = T_VOID; \ + SP++;*/ + + +/* Common routines */ + +void SUBR_leave(int nparam); +void SUBR_leave_void(int nparam); + +#define SUBR_check_string(_value) (TYPE_is_string((_value)->type) ? ((_value)->_string.len == 0) : SUBR_check_string_real(_value)) + +#define VOID_STRING(_value) \ +do { \ + RELEASE_STRING(_value); \ + STRING_void_value(_value); \ +} while(0); + +bool SUBR_check_string_real(VALUE *param); +void SUBR_check_integer(VALUE *param); +void SUBR_check_float(VALUE *param); + +int SUBR_get_integer(VALUE *param); +double SUBR_get_float(VALUE *param); +void *SUBR_get_pointer(VALUE *param); +void *SUBR_get_pointer_or_string(VALUE *param); + +char *SUBR_get_string(VALUE *param); + +#define SUBR_get_string_len(_param, _pstr, _plen) \ +do { \ + if (SUBR_check_string(_param)) \ + { \ + *(_pstr) = NULL; \ + *(_plen) = 0; \ + } \ + else \ + { \ + *(_pstr) = (_param)->_string.addr + (_param)->_string.start; \ + *(_plen) = (_param)->_string.len; \ + } \ +} while(0); + +bool SUBR_get_boolean(VALUE *param); + +TYPE SUBR_get_type(VALUE *param); +TYPE SUBR_check_good_type(VALUE *param, int count); + +/* subr_math.c */ + +//void SUBR_add(ushort code); +//void SUBR_sub(ushort code); +//void SUBR_mul(ushort code); +//void SUBR_div(ushort code); +void SUBR_quo(ushort code); +void SUBR_rem(ushort code); +void SUBR_pow(ushort code); + +void SUBR_and_(ushort code); +void SUBR_not(ushort code); + +void SUBR_neg(ushort code); +void SUBR_int(ushort code); +void SUBR_abs(ushort code); +void SUBR_fix(ushort code); +void SUBR_sgn(ushort code); +void SUBR_pi(ushort code); +void SUBR_math(ushort code); +void SUBR_math2(ushort code); + +void SUBR_randomize(ushort code); +void SUBR_rnd(ushort code); +void SUBR_round(ushort code); + +void SUBR_isnan(ushort code); + +/* subr_string.c */ + +void SUBR_cat(ushort code); +void SUBR_file(ushort code); +//void SUBR_left(void); +//void SUBR_right(void); +//void SUBR_mid(void); +//void SUBR_len(void); +void SUBR_trim(ushort code); +void SUBR_space(void); +void SUBR_string(void); +void SUBR_upper(ushort code); +void SUBR_lower(void); +void SUBR_chr(void); +void SUBR_asc(ushort code); +void SUBR_instr(ushort code); +void SUBR_like(ushort code); +void SUBR_scan(void); +void SUBR_subst(ushort code); +void SUBR_replace(ushort code); +void SUBR_split(ushort code); +void SUBR_iconv(void); +void SUBR_sconv(ushort code); +void SUBR_is_chr(ushort code); +void SUBR_tr(void); +void SUBR_quote(ushort code); +void SUBR_unquote(ushort code); +void SUBR_swap(ushort code); + +/* subr_test.c */ + +//void SUBR_comp(ushort code); +//void SUBR_compn(ushort code); +//void SUBR_compi(ushort code); +void SUBR_case(ushort code); +void SUBR_bit(ushort code); +void SUBR_min_max(ushort code); +void SUBR_if(ushort code); +void SUBR_choose(ushort code); +void SUBR_near(void); +void SUBR_strcomp(ushort code); +void SUBR_is(ushort code); + +/* subr_conv.c */ + +void SUBR_is_type(ushort code); +//void SUBR_conv(ushort code); +void SUBR_type(ushort code); +void SUBR_str(void); +void SUBR_val(void); +void SUBR_format(ushort code); +void SUBR_hex_bin(ushort code); + +/* subr_time.c */ + +void SUBR_timer(void); +void SUBR_now(void); +void SUBR_year(ushort code); +void SUBR_time(ushort code); +void SUBR_date(ushort code); +void SUBR_date_op(ushort code); +void SUBR_week(ushort code); + +/* subr_file.c */ + +void SUBR_open(ushort code); +void SUBR_close(void); +void SUBR_print(ushort code); +void SUBR_linput(void); +void SUBR_eof(ushort code); +void SUBR_lof(ushort code); +void SUBR_seek(ushort code); +void SUBR_input(ushort code); +void SUBR_read(ushort code); +void SUBR_write(ushort code); +void SUBR_flush(void); +void SUBR_lock(ushort code); +void SUBR_inp_out(ushort code); + +void SUBR_stat(ushort code); +void SUBR_exist(ushort code); +void SUBR_dir(ushort code); +void SUBR_kill(ushort code); +void SUBR_mkdir(ushort code); +void SUBR_rmdir(ushort code); +void SUBR_move(ushort code); +void SUBR_link(ushort code); +void SUBR_temp(ushort code); +void SUBR_isdir(void); +void SUBR_access(ushort code); +void SUBR_rdir(ushort code); +void SUBR_dfree(); + +void SUBR_exit_inp_out(void); +#define SUBR_exit SUBR_exit_inp_out + +/* subr_extern.c */ + +void SUBR_alloc(ushort code); +void SUBR_free(void); +void SUBR_realloc(ushort code); +void SUBR_strptr(ushort code); +void SUBR_varptr(ushort code); +void SUBR_peek(ushort code); +void SUBR_make(ushort code); +void SUBR_poke(ushort code); + +/* subr_misc.c */ + +void SUBR_error(void); +void SUBR_shell(void); +void SUBR_wait(ushort code); +void SUBR_sleep(ushort code); +void SUBR_exec(ushort code); +void SUBR_eval(ushort code); +void SUBR_array(ushort code); +void SUBR_collection(ushort code); +void SUBR_debug(ushort code); + +void EVAL_string(char *expr); + +#endif diff --git a/main/gbx/gbx_subr_conv.c b/main/gbx/gbx_subr_conv.c new file mode 100644 index 00000000..4e9d6941 --- /dev/null +++ b/main/gbx/gbx_subr_conv.c @@ -0,0 +1,291 @@ +/*************************************************************************** + + gbx_subr_conv.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" + +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_local.h" + +#include "gbx_string.h" +#include "gbx_date.h" +#include "gbx_number.h" + + +void SUBR_is_type(ushort code) +{ + static void *jump[] = { + &&__BAD, &&__BOOLEAN, &&__BAD, &&__BAD, &&__INTEGER, &&__LONG, &&__BAD, &&__FLOAT, + &&__DATE, &&__BAD, &&__BAD, &&__BAD, &&__BAD, &&__BAD, &&__NUMBER, &&__NULL, + &&__BAD, &&__BAD + }; + + bool test; + char *addr; + int len; + VALUE temp; + + SUBR_ENTER_PARAM(1); + + code &= 0x3F; + + if (code == T_NULL) + goto __NULL; + + //VALUE_conv_string(PARAM); + SUBR_get_string_len(PARAM, &addr, &len); + VALUE_from_local_string(&temp, addr, len); + + goto *jump[code]; + +__BOOLEAN: + + test = temp.type == T_BOOLEAN; + goto __END; + +__INTEGER: + + test = temp.type == T_INTEGER; + goto __END; + +__LONG: + + test = temp.type == T_LONG || temp.type == T_INTEGER; + goto __END; + +__FLOAT: +__NUMBER: + + test = temp.type == T_FLOAT || temp.type == T_LONG || temp.type == T_INTEGER; + goto __END; + +__DATE: + + test = temp.type == T_DATE; + goto __END; + +__NULL: + + test = VALUE_is_null(PARAM); + goto __END; + +__BAD: + + THROW_ILLEGAL(); + +__END: + + RELEASE(PARAM); + SP--; + SP->type = T_BOOLEAN; + SP->_integer.value = (-test); + SP++; +} + +/* +void SUBR_conv(ushort code) +{ + VALUE_convert(SP - 1, code & 0x3F); +} +*/ + +void SUBR_type(ushort code) +{ + TYPE type; + int val; + CLASS *class; + + SUBR_ENTER_PARAM(1); + + if (code & 0x3F) + { + if (PARAM->type == T_CLASS) + { + class = PARAM->_class.class; + val = CLASS_sizeof(class) - (class->size - class->off_event); + } + else + val = TYPE_sizeof_memory(SUBR_get_integer(PARAM)); + } + else + { + type = PARAM->type; + if (type == T_VARIANT) + type = PARAM->_variant.vtype; + + if (type == T_CSTRING) + val = T_STRING; + else if (TYPE_is_object(type) && type != T_NULL) + val = T_OBJECT; + else + val = type; + } + + RETURN->_integer.value = val; + RETURN->type = T_INTEGER; + + SUBR_LEAVE(); +} + + +void SUBR_str(void) +{ + char *addr; + int len; + + SUBR_ENTER_PARAM(1); + + VALUE_to_local_string(PARAM, &addr, &len); + STRING_new_temp_value(RETURN, addr, len); + + SUBR_LEAVE(); +} + + +void SUBR_val(void) +{ + char *addr; + int len; + + SUBR_ENTER_PARAM(1); + + if (SUBR_check_string(PARAM)) + VALUE_null(RETURN); + else + { + VALUE_get_string(PARAM, &addr, &len); + VALUE_from_local_string(RETURN, addr, len); + VALUE_conv_variant(RETURN); + } + + SUBR_LEAVE(); +} + + +void SUBR_format(ushort code) +{ + int fmt_type; + char *format = NULL; + int len = 0; + DATE_SERIAL *date; + char *str; + int len_str; + + SUBR_ENTER(); + + if (NPARAM == 1) + fmt_type = LF_STANDARD; + else + { + if (PARAM[1].type == T_VARIANT) + VARIANT_undo(&PARAM[1]); + + if (TYPE_is_string(PARAM[1].type)) + { + fmt_type = LF_USER; + VALUE_get_string(&PARAM[1], &format, &len); + if (!len) + fmt_type = LF_STANDARD; + } + else if (TYPE_is_integer(PARAM[1].type)) + { + fmt_type = PARAM[1]._integer.value; + if (fmt_type <= LF_USER || fmt_type >= LF_MAX) + THROW(E_ARG); + } + else + THROW_TYPE(T_INTEGER, PARAM[1].type); + } + + if (PARAM->type == T_VARIANT) + VARIANT_undo(PARAM); + + if (PARAM->type == T_DATE) + { + date = DATE_split(PARAM); + if (LOCAL_format_date(date, fmt_type, format, len, &str, &len_str)) + THROW(E_FORMAT); + } + else + { + VALUE_conv_float(PARAM); + if (LOCAL_format_number(PARAM->_float.value, fmt_type, format, len, &str, &len_str, TRUE)) + THROW(E_FORMAT); + } + + /*if (NPARAM >= 2) + RELEASE_STRING(&PARAM[1]);*/ + + STRING_new_temp_value(RETURN, str, len_str); + + SUBR_LEAVE(); +} + + +void SUBR_hex_bin(ushort code) +{ + int prec = 0; + int base; + int max_prec; + + SUBR_ENTER(); + + VALUE_conv(PARAM, T_LONG); + + switch(code >> 8) + { + case CODE_BIN: + base = 2; + max_prec = 64; + break; + + case CODE_HEX: + base = 16; + max_prec = 16; + break; + + default: + if (NPARAM == 0) // Compatibility with 2010 bytecode + { + SUBR_upper(1); + return; + } + base = 8; + max_prec = 22; + break; + } + + if (NPARAM == 2) + { + VALUE_conv_integer(&PARAM[1]); + + prec = PARAM[1]._integer.value; + + if (prec < 1 || prec > max_prec) + THROW(E_ARG); + } + + NUMBER_int_to_string(PARAM->_long.value, prec, base, RETURN); + + SUBR_LEAVE(); +} diff --git a/main/gbx/gbx_subr_extern.c b/main/gbx/gbx_subr_extern.c new file mode 100644 index 00000000..547fc5ff --- /dev/null +++ b/main/gbx/gbx_subr_extern.c @@ -0,0 +1,365 @@ +/*************************************************************************** + + gbx_subr_extern.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_common_check.h" +#include "gb_pcode.h" + +#include "gbx_subr.h" + + +void SUBR_alloc(ushort code) +{ + int size; + int count; + void *ptr; + char *copy; + + SUBR_ENTER(); + + if (NPARAM == 2) + count = SUBR_get_integer(&PARAM[1]); + else + count = 1; + + if (TYPE_is_null(PARAM->type)) + { + size = 1; + copy = NULL; + } + if (TYPE_is_string(PARAM->type)) + { + size = PARAM->_string.len + 1; + copy = PARAM->_string.addr + PARAM->_string.start; + } + else + { + size = SUBR_get_integer(PARAM); + copy = NULL; + } + + if (count <= 0 || size <= 0) + THROW(E_ARG); + + ALLOC(&ptr, size * count); + + if (copy) + { + size--; + memcpy(ptr, copy, size); + ((char *)ptr)[size] = 0; + } + + RETURN->type = T_POINTER; + RETURN->_pointer.value = ptr; + + SUBR_LEAVE(); +} + + +void SUBR_free(void) +{ + void *ptr; + + SUBR_ENTER_PARAM(1); + + ptr = SUBR_get_pointer(PARAM); + + IFREE(ptr); + + SUBR_LEAVE_VOID(); +} + + +void SUBR_realloc(ushort code) +{ + int size; + int count; + void *ptr; + + SUBR_ENTER(); + + if (NPARAM == 3) + size = SUBR_get_integer(&PARAM[2]); + else + size = 1; + + count = SUBR_get_integer(&PARAM[1]); + + if (size <= 0 || count <= 0) + THROW(E_ARG); + + ptr = SUBR_get_pointer(&PARAM[0]); + + REALLOC(&ptr, size * count); + + RETURN->type = T_POINTER; + RETURN->_pointer.value = ptr; + + SUBR_LEAVE(); +} + + +void SUBR_strptr(ushort code) +{ + char *ptr; + ssize_t len = 0; + bool err; + + SUBR_ENTER(); + + ptr = (char *)SUBR_get_pointer(PARAM); + + if (NPARAM == 1) + { + err = CHECK_strlen(ptr, &len); + } + else + { + len = SUBR_get_integer(&PARAM[1]); + err = CHECK_address(ptr, len); + } + + if (err) + { + VALUE_null(RETURN); + } + else + { + RETURN->type = T_CSTRING; + RETURN->_string.addr = ptr; + RETURN->_string.start = 0; + RETURN->_string.len = (int)len; + } + + SUBR_LEAVE(); +} + +void SUBR_varptr(ushort code) +{ + ushort op; + void *ptr; + VALUE *val; + CLASS_VAR *var; + + SUBR_ENTER_PARAM(1); + + op = (ushort)SUBR_get_integer(PARAM); + + if ((code & 0xFF) == 1) + { + uint64_t optargs = BP[FP->n_local + FP->n_ctrl - 1]._long.value; + + RETURN->type = T_BOOLEAN; + RETURN->_boolean.value = (optargs & (1 << (FP->n_param + (op & 0xFF) - 256))) ? -1 : 0; + } + else + { + if ((op & 0xFF00) == C_PUSH_LOCAL || (op & 0xFF00) == C_PUSH_PARAM + || (op & 0xFF00) == C_PUSH_LOCAL_NOREF || (op & 0xFF00) == C_PUSH_PARAM_NOREF) + { + if ((op & 0xFF00) == C_PUSH_PARAM || (op & 0xFF00) == C_PUSH_PARAM_NOREF) + val = &PP[(signed char)(op & 0xFF)]; + else + val = &BP[op & 0xFF]; + + switch(val->type) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + ptr = &val->_integer.value; + break; + + case T_LONG: + ptr = &val->_long.value; + break; + + case T_SINGLE: + ptr = &val->_single.value; + break; + + case T_FLOAT: + ptr = &val->_float.value; + break; + + case T_DATE: + ptr = &val->_date.date; + break; + + case T_POINTER: + ptr = &val->_pointer.value; + break; + + case T_VARIANT: + ptr = &val->_variant.value.data; + break; + + default: + THROW(E_UTYPE); + } + } + else if ((op & 0xF800) == C_PUSH_DYNAMIC) + { + var = &CP->load->dyn[op & 0x7FF]; + + if (OP == NULL) + THROW_ILLEGAL(); + + ptr = &OP[var->pos]; + if (var->type.id == T_VARIANT) + ptr = &((VARIANT *)ptr)->value.data; + } + else if ((op & 0xF800) == C_PUSH_STATIC) + { + var = &CP->load->stat[op & 0x7FF]; + ptr = (char *)CP->stat + var->pos; + if (var->type.id == T_VARIANT) + ptr = &((VARIANT *)ptr)->value.data; + } + else + THROW_ILLEGAL(); + + RETURN->type = T_POINTER; + RETURN->_pointer.value = ptr; + } + + SUBR_LEAVE(); +} + + +void SUBR_peek(ushort code) +{ + void *ptr; + + SUBR_ENTER_PARAM(1); + + ptr = SUBR_get_pointer_or_string(PARAM); + + CHECK_enter(); + if (sigsetjmp(CHECK_jump, TRUE) == 0) + VALUE_read(RETURN, ptr, code & 0xF); + CHECK_leave(); + + if (CHECK_got_error()) + THROW(E_ARG); + + SUBR_LEAVE(); +} + + +void SUBR_poke(ushort code) +{ + void *ptr; + + SUBR_ENTER_PARAM(2); + + ptr = SUBR_get_pointer_or_string(PARAM); + + CHECK_enter(); + if (sigsetjmp(CHECK_jump, TRUE) == 0) + VALUE_write(&PARAM[1], ptr, code & 0xF); + CHECK_leave(); + + if (CHECK_got_error()) + THROW(E_ARG); + + SUBR_LEAVE_VOID(); +} + + +void SUBR_make(ushort code) +{ + static void *jump[] = { + &&__ERROR, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, &&__ERROR, &&__ERROR, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR + }; + + TYPE type; + + SUBR_ENTER_PARAM(1); + + type = code & 0x3F; + VALUE_conv(PARAM, type); + STRING_new_temp_value(RETURN, NULL, TYPE_sizeof_memory(type)); + goto *jump[type]; + +__BOOLEAN: + + *(RETURN->_string.addr) = PARAM->_boolean.value != 0; + goto __END; + +__BYTE: + + *(RETURN->_string.addr) = PARAM->_byte.value; + goto __END; + +__SHORT: + + memcpy(RETURN->_string.addr, &PARAM->_short.value, sizeof(short)); + goto __END; + +__INTEGER: + + memcpy(RETURN->_string.addr, &PARAM->_integer.value, sizeof(int)); + goto __END; + +__LONG: + + memcpy(RETURN->_string.addr, &PARAM->_long.value, sizeof(int64_t)); + goto __END; + +__SINGLE: + + memcpy(RETURN->_string.addr, &PARAM->_single.value, sizeof(float)); + goto __END; + +__FLOAT: + + memcpy(RETURN->_string.addr, &PARAM->_float.value, sizeof(double)); + goto __END; + +__DATE: + + memcpy(RETURN->_string.addr, &PARAM->_date.date, sizeof(int) * 2); + goto __END; + +__POINTER: + + memcpy(RETURN->_string.addr, &PARAM->_pointer.value, sizeof(void *)); + goto __END; + +__ERROR: + + THROW_ILLEGAL(); + +__END: + + SUBR_LEAVE(); +} + + diff --git a/main/gbx/gbx_subr_file.c b/main/gbx/gbx_subr_file.c new file mode 100644 index 00000000..2eb86f25 --- /dev/null +++ b/main/gbx/gbx_subr_file.c @@ -0,0 +1,1153 @@ +/*************************************************************************** + + gbx_subr_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_common_buffer.h" + +#include "gbx_subr.h" +#include "gb_file.h" +#include "gb_list.h" +#include "gbx_stream.h" +#include "gbx_archive.h" +#include "gbx_api.h" +#include "gbx_local.h" +#include "gbx_regexp.h" +#include "gbx_string.h" +#include "gbx_c_file.h" +#include "gbx_math.h" + +#include + +typedef + struct _stream { + struct _stream *next; + CSTREAM *stream; + } + CSTREAM_NODE; + +static void *_default_in = NULL; +static void *_default_out = NULL; +static void *_default_err = NULL; + +static GB_ARRAY _result; +static char *_pattern; +static int _len_pattern; +static int _ignore; + +static STREAM *_stream; + + +static void push_stream(void **list, CSTREAM *stream) +{ + CSTREAM_NODE *slot; + + ALLOC(&slot, sizeof(CSTREAM_NODE)); + slot->stream = stream; + //OBJECT_REF(stream); + + slot->next = *list; + *list = slot; +} + + +static CSTREAM *pop_stream(void **list) +{ + CSTREAM *stream; + CSTREAM_NODE *slot; + + if (!*list) + return NULL; + + stream = ((CSTREAM_NODE *)*list)->stream; + slot = *list; + *list = slot->next; + FREE(&slot); + + return stream; +} + +static STREAM *get_default(intptr_t val) +{ + STREAM *stream = NULL; + + switch(val) + { + case 0: + if (_default_in) + stream = CSTREAM_TO_STREAM(((CSTREAM_NODE *)_default_in)->stream); + else + stream = CSTREAM_TO_STREAM(CFILE_get_standard_stream(CFILE_IN)); + break; + case 1: + if (_default_out) + stream = CSTREAM_TO_STREAM(((CSTREAM_NODE *)_default_out)->stream); + else + stream = CSTREAM_TO_STREAM(CFILE_get_standard_stream(CFILE_OUT)); + break; + case 2: + if (_default_err) + stream = CSTREAM_TO_STREAM(((CSTREAM_NODE *)_default_err)->stream); + else + stream = CSTREAM_TO_STREAM(CFILE_get_standard_stream(CFILE_ERR)); + break; + } + + if (!stream) + THROW(E_CLOSED); + + return stream; +} + +static inline STREAM *_get_stream(VALUE *value, bool can_default) +{ + STREAM *stream; + + VARIANT_undo(value); + + if ((can_default) && TYPE_is_integer(value->type) && value->_integer.value >= 0 && value->_integer.value <= 2) + stream = get_default((intptr_t)(value->_integer.value)); + else + { + if (TYPE_is_object(value->type) && value->_object.object && OBJECT_class(value->_object.object)->is_stream) + stream = CSTREAM_TO_STREAM(value->_object.object); + else + { + if (VALUE_is_null(value)) + THROW(E_NULL); + + VALUE_conv_object(value, (TYPE)CLASS_Stream); + stream = NULL; + } + } + + return stream; +} + +/*#define _get_stream(_value, _can_default) \ + STREAM *stream; \ + \ + VARIANT_undo(_value); \ + \ + if ((_can_default) && TYPE_is_integer((_value)->type) && (_value)->_integer.value >= 0 && (_value)->_integer.value <= 2) \ + stream = get_default((intptr_t)(_value)->_integer.value); \ + else \ + { \ + if (TYPE_is_object((_value)->type) && (_value)->_object.object && OBJECT_class((_value)->_object.object)->is_stream) \ + stream = CSTREAM_TO_STREAM((_value)->_object.object); \ + else \ + { \ + if (VALUE_is_null(_value)) \ + THROW(E_NULL); \ + \ + VALUE_conv_object((_value), (TYPE)CLASS_Stream); \ + stream = NULL; \ + } \ + }*/ + +#define get_stream(_value, _can_default) \ +({ \ + STREAM *_stream = _get_stream(_value, _can_default); \ + \ + if (STREAM_is_closed(_stream)) \ + THROW(E_CLOSED); \ + \ + _stream; \ +}) + +#define get_stream_for_writing(_value, _can_default) \ +({ \ + STREAM *_stream = _get_stream(_value, _can_default); \ + \ + if (STREAM_is_closed_for_writing(_stream)) \ + THROW(E_CLOSED); \ + \ + _stream; \ +}) + +static char *get_path(VALUE *param) +{ + char *name; + int len; + + SUBR_get_string_len(param, &name, &len); + + return STRING_conv_file_name(name, len); +} + +void SUBR_open(ushort code) +{ + CFILE *file; + STREAM stream; + int mode; + void *addr; + + SUBR_ENTER_PARAM(2); + + SUBR_check_integer(&PARAM[1]); + mode = PARAM[1]._integer.value ^ GB_ST_BUFFERED; + + if (code & 0x3F) + { + if (TYPE_is_pointer(PARAM->type)) + addr = (void *)PARAM->_pointer.value; + else + THROW_TYPE(T_POINTER, PARAM->type); + + STREAM_open(&stream, (char *)addr, mode | GB_ST_MEMORY); + } + else if (mode & GB_ST_STRING) + { + char *str; + + STREAM_open(&stream, NULL, mode); + + if (!VALUE_is_null(PARAM)) + { + str = SUBR_get_string(PARAM); + + if (mode & GB_ST_WRITE) + { + stream.string.buffer = STRING_new(str, STRING_length(str)); + } + else + { + stream.string.buffer = str; + STRING_ref(str); + } + stream.string.size = STRING_length(str); + } + } + else + { + STREAM_open(&stream, get_path(PARAM), mode); + } + + file = CFILE_create(&stream, mode); + + OBJECT_put(RETURN, file); + + SUBR_LEAVE(); +} + + +void SUBR_close(void) +{ + STREAM *stream; + + SUBR_ENTER_PARAM(1); + + stream = get_stream(PARAM, FALSE); + + if (stream->type == &STREAM_string) + { + char *buffer = stream->string.buffer; + + RETURN->type = T_STRING; + RETURN->_string.addr = buffer; + RETURN->_string.start = 0; + RETURN->_string.len = stream->string.size; + + STRING_ref(buffer); + STREAM_close(stream); + STRING_free_later(buffer); + + //fprintf(stderr, "buffer ref = %d\n", STRING_from_ptr(buffer)->ref); + + SUBR_LEAVE(); + } + else + { + STREAM_close(stream); + SUBR_LEAVE_VOID(); + } +} + + +void SUBR_flush(void) +{ + SUBR_ENTER_PARAM(1); + + STREAM_flush(get_stream(PARAM, TRUE)); + + SUBR_LEAVE_VOID(); +} + + +/*static void print_it(char *addr, long len) +{ + STREAM_write(_stream, addr, len); +}*/ + +void SUBR_print(ushort code) +{ + int i; + char *addr; + int len; + + SUBR_ENTER(); + + if (NPARAM < 1) + THROW(E_NEPARAM); + + _stream = get_stream_for_writing(PARAM, TRUE); + + //PRINT_init(print_it, FALSE); + + for (i = 1; i < NPARAM; i++) + { + PARAM++; + VALUE_to_local_string(PARAM, &addr, &len); + if (len == 1 && *addr == '\n') + STREAM_write_eol(_stream); + else + STREAM_write(_stream, addr, len); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_linput(void) +{ + STREAM *stream; + char *addr; + + stream = get_stream(&SP[-1], TRUE); + + addr = STREAM_line_input(stream, NULL); + + SP--; + if (!TYPE_is_integer(SP->type)) + RELEASE_OBJECT(SP); + + SP->type = T_STRING; + SP->_string.addr = addr; + SP->_string.start = 0; + SP->_string.len = STRING_length(addr); + + SP++; +} + + +void SUBR_input(ushort code) +{ + static STREAM *stream = NULL; + char *addr; + + SUBR_ENTER(); + + if (NPARAM == 1) + stream = get_stream(PARAM, TRUE); + + if (stream) + addr = STREAM_input(stream); + else + addr = NULL; + + if (NPARAM == 1) + { + SP--; + if (!TYPE_is_integer(SP->type)) + RELEASE_OBJECT(SP); + } + + if (addr) + { + //VALUE_from_string(SP, addr, STRING_length(addr)); + SP->type = T_STRING; + SP->_string.addr = addr; + SP->_string.start = 0; + SP->_string.len = STRING_length(addr); + } + else + STRING_void_value(SP); + + SP++; +} + + +void SUBR_eof(ushort code) +{ + STREAM *stream; + bool eof; + + SUBR_ENTER(); + + if (NPARAM == 1) + { + stream = get_stream(PARAM, FALSE); + eof = STREAM_eof(stream); + RELEASE_OBJECT(PARAM); + SP--; + } + else + { + stream = get_default(0); + eof = STREAM_eof(stream); + } + + SP->type = T_BOOLEAN; + SP->_boolean.value = (-eof); + SP++; +} + + +void SUBR_lof(ushort code) +{ + STREAM *stream; + + SUBR_ENTER(); + + if (NPARAM == 1) + stream = get_stream(PARAM, FALSE); + else + stream = get_default(0); + + RETURN->type = T_LONG; + STREAM_lof(stream, &(RETURN->_long.value)); + + SUBR_LEAVE(); +} + + +void SUBR_seek(ushort code) +{ + STREAM *stream; + int64_t pos; + int64_t len; + int whence = SEEK_SET; + + SUBR_ENTER(); + + stream = get_stream(PARAM, FALSE); + + if (NPARAM >= 2) + { + VALUE_conv(&PARAM[1], T_LONG); + pos = PARAM[1]._long.value; + + if (NPARAM == 3) + { + VALUE_conv_integer(&PARAM[2]); + whence = PARAM[2]._integer.value; + if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) + THROW(E_ARG); + } + else + { + if (pos < 0) + { + STREAM_lof(stream, &len); + pos += len; + } + } + + STREAM_seek(stream, pos, (int)whence); + RETURN->type = T_VOID; + } + else + { + RETURN->type = T_LONG; + RETURN->_long.value = STREAM_tell(stream); + } + + SUBR_LEAVE(); +} + +void SUBR_read(ushort code) +{ + STREAM *stream; + char *data; + int len = 0; + int eff; + + SUBR_ENTER_PARAM(2); + + stream = get_stream(PARAM, TRUE); + + if (code & 0x3F) + { + VALUE_conv_integer(&PARAM[1]); + len = PARAM[1]._integer.value; + + if (len == 0) + { + STRING_void_value(RETURN); + } + else if (len > 0) + { + data = STRING_new_temp(NULL, len); + + if ((code & 0x3F) == 2) + STREAM_peek(stream, data, len); + else + STREAM_read(stream, data, len); + + RETURN->type = T_STRING; + RETURN->_string.addr = data; + RETURN->_string.start = 0; + RETURN->_string.len = len; + } + else + { + len = (-len); + + data = STRING_new(NULL, len); + + eff = STREAM_read_max(stream, data, len); + + if (eff == 0) + { + STRING_void_value(RETURN); + STRING_free(&data); + } + else + { + if (eff < len) + { + data = STRING_extend(data, eff); + len = eff; + } + + STRING_extend_end(data); + + RETURN->type = T_STRING; + RETURN->_string.addr = data; + RETURN->_string.start = 0; + RETURN->_string.len = len; + } + } + } + else + { + TYPE type = SUBR_get_type(&PARAM[1]); + STREAM_read_type(stream, type, RETURN); + } + + SUBR_LEAVE(); +} + + +void SUBR_write(ushort code) +{ + STREAM *stream; + + SUBR_ENTER_PARAM(3); + + stream = get_stream_for_writing(PARAM, TRUE); + + if (code & 0x3F) + { + char *str; + int len; + int lenw; + + VALUE_conv_integer(&PARAM[2]); + lenw = PARAM[2]._integer.value; + + if (TYPE_is_pointer(PARAM[1].type)) + { + if (lenw < 0) + lenw = 0; + len = lenw; + str = (char *)PARAM[1]._pointer.value; + } + else + { + SUBR_get_string_len(&PARAM[1], &str, &len); + if (lenw < 0) + lenw = len; + } + + if (lenw > 0) + { + STREAM_write(stream, str, Min(len, lenw)); + if (lenw > len) + STREAM_write_zeros(stream, lenw - len); + } + } + else + { + TYPE type; + type = SUBR_get_type(&PARAM[2]); + VALUE_conv(&PARAM[1], type); + STREAM_write_type(stream, type, &PARAM[1]); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_stat(ushort code) +{ + const char *path; + CSTAT *cstat; + FILE_STAT info; + bool follow = FALSE; + + SUBR_ENTER(); + + path = get_path(PARAM); + + if (NPARAM == 2) + follow = SUBR_get_boolean(&PARAM[1]); + + FILE_stat(path, &info, follow); + + cstat = OBJECT_new(CLASS_Stat, NULL, NULL); + OBJECT_UNREF_KEEP(cstat); + cstat->info = info; + cstat->path = STRING_new_zero(path); + + RETURN->_object.class = CLASS_Stat; + RETURN->_object.object = cstat; + + SUBR_LEAVE(); +} + + +void SUBR_exist(ushort code) +{ + bool exist; + const char *path; + bool follow = FALSE; + + SUBR_ENTER(); + + if (!NPARAM) + { + NPARAM++; + PARAM--; + } + + path = get_path(PARAM); + + if (NPARAM == 2) + follow = SUBR_get_boolean(&PARAM[1]); + + exist = FILE_exist_follow(path, follow); + + RETURN->type = T_BOOLEAN; + RETURN->_integer.value = exist ? -1 : 0; + + SUBR_LEAVE(); +} + + +void SUBR_dir(ushort code) +{ + GB_ARRAY array; + const char *path; + char *pattern; + int len_pattern; + char *str; + int attr = 0; + + SUBR_ENTER(); + + path = get_path(PARAM); + + if (NPARAM >= 2) + { + pattern = SUBR_get_string(&PARAM[1]); + if (NPARAM == 3) + attr = SUBR_get_integer(&PARAM[2]); + } + else + pattern = NULL; + + FILE_dir_first(path, pattern, attr); + + GB_ArrayNew(&array, T_STRING, 0); + + while (!FILE_dir_next(&pattern, &len_pattern)) + { + if (!LOCAL_is_UTF8) + { + if (STRING_conv(&str, pattern, len_pattern, LOCAL_encoding, SC_UTF8, FALSE)) + str = STRING_new(pattern, len_pattern); + else + STRING_ref(str); + } + else + str = STRING_new(pattern, len_pattern); + + *((char **)GB_ArrayAdd(array)) = str; + } + + RETURN->_object.class = OBJECT_class(array); + RETURN->_object.object = array; + + SUBR_LEAVE(); +} + + +static void found_file(const char *path) +{ + char *str; + int len; + + path += _ignore; + len = strlen(path); + + if (_pattern && !REGEXP_match(_pattern, _len_pattern, path, len)) + return; + + if (!LOCAL_is_UTF8) + { + if (STRING_conv(&str, path, len, LOCAL_encoding, SC_UTF8, FALSE)) + str = STRING_new(path, len); + else + STRING_ref(str); + } + else + str = STRING_new(path, len); + + *((char **)GB_ArrayAdd(_result)) = str; +} + +void SUBR_rdir(ushort code) +{ + const char *path; + int attr = 0; + bool follow = FALSE; + + SUBR_ENTER(); + + path = get_path(PARAM); + + if (NPARAM >= 2) + { + SUBR_get_string_len(&PARAM[1], &_pattern, &_len_pattern); + if (NPARAM >= 3) + { + attr = SUBR_get_integer(&PARAM[2]); + if (NPARAM == 4) + follow = SUBR_get_boolean(&PARAM[3]); + } + } + else + _pattern = NULL; + + GB_ArrayNew(&_result, T_STRING, 0); + + if (!path || *path == 0) + path = "."; + _ignore = strlen(path); + if (_ignore > 0 && path[_ignore - 1] != '/') + _ignore++; + + FILE_recursive_dir(path, found_file, NULL, attr, follow); + + RETURN->_object.class = OBJECT_class(_result); + RETURN->_object.object = _result; + + SUBR_LEAVE(); +} + + +void SUBR_kill(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch(code & 0xFF) + { + case 0: + FILE_unlink(get_path(PARAM)); + break; + + case 1: + FILE_mkdir_mode(get_path(PARAM), CFILE_default_dir_auth); + break; + + case 2: + FILE_rmdir(get_path(PARAM)); + break; + + default: + THROW_ILLEGAL(); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_mkdir(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch (code & 0xFF) + { + case 0: // Deprecated Mkdir + SUBR_kill(1); + return; + + case 1: // Even + VALUE_conv(PARAM, T_LONG); + PARAM->type = GB_T_BOOLEAN; + PARAM->_boolean.value = (PARAM->_long.value & 1) == 0 ? -1 : 0; + break; + + case 2: // Odd + VALUE_conv(PARAM, T_LONG); + PARAM->type = GB_T_BOOLEAN; + PARAM->_boolean.value = (PARAM->_long.value & 1) != 0 ? -1 : 0; + break; + + default: + THROW_ILLEGAL(); + } +} + + +void SUBR_rmdir(ushort code) +{ + SUBR_ENTER(); + int min, max; + + switch (code & 0x3F) + { + case 0: // Deprecated RmDir + SUBR_kill(2); + return; + + default: // Rand + + if (NPARAM == 1) + { + VALUE_conv_integer(PARAM); + min = 0; + max = PARAM->_integer.value; + } + else + { + VALUE_conv_integer(PARAM); + VALUE_conv_integer(&PARAM[1]); + min = PARAM->_integer.value; + max = PARAM[1]._integer.value; + if (max < min) + { + int temp = max; + max = min; + min = temp; + } + } + + RETURN->type = T_INTEGER; + RETURN->_integer.value = min + (int)(rnd() * (max + 1 - min)); + break; + } + + SUBR_LEAVE(); +} + + +void SUBR_move(ushort code) +{ + char *path; + char *auth; + FILE_STAT info; + + SUBR_ENTER_PARAM(2); + + path = get_path(&PARAM[0]); + + switch (code & 0xFF) + { + case 0: // Move + + FILE_rename(path, get_path(&PARAM[1])); + break; + + case 1: // Copy + + FILE_copy(path, get_path(&PARAM[1])); + break; + + case 2: // Link + + /* Parameters are NOT inverted ANYMORE! */ + FILE_link(path, get_path(&PARAM[1])); + break; + + case 3: // Chmod + + auth = SUBR_get_string(&PARAM[1]); + FILE_stat(path, &info, TRUE); + FILE_chmod(path, FILE_mode_from_string(info.mode, auth)); + break; + + case 4: // Chown + + auth = SUBR_get_string(&PARAM[1]); + FILE_chown(path, auth); + break; + + case 5: // Chgrp + + auth = SUBR_get_string(&PARAM[1]); + FILE_chgrp(path, auth); + break; + + case 6: // Move DownTo + + FILE_rename_unlink(path, get_path(&PARAM[1])); + break; + + default: + THROW_ILLEGAL(); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_link(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch (code & 0xFF) + { + case 0: // Deprecated Link + SUBR_move(2); + return; + + case 1: // IsNan + VALUE_conv(PARAM, T_FLOAT); + PARAM->type = GB_T_BOOLEAN; + PARAM->_boolean.value = isnan(PARAM->_float.value) ? -1 : 0; + break; + + case 2: // IsInf + VALUE_conv(PARAM, T_FLOAT); + PARAM->type = GB_T_INTEGER; + PARAM->_integer.value = isinf(PARAM->_float.value); + break; + + default: + THROW_ILLEGAL(); + } +} + + +void SUBR_temp(ushort code) +{ + char *temp; + int len; + + SUBR_ENTER(); + + if (NPARAM == 0) + temp = FILE_make_temp(&len, NULL); + else + temp = FILE_make_temp(&len, SUBR_get_string(PARAM)); + + STRING_new_temp_value(RETURN, temp, len); + + SUBR_LEAVE(); +} + + +void SUBR_isdir(void) +{ + bool isdir; + const char *path; + + SUBR_ENTER_PARAM(1); + + path = get_path(PARAM); + + isdir = FILE_is_dir(path); + + RETURN->type = T_BOOLEAN; + RETURN->_integer.value = isdir ? -1 : 0; + + SUBR_LEAVE(); +} + + +void SUBR_access(ushort code) +{ + int access; + + SUBR_ENTER(); + + if (NPARAM == 1) + access = GB_ST_READ; + else + { + VALUE_conv_integer(&PARAM[1]); + access = PARAM[1]._integer.value; + } + + RETURN->type = T_BOOLEAN; + RETURN->_integer.value = FILE_access(get_path(PARAM), access) ? -1 : 0; + + SUBR_LEAVE(); +} + + +void SUBR_lock(ushort code) +{ + code &= 0x1F; + + if (code == 1) + { + SUBR_ENTER_PARAM(1); + STREAM_close(get_stream(PARAM, FALSE)); + SUBR_LEAVE_VOID(); + } + else + { + SUBR_ENTER_PARAM((code == 2) ? 2 : 1); + STREAM stream; + CFILE *file; + const char *path = get_path(PARAM); + double wait, timer; + + if (FILE_is_relative(path)) + THROW(E_BADPATH); + + if (code == 2) + { + DATE_timer(&wait, FALSE); + wait += SUBR_get_float(&PARAM[1]); + } + + for(;;) + { + STREAM_open(&stream, path, GB_ST_LOCK); + + if (!STREAM_lock_all(&stream) && FILE_exist(path)) + break; + + STREAM_close(&stream); + + if (code == 2) + { + DATE_timer(&timer, FALSE); + if (timer < wait) + { + usleep(10000); + continue; + } + } + + THROW(E_LOCK); + } + + file = CFILE_create(&stream, GB_ST_LOCK); + OBJECT_put(RETURN, file); + SUBR_LEAVE(); + } +} + + +void SUBR_inp_out(ushort code) +{ + CSTREAM *stream; + void **where; + + SUBR_ENTER_PARAM(1); + + switch(code & 0x1F) + { + case 0: where = &_default_in; break; + case 1: where = &_default_out; break; + default: where = &_default_err; break; + } + + if (VALUE_is_null(PARAM)) + { + stream = pop_stream(where); + if (stream) + OBJECT_UNREF(stream); + return; + } + + VALUE_conv_object(PARAM, (TYPE)CLASS_Stream); + + stream = PARAM->_object.object; + OBJECT_REF(stream); + + push_stream(where, stream); + + SUBR_LEAVE_VOID(); +} + +static void free_list(void **list) +{ + CSTREAM *stream; + + for(;;) + { + stream = pop_stream(list); + if (!stream) + return; + OBJECT_UNREF(stream); + } +} + +void SUBR_exit_inp_out(void) +{ + free_list((void **)&_default_in); + free_list((void **)&_default_out); + free_list((void **)&_default_err); +} + + +void SUBR_dfree(void) +{ + SUBR_ENTER_PARAM(1); + + RETURN->type = T_LONG; + RETURN->_long.value = FILE_free(get_path(PARAM)); + + SUBR_LEAVE(); +} + +void SUBR_debug(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM == 0) + { + STREAM *stream = get_default(2); + const char *s = DEBUG_get_current_position(); + + STREAM_write(stream, (void *)s, strlen(s)); + STREAM_write(stream, ": ", 2); + + RETURN->type = T_INTEGER; + RETURN->_integer.value = 2; + } + else if (NPARAM == 1) + { + VALUE_conv_boolean(PARAM); + if (PARAM->_boolean.value == 0) + THROW(E_ASSERT); + } + + SUBR_LEAVE(); +} + diff --git a/main/gbx/gbx_subr_math.c b/main/gbx/gbx_subr_math.c new file mode 100644 index 00000000..10979c1a --- /dev/null +++ b/main/gbx/gbx_subr_math.c @@ -0,0 +1,963 @@ +/*************************************************************************** + + gbx_subr_math.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SUBR_MATH_C + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif + +#include "gb_common.h" +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_math.h" + + +#define ABS(x) ((x) < 0 ? (-x) : (x)) + +#define SMT_NAME SUBR_quo +#define SMT_TYPE 3 +#define SMT_OP / + +#include "gbx_subr_math_temp.h" + + +#define SMT_NAME SUBR_rem +#define SMT_TYPE 3 +#define SMT_OP % + +#include "gbx_subr_math_temp.h" + +void SUBR_pi(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM == 0) + { + SP->type = T_FLOAT; + SP->_float.value = M_PI; + SP++; + } + else + { + VALUE_conv_float(PARAM); + PARAM->_float.value = M_PI * PARAM->_float.value; + } +} + + +void SUBR_randomize(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM == 0) + randomize(FALSE, 0); + else + randomize(TRUE, (uint)SUBR_get_integer(PARAM)); + + RETURN->type = T_VOID; + + SUBR_LEAVE(); +} + + +void SUBR_rnd(ushort code) +{ + double min = 0.0, max = 1.0; + + SUBR_ENTER(); + + if (NPARAM >= 1) + { + VALUE_conv_float(&PARAM[0]); + max = PARAM->_float.value; + } + + if (NPARAM == 2) + { + min = max; + VALUE_conv_float(&PARAM[1]); + max = PARAM[1]._float.value; + } + + RETURN->type = T_FLOAT; + RETURN->_float.value = (rnd() * (max - min)) + min; + + SUBR_LEAVE(); +} + + +void SUBR_round(ushort code) +{ + int val = 0; + double power; + + SUBR_ENTER(); + + if (NPARAM == 2) + val = SUBR_get_integer(&PARAM[1]); + + power = pow(10, val); + + VALUE_conv_float(&PARAM[0]); + + RETURN->type = T_FLOAT; + /*RETURN->_float.value = rint(PARAM->_float.value / power) * power;*/ + RETURN->_float.value = floor(PARAM->_float.value / power + 0.5) * power; + + SUBR_LEAVE(); +} + + +void SUBR_math(ushort code) +{ + static void *jump[] = { + NULL, &&__FRAC, &&__LOG, &&__EXP, &&__SQRT, &&__SIN, &&__COS, &&__TAN, &&__ATAN, &&__ASIN, &&__ACOS, + &&__DEG, &&__RAD, &&__LOG10, &&__SINH, &&__COSH, &&__TANH, &&__ASINH, &&__ACOSH, &&__ATANH, + &&__EXP2, &&__EXP10, &&__LOG2, &&__CBRT, &&__EXPM1, &&__LOG1P, &&__FLOOR, &&__CEIL + }; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_float(PARAM); + goto *jump[code & 0x1F]; + +__FRAC: PARAM->_float.value = frac(PARAM->_float.value); goto __END; +__LOG: PARAM->_float.value = __builtin_log(PARAM->_float.value); goto __END; +__EXP: PARAM->_float.value = __builtin_exp(PARAM->_float.value); goto __END; +__SQRT: PARAM->_float.value = __builtin_sqrt(PARAM->_float.value); goto __END; +__SIN: PARAM->_float.value = __builtin_sin(PARAM->_float.value); goto __END; +__COS: PARAM->_float.value = __builtin_cos(PARAM->_float.value); goto __END; +__TAN: PARAM->_float.value = __builtin_tan(PARAM->_float.value); goto __END; +__ATAN: PARAM->_float.value = __builtin_atan(PARAM->_float.value); goto __END; +__ASIN: PARAM->_float.value = __builtin_asin(PARAM->_float.value); goto __END; +__ACOS: PARAM->_float.value = __builtin_acos(PARAM->_float.value); goto __END; +__DEG: PARAM->_float.value = deg(PARAM->_float.value); goto __END; +__RAD: PARAM->_float.value = rad(PARAM->_float.value); goto __END; +__LOG10: PARAM->_float.value = log10(PARAM->_float.value); goto __END; +__SINH: PARAM->_float.value = __builtin_sinh(PARAM->_float.value); goto __END; +__COSH: PARAM->_float.value = __builtin_cosh(PARAM->_float.value); goto __END; +__TANH: PARAM->_float.value = __builtin_tanh(PARAM->_float.value); goto __END; +__ASINH: PARAM->_float.value = __builtin_asinh(PARAM->_float.value); goto __END; +__ACOSH: PARAM->_float.value = __builtin_acosh(PARAM->_float.value); goto __END; +__ATANH: PARAM->_float.value = __builtin_atanh(PARAM->_float.value); goto __END; +__EXP2: PARAM->_float.value = __builtin_exp2(PARAM->_float.value); goto __END; +#if defined(__clang__) + __EXP10: PARAM->_float.value = exp10(PARAM->_float.value); goto __END; +#else + __EXP10: PARAM->_float.value = __builtin_exp10(PARAM->_float.value); goto __END; +#endif +__LOG2: PARAM->_float.value = __builtin_log2(PARAM->_float.value); goto __END; +__CBRT: PARAM->_float.value = __builtin_cbrt(PARAM->_float.value); goto __END; +__EXPM1: PARAM->_float.value = __builtin_expm1(PARAM->_float.value); goto __END; +__LOG1P: PARAM->_float.value = __builtin_log1p(PARAM->_float.value); goto __END; +__FLOOR: PARAM->_float.value = __builtin_floor(PARAM->_float.value); goto __END; +__CEIL: PARAM->_float.value = __builtin_ceil(PARAM->_float.value); goto __END; + +__END: + + //fprintf(stderr, "m: %.24g\n", PARAM->_float.value); + if (!isfinite(PARAM->_float.value)) + THROW(E_MATH); +} + + +void SUBR_math2(ushort code) +{ + static void *jump[] = { NULL, &&__ATAN2, &&__ANG, &&__HYPOT }; + + SUBR_ENTER_PARAM(2); + + VALUE_conv_float(&PARAM[0]); + VALUE_conv_float(&PARAM[1]); + + goto *jump[code & 0x1F]; + +__ATAN2: PARAM->_float.value = __builtin_atan2(PARAM[0]._float.value, PARAM[1]._float.value); goto __END; +__ANG: PARAM->_float.value = __builtin_atan2(PARAM[1]._float.value, PARAM[0]._float.value); goto __END; +__HYPOT: PARAM->_float.value = sqrt(PARAM[0]._float.value * PARAM[0]._float.value + PARAM[1]._float.value * PARAM[1]._float.value); goto __END; + +__END: + + if (!finite(PARAM->_float.value)) + THROW(E_MATH); + + SP--; +} + + +void SUBR_pow(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__NUMBER_INTEGER, &&__NUMBER_FLOAT, &&__OBJECT_FLOAT, &&__OBJECT_OTHER, &&__OBJECT_OBJECT + }; + + VALUE *P1, *P2; + uchar type; + bool variant = FALSE; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__VARIANT: + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + if (TYPE_is_number(P1->type) && TYPE_is_number(P2->type)) + { + if (TYPE_is_integer(P2->type)) + type = 1; + else + type = 2; + } + else + { + type = EXEC_check_operator(P1, P2, CO_POW); + + if (type == OP_OBJECT_FLOAT) + type = 3; + else if (type == OP_OBJECT_OTHER) + type = 4; + else if (type == OP_OBJECT_OBJECT) + type = 5; + else + THROW(E_TYPE, "Number", TYPE_get_name(P2->type)); + } + + if (!variant) + { + if (P1->type != T_OBJECT && P2->type != T_OBJECT) + *PC |= type; + goto *jump[type]; + } + else + { + SUBR_pow(type); + VALUE_conv_variant(P1); + return; + } + +__NUMBER_INTEGER: + + { + static void *ni_jump[] = { &&__M4, &&__M3, &&__M2, &&__M1, &&__P0, &&__END, &&__P2, &&__P3, &&__P4 }; + int val = P2->_integer.value; + + VALUE_conv_float(P1); + + if (val >= -4 && val <= 4) + goto *ni_jump[val + 4]; + else + goto __NUMBER_FLOAT; + + __P0: P1->_float.value = 1.0; goto __END; + __P2: P1->_float.value *= P1->_float.value; goto __END_NUMBER; + __P3: P1->_float.value *= P1->_float.value * P1->_float.value; goto __END_NUMBER; + __P4: P1->_float.value = P1->_float.value * P1->_float.value * P1->_float.value * P1->_float.value; goto __END_NUMBER; + __M1: P1->_float.value = 1.0 / P1->_float.value; goto __END_NUMBER; + __M2: P1->_float.value = 1.0 / P1->_float.value / P1->_float.value; goto __END_NUMBER; + __M3: P1->_float.value = 1.0 / P1->_float.value / P1->_float.value / P1->_float.value; goto __END_NUMBER; + __M4: P1->_float.value = 1.0 / P1->_float.value / P1->_float.value / P1->_float.value / P1->_float.value; goto __END_NUMBER; + } + +__NUMBER_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + P1->_float.value = pow(P1->_float.value, P2->_float.value); + goto __END; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_POWF, P1, P2); + goto __END; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_POWO, P1, P2); + goto __END; + +__OBJECT_OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_POW, P1, P2); + goto __END; + +__END_NUMBER: + + if (!finite(P1->_float.value)) + THROW(E_MATH); + +__END: + + SP--; +} + + +void SUBR_not(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, + &&__OBJECT + }; + + VALUE *P1; + void *jump_end; + TYPE type = code & 0x1F; + bool test; + + P1 = SP - 1; + jump_end = &&__END; + goto *jump[type]; + +__BOOLEAN: + + P1->_integer.value = P1->_integer.value ? 0 : (-1); + goto *jump_end; + +__BYTE: + + P1->_integer.value = (unsigned char)~P1->_integer.value; + goto *jump_end; + +__SHORT: + + P1->_integer.value = (short)~P1->_integer.value; + goto *jump_end; + +__INTEGER: + + P1->_integer.value = ~P1->_integer.value; + goto *jump_end; + +__LONG: + + P1->_long.value = ~P1->_long.value; + goto *jump_end; + +__SINGLE: +__FLOAT: + goto __ERROR; + +__DATE: +__STRING: +__OBJECT: +__NULL: + + test = VALUE_is_null(P1); + RELEASE(P1); + + P1->_integer.value = test ? (-1) : 0; + P1->type = T_BOOLEAN; + goto *jump_end; + +__VARIANT: + + type = P1->type; + + if (TYPE_is_variant(type)) + { + type = P1->_variant.vtype; + jump_end = &&__VARIANT_END; + VARIANT_undo(P1); + } + else if (TYPE_is_object(type)) + *PC |= T_OBJECT; + else if (type) + *PC |= type; + else + goto __ERROR; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__ERROR: + + THROW(E_TYPE, "Number, String or Object", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + return; +} + +void SUBR_and_(ushort code) +{ + static void *jump[] = { + &&__UNKNOWN, &&__UNKNOWN, &&__UNKNOWN, NULL, + &&__BOOLEAN_AND, &&__BOOLEAN_OR, &&__BOOLEAN_XOR, NULL, + &&__BYTE_AND, &&__BYTE_OR, &&__BYTE_XOR, NULL, + &&__SHORT_AND, &&__SHORT_OR, &&__SHORT_XOR, NULL, + &&__INTEGER_AND, &&__INTEGER_OR, &&__INTEGER_XOR, NULL, + &&__LONG_AND, &&__LONG_OR, &&__LONG_XOR, NULL, + &&__OTHER, &&__OTHER, &&__OTHER, NULL, + &&__VARIANT, &&__VARIANT, &&__VARIANT, NULL, + &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + void *jump_end; + + P1 = SP - 2; + P2 = P1 + 1; + + jump_end = &&__END; + type = code & 0x0F; + goto *jump[(code >> 8) - (C_AND >> 8) + type * 4]; + +__BOOLEAN_AND: + P1->type = T_BOOLEAN; P1->_integer.value &= P2->_integer.value; goto *jump_end; + +__BOOLEAN_OR: + P1->type = T_BOOLEAN; P1->_integer.value |= P2->_integer.value; goto *jump_end; + +__BOOLEAN_XOR: + P1->type = T_BOOLEAN; P1->_integer.value ^= P2->_integer.value; goto *jump_end; + +__BYTE_AND: + P1->type = T_BYTE; P1->_integer.value = (unsigned char)(P1->_integer.value & P2->_integer.value); goto *jump_end; + +__BYTE_OR: + P1->type = T_BYTE; P1->_integer.value = (unsigned char)(P1->_integer.value | P2->_integer.value); goto *jump_end; + +__BYTE_XOR: + P1->type = T_BYTE; P1->_integer.value = (unsigned char)(P1->_integer.value ^ P2->_integer.value); goto *jump_end; + +__SHORT_AND: + P1->type = T_SHORT; P1->_integer.value = (short)(P1->_integer.value & P2->_integer.value); goto *jump_end; + +__SHORT_OR: + P1->type = T_SHORT; P1->_integer.value = (short)(P1->_integer.value | P2->_integer.value); goto *jump_end; + +__SHORT_XOR: + P1->type = T_SHORT; P1->_integer.value = (short)(P1->_integer.value ^ P2->_integer.value); goto *jump_end; + +__INTEGER_AND: + P1->type = T_INTEGER; P1->_integer.value &= P2->_integer.value; goto *jump_end; + +__INTEGER_OR: + P1->type = T_INTEGER; P1->_integer.value |= P2->_integer.value; goto *jump_end; + +__INTEGER_XOR: + P1->type = T_INTEGER; P1->_integer.value ^= P2->_integer.value; goto *jump_end; + +__LONG_AND: + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + P1->_long.value &= P2->_long.value; goto *jump_end; + +__LONG_OR: + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + P1->_long.value |= P2->_long.value; goto *jump_end; + +__LONG_XOR: + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + P1->_long.value ^= P2->_long.value; goto *jump_end; + +__UNKNOWN: + + type = Max(P1->type, P2->type); + + if (!TYPE_is_integer_long(type)) + { + if (P1->type != T_VARIANT && P2->type != T_VARIANT) + type = 6; + else + type = 7; + } + + *PC |= type; + goto *jump[(code >> 8) - (C_AND >> 8) + type * 4]; + +__OTHER: + + if (!TYPE_is_integer_long(P1->type)) + { + if (TYPE_is_number(P1->type)) + { + type = P1->type; + goto __ERROR; + } + VALUE_convert_boolean(P1); + } + + if (!TYPE_is_integer_long(P2->type)) + { + if (TYPE_is_number(P2->type)) + { + type = P2->type; + goto __ERROR; + } + VALUE_convert_boolean(P2); + } + + type = Max(P1->type, P2->type); + goto *jump[(code >> 8) - (C_AND >> 8) + type * 4]; + +__VARIANT: + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + if (!TYPE_is_integer_long(P1->type)) + { + if (TYPE_is_number(P1->type)) + { + type = P1->type; + goto __ERROR; + } + VALUE_convert_boolean(P1); + } + + if (!TYPE_is_integer_long(P2->type)) + { + if (TYPE_is_number(P2->type)) + { + type = P2->type; + goto __ERROR; + } + VALUE_convert_boolean(P2); + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_integer_long(type)) + { + jump_end = &&__VARIANT_END; + goto *jump[(code >> 8) - (C_AND >> 8) + type * 4]; + } + +__ERROR: + + THROW(E_TYPE, "Number, String or Object", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + + SP--; +} + +#define MANAGE_VARIANT(_func) \ +({ \ + type = P1->type; \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + if (TYPE_is_variant(type)) \ + { \ + type = P1->_variant.vtype; \ + if (TYPE_is_number_date(type)) \ + { \ + VARIANT_undo(P1); \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + } \ +}) + +#define MANAGE_VARIANT_OBJECT(_func, _op) \ +({ \ + type = P1->type; \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + if (P1->type != T_OBJECT) \ + *PC |= T_DATE + 1; \ + goto *jump[T_DATE + 1]; \ + } \ + \ + if (TYPE_is_variant(type)) \ + { \ + VARIANT_undo(P1); \ + type = P1->type; \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + (_func)(T_DATE + 1); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + } \ +}) + +#define MANAGE_VARIANT_OBJECT_2(_func, _op, _op2) \ +({ \ + type = P1->type; \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + if (P1->type != T_OBJECT) \ + *PC |= T_DATE + 1; \ + goto *jump[T_DATE + 1]; \ + } \ + if (EXEC_check_operator_single(P1, _op2)) \ + { \ + if (P1->type != T_OBJECT) \ + *PC |= T_DATE + 2; \ + goto *jump[T_DATE + 2]; \ + } \ + \ + if (TYPE_is_variant(type)) \ + { \ + VARIANT_undo(P1); \ + type = P1->type; \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + (_func)(T_DATE + 1); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + if (EXEC_check_operator_single(P1, _op2)) \ + { \ + (_func)(T_DATE + 2); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + } \ +}) + + +void SUBR_sgn(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + type = code & 0x0F; + goto *jump[type]; + +__INTEGER: P1->_integer.value = lsgn(P1->_integer.value); goto __END; + +__LONG: P1->_integer.value = llsgn(P1->_long.value); goto __END; + +__SINGLE: P1->_integer.value = (P1->_single.value > 0) ? 1 : ((P1->_single.value < 0) ? (-1) : 0); goto __END; + +__FLOAT: P1->_integer.value = fsgn(P1->_float.value); goto __END; + +__OBJECT: + + EXEC_operator_object_sgn(P1); + return; + +__VARIANT: + + type = P1->type; + + if (TYPE_is_number(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (EXEC_check_operator_single(P1, CO_SGN)) + { + if (P1->type != T_OBJECT) + *PC |= T_DATE + 1; + goto *jump[T_DATE + 1]; + } + + if (TYPE_is_variant(type)) + { + type = P1->_variant.vtype; + if (TYPE_is_number(type)) + { + VARIANT_undo(P1); + goto *jump[type]; + } + } + + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + P1->type = T_INTEGER; +} + + +void SUBR_neg(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: + + return; + +__BYTE: + + P1->_integer.value = (unsigned char)(-P1->_integer.value); return; + +__SHORT: + + P1->_integer.value = (short)(-P1->_integer.value); return; + +__INTEGER: + + P1->_integer.value = (-P1->_integer.value); return; + +__LONG: + + P1->_long.value = (-P1->_long.value); return; + +__SINGLE: + + P1->_single.value = (-P1->_single.value); return; + +__FLOAT: + + P1->_float.value = (-P1->_float.value); return; + +__OBJECT: + + EXEC_operator_object_single(CO_NEG, P1); + return; + +__VARIANT: + + MANAGE_VARIANT_OBJECT(SUBR_neg, CO_NEG); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +void SUBR_abs(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT, &&__OBJECT_FLOAT + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_INTEGER; + goto __INTEGER; + +__BYTE: + + P1->_integer.value = (unsigned char)ABS(-P1->_integer.value); return; + +__SHORT: + + P1->_integer.value = (short)ABS(P1->_integer.value); return; + +__INTEGER: + + P1->_integer.value = ABS(P1->_integer.value); return; + +__LONG: + + P1->_long.value = ABS(P1->_long.value); return; + +__SINGLE: + + P1->_single.value = fabsf(P1->_single.value); return; + +__FLOAT: + + P1->_float.value = fabs(P1->_float.value); return; + +__OBJECT: + + EXEC_operator_object_single(CO_ABS, P1); + return; + +__OBJECT_FLOAT: + + EXEC_operator_object_fabs(P1); + return; + +__VARIANT: + + MANAGE_VARIANT_OBJECT_2(SUBR_abs, CO_ABS, CO_FABS); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +void SUBR_int(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: + + return; + +__SINGLE: + + P1->_single.value = floorf(P1->_single.value); return; + +__FLOAT: + + P1->_float.value = floor(P1->_float.value); return; + +__VARIANT: + + MANAGE_VARIANT(SUBR_int); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +void SUBR_fix(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: + + return; + +__SINGLE: + + P1->_single.value = fixf(P1->_single.value); return; + +__FLOAT: + + P1->_float.value = fix(P1->_float.value); return; + +__VARIANT: + + MANAGE_VARIANT(SUBR_fix); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} diff --git a/main/gbx/gbx_subr_math_temp.h b/main/gbx/gbx_subr_math_temp.h new file mode 100644 index 00000000..3603e468 --- /dev/null +++ b/main/gbx/gbx_subr_math_temp.h @@ -0,0 +1,463 @@ +/*************************************************************************** + + gbx_subr_math_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#if SMT_TYPE == 1 + +void SMT_NAME(void) +{ + #ifdef SMT_FLOAT + + static void *jump[] = { + &&__VARIANT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__ERROR + }; + + #elif defined(SMT_INTEGER) + + static void *jump[] = { + &&__VARIANT, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__ERROR, &&__ERROR, &&__ERROR + }; + + #else + + static void *jump[] = { + &&__VARIANT, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__FLOAT, &&__FLOAT, &&__ERROR + }; + + #endif + + VALUE *P1; + void *jump_end; + TYPE type = EXEC_code & 0x0F; + + P1 = SP - 1; + jump_end = &&__END; + goto *jump[type]; + +__VARIANT: + + type = P1->type; + + if (TYPE_is_number_date(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (TYPE_is_variant(type)) + { + type = P1->_variant.vtype; + if (TYPE_is_number_date(type)) + { + VARIANT_undo(P1); + jump_end = &&__VARIANT_END; + goto *jump[type]; + } + } + + goto __ERROR; + +#ifndef SMT_FLOAT + +__INTEGER: + + #ifdef SMT_OP + P1->_integer.value = SMT_OP ( P1->_integer.value ); + #elif defined(SMT_FUNC_INTEGER) + P1->_integer.value = SMT_FUNC_INTEGER ( P1->_integer.value ); + #elif defined(SMT_FUNC) + P1->_integer.value = SMT_FUNC ( P1->_integer.value ); + #endif + + P1->type = type; + goto *jump_end; + +__LONG: + + VALUE_conv(P1, T_LONG); + + #ifdef SMT_OP + P1->_long.value = SMT_OP ( P1->_long.value ); + #elif defined(SMT_FUNC_LONG) + P1->_long.value = SMT_FUNC_LONG ( P1->_long.value ); + #elif defined(SMT_FUNC) + P1->_long.value = SMT_FUNC ( P1->_long.value ); + #else + #error "LONG function not defined" + #endif + + P1->type = type; + goto *jump_end; + +#endif + +#ifndef SMT_INTEGER + +__FLOAT: + + VALUE_conv_float(P1); + + #ifdef SMT_OP + P1->_float.value = SMT_OP ( P1->_float.value ); + #elif defined(SMT_FUNC_FLOAT) + P1->_float.value = SMT_FUNC_FLOAT ( P1->_float.value ); + #elif defined(SMT_FUNC) + P1->_float.value = SMT_FUNC ( P1->_float.value ); + #endif + + if (!finite(P1->_float.value)) + THROW(E_MATH); + + goto *jump_end; + +#endif + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + return; +/* SP--;*/ +/* if (PCODE_is_void(*PC)) SP--;*/ +} + +#endif + + +#if SMT_TYPE == 2 + +void SMT_NAME(void) +{ + + #ifdef SMT_FLOAT + + static void *jump[] = { + &&__VARIANT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__DATE + }; + + #elif defined(SMT_INTEGER) + + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__ERROR, &&__ERROR, &&__DATE + }; + + #else + + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__FLOAT, &&__FLOAT, &&__DATE + }; + + #endif + + TYPE type; + VALUE *P1, *P2; + void *jump_end; + + P1 = SP - 2; + P2 = P1 + 1; + + jump_end = &&__END; + type = EXEC_code & 0x0F; + goto *jump[type]; + +#ifndef SMT_FLOAT + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + /* + VALUE_conv(P1, type); + VALUE_conv(P2, type); + */ + + #ifdef SMT_TEST_ZERO + if (P2->_integer.value == 0) + THROW(E_ZERO); + #endif + + P1->_integer.value SMT_OP P2->_integer.value; + P1->type = type; + goto *jump_end; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + #ifdef SMT_TEST_ZERO + if (P2->_long.value == 0) + THROW(E_ZERO); + #endif + + P1->_long.value SMT_OP P2->_long.value; + P1->type = type; + goto *jump_end; + +#endif + +__DATE: + +#ifndef SMT_DATE + + goto __ERROR; + +#endif + +#ifndef SMT_INTEGER + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + #ifdef SMT_OP + P1->_float.value SMT_OP P2->_float.value; + #elif defined(SMT_FUNC) + P1->_float.value = SMT_FUNC ( P1->_float.value, P2->_float.value ); + #endif + + #ifdef SMT_TEST_ZERO + if (!finite(P1->_float.value)) + { + if (P2->_float.value == 0.0) + THROW(E_ZERO); + else + THROW(E_MATH); + } + #elif defined(SMT_TEST_RESULT) + if (!finite(P1->_float.value)) + THROW(E_MATH); + #endif + + goto *jump_end; + +#endif + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + if (TYPE_is_string(P1->type)) + VALUE_conv_float(P1); + + if (TYPE_is_string(P2->type)) + VALUE_conv_float(P2); + + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) + type = T_NULL; + else + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + jump_end = &&__VARIANT_END; + goto *jump[type]; + } + + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + + SP--; + /*if (!PCODE_is_void(*PC)) SP++;*/ +} + +#endif + + +#if SMT_TYPE == 3 + +void SMT_NAME(ushort code) +{ + + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__ERROR, &&__ERROR, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_integer_long(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) + type = T_NULL; + else + type = Max(P1->type, P2->type); + + if (TYPE_is_integer_long(type)) + goto *jump[type]; + + goto __ERROR; + +__BOOLEAN: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->type = T_BOOLEAN; + goto __END; + +__BYTE: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->_integer.value = (unsigned char)(P1->_integer.value SMT_OP P2->_integer.value); + P1->type = T_BYTE; + goto __END; + +__SHORT: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->_integer.value = (short)(P1->_integer.value SMT_OP P2->_integer.value); + P1->type = T_SHORT; + goto __END; + +__INTEGER: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->_integer.value = P1->_integer.value SMT_OP P2->_integer.value; + P1->type = T_INTEGER; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + if (P2->_long.value == 0) + THROW(E_ZERO); + + P1->_long.value = P1->_long.value SMT_OP P2->_long.value; + P1->type = T_LONG; + goto __END; + +__ERROR: + + THROW(E_TYPE, "Integer", TYPE_get_name(type)); + +__END: + + SP--; + /*if (!PCODE_is_void(*PC)) SP++;*/ +} + +#endif + + +#undef SMT_TYPE +#undef SMT_NAME + +#ifdef SMT_OP +#undef SMT_OP +#endif + +#ifdef SMT_FUNC +#undef SMT_FUNC +#endif + +#ifdef SMT_FUNC_INTEGER +#undef SMT_FUNC_INTEGER +#endif + +#ifdef SMT_FUNC_LONG +#undef SMT_FUNC_LONG +#endif + +#ifdef SMT_FUNC_FLOAT +#undef SMT_FUNC_FLOAT +#endif + +#ifdef SMT_FLOAT +#undef SMT_FLOAT +#endif + +#ifdef SMT_DATE +#undef SMT_DATE +#endif + +#ifdef SMT_INTEGER +#undef SMT_INTEGER +#endif + +#ifdef SMT_TEST_ZERO +#undef SMT_TEST_ZERO +#endif + +#ifdef SMT_CHECK_FLOAT +#undef SMT_CHECK_FLOAT +#endif + +#ifdef SMT_RESULT +#undef SMT_RESULT +#endif diff --git a/main/gbx/gbx_subr_misc.c b/main/gbx/gbx_subr_misc.c new file mode 100644 index 00000000..71940004 --- /dev/null +++ b/main/gbx/gbx_subr_misc.c @@ -0,0 +1,448 @@ +/*************************************************************************** + + gbx_subr_misc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include + +#include "gb_common.h" +#include "gbx_subr.h" +#include "gbx_class.h" +#include "gambas.h" +#include "gbx_eval.h" +#include "gbx_date.h" +#include "gbx_archive.h" + +#include "gbx_api.h" +#include "gbx_c_collection.h" +#include "gbx_c_process.h" +#include "gbx_debug.h" +#include "gbx_watch.h" +#include "gbx_math.h" + + +static EVAL_INTERFACE EVAL; +static CCOLLECTION *eval_env; + +static void init_eval(void) +{ + static bool init = FALSE; + + if (init) + return; + + COMPONENT_load(COMPONENT_create("gb.eval")); + LIBRARY_get_interface_by_name("gb.eval", EVAL_INTERFACE_VERSION, &EVAL); + init = TRUE; +} + +void SUBR_error(void) +{ + SP->type = T_BOOLEAN; + SP->_boolean.value = EXEC_got_error ? -1 : 0; + SP++; +} + + +void SUBR_wait(ushort code) +{ + SUBR_ENTER(); + + code &= 0x1F; + + EXEC_set_native_error(FALSE); + + if (code == 0) + GB_Wait(-1); + else if (code == 1) + { + int delay = (int)(SUBR_get_float(PARAM) * 1000 + 0.5); + if (delay <= 0) + delay = 0; + GB_Wait(delay); + } + else if (code == 2) + { + NPARAM = 0; + GB_Wait(-2); + } + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_sleep(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch(code & 0x3F) + { + case 0: // Sleep + { + double wait; + struct timespec rem; + + wait = SUBR_get_float(PARAM); + + rem.tv_sec = (time_t)(int)wait; + rem.tv_nsec = (int)(frac(wait) * 1E9); + + while (nanosleep(&rem, &rem) < 0); + break; + } + case 1: // Use + { + char *name = SUBR_get_string(PARAM); + COMPONENT *comp = COMPONENT_find(name); + if (!comp) + comp = COMPONENT_create(name); + + COMPONENT_load(comp); + break; + } + case 2: // CheckExec + { + CPROCESS_check(PARAM->_object.object); + break; + } + } + + SUBR_LEAVE(); +} + +static void error_subr_exec(CPROCESS *process) +{ + OBJECT_UNREF(process); +} + +void SUBR_exec(ushort code) +{ + void *cmd; + bool wait; + int mode; + CPROCESS *process; + bool ret; + bool shell; + char *name; + CARRAY *env; + + SUBR_ENTER_PARAM(4); + + shell = (code & 0x1F) != 0; + + if (shell) + cmd = (void *)SUBR_get_string(PARAM); + else + { + VALUE_conv_object(PARAM, (TYPE)CLASS_StringArray); + cmd = (void *)(PARAM->_object.object); + } + + if (!cmd) + THROW(E_ARG); + + if (VALUE_is_null(&PARAM[1])) + env = NULL; + else + { + VALUE_conv_object(&PARAM[1], (TYPE)CLASS_StringArray); + env = (PARAM[1]._object.object); + } + + VALUE_conv_integer(&PARAM[2]); + mode = PARAM[2]._integer.value; + wait = mode & PM_WAIT; + + name = SUBR_get_string(&PARAM[3]); + + ret = TRUE; // !PCODE_is_void(code); + + if (shell) + mode |= PM_SHELL; + + //STRING_ref(name); ## This should not be needed + process = CPROCESS_create(mode, cmd, name, env); + //STRING_unref(&name); + + if (wait) + { + OBJECT_REF(process); + + ON_ERROR_1(error_subr_exec, process) + { + CPROCESS_wait_for(process, 0); + } + END_ERROR + + if (!ret) + { + OBJECT_UNREF(process); + } + else if (!process->to_string) + { + OBJECT_UNREF_KEEP(process); + } + } + + if (ret) + { + if (process->to_string) + { + char *result = process->result; + process->result = NULL; + + RELEASE_MANY(SP, NPARAM); + + SP->type = T_STRING; + SP->_string.addr = result; + SP->_string.start = 0; + SP->_string.len = STRING_length(result); + SP++; + + OBJECT_UNREF(process); + } + else + { + RETURN->_object.class = CLASS_Process; + RETURN->_object.object = process; + SUBR_LEAVE(); + } + } + else + { + SUBR_LEAVE_VOID(); + } +} + + +static bool get_value(const char *sym, int len, GB_VARIANT *value) +{ + if (eval_env) + if (!GB_CollectionGet(eval_env, sym, len, value)) + return FALSE; + + value->type = T_NULL; + return TRUE; +} + +void EVAL_string(char *expr) +{ + int len; + EXPRESSION *eval; + STREAM *stream; + + init_eval(); + len = strlen(expr); + eval_env = NULL; + + EVAL.New((void **)(void *)&eval, expr, len); + + if (EVAL.Compile(eval, FALSE)) + { + GB_Error(eval->error); + goto _ERROR; + } + + if (!EVAL.Run(eval, get_value)) + goto _ERROR; + + goto _FREE; + +_ERROR: + EVAL.Free((void **)(void *)&eval); + PROPAGATE(); + +_FREE: + EVAL.Free((void **)(void *)&eval); + + VALUE_to_local_string(&TEMP, &expr, &len); + stream = CSTREAM_TO_STREAM(CFILE_get_standard_stream(CFILE_OUT)); + STREAM_write(stream, expr, len); + STREAM_write_eol(stream); + STREAM_flush(stream); +} + +void SUBR_eval(ushort code) +{ + char *expr; + int len; + EXPRESSION *eval; + + SUBR_ENTER(); + + init_eval(); + SUBR_get_string_len(PARAM, &expr, &len); + + if (NPARAM == 2) + { + VALUE_conv_object(&PARAM[1], (TYPE)CLASS_Collection); + eval_env = (CCOLLECTION *)(PARAM[1]._object.object); + } + else + eval_env = NULL; + + EVAL.New((void **)(void *)&eval, expr, len); + + if (EVAL.Compile(eval, FALSE)) + { + GB_Error(eval->error); + goto _ERROR; + } + + if (!EVAL.Run(eval, get_value)) + goto _ERROR; + + *RP = TEMP; + TEMP.type = T_VOID; + goto _FREE; + +_ERROR: + + EVAL.Free((void **)(void *)&eval); + PROPAGATE(); + +_FREE: + + EVAL.Free((void **)(void *)&eval); + SUBR_LEAVE(); +} + +void SUBR_array(ushort code) +{ + static bool reuse = FALSE; + + TYPE type; + int i, j; + CARRAY *array; + bool next_reuse; + + SUBR_ENTER(); + + next_reuse = code & CODE_CALL_VARIANT; + + if (reuse) + { + array = (CARRAY *)(PARAM[-1]._object.object); + type = array->type; + } + else + { + type = SUBR_check_good_type(PARAM, NPARAM); + + if (type == T_NULL) + type = T_OBJECT; + } + + for (i = 0; i < NPARAM; i++) + VALUE_conv(&PARAM[i], type); + + if (reuse) + { + j = array->count; + CARRAY_resize(array, j + NPARAM); + } + else + { + j = 0; + GB_ArrayNew(POINTER(&array), type, NPARAM); + OBJECT_REF(array); + } + + for (i = 0; i < NPARAM; i++, j++) + { + GB_Store(type, (GB_VALUE *)&PARAM[i], GB_ArrayGet(array, j)); + RELEASE(&PARAM[i]); + } + + if (reuse) + { + SP = PARAM; + } + else + { + PARAM->_object.class = OBJECT_class(array); //CLASS_Array; + PARAM->_object.object = array; + SP = PARAM + 1; + } + + reuse = next_reuse; +} + +void SUBR_collection(ushort code) +{ + static bool reuse = FALSE; + + int i; + GB_COLLECTION col; + char *key; + int len; + VALUE *vkey, *vval; + bool next_reuse; + + SUBR_ENTER(); + + next_reuse = code & CODE_CALL_VARIANT; + + if (reuse) + col = (GB_COLLECTION)(PARAM[-1]._object.object); + else + { + GB_CollectionNew(&col, GB_COMP_BINARY); + OBJECT_REF(col); + } + + for (i = 0; i < NPARAM; i += 2) + { + vkey = &PARAM[i]; + vval = vkey + 1; + SUBR_get_string_len(vkey, &key, &len); + VALUE_conv_variant(vval); + if (GB_CollectionSet(col, key, len, (GB_VARIANT *)vval)) + { + OBJECT_UNREF(col); + THROW(E_VKEY); + } + RELEASE_STRING(&PARAM[i]); + RELEASE(&PARAM[i + 1]); + } + + if (reuse) + { + SP = PARAM; + } + else + { + PARAM->_object.class = OBJECT_class(col); //CLASS_Array; + PARAM->_object.object = col; + SP = PARAM + 1; + } + + reuse = next_reuse; +} + + diff --git a/main/gbx/gbx_subr_string.c b/main/gbx/gbx_subr_string.c new file mode 100644 index 00000000..a5a5f0fc --- /dev/null +++ b/main/gbx/gbx_subr_string.c @@ -0,0 +1,1315 @@ +/*************************************************************************** + + gbx_subr_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" + +#include +#include + +#include "gb_pcode.h" +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_regexp.h" +#include "gbx_class.h" +#include "gbx_string.h" +#include "gbx_split.h" +#include "gbx_c_array.h" +#include "gbx_local.h" +#include "gbx_compare.h" + +//--------------------------------------------------------------------------- + +void SUBR_cat(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM <= 2) + { + int len, len2; + char *str; + + if (NPARAM == 1) + { + PARAM--; + + VALUE_conv_string(&PARAM[1]); + len2 = PARAM[1]._string.len; + + if (PARAM[0].type == T_STRING) + { + if (len2 == 0) + { + RELEASE_STRING(&PARAM[0]); + RELEASE_STRING(&PARAM[1]); + SP -= 2; + PC++; + return; + } + + str = PARAM[0]._string.addr; + len = PARAM[0]._string.len; + + if (len && PARAM[0]._string.start == 0 && len == STRING_length(str) && STRING_from_ptr(str)->ref == 2) + { + STRING_from_ptr(str)->ref--; + + str = STRING_add(str, PARAM[1]._string.addr + PARAM[1]._string.start, len2); + + if (PCODE_is(PC[1], C_POP_LOCAL)) + { + VALUE *bp = &BP[(signed char)PC[1]]; + bp->_string.addr = str; + bp->_string.len += len2; + } + else if (PCODE_is(PC[1], C_POP_PARAM)) + { + VALUE *pp = &PP[(signed char)PC[1]]; + pp->_string.addr = str; + pp->_string.len += len2; + } + else if (PCODE_is(PC[1], C_POP_STATIC)) + { + CLASS_VAR *var = &CP->load->stat[PC[1] & 0x7FF]; + *(char **)((char *)CP->stat + var->pos) = str; + } + else if (PCODE_is(PC[1], C_POP_DYNAMIC)) + { + CLASS_VAR *var = &CP->load->dyn[PC[1] & 0x7FF]; + *(char **)(OP + var->pos) = str; + } + + RELEASE_STRING(&PARAM[1]); + SP -= 2; + PC++; + return; + } + } + else + { + VALUE_conv_string(&PARAM[0]); + len = PARAM[0]._string.len ; + } + } + else + { + VALUE_conv_string(&PARAM[0]); + len = PARAM[0]._string.len ; + VALUE_conv_string(&PARAM[1]); + len2 = PARAM[1]._string.len; + } + + str = STRING_new(NULL, len + len2); + + //fprintf(stderr, "normal: str = %p p0 = %p p1 = %p\n", str, PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[1]._string.addr + PARAM[1]._string.start); + + memcpy(str, PARAM[0]._string.addr + PARAM[0]._string.start, len); + memcpy(&str[len], PARAM[1]._string.addr + PARAM[1]._string.start, len2); + + RELEASE_STRING(&PARAM[0]); + RELEASE_STRING(&PARAM[1]); + + SP -= 2; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len + len2; + SP++; + } + else + { + int i; + int len, len_cat; + char *str, *ptr; + + len_cat = 0; + + for (i = 0; i < NPARAM; i++) + { + VALUE_conv_string(&PARAM[i]); + len_cat += PARAM[i]._string.len; + } + + str = STRING_new(NULL, len_cat); + ptr = str; + + i = NPARAM; + while (i--) + { + len = PARAM->_string.len; + + if (len) + { + memcpy(ptr, PARAM->_string.addr + PARAM->_string.start, len); + ptr += len; + } + + RELEASE_STRING(PARAM); + PARAM++; + } + + SP -= NPARAM; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len_cat; + SP++; + } +} + + +void SUBR_file(ushort code) +{ + int i; + int length; + char *addr; + int len; + char *str, *ptr; + bool slash; + + SUBR_ENTER(); + + length = 0; + slash = FALSE; + + for (i = 0; i < NPARAM; i++) + { + VALUE_conv_string(&PARAM[i]); + VALUE_get_string(&PARAM[i], &addr, &len); + //SUBR_get_string_len(&PARAM[i], &addr, &len); + + if (addr && len > 0) + { + if (length > 0) + { + if (!slash && addr[0] != '/') + length++; + else if (slash && addr[0] == '/') + length--; + } + + slash = addr[len - 1] == '/'; + + length += len; + } + + } + + str = STRING_new(NULL, length); + ptr = str; + slash = FALSE; + + i = NPARAM; + while (i--) + { + if (PARAM->type != T_NULL) + { + VALUE_get_string(PARAM, &addr, &len); + + if (len > 0) + { + if (ptr > str) + { + if (!slash && *addr != '/') + *ptr++ = '/'; + else if (slash && *addr == '/') + ptr--; + } + + slash = addr[len - 1] == '/'; + + memcpy(ptr, addr, len); + ptr += len; + } + + RELEASE_STRING(PARAM); + } + + PARAM++; + } + + SP -= NPARAM; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = length; + SP++; +} + + + +void SUBR_space(void) +{ + int len; + + SUBR_ENTER_PARAM(1); + + SUBR_check_integer(PARAM); + len = PARAM->_integer.value; + + if (len < 0) + THROW(E_ARG); + + if (len == 0) + { + STRING_void_value(&SP[-1]); + } + else + { + char *str = STRING_new(NULL, len); + memset(str, ' ', len); + SP--; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len; + SP++; + } + + //SUBR_LEAVE(); +} + + + +void SUBR_string(void) +{ + int i; + char *d; + char *s; + int ld, ls; + + SUBR_ENTER_PARAM(2); + + SUBR_check_integer(PARAM); + SUBR_get_string_len(&PARAM[1], &s, &ls); + + ld = PARAM->_integer.value * ls; + if (ld < 0) + THROW(E_ARG); + + if (ld == 0) + { + STRING_void_value(RETURN); + } + else + { + STRING_new_temp_value(RETURN, NULL, ld); + d = RETURN->_string.addr; + + for (i = 0; i < PARAM->_integer.value; i++) + { + memcpy(d, s, ls); + d += ls; + } + + *d = 0; + } + + SUBR_LEAVE(); +} + + +void SUBR_trim(ushort code) +{ + unsigned char *str; + bool left, right; + + SUBR_GET_PARAM(1); + + if (SUBR_check_string(PARAM)) + { + VOID_STRING(PARAM); + return; + } + + code &= 0x1F; + left = (code == 0 || code == 1); + right = (code == 0 || code == 2); + + if (PARAM->_string.len > 0) + { + str = (uchar *)&PARAM->_string.addr[PARAM->_string.start]; + + if (left) + { + while (PARAM->_string.len > 0 && *str <= ' ') + { + PARAM->_string.start++; + PARAM->_string.len--; + str++; + } + } + + if (right) + { + while (PARAM->_string.len > 0 && str[PARAM->_string.len - 1] <= ' ') + { + PARAM->_string.len--; + } + } + } +} + +void SUBR_upper(ushort code) +{ + char *str; + int len, i; + + SUBR_ENTER_PARAM(1); + + if (SUBR_check_string(PARAM)) + { + VOID_STRING(&SP[-1]); + } + else + { + len = PARAM->_string.len; + if (len > 0) + { + str = STRING_new(&PARAM->_string.addr[PARAM->_string.start], PARAM->_string.len); + + if (code & 0x3F) + { + for (i = 0; i < len; i++) + str[i] = tolower(str[i]); + } + else + { + for (i = 0; i < len; i++) + str[i] = toupper(str[i]); + } + + SP--; + RELEASE_STRING(SP); + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len; + SP++; + } + } +} + +void SUBR_chr(void) +{ + int car; + + SUBR_GET_PARAM(1); + + VALUE_conv_integer(PARAM); + + car = PARAM->_integer.value; + if (car < 0 || car > 255) + THROW(E_ARG); + + STRING_char_value(PARAM, car); +} + +void SUBR_asc(ushort code) +{ + int pos = 0; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + pos = 1; + if (NPARAM == 2) + { + SUBR_check_integer(&PARAM[1]); + pos = PARAM[1]._integer.value; + } + + if (pos < 1 || pos > PARAM->_string.len) + pos = 0; + else + pos = (unsigned char)PARAM->_string.addr[PARAM->_string.start + pos - 1]; + } + + RETURN->type = T_INTEGER; + RETURN->_integer.value = pos; + + SUBR_LEAVE(); +} + +void SUBR_instr(ushort code) +{ + bool right, nocase = FALSE; + int is, pos; + //int pos2; + char *ps, *pp; + int ls, lp; + + SUBR_ENTER(); + + pos = 0; + + if (SUBR_check_string(PARAM)) + goto __FOUND; + + if (SUBR_check_string(&PARAM[1])) + goto __FOUND; + + lp = PARAM[1]._string.len; + ls = PARAM->_string.len; + + right = ((code >> 8) == CODE_RINSTR); + + if (lp > ls) goto __FOUND; + + is = 0; + + if (NPARAM >= 3) + is = SUBR_get_integer(&PARAM[2]); + + if (NPARAM == 4) + nocase = SUBR_get_integer(&PARAM[3]) == GB_COMP_NOCASE; + + ps = PARAM->_string.addr + PARAM->_string.start; + pp = PARAM[1]._string.addr + PARAM[1]._string.start; + + pos = STRING_search(ps, ls, pp, lp, is, right, nocase); + +__FOUND: + + RELEASE_STRING(PARAM); + RELEASE_STRING(&PARAM[1]); + + SP -= NPARAM; + SP->type = T_INTEGER; + SP->_integer.value = pos; + SP++; +} + +void SUBR_like(ushort code) +{ + static const void *jump[] = { &&__LIKE, &&__BEGINS, &&__ENDS, &&__MATCH }; + char *pattern; + char *string; + int len_pattern, len_string; + bool ret = FALSE; + + SUBR_ENTER_PARAM(2); + + SUBR_get_string_len(&PARAM[0], &string, &len_string); + SUBR_get_string_len(&PARAM[1], &pattern, &len_pattern); + + goto *jump[code & 0x3]; + +__LIKE: + + ret = REGEXP_match(pattern, len_pattern, string, len_string); + goto __RETURN; + +__BEGINS: + + if (len_pattern == 0) + ret = TRUE; + else if (len_pattern <= len_string) + ret = STRING_equal_same(string, pattern, len_pattern); + goto __RETURN; + +__ENDS: + + if (len_pattern == 0) + ret = TRUE; + else if (len_pattern <= len_string) + ret = STRING_equal_same(string + len_string - len_pattern, pattern, len_pattern); + goto __RETURN; + +__MATCH: + + ret = REGEXP_match_pcre(pattern, len_pattern, string, len_string); + goto __RETURN; + +__RETURN: + + RETURN->type = T_BOOLEAN; + RETURN->_boolean.value = -(ret ^ !!(code & 0x4)); + + SUBR_LEAVE(); +} + +static int subst_nparam; +static VALUE *subst_param; + +static void get_subst(int np, char **str, int *len) +{ + if (np > 0 && np < subst_nparam) + VALUE_get_string(&subst_param[np], str, len); + else + { + *str = NULL; + *len = 0; + } +} + +void SUBR_subst(ushort code) +{ + char *string; + int len; + int np; + + SUBR_ENTER(); + + SUBR_get_string_len(&PARAM[0], &string, &len); + + for (np = 1; np < NPARAM; np++) + VALUE_conv_string(&PARAM[np]); + + subst_param = PARAM; + subst_nparam = NPARAM; + + string = STRING_subst(string, len, get_subst); + + /*for (np = 0; np < NPARAM; np++) + RELEASE_STRING(&PARAM[np]);*/ + + RETURN->type = T_STRING; + RETURN->_string.addr = (char *)string; + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(string); + + SUBR_LEAVE(); +} + +void SUBR_replace(ushort code) +{ + char *ps; + char *pp; + char *pr; + int ls, lp, lr; + int pos; + bool nocase = FALSE; + + SUBR_ENTER(); + + SUBR_get_string_len(&PARAM[0], &ps, &ls); + SUBR_get_string_len(&PARAM[1], &pp, &lp); + SUBR_get_string_len(&PARAM[2], &pr, &lr); + if (NPARAM == 4) + nocase = SUBR_get_integer(&PARAM[3]) == GB_COMP_NOCASE; + + if (lp == 0 || ls == 0) + { + RELEASE(&PARAM[1]); + RELEASE(&PARAM[2]); + SP -= NPARAM; + SP++; + return; + } + + if (lp == lr) + { + ps = STRING_new_temp(ps, ls); + RETURN->_string.addr = ps; + RETURN->_string.len = ls; + + if (lp == 1) + { + char cp = *pp; + char cr = *pr; + + if (nocase) + { + char cpl = tolower(cp); + cp = toupper(cp); + + for (pos = 0; pos < ls; pos++) + { + if (ps[pos] == cp || ps[pos] == cpl) + ps[pos] = cr; + } + } + else + { + for (pos = 0; pos < ls; pos++) + { + if (ps[pos] == cp) + ps[pos] = cr; + } + } + } + else + { + for(;;) + { + pos = STRING_search(ps, ls, pp, lp, 0, FALSE, nocase); + if (pos == 0) + break; + pos--; + memcpy(&ps[pos], pr, lp); + pos += lp; + ps += pos; + ls -= pos; + } + } + } + else + { + STRING_start_len(ls); + + for(;;) + { + pos = STRING_search(ps, ls, pp, lp, 1, FALSE, nocase); + if (pos == 0) + break; + + pos--; + + if (pos > 0) + STRING_make(ps, pos); + + STRING_make(pr, lr); + + pos += lp; + + ps += pos; + ls -= pos; + + if (ls <= 0) + break; + } + + STRING_make(ps, ls); + RETURN->_string.addr = STRING_end_temp(); + RETURN->_string.len = STRING_length(RETURN->_string.addr); + } + + RETURN->type = T_STRING; + RETURN->_string.start = 0; + + SUBR_LEAVE(); +} + +void SUBR_split(ushort code) +{ + CARRAY *array; + char *str; + int lstr; + char *sep = NULL; + int lsep = 0; + char *esc = NULL; + int lesc = 0; + bool no_void = FALSE; + bool keep_esc = FALSE; + + SUBR_ENTER(); + + VALUE_conv_string(PARAM); + VALUE_get_string(PARAM, &str, &lstr); + + if (NPARAM >= 2) + { + SUBR_get_string_len(&PARAM[1], &sep, &lsep); + if (NPARAM >= 3) + { + SUBR_get_string_len(&PARAM[2], &esc, &lesc); + if (NPARAM >= 4) + { + no_void = SUBR_get_boolean(&PARAM[3]); + if (NPARAM == 5) + keep_esc = SUBR_get_boolean(&PARAM[4]); + } + } + } + + array = STRING_split(str, lstr, sep, lsep, esc, lesc, no_void, keep_esc); + + RETURN->_object.class = CLASS_StringArray; + RETURN->_object.object = array; + + SUBR_LEAVE(); +} + +void SUBR_scan(void) +{ + CARRAY *array; + char *str; + int len_str; + char *pat; + int len_pat; + + SUBR_ENTER_PARAM(2); + + SUBR_get_string_len(&PARAM[0], &str, &len_str); + SUBR_get_string_len(&PARAM[1], &pat, &len_pat); + + array = OBJECT_create(CLASS_StringArray, NULL, NULL, 0); + + if (len_str && len_pat) + REGEXP_scan(array, pat, len_pat, str, len_str); + + RETURN->_object.class = CLASS_StringArray; + RETURN->_object.object = array; + + SUBR_LEAVE(); +} + +void SUBR_iconv(void) +{ + char *str; + const char *src; + const char *dst; + char *result; + int len; + + SUBR_ENTER_PARAM(3); + + str = SUBR_get_string(&PARAM[0]); + len = PARAM[0]._string.len; + + src = SUBR_get_string(&PARAM[1]); + dst = SUBR_get_string(&PARAM[2]); + + STRING_conv(&result, str, len, src, dst, TRUE); + + if (!result) + VALUE_null(RETURN); + else + { + RETURN->type = T_STRING; + RETURN->_string.addr = result; + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(result); + } + + SUBR_LEAVE(); +} + +void SUBR_sconv(ushort code) +{ + char *str; + const char *src; + const char *dst; + char *result; + int len; + + SUBR_ENTER_PARAM(1); + + if (LOCAL_is_UTF8) + return; + + str = SUBR_get_string(&PARAM[0]); + len = PARAM[0]._string.len; + + if (code & 0xF) + { + src = LOCAL_encoding; + dst = SC_UTF8; + } + else + { + src = SC_UTF8; + dst = LOCAL_encoding; + } + + STRING_conv(&result, str, len, src, dst, TRUE); + + if (!result) + VALUE_null(RETURN); + else + { + RETURN->type = T_STRING; + RETURN->_string.addr = result; + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(result); + } + + SUBR_LEAVE(); +} + +void SUBR_is_chr(ushort code) +{ + static ushort test[256] = { + 0x0041, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x00C1, 0x0041, 0x0041, 0x0041, 0x0041, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x00C1, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0231, 0x0231, 0x0231, 0x0231, 0x0231, 0x0231, 0x0231, 0x0231, + 0x0231, 0x0231, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x020B, + 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, + 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, 0x020B, + 0x020B, 0x020B, 0x020B, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0207, + 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, + 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, 0x0207, + 0x0207, 0x0207, 0x0207, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101 + }; + + char *addr; + int len; + int i; + bool ret; + ushort bit; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(PARAM); + + //SUBR_get_string_len(PARAM, &addr, &len); + VALUE_get_string(PARAM, &addr, &len); + + if (len == 1) + ret = (test[(unsigned char)*addr] & (1 << ((code & 0xF) - 1))) != 0; + else if (len <= 0) + ret = FALSE; + else + { + bit = 1 << ((code & 0xF) - 1); + ret = TRUE; + for (i = 0; i < len; i++) + { + if ((test[(unsigned char)addr[i]] & bit) == 0) + { + ret = FALSE; + break; + } + } + } + + RELEASE_STRING(PARAM); + + SP--; + SP->type = T_BOOLEAN; + SP->_boolean.value = ret; + SP++; +} + +void SUBR_tr(void) +{ + char *str; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(&PARAM[0]); + + if (SUBR_check_string(PARAM)) + STRING_void_value(RETURN); + else + { + str = STRING_new_temp(&PARAM->_string.addr[PARAM->_string.start], PARAM->_string.len); + + RETURN->type = T_CSTRING; + RETURN->_string.addr = (char *)LOCAL_gettext(str); + RETURN->_string.start = 0; + RETURN->_string.len = strlen(RETURN->_string.addr); + } + + SUBR_LEAVE(); +} + +static void make_hex_char(uchar c) +{ + static const char hex_digit[] = "0123456789ABCDEF"; + + STRING_make_char(hex_digit[c >> 4]); + STRING_make_char(hex_digit[c & 15]); +} + +void SUBR_quote(ushort code) +{ + static void *jump[8] = { + &&__QUOTE, &&__SHELL, &&__HTML, &&__BASE64, &&__URL , &&__ILLEGAL, &&__ILLEGAL, &&__ILLEGAL + }; + + char *str; + int lstr; + int i; + unsigned char c; + char buf[8]; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(&PARAM[0]); + + str = PARAM->_string.addr + PARAM->_string.start; + lstr = PARAM->_string.len; + + code &= 0x7; + + STRING_start_len(lstr); + + goto *jump[code]; + +__QUOTE: + + STRING_make_char('"'); + + for (i = 0; i < lstr; i++) + { + c = str[i]; + //if (c >= ' ' && c <= 126 && c != '\\' && c != '"') + if (c >= ' ' && c != '\\' && c != '"') + STRING_make_char(c); + else + { + STRING_make_char('\\'); + if (c == '\n') + c = 'n'; + else if (c == '\r') + c = 'r'; + else if (c == '\t') + c = 't'; + else if (!(c == '"' || c == '\\')) + { + snprintf(buf, sizeof(buf), "x%02X", c); + STRING_make(buf, 3); + continue; + } + STRING_make_char(c); + } + } + + STRING_make_char('"'); + goto __END; + +__SHELL: + + /*if (!LOCAL_is_UTF8) + { + char *conv; + STRING_conv(&conv, str, lstr, SC_UTF8, LOCAL_encoding, FALSE); + str = conv; + lstr = str ? strlen(str) : 0; + }*/ + + // TODO: The following works with bash, but not with dash! + + STRING_make_char('\''); + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '\'') + { + STRING_make("'\\'", 3); + } + STRING_make_char(c); + /* + if (c == '\n') + STRING_make("$'\\n'", 5); + else if (c == '\r') + STRING_make("$'\\r'", 5); + else if (c == '\t') + STRING_make("$'\\t'", 5); + else if (c < ' ') //|| (c > 126 && !LOCAL_is_UTF8)) + { + snprintf(buf, sizeof(buf), "$'\\x%02X'", c); + STRING_make(buf, 7); + } + else if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || index(".-/_~", c) || c > 126) + STRING_make_char(c); + else + { + STRING_make_char('\\'); + STRING_make_char(c); + } + */ + } + + STRING_make_char('\''); + + goto __END; + +__HTML: + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '&') + STRING_make("&", 5); + else if (c == '<') + STRING_make("<", 4); + else if (c == '>') + STRING_make(">", 4); + else if (c == '"') + STRING_make(""", 6); + else if (c == '\'') + STRING_make("'", 6); + else if (c == 0xC2 && (uchar)str[i + 1] == 0xA0) + { + STRING_make(" ", 6); + i++; + } + else + STRING_make_char(c); + } + + goto __END; + +__BASE64: + { + static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + uchar *in; + char *out = buf; + + for (i = 0; i < (lstr - 2); i += 3) + { + in = (uchar *)&str[i]; + out[0] = base64[in[0] >> 2]; + out[1] = base64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)]; + out[2] = base64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)]; + out[3] = base64[in[2] & 0x3F]; + STRING_make(out, 4); + } + + if (i < lstr) + { + in = (uchar *)&str[i]; + lstr -= i; + out[0] = base64[in[0] >> 2]; + out[1] = base64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)]; + out[2] = (lstr > 1 ? base64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ] : '='); + out[3] = (lstr > 2 ? base64[in[2] & 0x3F] : '='); + STRING_make(out, 4); + } + } + + goto __END; + +__URL: + + // Warning! '/' is not encoded, so that the function is more practical, by supposing that no file url will have '/' in its name. + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || index("-._~,$!/", c)) + STRING_make_char(c); + else + { + STRING_make_char('%'); + make_hex_char(c); + } + } + + goto __END; + +__ILLEGAL: + + THROW_ILLEGAL(); + +__END: + + RETURN->type = T_STRING; + RETURN->_string.addr = STRING_end_temp(); + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(RETURN->_string.addr); + + SUBR_LEAVE(); +} + +static int read_hex_digit(unsigned char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return 0; +} + +void SUBR_unquote(ushort code) +{ + static void *jump[4] = { &&__UNQUOTE, &&__FROM_BASE64, &&__FROM_URL, &&__ILLEGAL }; + + char *str; + int lstr; + int i; + unsigned char c; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(&PARAM[0]); + + str = PARAM->_string.addr + PARAM->_string.start; + lstr = PARAM->_string.len; + + STRING_start_len(lstr); + + goto *jump[code & 0x3]; + +__UNQUOTE: + + if (lstr >= 2 && str[0] == '"' && str[lstr - 1] == '"') + { + str++; + lstr -= 2; + } + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '\\') + { + i++; + if (i >= lstr) + break; + c = str[i]; + + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + else if (c == 'r') + c = '\r'; + else if (c == 'x') + { + if (i >= (lstr - 2)) + break; + + c = (read_hex_digit(str[i + 1]) << 4) + read_hex_digit(str[i + 2]); + i += 2; + } + } + + STRING_make_char(c); + } + + goto __END; + +__FROM_BASE64: + + { + char buf[4]; + unsigned char n = 0; + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c >= 'A' && c <= 'Z') + c = c - 'A'; + else if (c >= 'a' && c <= 'z') + c = c - 'a' + 26; + else if (c >= '0' && c <= '9') + c = c - '0' + 52; + else if (c == '+') + c = 62; + else if (c == '/') + c = 63; + else if (c == '=') + break; + else + continue; + + switch (n & 3) + { + case 0: buf[0] = c << 2; break; + case 1: buf[0] |= c >> 4; buf[1] = c << 4; break; + case 2: buf[1] |= c >> 2; buf[2] = c << 6; break; + case 3: buf[2] |= c; STRING_make(buf, 3); break; + } + n++; + } + + if ((n & 3) > 1) + STRING_make(buf, (n & 3) - 1); + } + + goto __END; + +__FROM_URL: + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '+') + c = ' '; + else if (c == '%') + { + if (i >= (lstr - 2)) + break; + + c = (read_hex_digit(str[i + 1]) << 4) + read_hex_digit(str[i + 2]); + i += 2; + } + + STRING_make_char(c); + } + + goto __END; + +__ILLEGAL: + + THROW_ILLEGAL(); + +__END: + + RETURN->type = T_STRING; + RETURN->_string.addr = STRING_end_temp(); + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(RETURN->_string.addr); + + SUBR_LEAVE(); +} + +void SUBR_swap(ushort code) +{ + char *src, *dst; + int len, i, j; + + if (!(code & 0xFF)) + { + SUBR_move(1); + return; + } + + SUBR_ENTER(); + + if (NPARAM == 2 && (SUBR_get_integer(&PARAM[1]) == GB_BIG_ENDIAN) == EXEC_big_endian) + { + SP--; + return; + } + + if (SUBR_check_string(PARAM)) + STRING_void_value(RETURN); + else + { + len = PARAM->_string.len; + if (len > 0) + { + src = PARAM->_string.addr + PARAM->_string.start; + dst = STRING_new_temp(NULL, PARAM->_string.len); + + for (i = 0, j = len - 1; i < len; i++,j--) + dst[i] = src[j]; + + RETURN->type = T_STRING; + RETURN->_string.addr = dst; + RETURN->_string.start = 0; + RETURN->_string.len = len; + } + } + + SUBR_LEAVE(); +} + + diff --git a/main/gbx/gbx_subr_test.c b/main/gbx/gbx_subr_test.c new file mode 100644 index 00000000..49465729 --- /dev/null +++ b/main/gbx/gbx_subr_test.c @@ -0,0 +1,588 @@ +/*************************************************************************** + + gbx_subr_test.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include + +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_date.h" +#include "gbx_object.h" +#include "gbx_math.h" +#include "gbx_compare.h" + +#define STT_NAME SUBR_case +#define STT_TEST == +#define STT_CASE + +#include "gbx_subr_test_temp.h" + + +void SUBR_bit(ushort code) +{ + static void *jump[16] = { + &&__ERROR, &&__BCLR, &&__BSET, &&__BTST, &&__BCHG, &&__ASL, &&__ASR, &&__ROL, + &&__ROR, &&__LSL, &&__LSR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR + }; + + static int nbits[6] = { 0, 0, 8, 16, 32, 64 }; + + int64_t val; + int bit; + TYPE type; + int n; + bool variant; + + SUBR_ENTER_PARAM(2); + + type = PARAM->type; + + variant = TYPE_is_variant(type); + if (variant) + type = PARAM->_variant.vtype; + + if (type <= T_BOOLEAN || type > T_LONG) + THROW(E_TYPE, "Number", TYPE_get_name(type)); + + VALUE_conv(PARAM, T_LONG); + val = PARAM->_long.value; + + n = nbits[type]; + + bit = SUBR_get_integer(&PARAM[1]); + + if ((bit < 0) || (bit >= n)) + THROW(E_ARG); + + RETURN->type = type; + + goto *jump[code & 0xF]; + +__BCLR: + + val &= ~(1ULL << bit); + goto __END; + +__BSET: + + val |= (1ULL << bit); + goto __END; + +__BTST: + + RETURN->type = T_BOOLEAN; + RETURN->_boolean.value = (val & (1ULL << bit)) ? (-1) : 0; + goto __LEAVE; + +__BCHG: + + val ^= (1ULL << bit); + goto __END; + +__ASL: + + { + static void *asl_jump[6] = { &&__ERROR, &&__ERROR, &&__ASL_BYTE, &&__ASL_SHORT, &&__ASL_INTEGER, &&__ASL_LONG }; + + goto *asl_jump[type]; + + __ASL_BYTE: + val = ((unsigned char)val << bit); + goto __END_BYTE; + + __ASL_SHORT: + val = (((short)val << bit) & 0x7FFF) | (((short)val) & 0x8000); + goto __END_SHORT; + + __ASL_INTEGER: + val = (((int)val << bit) & 0x7FFFFFFF) | (((int)val) & 0x80000000); + goto __END_INTEGER; + + __ASL_LONG: + val = ((val << bit) & 0x7FFFFFFFFFFFFFFFLL) | (val & 0x8000000000000000LL); + goto __END_LONG; + } + +__ASR: + + { + static void *asr_jump[6] = { &&__ERROR, &&__ERROR, &&__ASR_BYTE, &&__ASR_SHORT, &&__ASR_INTEGER, &&__ASR_LONG }; + + goto *asr_jump[type]; + + __ASR_BYTE: + val = ((unsigned char)val >> bit); + goto __END_BYTE; + + __ASR_SHORT: + val = (((short)val >> bit) & 0x7FFF) | (((short)val) & 0x8000); + goto __END_SHORT; + + __ASR_INTEGER: + val = (((int)val >> bit) & 0x7FFFFFFF) | (((int)val) & 0x80000000); + goto __END_INTEGER; + + __ASR_LONG: + val = ((val >> bit) & 0x7FFFFFFFFFFFFFFFLL) | (val & 0x8000000000000000LL); + goto __END_LONG; + } + +__ROL: + + { + static void *rol_jump[6] = { &&__ERROR, &&__ERROR, &&__ROL_BYTE, &&__ROL_SHORT, &&__ROL_INTEGER, &&__ROL_LONG }; + + goto *rol_jump[type]; + + __ROL_BYTE: + val = (val << bit) | (val >> (8 - bit)); + goto __END_BYTE; + + __ROL_SHORT: + val = ((ushort)val << bit) | ((ushort)val >> (16 - bit)); + goto __END_SHORT; + + __ROL_INTEGER: + val = ((uint)val << bit) | ((uint)val >> (32 - bit)); + goto __END_INTEGER; + + __ROL_LONG: + val = ((uint64_t)val << bit) | ((uint64_t)val >> (64 - bit)); + goto __END_LONG; + } + +__ROR: + + { + static void *ror_jump[6] = { &&__ERROR, &&__ERROR, &&__ROR_BYTE, &&__ROR_SHORT, &&__ROR_INTEGER, &&__ROR_LONG }; + + goto *ror_jump[type]; + + __ROR_BYTE: + val = (val >> bit) | (val << (8 - bit)); + goto __END_BYTE; + + __ROR_SHORT: + val = ((ushort)val >> bit) | ((ushort)val << (16 - bit)); + goto __END_SHORT; + + __ROR_INTEGER: + val = ((uint)val >> bit) | ((uint)val << (32 - bit)); + goto __END_INTEGER; + + __ROR_LONG: + val = ((uint64_t)val >> bit) | ((uint64_t)val << (64 - bit)); + goto __END_LONG; + } + +__LSL: + + { + static void *lsl_jump[6] = { &&__ERROR, &&__ERROR, &&__LSL_BYTE, &&__LSL_SHORT, &&__LSL_INTEGER, &&__LSL_LONG }; + + goto *lsl_jump[type]; + + __LSL_BYTE: + val = ((unsigned char)val << bit); + goto __END_BYTE; + + __LSL_SHORT: + val = ((unsigned short)val << bit); + goto __END_SHORT; + + __LSL_INTEGER: + val = ((unsigned int)val << bit); + goto __END_INTEGER; + + __LSL_LONG: + val = ((uint64_t)val << bit); + goto __END_LONG; + } + +__LSR: + + { + static void *lsr_jump[6] = { &&__ERROR, &&__ERROR, &&__LSR_BYTE, &&__LSR_SHORT, &&__LSR_INTEGER, &&__LSR_LONG }; + + goto *lsr_jump[type]; + + __LSR_BYTE: + val = ((unsigned char)val >> bit); + goto __END_BYTE; + + __LSR_SHORT: + val = ((unsigned short)val >> bit); + goto __END_SHORT; + + __LSR_INTEGER: + val = ((unsigned int)val >> bit); + goto __END_INTEGER; + + __LSR_LONG: + val = ((uint64_t)val >> bit); + goto __END_LONG; + } + +__ERROR: + + THROW_ILLEGAL(); + +__END: + + { + static void *end_jump[6] = { &&__ERROR, &&__ERROR, &&__END_BYTE, &&__END_SHORT, &&__END_INTEGER, &&__END_LONG }; + + goto *end_jump[type]; + + __END_BYTE: + RETURN->_integer.value = (unsigned int)val & 0xFF; + goto __END_VARIANT; + + __END_SHORT: + RETURN->_integer.value = (int)(short)val; + goto __END_VARIANT; + + __END_INTEGER: + RETURN->_integer.value = (int)val; + goto __END_VARIANT; + + __END_LONG: + RETURN->_long.value = val; + goto __END_VARIANT; + } + +__END_VARIANT: + + if (variant) + VALUE_conv_variant(RETURN); + +__LEAVE: + + SUBR_LEAVE(); +} + + +void SUBR_if(ushort code) +{ + int i; + unsigned char test; + TYPE type; + + SUBR_ENTER_PARAM(3); + + VALUE_conv_boolean(PARAM); + i = PARAM->_boolean.value ? 1 : 2; + + test = code & 0x1F; + + if (!test) + { + type = PARAM[1].type; + if (PARAM[2].type == type && type <= T_VARIANT) + { + *PC |= 0x1F; + } + else + { + type = SUBR_check_good_type(&PARAM[1], 2); + if (TYPE_is_object(type)) + type = T_OBJECT; + *PC |= (unsigned char)type; + + VALUE_conv(&PARAM[i], type); + } + } + else if (test != 0x1F) + { + VALUE_conv(&PARAM[i], (TYPE)test); + } + + *PARAM = PARAM[i]; + RELEASE(&PARAM[3 - i]); + SP -= 2; +} + + +void SUBR_choose(ushort code) +{ + int val; + + SUBR_ENTER(); + + VALUE_conv_integer(PARAM); + val = PARAM->_integer.value; + + if (val >= 1 && val < NPARAM) + { + VALUE_conv_variant(&PARAM[val]); + *RETURN = PARAM[val]; + } + else + { + RETURN->type = T_VARIANT; + RETURN->_variant.vtype = T_NULL; + } + + SUBR_LEAVE(); +} + + +void SUBR_near(void) +{ + int result; + + SUBR_ENTER_PARAM(2); + + VALUE_conv_string(&PARAM[0]); + VALUE_conv_string(&PARAM[1]); + + //result = STRING_comp_value_ignore_case(&PARAM[0], &PARAM[1]) ? -1 : 0; + result = STRING_equal_ignore_case(PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[0]._string.len, PARAM[1]._string.addr + PARAM[1]._string.start, PARAM[1]._string.len) ? -1 : 0; + + RELEASE_STRING(&PARAM[0]); + RELEASE_STRING(&PARAM[1]); + + PARAM->type = T_BOOLEAN; + PARAM->_boolean.value = result; + + SP--; +} + + +void SUBR_strcomp(ushort code) +{ + int mode = GB_COMP_BINARY; + /*char *s1, *s2; + int l1, l2; + int ret;*/ + + SUBR_ENTER(); + + VALUE_conv_string(&PARAM[0]); + VALUE_conv_string(&PARAM[1]); + + if (NPARAM == 3) + mode = SUBR_get_integer(&PARAM[2]); + + RETURN->_integer.type = T_INTEGER; + RETURN->_integer.value = (*COMPARE_get_string_func(mode))(PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[0]._string.len, PARAM[1]._string.addr + PARAM[1]._string.start, PARAM[1]._string.len, mode & GB_COMP_NOCASE, FALSE); + + SUBR_LEAVE(); +} + + +void SUBR_is(ushort code) +{ + VALUE *P1 = SP - 2; + VALUE *P2 = SP - 1; + void *object; + CLASS *klass; + bool res; + + VALUE_conv(P1, T_OBJECT); + object = P1->_object.object; + klass = P2->_class.class; + + if (!object) + res = FALSE; + else + res = (OBJECT_class(object) == klass || CLASS_inherits(OBJECT_class(object), klass)); + + OBJECT_UNREF(object); + + P1->type = T_BOOLEAN; + P1->_boolean.value = -(res ^ (code & 1)); + SP--; +} + + +void SUBR_min_max(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__MIN_BOOLEAN, &&__MIN_BYTE, &&__MIN_SHORT, &&__MIN_INTEGER, &&__MIN_LONG, &&__MIN_SINGLE, &&__MIN_FLOAT, + &&__MIN_DATE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &&__VARIANT, &&__MAX_BOOLEAN, &&__MAX_BYTE, &&__MAX_SHORT, &&__MAX_INTEGER, &&__MAX_LONG, &&__MAX_SINGLE, &&__MAX_FLOAT, + &&__MAX_DATE, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }; + + TYPE type; + VALUE *P1, *P2; + void *jump_end; + + P1 = SP - 2; + P2 = P1 + 1; + + jump_end = &&__END; + type = code & 0x0F; + goto *jump[code & 0x1F]; + +__MIN_BOOLEAN: +__MIN_BYTE: +__MIN_SHORT: +__MIN_INTEGER: + + P1->type = type; + + if (P2->_integer.value < P1->_integer.value) + P1->_integer.value = P2->_integer.value; + + goto *jump_end; + +__MAX_BOOLEAN: +__MAX_BYTE: +__MAX_SHORT: +__MAX_INTEGER: + + P1->type = type; + + if (P2->_integer.value > P1->_integer.value) + P1->_integer.value = P2->_integer.value; + + goto *jump_end; + +__MIN_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + if (P2->_long.value < P1->_long.value) + P1->_long.value = P2->_long.value; + + goto *jump_end; + +__MAX_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + if (P2->_long.value > P1->_long.value) + P1->_long.value = P2->_long.value; + + goto *jump_end; + +__MIN_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + if (P2->_single.value < P1->_single.value) + P1->_single.value = P2->_single.value; + + goto *jump_end; + +__MAX_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + if (P2->_single.value > P1->_single.value) + P1->_single.value = P2->_single.value; + + goto *jump_end; + +__MIN_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + if (P2->_float.value < P1->_float.value) + P1->_float.value = P2->_float.value; + + goto *jump_end; + +__MAX_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + if (P2->_float.value > P1->_float.value) + P1->_float.value = P2->_float.value; + + goto *jump_end; + +__MIN_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + if (DATE_comp_value(P1, P2) == 1) + { + P1->_date.date = P2->_date.date; + P1->_date.time = P2->_date.time; + } + + goto *jump_end; + +__MAX_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + if (DATE_comp_value(P1, P2) == -1) + { + P1->_date.date = P2->_date.date; + P1->_date.time = P2->_date.time; + } + + goto *jump_end; + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + code = type + ((code >> 8) == CODE_MAX) * 16; + *PC |= code; + goto *jump[code]; + } + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + jump_end = &&__VARIANT_END; + code = type + ((code >> 8) == CODE_MAX) * 16; + goto *jump[code]; + } + + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number or date", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + + SP--; +} diff --git a/main/gbx/gbx_subr_test_temp.h b/main/gbx/gbx_subr_test_temp.h new file mode 100644 index 00000000..568ed337 --- /dev/null +++ b/main/gbx/gbx_subr_test_temp.h @@ -0,0 +1,333 @@ +/*************************************************************************** + + gbx_subr_test_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef STT_INEQUALITY + +void STT_NAME(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, + &&__NULL, + &&__OBJECT + }; + + TYPE type; + VALUE *P1, *P2; + bool result, variant; + + P1 = SP - 2; + P2 = P1 + 1; + + variant = FALSE; + type = code & 0x1F; + goto *jump[type]; + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_variant(P1->type)) + { + #ifdef STT_CASE + TEMP = *P1; + P1 = &TEMP; + #endif + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + if (variant) + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + + if (!variant) + *PC |= type; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value STT_TEST P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value STT_TEST P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + #ifdef STT_NEAR + result = P1->_date.date STT_TEST P2->_date.date; + #else + result = (DATE_comp_value(P1, P2) STT_TEST 0); + #endif + goto __END; + +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0 STT_TEST 1; + else + #ifdef STT_NEAR + result = (STRING_equal_ignore_case(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) STT_TEST 1); + #else + result = (STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len) STT_TEST 1); + #endif + + goto __END_RELEASE; + +#ifdef STT_NEAR + +__SINGLE: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = fabs(P1->_float.value - P2->_float.value) <= 1E-6 * fabs(P1->_float.value + P2->_float.value); + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = fabs(P1->_float.value - P2->_float.value) <= 1E-12 * fabs(P1->_float.value + P2->_float.value); + goto __END; + +#else + +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value STT_TEST P2->_float.value; + goto __END; + +#endif + +__OBJECT: + + result = OBJECT_comp_value(P1, P2) STT_TEST TRUE; + goto __END_RELEASE; + +__NULL: + + result = VALUE_is_null(P1->type == T_NULL ? P2 : P1) STT_TEST TRUE; + goto __END_RELEASE; + +__ERROR: + + THROW(E_TYPE, "Number or Date", TYPE_get_name(type)); + +__END_RELEASE: + +#ifdef STT_CASE + RELEASE(P2); +#else + RELEASE(P1); + RELEASE(P2); +#endif + +__END: + + #ifdef STT_CASE + + P2->type = T_BOOLEAN; + P2->_boolean.value = result ? -1 : 0; + + #else + + P1->type = T_BOOLEAN; + P1->_boolean.value = result ? -1 : 0; + + SP--; + + #endif + +} + +#else /* inequality tests */ + +void STT_NAME(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, + &&__NULL, &&__ERROR + }; + + TYPE type, typem; + VALUE *P1, *P2; + bool result, variant; + + P1 = SP - 2; + P2 = P1 + 1; + + variant = FALSE; + type = code & 0x1F; + goto *jump[type]; + +__VARIANT: + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + + if (!variant) + *PC |= type; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value STT_TEST P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value STT_TEST P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = (DATE_comp_value(P1, P2) STT_TEST 0); + goto __END; + +__NULL: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + +__STRING: + + result = (STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) STT_TEST 0); + + goto __END_RELEASE; + +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value STT_TEST P2->_float.value; + goto __END; + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = result ? -1 : 0; + + SP--; +} + +#endif + + +#ifdef STT_INEQUALITY +#undef STT_INEQUALITY +#endif + +#undef STT_NAME +#undef STT_TEST + +#ifdef STT_NO_OBJECT +#undef STT_NO_OBJECT +#endif + +#ifdef STT_CASE +#undef STT_CASE +#endif + +#ifdef STT_NULL +#undef STT_NULL +#endif + +#ifdef STT_NEAR +#undef STT_NEAR +#endif + + diff --git a/main/gbx/gbx_subr_time.c b/main/gbx/gbx_subr_time.c new file mode 100644 index 00000000..e0d8649e --- /dev/null +++ b/main/gbx/gbx_subr_time.c @@ -0,0 +1,287 @@ +/*************************************************************************** + + gbx_subr_time.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" + +#include +#include + +#include "gb_error.h" +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_local.h" +#include "gbx_date.h" + + +void SUBR_timer(void) +{ + double result = 0.0; + + DATE_timer(&result, TRUE); + + SP->type = T_FLOAT; + SP->_float.value = result; + SP++; +} + + +void SUBR_now(void) +{ + DATE_now(SP); + SP++; +} + + +void SUBR_year(ushort code) +{ + DATE_SERIAL *date; + int val; + + SUBR_ENTER_PARAM(1); + + VALUE_conv(PARAM, T_DATE); + + date = DATE_split(PARAM); + + switch(code & 0xF) + { + case 1: val = date->year; break; + case 2: val = date->month; break; + case 3: val = date->day; break; + case 4: val = date->hour; break; + case 5: val = date->min; break; + case 6: val = date->sec; break; + case 7: val = date->weekday; break; + case 8: val = date->msec; break; + default: val = 0; + } + + PARAM->type = T_INTEGER; + PARAM->_integer.value = val; + + #if 0 + SUBR_LEAVE() /* Not necessary */ + #endif +} + + +void SUBR_date(ushort code) +{ + DATE_SERIAL date; + + SUBR_ENTER(); + + if (NPARAM <= 1) + { + if (NPARAM == 0) + DATE_now(PARAM); + else + VALUE_conv(PARAM, T_DATE); + + date = *DATE_split(PARAM); + date.hour = 0; + date.min = 0; + date.sec = 0; + date.msec = 0; + } + else if (NPARAM >= 3) + { + VALUE_conv_integer(PARAM); + VALUE_conv_integer(&PARAM[1]); + VALUE_conv_integer(&PARAM[2]); + + CLEAR(&date); + date.year = PARAM->_integer.value; + date.month = PARAM[1]._integer.value; + date.day = PARAM[2]._integer.value; + + if (NPARAM >= 4) + { + VALUE_conv_integer(&PARAM[3]); + date.hour = PARAM[3]._integer.value; + } + + if (NPARAM >= 5) + { + VALUE_conv_integer(&PARAM[4]); + date.min = PARAM[4]._integer.value; + } + + if (NPARAM >= 6) + { + VALUE_conv_integer(&PARAM[5]); + date.sec = PARAM[5]._integer.value; + } + + if (NPARAM >= 7) + { + VALUE_conv_integer(&PARAM[6]); + date.msec = PARAM[6]._integer.value; + } + } + else + THROW(E_NEPARAM); + + if (DATE_make(&date, RETURN)) + THROW(E_DATE); + + SUBR_LEAVE(); +} + + +void SUBR_time(ushort code) +{ + DATE_SERIAL date; + + SUBR_ENTER(); + + if (NPARAM <= 1) + { + if (NPARAM == 0) + DATE_now(PARAM); + else + VALUE_conv(PARAM, T_DATE); + + date = *DATE_split(PARAM); + date.year = 0; + } + else if (NPARAM >= 3) + { + VALUE_conv_integer(PARAM); + VALUE_conv_integer(&PARAM[1]); + VALUE_conv_integer(&PARAM[2]); + + CLEAR(&date); + date.hour = PARAM->_integer.value; + date.min = PARAM[1]._integer.value; + date.sec = PARAM[2]._integer.value; + if (NPARAM == 4) + { + VALUE_conv_integer(&PARAM[3]); + date.msec = PARAM[3]._integer.value; + } + } + else + THROW(E_NEPARAM); + + if (DATE_make(&date, RETURN)) + THROW(E_DATE); + + SUBR_LEAVE(); +} + + +void SUBR_date_op(ushort code) +{ + SUBR_ENTER_PARAM(3); + + switch (code & 0xF) + { + case 0: /* DateAdd */ + + VALUE_conv(PARAM, T_DATE); + *RETURN = *PARAM; + DATE_add(RETURN, SUBR_get_integer(&PARAM[1]), SUBR_get_integer(&PARAM[2])); + + break; + + case 1: /* DateDiff */ + + VALUE_conv(PARAM, T_DATE); + VALUE_conv(&PARAM[1], T_DATE); + + /* Dates are inverted! */ + RETURN->_integer.value = DATE_diff(&PARAM[1], PARAM, SUBR_get_integer(&PARAM[2])); + RETURN->type = T_INTEGER; + + break; + } + + SUBR_LEAVE(); +} + + +void SUBR_week(ushort code) +{ + bool plain = FALSE; + int start = LOCAL_get_first_day_of_week(); + DATE_SERIAL ds; + VALUE date, first; + int day, n; + + SUBR_ENTER(); + + if (NPARAM >= 1) + { + VALUE_conv(PARAM, T_DATE); + date = *PARAM; + + if (NPARAM >= 2) + { + start = SUBR_get_integer(&PARAM[1]); + if (start < 0 || start > 6) + THROW(E_ARG); + + if (NPARAM == 3) + plain = SUBR_get_boolean(&PARAM[2]); + } + } + else + DATE_now(&date); + + /* Split it */ + ds = *DATE_split(&date); + /* Set to 1 Jan of the current year */ + ds.month = 1; + ds.day = 1; + ds.hour = 0; + ds.min = 0; + ds.sec = 0; + /* Convert to date & time */ + DATE_make(&ds, &first); + + /* Get the weekday of this 1 Jan */ + day = DATE_split(&first)->weekday; + + /* number of beginning days to ignore */ + + n = 0; + while (day != start) + { + day++; + if (day > 6) + day = 0; + n++; + } + + if (!plain) + { + if (n >= 4) + n -= 7; + } + + RETURN->type = T_INTEGER; + RETURN->_integer.value = (DATE_diff(&date, &first, DP_DAY) - n + 7) / 7; + + SUBR_LEAVE(); +} diff --git a/main/gbx/gbx_test.c b/main/gbx/gbx_test.c new file mode 100644 index 00000000..58a9c179 --- /dev/null +++ b/main/gbx/gbx_test.c @@ -0,0 +1,93 @@ +/*************************************************************************** + + gbx_test.c + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_TEST_C + +#include "gb_common.h" +#include "gbx_class.h" +#include "gbx_api.h" +#include "gbx_project.h" +#include "gbx_exec.h" +#include "gbx_test.h" + +static CLASS_DESC_METHOD *find_method(const char *name) +{ + CLASS_DESC_METHOD *method = (CLASS_DESC_METHOD *)CLASS_get_symbol_desc_kind(PROJECT_class, name, CD_STATIC_METHOD, 0, T_ANY); + if (!method) + ERROR_panic("Test.%s() method unavailable", name); + return method; +} + +static void error_test_run(CARRAY *tests) +{ + OBJECT_UNREF(tests); +} + +void TEST_run(const char *test_list) +{ + CLASS_DESC_METHOD *startup; + CARRAY *tests; + int i; + char *test; + char *p; + char *name; + CLASS *class; + + + if (!test_list || !*test_list) + { + EXEC_public_desc(PROJECT_class, NULL, find_method("_list"), 0); + return; + } + + startup = find_method("_add"); + + tests = STRING_split(test_list, strlen(test_list), ",", 1, NULL, 0, TRUE, FALSE); + + ON_ERROR_1(error_test_run, tests) + { + for (i = 0; i < tests->count; i++) + { + test = *(char **)CARRAY_get_data_unsafe(tests, i); + p = index(test, '.'); + if (p) + name = STRING_new_temp(test, p - test); + else + name = test; + + class = CLASS_find(name); + CLASS_load(class); + if (!class->is_test) + continue; + + GB_Push(2, T_OBJECT, class, T_STRING, test, STRING_length(test)); + EXEC_public_desc(PROJECT_class, NULL, startup, 2); + } + } + END_ERROR + + OBJECT_UNREF(tests); + + EXEC_public_desc(PROJECT_class, NULL, find_method("_run"), 0); +} + diff --git a/main/gbx/gbx_test.h b/main/gbx/gbx_test.h new file mode 100644 index 00000000..d483cc59 --- /dev/null +++ b/main/gbx/gbx_test.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + gbx_test.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_TEST_H +#define __GBX_TEST_H + +#include "gbx_c_array.h" +#include "gbx_split.h" + +void TEST_run(const char *test_list); + +#endif diff --git a/main/gbx/gbx_type.c b/main/gbx/gbx_type.c new file mode 100644 index 00000000..b46fc5d2 --- /dev/null +++ b/main/gbx/gbx_type.c @@ -0,0 +1,351 @@ +/*************************************************************************** + + gbx_type.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_TYPE_C + +#include + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_limit.h" +#include "gbx_variant.h" +#include "gbx_class.h" +#include "gambas.h" + +#include "gbx_type.h" + + +void *TYPE_joker = NULL; + +const size_t TYPE_sizeof_memory_tab[16] = { 0, 1, 1, 2, 4, 8, 4, 8, 8, sizeof(void *), sizeof(void *), sizeof(void *), sizeof(VARIANT), 0, 0, 0 }; + + +// Needed size for storing a class global variable + +size_t TYPE_sizeof(TYPE type) +{ + static size_t size[16] = { 0, 4, 4, 4, 4, 8, 8, 8, 8, sizeof(void *), sizeof(void *), sizeof(void *), sizeof(VARIANT), 0, 0, 0 }; + + if (TYPE_is_object(type)) + return sizeof(void *); + else + return size[type]; +} + + +const char *TYPE_get_name(TYPE type) +{ + static const char *name[17] = + { + "Void", + "Boolean", + "Byte", + "Short", + "Integer", + "Long", + "Single", + "Float", + "Date", + "String", + "String", + "Pointer", + "Variant", + "Function", + "Class", + "Null", + "Object" + }; + + + if (TYPE_is_pure_object(type)) + return ((CLASS *)type)->name; + else + return name[type]; +} + + +const char *TYPE_to_string(TYPE type) +{ + switch (type) + { + case T_BOOLEAN: return "b"; + case T_BYTE: return "c"; + case T_SHORT: return "h"; + case T_INTEGER: return "i"; + case T_LONG: return "l"; + case T_SINGLE: return "g"; + case T_FLOAT: return "f"; + case T_DATE: return "d"; + case T_STRING: return "s"; + case T_POINTER: return "p"; + case T_VARIANT: return "v"; + case T_OBJECT: return "o"; + + default: + if (TYPE_is_pure_object(type)) + return ((CLASS *)type)->name; + else + return ""; + + } +} + + +void TYPE_signature_length(const char *sign, char *len_min, char *len_max, char *var) +{ + char c; + int len = 0; + bool brace = FALSE; + + *len_min = 0; + *len_max = 0; + *var = 0; + + if (sign == NULL) + return; + + for(;;) + { + c = *sign++; + if (c == 0) + break; + + if (c == '.') + { + *var = 1; + break; + } + + if (c == '[') + { + brace = TRUE; + continue; + } + + + if (c == '\'' || c == '(' || c == '<') + { + for(;;) + { + c = *sign; + if (c == 0) + break; + sign++; + if (c == '\'' || c == ')' || c == '>') + break; + } + continue; + } + + if (c == ']') + continue; + + if (!brace) + *len_min = len + 1; + + if (islower(c)) + { + len++; + continue; + } + + len++; + + for(;;) + { + c = *sign; + if (c == 0) + break; + sign++; + if (c == ';') + break; + } + } + + *len_max = len; +} + + +TYPE TYPE_from_string(const char **ptype) +{ + const char *start; + const char *type; + bool quote = FALSE; + + for(;;) + { + type = *ptype; + + if (type == NULL || *type == 0) + return T_VOID; + + (*ptype)++; + + if (*type == '\'') + { + quote = !quote; + continue; + } + + if (*type == ')' || *type == '>') + { + quote = FALSE; + continue; + } + + if (quote) + continue; + + if (*type == '(' || *type == '<') + { + quote = TRUE; + continue; + } + + //if (index("[]<>", *type) == NULL) + if (!(*type == '[' || *type == ']' || *type == '<' || *type == '>')) + break; + } + + switch(*type) + { + case 'b': return T_BOOLEAN; + case 'c': return T_BYTE; + case 'h': return T_SHORT; + case 'i': return T_INTEGER; + case 'l': return T_LONG; + case 'g': return T_SINGLE; + case 'f': return T_FLOAT; + case 'd': return T_DATE; + case 's': return T_STRING; + case 'v': return T_VARIANT; + case 'o': return T_OBJECT; + case 'p': return T_POINTER; + + default: + + start = type; + + while (*type != ';' && *type != 0 && *type != '(') + type++; + + if (*start == '*') + { + strcpy(COMMON_buffer, ((CLASS *)TYPE_joker)->name); + start++; + if (type > start) + strncat(COMMON_buffer, start, type - start); + } + else if (*start && start[1] == '*') + { + COMMON_buffer[0] = *start; + strcpy(&COMMON_buffer[1], ((CLASS *)TYPE_joker)->name); + start += 2; + if (type > start) + strncat(COMMON_buffer, start, type - start); + } + else + { + memcpy(COMMON_buffer, start, type - start); + COMMON_buffer[type - start] = 0; + } + + *ptype = (char *)type + 1; + + // Template classes search their symbols locally first + if (TYPE_joker) + return (TYPE)CLASS_find(NULL); + else + return (TYPE)CLASS_find_global(NULL); + } +} + + +TYPE *TYPE_transform_signature(TYPE **signature, const char *sign, int npmax) +{ + TYPE *tsign; + int i; + + //fprintf(stderr, "TYPE_transform_signature: %s\n", sign); + + tsign = *signature; + + for (i = 0; i < npmax; i++) + { + tsign[i] = TYPE_from_string(&sign); + //fprintf(stderr, "%p ", (void *)tsign[i]); + } + //fputc('\n', stderr); + + *signature += npmax; + + return tsign; +} + +bool TYPE_are_compatible(TYPE type, TYPE ptype) +{ + if (type == ptype) + return TRUE; + + if (!TYPE_are_objects(type, ptype)) + return FALSE; + + if (ptype == T_OBJECT) + return TRUE; + + if (type == T_OBJECT) + return FALSE; + + if (CLASS_inherits((CLASS *)type, (CLASS *)ptype)) + return TRUE; + + return FALSE; +} + +bool TYPE_compare_signature(TYPE *sign1, int np1, TYPE *sign2, int np2, bool check_compat) +{ + int i; + + if (np1 != np2) + return TRUE; + + if (check_compat) + { + for (i = 0; i < np1; i++) + { + if (!TYPE_are_compatible(sign1[i], sign2[i])) + return TRUE; + } + } + else + { + for (i = 0; i < np1; i++) + { + if (sign1[i] != sign2[i]) + return TRUE; + } + } + + return FALSE; +} + diff --git a/main/gbx/gbx_type.h b/main/gbx/gbx_type.h new file mode 100644 index 00000000..d1fcbbc9 --- /dev/null +++ b/main/gbx/gbx_type.h @@ -0,0 +1,99 @@ +/*************************************************************************** + + gbx_type.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_TYPE_H +#define __GBX_TYPE_H + +#include "gb_type_common.h" + +/* Types du compilateur */ + +#define MAX_TYPE 16 + +typedef + struct { + unsigned char flag; + unsigned char id; + short value; + } + CTYPE; + +#define CTYPE_is_static(type) ((type).flag & TF_STATIC) +#define CTYPE_is_public(type) ((type).flag & TF_PUBLIC) +#define CTYPE_get_kind(type) ((type).flag & 0x7) + +// If type > T_OBJECT, then type is a pointer to the class + +typedef + uintptr_t TYPE; + +typedef + void (*TYPE_FUNC)(); + +typedef + TYPE_FUNC TYPE_JUMP[T_OBJECT]; + + +#ifndef __GBX_TYPE_C +EXTERN void *TYPE_joker; +EXTERN const size_t TYPE_sizeof_memory_tab[]; +#endif + +#define TYPE_is_void(type) ((type) == T_VOID) +#define TYPE_is_null(type) ((type) == T_NULL) +#define TYPE_is_object(type) ((type) >= T_OBJECT) +#define TYPE_is_object_null(type) ((type) >= T_NULL) +#define TYPE_is_pure_object(type) ((type) > T_OBJECT) +#define TYPE_is_boolean(type) ((type) == T_BOOLEAN) +#define TYPE_is_integer(type) ((type) >= T_BOOLEAN && (type) <= T_INTEGER) +#define TYPE_is_integer_long(type) ((type) >= T_BOOLEAN && (type) <= T_LONG) +#define TYPE_is_long(type) ((type) == T_LONG) +#define TYPE_is_single(type) ((type) == T_SINGLE) +#define TYPE_is_float(type) ((type) == T_FLOAT) +#define TYPE_is_variant(type) ((type) == T_VARIANT) +#define TYPE_is_number(type) ((type) >= T_BOOLEAN && (type) <= T_FLOAT) +#define TYPE_is_number_date(type) ((type) >= T_BOOLEAN && (type) <= T_DATE) +#define TYPE_is_string(type) ((type) == T_STRING || (type) == T_CSTRING) +#define TYPE_is_function(type) ((type) == T_FUNCTION) +#define TYPE_is_pointer(type) ((type) == T_POINTER) + +#define TYPE_are_objects(_t1, _t2) (TYPE_is_object(_t1) && TYPE_is_object(_t2)) +//#define TYPE_are_not_objects(_t1, _t2) (((_t1) | (_t2)) < T_OBJECT) + +size_t TYPE_sizeof(TYPE type); +#define TYPE_sizeof_memory(_type) (TYPE_is_object(_type) ? sizeof(void *) : TYPE_sizeof_memory_tab[_type]) +#define TYPE_is_value(_type) (TYPE_is_object(_type) || TYPE_is_null(_type) || TYPE_sizeof_memory_tab[_type] > 0) + +// for CLASS_get_symbol_desc_kind() +#define T_ANY ((TYPE)-1) + +const char *TYPE_get_name(TYPE type); + +TYPE TYPE_from_string(const char **ptype); +const char *TYPE_to_string(TYPE type); +TYPE *TYPE_transform_signature(TYPE **signature, const char *sign, int nparam); +void TYPE_signature_length(const char *sign, char *len_min, char *len_max, char *var); +bool TYPE_are_compatible(TYPE type, TYPE ptype); +bool TYPE_compare_signature(TYPE *sign1, int np1, TYPE *sign2, int np2, bool check_compat); + +#endif diff --git a/main/gbx/gbx_value.c b/main/gbx/gbx_value.c new file mode 100644 index 00000000..b477d2d2 --- /dev/null +++ b/main/gbx/gbx_value.c @@ -0,0 +1,2194 @@ +/*************************************************************************** + + gbx_value.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_VALUE_C + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_overflow.h" + +#include "gbx_math.h" +#include "gbx_type.h" + +#include "gbx_c_array.h" +#include "gbx_string.h" +#include "gbx_number.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_date.h" +#include "gbx_struct.h" +#include "gbx_exec.h" +#include "gbx_local.h" +#include "gb_common_buffer.h" +#include "gbx_extern.h" + +#include "gbx_value.h" + +#if 0 +static bool unknown_function(VALUE *value) +{ + if (value->_function.kind == FUNCTION_UNKNOWN) + { + EXEC_unknown_property = TRUE; + EXEC_unknown_name = CP->load->unknown[value->_function.index]; + + EXEC_special(SPEC_UNKNOWN, value->_function.class, value->_function.object, 0, FALSE); + + //object = value->_function.object; + OBJECT_UNREF(value->_function.object); + + SP--; + //*val = *SP; + COPY_VALUE(value, SP); + return TRUE; + } + else + return FALSE; +} +#endif + +void THROW_TYPE(TYPE wanted, TYPE got) +{ + THROW(E_TYPE, TYPE_get_name(wanted), TYPE_get_name(got)); +} + +static void undo_variant(VALUE *value) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__CSTRING, &&__POINTER, &&__VOID, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + TYPE type = value->_variant.vtype; + + //if (index != T_NULL) + // VALUE_read(value, &value->_variant.value, value->_variant.vtype); + + value->type = type; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + value->_boolean.value = value->_variant.value._boolean ? -1 : 0; + return; + +__BYTE: + + value->_byte.value = value->_variant.value._byte; + return; + +__SHORT: + + value->_short.value = value->_variant.value._short; + return; + +__INTEGER: + + value->_integer.value = value->_variant.value._integer; + return; + +__LONG: + + value->_long.value = value->_variant.value._long; + return; + +__SINGLE: + + value->_single.value = value->_variant.value._single; + return; + +__FLOAT: + + value->_float.value = value->_variant.value._float; + return; + +__DATE: + + // It works, as the normal date field is before the variant date field! + value->_date.date = value->_variant.value._date.date; + value->_date.time = value->_variant.value._date.time; + return; + +__STRING: + + { + char *str = value->_variant.value._string; + + value->type = T_STRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = STRING_length(str); + + return; + } + +__CSTRING: + + { + char *str = value->_variant.value._string; + + value->type = T_CSTRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = strlen(str); + + return; + } + +__OBJECT: + + value->_object.object = value->_variant.value._object; + return; + +__POINTER: + + value->_pointer.value = value->_variant.value._pointer; + return; + +__CLASS: // Is it useful for variants ? + + value->_class.class = value->_variant.value._object; + value->_class.super = NULL; + return; + +__NULL: + return; + +__VOID: +__FUNCTION: + + ERROR_panic("Bad type (%d) for undo_variant", type); +} + + +static void VALUE_put(VALUE *value, void *addr, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + VALUE_conv(value, type); + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + *((unsigned char *)addr) = (value->_boolean.value != 0 ? 255 : 0); + return; + +__BYTE: + + *((unsigned char *)addr) = (unsigned char)(value->_byte.value); + return; + +__SHORT: + + *((short *)addr) = (short)(value->_short.value); + return; + +__INTEGER: + + *((int *)addr) = value->_integer.value; + return; + +__LONG: + + *((int64_t *)addr) = value->_long.value; + return; + +__SINGLE: + + *((float *)addr) = value->_single.value; + return; + +__FLOAT: + + *((double *)addr) = value->_float.value; + return; + +__DATE: + + /* Inverted, if value ~= addr */ + + ((int *)addr)[1] = value->_date.time; + ((int *)addr)[0] = value->_date.date; + return; + +/*__STRING: + + ((int *)addr)[0] = (int)(value->_string.addr + value->_string.start); + ((int *)addr)[1] = value->_string.len; + return;*/ + +__POINTER: + + *((void **)addr) = value->_pointer.value; + return; + +__OBJECT: + + *((void **)addr) = value->_object.object; + return; + +__VARIANT: + + *((VARIANT *)addr) = *((VARIANT *)&value->_variant.vtype); + return; + +__CLASS: + + *((void **)addr) = value->_class.class; + return; + +__VOID: +__FUNCTION: +__NULL: +__STRING: + + ERROR_panic("Bad type (%d) for VALUE_put", type); +} + + +/* This function must keep the datatype, as it is used for initializing local variables */ + +void VALUE_default(VALUE *value, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + value->type = type; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + value->_integer.value = 0; + return; + +__LONG: + + value->_long.value = 0; + return; + +__SINGLE: + value->_single.value = 0; + return; + +__FLOAT: + value->_float.value = 0; + return; + +__STRING: + value->_string.addr = NULL; + value->_string.start = 0; + value->_string.len = 0; + return; + +__VARIANT: + value->_variant.vtype = T_NULL; + return; + +__POINTER: + value->_pointer.value = NULL; + return; + +__DATE: + value->_date.date = 0; + value->_date.time = 0; + return; + +__VOID: + return; + +__OBJECT: + value->_object.class = (CLASS *)type; + value->_object.object = NULL; + return; + +__FUNCTION: +__CLASS: +__NULL: + ERROR_panic("VALUE_default: Unknown default type"); +} + + +void VALUE_convert(VALUE *value, TYPE type) +{ + static const void *jump[16][16] = + { + /* ,------> void b c h i l g f d cs s p v func class n */ + // | + /* void */ { &&__OK, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, }, + /* b */ { &&__N, &&__OK, &&__b2c, &&__b2h, &&__TYPE, &&__b2l, &&__b2g, &&__b2f, &&__N, &&__b2s, &&__b2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* c */ { &&__N, &&__c2b, &&__OK, &&__c2h, &&__TYPE, &&__c2l, &&__c2g, &&__c2f, &&__c2d, &&__c2s, &&__c2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* h */ { &&__N, &&__h2b, &&__h2c, &&__OK, &&__TYPE, &&__h2l, &&__h2g, &&__h2f, &&__h2d, &&__h2s, &&__h2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* i */ { &&__N, &&__i2b, &&__i2c, &&__i2h, &&__OK, &&__i2l, &&__i2g, &&__i2f, &&__i2d, &&__i2s, &&__i2s, &&__i2p, &&__2v, &&__N, &&__N, &&__N, }, + /* l */ { &&__N, &&__l2b, &&__l2c, &&__l2h, &&__l2i, &&__OK, &&__l2g, &&__l2f, &&__l2d, &&__l2s, &&__l2s, &&__l2p, &&__2v, &&__N, &&__N, &&__N, }, + /* g */ { &&__N, &&__g2b, &&__g2c, &&__g2h, &&__g2i, &&__g2l, &&__OK, &&__g2f, &&__g2d, &&__g2s, &&__g2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* f */ { &&__N, &&__f2b, &&__f2c, &&__f2h, &&__f2i, &&__f2l, &&__f2g, &&__OK, &&__f2d, &&__f2s, &&__f2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* d */ { &&__N, &&__d2b, &&__d2c, &&__d2h, &&__d2i, &&__d2l, &&__d2g, &&__d2f, &&__OK, &&__d2s, &&__d2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* cs */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__s2p, &&__s2v, &&__N, &&__N, &&__N, }, + /* s */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__s2p, &&__s2v, &&__N, &&__N, &&__N, }, + /* p */ { &&__N, &&__p2b, &&__N, &&__N, &&__p2i, &&__p2l, &&__N, &&__N, &&__N, &&__p2s, &&__p2s, &&__OK, &&__2v, &&__N, &&__N, &&__N, }, + /* v */ { &&__N, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__OK, &&__N, &&__v2, &&__v2, }, + /* func */ { &&__N, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__F2p, &&__func, &&__OK, &&__N, &&__func, }, + /* class */ { &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__2v, &&__N, &&__OK, &&__N, }, + /* null */ { &&__N, &&__n2b, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__n2d, &&__n2s, &&__n2s, &&__n2p, &&__2v, &&__N, &&__N, &&__OK, }, + }; + + int len; + char *addr; + CLASS *class; + bool test; + +__CONV: + + if ((type | value->type) >> 4) + goto __OBJECT; + else + goto *jump[value->type][type]; + +__c2b: +__h2b: +__i2b: + + value->_integer.value = -(value->_integer.value != 0); + value->type = T_BOOLEAN; + return; + +__l2b: + + value->_integer.value = -(value->_long.value != 0); + value->type = T_BOOLEAN; + return; + +__g2b: + + value->_integer.value = -(value->_single.value != 0); + value->type = T_BOOLEAN; + return; + +__f2b: + + value->_integer.value = -(value->_float.value != 0); + value->type = T_BOOLEAN; + return; + +__d2b: + value->_integer.value = -(value->_date.date != 0 || value->_date.time != 0); + value->type = T_BOOLEAN; + return; + +__p2b: + value->_integer.value = -(value->_pointer.value != NULL); + value->type = T_BOOLEAN; + return; + +#if DO_NOT_CHECK_OVERFLOW + +__b2c: +__h2c: +__i2c: + value->_integer.value = (uchar)value->_integer.value; + value->type = T_BYTE; + return; + +__l2c: + value->_integer.value = (uchar)value->_long.value; + value->type = T_BYTE; + return; + +#else + +__b2c: + value->_integer.value = (uchar)value->_integer.value; + value->type = T_BYTE; + return; + +__h2c: +__i2c: + { + uchar result; + + if (__builtin_add_overflow(value->_integer.value, 0, &result)) + THROW_OVERFLOW(); + value->_integer.value = result; + value->type = T_BYTE; + return; + } + +__l2c: + { + uchar result; + + if (__builtin_add_overflow(value->_long.value, 0, &result)) + THROW_OVERFLOW(); + value->_integer.value = result; + value->type = T_BYTE; + return; + } + +#endif + +__g2c: + + value->_integer.value = (unsigned char)value->_single.value; + value->type = T_BYTE; + return; + +__f2c: + + value->_integer.value = (unsigned char)value->_float.value; + value->type = T_BYTE; + return; + +#if DO_NOT_CHECK_OVERFLOW + +__b2h: +__c2h: +__i2h: + value->type = T_SHORT; + return; + +__l2h: + value->_integer.value = (short)value->_long.value; + value->type = T_SHORT; + return; + +#else + +__b2h: +__c2h: + value->type = T_SHORT; + return; + +__i2h: + { + short result; + + if (__builtin_add_overflow(value->_integer.value, 0, &result)) + THROW_OVERFLOW(); + value->_integer.value = result; + value->type = T_SHORT; + return; + } + +__l2h: + { + short result; + + if (__builtin_add_overflow(value->_long.value, 0, &result)) + THROW_OVERFLOW(); + value->_integer.value = result; + value->type = T_SHORT; + return; + } + +#endif + +__g2h: + + value->_integer.value = (short)value->_single.value; + value->type = T_SHORT; + return; + +__f2h: + + value->_integer.value = (short)value->_float.value; + value->type = T_SHORT; + return; + +#if DO_NOT_CHECK_OVERFLOW + +__l2i: + value->_integer.value = (int)value->_long.value; + value->type = T_INTEGER; + return; + +#else + +__l2i: + if (__builtin_add_overflow(value->_long.value, 0, &value->_integer.value)) + THROW_OVERFLOW(); + value->type = T_INTEGER; + return; + +#endif + +__g2i: + + value->_integer.value = (int)value->_single.value; + value->type = T_INTEGER; + return; + +__f2i: + + value->_integer.value = (int)value->_float.value; + value->type = T_INTEGER; + return; + +__p2i: + + value->_integer.value = (int)(intptr_t)value->_pointer.value; + value->type = T_INTEGER; + return; + +__b2l: +__c2l: +__h2l: +__i2l: + + value->_long.value = (int64_t)value->_integer.value; + value->type = T_LONG; + return; + +__g2l: + + value->_long.value = (int64_t)value->_single.value; + value->type = T_LONG; + return; + +__f2l: + + value->_long.value = (int64_t)value->_float.value; + value->type = T_LONG; + return; + +__p2l: + + value->_long.value = (int64_t)(intptr_t)value->_pointer.value; + value->type = T_LONG; + return; + +__b2g: +__c2g: +__h2g: +__i2g: + + value->_single.value = value->_integer.value; + value->type = T_SINGLE; + return; + +__l2g: + + value->_single.value = (float)value->_long.value; + if (!isfinite(value->_single.value)) + THROW(E_OVERFLOW); + value->type = T_SINGLE; + return; + +__f2g: + + value->_single.value = (float)value->_float.value; + if (!isfinite(value->_single.value)) + THROW(E_OVERFLOW); + value->type = T_SINGLE; + return; + +__b2f: +__c2f: +__h2f: +__i2f: + + value->_float.value = value->_integer.value; + value->type = T_FLOAT; + return; + +__l2f: + + value->_float.value = value->_long.value; + value->type = T_FLOAT; + return; + +__g2f: + + value->_float.value = value->_single.value; + value->type = T_FLOAT; + return; + +__c2d: +__h2d: +__i2d: + + value->_date.date = Max(0, value->_integer.value); + value->_date.time = 0; + value->type = T_DATE; + return; + +__l2d: + + if (value->_long.value < 0) + value->_date.date = 0; + else if (value->_long.value > INT_MAX) + value->_date.date = INT_MAX; + else + value->_date.date = (int)value->_long.value; + + value->_date.time = 0; + value->type = T_DATE; + return; + +__g2d: + { + float val = value->_single.value; + float ival = floorf(val); + value->_date.time = (int)((val - ival) * 86400000.0 + 0.5); + value->_date.date = (int)ival; + value->type = T_DATE; + return; + } + +__f2d: + { + double val = value->_float.value; + double ival = floor(val); + value->_date.time = (int)((val - ival) * 86400000.0 + 0.5); + value->_date.date = (int)ival; + value->type = T_DATE; + return; + } + +__d2c: +__d2h: +__d2i: + + value->_integer.value = value->_date.date; + value->type = T_INTEGER; + goto *jump[T_INTEGER][type]; + +__d2l: + + value->_long.value = value->_date.date; + value->type = T_LONG; + return; + +__d2g: + + value->_single.value = (float)value->_date.date + (float)value->_date.time / 86400000.0; + value->type = T_SINGLE; + return; + +__d2f: + + value->_float.value = (double)value->_date.date + (double)value->_date.time / 86400000.0; + value->type = T_FLOAT; + return; + +__b2s: + + if (value->_boolean.value) + STRING_char_value(value, 'T'); + else + STRING_void_value(value); + return; + +__c2s: +__h2s: +__i2s: + +/*len = sprintf(COMMON_buffer, "%d", value->_integer.value); + STRING_new_temp_value(value, COMMON_buffer, len);*/ + NUMBER_int_to_string(value->_integer.value, 0, 10, value); + BORROW(value); + return; + +__l2s: + +/*len = sprintf(COMMON_buffer, "%" PRId64, value->_long.value); + STRING_new_temp_value(value, COMMON_buffer, len);*/ + NUMBER_int_to_string(value->_long.value, 0, 10, value); + BORROW(value); + return; + +__g2s: + + LOCAL_format_number(value->_single.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__f2s: + + LOCAL_format_number(value->_float.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__p2s: + #if OS_64BITS + NUMBER_int_to_string((int64_t)(intptr_t)value->_pointer.value, 0, 16, value); + #else + NUMBER_int_to_string((int)(intptr_t)value->_pointer.value, 0, 16, value); + #endif + BORROW(value); + return; + +__d2s: + + len = DATE_to_string(COMMON_buffer, value); + STRING_new_temp_value(value, COMMON_buffer, len); + BORROW(value); + return; + +__s2b: + + addr = value->_string.addr; + value->_integer.value = -(addr != NULL && value->_string.len != 0); + if (value->type == T_STRING) + STRING_unref(&addr); + value->type = T_BOOLEAN; + return; + +__s2c: +__s2h: +__s2i: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_INTEGER, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + goto *jump[T_INTEGER][type]; + +__s2l: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_LONG, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + return; + +__s2g: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_FLOAT, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + value->_single.value = value->_float.value; + + STRING_unref(&addr); + value->type = type; + return; + +__s2f: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_FLOAT, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + value->type = type; + return; + +__s2d: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (DATE_from_string(value->_string.addr + value->_string.start, value->_string.len, value, FALSE)) + goto __N; + + STRING_unref(&addr); + return; + +__s2p: + + value->_pointer.value = value->_string.addr + value->_string.start; + value->type = T_POINTER; + return; + +__n2b: + + value->_integer.value = 0; + value->type = T_BOOLEAN; + return; + +__n2d: + + DATE_void_value(value); + return; + +__n2s: + + STRING_void_value(value); + return; + +__n2p: + + value->_pointer.value = 0; + value->type = T_POINTER; + return; + +__v2: + + undo_variant(value); + goto __CONV; + +__s2v: + + addr = STRING_copy_from_value_temp(value); + + if (addr != value->_string.addr) + { + STRING_ref(addr); + + if (value->type == T_STRING) + STRING_unref(&value->_string.addr); + } + + value->_variant.vtype = T_STRING; //value->type; + value->_variant.value._string = addr; + value->type = T_VARIANT; + return; + +__2v: + + /* VALUE_put ne fonctionne pas avec T_STRING ! */ + if (value->type != T_NULL) + VALUE_put(value, &value->_variant.value, value->type); + + value->_variant.vtype = value->type; + value->type = T_VARIANT; + return; + +__func: + + goto __N; + +__i2p: + value->_pointer.value = (void *)(intptr_t)value->_integer.value; + value->type = T_POINTER; + return; + +__l2p: + value->_pointer.value = (void *)(intptr_t)value->_long.value; + value->type = T_POINTER; + return; + +__F2p: + + value->_pointer.value = EXTERN_make_callback(&value->_function); + value->type = T_POINTER; + return; + +__OBJECT: + + if (!TYPE_is_object(type)) + { + if (type == T_BOOLEAN) + { + test = (value->_object.object != NULL); + OBJECT_UNREF(value->_object.object); + value->_boolean.value = -test; + value->type = T_BOOLEAN; + return; + } + + if (type == T_VARIANT) + goto __2v; + + if (!value->_object.object) + goto __N; + + if (value->type == T_OBJECT) + class = OBJECT_class(value->_object.object); + else + class = value->_object.class; + + if (class->has_convert) + { + void *unref = value->_object.object; + TYPE old_type = value->type; + + if (!((*class->convert)(value->_object.object, type, value))) + { + OBJECT_UNREF(unref); + + if (value->type == old_type) + goto __TYPE; + else + goto __OK; + } + } + + goto __N; + } + + if (!TYPE_is_object(value->type)) + { + if (value->type == T_NULL) + { + OBJECT_null(value, (CLASS *)type); // Also works if type == T_OBJECT + goto __TYPE; + } + + if (value->type == T_POINTER && type != T_OBJECT) + { + class = (CLASS *)type; + + if (CLASS_is_struct(class)) + { + value->_object.object = CSTRUCT_create_static(STRUCT_CONST, class, value->_pointer.value); + OBJECT_REF(value->_object.object); + goto __TYPE; + } + } + + if (value->type == T_VARIANT) + goto __v2; + + if (value->type == T_FUNCTION) + goto __func; + + if (value->type == T_CLASS) + { + class = value->_class.class; + + if (CLASS_is_virtual(class)) + THROW(E_VIRTUAL); + + CLASS_load(class); + + if (class->auto_create) + value->_object.object = CLASS_auto_create(class, 0); + else + value->_object.object = class; + + OBJECT_REF(value->_object.object); + value->type = T_OBJECT; + /* on continue... */ + } + else + { + if (TYPE_is_pure_object(type)) + { + class = (CLASS *)type; + + if (class->has_convert) + { + if (!((*class->convert)(NULL, value->type, value))) + { + OBJECT_REF_CHECK(value->_object.object); + goto __TYPE; + } + } + } + + goto __N; + } + } + + if (value->_object.object == NULL) + goto __TYPE; + + if (value->type == T_OBJECT) + class = OBJECT_class(value->_object.object); + else + class = value->_object.class; + + if (CLASS_is_virtual(class)) + THROW(E_VIRTUAL); + + if (type == T_OBJECT) + goto __TYPE; + +__RETRY: + + if ((class == (CLASS *)type) || CLASS_inherits(class, (CLASS *)type)) + goto __TYPE; + + if (value->type != T_OBJECT && value->_object.object) + { + class = OBJECT_class(value->_object.object); + value->type = T_OBJECT; + goto __RETRY; + } + + if (class->has_convert) + { + void *unref = value->_object.object; + if (!((*class->convert)(value->_object.object, type, value))) + { + OBJECT_UNREF(unref); + OBJECT_REF_CHECK(value->_object.object); + goto __TYPE; + } + } + + CLASS *class2 = (CLASS *)type; + if (class2->has_convert) + { + void *unref = value->_object.object; + if (!((*class2->convert)(NULL, OBJECT_class(unref), value))) + { + OBJECT_UNREF(unref); + OBJECT_REF_CHECK(value->_object.object); + goto __TYPE; + } + } + + THROW_TYPE(type, (TYPE)class); + +__TYPE: + + value->type = type; + +__OK: + + return; + +__N: + + THROW_TYPE(type, value->type); + +__NR: + + THROW(E_NRETURN); +} + + +void VALUE_write_variant(VALUE *value, void *addr) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__CSTRING, &&__POINTER, &&__VOID, &&__VOID, &&__CLASS, &&__NULL + }; + + TYPE type = value->_variant.vtype; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: +__SINGLE: +__FLOAT: +__DATE: +__POINTER: +__NULL: + + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = value->_variant.vtype; + ((VARIANT *)addr)->value.data = value->_variant.value.data; + return; + +__CSTRING: +__STRING: +{ + char *str = value->_variant.value._string; + STRING_ref(str); + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = GB_T_STRING; + ((VARIANT *)addr)->value._string = str; + return; +} + +__OBJECT: +__CLASS: +{ + void *object = value->_variant.value._object; + OBJECT_REF_CHECK(object); + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = type; + ((VARIANT *)addr)->value._object = object; + return; +} + +__VOID: + + ERROR_panic("Bad type (%d) for VALUE_write_variant", type); +} + +void VALUE_write(VALUE *value, void *addr, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + char *str; + +__CONV: + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + VALUE_conv_boolean(value); + *((unsigned char *)addr) = (value->_boolean.value != 0 ? 255 : 0); + return; + +__BYTE: + + VALUE_conv(value, T_BYTE); + *((unsigned char *)addr) = (unsigned char)(value->_byte.value); + return; + +__SHORT: + + VALUE_conv(value, T_SHORT); + *((short *)addr) = (short)(value->_short.value); + return; + +__INTEGER: + + VALUE_conv_integer(value); + *((int *)addr) = value->_integer.value; + return; + +__LONG: + + VALUE_conv(value, T_LONG); + *((int64_t *)addr) = value->_long.value; + return; + +__SINGLE: + + VALUE_conv(value, T_SINGLE); + *((float *)addr) = value->_single.value; + return; + +__FLOAT: + + VALUE_conv_float(value); + *((double *)addr) = value->_float.value; + return; + +__DATE: + + VALUE_conv(value, T_DATE); + ((int *)addr)[0] = value->_date.date; + ((int *)addr)[1] = value->_date.time; + return; + +__STRING: + + VALUE_conv_string(value); + + str = STRING_copy_from_value_temp(value); + STRING_ref(str); + STRING_unref((char **)addr); + *((char **)addr) = str; + return; + +__OBJECT: + + VALUE_conv(value, type); + + OBJECT_REF_CHECK(value->_object.object); + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = value->_object.object; + return; + +__CLASS: + + VALUE_conv(value, type); + + OBJECT_REF(value->_class.class); + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = value->_class.class; + return; + +__POINTER: + + VALUE_conv(value, T_POINTER); + *((void **)addr) = value->_pointer.value; + return; + +__VARIANT: + + VARIANT_undo(value); + + type = value->type; + if (type == T_CSTRING) + type = T_STRING; + + VARIANT_clear((VARIANT *)addr); + ((VARIANT *)addr)->type = type; + + /* Et si type ne fait pas partie des types valides pour cette fonction ?? */ + if (type == T_NULL) + return; + + addr = &((VARIANT *)addr)->value.data; + /*goto *jump[Min(T_OBJECT, type)];*/ + goto __CONV; + +__VOID: + + THROW(E_NRETURN); + +__FUNCTION: + + THROW_TYPE(T_VARIANT, type); + +__NULL: + + ERROR_panic("Bad type (%d) for VALUE_write", type); +} + + + +void VALUE_read(VALUE *value, void *addr, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__CSTRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + value->type = type; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + value->_boolean.value = (*((unsigned char *)addr) != 0) ? (-1) : 0; + return; + +__BYTE: + + value->_byte.value = *((unsigned char *)addr); + return; + +__SHORT: + + value->_short.value = *((short *)addr); + return; + +__INTEGER: + + value->_integer.value = *((int *)addr); + return; + +__LONG: + + value->_long.value = *((int64_t *)addr); + return; + +__SINGLE: + + value->_single.value = *((float *)addr); + return; + +__FLOAT: + + value->_float.value = *((double *)addr); + return; + +__DATE: + + value->_date.date = ((int *)addr)[0]; + value->_date.time = ((int *)addr)[1]; + return; + +__STRING: + + { + char *str = *((char **)addr); + + value->type = T_STRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = STRING_length(str); + + return; + } + +__CSTRING: + + { + char *str = *((char **)addr); + + value->type = T_CSTRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = (str == NULL) ? 0 : strlen(str); + + return; + } + +__OBJECT: + + value->_object.object = *((void **)addr); + return; + +__POINTER: + + value->_pointer.value = *((void **)addr); + return; + +__VARIANT: + + value->_variant.type = T_VARIANT; + value->_variant.vtype = ((VARIANT *)addr)->type; + + if (value->_variant.vtype == T_VOID) + value->_variant.vtype = T_NULL; + + VARIANT_copy_value(&value->_variant, ((VARIANT *)addr)); + + return; + +__CLASS: + + value->_class.class = *((void **)addr); + value->_class.super = NULL; + return; + +__VOID: +__FUNCTION: +__NULL: + + ERROR_panic("Bad type (%d) for VALUE_read", type); +} + + +void VALUE_free(void *addr, TYPE type) +{ + if (type == T_STRING) + { + STRING_unref((char **)addr); + *((char **)addr) = NULL; + } + else if (TYPE_is_object(type)) + { + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = NULL; + } + else if (type == T_VARIANT) + { + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = T_NULL; + } +} + + + +void VALUE_to_local_string(VALUE *value, char **addr, int *len) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + +__CONV: + + if (TYPE_is_object(value->type)) + goto __OBJECT; + else + goto *jump[value->type]; + +__NULL: + + *addr = ""; // To be coherent with Print "", as Null == "" */ + *len = 0; + return; + +__BOOLEAN: + + if (value->_boolean.value) + { + *addr = LOCAL_local.true_str; + *len = LOCAL_local.len_true_str; + } + else + { + *addr = LOCAL_local.false_str; + *len = LOCAL_local.len_false_str; + } + return; + +__BYTE: +__SHORT: +__INTEGER: + + *len = sprintf(COMMON_buffer, "%d", value->_integer.value); + *addr = COMMON_buffer; + + return; + +__LONG: + + *len = sprintf(COMMON_buffer, "%" PRId64, value->_long.value); + *addr = COMMON_buffer; + + return; + +__DATE: + { + const DATE_SERIAL *date = DATE_split(value); + LOCAL_format_date(date, DATE_SERIAL_has_no_date(date) ? LF_LONG_TIME : LF_STANDARD, NULL, 0, addr, len); + return; + } + +__SINGLE: + + LOCAL_format_number(value->_single.value, LF_SHORT_NUMBER, NULL, 0, addr, len, TRUE); + return; + +__FLOAT: + + LOCAL_format_number(value->_float.value, LF_STANDARD, NULL, 0, addr, len, TRUE); + return; + +__STRING: + + *len = value->_string.len; + *addr = value->_string.addr + value->_string.start; + return; + +__OBJECT: + + { + CLASS *class; + + if (VALUE_is_null(value)) + goto __NULL; + + class = OBJECT_class(value->_object.object); + if (class->has_convert) + { + VALUE temp; + if (!((*class->convert)(value->_object.object, T_CSTRING, &temp))) + { + *addr = temp._string.addr + temp._string.start; + *len = temp._string.len; + STRING_free_later(*addr); + return; + } + } + + *len = sprintf(COMMON_buffer, "(%s %p)", class->name, value->_object.object); + *addr = COMMON_buffer; + return; + } + +__POINTER: + + if (VALUE_is_null(value)) + goto __NULL; + + *len = sprintf(COMMON_buffer, "(Pointer %p)", value->_pointer.value); + *addr = COMMON_buffer; + return; + +__VARIANT: + + VARIANT_undo(value); + goto __CONV; + +__VOID: + + THROW(E_NRETURN); + +__CLASS: + + *len = sprintf(COMMON_buffer, "(Class %s)", value->_class.class->name); + *addr = COMMON_buffer; + return; + +__FUNCTION: + + //if (unknown_function(value)) + // goto __CONV; + + *len = sprintf(COMMON_buffer, "(Function %s:%d)", value->_function.class->name, value->_function.index); + *addr = COMMON_buffer; +} + + +void VALUE_from_local_string(VALUE *value, const char *addr, int len) +{ + while (len > 0 && isspace(*addr)) + addr++, len--; + + while (len > 0 && isspace(addr[len - 1])) + len--; + + if (len > 0) + { + if (!DATE_from_string(addr, len, value, TRUE)) + return; + + if (!NUMBER_from_string(NB_READ_ALL | NB_READ_HEX_BIN | NB_LOCAL, addr, len, value)) + return; + + if (len == LOCAL_local.len_true_str && strncasecmp(addr, LOCAL_local.true_str, len) == 0) + { + value->type = T_BOOLEAN; + value->_boolean.value = -1; + return; + } + + if (len == LOCAL_local.len_false_str && strncasecmp(addr, LOCAL_local.false_str, len) == 0) + { + value->type = T_BOOLEAN; + value->_boolean.value = 0; + return; + } + } + + VALUE_null(value); +} + + +void VALUE_class_read(CLASS *class, VALUE *value, char *addr, CTYPE ctype, void *ref) +{ + VALUE_class_read_inline(class, value, addr, ctype, ref, VCR); +} + +void VALUE_class_write(CLASS *class, VALUE *value, char *addr, CTYPE ctype) +{ + if (ctype.id == T_OBJECT) + { + TYPE type = (ctype.value >= 0) ? (TYPE)class->load->class_ref[ctype.value] : T_OBJECT; + + VALUE_conv(value, type); + + OBJECT_REF_CHECK(value->_object.object); + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = value->_object.object; + } + else if (ctype.id == TC_STRUCT) + { + int i; + CLASS *sclass; + CLASS_DESC *desc; + void *src; + char *saddr; + VALUE temp; + int offset; + + sclass = class->load->class_ref[ctype.value]; + VALUE_conv(value, (TYPE)sclass); + src = value->_object.object; + + for (i = 0; i < sclass->n_desc; i++) + { + desc = sclass->table[i].desc; + ctype = desc->variable.ctype; + offset = desc->variable.offset; + + if (((CSTRUCT *)src)->ref) + saddr = (char *)((CSTATICSTRUCT *)src)->addr; + else + saddr = (char *)src + sizeof(CSTRUCT); + + VALUE_class_read(desc->variable.class, &temp, &saddr[offset], ctype, src); + //BORROW(&temp); + VALUE_class_write(sclass, &temp, &addr[offset], ctype); + RELEASE(&temp); + } + } + else if (ctype.id == TC_ARRAY) + { + CLASS_ARRAY *adesc = class->load->array[ctype.value]; + CLASS *aclass = CARRAY_get_array_class(class, adesc->ctype); + int acount = CARRAY_get_static_count(adesc); + int i, count; + CARRAY *src; + VALUE temp; + char *saddr; + + VALUE_conv(value, (TYPE)aclass); + src = (CARRAY *)value->_object.object; + + ctype = adesc->ctype; + + count = src->count; + if (count > acount) + count = acount; + + saddr = CARRAY_get_data(src, 0); + + for (i = 0; i < count; i++) + { + VALUE_class_read(class, &temp, saddr, ctype, src); + //BORROW(&temp); + VALUE_class_write(class, &temp, addr, ctype); + RELEASE(&temp); + saddr += src->size; + addr += src->size; + } + } + else + { + VALUE_write(value, addr, (TYPE)ctype.id); + } +} + +void VALUE_class_constant(CLASS *class, VALUE *value, int ind) +{ + VALUE_class_constant_inline(class, value, ind); +} + + +bool VALUE_is_null(VALUE *val) +{ + static void *jump[16] = { + &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FALSE, &&__FALSE, &&__NULL + }; + + TYPE type = val->type; + + if (TYPE_is_object(type)) + return val->_object.object == NULL; + else + goto *jump[type]; + +__NULL: + return TRUE; + +__STRING: + return val->_string.addr == 0 || val->_string.len == 0; + +__DATE: + return val->_date.date == 0 && val->_date.time == 0; + +__POINTER: + return val->_pointer.value == NULL; + +__VARIANT: + + if (val->_variant.vtype == T_NULL) + return TRUE; + + if (val->_variant.vtype == T_STRING) + return val->_variant.value._string == NULL; + + if (val->_variant.vtype == T_DATE) + return val->_variant.value.data == 0; + + if (val->_variant.vtype == T_POINTER) + return val->_variant.value._pointer == NULL; + + if (TYPE_is_object(val->_variant.vtype)) + return val->_variant.value._object == NULL; + +__FALSE: + return FALSE; +} + + +void VALUE_convert_boolean(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__OK, &&__c2b, &&__h2b, &&__i2b, &&__l2b, &&__g2b, &&__f2b, + &&__d2b, &&__s2b, &&__s2b, &&__N, &&__v2, &&__func, &&__N, &&__n2b + }; + + char *addr; + TYPE type = value->type; + + if (TYPE_is_object(type)) + { + if (value->_object.object) + { + OBJECT_just_unref(value->_object.object); + value->_boolean.value = -1; + } + value->type = T_BOOLEAN; + return; + } + else + goto *jump[type]; + +__c2b: +__h2b: +__i2b: + + value->_integer.value = (value->_integer.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__l2b: + + value->_integer.value = (value->_long.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__g2b: + + value->_integer.value = (value->_single.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__f2b: + + value->_integer.value = (value->_float.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__d2b: + + value->_integer.value = (value->_date.date != 0 || value->_date.time != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__s2b: + + addr = value->_string.addr; + value->_integer.value = ((addr != NULL) && (value->_string.len != 0)) ? -1 : 0; + if (value->type == T_STRING) + STRING_unref(&addr); + value->type = T_BOOLEAN; + return; + +__n2b: + + value->_integer.value = 0; + value->type = T_BOOLEAN; + return; + +__v2: +{ + static const void *jumpv[] = { &&__NR, &&__bv2b, &&__bv2b, &&__hv2b, &&__iv2b, &&__lv2b, &&__iv2b, &&__lv2b, &&__lv2b, &&__sv2b, &&__sv2b, &&__pv2b, &&__N, &&__N, &&__N, &&__nv2b }; + int test; + + type = value->_variant.vtype; + if (TYPE_is_object(type)) + goto __ov2b; + else + goto *jumpv[type]; + +__bv2b: + test = value->_variant.value._boolean != 0; + goto __VOK; + +__hv2b: + test = value->_variant.value._short != 0; + goto __VOK; + +__iv2b: + test = value->_variant.value._integer != 0; + goto __VOK; + +__lv2b: + test = value->_variant.value._long != 0; + goto __VOK; + +__pv2b: + test = value->_variant.value._pointer != 0; + goto __VOK; + +__ov2b: + test = value->_variant.value._object != 0; + OBJECT_UNREF(value->_variant.value._object); + goto __VOK; + +__sv2b: + test = value->_variant.value._string && *value->_variant.value._string; + STRING_unref(&value->_variant.value._string); + goto __VOK; + +__nv2b: + test = FALSE; + +__VOK: + value->type = T_BOOLEAN; + value->_integer.value = test ? -1 : 0; + return; +} + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__N: + + THROW_TYPE(T_BOOLEAN, type); + +__NR: + + THROW(E_NRETURN); + +__OK: + return; +} + + +void VALUE_convert_integer(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__l2i, &&__g2i, &&__f2i, + &&__d2i, &&__s2i, &&__s2i, &&__N, &&__v2, &&__func, &&__N, &&__N + }; + + char *addr; + +__CONV: + + goto *jump[value->type]; + +__l2i: + +#if DO_NOT_CHECK_OVERFLOW + value->_integer.value = (int)value->_long.value; +#else + if (__builtin_add_overflow(value->_long.value, 0, &value->_integer.value)) + THROW_OVERFLOW(); +#endif + goto __TYPE; + +__g2i: + + value->_integer.value = (int)value->_single.value; + goto __TYPE; + +__f2i: + + value->_integer.value = (int)value->_float.value; + goto __TYPE; + +__d2i: + + value->_integer.value = value->_date.date; + goto __TYPE; + +__s2i: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_INTEGER, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + goto __TYPE; + +__v2: + + undo_variant(value); + if (TYPE_is_object(value->type)) + goto __N; + else + goto __CONV; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__TYPE: + + value->type = T_INTEGER; + +__OK: + + return; + +__N: + + THROW_TYPE(T_INTEGER, value->type); + +__NR: + + THROW(E_NRETURN); +} + + +void VALUE_convert_float(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__b2f, &&__c2f, &&__h2f, &&__i2f, &&__l2f, &&__g2f, &&__OK, + &&__d2f, &&__s2f, &&__s2f, &&__N, &&__v2, &&__func, &&__N, &&__N + }; + + char *addr; + +__CONV: + + if (TYPE_is_object(value->type)) + goto __N; + else + goto *jump[value->type]; + +__b2f: +__c2f: +__h2f: +__i2f: + + value->_float.value = value->_integer.value; + value->type = T_FLOAT; + return; + +__l2f: + + value->_float.value = value->_long.value; + value->type = T_FLOAT; + return; + +__g2f: + + value->_float.value = value->_single.value; + value->type = T_FLOAT; + return; + +__d2f: + + value->_float.value = (double)value->_date.date + (double)value->_date.time / 86400000.0; + value->type = T_FLOAT; + return; + +__s2f: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_FLOAT, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + return; + +__v2: + + undo_variant(value); + if (TYPE_is_object(value->type)) + goto __N; + else + goto __CONV; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__N: + + THROW_TYPE(T_FLOAT, value->type); + +__NR: + + THROW(E_NRETURN); + +__OK: + + return; +} + + +void VALUE_convert_string(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__b2s, &&__c2s, &&__h2s, &&__i2s, &&__l2s, &&__g2s, &&__f2s, + &&__d2s, &&__OK, &&__OK, &&__N, &&__v2, &&__func, &&__N, &&__n2s + }; + + int len; + char *addr; + +__CONV: + + goto *jump[value->type]; + +__b2s: + + if (value->_boolean.value) + STRING_char_value(value, 'T'); + else + STRING_void_value(value); + return; + +__c2s: +__h2s: +__i2s: + + NUMBER_int_to_string(value->_integer.value, 0, 10, value); + BORROW(value); + return; + +__l2s: + + NUMBER_int_to_string(value->_long.value, 0, 10, value); + BORROW(value); + return; + +__g2s: + + LOCAL_format_number(value->_single.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__f2s: + + LOCAL_format_number(value->_float.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__d2s: + + len = DATE_to_string(COMMON_buffer, value); + STRING_new_temp_value(value, COMMON_buffer, len); + BORROW(value); + return; + +__n2s: + + STRING_void_value(value); + return; + +__v2: + + undo_variant(value); + if (TYPE_is_object(value->type)) + goto __N; + else + goto __CONV; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__OK: + + return; + +__N: + + THROW_TYPE(T_STRING, value->type); + +__NR: + + THROW(E_NRETURN); +} + + +void VALUE_convert_variant(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__2v, &&__2v, &&__2v, &&__2v, &&__2v, &&__2v, &&__2v, + &&__2v, &&__s2v, &&__s2v, &&__2v, &&__OK, &&__func, &&__2v, &&__2v + }; + + char *addr; + +//__CONV: + + if (TYPE_is_object(value->type)) + goto __2v; + else + goto *jump[value->type]; + +__s2v: + + addr = STRING_copy_from_value_temp(value); + + if (addr != value->_string.addr) + { + STRING_ref(addr); + + if (value->type == T_STRING) + STRING_unref(&value->_string.addr); + } + + value->_variant.value._string = addr; + value->_variant.vtype = T_STRING; + + goto __TYPE; + +__2v: + + /* VALUE_put ne fonctionne pas avec T_STRING ! */ + if (value->type != T_NULL) + VALUE_put(value, &value->_variant.value, value->type); + + value->_variant.vtype = value->type; + goto __TYPE; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__TYPE: + + value->type = T_VARIANT; + +__OK: + + return; + +__N: + + THROW_TYPE(T_VARIANT, value->type); + +__NR: + + THROW(E_NRETURN); +} + + +void VALUE_undo_variant(VALUE *value) +{ + undo_variant(value); +} diff --git a/main/gbx/gbx_value.h b/main/gbx/gbx_value.h new file mode 100644 index 00000000..f99d15f7 --- /dev/null +++ b/main/gbx/gbx_value.h @@ -0,0 +1,661 @@ +/*************************************************************************** + + gbx_value.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_VALUE_H +#define __GBX_VALUE_H + +#include + +#include "gbx_type.h" +#include "gbx_class.h" + +#ifndef __DATE_DECLARED +#define __DATE_DECLARED +typedef + struct { + int date; + int time; + } + DATE; +#endif + +typedef + struct { + TYPE type; + int value; + } + VALUE_BOOLEAN; + +typedef + struct { + TYPE type; + int value; + } + VALUE_BYTE; + +typedef + struct { + TYPE type; + int value; + } + VALUE_SHORT; + +typedef + struct { + TYPE type; + int value; + } + VALUE_INTEGER; + +typedef + struct { + TYPE type; + #ifndef OS_64BITS + int _padding; + #endif + int64_t value; + } + VALUE_LONG; + +typedef + struct { + TYPE type; + char *value; + } + VALUE_POINTER; + +typedef + struct { + TYPE type; + float value; + } + VALUE_SINGLE; + +typedef + struct { + TYPE type; + #ifndef OS_64BITS + int _padding; + #endif + double value; + } + VALUE_FLOAT; + +typedef + struct { + TYPE type; + int date; /* number of days */ + int time; /* number of milliseconds */ + } + VALUE_DATE; + +typedef + struct { + TYPE type; + char *addr; + int start; + int len; + } + VALUE_STRING; + +typedef + struct { + TYPE type; + CLASS *class; + void *object; + char kind; + char defined; + short index; + } + VALUE_FUNCTION; + +enum +{ + FUNCTION_NULL, + FUNCTION_NATIVE, + FUNCTION_PRIVATE, + FUNCTION_PUBLIC, + FUNCTION_EVENT, + FUNCTION_EXTERN, + FUNCTION_UNKNOWN, + FUNCTION_CALL, + FUNCTION_SUBR +}; + +typedef + struct { + TYPE type; + TYPE ptype; + intptr_t value[2]; + } + VALUE_VOID; + +typedef + struct { + TYPE type; + TYPE vtype; + union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + DATE _date; + char *_string; + void *_pointer; + void *_object; + int64_t data; + } + value; + } + VALUE_VARIANT; + +typedef + struct { + CLASS *class; + void *object; + void *super; + } + VALUE_OBJECT; + +typedef + struct { + TYPE type; + CLASS *class; + void *super; + } + VALUE_CLASS; + +typedef + union value { + TYPE type; + VALUE_BOOLEAN _boolean; + VALUE_BYTE _byte; + VALUE_SHORT _short; + VALUE_INTEGER _integer; + VALUE_LONG _long; + VALUE_SINGLE _single; + VALUE_FLOAT _float; + VALUE_DATE _date; + VALUE_STRING _string; + VALUE_POINTER _pointer; + VALUE_FUNCTION _function; + VALUE_VARIANT _variant; + VALUE_CLASS _class; + VALUE_OBJECT _object; + VALUE_VOID _void; + } + VALUE; + +typedef + void (*VALUE_CONVERT_FUNC)(VALUE *); + +#define VALUE_copy(_dst, _src) \ + ((_dst)->_void.type = (_src)->_void.type, \ + (_dst)->_void.ptype = (_src)->_void.ptype, \ + (_dst)->_void.value[0] = (_src)->_void.value[0], \ + (_dst)->_void.value[1] = (_src)->_void.value[1]) + +#define VALUE_is_equal(_v1, _v2) (*_v1 == *v2) + +#define VALUE_is_object(val) (TYPE_is_object((val)->type)) +#define VALUE_is_string(val) ((val)->type == T_STRING || (val)->type == T_CSTRING) +#define VALUE_is_number(val) ((val)->type >= T_BYTE && (val)->type <= T_FLOAT) + +void VALUE_default(VALUE *value, TYPE type); + +void VALUE_convert(VALUE *value, TYPE type); + +void VALUE_convert_boolean(VALUE *value); +void VALUE_convert_integer(VALUE *value); +void VALUE_convert_float(VALUE *value); +void VALUE_convert_string(VALUE *value); +void VALUE_convert_variant(VALUE *value); + +void VALUE_read(VALUE *value, void *addr, TYPE type); +void VALUE_write(VALUE *value, void *addr, TYPE type); + +void VALUE_undo_variant(VALUE *value); +void VALUE_write_variant(VALUE *value, void *addr); + +//void VALUE_put(VALUE *value, void *addr, TYPE type); + +void VALUE_free(void *addr, TYPE type); +void VALUE_to_local_string(VALUE *value, char **addr, int *len); +void VALUE_from_local_string(VALUE *value, const char *addr, int len); + +void VALUE_class_read(CLASS *class, VALUE *value, char *addr, CTYPE ctype, void *ref); +void VALUE_class_write(CLASS *class, VALUE *value, char *addr, CTYPE ctype); +void VALUE_class_constant(CLASS *class, VALUE *value, int ind); + +#define VALUE_null(_val) ({ (_val)->type = T_NULL; (_val)->_object.object = NULL; }) +bool VALUE_is_null(VALUE *val); + +//void VALUE_get_string(VALUE *val, char **text, int *length); +#define VALUE_get_string(_value, _ptext, _plen) \ +({ \ + *(_plen) = (_value)->_string.len; \ + if (*(_plen)) \ + *(_ptext) = (_value)->_string.start + (_value)->_string.addr; \ + else \ + *(_ptext) = NULL; \ +}) + +void THROW_TYPE(TYPE wanted, TYPE got) NORETURN; + +#define VALUE_conv(_value, _type) \ +({ \ + if ((_value)->type != (_type)) \ + VALUE_convert(_value, _type); \ +}) + +#if 0 + +#define VALUE_conv_boolean(_value) \ +({ \ + VALUE *v = _value; \ + if (v->type != T_BOOLEAN) \ + { \ + VALUE_convert_boolean(v); \ + } \ +}) + +#define VALUE_conv_integer(_value) \ +({ \ + VALUE *v = _value; \ + if (v->type != T_INTEGER) \ + { \ + if (TYPE_is_object(v->type)) \ + THROW_TYPE_INTEGER(v->type); \ + VALUE_convert_integer(v); \ + } \ +}) + +#define VALUE_conv_float(_value) \ +({ \ + VALUE *v = _value; \ + if (v->type != T_FLOAT) \ + { \ + if (TYPE_is_object(v->type)) \ + THROW_TYPE_FLOAT(v->type); \ + VALUE_convert_float(v); \ + } \ +}) + +#define VALUE_conv_string(_value) \ +({ \ + VALUE *v = _value; \ + if (v->type != T_STRING && v->type != T_CSTRING) \ + { \ + if (TYPE_is_object(v->type)) \ + THROW_TYPE_STRING(v->type); \ + VALUE_convert_string(v); \ + } \ +}) + +#define VALUE_conv_variant(_value) \ +({ \ + if ((_value)->type != T_VARIANT) \ + VALUE_convert_variant(_value); \ +}) + +#define VALUE_conv_object(_value, _type) \ +({ \ + if ((_value)->type != (_type)) \ + VALUE_convert_object(_value, _type); \ +}) + +#else + +#define VALUE_conv_boolean(_value) \ +({ \ + if ((_value)->type != T_BOOLEAN) \ + VALUE_convert_boolean(_value); \ +}) + +#define VALUE_conv_float(_value) \ +({ \ + if ((_value)->type != T_FLOAT) \ + VALUE_convert_float(_value); \ +}) + +#define VALUE_conv_variant(_value) \ +({ \ + if ((_value)->type != T_VARIANT) \ + VALUE_convert_variant(_value); \ +}) + +//#define VALUE_conv_boolean(_value) VALUE_conv(_value, T_BOOLEAN) +#define VALUE_conv_integer(_value) VALUE_conv(_value, T_INTEGER) +//#define VALUE_conv_float(_value) VALUE_conv(_value, T_FLOAT) +//#define VALUE_conv_variant(_value) VALUE_conv(_value, T_VARIANT) +#define VALUE_conv_object(_value, _type) VALUE_conv(_value, _type) + +#define VALUE_conv_string(_value) \ +({ \ + if ((_value)->type != T_STRING && (_value)->type != T_CSTRING) \ + VALUE_conv(_value, T_STRING); \ +}) + +#endif + +#define VALUE_is_super(_value) (EXEC_super && EXEC_super == (_value)->_object.super) + +#define VALUE_class_read_inline(_class, _value, _addr, _ctype, _ref, _prefix) \ +({ \ + static void *jump[17] = { \ + &&__##_prefix##VOID, &&__##_prefix##BOOLEAN, &&__##_prefix##BYTE, &&__##_prefix##SHORT, &&__##_prefix##INTEGER, &&__##_prefix##LONG, &&__##_prefix##SINGLE, &&__##_prefix##FLOAT, &&__##_prefix##DATE, \ + &&__##_prefix##STRING, &&__##_prefix##CSTRING, &&__##_prefix##POINTER, &&__##_prefix##VARIANT, &&__##_prefix##ARRAY, &&__##_prefix##STRUCT, &&__##_prefix##NULL, &&__##_prefix##OBJECT \ + }; \ + \ + for(;;) \ + { \ + (_value)->type = (_ctype).id; \ + goto *jump[(_ctype).id]; \ + \ + __##_prefix##BOOLEAN: \ + (_value)->_boolean.value = -(*((unsigned char *)(_addr)) != 0); \ + break; \ + \ + __##_prefix##BYTE: \ + (_value)->_byte.value = *((unsigned char *)(_addr)); \ + break; \ + \ + __##_prefix##SHORT: \ + (_value)->_short.value = *((short *)(_addr)); \ + break; \ + \ + __##_prefix##INTEGER: \ + (_value)->_integer.value = *((int *)(_addr)); \ + break; \ + \ + __##_prefix##LONG: \ + (_value)->_long.value = *((int64_t *)(_addr)); \ + break; \ + \ + __##_prefix##SINGLE: \ + (_value)->_single.value = *((float *)(_addr)); \ + break; \ + \ + __##_prefix##FLOAT: \ + (_value)->_float.value = *((double *)(_addr)); \ + break; \ + \ + __##_prefix##DATE: \ + (_value)->_date.date = ((int *)(_addr))[0]; \ + (_value)->_date.time = ((int *)(_addr))[1]; \ + break; \ + \ + __##_prefix##STRING: \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_STRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = STRING_length(str); \ + STRING_ref(str); \ + \ + break; \ + } \ + \ + __##_prefix##CSTRING: \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = (str == NULL) ? 0 : strlen(str); \ + \ + break; \ + } \ + \ + __##_prefix##OBJECT: \ + (_value)->_object.object = *((void **)(_addr)); \ + (_value)->type = ((_ctype).value >= 0) ? (TYPE)(_class)->load->class_ref[(_ctype).value] : T_OBJECT; \ + OBJECT_REF_CHECK((_value)->_object.object); \ + break; \ + \ + __##_prefix##POINTER: \ + (_value)->_pointer.value = *((void **)(_addr)); \ + break; \ + \ + __##_prefix##VARIANT: \ + (_value)->_variant.type = T_VARIANT; \ + (_value)->_variant.vtype = ((VARIANT *)(_addr))->type; \ + if ((_value)->_variant.vtype == T_VOID) \ + (_value)->_variant.vtype = T_NULL; \ + VARIANT_copy_value(&(_value)->_variant, ((VARIANT *)(_addr))); \ + EXEC_borrow(T_VARIANT, _value); \ + break; \ + \ + __##_prefix##ARRAY: \ + { \ + void *_array = CARRAY_create_static((_class), (_ref), (_class)->load->array[(_ctype).value], (_addr)); \ + (_value)->_object.class = OBJECT_class(_array); \ + (_value)->_object.object = _array; \ + OBJECT_REF(_array); \ + break; \ + } \ + \ + __##_prefix##STRUCT: \ + { \ + void *_struct = CSTRUCT_create_static((_ref), (_class)->load->class_ref[(_ctype).value], (_addr)); \ + (_value)->_object.class = OBJECT_class(_struct); \ + (_value)->_object.object = _struct; \ + OBJECT_REF(_struct); \ + break; \ + } \ + \ + __##_prefix##VOID: \ + __##_prefix##NULL: \ + THROW_ILLEGAL(); \ + } \ +}) + +#define VALUE_class_constant_inline(_class, _value, _ind) \ +({ \ + static void *jump[] = \ + { \ + &&__ILLEGAL, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, \ + &&__ILLEGAL, &&__STRING, &&__CSTRING, &&__POINTER, &&__ILLEGAL, &&__ILLEGAL, &&__ILLEGAL, &&__ILLEGAL \ + }; \ + \ + CLASS_CONST *NO_WARNING(cc); \ + \ + for(;;) \ + { \ + cc = &(_class)->load->cst[_ind]; \ + goto *jump[cc->type]; \ + \ + __INTEGER: \ + \ + (_value)->type = T_INTEGER; \ + (_value)->_integer.value = cc->_integer.value; \ + break; \ + \ + __LONG: \ + \ + (_value)->type = T_LONG; \ + (_value)->_long.value = cc->_long.value; \ + break; \ + \ + __SINGLE: \ + \ + (_value)->type = T_SINGLE; \ + (_value)->_single.value = cc->_single.value; \ + break; \ + \ + __FLOAT: \ + \ + (_value)->type = T_FLOAT; \ + (_value)->_float.value = cc->_float.value; \ + break; \ + \ + __STRING: \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = (char *)cc->_string.addr; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = cc->_string.len; \ + break; \ + \ + __CSTRING: \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = (char *)LOCAL_gettext(cc->_string.addr); \ + (_value)->_string.start = 0; \ + (_value)->_string.len = strlen((_value)->_string.addr); \ + break; \ + \ + __POINTER: \ + \ + (_value)->type = T_POINTER; \ + (_value)->_pointer.value = NULL; \ + break; \ + \ + __ILLEGAL: \ + \ + THROW_ILLEGAL(); \ + } \ +}) + +#define VALUE_read_inline_type(_value, _addr, _ctype, _type, _label_noref, _label_ref) \ +({ \ + static void *jump[17] = { \ + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, \ + &&__STRING, &&__CSTRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT \ + }; \ + \ + for(;;) \ + { \ + (_value)->type = (_type); \ + goto *jump[_ctype]; \ + \ + __BOOLEAN: \ + \ + (_value)->_boolean.value = (*((unsigned char *)(_addr)) != 0) ? (-1) : 0; \ + goto _label_noref; \ + \ + __BYTE: \ + \ + (_value)->_byte.value = *((unsigned char *)(_addr)); \ + goto _label_noref; \ + \ + __SHORT: \ + \ + (_value)->_short.value = *((short *)(_addr)); \ + goto _label_noref; \ + \ + __INTEGER: \ + \ + (_value)->_integer.value = *((int *)(_addr)); \ + goto _label_noref; \ + \ + __LONG: \ + \ + (_value)->_long.value = *((int64_t *)(_addr)); \ + goto _label_noref; \ + \ + __SINGLE: \ + \ + (_value)->_single.value = *((float *)(_addr)); \ + goto _label_noref; \ + \ + __FLOAT: \ + \ + (_value)->_float.value = *((double *)(_addr)); \ + goto _label_noref; \ + \ + __DATE: \ + \ + (_value)->_date.date = ((int *)(_addr))[0]; \ + (_value)->_date.time = ((int *)(_addr))[1]; \ + goto _label_noref; \ + \ + __STRING: \ + \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_STRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = STRING_length(str); \ + \ + goto _label_ref; \ + } \ + \ + __CSTRING: \ + \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = (str == NULL) ? 0 : strlen(str); \ + \ + goto _label_noref; \ + } \ + \ + __OBJECT: \ + \ + (_value)->_object.object = *((void **)(_addr)); \ + goto _label_ref; \ + \ + __POINTER: \ + \ + (_value)->_pointer.value = *((void **)(_addr)); \ + goto _label_noref; \ + break; \ + \ + __VARIANT: \ + \ + (_value)->_variant.type = T_VARIANT; \ + (_value)->_variant.vtype = ((VARIANT *)(_addr))->type; \ + \ + if ((_value)->_variant.vtype == T_VOID) \ + (_value)->_variant.vtype = T_NULL; \ + \ + VARIANT_copy_value(&(_value)->_variant, ((VARIANT *)(_addr))); \ + \ + goto _label_ref; \ + \ + __CLASS: \ + \ + (_value)->_class.class = *((void **)(_addr)); \ + (_value)->_class.super = NULL; \ + goto _label_noref; \ + \ + __VOID: \ + __FUNCTION: \ + __NULL: \ + THROW_ILLEGAL(); \ + } \ +}) + +#endif diff --git a/main/gbx/gbx_variant.h b/main/gbx/gbx_variant.h new file mode 100644 index 00000000..8721ae36 --- /dev/null +++ b/main/gbx/gbx_variant.h @@ -0,0 +1,99 @@ +/*************************************************************************** + + gbx_variant.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __VARIANT_H +#define __VARIANT_H + +#include "gbx_type.h" +#include "gbx_string.h" +#include "gbx_object.h" + +// On ARM 32 bits, sizeof(VARIANT) = 12, so it must be packed like GB_VARIANT + +typedef + struct + { + TYPE type; + union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + GB_DATE_VALUE _date; + char *_string; + void *_object; + void *_pointer; + int64_t data; + } + value; + } + PACKED + VARIANT; + +#define VARIANT_copy_value(_dst, _src) (_dst)->value.data = (_src)->value.data + +#define VARIANT_undo(_value) \ +({ \ + if ((_value)->type == T_VARIANT) \ + VALUE_undo_variant(_value); \ +}) + +#define VARIANT_free(_var) \ +({ \ + if ((_var)->type == T_STRING) \ + { \ + STRING_unref(&(_var)->value._string); \ + } \ + else if (TYPE_is_object((_var)->type)) \ + { \ + OBJECT_UNREF((_var)->value._object); \ + } \ +}) + +#define VARIANT_keep(_var) \ +({ \ + if ((_var)->type == T_STRING) \ + { \ + STRING_ref((_var)->value._string); \ + } \ + else if (TYPE_is_object((_var)->type)) \ + { \ + OBJECT_REF_CHECK((_var)->value._object); \ + } \ +}) + +#define VARIANT_is_null(_var) \ + (((_var)->type == T_NULL) || ((_var)->type == T_STRING && !(_var)->value._string) || (TYPE_is_object((_var)->type) && !(_var)->value._object)) + +#define VARIANT_clear(_var) \ +({ \ + VARIANT_free(_var); \ + (_var)->type = 0; \ + (_var)->value.data = 0; \ +}) + +#endif + diff --git a/main/gbx/gbx_watch.c b/main/gbx/gbx_watch.c new file mode 100644 index 00000000..e6c900b9 --- /dev/null +++ b/main/gbx/gbx_watch.c @@ -0,0 +1,766 @@ +/*************************************************************************** + + gbx_watch.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_WATCH_C + +#include "gb_common.h" +#include "gb_error.h" + +#include +#include +#include + +#include "gb_array.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_date.h" +#include "gbx_c_timer.h" +#include "gbx_watch.h" + +//#define DEBUG_TIMER 1 +//#define DEBUG_WATCH 1 + +static fd_set read_fd; +static fd_set write_fd; + +static WATCH_CALLBACK *watch_callback = NULL; +//static short *watch_index = NULL; + +static int max_fd = 0; + +static WATCH_TIMER *_timers = NULL; + +static int _do_not_really_delete_callback = 0; +static bool _must_delete_callback = FALSE; + +static struct timeval _last_time = { 0 }; + +#ifdef DEBUG_TIMER +double _debug_time; +static double time_to_double(const struct timeval *t) +{ + return (double)t->tv_sec + t->tv_usec / 1E6 - _debug_time; +} +#endif + +static void time_add(struct timeval *t1, const struct timeval *t2) +{ + t1->tv_sec += t2->tv_sec; + t1->tv_usec += t2->tv_usec; + if (t1->tv_usec > 1000000) + { + t1->tv_usec -= 1000000; + t1->tv_sec++; + } +} + +static void time_sub(struct timeval *t1, const struct timeval *t2) +{ + t1->tv_sec -= t2->tv_sec; + t1->tv_usec -= t2->tv_usec; + if (t1->tv_usec < 0) + { + t1->tv_usec += 1000000; + t1->tv_sec--; + } +} + +#define time_comp(t1, t2, op) ((t1)->tv_sec op (t2)->tv_sec || ((t1)->tv_sec == (t2)->tv_sec && (t1)->tv_usec op (t2)->tv_usec)) +#define time_lower_than(t1, t2) time_comp(t1, t2, <) + +static void time_from_ms(struct timeval *t, int ms) +{ + t->tv_sec = ms / 1000; + t->tv_usec = (ms % 1000) * 1000; +} + +static struct timeval *time_now(void) +{ + static struct timeval current; + + gettimeofday(¤t, NULL); + if (current.tv_usec < 0 || current.tv_usec >= 1000000) + fprintf(stderr, "gbx3: warning: gettimeofday: tv_usec = %ld!\n", current.tv_usec); + return ¤t; +} + + +static void add_timer(GB_TIMER *timer, const struct timeval *timeout) +{ + int i; + WATCH_TIMER *wt; + + for (i = 0; i < ARRAY_count(_timers); i++) + { + //if (_timers[i].timeout > timeout) + if (time_lower_than(timeout, &_timers[i].timeout)) + break; + } + + wt = ARRAY_insert(&_timers, i); + wt->timer = timer; + wt->timeout = *timeout; + + #ifdef DEBUG_TIMER + fprintf(stderr, "add_timer: %p at %i: %g\n", timer, i, time_to_double(timeout)); + #endif +} + +static int find_timer(GB_TIMER *timer) +{ + int i; + + for (i = 0; i < ARRAY_count(_timers); i++) + { + if (_timers[i].timer == timer) + return i; + } + + return (-1); +} + +static void remove_timer(GB_TIMER *timer) +{ + int i = find_timer(timer); + + if (i >= 0) + { + ARRAY_remove(&_timers, i) + #ifdef DEBUG_TIMER + fprintf(stderr, "remove_timer: %p at %i\n", timer, i); + #endif + } +} + +double WATCH_get_timeout(GB_TIMER *timer) +{ + int i = find_timer(timer); + + if (i < 0) + return 0.0; + else + return DATE_to_double(&_timers[i].timeout, TRUE); +} + +static void raise_timers() +{ + struct timeval timeout; + struct timeval delay; + struct timeval *now; + GB_TIMER *timer; + + now = time_now(); + + while (ARRAY_count(_timers)) + { + timeout = _timers[0].timeout; + if (time_lower_than(now, &timeout)) + return; + + timer = _timers[0].timer; + + #ifdef DEBUG_TIMER + fprintf(stderr, "raise_timers: %p has been triggered! now = %.7f timeout = %.7f\n", timer, time_to_double(now), time_to_double(&timeout)); + #endif + + time_from_ms(&delay, timer->delay); + time_add(&timeout, &delay); + + if (time_lower_than(&timeout, now)) + { + timeout = *now; + time_add(&timeout, &delay); + } + + remove_timer(timer); + add_timer(timer, &timeout); + + CTIMER_raise(timer); + } +} + + +static bool get_timeout(const struct timeval *wait, struct timeval *tv) +{ + struct timeval timeout; + struct timeval *now; + + if (ARRAY_count(_timers) == 0 && !wait) + return TRUE; + + now = time_now(); + + #ifdef DEBUG_TIMER + fprintf(stderr, "get_timeout: now = %.7g timeout = %.7g\n", time_to_double(now), time_to_double(&_timers[0].timeout)); + #endif + + if (ARRAY_count(_timers) == 0) + timeout = *wait; + else + { + timeout = _timers[0].timeout; + if (wait && time_lower_than(wait, &timeout)) + timeout = *wait; + } + + time_sub(&timeout, now); + + // HZ = 100 on Linux. If a timer must be triggered in less than 10 ms, then + // we do a busy wait instead of calling the system. + + if (timeout.tv_sec == 0 && timeout.tv_usec < (1000000 / 100)) + { + #ifdef DEBUG_TIMER + fprintf(stderr, "busy loop\n"); + #endif + // busy loop! + time_add(&timeout, now); + for(;;) + { + now = time_now(); + if (!time_lower_than(now, &timeout)) + break; + } + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + else if (timeout.tv_sec < 0) + { + #ifdef DEBUG_TIMER + fprintf(stderr, "timeout < 0: %ld %ld\n", timeout.tv_sec, timeout.tv_usec); + #endif + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + + *tv = timeout; + + #ifdef DEBUG_TIMER + fprintf(stderr, "timeout: %.7f %ld %ld\n", timeout.tv_sec + timeout.tv_usec / 1E6, tv->tv_sec, tv->tv_usec); + #endif + return FALSE; +} + + +void WATCH_init(void) +{ + FD_ZERO(&read_fd); + FD_ZERO(&write_fd); + + ARRAY_create(&watch_callback); + //ARRAY_create(&watch_index); + ARRAY_create(&_timers); + + #ifdef DEBUG_TIMER + DATE_timer(&_debug_time, FALSE); + #endif +} + + +void WATCH_exit(void) +{ + ARRAY_delete(&watch_callback); + //ARRAY_delete(&watch_index); + ARRAY_delete(&_timers); +} + + +static void watch_fd(int fd, int flag, bool watch) +{ + #if DEBUG_WATCH + fprintf(stderr, "watch_fd: %d for %s: %s\n", fd, flag == GB_WATCH_READ ? "read" : "write", watch ? "set" : "clear"); + #endif + + if (flag == GB_WATCH_READ) + { + if (watch) + FD_SET(fd, &read_fd); + else + FD_CLR(fd, &read_fd); + } + else if (flag == GB_WATCH_WRITE) + { + if (watch) + FD_SET(fd, &write_fd); + else + FD_CLR(fd, &write_fd); + } +} + + +static int watch_find_callback(int fd) +{ + int i; + + for (i = 0; i < ARRAY_count(watch_callback); i++) + { + if (watch_callback[i].fd == fd) + return i; + } + + return -1; +} + + +static int find_max_fd(void) +{ + int i; + int max = -1; + + for (i = 0; i < ARRAY_count(watch_callback); i++) + { + if (watch_callback[i].fd > max) + max = watch_callback[i].fd; + } + + return max; +} + + +static WATCH_CALLBACK *watch_create_callback(int fd) +{ + int pos; + WATCH_CALLBACK *wcb; + + #if DEBUG_WATCH + fprintf(stderr, "watch_create_callback: %d\n", fd); + #endif + + pos = watch_find_callback(fd); + if (pos < 0) + { + pos = ARRAY_count(watch_callback); + wcb = ARRAY_add_void(&watch_callback); + wcb->fd = fd; + } + else + wcb = &watch_callback[pos]; + + if (fd > max_fd) + max_fd = fd; + + #if DEBUG_WATCH + fprintf(stderr, "watch_create_callback: %d -> %d read = %p (%p) write = %p (%p)\n", fd, pos, wcb->callback_read, (void *)wcb->param_read, wcb->callback_write, (void *)wcb->param_write); + #endif + + //ensure_watch_index(fd); + //watch_index[fd] = pos; + + return wcb; +} + + +static void watch_delete_callback(int pos) +{ + WATCH_CALLBACK *wcb; + int fd; + + #if DEBUG_WATCH + fprintf(stderr, "watch_delete_callback: %d (%d)\n", fd, _do_not_really_delete_callback); + #endif + + //watch_index[fd] = -1; + + wcb = &watch_callback[pos]; + fd = wcb->fd, + wcb->fd = -1; + watch_fd(fd, GB_WATCH_READ, FALSE); + watch_fd(fd, GB_WATCH_WRITE, FALSE); + max_fd = find_max_fd(); + + if (_do_not_really_delete_callback) + { + #if DEBUG_WATCH + fprintf(stderr, "--> do not really delete\n"); + #endif + _must_delete_callback = TRUE; + return; + } + + ARRAY_remove(&watch_callback, pos); + + #if DEBUG_WATCH + fprintf(stderr, "--> deleted\n"); + #endif +} + + +static void watch_delete_callback_from_fd(int fd) +{ + int pos = watch_find_callback(fd); + if (pos >= 0) + watch_delete_callback(pos); +} + + +void WATCH_watch(int fd, int type, void *callback, intptr_t param) +{ + WATCH_CALLBACK *wcb; + + if (fd < 0 || fd > FD_SETSIZE) + { + if (type != GB_WATCH_NONE) + ERROR_warning("trying to watch fd #%d", fd); + return; + } + + if (type == GB_WATCH_NONE) + watch_delete_callback_from_fd(fd); + else + { + wcb = watch_create_callback(fd); + if (type == GB_WATCH_READ) + { + wcb->callback_read = callback; + wcb->param_read = param; + } + else + { + wcb->callback_write = callback; + wcb->param_write = param; + } + + #if DEBUG_WATCH + fprintf(stderr, "add watch: %d -> %d read = %p (%p) write = %p (%p)\n", fd, watch_find_callback(fd), wcb->callback_read, (void *)wcb->param_read, wcb->callback_write, (void *)wcb->param_write); + #endif + + if (!wcb->callback_read && !wcb->callback_write) + watch_delete_callback_from_fd(fd); + else + { + watch_fd(fd, GB_WATCH_READ, wcb->callback_read != NULL); + watch_fd(fd, GB_WATCH_WRITE, wcb->callback_write != NULL); + } + } +} + +void WATCH_transfer_watch(void) +{ + WATCH_CALLBACK *wcb; + int i; + + for (i = 0; i < ARRAY_count(watch_callback); i++) + { + wcb = &watch_callback[i]; + if (wcb->callback_read) HOOK(watch)(wcb->fd, GB_WATCH_READ, wcb->callback_read, wcb->param_read); + if (wcb->callback_write) HOOK(watch)(wcb->fd, GB_WATCH_WRITE, wcb->callback_write, wcb->param_write); + } + + ARRAY_delete(&watch_callback); + ARRAY_create(&watch_callback); +} + + +void WATCH_transfer_timer(void) +{ + WATCH_TIMER *wt; + int i; + + for (i = 0; i < ARRAY_count(_timers); i++) + { + wt = &_timers[i]; + HOOK(timer)((GB_TIMER *)wt->timer, TRUE); + } + + ARRAY_delete(&_timers); + ARRAY_create(&_timers); +} + + +static void raise_callback(fd_set *rfd, fd_set *wfd) +{ + int i; + WATCH_CALLBACK wcb; + + _do_not_really_delete_callback++; + + #if DEBUG_WATCH + fprintf(stderr, "raise_callback: max_fd = %d\n", max_fd); + #endif + for (i = 0; i < ARRAY_count(watch_callback); i++) + { + // We copy the callback structure, because the watch_callback array can change during the + // execution of the callbacks. + + if (watch_callback[i].fd < 0) + continue; + + wcb = watch_callback[i]; + + #if DEBUG_WATCH + fprintf(stderr, "raise_callback: [%d] fd = %d read = %p (%p) write = %p (%p)\n", i, wcb.fd, wcb.callback_read, (void *)wcb.param_read, wcb.callback_write, (void *)wcb.param_write); + #endif + + if (FD_ISSET(wcb.fd, rfd)) + { + FD_CLR(wcb.fd, rfd); + if (wcb.callback_read) + { + #ifdef DEBUG_WATCH + fprintf(stderr, "call read callback on fd %d\n", wcb.fd); + #endif + (*(wcb.callback_read))(wcb.fd, GB_WATCH_READ, wcb.param_read); + } + } + + if (watch_callback[i].fd < 0) + continue; + + if (FD_ISSET(wcb.fd, wfd)) + { + FD_CLR(wcb.fd, wfd); + if (wcb.callback_write) + { + #ifdef DEBUG_WATCH + fprintf(stderr, "call write callback on fd %d\n", wcb.fd); + #endif + (*(wcb.callback_write))(wcb.fd, GB_WATCH_WRITE, wcb.param_write); + } + } + } + + _do_not_really_delete_callback--; + + if (!_do_not_really_delete_callback && _must_delete_callback) + { + #if DEBUG_WATCH + fprintf(stderr, "do must delete callback\n"); + #endif + i = 0; + while (i < ARRAY_count(watch_callback)) + { + if (watch_callback[i].fd < 0) + { + ARRAY_remove(&watch_callback, i); + } + else + i++; + } + _must_delete_callback = FALSE; + } +} + + +static int do_select(fd_set *rfd, fd_set *wfd, struct timeval *timeout) +{ + int fd; + + for (fd = max_fd; fd >= 0; fd--) + { + if (FD_ISSET(fd, &read_fd) || FD_ISSET(fd, &write_fd)) + break; + } + + if (fd < 0 && !timeout) + return 0; + + max_fd = fd; + + *rfd = read_fd; + *wfd = write_fd; + + return select(max_fd + 1, rfd, wfd, NULL, timeout); +} + +static bool do_loop(struct timeval *wait) +{ + int ret; + struct timeval tv; + fd_set rfd, wfd; + bool something_done = FALSE; + + if (EVENT_check_post()) + something_done = TRUE; + + #ifdef DEBUG_TIMER + fprintf(stderr, "\ndo_loop: now = %.7g: select (%d)\n", time_to_double(time_now()), something_done); + #endif + + if (get_timeout(wait, &tv)) + ret = do_select(&rfd, &wfd, NULL); + else + { + ret = do_select(&rfd, &wfd, &tv); + something_done = ARRAY_count(_timers) > 0; + } + + if (ret < 0 && errno != EINTR) + THROW_SYSTEM(errno, NULL); + + #ifdef DEBUG_TIMER + fprintf(stderr, "do_loop: now = %.7g: timers (%d)\n", time_to_double(time_now()), something_done); + #endif + + raise_timers(); + + #ifdef DEBUG_TIMER + fprintf(stderr, "do_loop: now = %.7g: callbacks\n", time_to_double(time_now())); + #endif + + if (ret > 0) + { + raise_callback(&rfd, &wfd); + something_done = TRUE; + } + else if (ret < 0) + { + something_done = TRUE; + } + + //if (EVENT_check_post()) + // something_done = TRUE; + + return something_done; +} + + +bool WATCH_one_loop(int wait) +{ + struct timeval timeout; + + if (wait == 0) + return do_loop(NULL); + else + { + time_from_ms(&timeout, wait); + return do_loop(&timeout); + } +} + +void WATCH_loop(void) +{ + while (do_loop(NULL)); +} + + +void WATCH_wait(int wait) +{ + struct timeval *now; + struct timeval timeout; + + if (wait == 0) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + do_loop(&timeout); + } + else if (wait < 0) + { + do_loop(NULL); + } + else + { + now = time_now(); + time_from_ms(&timeout, wait); + time_add(&timeout, now); + + for(;;) + { + now = time_now(); + if (time_lower_than(&timeout, now)) + break; + do_loop(&timeout); + } + } +} + +int WATCH_process(int fd_end, int fd_output, int fd_error, int timeout) +{ + fd_set rfd; + int ret, fd_max; + struct timeval tv; + + fd_max = fd_end > fd_output ? fd_end : fd_output; + fd_max = fd_error > fd_max ? fd_error : fd_max; + + for(;;) + { + FD_ZERO(&rfd); + FD_SET(fd_end, &rfd); + if (fd_output >= 0) + FD_SET(fd_output, &rfd); + if (fd_error >= 0) + FD_SET(fd_error, &rfd); + + if (timeout > 0) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ret = select(fd_max + 1, &rfd, NULL, NULL, &tv); + if (ret == 0) + break; + } + else + { + ret = select(fd_max + 1, &rfd, NULL, NULL, NULL); + } + + //fprintf(stderr, "WATCH_process: select -> %d [%d] / fd_end -> %d\n", ret, errno, FD_ISSET(fd_end, &rfd)); + + if (ret > 0) + break; + if (errno != EINTR) + break; + } + + ret = timeout > 0 ? WP_TIMEOUT : WP_NOTHING; + + if (FD_ISSET(fd_end, &rfd)) ret += WP_END; + if (fd_output >= 0 && FD_ISSET(fd_output, &rfd)) ret += WP_OUTPUT; + if (fd_error >= 0 && FD_ISSET(fd_error, &rfd)) ret += WP_ERROR; + return ret; +} + + +void WATCH_timer(void *t, int on) +{ + GB_TIMER *timer = (GB_TIMER *)t; + struct timeval timeout; + + if (on) + { + time_from_ms(&timeout, timer->delay); + time_add(&timeout, time_now()); + add_timer(timer, &timeout); + timer->id = (intptr_t)timer; + } + else + { + remove_timer(timer); + timer->id = 0; + } +} + +void WATCH_little_sleep(void) +{ + struct timeval tv; + struct timeval *now; + + now = time_now(); + tv = *now; + time_sub(&tv, &_last_time); + if (tv.tv_sec == 0 && tv.tv_usec < 10000) + usleep(10000 - tv.tv_usec); + _last_time = *now; +} + diff --git a/main/gbx/gbx_watch.h b/main/gbx/gbx_watch.h new file mode 100644 index 00000000..afdc75a2 --- /dev/null +++ b/main/gbx/gbx_watch.h @@ -0,0 +1,75 @@ +/*************************************************************************** + + gbx_watch.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_WATCH_H +#define __GBX_WATCH_H + +#include "gambas.h" +#include +#include + +#ifdef OS_OPENBSD +#include +#endif + +enum { + WP_NOTHING = 0, + WP_END = 1, + WP_OUTPUT = 2, + WP_ERROR = 4, + WP_TIMEOUT = 8 +}; + +typedef + struct { + int fd; + void (*callback_read)(); + void (*callback_write)(); + intptr_t param_read; + intptr_t param_write; + } + WATCH_CALLBACK; + +typedef + struct { + GB_TIMER *timer; + struct timeval timeout; + } + WATCH_TIMER; + +void WATCH_init(void); +void WATCH_exit(void); +void WATCH_transfer_watch(void); +void WATCH_transfer_timer(void); + +void WATCH_watch(int fd, int flag, void *callback, intptr_t param); +bool WATCH_one_loop(int); +void WATCH_loop(void); +void WATCH_wait(int); +int WATCH_loop_signal(const sigset_t *sig); +int WATCH_process(int fd_end, int fd_output, int fd_error, int timeout); +void WATCH_timer(void *t, int on); +double WATCH_get_timeout(GB_TIMER *timer); +void WATCH_little_sleep(void); + +#endif /* */ diff --git a/main/lib/Makefile.am b/main/lib/Makefile.am new file mode 100644 index 00000000..3d96845e --- /dev/null +++ b/main/lib/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = debug eval db compress vb option geom draw image gui gui.opengl gui.qt gui.qt.webkit gui.qt.opengl image.effect signal term complex data clipper gui.trayicon gui.webview @INOTIFY_DIR@ jit test hash +EXTRA_DIST = gb.component diff --git a/main/lib/clipper/LICENSE b/main/lib/clipper/LICENSE new file mode 100644 index 00000000..3793cdd9 --- /dev/null +++ b/main/lib/clipper/LICENSE @@ -0,0 +1,26 @@ +The Clipper Library (including Delphi, C++ & C# source code, other accompanying +code, examples and documentation), hereafter called "the Software", has been +released under the following license, terms and conditions: + +Boost Software License - Version 1.0 - August 17th, 2003 +http://www.boost.org/LICENSE_1_0.txt + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the Software covered by this license to use, reproduce, +display, distribute, execute, and transmit the Software, and to prepare +derivative works of the Software, and to permit third-parties to whom the +Software is furnished to do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including the +above license grant, this restriction and the following disclaimer, must be +included in all copies of the Software, in whole or in part, and all derivative +works of the Software, unless such copies or derivative works are solely in the +form of machine-executable object code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL +THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY +DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/main/lib/clipper/Makefile.am b/main/lib/clipper/Makefile.am new file mode 100644 index 00000000..5a0b6e1c --- /dev/null +++ b/main/lib/clipper/Makefile.am @@ -0,0 +1,19 @@ +COMPONENT = gb.clipper +include $(top_srcdir)/component.am + +noinst_LTLIBRARIES = libclipper.la +gblib_LTLIBRARIES = gb.clipper.la + +libclipper_la_LIBADD = +libclipper_la_LDFLAGS = -module @LD_FLAGS@ +libclipper_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS_OPT) -fexceptions + +libclipper_la_SOURCES = \ + clipper.hpp clipper.cpp + +gb_clipper_la_LIBADD = libclipper.la +gb_clipper_la_LDFLAGS = -module @LD_FLAGS@ +gb_clipper_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS) + +gb_clipper_la_SOURCES = \ + main.h main.cpp gb.geom.h c_clipper.cpp c_clipper.h diff --git a/main/lib/clipper/c_clipper.cpp b/main/lib/clipper/c_clipper.cpp new file mode 100644 index 00000000..fd0fe900 --- /dev/null +++ b/main/lib/clipper/c_clipper.cpp @@ -0,0 +1,485 @@ +/*************************************************************************** + + c_clipper.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_CLIPPER_CPP + +#include "main.h" +#include "gb.geom.h" +#include "c_clipper.h" + +#define SCALE 1048576.0 + +static IntPoint to_point_xy(double x, double y) +{ + return IntPoint(x * SCALE + 0.5, y * SCALE + 0.5); +} + +static IntPoint to_point(GEOM_POINTF *point) +{ + return to_point_xy(point->x, point->y); +} + +static GEOM_POINTF *from_point(IntPoint p) +{ + return GEOM.CreatePointF((double)p.X / SCALE, (double)p.Y / SCALE); +} + +static bool is_polygon_closed(Path &p) +{ + int n = p.size() - 1; + + if (n <= 1) + return false; + + return p[0].X == p[n].X && p[0].Y == p[n].Y; +} + +static void set_polygon_closed(Path &p, bool closed) +{ + if (is_polygon_closed(p) == closed) + return; + + if (closed) + p.push_back(p[0]); + else + p.erase(p.begin() + p.size() - 1); +} + +static bool to_polygons(Paths &polygons, GB_ARRAY array) +{ + int count; + CPOLYGON *p; + int i; + + if (GB.CheckObject(array)) + return true; + + count = GB.Array.Count(array); + if (count == 0) + return false; + + polygons.clear(); + + for(i = 0; i < count; i++) + { + p = *(CPOLYGON **)GB.Array.Get(array, i); + if (!p) + continue; + + polygons.push_back(*(p->poly)); + } + + return false; +} + +static GB_ARRAY from_polygons(Paths &polygons, bool closed) +{ + GB_ARRAY a; + CPOLYGON *p; + uint i; + + GB.Array.New(&a, GB.FindClass("Polygon"), polygons.size()); + + for (i = 0; i < polygons.size(); i++) + { + if (polygons[i].size() == 0) + continue; + + set_polygon_closed(polygons[i], closed); + + p = (CPOLYGON *)GB.New(GB.FindClass("Polygon"), NULL, NULL); + *(p->poly) = polygons[i]; + + *(GB_ARRAY *)GB.Array.Get(a, i) = p; + GB.Ref(p); + } + + return a; +} + +//--------------------------------------------------------------------------- + +#define THIS ((CPOLYGON *)_object) +#define POLY THIS->poly + +static bool _convert_polygon(CPOLYGON *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (type != GB.FindClass("PointF[]")) + return true; + + if (THIS) + { + // Polygon --> PointF[] + GB_ARRAY a; + int i; + GEOM_POINTF **data; + + GB.Array.New(&a, GB.FindClass("PointF"), POLY->size()); // + THIS->closed); + data = (GEOM_POINTF **)GB.Array.Get(a, 0); + for(i = 0; i < (int)POLY->size(); i++) + { + data[i] = from_point((*POLY)[i]); + GB.Ref(data[i]); + } + + /*if (closed) + { + data[i] = from_point((*POLY)[0]); + GB.Ref(data[i]); + }*/ + + conv->_object.value = a; + return false; + } + else + { + // PointF[] --> Polygon + CPOLYGON *p; + GB_ARRAY a = (GB_ARRAY)conv->_object.value; + int size = GB.Array.Count(a); + int i; + GEOM_POINTF **points; + + p = (CPOLYGON *)GB.New(GB.FindClass("Polygon"), NULL, NULL); + + points = (GEOM_POINTF **)GB.Array.Get(a, 0); + for (i = 0; i < size; i++) + { + if (!points[i]) + continue; + + p->poly->push_back(to_point(points[i])); + } + + conv->_object.value = p; + return false; + } +} + +BEGIN_METHOD(Polygon_new, GB_INTEGER size) + + POLY = new Path; + + if (!MISSING(size)) + POLY->resize(VARG(size)); + +END_METHOD + +BEGIN_METHOD_VOID(Polygon_free) + + delete POLY; + +END_METHOD + +BEGIN_METHOD(Polygon_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= (int)POLY->size()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(from_point((*POLY)[index])); + +END_METHOD + +BEGIN_METHOD(Polygon_put, GB_OBJECT point; GB_INTEGER index) + + int index = VARG(index); + GEOM_POINTF *point = (GEOM_POINTF *)VARG(point); + + if (GB.CheckObject(point)) + return; + + if (index < 0 || index >= (int)POLY->size()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + (*POLY)[index] = to_point(point); + +END_METHOD + +BEGIN_PROPERTY(Polygon_Count) + + GB.ReturnInteger(POLY->size()); + +END_PROPERTY + +BEGIN_PROPERTY(Polygon_Max) + + GB.ReturnInteger(POLY->size() - 1); + +END_PROPERTY + +BEGIN_PROPERTY(Polygon_Area) + + GB.ReturnFloat(Area(*POLY) / SCALE / SCALE); + +END_PROPERTY + +BEGIN_METHOD_VOID(Polygon_Reverse) + + ReversePath(*POLY); + +END_METHOD + +BEGIN_METHOD(Polygon_Simplify, GB_INTEGER fill) + + Paths result; + + SimplifyPolygon(*POLY, result, (PolyFillType)VARGOPT(fill, pftNonZero)); + + GB.ReturnObject(from_polygons(result, is_polygon_closed(*POLY))); + +END_METHOD + +BEGIN_METHOD(Polygon_Clean, GB_FLOAT distance) + + bool closed; + CPOLYGON *result = (CPOLYGON *)GB.New(GB.FindClass("Polygon"), NULL, 0); + + result->poly->resize(POLY->size()); + + closed = is_polygon_closed(*POLY); + + CleanPolygon(*POLY, *(result->poly), VARGOPT(distance, 1.415)); + + set_polygon_closed(*(result->poly), closed); + + GB.ReturnObject(result); + +END_METHOD + +BEGIN_METHOD(Polygon_Add, GB_FLOAT x; GB_FLOAT y) + + POLY->push_back(to_point_xy(VARG(x), VARG(y))); + +END_METHOD + +BEGIN_METHOD(Polygon_AddPoint, GB_OBJECT point) + + GEOM_POINTF *point = (GEOM_POINTF *)VARG(point); + + if (GB.CheckObject(point)) + return; + + POLY->push_back(to_point(point)); + +END_METHOD + +BEGIN_METHOD(Polygon_Remove, GB_INTEGER index; GB_INTEGER count) + + int index = VARG(index); + int count = VARGOPT(count, 1); + int index2; + + if (index < 0 || index >= (int)POLY->size()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + if (count < 0) + count = (int)POLY->size() - index; + + index2 = index + count; + + if (index2 > (int)POLY->size()) + index2 = POLY->size(); + + if (count == 1) + POLY->erase(POLY->begin() + index); + else + POLY->erase(POLY->begin() + index, POLY->begin() + index2); + +END_METHOD + +BEGIN_PROPERTY(Polygon_Orientation) + + GB.ReturnBoolean(Orientation(*POLY)); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Clipper_Offset, GB_OBJECT polygons; GB_FLOAT delta; GB_INTEGER join; GB_FLOAT limit; GB_BOOLEAN do_not_fix) + + Paths polygons; + Paths result; + + if (to_polygons(polygons, VARG(polygons))) + return; + + SimplifyPolygons(polygons, result, pftNonZero); + polygons = result; + + ClipperOffset co; + co.AddPaths(polygons, (JoinType)VARGOPT(join, jtSquare), etClosedPolygon); + co.MiterLimit = VARGOPT(limit, 0.0); + co.Execute(result, VARG(delta) * SCALE); + + //OffsetPaths(polygons, result, VARG(delta) * SCALE, (JoinType)VARGOPT(join, jtSquare), VARGOPT(limit, 0.0), !VARGOPT(do_not_fix, false)); + + GB.ReturnObject(from_polygons(result, true)); + +END_METHOD + + +BEGIN_METHOD(Clipper_Simplify, GB_OBJECT polygons; GB_INTEGER fill) + + Paths polygons; + Paths result; + + if (to_polygons(polygons, VARG(polygons))) + return; + + SimplifyPolygons(polygons, result, (PolyFillType)VARGOPT(fill, pftNonZero)); + + GB.ReturnObject(from_polygons(result, true)); + +END_METHOD + +BEGIN_METHOD(Clipper_Clean, GB_OBJECT polygons; GB_FLOAT distance) + + Paths polygons; + Paths result; + + if (to_polygons(polygons, VARG(polygons))) + return; + + result.resize(polygons.size()); + + CleanPolygons(polygons, result, VARGOPT(distance, 1.415)); + + GB.ReturnObject(from_polygons(result, true)); + +END_METHOD + +static void execute(ClipType action, PolyFillType fill, void *subject, void *clip) +{ + Clipper c; + Paths psubject, pclip, result; + PolyTree tree; + + if (to_polygons(psubject, subject)) + return; + + if (clip && to_polygons(pclip, clip)) + return; + + c.AddPaths(psubject, ptSubject, true); + if (clip) + c.AddPaths(pclip, ptClip, true); + + c.StrictlySimple(true); + c.Execute(action, tree, fill, fill); + ClosedPathsFromPolyTree(tree, result); + + GB.ReturnObject(from_polygons(result, true)); +} + +BEGIN_METHOD(Clipper_Union, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctUnion, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +BEGIN_METHOD(Clipper_Intersection, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctIntersection, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +BEGIN_METHOD(Clipper_Difference, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctDifference, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +BEGIN_METHOD(Clipper_ExclusiveOr, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctXor, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC PolygonDesc[] = +{ + GB_DECLARE("Polygon", sizeof(CPOLYGON)), + + GB_METHOD("_new", NULL, Polygon_new, "[(Size)i]"), + GB_METHOD("_free", NULL, Polygon_free, NULL), + + GB_METHOD("_get", "PointF", Polygon_get, "(Index)i"), + GB_METHOD("_put", NULL, Polygon_put, "(Point)PointF;(Index)i"), + + GB_PROPERTY_READ("Count", "i", Polygon_Count), + GB_PROPERTY_READ("Max", "i", Polygon_Max), + GB_PROPERTY_READ("Area", "f", Polygon_Area), + GB_PROPERTY_READ("Orientation", "b", Polygon_Orientation), + + GB_METHOD("Reverse", NULL, Polygon_Reverse, NULL), + GB_METHOD("Simplify", "Polygon[]", Polygon_Simplify, "[(Fill)i]"), + GB_METHOD("Clean", "Polygon", Polygon_Clean, "[(Distance)f]"), + + GB_METHOD("Add", NULL, Polygon_Add, "(X)f(Y)f"), + GB_METHOD("AddPoint", NULL, Polygon_AddPoint, "(Point)PointF;"), + GB_METHOD("Remove", NULL, Polygon_Remove, "(Index)i[(Count)i]"), + + GB_INTERFACE("_convert", &_convert_polygon), + + GB_END_DECLARE +}; + +GB_DESC ClipperDesc[] = +{ + GB_DECLARE_VIRTUAL("Clipper"), + + GB_CONSTANT("JoinMiter", "i", jtMiter), + GB_CONSTANT("JoinSquare", "i", jtSquare), + GB_CONSTANT("JoinRound", "i", jtRound), + + GB_CONSTANT("FillEvenOdd", "i", pftEvenOdd), + GB_CONSTANT("FillWinding", "i", pftNonZero), + GB_CONSTANT("FillNonZero", "i", pftNonZero), + GB_CONSTANT("FillPositive", "i", pftPositive), + GB_CONSTANT("FillNegative", "i", pftNegative), + + GB_STATIC_METHOD("Offset", "Polygon[]", Clipper_Offset, "(Polygons)Polygon[];(Delta)f[(Join)i(Limit)f(DoNotFix)b]"), + GB_STATIC_METHOD("Simplify", "Polygon[]", Clipper_Simplify, "(Polygons)Polygon[];[(Fill)i]"), + GB_STATIC_METHOD("Clean", "Polygon[]", Clipper_Clean, "(Polygons)Polygon[];[(Distance)f]"), + + GB_STATIC_METHOD("Union", "Polygon[]", Clipper_Union, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + GB_STATIC_METHOD("Intersection", "Polygon[]", Clipper_Intersection, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + GB_STATIC_METHOD("Difference", "Polygon[]", Clipper_Difference, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + GB_STATIC_METHOD("ExclusiveOr", "Polygon[]", Clipper_ExclusiveOr, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/clipper/c_clipper.h b/main/lib/clipper/c_clipper.h new file mode 100644 index 00000000..2ef5f1c2 --- /dev/null +++ b/main/lib/clipper/c_clipper.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_clipper.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_CLIPPER_H +#define __C_CLIPPER_H + +#include "gambas.h" +#include "clipper.hpp" + +using namespace ClipperLib; + +#ifndef __C_CLIPPER_CPP + +extern GB_DESC PolygonDesc[]; +extern GB_DESC ClipperDesc[]; + +#endif + +typedef + struct { + GB_BASE ob; + Path *poly; + //bool closed; + } + CPOLYGON; + + +#endif diff --git a/main/lib/clipper/clipper.cpp b/main/lib/clipper/clipper.cpp new file mode 100644 index 00000000..5c982df4 --- /dev/null +++ b/main/lib/clipper/clipper.cpp @@ -0,0 +1,4630 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.2 * +* Date : 27 February 2017 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2017 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +struct TEdge { + IntPoint Bot; + IntPoint Curr; //current (updated for every new scanbeam) + IntPoint Top; + double Dx; + PolyType PolyTyp; + EdgeSide Side; //side only refers to current side of solution poly + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; +}; + +struct OutPt; + +//OutRec: contains a path in the clipping solution. Edges in the AEL will +//carry a pointer to an OutRec when they are part of the clipping solution. +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +struct LocMinSorter +{ + inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) + { + return locMin2.Y < locMin1.Y; + } +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return 0; +} +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs[0] != AllNodes[0]) result--; + return result; +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +PolyNode::PolyNode(): Parent(0), Index(0), m_IsOpen(false) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.push_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return 0; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + ulong64 lo; + long64 hi; + + Int128(long64 _lo = 0) + { + lo = (ulong64)_lo; + if (_lo < 0) hi = -1; else hi = 0; + } + + + Int128(const Int128 &val): lo(val.lo), hi(val.hi){} + + Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + + Int128& operator = (const long64 &val) + { + lo = (ulong64)val; + if (val < 0) hi = -1; else hi = 0; + return *this; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const + { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi != val.hi) + return hi > val.hi; + else + return lo > val.lo; + } + + bool operator < (const Int128 &val) const + { + if (hi != val.hi) + return hi < val.hi; + else + return lo < val.lo; + } + + bool operator >= (const Int128 &val) const + { return !(*this < val);} + + bool operator <= (const Int128 &val) const + { return !(*this > val);} + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (lo < rhs.lo) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + *this += -rhs; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result -= rhs; + return result; + } + + Int128 operator-() const //unary negation + { + if (lo == 0) + return Int128(-hi, 0); + else + return Int128(~hi, ~lo + 1); + } + + operator double() const + { + const double shift64 = 18446744073709551616.0; //2^64 + if (hi < 0) + { + if (lo == 0) return (double)hi * shift64; + else return -(double)(~lo + ~hi * shift64); + } + else + return (double)(lo + hi * shift64); + } + +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (long64 lhs, long64 rhs) +{ + bool negate = (lhs < 0) != (rhs < 0); + + if (lhs < 0) lhs = -lhs; + ulong64 int1Hi = ulong64(lhs) >> 32; + ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); + + if (rhs < 0) rhs = -rhs; + ulong64 int2Hi = ulong64(rhs) >> 32; + ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + Int128 tmp; + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); + if (tmp.lo < b) tmp.hi++; + if (negate) tmp = -tmp; + return tmp; +}; +#endif + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +bool Orientation(const Path &poly) +{ + return Area(poly) >= 0; +} +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutPt *op) +{ + const OutPt *startOp = op; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != startOp); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + return Area(outRec.Pts); +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos +//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf +int PointInPolygon(const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt* startOp = op; + for(;;) + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + OutPt* op = OutPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res > 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == + Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); + else +#endif + return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == + (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(TEdge &e) +{ + return e.Dx == HORIZONTAL; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint pt1, const IntPoint pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline void SetDx(TEdge &e) +{ + cInt dy = (e.Top.Y - e.Bot.Y); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; +} +//--------------------------------------------------------------------------- + +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + +inline cInt TopX(TEdge &edge, const cInt currentY) +{ + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + + double b1, b2; + if (Edge1.Dx == Edge2.Dx) + { + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; + } + else if (Edge1.Dx == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Dx == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); + else + ip.X = Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1, *pp2; + pp1 = pp; + do { + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == 0) return; + pp->Prev->Next = 0; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->Next; + delete tmpPp; + } +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + //std::memset(e, 0, sizeof(TEdge)); + *e = { 0 }; + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + SetDx(e); + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + std::swap(e.Top.X, e.Bot.X); +#ifdef use_xyz + std::swap(e.Top.Z, e.Bot.Z); +#endif +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + + if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && + std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = 0; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = 0; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = 0; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) +{ + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +ClipperBase::ClipperBase() //constructor +{ + m_CurrentLM = m_MinimaList.begin(); //begin() == end() here + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw clipperException("Coordinate outside allowed range"); + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) +{ + TEdge *Result = E; + TEdge *Horz = 0; + + if (E->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + if (NextIsForward) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } + else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + + if (E == Result) + { + if (NextIsForward) Result = E->Next; + else Result = E->Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (NextIsForward) + E = Result->Next; + else + E = Result->Prev; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.push_back(locMin); + } + return Result; + } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [highI +1]; + + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + for (;;) + { + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; + E = E->Next; + } + m_MinimaList.push_back(locMin); + m_edges.push_back(edges); + return true; + } + + m_edges.push_back(edges); + bool leftBoundIsForward; + TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E->Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = 0; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = 0; + m_MinimaList.push_back(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + TEdge* edges = m_edges[i]; + delete [] edges; + } + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList.begin(); + if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue + //reset all edges ... + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) + { + InsertScanbeam(lm->Y); + TEdge* e = lm->LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm->RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + m_MinimaList.clear(); + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) +{ + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); + ++m_CurrentLM; + return true; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + MinimaList::iterator lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm != m_MinimaList.end()) + { + //todo - needs fixing for open paths + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + ++lm; + } + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopScanbeam(cInt &Y) +{ + if (m_Scanbeam.empty()) return false; + Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted + if (AelPrev) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +OutRec* ClipperBase::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size() - 1; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if (Edge1->NextInAEL == Edge2) + { + TEdge* Next = Edge2->NextInAEL; + if (Next) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if (Edge2->NextInAEL == Edge1) + { + TEdge* Next = Edge1->NextInAEL; + if (Next) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; + else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) +{ + if (!e->NextInLML) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::LocalMinimaPending() +{ + return (m_CurrentLM != m_MinimaList.end()); +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) : ClipperBase() //constructor +{ + m_ExecuteLocked = false; + m_UseFullRange = false; + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(ZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) +{ + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is needed for open path clipping."); + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + bool succeeded = true; + try { + Reset(); + m_Maxima = MaximaList(); + m_SortedEdges = 0; + + succeeded = true; + cInt botY, topY; + if (!PopScanbeam(botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(topY) || LocalMinimaPending()) + { + ProcessHorizontals(); + ClearGhostJoins(); + if (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + InsertLocalMinimaIntoAEL(botY); + } + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + //fix orientations ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts) continue; + if (outRec->IsOpen) + FixupOutPolyline(*outRec); + else + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); + } + + ClearJoins(); + ClearGhostJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (Abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0 && prevE->Top.Y < Pt.Y && e->Top.Y < Pt.Y) + { + cInt xPrev = TopX(*prevE, Pt.Y); + cInt xE = TopX(*e, Pt.Y); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::PopEdgeFromSEL(TEdge *&edge) +{ + if (!m_SortedEdges) return false; + edge = m_SortedEdges; + DeleteFromSEL(m_SortedEdges); + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearGhostJoins() +{ + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = 0; + j->OffPt = OffPt; + m_GhostJoins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + const LocalMinimum *lm; + while (PopLocalMinima(botY, lm)) + { + TEdge* lb = lm->LeftBound; + TEdge* rb = lm->RightBound; + + OutPt *Op1 = 0; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + + if (rb) + { + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + InsertScanbeam(rb->NextInLML->Top.Y); + } + else InsertScanbeam( rb->Top.Y ); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) + { + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + { + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); + } + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) +{ + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) +{ + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + { + AddLocalMaxPoly(e1, e2, Pt); + } + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + { + AddLocalMinPoly(e1, e2, Pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + SwapSides( *e1, *e2 ); + } +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) +{ + TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; + } + e2 = e2->PrevInAEL; + } + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (OutRec1RightOfOutRec2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + } + + outRec1->BottomPt = 0; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = e1->Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); + e->OutIdx = outRec->Idx; + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; + return newOp; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::GetLastOutPt(TEdge *e) +{ + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) + ProcessHorizontal(horzEdge); +} +//------------------------------------------------------------------------------ + +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + return e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + return e->Prev; + else return 0; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPairEx(TEdge *e) +{ + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge) +{ + Direction dir; + cInt horzLeft, horzRight; + bool IsOpen = (horzEdge->WindDelta == 0); + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; + if (m_Maxima.size() > 0) + { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) + { + + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (m_Maxima.size() > 0) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + maxIt++; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + maxRit++; + } + } + }; + + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { +#ifdef use_xyz + if (dir == dLeftToRight) SetZ(e->Curr, *horzEdge, *e); + else SetZ(e->Curr, *e, *horzEdge); +#endif + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + } + + //OK, so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (horzEdge->OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + + if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt); + } + TEdge* eNext = GetNextInAEL(e, dir); + SwapPositionsInAEL( horzEdge, e ); + e = eNext; + } //end while(e) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + } //end for (;;) + + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Top); + } + + if (horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); + else return false; + } + catch(...) + { + m_SortedEdges = 0; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = 0; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; + else break; + } + while ( isModified ); + m_SortedEdges = 0; //important +} +//------------------------------------------------------------------------------ + + +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPairEx(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPairEx(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; +#ifdef use_xyz + e->Curr.Z = topY == e->Top.Y ? e->Top.Z : (topY == e->Bot.Y ? e->Bot.Z : 0); +#endif + } + + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + m_Maxima.sort(); + ProcessHorizontals(); + m_Maxima.clear(); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolyline(OutRec &outrec) +{ + OutPt *pp = outrec.Pts; + OutPt *lastPP = pp->Prev; + while (pp != lastPP) + { + pp = pp->Next; + if (pp->Pt == pp->Prev->Pt) + { + if (pp == lastPP) lastPP = pp->Prev; + OutPt *tmpPP = pp->Prev; + tmpPP->Next = pp->Next; + pp->Next->Prev = tmpPP; + delete pp; + pp = tmpPP; + } + } + + if (pp == pp->Prev) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = 0; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) + { + pg.push_back(p->Pt); + p = p->Prev; + } + polys.push_back(pg); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.push_back(pn); + outRec->PolyNd = pn; + pn->Parent = 0; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.push_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //tests if NewOutRec contains the polygon before reassigning FirstLeft + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + + if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) + outRec->FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) + outRec->FirstLeft = OuterOutRec; + else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) + outRec->FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && firstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + Join* join = m_Joins[i]; + + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = 0; + outRec2 = CreateOutRec(); + outRec2->Pts = join->OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec1 contains outRec2 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec2 contains outRec1 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) +{ + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) + { + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if (endType == etClosedPolygon && j < 2) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op || outrec->IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + out_polys.resize(in_polys.size()); + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + solution.push_back(quad); + } +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void TranslatePath(const Path& input, Path& output, const IntPoint delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } + } + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //ClipperLib namespace diff --git a/main/lib/clipper/clipper.hpp b/main/lib/clipper/clipper.hpp new file mode 100644 index 00000000..df1f8137 --- /dev/null +++ b/main/lib/clipper/clipper.hpp @@ -0,0 +1,406 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.2 * +* Date : 27 February 2017 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2017 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#define CLIPPER_VERSION "6.4.2" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +#define use_lines + +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 + typedef int cInt; + static cInt const loRange = 0x7FFF; + static cInt const hiRange = 0x7FFF; +#else + typedef signed long long cInt; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode(); + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + PolyNode* GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; +private: + //PolyNode& operator =(PolyNode& other); + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; +}; + +class PolyTree: public PolyNode +{ +public: + ~PolyTree(){ Clear(); }; + PolyNode* GetFirst() const; + void Clear(); + int Total() const; +private: + //PolyTree& operator =(PolyTree& other); + PolyNodes AllNodes; + friend class Clipper; //to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec* CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + + typedef std::vector MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase +{ +public: + Clipper(int initOptions = 0); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc); +#endif +protected: + virtual bool ExecuteInternal(); +private: + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::list MaximaList; + MaximaList m_Maxima; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge); + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void DoMaxima(TEdge *e); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + OutPt* GetLastOutPt(TEdge *e); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec); + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/main/lib/clipper/gb.clipper.component b/main/lib/clipper/gb.clipper.component new file mode 100644 index 00000000..7522f1a4 --- /dev/null +++ b/main/lib/clipper/gb.clipper.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.clipper +Author=Benoît Minisini +State=Stable diff --git a/main/lib/clipper/gb.geom.h b/main/lib/clipper/gb.geom.h new file mode 120000 index 00000000..2ae286cf --- /dev/null +++ b/main/lib/clipper/gb.geom.h @@ -0,0 +1 @@ +../geom/gb.geom.h \ No newline at end of file diff --git a/main/lib/clipper/main.cpp b/main/lib/clipper/main.cpp new file mode 100644 index 00000000..427944c4 --- /dev/null +++ b/main/lib/clipper/main.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "c_clipper.h" +#include "main.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +GEOM_INTERFACE GEOM EXPORT; + +GB_DESC *GB_CLASSES [] EXPORT = +{ + PolygonDesc, + ClipperDesc, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.geom"; + +int EXPORT GB_INIT(void) +{ + GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/main/lib/clipper/main.h b/main/lib/clipper/main.h new file mode 100644 index 00000000..507e22df --- /dev/null +++ b/main/lib/clipper/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.geom.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern "C" GB_INTERFACE GB; +extern "C" GEOM_INTERFACE GEOM; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/complex/Makefile.am b/main/lib/complex/Makefile.am new file mode 100644 index 00000000..bb78945c --- /dev/null +++ b/main/lib/complex/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.complex +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.complex.la + +gb_complex_la_LIBADD = @MATH_LIB@ +gb_complex_la_LDFLAGS = -module @LD_FLAGS@ +gb_complex_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_complex_la_SOURCES = \ + ccomplex.h ccomplex.c \ + main.h main.c + + diff --git a/main/lib/complex/ccomplex.c b/main/lib/complex/ccomplex.c new file mode 100644 index 00000000..1d1be7e8 --- /dev/null +++ b/main/lib/complex/ccomplex.c @@ -0,0 +1,543 @@ +/*************************************************************************** + + ccomplex.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOMPLEX_C + +#include "ccomplex.h" + +#define THIS ((CCOMPLEX *)_object) +#define RE(_c) ((_c)->v[0]) +#define IM(_c) ((_c)->v[1]) +#define ABS(_c) (hypot(RE(_c), IM(_c))) +#define ABS2(_c) (RE(_c) * RE(_c) + IM(_c) * IM(_c)) +#define ZERO(_c) (RE(_c) == 0.0 && IM(_c) == 0.0) + + +//---- Complex number creation ---------------------------------------------- + +CCOMPLEX *COMPLEX_create(double re, double im) +{ + static GB_CLASS CLASS_Complex = (GB_CLASS)NULL; + CCOMPLEX *c; + + if (!isfinite(re) || !isfinite(im)) + { + GB.Error(GB_ERR_MATH); + return NULL; + } + + if (!CLASS_Complex) + CLASS_Complex = GB.FindClass("Complex"); + + c = (CCOMPLEX *)GB.New(CLASS_Complex, NULL, NULL); + c->v[0] = re; + c->v[1] = im; + + return c; +} + +//#define COMPLEX_make(_a, _re, _im) (((_a)->ob.ref <= 1) ? ((_a)->v[0] = (_re), (_a)->v[1] = (_im), (_a)) : COMPLEX_create((_re), (_im))) + +static inline CCOMPLEX *COMPLEX_make(CCOMPLEX *a, const double re, const double im) +{ + if (a->ob.ref <= 1) + { + if (!isfinite(re) || !isfinite(im)) + { + GB.Error(GB_ERR_MATH); + return NULL; + } + + a->v[0] = re; + a->v[1] = im; + return a; + } + else + return COMPLEX_create(re, im); +} + +CCOMPLEX *COMPLEX_push_complex(double value) +{ + return COMPLEX_create(0, value); +} + +//---- Arithmetic operators ------------------------------------------------- + +static CCOMPLEX *_addf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, RE(a) + f, IM(a)); +} + +static CCOMPLEX *_add(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, RE(a) + RE(b), IM(a) + IM(b)); +} + +static CCOMPLEX *_subf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + return COMPLEX_make(a, f - RE(a), -IM(a)); + else + return COMPLEX_make(a, RE(a) - f, IM(a)); +} + +static CCOMPLEX *_sub(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, RE(a) - RE(b), IM(a) - IM(b)); +} + +static CCOMPLEX *_mulf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, RE(a) * f, IM(a) * f); +} + +static CCOMPLEX *_mul(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, RE(a) * RE(b) - IM(a) * IM(b), RE(a) * IM(b) + IM(a) * RE(b)); +} + +static CCOMPLEX *_divf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + { + if (ZERO(a)) + return NULL; + + double s = ABS2(a); + double re, im; + + re = RE(a) / s; + im = -IM(a) / s; + + return COMPLEX_make(a, re * f, im * f); + } + else + { + if (f == 0.0) + return NULL; + + return COMPLEX_make(a, RE(a) / f, IM(a) / f); + } +} + +static CCOMPLEX *_div(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + double ar = RE(a), ai = IM(a); + double br = RE(b), bi = IM(b); + + if (br == 0.0 && bi == 0.0) + return NULL; + + double s = 1.0 / ( br * br + bi * bi ); + + double zr = (ar * br + ai * bi) * s; + double zi = (ai * br - ar * bi) * s; + + return COMPLEX_make(a, zr, zi); + +} + +static int _equal(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return RE(a) == RE(b) && IM(a) == IM(b); +} + +static int _equalf(CCOMPLEX *a, double f, bool invert) +{ + return RE(a) == f && IM(a) == 0; +} + +static double _fabs(CCOMPLEX *a) +{ + return ABS(a); +} + +static CCOMPLEX *_neg(CCOMPLEX *a) +{ + return COMPLEX_make(a, -RE(a), -IM(a)); +} + +double _logabs(CCOMPLEX *a) +{ + double xabs = fabs(RE(a)); + double yabs = fabs(IM(a)); + double max, u; + + if (xabs >= yabs) + { + max = xabs; + u = yabs / xabs; + } + else + { + max = yabs; + u = xabs / yabs; + } + + /* Handle underflow when u is close to 0 */ + return log(max) + 0.5 * log1p (u * u); +} + +static double _arg(CCOMPLEX *a) +{ + if (ZERO(a)) + return 0.0; + else + return atan2(IM(a), RE(a)); +} + +static CCOMPLEX *_powi(CCOMPLEX *a, int i) +{ + CCOMPLEX *r; + bool inv; + + inv = i < 0; + i = abs(i); + + if (i == 2) + r = _mul(a, a, FALSE); + else if (i == 3) + { + r = COMPLEX_create(RE(a), IM(a)); + r = _mul(r, a, FALSE); + r = _mul(r, a, FALSE); + } + else if (i == 4) + { + a = _mul(a, a, FALSE); + r = _mul(a, a, FALSE); + } + else + r = COMPLEX_make(a, RE(a), IM(a)); + + if (inv) + return _divf(r, 1, TRUE); + else + return r; +} + +static CCOMPLEX *_pow(CCOMPLEX *a, CCOMPLEX *b) +{ + if (RE(a) == 0.0 && IM(a) == 0.0) + { + if (RE(b) == 0.0 && IM(b) == 0.0) + return COMPLEX_make(a, 1.0, 0.0); + else + return COMPLEX_make(a, 0.0, 0.0); + } + else if (IM(b) == 0.0) + { + if (RE(b) >= 4.0 && RE(b) <= -4.0 && RE(b) == (int)RE(b)) + return _powi(a, (int)RE(b)); + } + + double logr = _logabs (a); + double theta = _arg(a); + + double br = RE(b), bi = IM(b); + + double rho = exp(logr * br - bi * theta); + double beta = theta * br + bi * logr; + + return COMPLEX_make(a, rho * cos (beta), rho * sin (beta)); +} + +static CCOMPLEX *_powf(CCOMPLEX *a, double b) +{ + if (RE(a) == 0.0 && IM(a) == 0.0) + { + if (b == 0.0) + return COMPLEX_make(a, 1.0, 0.0); + else + return COMPLEX_make(a, 0.0, 0.0); + } + else if (b == 0.0) + return COMPLEX_make(a, 1.0, 0.0); + else if (b <= 4.0 && b >= -4.0 && b == (int)b) + return _powi(a, (int)b); + else + { + double logr = _logabs (a); + double theta = _arg (a); + double rho = exp (logr * b); + double beta = theta * b; + + return COMPLEX_make(a, rho * cos(beta), rho * sin(beta)); + } +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .add = (void *)_add, + .addf = (void *)_addf, + .sub = (void *)_sub, + .subf = (void *)_subf, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .div = (void *)_div, + .divf = (void *)_divf, + .pow = (void *)_pow, + .powf = (void *)_powf, + .fabs = (void *)_fabs, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +char *COMPLEX_to_string(double real, double imag, bool local) +{ + char buffer[64]; + char *p; + char *str; + int len; + + if (real == 0.0 && imag == 0.0) + return GB.NewString("0", 1); + + p = buffer; + + if (real != 0.0) + { + GB.NumberToString(local, real, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + + if (imag != 0.0) + { + if (imag < 0.0) + { + *p++ = '-'; + imag = (-imag); + } + else if (p != buffer) + *p++ = '+'; + + if (imag != 1.0) + { + GB.NumberToString(local, imag, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + *p++ = 'i'; + } + + return GB.NewString(buffer, p - buffer); +} + +static bool _convert(CCOMPLEX *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + if (IM(a)) + return TRUE; + conv->_float.value = RE(a); + return FALSE; + + case GB_T_SINGLE: + if (IM(a)) + return TRUE; + conv->_single.value = RE(a); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + if (IM(a)) + return TRUE; + conv->_integer.value = RE(a); + return FALSE; + + case GB_T_LONG: + if (IM(a)) + return TRUE; + conv->_long.value = RE(a); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = COMPLEX_to_string(RE(a), IM(a), type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + switch(type) + { + case GB_T_FLOAT: + conv->_object.value = COMPLEX_create(conv->_float.value, 0); + return FALSE; + + case GB_T_SINGLE: + conv->_object.value = COMPLEX_create(conv->_single.value, 0); + return FALSE; + + case GB_T_LONG: + conv->_object.value = COMPLEX_create((double)conv->_long.value, 0); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_object.value = COMPLEX_create(conv->_integer.value, 0); + return FALSE; + + default: + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Complex_new, GB_FLOAT real; GB_FLOAT imag) + + THIS->v[0] = VARGOPT(real, 0.0); + THIS->v[1] = VARGOPT(imag, 0.0); + +END_METHOD + + +BEGIN_METHOD(Complex_call, GB_FLOAT real; GB_FLOAT imag) + + GB.ReturnObject(COMPLEX_create(VARG(real), VARG(imag))); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Copy) + + GB.ReturnObject(COMPLEX_create(RE(THIS), IM(THIS))); + +END_METHOD + + +BEGIN_METHOD(Complex_Polar, GB_FLOAT abs; GB_FLOAT arg) + + double mod = VARG(abs); + double arg = VARG(arg); + + GB.ReturnObject(COMPLEX_create(cos(arg) * mod, sin(arg) * mod)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Arg) + + GB.ReturnFloat(_arg(THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs) + + GB.ReturnFloat(ABS(THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs2) + + GB.ReturnFloat(ABS2(THIS)); + +END_METHOD + + +BEGIN_PROPERTY(Complex_Real) + + if (READ_PROPERTY) + GB.ReturnFloat(RE(THIS)); + else + THIS->v[0] = VPROP(GB_FLOAT); + +END_PROPERTY + + +BEGIN_PROPERTY(Complex_Imag) + + if (READ_PROPERTY) + GB.ReturnFloat(IM(THIS)); + else + THIS->v[1] = VPROP(GB_FLOAT); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Complex_Inv) + + GB.ReturnObject(_divf(THIS, 1, TRUE)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Conj) + + GB.ReturnObject(COMPLEX_create(RE(THIS), -IM(THIS))); + +END_METHOD + + +BEGIN_METHOD(Complex_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(COMPLEX_to_string(RE(THIS), IM(THIS), VARGOPT(local, FALSE)))); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC ComplexDesc[] = +{ + GB_DECLARE("Complex", sizeof(CCOMPLEX)), + + // Utility Methods + GB_METHOD("_new", NULL, Complex_new, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("_call", "Complex", Complex_call, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("Polar", "Complex", Complex_Polar, "[(Abs)f(Arg)f]"), + + GB_METHOD("Copy", "Complex", Complex_Copy, NULL), + GB_METHOD("ToString", "s", Complex_ToString, "[(Local)b]"), + + GB_PROPERTY("Real", "f", Complex_Real), + GB_PROPERTY("Imag", "f", Complex_Imag), + + GB_METHOD("Abs2", "f", Complex_Abs2, NULL), + GB_METHOD("Arg", "f", Complex_Arg, NULL), + + GB_METHOD("Conj", "Complex", Complex_Conj, NULL), + GB_METHOD("Inv", "Complex", Complex_Inv, NULL), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/main/lib/complex/ccomplex.h b/main/lib/complex/ccomplex.h new file mode 100644 index 00000000..83c4394d --- /dev/null +++ b/main/lib/complex/ccomplex.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + ccomplex.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOMPLEX_H +#define __CCOMPLEX_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CDEBUG_C +extern GB_DESC ComplexDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + double v[2]; + } + CCOMPLEX; + +CCOMPLEX *COMPLEX_create(double re, double im); +CCOMPLEX *COMPLEX_push_complex(double value); + +#endif diff --git a/main/lib/complex/gb.complex.component b/main/lib/complex/gb.complex.component new file mode 100644 index 00000000..d362d409 --- /dev/null +++ b/main/lib/complex/gb.complex.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.component +Author=Benoît Minisini +Implements=Complex diff --git a/main/lib/complex/main.c b/main/lib/complex/main.c new file mode 100644 index 00000000..7b69a7fd --- /dev/null +++ b/main/lib/complex/main.c @@ -0,0 +1,58 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "ccomplex.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ComplexDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "PUSH_COMPLEX")) + { + *value = (void *)COMPLEX_push_complex; + return TRUE; + } + else + return FALSE; +} + + diff --git a/main/lib/complex/main.h b/main/lib/complex/main.h new file mode 100644 index 00000000..fa8be1b0 --- /dev/null +++ b/main/lib/complex/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/compress/CCompress.c b/main/lib/compress/CCompress.c new file mode 100644 index 00000000..c3abefc2 --- /dev/null +++ b/main/lib/compress/CCompress.c @@ -0,0 +1,187 @@ +/*************************************************************************** + + CCompress.c + + (c) 2003-2004 Daniel Campos Fern�dez + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOMPRESS_C + + +#include "CCompress.h" +#include "main.h" +#include + +#define Check_Driver() if (!THIS->driver) { GB.Error("No driver specified"); return; } + +#define Check_Level() if (!MISSING(Level)) \ + level=VARG(Level); \ + else \ + level=THIS->driver->default_compression(); \ + \ + if ( (level < THIS->driver->min_compression()) || (level > THIS->driver->max_compression()) ) \ + if (level != THIS->driver->default_compression()) \ + { \ + GB.Error("Invalid compression level"); \ + return; \ + } + +//************************************************************************* +//############################### COMPRESS ################################ +//************************************************************************* + +/************************************************* +Gambas object "Constructor" +*************************************************/ +BEGIN_METHOD_VOID(CCOMPRESS_new) + + THIS->driver=NULL; + THIS->stream.desc=NULL; + +END_METHOD +/************************************************* +Gambas object "Destructor" +*************************************************/ +BEGIN_METHOD_VOID(CCOMPRESS_free) + + + +END_METHOD + +BEGIN_METHOD (CCOMPRESS_String,GB_STRING Source;GB_INTEGER Level;GB_BOOLEAN AllowGrow;) + + int level; + char *target=NULL; + unsigned int lent; + int allow=0; + + Check_Driver(); + Check_Level(); + lent=0; + if (!MISSING(AllowGrow)) + if ( VARG(AllowGrow) ) allow=1; + + THIS->driver->Compress.String(&target,&lent,STRING(Source),LENGTH(Source),level); + + if (!lent) { GB.ReturnVoidString(); return; } + if ( (!allow) && (LENGTH(Source)<=lent) ) + { + if (target) GB.Free(POINTER(&target)); + GB.ReturnNewString (STRING(Source),LENGTH(Source)); + return; + } + + GB.ReturnNewString (target,lent); + if (target) GB.Free(POINTER(&target)); + +END_METHOD + + +BEGIN_METHOD (CCOMPRESS_File,GB_STRING Source;GB_STRING Target;GB_INTEGER Level;) + + int level; + + Check_Driver(); + Check_Level(); + THIS->driver->Compress.File(STRING(Source),STRING(Target),level); + + + + +END_METHOD + +BEGIN_METHOD (CCOMPRESS_Open,GB_STRING Path;GB_INTEGER Level;) + + int level; + + Check_Driver(); + Check_Level(); + + if (THIS->stream.desc) + { + GB.Error ("File is already opened"); + return; + } + + THIS->driver->Compress.Open(STRING(Path),level,&THIS->stream); + +END_METHOD + +BEGIN_PROPERTY ( COMPRESS_Min ) + + if (!THIS->driver) { GB.ReturnInteger(0); return; } + GB.ReturnInteger(THIS->driver->min_compression()); + +END_PROPERTY + +BEGIN_PROPERTY ( COMPRESS_Max ) + + if (!THIS->driver) { GB.ReturnInteger(0); return; } + GB.ReturnInteger(THIS->driver->max_compression()); + +END_PROPERTY + +BEGIN_PROPERTY ( COMPRESS_Default ) + + if (!THIS->driver) { GB.ReturnInteger(0); return; } + GB.ReturnInteger(THIS->driver->default_compression()); + +END_PROPERTY + +BEGIN_PROPERTY ( COMPRESS_Type ) + + if (READ_PROPERTY) + { + if (!THIS->driver) { GB.ReturnVoidString(); return; } + GB.ReturnNewZeroString(THIS->driver->name); + return; + } + + if (THIS->stream.desc) { GB.Error("Type can not be changed while the stream is opened"); return; } + + if (!(THIS->driver = COMPRESS_GetDriver(GB.ToZeroString(PROP(GB_STRING))))) + GB.Error("Cannot find driver &1", GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +/******************************************************************* +Interface declaration +*******************************************************************/ +GB_DESC CCompressDesc[] = +{ + + GB_DECLARE("Compress", sizeof(CCOMPRESS)), + + GB_INHERITS("Stream"), + + GB_PROPERTY_READ("Min","i",COMPRESS_Min), + GB_PROPERTY_READ("Max","i",COMPRESS_Max), + GB_PROPERTY_READ("Default","i",COMPRESS_Default), + GB_PROPERTY("Type","s",COMPRESS_Type), + + GB_METHOD("_new", NULL, CCOMPRESS_new,NULL), + GB_METHOD("_free", NULL, CCOMPRESS_free, NULL), + + GB_METHOD("String","s",CCOMPRESS_String,"(Source)s[(Level)i(AllowGrow)b]"), + GB_METHOD("File",NULL,CCOMPRESS_File,"(Source)s(Target)s[(Level)i]"), + GB_METHOD("Open",NULL,CCOMPRESS_Open,"(Path)s[(Level)i]"), + + GB_END_DECLARE +}; + diff --git a/main/lib/compress/CCompress.h b/main/lib/compress/CCompress.h new file mode 100644 index 00000000..7cdbd4b7 --- /dev/null +++ b/main/lib/compress/CCompress.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + CCompress.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOMPRESS_H +#define __CCOMPRESS_H + +#include "gambas.h" +#include "gb.compress.h" + +#ifndef __CCOMPRESS_C + + +extern GB_DESC CCompressDesc[]; +extern GB_STREAM_DESC CCompressStream; + +#else + +#define THIS ((CCOMPRESS *)_object) + +#endif +typedef struct +{ + GB_BASE ob; + GB_STREAM stream; + COMPRESS_DRIVER *driver; + + +} CCOMPRESS; + + +#endif diff --git a/main/lib/compress/CUncompress.c b/main/lib/compress/CUncompress.c new file mode 100644 index 00000000..042e6f8a --- /dev/null +++ b/main/lib/compress/CUncompress.c @@ -0,0 +1,149 @@ +/*************************************************************************** + + CUncompress.c + + (c) 2003-2004 Daniel Campos Fern�dez + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CUNCOMPRESS_C + +#include "CUncompress.h" +#include "main.h" +#include + +#define Check_Driver() if (!THIS->driver) { GB.Error("No driver specified"); return; } + + +//************************************************************************* +//#################### INITIALIZATION AND DESTRUCTION ##################### +//************************************************************************* +/************************************************* + Class "Constructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_init) + + + +END_METHOD +/************************************************* + Class "Destructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_exit) + + + +END_METHOD +/************************************************* + Gambas object "Constructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_new) + + THIS->driver=NULL; + THIS->stream.desc=NULL; + +END_METHOD +/************************************************* + Gambas object "Destructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_free) + + if (THIS->stream.desc && THIS->driver) THIS->driver->Compress.Close(&THIS->stream); + +END_METHOD + +BEGIN_METHOD (CUNCOMPRESS_File,GB_STRING Source;GB_STRING Target;) + + Check_Driver(); + THIS->driver->Uncompress.File(STRING(Source),STRING(Target)); + +END_METHOD + +BEGIN_METHOD (CUNCOMPRESS_String,GB_STRING Source;) + + char *target=NULL; + unsigned int lent=0; + + Check_Driver(); + if (!LENGTH(Source)) { GB.ReturnVoidString(); return; } + + THIS->driver->Uncompress.String(&target,&lent,STRING(Source),LENGTH(Source)); + + if (!lent) { GB.ReturnVoidString(); return; } + GB.ReturnNewString (target,lent); + GB.Free(POINTER(&target)); + +END_METHOD + +BEGIN_METHOD (CUNCOMPRESS_Open,GB_STRING Path;) + + + Check_Driver(); + + if (THIS->stream.desc) { GB.Error ("File is already opened"); return; } + + THIS->driver->Uncompress.Open(STRING(Path),&THIS->stream); + +END_METHOD + +BEGIN_PROPERTY (CUNCOMPRESS_Type) + + if (READ_PROPERTY) + { + if (!THIS->driver) + { + GB.ReturnNull(); + return; + } + GB.ReturnNewZeroString(THIS->driver->name); + return; + } + + if (THIS->stream.desc) { GB.Error("Type can not be changed while the stream is opened"); return; } + + if (!(THIS->driver = COMPRESS_GetDriver(GB.ToZeroString(PROP(GB_STRING))))) + { + GB.Error("Cannot find driver &1", GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + +/******************************************************************* + Interface declaration + *******************************************************************/ +GB_DESC CUncompressDesc[] = +{ + + GB_DECLARE("Uncompress", sizeof(CUNCOMPRESS)), + + GB_INHERITS("Stream"), + + GB_PROPERTY("Type","s",CUNCOMPRESS_Type), + + GB_STATIC_METHOD("_init", NULL, CUNCOMPRESS_init, NULL), + GB_STATIC_METHOD("_exit", NULL, CUNCOMPRESS_exit, NULL), + GB_METHOD("_new", NULL, CUNCOMPRESS_new, NULL), + GB_METHOD("_free", NULL, CUNCOMPRESS_free, NULL), + + GB_METHOD("String","s",CUNCOMPRESS_String,"(Source)s"), + GB_METHOD("File",NULL,CUNCOMPRESS_File,"(Source)s(Target)s"), + GB_METHOD("Open",NULL,CUNCOMPRESS_Open,"(Path)s"), + + GB_END_DECLARE +}; + diff --git a/main/lib/compress/CUncompress.h b/main/lib/compress/CUncompress.h new file mode 100644 index 00000000..e87b44b5 --- /dev/null +++ b/main/lib/compress/CUncompress.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + CUncompress.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CUNCOMPRESS_H +#define __CUNCOMPRESS_H + +#include "gambas.h" +#include "gb.compress.h" + +#ifndef __CUNCOMPRESS_C + + +extern GB_DESC CUncompressDesc[]; + +#else + +#define THIS ((CUNCOMPRESS *)_object) + +#endif +typedef struct +{ + GB_BASE ob; + GB_STREAM stream; + COMPRESS_DRIVER *driver; + +} CUNCOMPRESS; + + +#endif diff --git a/main/lib/compress/Makefile.am b/main/lib/compress/Makefile.am new file mode 100644 index 00000000..34b6dd99 --- /dev/null +++ b/main/lib/compress/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.compress +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.compress.la + +gb_compress_la_LIBADD = +gb_compress_la_LDFLAGS = -module @LD_FLAGS@ +gb_compress_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_compress_la_SOURCES = gb.compress.h main.h main.c CCompress.h CCompress.c CUncompress.h CUncompress.c + + diff --git a/main/lib/compress/gb.compress.component b/main/lib/compress/gb.compress.component new file mode 100644 index 00000000..3bc8fd10 --- /dev/null +++ b/main/lib/compress/gb.compress.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.compress +Author=Daniel Campos Fernández,Laurent Carlier diff --git a/main/lib/compress/gb.compress.h b/main/lib/compress/gb.compress.h new file mode 100644 index 00000000..83fc1d29 --- /dev/null +++ b/main/lib/compress/gb.compress.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + gb.compress.h + + (c) 2003-2004 Daniel Campos Fern�ndez + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMPRESS_H +#define __GB_COMPRESS_H + +#include "gambas.h" + +typedef + struct { + char *type; + } + COMPRESS_DESC; + + +typedef + struct { + char *name; + int (*max_compression)(void); + int (*min_compression)(void); + int (*default_compression)(void); + + struct { + int (*String) (char **target,unsigned int *lent,char *source,unsigned int len,int level); + int (*File) (char *source,char *target,int level); + void (*Open) (char *path,int level,GB_STREAM *stream); + int (*Close) (GB_STREAM *stream); + } Compress; + + struct { + int (*String) (char **target,unsigned int *lent,char *source,unsigned int len); + int (*File) (char *source,char *target); + void (*Open) (char *path,GB_STREAM *stream); + int (*Close) (GB_STREAM *stream); + } Uncompress; + } + COMPRESS_DRIVER; + +typedef + struct { + intptr_t version; + void (*Register)(COMPRESS_DRIVER *); + } + COMPRESS_INTERFACE; + +#define COMPRESS_INTERFACE_VERSION 1 + +#endif diff --git a/main/lib/compress/main.c b/main/lib/compress/main.c new file mode 100644 index 00000000..1ff69a49 --- /dev/null +++ b/main/lib/compress/main.c @@ -0,0 +1,111 @@ +/*************************************************************************** + + main.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define MAX_DRIVER 8 +#define __MAIN_C + +#include + +#include "CCompress.h" +#include "CUncompress.h" +#include "gb.compress.h" + +#include "gb_common.h" + +#include "main.h" + + + +GB_INTERFACE GB EXPORT; + +static COMPRESS_DRIVER *_drivers[MAX_DRIVER]; +static int _drivers_count = 0; + + +static void COMPRESS_Register(COMPRESS_DRIVER *driver) +{ + if (_drivers_count >= MAX_DRIVER) + return; + + _drivers[_drivers_count] = driver; + _drivers_count++; + +} + +COMPRESS_DRIVER *COMPRESS_GetDriver(char *type) +{ + int i; + char *comp; + + if (!type || !*type) + { + GB.Error("Driver name missing"); + return NULL; + } + + comp = alloca(strlen(type) + 14); + + strcpy(comp, "gb.compress."); + strcat(comp, type); + + if (GB.Component.Load(comp)) + { + GB.Error("Cannot find driver for : &1", type); + return NULL; + } + + for (i = 0; i < _drivers_count; i++) + { + if (strcasecmp(_drivers[i]->name, type) == 0) + return _drivers[i]; + } + + return NULL; +} + +void *GB_COMPRESS_1[] EXPORT = { + + (void *)1, + + (void *)COMPRESS_Register, + + NULL +}; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CCompressDesc, + CUncompressDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ + +} + diff --git a/main/lib/compress/main.h b/main/lib/compress/main.h new file mode 100644 index 00000000..16729d1d --- /dev/null +++ b/main/lib/compress/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + + + +#include "gb_common.h" +#include "gambas.h" +#include "gb.compress.h" + + + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif +COMPRESS_DRIVER* COMPRESS_GetDriver(char *type); +#endif diff --git a/main/lib/data/Makefile.am b/main/lib/data/Makefile.am new file mode 100644 index 00000000..01600cf5 --- /dev/null +++ b/main/lib/data/Makefile.am @@ -0,0 +1,21 @@ +COMPONENT = gb.data +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.data.la + +gb_data_la_LDFLAGS = -module @LD_FLAGS@ +gb_data_la_CPPFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx +gb_data_la_CFLAGS = $(AM_CFLAGS) + +gb_data_la_SOURCES = \ + main.h main.c list.h lookup3.h string_compare.h \ + c_list.h c_list.c \ + c_deque.h c_deque.c \ + c_circular.h c_circular.c \ + c_avltree.h c_avltree.c \ + c_graph.h c_graph.c \ + c_graphmatrix.h c_graphmatrix.c \ + c_heap.h c_heap.c \ + trie.h trie.c \ + c_trie.h c_trie.c + diff --git a/main/lib/data/TODO b/main/lib/data/TODO new file mode 100644 index 00000000..1f3644c7 --- /dev/null +++ b/main/lib/data/TODO @@ -0,0 +1,55 @@ +NOTE that one should not compare elements in a list based on their indices. +If List2 is an exact copy of List1 and both Currents point to the same +element, it is not guaranteed that List1.Index = List2.Index because one of +them could be the negative equivalent of the other (non-negative) one. + +* OPT: Dynamically add up to 8 anchors to increase the chance of finding a + very good anchor for traversals. These are automatically updated on + append/prepend/take operations to be widespread across the List. +* OPT: Also save an anchor of the last element obtained via _get(). + Sequential traversal with indices should now be almost array-like in + speed. + +* BUG: Current can be used as an enumerator now without corrupting the list + and also if it is initially invalid. + +* OPT: Make the VAL_append()/prepend() algorithms more clever: They can use + any neighbour chunk now to borrow space for the inserted value. This + measure reduces list fragmentation and also enables to satisfy the (L) + postulate. + +* NEW: Push() and Pop() are new methods of the List class to save and + restore anchors from a Gambas program. These can be used to quickly go + back to a formerly-visited element. + +AvlTree: +* NEW: Add a Copy() method to return a deep copy of an AvlTree. +* NEW: Split() is a new method of AvlTree to return the left or right + subtree of a given node or both. +* NEW: Add Insert() to insert an AvlTree into another one. (See a thread on + stackoverflow about an O(log n) algorithm for that.) +* NEW: Add the Reverse virtual property to traver in reverse in-order. + +Graph: +* Write down the hidden methods and their signatures +* Set down the relationships between methods; which default implementation + calls what; what is minimum needed to implement a fully-functional Graph +* NEW: Add default implementations for some hidden functions + +RedBlackTree: +* NEW: RedBlackTree class. + +SearchTree: +* NEW: SearchTree is the base class of all search trees. + +PrioQueue: +* NEW: Re-implement PrioQueue using a RedBlackTree. + -> contain structures (key, time, value) where key is the key and time + is a logical timestamp (rbtree->clock++). If key1 == key2, then compare + the unique insertion timestamps. +* OPT: Don't have Integer priority parameters. Consistently use + GB.CompVariant() to compare elements. This is at least as mighty as + integer priorities, but better encapsulated. + +- Use libtree to provide some trees (if possible). +- General, navigatable tree class. diff --git a/main/lib/data/c_avltree.c b/main/lib/data/c_avltree.c new file mode 100644 index 00000000..ee27cf90 --- /dev/null +++ b/main/lib/data/c_avltree.c @@ -0,0 +1,764 @@ +/* + * c_avltree.c - AvlTree class + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_AVLTREE_C + +#include + +#include "gambas.h" +#include "gb_common.h" +#include "string_compare.h" + +#include "c_avltree.h" + +typedef struct node NODE; + +struct node { + char *key; /* Key string */ + size_t length; /* Key length */ + int balance; /* Balance value in {-1, 0, 1} */ + NODE *left, *right; /* Children or NULL */ + NODE *parent; /* Parent */ + GB_VARIANT_VALUE val; /* Payload */ +}; + +typedef struct { + GB_BASE ob; + NODE *root; /* The root of the tree */ + NODE *last; /* Last used node */ + size_t count; /* Element count */ + size_t height; /* Tree height */ +} CAVLTREE; + +static void CAVLTREE_init(CAVLTREE *tree) +{ + tree->root = tree->last = NULL; + tree->count = tree->height = 0; +} + +/* In-order. */ +static NODE *CAVLTREE_next(CAVLTREE *tree, NODE *node) +{ + NODE *next; + + if ((next = node->right)) { + while (next->left) + next = next->left; + return next; + } + for (next = node->parent; node == next->right; next = next->parent) + node = next; + /* This condition is only met when climing from the right subtree to + * root in the above loop. We're done then. */ + if (node == next) + return NULL; + return next; +} + +static NODE *NODE_new(NODE *parent, char *key, size_t length) +{ + NODE *node; + + GB.Alloc((void **) &node, sizeof(*node)); + node->key = GB.NewString(key, length); + node->length = length; + node->balance = 0; + node->left = node->right = NULL; + /* If 'parent' is NULL, this shall be its own parent */ + node->parent = parent ? : node; + node->val.type = GB_T_NULL; + return node; +} + +static void NODE_destroy(NODE *node) +{ + GB.FreeString(&node->key); + GB.StoreVariant(NULL, &node->val); + GB.Free((void **) &node); +} + +static NODE *CAVLTREE_first(CAVLTREE *tree) +{ + NODE *first = tree->root; + + if (!first) + return NULL; + while (first->left) + first = first->left; + return first; +} + +#define THIS ((CAVLTREE *) _object) + +/**G + * Create a new, empty AvlTree. + **/ +BEGIN_METHOD_VOID(AvlTree_new) + + CAVLTREE_init(THIS); + +END_METHOD + +struct enum_state { + int started; + NODE *next; +}; + +static void CAVLTREE_clear(CAVLTREE *tree) +{ + NODE *node = CAVLTREE_first(tree); + + while (node) + { + NODE_destroy(node); + node = CAVLTREE_next(tree, node); + } + + /* Fix enumerators */ + void *ebuf; + struct enum_state *state; + + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + state->next = NULL; + } + GB.EndEnum(ebuf); + + tree->root = tree->last = NULL; + tree->count = 0; + tree->height = 0; +} + +/**G + * Clears the tree automatically. + **/ +BEGIN_METHOD_VOID(AvlTree_free) + + CAVLTREE_clear(THIS); + +END_METHOD + +static NODE *CAVLTREE_find(CAVLTREE *tree, char *key, size_t length) +{ + NODE *node = tree->root; + int res; + + while (node) { + res = STRING_compare(key, length, node->key, node->length); + + if (!res) + return node; + else if (res < 0) + node = node->left; + else + node = node->right; + } + return NULL; +} + +/**G + * Return the value associated with the given key. If no node with this key + * was found, Null is returned. + **/ +BEGIN_METHOD(AvlTree_get, GB_STRING key) + + NODE *node; + + node = CAVLTREE_find(THIS, STRING(key), LENGTH(key)); + THIS->last = node; + if (!node) { + GB.ReturnNull(); + return; + } + GB.ReturnVariant(&node->val); + +END_METHOD + +#if 0 +/* Reverse in-order */ +static NODE *CAVLTREE_prev(CAVLTREE *tree, NODE *node) +{ + NODE *prev; + + if ((prev = node->left)) { + while (prev->right) + prev = prev->right; + return prev; + } + for (prev = node->parent; node == prev->left; prev = prev->parent) + node = prev; + if (node == prev) + return NULL; + return prev; +} +#endif + +static inline void rotate_left(CAVLTREE *tree, NODE *rot) +{ + NODE *right = rot->right, *parent = rot->parent; + + if (rot == tree->root) { + tree->root = right; + /* Don't forget to add the root signature */ + right->parent = right; + } else { + if (rot == parent->left) + parent->left = right; + else + parent->right = right; + right->parent = parent; + } + rot->parent = right; + + rot->right = right->left; + if (rot->right) + rot->right->parent = rot; + right->left = rot; +} + +static inline void rotate_right(CAVLTREE *tree, NODE *rot) +{ + NODE *left = rot->left, *parent = rot->parent; + + if (rot == tree->root) { + tree->root = left; + left->parent = left; + } else { + if (rot == parent->left) + parent->left = left; + else + parent->right = left; + left->parent = parent; + } + rot->parent = left; + + rot->left = left->right; + if (rot->left) + rot->left->parent = rot; + left->right = rot; +} + +#define sgn(x) \ +({ \ + typeof(x) __x = (x); \ + __x ? (__x < 0 ? -1 : 1) : 0; \ +}) + +static NODE *CAVLTREE_find_add(CAVLTREE *tree, char *key, size_t length) +{ + NODE *node, *parent, *reb, *rot, *new; + int res; + + /* Make GCC happy */ + parent = NULL; + res = 0; + + /* Slightly extended version of NODE_find() which gathers additional + * data for insertion. I'd like to have them separate. */ + reb = node = tree->root; + while (node) { + res = STRING_compare(key, length, node->key, node->length); + + if (!res) + return node; + if (node->balance) + reb = node; + parent = node; + if (res < 0) + node = node->left; + else + node = node->right; + } + res = sgn(res); + + new = node = NODE_new(parent, key, length); + tree->count++; + + /* Fix enumerations, pt. I: empty tree */ + void *ebuf; + struct enum_state *state; + + if (!tree->root) { + tree->root = node; + tree->height++; + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + state->next = node; + } + GB.EndEnum(ebuf); + return node; + } + if (res == -1) + parent->left = node; + else + parent->right = node; + + /* Fix enumerators, pt. II: all other cases */ + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + /* + * Nasty. If a new element is inserted before the current + * state->next OR state->next is NULL and a new last + * element is added, update the state. + */ + if (state->next == parent + || (!state->next && !CAVLTREE_next(tree, new))) + state->next = new; + } + GB.EndEnum(ebuf); + + /* Adjust balance factors */ + while (node != reb) { + if (node == parent->left) + parent->balance--; + else + parent->balance++; + node = parent; + parent = parent->parent; + } + + /* Rebalance the tree */ + switch (reb->balance) { + case 1: + case -1: + tree->height++; + break; + case 2: /* Right heavy */ + rot = reb->right; + if (rot->balance == 1) { /* Right-right */ + reb->balance = 0; + rot->balance = 0; + } else { /* Right-left */ + switch (rot->left->balance) { + case 1: + reb->balance = -1; + rot->balance = 0; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 0; + rot->balance = 1; + break; + } + rot->left->balance = 0; + rotate_right(tree, rot); + } + rotate_left(tree, reb); + break; + case -2: /* Left heavy */ + rot = reb->left; + if (rot->balance == -1) { /* Left-left */ + reb->balance = 0; + rot->balance = 0; + } else { /* Left-right */ + switch (rot->right->balance) { + case 1: + reb->balance = 0; + rot->balance = -1; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 1; + rot->balance = 0; + break; + } + rot->right->balance = 0; + rotate_left(tree, rot); + } + rotate_right(tree, reb); + break; + } + + return new; +} + +#ifdef DEBUG_ME +static void dump_node(NODE *node) +{ + fprintf(stderr, "%p \"%s\" (parent=%p \"%s\", left=%p \"%s\", " + "right=%p \"%s\") balance=%d\n", node, node->key, + node->parent, node->parent->key, node->left, + node->left ? node->left->key : "", node->right, + node->right ? node->right->key : "", node->balance); + if (node->left) + dump_node(node->left); + if (node->right) + dump_node(node->right); +} +#endif + +static void CAVLTREE_remove(CAVLTREE *tree, char *key, size_t length) +{ + NODE *node, *rep, *child, *reb; + int d; /* A balance delta */ + int process_root = 1; + + node = CAVLTREE_find(tree, key, length); + +#ifdef DEBUG_ME + fprintf(stderr, "Deletion of %p \"%s\"\n", node, key); + dump_node(tree->root); + fprintf(stderr, "-->\n"); +#endif + + if (!node) + return; + tree->count--; + if (node == tree->last) + tree->last = NULL; + + if (!node->left || !node->right) { + rep = node->left ? : node->right; + reb = node->parent; + d = node == reb->left ? 1: -1; + goto replace; + } + + rep = CAVLTREE_next(tree, node); + + /* Detach replacement node */ + reb = rep->parent; + d = rep == reb->left ? 1 : -1; + if (reb == node) + goto replace; + child = rep->left ? : rep->right; + if (child) + child->parent = reb; + if (rep == reb->left) + reb->left = child; + else + reb->right = child; + + /* Replace 'node' by 'rep'. At this point, 'rep' may be anything + * from an inner node to a half-leaf or leaf. */ +replace:; + /* Fix enumerations */ + void *ebuf; + struct enum_state *state; + + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + if (state->next == node) + state->next = rep; + } + GB.EndEnum(ebuf); + + if (node == tree->root) { + tree->root = rep; + if (rep) { + rep->parent = rep; + } else { /* Tree gets empty */ + tree->count = 0; + tree->height = 0; + NODE_destroy(node); + return; + } + } else { + if (node == node->parent->left) + node->parent->left = rep; + else + node->parent->right = rep; + if (rep) + rep->parent = node->parent; + } + if (rep) { + rep->balance = node->balance; + if (rep != node->left) { + rep->left = node->left; + if (rep->left) + rep->left->parent = rep; + } else { + rep->balance++; + } + if (rep != node->right) { + rep->right = node->right; + if (rep->right) + rep->right->parent = rep; + } else { + rep->balance--; + } + } + + NODE_destroy(node); + + /* Rebalance */ + if (reb == tree->root) + process_root = 0; + do { + int old_balance = reb->balance; + NODE *rot; + + reb->balance += d; + switch (reb->balance) { + case 2: /* Right heavy */ + rot = reb->right; + if (rot->balance == 1) { /* Right-right */ + reb->balance = 0; + rot->balance = 0; + } else { /* Right-left */ + switch (rot->left->balance) { + case 1: + reb->balance = -1; + rot->balance = 0; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 0; + rot->balance = 1; + break; + } + rot->left->balance = 0; + rotate_right(tree, rot); + } + rotate_left(tree, reb); + break; + case -2: /* Left heavy */ + rot = reb->left; + if (rot->balance == -1) { /* Left-left */ + reb->balance = 0; + rot->balance = 0; + } else { /* Left-right */ + switch (rot->right->balance) { + case 1: + reb->balance = 0; + rot->balance = -1; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 1; + rot->balance = 0; + break; + } + rot->right->balance = 0; + rotate_left(tree, rot); + } + rotate_right(tree, reb); + break; + case -1: + case 1: + goto end; + } + d = reb->balance - old_balance; + if (reb == reb->parent->right) + d = -d; + reb = reb->parent; + } while (reb != tree->root || process_root--); + tree->height--; +end:; +#ifdef DEBUG_ME + dump_node(tree->root); + fprintf(stderr, "Deletion complete\n\n"); +#endif +} + +/**G + * Creates a new node with the given key and value or changes the value of + * an already existing key. If the value is Null, then the node is removed. + **/ +BEGIN_METHOD(AvlTree_put, GB_VARIANT value; GB_STRING key) + + NODE *node; + + if (VARG(value).type == GB_T_NULL) { + CAVLTREE_remove(THIS, STRING(key), LENGTH(key)); + return; + } + node = CAVLTREE_find_add(THIS, STRING(key), LENGTH(key)); + GB.StoreVariant(ARG(value), &node->val); + THIS->last = node; + +END_METHOD + +/**G + * Visit each element of the tree in-order, i.e. from the smallest key to + * the greatest. The Key property of the tree is set according to the value + * in the enumerator. + * + * {example + * Dim v As Variant + * + * Print "Key", "Value" + * For Each v In hTree + * Print hTree.Key, v + * Next + * } + **/ +BEGIN_METHOD_VOID(AvlTree_next) + + NODE *node; + struct enum_state *state = GB.GetEnum(); + + if (!state->started) { + state->started = 1; + node = CAVLTREE_first(THIS); + } else { + node = state->next; + } + if (!node) { + GB.StopEnum(); + return; + } + state->next = CAVLTREE_next(THIS, node); + THIS->last = node; + GB.ReturnVariant(&node->val); + +END_METHOD + +/**G + * Clear the tree, i.e. remove all elements. This is **way** faster than + * removing every element by assigning Null to it like this: + * + * For Each v In hTree + * hTree[hTree.Key] = Null + * Next + * + * Because removing an element may require to rebalance the tree at most + * hTree.Height times. For each element, this is a great overhead to have an + * empty tree. + * + * So use hTree.Clear() if the tree shall be emptied. + **/ +BEGIN_METHOD_VOID(AvlTree_Clear) + + CAVLTREE_clear(THIS); + +END_METHOD + +/**G + * Return whether an element with the given key exists. + **/ +BEGIN_METHOD(AvlTree_Exist, GB_STRING key) + + NODE *node; + + node = CAVLTREE_find(THIS, STRING(key), LENGTH(key)); + /* TODO: Use this as a cache for subsequent MoveTo() or anything */ + THIS->last = node; + GB.ReturnBoolean(!!node); + +END_METHOD + +/**G + * Return the balance factor of the AvlTree. It is either -1, 0 or 1. + **/ +BEGIN_PROPERTY(AvlTree_Balance) + + if (!THIS->root) { + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(THIS->root->balance); + +END_PROPERTY + +/**G + * Return the number of elements in the tree. + **/ +BEGIN_PROPERTY(AvlTree_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + +/**G + * Return the height of the tree. + **/ +BEGIN_PROPERTY(AvlTree_Height) + + GB.ReturnInteger(THIS->height); + +END_PROPERTY + +/**G + * Return the last used key. This can be Null if the element was removed + * meanwhile. + * + * Be careful as this property changes with nearly every operation on the + * tree. + **/ +BEGIN_PROPERTY(AvlTree_Key) + + if (!THIS->last) + GB.ReturnNull(); + else + GB.ReturnString(THIS->last->key); + +END_PROPERTY + +GB_DESC CAvlTree[] = { + GB_DECLARE("AvlTree", sizeof(CAVLTREE)), + + GB_METHOD("_new", NULL, AvlTree_new, NULL), + GB_METHOD("_free", NULL, AvlTree_free, NULL), + GB_METHOD("_get", "v", AvlTree_get, "(Key)s"), + GB_METHOD("_put", NULL, AvlTree_put, "(Value)v(Key)s"), + GB_METHOD("_next", "v", AvlTree_next, NULL), + + GB_METHOD("Clear", NULL, AvlTree_Clear, NULL), + GB_METHOD("Exist", "b", AvlTree_Exist, "(Key)s"), + +#if 0 + /* Returns left and right subtree of root as array of new + * (deeply-copied) trees. This is trivial as any subtree in an + * AvlTree is also an AvlTree. */ + GB_METHOD("Split", ".AvlTree.Split", AvlTree_Split, "(Key)s"), + /* Merge into this tree. */ + GB_METHOD("Merge", NULL, AvlTree_Merge, "(OtherTree)AvlTree"), + /* Return a deep copy of the AvlTree */ + GB_METHOD("Copy", "AvlTree", AvlTree_Copy, NULL), +#endif + + GB_PROPERTY_READ("Balance", "i", AvlTree_Balance), + GB_PROPERTY_READ("Count", "i", AvlTree_Count), + GB_PROPERTY_READ("Height", "i", AvlTree_Height), + + GB_PROPERTY_READ("Key", "s", AvlTree_Key), + + GB_END_DECLARE +}; + +#if 0 +GB_DESC CAvlTreeSplit[] = { + GB_DECLARE_VIRTUAL(".AvlTree.Split"), + + GB_PROPERTY_READ("Left", "AvlTree", AvlTreeSplit_Left), + GB_PROPERTY_READ("Right", "AvlTree", AvlTreeSplit_Right), + GB_PROPERTY_READ("Both", "AvlTree[]", AvlTreeSplit_Both), + + GB_END_DECLARE +}; +#endif diff --git a/main/lib/data/c_avltree.h b/main/lib/data/c_avltree.h new file mode 100644 index 00000000..43cb2f8f --- /dev/null +++ b/main/lib/data/c_avltree.h @@ -0,0 +1,33 @@ +/* + * c_avltree.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_AVLTREE_H +#define __C_AVLTREE_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_AVLTREE_C +extern GB_DESC CAvlTree[]; +#endif + +#endif /* !__C_AVLTREE_H */ diff --git a/main/lib/data/c_circular.c b/main/lib/data/c_circular.c new file mode 100644 index 00000000..d6a9d77a --- /dev/null +++ b/main/lib/data/c_circular.c @@ -0,0 +1,307 @@ +/* + * c_circular.c - Circular/Ring buffer type + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_CIRCULAR_C + +#include "gambas.h" +#include "c_circular.h" + +typedef struct { + GB_BASE ob; + GB_VARIANT_VALUE *elements; + size_t size; /* Mirror of the array's size to speed things up */ + int reader; + int writer; + int overwrite; + int empty : 1; + int full : 1; +} CCIRCULAR; + +static int CCIRCULAR_size(CCIRCULAR *circ) +{ + return circ->size; +} + +static int CCIRCULAR_is_empty(CCIRCULAR *circ) +{ + return circ->empty; +} + +static int CCIRCULAR_is_full(CCIRCULAR *circ) +{ + return circ->full; +} + +/* + * All movements go forward. This means that moving the writer could never + * induce an 'empty' state and conversely moving the reader could never make + * the circular 'full'. + */ + +static void CCIRCULAR_move_index(CCIRCULAR *circ, int *idx, int pos) +{ + size_t size = CCIRCULAR_size(circ); + + if (!size) + pos = 0; + else if (pos >= size) + pos %= size; + *idx = pos; + /* Set empty/full flags */ + if (circ->reader == circ->writer) { + /* But only if we really operated on the given @circ */ + if (idx == &circ->reader) + circ->empty = 1; + else if (idx == &circ->writer) + circ->full = 1; + } else { + circ->empty = circ->full = 0; + } +} + +static void CCIRCULAR_inc_index(CCIRCULAR *circ, int *idx) +{ + CCIRCULAR_move_index(circ, idx, *idx + 1); +} + +static void CCIRCULAR_reset(CCIRCULAR *circ) +{ + circ->reader = circ->writer = 0; + circ->empty = 1; + if (!circ->size) + circ->full = 1; + else + circ->full = 0; +} + +static void CCIRCULAR_init(CCIRCULAR *circ, size_t size, int overwrite) +{ + circ->size = size; + GB.NewArray(&circ->elements, sizeof(GB_VARIANT_VALUE), circ->size); + CCIRCULAR_reset(circ); + circ->overwrite = overwrite; +} + +static void CCIRCULAR_destroy(CCIRCULAR *circ) +{ + GB.FreeArray((void *) &circ->elements); +} + +#define THIS ((CCIRCULAR *) _object) + +BEGIN_METHOD(Circular_new, GB_INTEGER size; GB_BOOLEAN overwrite) + + CCIRCULAR_init(THIS, VARG(size), VARGOPT(overwrite, 1)); + +END_METHOD + +static GB_VARIANT_VALUE *CCIRCULAR_read(CCIRCULAR *circ) +{ + GB_VARIANT_VALUE *var; + + if (CCIRCULAR_is_empty(circ)) + return NULL; + var = &circ->elements[circ->reader]; + CCIRCULAR_inc_index(circ, &circ->reader); + return var; +} + +static void CCIRCULAR_read_and_free_all(CCIRCULAR *circ) +{ + int i; + + for (i = 0; i < circ->size; i++) + GB.StoreVariant(NULL, &circ->elements[i]); + CCIRCULAR_reset(circ); +} + +BEGIN_METHOD_VOID(Circular_free) + + CCIRCULAR_read_and_free_all(THIS); + CCIRCULAR_destroy(THIS); + +END_METHOD + +static void CCIRCULAR_write(CCIRCULAR *circ, GB_VARIANT *variant) +{ + if (CCIRCULAR_is_full(circ)) { + if (circ->overwrite) /* Consume oldest value and continue */ + CCIRCULAR_read(circ); + else /* Do nothing */ + return; + } + GB.StoreVariant(variant, &circ->elements[circ->writer]); + CCIRCULAR_inc_index(circ, &circ->writer); +} + +BEGIN_METHOD(Circular_Write, GB_VARIANT value) + + CCIRCULAR_write(THIS, ARG(value)); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Read) + + if (CCIRCULAR_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + GB.ReturnVariant(CCIRCULAR_read(THIS)); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Peek) + + if (CCIRCULAR_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + GB.ReturnVariant(&THIS->elements[THIS->reader]); + +END_METHOD + +static void CCIRCULAR_resize(CCIRCULAR *circ, size_t new) +{ + size_t old = CCIRCULAR_size(circ); + + if (old == new) + return; + if (old < new) { + GB_VARIANT_VALUE *buf; + int i; + + buf = GB.Insert(&circ->elements, old, new - old); + for (i = 0; old < new; old++, i++) + buf[i].type = GB_T_NULL; + } else { + int i; + + for (i = new; i < old; i++) + GB.StoreVariant(NULL, &circ->elements[i]); + GB.Remove(&circ->elements, new, old - new); + /* Move the indices accordingly */ + if (circ->reader > new) + circ->reader = new; + if (circ->writer > new) + circ->writer = new; + /* 0-length circular? */ + if (!new) { + circ->empty = circ->full = 1; + } + } + circ->size = new; +} + +BEGIN_METHOD(Circular_Resize, GB_INTEGER size) + + CCIRCULAR_resize(THIS, VARG(size)); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Clear) + + CCIRCULAR_read_and_free_all(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Reset) + + CCIRCULAR_reset(THIS); + +END_METHOD + +BEGIN_PROPERTY(Circular_IsEmpty) + + GB.ReturnBoolean(CCIRCULAR_is_empty(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_IsFull) + + GB.ReturnBoolean(CCIRCULAR_is_full(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Reader) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->reader); + return; + } + CCIRCULAR_move_index(THIS, &THIS->reader, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Writer) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->writer); + return; + } + CCIRCULAR_move_index(THIS, &THIS->writer, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Size) + + if (READ_PROPERTY) { + GB.ReturnInteger(CCIRCULAR_size(THIS)); + return; + } + CCIRCULAR_resize(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Overwrite) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->overwrite); + return; + } + THIS->overwrite = VPROP(GB_BOOLEAN); + +END_PROPERTY + +GB_DESC CCircular[] = { + GB_DECLARE("Circular", sizeof(CCIRCULAR)), + + GB_METHOD("_new", NULL, Circular_new, "(Size)i[(Overwrite)b]"), + GB_METHOD("_free", NULL, Circular_free, NULL), + + GB_METHOD("Write", NULL, Circular_Write, "(Value)v"), + GB_METHOD("Read", "v", Circular_Read, NULL), + GB_METHOD("Peek", "v", Circular_Peek, NULL), + + GB_METHOD("Resize", NULL, Circular_Resize, "(Size)i"), + GB_METHOD("Clear", NULL, Circular_Clear, NULL), + GB_METHOD("Reset", NULL, Circular_Reset, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Circular_IsEmpty), + GB_PROPERTY_READ("IsFull", "b", Circular_IsFull), + GB_PROPERTY("Reader", "i", Circular_Reader), + GB_PROPERTY("Writer", "i", Circular_Writer), + GB_PROPERTY("Size", "i", Circular_Size), + GB_PROPERTY("Overwrite", "b", Circular_Overwrite), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_circular.h b/main/lib/data/c_circular.h new file mode 100644 index 00000000..6d06e542 --- /dev/null +++ b/main/lib/data/c_circular.h @@ -0,0 +1,33 @@ +/* + * c_circular.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_CIRCULAR_H +#define __C_CIRCULAR_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_CIRCULAR_C +extern GB_DESC CCircular[]; +#endif + +#endif /* !__C_CIRCULAR_H */ diff --git a/main/lib/data/c_deque.c b/main/lib/data/c_deque.c new file mode 100644 index 00000000..79a681b7 --- /dev/null +++ b/main/lib/data/c_deque.c @@ -0,0 +1,391 @@ +/* + * c_deque.c - Deque, FILO and FIFO types + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_DEQUE_C + +#include "gambas.h" +#include "gb_common.h" /* EXTERN for gbx_type.h */ +#include "gbx_type.h" /* TYPE_is_object() */ + +#include "c_list.h" +#include "c_deque.h" + +typedef struct { + GB_VARIANT_VALUE var; + LIST list; + int prio; +} CDEQUE_ELEM; +#define get_elem(node) LIST_data(node, CDEQUE_ELEM, list) + +static CDEQUE_ELEM *CDEQUE_new_elem(GB_VARIANT *variant) +{ + CDEQUE_ELEM *new; + + GB.Alloc((void **) &new, sizeof(*new)); + new->var.type = GB_T_NULL; + LIST_init(&new->list); + GB.StoreVariant(variant, &new->var); + return new; +} + +static CDEQUE_ELEM *CDEQUE_copy_elem(CDEQUE_ELEM *elem) +{ + CDEQUE_ELEM *copy; + + GB.Alloc((void **) ©, sizeof(*copy)); + LIST_init(©->list); + copy->prio = elem->prio; + memcpy(©->var, &elem->var, sizeof(copy->var)); + if (TYPE_is_object(copy->var.type)) + GB.Ref(copy->var.value._object); + return copy; +} + +static void CDEQUE_destroy_elem(CDEQUE_ELEM *elem) +{ + LIST_unlink(&elem->list); + GB.StoreVariant(NULL, &elem->var); + GB.Free((void **) &elem); +} + +typedef struct { + GB_BASE ob; + LIST elements; +} CDEQUE; + +static int CDEQUE_is_empty(CDEQUE *dq) +{ + return LIST_is_empty(&dq->elements); +} + +static void CDEQUE_init(CDEQUE *dq) +{ + LIST_init(&dq->elements); +} + +#define THIS ((CDEQUE *) _object) + +BEGIN_METHOD_VOID(Deque_new) + + CDEQUE_init(THIS); + +END_METHOD + +static CDEQUE_ELEM *CDEQUE_pop_front(CDEQUE *dq) +{ + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(dq)) + return NULL; + elem = get_elem(dq->elements.next); + LIST_unlink(&elem->list); + return elem; +} + +static void CDEQUE_pop_and_free_all(CDEQUE *dq) +{ + while (!CDEQUE_is_empty(dq)) + CDEQUE_destroy_elem(CDEQUE_pop_front(dq)); +} + +BEGIN_METHOD_VOID(Deque_free) + + CDEQUE_pop_and_free_all(THIS); + +END_METHOD + +static void CDEQUE_push_front(CDEQUE *dq, CDEQUE_ELEM *elem) +{ + LIST_append(&dq->elements, &elem->list); +} + +BEGIN_METHOD(Deque_PushFront, GB_VARIANT value) + + CDEQUE_ELEM *elem; + + elem = CDEQUE_new_elem(ARG(value)); + CDEQUE_push_front(THIS, elem); + +END_METHOD + +static void CDEQUE_push_back(CDEQUE *dq, CDEQUE_ELEM *elem) +{ + LIST_prepend(&dq->elements, &elem->list); +} + +BEGIN_METHOD(Deque_PushBack, GB_VARIANT value) + + CDEQUE_ELEM *elem; + + elem = CDEQUE_new_elem(ARG(value)); + CDEQUE_push_back(THIS, elem); + +END_METHOD + +BEGIN_METHOD_VOID(Deque_PopFront) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + elem = CDEQUE_pop_front(THIS); + GB.ReturnVariant(&elem->var); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &elem->var); + GB.ReturnRelease(); + CDEQUE_destroy_elem(elem); + +END_METHOD + +static CDEQUE_ELEM *CDEQUE_pop_back(CDEQUE *dq) +{ + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(dq)) + return NULL; + elem = get_elem(dq->elements.prev); + LIST_unlink(&elem->list); + return elem; +} + +BEGIN_METHOD_VOID(Deque_PopBack) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + elem = CDEQUE_pop_back(THIS); + GB.ReturnVariant(&elem->var); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &elem->var); + GB.ReturnRelease(); + CDEQUE_destroy_elem(elem); + +END_METHOD + +BEGIN_METHOD_VOID(Deque_PeekFront) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } else { + elem = get_elem(THIS->elements.next); + GB.ReturnVariant(&elem->var); + } + +END_METHOD + +BEGIN_METHOD_VOID(Deque_PeekBack) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } else { + elem = get_elem(THIS->elements.prev); + GB.ReturnVariant(&elem->var); + } + +END_METHOD + +BEGIN_METHOD_VOID(Deque_Clear) + + CDEQUE_pop_and_free_all(THIS); + +END_METHOD + +static void CDEQUE_copy(CDEQUE *src, CDEQUE *dst) +{ + LIST *node; + + list_for_each(node, &src->elements) + CDEQUE_push_back(dst, CDEQUE_copy_elem(get_elem(node))); +} + +BEGIN_METHOD_VOID(Deque_Copy) + + CDEQUE *copy; + + /* This method is also used by Queue and Stack, so get the right + * class. */ + copy = GB.New(GB.GetClass(THIS), NULL, NULL); + CDEQUE_copy(THIS, copy); + GB.ReturnObject(copy); + +END_METHOD + +BEGIN_PROPERTY(Deque_IsEmpty) + + GB.ReturnBoolean(CDEQUE_is_empty(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(Deque_Size) + + size_t size; + LIST *node; + + size = 0; + list_for_each(node, &THIS->elements) + size++; + GB.ReturnInteger(size); + +END_PROPERTY + +/* + * Double-ended queue + */ + +GB_DESC CDeque[] = { + GB_DECLARE("Deque", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("PushFront", NULL, Deque_PushFront, "(Value)v"), + GB_METHOD("PushBack", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("PopFront", "v", Deque_PopFront, NULL), + GB_METHOD("PopBack", "v", Deque_PopBack, NULL), + GB_METHOD("PeekFront", "v", Deque_PeekFront, NULL), + GB_METHOD("PeekBack", "v", Deque_PeekBack, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + GB_METHOD("Copy", "Deque", Deque_Copy, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; + +/* + * LIFO + */ + +GB_DESC CStack[] = { + GB_DECLARE("Stack", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("Push", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("Pop", "v", Deque_PopBack, NULL), + GB_METHOD("Peek", "v", Deque_PeekBack, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + GB_METHOD("Copy", "Stack", Deque_Copy, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; + +/* + * FIFO + */ + +GB_DESC CQueue[] = { + GB_DECLARE("Queue", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("Enqueue", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("Enq", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("Dequeue", "v", Deque_PopFront, NULL), + GB_METHOD("Deq", "v", Deque_PopFront, NULL), + GB_METHOD("Peek", "v", Deque_PeekFront, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + GB_METHOD("Copy", "Queue", Deque_Copy, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; + +/* + * We keep the Priority queue sorted by a special Enqueue() function. + * The higher the priority, the closer to the beginning the value will be + * enqueued. For equal priorities, the later added element will be enqueued + * at a higher index. + * + * TODO: This is an O(n) search over a linked list. We gotta change that! + */ + +static void CPRIOQ_enqueue(CDEQUE *dq, CDEQUE_ELEM *elem, int prio) +{ + LIST *next; + CDEQUE_ELEM *e; + + /* Just find the right node to prepend the new value to */ + list_for_each(next, &dq->elements) { + e = get_elem(next); + if (prio > e->prio) + break; + } + elem->prio = prio; + LIST_prepend(next, &elem->list); +} + +BEGIN_METHOD(PrioQueue_Enqueue, GB_VARIANT value; GB_INTEGER prio) + + CDEQUE_ELEM *elem; + + elem = CDEQUE_new_elem(ARG(value)); + CPRIOQ_enqueue(THIS, elem, VARG(prio)); + +END_METHOD + +/* + * Priority FIFO + */ + +GB_DESC CPrioQueue[] = { + GB_DECLARE("PrioQueue", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("Enqueue", NULL, PrioQueue_Enqueue, "(Value)v(Prio)i"), + GB_METHOD("Enq", NULL, PrioQueue_Enqueue, "(Value)v(Prio)i"), + GB_METHOD("Dequeue", "v", Deque_PopFront, NULL), + GB_METHOD("Deq", "v", Deque_PopFront, NULL), + GB_METHOD("Peek", "v", Deque_PeekFront, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_deque.h b/main/lib/data/c_deque.h new file mode 100644 index 00000000..3ceb8bfc --- /dev/null +++ b/main/lib/data/c_deque.h @@ -0,0 +1,36 @@ +/* + * c_deque.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_DEQUE_H +#define __C_DEQUE_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_DEQUE_C +extern GB_DESC CDeque[]; +extern GB_DESC CStack[]; +extern GB_DESC CQueue[]; +extern GB_DESC CPrioQueue[]; +#endif + +#endif /* !__C_DEQUE_H */ diff --git a/main/lib/data/c_graph.c b/main/lib/data/c_graph.c new file mode 100644 index 00000000..0433d72c --- /dev/null +++ b/main/lib/data/c_graph.c @@ -0,0 +1,485 @@ +/* + * c_graph.c - (Un)Directed, (un)weighted Graph interface + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_GRAPH_C + +#include + +#include "gambas.h" +#include "c_graph.h" + +static GB_HASHTABLE interfaces; + +BEGIN_METHOD_VOID(Graph_init) + + GB.HashTable.New(&interfaces, GB_COMP_BINARY); + +END_METHOD + +static void enum_func(void *x) +{ + GB.Free(&x); +} + +BEGIN_METHOD_VOID(Graph_exit) + + GB.HashTable.Enum(interfaces, (GB_HASHTABLE_ENUM_FUNC) enum_func); + GB.HashTable.Free(&interfaces); + +END_METHOD + +BEGIN_METHOD_VOID(Graph_NoMethod) + + GB.Error("This method is not implemented."); + +END_METHOD + +BEGIN_PROPERTY(Graph_NoProperty) + + GB.Error("This property is not implemented."); + +END_PROPERTY + +BEGIN_METHOD(Graph_call, GB_BOOLEAN d; GB_BOOLEAN w) + + GB_CLASS GraphMatrixClass; + + GB.Push(2, GB_T_BOOLEAN, VARGOPT(d, 0), + GB_T_BOOLEAN, VARGOPT(w, 0)); + GraphMatrixClass = GB.FindClass("GraphMatrix"); + GB.ReturnObject(GB.New(GraphMatrixClass, NULL, (void *) (intptr_t) 2)); + +END_METHOD + +#define get_func(func, arg, ret, need) \ +do { \ + GB_FUNCTION f; \ + \ + err = #func; \ + if (GB.GetFunction(&f, _object, #func, arg, ret)) { \ + if (need) \ + goto error; \ + else /* Clear error */ \ + GB.Error(NULL); \ + desc->func = 0; \ + } else { \ + desc->func = f.index; \ + } \ +} while(0) + +GRAPH_DESC *get_desc(void *_object) +{ + GRAPH_DESC *desc; + const char *err; + + GB.Alloc((void **) &desc, sizeof(*desc)); + get_func(_getVertex, "s", NULL, 0); + get_func(_getEdge, "ss", NULL, 0); + get_func(_nextVertex, NULL, "s", 0); + get_func(_nextEdge, NULL, "String[]", 0); + get_func(_countVertices, NULL, "i", 0); + get_func(_countEdges, NULL, "i", 0); + get_func(_nextInEdge, NULL, "String[]", 0); + get_func(_nextOutEdge, NULL, "String[]", 0); + get_func(_nextAdjacent, NULL, "s", 0); + get_func(_vertexProperty, ".", "b", 0); + get_func(_edgeProperty, ".", "b", 0); + get_func(_vertexUnknown, ".", "v", 0); + get_func(_edgeUnknown, ".", "v", 0); + return desc; + +error: + GB.Error("Method \"&1\" not found", err); + GB.Free((void **) &desc); + return NULL; +} + +#define THIS ((CGRAPH *) _object) + +BEGIN_METHOD_VOID(Graph_new) + + GRAPH_DESC *desc; + char *name = GB.GetClassName(THIS); + size_t len = GB.StringLength(name); + + if (GB.HashTable.Get(interfaces, name, len, (void **) &desc)) { + desc = get_desc(THIS); + GB.HashTable.Add(interfaces, name, len, desc); + } + THIS->desc = desc; + THIS->vertex = NULL; + GB.Array.New(&THIS->edge, GB_T_STRING, 2); + THIS->tag.type = GB_T_NULL; + +END_METHOD + +BEGIN_METHOD_VOID(Graph_free) + + GB.StoreVariant(NULL, &THIS->tag); + GB.Unref(&THIS->edge); + GB.FreeString(&THIS->vertex); + +END_METHOD + +BEGIN_PROPERTY(Graph_Tag) + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->tag); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->tag); + +END_PROPERTY + +#define SET_VERTEX() \ +do { \ + GB.FreeString(&THIS->vertex); \ + THIS->vertex = GB.NewString(STRING(vert), LENGTH(vert));\ +} while (0) + +#define SET_VERTEX_P() \ +do { \ + GB.FreeString(&THIS->vertex); \ + THIS->vertex = GB.NewString(PSTRING(), PLENGTH()); \ +} while (0) + +#define SET_EDGE() \ +do { \ + GB.StoreString(ARG(src), GB.Array.Get(THIS->edge, 0)); \ + GB.StoreString(ARG(dst), GB.Array.Get(THIS->edge, 1)); \ +} while (0) + +#define SET_EDGE_P() \ +do { \ + GB.StoreObject(VPROP(GB_OBJECT), &THIS->edge); \ +} while (0) + +BEGIN_PROPERTY(Graph_Vertex) + + if (READ_PROPERTY) { + GB.ReturnString(THIS->vertex); + return; + } + SET_VERTEX_P(); + +END_PROPERTY + +BEGIN_PROPERTY(Graph_Edge) + + if (READ_PROPERTY) { + GB.ReturnObject(THIS->edge); + return; + } + SET_EDGE_P(); + +END_PROPERTY + +GB_DESC CGraph[] = { + GB_DECLARE("Graph", sizeof(CGRAPH)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_init", NULL, Graph_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Graph_exit, NULL), + + GB_STATIC_METHOD("_call", "Graph", Graph_call, "[(Directed)b(Weighted)b]"), + GB_METHOD("_new", NULL, Graph_new, NULL), + GB_METHOD("_free", NULL, Graph_free, NULL), + + /* Graph */ + GB_METHOD("_getVertex", ".Graph.Vertex", Graph_NoMethod, "(Vertex)s"), + GB_METHOD("_getEdge", ".Graph.Edge", Graph_NoMethod, "(Src)s(Dst)s"), + + GB_METHOD("_nextVertex", "s", Graph_NoMethod, NULL), + GB_METHOD("_nextEdge", "String[]", Graph_NoMethod, NULL), + + GB_METHOD("_countVertices", "i", Graph_NoMethod, NULL), + GB_METHOD("_countEdges", "i", Graph_NoMethod, NULL), + + /* Vertex */ + GB_METHOD("_nextInEdge", "String[]", Graph_NoMethod, NULL), + GB_METHOD("_nextOutEdge", "String[]", Graph_NoMethod, NULL), + + /* Public */ + GB_METHOD("Add", ".Graph.Vertex", Graph_NoMethod, "(Name)s"), + GB_METHOD("Remove", NULL, Graph_NoMethod, "(Vertex)s"), + GB_METHOD("Connect", ".Graph.Edge", Graph_NoMethod, "(Src)s(Dst)s"), + GB_METHOD("Disconnect", NULL, Graph_NoMethod, "(Src)s(Dst)s"), + + GB_PROPERTY("Tag", "v", Graph_Tag), + + GB_PROPERTY("_Vertex", "s", Graph_Vertex), + GB_PROPERTY("_Edge", "String[]", Graph_Edge), + + GB_PROPERTY_SELF("Vertices", ".Graph.Vertices"), + GB_PROPERTY_SELF("Edges", ".Graph.Edges"), + + GB_PROPERTY_SELF("InEdges", ".Graph.InEdges"), + GB_PROPERTY_SELF("OutEdges", ".Graph.OutEdges"), + GB_PROPERTY_SELF("Adjacent", ".Graph.Adjacent"), + + GB_END_DECLARE +}; + +/* + * Call another method/property of this class and fail if not found. + */ +#define CALL_GRAPH(func, narg) \ +do { \ + GB_FUNCTION f; \ + \ + f.index = THIS->desc->func; \ + f.object = THIS; \ + GB.Call(&f, narg, 0); \ +} while (0) + +#define CALL_GRAPH_VAL(func, narg, arg, ret, retval) \ +do { \ + GB_FUNCTION f; \ + \ + if (GB.GetFunction(&f, _object, func, arg, ret)) { \ + GB.Error("Method " func " not found"); \ + return; \ + } \ + retval = GB.Call(&f, narg, 0); \ +} while (0) + +/* + * Try to call another method/property of this class. If found, return + * afterwards. If not, continue. + */ +#define TRY_CALL_GRAPH(func, narg) \ +do { \ + if (THIS->desc->func) { \ + CALL_GRAPH(func, narg); \ + return; \ + } \ +} while (0) + +BEGIN_METHOD_VOID(GraphVertices_next) + + CALL_GRAPH(_nextVertex, 0); + +END_METHOD + +BEGIN_METHOD(GraphVertices_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_PROPERTY(GraphVertices_Count) + + CALL_GRAPH(_countVertices, 0); + /* Default: enumerate the vertices, counting. Then use + * TRY_CALL_GRAPH. */ + +END_PROPERTY + +GB_DESC CGraphVertices[] = { + GB_DECLARE_VIRTUAL(".Graph.Vertices"), + + GB_METHOD("_next", "s", GraphVertices_next, NULL), + GB_METHOD("_get", ".Graph.Vertex", GraphVertices_get, "(Vertex)s"), + + GB_PROPERTY_READ("Count", "i", GraphVertices_Count), + + GB_END_DECLARE +}; + +BEGIN_METHOD_VOID(GraphEdges_next) + + CALL_GRAPH(_nextEdge, 0); + +END_METHOD + +BEGIN_METHOD(GraphEdges_get, GB_STRING src; GB_STRING dst) + + SET_EDGE(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_PROPERTY(GraphEdges_Count) + + CALL_GRAPH(_countEdges, 0); + /* Default: enumerate the edges, counting. Then use + * TRY_CALL_GRAPH. */ + +END_PROPERTY + +GB_DESC CGraphEdges[] = { + GB_DECLARE_VIRTUAL(".Graph.Edges"), + + GB_METHOD("_next", "String[]", GraphEdges_next, NULL), + GB_METHOD("_get", ".Graph.Edge", GraphEdges_get, "(Src)s(Dst)s"), + GB_METHOD("Exist", "b", Graph_NoMethod, "(Src)s(Dst)s"), + + GB_PROPERTY_READ("Count", "i", GraphEdges_Count), + + GB_END_DECLARE +}; + +BEGIN_METHOD(GraphInEdges_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(GraphInEdges_next) + + CALL_GRAPH(_nextInEdge, 0); + +END_METHOD + +GB_DESC CGraphInEdges[] = { + GB_DECLARE_VIRTUAL(".Graph.InEdges"), + + GB_METHOD("_get", ".Graph.InEdges", GraphInEdges_get, "(Vertex)s"), + GB_METHOD("_next", "String[]", GraphInEdges_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_METHOD(GraphOutEdges_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(GraphOutEdges_next) + + CALL_GRAPH(_nextOutEdge, 0); + +END_METHOD + +GB_DESC CGraphOutEdges[] = { + GB_DECLARE_VIRTUAL(".Graph.OutEdges"), + + GB_METHOD("_get", ".Graph.OutEdges", GraphOutEdges_get, "(Vertex)s"), + GB_METHOD("_next", "String[]", GraphOutEdges_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_METHOD(GraphAdjacent_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(GraphAdjacent_next) + + CALL_GRAPH(_nextAdjacent, 0); + +END_METHOD + +GB_DESC CGraphAdjacent[] = { + GB_DECLARE_VIRTUAL(".Graph.Adjacent"), + + GB_METHOD("_get", ".Graph.Adjacent", GraphAdjacent_get, "(Vertex)s"), + GB_METHOD("_next", "s", GraphAdjacent_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(GraphVertex_InDegree) + + /* Call ??? */ + /* Default: enumerate */ + assert(0); + +END_PROPERTY + +BEGIN_PROPERTY(GraphVertex_OutDegree) + + /* Call ??? */ + /* Default: enumerate */ + assert(0); + +END_PROPERTY + +BEGIN_METHOD_VOID(GraphVertex_property) + + CALL_GRAPH(_vertexProperty, GB.NParam()); + +END_METHOD + +BEGIN_METHOD_VOID(GraphVertex_unknown) + + CALL_GRAPH(_vertexUnknown, GB.NParam()); + +END_METHOD + +GB_DESC CGraphVertex[] = { + GB_DECLARE_VIRTUAL(".Graph.Vertex"), + + GB_PROPERTY_READ("InDegree", "i", GraphVertex_InDegree), + GB_PROPERTY_READ("OutDegree", "i", GraphVertex_OutDegree), + + GB_METHOD("_property", "b", GraphVertex_property, "."), + GB_METHOD("_unknown", "v", GraphVertex_unknown, "."), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(GraphEdge_Src) + + GB.ReturnString(GB.Array.Get(THIS->edge, 0)); + +END_PROPERTY + +BEGIN_PROPERTY(GraphEdge_Dst) + + GB.ReturnString(GB.Array.Get(THIS->edge, 1)); + +END_PROPERTY + +BEGIN_METHOD_VOID(GraphEdge_property) + + CALL_GRAPH(_edgeProperty, GB.NParam()); + +END_METHOD + +BEGIN_METHOD_VOID(GraphEdge_unknown) + + CALL_GRAPH(_edgeUnknown, GB.NParam()); + +END_METHOD + +GB_DESC CGraphEdge[] = { + GB_DECLARE_VIRTUAL(".Graph.Edge"), + + GB_PROPERTY_READ("Src", "s", GraphEdge_Src), + GB_PROPERTY_READ("Dst", "s", GraphEdge_Dst), + + GB_PROPERTY_READ("Source", "s", GraphEdge_Src), + GB_PROPERTY_READ("Destination", "s", GraphEdge_Dst), + GB_PROPERTY("Weight", "f", Graph_NoProperty), + + GB_METHOD("_property", "b", GraphEdge_property, "."), + GB_METHOD("_unknown", "v", GraphEdge_unknown, "."), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_graph.h b/main/lib/data/c_graph.h new file mode 100644 index 00000000..40d092f0 --- /dev/null +++ b/main/lib/data/c_graph.h @@ -0,0 +1,66 @@ +/* + * c_graph.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_GRAPH_H +#define __C_GRAPH_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +typedef struct { + ushort _getVertex; + ushort _getEdge; + + ushort _nextVertex; + ushort _nextEdge; + + ushort _countVertices; + ushort _countEdges; + + ushort _nextInEdge; + ushort _nextOutEdge; + ushort _nextAdjacent; + + ushort _vertexProperty; + ushort _edgeProperty; + + ushort _vertexUnknown; + ushort _edgeUnknown; +} GRAPH_DESC; + +typedef struct { + GB_BASE ob; + GRAPH_DESC *desc; + char *vertex; + GB_ARRAY edge; + GB_VARIANT_VALUE tag; +} CGRAPH; + +extern GRAPH_DESC *get_desc(void *_object); + +#ifndef __C_GRAPH_C +extern GB_DESC CGraph[], CGraphVertices[], CGraphEdges[], CGraphInEdges[], + CGraphOutEdges[], CGraphAdjacent[], CGraphVertex[], + CGraphEdge[]; +#endif + +#endif /* __C_GRAPH_H */ diff --git a/main/lib/data/c_graphmatrix.c b/main/lib/data/c_graphmatrix.c new file mode 100644 index 00000000..7241e311 --- /dev/null +++ b/main/lib/data/c_graphmatrix.c @@ -0,0 +1,699 @@ +/* + * c_graphmatrix.c - Graph as adjacency matrix + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_GRAPHMATRIX_C + +#include + +#include "gambas.h" +#include "c_graph.h" +#include "c_graphmatrix.h" + +#define E_NOVERTEX "Vertex does not exist" +#define E_NOEDGE "Edge does not exist" +#define E_NOPUT "No suitable _put method in the Matrix class" + +typedef struct { + int set : 1; + double weight; +} EDGE; + +/* + * The VERT structure is a row in the adjacency matrix. + */ +typedef struct { + EDGE *edges; + GB_VARIANT_VALUE val; + char *name; +} VERT; + +/* Virtual object selector or part of enumeration state */ +union virt { + unsigned int vertex; + struct { + unsigned int src, dst; + }; +}; + +typedef struct { + CGRAPH base; + int directed : 1; + int weighted : 1; + GB_HASHTABLE names; + VERT *matrix; + /* + * NOTE: This field is used by the "true" virtual object portions of + * this class: GraphMatrix.Vertices[x] and GraphMatrix.Edges[x, y]. + * + * In the enumerators (InEdges, OutEdges, Adjacent), we must use the + * "current" vertex/edge which is stored within ->base. + */ + union virt v; + void *gsl_matrix; /* Cache gb.gsl Matrix object of this */ +} CMATRIX; + +#define THIS ((CMATRIX *) _object) + +BEGIN_METHOD(Matrix_new, GB_BOOLEAN d; GB_BOOLEAN w) + + THIS->directed = VARGOPT(d, 0); + THIS->weighted = VARGOPT(w, 0); + THIS->v.vertex = -1; + THIS->v.src = THIS->v.dst = -1; + GB.HashTable.New(&THIS->names, GB_COMP_NOCASE); + GB.NewArray(&THIS->matrix, sizeof(*THIS->matrix), 0); + THIS->gsl_matrix = NULL; + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_free) + + unsigned int i, count = GB.Count(THIS->matrix); + + GB.HashTable.Free(&THIS->names); + for (i = 0; i < count; i++) { + VERT *cur = &THIS->matrix[i]; + + GB.FreeString(&cur->name); + GB.FreeArray(&cur->edges); + GB.StoreVariant(NULL, &cur->val); + } + GB.FreeArray(&THIS->matrix); + GB.Unref(&THIS->gsl_matrix); + +END_METHOD + +static unsigned int get_vertex(CMATRIX *mat, const char *str, size_t len) +{ + uintptr_t vert; /* Be wide enough to get a void *! */ + + if (GB.HashTable.Get(mat->names, str, len, (void **) &vert)) + return -1; + assert(vert >= 0 && vert < GB.Count(mat->matrix)); + return (unsigned int) vert; +} + +static unsigned int get_cur_vertex(CMATRIX *mat) +{ + return get_vertex(mat, mat->base.vertex, + GB.StringLength(mat->base.vertex)); +} + +BEGIN_METHOD(Matrix_getVertex, GB_STRING vert) + + unsigned int vert = get_vertex(THIS, STRING(vert), LENGTH(vert)); + + if (vert == -1) { + GB.Error(E_NOVERTEX); + return; + } + THIS->v.vertex = vert; + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_getEdge, GB_STRING src; GB_STRING dst) + + unsigned int src = get_vertex(THIS, STRING(src), LENGTH(src)), + dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + + if (src == -1 || dst == -1) { + GB.Error(E_NOVERTEX); + return; + } + if (!THIS->matrix[src].edges[dst].set) { + GB.Error(E_NOEDGE); + return; + } + THIS->v.src = src; THIS->v.dst = dst; + GB.ReturnSelf(THIS); + +END_METHOD + +struct enum_state { + union virt v; + GB_ARRAY e; +}; + +BEGIN_METHOD_VOID(Matrix_nextVertex) + + struct enum_state *state = GB.GetEnum(); + + if (state->v.vertex == GB.Count(THIS->matrix)) { + GB.StopEnum(); + return; + } + GB.ReturnString(THIS->matrix[state->v.vertex++].name); + +END_METHOD + +/* Return non-zero if no next edge was found */ +static int next_edge(CMATRIX *mat, unsigned int *srcp, unsigned int *dstp) +{ + unsigned int src = *srcp, dst = *dstp; + unsigned int count = GB.Count(mat->matrix); + + do { + dst = (dst + 1) % count; + if (!dst) + src++; + if (src >= count) + return -1; + } while (!mat->matrix[src].edges[dst].set); + *srcp = src; + *dstp = dst; + return 0; +} + +/**G + * The same String[] object is used during the enumeration. If you want to + * save a snapshot of it, use String[].Copy(). + * + * Also, you should maybe not change the contents of this array. It may work + * now but I can't guarantee it will in the future. + */ +BEGIN_METHOD_VOID(Matrix_nextEdge) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + GB.Array.New(&state->e, GB_T_STRING, 2); + GB.Ref(state->e); + /* Try edge 0,0 which is a special case according to the + * logic below. */ + if (THIS->matrix[src].edges[dst].set) + goto found; + } + /* End of enumeration? */ + if (next_edge(THIS, &src, &dst)) { + GB.StopEnum(); + GB.Unref(&state->e); + return; + } + state->v.src = src; state->v.dst = dst; +found:; + GB_STRING str; + + str.type = GB_T_STRING; + + str.value.addr = THIS->matrix[src].name; + str.value.start = 0; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 0)); + + str.value.addr = THIS->matrix[dst].name; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 1)); + GB.ReturnObject(state->e); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_countVertices) + + GB.ReturnInteger(GB.Count(THIS->matrix)); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_countEdges) + + unsigned int i, j, count = GB.Count(THIS->matrix), edges = 0; + + for (i = 0; i < count; i++) + for (j = 0; j < count; j++) + if (THIS->matrix[i].edges[j].set) + edges++; + GB.ReturnInteger(edges); + +END_METHOD + +static int next_edge_vertical(CMATRIX *mat, unsigned int *srcp, + unsigned int *dstp) +{ + unsigned int src = *srcp, dst = *dstp; + unsigned int count = GB.Count(mat->matrix); + + do { + src = (src + 1) % count; + if (!src) + dst++; + if (dst >= count) + return -1; + } while (!mat->matrix[src].edges[dst].set); + *srcp = src; + *dstp = dst; + return 0; +} + +BEGIN_METHOD_VOID(Matrix_nextInEdge) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + dst = state->v.dst = get_cur_vertex(THIS); + GB.Array.New(&state->e, GB_T_STRING, 2); + GB.Ref(state->e); + if (THIS->matrix[src].edges[dst].set) + goto found; + } + if (next_edge_vertical(THIS, &src, &dst) || dst != state->v.dst) { + GB.StopEnum(); + GB.Unref(&state->e); + return; + } + state->v.src = src; +found:; + GB_STRING str; + + str.type = GB_T_STRING; + + str.value.addr = THIS->matrix[src].name; + str.value.start = 0; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 0)); + + str.value.addr = THIS->matrix[dst].name; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 1)); + GB.ReturnObject(state->e); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_nextOutEdge) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + src = state->v.src = get_cur_vertex(THIS); + GB.Array.New(&state->e, GB_T_STRING, 2); + GB.Ref(state->e); + if (THIS->matrix[src].edges[dst].set) + goto found; + } + if (next_edge(THIS, &src, &dst) || src != state->v.src) { + GB.StopEnum(); + GB.Unref(&state->e); + return; + } + state->v.dst = dst; +found:; + GB_STRING str; + + str.type = GB_T_STRING; + + str.value.addr = THIS->matrix[src].name; + str.value.start = 0; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 0)); + + str.value.addr = THIS->matrix[dst].name; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 1)); + GB.ReturnObject(state->e); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_nextAdjacent) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + src = state->v.src = get_cur_vertex(THIS); + state->e = (void *) 1; + if (THIS->matrix[src].edges[dst].set) + goto found; + } + if (next_edge(THIS, &src, &dst) || src != state->v.src) { + GB.StopEnum(); + return; + } + state->v.dst = dst; +found: + GB.ReturnString(THIS->matrix[dst].name); + +END_METHOD + +/* TODO: This is used in Add() and Remove() to completely delete the gb.gsl + * matrix. We could also enlarge/shrink it accordingly... */ +static void invalidate_gsl_matrix(CMATRIX *mat) +{ + GB.Unref(&mat->gsl_matrix); + mat->gsl_matrix = NULL; +} + +static void update_gsl_matrix(CMATRIX *mat, unsigned i, unsigned j) +{ + GB_FUNCTION put; + + if (!mat->gsl_matrix) + return; + if (GB.GetFunction(&put, mat->gsl_matrix, "_put", "vii", NULL)) { + GB.Error(E_NOPUT); + return; + } + GB.Push(3, GB_T_INTEGER, !!mat->matrix[i].edges[j].set, + GB_T_INTEGER, i, + GB_T_INTEGER, j); + GB.Call(&put, 3, 0); +} + +BEGIN_METHOD(Matrix_Add, GB_STRING name) + + unsigned int vert = get_vertex(THIS, STRING(name), LENGTH(name)), i; + VERT *new; + + if (vert != -1) + goto end; + + vert = GB.Count(THIS->matrix); + new = GB.Add(&THIS->matrix); + /* Add edge buffers to all other vertices */ + for (i = 0; i < vert; i++) { + EDGE *e = GB.Add(&THIS->matrix[i].edges); + + e->set = 0; + e->weight = 0; + } + GB.NewArray(&new->edges, sizeof(*new->edges), vert + 1); + /* No outgoing edges */ + memset(new->edges, 0, (vert + 1) * sizeof(*new->edges)); + + new->val.type = GB_T_NULL; + GB.StoreVariant(NULL, &new->val); + + new->name = GB.NewString(STRING(name), LENGTH(name)); + + GB.HashTable.Add(THIS->names, STRING(name), LENGTH(name), + (void *) (intptr_t) vert); + + invalidate_gsl_matrix(THIS); + +end: + THIS->v.vertex = vert; + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_Remove, GB_STRING vert) + + unsigned int vert = get_vertex(THIS, STRING(vert), LENGTH(vert)); + unsigned int count = GB.Count(THIS->matrix), i; + + if (vert == -1) { + GB.Error(E_NOVERTEX); + return; + } + for (i = 0; i < count; i++) { + if (i == vert) + continue; + GB.Remove(&THIS->matrix[i].edges, i, 1); + } + GB.FreeArray(&THIS->matrix[vert].edges); + GB.StoreVariant(NULL, &THIS->matrix[vert].val); + GB.FreeString(&THIS->matrix[vert].name); + GB.Remove(&THIS->matrix, vert, 1); + + GB.HashTable.Remove(THIS->names, STRING(vert), LENGTH(vert)); + + invalidate_gsl_matrix(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_Connect, GB_STRING src; GB_STRING dst; GB_FLOAT w) + + unsigned int src = get_vertex(THIS, STRING(src), LENGTH(src)), + dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + float w = VARGOPT(w, 1); + + if (src == -1 || dst == -1) { + GB.Error(E_NOVERTEX); + return; + } + THIS->matrix[src].edges[dst].set = 1; + THIS->matrix[src].edges[dst].weight = w; + THIS->v.src = src; THIS->v.dst = dst; + update_gsl_matrix(THIS, src, dst); + /* Duplicate if the graph is undirected */ + if (!THIS->directed && src != dst) { + THIS->matrix[dst].edges[src].set = 1; + THIS->matrix[dst].edges[src].weight = w; + update_gsl_matrix(THIS, dst, src); + } + + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_Disconnect, GB_STRING src; GB_STRING dst) + + unsigned int src = get_vertex(THIS, STRING(src), LENGTH(src)), + dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + + if (src == -1 || dst == -1) { + GB.Error(E_NOVERTEX); + return; + } + THIS->matrix[src].edges[dst].set = 0; + update_gsl_matrix(THIS, src, dst); + if (!THIS->directed && src != dst) { + THIS->matrix[dst].edges[src].set = 0; + update_gsl_matrix(THIS, dst, src); + } + +END_METHOD + +BEGIN_PROPERTY(Matrix_Matrix) + + unsigned int count = GB.Count(THIS->matrix), i, j; + void *obj; + GB_FUNCTION put; + + if (THIS->gsl_matrix) { + GB.ReturnObject(THIS->gsl_matrix); + return; + } + + if (GB.Component.Load("gb.gsl")) { + GB.Error("gb.gsl could not be found"); + return; + } + + GB.Push(3, GB_T_INTEGER, count, + GB_T_INTEGER, count, + GB_T_BOOLEAN, 0); + obj = GB.New(GB.FindClass("Matrix"), NULL, (void *) (intptr_t) 3); + if (GB.GetFunction(&put, obj, "_put", "vii", NULL)) { + GB.Error(E_NOPUT); + return; + } + /* TODO: Direct access possible? */ + for (i = 0; i < count; i++) { + for (j = 0; j < count; j++) { + GB.Push(3, GB_T_INTEGER, + !!THIS->matrix[i].edges[j].set, + GB_T_INTEGER, i, + GB_T_INTEGER, j); + GB.Call(&put, 3, 0); + } + } + THIS->gsl_matrix = obj; + GB.Ref(obj); + GB.ReturnObject(obj); + +END_PROPERTY + +GB_DESC CGraphMatrix[] = { + GB_DECLARE("GraphMatrix", sizeof(CMATRIX)), + GB_INHERITS("Graph"), + + GB_METHOD("_new", NULL, Matrix_new, "[(Directed)b(Weighted)b]"), + GB_METHOD("_free", NULL, Matrix_free, NULL), + + GB_METHOD("_getVertex", ".Matrix.Vertex", Matrix_getVertex, "(Vertex)s"), + GB_METHOD("_getEdge", ".Matrix.Edge", Matrix_getEdge, "(Src)s(Dst)s"), + + GB_METHOD("_nextVertex", "s", Matrix_nextVertex, NULL), + GB_METHOD("_nextEdge", "String[]", Matrix_nextEdge, NULL), + + GB_METHOD("_countVertices", "i", Matrix_countVertices, NULL), + GB_METHOD("_countEdges", "i", Matrix_countEdges, NULL), + + GB_METHOD("_nextInEdge", "String[]", Matrix_nextInEdge, NULL), + GB_METHOD("_nextOutEdge", "String[]", Matrix_nextOutEdge, NULL), + GB_METHOD("_nextAdjacent", "s", Matrix_nextAdjacent, NULL), + + GB_METHOD("Add", ".Matrix.Vertex", Matrix_Add, "(Name)s"), + GB_METHOD("Remove", NULL, Matrix_Remove, "(Vertex)s"), + GB_METHOD("Connect", ".Matrix.Edge", Matrix_Connect, "(Src)s(Dst)s[(Weight)f]"), + GB_METHOD("Disconnect", NULL, Matrix_Disconnect, "(Src)s(Dst)s"), + + GB_PROPERTY_SELF("Vertices", ".Matrix.Vertices"), + GB_PROPERTY_SELF("Edges", ".Matrix.Edges"), + + /* + * Require gb.gsl. + */ + //GB_STATIC_METHOD("FromMatrix", "GraphMatrix", Matrix_FromMatrix, "(Matrix)Matrix;"), + GB_PROPERTY_READ("Matrix", "Matrix", Matrix_Matrix), + + GB_END_DECLARE +}; + +BEGIN_METHOD(MatrixVertices_get, GB_STRING vert) + + THIS->v.vertex = get_vertex(THIS, STRING(vert), LENGTH(vert)); + GB.ReturnSelf(THIS); + +END_METHOD + +GB_DESC CMatrixVertices[] = { + GB_DECLARE_VIRTUAL(".Matrix.Vertices"), + GB_INHERITS(".Graph.Vertices"), + + GB_METHOD("_get", ".Matrix.Vertex", MatrixVertices_get, "(Vertex)s"), + + GB_END_DECLARE +}; + +BEGIN_METHOD(MatrixEdges_get, GB_STRING src; GB_STRING dst) + + THIS->v.src = get_vertex(THIS, STRING(src), LENGTH(src)); + THIS->v.dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + GB.ReturnSelf(THIS); + +END_METHOD + +GB_DESC CMatrixEdges[] = { + GB_DECLARE_VIRTUAL(".Matrix.Edges"), + GB_INHERITS(".Graph.Edges"), + + GB_METHOD("_get", ".Matrix.Edge", MatrixEdges_get, "(Src)s(Dst)s"), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(MatrixVertex_InDegree) + + unsigned int i, count = GB.Count(THIS->matrix), deg = 0; + + for (i = 0; i < count; i++) + if (THIS->matrix[i].edges[THIS->v.vertex].set) + deg++; + GB.ReturnInteger(deg); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixVertex_OutDegree) + + unsigned int j, count = GB.Count(THIS->matrix), deg = 0; + + for (j = 0; j < count; j++) + if (THIS->matrix[THIS->v.vertex].edges[j].set) + deg++; + GB.ReturnInteger(deg); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixVertex_Name) + + char *name = THIS->matrix[THIS->v.vertex].name; + + if (READ_PROPERTY) { + GB.ReturnString(name); + return; + } + GB.HashTable.Remove(THIS->names, name, GB.StringLength(name)); + GB.FreeString(&THIS->matrix[THIS->v.vertex].name); + THIS->matrix[THIS->v.vertex].name = + GB.NewString(PSTRING(), PLENGTH()); + GB.HashTable.Add(THIS->names, PSTRING(), PLENGTH(), + (void *) (intptr_t) THIS->v.vertex); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixVertex_Value) + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->matrix[THIS->v.vertex].val); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->matrix[THIS->v.vertex].val); + +END_METHOD + +GB_DESC CMatrixVertex[] = { + GB_DECLARE_VIRTUAL(".Matrix.Vertex"), + GB_INHERITS(".Graph.Vertex"), + + GB_PROPERTY_READ("InDegree", "i", MatrixVertex_InDegree), + GB_PROPERTY_READ("OutDegree", "i", MatrixVertex_OutDegree), + GB_PROPERTY("Name", "s", MatrixVertex_Name), + GB_PROPERTY("Value", "v", MatrixVertex_Value), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(MatrixEdge_Src) + + int src = THIS->v.src; + + GB.ReturnString(THIS->matrix[src].name); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixEdge_Dst) + + int dst = THIS->v.dst; + + GB.ReturnString(THIS->matrix[dst].name); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixEdge_Weight) + + int src = THIS->v.src, dst = THIS->v.dst; + + if (READ_PROPERTY) { + GB.ReturnFloat(THIS->matrix[src].edges[dst].weight); + return; + } + THIS->matrix[src].edges[dst].weight = VPROP(GB_FLOAT); + if (!THIS->directed && src != dst) + THIS->matrix[dst].edges[src].weight = VPROP(GB_FLOAT); + +END_PROPERTY + +GB_DESC CMatrixEdge[] = { + GB_DECLARE_VIRTUAL(".Matrix.Edge"), + GB_INHERITS(".Graph.Edge"), + + GB_PROPERTY_READ("Src", "s", MatrixEdge_Src), + GB_PROPERTY_READ("Dst", "s", MatrixEdge_Dst), + GB_PROPERTY("Weight", "f", MatrixEdge_Weight), + + GB_PROPERTY_READ("Source", "s", MatrixEdge_Src), + GB_PROPERTY_READ("Destination", "s", MatrixEdge_Dst), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_graphmatrix.h b/main/lib/data/c_graphmatrix.h new file mode 100644 index 00000000..fa1309e8 --- /dev/null +++ b/main/lib/data/c_graphmatrix.h @@ -0,0 +1,34 @@ +/* + * c_graphmatrix.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_GRAPHMATRIX_H +#define __C_GRAPHMATRIX_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_GRAPHMATRIX_C +extern GB_DESC CGraphMatrix[], CMatrixVertices[], CMatrixEdges[], + CMatrixVertex[], CMatrixEdge[]; +#endif + +#endif /* __C_GRAPHMATRIX_H */ diff --git a/main/lib/data/c_heap.c b/main/lib/data/c_heap.c new file mode 100644 index 00000000..bb4d8d79 --- /dev/null +++ b/main/lib/data/c_heap.c @@ -0,0 +1,406 @@ +/* + * c_heap.c - (Min-/Max-)Heap and PrioSet + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_HEAP_C + +#include "gambas.h" +#include "gb_common.h" /* EXTERN for gbx_compare.h */ +#include "gbx_compare.h" /* GB_COMP_{ASCENT,DESCENT} */ +#include "gbx_type.h" /* TYPE_is_object() */ + +#include "c_heap.h" + +typedef struct { + GB_BASE ob; + int mode; + int count; + GB_VARIANT_VALUE *h; +} CHEAP; /* "Hey! Don't talk like this about my data structures!" */ + +static inline int compare(CHEAP *heap, int i, int j) +{ + int res = GB.CompVariant(&heap->h[i], &heap->h[j]); + + return heap->mode == GB_COMP_ASCENT ? res : -res; +} + +static inline int compare1(CHEAP *heap, GB_VARIANT_VALUE *x, int j) +{ + int res = GB.CompVariant(x, &heap->h[j]); + + return heap->mode == GB_COMP_ASCENT ? res : -res; +} + +static inline int compare3(CHEAP *heap, GB_VARIANT_VALUE *x, + GB_VARIANT_VALUE *y) +{ + int res = GB.CompVariant(x, y); + + return heap->mode == GB_COMP_ASCENT ? res : -res; +} + +static inline void copy(CHEAP *heap, int src, int dst) +{ + memmove(&heap->h[dst], &heap->h[src], sizeof(GB_VARIANT_VALUE)); +} + +static inline void copy1(CHEAP *heap, int src, GB_VARIANT_VALUE *dst) +{ + memmove(dst, &heap->h[src], sizeof(GB_VARIANT_VALUE)); +} + +static inline void copy2(CHEAP *heap, GB_VARIANT_VALUE *src, int dst) +{ + memmove(&heap->h[dst], src, sizeof(GB_VARIANT_VALUE)); +} + +#define left(k) (2 * (k) + 1) +#define right(k) (left(k) + 1) +#define parent(k) (((k) - 1) / 2) + +static int upheap(CHEAP *heap, int k) +{ + GB_VARIANT_VALUE x; + int r = 0; + + copy1(heap, k, &x); + while (k && compare1(heap, &x, parent(k)) < 0) { + copy(heap, parent(k), k); + k = parent(k); + r++; + } + copy2(heap, &x, k); + return r; +} + +static int downheap(CHEAP *heap, int k) +{ + int count = GB.Count(heap->h), r = 0; + GB_VARIANT_VALUE x; + + copy1(heap, k, &x); + while (k <= parent(count - 1)) { + int j, l = j = left(k), r = right(k); + + if (r < count && compare(heap, l, r) > 0) + j = r; + if (compare1(heap, &x, j) <= 0) + break; + copy(heap, j, k); + k = j; + r++; + } + copy2(heap, &x, k); + return r; +} + +static void new_heap(CHEAP *heap, int count) +{ + GB.NewArray(&heap->h, sizeof(*heap->h), count); +} + +static void rebuild(CHEAP *heap) +{ + int i; + + for (i = parent(GB.Count(heap->h) - 1); i >= 0; i--) + downheap(heap, i); +} + +static void from_array(CHEAP *heap, GB_ARRAY array) +{ + int count = GB.Array.Count(array), i; + + new_heap(heap, count); + for (i = 0; i < count; i++) { + memcpy(&heap->h[i], GB.Array.Get(array, i), + sizeof(*heap->h)); + if (TYPE_is_object(heap->h[i].type)) + GB.Ref(heap->h[i].value._object); + } + rebuild(heap); +} + +#define THIS ((CHEAP *) _object) + +/**G + * Creates a new Heap. + * + * If 'Mode' is gb.Ascent, it's a MinHeap, i.e. the smallest element is at + * the beginning. If 'mode' is gb.Descent, it's a MaxHeap. + * + * If the 'Array' argument is given, a copy of that array is transformed + * into a Heap (by using a bottom-up algorithm which is O(n)). + */ +BEGIN_METHOD(Heap_new, GB_INTEGER mode; GB_OBJECT array) + + THIS->mode = VARG(mode); + if (THIS->mode != GB_COMP_ASCENT && THIS->mode != GB_COMP_DESCENT) { + GB.Error("Invalid mode"); + return; + } + + if (MISSING(array)) { + new_heap(THIS, 0); + } else { + GB_ARRAY array = (GB_ARRAY) VARG(array); + + if (GB.CheckObject(array)) + return; + from_array(THIS, array); + } + +END_METHOD + +/**G + * Free up storage of the Heap + */ +BEGIN_METHOD_VOID(Heap_free) + + int count = GB.Count(THIS->h), i; + + for (i = 0; i < count; i++) + GB.StoreVariant(NULL, &THIS->h[i]); + GB.FreeArray(&THIS->h); + +END_METHOD + +/**G + * Insert an element into the Heap. + */ +BEGIN_METHOD(Heap_Insert, GB_VARIANT data) + + GB.StoreVariant(ARG(data), GB.Add(&THIS->h)); + upheap(THIS, GB.Count(THIS->h) - 1); + +END_METHOD + +static void delete(CHEAP *heap, int i, GB_VARIANT_VALUE *x) +{ + int count = GB.Count(heap->h); + + copy1(heap, i, x); + copy(heap, count - 1, i); + GB.Remove(&heap->h, count - 1, 1); + downheap(heap, i); +} + +/**G + * Remove the first element. + */ +BEGIN_METHOD_VOID(Heap_Remove) + + int count = GB.Count(THIS->h); + GB_VARIANT_VALUE x; + + if (!count) { + GB.Error(GB_ERR_BOUND); + return; + } + + delete(THIS, 0, &x); + GB.ReturnVariant(&x); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &x); + GB.ReturnRelease(); + +END_METHOD + +/* + * Comparison by identity works as follows: + * - if both are objects and have _identity() methods which return + * variants, the return value is the comparison of those variants, + * - if they are objects without _identity(), they are compared based + * on their addresses in memory, + * - else they are not objects and are normally compared. + */ +static int compare_identity(CHEAP *heap, GB_VARIANT_VALUE *a, + GB_VARIANT_VALUE *b) +{ + static int has_identity; + static GB_FUNCTION aid; + static GB_VARIANT_VALUE *last = NULL; + static GB_VALUE x; + + GB_FUNCTION bid; + GB_VALUE *y; + + if (!TYPE_is_object(a->type) || !TYPE_is_object(b->type)) + return compare3(heap, a, b); + + if (last != a) { + if (!GB.GetFunction(&aid, a->value._object, "_identity", + NULL, "v")) { + has_identity = 1; + memcpy(&x, GB.Call(&aid, 0, 0), sizeof(x)); + } else { + has_identity = 0; + } + GB.Error(NULL); /* Clear any GetFunction error */ + last = a; + } + + /* No _identity()? */ + if (!has_identity || GB.GetFunction(&bid, b->value._object, + "_identity", NULL, "v")) { + GB.Error(NULL); + return a->value._object != b->value._object; + } + + y = GB.Call(&bid, 0, 0); + return compare3(heap, &x._variant.value, &y->_variant.value); +} + +/**G + * Find all occurences of `Old' and replace them by `New'. This is an O(n) + * operation. Additionally the heap has to be rebuilt as soon as there is + * more than one replacement made. + * + * If `New' is Null, then the entry will be deleted. + * + * The search for objects is done by identity and *not* by using the + * _compare() method. "By identity" means that if two objects are to be + * compared and both have a special _identity() method returning a Variant, + * the return values of these methods are compared. + * + * If one of the objects does not implement the _identity() method, the + * default is comparison by object addresses in memory. This strategy lets + * you save primitive data types (Integer, Boolean, String), etc. in the + * heap. But you can also *distinct* objects in equivalence classes. The + * _compare() method defines your equivalence relation, the identity + * comparison enables to discern different objects. + * + * If we are a heap of objects whose _compare() methods compare some sort of + * priority, and `Old' is the same object as `New', this can be used to + * propagate a priority change made to the object and will correct its + * position in the heap. + * + * This has an application in Dijkstra's algorithm (or any priority first + * search like Prim or A*) where you would update a node's priority. + */ +BEGIN_METHOD(Heap_Update, GB_VARIANT old; GB_VARIANT new) + + int count = GB.Count(THIS->h), i, found = 0, idx = -1; + GB_VARIANT_VALUE *old, *new; + + old = &VARG(old); new = &VARG(new); + for (i = 0; i < count; i++) { + if (compare_identity(THIS, old, &THIS->h[i])) + continue; + /* + * Make Null delete the entry. We don't count that as a + * finding because we can fix the heap up on the fly. + */ + if (new->type == GB_T_NULL) { + GB_VARIANT_VALUE buf; + + delete(THIS, i, &buf); + GB.StoreVariant(NULL, &buf); + count = GB.Count(THIS->h); + continue; + } + /* XXX: If `old' and `new' are the same and if they're + * objects, memory errors will occur if you store one over + * the other... Maybe StoreVariant() wasn't made to replace + * an object by itself (refcount goes to zero before it is + * incremented again, maybe?) */ + if (!TYPE_is_object(THIS->h[i].type) + || THIS->h[i].value._object != new->value._object) + GB.StoreVariant(ARG(new), &THIS->h[i]); + found++; + idx = i; + } + /* + * Most applications will have pairwise distinct elements in the + * heap, so that at most one element is changed. In this case, we + * can get away more quickly (O(log n) vs. O(n)) with an upheap() + * or downheap() call. + */ + if (found == 1) { + if (!upheap(THIS, idx)) + downheap(THIS, idx); + } else if (found) { + rebuild(THIS); + } + +END_METHOD + +/**G + * Return or set the first element of the Heap. + */ +BEGIN_PROPERTY(Heap_First) + + if (!GB.Count(THIS->h)) { + GB.Error(GB_ERR_BOUND); + return; + } + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->h[0]); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->h[0]); + downheap(THIS, 0); + +END_PROPERTY + +/**G + * Return the number of elements in the Heap. + */ +BEGIN_PROPERTY(Heap_Count) + + GB.ReturnInteger(GB.Count(THIS->h)); + +END_PROPERTY + +/**G + * Return whether the Heap is empty. + */ +BEGIN_PROPERTY(Heap_IsEmpty) + + GB.ReturnBoolean(GB.Count(THIS->h) == 0); + +END_PROPERTY + +GB_DESC CHeap[] = { + /**G Heap + * This class implements a dynamic heap. It can be a MinHeap or a + * MaxHeap, depending on what mode you specify on construction. + * + * Being `dynamic' means that this class allows you to update the + * data or the position of elements which are already in the heap. + */ + GB_DECLARE("Heap", sizeof(CHEAP)), + + GB_METHOD("_new", NULL, Heap_new, "(Mode)i[(Array)Variant[];]"), + GB_METHOD("_free", NULL, Heap_free, NULL), + + GB_METHOD("Insert", NULL, Heap_Insert, "(Data)v"), + GB_METHOD("Remove", "v", Heap_Remove, NULL), + GB_METHOD("Update", NULL, Heap_Update, "(Old)v(New)v"), + + GB_PROPERTY("First", "v", Heap_First), + + GB_PROPERTY_READ("Count", "i", Heap_Count), + GB_PROPERTY_READ("IsEmpty", "b", Heap_IsEmpty), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_heap.h b/main/lib/data/c_heap.h new file mode 100644 index 00000000..6d1ae526 --- /dev/null +++ b/main/lib/data/c_heap.h @@ -0,0 +1,33 @@ +/* + * c_heap.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_HEAP_H +#define __C_HEAP_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_HEAP_C +extern GB_DESC CHeap[]; +#endif + +#endif /* __C_HEAP_H */ diff --git a/main/lib/data/c_list.c b/main/lib/data/c_list.c new file mode 100644 index 00000000..e1cf9528 --- /dev/null +++ b/main/lib/data/c_list.c @@ -0,0 +1,1363 @@ +/* + * c_list.c - Circular doubly-linked lists + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_LIST_C + +#include + +#include "gambas.h" +#include "gb_common.h" +#include "list.h" +#include "c_list.h" + +#define CHUNK_SIZE 16 + +/* + * List implementation properties: + * + Increase cache locality by saving CHUNK_SIZE values inside a single + * chunk. + * + A special algorithm to re-arrange the values in a chunk guarantees some + * properties we can use to speed up the critical paths, such as + * traversals. + * + Cached references ('anchors') in the list which can be used calculate + * the best starting point for a traversal to a given index. + * - Fragmentation in the middle chunks in a list because of the (O) + * postulate of the rearrangement algorithm. + */ + +typedef struct { + LIST list; + GB_VARIANT_VALUE var[CHUNK_SIZE]; + int first; /* First valid element in var */ + int last; /* Last valid element in var */ +} CHUNK; +#define get_chunk(node) LIST_data(node, CHUNK, list) + +typedef struct { + CHUNK *ck; + int idx; /* Absolute index into ->ck->var */ + int lgi; /* Absolute list global index [-Count; Count - 1] */ +} VAL; + +typedef struct { + GB_BASE ob; + LIST list; /* Beginning of linked CHUNKs */ + VAL current; /* Current element */ + size_t count; /* Do not iterate over all elements to get this */ + int autonorm; /* Automatically normalise indices */ +} CLIST; + +static void CHUNK_init(CHUNK *ck) +{ + int i; + + LIST_init(&ck->list); + for (i = 0; i < CHUNK_SIZE; i++) + ck->var[i].type = GB_T_NULL; + ck->first = -1; + ck->last = -1; +} + +static CHUNK *CHUNK_new(void) +{ + CHUNK *new; + + GB.Alloc((void **) &new, sizeof(*new)); + CHUNK_init(new); + return new; +} + +static inline int CHUNK_count(CHUNK *ck) +{ + return ck->last - ck->first + 1; +} + +static inline int CHUNK_is_first(CLIST *list, CHUNK *ck) +{ + return list->list.next == &ck->list; +} + +static inline int VAL_is_first(CLIST *list, VAL *val) +{ + CHUNK *ck = val->ck; + + return CHUNK_is_first(list, ck) && val->idx == ck->first; +} + +static inline int CHUNK_is_last(CLIST *list, CHUNK *ck) +{ + return list->list.prev == &ck->list; +} + +static inline int VAL_is_last(CLIST *list, VAL *val) +{ + CHUNK *ck = val->ck; + + return CHUNK_is_last(list, ck) && val->idx == ck->last; +} + +static void CHUNK_free_all(CHUNK *ck) +{ + int i; + + if (ck->first < 0 || ck->last < 0) + return; + for (i = ck->first; i <= ck->last; i++) + if (ck->var[i].type != GB_T_NULL) + GB.StoreVariant(NULL, &ck->var[i]); + ck->first = ck->last = -1; +} + +static void CHUNK_destroy(CHUNK *ck) +{ + /* The chunk *must* be unlinked */ + CHUNK_free_all(ck); + GB.Free((void **) &ck); +} + +#define THIS ((CLIST *) _object) + +BEGIN_METHOD_VOID(List_new) + + LIST_init(&THIS->list); + THIS->current.ck = NULL; + THIS->count = 0; + THIS->autonorm = 0; + +END_METHOD + +BEGIN_METHOD_VOID(List_free) + + LIST *node, *next; + CHUNK *ck; + + list_for_each_safe(node, &THIS->list, next) { + LIST_unlink(node); + ck = get_chunk(node); + CHUNK_destroy(ck); + } + THIS->current.ck = NULL; + THIS->count = 0; + +END_METHOD + +static inline GB_VARIANT_VALUE *VAL_value(VAL *val) +{ +#ifdef DEBUG_ME + if (val->idx < val->ck->first || val->idx > val->ck->last) + printf(": err: %d : %d,%d\n", val->idx, val->ck->first, + val->ck->last); +#endif + assert(val->idx >= val->ck->first && val->idx <= val->ck->last); + return &val->ck->var[val->idx]; +} + +static inline int VAL_is_equal(VAL *v1, VAL *v2) +{ + return v1->ck == v2->ck && v1->idx == v2->idx; +} + +/* + * A VAL carries an absolute index of the element it represents in the list. + * This must be updated by each of the functions that modified a VAL because + * they are trusted by traversing algorithms (which have access to Current + * and the enumerators) when looking for the shortest around through the + * list. + * + * The index can be positive or negative. We need some magic to update it + * correctly everytime and with every implementation of C (that's about the + * sign of the result from a % operation). + * + * (Be sure to compile this code with a high optimisation level.) + */ + +#ifndef sgn +# define sgn(x) \ +({ \ + int __x = (x); \ + \ + __x < 0 ? -1 : (__x ? 1 : 0); \ +}) +#endif + +/* One's complement abs() */ +#define onesabs(x) \ +({ \ + int __x = (x); \ + \ + __x < 0 ? ~__x : __x; \ +}) + +/* Get corresponding non-negative index over the list */ +#define abslgi(list, i) \ +({ \ + CLIST *__l = (list); \ + int __i = (i); \ + \ + __i < 0 ? __l->count + __i : __i; \ +}) + +#define update_lgi(list, val, i) \ +do { \ + CLIST *__l = (list); \ + VAL *__v = (val); \ + int __i = (i); \ + \ + if (!__l->count) { \ + __v->ck = NULL; \ + } else { \ + __v->lgi = (onesabs(__i) % __l->count); \ + if (__i < 0) \ + __v->lgi = ~__v->lgi; \ + } \ +} while (0) + +static void CLIST_first(CLIST *list, VAL *buf) +{ + if (!list->count) { + buf->ck = NULL; + /* + * The VAL is invalid but we want to clear this anyways. + */ + update_lgi(list, buf, 0); + return; + } + + buf->ck = get_chunk(list->list.next); + buf->idx = buf->ck->first; + update_lgi(list, buf, 0); +} + +static void CLIST_last(CLIST *list, VAL *buf) +{ + if (!list->count) { + buf->ck = NULL; + update_lgi(list, buf, 0); + return; + } + buf->ck = get_chunk(list->list.prev); + buf->idx = buf->ck->last; + update_lgi(list, buf, -1); +} + +/* + * Modify 'val' so that it points to the next/prev valid value. 'first' is + * used to detect the end of an enumeration, i.e. where no other elements + * should be looked for. In this case, NULL is written to val->ck. + * + * Since the LGI is said to be in bounds of [-Count; Count - 1], these + * functions must watch when the list head is traversed and reset the index + * accordingly. + */ + +static void CHUNK_next(CLIST *list, VAL *val) +{ + LIST *node; + + update_lgi(list, val, val->lgi + 1); + + /* Try to just update the index. */ + if (val->idx < val->ck->last) { + val->idx++; + return; + } + + /* Go to next chunk */ + if ((node = val->ck->list.next) == &list->list) + node = node->next; + val->ck = get_chunk(node); + val->idx = val->ck->first; +} + +static void CHUNK_next_enum(CLIST *list, VAL *first, VAL *val) +{ + CHUNK *ck = val->ck; + LIST *node; + + assert(first != val); + + update_lgi(list, val, val->lgi + 1); + + if (val->idx < ck->last) { + val->idx++; + if (VAL_is_equal(first, val)) + goto no_next; + return; + } + if ((node = ck->list.next) == &list->list) + node = node->next; + ck = get_chunk(node); + val->ck = ck; + val->idx = ck->first; + if (VAL_is_equal(first, val)) + goto no_next; + return; + +no_next: + val->ck = NULL; + update_lgi(list, val, 0); +} + +static void CHUNK_prev(CLIST *list, VAL *val) +{ + LIST *node; + + update_lgi(list, val, val->lgi - 1); + + if (val->idx > val->ck->first) { + val->idx--; + return; + } + if ((node = val->ck->list.prev) == &list->list) + node = node->prev; + val->ck = get_chunk(node); + val->idx = val->ck->last; +} + +static void CHUNK_prev_enum(CLIST *list, VAL *first, VAL *val) +{ + CHUNK *ck = val->ck; + LIST *node; + + assert(first != val); + + update_lgi(list, val, val->lgi - 1); + + if (val->idx > ck->first) { + val->idx--; + if (VAL_is_equal(first, val)) + goto no_prev; + return; + } + if ((node = ck->list.prev) == &list->list) + node = node->prev; + ck = get_chunk(node); + val->ck = ck; + val->idx = ck->last; + if (VAL_is_equal(first, val)) + goto no_prev; + return; + +no_prev: + val->ck = NULL; + update_lgi(list, val, 0); +} + +/* + * Random access function stuff (for CLIST_get()). + * Negative 'idx' makes go backwards. The 'val' is filled. Out of bounds is + * signalled by val->ck == NULL. + * + * We use all references (i.e. Current and the enumerators) to get a good + * anchor, i.e. a VAL and a direction from where we can get to the desired + * index the shortest way. + */ + +/* XXX: sizeof(VAL) may never exceed 3*sizeof(intptr_t)! */ +struct enum_state { + CHUNK *first; + VAL next; +}; + +#define begin_all_references(list) \ +do { \ + CLIST *__list = list; \ + VAL *__vp; \ + void *__ebuf; \ + struct enum_state *__es = NULL; \ + \ + __ebuf = GB.BeginEnum(__list); \ + if (!__list->current.ck) { \ + if (GB.NextEnum()) { \ + __vp = NULL; \ + } else { \ + __es = (struct enum_state *) GB.GetEnum(); \ + __vp = &__es->next; \ + } \ + } else { \ + __vp = &__list->current; \ + } \ + for (; __vp; __vp = GB.NextEnum() ? NULL : \ + (__es = (struct enum_state *) GB.GetEnum(), &__es->next)) { + +#define end_all_references \ + } \ + GB.EndEnum(__ebuf); \ +} while (0) + +struct anchor { + VAL start; + int direction; +}; + +/* 'idx' is required to be non-negative and in bounds at this point. */ +static inline void get_best_anchor(CLIST *list, int idx, struct anchor *buf) +{ + int d, tmp; + + /* Distance from head forwards/backwards/of all references */ + d = idx; + tmp = list->count - 1 - idx; + if (tmp < d) { + d = tmp; + CLIST_last(list, &buf->start); + } else { + CLIST_first(list, &buf->start); + } + begin_all_references(list) { + tmp = abs(abslgi(list, __vp->lgi) - idx); + if (tmp < d) { + d = tmp; + memcpy(&buf->start, __vp, sizeof(buf->start)); + } + } end_all_references; + + buf->direction = sgn(idx - abslgi(list, buf->start.lgi)); +} + +static inline void get_body_forward(CLIST *list, LIST *node, int i, + VAL *val) +{ + CHUNK *ck; + int count; + + while (1) { + ck = get_chunk(node); + count = CHUNK_count(ck); + + if (i < count) { + val->ck = ck; + val->idx = ck->first + i; + return; + } + i -= count; + do + node = node->next; + while (node == &list->list); + } +} + +static inline void get_body_backward(CLIST *list, LIST *node, int i, + VAL *val) +{ + CHUNK *ck; + int count; + + while (1) { + do + node = node->prev; + while (node == &list->list); + ck = get_chunk(node); + count = -CHUNK_count(ck); + + if (i >= count) { + val->ck = ck; + val->idx = ck->last + i + 1; + return; + } + i -= count; + } +} + +static void CLIST_get(CLIST *list, int idx, VAL *val) +{ + LIST *node; + int i, dir; + struct anchor anchor; + + /* Make a non-negative index */ + i = abslgi(list, idx); + /* We do _not_ allow indices to wrap around the end, like we could: + * i %= list->count. Don't use a loop just to detect that case. */ + if (i >= list->count) { + /* Not enough elements. */ + val->ck = NULL; + return; + } + + get_best_anchor(list, i, &anchor); + dir = anchor.direction; + + update_lgi(list, val, idx); + /* Got that index in a reference already? Just copy. */ + if (!dir) { + val->ck = anchor.start.ck; + val->idx = anchor.start.idx; + return; + } + + node = &anchor.start.ck->list; + /* + * Don't start exactly at the given anchor point (possibly in the + * middle of a chunk) but instead at the beginning of the chunk. + * Because of cache spatiality, this is not a great loss and the + * code is simplified much. + */ + i -= abslgi(list, anchor.start.lgi); + i += anchor.start.idx - anchor.start.ck->first; + + /* + * Prevent a if (i < 0) branch prediction catastrophe if we merged + * both algorithms into one loop. + */ + if (i < 0) + get_body_backward(list, node, i, val); + else + get_body_forward(list, node, i, val); +} + +BEGIN_METHOD_VOID(List_next) + + struct enum_state *state = GB.GetEnum(); + GB_VARIANT_VALUE *val; + /* XXX: Would like to cache that in the enum_state but no space left + * there... */ + VAL start; + + if (!state->first) { /* Beginning */ + CLIST_first(THIS, &state->next); + state->first = state->next.ck; + } + /* No elements left? */ + if (!state->next.ck) { + GB.StopEnum(); + return; + } + + val = VAL_value(&state->next); + start.ck = state->first; + start.idx = start.ck->first; + CHUNK_next_enum(THIS, &start, &state->next); + GB.ReturnVariant(val); + +END_METHOD + +/* + * The same as List_next but backwards. + */ + +BEGIN_METHOD_VOID(ListBackwards_next) + + struct enum_state *state = GB.GetEnum(); + GB_VARIANT_VALUE *val; + VAL start; + + if (!state->first) { /* Beginning */ + CLIST_last(THIS, &state->next); + state->first = state->next.ck; + } + /* No elements left? */ + if (!state->next.ck) { + state->first = NULL; + GB.StopEnum(); + return; + } + + val = VAL_value(&state->next); + start.ck = state->first; + start.idx = start.ck->last; + CHUNK_prev_enum(THIS, &start, &state->next); + GB.ReturnVariant(val); + +END_METHOD + +static inline int normalise_index(CLIST *list, int index) +{ + int i; + + i = onesabs(index) % list->count; + if (index < 0) + i = ~i; + return i; +} + +BEGIN_METHOD(List_get, GB_INTEGER index) + + int index = VARG(index); + VAL val; + + if (THIS->autonorm) + index = normalise_index(THIS, index); + CLIST_get(THIS, index, &val); + if (!val.ck) { + GB.Error(GB_ERR_BOUND); + return; + } + GB.ReturnVariant(VAL_value(&val)); + +END_METHOD + +BEGIN_METHOD(List_put, GB_VARIANT var; GB_INTEGER index) + + int index = VARG(index); + VAL val; + + if (THIS->autonorm) + index = normalise_index(THIS, index); + CLIST_get(THIS, index, &val); + if (!val.ck) { + GB.Error(GB_ERR_BOUND); + return; + } + GB.StoreVariant(ARG(var), VAL_value(&val)); + +END_METHOD + +/* + * The main problem when modifying the list structure is that there are + * references to VALs in Current and all the enumerators. + * + * The following postulates shall be met by the algorithms: + * + * (V) Value. If an element other than that a VAL refers to is removed or an + * element is added, the reference shall be modified according to the + * operation, i.e. it shall stay pointing to the particular value it has + * pointed to before. References are value-bound. + * + * (B) Beginning. If the element a reference points to is removed, the + * reference shall remain relative to the beginning of the list (as long + * as it doesn't get empty), i.e. it moves on to the next value. This + * will guarantee that the following code works as expected: + * + * Dim vEnum As Variant + * + * For Each vEnum In hList + * hList.Take(hList.FindFirst(vEnum)) + * Next + * + * If the list gets empty, the references are invalidated. + * + * The rearrangement algorithm shall assure the following: + * + * (C) Coherency. All values in a chunk must be contiguous. + * + * (A) Alignment. The first chunk has all elements aligned to its end; the + * last chunk has all elements aligned to its beginning. For the special + * case of only one chunk in the List (the 'sole chunk'), it is not + * specially aligned but its initial element is at CHUNK_SIZE/2-1. + * Should a chunk be allocated and linked into the middle of the list, + * its initial element shall also be at CHUNK_SIZE/2-1. + * + * (L) Least Copy. Rearrangement for any non-aligned chunk shall strive for + * the least copy operations. + * + * (O) Order. Values get never reordered with respect to the list. It is + * possible, however, to move elements to other chunks as long as the + * order persists. + * + * Note that if (O) would not apply, on the one hand, we could make more + * intelligent algorithms, but on the other hand, the first posulates must + * be improved so it remains in the first place. + */ + +static void CLIST_append(CLIST *list, GB_VARIANT *var) +{ + CHUNK *ck; + + ck = get_chunk(list->list.next); + /* (A) */ + if (!list->count) { + ck = CHUNK_new(); + ck->first = ck->last = CHUNK_SIZE / 2 - 1; + LIST_append(&list->list, &ck->list); + } else if (ck->first == 0) { + ck = CHUNK_new(); + ck->first = ck->last = CHUNK_SIZE - 1; + LIST_append(&list->list, &ck->list); + } else { + ck->first--; + } + /* (C), (O) */ + GB.StoreVariant(var, &ck->var[ck->first]); + list->count++; + + /* (V) */ + begin_all_references(list) { + if (__vp->lgi >= 0) + __vp->lgi++; + if (__vp->ck == ck) + __vp->idx++; + } end_all_references; +} + +static void CLIST_prepend(CLIST *list, GB_VARIANT *var) +{ + CHUNK *ck; + + ck = get_chunk(list->list.prev); + /* (A) */ + if (!list->count) { + ck = CHUNK_new(); + ck->first = ck->last = CHUNK_SIZE / 2 - 1; + LIST_prepend(&list->list, &ck->list); + } else if (ck->last == CHUNK_SIZE - 1) { + ck = CHUNK_new(); + ck->first = ck->last = 0; + LIST_prepend(&list->list, &ck->list); + } else { + ck->last++; + } + /* (C), (O) */ + GB.StoreVariant(var, &ck->var[ck->last]); + list->count++; + + /* (V) */ + begin_all_references(list) { + if (__vp->lgi < 0) + __vp->lgi--; + } end_all_references; +} + +/* + * With the VAL_append()/prepend() functions it is a little more difficult. + * We have to distinguish three cases (processed in this order): + * 1) There is space for the operation in this chunk: so shift elements and + * insert the new value; + * 1a) If the element is to be appended/prepended after/before the + * last/first element, do not shift; + * 2) There is space _immediately_ free in a neighbour chunk for the + * operation (for append it's the next chunk, for prepend the previous + * one), i.e. we don't need to shift the neighbour chunk, then shift + * values into this chunk and insert the new value. This may never cross + * the list head!; + * 2a) See 1a); + * 3) If none of the previous things worked, allocate a new chunk and shift + * in there; + * 3a) See 1a); + * + * Point 2) didn't shift values in the neighbour because this could go + * infinitely through the list so we just allocate a new chunk to do it fast + * and easily. + */ + +static void VAL_append(CLIST *list, VAL *val, GB_VARIANT *var) +{ + CHUNK *ck, *next = NULL; + int s, n, shifted_to_next = 0; + GB_VARIANT_VALUE *buf; + VAL back; + + ck = val->ck; + /* Currently not (L). We only shift towards the end. */ + if (ck->last < CHUNK_SIZE - 1) { /* 1) */ + ck->last++; + if (val->idx == ck->last - 1) { /* 1a) */ + buf = &ck->var[ck->last]; + } else { + shift: + s = val->idx + 1; + n = ck->last - s; + memmove(&ck->var[s + 1], &ck->var[s], + n * sizeof(ck->var[0])); + buf = &ck->var[s]; + } + } else { + LIST *node = ck->list.next; + + /* 2) */ + if (node == &list->list) + goto add_new_chunk; + + next = get_chunk(node); + if (next->first) { + next->first--; + if (val->idx == ck->last) { /* 2a) */ + buf = &next->var[next->first]; + goto have_buf; + } + shift_to_next: + shifted_to_next = 1; + memcpy(&next->var[next->first], &ck->var[ck->last], + sizeof(ck->var[0])); + goto shift; + } else { /* 3) */ + add_new_chunk: + next = CHUNK_new(); + next->first = next->last = CHUNK_SIZE / 2 - 1; + LIST_append(&ck->list, &next->list); + if (val->idx == ck->last) { /* 3a) */ + buf = &next->var[next->first]; + goto have_buf; + } + goto shift_to_next; + } + } + +have_buf: + bzero(buf, sizeof(*buf)); + buf->type = GB_T_NULL; + GB.StoreVariant(var, buf); + list->count++; + + int lgi, vlgi; + + /* + * Nasty: When appending to the last element iff it has a negative + * LGI, i.e. val->lgi == -1, then we must not produce a 'lgi' of + * value 0. For a positive LGI of the last element, we just + * incremented list->count so that's no problem. + */ + if (val->lgi == -1) + lgi = abslgi(list, -1); + else + lgi = normalise_index(list, abslgi(list, val->lgi + 1)); + memcpy(&back, val, sizeof(back)); + begin_all_references(list) { + vlgi = abslgi(list, __vp->lgi); + + if (vlgi <= lgi && __vp->lgi < 0) + __vp->lgi--; + else if (vlgi > lgi && __vp->lgi >= 0) + __vp->lgi++; + __vp->lgi = normalise_index(list, __vp->lgi); + + if (__vp->ck == back.ck) { + if (shifted_to_next && __vp->idx == back.ck->last) { + __vp->ck = next; + __vp->idx = next->first; + } else if (__vp->idx > back.idx) { + __vp->idx++; + } + } + } end_all_references; +} + +static void VAL_prepend(CLIST *list, VAL *val, GB_VARIANT *var) +{ + CHUNK *ck, *prev = NULL; + int s, n, shifted_to_prev = 0; + GB_VARIANT_VALUE *buf; + VAL back; + + ck = val->ck; + /* Not (L) */ + if (ck->first) { /* 1) */ + ck->first--; + if (val->idx == ck->first + 1) { /* 1a) */ + buf = &ck->var[ck->first]; + } else { + shift: + s = ck->first + 1; + n = val->idx - s; + memmove(&ck->var[s - 1], &ck->var[s], + n * sizeof(ck->var[0])); + buf = &ck->var[s + n - 1]; + } + } else { + LIST *node = ck->list.prev; + + /* 2) */ + if (node == &list->list) + goto add_new_chunk; + + prev = get_chunk(node); + if (prev->last < CHUNK_SIZE - 1) { + prev->last++; + if (val->idx == ck->first) { /* 2a) */ + buf = &prev->var[prev->last]; + goto have_buf; + } + shift_to_prev: + shifted_to_prev = 1; + memcpy(&prev->var[prev->last], &ck->var[ck->first], + sizeof(ck->var[0])); + goto shift; + } else { /* 3) */ + add_new_chunk: + prev = CHUNK_new(); + prev->first = prev->last = CHUNK_SIZE / 2 - 1; + LIST_prepend(&ck->list, &prev->list); + if (val->idx == ck->first) { /* 3a) */ + buf = &prev->var[prev->last]; + goto have_buf; + } + goto shift_to_prev; + } + } + +have_buf: + bzero(buf, sizeof(*buf)); + buf->type = GB_T_NULL; + GB.StoreVariant(var, buf); + list->count++; + + int lgi, vlgi; + + /* + * We have a similar case here as in VAL_append() but the other way + * around: if the element is the new first one, we must check if + * val->lgi is 0, it then may stay 0. If it was -list->count, + * everything is fine because of list->count++ above. + */ + if (!val->lgi) + lgi = 0; + else + lgi = normalise_index(list, abslgi(list, val->lgi - 1)); + memcpy(&back, val, sizeof(back)); + begin_all_references(list) { + vlgi = abslgi(list, __vp->lgi); + + if (vlgi <= lgi && __vp->lgi < 0) + __vp->lgi--; + else if (vlgi >= lgi && __vp->lgi >= 0) + __vp->lgi++; + __vp->lgi = normalise_index(list, __vp->lgi); + + if (__vp->ck == back.ck) { + if (shifted_to_prev && __vp->idx == back.ck->first) { + __vp->ck = prev; + __vp->idx = prev->last; + } else if (__vp->idx < back.idx) { + __vp->idx--; + } + } + } end_all_references; +} + +static void CLIST_take(CLIST *list, VAL *val, GB_VARIANT_VALUE *buf) +{ + GB_VARIANT_VALUE *v; + CHUNK *ck = val->ck; + VAL back; + int gets_empty, is_last; + int i, src, dst, phantom; + int n, m; + size_t size; + + i = val->idx; + v = &ck->var[i]; + /* Save that value */ + memcpy(buf, v, sizeof(*buf)); + /* No need to not (O) */ + + gets_empty = (CHUNK_count(ck) == 1); + if (gets_empty) { + phantom = i; + src = dst = 0; + goto no_move; + } + + n = i - ck->first; + m = ck->last - i; + is_last = CHUNK_is_last(list, ck); + /* (A) */ + if (CHUNK_is_first(list, ck)) { + if (is_last) /* Sole */ + goto normal; + goto first; /* Algorithms match */ + } else if (is_last) { + goto last; + } +normal: + /* (L) */ + if (n <= m) { + first: /* Move block before i upwards */ + src = ck->first; + dst = src + 1; + ck->first++; + phantom = src; + } else { + last: /* Move block after i downwards */ + src = i + 1; + dst = i; + n = m; + ck->last--; + phantom = i + m; + } + /* (C) */ + size = n * sizeof(ck->var[0]); + memmove(&ck->var[dst], &ck->var[src], size); + +no_move: + /* Don't forget to remove the phantom value (overriding the actual + * data with zeros to be sure from leakage) */ + bzero(&ck->var[phantom], sizeof(ck->var[0])); + ck->var[phantom].type = GB_T_NULL; + + list->count--; + + /* Don't accidentally erase information if 'val' is a reference + * itself because then once __vp == val and __vp will be changed. */ + memcpy(&back, val, sizeof(back)); + begin_all_references(list) { +#ifdef DEBUG_ME + printf(": in: %p -> %d (%d) %d %d\n", __vp, __vp->idx, + __vp->lgi, back.idx, src < dst); +#endif + + /* The LGI stuff is pretty much independent of the ->ck and + * ->idx code and conditions. We only care about negative + * LGIs basically because non-negative ones automagically + * stay correct according to (B). */ + int vlgi = abslgi(list, __vp->lgi); + int blgi = abslgi(list, back.lgi); + + if (vlgi >= blgi) { + /* If this reference is taken and it's the last + * element of the list, then set it to zero. Kind + * of a nasty condition to occur but this is the + * only difficulty here because of (B). */ + if (vlgi == list->count && vlgi == blgi) + __vp->lgi = 0; + else if (__vp->lgi < 0) + __vp->lgi++; + } + + if (__vp->ck != back.ck) + continue; + + /* (B) */ + if (__vp->idx == back.idx) { + if (!list->count) { + __vp->ck = NULL; + continue; + } else if (gets_empty) { + goto next_chunk; + } else if (src < dst) { + /* According to ck->first++ above which + * happens only when src < dst. */ + __vp->idx++; + } + } + /* (V) */ + LIST *node; + + if (__vp->idx > __vp->ck->last) { + next_chunk: + if ((node = __vp->ck->list.next) == &list->list) + node = node->next; + __vp->ck = get_chunk(node); + __vp->idx = __vp->ck->first; + } + +#ifdef DEBUG_ME + printf(": out: %p -> %d (%d) (%p,%d,%d)\n", __vp, __vp->idx, + __vp->lgi, __vp->ck, __vp->ck ? + __vp->ck->first : 0, __vp->ck ? + __vp->ck->last : 0); +#endif + } end_all_references; + if (gets_empty) { + LIST_unlink(&ck->list); + CHUNK_destroy(ck); + } +} + +BEGIN_METHOD(List_Append, GB_VARIANT value) + + CLIST_append(THIS, ARG(value)); + +END_METHOD + +BEGIN_METHOD(List_Prepend, GB_VARIANT value) + + CLIST_prepend(THIS, ARG(value)); + +END_METHOD + +#define CHECK_CURRENT() (THIS->current.ck) +#define CHECK_RAISE_CURRENT() \ + if (!CHECK_CURRENT()) { \ + GB.Error("No current element"); \ + return; \ + } + +BEGIN_METHOD(List_Take, GB_INTEGER index) + + VAL val; + GB_VARIANT_VALUE buf; + int index; + + if (MISSING(index)) { + CHECK_RAISE_CURRENT(); + CLIST_take(THIS, &THIS->current, &buf); + } else { + index = VARG(index); + if (THIS->autonorm) + index = normalise_index(THIS, index); + CLIST_get(THIS, index, &val); + if (!val.ck) { + GB.Error(GB_ERR_BOUND); + return; + } + CLIST_take(THIS, &val, &buf); + } + GB.ReturnVariant(&buf); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &buf); + GB.ReturnRelease(); + +END_METHOD + +#define IMPLEMENT_Move(which, magic) \ +BEGIN_METHOD_VOID(List_Move ## which) \ + \ + if (!THIS->count) { \ + GB.Error("No elements"); \ + return; \ + } \ + magic; \ + \ +END_METHOD + +IMPLEMENT_Move(Next, if (!CHECK_CURRENT())CLIST_first(THIS, &THIS->current); + CHUNK_next(THIS, &THIS->current);) +IMPLEMENT_Move(Prev, if (!CHECK_CURRENT()) CLIST_last(THIS, &THIS->current); + CHUNK_prev(THIS, &THIS->current);) +IMPLEMENT_Move(First, CLIST_first(THIS, &THIS->current);) +IMPLEMENT_Move(Last, CLIST_last(THIS, &THIS->current);) + +BEGIN_METHOD(List_MoveTo, GB_INTEGER index) + + int index = VARG(index); + + if (THIS->autonorm) + index = normalise_index(THIS, index); + + CLIST_get(THIS, index, &THIS->current); + if (!THIS->current.ck) + GB.Error(GB_ERR_BOUND); + +END_METHOD + +/* + * Modify 'val' to point to the next/prev value equal to 'comp'. If nothing + * found - 'val' is allowed to cycle and point to itself again! - NULL is + * written to val->ck. + */ + +static void CLIST_find_forward(CLIST *list, VAL *val, GB_VARIANT *comp) +{ + CHUNK *last = NULL; + int cached_diff; + VAL start; + + memcpy(&start, val, sizeof(start)); + + cached_diff = 1; + do { + /* We actually enumerate but do the checking ourselves */ + CHUNK_next(list, val); + /* Note that comparing here allows 'val' to point to itself + * again. This is intentional for cyclic lists */ + if (!GB.CompVariant(VAL_value(val), &comp->value)) + return; + if (val->ck != last) + last = val->ck; + if (last == start.ck && val->idx == start.idx) + cached_diff = 0; + } while (cached_diff); + /* Invalidate */ + val->ck = NULL; /* This is most likely &list->current */ +} + +static void CLIST_find_backward(CLIST *list, VAL *val, GB_VARIANT *comp) +{ + CHUNK *last = NULL; + int cached_diff; + VAL start; + + memcpy(&start, val, sizeof(start)); + + cached_diff = 1; + do { + CHUNK_prev(list, val); + if (!GB.CompVariant(VAL_value(val), &comp->value)) + return; + if (val->ck != last) + last = val->ck; + if (last == start.ck && val->idx == start.idx) + cached_diff = 0; + } while (cached_diff); + val->ck = NULL; +} + +#define CHECK_RET_CURRENT() \ + if (!CHECK_CURRENT()) { \ + GB.ReturnNull(); \ + return; \ + } + +#define IMPLEMENT_Find(which, which2, magic) \ + \ +BEGIN_METHOD(List_Find ## which, GB_VARIANT value) \ + \ + if (!THIS->count) { \ + GB.Error("No elements"); \ + return; \ + } \ + magic; \ + CLIST_find_ ## which2 (THIS, &THIS->current, ARG(value)); \ + \ +END_METHOD + +IMPLEMENT_Find(Next, forward, + if (!CHECK_CURRENT()) CLIST_first(THIS, &THIS->current)) +IMPLEMENT_Find(Prev, backward, + if (!CHECK_CURRENT()) CLIST_last(THIS, &THIS->current)) +IMPLEMENT_Find(First, forward, CLIST_first(THIS, &THIS->current)) +IMPLEMENT_Find(Last, backward, CLIST_last(THIS, &THIS->current)) + +BEGIN_PROPERTY(List_AutoNormalize) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->autonorm); + return; + } + THIS->autonorm = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_Value) + + GB_VARIANT_VALUE *val; + + CHECK_RET_CURRENT(); + val = VAL_value(&THIS->current); + if (READ_PROPERTY) { + GB.ReturnVariant(val); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), val); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_Index) + + int index; + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->current.lgi); + return; + } + if (THIS->autonorm) + index = normalise_index(THIS, VPROP(GB_INTEGER)); + else + index = VPROP(GB_INTEGER); + CLIST_get(THIS, index, &THIS->current); + if (!THIS->current.ck) + GB.Error(GB_ERR_BOUND); + +END_PROPERTY + +BEGIN_PROPERTY(List_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + +BEGIN_PROPERTY(List_Current) + + CHECK_RAISE_CURRENT(); + GB.ReturnSelf(THIS); + +END_PROPERTY + +GB_DESC CList[] = { + GB_DECLARE("List", sizeof(CLIST)), + + GB_METHOD("_new", NULL, List_new, NULL), + GB_METHOD("_free", NULL, List_free, NULL), + GB_METHOD("_next", "v", List_next, NULL), + GB_METHOD("_get", "v", List_get, "(Index)i"), + GB_METHOD("_put", NULL, List_put, "(Value)v(Index)i"), + + GB_METHOD("Clear", NULL, List_free, NULL), + + GB_METHOD("Append", NULL, List_Append, "(Value)v"), + GB_METHOD("Prepend", NULL, List_Prepend, "(Value)v"), + GB_METHOD("Take", "v", List_Take, "[(Index)i]"), + + GB_METHOD("MoveNext", NULL, List_MoveNext, NULL), + GB_METHOD("MovePrev", NULL, List_MovePrev, NULL), + GB_METHOD("MovePrevious", NULL, List_MovePrev, NULL), + GB_METHOD("MoveFirst", NULL, List_MoveFirst, NULL), + GB_METHOD("MoveLast", NULL, List_MoveLast, NULL), + GB_METHOD("MoveTo", NULL, List_MoveTo, "(Index)i"), + + GB_METHOD("FindNext", NULL, List_FindNext, "(Value)v"), + GB_METHOD("FindPrev", NULL, List_FindPrev, "(Value)v"), + GB_METHOD("FindPrevious", NULL, List_FindPrev, "(Value)v"), + GB_METHOD("FindFirst", NULL, List_FindFirst, "(Value)v"), + GB_METHOD("FindLast", NULL, List_FindLast, "(Value)v"), + + GB_PROPERTY("AutoNormalize", "b", List_AutoNormalize), + GB_PROPERTY("Current", ".List.Item", List_Current), + GB_PROPERTY("Value", "v", ListItem_Value), + GB_PROPERTY("Index", "i", ListItem_Index), + GB_PROPERTY_READ("Count", "i", List_Count), + GB_PROPERTY_SELF("Backwards", ".List.Backwards"), + + GB_END_DECLARE +}; + +GB_DESC CListBackwards[] = { + GB_DECLARE_VIRTUAL(".List.Backwards"), + + GB_METHOD("_next", "v", ListBackwards_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_METHOD(ListItem_Append, GB_VARIANT value) + + VAL_append(THIS, &THIS->current, ARG(value)); + +END_METHOD + +BEGIN_METHOD(ListItem_Prepend, GB_VARIANT value) + + VAL_prepend(THIS, &THIS->current, ARG(value)); + +END_METHOD + +BEGIN_PROPERTY(ListItem_IsFirst) + + GB.ReturnBoolean(VAL_is_first(THIS, &THIS->current)); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_IsLast) + + GB.ReturnBoolean(VAL_is_last(THIS, &THIS->current)); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_IsValid) + + GB.ReturnBoolean(!!CHECK_CURRENT()); + +END_PROPERTY + +GB_DESC CListItem[] = { + GB_DECLARE_VIRTUAL(".List.Item"), + + GB_METHOD("Append", NULL, ListItem_Append, "(Value)v"), + GB_METHOD("Prepend", NULL, ListItem_Prepend, "(Value)v"), + + GB_PROPERTY_READ("IsFirst", "b", ListItem_IsFirst), + GB_PROPERTY_READ("IsLast", "b", ListItem_IsLast), + GB_PROPERTY_READ("IsValid", "b", ListItem_IsValid), + GB_PROPERTY_READ("Index", "i", ListItem_Index), + GB_PROPERTY("Value", "v", ListItem_Value), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_list.h b/main/lib/data/c_list.h new file mode 100644 index 00000000..7588695e --- /dev/null +++ b/main/lib/data/c_list.h @@ -0,0 +1,36 @@ +/* + * c_list.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_LIST_H +#define __C_LIST_H + +#include "gambas.h" +#include "list.h" + +extern GB_INTERFACE GB; + +#ifndef __C_LIST_C +extern GB_DESC CList[]; +extern GB_DESC CListBackwards[]; +extern GB_DESC CListItem[]; +#endif + +#endif /* !__C_LIST_H */ diff --git a/main/lib/data/c_trie.c b/main/lib/data/c_trie.c new file mode 100644 index 00000000..34d6e128 --- /dev/null +++ b/main/lib/data/c_trie.c @@ -0,0 +1,590 @@ +/* + * c_trie.c - (Patricia) Trie / Prefix tree + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_TRIE_C + +#include +#include +#include + +#include "gambas.h" +#include "c_trie.h" + +#include "trie.h" + +typedef struct { + GB_BASE ob; + struct trie *root; + char *key; + size_t count; + uint64_t time; +} CTRIE; + +#define ERR_OOM "Out of memory" + +#define THIS ((CTRIE *) _object) + +#define RESET_TIME() (THIS->time = 0) +#define UPDATE_TIME() (THIS->time++) + +/**G + * Create a new, empty Trie. + */ +BEGIN_METHOD_VOID(Trie_new) + + THIS->root = new_trie(); + THIS->key = NULL; + THIS->count = 0; + RESET_TIME(); + +END_METHOD + +static void value_dtor(void *val) +{ + GB.StoreVariant(NULL, (GB_VARIANT_VALUE *) val); + GB.Free(&val); +} + +BEGIN_METHOD_VOID(Trie_free) + + destroy_trie(THIS->root, value_dtor); + GB.FreeString(&THIS->key); + UPDATE_TIME(); + +END_METHOD + +/**G + * Return the value associated with a key. If the key was not found, return + * Null. + */ +BEGIN_METHOD(Trie_get, GB_STRING key) + + GB_VARIANT_VALUE *val; + + val = trie_value(THIS->root, STRING(key), LENGTH(key)); + if (!val) + GB.ReturnNull(); + else + GB.ReturnVariant(val); + +END_METHOD + +/**G + * Associate a value with a given key. If the value is Null, the key is + * removed. + */ +BEGIN_METHOD(Trie_put, GB_VARIANT value; GB_STRING key) + + GB_VARIANT_VALUE *val; + void *oldval; + + if (VARG(value).type == GB_T_NULL) { + trie_remove(THIS->root, STRING(key), LENGTH(key), + value_dtor); + UPDATE_TIME(); + return; + } + GB.Alloc((void **) &val, sizeof(*val)); + val->type = GB_T_NULL; + GB.StoreVariant(ARG(value), val); + oldval = trie_insert(THIS->root, STRING(key), LENGTH(key), val); + if (oldval) + value_dtor(oldval); + UPDATE_TIME(); + +END_METHOD + +/**G + * Remove the named element. This is equivalent to _put'ing Null into its + * key. + */ +BEGIN_METHOD(Trie_Remove, GB_STRING key) + + trie_remove(THIS->root, STRING(key), LENGTH(key), value_dtor); + UPDATE_TIME(); + +END_METHOD + +struct stack { + struct trie *node; + int idx, visited : 1; + struct stack *prev; +}; + +struct enum_state { + struct stack *top; + int start; +}; + +/**G + * Enumerates all values in the Trie in lexicographic key order. The Key + * property is set for each enumerated value. + * + * If you picture the Trie as a tree (and have all child nodes ordered + * lexicographically), the lexicographic traversal of the trie is the + * pre-order traversal (of nodes with a value). + */ +BEGIN_METHOD_VOID(Trie_next) + + struct enum_state *state = GB.GetEnum(); + struct stack *top; + struct trie *node = NULL; /* silence compiler with "goto visit" */ + + if (!state->start) { + state->start = 1; + GB.FreeString(&THIS->key); + THIS->key = GB.NewString("", 0); + GB.Alloc((void **) &state->top, sizeof(*state->top)); + state->top->node = THIS->root; + state->top->idx = 0; + state->top->visited = 0; + state->top->prev = NULL; + top = state->top; + goto visit; + } + + top = state->top; +next: + if (top->idx >= top->node->nchildren) { + struct stack *prev = top->prev; + size_t len = GB.StringLength(THIS->key) - top->node->len; + + THIS->key = GB.ExtendString(THIS->key, len); + GB.Free((void **) &top); + top = prev; + if (!top) { + GB.StopEnum(); + return; + } + top->idx++; + goto next; + } + + node = top->node->children[top->idx]; +visit: + if (!top->visited) { + /* AddString() will take the root node's len == 0 as a + * request to use strlen(). Make that a special case. */ + if (top->node->len) { + THIS->key = GB.AddString(THIS->key, + top->node->key, + top->node->len); + } + } else { + struct stack *old = top; + + if (old->node->nchildren) { + GB.Alloc((void **) &top, sizeof(*top)); + top->node = node; + top->idx = 0; + top->visited = 0; + top->prev = old; + goto visit; + } + } + + top->visited = 1; + state->top = top; + if (!top->node->value) + goto next; + GB.ReturnVariant(top->node->value); + +END_METHOD + +/**G + * Remove all elements from the Trie. + */ +BEGIN_METHOD_VOID(Trie_Clear) + + clear_trie(THIS->root, value_dtor); + UPDATE_TIME(); + +END_METHOD + +/**G + * Return whether the named key exists, i.e. if it has a value. + * + * This does not return if the given string is *part* of a path to another + * node, it will only give you exact matches. To test if a given prefix + * exists, use [../GetPrefix]. + */ +BEGIN_METHOD(Trie_Exist, GB_STRING key) + + struct trie *node; + + node = trie_find(THIS->root, STRING(key), LENGTH(key)); + GB.ReturnBoolean(!!node); + +END_METHOD + +typedef struct CPREFIX { + GB_BASE ob; + CTRIE *trie; + struct trie_prefix p; + char *key; + char *prefix; + uint64_t time; +} CPREFIX; + +/**G + * Return a TriePrefix object to search part of a trie. + * + * If the prefix is not found, Null is returned. + */ +BEGIN_METHOD(Trie_GetPrefix, GB_STRING prefix) + + static GB_CLASS TriePrefix; + struct trie_prefix p; + CPREFIX *obj; + + trie_reset_prefix(&p); + trie_constrain2(THIS->root, &p, STRING(prefix), LENGTH(prefix)); + if (!p.node) { + GB.ReturnNull(); + return; + } + + if (!TriePrefix) + TriePrefix = GB.FindClass("TriePrefix"); + obj = GB.New(TriePrefix, NULL, NULL); + obj->trie = THIS; + GB.Ref(THIS); + obj->p = p; + obj->key = NULL; + obj->prefix = GB.NewString(STRING(prefix), LENGTH(prefix)); + obj->time = THIS->time; + GB.ReturnObject(obj); + +END_METHOD + +/**G + * Return the completion of the given prefix, that is the longest + * unambiguous continuation of the prefix, like when you hit + * in the console, your shell might complete a command or file name + * for you. + * + * If the prefix is not found, Null is returned. + */ +BEGIN_METHOD(Trie_Complete, GB_STRING prefix) + + struct trie_prefix p; + char *s; + + trie_reset_prefix(&p); + trie_constrain2(THIS->root, &p, STRING(prefix), LENGTH(prefix)); + if (!p.node) { + GB.ReturnNull(); + return; + } + + s = GB.NewString(STRING(prefix), LENGTH(prefix)); + /* Again, we need to special-case p.node->len - p.i == 0. */ + if (p.node->len - p.i) + s = GB.AddString(s, p.node->key + p.i, p.node->len - p.i); + GB.ReturnString(s); + GB.ReturnBorrow(); + GB.FreeString(&s); + GB.ReturnRelease(); + +END_METHOD + +/**G + * Return the number of keys in the Trie. + */ +BEGIN_PROPERTY(Trie_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + +/**G + * Return the key of the last enumerated element. + */ +BEGIN_PROPERTY(Trie_Key) + + GB.ReturnString(THIS->key); + +END_PROPERTY + +GB_DESC CTrie[] = { + /**G + * This class implements a Patricia Trie. You can learn about its + * semantics from [Wikipedia] (http://en.wikipedia.org/wiki/Radix_tree) + */ + GB_DECLARE("Trie", sizeof(CTRIE)), + + GB_METHOD("_new", NULL, Trie_new, NULL), + GB_METHOD("_free", NULL, Trie_free, NULL), + + GB_METHOD("_get", "v", Trie_get, "(Key)s"), + GB_METHOD("_put", NULL, Trie_put, "(Value)v(Key)s"), + GB_METHOD("_next", "v", Trie_next, NULL), + + /**G Trie Add + * Add an element to the trie. A synonym for _put. + */ + GB_METHOD("Add", NULL, Trie_put, "(Value)v(Key)s"), + GB_METHOD("Remove", NULL, Trie_Remove, "(Key)s"), + GB_METHOD("Clear", NULL, Trie_Clear, NULL), + GB_METHOD("Exist", "b", Trie_Exist, "(Key)s"), + GB_METHOD("GetPrefix", "TriePrefix", Trie_GetPrefix, "(Prefix)s"), + GB_METHOD("Complete", "s", Trie_Complete, "(Prefix)s"), + + GB_PROPERTY_READ("Count", "i", Trie_Count), + GB_PROPERTY_READ("Key", "s", Trie_Key), + + GB_END_DECLARE +}; + +#undef THIS +#define THIS ((CPREFIX *) _object) +#define TRIE (THIS->trie) +#define PREFIX (&THIS->p) + +/* A TriePrefix is a valid object if it's non-NULL, the prefix string was + * found and the trie was not modified since its creation. */ +static int check_prefix(CPREFIX *p) +{ + //printf("p=%p, state=%d, time=%lu (%lu)\n", p, p->p.state, p->time, p->trie->time); + return !p || p->p.state == TRIE_UNSET || p->time != p->trie->time; +} + +BEGIN_METHOD_VOID(TriePrefix_free) + + GB.Unref((void **) &TRIE); + GB.FreeString(&THIS->key); + GB.FreeString(&THIS->prefix); + +END_METHOD + +/**G + * This is the same as hTrie[hPrefix.Prefix & RelKey] where hTrie is the + * Trie from which hPrefix was created, except that it is faster. + */ +BEGIN_METHOD(TriePrefix_get, GB_STRING rel) + + GB_VARIANT_VALUE *val; + + val = trie_value2(TRIE->root, PREFIX, STRING(rel), LENGTH(rel)); + if (!val) + GB.ReturnNull(); + else + GB.ReturnVariant(val); + +END_METHOD + +/**G + * Iterate through all keys in the prefix range, in lexicographic order. + * + * See also + * [../../trie/_next] + */ +BEGIN_METHOD_VOID(TriePrefix_next) + + struct enum_state *state = GB.GetEnum(); + struct stack *top; + struct trie *node = NULL; /* silence compiler */ + + if (!state->start) { + state->start = 1; + GB.FreeString(&THIS->key); + THIS->key = GB.NewString("", 0); + GB.Alloc((void **) &state->top, sizeof(*state->top)); + state->top->node = PREFIX->node; + state->top->idx = 0; + state->top->visited = 0; + state->top->prev = NULL; + top = state->top; + goto visit; + } + + top = state->top; +next: + if (top->idx >= top->node->nchildren) { + struct stack *prev = top->prev; + size_t len = GB.StringLength(THIS->key) - top->node->len; + + THIS->key = GB.ExtendString(THIS->key, len); + GB.Free((void **) &top); + top = prev; + if (!top) { + GB.StopEnum(); + return; + } + top->idx++; + goto next; + } + + node = top->node->children[top->idx]; +visit: + if (!top->visited) { + int i = 0; + + /* Take the offset in the prefix' root into account */ + if (!top->prev) + i = PREFIX->i; + /* If top->node->len - i == 0, we want to add nothing, but + * GB.AddString() will take that as a request to use + * strlen() itself. So special-case that. */ + if (top->node->len - i) { + THIS->key = GB.AddString(THIS->key, + top->node->key + i, + top->node->len - i); + } + } else { + struct stack *old = top; + + if (old->node->nchildren) { + GB.Alloc((void **) &top, sizeof(*top)); + top->node = node; + top->idx = 0; + top->visited = 0; + top->prev = old; + goto visit; + } + } + + top->visited = 1; + state->top = top; + if (!top->node->value) + goto next; + GB.ReturnVariant(top->node->value); + +END_METHOD + +/**G + * Return if the given key exists relative to the prefix. This returns the + * same as hTrie.Exist(hPrefix.Prefix & RelKey). + * + * See also + * [../_get] + */ +BEGIN_METHOD(TriePrefix_Exist, GB_STRING rel) + + struct trie *node; + + node = trie_find2(TRIE->root, PREFIX, STRING(rel), LENGTH(rel)); + GB.ReturnBoolean(!!node); + +END_METHOD + +/**G + * Add bytes to the prefix. If the extended prefix does not exist within the + * Trie, an error is raised. + */ +BEGIN_METHOD(TriePrefix_Add, GB_STRING rel) + + char *s = THIS->prefix; + struct trie_prefix new = *PREFIX; + + trie_constrain2(TRIE->root, &new, STRING(rel), LENGTH(rel)); + if (!new.node) { + GB.Error("Prefix does not exist"); + return; + } + *PREFIX = new; + THIS->prefix = GB.AddString(s, STRING(rel), LENGTH(rel)); + +END_METHOD + +/**G + * Remove bytes from the end of the prefix. There is no way this function + * can fail since it removes Min(Len(hPrefix.Prefix), Length) bytes and + * if the TriePrefix was valid, the weaker prefix will also be valid. + */ +BEGIN_METHOD(TriePrefix_Remove, GB_INTEGER len) + + char *s = THIS->prefix; + size_t len = VARGOPT(len, 1), l; + + if (len < 0) + GB.Error("Invalid length"); + if (len <= 0) + return; + + l = GB.StringLength(s); + if (len > l) + len = l; + + /* + * Since the struct trie has no uplinks to parent nodes, we cannot + * go backwards beyond node boundaries. Thus, we do it the less + * elegant way: remove characters from the Prefix property and + * recreate the prefix from scratch. + * + * As noted in the help text above, this will always work. + */ + l -= len; + THIS->prefix = GB.ExtendString(s, l); + trie_reset_prefix(PREFIX); + trie_constrain2(TRIE->root, PREFIX, THIS->prefix, l); + +END_METHOD + +/**G + * Return the **relative key** of the last enumerated object. + * + * See also + * [../_get] + */ +BEGIN_PROPERTY(TriePrefix_Key) + + GB.ReturnString(THIS->key); + +END_PROPERTY + +/**G + * Return the prefix string of this object. + */ +BEGIN_PROPERTY(TriePrefix_Prefix) + + GB.ReturnString(THIS->prefix); + +END_PROPERTY + +GB_DESC CTriePrefix[] = { + /**G + * This class provides a read-only view of part of a Trie. It lets + * you examine keys with a common prefix. Searches begin in the + * middle of the Trie and are thus faster. + * + * TriePrefix objects are invalidated when you change the Trie, so + * be careful if you store them persistently. + */ + GB_DECLARE("TriePrefix", sizeof(CPREFIX)), + GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(check_prefix), + + GB_METHOD("_free", NULL, TriePrefix_free, NULL), + + GB_METHOD("_get", "v", TriePrefix_get, "(RelKey)s"), + GB_METHOD("_next", "v", TriePrefix_next, NULL), + + GB_METHOD("Exist", "b", TriePrefix_Exist, "(RelKey)s"), + GB_METHOD("Add", NULL, TriePrefix_Add, "(RelKey)s"), + GB_METHOD("Remove", NULL, TriePrefix_Remove, "[(Length)i]"), + + GB_PROPERTY_READ("Key", "s", TriePrefix_Key), + GB_PROPERTY_READ("Prefix", "s", TriePrefix_Prefix), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_trie.h b/main/lib/data/c_trie.h new file mode 100644 index 00000000..b29010f0 --- /dev/null +++ b/main/lib/data/c_trie.h @@ -0,0 +1,33 @@ +/* + * c_trie.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_TRIE_H +#define __C_TRIE_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_TRIE_C +extern GB_DESC CTrie[], CTriePrefix[]; +#endif + +#endif /* __C_TRIE_H */ diff --git a/main/lib/data/gb.data.component b/main/lib/data/gb.data.component new file mode 100644 index 00000000..33a5dc85 --- /dev/null +++ b/main/lib/data/gb.data.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Stable diff --git a/main/lib/data/gb.data/.component b/main/lib/data/gb.data/.component new file mode 100644 index 00000000..07b768ee --- /dev/null +++ b/main/lib/data/gb.data/.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.data +Version=3.10.90 diff --git a/main/lib/data/gb.data/.directory b/main/lib/data/gb.data/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/lib/data/gb.data/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/lib/data/gb.data/.icon.png b/main/lib/data/gb.data/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __LIST_H +#define __LIST_H + +#include "gambas.h" + +typedef struct list { + struct list *prev, *next; +} LIST; + +#define INIT_LIST(name) {&name, &name} +#define DECLARE_LIST(name) LIST name = INIT_LIST(name) + +#define LIST_data(list, type, member) \ + ((type *) ((char *) (list) - offsetof(type, member))) + +/* All but the entry node */ + +#define list_for_each(_node, _list) \ + for (_node = (_list)->next; _node != (_list); _node = _node->next) + +#define list_for_each_prev(_node, _list) \ + for (_node = (_list)->prev; _node != (_list); _node = _node->prev) + +/* All, including the entry node (at the beginning of the loop) */ + +#define list_for_each_first(_node, _list, _c) \ + for (_node = (_list), _c = 1; _node != (_list) || _c--; \ + _node = _node->next) + +#define list_for_each_prev_first(_node, _list, _c) \ + for (_node = (_list), _c = 1; _node != (_list) || _c--; \ + _node = _node->prev) + +/* All but entry node, may modify the list */ + +#define list_for_each_safe(_node, _list, _next) \ + for (_node = (_list)->next, _next = _node->next; _node != (_list);\ + _node = _next, _next = _node->next) + +static inline void LIST_init(LIST *list) +{ + list->prev = list->next = list; +} + +static inline int LIST_is_empty(LIST *list) +{ + return list->next == list; +} + +static inline void LIST_prepend(LIST *list, LIST *new) +{ + LIST *new_end = new->prev; + + list->prev->next = new; + new->prev = list->prev; + new_end->next = list; + list->prev = new_end; +} + +static inline void LIST_append(LIST *list, LIST *new) +{ + LIST *new_end = new->prev; + + new_end->next = list->next; + list->next->prev = new_end; + new->prev = list; + list->next = new; +} + +static inline LIST *LIST_unlink(LIST *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; + node->prev = node->next = node; + return node; +} + +#endif /* !__LIST_H */ diff --git a/main/lib/data/lookup3.h b/main/lib/data/lookup3.h new file mode 100644 index 00000000..861da47d --- /dev/null +++ b/main/lib/data/lookup3.h @@ -0,0 +1,142 @@ +/* + * lookup3.h + * + * These routines were originally written by Bob Jenkins. The original file + * http://burtleburtle.net/bob/c/lookup3.c (10th Aug 2013) contained these + * credits: + * + * --- + * lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * These are functions for producing 32-bit hashes for hash table lookup. + * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() + * are externally useful functions. Routines to test the hash are included + * if SELF_TEST is defined. You can use this free for any purpose. It's in + * the public domain. It has no warranty. + * --- + * + * This file is a (rigorously) modified version for Gambas. + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __LOOKUP3_H +#define __LOOKUP3_H + +#include + +#define lookup3_size(n) ((uint32_t) 1 << (n)) +#define lookup3_mask(n) (lookup3_size(n) - 1) +#define lookup3_rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) + +/* lookup3_mix -- mix 3 32-bit values reversibly. */ +#define lookup3_mix(a, b, c) \ +{ \ + a -= c; a ^= lookup3_rot(c, 4); c += b; \ + b -= a; b ^= lookup3_rot(a, 6); a += c; \ + c -= b; c ^= lookup3_rot(b, 8); b += a; \ + a -= c; a ^= lookup3_rot(c, 16); c += b; \ + b -= a; b ^= lookup3_rot(a, 19); a += c; \ + c -= b; c ^= lookup3_rot(b, 4); b += a; \ +} + +/* lookup3_final -- final mixing of 3 32-bit values (a,b,c) into c */ +#define lookup3_final(a,b,c) \ +{ \ + c ^= b; c -= lookup3_rot(b, 14); \ + a ^= c; a -= lookup3_rot(c, 11); \ + b ^= a; b -= lookup3_rot(a, 25); \ + c ^= b; c -= lookup3_rot(b, 16); \ + a ^= c; a -= lookup3_rot(c, 4); \ + b ^= a; b -= lookup3_rot(a, 14); \ + c ^= b; c -= lookup3_rot(b, 24); \ +} + +/* + * __lookup3_64() -- Hashes an array of 32 bit values into a 64 bit value + * @k : the key, an array of uint32_t values + * @length: the length of the key, in uint32_ts + * @pc : seed #1 + * @pb : seed #2 + * + * If 'pb' is zero then the result will be valid a 32 bit value. + */ +static inline uint64_t __lookup3_64(const uint32_t *k, size_t length, + uint32_t pc, uint32_t pb) +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + pc; + c += pb; + + /* Handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + lookup3_mix(a, b, c); + length -= 3; + k += 3; + } + + /* Handle the last 3 uint32_t's */ + switch(length) { /* All the case statements fall through */ + case 3: + c += k[2]; + case 2: + b += k[1]; + case 1: + a += k[0]; + lookup3_final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + /* Report the result. Mirror the 32 bit version if 'pb' was zero. */ + return c + pb ? ((uint64_t) b) << 32 : 0; +} + +/* + * Arbitrary string hash wrappers. They pad the string with '\0' to a 32 + * bits boundary. So two (key,length) pairs ("a",1) and ("a\0",2) would + * give identical hashes. Beware! + */ + +static inline uint32_t lookup3_32(const char *k, size_t length, + uint32_t initval) +{ + size_t len = (length + 31) / 32; + uint32_t key[len]; + + bzero(key, len * sizeof(key[0])); + memcpy(key, k, length); + return __lookup3_64(key, len, initval, 0); +} + +static inline uint64_t lookup3_64(const char *k, size_t length, + uint32_t pc, uint32_t pb) +{ + size_t len = (length + 31) / 32; + uint32_t key[len]; + + bzero(key, len * sizeof(key[0])); + memcpy(key, k, length); + return __lookup3_64(key, len, pc, pb); +} + +#endif /* __LOOKUP3_H */ diff --git a/main/lib/data/main.c b/main/lib/data/main.c new file mode 100644 index 00000000..c09da0bf --- /dev/null +++ b/main/lib/data/main.c @@ -0,0 +1,81 @@ +/* + * main.c - gb.data glue + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __MAIN_C + +#include "c_list.h" +#include "c_deque.h" +#include "c_circular.h" +#include "c_avltree.h" +#include "c_trie.h" +#include "c_graph.h" +#include "c_graphmatrix.h" +#include "c_heap.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + CList, + CListBackwards, + CListItem, + + CDeque, + CStack, + CQueue, + CPrioQueue, + + CCircular, + + CAvlTree, + + CTrie, + CTriePrefix, + + CGraph, + CGraphVertices, + CGraphEdges, + CGraphInEdges, + CGraphOutEdges, + CGraphAdjacent, + CGraphVertex, + CGraphEdge, + + CGraphMatrix, + CMatrixVertices, + CMatrixEdges, + CMatrixVertex, + CMatrixEdge, + + CHeap, + + NULL +}; + +int EXPORT GB_INIT() +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/data/main.h b/main/lib/data/main.h new file mode 100644 index 00000000..1b8d6481 --- /dev/null +++ b/main/lib/data/main.h @@ -0,0 +1,32 @@ +/* + * main.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* !__MAIN_H */ diff --git a/main/lib/data/string_compare.h b/main/lib/data/string_compare.h new file mode 100644 index 00000000..4323fd0b --- /dev/null +++ b/main/lib/data/string_compare.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + gb_common_string_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gambas.h" + +int STRING_compare(const char *str1, int len1, const char *str2, int len2) +{ + uint i; + int len = len1 < len2 ? len1 : len2; + int diff; + unsigned char c1, c2; + + for (i = 0; i < len; i++) + { + c1 = str1[i]; + c2 = str2[i]; + if (c1 > c2) return 1; + if (c1 < c2) return -1; + } + + diff = len1 - len2; + return diff < 0 ? (-1) : diff > 0 ? 1 : 0; +} diff --git a/main/lib/data/trie.c b/main/lib/data/trie.c new file mode 100644 index 00000000..5c44cc0c --- /dev/null +++ b/main/lib/data/trie.c @@ -0,0 +1,756 @@ +/* + * trie.c + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include + +#include "trie.h" + +#include "c_trie.h" + +/** + * __key_index() - Return a unique number for the character + * @c: char + */ +static inline int __key_index(char c) +{ + return (int) (unsigned char) c; +} + +static inline int popcnt(uint64_t word) +{ + int n; + + for (n = 0; word; n++) + word &= word - 1; + return n; +} + +#define MASK_SIZE \ + (__CHAR_BIT__ * sizeof(((struct trie *) 0)->mask[0])) +#define INDEX(i) (i / MASK_SIZE) +#define OFFSET(i) (i % MASK_SIZE) + +/** + * __key_to_array_index() - Return array index over a node's ->children + * corresponding to a key character + * @node: struct trie + * @c: the character + */ +static inline int __key_to_array_index(const struct trie *node, char c) +{ + int i = __key_index(c), j, n; + + for (j = n = 0; i >= MASK_SIZE; j++, i -= MASK_SIZE) + n += popcnt(node->mask[j]); + n += popcnt(node->mask[j] & ((1ULL << i) - 1)); + return n; +} + +static inline void __set_bit(uint64_t mask[4], int i) +{ + mask[INDEX(i)] |= 1ULL << (OFFSET(i)); +} + +static inline void set_bit(struct trie *node, int i) +{ + __set_bit(node->mask, i); +} + +static inline void clear_bit(struct trie *node, int i) +{ + node->mask[INDEX(i)] &= ~(1ULL << (OFFSET(i))); +} + +static inline int test_bit(const struct trie *node, int i) +{ + return !!(node->mask[INDEX(i)] & (1ULL << (OFFSET(i)))); +} + +/** + * get_continuation() - Return the node continuing the key of another node + * with a given character + * @node: struct trie + * @c: the character + * + * If you have a trie like + * + * 0 + * | + * te + * | + * +--+--+ + * | | + * st rm + * + * and search for the key "term", this function comes in handy at the node + * "te". You will call get_continuation(te_node, 'r') which yields rm_node. + * + * If no such continuation exists, NULL is returned. + */ +static inline struct trie *get_continuation(const struct trie *node, char c) +{ + int i = __key_index(c); + int j = __key_to_array_index(node, c); + + if (!test_bit(node, i)) + return NULL; + return node->children[j]; +} + +struct __trie_find_res { + struct trie *node, *parent; + int i, j; +}; + +/** + * __trie_find() - Get the node containing a key + * @trie: struct trie + * @key: the key + * @len: length + * + * This function returns the node in which `key' ends which may NOT be the + * node which has exactly the key `key'. It returns NULL, if no such node + * was found. + */ +static struct __trie_find_res __trie_find(const struct trie *trie, + const char *key, size_t len) +{ + struct trie *node = (struct trie *) trie, *parent = NULL; + int i = 0, j = 0; + + while (node) { + i = 0; + while (i < node->len && j < len && node->key[i] == key[j]) { + i++; + j++; + } + /* + * Four cases: + * 1) the `key' and `node' were entirely consumed: perfect + * match. Get out. + * 2) only `key' consumed: we're done as the key lies + * within the node. + * 3) only `node' was consumed: recurse to its children. + * 4) if neither of the above, node and key deverged here, + * so break the loop since this is as close as we can + * get. + */ + if (j == len) { + break; + } else if (i == node->len) { + parent = node; + node = get_continuation(node, key[j]); + } else { + break; + } + } + return (struct __trie_find_res) {node, parent, i, j}; +} + +/** + * __is_exact() - Return whether a found node matches a key exactly + * @res: struct __trie_find_res + * @len: length + */ +static inline int __is_exact(struct __trie_find_res *res, size_t len) +{ + return res->i == res->node->len && res->j == len; +} + +/** + * __trie_find_exact() - Get the node with ends in a key + * @trie: struct trie + * @key: the key + * @len: length + * + * Unlike __trie_find(), this function returns the node which has the key + * `key' or NULL if none. Note, however, that this is not a guarantee that + * the node contains a non-NULL ->value. + */ +static struct trie *__trie_find_exact(const struct trie *trie, + const char *key, size_t len) +{ + struct __trie_find_res res = __trie_find(trie, key, len); + + return (res.node && __is_exact(&res, len)) ? res.node : NULL; +} + +/** + * __new_node() - Allocate a trie node + * @key: part of the key + * @len: length of `key' + * @value: payload + * + * If `len' equals zero, the length is obtained from `key' via strlen(). For + * the single case where a zero length is correct, this doesn't do much + * harm - as opposed to the strangeness of comparing a size_t to -1 or some + * other clearly invalid value. + */ +static struct trie *new_node(const char *key, size_t len, void *value) +{ + struct trie *trie; + + /*if (!len) + len = strlen(key);*/ + + GB.Alloc((void **) &trie, sizeof(*trie) + len); + memset(trie->mask, 0, sizeof(trie->mask)); + trie->children = NULL; + trie->nchildren = 0; + trie->value = value; + trie->len = len; + memcpy(trie->key, key, len); + return trie; +} + +/** + * new_trie() - Allocate a new trie + */ +struct trie *new_trie(void) +{ + return new_node("", 0, NULL); +} + +/** + * destroy_node() - Deallocate a single node + * @node: struct trie + * @dtor: value destructor + */ +static void destroy_node(struct trie *node, void (*dtor)(void *)) +{ + GB.Free((void **) &node->children); + if (node->value && dtor) + dtor(node->value); + GB.Free((void **) &node); +} + +/** + * destroy_trie() - Deallocate an entire trie + * @trie: struct trie + * @dtor: value destructor + */ +void destroy_trie(struct trie *trie, void (*dtor)(void *)) +{ + int i; + + for (i = 0; i < trie->nchildren; i++) + destroy_trie(trie->children[i], dtor); + destroy_node(trie, dtor); +} + +/** + * clear_trie() - Remove all but the root + * @trie: struct trie + * @dtor: value destructor + */ +void clear_trie(struct trie *trie, void (*dtor)(void *)) +{ + int i; + + for (i = 0; i < trie->nchildren; i++) + destroy_trie(trie->children[i], dtor); + memset(trie->mask, 0, sizeof(trie->mask)); + GB.Free((void **) &trie->children); + trie->children = NULL; + trie->nchildren = 0; + if (trie->value) + dtor(trie->value); + trie->value = NULL; +} + +/** + * __sort_two_children() - Sort (at most) two children into a children array + * @array: array with enough space for the element(s) + * @mask: buffer + * @child1: struct trie + * @child2: struct trie, may be NULL + * + * This function writes the apropriate mask for the array into the `mask' + * argument. + * + * The `child2' can be NULL in which case it is ignored and not assigned to + * the array. + */ +static inline void __sort_two_children(const struct trie *array[2], + uint64_t mask[4], + const struct trie *child1, + const struct trie *child2) +{ + int i, j; + + i = __key_index(*child1->key); + j = child2 ? __key_index(*child2->key) : 0; /* just to initialise */ + if (!child2 || i < j) { + array[0] = child1; + if (child2) + array[1] = child2; + } else { + array[0] = child2; + array[1] = child1; + } + __set_bit(mask, i); + if (child2) + __set_bit(mask, j); +} + +/** + * __trie_insert_split() - Split a node to insert a new key + * @res: struct __trie_find_res + * @key: the key + * @len: length + * @value: the value + */ +static void __trie_insert_split(struct __trie_find_res *res, const char *key, + size_t len, void *value) +{ + struct trie *node = res->node, *bottom, *branch = NULL; + struct trie **topchildren; + /* + * If key[res->j] == '\0', the key lies within `node' and will be in + * the "top" node already, so we save the `branch'. + */ + int have_branch = !!key[res->j]; + + /* + * - `bottom' will contain the bottom part of the split node; + * - `branch' will be the new node associated with the wanted key + * (if it is not within the `node') + * - `topchildren' is the new ->children array of the "top" half of + * the split node - which will consist of `bottom' and `branch'. + */ + bottom = new_node(&node->key[res->i], node->len - res->i, + node->value); + + if (have_branch) { + branch = new_node(&key[res->j], len - res->j, value); + GB.Alloc((void **) &topchildren, 2 * sizeof(*topchildren)); + } else { + GB.Alloc((void **) &topchildren, sizeof(*topchildren)); + } + /* While doing the Alloc() stuff, we can already Realloc() the + * "top" node here... */ + GB.Realloc((void **) &node, sizeof(*node) + res->j); + /* Link the split node into the trie again */ + int i = __key_to_array_index(res->parent, *node->key); + + res->parent->children[i] = node; + + /* + * new_node() set `bottom' already up quite well. However, we need + * to tweak: ->mask, ->children and ->nchildren. + * + * After we have copied them from the "top" node, we can set the + * members there correctly to: ->mask, ->children, ->nchildren, + * ->value and ->len need tweaking while ->key was cut properly by + * Realloc(). + */ + memcpy(bottom->mask, node->mask, sizeof(bottom->mask)); + bottom->children = node->children; + bottom->nchildren = node->nchildren; + + /* + * __sort_two_children() is aware that `branch' may be NULL. + */ + memset(node->mask, 0, sizeof(node->mask)); + __sort_two_children((const struct trie **) topchildren, + node->mask, bottom, branch); + node->children = topchildren; + node->nchildren = have_branch ? 2 : 1; + node->value = NULL; + node->len = res->i; + + /* + * The new `branch' has everything right if it exists as it has no + * children which need extra care. If it wasn't created, we need to + * assign the `value' to the "top" node now. + */ + if (!have_branch) + node->value = value; +} + +/** + * __trie_insert_child() - Extend an already existing key to a new one + * @res: struct __trie_find_res + * @key: the key + * @len: length + * @value: the value + */ +static void __trie_insert_child(struct __trie_find_res *res, const char *key, + size_t len, void *value) +{ + struct trie *node = res->parent, *child, **children; + int i, j, k; + + /* + * Here, we CAN'T have the case that `node' has no children and no + * value so that we could concatenate the keys. This just cannot + * happen while adding nodes: then we would have added a leaf node + * without a value which doesn't happen. The case above can only + * occur when children are *removed* from an interior parent without + * a value and is thus handled in the trie_remove() function. + */ + + child = new_node(&key[res->j], len - res->j, value); + i = __key_index(*child->key); + j = __key_to_array_index(node, *child->key); + children = node->children; + GB.Realloc((void **) &children, (node->nchildren + 1) * + sizeof(*children)); + + for (k = node->nchildren; k > j; k--) + children[k] = children[k - 1]; + children[k] = child; + node->children = children; + node->nchildren++; + set_bit(node, i); +} + +/** + * trie_insert() - Associate a value with a key in the trie + * @trie: struct trie + * @key: the key + * @len: length + * @value: the value + * + * You can use the empty string as `key' to save data in the trie's root + * node. Note that the NULL pointer is an invalid `value' and is used to + * detect value-less nodes, so don't use it! + * + * If a value was replaced, the old value is returned. + */ +void *trie_insert(struct trie *trie, const char *key, size_t len, void *value) +{ + struct __trie_find_res res = __trie_find(trie, key, len); + + if (res.node) { + if (__is_exact(&res, len)) { + void *last = res.node->value; + + res.node->value = value; + return last; + } + __trie_insert_split(&res, key, len, value); + } else { + __trie_insert_child(&res, key, len, value); + } + return NULL; +} + +/** + * __trie_remove_leaf() - Remove a leaf node + * @res: struct __trie_find_res + * @dtor: value destructor + */ +static void __trie_remove_leaf(struct __trie_find_res *res, + void (*dtor)(void *)) +{ + struct trie *node = res->node, *parent = res->parent; + int i, j, k; + + /* + * Unlink the node which means + * a) delete it from its parent's mask and + * b) delete it from its parent's children array + * + * Then we can simply destroy it. + */ + + i = __key_index(*node->key); + j = __key_to_array_index(parent, *node->key); + + /* + * b) -- yes, we do b) before a) to not need to undo the mask + * changes if an allocation fails :-) and the code is different for + * each of the cases below, anyway: + * i) if the parent will have no children left, we don't bother + * doing reallocations and stuff. + * ii) if the value-less non-root parent will only have one child + * left, merge these two nodes to save space. + * iii) else, reallocate the children array normally. + * + * In i), it is impossible that the parent would have no value + * because of ii) in a former removal (just saying that every leaf + * node (except the root if it becomes a leaf) is guaranteed to have + * a value). + */ + if (parent->nchildren == 1) { /* i) */ + GB.Free((void **) &parent->children); + parent->children = NULL; + parent->nchildren = 0; + /* a) */ + clear_bit(parent, i); + parent->nchildren--; + /* !parent->len is equivalent to parent == trie_root */ + } else if (parent->nchildren == 2 && !parent->value + && !parent->len) { /* ii) */ + struct trie *other; + + if (parent->children[0] == node) + other = parent->children[1]; + else + other = parent->children[0]; + GB.Realloc((void **) &parent, sizeof(*parent) + parent->len + + other->len); + memcpy(parent->key + parent->len, other->key, other->len); + parent->len += other->len; + /* does a) */ + memcpy(parent->mask, other->mask, sizeof(parent->mask)); + GB.Free((void **) &parent->children); + parent->children = other->children; + parent->nchildren = other->nchildren; + parent->value = other->value; + /* Do NOT destroy_node() as we copied its ->children! */ + GB.Free((void **) &other); + } else { /* iii) */ + /* does a) */ + for (k = j + 1; k < parent->nchildren; k++) + parent->children[k - 1] = parent->children[k]; + parent->nchildren--; + GB.Realloc((void **) &parent->children, parent->nchildren * + sizeof(*parent->children)); + clear_bit(parent, i); + } + + destroy_node(node, dtor); +} + +/** + * __trie_remove_interior() - Remove an interior node + * @res: struct __trie_find_res + * @dtor: value destructor + */ +static void __trie_remove_interior(struct __trie_find_res *res, + void (*dtor)(void *)) +{ + /* + * Let's see: an interior node can only have 2 or more children, so + * we cannot possibly do any compression of the nodes. We just erase + * the value and leave the trie structure as-is. + */ + dtor(res->node->value); + res->node->value = NULL; +} + +/** + * trie_remove() - Remove a key from the trie + * @trie: struct trie + * @key: the key + * @len: length + * @dtor: value destructor + */ +void trie_remove(struct trie *trie, const char *key, size_t len, + void (*dtor)(void *)) +{ + struct __trie_find_res res; + struct trie *node; + + res = __trie_find(trie, key, len); + node = res.node; + /* + * We only want to work with valued, exactly-matching non-roots. + * Delete a value from the root anyways. + */ + if (!node || !__is_exact(&res, len) || !node->value) + return; + if (node == trie) { + dtor(node->value); + node->value = NULL; + return; + } + + if (!node->children) + __trie_remove_leaf(&res, dtor); + else + __trie_remove_interior(&res, dtor); +} + +/** + * trie_find() - Get a trie node from its key + * @trie: struct trie + * @key: the key + * @len: length + * + * Returns NULL if the key was not found. The empty string maps to the root + * node of the trie. + */ +struct trie *trie_find(const struct trie *trie, const char *key, size_t len) +{ + return __trie_find_exact(trie, key, len); +} + +/** + * trie_value() - Get value corresponding to a key + * @trie: struct trie + * @key: the key + * @len: length + * + * Return NULL if the key was not found. The empty string maps to the root + * node of the trie. + */ +void *trie_value(const struct trie *trie, const char *key, size_t len) +{ + struct trie *node = trie_find(trie, key, len); + + return node ? node->value : NULL; +} + +/** + * trie_constrain() - Constrain the trie paths + * @trie: struct trie + * @p: struct trie_prefix + * @c: character + * + * To `constrain' a trie means to limit keys to a given prefix. If you have + * a trie consisting of keys "test", "tesla" and "term" and you constrain it + * with the prefix "tes", only "test" and "tesla" will be reachable. + * + * The constraint is saved in the struct trie_prefix. Contraining a trie + * does not alter its structure so that you can constrain the same trie + * multiple times simultaneously. + * + * By calling this function multiple times, you can refine the prefix in + * `p'. To begin without a constraint, use a prefix filled by + * trie_reset_prefix(). + * + * If the prefix is not found, `p' is reset. + * + * WARNING + * Using any of the prefix-aware functions implies that the trie did not + * change between calls. If it did, the prefix may be invalid and the + * program may crash in consequence. + */ +void trie_constrain(const struct trie *trie, struct trie_prefix *p, char c) +{ + struct trie *node; + int i; + + node = p->node ? : (struct trie *) trie; + i = p->i; + + if (i == node->len) { + node = get_continuation(node, c); + if (!node) + goto reset; + p->node = node; + /* node->len is guaranteed to be positive here */ + p->i = 1; + } else { + if (node->key[i] != c) + goto reset; + p->i = ++i; + } + /* + * As a `logical' node counts only what has a value. If you insert + * "soup" and "sour", the resulting "sou" node would NOT exist + * logically. + * + * Additionally, the match must be exact to count as TRIE_EXACT. + */ + if (p->i == node->len && node->value) + p->state = TRIE_EXACT; + else + p->state = TRIE_EXIST; + return; + +reset: + trie_reset_prefix(p); +} + +/** + * trie_constrain2() - Constrain the trie multiple times + * @trie: struct trie + * @p: struct trie_prefix + * @str: string + * @len: length + * + * This function calls trie_constrain() in a loop - but with the + * difference that as soon as the prefix is not found, the function + * returns. In effect, the `str' is taken of consecutive constraints + * which should *ALL* be applied in row or none of them. + */ +void trie_constrain2(const struct trie *trie, struct trie_prefix *p, + const char *str, size_t len) +{ + int i; + + if (!len) { + p->state = trie->value ? TRIE_EXACT : TRIE_EXIST; + p->node = (struct trie *) trie; + p->i = 0; + return; + } + + for (i = 0; i < len; i++) { + trie_constrain(trie, p, str[i]); + if (p->state == TRIE_UNSET) + return; + } +} + +/** + * trie_find2() - Find a key from a trie constraint + * @trie: struct trie + * @p: struct trie_prefix + * @key: the key relative (!) to the prefix `p' + * @len: length + * + * This function is similar to trie_find(), except that keys are relative to + * the constraint in `p'. + */ +struct trie *trie_find2(const struct trie *trie, + const struct trie_prefix *p, + const char *key, size_t len) +{ + struct trie *node; + int i, j; + + node = p->node ? : (struct trie *) trie; + i = p->i; + /* + * First consume the rest of the prefix node. If none of the trivial + * cases occured, we can then use the normal traversal algorithm. + */ + for (j = 0; i < node->len && j < len; i++, j++) + if (node->key[i] != key[j]) + return NULL; + if (j == len) + return node; + node = get_continuation(node, key[j]); + if (!node) + return NULL; + return __trie_find_exact(node, key, len); +} + +/** + * trie_value2() - Analogon to trie_value() using trie_find2() + * @trie: struct trie + * @p: struct trie_prefix + * @key: the key + * @len: length + */ +void *trie_value2(const struct trie *trie, const struct trie_prefix *p, + const char *key, size_t len) +{ + struct trie *node = trie_find2(trie, p, key, len); + + return node ? node->value : NULL; +} diff --git a/main/lib/data/trie.h b/main/lib/data/trie.h new file mode 100644 index 00000000..f6fa35f2 --- /dev/null +++ b/main/lib/data/trie.h @@ -0,0 +1,82 @@ +/* + * trie.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef TRIE_H +#define TRIE_H + +#include +#include + +struct trie { + uint64_t mask[4]; /* 256 Bits */ + struct trie **children; + unsigned int nchildren; + void *value; + size_t len; + char key[]; +}; + +enum trie_path { + TRIE_UNSET = 0, /* No chance to get a match from here */ + TRIE_EXIST, /* We are within a key */ + TRIE_EXACT, /* We got an exact match */ +}; + +struct trie_prefix { + enum trie_path state; + struct trie *node; + int i; +}; + +extern struct trie *new_trie(void); +extern void destroy_trie(struct trie *trie, void (*dtor)(void *)); +extern void clear_trie(struct trie *trie, void (*dtor)(void *)); + +extern void *trie_insert(struct trie *trie, const char *key, size_t len, + void *value); +extern void trie_remove(struct trie *trie, const char *key, size_t len, + void (*dtor)(void *)); + +extern struct trie *trie_find(const struct trie *trie, const char *key, + size_t len); +extern void *trie_value(const struct trie *trie, const char *key, + size_t len); + +static inline void trie_reset_prefix(struct trie_prefix *p) +{ + p->state = TRIE_UNSET; + p->node = NULL; + p->i = 0; +} + +extern void trie_constrain(const struct trie *trie, + struct trie_prefix *p, char c); +extern void trie_constrain2(const struct trie *trie, + struct trie_prefix *p, const char *str, + size_t len); +extern struct trie *trie_find2(const struct trie *trie, + const struct trie_prefix *p, + const char *key, size_t len); +extern void *trie_value2(const struct trie *trie, + const struct trie_prefix *p, + const char *key, size_t len); + +#endif /* TRIE_H */ diff --git a/main/lib/db/CConnection.c b/main/lib/db/CConnection.c new file mode 100644 index 00000000..cec0963b --- /dev/null +++ b/main/lib/db/CConnection.c @@ -0,0 +1,854 @@ +/*************************************************************************** + + CConnection.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONNECTION_C + +#include "main.h" + +#include "CTable.h" +//#include "CView.h" +#include "CDatabase.h" +#include "CUser.h" +#include "CConnection.h" + + +/*************************************************************************** + + Connection + +***************************************************************************/ + +static CCONNECTION *_current = NULL; + +static SUBCOLLECTION_DESC _databases_desc = +{ + ".Connection.Databases", + (void *)CDATABASE_get, + (void *)CDATABASE_exist, + (void *)CDATABASE_list, + (void *)CDATABASE_release +}; + +static SUBCOLLECTION_DESC _users_desc = +{ + ".Connection.Users", + (void *)CUSER_get, + (void *)CUSER_exist, + (void *)CUSER_list, + (void *)CUSER_release +}; + +static SUBCOLLECTION_DESC _tables_desc = +{ + ".Connection.Tables", + (void *)CTABLE_get, + (void *)CTABLE_exist, + (void *)CTABLE_list, + (void *)CTABLE_release +}; + +/*static GB_SUBCOLLECTION_DESC _views_desc = +{ + ".ConnectionViews", + (void *)CVIEW_get, + (void *)CVIEW_exist, + (void *)CVIEW_list +};*/ + + + +static void open_connection(CCONNECTION *_object) +{ + GB_COLLECTION options; + + options = GB.GetProperty(THIS, "Options")->_object.value; + + if (DB_Open(&THIS->desc, &THIS->driver, &THIS->db, options)) + return; + + THIS->limit = 0; + THIS->trans = 0; + + THIS->db.flags.system = !THIS->desc.name || THIS->driver->Database.IsSystem(&THIS->db, THIS->desc.name); +} + +static bool check_opened(CCONNECTION *_object) +{ + DB_CurrentDatabase = &THIS->db; + + /*if (!THIS->db.handle) + open_connection(THIS);*/ + + if (!THIS->db.handle) + { + GB.Error("Connection is not opened"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_OPEN() \ + if (check_opened(THIS)) \ + return; + +static int get_current(CCONNECTION **current) +{ + if (*current == NULL) + { + if (_current == NULL) + { + GB.Error("No current connection"); + return TRUE; + } + *current = _current; + } + + return FALSE; +} + +#define CHECK_DB() \ + if (get_current((CCONNECTION **)(void *)&_object)) \ + return; + +static void close_connection(CCONNECTION *_object) +{ + if (!THIS->db.handle) + return; + + GB.Unref(POINTER(&THIS->databases)); + THIS->databases = NULL; + GB.Unref(POINTER(&THIS->users)); + THIS->users = NULL; + GB.Unref(POINTER(&THIS->tables)); + THIS->tables = NULL; + + THIS->driver->Close(&THIS->db); + GB.FreeString(&THIS->db.charset); + + THIS->db.handle = NULL; + THIS->driver = NULL; +} + + +BEGIN_METHOD_VOID(Connection_new) + + THIS->db.handle = NULL; + THIS->db.ignore_case = FALSE; // Now case is sensitive by default! + THIS->db.timeout = 20; // Connection timeout is 20 seconds by default + THIS->db.timezone = GB.System.TimeZone(); + + if (_current == NULL) + _current = THIS; + +END_METHOD + + +BEGIN_METHOD_VOID(Connection_free) + + close_connection(THIS); + + if (_current == THIS) + _current = NULL; + + GB.StoreString(NULL, &THIS->desc.type); + GB.StoreString(NULL, &THIS->desc.host); + GB.StoreString(NULL, &THIS->desc.user); + GB.StoreString(NULL, &THIS->desc.password); + GB.StoreString(NULL, &THIS->desc.name); + GB.StoreString(NULL, &THIS->desc.port); + GB.StoreString(NULL, &THIS->db.charset); + +END_METHOD + + +#define IMPLEMENT(_name, _prop) \ +BEGIN_PROPERTY(Connection_##_name) \ +\ + if (READ_PROPERTY) \ + GB.ReturnString(THIS->desc._prop); \ + else \ + GB.StoreString(PROP(GB_STRING), &THIS->desc._prop); \ +\ +END_PROPERTY + +IMPLEMENT(Type, type) +IMPLEMENT(Host, host) +IMPLEMENT(User, user) +IMPLEMENT(Password, password) +IMPLEMENT(Name, name) +IMPLEMENT(Port, port) + + +BEGIN_PROPERTY(Connection_Version) + + CHECK_DB(); + CHECK_OPEN(); + + GB.ReturnInteger(THIS->db.version); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Timeout) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->db.timeout); + else + THIS->db.timeout = VPROP(GB_INTEGER); + +END_PROPERTY + + +#if 0 +BEGIN_PROPERTY(Connection_Timezone) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->db.timezone); + else + THIS->db.timezone = VPROP(GB_INTEGER); + +END_PROPERTY +#endif + + +BEGIN_PROPERTY(Connection_Opened) + + CHECK_DB(); + + GB.ReturnBoolean(THIS->db.handle != NULL); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Error) + + CHECK_DB(); + + GB.ReturnInteger(THIS->db.error); + +END_PROPERTY + + +/*BEGIN_PROPERTY(Connection_Transaction) + + CHECK_DB(); + + GB.ReturnInteger(THIS->trans); + +END_PROPERTY*/ + + +BEGIN_METHOD_VOID(Connection_Open) + + CHECK_DB(); + + if (THIS->db.handle) + { + GB.Error("Connection already opened"); + return; + } + + open_connection(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Connection_Close) + + CHECK_DB(); + + close_connection(THIS); + +END_METHOD + +#if 0 +BEGIN_PROPERTY(CCONNECTION_ignore_case) + + CHECK_DB(); + CHECK_OPEN(); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->db.ignore_case); + else + { + if (THIS->db.flags.no_case) + { + if (THIS->db.ignore_case) + GB.Error("This database driver cannot be case sensitive"); + else + GB.Error("This database driver is always case sensitive"); + return; + } + THIS->db.ignore_case = VPROP(GB_BOOLEAN); + } + +END_PROPERTY +#endif + +BEGIN_PROPERTY(Connection_IgnoreCharset) + + CHECK_DB(); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->ignore_charset); + else + THIS->ignore_charset = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Connection_Collations) + + GB_ARRAY array; + + CHECK_DB(); + CHECK_OPEN(); + + if (!THIS->db.flags.no_collation) + { + array = THIS->driver->GetCollations(&THIS->db); + if (array) + { + GB.ReturnObject(array); + return; + } + } + + GB.Error("Collations are not supported"); + +END_PROPERTY + +BEGIN_METHOD_VOID(Connection_Begin) + + CHECK_DB(); + CHECK_OPEN(); + + if (!THIS->db.flags.no_nest || THIS->trans == 0) + THIS->driver->Begin(&THIS->db); + THIS->trans++; + +END_METHOD + + +BEGIN_METHOD_VOID(Connection_Commit) + + CHECK_DB(); + CHECK_OPEN(); + + if (THIS->trans == 0) + { + //GB.Error("Not in a transaction"); + return; + } + + THIS->trans--; + if (!THIS->db.flags.no_nest || THIS->trans == 0) + THIS->driver->Commit(&THIS->db); + +END_METHOD + + +BEGIN_METHOD_VOID(Connection_Rollback) + + CHECK_DB(); + CHECK_OPEN(); + + if (THIS->trans == 0) + { + //GB.Error("Not in a transaction"); + return; + } + + THIS->trans--; + if (!THIS->db.flags.no_nest || THIS->trans == 0) + THIS->driver->Rollback(&THIS->db); + +END_METHOD + + +BEGIN_METHOD(Connection_Limit, GB_INTEGER limit) + + CHECK_DB(); + CHECK_OPEN(); + + THIS->limit = VARG(limit); + GB.ReturnObject(THIS); + +END_PROPERTY + +static char *_make_query_buffer; +static char *_make_query_original; + +static void make_query_get_param(int index, char **str, int *len) +{ + if (index == 1) + *str = _make_query_buffer; + else if (index == 2) + *str = _make_query_original; + + *len = -1; +} + +static char *make_query(CCONNECTION *_object, char *pattern, int len, int narg, GB_VALUE *arg) +{ + char *query; + const char *keyword; + char buffer[32]; + + query = DB_MakeQuery(THIS->driver, &THIS->db, pattern, len, narg, arg); + + if (query && THIS->limit > 0 && strncasecmp(query, "SELECT ", 7) == 0) + { + keyword = THIS->db.limit.keyword; + if (!keyword) + keyword = "LIMIT"; + + snprintf(buffer, sizeof(buffer), "%s %d", keyword, THIS->limit); + + _make_query_buffer = buffer; + _make_query_original = &query[7]; + + switch (THIS->db.limit.pos) + { + case DB_LIMIT_AT_BEGIN: + query = GB.SubstString("SELECT &1 &2", 0, make_query_get_param); + break; + + case DB_LIMIT_AT_END: + default: + query = GB.SubstString("SELECT &2 &1", 0, make_query_get_param); + break; + } + + THIS->limit = 0; + } + + return query; +} + +BEGIN_METHOD(Connection_Exec, GB_STRING query; GB_VALUE param[0]) + + char *query; + CRESULT *result; + + CHECK_DB(); + CHECK_OPEN(); + + query = make_query(THIS, STRING(query), LENGTH(query), GB.NParam(), ARG(param[0])); + if (!query) + return; + + result = DB_MakeResult(THIS, RESULT_FIND, NULL, query); + + if (result) + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(Connection_Create, GB_STRING table) + + CRESULT *result; + char *table = GB.ToZeroString(ARG(table)); + + CHECK_DB(); + CHECK_OPEN(); + + if (!table || !*table) + { + GB.Error("Void table name"); + return; + } + + result = DB_MakeResult(THIS, RESULT_CREATE, table, NULL); + + if (result) + GB.ReturnObject(result); + else + GB.ReturnNull(); + +END_METHOD + + +static char *get_query(char *prefix, CCONNECTION *_object, char *table, int len_table, char *query, int len_query, GB_VALUE *arg) +{ + if (!len_table) + { + GB.Error("Void table name"); + return NULL; + } + + q_init(); + + q_add(prefix); + q_add(" "); + q_add(DB_GetQuotedTable(THIS->driver, &THIS->db, table, len_table)); + + if (query && len_query > 0) + { + q_add(" "); + if (strncasecmp(query, "WHERE ", 6) && strncasecmp(query, "ORDER BY ", 9)) + q_add("WHERE "); + q_add_length(query, len_query); + } + + query = make_query(THIS, q_get(), q_length(), GB.NParam(), arg); + + return query; +} + + +BEGIN_METHOD(Connection_Find, GB_STRING table; GB_STRING query; GB_VALUE param[0]) + + char *query; + CRESULT *result; + + CHECK_DB(); + CHECK_OPEN(); + + query = get_query("SELECT * FROM", THIS, STRING(table), LENGTH(table), + MISSING(query) ? NULL : STRING(query), + MISSING(query) ? 0 : LENGTH(query), + ARG(param[0])); + + if (!query) + return; + + result = DB_MakeResult(THIS, RESULT_FIND, NULL, query); + + if (result) + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(Connection_Delete, GB_STRING table; GB_STRING query; GB_VALUE param[0]) + + char *query; + + CHECK_DB(); + CHECK_OPEN(); + + query = get_query("DELETE FROM", THIS, STRING(table), LENGTH(table), + MISSING(query) ? NULL : STRING(query), + MISSING(query) ? 0 : LENGTH(query), + ARG(param[0])); + + if (!query) + return; + + DB_MakeResult(THIS, RESULT_DELETE, NULL, query); + +END_METHOD + + +BEGIN_METHOD(Connection_Edit, GB_STRING table; GB_STRING query; GB_VALUE param[0]) + + char *query; + CRESULT *result; + /*char *table = GB.ToZeroString(ARG(table));*/ + + CHECK_DB(); + CHECK_OPEN(); + + /*if (check_table(THIS, table, TRUE)) + return;*/ + + query = get_query("SELECT * FROM", THIS, STRING(table), LENGTH(table), + MISSING(query) ? NULL : STRING(query), + MISSING(query) ? 0 : LENGTH(query), + ARG(param[0])); + + if (!query) + return; + + result = DB_MakeResult(THIS, RESULT_EDIT, GB.ToZeroString(ARG(table)), query); + + if (result) + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(Connection_Quote, GB_STRING name; GB_BOOLEAN is_table) + + char *name = STRING(name); + int len = LENGTH(name); + + CHECK_DB(); + CHECK_OPEN(); + + if (VARGOPT(is_table, FALSE)) // && THIS->db.flags.schema) + GB.ReturnNewZeroString(DB_GetQuotedTable(THIS->driver, &THIS->db, STRING(name), LENGTH(name))); + else + { + q_init(); + q_add(THIS->driver->GetQuote()); + q_add_length(name, len); + q_add(THIS->driver->GetQuote()); + GB.ReturnString(q_get()); + } + +END_METHOD + + +BEGIN_METHOD(Connection_FormatBlob, GB_STRING data) + + DB_BLOB blob; + + CHECK_DB(); + CHECK_OPEN(); + + blob.data = STRING(data); + blob.length = LENGTH(data); + + q_init(); + DB_CurrentDatabase = &THIS->db; + (*THIS->driver->FormatBlob)(&blob, q_add_length); + GB.ReturnString(q_get()); + +END_METHOD + + +BEGIN_METHOD(Connection_Subst, GB_STRING query; GB_VALUE param[0]) + + char *query; + + CHECK_DB(); + CHECK_OPEN(); + + query = make_query(THIS, STRING(query), LENGTH(query), GB.NParam(), ARG(param[0])); + + if (!query) + return; + + GB.ReturnString(query); + +END_METHOD + + +BEGIN_PROPERTY(Connection_Current) + + if (READ_PROPERTY) + GB.ReturnObject(_current); + else + _current = (CCONNECTION *)VPROP(GB_OBJECT); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Charset) + + CHECK_DB(); + CHECK_OPEN(); + + if (THIS->db.charset) + GB.ReturnString(THIS->db.charset); + else + GB.ReturnConstZeroString("ASCII"); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Databases) + + CHECK_DB(); + CHECK_OPEN(); + + GB_SubCollectionNew(&THIS->databases, &_databases_desc, THIS); + GB.ReturnObject(THIS->databases); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Users) + + CHECK_DB(); + CHECK_OPEN(); + + GB_SubCollectionNew(&THIS->users, &_users_desc, THIS); + GB.ReturnObject(THIS->users); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Tables) + + CHECK_DB(); + CHECK_OPEN(); + + GB_SubCollectionNew(&THIS->tables, &_tables_desc, THIS); + GB.ReturnObject(THIS->tables); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CCONNECTION_views) + + CHECK_DB(); + CHECK_OPEN(); + + GB.SubCollection.New(&THIS->views, &_views_desc, THIS); + GB.ReturnObject(THIS->views); + +END_PROPERTY*/ + + +BEGIN_PROPERTY(Connection_Debug) + + if (READ_PROPERTY) + GB.ReturnBoolean(DB_IsDebug()); + else + DB_SetDebug(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Handle) + + CHECK_DB(); + GB.ReturnPointer(THIS->db.handle); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_LastInsertId) + + CHECK_DB(); + CHECK_OPEN(); + + GB.ReturnLong((*THIS->driver->GetLastInsertId)(&THIS->db)); + +END_PROPERTY + +GB_DESC CConnectionDesc[] = +{ + GB_DECLARE("_Connection", sizeof(CCONNECTION)), + + GB_METHOD("_new", NULL, Connection_new, NULL), + GB_METHOD("_free", NULL, Connection_free, NULL), + + GB_PROPERTY("Type", "s", Connection_Type), + GB_PROPERTY("Host", "s", Connection_Host), + GB_PROPERTY("Login", "s", Connection_User), + GB_PROPERTY("User", "s", Connection_User), + GB_PROPERTY("Password", "s", Connection_Password), + GB_PROPERTY("Name", "s", Connection_Name), + GB_PROPERTY("Port", "s", Connection_Port), + GB_PROPERTY("Timeout", "i", Connection_Timeout), + //GB_PROPERTY("Timezone", "i", Connection_Timezone), + GB_PROPERTY_READ("Charset", "s", Connection_Charset), + GB_PROPERTY_READ("Version", "i", Connection_Version), + GB_PROPERTY_READ("Opened", "b", Connection_Opened), + GB_PROPERTY_READ("Error", "i", Connection_Error), + //GB_PROPERTY_READ("Transaction", "i", Connection_Transaction), + GB_PROPERTY("IgnoreCharset", "b", Connection_IgnoreCharset), + GB_PROPERTY_READ("Collations", "String[]", Connection_Collations), + GB_PROPERTY_READ("Handle", "p", Connection_Handle), + + GB_PROPERTY_READ("LastInsertId", "l", Connection_LastInsertId), + + GB_METHOD("Open", NULL, Connection_Open, NULL), + GB_METHOD("Close", NULL, Connection_Close, NULL), + + GB_METHOD("Limit", "Connection", Connection_Limit, "(Limit)i"), + GB_METHOD("Exec", "Result", Connection_Exec, "(Request)s(Arguments)."), + GB_METHOD("Create", "Result", Connection_Create, "(Table)s"), + GB_METHOD("Find", "Result", Connection_Find, "(Table)s[(Request)s(Arguments).]"), + GB_METHOD("Edit", "Result", Connection_Edit, "(Table)s[(Request)s(Arguments).]"), + GB_METHOD("Delete", NULL, Connection_Delete, "(Table)s[(Request)s(Arguments).]"), + GB_METHOD("Subst", "s", Connection_Subst, "(Format)s(Arguments)."), + + GB_METHOD("Begin", NULL, Connection_Begin, NULL), + GB_METHOD("Commit", NULL, Connection_Commit, NULL), + GB_METHOD("Rollback", NULL, Connection_Rollback, NULL), + + GB_METHOD("Quote", "s", Connection_Quote, "(Name)s[(Table)b]"), + GB_METHOD("FormatBlob", "s", Connection_FormatBlob, "(Data)s"), + + GB_PROPERTY("Tables", ".Connection.Tables", Connection_Tables), + GB_PROPERTY("Databases", ".Connection.Databases", Connection_Databases), + GB_PROPERTY("Users", ".Connection.Users", Connection_Users), + //GB_PROPERTY("Views", ".ConnectionViews", CCONNECTION_views), + + GB_CONSTANT("_Properties", "s", "Type,Host,Login,Password,Name,Port"), + + GB_END_DECLARE +}; + + +GB_DESC CDBDesc[] = +{ + GB_DECLARE("DB", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Boolean", "i", GB_T_BOOLEAN), + GB_CONSTANT("Integer", "i", GB_T_INTEGER), + GB_CONSTANT("Long", "i", GB_T_LONG), + GB_CONSTANT("Float", "i", GB_T_FLOAT), + GB_CONSTANT("Date", "i", GB_T_DATE), + GB_CONSTANT("String", "i", GB_T_STRING), + GB_CONSTANT("Serial", "i", DB_T_SERIAL), + GB_CONSTANT("Blob", "i", DB_T_BLOB), + + GB_STATIC_PROPERTY("Current", "Connection", Connection_Current), + + GB_STATIC_METHOD("Open", NULL, Connection_Open, NULL), + GB_STATIC_METHOD("Close", NULL, Connection_Close, NULL), + + GB_STATIC_PROPERTY_READ("Charset", "s", Connection_Charset), + GB_STATIC_PROPERTY_READ("Version", "i", Connection_Version), + GB_STATIC_PROPERTY_READ("Opened", "b", Connection_Opened), + GB_STATIC_PROPERTY_READ("Error", "i", Connection_Error), + //GB_STATIC_PROPERTY_READ("Transaction", "i", Connection_Transaction), + GB_STATIC_PROPERTY("IgnoreCharset", "b", Connection_IgnoreCharset), + GB_STATIC_PROPERTY_READ("Collations", "String[]", Connection_Collations), + GB_STATIC_PROPERTY_READ("Handle", "p", Connection_Handle), + + GB_STATIC_PROPERTY_READ("LastInsertId", "l", Connection_LastInsertId), + + GB_STATIC_PROPERTY("Debug", "b", Connection_Debug), + + GB_STATIC_METHOD("Limit", "Connection", Connection_Limit, "(Limit)i"), + GB_STATIC_METHOD("Exec", "Result", Connection_Exec, "(Request)s(Arguments)."), + GB_STATIC_METHOD("Create", "Result", Connection_Create, "(Table)s"), + GB_STATIC_METHOD("Find", "Result", Connection_Find, "(Table)s[(Request)s(Arguments).]"), + GB_STATIC_METHOD("Edit", "Result", Connection_Edit, "(Table)s[(Request)s(Arguments).]"), + GB_STATIC_METHOD("Delete", NULL, Connection_Delete, "(Table)s[(Request)s(Arguments).]"), + GB_STATIC_METHOD("Subst", "s", Connection_Subst, "(Format)s(Arguments)."), + + GB_STATIC_METHOD("Begin", NULL, Connection_Begin, NULL), + GB_STATIC_METHOD("Commit", NULL, Connection_Commit, NULL), + GB_STATIC_METHOD("Rollback", NULL, Connection_Rollback, NULL), + + GB_STATIC_METHOD("Quote", "s", Connection_Quote, "(Name)s[(Table)b]"), + GB_STATIC_METHOD("FormatBlob", "s", Connection_FormatBlob, "(Data)s"), + + GB_STATIC_PROPERTY("Tables", ".Connection.Tables", Connection_Tables), + //GB_STATIC_PROPERTY("Views", ".ConnectionViews", CCONNECTION_views), + GB_STATIC_PROPERTY("Databases", ".Connection.Databases", Connection_Databases), + GB_STATIC_PROPERTY("Users", ".Connection.Users", Connection_Users), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CConnection.h b/main/lib/db/CConnection.h new file mode 100644 index 00000000..bc90a3d6 --- /dev/null +++ b/main/lib/db/CConnection.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CConnection.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONNECTION_H +#define __CCONNECTION_H + +#include "gambas.h" +#include "gb.db.h" +#include "c_subcollection.h" + +#ifndef __CCONNECTION_C +extern GB_DESC CConnectionDesc[]; +extern GB_DESC CDBDesc[]; +#else + +#define THIS ((CCONNECTION *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + DB_DATABASE db; + DB_DESC desc; + CSUBCOLLECTION *databases; + CSUBCOLLECTION *tables; + CSUBCOLLECTION *views; + CSUBCOLLECTION *users; + int limit; + int trans; + bool ignore_charset; + } + CCONNECTION; + +#endif diff --git a/main/lib/db/CDatabase.c b/main/lib/db/CDatabase.c new file mode 100644 index 00000000..5b294abd --- /dev/null +++ b/main/lib/db/CDatabase.c @@ -0,0 +1,206 @@ +/*************************************************************************** + + CDatabase.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDATABASE_C + +#include "main.h" + +#include "CDatabase.h" + + +static int valid_database(CDATABASE *_object) +{ + return !THIS->conn || !THIS->conn->db.handle; +} + +static bool check_database(CCONNECTION *conn, const char *name, bool must_exist) +{ + bool exist = conn->driver->Database.Exist(&conn->db, (char *)name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown database: &1", name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Database already exists: &1", name); + return TRUE; + } + } + + return FALSE; +} + + +void *CDATABASE_get(CCONNECTION *conn, const char *name) +{ + CDATABASE *_object; + + if (check_database(conn, name, TRUE)) + return NULL; + + _object = GB.New(GB.FindClass("Database"), NULL, NULL); + THIS->conn = conn; + THIS->driver = conn->driver; + THIS->name = GB.NewZeroString(name); + return THIS; +} + +int CDATABASE_exist(CCONNECTION *conn, const char *name) +{ + return conn->driver->Database.Exist(&conn->db, (char *)name); +} + +void CDATABASE_list(CCONNECTION *conn, char ***list) +{ + conn->driver->Database.List(&conn->db, list); +} + +void CDATABASE_release(CCONNECTION *conn, void *_object) +{ + THIS->conn = NULL; +} + + +/*************************************************************************** + + Database + +***************************************************************************/ + +BEGIN_METHOD_VOID(CDATABASE_free) + + if (!valid_database(THIS)) + GB_SubCollectionRemove(THIS->conn->databases, THIS->name, 0); + GB.FreeString(&THIS->name); + +END_METHOD + + +BEGIN_PROPERTY(CDATABASE_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(CDATABASE_system) + + GB.ReturnBoolean(THIS->driver->Database.IsSystem(&THIS->conn->db, THIS->name)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CDATABASE_delete) + + THIS->conn->driver->Database.Delete(&THIS->conn->db, THIS->name); + +END_METHOD + + +BEGIN_PROPERTY(CDATABASE_connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + + + + +GB_DESC CDatabaseDesc[] = +{ + GB_DECLARE("Database", sizeof(CDATABASE)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_database), + + GB_METHOD("_free", NULL, CDATABASE_free, NULL), + + GB_METHOD("Delete", NULL, CDATABASE_delete, NULL), + + GB_PROPERTY_READ("Name", "s", CDATABASE_name), + GB_PROPERTY_READ("System", "b", CDATABASE_system), + GB_PROPERTY_READ("Connection", "Connection", CDATABASE_connection), + + GB_END_DECLARE +}; + + + +/*************************************************************************** + + .Connection.Databases + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CDATABASE_add, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + if (DB_CheckNameWith(name, "database", conn->db.db_name_char)) + return; + + if (check_database(conn, name, FALSE)) + return; + + conn->driver->Database.Create(&conn->db, name); + +END_METHOD + + +BEGIN_METHOD(CDATABASE_remove, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + GB_SubCollectionRemove(THIS, STRING(name), LENGTH(name)); + + if (check_database(conn, name, TRUE)) + return; + + conn->driver->Database.Delete(&conn->db, name); + +END_METHOD + + + +GB_DESC CConnectionDatabasesDesc[] = +{ + GB_DECLARE(".Connection.Databases", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, CDATABASE_add, "(Name)s"), + GB_METHOD("Remove", NULL, CDATABASE_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CDatabase.h b/main/lib/db/CDatabase.h new file mode 100644 index 00000000..6f0331db --- /dev/null +++ b/main/lib/db/CDatabase.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + CDatabase.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDATABASE_H +#define __CDATABASE_H + +#include "gambas.h" +#include "gb.db.h" +#include "CConnection.h" + +#ifndef __CDATABASE_C +extern GB_DESC CConnectionDatabasesDesc[]; +extern GB_DESC CDatabaseDesc[]; +#else + +#define THIS ((CDATABASE *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + char *name; + } + CDATABASE; + +void *CDATABASE_get(CCONNECTION *conn, const char *key); +int CDATABASE_exist(CCONNECTION *conn, const char *key); +void CDATABASE_list(CCONNECTION *conn, char ***list); +void CDATABASE_release(CCONNECTION *conn, void *_object); + +#endif + diff --git a/main/lib/db/CField.c b/main/lib/db/CField.c new file mode 100644 index 00000000..06b6d48d --- /dev/null +++ b/main/lib/db/CField.c @@ -0,0 +1,329 @@ +/*************************************************************************** + + CField.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFIELD_C + +#include "main.h" + +#include "CField.h" + + +static int valid_field(CFIELD *_object) +{ + return !THIS->table || !THIS->table->conn || !THIS->table->conn->db.handle; +} + +static bool exist_field(CTABLE *table, const char *name) +{ + DB_FIELD *fp; + + if (!name || !*name) + return FALSE; + + if (table->create) + { + for (fp = table->new_fields; fp; fp = fp->next) + { + if (!strcasecmp(fp->name, name)) + return TRUE; + } + + return FALSE; + } + else + return table->driver->Field.Exist(&table->conn->db, table->name, (char *)name); +} + +static bool check_field(CTABLE *table, char *name, bool must_exist) +{ + bool exist = exist_field(table, name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown field: &1.&2", table->name, name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Field already exists: &1.&2", table->name, name); + return TRUE; + } + } + + return FALSE; +} + + +static bool check_type(int type) +{ + if (type == GB_T_BOOLEAN + || type == GB_T_INTEGER + || type == GB_T_LONG + || type == GB_T_FLOAT + || type == GB_T_DATE + || type == GB_T_STRING + || type == DB_T_SERIAL + || type == DB_T_BLOB) + return FALSE; + + GB.Error("Bad field type"); + return TRUE; +} + + +static CFIELD *make_field(CTABLE *table, const char *name, bool must_exist) +{ + CFIELD *_object; + + if (check_field(table, (char *)name, must_exist)) + return NULL; + + _object = GB.New(GB.FindClass("Field"), NULL, NULL); + THIS->table = table; + THIS->driver = table->conn->driver; + THIS->name = GB.NewZeroString(name); + + return _object; +} + + +void CFIELD_free_info(DB_FIELD *info) +{ + GB.FreeString(&info->name); + GB.FreeString(&info->collation); + GB.StoreVariant(NULL, &info->def); + info->type = GB_T_NULL; + info->length = 0; +} + +static void add_new_field(CTABLE *table, DB_FIELD *field) +{ + DB_FIELD **fp; + + fp = &table->new_fields; + + for(;;) + { + if (!*fp) + { + *fp = field; + field->next = NULL; + return; + } + + fp = &((*fp)->next); + } +} + + +void *CFIELD_get(CTABLE *table, const char *name) +{ + CFIELD *field = make_field(table, name, TRUE); + table->driver->Field.Info(&table->conn->db, table->name, (char *)name, &field->info); + return field; +} + + +int CFIELD_exist(CTABLE *table, const char *name) +{ + return exist_field(table, name); +} + +void CFIELD_list(CTABLE *table, char ***list) +{ + table->driver->Field.List(&table->conn->db, table->name, list); +} + +void CFIELD_release(CTABLE *table, void *_object) +{ + THIS->table = NULL; +} + + +/*************************************************************************** + + Field + +***************************************************************************/ + +BEGIN_METHOD_VOID(Field_free) + + if (!valid_field(THIS)) + GB_SubCollectionRemove(THIS->table->fields, THIS->name, 0); + + GB.FreeString(&THIS->name); + + CFIELD_free_info(&THIS->info); + +END_METHOD + + + +/*BEGIN_METHOD_VOID(CFIELD_delete) + + THIS->table->conn->driver->User.Delete(THIS->conn->handle, THIS->name); + +END_METHOD*/ + + +BEGIN_PROPERTY(Field_Name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Type) + + GB.ReturnInteger(THIS->info.type); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Length) + + GB.ReturnInteger(THIS->info.length); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Default) + + GB.ReturnVariant(&THIS->info.def); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Table) + + GB.ReturnObject(THIS->table); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Collation) + + GB.ReturnString(THIS->info.collation); + +END_PROPERTY + + +GB_DESC CFieldDesc[] = +{ + GB_DECLARE("Field", sizeof(CFIELD)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_field), + + GB_METHOD("_free", NULL, Field_free, NULL), + + //GB_METHOD("Delete", NULL, CFIELD_delete, NULL), + + GB_PROPERTY_READ("Name", "s", Field_Name), + GB_PROPERTY_READ("Type", "i", Field_Type), + GB_PROPERTY_READ("Length", "i", Field_Length), + GB_PROPERTY_READ("Default", "v", Field_Default), + GB_PROPERTY_READ("Table", "Table", Field_Table), + GB_PROPERTY_READ("Collation", "s", Field_Collation), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + .Table.Fields + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(Field_Add, GB_STRING name; GB_INTEGER type; GB_INTEGER length; GB_VARIANT def; GB_STRING collation) + + CTABLE *table = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + DB_FIELD new_field, *info; + + if (!table->create) + { + GB.Error("Table already exists"); + return; + } + + if (DB_CheckName(name, "field")) + return; + + if (check_field(table, name, FALSE)) + return; + + new_field.next = NULL; + + new_field.type = VARG(type); + if (check_type(new_field.type)) + return; + + new_field.length = VARGOPT(length, 0); + if (new_field.length < 0) + new_field.length = 0; + else if (new_field.length > 65535) + new_field.length = 65535; + + GB.Alloc(POINTER(&info), sizeof(DB_FIELD)); + + info->next = NULL; + info->type = new_field.type; + info->length = new_field.length; + + info->def.type = GB_T_NULL; + if (!MISSING(def)) + GB.StoreVariant(ARG(def), &info->def); + + info->name = GB.NewString(STRING(name), LENGTH(name)); + //DB_LowerString(info->name); + + if (info->type == GB_T_STRING && !MISSING(collation) && LENGTH(collation) > 0) + info->collation = GB.NewString(STRING(collation), LENGTH(collation)); + else + info->collation = NULL; + + add_new_field(table, info); + +END_METHOD + + +GB_DESC CTableFieldsDesc[] = +{ + GB_DECLARE(".Table.Fields", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, Field_Add, "(Name)s(Type)i[(Length)i(Default)v(Collation)s]"), + //GB_METHOD("Remove", NULL, CFIELD_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CField.h b/main/lib/db/CField.h new file mode 100644 index 00000000..9579a443 --- /dev/null +++ b/main/lib/db/CField.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + CField.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFIELD_H +#define __CFIELD_H + +#include "gambas.h" +#include "gb.db.h" +#include "CTable.h" + +#ifndef __CFIELD_C +extern GB_DESC CTableFieldsDesc[]; +extern GB_DESC CFieldDesc[]; +#else + +#define THIS ((CFIELD *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CTABLE *table; + char *name; + DB_FIELD info; + } + CFIELD; + +void *CFIELD_get(CTABLE *table, const char *name); +int CFIELD_exist(CTABLE *table, const char *name); +void CFIELD_list(CTABLE *table, char ***list); +void CFIELD_release(CTABLE *table, void *_object); + +void CFIELD_free_info(DB_FIELD *info); + +#endif + diff --git a/main/lib/db/CIndex.c b/main/lib/db/CIndex.c new file mode 100644 index 00000000..cc061410 --- /dev/null +++ b/main/lib/db/CIndex.c @@ -0,0 +1,274 @@ +/*************************************************************************** + + CIndex.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CINDEX_C + +#include "main.h" + +#include "CIndex.h" + + +static int valid_index(CINDEX *_object) +{ + return !THIS->table || !THIS->table->conn || !THIS->table->conn->db.handle; +} + + +static bool exist_index(CTABLE *table, const char *name) +{ + if (!name || !*name) + return FALSE; + + return table->driver->Index.Exist(&table->conn->db, table->name, (char *)name); +} + +static bool check_index(CTABLE *table, const char *name, bool must_exist) +{ + bool exist = exist_index(table, name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown index: &1.&2", table->name, name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Index already exists: &1.&2", table->name, name); + return TRUE; + } + } + + return FALSE; +} + + +static CINDEX *make_index(CTABLE *table, const char *name, bool must_exist) +{ + CINDEX *_object; + + if (check_index(table, name, must_exist)) + return NULL; + + _object = GB.New(GB.FindClass("Index"), NULL, NULL); + THIS->table = table; + THIS->driver = table->conn->driver; + THIS->name = GB.NewZeroString(name); + + return _object; +} + + +void *CINDEX_get(CTABLE *table, const char *name) +{ + CINDEX *index = make_index(table, name, TRUE); + table->driver->Index.Info(&table->conn->db, table->name, (char *)name, &index->info); + return index; +} + + +int CINDEX_exist(CTABLE *table, const char *name) +{ + return exist_index(table, name); +} + + +void CINDEX_list(CTABLE *table, char ***list) +{ + table->driver->Index.List(&table->conn->db, table->name, list); +} + +void CINDEX_release(CTABLE *table, void *_object) +{ + THIS->table = NULL; +} + + +/*************************************************************************** + + Index + +***************************************************************************/ + +BEGIN_METHOD_VOID(CINDEX_free) + + if (!valid_index(THIS)) + GB_SubCollectionRemove(THIS->table->indexes, THIS->name, 0); + + GB.FreeString(&THIS->name); + + GB.FreeString(&THIS->info.name); + GB.FreeString(&THIS->info.fields); + THIS->info.unique = FALSE; + +END_METHOD + + + +BEGIN_PROPERTY(CINDEX_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_fields) + + GB_ARRAY array; + char *fields; + char *name; + + fields = GB.NewZeroString(THIS->info.fields); + GB.Array.New(&array, GB_T_STRING, 0); + + name = strtok(fields, ","); + while (name) + { + *((char **)GB.Array.Add(array)) = GB.NewZeroString(name); + name = strtok(NULL, ","); + } + + GB.FreeString(&fields); + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_unique) + + GB.ReturnBoolean(THIS->info.unique); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_primary) + + GB.ReturnBoolean(THIS->info.primary); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_table) + + GB.ReturnObject(THIS->table); + +END_PROPERTY + + +GB_DESC CIndexDesc[] = +{ + GB_DECLARE("Index", sizeof(CINDEX)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_index), + + GB_METHOD("_free", NULL, CINDEX_free, NULL), + + GB_PROPERTY_READ("Name", "s", CINDEX_name), + GB_PROPERTY_READ("Unique", "b", CINDEX_unique), + GB_PROPERTY_READ("Primary", "b", CINDEX_primary), + GB_PROPERTY_READ("Fields", "String[]", CINDEX_fields), + + GB_PROPERTY_READ("Table", "Table", CINDEX_table), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + .Table.Indexes + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CINDEX_add, GB_STRING name; GB_OBJECT fields; GB_BOOLEAN unique) + + CTABLE *table = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + DB_INDEX info; + int i; + GB_ARRAY fields; + char *field; + + if (DB_CheckNameWith(name, "index", ".")) + return; + + if (check_index(table, name, FALSE)) + return; + + info.name = name; + + fields = (GB_ARRAY)VARG(fields); + q_init(); + for (i = 0; i < GB.Array.Count(fields); i++) + { + field = *(char **)GB.Array.Get(fields, i); + + if (i > 0) + q_add(","); + + q_add(table->driver->GetQuote()); + q_add(field); + q_add(table->driver->GetQuote()); + } + + info.fields = q_steal(); + info.unique = VARGOPT(unique, FALSE); + + table->driver->Index.Create(&table->conn->db, table->name, name, &info); + + GB.FreeString(&info.fields); + +END_METHOD + + +BEGIN_METHOD(CINDEX_remove, GB_STRING name) + + CTABLE *table = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + if (check_index(table, name, TRUE)) + return; + + table->driver->Index.Delete(&table->conn->db, table->name, name); + +END_METHOD + + +GB_DESC CTableIndexesDesc[] = +{ + GB_DECLARE(".Table.Indexes", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, CINDEX_add, "(Name)s(Fields)String[];[(Unique)b]"), + GB_METHOD("Remove", NULL, CINDEX_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CIndex.h b/main/lib/db/CIndex.h new file mode 100644 index 00000000..ffff27c9 --- /dev/null +++ b/main/lib/db/CIndex.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CIndex.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CINDEX_H +#define __CINDEX_H + +#include "gambas.h" +#include "gb.db.h" +#include "CTable.h" + +#ifndef __CINDEX_C +extern GB_DESC CTableIndexesDesc[]; +extern GB_DESC CIndexDesc[]; +#else + +#define THIS ((CINDEX *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CTABLE *table; + char *name; + DB_INDEX info; + } + CINDEX; + +void *CINDEX_get(CTABLE *table, const char *name); +int CINDEX_exist(CTABLE *table, const char *name); +void CINDEX_list(CTABLE *table, char ***list); +void CINDEX_release(CTABLE *table, void *_object); + +#endif + diff --git a/main/lib/db/CResult.c b/main/lib/db/CResult.c new file mode 100644 index 00000000..d639f817 --- /dev/null +++ b/main/lib/db/CResult.c @@ -0,0 +1,1011 @@ +/*************************************************************************** + + CResult.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRESULT_C + +#include "main.h" + +#include "deletemap.h" +#include "CField.h" +#include "CResultField.h" +#include "CResult.h" + +GB_CLASS CLASS_Blob; + +static CBLOB *make_blob(CRESULT *result, int field); +static void set_blob(CBLOB *_object, char *data, int length); + +static SUBCOLLECTION_DESC _fields_desc = +{ + ".Result.Fields", + (void *)CRESULTFIELD_get, + (void *)CRESULTFIELD_exist, + (void *)NULL, + (void *)CRESULTFIELD_release +}; + +//------------------------------------------------------------------------- + +static int check_result(CRESULT *_object) +{ + return (THIS->conn->db.handle == NULL); +} + + +static bool check_available(CRESULT *_object) +{ + if (!THIS->available) + { + GB.Error("Result is not available"); + return TRUE; + } + else + return FALSE; +} + + +static void init_buffer(CRESULT *_object) +{ + int i; + + if (THIS->info.nfield == 0) + return; + + GB.Alloc(POINTER(&THIS->buffer), sizeof(GB_VARIANT_VALUE) * THIS->info.nfield); + BARRAY_create(&THIS->changed, THIS->info.nfield); + BARRAY_clear_all(THIS->changed, THIS->info.nfield); + + for (i = 0; i < THIS->info.nfield; i++) + THIS->buffer[i].type = GB_T_NULL; +} + + +static void void_buffer(CRESULT *_object) +{ + int i; + + //fprintf(stderr, "void_buffer\n"); + + if (THIS->info.nfield == 0) + return; + + for (i = 0; i < THIS->info.nfield; i++) + GB.StoreVariant(NULL, &THIS->buffer[i]); + + BARRAY_clear_all(THIS->changed, THIS->info.nfield); +} + + +static void release_buffer(CRESULT *_object) +{ + if (THIS->buffer) + { + void_buffer(THIS); + GB.Free(POINTER(&THIS->buffer)); + BARRAY_delete(&THIS->changed); + } +} + + +static bool load_buffer(CRESULT *_object, int vpos) +{ + int i, ind; + int pos; + int result; + + if (vpos == THIS->pos) + return FALSE; + + DB_CurrentDatabase = &THIS->conn->db; + + if (THIS->count < 0 || THIS->conn->db.flags.no_seek) + { + if (vpos != (THIS->pos + 1)) + { + GB.Error("Result is forward only"); + return TRUE; + } + } + else + { + if (vpos < 0 || vpos >= THIS->count || THIS->info.nfield == 0) + { + THIS->pos = -1; + THIS->available = FALSE; + return TRUE; + } + } + + pos = DELETE_MAP_virtual_to_real(THIS->dmap, vpos); + + //fprintf(stderr, "Result %p: Loading real %ld\n", THIS, pos); + + void_buffer(THIS); + + if (THIS->handle) + { + result = THIS->driver->Result.Fill(&THIS->conn->db, THIS->handle, pos, THIS->buffer, (pos > 0) && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))); + + if (result == DB_ERROR) + return TRUE; + else if (result == DB_NO_DATA) + { + THIS->pos = -1; + THIS->available = FALSE; + return TRUE; + } + + if (THIS->mode == RESULT_EDIT) + { + q_init(); + + for (i = 0; i < THIS->info.nindex; i++) + { + ind = THIS->info.index[i]; + if (i > 0) q_add(" AND "); + q_add(THIS->driver->GetQuote()); + q_add(THIS->info.field[ind].name); + q_add(THIS->driver->GetQuote()); + if (THIS->buffer[ind].type == GB_T_NULL) + q_add(" IS NULL"); + else + { + q_add(" = "); + DB_FormatVariant(THIS->driver, &THIS->buffer[ind], q_add_length); + } + } + + GB.FreeString(&THIS->edit); + THIS->edit = q_steal(); + } + } + + THIS->pos = vpos; + THIS->available = TRUE; + return FALSE; +} + + +static void reload_buffer(CRESULT *_object) +{ + int pos = THIS->pos; + THIS->pos = -1; + load_buffer(THIS, pos); +} + + +static void table_release(DB_INFO *info) +{ + int i; + + if (info->table) + GB.FreeString(&info->table); + + if (info->field) + { + for (i = 0; i < info->nfield; i++) + CFIELD_free_info(&info->field[i]); + + GB.Free(POINTER(&info->field)); + } + + if (info->index) + GB.Free(POINTER(&info->index)); +} + + +static GB_TYPE get_field_type(CRESULT *_object, int field) +{ + GB_TYPE type; + + if (THIS->info.field) + type = THIS->info.field[field].type; + else + type = THIS->driver->Result.Field.Type(THIS->handle, field); + + //fprintf(stderr, "get_field_type: %d -> %ld\n", field, type); + return type; +} + + +CRESULT *DB_MakeResult(CCONNECTION *conn, int mode, char *table_temp, char *query) +{ + CRESULT *_object; + DB_RESULT res; + char *duplicate; + char *token; + const char *error = NULL; + char *arg; + char *table; + + switch (mode) + { + case RESULT_FIND: + + if (conn->driver->Exec(&conn->db, query, &res, "Query failed: &1")) + return NULL; + + break; + + case RESULT_CREATE: + + res = NULL; + break; + + case RESULT_EDIT: + + if (conn->driver->Exec(&conn->db, query, &res, "Query failed: &1")) + return NULL; + + break; + + case RESULT_DELETE: + + conn->driver->Exec(&conn->db, query, NULL, "Query failed: &1"); + return NULL; + } + + _object = GB.New(GB.FindClass("Result"), NULL, NULL); + + THIS->conn = conn; + GB.Ref(conn); + + THIS->driver = conn->driver; + THIS->available = FALSE; + THIS->mode = mode; + THIS->handle = res; + THIS->pos = -1; + THIS->dmap = NULL; + + // table must be copied because it can be a temporary string! + table = GB.NewZeroString(table_temp); + + switch (mode) + { + case RESULT_FIND: + + THIS->driver->Result.Init(THIS->handle, &THIS->info, &THIS->count); + break; + + case RESULT_CREATE: + + if (THIS->driver->Table.Init(&conn->db, table, &THIS->info)) + goto ERROR; + + THIS->count = 1; + break; + + case RESULT_EDIT: + + THIS->driver->Result.Init(THIS->handle, &THIS->info, &THIS->count); + + if (THIS->driver->Table.Init(&conn->db, table, &THIS->info)) + goto ERROR; + + if (THIS->driver->Table.Index(&conn->db, table, &THIS->info)) + { + error = "Table '&1' has no primary key"; + arg = table; + goto ERROR; + } + + break; + } + + init_buffer(THIS); + load_buffer(THIS, 0); + + GB.FreeString(&table); + return THIS; + +ERROR: + + if (!error) + { + if (strchr(table, (int)',') == NULL) + { + arg = table; + if (!THIS->driver->Table.Exist(&conn->db, table)) + error = "Unknown table: &1"; + else + error = "Cannot read information about table &1"; + } + else + { + duplicate = GB.NewZeroString(table); + token = strtok(duplicate,","); + do { + arg = token; + if (!THIS->driver->Table.Exist(&conn->db, token)) + error = "Unknown table: &1"; + else + error = "Cannot read information about table '&1'"; + } + while ((token = strtok(NULL, ".")) != NULL); + GB.FreeString(&duplicate); + } + } + + GB.Free(POINTER(&_object)); + GB.Error(error, arg); + GB.FreeString(&table); + + return NULL; +} + + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Result_free) + + release_buffer(THIS); + + if (THIS->mode != RESULT_CREATE) + THIS->driver->Result.Release(THIS->handle, &THIS->info, check_result(THIS)); + + if (THIS->mode != RESULT_FIND) + table_release(&THIS->info); + + if (THIS->edit) + GB.FreeString(&THIS->edit); + + DELETE_MAP_free(&THIS->dmap); + + GB.Unref(POINTER(&THIS->conn)); + GB.Unref(POINTER(&THIS->fields)); + +END_METHOD + + +BEGIN_PROPERTY(Result_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Max) + + GB.ReturnInteger(THIS->count - 1); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Index) + + GB.ReturnInteger(THIS->pos); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Available) + + GB.ReturnBoolean(THIS->available); + +END_PROPERTY + + +static void check_blob(CRESULT *_object, int field) +{ + GB_VARIANT val; + + //fprintf(stderr, "check_blob: %d\n", field); + + if (THIS->buffer[field].type == GB_T_NULL) + { + val.type = GB_T_VARIANT; + val.value.type = (GB_TYPE)CLASS_Blob; + val.value.value._object = make_blob(THIS, field); + + GB.StoreVariant(&val, &THIS->buffer[field]); + } +} + +BEGIN_METHOD(Result_get, GB_STRING field) + + int index; + GB_TYPE type; + + if (check_available(THIS)) + return; + + index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE); + if (index < 0) + return; + + type = get_field_type(THIS, index); + + if (type == DB_T_BLOB) + check_blob(THIS, index); + + GB.ReturnVariant(&THIS->buffer[index]); + +END_METHOD + + +BEGIN_METHOD(Result_GetAll, GB_STRING field) + + int index; + int pos; + GB_TYPE type, atype; + GB_ARRAY result; + GB_VARIANT_VALUE *val; + + index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE); + if (index < 0) + return; + + atype = type = get_field_type(THIS, index); + if (atype == DB_T_SERIAL) + atype = GB_T_LONG; + else if (atype == DB_T_BLOB) + atype = GB_T_OBJECT; + + GB.Array.New(POINTER(&result), atype, 0); + + pos = THIS->pos; + load_buffer(THIS, 0); + + while (THIS->available) + { + if (type == DB_T_BLOB) + check_blob(THIS, index); + + val = &THIS->buffer[index]; + + switch (atype) + { + case GB_T_BOOLEAN: *(char *)GB.Array.Add(result) = val->value._boolean; break; + case GB_T_INTEGER: *(int *)GB.Array.Add(result) = val->value._integer; break; + case GB_T_LONG: *(int64_t *)GB.Array.Add(result) = val->value._long; break; + case GB_T_FLOAT: *(double *)GB.Array.Add(result) = val->value._float; break; + case GB_T_DATE: *(GB_DATE_VALUE *)GB.Array.Add(result) = val->value._date; break; + + case GB_T_STRING: + if (val->type == GB_T_CSTRING) + *(char **)GB.Array.Add(result) = GB.NewString(val->value._string, strlen(val->value._string)); + else + *(char **)GB.Array.Add(result) = GB.RefString(val->value._string); + break; + + case GB_T_OBJECT: *(void **)GB.Array.Add(result) = val->value._object; GB.Ref(val->value._object); break; + } + + load_buffer(THIS, THIS->pos + 1); + } + + if (THIS->count >= 0) + load_buffer(THIS, pos); + + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(Result_put, GB_VARIANT value; GB_STRING field) + + int index; + GB_TYPE type; + + if (check_available(THIS)) + return; + + if (THIS->mode == RESULT_FIND) + { + GB.Error("Result is read-only"); + return; + } + + index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE); + if (index < 0) + return; + + type = get_field_type(THIS, index); + + if (type == DB_T_SERIAL) + { + //GB.Error("Read-only field"); + return; + } + + if (type == DB_T_BLOB) + { + check_blob(THIS, index); + + if (VARG(value).type == (GB_TYPE)CLASS_Blob) + { + CBLOB *src = VARG(value).value._object; + set_blob((CBLOB *)THIS->buffer[index].value._object, src->data, src->length); + } + else + { + GB_STRING *str = (GB_STRING *)(void *)ARG(value); + + if (GB.Conv((GB_VALUE *)(void *)ARG(value), GB_T_STRING)) + return; + + set_blob((CBLOB *)THIS->buffer[index].value._object, str->value.addr + str->value.start, str->value.len); + } + + BARRAY_set(THIS->changed, index); + return; + } + + if (VARG(value).type != GB_T_NULL && VARG(value).type != type) + { + if (GB.Conv((GB_VALUE *)(void *)ARG(value), THIS->info.field[index].type)) + { + GB.Error("Type mismatch"); + return; + } + + GB.Conv((GB_VALUE *)(void *)ARG(value), GB_T_VARIANT); + } + + GB.StoreVariant(ARG(value), &THIS->buffer[index]); + BARRAY_set(THIS->changed, index); + +END_METHOD + +#if 0 +BEGIN_METHOD(CRESULT_copy, GB_OBJECT result) + + CRESULT *result = (CRESULT *)VARG(result); + int index; + + if (THIS->mode == RESULT_FIND) + { + GB.Error("Result is read-only"); + return; + } + + for (index = 0; index < + + index = find_field(THIS, GB.ToZeroString(ARG(field))); + if (index < 0) + return; + + if (VARG(value).type != GB_T_NULL && VARG(value).type != THIS->info.types[index]) + /*{ + GB.Error("Type mismatch"); + return; + }*/ + { + if (GB.Conv((GB_VALUE *)ARG(value), THIS->info.types[index])) + return; + + GB.Conv((GB_VALUE *)ARG(value), GB_T_VARIANT); + } + + GB.StoreVariant(ARG(value), &THIS->buffer[index]); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Result_MoveFirst) + + GB.ReturnBoolean(load_buffer(THIS, 0)); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_MoveLast) + + if (THIS->count < 0) + GB.Error("Result is forward only"); + else + GB.ReturnBoolean(load_buffer(THIS, THIS->count - 1)); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_MovePrevious) + + GB.ReturnBoolean(load_buffer(THIS, THIS->pos - 1)); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_MoveNext) + + GB.ReturnBoolean(load_buffer(THIS, THIS->pos + 1)); + +END_METHOD + + +BEGIN_METHOD(Result_MoveTo, GB_INTEGER pos) + + GB.ReturnBoolean(load_buffer(THIS, VARG(pos))); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_next) + + int *pos = (int *)GB.GetEnum(); + + if (!load_buffer(THIS, *pos)) + (*pos)++; + else + GB.StopEnum(); + +END_METHOD + +BEGIN_METHOD_VOID(Result_Update) + + int i; + bool comma; + DB_INFO *info = &THIS->info; + + if (check_available(THIS)) + return; + + DB_CurrentDatabase = &THIS->conn->db; + + q_init(); + + switch(THIS->mode) + { + case RESULT_CREATE: + + if (BARRAY_is_void(THIS->changed, THIS->info.nfield)) + break; + + q_add("INSERT INTO "); + q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table, -1)); + q_add(" ( "); + + comma = FALSE; + for (i = 0; i < info->nfield; i++) + { + if (THIS->buffer[i].type == GB_T_NULL) + continue; + if (!BARRAY_test(THIS->changed, i)) + continue; + if (comma) q_add(", "); + q_add(THIS->driver->GetQuote()); + q_add(info->field[i].name); + q_add(THIS->driver->GetQuote()); + comma = TRUE; + } + + if (!comma) + { + q_add(THIS->driver->GetQuote()); + q_add(info->field[0].name); + q_add(THIS->driver->GetQuote()); + } + + q_add(" ) VALUES ( "); + + comma = FALSE; + for (i = 0; i < info->nfield; i++) + { + if (THIS->buffer[i].type == GB_T_NULL) + continue; + if (!BARRAY_test(THIS->changed, i)) + continue; + if (comma) q_add(", "); + DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length); + comma = TRUE; + } + + if (!comma) + { + DB_FormatVariant(THIS->driver, &THIS->buffer[0], q_add_length); + } + + q_add(" )"); + + if (!THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot create record: &1")) + void_buffer(THIS); + + break; + + case RESULT_EDIT: + + if (BARRAY_is_void(THIS->changed, THIS->info.nfield)) + break; + + q_add("UPDATE "); + q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table, -1)); + q_add(" SET "); + + comma = FALSE; + for (i = 0; i < info->nfield; i++) + { + if (!BARRAY_test(THIS->changed, i)) + continue; + if (comma) q_add(", "); + q_add(THIS->driver->GetQuote()); + q_add(THIS->info.field[i].name); + q_add(THIS->driver->GetQuote()); + q_add(" = "); + DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length); + comma = TRUE; + } + + q_add(" WHERE "); + q_add(THIS->edit); + + THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot modify record: &1"); + + break; + + default: + + GB.Error("Result is read-only"); + break; + } + + BARRAY_clear_all(THIS->changed, THIS->info.nfield); + +END_METHOD + + +BEGIN_METHOD(Result_Delete, GB_BOOLEAN keep) + + DB_INFO *info = &THIS->info; + int *pos; + void *save_enum; + + if (check_available(THIS)) + return; + + q_init(); + + switch(THIS->mode) + { + case RESULT_CREATE: + + void_buffer(THIS); + break; + + case RESULT_EDIT: + + q_add("DELETE FROM "); + q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table, -1)); + q_add(" WHERE "); + q_add(THIS->edit); + + THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot delete record: &1"); + + if (!VARGOPT(keep, FALSE) && THIS->count > 0) + { + DELETE_MAP_add(&THIS->dmap, THIS->pos); + THIS->count--; + reload_buffer(THIS); + + save_enum = GB.BeginEnum(THIS); + while (!GB.NextEnum()) + { + pos = (int *)GB.GetEnum(); + if (*pos > THIS->pos) + (*pos)--; + } + GB.EndEnum(save_enum); + } + + break; + + default: + + GB.Error("Result is read-only"); + break; + } + +END_METHOD + + +BEGIN_PROPERTY(Result_Fields) + + GB_SubCollectionNew(&THIS->fields, &_fields_desc, THIS); + GB.ReturnObject(THIS->fields); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + +BEGIN_PROPERTY(Result_Editable) + + GB.ReturnBoolean(THIS->mode == RESULT_CREATE || THIS->mode == RESULT_EDIT); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CResultDesc[] = +{ + GB_DECLARE("Result", sizeof(CRESULT)), GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(check_result), + + GB_METHOD("_free", NULL, Result_free, NULL), + + GB_PROPERTY_READ("Count", "i", Result_Count), + GB_PROPERTY_READ("Length", "i", Result_Count), + GB_PROPERTY_READ("Available", "b", Result_Available), + GB_PROPERTY_READ("Index", "i", Result_Index), + GB_PROPERTY_READ("Max", "i", Result_Max), + GB_PROPERTY_READ("Editable", "b", Result_Editable), + + GB_METHOD("_get", "v", Result_get, "(Field)s"), + GB_METHOD("_put", NULL, Result_put, "(Value)v(Field)s"), + GB_METHOD("_next", NULL, Result_next, NULL), + + GB_METHOD("MoveFirst", "b", Result_MoveFirst, NULL), + GB_METHOD("MoveLast", "b", Result_MoveLast, NULL), + GB_METHOD("MovePrevious", "b", Result_MovePrevious, NULL), + GB_METHOD("MoveNext", "b", Result_MoveNext, NULL), + GB_METHOD("MoveTo", "b", Result_MoveTo, "(Index)i"), + + GB_METHOD("Update", NULL, Result_Update, NULL), + GB_METHOD("Delete", NULL, Result_Delete, "[(Keep)b]"), + + GB_METHOD("All", "Array", Result_GetAll, "(Field)s"), + + GB_PROPERTY_READ("Fields", ".Result.Fields", Result_Fields), + GB_PROPERTY_READ("Connection", "Connection", Result_Connection), + + GB_END_DECLARE +}; + +//------------------------------------------------------------------------- + +static bool _convert_blob(CBLOB *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (BLOB) + { + switch (type) + { + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = BLOB->data; + conv->_string.value.start = 0; + conv->_string.value.len = BLOB->length; + conv->type = GB_T_CSTRING; + return FALSE; + + default: + return TRUE; + } + } + else + return TRUE; +} + +/*static int check_blob(CBLOB *_object) +{ + return check_result(BLOB->result) || (BLOB->result->pos != BLOB->pos); +}*/ + +static CBLOB *make_blob(CRESULT *result, int field) +{ + CBLOB *_object; + + _object = GB.New(CLASS_Blob, NULL, NULL); + + //BLOB->result = result; + //GB.Ref(result); + + //BLOB->field = field; + //BLOB->pos = result->pos; + BLOB->data = NULL; + BLOB->length = 0; + BLOB->constant = TRUE; + + if (result->handle && result->pos >= 0) + { + BLOB->constant = FALSE; + result->driver->Result.Blob(result->handle, result->pos, field, BLOB); + if (BLOB->constant) + set_blob(BLOB, BLOB->data, BLOB->length); + } + + //fprintf(stderr, "make_blob: [%d] %d (%d) -> %p\n", result->pos, field, BLOB->length, BLOB); + + //GB.UnrefKeep(POINTER(&_object), FALSE); + return BLOB; +} + +static void set_blob(CBLOB *_object, char *data, int length) +{ + if (!BLOB->constant && BLOB->data) + GB.FreeString((char **)&BLOB->data); + + if (data && length) + { + BLOB->data = GB.NewString(data, length); + BLOB->constant = FALSE; + } + + BLOB->length = length; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Blob_init) + + CLASS_Blob = GB.FindClass("Blob"); + +END_METHOD + +BEGIN_METHOD_VOID(Blob_free) + + //GB.Unref(POINTER(&BLOB->result)); + set_blob(BLOB, NULL, 0); + +END_METHOD + +/*BEGIN_PROPERTY(CBLOB_result) + + GB.ReturnObject(BLOB->result); + +END_PROPERTY*/ + +BEGIN_PROPERTY(Blob_Data) + + if (READ_PROPERTY) + { + if (BLOB->length) + GB.ReturnConstString(BLOB->data, BLOB->length); + else + GB.ReturnVoidString(); + } + else + { + set_blob(BLOB, PSTRING(), PLENGTH()); + } + +END_PROPERTY + +BEGIN_PROPERTY(Blob_Length) + + GB.ReturnInteger(BLOB->length); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CBlobDesc[] = +{ + GB_DECLARE("Blob", sizeof(CBLOB)), GB_NOT_CREATABLE(), + + //GB_HOOK_CHECK(check_blob), + + GB_STATIC_METHOD("_init", NULL, Blob_init, NULL), + GB_METHOD("_free", NULL, Blob_free, NULL), + + //GB_PROPERTY_READ("Result", "Result", CBLOB_result), + GB_PROPERTY("Data", "s", Blob_Data), + GB_PROPERTY_READ("Length", "i", Blob_Length), + GB_INTERFACE("_convert", &_convert_blob), + //GB_METHOD("_unknown", "v", CBLOB_unknown, "v"), + + GB_END_DECLARE +}; diff --git a/main/lib/db/CResult.h b/main/lib/db/CResult.h new file mode 100644 index 00000000..4295cc51 --- /dev/null +++ b/main/lib/db/CResult.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + CResult.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRESULT_H +#define __CRESULT_H + +#include "gambas.h" +#include "gb.db.h" +#include "deletemap.h" +#include "CDatabase.h" +#include "gb_barray.h" +#include "c_subcollection.h" + +#ifndef __CRESULT_C +extern GB_DESC CResultDesc[]; +extern GB_DESC CBlobDesc[]; +extern GB_CLASS CLASS_Blob; +#else + +#define THIS ((CRESULT *)_object) +#define BLOB ((CBLOB *)_object) + +#endif + +enum +{ + RESULT_FIND = 0, + RESULT_EDIT = 1, + RESULT_CREATE = 2, + RESULT_DELETE = 3 +}; + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + DB_RESULT handle; + GB_VARIANT_VALUE *buffer; + BARRAY changed; + char *edit; + DB_INFO info; + int pos; + int count; + int field; + CSUBCOLLECTION *fields; + DELETE_MAP *dmap; + unsigned available : 1; + unsigned no_seek : 1; + unsigned mode : 2; + } + CRESULT; + +#define CBLOB DB_BLOB + +CRESULT *DB_MakeResult(CCONNECTION *db, int mode, char *table, char *query); + +#endif diff --git a/main/lib/db/CResultField.c b/main/lib/db/CResultField.c new file mode 100644 index 00000000..15bc0ecf --- /dev/null +++ b/main/lib/db/CResultField.c @@ -0,0 +1,258 @@ +/*************************************************************************** + + CResultField.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRESULTFIELD_C + +#include + +#include "main.h" + +#include "CResultField.h" + + +static int valid_result_field(CRESULTFIELD *_object) +{ + return !THIS->result || !THIS->result->conn || !THIS->result->conn->db.handle; +} + + +int CRESULTFIELD_find(CRESULT *result, const char *name, bool error) +{ + int index; + char *p; + + if (!name || !*name) + return -1; + + index = strtol(name, &p, 10); + if (*name && *p == 0) + { + if (index < 0 || index >= result->info.nfield) + { + if (error) + GB.Error("Bad field index"); + index = -1; + } + } + else + { + if (result->handle) + index = result->driver->Result.Field.Index(result->handle, (char *)name, &result->conn->db); + else + { + for (index = 0; index < result->info.nfield; index++) + { + if (strcasecmp(name, result->info.field[index].name) == 0) + break; + } + } + + if (index < 0 || index >= result->info.nfield) + { + if (error) + GB.Error("Unknown field: &1", name); + index = -1; + } + } + + return index; +} + + +static CRESULTFIELD *make_result_field(CRESULT *result, int index) +{ + CRESULTFIELD *_object; + + _object = GB.New(GB.FindClass("ResultField"), NULL, NULL); + THIS->result = result; + THIS->driver = result->conn->driver; + THIS->index = index; + + return _object; +} + + +void *CRESULTFIELD_get(CRESULT *result, const char *name) +{ + int index; + + if ((intptr_t)name >> 16) + index = CRESULTFIELD_find(result, name, TRUE); + else + index = (int)(intptr_t)name; + + if (index < 0) + return NULL; + else + return make_result_field(result, index); +} + + +int CRESULTFIELD_exist(CRESULT *result, const char *name) +{ + return CRESULTFIELD_find(result, name, FALSE) >= 0; +} + +char *CRESULTFIELD_key(CRESULT *result, int index) +{ + if (result->handle) + return result->driver->Result.Field.Name(result->handle, index); + else + return result->info.field[index].name; +} + +void CRESULTFIELD_release(CRESULT *result, void *_object) +{ + THIS->result = NULL; +} + + + + +/*************************************************************************** + + ResultField + +***************************************************************************/ + +BEGIN_METHOD_VOID(CRESULTFIELD_free) + + if (!valid_result_field(THIS)) + GB_SubCollectionRemove(THIS->result->fields, CRESULTFIELD_key(THIS->result, THIS->index), 0); + +END_METHOD + + +BEGIN_PROPERTY(CRESULTFIELD_name) + + GB.ReturnNewZeroString(CRESULTFIELD_key(THIS->result, THIS->index)); + +END_PROPERTY + + +BEGIN_PROPERTY(CRESULTFIELD_type) + + CRESULT *result = THIS->result; + + if (result->handle) + GB.ReturnInteger(result->driver->Result.Field.Type(result->handle, THIS->index)); + else + GB.ReturnInteger(result->info.field[THIS->index].type); + +END_PROPERTY + + +BEGIN_PROPERTY(CRESULTFIELD_length) + + CRESULT *result = THIS->result; + + if (result->handle) + GB.ReturnInteger(result->driver->Result.Field.Length(result->handle, THIS->index)); + else + GB.ReturnInteger(result->info.field[THIS->index].length); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CRESULTFIELD_default) + + GB.Error("No default value"); + +END_PROPERTY*/ + +BEGIN_PROPERTY(CRESULTFIELD_result) + + GB.ReturnObject(THIS->result); + +END_PROPERTY + + + +GB_DESC CResultFieldDesc[] = +{ + GB_DECLARE("ResultField", sizeof(CRESULTFIELD)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_result_field), + + GB_METHOD("_free", NULL, CRESULTFIELD_free, NULL), + + GB_PROPERTY_READ("Name", "s", CRESULTFIELD_name), + GB_PROPERTY_READ("Type", "i", CRESULTFIELD_type), + GB_PROPERTY_READ("Length", "i", CRESULTFIELD_length), + + GB_PROPERTY_READ("Result", "Result", CRESULTFIELD_result), + + GB_END_DECLARE +}; + + + +/*************************************************************************** + + .Result.Fields + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_PROPERTY(CRESULTFIELD_count) + + CRESULT *result = GB_SubCollectionContainer(THIS); + GB.ReturnInteger(result->info.nfield); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CRESULTFIELD_next) + + CRESULT *result = GB_SubCollectionContainer(THIS); + int *index = (int *)GB.GetEnum(); + CRESULTFIELD *rf; + + if (*index >= result->info.nfield) + GB.StopEnum(); + else + { + rf = GB_SubCollectionGet(THIS, CRESULTFIELD_key(result, *index), 0); + (*index)++; + GB.ReturnObject(rf); + } + +END_METHOD + + + +GB_DESC CResultFieldsDesc[] = +{ + GB_DECLARE(".Result.Fields", 0), GB_INHERITS(".SubCollection"), + + GB_PROPERTY_READ("Count", "i", CRESULTFIELD_count), + //GB_PROPERTY_READ("Length", "i", CRESULTFIELD_count), + GB_METHOD("_next", "ResultField", CRESULTFIELD_next, NULL), + + GB_END_DECLARE +}; + + + diff --git a/main/lib/db/CResultField.h b/main/lib/db/CResultField.h new file mode 100644 index 00000000..aa2b6206 --- /dev/null +++ b/main/lib/db/CResultField.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + CResultField.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRESULTFIELD_H +#define __CRESULTFIELD_H + +#include "gambas.h" +#include "gb.db.h" +#include "CResult.h" + +#ifndef __CRESULTFIELD_C +extern GB_DESC CResultFieldsDesc[]; +extern GB_DESC CResultFieldDesc[]; +#else + +#define THIS ((CRESULTFIELD *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CRESULT *result; + int index; + } + CRESULTFIELD; + +void *CRESULTFIELD_get(CRESULT *result, const char *name); +int CRESULTFIELD_exist(CRESULT *result, const char *name); +int CRESULTFIELD_find(CRESULT *result, const char *name, bool error); +void CRESULTFIELD_release(CRESULT *result, void *_object); + +char *CRESULTFIELD_key(CRESULT *result, int index); + +#endif + diff --git a/main/lib/db/CTable.c b/main/lib/db/CTable.c new file mode 100644 index 00000000..ed2bcf48 --- /dev/null +++ b/main/lib/db/CTable.c @@ -0,0 +1,450 @@ +/*************************************************************************** + + CTable.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTABLE_C + +#include + +#include "main.h" + +#include "CField.h" +#include "CIndex.h" +#include "CTable.h" + + +static SUBCOLLECTION_DESC _fields_desc = +{ + ".Table.Fields", + (void *)CFIELD_get, + (void *)CFIELD_exist, + (void *)CFIELD_list, + (void *)CFIELD_release +}; + +static SUBCOLLECTION_DESC _indexes_desc = +{ + ".Table.Indexes", + (void *)CINDEX_get, + (void *)CINDEX_exist, + (void *)CINDEX_list, + (void *)CINDEX_release +}; + + +static int valid_table(CTABLE *_object) +{ + return !THIS->conn || !THIS->conn->db.handle; +} + + +static bool check_table(CCONNECTION *conn, char *name, bool must_exist) +{ + bool exist = conn->driver->Table.Exist(&conn->db, name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown table: &1", name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Table already exists: &1", name); + return TRUE; + } + } + + return FALSE; +} + + +static CTABLE *make_table(CCONNECTION *conn, const char *name, bool must_exist) +{ + CTABLE *_object; + + if (check_table(conn, (char *)name, must_exist)) + return NULL; + + //fprintf(stderr, "make_table: '%s'\n", name); + + _object = GB.New(GB.FindClass("Table"), NULL, NULL); + THIS->conn = conn; + //GB.Ref(conn); + THIS->driver = conn->driver; + THIS->name = GB.NewZeroString(name); + + //fprintf(stderr, "make_table: -> %p '%s'\n", THIS, THIS->name); + + return _object; +} + + +void *CTABLE_get(CCONNECTION *conn, const char *name) +{ + return make_table(conn, name, TRUE); +} + + +int CTABLE_exist(CCONNECTION *conn, const char *name) +{ + return conn->driver->Table.Exist(&conn->db, (char *)name); +} + + +void CTABLE_list(CCONNECTION *conn, char ***list) +{ + conn->driver->Table.List(&conn->db, list); +} + +void CTABLE_release(CCONNECTION *conn, void *_object) +{ + THIS->conn = NULL; +} + + +/*************************************************************************** + + Table + +***************************************************************************/ + +static void free_new_fields(CTABLE *_object) +{ + DB_FIELD *fp; + DB_FIELD *next; + + for (fp = THIS->new_fields; fp; fp = next) + { + next = fp->next; + CFIELD_free_info(fp); + GB.Free(POINTER(&fp)); + } + + THIS->new_fields = NULL; +} + + +BEGIN_PROPERTY(CTABLE_primary_key) + + GB_ARRAY primary; + int i, n; + char *field; + + if (THIS->create) + { + if (READ_PROPERTY) + { + if (!THIS->primary) + { + GB.ReturnNull(); + return; + } + + GB.ReturnObject(DB_StringArrayToGambasArray(THIS->primary)); + } + else + { + primary = (GB_ARRAY)VPROP(GB_OBJECT); + if (primary) + n = GB.Array.Count(primary); + else + n = 0; + + for (i = 0; i < n; i++) + { + field = *((char **)GB.Array.Get(primary, i)); + if (!CFIELD_exist(THIS, field)) + { + if (!field) + GB.Error("Void field name"); + else + GB.Error("Unknown field: &1", field); + return; + } + } + + DB_FreeStringArray(&THIS->primary); + if (n) + { + GB.NewArray(&THIS->primary, sizeof(char *), n); + for (i = 0; i < n; i++) + THIS->primary[i] = GB.NewZeroString(*((char **)GB.Array.Get(primary, i))); + } + } + } + else + { + if (READ_PROPERTY) + { + if (THIS->driver->Table.PrimaryKey(&THIS->conn->db, THIS->name, &THIS->primary)) + { + if (!GB.HasError()) + GB.Error("Unable to retrieve primary key for table: &1", THIS->name); + return; + } + + GB.ReturnObject(DB_StringArrayToGambasArray(THIS->primary)); + DB_FreeStringArray(&THIS->primary); + } + else + GB.Error("Read-only property"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_system) + + GB.ReturnBoolean(THIS->driver->Table.IsSystem(&THIS->conn->db, THIS->name)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_type) + + char *type; + + if (THIS->create) + { + if (READ_PROPERTY) + GB.ReturnString(THIS->type); + else + GB.StoreString(PROP(GB_STRING), &THIS->type); + } + else + { + if (READ_PROPERTY) + { + type = THIS->driver->Table.Type(&THIS->conn->db, THIS->name, NULL); + if (type) + GB.ReturnNewZeroString(type); + else + GB.ReturnVoidString(); + } + else + THIS->driver->Table.Type(&THIS->conn->db, THIS->name, GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTABLE_update) + + DB_FIELD *fp; + DB_FIELD *fp_serial = NULL; + + if (!THIS->new_fields) + { + GB.Error("No field"); + return; + } + + for (fp = THIS->new_fields; fp; fp = fp->next) + { + if (fp->type == DB_T_SERIAL) + { + if (THIS->conn->db.flags.no_serial) + { + GB.Error("Serial fields are not supported"); + return; + } + if (fp_serial) + { + GB.Error("Only one serial field is allowed"); + return; + } + fp_serial = fp; + } + else if (fp->type == DB_T_BLOB) + { + if (THIS->conn->db.flags.no_blob) + { + GB.Error("Blob fields are not supported"); + return; + } + } + } + + if (fp_serial) + { + if (!(THIS->primary && GB.Count(THIS->primary) == 1 && strcmp(THIS->primary[0], fp_serial->name) == 0)) + { + GB.Error("The serial field must be the primary key"); + return; + } + } + + /*if (!THIS->primary || GB.Count(THIS->primary) == 0) + { + GB.Error("No primary key"); + return; + }*/ + + if (THIS->driver->Table.Create(&THIS->conn->db, THIS->name, THIS->new_fields, THIS->primary, THIS->type)) + return; + + free_new_fields(THIS); + DB_FreeStringArray(&THIS->primary); + THIS->create = FALSE; + + //GB.Unref(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(CTABLE_free) + + //fprintf(stderr, "CTABLE_free: %p '%s'\n", THIS, THIS->name); + + if (!valid_table(THIS)) + GB_SubCollectionRemove(THIS->conn->tables, THIS->name, 0); + //GB.Unref(POINTER(&THIS->conn)); + + GB.FreeString(&THIS->name); + GB.FreeString(&THIS->type); + DB_FreeStringArray(&THIS->primary); + + GB.Unref(POINTER(&THIS->fields)); + GB.Unref(POINTER(&THIS->indexes)); + + free_new_fields(THIS); + +END_METHOD + + +BEGIN_PROPERTY(CTABLE_fields) + + GB_SubCollectionNew(&THIS->fields, &_fields_desc, THIS); + GB.ReturnObject(THIS->fields); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_indexes) + + GB_SubCollectionNew(&THIS->indexes, &_indexes_desc, THIS); + GB.ReturnObject(THIS->indexes); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + + + +GB_DESC CTableDesc[] = +{ + GB_DECLARE("Table", sizeof(CTABLE)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_table), + + GB_METHOD("_free", NULL, CTABLE_free, NULL), + + //GB_METHOD("AddField", NULL, CTABLE_add_field, "(Name)s(Type)i[(Length)i(Default)v"]) + + GB_PROPERTY_READ("Name", "s", CTABLE_name), + GB_PROPERTY_READ("System", "b", CTABLE_system), + GB_PROPERTY("PrimaryKey", "String[]", CTABLE_primary_key), + GB_PROPERTY("Type", "s", CTABLE_type), + GB_PROPERTY_READ("Connection", "Connection", CTABLE_connection), + + GB_METHOD("Update", NULL, CTABLE_update, NULL), + + GB_PROPERTY_READ("Fields", ".Table.Fields", CTABLE_fields), + GB_PROPERTY_READ("Indexes", ".Table.Indexes", CTABLE_indexes), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + .Connection.Tables + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CTABLE_add, GB_STRING name; GB_STRING type) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + CTABLE *table; + char *name = GB.ToZeroString(ARG(name)); + + if (DB_CheckNameWith(name, "table", ".")) + return; + + table = make_table(conn, name, FALSE); + if (!table) + return; + + GB_SubCollectionAdd(THIS, STRING(name), LENGTH(name), table); + + if (!MISSING(type)) + GB.StoreString(ARG(type), &table->type); + + table->create = TRUE; + GB.ReturnObject(table); + +END_METHOD + + +BEGIN_METHOD(CTABLE_remove, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + GB_SubCollectionRemove(THIS, STRING(name), LENGTH(name)); + + if (check_table(conn, name, TRUE)) + return; + + conn->driver->Table.Delete(&conn->db, name); + +END_METHOD + +GB_DESC CConnectionTablesDesc[] = +{ + GB_DECLARE(".Connection.Tables", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", "Table", CTABLE_add, "(Name)s[(Type)s]"), + GB_METHOD("Remove", NULL, CTABLE_remove, "(Name)s"), + + GB_END_DECLARE +}; diff --git a/main/lib/db/CTable.h b/main/lib/db/CTable.h new file mode 100644 index 00000000..f7235ce4 --- /dev/null +++ b/main/lib/db/CTable.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + CTable.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTABLE_H +#define __CTABLE_H + +#include "gambas.h" +#include "gb.db.h" +#include "CDatabase.h" +#include "c_subcollection.h" + +#ifndef __CTABLE_C +extern GB_DESC CTableDesc[]; +extern GB_DESC CConnectionTablesDesc[]; +//extern GB_DESC CTableFieldDesc[]; +//extern GB_DESC CTableIndexDesc[]; +#else + +#define THIS ((CTABLE *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + char *name; + char *type; + CSUBCOLLECTION *fields; + CSUBCOLLECTION *indexes; + bool create; + DB_FIELD *new_fields; /* linked list of DB_FIELD structures located in the objects stored in fields */ + char **primary; + } + CTABLE; + +void *CTABLE_get(CCONNECTION *conn, const char *key); +int CTABLE_exist(CCONNECTION *conn, const char *key); +void CTABLE_list(CCONNECTION *conn, char ***list); +void CTABLE_release(CCONNECTION *conn, void *_object); + +#endif diff --git a/main/lib/db/CUser.c b/main/lib/db/CUser.c new file mode 100644 index 00000000..7a711e8a --- /dev/null +++ b/main/lib/db/CUser.c @@ -0,0 +1,229 @@ +/*************************************************************************** + + CUser.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CUSER_C + +#include "main.h" + +#include "CUser.h" + + +static int valid_user(CUSER *_object) +{ + return !THIS->conn || !THIS->conn->db.handle; +} + +static bool check_user(CCONNECTION *conn, const char *name, bool must_exist) +{ + bool exist = conn->driver->User.Exist(&conn->db, (char *)name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown user: &1", name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("User already exists: &1", name); + return TRUE; + } + } + + return FALSE; +} + + +void *CUSER_get(CCONNECTION *conn, const char *name) +{ + CUSER *_object; + + if (check_user(conn, name, TRUE)) + return NULL; + + _object = GB.New(GB.FindClass("DatabaseUser"), NULL, NULL); + THIS->conn = conn; + THIS->driver = conn->driver; + THIS->name = GB.NewZeroString(name); + conn->driver->User.Info(&conn->db, THIS->name, &THIS->info); + return THIS; +} + + +int CUSER_exist(CCONNECTION *conn, const char *name) +{ + return conn->driver->User.Exist(&conn->db, (char *)name); +} + + +void CUSER_list(CCONNECTION *conn, char ***list) +{ + conn->driver->User.List(&conn->db, list); +} + + +void CUSER_release(CCONNECTION *conn, void *_object) +{ + THIS->conn = NULL; +} + + +/*************************************************************************** + + User + +***************************************************************************/ + +BEGIN_METHOD_VOID(CUSER_free) + + if (!valid_user(THIS)) + GB_SubCollectionRemove(THIS->conn->users, THIS->name, 0); + GB.FreeString(&THIS->name); + GB.FreeString(&THIS->info.password); + +END_METHOD + + +BEGIN_PROPERTY(CUSER_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CUSER_delete) + + THIS->conn->driver->User.Delete(&THIS->conn->db, THIS->name); + +END_METHOD + + +BEGIN_PROPERTY(CUSER_password) + + if (READ_PROPERTY) + GB.ReturnString(THIS->info.password); + else if (THIS->name) + { + GB.StoreString(PROP(GB_STRING), &THIS->info.password); + THIS->driver->User.SetPassword(&THIS->conn->db, THIS->name, THIS->info.password); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CUSER_administrator) + + GB.ReturnBoolean(THIS->info.admin); + +END_PROPERTY + + +BEGIN_PROPERTY(CUSER_connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + + +GB_DESC CUserDesc[] = +{ + GB_DECLARE("DatabaseUser", sizeof(CUSER)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_user), + + GB_METHOD("_free", NULL, CUSER_free, NULL), + + GB_METHOD("Delete", NULL, CUSER_delete, NULL), + + GB_PROPERTY_READ("Name", "s", CUSER_name), + GB_PROPERTY_READ("Administrator", "b", CUSER_administrator), + GB_PROPERTY("Password", "s", CUSER_password), + GB_PROPERTY_READ("Connection", "Connection", CUSER_connection), + + GB_END_DECLARE +}; + + + +/*************************************************************************** + + .Connection.Users + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CUSER_add, GB_STRING name; GB_STRING password; GB_BOOLEAN admin) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + DB_USER info; + + CLEAR(&info); + + if (DB_CheckNameWith(name, "user", "@%")) + return; + + if (check_user(conn, name, FALSE)) + return; + + info.admin = VARGOPT(admin, FALSE); + if (!MISSING(password)) + info.password = GB.ToZeroString(ARG(password)); + + conn->driver->User.Create(&conn->db, name, &info); + +END_METHOD + + +BEGIN_METHOD(CUSER_remove, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + GB_SubCollectionRemove(THIS, STRING(name), LENGTH(name)); + + if (check_user(conn, name, TRUE)) + return; + + conn->driver->User.Delete(&conn->db, name); + +END_METHOD + + +GB_DESC CConnectionUsersDesc[] = +{ + GB_DECLARE(".Connection.Users", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, CUSER_add, "(Name)s[(Password)s(Admin)b]"), + GB_METHOD("Remove", NULL, CUSER_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CUser.h b/main/lib/db/CUser.h new file mode 100644 index 00000000..e8cb559d --- /dev/null +++ b/main/lib/db/CUser.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CUser.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CUSER_H +#define __CUSER_H + +#include "gambas.h" +#include "gb.db.h" +#include "CConnection.h" + +#ifndef __CUSER_C +extern GB_DESC CConnectionUsersDesc[]; +extern GB_DESC CUserDesc[]; +#else + +#define THIS ((CUSER *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + char *name; + DB_USER info; + } + CUSER; + +void *CUSER_get(CCONNECTION *conn, const char *key); +int CUSER_exist(CCONNECTION *conn, const char *key); +void CUSER_list(CCONNECTION *conn, char ***list); +void CUSER_release(CCONNECTION *conn, void *_object); + +#endif + diff --git a/main/lib/db/Makefile.am b/main/lib/db/Makefile.am new file mode 100644 index 00000000..93b50614 --- /dev/null +++ b/main/lib/db/Makefile.am @@ -0,0 +1,25 @@ +COMPONENT = gb.db +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.db.la + +gb_db_la_LIBADD = @C_LIB@ +gb_db_la_LDFLAGS = -module @LD_FLAGS@ +gb_db_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_db_la_SOURCES = \ + gb.db.h gb.db.proto.h gb_barray.h \ + c_subcollection.h c_subcollection.c \ + main.h main.c \ + deletemap.h deletemap.c \ + CConnection.h CConnection.c \ + CDatabase.h CDatabase.c \ + CUser.h CUser.c \ + CTable.h CTable.c \ + CField.h CField.c \ + CIndex.h CIndex.c \ + CResult.h CResult.c \ + CResultField.h CResultField.c \ + sqlite.h sqlite.c + + diff --git a/main/lib/db/c_subcollection.c b/main/lib/db/c_subcollection.c new file mode 100644 index 00000000..127c9912 --- /dev/null +++ b/main/lib/db/c_subcollection.c @@ -0,0 +1,280 @@ +/*************************************************************************** + + c_subcollection.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_SUBCOLLECTION_C + +#include "main.h" +#include "c_subcollection.h" + + +static void free_string_array(char ***parray) +{ + int i; + char **array = *parray; + + if (!*parray) + return; + + for (i = 0; i < GB.Count(array); i++) + GB.FreeString(&array[i]); + + GB.FreeArray(parray); +} + +static void *get_from_key(CSUBCOLLECTION *_object, const char *key, int len) +{ + void *data; + char *tkey; + + if (!key || !*key) + return NULL; + + if (len <= 0) + len = strlen(key); + + //fprintf(stderr, "get_from_key: %.*s\n", len, key); + + if (GB.HashTable.Get(THIS->hash_table, key, len, &data)) + { + tkey = GB.TempString(key, len); + data = (*THIS->desc->get)(THIS->container, tkey); + if (data) + { + //fprintf(stderr, "get_from_key: insert %p '%.*s'\n", data, len, key); + GB.HashTable.Add(THIS->hash_table, key, len, data); + GB.Ref(data); + } + } + + return data; +} + +static CSUBCOLLECTION *_current = NULL; + +static void clear_one(void *data) +{ + CSUBCOLLECTION *save = _current; + + if (_current->desc->release) + (*_current->desc->release)(_current->container, data); + + //fprintf(stderr, "clear: %p\n", data); + GB.Unref(&data); + + _current = save; +} + +static void clear_subcollection(CSUBCOLLECTION *_object) +{ + _current = THIS; + GB.HashTable.Enum(THIS->hash_table, clear_one); + GB.HashTable.Free(&THIS->hash_table); +} + + +BEGIN_METHOD_VOID(CSUBCOLLECTION_free) + + //*THIS->store = NULL; + free_string_array(&THIS->list); + + clear_subcollection(THIS); + +END_METHOD + + +BEGIN_PROPERTY(CSUBCOLLECTION_count) + + /*free_string_array(&THIS->list);*/ + if (!THIS->list) + (*THIS->desc->list)(THIS->container, &THIS->list); + + if (THIS->list) + GB.ReturnInteger(GB.Count(THIS->list)); + else + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD(CSUBCOLLECTION_exist, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + + if (!key || !*key) + GB.ReturnBoolean(FALSE); + else + GB.ReturnBoolean((*THIS->desc->exist)(THIS->container, key)); + +END_METHOD + + +BEGIN_METHOD_VOID(CSUBCOLLECTION_next) + + int *pos = (int *)GB.GetEnum(); + + if (THIS->desc->list) + { + char *key = NULL; + int n; + + if (*pos == 0) + { + free_string_array(&THIS->list); + (*THIS->desc->list)(THIS->container, &THIS->list); + } + + for(;;) + { + if (THIS->list) + { + if (*pos < GB.Count(THIS->list)) + { + n = (*pos)++; + key = THIS->list[n]; + } + } + + if (!key || !*key) + { + GB.StopEnum(); + return; + } + else + { + void *table = get_from_key(THIS, key, 0); + GB.Error(NULL); + + if (table) + { + GB.ReturnObject(table); + return; + } + } + } + } + else + { + /*void *elt; + + elt = get_from_key(THIS, NULL, *pos); + (*pos)++; + if (elt) + GB_ReturnObject(elt); + else*/ + GB.StopEnum(); + } + +END_METHOD + + +BEGIN_METHOD(CSUBCOLLECTION_get, GB_STRING key) + + GB.ReturnObject(get_from_key(THIS, STRING(key), LENGTH(key))); + +END_METHOD + +BEGIN_METHOD_VOID(CSUBCOLLECTION_refresh) + + clear_subcollection(THIS); + GB.HashTable.New(&THIS->hash_table, GB_COMP_BINARY); + +END_METHOD + +GB_DESC SubCollectionDesc[] = +{ + GB_DECLARE(".SubCollection", sizeof(CSUBCOLLECTION)), + + GB_METHOD("_free", NULL, CSUBCOLLECTION_free, NULL), + + GB_PROPERTY_READ("Count", "i", CSUBCOLLECTION_count), + //GB_PROPERTY_READ("Length", "i", CSUBCOLLECTION_count), + + GB_METHOD("Exist", "b", CSUBCOLLECTION_exist, "(Key)s"), + GB_METHOD("_next", "o", CSUBCOLLECTION_next, NULL), + GB_METHOD("_get", "o", CSUBCOLLECTION_get, "(Key)s"), + GB_METHOD("Refresh", NULL, CSUBCOLLECTION_refresh, NULL), + + GB_END_DECLARE +}; + + +void GB_SubCollectionNew(CSUBCOLLECTION **subcollection, SUBCOLLECTION_DESC *desc, void *container) +{ + CSUBCOLLECTION *ob; + + if (*subcollection) + return; + + ob = GB.New(GB.FindClass(desc->klass), NULL, NULL); + + ob->container = container; + //ob->store = subcollection; + //GB_Ref(container); + ob->desc = desc; + GB.HashTable.New(&ob->hash_table, GB_COMP_BINARY); + + *subcollection = ob; + GB.Ref(ob); +} + + +void *GB_SubCollectionContainer(void *_object) +{ + return THIS->container; +} + + +void GB_SubCollectionAdd(void *_object, const char *key, int len, void *value) +{ + if (len <= 0) + len = strlen(key); + + GB.Ref(value); + GB_SubCollectionRemove(THIS, key, len); + //fprintf(stderr, "GB_SubCollectionAdd: insert %p '%.*s'\n", value, len, key); + GB.HashTable.Add(THIS->hash_table, key, len, value); +} + +void GB_SubCollectionRemove(void *_object, const char *key, int len) +{ + void *old_value; + + if (!THIS) + return; + + if (len <= 0) + len = strlen(key); + + if (!GB.HashTable.Get(THIS->hash_table, key, len, &old_value)) + { + //fprintf(stderr, "GB_SubCollectionRemove: remove %p '%.*s'\n", old_value, len, key); + GB.HashTable.Remove(THIS->hash_table, key, len); + GB.Unref(&old_value); + } +} + + +void *GB_SubCollectionGet(void *_object, const char *key, int len) +{ + return get_from_key(THIS, key, len); +} diff --git a/main/lib/db/c_subcollection.h b/main/lib/db/c_subcollection.h new file mode 100644 index 00000000..79f7ed0c --- /dev/null +++ b/main/lib/db/c_subcollection.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + c_subcollection.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_SUBCOLLECTION_H +#define __C_SUBCOLLECTION_H + +#include "gambas.h" + +/* SubCollection description */ + +typedef + struct { + char *klass; + void *(*get)(void *, const char *); + int (*exist)(void *, const char *); + void (*list)(void *, char ***); + void (*release)(void *, void *); + } + SUBCOLLECTION_DESC; + +typedef + struct { + GB_BASE object; + GB_HASHTABLE hash_table; + int mode; + void *container; + SUBCOLLECTION_DESC *desc; + char **list; + } + CSUBCOLLECTION; + +#ifndef __C_SUBCOLLECTION_C + +extern GB_DESC SubCollectionDesc[]; + +#else + +#define THIS ((CSUBCOLLECTION *)_object) + +#endif + +void GB_SubCollectionNew(CSUBCOLLECTION **subcollection, SUBCOLLECTION_DESC *desc, void *container); +void *GB_SubCollectionContainer(void *_object); +void GB_SubCollectionAdd(void *_object, const char *key, int len, void *value); +void GB_SubCollectionRemove(void *_object, const char *key, int len); +void *GB_SubCollectionGet(void *_object, const char *key, int len); + +#endif diff --git a/main/lib/db/deletemap.c b/main/lib/db/deletemap.c new file mode 100644 index 00000000..39c63eb4 --- /dev/null +++ b/main/lib/db/deletemap.c @@ -0,0 +1,183 @@ +/*************************************************************************** + + deletemap.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DELETEMAP_C + +#include "gambas.h" +#include "main.h" +#include "deletemap.h" + +//#define DEBUG_ME + +typedef + struct _DELETE_SLOT { + struct _DELETE_SLOT *prev; + struct _DELETE_SLOT *next; + int start; + int length; + } + DELETE_SLOT; + +int DELETE_MAP_virtual_to_real(DELETE_MAP *dmap, int vpos) +{ + DELETE_SLOT *slot = (DELETE_SLOT *)dmap; + int rpos = vpos; + + while (slot) + { + if (rpos < slot->start) + break; + rpos += slot->length; + slot = slot->next; + } + + #ifdef DEBUG_ME + printf("DELETE_MAP_virtual_to_real: %ld => %ld\n", vpos, rpos); + #endif + + return rpos; +} + +int DELETE_MAP_real_to_virtual(DELETE_MAP *dmap, int rpos) +{ + DELETE_SLOT *slot = (DELETE_SLOT *)dmap; + int vpos = rpos; + + while (slot) + { + if (rpos < slot->start) + break; + if (rpos < (slot->start + slot->length)) + return (-1); + vpos -= slot->length; + slot = slot->next; + } + + #ifdef DEBUG_ME + printf("DELETE_MAP_real_to_virtual: %ld => %ld\n", rpos, vpos); + #endif + + return vpos; +} + +#ifdef DEBUG_ME +static void dump(DELETE_MAP *dmap) +{ + DELETE_SLOT *slot = (DELETE_SLOT *)dmap; + + printf("dumping map %p\n", dmap); + while (slot) + { + printf("[ %ld %ld ]\n", slot->start, slot->length); + slot = slot->next; + } + printf("\n"); +} +#endif + +static void create_slot(DELETE_SLOT **pslot, int pos, DELETE_SLOT *before, DELETE_SLOT *after) +{ + GB.Alloc(POINTER(pslot), sizeof(DELETE_SLOT)); + (*pslot)->prev = before; + (*pslot)->next = after; + (*pslot)->start = pos; + (*pslot)->length = 1; + + if (before) + before->next = *pslot; + if (after) + after->prev = *pslot; +} + +static DELETE_SLOT *delete_slot(DELETE_SLOT *slot) +{ + DELETE_SLOT *nslot; + + nslot = slot->next; + + if (slot->prev) + slot->prev->next = slot->next; + if (slot->next) + slot->next->prev = slot->prev; + + GB.Free(POINTER(&slot)); + + return nslot; +} + +void DELETE_MAP_add(DELETE_MAP **dmap, int vpos) +{ + DELETE_SLOT *slot; + DELETE_SLOT *nslot; + DELETE_SLOT *bslot = NULL; + int rpos; + + if (vpos < 0) + return; + + rpos = DELETE_MAP_virtual_to_real(*dmap, vpos); + + for (slot = (DELETE_SLOT *)*dmap; slot; slot = slot->next) + { + if (rpos < slot->start) + break; + bslot = slot; + } + + create_slot(&nslot, rpos, bslot, slot); + if ((DELETE_SLOT *)*dmap == slot) + *dmap = (DELETE_MAP *)nslot; + + slot = nslot; + if (slot->prev) + slot = slot->prev; + + while (slot->next) + { + if ((slot->start + slot->length) == (slot->next->start)) + { + nslot = slot->next; + slot->length += nslot->length; + delete_slot(nslot); + } + else + slot = slot->next; + } + + #ifdef DEBUG_ME + printf("DELETE_MAP_add: %ld\n", vpos); + dump(*dmap); + #endif +} + +void DELETE_MAP_free(DELETE_MAP **dmap) +{ + DELETE_SLOT *slot; + + slot = (DELETE_SLOT *)*dmap; + while (slot) + slot = delete_slot(slot); + + *dmap = NULL; +} + diff --git a/main/lib/db/deletemap.h b/main/lib/db/deletemap.h new file mode 100644 index 00000000..326e8ceb --- /dev/null +++ b/main/lib/db/deletemap.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + deletemap.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DELETEMAP_H +#define __DELETEMAP_H + +typedef + void *DELETE_MAP; + +int DELETE_MAP_virtual_to_real(DELETE_MAP *dmap, int vpos); +int DELETE_MAP_real_to_virtual(DELETE_MAP *dmap, int rpos); +void DELETE_MAP_add(DELETE_MAP **dmap, int vpos); +void DELETE_MAP_free(DELETE_MAP **dmap); + +#endif diff --git a/main/lib/db/gb.db.component b/main/lib/db/gb.db.component new file mode 100644 index 00000000..dd66d23c --- /dev/null +++ b/main/lib/db/gb.db.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.db +Name=Database access component +Author=Benoît Minisini,Nigel Gerrard,Andrea Bortolan,Daniel Vostanikian diff --git a/main/lib/db/gb.db.h b/main/lib/db/gb.db.h new file mode 100644 index 00000000..414a7f69 --- /dev/null +++ b/main/lib/db/gb.db.h @@ -0,0 +1,273 @@ +/*************************************************************************** + + gb.db.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DB_H +#define __GB_DB_H + +#include "gambas.h" + +#define DB_INTERFACE_VERSION 1 + +typedef + struct { + char *type; + char *host; + char *port; + char *name; + char *user; + char *password; + int options; + } + DB_DESC; + +/* LIMIT position */ + +#define DB_LIMIT_NONE 0 +#define DB_LIMIT_AT_BEGIN 1 +#define DB_LIMIT_AT_END 2 + +typedef + struct { + void *handle; /* Connection handle */ + int version; /* Version of the database system */ + char *charset; /* Charset used by the database */ + void *data; /* Can be used by the driver for storing its own private data */ + int error; /* Last SQL error code raise by a query */ + int timeout; /* Connection timeout */ + int timezone; /* Timezone of dates (default to local timezone) */ + unsigned ignore_case : 1; /* If table, field and index names are case sensitive */ + struct { + unsigned no_table_type : 1; /* Tables do not have types */ + unsigned no_serial : 1; /* Serial fields are not supported */ + unsigned no_blob : 1; /* Blob fields are not supported */ + unsigned no_seek : 1; /* Cannot seek anywhere in a Result */ + unsigned no_nest : 1; /* Cannot nest transactions */ + //unsigned no_case : 1; /* table, field and index names must be converted to lower case */ + unsigned schema : 1; /* If table names can be prefixed by a schema name and a dot */ + unsigned no_collation : 1; /* No collation support at field level */ + unsigned system : 1; /* system database */ + } + flags; + struct { + const char *keyword; /* keyword for limiting the result of a query */ + int pos; /* position of 'limit' keyword */ + } + limit; + const char *db_name_char; /* These characters are allowed in a database name */ + } + DB_DATABASE; + +typedef + void *DB_RESULT; + +typedef + struct _DB_FIELD { + struct _DB_FIELD *next; + char *name; + GB_TYPE type; /* gambas field type */ + int length; /* max length for text fields (0 = no limit) */ + GB_VARIANT_VALUE def; /* default value */ + char *collation; /* field collation */ + } + DB_FIELD; + + +typedef + struct { + char *table; + int nfield; + int nindex; + DB_FIELD *field; + int *index; + } + DB_INFO; + +typedef + struct { + char *name; + char *fields; /* list of index fields separated by commas */ + int unique; /* index is unique */ + int primary; /* primary index */ + } + DB_INDEX; + +typedef + struct { + char *name; + char *password; + int admin; /* user is a superuser */ + } + DB_USER; + +typedef + struct { + GB_BASE ob; + char *data; + int length; + int constant; + } + DB_BLOB; + +typedef + void (*DB_FORMAT_CALLBACK)(const char *, int); + +typedef + void (*DB_SUBST_CALLBACK)(int, char **, int *, char); + +typedef + void (*DB_OPTIONS_CALLBACK)(const char *, GB_VALUE *); + +typedef + struct { + const char *name; + + int (*Open)(DB_DESC *desc, DB_DATABASE *db); + void (*Close)(DB_DATABASE *db); + + int (*Format)(GB_VALUE *val, DB_FORMAT_CALLBACK add); + void (*FormatBlob)(DB_BLOB *blob, DB_FORMAT_CALLBACK add); + + int (*Exec)(DB_DATABASE *db, const char *, DB_RESULT *result, const char *err); + + int (*Begin)(DB_DATABASE *db); + int (*Commit)(DB_DATABASE *db); + int (*Rollback)(DB_DATABASE *db); + GB_ARRAY (*GetCollations)(DB_DATABASE *db); + const char *(*GetQuote)(void); + int64_t (*GetLastInsertId)(DB_DATABASE *db); + + struct { + void (*Init)(DB_RESULT result, DB_INFO *info, int *count); + int (*Fill)(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next); + void (*Blob)(DB_RESULT result, int pos, int field, DB_BLOB *blob); + void (*Release)(DB_RESULT result, DB_INFO *info, bool invalid); + struct { + GB_TYPE (*Type)(DB_RESULT result, int index); + char *(*Name)(DB_RESULT result, int index); + int (*Index)(DB_RESULT result, const char *name, DB_DATABASE *db); + int (*Length)(DB_RESULT result, int index); + } + Field; + } + Result; + + struct { + int (*Exist)(DB_DATABASE *db, const char *table, const char *field); + int (*List)(DB_DATABASE *db, const char *table, char ***fields); + int (*Info)(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); + } + Field; + + struct { + int (*Init)(DB_DATABASE *db, const char *table, DB_INFO *info); + int (*Index)(DB_DATABASE *db, const char *table, DB_INFO *info); + void (*Release)(DB_DATABASE *db, DB_INFO *info); + int (*Exist)(DB_DATABASE *db, const char *table); + int (*List)(DB_DATABASE *db, char ***tables); + int (*PrimaryKey)(DB_DATABASE *db, const char *table, char ***primary); + int (*IsSystem)(DB_DATABASE *db, const char *table); + char *(*Type)(DB_DATABASE *db, const char *table, const char *type); + int (*Delete)(DB_DATABASE *db, const char *table); + int (*Create)(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *tabletype); + } + Table; + + struct { + int (*Exist)(DB_DATABASE *db, const char *table, const char *index); + int (*List)(DB_DATABASE *db, const char *table, char ***indexes); + int (*Info)(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); + int (*Delete)(DB_DATABASE *db, const char *table, const char *index); + int (*Create)(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); + } + Index; + + struct { + int (*Exist)(DB_DATABASE *db, const char *name); + int (*List)(DB_DATABASE *db, char ***names); + int (*IsSystem)(DB_DATABASE *db, const char *name); + int (*Delete)(DB_DATABASE *db, const char *name); + int (*Create)(DB_DATABASE *db, const char *name); + } + Database; + + struct { + int (*Exist)(DB_DATABASE *db, const char *user); + int (*List)(DB_DATABASE *db, char ***users); + int (*Info)(DB_DATABASE *db, const char *user, DB_USER *info); + int (*Delete)(DB_DATABASE *db, const char *user); + int (*Create)(DB_DATABASE *db, const char *user, DB_USER *info); + int (*SetPassword)(DB_DATABASE *db, const char *user, const char *password); + } + User; + } + DB_DRIVER; + +typedef + struct { + intptr_t version; + void (*Register)(DB_DRIVER *); + void (*Format)(DB_DRIVER *, GB_VALUE *, DB_FORMAT_CALLBACK); + void (*FormatVariant)(DB_DRIVER *, GB_VARIANT_VALUE *, DB_FORMAT_CALLBACK); + bool (*IsDebug)(); + void (*Debug)(const char *, const char *, ...); + void (*TryAnother)(const char *); + char *(*SubstString)(const char *, int, DB_SUBST_CALLBACK); + char *(*QuoteString)(const char *, int, char); + char *(*UnquoteString)(const char *, int, char); + DB_DATABASE *(*GetCurrentDatabase)(); + void (*GetOptions)(DB_OPTIONS_CALLBACK); + + struct { + void (*Init)(void); + void (*Add)(const char *); + void (*AddLower)(const char *); + void (*AddLength)(const char *, int); + char *(*Get)(void); + char *(*GetNew)(void); + int (*Length)(void); + } + Query; + + struct { + int (*Find)(char **, const char *); + } + StringArray; + } + DB_INTERFACE; + +/* Field datatypes */ + +#define DB_T_SERIAL ((GB_TYPE)-1) +#define DB_T_BLOB ((GB_TYPE)-2) + +// Result.Fill() return values + +#define DB_OK 0 +#define DB_ERROR 1 +#define DB_NO_DATA 2 + +/* Field Separator Character e.g. Table.field = Table.field */ + +#define FLD_SEP '.' + +#endif /* __MAIN_H */ diff --git a/main/lib/db/gb.db.proto.h b/main/lib/db/gb.db.proto.h new file mode 100644 index 00000000..02d13052 --- /dev/null +++ b/main/lib/db/gb.db.proto.h @@ -0,0 +1,148 @@ +/*************************************************************************** + + gb.db.proto.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DB_PROTO_H +#define __GB_DB_PROTO_H + +#include "gb.db.h" + +static const char *get_quote(void); +static int open_database(DB_DESC *desc, DB_DATABASE *db); +static void close_database(DB_DATABASE *db); +static GB_ARRAY get_collations(DB_DATABASE *db); +static int64_t get_last_insert_id(DB_DATABASE *db); +static int format_value(GB_VALUE *arg, DB_FORMAT_CALLBACK add); +static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add); +static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err); +static void query_init(DB_RESULT result, DB_INFO *info, int *count); +static void query_release(DB_RESULT result, DB_INFO *info, bool invalid); +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next); +static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob); +static char *field_name(DB_RESULT result, int field); +static int field_index(DB_RESULT result, const char *name, DB_DATABASE *db); +static GB_TYPE field_type(DB_RESULT result, int field); +static int field_length(DB_RESULT result, int field); +static int begin_transaction(DB_DATABASE *db); +static int commit_transaction(DB_DATABASE *db); +static int rollback_transaction(DB_DATABASE *db); +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); +static int table_init(DB_DATABASE *db, const char *table, DB_INFO *info); +static int table_index(DB_DATABASE *db, const char *table, DB_INFO *info); +static void table_release(DB_DATABASE *db, DB_INFO *info); +static int table_exist(DB_DATABASE *db, const char *table); +static int table_list(DB_DATABASE *db, char ***tables); +static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary); +static int table_is_system(DB_DATABASE *db, const char *table); +static char *table_type(DB_DATABASE *db, const char *table, const char *type); +static int table_delete(DB_DATABASE *db, const char *table); +static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *type); +static int field_exist(DB_DATABASE *db, const char *table, const char *field); +static int field_list(DB_DATABASE *db, const char *table, char ***fields); +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); +static int index_exist(DB_DATABASE *db, const char *table, const char *index); +static int index_list(DB_DATABASE *db, const char *table, char ***indexes); +static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); +static int index_delete(DB_DATABASE *db, const char *table, const char *index); +static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); +static int database_exist(DB_DATABASE *db, const char *name); +static int database_list(DB_DATABASE *db, char ***databases); +static int database_is_system(DB_DATABASE *db, const char *name); +static int database_delete(DB_DATABASE *db, const char *name); +static int database_create(DB_DATABASE *db, const char *name); +static int user_exist(DB_DATABASE *db, const char *name); +static int user_list(DB_DATABASE *db, char ***users); +static int user_info(DB_DATABASE *db, const char *name, DB_USER *info ); +static int user_delete(DB_DATABASE *db, const char *name); +static int user_create(DB_DATABASE *db, const char *name, DB_USER *info); +static int user_set_password(DB_DATABASE *db, const char *name, const char *password); + +#define DECLARE_DRIVER(_driver, _name) \ +static DB_DRIVER _driver = \ +{ \ + _name, \ + open_database, \ + close_database, \ + format_value, \ + format_blob, \ + exec_query, \ + begin_transaction, \ + commit_transaction, \ + rollback_transaction, \ + get_collations, \ + get_quote, \ + get_last_insert_id, \ + { \ + query_init, \ + query_fill, \ + blob_read, \ + query_release, \ + { \ + field_type, \ + field_name, \ + field_index, \ + field_length, \ + }, \ + }, \ + { \ + field_exist, \ + field_list, \ + field_info, \ + }, \ + { \ + table_init, \ + table_index, \ + table_release, \ + table_exist, \ + table_list, \ + table_primary_key, \ + table_is_system, \ + table_type, \ + table_delete, \ + table_create, \ + }, \ + { \ + index_exist, \ + index_list, \ + index_info, \ + index_delete, \ + index_create, \ + }, \ + { \ + database_exist, \ + database_list, \ + database_is_system, \ + database_delete, \ + database_create, \ + }, \ + { \ + user_exist, \ + user_list, \ + user_info, \ + user_delete, \ + user_create, \ + user_set_password \ + } \ +}; + + +#endif diff --git a/main/lib/db/gb.db/.component b/main/lib/db/gb.db/.component new file mode 100644 index 00000000..4e93821e --- /dev/null +++ b/main/lib/db/gb.db/.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.db +Version=3.18.0 diff --git a/main/lib/db/gb.db/.directory b/main/lib/db/gb.db/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/lib/db/gb.db/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/lib/db/gb.db/.icon.png b/main/lib/db/gb.db/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eedc7d8863854cb8646b25127e14db92ddf35ed5 GIT binary patch literal 10933 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

    ://:@:/ + + iPos = InStr(DatabaseURL, "://") + If iPos = 0 Then Goto BAD_URL + + Me.Type = Left(DatabaseURL, iPos - 1) + DatabaseURL = Mid$(DatabaseURL, iPos + 3) + + iPos = RInStr(DatabaseURL, "/") + If iPos Then + Me.Name = FromUrl(Mid$(DatabaseURL, iPos + 1)) + DatabaseURL = Left(DatabaseURL, iPos - 1) + Endif + + iPos = InStr(DatabaseURL, "@") + If iPos Then + + sIdent = Left(DatabaseURL, iPos - 1) + DatabaseURL = Mid$(DatabaseURL, iPos + 1) + + iPos = InStr(sIdent, ":") + If iPos Then + Me.Password = FromUrl(Mid$(sIdent, iPos + 1)) + Me.User = FromUrl(Left$(sIdent, iPos - 1)) + Else + Me.User = FromUrl(sIdent) + Endif + + Endif + + iPos = InStr(DatabaseURL, ":") + If iPos Then + Me.Host = FromUrl(Left$(DatabaseURL, iPos - 1)) + Me.Port = FromUrl(Mid$(DatabaseURL, iPos + 1)) + Else + Me.Host = FromUrl(DatabaseURL) + Endif + + Return + +BAD_URL: + + Error.Raise("Malformed URL") + +End + + +Private Function GetTempTableName() As String + + Dim iInd As Integer + Dim sTemp As String + + For iInd = 0 To 99 + sTemp = "_gb_temp" + If iInd Then sTemp = sTemp & "_" & iInd + If Not Me.Tables.Exist(sTemp) Then Break + Inc iInd + sTemp = "" + Next + + If Not sTemp Then Error.Raise("Cannot find a free temporary table name") + + Return sTemp + +End + +Private Sub Convert(vVal As Variant, iType As Integer) As Variant + + Select iType + Case db.Float + vVal = Val(vVal) + If TypeOf(vVal) <= gb.Float Then Return vVal + Case db.Date + vVal = Val(vVal) + If TypeOf(vVal) = gb.Date Then Return vVal + End Select + + Error.Raise("Type mismatch") + +End + +Private Function CopyTable(sTable As String) As String + + Dim sTemp As String + Dim iInd As Integer + Dim sReq As String + Dim hSrc As Table + Dim hDst As Table + Dim rSrc As Result + Dim rDst As Result + Dim hField As Field + Dim sError As String + Dim iMax As Integer + + sTemp = GetTempTableName() + + Select Case Me.Type + + Case "postgresql" + sReq = "SELECT * INTO TABLE " & sTemp & " FROM \"" & sTable & "\"" + Me.Exec(sReq) + + Case "mysql" + sReq = "CREATE TABLE " & sTemp & " SELECT * FROM `" & sTable & "`" + Me.Exec(sReq) + + 'CASE "sqlite" + ' sReq = "INSERT INTO " & sTemp & " SELECT * FROM " & sTable + + Default + + hSrc = Me.Tables[sTable] + hDst = Me.Tables.Add(sTemp, hSrc.Type) + + 'IF NOT hSrc.PrimaryKey THEN Error.Raise("No primary key") + + For Each hField In hSrc.Fields + With hField + hDst.Fields.Add(.Name, .Type, .Length, .Default) + End With + Next + + iMax = hSrc.Fields.Count - 1 + + hDst.PrimaryKey = hSrc.PrimaryKey + + hDst.Update + + Me.Begin + + rSrc = Me.Find(sTable) + rDst = Me.Create(sTemp) + + For Each rSrc + For iInd = 0 To iMax + rDst[iInd] = rSrc[iInd] + Next + rDst.Update + Next + + Me.Commit + + End Select + + Return sTemp + +Catch + + sError = Error.Text + + Select Case Me.Type + + Case "postgresql" + + Case "mysql" + Try Me.Rollback + If Me.Tables.Exist(sTable) Then + Try Me.Tables.Remove(sTemp) + Else + If Me.Tables.Exist(sTemp) Then + sReq = "RENAME TABLE " & sTemp & " TO " & sTable + Try Me.Exec(sReq) + Else + Error.Raise("Severe Error: Table has been lost!!") + Endif + Endif + + Default + Try Me.Rollback + If Me.Tables.Exist(sTable) Then + Try Me.Tables.Remove(sTemp) + Else + If Me.Tables.Exist(sTable) Then + Error.Raise("Severe Error: Table " & sTable & " has not been recreated. Data held in " & sTemp) + Endif + Endif + + End Select + + Error.Raise("Cannot copy table data: " & sError) + +End + +Private Sub ReadTemplate(hFile As File, Optional iLine As Integer) As Collection[] + + Dim cResult As New Collection[] + + Dim cTable As Collection + Dim cField As Collection + Dim cIndex As Collection + Dim sLine As String + Dim cCurrent As Collection + Dim sErr As String + Dim iPos As Integer + + For Each sLine In hFile.Lines + + Inc iLine + sLine = Trim(sLine) + If Not sLine Then Continue + If sLine Begins "#" Then Continue + + If sLine = "}" Then + If cIndex Then + cTable["Indexes"].Add(cIndex) + cIndex = Null + cCurrent = cTable + Else If cField Then + cTable["Fields"].Add(cField) + cField = Null + cCurrent = cTable + Else If cTable Then + cResult.Add(cTable) + cTable = Null + cIndex = Null + cField = Null + cCurrent = Null + Else + sErr = "Unexpected '}'" + Goto SYNTAX_ERROR + Endif + Continue + Endif + + If cIndex Then + Else If cField Then + Else If cTable Then + If sLine = "{ Index" Then + cIndex = New Collection + cCurrent = cIndex + Continue + Else If sLine = "{ Field" Then + cField = New Collection + cCurrent = cField + Continue + Endif + Else + If sLine <> "{ Table" Then + sErr = "`{ Table` expected" + Goto SYNTAX_ERROR + Endif + cTable = New Collection + cTable["Fields"] = New Collection[] + cTable["Indexes"] = New Collection[] + cCurrent = cTable + Continue + Endif + + iPos = InStr(sLine, "=") + If iPos <= 1 Then Goto SYNTAX_ERROR + ' FIXME: Calling Eval() is not very secure! + Try cCurrent[Trim(Left(sLine, iPos - 1))] = Eval(Trim(Mid$(sLine, iPos + 1))) + If Error Then Goto SYNTAX_ERROR + + Next + + If cCurrent Then Goto SYNTAX_ERROR + + Return cResult + +SYNTAX_ERROR: + + If Not sErr Then sErr = "`" & sLine & "`" + Error.Raise("Syntax error in database template at line " & CStr(iLine) & ": " & sErr) + +End + +Private Sub IsSameTable(cTable As Collection, cTable2 As Collection) As Boolean + + Dim hFile As File + Dim sTable As String + Dim sTable2 As String + + hFile = Open String For Write + + Write #hFile, cTable As Collection + sTable = Close #hFile + + hFile = Open String For Write + Write #hFile, cTable2 As Collection + sTable2 = Close #hFile + + Return sTable = sTable2 + +End + +Public Sub ApplyTemplate(Template As String) + + Dim hFile As File + Dim sLine As String + Dim cTable As Collection + Dim cField As Collection + Dim cIndex As Collection + Dim sTable As String + Dim hTable As Table + Dim iLength As Integer + Dim sColl As String + Dim aKey As String[] + Dim sCopyTable As String + Dim sName As String + Dim rTemp As Result + Dim rTable As Result + Dim cTableOrg As Collection + Dim aTables As Collection[] + + DEBUG_ME = Env["GB_DB_DEBUG_TEMPLATE"] = "1" + + If DEBUG_ME Then Error "gb.db: applying template to: "; Me.Url; "..." + + hFile = Open String Template + + Line Input #hFile, sLine + If sLine <> "# Gambas Database Template File 3.0" Then Error.Raise("Bad database template format") + + aTables = ReadTemplate(hFile, 1) + + Close #hFile + + For Each cTable In aTables + + sTable = cTable["Name"] + If Not sTable Then Continue + + If Me.Tables.Exist(sTable) Then + + 'If Not UpdateExistingTables Then Continue + + hFile = Open String For Write + WriteTable(hFile, sTable) + Seek #hFile, 0 + cTableOrg = ReadTemplate(hFile)[0] + Close #hFile + + If IsSameTable(cTable, cTableOrg) Then + Continue + Endif + + If DEBUG_ME Then Error "gb.db: updating table "; sTable + + sCopyTable = CopyTable(sTable) + Try Me.Tables.Remove(sTable) + + Else + + If DEBUG_ME Then Error "gb.db: creating table "; sTable + sCopyTable = "" + + Endif + + 'Print "create table: "; sTable;; cTable["Type"];; cTable["Fields"].Count + hTable = Me.Tables.Add(sTable, cTable["Type"]) + For Each cField In cTable["Fields"] + iLength = 0 + Try iLength = cField["Length"] + 'Print "create field: "; cField["Name"];; cField["Type"];; iLength;; cField["Default"];; cField["Collation"] + sColl = cField["Collation"] + If sColl = "default" Then sColl = "" + hTable.Fields.Add(cField["Name"], cField["Type"], iLength, cField["Default"], sColl) + Next + + 'Print "primary key: "; cTable["PrimaryKey"].Join(",") + aKey = cTable["PrimaryKey"] + If aKey.Count = 1 And If aKey[0] = "" Then aKey.Clear + If aKey.Count Then hTable.PrimaryKey = cTable["PrimaryKey"] + hTable.Update + + For Each cIndex In cTable["Indexes"] + 'Print "create index: "; cIndex["Name"];; cIndex["Fields"].Join(",");; cIndex["Unique"] + hTable.Indexes.Add(cIndex["Name"], cIndex["Fields"], cIndex["Unique"]) + Next + + If sCopyTable Then + + rTemp = Me.Find(sCopyTable) + If rTemp.Count Then + + If DEBUG_ME Then Error "gb.db: copying "; rTemp.Count; " records in the new table..." + + Me.Begin + + rTable = Me.Create(sTable) + + For Each rTemp + + For Each cField In cTable["Fields"] + sName = cField["Name"] + Try rTable[sName] = rTemp[sName] + If Error Then + Try rTable[sName] = Convert(rTemp[sName], cField["Type"]) + Endif + Next + + Try rTable.Update + + Next + + Try Me.Commit + + Endif + + Endif + + Next + +End + +Private Sub WriteTable(hFile As Stream, sTable As String) As String + + Dim hTable As Table + Dim hField As Field + Dim hIndex As Index + Dim aKey As String[] + + hTable = Me.Tables[sTable] + + Print #hFile, "{ Table" + Print #hFile, " Name="; Quote(hTable.Name) + If hTable.Type Then Print #hFile, " Type="; Quote(hTable.Type) + aKey = hTable.PrimaryKey + If aKey.Count Then Print #hFile, " PrimaryKey=[\""; aKey.Join("\",\""); "\"]" + + For Each hField In hTable.Fields + + Print #hFile, " { Field" + Print #hFile, " Name="; Quote(hField.Name) + Print #hFile, " Type="; + + Select hField.Type + Case db.Blob + Print #hFile, "db.Blob" + Case db.Boolean + Print #hFile, "db.Boolean" + Case db.Date + Print #hFile, "db.Date" + Case db.Float + Print #hFile, "db.Float" + Case db.Integer + Print #hFile, "db.Integer" + Case db.Long + Print #hFile, "db.Long" + Case db.Serial + Print #hFile, "db.Serial" + Case db.String + Print #hFile, "db.String" + If hField.Length Then Print #hFile, " Length="; hField.Length + Case Else + Error.Raise("Unknown database field type") + End Select + + If Not IsNull(hField.Default) Then + Print #hFile, " Default="; + If hField.Type = db.String Then + Print #hFile, Quote(hField.Default) + Else If hField.Type = db.Boolean Then + Print #hFile, If(hField.Default, "True", "False") + Else If hField.Type = db.Date Then + Print #hFile, "CDate(\""; CStr(hField.Default); "\")" + Else + Print #hFile, CStr(hField.Default) + Endif + Endif + + If hField.Collation Then + Print #hFile, " Collation="; Quote(hField.Collation) + Endif + + Print #hFile, " }" + + Next + + For Each hIndex In hTable.Indexes + If hIndex.Primary Then Continue + Print #hFile, " { Index " + Print #hFile, " Name="; Quote(hIndex.Name) + Print #hFile, " Unique="; If(hIndex.Unique, "True", "False") + Print #hFile, " Fields=[\""; hIndex.Fields.Join("\",\""); "\"]" + Print #hFile, " }" + Next + + Print #hFile, "}" + +End + +Public Sub GetTemplate() As String + + Dim hFile As File + Dim aTable As String[] + Dim hTable As Table + Dim sTable As String + Dim sTemplate As String + + hFile = Open String For Write + + aTable = New String[] + For Each hTable In Me.Tables + If hTable.System Then Continue + 'If sTable And If hTable.Name <> sTable Then Continue + aTable.Add(hTable.Name) + Next + + Print #hFile, TEMPLATE_MAGIC + + For Each sTable In aTable + + WriteTable(hFile, sTable) + + Next + + sTemplate = Close #hFile + Return sTemplate + +End + +Public Sub Copy() As Connection + + Dim hConn As Connection + + hConn = New Connection + hConn.Host = Me.Host + hConn.IgnoreCharset = Me.IgnoreCharset + hConn.Name = Me.Name + hConn.Password = Me.Password + hConn.Port = Me.Port + hConn.Timeout = Me.Timeout + hConn.Type = Me.Type + hConn.User = Me.User + + Return hConn + +End + +Private Function SQL_Read() As SQLRequest + + Return New SQLRequest(Me) + +End + +Private Function Url_Read() As String + + Dim sUrl As String + + sUrl = Me.Type & "://" + If Me.User Then sUrl &= Me.User & "@" + sUrl &= Me.Host &/ Me.Name + Return sUrl + +End + + +Private Function Options_Read() As Collection + + If Not $hOptions Then $hOptions = New Collection + Return $hOptions + +End + +Private Sub Options_Write(Value As Collection) + + If Not Value Then + $hOptions = Null + Else + $hOptions = Value.Copy() + Endif + +End diff --git a/main/lib/db/gb.db/.src/Connections.class b/main/lib/db/gb.db/.src/Connections.class new file mode 100644 index 00000000..01ddf6dd --- /dev/null +++ b/main/lib/db/gb.db/.src/Connections.class @@ -0,0 +1,215 @@ +' Gambas class file + +Export + +Class Desktop + +Static Property Read Count As Integer +Static Property Read Key As String + +Static Private $cConn As New Collection +Static Private $aConn As String[] +Static Private $sKey As String + +Static Public Sub Exist(Name As String) As Boolean + + If $cConn.Exist(Name) Or If Exist(".../.connection" &/ Name & ".connection") Then Return True + +End + +Static Private Sub Init() + + Dim sFile As String + + If $aConn Then Return + $aConn = New String[] + + For Each sFile In Dir(".../.connection", "*.connection") + $aConn.Add(File.BaseName(sFile)) + Next + +End + +Static Private Sub ReadConnectionFile(sName As String) As Collection + + Dim sPath As String + Dim cData As New Collection + Dim hFile As File + Dim sLine As String + Dim bInConnection As Boolean + Dim aLine As String[] + Dim sType As String + + sPath = ".../.connection" &/ sName & ".connection" + If Not Exist(sPath) Then Return + + hFile = Open sPath + + While Not Eof(hFile) + + Line Input #hFile, sLine + If Left(sLine) = "#" Then Continue + If Left(sLine) = "[" Then + bInConnection = sLine = "[Connection]" + Continue + Endif + + If bInConnection Then + + aLine = Scan(sLine, "*=*") + If aLine.Count < 2 Then Continue + + Select Case LCase(aLine[0]) + + Case "type" + sType = LCase(UnQuote(aLine[1])) + cData["type"] = sType + + Case "host" + If sType Not Begins "sqlite" Then cData["host"] = UnQuote(aLine[1]) + + Case "path" + If sType Begins "sqlite" Then cData["host"] = UnQuote(aLine[1]) + + Case "port" + cData["port"] = UnQuote(aLine[1]) + + Case "database" + cData["name"] = UnQuote(aLine[1]) + + Case "ignorecharset" + cData["ignorecharset"] = LCase(aLine[1]) = "true" + + Case "user" + cData["user"] = UnQuote(aLine[1]) + + Case "rememberpassword" + cData["rememberpassword"] = LCase(aLine[1]) = "true" + + End Select + + Endif + + Wend + + Close #hFile + + Return cData + +End + +Static Private Sub GetConnectionFrom(sName As String, cData As Collection) As Connection + + Dim hConn As Connection + Dim bPassword As Boolean + + hConn = New Connection + hConn.Type = cData["type"] + hConn.Host = cData["host"] + hConn.Port = cData["port"] + hConn.Name = cData["name"] + hConn.IgnoreCharset = cData["ignorecharset"] + hConn.User = cData["user"] + + bPassword = cData["rememberpassword"] + + If bPassword Then + If Component.IsLoaded("gb.desktop") Then + Try hConn.Password = Desktop.Passwords[Application.Name &/ sName] + If Error Then + Error "gb.db: warning: "; Application.Name &/ sName; ": unable to retrieve connection password: "; Error.Text + Endif + Endif + Endif + + Return hConn + +End + + + +Static Public Sub _get(Name As String) As Connection + + Dim hConn As Connection + Dim cData As Collection + + Init() + + If $cConn.Exist(Name) Then Return $cConn[Name] + + cData = ReadConnectionFile(Name) + If Not cData Then Return + + hConn = GetConnectionFrom(Name, cData) + $cConn[Name] = hConn + Return hConn + +End + +Static Public Sub _next() As Connection + + Init() + + If Not Enum.Index Then Enum.Index = 0 + + If Enum.Index >= $aConn.Count Then + $sKey = "" + Enum.Stop + Else + Inc Enum.Index + $sKey = $aConn[Enum.Index - 1] + Return _get($sKey) + Endif + +End + +Static Private Function Key_Read() As String + + Return $sKey + +End + +Static Public Sub Create(Name As String) As Connection + + Dim cData As Collection + Dim hConn As Connection + Dim hConnDB As Connection + Dim sPath As String + + Init() + + cData = ReadConnectionFile(Name) + If Not cData Then Return + + hConn = GetConnectionFrom(Name, cData) + + sPath = ".../.connection" &/ Name & ".template" + If Exist(sPath) Then + + hConnDB = New Connection + hConnDB.Type = hConn.Type + hConnDB.Host = hConn.Host + hConnDB.Port = hConn.Port + hConnDB.Open + If Not hConnDB.Databases.Exist(hConn.Name) Then + hConnDB.Databases.Add(hConn.Name) + Endif + hConnDB.Close + + hConn.Open + hConn.ApplyTemplate(File.Load(sPath)) + hConn.Close + + Endif + + Return hConn + +End + + +Static Private Function Count_Read() As Integer + + Init() + Return $aConn.Count + +End diff --git a/main/lib/db/gb.db/.src/Main.module b/main/lib/db/gb.db/.src/Main.module new file mode 100644 index 00000000..d28a2a96 --- /dev/null +++ b/main/lib/db/gb.db/.src/Main.module @@ -0,0 +1,15 @@ +' Gambas module file + +Public Sub Main() + + Dim hConn As New Connection + + hConn.Type = "postgresql" + hConn.Options["dbname"] = "omogen_benoit_kubuntu_sanef_fox2" + hConn.Open + + Print hConn.Tables.Count + + hConn.Close + +End diff --git a/main/lib/db/gb.db/.src/SQLRequest.class b/main/lib/db/gb.db/.src/SQLRequest.class new file mode 100644 index 00000000..d1dade8c --- /dev/null +++ b/main/lib/db/gb.db/.src/SQLRequest.class @@ -0,0 +1,176 @@ +' Gambas class file + +Export + +Private $sTable As String +Private $sWhere As String +Private $sType As String +Private $aField As String[] +Private $aOrderBy As String[] +Private $hConn As Connection +Private $sOp As String + +Public Sub _new(Connection As Connection) + + $hConn = Connection + +End + +Public Sub Select(Optional Fields As Variant, ...) As SQLRequest + + Dim aField As String[] + + $sType = "SELECT" + + If Fields Then + Try aField = Fields + If Error Then + aField = [CStr(Fields)] + aField.Insert(Param.All) + Else + aField = aField.Copy() + Endif + Endif + + $aField = aField + Return Me + +End + +Public Sub Delete() As SQLRequest + + $sType = "DELETE" + Return Me + +End + + +Public Function From(Table As String) As SQLRequest + + $sTable = Table + Return Me + +End + + +Public Function Where((Where) As String, ...) As SQLRequest + + Dim aArg As New Variant[] + + Where = Trim(Where) + If Not Where Then Return Me + + If $sOp Then + If $sWhere Then + $sWhere &= " " & $sOp + Endif + $sOp = "" + Endif + + aArg.Add(Where) + aArg.Insert(Param.All) + $sWhere &= " (" & Object.Call($hConn, "Subst", aArg) & ")" + $sOp = "AND" + Return Me + +End + +Public Function OrderBy((OrderBy) As Variant, ...) As SQLRequest + + Dim aOrderBy As String[] + + If IsNull(OrderBy) Then Return Me + + Try aOrderBy = OrderBy + If Error Then + aOrderBy = [CStr(OrderBy)] + aOrderBy.Insert(Param.All) + Endif + + $aOrderBy = aOrderBy + + Return Me + +End + +Public Function Or() As SQLRequest + + $sOp = "OR" + Return Me + +End + +Public Function And() As SQLRequest + + $sOp = "AND" + Return Me + +End + +Public Sub Get() As String + + Return _call() + +End + + +Public Function _call() As String + + Dim sReq As String + Dim I As Integer + Dim aScan As String[] + Dim sField As String + Dim bDesc As Boolean + + If Not $sTable Then Error.Raise("No table specified") + + sReq = $sType + + If $aField Then + + sReq &= " " + + For I = 0 To $aField.Max + If I Then sReq &= "," + aScan = Scan($aField[I], "* AS *") + If aScan.Count = 2 And If InStr(aScan[1], " ") = 0 Then + sReq &= $aField[I] + Else If $aField[I] = "*" Then + sReq &= "*" + Else + sReq &= $hConn.Quote($aField[I]) + Endif + Next + + Else If $sType = "SELECT" Then + + sReq &= " *" + + Endif + + sReq &= " FROM " & $hConn.Quote($sTable, True) + + If $sWhere Then sReq &= " WHERE" & $sWhere + + If $aOrderBy Then + + sReq &= " ORDER BY " + + For I = 0 To $aOrderBy.Max + If I Then sReq &= "," + sField = $aOrderBy[I] + If sField Ends " DESC" Then + sField = Left(sField, -5) + bDesc = True + Else + bDesc = False + Endif + sReq &= $hConn.Quote(sField) + If bDesc Then sReq &= " DESC" + Next + + Endif + + Return sReq + +End diff --git a/main/lib/db/gb_barray.h b/main/lib/db/gb_barray.h new file mode 100644 index 00000000..99929481 --- /dev/null +++ b/main/lib/db/gb_barray.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + gb_barray.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_BARRAY_H +#define __GB_BARRAY_H + +#include +#include "gb_alloc.h" + +typedef + int *BARRAY; + +#define BARRAY_SIZE sizeof(int) +#define BARRAY_NBITS (BARRAY_SIZE << 3) + +#define BARRAY_clear_all(_data, _size) memset((_data), 0, ((_size) + BARRAY_NBITS - 1) / BARRAY_NBITS * BARRAY_SIZE) + +#define BARRAY_create(_pdata, _size) GB.Alloc(POINTER((_pdata)), ((_size) + BARRAY_NBITS - 1) / BARRAY_NBITS * BARRAY_SIZE) + +#define BARRAY_delete(_pdata) GB.Free(POINTER((_pdata))) + +#define BARRAY_set(_data, _bit) (_data[(_bit) / BARRAY_NBITS] |= (1 << ((_bit) & (BARRAY_NBITS - 1)))) +#define BARRAY_clear(_data, _bit) ((_data)[(_bit) / BARRAY_NBITS] &= ~(1 << ((_bit) & (BARRAY_NBITS - 1)))) +#define BARRAY_invert(_data, _bit) ((_data)[(_bit) / BARRAY_NBITS] ^= (1 << ((_bit) & (BARRAY_NBITS - 1)))) + +#define BARRAY_test(_data, _bit) (((_data)[(_bit) / BARRAY_NBITS] & (1 << ((_bit) & (BARRAY_NBITS - 1)))) != 0) + +#define BARRAY_is_void(_data, _size) \ +({ \ + int i, v = 0; \ + int size = ((_size) + BARRAY_NBITS - 1) / BARRAY_NBITS; \ + for (i = 0; !v && i < size; i++) \ + v |= (_data)[i]; \ + v == 0; \ +}) + +#endif diff --git a/main/lib/db/main.c b/main/lib/db/main.c new file mode 100644 index 00000000..3c1af43a --- /dev/null +++ b/main/lib/db/main.c @@ -0,0 +1,767 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" + +#include "c_subcollection.h" +#include "CConnection.h" +#include "CDatabase.h" +#include "CUser.h" +#include "CTable.h" +#include "CField.h" +#include "CIndex.h" +#include "CResult.h" +#include "CResultField.h" + +#include "sqlite.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; + + +DB_DATABASE *DB_CurrentDatabase = NULL; + +static DB_DRIVER *_drivers[MAX_DRIVER]; +static int _drivers_count = 0; +static char *_query = NULL; + +#define TEMP_MAX 64 +static char _temp[TEMP_MAX]; +static int _temp_len; + +static bool _debug = FALSE; +static const char *_try_another = NULL; + +static GB_COLLECTION _options; + + +int DB_CheckNameWith(const char *name, const char *msg, const char *more) +{ + unsigned char c; + const char *p = name; + + if (!name || !*name) + { + GB.Error("Void &1 name", msg); + return TRUE; + } + + while ((c = *p++)) + { + if ((c >= 'A' && c <='Z') || (c >= 'a' && c <='z') || (c >= '0' && c <= '9') || c == '_') + continue; + + if (more && index(more, c)) + continue; + + GB.Error("Bad &1 name: &2", msg, name); + return TRUE; + } + + return FALSE; +} + + +void DB_FreeStringArray(char ***parray) +{ + int i; + char **array = *parray; + + if (!*parray) + return; + + for (i = 0; i < GB.Count(array); i++) + GB.FreeString(&array[i]); + + GB.FreeArray(parray); +} + +GB_ARRAY DB_StringArrayToGambasArray(char **array) +{ + GB_ARRAY garray; + int i, n; + char *str; + + n = GB.Count(array); + + GB.Array.New(&garray, GB_T_STRING, n); + + for (i = 0; i < n; i++) + { + str = GB.NewZeroString(array[i]); + *((char **)GB.Array.Get(garray, i)) = str; + } + + return garray; +} + +int DB_FindStringArray(char **array, const char *elt) +{ + int i; + + for (i = 0; i < GB.Count(array); i++) + { + if (!strcasecmp(elt, array[i])) + return i; + } + + return -1; +} + + +static void DB_Register(DB_DRIVER *driver) +{ + if (_drivers_count >= MAX_DRIVER) + return; + + _drivers[_drivers_count] = driver; + _drivers_count++; +} + + +void DB_TryAnother(const char *driver) +{ + _try_another = driver; +} + +static DB_DRIVER *DB_GetDriver(const char *type) +{ + int i; + char comp[type ? strlen(type) + 8 : 1]; + + if (!type) + { + GB.Error("Driver name missing"); + return NULL; + } + + strcpy(comp, "gb.db."); + strcat(comp, type); + + GB.Component.Load(comp); + GB.Error(NULL); // reset the error flag; + + for (i = 0; i < _drivers_count; i++) + { + if (strcasecmp(_drivers[i]->name, type) == 0) + return _drivers[i]; + } + + GB.Error("Cannot find driver for database: &1", type); + return NULL; +} + + +bool DB_Open(DB_DESC *desc, DB_DRIVER **driver, DB_DATABASE *db, GB_COLLECTION options) +{ + DB_DRIVER *d; + int res; + int timeout; + const char *type = desc->type; + + _options = options; + + timeout = db->timeout; + CLEAR(db); + db->timeout = timeout; + + for(;;) + { + d = DB_GetDriver(type); + if (!d) + return TRUE; + + *driver = d; + + _try_another = NULL; + res = (*d->Open)(desc, db); + if (!res) + return FALSE; + + if (!_try_another) + return TRUE; + + type = _try_another; + } +} + + +void DB_GetOptions(DB_OPTIONS_CALLBACK cb) +{ + GB_COLLECTION_ITER iter; + GB_VALUE value; + char *key; + int len; + + GB.Collection.Enum(_options, &iter, NULL, NULL, NULL); + for(;;) + { + if (GB.Collection.Enum(_options, &iter, (GB_VARIANT *)&value, &key, &len)) + break; + + GB.BorrowValue(&value); + (*cb)(GB.TempString(key, len), &value); + GB.ReleaseValue(&value); + } +} + + +void DB_Format(DB_DRIVER *driver, GB_VALUE *arg, DB_FORMAT_CALLBACK add) +{ + static char buffer[32]; + + char *s; + int l; + int i; + + if (arg->type == GB_T_VARIANT) + GB.Conv(arg, ((GB_VARIANT *)arg)->value.type); + + if (arg->type == (GB_TYPE)CLASS_Blob) + { + (*driver->FormatBlob)((DB_BLOB *)(((GB_OBJECT *)arg)->value), add); + return; + } + + if ((arg->type == GB_T_DATE && arg->_date.value.date == 0 && arg->_date.value.time == 0) + || (arg->type == GB_T_STRING && arg->_string.value.len == 0) + || (arg->type == GB_T_NULL)) + { + add("NULL", 4); + return; + } + + if (!(*driver->Format)(arg, add)) + { + switch (arg->type) + { + case GB_T_BOOLEAN: + + if (VALUE((GB_BOOLEAN *)arg)) + add("TRUE", 4); + else + add("FALSE", 5); + + return; + + case GB_T_BYTE: + case GB_T_SHORT: + case GB_T_INTEGER: + + l = sprintf(buffer, "%d", VALUE((GB_INTEGER *)arg)); + add(buffer, l); + return; + + case GB_T_LONG: + + l = sprintf(buffer, "%" PRId64, VALUE((GB_LONG *)arg)); + add(buffer, l); + return; + + case GB_T_FLOAT: + + GB.NumberToString(FALSE, VALUE((GB_FLOAT *)arg), NULL, &s, &l); + add(s, l); + + return; + + case GB_T_STRING: + case GB_T_CSTRING: + + s = VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start; + l = VALUE((GB_STRING *)arg).len; + + add("'", 1); + + for (i = 0; i < l; i++, s++) + { + add(s, 1); + if (*s == '\'' || *s == '\\') + add(s, 1); + } + + add("'", 1); + return; + + default: + fprintf(stderr, "gb.db: DB_Format: unsupported datatype: %d\n", (int)arg->type); + return; + } + } +} + + +void DB_FormatVariant(DB_DRIVER *driver, GB_VARIANT_VALUE *arg, DB_FORMAT_CALLBACK add) +{ + GB_VALUE value; + + value.type = arg->type; + + switch(arg->type) + { + case GB_T_NULL: + break; + + case GB_T_STRING: + case GB_T_CSTRING: + { + GB_STRING *val = (GB_STRING *)(void *)&value; + val->value.addr = arg->value._string; + val->value.start = 0; + if (arg->type == GB_T_STRING) + val->value.len = GB.StringLength(arg->value._string); + else + val->value.len = strlen(arg->value._string); + } + break; + + default: + value.type = GB_T_VARIANT; + value._variant.value = *arg; + GB.Conv(&value, arg->type); + break; + } + + DB_Format(driver, &value, add); +} + + +static int query_narg; +static GB_VALUE *query_arg; +static DB_DRIVER *query_driver; +static DB_DATABASE *query_db; + +static void mq_add_param(int index, char before, char after) +{ + GB_VALUE *arg; + + if (index < 1 || index > query_narg) + return; + + arg = &query_arg[index - 1]; + + //fprintf(stderr, "mq_add_param: %d %c %c\n", index, before, after); + + if (before == '[' && after == ']') + { + GB.SubstStringUnquote(); + if (!GB.Conv(arg, GB_T_STRING)) + GB.SubstAddCallback(DB_GetQuotedTable(query_driver, query_db, arg->_string.value.addr + arg->_string.value.start, arg->_string.value.len), -1); + } + else if ((before == '\'' || before == '`') && after == before) + { + GB.SubstStringUnquote(); + if (!GB.Conv(arg, GB_T_STRING)) + { + GB.SubstAddCallback(query_driver->GetQuote(), -1); + GB.SubstAddCallback(arg->_string.value.addr + arg->_string.value.start, arg->_string.value.len); + GB.SubstAddCallback(query_driver->GetQuote(), -1); + } + } + else + DB_Format(query_driver, arg, (DB_FORMAT_CALLBACK)GB.SubstAddCallback); +} + +char *DB_MakeQuery(DB_DRIVER *driver, DB_DATABASE *db, const char *pattern, int len, int narg, GB_VALUE *arg) +{ + char *query; + + query_narg = narg; + query_arg = arg; + query_driver = driver; + query_db = db; + + if (narg == 0) + query = GB.TempString(pattern, len); + else + query = GB.SubstStringAdd(pattern, len, mq_add_param); + + if (!query || *query == 0) + { + GB.Error("Void query"); + return NULL; + } + else + return query; +} + + +void q_init(void) +{ + GB.FreeString(&_query); + _query = NULL; + _temp_len = 0; +} + +static void q_dump_temp(void) +{ + if (!_temp_len) + return; + + _query = GB.AddString(_query, _temp, _temp_len); + _temp_len = 0; +} + +void q_add_length(const char *str, int len) +{ + if (!str) + return; + + if ((_temp_len + len) > TEMP_MAX) + q_dump_temp(); + + if (len > TEMP_MAX) + _query = GB.AddString(_query, str, len); + else + { + memcpy(&_temp[_temp_len], str, len); + _temp_len += len; + } +} + +void q_add(const char *str) +{ + if (str) + q_add_length(str, strlen(str)); +} + +void q_add_lower(const char *str) +{ + int i, len; + char *lstr; + + if (!str) + return; + + len = strlen(str); + + if (len <= 0) + return; + + lstr = GB.TempString(str, len); + for (i = 0; i < len; i++) + lstr[i] = GB.ToLower(lstr[i]); + + q_add_length(lstr, len); +} + +char *q_get(void) +{ + q_dump_temp(); + return _query; +} + +char *q_steal(void) +{ + char *s; + q_dump_temp(); + s = _query; + _query = NULL; + return s; +} + +int q_length(void) +{ + return GB.StringLength(_query) + _temp_len; +} + +void DB_SetDebug(int debug) +{ + _debug = debug; +} + +int DB_IsDebug(void) +{ + return _debug; +} + +void DB_Debug(const char *prefix, const char *msg, ...) +{ + va_list args; + struct timeval tv; + GB_DATE_SERIAL *date; + GB_DATE val; + + if (!_debug) + return; + + if (gettimeofday(&tv, NULL) == 0) + { + GB.MakeDateFromTime((time_t)tv.tv_sec, tv.tv_usec, &val); + date = GB.SplitDate(&val); + fprintf(stderr, "%04d-%02d-%02d %02d:%02d:%02d.%03d ", date->year, date->month, date->day, date->hour, date->min, date->sec, date->msec); + } + + fprintf(stderr, "%s: ", prefix); + + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + + fputc('\n', stderr); + fflush(stderr); +} + + +static char *_quote; +DB_SUBST_CALLBACK _quote_cb; + +static void ss_get_param(int index, char **str, int *len) +{ + (*_quote_cb)(index, str, len, _quote[index]); +} + +char *DB_SubstString(const char *pattern, int len_pattern, DB_SUBST_CALLBACK add) +{ + int i; + unsigned char last_c, c; + int n; + char quote[20] = {0}; + + c = 0; + + len_pattern--; + for (i = 0; i < len_pattern; i++) + { + last_c = c; + c = pattern[i]; + + if (c == '&') + { + c = pattern[++i]; + if (c == '&') + continue; + if (isdigit(c)) + { + n = c - '0'; + c = pattern[++i]; + if (isdigit(c)) + { + n = n * 10 + c - '0'; + i++; + } + quote[n] = last_c; + } + } + } + + _quote_cb = add; + _quote = quote; + + return GB.SubstString(pattern, len_pattern, ss_get_param); +} + +char *DB_QuoteString(const char *str, int len, char quote) +{ + char *res, *p, c; + int len_res; + int i; + + len_res = len; + for (i = 0; i < len; i++) + { + if (str[i] == quote) + len_res++; + } + + res = GB.TempString(NULL, len_res); + + p = res; + for (i = 0; i < len; i++) + { + c = str[i]; + *p++ = c; + if (c == quote || c == '\\') + *p++ = c; + } + *p = 0; + + return res; +} + +char *DB_UnquoteString(const char *str, int len, char quote) +{ + char *res, *p, c; + int len_res; + int i; + + if (len >= 2 && str[0] == quote && str[len - 1] == quote) + { + str++; + len -= 2; + } + + if (!len) + return ""; + + len_res = len; + for (i = 0; i < (len - 1); i++) + { + if ((str[i] == quote && str[i + 1] == quote) || str[i] == '\\') + { + len_res--; + i++; + } + } + + res = GB.TempString(NULL, len_res); + + p = res; + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == quote && (i + 1) < len && str[i + 1] == quote) + i++; + else if (c == '\\' && (i + 1) < len) + { + c = str[i + 1]; + i++; + } + + *p++ = c; + } + *p = 0; + + return res; +} + +DB_DATABASE *DB_GetCurrent() +{ + return DB_CurrentDatabase; +} + +char *DB_GetQuotedTable(DB_DRIVER *driver, DB_DATABASE *db, const char *table, int len) +{ + char *point = NULL; + char *res; + const char *quote; + + if (!table) + return ""; + + if (len < 0) + len = strlen(table); + + if (len == 0) + return ""; + + if (db->flags.schema) + point = index(table, '.'); + + quote = (*driver->GetQuote)(); + + if (!point) + { + res = GB.TempString(NULL, len + 2); + sprintf(res, "%s%.*s%s", quote, len, table, quote); + } + else + { + int len_schema = (int)(point - table); + res = GB.TempString(NULL, len + 4); + sprintf(res, "%s%.*s%s.%s%.*s%s", quote, len_schema, table, quote, quote, len - len_schema - 1, point + 1, quote); + } + + return res; +} + + +GB_DESC *GB_CLASSES [] EXPORT = +{ + SubCollectionDesc, + CIndexDesc, + CFieldDesc, + CTableFieldsDesc, + CTableIndexesDesc, + CTableDesc, + CUserDesc, + CDatabaseDesc, + CConnectionUsersDesc, + CConnectionDatabasesDesc, + CConnectionTablesDesc, + CConnectionDesc, + CDBDesc, + CBlobDesc, + CResultFieldDesc, + CResultFieldsDesc, + CResultDesc, + NULL +}; + +void *GB_DB_1[] EXPORT = { + + (void *)1, + + (void *)DB_Register, + (void *)DB_Format, + (void *)DB_FormatVariant, + (void *)DB_IsDebug, + (void *)DB_Debug, + (void *)DB_TryAnother, + (void *)DB_SubstString, + (void *)DB_QuoteString, + (void *)DB_UnquoteString, + (void *)DB_GetCurrent, + (void *)DB_GetOptions, + + (void *)q_init, + (void *)q_add, + (void *)q_add_lower, + (void *)q_add_length, + (void *)q_get, + (void *)q_steal, + (void *)q_length, + + (void *)DB_FindStringArray, + + NULL +}; + + +int EXPORT GB_INIT(void) +{ + char *env = getenv("GB_DB_DEBUG"); + + if (env && strcmp(env, "0")) + DB_SetDebug(TRUE); + + DB_Register(&DB_sqlite_pseudo_driver); + return 0; +} + + +void EXPORT GB_EXIT() +{ + GB.FreeString(&_query); +} + diff --git a/main/lib/db/main.h b/main/lib/db/main.h new file mode 100644 index 00000000..8901fe41 --- /dev/null +++ b/main/lib/db/main.h @@ -0,0 +1,65 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.db.h" +#include "c_subcollection.h" +#include "CResult.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern DB_DATABASE *DB_CurrentDatabase; +#endif + +#define MAX_DRIVER 8 + +bool DB_Open(DB_DESC *desc, DB_DRIVER **driver, DB_DATABASE *db, GB_COLLECTION options); +char *DB_MakeQuery(DB_DRIVER *driver, DB_DATABASE *db, const char *pattern, int len, int narg, GB_VALUE *arg); +void DB_Format(DB_DRIVER *driver, GB_VALUE *arg, DB_FORMAT_CALLBACK func); +void DB_FormatVariant(DB_DRIVER *driver, GB_VARIANT_VALUE *arg, DB_FORMAT_CALLBACK func); +char *DB_GetQuotedTable(DB_DRIVER *driver, DB_DATABASE *db, const char *table, int len_table); + +void DB_LowerString(char *s); +int DB_CheckNameWith(const char *name, const char *msg, const char *more); +#define DB_CheckName(_name, _msg) DB_CheckNameWith(_name, _msg, NULL) +void DB_FreeStringArray(char ***parray); +GB_ARRAY DB_StringArrayToGambasArray(char **array); +int DB_FindStringArray(char **array, const char *elt); +void DB_SetDebug(int debug); +int DB_IsDebug(void); +void DB_Debug(const char *prefix, const char *msg, ...); +void DB_TryAnother(const char *); + +void q_init(void); +void q_add(const char *str); +void q_add_lower(const char *str); +void q_add_length(const char *str, int len); +char *q_get(void); +char *q_steal(void); +int q_length(void); + +#endif /* __MAIN_H */ diff --git a/main/lib/db/sqlite.c b/main/lib/db/sqlite.c new file mode 100644 index 00000000..ab6026a3 --- /dev/null +++ b/main/lib/db/sqlite.c @@ -0,0 +1,213 @@ +/*************************************************************************** + + sqlite.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __SQLITE_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + + +/* Internal function to check whether a file is a sqlite database file */ + +static bool is_sqlite2_database(const char *filename) +{ + FILE* fp; + bool res; + char magic_text[48]; + + fp = fopen(filename, "r"); + if (!fp) + return FALSE; + + res = fread(magic_text, 1, 47, fp) == 47; + fclose(fp); + + if (!res) + return FALSE; + + magic_text[47] = '\0'; + + if (strcmp(magic_text, "** This file contains an SQLite 2.1 database **")) + return FALSE; + + return TRUE; +} + +static bool is_sqlite3_database(const char *filename) +{ + FILE *fp; + bool res; + char magic_text[16]; + + fp = fopen(filename, "r"); + if (!fp) + return FALSE; + + res = fread(magic_text, 1, 15, fp) == 15; + fclose(fp); + + if (!res) + return FALSE; + + magic_text[15] = '\0'; + + if (strcmp(magic_text, "SQLite format 3")) + return FALSE; + + return TRUE; +} + +static bool is_database_file(const char *filename) +{ + return is_sqlite3_database(filename) || is_sqlite2_database(filename); +} + +static char *find_database(const char *name, const char *hostName) +{ + char *dbhome = NULL; + char *fullpath = NULL; + char *path; + + /* Does Name includes fullpath */ + if (*name == '/') + { + if (is_database_file(name)) + return (char *)name; + } + + /* Hostname contains home area */ + fullpath = GB.NewZeroString(hostName); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + + path = GB.FileName(fullpath, GB.StringLength(fullpath)); + GB.FreeString(&fullpath); + + if (is_database_file(path)) + return path; + + /* Check the GAMBAS_SQLITE_DBHOME setting */ + dbhome = getenv("GAMBAS_SQLITE_DBHOME"); + + if (dbhome != NULL) + { + fullpath = GB.NewZeroString(dbhome); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + + path = GB.FileName(fullpath, GB.StringLength(fullpath)); + GB.FreeString(&fullpath); + + if (is_database_file(path)) + return path; + } + + fullpath = GB.NewZeroString(GB.TempDir()); + fullpath = GB.AddString(fullpath, "/sqlite/", 0); + fullpath = GB.AddString(fullpath, name, 0); + GB.FreeStringLater(fullpath); + + if (is_database_file(fullpath)) + return fullpath; + + return NULL; +} + +//------------------------------------------------------------------------- + +static int open_database(DB_DESC *desc, DB_DATABASE * db) +{ + char *host; + const char *db_fullpath = NULL; + bool ver2 = FALSE; + + if (!desc->name) // memory database + goto __SQLITE; + + host = desc->host; + if (!host) + host = ""; + + db_fullpath = find_database(desc->name, host); + if (!db_fullpath) + { + GB.Error("Unable to locate database `&1` in `&2`", desc->name, host); + return TRUE; + } + + ver2 = is_sqlite2_database(db_fullpath); + + if (ver2) + goto __SQLITE2; + else + goto __SQLITE3; + +__SQLITE: + + GB.Component.Load("gb.db.sqlite3"); + GB.Error(NULL); + + if (GB.Component.Exist("gb.db.sqlite3")) + goto __SQLITE3; + else + goto __SQLITE2; + +__SQLITE2: + DB_TryAnother("sqlite2"); + return TRUE; + +__SQLITE3: + DB_TryAnother("sqlite3"); + return TRUE; +} + + +//------------------------------------------------------------------------- + +static int database_is_system(DB_DATABASE * db, const char *name) +{ + return FALSE; +} + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +DB_DRIVER DB_sqlite_pseudo_driver = +{ + .name = "sqlite", + .Open = open_database, + .Database.IsSystem = database_is_system +}; + diff --git a/main/lib/db/sqlite.h b/main/lib/db/sqlite.h new file mode 100644 index 00000000..277e53db --- /dev/null +++ b/main/lib/db/sqlite.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + sqlite.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SQLITE_H +#define __SQLITE_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.db.h" + +#ifndef __SQLITE_C +extern DB_DRIVER DB_sqlite_pseudo_driver; +#endif + +#endif diff --git a/main/lib/debug/CDebug.c b/main/lib/debug/CDebug.c new file mode 100644 index 00000000..5cbf2be3 --- /dev/null +++ b/main/lib/debug/CDebug.c @@ -0,0 +1,323 @@ +/*************************************************************************** + + CDebug.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDEBUG_C + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "gb_limit.h" +#include "gb.debug.h" +#include "debug.h" +#include "CDebug.h" + +/*#define DEBUG*/ + +DECLARE_EVENT(EVENT_Read); + +static int _started = FALSE; +static int _fdr = -1; +static int _fdw = -1; +static CDEBUG *_debug_object = NULL; + +#define BUFFER_SIZE DEBUG_OUTPUT_MAX_SIZE +static char *_buffer = NULL; +static int _buffer_left; + +#if 0 +static void callback_read(int fd, int type, intptr_t param) +{ + int n; + + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) | O_NONBLOCK); + + n = read(_fdr, _buffer, 4096); + if (n > 0) + { + GB.Raise(_debug_object, EVENT_Read, 1, GB_T_STRING, _buffer, n); + } + else + { + if (n == 0 || (errno != EINTR && errno != EAGAIN)) + GB.Watch(fd, GB_WATCH_NONE, (void *)callback_read, 0); + } + + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) & ~O_NONBLOCK); +} +#endif + +static void callback_read(int fd, int type, intptr_t param) +{ + int n, i, p; + + for(;;) + { + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) | O_NONBLOCK); + + if (_buffer_left) + { + n = read(_fdr, &_buffer[_buffer_left], BUFFER_SIZE - _buffer_left); + if (n > 0) + { + n += _buffer_left; + _buffer_left = 0; + } + } + else + n = read(_fdr, _buffer, BUFFER_SIZE); + + if (n <= 0) + { + if (n == 0 || (errno != EINTR && errno != EAGAIN)) + GB.Watch(fd, GB_WATCH_NONE, (void *)callback_read, 0); + + break; // try again + } + + p = 0; + + for (i = 0; i < n; i++) + { + if (_buffer[i] == '\n') + { + /*fprintf(stderr, "CDEBUG_read: <<< %.*s >>>\n", i - p, &_buffer[p]);*/ + GB.Raise(_debug_object, EVENT_Read, 1, GB_T_STRING, i <= p ? NULL : &_buffer[p], i - p + 1); + if (!_buffer) + break; + p = i + 1; + } + } + + if (!_buffer) + break; + + if (p == 0 && n >= BUFFER_SIZE) + { + GB.Raise(_debug_object, EVENT_Read, 1, GB_T_STRING, _buffer, BUFFER_SIZE); + if (!_buffer) + break; + _buffer_left = 0; + } + else + { + _buffer_left = n - p; + if (p && n > p) + memmove(_buffer, &_buffer[p], _buffer_left); + } + } + + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) & ~O_NONBLOCK); +} + + +static char *fifo_path(char *path, const char *direction) +{ + sprintf(path, DEBUG_FIFO_PATTERN, getuid(), getpid(), direction ? direction : ""); + return path; +} + +static void open_write_fifo() +{ + int i; + char path[PATH_MAX]; + + fifo_path(path, "out"); + + for(i = 0; i < 4; i++) + { + _fdw = open(path, O_WRONLY); + if (_fdw >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; + usleep(20000); + } + + if (_fdw < 0) + { + GB.Error("Unable to open fifo: &1: &2", path, strerror(errno)); + return; + } +} + +BEGIN_METHOD_VOID(Debug_Begin) + + char path[PATH_MAX]; + + signal(SIGPIPE, SIG_IGN); + + unlink(fifo_path(path, "in")); + if (mkfifo(path, 0600)) + { + GB.Error("Cannot create input fifo in /tmp: &1", strerror(errno)); + return; + } + + unlink(fifo_path(path, "out")); + if (mkfifo(path, 0600)) + { + GB.Error("Cannot create output fifo in /tmp: &1", strerror(errno)); + return; + } + + GB.ReturnNewZeroString(fifo_path(path, "")); + +END_METHOD + + +BEGIN_METHOD_VOID(Debug_Start) + + char path[DEBUG_FIFO_PATH_MAX]; + + if (_started) + return; + + _fdr = open(fifo_path(path, "in"), O_RDONLY | O_NONBLOCK); + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) & ~O_NONBLOCK); + + _debug_object = GB.New(GB.FindClass("Debug"), "Debug", NULL); + GB.Ref(_debug_object); + + GB.Alloc(POINTER(&_buffer), BUFFER_SIZE); + _buffer_left = 0; + + GB.Watch(_fdr, GB_WATCH_READ, (void *)callback_read, 0); + + _started = TRUE; + +END_METHOD + + +BEGIN_METHOD_VOID(Debug_Stop) + + if (!_started) + return; + + GB.Watch(_fdr, GB_WATCH_NONE, (void *)callback_read, 0); + GB.Free(POINTER(&_buffer)); + + GB.Unref(POINTER(&_debug_object)); + + if (_fdw >= 0) + { + close(_fdw); + _fdw = -1; + } + + close(_fdr); + _fdr = -1; + + _started = FALSE; + +END_METHOD + + +BEGIN_METHOD_VOID(Debug_End) + + char path[DEBUG_FIFO_PATH_MAX]; + + CALL_METHOD_VOID(Debug_Stop); + + unlink(fifo_path(path, "in")); + unlink(fifo_path(path, "out")); + + signal(SIGPIPE, SIG_DFL); + +END_METHOD + + +BEGIN_METHOD(Debug_Write, GB_STRING data) + + const char *data = STRING(data); + int len = LENGTH(data); + + if (_fdw < 0) + open_write_fifo(); + + if (data && len > 0) + { + if (write(_fdw, data, len) != len) + goto __ERROR; + } + if (write(_fdw, "\n", 1) != 1) + goto __ERROR; + + return; + +__ERROR: + + fprintf(stderr, "gb.debug: warning: unable to send data to the debugger: %s\n", strerror(errno)); + +END_METHOD + + +BEGIN_METHOD(Debug_GetSignal, GB_INTEGER signal) + + GB.ReturnNewZeroString(strsignal(VARG(signal))); + +END_METHOD + + +BEGIN_PROPERTY(Debug_Fifo) + + GB.ReturnString(DEBUG_fifo); + +END_PROPERTY + +BEGIN_METHOD(Debug_Signal, GB_INTEGER pid) + + kill(VARG(pid), SIGUSR2); + +END_METHOD + + +GB_DESC CDebugDesc[] = +{ + GB_DECLARE("Debug", sizeof(CDEBUG)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_exit", NULL, Debug_End, NULL), + + GB_STATIC_METHOD("Begin", "s", Debug_Begin, NULL), + GB_STATIC_METHOD("End", NULL, Debug_End, NULL), + GB_STATIC_METHOD("Start", NULL, Debug_Start, NULL), + GB_STATIC_METHOD("Stop", NULL, Debug_Stop, NULL), + + GB_STATIC_METHOD("GetSignal", "s", Debug_GetSignal, "(Signal)i"), + + GB_STATIC_METHOD("Write", NULL, Debug_Write, "(Data)s"), + + GB_STATIC_METHOD("Signal", NULL, Debug_Signal, "(ProcessId)i"), + + GB_STATIC_PROPERTY_READ("Fifo", "s", Debug_Fifo), + + GB_EVENT("Read", NULL, "(Data)s", &EVENT_Read), + + GB_END_DECLARE +}; + diff --git a/main/lib/debug/CDebug.h b/main/lib/debug/CDebug.h new file mode 100644 index 00000000..3e1091eb --- /dev/null +++ b/main/lib/debug/CDebug.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + CDebug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDEBUG_H +#define __CDEBUG_H + +#include "gambas.h" + +typedef + struct { + GB_BASE ob; + } + CDEBUG; + +#ifndef __CDEBUG_C +extern GB_DESC CDebugDesc[]; +#endif + +#endif diff --git a/main/lib/debug/Makefile.am b/main/lib/debug/Makefile.am new file mode 100644 index 00000000..d0c201af --- /dev/null +++ b/main/lib/debug/Makefile.am @@ -0,0 +1,18 @@ +COMPONENT = gb.debug +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.debug.la + +gb_debug_la_LIBADD = @GBX_THREAD_LIB@ +gb_debug_la_LDFLAGS = -module @LD_FLAGS@ +gb_debug_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @GBX_THREAD_INC@ @INCLTDL@ $(AM_CFLAGS) + +gb_debug_la_SOURCES = \ + CDebug.h CDebug.c \ + print.h print.c \ + debug.h debug.c \ + profile.h profile.c \ + main.h main.c \ + gb.debug.h + + diff --git a/main/lib/debug/debug.c b/main/lib/debug/debug.c new file mode 100644 index 00000000..557dfa7b --- /dev/null +++ b/main/lib/debug/debug.c @@ -0,0 +1,1504 @@ +/*************************************************************************** + + debug.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DEBUG_C + +// Do not include gbx_debug.h +#define __GBX_DEBUG_H + +#include "gb_common.h" +#include "gambas.h" + +#include +#include +#include + +#include "gb_error.h" +#include "gbx_type.h" +#include "gb_limit.h" +#include "gbx_stack.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_local.h" +#include "gbx_object.h" + +#include "gbx_eval.h" + +#include "print.h" + +#include "debug.h" + +//#define DEBUG_ME + +typedef + struct { + int id; + FUNCTION *func; + PCODE *addr; + CLASS *class; + ushort line; + VALUE *bp; + FUNCTION *fp; + } + DEBUG_BREAK; + +typedef + struct { + int id; + EXPRESSION *expr; + VALUE value; + unsigned changed : 1; + } + DEBUG_WATCH; + +typedef + struct { + char *pattern; + DEBUG_TYPE type; + void (*func)(char *); + bool loop; + } + DEBUG_COMMAND; + + + +DEBUG_INFO DEBUG_info = { 0 }; +GB_DEBUG_INTERFACE *DEBUG_interface; +char DEBUG_buffer[DEBUG_BUFFER_MAX + 1]; +char *DEBUG_fifo = NULL; + +static DEBUG_BREAK *_breakpoints; +static char *_error = NULL; + +static DEBUG_WATCH *_watches; + +static EVAL_INTERFACE EVAL; + +static int _fdr; +static int _fdw; +static FILE *_out; +static FILE *_in = NULL; +static bool _fifo; + +#define EXEC_current (*(STACK_CONTEXT *)GB_DEBUG.GetExec()) + +#ifdef DEBUG_ME +#define WARNING(_msg, ...) fprintf(stderr, "W\t" _msg "\n", ##__VA_ARGS__) +#define INFO(_msg, ...) fprintf(stderr, "I\t" _msg "\n", ##__VA_ARGS__) +#else +#define WARNING(_msg, ...) fprintf(_out, "W\t" _msg "\n", ##__VA_ARGS__) +#define INFO(_msg, ...) fprintf(_out, "I\t" _msg "\n", ##__VA_ARGS__) +#endif + +static void init_eval_interface() +{ + static bool init = FALSE; + + if (!init) + { + GB.GetInterface("gb.eval", EVAL_INTERFACE_VERSION, &EVAL); + init = TRUE; + } +} + +void DEBUG_break_on_next_line(void) +{ + DEBUG_info.stop = TRUE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = NULL; + DEBUG_info.bp = NULL; + DEBUG_info.pp = NULL; +} + + +static void signal_user(int sig) +{ + signal(SIGUSR2, signal_user); + + #ifdef DEBUG_ME + fprintf(stderr, "Got SIGUSR2\n"); + #endif + + /*CAPP_got_signal();*/ + DEBUG_break_on_next_line(); +} + + +bool DEBUG_calc_line_from_position(CLASS *class, FUNCTION *func, PCODE *addr, ushort *line) +{ + int lo, hi; + int mid; + ushort pos = addr - func->code; + ushort *post; + + if (func->debug) + { + post = func->debug->pos; + + lo = 0; + hi = func->debug->nline - 1; + + while (lo < hi) + { + mid = (lo + hi) >> 1; + if (pos < post[mid]) + { + hi = mid; + } + else if (pos >= post[mid + 1]) + { + lo = mid + 1; + } + else + { + *line = mid + func->debug->line; + return FALSE; + } + } + } + + return TRUE; +} + + +static bool calc_position_from_line(CLASS *class, ushort line, FUNCTION **function, PCODE **addr) +{ + int i; + ushort pos, pos_after; + FUNCTION *func = NULL; + FUNC_DEBUG *debug = NULL; + + // Look at the first functions last, because global variables can be declared and + // initialized everywhere in the file, and initialization function is the first one. + + for(i = class->load->n_func - 1; i >= 0; i--) + { + func = &class->load->func[i]; + debug = func->debug; + //fprintf(stderr, "calc_position_from_line: %s (%d -> %d) / %d\n", debug->name, debug->line, debug->line + debug->nline - 1, line); + if (debug && line >= debug->line && line < (debug->line + debug->nline)) + break; + } + + if (i < 0) + return TRUE; + + //fprintf(stderr, "calc_position_from_line: %s OK\n", debug->name); + + line -= debug->line; + + for(;;) + { + pos = debug->pos[line]; + pos_after = debug->pos[line + 1]; + if (pos != pos_after) + break; + + line++; + if (line >= debug->nline) + return TRUE; + } + + *function = func; + *addr = &func->code[pos]; + + /*printf("%s.%d -> %04X\n", class->name, line + debug->line, **addr);*/ + + return FALSE; +} + + +DEBUG_INFO *DEBUG_init(GB_DEBUG_INTERFACE *debug, bool fifo, const char *fifo_name) +{ + char path[DEBUG_FIFO_PATH_MAX]; + + DEBUG_interface = debug; + _fifo = fifo; + + if (_fifo) + { + DEBUG_fifo = GB.NewZeroString(fifo_name); + + snprintf(path, sizeof(path), "%sin", fifo_name); + + for(;;) + { + _fdw = open(path, O_WRONLY | O_CLOEXEC); + if (_fdw >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + { + fprintf(stderr, "gb.debug: unable to open input fifo: %s: %s\n", strerror(errno), path); + return NULL; + } + } + + _out = fdopen(_fdw, "w"); + + if (!_out) + { + fprintf(stderr, "gb.debug: unable to create stream on input fifo: %s: %s\n", strerror(errno), path); + return NULL; + } + } + else + { + _out = stdout; + } + + //ARRAY_create(&_breakpoints); + GB.NewArray(&_breakpoints, sizeof(DEBUG_BREAK), 16); + GB.NewArray(&_watches, sizeof(DEBUG_WATCH), 0); + + signal(SIGUSR2, signal_user); + signal(SIGPIPE, SIG_IGN); + + setlinebuf(_out); + + return &DEBUG_info; +} + +void DEBUG_exit(void) +{ + int i; + + GB.FreeArray(&_breakpoints); + + if (_watches) + { + for (i = 0; i < GB.Count(_watches); i++) + EVAL.Free(POINTER(&_watches[i].expr)); + + GB.FreeArray(&_watches); + } + + GB.FreeString(&DEBUG_fifo); + GB.FreeString(&_error); + + // Don't do it, it blocks! + + /*if (EXEC_fifo) + { + fclose(_in); + fclose(_out); + } + */ +} + + +static int find_free_breakpoint(void) +{ + int i; + char used[MAX_BREAKPOINT]; + + memset(used, FALSE, MAX_BREAKPOINT); + + for (i = 0; i < ARRAY_count(_breakpoints); i++) + used[_breakpoints[i].id - 1] = TRUE; + + for (i = 0; i < MAX_BREAKPOINT; i++) + if (!used[i]) + return (i + 1); + + return 0; +} + +static bool init_breakpoint(DEBUG_BREAK *brk) +{ + PCODE *addr = NULL; + FUNCTION *func; + + //fprintf(stderr, "init_breakpoint: id = %d\n", brk->id); + + if (brk->addr || !CLASS_is_loaded(brk->class)) + { + WARNING("breakpoint is pending"); + return TRUE; + } + + if (CLASS_is_native(brk->class) || !brk->class->debug) + { + WARNING("Cannot set breakpoint: no debugging information"); + return TRUE; + } + + if (calc_position_from_line(brk->class, brk->line, &func, &addr)) + { + WARNING("Cannot set breakpoint: cannot calculate position"); + //fprintf(_out, "Cannot calc position from line number\n"); + return TRUE; + } + + if (!PCODE_is_breakpoint(*addr)) + { + //WARNING("Cannot set breakpoint: Not a line beginning: %04d: %04X", addr - func->code, *addr); + WARNING("Cannot set breakpoint: Not a line beginning"); + //fprintf(_out, "Not a line beginning ?\n"); + return TRUE; + } + + if (*addr & 0xFF) + { + WARNING("breakpoint already set"); + //fprintf(_out, "_breakpoints already set\n"); + return FALSE; + } + + brk->addr = addr; + *addr = PCODE_BREAKPOINT(brk->id); + + //fprintf(stderr, "init_breakpoint: OK\n"); + + #ifdef DEBUG_ME + fprintf(stderr, "init_breakpoint: %s.%d\n", brk->class->name, brk->line); + #endif + + INFO("breakpoint set: %s.%d", brk->class->name, brk->line); + return FALSE; +} + + +static bool set_breakpoint(CLASS *class, ushort line) +{ + DEBUG_BREAK *brk; + int id; + + if (GB.Count(_breakpoints) >= MAX_BREAKPOINT) + { + WARNING("Too many breakpoints"); + return TRUE; + } + + id = find_free_breakpoint(); + if (id == 0) + { + WARNING("Cannot create breakpoint"); + return TRUE; + } + + brk = (DEBUG_BREAK *)GB.Add(&_breakpoints); + + brk->id = id; + brk->addr = NULL; + brk->class = class; + brk->line = line; + + #ifdef DEBUG_ME + fprintf(stderr, "set_breakpoint: %s.%d\n", class->name, line); + #endif + + init_breakpoint(brk); + + return FALSE; +} + + +static bool unset_breakpoint(CLASS *class, ushort line) +{ + int i; + DEBUG_BREAK *brk; + + for (i = 0; i < GB.Count(_breakpoints); i++) + { + brk = &_breakpoints[i]; + if (brk->class == class && brk->line == line) + { + if (brk->addr) + *(brk->addr) = PCODE_BREAKPOINT(0); + GB.Remove(&_breakpoints, i, 1); + + #ifdef DEBUG_ME + fprintf(stderr, "unset_breakpoint: %s.%d\n", class->name, line); + #endif + + INFO("breakpoint removed"); + return FALSE; + } + } + + WARNING("Unknown breakpoint"); + return TRUE; +} + + +static void unset_all_breakpoints() +{ + int i; + DEBUG_BREAK *brk; + + for (i = 0; i < GB.Count(_breakpoints); i++) + { + brk = &_breakpoints[i]; + if (brk->addr) + *(brk->addr) = PCODE_BREAKPOINT(0); + } + + GB.Remove(&_breakpoints, 0, GB.Count(_breakpoints)); +} + + +void DEBUG_init_breakpoints(CLASS *class) +{ + int i; + DEBUG_BREAK *brk; + + #ifdef DEBUG_ME + fprintf(stderr, "DEBUG_init_breakpoints: %p %s\n", class, class->name); + #endif + + for (i = 0; i < GB.Count(_breakpoints); i++) + { + brk = &_breakpoints[i]; + if (brk->class == class) + { + //fprintf(stderr, "DEBUG_init_breakpoints: %s\n", class->name); + init_breakpoint(brk); + } + } +} + + +static void print_local() +{ + int i; + FUNCTION *fp; + LOCAL_SYMBOL *lp; + + fp = DEBUG_info.fp; + + if (!fp || !fp->debug) + return; + + for (i = 0; i < fp->debug->n_local; i++) + { + lp = &fp->debug->local[i]; + fprintf(_out, "%.*s ", lp->sym.len, lp->sym.name); + } +} + + +static void print_symbol(GLOBAL_SYMBOL *gp, bool is_static, bool is_public) +{ + if (CTYPE_get_kind(gp->ctype) != TK_VARIABLE && CTYPE_get_kind(gp->ctype) != TK_CONST) + return; + + if (CTYPE_is_static(gp->ctype) && !is_static) + return; + + if (!CTYPE_is_static(gp->ctype) && is_static) + return; + + if (CTYPE_is_public(gp->ctype) && !is_public) + return; + + if (!CTYPE_is_public(gp->ctype) && is_public) + return; + + fprintf(_out, "%.*s ", gp->sym.len, gp->sym.name); +} + + +static void print_object() +{ + int i; + GLOBAL_SYMBOL *gp; + CLASS *cp = DEBUG_info.cp; + void *op = DEBUG_info.op; + + if (!cp || !cp->load) + return; + + fprintf(_out, "S: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, TRUE, TRUE); + } + + fprintf(_out, "s: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, TRUE, FALSE); + } + + if (op) + { + fprintf(_out, "D: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, FALSE, TRUE); + } + + fprintf(_out, "d: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, FALSE, FALSE); + } + } +} + +static void command_quit(char *cmd) +{ + exit(1); +} + +static void command_hold(char *cmd) +{ + GB_DEBUG.DebugHold(); +} + +static void command_go(char *cmd) +{ + GB.Component.Signal(GB_SIGNAL_DEBUG_CONTINUE, 0); + + DEBUG_info.stop = FALSE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = NULL; + DEBUG_info.bp = NULL; + DEBUG_info.pp = NULL; +} + +static void command_step(char *cmd) +{ + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_break_on_next_line(); +} + +static void command_next(char *cmd) +{ + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_info.stop = TRUE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = FP; + DEBUG_info.bp = BP; + DEBUG_info.pp = PP; +} + +static void command_from(char *cmd) +{ + STACK_CONTEXT *sc = GB_DEBUG.GetStack(0); //STACK_get_current(); + + if (sc && sc->pc) + { + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_info.stop = TRUE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = sc->fp; + DEBUG_info.bp = sc->bp; + DEBUG_info.pp = sc->pp; + } + else + { + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_info.stop = TRUE; + DEBUG_info.leave = TRUE; + DEBUG_info.fp = FP; + DEBUG_info.bp = BP; + DEBUG_info.pp = PP; + } +} + + +static void command_breakpoint(char *cmd) +{ + char *comp = NULL; + char class_name[256]; + ushort line; + bool unset = *cmd == '-'; + char *p; + + cmd++; + + if (unset && cmd[0] == '*' && cmd[1] == 0) + { + unset_all_breakpoints(); + return; + } + + if (*cmd == '[') + { + p = index(cmd, ']'); + if (p && p[1] == '.') + { + comp = &cmd[1]; + *p = 0; + cmd = p + 2; + + if (comp[0] == '$' && !comp[1]) + comp = NULL; + } + } + + if (sscanf(cmd, "%256[^.].%hu", class_name, &line) != 2) + WARNING("Cannot %s breakpoint: syntax error", unset ? "remove" : "add"); + else if (unset) + unset_breakpoint((CLASS *)GB_DEBUG.FindClass(comp, class_name), line); + else + set_breakpoint((CLASS *)GB_DEBUG.FindClass(comp, class_name), line); +} + + +void DEBUG_backtrace(FILE *out) +{ + int i, n; + STACK_CONTEXT *context; + + fprintf(out, "%s", DEBUG_get_current_position()); + + //for (i = 0; i < (STACK_frame_count - 1); i++) + n = 0; + for (i = 0;; i++) + { + context = GB_DEBUG.GetStack(i); //&STACK_frame[i]; + if (!context) + break; + + n += fprintf(out, " %s", DEBUG_get_position(context->cp, context->fp, context->pc)); + + /*if (context->pc) + { + line = 0; + if (DEBUG_calc_line_from_position(context->cp, context->fp, context->pc, &line)) + n += fprintf(out, " %s.?.?", context->cp->name); + else + n += fprintf(out, " %s.%s.%d", context->cp->name, context->fp->debug->name, line); + } + else if (context->cp) + n += fprintf(out, " ?");*/ + + if (n >= (DEBUG_OUTPUT_MAX_SIZE / 2)) + { + fprintf(out, " ..."); + break; + } + } +} + + +static void debug_info(bool frame) +{ + const char *p; + char c; + int i; + DEBUG_WATCH *watch; + + fprintf(_out, "%c[%d]\t", frame ? '@' : '*', getpid()); + + if (_error) + { + p = _error; + for(;;) + { + c = *p++; + if (!c) + break; + + if (c == '\n' || c == '\r' || c == '\t') + c = ' '; + fputc(c, _out); + } + } + + fprintf(_out, "\t"); + + DEBUG_backtrace(_out); + fprintf(_out, "\t"); + + print_local(); + fprintf(_out, "\t"); + + print_object(); + fprintf(_out, "\t"); + + for (i = 0; i < GB.Count(_watches); i++) + { + watch = &_watches[i]; + if (watch->changed) + fprintf(_out, "%d ", watch->id); + } + + fputc('\n', _out); +} + + +static void set_info(STACK_CONTEXT *context) +{ + if (context) + { + DEBUG_info.bp = context->bp; + DEBUG_info.pp = context->pp; + DEBUG_info.fp = context->fp; + DEBUG_info.op = context->op; + DEBUG_info.cp = context->cp; + } + else + { + DEBUG_info.bp = BP; + DEBUG_info.pp = PP; + DEBUG_info.fp = FP; + DEBUG_info.op = OP; + DEBUG_info.cp = CP; + } +} + + +static void command_frame(char *cmd) +{ + int i; + int frame; + STACK_CONTEXT *context = NULL; + + if (cmd) + { + frame = atoi(&cmd[1]); + //fprintf(_out, "switching to frame %d\n", frame); + + if (frame > 0) + { + for (i = 0;; i++) + { + context = GB_DEBUG.GetStack(i); + if (!context) + break; + if (!context->pc && !context->cp) + continue; + + frame--; + if (!frame) + break; + } + } + } + + set_info(context); + debug_info(cmd != NULL); +} + + +static void command_eval(char *cmd) +{ + EXPRESSION *expr; + ERROR_INFO save_error = { 0 }; + ERROR_INFO save_last = { 0 }; + DEBUG_INFO save_debug; + VALUE *val; + VALUE result; + int start, len; + FILE *out; + const char *name; + int ret; + + init_eval_interface(); + + //out = *cmd == '!' ? stdout : _out; + out = _out; + + len = strlen(cmd); + for (start = 0; start < len; start++) + { + if (cmd[start] == '\t') + break; + //if (*cmd != '!') + fputc(cmd[start], _out); + } + + if (start >= len) + return; + + //if (*cmd != '!') + fprintf(_out, "\t"); + + GB_DEBUG.SaveError(&save_error, &save_last); + save_debug = DEBUG_info; + + start++; + EVAL.New(POINTER(&expr), &cmd[start], len - start); + + if (EVAL.Compile(expr, *cmd == '=')) + { + //if (*cmd != '!') + fprintf(_out, "!"); + fputs(expr->error, out); + goto __END; + } + + GB_DEBUG.EnterEval(); + val = (VALUE *)EVAL.Run(expr, GB_DEBUG.GetValue); + GB_DEBUG.LeaveEval(); + if (!val) + goto __ERROR; + + result = *val; + GB.BorrowValue((GB_VALUE *)&result); + + switch(*cmd) + { + case '?': + PRINT_value(out, val, TRUE); + break; + + case '!': + PRINT_value(out, val, FALSE); + break; + + case '#': + PRINT_object(out, val); + break; + + case '=': + if (!EVAL.GetAssignmentSymbol(expr, &name, &len)) + { + ret = GB_DEBUG.SetValue(name, len, val); + if (ret == GB_DEBUG_SET_ERROR) + { + GB.ReleaseValue((GB_VALUE *)&result); + goto __ERROR; + } + else if (ret == GB_DEBUG_SET_READ_ONLY) + { + GB.ReleaseValue((GB_VALUE *)&result); + fprintf(out, "!%.*s is read-only", len, name); + goto __END; + } + } + fprintf(out, "OK"); + break; + } + + GB.ReleaseValue((GB_VALUE *)&result); + goto __END; + +__ERROR: + + fprintf(out, "!"); + fputs(GB_DEBUG.GetErrorMessage(), out); + +__END: + + EVAL.Free(POINTER(&expr)); + DEBUG_info = save_debug; //.cp = NULL; + GB_DEBUG.RestoreError(&save_error, &save_last); + + fputc('\n', out); + fflush(out); +} + +static int find_watch(int id) +{ + int i; + DEBUG_WATCH *watch; + + for (i = 0; i < GB.Count(_watches); i++) + { + watch = &_watches[i]; + if (watch->id == id) + return i; + } + + return -1; +} + +static int add_watch(int id, EXPRESSION *expr, VALUE *value) +{ + DEBUG_WATCH *watch = (DEBUG_WATCH *)GB.Add(&_watches); + watch->id = id; + watch->expr = expr; + if (value) + watch->value = *value; + return GB.Count(_watches) - 1; +} + + +static void remove_watch(int index) +{ + DEBUG_WATCH *watch = &_watches[index]; + + EVAL.Free(POINTER(&watch->expr)); + GB.Remove(&_watches, index, 1); +} + +static void command_watch(char *cmd) +{ + EXPRESSION *expr; + int start, len; + int id; + int index; + VALUE *value; + ERROR_INFO save_error = { 0 }; + ERROR_INFO save_last = { 0 }; + DEBUG_INFO save_debug; + + init_eval_interface(); + + len = strlen(cmd); + for (start = 0; start < len; start++) + { + if (cmd[start] == '\t') + break; + } + + cmd[start] = 0; + id = atoi(&cmd[1]); + if (id == 0) + return; + + expr = NULL; + value = NULL; + + if (len > start) + { + GB_DEBUG.SaveError(&save_error, &save_last); + save_debug = DEBUG_info; + + start++; + EVAL.New(POINTER(&expr), &cmd[start], len - start); + + if (EVAL.Compile(expr, FALSE)) + { + fputs(expr->error, _out); + EVAL.Free(POINTER(&expr)); + } + else + { + GB_DEBUG.EnterEval(); + value = (VALUE *)EVAL.Run(expr, GB_DEBUG.GetValue); + GB_DEBUG.LeaveEval(); + } + + DEBUG_info = save_debug; + GB_DEBUG.RestoreError(&save_error, &save_last); + + if (!expr) + return; + } + + index = find_watch(id); + if (index >= 0) + remove_watch(index); + if (expr) + add_watch(id, expr, value); + + DEBUG_info.watch = GB.Count(_watches) > 0; +} + + +static void command_symbol(char *cmd) +{ + int start, len; + ERROR_INFO save_error = { 0 }; + ERROR_INFO save_last = { 0 }; + DEBUG_INFO save_debug; + + GB_DEBUG.SaveError(&save_error, &save_last); + save_debug = DEBUG_info; + + len = strlen(cmd); + for (start = 0; start < len; start++) + { + if (cmd[start] == '\t') + break; + fputc(cmd[start], _out); + } + + if (start >= len) + return; + + fprintf(_out, "\t"); + + /*DEBUG_info.bp = BP; + DEBUG_info.fp = FP; + DEBUG_info.op = OP; + DEBUG_info.cp = CP;*/ + + start++; + PRINT_symbol(_out, &cmd[start], len - start); + + fputc('\n', _out); + fflush(_out); + + DEBUG_info = save_debug; + GB_DEBUG.RestoreError(&save_error, &save_last); +} + + +static void command_option(char *cmd) +{ + bool on; + + if (!cmd[1] || !cmd[2]) + return; + + on = cmd[2] == '+'; + + switch(cmd[1]) + { + case 'b': + + GB_DEBUG.BreakOnError(on); + break; + + case 'g': + GB_DEBUG.DebugInside(on); + break; + + default: + ; + } +} + + +void DEBUG_breakpoint(int id) +{ + DEBUG_main(FALSE); +} + + +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc) +{ + const char *comp_name; + const char *class_name; + const char *func_name; + ushort line = 0; + + if (!cp) + return "?"; + + class_name = cp->name; + + while (*class_name == '^') + class_name++; + + if (cp->component) + comp_name = cp->component->name; + else + comp_name = "$"; + + if (fp && fp->debug) + { + func_name = fp->debug->name; + if (pc) + DEBUG_calc_line_from_position(cp, fp, pc, &line); + } + else + func_name = "?"; + + snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "[%s].%s.%s.%d", comp_name, class_name, func_name, line); + return DEBUG_buffer; +} + +const char *DEBUG_get_profile_position(CLASS *cp, FUNCTION *fp, PCODE *pc) +{ + static uint prof_index = 0; + const char *name; + const char *func; + char buffer[16], buffer2[16]; + + func = "?"; + + if (cp) + { + if (cp->load && cp->load->prof) + { + if (cp->load->prof[0] == 0) + { + prof_index++; + cp->load->prof[0] = prof_index; + name = cp->name; + } + else + { + sprintf(buffer, "%u", cp->load->prof[0]); + name = buffer; + } + + if (fp && fp->debug) + { + int i = fp->debug->index + 1; + if (cp->load->prof[i] == 0) + { + prof_index++; + cp->load->prof[i] = prof_index; + func = fp->debug->name; + } + else + { + sprintf(buffer2, "%u", cp->load->prof[i]); + func = buffer2; + } + } + else + func = "?"; + } + else + name = cp->name; + } + else + name = "?"; + + if (pc) + { + ushort line = 0; + + if (fp != NULL && fp->debug) + DEBUG_calc_line_from_position(cp, fp, pc, &line); + + snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "%.64s.%.64s.%d", name, func, line); + } + else + { + snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "%.64s.%.64s", name, func); + } + + return DEBUG_buffer; +} + + +const char *DEBUG_get_current_position(void) +{ + return DEBUG_get_position(CP, FP, PC); +} + + +void DEBUG_where(void) +{ + fprintf(_out ? _out : stderr, "%s: ", DEBUG_get_current_position()); +} + + +void DEBUG_welcome(void) +{ + if (!_fifo) + fprintf(_out, DEBUG_WELCOME); +} + +static bool compare_values(VALUE *a, VALUE *b) +{ + void *jump[] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + void *vjump[] = { + &&__VOID, &&__VBOOLEAN, &&__VBYTE, &&__VSHORT, &&__VINTEGER, &&__VLONG, &&__VSINGLE, &&__VFLOAT, + &&__VDATE, &&__VSTRING, &&__VSTRING, &&__VPOINTER, &&__VOID, &&__VOID, &&__VOID, &&__VOID + }; + + if (a->type != b->type) + return TRUE; + + if (TYPE_is_object(a->type)) + goto __OBJECT; + else + goto *jump[a->type]; + +__VOID: +__NULL: + return FALSE; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + return a->_integer.value != b->_integer.value; + +__LONG: +__DATE: + return a->_long.value != b->_long.value; + +__SINGLE: + return a->_single.value != b->_single.value; + +__FLOAT: + return a->_float.value != b->_float.value; + +__STRING: + return a->_string.addr != b->_string.addr || a->_string.start != b->_string.start || a->_string.len != b->_string.len; + +__POINTER: +__OBJECT: +__CLASS: + return a->_pointer.value != b->_pointer.value; + +__FUNCTION: + return a->_function.class != b->_function.class || a->_function.object != b->_function.object || a->_function.index != b->_function.index; + +__VARIANT: + if (a->_variant.vtype != b->_variant.vtype) + return TRUE; + + if (TYPE_is_object(a->_variant.vtype)) + goto __VOBJECT; + else + goto *vjump[a->_variant.vtype]; + +__VBOOLEAN: return a->_variant.value._boolean != b->_variant.value._boolean; +__VBYTE: return a->_variant.value._byte != b->_variant.value._byte; +__VSHORT: return a->_variant.value._short != b->_variant.value._short; +__VINTEGER: return a->_variant.value._integer != b->_variant.value._integer; +__VDATE: __VLONG: return a->_variant.value._long != b->_variant.value._long; +__VSINGLE: return a->_variant.value._single!= b->_variant.value._single; +__VFLOAT: return a->_variant.value._float != b->_variant.value._float; +__VPOINTER: __VOBJECT: __VSTRING: return a->_variant.value._string != b->_variant.value._string; +} + + +bool DEBUG_check_watches(void) +{ + int i; + DEBUG_WATCH *watch; + VALUE *value; + bool changed = FALSE; + ERROR_INFO save_error = { 0 }; + ERROR_INFO save_last = { 0 }; + DEBUG_INFO save_debug; + + set_info(NULL); + + GB_DEBUG.SaveError(&save_error, &save_last); + save_debug = DEBUG_info; + + for (i = 0; i < GB.Count(_watches); i++) + { + watch = &_watches[i]; + watch->changed = FALSE; + + GB_DEBUG.EnterEval(); + value = (VALUE *)EVAL.Run(watch->expr, GB_DEBUG.GetValue); + GB_DEBUG.LeaveEval(); + if (!value) + continue; + + if (compare_values(&watch->value, value)) + { + if (watch->value.type != T_VOID) + changed = TRUE; + watch->value = *value; + watch->changed = TRUE; + } + } + + DEBUG_info = save_debug; + GB_DEBUG.RestoreError(&save_error, &save_last); + + if (!changed) + return FALSE; + + DEBUG_main(FALSE); + return TRUE; +} + + +static void open_read_fifo() +{ + char path[DEBUG_FIFO_PATH_MAX]; + + if (_fifo) + { + snprintf(path, sizeof(path), "%sout", DEBUG_fifo); + + for(;;) + { + _fdr = open(path, O_RDONLY | O_CLOEXEC); + if (_fdr >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + { + fprintf(stderr, "gb.debug: unable to open output fifo: %s: %s\n", strerror(errno), path); + return; + } + usleep(20000); + } + + _in = fdopen(_fdr, "r"); + + if (!_in) + { + fprintf(stderr, "gb.debug: unable to open stream on output fifo: %s: %s\n", strerror(errno), path); + return; + } + + setlinebuf(_in); + } + else + { + _in = stdin; + } +} + +void DEBUG_main(bool error) +{ + static DEBUG_TYPE last_command = TC_NONE; + + static DEBUG_COMMAND Command[] = + { + // "p" and "i" are reserved for remote debugging. + + { "q", TC_NONE, command_quit, FALSE }, + { "n", TC_NEXT, command_next, FALSE }, + { "s", TC_STEP, command_step, FALSE }, + { "f", TC_FROM, command_from, FALSE }, + { "g", TC_GO, command_go, FALSE }, + { "+", TC_NONE, command_breakpoint, TRUE }, + { "-", TC_NONE, command_breakpoint, TRUE }, + { "&", TC_NONE, command_symbol, TRUE }, + { "?", TC_NONE, command_eval, TRUE }, + { "!", TC_NONE, command_eval, TRUE }, + { "#", TC_NONE, command_eval, TRUE }, + { "=", TC_NONE, command_eval, TRUE }, + { "@", TC_NONE, command_frame, TRUE }, + { "o", TC_NONE, command_option, TRUE }, + { "w", TC_NONE, command_watch, TRUE }, + { "h", TC_NONE, command_hold, TRUE }, + + { NULL } + }; + + static bool first = TRUE; + char *cmd = NULL; + char cmdbuf[256]; + int len; + DEBUG_COMMAND *tc = NULL; + int save_errno = errno; + /*static int cpt = 0;*/ + + GB.FreeString(&_error); + if (error) + _error = GB.NewZeroString(GB_DEBUG.GetErrorMessage()); + + fflush(_out); + + #ifdef DEBUG_ME + fprintf(stderr, "DEBUG_main {\n"); + #endif + + if (_fifo) + { + fprintf(_out, first ? "!!\n" : "!\n"); + first = FALSE; + } + + command_frame(NULL); + + if (!_in) + open_read_fifo(); + + do + { + /*if (CP == NULL) + printf("[]:"); + else + printf("[%s%s]:", DEBUG_get_current_position(), _error ? "*" : "");*/ + + GB.Component.Signal(GB_SIGNAL_DEBUG_BREAK, 0); + + if (!_fifo) + { + fprintf(_out, "> "); + fflush(_out); + } + + GB.FreeString(&cmd); + + for(;;) + { + *cmdbuf = 0; + errno = 0; + if (fgets(cmdbuf, sizeof(cmdbuf), _in) == NULL && errno != EINTR) + break; + if (!*cmdbuf) + continue; + cmd = GB.AddString(cmd, cmdbuf, 0); + if (cmd[GB.StringLength(cmd) - 1] == '\n') + break; + } + + len = GB.StringLength(cmd); + + // A null string command means an I/O error + if (len == 0) + { + fprintf(stderr, "gb.debug: warning: debugger I/O error: %s\n", strerror(errno)); + exit(1); + } + + if (len > 0 && cmd[len - 1] == '\n') + { + len--; + cmd[len] = 0; + } + + #ifdef DEBUG_ME + fprintf(stderr, "--> %s\n", cmd); + #endif + + if (len == 0) + { + if (last_command == TC_NONE) + continue; + + for (tc = Command; tc->pattern; tc++) + { + if (tc->type == last_command) + { + (*tc->func)(cmd); + break; + } + } + } + else + { + for (tc = Command; tc->pattern; tc++) + { + if (strncasecmp(tc->pattern, cmd, strlen(tc->pattern)) == 0) + { + if (tc->type != TC_NONE) + last_command = tc->type; + (*tc->func)(cmd); + break; + } + } + } + + if (tc->pattern == NULL) + WARNING("Unknown command: %s", cmd); + + fflush(_out); + } + while (last_command == TC_NONE || tc->pattern == NULL || tc->loop); + + GB.FreeString(&cmd); + + errno = save_errno; + + #ifdef DEBUG_ME + fprintf(stderr, "} DEBUG_main\n"); + #endif +} + diff --git a/main/lib/debug/debug.h b/main/lib/debug/debug.h new file mode 100644 index 00000000..b2a1dae6 --- /dev/null +++ b/main/lib/debug/debug.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + debug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DEBUG_H +#define __DEBUG_H + +#include "gambas.h" +#include "main.h" +#include "gb.debug.h" +#include "gb_pcode.h" +#include "gbx_value.h" + +typedef + enum { + TC_NONE = 0, + TC_STEP = 1, + TC_NEXT = 2, + TC_GO = 3, + TC_FROM = 4 + } + DEBUG_TYPE; + +#ifndef __GBX_DEBUG_C +EXTERN DEBUG_INFO DEBUG_info; +EXTERN GB_DEBUG_INTERFACE *DEBUG_interface; +EXTERN char DEBUG_buffer[]; +EXTERN char *DEBUG_fifo; +#endif + +#define DEBUG_BUFFER_MAX 512 + +#define GB_DEBUG (*DEBUG_interface) + +#define DEBUG_WELCOME "Welcome to the Gambas debugger.\n" + +void DEBUG_breakpoint(int id); +void DEBUG_main(bool error); + +bool DEBUG_calc_line_from_position(CLASS *class, FUNCTION *func, PCODE *addr, ushort *line); +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc); +const char *DEBUG_get_profile_position(CLASS *cp, FUNCTION *fp, PCODE *pc); +const char *DEBUG_get_current_position(void); +void DEBUG_init_breakpoints(CLASS *class); + +void DEBUG_break_on_next_line(void); + +DEBUG_INFO *DEBUG_init(GB_DEBUG_INTERFACE *debug, bool fifo, const char*fifo_name); +void DEBUG_exit(void); +void DEBUG_welcome(void); +void DEBUG_where(void); +void DEBUG_backtrace(FILE *out); +bool DEBUG_check_watches(void); + +#endif diff --git a/main/lib/debug/gb.debug.component b/main/lib/debug/gb.debug.component new file mode 100644 index 00000000..e1c80a2c --- /dev/null +++ b/main/lib/debug/gb.debug.component @@ -0,0 +1,2 @@ +[Component] +Author=Benoît Minisini diff --git a/main/lib/debug/gb.debug.h b/main/lib/debug/gb.debug.h new file mode 100644 index 00000000..64646b65 --- /dev/null +++ b/main/lib/debug/gb.debug.h @@ -0,0 +1,128 @@ +/*************************************************************************** + + gb.debug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DEBUG_H +#define __GB_DEBUG_H + +#include +#include "gambas.h" +#include "gbx_class.h" +#include "gbx_value.h" +#include "gb_pcode.h" + +#define DEBUG_INTERFACE_VERSION 1 + +typedef + struct { + unsigned stop : 1; + unsigned leave : 1; + unsigned watch : 1; + FUNCTION *fp; + VALUE *bp; + VALUE *pp; + void *op; + CLASS *cp; + PCODE *ec; + VALUE *ep; + } + DEBUG_INFO; + +enum +{ + GB_DEBUG_ACCESS_NORMAL = 0, + GB_DEBUG_ACCESS_ARRAY = 1, + GB_DEBUG_ACCESS_COLLECTION = 2 +}; + +enum +{ + GB_DEBUG_SET_OK = 0, + GB_DEBUG_SET_ERROR = 1, + GB_DEBUG_SET_READ_ONLY = 2 +}; + +typedef + void (*GB_DEBUG_ENUM_CB)(char *key, int len); + +typedef + struct { + void *(*GetExec)(void); + void *(*GetStack)(int frame); + const char *(*GetErrorMessage)(void); + void (*SaveError)(void *, void *); + void (*RestoreError)(void *, void *); + void (*ToString)(GB_VALUE *value, char **addr, int *len); + int (*FormatDate)(GB_DATE_SERIAL *date, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str); + int (*FormatNumber)(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, bool local); + bool (*GetValue)(const char *sym, int len, GB_VARIANT *ret); + int (*SetValue)(const char *sym, int len, VALUE *value); + void (*GetArrayValue)(GB_ARRAY array, int index, GB_VALUE *value); + void (*EnumKeys)(void *collection, GB_DEBUG_ENUM_CB cb); + void *(*GetNextSortedSymbol)(void *klass, int *index); + int (*GetObjectAccessType)(void *object, CLASS *klass, int *count); + GB_CLASS (*FindClass)(const char *comp_name, const char *class_name); + int *(*GetArrayBounds)(void *array); + void (*BreakOnError)(bool); + void (*EnterEval)(void); + void (*LeaveEval)(void); + void (*DebugInside)(bool); + void (*DebugHold)(void); + } + GB_DEBUG_INTERFACE; + +typedef + struct { + intptr_t version; + DEBUG_INFO *(*Init)(GB_DEBUG_INTERFACE *debug, int fifo, const char *fifo_name); + void (*Exit)(void); + void (*Welcome)(void); + void (*Main)(int error); + void (*Where)(void); + void (*Backtrace)(FILE *out); + void (*Breakpoint)(int id); + void (*BreakOnNextLine)(void); + const char *(*GetPosition)(void *klass, void *func, void *pcode); + const char *(*GetCurrentPosition)(void); + void (*InitBreakpoints)(void *klass); + bool (*CheckWatches)(void); + struct { + void (*Init)(const char *path); + void (*Add)(void *cp, void *fp, void *pc); + void (*Exit)(void); + void (*Begin)(void *cp, void *fp); + void (*End)(void *cp, void *fp); + void (*Cancel)(void); + } + Profile; + void *_null; + } + DEBUG_INTERFACE; + +#define DEBUG_OUTPUT_MAX_SIZE 65536 +#define DEBUG_FIFO_PATTERN FILE_TEMP_PREFIX "/gambas" GAMBAS_VERSION_STRING "-ide-debug-%d.%s" +#define DEBUG_FIFO_PATH_MAX 64 +#define DEBUG_WAIT_LINK "/tmp/gambas-%s.debug" +#define DEBUG_WAIT_IGNORE "/tmp/gambas-%s.debug.%d" +#define DEBUG_WAIT_IGNORE_MAX 8 + +#endif diff --git a/main/lib/debug/main.c b/main/lib/debug/main.c new file mode 100644 index 00000000..bb0eeaa8 --- /dev/null +++ b/main/lib/debug/main.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "debug.h" +#include "profile.h" +#include "CDebug.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CDebugDesc, + NULL +}; + +void *GB_DEBUG_1[] EXPORT = +{ + (void *)1, + (void *)DEBUG_init, + (void *)DEBUG_exit, + (void *)DEBUG_welcome, + (void *)DEBUG_main, + (void *)DEBUG_where, + (void *)DEBUG_backtrace, + (void *)DEBUG_breakpoint, + (void *)DEBUG_break_on_next_line, + (void *)DEBUG_get_position, + (void *)DEBUG_get_current_position, + (void *)DEBUG_init_breakpoints, + (void *)DEBUG_check_watches, + (void *)PROFILE_init, + (void *)PROFILE_add, + (void *)PROFILE_exit, + (void *)PROFILE_begin, + (void *)PROFILE_end, + (void *)PROFILE_cancel, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/debug/main.h b/main/lib/debug/main.h new file mode 100644 index 00000000..6425d51a --- /dev/null +++ b/main/lib/debug/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif diff --git a/main/lib/debug/print.c b/main/lib/debug/print.c new file mode 100644 index 00000000..f79072be --- /dev/null +++ b/main/lib/debug/print.c @@ -0,0 +1,495 @@ +/*************************************************************************** + + print.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __PRINT_C + +// Do not include gbx_debug.h +#define __GBX_DEBUG_H + +#include "gb_common.h" + +#include +#include +#include + +#include "gb_error.h" +#include "gbx_type.h" +#include "gb_limit.h" +#include "gbx_class.h" +#include "gbx_object.h" +#include "gbx_local.h" +#include "gbx_variant.h" + +#include "gambas.h" + +#include "print.h" + + +static FILE *_where; +static int _level; + +static void print_value(VALUE *value); + +static void print_string(const char *s, int len, bool limited) +{ + int i; + uchar c; + + fputc('"', _where); + + for (i = 0; i < len; i++) + { + if (limited && i > (DEBUG_OUTPUT_MAX_SIZE - 8)) + { + fprintf(_where, "..."); + break; + } + + c = s[i]; + + if (c < 32) + { + if (c == 10) + fprintf(_where, "\\n"); + else if (c == 13) + fprintf(_where, "\\r"); + else if (c == 9) + fprintf(_where, "\\t"); + else + fprintf(_where, "\\x%02X", c); + } + else if (c == '\"') + { + fprintf(_where, "\\\""); + } + else + { + fputc(c, _where); + } + } + + fputc('"', _where); +} + +#if 0 +static void new_line(void) +{ + int i; + + fputc('\t', _where); + for (i = 1; i < _level; i++) + fprintf(_where, " "); +} +#endif + +static void print_object(void *object) +{ + //int i; + //char *key; + //VALUE value; + //long len; + CLASS *class = OBJECT_class(object); + + if (*class->name == '$') + fprintf(_where, "(Struct %s %p) [%ld]", &class->name[1], object, (long)OBJECT_count(object)); + else + fprintf(_where, "(%s %p) [%ld]", class->name, object, (long)OBJECT_count(object)); + +/* + if (GB.Is(object, GB.FindClass("Collection"))) + { + //if (_level > 1) + // new_line(); + + //fprintf(_where, "%s", OBJECT_class(object)->name); + GB_DEBUG.EnumCollection(object, NULL, NULL, NULL); + for (i = 0; i < 8; i++) + { + if (GB_DEBUG.EnumCollection(object, (GB_VARIANT *)&value, &key, &len)) + break; + new_line(); + fprintf(_where, "["); + print_string(key, len); + fprintf(_where, "] = "); + print_value(&value); + } + if (GB.Collection.Count(object) > 8) + { + new_line(); + fprintf(_where, "... #%ld", GB.Collection.Count(object)); + } + } + else if (GB.Is(object, GB.FindClass("Array"))) + { + //if (_level > 1) + // new_line(); + + //fprintf(_where, "%s", OBJECT_class(object)->name); + for (i = 0; i < GB.Array.Count(object); i++) + { + if (i >= 8 && i < (GB.Array.Count(object) - 1)) + { + new_line(); + fprintf(_where, "..."); + i = GB.Array.Count(object) - 1; + } + new_line(); + GB_DEBUG.GetArrayValue(object, i, (GB_VALUE *)&value); + fprintf(_where, "[%d] = ", i); + print_value(&value); + } + } +*/ +} + +static void print_value(VALUE *value) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + VALUE conv; + char *addr; + int len; + + //*more = FALSE; + + if (_level >= 4) + { + fprintf(_where, "..."); + return; + } + + _level++; + +__CONV: + + if (TYPE_is_object(value->type)) + goto __OBJECT; + else + goto *jump[value->type]; + +__NULL: + + fprintf(_where, "Null"); + goto __RETURN; + +__BOOLEAN: + + fprintf(_where, value->_boolean.value ? "True" : "False"); + goto __RETURN; + +__BYTE: +__SHORT: +__INTEGER: + + fprintf(_where, "%d", value->_integer.value); + goto __RETURN; + +__LONG: + + fprintf(_where, "%" PRId64, value->_long.value); + goto __RETURN; + +__DATE: + + GB_DEBUG.FormatDate(GB.SplitDate((GB_DATE *)value), LF_STANDARD, NULL, 0, &addr, &len); + goto __PRINT; + +__SINGLE: + + GB_DEBUG.FormatNumber(value->_single.value, LF_SHORT_NUMBER, NULL, 0, &addr, &len, TRUE); + goto __PRINT; + +__FLOAT: + + GB_DEBUG.FormatNumber(value->_float.value, LF_STANDARD, NULL, 0, &addr, &len, TRUE); + +__PRINT: + + fprintf(_where, "%.*s", (int)len, addr); + goto __RETURN; + +__STRING: + + print_string(value->_string.addr + value->_string.start, value->_string.len, TRUE); + goto __RETURN; + +__OBJECT: + + if (!value->_object.object) + goto __NULL; + + //*more = !CLASS_is_native(OBJECT_class(value->_object.object)); + + print_object(value->_object.object); + goto __RETURN; + +__VARIANT: + + conv = *value; + value = &conv; + GB.Conv((GB_VALUE *)value, (GB_TYPE)value->_variant.vtype); + //VARIANT_undo(value); + goto __CONV; + +__VOID: + + fprintf(_where, "Void"); + goto __RETURN; + +__CLASS: + + { + CLASS *class = value->_class.class; + //*more = (!CLASS_is_native(class) && class->load->n_stat > 0); + + fprintf(_where, "(%s)", class->name); + goto __RETURN; + } + +__POINTER: + + if (!value->_pointer.value) + goto __NULL; + + fprintf(_where, "(%p)", value->_pointer.value); + goto __RETURN; + +__FUNCTION: + + fprintf(_where, "Function"); + goto __RETURN; + +__RETURN: + _level--; +} + + +void PRINT_value(FILE *where, VALUE *value, bool format) +{ + char *pval; + int lpval; + + _where = where; + + if (format) + { + _level = 0; + print_value(value); + } + else + { + GB_DEBUG.ToString((GB_VALUE *)value, &pval, &lpval); + print_string(pval, lpval, FALSE); + } +} + +void PRINT_symbol(FILE *where, const char *sym, int len) +{ + GB_VALUE value; + + _where = where; + + if (GB_DEBUG.GetValue(sym, len, (GB_VARIANT *)(void *)&value)) + { + fprintf(_where, "Unknown symbol"); + return; + } + + GB.BorrowValue(&value); + print_value((VALUE *)&value); + GB.ReleaseValue(&value); +} + +static void print_key(char *key, int len) +{ + fprintf(_where, " "); + print_string(key, len, TRUE); +} + +void PRINT_object(FILE *where, VALUE *value) +{ + VALUE conv; + void *object; + int index; + int count = 0; + CLASS *class, *real_class; + CLASS_DESC_SYMBOL *cd; + char *key; + int len; + bool static_class; + int access; + int *dim; + + _where = where; + + if (value->type == T_VARIANT) + { + conv = *value; + value = &conv; + GB.Conv((GB_VALUE *)value, (GB_TYPE)value->_variant.vtype); + } + + if (value->type < T_OBJECT && value->type != T_CLASS) + { + //fprintf(_where, "\n"); + return; + } + + real_class = NULL; + + if (value->type == T_CLASS) + { + object = value->_class.class; + class = (CLASS *)object; + static_class = TRUE; + real_class = NULL; + } + else + { + object = value->_object.object; + real_class = (CLASS *)GB.GetClass(object); + if (value->type == T_OBJECT) + class = real_class; + else + class = value->_object.class; + + if (real_class == class) + real_class = NULL; + + /*if (value->type != T_OBJECT && value->_object.class->is_virtual) + { + } + else + class = (CLASS *)GB.GetClass(object);*/ + //fprintf(stderr, "PRINT_object: %s %s\n", class->name, value->_object.class->name); + static_class = FALSE; + } + + if (!object) + return; + + fprintf(_where, "%s ", class->name); + if (real_class) fprintf(_where, "%s", real_class->name); + fputc(' ', _where); + + access = GB_DEBUG.GetObjectAccessType(object, class, &count); + + if (access == GB_DEBUG_ACCESS_COLLECTION) + { + fprintf(_where, "C: [%d]", count); + + GB_DEBUG.EnumKeys(object, print_key); + fprintf(_where, " "); + } + else if (GB.Is(object, GB.FindClass("Array"))) + { + dim = GB_DEBUG.GetArrayBounds(object); + if (!dim) + fprintf(_where, "A: [%d] ", count); + else + { + fprintf(_where, "A: ["); + for(;;) + { + len = *dim++; + if (len > 0) + fprintf(_where, "%d,", len); + else + { + fprintf(_where, "%d", -len); + break; + } + } + fprintf(_where, "] "); + } + } + else if (access == GB_DEBUG_ACCESS_ARRAY) + { + fprintf(_where, "A: [%d] ", count); + } + + if (!class->is_virtual && real_class) + class = real_class; + + fprintf(_where, "S:"); + + index = 0; + + for(;;) + { + cd = (CLASS_DESC_SYMBOL *)GB_DEBUG.GetNextSortedSymbol(class, &index); + if (!cd) + break; + key = cd->name; + if (cd->len == 0 || (cd->len == 1 && key[0] == '.')) + continue; + + switch(CLASS_DESC_get_type(cd->desc)) + { + case CD_STATIC_VARIABLE: + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + case CD_CONSTANT: + fprintf(_where, " %.*s", cd->len, key); + break; + } + } + + if (count > 0 && static_class) + fprintf(_where, " [%d]", count); + + fprintf(_where, " D:"); + + if (!static_class) + { + index = 0; + + for(;;) + { + cd = (CLASS_DESC_SYMBOL *)GB_DEBUG.GetNextSortedSymbol(class, &index); + if (!cd) + break; + key = cd->name; + if (cd->len == 0 || (cd->len == 1 && key[0] == '.')) + continue; + + switch(CLASS_DESC_get_type(cd->desc)) + { + case CD_VARIABLE: + case CD_PROPERTY: + case CD_PROPERTY_READ: + case CD_STRUCT_FIELD: + fprintf(_where, " %.*s", cd->len, key); + break; + } + } + + if (count > 0) + fprintf(_where, " [%d]", count); + } + + //fprintf(_where, "\n"); +} diff --git a/main/lib/debug/print.h b/main/lib/debug/print.h new file mode 100644 index 00000000..a71025c5 --- /dev/null +++ b/main/lib/debug/print.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + print.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __PRINT_H +#define __PRINT_H + +#include + +#include "gambas.h" +#include "main.h" +#include "gb.debug.h" +#include "gbx_value.h" +#include "debug.h" + +PUBLIC void PRINT_value(FILE *where, VALUE *value, bool format); +PUBLIC void PRINT_object(FILE *where, VALUE *value); +PUBLIC void PRINT_symbol(FILE *where, const char *sym, int len); + +#endif diff --git a/main/lib/debug/profile.c b/main/lib/debug/profile.c new file mode 100644 index 00000000..fd6f768f --- /dev/null +++ b/main/lib/debug/profile.c @@ -0,0 +1,246 @@ +/*************************************************************************** + + profile.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __PROFILE_C + +#include +#include + +#include "gambas.h" +#include "debug.h" +#include "main.h" + +// Maximum profile file is 512 Mb by default + +#define MAX_PROFILE_SIZE (512 << 20) + +static bool _init = FALSE; +static FILE *_file; +static int _last_line = 0; +static bool _new_line = TRUE; +static int _count = 0; +static size_t _max_profile_size = MAX_PROFILE_SIZE; +//static long _ticks_per_sec; + +static uint64_t get_time(void) +{ + static uint64_t last = 0; + + struct timeval time; + //struct tms time; + uint64_t t; + + gettimeofday(&time, NULL); + t = (uint64_t)time.tv_sec * 1000000 + (uint64_t)time.tv_usec - last; + + //times(&time); + //t = (uint64_t)(time.tms_utime + time.tms_stime) * 1000000 / _ticks_per_sec - last; + + last += t; + + return t; +} + +void PROFILE_init(const char *path) +{ + char *env; + size_t max; + char buffer[PATH_MAX + 1]; + + if (_init) + return; + + if (!path) + { + sprintf(buffer, ".%d.prof", getpid()); + path = buffer; + } + + //fprintf(stderr, "gb.profile: start profiling: %s\n", path); + + _file = fopen(path, "w"); + if (!_file) + { + fprintf(stderr, "gb.debug: cannot create profile file '%s': %s\n", path, strerror(errno)); + abort(); + } + + fprintf(_file, "[1]\n"); + + //_ticks_per_sec = sysconf(_SC_CLK_TCK); + //fprintf(stderr, "_ticks_per_sec = %ld\n", _ticks_per_sec); + + env = getenv("GB_PROFILE_MAX"); + if (env) + { + max = atoi(env); + if (max > 0) + { + if (max < 128) + max = 128; + else if (max > 4096) + max = 4096; + + _max_profile_size = max << 20; + } + } + + _init = TRUE; + get_time(); +} + +void PROFILE_cancel(void) +{ + if (_init) + { + close(fileno(_file)); + _init = FALSE; + } +} + +void PROFILE_exit(void) +{ + if (!_init) + return; + + //fprintf(stderr, "gb.profile: stop profiling\n"); + + if (!_new_line) + fputc('\n', _file); + + fclose(_file); +} + +static void check_size() +{ + _count = 0; + if (ftell(_file) > _max_profile_size) + { + fprintf(stderr, "gb.debug: maximum profile size reached\n"); + PROFILE_exit(); + abort(); + } +} + + +#define CODE(n) (n + '9' + 1) + +static void add_line(ushort line, uint64_t time) +{ + int n; + char buf[32], num[32]; + char *p; + + n = line - _last_line; + p = buf; + + if (n >= -9 && n <= 9) + *p++ = CODE(0 + n + 9); + else if (n >= -99 && n <= 99) + { + *p++ = n > 0 ? CODE(19) : CODE(20); + *p++ = CODE(0) + abs(n) - 10; + } + else + { + *p++ = n > 0 ? CODE(21) : CODE(22); + n = sprintf(num, "%d", abs(n)); + *p++ = CODE(n); + strcpy(p, num); + p += n; + } + + if (time <= 9) + *p++ = CODE(time); + else + { + n = sprintf(num, "%" PRIu64, time); + *p++ = CODE(10 + n - 2); + strcpy(p, num); + p += n; + } + + *p = 0; + + fputs(buf, _file); + + _last_line = line; + _new_line = FALSE; + _count++; + if ((_count & 0xFFFFF) == 0) + check_size(); +} + +void PROFILE_add(void *cp, void *fp, void *pc) +{ + uint64_t time = get_time(); + ushort line; + + line = 0; + if (_count && DEBUG_calc_line_from_position(cp, fp, pc, &line)) + return; + + add_line(line, time); +} + +void PROFILE_begin(void *cp, void *fp) +{ + uint64_t time = get_time(); + const char *where = cp ? DEBUG_get_profile_position(cp, fp, NULL) : "0"; + + if (!_new_line) + fputc('\n', _file); + fprintf(_file, "(%s %" PRId64 "\n", where, time); + _last_line = 0; + _new_line = TRUE; + + _count++; + if ((_count & 0xFFFFF) == 0) + check_size(); + /*if (cp && fp) + { + FUNCTION *ffp = (FUNCTION *)fp; + add_line(ffp->debug->line, time); + }*/ +} + +void PROFILE_end(void *cp, void *fp) +{ + uint64_t time = get_time(); + //const char *where; + + if (cp && fp) + { + FUNCTION *ffp = (FUNCTION *)fp; + if (ffp->debug) + add_line(ffp->debug->line + ffp->debug->nline, time); + } + + //where = cp ? DEBUG_get_position(cp, fp, NULL) : ".System.EventLoop"; + + if (!_new_line) + fputc('\n', _file); + fprintf(_file, ")\n"); + _last_line = 0; + _new_line = TRUE; +} diff --git a/main/lib/debug/profile.h b/main/lib/debug/profile.h new file mode 100644 index 00000000..e41a39e4 --- /dev/null +++ b/main/lib/debug/profile.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + profile.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __PROFILE_H +#define __PROFILE_H + +void PROFILE_init(void); +void PROFILE_exit(void); +void PROFILE_add(void *cp, void *fp, void *pc); +void PROFILE_begin(void *cp, void *fp); +void PROFILE_end(void *cp, void *fp); +void PROFILE_cancel(void); + +#endif diff --git a/main/lib/draw/Makefile.am b/main/lib/draw/Makefile.am new file mode 100644 index 00000000..ff1fe8fb --- /dev/null +++ b/main/lib/draw/Makefile.am @@ -0,0 +1,15 @@ +EXTRA_DIST = gb.image.h gb.geom.h + +gblib_LTLIBRARIES = gb.draw.la + +gb_draw_la_LIBADD = @MATH_LIB@ +gb_draw_la_LDFLAGS = -module @LD_FLAGS@ +gb_draw_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_draw_la_SOURCES = \ + gb.draw.h gb.paint.h \ + gb_list.c \ + cpaint.h cpaint.c \ + main.h main.c + + diff --git a/main/lib/draw/cdraw.c b/main/lib/draw/cdraw.c new file mode 100644 index 00000000..506bbc1e --- /dev/null +++ b/main/lib/draw/cdraw.c @@ -0,0 +1,1309 @@ +/*************************************************************************** + + cdraw.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_C + +#include "gb.image.h" +#include "matrix.h" +#include "main.h" +#include "crect.h" +#include "cpaint.h" +#include "cdraw.h" + +static GB_DRAW *_current = NULL; +#define THIS _current +#define DRAW _current->desc +#define THIS_MATRIX ((MATRIX *)&(THIS->matrix)) +#define CHECK_DEVICE() if (check_device()) return + +static bool check_device() +{ + if (!_current || !_current->opened) + { + GB.Error("No current device"); + return TRUE; + } + else + return FALSE; +} + +GB_DRAW *DRAW_from_device(void *device) +{ + GB_DRAW *d; + + for (d = _current; d; d = d->previous) + { + if (d->device == device && d->opened) + break; + } + + return d; +} + +PUBLIC GB_DRAW *DRAW_get_current() +{ + check_device(); + return _current; +} + +bool DRAW_open(GB_DRAW *draw) +{ + if (draw->opened) + return FALSE; + + //fprintf(stderr, "DRAW_open: %p\n", draw); + GB.Alloc(POINTER(&draw->extra), draw->desc->size); + memset(draw->extra, 0, draw->desc->size); + + draw->opened = !draw->desc->Begin(draw); + return !draw->opened; +} + +void DRAW_close(GB_DRAW *draw) +{ + if (draw->opened) + { + //fprintf(stderr, "DRAW_close: %p\n", draw); + draw->desc->End(draw); + GB.Free(POINTER(&draw->extra)); + draw->opened = FALSE; + } +} + +bool DRAW_begin(void *device) +{ + GB_DRAW_DESC *desc; + GB_DRAW *draw; + GB_CLASS klass; + GB_DRAW *other; + + klass = GB.GetClass(device); + if (klass == GB.FindClass("Class")) + { + klass = (GB_CLASS)device; + desc = (GB_DRAW_DESC *)GB.GetClassInterface(klass, "StaticDraw"); + } + else + { + desc = (GB_DRAW_DESC *)GB.GetClassInterface(klass, "Draw"); + } + + if (!desc) + { + GB.Error("Not a drawable object"); + return TRUE; + } + + GB.Alloc(POINTER(&draw), sizeof(GB_DRAW)); + + other = DRAW_from_device(device); + + draw->desc = desc; + GB.Ref(device); + draw->device = device; + MATRIX_init(&draw->matrix); + draw->save = NULL; + draw->opened = FALSE; + draw->xform = FALSE; + + draw->previous = _current; + _current = draw; + + draw->paint = PAINT_from_device(device); + if (draw->paint) + PAINT_close(draw->paint); + + if (other) + { + draw->extra = other->extra; + } + else + { + if (DRAW_open(draw)) + return TRUE; + + DRAW->SetBackground(draw, GB_DRAW_COLOR_DEFAULT); + DRAW->SetForeground(draw, GB_DRAW_COLOR_DEFAULT); + DRAW->Fill.SetColor(draw, GB_DRAW_COLOR_DEFAULT); + } + + return FALSE; +} + + +void DRAW_end() +{ + GB_DRAW *draw; + + if (!_current) + return; + + draw = _current; + _current = _current->previous; + + DRAW_close(draw); + + if (draw->paint) + PAINT_open(draw->paint); + + GB.Unref(POINTER(&draw->device)); + GB.Free(POINTER(&draw)); +} + + +BEGIN_METHOD(CDRAW_begin, GB_OBJECT device) + + void *device = VARG(device); + + if (GB.CheckObject(device)) + return; + + DRAW_begin(device); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_end) + + DRAW_end(); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_exit) + + while (_current) + DRAW_end(); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_save) + + CHECK_DEVICE(); + DRAW->Save(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_restore) + + CHECK_DEVICE(); + DRAW->Restore(THIS); + +END_METHOD + +BEGIN_PROPERTY(CDRAW_device) + + if (THIS) + GB.ReturnObject(THIS->device); + else + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_width) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->width); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_height) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->height); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_resolution) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->resolution); + +END_PROPERTY + +static void handle_int_property(void *_param, int (*get)(GB_DRAW *), void (*set)(GB_DRAW *, int)) +{ + if (READ_PROPERTY) + GB.ReturnInteger((*get)(THIS)); + else + (*set)(THIS, VPROP(GB_INTEGER)); +} + +BEGIN_PROPERTY(CDRAW_background) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->GetBackground, DRAW->SetBackground); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_foreground) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->GetForeground, DRAW->SetForeground); + +END_PROPERTY + +BEGIN_PROPERTY(Draw_ClipRect) + + CRECT *rect; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + if (!DRAW->Clip.IsEnabled(THIS)) + { + GB.ReturnNull(); + return; + } + + rect = CRECT_create(); + DRAW->Clip.Get(THIS, &rect->x, &rect->y, &rect->w, &rect->h); + GB.ReturnObject(rect); + } + else + { + rect = (CRECT *)VPROP(GB_OBJECT); + DRAW->Clip.SetEnabled(THIS, FALSE); + if (rect) + DRAW->Clip.Set(THIS, rect->x, rect->y, rect->w, rect->h); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_clip_x) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, &val, NULL, NULL, NULL); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_y) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, NULL, &val, NULL, NULL); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_w) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, NULL, NULL, &val, NULL); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_h) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, NULL, NULL, NULL, &val); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_enabled) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnBoolean(DRAW->Clip.IsEnabled(THIS)); + else + DRAW->Clip.SetEnabled(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD(CDRAW_clip, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CHECK_DEVICE(); + DRAW->Clip.Set(THIS, VARG(x), VARG(y), VARG(w), VARG(h)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_invert) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnBoolean(DRAW->IsInverted(THIS)); + else + DRAW->SetInverted(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_transparent) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnBoolean(DRAW->IsTransparent(THIS)); + else + DRAW->SetTransparent(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_font) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(DRAW->GetFont(THIS)); + else + { + if (GB.CheckObject(VPROP(GB_OBJECT))) + return; + DRAW->SetFont(THIS, VPROP(GB_OBJECT)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_line_width) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Line.GetWidth, DRAW->Line.SetWidth); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_line_style) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Line.GetStyle, DRAW->Line.SetStyle); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_color) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Fill.GetColor, DRAW->Fill.SetColor); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_style) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Fill.GetStyle, DRAW->Fill.SetStyle); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_x) + + int x, y; + + CHECK_DEVICE(); + + DRAW->Fill.GetOrigin(THIS, &x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(x); + else + DRAW->Fill.SetOrigin(THIS, VPROP(GB_INTEGER), y); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_y) + + int x, y; + + CHECK_DEVICE(); + + DRAW->Fill.GetOrigin(THIS, &x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(y); + else + DRAW->Fill.SetOrigin(THIS, x, VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD(CDRAW_point, GB_INTEGER x; GB_INTEGER y) + + int x, y; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + + if (THIS->xform) + MATRIX_map_point(THIS_MATRIX, &x, &y); + + DRAW->Draw.Point(THIS, x, y); + +END_METHOD + + +BEGIN_METHOD(CDRAW_line, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2) + + int x1, y1, x2, y2; + + CHECK_DEVICE(); + + x1 = VARG(x1); + y1 = VARG(y1); + x2 = VARG(x2); + y2 = VARG(y2); + + if (THIS->xform) + { + MATRIX_map_point(THIS_MATRIX, &x1, &y1); + MATRIX_map_point(THIS_MATRIX, &x2, &y2); + } + + DRAW->Draw.Line(THIS, x1, y1, x2, y2); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (w < 0) + x += w, w = (-w); + if (h < 0) + y += h, h = (-h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + DRAW->Draw.Rect(THIS, x, y, w, h); + +END_METHOD + +BEGIN_METHOD(CDRAW_fill_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER color) + + int x, y, w, h; + int fill_style, line_style, fill_color; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (w < 0) + x += w, w = (-w); + if (h < 0) + y += h, h = (-h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + line_style = DRAW->Line.GetStyle(THIS); + DRAW->Line.SetStyle(THIS, 0); + fill_style = DRAW->Fill.GetStyle(THIS); + DRAW->Fill.SetStyle(THIS, 1); + fill_color = DRAW->Fill.GetColor(THIS); + if (!MISSING(color)) + DRAW->Fill.SetColor(THIS, VARG(color)); + + DRAW->Draw.Rect(THIS, x, y, w, h); + + DRAW->Line.SetStyle(THIS, line_style); + DRAW->Fill.SetStyle(THIS, fill_style); + DRAW->Fill.SetColor(THIS, fill_color); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_clear) + + int fill_style, line_style, fill_color, bg_color; + + CHECK_DEVICE(); + + line_style = DRAW->Line.GetStyle(THIS); + DRAW->Line.SetStyle(THIS, 0); + fill_style = DRAW->Fill.GetStyle(THIS); + DRAW->Fill.SetStyle(THIS, 1); + bg_color = DRAW->GetBackground(THIS); + DRAW->SetBackground(THIS, GB_DRAW_COLOR_DEFAULT); + fill_color = DRAW->Fill.GetColor(THIS); + DRAW->Fill.SetColor(THIS, DRAW->GetBackground(THIS)); + + DRAW->Draw.Rect(THIS, 0, 0, THIS->width, THIS->height); + + DRAW->Line.SetStyle(THIS, line_style); + DRAW->Fill.SetStyle(THIS, fill_style); + DRAW->SetBackground(THIS, bg_color); + DRAW->Fill.SetColor(THIS, fill_color); + +END_METHOD + +// BEGIN_METHOD(CDRAW_round_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_FLOAT round) +// +// CHECK_DEVICE(); +// DRAW->Draw.RoundRect(THIS, VARG(x), VARG(y), VARG(w), VARG(h), VARGOPT(round, 0.25)); +// +// END_METHOD + + +BEGIN_METHOD(CDRAW_ellipse, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_FLOAT start; GB_FLOAT end) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + DRAW->Draw.Ellipse(THIS, x, y, w, h, VARGOPT(start, 0.0), VARGOPT(end, 0.0)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_arc, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_FLOAT start; GB_FLOAT end) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + DRAW->Draw.Arc(THIS, x, y, w, h, VARGOPT(start, 0.0), VARGOPT(end, 0.0)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_circle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER radius; GB_FLOAT start; GB_FLOAT end) + + int x, y, w, h; + int radius; + + CHECK_DEVICE(); + + radius = VARG(radius); + if (radius <= 0) + return; + + x = VARG(x) - radius + 1; + y = VARG(y) - radius + 1; + w = radius * 2 - 1; + h = radius * 2 - 1; + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.Ellipse(THIS, x, y, w, h, VARGOPT(start, 0.0), VARGOPT(end, 0.0)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER align) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + if (THIS->xform) + { + if (w < 0 || h < 0) + MATRIX_map_point(THIS_MATRIX, &x, &y); + else + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + } + + DRAW->Draw.Text(THIS, STRING(text), LENGTH(text), x, y, w, h, VARGOPT(align, GB_DRAW_ALIGN_DEFAULT)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_text_width, GB_STRING text) + + int w; + CHECK_DEVICE(); + DRAW->Draw.TextSize(THIS, STRING(text), LENGTH(text), &w, NULL); + GB.ReturnInteger(w); + +END_METHOD + + +BEGIN_METHOD(CDRAW_text_height, GB_STRING text) + + int h; + CHECK_DEVICE(); + DRAW->Draw.TextSize(THIS, STRING(text), LENGTH(text), NULL, &h); + GB.ReturnInteger(h); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rich_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER align) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + if (THIS->xform) + { + if (w < 0 || h < 0) + MATRIX_map_point(THIS_MATRIX, &x, &y); + else + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + } + + DRAW->Draw.RichText(THIS, STRING(text), LENGTH(text), x, y, w, h, VARGOPT(align, GB_DRAW_ALIGN_DEFAULT)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rich_text_width, GB_STRING text) + + int w; + CHECK_DEVICE(); + DRAW->Draw.RichTextSize(THIS, STRING(text), LENGTH(text), -1, &w, NULL); + GB.ReturnInteger(w); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rich_text_height, GB_STRING text; GB_INTEGER width) + + int w = VARGOPT(width, -1); + int h; + CHECK_DEVICE(); + DRAW->Draw.RichTextSize(THIS, STRING(text), LENGTH(text), w, NULL, &h); + GB.ReturnInteger(h); + +END_METHOD + + +BEGIN_METHOD(CDRAW_polyline, GB_OBJECT points) + + uint n; + GB_ARRAY points = VARG(points); + int *coord; + + CHECK_DEVICE(); + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + coord = (int *)GB.Array.Get(points, 0); + + if (THIS->xform) + coord = MATRIX_map_array(THIS_MATRIX, coord, n); + + DRAW->Draw.Polyline(THIS, n, coord); + + if (THIS->xform) + MATRIX_free_array(&coord); + +END_METHOD + + +BEGIN_METHOD(CDRAW_polygon, GB_OBJECT points) + + uint n; + GB_ARRAY points = VARG(points); + int *coord; + + CHECK_DEVICE(); + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + coord = (int *)GB.Array.Get(points, 0); + + if (THIS->xform) + coord = MATRIX_map_array(THIS_MATRIX, coord, n); + + DRAW->Draw.Polygon(THIS, n, coord); + + if (THIS->xform) + MATRIX_free_array(&coord); + +END_METHOD + + +BEGIN_METHOD(CDRAW_picture, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + GB_PICTURE picture = VARG(picture); + GB_PICTURE_INFO info; + int x, y, w, h; + + CHECK_DEVICE(); + + if (GB.CheckObject(picture)) + return; + + DRAW->GetPictureInfo(THIS, picture, &info); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, info.width); + h = VARGOPT(h, info.height); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.Picture(THIS, picture, + x, y, w, h, + VARGOPT(sx, 0), VARGOPT(sy, 0), VARGOPT(sw, -1), VARGOPT(sh, -1)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_image, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + GB_IMAGE image = VARG(image); + int x, y, w, h; + GB_IMG *info = (GB_IMG *)image; + + CHECK_DEVICE(); + + if (GB.CheckObject(image)) + return; + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, info->width); + h = VARGOPT(h, info->height); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.Image(THIS, image, + x, y, w, h, + VARGOPT(sx, 0), VARGOPT(sy, 0), VARGOPT(sw, -1), VARGOPT(sh, -1)); + +END_METHOD + +static uint blend_color(uint col, uint gray, uint alpha) +{ + gray *= (0xFF - alpha); + + return + ((col & 0xFF) * alpha + gray) >> 8 + | ((((col >> 8) & 0xFF) * alpha + gray) >> 8) << 8 + | ((((col >> 16) & 0xFF) * alpha + gray) >> 8) << 16; +} + +BEGIN_METHOD(CDRAW_zoom, GB_OBJECT image; GB_INTEGER zoom; GB_INTEGER x; GB_INTEGER y; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + GB_IMAGE image = VARG(image); + GB_IMG *info = (GB_IMG *)image; + int zoom, size, size2; + int x, y, sx, sy, sw, sh; + int i, j, xr, yr; + uint col, col1, col2, last_col; + bool border; + int fill_style, fill_color; + uint a; + bool opaque; + + CHECK_DEVICE(); + + if (GB.CheckObject(image)) + return; + + zoom = VARG(zoom); + if (zoom < 1) + { + GB.Error("Bad zoom factor"); + return; + } + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, info->width); + sh = VARGOPT(sh, info->height); + + DRAW_NORMALIZE(x, y, sw, sh, sx, sy, sw, sh, info->width, info->height); + + //DRAW->Fill.GetOrigin(THIS, &ox, &oy); + + border = DRAW->Line.GetStyle(THIS); + + if (zoom < 6 && !border) + { + DRAW->Fill.SetStyle(THIS, 1); + DRAW->Fill.SetColor(THIS, 0x979797); + DRAW->Draw.Rect(THIS, x, y, sw * zoom, sh * zoom); + DRAW->Draw.Image(THIS, image, x, y, sw * zoom, sh * zoom, sx, sy, sw, sh); + } + else + { + //IMAGE.Convert(info, GB_IMAGE_BGRA); + // May have to convert the image data + //if (info->format != GB_IMAGE_BGRA && info->format != GB_IMAGE_BGRX) + // GB.Alloc(POINTER(&conv), sw * sizeof(int)); + + size = zoom; + size2 = size / 2; + //if (border) size++; + + fill_style = DRAW->Fill.GetStyle(THIS); + fill_color = DRAW->Fill.GetColor(THIS); + + //last_style = -1; + //style = border; + last_col = 0; + col1 = 0; + col2 = 0; + a = 0xFF; + opaque = TRUE; + + DRAW->Fill.SetStyle(THIS, 1); // FILL_SOLID + DRAW->Fill.SetColor(THIS, 0); + + for (j = sy, yr = y; j < (sy + sh); j++, yr += zoom) + { + for (i = sx, xr = x; i < (sx + sw); i++, xr += zoom) + { + col = IMAGE.GetPixel(info, i, j); + + if (col != last_col) + { + last_col = col; + + a = (col >> 24) ^ 0xFF; + col &= 0xFFFFFF; + + if (a < 0xFF) + { + if (a == 0) + { + col1 = 0x808080; + col2 = 0xC0C0C0; + } + else + { + col1 = blend_color(col, 0x80, a); + col2 = blend_color(col, 0xC0, a); + } + opaque = FALSE; + } + else + { + opaque = TRUE; + DRAW->Fill.SetColor(THIS, col); + } + } + + if (opaque) + DRAW->Draw.Rect(THIS, xr, yr, size + 1, size + 1); + else + { + DRAW->Fill.SetColor(THIS, col1); + DRAW->Line.SetStyle(THIS, 0); + DRAW->Draw.Rect(THIS, xr, yr, size, size); + DRAW->Fill.SetColor(THIS, col2); + DRAW->Draw.Rect(THIS, xr + size2, yr, size - size2, size2); + DRAW->Draw.Rect(THIS, xr, yr + size2, size2, size - size2); + DRAW->Line.SetStyle(THIS, border); + if (border && a >= 16) + { + DRAW->Fill.SetStyle(THIS, 0); + DRAW->Draw.Rect(THIS, xr, yr, size + 1, size + 1); + DRAW->Fill.SetStyle(THIS, 1); + } + } + + } + } + + DRAW->Fill.SetStyle(THIS, fill_style); + DRAW->Fill.SetColor(THIS, fill_color); + } + +END_METHOD + + +BEGIN_METHOD(CDRAW_tile, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + GB_PICTURE picture = VARG(picture); + + CHECK_DEVICE(); + + if (GB.CheckObject(picture)) + return; + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.TiledPicture(THIS, picture, x, y, w, h); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_reset) + + CHECK_DEVICE(); + MATRIX_reset(THIS_MATRIX); + THIS->xform = FALSE; + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_push) + + MATRIX *save; + + CHECK_DEVICE(); + + GB.Alloc(POINTER(&save), sizeof(MATRIX)); + *save = *THIS_MATRIX; + save->next = THIS->save; + THIS->save = save; + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_pop) + + MATRIX *save; + + CHECK_DEVICE(); + + if (!THIS->save) + { + MATRIX_reset(THIS_MATRIX); + return; + } + + save = THIS->save; + THIS->save = save->next; + *THIS_MATRIX = *save; + + GB.Free(POINTER(&save)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_translate, GB_FLOAT dx; GB_FLOAT dy) + + double dx = VARG(dx); + double dy = VARG(dy); + + CHECK_DEVICE(); + MATRIX_translate(THIS_MATRIX, dx, dy); + THIS->xform = !THIS->matrix.identity; + +END_METHOD + + +BEGIN_METHOD(CDRAW_scale, GB_FLOAT dx; GB_FLOAT dy) + + double dx = VARG(dx); + double dy = VARG(dy); + + CHECK_DEVICE(); + MATRIX_scale(THIS_MATRIX, dx, dy); + THIS->xform = !THIS->matrix.identity; + +END_METHOD + + +// BEGIN_METHOD(CDRAW_rotate, GB_FLOAT a) +// +// double a = VARG(a); +// +// DP->rotate(a); +// if (DPM) +// DPM->rotate(a); +// +// END_METHOD + +/**************************************************************************** + + Style API + +****************************************************************************/ + +#define GET_COORD() \ + int x, y, w, h; \ +\ + CHECK_DEVICE(); \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (THIS->xform) \ + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); \ +\ + if (w < 1 || h < 1) \ + return; + + +BEGIN_METHOD(CDRAW_style_arrow, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER type; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Arrow(THIS, x, y, w, h, VARG(type), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_check, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER value; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Check(THIS, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_option, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Option(THIS, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_separator, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Separator(THIS, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_button, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state; GB_BOOLEAN flat) + + GET_COORD(); + DRAW->Style.Button(THIS, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(flat, FALSE)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_panel, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER border; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Panel(THIS, x, y, w, h, VARG(border), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_handle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Handle(THIS, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(DrawStyle_Box, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Box(THIS, x, y, w, h, VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +GB_DESC CDrawClipDesc[] = +{ + GB_DECLARE(".Draw.Clip", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("X", "i", CDRAW_clip_x), + GB_STATIC_PROPERTY_READ("Y", "i", CDRAW_clip_y), + GB_STATIC_PROPERTY_READ("W", "i", CDRAW_clip_w), + GB_STATIC_PROPERTY_READ("H", "i", CDRAW_clip_h), + GB_STATIC_PROPERTY_READ("Width", "i", CDRAW_clip_w), + GB_STATIC_PROPERTY_READ("Height", "i", CDRAW_clip_h), + + GB_STATIC_PROPERTY("Enabled", "b", CDRAW_clip_enabled), + GB_STATIC_METHOD("_call", NULL, CDRAW_clip, "(X)i(Y)i(Width)i(Height)i"), + + GB_END_DECLARE +}; + +GB_DESC CDrawStyleDesc[] = +{ + GB_DECLARE(".Draw.Style", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Arrow", NULL, CDRAW_style_arrow, "(X)i(Y)i(Width)i(Height)i(Type)i[(Flag)i]"), + GB_STATIC_METHOD("Check", NULL, CDRAW_style_check, "(X)i(Y)i(Width)i(Height)i(Value)i[(Flag)i]"), + GB_STATIC_METHOD("Option", NULL, CDRAW_style_option, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i]"), + GB_STATIC_METHOD("Separator", NULL, CDRAW_style_separator, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("Button", NULL, CDRAW_style_button, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i(Flat)b]"), + GB_STATIC_METHOD("Panel", NULL, CDRAW_style_panel, "(X)i(Y)i(Width)i(Height)i(Border)i[(Flag)i]"), + GB_STATIC_METHOD("Handle", NULL, CDRAW_style_handle, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("Box", NULL, DrawStyle_Box, "(X)i(Y)i(Width)i(Height)i[(Flag)i]"), + + GB_END_DECLARE +}; + +GB_DESC CDrawDesc[] = +{ + GB_DECLARE("Draw", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, CDRAW_exit, NULL), + + GB_STATIC_METHOD("Begin", NULL, CDRAW_begin, "(Device)o"), + GB_STATIC_METHOD("End", NULL, CDRAW_end, NULL), + + GB_STATIC_METHOD("Save", NULL, CDRAW_save, NULL), + GB_STATIC_METHOD("Restore", NULL, CDRAW_restore, NULL), + + GB_STATIC_PROPERTY_READ("Device", "o", CDRAW_device), + + GB_STATIC_PROPERTY_READ("W", "i", CDRAW_width), + GB_STATIC_PROPERTY_READ("H", "i", CDRAW_height), + GB_STATIC_PROPERTY_READ("Width", "i", CDRAW_width), + GB_STATIC_PROPERTY_READ("Height", "i", CDRAW_height), + GB_STATIC_PROPERTY_READ("Resolution", "i", CDRAW_resolution), + + GB_STATIC_PROPERTY("ClipRect", "Rect", Draw_ClipRect), + GB_STATIC_PROPERTY_SELF("Clip", ".Draw.Clip"), + GB_STATIC_PROPERTY_SELF("Style", ".Draw.Style"), + + GB_STATIC_PROPERTY("Background", "i", CDRAW_background), + GB_STATIC_PROPERTY("Foreground", "i", CDRAW_foreground), + //GB_STATIC_PROPERTY("BackColor", "i", CDRAW_background), + //GB_STATIC_PROPERTY("ForeColor", "i", CDRAW_foreground), + + GB_STATIC_PROPERTY("Invert", "b", CDRAW_invert), + GB_STATIC_PROPERTY("Transparent", "b", CDRAW_transparent), + + GB_STATIC_PROPERTY("Font", "Font", CDRAW_font), + + GB_STATIC_PROPERTY("LineWidth", "i", CDRAW_line_width), + GB_STATIC_PROPERTY("LineStyle", "i", CDRAW_line_style), + + GB_STATIC_PROPERTY("FillColor", "i", CDRAW_fill_color), + GB_STATIC_PROPERTY("FillStyle", "i", CDRAW_fill_style), + GB_STATIC_PROPERTY("FillX", "i", CDRAW_fill_x), + GB_STATIC_PROPERTY("FillY", "i", CDRAW_fill_y), + + GB_STATIC_METHOD("Clear", NULL, CDRAW_clear, NULL), + GB_STATIC_METHOD("Point", NULL, CDRAW_point, "(X)i(Y)i"), + GB_STATIC_METHOD("Line", NULL, CDRAW_line, "(X1)i(Y1)i(X2)i(Y2)i"), + GB_STATIC_METHOD("Rect", NULL, CDRAW_rect, "(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("FillRect", NULL, CDRAW_fill_rect, "(X)i(Y)i(Width)i(Height)i[(Color)i]"), + //GB_STATIC_METHOD("RoundRect", NULL, CDRAW_round_rect, "(X)i(Y)i(Width)i(Height)i[(Round)f]"), + GB_STATIC_METHOD("Circle", NULL, CDRAW_circle, "(X)i(Y)i(Radius)i[(Start)f(End)f]"), + GB_STATIC_METHOD("Arc", NULL, CDRAW_arc, "(X)i(Y)i(Width)i(Height)i[(Start)f(End)f]"), + GB_STATIC_METHOD("Ellipse", NULL, CDRAW_ellipse, "(X)i(Y)i(Width)i(Height)i[(Start)f(End)f]"), + GB_STATIC_METHOD("Text", NULL, CDRAW_text, "(Text)s(X)i(Y)i[(Width)i(Height)i(Alignment)i)]"), + GB_STATIC_METHOD("TextWidth", "i", CDRAW_text_width, "(Text)s"), + GB_STATIC_METHOD("TextHeight", "i", CDRAW_text_height, "(Text)s"), + GB_STATIC_METHOD("RichText", NULL, CDRAW_rich_text, "(Text)s(X)i(Y)i[(Width)i(Height)i(Alignment)i)]"), + GB_STATIC_METHOD("RichTextWidth", "i", CDRAW_rich_text_width, "(Text)s"), + GB_STATIC_METHOD("RichTextHeight", "i", CDRAW_rich_text_height, "(Text)s[(Width)i]"), + GB_STATIC_METHOD("Polyline", NULL, CDRAW_polyline, "(Points)Integer[]"), + GB_STATIC_METHOD("Polygon", NULL, CDRAW_polygon, "(Points)Integer[]"), + + GB_STATIC_METHOD("Picture", NULL, CDRAW_picture, "(Picture)Picture;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + //GB_STATIC_METHOD("AlignedPicture", NULL, CDRAW_picture, "(Picture)Picture;(X)i(Y)i(Width)i(Height)i(Alignment)i"), + GB_STATIC_METHOD("Tile", NULL, CDRAW_tile, "(Picture)Picture;(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("Image", NULL, CDRAW_image, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + //GB_STATIC_METHOD("AlignedImage", NULL, CDRAW_picture, "(Image)Image;(X)i(Y)i(Width)i(Height)i(Alignment)i"), + GB_STATIC_METHOD("Zoom", NULL, CDRAW_zoom, "(Image)Image;(Zoom)i(X)i(Y)i[(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_STATIC_METHOD("Reset", NULL, CDRAW_reset, NULL), + GB_STATIC_METHOD("Push", NULL, CDRAW_push, NULL), + GB_STATIC_METHOD("Pop", NULL, CDRAW_pop, NULL), + GB_STATIC_METHOD("Translate", NULL, CDRAW_translate, "(DX)f(DY)f"), + GB_STATIC_METHOD("Scale", NULL, CDRAW_scale, "(SX)f(SY)f"), + + GB_CONSTANT("Normal", "i", GB_DRAW_STATE_NORMAL), + GB_CONSTANT("Disabled", "i", GB_DRAW_STATE_DISABLED), + GB_CONSTANT("Focus", "i", GB_DRAW_STATE_FOCUS), + GB_CONSTANT("Hover", "i", GB_DRAW_STATE_HOVER), + //GB_CONSTANT("ToolButton", "i", GB_DRAW_STATE_TOOL_BUTTON), + #if 0 + GB_STATIC_METHOD("Drawing", NULL, CDRAW_drawing, "(Drawing)Drawing;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_STATIC_METHOD("Rotate", NULL, CDRAW_rotate, "(Angle)f"), + #endif + + GB_END_DECLARE +}; + + diff --git a/main/lib/draw/cdraw.h b/main/lib/draw/cdraw.h new file mode 100644 index 00000000..d438672a --- /dev/null +++ b/main/lib/draw/cdraw.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + cdraw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "gambas.h" +#include "gb.draw.h" + +#ifndef __CDRAW_C + +extern GB_DESC CDrawDesc[]; +extern GB_DESC CDrawClipDesc[]; +extern GB_DESC CDrawStyleDesc[]; + +#endif + +GB_DRAW *DRAW_get_current(); +GB_DRAW *DRAW_from_device(void *device); +bool DRAW_begin(void *device); +void DRAW_end(); +bool DRAW_open(GB_DRAW *draw); +void DRAW_close(GB_DRAW *draw); + +#endif diff --git a/main/lib/draw/cpaint.c b/main/lib/draw/cpaint.c new file mode 100644 index 00000000..4d6aed0f --- /dev/null +++ b/main/lib/draw/cpaint.c @@ -0,0 +1,1855 @@ +/*************************************************************************** + + cpaint.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPAINT_C + +#include "gb.image.h" +#include "main.h" +#include "cpaint.h" + +static GB_PAINT *_current = NULL; +#define THIS _current +#define PAINT _current->desc + +#define XTHIS ((PAINT_EXTENTS *)_object) + +#define MTHIS ((PAINT_MATRIX *)_object) +#define MPAINT (_matrix_desc) + +#define BTHIS ((PAINT_BRUSH *)_object) +#define BPAINT (BTHIS->desc) + +#define CHECK_DEVICE() if (check_device()) return +#define CHECK_PATH() if (check_path()) return + +#define RAD(_deg) ((_deg) * M_PI / 180) + +static GB_PAINT_MATRIX_DESC *_matrix_desc = NULL; + +static void load_matrix_interface() +{ + if (!_matrix_desc) + { + _matrix_desc = (GB_PAINT_MATRIX_DESC *)GB.GetClassInterface(GB.FindClass("Image"), "PaintMatrix"); + if (!_matrix_desc) + { + fprintf(stderr, "gb.draw: error: unable to find PaintMatrix interface\n"); + abort(); + } + } +} + +static bool check_device() +{ + if (!_current || !_current->extra) + { + GB.Error("No current device"); + return TRUE; + } + else + return FALSE; +} + +static bool check_path() +{ + if (THIS->has_path) + { + GB.Error("Pending path"); + return TRUE; + } + else + return FALSE; +} + +GB_PAINT *PAINT_get_current() +{ + if (check_device()) + return NULL; + else + return _current; +} + +GB_PAINT *PAINT_from_device(void *device) +{ + GB_PAINT *d; + + for (d = _current; d; d = d->previous) + { + if (d->device == device && d->opened) + break; + } + + return d; +} + +bool PAINT_is_painted(void *device) +{ + return PAINT_from_device(device) != NULL; +} + +void PAINT_set_background(GB_COLOR color) +{ + if (!THIS) + return; + + PAINT->Background(THIS, TRUE, &color); + GB.StoreObject(NULL, POINTER(&THIS->brush)); +} + +bool PAINT_open(GB_PAINT *paint) +{ + if (paint->opened) + return FALSE; + + //fprintf(stderr, "PAINT_open: %p\n", paint); + GB.Alloc(POINTER(&paint->extra), paint->desc->size); + memset(paint->extra, 0, paint->desc->size); + paint->opened = !paint->desc->Begin(paint); + + if (!paint->opened) + GB.Free(POINTER(&paint->extra)); + + return !paint->opened; +} + +void PAINT_close(GB_PAINT *paint) +{ + if (!paint->opened) + return; + + if (!paint->other) + { + //fprintf(stderr, "PAINT_close: %p\n", paint); + paint->desc->End(paint); + GB.Free(POINTER(&paint->extra)); + } + else + PAINT->Restore(THIS); + + paint->opened = FALSE; +} + +bool PAINT_begin(void *device) +{ + GB_PAINT_DESC *desc; + GB_PAINT *paint, *other; + GB_CLASS klass; + + klass = GB.GetClass(device); + desc = (GB_PAINT_DESC *)GB.GetClassInterface(klass, "Paint"); + load_matrix_interface(); + + if (!desc) + { + GB.Error("Not a paintable device"); + return TRUE; + } + + GB.Alloc(POINTER(&paint), sizeof(GB_PAINT)); + + other = PAINT_from_device(device); + + paint->desc = desc; + GB.Ref(device); + paint->device = device; + paint->brush = NULL; + paint->opened = FALSE; + paint->other = FALSE; + paint->has_path = FALSE; + paint->tag = NULL; + paint->area.x = paint->area.y = 0; + paint->fontScale = 1; + + paint->previous = _current; + _current = paint; + + //paint->draw = DRAW_from_device(device); + //if (paint->draw) + // DRAW_close(paint->draw); + + if (other) + { + paint->extra = other->extra; + paint->opened = TRUE; + paint->other = TRUE; + paint->area = other->area; + paint->resolutionX = other->resolutionX; + paint->resolutionY = other->resolutionY; + paint->brush = other->brush; + if (paint->brush) + GB.Ref(paint->brush); + paint->fontScale = other->fontScale; + PAINT->Save(THIS); + } + else + { + if (PAINT_open(paint)) + return TRUE; + } + + return FALSE; +} + +void PAINT_end() +{ + GB_PAINT *paint; + + if (!_current) + return; + + paint = _current; + _current = _current->previous; + + PAINT_close(paint); + + //if (paint->draw) + // DRAW_open(paint->draw); + + if (paint->brush) + GB.Unref(POINTER(&paint->brush)); + GB.Unref(POINTER(&paint->device)); + GB.StoreObject(NULL, POINTER(&(paint->tag))); + GB.Free(POINTER(&paint)); +} + +void PAINT_translate(float tx, float ty) +{ + GB_TRANSFORM transform; + + if (tx == 0.0 && ty == 0.0) + return; + + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + MPAINT->Translate(transform, tx, ty); + PAINT->Matrix(THIS, TRUE, transform); + MPAINT->Delete(&transform); +} + +void PAINT_scale(float sx, float sy) +{ + GB_TRANSFORM transform; + + if (sx == 1.0 && sy == 1.0) + return; + + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + MPAINT->Scale(transform, sx, sy); + PAINT->Matrix(THIS, TRUE, transform); + MPAINT->Delete(&transform); +} + +void PAINT_set_area(GEOM_RECTF *area) +{ + THIS->area.x += area->x; + THIS->area.y += area->y; + + THIS->area.width = area->w; //MIN(area->w, THIS->area.width - THIS->area.x); + THIS->area.height = area->h; //MIN(area->h, THIS->area.height - THIS->area.y); + + if (THIS->area.width <= 0 || THIS->area.height <= 0) + THIS->area.width = THIS->area.height = 0; + + PAINT_translate(area->x, area->y); +} + +//---- PaintExtents --------------------------------------------------------- + +#define IMPLEMENT_EXTENTS_PROPERTY(_method, _field) \ +BEGIN_PROPERTY(_method) \ + GB.ReturnFloat(XTHIS->ext._field); \ +END_PROPERTY + +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_X, x1) +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_Y, y1) +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_X2, x2) +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_Y2, y2) + +BEGIN_PROPERTY(PaintExtents_Width) + + GB.ReturnFloat(XTHIS->ext.x2 - XTHIS->ext.x1); + +END_PROPERTY + +BEGIN_PROPERTY(PaintExtents_Height) + + GB.ReturnFloat(XTHIS->ext.y2 - XTHIS->ext.y1); + +END_PROPERTY + +BEGIN_METHOD(PaintExtents_Merge, GB_OBJECT extents) + + PAINT_EXTENTS *extents = VARG(extents); + + if (GB.CheckObject(extents)) + return; + + if (extents->ext.x1 < XTHIS->ext.x1) XTHIS->ext.x1 = extents->ext.x1; + if (extents->ext.y1 < XTHIS->ext.y1) XTHIS->ext.y1 = extents->ext.y1; + if (extents->ext.x2 > XTHIS->ext.x2) XTHIS->ext.x2 = extents->ext.x2; + if (extents->ext.y2 > XTHIS->ext.y2) XTHIS->ext.y2 = extents->ext.y2; + + GB.ReturnObject(XTHIS); + +END_METHOD + +GB_DESC PaintExtentsDesc[] = +{ + GB_DECLARE("PaintExtents", sizeof(PAINT_EXTENTS)), + + GB_PROPERTY_READ("X", "f", PaintExtents_X), + GB_PROPERTY_READ("Y", "f", PaintExtents_Y), + GB_PROPERTY_READ("X2", "f", PaintExtents_X2), + GB_PROPERTY_READ("Y2", "f", PaintExtents_Y2), + GB_PROPERTY_READ("W", "f", PaintExtents_Width), + GB_PROPERTY_READ("H", "f", PaintExtents_Height), + GB_PROPERTY_READ("Width", "f", PaintExtents_Width), + GB_PROPERTY_READ("Height", "f", PaintExtents_Height), + + GB_METHOD("Merge", "PaintExtents", PaintExtents_Merge, "(Extents)PaintExtents;"), + + GB_END_DECLARE +}; + + +//---- PaintMatrix ---------------------------------------------------------- + +static bool _do_not_init = FALSE; + +static PAINT_MATRIX *create_matrix(GB_TRANSFORM transform) +{ + PAINT_MATRIX *matrix; + _do_not_init = TRUE; + matrix = GB.New(GB.FindClass("PaintMatrix"), NULL, NULL); + _do_not_init = FALSE; + matrix->transform = transform; + return matrix; +} + +BEGIN_METHOD(PaintMatrix_new, GB_FLOAT xx; GB_FLOAT xy; GB_FLOAT yx; GB_FLOAT yy; GB_FLOAT x0; GB_FLOAT y0) + + load_matrix_interface(); + + if (_do_not_init) + return; + + MPAINT->Create(&MTHIS->transform); + MPAINT->Init(MTHIS->transform, VARGOPT(xx, 1.0), VARGOPT(xy, 0.0), VARGOPT(yx, 0.0), VARGOPT(yy, 1.0), VARGOPT(x0, 0.0), VARGOPT(y0, 0.0)); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_call, GB_FLOAT xx; GB_FLOAT xy; GB_FLOAT yx; GB_FLOAT yy; GB_FLOAT x0; GB_FLOAT y0) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + MPAINT->Init(transform, VARGOPT(xx, 1.0), VARGOPT(xy, 0.0), VARGOPT(yx, 0.0), VARGOPT(yy, 1.0), VARGOPT(x0, 0.0), VARGOPT(y0, 0.0)); + GB.ReturnObject(create_matrix(transform)); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_free) + + if (MTHIS->transform) + MPAINT->Delete(&MTHIS->transform); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_Copy) + + GB_TRANSFORM transform; + + MPAINT->Copy(&transform, MTHIS->transform); + GB.ReturnObject(create_matrix(transform)); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_Reset) + + MPAINT->Init(MTHIS->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Translate, GB_FLOAT tx; GB_FLOAT ty) + + MPAINT->Translate(MTHIS->transform, (float)VARG(tx), (float)VARG(ty)); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Scale, GB_FLOAT sx; GB_FLOAT sy) + + MPAINT->Scale(MTHIS->transform, (float)VARG(sx), (float)VARG(sy)); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Rotate, GB_FLOAT angle) + + MPAINT->Rotate(MTHIS->transform, (float)VARG(angle)); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_Invert) + + if (MPAINT->Invert(MTHIS->transform)) + GB.ReturnNull(); + else + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Multiply, GB_OBJECT matrix) + + PAINT_MATRIX *matrix = (PAINT_MATRIX *)VARG(matrix); + + if (GB.CheckObject(matrix)) + return; + + MPAINT->Multiply(MTHIS->transform, matrix->transform); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Map, GB_OBJECT point) + + GEOM_POINTF *point = VARG(point); + double x, y; + + if (GB.CheckObject(point)) + return; + + x = point->x; + y = point->y; + + MPAINT->Map(MTHIS->transform, &x, &y); + + GB.ReturnObject(GEOM.CreatePointF(x, y)); + +END_METHOD + +GB_DESC PaintMatrixDesc[] = +{ + GB_DECLARE("PaintMatrix", sizeof(PAINT_MATRIX)), + + GB_METHOD("_new", NULL, PaintMatrix_new, "[(XX)f(XY)f(YX)f(YY)f(X0)f(Y0)f]"), + GB_STATIC_METHOD("_call", "PaintMatrix", PaintMatrix_call, "[(XX)f(XY)f(YX)f(YY)f(X0)f(Y0)f]"), + GB_METHOD("_free", NULL, PaintMatrix_free, NULL), + + GB_METHOD("Copy", "PaintMatrix", PaintMatrix_Copy, NULL), + GB_METHOD("Reset", "PaintMatrix", PaintMatrix_Reset, NULL), + GB_METHOD("Translate", "PaintMatrix", PaintMatrix_Translate, "(TX)f(TY)f"), + GB_METHOD("Scale", "PaintMatrix", PaintMatrix_Scale, "(SX)f(SY)f"), + GB_METHOD("Rotate", "PaintMatrix", PaintMatrix_Rotate, "(Angle)f"), + GB_METHOD("Invert", "PaintMatrix", PaintMatrix_Invert, NULL), + GB_METHOD("Multiply", "PaintMatrix", PaintMatrix_Multiply, "(Matrix)PaintMatrix;"), + GB_METHOD("Map", "PointF", PaintMatrix_Map, "(Point)PointF;"), + + GB_END_DECLARE +}; + + +//---- PaintBrush ----------------------------------------------------------- + +BEGIN_METHOD_VOID(PaintBrush_free) + + BPAINT->Brush.Free(BTHIS->brush); + +END_METHOD + +BEGIN_PROPERTY(PaintBrush_Matrix) + + GB_TRANSFORM transform; + PAINT_MATRIX *matrix; + + if (READ_PROPERTY) + { + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + GB.ReturnObject(create_matrix(transform)); + } + else + { + matrix = (PAINT_MATRIX *)VPROP(GB_OBJECT); + if (!matrix) + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, NULL); + else + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, matrix->transform); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(PaintBrush_Reset) + + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, NULL); + +END_METHOD + +BEGIN_METHOD(PaintBrush_Translate, GB_FLOAT tx; GB_FLOAT ty) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + MPAINT->Translate(transform, (float)VARG(tx), (float)VARG(ty)); + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + +BEGIN_METHOD(PaintBrush_Scale, GB_FLOAT sx; GB_FLOAT sy) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + MPAINT->Scale(transform, (float)VARG(sx), (float)VARG(sy)); + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + +BEGIN_METHOD(PaintBrush_Rotate, GB_FLOAT angle) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + MPAINT->Rotate(transform, (float)VARG(angle)); + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + + +GB_DESC PaintBrushDesc[] = +{ + GB_DECLARE("PaintBrush", sizeof(PAINT_BRUSH)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, PaintBrush_free, NULL), + + GB_PROPERTY("Matrix", "PaintMatrix", PaintBrush_Matrix), + GB_METHOD("Reset", NULL, PaintBrush_Reset, NULL), + GB_METHOD("Translate", NULL, PaintBrush_Translate, "(TX)f(TY)f"), + GB_METHOD("Scale", NULL, PaintBrush_Scale, "(SX)f(SY)f"), + GB_METHOD("Rotate", NULL, PaintBrush_Rotate, "(Angle)f"), + + GB_END_DECLARE +}; + + +//---- Paint ---------------------------------------------------------------- + + +BEGIN_METHOD(Paint_Begin, GB_OBJECT device; GB_OBJECT area) + + void *device = VARG(device); + GEOM_RECTF *area = VARGOPT(area, NULL); + + if (GB.CheckObject(device)) + return; + + PAINT_begin(device); + if (area) + PAINT_set_area(area); + +END_METHOD + + +BEGIN_METHOD_VOID(Paint_End) + + PAINT_end(); + +END_METHOD + + +BEGIN_METHOD_VOID(Paint_exit) + + while (_current) + PAINT_end(); + +END_METHOD + + +BEGIN_PROPERTY(Paint_Device) + + if (THIS) + GB.ReturnObject(THIS->device); + else + GB.ReturnNull(); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Width) + + CHECK_DEVICE(); + GB.ReturnFloat(THIS->area.width); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Height) + + CHECK_DEVICE(); + GB.ReturnFloat(THIS->area.height); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_ResolutionX) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->resolutionX); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_ResolutionY) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->resolutionY); + +END_PROPERTY + +#define IMPLEMENT_METHOD(_method, _api) \ +BEGIN_METHOD_VOID(_method) \ + CHECK_DEVICE(); \ + PAINT->_api(THIS); \ +END_METHOD + + +#define IMPLEMENT_METHOD_PRESERVE(_method, _api) \ +BEGIN_METHOD(_method, GB_BOOLEAN preserve) \ + bool preserve = VARGOPT(preserve, FALSE); \ + CHECK_DEVICE(); \ + PAINT->_api(THIS, preserve); \ + if (!preserve) \ + THIS->has_path = FALSE; \ +END_METHOD + +#define IMPLEMENT_PROPERTY_EXTENTS(_property, _api) \ +BEGIN_PROPERTY(_property) \ + PAINT_EXTENTS *extents; \ + CHECK_DEVICE(); \ + extents = GB.New(GB.FindClass("PaintExtents"), NULL, NULL); \ + PAINT->_api(THIS, &extents->ext); \ + GB.ReturnObject(extents); \ +END_METHOD + +#define IMPLEMENT_PROPERTY(_property, _api, _type, _gtype, _return) \ +BEGIN_PROPERTY(_property) \ + _type value; \ + CHECK_DEVICE(); \ + if (READ_PROPERTY) \ + { \ + PAINT->_api(THIS, FALSE, &value); \ + _return(value); \ + } \ + else \ + { \ + value = (_type)VPROP(_gtype); \ + PAINT->_api(THIS, TRUE, &value); \ + } \ +END_METHOD + +#define IMPLEMENT_PROPERTY_BOOLEAN(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, int, GB_BOOLEAN, GB.ReturnBoolean) + +#define IMPLEMENT_PROPERTY_INTEGER(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, int, GB_INTEGER, GB.ReturnInteger) + +#define IMPLEMENT_PROPERTY_UNSIGNED_INTEGER(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, uint, GB_INTEGER, GB.ReturnInteger) + +#define IMPLEMENT_PROPERTY_FLOAT(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, float, GB_FLOAT, GB.ReturnFloat) + +IMPLEMENT_PROPERTY_BOOLEAN(Paint_Invert, Invert) +IMPLEMENT_PROPERTY_INTEGER(Paint_FillStyle, FillStyle) + +BEGIN_PROPERTY(Paint_Background) + + uint value; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + PAINT->Background(THIS, FALSE, &value); + GB.ReturnInteger(value); + } + else + { + PAINT_set_background(VPROP(GB_INTEGER)); + } + +END_METHOD + +IMPLEMENT_PROPERTY_BOOLEAN(Paint_Antialias, Antialias) +IMPLEMENT_METHOD(Paint_Save, Save) +IMPLEMENT_METHOD(Paint_Restore, Restore) +IMPLEMENT_METHOD_PRESERVE(Paint_Clip, Clip) +IMPLEMENT_METHOD(Paint_ResetClip, ResetClip) +IMPLEMENT_PROPERTY_EXTENTS(Paint_ClipExtents, ClipExtents) +IMPLEMENT_METHOD_PRESERVE(Paint_Fill, Fill) +IMPLEMENT_METHOD_PRESERVE(Paint_Stroke, Stroke) +IMPLEMENT_PROPERTY_EXTENTS(Paint_PathExtents, PathExtents) + + +static GB_ARRAY _outline; +static GB_ARRAY _outline_p; + +static void make_path_outline(int cmd, float x, float y) +{ + if (!_outline) + GB.Array.New(&_outline, GB.FindClass("PointF[]"), 0); + + if (cmd == GB_PAINT_PATH_MOVE) + { + GB.Array.New(&_outline_p, GB.FindClass("PointF"), 0); + GB.Ref(_outline_p); + *((void **)GB.Array.Add(_outline)) = _outline_p; + } + + GEOM_POINTF *p = GEOM.CreatePointF(x, y); + *((void **)GB.Array.Add(_outline_p)) = p; + GB.Ref(p); +} + +BEGIN_PROPERTY(Paint_PathOutline) + + CHECK_DEVICE(); + + _outline = NULL; + PAINT->PathOutline(THIS, make_path_outline); + GB.ReturnObject(_outline); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_ClipRect) + + GB_EXTENTS ext; + GEOM_RECT *rect; + int w, h; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + (*PAINT->ClipExtents)(THIS, &ext); + + w = floorf(ext.x2) - ceilf(ext.x1); + h = floorf(ext.y2) - ceilf(ext.y1); + + if (w <= 0 || h <= 0) + { + GB.ReturnNull(); + return; + } + + rect = GEOM.CreateRect(); + rect->x = ceilf(ext.x1); + rect->y = ceilf(ext.y1); + rect->w = w; + rect->h = h; + + GB.ReturnObject(rect); + } + else + { + rect = (GEOM_RECT *)VPROP(GB_OBJECT); + if (rect) + PAINT->ClipRect(THIS, rect->x, rect->y, rect->w, rect->h); + else + PAINT->ResetClip(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD(Paint_PathContains, GB_FLOAT x; GB_FLOAT y) + + CHECK_DEVICE(); + GB.ReturnBoolean(PAINT->PathContains(THIS, (float)VARG(x), (float)VARG(y))); + +END_METHOD + +IMPLEMENT_PROPERTY_INTEGER(Paint_FillRule, FillRule) +IMPLEMENT_PROPERTY_INTEGER(Paint_LineCap, LineCap) +IMPLEMENT_PROPERTY_INTEGER(Paint_LineJoin, LineJoin) +IMPLEMENT_PROPERTY_INTEGER(Paint_Operator, Operator) +IMPLEMENT_PROPERTY_FLOAT(Paint_LineWidth, LineWidth) +IMPLEMENT_PROPERTY_FLOAT(Paint_MiterLimit, MiterLimit) + +BEGIN_PROPERTY(Paint_Brush) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(THIS->brush); + else + { + PAINT_BRUSH *old_brush = THIS->brush; + PAINT_BRUSH *new_brush = (PAINT_BRUSH *)VPROP(GB_OBJECT); + if (new_brush) + { + GB.Ref(new_brush); + PAINT->SetBrush(THIS, new_brush->brush); + } + GB.Unref(POINTER(&old_brush)); + THIS->brush = new_brush; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_BrushOrigin) + + float x, y; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + PAINT->BrushOrigin(THIS, FALSE, &x, &y); + GB.ReturnObject(GEOM.CreatePointF(x, y)); + } + else + { + GEOM_POINT *p = VPROP(GB_OBJECT); + if (!p) + x = y = 0.0; + else + { + x = p->x; + y = p->y; + } + + PAINT->BrushOrigin(THIS, TRUE, &x, &y); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Dash) + + GB_ARRAY array; + float *dashes; + int count, i; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + PAINT->Dash(THIS, FALSE, &dashes, &count); + if (!count) + GB.ReturnNull(); + else + { + GB.Array.New(POINTER(&array), GB_T_FLOAT, count); + for (i = 0; i < count; i++) + *((double *)GB.Array.Get(array, i)) = (double)dashes[i]; + GB.ReturnObject(array); + } + GB.Free(POINTER(&dashes)); + } + else + { + array = (GB_ARRAY)VPROP(GB_OBJECT); + if (!array) + count = 0; + else + count = GB.Array.Count(array); + + if (!count) + { + PAINT->Dash(THIS, TRUE, NULL, &count); + //THIS->lineStyle = GB_PAINT_LINE_STYLE_SOLID; + } + else + { + GB.Alloc(POINTER(&dashes), sizeof(float) * count); + for (i = 0; i < count; i++) + dashes[i] = (float)*((double *)GB.Array.Get(array, i)); + PAINT->Dash(THIS, TRUE, &dashes, &count); + GB.Free(POINTER(&dashes)); + //THIS->lineStyle = GB_PAINT_LINE_STYLE_CUSTOM; + } + } + +END_PROPERTY + + +IMPLEMENT_PROPERTY_FLOAT(Paint_DashOffset, DashOffset) + +BEGIN_METHOD_VOID(Paint_NewPath) + + CHECK_DEVICE(); + + PAINT->NewPath(THIS); + THIS->has_path = FALSE; + +END_METHOD + +IMPLEMENT_METHOD(Paint_ClosePath, ClosePath) + + +BEGIN_PROPERTY(Paint_X) + + float x, y; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &x, &y); + GB.ReturnFloat((double)x); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Y) + + float x, y; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &x, &y); + GB.ReturnFloat((double)y); + +END_PROPERTY + + +BEGIN_METHOD(Paint_Arc, GB_FLOAT xc; GB_FLOAT yc; GB_FLOAT radius; GB_FLOAT angle; GB_FLOAT length; GB_BOOLEAN pie) + + CHECK_DEVICE(); + + bool pie = VARGOPT(pie, FALSE); + float angle = VARGOPT(angle, 0.0); + float length = VARGOPT(length, MISSING(angle) ? M_PI * 2 : 0.0); + + if (MISSING(length) || length == 0.0) + pie = FALSE; + + PAINT->Arc(THIS, VARG(xc), VARG(yc), VARG(radius), angle, length, pie); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_Ellipse, GB_FLOAT x; GB_FLOAT y; GB_FLOAT width; GB_FLOAT height; GB_FLOAT angle; GB_FLOAT length; GB_BOOLEAN pie) + + CHECK_DEVICE(); + + bool pie = VARGOPT(pie, FALSE); + float angle = VARGOPT(angle, 0.0); + float length = VARGOPT(length, MISSING(angle) ? M_PI * 2 : 0.0); + + if (MISSING(length) || length == 0.0) + pie = FALSE; + + PAINT->Ellipse(THIS, VARG(x), VARG(y), VARG(width), VARG(height), angle, length, pie); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_Polygon, GB_OBJECT points) + + GB_ARRAY points = VARG(points); + int i, n; + double *p; + + CHECK_DEVICE(); + + if (!points) + return; + + n = GB.Array.Count(points); + if (n < 4) + return; + + CHECK_DEVICE(); + + p = (double *)GB.Array.Get(points, 0); + + PAINT->MoveTo(THIS, p[0], p[1]); + for (i = 2; i < n; i+= 2) + PAINT->LineTo(THIS, p[i], p[i + 1]); + PAINT->LineTo(THIS, p[0], p[1]); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_CurveTo, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2; GB_FLOAT x3; GB_FLOAT y3) + + CHECK_DEVICE(); + PAINT->CurveTo(THIS, VARG(x1), VARG(y1), VARG(x2), VARG(y2), VARG(x3), VARG(y3)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_RelCurveTo, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2; GB_FLOAT x3; GB_FLOAT y3) + + float x, y; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &x, &y); + PAINT->CurveTo(THIS, x + VARG(x1), y + VARG(y1), x + VARG(x2), y + VARG(y2), x + VARG(x3), y + VARG(y3)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_LineTo, GB_FLOAT x; GB_FLOAT y) + + CHECK_DEVICE(); + PAINT->LineTo(THIS, VARG(x), VARG(y)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_RelLineTo, GB_FLOAT x; GB_FLOAT y) + + float fx, fy; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &fx, &fy); + PAINT->LineTo(THIS, fx + VARG(x), fy + VARG(y)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_MoveTo, GB_FLOAT x; GB_FLOAT y) + + CHECK_DEVICE(); + PAINT->MoveTo(THIS, VARG(x), VARG(y)); + +END_METHOD + + +BEGIN_METHOD(Paint_RelMoveTo, GB_FLOAT x; GB_FLOAT y) + + float fx, fy; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &fx, &fy); + PAINT->MoveTo(THIS, fx + VARG(x), fy + VARG(y)); + +END_METHOD + + +BEGIN_METHOD(Paint_Rectangle, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_FLOAT radius) + + CHECK_DEVICE(); + + float x = VARG(x); + float y = VARG(y); + float w = VARG(w); + float h = VARG(h); + float r = VARGOPT(radius, 0.0); + + if (w == 0.0 || h == 0.0) + return; + + float sw = w > 0.0 ? 1 : -1; + float sh = h > 0.0 ? 1 : -1; + + if (r <= 0.0) + { + PAINT->Rectangle(THIS, x, y, w, h); + } + else + { + r = Min(r, Min(fabsf(w), fabsf(h)) / 2); + float r2 = r * (1-0.55228475); + + PAINT->MoveTo(THIS, x + r * sw, y); + PAINT->LineTo(THIS, x + w - r * sw, y); + PAINT->CurveTo(THIS, x + w - r2 * sw, y, x + w, y + r2 * sh, x + w, y + r * sh); + PAINT->LineTo(THIS, x + w, y + h - r * sh); + PAINT->CurveTo(THIS, x + w, y + h - r2 * sh, x + w - r2 * sw, y + h, x + w - r * sw, y + h); + PAINT->LineTo(THIS, x + r * sw, y + h); + PAINT->CurveTo(THIS, x + r2 * sw, y + h, x, y + h - r2 * sh, x, y + h - r * sh); + PAINT->LineTo(THIS, x, y + r * sh); + PAINT->CurveTo(THIS, x, y + r2 * sh, x + r2 * sw, y, x + r * sw, y); + } + + THIS->has_path = TRUE; + +END_METHOD + + +IMPLEMENT_PROPERTY(Paint_Font, Font, GB_FONT, GB_OBJECT, GB.ReturnObject) + +BEGIN_PROPERTY(Paint_FontScale) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->fontScale); + else + { + THIS->fontScale = VPROP(GB_FLOAT); + if (THIS->fontScale == 0) + THIS->fontScale = 1; + } + +END_PROPERTY + + +BEGIN_METHOD(Paint_Text, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->Text(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), FALSE); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_RichText, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->RichText(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), FALSE); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_DrawText, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + CHECK_PATH(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->Text(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), TRUE); + +END_METHOD + + +BEGIN_METHOD(Paint_DrawRichText, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + CHECK_PATH(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->RichText(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), TRUE); + +END_METHOD + + +BEGIN_METHOD(Paint_TextExtents, GB_STRING text) + + PAINT_EXTENTS *extents; + + CHECK_DEVICE(); + + extents = GB.New(GB.FindClass("PaintExtents"), NULL, NULL); + + if (!LENGTH(text)) + { + PAINT->GetCurrentPoint(THIS, &extents->ext.x1, &extents->ext.y1); + extents->ext.x2 = extents->ext.x1; + extents->ext.y2 = extents->ext.y1; + } + else + PAINT->TextExtents(THIS, STRING(text), LENGTH(text), &extents->ext); + + GB.ReturnObject(extents); + +END_METHOD + + +BEGIN_METHOD(Paint_RichTextExtents, GB_STRING text; GB_FLOAT width) + + PAINT_EXTENTS *extents; + + CHECK_DEVICE(); + + extents = GB.New(GB.FindClass("PaintExtents"), NULL, NULL); + PAINT->RichTextExtents(THIS, STRING(text), LENGTH(text), &extents->ext, VARGOPT(width, -1)); + + GB.ReturnObject(extents); + +END_METHOD + + +BEGIN_METHOD(Paint_TextSize, GB_STRING text) + + float w, h; + GEOM_RECTF *size; + + CHECK_DEVICE(); + PAINT->TextSize(THIS, STRING(text), LENGTH(text), &w, &h); + size = GEOM.CreateRectF(); + size->w = w; + size->h = h; + GB.ReturnObject(size); + +END_METHOD + + +BEGIN_PROPERTY(Paint_TextHeight) + + float h; + + CHECK_DEVICE(); + PAINT->TextSize(THIS, " ", 1, NULL, &h); + GB.ReturnFloat(h); + +END_PROPERTY + + +BEGIN_METHOD(Paint_RichTextSize, GB_STRING text; GB_FLOAT width) + + float w, h; + GEOM_RECTF *size; + + w = VARGOPT(width, -1); + + CHECK_DEVICE(); + PAINT->RichTextSize(THIS, STRING(text), LENGTH(text), w, &w, &h); + size = GEOM.CreateRectF(); + size->w = w; + size->h = h; + GB.ReturnObject(size); + +END_METHOD + + +static PAINT_BRUSH *make_brush(GB_PAINT *d, GB_BRUSH brush) +{ + PAINT_BRUSH *that; + that = GB.New(GB.FindClass("PaintBrush"), NULL, NULL); + that->desc = d->desc; + that->brush = brush; + GB.ReturnObject(that); + return that; +} + + +BEGIN_METHOD(Paint_Color, GB_INTEGER color) + + GB_BRUSH brush; + + CHECK_DEVICE(); + + PAINT->Brush.Color(&brush, VARG(color)); + make_brush(THIS, brush); + +END_METHOD + + +BEGIN_METHOD(Paint_Image, GB_OBJECT image; GB_FLOAT x; GB_FLOAT y) + + GB_BRUSH brush; + + CHECK_DEVICE(); + + if (GB.CheckObject(VARG(image))) + return; + + PAINT->Brush.Image(&brush, (GB_IMAGE)VARG(image)); + make_brush(THIS, brush); + + if (!MISSING(x) || !MISSING(y)) + { + GB_TRANSFORM transform; + MPAINT->Create(&transform); + MPAINT->Translate(transform, VARGOPT(x, 0.0), VARGOPT(y, 0.0)); + PAINT->Brush.Matrix(brush, TRUE, transform); + MPAINT->Delete(&transform); + } + +END_METHOD + + +BEGIN_METHOD(Paint_LinearGradient, GB_FLOAT x0; GB_FLOAT y0; GB_FLOAT x1; GB_FLOAT y1; GB_OBJECT colors; GB_OBJECT positions; GB_INTEGER extend) + + GB_BRUSH brush; + GB_ARRAY positions, colors; + int nstop; + + CHECK_DEVICE(); + + positions = (GB_ARRAY)VARG(positions); + if (GB.CheckObject(positions)) + return; + colors = (GB_ARRAY)VARG(colors); + if (GB.CheckObject(colors)) + return; + + nstop = Min(GB.Array.Count(positions), GB.Array.Count(colors)); + + PAINT->Brush.LinearGradient(&brush, (float)VARG(x0), (float)VARG(y0), (float)VARG(x1), (float)VARG(y1), + nstop, (double *)GB.Array.Get(positions, 0), (GB_COLOR *)GB.Array.Get(colors, 0), VARGOPT(extend, GB_PAINT_EXTEND_PAD)); + + make_brush(THIS, brush); + +END_METHOD + + +BEGIN_METHOD(Paint_RadialGradient, GB_FLOAT cx; GB_FLOAT cy; GB_FLOAT radius; GB_FLOAT fx; GB_FLOAT fy; GB_OBJECT colors; GB_OBJECT positions; GB_INTEGER extend) + + GB_BRUSH brush; + GB_ARRAY positions, colors; + int nstop; + + CHECK_DEVICE(); + + positions = (GB_ARRAY)VARG(positions); + if (GB.CheckObject(positions)) + return; + colors = (GB_ARRAY)VARG(colors); + if (GB.CheckObject(colors)) + return; + + nstop = Min(GB.Array.Count(positions), GB.Array.Count(colors)); + + PAINT->Brush.RadialGradient(&brush, (float)VARG(cx), (float)VARG(cy), (float)VARG(radius), (float)VARG(fx), (float)VARG(fy), + nstop, (double *)GB.Array.Get(positions, 0), (GB_COLOR *)GB.Array.Get(colors, 0), VARGOPT(extend, GB_PAINT_EXTEND_PAD)); + + make_brush(THIS, brush); + +END_METHOD + + +BEGIN_PROPERTY(Paint_Matrix) + + GB_TRANSFORM transform; + PAINT_MATRIX *matrix; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + GB.ReturnObject(create_matrix(transform)); + } + else + { + matrix = (PAINT_MATRIX *)VPROP(GB_OBJECT); + if (!matrix) + PAINT->Matrix(THIS, TRUE, NULL); + else + PAINT->Matrix(THIS, TRUE, matrix->transform); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Paint_Reset) + + CHECK_DEVICE(); + PAINT->Matrix(THIS, TRUE, NULL); + PAINT_translate(THIS->area.x, THIS->area.y); + +END_METHOD + + +BEGIN_METHOD(Paint_Translate, GB_FLOAT tx; GB_FLOAT ty) + + CHECK_DEVICE(); + PAINT_translate(VARG(tx), VARG(ty)); + +END_METHOD + + +BEGIN_METHOD(Paint_Scale, GB_FLOAT sx; GB_FLOAT sy) + + CHECK_DEVICE(); + double sx = VARG(sx); + double sy = VARGOPT(sy, sx); + + PAINT_scale(sx, sy); + +END_METHOD + + +BEGIN_METHOD(Paint_Rotate, GB_FLOAT angle) + + GB_TRANSFORM transform; + + CHECK_DEVICE(); + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + MPAINT->Rotate(transform, (float)VARG(angle)); + PAINT->Matrix(THIS, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + + +BEGIN_METHOD(Paint_DrawImage, GB_OBJECT image; GB_FLOAT x; GB_FLOAT y; GB_FLOAT width; GB_FLOAT height; GB_FLOAT opacity; GB_OBJECT source) + + GB_IMG *image; + float x, y, w, h; + float opacity = VARGOPT(opacity, 1.0); + GEOM_RECT *source = (GEOM_RECT *)VARGOPT(source, NULL); + + CHECK_DEVICE(); + CHECK_PATH(); + + image = (GB_IMG *)VARG(image); + + if (GB.CheckObject(image)) + return; + + x = VARG(x); + y = VARG(y); + w = VARGOPT(width, -1); + h = VARGOPT(height, -1); + + if (GB.CheckObject(VARG(image))) + return; + + if (w < 0) w = image->width; + if (h < 0) h = image->height; + + if (w <= 0.0 || h <= 0.0) + return; + + if (image->width <= 0 || image->height <= 0) + return; + + PAINT->DrawImage(THIS, VARG(image), x, y, w, h, opacity, source ? (GB_RECT *)&source->x : NULL); + +END_METHOD + + +BEGIN_METHOD(Paint_DrawPicture, GB_OBJECT picture; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_OBJECT source) + + GB_PICTURE picture = VARG(picture); + GB_PICTURE_INFO info; + GEOM_RECT *source = (GEOM_RECT *)VARGOPT(source, NULL); + float x, y, w, h; + + CHECK_DEVICE(); + CHECK_PATH(); + + if (GB.CheckObject(picture)) + return; + + PAINT->GetPictureInfo(THIS, picture, &info); + + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + if (w < 0) w = info.width; + if (h < 0) h = info.height; + + if (info.width <= 0 || info.height <= 0) + return; + + PAINT->DrawPicture(THIS, picture, x, y, w, h, source ? (GB_RECT *)&source->x : NULL); + +END_METHOD + + +BEGIN_METHOD(Paint_ZoomImage, GB_OBJECT image; GB_INTEGER zoom; GB_INTEGER x; GB_INTEGER y; GB_INTEGER grid; GB_OBJECT source) + + GB_IMAGE image = VARG(image); + GB_IMG *info = (GB_IMG *)image; + GEOM_RECT *source = (GEOM_RECT *)VARGOPT(source, NULL); + int zoom; + int x, y, sx, sy, sw, sh; + int i, j, xr, yr; + bool border; + GB_COLOR borderColor; + int antialias = FALSE; + GB_RECT rect; + float opacity = 1.0; //VARGOPT(opacity, 1.0); + + CHECK_DEVICE(); + CHECK_PATH(); + + if (GB.CheckObject(image)) + return; + + zoom = VARG(zoom); + if (zoom < 1) + { + GB.Error("Bad zoom factor"); + return; + } + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + + if (source) + { + sx = source->x; + sy = source->y; + sw = source->w; + sh = source->h; + } + else + { + sx = sy = 0; + sw = info->width; + sh = info->height; + } + + DRAW_NORMALIZE(x, y, sw, sh, sx, sy, sw, sh, info->width, info->height); + + //DRAW->Fill.GetOrigin(THIS, &ox, &oy); + + PAINT->Save(THIS); + PAINT->Antialias(THIS, TRUE, &antialias); + + borderColor = VARGOPT(grid, GB_COLOR_DEFAULT); + border = borderColor != GB_COLOR_DEFAULT; + + rect.x = sx; + rect.y = sy; + rect.w = sw; + rect.h = sh; + + PAINT->DrawImage(THIS, image, x, y, sw * zoom, sh * zoom, opacity, &rect); + + if (border && zoom >= 3) + { + float dashes[2] = { 1.0, 1.0 }; + int count; + float *pdashes = dashes; + + for (i = sx + 1, xr = x + zoom; i < (sx + sw); i++, xr += zoom) + { + //PAINT->FillRect(THIS, xr, y, 1, sh * zoom, borderColor); + PAINT->MoveTo(THIS, xr, y); + PAINT->LineTo(THIS, xr, y + sh * zoom); + } + + for (j = sy + 1, yr = y + zoom; j < (sy + sh); j++, yr += zoom) + { + //PAINT->FillRect(THIS, x, yr, sw * zoom, 1, borderColor); + PAINT->MoveTo(THIS, x, yr); + PAINT->LineTo(THIS, x + sw * zoom, yr); + } + + count = 0; + PAINT->Dash(THIS, TRUE, NULL, &count); + PAINT->Background(THIS, TRUE, &borderColor); + PAINT->Stroke(THIS, TRUE); + + borderColor ^= 0xFFFFFF; + count = 2; + PAINT->Dash(THIS, TRUE, &pdashes, &count); + PAINT->Background(THIS, TRUE, &borderColor); + PAINT->Stroke(THIS, FALSE); + + THIS->has_path = FALSE; + } + + PAINT->Restore(THIS); + +END_METHOD + + +#if 0 +BEGIN_PROPERTY(Paint_LineStyle) + + int v; + int count; + float dashes[6]; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->lineStyle); + return; + } + + v = VPROP(GB_INTEGER); + + switch (v) + { + case GB_PAINT_LINE_STYLE_NONE: + break; + + case GB_PAINT_LINE_STYLE_SOLID: + PAINT->Dash(THIS, TRUE, NULL, &count); + break; + + case GB_PAINT_LINE_STYLE_DASH: + dashes[0] = 3; dashes[1] = 3; count = 2; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + case GB_PAINT_LINE_STYLE_DOT: + dashes[0] = 1; dashes[1] = 3; count = 2; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + case GB_PAINT_LINE_STYLE_DASH_DOT: + dashes[0] = 3; dashes[1] = 3; dashes[2] = 3; dashes[3] = 1; dacount = 4; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + case GB_PAINT_LINE_STYLE_DASH_DOT_DOT: + dashes[0] = 3; dashes[1] = 3; dashes[2] = 3; dashes[3] = 1; dashes[4] = 3; dashes[5] = 1; dacount = 6; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + default: + return; + } + + THIS->lineStyle = v; + +END_PROPERTY +#endif + +BEGIN_METHOD(Paint_FillRect, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER color) + + CHECK_DEVICE(); + CHECK_PATH(); + PAINT->FillRect(THIS, VARG(x), VARG(y), VARG(w), VARG(h), VARG(color)); + +END_METHOD + + +BEGIN_PROPERTY(Paint_Tag) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(THIS->tag); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->tag))); + +END_PROPERTY + +#if 0 +Static Public Sub DrawEllipsizedText(Text As String, X As Integer, Y As Integer, W As Integer, H As Integer, Optional Alignment As Integer = Align.TopLeft) + + Dim aElt As String[] = Split(Text, " \n") + Dim sElt As String + Dim I As Integer + Dim HL As Integer = Paint.Font.Height + Dim YT, WT As Integer + Dim sText As String + + YT = 0 + + For I = 0 To aElt.Max + sElt = aElt[I] + + If (YT + HL * 2) > H Then + WT = Paint.Font.TextWidth(LTrim(sText & " " & sElt & "…")) + If WT > W Then + Paint.DrawText(sText & "…", X, Y + YT, W, H, Align.TopLeft) + Return + Endif + Else + WT = Paint.Font.TextWidth(LTrim(sText & " " & sElt)) + If WT > W Then + Paint.DrawText(sText, X, Y + YT, W, H, Align.TopLeft) + sText = "" + YT += HL + Endif + Endif + + sText = RTrim(sText & " " & sElt) + + Next + + Paint.DrawText(sText, X, Y + YT, W, H, Align.TopLeft) + +End +#endif + +//-- Style API -------------------------------------------------------------- + +#define GET_COORD() \ + int x, y, w, h; \ +\ + CHECK_DEVICE(); \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (w < 1 || h < 1) \ + return; + + +GB_DESC PaintDesc[] = +{ + GB_DECLARE("Paint", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Paint_exit, NULL), + + GB_CONSTANT("ExtendPad", "i", GB_PAINT_EXTEND_PAD), + GB_CONSTANT("ExtendRepeat", "i", GB_PAINT_EXTEND_REPEAT), + GB_CONSTANT("ExtendReflect", "i", GB_PAINT_EXTEND_REFLECT), + + GB_CONSTANT("FillRuleWinding", "i", GB_PAINT_FILL_RULE_WINDING), + GB_CONSTANT("FillRuleEvenOdd", "i", GB_PAINT_FILL_RULE_EVEN_ODD), + + GB_CONSTANT("LineCapButt", "i", GB_PAINT_LINE_CAP_BUTT), + GB_CONSTANT("LineCapRound", "i", GB_PAINT_LINE_CAP_ROUND), + GB_CONSTANT("LineCapSquare", "i", GB_PAINT_LINE_CAP_SQUARE), + + GB_CONSTANT("LineJoinMiter", "i", GB_PAINT_LINE_JOIN_MITER), + GB_CONSTANT("LineJoinRound", "i", GB_PAINT_LINE_JOIN_ROUND), + GB_CONSTANT("LineJoinBevel", "i", GB_PAINT_LINE_JOIN_BEVEL), + + GB_CONSTANT("OperatorClear", "i", GB_PAINT_OPERATOR_CLEAR), + GB_CONSTANT("OperatorSource", "i", GB_PAINT_OPERATOR_SOURCE), + GB_CONSTANT("OperatorOver", "i", GB_PAINT_OPERATOR_OVER), + GB_CONSTANT("OperatorIn", "i", GB_PAINT_OPERATOR_IN), + GB_CONSTANT("OperatorOut", "i", GB_PAINT_OPERATOR_OUT), + GB_CONSTANT("OperatorATop", "i", GB_PAINT_OPERATOR_ATOP), + GB_CONSTANT("OperatorDest", "i", GB_PAINT_OPERATOR_DEST), + GB_CONSTANT("OperatorDestOver", "i", GB_PAINT_OPERATOR_DEST_OVER), + GB_CONSTANT("OperatorDestIn", "i", GB_PAINT_OPERATOR_DEST_IN), + GB_CONSTANT("OperatorDestOut", "i", GB_PAINT_OPERATOR_DEST_OUT), + GB_CONSTANT("OperatorDestATop", "i", GB_PAINT_OPERATOR_DEST_ATOP), + GB_CONSTANT("OperatorXor", "i", GB_PAINT_OPERATOR_XOR), + GB_CONSTANT("OperatorAdd", "i", GB_PAINT_OPERATOR_ADD), + GB_CONSTANT("OperatorSaturate", "i", GB_PAINT_OPERATOR_SATURATE), + + GB_STATIC_METHOD("Begin", NULL, Paint_Begin, "(Device)o[(Area)RectF;]"), + GB_STATIC_METHOD("End", NULL, Paint_End, NULL), + + GB_STATIC_PROPERTY_READ("Device", "o", Paint_Device), + GB_STATIC_PROPERTY_READ("W", "f", Paint_Width), + GB_STATIC_PROPERTY_READ("H", "f", Paint_Height), + GB_STATIC_PROPERTY_READ("Width", "f", Paint_Width), + GB_STATIC_PROPERTY_READ("Height", "f", Paint_Height), + GB_STATIC_PROPERTY_READ("ResolutionX", "i", Paint_ResolutionX), + GB_STATIC_PROPERTY_READ("ResolutionY", "i", Paint_ResolutionY), + GB_STATIC_PROPERTY("AntiAlias", "b", Paint_Antialias), + + GB_STATIC_PROPERTY("_Invert", "b", Paint_Invert), + GB_STATIC_PROPERTY("_FillStyle", "i", Paint_FillStyle), + GB_STATIC_PROPERTY("Background", "i", Paint_Background), + GB_STATIC_PROPERTY("_Tag", "o", Paint_Tag), + + GB_STATIC_METHOD("Save", NULL, Paint_Save, NULL), + GB_STATIC_METHOD("Restore", NULL, Paint_Restore, NULL), + + GB_STATIC_METHOD("Clip", NULL, Paint_Clip, "[(Preserve)b]"), + GB_STATIC_METHOD("ResetClip", NULL, Paint_ResetClip, NULL), + GB_STATIC_PROPERTY_READ("ClipExtents", "PaintExtents", Paint_ClipExtents), + GB_STATIC_PROPERTY("ClipRect", "Rect", Paint_ClipRect), + + GB_STATIC_METHOD("Fill", NULL, Paint_Fill, "[(Preserve)b]"), + //GB_STATIC_PROPERTY_READ("FillExtents", "PaintExtents", Paint_FillExtents), + //GB_STATIC_METHOD("InFill", "b", Paint_InFill, "(X)f(Y)f"), + + //GB_STATIC_METHOD("Mask", NULL, CAIRO_mask, "(Pattern)CairoPattern;"), + + //GB_STATIC_METHOD("Paint", NULL, CAIRO_paint, "[(Alpha)f]"), + + GB_STATIC_METHOD("Stroke", NULL, Paint_Stroke, "[(Preserve)b]"), + //GB_STATIC_PROPERTY_READ("StrokeExtents", "PaintExtents", Paint_StrokeExtents), + //GB_STATIC_METHOD("InStroke", "b", Paint_InStroke, "(X)f(Y)f"), + + GB_STATIC_PROPERTY_READ("PathExtents", "PaintExtents", Paint_PathExtents), + GB_STATIC_METHOD("PathContains", "b", Paint_PathContains, "(X)f(Y)f"), + GB_STATIC_PROPERTY_READ("PathOutline", "PointF[][]", Paint_PathOutline), + + GB_STATIC_PROPERTY("Brush", "PaintBrush", Paint_Brush), + GB_STATIC_PROPERTY("BrushOrigin", "PointF", Paint_BrushOrigin), + GB_STATIC_PROPERTY("Dash", "Float[]", Paint_Dash), + GB_STATIC_PROPERTY("LineDash", "Float[]", Paint_Dash), + GB_STATIC_PROPERTY("DashOffset", "f", Paint_DashOffset), + GB_STATIC_PROPERTY("LineDashOffset", "f", Paint_DashOffset), + GB_STATIC_PROPERTY("FillRule", "i", Paint_FillRule), + GB_STATIC_PROPERTY("LineCap", "i", Paint_LineCap), + GB_STATIC_PROPERTY("LineJoin", "i", Paint_LineJoin), + GB_STATIC_PROPERTY("LineWidth", "f", Paint_LineWidth), + //GB_STATIC_PROPERTY("LineStyle", "i", Paint_LineStyle), + GB_STATIC_PROPERTY("MiterLimit", "f", Paint_MiterLimit), + GB_STATIC_PROPERTY("Operator", "i", Paint_Operator), + //GB_STATIC_PROPERTY("Tolerance", "f", CAIRO_tolerance), + + GB_STATIC_METHOD("NewPath", NULL, Paint_NewPath, NULL), + GB_STATIC_METHOD("ClosePath", NULL, Paint_ClosePath, NULL), + + GB_STATIC_PROPERTY_READ("X", "f", Paint_X), + GB_STATIC_PROPERTY_READ("Y", "f", Paint_Y), + + GB_STATIC_METHOD("Rectangle", NULL, Paint_Rectangle, "(X)f(Y)f(Width)f(Height)f[(Radius)f]"), + GB_STATIC_METHOD("FillRect", NULL, Paint_FillRect, "(X)f(Y)f(Width)f(Height)f(Color)i"), + GB_STATIC_METHOD("Arc", NULL, Paint_Arc, "(XC)f(YC)f(Radius)f[(Angle)f(Length)f(Pie)b]"), + GB_STATIC_METHOD("Ellipse", NULL, Paint_Ellipse, "(X)f(Y)f(Width)f(Height)f[(Angle)f(Length)f(Pie)b]"), + GB_STATIC_METHOD("Polygon", NULL, Paint_Polygon, "(Points)Float[];"), + + GB_STATIC_METHOD("CurveTo", NULL, Paint_CurveTo, "(X1)f(Y1)f(X2)f(Y2)f(X3)f(Y3)f"), + GB_STATIC_METHOD("RelCurveTo", NULL, Paint_RelCurveTo, "(X1)f(Y1)f(X2)f(Y2)f(X3)f(Y3)f"), + GB_STATIC_METHOD("LineTo", NULL, Paint_LineTo, "(X)f(Y)f"), + GB_STATIC_METHOD("RelLineTo", NULL, Paint_RelLineTo, "(X)f(Y)f"), + GB_STATIC_METHOD("MoveTo", NULL, Paint_MoveTo, "(X)f(Y)f"), + GB_STATIC_METHOD("RelMoveTo", NULL, Paint_RelMoveTo, "(X)f(Y)f"), + GB_STATIC_PROPERTY("Font", "Font", Paint_Font), + GB_STATIC_PROPERTY("FontScale", "f", Paint_FontScale), + GB_STATIC_METHOD("Text", NULL, Paint_Text, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("TextSize", "RectF", Paint_TextSize, "(Text)s"), + GB_STATIC_PROPERTY_READ("TextHeight", "f", Paint_TextHeight), + GB_STATIC_METHOD("RichText", NULL, Paint_RichText, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("RichTextSize", "RectF", Paint_RichTextSize, "(Text)s[(Width)f]"), + GB_STATIC_METHOD("DrawText", NULL, Paint_DrawText, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("DrawRichText", NULL, Paint_DrawRichText, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("TextExtents", "PaintExtents", Paint_TextExtents, "(Text)s"), + GB_STATIC_METHOD("RichTextExtents", "PaintExtents", Paint_RichTextExtents, "(Text)s[(Width)f]"), + + GB_STATIC_METHOD("Color", "PaintBrush", Paint_Color, "(Color)i"), + GB_STATIC_METHOD("Image", "PaintBrush", Paint_Image, "(Image)Image;[(X)f(Y)f]"), + GB_STATIC_METHOD("LinearGradient", "PaintBrush", Paint_LinearGradient, "(X0)f(Y0)f(X1)f(Y1)f(Colors)Integer[];(Positions)Float[];[(Extend)i]"), + GB_STATIC_METHOD("RadialGradient", "PaintBrush", Paint_RadialGradient, "(CX)f(CY)f(Radius)f(FX)f(FY)f(Colors)Integer[];(Positions)Float[];[(Extend)i]"), + + GB_STATIC_PROPERTY("Matrix", "PaintMatrix", Paint_Matrix), + + GB_STATIC_METHOD("Reset", NULL, Paint_Reset, NULL), + GB_STATIC_METHOD("Translate", NULL, Paint_Translate, "(TX)f(TY)f"), + GB_STATIC_METHOD("Scale", NULL, Paint_Scale, "(SX)f[(SY)f]"), + GB_STATIC_METHOD("Rotate", NULL, Paint_Rotate, "(Angle)f"), + + //GB_STATIC_METHOD("Clear", NULL, Paint_Clear, NULL), + + GB_STATIC_METHOD("DrawImage", NULL, Paint_DrawImage, "(Image)Image;(X)f(Y)f[(Width)f(Height)f(Opacity)f(Source)Rect;]"), + GB_STATIC_METHOD("DrawPicture", NULL, Paint_DrawPicture, "(Picture)Picture;(X)f(Y)f[(Width)f(Height)f(Source)Rect;]"), + GB_STATIC_METHOD("ZoomImage", NULL, Paint_ZoomImage, "(Image)Image;(Zoom)i(X)i(Y)i[(Grid)i(Source)Rect;]"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/draw/cpaint.h b/main/lib/draw/cpaint.h new file mode 100644 index 00000000..30c38f96 --- /dev/null +++ b/main/lib/draw/cpaint.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + cpaint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPAINT_H +#define __CPAINT_H + +#include "gambas.h" +#include "gb.paint.h" + +#ifndef __CPAINT_C + +extern GB_DESC PaintExtentsDesc[]; +extern GB_DESC PaintMatrixDesc[]; +extern GB_DESC PaintBrushDesc[]; +extern GB_DESC PaintDesc[]; + +#endif + +GB_PAINT *PAINT_get_current(); +void *PAINT_get_current_device(); +GB_PAINT *PAINT_from_device(void *device); +bool PAINT_is_painted(void *device); +void PAINT_set_background(GB_COLOR color); +void PAINT_translate(float tx, float ty); +void PAINT_scale(float sx, float sy); + +bool PAINT_begin(void *device); +void PAINT_end(); +bool PAINT_open(GB_PAINT *paint); +void PAINT_close(GB_PAINT *paint); + +#endif diff --git a/main/lib/draw/gb.draw.h b/main/lib/draw/gb.draw.h new file mode 100644 index 00000000..facdc761 --- /dev/null +++ b/main/lib/draw/gb.draw.h @@ -0,0 +1,101 @@ +/*************************************************************************** + + gb.draw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DRAW_H +#define __GB_DRAW_H + +#include "gb_common.h" +#include "gambas.h" + +#define GB_DRAW_ALIGN_DEFAULT (-1) +#define GB_DRAW_COLOR_DEFAULT (-1) + +enum { + GB_DRAW_STATE_NORMAL = 0, + GB_DRAW_STATE_DISABLED = 1, + GB_DRAW_STATE_FOCUS = 2, + GB_DRAW_STATE_HOVER = 4, + GB_DRAW_STATE_ACTIVE = 8 + }; + +typedef + void *GB_PICTURE; + +typedef + struct { + int width; + int height; + } + GB_PICTURE_INFO; + +#ifndef __GB_IMAGE_DEFINED +#define __GB_IMAGE_DEFINED +typedef + void *GB_IMAGE; +#endif + +#ifndef __GB_COLOR_DEFINED +#define __GB_COLOR_DEFINED +typedef + unsigned int GB_COLOR; +#endif + +typedef + void *GB_FONT; + +#define DRAW_INTERFACE_VERSION 1 + +typedef + struct { + int version; + struct { + void *(*GetCurrent)(); + void (*Begin)(void *); + void (*End)(); + bool (*IsPainted)(void *); + void (*SetBackground)(GB_COLOR color); + void (*Translate)(float x, float y); + void (*Scale)(float sx, float sy); + } + Paint; + } + DRAW_INTERFACE; + +#define DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, width, height) \ + if (sw < 0) sw = width; \ + if (sh < 0) sh = height; \ + if (w < 0) w = sw; \ + if (h < 0) h = sh; \ + if (sx < 0) sw += sx, sx = 0; \ + if (sy < 0) sh += sy, sy = 0; \ + if (sw > ((width) - sx)) \ + sw = ((width) - sx); \ + if (sh > ((height) - sy)) \ + sh = ((height) - sy); \ + if (sx >= (width) || sy >= (height) || sw <= 0 || sh <= 0) \ + return; + +#endif + + + diff --git a/main/lib/draw/gb.geom.h b/main/lib/draw/gb.geom.h new file mode 120000 index 00000000..2ae286cf --- /dev/null +++ b/main/lib/draw/gb.geom.h @@ -0,0 +1 @@ +../geom/gb.geom.h \ No newline at end of file diff --git a/main/lib/draw/gb.image.h b/main/lib/draw/gb.image.h new file mode 120000 index 00000000..d53c98b6 --- /dev/null +++ b/main/lib/draw/gb.image.h @@ -0,0 +1 @@ +../image/gb.image.h \ No newline at end of file diff --git a/main/lib/draw/gb.paint.h b/main/lib/draw/gb.paint.h new file mode 100644 index 00000000..94cbacbc --- /dev/null +++ b/main/lib/draw/gb.paint.h @@ -0,0 +1,244 @@ +/*************************************************************************** + + gb.paint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_PAINT_H +#define __GB_PAINT_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.draw.h" + +enum { + GB_PAINT_EXTEND_PAD, + GB_PAINT_EXTEND_REPEAT, + GB_PAINT_EXTEND_REFLECT +}; + +enum { + GB_PAINT_FILL_RULE_WINDING, + GB_PAINT_FILL_RULE_EVEN_ODD +}; + +enum { + GB_PAINT_LINE_CAP_BUTT, + GB_PAINT_LINE_CAP_ROUND, + GB_PAINT_LINE_CAP_SQUARE +}; + +enum { + GB_PAINT_LINE_JOIN_MITER, + GB_PAINT_LINE_JOIN_ROUND, + GB_PAINT_LINE_JOIN_BEVEL +}; + +enum { + GB_PAINT_OPERATOR_CLEAR, + GB_PAINT_OPERATOR_SOURCE, + GB_PAINT_OPERATOR_OVER, + GB_PAINT_OPERATOR_IN, + GB_PAINT_OPERATOR_OUT, + GB_PAINT_OPERATOR_ATOP, + GB_PAINT_OPERATOR_DEST, + GB_PAINT_OPERATOR_DEST_OVER, + GB_PAINT_OPERATOR_DEST_IN, + GB_PAINT_OPERATOR_DEST_OUT, + GB_PAINT_OPERATOR_DEST_ATOP, + GB_PAINT_OPERATOR_XOR, + GB_PAINT_OPERATOR_ADD, + GB_PAINT_OPERATOR_SATURATE +}; + +enum { + GB_PAINT_PATH_MOVE, + GB_PAINT_PATH_LINE +}; + +struct GB_PAINT_DESC; + +typedef + struct { + int x, y, w, h; + } + GB_RECT; + +typedef + struct { + float x1, y1, x2, y2; + } + GB_EXTENTS; + +typedef + void *GB_BRUSH; + +typedef + void *GB_TRANSFORM; + +typedef + struct { + GB_BASE ob; + GB_EXTENTS ext; + } + PAINT_EXTENTS; + +typedef + struct { + GB_BASE ob; + GB_TRANSFORM transform; + } + PAINT_MATRIX; + +typedef + struct { + GB_BASE ob; + struct GB_PAINT_DESC *desc; // drawing driver + GB_BRUSH brush; // brush + } + PAINT_BRUSH; + +typedef + struct GB_PAINT { + struct GB_PAINT_DESC *desc; // drawing driver + struct GB_PAINT *previous; // previous drawing context + void *device; // drawing object + struct { + double x; + double y; + double width; + double height; + } area; // drawing area + int resolutionX; // device horizontal resolution in DPI + int resolutionY; // device vertical resolution in DPI + PAINT_BRUSH *brush; // current brush + double fontScale; // font scale + void *extra; // driver-specific state + unsigned opened : 1; // if the painting has been opened + unsigned other : 1; // if painting are imbricated on that device + unsigned has_path : 1; // if there is a current path + void *tag; // needed to support the old Draw class + } + GB_PAINT; + +typedef + void (*GB_PAINT_OUTLINE_CB)(int, float, float); + +typedef + struct GB_PAINT_DESC { + // Size of the GB_PAINT structure extra data + int size; + // Begins and terminates the drawing + int (*Begin)(GB_PAINT *d); + void (*End)(GB_PAINT *d); + + void (*Save)(GB_PAINT *d); + void (*Restore)(GB_PAINT *d); + + void (*Antialias)(GB_PAINT *d, int set, int *antialias); + + void (*Font)(GB_PAINT *d, int set, GB_FONT *font); + + void (*Background)(GB_PAINT *d, int set, GB_COLOR *color); + void (*Invert)(GB_PAINT *d, int set, int *invert); + + void (*Clip)(GB_PAINT *d, int preserve); + void (*ResetClip)(GB_PAINT *d); + void (*ClipExtents)(GB_PAINT *d, GB_EXTENTS *ext); + void (*ClipRect)(GB_PAINT *d, int x, int y, int w, int h); + + void (*Fill)(GB_PAINT *d, int preserve); + void (*Stroke)(GB_PAINT *d, int preserve); + + void (*PathExtents)(GB_PAINT *d, GB_EXTENTS *ext); + int (*PathContains)(GB_PAINT *d, float x, float y); + void (*PathOutline)(GB_PAINT *d, GB_PAINT_OUTLINE_CB cb); + + void (*Dash)(GB_PAINT *d, int set, float **dash, int *count); + void (*DashOffset)(GB_PAINT *d, int set, float *offset); + + void (*FillRule)(GB_PAINT *d, int set, int *value); + void (*FillStyle)(GB_PAINT *d, int set, int *value); + void (*LineCap)(GB_PAINT *d, int set, int *value); + void (*LineJoin)(GB_PAINT *d, int set, int *value); + void (*LineWidth)(GB_PAINT *d, int set, float *value); + void (*MiterLimit)(GB_PAINT *d, int set, float *value); + + void (*Operator)(GB_PAINT *d, int set, int *value); + + void (*NewPath)(GB_PAINT *d); + void (*ClosePath)(GB_PAINT *d); + + void (*Arc)(GB_PAINT *d, float xc, float yc, float radius, float angle, float length, bool pie); + void (*Ellipse)(GB_PAINT *d, float x, float y, float width, float height, float angle, float length, bool pie); + void (*Rectangle)(GB_PAINT *d, float x, float y, float width, float height); + void (*GetCurrentPoint)(GB_PAINT *d, float *x, float *y); + void (*MoveTo)(GB_PAINT *d, float x, float y); + void (*LineTo)(GB_PAINT *d, float x, float y); + void (*CurveTo)(GB_PAINT *d, float x1, float y1, float x2, float y2, float x3, float y3); + + void (*Text)(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw); + void (*TextExtents)(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext); + void (*TextSize)(GB_PAINT *d, const char *text, int len, float *w, float *h); + void (*RichText)(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw); + void (*RichTextExtents)(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext, float width); + void (*RichTextSize)(GB_PAINT *d, const char *text, int len, float width, float *w, float *h); + + void (*Matrix)(GB_PAINT *d, int set, GB_TRANSFORM matrix); + + void (*SetBrush)(GB_PAINT *d, GB_BRUSH brush); + void (*BrushOrigin)(GB_PAINT *d, int set, float *x, float *y); + + void (*DrawImage)(GB_PAINT *d, GB_IMAGE image, float x, float y, float w, float h, float opacity, GB_RECT *source); + void (*DrawPicture)(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source); + void (*GetPictureInfo)(GB_PAINT *d, GB_PICTURE picture, GB_PICTURE_INFO *info); + void (*FillRect)(GB_PAINT *d, float x, float y, float width, float height, GB_COLOR color); + + struct { + void (*Free)(GB_BRUSH brush); + void (*Color)(GB_BRUSH *brush, GB_COLOR color); + void (*Image)(GB_BRUSH *brush, GB_IMAGE image); + void (*LinearGradient)(GB_BRUSH *brush, float x0, float y0, float x1, float y1, int nstop, double *positions, GB_COLOR *colors, int extend); + void (*RadialGradient)(GB_BRUSH *brush, float cx, float cy, float r, float fx, float fy, int nstop, double *positions, GB_COLOR *colors, int extend); + void (*Matrix)(GB_BRUSH brush, int set, GB_TRANSFORM matrix); + } + Brush; + } + GB_PAINT_DESC; + +typedef + struct GB_PAINT_MATRIX_DESC { + void (*Create)(GB_TRANSFORM *matrix); + void (*Copy)(GB_TRANSFORM *matrix, GB_TRANSFORM copy); + void (*Delete)(GB_TRANSFORM *matrix); + void (*Init)(GB_TRANSFORM matrix, float xx, float yx, float xy, float yy, float x0, float y0); + void (*Translate)(GB_TRANSFORM matrix, float tx, float ty); + void (*Scale)(GB_TRANSFORM matrix, float sx, float sy); + void (*Rotate)(GB_TRANSFORM matrix, float angle); + int (*Invert)(GB_TRANSFORM matrix); + void (*Multiply)(GB_TRANSFORM matrix, GB_TRANSFORM matrix2); + void (*Map)(GB_TRANSFORM matrix, double *x, double *y); + } + GB_PAINT_MATRIX_DESC; + +#endif + + + diff --git a/main/lib/draw/gb_list.c b/main/lib/draw/gb_list.c new file mode 100644 index 00000000..281dbe19 --- /dev/null +++ b/main/lib/draw/gb_list.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_list.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_list_temp.h" diff --git a/main/lib/draw/main.c b/main/lib/draw/main.c new file mode 100644 index 00000000..e37da1ca --- /dev/null +++ b/main/lib/draw/main.c @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "cpaint.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; +GEOM_INTERFACE GEOM EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES [] EXPORT = +{ + PaintExtentsDesc, + PaintMatrixDesc, + PaintBrushDesc, + PaintDesc, + NULL +}; + +void *GB_DRAW_1[] EXPORT = +{ + (void *)1, + (void *)PAINT_get_current, + (void *)PAINT_begin, + (void *)PAINT_end, + (void *)PAINT_is_painted, + (void *)PAINT_set_background, + (void *)PAINT_translate, + (void *)PAINT_scale, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.geom"; + +int EXPORT GB_INIT(void) +{ + GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + GB.Component.Load("gb.image"); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/draw/main.h b/main/lib/draw/main.h new file mode 100644 index 00000000..12381756 --- /dev/null +++ b/main/lib/draw/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb.geom.h" +#include "gb.draw.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +extern GEOM_INTERFACE GEOM; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/draw/matrix.c b/main/lib/draw/matrix.c new file mode 100644 index 00000000..bb5e4c2f --- /dev/null +++ b/main/lib/draw/matrix.c @@ -0,0 +1,221 @@ +/*************************************************************************** + + matrix.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MATRIX_C + +#include "gb_common.h" +#include "matrix.h" +#include "main.h" + +static INLINE int DROUND(double d) +{ + return d >= 0.0 ? (int)(d + 0.5) : (int)( d - ((int)d-1) + 0.5 ) + ((int)d-1); +} + +static INLINE double DMIN(double a, double b) +{ + return (a > b ? b : a); +} + +static INLINE double DMAX(double a, double b) +{ + return (a > b ? a : b); +} + +void MATRIX_reset(MATRIX *matrix) +{ + matrix->m11 = matrix->m22 = 1; + matrix->m12 = matrix->m21 = 0; + matrix->dx = matrix->dy = 0; + matrix->identity = TRUE; + matrix->rotation = FALSE; +} + +void MATRIX_init(MATRIX *matrix) +{ + matrix->next = NULL; + MATRIX_reset(matrix); +} + +#define MAPDOUBLE(mat, x, y, nx, ny ) \ +{ \ + double fx = x; \ + double fy = y; \ + nx = mat->m11*fx + mat->m21*fy + mat->dx; \ + ny = mat->m12*fx + mat->m22*fy + mat->dy; \ +} + +void MATRIX_map_point(MATRIX *matrix, int *x, int *y) +{ + double nx; + double ny; + + MAPDOUBLE(matrix, *x, *y, nx, ny); + + *x = DROUND(nx); + *y = DROUND(ny); +} + +void MATRIX_map_rect(MATRIX *matrix, int *x, int *y, int *w, int *h) +{ + int rx, ry, rw, rh; + + if (matrix->m12 == 0.0F && matrix->m21 == 0.0F) + { + rx = DROUND(matrix->m11 * *x + matrix->dx); + ry = DROUND(matrix->m22 * *y + matrix->dy); + rw = DROUND(matrix->m11 * *w); + rh = DROUND(matrix->m22 * *h); + + if (rw < 0) + { + rw = -rw; + rx -= rw - 1; + } + + if (rh < 0) + { + rh = -rh; + ry -= rh-1; + } + } + else + { + int left = *x; + int top = *y; + int right = *x + *w; + int bottom = *y + *h; + + double x0, y0; + double x, y; + MAPDOUBLE(matrix, left, top, x0, y0 ); + double xmin = x0; + double ymin = y0; + double xmax = x0; + double ymax = y0; + MAPDOUBLE(matrix, right, top, x, y ); + xmin = DMIN( xmin, x ); + ymin = DMIN( ymin, y ); + xmax = DMAX( xmax, x ); + ymax = DMAX( ymax, y ); + MAPDOUBLE(matrix, right, bottom, x, y ); + xmin = DMIN( xmin, x ); + ymin = DMIN( ymin, y ); + xmax = DMAX( xmax, x ); + ymax = DMAX( ymax, y ); + MAPDOUBLE(matrix, left, bottom, x, y ); + xmin = DMIN( xmin, x ); + ymin = DMIN( ymin, y ); + xmax = DMAX( xmax, x ); + ymax = DMAX( ymax, y ); + double ww = xmax - xmin; + double hh = ymax - ymin; + xmin -= ( xmin - x0 ) / ww; + ymin -= ( ymin - y0 ) / hh; + xmax -= ( xmax - x0 ) / ww; + ymax -= ( ymax - y0 ) / hh; + + rx = DROUND(xmin); + ry = DROUND(ymin); + rw = DROUND(xmax) - DROUND(xmin) + 1; + rh = DROUND(ymax) - DROUND(ymin) + 1; + } + + *x = rx; + *y = ry; + *w = rw; + *h = rh; +} + +int *MATRIX_map_array(MATRIX *matrix, int *coord, int npoint) +{ + int *map_coord, *map; + int i; + + GB.Alloc(POINTER(&map_coord), sizeof(int) * npoint * 2); + + map = map_coord; + for (i = 0; i < npoint; i++) + { + map[0] = coord[0]; + map[1] = coord[1]; + MATRIX_map_point(matrix, &map[0], &map[1]); + coord += 2; + map += 2; + } + + return map_coord; +} + +void MATRIX_free_array(int **coord) +{ + GB.Free(POINTER(coord)); +} + + +static void update_flag(MATRIX *matrix) +{ + matrix->identity = + matrix->m11 == 1.0 && matrix->m22 == 1.0 + && matrix->m12 == 0.0 && matrix->m21 == 0.0 + && matrix->dx == 0.0 && matrix->dy == 0.0; + + matrix->rotation = matrix->m12 != 0.0 || matrix->m21 != 0.0; +} + +void MATRIX_translate(MATRIX *matrix, double dx, double dy) +{ + matrix->dx += dx * matrix->m11 + dy * matrix->m21; + matrix->dy += dy * matrix->m22 + dx * matrix->m12; + + update_flag(matrix); +} + +void MATRIX_scale(MATRIX *matrix, double sx, double sy) +{ + matrix->m11 *= sx; + matrix->m12 *= sx; + matrix->m21 *= sy; + matrix->m22 *= sy; + + update_flag(matrix); +} + +void MATRIX_rotate(MATRIX *matrix, double angle) +{ + double sina = sin(angle); + double cosa = cos(angle); + + double m11 = cosa * matrix->m11 + sina * matrix->m21; + double m12 = cosa * matrix->m12 + sina * matrix->m22; + double m21 = sina * matrix->m11 + cosa * matrix->m21; + double m22 = sina * matrix->m12 + cosa * matrix->m22; + + matrix->m11 = m11; + matrix->m12 = m12; + matrix->m21 = m21; + matrix->m22 = m22; + + update_flag(matrix); +} + diff --git a/main/lib/draw/matrix.h b/main/lib/draw/matrix.h new file mode 100644 index 00000000..0f701117 --- /dev/null +++ b/main/lib/draw/matrix.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + matrix.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MATRIX_H +#define __MATRIX_H + +#include "gb_common.h" +#include "gb.draw.h" + +typedef + GB_MATRIX + MATRIX; + +void MATRIX_init(MATRIX *matrix); +void MATRIX_reset(MATRIX *matrix); + +void MATRIX_translate(MATRIX *matrix, double dx, double dy); +void MATRIX_scale(MATRIX *matrix, double sx, double sy); +void MATRIX_rotate(MATRIX *matrix, double angle); + +void MATRIX_map_point(MATRIX *matrix, int *x, int *y); +void MATRIX_map_rect(MATRIX *matrix, int *x, int *y, int *w, int *h); + +int *MATRIX_map_array(MATRIX *matrix, int *coord, int npoint); +void MATRIX_free_array(int **coord); + +#define MATRIX_is_identity(_matrix) ((_matrix)->identity) + +#endif diff --git a/main/lib/eval/Makefile.am b/main/lib/eval/Makefile.am new file mode 100644 index 00000000..c7cd311a --- /dev/null +++ b/main/lib/eval/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.eval +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.eval.la + +gb_eval_la_LIBADD = +gb_eval_la_LDFLAGS = -module @LD_FLAGS@ +gb_eval_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_eval_la_SOURCES = \ + gb_alloc_override.h \ + gb_error.h gb_error.c \ + gb_array.c \ + gb_table.c \ + eval_code.h eval_code.c \ + eval_read.h eval_read.c \ + eval_reserved.c \ + eval_trans.h eval_trans.c \ + eval_trans_expr.c \ + eval_trans_tree.c \ + eval.h eval.c \ + eval_analyze.h eval_analyze.c \ + c_expression.h c_expression.c \ + c_system.h c_system.c \ + c_highlight.h c_highlight.c \ + gb.eval.h \ + main.h main.c + + diff --git a/main/lib/eval/c_expression.c b/main/lib/eval/c_expression.c new file mode 100644 index 00000000..28db8931 --- /dev/null +++ b/main/lib/eval/c_expression.c @@ -0,0 +1,196 @@ +/*************************************************************************** + + c_expression.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_EXPRESSION_C + +#include "gb_common.h" +#include "gambas.h" + +#include "eval.h" +#include "main.h" + +#include "c_expression.h" + +/*#define DEBUG*/ + +static CEXPRESSION *_current; + + +BEGIN_METHOD_VOID(Expression_new) + + THIS->compiled = FALSE; + CLEAR(&THIS->expr); + THIS->expr.parent = THIS; + THIS->expr.custom = GB.GetClass(THIS) != CLASS_Expression; + +END_METHOD + + +BEGIN_METHOD_VOID(Expression_free) + + EVAL_clear(&THIS->expr, FALSE); + GB.FreeString(&THIS->text); + GB.FreeString(&THIS->expr.source); + GB.Unref((void **)&THIS->env); + +END_METHOD + + +BEGIN_PROPERTY(Expression_Text) + + if (READ_PROPERTY) + GB.ReturnString(THIS->text); + else + { + GB.StoreString(PROP(GB_STRING), &THIS->text); + GB.FreeString(&THIS->expr.source); + THIS->expr.source = GB.NewString(THIS->text, VPROP(GB_STRING).len); + THIS->expr.len = VPROP(GB_STRING).len; + THIS->compiled = FALSE; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Expression_Environment) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->env); + else + GB.StoreObject(PROP(GB_OBJECT), &THIS->env); + +END_PROPERTY + +static void prepare(CEXPRESSION *_object) +{ + if (!THIS->compiled && (THIS->expr.len > 0)) + { + if (!EVAL_compile(&THIS->expr, FALSE)) + THIS->compiled = TRUE; + else + GB.Error(THIS->expr.error); + } +} + +BEGIN_METHOD_VOID(Expression_Prepare) + + prepare(THIS); + +END_METHOD + + +static bool get_variable(const char *sym, int len, GB_VARIANT *value) +{ + if (_current->env) + if (!GB.Collection.Get(_current->env, sym, len, value)) + return FALSE; + + if (_current->expr.custom) + { + GB_FUNCTION func; + GB_VALUE *ret; + + if (!GB.GetFunction(&func, (void *)_current, "GetValue", NULL, NULL)) + { + GB.Push(1, GB_T_STRING, sym, len); + ret = GB.Call(&func, 1, FALSE); + *value = ret->_variant; + return FALSE; //value->type == GB_T_NULL; + } + } + + value->type = GB_T_NULL; + return TRUE; +} + +static void execute(CEXPRESSION *_object) +{ + CEXPRESSION *save_current; + + if (!THIS->compiled) + prepare(THIS); + + if (!THIS->compiled) + { + GB.ReturnVariant(NULL); + return; + } + + save_current = _current; + _current = THIS; + EVAL_expression(&THIS->expr, (EVAL_FUNCTION)get_variable); + GB.ReturnConvVariant(); + _current = save_current; +} + +BEGIN_PROPERTY(Expression_Value) + + execute(THIS); + +END_PROPERTY + +BEGIN_METHOD(Expression_IsSubr, GB_STRING name) + + (_p); + GB.ReturnBoolean(TRUE); + +END_METHOD + +BEGIN_METHOD(Expression_IsIdentifier, GB_STRING name) + + (_p); + GB.ReturnBoolean(TRUE); + +END_METHOD + +BEGIN_METHOD(Expression_GetValue, GB_STRING name) + + (_p); + GB.ReturnVariant(NULL); + +END_METHOD + +GB_DESC CExpressionDesc[] = +{ + GB_DECLARE("Expression", sizeof(CEXPRESSION)), + + //GB_STATIC_METHOD("_init", NULL, Expression_init, NULL), + //GB_STATIC_METHOD("_exit", NULL, Expression_exit, NULL), + + GB_METHOD("_new", NULL, Expression_new, NULL), + GB_METHOD("_free", NULL, Expression_free, NULL), + + GB_PROPERTY("Text", "s", Expression_Text), + GB_PROPERTY("Environment", "Collection;", Expression_Environment), + + GB_METHOD("Compile", NULL, Expression_Prepare, NULL), + + GB_PROPERTY_READ("Value", "v", Expression_Value), + + GB_STATIC_METHOD("IsSubr", "b", Expression_IsSubr, "(Name)s"), + GB_STATIC_METHOD("IsIdentifier", "b", Expression_IsIdentifier, "(Name)s"), + GB_METHOD("GetValue", "v", Expression_GetValue, "(Name)s"), + + GB_END_DECLARE +}; + diff --git a/main/lib/eval/c_expression.h b/main/lib/eval/c_expression.h new file mode 100644 index 00000000..d606e2e7 --- /dev/null +++ b/main/lib/eval/c_expression.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + c_expression.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_EXPRESSION_H +#define __C_EXPRESSION_H + +#include "gambas.h" +#include "eval.h" + +#ifndef __C_EXPRESSION_C +extern GB_DESC CExpressionDesc[]; +#else + +#define THIS OBJECT(CEXPRESSION) + +#endif + +typedef + struct { + GB_BASE ob; + char *text; + void *env; + EXPRESSION expr; + int pos; + bool compiled; + } + CEXPRESSION; + +#endif diff --git a/main/lib/eval/c_highlight.c b/main/lib/eval/c_highlight.c new file mode 100644 index 00000000..9e9adbad --- /dev/null +++ b/main/lib/eval/c_highlight.c @@ -0,0 +1,317 @@ +/*************************************************************************** + + c_highlight.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_HIGHLIGHT_CPP + +#include "gb_common.h" +#include "main.h" +#include "gb.eval.h" +#include "eval_analyze.h" + +#include "c_highlight.h" + +static void *_analyze_symbol = 0; +static void *_analyze_type = 0; +static void *_analyze_pos = 0; +static char *_analyze_text = 0; + +static char *_purged_line = NULL; + +static int get_char_length(unsigned char c) +{ + int n = 1; + + if (c & 0x80) + { + for (;;) + { + c <<= 1; + if (!(c & 0x80)) + break; + n++; + } + } + + return n; +} + +static char *purge(const char *s, int len_s, bool comment, bool string) +{ + char c; + uint i; + int lc, ls; + bool in_comment = FALSE; + char wait = 0; + char *r = NULL; + + for (i = 0; i < len_s; i += ls) + { + c = s[i]; + ls = lc = get_char_length((unsigned char)c); + + switch(wait) + { + case 0: + + if (in_comment) + { + if (!comment) + c = ' ', lc = 1; + } + else if (c == '"') + wait = '"'; + else if (c == '\'') + in_comment = TRUE; + + break; + + case '"': + if (c == '"') + wait = 0; + else if (c == '\\') + { + if (string) + { + if (i < len_s) + r = GB.AddChar(r, c); + //r += c; + i++; + c = s[i]; + lc = get_char_length((unsigned char)c); + } + else + { + i++; + if (i < len_s) + r = GB.AddChar(r, ' '); + //r += ' '; + c = ' ', lc = 1; + } + } + else + { + if (!string) + c = ' ', lc = 1; + } + break; + } + + if (lc == 1) + r = GB.AddChar(r, c); + else + r = GB.AddString(r, &s[i], lc); + } + + GB.FreeString(&_purged_line); + _purged_line = r; + + return r; +} + + +static int convState(int state) +{ + switch(state) + { + case RT_END: return HIGHLIGHT_NORMAL; + case RT_RESERVED: return HIGHLIGHT_KEYWORD; + case RT_IDENTIFIER: return HIGHLIGHT_SYMBOL; + case RT_CLASS: return HIGHLIGHT_DATATYPE; + case RT_INTEGER: return HIGHLIGHT_NUMBER; + case RT_NUMBER: return HIGHLIGHT_NUMBER; + case RT_STRING: return HIGHLIGHT_STRING; + case RT_SUBR: return HIGHLIGHT_SUBR; + case RT_COMMENT: return HIGHLIGHT_COMMENT; + case RT_OPERATOR: return HIGHLIGHT_OPERATOR; + case RT_DATATYPE: return HIGHLIGHT_DATATYPE; + case RT_ERROR: return HIGHLIGHT_ERROR; + case RT_HELP: return HIGHLIGHT_HELP; + case RT_PREPROCESSOR: return HIGHLIGHT_PREPROCESSOR; + case RT_ESCAPE: return HIGHLIGHT_ESCAPE; + case RT_CONSTANT: return HIGHLIGHT_CONSTANT; + case RT_LABEL: return HIGHLIGHT_LABEL; + default: return HIGHLIGHT_NORMAL; + } +} + +static void analyze(const char *src, int len_src, bool rewrite, int state) +{ + GB_ARRAY garray, tarray, parray; + int i, n, pos, len, p, upos, ulen, l; + char *str; + EVAL_ANALYZE result; + + EVAL_analyze(src, len_src, state == HIGHLIGHT_COMMENT ? RT_COMMENT : RT_END, &result, rewrite); + + n = 0; + for (i = 0; i < result.len; i++) + { + if (result.color[i].state == RT_END) + break; + if (result.color[i].state != RT_SPACE) + n++; + } + + GB.Array.New(&garray, GB_T_STRING, n); + GB.Array.New(&tarray, GB_T_INTEGER, n); + GB.Array.New(&parray, GB_T_INTEGER, n); + + pos = 0; + upos = 0; + i = 0; + for (p = 0; p < result.len; p++) + { + len = result.color[p].len; + + if (result.color[p].state == RT_SPACE) + { + ulen = len; + } + else + { + ulen = 0; + for (l = 0; l < len; l++) + ulen += get_char_length(result.str[upos + ulen]); + + str = GB.NewString(&result.str[upos], ulen); + *((char **)GB.Array.Get(garray, i)) = str; + *((int *)GB.Array.Get(tarray, i)) = convState(result.color[p].state); + *((int *)GB.Array.Get(parray, i)) = pos; + i++; + } + + pos += len; + upos += ulen; + } + + GB.Unref(&_analyze_symbol); + _analyze_symbol = garray; + GB.Ref(garray); + + GB.Unref(&_analyze_type); + _analyze_type = tarray; + GB.Ref(tarray); + + GB.Unref(&_analyze_pos); + _analyze_pos = parray; + GB.Ref(parray); + + GB.FreeString(&_analyze_text); + _analyze_text = result.str; +} + +BEGIN_METHOD_VOID(Highlight_exit) + + GB.Unref(&_analyze_symbol); + GB.Unref(&_analyze_type); + GB.Unref(&_analyze_pos); + GB.FreeString(&_analyze_text); + GB.FreeString(&_purged_line); + +END_METHOD + + + +BEGIN_METHOD(Highlight_Purge, GB_STRING text; GB_BOOLEAN comment; GB_BOOLEAN string) + + bool comment = VARGOPT(comment, FALSE); + bool string = VARGOPT(string, FALSE); + + if (comment && string) + GB.ReturnNewString(STRING(text), LENGTH(text)); + else + GB.ReturnString(purge(STRING(text), LENGTH(text), comment, string)); + +END_METHOD + +BEGIN_METHOD(Highlight_Analyze, GB_STRING text; GB_BOOLEAN rewrite; GB_INTEGER state) + + analyze(STRING(text), LENGTH(text), VARGOPT(rewrite, FALSE), VARGOPT(state, HIGHLIGHT_NORMAL)); + GB.ReturnObject(_analyze_symbol); + +END_METHOD + +BEGIN_PROPERTY(Highlight_Symbols) + + GB.ReturnObject(_analyze_symbol); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Types) + + GB.ReturnObject(_analyze_type); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Positions) + + GB.ReturnObject(_analyze_pos); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_TextAfter) + + GB.ReturnString(_analyze_text); + +END_PROPERTY + +GB_DESC CHighlightDesc[] = +{ + GB_DECLARE("Highlight", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Background", "i", HIGHLIGHT_BACKGROUND), + GB_CONSTANT("Normal", "i", HIGHLIGHT_NORMAL), + GB_CONSTANT("Keyword", "i", HIGHLIGHT_KEYWORD), + GB_CONSTANT("Function", "i", HIGHLIGHT_SUBR), + GB_CONSTANT("Operator", "i", HIGHLIGHT_OPERATOR), + GB_CONSTANT("Symbol", "i", HIGHLIGHT_SYMBOL), + GB_CONSTANT("Number", "i", HIGHLIGHT_NUMBER), + GB_CONSTANT("String", "i", HIGHLIGHT_STRING), + GB_CONSTANT("Comment", "i", HIGHLIGHT_COMMENT), + GB_CONSTANT("Breakpoint", "i", HIGHLIGHT_BREAKPOINT), + GB_CONSTANT("Current", "i", HIGHLIGHT_CURRENT), + GB_CONSTANT("DataType", "i", HIGHLIGHT_DATATYPE), + GB_CONSTANT("Selection", "i", HIGHLIGHT_SELECTION), + GB_CONSTANT("Highlight", "i", HIGHLIGHT_HIGHLIGHT), + GB_CONSTANT("CurrentLine", "i", HIGHLIGHT_LINE), + GB_CONSTANT("Error", "i", HIGHLIGHT_ERROR), + GB_CONSTANT("Help", "i", HIGHLIGHT_HELP), + GB_CONSTANT("Preprocessor", "i", HIGHLIGHT_PREPROCESSOR), + GB_CONSTANT("Escape", "i", HIGHLIGHT_ESCAPE), + GB_CONSTANT("Label", "i", HIGHLIGHT_LABEL), + GB_CONSTANT("Constant", "i", HIGHLIGHT_CONSTANT), + GB_CONSTANT("Alternate", "i", HIGHLIGHT_ALTERNATE), + GB_CONSTANT("Added", "i", HIGHLIGHT_ADDED), + GB_CONSTANT("Removed", "i", HIGHLIGHT_REMOVED), + GB_CONSTANT("Custom", "i", HIGHLIGHT_NUM_COLOR), + + GB_STATIC_METHOD("_exit", NULL, Highlight_exit, NULL), + GB_STATIC_METHOD("Analyze", "String[]", Highlight_Analyze, "(Code)s[(Rewrite)b(State)i]"), + GB_STATIC_PROPERTY_READ("Symbols", "String[]", Highlight_Symbols), + GB_STATIC_PROPERTY_READ("Types", "Integer[]", Highlight_Types), + GB_STATIC_PROPERTY_READ("Positions", "Integer[]", Highlight_Positions), + GB_STATIC_PROPERTY_READ("TextAfter", "s", Highlight_TextAfter), + GB_STATIC_METHOD("Purge", "s", Highlight_Purge, "(Code)s[(Comment)b(String)b]"), + + GB_END_DECLARE +}; diff --git a/main/lib/eval/c_highlight.h b/main/lib/eval/c_highlight.h new file mode 100644 index 00000000..d690baab --- /dev/null +++ b/main/lib/eval/c_highlight.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + c_highlight.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_HIGHLIGHT_H +#define __C_HIGHLIGHT_H + +#include "gambas.h" +#include "gb.eval.h" + +#ifndef __C_HIGHLIGHT_C + +extern GB_DESC CHighlightDesc[]; + +#endif + +#endif diff --git a/main/lib/eval/c_system.c b/main/lib/eval/c_system.c new file mode 100644 index 00000000..ed50299d --- /dev/null +++ b/main/lib/eval/c_system.c @@ -0,0 +1,131 @@ +/*************************************************************************** + + c_system.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_SYSTEM_C + +#include "gambas.h" +#include "gb_common.h" +#include "gb_reserved.h" +#include "c_system.h" + +static GB_ARRAY _keywords = 0; +static GB_ARRAY _datatypes = 0; +static GB_ARRAY _subroutines = 0; + +BEGIN_PROPERTY(System_Keywords) + + COMP_INFO *info; + char *str; + + if (!_keywords) + { + GB.Array.New(&_keywords, GB_T_STRING, 0); + + for (info = &COMP_res_info[1]; info->name; info++) + { + if (*info->name >= 'A' && *info->name <= 'Z') + { + str = GB.NewZeroString(info->name); + *((char **)GB.Array.Add(_keywords)) = str; + } + } + + GB.Array.SetReadOnly(_keywords); + GB.Ref(_keywords); + } + + GB.ReturnObject(_keywords); + +END_PROPERTY + +BEGIN_PROPERTY(System_Subroutines) + + SUBR_INFO *subr; + char *str; + + if (!_subroutines) + { + GB.Array.New(&_subroutines, GB_T_STRING, 0); + + for (subr = &COMP_subr_info[0]; subr->name; subr++) + { + str = GB.NewZeroString(subr->name); + *((char **)GB.Array.Add(_subroutines)) = str; + } + + GB.Array.SetReadOnly(_subroutines); + GB.Ref(_subroutines); + } + + GB.ReturnObject(_subroutines); + +END_PROPERTY + +BEGIN_PROPERTY(System_Datatypes) + + COMP_INFO *info; + char *str; + + if (!_datatypes) + { + GB.Array.New(&_datatypes, GB_T_STRING, 0); + + for (info = &COMP_res_info[1]; info->name; info++) + { + if (info->flag & RSF_TYPE) + { + str = GB.NewZeroString(info->name); + *((char **)GB.Array.Add(_datatypes)) = str; + } + } + + GB.Array.SetReadOnly(_datatypes); + GB.Ref(_datatypes); + } + + GB.ReturnObject(_datatypes); + +END_PROPERTY + +BEGIN_METHOD_VOID(CSYSTEM_exit) + + GB.Unref((void **)&_keywords); + GB.Unref((void **)&_datatypes); + GB.Unref((void **)&_subroutines); + +END_METHOD + + +GB_DESC CSystemDesc[] = +{ + GB_DECLARE("System", 0), + + GB_STATIC_METHOD("_exit", NULL, CSYSTEM_exit, NULL), + + GB_STATIC_PROPERTY_READ("Keywords", "String[]", System_Keywords), + GB_STATIC_PROPERTY_READ("Datatypes", "String[]", System_Datatypes), + GB_STATIC_PROPERTY_READ("Subroutines", "String[]", System_Subroutines), + + GB_END_DECLARE +}; + diff --git a/main/lib/eval/c_system.h b/main/lib/eval/c_system.h new file mode 100644 index 00000000..5ac17159 --- /dev/null +++ b/main/lib/eval/c_system.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + c_system.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_SYSTEM_H +#define __C_SYSTEM_H + +#include "gambas.h" +#include "gb_common.h" +#include "eval.h" + +#ifndef __C_SYSTEM_C +extern GB_DESC CSystemDesc[]; +#endif + +#endif diff --git a/main/lib/eval/eval.c b/main/lib/eval/eval.c new file mode 100644 index 00000000..8234f363 --- /dev/null +++ b/main/lib/eval/eval.c @@ -0,0 +1,291 @@ +/*************************************************************************** + + eval.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_C + +#include "gb_alloc_override.h" +#include "gb_common.h" +#include "gb_error.h" +#include "gb_array.h" + +#include "eval_analyze.h" +#include "eval_trans.h" +#include "gb_code.h" +#include "eval.h" + +#include "gb_common_case_temp.h" + +/*#define DEBUG*/ + +EXPRESSION *EVAL; +EXPRESSION EVAL_read_expr; + + +void EVAL_init(void) +{ + RESERVED_init(); + CLEAR(&EVAL_read_expr); +} + + +void EVAL_exit(void) +{ + EVAL_clear(&EVAL_read_expr, FALSE); + RESERVED_exit(); + EVAL_analyze_exit(); +} + +GB_VALUE *EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION get_value) +{ + EVAL = expr; + + /* Creates a class and a function for the evaluation context */ + + CLEAR(&EVAL->func); + EVAL->func.type = T_VARIANT; + EVAL->func.n_param = EVAL->nvar; + EVAL->func.npmin = EVAL->nvar; + EVAL->func.stack_usage = EVAL->stack_usage; + EVAL->func.code = EVAL->code; + + CLEAR(&EVAL->class_load); + EVAL->class_load.cst = EVAL->cst; + EVAL->class_load.func = &EVAL->func; + EVAL->class_load.class_ref = EVAL->class; + EVAL->class_load.unknown = EVAL->unknown; + + CLEAR(&EVAL->exec_class); + /*_class.class = CLASS_class;*/ + EVAL->exec_class.ref = 1; + EVAL->exec_class.count = 1; + EVAL->exec_class.name = ".Eval"; + EVAL->exec_class.loaded = TRUE; + EVAL->exec_class.ready = TRUE; + EVAL->exec_class.load = &EVAL->class_load; + + return GB.Eval(EVAL, get_value); +} + + +void EVAL_clear(EXPRESSION *expr, bool keep_error) +{ + ARRAY_delete(&expr->tree); + + ARRAY_delete(&expr->var); + ARRAY_delete(&expr->unknown); + ARRAY_delete(&expr->class); + ARRAY_delete(&expr->cst); + + TABLE_delete(&expr->string); + TABLE_delete(&expr->table); + + if (expr->pattern) + FREE(&expr->pattern); + if (expr->code) + FREE(&expr->code); + + if (!keep_error) + GB.FreeString(&expr->error); +} + + +void EVAL_start(EXPRESSION *expr) +{ + int index; + + ALLOC(&expr->pattern, sizeof(PATTERN) * (16 + expr->len)); + expr->pattern_count = 0; + + TABLE_create(&expr->table, sizeof(EVAL_SYMBOL), EVAL->analyze ? TF_NORMAL : TF_IGNORE_CASE); + TABLE_create(&expr->string, sizeof(SYMBOL), TF_NORMAL); + + ARRAY_create(&expr->cst); + ARRAY_create(&expr->class); + ARRAY_create(&expr->unknown); + expr->code = NULL; + expr->ncode = 0; + expr->ncode_max = 0; + ARRAY_create(&expr->var); + expr->nvar = 0; + + if (EVAL->custom) + { + // _ is the first symbol, it returns the Expression object + index = TABLE_add_symbol(expr->table, "_", 1); + EVAL_add_variable(index); + } +} + +bool EVAL_compile(EXPRESSION *expr, bool assign) +{ + bool error = FALSE; + + EVAL = expr; + + EVAL_clear(EVAL, FALSE); + + if (expr->len == 0) + return TRUE; + + EVAL_start(EVAL); + + TRY + { + EVAL_read(); + + EVAL->current = EVAL->pattern; + + if (PATTERN_is(*EVAL->current, RS_LET)) + { + EVAL->current++; + assign = TRUE; + } + + if (assign) + { + if (!TRANS_affectation()) + THROW(E_SYNTAX); + } + else + TRANS_expression(); + + if (!PATTERN_is_end(*EVAL->current)) + THROW(E_SYNTAX); + + CODE_return(1); + + EVAL->stack_usage = CODE_stack_usage; + } + CATCH + { + EVAL_clear(EVAL, TRUE); + error = TRUE; + } + END_TRY + + #ifdef DEBUG + CODE_dump(EVAL->code); + printf("Stack usage = %d\n", CODE_stack_usage); + #endif + + return error; +} + +bool EVAL_get_assignment_symbol(EXPRESSION *expr, const char **name, int *len) +{ + ushort code = EVAL->assign_code; + int index; + EVAL_SYMBOL *sym; + + if ((code & 0xFF00) == C_POP_PARAM) + { + index = -((signed char)(code & 0xFF)) - 1; + sym = (EVAL_SYMBOL *)TABLE_get_symbol(EVAL->table, EVAL->var[index]); + *name = sym->sym.name; + *len = sym->sym.len; + return FALSE; + } + else + return TRUE; +} + +void EVAL_new(EXPRESSION **expr, char *src, int len) +{ + GB.Alloc((void **)expr, sizeof(EXPRESSION)); + CLEAR(*expr); + (*expr)->source = GB.NewString(src, len); + (*expr)->source = GB.AddString((*expr)->source, "\n\0", 2); + (*expr)->len = len + 2; + /*(*expr)->option = option;*/ +} + + +void EVAL_free(EXPRESSION **pexpr) +{ + EVAL_clear(*pexpr, FALSE); + GB.FreeString(&(*pexpr)->source); + GB.Free((void **)pexpr); +} + + +int EVAL_add_constant(CLASS_CONST *cst) +{ + int num; + CLASS_CONST *desc; + + num = ARRAY_count(EVAL->cst); + + desc = ARRAY_add(&EVAL->cst); + *desc = *cst; + + return num; +} + + +int EVAL_add_class(char *name) +{ + int num; + CLASS **desc; + + num = ARRAY_count(EVAL->class); + + desc = ARRAY_add(&EVAL->class); + *desc = (CLASS *)GB.FindClassLocal(name); + + /*sym->class = num + 1;*/ + + return num; +} + + +int EVAL_add_unknown(char *name) +{ + int num; + char **desc; + + num = ARRAY_count(EVAL->unknown); + + desc = ARRAY_add(&EVAL->unknown); + *desc = name; + + return num; +} + + +int EVAL_add_variable(int index) +{ + EVAL_SYMBOL *sym; + + sym = (EVAL_SYMBOL *)TABLE_get_symbol(EVAL->table, index); + + if (sym->local == 0) + { + EVAL->nvar++; + sym->local = EVAL->nvar; + + *((int *)ARRAY_add(&EVAL->var)) = index; + } + + return (-sym->local); +} + diff --git a/main/lib/eval/eval.h b/main/lib/eval/eval.h new file mode 100644 index 00000000..890a6496 --- /dev/null +++ b/main/lib/eval/eval.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + eval.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_H +#define __EVAL_H + +#include "gb_table.h" +#include "gb_reserved.h" +#include "gb_error.h" +#include "eval_read.h" +/*#include "CCollection.h"*/ +#include "main.h" + +#include "../../gbx/gbx_expression.h" +#include "gb.eval.h" + +#ifndef __EVAL_C +EXTERN EXPRESSION *EVAL; +EXTERN EXPRESSION EVAL_read_expr; +#endif + + +void EVAL_init(void); +void EVAL_exit(void); + +void EVAL_new(EXPRESSION **expr, char *src, int len); +void EVAL_free(EXPRESSION **expr); +bool EVAL_compile(EXPRESSION *expr, bool assign); + +GB_VALUE *EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION get_value); +void EVAL_clear(EXPRESSION *expr, bool keep_error); + +int EVAL_add_constant(CLASS_CONST *cst); +int EVAL_add_class(char *name); +int EVAL_add_unknown(char *name); +int EVAL_add_variable(int index); + +void EVAL_start(EXPRESSION *expr); + +bool EVAL_get_assignment_symbol(EXPRESSION *expr, const char **name, int *len); + +#endif diff --git a/main/lib/eval/eval_analyze.c b/main/lib/eval/eval_analyze.c new file mode 100644 index 00000000..983814e3 --- /dev/null +++ b/main/lib/eval/eval_analyze.c @@ -0,0 +1,766 @@ +/*************************************************************************** + + eval_analyze.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_ANALYZE_C + +#include "gambas.h" +#include "gb_common.h" +#include "gb_array.h" +#include "eval_analyze.h" + +#include "c_system.h" +/*#define DEBUG*/ + +static const uchar _utf8_char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +static char _analyze_buffer[256]; +static int _analyze_buffer_pos; + +#define COLOR_BUFFER_SIZE 256 +static EVAL_COLOR _colors[COLOR_BUFFER_SIZE]; +static int _colors_len = 0; +static EVAL_COLOR *_color_buffer = NULL; + +#define NEXT_UTF8_CHAR(_p) (_p += _utf8_char_length[(uchar)*(_p)]) + +static PATTERN get_last_pattern(PATTERN *pattern) +{ + for(;;) + { + pattern--; + if (PATTERN_type(*pattern) != RT_SPACE) + return *pattern; + } +} + +static PATTERN get_next_pattern(PATTERN *pattern) +{ + for(;;) + { + pattern++; + if (PATTERN_type(*pattern) != RT_SPACE) + return *pattern; + } +} + +static int get_type(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + + if (type == RT_RESERVED) + { + if (index >= RS_COLON) + { + if (!((index == RS_AND || index == RS_OR) && PATTERN_is(get_next_pattern(pattern), RS_IF))) + type = RT_OPERATOR; + } + else if (RES_is_type(index)) + type = RT_DATATYPE; + else if (index == RS_WITH && pattern > EVAL->pattern) + { + index = PATTERN_index(get_last_pattern(pattern)); + if (index == RS_BEGINS || index == RS_ENDS) + type = RT_OPERATOR; + } + } + + return type; +} + +static bool is_me_last_error_kind(PATTERN pattern) +{ + return PATTERN_is(pattern, RS_ME) + || PATTERN_is(pattern, RS_SUPER) + || PATTERN_is(pattern, RS_LAST) + || PATTERN_is(pattern, RS_TRUE) + || PATTERN_is(pattern, RS_FALSE) + || PATTERN_is(pattern, RS_PINF) + || PATTERN_is(pattern, RS_MINF) + || PATTERN_is(pattern, RS_ERROR) + || PATTERN_is(pattern, RS_NULL); +} + +static bool is_optional_kind(PATTERN pattern) +{ + return PATTERN_is(pattern, RS_OPTIONAL) + || PATTERN_is(pattern, RS_BYREF); +} + +static void get_symbol(PATTERN pattern, const char **symbol, int *len) +{ + static char keyword[32]; + int i; + SYMBOL *sym; + int type = PATTERN_type(pattern); + int index = PATTERN_index(pattern); + + switch(type) + { + case RT_RESERVED: + *symbol = COMP_res_info[index].name; + *len = strlen(*symbol); + if (!EVAL->rewrite) + { + memcpy(keyword, *symbol, *len); + for (i = 0; i < *len; i++) + keyword[i] = toupper(keyword[i]); + *symbol = keyword; + } + return; + + case RT_INTEGER: + *len = sprintf(keyword, "%d", PATTERN_signed_index(pattern)); + *symbol = keyword; + return; + + case RT_NUMBER: + case RT_IDENTIFIER: + sym = TABLE_get_symbol(EVAL->table, index); + break; + + case RT_CLASS: + sym = TABLE_get_symbol(EVAL->table, index); + break; + + case RT_STRING: + case RT_TSTRING: + case RT_COMMENT: + case RT_ERROR: + sym = TABLE_get_symbol(EVAL->string, index); + break; + + case RT_SUBR: + *symbol = COMP_subr_info[index].name; + *len = strlen(*symbol); + return; + + default: + *symbol = NULL; + *len = 0; + return; + } + + *symbol = sym->name; + *len = sym->len; + /*if (*len > EVAL_COLOR_MAX_LEN) + *len = EVAL_COLOR_MAX_LEN;*/ +} + + +static void add_data(int state, int len) +{ + EVAL_COLOR *color; + + while (len > EVAL_COLOR_MAX_LEN) + { + add_data(state, EVAL_COLOR_MAX_LEN); + len -= EVAL_COLOR_MAX_LEN; + } + + if (len == 0) + return; + + if (_colors_len >= COLOR_BUFFER_SIZE) + { + if (!_color_buffer) + ARRAY_create_inc(&_color_buffer, COLOR_BUFFER_SIZE); + + color = ARRAY_add_many(&_color_buffer, COLOR_BUFFER_SIZE); + memcpy(color, _colors, sizeof(EVAL_COLOR) * COLOR_BUFFER_SIZE); + _colors_len = 0; + } + + color = &_colors[_colors_len]; + color->state = state; + color->len = len; + color->alternate = FALSE; + _colors_len++; +} + +static void add_data_merge(int state, int len) +{ + if (_colors_len > 0 && _colors[_colors_len - 1].state == state && (_colors[_colors_len - 1].len + len) <= EVAL_COLOR_MAX_LEN) + _colors[_colors_len - 1].len += len; + else + add_data(state, len); +} + +static void flush_colors(EVAL_ANALYZE *result) +{ + EVAL_COLOR *color; + + if (_color_buffer) + { + if (_colors_len) + { + color = ARRAY_add_many(&_color_buffer, _colors_len); + memcpy(color, _colors, sizeof(EVAL_COLOR) * _colors_len); + } + + result->color = _color_buffer; + result->len = ARRAY_count(_color_buffer); + } + else + { + result->color = _colors; + result->len = _colors_len; + } +} + +static int is_proc(void) +{ + PATTERN pattern; + int i; + + if (!EVAL->pattern) + return FALSE; + + for (i = 0;; i++) + { + pattern = EVAL->pattern[i]; + if (PATTERN_is_end(pattern) || PATTERN_is_newline(pattern)) + return FALSE; + + if (PATTERN_is(pattern, RS_PRIVATE) || PATTERN_is(pattern, RS_PUBLIC) || PATTERN_is(pattern, RS_STATIC) || PATTERN_is(pattern, RS_FAST) || PATTERN_is_space(pattern)) + continue; + + return (PATTERN_is(pattern, RS_SUB) || PATTERN_is(pattern, RS_PROCEDURE) || PATTERN_is(pattern, RS_FUNCTION)); + } +} + +static int get_symbol_indent(const char *symbol, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + if (!(symbol[i] > 0 && symbol[i] < 33)) + return i; + } + + return len; +} + +static bool symbol_starts_with(const char *symbol, int len, int from, const char *comp) +{ + int l = strlen(comp); + return (from < (len - l) && strncmp(&symbol[from], comp, l) == 0); +} + + +static int get_utf8_length(const char *s, int l) +{ + int len; + int i; + + for (i = 0, len = 0; i < l; i++) + { + if ((s[i] & 0xC0) != 0x80) + len++; + } + + return len; +} + +static void init_result() +{ + _analyze_buffer_pos = 0; +} + +static void flush_result(EVAL_ANALYZE *result) +{ + if (_analyze_buffer_pos > 0) + { + result->str = GB.AddString(result->str, _analyze_buffer, _analyze_buffer_pos); + _analyze_buffer_pos = 0; + } +} + +static void add_result(EVAL_ANALYZE *result, const char *str, int len) +{ + if ((_analyze_buffer_pos + len) > sizeof(_analyze_buffer)) + { + flush_result(result); + if (len >= sizeof(_analyze_buffer)) + { + result->str = GB.AddString(result->str, str, len); + return; + } + } + + memcpy(&_analyze_buffer[_analyze_buffer_pos], str, len); + _analyze_buffer_pos += len; +} + +static void add_result_char(EVAL_ANALYZE *result, char c) +{ + if ((_analyze_buffer_pos + 1) > sizeof(_analyze_buffer)) + flush_result(result); + + _analyze_buffer[_analyze_buffer_pos++] = c; +} + +static void add_result_spaces(EVAL_ANALYZE *result, int nspace) +{ + while (nspace > 0) + { + add_result_char(result, ' '); + nspace--; + } +} + +static void analyze(EVAL_ANALYZE *result) +{ + PATTERN *pattern; + int type, old_type, next_type; + const char *symbol; + const char *p; + bool space_before, space_after; + int len, i, l; + bool preprocessor; + + _colors_len = 0; + EVAL_analyze_exit(); + + pattern = EVAL->pattern; + preprocessor = FALSE; + + if (EVAL->len <= 0) + return; + + if (!pattern) + return; + + init_result(); + + type = EVAL->comment ? RT_COMMENT : RT_END; + next_type = RT_END; + old_type = RT_END; + space_after = FALSE; + + for(;;) + { + type = get_type(pattern); + + if (type == RT_END) + break; + + if (type == RT_SPACE) + { + if (!EVAL->rewrite || _colors_len == 0 || PATTERN_is_end(pattern[1]) || PATTERN_is_comment(pattern[1])) + { + len = PATTERN_index(*pattern); + add_data(RT_SPACE, len); + add_result_spaces(result, len); + } + goto __NEXT_PATTERN; + } + + old_type = next_type; + next_type = type; + + get_symbol(*pattern, &symbol, &len); + + space_before = space_after; + space_after = FALSE; + + //if (in_quote && (type == RT_RESERVED || type == RT_DATATYPE || type == RT_SUBR)) + // type = RT_IDENTIFIER; + + switch(type) + { + case RT_RESERVED: + + if (is_me_last_error_kind(*pattern)) + { + if (old_type != RT_OPERATOR) + space_before = TRUE; + next_type = RT_IDENTIFIER; + if (PATTERN_is(*pattern, RS_ERROR) && old_type == RT_END) + space_after = TRUE; + } + else if (is_optional_kind(*pattern)) + { + if (old_type != RT_OPERATOR) + space_before = TRUE; + } + else if (PATTERN_is_preprocessor(*pattern)) + { + preprocessor = TRUE; + space_before = FALSE; + } + else + space_before = TRUE; + + break; + + case RT_DATATYPE: + //state = Datatype; + if (PATTERN_is(get_last_pattern(pattern), RS_OPEN)) + type = RT_RESERVED; + + if (old_type != RT_OPERATOR) + space_before = TRUE; + + break; + + case RT_IDENTIFIER: + case RT_CLASS: + //state = Symbol; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_INTEGER: + case RT_NUMBER: + //state = Number; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_STRING: + //state = String; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_SUBR: + //state = Subr; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_COMMENT: + //state = Commentary; + space_before = FALSE; //*symbol != ' '; + i = get_symbol_indent(symbol, len); + if ((len >= 2) && (i <= (len - 2)) && (symbol[i + 1] == '\'')) + type = RT_HELP; + else + { + while (i < len && (uchar)symbol[i] == '\'') + i++; + + while (i < len && (uchar)symbol[i] <= ' ') + i++; + + if (i < len) + { + if (symbol_starts_with(symbol, len, i, "NOTE:") + || symbol_starts_with(symbol, len, i, "TODO:") + || symbol_starts_with(symbol, len, i, "FIXME:")) + type = RT_HELP; + } + } + break; + + case RT_OPERATOR: + + if (index("([)]@", *symbol)) + { + space_after = FALSE; + } + else if (index(":;,", *symbol)) + { + space_before = FALSE; + space_after = TRUE; + } + else if (index("#{", *symbol)) + { + if (old_type != RT_OPERATOR) + space_before = TRUE; + space_after = FALSE; + //in_quote = *symbol == '{'; + + /*if (!preprocessor && *symbol == '#' && old_type == RT_END) + preprocessor = TRUE;*/ + } + else if (index("}", *symbol)) + { + space_before = FALSE; + space_after = FALSE; + //in_quote = FALSE; + } + else if (index(".!", *symbol)) //symbol[0] == '.' && symbol[1] == 0) + { + //space_before = FALSE; + space_after = FALSE; + } + else if (*symbol == '-' && len == 1) + { + if (old_type == RT_OPERATOR && (PATTERN_is(get_last_pattern(pattern), RS_LBRA) || PATTERN_is(get_last_pattern(pattern),RS_LSQR))) + space_before = FALSE; + else + space_before = TRUE; + + if (old_type == RT_RESERVED || old_type == RT_DATATYPE) + space_after = FALSE; + else if (old_type == RT_OPERATOR) + { + get_symbol(get_last_pattern(pattern), &symbol, &len); + if (index(")]}", *symbol)) + space_after = TRUE; + else + space_after = FALSE; + get_symbol(*pattern, &symbol, &len); + } + else + space_after = TRUE; + } + else if (PATTERN_is(*pattern, RS_NOT)) + { + if (old_type == RT_OPERATOR && (PATTERN_is(get_last_pattern(pattern), RS_LBRA) || PATTERN_is(get_last_pattern(pattern),RS_LSQR))) + space_before = FALSE; + else + space_before = TRUE; + + space_after = TRUE; + } + else + { + space_before = TRUE; + space_after = TRUE; + } + + if (old_type == RT_RESERVED) + space_before = TRUE; + + break; + + case RT_ERROR: + space_before = TRUE; + break; + } + + if (EVAL->rewrite && space_before && old_type != RT_END) + { + add_result_char(result, ' '); + add_data(preprocessor ? RT_PREPROCESSOR : RT_SPACE, 1); + } + + if (type == RT_STRING) + add_result_char(result, '"'); + + if (len) + { + if (EVAL->rewrite && type == RT_CLASS) + { + add_result_char(result, toupper(symbol[0])); + if (len > 1) add_result(result, &symbol[1], len - 1); + } + else + add_result(result, symbol, len); + //printf("add: %.*s\n", len, symbol); + len = get_utf8_length(symbol, len); + } + + if (type == RT_STRING) + { + add_result_char(result, '"'); + len += 2; + } + + if (EVAL->rewrite) + { + if (type == RT_STRING) + { + add_data(RT_STRING, 1); + len -= 2; + for (i = 0, p = symbol; i < len; i++) + { + if (*p == '\\') + { + i++; + NEXT_UTF8_CHAR(p); + + add_data_merge(RT_ESCAPE, 1); + if (i < len) + { + if (*p == 'x' && i < (len - 2) && isxdigit(p[1]) && isxdigit(p[2])) + { + l = 3; + i += 2; + } + else + l = 1; + add_data_merge(RT_ESCAPE, l); + + while (l--) + NEXT_UTF8_CHAR(p); + } + } + else + { + NEXT_UTF8_CHAR(p); + add_data_merge(RT_STRING, 1); + } + } + add_data_merge(RT_STRING, 1); + goto __NEXT_PATTERN; + } + else if (type == RT_IDENTIFIER) + { + if (PATTERN_is(get_next_pattern(pattern), RS_COLON)) + { + add_result_char(result, ':'); + add_data(RT_LABEL, len + 1); + space_after = TRUE; + pattern ++; + goto __NEXT_PATTERN; + } + else if (old_type == RT_RESERVED && (PATTERN_is(get_last_pattern(pattern), RS_GOTO) || PATTERN_is(get_last_pattern(pattern), RS_GOSUB))) + { + type = RT_LABEL; + } + } + else if (type == RT_RESERVED) + { + if (PATTERN_is(*pattern, RS_NULL)) + { + if (!(old_type == RT_RESERVED && PATTERN_is(get_last_pattern(pattern), RS_OPEN))) + type = RT_CONSTANT; + } + else if (PATTERN_is(*pattern, RS_TRUE) + || PATTERN_is(*pattern, RS_FALSE) + || PATTERN_is(*pattern, RS_PINF) + || PATTERN_is(*pattern, RS_MINF)) + { + type = RT_CONSTANT; + } + } + } + + if (preprocessor && type != RT_COMMENT && type != RT_HELP) + add_data(RT_PREPROCESSOR, len); + else + add_data(type, len); + //printf("add_data: %.d (%d)\n", type, len); + + __NEXT_PATTERN: + pattern++; + } + + flush_result(result); + flush_colors(result); + + //fprintf(stderr, "analyze: %d %s\n", strlen(result->str), result->str); +} + + +#define add_pattern(_type, _index) EVAL->pattern[EVAL->pattern_count++] = PATTERN_make((_type), (_index)); + +static void add_end_pattern(void) +{ + int index; + int len; + + len = EVAL->len - (READ_source_ptr - EVAL->source); + if (len > 0) + { + index = TABLE_add_symbol(EVAL->string, READ_source_ptr, len); + add_pattern(RT_ERROR, index); + } + + add_pattern(RT_END, 0); + //get_symbol(PATTERN_make(RT_ERROR, index), &sym, &len); +} + + +PUBLIC void EVAL_analyze(const char *src, int len, int state, EVAL_ANALYZE *result, bool rewrite) +{ + //int nspace = 0; + + #ifdef DEBUG + printf("EVAL: %*.s\n", expr->len, expr->source); + #endif + + CLEAR(result); + + /*while (len > 0 && src[len - 1] == ' ') + { + len--; + nspace++; + }*/ + + result->len = 0; + result->str = NULL; + + if (len > 0) + { + EVAL = &EVAL_read_expr; + + EVAL_clear(EVAL, FALSE); + + EVAL->source = GB.NewString(src, len); + EVAL->source = GB.AddString(EVAL->source, "\0\0", 2); + EVAL->len = len; + + EVAL->analyze = TRUE; + EVAL->rewrite = rewrite; + EVAL->comment = state == RT_COMMENT; + + //fprintf(stderr, "EVAL_analyze: [%d] %.*s\n", EVAL->comment, len, src); + + EVAL_start(EVAL); + + TRY + { + EVAL_read(); + } + CATCH + { + add_end_pattern(); + } + END_TRY + + analyze(result); + result->proc = is_proc(); + result->state = EVAL->comment ? RT_COMMENT : RT_END; + + //fprintf(stderr, "--> [%d]\n", EVAL->comment); + + GB.FreeString(&EVAL->source); + } + else + { + result->proc = FALSE; + } +} + + +void EVAL_analyze_exit(void) +{ + if (_color_buffer) + ARRAY_delete(&_color_buffer); +} diff --git a/main/lib/eval/eval_analyze.h b/main/lib/eval/eval_analyze.h new file mode 100644 index 00000000..3ca9c01a --- /dev/null +++ b/main/lib/eval/eval_analyze.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + eval_analyze.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_ANALYZE_H +#define __EVAL_ANALYZE_H + +#include "eval.h" + +void EVAL_analyze(const char *src, int len, int state, EVAL_ANALYZE *result, bool rewrite); +void EVAL_analyze_exit(void); + +#endif diff --git a/main/lib/eval/eval_code.c b/main/lib/eval/eval_code.c new file mode 100644 index 00000000..0714b07d --- /dev/null +++ b/main/lib/eval/eval_code.c @@ -0,0 +1,40 @@ +/*************************************************************************** + + eval_code.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_CODE_C +#define __GB_CODE_C + +#define PROJECT_EXEC + +#include "gb_alloc_override.h" + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_reserved.h" +#include "gb_code.h" +#include "gb_limit.h" +#include "eval.h" + +#define COMP_version 0x03180000 + +#include "gb_code_temp.h" diff --git a/main/lib/eval/eval_code.h b/main/lib/eval/eval_code.h new file mode 100644 index 00000000..241b39a3 --- /dev/null +++ b/main/lib/eval/eval_code.h @@ -0,0 +1,109 @@ +/*************************************************************************** + + eval_code.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CODE_H +#define __CODE_H + +#include "gb_pcode.h" + +#ifndef __CODE_C +EXTERN short CODE_stack_usage; +#endif + +PUBLIC void CODE_begin_function(FUNCTION *func); +PUBLIC void CODE_end_function(FUNCTION *func); + +PUBLIC boolean CODE_popify_last(void); +PUBLIC boolean CODE_check_statement_last(void); +PUBLIC boolean CODE_check_pop_local_last(short *local); + +PUBLIC int CODE_get_current_pos(); +PUBLIC void CODE_dump(PCODE *code); + +PUBLIC void CODE_push_number(int value); +PUBLIC void CODE_push_const(short value); + +PUBLIC void CODE_push_local(short num); +PUBLIC void CODE_pop_local(short num); + +PUBLIC void CODE_push_array(short nparam); + +PUBLIC void CODE_push_global(short global, boolean is_static, boolean is_function); +PUBLIC void CODE_pop_global(short global, boolean is_static); + +PUBLIC void CODE_push_symbol(short symbol); +PUBLIC void CODE_pop_symbol(short symbol); + +PUBLIC void CODE_push_unknown(short symbol); +PUBLIC void CODE_pop_unknown(short symbol); + +PUBLIC void CODE_push_class(short class); +PUBLIC void CODE_push_special(short spec); +PUBLIC void CODE_push_event(short event); + +PUBLIC void CODE_pop_optional(short num); + +PUBLIC void CODE_jump(void); +PUBLIC void CODE_jump_first(short local); +PUBLIC void CODE_jump_next(void); +PUBLIC void CODE_jump_if_true(void); +PUBLIC void CODE_jump_if_false(void); +PUBLIC void CODE_jump_length(short src, short dst); +PUBLIC void CODE_first(short local); +PUBLIC void CODE_next(void); + +PUBLIC void CODE_op(short op, short nparam, boolean fixed); + +PUBLIC void CODE_new(ushort nparam, boolean array, boolean event); + +PUBLIC void CODE_push_me(void); +PUBLIC void CODE_push_last(void); +PUBLIC void CODE_push_null(void); +PUBLIC void CODE_push_boolean(boolean value); + +PUBLIC void CODE_dup(void); + +PUBLIC void CODE_return(boolean return_value); + +PUBLIC void CODE_quit(void); +PUBLIC void CODE_stop(void); + +PUBLIC void CODE_push_char(char car); +PUBLIC void CODE_push_void(void); + +PUBLIC void CODE_event(boolean on); + +PUBLIC void CODE_subr(short subr, short nparam, short optype, boolean output, boolean fixed); +PUBLIC void CODE_call(short nparam, boolean output); +PUBLIC void CODE_drop(void); +PUBLIC void CODE_push_return(void); + +PUBLIC void CODE_try(void); +PUBLIC void CODE_end_try(void); +PUBLIC void CODE_catch(void); + +PUBLIC void CODE_pop_ctrl(short num); + +PUBLIC void CODE_break(void); + +#endif diff --git a/main/lib/eval/eval_read.c b/main/lib/eval/eval_read.c new file mode 100644 index 00000000..38179753 --- /dev/null +++ b/main/lib/eval/eval_read.c @@ -0,0 +1,759 @@ +/*************************************************************************** + + eval_read.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_READ_C + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_table.h" + +#include "eval.h" +#include "eval_read.h" + +//#define DEBUG 1 + +PUBLIC const char *READ_source_ptr; +#define source_ptr READ_source_ptr + +static bool is_init = FALSE; +static bool _begin_line = FALSE; + +static char ident_car[256]; +static char first_car[256]; +static char noop_car[256]; +static char canres_car[256]; + +#undef isspace +#define isspace(_c) (((uchar)_c) <= ' ') +#undef isdigit +#define isdigit(_c) ((_c) >= '0' && (_c) <= '9') + +enum +{ + GOTO_BREAK, + GOTO_SPACE, + GOTO_NEWLINE, + GOTO_COMMENT, + GOTO_STRING, + GOTO_IDENT, + GOTO_QUOTED_IDENT, + GOTO_ERROR, + GOTO_SHARP, + GOTO_NUMBER, + GOTO_NUMBER_OR_OPERATOR, + GOTO_OPERATOR +}; + + +static void READ_init(void) +{ + unsigned char i; + + if (!is_init) + { + for (i = 0; i < 255; i++) + { + ident_car[i] = (i != 0) && ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9') || strchr("$_?@", i)); + noop_car[i] = ident_car[i] || (i >= '0' && i <= '9') || i <= ' '; + canres_car[i] = (i != ':') && (i != '.') && (i != '!') && (i != '('); + + if (i == 0) + first_car[i] = GOTO_BREAK; + else if (i == '\n') + first_car[i] = GOTO_NEWLINE; + else if (i <= ' ') + first_car[i] = GOTO_SPACE; + else if (i == '\'') + first_car[i] = GOTO_COMMENT; + else if (i == '"') + first_car[i] = GOTO_STRING; + else if (i == '#') + first_car[i] = GOTO_SHARP; + else if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == '$' || i == '_') + first_car[i] = GOTO_IDENT; + else if (i == '{') + first_car[i] = GOTO_QUOTED_IDENT; + else if (i >= '0' && i <= '9') + first_car[i] = GOTO_NUMBER; + else if (i >= 127) + first_car[i] = GOTO_ERROR; + else if (i == '+' || i == '-' || i == '&') + first_car[i] = GOTO_NUMBER_OR_OPERATOR; + else + first_car[i] = GOTO_OPERATOR; + } + + is_init = TRUE; + } +} + +static bool _no_quote = FALSE; +#define BUF_MAX 255 +static char _buffer[BUF_MAX + 1]; + +PUBLIC char *READ_get_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + const char *str; + const char *before = _no_quote ? "" : "'"; + const char *after = _no_quote ? "" : "'"; + + switch(type) + { + case RT_RESERVED: + //snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", before, TABLE_get_symbol_name(COMP_res_table, index), after); + str = COMP_res_info[index].name; + if (ispunct((unsigned char)*str)) + snprintf(_buffer, BUF_MAX, "%s%s%s", before, str, after); + else + strcpy(_buffer, str); + break; + + case RT_INTEGER: + snprintf(_buffer, BUF_MAX, "%s%d%s", before, PATTERN_signed_index(*pattern), after); + break; + + case RT_NUMBER: + case RT_IDENTIFIER: + case RT_CLASS: + snprintf(_buffer, BUF_MAX, "%s%s%s", before, TABLE_get_symbol_name(EVAL->table, index), after); + break; + + case RT_STRING: + case RT_TSTRING: + if (_no_quote) + snprintf(_buffer, BUF_MAX, "\"%s\"", TABLE_get_symbol_name(EVAL->string, index)); + else + strcpy(_buffer, "string"); + break; + + case RT_NEWLINE: + case RT_END: + strcpy(_buffer, "end of expression"); + break; + + case RT_SUBR: + //snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", bafore, COMP_subr_info[index].name, after); + strcpy(_buffer, COMP_subr_info[index].name); + break; + + case RT_COMMENT: + strncpy(_buffer, TABLE_get_symbol_name(EVAL->string, index), BUF_MAX); + _buffer[BUF_MAX] = 0; + break; + + case RT_SPACE: + snprintf(_buffer, BUF_MAX, "[%d]", index); + break; + + default: + sprintf(_buffer, "%s?%08X?%s", before, *pattern, after); + } + + return _buffer; +} + +#if DEBUG +PUBLIC void READ_dump_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + int pos; + + pos = (int)(pattern - EVAL->pattern); + if (pos < 0 || pos >= EVAL->pattern_count) + return; + + printf("%d ", pos); + + if (PATTERN_flag(*pattern) & RT_FIRST) + printf("!"); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_POINT) + printf("."); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_CLASS) + printf("C"); + else + printf(" "); + + printf(" "); + + _no_quote = TRUE; + + if (type == RT_RESERVED) + printf("RESERVED %s\n", READ_get_pattern(pattern)); + else if (type == RT_INTEGER) + printf("INTEGER %s\n", READ_get_pattern(pattern)); + else if (type == RT_NUMBER) + printf("NUMBER %s\n", READ_get_pattern(pattern)); + else if (type == RT_IDENTIFIER) + printf("IDENTIFIER %s\n", READ_get_pattern(pattern)); + else if (type == RT_CLASS) + printf("CLASS %s\n", READ_get_pattern(pattern)); + else if (type == RT_STRING) + printf("STRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_TSTRING) + printf("TSTRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_NEWLINE) + printf("NEWLINE (%d)\n", index); + else if (type == RT_END) + printf("END\n"); + else if (type == RT_PARAM) + printf("PARAM %d\n", index); + else if (type == RT_SUBR) + printf("SUBR %s\n", READ_get_pattern(pattern)); + else if (type == RT_COMMENT) + printf("COMMENT %s\n", READ_get_pattern(pattern)); + else if (type == RT_SPACE) + printf("SPACE %s\n", READ_get_pattern(pattern)); + else + printf("? %d\n", index); + + _no_quote = FALSE; +} +#endif + +#define get_char_offset(_offset) ((unsigned char)source_ptr[(_offset)]) +#define get_char() ((unsigned char)(*source_ptr)) + +static unsigned char next_char(void) +{ + source_ptr++; + return get_char(); +} + +#ifdef DEBUG + +static void add_pattern(int type, int index) +{ + EVAL->pattern[EVAL->pattern_count++] = PATTERN_make(type, index); + READ_dump_pattern(&EVAL->pattern[EVAL->pattern_count - 1]); +} + +#else + +#define add_pattern(_type, _index) EVAL->pattern[EVAL->pattern_count++] = PATTERN_make((_type), (_index)); + +#endif + +static PATTERN get_previous_pattern(int n) +{ + int i; + PATTERN pattern; + + for (i = EVAL->pattern_count - 1; i >= 0; i--) + { + pattern = EVAL->pattern[i]; + if (!PATTERN_is_space(pattern)) + { + n--; + if (n == 0) + return pattern; + } + } + + return NULL_PATTERN; +} + +#define get_last_pattern() get_previous_pattern(1) +#define get_last_last_pattern() get_previous_pattern(2) + +static void add_newline() +{ + add_pattern(RT_NEWLINE, 0); +} + + +static void add_end() +{ + add_pattern(RT_END, 0); +} + + +#include "gbc_read_temp.h" + + +static void add_quoted_identifier(void) +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + PATTERN last_pattern; + + last_pattern = get_last_pattern(); + + type = RT_IDENTIFIER; + + start = source_ptr; + len = 1; + + for(;;) + { + source_ptr++; + car = get_char(); + len++; + if (!ident_car[car]) + break; + } + + source_ptr++; + + if (!EVAL->analyze) + { + if (car != '}') + THROW("Missing '}'"); + + if (len == 2) + THROW("Void identifier"); + } + else + { + if (!car) + len--; + + if (car != '}' || len <= 2) + type = RT_ERROR; + } + + if (!EVAL->analyze && PATTERN_is(last_pattern, RS_EXCL)) + { + index = TABLE_add_symbol(EVAL->string, start + 1, len - 2); + type = RT_STRING; + } + else + { + if (!EVAL->rewrite && type != RT_ERROR) + { + start++; + len -= 2; + } + index = TABLE_add_symbol(type == RT_ERROR ? EVAL->string : EVAL->table, start, len); + } + + add_pattern(type, index); +} + + +static void add_operator() +{ + unsigned char car; + const char *start; + const char *end; + int len; + int op = NO_SYMBOL; + int index; + + start = source_ptr; + end = start; + len = 1; + + for(;;) + { + source_ptr++; + + index = RESERVED_find_word(start, len); + if (index >= 0) + { + op = index; + end = source_ptr; + } + + car = get_char(); + //if (!isascii(car) || !ispunct(car)) + if (noop_car[car]) + break; + len++; + } + + source_ptr = end; + + if (EVAL->analyze && op == RS_QUES) + op = RS_PRINT; + + if (op < 0) + THROW("Unknown operator"); + + add_pattern(RT_RESERVED, op); +} + + +static int xdigit_val(unsigned char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + else + return (-1); +} + + +static void add_string() +{ + unsigned char car; + const char *start; + int len; + int index; + ushort newline; + bool jump; + char *p; + int i; + + start = source_ptr; + len = 0; + newline = 0; + jump = FALSE; + p = (char *)source_ptr; + + //fprintf(stderr, "EVAL_read: add_string: [%d] %s\n", source_ptr - EVAL->source, source_ptr); + + for(;;) + { + source_ptr++; + car = get_char(); + + //fprintf(stderr, "EVAL_read: car = %d jump = %d\n", car, jump); + + if (jump) + { + if (car == '\n') + newline++; + else if (car == '"') + jump = FALSE; + else if (!car || !isspace(car)) + break; + } + else + { + p++; + len++; + + if (!car || car == '\n') + THROW("Non terminated string"); + + if (car == '\\') + { + source_ptr++; + car = get_char(); + + if (car == 'n') + *p = '\n'; + else if (car == 't') + *p = '\t'; + else if (car == 'r') + *p = '\r'; + else if (car == 'b') + *p = '\b'; + else if (car == 'v') + *p = '\v'; + else if (car == 'f') + *p = '\f'; + else if (car == 'e') + *p = '\x1B'; + else if (car == '0') + *p = 0; + else if (car == '\"' || car == '\'' || car == '\\') + *p = car; + else + { + if (car == 'x') + { + i = xdigit_val(get_char_offset(1)); + if (i >= 0) + { + car = i; + i = xdigit_val(get_char_offset(2)); + if (i >= 0) + { + car = (car << 4) | (uchar)i; + *p = car; + source_ptr += 2; + continue; + } + } + } + + THROW("Bad character constant in string"); + } + } + else if (car == '"') + { + p--; + len--; + jump = TRUE; + } + else + *p = car; + } + } + + p[1] = 0; + + //fprintf(stderr, "EVAL_read: add_string (end): [%d] %s\n", source_ptr - EVAL->source, source_ptr); + if (len > 0) + { + index = TABLE_add_symbol(EVAL->string, start + 1, len); + add_pattern(RT_STRING, index); + } + else + add_pattern(RT_STRING, VOID_STRING_INDEX); + + for (i = 0; i < newline; i++) + add_newline(); + + source_ptr -= newline; +} + + +static void add_comment() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + + start = source_ptr; + len = 1; + + for(;;) + { + source_ptr++; + car = get_char(); + if (car == 0 || car == '\n') + break; + len++; + } + + index = TABLE_add_symbol(EVAL->string, start, len); + type = RT_COMMENT; + + add_pattern(type, index); +} + + +static void add_string_for_analyze() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + + start = source_ptr; + len = 0; + + for(;;) + { + source_ptr++; + car = get_char(); + if (car == '\\') + { + source_ptr++; + car = get_char(); + len++; + if (car == 0) + break; + } + else if (car == 0 || car == '\n' || car == '"') + break; + len++; + } + + if (car == '"') + source_ptr++; + + index = TABLE_add_symbol(EVAL->string, start + 1, len); + type = RT_STRING; + + add_pattern(type, index); + //fprintf(stderr, "add_string_for_analyze: %s\n", TABLE_get_symbol_name(EVAL->string, index)); +} + + +static void add_spaces() +{ + unsigned char car; + int len; + + len = 1; + + for(;;) + { + source_ptr++; + car = get_char(); + if (car > ' ' || car == '\n' || car == 0) + break; + len++; + } + + add_pattern(RT_SPACE, len); +} + + +PUBLIC void EVAL_read(void) +{ + static const void *jump_char[12] = + { + &&__BREAK, + &&__SPACE, + &&__NEWLINE, + &&__COMMENT, + &&__STRING, + &&__IDENT, + &&__QUOTED_IDENT, + &&__ERROR, + &&__SHARP, + &&__NUMBER, + &&__NUMBER_OR_OPERATOR, + &&__OPERATOR + }; + + unsigned char car; + + READ_init(); + + source_ptr = EVAL->source; + _begin_line = TRUE; + + for(;;) + { + car = get_char(); + goto *jump_char[(int)first_car[car]]; + + __ERROR: + + THROW(E_SYNTAX); + + __SPACE: + + if (EVAL->analyze) + add_spaces(); + else + source_ptr++; + + continue; + + __NEWLINE: + + source_ptr++; + add_newline(); + _begin_line = TRUE; + continue; + + __COMMENT: + + if (EVAL->analyze) + { + add_comment(); + } + else + { + do + { + source_ptr++; + car = get_char(); + } + while (car != '\n' && car != 0); + } + + _begin_line = FALSE; + continue; + + __STRING: + + if (EVAL->analyze) + add_string_for_analyze(); + else + add_string(); + _begin_line = FALSE; + continue; + + __IDENT: + + add_identifier(); + _begin_line = FALSE; + continue; + + __QUOTED_IDENT: + + add_quoted_identifier(); + _begin_line = FALSE; + continue; + + __SHARP: + + if (_begin_line) + { + _begin_line = FALSE; + if (get_char_offset(1) == '!' && EVAL->analyze) + add_comment(); + else + add_identifier(); + continue; + } + + __NUMBER_OR_OPERATOR: + + if (add_number()) + goto __OPERATOR; + + _begin_line = FALSE; + continue; + + __NUMBER: + + add_number(); + _begin_line = FALSE; + continue; + + __OPERATOR: + + add_operator(); + _begin_line = FALSE; + continue; + } + +__BREAK: + + // We add end markers to simplify the compiler job, when it needs to look + // at many patterns in one shot. + + add_end(); + add_end(); + add_end(); + add_end(); +} + diff --git a/main/lib/eval/eval_read.h b/main/lib/eval/eval_read.h new file mode 100644 index 00000000..f4dec483 --- /dev/null +++ b/main/lib/eval/eval_read.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + eval_read.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_READ_H +#define __EVAL_READ_H + +#include "gbc_read_common.h" + +#ifndef __EVAL_READ_C +EXTERN const char *READ_source_ptr; +#endif + +PUBLIC void EVAL_read(void); +PUBLIC char *READ_get_pattern(PATTERN *pattern); + +#endif diff --git a/main/lib/eval/eval_reserved.c b/main/lib/eval/eval_reserved.c new file mode 100644 index 00000000..3acda182 --- /dev/null +++ b/main/lib/eval/eval_reserved.c @@ -0,0 +1,26 @@ +/*************************************************************************** + + eval_reserved.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +//#define __EVAL_RESERVED_C + +#include "gb_reserved_temp.h" diff --git a/main/lib/eval/eval_trans.c b/main/lib/eval/eval_trans.c new file mode 100644 index 00000000..dc0ec9b7 --- /dev/null +++ b/main/lib/eval/eval_trans.c @@ -0,0 +1,458 @@ +/*************************************************************************** + + eval_trans.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_C + +#include +#include + +#include "gb_common.h" +#include "gb_error.h" + +#include "gb_reserved.h" +#include "eval_read.h" +#include "eval_trans.h" +#include "eval.h" + +/* +PUBLIC void TRANS_reset(void) +{ + JOB->line = 1; + JOB->current = JOB->pattern; + JOB->end = &(JOB->pattern[ARRAY_count(JOB->pattern)]); +} + + +PUBLIC boolean TRANS_newline(void) +{ + if (PATTERN_IS_NEWLINE(*JOB->current)) + { + JOB->line = PATTERN_INDEX(*JOB->current) + 1; + JOB->current++; + return TRUE; + } + + return FALSE; +} +*/ + +#if 0 +PUBLIC boolean TRANS_get_number(int index, TRANS_NUMBER *result) +{ + char car; + int val; + double dval; + char *end; + int pos; + + int base = 0; + char *number = TABLE_get_symbol_name(EVAL->table, index); + boolean minus = FALSE; + boolean is_unsigned = FALSE; + + car = *number; + + if (car == '+' || car == '-') + { + minus = (car == '-'); + car = *(++number); + } + + if (car == '&') + { + car = *(++number); + car = toupper(car); + + if (car == 'H') + { + base = 16; + car = *(++number); + } + else if (car == 'X') + { + base = 2; + car = *(++number); + } + else + base = 16; + } + else if (car == '%') + { + base = 2; + car = *(++number); + } + + if (!car) + return TRUE; + + if (car == '-' || car == '+') + return TRUE; + + if (car == '0' && toupper(number[1]) == 'X') + return TRUE; + + pos = strlen(number) - 1; + if (number[pos] == '&') + { + number[pos] = 0; + is_unsigned = TRUE; + } + + errno = 0; + + if (base) + { + val = strtol(number, &end, base); + + if (!is_unsigned && val >= 0x8000L && val <= 0xFFFFL) + val |= 0xFFFF0000; + } + else + { + base = 10; + val = strtol(number, &end, base); + if (errno || *end) + { + if (is_unsigned) + return TRUE; + + errno = 0; + base = 0; + dval = strtod(number, &end); + } + } + + if (*end || errno) + return TRUE; + + if (!base) + { + result->type = T_FLOAT; + result->dval = minus ? (-dval) : dval; + } + else + { + result->type = T_INTEGER; + result->ival = minus ? (-val) : val; + } + + return FALSE; +} +#endif + +bool TRANS_get_number(int index, TRANS_NUMBER *result) +{ + GB_VALUE value; + SYMBOL *sym = TABLE_get_symbol(EVAL->table, index); + int len = sym->len; + + if (len > 0 && tolower(sym->name[len - 1]) == 'i') + { + len--; + result->complex = TRUE; + } + else + result->complex = FALSE; + + if (GB.NumberFromString(GB_NB_READ_ALL | GB_NB_READ_HEX_BIN, sym->name, len, &value)) + return TRUE; + + if (value.type == T_INTEGER) + { + result->type = T_INTEGER; + result->ival = ((GB_INTEGER *)(void *)&value)->value; + } + else if (value.type == T_LONG) + { + result->type = T_LONG; + result->lval = ((GB_LONG *)(void *)&value)->value; + } + else + { + result->type = T_FLOAT; + result->dval = ((GB_FLOAT *)(void *)&value)->value; + } + + return FALSE; +} + +#if 0 +static PATTERN *trans_square(PATTERN *look, int mode, TRANS_DECL *result) +{ + TRANS_NUMBER tnum; + int i; + + if (!(mode & TT_CAN_SQUARE)) + { + if (PATTERN_is(*look, RS_LSQR)) + THROW("Arrays are forbidden here"); + return look; + } + + if (!PATTERN_IS(*look, RS_LSQR)) + return look; + + /* + if (result->is_array) + THROW("Syntax error. Duplicated array declaration"); + */ + + look++; + + /* + if (PATTERN_IS(*look, RS_RSQR)) + { + look++; + return look; + } + */ + + if (mode && TT_CAN_ARRAY) + { + for (i = 0;; i++) + { + if (i > MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + if (TRANS_get_number(PATTERN_INDEX(*look), &tnum)) + THROW(E_SYNTAX); + if (tnum.type != T_INTEGER) + THROW(E_SYNTAX); + if (tnum.ival < 1 || tnum.ival > (2 << 22)) /* 4 Mo, ca devrait suffire... ;-) */ + THROW("Bad subscript range"); + + result->array.dim[i] = tnum.ival; + result->array.ndim++; + look++; + + if (PATTERN_is(*look, RS_RSQR)) + break; + + if (!PATTERN_is(*look, RS_COMMA)) + THROW("Missing comma"); + look++; + } + } + + if (!PATTERN_IS(*look, RS_RSQR)) + THROW("Missing ']'"); + + look++; + return look; +} +#endif + +#if 0 +PUBLIC boolean TRANS_type(int mode, TRANS_DECL *result) +{ + PATTERN *look = JOB->current; + short id = 0; + int value = -1L; + int flag = 0; + + /* Ne pas remplir la structure de z�os */ + + /* Attention ! Probl�e du tableau d'objet ! */ + + TYPE_clear(&result->type); + result->is_new = FALSE; + result->array.ndim = 0; + + look = trans_square(look, mode, result); + + if (!PATTERN_IS(*look, RS_AS)) + { + if (mode & TT_DO_NOT_CHECK_AS) + return FALSE; + else + THROW("Missing AS"); + } + + look++; + + if (mode & TT_CAN_NEW) + { + if (PATTERN_IS(*look, RS_NEW)) + { + if (TYPE_get_id(result->type) == T_ARRAY) + THROW("Cannot mix NEW and array declaration"); + + result->is_new = TRUE; + look++; + } + } + + if (PATTERN_IS_TYPE(*look)) + { + id = RES_get_type(PATTERN_index(*look)); + if (id == T_OBJECT) + value = (-1); + look++; + } + else if (PATTERN_IS_IDENTIFIER(*look)) + { + id = T_OBJECT; + value = CLASS_add_class(JOB->class, PATTERN_INDEX(*look)); + look++; + } + else + THROW(E_SYNTAX); + + /*look = trans_square(look, mode, result);*/ + + if (id == T_VOID) + return FALSE; + + /* + if (result->is_array && result->array.ndim == 0) + result->is_array = FALSE; + */ + + if (result->array.ndim > 0) + { + result->array.type = TYPE_make(id, value, flag); + result->type = TYPE_make(T_ARRAY, CLASS_add_array(JOB->class, &result->array), 0); + } + else + result->type = TYPE_make(id, value, flag); + + JOB->current = look; + return TRUE; +} +#endif + +#if 0 +PUBLIC boolean TRANS_check_declaration(void) +{ + PATTERN *look = JOB->current; + + if (!PATTERN_is_identifier(*look)) + return FALSE; + look++; + + if (PATTERN_is(*look, RS_LSQR)) + { + for(;;) + { + look++; + if (PATTERN_is(*look, RS_RSQR)) + break; + if (PATTERN_is_newline(*look)) + return FALSE; + } + look++; + } + + if (!PATTERN_is(*look, RS_AS)) + return FALSE; + + return TRUE; +} +#endif + +#if 0 +PUBLIC void TRANS_get_constant_value(TRANS_DECL *decl, PATTERN value) +{ + int index; + TRANS_NUMBER number; + int type; + + index = PATTERN_index(value); + + /* V�ification de la constante */ + + type = TYPE_get_id(decl->type); + + switch(type) + { + case T_BOOLEAN: + + decl->is_integer = TRUE; + + if (PATTERN_is(value, RS_TRUE)) + decl->value = -1L; + else if (PATTERN_is(value, RS_FALSE)) + decl->value = 0L; + else + THROW("Type mismatch"); + + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: + + decl->is_integer = TRUE; + + if (TRANS_get_number(index, &number)) + THROW("Type mismatch"); + + if (number.type != T_INTEGER) + THROW("Type mismatch"); + + if (((type == T_BYTE) && (number.ival < 0 || number.ival > 255)) + || ((type == T_SHORT) && (number.ival < -32768L || number.ival > 32767L))) + THROW("Out of range"); + + decl->value = number.ival; + break; + + case T_STRING: case T_FLOAT: + + decl->is_integer = FALSE; + decl->value = index; + break; + + default: + + THROW("Bad constant type"); + } +} +#endif + +#if 0 +PUBLIC void TRANS_want(int reserved) +{ + if (!PATTERN_is(*JOB->current, reserved)) + THROW("Syntax error. %s expected", COMP_res_info[reserved].name); + JOB->current++; +} + + +PUBLIC boolean TRANS_is(int reserved) +{ + if (PATTERN_is(*JOB->current, reserved)) + { + JOB->current++; + return TRUE; + } + else + return FALSE; +} + +PUBLIC void TRANS_ignore(int reserved) +{ + if (PATTERN_is(*JOB->current, reserved)) + JOB->current++; +} + +#endif + diff --git a/main/lib/eval/eval_trans.h b/main/lib/eval/eval_trans.h new file mode 100644 index 00000000..b91c98b5 --- /dev/null +++ b/main/lib/eval/eval_trans.h @@ -0,0 +1,73 @@ +/*************************************************************************** + + eval_trans.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_TRANS_H +#define __EVAL_TRANS_H + +#include "gb_type_common.h" +#include "gb_reserved.h" +#include "eval_read.h" +#include "gb_limit.h" +#include "../../gbx/gbx_class.h" + +#include "gbc_trans_common.h" + +/* +enum { + TT_NOTHING = 0, + TT_DO_NOT_CHECK_AS = 1, + TT_CAN_SQUARE = 2, + TT_CAN_ARRAY = 4, + TT_CAN_NEW = 8 + }; +*/ + +/* +void TRANS_reset(void); +boolean TRANS_newline(void); +boolean TRANS_type(int flag, TRANS_DECL *result); +boolean TRANS_check_declaration(void); +void TRANS_get_constant_value(TRANS_DECL *decl, PATTERN value); + +void TRANS_want(int reserved); +boolean TRANS_is(int reserved); +void TRANS_ignore(int reserved); +*/ + +bool TRANS_get_number(int index, TRANS_NUMBER *result); + +/* eval_trans_expr.c */ + +void TRANS_expression(void); +bool TRANS_affectation(void); +void TRANS_operation(short op, short nparam, PATTERN previous); + +/* eval_trans_tree.c */ + +#define RS_UNARY (-1) + +void TRANS_tree(void); +/*boolean TRANS_is_statement(TRANS_TREE *tree);*/ + +#endif + diff --git a/main/lib/eval/eval_trans_expr.c b/main/lib/eval/eval_trans_expr.c new file mode 100644 index 00000000..2db42154 --- /dev/null +++ b/main/lib/eval/eval_trans_expr.c @@ -0,0 +1,626 @@ +/*************************************************************************** + + eval_trans_expr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_TRANS_EXPR_C + +#define PROJECT_EXEC + +#include "gb_common.h" +#include + +#include "gb_error.h" +#include "gb_reserved.h" + +#include "gb_code.h" +#include "eval_trans.h" +#include "eval.h" + +/*#define DEBUG*/ + +static int subr_array_index = -1; +static int subr_collection_index = -1; + +static void find_subr(int *index, const char *name) +{ + if (*index < 0) + *index = RESERVED_find_subr(name, strlen(name)); +} + +static short get_nparam(PATTERN *tree, int *index) +{ + PATTERN pattern; + + if (*index < (ARRAY_count(tree) - 1)) + { + pattern = tree[*index + 1]; + if (PATTERN_is_param(pattern)) + { + (*index)++; + return (short)PATTERN_index(pattern); + } + } + + return 0; +} + + +static void push_integer(int index) +{ + CODE_push_number(index); +} + + +static void push_number(int index) +{ + TRANS_NUMBER number; + CLASS_CONST cst; + //SYMBOL *sym; + + if (TRANS_get_number(index, &number)) + THROW(E_SYNTAX); + + if (number.type == T_INTEGER) + { + CODE_push_number(number.ival); + } + else if (number.type == T_FLOAT && number.dval == (double)(int)number.dval && number.dval >= -128 && number.dval <= 127) + { + CODE_push_float(number.dval); + } + else + { + cst.type = number.type; + if (cst.type == T_FLOAT) + cst._float.value = number.dval; + else if (cst.type == T_LONG) + cst._long.value = number.lval; + + CODE_push_const(EVAL_add_constant(&cst)); + } + + if (number.complex) + CODE_push_complex(); +} + + +static void push_string(int index, bool trans) +{ + CLASS_CONST cst; + SYMBOL *sym; + int len; + + if (index == VOID_STRING_INDEX) + len = 0; + else + { + sym = TABLE_get_symbol(EVAL->string, index); + len = sym->len; + } + + if (len == 0) + { + CODE_push_void_string(); + } + else if (len == 1) + { + CODE_push_char(*(sym->name)); + } + else + { + cst.type = trans ? T_CSTRING : T_STRING; + cst._string.addr = sym->name; + cst._string.len = len; + + CODE_push_const(EVAL_add_constant(&cst)); + } +} + + +/* +static void push_class(int index) +{ + TRANS_DECL decl; + + decl.type = TYPE_make(T_STRING, 0, 0); + decl.index = NO_SYMBOL; + decl.value = index; + CODE_push_class(CLASS_add_constant(EVAL->class, &decl)); +} +*/ + + +static void trans_class(int index) +{ + SYMBOL *sym = TABLE_get_symbol(EVAL->table, index); + + if (GB.ExistClassLocal(sym->name)) + CODE_push_class(EVAL_add_class(sym->name)); + else + { + THROW("Unknown class"); + } +} + + +static void trans_identifier(int index, bool first, bool point) +{ + SYMBOL *sym = TABLE_get_symbol(EVAL->table, index); + + if (sym->name[sym->len]) + sym->name[sym->len] = 0; + + if (point) + { + CODE_push_unknown(EVAL_add_unknown(sym->name)); + } + else if (first && GB.ExistClassLocal(sym->name)) + { + //printf("%.*s %s\n", sym->symbol.len, sym->symbol.name, isupper(*sym->symbol.name) ? "U" : "l"); + CODE_push_class(EVAL_add_class(sym->name)); + } + else + { + CODE_push_local(EVAL_add_variable(index)); + } +} + + +static void trans_subr(int subr, short nparam) +{ + SUBR_INFO *info = &COMP_subr_info[subr]; + + //fprintf(stderr, "trans_subr: %d: %s: %d %d\n", subr, info->name, info->min_param, info->max_param); + + if (nparam < info->min_param) + THROW2("Not enough arguments to &1()", info->name); + else if (nparam > info->max_param) + THROW2("Too many arguments to &1()", info->name); + + CODE_subr(info->opcode, nparam, info->optype, info->max_param == info->min_param); +} + + +void TRANS_operation(short op, short nparam, PATTERN previous) +{ + COMP_INFO *info = &COMP_res_info[op]; + + switch (info->value) + { + case OP_PT: + + /*if (nparam == 0) + TRANS_use_with();*/ + if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + break; + + case OP_EXCL: + if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + break; + + case OP_LSQR: + CODE_push_array(nparam); + break; + + case OP_RSQR: + find_subr(&subr_array_index, ".Array"); + if (nparam > MAX_PARAM_OP) + CODE_subr(COMP_subr_info[subr_array_index].opcode, MAX_PARAM_OP + 1, CODE_CALL_VARIANT + MAX_PARAM_OP, FALSE); + else + trans_subr(subr_array_index, nparam); + break; + + case OP_COLON: + find_subr(&subr_collection_index, ".Collection"); + if (nparam > MAX_PARAM_OP) + CODE_subr(COMP_subr_info[subr_collection_index].opcode, MAX_PARAM_OP, CODE_CALL_VARIANT + MAX_PARAM_OP - 1, FALSE); + else + trans_subr(subr_collection_index, nparam); + break; + + case OP_LBRA: + CODE_call(nparam); + break; + + case OP_MINUS: + if (nparam == 1) + CODE_op(C_NEG, 0, nparam, TRUE); + else + CODE_op(info->code, info->subcode, nparam, TRUE); + break; + + default: + + CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN)); + } +} + + +static void trans_expr_from_tree(PATTERN *tree) +{ + int i; + short nparam; + int count; + PATTERN pattern, prev_pattern; //, next_pattern + + count = ARRAY_count(tree) - 1; + pattern = NULL_PATTERN; + + for (i = 0; i <= count; i++) + { + prev_pattern = pattern; + pattern = tree[i]; + /*if (i < count) + next_pattern = tree[i + 1]; + else + next_pattern = NULL_PATTERN;*/ + + if (PATTERN_is_integer(pattern)) + push_integer(PATTERN_signed_index(pattern)); + + if (PATTERN_is_number(pattern)) + push_number(PATTERN_index(pattern)); + + else if (PATTERN_is_string(pattern)) + push_string(PATTERN_index(pattern), FALSE); + + else if (PATTERN_is_tstring(pattern)) + push_string(PATTERN_index(pattern), TRUE); + + else if (PATTERN_is_identifier(pattern)) + trans_identifier(PATTERN_index(pattern), PATTERN_is_first(pattern), PATTERN_is_point(pattern)); + + else if (PATTERN_is_class(pattern)) + trans_class(PATTERN_index(pattern)); + + else if (PATTERN_is_subr(pattern)) + { + nparam = get_nparam(tree, &i); + trans_subr(PATTERN_index(pattern), nparam); + } + + else if (PATTERN_is_reserved(pattern)) + { + if (PATTERN_is(pattern, RS_TRUE)) + { + CODE_push_boolean(TRUE); + } + else if (PATTERN_is(pattern, RS_FALSE)) + { + CODE_push_boolean(FALSE); + } + else if (PATTERN_is(pattern, RS_NULL)) + { + CODE_push_null(); + } + else if (PATTERN_is(pattern, RS_ME)) + { + /*if (FUNCTION_is_static(EVAL->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_me(TRUE); + } + else if (PATTERN_is(pattern, RS_SUPER)) + { + /*if (FUNCTION_is_static(EVAL->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_super(TRUE); + } + else if (PATTERN_is(pattern, RS_LAST)) + { + CODE_push_last(); + } + /* + else if (PATTERN_is(pattern, RS_AT)) + { + if (!CODE_popify_last()) + THROW("Invalid output parameter"); + } + */ + else if (PATTERN_is(pattern, RS_COMMA)) + { + CODE_drop(); + } + /*else if (PATTERN_is(pattern, RS_ERROR)) + { + TRANS_subr(TS_SUBR_ERROR, 0); + }*/ + else if (PATTERN_is(pattern, RS_OPTIONAL)) + { + CODE_push_void(); + } + else if (PATTERN_is(pattern, RS_PINF)) + { + CODE_push_inf(FALSE); + } + else if (PATTERN_is(pattern, RS_MINF)) + { + CODE_push_inf(TRUE); + } + else + { + nparam = get_nparam(tree, &i); + TRANS_operation((short)PATTERN_index(pattern), nparam, prev_pattern); + } + } + } +} + +#if 0 +static void trans_new(void) +{ + int index; + int i, nparam; + bool array = FALSE; + bool event = FALSE; + bool collection = FALSE; + + if (PATTERN_is_identifier(*EVAL->current)) + { + index = PATTERN_index(*EVAL->current); + CODE_push_class(CLASS_add_class(EVAL->class, index)); + nparam = 1; + } + else if (PATTERN_is_type(*EVAL->current)) + { + if (PATTERN_is(EVAL->current[1], RS_LSQR)) + { + CODE_push_number(RES_get_type(PATTERN_index(*EVAL->current))); + nparam = 1; + } + else + THROW("Cannot instanciate native types"); + } + + EVAL->current++; + + if (PATTERN_is(*EVAL->current, RS_LSQR)) + { + if (collection) + THROW("Array declaration is forbidden with typed collection"); + + EVAL->current++; + + for (i = 0;; i++) + { + if (i > MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*EVAL->current, RS_RSQR)) + break; + + if (!PATTERN_is(*EVAL->current, RS_COMMA)) + THROW("Comma missing"); + + EVAL->current++; + } + + EVAL->current++; + array = TRUE; + } + else + { + if (PATTERN_is(*EVAL->current, RS_LBRA)) + { + EVAL->current++; + + for(;;) + { + if (nparam > MAX_PARAM_FUNC) + THROW("Too many arguments"); + + if (PATTERN_is(*EVAL->current, RS_AT)) + THROW("NEW cannot have output parameters"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*EVAL->current, RS_RBRA)) + break; + + if (!PATTERN_is(*EVAL->current, RS_COMMA)) + THROW("Comma missing"); + + EVAL->current++; + } + + EVAL->current++; + } + + if (PATTERN_is(*EVAL->current, RS_AS)) + { + EVAL->current++; + TRANS_expression(FALSE); + nparam++; + event = TRUE; + } + + /* + CODE_call(nparam, FALSE); + CODE_drop(); + */ + } + + if (collection) + CODE_new(nparam, TRUE, event); + else + CODE_new(nparam, array, event); +} +#endif + +void TRANS_expression() +{ + TRANS_tree(); + + trans_expr_from_tree(EVAL->tree); + + ARRAY_delete(&EVAL->tree); +} + + +void TRANS_reference(void) +{ + TRANS_expression(); + + if (!CODE_popify_last(FALSE)) + THROW("Invalid assignment"); + + EVAL->assign_code = EVAL->code[EVAL->ncode - 1]; +} + +static void trans_operation(short op, short nparam, PATTERN previous) +{ + COMP_INFO *info = &COMP_res_info[op]; + CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN)); +} + +bool TRANS_affectation(void) +{ + /*static TRANS_STATEMENT statement[] = { + //{ RS_NEW, TRANS_new }, + { RS_OPEN, TRANS_open }, + { RS_SHELL, TRANS_shell }, + { RS_EXEC, TRANS_exec }, + { RS_RAISE, TRANS_raise }, + { RS_PIPE, TRANS_pipe }, + { RS_LOCK, TRANS_lock }, + { RS_NONE, NULL } + };*/ + + //TRANS_STATEMENT *st; + PATTERN *look = EVAL->current; + PATTERN *left, *expr, *after; + int niv = 0; + bool equal = FALSE; + //bool stat = FALSE; + int op; + + for(;;) + { + if (PATTERN_is_newline(*look) || PATTERN_is_end(*look)) + break; + + if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR)) + { + niv++; + } + else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR)) + { + if (niv > 0) + niv--; + } + else if (niv == 0) + { + if (PATTERN_is(*look, RS_EQUAL)) + { + equal = TRUE; + op = RS_NONE; + break; + } + else if (PATTERN_is_reserved(*look) && RES_is_assignment(PATTERN_index(*look))) + { + equal = TRUE; + op = RES_get_assignment_operator(PATTERN_index(*look)); + break; + } + } + + look++; + } + + if (!equal) + return FALSE; + + left = EVAL->current; + *look++ = PATTERN_make(RT_NEWLINE, 0); + expr = look; + + EVAL->current = expr; + + /*if (op == RS_NONE && (PATTERN_is_reserved(*EVAL->current))) + { + if (PATTERN_is(*EVAL->current, RS_NEW)) + { + EVAL->current++; + TRANS_in_affectation++; + TRANS_new(); + TRANS_in_affectation--; + stat = TRUE; + } + else + { + for (st = statement; st->id; st++) + { + if (PATTERN_is(*EVAL->current, st->id)) + { + EVAL->current++; + TRANS_in_affectation++; + (*st->func)(); + TRANS_in_affectation--; + stat = TRUE; + } + } + } + }*/ + + //if (!stat) + //{ + if (op != RS_NONE) + { + EVAL->current = left; + TRANS_expression(); + } + + EVAL->current = expr; + TRANS_expression(); + after = EVAL->current; + + if (op != RS_NONE) + { + /*if (op == RS_AMP) + CODE_string_add();*/ + trans_operation(op, 2, NULL_PATTERN); + } + //} + + after = EVAL->current; + + /*if (dup) + CODE_dup();*/ + + CODE_dup(); // So that Assign() returns the assigned value + + EVAL->current = left; + TRANS_reference(); + + EVAL->current = after; + + return TRUE; +} + + diff --git a/main/lib/eval/eval_trans_tree.c b/main/lib/eval/eval_trans_tree.c new file mode 100644 index 00000000..739316ca --- /dev/null +++ b/main/lib/eval/eval_trans_tree.c @@ -0,0 +1,713 @@ +/*************************************************************************** + + eval_trans_tree.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __TRANS_TREE_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_reserved.h" + +#include "eval_trans.h" +#include "eval_read.h" +#include "eval.h" + +//#define DEBUG + +static short level; +static PATTERN *current; +/*PRIVATE PATTERN last_pattern;*/ + +static void analyze_expr(short priority, short op_main); + + +static void inc_level() +{ + level++; + if (level > MAX_EXPR_LEVEL) + THROW("Expression too complex"); +} + + +static void dec_level() +{ + level--; +} + + +static void add_pattern(PATTERN pattern) +{ + PATTERN *node; + short index; + + index = (short)ARRAY_count(EVAL->tree); + + if (index >= MAX_EXPR_PATTERN) + THROW("Expression too complex"); + + node = ARRAY_add(&EVAL->tree); + *node = pattern; + /*last_pattern = pattern;*/ + + + /*#ifdef DEBUG + READ_dump_pattern(&pattern); + #endif*/ +} + + +static void remove_last_pattern() +{ + if (!ARRAY_count(EVAL->tree)) + return; + + ARRAY_remove_last(&EVAL->tree); +} + + +static PATTERN get_last_pattern(int dep) +{ + short index; + + index = (short)ARRAY_count(EVAL->tree); + if (index < dep) + return NULL_PATTERN; + + return EVAL->tree[index - dep]; +} + + +static void change_last_pattern(int dep, PATTERN pattern) +{ + short index; + + index = (short)ARRAY_count(EVAL->tree); + if (index < dep) + return; + + EVAL->tree[index - dep] = pattern; +} + + +static void check_last_first(int dep) +{ + if (PATTERN_is_identifier(get_last_pattern(dep))) + change_last_pattern(dep, PATTERN_set_flag(get_last_pattern(dep), RT_FIRST)); +} + + +static void add_reserved_pattern(int reserved) +{ + add_pattern(PATTERN_make(RT_RESERVED, reserved)); +} + + + +static void add_operator_output(short op, short nparam) +{ + PATTERN pattern; + + /* + Why this test? + + add_operator() can be called without operator. See: + if (RES_priority(op) < prio) ... + */ + + if (op == RS_NONE || op == RS_UNARY) + return; + + if (op == RS_EXCL) + { + op = RS_LSQR; + nparam = 2; + + check_last_first(2); + } + + pattern = PATTERN_make(RT_RESERVED, op); + + /*if (op == RS_LBRA && has_output) + pattern = PATTERN_set_flag(pattern, RT_OUTPUT);*/ + + add_pattern(pattern); + + pattern = PATTERN_make(RT_PARAM, nparam); + add_pattern(pattern); +} + + +static void add_operator(short op, short nparam) +{ + add_operator_output(op, nparam); +} + + + +static void add_subr(PATTERN subr_pattern, short nparam) +{ + PATTERN pattern; + + /*if (has_output) + subr_pattern = PATTERN_set_flag(subr_pattern, RT_OUTPUT);*/ + + add_pattern(subr_pattern); + + pattern = PATTERN_make(RT_PARAM, nparam); + add_pattern(pattern); +} + + +static void analyze_make_array() +{ + int n = 0; + bool checked = FALSE; + bool collection = FALSE; + + if (!PATTERN_is(*current, RS_RSQR)) + { + for(;;) + { + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + + if (!checked) + { + collection = PATTERN_is(*current, RS_COLON); + checked = TRUE; + } + + if (collection) + { + if (!PATTERN_is(*current, RS_COLON)) + THROW("Missing ':'"); + current++; + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + } + + if (!PATTERN_is(*current, RS_COMMA)) + break; + current++; + + if (collection) + { + if (n == (MAX_PARAM_OP - 1)) + { + add_operator(RS_COLON, MAX_PARAM_OP + 1); + n = 0; + } + } + else + { + if (n == MAX_PARAM_OP) + { + add_operator(RS_RSQR, MAX_PARAM_OP + 1); + n = 0; + } + } + } + } + + if (!PATTERN_is(*current, RS_RSQR)) + THROW("Missing ']'"); + current++; + + add_operator(collection ? RS_COLON : RS_RSQR, n); +} + + + +static void analyze_single(int op) +{ + PATTERN *pattern; + + if (PATTERN_is_newline(*current)) + current++; + + if (op == RS_PT && !PATTERN_is_identifier(*current)) + THROW("The '.' operator must be followed by an identifier"); + else if (op == RS_EXCL && !PATTERN_is_string(*current)) + THROW("The '!' operator must be followed by an identifier"); + + /* ( expr ) */ + + if (PATTERN_is(*current, RS_LBRA)) + { + int index = ARRAY_count(EVAL->tree); + PATTERN last; + + current++; + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*current, RS_RBRA)) + THROW("Missing ')'"); + current++; + + if (ARRAY_count(EVAL->tree) == (index + 1)) + { + last = get_last_pattern(1); + if (PATTERN_is_string(last)) + change_last_pattern(1, PATTERN_make(RT_TSTRING, PATTERN_index(last))); + } + } + + /* [ expr, expr, ... ] */ + + else if (PATTERN_is(*current, RS_LSQR)) + { + current++; + analyze_make_array(); + } + + /* - expr | NOT expr */ + + else if (PATTERN_is(*current, RS_MINUS) || PATTERN_is(*current, RS_NOT)) + { + pattern = current; + current++; + + analyze_expr(RES_priority(RS_NOT), RS_UNARY); + add_operator(PATTERN_index(*pattern), 1); + } + + /* . symbol + + else if (PATTERN_is(*current, RS_PT) && PATTERN_is_identifier(current[1])) + { + add_operator(PATTERN_index(current[0]), 0); + add_pattern(PATTERN_set_flag(current[1], RT_POINT)); + current += 2; + }*/ + + /* NULL, TRUE, FALSE, ME, LAST */ + /* nombre, chaine ou symbole */ + + else if (PATTERN_is(*current, RS_NULL) + || PATTERN_is(*current, RS_ME) + || PATTERN_is(*current, RS_LAST) + || PATTERN_is(*current, RS_TRUE) + || PATTERN_is(*current, RS_FALSE) + || PATTERN_is(*current, RS_PINF) + || PATTERN_is(*current, RS_MINF) + //|| PATTERN_is(*current, RS_ERROR) + || (!PATTERN_is_reserved(*current) && !PATTERN_is_newline(*current) && !PATTERN_is_end(*current))) + { + add_pattern(*current); + + if (PATTERN_is_identifier(*current)) + { + /*if ((op == RS_NONE || op == RS_UNARY) && (PATTERN_is_identifier(*current))) + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_FIRST));*/ + if (op == RS_PT) + { + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_POINT)); + check_last_first(2); + } + } + + current++; + } + + else + THROW2("Unexpected &1", READ_get_pattern(current)); +} + + +static void analyze_call() +{ + int nparam_post = 0; + PATTERN subr_pattern = NULL_PATTERN; + PATTERN last_pattern = get_last_pattern(1); + SUBR_INFO *info; + bool optional = TRUE; + + if (PATTERN_is_subr(last_pattern)) + { + subr_pattern = last_pattern; + remove_last_pattern(); + optional = FALSE; + } + else if (PATTERN_is_identifier(last_pattern)) + { + if (EVAL->custom) + { + change_last_pattern(1, PATTERN_make(RT_IDENTIFIER, EVAL->var[0])); + add_reserved_pattern(RS_PT); + add_pattern(PATTERN_set_flag(last_pattern, RT_POINT)); + } + + check_last_first(1); + } + else if (PATTERN_is_string(last_pattern) || PATTERN_is_integer(last_pattern) || PATTERN_is_number(last_pattern)) + THROW(E_SYNTAX); + + /* N.B. Le cas où last_pattern = "." n'a pas de test spécifique */ + + if (subr_pattern && subr_pattern == PATTERN_make(RT_SUBR, SUBR_VarPtr)) + THROW("VarPtr() cannot be used with Eval()"); + + for (;;) + { + if (PATTERN_is(*current, RS_RBRA)) + { + current++; + break; + } + + if (nparam_post > 0) + { + if (!PATTERN_is(*current, RS_COMMA)) + THROW("Missing ')'"); + current++; + } + + if (optional && (PATTERN_is(*current, RS_COMMA) || PATTERN_is(*current, RS_RBRA))) + { + add_reserved_pattern(RS_OPTIONAL); + } + else + { + analyze_expr(0, RS_NONE); + } + + nparam_post++; + + if (nparam_post > MAX_PARAM_FUNC) + THROW("Too many arguments"); + } + + if (get_last_pattern(1) == PATTERN_make(RT_RESERVED, RS_OPTIONAL)) + THROW("Syntax error. Needless arguments"); + + /* + while (nparam_post > 0) + { + if (get_last_pattern(1) != PATTERN_make(RT_RESERVED, RS_OPTIONAL)) + break; + + remove_last_pattern(); + nparam_post--; + } + */ + + if (subr_pattern == NULL_PATTERN) + add_operator_output(RS_LBRA, nparam_post); + else + { + info = &COMP_subr_info[PATTERN_index(subr_pattern)]; + + if (nparam_post < info->min_param) + THROW2("Not enough arguments to &1", info->name); + else if (nparam_post > info->max_param) + THROW2("Too many arguments to &1", info->name); + + add_subr(subr_pattern, nparam_post); + } +} + + +static void analyze_array() +{ + int i; + + check_last_first(1); + + for(i = 0; i < MAX_ARRAY_DIM; i++) + { + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*current, RS_COMMA)) + break; + + current++; + } + + if (!PATTERN_is(*current, RS_RSQR)) + THROW("Missing ']'"); + current++; + + add_operator(RS_LSQR, i + 2); +} + +#if 0 +static void analyze_expr_check_first(int op_curr) +{ + /* On laisse le marqueur RT_FIRST que si on a affaire �l'op�ateur '.' */ + /* + last = get_last_pattern(); + if (PATTERN_is_first(last)) + { + if (op_curr != RS_PT) + change_last_pattern(PATTERN_unset_flag(last, RT_FIRST)); + } + */ +} +#endif + +static void analyze_expr(short priority, short op_main) +{ + short op, op_curr, op_not; + short prio; + short nparam; + + inc_level(); + + op_curr = op_main; + op_not = RS_NONE; + nparam = (op_main == RS_NONE || op_main == RS_UNARY) ? 0 : 1; + + /* Special NEW case */ + /* It should never be used, analysis is done elsewhere */ + + if (PATTERN_is(*current, RS_NEW)) + THROW("Cannot use NEW operator there"); + + // Operand analysis + +READ_OPERAND: + + //analyze_expr_check_first(op_curr); + + analyze_single(op_curr); + nparam++; + + if (nparam > MAX_PARAM_OP) + THROW("Expression too complex. Too many operands"); + + // operator + +READ_OPERATOR: + + if (!PATTERN_is_reserved(*current)) + goto OPERATOR_END; + + op = PATTERN_index(*current); + + if (!RES_is_operator(op)) + goto OPERATOR_END; + + current++; + + if (op == RS_NOT && PATTERN_is_reserved(*current)) + { + op_not = PATTERN_index(*current); + if (RES_is_operator(op_not) && RES_can_have_not_before(op_not)) + { + op = op_not + 1; + current++; + } + } + + /*if ((op == RS_BEGINS || op == RS_ENDS) && PATTERN_is(*current, RS_WITH)) + current++;*/ + + if (priority) + prio = priority; + else if (op_curr == RS_NONE) + prio = 0; + else + prio = RES_priority(op_curr); + + if (op_curr == RS_NONE) + { + if (RES_is_binary(op) || RES_is_n_ary(op)) + { + op_curr = op; + goto READ_OPERAND; + } + } + + if (op_curr == op) + { + if (!(RES_is_binary(op) && nparam == 2)) + goto READ_OPERAND; + } + + if (RES_priority(op) > prio) + { + if (op == RS_LSQR) + analyze_array(); + else if (op == RS_LBRA) + analyze_call(); + else + analyze_expr(RES_priority(op), op); + + goto READ_OPERATOR; + } + + if (RES_priority(op) == prio) + { + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + goto READ_OPERATOR; + } + else + { + if (RES_is_only(op_curr) || RES_is_only(op)) + THROW("Ambiguous expression. Please use brackets"); + + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + if (RES_priority(op) < prio) + { + if ((op_main != RS_NONE) || (priority > 0)) + { + add_operator(op_curr, nparam); + current--; + if (op_not != RS_NONE) + current--; + goto END; + } + + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else + { + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + dec_level(); + return; + +OPERATOR_END: + + //analyze_expr_check_first(op_curr); + + add_operator(op_curr, nparam); + +END: + + dec_level(); + return; + +} + + +PUBLIC void TRANS_tree() +{ + #ifdef DEBUG + int i; + #endif + + /*last_pattern = NULL_PATTERN;*/ + + ARRAY_create(&EVAL->tree); + /*ARRAY_add(&tree);*/ + + current = EVAL->current; //EVAL->pattern; + level = 0; + + if (PATTERN_is_newline(*current) || PATTERN_is_end(*current)) + THROW(E_SYNTAX); + + analyze_expr(0, RS_NONE); + + while (PATTERN_is_newline(*current)) + current++; + + EVAL->current = current; + + #ifdef DEBUG + printf("\n"); + for (i = 0; i < ARRAY_count(EVAL->tree); i++) + READ_dump_pattern(&EVAL->tree[i]); + #endif +} + + +#if 0 +PUBLIC bool TRANS_is_statement(TRANS_TREE *tree) +{ + PATTERN last; + int count; + + count = ARRAY_count(tree); + + if (count == 0) + return FALSE; + + count--; + last = tree[count]; + + if (PATTERN_is_param(last) && (count > 0)) + { + count--; + last = tree[count]; + } + + if (PATTERN_is(last, RS_PT) || (PATTERN_is_identifier(last) && count == 0)) + { + add_operator(RS_LBRA, 0); + return TRUE; + } + + if (PATTERN_is_subr(last) + || PATTERN_is(last, RS_LBRA) + || PATTERN_is(last, RS_RBRA) + || PATTERN_is(last, RS_AT) + || PATTERN_is(last, RS_COMMA)) + return TRUE; + + #ifdef DEBUG + printf("Last = "); + READ_dump_pattern(&last); + #endif + + return FALSE; + +} +#endif + diff --git a/main/lib/eval/gb.eval.component b/main/lib/eval/gb.eval.component new file mode 100644 index 00000000..427ea0af --- /dev/null +++ b/main/lib/eval/gb.eval.component @@ -0,0 +1,20 @@ +[Component] +Key=gb.eval +Name=Gambas expression evaluator +Name[fr]=Evaluateur d'expression de Gambas +Name[pl]=Kompilator wyrażeń Gambasa +Name[es]=Evaluador de expresiones de Gambas +Name[tr]=Gambas ifade değerlendirici +Author=Benoît Minisini + +;-------------------------------------------------- + +[Component] +Key=gb.eval +Name=Gambas expression evaluator +Name[fr]=Evaluateur d'expression de Gambas +Name[pl]=Kompilator wyrażeń Gambasa +Name[es]=Evaluador de expresiones de Gambas +Author=Benoît Minisini + + diff --git a/main/lib/eval/gb.eval.h b/main/lib/eval/gb.eval.h new file mode 100644 index 00000000..9234c737 --- /dev/null +++ b/main/lib/eval/gb.eval.h @@ -0,0 +1,111 @@ +/*************************************************************************** + + gb.eval.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_EVAL_H +#define __GB_EVAL_H + +#include "gambas.h" +#include "gbc_read_common.h" + +/*#define DEBUG*/ + +typedef + bool (*EVAL_FUNCTION)(const char *, int, GB_VARIANT *); + +typedef + void *EVAL_EXPRESSION; + +#define EVAL_COLOR_MAX_LEN 1023 + +typedef + struct { + unsigned state : 5; + unsigned alternate : 1; + unsigned len : 10; + } + EVAL_COLOR; + +typedef + EVAL_COLOR *EVAL_COLOR_ARRAY; + +typedef + struct + { + char *str; + EVAL_COLOR *color; + int len; + int proc; + int state; + } + EVAL_ANALYZE; + +#define EVAL_NORMAL 0 +#define EVAL_USE_CONTEXT 1 + +typedef + enum + { + HIGHLIGHT_BACKGROUND, + HIGHLIGHT_NORMAL, + HIGHLIGHT_SELECTION, + HIGHLIGHT_ALTERNATE, + HIGHLIGHT_HIGHLIGHT, + HIGHLIGHT_LINE, + HIGHLIGHT_ADDED, + HIGHLIGHT_REMOVED, + HIGHLIGHT_ERROR, + HIGHLIGHT_COMMENT, + HIGHLIGHT_HELP, + HIGHLIGHT_KEYWORD, + HIGHLIGHT_SUBR, + HIGHLIGHT_OPERATOR, + HIGHLIGHT_SYMBOL, + HIGHLIGHT_NUMBER, + HIGHLIGHT_STRING, + HIGHLIGHT_BREAKPOINT, + HIGHLIGHT_CURRENT, + HIGHLIGHT_DATATYPE, + HIGHLIGHT_PREPROCESSOR, + HIGHLIGHT_ESCAPE, + HIGHLIGHT_LABEL, + HIGHLIGHT_CONSTANT, + HIGHLIGHT_NUM_COLOR + } + HIGHLIGHT_COLOR; + +typedef + struct { + int version; + void (*Analyze)(const char *src, int len, int state, EVAL_ANALYZE *result, bool rewrite); + void (*New)(EVAL_EXPRESSION *expr, const char *src, int len); + bool (*Compile)(EVAL_EXPRESSION expr, bool assign); + GB_VALUE *(*Run)(EVAL_EXPRESSION expr, EVAL_FUNCTION func); + void (*Free)(EVAL_EXPRESSION *expr); + bool (*GetAssignmentSymbol)(EVAL_EXPRESSION expr, const char **sym, int *len); + } + EVAL_INTERFACE; + +#define EVAL_INTERFACE_VERSION 2 + +#endif + diff --git a/main/lib/eval/gb_alloc_override.h b/main/lib/eval/gb_alloc_override.h new file mode 100644 index 00000000..7f255dcc --- /dev/null +++ b/main/lib/eval/gb_alloc_override.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + gb_alloc_override.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ALLOC_H +#define __GB_ALLOC_H + +#include "main.h" + +#define ALLOC(_ptr, _len) GB.Alloc((void **)(void *)_ptr, _len) +#define FREE(_ptr) GB.Free((void **)(void *)_ptr) +#define REALLOC(_ptr, _len) GB.Realloc((void **)(void *)_ptr, _len) + +#endif + diff --git a/main/lib/eval/gb_array.c b/main/lib/eval/gb_array.c new file mode 100644 index 00000000..6b2cfe60 --- /dev/null +++ b/main/lib/eval/gb_array.c @@ -0,0 +1,26 @@ +/*************************************************************************** + + gb_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_alloc_override.h" +#include "gb_array_temp.h" + diff --git a/main/lib/eval/gb_error.c b/main/lib/eval/gb_error.c new file mode 100644 index 00000000..935642f6 --- /dev/null +++ b/main/lib/eval/gb_error.c @@ -0,0 +1,116 @@ +/*************************************************************************** + + gb_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ERROR_C + +#include "main.h" +#include "gb_common.h" +#include +#include "eval.h" +#include "gb_error.h" + +ERROR_CONTEXT *ERROR_current = NULL; + +void ERROR_clear(void) +{ + errno = 0; +} + +void ERROR_reset(ERROR_INFO *info) +{ + if (!info->code) + return; + + info->code = 0; + if (info->free) + { + GB.FreeString(&info->msg); + info->free = FALSE; + } + + info->msg = NULL; +} + + +void ERROR_propagate() +{ + if (ERROR_in_catch(ERROR_current)) + ERROR_leave(ERROR_current); + longjmp(ERROR_current->env, 1); +} + + + +PUBLIC char *ERROR_get(void) +{ + /* + if (code > 0 && code < 256) + return strerror(code); + else + return ERROR_Message[code - 256]; + */ + return strerror(errno); +} + +PUBLIC void THROW(const char *msg) +{ + GB.FreeString(&EVAL->error); + EVAL->error = GB.NewZeroString(msg); + ERROR_propagate(); +} + +static const char *_error_arg; + +static void get_error_arg(int index, char **str, int *len) +{ + *str = (char *)_error_arg; + *len = strlen(_error_arg); +} + +PUBLIC void THROW2(const char *pattern, const char *msg) +{ + GB.FreeString(&EVAL->error); + _error_arg = msg; + EVAL->error = GB.NewZeroString(GB.SubstString(pattern, strlen(pattern), get_error_arg)); + ERROR_propagate(); +} + +void ERROR_panic(const char *error, ...) +{ + va_list args; + + va_start(args, error); + + fflush(NULL); + + fprintf(stderr, "\n** INTERNAL ERROR **\n**"); + vfprintf(stderr, error, args); + putc('\n', stderr); + /*if (ERROR_current->info.code) + { + ERROR_print(); + }*/ + fprintf(stderr, "** Program aborting. Sorry! :-(\n"); + /*abort();*/ + _exit(1); +} diff --git a/main/lib/eval/gb_error.h b/main/lib/eval/gb_error.h new file mode 100644 index 00000000..cc9ea532 --- /dev/null +++ b/main/lib/eval/gb_error.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + gb_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_H +#define __GB_ERROR_H + +#include +#include + +#include "gb_limit.h" + +#define NO_ERROR_HANDLER + +#include "gb_error_common.h" + +#define E_MEMORY "Out of memory" +#define E_SYNTAX "Syntax error" + +#ifndef __GB_ERROR_C +EXTERN ERROR_CONTEXT *ERROR_current; +#endif + +void ERROR_clear(void); +char *ERROR_get(void); +void ERROR_reset(ERROR_INFO *info); + +//void ERROR_enter(ERROR_CONTEXT *err); +//void ERROR_leave(ERROR_CONTEXT *err); + +void PROPAGATE() NORETURN; +void THROW(const char *msg) NORETURN; +void THROW2(const char *pattern, const char *msg) NORETURN; + +void ERROR_panic(const char *error, ...) NORETURN; + +void ERROR_print(void); +void ERROR_print_at(FILE *where); + +#endif diff --git a/main/lib/eval/gb_table.c b/main/lib/eval/gb_table.c new file mode 100644 index 00000000..591ca067 --- /dev/null +++ b/main/lib/eval/gb_table.c @@ -0,0 +1,28 @@ +/*************************************************************************** + + gb_table.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gambas.h" +#include "gb_alloc_override.h" + +#include "gb_table_temp.h" + diff --git a/main/lib/eval/main.c b/main/lib/eval/main.c new file mode 100644 index 00000000..dad675c5 --- /dev/null +++ b/main/lib/eval/main.c @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gb_common.h" +#include "gambas.h" + +#include "eval.h" +#include "eval_analyze.h" +#include "main.h" + +#include "c_expression.h" +#include "c_system.h" +#include "c_highlight.h" + +GB_INTERFACE GB EXPORT; + +GB_CLASS CLASS_Expression; + +void *GB_EVAL_2[] EXPORT = { + + (void *)EVAL_INTERFACE_VERSION, + (void *)EVAL_analyze, + (void *)EVAL_new, + (void *)EVAL_compile, + (void *)EVAL_expression, + (void *)EVAL_free, + (void *)EVAL_get_assignment_symbol, + + NULL + }; + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CExpressionDesc, + CSystemDesc, + CHighlightDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + CLASS_Expression = GB.FindClass("Expression"); + + EVAL_init(); + + return 0; +} + +void EXPORT GB_EXIT() +{ + EVAL_exit(); +} + + diff --git a/main/lib/eval/main.h b/main/lib/eval/main.h new file mode 100644 index 00000000..ae82a427 --- /dev/null +++ b/main/lib/eval/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; + +extern GB_CLASS CLASS_Expression; +#endif + +#endif diff --git a/main/lib/gb.component b/main/lib/gb.component new file mode 100644 index 00000000..2f4ae673 --- /dev/null +++ b/main/lib/gb.component @@ -0,0 +1,4 @@ +[Component] +Key=gb +Name=Gambas internal native classes +Author=Benoît Minisini diff --git a/main/lib/geom/Makefile.am b/main/lib/geom/Makefile.am new file mode 100644 index 00000000..7e175915 --- /dev/null +++ b/main/lib/geom/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.geom +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.geom.la + +gb_geom_la_LIBADD = @MATH_LIB@ +gb_geom_la_LDFLAGS = -module @LD_FLAGS@ +gb_geom_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_geom_la_SOURCES = \ + crect.h crect.c crect_temp.h \ + cpoint.h cpoint.c cpoint_temp.h \ + gb.geom.h main.h main.c diff --git a/main/lib/geom/cpoint.c b/main/lib/geom/cpoint.c new file mode 100644 index 00000000..bdfb2217 --- /dev/null +++ b/main/lib/geom/cpoint.c @@ -0,0 +1,33 @@ +/*************************************************************************** + + cpoint.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPOINT_C + +#include "gb_common.h" +#include "cpoint.h" +#include "crect.h" +#include "cpoint_temp.h" + +IMPLEMENT_POINT_CLASS(CPOINT, Point, GB_INTEGER, int, "i", GB.ReturnInteger, ((CPOINT *)_object), CRECT, Rect) +IMPLEMENT_POINT_CLASS(CPOINTF, PointF, GB_FLOAT, double, "f", GB.ReturnFloat, ((CPOINTF *)_object), CRECTF, RectF) + diff --git a/main/lib/geom/cpoint.h b/main/lib/geom/cpoint.h new file mode 100644 index 00000000..a0663950 --- /dev/null +++ b/main/lib/geom/cpoint.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + cpoint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPOINT_H +#define __CPOINT_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CRECT_C +extern GB_DESC PointDesc[]; +extern GB_DESC PointFDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + int x; + int y; + } + CPOINT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + } + CPOINTF; + +#endif + +CPOINT * CPOINT_create(int x, int y); +CPOINTF * CPOINTF_create(double x, double y); diff --git a/main/lib/geom/cpoint_temp.h b/main/lib/geom/cpoint_temp.h new file mode 100644 index 00000000..920af5e2 --- /dev/null +++ b/main/lib/geom/cpoint_temp.h @@ -0,0 +1,262 @@ +/*************************************************************************** + + cpoint_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define IMPLEMENT_POINT_CLASS(__struct, __name, __gtype, __ctype, __sign, __return, __this, __rstruct, __rname) \ + \ +__struct * __struct##_create(__ctype x, __ctype y) \ +{ \ + __struct *p = GB.New(GB.FindClass(#__name), NULL, NULL); \ + p->x = x; \ + p->y = y; \ + return p; \ +} \ + \ +static inline __struct *__struct##_make(__struct *a, const __ctype x, const __ctype y) \ +{ \ + if (a->ob.ref <= 1) \ + { \ + a->x = x; \ + a->y = y; \ + return a; \ + } \ + else \ + return __struct##_create(x, y); \ +} \ + \ +static __struct *_add_##__name(__struct *a, __struct *b, bool invert) \ +{ \ + return __struct##_make(a, a->x + b->x, a->y + b->y); \ +} \ + \ +static __struct *_sub_##__name(__struct *a, __struct *b, bool invert) \ +{ \ + return __struct##_make(a, a->x - b->x, a->y - b->y); \ +} \ + \ +static __struct *_mulf_##__name(__struct *a, double f, bool invert) \ +{ \ + return __struct##_make(a, a->x * f, a->y * f); \ +} \ + \ +static __struct *_mulo_##__name(__struct *a, void *b, bool invert) \ +{ \ + return NULL; \ +} \ + \ +static __struct *_divf_##__name(__struct *a, double f, bool invert) \ +{ \ + if (invert) \ + return NULL; \ + if (f == 0.0) \ + return NULL; \ + \ + return __struct##_make(a, a->x / f, a->y / f); \ +} \ + \ +static __struct *_divo_##__name(__struct *a, void *b, bool invert) \ +{ \ + return NULL; \ +} \ + \ +static int _equal_##__name(__struct *a, __struct *b, bool invert) \ +{ \ + return a->x == b->x && a->y == b->y; \ +} \ + \ +static double _fabs_##__name(__struct *a) \ +{ \ + return hypot(a->x, a->y); \ +} \ + \ +static __struct *_neg_##__name(__struct *a) \ +{ \ + return __struct##_make(a, -a->x, -a->y); \ +} \ + \ +static GB_OPERATOR_DESC _operator_##__name = \ +{ \ + .equal = (void *)_equal_##__name, \ + .add = (void *)_add_##__name, \ + .sub = (void *)_sub_##__name, \ + .mulf = (void *)_mulf_##__name, \ + .mulo = (void *)_mulo_##__name, \ + .divf = (void *)_divf_##__name, \ + .divo = (void *)_divo_##__name, \ + .fabs = (void *)_fabs_##__name, \ + .neg = (void *)_neg_##__name, \ +}; \ + \ +char *__struct##_to_string(void *a, bool local) \ +{ \ + char *result = NULL; \ + char *str; \ + int len; \ + \ + __ctype x = ((__struct *)a)->x; \ + __ctype y = ((__struct *)a)->y; \ + \ + result = GB.AddChar(result, '['); \ + \ + GB.NumberToString(local, x, NULL, &str, &len); \ + result = GB.AddString(result, str, len); \ + \ + result = GB.AddChar(result, local ? ' ' : ','); \ + \ + GB.NumberToString(local, y, NULL, &str, &len); \ + result = GB.AddString(result, str, len); \ + \ + result = GB.AddChar(result, ']'); \ + \ + return result; \ +} \ + \ +static bool _convert_##__name(void *a, GB_TYPE type, GB_VALUE *conv) \ +{ \ + if (a) \ + { \ + double norm = _fabs_##__name(a); \ + \ + switch (type) \ + { \ + case GB_T_FLOAT: \ + conv->_float.value = norm; \ + return FALSE; \ + \ + case GB_T_SINGLE: \ + conv->_single.value = norm; \ + return FALSE; \ + \ + case GB_T_INTEGER: \ + case GB_T_SHORT: \ + case GB_T_BYTE: \ + conv->_integer.value = norm; \ + return FALSE; \ + \ + case GB_T_LONG: \ + conv->_long.value = norm; \ + return FALSE; \ + \ + case GB_T_STRING: \ + case GB_T_CSTRING: \ + conv->_string.value.addr = __struct##_to_string(a, type == GB_T_CSTRING); \ + conv->_string.value.start = 0; \ + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); \ + return FALSE; \ + \ + default: \ + if (type == GB.FindClass("Point")) \ + { \ + conv->_object.value = CPOINT_create(((__struct *)a)->x, ((__struct *)a)->y); \ + return FALSE; \ + } \ + if (type == GB.FindClass("PointF")) \ + { \ + conv->_object.value = CPOINTF_create(((__struct *)a)->x, ((__struct *)a)->y); \ + return FALSE; \ + } \ + else \ + return TRUE; \ + } \ + } \ + else \ + return TRUE; \ +} \ + \ + \ +BEGIN_METHOD(__name##_new, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + if (!MISSING(x) && !MISSING(y)) \ + { \ + __this->x = VARG(x); \ + __this->y = VARG(y); \ + } \ + else if (!MISSING(x) || !MISSING(y)) \ + { \ + GB.Error("Not enough arguments"); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_call, __gtype x; __gtype y) \ + \ + GB.ReturnObject(__struct##_create(VARGOPT(x, 0), VARGOPT(y, 0))); \ + \ +END_METHOD \ + \ +BEGIN_PROPERTY(__name##_X) \ + \ + if (READ_PROPERTY) \ + __return(__this->x); \ + else \ + __this->x = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Y) \ + \ + if (READ_PROPERTY) \ + __return(__this->y); \ + else \ + __this->y = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_METHOD_VOID(__name##_Copy) \ + \ + GB.ReturnObject(__struct##_create(__this->x, __this->y)); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_InRect, GB_OBJECT rect) \ + \ + __rstruct *rect = VARG(rect); \ + \ + if (GB.CheckObject(rect)) \ + return; \ + \ + GB.ReturnBoolean( \ + (__this->x >= rect->x) && (__this->x < (rect->x + rect->w)) \ + && (__this->y >= rect->y) && (__this->y < (rect->y + rect->h)) \ + ); \ + \ +END_METHOD \ + \ +GB_DESC __name##Desc[] = \ +{ \ + GB_DECLARE(#__name, sizeof(__struct)), \ + \ + GB_METHOD("_new", NULL, __name##_new, "[(X)" __sign "(Y)" __sign), \ + GB_STATIC_METHOD("_call", #__name, __name##_call, "[(X)" __sign "(Y)" __sign), \ + \ + GB_PROPERTY("X", __sign, __name##_X), \ + GB_PROPERTY("Y", __sign, __name##_Y), \ + \ + GB_METHOD("Copy", #__name, __name##_Copy, NULL), \ + GB_METHOD("InRect", "b", __name##_InRect, "(Rectangle)" #__rname ";"), \ + \ + GB_INTERFACE("_operator", &_operator_##__name), \ + GB_INTERFACE("_convert", &_convert_##__name), \ + \ + GB_END_DECLARE \ +}; + diff --git a/main/lib/geom/crect.c b/main/lib/geom/crect.c new file mode 100644 index 00000000..c9a90dfc --- /dev/null +++ b/main/lib/geom/crect.c @@ -0,0 +1,32 @@ +/*************************************************************************** + + crect.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRECT_C + +#include "gb_common.h" +#include "cpoint.h" +#include "crect.h" +#include "crect_temp.h" + +IMPLEMENT_RECT_CLASS(CRECT, Rect, GB_INTEGER, int, "i", GB.ReturnInteger, ((CRECT *)_object), CPOINT, Point) +IMPLEMENT_RECT_CLASS(CRECTF, RectF, GB_FLOAT, double, "f", GB.ReturnFloat, ((CRECTF *)_object), CPOINTF, PointF) diff --git a/main/lib/geom/crect.h b/main/lib/geom/crect.h new file mode 100644 index 00000000..df3dc25b --- /dev/null +++ b/main/lib/geom/crect.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + crect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRECT_H +#define __CRECT_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CRECT_C +extern GB_DESC RectDesc[]; +extern GB_DESC RectFDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + int x; + int y; + int w; + int h; + } + PACKED + CRECT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + double w; + double h; + } + PACKED + CRECTF; + +CRECT *CRECT_create(void); +CRECTF *CRECTF_create(void); + +#endif diff --git a/main/lib/geom/crect_temp.h b/main/lib/geom/crect_temp.h new file mode 100644 index 00000000..a667e282 --- /dev/null +++ b/main/lib/geom/crect_temp.h @@ -0,0 +1,427 @@ +/*************************************************************************** + + crect_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define IMPLEMENT_RECT_CLASS(__struct, __name, __gtype, __ctype, __sign, __return, __this, __pstruct, __pname) \ + \ +static void __struct##_normalize(__struct *_object) \ +{ \ + if (__this->w < 0) \ + { \ + __this->w = (- __this->w); \ + __this->x -= __this->w; \ + } \ + \ + if (__this->h < 0) \ + { \ + __this->h = (- __this->h); \ + __this->y -= __this->h; \ + } \ +} \ + \ +__struct * __struct##_create(void) \ +{ \ + return GB.New(GB.FindClass(#__name), NULL, NULL); \ +} \ + \ + \ +BEGIN_METHOD(__name##_new, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + if (!MISSING(x) && !MISSING(y) && !MISSING(w) && !MISSING(h)) \ + { \ + __this->x = VARG(x); \ + __this->y = VARG(y); \ + __this->w = VARG(w); \ + __this->h = VARG(h); \ + __struct##_normalize(__this); \ + } \ + else if (!MISSING(x) || !MISSING(y) || !MISSING(w) || !MISSING(h)) \ + { \ + GB.Error("Not enough arguments"); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_call, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + __struct *rect = __struct##_create(); \ + \ + rect->x = VARG(x); \ + rect->y = VARG(y); \ + rect->w = VARG(w); \ + rect->h = VARG(h); \ + __struct##_normalize(rect); \ + \ + GB.ReturnObject(rect); \ + \ +END_METHOD \ + \ +BEGIN_PROPERTY(__name##_X) \ + \ + if (READ_PROPERTY) \ + __return(__this->x); \ + else \ + __this->x = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Y) \ + \ + if (READ_PROPERTY) \ + __return(__this->y); \ + else \ + __this->y = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Width) \ + \ + if (READ_PROPERTY) \ + __return(__this->w); \ + else \ + { \ + __this->w = VPROP(__gtype); \ + __struct##_normalize(__this); \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Height) \ + \ + if (READ_PROPERTY) \ + __return(__this->h); \ + else \ + { \ + __this->h = VPROP(__gtype); \ + __struct##_normalize(__this); \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Left) \ + \ + if (READ_PROPERTY) \ + __return(__this->x); \ + else \ + { \ + __ctype dx = VPROP(__gtype) - __this->x; \ + if (dx > __this->w) \ + dx = __this->w; \ + \ + __this->x += dx; \ + __this->w -= dx; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Top) \ + \ + if (READ_PROPERTY) \ + __return(__this->y); \ + else \ + { \ + __ctype dy = VPROP(__gtype) - __this->y; \ + if (dy > __this->h) \ + dy = __this->h; \ + \ + __this->y += dy; \ + __this->h -= dy; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Right) \ + \ + if (READ_PROPERTY) \ + __return(__this->x + __this->w); \ + else \ + { \ + __ctype x2 = VPROP(__gtype); \ + if (x2 < __this->x) \ + x2 = __this->x; \ + \ + __this->w = x2 - __this->x; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Bottom) \ + \ + if (READ_PROPERTY) \ + __return(__this->y + __this->h); \ + else \ + { \ + __ctype y2 = VPROP(__gtype); \ + if (y2 < __this->y) \ + y2 = __this->y; \ + \ + __this->h = y2 - __this->y; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_METHOD_VOID(__name##_Clear) \ + \ + __this->x = __this->y = __this->w = __this->h = 0; \ + \ +END_METHOD \ + \ +BEGIN_METHOD_VOID(__name##_IsVoid) \ + \ + GB.ReturnBoolean(__this->w <= 0 || __this->h <= 0); \ + \ +END_METHOD \ + \ +BEGIN_METHOD_VOID(__name##_Copy) \ + \ + __struct *copy = __struct##_create(); \ + \ + copy->x = __this->x; \ + copy->y = __this->y; \ + copy->w = __this->w; \ + copy->h = __this->h; \ + \ + GB.ReturnObject(copy); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Move, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + __this->x = VARG(x); \ + __this->y = VARG(y); \ + if (!MISSING(w) && !MISSING(h)) \ + { \ + __this->w = VARG(w); \ + __this->h = VARG(h); \ + __struct##_normalize(__this); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Resize, __gtype w; __gtype h) \ + \ + __this->w = VARG(w); \ + __this->h = VARG(h); \ + __struct##_normalize(__this); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Translate, __gtype dx; __gtype dy) \ + \ + __this->x += VARG(dx); \ + __this->y += VARG(dy); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Union, GB_OBJECT rect) \ + \ + __struct *dest; \ + __ctype x, y, w, h; \ + __struct *rect = (__struct *)VARG(rect); \ + \ + if (GB.CheckObject(rect)) \ + return; \ + \ + dest = __struct##_create(); \ + \ + x = Min(__this->x, rect->x); \ + y = Min(__this->y, rect->y); \ + w = Max(__this->x + __this->w, rect->x + rect->w) - x; \ + h = Max(__this->y + __this->h, rect->y + rect->h) - y; \ + \ + dest->x = x; \ + dest->y = y; \ + dest->w = w; \ + dest->h = h; \ + \ + GB.ReturnObject(dest); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Intersection, GB_OBJECT rect) \ + \ + __struct *dest; \ + __ctype x, y, x2, y2; \ + __struct *rect = (__struct *)VARG(rect); \ + \ + if (GB.CheckObject(rect)) \ + return; \ + \ + x = Max(__this->x, rect->x); \ + y = Max(__this->y, rect->y); \ + x2 = Min(__this->x + __this->w, rect->x + rect->w); \ + y2 = Min(__this->y + __this->h, rect->y + rect->h); \ + \ + if (x2 > x && y2 > y) \ + { \ + dest = __struct##_create(); \ + \ + dest->x = x; \ + dest->y = y; \ + dest->w = x2 - x; \ + dest->h = y2 - y; \ + \ + GB.ReturnObject(dest); \ + } \ + else \ + { \ + GB.ReturnNull(); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Contains, __gtype x; __gtype y) \ + \ + __ctype x = VARG(x); \ + __ctype y = VARG(y); \ + \ + GB.ReturnBoolean((x >= __this->x) && (x < (__this->x + __this->w)) && (y >= __this->y) && (y < (__this->y + __this->h))); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Shrink, __gtype left; __gtype top; __gtype right; __gtype bottom) \ + \ + __ctype left = VARG(left); \ + __ctype top = VARGOPT(top, left); \ + __ctype right = VARGOPT(right, left); \ + __ctype bottom = VARGOPT(bottom, top); \ + \ + __this->x += left; \ + __this->w -= (left + right); \ + __this->y += top; \ + __this->h -= (top + bottom); \ + \ + if (__this->w < 1 || __this->h < 1) \ + __this->w = __this->h = 0; \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Expand, __gtype left; __gtype top; __gtype right; __gtype bottom) \ + \ + __ctype left = VARG(left); \ + __ctype top = VARGOPT(top, left); \ + __ctype right = VARGOPT(right, left); \ + __ctype bottom = VARGOPT(bottom, top); \ + \ + __this->x -= left; \ + __this->w += (left + right); \ + __this->y -= top; \ + __this->h += (top + bottom); \ + \ + if (__this->w < 1 || __this->h < 1) \ + __this->w = __this->h = 0; \ + \ +END_METHOD \ + \ +BEGIN_METHOD_VOID(__name##_Center) \ + \ + __pstruct *point = GB.New(GB.FindClass(#__pname), NULL, NULL); \ + point->x = __this->x + __this->w / 2; \ + point->y = __this->y + __this->h / 2; \ + GB.ReturnObject(point); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Stretch, __gtype width; __gtype height; GB_OBJECT frame; GB_INTEGER align) \ + \ + __struct *frame = (__struct *)VARG(frame); \ + int align = VARGOPT(align, ALIGN_CENTER); \ + __ctype w = VARG(width); \ + __ctype h = VARG(height); \ + \ + __struct *result; \ + double scalew, scaleh; \ + \ + if (GB.CheckObject(frame)) \ + return; \ + \ + result = __struct##_create(); \ + if (w <= 0 || h <= 0 || frame->w <= 0 || frame->h <= 0) \ + { \ + GB.ReturnObject(result); \ + return; \ + } \ + \ + scalew = (double)frame->w / w; \ + scaleh = (double)frame->h / h; \ + if (scalew > scaleh) \ + scalew = scaleh; \ + else \ + scaleh = scalew; \ + \ + result->w = w * scalew; \ + result->h = h * scaleh; \ + \ + if (ALIGN_IS_LEFT(align)) \ + result->x = frame->x; \ + else if (ALIGN_IS_CENTER(align)) \ + result->x = frame->x + (frame->w - result->w) / 2; \ + else if (ALIGN_IS_RIGHT(align)) \ + result->x = frame->x + frame->w - result->w; \ + \ + if (ALIGN_IS_TOP(align)) \ + result->y = frame->y; \ + else if (ALIGN_IS_MIDDLE(align)) \ + result->y = frame->y + (frame->h - result->h) / 2; \ + else if (ALIGN_IS_BOTTOM(align)) \ + result->y = frame->y + frame->h - result->h; \ + \ + GB.ReturnObject(result); \ + \ +END_METHOD \ + \ +GB_DESC __name##Desc[] = \ +{ \ + GB_DECLARE(#__name, sizeof(__struct)), \ + \ + GB_METHOD("_new", NULL, __name##_new, "[(X)" __sign "(Y)" __sign "(Width)" __sign "(Height)" __sign "]"), \ + GB_STATIC_METHOD("_call", #__name, __name##_call, "(X)" __sign "(Y)" __sign "(Width)" __sign "(Height)" __sign), \ + \ + GB_PROPERTY("X", __sign, __name##_X), \ + GB_PROPERTY("Y", __sign, __name##_Y), \ + GB_PROPERTY("W", __sign, __name##_Width), \ + GB_PROPERTY("H", __sign, __name##_Height), \ + GB_PROPERTY("Width", __sign, __name##_Width), \ + GB_PROPERTY("Height", __sign, __name##_Height), \ + GB_PROPERTY("Left", __sign, __name##_Left), \ + GB_PROPERTY("Top", __sign, __name##_Top), \ + GB_PROPERTY("Right", __sign, __name##_Right), \ + GB_PROPERTY("Bottom", __sign, __name##_Bottom), \ + \ + GB_METHOD("Clear", NULL, __name##_Clear, NULL), \ + GB_METHOD("IsVoid", "b", __name##_IsVoid, NULL), \ + GB_METHOD("Copy", #__name, __name##_Copy, NULL), \ + GB_METHOD("Move", NULL, __name##_Move, "(X)" __sign "(Y)" __sign "[(Width)" __sign "(Height)" __sign "]"), \ + GB_METHOD("Resize", NULL, __name##_Resize, "(Width)" __sign "(Height)" __sign ""), \ + GB_METHOD("Translate", NULL, __name##_Translate, "(DX)" __sign "(DY)" __sign ""), \ + GB_METHOD("Union", #__name, __name##_Union, "(Rect)" #__name ";"), \ + GB_METHOD("Intersection", #__name, __name##_Intersection, "(Rect)" #__name ";"), \ + GB_METHOD("Contains", "b", __name##_Contains, "(X)" __sign "(Y)" __sign ""), \ + GB_METHOD("Adjust", NULL, __name##_Shrink, "(Left)" __sign "[(Top)" __sign "(Right)" __sign "(Bottom)" __sign "]"), \ + GB_METHOD("Expand", NULL, __name##_Expand, "(Left)" __sign "[(Top)" __sign "(Right)" __sign "(Bottom)" __sign "]"), \ + GB_METHOD("Shrink", NULL, __name##_Shrink, "(Left)" __sign "[(Top)" __sign "(Right)" __sign "(Bottom)" __sign "]"), \ + GB_METHOD("Center", #__pname, __name##_Center, NULL), \ + \ + GB_STATIC_METHOD("Stretch", #__name, __name##_Stretch, "(Width)" __sign "(Height)" __sign "(Frame)" #__name ";[(Alignment)i]"), \ + \ + GB_END_DECLARE \ +}; diff --git a/main/lib/geom/gb.geom.component b/main/lib/geom/gb.geom.component new file mode 100644 index 00000000..23a4d791 --- /dev/null +++ b/main/lib/geom/gb.geom.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Finished diff --git a/main/lib/geom/gb.geom.h b/main/lib/geom/gb.geom.h new file mode 100644 index 00000000..7a166e36 --- /dev/null +++ b/main/lib/geom/gb.geom.h @@ -0,0 +1,105 @@ +/*************************************************************************** + + gb.geom.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_GEOM_H +#define __GB_GEOM_H + +#include "gambas.h" + +enum +{ + ALIGN_NORMAL = 0x00, + ALIGN_LEFT = 0x01, + ALIGN_RIGHT = 0x02, + ALIGN_CENTER = 0x03, + ALIGN_TOP_NORMAL = 0x10, + ALIGN_TOP_LEFT = 0x11, + ALIGN_TOP_RIGHT = 0x12, + ALIGN_TOP = 0x13, + ALIGN_BOTTOM_NORMAL = 0x20, + ALIGN_BOTTOM_LEFT = 0x21, + ALIGN_BOTTOM_RIGHT = 0x22, + ALIGN_BOTTOM = 0x23, + ALIGN_JUSTIFY = 0x04, +}; + +#define ALIGN_IS_TOP(_align) (((_align) & 0xF0) == 0x10) +#define ALIGN_IS_BOTTOM(_align) (((_align) & 0xF0) == 0x20) +#define ALIGN_IS_MIDDLE(_align) (((_align) & 0xF0) == 0x00) +#define ALIGN_IS_LEFT(_align) (((_align) & 0xF) == 0x1 || (((_align) & 0xF) == 0x0 && !GB.System.IsRightToLeft())) +#define ALIGN_IS_RIGHT(_align) (((_align) & 0xF) == 0x2 || (((_align) & 0xF) == 0x0 && GB.System.IsRightToLeft())) +#define ALIGN_IS_CENTER(_align) (((_align) & 0xF) == 0x3) +#define ALIGN_IS_NORMAL(_align) (((_align) & 0xF) == 0x0) +#define ALIGN_MAKE(_halign, _valign) (((_halign) & 0xF) | ((_valign) & 0xF0)) + +typedef + struct { + GB_BASE ob; + int x; + int y; + } + GEOM_POINT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + } + GEOM_POINTF; + +typedef + struct { + GB_BASE ob; + int x; + int y; + int w; + int h; + } + GEOM_RECT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + double w; + double h; + } + GEOM_RECTF; + +#define GEOM_INTERFACE_VERSION 1 + +typedef + struct { + int version; + GEOM_POINT *(*CreatePoint)(int x, int y); + GEOM_POINTF *(*CreatePointF)(double x, double y); + GEOM_RECT *(*CreateRect)(void); + GEOM_RECTF *(*CreateRectF)(void); + } + GEOM_INTERFACE; + +#endif + + diff --git a/main/lib/geom/main.c b/main/lib/geom/main.c new file mode 100644 index 00000000..5b3c8faf --- /dev/null +++ b/main/lib/geom/main.c @@ -0,0 +1,67 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "cpoint.h" +#include "crect.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +void *GB_GEOM_1[] EXPORT = { + + (void *)1, + (void *)CPOINT_create, + (void *)CPOINTF_create, + (void *)CRECT_create, + (void *)CRECTF_create, + NULL + }; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + PointDesc, + PointFDesc, + RectDesc, + RectFDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/geom/main.h b/main/lib/geom/main.h new file mode 100644 index 00000000..45233c83 --- /dev/null +++ b/main/lib/geom/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.geom.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif /* __MAIN_H */ diff --git a/main/lib/gui.opengl/Makefile.am b/main/lib/gui.opengl/Makefile.am new file mode 100644 index 00000000..ce29f239 --- /dev/null +++ b/main/lib/gui.opengl/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.gui.opengl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.opengl.la + +gb_gui_opengl_la_LIBADD = +gb_gui_opengl_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_opengl_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_opengl_la_SOURCES = main.h main.c + diff --git a/main/lib/gui.opengl/gb.gui.opengl.component b/main/lib/gui.opengl/gb.gui.opengl.component new file mode 100644 index 00000000..65350c19 --- /dev/null +++ b/main/lib/gui.opengl/gb.gui.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.gui,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Finished diff --git a/main/lib/gui.opengl/main.c b/main/lib/gui.opengl/main.c new file mode 100644 index 00000000..81466fee --- /dev/null +++ b/main/lib/gui.opengl/main.c @@ -0,0 +1,75 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4.opengl|gb.qt5.opengl|gb.gtk.opengl|gb.gtk3.opengl"; + +int EXPORT GB_INIT(void) +{ + const char *comp = NULL; + char *env; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + comp = "gb.qt4.opengl"; + else if (strcmp(env, "gb.qt5") == 0) + comp = "gb.qt5.opengl"; + else if (strcmp(env, "gb.gtk") == 0) + comp = "gb.gtk.opengl"; + else if (strcmp(env, "gb.gtk3") == 0) + comp = "gb.gtk3.opengl"; + } + + if (!comp) + { + // GB_GUI should be set by gb.gui + if (!env) + fprintf(stderr, "gb.gui.opengl: error: no component specified in GB_GUI environment variable\n"); + else + fprintf(stderr, "gb.gui.opengl: error: unsupported component specified in GB_GUI environment variable\n"); + exit(1); + } + + if (GB.Component.Load(comp)) + fprintf(stderr, "gb.gui.opengl: unable to load '%s' component\n", comp); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.opengl/main.h b/main/lib/gui.opengl/main.h new file mode 100644 index 00000000..fa8be1b0 --- /dev/null +++ b/main/lib/gui.opengl/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui.qt.opengl/Makefile.am b/main/lib/gui.qt.opengl/Makefile.am new file mode 100644 index 00000000..ead52ad2 --- /dev/null +++ b/main/lib/gui.qt.opengl/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui.qt.opengl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.qt.opengl.la + +gb_gui_qt_opengl_la_LIBADD = +gb_gui_qt_opengl_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_qt_opengl_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_qt_opengl_la_SOURCES = \ + main.h main.c + diff --git a/main/lib/gui.qt.opengl/gb.gui.qt.opengl.component b/main/lib/gui.qt.opengl/gb.gui.qt.opengl.component new file mode 100644 index 00000000..5f12979e --- /dev/null +++ b/main/lib/gui.qt.opengl/gb.gui.qt.opengl.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +Requires=gb.gui.qt +State=Finished diff --git a/main/lib/gui.qt.opengl/main.c b/main/lib/gui.qt.opengl/main.c new file mode 100644 index 00000000..8d4bd219 --- /dev/null +++ b/main/lib/gui.qt.opengl/main.c @@ -0,0 +1,71 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4.opengl|gb.qt5.opengl"; + +int EXPORT GB_INIT(void) +{ + const char *comp = NULL; + char *env; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + comp = "gb.qt4.opengl"; + else if (strcmp(env, "gb.qt5") == 0) + comp = "gb.qt5.opengl"; + } + + if (!comp) + { + // GB_GUI should be set by gb.gui + if (!env || !*env) + fprintf(stderr, "gb.gui.qt.opengl: error: no component specified in GB_GUI environment variable"); + else + fprintf(stderr, "gb.gui.qt.opengl: error: unsupported component specified in GB_GUI environment variable"); + exit(1); + } + + if (GB.Component.Load(comp)) + fprintf(stderr, "gb.gui.qt.opengl: unable to load '%s' component\n", comp); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.qt.opengl/main.h b/main/lib/gui.qt.opengl/main.h new file mode 100644 index 00000000..fa8be1b0 --- /dev/null +++ b/main/lib/gui.qt.opengl/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui.qt.webkit/Makefile.am b/main/lib/gui.qt.webkit/Makefile.am new file mode 100644 index 00000000..cfe0189c --- /dev/null +++ b/main/lib/gui.qt.webkit/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui.qt.webkit +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.qt.webkit.la + +gb_gui_qt_webkit_la_LIBADD = +gb_gui_qt_webkit_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_qt_webkit_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_qt_webkit_la_SOURCES = \ + main.h main.c + diff --git a/main/lib/gui.qt.webkit/gb.gui.qt.webkit.component b/main/lib/gui.qt.webkit/gb.gui.qt.webkit.component new file mode 100644 index 00000000..5f12979e --- /dev/null +++ b/main/lib/gui.qt.webkit/gb.gui.qt.webkit.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +Requires=gb.gui.qt +State=Finished diff --git a/main/lib/gui.qt.webkit/main.c b/main/lib/gui.qt.webkit/main.c new file mode 100644 index 00000000..ecf05913 --- /dev/null +++ b/main/lib/gui.qt.webkit/main.c @@ -0,0 +1,71 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4.webkit|gb.qt5.webkit"; + +int EXPORT GB_INIT(void) +{ + const char *comp = NULL; + char *env; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + comp = "gb.qt4.webkit"; + else if (strcmp(env, "gb.qt5") == 0) + comp = "gb.qt5.webkit"; + } + + if (!comp) + { + // GB_GUI should be set by gb.gui + if (!env || !*env) + fprintf(stderr, "gb.gui.qt.webkit: error: no component specified in GB_GUI environment variable"); + else + fprintf(stderr, "gb.gui.qt.webkit: error: unsupported component specified in GB_GUI environment variable"); + exit(1); + } + + if (GB.Component.Load(comp)) + fprintf(stderr, "gb.gui.qt.webkit: unable to load '%s' component\n", comp); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.qt.webkit/main.h b/main/lib/gui.qt.webkit/main.h new file mode 100644 index 00000000..fa8be1b0 --- /dev/null +++ b/main/lib/gui.qt.webkit/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui.qt/Makefile.am b/main/lib/gui.qt/Makefile.am new file mode 100644 index 00000000..28b0f89e --- /dev/null +++ b/main/lib/gui.qt/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui.qt +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.qt.la + +gb_gui_qt_la_LIBADD = +gb_gui_qt_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_qt_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_qt_la_SOURCES = \ + main.h main.c gb_gui_test_temp.h + diff --git a/main/lib/gui.qt/gb.gui.qt.component b/main/lib/gui.qt/gb.gui.qt.component new file mode 100644 index 00000000..fdcc7ade --- /dev/null +++ b/main/lib/gui.qt/gb.gui.qt.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form +State=Finished \ No newline at end of file diff --git a/main/lib/gui.qt/gb_gui_test_temp.h b/main/lib/gui.qt/gb_gui_test_temp.h new file mode 120000 index 00000000..d5d480a2 --- /dev/null +++ b/main/lib/gui.qt/gb_gui_test_temp.h @@ -0,0 +1 @@ +../gui/gb_gui_test_temp.h \ No newline at end of file diff --git a/main/lib/gui.qt/main.c b/main/lib/gui.qt/main.c new file mode 100644 index 00000000..9bba0bac --- /dev/null +++ b/main/lib/gui.qt/main.c @@ -0,0 +1,132 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +enum { USE_NOTHING, USE_GB_QT4, USE_GB_QT5 }; + +const GB_INTERFACE *GB_PTR EXPORT; + +// Prevents gbi3 from complaining + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4|gb.qt5"; + +static bool _debug = FALSE; + + +const char *get_name(int use) +{ + switch (use) + { + case USE_GB_QT4: return "gb.qt4"; + default: return "gb.qt5"; + } +} + +#include "gb_gui_test_temp.h" + +int EXPORT GB_INIT(void) +{ + int use = USE_NOTHING; + int use_other = USE_NOTHING; + char *env; + const char *comp; + const char *fail; + char not_found[32]; + + env = getenv("GB_GUI"); + if (env && *env) + { + if (strcmp(env, "gb.qt4") == 0) + use = USE_GB_QT4; + else if (strcmp(env, "gb.qt5") == 0) + use = USE_GB_QT5; + else + fprintf(stderr, "gb.gui.qt: warning: '%s' component not supported\n", env); + } + + env = getenv("GB_GUI_DEBUG"); + if (env && strcmp(env, "0")) + _debug = TRUE; + + if (use == USE_NOTHING) + { + use = GUI_should_use(); + if (use == USE_NOTHING) + use = USE_GB_QT5; + } + + if (_debug) + fprintf(stderr, "gb.gui: checking %s...\n", get_name(use)); + + fail = GUI_can_use(use); + if (fail) + { + strcpy(not_found, fail); + + if (use == USE_GB_QT4) + use_other = USE_GB_QT5; + else + use_other = USE_GB_QT4; + + if (!GUI_can_use(use_other)) + { + fprintf(stderr, "gb.gui.qt: warning: '%s' component not found, using '%s' instead\n", not_found, get_name(use_other)); + use = use_other; + } + else + { + fprintf(stderr, "gb.gui.qt: error: '%s' component not found, unable to find any QT replacement component\n", not_found); + exit(1); + } + } + + comp = get_name(use); + + if (GB.Component.Load(comp)) + { + fprintf(stderr, "gb.gui.qt: error: cannot load component '%s'\n", comp); + exit(1); + } + else + { + if (_debug) + fprintf(stderr, "gb.gui.qt: loading '%s'\n", comp); + } + + setenv("GB_GUI", comp, TRUE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/gui.qt/main.h b/main/lib/gui.qt/main.h new file mode 100644 index 00000000..6425d51a --- /dev/null +++ b/main/lib/gui.qt/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif diff --git a/main/lib/gui.trayicon/Makefile.am b/main/lib/gui.trayicon/Makefile.am new file mode 100644 index 00000000..e4e49490 --- /dev/null +++ b/main/lib/gui.trayicon/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.gui.trayicon +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.trayicon.la + +gb_gui_trayicon_la_LIBADD = +gb_gui_trayicon_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_trayicon_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_gui_trayicon_la_SOURCES = \ + cfaketrayicon.h cfaketrayicon.c \ + main.h main.c + diff --git a/main/lib/gui.trayicon/cfaketrayicon.c b/main/lib/gui.trayicon/cfaketrayicon.c new file mode 100644 index 00000000..9e9693e1 --- /dev/null +++ b/main/lib/gui.trayicon/cfaketrayicon.c @@ -0,0 +1,83 @@ +/*************************************************************************** + + cfaketrayicon.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFAKETRAYICON_CPP + +#include "gambas.h" +#include "main.h" + +#include "cfaketrayicon.h" + + +GB_DESC FakeTrayIconsDesc[] = +{ + GB_DECLARE_STATIC("TrayIcons"), + + GB_STATIC_METHOD("_next", "TrayIcon", NULL, NULL), + GB_STATIC_METHOD("_get", "TrayIcon", NULL, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", NULL), + GB_STATIC_METHOD("DeleteAll", NULL, NULL, NULL), + + GB_END_DECLARE +}; + + +GB_DESC FakeTrayIconDesc[] = +{ + GB_DECLARE("TrayIcon", 0), + + //GB_STATIC_METHOD("_exit", NULL, NULL, NULL), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_METHOD("_new", NULL, NULL, NULL), + GB_METHOD("_free", NULL, NULL, NULL), + + GB_METHOD("Show", NULL, NULL, NULL), + GB_METHOD("Hide", NULL, NULL, NULL), + GB_METHOD("Delete", NULL, NULL, NULL), + + GB_PROPERTY("Picture", "Picture", NULL), + GB_PROPERTY("Icon", "Picture", NULL), + GB_PROPERTY("Visible", "b", NULL), + + GB_PROPERTY("Text", "s", NULL), + GB_PROPERTY("PopupMenu", "s", NULL), + GB_PROPERTY("Tooltip", "s", NULL), + GB_PROPERTY("Tag", "v", NULL), + + GB_EVENT("Click", NULL, NULL, NULL), + GB_EVENT("MiddleClick", NULL, NULL, NULL), + GB_EVENT("Scroll", NULL, "(Delta)f(Orientation)i", NULL), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_Family", "s", "*"), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Special"), + GB_CONSTANT("_DefaultEvent", "s", "Click"), + GB_CONSTANT("_Properties", "s", "Visible=False,Tag,Tooltip,Picture{Picture:NoCache},PopupMenu{Menu}"), + + GB_END_DECLARE +}; + diff --git a/main/lib/gui.trayicon/cfaketrayicon.h b/main/lib/gui.trayicon/cfaketrayicon.h new file mode 100644 index 00000000..8d6289f9 --- /dev/null +++ b/main/lib/gui.trayicon/cfaketrayicon.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + ctrayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFAKETRAYICON_H +#define __CFAKETRAYICON_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CFAKETRAYICON_CPP +extern GB_DESC FakeTrayIconsDesc[]; +extern GB_DESC FakeTrayIconDesc[]; +#endif + +#endif diff --git a/main/lib/gui.trayicon/gb.gui.trayicon.component b/main/lib/gui.trayicon/gb.gui.trayicon.component new file mode 100644 index 00000000..6b4c404f --- /dev/null +++ b/main/lib/gui.trayicon/gb.gui.trayicon.component @@ -0,0 +1,5 @@ +[Component] +Author=Benoît Minisini +Requires=gb.dbus +Needs=Form +State=Finished \ No newline at end of file diff --git a/main/lib/gui.trayicon/main.c b/main/lib/gui.trayicon/main.c new file mode 100644 index 00000000..75a8676e --- /dev/null +++ b/main/lib/gui.trayicon/main.c @@ -0,0 +1,81 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "cfaketrayicon.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +// Prevents gbi3 from complaining + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +GB_DESC *GB_OPTIONAL_CLASSES[] EXPORT = +{ + FakeTrayIconDesc, FakeTrayIconsDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_AFTER_INIT(void) +{ + GB_FUNCTION func; + bool has_dbus_systemtray = FALSE; + void (*declare_tray_icon)(); + char *env; + + env = getenv("GB_GUI_TRAYICON_NO_DBUS"); + + if (env && *env == '1') + has_dbus_systemtray = FALSE; + else + { + GB.Component.Load("gb.dbus"); + + if (!GB.GetFunction(&func, (void *)GB.FindClass("DBus"), "_HasSystemTray", NULL, NULL)) + has_dbus_systemtray = GB.Call(&func, 0, FALSE)->_boolean.value; + } + + if (has_dbus_systemtray) + GB.Component.Load("gb.dbus.trayicon"); + else + { + GB.Component.GetInfo("DECLARE_TRAYICON", POINTER(&declare_tray_icon)); + (*declare_tray_icon)(); + } +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.trayicon/main.h b/main/lib/gui.trayicon/main.h new file mode 100644 index 00000000..fa8be1b0 --- /dev/null +++ b/main/lib/gui.trayicon/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui.webview/Makefile.am b/main/lib/gui.webview/Makefile.am new file mode 100644 index 00000000..1d6807f3 --- /dev/null +++ b/main/lib/gui.webview/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.gui.webview +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.webview.la + +gb_gui_webview_la_LIBADD = +gb_gui_webview_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_webview_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_webview_la_SOURCES = main.h main.c + diff --git a/main/lib/gui.webview/gb.gui.webview.component b/main/lib/gui.webview/gb.gui.webview.component new file mode 100644 index 00000000..3348c007 --- /dev/null +++ b/main/lib/gui.webview/gb.gui.webview.component @@ -0,0 +1,5 @@ +[Component] +Author=Benoît Minisini +Require=gb.gui +Type=Form +State=Finished diff --git a/main/lib/gui.webview/main.c b/main/lib/gui.webview/main.c new file mode 100644 index 00000000..d141c9cb --- /dev/null +++ b/main/lib/gui.webview/main.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + main.c + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt5.webview|gb.gtk3.webview|gb.qt4.webview"; + +int EXPORT GB_INIT(void) +{ + const char *comp = NULL; + char *env; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt5") == 0) + comp = "gb.qt5.webview"; + else if (strcmp(env, "gb.qt4") == 0) + comp = "gb.qt4.webview"; + else if (strcmp(env, "gb.gtk3") == 0) + comp = "gb.gtk3.webview"; + } + + if (!comp) + { + // GB_GUI should be set by gb.gui + if (!env || !*env) + fprintf(stderr, "gb.gui.webview: error: no component specified in GB_GUI environment variable\n"); + else + fprintf(stderr, "gb.gui.webview: error: unsupported component specified in GB_GUI environment variable\n"); + exit(1); + } + + if (GB.Component.Load(comp)) + fprintf(stderr, "gb.gui.webview: unable to load '%s' component\n", comp); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.webview/main.h b/main/lib/gui.webview/main.h new file mode 100644 index 00000000..d8f74aaf --- /dev/null +++ b/main/lib/gui.webview/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui/Makefile.am b/main/lib/gui/Makefile.am new file mode 100644 index 00000000..1bc2ea26 --- /dev/null +++ b/main/lib/gui/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.la + +gb_gui_la_LIBADD = +gb_gui_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_la_SOURCES = \ + main.h main.c gb_gui_test_temp.h + diff --git a/main/lib/gui/gb.gui.component b/main/lib/gui/gb.gui.component new file mode 100644 index 00000000..45c1ae57 --- /dev/null +++ b/main/lib/gui/gb.gui.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form +State=Finished diff --git a/main/lib/gui/gb_gui_test_temp.h b/main/lib/gui/gb_gui_test_temp.h new file mode 100644 index 00000000..ea34ad14 --- /dev/null +++ b/main/lib/gui/gb_gui_test_temp.h @@ -0,0 +1,85 @@ +/*************************************************************************** + + gb_gui_test_temp.h + + (c) 2000-2020 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_GUI_TEST_TEMP_H + +static char *make_name(const char *prefix, const char *suffix) +{ + static char buffer[32]; + + snprintf(buffer, sizeof(buffer), "%s.%s", prefix, suffix); + return buffer; +} + +static const char *GUI_can_use(int use) +{ + static const char *ext[] = { "ext", "webkit", "opengl", "webview", NULL }; + const char **pext; + const char *name; + char *child; + + name = get_name(use); + + if (!GB.Component.CanLoadLibrary(name)) + return name; + + for (pext = ext; *pext; pext++) + { + if (!GB.Component.Exist(make_name("gb.gui", *pext))) + continue; + + child = make_name(name, *pext); + if (!GB.Component.CanLoadLibrary(child)) + return child; + if (_debug) + fprintf(stderr, " %s OK\n", child); + } + + return NULL; +} + +static int GUI_should_use() +{ + const char *env = getenv("XDG_CURRENT_DESKTOP"); + + if (env && strncasecmp(env, "KDE", 3) == 0) + { + env = getenv("KDE_FULL_SESSION"); + + if (env && !strcmp(env, "true")) + { + env = getenv("KDE_SESSION_VERSION"); + if (env) + { + if (strcmp(env, "4") == 0) + return USE_GB_QT4; + else if (strcmp(env, "5") == 0) + return USE_GB_QT5; + } + } + } + + return USE_NOTHING; +} + + diff --git a/main/lib/gui/main.c b/main/lib/gui/main.c new file mode 100644 index 00000000..4c90be1f --- /dev/null +++ b/main/lib/gui/main.c @@ -0,0 +1,152 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +enum { USE_NOTHING, USE_GB_QT4, USE_GB_QT5, USE_GB_GTK, USE_GB_GTK3 }; + +static char use_list[4][3] = { + { USE_GB_QT5, USE_GB_GTK, USE_GB_GTK3 }, + { USE_GB_QT4, USE_GB_GTK3, USE_GB_GTK }, + { USE_GB_GTK3, USE_GB_QT4, USE_GB_QT5 }, + { USE_GB_GTK, USE_GB_QT5, USE_GB_QT4 }, +}; + +GB_INTERFACE GB EXPORT; + +// Prevents gbi3 from complaining + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4|gb.qt5|gb.gtk|gb.gtk3"; + +static bool _debug = FALSE; + + +const char *get_name(int use) +{ + switch (use) + { + case USE_GB_QT4: return "gb.qt4"; + case USE_GB_QT5: return "gb.qt5"; + case USE_GB_GTK3: return "gb.gtk3"; + default: return "gb.gtk"; + } +} + +#include "gb_gui_test_temp.h" + +int EXPORT GB_INIT(void) +{ + int use = USE_NOTHING; + int use_other = USE_NOTHING; + char *env; + const char *comp; + int i; + const char *fail; + char not_found[32]; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + use = USE_GB_QT4; + else if (strcmp(env, "gb.qt5") == 0) + use = USE_GB_QT5; + else if (strcmp(env, "gb.gtk") == 0) + use = USE_GB_GTK; + else if (strcmp(env, "gb.gtk3") == 0) + use = USE_GB_GTK3; + } + + env = getenv("GB_GUI_DEBUG"); + if (env && strcmp(env, "0")) + _debug = TRUE; + + if (use == USE_NOTHING) + { + use = GUI_should_use(); + if (use == USE_NOTHING) + use = USE_GB_GTK3; + } + + if (_debug) + fprintf(stderr, "gb.gui: checking %s...\n", get_name(use)); + + fail = GUI_can_use(use); + if (fail) + { + strcpy(not_found, fail); + use_other = USE_NOTHING; + for (i = 0; i <= 2; i++) + { + if (_debug) + fprintf(stderr, "gb.gui: checking %s...\n", get_name(use_list[use - 1][i])); + + if (!GUI_can_use(use_list[use - 1][i])) + { + use_other = use_list[use - 1][i]; + break; + } + } + + if (use_other) + { + fprintf(stderr, "gb.gui: warning: '%s' component not found, using '%s' instead\n", not_found, get_name(use_other)); + use = use_other; + } + else + { + fprintf(stderr, "gb.gui: error: '%s' component not found, unable to find any GUI replacement component\n", not_found); + exit(1); + } + } + + comp = get_name(use); + + if (GB.Component.Load(comp)) + { + fprintf(stderr, "gb.gui: error: cannot load component '%s'\n", comp); + exit(1); + } + else + { + if (_debug) + fprintf(stderr, "gb.gui: loading '%s'\n", comp); + } + + setenv("GB_GUI", comp, TRUE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui/main.h b/main/lib/gui/main.h new file mode 100644 index 00000000..fa8be1b0 --- /dev/null +++ b/main/lib/gui/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/hash/Makefile.am b/main/lib/hash/Makefile.am new file mode 100644 index 00000000..39590ffa --- /dev/null +++ b/main/lib/hash/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.hash +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.hash.la + +gb_hash_la_LDFLAGS = -module @LD_FLAGS@ +gb_hash_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_hash_la_SOURCES = \ + main.h main.c \ + hash.h hash.c platform.h \ + c_hash.h c_hash.c + + diff --git a/main/lib/hash/c_hash.c b/main/lib/hash/c_hash.c new file mode 100644 index 00000000..9e2698ba --- /dev/null +++ b/main/lib/hash/c_hash.c @@ -0,0 +1,75 @@ +/*************************************************************************** + + c_hash.c + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_HASH_C + +#include "main.h" +#include "c_hash.h" + +static void do_hash(int algo, const char *data, int len) +{ + HASH_begin(algo); + HASH_process(data, len); + GB.ReturnString(HASH_end()); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Hash_Md5, GB_STRING data) + + do_hash(HASH_MD5, STRING(data), LENGTH(data)); + +END_METHOD + +BEGIN_METHOD(Hash_Sha1, GB_STRING data) + + do_hash(HASH_SHA1, STRING(data), LENGTH(data)); + +END_METHOD + +BEGIN_METHOD(Hash_Sha256, GB_STRING data) + + do_hash(HASH_SHA256, STRING(data), LENGTH(data)); + +END_METHOD + +BEGIN_METHOD(Hash_Sha512, GB_STRING data) + + do_hash(HASH_SHA512, STRING(data), LENGTH(data)); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC HashDesc[] = +{ + GB_DECLARE_STATIC("Hash"), + + GB_STATIC_METHOD("Md5", "s", Hash_Md5, "(Data)s"), + GB_STATIC_METHOD("Sha1", "s", Hash_Sha1, "(Data)s"), + GB_STATIC_METHOD("Sha256", "s", Hash_Sha256, "(Data)s"), + GB_STATIC_METHOD("Sha512", "s", Hash_Sha512, "(Data)s"), + + GB_END_DECLARE +}; + diff --git a/main/lib/hash/c_hash.h b/main/lib/hash/c_hash.h new file mode 100644 index 00000000..f6810872 --- /dev/null +++ b/main/lib/hash/c_hash.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + c_hash.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_HASH_H +#define __C_HASH_H + +#include "gambas.h" + +#ifndef __C_HASH_C +extern GB_DESC HashDesc[]; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/hash/gb.hash.component b/main/lib/hash/gb.hash.component new file mode 100644 index 00000000..26cd84f1 --- /dev/null +++ b/main/lib/hash/gb.hash.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.component +Author=Benoît Minisini + diff --git a/main/lib/hash/gb.hash.h b/main/lib/hash/gb.hash.h new file mode 100644 index 00000000..22f44057 --- /dev/null +++ b/main/lib/hash/gb.hash.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + gb.hash.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_HASH_H +#define __GB_HASH_H + +#include "gambas.h" + +enum +{ + HASH_MD5, + HASH_SHA1, + HASH_SHA256, + HASH_SHA512 +}; + +#define HASH_INTERFACE_VERSION 1 + +typedef + struct { + int version; + bool (*Begin)(int hash); + void (*Process)(const void *buffer, size_t len); + char *(*End)(); + } + HASH_INTERFACE; + +#endif + + + diff --git a/main/lib/hash/hash.c b/main/lib/hash/hash.c new file mode 100644 index 00000000..66b6fa6a --- /dev/null +++ b/main/lib/hash/hash.c @@ -0,0 +1,1506 @@ +/*************************************************************************** + + hash.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + * Utility routines. + * + * Copyright (C) 2010 Denys Vlasenko + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#define __HASH_C + +#include "hash.h" +#include "platform.h" + +#define NEED_SHA512 1 //(ENABLE_SHA512SUM || ENABLE_USE_BB_CRYPT_SHA) +#define CONFIG_MD5_SMALL 0 +#define CONFIG_SHA3_SMALL 0 + +#if !(ULONG_MAX > 0xffffffff) +static uint64_t bb_bswap_64(uint64_t x) +{ + return bswap_64(x); +} +#endif + +/* gcc 4.2.1 optimizes rotr64 better with inline than with macro + * (for rotX32, there is no difference). Why? My guess is that + * macro requires clever common subexpression elimination heuristics + * in gcc, while inline basically forces it to happen. + */ +//#define rotl32(x,n) (((x) << (n)) | ((x) >> (32 - (n)))) +static ALWAYS_INLINE uint32_t rotl32(uint32_t x, unsigned n) +{ + return (x << n) | (x >> (32 - n)); +} +//#define rotr32(x,n) (((x) >> (n)) | ((x) << (32 - (n)))) +static ALWAYS_INLINE uint32_t rotr32(uint32_t x, unsigned n) +{ + return (x >> n) | (x << (32 - n)); +} +/* rotr64 in needed for sha512 only: */ +//#define rotr64(x,n) (((x) >> (n)) | ((x) << (64 - (n)))) +static ALWAYS_INLINE uint64_t rotr64(uint64_t x, unsigned n) +{ + return (x >> n) | (x << (64 - n)); +} + +/* rotl64 only used for sha3 currently */ +static ALWAYS_INLINE uint64_t rotl64(uint64_t x, unsigned n) +{ + return (x << n) | (x >> (64 - n)); +} + +/* Process the remaining bytes in the buffer */ +static void common64_end(md5_ctx_t *ctx, int swap_needed) +{ + unsigned bufpos = ctx->total64 & 63; + /* Pad the buffer to the next 64-byte boundary with 0x80,0,0,0... */ + ctx->wbuffer[bufpos++] = 0x80; + + /* This loop iterates either once or twice, no more, no less */ + while (1) { + unsigned remaining = 64 - bufpos; + memset(ctx->wbuffer + bufpos, 0, remaining); + /* Do we have enough space for the length count? */ + if (remaining >= 8) { + /* Store the 64-bit counter of bits in the buffer */ + uint64_t t = ctx->total64 << 3; + if (swap_needed) + t = bb_bswap_64(t); + /* wbuffer is suitably aligned for this */ + *(bb__aliased_uint64_t *) (&ctx->wbuffer[64 - 8]) = t; + } + ctx->process_block(ctx); + if (remaining >= 8) + break; + bufpos = 0; + } +} + + +/* + * Compute MD5 checksum of strings according to the + * definition of MD5 in RFC 1321 from April 1992. + * + * Written by Ulrich Drepper , 1995. + * + * Copyright (C) 1995-1999 Free Software Foundation, Inc. + * Copyright (C) 2001 Manuel Novoa III + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003 Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* 0: fastest, 3: smallest */ +#if CONFIG_MD5_SMALL < 0 +# define MD5_SMALL 0 +#elif CONFIG_MD5_SMALL > 3 +# define MD5_SMALL 3 +#else +# define MD5_SMALL CONFIG_MD5_SMALL +#endif + +/* These are the four functions used in the four steps of the MD5 algorithm + * and defined in the RFC 1321. The first function is a little bit optimized + * (as found in Colin Plumbs public domain implementation). + * #define FF(b, c, d) ((b & c) | (~b & d)) + */ +#undef FF +#undef FG +#undef FH +#undef FI +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF(d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Hash a single block, 64 bytes long and 4-byte aligned */ +static void md5_process_block64(md5_ctx_t *ctx) +{ +#if MD5_SMALL > 0 + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + T[i] = (int)(2^32 * fabs(sin(i))), i=1..64 + */ + static const uint32_t C_array[] ALIGN4 = { + /* round 1 */ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + /* round 2 */ + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + /* round 3 */ + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + /* round 4 */ + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + static const char P_array[] ALIGN1 = { +# if MD5_SMALL > 1 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ +# endif + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ + }; +#endif + uint32_t *words = (void*) ctx->wbuffer; + uint32_t A = ctx->hash[0]; + uint32_t B = ctx->hash[1]; + uint32_t C = ctx->hash[2]; + uint32_t D = ctx->hash[3]; + +#if MD5_SMALL >= 2 /* 2 or 3 */ + + static const char S_array[] ALIGN1 = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; + const uint32_t *pc; + const char *pp; + const char *ps; + int i; + uint32_t temp; + + if (BB_BIG_ENDIAN) + for (i = 0; i < 16; i++) + words[i] = SWAP_LE32(words[i]); + +# if MD5_SMALL == 3 + pc = C_array; + pp = P_array; + ps = S_array - 4; + + for (i = 0; i < 64; i++) { + if ((i & 0x0f) == 0) + ps += 4; + temp = A; + switch (i >> 4) { + case 0: + temp += FF(B, C, D); + break; + case 1: + temp += FG(B, C, D); + break; + case 2: + temp += FH(B, C, D); + break; + default: /* case 3 */ + temp += FI(B, C, D); + } + temp += words[(int) (*pp++)] + *pc++; + temp = rotl32(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } +# else /* MD5_SMALL == 2 */ + pc = C_array; + pp = P_array; + ps = S_array; + + for (i = 0; i < 16; i++) { + temp = A + FF(B, C, D) + words[(int) (*pp++)] + *pc++; + temp = rotl32(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FG(B, C, D) + words[(int) (*pp++)] + *pc++; + temp = rotl32(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FH(B, C, D) + words[(int) (*pp++)] + *pc++; + temp = rotl32(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FI(B, C, D) + words[(int) (*pp++)] + *pc++; + temp = rotl32(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } +# endif + /* Add checksum to the starting values */ + ctx->hash[0] += A; + ctx->hash[1] += B; + ctx->hash[2] += C; + ctx->hash[3] += D; + +#else /* MD5_SMALL == 0 or 1 */ + +# if MD5_SMALL == 1 + const uint32_t *pc; + const char *pp; + int i; +# endif + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithm's processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we save swapped words in WORDS array. */ +# undef OP +# define OP(a, b, c, d, s, T) \ + do { \ + a += FF(b, c, d) + (*words IF_BIG_ENDIAN(= SWAP_LE32(*words))) + T; \ + words++; \ + a = rotl32(a, s); \ + a += b; \ + } while (0) + + /* Round 1 */ +# if MD5_SMALL == 1 + pc = C_array; + for (i = 0; i < 4; i++) { + OP(A, B, C, D, 7, *pc++); + OP(D, A, B, C, 12, *pc++); + OP(C, D, A, B, 17, *pc++); + OP(B, C, D, A, 22, *pc++); + } +# else + OP(A, B, C, D, 7, 0xd76aa478); + OP(D, A, B, C, 12, 0xe8c7b756); + OP(C, D, A, B, 17, 0x242070db); + OP(B, C, D, A, 22, 0xc1bdceee); + OP(A, B, C, D, 7, 0xf57c0faf); + OP(D, A, B, C, 12, 0x4787c62a); + OP(C, D, A, B, 17, 0xa8304613); + OP(B, C, D, A, 22, 0xfd469501); + OP(A, B, C, D, 7, 0x698098d8); + OP(D, A, B, C, 12, 0x8b44f7af); + OP(C, D, A, B, 17, 0xffff5bb1); + OP(B, C, D, A, 22, 0x895cd7be); + OP(A, B, C, D, 7, 0x6b901122); + OP(D, A, B, C, 12, 0xfd987193); + OP(C, D, A, B, 17, 0xa679438e); + OP(B, C, D, A, 22, 0x49b40821); +# endif + words -= 16; + + /* For the second to fourth round we have the possibly swapped words + in WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +# undef OP +# define OP(f, a, b, c, d, k, s, T) \ + do { \ + a += f(b, c, d) + words[k] + T; \ + a = rotl32(a, s); \ + a += b; \ + } while (0) + + /* Round 2 */ +# if MD5_SMALL == 1 + pp = P_array; + for (i = 0; i < 4; i++) { + OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++); + OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++); + OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++); + OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++); + } +# else + OP(FG, A, B, C, D, 1, 5, 0xf61e2562); + OP(FG, D, A, B, C, 6, 9, 0xc040b340); + OP(FG, C, D, A, B, 11, 14, 0x265e5a51); + OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP(FG, A, B, C, D, 5, 5, 0xd62f105d); + OP(FG, D, A, B, C, 10, 9, 0x02441453); + OP(FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP(FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP(FG, D, A, B, C, 14, 9, 0xc33707d6); + OP(FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP(FG, B, C, D, A, 8, 20, 0x455a14ed); + OP(FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP(FG, C, D, A, B, 7, 14, 0x676f02d9); + OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a); +# endif + + /* Round 3 */ +# if MD5_SMALL == 1 + for (i = 0; i < 4; i++) { + OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++); + OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++); + OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++); + OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++); + } +# else + OP(FH, A, B, C, D, 5, 4, 0xfffa3942); + OP(FH, D, A, B, C, 8, 11, 0x8771f681); + OP(FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP(FH, B, C, D, A, 14, 23, 0xfde5380c); + OP(FH, A, B, C, D, 1, 4, 0xa4beea44); + OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP(FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP(FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP(FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP(FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP(FH, B, C, D, A, 6, 23, 0x04881d05); + OP(FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP(FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP(FH, B, C, D, A, 2, 23, 0xc4ac5665); +# endif + + /* Round 4 */ +# if MD5_SMALL == 1 + for (i = 0; i < 4; i++) { + OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++); + OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++); + OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++); + OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++); + } +# else + OP(FI, A, B, C, D, 0, 6, 0xf4292244); + OP(FI, D, A, B, C, 7, 10, 0x432aff97); + OP(FI, C, D, A, B, 14, 15, 0xab9423a7); + OP(FI, B, C, D, A, 5, 21, 0xfc93a039); + OP(FI, A, B, C, D, 12, 6, 0x655b59c3); + OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP(FI, C, D, A, B, 10, 15, 0xffeff47d); + OP(FI, B, C, D, A, 1, 21, 0x85845dd1); + OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP(FI, C, D, A, B, 6, 15, 0xa3014314); + OP(FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP(FI, A, B, C, D, 4, 6, 0xf7537e82); + OP(FI, D, A, B, C, 11, 10, 0xbd3af235); + OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP(FI, B, C, D, A, 9, 21, 0xeb86d391); +# undef OP +# endif + /* Add checksum to the starting values */ + ctx->hash[0] += A; + ctx->hash[1] += B; + ctx->hash[2] += C; + ctx->hash[3] += D; +#endif +} +#undef FF +#undef FG +#undef FH +#undef FI + +/* Initialize structure containing state of computation. + * (RFC 1321, 3.3: Step 3) + */ +void md5_begin(md5_ctx_t *ctx) +{ + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->total64 = 0; + ctx->process_block = md5_process_block64; +} + +/* Used also for sha1 and sha256 */ +void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) +{ + unsigned bufpos = ctx->total64 & 63; + + ctx->total64 += len; + + while (1) { + unsigned remaining = 64 - bufpos; + if (remaining > len) + remaining = len; + /* Copy data into aligned buffer */ + memcpy(ctx->wbuffer + bufpos, buffer, remaining); + len -= remaining; + buffer = (const char *)buffer + remaining; + bufpos += remaining; + + /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ + bufpos -= 64; + if (bufpos != 0) + break; + + /* Buffer is filled up, process it */ + ctx->process_block(ctx); + /*bufpos = 0; - already is */ + } +} + +/* Process the remaining bytes in the buffer and put result from CTX + * in first 16 bytes following RESBUF. The result is always in little + * endian byte order, so that a byte-wise output yields to the wanted + * ASCII representation of the message digest. + */ +unsigned md5_end(md5_ctx_t *ctx, void *resbuf) +{ + /* MD5 stores total in LE, need to swap on BE arches: */ + common64_end(ctx, /*swap_needed:*/ BB_BIG_ENDIAN); + + /* The MD5 result is in little endian byte order */ + if (BB_BIG_ENDIAN) { + ctx->hash[0] = SWAP_LE32(ctx->hash[0]); + ctx->hash[1] = SWAP_LE32(ctx->hash[1]); + ctx->hash[2] = SWAP_LE32(ctx->hash[2]); + ctx->hash[3] = SWAP_LE32(ctx->hash[3]); + } + + memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * 4); + return sizeof(ctx->hash[0]) * 4; +} + + +/* + * SHA1 part is: + * Copyright 2007 Rob Landley + * + * Based on the public domain SHA-1 in C by Steve Reid + * from http://www.mirrors.wiretapped.net/security/cryptography/hashes/sha1/ + * + * Licensed under GPLv2, see file LICENSE in this source tree. + * + * --------------------------------------------------------------------------- + * + * SHA256 and SHA512 parts are: + * Released into the Public Domain by Ulrich Drepper . + * Shrank by Denys Vlasenko. + * + * --------------------------------------------------------------------------- + * + * The best way to test random blocksizes is to go to coreutils/md5_sha1_sum.c + * and replace "4096" with something like "2000 + time(NULL) % 2097", + * then rebuild and compare "shaNNNsum bigfile" results. + */ + +static void sha1_process_block64(sha1_ctx_t *ctx) +{ + static const uint32_t rconsts[] ALIGN4 = { + 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 + }; + int i, j; + int cnt; + uint32_t W[16+16]; + uint32_t a, b, c, d, e; + + /* On-stack work buffer frees up one register in the main loop + * which otherwise will be needed to hold ctx pointer */ + for (i = 0; i < 16; i++) + W[i] = W[i+16] = SWAP_BE32(((uint32_t*)ctx->wbuffer)[i]); + + a = ctx->hash[0]; + b = ctx->hash[1]; + c = ctx->hash[2]; + d = ctx->hash[3]; + e = ctx->hash[4]; + + /* 4 rounds of 20 operations each */ + cnt = 0; + for (i = 0; i < 4; i++) { + j = 19; + do { + uint32_t work; + + work = c ^ d; + if (i == 0) { + work = (work & b) ^ d; + if (j <= 3) + goto ge16; + /* Used to do SWAP_BE32 here, but this + * requires ctx (see comment above) */ + work += W[cnt]; + } else { + if (i == 2) + work = ((b | c) & d) | (b & c); + else /* i = 1 or 3 */ + work ^= b; + ge16: + W[cnt] = W[cnt+16] = rotl32(W[cnt+13] ^ W[cnt+8] ^ W[cnt+2] ^ W[cnt], 1); + work += W[cnt]; + } + work += e + rotl32(a, 5) + rconsts[i]; + + /* Rotate by one for next time */ + e = d; + d = c; + c = /* b = */ rotl32(b, 30); + b = a; + a = work; + cnt = (cnt + 1) & 15; + } while (--j >= 0); + } + + ctx->hash[0] += a; + ctx->hash[1] += b; + ctx->hash[2] += c; + ctx->hash[3] += d; + ctx->hash[4] += e; +} + +/* Constants for SHA512 from FIPS 180-2:4.2.3. + * SHA256 constants from FIPS 180-2:4.2.2 + * are the most significant half of first 64 elements + * of the same array. + */ +#undef K +#if NEED_SHA512 +typedef uint64_t sha_K_int; +# define K(v) v +#else +typedef uint32_t sha_K_int; +# define K(v) (uint32_t)(v >> 32) +#endif +static const sha_K_int sha_K[] ALIGN8 = { + K(0x428a2f98d728ae22ULL), K(0x7137449123ef65cdULL), + K(0xb5c0fbcfec4d3b2fULL), K(0xe9b5dba58189dbbcULL), + K(0x3956c25bf348b538ULL), K(0x59f111f1b605d019ULL), + K(0x923f82a4af194f9bULL), K(0xab1c5ed5da6d8118ULL), + K(0xd807aa98a3030242ULL), K(0x12835b0145706fbeULL), + K(0x243185be4ee4b28cULL), K(0x550c7dc3d5ffb4e2ULL), + K(0x72be5d74f27b896fULL), K(0x80deb1fe3b1696b1ULL), + K(0x9bdc06a725c71235ULL), K(0xc19bf174cf692694ULL), + K(0xe49b69c19ef14ad2ULL), K(0xefbe4786384f25e3ULL), + K(0x0fc19dc68b8cd5b5ULL), K(0x240ca1cc77ac9c65ULL), + K(0x2de92c6f592b0275ULL), K(0x4a7484aa6ea6e483ULL), + K(0x5cb0a9dcbd41fbd4ULL), K(0x76f988da831153b5ULL), + K(0x983e5152ee66dfabULL), K(0xa831c66d2db43210ULL), + K(0xb00327c898fb213fULL), K(0xbf597fc7beef0ee4ULL), + K(0xc6e00bf33da88fc2ULL), K(0xd5a79147930aa725ULL), + K(0x06ca6351e003826fULL), K(0x142929670a0e6e70ULL), + K(0x27b70a8546d22ffcULL), K(0x2e1b21385c26c926ULL), + K(0x4d2c6dfc5ac42aedULL), K(0x53380d139d95b3dfULL), + K(0x650a73548baf63deULL), K(0x766a0abb3c77b2a8ULL), + K(0x81c2c92e47edaee6ULL), K(0x92722c851482353bULL), + K(0xa2bfe8a14cf10364ULL), K(0xa81a664bbc423001ULL), + K(0xc24b8b70d0f89791ULL), K(0xc76c51a30654be30ULL), + K(0xd192e819d6ef5218ULL), K(0xd69906245565a910ULL), + K(0xf40e35855771202aULL), K(0x106aa07032bbd1b8ULL), + K(0x19a4c116b8d2d0c8ULL), K(0x1e376c085141ab53ULL), + K(0x2748774cdf8eeb99ULL), K(0x34b0bcb5e19b48a8ULL), + K(0x391c0cb3c5c95a63ULL), K(0x4ed8aa4ae3418acbULL), + K(0x5b9cca4f7763e373ULL), K(0x682e6ff3d6b2b8a3ULL), + K(0x748f82ee5defb2fcULL), K(0x78a5636f43172f60ULL), + K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL), + K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL), + K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL), +#if NEED_SHA512 /* [64]+ are used for sha512 only */ + K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL), + K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL), + K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL), + K(0x113f9804bef90daeULL), K(0x1b710b35131c471bULL), + K(0x28db77f523047d84ULL), K(0x32caab7b40c72493ULL), + K(0x3c9ebe0a15c9bebcULL), K(0x431d67c49c100d4cULL), + K(0x4cc5d4becb3e42b6ULL), K(0x597f299cfc657e2aULL), + K(0x5fcb6fab3ad6faecULL), K(0x6c44198c4a475817ULL), +#endif +}; +#undef K + +#undef Ch +#undef Maj +#undef S0 +#undef S1 +#undef R0 +#undef R1 + +static void sha256_process_block64(sha256_ctx_t *ctx) +{ + unsigned t; + uint32_t W[64], a, b, c, d, e, f, g, h; + const uint32_t *words = (uint32_t*) ctx->wbuffer; + + /* Operators defined in FIPS 180-2:4.1.2. */ +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define S0(x) (rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22)) +#define S1(x) (rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25)) +#define R0(x) (rotr32(x, 7) ^ rotr32(x, 18) ^ (x >> 3)) +#define R1(x) (rotr32(x, 17) ^ rotr32(x, 19) ^ (x >> 10)) + + /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ + for (t = 0; t < 16; ++t) + W[t] = SWAP_BE32(words[t]); + for (/*t = 16*/; t < 64; ++t) + W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16]; + + a = ctx->hash[0]; + b = ctx->hash[1]; + c = ctx->hash[2]; + d = ctx->hash[3]; + e = ctx->hash[4]; + f = ctx->hash[5]; + g = ctx->hash[6]; + h = ctx->hash[7]; + + /* The actual computation according to FIPS 180-2:6.2.2 step 3. */ + for (t = 0; t < 64; ++t) { + /* Need to fetch upper half of sha_K[t] + * (I hope compiler is clever enough to just fetch + * upper half) + */ + uint32_t K_t = NEED_SHA512 ? (sha_K[t] >> 32) : sha_K[t]; + uint32_t T1 = h + S1(e) + Ch(e, f, g) + K_t + W[t]; + uint32_t T2 = S0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } +#undef Ch +#undef Maj +#undef S0 +#undef S1 +#undef R0 +#undef R1 + /* Add the starting values of the context according to FIPS 180-2:6.2.2 + step 4. */ + ctx->hash[0] += a; + ctx->hash[1] += b; + ctx->hash[2] += c; + ctx->hash[3] += d; + ctx->hash[4] += e; + ctx->hash[5] += f; + ctx->hash[6] += g; + ctx->hash[7] += h; +} + +#if NEED_SHA512 +static void sha512_process_block128(sha512_ctx_t *ctx) +{ + unsigned t; + uint64_t W[80]; + /* On i386, having assignments here (not later as sha256 does) + * produces 99 bytes smaller code with gcc 4.3.1 + */ + uint64_t a = ctx->hash[0]; + uint64_t b = ctx->hash[1]; + uint64_t c = ctx->hash[2]; + uint64_t d = ctx->hash[3]; + uint64_t e = ctx->hash[4]; + uint64_t f = ctx->hash[5]; + uint64_t g = ctx->hash[6]; + uint64_t h = ctx->hash[7]; + const uint64_t *words = (uint64_t*) ctx->wbuffer; + + /* Operators defined in FIPS 180-2:4.1.2. */ +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define S0(x) (rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39)) +#define S1(x) (rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41)) +#define R0(x) (rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7)) +#define R1(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6)) + + /* Compute the message schedule according to FIPS 180-2:6.3.2 step 2. */ + for (t = 0; t < 16; ++t) + W[t] = SWAP_BE64(words[t]); + for (/*t = 16*/; t < 80; ++t) + W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16]; + + /* The actual computation according to FIPS 180-2:6.3.2 step 3. */ + for (t = 0; t < 80; ++t) { + uint64_t T1 = h + S1(e) + Ch(e, f, g) + sha_K[t] + W[t]; + uint64_t T2 = S0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } +#undef Ch +#undef Maj +#undef S0 +#undef S1 +#undef R0 +#undef R1 + /* Add the starting values of the context according to FIPS 180-2:6.3.2 + step 4. */ + ctx->hash[0] += a; + ctx->hash[1] += b; + ctx->hash[2] += c; + ctx->hash[3] += d; + ctx->hash[4] += e; + ctx->hash[5] += f; + ctx->hash[6] += g; + ctx->hash[7] += h; +} +#endif /* NEED_SHA512 */ + +void sha1_begin(sha1_ctx_t *ctx) +{ + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->hash[4] = 0xc3d2e1f0; + ctx->total64 = 0; + ctx->process_block = sha1_process_block64; +} + +static const uint32_t init256[] ALIGN4 = { + 0, + 0, + 0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19, +}; +#if NEED_SHA512 +static const uint32_t init512_lo[] ALIGN4 = { + 0, + 0, + 0xf3bcc908, + 0x84caa73b, + 0xfe94f82b, + 0x5f1d36f1, + 0xade682d1, + 0x2b3e6c1f, + 0xfb41bd6b, + 0x137e2179, +}; +#endif /* NEED_SHA512 */ + +// Note: SHA-384 is identical to SHA-512, except that initial hash values are +// 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, +// 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4, +// and the output is constructed by omitting last two 64-bit words of it. + +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.2) */ +void sha256_begin(sha256_ctx_t *ctx) +{ + memcpy(&ctx->total64, init256, sizeof(init256)); + /*ctx->total64 = 0; - done by prepending two 32-bit zeros to init256 */ + ctx->process_block = sha256_process_block64; +} + +#if NEED_SHA512 +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.3) */ +void sha512_begin(sha512_ctx_t *ctx) +{ + int i; + /* Two extra iterations zero out ctx->total64[2] */ + uint64_t *tp = ctx->total64; + for (i = 0; i < 8 + 2; i++) + tp[i] = ((uint64_t)(init256[i]) << 32) + init512_lo[i]; + /*ctx->total64[0] = ctx->total64[1] = 0; - already done */ +} + +void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) +{ + unsigned bufpos = ctx->total64[0] & 127; + unsigned remaining; + + /* First increment the byte count. FIPS 180-2 specifies the possible + length of the file up to 2^128 _bits_. + We compute the number of _bytes_ and convert to bits later. */ + ctx->total64[0] += len; + if (ctx->total64[0] < len) + ctx->total64[1]++; + + while (1) { + remaining = 128 - bufpos; + if (remaining > len) + remaining = len; + /* Copy data into aligned buffer */ + memcpy(ctx->wbuffer + bufpos, buffer, remaining); + len -= remaining; + buffer = (const char *)buffer + remaining; + bufpos += remaining; + + /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ + bufpos -= 128; + if (bufpos != 0) + break; + + /* Buffer is filled up, process it */ + sha512_process_block128(ctx); + /*bufpos = 0; - already is */ + } +} +#endif /* NEED_SHA512 */ + +/* Used also for sha256 */ +unsigned sha1_end(sha1_ctx_t *ctx, void *resbuf) +{ + unsigned hash_size; + + /* SHA stores total in BE, need to swap on LE arches: */ + common64_end(ctx, /*swap_needed:*/ BB_LITTLE_ENDIAN); + + hash_size = (ctx->process_block == sha1_process_block64) ? 5 : 8; + /* This way we do not impose alignment constraints on resbuf: */ + if (BB_LITTLE_ENDIAN) { + unsigned i; + for (i = 0; i < hash_size; ++i) + ctx->hash[i] = SWAP_BE32(ctx->hash[i]); + } + hash_size *= sizeof(ctx->hash[0]); + memcpy(resbuf, ctx->hash, hash_size); + return hash_size; +} + +#if NEED_SHA512 +unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) +{ + unsigned bufpos = ctx->total64[0] & 127; + + /* Pad the buffer to the next 128-byte boundary with 0x80,0,0,0... */ + ctx->wbuffer[bufpos++] = 0x80; + + while (1) { + unsigned remaining = 128 - bufpos; + memset(ctx->wbuffer + bufpos, 0, remaining); + if (remaining >= 16) { + /* Store the 128-bit counter of bits in the buffer in BE format */ + uint64_t t; + t = ctx->total64[0] << 3; + t = SWAP_BE64(t); + *(bb__aliased_uint64_t *) (&ctx->wbuffer[128 - 8]) = t; + t = (ctx->total64[1] << 3) | (ctx->total64[0] >> 61); + t = SWAP_BE64(t); + *(bb__aliased_uint64_t *) (&ctx->wbuffer[128 - 16]) = t; + } + sha512_process_block128(ctx); + if (remaining >= 16) + break; + bufpos = 0; + } + + if (BB_LITTLE_ENDIAN) { + unsigned i; + for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i) + ctx->hash[i] = SWAP_BE64(ctx->hash[i]); + } + memcpy(resbuf, ctx->hash, sizeof(ctx->hash)); + return sizeof(ctx->hash); +} +#endif /* NEED_SHA512 */ + + +/* + * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, + * Michael Peeters and Gilles Van Assche. For more information, feedback or + * questions, please refer to our website: http://keccak.noekeon.org/ + * + * Implementation by Ronny Van Keer, + * hereby denoted as "the implementer". + * + * To the extent possible under law, the implementer has waived all copyright + * and related or neighboring rights to the source code in this file. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * Busybox modifications (C) Lauri Kasanen, under the GPLv2. + */ + +#if CONFIG_SHA3_SMALL < 0 +# define SHA3_SMALL 0 +#elif CONFIG_SHA3_SMALL > 1 +# define SHA3_SMALL 1 +#else +# define SHA3_SMALL CONFIG_SHA3_SMALL +#endif + +#define OPTIMIZE_SHA3_FOR_32 0 +/* + * SHA3 can be optimized for 32-bit CPUs with bit-slicing: + * every 64-bit word of state[] can be split into two 32-bit words + * by even/odd bits. In this form, all rotations of sha3 round + * are 32-bit - and there are lots of them. + * However, it requires either splitting/combining state words + * before/after sha3 round (code does this now) + * or shuffling bits before xor'ing them into state and in sha3_end. + * Without shuffling, bit-slicing results in -130 bytes of code + * and marginal speedup (but of course it gives wrong result). + * With shuffling it works, but +260 code bytes, and slower. + * Disabled for now: + */ +#if 0 /* LONG_MAX == 0x7fffffff */ +# undef OPTIMIZE_SHA3_FOR_32 +# define OPTIMIZE_SHA3_FOR_32 1 +#endif + +#if OPTIMIZE_SHA3_FOR_32 +/* This splits every 64-bit word into a pair of 32-bit words, + * even bits go into first word, odd bits go to second one. + * The conversion is done in-place. + */ +static void split_halves(uint64_t *state) +{ + /* Credit: Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */ + uint32_t *s32 = (uint32_t*)state; + uint32_t t, x0, x1; + int i; + for (i = 24; i >= 0; --i) { + x0 = s32[0]; + t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); + t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); + t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); + t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); + x1 = s32[1]; + t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); + t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); + t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); + t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); + *s32++ = (x0 & 0x0000FFFF) | (x1 << 16); + *s32++ = (x0 >> 16) | (x1 & 0xFFFF0000); + } +} +/* The reverse operation */ +static void combine_halves(uint64_t *state) +{ + uint32_t *s32 = (uint32_t*)state; + uint32_t t, x0, x1; + int i; + for (i = 24; i >= 0; --i) { + x0 = s32[0]; + x1 = s32[1]; + t = (x0 & 0x0000FFFF) | (x1 << 16); + x1 = (x0 >> 16) | (x1 & 0xFFFF0000); + x0 = t; + t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); + t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); + t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); + t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); + *s32++ = x0; + t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); + t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); + t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); + t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); + *s32++ = x1; + } +} +#endif + +/* + * In the crypto literature this function is usually called Keccak-f(). + */ +static void sha3_process_block72(uint64_t *state) +{ + enum { NROUNDS = 24 }; + +#if OPTIMIZE_SHA3_FOR_32 + /* + static const uint32_t IOTA_CONST_0[NROUNDS] ALIGN4 = { + 0x00000001UL, + 0x00000000UL, + 0x00000000UL, + 0x00000000UL, + 0x00000001UL, + 0x00000001UL, + 0x00000001UL, + 0x00000001UL, + 0x00000000UL, + 0x00000000UL, + 0x00000001UL, + 0x00000000UL, + 0x00000001UL, + 0x00000001UL, + 0x00000001UL, + 0x00000001UL, + 0x00000000UL, + 0x00000000UL, + 0x00000000UL, + 0x00000000UL, + 0x00000001UL, + 0x00000000UL, + 0x00000001UL, + 0x00000000UL, + }; + ** bits are in lsb: 0101 0000 1111 0100 1111 0001 + */ + uint32_t IOTA_CONST_0bits = (uint32_t)(0x0050f4f1); + static const uint32_t IOTA_CONST_1[NROUNDS] ALIGN4 = { + 0x00000000UL, + 0x00000089UL, + 0x8000008bUL, + 0x80008080UL, + 0x0000008bUL, + 0x00008000UL, + 0x80008088UL, + 0x80000082UL, + 0x0000000bUL, + 0x0000000aUL, + 0x00008082UL, + 0x00008003UL, + 0x0000808bUL, + 0x8000000bUL, + 0x8000008aUL, + 0x80000081UL, + 0x80000081UL, + 0x80000008UL, + 0x00000083UL, + 0x80008003UL, + 0x80008088UL, + 0x80000088UL, + 0x00008000UL, + 0x80008082UL, + }; + + uint32_t *const s32 = (uint32_t*)state; + unsigned round; + + split_halves(state); + + for (round = 0; round < NROUNDS; round++) { + unsigned x; + + /* Theta */ + { + uint32_t BC[20]; + for (x = 0; x < 10; ++x) { + BC[x+10] = BC[x] = s32[x]^s32[x+10]^s32[x+20]^s32[x+30]^s32[x+40]; + } + for (x = 0; x < 10; x += 2) { + uint32_t ta, tb; + ta = BC[x+8] ^ rotl32(BC[x+3], 1); + tb = BC[x+9] ^ BC[x+2]; + s32[x+0] ^= ta; + s32[x+1] ^= tb; + s32[x+10] ^= ta; + s32[x+11] ^= tb; + s32[x+20] ^= ta; + s32[x+21] ^= tb; + s32[x+30] ^= ta; + s32[x+31] ^= tb; + s32[x+40] ^= ta; + s32[x+41] ^= tb; + } + } + /* RhoPi */ + { + uint32_t t0a,t0b, t1a,t1b; + t1a = s32[1*2+0]; + t1b = s32[1*2+1]; + +#define RhoPi(PI_LANE, ROT_CONST) \ + t0a = s32[PI_LANE*2+0];\ + t0b = s32[PI_LANE*2+1];\ + if (ROT_CONST & 1) {\ + s32[PI_LANE*2+0] = rotl32(t1b, ROT_CONST/2+1);\ + s32[PI_LANE*2+1] = ROT_CONST == 1 ? t1a : rotl32(t1a, ROT_CONST/2+0);\ + } else {\ + s32[PI_LANE*2+0] = rotl32(t1a, ROT_CONST/2);\ + s32[PI_LANE*2+1] = rotl32(t1b, ROT_CONST/2);\ + }\ + t1a = t0a; t1b = t0b; + + RhoPi(10, 1) + RhoPi( 7, 3) + RhoPi(11, 6) + RhoPi(17,10) + RhoPi(18,15) + RhoPi( 3,21) + RhoPi( 5,28) + RhoPi(16,36) + RhoPi( 8,45) + RhoPi(21,55) + RhoPi(24, 2) + RhoPi( 4,14) + RhoPi(15,27) + RhoPi(23,41) + RhoPi(19,56) + RhoPi(13, 8) + RhoPi(12,25) + RhoPi( 2,43) + RhoPi(20,62) + RhoPi(14,18) + RhoPi(22,39) + RhoPi( 9,61) + RhoPi( 6,20) + RhoPi( 1,44) +#undef RhoPi + } + /* Chi */ + for (x = 0; x <= 40;) { + uint32_t BC0, BC1, BC2, BC3, BC4; + BC0 = s32[x + 0*2]; + BC1 = s32[x + 1*2]; + BC2 = s32[x + 2*2]; + s32[x + 0*2] = BC0 ^ ((~BC1) & BC2); + BC3 = s32[x + 3*2]; + s32[x + 1*2] = BC1 ^ ((~BC2) & BC3); + BC4 = s32[x + 4*2]; + s32[x + 2*2] = BC2 ^ ((~BC3) & BC4); + s32[x + 3*2] = BC3 ^ ((~BC4) & BC0); + s32[x + 4*2] = BC4 ^ ((~BC0) & BC1); + x++; + BC0 = s32[x + 0*2]; + BC1 = s32[x + 1*2]; + BC2 = s32[x + 2*2]; + s32[x + 0*2] = BC0 ^ ((~BC1) & BC2); + BC3 = s32[x + 3*2]; + s32[x + 1*2] = BC1 ^ ((~BC2) & BC3); + BC4 = s32[x + 4*2]; + s32[x + 2*2] = BC2 ^ ((~BC3) & BC4); + s32[x + 3*2] = BC3 ^ ((~BC4) & BC0); + s32[x + 4*2] = BC4 ^ ((~BC0) & BC1); + x += 9; + } + /* Iota */ + s32[0] ^= IOTA_CONST_0bits & 1; + IOTA_CONST_0bits >>= 1; + s32[1] ^= IOTA_CONST_1[round]; + } + + combine_halves(state); +#else + /* Native 64-bit algorithm */ + static const uint16_t IOTA_CONST[NROUNDS] ALIGN2 = { + /* Elements should be 64-bit, but top half is always zero + * or 0x80000000. We encode 63rd bits in a separate word below. + * Same is true for 31th bits, which lets us use 16-bit table + * instead of 64-bit. The speed penalty is lost in the noise. + */ + 0x0001, + 0x8082, + 0x808a, + 0x8000, + 0x808b, + 0x0001, + 0x8081, + 0x8009, + 0x008a, + 0x0088, + 0x8009, + 0x000a, + 0x808b, + 0x008b, + 0x8089, + 0x8003, + 0x8002, + 0x0080, + 0x800a, + 0x000a, + 0x8081, + 0x8080, + 0x0001, + 0x8008, + }; + /* bit for CONST[0] is in msb: 0011 0011 0000 0111 1101 1101 */ + const uint32_t IOTA_CONST_bit63 = (uint32_t)(0x3307dd00); + /* bit for CONST[0] is in msb: 0001 0110 0011 1000 0001 1011 */ + const uint32_t IOTA_CONST_bit31 = (uint32_t)(0x16381b00); + + static const uint8_t ROT_CONST[24] ALIGN1 = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, + }; + static const uint8_t PI_LANE[24] ALIGN1 = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, + }; + /*static const uint8_t MOD5[10] ALIGN1 = { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, };*/ + + unsigned x; + unsigned round; + + if (BB_BIG_ENDIAN) { + for (x = 0; x < 25; x++) { + state[x] = SWAP_LE64(state[x]); + } + } + + for (round = 0; round < NROUNDS; ++round) { + /* Theta */ + { + uint64_t BC[10]; + for (x = 0; x < 5; ++x) { + BC[x + 5] = BC[x] = state[x] + ^ state[x + 5] ^ state[x + 10] + ^ state[x + 15] ^ state[x + 20]; + } + /* Using 2x5 vector above eliminates the need to use + * BC[MOD5[x+N]] trick below to fetch BC[(x+N) % 5], + * and the code is a bit _smaller_. + */ + for (x = 0; x < 5; ++x) { + uint64_t temp = BC[x + 4] ^ rotl64(BC[x + 1], 1); + state[x] ^= temp; + state[x + 5] ^= temp; + state[x + 10] ^= temp; + state[x + 15] ^= temp; + state[x + 20] ^= temp; + } + } + + /* Rho Pi */ + if (SHA3_SMALL) { + uint64_t t1 = state[1]; + for (x = 0; x < 24; ++x) { + uint64_t t0 = state[PI_LANE[x]]; + state[PI_LANE[x]] = rotl64(t1, ROT_CONST[x]); + t1 = t0; + } + } else { + /* Especially large benefit for 32-bit arch (75% faster): + * 64-bit rotations by non-constant usually are SLOW on those. + * We resort to unrolling here. + * This optimizes out PI_LANE[] and ROT_CONST[], + * but generates 300-500 more bytes of code. + */ + uint64_t t0; + uint64_t t1 = state[1]; +#define RhoPi_twice(x) \ + t0 = state[PI_LANE[x ]]; \ + state[PI_LANE[x ]] = rotl64(t1, ROT_CONST[x ]); \ + t1 = state[PI_LANE[x+1]]; \ + state[PI_LANE[x+1]] = rotl64(t0, ROT_CONST[x+1]); + RhoPi_twice(0); RhoPi_twice(2); + RhoPi_twice(4); RhoPi_twice(6); + RhoPi_twice(8); RhoPi_twice(10); + RhoPi_twice(12); RhoPi_twice(14); + RhoPi_twice(16); RhoPi_twice(18); + RhoPi_twice(20); RhoPi_twice(22); +#undef RhoPi_twice + } + /* Chi */ +# if LONG_MAX > 0x7fffffff + for (x = 0; x <= 20; x += 5) { + uint64_t BC0, BC1, BC2, BC3, BC4; + BC0 = state[x + 0]; + BC1 = state[x + 1]; + BC2 = state[x + 2]; + state[x + 0] = BC0 ^ ((~BC1) & BC2); + BC3 = state[x + 3]; + state[x + 1] = BC1 ^ ((~BC2) & BC3); + BC4 = state[x + 4]; + state[x + 2] = BC2 ^ ((~BC3) & BC4); + state[x + 3] = BC3 ^ ((~BC4) & BC0); + state[x + 4] = BC4 ^ ((~BC0) & BC1); + } +# else + /* Reduced register pressure version + * for register-starved 32-bit arches + * (i386: -95 bytes, and it is _faster_) + */ + for (x = 0; x <= 40;) { + uint32_t BC0, BC1, BC2, BC3, BC4; + uint32_t *const s32 = (uint32_t*)state; +# if SHA3_SMALL + do_half: +# endif + BC0 = s32[x + 0*2]; + BC1 = s32[x + 1*2]; + BC2 = s32[x + 2*2]; + s32[x + 0*2] = BC0 ^ ((~BC1) & BC2); + BC3 = s32[x + 3*2]; + s32[x + 1*2] = BC1 ^ ((~BC2) & BC3); + BC4 = s32[x + 4*2]; + s32[x + 2*2] = BC2 ^ ((~BC3) & BC4); + s32[x + 3*2] = BC3 ^ ((~BC4) & BC0); + s32[x + 4*2] = BC4 ^ ((~BC0) & BC1); + x++; +# if SHA3_SMALL + if (x & 1) + goto do_half; + x += 8; +# else + BC0 = s32[x + 0*2]; + BC1 = s32[x + 1*2]; + BC2 = s32[x + 2*2]; + s32[x + 0*2] = BC0 ^ ((~BC1) & BC2); + BC3 = s32[x + 3*2]; + s32[x + 1*2] = BC1 ^ ((~BC2) & BC3); + BC4 = s32[x + 4*2]; + s32[x + 2*2] = BC2 ^ ((~BC3) & BC4); + s32[x + 3*2] = BC3 ^ ((~BC4) & BC0); + s32[x + 4*2] = BC4 ^ ((~BC0) & BC1); + x += 9; +# endif + } +# endif /* long is 32-bit */ + /* Iota */ + state[0] ^= IOTA_CONST[round] + | (uint32_t)((IOTA_CONST_bit31 << round) & 0x80000000) + | (uint64_t)((IOTA_CONST_bit63 << round) & 0x80000000) << 32; + } + + if (BB_BIG_ENDIAN) { + for (x = 0; x < 25; x++) { + state[x] = SWAP_LE64(state[x]); + } + } +#endif +} + +void sha3_begin(sha3_ctx_t *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + /* SHA3-512, user can override */ + ctx->input_block_bytes = (1600 - 512*2) / 8; /* 72 bytes */ +} + +void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) +{ +#if SHA3_SMALL + const uint8_t *data = buffer; + unsigned bufpos = ctx->bytes_queued; + + while (1) { + unsigned remaining = ctx->input_block_bytes - bufpos; + if (remaining > len) + remaining = len; + len -= remaining; + /* XOR data into buffer */ + while (remaining != 0) { + uint8_t *buf = (uint8_t*)ctx->state; + buf[bufpos] ^= *data++; + bufpos++; + remaining--; + } + + /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ + bufpos -= ctx->input_block_bytes; + if (bufpos != 0) + break; + + /* Buffer is filled up, process it */ + sha3_process_block72(ctx->state); + /*bufpos = 0; - already is */ + } + ctx->bytes_queued = bufpos + ctx->input_block_bytes; +#else + /* +50 bytes code size, but a bit faster because of long-sized XORs */ + const uint8_t *data = buffer; + unsigned bufpos = ctx->bytes_queued; + unsigned iblk_bytes = ctx->input_block_bytes; + + /* If already data in queue, continue queuing first */ + if (bufpos != 0) { + while (len != 0) { + uint8_t *buf = (uint8_t*)ctx->state; + buf[bufpos] ^= *data++; + len--; + bufpos++; + if (bufpos == iblk_bytes) { + bufpos = 0; + goto do_block; + } + } + } + + /* Absorb complete blocks */ + while (len >= iblk_bytes) { + /* XOR data onto beginning of state[]. + * We try to be efficient - operate one word at a time, not byte. + * Careful wrt unaligned access: can't just use "*(long*)data"! + */ + unsigned count = iblk_bytes / sizeof(long); + long *buf = (long*)ctx->state; + do { + long v; + move_from_unaligned_long(v, (long*)data); + *buf++ ^= v; + data += sizeof(long); + } while (--count); + len -= iblk_bytes; + do_block: + sha3_process_block72(ctx->state); + } + + /* Queue remaining data bytes */ + while (len != 0) { + uint8_t *buf = (uint8_t*)ctx->state; + buf[bufpos] ^= *data++; + bufpos++; + len--; + } + + ctx->bytes_queued = bufpos; +#endif +} + +unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) +{ + /* Padding */ + uint8_t *buf = (uint8_t*)ctx->state; + /* + * Keccak block padding is: add 1 bit after last bit of input, + * then add zero bits until the end of block, and add the last 1 bit + * (the last bit in the block) - the "10*1" pattern. + * SHA3 standard appends additional two bits, 01, before that padding: + * + * SHA3-224(M) = KECCAK[448](M||01, 224) + * SHA3-256(M) = KECCAK[512](M||01, 256) + * SHA3-384(M) = KECCAK[768](M||01, 384) + * SHA3-512(M) = KECCAK[1024](M||01, 512) + * (M is the input, || is bit concatenation) + * + * The 6 below contains 01 "SHA3" bits and the first 1 "Keccak" bit: + */ + buf[ctx->bytes_queued] ^= 6; /* bit pattern 00000110 */ + buf[ctx->input_block_bytes - 1] ^= 0x80; + + sha3_process_block72(ctx->state); + + /* Output */ + memcpy(resbuf, ctx->state, 64); + return 64; +} + + + diff --git a/main/lib/hash/hash.h b/main/lib/hash/hash.h new file mode 100644 index 00000000..ee430371 --- /dev/null +++ b/main/lib/hash/hash.h @@ -0,0 +1,76 @@ +/*************************************************************************** + + hash.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __HASH_H +#define __HASH_H + +#include +#include +#include + +typedef struct md5_ctx_t { + uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ + void (*process_block)(struct md5_ctx_t*); + uint64_t total64; /* must be directly before hash[] */ + uint32_t hash[8]; /* 4 elements for md5, 5 for sha1, 8 for sha256 */ +} md5_ctx_t; +typedef struct md5_ctx_t sha1_ctx_t; +typedef struct md5_ctx_t sha256_ctx_t; +typedef struct sha512_ctx_t { + uint64_t total64[2]; /* must be directly before hash[] */ + uint64_t hash[8]; + uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ +} sha512_ctx_t; +typedef struct sha3_ctx_t { + uint64_t state[25]; + unsigned bytes_queued; + unsigned input_block_bytes; +} sha3_ctx_t; +void md5_begin(md5_ctx_t *ctx); +void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len); +unsigned md5_end(md5_ctx_t *ctx, void *resbuf); +void sha1_begin(sha1_ctx_t *ctx); +#define sha1_hash md5_hash +unsigned sha1_end(sha1_ctx_t *ctx, void *resbuf); +void sha256_begin(sha256_ctx_t *ctx); +#define sha256_hash md5_hash +#define sha256_end sha1_end +void sha512_begin(sha512_ctx_t *ctx); +void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len); +unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf); +void sha3_begin(sha3_ctx_t *ctx); +void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len); +unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf); +/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */ +typedef struct md5_ctx_t md5sha_ctx_t; +#define md5sha_hash md5_hash +#define sha_end sha1_end +enum { + MD5_OUTSIZE = 16, + SHA1_OUTSIZE = 20, + SHA256_OUTSIZE = 32, + SHA512_OUTSIZE = 64, + SHA3_OUTSIZE = 28, +}; + +#endif diff --git a/main/lib/hash/main.c b/main/lib/hash/main.c new file mode 100644 index 00000000..3db1f9fd --- /dev/null +++ b/main/lib/hash/main.c @@ -0,0 +1,150 @@ +/*************************************************************************** + + main.c + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "hash.h" +#include "c_hash.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +static union { + //sha3_ctx_t sha3; + sha512_ctx_t sha512; + sha256_ctx_t sha256; + sha1_ctx_t sha1; + md5_ctx_t md5; +} _context; + +static int _algo = -1; +static void (*_update)(void*, const void*, size_t); +static unsigned (*_final)(void*, void*); +static int _hash_len; + +bool HASH_begin(int algo) +{ + switch(algo) + { + case HASH_MD5: + md5_begin(&_context.md5); + _update = (void*)md5_hash; + _final = (void*)md5_end; + _hash_len = 16; + break; + + case HASH_SHA1: + sha1_begin(&_context.sha1); + _update = (void*)sha1_hash; + _final = (void*)sha1_end; + _hash_len = 20; + break; + + case HASH_SHA256: + sha256_begin(&_context.sha256); + _update = (void*)sha256_hash; + _final = (void*)sha256_end; + _hash_len = 32; + break; + + case HASH_SHA512: + sha512_begin(&_context.sha512); + _update = (void*)sha512_hash; + _final = (void*)sha512_end; + _hash_len = 64; + break; + + default: + return TRUE; + } + + _algo = algo; + return FALSE; +} + +void HASH_process(const void *data, size_t len) +{ + if (_algo < 0) + return; + + (*_update)(&_context, data, len); +} + +char *HASH_end(void) +{ + static const char hex_digit[16] = "0123456789ABCDEF"; + + char *result = NULL; + unsigned char hash[64]; + char hex[2]; + int size; + int i; + + if (_algo < 0) + return NULL; + + size = (*_final)(&_context, (void *)&hash[0]); + + for (i = 0; i < size; i++) + { + hex[0] = hex_digit[hash[i] >> 4]; + hex[1] = hex_digit[hash[i] & 15]; + result = GB.AddString(result, hex, 2); + } + + GB.FreeStringLater(result); + return result; +} + +/*void *GB_HASH_1[] EXPORT = { + + (void *)1, + HASH_begin, + HASH_process, + HASH_end, + NULL + };*/ + +GB_DESC *GB_CLASSES[] EXPORT = +{ + HashDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/hash/main.h b/main/lib/hash/main.h new file mode 100644 index 00000000..084b3598 --- /dev/null +++ b/main/lib/hash/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.hash.h" + +#ifndef __MAIN_C +extern GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +bool HASH_begin(int algo); +void HASH_process(const void *data, size_t len); +char *HASH_end(void); + +#endif /* __MAIN_H */ diff --git a/main/lib/hash/platform.h b/main/lib/hash/platform.h new file mode 100644 index 00000000..fbe548b0 --- /dev/null +++ b/main/lib/hash/platform.h @@ -0,0 +1,639 @@ +/* + * Copyright 2006, Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#ifndef BB_PLATFORM_H +#define BB_PLATFORM_H 1 + + +/* Convenience macros to test the version of gcc. */ +#undef __GNUC_PREREQ +#if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define __GNUC_PREREQ(maj, min) 0 +#endif + +/* __restrict is known in EGCS 1.2 and above. */ +#if !__GNUC_PREREQ(2,92) +# ifndef __restrict +# define __restrict +# endif +#endif + +#if !__GNUC_PREREQ(2,7) +# ifndef __attribute__ +# define __attribute__(x) +# endif +#endif + +#if !__GNUC_PREREQ(5,0) +# define deprecated(msg) deprecated +#endif + +#undef inline +#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L +/* it's a keyword */ +#elif __GNUC_PREREQ(2,7) +# define inline __inline__ +#else +# define inline +#endif + +#ifndef __const +# define __const const +#endif + +#define UNUSED_PARAM __attribute__ ((__unused__)) +#define NORETURN __attribute__ ((__noreturn__)) + +#if __GNUC_PREREQ(4,5) +# define bb_unreachable(altcode) __builtin_unreachable() +#else +# define bb_unreachable(altcode) altcode +#endif + +/* "The malloc attribute is used to tell the compiler that a function + * may be treated as if any non-NULL pointer it returns cannot alias + * any other pointer valid when the function returns. This will often + * improve optimization. Standard functions with this property include + * malloc and calloc. realloc-like functions have this property as long + * as the old pointer is never referred to (including comparing it + * to the new pointer) after the function returns a non-NULL value." + */ +#define RETURNS_MALLOC __attribute__ ((malloc)) +#define PACKED __attribute__ ((__packed__)) +#define ALIGNED(m) __attribute__ ((__aligned__(m))) + +/* __NO_INLINE__: some gcc's do not honor inlining! :( */ +#if __GNUC_PREREQ(3,0) && !defined(__NO_INLINE__) +# define ALWAYS_INLINE __attribute__ ((always_inline)) inline +/* I've seen a toolchain where I needed __noinline__ instead of noinline */ +# define NOINLINE __attribute__((__noinline__)) +# if !ENABLE_WERROR +# define DEPRECATED __attribute__ ((__deprecated__)) +# define UNUSED_PARAM_RESULT __attribute__ ((warn_unused_result)) +# else +# define DEPRECATED +# define UNUSED_PARAM_RESULT +# endif +#else +# define ALWAYS_INLINE inline +# define NOINLINE +# define DEPRECATED +# define UNUSED_PARAM_RESULT +#endif + +/* used by unit test machinery to run registration functions before calling main() */ +#define INIT_FUNC __attribute__ ((constructor)) + +/* -fwhole-program makes all symbols local. The attribute externally_visible + * forces a symbol global. */ +#if __GNUC_PREREQ(4,1) +# define EXTERNALLY_VISIBLE __attribute__(( visibility("default") )) +//__attribute__ ((__externally_visible__)) +#else +# define EXTERNALLY_VISIBLE +#endif + +/* At 4.4 gcc become much more anal about this, need to use "aliased" types */ +#if __GNUC_PREREQ(4,4) +# define FIX_ALIASING __attribute__((__may_alias__)) +#else +# define FIX_ALIASING +#endif + +/* We use __extension__ in some places to suppress -pedantic warnings + * about GCC extensions. This feature didn't work properly before + * gcc 2.8. */ +#if !__GNUC_PREREQ(2,8) +# ifndef __extension__ +# define __extension__ +# endif +#endif + +/* FAST_FUNC is a qualifier which (possibly) makes function call faster + * and/or smaller by using modified ABI. It is usually only needed + * on non-static, busybox internal functions. Recent versions of gcc + * optimize statics automatically. FAST_FUNC on static is required + * only if you need to match a function pointer's type. + * FAST_FUNC may not work well with -flto so allow user to disable this. + * (-DFAST_FUNC= ) + */ +#ifndef FAST_FUNC +# if __GNUC_PREREQ(3,0) && defined(i386) +/* stdcall makes callee to pop arguments from stack, not caller */ +# define FAST_FUNC __attribute__((regparm(3),stdcall)) +/* #elif ... - add your favorite arch today! */ +# else +# define FAST_FUNC +# endif +#endif + +/* Make all declarations hidden (-fvisibility flag only affects definitions) */ +/* (don't include system headers after this until corresponding pop!) */ +#if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__) +# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)") +# define POP_SAVED_FUNCTION_VISIBILITY _Pragma("GCC visibility pop") +#else +# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN +# define POP_SAVED_FUNCTION_VISIBILITY +#endif + +/* gcc-2.95 had no va_copy but only __va_copy. */ +#if !__GNUC_PREREQ(3,0) +# include +# if !defined va_copy && defined __va_copy +# define va_copy(d,s) __va_copy((d),(s)) +# endif +#endif + + +/* ---- Endian Detection ------------------------------------ */ + +#include +#if defined(__digital__) && defined(__unix__) +# include +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ + || defined(__APPLE__) +# include /* rlimit */ +# include +# define bswap_64 __bswap64 +# define bswap_32 __bswap32 +# define bswap_16 __bswap16 +#else +# include +# include +#endif + +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN +# define BB_BIG_ENDIAN 1 +# define BB_LITTLE_ENDIAN 0 +#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN +# define BB_BIG_ENDIAN 1 +# define BB_LITTLE_ENDIAN 0 +#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN +# define BB_BIG_ENDIAN 1 +# define BB_LITTLE_ENDIAN 0 +#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#elif defined(__386__) +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#else +# error "Can't determine endianness" +#endif + +#if ULONG_MAX > 0xffffffff +/* inline 64-bit bswap only on 64-bit arches */ +# define bb_bswap_64(x) bswap_64(x) +#endif + +/* SWAP_LEnn means "convert CPU<->little_endian by swapping bytes" */ +#if BB_BIG_ENDIAN +# define SWAP_BE16(x) (x) +# define SWAP_BE32(x) (x) +# define SWAP_BE64(x) (x) +# define SWAP_LE16(x) bswap_16(x) +# define SWAP_LE32(x) bswap_32(x) +# define SWAP_LE64(x) bb_bswap_64(x) +# define IF_BIG_ENDIAN(...) __VA_ARGS__ +# define IF_LITTLE_ENDIAN(...) +#else +# define SWAP_BE16(x) bswap_16(x) +# define SWAP_BE32(x) bswap_32(x) +# define SWAP_BE64(x) bb_bswap_64(x) +# define SWAP_LE16(x) (x) +# define SWAP_LE32(x) (x) +# define SWAP_LE64(x) (x) +# define IF_BIG_ENDIAN(...) +# define IF_LITTLE_ENDIAN(...) __VA_ARGS__ +#endif + + +/* ---- Unaligned access ------------------------------------ */ + +#include +typedef int bb__aliased_int FIX_ALIASING; +typedef long bb__aliased_long FIX_ALIASING; +typedef uint16_t bb__aliased_uint16_t FIX_ALIASING; +typedef uint32_t bb__aliased_uint32_t FIX_ALIASING; +typedef uint64_t bb__aliased_uint64_t FIX_ALIASING; + +/* NB: unaligned parameter should be a pointer, aligned one - + * a lvalue. This makes it more likely to not swap them by mistake + */ +#if defined(i386) || defined(__x86_64__) || defined(__powerpc__) +# define BB_UNALIGNED_MEMACCESS_OK 1 +# define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp)) +# define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp)) +# define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p)) +# define move_from_unaligned32(v, u32p) ((v) = *(bb__aliased_uint32_t*)(u32p)) +# define move_from_unaligned64(v, u64p) ((v) = *(bb__aliased_uint64_t*)(u64p)) +# define move_to_unaligned16(u16p, v) (*(bb__aliased_uint16_t*)(u16p) = (v)) +# define move_to_unaligned32(u32p, v) (*(bb__aliased_uint32_t*)(u32p) = (v)) +# define move_to_unaligned64(u64p, v) (*(bb__aliased_uint64_t*)(u64p) = (v)) +/* #elif ... - add your favorite arch today! */ +#else +# define BB_UNALIGNED_MEMACCESS_OK 0 +/* performs reasonably well (gcc usually inlines memcpy here) */ +# define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int))) +# define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long))) +# define move_from_unaligned16(v, u16p) (memcpy(&(v), (u16p), 2)) +# define move_from_unaligned32(v, u32p) (memcpy(&(v), (u32p), 4)) +# define move_from_unaligned64(v, u64p) (memcpy(&(v), (u64p), 8)) +# define move_to_unaligned16(u16p, v) do { \ + uint16_t __t = (v); \ + memcpy((u16p), &__t, 2); \ +} while (0) +# define move_to_unaligned32(u32p, v) do { \ + uint32_t __t = (v); \ + memcpy((u32p), &__t, 4); \ +} while (0) +# define move_to_unaligned64(u64p, v) do { \ + uint64_t __t = (v); \ + memcpy((u64p), &__t, 8); \ +} while (0) +#endif + +/* Unaligned, fixed-endian accessors */ +#define get_unaligned_le32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_LE32(v); }) +#define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); }) +#define put_unaligned_le32(val, buf) move_to_unaligned32(buf, SWAP_LE32(val)) +#define put_unaligned_be32(val, buf) move_to_unaligned32(buf, SWAP_BE32(val)) + +/* unxz needs an aligned fixed-endian accessor. + * (however, the compiler does not realize it's aligned, the cast is still necessary) + */ +#define get_le32(u32p) ({ uint32_t v = *(bb__aliased_uint32_t*)(u32p); SWAP_LE32(v); }) + + +/* ---- Size-saving "small" ints (arch-dependent) ----------- */ + +#if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__) +/* add other arches which benefit from this... */ +typedef signed char smallint; +typedef unsigned char smalluint; +#else +/* for arches where byte accesses generate larger code: */ +typedef int smallint; +typedef unsigned smalluint; +#endif + +/* ISO C Standard: 7.16 Boolean type and values */ +#if (defined __digital__ && defined __unix__) +/* old system without (proper) C99 support */ +# define bool smalluint +#else +/* modern system, so use it */ +# include +#endif + + +/*----- Kernel versioning ------------------------------------*/ + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#ifdef __UCLIBC__ +# define UCLIBC_VERSION KERNEL_VERSION(__UCLIBC_MAJOR__, __UCLIBC_MINOR__, __UCLIBC_SUBLEVEL__) +#else +# define UCLIBC_VERSION 0 +#endif + + +/* ---- Miscellaneous --------------------------------------- */ + +#if defined __GLIBC__ \ + || defined __UCLIBC__ \ + || defined __dietlibc__ \ + || defined __BIONIC__ \ + || defined _NEWLIB_VERSION +# include +#endif + +/* Define bb_setpgrp */ +#if (defined(__digital__) && defined(__unix__)) || defined(__FreeBSD__) +/* use legacy setpgrp(pid_t, pid_t) for now. move to platform.c */ +# define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0) +#else +# define bb_setpgrp() setpgrp() +#endif + +/* fdprintf is more readable, we used it before dprintf was standardized */ +#include +#define fdprintf dprintf + +/* Useful for defeating gcc's alignment of "char message[]"-like data */ +#if !defined(__s390__) + /* on s390[x], non-word-aligned data accesses require larger code */ +# define ALIGN1 __attribute__((aligned(1))) +# define ALIGN2 __attribute__((aligned(2))) +# define ALIGN4 __attribute__((aligned(4))) +#else +/* Arches which MUST have 2 or 4 byte alignment for everything are here */ +# define ALIGN1 +# define ALIGN2 +# define ALIGN4 +#endif +#define ALIGN8 __attribute__((aligned(8))) +#define ALIGN_PTR __attribute__((aligned(sizeof(void*)))) + +/* + * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably. + * For earlier versions there is no reliable way to check if we are building + * for a mmu-less system. + */ +#if ENABLE_NOMMU || \ + (defined __UCLIBC__ && \ + UCLIBC_VERSION > KERNEL_VERSION(0, 9, 28) && \ + !defined __ARCH_USE_MMU__) +# define BB_MMU 0 +# define USE_FOR_NOMMU(...) __VA_ARGS__ +# define USE_FOR_MMU(...) +#else +# define BB_MMU 1 +# define USE_FOR_NOMMU(...) +# define USE_FOR_MMU(...) __VA_ARGS__ +#endif + +#if defined(__digital__) && defined(__unix__) +# include +# include +# define PRIu32 "u" +# if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET +# define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET) +# endif +# if !defined ADJ_FREQUENCY && defined MOD_FREQUENCY +# define ADJ_FREQUENCY MOD_FREQUENCY +# endif +# if !defined ADJ_TIMECONST && defined MOD_TIMECONST +# define ADJ_TIMECONST MOD_TIMECONST +# endif +# if !defined ADJ_TICK && defined MOD_CLKB +# define ADJ_TICK MOD_CLKB +# endif +#endif + +#if defined(__CYGWIN__) +# define MAXSYMLINKS SYMLOOP_MAX +#endif + +#if defined(ANDROID) || defined(__ANDROID__) +# define BB_ADDITIONAL_PATH ":/system/sbin:/system/bin:/system/xbin" +# define SYS_ioprio_set __NR_ioprio_set +# define SYS_ioprio_get __NR_ioprio_get +#endif + + +/* ---- Who misses what? ------------------------------------ */ + +/* Assume all these functions and header files exist by default. + * Platforms where it is not true will #undef them below. + */ +#define HAVE_CLEARENV 1 +#define HAVE_FDATASYNC 1 +#define HAVE_DPRINTF 1 +#define HAVE_MEMRCHR 1 +#define HAVE_MKDTEMP 1 +#define HAVE_TTYNAME_R 1 +#define HAVE_PTSNAME_R 1 +#define HAVE_SETBIT 1 +#define HAVE_SIGHANDLER_T 1 +#define HAVE_STPCPY 1 +#define HAVE_STPNCPY 1 +#define HAVE_MEMPCPY 1 +#define HAVE_STRCASESTR 1 +#define HAVE_STRCHRNUL 1 +#define HAVE_STRSEP 1 +#define HAVE_STRSIGNAL 1 +#define HAVE_STRVERSCMP 1 +#define HAVE_VASPRINTF 1 +#define HAVE_USLEEP 1 +#define HAVE_UNLOCKED_STDIO 1 +#define HAVE_UNLOCKED_LINE_OPS 1 +#define HAVE_GETLINE 1 +#define HAVE_XTABS 1 +#define HAVE_MNTENT_H 1 +#define HAVE_NET_ETHERNET_H 1 +#define HAVE_SYS_STATFS_H 1 +#define HAVE_PRINTF_PERCENTM 1 +#define HAVE_WAIT3 1 +#define HAVE_DEV_FD 1 +#define DEV_FD_PREFIX "/dev/fd/" + +#if defined(__UCLIBC__) +# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) +# undef HAVE_STRVERSCMP +# endif +# if UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30) +# ifndef __UCLIBC_SUSV3_LEGACY__ +# undef HAVE_USLEEP +# endif +# endif +#endif + +#if defined(__WATCOMC__) +# undef HAVE_DPRINTF +# undef HAVE_GETLINE +# undef HAVE_MEMRCHR +# undef HAVE_MKDTEMP +# undef HAVE_SETBIT +# undef HAVE_STPCPY +# undef HAVE_STPNCPY +# undef HAVE_STRCASESTR +# undef HAVE_STRCHRNUL +# undef HAVE_STRSEP +# undef HAVE_STRSIGNAL +# undef HAVE_STRVERSCMP +# undef HAVE_VASPRINTF +# undef HAVE_UNLOCKED_STDIO +# undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_NET_ETHERNET_H +#endif + +#if defined(__CYGWIN__) +# undef HAVE_CLEARENV +# undef HAVE_FDPRINTF +# undef HAVE_MEMRCHR +# undef HAVE_PTSNAME_R +# undef HAVE_STRVERSCMP +# undef HAVE_UNLOCKED_LINE_OPS +#endif + +/* These BSD-derived OSes share many similarities */ +#if (defined __digital__ && defined __unix__) \ + || defined __APPLE__ \ + || defined __OpenBSD__ || defined __NetBSD__ +# undef HAVE_CLEARENV +# undef HAVE_FDATASYNC +# undef HAVE_GETLINE +# undef HAVE_MNTENT_H +# undef HAVE_PTSNAME_R +# undef HAVE_SYS_STATFS_H +# undef HAVE_SIGHANDLER_T +# undef HAVE_STRVERSCMP +# undef HAVE_XTABS +# undef HAVE_DPRINTF +# undef HAVE_UNLOCKED_STDIO +# undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_PRINTF_PERCENTM +#endif + +#if defined(__dietlibc__) +# undef HAVE_STRCHRNUL +#endif + +#if defined(__APPLE__) +# undef HAVE_STRCHRNUL +#endif + +#if defined(__FreeBSD__) +/* users say mempcpy is not present in FreeBSD 9.x */ +# undef HAVE_MEMPCPY +# undef HAVE_CLEARENV +# undef HAVE_FDATASYNC +# undef HAVE_MNTENT_H +# undef HAVE_PTSNAME_R +# undef HAVE_SYS_STATFS_H +# undef HAVE_SIGHANDLER_T +# undef HAVE_STRVERSCMP +# undef HAVE_XTABS +# undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_PRINTF_PERCENTM +# include +# if __FreeBSD_version < 1000029 +# undef HAVE_STRCHRNUL /* FreeBSD added strchrnul() between 1000028 and 1000029 */ +# endif +#endif + +#if defined(__NetBSD__) +# define HAVE_GETLINE 1 /* Recent NetBSD versions have getline() */ +#endif + +#if defined(__digital__) && defined(__unix__) +# undef HAVE_STPCPY +# undef HAVE_STPNCPY +#endif + +#if defined(ANDROID) || defined(__ANDROID__) +# if __ANDROID_API__ < 8 + /* ANDROID < 8 has no [f]dprintf at all */ +# undef HAVE_DPRINTF +# elif __ANDROID_API__ < 21 + /* ANDROID < 21 has fdprintf */ +# define dprintf fdprintf +# else + /* ANDROID >= 21 has standard dprintf */ +# endif +# if __ANDROID_API__ < 21 +# undef HAVE_TTYNAME_R +# undef HAVE_GETLINE +# undef HAVE_STPCPY +# undef HAVE_STPNCPY +# endif +# if __ANDROID_API__ >= 21 +# undef HAVE_WAIT3 +# endif +# undef HAVE_MEMPCPY +# undef HAVE_STRCHRNUL +# undef HAVE_STRVERSCMP +# undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_NET_ETHERNET_H +# undef HAVE_PRINTF_PERCENTM +#endif + +/* + * Now, define prototypes for all the functions defined in platform.c + * These must come after all the HAVE_* macros are defined (or not) + */ + +#ifndef HAVE_DPRINTF +extern int dprintf(int fd, const char *format, ...); +#endif + +#ifndef HAVE_MEMRCHR +extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; +#endif + +#ifndef HAVE_MKDTEMP +extern char *mkdtemp(char *template) FAST_FUNC; +#endif + +#ifndef HAVE_TTYNAME_R +#define ttyname_r bb_ttyname_r +extern int ttyname_r(int fd, char *buf, size_t buflen); +#endif + +#ifndef HAVE_SETBIT +# define setbit(a, b) ((a)[(b) >> 3] |= 1 << ((b) & 7)) +# define clrbit(a, b) ((a)[(b) >> 3] &= ~(1 << ((b) & 7))) +#endif + +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); +#endif + +#ifndef HAVE_STPCPY +extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; +#endif + +#ifndef HAVE_STPNCPY +extern char *stpncpy(char *p, const char *to_add, size_t n) FAST_FUNC; +#endif + +#ifndef HAVE_MEMPCPY +#include +/* In case we are wrong about !HAVE_MEMPCPY, and toolchain _does_ have + * mempcpy(), avoid colliding with it: + */ +#define mempcpy bb__mempcpy +static ALWAYS_INLINE void *mempcpy(void *dest, const void *src, size_t len) +{ + return memcpy(dest, src, len) + len; +} +#endif + +#ifndef HAVE_STRCASESTR +extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; +#endif + +#ifndef HAVE_STRCHRNUL +extern char *strchrnul(const char *s, int c) FAST_FUNC; +#endif + +#ifndef HAVE_STRSEP +extern char *strsep(char **stringp, const char *delim) FAST_FUNC; +#endif + +#ifndef HAVE_STRSIGNAL +/* Not exactly the same: instead of "Stopped" it shows "STOP" etc */ +# define strsignal(sig) get_signame(sig) +#endif + +#ifndef HAVE_USLEEP +extern int usleep(unsigned) FAST_FUNC; +#endif + +#ifndef HAVE_VASPRINTF +extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; +#endif + +#ifndef HAVE_GETLINE +# include /* for FILE */ +# include /* size_t */ +extern ssize_t getline(char **lineptr, size_t *n, FILE *stream) FAST_FUNC; +#endif + +#define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0]))) + +#endif + diff --git a/main/lib/image.effect/CImage.cpp b/main/lib/image.effect/CImage.cpp new file mode 100644 index 00000000..d19a9d63 --- /dev/null +++ b/main/lib/image.effect/CImage.cpp @@ -0,0 +1,473 @@ +/*************************************************************************** + + CImage.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include + +#include "gambas.h" +#include "kimageeffect.h" +#include "effect.h" +#include "main.h" + +#include "CImage.h" + + +static void return_copy(GB_IMAGE img) +{ + GB_IMG *info = (GB_IMG *)img; + GB_IMG *copy; + + copy = IMAGE.Create(info->width, info->height, info->format, info->data); + GB.ReturnObject(copy); +} + +BEGIN_METHOD(CIMAGE_gradient, GB_INTEGER w; GB_INTEGER h; GB_INTEGER cs; GB_INTEGER cd; GB_INTEGER orient; GB_FLOAT x; GB_FLOAT y) + + QSize size(VARG(w), VARG(h)); + QColor cs(VARG(cs)); + QColor cd(VARG(cd)); + + if (MISSING(x) || MISSING(y)) + { + QImage img = KImageEffect::gradient(size, cs, cd, (KImageEffect::GradientType)VARG(orient)); + GB.ReturnObject(img.object()); + } + else + { + QImage img = KImageEffect::unbalancedGradient(size, cs, cd, (KImageEffect::GradientType)VARG(orient), (int)(VARG(x) * 200), (int)(VARG(y) * 200)); + GB.ReturnObject(img.object()); + } + +END_METHOD + + +BEGIN_METHOD(CIMAGE_intensity, GB_FLOAT val; GB_INTEGER channel) + + KImageEffect::RGBComponent channel = KImageEffect::All; + QImage img(THIS); + + if (!MISSING(channel)) + channel = (KImageEffect::RGBComponent)VARG(channel); + + if (channel == KImageEffect::All) + KImageEffect::intensity(img, VARG(val)); + else + KImageEffect::channelIntensity(img, VARG(val), channel); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_flatten, GB_INTEGER c1; GB_INTEGER c2) + + QImage img(THIS); + + KImageEffect::flatten(img, VARG(c1), VARG(c2)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_fade, GB_INTEGER col; GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::fade(img, VARG(val), VARG(col)); + +END_METHOD + + +/*BEGIN_METHOD_VOID(CIMAGE_gray) + + QImage img(THIS); + + KImageEffect::toGray(img, false); + +END_METHOD*/ + + +/*BEGIN_METHOD(CIMAGE_desaturate, GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::desaturate(img, VARGOPT(val, 0.3)); + +END_METHOD*/ + + +BEGIN_METHOD(CIMAGE_threshold, GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::threshold(img, (uint)(VARG(val) * 255)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_solarize, GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::solarize(img, VARG(val) * 100); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_normalize) + + QImage img(THIS); + + KImageEffect::normalize(img); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_equalize) + + QImage img(THIS); + + KImageEffect::equalize(img); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_balance, GB_FLOAT brightness; GB_FLOAT contrast; GB_FLOAT gamma; GB_INTEGER channel) + + Effect::balance(THIS, VARGOPT(channel, Effect::All), (int)(VARG(brightness) * 50), (int)(VARG(contrast) * 50), (int)(VARG(gamma) * 50)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_invert, GB_INTEGER channel) + + Effect::invert(THIS, VARGOPT(channel, Effect::All)); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_emboss) + + QImage src(THIS); + QImage dest = KImageEffect::emboss(src, 0, 1); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_edge, GB_FLOAT radius) + + double radius = VARGOPT(radius, -1); + + if (radius != 0) + { + if (radius < 0) + radius = 0; + QImage src(THIS); + QImage dest = KImageEffect::edge(src, radius); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_despeckle) + + QImage src(THIS); + QImage dest = KImageEffect::despeckle(src); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_blur, GB_FLOAT val) + + double val = VARGOPT(val, 0.2); + double sigma = 0.5 + val * (4.0 - 0.5); + double radius = 8; + + QImage src(THIS); + QImage dest = KImageEffect::blur(src, radius, sigma); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_sharpen, GB_FLOAT val) + + double val = VARGOPT(val, 0.2); + double radius = 0.1 + val * (2.5 - 0.1); + double sigma = radius < 1 ? radius : sqrt(radius); + + QImage src(THIS); + QImage dest = KImageEffect::sharpen(src, radius, sigma); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_charcoal, GB_FLOAT radius) + + double radius = VARGOPT(radius, -1); + + if (radius != 0) + { + if (radius < 0) + radius = 0; + QImage src(THIS); + QImage dest = KImageEffect::charcoal(src, radius, 0.5); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_oil_paint, GB_FLOAT radius) + + double radius = VARGOPT(radius, -1); + + if (radius != 0) + { + if (radius < 0) + radius = 0; + QImage src(THIS); + QImage dest = KImageEffect::oilPaintConvolve(src, radius); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_spread, GB_INTEGER amount) + + int amount = VARGOPT(amount, 0); + + if (amount > 0) + { + QImage src(THIS); + QImage dest = KImageEffect::spread(src, VARGOPT(amount, 3)); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_shade, GB_FLOAT azimuth; GB_FLOAT elevation) + + QImage src(THIS); + QImage dest = KImageEffect::shade(src, true, VARGOPT(azimuth, M_PI/6), VARGOPT(elevation, M_PI/6)); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_swirl, GB_FLOAT angle; GB_INTEGER background) + + QImage src(THIS); + QImage dest = KImageEffect::swirl(src, VARGOPT(angle, M_PI/3), VARGOPT(background, 0xFFFFFF) ^ 0xFF000000); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_wave, GB_FLOAT amp; GB_FLOAT freq; GB_INTEGER background) + + QImage src(THIS); + QImage dest = KImageEffect::wave(src, VARGOPT(amp, 25.0), VARGOPT(freq, 150.0), VARGOPT(background, 0xFFFFFF) ^ 0xFF000000); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_noise, GB_INTEGER type) + + QImage src(THIS); + QImage dest = KImageEffect::addNoise(src, (KImageEffect::NoiseType)VARG(type)); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_implode, GB_FLOAT factor; GB_INTEGER background) + + QImage src(THIS); + QImage dest = KImageEffect::implode(src, VARGOPT(factor, 1.0) * 100.0, VARGOPT(background, 0xFFFFFF) ^ 0xFF000000); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD_VOID(Image_Histogram) + + CIMAGEHISTOGRAM *hist; + int *histogram; + QImage image(THIS); + unsigned int *p, *pm; + + hist = (CIMAGEHISTOGRAM *)GB.New(GB.FindClass("ImageHistogram"), NULL, NULL); + + GB.Alloc(POINTER(&histogram), sizeof(int) * 256 * 4); + + memset(histogram, 0, 256 * 4 * sizeof(int)); + + p = (unsigned int *)image.bits(); + pm = &p[image.width() * image.height()]; + + if (image.inverted()) + { + while (p < pm) + { + histogram[qRed(*p)]++; + histogram[qGreen(*p) + 256]++; + histogram[qBlue(*p) + 256 * 2]++; + histogram[qAlpha(*p) + 256 * 3]++; + p++; + } + } + else + { + while (p < pm) + { + histogram[qBlue(*p)]++; + histogram[qGreen(*p) + 256]++; + histogram[qRed(*p) + 256 * 2]++; + histogram[qAlpha(*p) + 256 * 3]++; + p++; + } + } + + hist->histogram = histogram; + GB.ReturnObject(hist); + +END_METHOD + + +BEGIN_METHOD_VOID(ImageHistogram_free) + + GB.Free(POINTER(&THIS_HISTOGRAM->histogram)); + +END_METHOD + + +BEGIN_METHOD(ImageHistogram_get, GB_INTEGER channel; GB_INTEGER value) + + int channel; + int value; + + switch(VARG(channel)) + { + case KImageEffect::Blue: channel = 0; break; + case KImageEffect::Green: channel = 1; break; + case KImageEffect::Red: channel = 2; break; + case KImageEffect::Alpha: channel = 3; break; + default: GB.Error("Bad channel"); return; + } + + value = VARG(value); + if (value < 0 || value > 255) + { + GB.Error("Out of bounds"); + return; + } + + GB.ReturnInteger(THIS_HISTOGRAM->histogram[channel * 256 + value]); + +END_METHOD + + +GB_DESC ImageHistogramDesc[] = +{ + GB_DECLARE("ImageHistogram", sizeof(CIMAGEHISTOGRAM)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, ImageHistogram_free, NULL), + GB_METHOD("_get", "i", ImageHistogram_get, "(Channel)i(Value)i"), + + GB_END_DECLARE +}; + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", 0), + + GB_CONSTANT("Vertical", "i", KImageEffect::VerticalGradient), + GB_CONSTANT("Horizontal", "i", KImageEffect::HorizontalGradient), + GB_CONSTANT("Diagonal", "i", KImageEffect::DiagonalGradient), + GB_CONSTANT("Cross", "i", KImageEffect::CrossDiagonalGradient), + GB_CONSTANT("Pyramid", "i", KImageEffect::PyramidGradient), + GB_CONSTANT("Rectangle", "i", KImageEffect::RectangleGradient), + GB_CONSTANT("PipeCross", "i", KImageEffect::PipeCrossGradient), + GB_CONSTANT("Elliptic", "i", KImageEffect::EllipticGradient), + + GB_CONSTANT("All", "i", KImageEffect::All), + GB_CONSTANT("Red", "i", KImageEffect::Red), + GB_CONSTANT("Green", "i", KImageEffect::Green), + GB_CONSTANT("Blue", "i", KImageEffect::Blue), + GB_CONSTANT("Alpha", "i", KImageEffect::Alpha), + + GB_CONSTANT("Uniform", "i", KImageEffect::UniformNoise), + GB_CONSTANT("Gaussian", "i", KImageEffect::GaussianNoise), + GB_CONSTANT("Multiplicative", "i", KImageEffect::MultiplicativeGaussianNoise), + GB_CONSTANT("Impulse", "i", KImageEffect::ImpulseNoise), + GB_CONSTANT("Laplacian", "i", KImageEffect::LaplacianNoise), + GB_CONSTANT("Poisson", "i", KImageEffect::PoissonNoise), + + GB_STATIC_METHOD("Gradient", "Image", CIMAGE_gradient, "(Width)i(Height)i(SrcColor)i(DstColor)i(Orientation)i[(XDecay)f(YDecay)f]"), + + GB_METHOD("Intensity", NULL, CIMAGE_intensity, "(Value)f[(Channel)i]"), + GB_METHOD("Flatten", NULL, CIMAGE_flatten, "(DarkColor)i(BrightColor)i"), + GB_METHOD("Fade", NULL, CIMAGE_fade, "(Color)i(Value)f"), + //GB_METHOD("Gray", NULL, CIMAGE_gray, NULL), + //GB_METHOD("Desaturate", NULL, CIMAGE_desaturate, "[(Value)f]"), + GB_METHOD("Threshold", NULL, CIMAGE_threshold, "(Value)f"), + GB_METHOD("Solarize", NULL, CIMAGE_solarize, "(Value)f"), + GB_METHOD("Normalize", NULL, CIMAGE_normalize, NULL), + GB_METHOD("Equalize", NULL, CIMAGE_equalize, NULL), + GB_METHOD("Balance", NULL, CIMAGE_balance, "(Brightness)f(Contrast)f(Gamma)f[(Channel)i]"), + GB_METHOD("Invert", NULL, CIMAGE_invert, "[(Channel)i]"), + + GB_METHOD("Emboss", "Image", CIMAGE_emboss, NULL), + GB_METHOD("Edge", "Image", CIMAGE_edge, "[(Radius)f]"), + GB_METHOD("Despeckle", "Image", CIMAGE_despeckle, NULL), + GB_METHOD("Charcoal", "Image", CIMAGE_charcoal, "[(Radius)f]"), + GB_METHOD("OilPaint", "Image", CIMAGE_oil_paint, "[(Radius)f]"), + GB_METHOD("Blur", "Image", CIMAGE_blur, "[(Value)f]"), + GB_METHOD("Sharpen", "Image", CIMAGE_sharpen, "[(Value)f]"), + GB_METHOD("Spread", "Image", CIMAGE_spread, "[(Amount)i]"), + GB_METHOD("Shade", "Image", CIMAGE_shade, "[(Azimuth)f(Elevation)f]"), + GB_METHOD("Swirl", "Image", CIMAGE_swirl, "[(Angle)f(Background)i]"), + GB_METHOD("Wave", "Image", CIMAGE_wave, "[(Amplitude)f(WaveLength)f(Background)i]"), + GB_METHOD("Noise", "Image", CIMAGE_noise, "(Noise)i"), + GB_METHOD("Implode", "Image", CIMAGE_implode, "[(Factor)f(Background)i]"), + + GB_METHOD("Histogram", "ImageHistogram", Image_Histogram, NULL), + + GB_END_DECLARE +}; + diff --git a/main/lib/image.effect/CImage.h b/main/lib/image.effect/CImage.h new file mode 100644 index 00000000..172751cd --- /dev/null +++ b/main/lib/image.effect/CImage.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "gambas.h" + +#ifndef __CIMAGE_CPP + +extern GB_DESC CImageDesc[]; +extern GB_DESC ImageHistogramDesc[]; + +#else + +#define THIS ((GB_IMAGE)_object) +#define THIS_HISTOGRAM ((CIMAGEHISTOGRAM *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + int *histogram; + } + CIMAGEHISTOGRAM; + +#endif diff --git a/main/lib/image.effect/Makefile.am b/main/lib/image.effect/Makefile.am new file mode 100644 index 00000000..9798c1f9 --- /dev/null +++ b/main/lib/image.effect/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.image.effect +include $(top_srcdir)/component.am + +noinst_LTLIBRARIES = libimageeffect.la +gblib_LTLIBRARIES = gb.image.effect.la + +libimageeffect_la_LIBADD = +libimageeffect_la_LDFLAGS = -module @LD_FLAGS@ +libimageeffect_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS_OPT) + +libimageeffect_la_SOURCES = \ + kimageeffect.h kimageeffect.cpp \ + effect.h effect.cpp + +gb_image_effect_la_LIBADD = libimageeffect.la +gb_image_effect_la_LDFLAGS = -module @LD_FLAGS@ +gb_image_effect_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS) + +gb_image_effect_la_SOURCES = \ +main.cpp main.h qt.h \ +kcpuinfo.h kcpuinfo.cpp \ +qcolor.h qcolor.cpp \ +qpoint.h qpoint.cpp \ +qsize.h qsize.cpp \ +qrect.h qrect.cpp \ +qimage.h qimage.cpp \ +CImage.h CImage.cpp + + diff --git a/main/lib/image.effect/effect.cpp b/main/lib/image.effect/effect.cpp new file mode 100644 index 00000000..55b7f73d --- /dev/null +++ b/main/lib/image.effect/effect.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + + effect.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EFFECT_CPP + +#include + +#include "effect.h" +#include "main.h" + +static void get_info(GB_IMAGE img, uint **data, uint *width, uint *height, uint *npixels, bool *inv) +{ + GB_IMG *info = (GB_IMG *)img; + + SYNCHRONIZE_IMAGE(info); + + *data = (uint *)info->data; + if (width) *width = info->width; + if (height) *height = info->height; + if (npixels) *npixels = info->width * info->height; + if (inv) *inv = GB_IMAGE_FMT_IS_SWAPPED(info->format); //(info->format == GB_IMAGE_RGBA || info->format == GB_IMAGE_RGBX); +} + +static inline int between0And255 (int val) +{ + if (val < 0) + return 0; + else if (val > 255) + return 255; + else + return val; +} + +static inline uint RGBA(int r, int g, int b, int a) +{ + return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); +} + +static inline int R(uint rgb ) { return (int)((rgb >> 16) & 0xff); } +static inline int G(uint rgb ) { return (int)((rgb >> 8) & 0xff); } +static inline int B(uint rgb ) { return (int)(rgb & 0xff); } +static inline int A(uint rgb ) { return (int)((rgb >> 24) & 0xff); } + +static inline uint invert(uint col) +{ + return ((col & 0xFF)) << 16 | ((col & 0xFF0000)) >> 16 | (col & 0xFF00FF00); +} + +static inline int myRound(double d) +{ + return d >= 0.0 ? int(d + 0.5) : int( d - ((int)d-1) + 0.5 ) + ((int)d-1); +} + +static inline int brightness (int base, int strength) +{ + return between0And255(base + strength * 255 / 50); +} + +static inline int contrast (int base, int strength) +{ + return between0And255 ((base - 127) * (strength + 50) / 50 + 127); +} + +static inline int gamma (int base, int strength) +{ + return between0And255 (myRound(255.0 * pow (base / 255.0, 1.0 / pow (10, strength / 50.0)))); +} + +static inline int brightnessContrastGamma(int base, int newBrightness, int newContrast, int newGamma) +{ + return gamma(contrast(brightness(base, newBrightness), newContrast), newGamma); +} + +void Effect::balance(GB_IMAGE img, int channels, int brightness, int contrast, int gamma) +{ + uchar transformRed[256], transformGreen[256], transformBlue[256]; + uint i; + uint np; + uint *p; + bool inv; + uint col; + + get_info(img, &p, NULL, NULL, &np, &inv); + + if (!inv) + { + for (i = 0; i < 256; i++) + { + uchar applied = brightnessContrastGamma(i, brightness, contrast, gamma); + + transformRed[i] = (channels & Red) ? applied : i; + transformGreen[i] = (channels & Green) ? applied : i; + transformBlue[i] = (channels & Blue) ? applied : i; + } + } + else + { + for (i = 0; i < 256; i++) + { + uchar applied = brightnessContrastGamma(i, brightness, contrast, gamma); + + transformBlue[i] = (channels & Red) ? applied : i; + transformGreen[i] = (channels & Green) ? applied : i; + transformRed[i] = (channels & Blue) ? applied : i; + } + } + + for (i = 0; i < np; i++) + { + col = p[i]; + p[i] = RGBA(transformRed[R(col)], transformGreen[G(col)], transformBlue[B(col)], A(col)); + } +} + +void Effect::invert(GB_IMAGE img, int channels) +{ + uint np; + uint *p; + bool inv; + uint mask; + uint i; + + get_info(img, &p, NULL, NULL, &np, &inv); + + if (!inv) + mask = RGBA((channels & Red) ? 0xFF : 0, (channels & Green) ? 0xFF : 0, (channels & Blue) ? 0xFF : 0, 0); + else + mask = RGBA((channels & Blue) ? 0xFF : 0, (channels & Green) ? 0xFF : 0, (channels & Red) ? 0xFF : 0, 0); + + for (i = 0; i < np; i++) + p[i] ^= mask; +} + diff --git a/main/lib/image.effect/effect.h b/main/lib/image.effect/effect.h new file mode 100644 index 00000000..1867a395 --- /dev/null +++ b/main/lib/image.effect/effect.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + effect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EFFECT_H +#define __EFFECT_H + +#include "main.h" + +class Effect +{ +public: + + enum { + Red = 1, + Green = 2, + Blue = 4, + All = 7 + }; + + static void balance(GB_IMAGE img, int channels, int brightness, int contrast, int gamma); + static void invert(GB_IMAGE img, int channels); +}; + +#endif diff --git a/main/lib/image.effect/gb.image.effect.component b/main/lib/image.effect/gb.image.effect.component new file mode 100644 index 00000000..f0c721ea --- /dev/null +++ b/main/lib/image.effect/gb.image.effect.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.image.component +Name=Image processing component +Author=Benoît Minisini +Requires=gb.image diff --git a/main/lib/image.effect/kcpuinfo.cpp b/main/lib/image.effect/kcpuinfo.cpp new file mode 100644 index 00000000..ba509c84 --- /dev/null +++ b/main/lib/image.effect/kcpuinfo.cpp @@ -0,0 +1,212 @@ +/*************************************************************************** + + kcpuinfo.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Fredrik H�glund + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include "kcpuinfo.h" + + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +# define HAVE_GNU_INLINE_ASM +#endif + + +/* +// Copied from kdecore/kglobal.h +#if __GNUC__ - 0 > 3 || (__GNUC__ - 0 == 3 && __GNUC_MINOR__ - 0 > 2) +# define KDE_NO_EXPORT __attribute__ ((visibility("hidden"))) +#else +# define KDE_NO_EXPORT +#endif +*/ +#define KDE_NO_EXPORT + +typedef void (*kde_sighandler_t) (int); + +/* +#ifdef __i386__ +static jmp_buf KDE_NO_EXPORT env; + +// Sighandler for the SSE OS support check +static void KDE_NO_EXPORT sighandler( int ) +{ + std::longjmp( env, 1 ); +} +#endif + +#ifdef __PPC__ +static sigjmp_buf KDE_NO_EXPORT jmpbuf; +static sig_atomic_t KDE_NO_EXPORT canjump = 0; + +static void KDE_NO_EXPORT sigill_handler( int sig ) +{ + if ( !canjump ) { + signal( sig, SIG_DFL ); + raise( sig ); + } + canjump = 0; + siglongjmp( jmpbuf, 1 ); +} +#endif +*/ + +static int KDE_NO_EXPORT getCpuFeatures() +{ + int features = 0; + +#if defined( HAVE_GNU_INLINE_ASM ) +#if defined( __i386__ ) + bool haveCPUID = false; + bool have3DNOW = false; + int result = 0; + + // First check if the CPU supports the CPUID instruction + __asm__ __volatile__( + // Try to toggle the CPUID bit in the EFLAGS register + "pushf \n\t" // Push the EFLAGS register onto the stack + "popl %%ecx \n\t" // Pop the value into ECX + "movl %%ecx, %%edx \n\t" // Copy ECX to EDX + "xorl $0x00200000, %%ecx \n\t" // Toggle bit 21 (CPUID) in ECX + "pushl %%ecx \n\t" // Push the modified value onto the stack + "popf \n\t" // Pop it back into EFLAGS + + // Check if the CPUID bit was successfully toggled + "pushf \n\t" // Push EFLAGS back onto the stack + "popl %%ecx \n\t" // Pop the value into ECX + "xorl %%eax, %%eax \n\t" // Zero out the EAX register + "cmpl %%ecx, %%edx \n\t" // Compare ECX with EDX + "je .Lno_cpuid_support%= \n\t" // Jump if they're identical + "movl $1, %%eax \n\t" // Set EAX to true + ".Lno_cpuid_support%=: \n\t" + : "=a"(haveCPUID) : : "%ecx", "%edx" ); + + // If we don't have CPUID we won't have the other extensions either + if ( ! haveCPUID ) + return 0L; + + // Execute CPUID with the feature request bit set + __asm__ __volatile__( + "pushl %%ebx \n\t" // Save EBX + "movl $1, %%eax \n\t" // Set EAX to 1 (features request) + "cpuid \n\t" // Call CPUID + "popl %%ebx \n\t" // Restore EBX + : "=d"(result) : : "%eax", "%ecx" ); + + // Test bit 23 (MMX support) + if ( result & 0x00800000 ) + features |= KCPUInfo::IntelMMX; + + __asm__ __volatile__( + "pushl %%ebx \n\t" + "movl $0x80000000, %%eax \n\t" + "cpuid \n\t" + "cmpl $0x80000000, %%eax \n\t" + "jbe .Lno_extended%= \n\t" + "movl $0x80000001, %%eax \n\t" + "cpuid \n\t" + "test $0x80000000, %%edx \n\t" + "jz .Lno_extended%= \n\t" + "movl $1, %%eax \n\t" // // Set EAX to true + ".Lno_extended%=: \n\t" + "popl %%ebx \n\t" // Restore EBX + : "=a"(have3DNOW) : ); + + if ( have3DNOW ) + features |= KCPUInfo::AMD3DNOW; + +#ifdef HAVE_X86_SSE + // Test bit 25 (SSE support) + if ( result & 0x00200000 ) { + features |= KCPUInfo::IntelSSE; + + // OS support test for SSE. + // Install our own sighandler for SIGILL. + kde_sighandler_t oldhandler = std::signal( SIGILL, sighandler ); + + // Try executing an SSE insn to see if we get a SIGILL + if ( setjmp( env ) ) + features ^= KCPUInfo::IntelSSE; // The OS support test failed + else + __asm__ __volatile__("xorps %xmm0, %xmm0"); + + // Restore the default sighandler + std::signal( SIGILL, oldhandler ); + + // Test bit 26 (SSE2 support) + if ( (result & 0x00400000) && (features & KCPUInfo::IntelSSE) ) + features |= KCPUInfo::IntelSSE2; + + // Note: The OS requirements for SSE2 are the same as for SSE + // so we don't have to do any additional tests for that. + } +#endif // HAVE_X86_SSE +#elif defined __PPC__ && defined HAVE_PPC_ALTIVEC + signal( SIGILL, sigill_handler ); + if ( sigsetjmp( jmpbuf, 1 ) ) { + signal( SIGILL, SIG_DFL ); + } else { + canjump = 1; + __asm__ __volatile__( "mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : /* none */ + : "r" (-1) ); + signal( SIGILL, SIG_DFL ); + features |= KCPUInfo::AltiVec; + } +#endif // __i386__ +#endif //HAVE_GNU_INLINE_ASM + + return features; +} + +unsigned int KCPUInfo::s_features = getCpuFeatures(); + + diff --git a/main/lib/image.effect/kcpuinfo.h b/main/lib/image.effect/kcpuinfo.h new file mode 100644 index 00000000..1fceb7b9 --- /dev/null +++ b/main/lib/image.effect/kcpuinfo.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + kcpuinfo.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Fredrik Höglund + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __KCPUINFO_H +#define __KCPUINFO_H + + +/** + * This class provides a means for applications to obtain information at + * runtime about processor support for certain architecture extensions, + * such as MMX, SSE, 3DNow and AltiVec. + * + * @since 3.2 + */ +class KCPUInfo +{ + public: + /** + * This enum contains the list of architecture extensions you + * can query. + */ + enum Extensions { + IntelMMX = 1 << 0, //!< Intel's MMX instructions. + IntelSSE = 1 << 1, //!< Intel's SSE instructions. + IntelSSE2 = 1 << 2, //!< Intel's SSE2 instructions. + AMD3DNOW = 1 << 3, //!< AMD 3DNOW instructions + AltiVec = 1 << 4 //!< Motorola AltiVec instructions + }; + + /** + * Returns true if the processor supports @p extension, + * and false otherwise. + * + * @param extension the feature to query. + * @return If true, the processor supports @p extension. + * @see Extensions + */ + static bool haveExtension( unsigned int extension ) + { return s_features & extension; } + + private: + static unsigned int s_features; +}; + +#endif + diff --git a/main/lib/image.effect/kimageeffect.cpp b/main/lib/image.effect/kimageeffect.cpp new file mode 100644 index 00000000..3e6c44f2 --- /dev/null +++ b/main/lib/image.effect/kimageeffect.cpp @@ -0,0 +1,5043 @@ +/*************************************************************************** + + kimageeffect.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* This file is part of the KDE libraries + Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley + (C) 1998, 1999 Christian Tibirna + (C) 1998, 1999 Dirk A. Mueller + (C) 1999 Geert Jansen + (C) 2000 Josef Weidendorfer + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +// $Id: kimageeffect.cpp,v 1.50.2.1 2004/01/23 19:04:06 orlovich Exp $ + +#include +#include + +#include +#include + +#include "qcolor.h" +#include "qimage.h" +#include "kimageeffect.h" +#include "kcpuinfo.h" + +#include + +#if 0 +#if defined(__i386__) && ( defined(__GNUC__) || defined(__INTEL_COMPILER) ) +# if defined( HAVE_X86_MMX ) +# define USE_MMX_INLINE_ASM +# endif +# if defined( HAVE_X86_SSE2 ) +# define USE_SSE2_INLINE_ASM +# endif +#endif +#endif + +//====================================================================== +// +// Utility stuff for effects ported from ImageMagick to QImage +// +//====================================================================== +#define MaxRGB 255L +#define DegreesToRadians(x) ((x)*M_PI/180.0) +#define MagickSQ2PI 2.50662827463100024161235523934010416269302368164062 +#define MagickEpsilon 1.0e-12 +#define MagickPI 3.14159265358979323846264338327950288419716939937510 + +static inline unsigned int intensityValue(unsigned int color) +{ + return((unsigned int)((0.299*qRed(color) + + 0.587*qGreen(color) + + 0.1140000000000001*qBlue(color)))); +} + +/*static inline void liberateMemory(void **memory) +{ + assert(memory != (void **)NULL); + if(*memory == (void *)NULL) return; + free(*memory); + *memory=(void *) NULL; +}*/ +#define liberateMemory(_pmemory) \ +({ \ + free(*(_pmemory)); \ + *(_pmemory) = NULL; \ +}) + +struct double_packet +{ + double red; + double green; + double blue; + double alpha; +}; + +struct short_packet +{ + unsigned short int red; + unsigned short int green; + unsigned short int blue; + unsigned short int alpha; +}; + + +// Functions for managing BGRA images + +static inline uint invert(uint col) +{ + return ((col & 0xFF)) << 16 | ((col & 0xFF0000)) >> 16 | (col & 0xFF00FF00); +} + +#define INVERT(_exp) \ + if (image.inverted()) \ + { \ + _exp = invert(_exp); \ + } + +//====================================================================== +// +// Gradient effects +// +//====================================================================== + +QImage KImageEffect::gradient(const QSize &size, const QColor &ca, + const QColor &cb, GradientType eff, int ncols) +{ + int rDiff, gDiff, bDiff; + int rca, gca, bca, rcb, gcb, bcb; + + QImage image(size, false); + + if (size.width() == 0 || size.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::gradient: invalid image" << std::endl; +#endif + return image; + } + + int x, y; + + rDiff = (rcb = cb.red()) - (rca = ca.red()); + gDiff = (gcb = cb.green()) - (gca = ca.green()); + bDiff = (bcb = cb.blue()) - (bca = ca.blue()); + + if( eff == VerticalGradient || eff == HorizontalGradient ){ + + uint *p; + uint rgb; + + int rl = rca << 16; + int gl = gca << 16; + int bl = bca << 16; + + if( eff == VerticalGradient ) { + + int rcdelta = ((1<<16) / size.height()) * rDiff; + int gcdelta = ((1<<16) / size.height()) * gDiff; + int bcdelta = ((1<<16) / size.height()) * bDiff; + + for ( y = 0; y < size.height(); y++ ) { + p = (uint *) image.scanLine(y); + + rl += rcdelta; + gl += gcdelta; + bl += bcdelta; + + rgb = qRgb( (rl>>16), (gl>>16), (bl>>16) ); + INVERT(rgb); + + for( x = 0; x < size.width(); x++ ) { + *p = rgb; + p++; + } + } + + } + else { // must be HorizontalGradient + + unsigned int *o_src = (unsigned int *)image.scanLine(0); + unsigned int *src = o_src; + uint rgb; + + int rcdelta = ((1<<16) / size.width()) * rDiff; + int gcdelta = ((1<<16) / size.width()) * gDiff; + int bcdelta = ((1<<16) / size.width()) * bDiff; + + for( x = 0; x < size.width(); x++) { + + rl += rcdelta; + gl += gcdelta; + bl += bcdelta; + + rgb = qRgb( (rl>>16), (gl>>16), (bl>>16)); + INVERT(rgb); + + *src++ = rgb; + } + + src = o_src; + + // Believe it or not, manually copying in a for loop is faster + // than calling memcpy for each scanline (on the order of ms...). + // I think this is due to the function call overhead (mosfet). + + for (y = 1; y < size.height(); ++y) { + + p = (unsigned int *)image.scanLine(y); + src = o_src; + for(x=0; x < size.width(); ++x) + *p++ = *src++; + } + } + } + + else { + + float rfd, gfd, bfd; + float rd = rca, gd = gca, bd = bca; + + unsigned char *xtable[3]; + unsigned char *ytable[3]; + + unsigned int w = size.width(), h = size.height(); + xtable[0] = new unsigned char[w]; + xtable[1] = new unsigned char[w]; + xtable[2] = new unsigned char[w]; + ytable[0] = new unsigned char[h]; + ytable[1] = new unsigned char[h]; + ytable[2] = new unsigned char[h]; + w*=2, h*=2; + + if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) { + // Diagonal dgradient code inspired by BlackBox (mosfet) + // BlackBox dgradient is (C) Brad Hughes, and + // Mike Cole . + + rfd = (float)rDiff/w; + gfd = (float)gDiff/w; + bfd = (float)bDiff/w; + + int dir; + for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) { + dir = eff == DiagonalGradient? x : size.width() - x - 1; + xtable[0][dir] = (unsigned char) rd; + xtable[1][dir] = (unsigned char) gd; + xtable[2][dir] = (unsigned char) bd; + } + rfd = (float)rDiff/h; + gfd = (float)gDiff/h; + bfd = (float)bDiff/h; + rd = gd = bd = 0; + for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) { + ytable[0][y] = (unsigned char) rd; + ytable[1][y] = (unsigned char) gd; + ytable[2][y] = (unsigned char) bd; + } + + for (y = 0; y < size.height(); y++) { + unsigned int *scanline = (unsigned int *)image.scanLine(y); + for (x = 0; x < size.width(); x++) { + scanline[x] = qRgb(xtable[0][x] + ytable[0][y], + xtable[1][x] + ytable[1][y], + xtable[2][x] + ytable[2][y]); + INVERT(scanline[x]); + } + } + } + + else if (eff == RectangleGradient || + eff == PyramidGradient || + eff == PipeCrossGradient || + eff == EllipticGradient) + { + int rSign = rDiff>0? 1: -1; + int gSign = gDiff>0? 1: -1; + int bSign = bDiff>0? 1: -1; + + rfd = (float)rDiff / size.width(); + gfd = (float)gDiff / size.width(); + bfd = (float)bDiff / size.width(); + + rd = (float)rDiff/2; + gd = (float)gDiff/2; + bd = (float)bDiff/2; + + for (x = 0; x < size.width(); x++, rd-=rfd, gd-=gfd, bd-=bfd) + { + xtable[0][x] = (unsigned char) abs((int)rd); + xtable[1][x] = (unsigned char) abs((int)gd); + xtable[2][x] = (unsigned char) abs((int)bd); + } + + rfd = (float)rDiff/size.height(); + gfd = (float)gDiff/size.height(); + bfd = (float)bDiff/size.height(); + + rd = (float)rDiff/2; + gd = (float)gDiff/2; + bd = (float)bDiff/2; + + for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd) + { + ytable[0][y] = (unsigned char) abs((int)rd); + ytable[1][y] = (unsigned char) abs((int)gd); + ytable[2][y] = (unsigned char) abs((int)bd); + } + unsigned int rgb; + int h = (size.height()+1)>>1; + for (y = 0; y < h; y++) { + unsigned int *sl1 = (unsigned int *)image.scanLine(y); + unsigned int *sl2 = (unsigned int *)image.scanLine(QMAX(size.height()-y-1, y)); + + int w = (size.width()+1)>>1; + int x2 = size.width()-1; + + for (x = 0; x < w; x++, x2--) { + rgb = 0; + if (eff == PyramidGradient) { + rgb = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]), + gcb-gSign*(xtable[1][x]+ytable[1][y]), + bcb-bSign*(xtable[2][x]+ytable[2][y])); + } + else if (eff == RectangleGradient) { + rgb = qRgb(rcb - rSign * + QMAX(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMAX(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMAX(xtable[2][x], ytable[2][y]) * 2); + } + else if (eff == PipeCrossGradient) { + rgb = qRgb(rcb - rSign * + QMIN(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMIN(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMIN(xtable[2][x], ytable[2][y]) * 2); + } + else if (eff == EllipticGradient) { + rgb = qRgb(rcb - rSign * + (int)sqrt((xtable[0][x]*xtable[0][x] + + ytable[0][y]*ytable[0][y])*2.0), + gcb - gSign * + (int)sqrt((xtable[1][x]*xtable[1][x] + + ytable[1][y]*ytable[1][y])*2.0), + bcb - bSign * + (int)sqrt((xtable[2][x]*xtable[2][x] + + ytable[2][y]*ytable[2][y])*2.0)); + } + + //INVERT(rgb); + sl1[x] = sl2[x] = rgb; + sl1[x2] = sl2[x2] = rgb; + } + } + + if (image.inverted()) + image.invert(); + } + + delete [] xtable[0]; + delete [] xtable[1]; + delete [] xtable[2]; + delete [] ytable[0]; + delete [] ytable[1]; + delete [] ytable[2]; + } + +#if 0 + // dither if necessary + if (ncols && (QPixmap::defaultDepth() < 15 )) { + if ( ncols < 2 || ncols > 256 ) + ncols = 3; + QColor *dPal = new QColor[ncols]; + for (int i=0; i 200 ) xfactor = 200; + if (yfactor > 200 ) yfactor = 200; + + + // float xbal = xfactor/5000.; + // float ybal = yfactor/5000.; + float xbal = xfactor/30./size.width(); + float ybal = yfactor/30./size.height(); + float rat; + + int rDiff, gDiff, bDiff; + int rca, gca, bca, rcb, gcb, bcb; + + QImage image(size, false); + + if (size.width() == 0 || size.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::unbalancedGradient : invalid image\n"; +#endif + return image; + } + + int x, y; + unsigned int *scanline; + + rDiff = (rcb = cb.red()) - (rca = ca.red()); + gDiff = (gcb = cb.green()) - (gca = ca.green()); + bDiff = (bcb = cb.blue()) - (bca = ca.blue()); + + if( eff == VerticalGradient || eff == HorizontalGradient){ + QColor cRow; + + uint *p; + uint rgbRow; + + if( eff == VerticalGradient) { + for ( y = 0; y < size.height(); y++ ) { + dir = _yanti ? y : size.height() - 1 - y; + p = (uint *) image.scanLine(dir); + rat = 1 - exp( - (float)y * ybal ); + + cRow.setRgb( rcb - (int) ( rDiff * rat ), + gcb - (int) ( gDiff * rat ), + bcb - (int) ( bDiff * rat ) ); + + rgbRow = cRow.rgb(); + INVERT(rgbRow); + + for( x = 0; x < size.width(); x++ ) { + *p = rgbRow; + p++; + } + } + } + else { + + unsigned int *src = (unsigned int *)image.scanLine(0); + for(x = 0; x < size.width(); x++ ) + { + dir = _xanti ? x : size.width() - 1 - x; + rat = 1 - exp( - (float)x * xbal ); + + src[dir] = qRgb(rcb - (int) ( rDiff * rat ), + gcb - (int) ( gDiff * rat ), + bcb - (int) ( bDiff * rat )); + INVERT(src[dir]); + } + + // Believe it or not, manually copying in a for loop is faster + // than calling memcpy for each scanline (on the order of ms...). + // I think this is due to the function call overhead (mosfet). + + for(y = 1; y < size.height(); ++y) + { + scanline = (unsigned int *)image.scanLine(y); + for(x=0; x < size.width(); ++x) + scanline[x] = src[x]; + } + } + } + + else { + int w=size.width(), h=size.height(); + + unsigned char *xtable[3]; + unsigned char *ytable[3]; + xtable[0] = new unsigned char[w]; + xtable[1] = new unsigned char[w]; + xtable[2] = new unsigned char[w]; + ytable[0] = new unsigned char[h]; + ytable[1] = new unsigned char[h]; + ytable[2] = new unsigned char[h]; + + if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) + { + for (x = 0; x < w; x++) { + dir = _xanti ? x : w - 1 - x; + rat = 1 - exp( - (float)x * xbal ); + + xtable[0][dir] = (unsigned char) ( rDiff/2 * rat ); + xtable[1][dir] = (unsigned char) ( gDiff/2 * rat ); + xtable[2][dir] = (unsigned char) ( bDiff/2 * rat ); + } + + for (y = 0; y < h; y++) { + dir = _yanti ? y : h - 1 - y; + rat = 1 - exp( - (float)y * ybal ); + + ytable[0][dir] = (unsigned char) ( rDiff/2 * rat ); + ytable[1][dir] = (unsigned char) ( gDiff/2 * rat ); + ytable[2][dir] = (unsigned char) ( bDiff/2 * rat ); + } + + for (y = 0; y < h; y++) { + unsigned int *scanline = (unsigned int *)image.scanLine(y); + for (x = 0; x < w; x++) { + scanline[x] = qRgb(rcb - (xtable[0][x] + ytable[0][y]), + gcb - (xtable[1][x] + ytable[1][y]), + bcb - (xtable[2][x] + ytable[2][y])); + INVERT(scanline[x]); + } + } + } + + else if (eff == RectangleGradient || + eff == PyramidGradient || + eff == PipeCrossGradient || + eff == EllipticGradient) + { + int rSign = rDiff>0? 1: -1; + int gSign = gDiff>0? 1: -1; + int bSign = bDiff>0? 1: -1; + + for (x = 0; x < w; x++) + { + dir = _xanti ? x : w - 1 - x; + rat = 1 - exp( - (float)x * xbal ); + + xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat))); + xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat))); + xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat))); + } + + for (y = 0; y < h; y++) + { + dir = _yanti ? y : h - 1 - y; + + rat = 1 - exp( - (float)y * ybal ); + + ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat))); + ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat))); + ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat))); + } + + for (y = 0; y < h; y++) { + unsigned int *scanline = (unsigned int *)image.scanLine(y); + for (x = 0; x < w; x++) { + if (eff == PyramidGradient) + { + scanline[x] = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]), + gcb-gSign*(xtable[1][x]+ytable[1][y]), + bcb-bSign*(xtable[2][x]+ytable[2][y])); + } + if (eff == RectangleGradient) + { + scanline[x] = qRgb(rcb - rSign * + QMAX(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMAX(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMAX(xtable[2][x], ytable[2][y]) * 2); + } + if (eff == PipeCrossGradient) + { + scanline[x] = qRgb(rcb - rSign * + QMIN(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMIN(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMIN(xtable[2][x], ytable[2][y]) * 2); + } + if (eff == EllipticGradient) + { + scanline[x] = qRgb(rcb - rSign * + (int)sqrt((xtable[0][x]*xtable[0][x] + + ytable[0][y]*ytable[0][y])*2.0), + gcb - gSign * + (int)sqrt((xtable[1][x]*xtable[1][x] + + ytable[1][y]*ytable[1][y])*2.0), + bcb - bSign * + (int)sqrt((xtable[2][x]*xtable[2][x] + + ytable[2][y]*ytable[2][y])*2.0)); + } + INVERT(scanline[x]); + } + } + } + + #if 0 + if (ncols && (QPixmap::defaultDepth() < 15 )) { + if ( ncols < 2 || ncols > 256 ) + ncols = 3; + QColor *dPal = new QColor[ncols]; + for (int i=0; i 8 ? 256 : image.numColors(); + int pixels = image.depth() > 8 ? image.width()*image.height() : + image.numColors(); + unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() : + (unsigned int *)image.colorTable(); + + bool brighten = (percent >= 0); + if(percent < 0) + percent = -percent; + + fprintf(stderr, "image: %d x %d = %d\n", image.width(), image.height(), pixels); + +#ifdef USE_MMX_INLINE_ASM + bool haveMMX = KCPUInfo::haveExtension( KCPUInfo::IntelMMX ); + + if(haveMMX) + { + Q_UINT16 p = Q_UINT16(256.0f*(percent)); + KIE4Pack mult = {{p,p,p,0}}; + + __asm__ __volatile__( + "pxor %%mm7, %%mm7\n\t" // zero mm7 for unpacking + "movq (%0), %%mm6\n\t" // copy intensity change to mm6 + : : "r"(&mult), "m"(mult)); + + unsigned int rem = pixels % 4; + pixels -= rem; + Q_UINT32 *end = ( data + pixels ); + + if (brighten) + { + while ( data != end ) { + __asm__ __volatile__( + "movq (%0), %%mm0\n\t" + "movq 8(%0), %%mm4\n\t" // copy 4 pixels of data to mm0 and mm4 + "movq %%mm0, %%mm1\n\t" + "movq %%mm0, %%mm3\n\t" + "movq %%mm4, %%mm5\n\t" // copy to registers for unpacking + "punpcklbw %%mm7, %%mm0\n\t" + "punpckhbw %%mm7, %%mm1\n\t" // unpack the two pixels from mm0 + "pmullw %%mm6, %%mm0\n\t" + "punpcklbw %%mm7, %%mm4\n\t" + "pmullw %%mm6, %%mm1\n\t" // multiply by intensity*256 + "psrlw $8, %%mm0\n\t" // divide by 256 + "pmullw %%mm6, %%mm4\n\t" + "psrlw $8, %%mm1\n\t" + "psrlw $8, %%mm4\n\t" + "packuswb %%mm1, %%mm0\n\t" // pack solution into mm0. saturates at 255 + "movq %%mm5, %%mm1\n\t" + + "punpckhbw %%mm7, %%mm1\n\t" // unpack 4th pixel in mm1 + + "pmullw %%mm6, %%mm1\n\t" + "paddusb %%mm3, %%mm0\n\t" // add intesity result to original of mm0 + "psrlw $8, %%mm1\n\t" + "packuswb %%mm1, %%mm4\n\t" // pack upper two pixels into mm4 + + "movq %%mm0, (%0)\n\t" // rewrite to memory lower two pixels + "paddusb %%mm5, %%mm4\n\t" + "movq %%mm4, 8(%0)\n\t" // rewrite upper two pixels + : : "r"(data) ); + data += 4; + } + + end += rem; + while ( data != end ) { + __asm__ __volatile__( + "movd (%0), %%mm0\n\t" // repeat above but for + "punpcklbw %%mm7, %%mm0\n\t" // one pixel at a time + "movq %%mm0, %%mm3\n\t" + "pmullw %%mm6, %%mm0\n\t" + "psrlw $8, %%mm0\n\t" + "paddw %%mm3, %%mm0\n\t" + "packuswb %%mm0, %%mm0\n\t" + "movd %%mm0, (%0)\n\t" + : : "r"(data) ); + data++; + } + } + else + { + while ( data != end ) { + __asm__ __volatile__( + "movq (%0), %%mm0\n\t" + "movq 8(%0), %%mm4\n\t" + "movq %%mm0, %%mm1\n\t" + "movq %%mm0, %%mm3\n\t" + + "movq %%mm4, %%mm5\n\t" + + "punpcklbw %%mm7, %%mm0\n\t" + "punpckhbw %%mm7, %%mm1\n\t" + "pmullw %%mm6, %%mm0\n\t" + "punpcklbw %%mm7, %%mm4\n\t" + "pmullw %%mm6, %%mm1\n\t" + "psrlw $8, %%mm0\n\t" + "pmullw %%mm6, %%mm4\n\t" + "psrlw $8, %%mm1\n\t" + "psrlw $8, %%mm4\n\t" + "packuswb %%mm1, %%mm0\n\t" + "movq %%mm5, %%mm1\n\t" + + "punpckhbw %%mm7, %%mm1\n\t" + + "pmullw %%mm6, %%mm1\n\t" + "psubusb %%mm0, %%mm3\n\t" // subtract darkening amount + "psrlw $8, %%mm1\n\t" + "packuswb %%mm1, %%mm4\n\t" + + "movq %%mm3, (%0)\n\t" + "psubusb %%mm4, %%mm5\n\t" // only change for this version is + "movq %%mm5, 8(%0)\n\t" // subtraction here as we are darkening image + : : "r"(data) ); + data += 4; + } + + end += rem; + while ( data != end ) { + __asm__ __volatile__( + "movd (%0), %%mm0\n\t" + "punpcklbw %%mm7, %%mm0\n\t" + "movq %%mm0, %%mm3\n\t" + "pmullw %%mm6, %%mm0\n\t" + "psrlw $8, %%mm0\n\t" + "psubusw %%mm0, %%mm3\n\t" + "packuswb %%mm3, %%mm3\n\t" + "movd %%mm3, (%0)\n\t" + : : "r"(data) ); + data++; + } + } + __asm__ __volatile__("emms"); // clear mmx state + } + else +#endif // USE_MMX_INLINE_ASM + { + unsigned char *segTbl = new unsigned char[segColors]; + int tmp; + if(brighten){ // keep overflow check out of loops + for(int i=0; i < segColors; ++i){ + tmp = (int)(i*percent); + if(tmp > 255) + tmp = 255; + segTbl[i] = tmp; + } + } + else{ + for(int i=0; i < segColors; ++i){ + tmp = (int)(i*percent); + if(tmp < 0) + tmp = 0; + segTbl[i] = tmp; + } + } + + if(brighten){ // same here + for(int i=0; i < pixels; ++i){ + int r = qRed(data[i]); + int g = qGreen(data[i]); + int b = qBlue(data[i]); + int a = qAlpha(data[i]); + r = r + segTbl[r] > 255 ? 255 : r + segTbl[r]; + g = g + segTbl[g] > 255 ? 255 : g + segTbl[g]; + b = b + segTbl[b] > 255 ? 255 : b + segTbl[b]; + data[i] = qRgba(r, g, b,a); + INVERT(data[i]); + } + } + else{ + for(int i=0; i < pixels; ++i){ + int r = qRed(data[i]); + int g = qGreen(data[i]); + int b = qBlue(data[i]); + int a = qAlpha(data[i]); + r = r - segTbl[r] < 0 ? 0 : r - segTbl[r]; + g = g - segTbl[g] < 0 ? 0 : g - segTbl[g]; + b = b - segTbl[b] < 0 ? 0 : b - segTbl[b]; + data[i] = qRgba(r, g, b, a); + INVERT(data[i]); + } + } + delete [] segTbl; + } + + return image; +} + +QImage& KImageEffect::channelIntensity(QImage &image, float percent, + RGBComponent channel) +{ + if (image.width() == 0 || image.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::channelIntensity : invalid image\n"; +#endif + return image; + } + + int segColors = image.depth() > 8 ? 256 : image.numColors(); + unsigned char *segTbl = new unsigned char[segColors]; + int pixels = image.depth() > 8 ? image.width()*image.height() : + image.numColors(); + unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() : + (unsigned int *)image.colorTable(); + bool brighten = (percent >= 0); + if(percent < 0) + percent = -percent; + + if(brighten){ // keep overflow check out of loops + for(int i=0; i < segColors; ++i){ + int tmp = (int)(i*percent); + if(tmp > 255) + tmp = 255; + segTbl[i] = tmp; + } + } + else{ + for(int i=0; i < segColors; ++i){ + int tmp = (int)(i*percent); + if(tmp < 0) + tmp = 0; + segTbl[i] = tmp; + } + } + + if(brighten){ // same here + if(channel == Red){ // and here ;-) + for(int i=0; i < pixels; ++i){ + int c = qRed(data[i]); + c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; + data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else if(channel == Green){ + for(int i=0; i < pixels; ++i){ + int c = qGreen(data[i]); + c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; + data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else{ + for(int i=0; i < pixels; ++i){ + int c = qBlue(data[i]); + c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; + data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i])); + INVERT(data[i]); + } + } + + } + else{ + if(channel == Red){ + for(int i=0; i < pixels; ++i){ + int c = qRed(data[i]); + c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; + data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else if(channel == Green){ + for(int i=0; i < pixels; ++i){ + int c = qGreen(data[i]); + c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; + data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else{ + for(int i=0; i < pixels; ++i){ + int c = qBlue(data[i]); + c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; + data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i])); + INVERT(data[i]); + } + } + } + delete [] segTbl; + + return image; +} + + +#if 0 +// Modulate an image with an RBG channel of another image +// +QImage& KImageEffect::modulate(QImage &image, QImage &modImage, bool reverse, + ModulationType type, int factor, RGBComponent channel) +{ + if (image.width() == 0 || image.height() == 0 || + modImage.width() == 0 || modImage.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::modulate : invalid image\n"; +#endif + return image; + } + + int r, g, b, h, s, v, a; + QColor clr; + int mod=0; + unsigned int x1, x2, y1, y2; + int x, y; + + // for image, we handle only depth 32 + if (image.depth()<32) image = image.convertDepth(32); + + // for modImage, we handle depth 8 and 32 + if (modImage.depth()<8) modImage = modImage.convertDepth(8); + + unsigned int *colorTable2 = (modImage.depth()==8) ? + modImage.colorTable():0; + unsigned int *data1, *data2; + unsigned char *data2b; + unsigned int color1, color2; + + x1 = image.width(); y1 = image.height(); + x2 = modImage.width(); y2 = modImage.height(); + + for (y = 0; y < (int)y1; y++) { + data1 = (unsigned int *) image.scanLine(y); + data2 = (unsigned int *) modImage.scanLine( y%y2 ); + data2b = (unsigned char *) modImage.scanLine( y%y2 ); + + x=0; + while(x < (int)x1) { + color2 = (colorTable2) ? colorTable2[*data2b] : *data2; + if (reverse) { + color1 = color2; + color2 = *data1; + } + else + color1 = *data1; + + if (type == Intensity || type == Contrast) { + r = qRed(color1); + g = qGreen(color1); + b = qBlue(color1); + if (channel != All) { + mod = (channel == Red) ? qRed(color2) : + (channel == Green) ? qGreen(color2) : + (channel == Blue) ? qBlue(color2) : + (channel == Gray) ? qGray(color2) : 0; + mod = mod*factor/50; + } + + if (type == Intensity) { + if (channel == All) { + r += r * factor/50 * qRed(color2)/256; + g += g * factor/50 * qGreen(color2)/256; + b += b * factor/50 * qBlue(color2)/256; + } + else { + r += r * mod/256; + g += g * mod/256; + b += b * mod/256; + } + } + else { // Contrast + if (channel == All) { + r += (r-128) * factor/50 * qRed(color2)/128; + g += (g-128) * factor/50 * qGreen(color2)/128; + b += (b-128) * factor/50 * qBlue(color2)/128; + } + else { + r += (r-128) * mod/128; + g += (g-128) * mod/128; + b += (b-128) * mod/128; + } + } + + if (r<0) r=0; if (r>255) r=255; + if (g<0) g=0; if (g>255) g=255; + if (b<0) b=0; if (b>255) b=255; + a = qAlpha(*data1); + *data1 = qRgba(r, g, b, a); + } + else if (type == Saturation || type == HueShift) { + clr.setRgb(color1); + clr.hsv(&h, &s, &v); + mod = (channel == Red) ? qRed(color2) : + (channel == Green) ? qGreen(color2) : + (channel == Blue) ? qBlue(color2) : + (channel == Gray) ? qGray(color2) : 0; + mod = mod*factor/50; + + if (type == Saturation) { + s -= s * mod/256; + if (s<0) s=0; if (s>255) s=255; + } + else { // HueShift + h += mod; + while(h<0) h+=360; + h %= 360; + } + + clr.setHsv(h, s, v); + a = qAlpha(*data1); + *data1 = clr.rgb() | ((uint)(a & 0xff) << 24); + } + data1++; data2++; data2b++; x++; + if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; } + } + } + return image; +} + + + +//====================================================================== +// +// Blend effects +// +//====================================================================== + + +// Nice and fast direct pixel manipulation +QImage& KImageEffect::blend(const QColor& clr, QImage& dst, float opacity) +{ + if (dst.width() <= 0 || dst.height() <= 0) + return dst; + + if (opacity < 0.0 || opacity > 1.0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n"; +#endif + return dst; + } + + int depth = dst.depth(); + if (depth != 32) + dst = dst.convertDepth(32); + + int pixels = dst.width() * dst.height(); + +#ifdef USE_SSE2_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) { + Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 ); + + KIE8Pack packedalpha = { { alpha, alpha, alpha, 256, + alpha, alpha, alpha, 256 } }; + + Q_UINT16 red = Q_UINT16( clr.red() * 256 * opacity ); + Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity ); + Q_UINT16 blue = Q_UINT16( clr.blue() * 256 * opacity ); + + KIE8Pack packedcolor = { { blue, green, red, 0, + blue, green, red, 0 } }; + + // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending + __asm__ __volatile__( + "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking + "movdqu (%0), %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6 + "movdqu (%1), %%xmm5\n\t" // Set up color * alpha * 256 in XMM5 + : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) ); + + Q_UINT32 *data = reinterpret_cast( dst.bits() ); + + // Check how many pixels we need to process to achieve 16 byte alignment + int offset = (16 - (Q_UINT32( data ) & 0x0f)) / 4; + + // The main loop processes 8 pixels / iteration + int remainder = (pixels - offset) % 8; + pixels -= remainder; + + // Alignment loop + for ( int i = 0; i < offset; i++ ) { + __asm__ __volatile__( + "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image + : : "r"(data), "r"(i) ); + } + + // Main loop + for ( int i = offset; i < pixels; i += 8 ) { + __asm__ __volatile( + // Load 8 pixels to XMM registers 1 - 4 + "movq (%0,%1,4), %%xmm0\n\t" // Load pixels 1 and 2 to XMM1 + "movq 8(%0,%1,4), %%xmm1\n\t" // Load pixels 3 and 4 to XMM2 + "movq 16(%0,%1,4), %%xmm2\n\t" // Load pixels 5 and 6 to XMM3 + "movq 24(%0,%1,4), %%xmm3\n\t" // Load pixels 7 and 8 to XMM4 + + // Prefetch the pixels for next iteration + "prefetchnta 32(%0,%1,4) \n\t" + + // Blend pixels 1 and 2 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + + // Blend pixels 3 and 4 + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm1\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm1\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm1\n\t" // Divide by 256 + + // Blend pixels 5 and 6 + "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm2\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm2\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm2\n\t" // Divide by 256 + + // Blend pixels 7 and 8 + "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm3\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm3\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm3\n\t" // Divide by 256 + + // Pack the pixels into 2 double quadwords + "packuswb %%xmm1, %%xmm0\n\t" // Pack pixels 1 - 4 to a double qword + "packuswb %%xmm3, %%xmm2\n\t" // Pack pixles 5 - 8 to a double qword + + // Write the pixels back to the image + "movdqa %%xmm0, (%0,%1,4)\n\t" // Store pixels 1 - 4 + "movdqa %%xmm2, 16(%0,%1,4)\n\t" // Store pixels 5 - 8 + : : "r"(data), "r"(i) ); + } + + // Cleanup loop + for ( int i = pixels; i < pixels + remainder; i++ ) { + __asm__ __volatile__( + "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image + : : "r"(data), "r"(i) ); + } + } else +#endif + +#ifdef USE_MMX_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) { + Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 ); + KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } }; + + Q_UINT16 red = Q_UINT16( clr.red() * 256 * opacity ); + Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity ); + Q_UINT16 blue = Q_UINT16( clr.blue() * 256 * opacity ); + + KIE4Pack packedcolor = { { blue, green, red, 0 } }; + + __asm__ __volatile__( + "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking + "movq (%0), %%mm6\n\t" // Set up (1 - alpha) * 256 in MM6 + "movq (%1), %%mm5\n\t" // Set up color * alpha * 256 in MM5 + : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) ); + + Q_UINT32 *data = reinterpret_cast( dst.bits() ); + + // The main loop processes 4 pixels / iteration + int remainder = pixels % 4; + pixels -= remainder; + + // Main loop + for ( int i = 0; i < pixels; i += 4 ) { + __asm__ __volatile__( + // Load 4 pixels to MM registers 1 - 4 + "movd (%0,%1,4), %%mm0\n\t" // Load the 1st pixel to MM0 + "movd 4(%0,%1,4), %%mm1\n\t" // Load the 2nd pixel to MM1 + "movd 8(%0,%1,4), %%mm2\n\t" // Load the 3rd pixel to MM2 + "movd 12(%0,%1,4), %%mm3\n\t" // Load the 4th pixel to MM3 + + // Blend the first pixel + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm0\n\t" // Divide by 256 + + // Blend the second pixel + "punpcklbw %%mm7, %%mm1\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm1\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm1\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm1\n\t" // Divide by 256 + + // Blend the third pixel + "punpcklbw %%mm7, %%mm2\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm2\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm2\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm2\n\t" // Divide by 256 + + // Blend the fourth pixel + "punpcklbw %%mm7, %%mm3\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm3\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm3\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm3\n\t" // Divide by 256 + + // Pack the pixels into 2 quadwords + "packuswb %%mm1, %%mm0\n\t" // Pack pixels 1 and 2 to a qword + "packuswb %%mm3, %%mm2\n\t" // Pack pixels 3 and 4 to a qword + + // Write the pixels back to the image + "movq %%mm0, (%0,%1,4)\n\t" // Store pixels 1 and 2 + "movq %%mm2, 8(%0,%1,4)\n\t" // Store pixels 3 and 4 + : : "r"(data), "r"(i) ); + } + + // Cleanup loop + for ( int i = pixels; i < pixels + remainder; i++ ) { + __asm__ __volatile__( + "movd (%0,%1,4), %%mm0\n\t" // Load one pixel to MM1 + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with 1 - alpha * 256 + "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm0\n\t" // Divide by 256 + "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword + "movd %%mm0, (%0,%1,4)\n\t" // Write the pixel to the image + : : "r"(data), "r"(i) ); + } + + // Empty the MMX state + __asm__ __volatile__("emms"); + } else +#endif // USE_MMX_INLINE_ASM + + { + int rcol, gcol, bcol; + clr.rgb(&rcol, &gcol, &bcol); + +#ifdef WORDS_BIGENDIAN // ARGB (skip alpha) + unsigned char *data = (unsigned char *)dst.bits() + 1; +#else // BGRA + unsigned char *data = (unsigned char *)dst.bits(); +#endif + + for (int i=0; i 1.0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n"; +#endif + return dst; + } + + if (src.depth() != 32) src = src.convertDepth(32); + if (dst.depth() != 32) dst = dst.convertDepth(32); + + int pixels = src.width() * src.height(); + +#ifdef USE_SSE2_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) { + Q_UINT16 alpha = Q_UINT16( opacity * 256.0 ); + KIE8Pack packedalpha = { { alpha, alpha, alpha, 0, + alpha, alpha, alpha, 0 } }; + + // Prepare the XMM6 and XMM7 registers for unpacking and blending + __asm__ __volatile__( + "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking + "movdqu (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6 + : : "r"(&packedalpha), "m"(packedalpha) ); + + Q_UINT32 *data1 = reinterpret_cast( src.bits() ); + Q_UINT32 *data2 = reinterpret_cast( dst.bits() ); + + // Check how many pixels we need to process to achieve 16 byte alignment + int offset = (16 - (Q_UINT32( data2 ) & 0x0f)) / 4; + + // The main loop processes 4 pixels / iteration + int remainder = (pixels - offset) % 4; + pixels -= remainder; + + // Alignment loop + for ( int i = 0; i < offset; i++ ) { + __asm__ __volatile__( + "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1 + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel + "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm1\n\t" // Multiply dst with 256 + "paddw %%xmm1, %%xmm0\n\t" // Add dst to result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image + : : "r"(data1), "r"(data2), "r"(i) ); + } + + // Main loop + for ( int i = offset; i < pixels; i += 4 ) { + __asm__ __volatile__( + // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3 + "movq (%0,%2,4), %%xmm0\n\t" // Load two src pixels to XMM0 + "movq (%1,%2,4), %%xmm1\n\t" // Load two dst pixels to XMM1 + "movq 8(%0,%2,4), %%xmm2\n\t" // Load two src pixels to XMM2 + "movq 8(%1,%2,4), %%xmm3\n\t" // Load two dst pixels to XMM3 + + // Prefetch the pixels for the iteration after the next one + "prefetchnta 32(%0,%2,4) \n\t" + "prefetchnta 32(%1,%2,4) \n\t" + + // Blend the first two pixels + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the dst pixels + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the src pixels + "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm1\n\t" // Multiply dst with 256 + "paddw %%xmm1, %%xmm0\n\t" // Add dst to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + + // Blend the next two pixels + "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the dst pixels + "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the src pixels + "psubw %%xmm3, %%xmm2\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm2\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm3\n\t" // Multiply dst with 256 + "paddw %%xmm3, %%xmm2\n\t" // Add dst to the result + "psrlw $8, %%xmm2\n\t" // Divide by 256 + + // Write the pixels back to the image + "packuswb %%xmm2, %%xmm0\n\t" // Pack the pixels to a double qword + "movdqa %%xmm0, (%1,%2,4)\n\t" // Store the pixels + : : "r"(data1), "r"(data2), "r"(i) ); + } + + // Cleanup loop + for ( int i = pixels; i < pixels + remainder; i++ ) { + __asm__ __volatile__( + "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1 + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel + "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm1\n\t" // Multiply dst with 256 + "paddw %%xmm1, %%xmm0\n\t" // Add dst to result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image + : : "r"(data1), "r"(data2), "r"(i) ); + } + } else +#endif // USE_SSE2_INLINE_ASM + +#ifdef USE_MMX_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) { + Q_UINT16 alpha = Q_UINT16( opacity * 256.0 ); + KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } }; + + // Prepare the MM6 and MM7 registers for blending and unpacking + __asm__ __volatile__( + "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking + "movq (%0), %%mm6\n\t" // Set up alpha * 256 in MM6 + : : "r"(&packedalpha), "m"(packedalpha) ); + + Q_UINT32 *data1 = reinterpret_cast( src.bits() ); + Q_UINT32 *data2 = reinterpret_cast( dst.bits() ); + + // The main loop processes 2 pixels / iteration + int remainder = pixels % 2; + pixels -= remainder; + + // Main loop + for ( int i = 0; i < pixels; i += 2 ) { + __asm__ __volatile__( + // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3 + "movd (%0,%2,4), %%mm0\n\t" // Load the 1st src pixel to MM0 + "movd (%1,%2,4), %%mm1\n\t" // Load the 1st dst pixel to MM1 + "movd 4(%0,%2,4), %%mm2\n\t" // Load the 2nd src pixel to MM2 + "movd 4(%1,%2,4), %%mm3\n\t" // Load the 2nd dst pixel to MM3 + + // Blend the first pixel + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel + "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel + "psubw %%mm1, %%mm0\n\t" // Subtract dst from src + "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%mm1\n\t" // Multiply dst with 256 + "paddw %%mm1, %%mm0\n\t" // Add dst to the result + "psrlw $8, %%mm0\n\t" // Divide by 256 + + // Blend the second pixel + "punpcklbw %%mm7, %%mm2\n\t" // Unpack the src pixel + "punpcklbw %%mm7, %%mm3\n\t" // Unpack the dst pixel + "psubw %%mm3, %%mm2\n\t" // Subtract dst from src + "pmullw %%mm6, %%mm2\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%mm3\n\t" // Multiply dst with 256 + "paddw %%mm3, %%mm2\n\t" // Add dst to the result + "psrlw $8, %%mm2\n\t" // Divide by 256 + + // Write the pixels back to the image + "packuswb %%mm2, %%mm0\n\t" // Pack the pixels to a qword + "movq %%mm0, (%1,%2,4)\n\t" // Store the pixels + : : "r"(data1), "r"(data2), "r"(i) ); + } + + // Blend the remaining pixel (if there is one) + if ( remainder ) { + __asm__ __volatile__( + "movd (%0), %%mm0\n\t" // Load one src pixel to MM0 + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel + "movd (%1), %%mm1\n\t" // Load one dst pixel to MM1 + "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel + "psubw %%mm1, %%mm0\n\t" // Subtract dst from src + "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%mm1\n\t" // Multiply dst with 256 + "paddw %%mm1, %%mm0\n\t" // Add dst to result + "psrlw $8, %%mm0\n\t" // Divide by 256 + "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword + "movd %%mm0, (%1)\n\t" // Write the pixel to the image + : : "r"(data1 + pixels), "r"(data2 + pixels) ); + } + + // Empty the MMX state + __asm__ __volatile__("emms"); + } else +#endif // USE_MMX_INLINE_ASM + + { +#ifdef WORDS_BIGENDIAN // ARGB (skip alpha) + unsigned char *data1 = (unsigned char *)dst.bits() + 1; + unsigned char *data2 = (unsigned char *)src.bits() + 1; +#else // BGRA + unsigned char *data1 = (unsigned char *)dst.bits(); + unsigned char *data2 = (unsigned char *)src.bits(); +#endif + + for (int i=0; i 1) initial_intensity = 1; + if (initial_intensity < -1) initial_intensity = -1; + if (initial_intensity < 0) { + unaffected = 1. + initial_intensity; + initial_intensity = 0; + } + + + float intensity = initial_intensity; + float var = 1. - initial_intensity; + + if (anti_dir) { + initial_intensity = intensity = 1.; + var = -var; + } + + int x, y; + + unsigned int *data = (unsigned int *)image.bits(); + + int image_width = image.width(); //Those can't change + int image_height = image.height(); + + + if( eff == VerticalGradient || eff == HorizontalGradient ) { + + // set the image domain to apply the effect to + xi = 0, xf = image_width; + yi = 0, yf = image_height; + if (eff == VerticalGradient) { + if (anti_dir) yf = (int)(image_height * unaffected); + else yi = (int)(image_height * (1 - unaffected)); + } + else { + if (anti_dir) xf = (int)(image_width * unaffected); + else xi = (int)(image_height * (1 - unaffected)); + } + + var /= (eff == VerticalGradient?yf-yi:xf-xi); + + int ind_base; + for (y = yi; y < (int)yf; y++) { + intensity = eff == VerticalGradient? intensity + var : + initial_intensity; + ind_base = image_width * y ; + for (x = xi; x < (int)xf ; x++) { + if (eff == HorizontalGradient) intensity += var; + ind = x + ind_base; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + } + } + } + else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) { + float xvar = var / 2 / image_width; // / unaffected; + float yvar = var / 2 / image_height; // / unaffected; + float tmp; + + for (x = 0; x < image_width ; x++) { + tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1); + ind = x; + for (y = 0; y < image_height ; y++) { + intensity = initial_intensity + tmp + yvar * y; + + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + + ind += image_width; + } + } + } + + else if (eff == RectangleGradient || eff == EllipticGradient) { + float xvar; + float yvar; + + for (x = 0; x < image_width / 2 + image_width % 2; x++) { + xvar = var / image_width * (image_width - x*2/unaffected-1); + for (y = 0; y < image_height / 2 + image_height % 2; y++) { + yvar = var / image_height * (image_height - y*2/unaffected -1); + + if (eff == RectangleGradient) + intensity = initial_intensity + QMAX(xvar, yvar); + else + intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar); + if (intensity > 1) intensity = 1; + if (intensity < 0) intensity = 0; + + //NW + ind = x + image_width * y ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + + //NE + ind = image_width - x - 1 + image_width * y ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + } + } + + //CT loop is doubled because of stupid central row/column issue. + // other solution? + for (x = 0; x < image_width / 2; x++) { + xvar = var / image_width * (image_width - x*2/unaffected-1); + for (y = 0; y < image_height / 2; y++) { + yvar = var / image_height * (image_height - y*2/unaffected -1); + + if (eff == RectangleGradient) + intensity = initial_intensity + QMAX(xvar, yvar); + else + intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar); + if (intensity > 1) intensity = 1; + if (intensity < 0) intensity = 0; + + //SW + ind = x + image_width * (image_height - y -1) ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + + //SE + ind = image_width-x-1 + image_width * (image_height - y - 1) ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + } + } + } +#ifndef NDEBUG + else std::cerr << "KImageEffect::blend effect not implemented" << std::endl; +#endif + return image; +} + +// Not very efficient as we create a third big image... +// +QImage& KImageEffect::blend(QImage &image1, QImage &image2, + GradientType gt, int xf, int yf) +{ + if (image1.width() == 0 || image1.height() == 0 || + image2.width() == 0 || image2.height() == 0) + return image1; + + QImage image3; + + image3 = KImageEffect::unbalancedGradient(image1.size(), + QColor(0,0,0), QColor(255,255,255), + gt, xf, yf, 0); + + return blend(image1,image2,image3, Red); // Channel to use is arbitrary +} + +// Blend image2 into image1, using an RBG channel of blendImage +// +QImage& KImageEffect::blend(QImage &image1, QImage &image2, + QImage &blendImage, RGBComponent channel) +{ + if (image1.width() == 0 || image1.height() == 0 || + image2.width() == 0 || image2.height() == 0 || + blendImage.width() == 0 || blendImage.height() == 0) { +#ifndef NDEBUG + std::cerr << "KImageEffect::blend effect invalid image" << std::endl; +#endif + return image1; + } + + int r, g, b; + int ind1, ind2, ind3; + + unsigned int x1, x2, x3, y1, y2, y3; + unsigned int a; + + int x, y; + + // for image1 and image2, we only handle depth 32 + if (image1.depth()<32) image1 = image1.convertDepth(32); + if (image2.depth()<32) image2 = image2.convertDepth(32); + + // for blendImage, we handle depth 8 and 32 + if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8); + + unsigned int *colorTable3 = (blendImage.depth()==8) ? + blendImage.colorTable():0; + + unsigned int *data1 = (unsigned int *)image1.bits(); + unsigned int *data2 = (unsigned int *)image2.bits(); + unsigned int *data3 = (unsigned int *)blendImage.bits(); + unsigned char *data3b = (unsigned char *)blendImage.bits(); + unsigned int color3; + + x1 = image1.width(); y1 = image1.height(); + x2 = image2.width(); y2 = image2.height(); + x3 = blendImage.width(); y3 = blendImage.height(); + + for (y = 0; y < (int)y1; y++) { + ind1 = x1*y; + ind2 = x2*(y%y2); + ind3 = x3*(y%y3); + + x=0; + while(x < (int)x1) { + color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3]; + + a = (channel == Red) ? qRed(color3) : + (channel == Green) ? qGreen(color3) : + (channel == Blue) ? qBlue(color3) : qGray(color3); + + r = (a*qRed(data1[ind1]) + (256-a)*qRed(data2[ind2]))/256; + g = (a*qGreen(data1[ind1]) + (256-a)*qGreen(data2[ind2]))/256; + b = (a*qBlue(data1[ind1]) + (256-a)*qBlue(data2[ind2]))/256; + + a = qAlpha(data1[ind1]); + data1[ind1] = qRgba(r, g, b, a); + + ind1++; ind2++; ind3++; x++; + if ( (x%x2) ==0) ind2 -= x2; + if ( (x%x3) ==0) ind3 -= x3; + } + } + return image1; +} + + +//====================================================================== +// +// Hash effects +// +//====================================================================== + +unsigned int KImageEffect::lHash(unsigned int c) +{ + unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c); + unsigned char nr, ng, nb; + nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr; + ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng; + nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb; + + return qRgba(nr, ng, nb, a); +} + + +// ----------------------------------------------------------------------------- + +unsigned int KImageEffect::uHash(unsigned int c) +{ + unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c); + unsigned char nr, ng, nb; + nr = r + (r >> 3); nr = nr < r ? ~0 : nr; + ng = g + (g >> 3); ng = ng < g ? ~0 : ng; + nb = b + (b >> 3); nb = nb < b ? ~0 : nb; + + return qRgba(nr, ng, nb, a); +} + + +// ----------------------------------------------------------------------------- + +QImage& KImageEffect::hash(QImage &image, Lighting lite, unsigned int spacing) +{ + if (image.width() == 0 || image.height() == 0) { +#ifndef NDEBUG + std::cerr << "KImageEffect::hash effect invalid image" << std::endl; +#endif + return image; + } + + int x, y; + unsigned int *data = (unsigned int *)image.bits(); + unsigned int ind; + + //CT no need to do it if not enough space + if ((lite == NorthLite || + lite == SouthLite)&& + (unsigned)image.height() < 2+spacing) return image; + if ((lite == EastLite || + lite == WestLite)&& + (unsigned)image.height() < 2+spacing) return image; + + if (lite == NorthLite || lite == SouthLite) { + for (y = 0 ; y < image.height(); y = y + 2 + spacing) { + for (x = 0; x < image.width(); x++) { + ind = x + image.width() * y; + data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]); + + ind = ind + image.width(); + data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + else if (lite == EastLite || lite == WestLite) { + for (y = 0 ; y < image.height(); y++) { + for (x = 0; x < image.width(); x = x + 2 + spacing) { + ind = x + image.width() * y; + data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]); + + ind++; + data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + else if (lite == NWLite || lite == SELite) { + for (y = 0 ; y < image.height(); y++) { + for (x = 0; + x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing); + x = x + 2 + spacing) { + ind = x + image.width() * y + ((y & 1)? 1 : 0); + data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]); + + ind++; + data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + else if (lite == SWLite || lite == NELite) { + for (y = 0 ; y < image.height(); y++) { + for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) { + ind = x + image.width() * y - ((y & 1)? 1 : 0); + data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]); + + ind++; + data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + return image; +} +#endif + +//====================================================================== +// +// Flatten effects +// +//====================================================================== + +QImage& KImageEffect::flatten(QImage &image, const QColor &ca, + const QColor &cb, int ncols) +{ + if (image.width() == 0 || image.height() == 0) + return image; + + // a bitmap is easy... + /*if (image.depth() == 1) { + image.setColor(0, ca.rgb()); + image.setColor(1, cb.rgb()); + return image; + }*/ + + int r1, r2, g1, g2, b1, b2; + int min = 0, max = 255; + + if (image.inverted()) + { + b1 = ca.red(); b2 = cb.red(); + g1 = ca.green(); g2 = cb.green(); + r1 = ca.blue(); r2 = cb.blue(); + } + else + { + r1 = ca.red(); r2 = cb.red(); + g1 = ca.green(); g2 = cb.green(); + b1 = ca.blue(); b2 = cb.blue(); + } + + QRgb col; + + // Get minimum and maximum greylevel. +/* if (image.numColors()) { + // pseudocolor + for (int i = 0; i < image.numColors(); i++) { + col = image.color(i); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + min = QMIN(min, mean); + max = QMAX(max, mean); + } + } else*/ { + // truecolor + for (int y=0; y < image.height(); y++) + for (int x=0; x < image.width(); x++) { + col = image.pixel(x, y); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + min = QMIN(min, mean); + max = QMAX(max, mean); + } + } + + // Conversion factors + float sr = ((float) r2 - r1) / (max - min); + float sg = ((float) g2 - g1) / (max - min); + float sb = ((float) b2 - b1) / (max - min); + + + // Repaint the image +/* if (image.numColors()) { + for (int i=0; i < image.numColors(); i++) { + col = image.color(i); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + int r = (int) (sr * (mean - min) + r1 + 0.5); + int g = (int) (sg * (mean - min) + g1 + 0.5); + int b = (int) (sb * (mean - min) + b1 + 0.5); + image.setColor(i, qRgba(r, g, b, qAlpha(col))); + } + } else*/ { + for (int y=0; y < image.height(); y++) + for (int x=0; x < image.width(); x++) { + col = image.pixel(x, y); + //INVERT(col); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + int r = (int) (sr * (mean - min) + r1 + 0.5); + int g = (int) (sg * (mean - min) + g1 + 0.5); + int b = (int) (sb * (mean - min) + b1 + 0.5); + col = qRgba(r, g, b, qAlpha(col)); + //INVERT(col); + image.setPixel(x, y, col); + } + } + + +/* // Dither if necessary + if ( (ncols <= 0) || ((image.numColors() != 0) && (image.numColors() <= ncols))) + return image; + + if (ncols == 1) ncols++; + if (ncols > 256) ncols = 256; + + QColor *pal = new QColor[ncols]; + sr = ((float) r2 - r1) / (ncols - 1); + sg = ((float) g2 - g1) / (ncols - 1); + sb = ((float) b2 - b1) / (ncols - 1); + + for (int i=0; i red) + r = cr - tbl[cr - red]; + else + r = cr + tbl[red - cr]; + if (cg > green) + g = cg - tbl[cg - green]; + else + g = cg + tbl[green - cg]; + if (cb > blue) + b = cb - tbl[cb - blue]; + else + b = cb + tbl[blue - cb]; + image.setColor(i, qRgba(r, g, b, qAlpha(col))); + } + + } else*/ { + // truecolor + for (int y=0; y red) + r = cr - tbl[cr - red]; + else + r = cr + tbl[red - cr]; + if (cg > green) + g = cg - tbl[cg - green]; + else + g = cg + tbl[green - cg]; + if (cb > blue) + b = cb - tbl[cb - blue]; + else + b = cb + tbl[blue - cb]; + *data++ = qRgba(r, g, b, qAlpha(col)); + } + } + } + + return image; +} + +//====================================================================== +// +// Color effects +// +//====================================================================== + +// This code is adapted from code (C) Rik Hemsley +// +// The formula used (r + b + g) /3 is different from the qGray formula +// used by Qt. This is because our formula is much much faster. If, +// however, it turns out that this is producing sub-optimal images, +// then it will have to change (kurt) +// +// It does produce lower quality grayscale ;-) Use fast == true for the fast +// algorithm, false for the higher quality one (mosfet). +QImage& KImageEffect::toGray(QImage &img, bool fast) +{ + if (img.width() == 0 || img.height() == 0) + return img; + +/* + if(fast){ + if (img.depth() == 32) { + uchar * r(img.bits()); + uchar * g(img.bits() + 1); + uchar * b(img.bits() + 2); + + uchar * end(img.bits() + img.numBytes()); + + while (r != end) { + + *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3 + + r += 4; + g += 4; + b += 4; + } + } + else + { + for (int i = 0; i < img.numColors(); i++) + { + uint r = qRed(img.color(i)); + uint g = qGreen(img.color(i)); + uint b = qBlue(img.color(i)); + + uint gray = (((r + g) >> 1) + b) >> 1; + img.setColor(i, qRgba(gray, gray, gray, qAlpha(img.color(i)))); + } + } + } + else*/{ + int pixels = img.depth() > 8 ? img.width()*img.height() : + img.numColors(); + unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() : + (unsigned int *)img.colorTable(); + int val, i; + for(i=0; i < pixels; ++i){ + val = qGray(data[i]); + data[i] = qRgba(val, val, val, qAlpha(data[i])); + } + } + return img; +} + +// CT 29Jan2000 - desaturation algorithms +QImage& KImageEffect::desaturate(QImage &image, float desat) +{ + if (image.width() == 0 || image.height() == 0) + return image; + + if (desat < 0) desat = 0.; + if (desat > 1) desat = 1.; + int pixels = image.depth() > 8 ? image.width()*image.height() : image.numColors(); + unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() : (unsigned int *)image.colorTable(); + int h, s, v, i; + uint col; + QColor clr; // keep constructor out of loop (mosfet) + for (i = 0; i < pixels; ++i) + { + col = data[i]; + INVERT(col); + clr.setRgb(col); + clr.hsv(&h, &s, &v); + clr.setHsv(h, (int)(s * (1. - desat)), v); + data[i] = clr.rgb(); + INVERT(data[i]); + } + return image; +} + +#if 0 +// Contrast stuff (mosfet) +QImage& KImageEffect::contrast(QImage &img, int c) +{ + if (img.width() == 0 || img.height() == 0) + return img; + + if(c > 255) + c = 255; + if(c < -255) + c = -255; + int pixels = img.depth() > 8 ? img.width()*img.height() : + img.numColors(); + unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() : + (unsigned int *)img.colorTable(); + int i, r, g, b; + for(i=0; i < pixels; ++i){ + r = qRed(data[i]); + g = qGreen(data[i]); + b = qBlue(data[i]); + if(qGray(data[i]) <= 127){ + if(r - c <= 255) + r -= c; + if(g - c <= 255) + g -= c; + if(b - c <= 255) + b -= c; + } + else{ + if(r + c <= 255) + r += c; + if(g + c <= 255) + g += c; + if(b + c <= 255) + b += c; + } + data[i] = qRgba(r, g, b, qAlpha(data[i])); + } + return(img); +} +#endif + +#if 0 +//====================================================================== +// +// Dithering effects +// +//====================================================================== + +// adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org) +// +// Floyd-Steinberg dithering +// Ref: Bitmapped Graphics Programming in C++ +// Marv Luse, Addison-Wesley Publishing, 1993. +QImage& KImageEffect::dither(QImage &img, const QColor *palette, int size) +{ + if (img.width() == 0 || img.height() == 0 || + palette == 0 || img.depth() <= 8) + return img; + + QImage dImage( img.width(), img.height(), 8, size ); + int i; + + dImage.setNumColors( size ); + for ( i = 0; i < size; i++ ) + dImage.setColor( i, palette[ i ].rgb() ); + + int *rerr1 = new int [ img.width() * 2 ]; + int *gerr1 = new int [ img.width() * 2 ]; + int *berr1 = new int [ img.width() * 2 ]; + + memset( rerr1, 0, sizeof( int ) * img.width() * 2 ); + memset( gerr1, 0, sizeof( int ) * img.width() * 2 ); + memset( berr1, 0, sizeof( int ) * img.width() * 2 ); + + int *rerr2 = rerr1 + img.width(); + int *gerr2 = gerr1 + img.width(); + int *berr2 = berr1 + img.width(); + + for ( int j = 0; j < img.height(); j++ ) + { + uint *ip = (uint * )img.scanLine( j ); + uchar *dp = dImage.scanLine( j ); + + for ( i = 0; i < img.width(); i++ ) + { + rerr1[i] = rerr2[i] + qRed( *ip ); + rerr2[i] = 0; + gerr1[i] = gerr2[i] + qGreen( *ip ); + gerr2[i] = 0; + berr1[i] = berr2[i] + qBlue( *ip ); + berr2[i] = 0; + ip++; + } + + *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size ); + + for ( i = 1; i < img.width()-1; i++ ) + { + int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size ); + *dp = indx; + + int rerr = rerr1[i]; + rerr -= palette[indx].red(); + int gerr = gerr1[i]; + gerr -= palette[indx].green(); + int berr = berr1[i]; + berr -= palette[indx].blue(); + + // diffuse red error + rerr1[ i+1 ] += ( rerr * 7 ) >> 4; + rerr2[ i-1 ] += ( rerr * 3 ) >> 4; + rerr2[ i ] += ( rerr * 5 ) >> 4; + rerr2[ i+1 ] += ( rerr ) >> 4; + + // diffuse green error + gerr1[ i+1 ] += ( gerr * 7 ) >> 4; + gerr2[ i-1 ] += ( gerr * 3 ) >> 4; + gerr2[ i ] += ( gerr * 5 ) >> 4; + gerr2[ i+1 ] += ( gerr ) >> 4; + + // diffuse red error + berr1[ i+1 ] += ( berr * 7 ) >> 4; + berr2[ i-1 ] += ( berr * 3 ) >> 4; + berr2[ i ] += ( berr * 5 ) >> 4; + berr2[ i+1 ] += ( berr ) >> 4; + + dp++; + } + + *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size ); + } + + delete [] rerr1; + delete [] gerr1; + delete [] berr1; + + img = dImage; + return img; +} + +int KImageEffect::nearestColor( int r, int g, int b, const QColor *palette, int size ) +{ + if (palette == 0) + return 0; + + int dr = palette[0].red() - r; + int dg = palette[0].green() - g; + int db = palette[0].blue() - b; + + int minDist = dr*dr + dg*dg + db*db; + int nearest = 0; + + for (int i = 1; i < size; i++ ) + { + dr = palette[i].red() - r; + dg = palette[i].green() - g; + db = palette[i].blue() - b; + + int dist = dr*dr + dg*dg + db*db; + + if ( dist < minDist ) + { + minDist = dist; + nearest = i; + } + } + + return nearest; +} + +bool KImageEffect::blend( + const QImage & upper, + const QImage & lower, + QImage & output +) +{ + if ( + upper.width() > lower.width() || + upper.height() > lower.height() || + upper.depth() != 32 || + lower.depth() != 32 + ) + { +#ifndef NDEBUG + std::cerr << "KImageEffect::blend : Sizes not correct\n" ; +#endif + return false; + } + + output = lower.copy(); + + uchar *i, *o; + int a; + int col; + int w = upper.width(); + int row(upper.height() - 1); + + do { + + i = upper.scanLine(row); + o = output.scanLine(row); + + col = w << 2; + --col; + + do { + + while (!(a = i[col]) && (col != 3)) { + --col; --col; --col; --col; + } + + --col; + o[col] += ((i[col] - o[col]) * a) >> 8; + + --col; + o[col] += ((i[col] - o[col]) * a) >> 8; + + --col; + o[col] += ((i[col] - o[col]) * a) >> 8; + + } while (col--); + + } while (row--); + + return true; +} + +#if 0 +// Not yet... +bool KImageEffect::blend( + const QImage & upper, + const QImage & lower, + QImage & output, + const QRect & destRect +) +{ + output = lower.copy(); + return output; +} + +#endif + +bool KImageEffect::blend( + int &x, int &y, + const QImage & upper, + const QImage & lower, + QImage & output +) +{ + int cx=0, cy=0, cw=upper.width(), ch=upper.height(); + + if ( upper.width() + x > lower.width() || + upper.height() + y > lower.height() || + x < 0 || y < 0 || + upper.depth() != 32 || lower.depth() != 32 ) + { + if ( x > lower.width() || y > lower.height() ) return false; + if ( upper.width()<=0 || upper.height() <= 0 ) return false; + if ( lower.width()<=0 || lower.height() <= 0 ) return false; + + if (x<0) {cx=-x; cw+=x; x=0; }; + if (cw + x > lower.width()) { cw=lower.width()-x; }; + if (y<0) {cy=-y; ch+=y; y=0; }; + if (ch + y > lower.height()) { ch=lower.height()-y; }; + + if ( cx >= upper.width() || cy >= upper.height() ) return true; + if ( cw <= 0 || ch <= 0 ) return true; + } + + output.create(cw,ch,32); +// output.setAlphaBuffer(true); // I should do some benchmarks to see if + // this is worth the effort + + QRgb *i, *o, *b; + + int a; + int j,k; + for (j=0; j(&lower.scanLine(y+j) [ (x+cw) << 2 ]); + i=reinterpret_cast(&upper.scanLine(cy+j)[ (cx+cw) << 2 ]); + o=reinterpret_cast(&output.scanLine(j) [ cw << 2 ]); + + k=cw-1; + --b; --i; --o; + do + { + while ( !(a=qAlpha(*i)) && k>0 ) + { + i--; +// *o=0; + *o=*b; + --o; --b; + k--; + }; +// *o=0xFF; + *o = qRgb(qRed(*b) + (((qRed(*i) - qRed(*b)) * a) >> 8), + qGreen(*b) + (((qGreen(*i) - qGreen(*b)) * a) >> 8), + qBlue(*b) + (((qBlue(*i) - qBlue(*b)) * a) >> 8)); + --i; --o; --b; + } while (k--); + } + + return true; +} + +bool KImageEffect::blendOnLower( + int x, int y, + const QImage & upper, + const QImage & lower +) +{ + int cx=0, cy=0, cw=upper.width(), ch=upper.height(); + + if ( upper.depth() != 32 || lower.depth() != 32 ) return false; + if ( x + cw > lower.width() || + y + ch > lower.height() || + x < 0 || y < 0 ) + { + if ( x > lower.width() || y > lower.height() ) return true; + if ( upper.width()<=0 || upper.height() <= 0 ) return true; + if ( lower.width()<=0 || lower.height() <= 0 ) return true; + + if (x<0) {cx=-x; cw+=x; x=0; }; + if (cw + x > lower.width()) { cw=lower.width()-x; }; + if (y<0) {cy=-y; ch+=y; y=0; }; + if (ch + y > lower.height()) { ch=lower.height()-y; }; + + if ( cx >= upper.width() || cy >= upper.height() ) return true; + if ( cw <= 0 || ch <= 0 ) return true; + } + + uchar *i, *b; + int a; + int k; + + for (int j=0; j0 ) +#else + while ( !(a=*(i-3)) && k>0 ) +#endif + { + i-=4; b-=4; k--; + }; + +#ifndef WORDS_BIGENDIAN + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; +#else + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + i -= 2; b -= 2; +#endif + } while (k--); + } + + return true; +} + +void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect) +{ + // clip rect + QRect lr = lowerRect & lower.rect(); + lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) ); + lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) ); + if ( !lr.isValid() ) return; + + // blend + for (int y = 0; y < lr.height(); y++) { + for (int x = 0; x < lr.width(); x++) { + QRgb *b = reinterpret_cast(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb)); + QRgb *d = reinterpret_cast(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb)); + int a = qAlpha(*d); + *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8), + qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8), + qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8)); + } + } +} + +void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect, float opacity) +{ + // clip rect + QRect lr = lowerRect & lower.rect(); + lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) ); + lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) ); + if ( !lr.isValid() ) return; + + // blend + for (int y = 0; y < lr.height(); y++) { + for (int x = 0; x < lr.width(); x++) { + QRgb *b = reinterpret_cast(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb)); + QRgb *d = reinterpret_cast(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb)); + int a = qRound(opacity * qAlpha(*d)); + *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8), + qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8), + qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8)); + } + } +} + +QRect KImageEffect::computeDestinationRect(const QSize &lowerSize, + Disposition disposition, QImage &upper) +{ + int w = lowerSize.width(); + int h = lowerSize.height(); + int ww = upper.width(); + int wh = upper.height(); + QRect d; + + switch (disposition) { + case NoImage: + break; + case Centered: + d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); + break; + case Tiled: + d.setRect(0, 0, w, h); + break; + case CenterTiled: + d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh, + w-1, h-1); + break; + case Scaled: + upper = upper.smoothScale(w, h); + d.setRect(0, 0, w, h); + break; + case CenteredAutoFit: + if( ww <= w && wh <= h ) { + d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered + break; + } + // fall through + case CenteredMaxpect: { + double sx = (double) w / ww; + double sy = (double) h / wh; + if (sx > sy) { + ww = (int)(sy * ww); + wh = h; + } else { + wh = (int)(sx * wh); + ww = w; + } + upper = upper.smoothScale(ww, wh); + d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); + break; + } + case TiledMaxpect: { + double sx = (double) w / ww; + double sy = (double) h / wh; + if (sx > sy) { + ww = (int)(sy * ww); + wh = h; + } else { + wh = (int)(sx * wh); + ww = w; + } + upper = upper.smoothScale(ww, wh); + d.setRect(0, 0, w, h); + break; + } + } + + return d; +} + +void KImageEffect::blendOnLower(QImage &upper, QImage &lower, + Disposition disposition, float opacity) +{ + QRect r = computeDestinationRect(lower.size(), disposition, upper); + for (int y = r.top(); y 8){ // DirectClass source image + unsigned int *srcData, *destData; + unsigned int *pixels; + pixels = (unsigned int *)malloc(src.width()*sizeof(unsigned int)); + if(!pixels){ + //qWarning("KImageEffect::sample(): Unable to allocate pixels buffer"); + free(pixels); + free(x_offset); + free(y_offset); + return(src); + } + j = (-1); + for(y=0; y < h; ++y){ + destData = (unsigned int *)dest.scanLine(y); + if(j != y_offset[y]){ + // read a scan line + j = (int)(y_offset[y]); + srcData = (unsigned int *)src.scanLine(j); + (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned int)); + } + // sample each column + for(x=0; x < w; ++x){ + k = (int)(x_offset[x]); + destData[x] = pixels[k]; + } + } + free(pixels); + } + /* + else{ // PsudeoClass source image + unsigned char *srcData, *destData; + unsigned char *pixels; + pixels = (unsigned char *)malloc(src.width()*sizeof(unsigned char)); + if(!pixels){ + qWarning("KImageEffect::sample(): Unable to allocate pixels buffer"); + free(pixels); + free(x_offset); + free(y_offset); + return(src); + } + // copy colortable + dest.setNumColors(src.numColors()); + (void)memcpy(dest.colorTable(), src.colorTable(), + src.numColors()*sizeof(unsigned int)); + + // sample image + j = (-1); + for(y=0; y < h; ++y){ + destData = (unsigned char *)dest.scanLine(y); + if(j != y_offset[y]){ + // read a scan line + j = (int)(y_offset[y]); + srcData = (unsigned char *)src.scanLine(j); + (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned char)); + } + // sample each column + for(x=0; x < w; ++x){ + k = (int)(x_offset[x]); + destData[x] = pixels[k]; + } + } + free(pixels); + }*/ + free(x_offset); + free(y_offset); + return(dest); +} +#endif + +void KImageEffect::threshold(QImage &img, unsigned int threshold) +{ + int i, count; + unsigned int *data; + if(img.depth() > 8){ // DirectClass + count = img.width()*img.height(); + data = (unsigned int *)img.bits(); + } + /*else{ // PsudeoClass + count = img.numColors(); + data = (unsigned int *)img.colorTable(); + }*/ + if (img.inverted()) + { + for(i=0; i < count; ++i) + data[i] = intensityValue(invert(data[i])) < threshold ? 0xFF000000 : 0xFFFFFFFF; + } + else + { + for(i=0; i < count; ++i) + data[i] = intensityValue(data[i]) < threshold ? 0xFF000000 : 0xFFFFFFFF; + } +} + +void KImageEffect::hull(const int x_offset, const int y_offset, + const int polarity, const int columns, + const int rows, + unsigned int *f, unsigned int *g) +{ + int x, y; + + unsigned int *p, *q, *r, *s; + unsigned int v; + if(f == NULL || g == NULL) + return; + p=f+(columns+2); + q=g+(columns+2); + r=p+(y_offset*(columns+2)+x_offset); + for (y=0; y < rows; y++){ + p++; + q++; + r++; + if(polarity > 0) + for (x=0; x < columns; x++){ + v=(*p); + if (*r > v) + v++; + *q=v; + p++; + q++; + r++; + } + else + for(x=0; x < columns; x++){ + v=(*p); + if (v > (unsigned int) (*r+1)) + v--; + *q=v; + p++; + q++; + r++; + } + p++; + q++; + r++; + } + p=f+(columns+2); + q=g+(columns+2); + r=q+(y_offset*(columns+2)+x_offset); + s=q-(y_offset*(columns+2)+x_offset); + for(y=0; y < rows; y++){ + p++; + q++; + r++; + s++; + if(polarity > 0) + for(x=0; x < (int) columns; x++){ + v=(*q); + if (((unsigned int) (*s+1) > v) && (*r > v)) + v++; + *p=v; + p++; + q++; + r++; + s++; + } + else + for (x=0; x < columns; x++){ + v=(*q); + if (((unsigned int) (*s+1) < v) && (*r < v)) + v--; + *p=v; + p++; + q++; + r++; + s++; + } + p++; + q++; + r++; + s++; + } +} + +QImage KImageEffect::despeckle(QImage &src) +{ + int i, j, x, y; + unsigned int *blue_channel, *red_channel, *green_channel, *buffer, + *alpha_channel; + int packets; + static const int + X[4]= {0, 1, 1,-1}, + Y[4]= {1, 0, 1, 1}; + + unsigned int *destData; + QImage dest(src.width(), src.height(), src.transparent()); + + packets = (src.width()+2)*(src.height()+2); + red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + buffer = (unsigned int *)calloc(packets, sizeof(unsigned int)); + if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel || + !buffer){ + free(red_channel); + free(green_channel); + free(blue_channel); + free(alpha_channel); + free(buffer); + return(src); + } + + // copy image pixels to color component buffers + j = src.width()+2; + if(src.depth() > 8){ // DirectClass source image + unsigned int *srcData; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned int *)src.scanLine(y); + ++j; + for(x=0; x < src.width(); ++x){ + red_channel[j] = qRed(srcData[x]); + green_channel[j] = qGreen(srcData[x]); + blue_channel[j] = qBlue(srcData[x]); + alpha_channel[j] = qAlpha(srcData[x]); + ++j; + } + ++j; + } + } + /* + else { // PsudeoClass source image + unsigned char *srcData; + unsigned int *cTable = src.colorTable(); + unsigned int pixel; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned char *)src.scanLine(y); + ++j; + for(x=0; x < src.width(); ++x){ + pixel = *(cTable+srcData[x]); + red_channel[j] = qRed(pixel); + green_channel[j] = qGreen(pixel); + blue_channel[j] = qBlue(pixel); + alpha_channel[j] = qAlpha(pixel); + ++j; + } + ++j; + } + }*/ + + // reduce speckle in red channel + for(i=0; i < 4; i++){ + hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer); + hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer); + hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer); + hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer); + } + // reduce speckle in green channel + for (i=0; i < packets; i++) + buffer[i]=0; + for (i=0; i < 4; i++){ + hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer); + hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer); + hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer); + hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer); + } + // reduce speckle in blue channel + for (i=0; i < packets; i++) + buffer[i]=0; + for (i=0; i < 4; i++){ + hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer); + hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer); + hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer); + hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer); + } + // copy color component buffers to despeckled image + j = dest.width()+2; + for(y=0; y < dest.height(); ++y) + { + destData = (unsigned int *)dest.scanLine(y); + ++j; + for (x=0; x < dest.width(); ++x) + { + destData[x] = qRgba(red_channel[j], green_channel[j], + blue_channel[j], alpha_channel[j]); + ++j; + } + ++j; + } + free(buffer); + free(red_channel); + free(green_channel); + free(blue_channel); + free(alpha_channel); + return(dest); +} + +unsigned int KImageEffect::generateNoise(unsigned int pixel, + NoiseType noise_type) +{ +#define NoiseEpsilon 1.0e-5 +#define NoiseMask 0x7fff +#define SigmaUniform 4.0 +#define SigmaGaussian 4.0 +#define SigmaImpulse 0.10 +#define SigmaLaplacian 10.0 +#define SigmaMultiplicativeGaussian 0.5 +#define SigmaPoisson 0.05 +#define TauGaussian 20.0 + + double alpha, beta, sigma, value; + alpha=(double) (rand() & NoiseMask)/NoiseMask; + if (alpha == 0.0) + alpha=1.0; + switch(noise_type){ + case UniformNoise: + default: + { + value=(double) pixel+SigmaUniform*(alpha-0.5); + break; + } + case GaussianNoise: + { + double tau; + + beta=(double) (rand() & NoiseMask)/NoiseMask; + sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta); + tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta); + value=(double) pixel+ + (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau); + break; + } + case MultiplicativeGaussianNoise: + { + if (alpha <= NoiseEpsilon) + sigma=MaxRGB; + else + sigma=sqrt(-2.0*log(alpha)); + beta=(rand() & NoiseMask)/NoiseMask; + value=(double) pixel+ + pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta); + break; + } + case ImpulseNoise: + { + if (alpha < (SigmaImpulse/2.0)) + value=0; + else + if (alpha >= (1.0-(SigmaImpulse/2.0))) + value=MaxRGB; + else + value=pixel; + break; + } + case LaplacianNoise: + { + if (alpha <= 0.5) + { + if (alpha <= NoiseEpsilon) + value=(double) pixel-MaxRGB; + else + value=(double) pixel+SigmaLaplacian*log(2.0*alpha); + break; + } + beta=1.0-alpha; + if (beta <= (0.5*NoiseEpsilon)) + value=(double) pixel+MaxRGB; + else + value=(double) pixel-SigmaLaplacian*log(2.0*beta); + break; + } + case PoissonNoise: + { + int + i; + + for (i=0; alpha > exp(-SigmaPoisson*pixel); i++) + { + beta=(double) (rand() & NoiseMask)/NoiseMask; + alpha=alpha*beta; + } + value=i/SigmaPoisson; + break; + } + } + if(value < 0.0) + return(0); + if(value > MaxRGB) + return(MaxRGB); + return((unsigned int) (value+0.5)); +} + +QImage KImageEffect::addNoise(QImage &src, NoiseType noise_type) +{ + int x, y; + QImage dest(src.width(), src.height(), 32); + unsigned int *destData; + + if(src.depth() > 8){ // DirectClass source image + unsigned int *srcData; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned int *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + for(x=0; x < src.width(); ++x){ + destData[x] = qRgba(generateNoise(qRed(srcData[x]), noise_type), + generateNoise(qGreen(srcData[x]), noise_type), + generateNoise(qBlue(srcData[x]), noise_type), + qAlpha(srcData[x])); + } + } + } + /*else{ // PsudeoClass source image + unsigned char *srcData; + unsigned int *cTable = src.colorTable(); + unsigned int pixel; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned char *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + for(x=0; x < src.width(); ++x){ + pixel = *(cTable+srcData[x]); + destData[x] = qRgba(generateNoise(qRed(pixel), noise_type), + generateNoise(qGreen(pixel), noise_type), + generateNoise(qBlue(pixel), noise_type), + qAlpha(pixel)); + } + } + + }*/ + return(dest); +} + +unsigned int KImageEffect::interpolateColor(QImage *image, double x_offset, + double y_offset, + unsigned int background) +{ + double alpha, beta; + unsigned int p = 0, q = 0, r = 0, s = 0; + int x, y; + + x = (int)x_offset; + y = (int)y_offset; + if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height())) + return(background); + if(image->depth() > 8){ + if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) { + unsigned int *t = (unsigned int *)image->scanLine(y); + p = t[x]; + q = t[x+1]; + r = t[x+image->width()]; + s = t[x+image->width()+1]; + } + else{ + unsigned int *t = (unsigned int *)image->scanLine(y); + p = background; + if((x >= 0) && (y >= 0)){ + p = t[x]; + } + q = background; + if(((x+1) < image->width()) && (y >= 0)){ + q = t[x+1]; + } + r = background; + if((x >= 0) && ((y+1) < image->height())){ + t = (unsigned int *)image->scanLine(y+1); + r = t[x+image->width()]; + } + s = background; + if(((x+1) < image->width()) && ((y+1) < image->height())){ + t = (unsigned int *)image->scanLine(y+1); + s = t[x+image->width()+1]; + } + + } + } + /*else{ + unsigned int *colorTable = (unsigned int *)image->colorTable(); + if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) { + unsigned char *t; + t = (unsigned char *)image->scanLine(y); + p = *(colorTable+t[x]); + q = *(colorTable+t[x+1]); + t = (unsigned char *)image->scanLine(y+1); + r = *(colorTable+t[x]); + s = *(colorTable+t[x+1]); + } + else{ + unsigned char *t; + p = background; + if((x >= 0) && (y >= 0)){ + t = (unsigned char *)image->scanLine(y); + p = *(colorTable+t[x]); + } + q = background; + if(((x+1) < image->width()) && (y >= 0)){ + t = (unsigned char *)image->scanLine(y); + q = *(colorTable+t[x+1]); + } + r = background; + if((x >= 0) && ((y+1) < image->height())){ + t = (unsigned char *)image->scanLine(y+1); + r = *(colorTable+t[x]); + } + s = background; + if(((x+1) < image->width()) && ((y+1) < image->height())){ + t = (unsigned char *)image->scanLine(y+1); + s = *(colorTable+t[x+1]); + } + + } + + }*/ + x_offset -= floor(x_offset); + y_offset -= floor(y_offset); + alpha = 1.0-x_offset; + beta = 1.0-y_offset; + + return(qRgba((unsigned char)(beta*(alpha*qRed(p)+x_offset*qRed(q))+y_offset*(alpha*qRed(r)+x_offset*qRed(s))), + (unsigned char)(beta*(alpha*qGreen(p)+x_offset*qGreen(q))+y_offset*(alpha*qGreen(r)+x_offset*qGreen(s))), + (unsigned char)(beta*(alpha*qBlue(p)+x_offset*qBlue(q))+y_offset*(alpha*qBlue(r)+x_offset*qBlue(s))), + (unsigned char)(beta*(alpha*qAlpha(p)+x_offset*qAlpha(q))+y_offset*(alpha*qAlpha(r)+x_offset*qAlpha(s))))); +} + +QImage KImageEffect::implode(QImage &src, double factor, + unsigned int background) +{ + double amount, distance, radius; + double x_center, x_distance, x_scale; + double y_center, y_distance, y_scale; + unsigned int *destData; + int x, y; + + QImage dest(src.width(), src.height(), src.transparent()); + + // compute scaling factor + x_scale = 1.0; + y_scale = 1.0; + x_center = (double)0.5*src.width(); + y_center = (double)0.5*src.height(); + radius=x_center; + if(src.width() > src.height()) + y_scale = (double)src.width()/src.height(); + else if(src.width() < src.height()){ + x_scale = (double) src.height()/src.width(); + radius = y_center; + } + amount=factor/10.0; + if(amount >= 0) + amount/=10.0; + if(src.depth() > 8){ // DirectClass source image + unsigned int *srcData; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned int *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + y_distance=y_scale*(y-y_center); + for(x=0; x < src.width(); ++x){ + destData[x] = srcData[x]; + x_distance = x_scale*(x-x_center); + distance= x_distance*x_distance+y_distance*y_distance; + if(distance < (radius*radius)){ + double factor; + // Implode the pixel. + factor=1.0; + if(distance > 0.0) + factor= + pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount); + destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center, + factor*y_distance/y_scale+y_center, + background); + } + } + } + } + /*else{ // PsudeoClass source image + unsigned char *srcData; + unsigned char idx; + unsigned int *cTable = src.colorTable(); + for(y=0; y < src.height(); ++y){ + srcData = (unsigned char *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + y_distance=y_scale*(y-y_center); + for(x=0; x < src.width(); ++x){ + idx = srcData[x]; + destData[x] = cTable[idx]; + x_distance = x_scale*(x-x_center); + distance= x_distance*x_distance+y_distance*y_distance; + if(distance < (radius*radius)){ + double factor; + // Implode the pixel. + factor=1.0; + if(distance > 0.0) + factor= + pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount); + destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center, + factor*y_distance/y_scale+y_center, + background); + } + } + } + + }*/ + return(dest); +} + +#if 0 +QImage KImageEffect::rotate(QImage &img, RotateDirection r) +{ + QImage dest; + int x, y; + if(img.depth() > 8){ + unsigned int *srcData, *destData; + switch(r){ + case Rotate90: + dest.create(img.height(), img.width(), img.depth()); + for(y=0; y < img.height(); ++y){ + srcData = (unsigned int *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned int *)dest.scanLine(x); + destData[img.height()-y-1] = srcData[x]; + } + } + break; + case Rotate180: + dest.create(img.width(), img.height(), img.depth()); + for(y=0; y < img.height(); ++y){ + srcData = (unsigned int *)img.scanLine(y); + destData = (unsigned int *)dest.scanLine(img.height()-y-1); + for(x=0; x < img.width(); ++x) + destData[img.width()-x-1] = srcData[x]; + } + break; + case Rotate270: + dest.create(img.height(), img.width(), img.depth()); + for(y=0; y < img.height(); ++y){ + srcData = (unsigned int *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned int *)dest.scanLine(img.width()-x-1); + destData[y] = srcData[x]; + } + } + break; + default: + dest = img; + break; + } + } + else{ + unsigned char *srcData, *destData; + unsigned int *srcTable, *destTable; + switch(r){ + case Rotate90: + dest.create(img.height(), img.width(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for(x=0; x < img.numColors(); ++x) + destTable[x] = srcTable[x]; + for(y=0; y < img.height(); ++y){ + srcData = (unsigned char *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned char *)dest.scanLine(x); + destData[img.height()-y-1] = srcData[x]; + } + } + break; + case Rotate180: + dest.create(img.width(), img.height(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for(x=0; x < img.numColors(); ++x) + destTable[x] = srcTable[x]; + for(y=0; y < img.height(); ++y){ + srcData = (unsigned char *)img.scanLine(y); + destData = (unsigned char *)dest.scanLine(img.height()-y-1); + for(x=0; x < img.width(); ++x) + destData[img.width()-x-1] = srcData[x]; + } + break; + case Rotate270: + dest.create(img.height(), img.width(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for(x=0; x < img.numColors(); ++x) + destTable[x] = srcTable[x]; + for(y=0; y < img.height(); ++y){ + srcData = (unsigned char *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned char *)dest.scanLine(img.width()-x-1); + destData[y] = srcData[x]; + } + } + break; + default: + dest = img; + break; + } + + } + return(dest); +} +#endif + +void KImageEffect::solarize(QImage &img, double factor) +{ + int i, count; + int threshold; + unsigned int *data; + + threshold = (int)(factor*(MaxRGB+1)/100.0); + /*if(img.depth() < 32){ + data = (unsigned int *)img.colorTable(); + count = img.numColors(); + } + else*/{ + data = (unsigned int *)img.bits(); + count = img.width()*img.height(); + } + + for(i=0; i < count; ++i){ + data[i] = qRgba(qRed(data[i]) > threshold ? MaxRGB-qRed(data[i]) : qRed(data[i]), + qGreen(data[i]) > threshold ? MaxRGB-qGreen(data[i]) : qGreen(data[i]), + qBlue(data[i]) > threshold ? MaxRGB-qBlue(data[i]) : qBlue(data[i]), + qAlpha(data[i])); + } +} + +QImage KImageEffect::spread(QImage &src, unsigned int amount) +{ + int quantum, x, y; + int x_distance, y_distance; + if(src.width() < 3 || src.height() < 3) + return(src); + QImage dest(src.width(), src.height(), src.transparent()); + quantum=(amount+1) >> 1; + if(src.depth() > 8){ // DirectClass source image + unsigned int *p, *q; + for(y=0; y < src.height(); y++){ + q = (unsigned int *)dest.scanLine(y); + for(x=0; x < src.width(); x++){ + x_distance = x + ((rand() % (amount+1))-quantum); + y_distance = y + ((rand() % (amount+1))-quantum); + x_distance = QMIN(x_distance, src.width()-1); + y_distance = QMIN(y_distance, src.height()-1); + if(x_distance < 0) + x_distance = 0; + if(y_distance < 0) + y_distance = 0; + p = (unsigned int *)src.scanLine(y_distance); + p += x_distance; + *q++=(*p); + } + } + } + /*else { // PsudeoClass source image + // just do colortable values + unsigned char *p, *q; + for(y=0; y < src.height(); y++){ + q = (unsigned char *)dest.scanLine(y); + for(x=0; x < src.width(); x++){ + x_distance = x + ((rand() & (amount+1))-quantum); + y_distance = y + ((rand() & (amount+1))-quantum); + x_distance = QMIN(x_distance, src.width()-1); + y_distance = QMIN(y_distance, src.height()-1); + if(x_distance < 0) + x_distance = 0; + if(y_distance < 0) + y_distance = 0; + p = (unsigned char *)src.scanLine(y_distance); + p += x_distance; + *q++=(*p); + } + } + }*/ + return(dest); +} + +QImage KImageEffect::swirl(QImage &src, double degrees, + unsigned int background) +{ + double cosine, distance, factor, radius, sine, x_center, x_distance, + x_scale, y_center, y_distance, y_scale; + int x, y; + unsigned int *q; + QImage dest(src.width(), src.height(), src.transparent()); + + // compute scaling factor + x_center = src.width()/2.0; + y_center = src.height()/2.0; + radius = QMAX(x_center,y_center); + x_scale=1.0; + y_scale=1.0; + if(src.width() > src.height()) + y_scale=(double)src.width()/src.height(); + else if(src.width() < src.height()) + x_scale=(double)src.height()/src.width(); + //degrees=DegreesToRadians(degrees); + // swirl each row + if(src.depth() > 8){ // DirectClass source image + unsigned int *p; + for(y=0; y < src.height(); y++){ + p = (unsigned int *)src.scanLine(y); + q = (unsigned int *)dest.scanLine(y); + y_distance = y_scale*(y-y_center); + for(x=0; x < src.width(); x++){ + // determine if the pixel is within an ellipse + *q=(*p); + x_distance = x_scale*(x-x_center); + distance = x_distance*x_distance+y_distance*y_distance; + if (distance < (radius*radius)){ + // swirl + factor = 1.0-sqrt(distance)/radius; + sine = sin(degrees*factor*factor); + cosine = cos(degrees*factor*factor); + *q = interpolateColor(&src, + (cosine*x_distance-sine*y_distance)/x_scale+x_center, + (sine*x_distance+cosine*y_distance)/y_scale+y_center, + background); + } + p++; + q++; + } + } + } + /*else{ // PsudeoClass source image + unsigned char *p; + unsigned int *cTable = (unsigned int *)src.colorTable(); + for(y=0; y < src.height(); y++){ + p = (unsigned char *)src.scanLine(y); + q = (unsigned int *)dest.scanLine(y); + y_distance = y_scale*(y-y_center); + for(x=0; x < src.width(); x++){ + // determine if the pixel is within an ellipse + *q = *(cTable+(*p)); + x_distance = x_scale*(x-x_center); + distance = x_distance*x_distance+y_distance*y_distance; + if (distance < (radius*radius)){ + // swirl + factor = 1.0-sqrt(distance)/radius; + sine = sin(degrees*factor*factor); + cosine = cos(degrees*factor*factor); + *q = interpolateColor(&src, + (cosine*x_distance-sine*y_distance)/x_scale+x_center, + (sine*x_distance+cosine*y_distance)/y_scale+y_center, + background); + } + p++; + q++; + } + } + + }*/ + return(dest); +} + +QImage KImageEffect::wave(QImage &src, double amplitude, double wavelength, + unsigned int background) +{ + double *sine_map; + int x, y; + unsigned int *q; + + QImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), src.transparent()); + // allocate sine map + sine_map = (double *)malloc(dest.width()*sizeof(double)); + if(!sine_map) + return(src); + for(x=0; x < dest.width(); ++x) + sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength); + // wave image + for(y=0; y < dest.height(); ++y){ + q = (unsigned int *)dest.scanLine(y); + for (x=0; x < dest.width(); x++){ + *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background); + ++q; + } + } + free(sine_map); + return(dest); +} + +// +// The following methods work by computing a value from neighboring pixels +// (mosfet 05/26/03) +// + +// New algorithms based on ImageMagick 5.5.6 (05/26/03) + +#if 0 +QImage KImageEffect::oilPaint(QImage &src, int /*radius*/) +{ + /* binary compat method - remove me when possible! */ + return(oilPaintConvolve(src, 0)); +} +#endif + +QImage KImageEffect::oilPaintConvolve(QImage &src, double radius) +{ + unsigned long count /*,*histogram*/; + unsigned long histogram[256]; + unsigned int k; + int width; + int x, y, mx, my, sx, sy; + int mcx, mcy; + unsigned int *s=0, *q; + + //if(src.depth() < 32) + // src.convertDepth(32); + QImage dest(src.width(), src.height(), src.transparent()); + + width = getOptimalKernelWidth(radius, 0.5); + if(src.width() < width || width <= 0) { + //qWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!"); + return(dest); + } + /* + histogram = (unsigned long *)malloc(256*sizeof(unsigned long)); + if(!histogram){ + qWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!"); + return(dest); + } + */ + unsigned int **jumpTable = (unsigned int **)src.jumpTable(); + for(y=0; y < dest.height(); ++y){ + sy = y-(width/2); + q = (unsigned int *)dest.scanLine(y); + for(x=0; x < dest.width(); ++x){ + count = 0; + memset(histogram, 0, 256*sizeof(unsigned long)); + //memset(histogram, 0, 256); + sy = y-(width/2); + for(mcy=0; mcy < width; ++mcy, ++sy){ + my = sy < 0 ? 0 : sy > src.height()-1 ? + src.height()-1 : sy; + sx = x+(-width/2); + for(mcx=0; mcx < width; ++mcx, ++sx){ + mx = sx < 0 ? 0 : sx > src.width()-1 ? + src.width()-1 : sx; + + k = intensityValue(jumpTable[my][mx]); + if(k > 255){ + //qWarning("KImageEffect::oilPaintConvolve(): k is %d", + // k); + k = 255; + } + histogram[k]++; + if(histogram[k] > count){ + count = histogram[k]; + s = jumpTable[my]+mx; + } + } + } + *q++ = (*s); + } + } + /* liberateMemory(histogram); */ + return(dest); +} + +#if 0 +QImage KImageEffect::charcoal(QImage &src, double /*factor*/) +{ + /* binary compat method - remove me when possible! */ + return(charcoal(src, 0, 1)); +} +#endif + +QImage KImageEffect::charcoal(QImage &src, double radius, double sigma) +{ + QImage img0 = edge(src, radius); + QImage img = blur(img0, radius, sigma); + img0.release(); + normalize(img); + img.invertPixels(); + KImageEffect::toGray(img); + return(img); +} + +void KImageEffect::normalize(QImage &image) +{ + struct double_packet high, low, intensity, *histogram; + struct short_packet *normalize_map; + long long number_pixels; + int x, y; + unsigned int *p, *q; + long i; + unsigned long threshold_intensity; + unsigned char r, g, b, a; + + //if(image.depth() < 32) // result will always be 32bpp + // image = image.convertDepth(32); + + histogram = (struct double_packet *) + malloc(256*sizeof(struct double_packet)); + normalize_map = (struct short_packet *) + malloc(256*sizeof(struct short_packet)); + + if(!histogram || !normalize_map){ + if(histogram) + liberateMemory( &histogram); + if(normalize_map) + liberateMemory( &normalize_map); + //qWarning("KImageEffect::normalize(): Unable to allocate memory!"); + return; + } + + /* + Form histogram. + */ + memset(histogram, 0, 256*sizeof(struct double_packet)); + for(y=0; y < image.height(); ++y){ + p = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + histogram[(unsigned char)(qRed(*p))].red++; + histogram[(unsigned char)(qGreen(*p))].green++; + histogram[(unsigned char)(qBlue(*p))].blue++; + histogram[(unsigned char)(qAlpha(*p))].alpha++; + p++; + } + } + + /* + Find the histogram boundaries by locating the 0.1 percent levels. + */ + number_pixels = (long long)image.width()*image.height(); + threshold_intensity = number_pixels/1000; + memset(&high, 0, sizeof(struct double_packet)); + memset(&low, 0, sizeof(struct double_packet)); + + /* red */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.red=255; high.red != 0; high.red--){ + intensity.red+=histogram[(unsigned char)high.red].red; + if(intensity.red > threshold_intensity) + break; + } + if(low.red == high.red){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.red=0; low.red < 255; low.red++){ + intensity.red+=histogram[(unsigned char)low.red].red; + if(intensity.red > threshold_intensity) + break; + } + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.red=255; high.red != 0; high.red--){ + intensity.red+=histogram[(unsigned char)high.red].red; + if(intensity.red > threshold_intensity) + break; + } + } + + /* green */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.green=255; high.green != 0; high.green--){ + intensity.green+=histogram[(unsigned char)high.green].green; + if(intensity.green > threshold_intensity) + break; + } + if(low.green == high.green){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.green=0; low.green < 255; low.green++){ + intensity.green+=histogram[(unsigned char)low.green].green; + if(intensity.green > threshold_intensity) + break; + } + memset(&intensity,0,sizeof(struct double_packet)); + for(high.green=255; high.green != 0; high.green--){ + intensity.green+=histogram[(unsigned char)high.green].green; + if(intensity.green > threshold_intensity) + break; + } + } + + /* blue */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.blue=255; high.blue != 0; high.blue--){ + intensity.blue+=histogram[(unsigned char)high.blue].blue; + if(intensity.blue > threshold_intensity) + break; + } + if(low.blue == high.blue){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.blue=0; low.blue < 255; low.blue++){ + intensity.blue+=histogram[(unsigned char)low.blue].blue; + if(intensity.blue > threshold_intensity) + break; + } + memset(&intensity,0,sizeof(struct double_packet)); + for(high.blue=255; high.blue != 0; high.blue--){ + intensity.blue+=histogram[(unsigned char)high.blue].blue; + if(intensity.blue > threshold_intensity) + break; + } + } + + /* alpha */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.alpha=255; high.alpha != 0; high.alpha--){ + intensity.alpha+=histogram[(unsigned char)high.alpha].alpha; + if(intensity.alpha > threshold_intensity) + break; + } + if(low.alpha == high.alpha){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.alpha=0; low.alpha < 255; low.alpha++){ + intensity.alpha+=histogram[(unsigned char)low.alpha].alpha; + if(intensity.alpha > threshold_intensity) + break; + } + memset(&intensity,0,sizeof(struct double_packet)); + for(high.alpha=255; high.alpha != 0; high.alpha--){ + intensity.alpha+=histogram[(unsigned char)high.alpha].alpha; + if(intensity.alpha > threshold_intensity) + break; + } + } + liberateMemory( &histogram); + + /* + Stretch the histogram to create the normalized image mapping. + */ + + // should the maxes be 65535? + memset(normalize_map, 0 ,256*sizeof(struct short_packet)); + for(i=0; i <= (long) 255; i++){ + if(i < (long) low.red) + normalize_map[i].red=0; + else if (i > (long) high.red) + normalize_map[i].red=65535; + else if (low.red != high.red) + normalize_map[i].red = + (unsigned short)((65535*(i-low.red))/(high.red-low.red)); + + if(i < (long) low.green) + normalize_map[i].green=0; + else if (i > (long) high.green) + normalize_map[i].green=65535; + else if (low.green != high.green) + normalize_map[i].green = + (unsigned short)((65535*(i-low.green))/(high.green-low.green)); + + if(i < (long) low.blue) + normalize_map[i].blue=0; + else if (i > (long) high.blue) + normalize_map[i].blue=65535; + else if (low.blue != high.blue) + normalize_map[i].blue = + (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue)); + + if(i < (long) low.alpha) + normalize_map[i].alpha=0; + else if (i > (long) high.alpha) + normalize_map[i].alpha=65535; + else if (low.alpha != high.alpha) + normalize_map[i].alpha = + (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha)); + + } + + for(y=0; y < image.height(); ++y){ + q = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + if(low.red != high.red) + r = (normalize_map[(unsigned short)(qRed(q[x]))].red)/257; + else + r = qRed(q[x]); + if(low.green != high.green) + g = (normalize_map[(unsigned short)(qGreen(q[x]))].green)/257; + else + g = qGreen(q[x]); + if(low.blue != high.blue) + b = (normalize_map[(unsigned short)(qBlue(q[x]))].blue)/257; + else + b = qBlue(q[x]); + if(low.alpha != high.alpha) + a = (normalize_map[(unsigned short)(qAlpha(q[x]))].alpha)/257; + else + a = qAlpha(q[x]); + q[x] = qRgba(r, g, b, a); + } + } + liberateMemory( &normalize_map); +} + +void KImageEffect::equalize(QImage &image) +{ + struct double_packet high, low, intensity, *map, *histogram; + struct short_packet *equalize_map; + int x, y; + unsigned int *p, *q; + long i; + unsigned char r, g, b, a; + + //if(image.depth() < 32) // result will always be 32bpp + // image = image.convertDepth(32); + + histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet)); + map=(struct double_packet *) malloc(256*sizeof(struct double_packet)); + equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet)); + if(!histogram || !map || !equalize_map){ + if(histogram) + liberateMemory( &histogram); + if(map) + liberateMemory( &map); + if(equalize_map) + liberateMemory( &equalize_map); + //qWarning("KImageEffect::equalize(): Unable to allocate memory!"); + return; + } + + /* + Form histogram. + */ + memset(histogram, 0, 256*sizeof(struct double_packet)); + for(y=0; y < image.height(); ++y){ + p = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + histogram[(unsigned char)(qRed(*p))].red++; + histogram[(unsigned char)(qGreen(*p))].green++; + histogram[(unsigned char)(qBlue(*p))].blue++; + histogram[(unsigned char)(qAlpha(*p))].alpha++; + p++; + } + } + /* + Integrate the histogram to get the equalization map. + */ + memset(&intensity, 0 ,sizeof(struct double_packet)); + for(i=0; i <= 255; ++i){ + intensity.red += histogram[i].red; + intensity.green += histogram[i].green; + intensity.blue += histogram[i].blue; + intensity.alpha += histogram[i].alpha; + map[i]=intensity; + } + low=map[0]; + high=map[255]; + memset(equalize_map, 0, 256*sizeof(short_packet)); + for(i=0; i <= 255; ++i){ + if(high.red != low.red) + equalize_map[i].red=(unsigned short) + ((65535*(map[i].red-low.red))/(high.red-low.red)); + if(high.green != low.green) + equalize_map[i].green=(unsigned short) + ((65535*(map[i].green-low.green))/(high.green-low.green)); + if(high.blue != low.blue) + equalize_map[i].blue=(unsigned short) + ((65535*(map[i].blue-low.blue))/(high.blue-low.blue)); + if(high.alpha != low.alpha) + equalize_map[i].alpha=(unsigned short) + ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha)); + } + liberateMemory( &histogram); + liberateMemory( &map); + + /* + Stretch the histogram. + */ + for(y=0; y < image.height(); ++y){ + q = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + if(low.red != high.red) + r = (equalize_map[(unsigned short)(qRed(q[x]))].red/257); + else + r = qRed(q[x]); + if(low.green != high.green) + g = (equalize_map[(unsigned short)(qGreen(q[x]))].green/257); + else + g = qGreen(q[x]); + if(low.blue != high.blue) + b = (equalize_map[(unsigned short)(qBlue(q[x]))].blue/257); + else + b = qBlue(q[x]); + if(low.alpha != high.alpha) + a = (equalize_map[(unsigned short)(qAlpha(q[x]))].alpha/257); + else + a = qAlpha(q[x]); + q[x] = qRgba(r, g, b, a); + } + } + liberateMemory( &equalize_map); + +} + +QImage KImageEffect::edge(QImage &image, double radius) +{ + double *kernel; + int width; + long i; + QImage dest; + + width = getOptimalKernelWidth(radius, 0.5); + if(image.width() < width || image.height() < width){ + //qWarning("KImageEffect::edge(): Image is smaller than radius!"); + return(dest); + } + kernel= (double *)malloc(width*width*sizeof(double)); + if(!kernel){ + //qWarning("KImageEffect::edge(): Unable to allocate memory!"); + return(dest); + } + for(i=0; i < (width*width); i++) + kernel[i]=(-1.0); + kernel[i/2]=width*width-1.0; + convolveImage(&image, &dest, width, kernel); + liberateMemory(&kernel); + return(dest); +} + +#if 0 +QImage KImageEffect::emboss(QImage &src) +{ + /* binary compat method - remove me when possible! */ + return(emboss(src, 0, 1)); +} +#endif + +QImage KImageEffect::emboss(QImage &image, double radius, double sigma) +{ + double alpha, *kernel; + int j, width; + long i, u, v; + QImage dest; + + if(sigma == 0.0){ + //qWarning("KImageEffect::emboss(): Zero sigma is not permitted!"); + return(dest); + } + + width = getOptimalKernelWidth(radius, sigma); + if(image.width() < width || image.height() < width){ + //qWarning("KImageEffect::emboss(): Image is smaller than radius!"); + return(dest); + } + kernel= (double *)malloc(width*width*sizeof(double)); + if(!kernel){ + //qWarning("KImageEffect::emboss(): Unable to allocate memory!"); + return(dest); + } + + //if(image.depth() < 32) + // image = image.convertDepth(32); + + i=0; + j=width/2; + for(v=(-width/2); v <= (width/2); v++){ + for(u=(-width/2); u <= (width/2); u++){ + alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma)); + kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/ + (2.0*MagickPI*sigma*sigma); + if (u == j) + kernel[i]=0.0; + i++; + } + j--; + } + convolveImage(&image, &dest, width, kernel); + liberateMemory(&kernel); + + equalize(dest); + return(dest); +} + +void KImageEffect::blurScanLine(double *kernel, int width, + unsigned int *src, unsigned int *dest, + int columns) +{ + double *p; + unsigned int *q; + int x; + long i; + double red, green, blue, alpha; + double scale = 0.0; + + if(width > columns){ + for(x=0; x < columns; ++x){ + scale = 0.0; + red = blue = green = alpha = 0.0; + p = kernel; + q = src; + for(i=0; i < columns; ++i){ + if((i >= (x-width/2)) && (i <= (x+width/2))){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + } + if(((i+width/2-x) >= 0) && ((i+width/2-x) < width)) + scale+=kernel[i+width/2-x]; + p++; + q++; + } + scale = 1.0/scale; + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + return; + } + + for(x=0; x < width/2; ++x){ + scale = 0.0; + red = blue = green = alpha = 0.0; + p = kernel+width/2-x; + q = src; + for(i=width/2-x; i < width; ++i){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + scale += (*p); + p++; + q++; + } + scale=1.0/scale; + + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + + for(; x < columns-width/2; ++x){ + red = blue = green = alpha = 0.0; + p = kernel; + q = src+(x-width/2); + for (i=0; i < (long) width; ++i){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + p++; + q++; + } + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + + for(; x < columns; ++x){ + red = blue = green = alpha = 0.0; + scale=0; + p = kernel; + q = src+(x-width/2); + for(i=0; i < columns-x+width/2; ++i){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + scale += (*p); + p++; + q++; + } + scale=1.0/scale; + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } +} + +int KImageEffect::getBlurKernel(int width, double sigma, double **kernel) +{ +#define KernelRank 3 + double alpha, normalize; + long i; + int bias; + + assert(sigma != 0.0); + if(width == 0) + width = 3; + *kernel=(double *)malloc(width*sizeof(double)); + if(*kernel == (double *)NULL) + return(0); + memset(*kernel, 0, width*sizeof(double)); + bias = KernelRank*width/2; + for(i=(-bias); i <= bias; i++){ + alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma)); + (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma); + } + normalize=0; + for(i=0; i < width; i++) + normalize+=(*kernel)[i]; + for(i=0; i < width; i++) + (*kernel)[i]/=normalize; + + return(width); +} + +#if 0 +QImage KImageEffect::blur(QImage &src, double /*factor*/) +{ + /* binary compat method - remove me when possible! */ + return(blur(src, 0, 1)); +} +#endif + +QImage KImageEffect::blur(QImage &src, double radius, double sigma) +{ + double *kernel; + QImage dest; + int width; + int x, y; + unsigned int *scanline, *temp; + unsigned int *p, *q; + + if(sigma == 0.0){ + //qWarning("KImageEffect::blur(): Zero sigma is not permitted!"); + return(dest); + } + //if(src.depth() < 32) + // src = src.convertDepth(32); + + kernel=(double *) NULL; + if(radius > 0) + width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel); + else{ + double *last_kernel; + last_kernel=(double *) NULL; + width=getBlurKernel(3,sigma,&kernel); + + while ((long) (MaxRGB*kernel[0]) > 0){ + if(last_kernel != (double *)NULL){ + liberateMemory( &last_kernel); + } + last_kernel=kernel; + kernel = (double *)NULL; + width = getBlurKernel(width+2, sigma, &kernel); + } + if(last_kernel != (double *) NULL){ + liberateMemory( &kernel); + width-=2; + kernel = last_kernel; + } + } + + if(width < 3){ + //qWarning("KImageEffect::blur(): Kernel radius is too small!"); + liberateMemory( &kernel); + return(dest); + } + + dest.create(src.width(), src.height(), src.transparent()); + + scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height()); + temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height()); + for(y=0; y < src.height(); ++y){ + p = (unsigned int *)src.scanLine(y); + q = (unsigned int *)dest.scanLine(y); + blurScanLine(kernel, width, p, q, src.width()); + } + + unsigned int **srcTable = (unsigned int **)src.jumpTable(); + unsigned int **destTable = (unsigned int **)dest.jumpTable(); + for(x=0; x < src.width(); ++x){ + for(y=0; y < src.height(); ++y){ + scanline[y] = srcTable[y][x]; + } + blurScanLine(kernel, width, scanline, temp, src.height()); + for(y=0; y < src.height(); ++y){ + destTable[y][x] = temp[y]; + } + } + liberateMemory( &scanline); + liberateMemory( &temp); + liberateMemory( &kernel); + return(dest); +} + +bool KImageEffect::convolveImage(QImage *image, QImage *dest, + const unsigned int order, + const double *kernel) +{ + long width; + double red, green, blue, alpha; + double normalize, *normal_kernel; + const double *k; + unsigned int *q; + int x, y, mx, my, sx, sy; + long i; + int mcx, mcy; + + width = order; + if((width % 2) == 0){ + //qWarning("KImageEffect: Kernel width must be an odd number!"); + return(false); + } + normal_kernel = (double *)malloc(width*width*sizeof(double)); + if(!normal_kernel){ + //qWarning("KImageEffect: Unable to allocate memory!"); + return(false); + } + dest->reset(); + dest->create(image->width(), image->height(), image->transparent()); + //if(image->depth() < 32) + // *image = image->convertDepth(32); + + normalize=0.0; + for(i=0; i < (width*width); i++) + normalize += kernel[i]; + if(fabs(normalize) <= MagickEpsilon) + normalize=1.0; + normalize=1.0/normalize; + for(i=0; i < (width*width); i++) + normal_kernel[i] = normalize*kernel[i]; + + unsigned int **jumpTable = (unsigned int **)image->jumpTable(); + for(y=0; y < dest->height(); ++y){ + sy = y-(width/2); + q = (unsigned int *)dest->scanLine(y); + for(x=0; x < dest->width(); ++x){ + k = normal_kernel; + red = green = blue = alpha = 0; + sy = y-(width/2); + for(mcy=0; mcy < width; ++mcy, ++sy){ + my = sy < 0 ? 0 : sy > image->height()-1 ? + image->height()-1 : sy; + sx = x+(-width/2); + for(mcx=0; mcx < width; ++mcx, ++sx){ + mx = sx < 0 ? 0 : sx > image->width()-1 ? + image->width()-1 : sx; + red += (*k)*(qRed(jumpTable[my][mx])*257); + green += (*k)*(qGreen(jumpTable[my][mx])*257); + blue += (*k)*(qBlue(jumpTable[my][mx])*257); + alpha += (*k)*(qAlpha(jumpTable[my][mx])*257); + ++k; + } + } + + red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5; + green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5; + + *q++ = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + } + free(normal_kernel); + return(true); + +} + +int KImageEffect::getOptimalKernelWidth(double radius, double sigma) +{ + double normalize, value; + long width; + long u; + + assert(sigma != 0.0); + if(radius > 0.0) + return((int)(2.0*ceil(radius)+1.0)); + for(width=5; ;){ + normalize=0.0; + for(u=(-width/2); u <= (width/2); u++) + normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma); + u=width/2; + value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize; + if((long)(65535*value) <= 0) + break; + width+=2; + } + return((int)width-2); +} + +#if 0 +QImage KImageEffect::sharpen(QImage &src, double /*factor*/) +{ + /* binary compat method - remove me when possible! */ + return(sharpen(src, 0, 1)); +} +#endif + +QImage KImageEffect::sharpen(QImage &image, double radius, double sigma) +{ + double alpha, normalize, *kernel; + int width; + long i, u, v; + QImage dest; + + if(sigma == 0.0){ + //qWarning("KImageEffect::sharpen(): Zero sigma is not permitted!"); + return(dest); + } + width = getOptimalKernelWidth(radius, sigma); + if(image.width() < width){ + //qWarning("KImageEffect::sharpen(): Image is smaller than radius!"); + return(dest); + } + kernel = (double *)malloc(width*width*sizeof(double)); + if(!kernel){ + //qWarning("KImageEffect::sharpen(): Unable to allocate memory!"); + return(dest); + } + + i = 0; + normalize=0.0; + for(v=(-width/2); v <= (width/2); v++){ + for(u=(-width/2); u <= (width/2); u++){ + alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma)); + kernel[i]=alpha/(2.0*MagickPI*sigma*sigma); + normalize+=kernel[i]; + i++; + } + } + kernel[i/2]=(-2.0)*normalize; + convolveImage(&image, &dest, width, kernel); + liberateMemory( &kernel); + return(dest); +} + +// End of new algorithms + +QImage KImageEffect::shade(QImage &src, bool color_shading, double azimuth, + double elevation) +{ + struct PointInfo{ + double x, y, z; + }; + + double distance, normal_distance, shade; + int x, y; + + struct PointInfo light, normal; + + unsigned int *q; + + QImage dest(src.width(), src.height(), src.transparent()); + + //azimuth = DegreesToRadians(azimuth); + //elevation = DegreesToRadians(elevation); + light.x = MaxRGB*cos(azimuth)*cos(elevation); + light.y = MaxRGB*sin(azimuth)*cos(elevation); + light.z = MaxRGB*sin(elevation); + normal.z= 2*MaxRGB; // constant Z of surface normal + + if(src.depth() > 8){ // DirectClass source image + unsigned int *p, *s0, *s1, *s2; + for(y=0; y < src.height(); ++y){ + p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3)); + q = (unsigned int *)dest.scanLine(y); + // shade this row of pixels. + *q++=(*(p+src.width())); + p++; + s0 = p; + s1 = p + src.width(); + s2 = p + 2*src.width(); + for(x=1; x < src.width()-1; ++x){ + // determine the surface normal and compute shading. + normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))- + (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))- + (double) intensityValue(*(s2+1)); + normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))- + (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)- + (double) intensityValue(*(s0+1)); + if((normal.x == 0) && (normal.y == 0)) + shade=light.z; + else{ + shade=0.0; + distance=normal.x*light.x+normal.y*light.y+normal.z*light.z; + if (distance > 0.0){ + normal_distance= + normal.x*normal.x+normal.y*normal.y+normal.z*normal.z; + if(fabs(normal_distance) > 0.0000001) + shade=distance/sqrt(normal_distance); + } + } + if(!color_shading){ + *q = qRgba((unsigned char)(shade), + (unsigned char)(shade), + (unsigned char)(shade), + qAlpha(*s1)); + } + else{ + *q = qRgba((unsigned char)((shade*qRed(*s1))/(MaxRGB+1)), + (unsigned char)((shade*qGreen(*s1))/(MaxRGB+1)), + (unsigned char)((shade*qBlue(*s1))/(MaxRGB+1)), + qAlpha(*s1)); + } + ++s0; + ++s1; + ++s2; + q++; + } + *q++=(*s1); + } + }/* + else{ // PsudeoClass source image + unsigned char *p, *s0, *s1, *s2; + int scanLineIdx; + unsigned int *cTable = (unsigned int *)src.colorTable(); + for(y=0; y < src.height(); ++y){ + scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3); + p = (unsigned char *)src.scanLine(scanLineIdx); + q = (unsigned int *)dest.scanLine(y); + // shade this row of pixels. + s0 = p; + s1 = (unsigned char *) src.scanLine(scanLineIdx+1); + s2 = (unsigned char *) src.scanLine(scanLineIdx+2); + *q++=(*(cTable+(*s1))); + ++p; + ++s0; + ++s1; + ++s2; + for(x=1; x < src.width()-1; ++x){ + // determine the surface normal and compute shading. + normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))- + (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))- + (double) intensityValue(*(cTable+(*(s2+1)))); + normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))- + (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))- + (double) intensityValue(*(cTable+(*(s0+1)))); + if((normal.x == 0) && (normal.y == 0)) + shade=light.z; + else{ + shade=0.0; + distance=normal.x*light.x+normal.y*light.y+normal.z*light.z; + if (distance > 0.0){ + normal_distance= + normal.x*normal.x+normal.y*normal.y+normal.z*normal.z; + if(fabs(normal_distance) > 0.0000001) + shade=distance/sqrt(normal_distance); + } + } + if(!color_shading){ + *q = qRgba((unsigned char)(shade), + (unsigned char)(shade), + (unsigned char)(shade), + qAlpha(*(cTable+(*s1)))); + } + else{ + *q = qRgba((unsigned char)((shade*qRed(*(cTable+(*s1))))/(MaxRGB+1)), + (unsigned char)((shade*qGreen(*(cTable+(*s1))))/(MaxRGB+1)), + (unsigned char)((shade*qBlue(*(cTable+(*s1))))/(MaxRGB+1)), + qAlpha(*s1)); + } + ++s0; + ++s1; + ++s2; + q++; + } + *q++=(*(cTable+(*s1))); + } + }*/ + return(dest); +} + +#if 0 +// High quality, expensive HSV contrast. You can do a faster one by just +// taking a grayscale threshold (ie: 128) and incrementing RGB color +// channels above it and decrementing those below it, but this gives much +// better results. (mosfet 12/28/01) +void KImageEffect::contrastHSV(QImage &image, bool sharpen) +{ + int i, sign; + unsigned int *data; + int count; + double brightness, scale, theta; + QColor c; + int h, s, v; + uint col; + + sign = sharpen ? 1 : -1; + scale=0.5000000000000001; + if(image.depth() > 8){ + count = image.width()*image.height(); + data = (unsigned int *)image.bits(); + } + else{ + count = image.numColors(); + data = (unsigned int *)image.colorTable(); + } + for(i=0; i < count; ++i){ + col = data[i]; + INVERT(col); + c.setRgb(data[i]); + c.hsv(&h, &s, &v); + brightness = v/255.0; + theta=(brightness-0.5)*M_PI; + brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign); + if (brightness > 1.0) + brightness=1.0; + else + if (brightness < 0) + brightness=0.0; + v = (int)(brightness*255); + c.setHsv(h, s, v); + data[i] = qRgba(c.red(), c.green(), c.blue(), qAlpha(data[i])); + INVERT(data[i]); + } +} + + +struct BumpmapParams { + BumpmapParams( double bm_azimuth, double bm_elevation, + int bm_depth, KImageEffect::BumpmapType bm_type, + bool invert ) { + /* Convert to radians */ + double azimuth = DegreesToRadians( bm_azimuth ); + double elevation = DegreesToRadians( bm_elevation ); + + /* Calculate the light vector */ + lx = (int)( cos(azimuth) * cos(elevation) * 255.0 ); + ly = (int)( sin(azimuth) * cos(elevation) * 255.0 ); + int lz = (int)( sin(elevation) * 255.0 ); + + /* Calculate constant Z component of surface normal */ + int nz = (6 * 255) / bm_depth; + nz2 = nz * nz; + nzlz = nz * lz; + + /* Optimize for vertical normals */ + background = lz; + + /* Calculate darkness compensation factor */ + compensation = sin(elevation); + + /* Create look-up table for map type */ + for (int i = 0; i < 256; i++) + { + double n = 0; + switch (bm_type) + { + case KImageEffect::Spherical: + n = i / 255.0 - 1.0; + lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5); + break; + + case KImageEffect::Sinuosidal: + n = i / 255.0; + lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) / + 2.0 + 0.5); + break; + + case KImageEffect::Linear: + default: + lut[i] = i; + } + + if (invert) + lut[i] = 255 - lut[i]; + } + } + int lx, ly; + int nz2, nzlz; + int background; + double compensation; + uchar lut[256]; +}; + + +static void bumpmap_convert_row( uint *row, + int width, + int bpp, + int has_alpha, + uchar *lut, + int waterlevel ) +{ + uint *p; + + p = row; + + has_alpha = has_alpha ? 1 : 0; + + if (bpp >= 3) + for (; width; width--) + { + if (has_alpha) { + unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5); + *p++ = lut[(unsigned int) ( waterlevel + + ( ( idx - + waterlevel) * qBlue( *row )) / 255.0 )]; + } else { + unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5); + *p++ = lut[idx]; + } + + ++row; + } +} + +static void bumpmap_row( uint *src, + uint *dest, + int width, + int bpp, + int has_alpha, + uint *bm_row1, + uint *bm_row2, + uint *bm_row3, + int bm_width, + int bm_xofs, + bool tiled, + bool row_in_bumpmap, + int ambient, + bool compensate, + BumpmapParams *params ) +{ + int xofs1, xofs2, xofs3; + int shade; + int ndotl; + int nx, ny; + int x; + int tmp; + + tmp = bm_xofs; + xofs2 = MOD(tmp, bm_width); + + for (x = 0; x < width; x++) + { + /* Calculate surface normal from bump map */ + + if (tiled || (row_in_bumpmap && + x >= - tmp && x < - tmp + bm_width)) { + if (tiled) { + xofs1 = MOD(xofs2 - 1, bm_width); + xofs3 = MOD(xofs2 + 1, bm_width); + } else { + xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1); + xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1); + } + nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] - + bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]); + ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] - + bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]); + } else { + nx = ny = 0; + } + + /* Shade */ + + if ((nx == 0) && (ny == 0)) + shade = params->background; + else { + ndotl = nx * params->lx + ny * params->ly + params->nzlz; + + if (ndotl < 0) + shade = (int)( params->compensation * ambient ); + else { + shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) ); + + shade = (int)( shade + QMAX(0.0, (255 * params->compensation - shade)) * + ambient / 255 ); + } + } + + /* Paint */ + + /** + * NOTE: if we want to work with non-32bit images the alpha handling would + * also change + */ + if (compensate) { + int red = (int)((qRed( *src ) * shade) / (params->compensation * 255)); + int green = (int)((qGreen( *src ) * shade) / (params->compensation * 255)); + int blue = (int)((qBlue( *src ) * shade) / (params->compensation * 255)); + int alpha = (int)((qAlpha( *src ) * shade) / (params->compensation * 255)); + ++src; + *dest++ = qRgba( red, green, blue, alpha ); + } else { + int red = qRed( *src ) * shade / 255; + int green = qGreen( *src ) * shade / 255; + int blue = qBlue( *src ) * shade / 255; + int alpha = qAlpha( *src ) * shade / 255; + ++src; + *dest++ = qRgba( red, green, blue, alpha ); + } + + /* Next pixel */ + + if (++xofs2 == bm_width) + xofs2 = 0; + } +} + +/** + * A bumpmapping algorithm. + * + * @param img the image you want bumpmap + * @param map the map used + * @param azimuth azimuth + * @param elevation elevation + * @param depth depth (not the depth of the image, but of the map) + * @param xofs X offset + * @param yofs Y offset + * @param waterlevel level that full transparency should represent + * @param ambient ambient lighting factor + * @param compensate compensate for darkening + * @param invert invert bumpmap + * @param type type of the bumpmap + * + * @return The destination image (dst) containing the result. + * @author Zack Rusin + */ +QImage KImageEffect::bumpmap(QImage &img, QImage &map, double azimuth, double elevation, + int depth, int xofs, int yofs, int waterlevel, + int ambient, bool compensate, bool invert, + BumpmapType type, bool tiled) +{ + QImage dst; + + if ( img.depth() != 32 || img.depth() != 32 ) { + qWarning( "Bump-mapping effect works only with 32 bit images"); + return dst; + } + + dst.create( img.width(), img.height(), img.depth() ); + int bm_width = map.width(); + int bm_height = map.height(); + int bm_bpp = map.depth(); + int bm_has_alpha = map.hasAlphaBuffer(); + + int yofs1, yofs2, yofs3; + + if ( tiled ) { + yofs2 = MOD( yofs, bm_height ); + yofs1 = MOD( yofs2 - 1, bm_height); + yofs3 = MOD( yofs2 + 1, bm_height); + } else { + yofs1 = 0; + yofs2 = 0; + yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 ); + } + + BumpmapParams params( azimuth, elevation, depth, type, invert ); + + uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 ); + uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 ); + uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 ); + + bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); + bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); + bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); + + for (int y = 0; y < img.height(); ++y) + { + int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height); + + uint* src_row = (unsigned int*)img.scanLine( y ); + uint* dest_row = (unsigned int*)dst.scanLine( y ); + + bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(), + bm_row1, bm_row2, bm_row3, bm_width, xofs, + tiled, + row_in_bumpmap, ambient, compensate, + ¶ms ); + + /* Next line */ + + if (tiled || row_in_bumpmap) + { + uint* bm_tmprow = bm_row1; + bm_row1 = bm_row2; + bm_row2 = bm_row3; + bm_row3 = bm_tmprow; + + if (++yofs2 == bm_height) + yofs2 = 0; + + if (tiled) + yofs3 = MOD(yofs2 + 1, bm_height); + else + yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1); + + bm_row3 = (unsigned int*)map.scanLine( yofs3 ); + bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, + params.lut, waterlevel ); + } + } + return dst; +} +#endif diff --git a/main/lib/image.effect/kimageeffect.h b/main/lib/image.effect/kimageeffect.h new file mode 100644 index 00000000..6970851a --- /dev/null +++ b/main/lib/image.effect/kimageeffect.h @@ -0,0 +1,811 @@ +/*************************************************************************** + + kimageeffect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* This file is part of the KDE libraries + Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley + (C) 1998, 1999 Christian Tibirna + (C) 1998, 1999 Dirk A. Mueller + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +// $Id: kimageeffect.h,v 1.37 2003/10/07 22:40:42 mueller Exp $ + +#ifndef __KIMAGE_EFFECT_H +#define __KIMAGE_EFFECT_H + +#include "qcolor.h" +#include "qsize.h" +#include "qpoint.h" +#include "qrect.h" +#include "qimage.h" + +/** + * This class includes various QImage based graphical effects. + * + * Everything is + * static, so there is no need to create an instance of this class. You can + * just call the static methods. They are encapsulated here merely to provide + * a common namespace. + */ +class KImageEffect +{ +public: + /** + * This enum provides a gradient type specification + * @see KImageEffect::blend(), KImageEffect::gradient(), + * KImageEffect::unbalancedGradient() + */ + enum GradientType { VerticalGradient, + HorizontalGradient, + DiagonalGradient, + CrossDiagonalGradient, + PyramidGradient, + RectangleGradient, + PipeCrossGradient, + EllipticGradient + }; + + /** + * This enum provides a RGB channel specification + * @see KImageEffect::blend(), KImageEffect::channelIntensity(), + * KImageEffect::modulate() + */ + enum RGBComponent { Red = 1, //!< Red channel + Green = 2, //!< Green channel + Blue = 4, //!< Blue channel + All = 7, //!< All channels + Alpha = 8 + //Gray = 15 //!< Grey channel + }; + + /** + * This enum provides a lighting direction specification + * @see KImageEffect::hash() + */ + enum Lighting {NorthLite, //!< Lighting from the top of the image + NWLite, //!< Lighting from the top left of the image + WestLite, //!< Lighting from the left of the image + SWLite, //!< Lighting from the bottom left of the image + SouthLite, //!< Lighting from the bottom of the image + SELite, //!< Lighting from the bottom right of the image + EastLite, //!< Lighting from the right of the image + NELite //!< Lighting from the top right of the image + }; + + /** + * This enum provides a modulation type specification + * @see KImageEffect::modulate() + */ + enum ModulationType { Intensity, //!< Modulate image intensity + Saturation, //!< Modulate image saturation + HueShift, //!< Modulate image hue + Contrast //!< Modulate image contrast + }; + + /** + * This enum provides a noise type specification + * @see KImageEffect::addNoise() + */ + enum NoiseType { UniformNoise=0, //!< Uniform distribution + GaussianNoise, //!< Gaussian distribution + MultiplicativeGaussianNoise, //!< Multiplicative Gaussian distribution + ImpulseNoise, //!< Impulse distribution + LaplacianNoise, //!< Laplacian distribution + PoissonNoise //!< Poisson distribution + }; + + /** + * This enum provides a rotation specification. + * @see KImageEffect::rotate() + */ + enum RotateDirection{ Rotate90, //!< Rotate 90 degrees to the right. + Rotate180, //!< Rotate 180 degrees. + Rotate270 //!< Rotate 90 degrees to the left. + }; + + /** + * Create a gradient from color a to color b of the specified type. + * + * @param size The desired size of the gradient. + * @param ca Color a + * @param cb Color b + * @param type The type of gradient. + * @param ncols The number of colors to use when not running on a + * truecolor display. The gradient will be dithered to this number of + * colors. Pass 0 to prevent dithering. + */ + static QImage gradient(const QSize &size, const QColor &ca, + const QColor &cb, GradientType type, int ncols=3); + + /** + * Create an unbalanced gradient. + * + * An unbalanced gradient is a gradient where the transition from + * color a to color b is not linear, but in this case, exponential. + * + * @param size The desired size of the gradient. + * @param ca Color a + * @param cb Color b + * @param type The type of gradient. + * @param xfactor The x decay length. Use a value between -200 and 200. + * @param yfactor The y decay length. + * @param ncols The number of colors. See KImageEffect:gradient. + */ + static QImage unbalancedGradient(const QSize &size, const QColor &ca, + const QColor &cb, GradientType type, int xfactor = 100, + int yfactor = 100, int ncols = 3); + +#if 0 + /** + * Blends a color into the destination image, using an opacity + * value for blending one into another. Very fast direct pixel + * manipulation is used. + * + * This function uses MMX and SSE2 instructions to blend the + * image on processors that support it. + * + * @param clr source color to be blended into the destination image. + * @param dst destination image in which the source will be blended into. + * @param opacity opacity (between 0.0 and 1.0) which determines how much + * the source color will be blended into the destination image. + * @return The destination image (dst) containing the result. + * @author Karol Szwed (gallium@kde.org) + * @author Fredrik Höglund (fredrik@kde.org) + */ + static QImage& blend(const QColor& clr, QImage& dst, float opacity); + + /** + * Blend the src image into the destination image, using an opacity + * value for blending one into another. Very fast direct pixel + * manipulation is used. + * + * This function uses MMX and SSE2 instructions to blend the + * images on processors that support it. + * + * @param src source image to be blended into the destination image. + * @param dst destination image in which the source will be blended into. + * @param opacity opacity (between 0.0 and 1.0) which determines how much + * the source image will be blended into the destination image. + * @return The destination image (dst) containing the result. + * @author Karol Szwed (gallium@kde.org) + * @author Fredrik Höglund (fredrik@kde.org) + */ + static QImage& blend(QImage& src, QImage& dst, float opacity); + + /** + * Blend the provided image into a background of the indicated color. + * + * @param initial_intensity this parameter takes values from -1 to 1: + * a) if positive: how much to fade the image in its + * less affected spot + * b) if negative: roughly indicates how much of the image + * remains unaffected + * @param bgnd indicates the color of the background to blend in + * @param eff lets you choose what kind of blending you like + * @param anti_dir blend in the opposite direction (makes no much sense + * with concentric blending effects) + * @param image must be 32bpp + */ + static QImage& blend(QImage &image, float initial_intensity, + const QColor &bgnd, GradientType eff, + bool anti_dir=false); + + /** + * Blend an image into another one, using a gradient type + * for blending from one to another. + * + * @param image1 source1 and result of blending + * @param image2 source2 of blending + * @param gt gradient type for blending between source1 and source2 + * @param xf x decay length for unbalanced gradient tpye + * @param yf y decay length for unbalanced gradient tpye + */ + static QImage& blend(QImage &image1,QImage &image2, + GradientType gt, int xf=100, int yf=100); + + /** + * Blend an image into another one, using a color channel of a + * third image for the decision of blending from one to another. + * + * @param image1 Source 1 and result of blending + * @param image2 Source 2 of blending + * @param blendImage If the gray value of of pixel is 0, the result + * for this pixel is that of image1; for a gray value + * of 1, the pixel of image2 is used; for a value + * in between, a corresponding blending is used. + * @param channel The RBG channel to use for the blending decision. + */ + static QImage& blend(QImage &image1, QImage &image2, + QImage &blendImage, RGBComponent channel); + + /** + * Blend an image into another one, using alpha in the expected way. + * @param upper the "upper" image + * @param lower the "lower" image + * @param output the target image + * @author Rik Hemsley (rikkus) + */ + static bool blend(const QImage & upper, const QImage & lower, QImage & output); +// Not yet... static bool blend(const QImage & image1, const QImage & image2, QImage & output, const QRect & destRect); + + /** + * Blend an image into another one, using alpha in the expected way and + * over coordinates @p x and @p y with respect to the lower image. + * The output is a QImage which is the @p upper image already blended + * with the @p lower one, so its size will be (in general) the same than + * @p upper instead of the same size than @p lower like the method above. + * In fact, the size of @p output is like upper's one only when it can be + * painted on lower, if there has to be some clipping, output's size will + * be the clipped area and x and y will be set to the correct up-left corner + * where the clipped rectangle begins. + * @param x x-coordinate of lower image + * @param y y-coordinate of lower image + * @param upper the "upper" image + * @param lower the "lower" image + * @param output the target image + */ + static bool blend(int &x, int &y, const QImage & upper, const QImage & lower, QImage & output); + + /** + * Blend an image into another one, using alpha in the expected way and + * over coordinates @p x and @p y with respect to the lower image. + * The output is painted in the own @p lower image. This is an optimization + * of the blend method above provided by convenience. + * @param x x-coordinate of lower image + * @param y y-coordinate of lower image + * @param upper the "upper" image + * @param lower the "lower" image, which becomes the output image + */ + static bool blendOnLower(int x, int y, const QImage & upper, const QImage & lower); + + /** + * Blend part of an image into part of another, using the alpha channel in + * the expected way. + * Note that the destination rectangle will be correctly clipped. + * + * @param upper the "upper" image + * @param upperOffset Offset for the part of the upper image to be used. + * @param lower the "lower" image + * @param lowerRect Rectangle for the part of the lower image where the + * blending will occur. + * @since 3.2 + */ + static void blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect); + + /** + * Blend part of an image into part of another, using the opacity value + * and the alpha channel in the expected way. + * Note that the destination rectangle will be correctly clipped. + * + * @param upper the "upper" image + * @param upperOffset Offset for the part of the upper image to be used. + * @param lower the "lower" image + * @param lowerRect Rectangle for the part of the lower image where the + * blending will occur. + * @param opacity Opacity (between 0.0 and 1.0) which determines how much + * the source image will be blended into the destination image. + * @since 3.2 + */ + static void blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect, float opacity); + + /** + * Disposition of a source image on top of a destination image. + * @see KImageEffect::computeDestinationRect, KImageEffect::blendOnLower + * @since 3.2 + */ + enum Disposition { NoImage = 0, //!< Don't overlay + Centered, //!< Center top image on botton image + Tiled, //!< Tile top image on bottom image + CenterTiled, //!< Center and tile top image on bottom image + CenteredMaxpect, //!< Center and scale aspect + TiledMaxpect, //!< Tile and scale aspect + Scaled, //!< Scale + CenteredAutoFit //!< Center and scale or scale aspect + }; + + /** + * Compute the destination rectangle where to draw the upper image on top + * of another image using the given disposition. For tiled + * disposition, the rectangle should be duplicated on the whole area to + * obtained the wanted effect. + * + * @param lowerSize The size of the destination image. + * @param disposition The wanted disposition. + * @param upper The upper image. Note that this image may be scaled to + * adjust to the requested disposition. + * + * @return the computed rectangle. Its size may exceed @e lowerSize. + * @since 3.2 + */ + static QRect computeDestinationRect(const QSize &lowerSize, + Disposition disposition, QImage &upper); + + /** + * Blend an image on top of another using a given disposition and a given + * opacity. The alpha channel of the upper image is used in the expected + * way. Beware the upper image may be modified. + * @since 3.2 + */ + static void blendOnLower(QImage &upper, QImage &lower, + Disposition disposition, float opacity); +#endif + + /** + * Fade an image to a certain background color. + * + * The number of colors will not be changed. + * + * @param image The QImage to process. + * @param val The strength of the effect. 0 <= val <= 1. + * @param color The background color. + * @return Returns the image(), provided for convenience. + */ + static QImage& fade(QImage &image, float val, const QColor &color); + + /** + * This recolors a pixmap. The most dark color will become color a, + * the most bright one color b, and in between. + * + * @param image A QImage to process. + * @param ca Color a + * @param cb Color b + * @param ncols The number of colors to dither the image to. + * Pass 0 to prevent dithering. + */ + static QImage& flatten(QImage &image, const QColor &ca, + const QColor &cb, int ncols=0); + +#if 0 + /** + * Build a hash on any given QImage + * + * @param image The QImage to process + * @param lite The hash faces the indicated lighting (cardinal poles). + * @param spacing How many unmodified pixels in between hashes. + * @return Returns the image(), provided for convenience. + */ + static QImage& hash(QImage &image, Lighting lite=NorthLite, + unsigned int spacing=0); + +#endif + + /** + * Either brighten or dim the image by a specified percent. + * For example, .50 will modify the colors by 50%. + * + * This function uses MMX instructions to process the image + * on processors that support it. + * + * @param image The QImage to process. + * @param percent The percent value. Use a negative value to dim. + * @return Returns The image(), provided for convenience. + * @author Daniel M. Duley (mosfet) + * @author Benjamin Roe (ben@benroe.com) + */ + static QImage& intensity(QImage &image, float percent); + + /** + * Modifies the intensity of a pixmap's RGB channel component. + * + * @param image The QImage to process. + * @param percent Percent value. Use a negative value to dim. + * @param channel Which channel(s) should be modified + * @return The @p image, provided for convenience. + * @author Daniel M. Duley (mosfet) + */ + static QImage& channelIntensity(QImage &image, float percent, + RGBComponent channel); + +#if 0 + /** + * Modulate the image with a color channel of another image. + * + * @param image The QImage to modulate and result. + * @param modImage The QImage to use for modulation. + * @param reverse Invert the meaning of image/modImage; result is image! + * @param type The modulation Type to use. + * @param factor The modulation amplitude; with 0 no effect [-200;200]. + * @param channel The RBG channel of image2 to use for modulation. + * @return Returns the image(), provided for convenience. + */ + static QImage& modulate(QImage &image, QImage &modImage, bool reverse, + ModulationType type, int factor, RGBComponent channel); +#endif + + /** + * Convert an image to grayscale. + * + * @param image The QImage to process. + * @param fast Set to @p true in order to use a faster but non-photographic + * quality algorithm. Appropriate for things such as toolbar icons. + * @return Returns the image(), provided for convenience. + * @author Daniel M. Duley (mosfet) + */ + static QImage& toGray(QImage &image, bool fast = false); + + /** + * Desaturate an image evenly. + * + * @param image The QImage to process. + * @param desat A value between 0 and 1 setting the degree of desaturation + * @return Returns the image(), provided for convenience. + */ + static QImage& desaturate(QImage &image, float desat = 0.3); + +#if 0 + /** + * Fast, but low quality contrast of an image. Also see contrastHSV. + * + * @param image The QImage to process. + * @param c A contrast value between -255 to 255. + * @return The image(), provided for convenience. + * @author Daniel M. Duley (mosfet) + */ + static QImage& contrast(QImage &image, int c); + + /** + * Dither an image using Floyd-Steinberg dithering for low-color + * situations. + * + * @param image The QImage to process. + * @param palette The color palette to use + * @param size The size of the palette + * @return Returns the image(), provided for convenience. + */ + static QImage& dither(QImage &image, const QColor *palette, int size); + + /** + * Calculate the image for a selected image, for instance a selected icon + * on the desktop. + * @param img the QImage to select + * @param col the selected color, usually from QColorGroup::highlight(). + */ + static QImage& selectedImage( QImage &img, const QColor &col ); + + /** + * High quality, expensive HSV contrast. You can do a faster one by just + * taking a intensity threshold (ie: 128) and incrementing RGB color + * channels above it and decrementing those below it, but this gives much + * better results. + * + * @param img The QImage to process. + * @param sharpen If true sharpness is increase, (spiffed). Otherwise + * it is decreased, (dulled). + * @author Daniel M. Duley (mosfet) + */ + static void contrastHSV(QImage &img, bool sharpen=true); + #endif + + /** + * Normalises the pixel values to span the full range of color values. + * This is a contrast enhancement technique. + * @param img the image that is normalised + * @author Daniel M. Duley (mosfet) + */ + static void normalize(QImage &img); + + /** + * Performs histogram equalisation on the reference + * image. + * @param img the image that is equalised + * @author Daniel M. Duley (mosfet) + */ + static void equalize(QImage &img); + + /** + * Thresholds the reference image. You can also threshold images by using + * ThresholdDither in the various QPixmap/QImage convert methods, but this + * lets you specify a threshold value. + * + * @param img The QImage to process. + * @param value The threshold value. + * @author Daniel M. Duley (mosfet) + */ + static void threshold(QImage &img, unsigned int value=128); + + /** + * Produces a 'solarization' effect seen when exposing a photographic + * film to light during the development process. + * + * @param img The QImage to process. + * @param factor The extent of the solarization (0-99.9) + * @author Daniel M. Duley (mosfet) + */ + static void solarize(QImage &img, double factor=50.0); + + /** + * Embosses the source image. This involves highlighting the edges + * and applying various other enhancements in order to get a metal + * effect. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The embossed image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage emboss(QImage &src, double radius, double sigma); + + #if 0 + /** + * Convenience method. + */ + static QImage emboss(QImage &src); + #endif + + /** + * Minimizes speckle noise in the source image using the 8 hull + * algorithm. + * + * @param src The QImage to process. + * @return The despeckled image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage despeckle(QImage &src); + + /** + * Produces a neat little "charcoal" effect. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The charcoal image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage charcoal(QImage &src, double radius, double sigma); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * with a radius and sigma instead! + */ + static QImage charcoal(QImage &src, double factor=50.0); + + /** + * Rotates the image by the specified amount + * + * @param src The QImage to process. + * @param r The rotate direction. + * @return The rotated image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage rotate(QImage &src, RotateDirection r); + + /** + * Scales an image using simple pixel sampling. This does not produce + * nearly as nice a result as QImage::smoothScale(), but has the + * advantage of being much faster - only a few milliseconds. + * + * @param src The QImage to process. + * @param w The new width. + * @param h The new height. + * @return The scaled image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage sample(QImage &src, int w, int h); + #endif + + /** + * Adds noise to an image. + * + * @param src The QImage to process. + * @param type The algorithm used to generate the noise. + * @return The image with noise added. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage addNoise(QImage &src, NoiseType type = GaussianNoise); + + /** + * Blurs an image by convolving pixel neighborhoods. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The blurred image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage blur(QImage &src, double radius, double sigma); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * with a radius and sigma instead! + */ + static QImage blur(QImage &src, double factor=50.0); + #endif + + /** + * Detects edges in an image using pixel neighborhoods and an edge + * detection mask. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @return The image with edges detected. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage edge(QImage &src, double radius); + + /** + * Implodes an image by a specified percent. + * + * @param src The QImage to process. + * @param factor The extent of the implosion. + * @param background An RGBA value to use for the background. After the + * effect some pixels may be "empty". This value is used for those pixels. + * @return The imploded image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage implode(QImage &src, double factor=30.0, + unsigned int background = 0xFFFFFFFF); + + /** + * Produces an oil painting effect. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @return The new image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage oilPaintConvolve(QImage &src, double radius); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * instead! + */ + static QImage oilPaint(QImage &src, int radius=3); + #endif + + /** + * Sharpens the pixels in the image using pixel neighborhoods. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The sharpened image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage sharpen(QImage &src, double radius, double sigma); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * instead! + */ + static QImage sharpen(QImage &src, double factor=30.0); + #endif + + /** + * Randomly displaces pixels. + * + * @param src The QImage to process. + * @param amount The vicinity for choosing a random pixel to swap. + * @return The image with pixels displaced. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage spread(QImage &src, unsigned int amount=3); + + /** + * Shades the image using a distance light source. + * + * @param src The QImage to process. + * @param color_shading If true do color shading, otherwise do grayscale. + * @param azimuth Determines the light source and direction. + * @param elevation Determines the light source and direction. + * @return The shaded image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage shade(QImage &src, bool color_shading, double azimuth, + double elevation); + /** + * Swirls the image by a specified amount + * + * @param src The QImage to process. + * @param degrees The tightness of the swirl. + * @param background An RGBA value to use for the background. After the + * effect some pixels may be "empty". This value is used for those pixels. + * @return The swirled image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage swirl(QImage &src, double angle, unsigned int background = + 0xFFFFFFFF); + + /** + * Modifies the pixels along a sine wave. + * + * @param src The QImage to process. + * @param amplitude The amplitude of the sine wave. + * @param frequency The frequency of the sine wave. + * @param background An RGBA value to use for the background. After the + * effect some pixels may be "empty". This value is used for those pixels. + * @return The new image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage wave(QImage &src, double amplitude=25.0, double frequency=150.0, + unsigned int background = 0xFFFFFFFF); +private: + +#if 0 + /** + * Helper function to fast calc some altered (lighten, shaded) colors + * + */ + static unsigned int lHash(unsigned int c); + static unsigned int uHash(unsigned int c); + + /** + * Helper function to find the nearest color to the RBG triplet + */ + static int nearestColor( int r, int g, int b, const QColor *pal, int size ); +#endif + static void hull(const int x_offset, const int y_offset, const int polarity, + const int width, const int height, + unsigned int *f, unsigned int *g); + static unsigned int generateNoise(unsigned int pixel, NoiseType type); + static unsigned int interpolateColor(QImage *image, double x, double y, + unsigned int background); + /* Various convolve routines */ + static int getOptimalKernelWidth(double radius, double sigma); + static bool convolveImage(QImage *image, QImage *dest, + const unsigned int order, + const double *kernel); + static void blurScanLine(double *kernel, int width, + unsigned int *src, unsigned int *dest, + int columns); + static int getBlurKernel(int width, double sigma, double **kernel); +}; + +#endif diff --git a/main/lib/image.effect/main.cpp b/main/lib/image.effect/main.cpp new file mode 100644 index 00000000..074a2cc7 --- /dev/null +++ b/main/lib/image.effect/main.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "CImage.h" +#include "main.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +GB_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ImageHistogramDesc, + CImageDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/main/lib/image.effect/main.h b/main/lib/image.effect/main.h new file mode 100644 index 00000000..43b77658 --- /dev/null +++ b/main/lib/image.effect/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "../image/gb.image.h" + +#ifndef __MAIN_CPP +extern "C" GB_INTERFACE GB; +extern "C" IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/image.effect/qcolor.cpp b/main/lib/image.effect/qcolor.cpp new file mode 100644 index 00000000..d7c279ca --- /dev/null +++ b/main/lib/image.effect/qcolor.cpp @@ -0,0 +1,1021 @@ +/*************************************************************************** + + qcolor.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qcolor.cpp 3.3.4 edited Dec 22 17:04 $ +** +** Implementation of QColor class +** +** Created : 940112 +** +** Copyright (C) 1992-2002 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qcolor.h" +//#include "qnamespace.h" +//#include "qdatastream.h" + +#include + + +/*! + \class QColor qcolor.h + \brief The QColor class provides colors based on RGB or HSV values. + + \ingroup images + \ingroup graphics + \ingroup appearance + + A color is normally specified in terms of RGB (red, green and blue) + components, but it is also possible to specify HSV (hue, saturation + and value) or set a color name (the names are copied from from the + X11 color database). + + In addition to the RGB value, a QColor also has a pixel value and a + validity. The pixel value is used by the underlying window system + to refer to a color. It can be thought of as an index into the + display hardware's color table. + + The validity (isValid()) indicates whether the color is legal at + all. For example, a RGB color with RGB values out of range is + illegal. For performance reasons, QColor mostly disregards illegal + colors. The result of using an invalid color is unspecified and + will usually be surprising. + + There are 19 predefined QColor objects: \c white, \c black, \c + red, \c darkRed, \c green, \c darkGreen, \c blue, \c darkBlue, \c + cyan, \c darkCyan, \c magenta, \c darkMagenta, \c yellow, \c + darkYellow, \c gray, \c darkGray, \c lightGray, \c color0 and \c + color1, accessible as members of the Qt namespace (ie. \c Qt::red). + + \img qt-colors.png Qt Colors + + The colors \c color0 (zero pixel value) and \c color1 (non-zero + pixel value) are special colors for drawing in \link QBitmap + bitmaps\endlink. Painting with \c color0 sets the bitmap bits to 0 + (transparent, i.e. background), and painting with \c color1 sets the + bits to 1 (opaque, i.e. foreground). + + The QColor class has an efficient, dynamic color allocation + strategy. A color is normally allocated the first time it is used + (lazy allocation), that is, whenever the pixel() function is called. + The following steps are taken to allocate a color. If, at any point, + a suitable color is found then the appropriate pixel value is + returned and the subsequent steps are not taken: + + \list 1 + \i Is the pixel value valid? If it is, just return it; otherwise, + allocate a pixel value. + \i Check an internal hash table to see if we allocated an equal RGB + value earlier. If we did, set the corresponding pixel value for the + color and return it. + \i Try to allocate the RGB value. If we succeed, we get a pixel value + that we save in the internal table with the RGB value. + Return the pixel value. + \i The color could not be allocated. Find the closest matching + color, save it in the internal table, and return it. + \endlist + + A color can be set by passing setNamedColor() an RGB string like + "#112233", or a color name, e.g. "blue". The names are taken from + X11's rgb.txt database but can also be used under Windows. To get + a lighter or darker color use light() and dark() respectively. + Colors can also be set using setRgb() and setHsv(). The color + components can be accessed in one go with rgb() and hsv(), or + individually with red(), green() and blue(). + + Use maxColors() and numBitPlanes() to determine the maximum number + of colors and the number of bit planes supported by the underlying + window system, + + If you need to allocate many colors temporarily, for example in an + image viewer application, enterAllocContext(), leaveAllocContext() and + destroyAllocContext() will prove useful. + + \section1 HSV Colors + + Because many people don't know the HSV color model very well, we'll + cover it briefly here. + + The RGB model is hardware-oriented. Its representation is close to + what most monitors show. In contrast, HSV represents color in a way + more suited to the human perception of color. For example, the + relationships "stronger than", "darker than" and "the opposite of" + are easily expressed in HSV but are much harder to express in RGB. + + HSV, like RGB, has three components: + + \list + + \i H, for hue, is either 0-359 if the color is chromatic (not + gray), or meaningless if it is gray. It represents degrees on the + color wheel familiar to most people. Red is 0 (degrees), green is + 120 and blue is 240. + + \i S, for saturation, is 0-255, and the bigger it is, the + stronger the color is. Grayish colors have saturation near 0; very + strong colors have saturation near 255. + + \i V, for value, is 0-255 and represents lightness or brightness + of the color. 0 is black; 255 is as far from black as possible. + + \endlist + + Here are some examples: Pure red is H=0, S=255, V=255. A dark red, + moving slightly towards the magenta, could be H=350 (equivalent to + -10), S=255, V=180. A grayish light red could have H about 0 (say + 350-359 or 0-10), S about 50-100, and S=255. + + Qt returns a hue value of -1 for achromatic colors. If you pass a + too-big hue value, Qt forces it into range. Hue 360 or 720 is + treated as 0; hue 540 is treated as 180. + + \sa QPalette, QColorGroup, QApplication::setColorSpec(), + \link http://www.inforamp.net/~poynton/Poynton-color.html Color FAQ\endlink +*/ + +/***************************************************************************** + Global colors + *****************************************************************************/ + +#if defined(Q_WS_WIN) +#define COLOR0_PIX 0x00ffffff +#define COLOR1_PIX 0 +#else +#define COLOR0_PIX 0 +#define COLOR1_PIX 1 +#endif + +#if 0 +static QColor stdcol[19]; + +QT_STATIC_CONST_IMPL QColor & Qt::color0 = stdcol[0]; +QT_STATIC_CONST_IMPL QColor & Qt::color1 = stdcol[1]; +QT_STATIC_CONST_IMPL QColor & Qt::black = stdcol[2]; +QT_STATIC_CONST_IMPL QColor & Qt::white = stdcol[3]; +QT_STATIC_CONST_IMPL QColor & Qt::darkGray = stdcol[4]; +QT_STATIC_CONST_IMPL QColor & Qt::gray = stdcol[5]; +QT_STATIC_CONST_IMPL QColor & Qt::lightGray = stdcol[6]; +QT_STATIC_CONST_IMPL QColor & Qt::red = stdcol[7]; +QT_STATIC_CONST_IMPL QColor & Qt::green = stdcol[8]; +QT_STATIC_CONST_IMPL QColor & Qt::blue = stdcol[9]; +QT_STATIC_CONST_IMPL QColor & Qt::cyan = stdcol[10]; +QT_STATIC_CONST_IMPL QColor & Qt::magenta = stdcol[11]; +QT_STATIC_CONST_IMPL QColor & Qt::yellow = stdcol[12]; +QT_STATIC_CONST_IMPL QColor & Qt::darkRed = stdcol[13]; +QT_STATIC_CONST_IMPL QColor & Qt::darkGreen = stdcol[14]; +QT_STATIC_CONST_IMPL QColor & Qt::darkBlue = stdcol[15]; +QT_STATIC_CONST_IMPL QColor & Qt::darkCyan = stdcol[16]; +QT_STATIC_CONST_IMPL QColor & Qt::darkMagenta = stdcol[17]; +QT_STATIC_CONST_IMPL QColor & Qt::darkYellow = stdcol[18]; +#endif + +/***************************************************************************** + QColor member functions + *****************************************************************************/ + +bool QColor::color_init = FALSE; // color system not initialized +bool QColor::globals_init = FALSE; // global color not initialized +QColor::ColorModel QColor::colormodel = d32; + +#if 0 +QColor* QColor::globalColors() +{ + return stdcol; +} +#endif + +/*! + Initializes the global colors. This function is called if a global + color variable is initialized before the constructors for our + global color objects are executed. Without this mechanism, + assigning a color might assign an uninitialized value. + + Example: + \code + QColor myColor = red; // will initialize red etc. + + int main( int argc, char **argc ) + { + } + \endcode +*/ + +#if 0 +void QColor::initGlobalColors() +{ + globals_init = TRUE; + +#if 0 + #ifdef Q_WS_X11 + // HACK: we need a way to recognize color0 and color1 uniquely, so + // that we can use color0 and color1 with fixed pixel values on + // all screens + stdcol[ 0].d.argb = qRgba(255, 255, 255, 1); + stdcol[ 1].d.argb = qRgba( 0, 0, 0, 1); + #else + stdcol[ 0].d.argb = qRgb(255,255,255); + stdcol[ 1].d.argb = 0; + #endif // Q_WS_X11 + stdcol[ 0].setPixel( COLOR0_PIX ); + stdcol[ 1].setPixel( COLOR1_PIX ); + + // From the "The Palette Manager: How and Why" by Ron Gery, March 23, + // 1992, archived on MSDN: + // The Windows system palette is broken up into two sections, + // one with fixed colors and one with colors that can be changed + // by applications. The system palette predefines 20 entries; + // these colors are known as the static or reserved colors and + // consist of the 16 colors found in the Windows version 3.0 VGA + // driver and 4 additional colors chosen for their visual appeal. + // The DEFAULT_PALETTE stock object is, as the name implies, the + // default palette selected into a device context (DC) and consists + // of these static colors. Applications can set the remaining 236 + // colors using the Palette Manager. + // The 20 reserved entries have indices in [0,9] and [246,255]. We + // reuse 17 of them. + stdcol[ 2].setRgb( 0, 0, 0 ); // index 0 black + stdcol[ 3].setRgb( 255, 255, 255 ); // index 255 white + stdcol[ 4].setRgb( 128, 128, 128 ); // index 248 medium gray + stdcol[ 5].setRgb( 160, 160, 164 ); // index 247 light gray + stdcol[ 6].setRgb( 192, 192, 192 ); // index 7 light gray + stdcol[ 7].setRgb( 255, 0, 0 ); // index 249 red + stdcol[ 8].setRgb( 0, 255, 0 ); // index 250 green + stdcol[ 9].setRgb( 0, 0, 255 ); // index 252 blue + stdcol[10].setRgb( 0, 255, 255 ); // index 254 cyan + stdcol[11].setRgb( 255, 0, 255 ); // index 253 magenta + stdcol[12].setRgb( 255, 255, 0 ); // index 251 yellow + stdcol[13].setRgb( 128, 0, 0 ); // index 1 dark red + stdcol[14].setRgb( 0, 128, 0 ); // index 2 dark green + stdcol[15].setRgb( 0, 0, 128 ); // index 4 dark blue + stdcol[16].setRgb( 0, 128, 128 ); // index 6 dark cyan + stdcol[17].setRgb( 128, 0, 128 ); // index 5 dark magenta + stdcol[18].setRgb( 128, 128, 0 ); // index 3 dark yellow +#endif +} +#endif + +/*! + \enum QColor::Spec + + The type of color specified, either RGB or HSV, e.g. in the + \c{QColor::QColor( x, y, z, colorSpec)} constructor. + + \value Rgb + \value Hsv +*/ + + +/*! + \fn QColor::QColor() + + Constructs an invalid color with the RGB value (0, 0, 0). An + invalid color is a color that is not properly set up for the + underlying window system. + + The alpha value of an invalid color is unspecified. + + \sa isValid() +*/ + + +/*! + \fn QColor::QColor( int r, int g, int b ) + + Constructs a color with the RGB value \a r, \a g, \a b, in the + same way as setRgb(). + + The color is left invalid if any or the arguments are illegal. + + \sa setRgb() +*/ + + +/*! + Constructs a color with the RGB value \a rgb and a custom pixel + value \a pixel. + + If \a pixel == 0xffffffff (the default), then the color uses the + RGB value in a standard way. If \a pixel is something else, then + the pixel value is set directly to \a pixel, skipping the normal + allocation procedure. +*/ + +QColor::QColor( QRgb rgb, uint pixel ) +{ + if ( pixel == 0xffffffff ) { + setRgb( rgb ); + } else { + d.argb = rgb; + setPixel( pixel ); + } +} + +void QColor::setPixel( uint pixel ) +{ + switch ( colormodel ) { + case d8: + d.d8.direct = TRUE; + d.d8.invalid = FALSE; + d.d8.dirty = FALSE; + d.d8.pix = pixel; + break; + case d32: + d.d32.pix = pixel; + break; + } +} + + +/*! + Constructs a color with the RGB or HSV value \a x, \a y, \a z. + + The arguments are an RGB value if \a colorSpec is QColor::Rgb. \a + x (red), \a y (green), and \a z (blue). All of them must be in the + range 0-255. + + The arguments are an HSV value if \a colorSpec is QColor::Hsv. \a + x (hue) must be -1 for achromatic colors and 0-359 for chromatic + colors; \a y (saturation) and \a z (value) must both be in the + range 0-255. + + \sa setRgb(), setHsv() +*/ + +QColor::QColor( int x, int y, int z, Spec colorSpec ) +{ + d.d32.argb = Invalid; + d.d32.pix = Dirt; + if ( colorSpec == Hsv ) + setHsv( x, y, z ); + else + setRgb( x, y, z ); +} + + +/*! + Constructs a named color in the same way as setNamedColor() using + name \a name. + + The color is left invalid if \a name cannot be parsed. + + \sa setNamedColor() +*/ + +#if 0 +QColor::QColor( const QString& name ) +{ + setNamedColor( name ); +} +#endif + +/*! + Constructs a named color in the same way as setNamedColor() using + name \a name. + + The color is left invalid if \a name cannot be parsed. + + \sa setNamedColor() +*/ + +#if 0 +QColor::QColor( const char *name ) +{ + setNamedColor( QString(name) ); +} +#endif + + +/*! + Constructs a color that is a copy of \a c. +*/ + +QColor::QColor( const QColor &c ) +{ + //if ( !globals_init ) + //initGlobalColors(); + d.argb = c.d.argb; + d.d32.pix = c.d.d32.pix; +} + + +/*! + Assigns a copy of the color \a c and returns a reference to this + color. +*/ + +QColor &QColor::operator=( const QColor &c ) +{ + //if ( !globals_init ) + //initGlobalColors(); + d.argb = c.d.argb; + d.d32.pix = c.d.d32.pix; + return *this; +} + + +/*! + \fn bool QColor::isValid() const + + Returns FALSE if the color is invalid, i.e. it was constructed using the + default constructor; otherwise returns TRUE. +*/ + +/*! + \internal +*/ +bool QColor::isDirty() const +{ + if ( colormodel == d8 ) { + return d.d8.dirty; + } else { + return d.d32.probablyDirty(); + } +} + +/*! + Returns the name of the color in the format "#RRGGBB", i.e. a "#" + character followed by three two-digit hexadecimal numbers. + + \sa setNamedColor() +*/ + +#if 0 +QString QColor::name() const +{ +#ifndef QT_NO_SPRINTF + QString s; + s.sprintf( "#%02x%02x%02x", red(), green(), blue() ); + return s; +#else + char s[20]; + sprintf( s, "#%02x%02x%02x", red(), green(), blue() ); + return QString(s); +#endif +} + +static int hex2int( QChar hexchar ) +{ + int v; + if ( hexchar.isDigit() ) + v = hexchar.digitValue(); + else if ( hexchar >= 'A' && hexchar <= 'F' ) + v = hexchar.cell() - 'A' + 10; + else if ( hexchar >= 'a' && hexchar <= 'f' ) + v = hexchar.cell() - 'a' + 10; + else + v = -1; + return v; +} +#endif + + +/*! + Sets the RGB value to \a name, which may be in one of these + formats: + \list + \i #RGB (each of R, G and B is a single hex digit) + \i #RRGGBB + \i #RRRGGGBBB + \i #RRRRGGGGBBBB + \i A name from the X color database (rgb.txt) (e.g. + "steelblue" or "gainsboro"). These color names also work + under Windows. + \endlist + + The color is invalid if \a name cannot be parsed. +*/ + +#if 0 +void QColor::setNamedColor( const QString &name ) +{ + if ( name.isEmpty() ) { + d.argb = 0; + if ( colormodel == d8 ) { + d.d8.invalid = TRUE; + } else { + d.d32.argb = Invalid; + } + } else if ( name[0] == '#' ) { + const QChar *p = name.unicode()+1; + int len = name.length()-1; + int r, g, b; + if ( len == 12 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[1]); + g = (hex2int(p[4]) << 4) + hex2int(p[5]); + b = (hex2int(p[8]) << 4) + hex2int(p[9]); + } else if ( len == 9 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[1]); + g = (hex2int(p[3]) << 4) + hex2int(p[4]); + b = (hex2int(p[6]) << 4) + hex2int(p[7]); + } else if ( len == 6 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[1]); + g = (hex2int(p[2]) << 4) + hex2int(p[3]); + b = (hex2int(p[4]) << 4) + hex2int(p[5]); + } else if ( len == 3 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[0]); + g = (hex2int(p[1]) << 4) + hex2int(p[1]); + b = (hex2int(p[2]) << 4) + hex2int(p[2]); + } else { + r = g = b = -1; + } + if ( (uint)r > 255 || (uint)g > 255 || (uint)b > 255 ) { + d.d32.argb = Invalid; + d.d32.pix = Dirt; +#if defined(QT_CHECK_RANGE) + qWarning( "QColor::setNamedColor: could not parse color '%s'", + name.local8Bit().data() ); +#endif + } else { + setRgb( r, g, b ); + } + } else { + setSystemNamedColor( name ); + } +} +#endif + +#undef max +#undef min + +/*! + \fn void QColor::getHsv( int &h, int &s, int &v ) const + \obsolete +*/ + +/*! \fn void QColor::getHsv( int *h, int *s, int *v ) const + + Returns the current RGB value as HSV. The contents of the \a h, \a + s and \a v pointers are set to the HSV values. If any of the three + pointers are null, the function does nothing. + + The hue (which \a h points to) is set to -1 if the color is + achromatic. + + \warning Colors are stored internally as RGB values, so getHSv() + may return slightly different values to those set by setHsv(). + + \sa setHsv(), rgb() +*/ + +/*! \obsolete Use getHsv() instead. + */ +void QColor::hsv( int *h, int *s, int *v) const +{ + if ( !h || !s || !v ) + return; + int r = qRed(d.argb); + int g = qGreen(d.argb); + int b = qBlue(d.argb); + uint max = r; // maximum RGB component + int whatmax = 0; // r=>0, g=>1, b=>2 + if ( (uint)g > max ) { + max = g; + whatmax = 1; + } + if ( (uint)b > max ) { + max = b; + whatmax = 2; + } + uint min = r; // find minimum value + if ( (uint)g < min ) min = g; + if ( (uint)b < min ) min = b; + int delta = max-min; + *v = max; // calc value + *s = max ? (510*delta+max)/(2*max) : 0; + if ( *s == 0 ) { + *h = -1; // undefined hue + } else { + switch ( whatmax ) { + case 0: // red is max component + if ( g >= b ) + *h = (120*(g-b)+delta)/(2*delta); + else + *h = (120*(g-b+delta)+delta)/(2*delta) + 300; + break; + case 1: // green is max component + if ( b > r ) + *h = 120 + (120*(b-r)+delta)/(2*delta); + else + *h = 60 + (120*(b-r+delta)+delta)/(2*delta); + break; + case 2: // blue is max component + if ( r > g ) + *h = 240 + (120*(r-g)+delta)/(2*delta); + else + *h = 180 + (120*(r-g+delta)+delta)/(2*delta); + break; + } + } +} + + +/*! + Sets a HSV color value. \a h is the hue, \a s is the saturation + and \a v is the value of the HSV color. + + If \a s or \a v are not in the range 0-255, or \a h is < -1, the + color is not changed. + + \warning Colors are stored internally as RGB values, so getHSv() + may return slightly different values to those set by setHsv(). + + \sa hsv(), setRgb() +*/ + +void QColor::setHsv( int h, int s, int v ) +{ + if ( h < -1 || (uint)s > 255 || (uint)v > 255 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QColor::setHsv: HSV parameters out of range" ); +#endif + return; + } + int r=v, g=v, b=v; + if ( s == 0 || h == -1 ) { // achromatic case + // Ignore + } else { // chromatic case + if ( (uint)h >= 360 ) + h %= 360; + uint f = h%60; + h /= 60; + uint p = (uint)(2*v*(255-s)+255)/510; + uint q, t; + if ( h&1 ) { + q = (uint)(2*v*(15300-s*f)+15300)/30600; + switch( h ) { + case 1: r=(int)q; g=(int)v, b=(int)p; break; + case 3: r=(int)p; g=(int)q, b=(int)v; break; + case 5: r=(int)v; g=(int)p, b=(int)q; break; + } + } else { + t = (uint)(2*v*(15300-(s*(60-f)))+15300)/30600; + switch( h ) { + case 0: r=(int)v; g=(int)t, b=(int)p; break; + case 2: r=(int)p; g=(int)v, b=(int)t; break; + case 4: r=(int)t; g=(int)p, b=(int)v; break; + } + } + } + setRgb( r, g, b ); +} + + +/*! + \fn QRgb QColor::rgb() const + + Returns the RGB value. + + The return type \e QRgb is equivalent to \c unsigned \c int. + + For an invalid color, the alpha value of the returned color is + unspecified. + + \sa setRgb(), hsv(), qRed(), qBlue(), qGreen(), isValid() +*/ + +/*! \fn void QColor::getRgb( int *r, int *g, int *b ) const + + Sets the contents pointed to by \a r, \a g and \a b to the red, + green and blue components of the RGB value respectively. The value + range for a component is 0..255. + + \sa rgb(), setRgb(), getHsv() +*/ + +/*! \obsolete Use getRgb() instead */ +void QColor::rgb( int *r, int *g, int *b) const +{ + *r = qRed(d.argb); + *g = qGreen(d.argb); + *b = qBlue(d.argb); +} + + +/*! + Sets the RGB value to \a r, \a g, \a b. The arguments, \a r, \a g + and \a b must all be in the range 0..255. If any of them are + outside the legal range, the color is not changed. + + \sa rgb(), setHsv() +*/ + +void QColor::setRgb( int r, int g, int b) +{ +/* if ( (uint)r > 255 || (uint)g > 255 || (uint)b > 255 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QColor::setRgb: RGB parameter(s) out of range" ); +#endif + return; + }*/ + d.argb = qRgba( r, g, b, alpha() ); + if ( colormodel == d8 ) { + d.d8.invalid = FALSE; + d.d8.direct = FALSE; + d.d8.dirty = TRUE; + } else { + d.d32.pix = Dirt; + } +} + + +/*! + \overload + Sets the RGB value to \a rgb. + + The type \e QRgb is equivalent to \c unsigned \c int. + + \sa rgb(), setHsv() +*/ + +void QColor::setRgb( QRgb rgb ) +{ + d.argb = rgb; + if ( colormodel == d8 ) { + d.d8.invalid = FALSE; + d.d8.direct = FALSE; + d.d8.dirty = TRUE; + } else { + d.d32.pix = Dirt; + } +} + +/*! + \fn int QColor::red() const + + Returns the R (red) component of the RGB value. +*/ + + +/*! + \fn int QColor::green() const + + Returns the G (green) component of the RGB value. +*/ + +/*! + \fn int QColor::blue() const + + Returns the B (blue) component of the RGB value. +*/ + + +/*! + Returns a lighter (or darker) color, but does not change this + object. + + Returns a lighter color if \a factor is greater than 100. Setting + \a factor to 150 returns a color that is 50% brighter. + + Returns a darker color if \a factor is less than 100. We recommend + using dark() for this purpose. If \a factor is 0 or negative, the + return value is unspecified. + + (This function converts the current RGB color to HSV, multiplies V + by \a factor, and converts the result back to RGB.) + + \sa dark() +*/ + +QColor QColor::light( int factor ) const +{ + if ( factor <= 0 ) // invalid lightness factor + return *this; + else if ( factor < 100 ) // makes color darker + return dark( 10000/factor ); + + int h, s, v; + hsv( &h, &s, &v ); + v = (factor*v)/100; + if ( v > 255 ) { // overflow + s -= v-255; // adjust saturation + if ( s < 0 ) + s = 0; + v = 255; + } + QColor c; + c.setHsv( h, s, v ); + return c; +} + + +/*! + Returns a darker (or lighter) color, but does not change this + object. + + Returns a darker color if \a factor is greater than 100. Setting + \a factor to 300 returns a color that has one-third the + brightness. + + Returns a lighter color if \a factor is less than 100. We + recommend using lighter() for this purpose. If \a factor is 0 or + negative, the return value is unspecified. + + (This function converts the current RGB color to HSV, divides V by + \a factor and converts back to RGB.) + + \sa light() +*/ + +QColor QColor::dark( int factor ) const +{ + if ( factor <= 0 ) // invalid darkness factor + return *this; + else if ( factor < 100 ) // makes color lighter + return light( 10000/factor ); + int h, s, v; + hsv( &h, &s, &v ); + v = (v*100)/factor; + QColor c; + c.setHsv( h, s, v ); + return c; +} + + +/*! + \fn bool QColor::operator==( const QColor &c ) const + + Returns TRUE if this color has the same RGB value as \a c; + otherwise returns FALSE. +*/ + +/*! + \fn bool QColor::operator!=( const QColor &c ) const + Returns TRUE if this color has a different RGB value from \a c; + otherwise returns FALSE. +*/ + +/*! + Returns the pixel value. + + This value is used by the underlying window system to refer to a + color. It can be thought of as an index into the display + hardware's color table, but the value is an arbitrary 32-bit + value. + + \sa alloc() +*/ + +/*! + \fn QStringList QColor::colorNames() + Returns a QStringList containing the color names Qt knows about. +*/ + +/***************************************************************************** + QColor stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QColor + Writes a color object, \a c to the stream, \a s. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QColor &c ) +{ + Q_UINT32 p = (Q_UINT32)c.rgb(); + if ( s.version() == 1 ) // Swap red and blue + p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00); + return s << p; +} + +/*! + \relates QColor + Reads a color object, \a c, from the stream, \a s. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QColor &c ) +{ + Q_UINT32 p; + s >> p; + if ( s.version() == 1 ) // Swap red and blue + p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00); + c.setRgb( p ); + return s; +} +#endif + +/***************************************************************************** + QColor global functions (documentation only) + *****************************************************************************/ + +/*! + \fn int qRed( QRgb rgb ) + \relates QColor + + Returns the red component of the RGB triplet \a rgb. + \sa qRgb(), QColor::red() +*/ + +/*! + \fn int qGreen( QRgb rgb ) + \relates QColor + + Returns the green component of the RGB triplet \a rgb. + \sa qRgb(), QColor::green() +*/ + +/*! + \fn int qBlue( QRgb rgb ) + \relates QColor + + Returns the blue component of the RGB triplet \a rgb. + \sa qRgb(), QColor::blue() +*/ + +/*! + \fn int qAlpha( QRgb rgba ) + \relates QColor + + Returns the alpha component of the RGBA quadruplet \a rgba. + */ + +/*! + \fn QRgb qRgb( int r, int g, int b ) + \relates QColor + + Returns the RGB triplet \a (r,g,b). + + The return type QRgb is equivalent to \c unsigned \c int. + + \sa qRgba(), qRed(), qGreen(), qBlue() +*/ + +/*! + \fn QRgb qRgba( int r, int g, int b, int a ) + \relates QColor + + Returns the RGBA quadruplet \a (r,g,b,a). + + The return type QRgba is equivalent to \c unsigned \c int. + + \sa qRgb(), qRed(), qGreen(), qBlue() +*/ + +/*! + \fn int qGray( int r, int g, int b ) + \relates QColor + + Returns a gray value 0..255 from the (\a r, \a g, \a b) triplet. + + The gray value is calculated using the formula (r*11 + g*16 + + b*5)/32. +*/ + +/*! + \overload int qGray( qRgb rgb ) + \relates QColor + + Returns a gray value 0..255 from the given \a rgb colour. +*/ + diff --git a/main/lib/image.effect/qcolor.h b/main/lib/image.effect/qcolor.h new file mode 100644 index 00000000..b35630f3 --- /dev/null +++ b/main/lib/image.effect/qcolor.h @@ -0,0 +1,255 @@ +/*************************************************************************** + + qcolor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qcolor.h 3.3.4 edited Nov 24 2003 $ +** +** Definition of QColor class +** +** Created : 940112 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QCOLOR_H +#define QCOLOR_H + +#include "qt.h" + +//#ifndef QT_H +//#include "qwindowdefs.h" +//#include "qstringlist.h" +//#endif // QT_H + +const QRgb RGB_MASK = 0x00ffffff; // masks RGB values + +Q_EXPORT inline int qRed( QRgb rgb ) // get red part of RGB +{ return (int)((rgb >> 16) & 0xff); } + +Q_EXPORT inline int qGreen( QRgb rgb ) // get green part of RGB +{ return (int)((rgb >> 8) & 0xff); } + +Q_EXPORT inline int qBlue( QRgb rgb ) // get blue part of RGB +{ return (int)(rgb & 0xff); } + +Q_EXPORT inline int qAlpha( QRgb rgb ) // get alpha part of RGBA +{ return (int)((rgb >> 24) & 0xff); } + +Q_EXPORT inline QRgb qRgb( int r, int g, int b )// set RGB value +{ return (0xff << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } + +Q_EXPORT inline QRgb qRgba( int r, int g, int b, int a )// set RGBA value +{ return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } + +Q_EXPORT inline int qGray( int r, int g, int b )// convert R,G,B to gray 0..255 +{ return (r*11+g*16+b*5)/32; } + +Q_EXPORT inline int qGray( QRgb rgb ) // convert RGB to gray 0..255 +{ return qGray( qRed(rgb), qGreen(rgb), qBlue(rgb) ); } + + +class Q_EXPORT QColor +{ +public: + enum Spec { Rgb, Hsv }; + + QColor(); + QColor( int r, int g, int b ); + QColor( int x, int y, int z, Spec ); + QColor( QRgb rgb, uint pixel=0xffffffff); + //QColor( const QString& name ); + QColor( const char *name ); + QColor( const QColor & ); + QColor &operator=( const QColor & ); + + bool isValid() const; + bool isDirty() const; + //QString name() const; + //void setNamedColor( const QString& name ); + + QRgb rgb() const; + void setRgb( int r, int g, int b ); + void setRgb( QRgb rgb ); + void getRgb( int *r, int *g, int *b) const { rgb( r, g, b); } + void rgb( int *r, int *g, int *b) const; // obsolete + + int red() const; + int green() const; + int blue() const; + int alpha() const; + + void setHsv( int h, int s, int v ); + void getHsv( int *h, int *s, int *v ) const { hsv( h, s, v ); } + void hsv( int *h, int *s, int *v ) const; // obsolete + void getHsv( int &h, int &s, int &v ) const { hsv( &h, &s, &v ); } // obsolete + + QColor light( int f = 150 ) const; + QColor dark( int f = 200 ) const; + + bool operator==( const QColor &c ) const; + bool operator!=( const QColor &c ) const; + + //uint alloc(); + //uint pixel() const; + +#if defined(Q_WS_X11) + // ### in 4.0, make this take a default argument of -1 for default screen? + uint alloc( int screen ); + uint pixel( int screen ) const; +#endif + + //static int maxColors(); + //static int numBitPlanes(); + + //static int enterAllocContext(); + //static void leaveAllocContext(); + //static int currentAllocContext(); + //static void destroyAllocContext( int ); + +#if defined(Q_WS_WIN) + static const QRgb* palette( int* numEntries = 0 ); + static int setPaletteEntries( const QRgb* entries, int numEntries, + int base = -1 ); + static HPALETTE hPal() { return hpal; } + static uint realizePal( QWidget * ); +#endif + + static void initialize(); + static void cleanup(); +#ifndef QT_NO_STRINGLIST + static QStringList colorNames(); +#endif + enum { Dirt = 0x44495254, Invalid = 0x49000000 }; + +private: + //void setSystemNamedColor( const QString& name ); + void setPixel( uint pixel ); + //static void initGlobalColors(); + static uint argbToPix32(QRgb); + //static QColor* globalColors(); + static bool color_init; + static bool globals_init; +#if defined(Q_WS_WIN) + static HPALETTE hpal; +#endif + static enum ColorModel { d8, d32 } colormodel; + union { + QRgb argb; + struct D8 { + QRgb argb; + uchar pix; + uchar invalid; + uchar dirty; + uchar direct; + } d8; + struct D32 { + QRgb argb; + uint pix; + bool invalid() const { return argb == QColor::Invalid && pix == QColor::Dirt; } + bool probablyDirty() const { return pix == QColor::Dirt; } + } d32; + } d; +}; + + +inline QColor::QColor() +{ d.d32.argb = Invalid; d.d32.pix = Dirt; } + +inline QColor::QColor( int r, int g, int b ) +{ + d.d32.argb = Invalid; + d.d32.pix = Dirt; + setRgb( r, g, b ); +} + +inline QRgb QColor::rgb() const +{ return d.argb; } + +inline int QColor::red() const +{ return qRed(d.argb); } + +inline int QColor::green() const +{ return qGreen(d.argb); } + +inline int QColor::blue() const +{ return qBlue(d.argb); } + +inline int QColor::alpha() const +{ return qAlpha(d.argb); } + +inline bool QColor::isValid() const +{ + if ( colormodel == d8 ) + return !d.d8.invalid; + else + return !d.d32.invalid(); +} + +inline bool QColor::operator==( const QColor &c ) const +{ + return d.argb == c.d.argb && isValid() == c.isValid(); +} + +inline bool QColor::operator!=( const QColor &c ) const +{ + return !operator==(c); +} + + +/***************************************************************************** + QColor stream functions + *****************************************************************************/ + +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QColor & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QColor & ); +#endif + +#endif // QCOLOR_H diff --git a/main/lib/image.effect/qimage.cpp b/main/lib/image.effect/qimage.cpp new file mode 100644 index 00000000..748bdb7d --- /dev/null +++ b/main/lib/image.effect/qimage.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + + qimage.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "qimage.h" + +void QImage::init() +{ + img = NULL; + bpl = 0; + jt = NULL; + inv = FALSE; +} + +void QImage::check() +{ + if (!img) + return; + if (GB_IMAGE_FMT_IS_SWAPPED(img->format)) + fprintf(stderr, "gb.image.effect: warning: unsupported image format: %s\n", IMAGE.FormatToString(img->format)); + inv = GB_IMAGE_FMT_IS_RGBA(img->format); +} + +void QImage::create(int w, int h, bool t) +{ + img = IMAGE.Create(w, h, t ? GB_IMAGE_BGRA : GB_IMAGE_BGRX, NULL); + //GB.Ref(img); + check(); + jumpTable(); +} + +QImage::QImage() +{ + init(); +} + +QImage::QImage(const QSize &size, bool t) +{ + init(); + create(size.width(), size.height(), t); +} + +QImage::QImage(int w, int h, bool t) +{ + init(); + create(w, h, t); +} + +QImage::QImage(GB_IMAGE image) +{ + init(); + img = (GB_IMG *)image; + //GB.Ref(img); + SYNCHRONIZE_IMAGE(img); + check(); + jumpTable(); +} + +QImage::QImage(const QImage ©) +{ + init(); + img = (GB_IMG *)copy.object(); + //GB.Ref(img); + SYNCHRONIZE_IMAGE(img); + check(); +} + +QImage::~QImage() +{ + if (jt) + { + free(jt); + jt = NULL; + } +} + +void QImage::release() +{ + GB.Unref(POINTER(&img)); + img = NULL; +} + +/*static inline uint invert(uint col) +{ + return (col >> 24) | (col << 24) | ((col & 0x00FF0000) >> 8) | ((col & 0x0000FF00) << 8); +}*/ + +void QImage::invert() +{ + uint i, n; + uchar *p, t; + + n = width() * height(); + p = (uchar *)bits(); + + for (i = 0; i < n; i++, p += 4) + { + t = p[0]; + p[0] = p[2]; + p[2] = t; + } +} + + +uchar **QImage::jumpTable() +{ + if (!jt && img->data) + { + int bpl = 4 * width(); + jt = (uchar**)malloc(height() * sizeof(uchar*)); + for (int i = 0; i < height(); i++) + jt[i] = (uchar *)img->data + i * bpl; + } + + return jt; +} + +void QImage::invertPixels() +{ + uint i, n; + uint *p; + + n = width() * height(); + p = (uint *)bits(); + + for (i = 0; i < n; i++) + p[i] ^= 0xFFFFFF; +} diff --git a/main/lib/image.effect/qimage.h b/main/lib/image.effect/qimage.h new file mode 100644 index 00000000..1e9bda73 --- /dev/null +++ b/main/lib/image.effect/qimage.h @@ -0,0 +1,493 @@ +/*************************************************************************** + + qimage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qimage.h 3.3.4 edited May 27 2003 $ +** +** Definition of QImage and QImageIO classes +** +** Created : 950207 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QIMAGE_H +#define QIMAGE_H + +#include "qsize.h" +#include "main.h" + +class QImage +{ +public: + + QImage(); + QImage(int w, int h, bool t); + QImage(const QSize &size, bool t); + QImage(GB_IMAGE img); + ~QImage(); + QImage(const QImage ©); + void reset() { }; + void release(); + void create(int w, int h, bool t); + GB_IMAGE object() const { return (GB_IMAGE)img; } + int depth() const { return 32; } + int width() const { return img->width; } + int height() const { return img->height; } + int numColors() const { return 0; } + QRgb *colorTable() const { return NULL; } + QRgb pixel(int x, int y) const { return ((uint *)img->data)[y * width() + x]; } + void setPixel(int x, int y, QRgb c) { ((uint *)img->data)[y * width() + x] = c; } + bool inverted() const { return inv; } + void invert(); + void invertPixels(); + bool transparent() { return GB_IMAGE_FMT_IS_TRANSPARENT(img->format); } + + uchar *scanLine( int ) const; + uchar *bits() const { return (uchar *)img->data; } + uchar **jumpTable(); + + +private: + + void init(); + void check(); + + GB_IMG *img; + int bpl; + bool inv; + uchar **jt; +}; + +inline uchar *QImage::scanLine( int i ) const +{ + return jt[i]; +} + + + +#if 0 + +class QImageDataMisc; // internal +#ifndef QT_NO_IMAGE_TEXT +class Q_EXPORT QImageTextKeyLang { +public: + QImageTextKeyLang(const char* k, const char* l) : key(k), lang(l) { } + QImageTextKeyLang() { } + + QCString key; + QCString lang; + + bool operator< (const QImageTextKeyLang& other) const + { return key < other.key || key==other.key && lang < other.lang; } + bool operator== (const QImageTextKeyLang& other) const + { return key==other.key && lang==other.lang; } +}; +#endif //QT_NO_IMAGE_TEXT + + +class Q_EXPORT QImage +{ +public: + enum Endian { IgnoreEndian, BigEndian, LittleEndian }; + + QImage(); + QImage( int width, int height, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); + QImage( const QSize&, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); +#ifndef QT_NO_IMAGEIO + QImage( const QString &fileName, const char* format=0 ); + QImage( const char * const xpm[] ); + QImage( const QByteArray &data ); +#endif + QImage( uchar* data, int w, int h, int depth, + QRgb* colortable, int numColors, + Endian bitOrder ); +#ifdef Q_WS_QWS + QImage( uchar* data, int w, int h, int depth, int pbl, + QRgb* colortable, int numColors, + Endian bitOrder ); +#endif + QImage( const QImage & ); + ~QImage(); + + QImage &operator=( const QImage & ); + QImage &operator=( const QPixmap & ); + bool operator==( const QImage & ) const; + bool operator!=( const QImage & ) const; + void detach(); + QImage copy() const; + QImage copy(int x, int y, int w, int h, int conversion_flags=0) const; + QImage copy(const QRect&) const; +#ifndef QT_NO_MIME + static QImage fromMimeSource( const QString& abs_name ); +#endif + bool isNull() const { return data->bits == 0; } + + int width() const { return data->w; } + int height() const { return data->h; } + QSize size() const { return QSize(data->w,data->h); } + QRect rect() const { return QRect(0,0,data->w,data->h); } + int depth() const { return data->d; } + int numColors() const { return data->ncols; } + Endian bitOrder() const { return (Endian) data->bitordr; } + + QRgb color( int i ) const; + void setColor( int i, QRgb c ); + void setNumColors( int ); + + bool hasAlphaBuffer() const; + void setAlphaBuffer( bool ); + + bool allGray() const; + bool isGrayscale() const; + + uchar *bits() const; + uchar *scanLine( int ) const; + uchar **jumpTable() const; + QRgb *colorTable() const; + int numBytes() const; + int bytesPerLine() const; + +#ifdef Q_WS_QWS + QGfx * graphicsContext(); +#endif + + bool create( int width, int height, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); + bool create( const QSize&, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); + void reset(); + + void fill( uint pixel ); + void invertPixels( bool invertAlpha = TRUE ); + + QImage convertDepth( int ) const; +#ifndef QT_NO_IMAGE_TRUECOLOR + QImage convertDepthWithPalette( int, QRgb* p, int pc, int cf=0 ) const; +#endif + QImage convertDepth( int, int conversion_flags ) const; + QImage convertBitOrder( Endian ) const; + + enum ScaleMode { + ScaleFree, + ScaleMin, + ScaleMax + }; +#ifndef QT_NO_IMAGE_SMOOTHSCALE + QImage smoothScale( int w, int h, ScaleMode mode=ScaleFree ) const; + QImage smoothScale( const QSize& s, ScaleMode mode=ScaleFree ) const; +#endif +#ifndef QT_NO_IMAGE_TRANSFORMATION + QImage scale( int w, int h, ScaleMode mode=ScaleFree ) const; + QImage scale( const QSize& s, ScaleMode mode=ScaleFree ) const; + QImage scaleWidth( int w ) const; + QImage scaleHeight( int h ) const; + QImage xForm( const QWMatrix &matrix ) const; +#endif + +#ifndef QT_NO_IMAGE_DITHER_TO_1 + QImage createAlphaMask( int conversion_flags=0 ) const; +#endif +#ifndef QT_NO_IMAGE_HEURISTIC_MASK + QImage createHeuristicMask( bool clipTight=TRUE ) const; +#endif +#ifndef QT_NO_IMAGE_MIRROR + QImage mirror() const; + QImage mirror(bool horizontally, bool vertically) const; +#endif + QImage swapRGB() const; + + static Endian systemBitOrder(); + static Endian systemByteOrder(); + +#ifndef QT_NO_IMAGEIO + static const char* imageFormat( const QString &fileName ); + static QStrList inputFormats(); + static QStrList outputFormats(); +#ifndef QT_NO_STRINGLIST + static QStringList inputFormatList(); + static QStringList outputFormatList(); +#endif + bool load( const QString &fileName, const char* format=0 ); + bool loadFromData( const uchar *buf, uint len, + const char *format=0 ); + bool loadFromData( QByteArray data, const char* format=0 ); + bool save( const QString &fileName, const char* format, + int quality=-1 ) const; + bool save( QIODevice * device, const char* format, + int quality=-1 ) const; +#endif //QT_NO_IMAGEIO + + bool valid( int x, int y ) const; + int pixelIndex( int x, int y ) const; + QRgb pixel( int x, int y ) const; + void setPixel( int x, int y, uint index_or_rgb ); + + // Auxiliary data + int dotsPerMeterX() const; + int dotsPerMeterY() const; + void setDotsPerMeterX(int); + void setDotsPerMeterY(int); + QPoint offset() const; + void setOffset(const QPoint&); +#ifndef QT_NO_IMAGE_TEXT + QValueList textList() const; + QStringList textLanguages() const; + QStringList textKeys() const; + QString text(const char* key, const char* lang=0) const; + QString text(const QImageTextKeyLang&) const; + void setText(const char* key, const char* lang, const QString&); +#endif +private: + void init(); + void reinit(); + void freeBits(); + static void warningIndexRange( const char *, int ); + + struct QImageData : public QShared { // internal image data + int w; // image width + int h; // image height + int d; // image depth + int ncols; // number of colors + int nbytes; // number of bytes data + int bitordr; // bit order (1 bit depth) + QRgb *ctbl; // color table + uchar **bits; // image data + bool alpha; // alpha buffer + int dpmx; // dots per meter X (or 0) + int dpmy; // dots per meter Y (or 0) + QPoint offset; // offset in pixels +#ifndef QT_NO_IMAGE_TEXT + QImageDataMisc* misc; // less common stuff +#endif + bool ctbl_mine; // this allocated ctbl + } *data; +#ifndef QT_NO_IMAGE_TEXT + QImageDataMisc& misc() const; +#endif +#ifndef QT_NO_IMAGEIO + bool doImageIO( QImageIO* io, int quality ) const; +#endif + friend Q_EXPORT void bitBlt( QImage* dst, int dx, int dy, + const QImage* src, int sx, int sy, + int sw, int sh, int conversion_flags ); +}; + + +// QImage stream functions + +#if !defined(QT_NO_DATASTREAM) && !defined(QT_NO_IMAGEIO) +Q_EXPORT QDataStream &operator<<( QDataStream &, const QImage & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QImage & ); +#endif + +#ifndef QT_NO_IMAGEIO +class QIODevice; +typedef void (*image_io_handler)( QImageIO * ); // image IO handler + + +struct QImageIOData; + + +class Q_EXPORT QImageIO +{ +public: + QImageIO(); + QImageIO( QIODevice *ioDevice, const char *format ); + QImageIO( const QString &fileName, const char* format ); + ~QImageIO(); + + + const QImage &image() const { return im; } + int status() const { return iostat; } + const char *format() const { return frmt; } + QIODevice *ioDevice() const { return iodev; } + QString fileName() const { return fname; } + int quality() const; + QString description() const { return descr; } + const char *parameters() const; + float gamma() const; + + void setImage( const QImage & ); + void setStatus( int ); + void setFormat( const char * ); + void setIODevice( QIODevice * ); + void setFileName( const QString & ); + void setQuality( int ); + void setDescription( const QString & ); + void setParameters( const char * ); + void setGamma( float ); + + bool read(); + bool write(); + + static const char* imageFormat( const QString &fileName ); + static const char *imageFormat( QIODevice * ); + static QStrList inputFormats(); + static QStrList outputFormats(); + + static void defineIOHandler( const char *format, + const char *header, + const char *flags, + image_io_handler read_image, + image_io_handler write_image ); + +private: + void init(); + + QImage im; // image + int iostat; // IO status + QCString frmt; // image format + QIODevice *iodev; // IO device + QString fname; // file name + char *params; // image parameters //### change to QImageIOData *d in 3.0 + QString descr; // image description + QImageIOData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QImageIO( const QImageIO & ); + QImageIO &operator=( const QImageIO & ); +#endif +}; + +#endif //QT_NO_IMAGEIO + +Q_EXPORT void bitBlt( QImage* dst, int dx, int dy, const QImage* src, + int sx=0, int sy=0, int sw=-1, int sh=-1, + int conversion_flags=0 ); + + +/***************************************************************************** + QImage member functions + *****************************************************************************/ + +inline bool QImage::hasAlphaBuffer() const +{ + return data->alpha; +} + +inline uchar *QImage::bits() const +{ + return data->bits ? data->bits[0] : 0; +} + +inline uchar **QImage::jumpTable() const +{ + return data->bits; +} + +inline QRgb *QImage::colorTable() const +{ + return data->ctbl; +} + +inline int QImage::numBytes() const +{ + return data->nbytes; +} + +inline int QImage::bytesPerLine() const +{ + return data->h ? data->nbytes/data->h : 0; +} + +inline QImage QImage::copy(const QRect& r) const +{ + return copy(r.x(), r.y(), r.width(), r.height()); +} + +inline QRgb QImage::color( int i ) const +{ +#if defined(QT_CHECK_RANGE) + if ( i >= data->ncols ) + warningIndexRange( "color", i ); +#endif + return data->ctbl ? data->ctbl[i] : (QRgb)-1; +} + +inline void QImage::setColor( int i, QRgb c ) +{ +#if defined(QT_CHECK_RANGE) + if ( i >= data->ncols ) + warningIndexRange( "setColor", i ); +#endif + if ( data->ctbl ) + data->ctbl[i] = c; +} + +inline uchar *QImage::scanLine( int i ) const +{ +#if defined(QT_CHECK_RANGE) + if ( i >= data->h ) + warningIndexRange( "scanLine", i ); +#endif + return data->bits ? data->bits[i] : 0; +} + +inline int QImage::dotsPerMeterX() const +{ + return data->dpmx; +} + +inline int QImage::dotsPerMeterY() const +{ + return data->dpmy; +} + +inline QPoint QImage::offset() const +{ + return data->offset; +} +#endif + +#endif // QIMAGE_H diff --git a/main/lib/image.effect/qpoint.cpp b/main/lib/image.effect/qpoint.cpp new file mode 100644 index 00000000..fdf096b9 --- /dev/null +++ b/main/lib/image.effect/qpoint.cpp @@ -0,0 +1,463 @@ +/*************************************************************************** + + qpoint.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qpoint.cpp 3.3.4 edited May 27 2003 $ +** +** Implementation of QPoint class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qpoint.h" +//#include "qdatastream.h" + + +/*! + \class QPoint qpoint.h + \brief The QPoint class defines a point in the plane. + + \ingroup images + \ingroup graphics + \mainclass + + A point is specified by an x coordinate and a y coordinate. + + The coordinate type is \c QCOORD (a 32-bit integer). The minimum + value of \c QCOORD is \c QCOORD_MIN (-2147483648) and the maximum + value is \c QCOORD_MAX (2147483647). + + The coordinates are accessed by the functions x() and y(); they + can be set by setX() and setY() or by the reference functions rx() + and ry(). + + Given a point \e p, the following statements are all equivalent: + \code + p.setX( p.x() + 1 ); + p += QPoint( 1, 0 ); + p.rx()++; + \endcode + + A QPoint can also be used as a vector. Addition and subtraction + of QPoints are defined as for vectors (each component is added + separately). You can divide or multiply a QPoint by an \c int or a + \c double. The function manhattanLength() gives an inexpensive + approximation of the length of the QPoint interpreted as a vector. + + Example: + \code + //QPoint oldPos is defined somewhere else + MyWidget::mouseMoveEvent( QMouseEvent *e ) + { + QPoint vector = e->pos() - oldPos; + if ( vector.manhattanLength() > 3 ) + ... //mouse has moved more than 3 pixels since oldPos + } + \endcode + + QPoints can be compared for equality or inequality, and they can + be written to and read from a QStream. + + \sa QPointArray QSize, QRect +*/ + + +/***************************************************************************** + QPoint member functions + *****************************************************************************/ + +/*! + \fn QPoint::QPoint() + + Constructs a point with coordinates (0, 0) (isNull() returns TRUE). +*/ + +/*! + \fn QPoint::QPoint( int xpos, int ypos ) + + Constructs a point with x value \a xpos and y value \a ypos. +*/ + +/*! + \fn bool QPoint::isNull() const + + Returns TRUE if both the x value and the y value are 0; otherwise + returns FALSE. +*/ + +/*! + \fn int QPoint::x() const + + Returns the x coordinate of the point. + + \sa setX() y() +*/ + +/*! + \fn int QPoint::y() const + + Returns the y coordinate of the point. + + \sa setY() x() +*/ + +/*! + \fn void QPoint::setX( int x ) + + Sets the x coordinate of the point to \a x. + + \sa x() setY() +*/ + +/*! + \fn void QPoint::setY( int y ) + + Sets the y coordinate of the point to \a y. + + \sa y() setX() +*/ + + +/*! + \fn QCOORD &QPoint::rx() + + Returns a reference to the x coordinate of the point. + + Using a reference makes it possible to directly manipulate x. + + Example: + \code + QPoint p( 1, 2 ); + p.rx()--; // p becomes (0, 2) + \endcode + + \sa ry() +*/ + +/*! + \fn QCOORD &QPoint::ry() + + Returns a reference to the y coordinate of the point. + + Using a reference makes it possible to directly manipulate y. + + Example: + \code + QPoint p( 1, 2 ); + p.ry()++; // p becomes (1, 3) + \endcode + + \sa rx() +*/ + + +/*! + \fn QPoint &QPoint::operator+=( const QPoint &p ) + + Adds point \a p to this point and returns a reference to this + point. + + Example: + \code + QPoint p( 3, 7 ); + QPoint q( -1, 4 ); + p += q; // p becomes (2,11) + \endcode +*/ + +/*! + \fn QPoint &QPoint::operator-=( const QPoint &p ) + + Subtracts point \a p from this point and returns a reference to + this point. + + Example: + \code + QPoint p( 3, 7 ); + QPoint q( -1, 4 ); + p -= q; // p becomes (4,3) + \endcode +*/ + +/*! + \fn QPoint &QPoint::operator*=( int c ) + + Multiplies this point's x and y by \a c, and returns a reference + to this point. + + Example: + \code + QPoint p( -1, 4 ); + p *= 2; // p becomes (-2,8) + \endcode +*/ + +/*! + \overload QPoint &QPoint::operator*=( double c ) + + Multiplies this point's x and y by \a c, and returns a reference + to this point. + + Example: + \code + QPoint p( -1, 4 ); + p *= 2.5; // p becomes (-3,10) + \endcode + + Note that the result is truncated because points are held as + integers. +*/ + + +/*! + \fn bool operator==( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns TRUE if \a p1 and \a p2 are equal; otherwise returns FALSE. +*/ + +/*! + \fn bool operator!=( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns TRUE if \a p1 and \a p2 are not equal; otherwise returns FALSE. +*/ + +/*! + \fn const QPoint operator+( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns the sum of \a p1 and \a p2; each component is added separately. +*/ + +/*! + \fn const QPoint operator-( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns \a p2 subtracted from \a p1; each component is subtracted + separately. +*/ + +/*! + \fn const QPoint operator*( const QPoint &p, int c ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. +*/ + +/*! + \overload const QPoint operator*( int c, const QPoint &p ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. +*/ + +/*! + \overload const QPoint operator*( const QPoint &p, double c ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. + + Note that the result is truncated because points are held as + integers. +*/ + +/*! + \overload const QPoint operator*( double c, const QPoint &p ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. + + Note that the result is truncated because points are held as + integers. +*/ + +/*! + \overload const QPoint operator-( const QPoint &p ) + + \relates QPoint + + Returns the QPoint formed by changing the sign of both components + of \a p, equivalent to \c{QPoint(0,0) - p}. +*/ + +/*! + \fn QPoint &QPoint::operator/=( int c ) + + Divides both x and y by \a c, and returns a reference to this + point. + + Example: + \code + QPoint p( -2, 8 ); + p /= 2; // p becomes (-1,4) + \endcode +*/ + +/*! + \overload QPoint &QPoint::operator/=( double c ) + + Divides both x and y by \a c, and returns a reference to this + point. + + Example: + \code + QPoint p( -3, 10 ); + p /= 2.5; // p becomes (-1,4) + \endcode + + Note that the result is truncated because points are held as + integers. +*/ + +/*! + \fn const QPoint operator/( const QPoint &p, int c ) + + \relates QPoint + + Returns the QPoint formed by dividing both components of \a p by + \a c. +*/ + +/*! + \overload const QPoint operator/( const QPoint &p, double c ) + + \relates QPoint + + Returns the QPoint formed by dividing both components of \a p + by \a c. + + Note that the result is truncated because points are held as + integers. +*/ + + +void QPoint::warningDivByZero() +{ +#if defined(QT_CHECK_MATH) + qWarning( "QPoint: Division by zero error" ); +#endif +} + + +/***************************************************************************** + QPoint stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QPoint + + Writes point \a p to the stream \a s and returns a reference to + the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QPoint &p ) +{ + if ( s.version() == 1 ) + s << (Q_INT16)p.x() << (Q_INT16)p.y(); + else + s << (Q_INT32)p.x() << (Q_INT32)p.y(); + return s; +} + +/*! + \relates QPoint + + Reads a QPoint from the stream \a s into point \a p and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QPoint &p ) +{ + if ( s.version() == 1 ) { + Q_INT16 x, y; + s >> x; p.rx() = x; + s >> y; p.ry() = y; + } + else { + Q_INT32 x, y; + s >> x; p.rx() = x; + s >> y; p.ry() = y; + } + return s; +} +#endif // QT_NO_DATASTREAM +/*! + Returns the sum of the absolute values of x() and y(), + traditionally known as the "Manhattan length" of the vector from + the origin to the point. The tradition arises because such + distances apply to travelers who can only travel on a rectangular + grid, like the streets of Manhattan. + + This is a useful, and quick to calculate, approximation to the + true length: sqrt(pow(x(),2)+pow(y(),2)). +*/ +int QPoint::manhattanLength() const +{ + return QABS(x())+QABS(y()); +} diff --git a/main/lib/image.effect/qpoint.h b/main/lib/image.effect/qpoint.h new file mode 100644 index 00000000..d07242c8 --- /dev/null +++ b/main/lib/image.effect/qpoint.h @@ -0,0 +1,235 @@ +/*************************************************************************** + + qpoint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qpoint.h 3.3.4 edited May 27 2003 $ +** +** Definition of QPoint class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QPOINT_H +#define QPOINT_H + +#include "qt.h" + +class Q_EXPORT QPoint +{ +public: + QPoint(); + QPoint( int xpos, int ypos ); + + bool isNull() const; + + int x() const; + int y() const; + void setX( int x ); + void setY( int y ); + + int manhattanLength() const; + + QCOORD &rx(); + QCOORD &ry(); + + QPoint &operator+=( const QPoint &p ); + QPoint &operator-=( const QPoint &p ); + QPoint &operator*=( int c ); + QPoint &operator*=( double c ); + QPoint &operator/=( int c ); + QPoint &operator/=( double c ); + + friend inline bool operator==( const QPoint &, const QPoint & ); + friend inline bool operator!=( const QPoint &, const QPoint & ); + friend inline const QPoint operator+( const QPoint &, const QPoint & ); + friend inline const QPoint operator-( const QPoint &, const QPoint & ); + friend inline const QPoint operator*( const QPoint &, int ); + friend inline const QPoint operator*( int, const QPoint & ); + friend inline const QPoint operator*( const QPoint &, double ); + friend inline const QPoint operator*( double, const QPoint & ); + friend inline const QPoint operator-( const QPoint & ); + friend inline const QPoint operator/( const QPoint &, int ); + friend inline const QPoint operator/( const QPoint &, double ); + +private: + static void warningDivByZero(); + +#if defined(Q_OS_MAC) + QCOORD yp; + QCOORD xp; +#else + QCOORD xp; + QCOORD yp; +#endif +}; + + +/***************************************************************************** + QPoint stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QPoint & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QPoint & ); +#endif + +/***************************************************************************** + QPoint inline functions + *****************************************************************************/ + +inline QPoint::QPoint() +{ xp=0; yp=0; } + +inline QPoint::QPoint( int xpos, int ypos ) +{ xp=(QCOORD)xpos; yp=(QCOORD)ypos; } + +inline bool QPoint::isNull() const +{ return xp == 0 && yp == 0; } + +inline int QPoint::x() const +{ return xp; } + +inline int QPoint::y() const +{ return yp; } + +inline void QPoint::setX( int x ) +{ xp = (QCOORD)x; } + +inline void QPoint::setY( int y ) +{ yp = (QCOORD)y; } + +inline QCOORD &QPoint::rx() +{ return xp; } + +inline QCOORD &QPoint::ry() +{ return yp; } + +inline QPoint &QPoint::operator+=( const QPoint &p ) +{ xp+=p.xp; yp+=p.yp; return *this; } + +inline QPoint &QPoint::operator-=( const QPoint &p ) +{ xp-=p.xp; yp-=p.yp; return *this; } + +inline QPoint &QPoint::operator*=( int c ) +{ xp*=(QCOORD)c; yp*=(QCOORD)c; return *this; } + +inline QPoint &QPoint::operator*=( double c ) +{ xp=(QCOORD)(xp*c); yp=(QCOORD)(yp*c); return *this; } + +inline bool operator==( const QPoint &p1, const QPoint &p2 ) +{ return p1.xp == p2.xp && p1.yp == p2.yp; } + +inline bool operator!=( const QPoint &p1, const QPoint &p2 ) +{ return p1.xp != p2.xp || p1.yp != p2.yp; } + +inline const QPoint operator+( const QPoint &p1, const QPoint &p2 ) +{ return QPoint(p1.xp+p2.xp, p1.yp+p2.yp); } + +inline const QPoint operator-( const QPoint &p1, const QPoint &p2 ) +{ return QPoint(p1.xp-p2.xp, p1.yp-p2.yp); } + +inline const QPoint operator*( const QPoint &p, int c ) +{ return QPoint(p.xp*c, p.yp*c); } + +inline const QPoint operator*( int c, const QPoint &p ) +{ return QPoint(p.xp*c, p.yp*c); } + +inline const QPoint operator*( const QPoint &p, double c ) +{ return QPoint((QCOORD)(p.xp*c), (QCOORD)(p.yp*c)); } + +inline const QPoint operator*( double c, const QPoint &p ) +{ return QPoint((QCOORD)(p.xp*c), (QCOORD)(p.yp*c)); } + +inline const QPoint operator-( const QPoint &p ) +{ return QPoint(-p.xp, -p.yp); } + +inline QPoint &QPoint::operator/=( int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + warningDivByZero(); +#endif + xp/=(QCOORD)c; + yp/=(QCOORD)c; + return *this; +} + +inline QPoint &QPoint::operator/=( double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + warningDivByZero(); +#endif + xp=(QCOORD)(xp/c); + yp=(QCOORD)(yp/c); + return *this; +} + +inline const QPoint operator/( const QPoint &p, int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + QPoint::warningDivByZero(); +#endif + return QPoint(p.xp/c, p.yp/c); +} + +inline const QPoint operator/( const QPoint &p, double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + QPoint::warningDivByZero(); +#endif + return QPoint((QCOORD)(p.xp/c), (QCOORD)(p.yp/c)); +} + +#define Q_DEFINED_QPOINT +#endif // QPOINT_H diff --git a/main/lib/image.effect/qrect.cpp b/main/lib/image.effect/qrect.cpp new file mode 100644 index 00000000..6304155c --- /dev/null +++ b/main/lib/image.effect/qrect.cpp @@ -0,0 +1,981 @@ +/*************************************************************************** + + qrect.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qrect.cpp 3.3.4 edited May 27 2003 $ +** +** Implementation of QRect class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#define QRECT_C + +#include "qrect.h" +//#include "qdatastream.h" + +/*! + \class QRect + \brief The QRect class defines a rectangle in the plane. + + \ingroup images + \ingroup graphics + \mainclass + + A rectangle is internally represented as an upper-left corner and + a bottom-right corner, but it is normally expressed as an + upper-left corner and a size. + + The coordinate type is QCOORD (defined in \c qwindowdefs.h as \c + int). The minimum value of QCOORD is QCOORD_MIN (-2147483648) and + the maximum value is QCOORD_MAX (2147483647). + + Note that the size (width and height) of a rectangle might be + different from what you are used to. If the top-left corner and + the bottom-right corner are the same, the height and the width of + the rectangle will both be 1. + + Generally, \e{width = right - left + 1} and \e{height = bottom - + top + 1}. We designed it this way to make it correspond to + rectangular spaces used by drawing functions in which the width + and height denote a number of pixels. For example, drawing a + rectangle with width and height 1 draws a single pixel. + + The default coordinate system has origin (0, 0) in the top-left + corner. The positive direction of the y axis is down, and the + positive x axis is from left to right. + + A QRect can be constructed with a set of left, top, width and + height integers, from two QPoints or from a QPoint and a QSize. + After creation the dimensions can be changed, e.g. with setLeft(), + setRight(), setTop() and setBottom(), or by setting sizes, e.g. + setWidth(), setHeight() and setSize(). The dimensions can also be + changed with the move functions, e.g. moveBy(), moveCenter(), + moveBottomRight(), etc. You can also add coordinates to a + rectangle with addCoords(). + + You can test to see if a QRect contains a specific point with + contains(). You can also test to see if two QRects intersect with + intersects() (see also intersect()). To get the bounding rectangle + of two QRects use unite(). + + \sa QPoint, QSize +*/ + + +/***************************************************************************** + QRect member functions + *****************************************************************************/ + +/*! + \fn QRect::QRect() + + Constructs an invalid rectangle. +*/ + +/*! + Constructs a rectangle with \a topLeft as the top-left corner and + \a bottomRight as the bottom-right corner. +*/ + +QRect::QRect( const QPoint &topLeft, const QPoint &bottomRight ) +{ + x1 = (QCOORD)topLeft.x(); + y1 = (QCOORD)topLeft.y(); + x2 = (QCOORD)bottomRight.x(); + y2 = (QCOORD)bottomRight.y(); +} + +/*! + Constructs a rectangle with \a topLeft as the top-left corner and + \a size as the rectangle size. +*/ + +QRect::QRect( const QPoint &topLeft, const QSize &size ) +{ + x1 = (QCOORD)topLeft.x(); + y1 = (QCOORD)topLeft.y(); + x2 = (QCOORD)(x1+size.width()-1); + y2 = (QCOORD)(y1+size.height()-1); +} + +/*! + \fn QRect::QRect( int left, int top, int width, int height ) + + Constructs a rectangle with the \a top, \a left corner and \a + width and \a height. + + Example (creates three identical rectangles): + \code + QRect r1( QPoint(100,200), QPoint(110,215) ); + QRect r2( QPoint(100,200), QSize(11,16) ); + QRect r3( 100, 200, 11, 16 ); + \endcode +*/ + + +/*! + \fn bool QRect::isNull() const + + Returns TRUE if the rectangle is a null rectangle; otherwise + returns FALSE. + + A null rectangle has both the width and the height set to 0, that + is right() == left() - 1 and bottom() == top() - 1. + + Note that if right() == left() and bottom() == top(), then the + rectangle has width 1 and height 1. + + A null rectangle is also empty. + + A null rectangle is not valid. + + \sa isEmpty(), isValid() +*/ + +/*! + \fn bool QRect::isEmpty() const + + Returns TRUE if the rectangle is empty; otherwise returns FALSE. + + An empty rectangle has a left() \> right() or top() \> bottom(). + + An empty rectangle is not valid. \c{isEmpty() == !isValid()} + + \sa isNull(), isValid(), normalize() +*/ + +/*! + \fn bool QRect::isValid() const + + Returns TRUE if the rectangle is valid; otherwise returns FALSE. + + A valid rectangle has a left() \<= right() and top() \<= bottom(). + + Note that non-trivial operations like intersections are not defined + for invalid rectangles. + + \c{isValid() == !isEmpty()} + + \sa isNull(), isEmpty(), normalize() +*/ + + +/*! + Returns a normalized rectangle, i.e. a rectangle that has a + non-negative width and height. + + It swaps left and right if left() \> right(), and swaps top and + bottom if top() \> bottom(). + + \sa isValid() +*/ + +QRect QRect::normalize() const +{ + QRect r; + if ( x2 < x1 ) { // swap bad x values + r.x1 = x2; + r.x2 = x1; + } else { + r.x1 = x1; + r.x2 = x2; + } + if ( y2 < y1 ) { // swap bad y values + r.y1 = y2; + r.y2 = y1; + } else { + r.y1 = y1; + r.y2 = y2; + } + return r; +} + + +/*! + \fn int QRect::left() const + + Returns the left coordinate of the rectangle. Identical to x(). + + \sa setLeft(), right(), topLeft(), bottomLeft() +*/ + +/*! + \fn int QRect::top() const + + Returns the top coordinate of the rectangle. Identical to y(). + + \sa setTop(), bottom(), topLeft(), topRight() +*/ + +/*! + \fn int QRect::right() const + + Returns the right coordinate of the rectangle. + + \sa setRight(), left(), topRight(), bottomRight() +*/ + +/*! + \fn int QRect::bottom() const + + Returns the bottom coordinate of the rectangle. + + \sa setBottom(), top(), bottomLeft(), bottomRight() +*/ + +/*! + \fn QCOORD &QRect::rLeft() + + Returns a reference to the left coordinate of the rectangle. + + \sa rTop(), rRight(), rBottom() +*/ + +/*! + \fn QCOORD &QRect::rTop() + + Returns a reference to the top coordinate of the rectangle. + + \sa rLeft(), rRight(), rBottom() +*/ + +/*! + \fn QCOORD &QRect::rRight() + + Returns a reference to the right coordinate of the rectangle. + + \sa rLeft(), rTop(), rBottom() +*/ + +/*! + \fn QCOORD &QRect::rBottom() + + Returns a reference to the bottom coordinate of the rectangle. + + \sa rLeft(), rTop(), rRight() +*/ + +/*! + \fn int QRect::x() const + + Returns the left coordinate of the rectangle. Identical to left(). + + \sa left(), y(), setX() +*/ + +/*! + \fn int QRect::y() const + + Returns the top coordinate of the rectangle. Identical to top(). + + \sa top(), x(), setY() +*/ + +/*! + \fn void QRect::setLeft( int pos ) + + Sets the left edge of the rectangle to \a pos. May change the + width, but will never change the right edge of the rectangle. + + Identical to setX(). + + \sa left(), setTop(), setWidth() +*/ + +/*! + \fn void QRect::setTop( int pos ) + + Sets the top edge of the rectangle to \a pos. May change the + height, but will never change the bottom edge of the rectangle. + + Identical to setY(). + + \sa top(), setBottom(), setHeight() +*/ + +/*! + \fn void QRect::setRight( int pos ) + + Sets the right edge of the rectangle to \a pos. May change the + width, but will never change the left edge of the rectangle. + + \sa right(), setLeft(), setWidth() +*/ + +/*! + \fn void QRect::setBottom( int pos ) + + Sets the bottom edge of the rectangle to \a pos. May change the + height, but will never change the top edge of the rectangle. + + \sa bottom(), setTop(), setHeight() +*/ + +/*! + \fn void QRect::setX( int x ) + + Sets the x position of the rectangle (its left end) to \a x. May + change the width, but will never change the right edge of the + rectangle. + + Identical to setLeft(). + + \sa x(), setY() +*/ + +/*! + \fn void QRect::setY( int y ) + + Sets the y position of the rectangle (its top) to \a y. May change + the height, but will never change the bottom edge of the + rectangle. + + Identical to setTop(). + + \sa y(), setX() +*/ + +/*! + Set the top-left corner of the rectangle to \a p. May change + the size, but will the never change the bottom-right corner of + the rectangle. + + \sa topLeft(), moveTopLeft(), setBottomRight(), setTopRight(), setBottomLeft() +*/ +void QRect::setTopLeft( const QPoint &p ) +{ + setLeft( p.x() ); + setTop( p.y() ); +} + +/*! + Set the bottom-right corner of the rectangle to \a p. May change + the size, but will the never change the top-left corner of + the rectangle. + + \sa bottomRight(), moveBottomRight(), setTopLeft(), setTopRight(), setBottomLeft() +*/ +void QRect::setBottomRight( const QPoint &p ) +{ + setRight( p.x() ); + setBottom( p.y() ); +} + +/*! + Set the top-right corner of the rectangle to \a p. May change + the size, but will the never change the bottom-left corner of + the rectangle. + + \sa topRight(), moveTopRight(), setTopLeft(), setBottomRight(), setBottomLeft() +*/ +void QRect::setTopRight( const QPoint &p ) +{ + setRight( p.x() ); + setTop( p.y() ); +} + +/*! + Set the bottom-left corner of the rectangle to \a p. May change + the size, but will the never change the top-right corner of + the rectangle. + + \sa bottomLeft(), moveBottomLeft(), setTopLeft(), setBottomRight(), setTopRight() +*/ +void QRect::setBottomLeft( const QPoint &p ) +{ + setLeft( p.x() ); + setBottom( p.y() ); +} + +/*! + \fn QPoint QRect::topLeft() const + + Returns the top-left position of the rectangle. + + \sa setTopLeft(), moveTopLeft(), bottomRight(), left(), top() +*/ + +/*! + \fn QPoint QRect::bottomRight() const + + Returns the bottom-right position of the rectangle. + + \sa setBottomRight(), moveBottomRight(), topLeft(), right(), bottom() +*/ + +/*! + \fn QPoint QRect::topRight() const + + Returns the top-right position of the rectangle. + + \sa setTopRight(), moveTopRight(), bottomLeft(), top(), right() +*/ + +/*! + \fn QPoint QRect::bottomLeft() const + + Returns the bottom-left position of the rectangle. + + \sa setBottomLeft(), moveBottomLeft(), topRight(), bottom(), left() +*/ + +/*! + \fn QPoint QRect::center() const + + Returns the center point of the rectangle. + + \sa moveCenter(), topLeft(), bottomRight(), topRight(), bottomLeft() +*/ + + +/*! + Extracts the rectangle parameters as the position \a *x, \a *y and + width \a *w and height \a *h. + + \sa setRect(), coords() +*/ + +void QRect::rect( int *x, int *y, int *w, int *h ) const +{ + *x = x1; + *y = y1; + *w = x2-x1+1; + *h = y2-y1+1; +} + +/*! + Extracts the rectangle parameters as the top-left point \a *xp1, + \a *yp1 and the bottom-right point \a *xp2, \a *yp2. + + \sa setCoords(), rect() +*/ + +void QRect::coords( int *xp1, int *yp1, int *xp2, int *yp2 ) const +{ + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + + +/*! + Sets the left position of the rectangle to \a pos, leaving the + size unchanged. + + \sa left(), setLeft(), moveTop(), moveRight(), moveBottom() +*/ +void QRect::moveLeft( int pos ) +{ + x2 += (QCOORD)(pos - x1); + x1 = (QCOORD)pos; +} + +/*! + Sets the top position of the rectangle to \a pos, leaving the + size unchanged. + + \sa top(), setTop(), moveLeft(), moveRight(), moveBottom() +*/ + +void QRect::moveTop( int pos ) +{ + y2 += (QCOORD)(pos - y1); + y1 = (QCOORD)pos; +} + +/*! + Sets the right position of the rectangle to \a pos, leaving the + size unchanged. + + \sa right(), setRight(), moveLeft(), moveTop(), moveBottom() +*/ + +void QRect::moveRight( int pos ) +{ + x1 += (QCOORD)(pos - x2); + x2 = (QCOORD)pos; +} + +/*! + Sets the bottom position of the rectangle to \a pos, leaving the + size unchanged. + + \sa bottom(), setBottom(), moveLeft(), moveTop(), moveRight() +*/ + +void QRect::moveBottom( int pos ) +{ + y1 += (QCOORD)(pos - y2); + y2 = (QCOORD)pos; +} + +/*! + Sets the top-left position of the rectangle to \a p, leaving the + size unchanged. + + \sa topLeft(), setTopLeft(), moveBottomRight(), moveTopRight(), moveBottomLeft() +*/ + +void QRect::moveTopLeft( const QPoint &p ) +{ + moveLeft( p.x() ); + moveTop( p.y() ); +} + +/*! + Sets the bottom-right position of the rectangle to \a p, leaving + the size unchanged. + + \sa bottomRight(), setBottomRight(), moveTopLeft(), moveTopRight(), moveBottomLeft() +*/ + +void QRect::moveBottomRight( const QPoint &p ) +{ + moveRight( p.x() ); + moveBottom( p.y() ); +} + +/*! + Sets the top-right position of the rectangle to \a p, leaving the + size unchanged. + + \sa topRight(), setTopRight(), moveTopLeft(), moveBottomRight(), moveBottomLeft() +*/ + +void QRect::moveTopRight( const QPoint &p ) +{ + moveRight( p.x() ); + moveTop( p.y() ); +} + +/*! + Sets the bottom-left position of the rectangle to \a p, leaving + the size unchanged. + + \sa bottomLeft(), setBottomLeft(), moveTopLeft(), moveBottomRight(), moveTopRight() +*/ + +void QRect::moveBottomLeft( const QPoint &p ) +{ + moveLeft( p.x() ); + moveBottom( p.y() ); +} + + +/*! + Sets the center point of the rectangle to \a p, leaving the size + unchanged. + + \sa center(), moveTopLeft(), moveBottomRight(), moveTopRight(), moveBottomLeft() +*/ + +void QRect::moveCenter( const QPoint &p ) +{ + QCOORD w = x2 - x1; + QCOORD h = y2 - y1; + x1 = (QCOORD)(p.x() - w/2); + y1 = (QCOORD)(p.y() - h/2); + x2 = x1 + w; + y2 = y1 + h; +} + + +/*! + Moves the rectangle \a dx along the x axis and \a dy along the y + axis, relative to the current position. Positive values move the + rectangle to the right and down. + + \sa moveTopLeft() +*/ + +void QRect::moveBy( int dx, int dy ) +{ + x1 += (QCOORD)dx; + y1 += (QCOORD)dy; + x2 += (QCOORD)dx; + y2 += (QCOORD)dy; +} + +/*! + Sets the coordinates of the rectangle's top-left corner to \a (x, + y), and its size to \a (w, h). + + \sa rect(), setCoords() +*/ + +void QRect::setRect( int x, int y, int w, int h ) +{ + x1 = (QCOORD)x; + y1 = (QCOORD)y; + x2 = (QCOORD)(x+w-1); + y2 = (QCOORD)(y+h-1); +} + +/*! + Sets the coordinates of the rectangle's top-left corner to \a + (xp1, yp1), and the coordinates of its bottom-right corner to \a + (xp2, yp2). + + \sa coords(), setRect() +*/ + +void QRect::setCoords( int xp1, int yp1, int xp2, int yp2 ) +{ + x1 = (QCOORD)xp1; + y1 = (QCOORD)yp1; + x2 = (QCOORD)xp2; + y2 = (QCOORD)yp2; +} + +/*! + Adds \a xp1, \a yp1, \a xp2 and \a yp2 respectively to the + existing coordinates of the rectangle. +*/ + +void QRect::addCoords( int xp1, int yp1, int xp2, int yp2 ) +{ + x1 += (QCOORD)xp1; + y1 += (QCOORD)yp1; + x2 += (QCOORD)xp2; + y2 += (QCOORD)yp2; +} + +/*! + \fn QSize QRect::size() const + + Returns the size of the rectangle. + + \sa width(), height() +*/ + +/*! + \fn int QRect::width() const + + Returns the width of the rectangle. The width includes both the + left and right edges, i.e. width = right - left + 1. + + \sa height(), size(), setHeight() +*/ + +/*! + \fn int QRect::height() const + + Returns the height of the rectangle. The height includes both the + top and bottom edges, i.e. height = bottom - top + 1. + + \sa width(), size(), setHeight() +*/ + +/*! + Sets the width of the rectangle to \a w. The right edge is + changed, but not the left edge. + + \sa width(), setLeft(), setRight(), setSize() +*/ + +void QRect::setWidth( int w ) +{ + x2 = (QCOORD)(x1 + w - 1); +} + +/*! + Sets the height of the rectangle to \a h. The top edge is not + moved, but the bottom edge may be moved. + + \sa height(), setTop(), setBottom(), setSize() +*/ + +void QRect::setHeight( int h ) +{ + y2 = (QCOORD)(y1 + h - 1); +} + +/*! + Sets the size of the rectangle to \a s. The top-left corner is not + moved. + + \sa size(), setWidth(), setHeight() +*/ + +void QRect::setSize( const QSize &s ) +{ + x2 = (QCOORD)(s.width() +x1-1); + y2 = (QCOORD)(s.height()+y1-1); +} + +/*! + Returns TRUE if the point \a p is inside or on the edge of the + rectangle; otherwise returns FALSE. + + If \a proper is TRUE, this function returns TRUE only if \a p is + inside (not on the edge). +*/ + +bool QRect::contains( const QPoint &p, bool proper ) const +{ + if ( proper ) + return p.x() > x1 && p.x() < x2 && + p.y() > y1 && p.y() < y2; + else + return p.x() >= x1 && p.x() <= x2 && + p.y() >= y1 && p.y() <= y2; +} + +/*! + \overload bool QRect::contains( int x, int y, bool proper ) const + + Returns TRUE if the point \a x, \a y is inside this rectangle; + otherwise returns FALSE. + + If \a proper is TRUE, this function returns TRUE only if the point + is entirely inside (not on the edge). +*/ + +/*! + \overload bool QRect::contains( int x, int y ) const + + Returns TRUE if the point \a x, \a y is inside this rectangle; + otherwise returns FALSE. +*/ + +/*! + \overload + + Returns TRUE if the rectangle \a r is inside this rectangle; + otherwise returns FALSE. + + If \a proper is TRUE, this function returns TRUE only if \a r is + entirely inside (not on the edge). + + \sa unite(), intersect(), intersects() +*/ + +bool QRect::contains( const QRect &r, bool proper ) const +{ + if ( proper ) + return r.x1 > x1 && r.x2 < x2 && r.y1 > y1 && r.y2 < y2; + else + return r.x1 >= x1 && r.x2 <= x2 && r.y1 >= y1 && r.y2 <= y2; +} + +/*! + Unites this rectangle with rectangle \a r. +*/ +QRect& QRect::operator|=(const QRect &r) +{ + *this = *this | r; + return *this; +} + +/*! + Intersects this rectangle with rectangle \a r. +*/ +QRect& QRect::operator&=(const QRect &r) +{ + *this = *this & r; + return *this; +} + + +/*! + Returns the bounding rectangle of this rectangle and rectangle \a + r. + + The bounding rectangle of a nonempty rectangle and an empty or + invalid rectangle is defined to be the nonempty rectangle. + + \sa operator|=(), operator&(), intersects(), contains() +*/ + +QRect QRect::operator|(const QRect &r) const +{ + if ( isValid() ) { + if ( r.isValid() ) { + QRect tmp; + tmp.setLeft( QMIN( x1, r.x1 ) ); + tmp.setRight( QMAX( x2, r.x2 ) ); + tmp.setTop( QMIN( y1, r.y1 ) ); + tmp.setBottom( QMAX( y2, r.y2 ) ); + return tmp; + } else { + return *this; + } + } else { + return r; + } +} + +/*! + Returns the bounding rectangle of this rectangle and rectangle \a + r. \c{r.unite(s)} is equivalent to \c{r|s}. +*/ +QRect QRect::unite( const QRect &r ) const +{ + return *this | r; +} + + +/*! + Returns the intersection of this rectangle and rectangle \a r. + + Returns an empty rectangle if there is no intersection. + + \sa operator&=(), operator|(), isEmpty(), intersects(), contains() +*/ + +QRect QRect::operator&( const QRect &r ) const +{ + QRect tmp; + tmp.x1 = QMAX( x1, r.x1 ); + tmp.x2 = QMIN( x2, r.x2 ); + tmp.y1 = QMAX( y1, r.y1 ); + tmp.y2 = QMIN( y2, r.y2 ); + return tmp; +} + +/*! + Returns the intersection of this rectangle and rectangle \a r. + \c{r.intersect(s)} is equivalent to \c{r&s}. +*/ +QRect QRect::intersect( const QRect &r ) const +{ + return *this & r; +} + +/*! + Returns TRUE if this rectangle intersects with rectangle \a r + (there is at least one pixel that is within both rectangles); + otherwise returns FALSE. + + \sa intersect(), contains() +*/ + +bool QRect::intersects( const QRect &r ) const +{ + return ( QMAX( x1, r.x1 ) <= QMIN( x2, r.x2 ) && + QMAX( y1, r.y1 ) <= QMIN( y2, r.y2 ) ); +} + + +/*! + \relates QRect + + Returns TRUE if \a r1 and \a r2 are equal; otherwise returns FALSE. +*/ + +bool operator==( const QRect &r1, const QRect &r2 ) +{ + return r1.x1==r2.x1 && r1.x2==r2.x2 && r1.y1==r2.y1 && r1.y2==r2.y2; +} + +/*! + \relates QRect + + Returns TRUE if \a r1 and \a r2 are different; otherwise returns FALSE. +*/ + +bool operator!=( const QRect &r1, const QRect &r2 ) +{ + return r1.x1!=r2.x1 || r1.x2!=r2.x2 || r1.y1!=r2.y1 || r1.y2!=r2.y2; +} + + +/***************************************************************************** + QRect stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QRect + + Writes the QRect, \a r, to the stream \a s, and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QRect &r ) +{ + if ( s.version() == 1 ) + s << (Q_INT16)r.left() << (Q_INT16)r.top() + << (Q_INT16)r.right() << (Q_INT16)r.bottom(); + else + s << (Q_INT32)r.left() << (Q_INT32)r.top() + << (Q_INT32)r.right() << (Q_INT32)r.bottom(); + return s; +} + +/*! + \relates QRect + + Reads a QRect from the stream \a s into rect \a r and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QRect &r ) +{ + if ( s.version() == 1 ) { + Q_INT16 x1, y1, x2, y2; + s >> x1; s >> y1; s >> x2; s >> y2; + r.setCoords( x1, y1, x2, y2 ); + } + else { + Q_INT32 x1, y1, x2, y2; + s >> x1; s >> y1; s >> x2; s >> y2; + r.setCoords( x1, y1, x2, y2 ); + } + return s; +} +#endif // QT_NO_DATASTREAM diff --git a/main/lib/image.effect/qrect.h b/main/lib/image.effect/qrect.h new file mode 100644 index 00000000..f663d2a6 --- /dev/null +++ b/main/lib/image.effect/qrect.h @@ -0,0 +1,294 @@ +/*************************************************************************** + + qrect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qrect.h 3.3.4 edited May 27 2003 $ +** +** Definition of QRect class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QRECT_H +#define QRECT_H + +#include "qt.h" +#include "qsize.h" + +#if defined(topLeft) +#error "Macro definition of topLeft conflicts with QRect" +// don't just silently undo people's defines: #undef topLeft +#endif + +class Q_EXPORT QRect // rectangle class +{ +public: + QRect() { x1 = y1 = 0; x2 = y2 = -1; } + QRect( const QPoint &topleft, const QPoint &bottomright ); + QRect( const QPoint &topleft, const QSize &size ); + QRect( int left, int top, int width, int height ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + QRect normalize() const; + + int left() const; + int top() const; + int right() const; + int bottom() const; + + QCOORD &rLeft(); + QCOORD &rTop(); + QCOORD &rRight(); + QCOORD &rBottom(); + + int x() const; + int y() const; + void setLeft( int pos ); + void setTop( int pos ); + void setRight( int pos ); + void setBottom( int pos ); + void setX( int x ); + void setY( int y ); + + void setTopLeft( const QPoint &p ); + void setBottomRight( const QPoint &p ); + void setTopRight( const QPoint &p ); + void setBottomLeft( const QPoint &p ); + + QPoint topLeft() const; + QPoint bottomRight() const; + QPoint topRight() const; + QPoint bottomLeft() const; + QPoint center() const; + + void rect( int *x, int *y, int *w, int *h ) const; + void coords( int *x1, int *y1, int *x2, int *y2 ) const; + + void moveLeft( int pos ); + void moveTop( int pos ); + void moveRight( int pos ); + void moveBottom( int pos ); + void moveTopLeft( const QPoint &p ); + void moveBottomRight( const QPoint &p ); + void moveTopRight( const QPoint &p ); + void moveBottomLeft( const QPoint &p ); + void moveCenter( const QPoint &p ); + void moveBy( int dx, int dy ); + + void setRect( int x, int y, int w, int h ); + void setCoords( int x1, int y1, int x2, int y2 ); + void addCoords( int x1, int y1, int x2, int y2 ); + + QSize size() const; + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void setSize( const QSize &s ); + + QRect operator|(const QRect &r) const; + QRect operator&(const QRect &r) const; + QRect& operator|=(const QRect &r); + QRect& operator&=(const QRect &r); + + bool contains( const QPoint &p, bool proper=FALSE ) const; + bool contains( int x, int y ) const; // inline methods, _don't_ merge these + bool contains( int x, int y, bool proper ) const; + bool contains( const QRect &r, bool proper=FALSE ) const; + QRect unite( const QRect &r ) const; + QRect intersect( const QRect &r ) const; + bool intersects( const QRect &r ) const; + + friend Q_EXPORT bool operator==( const QRect &, const QRect & ); + friend Q_EXPORT bool operator!=( const QRect &, const QRect & ); + +private: +#if defined(Q_WS_X11) || defined(Q_OS_TEMP) + friend void qt_setCoords( QRect *r, int xp1, int yp1, int xp2, int yp2 ); +#endif +#if defined(Q_OS_MAC) + QCOORD y1; + QCOORD x1; + QCOORD y2; + QCOORD x2; +#else + QCOORD x1; + QCOORD y1; + QCOORD x2; + QCOORD y2; +#endif +}; + +Q_EXPORT bool operator==( const QRect &, const QRect & ); +Q_EXPORT bool operator!=( const QRect &, const QRect & ); + + +/***************************************************************************** + QRect stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QRect & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QRect & ); +#endif + +/***************************************************************************** + QRect inline member functions + *****************************************************************************/ + +inline QRect::QRect( int left, int top, int width, int height ) +{ + x1 = (QCOORD)left; + y1 = (QCOORD)top; + x2 = (QCOORD)(left+width-1); + y2 = (QCOORD)(top+height-1); +} + +inline bool QRect::isNull() const +{ return x2 == x1-1 && y2 == y1-1; } + +inline bool QRect::isEmpty() const +{ return x1 > x2 || y1 > y2; } + +inline bool QRect::isValid() const +{ return x1 <= x2 && y1 <= y2; } + +inline int QRect::left() const +{ return x1; } + +inline int QRect::top() const +{ return y1; } + +inline int QRect::right() const +{ return x2; } + +inline int QRect::bottom() const +{ return y2; } + +inline QCOORD &QRect::rLeft() +{ return x1; } + +inline QCOORD & QRect::rTop() +{ return y1; } + +inline QCOORD & QRect::rRight() +{ return x2; } + +inline QCOORD & QRect::rBottom() +{ return y2; } + +inline int QRect::x() const +{ return x1; } + +inline int QRect::y() const +{ return y1; } + +inline void QRect::setLeft( int pos ) +{ x1 = (QCOORD)pos; } + +inline void QRect::setTop( int pos ) +{ y1 = (QCOORD)pos; } + +inline void QRect::setRight( int pos ) +{ x2 = (QCOORD)pos; } + +inline void QRect::setBottom( int pos ) +{ y2 = (QCOORD)pos; } + +inline void QRect::setX( int x ) +{ x1 = (QCOORD)x; } + +inline void QRect::setY( int y ) +{ y1 = (QCOORD)y; } + +inline QPoint QRect::topLeft() const +{ return QPoint(x1, y1); } + +inline QPoint QRect::bottomRight() const +{ return QPoint(x2, y2); } + +inline QPoint QRect::topRight() const +{ return QPoint(x2, y1); } + +inline QPoint QRect::bottomLeft() const +{ return QPoint(x1, y2); } + +inline QPoint QRect::center() const +{ return QPoint((x1+x2)/2, (y1+y2)/2); } + +inline int QRect::width() const +{ return x2 - x1 + 1; } + +inline int QRect::height() const +{ return y2 - y1 + 1; } + +inline QSize QRect::size() const +{ return QSize(x2-x1+1, y2-y1+1); } + +inline bool QRect::contains( int x, int y, bool proper ) const +{ + if ( proper ) + return x > x1 && x < x2 && + y > y1 && y < y2; + else + return x >= x1 && x <= x2 && + y >= y1 && y <= y2; +} + +inline bool QRect::contains( int x, int y ) const +{ + return x >= x1 && x <= x2 && + y >= y1 && y <= y2; +} +#define Q_DEFINED_QRECT +#endif // QRECT_H diff --git a/main/lib/image.effect/qsize.cpp b/main/lib/image.effect/qsize.cpp new file mode 100644 index 00000000..c9eddd6f --- /dev/null +++ b/main/lib/image.effect/qsize.cpp @@ -0,0 +1,452 @@ +/*************************************************************************** + + qsize.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qsize.cpp 3.3.4 edited Oct 23 2003 $ +** +** Implementation of QSize class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qsize.h" +//#include "qdatastream.h" + + +/*! + \class QSize + \brief The QSize class defines the size of a two-dimensional object. + + \ingroup images + \ingroup graphics + + A size is specified by a width and a height. + + The coordinate type is QCOORD (defined in \c as \c int). + The minimum value of QCOORD is QCOORD_MIN (-2147483648) and the maximum + value is QCOORD_MAX (2147483647). + + The size can be set in the constructor and changed with setWidth() + and setHeight(), or using operator+=(), operator-=(), operator*=() + and operator/=(), etc. You can swap the width and height with + transpose(). You can get a size which holds the maximum height and + width of two sizes using expandedTo(), and the minimum height and + width of two sizes using boundedTo(). + + + \sa QPoint, QRect +*/ + + +/***************************************************************************** + QSize member functions + *****************************************************************************/ + +/*! + \fn QSize::QSize() + Constructs a size with invalid (negative) width and height. +*/ + +/*! + \fn QSize::QSize( int w, int h ) + Constructs a size with width \a w and height \a h. +*/ + +/*! + \fn bool QSize::isNull() const + Returns TRUE if the width is 0 and the height is 0; otherwise + returns FALSE. +*/ + +/*! + \fn bool QSize::isEmpty() const + Returns TRUE if the width is less than or equal to 0, or the height is + less than or equal to 0; otherwise returns FALSE. +*/ + +/*! + \fn bool QSize::isValid() const + Returns TRUE if the width is equal to or greater than 0 and the height is + equal to or greater than 0; otherwise returns FALSE. +*/ + +/*! + \fn int QSize::width() const + Returns the width. + \sa height() +*/ + +/*! + \fn int QSize::height() const + Returns the height. + \sa width() +*/ + +/*! + \fn void QSize::setWidth( int w ) + Sets the width to \a w. + \sa width(), setHeight() +*/ + +/*! + \fn void QSize::setHeight( int h ) + Sets the height to \a h. + \sa height(), setWidth() +*/ + +/*! + Swaps the values of width and height. +*/ + +void QSize::transpose() +{ + QCOORD tmp = wd; + wd = ht; + ht = tmp; +} + +/*! \enum QSize::ScaleMode + + This enum type defines the different ways of scaling a size. + + \img scaling.png + + \value ScaleFree The size is scaled freely. The ratio is not preserved. + \value ScaleMin The size is scaled to a rectangle as large as possible + inside a given rectangle, preserving the aspect ratio. + \value ScaleMax The size is scaled to a rectangle as small as possible + outside a given rectangle, preserving the aspect ratio. + + \sa QSize::scale(), QImage::scale(), QImage::smoothScale() +*/ + +/*! + Scales the size to a rectangle of width \a w and height \a h according + to the ScaleMode \a mode. + + \list + \i If \a mode is \c ScaleFree, the size is set to (\a w, \a h). + \i If \a mode is \c ScaleMin, the current size is scaled to a rectangle + as large as possible inside (\a w, \a h), preserving the aspect ratio. + \i If \a mode is \c ScaleMax, the current size is scaled to a rectangle + as small as possible outside (\a w, \a h), preserving the aspect ratio. + \endlist + + Example: + \code + QSize t1( 10, 12 ); + t1.scale( 60, 60, QSize::ScaleFree ); + // t1 is (60, 60) + + QSize t2( 10, 12 ); + t2.scale( 60, 60, QSize::ScaleMin ); + // t2 is (50, 60) + + QSize t3( 10, 12 ); + t3.scale( 60, 60, QSize::ScaleMax ); + // t3 is (60, 72) + \endcode +*/ +void QSize::scale( int w, int h, ScaleMode mode ) +{ + if ( mode == ScaleFree ) { + wd = (QCOORD)w; + ht = (QCOORD)h; + } else { + bool useHeight = TRUE; + int w0 = width(); + int h0 = height(); + int rw = h * w0 / h0; + + if ( mode == ScaleMin ) { + useHeight = ( rw <= w ); + } else { // mode == ScaleMax + useHeight = ( rw >= w ); + } + + if ( useHeight ) { + wd = (QCOORD)rw; + ht = (QCOORD)h; + } else { + wd = (QCOORD)w; + ht = (QCOORD)( w * h0 / w0 ); + } + } +} + +/*! + \overload + + Equivalent to scale(\a{s}.width(), \a{s}.height(), \a mode). +*/ +void QSize::scale( const QSize &s, ScaleMode mode ) +{ + scale( s.width(), s.height(), mode ); +} + +/*! + \fn QCOORD &QSize::rwidth() + Returns a reference to the width. + + Using a reference makes it possible to directly manipulate the width. + + Example: + \code + QSize s( 100, 10 ); + s.rwidth() += 20; // s becomes (120,10) + \endcode + + \sa rheight() +*/ + +/*! + \fn QCOORD &QSize::rheight() + Returns a reference to the height. + + Using a reference makes it possible to directly manipulate the height. + + Example: + \code + QSize s( 100, 10 ); + s.rheight() += 5; // s becomes (100,15) + \endcode + + \sa rwidth() +*/ + +/*! + \fn QSize &QSize::operator+=( const QSize &s ) + + Adds \a s to the size and returns a reference to this size. + + Example: + \code + QSize s( 3, 7 ); + QSize r( -1, 4 ); + s += r; // s becomes (2,11) +\endcode +*/ + +/*! + \fn QSize &QSize::operator-=( const QSize &s ) + + Subtracts \a s from the size and returns a reference to this size. + + Example: + \code + QSize s( 3, 7 ); + QSize r( -1, 4 ); + s -= r; // s becomes (4,3) + \endcode +*/ + +/*! + \fn QSize &QSize::operator*=( int c ) + Multiplies both the width and height by \a c and returns a reference to + the size. +*/ + +/*! + \overload QSize &QSize::operator*=( double c ) + + Multiplies both the width and height by \a c and returns a reference to + the size. + + Note that the result is truncated. +*/ + +/*! + \fn bool operator==( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns TRUE if \a s1 and \a s2 are equal; otherwise returns FALSE. +*/ + +/*! + \fn bool operator!=( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns TRUE if \a s1 and \a s2 are different; otherwise returns FALSE. +*/ + +/*! + \fn const QSize operator+( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns the sum of \a s1 and \a s2; each component is added separately. +*/ + +/*! + \fn const QSize operator-( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns \a s2 subtracted from \a s1; each component is + subtracted separately. +*/ + +/*! + \fn const QSize operator*( const QSize &s, int c ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \overload const QSize operator*( int c, const QSize &s ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \overload const QSize operator*( const QSize &s, double c ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \overload const QSize operator*( double c, const QSize &s ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \fn QSize &QSize::operator/=( int c ) + Divides both the width and height by \a c and returns a reference to the + size. +*/ + +/*! + \fn QSize &QSize::operator/=( double c ) + \overload + Divides both the width and height by \a c and returns a reference to the + size. + + Note that the result is truncated. +*/ + +/*! + \fn const QSize operator/( const QSize &s, int c ) + \relates QSize + Divides \a s by \a c and returns the result. +*/ + +/*! + \fn const QSize operator/( const QSize &s, double c ) + \relates QSize + \overload + Divides \a s by \a c and returns the result. + + Note that the result is truncated. +*/ + +/*! + \fn QSize QSize::expandedTo( const QSize & otherSize ) const + + Returns a size with the maximum width and height of this size and + \a otherSize. +*/ + +/*! + \fn QSize QSize::boundedTo( const QSize & otherSize ) const + + Returns a size with the minimum width and height of this size and + \a otherSize. +*/ + + +void QSize::warningDivByZero() +{ +#if defined(QT_CHECK_MATH) + qWarning( "QSize: Division by zero error" ); +#endif +} + + +/***************************************************************************** + QSize stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QSize + Writes the size \a sz to the stream \a s and returns a reference to + the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QSize &sz ) +{ + if ( s.version() == 1 ) + s << (Q_INT16)sz.width() << (Q_INT16)sz.height(); + else + s << (Q_INT32)sz.width() << (Q_INT32)sz.height(); + return s; +} + +/*! + \relates QSize + Reads the size from the stream \a s into size \a sz and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QSize &sz ) +{ + if ( s.version() == 1 ) { + Q_INT16 w, h; + s >> w; sz.rwidth() = w; + s >> h; sz.rheight() = h; + } + else { + Q_INT32 w, h; + s >> w; sz.rwidth() = w; + s >> h; sz.rheight() = h; + } + return s; +} +#endif // QT_NO_DATASTREAM diff --git a/main/lib/image.effect/qsize.h b/main/lib/image.effect/qsize.h new file mode 100644 index 00000000..13eb3f02 --- /dev/null +++ b/main/lib/image.effect/qsize.h @@ -0,0 +1,256 @@ +/*************************************************************************** + + qsize.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qsize.h 3.3.4 edited May 27 2003 $ +** +** Definition of QSize class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QSIZE_H +#define QSIZE_H + +#include "qt.h" +#include "qpoint.h" + +class Q_EXPORT QSize +// ### Make QSize inherit Qt in Qt 4.0 +{ +public: + // ### Move this enum to qnamespace.h in Qt 4.0 + enum ScaleMode { + ScaleFree, + ScaleMin, + ScaleMax + }; + + QSize(); + QSize( int w, int h ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void transpose(); + + void scale( int w, int h, ScaleMode mode ); + void scale( const QSize &s, ScaleMode mode ); + + QSize expandedTo( const QSize & ) const; + QSize boundedTo( const QSize & ) const; + + QCOORD &rwidth(); + QCOORD &rheight(); + + QSize &operator+=( const QSize & ); + QSize &operator-=( const QSize & ); + QSize &operator*=( int c ); + QSize &operator*=( double c ); + QSize &operator/=( int c ); + QSize &operator/=( double c ); + + friend inline bool operator==( const QSize &, const QSize & ); + friend inline bool operator!=( const QSize &, const QSize & ); + friend inline const QSize operator+( const QSize &, const QSize & ); + friend inline const QSize operator-( const QSize &, const QSize & ); + friend inline const QSize operator*( const QSize &, int ); + friend inline const QSize operator*( int, const QSize & ); + friend inline const QSize operator*( const QSize &, double ); + friend inline const QSize operator*( double, const QSize & ); + friend inline const QSize operator/( const QSize &, int ); + friend inline const QSize operator/( const QSize &, double ); + +private: + static void warningDivByZero(); + + QCOORD wd; + QCOORD ht; +}; + + +/***************************************************************************** + QSize stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QSize & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QSize & ); +#endif + +/***************************************************************************** + QSize inline functions + *****************************************************************************/ + +inline QSize::QSize() +{ wd = ht = -1; } + +inline QSize::QSize( int w, int h ) +{ wd=(QCOORD)w; ht=(QCOORD)h; } + +inline bool QSize::isNull() const +{ return wd==0 && ht==0; } + +inline bool QSize::isEmpty() const +{ return wd<1 || ht<1; } + +inline bool QSize::isValid() const +{ return wd>=0 && ht>=0; } + +inline int QSize::width() const +{ return wd; } + +inline int QSize::height() const +{ return ht; } + +inline void QSize::setWidth( int w ) +{ wd=(QCOORD)w; } + +inline void QSize::setHeight( int h ) +{ ht=(QCOORD)h; } + +inline QCOORD &QSize::rwidth() +{ return wd; } + +inline QCOORD &QSize::rheight() +{ return ht; } + +inline QSize &QSize::operator+=( const QSize &s ) +{ wd+=s.wd; ht+=s.ht; return *this; } + +inline QSize &QSize::operator-=( const QSize &s ) +{ wd-=s.wd; ht-=s.ht; return *this; } + +inline QSize &QSize::operator*=( int c ) +{ wd*=(QCOORD)c; ht*=(QCOORD)c; return *this; } + +inline QSize &QSize::operator*=( double c ) +{ wd=(QCOORD)(wd*c); ht=(QCOORD)(ht*c); return *this; } + +inline bool operator==( const QSize &s1, const QSize &s2 ) +{ return s1.wd == s2.wd && s1.ht == s2.ht; } + +inline bool operator!=( const QSize &s1, const QSize &s2 ) +{ return s1.wd != s2.wd || s1.ht != s2.ht; } + +inline const QSize operator+( const QSize & s1, const QSize & s2 ) +{ return QSize(s1.wd+s2.wd, s1.ht+s2.ht); } + +inline const QSize operator-( const QSize &s1, const QSize &s2 ) +{ return QSize(s1.wd-s2.wd, s1.ht-s2.ht); } + +inline const QSize operator*( const QSize &s, int c ) +{ return QSize(s.wd*c, s.ht*c); } + +inline const QSize operator*( int c, const QSize &s ) +{ return QSize(s.wd*c, s.ht*c); } + +inline const QSize operator*( const QSize &s, double c ) +{ return QSize((QCOORD)(s.wd*c), (QCOORD)(s.ht*c)); } + +inline const QSize operator*( double c, const QSize &s ) +{ return QSize((QCOORD)(s.wd*c), (QCOORD)(s.ht*c)); } + +inline QSize &QSize::operator/=( int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + warningDivByZero(); +#endif + wd/=(QCOORD)c; ht/=(QCOORD)c; + return *this; +} + +inline QSize &QSize::operator/=( double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + warningDivByZero(); +#endif + wd=(QCOORD)(wd/c); ht=(QCOORD)(ht/c); + return *this; +} + +inline const QSize operator/( const QSize &s, int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + QSize::warningDivByZero(); +#endif + return QSize(s.wd/c, s.ht/c); +} + +inline const QSize operator/( const QSize &s, double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + QSize::warningDivByZero(); +#endif + return QSize((QCOORD)(s.wd/c), (QCOORD)(s.ht/c)); +} + +inline QSize QSize::expandedTo( const QSize & otherSize ) const +{ + return QSize( QMAX(wd,otherSize.wd), QMAX(ht,otherSize.ht) ); +} + +inline QSize QSize::boundedTo( const QSize & otherSize ) const +{ + return QSize( QMIN(wd,otherSize.wd), QMIN(ht,otherSize.ht) ); +} + + +#endif // QSIZE_H diff --git a/main/lib/image.effect/qt.h b/main/lib/image.effect/qt.h new file mode 100644 index 00000000..578b1944 --- /dev/null +++ b/main/lib/image.effect/qt.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + qt.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __QT_H +#define __QT_H + +#include "gb_common.h" + +#define Q_EXPORT +#define QT_NO_DATASTREAM +#define QT_NO_STRINGLIST +#define Q_WS_X11 + +#define QT_STATIC_CONST static const +#define QT_STATIC_CONST_IMPL const + +#define QMAX(a, b) ((b) < (a) ? (a) : (b)) +#define QMIN(a, b) ((a) < (b) ? (a) : (b)) +#define QABS(a) ((a) >= 0 ? (a) : -(a)) + +typedef + unsigned short Q_UINT16; + +typedef + int QCOORD; + +typedef + unsigned int QRgb; + +#define QImage MyQImage +#define QPoint MyQPoint +#define QSize MyQSize +#define QRect MyQRect +#define KImageEffect MyKImageEffect +#define QColor MyQColor + +#endif diff --git a/main/lib/image/CImage.c b/main/lib/image/CImage.c new file mode 100644 index 00000000..43715b29 --- /dev/null +++ b/main/lib/image/CImage.c @@ -0,0 +1,598 @@ +/*************************************************************************** + + CImage.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_C + +#include "main.h" +#include "image.h" +#include "CImage.h" + +static int _balance = 0; + +static double _brightness = 0; +static double _contrast = 0; +static double _gamma = 0; +static double _hue = 0; +static double _saturation = 0; +static double _lightness = 0; + +BEGIN_METHOD(Image_new, GB_INTEGER w; GB_INTEGER h; GB_INTEGER col; GB_INTEGER format) + + int format = IMAGE_get_default_format(); + + if (VARGOPT(format, 0) == 1) + format = GB_IMAGE_FMT_SET_PREMULTIPLIED(format); + + IMAGE_create(THIS_IMAGE, VARGOPT(w, 0), VARGOPT(h, 0), format, VARGOPT(col, 0)); + + if (!MISSING(col)) + IMAGE_fill(THIS_IMAGE, VARG(col)); + +END_METHOD + +BEGIN_METHOD_VOID(Image_free) + + IMAGE_delete(THIS_IMAGE); + +END_METHOD + +BEGIN_PROPERTY(Image_Width) + + GB.ReturnInteger(THIS_IMAGE->width); + +END_PROPERTY + +BEGIN_PROPERTY(Image_Height) + + GB.ReturnInteger(THIS_IMAGE->height); + +END_PROPERTY + +BEGIN_PROPERTY(Image_Depth) + + GB.ReturnInteger(32); + +END_PROPERTY + +BEGIN_METHOD(Image_Fill, GB_INTEGER col) + + IMAGE_fill(THIS_IMAGE, VARG(col)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_FillRect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; GB_INTEGER col) + + IMAGE_fill_rect(THIS_IMAGE, VARG(x), VARG(y), VARG(width), VARG(height), VARG(col), TRUE); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_PaintRect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; GB_INTEGER col) + + IMAGE_fill_rect(THIS_IMAGE, VARG(x), VARG(y), VARG(width), VARG(height), VARG(col), FALSE); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_get, GB_INTEGER x; GB_INTEGER y) + + GB.ReturnInteger(IMAGE_get_pixel(THIS_IMAGE, VARG(x), VARG(y))); + +END_METHOD + +BEGIN_METHOD(Image_put, GB_INTEGER col; GB_INTEGER x; GB_INTEGER y) + + IMAGE_set_pixel(THIS_IMAGE, VARG(x), VARG(y), VARG(col)); + +END_METHOD + +BEGIN_PROPERTY(Image_Data) + + GB.ReturnPointer((void *)THIS_IMAGE->data); + +END_PROPERTY + +BEGIN_METHOD_VOID(Image_Desaturate) + + IMAGE_make_gray(THIS_IMAGE); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Gray) + + fprintf(stderr, "warning: Image.Gray is deprecated, use Image.Desaturate instead.\n"); + Image_Desaturate(_object, _param); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Clear) + + //IMAGE_fill(THIS_IMAGE, 0XFF000000); + IMAGE_delete(THIS_IMAGE); + +END_METHOD + +BEGIN_METHOD(Image_Replace, GB_INTEGER src; GB_INTEGER dst; GB_BOOLEAN noteq) + + IMAGE_replace(THIS_IMAGE, VARG(src), VARG(dst), VARGOPT(noteq, FALSE)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Erase, GB_INTEGER color) + + IMAGE_make_transparent(THIS_IMAGE, VARGOPT(color, 0xFFFFFF)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Transparent) + + fprintf(stderr, "warning: Image.Transparent is deprecated, use Image.Erase instead.\n"); + Image_Erase(_object, _param); + +END_METHOD + +BEGIN_METHOD(Image_Colorize, GB_INTEGER color) + + IMAGE_colorize(THIS_IMAGE, VARG(color)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Mask, GB_INTEGER color) + + IMAGE_mask(THIS_IMAGE, VARG(color)); + GB.ReturnObject(THIS); + +END_METHOD + +// Parameter correction +#define CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh) \ + if ( sw < 0 ) sw = src->width; \ + if ( sh < 0 ) sh = src->height; \ + if (dw < 0) dw = sw; \ + if (dh < 0) dh = sh; \ + if (dw != sw || dh != sh) \ + { \ + GB.Error("Stretching images is not implemented in gb.image"); \ + return; \ + } \ + if ( sx < 0 ) { dx -= sx; dw += sx; sw += sx; sx = 0; } \ + if ( sy < 0 ) { dy -= sy; dh += sy; sh += sy; sy = 0; } \ + if ( dx < 0 ) { sx -= dx; sw += dx; dx = 0; } \ + if ( dy < 0 ) { sy -= dy; sh += dy; dy = 0; } \ + if ( (sx + sw) > src->width ) sw = src->width - sx; \ + if ( (sy + sh) > src->height ) sh = src->height - sy; \ + if ( (dx + sw) > dst->width ) sw = dst->width - dx; \ + if ( (dy + sh) > dst->height ) sh = dst->height - dy; \ + if (sw <= 0 || sh <= 0) \ + return; + +BEGIN_METHOD(Image_Copy, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CIMAGE *image; + int x = VARGOPT(x, 0); + int y = VARGOPT(y, 0); + int w = VARGOPT(w, THIS_IMAGE->width); + int h = VARGOPT(h, THIS_IMAGE->height); + + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + if ((x + w) > THIS_IMAGE->width) { w = THIS_IMAGE->width - x; } + if ((y + h) > THIS_IMAGE->height) { h = THIS_IMAGE->height - y; } + + image = GB.New(GB.FindClass("Image"), NULL, NULL); + + IMAGE_create(&image->image, w, h, THIS_IMAGE->format, GB_COLOR_DEFAULT); + if (w > 0 && h > 0) + IMAGE_bitblt(&image->image, 0, 0, -1, -1, THIS_IMAGE, x, y, w, h); + + GB.ReturnObject(image); + +END_METHOD + +BEGIN_METHOD(Image_Resize, GB_INTEGER width; GB_INTEGER height) + + GB_IMG tmp; + int w = VARG(width); + int h = VARG(height); + + if (w < 0) w = THIS_IMAGE->width; + if (h < 0) h = THIS_IMAGE->height; + + //IMAGE_convert(THIS_IMAGE, IMAGE_get_default_format()); + //fprintf(stderr, "format = %d\n", THIS_IMAGE->format); + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, w, h, THIS_IMAGE->format, GB_COLOR_DEFAULT); + IMAGE_bitblt(&tmp, 0, 0, -1, -1, THIS_IMAGE, 0, 0, w, h); + + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Mirror, GB_BOOLEAN horz; GB_BOOLEAN vert) + + GB_IMG tmp; + + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, THIS_IMAGE->width, THIS_IMAGE->height, THIS_IMAGE->format, GB_COLOR_DEFAULT); + IMAGE_mirror(THIS_IMAGE, &tmp, VARG(horz), VARG(vert)); + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_RotateLeft) + + GB_IMG tmp; + + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, THIS_IMAGE->height, THIS_IMAGE->width, THIS_IMAGE->format, GB_COLOR_DEFAULT); + IMAGE_rotate(THIS_IMAGE, &tmp, TRUE); + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_RotateRight) + + GB_IMG tmp; + + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, THIS_IMAGE->height, THIS_IMAGE->width, THIS_IMAGE->format, GB_COLOR_DEFAULT); + IMAGE_rotate(THIS_IMAGE, &tmp, FALSE); + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +#if 0 +BEGIN_METHOD(CIMAGE_transform, GB_FLOAT sx; GB_FLOAT sy; GB_FLOAT dx; GB_FLOAT dy) + + CIMAGE *image; + int w, h; + double dx = VARG(dx); + double dy = VARG(dy); + double f, s; + + GB.New(POINTER(&image), GB.FindClass("Image"), NULL, NULL); + GB.ReturnObject(image); + + f = fabs(dx); + if (f > fabs(dy)) + f = fabs(dy); + + s = 1 / sqrt(dx * dx + dy * dy); + + w = (int)(THIS_IMAGE->width * (1.0 + f) * s + 0.5); + h = (int)(THIS_IMAGE->height * (1.0 + f) * s + 0.5); + + if (!w || !h) + return; + + IMAGE_create(&image->image, w, h, THIS_IMAGE->format, GB_COLOR_DEFAULT); + IMAGE_transform(&image->image, THIS_IMAGE, VARG(sx), VARG(sy), VARG(dx), VARG(dy)); + +END_METHOD +#endif + +BEGIN_METHOD(Image_DrawAlpha, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CIMAGE *image = VARG(image); + + if (GB.CheckObject(image)) + return; + + IMAGE_draw_alpha(THIS_IMAGE, VARGOPT(x, 0), VARGOPT(y, 0), &image->image, VARGOPT(srcx, 0), VARGOPT(srcy, 0), VARGOPT(srcw, -1), VARGOPT(srch, -1)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_DrawImage, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CIMAGE *image = VARG(image); + + if (GB.CheckObject(image)) + return; + + IMAGE_bitblt(THIS_IMAGE, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, -1), VARGOPT(h, -1), &image->image, VARGOPT(srcx, 0), VARGOPT(srcy, 0), VARGOPT(srcw, -1), VARGOPT(srch, -1)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CIMAGE *image = VARG(image); + + if (GB.CheckObject(image)) + return; + + IMAGE_compose(THIS_IMAGE, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, -1), VARGOPT(h, -1), &image->image, VARGOPT(srcx, 0), VARGOPT(srcy, 0), VARGOPT(srcw, -1), VARGOPT(srch, -1)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_PROPERTY(Image_Format) + + int fmt; + + if (READ_PROPERTY) + { + GB.ReturnConstZeroString(IMAGE_format_to_string(THIS_IMAGE->format)); + } + else + { + fmt = IMAGE_format_from_string(GB.ToZeroString(PROP(GB_STRING))); + + if (fmt < 0) + { + GB.Error("Unknown format"); + return; + } + + IMAGE_convert(THIS_IMAGE, fmt); + } + +END_PROPERTY + +BEGIN_PROPERTY(Image_Debug) + + if (READ_PROPERTY) + GB.ReturnBoolean(IMAGE_debug); + else + IMAGE_debug = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_METHOD(Image_Opacity, GB_FLOAT opacity) + + int alpha = VARG(opacity) * 255; + + if (alpha < 0) + alpha = 0; + else if (alpha > 255) + alpha = 255; + + IMAGE_set_opacity(THIS_IMAGE, (uchar)alpha); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Invert, GB_BOOLEAN keep_color) + + IMAGE_invert(THIS_IMAGE, VARGOPT(keep_color, FALSE)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Blur, GB_INTEGER radius) + + IMAGE_blur(THIS_IMAGE, VARGOPT(radius, 8)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_PROPERTY(Image_Pixels) + + GB_ARRAY array; + int size; + + if (!GB_IMAGE_FMT_IS_32_BITS(THIS_IMAGE->format)) + { + GB.Error("Image format must be 32 bits"); + return; + } + + size = THIS_IMAGE->width * THIS_IMAGE->height; + + if (READ_PROPERTY) + { + GB.Array.New(&array, GB_T_INTEGER, size); + IMAGE_get_pixels(THIS_IMAGE, GB.Array.Get(array, 0)); + GB.ReturnObject(array); + } + else + { + array = VPROP(GB_OBJECT); + + if (GB.CheckObject(array)) + return; + + if (GB.Array.Count(array) < size) + { + GB.Error("Not enough pixels"); + return; + } + + IMAGE_set_pixels(THIS_IMAGE, GB.Array.Get(array, 0)); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(Image_BeginBalance) + + _balance++; + GB.ReturnObject(THIS); + + +END_METHOD + +BEGIN_METHOD_VOID(Image_EndBalance) + + if (_balance <= 0) + { + GB.Error("Missing call to BeginBalance"); + return; + } + + _balance--; + + if (_balance == 0) + IMAGE_balance(THIS_IMAGE, _brightness * 255, _contrast * 255, _gamma * 255, _hue * 180, _saturation * 255, _lightness * 255); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Brightness, GB_FLOAT value) + + if (_balance) + _brightness = VARG(value); + else + IMAGE_balance(THIS_IMAGE, VARG(value) * 255, 0, 0, 0, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Contrast, GB_FLOAT value) + + if (_balance) + _contrast = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, VARG(value) * 255, 0, 0, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Gamma, GB_FLOAT value) + + if (_balance) + _gamma = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, VARG(value) * 255, 0, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Hue, GB_FLOAT value) + + if (_balance) + _hue = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, 0, VARG(value) * 180, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Saturation, GB_FLOAT value) + + if (_balance) + _saturation = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, 0, 0, VARG(value) * 255, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Lightness, GB_FLOAT value) + + if (_balance) + _lightness = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, 0, 0, 0, VARG(value) * 255); + + GB.ReturnObject(THIS); + +END_METHOD + + + +//--------------------------------------------------------------------------- + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_CONSTANT("Standard", "i", 0), + GB_CONSTANT("Premultiplied", "i", 1), + + GB_STATIC_PROPERTY("Debug", "b", Image_Debug), + + GB_METHOD("_new", NULL, Image_new, "[(Width)i(Height)i(Color)i(Format)i]"), + GB_METHOD("_free", NULL, Image_free, NULL), + + GB_METHOD("_get", "i", Image_get, "(X)i(Y)i"), + GB_METHOD("_put", NULL, Image_put, "(Color)i(X)i(Y)i"), + + GB_PROPERTY_READ("Width", "i", Image_Width), + GB_PROPERTY_READ("Height", "i", Image_Height), + GB_PROPERTY_READ("W", "i", Image_Width), + GB_PROPERTY_READ("H", "i", Image_Height), + GB_PROPERTY_READ("Depth", "i", Image_Depth), + GB_PROPERTY_READ("Data", "p", Image_Data), + GB_PROPERTY("Format", "s", Image_Format), + GB_PROPERTY("Pixels", "Integer[]", Image_Pixels), + + GB_METHOD("Clear", NULL, Image_Clear, NULL), + GB_METHOD("Fill", "Image", Image_Fill, "(Color)i"), + GB_METHOD("Gray", "Image", Image_Gray, NULL), + GB_METHOD("Transparent", "Image", Image_Transparent, "[(Color)i]"), + GB_METHOD("Desaturate", "Image", Image_Desaturate, NULL), + GB_METHOD("Erase", "Image", Image_Erase, "[(Color)i]"), + GB_METHOD("Replace", "Image", Image_Replace, "(OldColor)i(NewColor)i[(NotEqual)b]"), + GB_METHOD("Colorize", "Image", Image_Colorize, "(Color)i"), + GB_METHOD("Mask", "Image", Image_Mask, "(Color)i"), + GB_METHOD("Opacity", "Image", Image_Opacity, "(Opacity)f"), + GB_METHOD("Invert", "Image", Image_Invert, "[(KeepHue)b]"), + + GB_METHOD("Copy", "Image", Image_Copy, "[(X)i(Y)i(Width)i(Height)i]"), + GB_METHOD("Resize", "Image", Image_Resize, "(Width)i(Height)i"), + + GB_METHOD("Mirror", "Image", Image_Mirror, "(Horizontal)b(Vertical)b"), + GB_METHOD("RotateLeft", "Image", Image_RotateLeft, NULL), + GB_METHOD("RotateRight", "Image", Image_RotateRight, NULL), + + GB_METHOD("FillRect", "Image", Image_FillRect, "(X)i(Y)i(Width)i(Height)i(Color)i"), + GB_METHOD("PaintRect", "Image", Image_PaintRect, "(X)i(Y)i(Width)i(Height)i(Color)i"), + GB_METHOD("DrawAlpha", "Image", Image_DrawAlpha, "(Image)Image;[(X)i(Y)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_METHOD("DrawImage", "Image", Image_DrawImage, "(Image)Image;[(X)i(Y)i(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + GB_METHOD("PaintImage", "Image", Image_PaintImage, "(Image)Image;[(X)i(Y)i(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_METHOD("Fuzzy", "Image", Image_Blur, "[(Radius)i]"), + + GB_METHOD("BeginBalance", "Image", Image_BeginBalance, NULL), + GB_METHOD("Brightness", "Image", Image_Brightness, "(Brightness)f"), + GB_METHOD("Contrast", "Image", Image_Contrast, "(Contrast)f"), + GB_METHOD("Gamma", "Image", Image_Gamma, "(Gamma)f"), + GB_METHOD("Hue", "Image", Image_Hue, "(Hue)f"), + GB_METHOD("Saturation", "Image", Image_Saturation, "(Saturation)f"), + GB_METHOD("Lightness", "Image", Image_Lightness, "(Lightness)f"), + GB_METHOD("EndBalance", "Image", Image_EndBalance, NULL), + + GB_END_DECLARE +}; + diff --git a/main/lib/image/CImage.h b/main/lib/image/CImage.h new file mode 100644 index 00000000..4e0d47fa --- /dev/null +++ b/main/lib/image/CImage.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "gambas.h" +#include "gb.image.h" + +#ifndef __CIMAGE_C + +extern GB_DESC CImageDesc[]; + +#else + +#define THIS ((CIMAGE *)_object) +#define THIS_IMAGE (&THIS->image) + +#endif + +typedef + struct { + GB_IMG image; + } + CIMAGE; + +#endif + diff --git a/main/lib/image/CImageStat.c b/main/lib/image/CImageStat.c new file mode 100644 index 00000000..29f515cb --- /dev/null +++ b/main/lib/image/CImageStat.c @@ -0,0 +1,140 @@ +/*************************************************************************** + + CImageStat.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGESTAT_C + +#include +#include "main.h" +#include "image_stat.h" +#include "CImageStat.h" + +// static int my_mmap(const char *path, char **paddr, int *plen) +// { +// struct stat info; +// int fd; +// void *addr; +// size_t len; +// +// fd = open(path, O_RDONLY); +// if (fd < 0) +// return -1; +// +// if (fstat(fd, &info) < 0) +// return -1; +// +// len = info.st_size; +// addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); +// if (addr == MAP_FAILED) +// return -1; +// +// *paddr = addr; +// *plen = len; +// return fd; +// } + +BEGIN_METHOD(CIMAGESTAT_call, GB_STRING path) + + char *path = GB.FileName(STRING(path), LENGTH(path)); + IMAGE_STREAM stream; + IMAGE_INFO info = {0}; + CIMAGESTAT *stat; + + if (GB.LoadFile(path, strlen(path), &stream.addr, &stream.len)) + return; + + stream.pos = 0; + + if (IMAGE_get_info(&stream, &info)) + { + GB.Error("Unable to stat image: &1", IMAGE_error); + stat = NULL; + } + else + { + stat = GB.New(GB.FindClass("ImageStat"), NULL, NULL); + stat->path = GB.NewZeroString(path); + stat->type = info.type; + stat->width = info.width; + stat->height = info.height; + stat->depth = info.depth; + } + + GB.ReleaseFile(stream.addr, stream.len); + + GB.ReturnObject(stat); + +END_METHOD + +BEGIN_METHOD_VOID(CIMAGESTAT_free) + + GB.FreeString(&THIS->path); + +END_METHOD + +BEGIN_PROPERTY(CIMAGESTAT_path) + + GB.ReturnString(THIS->path); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_type) + + GB.ReturnConstZeroString(THIS->type); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_width) + + GB.ReturnInteger(THIS->width); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_height) + + GB.ReturnInteger(THIS->height); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_depth) + + GB.ReturnInteger(THIS->depth); + +END_PROPERTY + +GB_DESC CImageStatDesc[] = +{ + GB_DECLARE("ImageStat", sizeof(CIMAGESTAT)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_call", "ImageStat", CIMAGESTAT_call, "(Path)s"), + GB_METHOD("_free", NULL, CIMAGESTAT_free, NULL), + + GB_PROPERTY_READ("Path", "s", CIMAGESTAT_path), + GB_PROPERTY_READ("Type", "s", CIMAGESTAT_type), + GB_PROPERTY_READ("Width", "i", CIMAGESTAT_width), + GB_PROPERTY_READ("Height", "i", CIMAGESTAT_height), + GB_PROPERTY_READ("Depth", "i", CIMAGESTAT_depth), + + GB_END_DECLARE +}; + diff --git a/main/lib/image/CImageStat.h b/main/lib/image/CImageStat.h new file mode 100644 index 00000000..e5814ae9 --- /dev/null +++ b/main/lib/image/CImageStat.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + CImageStat.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGESTAT_H +#define __CIMAGESTAT_H + +#include "gambas.h" + +#ifndef __CIMAGESTAT_C + +extern GB_DESC CImageStatDesc[]; + +#else + +#define THIS ((CIMAGESTAT *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + char *path; + char *type; + int width; + int height; + int depth; + } + CIMAGESTAT; + +#endif + diff --git a/main/lib/image/Makefile.am b/main/lib/image/Makefile.am new file mode 100644 index 00000000..abc4c7e5 --- /dev/null +++ b/main/lib/image/Makefile.am @@ -0,0 +1,24 @@ +COMPONENT = gb.image +include $(top_srcdir)/component.am + +noinst_LTLIBRARIES = libimage.la +gblib_LTLIBRARIES = gb.image.la + +libimage_la_LIBADD = +libimage_la_LDFLAGS = -module @LD_FLAGS@ +libimage_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS_OPT) + +libimage_la_SOURCES = \ + image.h image.c + +gb_image_la_LIBADD = libimage.la @MATH_LIB@ +gb_image_la_LDFLAGS = -module @LD_FLAGS@ +gb_image_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_image_la_SOURCES = \ + image_stat.h image_stat.c \ + CImage.h CImage.c \ + CImageStat.h CImageStat.c \ + c_color.h c_color.c \ + gb.image.h \ + main.h main.c diff --git a/main/lib/image/c_color.c b/main/lib/image/c_color.c new file mode 100644 index 00000000..c7e35e8a --- /dev/null +++ b/main/lib/image/c_color.c @@ -0,0 +1,729 @@ +/*************************************************************************** + + c_color.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_COLOR_C + +#include +#include "c_color.h" + +void gt_color_to_rgba(uint color, int *r, int *g, int *b, int *a) +{ + *b = color & 0xFF; + *g = (color >> 8) & 0xFF; + *r = (color >> 16) & 0xFF; + *a = (color >> 24) & 0xFF; +} + +static uint gt_rgba_to_color(int r, int g, int b, int a) +{ + return (uint)((uchar)b | ((uchar)g << 8) | ((uchar)r << 16) | ((uchar)a << 24)); +} + +void COLOR_rgb_to_hsv(int r, int g, int b, int *H, int *S, int *V) +{ + int v, x, f; + int i; + + x = r; + if (g < x) x = g; + if (b < x) x = b; + + v = r; + if (g > v) v = g; + if (b > v) v = b; + + if (v == x) + { + *H = -1; + *S = 0; + *V = v; + } + else + { + f = (r == x) ? g - b : ((g == x) ? b - r : r - g); + i = (r == x) ? 3 : ((g == x) ? 5 : 1); + *H = (int)((i - (double)f / (v - x)) * 60); + *S = ((v - x) * 255) / v; + *V = v; + if (*H == 360) *H = 0; + } +} + +static void gt_rgb_to_hsv_cached(int r, int g, int b, int *h, int *s, int *v) +{ + static int old_r = 0, old_g = 0, old_b = 0, old_h = -1, old_s = 0, old_v = 0; + + if (r == old_r && g == old_g && b == old_b) + { + *h = old_h; + *s = old_s; + *v = old_v; + return; + } + + COLOR_rgb_to_hsv(r, g, b, h, s, v); + + old_r = r; + old_g = g; + old_b = b; + old_h = *h; + old_s = *s; + old_v = *v; +} + +void COLOR_hsv_to_rgb(int h, int s, int v, int *R, int *G, int *B) +{ + double var_h; + int var_i; + int var_1, var_2, var_3; + int tmp_r, tmp_g, tmp_b; + + if (h < 0) + h = 360 - ((-h) % 360); + else + h = h % 360; + + /*H = ((double)h) / 360; + S = ((double)s) / 255; + V = ((double)v) / 255;*/ + + if (s == 0) + { + *R = v; + *G = v; + *B = v; + } + else + { + var_i = h / 60; + var_h = h % 60; //((double)h / 60) - var_i; + + //var_1 = V * ( 1 - S ); + var_1 = v * (255 - s) / 255; + + //var_2 = V * ( 1 - S * ( var_h - var_i ) ); + var_2 = v * (255 - s * var_h / 60) / 255; + + //var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) ); + var_3 = v * (255 - s * (60 - var_h) / 60) / 255; + + switch (var_i) + { + case 0: + tmp_r = v; + tmp_g = var_3; + tmp_b = var_1; + break; + + case 1: + tmp_r = var_2; + tmp_g = v; + tmp_b = var_1; + break; + + case 2: + tmp_r = var_1; + tmp_g = v; + tmp_b = var_3; + break; + + case 3: + tmp_r = var_1; + tmp_g = var_2; + tmp_b = v; + break; + + case 4: + tmp_r = var_3; + tmp_g = var_1; + tmp_b = v; + break; + + default: + tmp_r = v; + tmp_g = var_1; + tmp_b = var_2; + break; + } + + *R = tmp_r; + *G = tmp_g; + *B = tmp_b; + + } +} + +static int get_luminance(CCOLOR *_object) +{ + return (int)(0.299 * THIS->r + 0.587 * THIS->g + 0.114 * THIS->b + 0.5); +} + + +int COLOR_get_luminance(GB_COLOR col) +{ + CCOLOR info; + gt_color_to_rgba(col, &info.r, &info.g, &info.b, &info.a); + return get_luminance(&info); +} + + +int COLOR_invert_luminance(int l) +{ + double m = 0.7; + double d = l / 255.0; + + if (d < m) + d = 1 - (d * (1 - m) / m); + else + d = (1 - d) * m / (1 - m); + + return (int)(255 * d); +} + + +static void set_luminance(CCOLOR *_object, int l) +{ + int c; + + if (l <= 0) + { + THIS->r = 0; + THIS->g = 0; + THIS->b = 0; + return; + } + else if (l >= 255) + { + THIS->r = 255; + THIS->g = 255; + THIS->b = 255; + return; + } + + for(;;) + { + c = get_luminance(THIS); + if (c == l) + return; + if (c == (l + 1) || c == (l - 1)) + break; + THIS->r = MinMax(THIS->r + l - c, 0, 255); + THIS->g = MinMax(THIS->g + l - c, 0, 255); + THIS->b = MinMax(THIS->b + l - c, 0, 255); + } + + THIS->g = MinMax(THIS->g + ((l > c) ? 1 : -1), 0, 255); + c = get_luminance(THIS); + if (c == l) + return; + + THIS->r = MinMax(THIS->r + ((l > c) ? 1 : -1), 0, 255); + c = get_luminance(THIS); + if (c == l) + return; + + THIS->b = MinMax(THIS->b + ((l > c) ? 1 : -1), 0, 255); +} + + +GB_COLOR COLOR_set_luminance(GB_COLOR col, int l) +{ + CCOLOR info; + gt_color_to_rgba(col, &info.r, &info.g, &info.b, &info.a); + set_luminance(&info, l); + return gt_rgba_to_color(info.r, info.g, info.b, info.a); +} + + +GB_COLOR COLOR_merge(GB_COLOR col1, GB_COLOR col2, double weight) +{ + int r, g, b; + int h1, s1, v1, a1; + int h2, s2, v2, a2; + + if (weight == 0.0) + return col1; + else if (weight == 1.0) + return col2; + else + { + gt_color_to_rgba(col1, &r, &g, &b, &a1); + COLOR_rgb_to_hsv(r, g, b, &h1, &s1, &v1); + gt_color_to_rgba(col2, &r, &g, &b, &a2); + COLOR_rgb_to_hsv(r, g, b, &h2, &s2, &v2); + + #define MIX(_val1, _val2) ((int)((_val1) * (1 - weight) + (_val2) * weight + 0.5)) + + if (h1 < 0) + h1 = h2; + else if (h2 < 0) + h2 = h1; + else + h1 = MIX(h1, h2); + + COLOR_hsv_to_rgb(h1, MIX(s1, s2), MIX(v1, v2), &r, &g, &b); + + return gt_rgba_to_color(r, g, b, MIX(a1, a2)); + } +} + +GB_COLOR COLOR_gradient(GB_COLOR col1, GB_COLOR col2, double weight) +{ + int r1, g1, b1, a1; + int r2, g2, b2, a2; + + if (weight == 0.0) + return col1; + else if (weight == 1.0) + return col2; + else + { + gt_color_to_rgba(col1, &r1, &g1, &b1, &a1); + gt_color_to_rgba(col2, &r2, &g2, &b2, &a2); + + #define MIX(_val1, _val2) ((int)((_val1) * (1 - weight) + (_val2) * weight + 0.5)) + + return gt_rgba_to_color(MIX(r1, r2), MIX(g1, g2), MIX(b1, b2), MIX(a1, a2)); + } +} + +GB_COLOR COLOR_lighter(GB_COLOR color) +{ + int h, s, v; + int r, g, b, a; + + gt_color_to_rgba(color, &r, &g, &b, &a); + COLOR_rgb_to_hsv(r, g, b, &h, &s, &v); + COLOR_hsv_to_rgb(h, s / 2, 255 - (255 - v) / 2, &r, &g, &b); + + return gt_rgba_to_color(r, g, b, a); +} + +GB_COLOR COLOR_darker(GB_COLOR color) +{ + int h, s, v; + int r, g, b, a; + + gt_color_to_rgba(color, &r, &g, &b, &a); + COLOR_rgb_to_hsv(r, g, b, &h, &s, &v); + COLOR_hsv_to_rgb(h, s ? 255 - (255 - s) / 2 : 0, v / 2, &r, &g, &b); + + v = gt_rgba_to_color(r, g, b, a); + + return v; +} + +BEGIN_METHOD(Color_RGB, GB_INTEGER r; GB_INTEGER g; GB_INTEGER b; GB_INTEGER a) + + GB.ReturnInteger(gt_rgba_to_color(VARG(r), VARG(g), VARG(b), VARGOPT(a, 0))); + +END_METHOD + +BEGIN_METHOD(Color_SetRGB, GB_INTEGER color; GB_INTEGER red; GB_INTEGER green; GB_INTEGER blue; GB_INTEGER alpha) + + int r, g, b, a; + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + GB.ReturnInteger(gt_rgba_to_color(VARGOPT(red, r), VARGOPT(green, g), VARGOPT(blue, b), VARGOPT(alpha, a))); + +END_METHOD + +BEGIN_METHOD(Color_HSV, GB_INTEGER h; GB_INTEGER s; GB_INTEGER v; GB_INTEGER a) + + int r, g, b; + COLOR_hsv_to_rgb(VARG(h), VARG(s), VARG(v), &r, &g, &b); + GB.ReturnInteger(gt_rgba_to_color(r, g, b, VARGOPT(a, 0))); + +END_METHOD + +BEGIN_METHOD(Color_SetHSV, GB_INTEGER color; GB_INTEGER hue; GB_INTEGER saturation; GB_INTEGER value; GB_INTEGER alpha) + + int r, g, b, a, h, s, v; + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + gt_rgb_to_hsv_cached(r, g, b, &h, &s, &v); + COLOR_hsv_to_rgb(VARGOPT(hue, h), VARGOPT(saturation, s), VARGOPT(value, v), &r, &g, &b); + GB.ReturnInteger(gt_rgba_to_color(r, g, b, VARGOPT(alpha, a))); + +END_METHOD + +BEGIN_METHOD(Color_get, GB_INTEGER color) + + static GB_CLASS klass = 0; + CCOLOR *info; + + if (!klass) klass = GB.FindClass("ColorInfo"); + info = GB.New(klass, NULL, NULL); + + gt_color_to_rgba(VARG(color), &info->r, &info->g, &info->b, &info->a); + GB.ReturnObject(info); + +END_METHOD + +static void handle_rgba_property(CCOLOR *_object, void *_param, int prop) +{ + if (READ_PROPERTY) + { + switch(prop) + { + case CC_R: GB.ReturnInteger(THIS->r); break; + case CC_G: GB.ReturnInteger(THIS->g); break; + case CC_B: GB.ReturnInteger(THIS->b); break; + case CC_A: GB.ReturnInteger(THIS->a); break; + } + } + else + { + int v = VPROP(GB_INTEGER); + if (v < 0) v = 0; else if (v > 255) v = 255; + switch(prop) + { + case CC_R: THIS->r = v; break; + case CC_G: THIS->g = v; break; + case CC_B: THIS->b = v; break; + case CC_A: THIS->a = v; break; + } + } +} + +BEGIN_PROPERTY(ColorInfo_Alpha) + + handle_rgba_property(_object, _param, CC_A); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Red) + + handle_rgba_property(_object, _param, CC_R); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Green) + + handle_rgba_property(_object, _param, CC_G); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Blue) + + handle_rgba_property(_object, _param, CC_B); + +END_PROPERTY + +static void handle_hsv_property(CCOLOR *_object, void *_param, int prop) +{ + int h, s, v; + gt_rgb_to_hsv_cached(THIS->r, THIS->g, THIS->b, &h, &s, &v); + + if (READ_PROPERTY) + { + switch(prop) + { + case CC_H: GB.ReturnInteger(h); break; + case CC_V: GB.ReturnInteger(v); break; + case CC_S: GB.ReturnInteger(s); break; + } + } + else + { + switch(prop) + { + case CC_H: h = VPROP(GB_INTEGER) % 360; break; + case CC_V: v = VPROP(GB_INTEGER); if (v < 0) v = 0; else if (v > 255) v = 255; break; + case CC_S: s = VPROP(GB_INTEGER); if (s < 0) s = 0; else if (s > 255) s = 255; break; + } + COLOR_hsv_to_rgb(h, s, v, &THIS->r, &THIS->g, &THIS->b); + } +} + +BEGIN_PROPERTY(ColorInfo_Hue) + + handle_hsv_property(THIS, _param, CC_H); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Saturation) + + handle_hsv_property(THIS, _param, CC_S); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Value) + + handle_hsv_property(THIS, _param, CC_V); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Color) + + CCOLOR *info = THIS; + + if (READ_PROPERTY) + GB.ReturnInteger(gt_rgba_to_color(info->r, info->g, info->b, info->a)); + else + gt_color_to_rgba(VPROP(GB_INTEGER), &info->r, &info->g, &info->b, &info->a); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Luminance) + + if (READ_PROPERTY) + GB.ReturnInteger(get_luminance(THIS)); + else + set_luminance(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Color_Lighter, GB_INTEGER color) + + GB.ReturnInteger(COLOR_lighter(VARG(color))); + +END_METHOD + +BEGIN_METHOD(Color_Darker, GB_INTEGER color) + + GB.ReturnInteger(COLOR_darker(VARG(color))); + +END_METHOD + +BEGIN_METHOD(Color_Merge, GB_INTEGER color1; GB_INTEGER color2; GB_FLOAT weight) + + GB.ReturnInteger(COLOR_merge(VARG(color1), VARG(color2), VARGOPT(weight, 0.5))); + +END_METHOD + +BEGIN_METHOD(Color_Gradient, GB_INTEGER color1; GB_INTEGER color2; GB_FLOAT weight) + + GB.ReturnInteger(COLOR_gradient(VARG(color1), VARG(color2), VARGOPT(weight, 0.5))); + +END_METHOD + +BEGIN_METHOD(Color_Desaturate, GB_INTEGER color) + + int r, g, b, a, gray; + + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + gray = (r * 11 + g * 16 + b * 5) / 32; + GB.ReturnInteger(gt_rgba_to_color(gray, gray, gray, a)); + +END_METHOD + +BEGIN_METHOD(Color_Blend, GB_INTEGER src; GB_INTEGER dst) + + uint src = VARG(src); + uint dst = VARG(dst); + uchar rs, gs, bs; + uchar rd, gd, bd; + uchar as = src >> 24; + uchar ad = dst >> 24; + + if (as == 0xFF) + { + GB.ReturnInteger(dst); + return; + } + else if (as == 0) + { + GB.ReturnInteger(src); + return; + } + + ad ^= 0xFF; + as ^= 0xFF; + + bs = src & 0xFF; + gs = (src >> 8) & 0xFF; + rs = (src >> 16) & 0xFF; + + bd = dst & 0xFF; + gd = (dst >> 8) & 0xFF; + rd = (dst >> 16) & 0xFF; + + // D = S * alpha(S) + D * (1 - alpha(S)) + + bd = (((bs - bd) * as) >> 8) + bd; + rd = (((rs - rd) * as) >> 8) + rd; + gd = (((gs - gd) * as) >> 8) + gd; + if (ad < as) ad = as; + ad ^= 0xFF; + + GB.ReturnInteger(bd | (gd << 8) | (rd << 16) | (ad << 24)); + +END_METHOD + +BEGIN_METHOD(Color_GetAlpha, GB_INTEGER color) + + int r, g, b, a; + + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + GB.ReturnInteger(a); + +END_METHOD + +BEGIN_METHOD(Color_SetAlpha, GB_INTEGER color; GB_INTEGER alpha) + + int r, g, b, a; + + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + a = VARG(alpha); + GB.ReturnInteger(gt_rgba_to_color(r, g, b, a)); + +END_METHOD + +BEGIN_METHOD(Color_Distance, GB_INTEGER col1; GB_INTEGER col2) + + int r1, g1, b1, a1; + int r2, g2, b2, a2; + + gt_color_to_rgba(VARG(col1), &r1, &g1, &b1, &a1); + gt_color_to_rgba(VARG(col2), &r2, &g2, &b2, &a2); + + r1 -= r2; + g1 -= g2; + b1 -= b2; + a1 -= a2; + r1 *= r1; + g1 *= g1; + b1 *= b1; + a1 *= a1; + + GB.ReturnFloat(sqrt(r1 + b1 + g1 + a1) / 510.0); + +END_METHOD + +BEGIN_METHOD(Color_Invert, GB_INTEGER color; GB_BOOLEAN keep_hue) + + GB_COLOR color = VARG(color); + + if (VARGOPT(keep_hue, FALSE)) + { + GB.ReturnInteger(COLOR_set_luminance(color, COLOR_invert_luminance(COLOR_get_luminance(color)))); + } + else + { + int r, g, b, a; + gt_color_to_rgba(color, &r, &g, &b, &a); + GB.ReturnInteger(gt_rgba_to_color(255 - r, 255 - g, 255 - b, a)); + } + +END_METHOD + +GB_DESC CColorInfoDesc[] = +{ + GB_DECLARE("ColorInfo", sizeof(CCOLOR)), GB_NOT_CREATABLE(), + + GB_PROPERTY("Alpha", "i", ColorInfo_Alpha), + GB_PROPERTY("Red", "i", ColorInfo_Red), + GB_PROPERTY("Green", "i", ColorInfo_Green), + GB_PROPERTY("Blue", "i", ColorInfo_Blue), + GB_PROPERTY("Hue", "i", ColorInfo_Hue), + GB_PROPERTY("Saturation", "i", ColorInfo_Saturation), + GB_PROPERTY("Value", "i", ColorInfo_Value), + GB_PROPERTY("Color", "i", ColorInfo_Color), + GB_PROPERTY("Luminance", "i", ColorInfo_Luminance), + + GB_END_DECLARE +}; + +GB_DESC CColorDesc[] = +{ + GB_DECLARE_STATIC("Color"), + + GB_CONSTANT("Default", "i", GB_COLOR_DEFAULT), + + GB_CONSTANT("Black", "i", 0x000000), + GB_CONSTANT("White", "i", 0xFFFFFF), + + GB_CONSTANT("LightGray", "i", 0xC0C0C0), + GB_CONSTANT("Gray", "i", 0x808080), + GB_CONSTANT("DarkGray", "i", 0x404040), + + GB_CONSTANT("Blue", "i", 0x0000FF), + GB_CONSTANT("DarkBlue", "i", 0x000080), + GB_CONSTANT("SoftBlue", "i", 0x8080FF), + + GB_CONSTANT("Green", "i", 0x00FF00), + GB_CONSTANT("DarkGreen", "i", 0x008000), + GB_CONSTANT("SoftGreen", "i", 0x80FF80), + + GB_CONSTANT("Red", "i", 0xFF0000), + GB_CONSTANT("DarkRed", "i", 0x800000), + GB_CONSTANT("SoftRed", "i", 0xFF8080), + + GB_CONSTANT("Cyan", "i", 0x00FFFF), + GB_CONSTANT("DarkCyan", "i", 0x008080), + GB_CONSTANT("SoftCyan", "i", 0x80FFFF), + + GB_CONSTANT("Magenta", "i", 0xFF00FF), + GB_CONSTANT("DarkMagenta", "i", 0x800080), + GB_CONSTANT("SoftMagenta", "i", 0xFF80FF), + GB_CONSTANT("Pink", "i", 0xFF80FF), + + GB_CONSTANT("Yellow", "i", 0xFFFF00), + GB_CONSTANT("DarkYellow", "i", 0x808000), + GB_CONSTANT("SoftYellow", "i", 0xFFFF80), + + GB_CONSTANT("Orange", "i", 0xFF8000), + GB_CONSTANT("DarkOrange", "i", 0x804000), + GB_CONSTANT("SoftOrange", "i", 0xFFC080), + + GB_CONSTANT("Violet", "i", 0x8000FF), + GB_CONSTANT("DarkViolet", "i", 0x400080), + GB_CONSTANT("SoftViolet", "i", 0xC080FF), + + GB_CONSTANT("Royal", "i", 0x0080FF), + GB_CONSTANT("DarkRoyal", "i", 0x004080), + GB_CONSTANT("SoftRoyal", "i", 0x80C0FF), + + GB_CONSTANT("Purple", "i", 0xFF0080), + GB_CONSTANT("DarkPurple", "i", 0x800040), + GB_CONSTANT("SoftPurple", "i", 0xFF80C0), + + GB_CONSTANT("Transparent", "i", 0xFF000000), + + GB_STATIC_METHOD("RGB", "i", Color_RGB, "(Red)i(Green)i(Blue)i[(Alpha)i]"), + GB_STATIC_METHOD("HSV", "i", Color_HSV, "(Hue)i(Saturation)i(Value)i[(Alpha)i]"), + GB_STATIC_METHOD("_call", "i", Color_RGB, "(Red)i(Green)i(Blue)i[(Alpha)i]"), + + GB_STATIC_METHOD("Lighter", "i", Color_Lighter, "(Color)i"), + GB_STATIC_METHOD("Darker", "i", Color_Darker, "(Color)i"), + GB_STATIC_METHOD("Merge", "i", Color_Merge, "(Color1)i(Color2)i[(Weight)f]"), + GB_STATIC_METHOD("Gradient", "i", Color_Gradient, "(Color1)i(Color2)i[(Weight)f]"), + GB_STATIC_METHOD("Blend", "i", Color_Blend, "(Source)i(Destination)i"), + GB_STATIC_METHOD("Desaturate", "i", Color_Desaturate, "(Color)i"), + GB_STATIC_METHOD("Invert", "i", Color_Invert, "(Color)i[(KeepHue)b]"), + + GB_STATIC_METHOD("SetAlpha", "i", Color_SetAlpha, "(Color)i(Alpha)i"), + GB_STATIC_METHOD("SetRGB", "i", Color_SetRGB, "(Color)i[(Red)i(Green)i(Blue)i(Alpha)i]"), + GB_STATIC_METHOD("SetHSV", "i", Color_SetHSV, "(Color)i[(Hue)i(Saturation)i(Value)i(Alpha)i]"), + GB_STATIC_METHOD("GetAlpha", "i", Color_GetAlpha, "(Color)i"), + + GB_STATIC_METHOD("Distance", "f", Color_Distance, "(Color1)i(Color2)i"), + + GB_STATIC_METHOD("_get", "ColorInfo", Color_get, "(Color)i"), + //GB_STATIC_METHOD("_call", "ColorInfo", Color_get, "(Color)i"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/image/c_color.h b/main/lib/image/c_color.h new file mode 100644 index 00000000..74c5f718 --- /dev/null +++ b/main/lib/image/c_color.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + c_color.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_COLOR_H +#define __C_COLOR_H + +#include "main.h" + +#ifndef __C_COLOR_C +extern GB_DESC CColorDesc[]; +extern GB_DESC CColorInfoDesc[]; +#else +#define THIS ((CCOLOR *)_object) +enum { CC_R, CC_G, CC_B, CC_A, CC_H, CC_S, CC_V }; +#endif + +typedef + struct { + GB_BASE ob; + int r, g, b, a; + } + CCOLOR; + +void COLOR_rgb_to_hsv(int r, int g, int b, int *H, int *S, int *V); +void COLOR_hsv_to_rgb(int h, int s, int v, int *R, int *G, int *B); +GB_COLOR COLOR_merge(GB_COLOR col1, GB_COLOR col2, double weight); +GB_COLOR COLOR_lighter(GB_COLOR color); +GB_COLOR COLOR_darker(GB_COLOR color); +int COLOR_get_luminance(GB_COLOR color); +GB_COLOR COLOR_set_luminance(GB_COLOR color, int l); +int COLOR_invert_luminance(int l); + +#endif diff --git a/main/lib/image/gb.image.component b/main/lib/image/gb.image.component new file mode 100644 index 00000000..01ec1410 --- /dev/null +++ b/main/lib/image/gb.image.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.image +Name=Image buffer support +Author=Benoît Minisini +State=Stable diff --git a/main/lib/image/gb.image.h b/main/lib/image/gb.image.h new file mode 100644 index 00000000..7fe1735b --- /dev/null +++ b/main/lib/image/gb.image.h @@ -0,0 +1,173 @@ +/*************************************************************************** + + gb.image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_IMAGE_H +#define __GB_IMAGE_H + +#include "gambas.h" + +// Constants used by image data format + +#define GB_IMAGE_BGRX 0 // 00000 +#define GB_IMAGE_XRGB 1 // 00001 +#define GB_IMAGE_RGBX 2 // 00010 +#define GB_IMAGE_XBGR 3 // 00011 +#define GB_IMAGE_BGR 4 // 00100 +#define GB_IMAGE_RGB 5 // 00101 + +#define GB_IMAGE_BGRA 8 // 01000 +#define GB_IMAGE_ARGB 9 // 01001 +#define GB_IMAGE_RGBA 10 // 01010 +#define GB_IMAGE_ABGR 11 // 01011 + +#define GB_IMAGE_BGRP 24 // 11000 +#define GB_IMAGE_PRGB 25 // 11001 +#define GB_IMAGE_RGBP 26 // 11010 +#define GB_IMAGE_PBGR 27 // 11011 + +// Format test functions + +#define GB_IMAGE_FMT_IS_24_BITS(_format) ((_format) & 4) +#define GB_IMAGE_FMT_IS_32_BITS(_format) (((_format) & 4) == 0) + +#define GB_IMAGE_FMT_IS_RGBA(_format) ((_format) & 2) + +#define GB_IMAGE_FMT_IS_SWAPPED(_format) ((_format) & 1) + +#define GB_IMAGE_FMT_IS_TRANSPARENT(_format) ((_format) & 8) + +#define GB_IMAGE_FMT_IS_PREMULTIPLIED(_format) ((_format) & 16) +#define GB_IMAGE_FMT_SET_PREMULTIPLIED(_format) ((_format) | 16) +#define GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(_format) ((_format) & ~16) + +// Image owner information + +struct GB_IMG; + +typedef + struct { + const char *name; // owner name (this is the name of the component) + int format; // preferred format + void (*free)(struct GB_IMG *img, void *handle); // free owner handle + void (*release)(struct GB_IMG *img, void *handle); // free temporary handle + void *(*temp)(struct GB_IMG *img); // create a temporary handle for an image and returns it + void (*sync)(struct GB_IMG *img); // synchronize the data. Called only if the GB_IMG.sync flag is set + } + GB_IMG_OWNER; + +// Gambas image + +typedef + struct GB_IMG { + GB_BASE ob; + unsigned char *data; // points at the image data + int width; // image width in pixels + int height; // image height in pixels + int format; // image format (RGB, BGR, RGBA...) + GB_IMG_OWNER *owner; // owner of the data, NULL means gb.image + void *owner_handle; // handle for the owner + GB_IMG_OWNER *temp_owner; // owner of the temporary handle that does not own the data + void *temp_handle; // temporary handle + unsigned modified : 1; // data has been modified by gb.image + unsigned sync : 1; // data must be synchronized by calling GB_IMG_OWNER.sync() + unsigned is_void : 1; // void image (no data) + } + GB_IMG; + +#ifndef __GB_IMAGE_DEFINED +#define __GB_IMAGE_DEFINED +typedef + void *GB_IMAGE; +#endif + +// Pixel color: the color is not premultiplied, and the alpha component is inverted (0 = solid / 255 = transparent) + +#ifndef __GB_COLOR_DEFINED +#define __GB_COLOR_DEFINED +typedef + unsigned int GB_COLOR; +#endif + +// Split a color into its component. Uninvert the alpha component + +#define GB_COLOR_SPLIT(_color, _r, _g, _b, _a) \ +({ \ + uint _c = (uint)(_color); \ + _b = _c & 0xFF; \ + _g = (_c >> 8) & 0xFF; \ + _r = (_c >> 16) & 0xFF; \ + _a = (_c >> 24) ^ 0xFF; \ +}) + +// Create a GB_COLOR from rgba components + +#define GB_COLOR_MAKE(_r, _g, _b, _a) (((_b) & 0xFF) | (((_g) & 0xFF) << 8) | (((_r) & 0xFF) << 16) | ((((_a) & 0xFF) ^ 0xFF) << 24)) + +// Gambas image component interface + +#define IMAGE_INTERFACE_VERSION 1 + +typedef + struct { + intptr_t version; + // Create an image + GB_IMG *(*Create)(int width, int height, int format, unsigned char *data); + // Take image ownership by giving the image handle and information + void (*Take)(GB_IMG *img, GB_IMG_OWNER *owner, void *owner_handle, int width, int height, unsigned char *data); + // Create a temporary handle on the image without becoming the owner. + void *(*Check)(GB_IMG *img, GB_IMG_OWNER *temp_owner); + // Synchronize the image data if needed + void (*Synchronize)(GB_IMG *img); + // Return the size of the image data in bytes + int (*Size)(GB_IMG *img); + // Set the default format used when creating images + void (*SetDefaultFormat)(int format); + // Get the default format used when creating images + int (*GetDefaultFormat)(void); + // Get the color of a pixel + GB_COLOR (*GetPixel)(GB_IMG *img, int x, int y); + // Converts an image to one of the following formats: BGRA, RGBA, BGRP, RGBP + void (*Convert)(GB_IMG *img, int format); + // Merge two colors + GB_COLOR (*MergeColor)(GB_COLOR col1, GB_COLOR col2, double weight); + // Make a color lighter + GB_COLOR (*LighterColor)(GB_COLOR col); + // Make a color darker + GB_COLOR (*DarkerColor)(GB_COLOR col); + // Return the image format as a string + const char *(*FormatToString)(int format); + // Return the luminance of a color + int (*GetLuminance)(GB_COLOR col); + } + IMAGE_INTERFACE; + +#define GB_IMG_HANDLE(_image) ((_image)->temp_handle) + +#define SYNCHRONIZE_IMAGE(_image) (IMAGE.Synchronize(_image)) +#define MODIFY_IMAGE(_image) ((_image)->modified = 1) + +#define COLOR_DEFAULT ((GB_COLOR)-1) +#define GB_COLOR_DEFAULT ((GB_COLOR)-1) + +#endif + diff --git a/main/lib/image/image.c b/main/lib/image/image.c new file mode 100644 index 00000000..1798a671 --- /dev/null +++ b/main/lib/image/image.c @@ -0,0 +1,2452 @@ +/*************************************************************************** + + image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __IMAGE_C + +#include "c_color.h" +#include "image.h" + +bool IMAGE_debug = FALSE; + +typedef + struct { unsigned char d[3]; } PACKED uint24; + +typedef + struct { + int format; + const char *name; + } + FORMAT; + +//#define DEBUG_CONVERT +//#define DEBUG_ME 1 + +static int _default_format = GB_IMAGE_RGBA; + +static FORMAT _formats[] = +{ + { GB_IMAGE_BGRX, "BGRX" }, + { GB_IMAGE_XRGB, "XRGB" }, + { GB_IMAGE_RGBX, "RGBX" }, + { GB_IMAGE_XBGR, "XBGR" }, + { GB_IMAGE_BGR , "BGR" }, + { GB_IMAGE_RGB , "RGB" }, + + { GB_IMAGE_BGRA, "BGRA" }, + { GB_IMAGE_ARGB, "ARGB" }, + { GB_IMAGE_RGBA, "RGBA" }, + { GB_IMAGE_ABGR, "ABGR" }, + + { GB_IMAGE_BGRP, "BGRP" }, + { GB_IMAGE_PRGB, "PRGB" }, + { GB_IMAGE_RGBP, "RGBP" }, + { GB_IMAGE_PBGR, "PBGR" }, + { 0, NULL } +}; + +/*static inline unsigned char *GET_END_POINTER(GB_IMG *image) +{ + return &image->data[IMAGE_size(image)]; +}*/ + +#define GET_END_POINTER(_img) (&(_img)->data[IMAGE_size(_img)]) + +#define PREMUL(_x) \ +({ \ + uint x = (_x); \ + uint a = x >> 24; \ + \ + if (a == 0) \ + x = 0; \ + else if (a != 0xFF) \ + { \ + uint t = (x & 0xFF00FF) * a; \ + t = (t + ((t >> 8) & 0xFF00FF) + 0x800080) >> 8; \ + t &= 0xff00ff; \ + \ + x = ((x >> 8) & 0xff) * a; \ + x = (x + ((x >> 8) & 0xFF) + 0x80); \ + x &= 0xFF00; \ + x |= t | (a << 24); \ + } \ + x; \ +}) + +#define INV_PREMUL(__p) \ +({ \ + uint _p = (__p); \ + if (ALPHA(_p) == 0) \ + _p = 0; \ + else if (ALPHA(_p) != 0xFF) \ + _p = ((ALPHA(_p) << 24) \ + | (((255*RED(_p))/ ALPHA(_p)) << 16) \ + | (((255*GREEN(_p)) / ALPHA(_p)) << 8) \ + | ((255*BLUE(_p)) / ALPHA(_p))); \ + _p; \ +}) + +#define INV_SPREMUL(__p) \ +({ \ + uint _p = (__p); \ + if (SALPHA(_p) == 0) \ + _p = 0; \ + else if (SALPHA(_p) != 0xFF) \ + _p = ((SALPHA(_p) << 24) \ + | (((255*SRED(_p))/ SALPHA(_p)) << 16) \ + | (((255*SGREEN(_p)) / SALPHA(_p)) << 8) \ + | ((255*SBLUE(_p)) / SALPHA(_p))); \ + _p; \ +}) + +#define SWAP(__p) \ +({ \ + uint _p = (__p); \ + RGBA(ALPHA(_p), BLUE(_p), GREEN(_p), RED(_p)); \ +}) + +#define SWAP_RED_BLUE(__p) \ +({ \ + uint _p = (__p); \ + RGBA(BLUE(_p), GREEN(_p), RED(_p), ALPHA(_p)); \ +}) + +// Convert from GB_COLOR to a specific format + +static uint GB_COLOR_to_format(GB_COLOR col, int format) +{ + col ^= 0xFF000000; + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = PREMUL(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + return col; +} + +// Convert from a specific format to GB_COLOR + +static GB_COLOR GB_COLOR_from_format(uint col, int format) +{ + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = INV_PREMUL(col); + return col ^ 0xFF000000; +} + +// Convert from BGRA to a specific format + +static inline uint BGRA_to_format(uint col, int format) +{ + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = PREMUL(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + return col; +} + +// Convert from a specific format to BGRA + +static inline uint BGRA_from_format(uint col, int format) +{ + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = INV_PREMUL(col); + return col; +} + +// Convert from GB_COLOR to BGRA + +static inline uint GB_COLOR_to_BGRA(GB_COLOR col) +{ + return col ^ 0xFF000000; +} + +// Compose two BGRA colors + +static inline uint BGRA_compose(uint dst, uint src) +{ + unsigned char a = ALPHA(src); + if (a == 255) + return src; + else if (a == 0) + return dst; + else + { + unsigned char r = ((RED(src) - RED(dst)) * a) / 256 + RED(dst); + unsigned char g = ((GREEN(src) - GREEN(dst)) * a) / 256 + GREEN(dst); + unsigned char b = ((BLUE(src) - BLUE(dst)) * a) / 256 + BLUE(dst); + if (ALPHA(dst) > a) + a = ALPHA(dst); + return RGBA(r, g, b, a); + } +} + +static inline bool is_valid(GB_IMG *img, int x, int y) +{ + return !(x >= img->width || y >= img->height || x < 0 || y < 0); +} + +static void free_image(GB_IMG *img, void *image) +{ + //fprintf(stderr, "free_image: %p %p : %d\n", img, img->data, IMAGE_size(img)); + GB.Free(POINTER(&img->data)); +} + +static GB_IMG_OWNER _image_owner = { + "gb.image", + 0, + free_image, + free_image, + NULL, + NULL + }; + + +// Only converts to the following formats: +// - GB_IMAGE_BGRA +// - GB_IMAGE_BGRX +// - GB_IMAGE_RGBA +// - GB_IMAGE_RGBX + +static void convert_image(uchar *dst, int dst_format, uchar *src, int src_format, int w, int h) +{ + unsigned char *s = src; + unsigned char *d = dst; + int len; + unsigned char *dm; + uint *p, *pm; + bool psrc, pdst; + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: src_format = %d dst_format = %d\n", src_format, dst_format); + #endif + + len = w * h * sizeof(uint); + dm = &d[len]; + + psrc = GB_IMAGE_FMT_IS_PREMULTIPLIED(src_format); + pdst = GB_IMAGE_FMT_IS_PREMULTIPLIED(dst_format); + + /*if (psrc != pdst && GB_IMAGE_FMT_IS_SWAPPED(dst_format)) + { + p = (uint *)dst; + pm = (uint *)dm; + + if (psrc) + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: premultiplied -> normal\n"); + #endif + // convert premultiplied to normal + while (p != pm) + { + *p = INV_PREMUL(*p); + p++; + } + } + else + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: normal -> premultiplied\n"); + #endif + // convert normal to premultiplied + while (p != pm) + { + *p = PREMUL(*p); + p++; + } + } + }*/ + + src_format = GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(src_format); + dst_format = GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(dst_format); + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: after: src_format = %d dst_format = %d\n", src_format, dst_format); + #endif + + if (dst_format == GB_IMAGE_BGRA || dst_format == GB_IMAGE_BGRX) + { + switch (src_format) + { + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __0123; + + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __3210; + + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __2103; + + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __1230; + + case GB_IMAGE_BGR: + goto __012X; + + case GB_IMAGE_RGB: + goto __210X; + } + } + else if (dst_format == GB_IMAGE_RGBA || dst_format == GB_IMAGE_RGBX) + { + switch (src_format) + { + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __0123; + + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __3210; + + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __2103; + + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __1230; + + case GB_IMAGE_RGB: + goto __012X; + + case GB_IMAGE_BGR: + goto __210X; + } + } + else if (dst_format == GB_IMAGE_ARGB || dst_format == GB_IMAGE_XRGB) + { + switch (src_format) + { + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __0123; + + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __3210; + + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __0321; + + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __3012; + + case GB_IMAGE_RGB: + goto __012X; + + case GB_IMAGE_BGR: + goto __210X; + } + } + else if (dst_format == GB_IMAGE_ABGR || dst_format == GB_IMAGE_XBGR) + { + switch (src_format) + { + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __0123; + + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __3210; + + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __0321; + + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __3012; + + case GB_IMAGE_BGR: + goto __012X; + + case GB_IMAGE_RGB: + goto __210X; + } + } + +__0123: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 0123\n"); + #endif + memcpy(dst, src, len); + goto __PREMULTIPLIED; + +__3210: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 3210\n"); + #endif + while (d != dm) + { + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__0321: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 0321\n"); + #endif + while (d != dm) + { + d[0] = s[0]; + d[1] = s[3]; + d[2] = s[2]; + d[3] = s[1]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__2103: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 2103\n"); + #endif + + while (d != dm) + { + d[0] = s[2]; + d[1] = s[1]; + d[2] = s[0]; + d[3] = s[3]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__3012: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 3012\n"); + #endif + + while (d != dm) + { + d[0] = s[3]; + d[1] = s[0]; + d[2] = s[1]; + d[3] = s[2]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__1230: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 1230\n"); + #endif + + while (d != dm) + { + d[0] = s[1]; + d[1] = s[2]; + d[2] = s[3]; + d[3] = s[0]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__012X: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 012X\n"); + #endif + + while (d != dm) + { + d[0] = s[0]; + d[1] = s[1]; + d[2] = s[2]; + d[3] = 0xFF; + s += 3; + d += 4; + } + return; + +__210X: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 210X\n"); + #endif + + while (d != dm) + { + d[0] = s[2]; + d[1] = s[1]; + d[2] = s[0]; + d[3] = 0xFF; + s += 3; + d += 4; + } + return; + +__PREMULTIPLIED: + + if (psrc != pdst && !GB_IMAGE_FMT_IS_SWAPPED(dst_format)) + { + p = (uint *)dst; + pm = (uint *)dm; + + if (psrc) + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: premultiplied -> normal\n"); + #endif + // convert premultiplied to normal + while (p != pm) + { + *p = INV_PREMUL(*p); + p++; + } + } + else + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: normal -> premultiplied\n"); + #endif + // convert normal to premultiplied + while (p != pm) + { + *p = PREMUL(*p); + p++; + } + } + } +} + +#define SYNCHRONIZE(_img) ({ if ((_img)->sync && (_img)->temp_owner) (*(_img)->temp_owner->sync)(_img); }) +#define MODIFY(_img) ((_img)->modified = TRUE) + +int IMAGE_size(GB_IMG *img) +{ + return img->width * img->height * (GB_IMAGE_FMT_IS_24_BITS(img->format) ? 3 : 4); +} + +void IMAGE_create(GB_IMG *img, int width, int height, int format, GB_COLOR fill) +{ + GB_BASE save = img->ob; + CLEAR(img); + img->ob = save; + img->owner = &_image_owner; + + if (width <= 0 || height <= 0) + { + img->is_void = TRUE; + return; + } + + img->width = width; + img->height = height; + img->format = format; + if (fill == 0) + GB.AllocZero(POINTER(&img->data), IMAGE_size(img)); + else + GB.Alloc(POINTER(&img->data), IMAGE_size(img)); + img->owner_handle = img->data; + + if (fill && fill != GB_COLOR_DEFAULT) + IMAGE_fill(img, fill); + + #ifdef DEBUG_ME + fprintf(stderr, "IMAGE_create: %p\n", img); + #endif +} + + +void IMAGE_create_with_data(GB_IMG *img, int width, int height, int format, unsigned char *data) +{ + IMAGE_create(img, width, height, format, GB_COLOR_DEFAULT); + if (data && !IMAGE_is_void(img)) + memcpy(img->data, data, IMAGE_size(img)); +} + +const char *IMAGE_format_to_string(int fmt) +{ + FORMAT *pf; + + for (pf = _formats; pf->name; pf++) + { + if (fmt == pf->format) + return pf->name; + } + + return NULL; +} + +int IMAGE_format_from_string(char *fmt) +{ + FORMAT *pf; + + for (pf = _formats; pf->name; pf++) + { + if (strcmp(fmt, pf->name) == 0) + return pf->format; + } + + return -1; +} + +void IMAGE_convert(GB_IMG *img, int dst_format) +{ + uchar *data; + int src_format = img->format; + + if (src_format == dst_format) + return; + + //IMAGE_create(&tmp, img->width, img->height, format); + img->format = dst_format; + if (IMAGE_is_void(img)) + return; + + if (IMAGE_debug) + fprintf(stderr, "gb.image: convert: %s -> %s\n", IMAGE_format_to_string(src_format), IMAGE_format_to_string(dst_format)); + + GB.Alloc(POINTER(&data), IMAGE_size(img)); + convert_image(data, dst_format, img->data, src_format, img->width, img->height); + //GB.Free(POINTER(&img->data)); + IMAGE_take(img, &_image_owner, data, img->width, img->height, data); +} + +// Check if a temporary handle is needed, and create it if needed by calling the owner "temp" function + +void *IMAGE_check(GB_IMG *img, GB_IMG_OWNER *temp_owner) +{ + if (!img) + return NULL; + + // If we already have the temporary handle, then do nothing + if (img->temp_owner == temp_owner) + return img->temp_handle; + + #ifdef DEBUG_ME + fprintf(stderr, "IMAGE_check: %p: %s (%p) / %s (%p) -> %s\n", + img, + img->owner->name, img->owner_handle, + img->temp_owner ? img->temp_owner->name : "NULL", img->temp_handle, + temp_owner ? temp_owner->name : "NULL"); + #endif + + // If somebody else has a temporary handle + if (img->temp_owner) + { + // release it only if it is not the owner + if (img->temp_owner != img->owner && img->temp_owner->release) + (*img->temp_owner->release)(img, img->temp_handle); + img->temp_handle = 0; + img->temp_owner = NULL; + } + + // Get the temporary handle + if (temp_owner) + { + // If we are the owner, we must use our owner handle as temporary handle + if (img->owner == temp_owner) + img->temp_handle = img->owner_handle; + // If we are not the owner, then we will have to create the temporary handle ourself + else + { + // Synchronize the image if needed + SYNCHRONIZE(img); + // Conversion can make gb.image the new owner and temporary owner + IMAGE_convert(img, temp_owner->format); + img->temp_handle = (*temp_owner->temp)(img); + } + } + + // Become the temporary owner + img->temp_owner = temp_owner; + + #ifdef DEBUG_ME + fprintf(stderr, "==========>: %p: %s (%p) / %s (%p)\n", + img, + img->owner->name, img->owner_handle, + img->temp_owner ? img->temp_owner->name : "NULL", img->temp_handle); + #endif + + return img->temp_handle; +} + +// Take ownership of the image +void IMAGE_take(GB_IMG *img, GB_IMG_OWNER *owner, void *owner_handle, int width, int height, unsigned char *data) +{ + if (!img) + return; + + // If we are already the owner with the same handle, then do nothing + if (img->owner == owner && img->owner_handle == owner_handle) + return; + + // Release the old owner + //fprintf(stderr, "releasing image %p owned by %s\n", img, img->owner->name); + (*img->owner->free)(img, img->owner_handle); + + // If we have the temporary handle too, then clean it as it is necessarily the same + if (img->temp_owner == img->owner) + { + img->temp_owner = NULL; + img->temp_handle = 0; + } + + // Become the owner + img->owner = owner; + img->owner_handle = owner_handle; + + // As we are now the owner, then we must have the temporary handle too + IMAGE_check(img, NULL); + img->temp_owner = owner; + img->temp_handle = owner_handle; + + // Initialize the data + img->width = width; + img->height = height; + img->data = data; + if (owner && owner->format) + img->format = owner->format; + img->is_void = width <= 0 || height <= 0; +} + +void IMAGE_delete(GB_IMG *img) +{ + #ifdef DEBUG_ME + fprintf(stderr, "IMAGE_delete: %p\n", img); + #endif + //IMAGE_take(img, &_image_owner, NULL, 0, 0, NULL); + + // Release the temporary handle before the owner, because the temporary owner may write to the data before freeing! + + if (img->temp_owner && img->temp_owner != img->owner && img->temp_handle) + (*img->temp_owner->release)(img, img->temp_handle); + + if (!IMAGE_is_void(img)) + (*img->owner->free)(img, img->owner_handle); + + img->width = img->height = 0; + img->format = 0; + img->temp_owner = NULL; + img->temp_handle = NULL; + img->owner = &_image_owner; + img->owner_handle = NULL; + img->is_void = TRUE; +} + +void IMAGE_synchronize(GB_IMG *img) +{ + SYNCHRONIZE(img); +} + +#define GET_POINTER(_img, _p, _pm) \ + uint *_p = (uint *)(_img)->data; \ + uint *_pm = (uint *)GET_END_POINTER(_img); \ + if ((_img)->is_void) return; + +void IMAGE_fill(GB_IMG *img, GB_COLOR col) +{ + GET_POINTER(img, p, pm); + + //SYNCHRONIZE(img); unneeded, as the entire image will be replaced + + col = GB_COLOR_to_format(col, img->format); + //fprintf(stderr, "fill with %08X\n", col); + while (p != pm) + *p++ = col; + + MODIFY(img); +} + +// GB_IMAGE_RGB[APX] only +void IMAGE_make_gray(GB_IMG *img) +{ + GET_POINTER(img, p, pm); + uint col; + uchar g; + int format = img->format; + + SYNCHRONIZE(img); + + while (p != pm) + { + col = BGRA_from_format(*p, format); + g = GRAY(col); + + *p++ = BGRA_to_format(RGBA(g, g, g, ALPHA(col)), format); + } + + MODIFY(img); +} + +GB_COLOR IMAGE_get_pixel(GB_IMG *img, int x, int y) +{ + uint col; + + if (!is_valid(img, x, y)) + return (-1); + + SYNCHRONIZE(img); + col = ((uint *)img->data)[y * img->width + x]; + return GB_COLOR_from_format(col, img->format); +} + +void IMAGE_get_pixels(GB_IMG *img, int *data) +{ + SYNCHRONIZE(img); + + memcpy(data, img->data, img->width * img->height * sizeof(int)); +} + +void IMAGE_set_pixel(GB_IMG *img, int x, int y, GB_COLOR col) +{ + if (!is_valid(img, x, y)) + return; + + SYNCHRONIZE(img); + ((uint *)img->data)[y * img->width + x] = GB_COLOR_to_format(col, img->format); + MODIFY(img); +} + +void IMAGE_set_pixels(GB_IMG *img, int *data) +{ + SYNCHRONIZE(img); + memcpy(img->data, data, img->width * img->height * sizeof(int)); + MODIFY(img); +} + +void IMAGE_fill_rect(GB_IMG *img, int x, int y, int w, int h, GB_COLOR col, bool opaque) +{ + uint *p; + int i; + uint c; + int format = img->format; + + if (x >= img->width || y >= img->height) return; + + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + if ((x + w) > img->width) { w = img->width - x; } + if ((y + h) > img->height) { h = img->height - y; } + + if (w <= 0 || h <= 0) return; + + SYNCHRONIZE(img); + + p = &((uint *)img->data)[y * img->width + x]; + + c = GB_COLOR_to_BGRA(col); + + if (opaque || ALPHA(c) == 255) + { + c = BGRA_to_format(c, format); + while (h) + { + for(i = w; i; i--) + *p++ = c; + h--; + p += img->width - w; + } + } + else + { + while (h) + { + for(i = w; i; i--, p++) + *p = BGRA_to_format(BGRA_compose(BGRA_from_format(*p, format), c), format); + h--; + p += img->width - w; + } + } + + MODIFY(img); +} + +void IMAGE_replace(GB_IMG *img, GB_COLOR src, GB_COLOR dst, bool noteq) +{ + GET_POINTER(img, p, pm); + + src = GB_COLOR_to_format(src, img->format); + dst = GB_COLOR_to_format(dst, img->format); + + SYNCHRONIZE(img); + + if (noteq) + { + while (p != pm) + { + if (*p != src) + *p = dst; + p++; + } + } + else + { + while (p != pm) + { + if (*p == src) + *p = dst; + p++; + } + } + + MODIFY(img); +} + +// Comes from the GIMP + +typedef + struct { + float r; + float b; + float g; + float a; + } + FLOAT_RGB; + +static void color_to_alpha(FLOAT_RGB *src, const FLOAT_RGB *color) +{ + FLOAT_RGB alpha; + + alpha.a = src->a; + + if (color->r < 0.0001) + alpha.r = src->r; + else if (src->r > color->r) + alpha.r = (src->r - color->r) / (1.0 - color->r); + else if (src->r < color->r) + alpha.r = (color->r - src->r) / color->r; + else alpha.r = 0.0; + + if (color->g < 0.0001) + alpha.g = src->g; + else if (src->g > color->g) + alpha.g = (src->g - color->g) / (1.0 - color->g); + else if (src->g < color->g) + alpha.g = (color->g - src->g) / (color->g); + else alpha.g = 0.0; + + if (color->b < 0.0001) + alpha.b = src->b; + else if (src->b > color->b) + alpha.b = (src->b - color->b) / (1.0 - color->b); + else if (src->b < color->b) + alpha.b = (color->b - src->b) / (color->b); + else alpha.b = 0.0; + + if (alpha.r > alpha.g) + { + if (alpha.r > alpha.b) + src->a = alpha.r; + else + src->a = alpha.b; + } + else if (alpha.g > alpha.b) + { + src->a = alpha.g; + } + else + { + src->a = alpha.b; + } + + if (src->a < 0.0001) + return; + + src->r = (src->r - color->r) / src->a + color->r; + src->g = (src->g - color->g) / src->a + color->g; + src->b = (src->b - color->b) / src->a + color->b; + + src->a *= alpha.a; +} + +void IMAGE_make_transparent(GB_IMG *img, GB_COLOR col) +{ + uint color; + FLOAT_RGB rgb_color; + FLOAT_RGB rgb_src; + int format = img->format; + GET_POINTER(img, p, pm); + //uint *p = (uint *)img->data; + //uint *pm = (uint *)(img->data + IMAGE_size(img)); + + //fprintf(stderr, "IMAGE_make_transparent: %d x %d / %d\n", img->width, img->height, img->format); + + SYNCHRONIZE(img); + + color = GB_COLOR_to_BGRA(col); + rgb_color.b = BLUE(color) / 255.0; + rgb_color.g = GREEN(color) / 255.0; + rgb_color.r = RED(color) / 255.0; + rgb_color.a = 1.0; + + while (p != pm) + { + color = BGRA_from_format(*p, format); + rgb_src.b = BLUE(color) / 255.0; + rgb_src.g = GREEN(color) / 255.0; + rgb_src.r = RED(color) / 255.0; + rgb_src.a = ALPHA(color) / 255.0; + + color_to_alpha(&rgb_src, &rgb_color); + + color = RGBA( + (unsigned char)(255.0 * rgb_src.r + 0.5), + (unsigned char)(255.0 * rgb_src.g + 0.5), + (unsigned char)(255.0 * rgb_src.b + 0.5), + (unsigned char)(255.0 * rgb_src.a + 0.5) + ); + + *p = BGRA_to_format(color, format); + //fprintf(stderr, "[%d] %08X\n", p - (uint *)img->data, *p); + p++; + } + + MODIFY(img); + //fprintf(stderr, "IMAGE_make_transparent: ** DONE **\n"); +} + + +void IMAGE_set_default_format(int format) +{ + _default_format = format; //GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(format); +} + +int IMAGE_get_default_format() +{ + return _default_format; +} + +// Parameter correction +#define CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh) \ + if ( sw < 0 ) sw = src->width; \ + if ( sh < 0 ) sh = src->height; \ + if (dw < 0) dw = sw; \ + if (dh < 0) dh = sh; \ + if (dw != sw || dh != sh) \ + { \ + GB.Error("Stretching images is not implemented in gb.image"); \ + return; \ + } \ + if ( sx < 0 ) { dx -= sx; dw += sx; sw += sx; sx = 0; } \ + if ( sy < 0 ) { dy -= sy; dh += sy; sh += sy; sy = 0; } \ + if ( dx < 0 ) { sx -= dx; sw += dx; dx = 0; } \ + if ( dy < 0 ) { sy -= dy; sh += dy; dy = 0; } \ + if ( (sx + sw) > src->width ) sw = src->width - sx; \ + if ( (sy + sh) > src->height ) sh = src->height - sy; \ + if ( (dx + sw) > dst->width ) sw = dst->width - dx; \ + if ( (dy + sh) > dst->height ) sh = dst->height - dy; \ + if (sw <= 0 || sh <= 0) \ + return; + +void IMAGE_bitblt(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh) +{ + int sfmt = src->format; + int dfmt = dst->format; + + CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh); + + SYNCHRONIZE(src); + SYNCHRONIZE(dst); + + uint *d = (uint *)dst->data + dy * dst->width + dx; + uint *s = (uint *)src->data + sy * src->width + sx; + + if (GB_IMAGE_FMT_IS_32_BITS(sfmt) && GB_IMAGE_FMT_IS_32_BITS(dfmt)) + { + if (sfmt != dfmt) + { + const int dd = dst->width - sw; + const int ds = src->width - sw; + int t; + while (sh--) + { + for (t = sw; t--;) + { + *d = BGRA_to_format(BGRA_from_format(*s, sfmt), dfmt); + d++; + s++; + } + + d += dd; + s += ds; + } + } + else if (sw < 64) + { + const int dd = dst->width - sw; + const int ds = src->width - sw; + int t; + while (sh--) + { + for (t = sw; t--;) + *d++ = *s++; + d += dd; + s += ds; + } + } + else + { + // Trust libc + const int dd = dst->width; + const int ds = src->width; + const int b = sw * sizeof(uint); + while (sh--) + { + memcpy(d, s, b); + d += dd; + s += ds; + } + } + } + else if (GB_IMAGE_FMT_IS_24_BITS(sfmt) && GB_IMAGE_FMT_IS_24_BITS(sfmt)) + { + char *d = (char *)dst->data + (dy * dst->width + dx) * 3; + char *s = (char *)src->data + (sy * src->width + sx) * 3; + const int dd = dst->width * 3; + const int ds = src->width * 3; + const int b = sw * 3; + + while (sh--) + { + memcpy(d, s, b); + d += dd; + s += ds; + } + } + else + { + GB.Error("The pixel size of both images must be the same"); + } + + MODIFY(dst); +} + +void IMAGE_set_opacity(GB_IMG *dst, uchar opacity) +{ + if (!GB_IMAGE_FMT_IS_32_BITS(dst->format)) + { + GB.Error("The image must have an alpha channel"); + return; + } + + if (opacity == 255) + return; + + SYNCHRONIZE(dst); + + GET_POINTER(dst, p, pm); + + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(dst->format)) + { + uint *pp = p; + while (pp != pm) + { + *pp = INV_PREMUL(*pp); + pp++; + } + } + + uchar *d = (uchar *)p; + uchar *dm = (uchar *)pm; + + if (!GB_IMAGE_FMT_IS_SWAPPED(dst->format)) + { + d += 3; + dm += 3; + } + + if (opacity == 0) + { + while (d != dm) + { + *d = 0; + d += 4; + } + } + else + { + uchar da[256]; + int i; + + for (i = 0; i < 256; i++) + da[i] = i * opacity >> 8; + + while (d != dm) + { + *d = da[*d]; + d += 4; + } + } + + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(dst->format)) + { + uint *pp = p; + while (pp != pm) + { + *pp = PREMUL(*pp); + pp++; + } + } + + MODIFY(dst); +} + +void IMAGE_draw_alpha(GB_IMG *dst, int dx, int dy, GB_IMG *src, int sx, int sy, int sw, int sh) +{ + if (!GB_IMAGE_FMT_IS_32_BITS(src->format) || !GB_IMAGE_FMT_IS_32_BITS(dst->format)) + { + GB.Error("The images must have an alpha channel"); + return; + } + + CHECK_PARAMETERS(dst, dx, dy, sw, sh, src, sx, sy, sw, sh); + + SYNCHRONIZE(src); + SYNCHRONIZE(dst); + + uchar *d = (uchar *)((uint *)dst->data + dy * dst->width + dx); + uchar *s = (uchar *)((uint *)src->data + sy * src->width + sx); + + const int dd = (dst->width - sw) * 4; + const int ds = (src->width - sw) * 4; + //uint cs, cd; + int sformat = src->format; + int dformat = dst->format; + int t; + + if (!GB_IMAGE_FMT_IS_SWAPPED(sformat)) + s += 3; + if (!GB_IMAGE_FMT_IS_SWAPPED(dformat)) + d += 3; + + while (sh--) + { + for (t = sw; t--; d += 4,s += 4) + { + if (*s < *d) + *d = *s; + } + + d += dd; + s += ds; + } + + MODIFY(dst); +} + +void IMAGE_compose(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh) +{ + if (dst->format != src->format) + { + GB.Error("The images must have the same format"); + return; + } + + CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh); + + SYNCHRONIZE(src); + SYNCHRONIZE(dst); + + switch(src->format) + { + case GB_IMAGE_RGBA: case GB_IMAGE_BGRA: + { + #if 0 + uint *d = (uint *)dst->data + dy * dst->width + dx; + uint *s = (uint *)src->data + sy * src->width + sx; + + const int dd = dst->width - sw; + const int ds = src->width - sw; + int t; + while (sh--) + { + for (t = sw; t--;) + { + unsigned char a = ALPHA(*s); + if (a == 255) + *d = *s; + else if (a) + { + unsigned char r = ((RED(*s)-RED(*d)) * a) / 256 + RED(*d); + unsigned char g = ((GREEN(*s)-GREEN(*d)) * a) / 256 + GREEN(*d); + unsigned char b = ((BLUE(*s)-BLUE(*d)) * a) / 256 + BLUE(*d); + if (ALPHA(*d) > a) + a = ALPHA(*d); + *d = RGBA(r,g,b,a); + } + d++; + s++; + } + d += dd; + s += ds; + } + #else + uchar *d = (uchar *)((uint *)dst->data + dy * dst->width + dx); + uchar *s = (uchar *)((uint *)src->data + sy * src->width + sx); + + const int dd = (dst->width - sw) * 4; + const int ds = (src->width - sw) * 4; + int t; + while (sh--) + { + for (t = sw; t--;) + { + unsigned char a = s[3]; + if (a == 255) + *(uint *)d = *(uint *)s; + else if (a) + { + d[0] = ((s[0] - d[0]) * a) / 256 + d[0]; + d[1] = ((s[1] - d[1]) * a) / 256 + d[1]; + d[2] = ((s[2] - d[2]) * a) / 256 + d[2]; + if (d[3] > a) + d[3] = a; + } + d += 4; + s += 4; + } + d += dd; + s += ds; + } + #endif + + break; + } + + default: + GB.Error("Unsupported image format"); + return; + } + + MODIFY(dst); +} + +void IMAGE_colorize(GB_IMG *img, GB_COLOR color) +{ + GET_POINTER(img, p, pm); + uint col; + int h, s, v; + int r, g, b; + int hcol, scol, vcol; + int format = img->format; + uchar vcolmul[256]; + int i; + + SYNCHRONIZE(img); + + col = GB_COLOR_to_BGRA(color); + COLOR_rgb_to_hsv(RED(col), GREEN(col), BLUE(col), &hcol, &scol, &vcol); + + for (i = 0; i < 256; i++) + vcolmul[i] = vcol * i / 255; + + while (p != pm) + { + col = BGRA_from_format(*p, format); + COLOR_rgb_to_hsv(RED(col), GREEN(col), BLUE(col), &h, &s, &v); + COLOR_hsv_to_rgb(hcol, scol, vcolmul[v], &r, &g, &b); + *p++ = BGRA_to_format(RGBA(r, g, b, ALPHA(col)), img->format); + } + + MODIFY(img); +} + +void IMAGE_mask(GB_IMG *img, GB_COLOR color) +{ + GET_POINTER(img, p, pm); + uint col; + unsigned char red[256], blue[256], green[256], alpha[256]; + int i, r, g, b, a; + int format = img->format; + + SYNCHRONIZE(img); + + col = GB_COLOR_to_format(color, img->format); + r = RED(col); + g = GREEN(col); + b = BLUE(col); + a = ALPHA(col); + + for (i = 0; i < 256; i++) + { + red[i] = i * r / 255; + green[i] = i * g / 255; + blue[i] = i * b / 255; + alpha[i] = i * a / 255; + } + + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(red[RED(col)], green[GREEN(col)], blue[BLUE(col)], alpha[ALPHA(col)]), format); + } + + MODIFY(img); +} + +void IMAGE_mirror(GB_IMG *src, GB_IMG *dst, bool horizontal, bool vertical) +{ + if (dst->width != src->width || dst->height != src->height || dst->format != src->format || IMAGE_is_void(src)) + return; + + int w = src->width; + int h = src->height; + + int dxi = horizontal ? -1 : 1; + int dxs = horizontal ? (w - 1) : 0; + int dyi = vertical ? -1 : 1; + int dy = vertical ? (h - 1) : 0; + int sx, sy; + + SYNCHRONIZE(src); + + if (GB_IMAGE_FMT_IS_24_BITS(src->format)) + { + for (sy = 0; sy < h; sy++, dy += dyi) + { + uint24 *ssl = (uint24 *)(src->data + sy * src->width * 3); + uint24 *dsl = (uint24 *)(dst->data + dy * dst->width * 3); + int dx = dxs; + for (sx = 0; sx < w; sx++, dx += dxi) + dsl[dx] = ssl[sx]; + } + } + else + { + for (sy = 0; sy < h; sy++, dy += dyi) + { + uint *ssl = (uint *)(src->data + sy * src->width * 4); + uint *dsl = (uint *)(dst->data + dy * dst->width * 4); + int dx = dxs; + for (sx = 0; sx < w; sx++, dx += dxi) + dsl[dx] = ssl[sx]; + } + } + + MODIFY(dst); +} + +void IMAGE_rotate(GB_IMG *src, GB_IMG *dst, bool left) +{ + if (dst->width != src->height || dst->width != src->height || dst->format != src->format || IMAGE_is_void(src)) + return; + + int x, y; + int w = dst->width; + int h = dst->height; + + SYNCHRONIZE(src); + + if (GB_IMAGE_FMT_IS_24_BITS(src->format)) + { + uint24 *pd = (uint24 *)dst->data; + + if (left) + { + + for (y = 0; y < h; y++) + { + uint24 *ps = (uint24 *)(src->data + (h - 1 - y) * 3); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps += h; + } + } + } + else + { + for (y = 0; y < h; y++) + { + uint24 *ps = (uint24 *)(src->data + ((w - 1) * h + y) * 3); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps -= h; + } + } + } + } + else + { + uint *pd = (uint *)dst->data; + + if (left) + { + for (y = 0; y < h; y++) + { + uint *ps = (uint *)(src->data + (h - 1 - y) * 4); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps += h; + } + } + } + else + { + for (y = 0; y < h; y++) + { + uint *ps = (uint *)(src->data + ((w - 1) * h + y) * 4); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps -= h; + } + } + } + } + + MODIFY(dst); +} + +#if 0 +#define GET_RGBA(_col, _r, _g, _b, _a) { _r = RED(_col); _g = GREEN(_col); _b = BLUE(_col); _a = ALPHA(_col); } + +void IMAGE_transform(GB_IMG *dst, GB_IMG *src, double sx, double sy, double sdx, double sdy) +{ + int x, y; + double ssx; + double ssy; + uint col, cdp; + int ix, iy; + uchar r, g, b, a; + uchar rc, gc, bc, ac; + int wx, wy; + + uint *dp = (uint *)dst->data; + memset(dp, 0, IMAGE_size(dst)); + + dp += dst->width; + + for (y = 1; y < (dst->height - 1); y++) + { + ssx = sx; + ssy = sy; + + dp++; + for (x = 1; x < (dst->width - 1); x++) + { + ix = sx; + iy = sy; + + if (is_valid(src, ix, iy)) + { + col = BGRA_from_format(*((uint *)src->data + iy * src->width + ix), src->format); + + //dp[1] = RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, ALPHA(col) * wx / 256); + + wx = 128; + + cdp = BGRA_from_format(*dp, dst->format); + dp[0] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col)); + + wx = 32; + + cdp = BGRA_from_format(dp[-1], dst->format); + dp[-1] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col)); + + cdp = BGRA_from_format(dp[-dst->width], dst->format); + dp[-dst->width] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col)); + + dp[1] = RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, 0); + + dp[dst->width] = dp[1]; //RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, ALPHA(col) * wx / 256); + } + + sx += sdx; + sy += sdy; + dp++; + } + dp++; + sx = ssx - sdy; + sy = ssy + sdx; + } +} +#endif + +/* +StackBlur - a fast almost Gaussian Blur For Canvas + +Version: 0.5 +Author: Mario Klingemann +Contact: mario@quasimondo.com +Website: http://www.quasimondo.com/StackBlurForCanvas +Twitter: @quasimondo + +In case you find this class useful - especially in commercial projects - +I am not totally unhappy for a small donation to my PayPal account +mario@quasimondo.de + +Or support me on flattr: +https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript + +Copyright (c) 2010 Mario Klingemann + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +*/ + +static const short mul_table[] = { + 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, + 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, + 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, + 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, + 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, + 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, + 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, + 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, + 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, + 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, + 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, + 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, + 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, + 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, + 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, + 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259 +}; + + +static const char shg_table[] = { + 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 +}; + +typedef + struct _BLUR_STACK { + uchar r, g, b, a; + struct _BLUR_STACK *next; + } + BLUR_STACK; + +void IMAGE_blur(GB_IMG *img, int radius) //top_x, top_y, width, height, radius ) +{ + if (radius < 1) + return; + + if (radius >= 255) + radius = 254; + + if (img->is_void) + return; + + SYNCHRONIZE(img); + + uchar *pixels = (uchar *)img->data; + int width = img->width; + int height = img->height; + + uint x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, + r_out_sum, g_out_sum, b_out_sum, a_out_sum, + r_in_sum, g_in_sum, b_in_sum, a_in_sum, rbs; + + uchar pr, pg, pb, pa; + + uint div = radius + radius + 1; + uint widthMinus1 = width - 1; + uint heightMinus1 = height - 1; + uint radiusPlus1 = radius + 1; + uint sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; + + BLUR_STACK *stackStart, *stackEnd, *stack; + + stackStart = alloca(sizeof(BLUR_STACK) * div); + + stack = stackStart; + stackEnd = stackStart + radiusPlus1; + + for (i = 1; i < div; i++) + { + stack->next = stack + 1; + stack++; + } + + stack->next = stackStart; + + BLUR_STACK *stackIn = NULL; + BLUR_STACK *stackOut = NULL; + + yw = yi = 0; + + uint mul_sum = mul_table[radius]; + uint shg_sum = shg_table[radius]; + + //fprintf(stderr, "blur: format = %d / %02X%02X%02X%02X\n", img->format, pixels[0], pixels[1], pixels[2], pixels[3]); + + if (GB_IMAGE_FMT_IS_32_BITS(img->format)) + { + for (y = 0; y < height; y++) + { + r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + pa = pixels[yi + 3]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + a_out_sum = radiusPlus1 * pa; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + stack = stack->next; + } + + for (i = 1; i < radiusPlus1; i++) + { + p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); + rbs = radiusPlus1 - i; + + pr = pixels[p]; + pg = pixels[p + 1]; + pb = pixels[p + 2]; + pa = pixels[p + 3]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + a_sum += pa * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + + stack = stack->next; + } + + + stackIn = stackStart; + stackOut = stackEnd; + for (x = 0; x < width; x++) + { + pixels[yi] = (r_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 3] = (a_sum * mul_sum) >> shg_sum; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + a_out_sum -= stackIn->a; + + p = x + radius + 1; + p = ( yw + ( p < widthMinus1 ? p : widthMinus1 ) ) << 2; + + r_in_sum += pixels[p]; + g_in_sum += pixels[p + 1]; + b_in_sum += pixels[p + 2]; + a_in_sum += pixels[p + 3]; + + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + a_sum += a_in_sum; + + stackIn->r = pixels[p]; + stackIn->g = pixels[p + 1]; + stackIn->b = pixels[p + 2]; + stackIn->a = pixels[p + 3]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + a_out_sum += ( pa = stackOut->a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut->next; + + yi += 4; + } + yw += width; + } + + for (x = 0; x < width; x++) + { + g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; + + yi = x << 2; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + pa = pixels[yi + 3]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + a_out_sum = radiusPlus1 * pa; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + stack = stack->next; + } + + yp = width; + + for (i = 1; i <= radius; i++) + { + yi = ( yp + x ) << 2; + + rbs = radiusPlus1 - i; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + pa = pixels[yi + 3]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + a_sum += pa * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + stack = stack->next; + + if (i < heightMinus1) + yp += width; + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (y = 0; y < height; y++) + { + p = yi << 2; + + pixels[p] = (r_sum * mul_sum) >> shg_sum; + pixels[p + 1] = (g_sum * mul_sum) >> shg_sum; + pixels[p + 2] = (b_sum * mul_sum) >> shg_sum; + pixels[p + 3] = (a_sum * mul_sum) >> shg_sum; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + a_out_sum -= stackIn->a; + + p = y + radiusPlus1; + p = ( x + (( p < heightMinus1 ? p : heightMinus1 ) * width )) << 2; + + r_sum += ( r_in_sum += pixels[p]); + g_sum += ( g_in_sum += pixels[p+1]); + b_sum += ( b_in_sum += pixels[p+2]); + a_sum += ( a_in_sum += pixels[p+3]); + + stackIn->r = pixels[p]; + stackIn->g = pixels[p+1]; + stackIn->b = pixels[p+2]; + stackIn->a = pixels[p+3]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + a_out_sum += ( pa = stackOut->a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut->next; + + yi += width; + } + } + } + else // 24 bits ================================= + { + for (y = 0; y < height; y++) + { + r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack = stack->next; + } + + for (i = 1; i < radiusPlus1; i++) + { + p = yi + (( widthMinus1 < i ? widthMinus1 : i ) * 3 ); + rbs = radiusPlus1 - i; + + pr = pixels[p]; + pg = pixels[p + 1]; + pb = pixels[p + 2]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + + stack = stack->next; + } + + + stackIn = stackStart; + stackOut = stackEnd; + for (x = 0; x < width; x++) + { + pixels[yi] = (r_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum; // * 255 / pa; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + + p = x + radius + 1; + p = ( yw + ( p < widthMinus1 ? p : widthMinus1 ) ) * 3; + + r_in_sum += pixels[p]; + g_in_sum += pixels[p + 1]; + b_in_sum += pixels[p + 2]; + + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + + stackIn->r = pixels[p]; + stackIn->g = pixels[p + 1]; + stackIn->b = pixels[p + 2]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + + stackOut = stackOut->next; + + yi += 3; + } + yw += width; + } + + for (x = 0; x < width; x++) + { + g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0; + + yi = x << 2; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack = stack->next; + } + + yp = width; + + for (i = 1; i <= radius; i++) + { + yi = ( yp + x ) << 2; + + rbs = radiusPlus1 - i; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack = stack->next; + + if (i < heightMinus1) + yp += width; + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (y = 0; y < height; y++) + { + p = yi << 2; + + pixels[p] = (r_sum * mul_sum) >> shg_sum; + pixels[p + 1] = (g_sum * mul_sum) >> shg_sum; + pixels[p + 2] = (b_sum * mul_sum) >> shg_sum; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + + p = y + radiusPlus1; + p = ( x + (( p < heightMinus1 ? p : heightMinus1 ) * width )) * 3; + + r_sum += ( r_in_sum += pixels[p]); + g_sum += ( g_in_sum += pixels[p+1]); + b_sum += ( b_in_sum += pixels[p+2]); + + stackIn->r = pixels[p]; + stackIn->g = pixels[p + 1]; + stackIn->b = pixels[p + 2]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + + stackOut = stackOut->next; + + yi += width; + } + } + } + + MODIFY(img); +} + + +//--------------------------------------------------------------------------- + +static inline int between0And255 (int val) +{ + if (val < 0) + return 0; + else if (val > 255) + return 255; + else + return val; +} + +static inline int get_brightness(int base, int strength) +{ + if (strength == 0) return base; + return between0And255(base + strength); +} + +static inline int get_contrast(int base, int strength) +{ + if (strength == 0) return base; + return between0And255 ((base - 127) * (strength + 255) / 255 + 127); +} + +static inline int myRound(double d) +{ + return d >= 0.0 ? (int)(d + 0.5) : (int)( d - ((int)d-1) + 0.5 ) + ((int)d-1); +} + +static inline int get_gamma(int base, int strength) +{ + if (strength == 0) return base; + return between0And255(myRound(255.0 * pow(base / 255.0, 1.0 / pow(10, strength / 255.0)))); +} + + +void IMAGE_balance(GB_IMG *img, int brightness, int contrast, int gamma, int hue, int saturation, int lightness) +{ + GET_POINTER(img, p, pm); + uint col; + //int h, s, v; + //int r, g, b; + //int hcol, scol, vcol; + int format = img->format; + int i; + uchar *pp; + + SYNCHRONIZE(img); + + if (brightness || contrast || gamma) + { + uchar trgb[256]; + + for (i = 0; i < 256; i++) + trgb[i] = get_gamma(get_contrast(get_brightness(i, brightness), contrast), gamma); + + if (img->format == GB_IMAGE_BGRA || img->format == GB_IMAGE_RGBA) + { + while (p != pm) + { + pp = (uchar *)p; + pp[0] = trgb[pp[0]]; + pp[1] = trgb[pp[1]]; + pp[2] = trgb[pp[2]]; + p++; + } + } + else + { + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(trgb[RED(col)], trgb[GREEN(col)], trgb[BLUE(col)], ALPHA(col)), format); + } + } + } + + if (hue || saturation) + { + double sm; + uchar r, g, b; + + if (saturation < 0) + sm = 1 + saturation / 255.0; + else + sm = 1 + saturation / 255.0 * 2; + + double hue6 = (double)hue / 360 * 6; + + double l, h, s, v, m, f; + + p = (uint *)img->data; + while (p != pm) + { + col = BGRA_from_format(*p, format); + r = RED(col); g = GREEN(col); b = BLUE(col); + + uchar vs = r; + if (g > vs) vs = g; + if (b > vs) vs = b; + + uchar ms = r; + if (g < ms) ms = g; + if (b < ms) ms = b; + + uchar vm = (vs - ms); + + l = (double)(ms + vs) / 510; + + if (vs && vm) + { + if ((ms + vs) <= 255) + { + s = (double)vm / (vs + ms) * sm; + if (s > 1) s = 1; + v = l * (1 + s); + } + else + { + s = (double)vm / (510 - (vs + ms)) * sm; + if (s > 1) s = 1; + v = (l + s - l * s); + } + + if (r == vs) + { + if (g == ms) + h = 5 + (((double)vs-b)/vm) + hue6; + else + h = 1 - ((double)(vs-g)/vm) + hue6; + } + else if (g == vs) + { + if (b == ms) + h = 1 + ((double)(vs-r)/vm) + hue6; + else + h = 3 - ((double)(vs-b)/vm) + hue6; + } + else + { + if (r == ms) + h = 3 + ((double)(vs-g)/vm) + hue6; + else + h = 5 - ((double)(vs-r)/vm) + hue6; + } + + if (h < 0) h += 6; + if (h >= 6) h -= 6; + + m = l + l - v; + f = h - (int)h; + + switch((int)h) + { + case 0: + r = v*255; g = (m+((v-m)*f))*255; b = m*255; break; + case 1: + r = (v-((v-m)*f))*255; g = v*255; b = m*255; break; + case 2: + r = m*255; g = v*255; b = (m+((v-m)*f))*255; break; + case 3: + r = m*255; g = (v-((v-m)*f))*255; b = v*255; break; + case 4: + r = (m+((v-m)*f))*255; g = m*255; b = v*255; break; + case 5: + r = v*255; g = m*255; b = (v-((v-m)*f))*255; break; + } + } + + *p++ = BGRA_to_format(RGBA(between0And255(r), between0And255(g), between0And255(b), ALPHA(col)), format); + } + } + + if (lightness) + { + uchar trgb[256]; + double lp = 1 + lightness / 255.0; + double lm = 1 - lightness / 255.0; + + for (i = 0; i < 256; i++) + { + if (lightness < 0) + trgb[i] = between0And255(i * lp); + else + trgb[i] = between0And255(i * lm + lightness); + } + + p = (uint *)img->data; + + if (img->format == GB_IMAGE_BGRA || img->format == GB_IMAGE_RGBA) + { + while (p != pm) + { + pp = (uchar *)p; + pp[0] = trgb[pp[0]]; + pp[1] = trgb[pp[1]]; + pp[2] = trgb[pp[2]]; + p++; + } + } + else + { + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(trgb[RED(col)], trgb[GREEN(col)], trgb[BLUE(col)], ALPHA(col)), format); + } + } + } + + MODIFY(img); +} + + +//--------------------------------------------------------------------------- + +void IMAGE_invert(GB_IMG *img, bool keep_hue) // GB_COLOR bg, GB_COLOR fg) +{ + static uchar lum[256]; + static bool init_lum = FALSE; + + GET_POINTER(img, p, pm); + uint col; + int format = img->format; + int i; + + SYNCHRONIZE(img); + + if (!keep_hue) //bg == COLOR_DEFAULT || fg == COLOR_DEFAULT) + { + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(255 - RED(col), 255 - GREEN(col), 255 - BLUE(col), ALPHA(col)), format); + } + } + else + { + GB_COLOR gcol; + + if (!init_lum) + { + for (i = 0; i <= 255; i++) + lum[i] = COLOR_invert_luminance(i); + init_lum = TRUE; + } + + while (p != pm) + { + gcol = GB_COLOR_from_format(*p, format); + gcol = COLOR_set_luminance(gcol, lum[COLOR_get_luminance(gcol)]); + *p++ = GB_COLOR_to_format(gcol, format); + } + } + /*else + { + bl = COLOR_get_luminance(bg); + fl = COLOR_get_luminance(fg); + + for (i = 0; i < 256; i++) + lum[i] = (int)sqrt(fl * fl - (i * i - bl * bl)); + + while (p != pm) + { + col = BGRA_from_format(*p, format); + col = COLOR_set_luminance(col, lum[COLOR_get_luminance(col)]); + *p++ = BGRA_to_format(col, format); + } + }*/ + + MODIFY(img); +} diff --git a/main/lib/image/image.h b/main/lib/image/image.h new file mode 100644 index 00000000..1e9acdf1 --- /dev/null +++ b/main/lib/image/image.h @@ -0,0 +1,89 @@ +/*************************************************************************** + + image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __IMAGE_H +#define __IMAGE_H + +#include "main.h" + +#ifndef __IMAGE_C +extern bool IMAGE_debug; +#endif + +static inline int RED(uint rgba) { return ((rgba >> 16) & 0xff); } +static inline int GREEN(uint rgba) { return ((rgba >> 8) & 0xff); } +static inline int BLUE(uint rgba) { return (rgba & 0xff); } +static inline int ALPHA(uint rgba) { return ((rgba >> 24) & 0xff); } + +static inline uint RGB(int r, int g, int b) { return (0xff << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } +static inline uint RGBA(int r, int g, int b, int a) { return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } +static inline int GRAY(uint rgba) { return (RED(rgba) * 11 + GREEN(rgba) * 16 + BLUE(rgba) * 5) / 32; } + +int IMAGE_size(GB_IMG *img); + +void IMAGE_create(GB_IMG *img, int width, int height, int format, GB_COLOR fill); +void IMAGE_create_with_data(GB_IMG *img, int width, int height, int format, unsigned char *data); + +void IMAGE_take(GB_IMG *img, GB_IMG_OWNER *owner, void *owner_handle, int width, int height, unsigned char *data); +void *IMAGE_check(GB_IMG *img, GB_IMG_OWNER *temp_owner); +void IMAGE_synchronize(GB_IMG *img); + +void IMAGE_delete(GB_IMG *img); + +void IMAGE_convert(GB_IMG *img, int format); + +void IMAGE_fill(GB_IMG *img, GB_COLOR col); +void IMAGE_fill_rect(GB_IMG *img, int x, int y, int w, int h, GB_COLOR col, bool opaque); + +void IMAGE_make_gray(GB_IMG *img); +void IMAGE_make_transparent(GB_IMG *img, GB_COLOR color); + +GB_COLOR IMAGE_get_pixel(GB_IMG *img, int x, int y); +void IMAGE_set_pixel(GB_IMG *img, int x, int y, GB_COLOR col); + +void IMAGE_get_pixels(GB_IMG *img, int *data); +void IMAGE_set_pixels(GB_IMG *img, int *data); + +void IMAGE_replace(GB_IMG *img, GB_COLOR src, GB_COLOR dst, bool noteq); + +void IMAGE_set_default_format(int format); +int IMAGE_get_default_format(); +const char *IMAGE_format_to_string(int fmt); +int IMAGE_format_from_string(char *fmt); + +void IMAGE_bitblt(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh); +void IMAGE_draw_alpha(GB_IMG *dst, int dx, int dy, GB_IMG *src, int sx, int sy, int sw, int sh); +void IMAGE_compose(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh); +void IMAGE_colorize(GB_IMG *img, GB_COLOR color); +void IMAGE_mask(GB_IMG *img, GB_COLOR color); +void IMAGE_mirror(GB_IMG *src, GB_IMG *dst, bool horizontal, bool vertical); +void IMAGE_rotate(GB_IMG *src, GB_IMG *dst, bool left); +void IMAGE_transform(GB_IMG *dst, GB_IMG *src, double sx, double sy, double sdx, double sdy); +void IMAGE_set_opacity(GB_IMG *dst, uchar opacity); +void IMAGE_blur(GB_IMG *img, int radius); +void IMAGE_balance(GB_IMG *img, int brightness, int contrast, int gamma, int hue, int saturation, int lightness); +void IMAGE_invert(GB_IMG *img, bool keep_hue); + +#define IMAGE_is_void(_image) ((_image)->is_void) + +#endif diff --git a/main/lib/image/image_stat.c b/main/lib/image/image_stat.c new file mode 100644 index 00000000..a3480a36 --- /dev/null +++ b/main/lib/image/image_stat.c @@ -0,0 +1,624 @@ +/*************************************************************************** + + image_stat.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __IMAGE_STAT_C + +#include +#include + +#include "image_stat.h" + +const char *IMAGE_error = NULL; + +static const char _sign_gif[3] = { 'G', 'I', 'F' }; +static const char _sign_bmp[2] = { 'B', 'M' }; +static const char _sign_jpg[3] = { 0xff, 0xd8, 0xff }; +static const char _sign_png[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; +static const char _sign_tif_ii[4] = { 'I', 'I', 0x2A, 0x00 }; +static const char _sign_tif_mm[4] = { 'M', 'M', 0x00, 0x2A }; + +static int stream_seek(IMAGE_STREAM *stream, int pos, int whence) +{ + switch (whence) + { + case SEEK_CUR: + if ((stream->pos + pos) >= stream->len) + return 1; + if ((stream->pos + pos) < 0) + return 1; + stream->pos += pos; + return 0; + + case SEEK_SET: + if (pos < 0 || pos >= stream->len) + return 1; + stream->pos = pos; + return 0; + + default: + return 1; + } +} + +static int stream_read(IMAGE_STREAM *stream, void *addr, int len) +{ + int lmax = stream->len - stream->pos; + + if (len > lmax) + len = lmax; + + memcpy(addr, stream->addr + stream->pos, len); + stream->pos += len; + return len; +} + + +static int stream_getc(IMAGE_STREAM *stream) +{ + if (stream->pos >= stream->len) + return EOF; + + return (uchar)stream->addr[stream->pos++]; +} + + +static ushort read_ushort(IMAGE_STREAM * stream) +{ + uchar a[2]; + + /* returns 0 if we hit the end-of-file */ + if((stream_read(stream, a, 2)) < 2) + return 0; + + //fprintf(stderr, "read_ushort -> %d\n", (int)(((ushort)a[0]) << 8) + ((ushort)a[1])); + return (((ushort)a[0]) << 8) + ((ushort)a[1]); +} + + +static int get_ushort_at(void *pshort, int big_endian) +{ + if (big_endian) + return (((uchar *)pshort)[0] << 8) | ((uchar *)pshort)[1]; + else + return (((uchar *)pshort)[1] << 8) | ((uchar *)pshort)[0]; +} + +static signed short get_short_at(void *pshort, int big_endian) +{ + return (signed short)get_ushort_at(pshort, big_endian); +} + +static int get_int_at(void *pint, int big_endian) +{ + if (big_endian) + return (((char*)pint)[0] << 24) | (((uchar *)pint)[1] << 16) | (((uchar *)pint)[2] << 8 ) | (((uchar *)pint)[3] << 0); + else + return (((char*)pint)[3] << 24) | (((uchar *)pint)[2] << 16) | (((uchar *)pint)[1] << 8 ) | (((uchar *)pint)[0] << 0); +} + +static unsigned get_uint_at(void *pint, int big_endian) +{ + return (uint)get_int_at(pint, big_endian) & 0xffffffff; +} + + +//------------------------------------------------------------------------- + +static bool handle_gif (IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uchar dim[5]; + + if (stream_seek(stream, 3, SEEK_CUR)) + return TRUE; + + if (stream_read(stream, dim, sizeof(dim)) != sizeof(dim)) + return TRUE; + + result->width = (uint)dim[0] | (((uint)dim[1]) << 8); + result->height = (uint)dim[2] | (((uint)dim[3]) << 8); + result->depth = (dim[4] & 0x80) ? ((((uint)dim[4]) & 0x07) + 1) : 0; + return FALSE; +} + +//------------------------------------------------------------------------- + +static bool handle_bmp (IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uchar dim[16]; + int size; + + if (stream_seek(stream, 11, SEEK_CUR)) + return TRUE; + + if (stream_read(stream, dim, sizeof(dim)) != sizeof(dim)) + return TRUE; + + size = (((uint)dim[3]) << 24) + (((uint)dim[2]) << 16) + (((uint)dim[1]) << 8) + ((uint) dim[0]); + if (size == 12) + { + result->width = (((uint)dim[5]) << 8) + ((uint)dim[4]); + result->height = (((uint)dim[7]) << 8) + ((uint) dim[6]); + result->depth = ((uint)dim[11]); + return FALSE; + } + else if (size > 12 && (size <= 64 || size == 108)) + { + result->width = (((uint)dim[7]) << 24) + (((uint)dim[6]) << 16) + (((uint)dim[5]) << 8) + ((uint) dim[4]); + result->height = (((uint)dim[11]) << 24) + (((uint)dim[10]) << 16) + (((uint)dim[9]) << 8) + ((uint) dim[8]); + result->depth = (((uint)dim[15]) << 8) + ((uint)dim[14]); + return FALSE; + } + + return TRUE; +} + +//------------------------------------------------------------------------- + +static bool handle_png (IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uchar dim[13]; + + if (stream_seek(stream, 8, SEEK_CUR)) + return TRUE; + + if((stream_read(stream, dim, sizeof(dim))) < sizeof(dim)) + return TRUE; + + result->width = (((uint)dim[0]) << 24) + (((uint)dim[1]) << 16) + (((uint)dim[2]) << 8) + ((uint)dim[3]); + result->height = (((uint)dim[4]) << 24) + (((uint)dim[5]) << 16) + (((uint)dim[6]) << 8) + ((uint)dim[7]); + + switch (dim[9]) + { + case 0: result->depth = 8; break; + case 2: result->depth = 24; break; + case 3: result->depth = 8; break; + case 4: result->depth = 32; break; + case 6: result->depth = 32; break; + default: result->depth = 32; break; + } + + return FALSE; +} + +//------------------------------------------------------------------------- + +/* some defines for the different JPEG block types */ +#define M_SOF0 0xC0 /* Start Of Frame N */ +#define M_SOF1 0xC1 /* N indicates which compression process */ +#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */ +#define M_SOF3 0xC3 +#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */ +#define M_SOF6 0xC6 +#define M_SOF7 0xC7 +#define M_SOF9 0xC9 +#define M_SOF10 0xCA +#define M_SOF11 0xCB +#define M_SOF13 0xCD +#define M_SOF14 0xCE +#define M_SOF15 0xCF +#define M_SOI 0xD8 +#define M_EOI 0xD9 /* End Of Image (end of datastream) */ +#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */ +#define M_APP0 0xE0 +#define M_APP1 0xE1 +#define M_APP2 0xE2 +#define M_APP3 0xE3 +#define M_APP4 0xE4 +#define M_APP5 0xE5 +#define M_APP6 0xE6 +#define M_APP7 0xE7 +#define M_APP8 0xE8 +#define M_APP9 0xE9 +#define M_APP10 0xEA +#define M_APP11 0xEB +#define M_APP12 0xEC +#define M_APP13 0xED +#define M_APP14 0xEE +#define M_APP15 0xEF +#define M_COM 0xFE /* COMment */ + +#define M_PSEUDO 0xFFD8 /* pseudo marker for start of image(byte 0) */ + +static uint next_marker(IMAGE_STREAM * stream, int last_marker, int comment_correction, int ff_read) +{ + int a=0, marker; + + /* get marker byte, swallowing possible padding */ + if (last_marker == M_COM && comment_correction) + { + /* some software does not count the length bytes of COM section */ + /* one company doing so is very much envolved in JPEG... so we accept too */ + /* by the way: some of those companies changed their code now... */ + comment_correction = 2; + } + else + { + last_marker = 0; + comment_correction = 0; + } + + if (ff_read) + a = 1; /* already read 0xff in filetype detection */ + + do + { + if ((marker = stream_getc(stream)) == EOF) + return M_EOI;/* we hit EOF */ + + if (last_marker == M_COM && comment_correction > 0) + { + if (marker != 0xFF) + { + marker = 0xff; + comment_correction--; + } + else + { + last_marker = M_PSEUDO; /* stop skipping non 0xff for M_COM */ + } + } + + if (++a > 25) + { + /* who knows the maxim amount of 0xff? though 7 */ + /* but found other implementations */ + return M_EOI; + } + } + while (marker == 0xff); + + if (a < 2) + return M_EOI; /* at least one 0xff is needed before marker code */ + + if (last_marker == M_COM && comment_correction) + return M_EOI; /* ah illegal: char after COM section not 0xFF */ + + return (uint)marker; +} + +/* skip over a variable-length block; assumes proper length marker */ +static bool skip_variable(IMAGE_STREAM * stream) +{ + int length = read_ushort(stream); + + if (length < 2) + return 0; + + length -= 2; + + return stream_seek(stream, length, SEEK_CUR) == 0; +} + + +static bool handle_jpeg(IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uint marker = M_PSEUDO; + ushort ff_read = 1; + bool ret = TRUE; + + for(;;) + { + marker = next_marker(stream, marker, 1, ff_read); + ff_read = 0; + + //fprintf(stderr, "marker = 0x%02X\n", marker); + + switch (marker) + { + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + + read_ushort(stream); + stream_getc(stream); + result->height = read_ushort(stream); + result->width = read_ushort(stream); + stream_getc(stream); + result->depth = 24; + return FALSE; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + + if (!skip_variable(stream)) + return ret; + + break; + + case M_SOS: + case M_EOI: + + return ret; /* we're about to hit image data, or are at EOF. stop processing. */ + + default: + + if (!skip_variable(stream)) /* anything else isn't interesting */ + return ret; + + break; + } + } + + return ret; /* perhaps image broken -> no info but size */ +} + +//------------------------------------------------------------------------- + +//static const int _tiff_bytes_per_format[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; + +/* uncompressed only */ +#define TAG_IMAGEWIDTH 0x0100 +#define TAG_IMAGEHEIGHT 0x0101 +/* compressed images only */ +#define TAG_COMP_IMAGEWIDTH 0xA002 +#define TAG_COMP_IMAGEHEIGHT 0xA003 + +#define TAG_FMT_BYTE 1 +#define TAG_FMT_STRING 2 +#define TAG_FMT_USHORT 3 +#define TAG_FMT_ULONG 4 +#define TAG_FMT_URATIONAL 5 +#define TAG_FMT_SBYTE 6 +#define TAG_FMT_UNDEFINED 7 +#define TAG_FMT_SSHORT 8 +#define TAG_FMT_SLONG 9 +#define TAG_FMT_SRATIONAL 10 +#define TAG_FMT_SINGLE 11 +#define TAG_FMT_DOUBLE 12 + +static bool handle_tiff(IMAGE_STREAM * stream, IMAGE_INFO *result, bool big_endian) +{ + int i, num_entries; + uchar *dir_entry; + size_t ifd_size, dir_size, entry_value, ifd_addr; + int entry_tag , entry_type; + char *ifd_data, ifd_ptr[4]; + int width = 0, height = 0; + + if (stream_read(stream, ifd_ptr, 4) != 4) + return TRUE; + + ifd_addr = get_uint_at(ifd_ptr, big_endian); + if (stream_seek(stream, ifd_addr - 8, SEEK_CUR)) + return TRUE; + + ifd_size = 2; + GB.Alloc(POINTER(&ifd_data), ifd_size); + + if (stream_read(stream, ifd_data, 2) != 2) + { + GB.Free(POINTER(&ifd_data)); + return TRUE; + } + + num_entries = get_ushort_at(ifd_data, big_endian); + + // dir_size = + * + + dir_size = 2 + 12 * num_entries + 4; + + ifd_size = dir_size; + GB.Realloc(POINTER(&ifd_data), ifd_size); + + if (stream_read(stream, ifd_data + 2, dir_size - 2) != dir_size - 2) + { + GB.Free(POINTER(&ifd_data)); + return TRUE; + } + + /* now we have the directory we can look how long it should be */ + ifd_size = dir_size; + for(i = 0; i < num_entries; i++) + { + dir_entry = (uchar *)(ifd_data + 2 + i * 12); + entry_tag = get_ushort_at(dir_entry + 0, big_endian); + entry_type = get_ushort_at(dir_entry + 2, big_endian); + + switch(entry_type) + { + case TAG_FMT_BYTE: + case TAG_FMT_SBYTE: + entry_value = (size_t)(dir_entry[8]); + break; + case TAG_FMT_USHORT: + entry_value = get_ushort_at(dir_entry+8, big_endian); + break; + case TAG_FMT_SSHORT: + entry_value = get_short_at(dir_entry+8, big_endian); + break; + case TAG_FMT_ULONG: + entry_value = get_uint_at(dir_entry+8, big_endian); + break; + case TAG_FMT_SLONG: + entry_value = get_int_at(dir_entry+8, big_endian); + break; + default: + continue; + } + + switch(entry_tag) + { + case TAG_IMAGEWIDTH: + case TAG_COMP_IMAGEWIDTH: + width = entry_value; + break; + + case TAG_IMAGEHEIGHT: + case TAG_COMP_IMAGEHEIGHT: + height = entry_value; + break; + } + } + + GB.Free(POINTER(&ifd_data)); + + if (width > 0 && height > 0) + { + result->height = height; + result->width = width; + result->depth = 24; + return FALSE; + } + + return TRUE; +} + +//------------------------------------------------------------------------- + +static char *image_type_to_mime_type(int type) +{ + switch(type) + { + case IMAGE_TYPE_GIF: + return "image/gif"; + case IMAGE_TYPE_JPEG: + return "image/jpeg"; + case IMAGE_TYPE_PNG: + return "image/png"; + case IMAGE_TYPE_BMP: + return "image/bmp"; + case IMAGE_TYPE_TIFF_INTEL: + case IMAGE_TYPE_TIFF_MOTOROLA: + return "image/tiff"; + default: + case IMAGE_TYPE_UNKNOWN: + return "application/octet-stream"; + } +} + +static int get_image_type(IMAGE_STREAM * stream) +{ + char buffer[12]; + + if((stream_read(stream, buffer, 3)) != 3) + { + IMAGE_error = "Read error"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_gif, 3) == 0) + return IMAGE_TYPE_GIF; + + if (memcmp(buffer, _sign_jpg, 3) == 0) + return IMAGE_TYPE_JPEG; + + if (memcmp(buffer, _sign_png, 3) == 0) + { + if (stream_read(stream, buffer + 3, 5) != 5) + { + IMAGE_error = "Read error"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_png, 8) == 0) + return IMAGE_TYPE_PNG; + + IMAGE_error = "PNG file is corrupted"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_bmp, 2) == 0) + return IMAGE_TYPE_BMP; + + if (stream_read(stream, buffer + 3, 1) != 1) + { + IMAGE_error = "Read error"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_tif_ii, 4) == 0) + return IMAGE_TYPE_TIFF_INTEL; + + if (memcmp(buffer, _sign_tif_mm, 4) == 0) + return IMAGE_TYPE_TIFF_MOTOROLA; + + return IMAGE_TYPE_UNKNOWN; +} + + +bool IMAGE_get_info(IMAGE_STREAM *stream, IMAGE_INFO *result) +{ + int type; + bool err; + + type = get_image_type(stream); + if (type == IMAGE_TYPE_ERROR) + return TRUE; + + result->type = image_type_to_mime_type(type); + + switch(type) + { + case IMAGE_TYPE_GIF: + err = handle_gif(stream, result); + break; + case IMAGE_TYPE_JPEG: + err = handle_jpeg(stream, result); + break; + case IMAGE_TYPE_PNG: + err = handle_png(stream, result); + break; + case IMAGE_TYPE_BMP: + err = handle_bmp(stream, result); + break; + case IMAGE_TYPE_TIFF_INTEL: + err = handle_tiff(stream, result, FALSE); + break; + case IMAGE_TYPE_TIFF_MOTOROLA: + err = handle_tiff(stream, result, TRUE); + break; + default: + case IMAGE_TYPE_UNKNOWN: + err = FALSE; + break; + } + + if (err) + IMAGE_error = "Cannot read file"; + + return err; +} + diff --git a/main/lib/image/image_stat.h b/main/lib/image/image_stat.h new file mode 100644 index 00000000..48d9dae6 --- /dev/null +++ b/main/lib/image/image_stat.h @@ -0,0 +1,66 @@ +/*************************************************************************** + + image_stat.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __IMAGE_STAT_H +#define __IMAGE_STAT_H + +#include "main.h" + +typedef + struct { + char *addr; + int len; + int pos; + } + IMAGE_STREAM; + +typedef + enum + { + IMAGE_TYPE_ERROR = -1, + IMAGE_TYPE_UNKNOWN = 0, + IMAGE_TYPE_GIF = 1, + IMAGE_TYPE_JPEG, + IMAGE_TYPE_PNG, + IMAGE_TYPE_BMP, + IMAGE_TYPE_TIFF_INTEL, + IMAGE_TYPE_TIFF_MOTOROLA + } + IMAGE_FILETYPE; + +typedef + struct { + char *type; + unsigned int width; + unsigned int height; + unsigned int depth; + } + IMAGE_INFO; + +#ifndef __IMAGE_STAT_C +extern const char *IMAGE_error; +#endif + +bool IMAGE_get_info(IMAGE_STREAM *stream, IMAGE_INFO *info); + +#endif \ No newline at end of file diff --git a/main/lib/image/main.c b/main/lib/image/main.c new file mode 100644 index 00000000..da42cf45 --- /dev/null +++ b/main/lib/image/main.c @@ -0,0 +1,83 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include + +#include "CImage.h" +#include "CImageStat.h" +#include "c_color.h" +#include "image.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CColorInfoDesc, + CColorDesc, + CImageDesc, + CImageStatDesc, + NULL +}; + +static GB_IMG *create_image(int width, int height, int format, unsigned char *data) +{ + CIMAGE *image; + + image = GB.New(GB.FindClass("Image"), NULL, NULL); + IMAGE_create_with_data(&image->image, width, height, format, data); + return (GB_IMG *)image; +} + +void *GB_IMAGE_1[] EXPORT = +{ + (void *)IMAGE_INTERFACE_VERSION, + (void *)create_image, + (void *)IMAGE_take, + (void *)IMAGE_check, + (void *)IMAGE_synchronize, + (void *)IMAGE_size, + (void *)IMAGE_set_default_format, + (void *)IMAGE_get_default_format, + (void *)IMAGE_get_pixel, + (void *)IMAGE_convert, + (void *)COLOR_merge, + (void *)COLOR_lighter, + (void *)COLOR_darker, + (void *)IMAGE_format_to_string, + (void *)COLOR_get_luminance, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/image/main.h b/main/lib/image/main.h new file mode 100644 index 00000000..051f199b --- /dev/null +++ b/main/lib/image/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/inotify/Makefile.am b/main/lib/inotify/Makefile.am new file mode 100644 index 00000000..771f0ba9 --- /dev/null +++ b/main/lib/inotify/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.inotify +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.inotify.la + +gb_inotify_la_LIBADD = @INOTIFY_LIB@ +gb_inotify_la_LDFLAGS = -module @LD_FLAGS@ @INOTIFY_LDFLAGS@ +gb_inotify_la_CPPFLAGS = @INOTIFY_INC@ -I$(top_srcdir)/share + +gb_inotify_la_SOURCES = \ + main.h main.c \ + gb_list.c \ + c_watch.h c_watch.c diff --git a/main/lib/inotify/TODO b/main/lib/inotify/TODO new file mode 100644 index 00000000..39c32a66 --- /dev/null +++ b/main/lib/inotify/TODO @@ -0,0 +1,5 @@ + - Work on portability to non-Linux filesystem monitoring systems. + I've to look at: pnotify[0] or rather libkqueue[1] + +[0] https://code.google.com/p/pnotify +[1] http://sourceforge.net/projects/libkqueue diff --git a/main/lib/inotify/c_watch.c b/main/lib/inotify/c_watch.c new file mode 100644 index 00000000..676f1332 --- /dev/null +++ b/main/lib/inotify/c_watch.c @@ -0,0 +1,709 @@ +/* + * c_watch.c - Watch and .Watch.Events classes + * + * Copyright (C) 2013, 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_WATCH_C + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "c_watch.h" + +//#define DEBUG_ME 1 + +#define GB_ErrorErrno() GB.Error(strerror(errno)) + +enum { EV_READ, EV_STAT, EV_CLOSE, EV_CREATE, EV_DELETE, EV_WRITE, EV_MOVE, EV_MOVED_FROM, EV_MOVED_TO, EV_OPEN, NUM_EV }; + +typedef + struct cinfo CINFO; + +struct cinfo { + struct inotify_event *iev; + CINFO *prev; +}; + +typedef + struct { + int fd; + GB_HASHTABLE watches; + CINFO *top; + } + CINOTIFY; + +typedef + struct { + CWATCH *root; + char *path; + int wd; + int events[NUM_EV]; + } + WATCH_LIST; + +typedef + struct { + int *eventp; + uint32_t mask; + } + EVENT_TABLE; + +/* XXX: We currently only support one inotify instance */ +static CINOTIFY _ino = {-1, NULL, NULL}; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Stat); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Create); +DECLARE_EVENT(EVENT_Delete); +DECLARE_EVENT(EVENT_Write); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_MoveFrom); +DECLARE_EVENT(EVENT_MoveTo); +DECLARE_EVENT(EVENT_Open); + +static EVENT_TABLE _event_table[] = +{ + {&EVENT_Read, IN_ACCESS}, + {&EVENT_Stat, IN_ATTRIB}, + {&EVENT_Close, IN_CLOSE_WRITE | IN_CLOSE_NOWRITE}, + {&EVENT_Create, IN_CREATE}, + {&EVENT_Delete, IN_DELETE | IN_DELETE_SELF}, + {&EVENT_Write, IN_MODIFY}, + {&EVENT_Move, IN_MOVE_SELF}, + {&EVENT_MoveFrom, IN_MOVED_FROM}, + {&EVENT_MoveTo, IN_MOVED_TO}, + {&EVENT_Open, IN_OPEN} +}; + +static bool destroy_watch(CWATCH *watch); +static void callback(int fd, int flags, CINOTIFY *ino); + +static void init_inotify(void) +{ + if (_ino.fd >= 0) + return; + +#if DEBUG_ME + fprintf(stderr, "init_inotify\n"); +#endif + + _ino.fd = inotify_init(); + if (_ino.fd == -1) { + GB_ErrorErrno(); + return; + } + GB.HashTable.New(&_ino.watches, GB_COMP_BINARY); + GB.Watch(_ino.fd, GB_WATCH_READ, callback, (intptr_t) &_ino); + _ino.top = NULL; +} + +static void destroy_watch_list(WATCH_LIST *list) +{ + while (!destroy_watch(list->root)); +} + +static void exit_inotify(void) +{ + WATCH_LIST *list; + int fd = _ino.fd; // prevent a recursion + + if (_ino.fd < 0) + return; + + _ino.fd = -1; + +#if DEBUG_ME + fprintf(stderr, "exit_inotify\n"); +#endif + + while (!GB.HashTable.First(_ino.watches, (void **)&list)) + destroy_watch_list(list); + + GB.Watch(fd, GB_WATCH_NONE, NULL, 0); + close(fd); + GB.HashTable.Free(&_ino.watches); +} + +static WATCH_LIST *find_watch_list(CINOTIFY *ino, int wd) +{ + WATCH_LIST *list = NULL; + GB.HashTable.Get(ino->watches, (char *)&wd, sizeof(wd), (void **)&list); + return list; +} + +static WATCH_LIST *find_watch_list_from_path(CINOTIFY *ino, const char *path, int len, bool create) +{ + WATCH_LIST *list = NULL; + GB.HashTable.Get(ino->watches, path, len, (void **)&list); + + if (!list && create) + { +#if DEBUG_ME + fprintf(stderr, "find_watch_list_from_path: create watch list for %.*s\n", len, path); +#endif + GB.AllocZero(POINTER(&list), sizeof(WATCH_LIST)); + list->wd = -1; + list->path = GB.NewString(path, len); + GB.HashTable.Add(ino->watches, path, len, list); + } + + return list; +} + +static void update_watch_list(WATCH_LIST *list) +{ + int i; + uint32_t mask = 0; + + for (i = 0; i < NUM_EV; i++) + { + if (list->events[i]) + mask |= _event_table[i].mask; + } + + if (mask == 0) + { + if (list->wd >= 0) + { +#if DEBUG_ME + fprintf(stderr, "update_watch_list: remove watch %d\n", list->wd); +#endif + GB.HashTable.Remove(_ino.watches, (char *)&list->wd, sizeof(list->wd)); + inotify_rm_watch(_ino.fd, list->wd); + list->wd = -1; + } + } + else + { + int wd = inotify_add_watch(_ino.fd, list->path, mask); + + if (wd >= 0 && list->wd != wd) + { +#if DEBUG_ME + fprintf(stderr, "update_watch_list: add watch %d %08X\n", wd, mask); +#endif + list->wd = wd; + GB.HashTable.Add(_ino.watches, (char *)&wd, sizeof(wd), list); + } + } +} + +static void create_watch(CWATCH *watch, const char *path, int len) +{ + int i; + WATCH_LIST *list = find_watch_list_from_path(&_ino, path, len, TRUE); + + watch->root = list; + +#if DEBUG_ME + fprintf(stderr, "create_watch: %p %.*s\n", watch, len, path); +#endif + + LIST_insert(&list->root, watch, &watch->list); + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]++; + } + + update_watch_list(list); +} + +static bool destroy_watch(CWATCH *watch) +{ + int i; + WATCH_LIST *list = (WATCH_LIST *)watch->root; + + if (!list) + return FALSE; + + watch->root = NULL; + +#if DEBUG_ME + fprintf(stderr, "destroy_watch: %p %s\n", watch, list->path); +#endif + + GB.StoreVariant(NULL, &watch->tag); + LIST_remove(&list->root, watch, &watch->list); + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]--; + } + + update_watch_list(list); + + if (list->root == NULL) + { + GB.HashTable.Remove(_ino.watches, list->path, GB.StringLength(list->path)); + +#if DEBUG_ME + fprintf(stderr, "destroy_watch: remove watch list: %s -> %d\n", list->path, GB.HashTable.Count(_ino.watches)); +#endif + + GB.FreeString(&list->path); + GB.Free(POINTER(&list)); + + if (GB.HashTable.Count(_ino.watches) == 0) + exit_inotify(); + + return TRUE; + } + else + return FALSE; + +} + +static void pause_watch(CWATCH *watch) +{ + int i; + WATCH_LIST *list = (WATCH_LIST *)watch->root; + + if (watch->paused) + return; + + watch->paused = TRUE; + watch->save_events = watch->events; + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]--; + } + + watch->events = 0; + + update_watch_list(list); +} + +static void resume_watch(CWATCH *watch) +{ + int i; + WATCH_LIST *list = (WATCH_LIST *)watch->root; + + if (!watch->paused) + return; + + watch->paused = FALSE; + watch->events = watch->save_events; + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]++; + } + + watch->save_events = 0; + + update_watch_list(list); +} + +static void callback(int fd, int flags, CINOTIFY *ino) +{ + struct inotify_event *iev; + int i, j, length; + int event; + CINFO info; + WATCH_LIST *list; + CWATCH *watch; + uint32_t mask; + char buf[sizeof(*iev) + NAME_MAX + 1] __attribute__((aligned(sizeof(int)))); + + for(;;) + { + if ((length = read(fd, buf, sizeof(buf))) <= 0) + { + if (errno == EINTR) + continue; + + GB_ErrorErrno(); + return; + } + + break; + } + + for (i = 0; i < length; i += sizeof(*iev) + iev->len) + { + + iev = (struct inotify_event *) &buf[i]; + + list = find_watch_list(ino, iev->wd); + + if (!list && !(iev->mask & IN_Q_OVERFLOW)) { + if (getenv("GB_INOTIFY_DEBUG")) + fprintf(stderr, "gb.inotify: descriptor %d not known. Name was `%s'\n", iev->wd, iev->name); + continue; + } + + mask = iev->mask & ~(IN_IGNORED | IN_ISDIR | IN_Q_OVERFLOW | IN_UNMOUNT); + + LIST_for_each(watch, list->root) + { + if (watch->paused) + continue; + + for (j = 0; j < NUM_EV; j++) + { + event = *_event_table[j].eventp; + if (_event_table[j].mask & mask && GB.CanRaise(watch, event)) + { + /* + * In case, the event loop kicks in at GB.Raise() + * and preempts this callback with itself. + * + * The problem is then that the event info structure + * may get replaced by another, thus we lose data. + * That's why we save it here. However, it is + * sufficient to keep the CINFO on the stack! + */ + info.iev = iev; + info.prev = _ino.top; + _ino.top = &info; + + GB.Raise(watch, event, 0); + + _ino.top = info.prev; + } + } + } + + /* Also remove the watch if the kernel did. */ + /*if (iev->mask & IN_IGNORED) + destroy_watch(watch);*/ + } +} + +/**G + * Tidy up. + **/ +BEGIN_METHOD_VOID(Watch_exit) + + /* Free the remaining objects. destroy_watch() will then also take + * care of calling INOTIFY_exit() to free bookkeeping data. */ + exit_inotify(); + +END_METHOD + +#if 0 +/**G + * Return a Watch from its path. If the path is not watched, Null is + * returned. + **/ +BEGIN_METHOD(Watch_get, GB_STRING path) + + CWATCH *watch; + + watch = find_watch_path(&_ino, GB.ToZeroString(ARG(path))); + if (!watch) { + GB.ReturnNull(); + return; + } + GB.ReturnObject(watch); + +END_METHOD +#endif + +/**G + * The name of the file or directory which is subject to the event, relative + * to the Watch object which triggered the event. If Null, the directory + * itself is subject. + **/ +BEGIN_PROPERTY(Watch_Name) + + struct inotify_event *iev = _ino.top->iev; + + GB.ReturnNewZeroString(iev->len ? iev->name : ""); + +END_PROPERTY + +/**G + * Return whether the event subject is/was a directory. + **/ +BEGIN_PROPERTY(Watch_IsDir) + + GB.ReturnBoolean(!!(_ino.top->iev->mask & IN_ISDIR)); + +END_PROPERTY + +/**G + * Return whether the filesystem on which the watched path resided, was + * unmounted. In this case, the Watch object is invalidated just after the + * event. + **/ +BEGIN_PROPERTY(Watch_Unmount) + + GB.ReturnBoolean(!!(_ino.top->iev->mask & IN_UNMOUNT)); + +END_PROPERTY + +/**G + * A cookie used to associate events. This is currently only used to connect + * MoveFrom and MoveTo events of the same file. + **/ +BEGIN_PROPERTY(Watch_Cookie) + + GB.ReturnInteger(_ino.top->iev->cookie); + +END_PROPERTY + +#define THIS ((CWATCH *) _object) + +/**G + * Create a new watch for the given path. + * + * If the NoFollowLink parameter is set and true, symlinks are not followed. + * + * Events is a bitmask specifying which events are to be reported at all. + * The default mask is determined by the event handlers defined for this + * object. + * + * TODO: Maybe add support for IN_EXCL_UNLINK, IN_ONESHOT and IN_ONLYDIR. + **/ +BEGIN_METHOD(Watch_new, GB_STRING path; GB_BOOLEAN nofollow; GB_INTEGER events) + + int i; + ushort events = VARGOPT(events, 0); + + if (LENGTH(path) == 0) + { + GB.Error("Null path"); + return; + } + + //fprintf(stderr, "Watch_new: %p\n", THIS); + + /* If this is the first watch, we need an inotify instance first. + * We don't use the component's init function to set up _ino because + * the inotify instance needs a GB.Watch() which would keep the + * program open even if no watches were registered. So this is done + * ad-hoc if there are watches. */ + init_inotify(); + + /* Get the mask */ + if (!events) + { + for (i = 0; i < NUM_EV; i++) + { + if (GB.CanRaise(THIS, *_event_table[i].eventp)) + events |= (1 << i); //_event_table[i].mask; + } + /* But we need at least to watch one event. IN_DELETE_SELF + * seems a good one as it doesn't trigger too often :-) */ + //mask |= IN_DELETE_SELF; + } + + THIS->events = events; + THIS->nofollow = VARGOPT(nofollow, FALSE); + THIS->tag.type = GB_T_NULL; + + create_watch(THIS, STRING(path), LENGTH(path)); + +END_METHOD + +/**G + * Deallocate the watch. + **/ +BEGIN_METHOD_VOID(Watch_free) + + //fprintf(stderr, "Watch_free: %p\n", THIS); + + destroy_watch(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Watch_Resume) + + resume_watch(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Watch_Pause) + + pause_watch(THIS); + +END_METHOD + +/**G + * Return the watched path. + **/ +BEGIN_PROPERTY(Watch_Path) + + GB.ReturnString(((WATCH_LIST *)THIS->root)->path); + +END_PROPERTY + +/**G + * Return or set if the watch is paused. + * + * {seealso + * Pause(), Resume() + * } + **/ +BEGIN_PROPERTY(Watch_IsPaused) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->paused); + else if (THIS->paused != VPROP(GB_BOOLEAN)) + { + if (VPROP(GB_BOOLEAN)) + pause_watch(THIS); + else + resume_watch(THIS); + } + +END_PROPERTY + +/**G + * This Variant is free for use by the Gambas programmer. + **/ +BEGIN_PROPERTY(Watch_Tag) + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->tag); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->tag); + +END_PROPERTY + +GB_DESC CWatch[] = +{ + GB_DECLARE("Watch", sizeof(CWATCH)), + + GB_CONSTANT("Read", "i", 1 << EV_READ), + GB_CONSTANT("Stat", "i", 1 << EV_STAT), + GB_CONSTANT("Close", "i", 1 << EV_CLOSE), + GB_CONSTANT("Create", "i", 1 << EV_CREATE), + GB_CONSTANT("Delete", "i", 1 << EV_DELETE), + GB_CONSTANT("Write", "i", 1 << EV_WRITE), + GB_CONSTANT("Move", "i", 1 << EV_MOVE), + GB_CONSTANT("MoveFrom", "i", 1 << EV_MOVED_FROM), + GB_CONSTANT("MoveTo", "i", 1 << EV_MOVED_TO), + GB_CONSTANT("Open", "i", 1 << EV_OPEN), + GB_CONSTANT("All", "i", -1), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Stat", NULL, NULL, &EVENT_Stat), + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Create", NULL, NULL, &EVENT_Create), + GB_EVENT("Delete", NULL, NULL, &EVENT_Delete), + GB_EVENT("Write", NULL, NULL, &EVENT_Write), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("MoveFrom", NULL, NULL, &EVENT_MoveFrom), + GB_EVENT("MoveTo", NULL, NULL, &EVENT_MoveTo), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + + GB_STATIC_METHOD("_exit", NULL, Watch_exit, NULL), + //GB_STATIC_METHOD("_get", "Watch", Watch_get, "(Path)s"), + + GB_METHOD("Resume", NULL, Watch_Resume, NULL), + GB_METHOD("Pause", NULL, Watch_Pause, NULL), + + GB_STATIC_PROPERTY_READ("Name", "s", Watch_Name), + GB_STATIC_PROPERTY_READ("IsDir", "b", Watch_IsDir), + GB_STATIC_PROPERTY_READ("Unmount", "b", Watch_Unmount), + GB_STATIC_PROPERTY_READ("Cookie", "i", Watch_Cookie), + + GB_METHOD("_new", NULL, Watch_new, "(Path)s[(NoFollowLink)b(Events)i]"), + GB_METHOD("_free", NULL, Watch_free, NULL), + + GB_PROPERTY_SELF("Events", ".Watch.Events"), + GB_PROPERTY_READ("Path", "s", Watch_Path), + GB_PROPERTY("IsPaused", "b", Watch_IsPaused), + GB_PROPERTY("Tag", "v", Watch_Tag), + + GB_END_DECLARE +}; + +/**G + * Return if the given flag is set. You can also combine multiple flags to + * check if at least one of them is set. + **/ +BEGIN_METHOD(WatchEvents_get, GB_INTEGER events) + + GB.ReturnBoolean(!!((THIS->paused ? THIS->save_events : THIS->events) & VARG(events))); + +END_METHOD + +#define update_events(obj_events_p, events, value) \ +do { \ + if (value) \ + *(obj_events_p) |= (events); \ + else \ + *(obj_events_p) &= ~(events); \ +} while (0) + +/**G + * Set or clear a flag. You can combine multiple flags to set or clear them + * en masse. + **/ +BEGIN_METHOD(WatchEvents_put, GB_BOOLEAN value; GB_INTEGER events) + + WATCH_LIST *list = (WATCH_LIST *)THIS->root; + int i; + int events = VARG(events); + int value = VARG(value); + + if (!THIS->paused) + { + for (i = 0; i < NUM_EV; i++) + { + /* events is the bitmask of events to operate on, + * value is what to set all of them to. */ + if ((events & (1 << i)) == 0) + continue; + /* Don't update counts unless the value + * actually changed. */ + if (!!value == !!(THIS->events & (1 << i))) + continue; + + if (value) + list->events[i]++; + else + list->events[i]--; + } + + update_events(&THIS->events, events, value); + update_watch_list(list); + } + else + update_events(&THIS->save_events, events, value); + +END_METHOD + +GB_DESC CWatchEvents[] = +{ + GB_DECLARE_VIRTUAL(".Watch.Events"), + + GB_METHOD("_get", "b", WatchEvents_get, "(Events)i"), + GB_METHOD("_put", NULL, WatchEvents_put, "(Value)b(Events)i"), + + GB_END_DECLARE +}; diff --git a/main/lib/inotify/c_watch.h b/main/lib/inotify/c_watch.h new file mode 100644 index 00000000..d619d54f --- /dev/null +++ b/main/lib/inotify/c_watch.h @@ -0,0 +1,46 @@ +/* + * c_watch.h + * + * Copyright (C) 2013, 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_WATCH_H +#define __C_WATCH_H + +#include "main.h" +#include "gb_list.h" + +#ifndef __C_WATCH_C +extern GB_DESC CWatch[]; +extern GB_DESC CWatchEvents[]; +#endif + +typedef + struct { + GB_BASE ob; + LIST list; + void *root; + GB_VARIANT_VALUE tag; + ushort events; + ushort save_events; + bool nofollow; + bool paused; + } + CWATCH; + +#endif /* __C_WATCH_H */ diff --git a/main/lib/inotify/gb.inotify.component b/main/lib/inotify/gb.inotify.component new file mode 100644 index 00000000..33a5dc85 --- /dev/null +++ b/main/lib/inotify/gb.inotify.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Stable diff --git a/main/lib/inotify/gb_list.c b/main/lib/inotify/gb_list.c new file mode 100644 index 00000000..281dbe19 --- /dev/null +++ b/main/lib/inotify/gb_list.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_list.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_list_temp.h" diff --git a/main/lib/inotify/main.c b/main/lib/inotify/main.c new file mode 100644 index 00000000..ef1cf205 --- /dev/null +++ b/main/lib/inotify/main.c @@ -0,0 +1,43 @@ +/* + * main.c - gb.inotify glue + * + * Copyright (C) 2013, 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __MAIN_C + +#include "c_watch.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + CWatch, + CWatchEvents, + + NULL +}; + +int EXPORT GB_INIT() +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/inotify/main.h b/main/lib/inotify/main.h new file mode 100644 index 00000000..706234d4 --- /dev/null +++ b/main/lib/inotify/main.h @@ -0,0 +1,32 @@ +/* + * main.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/jit/Makefile.am b/main/lib/jit/Makefile.am new file mode 100644 index 00000000..6cb8a4bc --- /dev/null +++ b/main/lib/jit/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.jit +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.jit.la + +gb_jit_la_LIBADD = @C_LIB@ +gb_jit_la_LDFLAGS = -module @LD_FLAGS@ +gb_jit_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx $(AM_CFLAGS) + +gb_jit_la_SOURCES = \ + main.h main.c \ + gb_str.h gb_str.c \ + gbc_reserved.c \ + jit.h jit.c jit_body.c + + diff --git a/main/lib/jit/gb.jit.component b/main/lib/jit/gb.jit.component new file mode 100644 index 00000000..844988ff --- /dev/null +++ b/main/lib/jit/gb.jit.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.jit +Version=3.10.90 +State=2 +Hidden=True +Authors=Benoît Minisini diff --git a/main/lib/jit/gb.jit/.component b/main/lib/jit/gb.jit/.component new file mode 100644 index 00000000..0ca62472 --- /dev/null +++ b/main/lib/jit/gb.jit/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.jit +Version=3.18.0 +Authors=Benoît Minisini diff --git a/main/lib/jit/gb.jit/.directory b/main/lib/jit/gb.jit/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/lib/jit/gb.jit/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/lib/jit/gb.jit/.hidden/icon.png b/main/lib/jit/gb.jit/.hidden/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cc2067df08c8caa5ae975d64bf2557e183dd30bb GIT binary patch literal 693 zcmV;m0!safP);b>GGx0 zYt|Nr^?{X?S2Yqry+)8$R4%gN;Ll*-?Ez1W=2;zgs# z_xt_9;q73s(4EiXozCKYzS(55(POgFg2CGO`~69!$=>hvzTfO;w9-hV$^HKRWwX-! z{r-f)+oRFsX|>bFm7CHJigUn!|6n)i#{O>h<|`y4G~M)^obnRI1I<=<%`G z=Oi2!x!vpg{r<$_?l_#pHJik-+31qV->})q zNdN!=Ye_^wR9M69me*Q?KoCX~4P+Kk2*f0sV(-28-uwH%jZz}8%dRtf=NJC_bCUgm z*(sHZgsEz#5Dcu~zEwM#(-!PEHVyz_RZrkP%|nz49f32`fqr^)!QyrulH8C5yUi31 zchskFJqwvk69tz`Sss>20GuAq1P}B7ggc z7kCRI4{g-fxHlH$B8OjdFoF?~W(6}36#N-xpeXqI2|}RY4_kA+2_wYdx^!cQR=dU-4z>N9d9zMHOOY#-S1Ti1?@RLYj;}#5N7%baE zEU*b13fDs@uqi7J--D&%C4x*3mWt0k0@s6OYiw!h9(|oP&&)grJpqoF9wF$VShoM4 bV8HVYSg~VShNgoT00000NkvXXu0mjfwhwjK literal 0 HcmV?d00001 diff --git a/main/lib/jit/gb.jit/.icon.png b/main/lib/jit/gb.jit/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..09ac84d7fa8433213fd2674e9239853c8355ac70 GIT binary patch literal 10448 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" To sResult + + hComp = New CCompilation(sName, sPathSO, fTime) + + hComp.Run(sPath, "-w -fPIC -shared -lm") + + ' If $bDebug Then Error "gb.jit: linking to "; sPathSO + ' + ' 'gcc -shared -o libfoo.so foo.o + ' 'Exec [$sCompiler, "-shared", "-o", File.SetExt(sPath, "so"), File.SetExt(sPath, "o")] To sResult + ' 'Shell $sCompiler & " -shared " & sFlag & " -lm -o " & Shell(sPathSO) & " " & Shell(sPathO) & " 2>&1" To sResult + ' RunCompiler(sPathO, sPathSO, "-shared -lm") + ' If Not Exist(sPathSO) Then + ' Error "gb.jit: warning: unable to link JIT code:" + ' Error sResult + ' Return + ' Endif + +End + +Public Sub _Wait(sArch As String) As String + + Dim hComp As CCompilation + Dim sResult As String + + hComp = CCompilation.All[GetArchiveName(sArch)] + If Not hComp Then Return + + sResult = hComp.Wait() + Return sResult + +End + +Public Sub _Abort() + + Dim hComp As CCompilation + + If CCompilation.All.Count = 0 Then Return + + For Each hComp In CCompilation.All + hComp.Kill + Next + + CCompilation.All.Clear + +End + +Private Function Time_Read() As Float + + Return _Time + +End + +Private Function Debug_Read() As Boolean + + Return $bDebug + +End + +Private Sub Debug_Write(Value As Boolean) + + $bDebug = Value + +End diff --git a/main/lib/jit/gb.jit/.src/Main.module b/main/lib/jit/gb.jit/.src/Main.module new file mode 100644 index 00000000..96188747 --- /dev/null +++ b/main/lib/jit/gb.jit/.src/Main.module @@ -0,0 +1,7 @@ +' Gambas module file + +Public Sub Main() + + + +End diff --git a/main/lib/jit/gb.jit/.src/_ClassStat.class b/main/lib/jit/gb.jit/.src/_ClassStat.class new file mode 100644 index 00000000..a5e81c4d --- /dev/null +++ b/main/lib/jit/gb.jit/.src/_ClassStat.class @@ -0,0 +1,100 @@ +' Gambas class file + +Static Public Name As String +Static Public Parent As String +Static Public HasFast As Boolean + +Static Private $iSectionSize As Integer +Static Private $iSectionPos As Integer + +Static Private Sub GotoNextSection(hFile As File) + + $iSectionPos += $iSectionSize + Seek #hFile, $iSectionPos + $iSectionSize = Read #hFile As Integer + $iSectionPos += 4 + +End + +Static Private Sub ReadZeroString(hFile As File) As String + + Dim sStr As String + Dim iPos As Integer + + Do + sStr &= Read #hFile, Min(16, Lof(hFile) - Seek(hFile)) + iPos = InStr(sStr, Chr$(0)) + If iPos Then Return Left(sStr, iPos - 1) + Loop + +End + +Static Public Sub Stat(sDir As String, sName As String) + + Dim sPath As String + Dim hFile As File + Dim iVal As Integer + Dim iParent As Integer + Dim iFlag As Short + Dim bDebug As Boolean + Dim iStringPos As Integer + + sPath = sDir &/ ".gambas" &/ UCase(sName) + If Not Exist(sPath) Then Error.Raise("Class not found: " & sPath) + + $iSectionPos = 0 + $iSectionSize = 0 + + hFile = Open sPath + + Seek #hFile, 8 + iVal = Read #hFile As Integer + If iVal <> &H12345678 Then hFile.ByteOrder = 1 - hFile.ByteOrder + Seek #hFile, 12 + iVal = Read #hFile As Integer + bDebug = iVal And 1 + + $iSectionSize = 16 + + GotoNextSection(hFile) ' info + Seek #hFile, $iSectionPos + iParent = Read #hFile As Short + iFlag = Read #hFile As Short + + 'hStat.Exported = BTst(iFlag, 0) + 'hStat.AutoCreate = BTst(iFlag, 1) + 'hStat.Optional = BTst(iFlag, 2) + 'hStat.NoCreate = BTst(iFlag, 3) + HasFast = BTst(iFlag, 4) + + GotoNextSection(hFile) ' description + GotoNextSection(hFile) ' constant + GotoNextSection(hFile) ' reference + + If iParent <> -1 Then + + Seek #hFile, $iSectionPos + iParent * 4 + iParent = Read #hFile As Integer + iParent = Abs(iParent) + + Endif + + Do + iStringPos = $iSectionPos + Try GotoNextSection(hFile) + If Error Then Break + Loop + + Seek #hFile, iStringPos + Name = ReadZeroString(hFile) + + If iParent > 0 Then + + Seek #hFile, iStringPos + iParent + Parent = ReadZeroString(hFile) + + Endif + + Close hFile + +End diff --git a/main/lib/jit/gb.jit/gambas.h b/main/lib/jit/gb.jit/gambas.h new file mode 120000 index 00000000..9f0c378e --- /dev/null +++ b/main/lib/jit/gb.jit/gambas.h @@ -0,0 +1 @@ +../../../share/gambas.h \ No newline at end of file diff --git a/main/lib/jit/gb.jit/gb.jit.h b/main/lib/jit/gb.jit/gb.jit.h new file mode 120000 index 00000000..2b756893 --- /dev/null +++ b/main/lib/jit/gb.jit/gb.jit.h @@ -0,0 +1 @@ +../../../gbx/gb.jit.h \ No newline at end of file diff --git a/main/lib/jit/gb.jit/gb_error_common.h b/main/lib/jit/gb.jit/gb_error_common.h new file mode 120000 index 00000000..07c2de99 --- /dev/null +++ b/main/lib/jit/gb.jit/gb_error_common.h @@ -0,0 +1 @@ +../../../share/gb_error_common.h \ No newline at end of file diff --git a/main/lib/jit/gb.jit/gb_overflow.h b/main/lib/jit/gb.jit/gb_overflow.h new file mode 120000 index 00000000..e8d3a9d9 --- /dev/null +++ b/main/lib/jit/gb.jit/gb_overflow.h @@ -0,0 +1 @@ +../../../share/gb_overflow.h \ No newline at end of file diff --git a/main/lib/jit/gb.jit/jit.h b/main/lib/jit/gb.jit/jit.h new file mode 100644 index 00000000..4404b1a1 --- /dev/null +++ b/main/lib/jit/gb.jit/jit.h @@ -0,0 +1,684 @@ +#define GB_JIT 1 + +#include +#include +//#include + +#define TRUE 1 +#define FALSE 0 + +#define MAX(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a > _b ? _a : _b; }) +#define MIN(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a < _b ? _a : _b; }) +#define MIN_MAX(v, a, b) ({ __typeof__(v) _v = (v), _a = (a), _b = (b); _v < _a ? _a : (_v > _b ? _b : _v); }) + +// __attribute__((noreturn)) makes gcc dizzy and slow as hell +#define NORETURN + +#define E_NEPARAM 4 +#define E_TMPARAM 5 +#define E_TYPE 6 +#define E_OVERFLOW 7 +#define E_NOBJECT 12 +#define E_NULL 13 +#define E_NRETURN 18 +#define E_MATH 19 +#define E_ARG 20 +#define E_BOUND 21 +#define E_ZERO 26 +#define E_IOBJECT 29 +#define E_SARRAY 65 +#define E_UTYPE 71 + +static inline double frac(double x) +{ + x = fabs(x); + return x - floor(x); +} + +#ifdef __clang__ +#define __builtin_exp10 exp10 +static inline double exp10(double x) +{ + return pow(10, x); +} +#endif + +#ifdef __TINYC__ +#define __builtin_log log +#define __builtin_exp exp +#define __builtin_sqrt sqrt +#define __builtin_sin sin +#define __builtin_cos cos +#define __builtin_tan tan +#define __builtin_atan atan +#define __builtin_asin asin +#define __builtin_acos acos +#define __builtin_sinh sinh +#define __builtin_cosh cosh +#define __builtin_tanh tanh +#define __builtin_asinh asinh +#define __builtin_acosh acosh +#define __builtin_atanh atanh +#define __builtin_exp2(_expr) pow(2, (_expr)) +#define __builtin_exp10(_expr) pow(10, (_expr)) +#define __builtin_log2(_expr) (log(_expr) / M_LN2) +#define __builtin_cbrt cbrt +#define __builtin_expm1 expm1 +#define __builtin_log1p log1p +#define __builtin_floor floor +#define __builtin_ceil ceil +#endif + +typedef + unsigned char uchar; + +typedef + GB_VALUE VALUE; + +typedef + ushort PCODE; + +typedef + void (*EXEC_FUNC)(); + +typedef + void (*EXEC_FUNC_CODE)(ushort); + +typedef + struct { + GB_TYPE type; + GB_CLASS class; + void *super; + } + GB_VALUE_CLASS; + +typedef + struct { + GB_TYPE type; + void *object; + void *super; + } + GB_VALUE_OBJECT; + +typedef + struct { + GB_TYPE type; + void *class; + void *object; + char kind; + char defined; + short index; + } + GB_VALUE_FUNCTION; + +typedef + struct { + GB_TYPE type; + void *addr; + void *gp; + } + GB_VALUE_GOSUB; + +typedef + struct { + GB_BASE object; + int size; + int count; + GB_TYPE type; + void *data; + int *dim; + void *ref; + } + GB_ARRAY_IMPL; + +#define SP (*psp) +#define PC (JIT.exec->pc) +#define CP (JIT.exec->cp) +#define OP (JIT.exec->op) +#define FP (JIT.exec->fp) +#define PP (JIT.exec->pp) +#define BP (JIT.exec->bp) +#define EP (JIT.exec->ep) + +#define THROW(_err) ({ SP = sp; JIT.throw(_err); }) +#define THROW_PC(_err, _pc) ({ PC = &pc[_pc]; THROW(_err); }) +#define THROW_TYPE(_t1, _t2) ({ SP = sp; JIT.throw_type(_t1, _t2); }) +#define THROW_TYPE_PC(_t1, _t2, _pc) ({ PC = &pc[_pc]; THROW_TYPE(_t1, _t2); }) + +#define CHECK_FINITE(_val) ({ if (!isfinite(_val)) THROW(E_OVERFLOW); (_val); }) + +#define PARAM_b(_p) (JIT.conv(&sp[-n+(_p)], GB_T_BOOLEAN), sp[-n+(_p)]._boolean.value) +#define PARAM_c(_p) (JIT.conv(&sp[-n+(_p)], GB_T_BYTE), (uchar)(sp[-n+(_p)]._integer.value)) +#define PARAM_h(_p) (JIT.conv(&sp[-n+(_p)], GB_T_SHORT), (short)(sp[-n+(_p)]._integer.value)) +#define PARAM_i(_p) (JIT.conv(&sp[-n+(_p)], GB_T_INTEGER), sp[-n+(_p)]._integer.value) +#define PARAM_l(_p) (JIT.conv(&sp[-n+(_p)], GB_T_LONG), sp[-n+(_p)]._long.value) +#define PARAM_g(_p) (JIT.conv(&sp[-n+(_p)], GB_T_SINGLE), sp[-n+(_p)]._single.value) +#define PARAM_f(_p) (JIT.conv(&sp[-n+(_p)], GB_T_FLOAT), sp[-n+(_p)]._float.value) +#define PARAM_p(_p) (JIT.conv(&sp[-n+(_p)], GB_T_POINTER), sp[-n+(_p)]._pointer.value) +#define PARAM_d(_p) (JIT.conv(&sp[-n+(_p)], GB_T_DATE), *(GB_DATE *)&sp[-n+(_p)]) +#define PARAM_s(_p) (JIT.conv(&sp[-n+(_p)], GB_T_STRING), *(GB_STRING *)&sp[-n+(_p)]) +#define PARAM_o(_p) (JIT.conv(&sp[-n+(_p)], GB_T_OBJECT), *(GB_OBJECT *)&sp[-n+(_p)]) +#define PARAM_v(_p) (JIT.conv(&sp[-n+(_p)], GB_T_VARIANT), *(GB_VARIANT *)&sp[-n+(_p)]) +#define PARAM_O(_p, _type) (JIT.conv(&sp[-n+(_p)], (GB_TYPE)(_type)), *(GB_OBJECT *)&sp[-n+(_p)]) + +#define PARAM_OPT(_p, _type, _default) (((_p) >= n || (sp[-n+(_p)].type == GB_T_VOID)) ? (_default) : PARAM_##_type(_p)) +#define PARAM_OPT_b(_p) PARAM_OPT(_p, b, 0) +#define PARAM_OPT_c(_p) PARAM_OPT(_p, c, 0) +#define PARAM_OPT_h(_p) PARAM_OPT(_p, h, 0) +#define PARAM_OPT_i(_p) PARAM_OPT(_p, i, 0) +#define PARAM_OPT_l(_p) PARAM_OPT(_p, l, 0) +#define PARAM_OPT_g(_p) PARAM_OPT(_p, g, 0) +#define PARAM_OPT_f(_p) PARAM_OPT(_p, f, 0) +#define PARAM_OPT_p(_p) PARAM_OPT(_p, p, 0) +#define PARAM_OPT_d(_p) PARAM_OPT(_p, d, ({ GB_DATE _v; _v.type = GB_T_DATE; _v.value.time = _v.value.date = 0; _v; })) +#define PARAM_OPT_s(_p) PARAM_OPT(_p, s, GET_CSTRING("", 0, 0)) +#define PARAM_OPT_o(_p) PARAM_OPT(_p, o, GET_OBJECT(0, GB_T_OBJECT)) +#define PARAM_OPT_v(_p) PARAM_OPT(_p, v, ({ GB_VARIANT _v; _v.type = GB_T_VARIANT; _v.value.type = GB_T_NULL; _v; })) +#define PARAM_OPT_O(_p, _type) (((_p) >= n || (sp[-n+(_p)].type == GB_T_VOID)) ? GET_OBJECT(0, GB_T_OBJECT) : PARAM_O(_p, _type)) + +#define OPT(_p, _n) ({ \ + uchar _opt = 0; \ + int _i; \ + GB_VALUE *_param = &sp[-n+(_p)]; \ + for (_i = 0; _i < (_n); _i++) \ + { \ + if (((_i + (_p)) >= n) || _param->type == GB_T_VOID) \ + _opt |= (1 << _i); \ + _param++; \ + } \ + _opt; \ +}) + +#define RETURN_b(_val) (GB.ReturnBoolean((int)_val)) +#define RETURN_c(_val) (GB.Return(GB_T_BYTE, (int)(_val))) +#define RETURN_h(_val) (GB.Return(GB_T_SHORT, (int)(_val))) +#define RETURN_i(_val) (GB.ReturnInteger(_val)) +#define RETURN_l(_val) (GB.ReturnLong(_val)) +#define RETURN_g(_val) (GB.ReturnSingle(_val)) +#define RETURN_f(_val) (GB.ReturnFloat(_val)) +#define RETURN_d(_val) ({ GB_DATE _v = (_val); GB.ReturnDate(&_v); }) +#define RETURN_p(_val) (GB.ReturnPointer(_val)) +#define RETURN_o(_val) ({ GB_OBJECT _v = (_val); GB.Return(_v.type, _v.value); }) +#define RETURN_v(_val) ({ GB_VARIANT _v = (_val); GB.ReturnVariant(&_v.value); }) +#define RETURN_s(_val) (*(GB_STRING *)GB.GetReturnValue() = (_val)) + +#define PUSH_b(_val) ({ sp->_boolean.value = ((_val) ? -1 : 0); sp->type = GB_T_BOOLEAN; sp++; }) +#define PUSH_c(_val) ({ uchar _v = (_val); sp->_integer.value = _v; sp->type = GB_T_BYTE; sp++; }) +#define PUSH_h(_val) ({ short _v = (_val); sp->_integer.value = _v; sp->type = GB_T_SHORT; sp++; }) +#define PUSH_i(_val) ({ sp->_integer.value = (_val); sp->type = GB_T_INTEGER; sp++; }) +#define PUSH_l(_val) ({ int64_t _v = (_val); sp->_long.value = _v; sp->type = GB_T_LONG; sp++; }) +#define PUSH_g(_val) ({ float _v = (_val); sp->_single.value = _v; sp->type = GB_T_SINGLE; sp++; }) +#define PUSH_f(_val) ({ double _v = (_val); sp->_float.value = _v; sp->type = GB_T_FLOAT; sp++; }) +#define PUSH_p(_val) ({ intptr_t _v = (_val); sp->_pointer.value = _v; sp->type = GB_T_POINTER; sp++; }) +#define PUSH_d(_val) ({ *((GB_DATE *)sp) = (_val); sp++; }) +#define PUSH_t(_val) ({ *((GB_STRING *)sp) = (_val); sp++; }) +#define PUSH_s(_val) ({ *((GB_STRING *)sp) = (_val); if (sp->type == GB_T_STRING) GB.RefString(sp->_string.value.addr); sp++; }) +#define PUSH_o(_val) ({ *((GB_OBJECT *)sp) = (_val); GB.Ref(sp->_object.value); sp++; }) +#define PUSH_v(_val) ({ *((GB_VARIANT *)sp) = (_val); GB.BorrowValue(sp); sp++; }) +#define PUSH_C(_val) ({ GB_VALUE_CLASS _v; _v.type = GB_T_CLASS; _v.class = (_val); *((GB_VALUE_CLASS *)sp) = _v; sp++; }) +#define PUSH_u(_val) ({ *sp = (_val); GB.BorrowValue(sp); sp++; }) +#define PUSH_V() ({ sp->type = GB_T_VOID; sp++; }) +#define PUSH_n(_val) ({ sp->type = GB_T_NULL; sp++; }) +#define PUSH_F(_val) ({ sp->type = GB_T_FUNCTION; ((GB_VALUE_FUNCTION *)sp)->class = ((GB_VALUE_FUNCTION *)sp)->object = NULL; sp++; }) + +enum +{ + FUNCTION_NULL, + FUNCTION_NATIVE, + FUNCTION_PRIVATE, + FUNCTION_PUBLIC, + FUNCTION_EVENT, + FUNCTION_EXTERN, + FUNCTION_UNKNOWN, + FUNCTION_CALL, + FUNCTION_SUBR +}; + +#define PUSH_PRIVATE_FUNCTION(_index) ({ \ + GB_VALUE_FUNCTION *f = (GB_VALUE_FUNCTION *)sp; \ + f->type = GB_T_FUNCTION; \ + f->class = CP; \ + f->object = OP; \ + f->kind = FUNCTION_PRIVATE; \ + f->index = (_index); \ + f->defined = 1; \ + GB.Ref(OP); \ + sp++; \ +}) + +#define POP_b() ({ sp--; sp->_boolean.value; }) +#define POP_c() ({ sp--; (uchar)sp->_integer.value; }) +#define POP_h() ({ sp--; (short)sp->_integer.value; }) +#define POP_i() ({ sp--; sp->_integer.value; }) +#define POP_l() ({ sp--; sp->_long.value; }) +#define POP_g() ({ sp--; sp->_single.value; }) +#define POP_f() ({ sp--; sp->_float.value; }) +#define POP_p() ({ sp--; sp->_pointer.value; }) +#define POP_d() ({ sp--; *((GB_DATE*)sp); }) +#define POP_s() ({ sp--; JIT.unborrow(sp); *((GB_STRING*)sp); }) +#define POP_t() ({ sp--; *((GB_STRING*)sp); }) +#define POP_BORROW_s() ({ sp--; *((GB_STRING*)sp); }) +#define POP_o() ({ sp--; JIT.unborrow(sp); *((GB_OBJECT*)sp); }) +#define POP_BORROW_o() ({ sp--; *((GB_OBJECT*)sp); }) +#define POP_v() ({ sp--; JIT.unborrow(sp); *((GB_VARIANT*)sp); }) +#define POP_BORROW_v() ({ sp--; *((GB_VARIANT*)sp); }) +#define POP_u() ({ sp--; JIT.unborrow(sp); *sp; }) +#define POP_BORROW_u() ({ sp--; *sp }) +#define POP_V() (sp--) + +#define BORROW_s(_val) ({ GB_STRING _v = (_val); if ((_v).type == GB_T_STRING) GB.RefString(_v.value.addr); _v; }) +#define BORROW_o(_val) ({ GB_OBJECT _v = (_val); GB.Ref(_v.value); _v; }) +#define BORROW_v(_val) ({ GB_VARIANT _v = (_val); GB.BorrowValue((GB_VALUE *)&_v); _v; }) + +#define RELEASE_s(_val) ({ GB_STRING _v = (_val); if ((_v).type == GB_T_STRING) GB.FreeString(&_v.value.addr); _v; }) +#define RELEASE_o(_val) ({ GB_OBJECT _v = (_val); GB.Unref(&_v.value); _v; }) +#define RELEASE_v(_val) ({ GB_VARIANT _v = (_val); GB.ReleaseValue((GB_VALUE *)&_v); _v; }) + +#define RELEASE_FAST_s(_val) ({ if ((_val).type == GB_T_STRING) GB.FreeString(&(_val).value.addr); }) +#define RELEASE_FAST_o(_val) GB.Unref(&((_val).value)) +#define RELEASE_FAST_v(_val) GB.ReleaseValue((GB_VALUE *)&(_val)) + +#define CLASS(_class) ({ JIT.load_class((void *)_class); (GB_CLASS)(_class); }) + +#define CONSTANT_s(_addr, _len) GET_CSTRING((char *)_addr, 0, _len) +#define CONSTANT_t(_addr, _len) GET_CSTRING(GB.Translate((const char *)_addr), 0, strlen(temp.value.addr)) + +#define GET_CHAR(_char) GET_CSTRING(&JIT.char_table[(_char) * 2], 0, 1) + +#define GET_STRING(_addr, _start, _len) ({ \ + GB_STRING temp; \ + temp.type = GB_T_STRING; \ + temp.value.addr = (char *)(_addr); \ + temp.value.start = (_start); \ + temp.value.len = (_len); \ + temp; }) + +#define GET_CSTRING(_addr, _start, _len) ({ \ + GB_STRING temp; \ + temp.type = GB_T_CSTRING; \ + temp.value.addr = (char *)(_addr); \ + temp.value.start = (_start); \ + temp.value.len = (_len); \ + temp; }) + +#define GET_OBJECT(_addr, _type) ({ \ + GB_OBJECT temp; \ + temp.type = (GB_TYPE)(_type); \ + temp.value = (_addr); \ + temp; }) + +#define GET_DATE(_addr) ({ \ + GB_DATE temp; \ + int *_val = (int *)(_addr); \ + temp.type = GB_T_DATE; \ + temp.value.date = _val[0]; \ + temp.value.time = _val[1]; \ + temp; \ +}) + +#define GET_VARIANT(_val) ({ \ + GB_VARIANT temp; \ + temp.type = GB_T_VARIANT; \ + temp.value = (_val); \ + if (temp.value.type == GB_T_VOID) temp.value.type = GB_T_NULL; \ + temp; }) + +#define GET_FUNCTION(_pc) ({ CALL_UNKNOWN(_pc); POP_u(); }) + +#define GET_STRING_ADDR(_val) ({ \ + GB_STRING temp = (_val); \ + temp.value.addr + temp.value.start; \ +}) + +// TODO: automatic class +#define ADDR(_val) ({ \ + char *_object = (_val).value; \ + if (!_object) THROW(E_NULL); \ + _object; \ +}) + +#define ADDR_CHECK(_check, _val) ({ \ + char *_object = (_val).value; \ + if (!_object) THROW(E_NULL); \ + if (((int (*)())_check)(_object)) THROW(E_IOBJECT); \ + _object; \ +}) + +#define ADDR_UNSAFE(_val) ((char *)((_val).value)) + +#define GET_b(_addr) (*(bool *)(_addr)) +#define GET_c(_addr) (*(uchar *)(_addr)) +#define GET_h(_addr) (*(short *)(_addr)) +#define GET_i(_addr) (*(int *)(_addr)) +#define GET_l(_addr) (*(int64_t *)(_addr)) +#define GET_g(_addr) (*(float *)(_addr)) +#define GET_f(_addr) (*(double *)(_addr)) +#define GET_d(_addr) GET_DATE(_addr) +#define GET_p(_addr) (*(intptr_t *)(_addr)) +#define GET_s(_addr) GET_STRING((*(char **)(_addr)), 0, GB.StringLength(temp.value.addr)) +#define GET_o(_addr, _type) GET_OBJECT((*(char **)(_addr)), _type) +#define GET_v(_addr) GET_VARIANT((*(GB_VARIANT_VALUE *)(_addr))) +#define GET_S(_ref, _addr, _type) GET_OBJECT(JIT.static_struct((_ref), (_type), (_addr)), _type) +#define GET_A(_class, _ref, _addr, _type, _desc) ({ \ + SP = sp; \ + GET_OBJECT(JIT.static_array((_class), (_ref), (void *)(_desc), (_addr)), _type); \ +}) + +#define SET_b(_addr, _val) (GET_b(_addr) = ((_val) ? -1 : 0)) +#define SET_c(_addr, _val) (GET_c(_addr) = (_val)) +#define SET_h(_addr, _val) (GET_h(_addr) = (_val)) +#define SET_i(_addr, _val) (GET_i(_addr) = (_val)) +#define SET_l(_addr, _val) (GET_l(_addr) = (_val)) +#define SET_g(_addr, _val) (GET_g(_addr) = (_val)) +#define SET_f(_addr, _val) (GET_f(_addr) = (_val)) +#define SET_d(_addr, _val) ({ GB_DATE_VALUE temp = (_val).value; int *_d = (int *)(_addr); _d[0] = temp.date; _d[1] = temp.time; }) +#define SET_p(_addr, _val) (GET_p(_addr) = (_val)) +#define SET_s(_addr, _val) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreString((GB_STRING *)&temp, (char **)(_addr)); }) +#define SET_o(_addr, _val) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreObject((GB_OBJECT *)&temp, (void **)(_addr)); }) +#define SET_v(_addr, _val) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreVariant((GB_VARIANT *)&temp, (GB_VARIANT_VALUE *)(_addr)); }) +#define SET_SA(_class, _addr, _ctype, _val) ({ \ + GB_VALUE temp = (GB_VALUE)(_val); \ + int ctype = (_ctype); \ + GB.BorrowValue(&temp); \ + JIT.value_class_write((_class), &temp, (_addr), *(JIT_CTYPE *)&ctype); \ + GB.ReleaseValue(&temp); \ +}) + +#define GET_ARRAY_UNSAFE(_type, _array, _index) ({ \ + GB_ARRAY_IMPL *_a = (_array).value; \ + int _i = (_index); \ + &((_type *)_a->data)[_i]; \ +}) + +#define GET_ARRAY(_type, _array, _index) ({ \ + GB_ARRAY_IMPL *_a = (_array).value; \ + int _i = (_index); \ + if (!_a) THROW(E_NOBJECT); \ + if (_a->ref == _a) THROW(E_SARRAY); \ + if (_i < 0 || _i >= _a->count) THROW(E_BOUND); \ + &((_type *)_a->data)[_i]; \ +}) + +#define PUSH_ARRAY(_type, _array, _index, _unsafe) *GET_ARRAY##_unsafe(_type, _array, _index) + +#define PUSH_ARRAY_b(_array, _index, _unsafe) PUSH_ARRAY(bool, _array, _index, _unsafe) +#define PUSH_ARRAY_c(_array, _index, _unsafe) PUSH_ARRAY(uchar, _array, _index, _unsafe) +#define PUSH_ARRAY_h(_array, _index, _unsafe) PUSH_ARRAY(short, _array, _index, _unsafe) +#define PUSH_ARRAY_i(_array, _index, _unsafe) PUSH_ARRAY(int, _array, _index, _unsafe) +#define PUSH_ARRAY_l(_array, _index, _unsafe) PUSH_ARRAY(int64_t, _array, _index, _unsafe) +#define PUSH_ARRAY_g(_array, _index, _unsafe) PUSH_ARRAY(float, _array, _index, _unsafe) +#define PUSH_ARRAY_f(_array, _index, _unsafe) PUSH_ARRAY(double, _array, _index, _unsafe) +#define PUSH_ARRAY_p(_array, _index, _unsafe) PUSH_ARRAY(intptr_t, _array, _index, _unsafe) +#define PUSH_ARRAY_d(_array, _index, _unsafe) GET_DATE(GET_ARRAY##_unsafe(int, _array, _index)) +#define PUSH_ARRAY_s(_array, _index, _unsafe) GET_STRING(PUSH_ARRAY(char *, _array, _index, _unsafe), 0, GB.StringLength(temp.value.addr)) +#define PUSH_ARRAY_o(_array, _index, _unsafe) GET_OBJECT(PUSH_ARRAY(void *, _array, _index, _unsafe), GB_T_OBJECT) +#define PUSH_ARRAY_O(_array, _index, _type, _unsafe) GET_OBJECT(PUSH_ARRAY(void *, _array, _index, _unsafe), _type) +#define PUSH_ARRAY_v(_array, _index, _unsafe) GET_VARIANT(PUSH_ARRAY(GB_VARIANT_VALUE, _array, _index, _unsafe)) + +#define POP_ARRAY(_type, _array, _index, _val, _unsafe) (*GET_ARRAY##_unsafe(_type, _array, _index) = (_val)) + +#define POP_ARRAY_b(_array, _index, _val, _unsafe) POP_ARRAY(bool, _array, _index, ((_val) ? -1 : 0), _unsafe) +#define POP_ARRAY_c(_array, _index, _val, _unsafe) POP_ARRAY(uchar, _array, _index, _val, _unsafe) +#define POP_ARRAY_h(_array, _index, _val, _unsafe) POP_ARRAY(short, _array, _index, _val, _unsafe) +#define POP_ARRAY_i(_array, _index, _val, _unsafe) POP_ARRAY(int, _array, _index, _val, _unsafe) +#define POP_ARRAY_l(_array, _index, _val, _unsafe) POP_ARRAY(int64_t, _array, _index, _val, _unsafe) +#define POP_ARRAY_g(_array, _index, _val, _unsafe) POP_ARRAY(float, _array, _index, _val, _unsafe) +#define POP_ARRAY_f(_array, _index, _val, _unsafe) POP_ARRAY(double, _array, _index, _val, _unsafe) +#define POP_ARRAY_p(_array, _index, _val, _unsafe) POP_ARRAY(intptr_t, _array, _index, _val, _unsafe) +#define POP_ARRAY_d(_array, _index, _val, _unsafe) ({ GB_DATE temp = (GB_DATE)(_val); int *_p = (int *)GET_ARRAY##_unsafe(int, _array, _index)); _p[0] = temp.value.date; _p[1] = temp.value.time; }) +#define POP_ARRAY_s(_array, _index, _val, _unsafe) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreString((GB_STRING *)&temp, GET_ARRAY##_unsafe(char *, _array, _index)); }) +#define POP_ARRAY_o(_array, _index, _val, _unsafe) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreObject((GB_OBJECT *)&temp, GET_ARRAY##_unsafe(void *, _array, _index)); }) +#define POP_ARRAY_v(_array, _index, _val, _unsafe) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreVariant((GB_VARIANT *)&temp, GET_ARRAY##_unsafe(GB_VARIANT_VALUE, _array, _index)); }) + +#define CONV(_val, _src, _dest, _type) ({ PUSH_##_src(_val); SP = sp; JIT.conv(sp - 1, (GB_TYPE)(_type)); POP_##_dest(); }) + +#define CONV_d_b(_val) ({ GB_DATE _v = (_val); _v.value.date != 0 || _v.value.time != 0; }) +#define CONV_d_c(_val) ((uchar)((_val).value.date)) +#define CONV_d_h(_val) ((short)((_val).value.date)) +#define CONV_d_i(_val) ((_val).value.date) +#define CONV_d_l(_val) ((int64_t)((_val).value.date)) +#define CONV_d_g(_val) ({ GB_DATE _v = (_val); (float)((float)_v.value.date + (float)_v.value.time / 86400000.0); }) +#define CONV_d_f(_val) ({ GB_DATE _v = (_val); (double)((double)_v.value.date + (double)_v.value.time / 86400000.0); }) +#define CONV_d_p(_val) (THROW_TYPE(T_DATE, T_POINTER)) +#define CONV_d_s(_val) CONV(_val, d, s, GB_T_STRING) +#define CONV_d_o(_val) (THROW_TYPE(T_DATE, T_OBJECT)) + +#define CONV_o_O(_val, _class) CONV(_val, o, o, CLASS(_class)) + +#define CONV_o(_val) ({ GB_OBJECT temp = (_val); temp.type = GB_T_OBJECT; temp; }) + +#define GET_NULL_o() ({ GB_OBJECT temp; temp.type = GB_T_OBJECT; temp.value = NULL; temp; }) +#define GET_NULL_v() ({ GB_VARIANT temp; temp.type = GB_T_VARIANT; temp.value.type = GB_T_NULL; temp; }) +#define GET_NULL_s() GET_CSTRING("", 0, 0) +#define GET_NULL_d() ({ GB_DATE temp; temp.type = GB_T_DATE; temp.value.date = 0; temp.value.time = 0; temp; }) +#define GET_NULL_p() ((intptr_t)0) + +#define PUSH_GOSUB(_label) ({ \ + GB_VALUE_GOSUB *_p = (GB_VALUE_GOSUB *)sp; \ + _p->type = GB_T_VOID; \ + _p->addr = &&_label; \ + _p->gp = gp; \ + gp = _p; \ + sp++; \ +}) + +#define LEAVE_TRY() ERROR_leave(__err) + +#define RETURN_LEAVE_TRY() ({ \ + void *_addr = &&__L0; \ + if (!gp) { LEAVE_TRY(); goto __RETURN; } \ + _addr = gp->addr; \ + sp = (GB_VALUE *)gp; \ + gp = gp->gp; \ + goto *_addr; \ +}) + +#define RETURN() ({ \ + void *_addr; \ + if (!gp) goto __RETURN; \ + _addr = gp->addr; \ + sp = (GB_VALUE *)gp; \ + gp = gp->gp; \ + goto *_addr; \ +}) + +#define RELEASE_GOSUB() ({ \ + while(gp) \ + { \ + gp = gp->gp; \ + SP--; \ + } \ +}) + +#define CALL_SUBR(_pc, _func) ({ PC = &pc[_pc]; SP = sp; (*((EXEC_FUNC)_func))(); sp = SP; }) +#define CALL_SUBR_CODE(_pc, _func, _code) ({ PC = &pc[_pc]; SP = sp; (*((EXEC_FUNC_CODE)_func))(_code); sp = SP; }) +#define CALL_SUBR_UNKNOWN(_pc) ({ JIT.call_unknown(&pc[_pc], &sp); }) + +#define CALL_PUSH_ARRAY(_pc, _code) ({ PC = &pc[_pc]; SP = sp; (*(EXEC_FUNC)JIT.push_array)(_code); sp = SP; }) +#define CALL_POP_ARRAY(_pc, _code) ({ PC = &pc[_pc]; SP = sp; (*(EXEC_FUNC)JIT.pop_array)(_code); sp = SP; sp++; }) + +#define PUSH_UNKNOWN(_pc) ({ PC = &pc[_pc]; SP = sp; JIT.push_unknown(); sp = SP; }) +//#define PUSH_UNKNOWN(_pc) CALL_UNKNOWN(_pc) +#define POP_UNKNOWN(_pc) ({ PC = &pc[_pc]; SP = sp; JIT.pop_unknown(); sp = SP; }) + +#define PUSH_COMPLEX(_val) ({ PUSH_f(_val); SP = sp; JIT.push_complex(); POP_o(); }) + +#define GET_LAST() GET_OBJECT(*(JIT.event_last), GB_T_OBJECT) + +#define GET_ME(_type) ({ \ + GB_OBJECT _temp; \ + if (OP) \ + { \ + GB_VALUE_OBJECT *_p = (GB_VALUE_OBJECT *)&_temp; \ + _p->type = (GB_TYPE)(_type); \ + _p->object = OP; \ + } \ + else \ + { \ + GB_VALUE_CLASS *_p = (GB_VALUE_CLASS *)&_temp; \ + _p->type = GB_T_CLASS; \ + _p->class = (GB_TYPE)(_type); \ + } \ + _temp; }) + +#define GET_SUPER(_type) ({ \ + GB_OBJECT _temp; \ + if (OP) \ + { \ + GB_VALUE_OBJECT *_p = (GB_VALUE_OBJECT *)&_temp; \ + _p->type = (GB_TYPE)(_type); \ + _p->object = OP; \ + _p->super = *JIT.exec_super; \ + } \ + else \ + { \ + GB_VALUE_CLASS *_p = (GB_VALUE_CLASS *)&_temp; \ + _p->type = GB_T_CLASS; \ + _p->class = (GB_TYPE)(_type); \ + _p->super = *JIT.exec_super; \ + } \ + *JIT.exec_super = sp; \ + _temp; }) + +#define LEAVE_SUPER() { \ + while (sp < *JIT.exec_super) \ + *JIT.exec_super = ((GB_VALUE_OBJECT *)*JIT.exec_super)->super; \ +} + +#define CALL_UNKNOWN(_pc) ({ JIT.call_unknown(&pc[_pc], &sp); }) + +#define ENUM_FIRST(_code, _plocal, _penum) ({ GB.Unref(&(_penum).value); (_penum).type = 0; JIT.enum_first(_code, (GB_VALUE *)&_plocal, (GB_VALUE *)&_penum); }) + +#define ENUM_NEXT(_code, _plocal, _penum, _label) ({ \ + SP = sp; \ + bool _t = JIT.enum_next(_code, (GB_VALUE *)&_plocal, (GB_VALUE *)&_penum); \ + sp = SP; \ + if (_t) goto _label; \ +}) + +#define CALL_MATH(_func) ({ double _v = _func; if (!isfinite(_v)) THROW(E_MATH); _v; }) +#define CALL_MATH_UNSAFE(_func) (_func) + +#define ERROR_current (*(ERROR_CONTEXT **)(JIT.error_current)) +#define ERROR_handler (*(ERROR_HANDLER **)(JIT.error_handler)) +#define ERROR_reset JIT.error_reset + +#define QUIT(_code) (SP = sp, JIT.exec_quit(_code)) + +#define RAISE_EVENT(_event, _narg) ({ SP = sp; GB.Raise(OP, (_event), -(_narg)); sp = SP; }) +#define RAISE_UNKNOWN_EVENT(_pc) ({ PC = &pc[_pc]; SP = sp; JIT.push_unknown_event(0); sp = SP; }) + +#define MATH_ABS(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v < 0) ? (- _v) : _v; \ +}) + +#define MATH_SGN(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v < 0) ? -1 : ((_v > 0) ? 1 : 0); \ +}) + +#define MATH_FIX_g(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v >= 0) ? floorf(_v) : -floorf(fabsf(_v)); \ +}) + +#define MATH_FIX_f(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v >= 0) ? floor(_v) : -floor(fabs(_v)); \ +}) + +#define GET_STRING(_addr, _start, _len) ({ \ + GB_STRING temp; \ + temp.type = GB_T_STRING; \ + temp.value.addr = (char *)(_addr); \ + temp.value.start = (_start); \ + temp.value.len = (_len); \ + temp; }) + +#define SUBR_LEFT(_str, _len) ({ \ + GB_STRING temp = (_str); \ + int len = (_len); \ + if (len < 0) \ + len += (_str).value.len; \ + if (len < 0) \ + temp.value.len = 0; \ + else if (len < temp.value.len) \ + temp.value.len = len; \ + temp; }) + +#define SUBR_RIGHT(_str, _len) ({ \ + GB_STRING temp = (_str); \ + int len = (_len); \ + if (len < 0) \ + len += (_str).value.len; \ + int new_len = MIN_MAX(len, 0, temp.value.len); \ + temp.value.start += temp.value.len - new_len; \ + temp.value.len = new_len; \ + temp; }) + +#define SUBR_MID_END(_str, _pos, _pc) ({ \ + GB_STRING temp = (_str); \ + int pos = (_pos) - 1; \ + if (_pos < 0) THROW_PC(E_ARG, _pc); \ + temp.value.len -= pos; \ + if (temp.value.len <= 0) \ + temp.value.len = 0; \ + else \ + temp.value.start += pos; \ + temp; }) + +#define SUBR_MID(_str, _pos, _len, _pc) ({ \ + GB_STRING temp = (_str); \ + int pos = (_pos) - 1; \ + if (pos < 0) THROW_PC(E_ARG, _pc); \ + int len = (_len); \ + if (len < 0) \ + len = MAX(0, temp.value.len - pos + len); \ + len = MIN_MAX(len, 0, temp.value.len - pos); \ + if (len <= 0) \ + temp.value.len = 0; \ + else \ + { \ + temp.value.start += pos; \ + temp.value.len = len; \ + } \ + temp; }) + +#define MATH_ADD_UNSAFE(_ctype, _expr1, _expr2) ({_ctype _a = (_expr1); _ctype _b = (_expr2); _a + _b;}) +#define MATH_SUB_UNSAFE(_ctype, _expr1, _expr2) ({_ctype _a = (_expr1); _ctype _b = (_expr2); _a - _b;}) +#define MATH_MUL_UNSAFE(_ctype, _expr1, _expr2) ({_ctype _a = (_expr1); _ctype _b = (_expr2); _a * _b;}) + +#if DO_NOT_CHECK_OVERFLOW + +#define MATH_ADD MATH_ADD_UNSAFE +#define MATH_SUB MATH_SUB_UNSAFE +#define MATH_MUL MATH_MUL_UNSAFE + +#else + +#define MATH_ADD(_ctype, _expr1, _expr2) \ +({ \ + _ctype result; \ + if (__builtin_add_overflow((_ctype)(_expr1), (_ctype)(_expr2), &result)) \ + THROW(E_OVERFLOW); \ + result; \ +}) + +#define MATH_SUB(_ctype, _expr1, _expr2) \ +({ \ + _ctype result; \ + if (__builtin_sub_overflow((_ctype)(_expr1), (_ctype)(_expr2), &result)) \ + THROW(E_OVERFLOW); \ + result; \ +}) + +#define MATH_MUL(_ctype, _expr1, _expr2) \ +({ \ + _ctype result; \ + if (__builtin_mul_overflow((_ctype)(_expr1), (_ctype)(_expr2), &result)) \ + THROW(E_OVERFLOW); \ + result; \ +}) + +#endif diff --git a/main/lib/jit/gb.jit/jit_after.h b/main/lib/jit/gb.jit/jit_after.h new file mode 100644 index 00000000..60dbff59 --- /dev/null +++ b/main/lib/jit/gb.jit/jit_after.h @@ -0,0 +1,21 @@ +static VALUE *_jit_print_catch(VALUE **psp, VALUE *sp, VALUE *ssp, void *cp, void *fp, bool has_catch_finally) +{ + CP = cp; + FP = fp; + if (has_catch_finally) + JIT.error_set_last(FALSE); + if (SP > sp) sp = SP; else SP = sp; + LEAVE_SUPER(); + if (sp > ssp) { JIT.release_many(sp, sp - ssp); SP = sp = ssp; } + return sp; +} + +static VALUE *_jit_end_try(VALUE **psp, VALUE *sp) +{ + if (SP > sp) sp = SP; else SP = sp; + LEAVE_SUPER(); + if (sp > EP) { JIT.release_many(sp, sp - EP); SP = sp = EP; } + JIT.set_got_error(1); + JIT.error_set_last(FALSE); + return sp; +} \ No newline at end of file diff --git a/main/lib/jit/gb_str.c b/main/lib/jit/gb_str.c new file mode 100644 index 00000000..e45628cb --- /dev/null +++ b/main/lib/jit/gb_str.c @@ -0,0 +1,227 @@ +/*************************************************************************** + + gb_str.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_STR_C + +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "gb_str.h" + +//#define DEBUG + +static char *_free_later = NULL; +static char *_last_str = NULL; +static int _last_len = 0; + +#ifdef DEBUG +static int _count = 0; +#endif + +void STR_free(char *str) +{ + if (!str) + return; + +#ifdef DEBUG + _count--; + fprintf(stderr, "free %p -> %d\n", str, _count); +#endif + GB.Free((void **)&(str)); +} + +void STR_vadd(char **str, const char *fmt, va_list args) +{ + va_list copy; + int len, add; + char *new; + + va_copy(copy, args); + add = vsnprintf(NULL, 0, fmt, args); + + if (*str) + len = (*str == _last_str ? _last_len : strlen(*str)); + else + len = 0; + + GB.Alloc((void **)&new, len + add + 1); + if (*str) strcpy(new, *str); + + vsprintf(&new[len], fmt, copy); + va_end(copy); + +#ifdef DEBUG + if (*str) { + _count--; + fprintf(stderr, "free %p -> %d\n", *str, _count); + } + _count++; + fprintf(stderr, "alloc %p -> %d\n", new, _count); +#endif + if (*str) GB.Free((void **)str); + *str = new; + + _last_str = new; + _last_len = len + add; +} + + +void STR_add(char **str, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + STR_vadd(str, fmt, args); + va_end(args); +} + + +char *STR_copy_len(const char *str, int len) +{ + char *cpy; + + GB.Alloc((void **)&cpy, len + 1); + memcpy(cpy, str, len + 1); +#ifdef DEBUG + _count++; + fprintf(stderr, "alloc %p -> %d\n", cpy, _count); +#endif + return cpy; +} + + +char *STR_copy(const char *str) +{ + return STR_copy_len(str, strlen(str)); +} + + +/*static char *str_add(char *d, const char *s) +{ + for(;;) + { + if ((*d = *s) == 0) + break; + + d++; + s++; + } + + return d; +} + + +char *STR_cat(const char *str, ...) +{ + va_list args; + char *cpy; + char *p; + int len = 0; + + va_start(args, str); + + p = (char *)str; + while (p) + { + len += strlen(p); + p = va_arg(args, char *); + } + + va_end(args); + + GB.Alloc((void **)&cpy, len + 1); + p = cpy; + + va_start(args, str); + + while (str) + { + p = str_add(p, str); + str = va_arg(args, char *); + } + + va_end(args); + + return cpy; +}*/ + + +char *STR_upper(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = toupper(*p); + p++; + } + + return s; +} + + +char *STR_lower(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = tolower(*p); + p++; + } + + return s; +} + + +char *STR_free_later(char *str) +{ + if (_free_later) + STR_free(_free_later); + _free_later = str; + return str; +} + + +char *STR_print(const char *fmt, ...) +{ + va_list args; + char *str = NULL; + + va_start(args, fmt); + STR_vadd(&str, fmt, args); + va_end(args); + return str; +} + diff --git a/main/lib/jit/gb_str.h b/main/lib/jit/gb_str.h new file mode 100644 index 00000000..b69bb275 --- /dev/null +++ b/main/lib/jit/gb_str.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb_str.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_STR_H +#define __GB_STR_H + +#include + +char *STR_copy(const char *str); +char *STR_copy_len(const char *str, int len); +char *STR_cat(const char *str, ...); +char *STR_upper(const char *str); +char *STR_lower(const char *str); + +void STR_free(char *str); +char *STR_free_later(char *str); + +void STR_add(char **str, const char *fmt, ...); +char *STR_print(const char *fmt, ...); +void STR_vadd(char **str, const char *fmt, va_list args); + +#endif diff --git a/main/lib/jit/gbc_reserved.c b/main/lib/jit/gbc_reserved.c new file mode 100644 index 00000000..12c53de1 --- /dev/null +++ b/main/lib/jit/gbc_reserved.c @@ -0,0 +1,30 @@ +/*************************************************************************** + + gbc_reserved.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_RESERVED_C + +#define __GB_COMMON_CASE_H +#include "main.h" +#define PROJECT_COMP +#include "gb_reserved_temp.h" + diff --git a/main/lib/jit/jit.c b/main/lib/jit/jit.c new file mode 100644 index 00000000..4a9d8b02 --- /dev/null +++ b/main/lib/jit/jit.c @@ -0,0 +1,567 @@ +/*************************************************************************** + + jit.c + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_C + +#include +#include +#include +#include +#include + +#include "gb_str.h" +#include "jit.h" + +typedef + struct { + const char *name; + char type; + } + CLASS_TYPE; + +CLASS *JIT_class; +char *JIT_prefix; +bool JIT_last_print_is_label; + +static char *_buffer = NULL; +static char *_buffer_decl = NULL; +static char *_buffer_body = NULL; + +static bool _decl_null_string = FALSE; +static bool _decl_null_date = FALSE; +static bool _decl_null_object = FALSE; +static bool _decl_null_variant = FALSE; + +/*static const CLASS_TYPE _class_type[] = { + { "Boolean[]", T_BOOLEAN }, + { "Byte[]", T_BYTE }, + { "Short[]", T_SHORT }, + { "Integer[]", T_INTEGER }, + { "Long[]", T_LONG }, + { "Single[]", T_SINGLE }, + { "Float[]", T_FLOAT }, + { "Date[]", T_DATE }, + { "String[]", T_STRING }, + { "Pointer[]", T_POINTER }, + { "Object[]", T_OBJECT }, + { "Variant[]", T_VARIANT }, + { NULL, 0 } +};*/ + +static const char *_type_name[] = +{ + "V" , "b", "c", "h", "i", "l", "g", "f", + "d", "s", "t", "p", "v", "F", "C", "n", + "o", "u" +}; + +static const char *_gtype_name[] = +{ + "GB_T_VOID" , "GB_T_BOOLEAN", "GB_T_BYTE", "GB_T_SHORT", "GB_T_INTEGER", "GB_T_LONG", "GB_T_SINGLE", "GB_T_FLOAT", + "GB_T_DATE", "GB_T_STRING", "GB_T_CSTRING", "GB_T_POINTER", "GB_T_VARIANT", "?", "GB_T_CLASS", "?", + "GB_T_OBJECT" +}; + +static const char *_ctype_name[] = +{ + "void" , "bool", "uchar", "short", "int", "int64_t", "float", "double", + "GB_DATE", "GB_STRING", "GB_STRING", "intptr_t", "GB_VARIANT", "?", "void *", "?", + "GB_OBJECT", "GB_VALUE", "?" +}; + +const char *JIT_get_type(TYPE type) +{ + return _type_name[TYPEID(type)]; +} + +const char *JIT_get_gtype(TYPE type) +{ + return _gtype_name[TYPEID(type)]; +} + +const char *JIT_get_ctype(TYPE type) +{ + return _ctype_name[TYPEID(type)]; +} + +TYPE JIT_ctype_to_type(CLASS *class, CTYPE ctype) +{ + if (ctype.id == T_OBJECT && ctype.value >= 0) + return (TYPE)(class->load->class_ref[ctype.value]); + else if (ctype.id == TC_ARRAY) + { + CLASS_ARRAY *desc = class->load->array[ctype.value]; + return (TYPE)JIT.get_array_class(class, *(JIT_CTYPE *)&desc->ctype); + } + else if (ctype.id == TC_STRUCT) + return (TYPE)(class->load->class_ref[ctype.value]); + else + return (TYPE)(ctype.id); +} + + +static void JIT_begin(void) +{ + char *p; + + JIT_prefix = STR_lower(JIT_class->name); + + p = JIT_prefix; + while (*p) + { + if (*p == ':') + *p = '$'; + p++; + } + + _buffer = NULL; + _buffer_decl = NULL; + JIT_print("\n//////// %s\n\n", JIT_class->name); +} + +static char *JIT_end(void) +{ + char *result = _buffer; + + STR_free(JIT_prefix); + _buffer = NULL; + + GB.FreeStringLater(result); + return result; +} + +static void declare_implementation(FUNCTION *func, int index) +{ + int i; + int nopt; + int opt; + const char *vol = func->error ? "volatile " : ""; + + JIT_print("static %s jit_%s_%d_(", JIT_get_ctype(func->type), JIT_prefix, index); + + for (i = 0; i < func->npmin; i++) + { + if (i) JIT_print(","); + JIT_print("%s%s p%d", vol, JIT_get_ctype(func->param[i].type), i); + } + + if (i < func->n_param) + { + opt = nopt = 0; + + for (; i < func->n_param; i++) + { + if (i) JIT_print(","); + + if (nopt == 0) + { + JIT_print("uchar o%d,", opt); + opt++; + } + + JIT_print("%s%s p%d", vol, JIT_get_ctype(func->param[i].type), i); + + nopt++; + if (nopt >= 8) + nopt = 0; + } + } + + if (func->vararg) + { + if (func->n_param) + JIT_print(","); + JIT_print("uchar nv,GB_VALUE *v"); + } + + JIT_print(")"); +} + + +void JIT_declare_func(FUNCTION *func, int index) +{ + JIT_print("void jit_%s_%d(uchar n);\n", JIT_prefix, index); + + declare_implementation(func, index); + JIT_print(";\n"); +} + + +const char *JIT_get_default_value(TYPE type) +{ + switch(TYPEID(type)) + { + case T_DATE: + + if (!_decl_null_date) + { + JIT_print_decl(" const GB_DATE null_date = {GB_T_DATE};\n"); + _decl_null_date = TRUE; + } + return "null_date"; + + case T_STRING: + + if (!_decl_null_string) + { + JIT_print_decl(" const GB_STRING null_string = {GB_T_STRING};\n"); + _decl_null_string = TRUE; + } + return "null_string"; + + case T_OBJECT: + + if (!_decl_null_object) + { + JIT_print_decl(" const GB_OBJECT null_object = {GB_T_OBJECT};\n"); + _decl_null_object = TRUE; + } + return "null_object"; + + case T_VARIANT: + if (!_decl_null_variant) + { + JIT_print_decl(" const GB_VARIANT null_variant = {GB_T_VARIANT,{GB_T_NULL}};\n"); + _decl_null_variant = TRUE; + } + return "null_variant"; + + default: + return "0"; + } +} + + +static bool JIT_translate_func(FUNCTION *func, int index) +{ + int i; + TYPE type; + int nopt; + const char *def; + const char *vol = func->error ? "volatile " : ""; + + if (func->debug) + JIT_section(func->debug->name); + + JIT_print("void jit_%s_%d(uchar n)\n{\n", JIT_prefix, index); + + if (func->n_param || func->vararg) + JIT_print(" VALUE *sp = *((VALUE **)%p);\n", JIT.sp); + + JIT_print(" "); + + if (!TYPE_is_void(func->type)) + JIT_print("RETURN_%s(", JIT_get_type(func->type)); + + JIT_print("jit_%s_%d_(", JIT_prefix, index); + + for (i = 0; i < func->npmin; i++) + { + if (i) JIT_print(","); + type = func->param[i].type; + if (TYPE_is_pure_object(type)) + JIT_print("PARAM_O(%d, CLASS(%p))", i, type); + else + JIT_print("PARAM_%s(%d)", JIT_get_type(type), i); + } + + if (i < func->n_param) + { + nopt = 0; + + for (; i < func->n_param; i++) + { + if (i) JIT_print(","); + + if (nopt == 0) + JIT_print("OPT(%d,%d),", i, Min(func->n_param, i + 8) - i); + + type = func->param[i].type; + if (TYPE_is_pure_object(type)) + JIT_print("PARAM_OPT_O(%d, CLASS(%p))", i, type); + else + JIT_print("PARAM_OPT_%s(%d)", JIT_get_type(type), i); + + nopt++; + if (nopt >= 8) + nopt = 0; + } + } + + if (func->vararg) + { + if (func->n_param) + JIT_print(","); + JIT_print("n - %d,&sp[-n+%d]", i, i); + } + + if (!TYPE_is_void(func->type)) + JIT_print(")"); + + JIT_print(");\n"); + JIT_print("}\n\n"); + + declare_implementation(func, index); + JIT_print("\n{\n"); + + _buffer_decl = NULL; + _buffer_body = NULL; + _decl_null_date = FALSE; + _decl_null_string = FALSE; + _decl_null_object = FALSE; + _decl_null_variant = FALSE; + + for (i = -1; i < func->n_local; i++) + { + if (i < 0) + { + if (TYPE_is_void(func->type)) + continue; + type = func->type; + def = JIT_get_default_value(type); + JIT_print_decl(" %s r = ", JIT_get_ctype(type)); + } + else + { + type = JIT_ctype_to_type(JIT_class, func->local[i].type); + def = JIT_get_default_value(type); + JIT_print_decl(" %s%s l%d = ", vol, JIT_get_ctype(type), i); + } + + JIT_print_decl(def); + JIT_print_decl(";\n"); + } + + for (i = 0; i < func->n_param; i++) + { + type = func->param[i].type; + switch(TYPEID(type)) + { + case T_STRING: case T_OBJECT: case T_VARIANT: + JIT_print_body(" BORROW_%s(p%d);\n", JIT_get_type(type), i); + } + } + + if (JIT_translate_body(func, index)) + return TRUE; + + if (!TYPE_is_void(func->type)) + { + switch(TYPEID(func->type)) + { + case T_STRING: + case T_OBJECT: + case T_VARIANT: + JIT_print_body(" JIT.unborrow((GB_VALUE *)&r);\n"); + break; + } + + JIT_print_body(" return r;\n"); + } + else + JIT_print_body(" return;\n"); + + _buffer = GB.AddString(_buffer, _buffer_decl, GB.StringLength(_buffer_decl)); + JIT_print("\n"); + _buffer = GB.AddString(_buffer, _buffer_body, GB.StringLength(_buffer_body)); + + GB.FreeString(&_buffer_decl); + GB.FreeString(&_buffer_body); + + JIT_print("}\n"); + + return FALSE; +} + + +void JIT_vprint(char **buffer, const char *fmt, va_list args) +{ + int len, add; + va_list copy; + + va_copy(copy, args); + add = vsnprintf(NULL, 0, fmt, copy); + va_end(copy); + + len = GB.StringLength(*buffer); + + *buffer = GB.ExtendString(*buffer, len + add); + + vsprintf(*buffer + len, fmt, args); + + JIT_last_print_is_label = (strncmp(fmt, "__L", 3) == 0); +} + + +void JIT_print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + JIT_vprint(&_buffer, fmt, args); + va_end(args); +} + + +void JIT_print_decl(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + JIT_vprint(&_buffer_decl, fmt, args); + va_end(args); +} + + +void JIT_print_body(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + JIT_vprint(&_buffer_body, fmt, args); + va_end(args); +} + + +void JIT_section(const char *str) +{ + JIT_print("\n// %s\n\n", str); +} + + +void JIT_declare(TYPE type, const char *fmt, ...) +{ + va_list args; + const char *def; + + def = JIT_get_default_value(type); + + JIT_print_decl(" %s ", JIT_get_ctype(type)); + + va_start(args, fmt); + JIT_vprint(&_buffer_decl, fmt, args); + va_end(args); + + switch (TYPEID(type)) + { + case T_STRING: + case T_OBJECT: + case T_VARIANT: + JIT_print_decl(" = %s", def); + break; + } + + JIT_print_decl(";\n"); +} + + + +char *JIT_translate(const char *name, const char *from) +{ + CLASS *class; + int i; + FUNCTION *func; + + JIT_class = class = (CLASS *)GB.LoadClassFrom(name, from); + + JIT_begin(); + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + if (!func->fast) + continue; + JIT_declare_func(func, i); + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + if (!func->fast) + continue; + + JIT_last_print_is_label = FALSE; + if (JIT_translate_func(func, i)) + return NULL; + } + + return JIT_end(); +} + + +void JIT_panic(const char *fmt, ...) +{ + va_list args; + fprintf(stderr, "gb.jit: panic: "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + va_end(args); + fputc('\n', stderr); + fputs(_buffer, stderr); + if (_buffer_decl) fputs(_buffer_decl, stderr); + if (_buffer_body) fputs(_buffer_body, stderr); + fputc('\n', stderr); + abort(); +} + + +int JIT_get_code_size(FUNCTION *func) +{ + void *code = func->code; + int size = ((int *)code)[-1] / sizeof(ushort); + + if (func->code[size - 1] == 0) + size--; + + return size; +} + + +void JIT_load_class_without_init(CLASS *class) +{ + void *save_cp; + + if (class->loaded) + return; + + if (class->ready || class->in_load) + return; + + save_cp = JIT.exec->cp; + JIT.exec->cp = JIT_class; + + //fprintf(stderr, "gb.jit: load class: %s (%p)\n", class->name, class); + JIT.load_class_without_init(class); + + JIT.exec->cp = save_cp; +} + + +int JIT_find_symbol(CLASS *class, const char *name) +{ + JIT_load_class_without_init(class); + if (class->loaded) + return JIT.find_symbol(class->table, class->sort, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, name, strlen(name), NULL); + else + return NO_SYMBOL; +} + + diff --git a/main/lib/jit/jit.h b/main/lib/jit/jit.h new file mode 100644 index 00000000..2250bef4 --- /dev/null +++ b/main/lib/jit/jit.h @@ -0,0 +1,94 @@ +/*************************************************************************** + + jit.h + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_H +#define __JIT_H + +#define __GB_COMMON_CASE_H +#include "gb_common.h" +#include "gb_str.h" +#include "gb_pcode.h" +#include "gb_reserved.h" +#include "gbx_type.h" +#include "gbx_class.h" +#include "main.h" + +#define T_UNKNOWN 17 + +#undef TYPE_is_pure_object +#define TYPE_is_pure_object(_type) ((_type) > T_UNKNOWN) + +#undef TYPE_is_object +#define TYPE_is_object(_type) ((_type) == T_OBJECT || TYPE_is_pure_object(_type)) + +#define TYPEID(_type) (TYPE_is_pure_object(_type) ? T_OBJECT : (_type)) + +enum +{ + CALL_UNKNOWN, + CALL_PRIVATE, + CALL_EVENT, + CALL_EXTERN +}; + +#define PM_STRING 8 +#define PM_WAIT 16 + + +#ifndef __GBC_JIT_C +EXTERN char *JIT_prefix; +EXTERN CLASS *JIT_class; +EXTERN bool JIT_last_print_is_label; +#endif + + +// jit.c + +char *JIT_translate(const char *name, const char *from); + +void JIT_section(const char *str); + +void JIT_print(const char *fmt, ...); +void JIT_print_decl(const char *fmt, ...); +void JIT_print_body(const char *fmt, ...); +void JIT_declare(TYPE type, const char *fmt, ...); + +const char *JIT_get_type(TYPE type); +const char *JIT_get_gtype(TYPE type); +const char *JIT_get_ctype(TYPE type); +TYPE JIT_ctype_to_type(CLASS *class, CTYPE ctype); +const char *JIT_get_default_value(TYPE type); + +void JIT_panic(const char *fmt, ...) NORETURN; + +int JIT_get_code_size(FUNCTION *func); +int JIT_find_symbol(CLASS *class, const char *name); +void JIT_load_class_without_init(CLASS *class); + +// jit_body.c + +bool JIT_translate_body(FUNCTION *func, int index); + + +#endif + diff --git a/main/lib/jit/jit_body.c b/main/lib/jit/jit_body.c new file mode 100644 index 00000000..f13b1c7d --- /dev/null +++ b/main/lib/jit/jit_body.c @@ -0,0 +1,3783 @@ +/*************************************************************************** + + jit_body.c + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_BODY_C + +#define _GNU_SOURCE + +#include "config.h" +#include +#include +#include +#include +#include + +#include "jit.h" + +#define JIT_print JIT_print_body + +#define MAX_STACK 256 + +typedef + struct { + TYPE type; + char *expr; + ushort func; + ushort pc; + int index; + TYPE call; + } + STACK_SLOT; + +typedef + struct { + TYPE type; + char *expr; + } + CTRL_INFO; + +enum { + CALL_SUBR = 0, + CALL_SUBR_CODE = 1, + CALL_SUBR_UNKNOWN = 2, + CALL_NEW = 3, + CALL_RETURN_UNKNOWN = 128 +}; + +enum { + MATH_NEG, + MATH_ABS, + MATH_SGN, + MATH_INT, + MATH_FIX +}; + + +static FUNCTION *_func; + +static STACK_SLOT _stack[MAX_STACK]; +static int _stack_current = 0; + +static bool _decl_rs; +static bool _decl_ro; +static bool _decl_rv; +static bool _decl_tp; +static bool _decl_ra; +static bool _decl_as; + +static ushort _pc; + +static bool _no_release = FALSE; +static bool _no_release_but_borrow = FALSE; + +static int _loop_count; + +static TYPE *_dup_type; + +enum { LOOP_UNKNOWN, LOOP_UP, LOOP_DOWN }; + +static int _loop_type; + +static int *_ctrl_index; +static CTRL_INFO *_ctrl_info; + +static bool _has_gosub; +static bool _has_finally; +static bool _has_catch; +static bool _try_finished; + +static bool _has_just_dup; + +static bool _unsafe; + + +static void enter_function(FUNCTION *func, int index) +{ + _func = func; + + _decl_rs = FALSE; + _decl_ro = FALSE; + _decl_rv = FALSE; + _decl_tp = FALSE; + _decl_ra = FALSE; + _decl_as = FALSE; + + _has_gosub = FALSE; + _loop_count = 0; + _has_just_dup = FALSE; + + _has_catch = FALSE; + _has_finally = func->error && (func->code[func->error - 1] != C_CATCH); + + _unsafe = func->unsafe; + + GB.NewArray((void **)&_dup_type, sizeof(TYPE), 0); + GB.NewArray((void **)&_ctrl_info, sizeof(CTRL_INFO), 0); + + if (func->n_ctrl) + GB.AllocZero((void **)&_ctrl_index, sizeof(int) * func->n_ctrl); + else + _ctrl_index = NULL; + + JIT_print_decl(" VALUE **psp = (VALUE **)%p;\n", JIT.sp); + JIT_print_decl(" VALUE *sp = SP;\n"); + //JIT_print_decl(" VALUE *ep = sp;\n"); + //JIT_print(" VALUE *sp = SP; fprintf(stderr, \"> %d: sp = %%p\\n\", sp);\n", index); + JIT_print_decl(" ushort *pc = (ushort *)%p;\n", JIT.get_code(func)); + JIT_print_decl(" GB_VALUE_GOSUB *gp = 0;\n"); + JIT_print_decl(" bool error = FALSE;\n"); + + if (func->vararg) + { + JIT_print(" VALUE *fp = FP, *pp = PP, *bp = BP;\n"); + JIT_print(" FP = %p; PP = v; BP = sp;\n", func); + } + + JIT_print(" VALUE *ssp = sp;\n"); // fprintf(stderr, \"bp = %%p\\n\", bp);\n"); + JIT_print(" TRY {\n\n"); + + _try_finished = FALSE; +} + + +static void print_catch(void) +{ + JIT_print("\n } CATCH {\n\n"); + + JIT_print(" sp = _jit_print_catch(psp, sp, ssp, (void *)%p, (void *)%p, %d);\n", JIT_class, _func, _has_catch || _has_finally); + JIT_print(" error = TRUE;\n"); + JIT_print("\n } END_TRY\n\n"); + JIT_print("__FINALLY:;\n"); + _try_finished = TRUE; +} + +#define RELEASE_FAST(_expr, _type, _index) ({ \ + TYPE _t = (_type); \ + switch(TYPEID(_t)) \ + { \ + case T_STRING: case T_OBJECT: case T_VARIANT: \ + JIT_print((_expr), JIT_get_type(_t), (_index)); \ + } \ +}) + +static bool leave_function(FUNCTION *func, int index) +{ + int i; + + STR_free_later(NULL); + JIT_print("\n__RETURN:;\n"); + //JIT_print("__RETURN: fprintf(stderr, \"< %d: sp = %%p\\n\", sp);\n", ind); + + if (_stack_current) + JIT_panic("Stack mismatch: stack is not void"); + + if (!_has_catch && !_has_finally) + print_catch(); + + JIT_print("__RELEASE:;\n"); + if (func->vararg) + JIT_print(" FP = fp; BP = bp; PP = pp;\n"); + JIT_print(" SP = sp;\n"); + JIT_print(" RELEASE_GOSUB();\n"); + + for (i = 0; i < GB.Count(_ctrl_info); i++) + { + RELEASE_FAST(" RELEASE_FAST_%s(c%d);\n", _ctrl_info[i].type, i); + if (_ctrl_info[i].expr) + STR_free(_ctrl_info[i].expr); + } + + for (i = 0; i < GB.Count(_dup_type); i++) + RELEASE_FAST(" RELEASE_FAST_%s(d%d);\n", _dup_type[i], i); + + for (i = 0; i < func->n_local; i++) + RELEASE_FAST(" RELEASE_FAST_%s(l%d);\n", JIT_ctype_to_type(JIT_class, func->local[i].type), i); + + for (i = 0; i < func->n_param; i++) + RELEASE_FAST(" RELEASE_FAST_%s(p%d);\n", func->param[i].type, i); + + if (_decl_ra) + JIT_print(" GB.Unref(&ra);\n"); + + if (!_has_catch && !_has_finally) + { + JIT_print(" if (error) { "); + /*if (func->n_param) + JIT_print("SP -= %d; ", func->n_param);*/ + JIT_print("GB.Propagate(); }\n"); + } + + GB.Free((void **)&_ctrl_index); + GB.FreeArray((void **)&_ctrl_info); + GB.FreeArray((void **)&_dup_type); + + _func = NULL; + + return FALSE; +} + + +static TYPE get_local_type(FUNCTION *func, int index) +{ + TYPE type; + + if (index < func->n_local) + type = JIT_ctype_to_type(JIT_class, func->local[index].type); + else + type = _ctrl_info[_ctrl_index[index - func->n_local]].type; + + return type; +} + + +static void free_stack(int n) +{ + if (n < 0) n += _stack_current; + STR_free(_stack[n].expr); + _stack[n].expr = NULL; +} + + +static void check_stack(int n) +{ + if (_stack_current < n) + JIT_panic("Stack mismatch: stack is void"); +} + + +static void pop_stack(int n) +{ + int i; + + for (i = 1; i <= n; i++) + free_stack(-i); + + _stack_current -= n; +} + + +static void declare(bool *flag, const char *expr) +{ + if (*flag) + return; + + JIT_print_decl(" %s;\n", expr); + *flag = TRUE; +} + + +static void print_label(FUNCTION *func, ushort pc) +{ + //JIT_print("__L%d:; fprintf(stderr, \"[%s]\\n\");\n", pc, JIT.get_position(JIT_class, func, &func->code[pc])); + JIT_print("__L%d:; // %s\n", pc, JIT.get_position(JIT_class, func, &func->code[pc])); +} + + +static void push_one(TYPE type, const char *fmt, va_list args) +{ + if (_stack_current > MAX_STACK) + JIT_panic("Expression too complex"); + + CLEAR(&_stack[_stack_current]); + + if (fmt) + STR_vadd(&_stack[_stack_current].expr, fmt, args); + + _stack[_stack_current].type = type; + _stack[_stack_current].call = T_UNKNOWN; + _stack_current++; +} + + +static void push(TYPE type, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + push_one(type, fmt, args); + + va_end(args); +} + +static STACK_SLOT *get_stack(int n) +{ + if (n < 0) n += _stack_current; + return &_stack[n]; +} + +static TYPE get_type(int n) +{ + TYPE type; + + type = get_stack(n)->type; + + if (TYPE_is_pure_object(type)) + JIT_load_class_without_init((CLASS *)type); + + return type; +} + + +static char *get_expr(int n) +{ + return get_stack(n)->expr; +} + + +static void set_expr(int n, char *expr) +{ + if (n < 0) n += _stack_current; + _stack[n].expr = expr; +} + + +static CLASS *get_class(int n) +{ + TYPE type = get_type(n); + + if (type == T_CLASS) + { + sscanf(get_expr(n), "CLASS(%p)", (void **)&type); + if (type) + JIT_load_class_without_init((CLASS *)type); + } + else if (!TYPE_is_pure_object(type)) + type = 0; + + return (CLASS *)type; +} + + +static char *borrow_expr(char *expr, TYPE type) +{ + const char *type_name = JIT_get_type(type); + int len; + char *new_expr; + + len = strlen(expr); + if ((strncmp(&expr[len - 5], "();})", 5) == 0) && (strncmp(&expr[len - 10], "POP_", 4) == 0) && (expr[len - 6] == *type_name)) + new_expr = STR_print("%.*sPOP_BORROW_%s();})", len - 10, expr, type_name); + else + new_expr = STR_print("BORROW_%s(%s)", type_name, expr); + + STR_free(expr); + return new_expr; +} + + +static const char *get_conv_format(TYPE src, TYPE dest) +{ + static char buffer[64]; + + if (src == T_VOID) + { + sprintf(buffer, "(THROW_PC(E_NRETURN, %d),%s)", _pc, JIT_get_default_value(TYPEID(dest))); + return buffer; + } + + switch(dest) + { + case T_VOID: + + return "((void)%s)"; + + case T_BOOLEAN: + + switch(TYPEID(src)) + { + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER: + return "((%s)!=0)"; + case T_OBJECT: + return "({ void *_addr = (%s).value; if (_addr) { GB.Ref(_addr); GB.Unref(&_addr); } (_addr) != 0; })"; + } + break; + + case T_BYTE: + + switch(src) + { + case T_BOOLEAN: + return "((uchar)(%s)?255:0)"; + case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + return "((uchar)(%s))"; + } + break; + + case T_SHORT: + + switch(src) + { + case T_BOOLEAN: + return "((short)(%s)?-1:0)"; + case T_BYTE: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + return "((short)(%s))"; + } + break; + + case T_INTEGER: + + switch(src) + { + case T_BOOLEAN: + return "((int)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER: + return "((int)(%s))"; + } + break; + + case T_LONG: + + switch(src) + { + case T_BOOLEAN: + return "((int64_t)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_INTEGER: case T_SINGLE: case T_FLOAT: case T_POINTER: + return "((int64_t)(%s))"; + } + break; + + case T_SINGLE: + + switch(src) + { + case T_BOOLEAN: + return "((float)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_INTEGER: + return "((float)(%s))"; + case T_LONG: case T_FLOAT: + if (_unsafe) + return "((float)(%s))"; + else + return "(CHECK_FINITE((float)(%s)))"; + } + break; + + case T_FLOAT: + + switch(src) + { + case T_BOOLEAN: + return "((double)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: + return "((double)(%s))"; + } + break; + + case T_STRING: + + switch(src) + { + case T_CSTRING: return "%s"; + case T_NULL: return "GET_NULL_s()"; + } + break; + + case T_CSTRING: + + switch(src) + { + case T_STRING: return "%s"; + case T_NULL: return "GET_NULL_s()"; + } + break; + + default: + + if (src == T_NULL) + { + switch(dest) + { + case T_DATE: + case T_POINTER: + case T_VARIANT: + case T_OBJECT: + sprintf(buffer,"GET_NULL_%s()", JIT_get_type(dest)); + return buffer; + default: + sprintf(buffer, "GET_OBJECT(NULL, CLASS(%p))", (CLASS *)dest); + return buffer; + } + } + + if (TYPE_is_object(dest) && TYPE_is_object(src)) + { + if (TYPE_is_pure_object(dest)) + sprintf(buffer, "CONV_o_O(%%s, %p)", (CLASS *)dest); + else + sprintf(buffer, "CONV_o(%%s)"); + + return buffer; + } + break; + } + + if (TYPE_is_pure_object(dest)) + sprintf(buffer, "CONV(%%s, %s, %s, CLASS(%p))", JIT_get_type(src), JIT_get_type(dest), (CLASS *)dest); + else if (src == T_FUNCTION) + sprintf(buffer, "CONV(NULL, F, %s, %s)", JIT_get_type(dest), JIT_get_gtype(dest)); + else + sprintf(buffer, "CONV(%%s, %s, %s, %s)", JIT_get_type(src), JIT_get_type(dest), JIT_get_gtype(dest)); + + return buffer; +} + +static char *get_conv(TYPE src, TYPE dest, char *expr) +{ + char *new_expr; + char *borrow; + + if (dest == T_VOID) + { + switch (TYPEID(src)) + { + case T_OBJECT: + case T_STRING: + case T_VARIANT: + borrow = borrow_expr(expr, src); + new_expr = STR_print("RELEASE_%s(%s)", JIT_get_type(src), borrow); + STR_free(borrow); + return new_expr; + } + } + + if (src == T_VOID && !expr) // void method arguments + new_expr = STR_copy(JIT_get_default_value(TYPEID(dest))); + else + new_expr = STR_print(get_conv_format(src, dest), expr); + + STR_free(expr); + return new_expr; +} + +static char *peek(int n, TYPE conv) +{ + STACK_SLOT *ss; + char *expr; + TYPE type; + + ss = get_stack(n); + + expr = ss->expr; + type = ss->type; + + if (type == T_FUNCTION && !expr) + expr = ss->expr = STR_print("GET_FUNCTION(%d)", ss->pc); + + if (type != conv) + ss->expr = expr = get_conv(type, conv, expr); + + return expr; +} + + +static char *peek_pop(int n, TYPE conv, const char *fmt, va_list args) +{ + char *dest = NULL; + char *expr; + TYPE type; + char *op; + + if (n < 0) n += _stack_current; + + expr = _stack[n].expr; + type = _stack[n].type; + + if (fmt) + { + STR_vadd(&dest, fmt, args); + + if (!_no_release) + { + switch (TYPEID(conv)) + { + case T_STRING: + declare(&_decl_rs, "char *rs"); + JIT_print(" if ((%s).type == GB_T_STRING) rs = (%s).value.addr; else rs = NULL;\n", dest, dest); + break; + + case T_OBJECT: + declare(&_decl_ro, "void *ro"); + JIT_print(" ro = (%s).value;\n", dest); + break; + + case T_VARIANT: + declare(&_decl_rv, "GB_VARIANT rv"); + JIT_print(" rv = (%s);\n", dest); + break; + } + } + } + + if (type != conv) + _stack[n].expr = expr = get_conv(type, conv, expr); + + if (fmt) + { + if (!_no_release || _no_release_but_borrow) + { + switch (TYPEID(conv)) + { + case T_STRING: + case T_OBJECT: + case T_VARIANT: + _stack[n].expr = expr = borrow_expr(expr, conv); + break; + } + } + + if (_no_release) + { + JIT_print(" "); + JIT_print(dest, expr); + JIT_print("; "); + } + else + { + if (dest[strlen(dest) - 1] != '=') + op = " ="; + else + op = ""; + + JIT_print(" %s%s %s;\n", dest, op, expr); + + switch (TYPEID(conv)) + { + case T_STRING: JIT_print(" GB.FreeString(&rs);\n"); break; + case T_OBJECT: JIT_print(" GB.Unref(&ro);\n"); break; + case T_VARIANT: JIT_print(" GB.ReleaseValue((GB_VALUE *)&rv);\n"); break; + } + } + + STR_free(dest); + } + + return expr; +} + + +static char *push_expr(int n, TYPE type) +{ + const char *type_name; + char *expr; + char *new_expr; + int len; + + type_name = JIT_get_type(type); + + expr = peek(n, type); + + if (type == T_VOID) + return "PUSH_V()"; + + if (type == T_FUNCTION) + { + new_expr = STR_print("CALL_UNKNOWN(%d)", get_stack(n)->pc); + } + else + { + len = strlen(expr); + if ((strncmp(&expr[len - 5], "();})", 5) == 0) && (strncmp(&expr[len - 10], "POP_", 4) == 0) && (expr[len - 6] == *type_name)) + new_expr = STR_print("%.*s})", len - 10, expr); + else + new_expr = STR_print("PUSH_%s(%s)", type_name, expr); + } + + STR_free(expr); + set_expr(n, new_expr); + + //fprintf(stderr, "push_expr %s ===> %s\n", expr, new_expr); + + return new_expr; +} + + +static void pop(TYPE type, const char *fmt, ...) +{ + va_list args; + char *expr; + + check_stack(1); + + _stack_current--; + + va_start(args, fmt); + expr = peek_pop(_stack_current, type, fmt, args); + va_end(args); + + if (!fmt) + JIT_print(" %s;\n", expr); + + free_stack(_stack_current); +} + + +static bool check_swap(TYPE type, const char *fmt, ...) +{ + va_list args; + char *expr = NULL; + char *swap = NULL; + + if (_has_just_dup) + { + _has_just_dup = FALSE; + return TRUE; + } + + if (_stack_current < 2) + return TRUE; + + STR_add(&expr, "({ %s _t = %s; ", JIT_get_ctype(type), peek(-2, type)); + + va_start(args, fmt); + STR_vadd(&swap, fmt, args); + va_end(args); + STR_add(&expr, swap, peek(-1, type)); + STR_add(&expr, "; _t; })"); + + pop_stack(2); + + push(type, "%s", expr); + + STR_free(swap); + STR_free(expr); + + return FALSE; +} + + +static int add_ctrl(int index, TYPE type, const char *expr) +{ + int index_ctrl; + CTRL_INFO *info; + + index_ctrl = GB.Count(_ctrl_info); + + info = (CTRL_INFO *)GB.Add(&_ctrl_info); + + info->type = type; + if (expr) + info->expr = STR_copy(expr); + else + info->expr = NULL; + + if (index >= 0) + _ctrl_index[index] = index_ctrl; + + //JIT_print_decl(" %s c%d;\n", JIT_get_ctype(type), index_ctrl); + JIT_declare(type, "c%d", index_ctrl); + + return index_ctrl; +} + + +static void pop_ctrl(int index, TYPE type) +{ + int index_ctrl; + char *expr; + + if (type == T_VOID) + type = get_type(-1); + + if (type == T_CLASS) + expr = get_expr(-1); + else + expr = NULL; + + index_ctrl = add_ctrl(index, type, expr); + + //_no_release = TRUE; + if (expr) + pop_stack(1); + else + pop(type, "c%d", index_ctrl); + //_no_release = FALSE; +} + + +static void push_constant(CLASS *class, int index) +{ + CLASS_CONST *cc = &class->load->cst[index]; + + switch(cc->type) + { + case T_BOOLEAN: push(T_BOOLEAN, "(bool)%d", cc->_integer.value); break; + case T_BYTE: push(T_BYTE, "(uchar)%d", cc->_integer.value); break; + case T_SHORT: push(T_SHORT, "(short)%d", cc->_integer.value); break; + case T_INTEGER: push(T_INTEGER, "(int)%d", cc->_integer.value); break; + case T_LONG: push(T_LONG, "(int64_t)%" PRId64, cc->_long.value); break; + case T_SINGLE: push(T_SINGLE, "(*(float *)%p)", &cc->_single.value); break; + case T_FLOAT: push(T_FLOAT, "(*(double *)%p)", &cc->_float.value); break; + case T_STRING: push(T_CSTRING, "CONSTANT_s(%p, %d)", cc->_string.addr, cc->_string.len); break; + case T_CSTRING: push(T_CSTRING, "CONSTANT_t(%p, %d)", cc->_string.addr, 0); break; + case T_POINTER: push(T_POINTER, "(intptr_t)0"); break; + default: JIT_panic("unknown constant type"); + } +} + + +static void push_function(int func, int index) +{ + push(T_FUNCTION, NULL); + _stack[_stack_current - 1].func = func; + _stack[_stack_current - 1].index = index; + _stack[_stack_current - 1].pc = _pc; +} + + +static void push_static_variable(CLASS *class, CTYPE ctype, char *addr) +{ + TYPE type = JIT_ctype_to_type(class, ctype); + const char *klass; + char buffer[32]; + + if (class == JIT_class) + klass = "CP"; + else + { + sprintf(buffer, "CLASS(%p)", class); + klass = buffer; + } + + switch(ctype.id) + { + case TC_STRUCT: + push(type, "GET_S(%s, %p, CLASS(%p))", klass, addr, (CLASS *)type); + break; + + case TC_ARRAY: + //declare(&_decl_ra, "void *ra = NULL"); + push(type, "GET_A(%s, %s, %p, CLASS(%p), %p)", klass, klass, addr, (CLASS *)type, class->load->array[ctype.value]); + break; + + case T_OBJECT: + if (class == JIT_class) + { + if (TYPE_is_pure_object(type)) + push(type, "GET_o(%p, CLASS(%p))", addr, (CLASS *)type); + else + push(type, "GET_o(%p, GB_T_OBJECT)", addr); + } + else + { + if (TYPE_is_pure_object(type)) + push(type, "({ JIT.load_class(%p); GET_o(%p, CLASS(%p)); })", class, addr, (CLASS *)type); + else + push(type, "({ JIT.load_class(%p); GET_o(%p, GB_T_OBJECT); })", class, addr); + } + break; + + default: + if (class == JIT_class) + push(type, "GET_%s(%p)", JIT_get_type(type), addr); + else + push(type, "({ JIT.load_class(%p); GET_%s(%p); })", class, JIT_get_type(type), addr); + } +} + + +static void push_dynamic_variable(CLASS *class, CTYPE ctype, int pos, char *addr) +{ + TYPE type = JIT_ctype_to_type(class, ctype); + + switch(ctype.id) + { + case TC_STRUCT: + push(type, "GET_S(%s, %s + %d, CLASS(%p))", addr, addr, pos, (CLASS *)type); + break; + + case TC_ARRAY: + //declare(&_decl_ra, "void *ra = NULL"); + push(type, "GET_A(%p, %s, %s + %d, CLASS(%p), %p)", class, addr, addr, pos, (CLASS *)type, class->load->array[ctype.value]); + break; + + case T_OBJECT: + if (TYPE_is_pure_object(type)) + push(type, "GET_o(%s + %d, CLASS(%p))", addr, pos, (CLASS *)type); + else + push(type, "GET_o(%s + %d, GB_T_OBJECT)", addr, pos); + break; + + default: + push(type, "GET_%s(%s + %d)", JIT_get_type(type), addr, pos); + } +} + + +static void push_unknown(int index) +{ + TYPE type = T_UNKNOWN; + TYPE call_type = T_UNKNOWN; + char *expr; + CLASS *class; + + check_stack(1); + + // TODO: Check object + // if (!class->is_simple && ... + // if (UNLIKELY(class->must_check && (*(class->check))(object))) + // THROW(E_IOBJECT); + + class = get_class(-1); + + if (class) + { + CLASS_DESC *desc; + void *addr; + int pos; + CTYPE ctype; + char *sym; + char *get_addr; + bool static_class; + + static_class = get_type(-1) == T_CLASS; + + sym = JIT_class->load->unknown[index]; + + if (class == (CLASS *)GB.FindClass("Param")) + { + if (!strcasecmp(sym, "Count")) + { + pop_stack(1); + push(T_INTEGER, _func->vararg ? "nv" : "0"); + return; + } + else if (!strcasecmp(sym, "Max")) + { + pop_stack(1); + push(T_INTEGER, _func->vararg ? "(nv - 1)" : "-1"); + return; + } + } + + index = JIT_find_symbol(class, sym); + + if (index != NO_SYMBOL) + { + desc = class->table[index].desc; + class = desc->method.class; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_STATIC_VARIABLE: + + pop_stack(1); + + ctype = desc->variable.ctype; + addr = (char *)desc->variable.class->stat + desc->variable.offset; + + push_static_variable(class, ctype, addr); + + return; + + case CD_VARIABLE: + + // TODO: automatic class + + ctype = desc->variable.ctype; + + expr = peek(-1, (TYPE)class); + + pos = desc->variable.offset; + + if (_unsafe) + get_addr = STR_print("ADDR_UNSAFE(%s)", expr); + else if (class->must_check) + get_addr = STR_print("ADDR_CHECK(%p, %s)", class->check, expr); + else + get_addr = STR_print("ADDR(%s)", expr); + + pop_stack(1); + + push_dynamic_variable(class, ctype, pos, get_addr); + + STR_free(get_addr); + + return; + + case CD_CONSTANT: + + if (!static_class) + { + type = desc->constant.type; + break; + } + + pop_stack(1); + + switch(desc->constant.type) + { + case T_BOOLEAN: push(T_BOOLEAN, "(bool)%d", desc->constant.value._integer); break; + case T_BYTE: push(T_BYTE, "(uchar)%d", desc->constant.value._integer); break; + case T_SHORT: push(T_SHORT, "(short)%d", desc->constant.value._integer); break; + case T_INTEGER: push(T_INTEGER, "(int)%d", desc->constant.value._integer); break; + case T_LONG: push(T_LONG, "(int64_t)%" PRId64, desc->constant.value._long); break; + case T_SINGLE: push(T_SINGLE, "(*(float *)%p)", &desc->constant.value._single); break; + case T_FLOAT: push(T_FLOAT, "(*(double *)%p)", &desc->constant.value._float); break; + case T_POINTER: push(T_POINTER, "(intptr_t)%p", desc->constant.value._pointer); break; + case T_STRING: case T_CSTRING: + if (desc->constant.translate) + push(T_CSTRING, "CONSTANT_t(%p, %d)", desc->constant.value._string, strlen(desc->constant.value._string)); + else + push(T_CSTRING, "CONSTANT_s(%p, %d)", desc->constant.value._string, strlen(desc->constant.value._string)); + break; + default: JIT_panic("unknown constant type"); + } + + return; + + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + + type = desc->property.type; + break; + + case CD_METHOD: + case CD_STATIC_METHOD: + + call_type = desc->method.type; + break; + } + } + else + JIT_print(" // %s.%s ?\n", class->name, sym); + } + + expr = STR_copy(push_expr(-1, get_type(-1))); + pop_stack(1); + push(type, "({%s;PUSH_UNKNOWN(%d);POP_%s();})", expr, _pc, JIT_get_type(type)); + + _stack[_stack_current - 1].call = call_type; + + STR_free(expr); +} + + +static void pop_static_variable(CLASS *class, CTYPE ctype, char *addr) +{ + TYPE type = JIT_ctype_to_type(class, ctype); + const char *klass; + char buffer[32]; + + if (class == JIT_class) + klass = "CP"; + else + { + sprintf(buffer, "CLASS(%p)", class); + klass = buffer; + } + + _no_release = TRUE; + + switch(ctype.id) + { + case TC_STRUCT: + case TC_ARRAY: + + if (check_swap(type, "SET_SA(%s, %p, %d, %%s)", klass, addr, ctype)) + pop(type, "SET_SA(%s, %p, %d, %%s)", klass, addr, ctype); + + break; + + default: + if (check_swap(type, "SET_%s(%p, %%s)", JIT_get_type(type), addr)) + pop(type, "SET_%s(%p, %%s)", JIT_get_type(type), addr); + } + + _no_release = FALSE; +} + + +static void pop_dynamic_variable(CLASS *class, CTYPE ctype, int pos, char *addr) +{ + TYPE type = JIT_ctype_to_type(class, ctype); + const char *klass; + char buffer[32]; + + if (class == JIT_class) + klass = "CP"; + else + { + sprintf(buffer, "CLASS(%p)", class); + klass = buffer; + } + + _no_release = TRUE; + + switch(ctype.id) + { + case TC_STRUCT: + case TC_ARRAY: + + if (check_swap(type, "SET_SA(%s, %s + %d, %d, %%s)", klass, addr, pos, ctype)) + pop(type, "SET_SA(%s, %s + %d, %d, %%s)", klass, addr, pos, ctype); + + break; + + default: + if (check_swap(type, "SET_%s(%s + %d, %%s)", JIT_get_type(type), addr, pos)) + pop(type, "SET_%s(%s + %d, %%s)", JIT_get_type(type), addr, pos); + } + + _no_release = FALSE; +} + + +static void pop_unknown(int index) +{ + CLASS *class; + char *expr = NULL; + char *arg; + + check_stack(2); + + class = get_class(-1); + + if (class) + { + CLASS_DESC *desc; + void *addr; + int pos; + CTYPE ctype; + const char *sym; + char *get_addr; + + sym = JIT_class->load->unknown[index]; + index = JIT_find_symbol(class, sym); + if (index != NO_SYMBOL) + { + desc = class->table[index].desc; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_STATIC_VARIABLE: + + pop_stack(1); + + ctype = desc->variable.ctype; + addr = (char *)desc->variable.class->stat + desc->variable.offset; + + pop_static_variable(class, ctype, addr); + + return; + + case CD_VARIABLE: + + // TODO: automatic class + + ctype = desc->variable.ctype; + + expr = peek(-1, (TYPE)class); + + if (_unsafe) + get_addr = STR_print("ADDR_UNSAFE(%s)", expr); + else if (class->must_check) + get_addr = STR_print("ADDR_CHECK(%p, %s)", class->check, expr); + else + get_addr = STR_print("ADDR(%s)", expr); + + pop_stack(1); + + pos = desc->variable.offset; + + pop_dynamic_variable(class, ctype, pos, get_addr); + + STR_free(get_addr); + + return; + } + } + else + JIT_print(" // %s.%s ?\n", class->name, sym); + } + + arg = push_expr(-2, get_type(-2)); + STR_add(&expr,"%s;", arg); + + arg = push_expr(-1, get_type(-1)); + STR_add(&expr, "%s;POP_UNKNOWN(%d);", arg, _pc); + + pop_stack(2); + + push(T_VOID, "({%s})", expr); + + if (check_swap(T_UNKNOWN, "({%s})", expr)) + pop(T_VOID, NULL); + + STR_free(expr); +} + + +static void push_array(ushort code) +{ + TYPE type; + int i, narg; + char *expr = NULL; + char *expr1, *expr2; + const char *unsafe = _unsafe ? "_UNSAFE" : ""; + + narg = code & 0x3F; + check_stack(narg); + + type = get_type(-narg); + + if (TYPE_is_pure_object(type)) + { + CLASS *class = (CLASS *)type; + + //JIT_print(" // %s %d\n", class->name, class->is_array); + + if (class->is_array && !class->is_array_of_struct) + { + type = class->array_type; + + if (narg == 2) + { + expr1 = peek(-2, get_type(-2)); + expr2 = peek(-1, T_INTEGER); + + if (TYPE_is_pure_object(type)) + expr = STR_print("PUSH_ARRAY_O(%s,%s,CLASS(%p),%s)", expr1, expr2, (CLASS *)type, unsafe); + else + expr = STR_print("PUSH_ARRAY_%s(%s,%s,%s)", JIT_get_type(type), expr1, expr2, unsafe); + + pop_stack(2); + + push(type, "(%s)", expr); + + STR_free(expr); + + return; + } + } + else + type = T_UNKNOWN; + } + else + type = T_UNKNOWN; + + //declare_sp(); + + for (i = _stack_current - narg; i < _stack_current; i++) + { + STR_add(&expr, "%s;", push_expr(i, get_type(i))); + free_stack(i); + } + + _stack_current -= narg; + + STR_add(&expr, "CALL_PUSH_ARRAY(%d, 0x%04X);POP_%s();", _pc, code, JIT_get_type(type)); + + push(type, "({%s})", expr); + + STR_free(expr); +} + + +static void pop_array(ushort code) +{ + TYPE type; + int i, narg; + char *expr = NULL; + char *expr1, *expr2; + const char *unsafe = _unsafe ? "_UNSAFE" : ""; + + narg = code & 0x3F; + //fprintf(stderr, "pop_array: %04X %d\n", code, narg); + + check_stack(narg + 1); + + type = get_type(-narg); + + if (TYPE_is_pure_object(type)) + { + CLASS *class = (CLASS *)type; + + if (class->is_array && !class->is_array_of_struct) + { + type = class->array_type; + + if (narg == 2) + { + expr1 = peek(-2, get_type(-2)); + expr2 = peek(-1, T_INTEGER); + + STR_add(&expr, "POP_ARRAY_%s(%s,%s,%s,%s);", JIT_get_type(type), expr1, expr2, peek(-3, type), unsafe); + + pop_stack(3); + + goto _CHECK_SWAP; + } + } + + } + else + type = T_UNKNOWN; + + //declare_sp(); + + narg++; + + for (i = _stack_current - narg; i < _stack_current; i++) + { + STR_add(&expr, "%s;", push_expr(i, get_type(i))); + free_stack(i); + } + + _stack_current -= narg; + + STR_add(&expr, "CALL_POP_ARRAY(%d, 0x%04X);sp--;", _pc, code); + +_CHECK_SWAP: + + push(T_VOID, "({%s})", expr); + + if (check_swap(type, "({%s})", expr)) + pop(T_VOID, NULL); + + STR_free(expr); +} + + +static void push_subr(char mode, ushort code) +{ + const char *call; + TYPE type; + int i, narg; + char *expr = NULL; + ushort op; + bool rst = FALSE; + char type_id = 0; + void *addr; + + //declare_sp(); + + //JIT_print(" static ushort s%d = 0x%04X;\n", _subr_count, code); + + op = code >> 8; + + switch(mode & 7) + { + case CALL_SUBR_CODE: + call = "CALL_SUBR_CODE(%d, %p, 0x%04X)"; + addr = JIT.subr_table[op]; + break; + case CALL_SUBR: + call = "CALL_SUBR(%d, %p)"; + addr = JIT.subr_table[op]; + break; + case CALL_SUBR_UNKNOWN: + call = "CALL_SUBR_UNKNOWN(%d)"; + addr = NULL; + break; + case CALL_NEW: + call = "CALL_SUBR_CODE(%d, %p, 0x%04X)"; + addr = JIT.new; + break; + default: + return; + } + + if (op == (C_NEW >> 8)) + { + narg = code & 0x3F; + type = get_type(-narg); + } + else if (op == (C_NEG >> 8)) + { + narg = 1; + type = get_type(-1); + } + else if (op < CODE_FIRST_SUBR) + { + int index = RESERVED_get_from_opcode(code); + + if (index < 0) + JIT_panic("Unknown operator"); + + if (RES_is_unary(index)) + narg = 1; + else if (RES_is_binary(index)) + narg = 2; + else + narg = code & 0x3F; + + type_id = COMP_res_info[index].type; + rst = TRUE; + } + else + { + SUBR_INFO *info = SUBR_get_from_opcode(op - CODE_FIRST_SUBR, code & 0x3F); + if (!info) + JIT_panic("unknown subroutine"); + if (info->min_param <= info->max_param) + narg = code & 0x3F; + else + narg = info->min_param; + + type_id = info->type; + rst = TRUE; + } + + check_stack(narg); + + if (rst) + { + switch(type_id) + { + case RST_SAME: + case RST_BCLR: + type = get_type(-narg); + break; + + case RST_MIN: + type = Max(get_type(-1), get_type(-2)); + if (type > T_DATE && type != T_VARIANT) + type = T_UNKNOWN; + break; + + case RST_COLLECTION: + type = GB.FindClass("Collection"); + break; + + case RST_EXEC: + i = atoi(get_expr(-2)); + if ((i & PM_WAIT) && (i & PM_STRING)) + type = T_STRING; + else + type = GB.FindClass("Process"); + break; + + case RST_READ: + type = get_type(-1); + if (type == T_INTEGER) + type = atoi(get_expr(-1)); + else if (type == T_CLASS) + type = (TYPE)get_class(-1); + break; + + default: + type = (type_id >= T_UNKNOWN) ? T_UNKNOWN : type_id; + } + } + + if (op == (C_NEW >> 8)) + { + if (type == T_CLASS) + type = (TYPE)get_class(-narg); + else + type = T_OBJECT; + } + else if (op == CODE_DEBUG) + { + STR_add(&expr, "FP=(void *)%p;PC = &pc[%d];", _func, _pc); + type = narg == 0 ? T_INTEGER : T_BOOLEAN; + } + else if (op == (C_CAT >> 8)) + { + if (narg == 1) + { + narg = 2; + code = C_CAT | 2; // TODO: implement optimization of '&=' operator + } + } + + if (narg > 0) + { + for (i = _stack_current - narg; i < _stack_current; i++) + { + STR_add(&expr, "%s;", push_expr(i, get_type(i))); + free_stack(i); + } + + _stack_current -= narg; + } + + STR_add(&expr, call, _pc, addr, code); + + if (mode & CALL_RETURN_UNKNOWN) + type = T_UNKNOWN; + + STR_add(&expr, ";POP_%s();", JIT_get_type(type)); + + push(type, "({%s})", expr); + + STR_free(expr); +} + + +static void push_subr_add(ushort code, const char *op, const char *opb, bool allow_pointer) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + const char *unsafe; + const char *func; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + break; + + case T_DATE: case T_STRING: case T_CSTRING: + type = T_FLOAT; + break; + + case T_POINTER: + if (allow_pointer) + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + if (type == T_BOOLEAN) + op = opb; + + unsafe = (_unsafe || type > T_LONG) ? "_UNSAFE" : ""; + + switch (*op) + { + case '+': func = "_ADD"; break; + case '-': func = "_SUB"; break; + case '*': func = "_MUL"; break; + default: func = NULL; + } + + if (func) + expr = STR_print("MATH%s%s(%s, %s, %s)", func, unsafe, JIT_get_ctype(type), expr1, expr2); + else + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + pop_stack(2); + + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_div(ushort code) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_SINGLE: case T_FLOAT: + break; + + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + type = T_FLOAT; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + if (_unsafe) + expr = STR_print("({%s _a = %s; %s _b = %s; _a /= _b; _a;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2); + else + expr = STR_print("({%s _a = %s; %s _b = %s; _a /= _b; if (!isfinite(_a)) THROW_PC(E_ZERO, %d); _a;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, _pc); + + pop_stack(2); + + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_arithmetic(char op, ushort code) +{ + TYPE type; + char *expr; + const char *func; + + check_stack(1); + + type = get_type(-1); + + switch (op) + { + case MATH_ABS: func = "MATH_ABS"; break; + case MATH_NEG: func = "- "; break; + case MATH_SGN: func = "MATH_SGN"; break; + } + + switch(type) + { + case T_BOOLEAN: + if (op == MATH_NEG) + return; + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr = STR_copy(peek(-1, type)); + + pop_stack(1); + push(type, "(%s(%s))", func, expr); + STR_free(expr); +} + + +static void push_subr_float_arithmetic(char op, ushort code) +{ + TYPE type; + char *expr; + const char *func; + + check_stack(1); + + type = get_type(-1); + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + return; + + case T_SINGLE: + func = op == MATH_FIX ? "MATH_FIX_g" : "floorf"; + break; + + case T_FLOAT: + func = op == MATH_FIX ? "MATH_FIX_f" : "floor"; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr = STR_copy(peek(-1, type)); + + pop_stack(1); + push(type, "(%s(%s))", func, expr); + STR_free(expr); +} + + +static void push_subr_quo(ushort code, const char *op) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + if (_unsafe) + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + else + expr = STR_print("({%s _a = %s; %s _b = %s; if (_b == 0) THROW_PC(E_ZERO, %d); _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, _pc, op); + + pop_stack(2); + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_and(ushort code, const char *op) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + break; + + case T_DATE: case T_STRING: case T_CSTRING: + type = T_BOOLEAN; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + pop_stack(2); + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_not(ushort code) +{ + TYPE type; + char *expr; + char *op; + + check_stack(1); + + type = get_type(-1); + + switch(type) + { + case T_BOOLEAN: + op = "!"; + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + op = "~"; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr = STR_print("%s%s", op, peek(-1, type)); + + pop_stack(1); + push(type, "(%s)", expr); + STR_free(expr); +} + + +static bool push_subr_cat(ushort code) +{ + int index; + ushort code_pop; + TYPE type; + + if ((code & 0x3F) >= 2) + goto _DEFAULT_SUBR; + + _pc++; + code_pop = _func->code[_pc]; + + if (PCODE_is(code_pop, C_POP_LOCAL)) + { + index = (signed char)code_pop; + type = get_local_type(_func, index); + } + else if (PCODE_is(code_pop, C_POP_PARAM)) + { + index = _func->n_param + (signed char)code_pop; + type = _func->param[index].type; + } + else if (PCODE_is(code_pop, C_POP_STATIC)) + { + index = (code_pop & 0x7FF); + type = JIT_ctype_to_type(JIT_class, JIT_class->load->stat[index].type); + } + else if (PCODE_is(code_pop, C_POP_DYNAMIC)) + { + index = (code_pop & 0x7FF); + type = JIT_ctype_to_type(JIT_class, JIT_class->load->dyn[index].type); + } + else + goto _DEFAULT_SUBR; + + if (type != T_STRING) + goto _DEFAULT_SUBR; + + declare(&_decl_as, "GB_STRING as"); + _no_release = TRUE; + _no_release_but_borrow = TRUE; + pop(T_STRING, "as = %%s"); + _no_release_but_borrow = FALSE; + _no_release = FALSE; + pop_stack(1); + + //expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + if (PCODE_is(code_pop, C_POP_LOCAL)) + { + JIT_print(" JIT.add_string_local(&l%d, as);\n", index); + } + else if (PCODE_is(code_pop, C_POP_PARAM)) + { + JIT_print(" JIT.add_string_local(&p%d, as);\n", index); + } + else if (PCODE_is(code_pop, C_POP_STATIC)) + { + void *addr = &JIT_class->stat[JIT_class->load->stat[index].pos]; + JIT_print(" JIT.add_string_global(%p, as);\n", addr); + } + else if (PCODE_is(code_pop, C_POP_DYNAMIC)) + { + int pos = JIT_class->load->dyn[index].pos; + JIT_print(" JIT.add_string_global(&OP[%d], as);\n", pos); + } + + return TRUE; + +_DEFAULT_SUBR: + + push_subr(CALL_SUBR_CODE, code); + return FALSE; + + //JIT_print(" THROW_TYPE_PC(GB_T_STRING, %p, %d);", (void *)type, _pc); +} + +static void push_subr_comp(ushort code) +{ + char *op = NULL; + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER: + + switch(code & 0xFF00) + { + case C_EQ: op = "=="; break; + case C_NE: op = "!="; break; + case C_GT: op = ">"; break; + case C_LT: op = "<"; break; + case C_GE: op = ">="; break; + case C_LE: op = "<="; break; + } + break; + } + + if (!op) + { + switch(code & 0xFF00) + { + case C_EQ: case C_NE: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + break; + + case C_GT: case C_LT: case C_GE: case C_LE: + push_subr(CALL_SUBR_UNKNOWN + CALL_RETURN_UNKNOWN, code); + break; + } + + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + pop_stack(2); + + push(T_BOOLEAN, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_bit(ushort code) +{ + static const char *_actions[] = { + NULL, + "_v &= ~((%s)1 << _b)", // BClr + "_v |= ((%s)1 << _b)", // BSet + "((_v & ((%s)1 << _b)) != 0)", // BTst + "_v ^= ((%s)1 << _b)", // BChg + "_v = (((%s)_v << _b) & 0x%s) | (((%s)_v) & 0x%s)", // Asl + "_v = (((%s)_v >> _b) & 0x%s) | (((%s)_v) & 0x%s)", // Asr + "_v = ((%s)_v << _b) | ((%s)_v >> (%d - _b))", // Rol + "_v = ((%s)_v >> _b) | ((%s)_v << (%d - _b))", // Ror + "_v = ((%s)_v << _b)", // Lsl + "_v = ((%s)_v >> _b)", // Lsr + }; + + const char *action; + char *expr; + char *expr1, *expr2; + TYPE type; + const char *ctype, *uctype, *mask, *mask2; + int nbits; + + check_stack(2); + + type = get_type(-2); + + switch(type) + { + case T_BYTE: + ctype = "uchar"; uctype = "uchar"; + mask = "7F"; mask2 = "80"; + nbits = 8; + break; + + case T_SHORT: + ctype = "short"; uctype= "ushort"; + mask = "7FFF"; mask2 = "8000"; + nbits = 16; + break; + + case T_INTEGER: + ctype = "int"; uctype = "uint"; + mask = "7FFFFFFF"; mask2 = "80000000"; + nbits = 32; + break; + + case T_LONG: + ctype = "int64_t"; uctype= "uint64_t"; + mask = "7FFFFFFFFFFFFFFFLL"; mask2 = "8000000000000000LL"; + nbits = 64; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, T_INTEGER); + + code &= 0x3F; + + action = _actions[code]; + + if (_unsafe) + expr = STR_print("({ %s _v = %s; int _b = %s; ", ctype, expr1, expr2, nbits); + else + expr = STR_print("({ %s _v = %s; int _b = %s; if ((_b < 0) || (_b >= %d)) THROW_PC(E_ARG, %d); ", ctype, expr1, expr2, nbits, _pc); + + + switch(code) + { + case 1: case 2: case 3: case 4: + STR_add(&expr, action, uctype); + break; + + case 5: case 6: + STR_add(&expr, action, uctype, mask, uctype, mask2); + break; + + case 7: case 8: + STR_add(&expr, action, uctype, uctype, nbits); + break; + + case 9: case 10: + STR_add(&expr, action, uctype); + break; + } + + if (code == 3) + STR_add(&expr, "; })"); + else + STR_add(&expr, "; _v; })"); + + pop_stack(2); + + push(code == 3 ? T_BOOLEAN : type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_conv(ushort code) +{ + char *expr; + TYPE type; + TYPE conv = code & 0x3F; + + check_stack(1); + type = get_type(-1); + + if (type == conv) + return; + + expr = STR_copy(peek(-1, conv)); + pop_stack(1); + push(conv, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_len(ushort code) +{ + char *expr; + + check_stack(1); + + expr = STR_copy(peek(-1, T_STRING)); + pop_stack(1); + push(T_INTEGER, "((%s).value.len)", expr); + STR_free(expr); +} + + +static void push_subr_left_right(ushort code, const char *func) +{ + uint narg = code & 0x3F; + TYPE type; + char *expr_str; + char *expr_len = NULL; + + check_stack(narg); + + if (narg == 2) + { + expr_len = STR_copy(peek(-1, T_INTEGER)); + pop_stack(1); + } + + type = get_type(-1); + if (type == T_VARIANT || type == T_UNKNOWN) + type = T_STRING; + + expr_str = STR_copy(peek(-1, T_STRING)); + pop_stack(1); + + push(type, "%s(%s, %s)", func, expr_str, expr_len ? expr_len : "1"); + + STR_free(expr_len); + STR_free(expr_str); +} + + +static void push_subr_mid(ushort code) +{ + uint narg = code & 0x3F; + char *expr_len = NULL; + char *expr_start, *expr_str; + TYPE type; + + check_stack(narg); + + if (narg == 3) + { + expr_len = STR_copy(peek(-1, T_INTEGER)); + pop_stack(1); + } + + expr_start = STR_copy(peek(-1, T_INTEGER)); + pop_stack(1); + + type = get_type(-1); + if (type == T_VARIANT || type == T_UNKNOWN) + type = T_STRING; + + expr_str = STR_copy(peek(-1, T_STRING)); + pop_stack(1); + + if (expr_len) + push(type, "SUBR_MID(%s, %s, %s, %d)", expr_str, expr_start, expr_len, _pc); + else + push(type, "SUBR_MID_END(%s, %s, %d)", expr_str, expr_start, _pc); + + STR_free(expr_len); + STR_free(expr_start); + STR_free(expr_str); +} + + +static void push_call(ushort code) +{ + char *call = NULL; + const char *def; + int i, j; + int narg; + FUNCTION *func; + CLASS_EXTERN *ext; + int func_kind, func_index; + TYPE func_type; + int nopt, opt; + int nv; + + narg = code & 0x3F; + + if (_stack_current > narg && get_type(- narg - 1) == T_FUNCTION) + { + STACK_SLOT *s = &_stack[_stack_current - narg - 1]; + func_kind = s->func; + func_index = s->index; + func_type = T_UNKNOWN; + } + else + { + STACK_SLOT *s = &_stack[_stack_current - narg - 1]; + func_kind = CALL_UNKNOWN; + func_type = s->call; + } + + switch (func_kind) + { + case CALL_PRIVATE: + + func = &JIT_class->load->func[func_index]; + if (func->fast) + { + if (narg < func->npmin) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_NEPARAM, %d); temp; })", _pc); + } + else if (narg > func->n_param && !func->vararg) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_TMPARAM, %d); temp; })", _pc); + } + else + { + nv = 0; + if (func->vararg && (narg > func->n_param)) + { + if (func->type != T_VOID) + STR_add(&call, "%s _r;", JIT_get_ctype(func->type)); + + nv = narg - func->n_param; + for (i = 0; i < nv; i++) + STR_add(&call, "%s;", push_expr(i - nv, get_type(i - nv))); + } + + STR_add(&call, "SP=sp;"); + + if (nv && func->type != T_VOID) + STR_add(&call, "_r="); + + STR_add(&call, "jit_%s_%d_(", JIT_prefix, func_index); + + for (i = 0; i < func->npmin; i++) + { + if (i) STR_add(&call, ","); + STR_add(&call, "%s", peek(i - narg, func->param[i].type)); + } + + nopt = 0; + for (; i < func->n_param; i++) + { + if (i) STR_add(&call, ","); + + if (nopt == 0) + { + opt = 0; + for (j = 0; j < 8; j++) + { + if ((i + j) >= func->n_param) + break; + if (((i + j) >= narg) || get_type(i + j - narg) == T_VOID) + opt |= 1 << j; + } + STR_add(&call, "%d,", opt); + } + + if (i < narg) + STR_add(&call, "%s", peek(i - narg, func->param[i].type)); + else + { + def = JIT_get_default_value(func->param[i].type); + STR_add(&call, "({ %s temp = %s; temp; })", JIT_get_ctype(func->param[i].type), def); + } + + nopt++; + if (nopt >= 8) + nopt = 0; + } + + if (func->vararg) + { + if (func->n_param) + STR_add(&call, ","); + STR_add(&call, "%d,&sp[-%d]", nv, nv); + } + + STR_add(&call, ");"); + + if (nv) + { + STR_add(&call, "JIT.release_many(sp,%d);sp -= %d;", nv ,nv); + if (func->type != T_VOID) + STR_add(&call, "_r;"); + } + + pop_stack(narg + 1); + + push(func->type, "({%s})", call); + } + } + else + { + STR_add(&call, "PUSH_PRIVATE_FUNCTION(%d);", func_index); + + for (i = 0; i < narg; i++) + STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg))); + + pop_stack(narg + 1); + + STR_add(&call, "CALL_UNKNOWN(%d);POP_%s();", _pc, JIT_get_type(func->type)); + + push(func->type, "({%s})", call); + } + + break; + + case CALL_UNKNOWN: + + narg++; + for (i = 0; i < narg; i++) + STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg))); + + pop_stack(narg); + + STR_add(&call, "CALL_UNKNOWN(%d);POP_%s();", _pc, JIT_get_type(func_type)); + + push(func_type, "({%s})", call); + + break; + + case CALL_EVENT: + + for (i = 0; i < narg; i++) + STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg))); + + pop_stack(narg + 1); + + if (func_index != NO_SYMBOL) + STR_add(&call, "RAISE_EVENT(%d,%d);", func_index, narg); + else + STR_add(&call, "RAISE_UNKNOWN_EVENT(%d);", _pc); + + push(T_BOOLEAN, "({%s})", call); + + break; + + case CALL_EXTERN: + + ext = &JIT_class->load->ext[func_index]; + + if (narg < ext->n_param) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_NEPARAM, %d); temp })", _pc); + } + else if (narg > ext->n_param && !ext->vararg) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_TMPARAM, %d); temp })", _pc); + } + else + { + TYPE type; + char *expr; + + STR_add(&call,"SP = sp;(*(%s (*)())%p)(", JIT_get_ctype(ext->type), JIT.get_extern(ext)); + + for (i = 0; i < ext->n_param; i++) + { + if (i) STR_add(&call, ","); + type = ext->param[i].type; + expr = peek(i - narg, type); + if (type == T_STRING || type == T_CSTRING) + STR_add(&call, "GET_STRING_ADDR(%s)", expr); + else if (type >= T_OBJECT) + STR_add(&call, "JIT.get_object_addr((%s)._object.value)", expr); + else + STR_add(&call, "%s", expr); + } + + STR_add(&call, ");"); + + pop_stack(narg + 1); + + push(ext->type, "({%s})", call); + } + + break; + + default: + + JIT_panic("Unsupported call"); + } + + STR_free(call); +} + + +static void push_subr_isnan(ushort code) +{ + char *func; + char *expr; + + check_stack(1); + + switch (code & 0xFF) + { + case 1: // IsNan + func = "isnan"; + break; + + case 2: // IsInf + func = "isinf"; + break; + + default: + push_subr(CALL_SUBR_CODE, code); + return; + } + + expr = STR_print("%s(%s) != 0", func, peek(-1, T_FLOAT)); + + pop_stack(1); + push(T_BOOLEAN, "(%s)", expr); + STR_free(expr); +} + +static void push_subr_math(ushort code) +{ + static const char *func[] = { + NULL, "frac(%s)", "__builtin_log(%s)", "__builtin_exp(%s)", "__builtin_sqrt(%s)", "__builtin_sin(%s)", "__builtin_cos(%s)", "__builtin_tan(%s)", + "__builtin_atan(%s)", "__builtin_asin(%s)", "__builtin_acos(%s)", + "((%s) * 180 / M_PI)", // deg() + "((%s) * M_PI / 180)", // rad() + "log10(%s)", + "__builtin_sinh(%s)", "__builtin_cosh(%s)", "__builtin_tanh(%s)", "__builtin_asinh(%s)", "__builtin_acosh(%s)", "__builtin_atanh(%s)", +#ifndef HAVE_EXP2 + "pow(2, (%s))", +#else + "__builtin_exp2(%s)", +#endif +#ifndef HAVE_EXP10 + "pow(10, (%s))", +#else + "__builtin_exp10(%s)", +#endif +#ifndef HAVE_LOG2 + "(log(%s) / M_LN2)", +#else + "__builtin_log2(%s)", +#endif + "__builtin_cbrt(%s)", "__builtin_expm1(%s)", "__builtin_log1p(%s)", "__builtin_floor(%s)", "__builtin_ceil(%s)" + }; + + char *expr; + + check_stack(1); + + expr = STR_print(func[code & 0x1F], peek(-1, T_FLOAT)); + pop_stack(1); + + push(T_FLOAT, "%s(%s)", _unsafe ? "CALL_MATH_UNSAFE" : "CALL_MATH", expr); + + STR_free(expr); +} + + +static void push_subr_pi(ushort code) +{ + char *expr; + + if ((code & 0xFF) == 0) + { + push(T_FLOAT, "M_PI"); + return; + } + + check_stack(1); + + expr = STR_copy(peek(-1, T_FLOAT)); + pop_stack(1); + + push(T_FLOAT, "(M_PI*(%s))", expr); + + STR_free(expr); +} + + +static void push_complex(void) +{ + char *expr; + + expr = STR_copy(peek(-1, T_FLOAT)); + pop_stack(1); + + push(T_OBJECT, "PUSH_COMPLEX(%s)", expr); + + STR_free(expr); +} + + +static void push_event(bool unknown, int index) +{ + CLASS_DESC *desc; + const char *name; + + if (unknown) + { + name = JIT_class->load->unknown[index]; + // The ':' is already in the name, thanks to the compiler. + index = JIT_find_symbol(JIT_class, name); + if (index != NO_SYMBOL) + { + desc = JIT_class->table[index].desc; + if (CLASS_DESC_get_type(desc) == CD_EVENT) + index = desc->event.index; + else + index = NO_SYMBOL; + } + } + else if (JIT_class->parent) + index += JIT_class->parent->n_event; + + push_function(CALL_EVENT, index); +} + + +static void push_subr_varptr(ushort code) +{ + ushort op; + char var[16]; + int index; + TYPE type; + char *expr; + + check_stack(1); + + op = (ushort)atoi(get_expr(-1)); + pop_stack(1); + + if ((code & 0xFF) == 1) // IsMissing + { + push(T_BOOLEAN, "(o%d & %d)", op / 8, 1 << (op % 8)); + return; + } + + if ((op & 0xFF00) == C_PUSH_LOCAL || (op & 0xFF00) == C_PUSH_PARAM + || (op & 0xFF00) == C_PUSH_LOCAL_NOREF || (op & 0xFF00) == C_PUSH_PARAM_NOREF) + { + if ((op & 0xFF00) == C_PUSH_PARAM || (op & 0xFF00) == C_PUSH_PARAM_NOREF) + { + index = _func->n_param + (signed char)(op & 0xFF); + type = _func->param[index].type; + sprintf(var, "p%d", index); + } + else + { + index = op & 0xFF; + type = get_local_type(_func, index); + sprintf(var, "l%d", index); + } + + switch(TYPEID(type)) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + case T_LONG: + case T_SINGLE: + case T_FLOAT: + case T_POINTER: + expr = STR_print("&%s", var); + break; + + case T_DATE: + case T_OBJECT: + expr = STR_print("&%s.value", var); + break; + + /*case T_STRING: + case T_CSTRING: + expr = STR_print("(%s.value.addr + %s.value.start)", var, var); + break;*/ + + case T_VARIANT: + expr = STR_print("(%s.value.type == GB_T_STRING ? %s.value.value._string : &%s.value.value.data)", var, var, var); + break; + + default: + push(T_POINTER, "(THROW_PC(E_UTYPE, %d),(intptr_t)0)", _pc); + return; + } + } + else if ((op & 0xF800) == C_PUSH_DYNAMIC) + { + expr = STR_print("(&OP[%d])", JIT_class->load->dyn[op & 0x7FF].pos); + } + else if ((op & 0xF800) == C_PUSH_STATIC) + { + expr = STR_print("%p", JIT_class->stat + JIT_class->load->stat[op & 0x7FF].pos); + } + else + goto _ILLEGAL; + + push(T_POINTER, "((intptr_t)%s)", expr); + + STR_free(expr); + return; + +_ILLEGAL: + + JIT_panic("unsupported VarPtr()"); +} + + +static void push_subr_peek(ushort code) +{ + char *expr; + TYPE type; + + check_stack(1); + + if (_unsafe) + { + type = get_type(-1); + switch (type) + { + case T_POINTER: + case T_STRING: + case T_CSTRING: + + expr = STR_copy(peek(-1, type)); + pop_stack(1); + + code &= 0xF; + + if (type == T_POINTER) + push(code, "*(%s *)(%s)", JIT_get_ctype(code), expr); + else + push(code, "*(%s *)GET_STRING_ADDR(%s)", JIT_get_ctype(code), expr); + + STR_free(expr); + return; + } + } + + push_subr(CALL_SUBR_CODE, code); +} + + +static void push_subr_poke(ushort code) +{ + char *expr = NULL; + char *expr1; + char *expr2; + TYPE type; + int i; + + check_stack(2); + + code &= 0xF; + + if (_unsafe) + { + type = get_type(-2); + switch (type) + { + case T_POINTER: + case T_STRING: + case T_CSTRING: + + expr1 = peek(-2, type); + expr2 = peek(-1, code); + + if (type == T_POINTER) + STR_add(&expr, "*(%s *)(%s) = %s;", JIT_get_ctype(code), expr1, expr2); + else + STR_add(&expr, "*(%s *)GET_STRING_ADDR(%s) = %s;", JIT_get_ctype(code), expr1, expr2); + + pop_stack(2); + + push(T_VOID, "({%s})", expr); + + /*if (check_swap(type, "({%s})", expr)) + pop(T_VOID, NULL);*/ + + STR_free(expr); + return; + } + } + + for (i = _stack_current - 2; i < _stack_current; i++) + { + STR_add(&expr, "%s;", push_expr(i, get_type(i))); + free_stack(i); + } + + _stack_current -= 2; + + STR_add(&expr, "CALL_SUBR_CODE(%d, %p, %d); POP_V();", _pc, JIT.subr_poke, code); + + push(T_VOID, "({%s})", expr); + + /*if (check_swap(code & 0xF, "({%s})", expr)) + pop(T_VOID, NULL);*/ + + STR_free(expr); +} + + +#define GET_XXX() (((signed short)(code << 4)) >> 4) +#define GET_UXX() (code & 0xFFF) +#define GET_7XX() (code & 0x7FF) +#define GET_XX() ((signed char)code) +#define GET_UX() ((unsigned char)code) +#define GET_3X() (code & 0x3F) +#define TEST_XX() (code & 1) +#define PC (&func->code[p]) + +bool JIT_translate_body(FUNCTION *func, int ind) +{ + static const void *jump_table[256] = + { + /* 00 NOP */ &&_MAIN, + /* 01 PUSH LOCAL */ &&_PUSH_LOCAL, + /* 02 PUSH PARAM */ &&_PUSH_PARAM, + /* 03 PUSH ARRAY */ &&_PUSH_ARRAY, + /* 04 PUSH UNKNOWN */ &&_PUSH_UNKNOWN, + /* 05 PUSH EXTERN */ &&_PUSH_EXTERN, + /* 06 BYREF */ &&_BYREF, + /* 07 PUSH EVENT */ &&_PUSH_EVENT, + /* 08 QUIT */ &&_QUIT, + /* 09 POP LOCAL */ &&_POP_LOCAL, + /* 0A POP PARAM */ &&_POP_PARAM, + /* 0B POP ARRAY */ &&_POP_ARRAY, + /* 0C POP UNKNOWN */ &&_POP_UNKNOWN, + /* 0D POP OPTIONAL */ &&_POP_OPTIONAL, + /* 0E POP CTRL */ &&_POP_CTRL, + /* 0F BREAK */ &&_BREAK, + /* 10 RETURN */ &&_RETURN, + /* 11 PUSH SHORT */ &&_PUSH_SHORT, + /* 12 PUSH INTEGER */ &&_PUSH_INTEGER, + /* 13 PUSH CHAR */ &&_PUSH_CHAR, + /* 14 PUSH MISC */ &&_PUSH_MISC, + /* 15 PUSH ME */ &&_PUSH_ME, + /* 16 TRY */ &&_TRY, + /* 17 END TRY */ &&_END_TRY, + /* 18 CATCH */ &&_CATCH, + /* 19 DUP */ &&_DUP, + /* 1A DROP */ &&_DROP, + /* 1B NEW */ &&_NEW, + /* 1C CALL */ &&_CALL, + /* 1D CALL QUICK */ &&_CALL_QUICK, + /* 1E CALL EASY */ &&_CALL_SLOW, + /* 1F ON */ &&_ON_GOTO_GOSUB, + /* 20 JUMP */ &&_JUMP, + /* 21 JUMP IF TRUE */ &&_JUMP_IF_TRUE, + /* 22 JUMP IF FALSE */ &&_JUMP_IF_FALSE, + /* 23 GOSUB */ &&_GOSUB, + /* 24 JUMP FIRST */ &&_JUMP_FIRST, + /* 25 JUMP NEXT */ &&_JUMP_NEXT, + /* 26 FIRST */ &&_ENUM_FIRST, + /* 27 NEXT */ &&_ENUM_NEXT, + /* 28 = */ &&_SUBR_COMPE, + /* 29 <> */ &&_SUBR_COMPN, + /* 2A > */ &&_SUBR_COMPGT, + /* 2B <= */ &&_SUBR_COMPLE, + /* 2C < */ &&_SUBR_COMPLT, + /* 2D >= */ &&_SUBR_COMPGE, + /* 2E == */ &&_SUBR, + /* 2F CASE */ &&_SUBR_CODE, + /* 30 + */ &&_SUBR_ADD, + /* 31 - */ &&_SUBR_SUB, + /* 32 * */ &&_SUBR_MUL, + /* 33 / */ &&_SUBR_DIV, + /* 34 NEG */ &&_SUBR_NEG, + /* 35 \ */ &&_SUBR_QUO, + /* 36 MOD */ &&_SUBR_REM, + /* 37 ^ */ &&_SUBR_CODE, + /* 38 AND */ &&_SUBR_AND, + /* 39 OR */ &&_SUBR_OR, + /* 3A XOR */ &&_SUBR_XOR, + /* 3B NOT */ &&_SUBR_NOT, + /* 3C & */ &&_SUBR_CAT, + /* 3D LIKE */ &&_SUBR_CODE, + /* 3E &/ */ &&_SUBR_CODE, + /* 3F Is */ &&_SUBR_CODE, + /* 40 Left$ */ &&_SUBR_LEFT, + /* 41 Mid$ */ &&_SUBR_MID, + /* 42 Right$ */ &&_SUBR_RIGHT, + /* 43 Len */ &&_SUBR_LEN, + /* 44 Space$ */ &&_SUBR, + /* 45 String$ */ &&_SUBR, + /* 46 Trim$ */ &&_SUBR_CODE, + /* 47 UCase$ */ &&_SUBR_CODE, + /* 48 Oct$ */ &&_SUBR_CODE, + /* 49 Chr$ */ &&_SUBR, + /* 4A Asc */ &&_SUBR_CODE, + /* 4B InStr */ &&_SUBR_CODE, + /* 4C RInStr */ &&_SUBR_CODE, + /* 4D Subst$ */ &&_SUBR_CODE, + /* 4E Replace$ */ &&_SUBR_CODE, + /* 4F Split */ &&_SUBR_CODE, + /* 50 Scan */ &&_SUBR, + /* 51 Comp */ &&_SUBR_CODE, + /* 52 Conv */ &&_SUBR, + /* 53 DConv */ &&_SUBR_CODE, + /* 54 Abs */ &&_SUBR_ABS, + /* 55 Int */ &&_SUBR_INT, + /* 56 Fix */ &&_SUBR_FIX, + /* 57 Sgn */ &&_SUBR_SGN, + /* 58 Frac... */ &&_SUBR_MATH, + /* 59 Pi */ &&_SUBR_PI, + /* 5A Round */ &&_SUBR_CODE, + /* 5B Randomize */ &&_SUBR_CODE, + /* 5C Rnd */ &&_SUBR_CODE, + /* 5D Min */ &&_SUBR_CODE, + /* 5E Max */ &&_SUBR_CODE, + /* 5F IIf */ &&_SUBR_CODE, + /* 60 Choose */ &&_SUBR_CODE, + /* 61 Array */ &&_SUBR_CODE, + /* 62 ATan2... */ &&_SUBR_CODE, + /* 63 IsAscii... */ &&_SUBR_CODE, + /* 64 BClr... */ &&_SUBR_BIT, + /* 65 IsBoolean... */ &&_SUBR_CODE, + /* 66 TypeOf */ &&_SUBR_CODE, + /* 67 CBool... */ &&_SUBR_CONV, + /* 68 Bin$ */ &&_SUBR_CODE, + /* 69 Hex$ */ &&_SUBR_CODE, + /* 6A Val */ &&_SUBR, + /* 6B Str */ &&_SUBR, + /* 6C Format */ &&_SUBR_CODE, + /* 6D Timer */ &&_SUBR, + /* 6E Now */ &&_SUBR, + /* 6F Year... */ &&_SUBR_CODE, + /* 70 Week */ &&_SUBR_CODE, + /* 71 Date */ &&_SUBR_CODE, + /* 72 Time... */ &&_SUBR_CODE, + /* 73 DateAdd... */ &&_SUBR_CODE, + /* 74 Eval */ &&_SUBR_CODE, + /* 75 Error */ &&_SUBR, + /* 76 Debug */ &&_SUBR_CODE, + /* 77 Wait */ &&_SUBR_CODE, + /* 78 Open */ &&_SUBR_CODE, + /* 79 Close */ &&_SUBR, + /* 7A Input */ &&_SUBR_CODE, + /* 7B LineInput */ &&_SUBR, + /* 7C Print */ &&_SUBR_CODE, + /* 7D Read */ &&_SUBR_CODE, + /* 7E Write */ &&_SUBR_CODE, + /* 7F Flush */ &&_SUBR, + /* 80 Lock... */ &&_SUBR_CODE, + /* 81 InputFrom... */ &&_SUBR_CODE, + /* 82 Eof */ &&_SUBR_CODE, + /* 83 Lof */ &&_SUBR_CODE, + /* 84 Seek */ &&_SUBR_CODE, + /* 85 Kill */ &&_SUBR_CODE, + /* 86 Mkdir */ &&_SUBR_CODE, + /* 87 Rmdir */ &&_SUBR_CODE, + /* 88 Move */ &&_SUBR_CODE, + /* 89 Copy */ &&_SUBR_CODE, + /* 8A Link */ &&_SUBR_ISNAN, + /* 8B Exist */ &&_SUBR_CODE, + /* 8C Access */ &&_SUBR_CODE, + /* 8D Stat */ &&_SUBR_CODE, + /* 8E Dfree */ &&_SUBR, + /* 8F Temp$ */ &&_SUBR_CODE, + /* 90 IsDir */ &&_SUBR, + /* 91 Dir */ &&_SUBR_CODE, + /* 92 RDir */ &&_SUBR_CODE, + /* 93 Exec... */ &&_SUBR_CODE, + /* 94 Alloc */ &&_SUBR_CODE, + /* 95 Free */ &&_SUBR, + /* 96 Realloc */ &&_SUBR_CODE, + /* 97 StrPtr */ &&_SUBR_CODE, + /* 98 Sleep... */ &&_SUBR_CODE, + /* 99 VarPtr */ &&_SUBR_VARPTR, + /* 9A Collection */ &&_SUBR_CODE, + /* 9B Tr$ */ &&_SUBR, + /* 9C Quote$... */ &&_SUBR_CODE, + /* 9D Unquote$... */ &&_SUBR_CODE, + /* 9E MkInt$... */ &&_SUBR_CODE, + /* 9F Byte@... */ &&_SUBR_PEEK, + /* A0 ADD QUICK */ &&_ADD_QUICK, + /* A1 ADD QUICK */ &&_ADD_QUICK, + /* A2 ADD QUICK */ &&_ADD_QUICK, + /* A3 ADD QUICK */ &&_ADD_QUICK, + /* A4 ADD QUICK */ &&_ADD_QUICK, + /* A5 ADD QUICK */ &&_ADD_QUICK, + /* A6 ADD QUICK */ &&_ADD_QUICK, + /* A7 ADD QUICK */ &&_ADD_QUICK, + /* A8 ADD QUICK */ &&_ADD_QUICK, + /* A9 ADD QUICK */ &&_ADD_QUICK, + /* AA ADD QUICK */ &&_ADD_QUICK, + /* AB ADD QUICK */ &&_ADD_QUICK, + /* AC ADD QUICK */ &&_ADD_QUICK, + /* AD ADD QUICK */ &&_ADD_QUICK, + /* AE ADD QUICK */ &&_ADD_QUICK, + /* AF ADD QUICK */ &&_ADD_QUICK, + /* B0 PUSH CLASS */ &&_PUSH_CLASS, + /* B1 PUSH CLASS */ &&_PUSH_CLASS, + /* B2 PUSH CLASS */ &&_PUSH_CLASS, + /* B3 PUSH CLASS */ &&_PUSH_CLASS, + /* B4 PUSH CLASS */ &&_PUSH_CLASS, + /* B5 PUSH CLASS */ &&_PUSH_CLASS, + /* B6 PUSH CLASS */ &&_PUSH_CLASS, + /* B7 PUSH CLASS */ &&_PUSH_CLASS, + /* B8 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* B9 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BA PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BB PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BC PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BD PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BE PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BF PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* C0 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C1 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C2 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C3 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C4 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C5 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C6 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C7 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C8 PUSH STATIC */ &&_PUSH_STATIC, + /* C9 PUSH STATIC */ &&_PUSH_STATIC, + /* CA PUSH STATIC */ &&_PUSH_STATIC, + /* CB PUSH STATIC */ &&_PUSH_STATIC, + /* CC PUSH STATIC */ &&_PUSH_STATIC, + /* CD PUSH STATIC */ &&_PUSH_STATIC, + /* CE PUSH STATIC */ &&_PUSH_STATIC, + /* CF PUSH STATIC */ &&_PUSH_STATIC, + /* D0 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D1 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D2 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D3 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D4 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D5 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D6 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D7 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D8 POP STATIC */ &&_POP_STATIC, + /* D9 POP STATIC */ &&_POP_STATIC, + /* DA POP STATIC */ &&_POP_STATIC, + /* DB POP STATIC */ &&_POP_STATIC, + /* DC POP STATIC */ &&_POP_STATIC, + /* DD POP STATIC */ &&_POP_STATIC, + /* DE POP STATIC */ &&_POP_STATIC, + /* DF POP STATIC */ &&_POP_STATIC, + /* E0 PUSH CONST */ &&_PUSH_CONST, + /* E1 PUSH CONST */ &&_PUSH_CONST, + /* E2 PUSH CONST */ &&_PUSH_CONST, + /* E3 PUSH CONST */ &&_PUSH_CONST, + /* E4 PUSH CONST */ &&_PUSH_CONST, + /* E5 PUSH CONST */ &&_PUSH_CONST, + /* E6 PUSH CONST */ &&_PUSH_CONST, + /* E7 PUSH CONST */ &&_PUSH_CONST, + /* E8 PUSH CONST */ &&_PUSH_CONST, + /* E9 PUSH CONST */ &&_PUSH_CONST, + /* EA PUSH CONST */ &&_PUSH_CONST, + /* EB PUSH CONST */ &&_PUSH_CONST, + /* EC PUSH CONST */ &&_PUSH_CONST, + /* ED PUSH CONST */ &&_PUSH_CONST, + /* EE PUSH CONST */ &&_PUSH_CONST, + /* EF PUSH CONST */ &&_PUSH_CONST_EX, + /* F0 PUSH QUICK */ &&_PUSH_QUICK, + /* F1 PUSH QUICK */ &&_PUSH_LOCAL_NOREF, + /* F2 PUSH QUICK */ &&_PUSH_PARAM_NOREF, + /* F3 PUSH QUICK */ &&_JUMP_IF_TRUE_FAST, + /* F4 PUSH QUICK */ &&_JUMP_IF_FALSE_FAST, + /* F5 PUSH QUICK */ &&_PUSH_QUICK, + /* F6 PUSH QUICK */ &&_PUSH_QUICK, + /* F7 PUSH QUICK */ &&_PUSH_FLOAT, + /* F8 PUSH QUICK */ &&_SUBR_POKE, + /* F9 PUSH QUICK */ &&_POP_LOCAL_NOREF, + /* FA PUSH QUICK */ &&_POP_PARAM_NOREF, + /* F9 PUSH QUICK */ &&_POP_LOCAL_FAST, + /* FA PUSH QUICK */ &&_POP_PARAM_FAST, + /* FD PUSH QUICK */ &&_PUSH_QUICK, + /* FE PUSH QUICK */ &&_PUSH_QUICK, + /* FF PUSH QUICK */ &&_PUSH_QUICK + }; + + CLASS *class = JIT_class; + TYPE type; + CTYPE ctype; + uint p = 0; + ushort code; + int index; + int size = JIT_get_code_size(func); + void *addr; + int pos; + int i; + + enter_function(func, ind); + + //JIT_print(" JIT.debug(\"SP = %%p\\n\", SP);\n"); + + goto _MAIN; + +_MAIN: + + //fprintf(stderr, "[%d] %d\n", p, _stack_current); + + if (_has_finally && p == func->error) + print_catch(); + + if (!JIT_last_print_is_label) + print_label(func, p); + + if (p >= (size - 1)) // ignore the last opcode which is RETURN + return leave_function(func, ind); + + _pc = p; + code = func->code[p++]; + //fprintf(stderr, "--------—-------------------- %d / %04X\n", _stack_current, code); + goto *jump_table[code >> 8]; + +_DUP: + + check_stack(1); + + _has_just_dup = TRUE; + + index = GB.Count(_dup_type); + *(TYPE *)GB.Add(&_dup_type) = type = get_type(-1); + + //JIT_print_decl(" %s d%d;\n", JIT_get_ctype(type), index); + JIT_declare(type, "d%d", index); + //_no_release = TRUE; + pop(type, "d%d", index); + //_no_release = FALSE; + push(type, "d%d", index); + push(type, "d%d", index); + + goto _MAIN; + +_PUSH_LOCAL: + + index = GET_XX(); + type = get_local_type(func, index); + + if (index >= func->n_local) + { + index = _ctrl_index[index - func->n_local]; + CTRL_INFO *info = &_ctrl_info[index]; + + if (info->expr) + push(type, info->expr); + else + push(type, "c%d", index); + } + else + push(type, "l%d", index); + + goto _MAIN; + +_PUSH_LOCAL_NOREF: + + if (class->not_3_18) + goto _PUSH_QUICK; + else + goto _PUSH_LOCAL; + +_POP_LOCAL: + + index = GET_XX(); + type = get_local_type(func, index); + + if (index >= func->n_local) + { + pop(type, "c%d", _ctrl_index[index - func->n_local]); + } + else + { + if (check_swap(type, "l%d = %%s", index)) + pop(type, "l%d", index); + } + + goto _MAIN; + +_POP_LOCAL_NOREF: +_POP_LOCAL_FAST: + + if (class->not_3_18) + goto _PUSH_QUICK; + else + goto _POP_LOCAL; + +_POP_CTRL: + + pop_ctrl(GET_XX() - func->n_local, T_VOID); + goto _MAIN; + +_PUSH_PARAM: + + index = func->n_param + GET_XX(); + push(func->param[index].type, "p%d", index); + goto _MAIN; + +_PUSH_PARAM_NOREF: + + if (class->not_3_18) + goto _PUSH_QUICK; + else + goto _PUSH_PARAM; + +_POP_PARAM: + + index = func->n_param + GET_XX(); + type = func->param[index].type; + + if (check_swap(type, "p%d = %%s", index)) + pop(type, "p%d", index); + + goto _MAIN; + +_POP_PARAM_NOREF: +_POP_PARAM_FAST: + + if (class->not_3_18) + goto _PUSH_QUICK; + else + goto _POP_PARAM; + +_PUSH_QUICK: + + push(T_INTEGER, "%d", GET_XXX()); + goto _MAIN; + +_PUSH_FLOAT: + + if (class->not_3_18) + goto _PUSH_QUICK; + + push(T_FLOAT, "%d", GET_XX()); + goto _MAIN; + +_PUSH_SHORT: + + push(T_INTEGER, "%d", (short)PC[0]); + p++; + goto _MAIN; + +_PUSH_INTEGER: + + push(T_INTEGER, "%d", PC[0] | ((uint)PC[1] << 16)); + p += 2; + goto _MAIN; + +_PUSH_STATIC: + + index = GET_7XX(); + ctype = class->load->stat[index].type; + addr = &class->stat[class->load->stat[index].pos]; + + push_static_variable(JIT_class, ctype, addr); + + goto _MAIN; + +_POP_STATIC: + + index = GET_7XX(); + ctype = class->load->stat[index].type; + addr = &class->stat[class->load->stat[index].pos]; + + pop_static_variable(JIT_class, ctype, addr); + + goto _MAIN; + +_PUSH_DYNAMIC: + + index = GET_7XX(); + pos = class->load->dyn[index].pos; + ctype = class->load->dyn[index].type; + + push_dynamic_variable(JIT_class, ctype, pos, "OP"); + + goto _MAIN; + +_POP_DYNAMIC: + + index = GET_7XX(); + pos = class->load->dyn[index].pos; + ctype = class->load->dyn[index].type; + + pop_dynamic_variable(JIT_class, ctype, pos, "OP"); + + goto _MAIN; + +_PUSH_MISC: + + switch (GET_UX()) + { + case 0: + push(T_NULL, "NULL"); + break; + + case 1: + push(T_VOID, NULL); + break; + + case 2: + push(T_BOOLEAN, "0"); + break; + + case 3: + push(T_BOOLEAN, "(-1)"); + break; + + case 4: + push(T_OBJECT, "GET_LAST()"); + break; + + case 5: + push(T_CSTRING, "GET_CSTRING(\"\", 0, 0)"); + break; + + case 6: + push(T_FLOAT, "INFINITY"); + break; + + case 7: + push(T_FLOAT, "-INFINITY"); + break; + + case 8: + push_complex(); + break; + + /*case 9: + EXEC_push_vargs(); + break; + + case 10: + EXEC_drop_vargs(); + break; + + case 11: + EXEC_end_vargs(); + break; + */ + + default: + goto _ILLEGAL; + } + goto _MAIN; + +_PUSH_CHAR: + + push(T_CSTRING, "GET_CHAR(%d)", GET_UX()); + goto _MAIN; + +_POP_OPTIONAL: + + check_stack(1); + if (get_type(-1) == T_VOID) + { + pop_stack(1); + JIT_last_print_is_label = FALSE; + } + else + { + index = func->n_param + GET_XX() - func->npmin; + JIT_print(" if (o%d & %d)\n ", index / 8, (1 << (index % 8))); + index = func->n_param + GET_XX(); + type = func->param[index].type; + _no_release = TRUE; + _no_release_but_borrow = TRUE; + pop(type, "p%d = %%s", index); + _no_release_but_borrow = FALSE; + _no_release = FALSE; + } + goto _MAIN; + +_PUSH_CLASS: + + index = GET_7XX(); + type = (TYPE)class->load->class_ref[index]; + push(T_CLASS, "CLASS(%p)", (CLASS *)type); + goto _MAIN; + +_PUSH_FUNCTION: + + push_function(CALL_PRIVATE, GET_7XX()); + goto _MAIN; + +_PUSH_ME: + + index = GET_UX(); + if (index & 2) + push((TYPE)(JIT_class->parent), "GET_SUPER(%p)", JIT_class->parent); + else + push((TYPE)JIT_class, "GET_ME(%p)", JIT_class); + + goto _MAIN; + + /*if (GET_UX() & 2) + { + // The used class must be in the stack, because it is tested by exec_push && exec_pop + if (LIKELY(OP != NULL)) + { + SP->_object.class = SP->_object.class->parent; + SP->_object.super = EXEC_super; + } + else + { + SP->_class.class = SP->_class.class->parent; + SP->_class.super = EXEC_super; + } + + EXEC_super = SP; + + //fprintf(stderr, "%s\n", DEBUG_get_current_position()); + //BREAKPOINT(); + } + + PUSH(); + goto _NEXT;*/ + +_PUSH_UNKNOWN: + + push_unknown(PC[0]); + p++; + goto _MAIN; + +_POP_UNKNOWN: + + pop_unknown(PC[0]); + p++; + goto _MAIN; + +_CALL: + + push_call(code); + goto _MAIN; + +_SUBR: + + push_subr(CALL_SUBR, code); + goto _MAIN; + +_SUBR_CODE: + + push_subr(CALL_SUBR_CODE, code); + goto _MAIN; + +_DROP: + + pop(T_VOID, NULL); + goto _MAIN; + +_NEW: + + push_subr(CALL_NEW, code); + goto _MAIN; + +_RETURN: + + switch(code & 0xFF) + { + case 0: + if (_try_finished) + JIT_print(" RETURN();\n"); + else + JIT_print(" RETURN_LEAVE_TRY();\n"); + break; + + case 1: + pop(func->type, "r"); + if (!_try_finished) JIT_print(" LEAVE_TRY();\n"); + JIT_print(" goto __RETURN;\n"); + break; + + case 2: + if (!_try_finished) JIT_print(" LEAVE_TRY();\n"); + JIT_print(" goto __RETURN;\n"); + break; + + default: + goto _ILLEGAL; + } + goto _MAIN; + +_GOSUB: + + JIT_print(" PUSH_GOSUB(__L%d); goto __L%d;\n", p + 1, p + (signed short)PC[0] + 1); + JIT_print("__L%d:;\n", p + 1); + p++; + goto _MAIN; + +_JUMP: + + JIT_print(" goto __L%d;\n", p + (signed short)PC[0] + 1); + p++; + goto _MAIN; + +_JUMP_IF_TRUE: + + JIT_print(" if (%s) goto __L%d;\n", peek(-1, T_BOOLEAN), p + (signed short)PC[0] + 1); + pop_stack(1); + p++; + goto _MAIN; + +_JUMP_IF_FALSE: + + JIT_print(" if (!(%s)) goto __L%d;\n", peek(-1, T_BOOLEAN), p + (signed short)PC[0] + 1); + pop_stack(1); + p++; + goto _MAIN; + +_JUMP_IF_TRUE_FAST: + + if (class->not_3_18) + goto _PUSH_QUICK; + else + goto _JUMP_IF_TRUE; + +_JUMP_IF_FALSE_FAST: + + if (class->not_3_18) + goto _PUSH_QUICK; + else + goto _JUMP_IF_FALSE; + +_JUMP_FIRST: + + index = PC[2] & 0xFF; + type = get_local_type(func, index); + if (strcmp(get_expr(-1), "1") == 0) + _loop_type = LOOP_UP; + else if (strcmp(get_expr(-1), "-1") == 0) + _loop_type = LOOP_DOWN; + else + _loop_type = LOOP_UNKNOWN; + + pop(type, "const %s i%d", JIT_get_ctype(type), _loop_count); + pop(type, "const %s e%d", JIT_get_ctype(type), _loop_count); + + JIT_print(" goto __L%ds;\n", p + 1); + + goto _MAIN; + +_JUMP_NEXT: + + index = PC[1] & 0xFF; + JIT_print(" l%d += i%d;\n", index, _loop_count); + JIT_print("__L%ds:\n", p); + + switch(_loop_type) + { + case LOOP_UP: + JIT_print(" if (l%d > e%d) goto __L%d;\n", index, _loop_count, p + (signed short)PC[0] + 1); + break; + + case LOOP_DOWN: + JIT_print(" if (l%d < e%d) goto __L%d;\n", index, _loop_count, p + (signed short)PC[0] + 1); + break; + + case LOOP_UNKNOWN: + JIT_print(" if (((i%d > 0) && (l%d > e%d)) || ((i%d < 0) && (l%d < e%d))) goto __L%d;\n", _loop_count, index, _loop_count, _loop_count, index, _loop_count, p + (signed short)PC[0] + 1); + } + + _loop_count++; + + p +=2; + goto _MAIN; + +_ENUM_FIRST: + + index = GET_XX() - func->n_local; + type = get_type(-1); + if (!TYPE_is_object(type) && type != T_UNKNOWN) + { + JIT_print(" THROW_PC(E_NOBJECT, %d);\n", _pc); + pop_stack(1); + add_ctrl(index, T_OBJECT, NULL); + add_ctrl(index + 1, T_OBJECT, NULL); + } + else + { + pop_ctrl(index, type); + add_ctrl(index + 1, T_OBJECT, NULL); + JIT_print(" ENUM_FIRST(0x%04X, c%d, c%d);\n", code, _ctrl_index[index], _ctrl_index[index + 1]); + } + goto _MAIN; + +_ENUM_NEXT: + + index = (PC[-2] & 0xFF) - func->n_local; + + JIT_print(" ENUM_NEXT(0x%04X, c%d, c%d, __L%d);\n", code, _ctrl_index[index], _ctrl_index[index + 1], p + (signed short)PC[0] + 1); + if ((code & 1) == 0) + push(T_UNKNOWN, "POP_u()"); + + p++; + goto _MAIN; + +_PUSH_CONST: + + index = GET_UXX(); + push_constant(class, index); + goto _MAIN; + +_PUSH_CONST_EX: + + push_constant(class, PC[0]); + p++; + goto _MAIN; + +_PUSH_ARRAY: + + push_array(code); + goto _MAIN; + +_POP_ARRAY: + + pop_array(code); + goto _MAIN; + +_ADD_QUICK: + + index = GET_XXX(); + push(T_INTEGER, "%d", abs(index)); + if (index < 0) + { + PC[-1] = code = 0x3100; + goto _SUBR_SUB; + } + else + PC[-1] = code = 0x3000; + +_SUBR_ADD: + + push_subr_add(code, "+", "|", TRUE); + goto _MAIN; + +_SUBR_SUB: + + push_subr_add(code, "-", "^", TRUE); + goto _MAIN; + +_SUBR_MUL: + + push_subr_add(code, "*", "&", FALSE); + goto _MAIN; + +_SUBR_DIV: + + push_subr_div(code); + goto _MAIN; + +_SUBR_NEG: + + push_subr_arithmetic(MATH_NEG, code); + goto _MAIN; + +_SUBR_QUO: + + push_subr_quo(code, "/"); + goto _MAIN; + +_SUBR_REM: + + push_subr_quo(code, "%"); + goto _MAIN; + +_SUBR_AND: + + push_subr_and(code, "&"); + goto _MAIN; + +_SUBR_OR: + + push_subr_and(code, "|"); + goto _MAIN; + +_SUBR_XOR: + + push_subr_and(code, "^"); + goto _MAIN; + +_SUBR_NOT: + + push_subr_not(code); + goto _MAIN; + +_SUBR_CAT: + + if (push_subr_cat(code)) + p++; + goto _MAIN; + +_SUBR_ABS: + + push_subr_arithmetic(MATH_ABS, code); + goto _MAIN; + +_SUBR_SGN: + + push_subr_arithmetic(MATH_SGN, code); + goto _MAIN; + +_SUBR_INT: + + push_subr_float_arithmetic(MATH_INT, code); + goto _MAIN; + +_SUBR_FIX: + + push_subr_float_arithmetic(MATH_FIX, code); + goto _MAIN; + +_SUBR_COMPE: +_SUBR_COMPN: +_SUBR_COMPGT: +_SUBR_COMPLE: +_SUBR_COMPLT: +_SUBR_COMPGE: + + push_subr_comp(code); + goto _MAIN; + +_SUBR_ISNAN: + + push_subr_isnan(code); + goto _MAIN; + +_SUBR_CONV: + + push_subr_conv(code); + goto _MAIN; + +_SUBR_LEFT: + + push_subr_left_right(code, "SUBR_LEFT"); + goto _MAIN; + +_SUBR_MID: + + push_subr_mid(code); + goto _MAIN; + +_SUBR_RIGHT: + + push_subr_left_right(code, "SUBR_RIGHT"); + goto _MAIN; + +_SUBR_LEN: + + push_subr_len(code); + goto _MAIN; + +_SUBR_MATH: + + push_subr_math(code); + goto _MAIN; + +_SUBR_PI: + + push_subr_pi(code); + goto _MAIN; + +_SUBR_BIT: + + push_subr_bit(code); + goto _MAIN; + +_SUBR_VARPTR: + + push_subr_varptr(code); + goto _MAIN; + +_SUBR_PEEK: + + push_subr_peek(code); + goto _MAIN; + +_SUBR_POKE: + + if (class->not_3_18) + goto _PUSH_QUICK; + push_subr_poke(code); + goto _MAIN; + +_BREAK: + + goto _MAIN; + +_QUIT: + + JIT_print(" "); + + if ((code & 3) == 3) + { + check_stack(1); + JIT_print("%s,", push_expr(-1, T_BYTE)); + pop_stack(1); + } + + JIT_print(" QUIT(0x%04X);\n", code); + goto _MAIN; + +_TRY: + + declare(&_decl_tp, "VALUE *tp"); + JIT_print(" tp = EP; EP = sp;\n"); + JIT_print(" { VALUE *volatile sp = EP; TRY {\n"); + p++; + goto _MAIN; + +_END_TRY: + + JIT_print(" JIT.set_got_error(0);\n"); + JIT_print(" } CATCH {\n"); + JIT_print(" sp = _jit_end_try(psp, sp);\n"); + JIT_print(" } END_TRY }\n"); + JIT_print(" EP = tp;\n"); + goto _MAIN; + +_CATCH: + + _has_catch = TRUE; + if (!_has_finally) + print_catch(); + + JIT_print(" if (!error) goto __RELEASE;\n"); + + goto _MAIN; + +_ON_GOTO_GOSUB: + + index = GET_XX(); + + JIT_print(" {\n"); + JIT_print(" static void *jump[] = { "); + + for (i = 0; i < index; i++) + { + if (i) JIT_print(", "); + JIT_print("&&__L%d", PC[i] + p + i); + } + + JIT_print(" };\n"); + + pop(T_INTEGER, " int n"); + + p += index + 2; + + JIT_print(" if (n >= 0 && n < %d)\n", index); + + if ((PC[index + 1] & 0xFF00) == C_GOSUB) + { + JIT_print(" {\n"); + JIT_print(" PUSH_GOSUB(__L%d);\n", p); + } + + JIT_print(" goto *jump[n];\n"); + + if ((PC[index + 1] & 0xFF00) == C_GOSUB) + JIT_print(" }\n"); + + JIT_print(" }\n"); + + JIT_print("__L%d:;\n", p); + goto _MAIN; + +_PUSH_EVENT: + + index = GET_UX(); + if (index == 0xFF) + { + push_event(TRUE, PC[0]); + p++; + } + else + push_event(FALSE, index); + + goto _MAIN; + +_PUSH_EXTERN: + + index = GET_UX(); + push_function(CALL_EXTERN, index); + goto _MAIN; + +_BYREF: +_CALL_QUICK: +_CALL_SLOW: +_ILLEGAL: + + JIT_panic("unsupported opcode %04X", code); +} + diff --git a/main/lib/jit/main.c b/main/lib/jit/main.c new file mode 100644 index 00000000..c6ebfaa4 --- /dev/null +++ b/main/lib/jit/main.c @@ -0,0 +1,66 @@ +/*************************************************************************** + + main.c + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "jit.h" +#include "main.h" + +GB_INTERFACE *GB_PTR EXPORT; +JIT_INTERFACE *JIT_PTR EXPORT; + + +BEGIN_METHOD(Jit_Translate, GB_STRING klass; GB_STRING from) + + GB.ReturnString(JIT_translate(GB.ToZeroString(ARG(klass)), GB.ToZeroString(ARG(from)))); + +END_METHOD + + +GB_DESC JitDesc[] = +{ + GB_DECLARE_STATIC("__Jit"), + + GB_STATIC_METHOD("Translate", "s", Jit_Translate, "(Class)s(From)s"), + + GB_END_DECLARE +}; + + +GB_DESC *GB_CLASSES [] EXPORT = +{ + JitDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/jit/main.h b/main/lib/jit/main.h new file mode 100644 index 00000000..79506b11 --- /dev/null +++ b/main/lib/jit/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.jit.h" + +#ifndef __MAIN_C +extern GB_INTERFACE *GB_PTR; +extern JIT_INTERFACE *JIT_PTR; +#endif + +#define GB (*GB_PTR) +#define JIT (*JIT_PTR) + +#endif /* __MAIN_H */ diff --git a/main/lib/option/Makefile.am b/main/lib/option/Makefile.am new file mode 100755 index 00000000..ffd21b28 --- /dev/null +++ b/main/lib/option/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.option +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.option.la + +gb_option_la_LIBADD = +gb_option_la_LDFLAGS = -module @LD_FLAGS@ +gb_option_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_option_la_SOURCES = \ + main.h main.c \ + getoptions.h getoptions.c + + diff --git a/main/lib/option/gb.option.component b/main/lib/option/gb.option.component new file mode 100755 index 00000000..bbb8616f --- /dev/null +++ b/main/lib/option/gb.option.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.option +Author=Chintan Rao H +State=Deprecated diff --git a/main/lib/option/getoptions.c b/main/lib/option/getoptions.c new file mode 100644 index 00000000..cf189b8f --- /dev/null +++ b/main/lib/option/getoptions.c @@ -0,0 +1,396 @@ +/*************************************************************************** + + getoptions.c + + (c) 2000-2009 Chintan Rao + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GETOPTIONS_C + +#include "gb_common.h" +#include +#include +#include "getoptions.h" +#include "main.h" +#include + + +BEGIN_METHOD_VOID (getopt_calc) +{ + char **tmp,c,charec[2]={0}; + int i; + + THIS->index=0; + if(THIS->opt_arg!=NULL) + { + + for (i = 0; i < GB.Count(THIS->opt_arg); i++) + { + GB.FreeString(&(THIS->opt_arg[i])); + } + GB.FreeArray((void *) &(THIS->opt_arg)); + } + + if(THIS->opt_found!=NULL) + { + for (i = 0; i < GB.Count(THIS->opt_found); i++) + { + GB.FreeString(&(THIS->opt_found[i])); + } + GB.FreeArray((void *) &(THIS->opt_found)); + } + opterr=0; + optind=0; + GB.NewArray((void *) &(THIS->opt_found), sizeof(*(THIS->opt_found)), 0); + GB.NewArray((void *) &(THIS->opt_arg), sizeof(*(THIS->opt_arg)), 0); + GB.NewArray((void *) &(THIS->invalid), sizeof(*(THIS->invalid)), 0); + + + + while(1) + { + c=getopt(THIS->arg_count,THIS->argv,THIS->options); + if(c==-1) + { + break; + } + + + charec[0]=c; + tmp=(char **) GB.Add((void *) &(THIS->opt_found)); + *tmp = GB.NewZeroString(charec); + + charec[0]=optopt; + tmp=(char **) GB.Add((void *) &(THIS->invalid)); + if(optopt!=0) + { + *tmp = GB.NewZeroString(charec); + } + else + { + *tmp = GB.NewString("", 0); + } + + tmp=(char **) GB.Add((void *) &(THIS->opt_arg)); + if(optarg!=NULL) + { + *tmp = GB.NewZeroString(optarg); + } + else + { + *tmp = GB.NewString("", 0); + } + } + if(THIS->rest!=NULL) + { + GB.Unref((void *)&THIS->rest); + } + + GB.Array.New((void *) &(THIS->rest) , GB_T_STRING, THIS->arg_count-optind); + GB.Ref(THIS->rest); + for(i=optind;iarg_count;i++) + { + tmp=(char **)GB.Array.Get(THIS->rest,i-optind); + *tmp = GB.NewZeroString(THIS->argv[i]); + } +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTIONS_next ) +{ + char *str; + + if(THIS->index >= GB.Count(THIS->opt_found) ) + { + GB.ReturnNewZeroString (""); + return; + } + str=THIS->opt_found [ THIS->index ]; + GB.ReturnNewZeroString (str); + THIS->index++; +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTIONS_invalid ) +{ + char *str; + + if(THIS->index > GB.Count(THIS->invalid) || THIS->index<=0) + { + GB.ReturnNewZeroString (""); + return; + } + printf("%d\n",THIS->index); + str=THIS->invalid [ THIS->index-1 ]; + GB.ReturnNewZeroString (str); +} +END_METHOD + +BEGIN_METHOD_VOID (COPTIONS_opt_arg) +{ + char *str; + + if(THIS->index > GB.Count(THIS->opt_arg) || THIS->index<=0) + { + GB.ReturnNewZeroString (""); + return; + } + + str=THIS->opt_arg [ THIS->index - 1 ]; + GB.ReturnNewZeroString (str); +} +END_METHOD + +BEGIN_METHOD_VOID(COPTIONS_rest) +{ + GB.ReturnObject(THIS->rest); +} +END_METHOD + +BEGIN_METHOD ( COPTIONS_get, GB_STRING option ) + + int i; + char *str=NULL; + for ( i=0; iopt_found); i++ ) + { + if( THIS->opt_found[i][0]==STRING(option)[0] ) + { + if(THIS->opt_arg[i]!=NULL && STRING(option)[0]!='?') + { + str=THIS->opt_arg[i]; + } + else + { + GB.ReturnInteger(TRUE); + goto _RETURN; + } + } + } + if(str!=NULL) + { + GB.ReturnNewZeroString(str); + goto _RETURN; + } + + + /* should i chUck this out? + for ( i=0; iinvalid); i++ ) + { + if( THIS->invalid[i]!=NULL ) + { + if( THIS->invalid[i][0]==STRING(option)[0] ) + { + GB.ReturnInteger(TRUE); + return; + } + } + } + */ + + GB.ReturnInteger(FALSE); + +_RETURN: + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_METHOD ( COPTIONS_getarg ,GB_STRING option) +{ + int i; + char **tmp; + if(THIS->return_temp!=NULL) + { + GB.Unref(&THIS->return_temp); + } + + GB.Array.New(&THIS->return_temp,GB_T_STRING,0); + for(i=0; iopt_found ) ; i++ ) + { + if( THIS->opt_found[i][0] == STRING(option)[0] ) + { + if(THIS->opt_arg[i]!=NULL ) + { + tmp=(char **)GB.Array.Add(THIS->return_temp); + *tmp = GB.NewZeroString(THIS->opt_arg[i]); + } + } + } + GB.Ref(THIS->return_temp); + GB.ReturnObject(THIS->return_temp); +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTION_getallopt) +{ + int i; + char **tmp; + char charec[2]={0}; + if ( THIS->return_temp!=NULL ) + { + GB.Unref(&THIS->return_temp); + } + + GB.Array.New(&THIS->return_temp,GB_T_STRING,0); + for(i=0; iopt_found ) ; i++ ) + { + if( THIS->opt_found[i][0] != '?' && THIS->opt_found[i][0] != ':') + { + if(THIS->opt_found[i]!=NULL ) + { + tmp=(char **)GB.Array.Add(THIS->return_temp); + charec[0]=THIS->opt_found[i][0]; + *tmp = GB.NewZeroString(charec); + } + } + } + GB.Ref(THIS->return_temp); + GB.ReturnObject ( THIS->return_temp ); +} +END_METHOD + +BEGIN_PROPERTY(COPTIONS_cmdline) + + GB.ReturnObject(THIS->cmdline); + +END_PROPERTY + +BEGIN_PROPERTY(COPTIONS_options) + + THIS->options = GB.NewString(PSTRING(), PLENGTH()); + +END_PROPERTY + +BEGIN_METHOD ( COPTIONS_new, GB_STRING options; GB_OBJECT array ) +{ + char **tmp,*src; + int i,narg; + + THIS->options = GB.NewString(STRING(options), LENGTH(options)); + + GB.NewArray((void *) &(THIS->argv), sizeof(*(THIS->argv)), 0); // argv is where i keep track of what to free later + GB.Array.New((void *) &(THIS->cmdline) ,GB_T_STRING,arg_count); + GB.Ref(THIS->cmdline); + for(i=0;icmdline),i); + *tmp = GB.NewZeroString(cmd_arg[i]); + } + + if ( MISSING ( array ) ) + { + for ( i=0; iargv)); + *tmp = GB.NewZeroString(cmd_arg[i]); + } + THIS->arg_count=arg_count; + } + else + { + if(VARG(array)!=NULL) + { + narg = GB.Array.Count ( VARG ( array ) ); + for(i=0;iargv)); + *tmp = GB.NewZeroString(src); + } + THIS->arg_count=narg; + } + } + + /* start getting the options and store it*/ + + THIS->opt_arg=NULL; + THIS->opt_found=NULL; + THIS->return_temp=NULL; + THIS->index=0; + CALL_METHOD_VOID(getopt_calc); + + RETURN_SELF(); + return; +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTIONS_free ) +{ + int i = 0; + + GB.FreeString(&(THIS->options)); + + for (i = 0; i < GB.Count(THIS->argv); i++) + { + GB.FreeString(&(THIS->argv[i])); + } + GB.FreeArray((void *) &(THIS->argv)); + + if(THIS->rest!=NULL) + { + GB.Unref((void **)&THIS->rest); + } + + for (i = 0; i < GB.Count(THIS->opt_arg); i++) + { + GB.FreeString(&(THIS->opt_arg[i])); + } + GB.FreeArray((void *) &(THIS->opt_arg)); + + for (i = 0; i < GB.Count(THIS->opt_found); i++) + { + GB.FreeString(&(THIS->opt_found[i])); + } + GB.FreeArray((void *) &(THIS->opt_found)); + + for (i = 0; i < GB.Count(THIS->invalid); i++) + { + GB.FreeString(&(THIS->invalid[i])); + } + + if(THIS->return_temp!=NULL) + { + GB.Unref(&THIS->return_temp); + } + + GB.FreeArray((void *) &(THIS->invalid)); + GB.Unref((void *)&THIS->cmdline); +} +END_METHOD + +GB_DESC GetOptionsDesc[] = +{ + GB_DECLARE("GetOptions", sizeof(COPTIONS)), + + GB_METHOD ( "_new", NULL, COPTIONS_new, "(Options)s[(Argument)String[];]" ), + GB_METHOD ( "_free", NULL, COPTIONS_free, NULL ), + GB_METHOD ( "_get" ,"v",COPTIONS_get,"(Option)s" ), + GB_METHOD ( "NextValidOption","s",COPTIONS_next,NULL ), + GB_METHOD ( "OptionArgument","s",COPTIONS_opt_arg,NULL ), + GB_METHOD ( "RestOfArgs","String[]",COPTIONS_rest,NULL ), + GB_METHOD ( "GetErrorOption","s",COPTIONS_invalid,NULL ), + GB_METHOD ( "GetArgument","String[]",COPTIONS_getarg,"(Option)s"), + GB_METHOD ( "GetAllOptions","String[]",COPTION_getallopt,NULL), + + //GB_PROPERTY ( "Argument", "String[]", COPTIONS_arg), + //GB_STATIC_PROPERTY ( "Options", "s",COPTIONS_options), + GB_PROPERTY_READ ("CommandLineArgs","String[]",COPTIONS_cmdline), + + GB_END_DECLARE +}; diff --git a/main/lib/option/getoptions.h b/main/lib/option/getoptions.h new file mode 100644 index 00000000..358764ed --- /dev/null +++ b/main/lib/option/getoptions.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + getoptions.h + + (c) 2000-2009 Chintan Rao + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GETOPTIONS_H +#define __GETOPTIONS_H + +#include "gambas.h" + + +#ifndef __GETOPTIONS_C + +extern GB_DESC GetOptionsDesc[]; + +#else + +typedef +struct { + GB_BASE ob; + char *options; + char **argv; + int arg_count; + char **opt_found; + char **opt_arg; + char **invalid; + GB_ARRAY rest; + GB_ARRAY cmdline; + GB_ARRAY return_temp; + int index; +} +COPTIONS; + +#define THIS OBJECT(COPTIONS) + +#endif + +#endif diff --git a/main/lib/option/main.c b/main/lib/option/main.c new file mode 100644 index 00000000..bff19b44 --- /dev/null +++ b/main/lib/option/main.c @@ -0,0 +1,90 @@ +/*************************************************************************** + + main.c + + (c) 2000-2009 Chintan Rao + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "getoptions.h" + +#include "main.h" + +static void *_old_hook_main; + +char** cmd_arg; +int arg_count; + + +GB_INTERFACE GB EXPORT; + + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + GetOptionsDesc, + NULL +}; + +static void hook_main(int *argc, char ***argv) +{ + int i; + char **tmp; + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); + + arg_count = *argc; + + GB.NewArray(POINTER(&cmd_arg), sizeof(*cmd_arg), 0); + + for(i=0; i<*argc; i++) + { + tmp = (char **)GB.Add((void*)(&cmd_arg)); + *tmp = GB.NewZeroString((*argv)[i]); + } + + *argc = 1; +} + +int EXPORT GB_INIT(void) +{ + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + return 0; +} + + +void EXPORT GB_EXIT() +{ + int i; + for(i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern char ** cmd_arg; +extern int arg_count; +#endif + +#endif diff --git a/main/lib/signal/Makefile.am b/main/lib/signal/Makefile.am new file mode 100644 index 00000000..42d913ad --- /dev/null +++ b/main/lib/signal/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.signal +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.signal.la + +gb_signal_la_LIBADD = @GBX_THREAD_LIB@ +gb_signal_la_LDFLAGS = -module @LD_FLAGS@ +gb_signal_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @GBX_THREAD_INC@ @INCLTDL@ $(AM_CFLAGS) + +gb_signal_la_SOURCES = \ + csignal.h csignal.c \ + main.h main.c + + diff --git a/main/lib/signal/csignal.c b/main/lib/signal/csignal.c new file mode 100644 index 00000000..b3094542 --- /dev/null +++ b/main/lib/signal/csignal.c @@ -0,0 +1,269 @@ +/*************************************************************************** + + csignal.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSIGNAL_C + +#include +#include +#include +#include +#include + +#include "main.h" +#include "csignal.h" + +#ifndef SIGPOLL +#define SIGPOLL SIGIO +#endif + +// The "-1" signal is used for ignoring signal numbers + +#ifndef SIGPWR +#define SIGPWR -1 +#endif + +#ifdef OS_CYGWIN +#define SIGIOT -1 +#endif + + +#define NUM_SIGNALS 32 + +enum { + SH_DEFAULT = 0, + SH_IGNORE = 1, + SH_CATCH = 2 +}; + +typedef + struct SIGNAL_HANDLER { + GB_SIGNAL_CALLBACK *handler; + struct sigaction action; + char state; + } + SIGNAL_HANDLER; + +static SIGNAL_HANDLER _signals[NUM_SIGNALS] = { { 0 } }; +static int _signal = -1; +static GB_FUNCTION _application_signal_func; +static bool _init_signal = FALSE; + +static void init_signal(void) +{ + if (GB.GetFunction(&_application_signal_func, (void *)GB.Application.StartupClass(), "Application_Signal", "i", "")) + { + GB.Error("No Application_Signal event handler defined in startup class"); + return; + } + + _init_signal = TRUE; +} + +static void catch_signal(int num, intptr_t data) +{ + GB.Push(1, GB_T_INTEGER, num); + GB.Call(&_application_signal_func, 1, TRUE); +} + +static void handle_signal(int num, char state) +{ + struct sigaction action; + SIGNAL_HANDLER *sh; + + if (num < 0) + return; + + sh = &_signals[num]; + + if (sh->state == state) + return; + + if (sh->state == SH_IGNORE) + { + if (sigaction(num, &sh->action, NULL)) + { + GB.Error("Unable to reset signal handler"); + return; + } + } + else if (sh->state == SH_CATCH) + { + if (sh->handler) + { + GB.Signal.Unregister(num, sh->handler); + sh->handler = NULL; + } + } + + if (state == SH_IGNORE) + { + action.sa_handler = SIG_IGN; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + if (sigaction(num, &action, &sh->action)) + { + GB.Error("Unable to modify signal handler"); + return; + } + } + else if (state == SH_CATCH) + { + if (num == SIGKILL || num == SIGSTOP) + { + GB.Error("SIGKILL and SIGSTOP cannot be caught"); + return; + } + + if (!_init_signal) + init_signal(); + + sh->handler = GB.Signal.Register(num, catch_signal, 0); + } + + sh->state = state; +} + +static void exit_signal(void) +{ + int i; + + for (i = 0; i < NUM_SIGNALS; i++) + handle_signal(i, SH_DEFAULT); +} + + +BEGIN_METHOD_VOID(Signal_Reset) + + handle_signal(_signal, SH_DEFAULT); + +END_METHOD + +BEGIN_METHOD_VOID(Signal_Ignore) + + handle_signal(_signal, SH_IGNORE); + +END_METHOD + +BEGIN_METHOD_VOID(Signal_Catch) + + handle_signal(_signal, SH_CATCH); + +END_METHOD + +BEGIN_METHOD(Signal_get, GB_INTEGER num) + + int num = VARG(num); + + if (num < -1 || num >= NUM_SIGNALS) + { + GB.Error("Bad signal number"); + return; + } + + _signal = num; + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD_VOID(Signal_exit) + + exit_signal(); + +END_METHOD + +BEGIN_METHOD(Signal_Send, GB_INTEGER pid) + + if (kill(VARG(pid), _signal)) + GB.Error("Unable to send signal: &1", strerror(errno)); + +END_METHOD + +BEGIN_METHOD(Signal_SendOld, GB_INTEGER pid; GB_INTEGER sig) + + if (kill(VARG(pid), VARG(sig))) + GB.Error("Unable to send signal: &1", strerror(errno)); + +END_METHOD + + +GB_DESC CSignalHandlerDesc[] = +{ + GB_DECLARE_VIRTUAL(".SignalHandler"), + + GB_STATIC_METHOD("Reset", NULL, Signal_Reset, NULL), + GB_STATIC_METHOD("Ignore", NULL, Signal_Ignore, NULL), + GB_STATIC_METHOD("Catch", NULL, Signal_Catch, NULL), + GB_STATIC_METHOD("Send", NULL, Signal_Send, "(Process)i"), + + GB_END_DECLARE +}; + +GB_DESC CSignalDesc[] = +{ + GB_DECLARE_STATIC("Signal"), + + GB_CONSTANT("SIGHUP", "i", SIGHUP), + GB_CONSTANT("SIGINT", "i", SIGINT), + GB_CONSTANT("SIGQUIT", "i", SIGQUIT), + GB_CONSTANT("SIGILL", "i", SIGILL), + GB_CONSTANT("SIGTRAP", "i", SIGTRAP), + GB_CONSTANT("SIGABRT", "i", SIGABRT), + GB_CONSTANT("SIGIOT", "i", SIGIOT), + GB_CONSTANT("SIGBUS", "i", SIGBUS), + GB_CONSTANT("SIGFPE", "i", SIGFPE), + GB_CONSTANT("SIGKILL", "i", SIGKILL), + GB_CONSTANT("SIGUSR1", "i", SIGUSR1), + GB_CONSTANT("SIGSEGV", "i", SIGSEGV), + GB_CONSTANT("SIGUSR2", "i", SIGUSR2), + GB_CONSTANT("SIGPIPE", "i", SIGPIPE), + GB_CONSTANT("SIGALRM", "i", SIGALRM), + GB_CONSTANT("SIGTERM", "i", SIGTERM), + //GB_CONSTANT("SIGSTKFLT", "i", SIGSTKFLT), + //GB_CONSTANT("SIGCLD", "i", SIGCLD), + GB_CONSTANT("SIGCHLD", "i", SIGCHLD), + GB_CONSTANT("SIGCONT", "i", SIGCONT), + GB_CONSTANT("SIGSTOP", "i", SIGSTOP), + GB_CONSTANT("SIGTSTP", "i", SIGTSTP), + GB_CONSTANT("SIGTTIN", "i", SIGTTIN), + GB_CONSTANT("SIGTTOU", "i", SIGTTOU), + GB_CONSTANT("SIGURG", "i", SIGURG), + GB_CONSTANT("SIGXCPU", "i", SIGXCPU), + GB_CONSTANT("SIGXFSZ", "i", SIGXFSZ), + GB_CONSTANT("SIGVTALRM", "i", SIGVTALRM), + GB_CONSTANT("SIGPROF", "i", SIGPROF), + GB_CONSTANT("SIGWINCH", "i", SIGWINCH), + GB_CONSTANT("SIGPOLL", "i", SIGPOLL), + GB_CONSTANT("SIGIO", "i", SIGIO), + GB_CONSTANT("SIGPWR", "i", SIGPWR), + GB_CONSTANT("SIGSYS", "i", SIGSYS), + + GB_STATIC_METHOD("_exit", NULL, Signal_exit, NULL), + + GB_STATIC_METHOD("_get", ".SignalHandler", Signal_get, "(Signal)i"), + + GB_STATIC_METHOD("Send", NULL, Signal_SendOld, "(Process)i(Signal)i"), + + GB_END_DECLARE +}; + diff --git a/main/lib/signal/csignal.h b/main/lib/signal/csignal.h new file mode 100644 index 00000000..257a684b --- /dev/null +++ b/main/lib/signal/csignal.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + csignal.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSIGNAL_H +#define __CSIGNAL_H + +#include "gambas.h" + +#ifndef __CDEBUG_C +extern GB_DESC CSignalDesc[]; +extern GB_DESC CSignalHandlerDesc[]; +#endif + +#endif diff --git a/main/lib/signal/gb.signal.component b/main/lib/signal/gb.signal.component new file mode 100644 index 00000000..89ae9ac9 --- /dev/null +++ b/main/lib/signal/gb.signal.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.signal +Author=Benoît Minisini diff --git a/main/lib/signal/main.c b/main/lib/signal/main.c new file mode 100644 index 00000000..880826bd --- /dev/null +++ b/main/lib/signal/main.c @@ -0,0 +1,48 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "csignal.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CSignalHandlerDesc, + CSignalDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/signal/main.h b/main/lib/signal/main.h new file mode 100644 index 00000000..fa8be1b0 --- /dev/null +++ b/main/lib/signal/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/term/Makefile.am b/main/lib/term/Makefile.am new file mode 100644 index 00000000..2ec5ec8a --- /dev/null +++ b/main/lib/term/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.term +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.term.la + +gb_term_la_LIBADD = @GBX_THREAD_LIB@ +gb_term_la_LDFLAGS = -module @LD_FLAGS@ +gb_term_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @GBX_THREAD_INC@ @INCLTDL@ $(AM_CFLAGS) + +gb_term_la_SOURCES = \ + cterm.h cterm.c \ + main.h main.c + + diff --git a/main/lib/term/cterm.c b/main/lib/term/cterm.c new file mode 100644 index 00000000..60f08d6b --- /dev/null +++ b/main/lib/term/cterm.c @@ -0,0 +1,478 @@ +/*************************************************************************** + + cterm.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTERM_C + +#include "main.h" +#include +#include "cterm.h" + +#define STREAM_FD (GB.Stream.Handle(GB.Stream.Get(_object))) + +#define THIS_SETTINGS ((CTERMINALSETTINGS *)_object) +#define SETTINGS THIS_SETTINGS->settings + +static bool check_error(int ret) +{ + if (ret) + { + GB.Error(strerror(errno)); + return TRUE; + } + else + return FALSE; +} + +/*static bool _convert(CTERMINAL *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (!THIS && type >= GB_T_OBJECT && GB.Is(conv->_object.value, GB.FindClass("Stream"))) + { + + } + + return TRUE; + + switch(type) + { + case GB_T_FLOAT: + conv->_object.value = COMPLEX_create(conv->_float.value, 0); + return FALSE; + + case GB_T_SINGLE: + conv->_object.value = COMPLEX_create(conv->_single.value, 0); + return FALSE; + + case GB_T_LONG: + conv->_object.value = COMPLEX_create((double)conv->_long.value, 0); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_object.value = COMPLEX_create(conv->_integer.value, 0); + return FALSE; + + default: + return TRUE; + } +}*/ + +//--------------------------------------------------------------------------- + +static CTERMINALSETTINGS *TERMINALSETTINGS_copy(CTERMINALSETTINGS *_object) +{ + CTERMINALSETTINGS *settings; + settings = (CTERMINALSETTINGS *)GB.New(GB.FindClass("TerminalSettings"), NULL, NULL); + settings->settings = SETTINGS; + return settings; +} + +BEGIN_METHOD_VOID(TerminalSettings_MakeRaw) + + CTERMINALSETTINGS *settings = TERMINALSETTINGS_copy(THIS_SETTINGS); + cfmakeraw(&SETTINGS); + GB.ReturnObject(settings); + +END_METHOD + +BEGIN_METHOD_VOID(TerminalSettings_Copy) + + GB.ReturnObject(TERMINALSETTINGS_copy(THIS_SETTINGS)); + +END_METHOD + +static void handle_settings(tcflag_t *pflag, void *_param, int cst) +{ + if (READ_PROPERTY) + GB.ReturnBoolean(*pflag & cst); + else + { + if (VPROP(GB_BOOLEAN)) + *pflag |= cst; + else + *pflag &= ~cst; + } +} + +static void handle_settings_int(tcflag_t *pflag, void *_param, int mask) +{ + if (READ_PROPERTY) + GB.ReturnInteger(*pflag & mask); + else + { + *pflag &= ~mask; + *pflag |= VPROP(GB_INTEGER) & mask; + } +} + +static void handle_settings_char(cc_t pcc[], void *_param, int pos) +{ + if (READ_PROPERTY) + GB.ReturnInteger(pcc[pos]); + else + pcc[pos] = (cc_t)VPROP(GB_INTEGER); +} + +#define IMPLEMENT_TSP(_name, _field) \ +BEGIN_PROPERTY(TerminalSettings_##_name) \ + handle_settings(&SETTINGS._field, _param, _name); \ +END_PROPERTY + +#define IMPLEMENT_TSP_I(_name, _field) \ +BEGIN_PROPERTY(TerminalSettings_##_name) \ + handle_settings_int(&SETTINGS._field, _param, _name); \ +END_PROPERTY + +#define IMPLEMENT_TSP_C(_name) \ +BEGIN_PROPERTY(TerminalSettings_##_name) \ + handle_settings_char(SETTINGS.c_cc, _param, _name); \ +END_PROPERTY + +IMPLEMENT_TSP(IGNBRK, c_iflag) +IMPLEMENT_TSP(BRKINT, c_iflag) +IMPLEMENT_TSP(IGNPAR, c_iflag) +IMPLEMENT_TSP(PARMRK, c_iflag) +IMPLEMENT_TSP(INPCK, c_iflag) +IMPLEMENT_TSP(ISTRIP, c_iflag) +IMPLEMENT_TSP(INLCR, c_iflag) +IMPLEMENT_TSP(IGNCR, c_iflag) +IMPLEMENT_TSP(ICRNL, c_iflag) +IMPLEMENT_TSP(IXON, c_iflag) +IMPLEMENT_TSP(IXANY, c_iflag) +IMPLEMENT_TSP(IXOFF, c_iflag) +#ifdef OS_LINUX +IMPLEMENT_TSP(IUCLC, c_iflag) +IMPLEMENT_TSP(IUTF8, c_iflag) + +IMPLEMENT_TSP(OLCUC, c_oflag) +IMPLEMENT_TSP(OFILL, c_oflag) +#endif +IMPLEMENT_TSP(OPOST, c_oflag) +IMPLEMENT_TSP(ONLCR, c_oflag) +IMPLEMENT_TSP(OCRNL, c_oflag) +IMPLEMENT_TSP(ONOCR, c_oflag) +IMPLEMENT_TSP(ONLRET, c_oflag) + +#ifndef OS_BSD +IMPLEMENT_TSP_I(NLDLY, c_oflag) +IMPLEMENT_TSP_I(CRDLY, c_oflag) +IMPLEMENT_TSP_I(TABDLY, c_oflag) +IMPLEMENT_TSP_I(BSDLY, c_oflag) +IMPLEMENT_TSP_I(VTDLY, c_oflag) +IMPLEMENT_TSP_I(FFDLY, c_oflag) +#endif + +IMPLEMENT_TSP_I(CSIZE, c_cflag) +IMPLEMENT_TSP(CSTOPB, c_cflag) +IMPLEMENT_TSP(CREAD, c_cflag) +IMPLEMENT_TSP(PARENB, c_cflag) +IMPLEMENT_TSP(PARODD, c_cflag) +IMPLEMENT_TSP(HUPCL, c_cflag) +IMPLEMENT_TSP(CLOCAL, c_cflag) +IMPLEMENT_TSP(CRTSCTS, c_cflag) + +IMPLEMENT_TSP(ISIG, c_lflag) +IMPLEMENT_TSP(ICANON, c_lflag) +IMPLEMENT_TSP(ECHO, c_lflag) +IMPLEMENT_TSP(ECHOE, c_lflag) +IMPLEMENT_TSP(ECHOK, c_lflag) +IMPLEMENT_TSP(ECHONL, c_lflag) +IMPLEMENT_TSP(ECHOCTL, c_lflag) +IMPLEMENT_TSP(ECHOKE, c_lflag) +IMPLEMENT_TSP(FLUSHO, c_lflag) +IMPLEMENT_TSP(NOFLSH, c_lflag) +IMPLEMENT_TSP(TOSTOP, c_lflag) +IMPLEMENT_TSP(IEXTEN, c_lflag) + +#ifdef OS_LINUX +#ifndef CMSPAR +// This is a MIPS fix +#define CMSPAR 010000000000 +#endif + +IMPLEMENT_TSP(CMSPAR, c_cflag) +IMPLEMENT_TSP(XCASE, c_lflag) +#endif + +#ifndef OS_CYGWIN +IMPLEMENT_TSP(ECHOPRT, c_lflag) +IMPLEMENT_TSP(PENDIN, c_lflag) +#endif + +IMPLEMENT_TSP_C(VDISCARD) +IMPLEMENT_TSP_C(VEOF) +IMPLEMENT_TSP_C(VEOL) +IMPLEMENT_TSP_C(VEOL2) +IMPLEMENT_TSP_C(VERASE) +IMPLEMENT_TSP_C(VINTR) +IMPLEMENT_TSP_C(VKILL) +IMPLEMENT_TSP_C(VLNEXT) +IMPLEMENT_TSP_C(VMIN) +IMPLEMENT_TSP_C(VQUIT) +IMPLEMENT_TSP_C(VREPRINT) +IMPLEMENT_TSP_C(VSTART) +IMPLEMENT_TSP_C(VSTOP) +IMPLEMENT_TSP_C(VSUSP) +IMPLEMENT_TSP_C(VTIME) +IMPLEMENT_TSP_C(VWERASE) + +BEGIN_PROPERTY(TerminalSettings_InputSpeed) + + if (READ_PROPERTY) + GB.ReturnInteger(cfgetispeed(&SETTINGS)); + else + cfsetispeed(&SETTINGS, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(TerminalSettings_OutputSpeed) + + if (READ_PROPERTY) + GB.ReturnInteger(cfgetospeed(&SETTINGS)); + else + cfsetospeed(&SETTINGS, VPROP(GB_INTEGER)); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_PROPERTY(StreamTerm_Name) + + GB.ReturnNewZeroString(ttyname(STREAM_FD)); + +END_PROPERTY + +BEGIN_METHOD_VOID(StreamTerm_GetAttr) + + CTERMINALSETTINGS *settings; + + //fprintf(stderr, "fd = %d\n", STREAM_FD); + + settings = (CTERMINALSETTINGS *)GB.New(GB.FindClass("TerminalSettings"), NULL, NULL); + if (check_error(tcgetattr(STREAM_FD, &settings->settings))) + { + GB.Unref(POINTER(&settings)); + return; + } + + GB.ReturnObject(settings); + +END_PROPERTY + +BEGIN_METHOD(StreamTerm_SetAttr, GB_INTEGER mode; GB_OBJECT settings) + + CTERMINALSETTINGS *settings; + struct termios check; + + settings = (CTERMINALSETTINGS *)VARG(settings); + if (GB.CheckObject(settings)) + return; + + check_error(tcsetattr(STREAM_FD, VARG(mode), &settings->settings)); + + tcgetattr(STREAM_FD, &check); + if (check.c_iflag != settings->settings.c_iflag || check.c_oflag != settings->settings.c_oflag || check.c_lflag != settings->settings.c_lflag) + GB.Error("Unable to set terminal attributes"); + +END_PROPERTY + +BEGIN_METHOD(StreamTerm_Flush, GB_INTEGER mode) + + check_error(tcflush(STREAM_FD, VARG(mode))); + +END_METHOD + +BEGIN_METHOD_VOID(StreamTerm_Drain) + + check_error(tcdrain(STREAM_FD)); + +END_METHOD + +BEGIN_METHOD(StreamTerm_Flow, GB_INTEGER mode) + + check_error(tcflow(STREAM_FD, VARG(mode))); + +END_METHOD + +BEGIN_METHOD_VOID(StreamTerm_SendBreak) + + check_error(tcsendbreak(STREAM_FD, 0)); + +END_METHOD + +/*BEGIN_PROPERTY(Terminal_Control) + + GB.ReturnNewZeroString(ctermid(NULL)); + +END_PROPERTY*/ + +//--------------------------------------------------------------------------- + +#define __TC(_name) GB_CONSTANT(#_name, "i", _name) + +GB_DESC TermDesc[] = +{ + GB_DECLARE_STATIC("Term"), + + __TC(TCSANOW), __TC(TCSADRAIN), __TC(TCSAFLUSH), + + __TC(TCIFLUSH), __TC(TCOFLUSH), __TC(TCIOFLUSH), + + __TC(TCIOFF), __TC(TCION), __TC(TCOOFF), __TC(TCOON), +#ifdef OS_LINUX + __TC(NL0), __TC(NL1), __TC(CR0), __TC(CR1), __TC(CR2), __TC(CR3), __TC(TAB0), __TC(TAB1), __TC(TAB2), __TC(TAB3), __TC(XTABS), __TC(BS0), __TC(BS1), __TC(VT0), __TC(VT1), __TC(FF0), __TC(FF1), +#endif + __TC(CS5), __TC(CS6), __TC(CS7), __TC(CS8), + + GB_CONSTANT("VDISABLE", "i", _POSIX_VDISABLE), + + __TC(B0), __TC(B50), __TC(B75), __TC(B110), __TC(B134), __TC(B150), __TC(B200), __TC(B300), __TC(B600), __TC(B1200), __TC(B1800), __TC(B2400), __TC(B4800), __TC(B9600), __TC(B19200), __TC(B38400), __TC(B57600), __TC(B115200), __TC(B230400), + +#ifndef OS_BSD + __TC(B460800), __TC(B500000), __TC(B576000), __TC(B921600), __TC(B1000000), __TC(B1152000), __TC(B1500000), __TC(B2000000), __TC(B2500000), __TC(B3000000), +#endif +#ifdef OS_LINUX +__TC(B3500000), __TC(B4000000), +#endif + + GB_END_DECLARE +}; + +#define __TSP(_name) GB_PROPERTY(#_name, "b", TerminalSettings_##_name) +#define __TSP_I(_name) GB_PROPERTY(#_name, "i", TerminalSettings_##_name) + +GB_DESC TerminalSettingsDesc[] = +{ + GB_DECLARE("TerminalSettings", sizeof(CTERMINALSETTINGS)), + GB_NOT_CREATABLE(), + + __TSP(IGNBRK), + __TSP(BRKINT), + __TSP(IGNPAR), + __TSP(PARMRK), + __TSP(INPCK), + __TSP(ISTRIP), + __TSP(INLCR), + __TSP(IGNCR), + __TSP(ICRNL), + __TSP(IXON), + __TSP(IXANY), + __TSP(IXOFF), +#ifdef OS_LINUX + __TSP(IUCLC), + __TSP(IUTF8), + + __TSP(OLCUC), + __TSP(OFILL), +#endif + __TSP(OPOST), + __TSP(ONLCR), + __TSP(OCRNL), + __TSP(ONOCR), + __TSP(ONLRET), + +#ifndef OS_BSD + __TSP_I(NLDLY), + __TSP_I(CRDLY), + __TSP_I(TABDLY), + __TSP_I(BSDLY), + __TSP_I(VTDLY), + __TSP_I(FFDLY), +#endif + + __TSP_I(CSIZE), + __TSP(CSTOPB), + __TSP(CREAD), + __TSP(PARENB), + __TSP(PARODD), + __TSP(HUPCL), + __TSP(CLOCAL), + __TSP(CRTSCTS), + + __TSP(ISIG), + __TSP(ICANON), + __TSP(ECHO), + __TSP(ECHOE), + __TSP(ECHOK), + __TSP(ECHONL), + __TSP(ECHOCTL), + __TSP(ECHOKE), + __TSP(FLUSHO), + __TSP(NOFLSH), + __TSP(TOSTOP), + __TSP(IEXTEN), + +#ifdef OS_LINUX + __TSP(CMSPAR), + __TSP(XCASE), +#endif +#ifndef OS_CYGWIN + __TSP(ECHOPRT), + __TSP(PENDIN), +#endif + + __TSP_I(VDISCARD), + __TSP_I(VEOF), + __TSP_I(VEOL), + __TSP_I(VEOL2), + __TSP_I(VERASE), + __TSP_I(VINTR), + __TSP_I(VKILL), + __TSP_I(VLNEXT), + __TSP_I(VMIN), + __TSP_I(VQUIT), + __TSP_I(VREPRINT), + __TSP_I(VSTART), + __TSP_I(VSTOP), + __TSP_I(VSUSP), + __TSP_I(VTIME), + __TSP_I(VWERASE), + + GB_PROPERTY("InputSpeed", "i", TerminalSettings_InputSpeed), + GB_PROPERTY("OutputSpeed", "i", TerminalSettings_OutputSpeed), + + GB_METHOD("MakeRaw", "TerminalSettings", TerminalSettings_MakeRaw, NULL), + GB_METHOD("Copy", "TerminalSettings", TerminalSettings_Copy, NULL), + + GB_END_DECLARE +}; + +GB_DESC StreamTermDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stream.Term"), + + //GB_STATIC_PROPERTY_READ("Control", "s", Terminal_Control), + + GB_METHOD("Flush", NULL, StreamTerm_Flush, "(Mode)i"), + GB_METHOD("Drain", NULL, StreamTerm_Drain, NULL), + GB_METHOD("Flow", NULL, StreamTerm_Flow, "(Mode)i"), + GB_METHOD("SendBreak", NULL, StreamTerm_SendBreak, NULL), + + GB_METHOD("GetAttr", "TerminalSettings", StreamTerm_GetAttr, NULL), + GB_METHOD("SetAttr", NULL, StreamTerm_SetAttr, "(Mode)i(Settings)TerminalSettings;"), + + //GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; + diff --git a/main/lib/term/cterm.h b/main/lib/term/cterm.h new file mode 100644 index 00000000..cf51c321 --- /dev/null +++ b/main/lib/term/cterm.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + cterm.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTERM_H +#define __CTERM_H + +#include "gambas.h" +#include +#include + +#ifndef __CTERM_C +extern GB_DESC TermDesc[]; +extern GB_DESC StreamTermDesc[]; +extern GB_DESC TerminalSettingsDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + struct termios settings; + } + CTERMINALSETTINGS; + +#endif diff --git a/main/lib/term/gb.term.component b/main/lib/term/gb.term.component new file mode 100644 index 00000000..6e790664 --- /dev/null +++ b/main/lib/term/gb.term.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.term +Author=Benoît Minisini diff --git a/main/lib/term/main.c b/main/lib/term/main.c new file mode 100644 index 00000000..4bc5caa3 --- /dev/null +++ b/main/lib/term/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "cterm.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + TermDesc, + TerminalSettingsDesc, + StreamTermDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/term/main.h b/main/lib/term/main.h new file mode 100644 index 00000000..6425d51a --- /dev/null +++ b/main/lib/term/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif diff --git a/main/lib/test/Makefile.am b/main/lib/test/Makefile.am new file mode 100644 index 00000000..15adfc3d --- /dev/null +++ b/main/lib/test/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.test +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.test.la + +gb_test_la_LIBADD = @C_LIB@ +gb_test_la_LDFLAGS = -module @LD_FLAGS@ +gb_test_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx $(AM_CFLAGS) + +gb_test_la_SOURCES = \ + main.h main.c + + + diff --git a/main/lib/test/gb.test.component b/main/lib/test/gb.test.component new file mode 100644 index 00000000..be8cd9bb --- /dev/null +++ b/main/lib/test/gb.test.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.test +Version=3.14.90 +State=1 +Authors=Christof Thalhofer,Tobias Boege,Benoît Minisini diff --git a/main/lib/test/gb.test/.component b/main/lib/test/gb.test/.component new file mode 100644 index 00000000..ca970f35 --- /dev/null +++ b/main/lib/test/gb.test/.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.test +Version=3.18.0 +State=1 +Authors=Christof Thalhofer,Tobias Boege diff --git a/main/lib/test/gb.test/.directory b/main/lib/test/gb.test/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/lib/test/gb.test/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/lib/test/gb.test/.hidden/CHANGELOG b/main/lib/test/gb.test/.hidden/CHANGELOG new file mode 100644 index 00000000..10f60845 --- /dev/null +++ b/main/lib/test/gb.test/.hidden/CHANGELOG @@ -0,0 +1,177 @@ +* Wed Jan 22 2020 Christof Thalhofer 3.14.90 + +* Wed Jan 22 2020 Christof Thalhofer 3.14.90 + +* Wed Jan 22 2020 Christof Thalhofer 3.14.90 + +* Wed Jan 22 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 18 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 04 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 04 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 04 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 04 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 04 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 04 2020 Christof Thalhofer 3.14.90 + +* Sat Jan 04 2020 Christof Thalhofer 3.14.90 + +* Tue Sep 10 2019 Christof Thalhofer 3.12.56 + +* Tue Sep 10 2019 Christof Thalhofer 3.12.54 + +* Sat Feb 09 2019 Christof Thalhofer 3.12.45 + +* Sat Feb 02 2019 Christof Thalhofer 3.12.43 + +* Sat Jan 26 2019 Christof Thalhofer 3.12.41 + +* Sat Jan 26 2019 Christof Thalhofer 3.12.41 + +* Sat Jan 26 2019 Christof Thalhofer 3.12.40 + +* Sat Jan 26 2019 Christof Thalhifer 3.12.39 +- deg.gb.test + +* Thu Jan 17 2019 Christof Thalhifer 3.12.37 + +* Wed Jan 16 2019 Christof Thalhifer 3.12.34 + +* Wed Jan 16 2019 Christof Thalhifer 3.12.32 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.30 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.28 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.27 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.26 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.25 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.24 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.24 + +* Fri Jan 11 2019 Christof Thalhifer 3.12.24 + +* Sun Jan 06 2019 Christof Thalhifer 3.12.23 + +* Sun Jan 06 2019 Christof Thalhifer 3.12.22 + +* Sun Jan 06 2019 Christof Thalhifer 3.12.21 + +* Sun Jan 06 2019 Christof Thalhifer 3.12.20 + +* Sun Jan 06 2019 Christof Thalhifer 3.12.19 +- Selftest Ok + +* Sun Jan 06 2019 Christof Thalhifer 3.12.11 + +* Sun Jan 06 2019 Christof Thalhifer 3.12.8 + +* Sun Jan 06 2019 Christof Thalhifer 3.12.7 + +* Sat Jan 05 2019 Christof Thalhifer 3.12.6 + +* Sat Jan 05 2019 Christof Thalhifer 3.12.5 + +* Sat Jan 05 2019 Christof Thalhifer 3.12.4 + +* Sat Jan 05 2019 Christof Thalhifer 3.12.4 + +* Sat Jan 05 2019 Christof Thalhifer 3.12.3 + +* Sat Jan 05 2019 Christof Thalhifer 3.12.2 +- start with gb.test + +* Mon Nov 13 2017 christof 3.9.25 +- assert date + +* Sat Aug 12 2017 christof 3.9.23 +- refact, some fixes + +* Sat Aug 12 2017 christof 3.9.18 + +* Sat Aug 12 2017 christof 3.9.17 +- test install again + +* Sat Aug 12 2017 christof 3.9.17 +- test install + +* Sat Aug 12 2017 christof 3.9.17 +- internal testnames start with d8e8fca2dc0f896fd7cb4cb0031ba249 + +* Sat Jul 01 2017 christof 3.9.14 +- test install + +* Fri Feb 24 2017 christof 3.9.12 + +* Fri Feb 24 2017 christof 3.9.11 +- test + +* Thu Sep 29 2016 christof 3.9.10 + +* Thu Sep 29 2016 christof 3.9.10 + +* Wed Sep 28 2016 christof 3.9.10 +- Get testclasses from local classes + +* Sun Sep 25 2016 christof 3.9.9 + +* Sun Sep 25 2016 christof 3.9.9 + +* Sun Sep 25 2016 christof 3.9.9 + +* Sun Sep 25 2016 christof 3.9.9 + +* Sun Sep 25 2016 christof 3.9.9 + +* Sun Sep 25 2016 christof 3.9.9 +- Unittests file + +* Sat Sep 24 2016 christof 3.9.7 +- test fewer exports + +* Fri Sep 23 2016 christof 3.9.7 + +* Fri Sep 23 2016 christof 3.9.7 +- no prefix for TestContainers + +* Fri Sep 23 2016 christof 3.9.6 +- test + +* Fri Sep 23 2016 christof 3.9.6 +- test + +* Thu Sep 22 2016 christof 3.9.6 +- + +* Thu Sep 22 2016 christof 3.9.6 +- renamed to gb.deg.unittest + +* Thu Sep 22 2016 christof 3.9.6 + +* Wed Sep 21 2016 christof 3.9.5 +- Unhide classes + +* Wed Sep 21 2016 christof 3.9.4 +- AssertFalse + +* Wed Sep 21 2016 christof 3.9.3 + +* Wed Sep 21 2016 christof 3.9.3 +- own tests disabled + +* Wed Sep 21 2016 christof 3.9.2 +- gb entfernt + +* Wed Sep 21 2016 christof 3.9.1 +- Erstes Release + diff --git a/main/lib/test/gb.test/.hidden/flowchart.fodg b/main/lib/test/gb.test/.hidden/flowchart.fodg new file mode 100644 index 00000000..64f94786 --- /dev/null +++ b/main/lib/test/gb.test/.hidden/flowchart.fodg @@ -0,0 +1,617 @@ + + + + Christof Thalhofer2020-02-29T11:47:47.8402312232020-03-01T22:25:49.037241399PT44M22S16LibreOffice/5.1.6.2$Linux_X86_64 LibreOffice_project/10m0$Build-2 + + + 3713 + 9234 + 30162 + 26060 + + + view1 + true + false + true + true + false + false + false + false + true + 1500 + false + //////////////////////////////////////////8= + //////////////////////////////////////////8= + + false + true + true + 0 + 0 + true + true + true + 4 + 0 + -9738 + -8011 + 48655 + 32706 + 1000 + 1000 + 200 + 200 + 200 + 1 + 200 + 1 + false + 1500 + false + + + + + true + $(inst)/share/palette%3B$(user)/config/standard.sob + 0 + $(inst)/share/palette%3B$(user)/config/standard.soc + $(inst)/share/palette%3B$(user)/config/standard.sod + 1250 + false + + + de + DE + + + + + + $(inst)/share/palette%3B$(user)/config/standard.sog + $(inst)/share/palette%3B$(user)/config/standard.soh + false + false + true + true + false + false + true + false + false + false + $(inst)/share/palette%3B$(user)/config/standard.soe + false + 3 + 4 + false + 0 + low-resolution + Generic Printer + gAH+/0dlbmVyaWMgUHJpbnRlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAMApgAAAAAAAAAEAAhSAAAEdAAASm9iRGF0YSAxCnByaW50ZXI9R2VuZXJpYyBQcmludGVyCm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCm1hcmdpbmRhanVzdG1lbnQ9MCwwLDAsMApjb2xvcmRlcHRoPTI0CnBzbGV2ZWw9MApwZGZkZXZpY2U9MApjb2xvcmRldmljZT0wClBQRENvbnRleERhdGEKUGFnZVNpemU6QTQAABIAQ09NUEFUX0RVUExFWF9NT0RFCgBEVVBMRVhfT0ZG + false + 1 + 1 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Project to + be tested + + + + + + + + Testcode + + + + + + Productioncode + + + + + + + + + + + + + + + + + + gb.test + + + + + + Unittest + + + + + Assert.xyz + + + + + + + + + + + After testing done calls + + + + + calls + + + + + tests + + + + + calls + + + + + calls + + + + + + + + + + + + gb.test.tap + + + + + + Track + + + + + Ok + + + + + Summarize + + + + + + + + + + + Print to Stdout + + + + + + + + does + + + + + does + + + + + Tracking Data + + + + + + + + stores + + + + + Unittest + + + + + + + + + + + uses + + + + + + + + + + gbt3 + + + + + + + + + Preparesprojectand thencalls + + + + + + \ No newline at end of file diff --git a/main/lib/test/gb.test/.hidden/flowchart.svg b/main/lib/test/gb.test/.hidden/flowchart.svg new file mode 100644 index 00000000..76ba207f --- /dev/null +++ b/main/lib/test/gb.test/.hidden/flowchart.svg @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Project to be tested + + + + + + + + Testcode + + + + + + + + Productioncode + + + + + + + + + + + + + + + + + + + + + + + + + + gb.test + + + + + + + Unittest + + + + + + Assert.xyz + + + + + + + + + + + + + + + + + + + + After testing done calls + + + + + + calls + + + + + + tests + + + + + + calls + + + + + + calls + + + + + + + + + + + + + + gb.test.tap + + + + + + + Track + + + + + + Ok + + + + + + Summarize + + + + + + + + + + + + + + + + + + + + Print to Stdout + + + + + + + + + + + + + does + + + + + + does + + + + + + Tracking Data + + + + + + + + + + + + + stores + + + + + + Unittest + + + + + + + + + + + + + + + + + + + + uses + + + + + + + + + + + + + + gbt3 + + + + + + + + + + + + + + Preparesprojectand thencalls + + + + + + + \ No newline at end of file diff --git a/main/lib/test/gb.test/.hidden/gb.test.png b/main/lib/test/gb.test/.hidden/gb.test.png new file mode 100644 index 0000000000000000000000000000000000000000..31290c38892fab8a25ba4d9a4519aaf20b5baf9e GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIDN0X`wFKADB_@$qqBkdTl7WJ5?GV;fu1B%m^mk|4iehQBd-4bf4N84Ien z0>$DzT^vI+&L<}|ULM{NKA literal 0 HcmV?d00001 diff --git a/main/lib/test/gb.test/.hidden/summary-example.txt b/main/lib/test/gb.test/.hidden/summary-example.txt new file mode 100644 index 00000000..2efb5373 --- /dev/null +++ b/main/lib/test/gb.test/.hidden/summary-example.txt @@ -0,0 +1,14 @@ +# +# --------------- Fail --------------- +# 1000 Tests, 998 ok +# +# 2 Tests failed: +# 996: TestWurst:StretchWurst +# 998: TestSuppe:ThrowSuppe +# +# 1 Tests skipped: +# 100: TestKaese:LoecherSchneiden +# +# 1 Test Todo: +# 1000: TestBier:Trinken +# diff --git a/main/lib/test/gb.test/.icon.png b/main/lib/test/gb.test/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f4c43b01e76fca52d8563ea9f44e685376020bec GIT binary patch literal 8205 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π6~c} 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 Hn8g18VV0!c literal 0 HcmV?d00001 diff --git a/main/lib/test/gb.test/.lang/de.po b/main/lib/test/gb.test/.lang/de.po new file mode 100644 index 00000000..67074938 --- /dev/null +++ b/main/lib/test/gb.test/.lang/de.po @@ -0,0 +1,135 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.test 3.14.90\n" +"POT-Creation-Date: 2020-06-06 09:18 UTC\n" +"PO-Revision-Date: 2020-05-28 16:10 UTC\n" +"Last-Translator: christof \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "gb.test" +msgstr "" + +#: .project:2 +msgid "A Gambas component for unittesting and test-driven programming." +msgstr "Eine Gambas Komponente für Unittesting und testgetriebene Programmierung." + +#: Assert.module:137 +msgid "Expected value in RelativeApproximate is 0.0" +msgstr "" + +#: Assert.module:200 +msgid "Strings are of different lengths &1 and &2, respectively." +msgstr "Die Strings haben die unterschiedlichen Längen &1 bzw. &2." + +#: Assert.module:205 +msgid "Strings differ at position &1." +msgstr "Die Strings unterscheiden sich an Position &1." + +#: Assert.module:234 +msgid "No error happened" +msgstr "Kein Fehler aufgetreten" + +#: Assert.module:238 +msgid "Error was: &1 (code: &2) at &3" +msgstr "Der Fehler lautete &1 (code: &2) hier: &3" + +#: Helper.module:139 +#, fuzzy +msgid "Could not find a test suite with the name '&1'" +msgstr "Eine Testsuite mit dem Namen '&1' wurde nicht gefunden." + +#: TapParser.class:52 +msgid "Indentation on line '&1' is malformed" +msgstr "" + +#: TapParser.class:63 +msgid "Unrecognised TAP version '&1'" +msgstr "Unbekannte TAP Version '&1'" + +#: TapParser.class:64 +msgid "Illegal TAP version '&1'" +msgstr "Illegale TAP Version '&1'" + +#: TapParser.class:65 +msgid "Unsupported TAP version &1" +msgstr "Nicht unterstützte TAP Version '&1'" + +#: TapParser.class:71 +msgid "Couldn't extract test plan from '&1'" +msgstr "Der Testplan konnte nicht aus '&1' extrahiert werden." + +#: TapParser.class:145 +msgid "Not a test line '&1'" +msgstr "Keine Testzeile: '&1' " + +#: TapParser.class:172 +msgid "Premature end of directive" +msgstr "Vorzeitiges Ende" + +#: TapParser.class:180 +msgid "Invalid directive '&1'" +msgstr "Ungültige Richtlinie '&1'" + +#: TapPrinter.class:49 +msgid "Tests already finished" +msgstr "Die Tests sind bereits beendet." + +#: TapPrinter.class:50 +msgid "Too late to plan. Already ran &1 tests" +msgstr "Zu spät für den Plan. Bereits &1 Tests gelaufen" + +#: TapPrinter.class:53 +msgid "Number of tests must be non-negative" +msgstr "Anzahl der Tests muss positiv sein" + +#: TapPrinter.class:113 +msgid "Warning: Description '&1' should not start with a number" +msgstr "Warnung: Die Beschreibung '&1' sollte nicht mit einer Zahl beginnen" + +#: TapPrinter.class:127 +msgid "Unsupported directive '&1'" +msgstr "Nicht unterstützte Richtlinie '&1'" + +#: Test.module:110 +msgid "Ran '&1' " +msgstr "Ausgeführt: '&1'" + +#: Test.module:111 +msgid "Planned &1 tests but ran &2" +msgstr "&1 Tests geplant, aber &2 gelaufen" + +#: Test.module:114 +msgid "&1 skipped:" +msgstr "" + +#: Test.module:115 +msgid "&1 todo:" +msgstr "" + +#: Test.module:116 +msgid "&1 bonus:" +msgstr "" + +#: Test.module:118 +msgid "&1 tests failed:" +msgstr "" + +#: Test.module:140 +msgid "&2: &1 -- &3 &4" +msgstr "" + +#: Test.module:247 +msgid "Could not load test module '&1'" +msgstr "Testmodul '&1' wurde nicht gefunden." + +#: TestRunner.module:40 +msgid "&1 - &2" +msgstr "" + +#~ msgid "Description '&1' may not contain a '#' character" +#~ msgstr "Die Beschreibung '&1' ist das Zeichen '#' nicht erlaubt." diff --git a/main/lib/test/gb.test/.lang/it.po b/main/lib/test/gb.test/.lang/it.po new file mode 100644 index 00000000..20616f2b --- /dev/null +++ b/main/lib/test/gb.test/.lang/it.po @@ -0,0 +1,20 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.test 3.14.90\n" +"PO-Revision-Date: 2019-12-03 15:12 UTC\n" +"Last-Translator: gian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# gb-ignore +#: .project:1 +msgid "gb.test" +msgstr "" + +#: .project:2 +msgid "A Gambas component for unittesting and test-driven programming." +msgstr "Un componente Gambas per il testing d'unità e lo sviluppo guidato dalle verifiche di programmazione." + diff --git a/main/lib/test/gb.test/.lang/pt_BR.po b/main/lib/test/gb.test/.lang/pt_BR.po new file mode 100644 index 00000000..5cdb422c --- /dev/null +++ b/main/lib/test/gb.test/.lang/pt_BR.po @@ -0,0 +1,157 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.test 3.14.90\n" +"PO-Revision-Date: 2020-06-12 00:52 UTC\n" +"Last-Translator: Gen Braga \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# gb-ignore +#: .project:1 +msgid "gb.test" +msgstr "" + +# gb-ignore +#: .project:2 +msgid "A Gambas component for unittesting and test-driven programming." +msgstr "" + +#: Assert.module:18 +msgid "Failure: Assertion forbidden inside _Setup or _Teardown." +msgstr "Falhou: Assertion proibido em _Setup ou _Teardown." + +#: Assert.module:143 +msgid "Expected value in RelativeApproximate is 0.0" +msgstr "" + +#: Assert.module:206 +msgid "Strings are of different lengths &1 and &2, respectively." +msgstr "Strings são de comprimentos diferentes &1 e &2, respectivamente." + +#: Assert.module:211 +msgid "Strings differ at position &1." +msgstr "Strings diferem na posição &1" + +#: Assert.module:240 +msgid "No error happened" +msgstr "Nenhum erro ocorrido" + +#: Assert.module:244 +msgid "Error was: &1 (code: &2) at &3" +msgstr "Erro foi: &1 (código: &2) em &3" + +#: Helper.module:139 +msgid "Could not find a test suite with the name '&1'" +msgstr "Não foi possível encontrar uma suíte de teste com o nome '&1'" + +#: TapParser.class:52 +msgid "Indentation on line '&1' is malformed" +msgstr "Identação na linha '&1' malformada" + +#: TapParser.class:63 +msgid "Unrecognised TAP version '&1'" +msgstr "Versão do TAP '&1' não reconhecida" + +#: TapParser.class:64 +msgid "Illegal TAP version '&1'" +msgstr "Versão do TAP '&1' ilegal" + +#: TapParser.class:65 +msgid "Unsupported TAP version &1" +msgstr "Versão do TAP '&1' não suportada" + +#: TapParser.class:71 +msgid "Couldn't extract test plan from '&1'" +msgstr "Não foi possível extrair plano de teste de '&1'" + +#: TapParser.class:145 +msgid "Not a test line '&1'" +msgstr "Não é uma linha de teste '&1'" + +#: TapParser.class:172 +msgid "Premature end of directive" +msgstr "Fim de diretiva prematudo" + +#: TapParser.class:180 +msgid "Invalid directive '&1'" +msgstr "Diretiva '&1' inválida" + +#: TapPrinter.class:49 +msgid "Tests already finished" +msgstr "Testes já terminaram" + +#: TapPrinter.class:50 +msgid "Too late to plan. Already ran &1 tests" +msgstr "Tarde demais para planejar. Já executou &1 testes" + +#: TapPrinter.class:53 +msgid "Number of tests must be non-negative" +msgstr "Número de testes deve ser não-negativo" + +#: TapPrinter.class:115 +msgid "Warning: Description '&1' should not start with a number" +msgstr "Atenção: Descrição '&1' não deve iniciar com um número" + +#: TapPrinter.class:129 +msgid "Unsupported directive '&1'" +msgstr "Diretiva '&1' não suportada" + +#: Test.module:111 +msgid "Ran '&1' " +msgstr "Executou '&1'" + +#: Test.module:112 +msgid "Planned &1 tests but ran &2" +msgstr "Planejado &1 testes mas executou &2" + +#: Test.module:115 +msgid "&1 skipped:" +msgstr "Pulado &1:" + +#: Test.module:116 +msgid "&1 todo:" +msgstr "" + +#: Test.module:117 +msgid "&1 bonus:" +msgstr "bonus &1:" + +#: Test.module:120 +msgid "&1 test plans failed:" +msgstr "&1 planos de teste falharam." + +#: Test.module:121 +msgid "&1 tests failed:" +msgstr "&1 testes falharam" + +# gb-ignore +#: Test.module:147 +msgid ": &1" +msgstr "" + +# gb-ignore +#: Test.module:149 +msgid "# &1" +msgstr "" + +# gb-ignore +#: Test.module:151 +msgid "&2: &1 -- &3&4" +msgstr "" + +#: Test.module:166 +msgid "Planned &1 but ran &2" +msgstr "Planejado &1 mas executou &2" + +#: Test.module:273 +msgid "Could not load test module '&1'" +msgstr "Não foi possível carregar módulo de teste '&1'" + +# gb-ignore +#: TestRunner.module:40 +msgid "&1 - &2" +msgstr "" + diff --git a/main/lib/test/gb.test/.project b/main/lib/test/gb.test/.project new file mode 100644 index 00000000..0e5b8d9e --- /dev/null +++ b/main/lib/test/gb.test/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +Title=gb.test +Startup=ZzzDoSth +Icon=.hidden/gb.test.png +Version=3.18.0 +VersionFile=1 +Description="A Gambas component for unittesting and test-driven programming." +Authors="Christof Thalhofer\nTobias Boege" +TabSize=4 +Translate=1 +Language=en_US +Type=Component +ControlPublic=1 +ModulePublic=1 +SourcePath=/home/christof/temp +Maintainer=Christof Thalhofer +Address=christof@deganius.de +Url=www.deganius.de +License=General Public License +Prefix=1 +PackageName=gambas3-gb-test-3.14.90 +RuntimeVersion="3.9" +Packager=1 +Systems=debian +Groups=debian:"debug" diff --git a/main/lib/test/gb.test/.src/Helper.module b/main/lib/test/gb.test/.src/Helper.module new file mode 100644 index 00000000..a1bc595e --- /dev/null +++ b/main/lib/test/gb.test/.src/Helper.module @@ -0,0 +1,141 @@ +' Gambas module file + +Private $iSectionSize As Integer +Private $iSectionPos As Integer + +Private Sub GotoNextSection(hFile As File) + + $iSectionPos += $iSectionSize + Seek #hFile, $iSectionPos + $iSectionSize = Read #hFile As Integer + $iSectionPos += 4 + +End + +Private Sub ReadZeroString(hFile As File) As String + + Dim sStr As String + Dim iPos As Integer + + Do + sStr &= Read #hFile, Min(16, Lof(hFile) - Seek(hFile)) + iPos = InStr(sStr, Chr$(0)) + If iPos Then Return Left(sStr, iPos - 1) + Loop + +End + +Public Sub CheckTestModule(Name As String) As String + + Dim sPath As String + Dim hFile As File + Dim iVal As Integer + Dim iParent As Integer + Dim iFlag As Short + Dim bDebug As Boolean + Dim iStringPos As Integer + Dim sName As String + + sPath = ".../.gambas" &/ UCase(Name) + If Not Exist(sPath) Then Error.Raise("Class not found") + + $iSectionPos = 0 + $iSectionSize = 0 + + hFile = Open sPath + + Seek #hFile, 8 + iVal = Read #hFile As Integer + If iVal <> &H12345678 Then hFile.ByteOrder = 1 - hFile.ByteOrder + Seek #hFile, 12 + iVal = Read #hFile As Integer + bDebug = iVal And 1 + + $iSectionSize = 16 + + GotoNextSection(hFile) ' info + Seek #hFile, $iSectionPos + iParent = Read #hFile As Short + iFlag = Read #hFile As Short + + 'hStat.Exported = BTst(iFlag, 0) + 'hStat.AutoCreate = BTst(iFlag, 1) + 'hStat.Optional = BTst(iFlag, 2) + 'hStat.NoCreate = BTst(iFlag, 3) + 'hStat.HasFast = BTst(iFlag, 4) + If BTst(iFlag, 5) Then + + GotoNextSection(hFile) ' description + GotoNextSection(hFile) ' constant + GotoNextSection(hFile) ' reference + + If iParent <> -1 Then + + Seek #hFile, $iSectionPos + iParent * 4 + iParent = Read #hFile As Integer + iParent = Abs(iParent) + + Endif + + Do + iStringPos = $iSectionPos + Try GotoNextSection(hFile) + If Error Then Break + Loop + + Seek #hFile, iStringPos + sName = ReadZeroString(hFile) + + Close hFile + + Endif + + Return sName + +End + +Public Function GetTestSuiteByName(Name As String) As String + + Dim hFile As File + Dim sLine As String + Dim sName As String + Dim sTests As String + Dim bName As Boolean + Dim bTests As Boolean + + If Name Begins "@" Then Name = Mid$(Name, 2) + + hFile = Open ".../.test" + For Each sLine In hFile.Lines + + If sLine Begins "[" Then + bName = False + bTests = False + Else If sLine Begins "Name=" Then + sName = UnQuote(Mid$(sLine, 6)) + If bTests And If sName = Name Then Return sTests + bName = True + Else If sLine Begins "Tests=" Then + sTests = UnQuote(Mid$(sLine, 7)) + If bName And If sName = Name Then Return sTests + bTests = True + Endif + + Next + + ' Dim Set As New Settings(Application.Path &/ ".test") + ' Dim sKey As String + ' + ' If Name Begins "@" Then + ' Name = Right(Name, String.Len(Name) - 1) + ' Endif + ' + ' For Each sKey In Set.Keys + ' If Set[sKey &/ "Name"] = Name Then + ' Return Set[sKey &/ "Tests"] + ' Endif + ' Next + ' + Error.Raise(Subst(("Could not find a test suite with the name '&1'"), Name)) + +End diff --git a/main/lib/test/gb.test/.src/Tap/TapContext.class b/main/lib/test/gb.test/.src/Tap/TapContext.class new file mode 100644 index 00000000..0e7e6cee --- /dev/null +++ b/main/lib/test/gb.test/.src/Tap/TapContext.class @@ -0,0 +1,16 @@ +' Gambas class file + +''' The internal state of a TapPrinter. There is one such object for every subtest. + +'' The number of tests planned. +Public Plan As Integer +'' Whether the test is skipped altogether ("1..0 # SKIP" plan line). +Public SkipAll As Boolean +'' The number of tests run so far. +Public TestsRun As Integer +'' The serial ID used for the last assertion. +Public LastId As Long +'' Whether this test was finished by TapPrinter.Finish(). +Public Finished As Boolean +'' An assertion object representing the entirety of this subtest. +Public Summary As New TestAssertion diff --git a/main/lib/test/gb.test/.src/Tap/TapParser.class b/main/lib/test/gb.test/.src/Tap/TapParser.class new file mode 100644 index 00000000..9a3cdebd --- /dev/null +++ b/main/lib/test/gb.test/.src/Tap/TapParser.class @@ -0,0 +1,189 @@ +' Gambas class file + +''' Low-level parser for Test Anything Protocol ([TAP]) output. +''' It can only be used for one test process. +''' +''' [TAP]: http://testanything.org/tap-specification.html +''' +''' Supports parsing subtests as generated by TapPrinter: +''' A new subtest is announced by a "Test ..." line and the +''' whole subtest is on a new indentation level. + +Event Version(Version As Integer) + +Event Plan(Start As Integer, {End} As Integer, SkipAll As Boolean) +Event Assert(Ok As Boolean, Id As Long, Description As String, Directive As Integer, Comment As String) +Event BailOut(Comment As String) +Event Diagnostic(Comment As String) +Event Else({Line} As String) + +' Subtest events +Event BeginSubtest(Description As String) +Event EndSubtest + +Private $iLine As Integer = 0 +Private $iTestsRun As Integer = 0 +Private $iIndent As Integer = 0 + +Public Sub Parse(TapStream As Stream) + + While Not Eof(TapStream) + ParseLine(TapStream.ReadLine()) + Wend + +End + +Public Sub ParseLine({Line} As String) + + Dim sLine As String = {Line} + Dim iVersion As Integer + + Dim bResult As Boolean + Dim iId As Long + Dim iStart, iEnd As Integer + Dim bSkipAll As Boolean + Dim sDescription As String + Dim iDirective As Integer + Dim sComment As String + + ' Subtract current subtest indentation. TapPrinter uses 2 spaces. + If $iIndent > 0 And If Left$(sLine, 2 * $iIndent) Not Like " " Then + Dec $iIndent + If $iIndent > 0 And If Left$(sLine, 2 * $iIndent) Not Like " " Then Error.Raise(Subst$(("Indentation on line '&1' is malformed"), sLine)) + Raise EndSubtest + Endif + If $iIndent > 0 Then sLine = Right$(sLine, -2 * $iIndent) + + If $iLine = 0 And If sLine Begins "TAP version " Then + Try iVersion = CInt(Trim$(Mid$(sLine, 13))) + ' At present a "TAP version" line is always an error: + ' 1. It might not be an integer, which is an error + ' 2. A version lower than 13 is an error by the specification + ' 3. We don't support version 13 or above. + If Error Then Error.Raise(Subst$(("Unrecognised TAP version '&1'"), Trim$(Mid$(sLine, 13)))) + If iVersion < 13 Then Error.Raise(Subst$(("Illegal TAP version '&1'"), iVersion)) + If iVersion > 12 Then Error.Raise(Subst$(("Unsupported TAP version &1"), iVersion)) + Endif + If $iLine = 0 Then Raise Version(12) + + If sLine Match "^\\d+..\\d+" Then + bResult = ParsePlan(sLine, ByRef iStart, ByRef iEnd, ByRef bSkipAll) + If Not bResult Then Error.Raise(Subst$(("Couldn't extract test plan from '&1'"), sLine)) + Raise Plan(iStart, iEnd, bSkipAll) + + Else If sLine Begins "ok" Or If sLine Begins "not ok" Then + bResult = ParseTest(sLine, ByRef iId, ByRef sDescription, ByRef iDirective, ByRef sComment) + Raise Assert(bResult, iId, sDescription, iDirective, sComment) + + Else If sLine Begins "Bail out!" Then + Raise BailOut(Trim$(Mid$(sLine, 10))) + + Else If sLine Begins "#" Then + Raise Diagnostic(Trim$(Mid$(sLine, 2))) + + Else If sLine Begins "Test " Then + Raise BeginSubtest(Trim$(Mid$(sLine, 5))) + Inc $iIndent + + Else + Raise Else(sLine) + Endif + + Inc $iLine + +End + +Private Function ParsePlan(sLine As String, ByRef Start As Integer, ByRef {End} As Integer, ByRef SkipAll As Boolean) As Boolean + + Dim sComment As String + + SkipAll = False + With Scan(sLine, "*..*") + If Not IsInteger(.[0]) Then Return False + Start = CInt(.[0]) + If IsInteger(.[1]) Then + {End} = CInt(.[1]) + Else If .[1] Match "^\\d+\\s*#" + With Scan(.[1], "*#*") + {End} = CInt(Trim$(.[0])) + sComment = Trim$(.[1]) + If Upper$(sComment) Begins "SKIP" Then + SkipAll = True + Endif + End With + Else + Return False + Endif + End With + + Return True + +End + +Private Function ParseTest(sLine As String, ByRef Id As Long, ByRef Description As String, ByRef Directive As Integer, ByRef Comment As String) As Boolean + + Dim bResult As Boolean + Dim aWords As String[] + Dim sWord As String + + ' Tidy up caller's local variables + Id = 0 + Description = "" + Directive = TestAssertion.NONE + Comment = "" + + Inc $iTestsRun + + ' "ok" or "not ok" + If sLine Begins "ok" Then + bResult = True + sLine = Trim$(Mid$(sLine, 3)) + Else If sLine Begins "not ok" + bResult = False + sLine = Trim$(Mid$(sLine, 7)) + Else + Error.Raise(Subst$(("Not a test line '&1'"), sLine)) + Endif + + ' Make sure that if a "#" occurs, it will be after a space + sLine = Replace$(sLine, "#", " #") + aWords = Split(sLine, " ", "", True).Reverse() + + ' Test ID + Try sWord = aWords.Pop() + Try Id = CLong(sWord) + If Error Then + aWords.Push(sWord) + Id = $iTestsRun + Endif + + ' Description + While aWords.Count + sWord = aWords.Pop() + If sWord Begins "#" Then Break + Description &= sWord & " " + Wend + Description = Trim$(Description) + + ' Directive + If sWord Begins "#" Then + If sWord = "#" Then + Try sWord = aWords.Pop() + If Error Then Error.Raise(("Premature end of directive")) + Endif + Select Case Lower$(sWord) + Case "todo" + Directive = TestAssertion.TODO + Case "skip" + Directive = TestAssertion.SKIP + Default + Error.Raise(Subst$(("Invalid directive '&1'"), sWord)) + End Select + Endif + + ' Comment + Comment = Trim$(aWords.Reverse().Join(" ")) + + Return bResult + +End diff --git a/main/lib/test/gb.test/.src/Tap/TapPrinter.class b/main/lib/test/gb.test/.src/Tap/TapPrinter.class new file mode 100644 index 00000000..4c977f5f --- /dev/null +++ b/main/lib/test/gb.test/.src/Tap/TapPrinter.class @@ -0,0 +1,204 @@ +' Gambas class file + +''' Low-level class for planning and printing test results in Test Anything Protocol ([TAP]) format. +''' +''' [TAP]: http://testanything.org/tap-specification.html + +Event Filter + +Property {Output} As Stream Use $hOutput +Property Read Session As TapContext + +Property Line As String Use $sLine + +Private $aSubtests As New TapContext[] +Private $hCurrent As New TapContext + +Public Sub _new(Optional Tests As Integer, Optional Comment As String, Optional {Output} As Stream = File.Out) + + $hOutput = {Output} + With $hCurrent + .Plan = Test.NO_PLAN + .TestsRun = 0 + .LastId = 0 + .Finished = False + End With + If Not IsMissing(Tests) Then Plan(Tests, Comment) + +End + +Public Sub Subtest(Description As String, Optional Tests As Integer, Optional Comment As String) + + Print(Subst$("Test &1", Description)) + $aSubtests.Push($hCurrent) + With $hCurrent = New TapContext + .Summary.Description = Description + .Summary.Comment = Comment + .Plan = Test.NO_PLAN + .TestsRun = 0 + .LastId = 0 + .Finished = False + End With + If Tests Then Plan(Tests) + +End + +Public Sub Plan(Tests As Integer, Optional Comment As String) + + With $hCurrent + If .Finished Or .SkipAll Then Error.Raise(("Tests already finished")) + If .TestsRun Then Error.Raise(Subst$(("Too late to plan. Already ran &1 tests"), .TestsRun)) + ' TAP specification lists '1..0 # Skipped: WWW::Mechanize not installed' + ' as a valid example. + If Tests <= Test.NO_PLAN Then Error.Raise(("Number of tests must be non-negative")) + .Plan = Tests + End With + Print(Subst$("1..&1&2", Tests, IIf(Comment, " # " & Comment, ""))) + +End + +Public Sub SkipAll(Optional Comment As String) + + Plan(0, "SKIP" & IIf(Comment, " ", "") & Comment) + $hCurrent.SkipAll = True + +End + +Public Sub Finish() + + Dim hFinished As TapContext + + With $hCurrent + Dim hTest As TestAssertion + + If .Finished Then Error.Raise(("Tests already finished")) + + ' Print a plan line after the fact + If .Plan = Test.NO_PLAN Then + .Plan = .TestsRun + Print("1.." & .Plan) + Endif + + ' Sum up this subtest + .Finished = True + .Summary.SubPlanned = .Plan + .Summary.SubSkippedAll = .SkipAll + .Summary.Ok = (.Plan > 0 Or .SkipAll) And (.TestsRun = .Plan) + For Each hTest In .Summary.Subtests + .Summary.Ok = .Summary.Ok And hTest.Success + Next + End With + + ' Go one level up if possible. Unless we are the top-level test, + ' we add one assertion for this finishing subtest. + hFinished = $hCurrent + Try $hCurrent = $aSubtests.Pop() + If Not Error Then + Assert(hFinished.Summary) + Endif + +End + +Public Sub Assert(Assertion As TestAssertion) As TestAssertion + + Dim sDirective As String + Dim sLine As String + + With Assertion + If $hCurrent.Finished Or $hCurrent.SkipAll Then Error.Raise(("Tests already finished")) + + ' It is not advised to start a description with a number token because + ' it will be interpreted as the (optional) test ID. We issue a warning + ' about this but fix it anyway by always printing the ID before *and* + ' prefixing the Description with a dash, as is common. + If .Description Match "^[0-9]+(\\s|$)" Then + Error Subst$(("Warning: Description '&1' should not start with a number"), .Description) + Endif + ' If .Description Like "*#*" Then + ' Error.Raise(Subst$(("Description '&1' may not contain a '#' character"), .Description)) + ' Endif + + Inc $hCurrent.TestsRun + If Not .Id Then .Id = $hCurrent.TestsRun + $hCurrent.LastId = .Id + If .Line Then + sLine = Subst$("&1 &2 - &3: &4", IIf(.Ok, "ok", "not ok"), .Id, .Line, .Description) + Else + sLine = Subst$("&1 &2 - &3", IIf(.Ok, "ok", "not ok"), .Id, .Description) + Endif + + If .Directive <> TestAssertion.NONE Then + ' Matches the values of the Enum TestAssertion.TODO, TestAssertion.SKIP + sDirective = Choose(.Directive, "TODO", "SKIP") + If Not sDirective Then Error.Raise(Subst$(("Unsupported directive '&1'"), .Directive)) + sLine &= " # " & sDirective + If .Comment Then sLine &= " " & .Comment + Endif + End With + + Print(sLine) + $hCurrent.Summary.Subtests.Push(Assertion) + Return Assertion + +End + +'' Unwinds the subtest stack. BailOut will always bail out of all the subtests. +Private Sub Unwind() + + While $aSubtests.Count > 0 + Note("Unwinding subtest stack!") + Finish() + Wend + +End + +Public Sub BailOut(Optional Comment As String) + + Unwind() + 'The string "Bail out!" must not be changed! + Print("Bail out!" & IIf(Comment, " " & Comment, "")) + $hCurrent.Finished = True + +End + +Public Sub Diagnostic(Comment As String) + + Dim sLine As String + + ' Split on these doesn't do what I mean: on the empty string, we get + ' zero lines, on a newline we get two. I want one in both cases. + If Not Comment Or If Comment = gb.Lf Then + Print("#") + Return + Endif + + For Each sLine In Split(Comment, "\n") + Print("# " & sLine) + Next + +End + +Public Sub Note(Comment As String) + + Diagnostic(Comment) + +End + +Public Sub Print({Line} As String) + + Dim bCancel As Boolean + + $sLine = {Line} + bCancel = Raise Filter + If bCancel Then Return + Print #$hOutput, String$($aSubtests.Count, " "); $sLine + Flush #$hOutput + +End + +Private Function Session_Read() As TapContext + + If $aSubtests.Count Then Return $aSubtests[0] + Return $hCurrent + +End diff --git a/main/lib/test/gb.test/.src/TestAssertion.class b/main/lib/test/gb.test/.src/TestAssertion.class new file mode 100644 index 00000000..d627021d --- /dev/null +++ b/main/lib/test/gb.test/.src/TestAssertion.class @@ -0,0 +1,54 @@ +' Gambas class file + +''' This class represents an "ok" or "not ok" line in TAP. +''' It is generated by TapPrinter and TapParser. + +Export + +'' Possible values for the Directive field +Public Enum NONE = 0, TODO = 1, SKIP + +'' The serial number of this test. +Public Id As Long = 0 +'' Whether "ok" or "not ok" was printed. +Public Ok As Boolean = False +'' The test description. +Public Description As String = Null +'' The TAP directive constant NONE, TODO or SKIP. +Public Directive As Integer = NONE +'' The comment after the test description and directive, if any. +Public Comment As String = Null +'' If this is a subtest summary assertion, here are the child assertions. +Public Subtests As New TestAssertion[] +'' If this is a subtest summary assertion, its plan line. +Public SubPlanned As Integer +'' If this is a subtest summary assertion, whether it was skipped completely. +Public SubSkippedAll As Boolean +'' The position in the code where the test was called +Public Line As String + +'' Whether the test counts as succeeded. A "not ok" test can succeed if it was marked as TODO. +Property Read Success As Boolean +'' Whether all subtests succeeded. +Property Read SubSuccess As Boolean + +Private Function Success_Read() As Boolean + + Return Ok Or (Directive = TODO) + +End + +Private Function SubSuccess_Read() As Boolean + + If Not subtests.Count Then Return Me.Success + + Dim bRes As Boolean = (Subtests.Count = SubPlanned) + Dim iInd As Integer = 0 + + While bRes And (iInd < Subtests.Count) + bRes = bRes And Subtests[iInd].SubSuccess + Inc iInd + Wend + Return bRes + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/MustFail.test b/main/lib/test/gb.test/.src/TestMyself/MustFail.test new file mode 100644 index 00000000..38685cfd --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/MustFail.test @@ -0,0 +1,30 @@ +' Gambas test file + +Public Sub TestNoteGotAndExpected() + + 'tests the typo of Expected and Got in diagnosis + 'This test must fail to see what happens + + Dim Expected, Got As String + + Test.Plan(1) + + Got = "

      " & gb.Lf & + "
    1. Zum 15.12. in die Wintermischung 2
    2. " & gb.Lf & + "
    3. Zum 15.2. in die Fellwechselmischung Frühjahr
    4. " & gb.Lf & + "
    5. Zum 15.4. in die Sommermischung
    6. " & gb.Lf & + "
    7. Zum 15.8. in die Fellwechselmischung Herbst
    8. " & gb.Lf & + "
    9. Zum 15.10. in die Wintermischung 1
    10. " & gb.Lf & + "
    " + + Expected = "
      " & gb.Lf & + Trim("
    1. Zum 15.12. in die Wintermischung 2
    2. ") & gb.Lf & + Trim("
    3. Zum 15.2. in die Fellwechselmischung Frühjahr
    4. ") & gb.Lf & + Trim("
    5. Zum 15.4. in die Sommermischung
    6. ") & gb.Lf & + Trim("
    7. Zum 15.8. in die Fellwechselmischung Herbst
    8. ") & gb.Lf & + Trim("
    9. Zum 15.10. in die Wintermischung 1
    10. ") & gb.Lf & + "
    " + + Assert.Equals(Got, Expected, "Tests the typo of Expected and Got in diagnosis. Must fail.") + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TAllAsserts.test b/main/lib/test/gb.test/.src/TestMyself/TAllAsserts.test new file mode 100644 index 00000000..f3359541 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TAllAsserts.test @@ -0,0 +1,368 @@ +' Gambas test file + +''' Tests all Asserts + +Public Sub Approximate() + + Dim f As Float + + Test.Plan(1) + f = 0.55555555555555 + Assert.Approximate(f, 0.55555555555555, 0, "Check with equal floats") + +End + +Public Sub Equals() + + Test.Plan(3) + + ' ------------------------------------------------- Long + + Dim l As Long + + l = 555555555555555555 + Assert.Equals(555555555555555555, l, "Check long") + + ' ------------------------------------------------- Date + + Dim d As Date + Dim sdate As String + Dim d2 As Date + + d = Now() + sdate = CString(d) + d2 = CDate(sdate) + + Assert.Equals(d, d2, "Check with equal dates") + + ' ------------------------------------------------- Object + + Dim s As New TestCase("1", Me) + Dim s2 As Object + + s2 = s + + Assert.Equals(s2, s, "Equal Objects") + +End + +Public Sub Error() + + Dim r As Float + + Test.Plan(1) + Try r = 2 / 0 + + Assert.Error("Test error") + +End + +Public Sub ErrorCode() + + Dim r As Float + + Test.Plan(1) + Try r = 2 / 0 + Assert.ErrorCode(26, "Test error with code") + +End + +Public Sub Fail() + + Test.Plan(1) + Test.IntendedFailure() + Assert.Fail() + +End + +Public Sub Greater() + + Dim small, great As Variant + + Test.Plan(4) + + ' ------------------------------------------------- String + small = "a" + great = "z" + Assert.Greater(great, small) + + ' ------------------------------------------------- Integer + + small = -1 + great = 0 + Assert.Greater(great, small) + + ' ------------------------------------------------- Float + + small = 0.1 + great = 10000000000000000.5 + Assert.Greater(great, small) + + ' ------------------------------------------------- Date + + small = Date(Year(Now), Month(Now), Day(Now)) + great = Date(Year(Now) + 1, Month(Now), Day(Now)) + Assert.Greater(great, small) + +End + +Public Sub GreaterEqual() + + Dim small, great As Variant + + Test.Plan(8) + + ' ------------------------------------------------- String + small = "a" + great = "z" + Assert.GreaterEqual(great, small) + Assert.GreaterEqual(great, great) + + ' ------------------------------------------------- Integer + + small = -1 + great = 0 + Assert.GreaterEqual(great, small) + Assert.GreaterEqual(great, great) + + ' ------------------------------------------------- Float + + small = 0.1 + great = 10000000000000000.5 + Assert.GreaterEqual(great, small) + Assert.GreaterEqual(great, great) + + ' ------------------------------------------------- Date + + small = Date(Year(Now), Month(Now), Day(Now)) + great = Date(Year(Now) + 1, Month(Now), Day(Now)) + Assert.GreaterEqual(great, small) + Assert.GreaterEqual(great, great) + +End + +Public Sub IsType() + + Dim thing As Variant + + Test.Plan(7) + + ' ------------------------------------------------- Boolean + thing = True + Assert.IsType(thing, gb.Boolean) + ' ------------------------------------------------- Integer + thing = 456548812 + Assert.IsType(thing, gb.Integer) + + thing = 4565488121245 + Assert.IsType(thing, gb.Long) + + ' ------------------------------------------------- Float + thing = 456548812124513564879.123456789134698797987987 + Assert.IsType(thing, gb.Float) + + ' ------------------------------------------------- string + thing = "i bi a sdring" + Assert.IsType(thing, gb.String) + + ' ------------------------------------------------- Date + thing = Now() + Assert.IsType(thing, gb.Date) + + ' ------------------------------------------------- Object + thing = Class.Load("TAllAsserts") + Assert.IsType(thing, gb.Object) + +End + +Public Sub Less() + + Dim small, great As Variant + + Test.Plan(4) + + ' ------------------------------------------------- String + small = "a" + great = "z" + Assert.Less(small, great) + + ' ------------------------------------------------- Integer + + small = -1 + great = 0 + Assert.Less(small, great) + + ' ------------------------------------------------- Long + + small = 0.1 + great = 1 + Assert.Less(small, great) + + ' ------------------------------------------------- Date + + small = Date(Year(Now), Month(Now), Day(Now)) + great = Date(Year(Now) + 1, Month(Now), Day(Now)) + Assert.Less(small, great) + + +End + +Public Sub LessEqual() + + Dim small, great As Variant + + Test.Plan(8) + + ' ------------------------------------------------- String + small = "a" + great = "z" + Assert.LessEqual(small, great) + Assert.GreaterEqual(great, great) + + ' ------------------------------------------------- Integer + + small = -1 + great = 0 + Assert.LessEqual(small, great) + Assert.GreaterEqual(great, great) + + ' ------------------------------------------------- Float + + small = 0.1 + great = 10000000000000000.5 + Assert.LessEqual(small, great) + Assert.GreaterEqual(great, great) + + ' ------------------------------------------------- Date + + small = Date(Year(Now), Month(Now), Day(Now)) + great = Date(Year(Now) + 1, Month(Now), Day(Now)) + Assert.LessEqual(small, great) + Assert.GreaterEqual(great, great) + +End + +Public Sub Like() + + Test.Plan(2) + Assert.Like("Gambas", "G*", "Gambas like G*") + Assert.Like("Gambas", "?[Aa]*", "Gambas Like \"?[Aa]*\"") + +End + +Public Sub Match() + + Test.Plan(4) + Assert.Match("25724689346a625b5", "^\\d+a\\d+?(\\d)b\\1$", "Match with a frugal quantifier and backreference") + Assert.Match("25724689346a625b5", "^\\d+a\\d+(\\d)b\\1$", "Match with a normal quantifier (backtracking) and backreference") + Test.IntendedFailure + Assert.Match("25724689346a625b5", "^\\d+a\\d++(\\d)b\\1$", "Non-match with a possessive quantifier and backreference") + Assert.Match(Format$(Now, "yyyy-mm-dd"), "^\\d{4}-\\d{2}-\\d{2}$", "Date pattern match") + +End + +Public Sub NotNull() + + Test.Plan(2) + Assert.NotNull("" & Null & 1, "String is not Null") + Assert.NotNull(Me, "Object is not Null") + +End + +Public Sub NotOk() + + Test.Plan(1) + Assert.NotOk(False) + +End + +Public Sub Notequals() + + Dim small, great As Variant + + Test.Plan(5) + + ' ------------------------------------------------- String + small = "a" + great = "z" + Assert.Notequals(great, small) + + ' ------------------------------------------------- Integer + + small = -1 + great = 0 + Assert.Notequals(great, small) + + ' ------------------------------------------------- Float + + small = 0.1 + great = 10000000000000000.5 + Assert.Notequals(great, small) + + ' ------------------------------------------------- Date + + small = Date(Year(Now), Month(Now), Day(Now)) + great = Date(Year(Now) + 1, Month(Now), Day(Now)) + Assert.Notequals(great, small) + + ' ------------------------------------------------- Object + + Dim s As New TestCase("1", Me) + Dim s2 As New TestCase("2", Me) + + Assert.Notequals(s2, s) + +End + +Public Sub Noterror() + + Dim x As Variant + Try x = 2 / 0 + + Test.Plan(1) + + 'btw we can test Error.Clear + Error.Clear() + Assert.Noterror() + +End + +Public Sub Null() + + Test.Plan(2) + Assert.Null("", "Empty with \"\"") + Assert.Null(Null, "Empty with Null") + +End + +Public Sub Ok() + + Test.Plan(1) + Assert.Ok(True, "Testing True") + +End + +Public Sub Pass() + + Test.Plan(1) + Assert.Pass("Should report ok") + +End + +Public Sub RelativeApproximate() + + Test.Plan(1) + Assert.RelativeApproximate(0, 0, 0) + +End + +Public Sub StringEquals() + + Dim s1, s2 As String + + s1 = "blaahhh" + s2 = "blaahhh" + Test.Plan(1) + Assert.StringEquals(s1, s2, "Equal Strings") + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TBailout.test b/main/lib/test/gb.test/.src/TestMyself/TBailout.test new file mode 100644 index 00000000..91cc4259 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TBailout.test @@ -0,0 +1,8 @@ +' Gambas test file + +Public Sub Bailout() + + Test.Note("This will stop all testing immediately") + Test.BailOut("Bo-Test") + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TCrashes.test b/main/lib/test/gb.test/.src/TestMyself/TCrashes.test new file mode 100644 index 00000000..594daf73 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TCrashes.test @@ -0,0 +1,22 @@ +' Gambas test file + +' Gambas test module file + +''' Every test in this class crashes. +''' But the test system has to report it +''' and shall not crash itself + +Public Sub ErrorTwo() + + Error.Raise("I shot the sheriff.") + +End + +Public Sub DoACrash() + + Dim a As Long + + 'This is a crash + a = 3 / 0 + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TElse.test b/main/lib/test/gb.test/.src/TestMyself/TElse.test new file mode 100644 index 00000000..81af4a0b --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TElse.test @@ -0,0 +1,31 @@ +' Gambas test file + +Public Sub NoMessage() + + 'There is no message + Test.Plan(1) + Assert.Ok(True) + +End + +Public Sub NoMessageInbetween() + + Test.Plan(4) + Assert.Ok(True, "TestNoMessageInbetween Assert 1 description defined") + Assert.Ok(True) + Assert.Ok(True, "Assert 3 description defined") + Assert.Ok(True) + +End + +Public Sub Note() + + Test.Note("This\nis\na\nnote") + Test.Plan(0) + Test.Note("The next note is Null and that's alright:") + Test.Note(Null) + Test.SkipAll("This doesn't actually assert anything") + +End + + diff --git a/main/lib/test/gb.test/.src/TestMyself/TEmpty.test b/main/lib/test/gb.test/.src/TestMyself/TEmpty.test new file mode 100644 index 00000000..e3242dcb --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TEmpty.test @@ -0,0 +1,3 @@ +' Gambas test file + +''' Testing will BailOut if a testmodule is empty diff --git a/main/lib/test/gb.test/.src/TestMyself/TIntendedFailures.test b/main/lib/test/gb.test/.src/TestMyself/TIntendedFailures.test new file mode 100644 index 00000000..21ef819d --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TIntendedFailures.test @@ -0,0 +1,66 @@ +' Gambas test file + +''' Every test in this class failes, but is reverted to be reported as good + +Public Sub StringFailure() + + Test.Plan(1) + Test.IntendedFailure() + Assert.StringEquals("Lisa", "Paul", "Intentional String failure. Ok if not ok.") + +End + +Public Sub LongFailure() + + Test.Plan(1) + Test.IntendedFailure() + Assert.Equals(2, 3, "Intentional Long failure. Ok if not ok.") + +End + +Public Sub LongTypeMismatchFailure() + + Dim s As String = "3.0" + + Test.Plan(1) + Test.IntendedFailure() + Assert.Equals(s, 3, "Intentional Long type mismatch failure. Ok if not ok.") + +End + +Public Sub EqualsFailure() + + Dim oOne, oTwo As Object + + Test.Plan(4) + Test.Note("Equals failures") + Test.IntendedFailure() + Assert.Equals(2, 3) + + Test.IntendedFailure() + Assert.Equals("3", 2.0) + + Test.IntendedFailure() + Assert.Equals("2", 2.1) + + oOne = New Test + oTwo = New Test + Test.IntendedFailure() + Assert.Equals(oOne, oTwo) + +End + +Public Sub Error() + + Dim a As Long + + Test.Plan(2) + Try a = 3 / 0 + Test.IntendedFailure() + Assert.ErrorCode(5, "Wanted error 5. Ok if not ok.") + + ' No Error, AssertError has to report a failure + Test.IntendedFailure() + Assert.Error("No Error but error expected. Ok if not ok.") + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TInternals.test b/main/lib/test/gb.test/.src/TestMyself/TInternals.test new file mode 100644 index 00000000..56712547 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TInternals.test @@ -0,0 +1,141 @@ +' Gambas test file + +Public Sub ParseTestCommands() + + Dim sCommand As String + Dim Commands As TestCommand[] + Dim sGot As String + Dim iGot As Integer + + Test.Plan(9) + + 'Three TestModules + sCommand = "Bing, Bong, Bung" + Commands = TestCommand.FromString(sCommand) + sGot = Commands[0].ModuleName & Commands[1].ModuleName & Commands[2].ModuleName + + Assert.Equals(sGot, "BingBongBung", "FromString: Three testmodules") + + 'Test testmodules sort + sCommand = "Bing,Bung,Bong" + Commands = TestCommand.FromString(sCommand) + sGot = Commands[0].ModuleName & Commands[1].ModuleName & Commands[2].ModuleName + + Assert.Equals(sGot, "BingBongBung", "FromString: Testmodules have to be sorted") + + 'Three testmodules, one with three methods + sCommand = "Bong.B, Bing, Bong.A, Bung, Bong.C" + Commands = TestCommand.FromString(sCommand) + sGot = Commands[0].ModuleName & Commands[1].ModuleName & Commands[2].ModuleName + Assert.Equals(sGot, "BingBongBung", "FromString: Three testmodules, one with three methods") + + 'Methods name sort + sGot = Commands[1].Methods.Join() + Assert.Equals(sGot, "A,B,C", "FromString: Methods name sort") + + 'Three testmodules, one with methods, but Bong also wanted all, the more greedy one has to get it + sCommand = "Bong, Bing, Bong.A, Bung, Bong.B, Bong.C" + Commands = TestCommand.FromString(sCommand) + 'Methods name sort + iGot = Commands[1].Methods.Count + Assert.Equals(iGot, 0, "FromString: Greedy testmodule Bong") + + 'Programmer was a little tipsy but got it all except Bung which only wants test U + sCommand = "Bong, Bong.A, Bing, Bing, Bing, Bing, Bing, Bing, Bong.A, Bung.U, Bong.B, Bong.C, Bing, Bing, Bing, Bong, Bong" + Commands = TestCommand.FromString(sCommand) + 'Methods name sort + iGot = Commands[0].Methods.Count + Assert.Equals(iGot, 0, "FromString: Tipsy programmer 1") + + iGot = Commands[1].Methods.Count + Assert.Equals(iGot, 0, "FromString: Tipsy programmer 2") + + iGot = Commands[2].Methods.Count + Assert.Equals(iGot, 1, "FromString: Tipsy programmer 3") + + sGot = Commands[2].Methods[0] + Assert.Equals(sGot, "U", "FromString: Tipsy programmer 4") + +End + +Public Sub CommandsToString() + + Dim Commands As TestCommand[] + + Commands = TestCommand.FromString("Bong, Bong.A, Bing, Bing, Bing, Bing, Bing, Bing, Bong.A, Bung.U, Bong.B, Bong.C, Bing, Bing, Bing, Bong, Bong") + Assert.Equals(TestCommand.ToString(Commands), "Bing,Bong,Bung.U") + +End + +'' Tests the ability to return all tests +'' in the project as a collection + +Public Sub ReflectTest() + + Dim tests As Collection + + Test.Plan(8) + + tests = Test.AllTestsCollection() + + 'count of all testmodules, if it fails we created a new one and we have to count new + Assert.Equals(tests.Count, 13, "number of all testmodules") + Assert.Equals(tests["TAllAsserts"].Count, 21, "count of all tests in TAllAsserts") + Assert.Equals(tests["TBailout"].Count, 1, "count of all tests in TBailout") + Assert.Equals(tests["TCrashes"].Count, 2, "count of all tests in TCrashes") + Assert.Equals(tests["TElse"].Count, 3, "count of all tests in TElse") + Assert.Equals(tests["TEmpty"].Count, 0, "count of all tests in TEmpty") + Assert.Equals(tests["TInternals"].Count, 6, "count of all tests in TInternals") + Assert.Equals(tests["TParser"].Count, 3, "count of all tests in TParser") + +End + +'' test the ability to create a testsuite string with my own tests +Public Sub ReflectTestsString() + + ' These have to be adjusted when tests change + ' If tests are updated, get them with + 'Dim easy As String = Test.AllTests() + + Dim got, want As String + + Test.Plan(2) + + want = "MustFail.TestNoteGotAndExpected,TAllAsserts.Approximate;Equals;Error;ErrorCode;Fail;Greater;GreaterEqual;IsType;Less;LessEqual;Like;Match;NotNull;NotOk;Notequals;Noterror;Null;Ok;Pass;RelativeApproximate;StringEquals,TBailout.Bailout,TCrashes.DoACrash;ErrorTwo,TElse.NoMessage;NoMessageInbetween;Note,TIntendedFailures.EqualsFailure;Error;LongFailure;LongTypeMismatchFailure;StringFailure,TInternals.CommandsToString;FindTestSuiteByName;InterpreterInterface;ParseTestCommands;ReflectTest;ReflectTestsString,TParser.Runner;SkipAll;Subtests,TSetup.A;B,TSkipAll.SkipAll,TSummary.DoBadPlan;DoOneFailure;DoSkip;DoSomeAsserts;DoTodo;TodoErrorCode,TWrongPlan.IHaveAWrongPlan" + got = Test.AllTests() + Assert.Equals(got, want, "All Test names as string") + + want = Test.AllTests() + got = TestCommand.ToString(TestCommand.FromString(want)) + + Assert.Equals(got, want, "Check that Testcommand To and Fromstring do it the same way as Test.AllTests") + +End + +Public Sub InterpreterInterface() + + Dim Commands As TestCommand[] + Dim Tests As String = "TestAllAsserts.TestAssertEqualsDate;TestAssertEmpty;TestAssert,TestElse" + + Test.Plan(4) + Test.Note("Tests the testsuite as string as it is run by the interpreter") + + Commands = TestCommand.FromString(Tests) + + Assert.Equals(Commands.Count, 2, "we have 2 commands") + Assert.Equals(Commands[0].Methods.Count, 3, "command 2 has 3 methods") + Assert.Equals(Commands[0].Methods[0], "TestAssert", "methods were sorted") + + Tests = "TestAllAsserts.TestAssert;TestAssertEmpty;TestAssertEqualsDate,TestElse" + Assert.Equals(TestCommand.ToString(Commands), Tests, "commands as string defining a testsuite") + +End + +Public Sub FindTestSuiteByName() + + Dim sName As String = "Keep this test suite, it is necessary for testing gb.test." + + Test.Plan(1) + Assert.Equals(Helper.GetTestSuiteByName(sName), "TInternals.FindTestSuiteByName") + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TParser.test b/main/lib/test/gb.test/.src/TestMyself/TParser.test new file mode 100644 index 00000000..4775cf25 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TParser.test @@ -0,0 +1,123 @@ +' Gambas test file + +Private $aSkipAllEvents As New String[] + +'' Test parsing of SkipAll plans +Public Sub SkipAll() + Dim sTAP As String = "" +"1..2\n" +"Test Run\n" +" 1..1\n" +" ok 1\n" +"ok 1\n" +"Test Skip\n" +" 1..0 # skipped\n" +"ok 2\n" + Dim hTAP As Stream + Dim hParser As New TapParser As "SkipAllParser" + + Test.Plan(4) + + hTAP = Open String sTAP For Read + hParser.Parse(hTAP) + + Test.Plan(3 + 1) + Assert.Equals($aSkipAllEvents.Count, 3, "Event count is correct") + Assert.Equals($aSkipAllEvents[0], "Plan 1..2") + Assert.Equals($aSkipAllEvents[1], "Plan 1..1") + Assert.Equals($aSkipAllEvents[2], "Skip") + + Close #hTAP +End + +Public Sub SkipAllParser_Plan((Start) As Integer, (End) As Integer, (SkipAll) As Boolean) + $aSkipAllEvents.Add(IIf(SkipAll, "Skip", Subst$("Plan &1..&2", Start, {End}))) +End + +Private $aSubtestEvents As New String[] + +'' Test the order of events from the parser. +Public Sub Subtests() + Dim sTAP As String = "" +"# This tests subtest parsing and order of events\n" +"This line is ignored.\n" +"Test Subtest\n" +" ok 1 - good\n" +" ok 2 - better\n" +" not ok 3 - acceptable # TODO this was expected\n" +" 1..3\n" +"ok 1 - Subtest\n" +"\n" +"Test Another\n" +" 1..1\n" +" not ok 1 - failed\n" +"not ok 2 - Another\n" + Dim hTAP As Stream + Dim hParser As New TapParser As "SubtestsParser" + + hTAP = Open String sTAP For Read + hParser.Parse(hTAP) + + Test.Plan(15 + 1) + Assert.Equals($aSubtestEvents.Count, 15, "Event count is correct") + Assert.Equals($aSubtestEvents[0], "Diag") + Assert.Equals($aSubtestEvents[1], "Else") + Assert.Equals($aSubtestEvents[2], "Begin") + Assert.Equals($aSubtestEvents[3], "Ok") + Assert.Equals($aSubtestEvents[4], "Ok") + Assert.Equals($aSubtestEvents[5], "NotOk") + Assert.Equals($aSubtestEvents[6], "Plan") + Assert.Equals($aSubtestEvents[7], "End") + Assert.Equals($aSubtestEvents[8], "Ok") + Assert.Equals($aSubtestEvents[9], "Else") + Assert.Equals($aSubtestEvents[10], "Begin") + Assert.Equals($aSubtestEvents[11], "Plan") + Assert.Equals($aSubtestEvents[12], "NotOk") + Assert.Equals($aSubtestEvents[13], "End") + Assert.Equals($aSubtestEvents[14], "NotOk") + + Close #hTAP +End + +Public Sub SubtestsParser_Plan((Start) As Integer, (End) As Integer, (SkipAll) As Boolean) + $aSubtestEvents.Push("Plan") +End + +Public Sub SubtestsParser_Assert((Ok) As Boolean, (Id) As Long, (Description) As String, (Directive) As Integer, (Comment) As String) + $aSubtestEvents.Push(IIf(Ok, "Ok", "NotOk")) +End + +Public Sub SubtestsParser_Diagnostic((Comment) As String) + $aSubtestEvents.Push("Diag") +End + +Public Sub SubtestsParser_Else((Line) As String) + $aSubtestEvents.Push("Else") +End + +Public Sub SubtestsParser_BeginSubtest((Description) As String) + $aSubtestEvents.Push("Begin") +End + +Public Sub SubtestsParser_EndSubtest() + $aSubtestEvents.Push("End") +End + +'' Examine the object hierarchy returned by TestRunner, by running the Subtests test externally. +Public Sub Runner() + + Dim hStats As TestStats = TestRunner.Run(Application.Path, "TParser.Subtests") + + Test.Plan(7) + + Assert.NotNull(hStats, "Stats object is not null") + Assert.Equals(hStats.Planned, 1) + Assert.Equals(hStats.Passed, 1) + + Dim hMethod As TestAssertion = hStats.Subtests[0] + Assert.Ok(hMethod.Success) + Assert.Equals(hMethod.Subtests.Count, 16) + Assert.Equals(Right(hMethod.Subtests[0].Description, Len("Event count is correct")), "Event count is correct") + Assert.Equals(hMethod.Subtests[0].Subtests.Count, 0) + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TSetup.test b/main/lib/test/gb.test/.src/TestMyself/TSetup.test new file mode 100644 index 00000000..06667928 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TSetup.test @@ -0,0 +1,52 @@ +' Gambas test file + +'Plan is one more because Test in _TeardownEach is counted twice! + +Private $Done As New String[] + + +Public Sub _SeTuP() + + $Done.Add("Setup") + +End + +Public Sub _SeTuPeAcH() + + $Done.Add("SetupEach") + +End + +Public Sub _TeArDoWneAcH() + + $Done.Add("TeardownEach") + +End + +Public Sub A() + + $Done.Add("A") + Test.Plan(1) + + Assert.Equals($Done.Join(), "Setup,SetupEach,A") + +End + +Public Sub B() + + Test.Plan(1) + $Done.Add("B") + + Assert.Equals($Done.Join(), "Setup,SetupEach,A,TeardownEach,SetupEach,B") + +End + +Public Sub _TeArDoWn() + + $Done.Add("Teardown") + If $Done.Join() <> "Setup,SetupEach,A,TeardownEach,SetupEach,B,TeardownEach,Teardown" Then + 'Assertion forbidden inside _Setup or _Teardown, so we can just bail out to report the failure + Test.BailOut("_Teardown failed. Expected '" & "Setup,SetupEach,A,TeardownEach,SetupEach,B,TeardownEach,Teardown'" & " but got '" & $Done.Join() & "'") + Endif + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TSkipAll.test b/main/lib/test/gb.test/.src/TestMyself/TSkipAll.test new file mode 100644 index 00000000..758a619a --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TSkipAll.test @@ -0,0 +1,7 @@ +' Gambas test file + +Public Sub SkipAll() + + Test.SkipAll() + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TSummary.test b/main/lib/test/gb.test/.src/TestMyself/TSummary.test new file mode 100644 index 00000000..919e17d0 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TSummary.test @@ -0,0 +1,62 @@ +' Gambas test file + +''' This test can be executed to test a summary. Will fail. + +'' Will succeed +Public Sub DoSomeAsserts() + + Assert.Pass("Bla 1") + Assert.Pass("Bla 2") + Assert.Pass("Bla 3") + +End + +'' Will succeed +Public Sub DoSkip() + + Test.Skip("I skip this test to test Test.Skip.") + +End + +'' Will succeed +Public Sub DoTodo() + + Test.Todo("This fails but is todo.") + Assert.Ok(False, "Must be ok.") + + Test.Todo("This todo seems not to be removed yet, it is a bonus.") + Assert.Ok(True, "Must be ok.") + +End + +Public Sub TodoErrorCode() + + Dim r As Float + Test.Plan(2) + + Try r = 2 / 1 + Test.Todo("See automatic diagnostics 1/2") + Assert.ErrorCode(26, "Division by zero?") + + Try r = 2 / 0 + Test.Todo("See automatic diagnostics 2/2") + Assert.ErrorCode(25, "Division by zero?") + +End + + +'' Will fail +Public Sub DoOneFailure() + + Test.IntendedFailure() + Assert.Ok(False, "Intentional failure") + +End + +Public Sub DoBadPlan() + + Test._Subtest("Bad plan will appear in the summary", 2) + Assert.Ok(True) + Test._Finish() + +End diff --git a/main/lib/test/gb.test/.src/TestMyself/TWrongPlan.test b/main/lib/test/gb.test/.src/TestMyself/TWrongPlan.test new file mode 100644 index 00000000..e2e63de6 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestMyself/TWrongPlan.test @@ -0,0 +1,10 @@ +' Gambas test file + +Public Sub IHaveAWrongPlan() + + Test.Plan(1) + + Assert.Ok(True) + Assert.NotOk(False) + +End diff --git a/main/lib/test/gb.test/.src/TestRunner.module b/main/lib/test/gb.test/.src/TestRunner.module new file mode 100644 index 00000000..4bc94188 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestRunner.module @@ -0,0 +1,179 @@ +' Gambas module file + +''' Lists tests, Run tests, parse output and collect statistics. + +Export + +Private $hParser As TapParser +Private $hStats As TestStats + +' Subtest stack and the current subtest +Private $aSubtests As New TestAssertion[] +Private $hCurrent As TestAssertion +Private $bEndSubtest As Boolean + +'' Returns all tests in a project given by path +Public Sub List(Project As String) As TestCommand[] + + Dim sTests As String + + 'triggers gb.test inside a project to inspect + 'the project and return all tests inside as string + Exec ["gbx3", "-T", Test._TRIG_GETTESTS, Project] To sTests + + If sTests Begins "Bail out!" Then + Error.Raise(Subst("gb.test crashed with this error: &1", sTests)) + Endif + + Return TestCommand.FromString(sTests) + +End + +'' Runs Tests in a project given by path + +Public Sub Run(Project As String, Optional Tests As String) As TestStats + + Dim hProc As Process + + $hParser = New TapParser As "Parser" + With $hStats = New TestStats + .Name = Subst$("&1 - &2", Project, Tests) + .Plan = [1, 0] + .Started = Now() + End With + + ' Run the tests. Meanwhile the parser emits events which we handle to fill $hStats. + hProc = Exec ["gbx3", "-T", Tests, Project] For Input As "TapStream" + hProc.Wait() + + With $hStats + .Ended = Now() + .ExitCode = hProc.Value + .Run = .Passed + .Failed + .Todo + .Skipped + .Delta = .Planned - .Run + .Success = .ExitCode = 0 And (.SkippedAll Or (.Planned > 0)) And .Run = .Planned And .Failed = 0 + End With + Return $hStats + +End + +' -------------------- From TapStream to Parser -------------------- + +Private Sub AddLine(sLine As String) + + sLine = RTrim$(sLine) ' remove any trailing \r + $hStats.Lines.Add(sLine) + $hParser.ParseLine(sLine) + +End + +Public Sub TapStream_Read() + + AddLine(Last.ReadLine()) + +End + +Public Sub TapStream_Error(Message As String) + + AddLine(Message) + +End + +' -------------------- From Parser to $hStats -------------------- + +Public Sub Parser_Version(Version As Integer) + + $hStats.Version = Version + +End + +Public Sub Parser_BeginSubtest((Description) As String) + + $bEndSubtest = False + If $hCurrent Then $aSubtests.Push($hCurrent) + $hCurrent = New TestAssertion + ' Ignore the Description as we get it from its corresponding Assert event + +End + +Public Sub Parser_EndSubtest() + + $bEndSubtest = True + +End + +Public Sub Parser_Plan(Start As Integer, {End} As Integer, SkipAll As Boolean) + + If $hCurrent Then + $hCurrent.SubPlanned = 1 + {End} - Start + $hCurrent.SubSkippedAll = SkipAll + Else + $hStats.Plan = [Start, {End}] + $hStats.Planned = 1 + {End} - Start + $hStats.SkippedAll = SkipAll + Endif + +End + +' FIXME: It is an error if the test IDs are not sequential, according to prove. + +Public Sub Parser_Assert(Ok As Boolean, Id As Long, Description As String, Directive As Integer, Comment As String) + + Dim hTest As TestAssertion + + ' If this assertion summarizes the current subtest, pop it off the stack. + If $bEndSubtest Then + hTest = $hCurrent + If $aSubtests.Count Then + $hCurrent = $aSubtests.Pop() + Else + $hCurrent = Null + Endif + Else + hTest = New TestAssertion + Endif + $bEndSubtest = False + + With hTest + .Ok = Ok + .Id = Id + .Description = Description + .Directive = Directive + .Comment = Comment + End With + + ' If this is a subtest inside a test method, record it and exit early. + If $hCurrent Then + $hCurrent.Subtests.Push(hTest) + Return + Endif + + ' Otherwise this is a test method and we do special accounting. + $hStats.Subtests.Push(hTest) + With $hStats + Select Case Directive + Case TestAssertion.NONE + If hTest.Success Then + Inc .Passed + Else + Inc .Failed + Endif + + Case TestAssertion.TODO + Inc .Todo + If hTest.Success Then Inc .Bonus + + Case TestAssertion.SKIP + Inc .Skipped + + End Select + End With + +End + +Public Sub Parser_BailOut(Comment As String) + + $hStats.BailedOut = True + $hStats.BailMessage = Comment + +End diff --git a/main/lib/test/gb.test/.src/TestStats.class b/main/lib/test/gb.test/.src/TestStats.class new file mode 100644 index 00000000..f410f8cc --- /dev/null +++ b/main/lib/test/gb.test/.src/TestStats.class @@ -0,0 +1,47 @@ +' Gambas class file + +Export + +'' Name of the test +Public Name As String +'' Exit status of the test process +Public ExitCode As Integer +'' Whether the test was successful (all tests passed and at least one test was executed) +Public Success As Boolean +'' Number of run tests +Public Run As Integer +'' Difference between planned and run tests +Public Delta As Integer +'' Whether the test bailed out (aborted gracefully) +Public BailedOut As Boolean +'' If BailedOut, this contains the optional attached message +Public BailMessage As String +'' When the test started +Public Started As Date +'' When the test ended +Public Ended As Date + +'' TAP version in use +Public Version As Integer +'' Number of tests planned +Public Planned As Integer +'' Test range +Public Plan As Integer[] +'' Whether the entire test was skipped +Public SkippedAll As Boolean +'' Number of successful tests (not accounting for Todo or Skipped ones) +Public Passed As Integer +'' Number of failed tests (not accounting for Todo or Skipped ones) +Public Failed As Integer +'' Number of tested marked as to-do +Public Todo As Integer +'' Number of skipped tests +Public Skipped As Integer +'' Number of Todo tests which passed +Public Bonus As Integer + +'' Object representation of every assertion +Public Subtests As New TestAssertion[] + +'' A copy of the TAP stream +Public Lines As New String[] diff --git a/main/lib/test/gb.test/.src/TestSuite/Assert.module b/main/lib/test/gb.test/.src/TestSuite/Assert.module new file mode 100644 index 00000000..1151e29a --- /dev/null +++ b/main/lib/test/gb.test/.src/TestSuite/Assert.module @@ -0,0 +1,335 @@ +' Gambas module file + +''' This module extends the Assert instruction, which checks +''' that Expression is TRUE, and if the Expression is FALSE, +''' an uncatchable "Assertion failed" error is raised, the +''' PRINT or ERROR instruction is executed, and the program stops immediately. + +Export + +Public _IntendedFailure As Boolean + +Private $aIgnore As String[] = ["Assert", "Test", "TestCase", "TestSuite", "TapPrinter"] + +'' Assert that Result = True +Public Sub Ok(Result As Boolean, Optional Description As String) As Boolean + + Dim bRes As Boolean + Dim bt As String[] + Dim sStack As String + Dim iPos As Integer + + If Test._InSetup = True Then + Test.BailOut(("Failure: Assertion forbidden inside _Setup or _Teardown.")) + Endif + + If _IntendedFailure Then Result = Not Result + _IntendedFailure = False + + bt = System.Backtrace + + ' While bt[0] Begins "Assert." Or bt[0] Begins "Test." Or bt[0] Begins "TestCase." Or bt[0] Begins "TestSuite." Or bt[0] Begins "TapPrinter." + ' bt.Remove(0) + ' If bt.Count = 0 Then + ' Goto DAWAI + ' Endif + ' Wend + + While bt.Count + sStack = bt[0] + If sStack Like "[[]*].*" Then sStack = Mid$(sStack, InStr(sStack, "]") + 2) + iPos = InStr(sStack, ".") + If iPos Then sStack = Left(sStack, iPos - 1) + If Not $aIgnore.Exist(sStack) Then Break + bt.Remove(0) + Wend + +DAWAI: + If bt.Count > 0 Then + Test._Next.Line = bt[0] + Endif + + Test._Next.Ok = Result + Test._Next.Description = Description + bRes = Test._Printer.Assert(Test._Next).Ok + Test._Next = New TestAssertion + Return bRes + +End + +' -------------------- High-level test functions -------------------- +'' Assert that a test is passed. Reports Ok +Public Sub Pass(Optional Description As String) As Boolean + + Return Ok(True, Description) + +End + +'' Assert fail. Reports not ok. +Public Sub Fail(Optional Description As String) As Boolean + + Return Ok(False, Description) + +End + +'' Assert that Result = False. +Public Sub NotOk(Result As Boolean, Optional Description As String) As Boolean + + Return Ok(Not Result, Description) + +End + +'' Assert that Got = Expected +Public Sub Equals(Got As Variant, Expected As Variant, Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Got = Expected, Description) + If Not bRes Then + NoteGotAndExpected(Got, Expected) + Endif + Return bRes + +End + +'' Assert that Got <> Expected +Public Sub Notequals(Got As Variant, UnExpected As Variant, Optional Description As String) As Boolean + + Return Ok(Got <> UnExpected, Description) + +End + +'' Assert that Got <= Bound +Public Sub LessEqual(Got As Variant, Bound As Variant, Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Got <= Bound, Description) + If Not bRes Then + NoteGotAndExpected(Got, Bound) + Endif + Return bRes + +End + +'' Assert that Got < Bound +Public Sub Less(Got As Variant, Bound As Variant, Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Got < Bound, Description) + If Not bRes Then + NoteGotAndExpected(Got, Bound) + Endif + Return bRes + +End + +'' Assert that Got >= Bound +Public Sub GreaterEqual(Got As Variant, Bound As Variant, Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Got >= Bound, Description) + If Not bRes Then + NoteGotAndExpected(Got, Bound) + Endif + Return bRes + +End + +'' Assert that Got > Bound +Public Sub Greater(Got As Variant, Bound As Variant, Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Got > Bound, Description) + If Not bRes Then + NoteGotAndExpected(Got, Bound) + Endif + Return bRes + +End + +'' Asserts that _Got_ has an [absolute error](https://en.wikipedia.org/wiki/Approximation_error) to _Expected_ of at most _Precision_. +Public Sub Approximate(Got As Float, Expected As Float, Precision As Float, Optional Description As String) As Boolean + + Dim bRes As Boolean + Dim fAbsError As Float + + fAbsError = Abs(Got - Expected) + bRes = Ok(fAbsError <= Precision, Description) + If Not bRes Then + Test.Note(Null) + Test.Note(Subst$(("------------- Expected -------------" & gb.lf & "AbsError(&1, &2) <= &3"), Got, Expected, Precision)) + Test.Note(Null) + Test.Note(Subst$(("---------------- Got ---------------" & gb.lf & "Got = &1" & gb.lf & "Expected = &2" & gb.lf & "|Got - Expected| = &3"), Got, Expected, fAbsError)) + Test.Note("------------------------------------") + Test.Note(Null) + Endif + Return bRes + +End + +'' Asserts that _Got_ has a [relative error](https://en.wikipedia.org/wiki/Approximation_error) to _Expected_ of at most _RelPrecision_. +'' _Expected_ may not be zero, unless _Got_ is also zero in which case the test succeeds. +Public Sub RelativeApproximate(Got As Float, Expected As Float, RelPrecision As Float, Optional Description As String) As Boolean + + Dim bRes As Boolean + Dim fRelError As Float + + If Abs(Expected) = 0.0 Then + bRes = Ok(Abs(Got) = 0.0, Description) + Test.Note(("Expected value in RelativeApproximate is 0.0. Comparing value with 0.0 exactly...")) + Return bRes + Endif + + fRelError = Abs((Got - Expected) / Expected) + bRes = Ok(fRelError <= RelPrecision) + If Not bRes Then + Test.Note(Null) + Test.Note(Subst$(("------------- Expected -------------" & gb.lf & "RelError(&1, &2) <= &3"), Got, Expected, RelPrecision)) + Test.Note(Null) + Test.Note(Subst$(("---------------- Got ---------------" & gb.lf & "Got = &1" & gb.lf & "Expected = &2" & gb.lf & "|Got - Expected|/|Expected| = &3"), Got, Expected, fRelError)) + Test.Note("------------------------------------") + Test.Note(Null) + Endif + Return bRes + +End + +'' Assert that Got is of the type Type +Public Sub IsType(Got As Variant, Type As Integer, Optional Description As String) As Boolean + + Return Equals(TypeOf(Got), Type, Description) + +End + +'' Assert that Got = Null +Public Sub Null(Got As Variant, Optional Description As String) As Boolean + + Return Equals(Got, Null, Description) + +End + +'' Assert that Got <> Null +Public Sub NotNull(Got As Variant, Optional Description As String) As Boolean + + Return Notequals(Got, Null, Description) + +End + +'' Assert that Got Like Pattern. See also the Like string operator. +Public Sub Like(Got As String, Pattern As String, Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Got Like Pattern, Description) + If Not bRes Then + NoteGotAndExpected(Got, Pattern) + Endif + Return bRes + +End + +'' Assert that Got Match Pattern. See also the Match string operator. +Public Sub Match(Got As String, Pattern As String, Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Got Match Pattern, Description) + If Not bRes Then + NoteGotAndExpected(Got, Pattern) + Endif + Return bRes + +End + +'' Assert that Got = Expected. On failure reports hints about the difference. +Public Sub StringEquals(Got As String, Expected As String, Optional Description As String) As Boolean + + Dim bRes As Boolean + Dim iPos As Integer + + bRes = Equals(Got, Expected, Description) + If Not bRes Then + If Len(Got) <> Len(Expected) Then + Test.Note(Subst$(("Strings are of different lengths &1 and &2, respectively."), Len(Got), Len(Expected))) + Endif + For iPos = 1 To Min(Len(Got), Len(Expected)) + If Mid$(Got, iPos, 1) <> Mid$(Expected, iPos, 1) Then Break + Next + Test.Note(Subst$(("Strings differ at position &1."), iPos)) + Endif + Return bRes + +End + +'' Assert that an error happened. Reports not ok if no error happened. +'' +'' Example: +'' Try 2/0 +'' Assert.Error("division by zero") +Public Sub Error(Optional Description As String) As Boolean + + Return Ok(Error, Description) + +End + +'' Assert that an error happened with error code. Reports not ok +'' if no error happend or if the error code is wrong. +'' +'' Example: +'' Try Print 2 / 0 +'' Assert.ErrorCode(26, "division by zero with code 26") +Public Sub ErrorCode(Code As Integer, Optional Description As String) As Boolean + + Dim bRes As Boolean + + If Not Error Then + bRes = Fail(Description) + Test.Note(("No error happened")) + Else + bRes = Equals(Error.Code, Code, Description) + If Not bRes Then + Test.Note(Subst$(("Error was: &1 (code: &2) at &3"), Error.Text, Error.Code, Error.Where)) + Endif + Endif + + Error.Clear() + Return bRes + +End + +'' Assert that no error happened. +'' +''Example: +'' +''Try Print 2 / 2 +''Assert.Noterror("Division ok") +Public Sub Noterror(Optional Description As String) As Boolean + + Dim bRes As Boolean + + bRes = Ok(Not Error, Description) + If Not bRes Then + Test.Note(Subst$(("Error was: &1 (code: &2) at &3"), Error.Text, Error.Code, Error.Where)) + Endif + + Error.Clear() + Return bRes + +End + +' ------------------------------------------------- Helper functions + +Private Sub NoteGotAndExpected(Got As String, Expected As String) + + Test.Note(Null) + Test.Note(Subst$(("------------- Expected -------------" & gb.lf & "&1"), Expected)) + Test.Note(Null) + Test.Note(Subst$(("---------------- Got ---------------" & gb.lf & "&1"), Got)) + Test.Note("------------------------------------") + Test.Note(Null) + +End diff --git a/main/lib/test/gb.test/.src/TestSuite/Test.module b/main/lib/test/gb.test/.src/TestSuite/Test.module new file mode 100644 index 00000000..3e7219b8 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestSuite/Test.module @@ -0,0 +1,494 @@ +' Gambas module file + +Export + +Class __Test + +''' The class Test is the central class which orchestrates +''' the execution of tests and also gives a couple of tools to +''' manipulate running tests. + +'' Special value for unspecified test plan +Public Const NO_PLAN As Integer = -1 + +Property Read _Printer As TapPrinter +Property Read _Finished As Boolean +Property _Next As TestAssertion +Public _InSetup As Boolean + +Private $hPrinter As New TapPrinter +Private $hNext As TestAssertion + +Private $Suite As TestSuite + +'' Used to trigger Test.Main to print all tests as comma separated string. +Public Const _TRIGGER_GET_ALLTESTS As String = "#GetAllTests" + +'' The static procedure Test.Main() runs tests. By default it runs all tests (aka testmethods) in all +'' testmodules of a project ordered by name and prints the result to stdout. +'' +'' A testmodule is a special class with the ending test. It must contain at minimum one test. +'' +'' A test is a public sub in a testmodule whose name does not contain an underscore. +'' +'' On the command line the interpreter is commanded with this line to execute Test.Main(): +'' +'' gbx3 -T "*" /path/to/project +'' +'' The optional argument ca be used to create a testsuite which chooses any +'' combination of testmodule and test(s). The format of the string is like so: Testmodules +'' are separated by a comma. If not all tests of a testmodule should be called then the +'' name of the testmodule is followed by a dot and a string containing the tests separated +'' by a semikolon. For example: +'' +'' gbx3 -T "testmodule1,testmodule2.test1;test2,testmodule3" /path/to/project +'' +'' In the Gambas IDE one is able to define testsuites and store them with a individual name +'' in a file named ".test" in the project's path. +'' +'' A test suite stored like that can be called by it's name with a preceeding "@", for example: +'' +'' gbx3 -T "@My production testsuite1" /path/to/project +'' +'' Public methods called "_Setup", "_Teardown", "_SetupEach" or "_TeardownEach" inside a testmodule +'' can be used to create a testfixture, which defines a special environment for the tests. + +Public Sub Main(Optional Tests As String) + + Dim sTestsuite As String + + If Not Tests Then + 'prints the names of all testmodules + PrintAllTestModules() + Return + Endif + + If Tests = _TRIGGER_GET_ALLTESTS Then + Print AllTests() + Return + Endif + + If Tests = "*" Then + 'triggers all tests + Tests = "" + Endif + + If Tests Begins "@" Then + 'a test suite was called by name + sTestsuite = Tests + Tests = Helper.GetTestSuiteByName(Tests) + Endif + + ' run tests + Test._Reset() ' only if you run this Main multiple times per process, which you shouldn't + If Tests Then + FromString(Tests) + Endif + RunTests() + + If sTestsuite Then + sTestsuite = Replace(sTestsuite, "@", "Testsuite: ") + $hPrinter.Session.Summary.Description = sTestsuite + Else + $hPrinter.Session.Summary.Description = TestCommand.ToString(TestCommand.FromString(Tests)) + Endif + + PrintSummary() + Quit ExitCode() + +TheEnd: +Catch + Test.BailOut(Error.Text) + +End + +Private Sub PrintSummary() + + With $hPrinter.Session + Test._Print(Null) ' better readability for humans + + ' even if the tests came in unsorted, print it sorted + Test.Note(Subst$(("Ran '&1' "), TestCommand.ToString(TestCommand.FromString(.Summary.Description)))) + If .TestsRun <> .Plan Then Test.Note(Subst$(("Planned &1 tests but ran &2"), .Plan, .TestsRun)) + Test.Note(gb.Lf) + + ShowTestCollection(("&1 skipped:"), FindSkips(.Summary.Subtests, "")) + ShowTestCollection(("&1 todo:"), FindTodos(.Summary.Subtests, "")) + ShowTestCollection(("&1 bonus:"), FindBonus(.Summary.Subtests, "")) + + If Not .Summary.Success Then + ShowTestCollection(("&1 test plans failed:"), FindBadPlans(.Summary.Subtests, "")) + ShowTestCollection(("&1 tests failed:"), FindFailures(.Summary.Subtests, "")) + Endif + Test.Note(IIf(.Summary.Success, ("PASSED"), ("FAILED"))) + End With + +End + +Private Function ExitCode() As Integer + + Return IIf($hPrinter.Session.Summary.Success, 0, 1) + +End + +'' Prints a Collection[] of tests as returned by FindFailures, FindSkips, FindTodos. +'' _Description_ can contain '&1' which is substituted for _TestCollection_.Count. +Private Sub ShowTestCollection(Description As String, TestCollection As Collection[]) + + Dim cTest As Collection + + If TestCollection.Count Then + Test.Note(Subst$(Description, TestCollection.Count)) + For Each cTest In TestCollection + Dim hTest As TestAssertion = cTest!Assertion + Dim sName, sNote As String + sName = hTest.Description + If cTest["Note"] Then + sNote = Subst$(": &1", cTest["Note"]) + Else If hTest.Comment Then + sNote = Subst$("# &1", hTest.Comment) + Endif + + If hTest.Line Then + Test.Note(Subst$("&2: &1 -- &3&4", hTest.Line, hTest.Id, sName, IIf(sNote, " " & sNote, ""))) + Else + Test.Note(Subst$("&2: &1 -- &3&4", cTest["Path"], hTest.Id, sName, IIf(sNote, " " & sNote, ""))) + Endif + + Next + Test.Note(gb.Lf) + Endif + +End + +Private Function FindBadPlans(Tests As TestAssertion[], Prefix As String) As Collection[] + + Dim hTest As TestAssertion + Dim sName As String + Dim aRet As New Collection[] + + For Each hTest In Tests + sName = Prefix &/ hTest.Description + If hTest.Subtests.Count <> hTest.SubPlanned Then aRet.Add(["Path": Prefix, "Assertion": hTest, "Note": Subst$(("Planned &1 but ran &2"), hTest.SubPlanned, hTest.Subtests.Count)]) + aRet.Insert(FindBadPlans(hTest.Subtests, sName)) + Next + Return aRet + +End + +Private Function FindFailures(Tests As TestAssertion[], Prefix As String) As Collection[] + + Dim hTest As TestAssertion + Dim sName As String + Dim aRet As New Collection[] + + For Each hTest In Tests + sName = Prefix &/ hTest.Description + ' Only show the deepest subtests that caused failures. + If Not hTest.Success And If Not hTest.Subtests.Count Then aRet.Add(["Path": Prefix, "Assertion": hTest]) + aRet.Insert(FindFailures(hTest.Subtests, sName)) + Next + Return aRet + +End + +Private Function FindSkips(Tests As TestAssertion[], Prefix As String) As Collection[] + + Dim hTest As TestAssertion + Dim sName As String + Dim aRet As New Collection[] + + For Each hTest In Tests + sName = Prefix &/ hTest.Description + If hTest.Directive = TestAssertion.SKIP Then aRet.Add(["Path": Prefix, "Assertion": hTest]) + aRet.Insert(FindSkips(hTest.Subtests, sName)) + Next + Return aRet + +End + +Private Function FindTodos(Tests As TestAssertion[], Prefix As String) As Collection[] + + Dim hTest As TestAssertion + Dim sName As String + Dim aRet As New Collection[] + + For Each hTest In Tests + sName = Prefix &/ hTest.Description + If hTest.Directive = TestAssertion.TODO And If Not hTest.Ok Then aRet.Add(["Path": Prefix, "Assertion": hTest]) + aRet.Insert(FindTodos(hTest.Subtests, sName)) + Next + Return aRet + +End + +Private Function FindBonus(Tests As TestAssertion[], Prefix As String) As Collection[] + + Dim hTest As TestAssertion + Dim sName As String + Dim aRet As New Collection[] + + For Each hTest In Tests + sName = Prefix &/ hTest.Description + If hTest.Directive = TestAssertion.TODO And If hTest.Ok Then aRet.Add(["Path": Prefix, "Assertion": hTest]) + aRet.Insert(FindBonus(hTest.Subtests, sName)) + Next + Return aRet + +End + +'' Run all tests in $Suite. If $Suite is Null, run all tests. + +Private Function RunTests() + + Dim Testmodule As Class + + If Not $Suite Then + ' create as Suite with all tests + $Suite = New TestSuite + For Each TestModule In GetAllTestModules() + $Suite.AddAllTestCases(TestModule) + Next + Endif + + $Suite.Run() + If Not Test._Finished Then Test._Finish() + +End + +Private Function GetAllTestModules() As Class[] + + Dim TestClass As Class + Dim TestModules As New Class[] + Dim aNames As New String[] + Dim sName As String + + Component.Load("gb.util") + + For Each sName In Dir(".../.gambas") + sName = Helper.CheckTestModule(sName) + If sName Then aNames.Add(sName) + Next + + Assert aNames + + aNames.Sort + + For Each sName In aNames + TestClass = __Test.Load(sName) + If Not TestClass Then Error.Raise(Subst$(("Could not load test module '&1'"), sName)) + TestModules.Add(TestClass) + Next + + Return TestModules + +Catch + Test.BailOut("Error in " & Error.Where & ": " & Error.Text) + +End + +'' Returns an collection of all the testmodules and their tests of the project. +'' Key is the name of the testmodule, Value is a string array with the names of the tests it contains + +Public Function AllTestsCollection() As Collection + + Dim aoTestModules As Class[] + Dim cAlltests As New Collection + Dim oTestModule As Class + + aoTestModules = GetAllTestModules() + + For Each oTestModule In aoTestModules + cAlltests.Add(TestSuite.GetTestsFromTestModule(oTestModule), oTestModule.Name) + Next + + Return cAlltests + +End + +'' Returns a comma separated string with all tests of the project in the same way Test.Main() wants it. + +Public Function AllTests() As String + + Dim tests As Collection + Dim asTests As String[] + Dim asLines As New String[] + Dim sModule As String + + tests = AllTestsCollection() + + For Each tests + sModule = tests.Key + asTests = tests[tests.Key] + asTests.Sort + If asTests.Count > 0 Then + asLines.Add(sModule & "." & asTests.Join(";")) + Endif + Next + + asLines.Sort() + Return asLines.Join(",") + +End + +' ------------------------------------------------- Test controls + +'' Prints "Bail out!" to Stdout and stops all testing immediately. +Public Sub BailOut(Optional Comment As String) + + $hPrinter.BailOut(Comment) + Error.Raise("Bailout!") + +End + +'' Synonym for Note, prints Comment with leading # +Public Sub Diagnostic(Comment As String) + + $hPrinter.Diagnostic(Comment) + +End + +'' If the next assertion is a failure it reports ok. +'' In other words: Reverses the result of the following assertion. +'' "not ok" will be "ok" and vice versa. +Public Sub IntendedFailure() + + Assert._IntendedFailure = True + +End + +'' Synonym for Diagnostic, prints Comment with leading # +Public Sub Note(Comment As String) + + $hPrinter.Note(Comment) + +End + +'' Tell the testing system that the next assertion is a TODO. +'' Even if it fails it reports ok, but will be reported as Todo. +'' If the next assertion does not fail, it will be reported as bonus. +Public Sub Todo(Optional Comment As String) + + Test._Next.Directive = TestAssertion.TODO + Test._Next.Comment = Comment + +End + +'' Tell the testing system that a test is skipped. Reports ok. +Public Sub Skip(Optional Comment As String) + + Test._Next.Directive = TestAssertion.SKIP + Test._Next.Comment = Comment + Assert.Pass() + +End + +'' Prints Line to Stdout +Public Sub _Print({Line} As String) + + $hPrinter.Print({Line}) + +End + +Private Function _Printer_Read() As TapPrinter + + Return $hPrinter + +End + +Private Function _Finished_Read() As Boolean + + Return $hPrinter.Session.Finished + +End + +Public Sub _Reset() + + $hPrinter = New TapPrinter As "Printer" + +End + +Private Function _Next_Read() As TestAssertion + + If $hNext = Null Then + $hNext = New TestAssertion + Endif + Return $hNext + +End + +Private Sub _Next_Write(Value As TestAssertion) + + $hNext = Value + +End + +Public Sub _Subtest(Description As String, Optional Tests As Integer, Optional Comment As String) + + Test._Printer.Subtest(Description, Tests, Comment) + +End + +'' Plan the number of assertions within a test method. +'' If the number is not correct, the test fails. +Public Sub Plan(Tests As Integer, Optional Comment As String) + + Test._Printer.Plan(Tests, Comment) + +End + +'' Skip the current test. + +Public Sub SkipAll(Optional Comment As String) + + Test._Printer.SkipAll(Comment) + +End + +Public Sub _Finish() + + Test._Printer.Finish() + +End + +Private Sub PrintAllTestModules() + + Dim sName As String + Dim aTest As New String[] + + Component.Load("gb.util") + + For Each sName In Dir(".../.gambas") + sName = Helper.CheckTestModule(sName) + If sName Then aTest.Add(sName) + Next + + Print aTest.Join(); + +End + +'Fill suite from String +Private Sub FromString(Tests As String) + + Dim Commands As TestCommand[] + Dim Command As TestCommand + Dim TestModule As Class + Dim sName As String + + Commands = TestCommand.FromString(Tests) + + $Suite = New TestSuite + + For Each Command In Commands + With Command + TestModule = __Test.Load(.ModuleName) + If Command.Methods.Count = 0 Then + Command.Methods = $Suite.GetTestsFromTestModule(TestModule) + Endif + For Each sName In Command.Methods + $Suite.AddTestCase(sName, Testmodule) + Next + End With + Next + +End diff --git a/main/lib/test/gb.test/.src/TestSuite/TestCase.class b/main/lib/test/gb.test/.src/TestSuite/TestCase.class new file mode 100644 index 00000000..192f0b0f --- /dev/null +++ b/main/lib/test/gb.test/.src/TestSuite/TestCase.class @@ -0,0 +1,79 @@ +' Gambas class file + +''' The TestCase class is responsible for executing a specific test in a testmodule. +''' The test case to be executed is specified through the Name and TestModule +''' properties. The Run method will call the appropriate _Setup +''' and _Teardown methods for the test case as well as executing the test case +''' method itself. + +'' Name of the test case +Property Read Name As String + +'' Reference to the TestModule containing the test methods to be executed. +Property Read TestModule As Class + +Private $Name As String +Private $MyTestModule As Class + +'' Initializes the TestCase. Used in lieu of a constructor. +Public Sub _new(sName As String, TestModule As Class) + + Assert sName <> Null + $Name = sName + Assert TestModule + $MyTestModule = TestModule + +End Sub + +'' Name of the test case +Function Name_Read() As String + + Assert $Name <> Null + Return $Name + +End + +'' Create the fixture, run the test and collect the results in Track +Public Sub Run() + + Assert $MyTestModule + + '$MyTestModule._SetupEach + If $MyTestModule.Symbols.Exist("_SetupEach", gb.IgnoreCase) Then + Test._InSetup = True + Object.Call($MyTestModule, "_SetupEach") + Test._InSetup = False + Endif + + Try Object.Call($MyTestModule, Me.name) + If Error Then + Assert.Fail(Subst$("&1.&2 crashed with error '&3' in '&4'", $MyTestModule.Name, Me.Name, Error.Text, Error.Where)) + Endif + + If $MyTestModule.Symbols.Exist("_TeardownEach", gb.IgnoreCase) Then + Test._InSetup = True + Object.Call($MyTestModule, "_TeardownEach") + Test._InSetup = False + Endif + +Catch + + Test.BailOut("Test stopped with error \"" & Error.Text & "\" caused by " & $MyTestModule.Name & "." & Me.Name & ".") + +End Sub + +Private Function TestModule_Read() As Class + + Assert $MyTestModule + Return $MyTestModule + +End + +Public Function _compare(TC As Testcase) As Integer + + If Me.TestModule.Name = TC.TestModule.Name Then + Return Comp(Me.Name, TC.Name) + Endif + Return Comp(Me.TestModule.Name, TC.TestModule.Name) + +End diff --git a/main/lib/test/gb.test/.src/TestSuite/TestCommand.class b/main/lib/test/gb.test/.src/TestSuite/TestCommand.class new file mode 100644 index 00000000..adfda8c7 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestSuite/TestCommand.class @@ -0,0 +1,127 @@ +' Gambas class file + +''' The class TestCommand stores the names of a testmodule and optional tests to be executed. +''' It also contains methods to translate testcommands to string and vice versa. + +Export +Create Static + +'' Name of the testmodule to be called +Public ModuleName As String + +'' Name of tests in a testmodule to be called. If empty, all will be called. +Public Methods As New String[] + +'' Finds the Command for the TestModule with name TestModuleName +Static Public Function _Find(Commands As TestCommand[], TestModuleName As String) As TestCommand + + Dim Command As TestCommand + + For Each Command In Commands + If Lower(Command.ModuleName) = Lower(TestModuleName) Then + Return Command + Endif + Next + +End + +'' Creates a string from an array of testcommands +Static Public Function ToString(Commands As TestCommand[]) As String + + Dim Command As TestCommand + Dim asBuf As New String[] + + Commands.Sort() + For Each Command In Commands + If Command.Methods.Count = 0 Then + asBuf.Add(Command.ModuleName) + Else + Command.Methods.Sort() + asBuf.Add(Command.ModuleName & "." & Command.Methods.Join(";")) + Endif + Next + + Return asBuf.Join(",") + +End + +'' Parses a string with comma separated tests and creates an array of TestCommands. +Static Public Function FromString(Tests As String) As TestCommand[] + + Dim asAll, asMethod As String[] + Dim sCommand, sModulename, sMethod As String + Dim Command As TestCommand + Dim Commands As New TestCommand[] + Dim i As Integer + + If Tests <> Null Then + 'Commands = New TestCommand[] + + asAll = Split(Tests, ",", Null, True) + + For i = 0 To asAll.Count - 1 + asAll[i] = Trim(asAll[i]) + Next + + asAll.Sort + + For Each sCommand In asAll + + 'just to be sure + sModulename = Null + sMethod = Null + i = 0 + + Command = New TestCommand + + With Command + If InStr(sCommand, ".") > 0 Then + sModulename = Trim(Left(sCommand, InStr(sCommand, ".") - 1)) + sMethod = Trim(Right(sCommand, Len(sCommand) - InStr(sCommand, "."))) + + If InStr(sMethod, ";") > 0 Then + asMethod = Split(sMethod, ";") + .Methods = asMethod + Else + .Methods.Add(sMethod) + Endif + .ModuleName = sModulename + Else + .ModuleName = Trim(sCommand) + Endif + End With + + 'If InStr(Command, ".") > 0 Then + If Not Commands.Exist(Command) Then + Commands.Add(Command) + Endif + + For Each Commands + If Commands[i].ModuleName = sModulename And If Commands[i].Methods.Count > 0 Then + If Not Commands[i].Methods.Exist(sMethod) And If InStr(sMethod, ";") = 0 Then + Commands[i].Methods.Add(sMethod) + Endif + Endif + Inc i + Next + Next + + For Each Command In Commands + Command.Methods = Command.Methods.Sort() + Next + + Commands = Commands.Sort() + Endif + + Return Commands + +End + +Public Function _compare(TC As TestCommand) As Integer + + Dim ret As Integer + + ret = Comp(ModuleName, TC.ModuleName, gb.Binary) + Return ret + +End diff --git a/main/lib/test/gb.test/.src/TestSuite/TestSuite.class b/main/lib/test/gb.test/.src/TestSuite/TestSuite.class new file mode 100644 index 00000000..5df41bd0 --- /dev/null +++ b/main/lib/test/gb.test/.src/TestSuite/TestSuite.class @@ -0,0 +1,137 @@ +' Gambas class file + +''' The TestSuite class represents a suite of different testcases to be run. + +Property Read Tests As TestCase[] +Private $Tests As New TestCase[] + +'' Runs all tests contained within the collection and collects the result in the Track parameter. +Public Sub Run() + + Dim oTest As TestCase + Dim CurrentTestModule, LastTestModule As Class + Dim CurrentAction As String + + Assert $Tests + + $Tests.Sort() + + Test.Plan($Tests.Count) + For Each oTest In $Tests + CurrentTestModule = oTest.TestModule + If LastTestModule Then + If LastTestModule <> CurrentTestModule Then + CurrentAction = LastTestModule.Name & ":TeardownTestModule" + StopTestModule(LastTestModule) + CurrentAction = CurrentTestModule.Name & ":SetupTestModule" + StartTestModule(CurrentTestModule) + Endif + Else + 'Test.Note("-------------------- " & CurrentTestModule.Name) + CurrentAction = CurrentTestModule.Name & ":SetupTestModule" + StartTestModule(CurrentTestModule) + Endif + + Test._Print(Null) 'better readability for humans + Test._Subtest(Subst$("&1.&2", oTest.TestModule.Name, oTest.Name)) + 'Debug oTest.Name + oTest.Run() + If Not Test._Finished Then Test._Finish() + LastTestModule = CurrentTestModule + Next + + If LastTestModule Then + CurrentAction = LastTestModule.Name & ":TeardownTestModule" + StopTestModule(LastTestModule) + Endif + +Catch + If Error.Where Begins "Test.BailOut." Then Quit 1 + Test.BailOut("Test stopped with error \"" & Error.Text & "\" caused by " & CurrentAction & " in " & Error.Where & ".") + +End Sub + +Private Sub StartTestModule(TestModule As Class) + + If TestModule.Symbols.Exist("_Setup", gb.IgnoreCase) Then + Test._InSetup = True + Object.Call(TestModule, "_Setup") + Test._InSetup = False + Endif + +End + +Private Sub StopTestModule(TestModule As Class) + + If TestModule.Symbols.Exist("_Teardown", gb.IgnoreCase) Then + Test._InSetup = True + Object.Call(TestModule, "_Teardown") + Test._InSetup = False + Endif + +End + +'' Create a new test case and add it to the suite. +Public Function AddTestCase(sName As String, TestModule As Class) + + Dim test As TestCase + + Assert sName <> Null + Assert TestModule + + test = New TestCase(sName, TestModule) + Assert $Tests + + If Not $Tests.Exist(test) Then + $Tests.Add(test) + Endif + +End + +Static Public Function GetTestsFromTestModule(TestModule As Class) As String[] + + Dim sSymbol As String + Dim NoTestSymbols As String[] = ["_Setup", "_SetupEach", "_Teardown", "_TeardownEach", "Plan"] + Dim asTests As New String[] + + Assert TestModule + + For Each sSymbol In TestModule.Symbols + If Not NoTestSymbols.Exist(sSymbol, gb.IgnoreCase) And If InStr(sSymbol, "_") = 0 Then + asTests.Add(sSymbol) + Endif + Next + + Return asTests + +End + +'' Create all test cases that are contained in the specified testmodule and add them to the suite. +Public Function AddAllTestCases(TestModule As Class) + + Dim asTests As String[] + Dim i As Integer + Dim NameProcedure As String + + Assert TestModule + + asTests = GetTestsFromTestModule(TestModule) + asTests.Sort + + 'Assert asTests.Count > 0 Error "Failed: No tests in " & TestModule.Name + If asTests.Count = 0 Then + Assert.Todo("No tests in " & TestModule.Name) + Endif + + For i = 0 To asTests.Count - 1 + NameProcedure = CStr(asTests[i]) + AddTestCase(NameProcedure, TestModule) + Next + +End + +Private Function Tests_Read() As TestCase[] + + Return $Tests + +End diff --git a/main/lib/test/gb.test/.src/ZzzDoSth.module b/main/lib/test/gb.test/.src/ZzzDoSth.module new file mode 100644 index 00000000..2ca96237 --- /dev/null +++ b/main/lib/test/gb.test/.src/ZzzDoSth.module @@ -0,0 +1,23 @@ +' Gambas module file + +' Playground for development ... + + +Public Sub Main() + + ' ------------------------------------------------- Interpreter Interface + + Dim Ass As Class + Dim name As String + Dim Symbols As String[] + + Ass = Class.Load("Assert") + + Symbols = Ass.Symbols + Symbols.Sort + + For Each Name In Symbols + Print "Public Sub " & Name & "()" & gb.lf & "End" + Next + +End diff --git a/main/lib/test/gb.test/.test b/main/lib/test/gb.test/.test new file mode 100644 index 00000000..98206ab2 --- /dev/null +++ b/main/lib/test/gb.test/.test @@ -0,0 +1,37 @@ +[TestSuites] +Count=8 +TestWith=4 +Default="TCrashes.DoACrash" + +[TestSuites/1] +Name="! Must Bail out!" +Tests="TBailout.Bailout" + +[TestSuites/2] +Name="! Must fail (a couple of crashes)" +Tests="TCrashes.DoACrash;ErrorTwo" + +[TestSuites/3] +Name="! Must fail (wrong plan)" +Tests="TWrongPlan.IHaveAWrongPlan" + +[TestSuites/4] +Name="All good" +Tests="TAllAsserts.Approximate;Equals;Error;ErrorCode;Fail;Greater;GreaterEqual;IsType;Less;LessEqual;Like;Match;NotNull;NotOk;Notequals;Noterror;Null;Ok;Pass;RelativeApproximate;StringEquals,TElse.NoMessage;NoMessageInbetween;Note,TIntendedFailures.EqualsFailure;Error;LongFailure;LongTypeMismatchFailure;StringFailure,TInternals.CommandsToString;FindTestSuiteByName;InterpreterInterface;ParseTestCommands;ReflectTest;ReflectTestsString,TParser.Runner;SkipAll;Subtests,TSetup,TSkipAll.SkipAll" + +[TestSuites/5] +Name="Keep this test suite, it is necessary for testing gb.test." +Tests="TInternals.FindTestSuiteByName" + +[TestSuites/6] +Name="Summary" +Tests="MustFail.TestNoteGotAndExpected,TAllAsserts.Approximate;Equals;Error;ErrorCode;Fail;Greater;GreaterEqual;IsType;Less;LessEqual;Like;Match;NotNull;NotOk;Notequals;Noterror;Null;Ok;Pass;RelativeApproximate;StringEquals,TSummary.DoBadPlan;DoOneFailure;DoSkip;DoSomeAsserts;DoTodo;TodoErrorCode,TWrongPlan.IHaveAWrongPlan" + +[TestSuites/7] +Name="TAP Parser" +Tests="TParser.Runner;SkipAll;Subtests" + +[TestSuites/8] +Name="! Must fail (show Got vs Expected)" +Tests="MustFail.TestNoteGotAndExpected" + diff --git a/main/lib/test/gb.test/LICENSE b/main/lib/test/gb.test/LICENSE new file mode 100644 index 00000000..d6a93266 --- /dev/null +++ b/main/lib/test/gb.test/LICENSE @@ -0,0 +1,340 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/main/lib/test/gb.test/README.md b/main/lib/test/gb.test/README.md new file mode 100644 index 00000000..5542d974 --- /dev/null +++ b/main/lib/test/gb.test/README.md @@ -0,0 +1,151 @@ +# gb.test – the Gambas Unittest + +A Gambas component for unittesting and test-driven programming. With this component you can develop software in a test-driven matter (write test first, program functionality afterwards) and you are able to ensure that on refactoring the desired results of your code stays the same. + +Tests are output as [Tap](https://testanything.org/) so that they can be displayed, analyzed or viewed with any [Tap consumer](https://testanything.org/consumers.html). As each output includes a summary at the end with the string "PASSED" or "FAILED" at the last line you can even view the console output to decide whether the test has been successful or not. + +## How it works + +There is an example in [this simple Gambas project](unittesthelloworld-0.0.8.tar.gz). + +### Testmodule + +Start by creating a Testmodule, this is a class with any name, but the ending ".test", for example "TestHelloWorld.test". This class contains one or more public Subs, from which each represent a so called Testmethod. + +---- + ' Gambas test module file + ''' Testmodule TestHelloWorld + + '' Testmethod TestHelloWorld + Public Sub TestHelloWorld() + + Assert.EqualsString("Hello World", Hello.World(), "Strings should be equal") + + End +---- + +### Module(Function) to test: + +To make it work, we need a funktion that will be tested. So we create a function "World" in a module "Hello" in our project: + +---- + + ' Gambas module file + + ''' Module is named "Hello" + + Public Function World() As String + + Return "Hello World" + + End + +---- + +### Bringing Test into play + +A simple way to execute the Unittest is to create another module, name it "TestMe" or something more interesting and make it a Gambas startclass: + +---- + + 'Module TestMe + + 'Is a startclass + 'Starts the Unittest, when F5 was hit + + Public Sub Main() + + Test.Main() + + End + +---- + +If you did all this correctly and now hit <F5>, Gambas will execute the startfunction in module TestMe, which runs through the Testmethods of our Testmodule and presents the test result in the console, for example (this is from unittesthelloworld): + + 1..3 + + Testmethod TestHello:TestFortyTwo + ok 1 - Test Hello.FortyTwo + 1..1 + ok 1 - TestHello:TestFortyTwo + + Testmethod TestHello:TestHelloWorld + ok 1 - Test equal strings just for fun + ok 2 - HW strings should be equal + 1..2 + ok 2 - TestHello:TestHelloWorld + + Testmethod TestHello:TestRight + ok 1 - Test Hello.Right says Right + 1..1 + ok 3 - TestHello:TestRight + + # Ran: '' + # + # PASSED + +If a failure occurs it will report FAILED instead of PASSED and will show you the place of the failure. I you want to debug the code you can set a breakpoint inside TestHello.TestRight, hit <F5> again and start debugging. + +If you have a lot of tests, and want to let run just one, you can do that like so: + +---- + + 'Module TestMe + + 'Is a startclass + 'Starts the Unittest, when F5 was hit + + Public Sub Main() + + Test.Main("TestHello.TestRight") + + End + +---- + +You can combine any number of testmodules and/or testmethods For example like this: + + Test.Main("TestmoduleOne.TestmethodOne, TestmoduleOne.TestmethodTwo, TestmoduleTwo") + +In this case only two specific tests in TestmoduleOne and all in TestmoduleTwo will run. + +Testmodule and Testmethods can be named the same way as any Gambas Module or Method except that a Testmethod may not be named _Setup(), _Teardown(), _SetupEach() or _TeardownEach(). + +## Test your project on the console + +You also can test your on the console. The command **/usr/bin/gbt3 /path/to/my/project** executes the unittests and prints the result to standard output: + + 1..3 + + Testmethod TestHello:TestFortyTwo + ok 1 - Test Hello.FortyTwo + 1..1 + ok 1 - TestHello:TestFortyTwo + + Testmethod TestHello:TestHelloWorld + ok 1 - Test equal strings just for fun + ok 2 - HW strings should be equal + 1..2 + ok 2 - TestHello:TestHelloWorld + + Testmethod TestHello:TestRight + ok 1 - Test Hello.Right says Right + 1..1 + ok 3 - TestHello:TestRight + + # Ran: '' + # + # PASSED + +## Test fixture + +Sometimes it is neccessary to create a "fixture", a special environment for a test or a couple of tests, and to destroy this environment after the test is done. For example a database connection should be established at the beginning of all tests, some tables for testing should be created and deleted for every single testmethod and the database connection should be closed at the end. This can be done with Setup... and Teardown... functions inside the Testmodule. + +### Sub _SetupEach() and Sub _TeardownEach() + +You can create methods with these names to create an environment for each testmethod before it is invoked and to destroy it afterwards. If you have five testmethods inside your testmodule these functions will be invoked five times, _SetupEach() before each testmethod, _TeardownEach() after each testmethod. Got it? + +### Sub _Setup() and Sub _Teardown() + +You can create methods with these names to create an environment for all testmethods inside a testmodule, in the beginning _Setup() is invoked and after all testmethods inside the testmodule are done you can destroy the environment with _Teardown(). \ No newline at end of file diff --git a/main/lib/test/gb.test/taskell.md b/main/lib/test/gb.test/taskell.md new file mode 100644 index 00000000..2c332f47 --- /dev/null +++ b/main/lib/test/gb.test/taskell.md @@ -0,0 +1,54 @@ +## To Do + + +## Work + +- IDE: Reliability of Tab 'Unit tests'. + > When hit F4 with a newly opened project with tabs the tab just flickers but does not show up. After it is opened by hand with the mouse, the next F4 shows the tail of the TAP output, which is right. But if then the tab is resized with the mouse the next F4 shows the output anywhere in the middle and not the tail. The last one cannot reproduced always. + +## Done + +- Plan selftests + > gb.test: Every subtest in all tests must plan the own assertion count +- Write Documentation + * [ ] Test.Main overall doc what is does and how tests are run. +- Event handlers are not test methods + > Some test modules test object which emit events, but the event handlers are Public Subs which get picked up as test methods by gb.test. What can we do about it? +- TestAllAssertion must test all assertions +- Decide where Skip, SkipAll, Todo reside + > SkipAll is in Test, Skip in Assert, Todo in Assert. This is inconsistent, We have to decide where they should be. Chris: I am for Test. +- Install gbt3 at Gambas installation + > gbt3 is compiled and installed by make but there is no symlink gbt3 > gbt3.gambas +- Ensure TAP created according to spec + > Tested TAP via gbt3 and python tappy +- gbt3 has to return exit status at the end +- Assert and Test documentation + > Every public method in Test and Assert has to have a description +- Hide symbols in Test + > There are symbols in Test which should not be shown to the enduser. These shoult be hidden by a leading underscore: + * [ ] Subtest + * [ ] Finish + * [ ] Reset +- No Bug: Directives always 0 + > Was no bug, was lack of understanding +- TestAssertion: Skip and Todo + > TestAssertion must contain variables to store Skip and Todo for summary. See TestSummary. +- Todo Directive + > Todo directive does not exist. See TestSummary. Was wrong. Todo exists. +- Free Assert from non-assertions + > There are things in Assert which are no assertions, like Print, Finish, Note and so on. These should be methods of Test. For example: Test.Plan, Test.Note, Test.Finish ... +- Test.Main has to complain if testmodule doesn't exist +- Human readability of TAP output +- Subtests don't count the right way + > If there is only one testmodule called, subtest counts a lot of subtests instead just one +- gb.test doesn't print to console +- Summary as a TAP comment + > Description in .hidden/summary-example-txt + * [x] New method Test.ShowTodos + * [x] New method Test.ShowSkips +- Parser parse subtests +- Skip all + > Parse `1..0 # SKIP` style TAPs indicating that a whole test was skipped. +- Document Approximate and RelativeApproximate +- Plan + > If a plan failes it has to be reported in the TAP output as well as in the summary which is not the case at the moment. diff --git a/main/lib/test/gb.test/test.sh b/main/lib/test/gb.test/test.sh new file mode 100755 index 00000000..5867f066 --- /dev/null +++ b/main/lib/test/gb.test/test.sh @@ -0,0 +1,5 @@ +#! /bin/bash + +gbc3 -g ./ 2>&1 +gba3 ./ +gbr3 -s TestMe ./gb.test.gambas diff --git a/main/lib/test/gb.test/unittesthelloworld-1.0.0.tar.gz b/main/lib/test/gb.test/unittesthelloworld-1.0.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..6cc083e35a0643c5386700a29cb01128f5c50606 GIT binary patch literal 12298 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 literal 0 HcmV?d00001 diff --git a/main/lib/test/main.c b/main/lib/test/main.c new file mode 100644 index 00000000..cefdea34 --- /dev/null +++ b/main/lib/test/main.c @@ -0,0 +1,64 @@ +/*************************************************************************** + + main.c + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE *GB_PTR EXPORT; + + +BEGIN_METHOD(Test_Load, GB_STRING name) + + GB.ReturnObject((void *)GB.LoadClassFrom(GB.ToZeroString(ARG(name)), NULL)); + +END_METHOD + + +GB_DESC TestDesc[] = +{ + GB_DECLARE_STATIC("__Test"), + + GB_STATIC_METHOD("Load", "Class", Test_Load, "(Class)s"), + + GB_END_DECLARE +}; + + +GB_DESC *GB_CLASSES [] EXPORT = +{ + TestDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/test/main.h b/main/lib/test/main.h new file mode 100644 index 00000000..2df1d81f --- /dev/null +++ b/main/lib/test/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif /* __MAIN_H */ diff --git a/main/lib/vb/Makefile.am b/main/lib/vb/Makefile.am new file mode 100644 index 00000000..f157f2b5 --- /dev/null +++ b/main/lib/vb/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.vb +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.vb.la + +gb_vb_la_LIBADD = @C_LIB@ @MATH_LIB@ +gb_vb_la_LDFLAGS = -module @LD_FLAGS@ +gb_vb_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_vb_la_SOURCES = main.h main.c vb.h vb.c vbdate.h vbdate.c + + + + diff --git a/main/lib/vb/gb.vb.component b/main/lib/vb/gb.vb.component new file mode 100644 index 00000000..35845d62 --- /dev/null +++ b/main/lib/vb/gb.vb.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.vb +Name=Visual Basic compatibility +Author=Benoît Minisini,Daniel Campos Fernández,Nigel Gerrard + + diff --git a/main/lib/vb/main.c b/main/lib/vb/main.c new file mode 100644 index 00000000..e738739f --- /dev/null +++ b/main/lib/vb/main.c @@ -0,0 +1,50 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "vb.h" +#include "vbdate.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CVbDesc, + NULL +}; + + + +int EXPORT GB_INIT(void) +{ + return 0; +} + + + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/vb/main.h b/main/lib/vb/main.h new file mode 100644 index 00000000..eddc5750 --- /dev/null +++ b/main/lib/vb/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/vb/vb.c b/main/lib/vb/vb.c new file mode 100644 index 00000000..9af6da21 --- /dev/null +++ b/main/lib/vb/vb.c @@ -0,0 +1,312 @@ +/*************************************************************************** + + vb.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __VB_C + +#include "vb.h" +#include "vbdate.h" +#include + +#ifdef OS_CYGWIN + #include +#else + #include +#endif + +#include + +BEGIN_METHOD(CVB_val, GB_STRING str) + + GB_VALUE result; + + GB.NumberFromString(GB_NB_READ_ALL | GB_NB_READ_HEX_BIN, STRING(str), LENGTH(str), &result); + + if (result.type == GB_T_INTEGER) + GB.ReturnInteger(((GB_INTEGER *)(void *)&result)->value); + if (result.type == GB_T_LONG) + GB.ReturnLong(((GB_LONG *)(void *)&result)->value); + else if (result.type == GB_T_FLOAT) + GB.ReturnFloat(((GB_FLOAT *)(void *)&result)->value); + else + GB.ReturnInteger(0); + + GB.ReturnConvVariant(); + +END_METHOD + + +/* val should be GB_VARIANT. This is just a trick, so that + I can use STRING() and LENGTH() macros just after having + converted val to a string. Yes, I know... Horrible. +*/ + +BEGIN_METHOD(CVB_str, GB_STRING val) + + GB.Conv((GB_VALUE *)(void *)ARG(val), GB_T_STRING); + + GB.ReturnNewString(STRING(val), LENGTH(val)); + +END_METHOD + +BEGIN_METHOD(CVB_Left, GB_STRING val;GB_INTEGER Count;) + + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + if ( LENGTH(val)<=VARG(Count) ) + { + GB.ReturnNewString(STRING(val), LENGTH(val)); + return; + } + + GB.ReturnNewString(STRING(val), VARG(Count)); + +END_METHOD + +BEGIN_METHOD(CVB_Right, GB_STRING val;GB_INTEGER Count;) + + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + if ( LENGTH(val)<=VARG(Count) ) + { + GB.ReturnNewString(STRING(val), LENGTH(val)); + return; + } + + GB.ReturnNewString(STRING(val)+(LENGTH(val)-VARG(Count)), VARG(Count)); + +END_METHOD + +BEGIN_METHOD(CVB_Mid, GB_STRING val;GB_INTEGER Start;GB_INTEGER Count;) + + int count; + + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + + if (!MISSING (Count) ) + { + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + count=VARG(Count); + } + else + count=LENGTH(val); + + if ( VARG(Start)>LENGTH(val) ) + GB.ReturnNewString(NULL,0); + + if ( ( LENGTH(val)-VARG(Start) ) <= count ) count=LENGTH(val)-VARG(Start)+1; + + GB.ReturnNewString(STRING(val)+VARG(Start)-1, count ); + + +END_METHOD + +BEGIN_METHOD(CVB_DateAdd, GB_STRING period;GB_INTEGER interval; GB_DATE Date) + + char *Period=NULL; + int Interval = 0; + GB_DATE RETURN; + Period=GB.ToZeroString(ARG(period)); + Interval = VARG(interval); + + RETURN.type = ARG(Date)->type; + RETURN.value.date = ARG(Date)->value.date; + RETURN.value.time = ARG(Date)->value.time; + + if (strncasecmp(Period,"yyyy",4) == 0){ //Add Years + DATE_adjust(&RETURN, 4, Interval); + } + else { + if (strncasecmp(Period,"ww",2)== 0){ //Add weeks + DATE_adjust(&RETURN, 1, (7 * Interval)); + } + else { + switch(Period[0]) + { + case 's': /* Seconds */ + case 'S': + DATE_adjust(&RETURN, 2, (1000 * Interval)); + break; + case 'n': /* Minutes */ + case 'N': + DATE_adjust(&RETURN, 2, (60000 * Interval)); + break; + case 'h': /* Hours */ + case 'H': + DATE_adjust(&RETURN, 2, (3600000 * Interval)); + break; + case 'd': /* Days */ + case 'D': + case 'y': /* Day of Year */ + case 'Y': + DATE_adjust(&RETURN, 1, Interval); + break; + case 'w': /* Weekdays : Monday - Friday */ + case 'W': + DATE_adjust(&RETURN, 3, Interval); + break; + case 'm': /* Calendar Months */ + case 'M': + DATE_adjust(&RETURN, 0, Interval); + break; + case 'q': /* Quarters */ + case 'Q': + DATE_adjust(&RETURN, 0, (3 * Interval)); + break; + default: + GB.Error("Invalid date parameter"); + } + } + } + + + GB.ReturnDate(&RETURN); + +END_METHOD + +BEGIN_METHOD(CVB_DateDiff, GB_STRING period;GB_DATE Date1; GB_DATE Date2) + + GB_DATE Date1, Date2; + char *Period=NULL; + int Interval = 0; + + Period=GB.ToZeroString(ARG(period)); + Date1.type = ARG(Date1)->type; + Date1.value.date = ARG(Date1)->value.date; + Date1.value.time = ARG(Date1)->value.time; + + Date2.type = ARG(Date2)->type; + Date2.value.date = ARG(Date2)->value.date; + Date2.value.time = ARG(Date2)->value.time; + + if (strncasecmp(Period,"yyyy",4) == 0){ //Add Years + Interval = DATE_diff(&Date1, &Date2, 4); + } + else { + if (strncasecmp(Period,"ww",2)== 0){ //Weeks + Interval = DATE_diff(&Date1, &Date2, 5); + } + else { + switch(Period[0]) + { + case 's': /* Seconds */ + case 'S': + Interval = DATE_diff(&Date1, &Date2, 2)/1000; + break; + case 'n': /* Minutes */ + case 'N': + Interval = DATE_diff(&Date1, &Date2, 2)/60000; + break; + case 'h': /* Hours */ + case 'H': + Interval = DATE_diff(&Date1, &Date2, 2)/3600000; + break; + case 'd': /* Days */ + case 'D': + case 'y': /* Day of Year */ + case 'Y': + Interval = DATE_diff(&Date1, &Date2, 1); + break; + case 'w': /* Weekdays : Monday - Friday */ + case 'W': + Interval = DATE_diff(&Date1, &Date2, 3); + break; + case 'm': /* Calendar Months */ + case 'M': + Interval = DATE_diff(&Date1, &Date2, 0); + break; + case 'q': /* Quarters */ + case 'Q': + Interval = (DATE_diff(&Date1, &Date2, 0)/3); + break; + default: + GB.Error("Invalid date parameter"); + } + } + } + + GB.ReturnInteger(Interval); + +END_METHOD + +BEGIN_METHOD(CVB_Round,GB_FLOAT Number;GB_INTEGER Decimals;) + + double decimals=VARGOPT(Decimals, 0); + double number=VARG(Number); + + if (decimals < 0) { GB.Error("Invalid argument"); return; } + + #if 0 /* test */ + decimals=exp10(decimals); + number*=decimals; + number=round(number); + number/=decimals; + #endif + + decimals = pow(10, decimals); + number *= decimals; + number = rint(number); + number /= decimals; + + GB.ReturnFloat(number); + +END_METHOD + +GB_DESC CVbDesc[] = +{ + GB_DECLARE("Vb", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("Val", "v", CVB_val, "(String)s"), + GB_STATIC_METHOD("Str", "s", CVB_str, "(Value)v"), + GB_STATIC_METHOD("Str$", "s", CVB_str, "(Value)v"), + GB_STATIC_METHOD("Left", "s", CVB_Left, "(String)s(Count)i"), + GB_STATIC_METHOD("Left$", "s", CVB_Left, "(String)s(Count)i"), + GB_STATIC_METHOD("Right", "s", CVB_Right, "(String)s(Count)i"), + GB_STATIC_METHOD("Right$", "s", CVB_Right, "(String)s(Count)i"), + GB_STATIC_METHOD("Mid", "s", CVB_Mid, "(String)s(Start)i[(Count)i]"), + GB_STATIC_METHOD("Mid$", "s", CVB_Mid, "(String)s(Start)i[(Count)i]"), + GB_STATIC_METHOD("DateAdd", "d", CVB_DateAdd, "(period)s(interval)i(Date)d"), + GB_STATIC_METHOD("DateDiff", "i", CVB_DateDiff, "(period)s(Date1)d(Date2)d"), + GB_STATIC_METHOD("Round","f",CVB_Round,"(Number)f[(Decimals)i"), + //GB_METHOD("Dir", "s", CVB_dir, "(Mask)s[(Attributes)i]"), + + GB_END_DECLARE +}; + + + + + diff --git a/main/lib/vb/vb.h b/main/lib/vb/vb.h new file mode 100644 index 00000000..66b68ede --- /dev/null +++ b/main/lib/vb/vb.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + vb.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __VB_H +#define __VB_H + +#include "main.h" + +#ifndef __VB_C +extern GB_DESC CVbDesc[]; +#endif + +#endif diff --git a/main/lib/vb/vbdate.c b/main/lib/vb/vbdate.c new file mode 100644 index 00000000..5c16f1d7 --- /dev/null +++ b/main/lib/vb/vbdate.c @@ -0,0 +1,215 @@ +/*************************************************************************** + + vbdate.c + + (c) 2000-2003 Nigel Gerrard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __VBDATE_C + +#include "vbdate.h" +#include +#include + + +#define DATE_YEAR_MIN -4801 +#define DATE_YEAR_MAX 9999 + +/* Helper functions */ + +const char days_in_months[2][13] = +{ /* error, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */ +}; + +const short days_in_year[2][14] = +{ /* 0, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } /* leap year */ +}; + +/* Returns 1 for a leap year, 0 else */ +int date_is_leap_year(short year) +{ + if (year < 0) + year += 8001; + + if ((((year % 4) == 0) && ((year % 100) != 0)) || (year % 400) == 0) + return 1; + else + return 0; +} + +int date_is_valid(GB_DATE_SERIAL *date) +{ + return ((date->month >= 1) && (date->month <= 12) && + (date->year >= DATE_YEAR_MIN) && (date->year <= DATE_YEAR_MAX) && (date->year != 0) && + (date->day >= 1) && (date->day <= days_in_months[date_is_leap_year(date->year)][(short)date->month]) && + (date->hour >= 0) && (date->hour <= 23) && (date->min >= 0) && (date->min <= 59) && + (date->sec >= 0) && (date->sec <= 59)); +} + +/** Modulo function that works correctly for negative divisors */ +//#define modulo(x,y) ((x)%(y)<0?(x)%(y)+(y):(x)%(y)) + +static int modulo(int x, int y) +{ + int mod = x % y; + if (mod < 0) + mod += y; + return mod; +} + +void DATE_adjust( GB_DATE *vdate, int period, int interval) /* Adjust the date by the interval period */ +{ + + GB_DATE_SERIAL *date; + int year, month, day; + date = GB.SplitDate(vdate); + + switch(period){ + case 0: /* Calendar Month */ + year = ((date->year * 12) + (date->month - 1) + interval)/12; + month = modulo((date->month - 1)+interval, 12) + 1; + day = date->day > days_in_months[date_is_leap_year(year)][month] ? days_in_months[date_is_leap_year(year)][month] : date->day; + date->day = day; + date->month = month; + date->year = year; + GB.MakeDate(date, vdate); + break; + case 1: /* days */ + vdate->value.date += interval; + break; + case 2: /* Time */ + vdate->value.time += interval; + break; + case 3: /* weekdays - in this case weekdays are Mon - Fri */ + vdate->value.date += ( 7 * ( interval / 5)); + date->weekday += ( interval % 5 ); + if (date->weekday > 5){ + date->weekday -= 5; + vdate->value.date += 2; + } + if (date->weekday < 1){ + date->weekday += 5; + vdate->value.date -= 2; + } + vdate->value.date += ( interval % 5); + break; + case 4: /* Add year */ + while ( interval != 0){ + if ( interval < 0 ){ + vdate->value.date -= days_in_year[date_is_leap_year(date->year)][13]; + date->year--; + interval++; + } + else { + vdate->value.date += days_in_year[date_is_leap_year(date->year)][13]; + date->year++; + interval--; + } + } + break; + } + + /* Now if time takes it into another day */ + while (vdate->value.time >= 86400000){ + vdate->value.date++; + vdate->value.time -= 86400000; + } + /* Or time is negative so we need to take off a day */ + while (vdate->value.time < 0){ + vdate->value.date--; + vdate->value.time += 86400000; + } + + CLEAR(&date); + date = GB.SplitDate(vdate); + + if (!date_is_valid(date)){ + /*printf("Invalid date : year [%i] month [%i] day [%i] hour [%i] min [%i] sec [%i] weekday [%i] msec [%i]\n", + date->year, date->month, date->day, date->hour, date->min, date->sec, date->weekday, date->msec);*/ + GB.Error("Invalid Date Returned"); + } + CLEAR(&date); + +} + +int DATE_diff( GB_DATE *vdate1, GB_DATE *vdate2, int period) +{ + GB_DATE_SERIAL *date1, *date2; + int diff = 0; + int year1, year2, month1, month2, weekday1, weekday2; + + date1 = GB.SplitDate(vdate1); + weekday1 = date1->weekday; + year1 = date1->year; + month1 = date1->month; + + date2 = GB.SplitDate(vdate2); + weekday2 = date2->weekday; + year2 = date2->year; + month2 = date2->month; + + switch(period){ + case 0: /* Calendar Month */ + diff = ((year1 * 12) + month1) - ((year2 * 12) + month2); + break; + case 1: /* days */ + diff = vdate1->value.date - vdate2->value.date; + break; + case 2: /* Time */ + diff = ((vdate1->value.date - vdate2->value.date) * 86400000) + (vdate1->value.time - vdate2->value.time); + break; + case 3: /* weekdays */ + /* If invalid weekday then find closest valid day: + * eg. 7 -1, 6 -5*/ + weekday1 == 7 ? weekday1 = 1 : weekday1 == 6 ? weekday1 = 5 : weekday1; + weekday2 == 7 ? weekday2 = 1 : weekday2 == 6 ? weekday2 = 5 : weekday2; + + diff = (((vdate1->value.date - vdate2->value.date) / 7) * 5) + + (weekday1 - weekday2); + /* Needs validating */ + break; + case 4: /* year */ + diff = year1 - year2; + break; + case 5: /* Weeks */ + diff = (vdate1->value.date - vdate2->value.date)/7; + break; + } + CLEAR(&date1); + CLEAR(&date2); + return diff; +} + +/* End Helper functions */ + +/* +GB_DESC CVbDateDesc[] = +{ + GB_DECLARE("Vb", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("DateAdd", "d", CVB_DateAdd, "(period)s(interval)i(Date)v"), + GB_STATIC_METHOD("DateDiff", "i", CVB_DateDiff, "(period)s(Date1)v(Date2)v"), + + GB_END_DECLARE +};*/ + diff --git a/main/lib/vb/vbdate.h b/main/lib/vb/vbdate.h new file mode 100644 index 00000000..c656b64f --- /dev/null +++ b/main/lib/vb/vbdate.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + vbdate.h + + (c) 2000-2003 Nigel Gerrard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __VBDATE_H +#define __VBDATE_H + +#include "main.h" + +void DATE_adjust( GB_DATE *vdate, int period, int interval); /* Adjust the date by the interval period */ +int DATE_diff( GB_DATE *vdate1, GB_DATE *vdate2, int period); + +#endif diff --git a/main/m4 b/main/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/main/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/main/man/Makefile.am b/main/man/Makefile.am new file mode 100644 index 00000000..0b283cf6 --- /dev/null +++ b/main/man/Makefile.am @@ -0,0 +1 @@ +dist_man_MANS = gbx3.1 gbr3.1 gbc3.1 gba3.1 gbi3.1 gbh3.1 diff --git a/main/man/gba3.1 b/main/man/gba3.1 new file mode 100644 index 00000000..4371e5a6 --- /dev/null +++ b/main/man/gba3.1 @@ -0,0 +1,54 @@ +.TH "gba3" "1" "October 2013" "Ubuntu" "User Commands" + +.SH "NAME" +gba3 \- GAMBAS Archiver + +.SH "SYNOPSIS" +.B gba3 +[options] [] +.br +.B gba3 +-x + +.SH "DESCRIPTION" +Gambas is a free development environment based on a Basic interpreter with object extensions, a mix of Java(tm) and Visual Basic(tm). +With Gambas, you can quickly design your program GUI, access MySQL or PostgreSQL databases, pilot KDE applications with DCOP, translate your program into many languages, and so on... + +\fBgba3\fR is the archiver that allows you to create a standalone one-file executable from a Gambas project or extract a specific file from a Gambas executable (-x option). + +.SH "OPTIONS" +.TP +\fB\-h, --help\fR +display help and exit +.TP +\fB\-L, --license\fR +display license +.TP +\fB\-o, --output=ARCHIVE\fR +archive path [/.gambas] +.TP +\fB\-s, --swap\fR +swap endianness +.TP +\fB\-x, --extract=ARCHIVE\fR +archive path +.TP +\fB\-v, --verbose\fR +verbose output +.TP +\fB\-V, --version\fR +display version + +.SH "AVAILABILITY" +The latest version of Gambas can always be obtained from +\fBgambas.sourceforge.net\fR, documentation about the language is at +\fBhttp://gambaswiki.org/wiki\fR. + +.SH "REPORTING BUGS" +Report bugs to \fBhttp://gambaswiki.org/bugtracker\fR + +.SH "COPYRIGHT" +Copyright\(co Benoît Minisini ; +.PP +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/main/man/gbc3.1 b/main/man/gbc3.1 new file mode 100644 index 00000000..6b1a11c6 --- /dev/null +++ b/main/man/gbc3.1 @@ -0,0 +1,72 @@ +.TH "gbc3" "1" "October 2013" "Ubuntu" "User Commands" + +.SH "NAME" +gbc3 \- GAMBAS Compiler + +.SH "SYNOPSIS" +.B gbc3 +[options] [] + +.SH "DESCRIPTION" +Gambas is a free development environment based on a Basic interpreter with object extensions, a mix of Java(tm) and Visual Basic(tm). +With Gambas, you can quickly design your program GUI, access MySQL or PostgreSQL databases, pilot KDE applications with DCOP, translate your program into many languages, and so on... + +\fBgbc3\fR is the compiler that allows you to compile Gambas projects into architecture-independent bytecode. + +.SH "OPTIONS" +.TP +\fB\-a, --all\fR +compile all +.TP +\fB\-e, --translate-errors\fR +display translatable error messages +.TP +\fB\-g, --debug\fR +add debugging information +.TP +\fB\-h, --help\fR +display help and exit +.TP +\fB\-L, --license\fR +display license +.TP +\fB\-m, --public-module\fR +module symbols are public by default +.TP +\fB\-p, --public-control\fR +form controls are public +.TP +\fB\-r, --root \fR +gives the gambas installation directory +.TP +\fB\-s, --swap\fR +swap endianness +.TP +\fB\-t, --translate\fR +output translation files +.TP +\fB\-v, --verbose\fR +verbose output +.TP +\fB\-V, --version\fR +display version +.TP +\fB\-w, --warnings\fR +display warnings +.TP +\fB\-x, --exec\fR +define the 'Exec' preprocessor constant + +.SH "AVAILABILITY" +The latest version of Gambas can always be obtained from +\fBgambas.sourceforge.net\fR, documentation about the language is at +\fBhttp://gambaswiki.org/wiki\fR. + +.SH "REPORTING BUGS" +Report bugs to \fBhttp://gambaswiki.org/bugtracker\fR + +.SH "COPYRIGHT" +Copyright\(co Benoît Minisini ; +.PP +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/main/man/gbh3.1 b/main/man/gbh3.1 new file mode 100644 index 00000000..d40554b2 --- /dev/null +++ b/main/man/gbh3.1 @@ -0,0 +1,49 @@ +.TH "gbh3" "1" "October 2013" "Ubuntu" "User Commands" + +.SH "NAME" +gbh3 \- GAMBAS Help Extractor + +.SH "SYNOPSIS" +.B gbh3 +[options] [] + +.SH "DESCRIPTION" +Gambas is a free development environment based on a Basic interpreter with object extensions, a mix of Java(tm) and Visual Basic(tm). +With Gambas, you can quickly design your program GUI, access MySQL or PostgreSQL databases, pilot KDE applications with DCOP, translate your program into many languages, and so on... + +\fBgbh3\fR is used to extract Gambas documentation from C/C++ source files and +create .help files for a component from them. + +.SH "OPTIONS" +.TP +\fB\-r, --root \fR +gives the Gambas installation directory +.TP +\fB\-c, --component \fR +generate help directly in a component '*.help' file +.TP +\fB\-v, --verbose\fR +run in verbose mode +.TP +\fB\-V, --version\fR +display version +.TP +\fB\-L, --license\fR +display license +.TP +\fB\-h, --help\fR +display help and exit + +.SH "AVAILABILITY" +The latest version of Gambas can always be obtained from +\fBgambas.sourceforge.net\fR, documentation about the language is at +\fBhttp://gambaswiki.org/wiki\fR. + +.SH "REPORTING BUGS" +Report bugs to \fBhttp://gambaswiki.org/bugtracker\fR + +.SH "COPYRIGHT" +Copyright\(co Benoît Minisini ; +.PP +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/main/man/gbi3.1 b/main/man/gbi3.1 new file mode 100644 index 00000000..2b2559fb --- /dev/null +++ b/main/man/gbi3.1 @@ -0,0 +1,45 @@ +.TH "gbi3" "1" "October 2013" "Ubuntu" "User Commands" + +.SH "NAME" +gbi3 \- GAMBAS Component Informer + +.SH "SYNOPSIS" +.B gbi3 +[options] [components] + +.SH "DESCRIPTION" +Gambas is a free development environment based on a Basic interpreter with object extensions, a mix of Java(tm) and Visual Basic(tm). +With Gambas, you can quickly design your program GUI, access MySQL or PostgreSQL databases, pilot KDE applications with DCOP, translate your program into many languages, and so on... + +\fBgbi3\fR allows you to generate component description files. + +.SH "OPTIONS" +.TP +\fB\-h, --help\fR +display help and exit +.TP +\fB\-L, --license\fR +display license +.TP +\fB\-r, --root \fR +gives the gambas installation directory +.TP +\fB\-v, --verbose\fR +verbose output +.TP +\fB\-V, --version\fR +display version + +.SH "AVAILABILITY" +The latest version of Gambas can always be obtained from +\fBgambas.sourceforge.net\fR, documentation about the language is at +\fBhttp://gambaswiki.org/wiki\fR. + +.SH "REPORTING BUGS" +Report bugs to \fBhttp://gambaswiki.org/bugtracker\fR + +.SH "COPYRIGHT" +Copyright\(co Benoît Minisini ; +.PP +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/main/man/gbr3.1 b/main/man/gbr3.1 new file mode 100644 index 00000000..e77e89c8 --- /dev/null +++ b/main/man/gbr3.1 @@ -0,0 +1,66 @@ +.TH "gbr3" "1" "October 2013" "Ubuntu" "User Commands" + +.SH "NAME" +gbr3 \- GAMBAS Interpreter + +.SH "SYNOPSIS" +.B gbr3 +[options] [] + +.SH "DESCRIPTION" +Gambas is a free development environment based on a Basic interpreter with object extensions, a mix of Java(tm) and Visual Basic(tm). +With Gambas, you can quickly design your program GUI, access MySQL or PostgreSQL databases, pilot KDE applications with DCOP, translate your program into many languages, and so on... + +\fBgbr3\fR is the interpreter that allows you to execute a Gambas executable. + +.SH "OPTIONS" +.TP +\fB\-g\fR +enter debugging mode +.TP +\fB\-h, --help\fR +display help and exit +.TP +\fB\-H, --httpd\fR +run through an embedded http server +.TP +\fB\-j\fR +disable just-in-time compiler +.TP +\fB\-k\fR +do not unload shared libraries +.TP +\.fB\-L, --license\fR +display license +.TP +\fB\-p \fR +activate profiling and debugging mode +.TP +\fB\-r \fR +redirect standard error output +.TP +\fB\-s \fR +override startup class +.TP +\fB\-t, --trace\fR +dump the position of each executed line of code +.TP +\fB\-T \fR +run the specified test modules +.TP +\fB\-V, --version\fR +display version + +.SH "AVAILABILITY" +The latest version of Gambas can always be obtained from +\fBgambas.sourceforge.net\fR, documentation about the language is at +\fBwww.gambasdoc.org\fR. + +.SH "REPORTING BUGS" +Report bugs to \fBhttp://gambaswiki.org/bugtracker\fR + +.SH "COPYRIGHT" +Copyright\(co 2002, 2013 Benoît Minisini ; +.PP +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/main/man/gbx3.1 b/main/man/gbx3.1 new file mode 100644 index 00000000..38e99d47 --- /dev/null +++ b/main/man/gbx3.1 @@ -0,0 +1,72 @@ +.TH "gbx3" "1" "October 2013" "Ubuntu" "User Commands" + +.SH "NAME" +gbx3 \- GAMBAS Interpreter + +.SH "SYNOPSIS" +.B gbx3 +[options] [] [-- ] +.br +.B gbx3 +-e + +.SH "DESCRIPTION" +Gambas is a free development environment based on a Basic interpreter with object extensions, a mix of Java(tm) and Visual Basic(tm). +With Gambas, you can quickly design your program GUI, access MySQL or PostgreSQL databases, pilot KDE applications with DCOP, translate your program into many languages, and so on... + +\fBgbx3\fR is the interpreter that allows you to execute a Gambas project or evaluate a Gambas expression (-e option). + +.SH "OPTIONS" +.TP +\fB\-e\fR +evaluate an expression +.TP +\fB\-g\fR +enter debugging mode +.TP +\fB\-h, --help\fR +display help and exit +.TP +\fB\-H, --httpd\fR +run through an embedded http server +.TP +\fB\-j\fR +disable just-in-time compiler +.TP +\fB\-k\fR +do not unload shared libraries +.TP +\fB\-L, --license\fR +display license +.TP +\fB\-p \fR +activate profiling and debugging mode +.TP +\fB\-r \fR +redirect standard error output +.TP +\fB\-s \fR +override startup class +.TP +\fB\-t, --trace\fR +dump the position of each executed line of code +.TP +\fB\-T \fR +run the specified test modules +.TP +\fB\-V, --version\fR +display version + +.SH "AVAILABILITY" +The latest version of Gambas can always be obtained from +\fBgambas.sourceforge.net\fR, documentation about the language is at +\fBhttp://gambaswiki.org/wiki\fR. + +.SH "REPORTING BUGS" +Report bugs to \fBhttp://gambaswiki.org/bugtracker\fR + +.SH "COPYRIGHT" +Copyright\(co Benoît Minisini ; +.PP +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/main/mime/application-x-gambas3-48.png b/main/mime/application-x-gambas3-48.png new file mode 100644 index 0000000000000000000000000000000000000000..178775ed61f1021d14d2e26adfa81581ac28f9d1 GIT binary patch literal 1437 zcmV;O1!DS%P)3$g6vuz(zV|wv1wo6M>JYKQ$5%B}!9>=iKk zyAh8dFoM7c0{b}v?qCs8gjP9-b}7SdoZ_?@X3=6CR)mmnWP&*15 zbiQ$Ba+iAojwT1&h&AiyQ}-+$aSq=_@G>HtZom8{1>EDEN2`?eNTbo2#qHjyq!5Z} z?S)q!5NMZD9(9k(yjN=*&j+7yx^3YAEN5zKX6_e>vD}@t%l-(5_ubjt;hjQ&FpiKy z-7$0s{A`Yr8~ZKfc7f(l&coy~*p^gV7-pUNH?%~aWSjMMcz=3ScEf0JF$WV?E3>w!|;$XG(Q;*!F!MasE2e1nT{ zX_1Mv%P1U_QM#4U+KfX8KvGIr?9S%{DFI-L`dk*)?{&?QGXgPoFxNKmf~f%DHTO7X z_+R6Zq)h*!tOt3TWGat)7t)Z9)DFtM;R;6UrhJ!_J>SU-qOb7MC-S<_&}yD`PUL$j>T%r^^|{*qPIc5kv2Gn+T?s)sA!^{MXeED|V;F#Z{)wkJVh27&bN#sL&c31jPZYC>jQPDxm5Ubk@brxjr$ZUMOq7Q-$`> zL1bs%A^_2D3aF;k?~E6oe{*8kc%evz4r>rmMAKIXuwv0L*lEP7z6Qy7;p_#8TzTXT zfk=gvHC?rCT2QN+QGzgS#Hv1FvaWE>?TLZ#$qND&Ayjsre7Ew{Dj4bVLO_Zj885u# z&cr|u${lR+6^OJ;UO|y`Z?5ZI(qJeW`aYo_FS>kTVsDVns=yab(OUZWr+iNZB%Q@~ r`&M>&#wh1@wk-mLBrkG$QL#p6d-D%40g z*K{9C$mnXjA3m+uiLbfH(jK|d8GL7bN48Vd8=9*f?<~L>%kiPNb4vW0xU=e10NQKp zaw1^rfgo~w$?Wv};wP20(XtG(HAF8rV6Wu|f!cpx-+6m5#NMTwGgeH^r?*z={0_I&#T46HD67?fb42tE`epavTF7bglt z6U2j*=&UJ#BESxS5;21Rg_NQ2I5xbl{}1M0H6AxP3=nmY|J3s@=6|VKe`JlfNA<6* z|HS-{J5sdCe5iTw|D*Q5^!yLzFRRV47^Uuapk3;)T2=Z&wG&lRG6Q~cgD{bDsImkX z32B6!T`_~x#PGCDxO4(_39NYZmiCmU$oQky4wk(7$yUM#)4XuWUPDHJ%~SXLDyuV> z0uA>;3@nk18gqC~#yK4W<%bdx*3zoGva#Ff+C>WW!x|xCqsk^Xn;ZD8kU~8TgC*djs0@6+$wRpGh87!AHC$&*IdZm>OCw)9ne{4tgD0cdZ{4>M zM0+HPT#k%i6o|4_KfB3yPptqAPy8VQnAaL4qG$_S_}>0$z%AS-X}8lswRy^I{$y?b zVwu`tNA%kvL=I*d-8x|Pi^K(){au;@At1x^a^**%^TAt*PnZ%E$t%gqxLS?GUeDiP z0RtAV?X+>~3CJ?p^1@RXPZG#I&-@}YU_nEgV3-RTP;_QiL2q-(6)3ou6i7X&E{x1x zk6tfg*oza8aSP#!1dX$SIeOEeXFkvF?j;Ef+?wdcY~fg5p#M)g>sD5aZ+e;iL4 z!6j$B=E3e6p?$GfolZgAY+haU!1lN}GJE2^7Z=s3COywl@F^HvAWw|w4W{n|;h$(L znnIJ26C3W+;RqrA5={`u-Ojl9JvlsYnSBN>`PB=bCj*jJSI>0@LQD+51^2?2)n}-OxU;!kss-tizUoT4yzZnvk14SqxhqY0yNU@e3665G3KXm-ggA7) z>q3Sk>b}s0fKi|tlc~A~HI1$pVpBY57&obYvFg4oR@NP z5WU%&u`QgheS!r2GP&@x&RX6l@;ab*DubbGq_4qhR z9!`kFyf;pMIU<0%?@b0VzmX{ z6`IqnNDQC<`mSC=K-GU0?tE++{h0fcbCu{>)VNiVIbCz>a#!8c_4JF4_^w1^toAcX zONvqL05fjw7xYU^S2n=A)#b{}TYg%yUd0QE`+duBT%X&a%GRAr8Sez%>Ra>H2|Yqa zX%j^1{M7way^^*G60<3oguXJD4*AX=;GO}DrbgO-%!)DW_cZgjr*2aFb#G=VF8RH6 zdKQ^o_(hB{8ii#VnY$6l4Yi!>=ZSk)&Yt{i6K=Atqc_{^sUL^Kdu+bW)ww24?}EI; zLCHl_8;m&~wYPD#7qs+We+ZkjSSI!;MsD6{&&{5G8n_q?Nh!I#Oo#QkWV+2oUaOqw zS=r$hF4P0&-#kMtIL|(kyPmh>|HUa>8nboc89}8BZdKf=EUuZWii0ioF7944v3HZ% zm)MTz`_hviMH6!^SZ5}xjti%;-oqfc_%nw2vgX2AbSTHGa?~i=Isvvm|N9AqT#;-! zIzRGch5J~Knka@rHGNImxv6-#5GnZ@eWfc3Jgn79bhnq4 zT+9|7%h|%#b$D7&hFSkm`#5ar=qJgn^|)nd&{C9See~d6mtVR-k6qylv}Nx;A={N- zVia)eOjud~<9ZK3mL2CRbSgxE+DlJI3pvYqXhplvYt~T*LJb@zn2& z0W!V7z3$)?+)JF8k!WOAL#{$F#iyF_-?2GM&!swSbOxe4wX?5yVT{EKJP@8EIii@E zS#g0_fx-LEPaoF^RDC36HQg$WFdd=`%m~a zvJAJLZ<_}M)z|?y%D%l~@-d(t6bULB{&D1o%?Wb6$mCCpS$gie{?R3%CX8l+stZ}N zaym;lSMvlwe7cBU)WM1qE4?nK)(T+h)p?N}7btT_iX0@;m!l&Klta5Ir5V{jO+

    c-FORp_J~q1K-NzK{^gnMl;~Fx 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 + + + + Gambas 3 executable + Exécutable Gambas 3 + + + + + + \ No newline at end of file diff --git a/main/reconf b/main/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/main/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/main/share/Makefile.am b/main/share/Makefile.am new file mode 100644 index 00000000..379c0ba7 --- /dev/null +++ b/main/share/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = *.h diff --git a/main/share/gambas.h b/main/share/gambas.h new file mode 100644 index 00000000..fd01062d --- /dev/null +++ b/main/share/gambas.h @@ -0,0 +1,1320 @@ +/*************************************************************************** + + gambas.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GAMBAS_H +#define __GAMBAS_H + +#ifdef __CYGWIN__ +#include +#endif + +#ifndef NO_CONFIG_H +#ifdef PACKAGE_NAME + #undef PACKAGE_NAME + #undef PACKAGE_BUGREPORT + #undef PACKAGE_STRING + #undef PACKAGE_TARNAME + #undef PACKAGE_VERSION + #undef PACKAGE_URL +#endif +#include "config.h" +#endif + +#include +#include +#include +#include + +/* Gambas API Version */ + +#define GB_VERSION 1 + + +/* Useful macros */ + +#ifndef CLEAR +#define CLEAR(s) (memset(s, 0, sizeof(*s))) +#endif + +#ifndef offsetof +#define offsetof(_type, _arg) ((size_t)&(((_type *)0)->_arg)) +#endif + +/* The following symbols must be declared with EXPORT in a component: + - GB + - GB_INIT() + - GB_EXIT() + - GB_CLASSES + - The component interface if present +*/ + +#ifdef EXPORT +#undef EXPORT +#endif +#ifdef HAVE_GCC_VISIBILITY +#define EXPORT __attribute__((visibility("default"))) +#else +#define EXPORT +#endif + +#if !defined(__cplusplus) + #ifdef bool + #undef bool + #endif + #define bool char +#endif + +#ifndef PACKED +#define PACKED __attribute__((packed)) +#endif + +/* Gambas datatypes identifiers */ + +#define GB_T_VOID 0 +#define GB_T_BOOLEAN 1 +#define GB_T_BYTE 2 +#define GB_T_SHORT 3 +#define GB_T_INTEGER 4 +#define GB_T_LONG 5 +#define GB_T_SINGLE 6 +#define GB_T_FLOAT 7 +#define GB_T_DATE 8 +#define GB_T_STRING 9 +#define GB_T_CSTRING 10 +#define GB_T_POINTER 11 +#define GB_T_VARIANT 12 +#define GB_T_FUNCTION 13 +#define GB_T_CLASS 14 +#define GB_T_NULL 15 +#define GB_T_OBJECT 16 + + +/* This type represents a Gambas datatype identifier */ + +typedef + uintptr_t GB_TYPE; + + +/* This opaque type represents a Gambas class identifier */ + +typedef + GB_TYPE GB_CLASS; + + +/* This structure represents the base of every Gambas object. + It must be placed in the beginning of all object structure defined + in a component. +*/ + +typedef + struct { + GB_CLASS klass; + intptr_t ref; + } + GB_BASE; + + +/* Gambas STRING datatype definition */ + +typedef + struct { + GB_TYPE type; + struct { + char *addr; + int start; + int len; + } value; + #if __WORDSIZE == 64 + intptr_t _reserved; + #endif + } + GB_STRING; + + +/* Gambas INTEGER datatype definition */ + +typedef + struct { + GB_TYPE type; + int value; + #if __WORDSIZE == 64 + int _pad; + #endif + intptr_t _reserved[2]; + } + GB_INTEGER; + + +/* Gambas LONG datatype definition */ + +typedef + struct { + GB_TYPE type; + #if __WORDSIZE == 32 + int _pad; + #endif + int64_t value; + #if __WORDSIZE == 64 + intptr_t _reserved[2]; + #endif + } + GB_LONG; + + +/* Gambas POINTER datatype definition */ + +typedef + struct { + GB_TYPE type; + intptr_t value; + intptr_t _reserved[2]; + } + GB_POINTER; + + +/* Gambas BOOLEAN datatype definition */ + +typedef + struct { + GB_TYPE type; + int value; + #if __WORDSIZE == 64 + int _pad; + #endif + intptr_t _reserved[2]; + } + GB_BOOLEAN; + + +/* Gambas SINGLE datatype definition */ + +typedef + struct { + GB_TYPE type; + float value; + #if __WORDSIZE == 64 + float _pad; + #endif + intptr_t _reserved[2]; + } + GB_SINGLE; + + +/* Gambas FLOAT datatype definition */ + +typedef + struct { + GB_TYPE type; + #if __WORDSIZE == 32 + int _pad; + #endif + double value; + #if __WORDSIZE == 64 + intptr_t _reserved[2]; + #endif + } + GB_FLOAT; + + +/* Gambas DATE datatype definition */ + +typedef + struct { + int date; + int time; + } + GB_DATE_VALUE; + +typedef + struct { + GB_TYPE type; + GB_DATE_VALUE value; + #if __WORDSIZE == 64 + intptr_t _reserved[2]; + #else + int _reserved; + #endif + } + GB_DATE; + + +/* Gambas OBJECT datatype definition */ + +typedef + struct { + GB_TYPE type; + void *value; + intptr_t _reserved[2]; + } + GB_OBJECT; + + +/* Gambas VARIANT datatype definition */ + +typedef + struct { + GB_TYPE type; + union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + GB_DATE_VALUE _date; + char *_string; + intptr_t _pointer; + void *_object; + int64_t data; + } + value; + } + PACKED + GB_VARIANT_VALUE; + +typedef + struct { + GB_TYPE type; + GB_VARIANT_VALUE value; + #if __WORDSIZE == 64 + int64_t _pad; + #endif + } + GB_VARIANT; + + +/* Gambas common value definition */ + +typedef + union { + GB_TYPE type; + GB_BOOLEAN _boolean; + GB_INTEGER _integer; + GB_LONG _long; + GB_SINGLE _single; + GB_FLOAT _float; + GB_DATE _date; + GB_STRING _string; + GB_POINTER _pointer; + GB_OBJECT _object; + GB_VARIANT _variant; + } + GB_VALUE; + + +/* Predefined errors constants */ + +#define GB_ERR_TYPE ((char *)6) +#define GB_ERR_OVERFLOW ((char *)7) +#define GB_ERR_NSYMBOL ((char *)11) +#define GB_ERR_NOBJECT ((char *)12) +#define GB_ERR_NWRITE ((char *)16) +#define GB_ERR_NPROPERTY ((char *)17) +#define GB_ERR_MATH ((char *)19) +#define GB_ERR_ARG ((char *)20) +#define GB_ERR_BOUND ((char *)21) +#define GB_ERR_ZERO ((char *)26) + + +/* Gambas description start macro */ + +#define GB_DECLARE(name, size) \ + { name, (intptr_t)GB_VERSION, (intptr_t)size } + + +/* Gambas description end macro */ + +#define GB_END_DECLARE { (char *)0 } + + +/* Special description identifiers */ + +#define GB_VIRTUAL_CLASS_ID ((char *)1) +#define GB_HOOK_CHECK_ID ((char *)2) +#define GB_NOT_CREATABLE_ID ((char *)3) +#define GB_AUTO_CREATABLE_ID ((char *)4) +#define GB_INHERITS_ID ((char *)5) + + +/* Description hook macros */ + +//#define GB_HOOK_NEW(hook) { GB_HOOK_NEW_ID, (int)hook } +//#define GB_HOOK_FREE(hook) { GB_HOOK_FREE_ID, (int)hook } +#define GB_HOOK_CHECK(hook) { GB_HOOK_CHECK_ID, (intptr_t)hook } + + +/* Virtual class description macro */ + +#define GB_VIRTUAL_CLASS() { GB_VIRTUAL_CLASS_ID }, { GB_NOT_CREATABLE_ID } + +#define GB_DECLARE_VIRTUAL(name) \ + { name, (intptr_t)GB_VERSION, (intptr_t)0 }, GB_VIRTUAL_CLASS() + + +/* Not creatable class macro */ + +#define GB_NOT_CREATABLE() { GB_NOT_CREATABLE_ID } + +#define GB_DECLARE_STATIC(name) \ + { name, (intptr_t)GB_VERSION, (intptr_t)0 }, GB_NOT_CREATABLE() + + +/* Auto creatable class macro */ + +#define GB_AUTO_CREATABLE() { GB_AUTO_CREATABLE_ID } + + +/* Symbol description prefixes */ + +#define GB_PROPERTY_ID 'p' +#define GB_METHOD_ID 'm' +#define GB_CONSTANT_ID 'C' +#define GB_EVENT_ID ':' +#define GB_ENUM_ID '#' +#define GB_STATIC_PROPERTY_ID 'P' +#define GB_STATIC_METHOD_ID 'M' + + +/* Symbol description macros */ + +#define GB_CONSTANT(symbol, type, value) \ + { "C" symbol, (intptr_t)type, (intptr_t)value } + +#define GB_FLOAT_CONSTANT(symbol, value) \ + { "C" symbol, (intptr_t)"f", (intptr_t)0, (intptr_t)0, (double)value } + +#define GB_PROPERTY(symbol, type, proc) \ + { "p" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_PROPERTY_READ(symbol, type, proc) \ + { "r" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_PROPERTY_SELF(symbol, type) \ + { "r" symbol, (intptr_t)type, (intptr_t)(-1) } + +#define GB_PROPERTY_WRITE(symbol, type, proc) \ + { "w" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_METHOD(symbol, type, exec, signature) \ + { "m" symbol, (intptr_t)type, (intptr_t)exec, (intptr_t)signature } + +#define GB_EVENT(symbol, type, signature, id) \ + { "::" symbol, (intptr_t)type, (intptr_t)id, (intptr_t)signature } + +#define GB_STATIC_PROPERTY(symbol, type, proc) \ + { "P" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_STATIC_PROPERTY_READ(symbol, type, proc) \ + { "R" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_STATIC_PROPERTY_SELF(symbol, type) \ + { "R" symbol, (intptr_t)type, (-1) } + +#define GB_STATIC_PROPERTY_WRITE(symbol, type, proc) \ + { "W" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_STATIC_METHOD(symbol, type, exec, signature) \ + { "M" symbol, (intptr_t)type, (intptr_t)exec, (intptr_t)signature } + +#define GB_STATIC_FAST_METHOD(symbol, type, exec, signature) \ + { "M!" symbol, (intptr_t)type, (intptr_t)exec, (intptr_t)signature } + +#define GB_INHERITS(symbol) \ + { GB_INHERITS_ID, (intptr_t)symbol } + +#define GB_INTERFACE(symbol, pointer) \ + { "C_@" symbol, (intptr_t)"p", (intptr_t)pointer } + + +/* Method implementation begin macro */ + +#define BEGIN_METHOD(_name, par) \ +typedef \ + struct { \ + par; \ + } \ + _##_name; \ +\ +void _name(void *_object, void *_param) \ +{ \ +_##_name *_p = (_##_name *)_param; + + +/* Parameter-less Method implementation begin macro */ + +#define BEGIN_METHOD_VOID(_name) \ +void _name(void *_object, void *_param) { \ + + +/* Parameter access macro */ + +#define ARG(_name) (&(_p)->_name) + + +/* Testing if a argument is missing */ + +#define MISSING(_name) ((_p)->_name.type == GB_T_VOID) + + +/* Method implementation end macro */ + +#define END_METHOD } + + +/* Macro used for calling a parameter-less implementation method */ + +#define CALL_METHOD_VOID(_name) _name(_object, NULL) + + +/* Property implementation begin macro */ + +#define BEGIN_PROPERTY(_name) \ +void _name(void *_object, void *_param) { + + +/* Macro indicating if the property implementation is called for reading or writing */ + +#define READ_PROPERTY (_param == NULL) + + +/* Macro to get the value written to a property */ + +#define PROP(_type) ((_type *)_param) + + +/* Property implementation end macro */ + +#define END_PROPERTY } + + +/* Macros to get the value of an argument or a property */ + +#define VALUE(_arg) ((_arg)->value) +#define VARG(_p) VALUE(ARG(_p)) +#define VPROP(_p) VALUE(PROP(_p)) + + +/* Macros to get a string argument */ + +#define STRING(_arg) (VARG(_arg).addr + VARG(_arg).start) +#define LENGTH(_arg) (VARG(_arg).len) + + +/* Macros to get a string property */ + +#define PSTRING() (VPROP(GB_STRING).addr + VPROP(GB_STRING).start) +#define PLENGTH() (VPROP(GB_STRING).len) + + +/* Macro to get an optional argument */ + +#define VARGOPT(_arg, _default) (MISSING(_arg) ? (_default) : VARG(_arg)) + + +/* Casting macro. Usable only in an implementation function */ + +#define OBJECT(type) ((type *)_object) + + +/* Macro for returning itself. Usable only in an implementation function */ + +#define RETURN_SELF() GB.ReturnSelf(_object) + + +/* Macro for declaring a variable used for storing an event identifier */ + +#define DECLARE_EVENT(_event) static int _event + + +/* Macro to help accessing enumeration index. Use only in an enumeration method implementation */ + +#define ENUM(_type) (*((_type *)GB.GetEnum())) + + +/* Structure used for describing a class */ + +typedef + struct { + const char *name; + intptr_t val1; + intptr_t val2; + intptr_t val3; + #if __WORDSIZE == 64 + double val4; + intptr_t val5; + #else + double val4; + #endif + } + GB_DESC; + + +/* Type of a method implementation function */ + +typedef + void GB_METHOD_FUNC(void *, void *); + + +/* Type of a property implementation function */ + +typedef + void GB_PROPERTY_FUNC(void *, void *); + + +/* Macro for declaring a method implementation function */ + +#define DECLARE_METHOD(_method) GB_METHOD_FUNC _method + + +/* Macro for declaring a property implementation function */ + +#define DECLARE_PROPERTY(_property) GB_PROPERTY_FUNC _property + + +/* Constants used with the GB.Hook() API function */ + +#define GB_HOOK_MAX 10 + +#define GB_HOOK_MAIN 1 +#define GB_HOOK_LOOP 2 +#define GB_HOOK_WAIT 3 +#define GB_HOOK_TIMER 4 +#define GB_HOOK_LANG 5 +#define GB_HOOK_WATCH 6 +#define GB_HOOK_POST 7 +#define GB_HOOK_QUIT 8 +#define GB_HOOK_ERROR 9 +#define GB_HOOK_TIMEOUT 10 + +/* Macro for calling the previous hook */ + +#define CALL_HOOK_MAIN(_hook, _pargc, _pargv) do { if (_hook) { ((void (*)(int *, char ***))(_hook))((_pargc), (_pargv)); } } while (0); + +/* Constants that represent interpreter signals caught by GB_SIGNAL function */ + +#define GB_SIGNAL_DEBUG_BREAK 1 +#define GB_SIGNAL_DEBUG_CONTINUE 2 +#define GB_SIGNAL_DEBUG_FORWARD 3 + + +/* Constants used with the GB.Watch() API function */ + +#define GB_WATCH_NONE 0 +#define GB_WATCH_READ 1 +#define GB_WATCH_WRITE 2 + + +/* Type of a generic callback */ + +typedef + void (*GB_CALLBACK)(); + + +/* Type of a watch callback function */ + +typedef + void (*GB_WATCH_CALLBACK)(int, int, intptr_t); + + +/* Type of the GB.SubstString() callback */ + +typedef + void (*GB_SUBST_CALLBACK)(int, char **, int *); + + +/* Type of the GB.SubstStringAdd() callback */ + +typedef + void (*GB_SUBST_ADD_CALLBACK)(int, char, char); + + +/* Type of the GB.BrowseProject() callback */ + +typedef + void (*GB_BROWSE_PROJECT_CALLBACK)(const char *, int64_t); + + +/* Type of the GB.BrowseDirectory() callback */ + +typedef + void (*GB_BROWSE_CALLBACK)(const char *); + + +/* Type of a timer callback */ + +typedef + int (*GB_TIMER_CALLBACK)(intptr_t); + + +/* Type of a signal callback */ + +typedef + void (*GB_SIGNAL_CALLBACK)(int, intptr_t); + + +/* A structure for the components of a date */ + +typedef + struct { + int year; + int month; + int day; + int hour; + int min; + int sec; + int weekday; + int msec; + } + GB_DATE_SERIAL; + + +/* Opaque type of a Gambas interpreted or native function */ + +typedef + struct { + void *object; + ushort index; + } + GB_FUNCTION; + +#define GB_FUNCTION_IS_VALID(_func) ((_func)->index != 0) + + +/* Opaque type of a Gambas Array */ + +typedef + void *GB_ARRAY; + +typedef + struct { + unsigned size : 24; + unsigned read_only : 1; + unsigned n_dim : 3; + int count; + GB_TYPE type; + void *data; + int *dim; + void *ref; + + } + GB_ARRAY_BASE; + + +/* Opaque type of a Gambas Collection */ + +typedef + void *GB_COLLECTION; + + +/* Opaque type of a Gambas Collection iterator */ + +typedef + struct { + void *iter1; + void *iter2; + } + GB_COLLECTION_ITER; + + +/* opaque type of an hash table */ + +typedef + void *GB_HASHTABLE; + + +/* hash table enumeration function */ + +typedef + void (*GB_HASHTABLE_ENUM_FUNC)(void *); + + +/* Constants for end-of-line format */ + +#define GB_EOL_UNIX 0 +#define GB_EOL_WINDOWS 1 +#define GB_EOL_MAC 2 + + +/* opaque type for a Stream object */ + +struct GB_STREAM; + +typedef + struct { + int (*open)(struct GB_STREAM *stream, const char *path, int mode, void *data); + int (*close)(struct GB_STREAM *stream); + int (*read)(struct GB_STREAM *stream, char *buffer, int len); + int (*write)(struct GB_STREAM *stream, char *buffer, int len); + int (*seek)(struct GB_STREAM *stream, int64_t pos, int whence); + int (*tell)(struct GB_STREAM *stream, int64_t *pos); + int (*flush)(struct GB_STREAM *stream); + int (*eof)(struct GB_STREAM *stream); + int (*lof)(struct GB_STREAM *stream, int64_t *len); + int (*handle)(struct GB_STREAM *stream); + } + GB_STREAM_DESC; + +typedef + struct { + GB_STREAM_DESC *desc; + int _reserved; + #if __WORDSIZE == 64 + int _reserved1; + #endif + intptr_t _reserved2; + void *tag; + } + GB_STREAM_BASE; + +typedef + struct GB_STREAM { + GB_STREAM_DESC *desc; + int _reserved; + #if __WORDSIZE == 64 + int _reserved1; + #endif + intptr_t _reserved2; + void *tag; + #if __WORDSIZE == 64 + int _free[4]; + #else + int _free[5]; + #endif + GB_VARIANT_VALUE _reserved3; + } + GB_STREAM; + + +/* File constants */ + +#define GB_ST_READ 1 +#define GB_ST_WRITE 2 +#define GB_ST_READ_WRITE 3 +#define GB_ST_MODE 3 +#define GB_ST_EXEC 4 +#define GB_ST_APPEND 4 +#define GB_ST_CREATE 8 +#define GB_ST_ACCESS 15 +#define GB_ST_BUFFERED 16 +#define GB_ST_LOCK 32 +#define GB_ST_WATCH 64 +#define GB_ST_PIPE 128 +#define GB_ST_MEMORY 256 +#define GB_ST_STRING 512 +#define GB_ST_NULL 1024 + + +/* Constants used by the GB.NumberFromString() API function */ + +#define GB_NB_READ_INTEGER 1 +#define GB_NB_READ_LONG 2 +#define GB_NB_READ_INT_LONG 3 +#define GB_NB_READ_FLOAT 4 +#define GB_NB_READ_ALL 7 +#define GB_NB_READ_HEX_BIN 8 +#define GB_NB_LOCAL 16 + + +/* Comparison constants. Used by the GB.Collection.New() and GB.HashTable.New() API function */ + +#define GB_COMP_BINARY 0 +#define GB_COMP_NOCASE 1 +#define GB_COMP_LANG 2 +#define GB_COMP_LIKE 4 +#define GB_COMP_MATCH 5 +#define GB_COMP_NATURAL 8 + +#define GB_COMP_TYPE_MASK 15 + +#define GB_COMP_ASCENT 0 +#define GB_COMP_DESCENT 16 + + +/* Constant used by GB.ConvString to convert to 32 bits Unicode (it needs some special processing) */ + +#define GB_SC_UNICODE ((char *)-1) + + +/* Timer object */ + +typedef + struct { + GB_BASE object; + intptr_t id; + void *ext; + unsigned delay : 31; + unsigned triggered : 1; + unsigned task : 1; + unsigned ignore : 1; + } + GB_TIMER; + +/* Structure for GB.OnErrorBegin() handler */ + +typedef + struct { + void *prev; + void *context; + GB_CALLBACK handler; + intptr_t arg1; + intptr_t arg2; + } + GB_ERROR_HANDLER; + +/* Structure for GB.RaiseBegin handler */ + +typedef + struct { + void (*callback)(intptr_t); + intptr_t data; + void *old; + int level; + } + GB_RAISE_HANDLER; + +/* A macro for preventing gcc from warning about breaks in the + strict aliasing rules */ + +#define POINTER(_pointer) (void **)(void *)_pointer + +/* For classes that implements arithmetic operators (e.g. complex numbers...) */ + +typedef + struct { + int (*equal)(void *, void *, bool); + int (*equalf)(void *, double); + int (*equalo)(void *, void *, bool); + int (*comp)(void *, void *, bool); + int (*compf)(void *, double); + int (*compo)(void *, void *, bool); + void *(*add)(void *, void *, bool); + void *(*addf)(void *, double, bool); + void *(*addo)(void *, void *, bool); + void *(*sub)(void *, void *, bool); + void *(*subf)(void *, double, bool); + void *(*subo)(void *, void *, bool); + void *(*mul)(void *, void *, bool); + void *(*mulf)(void *, double, bool); + void *(*mulo)(void *, void *, bool); + void *(*div)(void *, void *, bool); + void *(*divf)(void *, double, bool); + void *(*divo)(void *, void *, bool); + void *(*pow)(void *, void *, bool); + void *(*powf)(void *, double, bool); + void *(*powo)(void *, void *, bool); + void *(*neg)(void *); + void *(*abs)(void *); + double (*fabs)(void *); + int (*sgn)(void *); + intptr_t _reserved; + } + GB_OPERATOR_DESC; + +/* Double-linked list API */ + +typedef + struct { + void *next; + void *prev; + } + GB_LIST; + +/* Information about a file */ + +typedef + struct { + short type; + short mode; + int atime; + int mtime; + int ctime; + int64_t size; + uid_t uid; + gid_t gid; + dev_t device; + unsigned hidden : 1; + unsigned blkdev : 1; + unsigned chrdev : 1; + } + GB_FILE_STAT; + +/* Constants for the GB_FILE_STAT structure */ + +#define GB_STAT_FILE 1 +#define GB_STAT_DIRECTORY 2 +#define GB_STAT_DEVICE 3 +#define GB_STAT_PIPE 4 +#define GB_STAT_SOCKET 5 +#define GB_STAT_LINK 6 + +/* Gambas Application Programming Interface */ + +typedef + struct { + intptr_t version; + + bool (*GetInterface)(const char *, int, void *); + + void *(*Hook)(int, void *); + + struct { + bool (*Load)(const char *); + bool (*CanLoadLibrary)(const char *); + bool (*Exist)(const char *); + bool (*IsLoaded)(const char *); + char *(*Current)(void); + bool (*GetInfo)(const char *, void **); + void (*Signal)(int, void *); + void (*Declare)(GB_DESC *); + } + Component; + + void (*Push)(int, ...); + bool (*GetFunction)(GB_FUNCTION *, void *, const char *, const char *, const char *); + GB_VALUE *(*Call)(GB_FUNCTION *, int, int); + void *(*GetClassInterface)(GB_CLASS, const char *); + GB_VALUE *(*GetProperty)(void *, const char *); + bool (*SetProperty)(void *, const char *, GB_VALUE *value); + bool (*Serialize)(const char *path, GB_VALUE *value); + bool (*UnSerialize)(const char *path, GB_VALUE *value); + + bool (*Loop)(int); + void (*Wait)(int); + void (*Post)(GB_CALLBACK, intptr_t); + void (*Post2)(GB_CALLBACK, intptr_t, intptr_t); + GB_TIMER *(*Every)(int, GB_TIMER_CALLBACK, intptr_t); + bool (*Raise)(void *, int, int, ...); + void (*RaiseBegin)(GB_RAISE_HANDLER *handler); + void (*RaiseEnd)(GB_RAISE_HANDLER *handler); + void (*RaiseLater)(void *, int); + void (*CheckPost)(void); + bool (*CanRaise)(void *, int); + int (*GetEvent)(GB_CLASS, const char *); + char *(*GetLastEventName)(void); + void (*RaiseTimer)(void *); + bool (*HasActiveTimer)(void); + bool (*Stopped)(void); + bool (*IsRaiseLocked)(void *); + + int (*NParam)(void); + bool (*Conv)(GB_VALUE *, GB_TYPE); + char *(*GetUnknown)(void); + + void (*Error)(const char *, ...); + bool (*HasError)(void); + char *(*GetErrorMessage)(void); + void (*Propagate)(void); + void (*Deprecated)(const char *, const char *, const char *); + void (*OnErrorBegin)(GB_ERROR_HANDLER *); + void (*OnErrorEnd)(GB_ERROR_HANDLER *); + + GB_CLASS (*GetClass)(void *); + char *(*GetClassName)(void *); + bool (*ExistClass)(const char *); + GB_CLASS (*FindClass)(const char *); + bool (*ExistClassLocal)(const char *); + GB_CLASS (*FindClassLocal)(const char *); + GB_TYPE (*GetArrayType)(GB_CLASS); + GB_CLASS (*GetArrayClass)(GB_CLASS); + GB_CLASS (*LoadClassFrom)(const char *, const char*); + bool (*Is)(void *, GB_CLASS); + void (*Ref)(void *); + void (*Unref)(void **); + //void (*UnrefKeep)(void **, int); + void (*Detach)(void *); + void (*Attach)(void *, void *, const char *); + void *(*Parent)(void *); + void *(*Create)(GB_CLASS, char *, void *); + void *(*New)(GB_CLASS, char *, void *); + void *(*AutoCreate)(GB_CLASS, int); + bool (*CheckObject)(void *); + bool (*IsLocked)(void *); + + void *(*GetEnum)(void); + void (*StopEnum)(void); + void *(*BeginEnum)(void *); + void (*EndEnum)(void *); + bool (*NextEnum)(void); + void (*StopAllEnum)(void *); + void (*OnFreeEnum)(GB_CALLBACK); + + GB_VALUE *(*GetReturnValue)(void); + void (*Return)(GB_TYPE, ...); + void (*ReturnInteger)(int); + void (*ReturnLong)(int64_t); + void (*ReturnPointer)(void *); + void (*ReturnBoolean)(int); + void (*ReturnDate)(GB_DATE *); + void (*ReturnObject)(void *); + void (*ReturnNull)(void); + void (*ReturnSingle)(float); + void (*ReturnFloat)(double); + void (*ReturnVariant)(GB_VARIANT_VALUE *); + void (*ReturnConvVariant)(void); + void (*ReturnBorrow)(void); + void (*ReturnRelease)(void); + void (*ReturnPtr)(GB_TYPE, void *); + void (*ReturnSelf)(void *); + + void (*ReturnString)(char *); + void (*ReturnVoidString)(void); + void (*ReturnConstString)(const char *, int); + void (*ReturnConstZeroString)(const char *); + void (*ReturnNewString)(const char *, int); + void (*ReturnNewZeroString)(const char *); + + char *(*NewString)(const char *, int); + char *(*NewZeroString)(const char *); + char *(*TempString)(const char *, int); + char *(*RefString)(char *); + void (*FreeString)(char **); + char *(*FreeStringLater)(char *); + char *(*ExtendString)(char *, int); + char *(*AddString)(char *, const char *, int); + char *(*AddChar)(char *, char); + int (*StringLength)(char *); + char *(*ToZeroString)(GB_STRING *); + bool (*MatchString)(const char *, int, const char *, int); + bool (*NumberFromString)(int, const char *, int, GB_VALUE *); + bool (*NumberToString)(int, double, const char *, char **, int *); + char *(*Translate)(const char *); + + char *(*SubstString)(const char *, int, GB_SUBST_CALLBACK); + char *(*SubstStringAdd)(const char *, int, GB_SUBST_ADD_CALLBACK); + void (*SubstStringUnquote)(void); + void (*SubstAddCallback)(const char *, int); + bool (*ConvString)(char **, const char *, int, const char *, const char *); + char *(*FileName)(char *, int); + char *(*RealFileName)(char *, int); + + bool (*LoadFile)(const char *, int, char **, int *); + void (*ReleaseFile)(char *, int); + char *(*TempDir)(void); + char *(*TempFile)(const char *); + bool (*CopyFile)(const char *, const char *); + void (*BrowseProject)(GB_BROWSE_PROJECT_CALLBACK); + void (*BrowseDirectory)(const char *, GB_BROWSE_CALLBACK, GB_BROWSE_CALLBACK); + bool (*StatFile)(const char *, GB_FILE_STAT *, bool); + + void (*Store)(GB_TYPE, GB_VALUE *, void *); + void (*StoreString)(GB_STRING *, char **); + void (*StoreObject)(GB_OBJECT *, void **); + void (*StoreVariant)(GB_VARIANT *, void *); + void (*ReadValue)(GB_VALUE *, void *, GB_TYPE); + void (*BorrowValue)(GB_VALUE *); + void (*ReleaseValue)(GB_VALUE *); + int (*CompVariant)(GB_VARIANT_VALUE *, GB_VARIANT_VALUE *); + + GB_DATE_SERIAL *(*SplitDate)(GB_DATE *); + bool (*MakeDate)(GB_DATE_SERIAL *, GB_DATE *); + void (*MakeDateFromTime)(time_t, int, GB_DATE *); + bool (*GetTime)(double *, int); + bool (*DateFromString)(const char *str, int len, GB_VALUE *val, bool local); + + void (*Watch)(int, int, void *, intptr_t); + + GB_VALUE *(*Eval)(void *, void *); + + void (*Alloc)(void **, int); + void (*AllocZero)(void **, int); + void (*Free)(void **); + void (*Realloc)(void **, int); + + void (*NewArray)(void *, int, int); + void (*FreeArray)(void *); + int (*Count)(void *); + void *(*Add)(void *); + void *(*Insert)(void *, int, int); + void (*Remove)(void *, int, int); + + int (*ToLower)(int); + int (*ToUpper)(int); + int (*StrCaseCmp)(const char *, const char *); + int (*StrNCaseCmp)(const char *, const char *, int); + + struct { + char *(*Name)(void); + char *(*Title)(void); + char *(*Version)(void); + char *(*Path)(void); + GB_CLASS (*StartupClass)(void); + } + Application; + + struct { + char *(*Charset)(void); + char *(*Language)(void); + void (*SetLanguage)(const char *); + char *(*DomainName)(void); + bool (*IsRightToLeft)(void); + char *(*Path)(void); + void (*HasForked)(void); + bool (*Debug)(void); + char *(*Home)(void); + int (*TimeZone)(void); + } + System; + + struct { + void (*New)(GB_ARRAY *, GB_TYPE, int); + int (*Count)(GB_ARRAY); + void *(*Add)(GB_ARRAY); + void *(*Get)(GB_ARRAY, int); + GB_TYPE (*Type)(GB_ARRAY); + void (*SetReadOnly)(GB_ARRAY); + } + Array; + + struct { + void (*New)(GB_COLLECTION *, int); + int (*Count)(GB_COLLECTION); + bool (*Set)(GB_COLLECTION, const char *, int, GB_VARIANT *); + bool (*Get)(GB_COLLECTION, const char *, int, GB_VARIANT *); + bool (*Enum)(GB_COLLECTION, GB_COLLECTION_ITER *, GB_VARIANT *, char **key, int *len); + } + Collection; + + struct { + void (*New)(GB_HASHTABLE *, int); + void (*Free)(GB_HASHTABLE *); + int (*Count)(GB_HASHTABLE); + void (*Add)(GB_HASHTABLE, const char *, int, void *); + void (*Remove)(GB_HASHTABLE, const char *, int); + bool (*Get)(GB_HASHTABLE, const char *, int, void **); + void (*Enum)(GB_HASHTABLE, GB_HASHTABLE_ENUM_FUNC); + bool (*First)(GB_HASHTABLE, void **); + } + HashTable; + + struct { + GB_STREAM *(*Get)(void *object); + void (*SetSwapping)(GB_STREAM *stream, int swap); + void (*SetAvailableNow)(GB_STREAM *stream, int available_now); + bool (*Block)(GB_STREAM *stream, int block); + int (*Read)(GB_STREAM *stream, void *addr, int len); + int (*Write)(GB_STREAM *stream, void *addr, int len); + int (*GetReadable)(GB_STREAM *stream, int *len); + bool (*Eof)(GB_STREAM *stream); + int (*Handle)(GB_STREAM *stream); + } + Stream; + + struct { + void (*Start)(int length); + char *(*End)(void); + void (*Add)(const char *src, int len); + GB_ARRAY (*Split)(const char *str, int lstr, const char *sep, int lsep, const char *esc, int lesc, bool no_void, bool keep_esc); + } + String; + + struct { + char *(*GetCurrentPosition)(void); + void (*EnterEventLoop)(void); + void (*LeaveEventLoop)(void); + } + Debug; + + struct { + GB_SIGNAL_CALLBACK *(*Register)(int signum, void (*func)(int, intptr_t), intptr_t data); + void (*Unregister)(int signum, GB_SIGNAL_CALLBACK *cb); + void (*MustCheck)(int signum); + } + Signal; + + struct { + void (*Add)(void *p_first, void *node, GB_LIST *list); + void (*Remove)(void *p_first, void *node, GB_LIST *list); + } + List; + + } + GB_INTERFACE; + + +/* + + Special methods that can be declared in a class + ----------------------------------------------- + + _get array reading operator + _put array writing operator + _new constructor + _free destructor + _next next iteration of an enumeration + _call called when the object or the class is used as a function + _unknown called when the name of the property or method is unknown + + + Syntax of a method or event signature + ------------------------------------- + + Gambas datatype String representation + + Boolean b + Byte c + Short h + Integer i + Long l + Single g + Float f + Date d + String s + Variant v + Object o + Pointer p + Any class ClassName; + +*/ + +#ifndef NO_GAMBAS_CASE_REPLACEMENT + +/* Replacements for case unsensitive comparisons. + They ensure that case comparison does not use current locale, + otherwise Turkish speakers will have problems! +*/ + +#include +#include + +#ifdef tolower +#undef tolower +#endif +#ifdef toupper +#undef toupper +#endif +#ifdef strcasecmp +#undef strcasecmp +#endif +#ifdef strncasecmp +#undef strncasecmp +#endif + +#define strcasecmp GB.StrCaseCmp +#define strncasecmp GB.StrNCaseCmp +#define toupper GB.ToUpper +#define tolower GB.ToLower + +#endif + +#endif diff --git a/main/share/gb_alloc.h b/main/share/gb_alloc.h new file mode 100644 index 00000000..4b4a19b9 --- /dev/null +++ b/main/share/gb_alloc.h @@ -0,0 +1,117 @@ +/*************************************************************************** + + gb_alloc.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ALLOC_H +#define __GB_ALLOC_H + +#include "gb_common.h" + +#define DEBUG_MEMORY 0 +#define OPTIMIZE_MEMORY 1 +//#define DO_NOT_PRINT_MEMORY + +#ifndef __GB_ALLOC_C +EXTERN int MEMORY_count; +#endif + +#define WHERE_AM_I MEMORY_where_am_i(__FILE__, __LINE__, __func__) + +#if DEBUG_MEMORY + +#undef OPTIMIZE_MEMORY +#define OPTIMIZE_MEMORY 0 + +typedef + struct ALLOC { + int _void; + struct ALLOC *next; + struct ALLOC *prev; + int id; + size_t size; + } + PACKED + ALLOC; + +EXTERN size_t MEMORY_size; +EXTERN FILE *MEMORY_log; + +#define ALLOC(_ptr, _size) MEMORY_alloc((void *)_ptr, _size, WHERE_AM_I) +#define ALLOC_ZERO(_ptr, _size) MEMORY_alloc_zero((void *)_ptr, _size, WHERE_AM_I) +#define REALLOC(_ptr, _size) MEMORY_realloc((void *)_ptr, _size, WHERE_AM_I) +#define FREE(_ptr) MEMORY_free((void *)_ptr, WHERE_AM_I) +#define IFREE(_ptr) FREE(&(_ptr)) + +#define GET_ALLOC_ID(_ptr) (((ALLOC *)((char *)(_ptr) - sizeof(ALLOC)))->id) + +char *MEMORY_where_am_i(const char *file, int line, const char *func); + +void MEMORY_alloc(void *p_ptr, size_t size, const char *src); +void MEMORY_alloc_zero(void *p_ptr, size_t size, const char *src); +void MEMORY_realloc(void *p_ptr, size_t size, const char *src); +void MEMORY_free(void *p_ptr, const char *src); +void MEMORY_check(void); + +void MEMORY_verify(void); +void MEMORY_check_ptr(void *ptr); + +#elif OPTIMIZE_MEMORY + +/*#define ALLOC(_ptr, _size, _src) (LIKELY((*(_ptr) = malloc(_size)) != 0) ? MEMORY_count++ : THROW_MEMORY()) +#define ALLOC_ZERO(_ptr, _size, _src) (LIKELY((*(_ptr) = calloc(_size, 1)) != 0) ? MEMORY_count++ : THROW_MEMORY()) +#define REALLOC(_ptr, _size, _src) (LIKELY((*(_ptr) = realloc(*(_ptr), _size)) != 0) ? 0 : THROW_MEMORY()) +#define FREE(_ptr, _src) (LIKELY(*(_ptr) != 0) ? free(*(_ptr)), *(_ptr) = NULL, MEMORY_count-- : 0) +#define IFREE(_ptr, _src) (LIKELY(_ptr != 0) ? free(_ptr), MEMORY_count-- : 0)*/ + +#define ALLOC(_ptr, _size) (*(_ptr) = my_malloc(_size)) +#define ALLOC_ZERO(_ptr, _size) (*(_ptr) = my_malloc(_size), memset(*(_ptr), 0, (_size))) +#define REALLOC(_ptr, _size) (*(_ptr) = my_realloc(*(_ptr), (_size))) +#define FREE(_ptr) (my_free(*(_ptr)), *(_ptr) = NULL) +#define IFREE(_ptr) (my_free(_ptr)) + +void *my_malloc(size_t len); +void my_free(void *alloc); +void *my_realloc(void *alloc, size_t len); + +#else + +#define ALLOC(_ptr, _size) MEMORY_alloc((void *)_ptr, _size) +#define ALLOC_ZERO(_ptr, _size) MEMORY_alloc_zero((void *)_ptr, _size) +#define REALLOC(_ptr, _size) MEMORY_realloc((void *)_ptr, _size) +#define FREE(_ptr) MEMORY_free((void *)_ptr) +#define IFREE(_ptr) FREE(&(_ptr)) + +void MEMORY_alloc(void *p_ptr, size_t size); +void MEMORY_alloc_zero(void *p_ptr, size_t size); +void MEMORY_realloc(void *p_ptr, size_t size); +void MEMORY_free(void *p_ptr); +void MEMORY_check(void); + +#endif + +void MEMORY_init(void); +void MEMORY_exit(void); +void MEMORY_clear_cache(void); +int THROW_MEMORY() NORETURN; + +#endif + diff --git a/main/share/gb_alloc_temp.h b/main/share/gb_alloc_temp.h new file mode 100644 index 00000000..44c76fda --- /dev/null +++ b/main/share/gb_alloc_temp.h @@ -0,0 +1,495 @@ +/*************************************************************************** + + gb_alloc_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ALLOC_C + +#include + +#ifdef HAVE_MALLOC_H +#include +#endif + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" + +int MEMORY_count = 0; + +//#define DEBUG_ME +//#define DO_NOT_PRINT_MEMORY + +#if defined(DEBUG_ME) || DEBUG_MEMORY +size_t MEMORY_size = 0; +size_t MEMORY_pool_size = 0; +#endif + +#if DEBUG_MEMORY + +static int _id = 0; +ALLOC *_alloc = NULL; +#ifdef PROJECT_EXEC +extern char *DEBUG_get_current_position(void); +#endif +FILE *MEMORY_log; + +#elif OPTIMIZE_MEMORY + +#define SIZE_INC 16 +#define SIZE_SHIFT 4 +#define REAL_SIZE(_size) (((_size) + (SIZE_INC - 1)) & ~(SIZE_INC - 1)) + +#define POOL_SIZE 16 +#define POOL_MAX 128 + +#define POOL_MAX_LEN (POOL_SIZE * SIZE_INC) + +static size_t *_pool[POOL_SIZE] = { 0 }; +static int _pool_count[POOL_SIZE] = { 0 }; +/*static size_t *_first_alloc = 0; +static size_t *_max_alloc = 0;*/ + +#endif + +int THROW_MEMORY() +{ + THROW(E_MEMORY); +} + +void MEMORY_init(void) +{ + #if DEBUG_MEMORY + /*char path[256]; + sprintf(path, "/tmp/gambas-memory-%d-%d.log", getuid(), getpid()); + MEMORY_log = fopen(path, "w+");*/ + MEMORY_log = stderr; + #endif +} + +void MEMORY_clear_cache() +{ +#if OPTIMIZE_MEMORY + int i; + void *ptr, *next; + size_t size = 0; + + for (i = 0; i < POOL_SIZE; i++) + { + ptr = _pool[i]; + while (ptr) + { + next = *((void **)ptr); + size += (i + 1) * SIZE_INC; + #ifdef DEBUG_ME + MEMORY_size -= (i + 1) * SIZE_INC; + #endif + free(ptr); + ptr = next; + } + _pool[i] = NULL; + _pool_count[i] = 0; + } + + #ifdef DEBUG_ME + fprintf(stderr, "free %ld bytes [%ld / %p]\n", size, MEMORY_size, sbrk(0)); + #endif + +#endif +} + +void MEMORY_exit(void) +{ + MEMORY_clear_cache(); + +#if DEBUG_MEMORY + if (MEMORY_count) + { + fprintf(MEMORY_log, "\n*************************************************\n"); + fprintf(MEMORY_log, "warning: %d allocation(s) non freed.\n", MEMORY_count); + while (_alloc) + { + fprintf(MEMORY_log, "<%d>\n", _alloc->id); + _alloc = _alloc->next; + } + } + fclose(MEMORY_log); +#else + if (MEMORY_count) + ERROR_warning("%d allocation(s) non freed.", MEMORY_count); +#endif +} + +#if OPTIMIZE_MEMORY +#else + +#if DEBUG_MEMORY +char *MEMORY_where_am_i(const char *file, int line, const char *func) +{ + static char buffer[256]; + + snprintf(buffer, sizeof(buffer), "[%s] %s:%d", file, func, line); + return buffer; +} + +#endif + +#if DEBUG_MEMORY +void MEMORY_alloc(void *p_ptr, size_t size, const char *src) +{ + ALLOC *alloc; + + alloc = (ALLOC *)malloc(sizeof(ALLOC) + size); + if (!alloc) + THROW(E_MEMORY); + + _id++; + alloc->id = _id; + alloc->prev = NULL; + alloc->next = _alloc; + alloc->size = size; + + if (_alloc) + _alloc->prev = alloc; + + _alloc = alloc; + + *((void **)p_ptr) = (char *)alloc + sizeof(ALLOC); + MEMORY_count++; + MEMORY_size += size; + + #ifndef DO_NOT_PRINT_MEMORY + #ifdef PROJECT_EXEC + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); + #endif + fprintf(MEMORY_log, "<%d> %s: MEMORY_alloc(%d) -> %p\n", _id, src, (int)size, (char *)alloc + sizeof(ALLOC)); + fflush(MEMORY_log); + /*if (_id == 2700) + BREAKPOINT();*/ + #endif +} +#else +void MEMORY_alloc(void *p_ptr, size_t size) +{ + void *alloc; + + alloc = malloc(size); + + if (!alloc) + THROW(E_MEMORY); + + *((void **)p_ptr) = alloc; + MEMORY_count++; +} +#endif + + +#if DEBUG_MEMORY +void MEMORY_alloc_zero(void *p_ptr, size_t size, const char *src) +{ + MEMORY_alloc(p_ptr, size, src); + memset(*((void **)p_ptr), 0, size); +} +#else +void MEMORY_alloc_zero(void *p_ptr, size_t size) +{ + void *alloc; + + alloc = calloc(size, 1); + + if (!alloc) + THROW(E_MEMORY); + + *((void **)p_ptr) = alloc; + MEMORY_count++; +} +#endif + + +#if DEBUG_MEMORY +void MEMORY_realloc(void *p_ptr, size_t size, const char *src) +{ + ALLOC *alloc = (ALLOC *)(*((char **)p_ptr) - sizeof(ALLOC)); + ALLOC *old = alloc; + + if (size == 0) + { + MEMORY_free(p_ptr, src); + return; + } + + alloc = realloc(alloc, sizeof(ALLOC) + size); + + if (!alloc) + THROW(E_MEMORY); + + MEMORY_size += size - alloc->size; + + if (_alloc == old) + _alloc = alloc; + + if (alloc->prev) + alloc->prev->next = alloc; + if (alloc->next) + alloc->next->prev = alloc; + + #ifndef DO_NOT_PRINT_MEMORY + #ifdef PROJECT_EXEC + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); + #endif + fprintf(MEMORY_log, "<%d> %s: MEMORY_realloc(%p, %d) -> %p\n", alloc->id, src, *((void **)p_ptr), (int)size, (char *)alloc + sizeof(ALLOC)); + fflush(MEMORY_log); + #endif + + *((void **)p_ptr) = (char *)alloc + sizeof(ALLOC); +} +#else +void MEMORY_realloc(void *p_ptr, size_t size) +{ + void *alloc = *((void **)p_ptr); + + if (size == 0) + { + MEMORY_free(p_ptr); + return; + } + + alloc = realloc(alloc, size); + + if (!alloc) + THROW(E_MEMORY); + + *((void **)p_ptr) = alloc; +} +#endif + + +#if DEBUG_MEMORY +void MEMORY_free(void *p_ptr, const char *src) +{ + ALLOC *alloc = (ALLOC *)(*((char **)p_ptr) - sizeof(ALLOC)); + + if (!(*((void **)p_ptr))) + return; + + if (alloc->prev) + alloc->prev->next = alloc->next; + if (alloc->next) + alloc->next->prev = alloc->prev; + + if (alloc == _alloc) + _alloc = alloc->next; + + #ifndef DO_NOT_PRINT_MEMORY + #ifdef PROJECT_EXEC + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); + #endif + fprintf(MEMORY_log, "<%d> %s: MEMORY_free(%p) / %p\n", alloc->id, src, *((void **)p_ptr), (char *)alloc + sizeof(ALLOC)); + fflush(MEMORY_log); + #endif + + MEMORY_size -= alloc->size; + + //*((long *)alloc) = 0x31415926; + free(alloc); + + *((void **)p_ptr) = NULL; + MEMORY_count--; +} +#else +void MEMORY_free(void *p_ptr) +{ + void *alloc = *((void **)p_ptr); + + if (!alloc) + return; + + free(alloc); + + *((void **)p_ptr) = NULL; + MEMORY_count--; +} +#endif + +#endif // OPTIMIZE_MEMORY + +#if OPTIMIZE_MEMORY + +//void DEBUG_print_current_backtrace(void); +//static bool _print_backtrace = FALSE; + +#define get_pool_index(_size) ((int)(_size >= POOL_MAX_LEN ? POOL_SIZE : ((_size / SIZE_INC) - 1))) + +void *my_malloc(size_t len) +{ + size_t *ptr; + size_t size = REAL_SIZE(len + sizeof(size_t)); + int pool = get_pool_index(size); + + MEMORY_count++; + + if (pool < POOL_SIZE) + { + if (_pool_count[pool]) + { + ptr = _pool[pool]; + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + MEMORY_pool_size -= size; + fprintf(stderr, "my_malloc: %d bytes from pool #%d -> %p (%p) [%ld / %ld]\n", size, pool, ptr + 1, sbrk(0), MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + _pool[pool] = *((void **)ptr); + _pool_count[pool]--; + *ptr++ = size; + return ptr; + } + } + + #ifdef DEBUG_ME + MEMORY_size += size; + #endif + ptr = malloc(size); + if (!ptr) + THROW_MEMORY(); + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + fprintf(stderr, "my_malloc: %d bytes from malloc -> %p (%p) [%ld / %ld]\n", size, ptr + 1, sbrk(0), MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + + /*if (!_first_alloc) + _first_alloc = ptr; + if (ptr > _max_alloc && !_print_backtrace) + { + _max_alloc = ptr; + fprintf(stderr, "%ld\n", (char *)_max_alloc - (char *)_first_alloc + size); + _print_backtrace = TRUE; + DEBUG_print_current_backtrace(); + _print_backtrace = FALSE; + }*/ + + *ptr++ = size; + return ptr; +} + +void my_free(void *alloc) +{ + size_t *ptr; + size_t size; + int pool; + + if (!alloc) + return; + + MEMORY_count--; + + ptr = alloc; + ptr--; + + size = *ptr; + pool = get_pool_index(size); + + //if (ptr < (_first_alloc + (_max_alloc - _first_alloc) / 2)) + if (1) + { + if (pool < POOL_SIZE) + { + if (_pool_count[pool] < POOL_MAX) + { + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + MEMORY_pool_size += size; + fprintf(stderr, "my_free: %p (%p) -> %d bytes to pool #%d [%ld / %ld]\n", alloc, sbrk(0), size, pool, MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + *((void **)ptr) = _pool[pool]; + _pool[pool] = ptr; + _pool_count[pool]++; + return; + } + } + } + + #ifdef DEBUG_ME + MEMORY_size -= size; + #ifndef DO_NOT_PRINT_MEMORY + fprintf(stderr, "my_free: %p (%p) -> %d bytes freed [%ld / %ld]\n", alloc, sbrk(0), size, MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + #endif + free(ptr); +} + +void *my_realloc(void *alloc, size_t new_len) +{ + size_t *ptr; + size_t size; + size_t new_size; + + if (!alloc) + return my_malloc(new_len); + + ptr = alloc; + ptr--; + size = *ptr; + new_size = REAL_SIZE(new_len + sizeof(size_t)); + + if (size == new_size) + return alloc; + + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + fprintf(stderr, "my_realloc: %p (%p) -> %d ==> %d\n", alloc, sbrk(0), size, new_size); + #endif + + if (new_len == 0) + { + my_free(alloc); + return NULL; + } + else if (size > POOL_MAX_LEN && new_size > POOL_MAX_LEN) + { + #ifdef DEBUG_ME + MEMORY_size += new_size - size; + #ifndef DO_NOT_PRINT_MEMORY + fprintf(stderr, "my_realloc: %p (%p) -> %d bytes reallocated to %d [%ld / %ld]\n", alloc, sbrk(0), size, new_size, MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + #endif + ptr = realloc(ptr, new_size); + if (!ptr) + THROW_MEMORY(); + *ptr++ = new_size; + return ptr; + } + else + { + size_t *nptr = my_malloc(new_len); + + nptr--; + + if (new_size < size) + memcpy(nptr, ptr, new_size); + else + memcpy(nptr, ptr, size); + + my_free(alloc); + + *nptr++ = new_size; + return nptr; + } +} + +#endif diff --git a/main/share/gb_arch.h b/main/share/gb_arch.h new file mode 100644 index 00000000..ed64d607 --- /dev/null +++ b/main/share/gb_arch.h @@ -0,0 +1,90 @@ +/*************************************************************************** + + gb_arch.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_ARCH_H +#define __GBX_ARCH_H + +#include "gb_common.h" +#include "gb_table.h" +#include "gb_magic.h" + +typedef + struct { + int magic; + int version; + int pos_data; + int pos_string; + int pos_table; + int n_symbol; + } + ARCH_HEADER; + +typedef + struct { + SYMBOL sym; + int pos; + int len; + } + ARCH_SYMBOL; + +typedef + struct { + struct { + uint name; + int len; + } + sym; + int pos; + int len; + } + ARCH_SYMBOL_32; + +typedef + struct { + int fd; + ARCH_HEADER header; + ARCH_SYMBOL *symbol; + ushort *sort; + char *string; + char *addr; + size_t length; + } + ARCH; + +typedef + struct { + ARCH_SYMBOL *sym; + int index; + int pos; + int len; + } + ARCH_FIND; + + +ARCH *ARCH_open(const char *path); +void ARCH_close(ARCH *arch); +bool ARCH_find(ARCH *arch, const char *path, int len_path, ARCH_FIND *find); +bool ARCH_read(ARCH *arch, int pos, void *buffer, int len); +//void ARCH_get_absolute_path(const char *path, int len_path, char *abs_path, int *len_abs_path); + +#endif diff --git a/main/share/gb_arch_temp.h b/main/share/gb_arch_temp.h new file mode 100644 index 00000000..0afe175a --- /dev/null +++ b/main/share/gb_arch_temp.h @@ -0,0 +1,362 @@ +/*************************************************************************** + + gb_arch_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_ARCH_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_swap.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_arch.h" + + +#ifndef PROJECT_EXEC +#define E_ARCH "Bad archive: &1" +#endif + +static bool _swap = FALSE; +static const char *_path; + +static void arch_error(const char *msg) +{ + const char *name = FILE_get_name(_path); + if (msg == NULL) + THROW(E_ARCH, name, strerror(errno)); + else + THROW(E_ARCH, name, msg); +} + +static void read_at(ARCH *arch, int pos, void *buf, int len) +{ + memcpy(buf, &arch->addr[pos], len); +} + + +static void load_arch(ARCH *arch, const char *path) +{ + int len, lens; + int i; + int pos; + int pos_sort; + struct stat info; + ARCH_SYMBOL_32 *sym; + + _path = path; + + arch->fd = open(path, O_RDONLY | O_CLOEXEC); + if (arch->fd < 0) + THROW(E_OPEN, path, strerror(errno)); + + if (fstat(arch->fd, &info) < 0) + THROW(E_OPEN, path, strerror(errno)); + + arch->length = info.st_size; + arch->addr = mmap(NULL, arch->length, PROT_READ, MAP_PRIVATE, arch->fd, 0); + if (arch->addr == MAP_FAILED) + THROW(E_OPEN, path, strerror(errno)); + + //fprintf(stderr, "mmap: %s\n", path); + + /* Header */ + + read_at(arch, 32, &arch->header, sizeof(ARCH_HEADER)); + _swap = arch->header.magic != ARCH_MAGIC; + + if (_swap) + SWAP_ints((int *)&arch->header, 6); + + if (arch->header.magic != ARCH_MAGIC) + arch_error("not an archive"); + + //if (arch->header.version != ARCH_VERSION) + // arch_error("bad version"); + + /* Strings */ + + len = arch->header.pos_table - arch->header.pos_string; + if (len <= 0) + arch_error("corrupted header"); + + ALLOC(&arch->string, len); + read_at(arch, arch->header.pos_string, arch->string, len); + + /* File names table */ + + len = arch->header.n_symbol * sizeof(ARCH_SYMBOL); + lens = arch->header.n_symbol * sizeof(ushort); + if (len <= 0 || lens <= 0) + arch_error("corrupted header"); + + ALLOC(&arch->symbol, len); + ALLOC(&arch->sort, lens); + + sym = (ARCH_SYMBOL_32 *)&arch->addr[arch->header.pos_table]; + for (i = 0; i < arch->header.n_symbol; i++, sym++) + { + //arch->symbol[i].sym.sort = sym->sym.sort; + arch->symbol[i].sym.len = sym->sym.len; + arch->symbol[i].sym.name = (char *)(intptr_t)sym->sym.name; + arch->symbol[i].pos = sym->pos; + arch->symbol[i].len = sym->len; + } + pos_sort = arch->header.pos_table + arch->header.n_symbol * sizeof(ARCH_SYMBOL_32); + + read_at(arch, pos_sort, arch->sort, lens); + + /* String relocation */ + + pos = 0; + if (_swap) + { + for (i = 0; i < arch->header.n_symbol; i++) + { + SWAP_short((short *)&arch->sort[i]); + SWAP_int(&arch->symbol[i].sym.len); + SWAP_int(&arch->symbol[i].pos); + SWAP_int(&arch->symbol[i].len); + arch->symbol[i].sym.name = &arch->string[pos]; + pos += arch->symbol[i].sym.len; + } + } + else + { + for (i = 0; i < arch->header.n_symbol; i++) + { + arch->symbol[i].sym.name = &arch->string[pos]; + pos += arch->symbol[i].sym.len; + } + } + + SYMBOL_compute_keys(arch->symbol, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL); + + _path = NULL; + + #if 0 + for (i = 0; i < arch->header.n_symbol; i++) + { + fprintf(stderr, "%i (%i) %.*s\n", i, arch->sort[i], arch->symbol[i].sym.len, arch->symbol[i].sym.name); + } + #endif +} + + +ARCH *ARCH_open(const char *path) +{ + ARCH *arch; + + ALLOC_ZERO(&arch, sizeof(ARCH)); + + load_arch(arch, path); + + return arch; +} + +void ARCH_close(ARCH *arch) +{ + if (arch->fd) + { + FREE(&arch->string); + FREE(&arch->symbol); + FREE(&arch->sort); + munmap(arch->addr, arch->length); + close(arch->fd); + } + + FREE(&arch); +} + + +static bool get_absolute_path(const char *path, int len_path, char *abs_path, int *len_abs_path) +{ + const char *p, *lp; + char *ap, *apm; + char c; + int rest; + bool err = FALSE; + + p = path; + lp = p; + ap = abs_path; + apm = &abs_path[PATH_MAX]; + *len_abs_path = 0; + + for(;;) + { + rest = &path[len_path] - p; + if (rest <= 0) + break; + + if (ap >= apm) + { + err = TRUE; + break; + } + + c = *p; + + if (p == lp && c == '.') + { + if (rest == 1 || p[1] == '/') + { + if (rest == 1) + p++; + else + p += 2; + + lp = p; + continue; + } + else if (rest >= 2 && p[1] == '.' && (rest == 2 || p[2] == '/')) + { + if (ap > abs_path) + { + ap--; // Jumps the last '/' + while (ap > abs_path) + { + ap--; + if (*ap == '/') + { + ap++; + break; + } + } + } + + p += 3; + lp = p; + continue; + } + } + + *ap++ = c; + p++; + + if (c == '/') + lp = p; + } + + *len_abs_path = ap - abs_path; + *ap = 0; + return err; +} + +static int strint(char *str, int value) +{ + char buffer[16]; + char *p; + int len; + + p = &buffer[15]; + while (value >= 10) + { + *p-- = '0' + value % 10; + value /= 10; + } + *p = '0' + value; + + len = buffer + sizeof(buffer) - p; + memcpy(str, p, len); + return len; +} + +bool ARCH_find(ARCH *arch, const char *path, int len_path, ARCH_FIND *find) +{ + int ind; + ARCH_SYMBOL *sym; + char tpath[PATH_MAX]; + int len_tpath; + int n; + + if (len_path <= 0) + len_path = strlen(path); + + while (len_path >= 2 && path[len_path - 1] == '/') + len_path--; + + if (get_absolute_path(path, len_path, tpath, &len_tpath)) + return TRUE; + + if (len_tpath == 0) + { + find->sym = NULL; + find->index = NO_SYMBOL; + find->pos = -1; + find->len = 0; + return FALSE; + } + + //if (arch->header.version == 2) + //{ + char *p; + char tpath2[len_tpath + 8]; + + for(;;) + { + p = index(tpath + 1, '/'); + if (!p) + break; + + ind = SYMBOL_find(arch->symbol, arch->sort, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL, tpath, p - tpath, 0); + if (ind == NO_SYMBOL) + break; + + sym = &arch->symbol[ind]; + tpath2[0] = '/'; + n = strint(&tpath2[1], ind); + tpath2[n + 1] = ':'; + strcpy(&tpath2[n + 2], p + 1); + len_tpath = n + 2 + strlen(p + 1); + memcpy(tpath, tpath2, len_tpath); + tpath[len_tpath] = 0; + } + + ind = SYMBOL_find(arch->symbol, arch->sort, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL, tpath, len_tpath, 0); + //} + //else + // SYMBOL_find_old(arch->symbol, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL, tpath, len_tpath, 0, &ind); + + if (ind == NO_SYMBOL) + return TRUE; + + sym = &arch->symbol[ind]; + + find->sym = sym; + find->pos = sym->pos; + find->len = sym->len; + find->index = ind; + + return FALSE; +} + + +bool ARCH_read(ARCH *arch, int pos, void *buffer, int len) +{ + memcpy(buffer, &arch->addr[pos], len); + return FALSE; +} diff --git a/main/share/gb_array.h b/main/share/gb_array.h new file mode 100644 index 00000000..30d3a717 --- /dev/null +++ b/main/share/gb_array.h @@ -0,0 +1,121 @@ +/*************************************************************************** + + gb_array.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ARRAY_H +#define __GB_ARRAY_H + +typedef + struct { + uint count; + uint max; + uint size; + uint inc; + } + ARRAY; + +typedef + int (*ARRAY_COMP_FUNC)(const void *, const void *); + +#define DATA_TO_ARRAY(_data) ((ARRAY *)(_data) - 1) +#define ARRAY_TO_DATA(_array) ((char *)((ARRAY *)(_array) + 1)) + +#define ARRAY_create(_data) ARRAY_create_with_size((_data), sizeof(**(_data)), 32) +#define ARRAY_create_inc(_data, _inc) ARRAY_create_with_size((_data), sizeof(**(_data)), (_inc)) + +void ARRAY_create_with_size(void *p_data, size_t size, uint inc); +void ARRAY_delete(void *p_data); + +#define ARRAY_set_inc(_data, _inc) (DATA_TO_ARRAY(_data)->inc = _inc) + +#define ARRAY_size(_data) (DATA_TO_ARRAY(_data)->size) +#define ARRAY_count(_data) ((_data) ? DATA_TO_ARRAY(_data)->count : 0) + +void ARRAY_realloc(void *p_data); + +void *ARRAY_add_data(void *p_data, uint num, bool zero); +void *ARRAY_add_data_one(void *p_data, bool zero); + +#define ARRAY_add_one(_pdata, _zero) \ +({ \ + ARRAY *__array = DATA_TO_ARRAY(*(_pdata)); \ + __typeof__(*(_pdata)) ptr; \ + int old_count = __array->count; \ + \ + __array->count++; \ + \ + if (__array->count <= __array->max) \ + { \ + ptr = *(_pdata) + old_count; \ + } \ + else \ + { \ + ARRAY_realloc(_pdata); \ + ptr = *(_pdata) + old_count; \ + } \ + if (_zero) memset(ptr, 0, sizeof(*ptr)); \ + ptr; \ +}) + +#define ARRAY_add_data_one(_pdata, _zero) \ +({ \ + ARRAY *__array = DATA_TO_ARRAY(*(_pdata)); \ + int size = __array->size; \ + char *ptr; \ + int old_count = __array->count; \ + \ + __array->count++; \ + \ + if (__array->count <= __array->max) \ + { \ + ptr = (char *)*(_pdata) + size * old_count; \ + } \ + else \ + { \ + ARRAY_realloc(_pdata); \ + ptr = (char *)*(_pdata) + size * old_count; \ + } \ + if (_zero) memset(ptr, 0, size); \ + (void *)ptr; \ +}) + +#define ARRAY_add(_pdata) ARRAY_add_one(_pdata, FALSE) +#define ARRAY_add_void(_pdata) ARRAY_add_one(_pdata, TRUE) +#define ARRAY_add_size(_pdata) ARRAY_add_data_one(_pdata, FALSE) +#define ARRAY_add_void_size(_pdata) ARRAY_add_data_one(_pdata, TRUE) + +#define ARRAY_add_many(_pdata, _num) ARRAY_add_data(_pdata, _num, FALSE) +#define ARRAY_add_many_void(_pdata, _num) ARRAY_add_data(_pdata, _num, TRUE) + +//PUBLIC void *ARRAY_get(void *data, int pos); +#define ARRAY_get(_data, _pos) ((char *)(_data) + DATA_TO_ARRAY(_data)->size * (_pos)) + +void *ARRAY_insert_many(void *p_data, int pos, uint count); +#define ARRAY_insert(_pdata, _pos) ARRAY_insert_many(_pdata, _pos, 1); +void ARRAY_remove_many(void *p_data, int pos, uint count); +#define ARRAY_remove(_pdata, _pos) ARRAY_remove_many(_pdata, _pos, 1); + +void ARRAY_remove_last(void *p_data); + +void ARRAY_qsort(void *data, ARRAY_COMP_FUNC cmp); + +#endif diff --git a/main/share/gb_array_temp.h b/main/share/gb_array_temp.h new file mode 100644 index 00000000..e68eaf8e --- /dev/null +++ b/main/share/gb_array_temp.h @@ -0,0 +1,217 @@ +/*************************************************************************** + + gb_array_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __ARRAY_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_array.h" + +void ARRAY_create_with_size(void *p_data, size_t size, uint inc) +{ + ARRAY *array; + + ALLOC(&array, sizeof(ARRAY)); + + array->count = 0; + array->max = 0; + array->size = (uint)size; + if (size > 2 && (size & 3)) + fprintf(stderr, "WARNING: ARRAY_create_with_size: size = %zi\n", size); + array->inc = inc; + + *((void **)p_data) = ARRAY_TO_DATA(array); +} + + +void ARRAY_delete(void *p_data) +{ + void **data = (void **)p_data; + ARRAY *alloc = DATA_TO_ARRAY(*data); + + if (!*data) + return; + + FREE(&alloc); + + *data = NULL; +} + + +static inline ARRAY *array_realloc(ARRAY *array) +{ + ARRAY *new_array; + + array->max = array->inc + ((array->count + array->inc) / array->inc) * array->inc; + new_array = array; + REALLOC(&new_array, sizeof(ARRAY) + array->max * array->size); + return new_array; +} + + +void *ARRAY_add_data(void *p_data, uint num, bool zero) +{ + void **data = (void **)p_data; + ARRAY *array = DATA_TO_ARRAY(*data); + char *ptr; + + array->count += num; + + if (array->count > array->max) + { + /*array->max = array->inc + ((array->count + array->inc) / array->inc) * array->inc; + new_array = array; + REALLOC(&new_array, sizeof(ARRAY) + array->max * array->size); + array = new_array;*/ + array = array_realloc(array); + *data = ARRAY_TO_DATA(array); + } + + ptr = (char *)array + sizeof(ARRAY) + array->size * (array->count - num); + + if (zero) memset(ptr, 0, array->size * num); + + return ptr; +} + + +void ARRAY_realloc(void *p_data) +{ + void **data = (void **)p_data; + ARRAY *array = DATA_TO_ARRAY(*data); + + *data = ARRAY_TO_DATA(array_realloc(array)); +} + + +/*void *ARRAY_add_data_one(void *p_data, bool zero) +{ + void **data = (void **)p_data; + register ARRAY *array = DATA_TO_ARRAY(*data); + int size = array->size; + char *ptr; + + array->count++; + + if (array->count > array->max) + { + array = array_realloc(array); + *data = ARRAY_TO_DATA(array); + } + + ptr = (char *)array + sizeof(ARRAY) + size * (array->count - 1); + + if (zero) memset(ptr, 0, size); + + return ptr; +}*/ + + +void ARRAY_remove_last(void *p_data) +{ + void **data = (void **)p_data; + ARRAY *array = DATA_TO_ARRAY(*data); + + if (!array->count) + return; + + array->count--; +} + + +void *ARRAY_insert_many(void *p_data, int pos, uint count) +{ + void **data; + ARRAY *array; + char *addr; + int len; + uint apos; + + data = (void **)p_data; + array = DATA_TO_ARRAY(*data); + + if ((pos < 0) || (pos > array->count)) + apos = array->count; + else + apos = pos; + + ARRAY_add_many(p_data, count); + array = DATA_TO_ARRAY(*data); + + addr = ((char *)(*data)) + array->size * apos; + len = (array->count - apos - count) * array->size; + + if (len > 0) + memmove(addr + array->size * count, addr, len); + + memset(addr, 0, array->size * count); + + return addr; +} + + +void ARRAY_remove_many(void *p_data, int pos, uint count) +{ + void **data = (void **)p_data; + ARRAY *array = DATA_TO_ARRAY(*data); + char *addr; + int len; + uint apos; + + if ((pos < 0) || (pos >= array->count)) + return; + + apos = pos; + + if (count < 0 || count > (array->count - apos)) + count = array->count - apos; + + addr = ((char *)(*data)) + array->size * apos; + len = (array->count - apos - count) * array->size; + + if (len > 0) + memmove(addr, addr + array->size * count, len); + + array->count -= count; + + if (array->max <= array->inc) + return; + + if (array->count > (array->max / 2)) + return; + + array->max = ((array->count + array->inc) / array->inc) * array->inc; + REALLOC(&array, sizeof(ARRAY) + array->max * array->size); + *data = ARRAY_TO_DATA(array); +} + + +void ARRAY_qsort(void *data, ARRAY_COMP_FUNC cmp) +{ + ARRAY *array = DATA_TO_ARRAY(data); + + if (array->count) + qsort(data, array->count, array->size, cmp); +} + diff --git a/main/share/gb_buffer.h b/main/share/gb_buffer.h new file mode 100644 index 00000000..95c3a3a8 --- /dev/null +++ b/main/share/gb_buffer.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gb_buffer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_BUFFER_H +#define __GB_BUFFER_H + +typedef + struct { + size_t length; + size_t max; + } + BUFFER; + + +#define BUFFER_INC 256 + +void BUFFER_create(void *p_data); +void BUFFER_delete(void *p_data); +bool BUFFER_load_file(void *p_data, const char *name); +offset_t BUFFER_add(void *p_data, const void *string, int len); +bool BUFFER_need(void *p_data, size_t size); +void BUFFER_add_char(void *p_data, char c); + +#define DATA_TO_BUFFER(_data) ((BUFFER *)(_data) - 1) +#define BUFFER_TO_DATA(_buffer) ((char *)((BUFFER *)(_buffer) + 1)) + +#define BUFFER_length(_data) (DATA_TO_BUFFER(_data)->length) + +#endif diff --git a/main/share/gb_buffer_temp.h b/main/share/gb_buffer_temp.h new file mode 100644 index 00000000..08c40fb8 --- /dev/null +++ b/main/share/gb_buffer_temp.h @@ -0,0 +1,163 @@ +/*************************************************************************** + + gb_buffer_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __BUFFER_C + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_buffer.h" + + +void BUFFER_create(void *p_data) +{ + BUFFER *buf; + + ALLOC(&buf, sizeof(BUFFER)); + buf->max = 0; + buf->length = 0; + + *((void **)p_data) = BUFFER_TO_DATA(buf); +} + + +void BUFFER_delete(void *p_data) +{ + void **data = (void **)p_data; + BUFFER *buf = DATA_TO_BUFFER(*data); + + FREE(&buf); + *data = NULL; +} + + +bool BUFFER_need(void *p_data, size_t size) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + + buffer->length += size; + //fprintf(stderr, "BUFFER_need: %ld (%ld / %ld)\n", size, buffer->length, buffer->max); + + if (buffer->length > buffer->max) + { + while (buffer->length >= buffer->max) + buffer->max += BUFFER_INC; + + REALLOC(&buffer, sizeof(char) * buffer->max + sizeof(BUFFER)); + *data = BUFFER_TO_DATA(buffer); + } + + return FALSE; +} + + +offset_t BUFFER_add(void *p_data, const void *string, int len) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + size_t pos; + + if (len < 0) + len = strlen((const char *)string); + + pos = buffer->length; + BUFFER_need(p_data, len); + + memcpy(*data + pos, string, len); + + //fprintf(stderr, ">> BUFFER_add\n"); + + return pos; +} + +void BUFFER_add_char(void *p_data, char c) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + size_t pos; + + pos = buffer->length; + BUFFER_need(p_data, 1); + *(char *)(*data + pos) = c; +} + +bool BUFFER_load_file(void *p_data, const char *name) +{ + void **data; + + int fd; + struct stat info; + int old_len; + int len, lenr; + void *p; + + fd = open(name, O_RDONLY); + if (fd < 0) + return TRUE; + + if (fstat(fd, &info)) + { + close(fd); + return TRUE; + } + + len = info.st_size; + + data = (void **)p_data; + old_len = DATA_TO_BUFFER(*data)->length; + + BUFFER_need(p_data, len); + + data = (void **)p_data; + + p = *data + old_len; + + for(;;) + { + lenr = read(fd, p, len); + if (lenr < 0) + { + close(fd); + return TRUE; + } + + if (lenr == len) + break; + + p += lenr; + len -= lenr; + } + + close(fd); + + return FALSE; +} diff --git a/main/share/gb_class_desc_common.h b/main/share/gb_class_desc_common.h new file mode 100644 index 00000000..c37537c4 --- /dev/null +++ b/main/share/gb_class_desc_common.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gb_class_desc_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_CLASS_DESC_COMMON_H +#define __GB_CLASS_DESC_COMMON_H + +enum +{ + CD_VARIABLE_ID = 1, + CD_STATIC_VARIABLE_ID = 2, + CD_PROPERTY_ID = 3, + CD_STATIC_PROPERTY_ID = 4, + CD_METHOD_ID = 5, + CD_STATIC_METHOD_ID = 6, + CD_CONSTANT_ID = 7, + CD_EVENT_ID = 8, + CD_EXTERN_ID = 9, +}; + +#endif diff --git a/main/share/gb_code.h b/main/share/gb_code.h new file mode 100644 index 00000000..0ffcd906 --- /dev/null +++ b/main/share/gb_code.h @@ -0,0 +1,171 @@ +/*************************************************************************** + + gb_code.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_CODE_H +#define __GB_CODE_H + +#include "gb_pcode.h" + +#ifndef __GB_CODE_C +EXTERN short CODE_stack_usage; +EXTERN unsigned char CODE_disabled; +#endif + + +/* Number of instruction added to a function code buffer at once. Must be a power of 2 */ +#define CODE_INSTR_INC 1024 +#define CODE_NO_POS (ushort)0xFFFF + +#define CODE_disable() (CODE_disabled++) +#define CODE_enable() (CODE_disabled--) + +#ifdef PROJECT_EXEC + +void CODE_begin_function(void); +void CODE_end_function(void); +bool CODE_popify_last(bool no_conv); + +#else + +#include "gb_common_swap.h" + +#ifndef __GB_CODE_C +EXTERN FUNCTION *CODE_current_func; +EXTERN bool CODE_break_is_allowed; +#endif + +void CODE_begin_function(FUNCTION *func); +void CODE_end_function(FUNCTION *func); +FUNCTION *CODE_set_function(FUNCTION *func); + +bool CODE_popify_last(bool no_conv); +bool CODE_check_statement_last(void); +bool CODE_check_pop_local_last(short *local); +bool CODE_check_jump_not(void); +bool CODE_check_fast_cat(void); + +bool CODE_check_varptr(void); +bool CODE_check_ismissing(void); + +#define CODE_allow_break() (CODE_break_is_allowed = TRUE) +//void CODE_break(void); + +void CODE_pop_local(short num); +void CODE_pop_local_noref(short num); +//void CODE_pop_param(short num); +void CODE_pop_global(short global, bool is_static); +void CODE_pop_symbol(short symbol); +void CODE_pop_unknown(short symbol); +void CODE_pop_optional(short num); + +//void CODE_push_special(short spec); +void CODE_push_event(short event); +void CODE_push_extern(short index); + +void CODE_jump(void); +void CODE_gosub(int ctrl_local); +void CODE_on(uchar num); +void CODE_jump_first(short local); +void CODE_jump_next(void); +void CODE_jump_if(bool test, bool fast); +void CODE_jump_length(ushort src, ushort dst); +void CODE_first(short local); +void CODE_next(bool drop); + +void CODE_new(ushort nparam, bool array, bool event); + +void CODE_quit(bool ret); +void CODE_stop(void); + +void CODE_event(bool on); +void CODE_stop_event(void); + +void CODE_try(void); +void CODE_end_try(void); +void CODE_catch(void); + +void CODE_pop_ctrl(short num); + +#endif /* PROJECT_EXEC */ + +#ifdef PROJECT_EXEC +ushort CODE_get_current_pos(void); +#else +#define CODE_get_current_pos() (CODE_current_func->ncode) +#endif + +ushort CODE_set_current_pos(ushort pos); +void CODE_ignore_next_stack_usage(void); + +void CODE_dump(PCODE *code, int count); + +void CODE_push_number(int value); +void CODE_push_float(int value); +void CODE_push_const(ushort value); + +void CODE_push_local_ref(short num, bool noref); +#define CODE_push_local(_num) CODE_push_local_ref(_num, TRUE); +//void CODE_push_param(short num); +void CODE_push_array(short nparam); +void CODE_push_global(short global, bool is_static, bool is_function); +void CODE_push_symbol(short symbol); +void CODE_push_unknown(short symbol); +void CODE_push_unknown_event(short symbol); +void CODE_push_class(short class); + +void CODE_op(short op, short subcode, short nparam, bool fixed); + +void CODE_push_me(bool); +void CODE_push_super(bool); +void CODE_push_last(void); +void CODE_push_null(void); +void CODE_push_void_string(); +void CODE_push_boolean(bool value); +void CODE_push_inf(bool neg); +void CODE_push_complex(); + +void CODE_push_vargs(); +void CODE_drop_vargs(); +void CODE_end_vargs(); + +void CODE_dup(void); + +void CODE_return(int return_value); + +void CODE_push_char(uchar car); +void CODE_push_void(void); + +void CODE_subr(short subr, short nparam, short optype, bool fixed); +//void CODE_subr_output(short subr, short nparam, int output); + +void CODE_call(short nparam); +void CODE_call_byref(short nparam, uint64_t byref); +void CODE_byref(uint64_t byref); +void CODE_drop(void); +void CODE_push_return(void); + +void CODE_nop(void); + +void CODE_string_add(void); + +#endif diff --git a/main/share/gb_code_temp.h b/main/share/gb_code_temp.h new file mode 100644 index 00000000..b3636694 --- /dev/null +++ b/main/share/gb_code_temp.h @@ -0,0 +1,1511 @@ +/*************************************************************************** + + gb_code_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +//#define DEBUG + +#define write_Zxxx(code, val) write_short(code | ((short)val & 0x0FFF)) +#define write_Z8xx(code, val) write_short(code | ((short)val & 0x07FF)) +#define write_ZZxx(code, val) write_short(code | ((short)val & 0x00FF)) + +#ifndef PROJECT_EXEC + +#define LAST_CODE \ +{ \ + if (JOB->debug && !JOB->nobreak) \ + CODE_break(); \ + cur_func->last_code2 = cur_func->last_code; \ + cur_func->last_code = cur_func->ncode; \ +} +#define CURRENT_CLASS JOB->class + +#else + +#define LAST_CODE \ +{ \ + cur_func->last_code2 = cur_func->last_code; \ + cur_func->last_code = cur_func->ncode; \ +} +#define CURRENT_CLASS EVAL + +#endif + +short CODE_stack_usage; +short CODE_stack; +unsigned char CODE_disabled = 0; + +static bool _ignore_next_stack_usage = FALSE; + +#ifdef PROJECT_EXEC +#define cur_func EVAL +#else +static FUNCTION *cur_func = NULL; +FUNCTION *CODE_current_func = NULL; +//static int last_line = 0; +#endif + +static void alloc_code(void) +{ + cur_func->ncode_max += CODE_INSTR_INC; + if (!cur_func->code) + ALLOC(&cur_func->code, sizeof(short) * CODE_INSTR_INC); + else + REALLOC(&cur_func->code, sizeof(short) * cur_func->ncode_max); +} + +#define write_short(_value) \ +({ \ + if (CODE_disabled == 0) \ + { \ + if (cur_func->ncode >= cur_func->ncode_max) \ + alloc_code(); \ + \ + cur_func->code[cur_func->ncode] = _value; \ + /*fprintf(stderr, "[%d] %04hX\n", cur_func->ncode, (ushort)value);*/ \ + cur_func->ncode++; \ + } \ +}) + +#ifdef PROJECT_COMP + +bool CODE_break_is_allowed = FALSE; + +static void CODE_break(void) +{ + if (!CODE_break_is_allowed) + return; + + /*if (last_line < 0) + { + if (CODE_get_current_pos()) + return; + } + else + { + if (JOB->line == last_line) + return; + + last_line = JOB->line; + }*/ + + #ifdef DEBUG + printf("BREAK\n"); + #endif + + write_short(C_BREAK); + CODE_break_is_allowed = FALSE; +} + +#endif + + +static void write_int(int value) +{ + write_short(value & 0xFFFF); + write_short((unsigned int)value >> 16); +} + + +/*static void remove_last(void) +{ + ARRAY_remove_last(&cur_func->code); + cur_func->last_code = ARRAY_count(cur_func->code); +}*/ + + +static inline void use_stack(int use) +{ + if (_ignore_next_stack_usage) + { + _ignore_next_stack_usage = FALSE; + return; + } + + CODE_stack += use; + CODE_stack_usage = Max(CODE_stack_usage, CODE_stack); + #ifdef DEBUG + printf("%04d: %d\n", cur_func->ncode, CODE_stack); + #endif +} + +static void CODE_undo() +{ + cur_func->ncode = cur_func->last_code; + cur_func->last_code = cur_func->last_code2; + cur_func->last_code2 = (-1); +} + +#ifdef PROJECT_EXEC +ushort CODE_get_current_pos(void) +{ + return cur_func->ncode; +} +#endif + +ushort CODE_set_current_pos(ushort pos) +{ + ushort old = cur_func->ncode; + cur_func->ncode = pos; + return old; +} + + +void CODE_ignore_next_stack_usage(void) +{ + _ignore_next_stack_usage = TRUE; +} + +#ifdef PROJECT_EXEC + +void CODE_begin_function() +{ + CODE_stack = 0; + CODE_stack_usage = 0; +} + +void CODE_end_function() +{ + if (CODE_stack) + { + fprintf(stderr, "gb.eval: bad stack usage computed: %d\n", CODE_stack); + THROW("Internal compiler error"); + } +} + +#else + +FUNCTION *CODE_set_function(FUNCTION *func) +{ + FUNCTION *prev = cur_func; + + if (cur_func) + { + cur_func->code_stack = CODE_stack; + cur_func->code_stack_usage = CODE_stack_usage; + } + + cur_func = func; + CODE_current_func = func; + + if (func) + { + CODE_stack = cur_func->code_stack; + CODE_stack_usage = cur_func->code_stack_usage; + } + + return prev; +} + +void CODE_begin_function(FUNCTION *func) +{ + func->code_stack = func->code_stack_usage = 0; + CODE_set_function(func); + + /*if (func->start == NULL) + last_line = (-1); + else + last_line = 0;*/ +} + +void CODE_end_function(FUNCTION *func) +{ + if (CODE_stack) + { + ERROR_warning("bad stack usage computed: %d\n", CODE_stack); + THROW("Internal compiler error"); + } +} + +#endif + + +static ushort *get_last_code() +{ + if ((ushort)cur_func->last_code == CODE_NO_POS) + return NULL; + + return &cur_func->code[cur_func->last_code]; +} + +static ushort *get_last_code2() +{ + if ((ushort)cur_func->last_code2 == CODE_NO_POS) + return NULL; + + return &cur_func->code[cur_func->last_code2]; +} + +bool CODE_popify_last(bool no_conv) +{ + /* + #ifdef DEBUG + printf("CODE_is_last_popable ? "); + if (!last_code) printf("FALSE, last_code = NULL"); + else printf("0x%04hX", *last_code); + printf("\n"); + #endif + */ + unsigned short *last_code, op; + + last_code = get_last_code(); + if (!last_code) + return FALSE; + + op = *last_code & 0xFF00; + + if ((op >= C_PUSH_LOCAL && op <= C_PUSH_UNKNOWN)) + { + *last_code += 0x0800; + } + else if (op == C_PUSH_LOCAL_NOREF) + { + if (no_conv) + *last_code = C_POP_LOCAL_FAST | (*last_code & 0xFF); + else + *last_code = C_POP_LOCAL_NOREF | (*last_code & 0xFF); + } + else if (op == C_PUSH_PARAM_NOREF) + { + if (no_conv) + *last_code = C_POP_PARAM_FAST | (*last_code & 0xFF); + else + *last_code = C_POP_PARAM_NOREF | (*last_code & 0xFF); + } + else if ((op & 0xF000) == C_PUSH_DYNAMIC) + { + *last_code += 0x1000; + } + else + return FALSE; + + use_stack(-2); + return TRUE; +} + + +#ifdef PROJECT_COMP + +bool CODE_check_statement_last(void) +{ + unsigned short op; + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return FALSE; + + op = *last_code & 0xFF00; + + if (op == C_CALL) + return TRUE; + + op >>= 8; + + if (op >= CODE_FIRST_SUBR && op <= CODE_LAST_SUBR) + return TRUE; + + return FALSE; +} + + +bool CODE_check_pop_local_last(short *local) +{ + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return FALSE; + + if ((*last_code & 0xFF00) == C_POP_LOCAL || (*last_code & 0xFF00) == C_POP_LOCAL_NOREF) + { + *local = *last_code & 0xFF; + return TRUE; + } + + return FALSE; +} + +bool CODE_check_jump_not(void) +{ + ushort op; + PCODE *last_code = get_last_code(); + + if (!last_code) + return FALSE; + + op = *last_code & 0xFF00; + if (op != C_NOT) + return FALSE; + + CODE_undo(); + return TRUE; +} + +#endif + +bool CODE_check_varptr(void) +{ + unsigned short op; + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return TRUE; + + op = *last_code; + if (!((op & 0xFF00) == C_PUSH_LOCAL || (op & 0xFF00) == C_PUSH_LOCAL_NOREF + || (op & 0xFF00) == C_PUSH_PARAM || (op & 0xF800) == C_PUSH_STATIC + || (op & 0xF800) == C_PUSH_DYNAMIC)) + return TRUE; + + *last_code = C_PUSH_INTEGER; + write_short((short)op); + return FALSE; +} + +bool CODE_check_fast_cat(void) +{ + unsigned short op; + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return FALSE; + + op = *last_code; + + if (!((op & 0xFF00) == C_POP_LOCAL || (op & 0xFF00) == C_POP_PARAM || (op & 0xF800) == C_POP_STATIC || (op & 0xF800) == C_POP_DYNAMIC)) + return FALSE; + + cur_func->code[cur_func->ncode - 2] &= 0xFF00; + cur_func->code[cur_func->ncode - 2] |= 1; + return TRUE; +} + +bool CODE_check_ismissing(void) +{ + unsigned short op; + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return TRUE; + + op = *last_code; + if ((op & 0xFF00) != C_PUSH_PARAM && ((op & 0xFF00) != C_PUSH_PARAM_NOREF)) + return TRUE; + + *last_code = C_PUSH_QUICK | (op & 0xFF); + return FALSE; +} + +void CODE_nop(void) +{ + LAST_CODE; + write_short(C_NOP); +} + +void CODE_push_number(int value) +{ + LAST_CODE; + + use_stack(1); + + if (value >= -256 && value < 256) + { + #ifdef DEBUG + printf("PUSH QUICK %d\n", value); + #endif + write_Zxxx(C_PUSH_QUICK, value); + } + else if (value >= -32768L && value < 32768L) + { + #ifdef DEBUG + printf("PUSH INTEGER %d\n", value); + #endif + write_short(C_PUSH_INTEGER); + write_short((short)value); + } + else + { + #ifdef DEBUG + printf("PUSH LONG %d\n", value); + #endif + write_short(C_PUSH_LONG); + write_int(value); + } +} + +void CODE_push_float(int value) +{ + LAST_CODE; + + use_stack(1); + + write_ZZxx(C_PUSH_FLOAT, value); +} + + +void CODE_push_const(ushort value) +{ + LAST_CODE; + + use_stack(1); + +#ifdef DEBUG + #ifdef PROJECT_EXEC + printf("PUSH CONST %d\n", value); + #else + printf("PUSH CONST %d %s\n", value, TABLE_get_symbol_name(CURRENT_CLASS->table, CURRENT_CLASS->constant[value].index)); + #endif +#endif + + if (value < 0xF00) + write_Zxxx(C_PUSH_CONST, value); + else + { + write_Zxxx(C_PUSH_CONST, 0xF00); + write_short((short)value); + } +} + + +void CODE_push_local_ref(short num, bool ref) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + if (num >= 0) + printf("PUSH LOCAL %d\n", num); + else + printf("PUSH PARAM %d\n", (-1) - num); + #endif + if (!ref && COMP_version >= 0x03180000) + { + if (num >= 0) + write_ZZxx(C_PUSH_LOCAL_NOREF, num); + else + write_ZZxx(C_PUSH_PARAM_NOREF, num); + } + else + { + if (num >= 0) + write_ZZxx(C_PUSH_LOCAL, num); + else + write_ZZxx(C_PUSH_PARAM, num); + } +} + + +#ifdef PROJECT_COMP + +void CODE_pop_local(short num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + if (num >= 0) + printf("POP LOCAL #%d\n", num); + else + printf("POP PARAM #%d\n", (-1) - num); + #endif + if (num >= 0) + write_ZZxx(C_POP_LOCAL, num); + else + write_ZZxx(C_POP_PARAM, num); +} + +void CODE_pop_local_noref(short num) +{ + if (COMP_version < 0x03180000) + { + CODE_pop_local(num); + return; + } + + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + if (num >= 0) + printf("POP LOCAL NOREF #%d\n", num); + else + printf("POP PARAM NOREF #%d\n", (-1) - num); + #endif + if (num >= 0) + write_ZZxx(C_POP_LOCAL_NOREF, num); + else + write_ZZxx(C_POP_PARAM_NOREF, num); +} + + +void CODE_pop_ctrl(short num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("POP CTRL #%d\n", num); + #endif + + write_ZZxx(C_POP_CTRL, num); +} + + +void CODE_pop_optional(short num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("POP OPTIONAL #%d\n", (-1) - num); + #endif + write_ZZxx(C_POP_OPTIONAL, num); +} + +#endif /* PROJECT_COMP */ + + +void CODE_push_array(short nparam) +{ + LAST_CODE; + + use_stack(1 - nparam); + + write_ZZxx(C_PUSH_ARRAY, nparam); +} + + +void CODE_push_global(short global, bool is_static, bool is_function) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH %s %d\n", is_static ? "STATIC" : "DYNAMIC", global); + #endif + + if (is_function) + write_Z8xx(C_PUSH_FUNCTION, global); + else if (is_static) + write_Z8xx(C_PUSH_STATIC, global); + else + write_Z8xx(C_PUSH_DYNAMIC, global); +} + + +#ifdef PROJECT_COMP + +void CODE_pop_global(short global, bool is_static) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("POP %s %d\n", is_static ? "STATIC" : "DYNAMIC", global); + #endif + + if (is_static) + write_Z8xx(C_POP_STATIC, global); + else + write_Z8xx(C_POP_DYNAMIC, global); +} + +#endif + +/* +void CODE_push_symbol(short symbol) +{ + LAST_CODE; + + use_stack(0); + + #ifdef DEBUG + printf("PUSH SYMBOL %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_short(C_PUSH_SYMBOL); + write_short(symbol); +} + + +void CODE_pop_symbol(short symbol) +{ + LAST_CODE; + + use_stack(-2); + + #ifdef DEBUG + printf("POP SYMBOL %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_short(C_POP_SYMBOL); + write_short(symbol); +} +*/ + + +void CODE_push_unknown(short symbol) +{ + LAST_CODE; + + use_stack(0); + +#ifdef DEBUG + #ifdef PROJECT_EXEC + printf("PUSH UNKNOWN %s\n", CURRENT_CLASS->unknown[symbol]); + #else + printf("PUSH UNKNOWN %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif +#endif + + write_short(C_PUSH_UNKNOWN); + write_short(symbol); +} + +void CODE_push_unknown_event(short symbol) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH UNKNOWN EVENT %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_ZZxx(C_PUSH_EVENT, 0xFF); + write_short(symbol); +} + + +#ifdef PROJECT_COMP + +void CODE_pop_unknown(short symbol) +{ + LAST_CODE; + + use_stack(-2); + + #ifdef DEBUG + printf("POP UNKNOWN %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_short(C_POP_UNKNOWN); + write_short(symbol); +} + +#endif + + +void CODE_push_class(short class) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH CLASS %d\n", class); + #endif + + write_Z8xx(C_PUSH_CLASS, class); + + #ifdef PROJECT_COMP + CURRENT_CLASS->class[class].used = TRUE; + #endif +} + +#ifdef PROJECT_COMP + +void CODE_jump() +{ + LAST_CODE; + + #ifdef DEBUG + printf("JUMP\n"); + #endif + write_short(C_JUMP); + write_short(0); +} + +void CODE_gosub(int ctrl_local) +{ + LAST_CODE; + + #ifdef DEBUG + printf("GOSUB\n"); + #endif + write_ZZxx(C_GOSUB, ctrl_local); + write_short(0); +} + +void CODE_on(uchar num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("ON\n"); + #endif + write_ZZxx(C_ON, num); +} + + +void CODE_jump_if(bool test, bool fast) +{ + ushort op; + + use_stack(-1); + + #ifdef DEBUG + printf("JUMP IF %s\n", test ? "TRUE" : "FALSE"); + #endif + + LAST_CODE; + + if (test) + { + if (fast && COMP_version >= 0x03180000) + op = C_JUMP_IF_TRUE_FAST; + else + op = C_JUMP_IF_TRUE; + } + else + { + if (fast && COMP_version >= 0x03180000) + op = C_JUMP_IF_FALSE_FAST; + else + op = C_JUMP_IF_FALSE; + } + write_short(op); + write_short(0); +} + + +void CODE_jump_first(short local) +{ + LAST_CODE; + + use_stack(-2); + + #ifdef DEBUG + printf("JUMP FIRST LOCAL %d\n", local); + #endif + + write_ZZxx(C_JUMP_FIRST, local); +} + + +void CODE_jump_next(void) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("JUMP NEXT\n"); + #endif + write_short(C_JUMP_NEXT); + /**pos = CODE_get_current_pos();*/ + write_short(0); +} + + +void CODE_jump_length(ushort src, ushort dst) +{ + if (src >= (cur_func->ncode - 1)) + return; + + int diff = (int)dst - (int)src; + + if (diff < -32768 || diff > 32767) + THROW("Jump is too far"); + + if (cur_func->code[src] == C_BREAK) + cur_func->code[src + 2] = (short)(diff - 3); //dst - (src + 2) - 1; + else if (cur_func->code[src] == C_NOP) + cur_func->code[src] = (short)diff; //dst - src; + else + cur_func->code[src + 1] = (short)(diff - 2); //dst - (src + 1) - 1; +} + + +void CODE_first(short local) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("ENUM FIRST LOCAL %d\n", local); + #endif + + write_ZZxx(C_FIRST, local); +} + + +void CODE_next(bool drop) +{ + LAST_CODE; + + use_stack(drop ? 0 : 1); + + #ifdef DEBUG + printf("ENUM NEXT%s\n", drop ? " DROP" : ""); + #endif + + write_ZZxx(C_NEXT, drop ? 1 : 0); + write_short(0); +} + +#endif /* PROJECT_COMP */ + +void CODE_op(short op, short subcode, short nparam, bool fixed) +{ + if (op == C_ADD || op == C_SUB) + { + PCODE *last_code; + short value, value2; + + last_code = get_last_code(); + + if (last_code && ((*last_code & 0xF000) == C_PUSH_QUICK)) + { + value = *last_code & 0xFFF; + if (value >= 0x800) value |= 0xF000; + if (op == C_SUB) value = (-value); // prevent -256 to be valid! + + #ifdef DEBUG + printf("ADD QUICK %d\n", value); + #endif + + if (COMP_version < 0x03180000 || (value > -256 && value < 256)) + { + *last_code = C_ADD_QUICK | (value & 0x0FFF); + + use_stack(1 - nparam); + + // Now, look if we are PUSH QUICK then ADD QUICK + + last_code = get_last_code2(); + if (last_code && ((*last_code & 0xF000) == C_PUSH_QUICK)) + { + value2 = *last_code & 0xFFF; + if (value2 >= 0x800) value2 |= 0xF000; + + if (value2 > -256 && value2 < 256) + { + value += value2; + + if (value >= -256 && value < 256) + { + *last_code = C_PUSH_QUICK | (value & 0x0FFF); + CODE_undo(); + } + } + } + + return; + } + } + } + + LAST_CODE; + + use_stack(1 - nparam); + + #ifdef DEBUG + printf("OP %d (%d)\n", op, nparam); + #endif + + if (fixed) + write_ZZxx(op, subcode); + else + write_ZZxx(op, nparam); +} + + +void CODE_push_me(bool debug) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH ME\n"); + #endif + + write_ZZxx(C_PUSH_ME, debug ? 1 : 0); +} + + +void CODE_push_super(bool debug) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH SUPER\n"); + #endif + + write_ZZxx(C_PUSH_ME, debug ? 3 : 2); +} + + +void CODE_push_last() +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH LAST\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_LAST); +} + + +void CODE_push_null() +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH NULL\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_NULL); +} + + +void CODE_push_void_string() +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH VOID STRING\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_STRING); +} + + + +/* +static bool change_last_call(ushort flag) +{ + ushort *last_code = get_last_code(); + + if (!last_code) + return FALSE; + + if ((*last_code & 0xFF00) == C_CALL) + { + *last_code = *last_code | flag; + return TRUE; + } + else if ((ushort)((*last_code) & 0xFF00) >= (ushort)CODE_FIRST_SUBR) + { + *last_code = *last_code | flag; + return TRUE; + } + else if (((*last_code & 0xFF00) == C_DROP) && flag == CODE_CALL_DROP) + + return FALSE; +} +*/ + +void CODE_dup(void) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("DUP\n"); + #endif + + write_short(C_DUP); +} + + +void CODE_return(int return_value) +{ + LAST_CODE; + + if (return_value == 1) + use_stack(-1); + + write_ZZxx(C_RETURN, return_value); + + #ifdef DEBUG + printf("RETURN (%d)\n", return_value); + #endif +} + + +#ifdef PROJECT_COMP + +void CODE_quit(bool ret) +{ + LAST_CODE; + + #ifdef DEBUG + printf("QUIT (%d)\n", ret); + #endif + + if (ret) + use_stack(-1); + + write_ZZxx(C_QUIT, ret ? 3 : 0); +} + + +void CODE_stop(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("STOP\n"); + #endif + + write_ZZxx(C_QUIT, 1); +} + +#endif /* PROJECT_COMP */ + + +void CODE_push_char(uchar car) +{ + LAST_CODE; + + use_stack(1); + write_ZZxx(C_PUSH_CHAR, car); + + #ifdef DEBUG + printf("PUSH CHAR %d\n", car); + #endif +} + + +void CODE_push_void(void) +{ + LAST_CODE; + + use_stack(1); + write_ZZxx(C_PUSH_MISC, CPM_VOID); + + #ifdef DEBUG + printf("PUSH VOID\n"); + #endif +} + + +#ifdef PROJECT_COMP + +void CODE_stop_event(void) +{ + LAST_CODE; + + write_ZZxx(C_QUIT, 2); + + #ifdef DEBUG + printf("STOP EVENT\n"); + #endif +} + +#endif + + +void CODE_subr(short subr, short nparam, short optype, bool fixed) +{ + LAST_CODE; + + use_stack(1 - nparam); + + #ifdef DEBUG + printf("SUBR %d (%d)\n", subr, nparam); + #endif + + if (optype == 0) + { + if (fixed) + nparam = 0; + } + else + { + nparam = optype; + } + + subr += CODE_FIRST_SUBR; + write_short(((subr & 0xFF) << 8) | (nparam & 0xFF)); +} + + +/*void CODE_subr_output(short subr, short nparam, int output) +{ + LAST_CODE; + + use_stack(output - nparam); + + #ifdef DEBUG + printf("SUBR OUTPUT %d %d (%d)\n", output, subr, nparam); + #endif + + subr += CODE_FIRST_SUBR; + write_short(((subr & 0xFF) << 8) | (nparam & 0xFF)); +}*/ + + +void CODE_call(short nparam) +{ + LAST_CODE; + + use_stack(-nparam); + + #ifdef DEBUG + printf("CALL ( %d )\n", nparam); + #endif + + write_ZZxx(C_CALL, nparam); +} + +void CODE_byref(uint64_t byref) +{ + LAST_CODE; + int n; + + #ifdef DEBUG + printf("BYREF\n"); + #endif + + if (byref >> 48) + n = 3; + else if (byref >> 32) + n = 2; + else if (byref >> 16) + n = 1; + else + n = 0; + + write_ZZxx(C_BYREF, n); + while (n >= 0) + { + write_short(byref & 0xFFFF); + byref >>= 16; + n--; + } +} + +void CODE_call_byref(short nparam, uint64_t byref) +{ + LAST_CODE; + int i, n; + + use_stack(-nparam); + + n = 0; + for (i = 0; i < nparam; i++) + { + if (byref & (1ULL << i)) + n++; + } + use_stack(n); + + #ifdef DEBUG + printf("CALL ( %d )\n", nparam); + #endif + + write_ZZxx(C_CALL, nparam); + CODE_byref(byref); +} + + +/*void CODE_push_return(void) +{ + LAST_CODE; + + use_stack(1); + write_short(C_PUSH_RETURN); + + #ifdef DEBUG + printf("PUSH RETURN\n"); + #endif +}*/ + + +#ifdef PROJECT_COMP + +void CODE_try(void) +{ + LAST_CODE; + + write_short(C_TRY); + write_short(0); + + #ifdef DEBUG + printf("TRY\n"); + #endif +} + + +void CODE_end_try(void) +{ + LAST_CODE; + + write_short(C_END_TRY); + + #ifdef DEBUG + printf("END TRY\n"); + #endif +} + + +void CODE_catch(void) +{ + LAST_CODE; + + write_short(C_CATCH); + + #ifdef DEBUG + printf("CATCH\n"); + #endif +} + +#endif + + +void CODE_drop(void) +{ + //ushort *last_code = get_last_code(); + //ushort subr; + + use_stack(-1); + + #ifdef DEBUG + printf("DROP\n"); + #endif + + /*if (last_code) + { + switch(*last_code & 0xFF00) + { + case C_DROP: + *last_code = (*last_code & 0xFF00) + (*last_code & 0xFF) + 1; + return; + + case C_CALL: + *last_code |= CODE_CALL_VOID; + return; + + default: + subr = (*last_code) >> 8; + if (subr >= CODE_FIRST_SUBR && subr <= CODE_LAST_SUBR && (!(*last_code & CODE_CALL_VOID))) + { + *last_code |= CODE_CALL_VOID; + return; + } + } + }*/ + + //THROW("Internal compiler error: Bad stack drop!"); + + LAST_CODE; + + write_ZZxx(C_DROP, 1); +} + + +/*void CODE_push_special(short spec) +{ + LAST_CODE; + + use_stack(0); + + #ifdef DEBUG + printf("PUSH SPECIAL %d\n", spec); + #endif + + write_ZZxx(C_PUSH_SPECIAL, spec); +} +*/ + +#ifdef PROJECT_COMP + +void CODE_push_event(short event) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH EVENT %d\n", event); + #endif + + write_ZZxx(C_PUSH_EVENT, event); +} + +void CODE_push_extern(short index) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH EXTERN %d\n", index); + #endif + + write_ZZxx(C_PUSH_EXTERN, index); +} + +void CODE_new(ushort nparam, bool array, bool event) +{ + LAST_CODE; + + use_stack(1 - nparam); + + #ifdef DEBUG + printf("NEW %s (%d)\n", (array ? "ARRAY" : (event ? "EVENT" : "")), nparam); + #endif + + if (array) + nparam |= CODE_NEW_ARRAY; + + if (event) + nparam |= CODE_NEW_EVENT; + + write_ZZxx(C_NEW, nparam); +} + +#endif + +void CODE_push_boolean(bool value) +{ + LAST_CODE; + + use_stack(1); + write_ZZxx(C_PUSH_MISC, value ? CPM_TRUE : CPM_FALSE); + + #ifdef DEBUG + printf("PUSH %s\n", value ? "TRUE" : "FALSE"); + #endif +} + +void CODE_push_inf(bool neg) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH %cINF\n", neg ? '-' : '+'); + #endif + + write_ZZxx(C_PUSH_MISC, neg ? CPM_MINF : CPM_PINF); +} + +void CODE_push_complex(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("PUSH COMPLEX\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_COMPLEX); +} + +void CODE_push_vargs(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("PUSH VARGS\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_VARGS); +} + +void CODE_drop_vargs(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("DROP VARGS\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_DROP_VARGS); +} + +void CODE_end_vargs(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("END VARGS\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_END_VARGS); +} + +#ifdef CODE_DUMP + +void CODE_dump(PCODE *code, int count) +{ + int i; + + printf("\n"); + + for (i = 0; i < count;) + i += PCODE_dump(stdout, i, &code[i]); + + printf("\n"); + +} + +#endif + +void CODE_string_add(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("STOP\n"); + #endif + + write_ZZxx(C_QUIT, 3); +} + diff --git a/main/share/gb_common.h b/main/share/gb_common.h new file mode 100644 index 00000000..6e132472 --- /dev/null +++ b/main/share/gb_common.h @@ -0,0 +1,209 @@ +/*************************************************************************** + + gb_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_H +#define __GB_COMMON_H + +#include "config.h" + +#ifdef _GNU_SOURCE +#undef _GNU_SOURCE +#endif +#define _GNU_SOURCE 500 + +#define _FILE_OFFSET_BITS 64 + +#define __STDC_FORMAT_MACROS 1 +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 +#define __USE_ISOC99 1 +#define __USE_ISOC9X 1 + +#if defined(__cplusplus) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GNU_LIBRARY__) || defined(OS_BSD) + +#include +#define HAVE_GETOPT_LONG 1 + +#endif + +#if defined(OS_CYGWIN) + + typedef void (*sighandler_t) (int); + typedef unsigned long ulong; + +#endif + +#if defined(OS_BSD) + + /* sighandler_t is replaced by sig_t */ + #define sighandler_t sig_t + + typedef unsigned long ulong; + + #if defined(UINTPTR_MAX) && defined(UINT64_MAX) && (UINTPTR_MAX == UINT64_MAX) + #define __WORDSIZE 64 + #else + #define __WORDSIZE 32 + #endif + +#endif + +#ifdef OS_SOLARIS + +/* PGS: The following #define prevents /usr/include/sys/mman.h on solaris + from #define'ing PRIVATE to 0x20, thus breaking Gambas. + Perhaps Gambas should use a different name? + BM: I don't use PRIVATE anymore! +*/ + #ifdef _POSIX_C_SOURCE + /* PGS: Stop compiler warnings when gcc on solaris does remember to define + _POSIX_C_SOURCE, e.g. when compiling qt related files. */ + #undef _POSIX_C_SOURCE + #endif + + #define _POSIX_C_SOURCE 3 + /* Get prototype for alloca() */ + #include + /* Get definition for index() */ + #include + +#endif + +#ifdef OS_MACOSX + +#include +#undef bool + +#endif + +#ifndef FALSE + enum + { + FALSE = 0, + TRUE = 1 + }; +#endif + +#if !defined(__cplusplus) + + #define bool char + +#endif + +typedef + unsigned char uchar; + +typedef + size_t offset_t; + +#define PUBLIC +#define INLINE __attribute__((always_inline)) inline +#define NOINLINE __attribute__((noinline)) +#define EXTERN extern +#define PACKED __attribute__((packed)) +#define NORETURN __attribute__((noreturn)) +#define CONST __attribute__((const)) + + +#if __WORDSIZE == 64 +#define OS_64BITS 1 +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define OS_LITTLE_ENDIAN 1 +#else +#define OS_BIG_ENDIAN 1 +#endif + +#ifndef LLONG_MAX +#define LLONG_MAX 9223372036854775807LL +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4095 +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 4095 +#endif + +#define CLEAR(s) (memset(s, 0, sizeof(*s))) + +/* Workaround spurious gcc warnings */ +#define NO_WARNING(var) var = var + +#ifndef offsetof + #define offsetof(_type, _arg) ((size_t)&(((_type *)0)->_arg)) +#endif + +#define Max(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a > _b ? _a : _b; }) +#define Min(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a < _b ? _a : _b; }) +#define MinMax(v, a, b) ({ __typeof__(v) _v = (v), _a = (a), _b = (b); _v < _a ? _a : (_v > _b ? _b : _v); }) + +#if (defined (__i386__) || defined (__x86_64__)) && defined (__GNUC__) && __GNUC__ >= 2 + #define BREAKPOINT() { __asm__ __volatile__ ("int $03"); } +#elif (defined (_MSC_VER) || defined (__DMC__)) && defined (_M_IX86) + #define BREAKPOINT() { __asm int 3h }G_STMT_END +#elif defined (__alpha__) && !defined(__osf__) && defined (__GNUC__) && __GNUC__ >= 2 + #define BREAKPOINT() { __asm__ __volatile__ ("bpt"); } +#else /* !__i386__ && !__alpha__ */ + #define BREAKPOINT() { raise(SIGTRAP); } +#endif /* __i386__ */ + +#define COPYRIGHT "(c) Benoît Minisini\n\n" \ + "This program is free software; you can redistribute it and/or \n" \ + "modify it under the terms of the GNU General Public License as \n" \ + "published by the Free Software Foundation; either version 2, or \n" \ + "(at your option) any later version.\n\n" + +#define $(_x) _x + +#define RESTART_SYSCALL(_code) \ + for(;;) \ + { \ + errno = 0; \ + if ((_code) >= 0 || (errno != EINTR)) \ + break; \ + } if (errno) + +#endif /* __GB_COMMON_H */ diff --git a/main/share/gb_common_buffer.h b/main/share/gb_common_buffer.h new file mode 100644 index 00000000..c28c87c4 --- /dev/null +++ b/main/share/gb_common_buffer.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + gb_common_buffer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_BUFFER_H +#define __GB_COMMON_BUFFER_H + +#define COMMON_BUF_MAX PATH_MAX + +#ifndef __COMMON_BUFFER_C +EXTERN int COMMON_pos; +EXTERN int COMMON_len; +EXTERN char COMMON_buffer[]; +EXTERN char *COMMON_start; +EXTERN int COMMON_last; +#endif + +void COMMON_init(void); + +void COMMON_buffer_init(const char *str, int len); +void COMMON_jump_space(void); +bool COMMON_has_string(const char *str, int len); + +#define COMMON_get_char(void) (COMMON_last = (COMMON_pos >= COMMON_len) ? (int)-1 : (int)(unsigned char)(COMMON_start[COMMON_pos++])) + +#define COMMON_look_char() ((COMMON_pos >= COMMON_len) ? (int)-1 : (int)(unsigned char)(COMMON_start[COMMON_pos])) + +#define COMMON_put_char(_c) \ +({ \ + if (COMMON_pos >= COMMON_len) \ + (-1); \ + else \ + { \ + COMMON_start[COMMON_pos++] = (_c); \ + 0; \ + } \ +}) + +#define COMMON_last_char() (COMMON_last) + +#define COMMON_get_current() (&COMMON_start[COMMON_pos]) + +#define COMMON_get_size_left(void) (COMMON_len - COMMON_pos) + +#endif diff --git a/main/share/gb_common_buffer_temp.h b/main/share/gb_common_buffer_temp.h new file mode 100644 index 00000000..7c4673d5 --- /dev/null +++ b/main/share/gb_common_buffer_temp.h @@ -0,0 +1,65 @@ +/*************************************************************************** + + gb_common_buffer_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_BUFFER_C + +#include "gb_common.h" +#include "gb_common_buffer.h" + +char COMMON_buffer[COMMON_BUF_MAX]; +int COMMON_pos; +int COMMON_len; + +char *COMMON_start; +int COMMON_last; + +void COMMON_buffer_init(const char *str, int len) +{ + COMMON_start = (char *)str; + COMMON_len = len; + COMMON_pos = 0; + COMMON_last = (-1); +} + + +void COMMON_jump_space(void) +{ + int c; + + for(;;) + { + c = COMMON_look_char(); + if (c <= 0 || !isspace(c)) + break; + COMMON_pos++; + } +} + + +bool COMMON_has_string(const char *str, int len) +{ + if (COMMON_get_size_left() < len) + return FALSE; + + return memcmp(&COMMON_start[COMMON_pos], str, len) == 0; +} diff --git a/main/share/gb_common_case.h b/main/share/gb_common_case.h new file mode 100644 index 00000000..416f3a1b --- /dev/null +++ b/main/share/gb_common_case.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + gb_common_case.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_CASE_H +#define __GB_COMMON_CASE_H + +#ifdef __CYGWIN__ +#include +#endif + +#include +#include + +extern const unsigned char COMMON_tolower[]; +extern const unsigned char COMMON_toupper[]; + +int COMMON_strcasecmp(const char *s1, const char *s2); +int COMMON_strncasecmp(const char *s1, const char *s2, size_t n); + +#ifdef tolower +#undef tolower +#endif +#ifdef toupper +#undef toupper +#endif +#ifdef strcasecmp +#undef strcasecmp +#endif +#ifdef strncasecmp +#undef strncasecmp +#endif + +#define tolower(_c) (COMMON_tolower[(unsigned char)(_c)]) +#define toupper(_c) (COMMON_toupper[(unsigned char)(_c)]) +#define strcasecmp(_s1, _s2) (COMMON_strcasecmp((_s1), (_s2))) +#define strncasecmp(_s1, _s2, _n) (COMMON_strncasecmp((_s1), (_s2), (_n))) + +#define NO_GAMBAS_CASE_REPLACEMENT + +#endif diff --git a/main/share/gb_common_case_temp.h b/main/share/gb_common_case_temp.h new file mode 100644 index 00000000..c0a656e2 --- /dev/null +++ b/main/share/gb_common_case_temp.h @@ -0,0 +1,104 @@ +/*************************************************************************** + + gb_common_case_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_CASE_C + +#include +#include "gb_common.h" + +const unsigned char COMMON_tolower[256] = + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + " !\"#$%&'()*+,-./" + "0123456789:;<=>?" + "@abcdefghijklmno" + "pqrstuvwxyz[\\]^_" + "`abcdefghijklmno" + "pqrstuvwxyz{|}~\x7F" + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" + "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" + "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" + "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF" + "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; + +const unsigned char COMMON_toupper[256] = + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + " !\"#$%&'()*+,-./" + "0123456789:;<=>?" + "@ABCDEFGHIJKLMNO" + "PQRSTUVWXYZ[\\]^_" + "`ABCDEFGHIJKLMNO" + "PQRSTUVWXYZ{|}~\x7F" + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" + "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" + "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" + "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF" + "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; + + +int COMMON_strcasecmp(const char *s1, const char *s2) +{ + register offset_t i; + register int d; + register char c; + + for (i = 0;; i++) + { + c = COMMON_tolower[(unsigned char)s1[i]]; + d = c - COMMON_tolower[(unsigned char)s2[i]]; + if (d < 0) + return -1; + else if (d > 0) + return 1; + else if (c == 0) + return 0; + } +} + +int COMMON_strncasecmp(const char *s1, const char *s2, size_t n) +{ + register offset_t i; + register int d; + register char c; + + for (i = 0; i < n; i++) + { + c = COMMON_tolower[(unsigned char)s1[i]]; + d = c - COMMON_tolower[(unsigned char)s2[i]]; + if (d < 0) + return -1; + else if (d > 0) + return 1; + } + + return 0; +} + + diff --git a/main/share/gb_common_string.h b/main/share/gb_common_string.h new file mode 100644 index 00000000..64dc1e69 --- /dev/null +++ b/main/share/gb_common_string.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gb_common_string.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_STRING_H +#define __GB_COMMON_STRING_H + +bool STRING_equal_same(const char *str1, const char *str2, int len); +bool STRING_equal_ignore_case_same(const char *str1, const char *str2, int len); +int STRING_compare(const char *str1, int len1, const char *str2, int len2); +int STRING_compare_ignore_case(const char *str1, int len1, const char *str2, int len2); + +// valgrind says that STRING_equal_same() is globally faster than memcmp_sse4, so... +//#define STRING_equal_same(_str1, _str2, _len) (memcmp(_str1, _str2, _len) == 0) +#define STRING_equal(_str1, _len1, _str2, _len2) ((_len1) == (_len2) && STRING_equal_same(_str1, _str2, _len1)) +#define STRING_equal_ignore_case(_str1, _len1, _str2, _len2) ((_len1) == (_len2) && STRING_equal_ignore_case_same(_str1, _str2, _len1)) + +#endif diff --git a/main/share/gb_common_string_temp.h b/main/share/gb_common_string_temp.h new file mode 100644 index 00000000..a47d0713 --- /dev/null +++ b/main/share/gb_common_string_temp.h @@ -0,0 +1,197 @@ +/*************************************************************************** + + gb_common_string_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common_case.h" + +bool STRING_equal_same(const char *str1, const char *str2, int len) +{ + static const void *jump[8] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7 }; + + //return len == 0 || __builtin_memcmp(str1, str2, len) == 0; + + #if defined(ARCH_X86_64) || defined(ARCH_X86) + + for(;;) + { + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + } + + #else + + while (len >= 8) + { + if (str1[0] ^ str2[0]) + return FALSE; + if (str1[1] ^ str2[1]) + return FALSE; + if (str1[2] ^ str2[2]) + return FALSE; + if (str1[3] ^ str2[3]) + return FALSE; + if (str1[4] ^ str2[4]) + return FALSE; + if (str1[5] ^ str2[5]) + return FALSE; + if (str1[6] ^ str2[6]) + return FALSE; + if (str1[7] ^ str2[7]) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + } + + #endif + + goto *jump[len]; + +__LEN_7: if (str1[6] ^ str2[6]) return FALSE; +__LEN_6: if (str1[5] ^ str2[5]) return FALSE; +__LEN_5: if (str1[4] ^ str2[4]) return FALSE; +__LEN_4: if (str1[3] ^ str2[3]) return FALSE; +__LEN_3: if (str1[2] ^ str2[2]) return FALSE; +__LEN_2: if (str1[1] ^ str2[1]) return FALSE; +__LEN_1: if (str1[0] ^ str2[0]) return FALSE; +__LEN_0: return TRUE; +} + +bool STRING_equal_ignore_case_same(const char *str1, const char *str2, int len) +{ + static const void *jump[8] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7 }; + + #if defined(ARCH_X86_64) || defined(ARCH_X86) + while (len >= 8) + { + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + { + if (toupper(str1[0]) ^ toupper(str2[0])) return FALSE; + if (toupper(str1[1]) ^ toupper(str2[1])) return FALSE; + if (toupper(str1[2]) ^ toupper(str2[2])) return FALSE; + if (toupper(str1[3]) ^ toupper(str2[3])) return FALSE; + if (toupper(str1[4]) ^ toupper(str2[4])) return FALSE; + if (toupper(str1[5]) ^ toupper(str2[5])) return FALSE; + if (toupper(str1[6]) ^ toupper(str2[6])) return FALSE; + if (toupper(str1[7]) ^ toupper(str2[7])) return FALSE; + } + + str1 += 8; + str2 += 8; + len -= 8; + } + #else + while (len >= 8) + { + if (str1[0] ^ str2[0] && toupper(str1[0]) ^ toupper(str2[0])) return FALSE; + if (str1[1] ^ str2[1] && toupper(str1[1]) ^ toupper(str2[1])) return FALSE; + if (str1[2] ^ str2[2] && toupper(str1[2]) ^ toupper(str2[2])) return FALSE; + if (str1[3] ^ str2[3] && toupper(str1[3]) ^ toupper(str2[3])) return FALSE; + if (str1[4] ^ str2[4] && toupper(str1[4]) ^ toupper(str2[4])) return FALSE; + if (str1[5] ^ str2[5] && toupper(str1[5]) ^ toupper(str2[5])) return FALSE; + if (str1[6] ^ str2[6] && toupper(str1[6]) ^ toupper(str2[6])) return FALSE; + if (str1[7] ^ str2[7] && toupper(str1[7]) ^ toupper(str2[7])) return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + } + #endif + + goto *jump[len]; + +__LEN_7: if (str1[6] ^ str2[6] && toupper(str1[6]) ^ toupper(str2[6])) return FALSE; +__LEN_6: if (str1[5] ^ str2[5] && toupper(str1[5]) ^ toupper(str2[5])) return FALSE; +__LEN_5: if (str1[4] ^ str2[4] && toupper(str1[4]) ^ toupper(str2[4])) return FALSE; +__LEN_4: if (str1[3] ^ str2[3] && toupper(str1[3]) ^ toupper(str2[3])) return FALSE; +__LEN_3: if (str1[2] ^ str2[2] && toupper(str1[2]) ^ toupper(str2[2])) return FALSE; +__LEN_2: if (str1[1] ^ str2[1] && toupper(str1[1]) ^ toupper(str2[1])) return FALSE; +__LEN_1: if (str1[0] ^ str2[0] && toupper(str1[0]) ^ toupper(str2[0])) return FALSE; +__LEN_0: return TRUE; +} + +int STRING_compare(const char *str1, int len1, const char *str2, int len2) +{ + uint i; + int len = len1 < len2 ? len1 : len2; + int diff; + register unsigned char c1, c2; + + for (i = 0; i < len; i++) + { + c1 = str1[i]; + c2 = str2[i]; + if (c1 > c2) return 1; + if (c1 < c2) return -1; + } + + diff = len1 - len2; + return (diff < 0) ? (-1) : (diff > 0) ? 1 : 0; +} + + +int STRING_compare_ignore_case(const char *str1, int len1, const char *str2, int len2) +{ + uint i; + int len = len1 < len2 ? len1 : len2; + int diff; + register unsigned char c1, c2; + + for (i = 0; i < len; i++) + { + c1 = tolower(str1[i]); + c2 = tolower(str2[i]); + if (c1 > c2) return 1; + if (c1 < c2) return -1; + } + + diff = len1 - len2; + return (diff < 0) ? (-1) : (diff > 0) ? 1 : 0; +} diff --git a/main/share/gb_common_swap.h b/main/share/gb_common_swap.h new file mode 100644 index 00000000..d9ce1d48 --- /dev/null +++ b/main/share/gb_common_swap.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb_common_swap.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_SWAP_H +#define __GB_COMMON_SWAP_H + +void SWAP_int(int *val); +void SWAP_ints(int *val, int n); + +void SWAP_short(short *val); +void SWAP_double(double *val); + +#define SWAP_float(_val) SWAP_int((int *)_val) +#define SWAP_int64(_val) SWAP_double((double *)(void *)_val) + +#if OS_64BITS +#define SWAP_pointer(_val) SWAP_int64(_val) +#else +#define SWAP_pointer(_val) SWAP_int(((int *)(void *)_val)) +#endif + +#endif diff --git a/main/share/gb_common_swap_temp.h b/main/share/gb_common_swap_temp.h new file mode 100644 index 00000000..b7672899 --- /dev/null +++ b/main/share/gb_common_swap_temp.h @@ -0,0 +1,71 @@ +/*************************************************************************** + + gb_common_swap_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +void SWAP_int(int *val) +{ + char *p = (char *)val; + char t1, t2; + + t1 = p[0]; + t2 = p[1]; + p[0] = p[3]; + p[1] = p[2]; + p[3] = t1; + p[2] = t2; +} + +void SWAP_ints(int *val, int n) +{ + while (n > 0) + { + SWAP_int(val); + val++; + n--; + } +} + +void SWAP_short(short *val) +{ + char *p = (char *)val; + char t; + + t = p[0]; + p[0] = p[1]; + p[1] = t; +} + +void SWAP_double(double *val) +{ + char *p = (char *)val; + char t; + int i, j; + + for (i = 0; i < 4; i++) + { + j = i ^ 7; + t = p[i]; + p[i] = p[j]; + p[j] = t; + } +} + diff --git a/main/share/gb_component.h b/main/share/gb_component.h new file mode 100644 index 00000000..79acabc9 --- /dev/null +++ b/main/share/gb_component.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + gb_component.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMPONENT_H +#define __GB_COMPONENT_H + +#include "config.h" + +typedef + struct { + int version; + void *func[0]; + } + INTERFACE; + +#define LIB_INIT "GB_INIT" +#define LIB_AFTER_INIT "GB_AFTER_INIT" +#define LIB_EXIT "GB_EXIT" +#define LIB_INTERFACE "GB_INTERFACE" +#define LIB_CLASS "GB_CLASSES" +#define LIB_OPTIONAL "GB_OPTIONAL_CLASSES" +#define LIB_INCLUDE "GB_INCLUDE" +#define LIB_SIGNAL "GB_SIGNAL" +#define LIB_INFO "GB_INFO" +#define LIB_NEED "GB_NEED" +#define LIB_GAMBAS "GB" +#define LIB_JIT "JIT" +#define LIB_MAIN "GB_MAIN" +#define LIB_FORK "GB_FORK" + +#ifdef DONT_USE_LTDL + #if defined(OS_MACOSX) + #define LIB_PATTERN "%s/%s.so" + #elif defined(OS_OPENBSD) + #define LIB_PATTERN "%s/%s." SHARED_LIBRARY_EXT ".0.0" + #elif defined(OS_CYGWIN) + #define LIB_PATTERN "%s/%s-0." SHARED_LIBRARY_EXT + #else + #define LIB_PATTERN "%s/%s." SHARED_LIBRARY_EXT + #endif +#else +#define LIB_PATTERN "%s/%s.la" +#endif + +#define ARCH_PATTERN "%s/%s.gambas" + +#define GAMBAS_LINK_PATH "/usr/bin/gbx" GAMBAS_VERSION_STRING + +#define GAMBAS_LIB_PATH "lib/gambas" GAMBAS_VERSION_STRING +#define GAMBAS_LIB64_PATH "lib64/gambas" GAMBAS_VERSION_STRING + +#endif diff --git a/main/share/gb_error_common.h b/main/share/gb_error_common.h new file mode 100644 index 00000000..dab4d0a7 --- /dev/null +++ b/main/share/gb_error_common.h @@ -0,0 +1,213 @@ +/*************************************************************************** + + gb_error_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_COMMON_H +#define __GB_ERROR_COMMON_H + +typedef + struct { + char code; // Error code + char native; // A native method has raised an error + char free; // If 'msg' sould be freed + char _reserved; + void *cp; + void *fp; + void *pc; + char *msg; + } + ERROR_INFO; + +typedef + struct _ERROR_CONTEXT { + struct _ERROR_CONTEXT *prev; + struct _ERROR_HANDLER *handler; + ERROR_INFO info; + jmp_buf env; + char ret; + } + ERROR_CONTEXT; + +typedef + struct _ERROR_HANDLER { + struct _ERROR_HANDLER *prev; + ERROR_CONTEXT *context; + void (*handler)(); + intptr_t arg1; + intptr_t arg2; + } + ERROR_HANDLER; + +#ifdef NO_ERROR_HANDLER +#define ERROR_handler NULL +#endif + +#define ERROR_LEAVE_DONE ((ERROR_CONTEXT *)-1) + +#if DEBUG_ERROR + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_debug("TRY %s %p\n", __FUNCTION__, &__err_context); \ + ERROR_depth++; \ + ERROR_enter(&__err_context); \ + __err_context.ret = setjmp(__err_context.env); \ + if (__err_context.ret == 0) + +/*#define CATCH \ + fprintf(stderr, "%p == %p ? %d\n", ERROR_current, __err, __err->ret); \ + if (__err->ret != 0 && (__err->ret = 2))*/ + +#define CATCH \ + if (__err_context.ret) { ERROR_depth--; ERROR_debug("CATCH %s %p\n", __FUNCTION__, &__err_context); ERROR_depth++; } \ + if (__err_context.ret) + +#define END_TRY \ + ERROR_depth--; \ + ERROR_debug("END TRY %s %p\n", __FUNCTION__, &__err_context); \ + ERROR_depth++; \ + ERROR_leave(&__err_context); \ + } \ + } + +#define PROPAGATE() fprintf(stderr, "PROPAGATE %s\n", __FUNCTION__), ERROR_propagate() + +#define ERROR_enter(_err) \ +do { \ + (_err)->prev = ERROR_current; \ + (_err)->info.code = 0; \ + (_err)->info.native = 0; \ + (_err)->handler = ERROR_handler; \ + ERROR_current = (_err); \ +} while(0) + +#define ERROR_leave(_err) \ +do { \ + ERROR_CONTEXT *_prev = (_err); \ + ERROR_depth--; \ + if (_prev->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_current = _prev->prev; \ + if (ERROR_current) \ + { \ + if (_prev->info.code) \ + { \ + ERROR_reset(&ERROR_current->info); \ + ERROR_current->info = _prev->info; \ + } \ + } \ + else \ + ERROR_reset(&_prev->info); \ + _prev->prev = ERROR_LEAVE_DONE; \ + } \ +} while(0) + +#else /* DEBUG_ERROR */ + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_CONTEXT *__err = &__err_context; \ + ERROR_enter(__err); \ + __err->ret = setjmp(__err->env); \ + if (__err->ret == 0) + +#define CATCH \ + else + +#define END_TRY \ + ERROR_leave(__err); \ + } \ + } + +#define PROPAGATE() ERROR_propagate() + +#define ERROR_enter(_err) \ +do { \ + _err->prev = ERROR_current; \ + _err->info.code = 0; \ + _err->info.native = 0; \ + _err->handler = ERROR_handler; \ + ERROR_current = _err; \ +} while(0) + +#ifdef GB_JIT + +static void _error_leave(ERROR_CONTEXT *_err) +{ + ERROR_CONTEXT *_prev = _err; + if (_prev->prev != ERROR_LEAVE_DONE) + { + ERROR_current = _prev->prev; + if (ERROR_current) + { + if (_prev->info.code) + { + ERROR_reset(&ERROR_current->info); + ERROR_current->info = _prev->info; + ERROR_current->info.native = FALSE; + } + } + else + ERROR_reset(&_prev->info); + _prev->prev = ERROR_LEAVE_DONE; + } +} + +#define ERROR_leave(_err) _error_leave(_err) + +#else + +#define ERROR_leave(_err) \ +do { \ + ERROR_CONTEXT *_prev = (_err); \ + if (_prev->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_current = _prev->prev; \ + if (ERROR_current) \ + { \ + if (_prev->info.code) \ + { \ + ERROR_reset(&ERROR_current->info); \ + ERROR_current->info = _prev->info; \ + ERROR_current->info.native = FALSE; \ + } \ + } \ + else \ + ERROR_reset(&_prev->info); \ + _prev->prev = ERROR_LEAVE_DONE; \ + } \ +} while(0) + +#endif + +#endif + +#define ERROR (&__err_context) + +#define ERROR_in_catch(_err) ((_err)->ret) + +#endif + diff --git a/main/share/gb_file_share.h b/main/share/gb_file_share.h new file mode 100644 index 00000000..81aefa35 --- /dev/null +++ b/main/share/gb_file_share.h @@ -0,0 +1,143 @@ +/*************************************************************************** + + gb_file_share.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FILE_H +#define __GB_FILE_H + +#include + +#include "gb_common.h" + +#ifdef PROJECT_EXEC + +#include "gambas.h" + +typedef + GB_FILE_STAT FILE_STAT; + +/* Constants for Stat() function */ + +#define GB_STAT_FILE 1 +#define GB_STAT_DIRECTORY 2 +#define GB_STAT_DEVICE 3 +#define GB_STAT_PIPE 4 +#define GB_STAT_SOCKET 5 +#define GB_STAT_LINK 6 + +#define GB_STAT_READ R_OK +#define GB_STAT_WRITE W_OK +#define GB_STAT_EXEC X_OK + +#define GB_STAT_USER 0 +#define GB_STAT_GROUP 1 +#define GB_STAT_OTHER 2 + +#define FILE_TEMP_PREFIX "/tmp/gambas.%d" +#define FILE_TEMP_DIR FILE_TEMP_PREFIX "/%d" +#define FILE_TEMP_FILE FILE_TEMP_DIR "/%d.tmp" +#define FILE_TEMP_PATTERN FILE_TEMP_DIR "/%s.tmp" + +#endif + +#ifndef GBX_INFO + +const char *FILE_cat(const char *path, ...); +char *FILE_buffer(void); +int FILE_buffer_length(void); +const char *FILE_get_dir(const char *path); +const char *FILE_get_name(const char *path); +const char *FILE_get_ext(const char *path); +const char *FILE_get_basename(const char *path); +/*PUBLIC const char *FILE_get(const char *path);*/ +const char *FILE_set_ext(const char *path, const char *ext); + +const char *FILE_getcwd(const char *subdir); +#define FILE_get_current_dir() FILE_getcwd(NULL) +void FILE_chdir(const char *path); + +const char *FILE_readlink(const char *link); +bool FILE_is_dir(const char *path); + +const char *FILE_find_gambas(void); + +void FILE_rename_ext(const char *src, const char *dst, bool unlink); +#define FILE_rename(_src, _dst) FILE_rename_ext(_src, _dst, FALSE) +#define FILE_rename_unlink(_src, _dst) FILE_rename_ext(_src, _dst, TRUE) + +void FILE_unlink(const char *path); + +char *FILE_get_home(void); +void FILE_exit(void); + +#ifdef PROJECT_EXEC + +void FILE_init(void); +void FILE_remove_temp_file(void); +void FILE_remove_temp_file_pid(pid_t pid); + +bool FILE_exist_follow(const char *path, bool follow); +#define FILE_exist(_path) FILE_exist_follow(_path, FALSE) +bool FILE_exist_real(const char *path); + +void FILE_stat(const char *path, FILE_STAT *info, bool follow); +void FILE_dir_first(const char *path, const char *pattern, int attr); +bool FILE_dir_next(char **path, int *len); + +void FILE_rmdir(const char *path); +void FILE_mkdir_mode(const char *path, mode_t mode); +#define FILE_mkdir(_path) FILE_mkdir_mode((_path), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) +void FILE_copy(const char *src, const char *dst); + +bool FILE_access(const char *path, int mode); +void FILE_link(const char *src, const char *dst); + +char *FILE_make_temp(int *len, const char *pattern); + +void FILE_recursive_dir(const char *dir, void (*found)(const char *), void (*afterfound)(const char *), int attr, bool follow); + +void FILE_make_path_dir(const char *path); + +int64_t FILE_free(const char *path); + +char *FILE_mode_to_string(mode_t mode); +mode_t FILE_mode_from_string(mode_t mode, const char *str); + +void FILE_chmod(const char *path, mode_t mode); +void FILE_chown(const char *path, const char *user); +void FILE_chgrp(const char *path, const char *group); + +#else + +bool FILE_exist(const char *path); +time_t FILE_get_time(const char *path); +size_t FILE_get_size(const char *path); +bool FILE_copy(const char *src, const char *dst); + +#endif + +#define FILE_is_absolute(_path) (*(_path) == '/' || * (_path) == '~') +#define FILE_is_relative(_path) (!FILE_is_absolute(_path)) + +#endif + +#endif diff --git a/main/share/gb_file_temp.h b/main/share/gb_file_temp.h new file mode 100644 index 00000000..5d51813e --- /dev/null +++ b/main/share/gb_file_temp.h @@ -0,0 +1,1308 @@ +/*************************************************************************** + + gb_file_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_FILE_C + +#include "config.h" + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_component.h" + +#include +#include +#include +#include +#include +#include + +#ifdef PROJECT_EXEC + +#include + +#include +#include + +#endif + +#include "gb_file.h" + +#define OPT_NLINK 1 +#define OPT_FSTATAT 1 + +#ifdef PROJECT_EXEC + +//FILE_STAT FILE_stat_info = { 0 }; + +static DIR *file_dir = NULL; +static char *file_pattern = NULL; +static char *file_path = NULL; +static bool file_dir_arch = FALSE; +static int file_attr; +static char *file_rdir_path = NULL; +static bool _temp_used = FALSE; + +#endif + +static char file_buffer[PATH_MAX + 16]; +static int file_buffer_length; + +#ifdef _DIRENT_HAVE_D_TYPE +#undef _DIRENT_HAVE_D_TYPE +#endif + +#ifdef _DIRENT_HAVE_D_TYPE +static bool _last_is_dir; +#endif + +static char *_home = NULL; +static uid_t _uid = 0; + +#ifdef PROJECT_EXEC + +typedef + struct _path { + struct _path *next; + char *path; + } + FILE_PATH; + +typedef + struct { + unsigned int mask; + unsigned char test[4]; + unsigned int mode[4]; + } + FILE_MODE_DECODE; + +static void push_path(void **list, const char *path) +{ + FILE_PATH *slot; + + ALLOC(&slot, sizeof(FILE_PATH)); + slot->path = STRING_new_zero(path); + + slot->next = *list; + *list = slot; + + //printf("push_path: %s\n", path); +} + + +static char *pop_path(void **list) +{ + char *path; + FILE_PATH *slot; + + if (!*list) + return NULL; + + path = ((FILE_PATH *)*list)->path; + slot = *list; + *list = ((FILE_PATH *)*list)->next; + FREE(&slot); + + //printf("pop_path: %s\n", path); + return path; +} + + +static void dir_exit(void) +{ + if (file_dir != NULL) + { + closedir(file_dir); + file_dir = NULL; + } + + STRING_free(&file_pattern); + STRING_free(&file_path); +} + + +char *FILE_make_temp(int *len, const char *pattern) +{ + static int count = 0; + + _temp_used = TRUE; + + if (len) + { + if (pattern) + *len = snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_PATTERN, (int)getuid(), (int)getpid(), pattern); + else + { + count++; + *len = snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_FILE, (int)getuid(), (int)getpid(), count); + } + } + else + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_DIR, (int)getuid(), (int)getpid()); + + return file_buffer; +} + +static void remove_temp_file(const char *path) +{ + if (FILE_is_dir(path)) + { + //fprintf(stderr, "rmdir: %s\n", path); + rmdir(path); + } + else + { + //fprintf(stderr, "unlink: %s\n", path); + unlink(path); + } +} + +void FILE_remove_temp_file(void) +{ + if (_temp_used) + FILE_recursive_dir(FILE_make_temp(NULL, NULL), NULL, remove_temp_file, 0, FALSE); + rmdir(FILE_make_temp(NULL, NULL)); + _temp_used = FALSE; +} + +void FILE_remove_temp_file_pid(pid_t pid) +{ + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_DIR, (int)getuid(), (int)pid); + FILE_recursive_dir(file_buffer, NULL, remove_temp_file, 0, FALSE); + rmdir(FILE_make_temp(NULL, NULL)); +} + +void FILE_init(void) +{ + struct stat info; + + _temp_used = TRUE; + FILE_remove_temp_file(); + + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_PREFIX, (int)getuid()); + (void)mkdir(file_buffer, S_IRWXU); + + if (lstat(file_buffer, &info) == 0 && S_ISDIR(info.st_mode) && chown(file_buffer, getuid(), getgid()) == 0 && chmod(file_buffer, S_IRWXU) == 0) + { + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_DIR, (int)getuid(), (int)getpid()); + (void)mkdir(file_buffer, S_IRWXU); + if (lstat(file_buffer, &info) == 0 && S_ISDIR(info.st_mode) && chown(file_buffer, getuid(), getgid()) == 0 && chmod(file_buffer, S_IRWXU) == 0) + return; + } + + ERROR_fatal("cannot initialize interpreter temporary directory. Do you try to hijack Gambas?"); +} + +void FILE_exit(void) +{ + STRING_free(&_home); + FILE_remove_temp_file(); + STRING_free(&file_rdir_path); + dir_exit(); +} + +#else + +void FILE_exit(void) +{ + STR_free(_home); +} + +#endif + +static char *stradd(char *d, const char *s) +{ + for(;;) + { + if ((*d = *s) == 0) + break; + d++; + s++; + } + + return d; +} + +/*#define stradd(d, s) \ +({ \ + char *_d = (d); \ + const char *_s = (s); \ + \ + for(;;) \ + { \ + if ((*_d = *_s) == 0) \ + break; \ + \ + _d++; \ + _s++; \ + } \ + \ + _d; \ +})*/ + +const char *FILE_cat(const char *path, ...) +{ + char *p; + va_list args; + int len; + bool end_slash = FALSE; + bool add_slash = FALSE; + + va_start(args, path); + + p = file_buffer; + + if (path != file_buffer) + *p = 0; + + for(;;) + { + if (*path == '/' && p != file_buffer) + path++; + + len = strlen(path); + if (add_slash) + len++; + + if (len > 0) + { + if ((p + len) > &file_buffer[PATH_MAX]) + THROW(E_TOOLONG); + + if (p != path) + { + if (add_slash) + p = stradd(p, "/"); + + p = stradd(p, path); + } + else + p += len; + + end_slash = (p[-1] == '/'); + } + + path = va_arg(args, char *); + if (path == NULL) + break; + + add_slash = ((!end_slash) && (*path != 0) && (*path != '/')); + } + + va_end(args); + + file_buffer_length = p - file_buffer; + return file_buffer; +} + + +char *FILE_buffer(void) +{ + return file_buffer; +} + + +int FILE_buffer_length(void) +{ + if (file_buffer_length < 0) + file_buffer_length = strlen(file_buffer); + + return file_buffer_length; +} + +static void init_file_buffer(const char *path) +{ + int len; + + if (path == file_buffer) + return; + + len = strlen(path); + + if (len > PATH_MAX) + THROW(E_TOOLONG); + + strcpy(file_buffer, path); + file_buffer_length = len; +} + +//#define INIT_FILE_BUFFER(_path) (init_file_buffer(), _path = file_buffer) + +const char *FILE_get_dir(const char *path) +{ + char *p; + + if (path == NULL || path[0] == 0) + return NULL; + + if (path[0] == '/' && path[1] == 0) + return "/"; + + init_file_buffer(path); + + p = rindex(file_buffer, '/'); + + if (p == NULL) + *file_buffer = 0; + else + { + *p = 0; + + if (file_buffer[0] == 0 && path[0] == '/') + strcpy(file_buffer, "/"); + } + + file_buffer_length = -1; + return file_buffer; +} + + +const char *FILE_get_name(const char *path) +{ + const char *p; + + p = rindex(path, '/'); + if (p) + return &p[1]; + else + return path; +} + + +const char *FILE_get_ext(const char *path) +{ + const char *p; + + p = rindex(path, '/'); + if (p) + path = &p[1]; + + p = rindex(path, '.'); + if (p == NULL) + return &path[strlen(path)]; + else + return p + 1; +} + + +const char *FILE_set_ext(const char *path, const char *ext) +{ + char *p; + + init_file_buffer(path); + + p = (char *)FILE_get_ext(file_buffer); + + if (!ext) + { + if (p > file_buffer && p[-1] == '.') + p[-1] = 0; + else + *p = 0; + return file_buffer; + } + + if (&p[strlen(ext)] >= &file_buffer[PATH_MAX]) + THROW(E_TOOLONG); + + if (p == file_buffer || p[-1] != '.') + *p++ = '.'; + + if (*ext == '.') + ext++; + + strcpy(p, ext); + + file_buffer_length = -1; + return file_buffer; +} + + +const char *FILE_get_basename(const char *path) +{ + char *p; + + path = FILE_get_name(path); + + init_file_buffer(path); + + p = rindex(file_buffer, '.'); + if (p) + *p = 0; + + file_buffer_length = -1; + return file_buffer; +} + +bool FILE_is_dir(const char *path) +{ + struct stat buf; + +#ifdef PROJECT_EXEC + + if (FILE_is_relative(path)) + { + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (lstat(path, &buf) == 0) + goto __OK; + }*/ + + return ARCHIVE_is_dir(NULL, path); + } + +#endif + + if (stat(path, &buf)) + return FALSE; + +/*#ifdef PROJECT_EXEC +__OK: +#endif*/ + + return (S_ISDIR(buf.st_mode)); +} + + +#ifdef PROJECT_EXEC + +bool FILE_exist_real(const char *path) +{ + struct stat buf; + + if (chdir(PROJECT_path)) return FALSE; + return (stat(path, &buf) == 0); +} + +void FILE_stat(const char *path, FILE_STAT *info, bool follow) +{ + struct stat buf; + int ret; + + //fprintf(stderr, "FILE_stat: %s\n", path); + + if (FILE_is_relative(path)) + { + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (lstat(path, &buf) == 0) + goto _OK; + }*/ + + ARCHIVE_stat(NULL, path, info); + return; + } + + if (follow) + ret = stat(path, &buf); + else + ret = lstat(path, &buf); + + if (ret) + THROW_SYSTEM(errno, path); + +//_OK: + + if (S_ISREG(buf.st_mode)) + info->type = GB_STAT_FILE; + else if (S_ISDIR(buf.st_mode)) + info->type = GB_STAT_DIRECTORY; + else if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) + info->type = GB_STAT_DEVICE; + else if (S_ISFIFO(buf.st_mode)) + info->type = GB_STAT_PIPE; + else if (S_ISSOCK(buf.st_mode)) + info->type = GB_STAT_SOCKET; + else if (S_ISLNK(buf.st_mode)) + info->type = GB_STAT_LINK; + + info->mode = buf.st_mode & 07777; + info->size = buf.st_size; + info->atime = (int)buf.st_atime; + info->mtime = (int)buf.st_mtime; + info->ctime = (int)buf.st_ctime; + info->hidden = (*FILE_get_name(path) == '.'); + info->uid = buf.st_uid; + info->gid = buf.st_gid; + info->device = buf.st_dev; + info->blkdev = S_ISBLK(buf.st_mode); + info->chrdev = S_ISCHR(buf.st_mode); +} + +char *FILE_mode_to_string(mode_t mode) +{ + char *str = file_buffer; + + str[0] = mode & S_IRUSR ? 'r' : '-'; + str[1] = mode & S_IWUSR ? 'w' : '-'; + str[2] = (mode & S_ISUID + ? (mode & S_IXUSR ? 's' : 'S') + : (mode & S_IXUSR ? 'x' : '-')); + str[3] = mode & S_IRGRP ? 'r' : '-'; + str[4] = mode & S_IWGRP ? 'w' : '-'; + str[5] = (mode & S_ISGID + ? (mode & S_IXGRP ? 's' : 'S') + : (mode & S_IXGRP ? 'x' : '-')); + str[6] = mode & S_IROTH ? 'r' : '-'; + str[7] = mode & S_IWOTH ? 'w' : '-'; + str[8] = (mode & S_ISVTX + ? (mode & S_IXOTH ? 't' : 'T') + : (mode & S_IXOTH ? 'x' : '-')); + str[9] = 0; + + file_buffer_length = 9; + + return str; +} + +mode_t FILE_mode_from_string(mode_t mode, const char *str) +{ + static FILE_MODE_DECODE decode[] = { + { S_IRUSR, { '-', 'r' }, { 0, S_IRUSR } }, + { S_IWUSR, { '-', 'w' }, { 0, S_IWUSR } }, + { S_IXUSR | S_ISUID, { '-', 'x', 'S', 's' }, { 0, S_IXUSR, S_ISUID, S_IXUSR | S_ISUID } }, + { S_IRGRP, { '-', 'r' }, { 0, S_IRGRP } }, + { S_IWGRP, { '-', 'w' }, { 0, S_IWGRP } }, + { S_IXGRP | S_ISGID, { '-', 'x', 'S', 's' }, { 0, S_IXGRP, S_ISGID, S_IXGRP | S_ISGID } }, + { S_IROTH, { '-', 'r' }, { 0, S_IROTH } }, + { S_IWOTH, { '-', 'w' }, { 0, S_IWOTH } }, + { S_IXOTH | S_ISVTX, { '-', 'x', 'T', 't' }, { 0, S_IXOTH, S_ISVTX, S_IXOTH | S_ISVTX } }, + { 0 } + }; + + unsigned char c, test; + FILE_MODE_DECODE *d = decode; + int i; + + while (d->mask) + { + c = *str++; + if (!c) + break; + + for (i = 0; i <= 3; i++) + { + test = d->test[i]; + if (!test) + break; + if (c == test) + { + mode = (mode & ~(d->mask)) | d->mode[i]; + break; + } + } + + d++; + } + + return mode; +} + + +void FILE_chmod(const char *path, mode_t mode) +{ + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (chmod(path, mode)) + THROW_SYSTEM(errno, path); +} + + +void FILE_chown(const char *path, const char *user) +{ + struct passwd *pwd; + uid_t uid; + + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (!strcmp(user, "root")) + uid = (uid_t)0; + else + { + errno = 0; + pwd = getpwnam(user); + if (errno) + THROW_SYSTEM(errno, path); + if (!pwd) + THROW(E_USER); + uid = pwd->pw_uid; + } + + if (chown(path, uid, (gid_t)-1)) + THROW_SYSTEM(errno, path); +} + + +void FILE_chgrp(const char *path, const char *group) +{ + struct group *grp; + gid_t gid; + + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (!strcmp(group, "root")) + gid = (gid_t)0; + else + { + errno = 0; + grp = getgrnam(group); + if (errno) + THROW_SYSTEM(errno, path); + if (!grp) + THROW(E_USER); + gid = grp->gr_gid; + } + + if (chown(path, (uid_t)-1, gid)) + THROW_SYSTEM(errno, path); +} + + +void FILE_dir_first(const char *path, const char *pattern, int attr) +{ + dir_exit(); + + if (!path || *path == 0) + path = "."; + + if (attr == (GB_STAT_FILE | GB_STAT_DIRECTORY)) + attr = 0; + + file_attr = attr; + + if (FILE_is_relative(path)) + { + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (lstat(path, &buf) == 0) + goto _OK; + }*/ + + file_dir_arch = TRUE; + ARCHIVE_dir_first(NULL, path, pattern, attr); + return; + } + + file_dir_arch = FALSE; + file_dir = opendir(path); + if (file_dir == NULL) + THROW_SYSTEM(errno, path); + + file_pattern = STRING_new_zero(pattern); + file_path = STRING_new_zero(path); +} + +#if OPT_FSTATAT +#else + #ifdef FSTATAT + #undef FSTATAT + #endif +#endif + +bool FILE_dir_next(char **path, int *len) +{ + struct dirent *entry; + int len_entry; + bool ret; + char *name; + #ifndef _DIRENT_HAVE_D_TYPE + struct stat info; + #ifndef HAVE_FSTATAT + char *p = file_buffer; + #endif + #endif + + if (file_dir_arch) + { + ret = ARCHIVE_dir_next(path, len, file_attr); + if (ret) + file_dir_arch = FALSE; + return ret; + } + + if (file_dir == NULL) + return TRUE; + + #ifndef _DIRENT_HAVE_D_TYPE + #ifndef HAVE_FSTATAT + if (file_attr) + { + init_file_buffer(file_path); + p += file_buffer_length; + + if (p[-1] != '/' && (file_buffer[1] || file_buffer[0] != '/')) + *p++ = '/'; + } + #endif + #endif + + for(;;) + { + entry = readdir(file_dir); + if (entry == NULL) + { + dir_exit(); + return TRUE; + } + + name = entry->d_name; + + if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) + continue; + + len_entry = strlen(name); + if ((len_entry + file_buffer_length) > PATH_MAX) + continue; + + if (file_attr) + { + #ifdef _DIRENT_HAVE_D_TYPE + if ((file_attr == GB_STAT_DIRECTORY) ^ (entry->d_type == DT_DIR)) + continue; + #else + #if HAVE_FSTATAT + if (fstatat(dirfd(file_dir), name, &info, 0)) + #else + strcpy(p, name); + if (stat(file_buffer, &info)) + #endif + { + if (file_attr == GB_STAT_DIRECTORY) + continue; + } + else if ((file_attr == GB_STAT_DIRECTORY) ^ (S_ISDIR(info.st_mode) != 0)) + continue; + #endif + } + + if (file_pattern == NULL) + break; + + if (REGEXP_match(file_pattern, STRING_length(file_pattern), name, len_entry)) + break; + } + + *path = name; + *len = len_entry; + #ifdef _DIRENT_HAVE_D_TYPE + _last_is_dir = entry->d_type == DT_DIR; + #endif + + return FALSE; +} + +//#undef _DIRENT_HAVE_D_TYPE + +void FILE_recursive_dir(const char *dir, void (*found)(const char *), void (*afterfound)(const char *), int attr, bool follow) +{ + void *list = NULL; + void *dir_list = NULL; + char *file; + int len; + char *path; + //struct stat buf; + #ifdef _DIRENT_HAVE_D_TYPE + #else + FILE_STAT info; + #endif + char *temp; + bool is_dir; + #if OPT_NLINK + int nsubdir = -1; + #endif + + if (!dir || *dir == 0) + dir = "."; + else if (!FILE_is_dir(dir)) + return; + + STRING_free(&file_rdir_path); + file_rdir_path = STRING_new_zero(dir); + + FILE_dir_first(dir, NULL, attr != GB_STAT_DIRECTORY ? 0 : GB_STAT_DIRECTORY); + + #if OPT_NLINK + if (file_dir && !FILE_is_relative(dir)) + { + struct stat dinfo; + fstat(dirfd(file_dir), &dinfo); + // If the number of links N to the directory: + // - Is > 2, then the directory has N - 2 sub-directories + // - Is = 2, then the directory has no sub-directory + // - Is < 2, then the file system is not POSIX, so we must scan everything + nsubdir = dinfo.st_nlink - 2; + } + #endif + + while (!FILE_dir_next(&file, &len)) + { + temp = STRING_new_temp(file, len); + path = (char *)FILE_cat(file_rdir_path, temp, NULL); + + #if OPT_NLINK + if (nsubdir || follow) + #endif + { + #ifdef _DIRENT_HAVE_D_TYPE + is_dir = _last_is_dir; + #else + if (follow) + is_dir = FILE_is_dir(path); + else + { + FILE_stat(path, &info, FALSE); + is_dir = info.type == GB_STAT_DIRECTORY; + } + #endif + + if (is_dir) + { + nsubdir--; + push_path(&dir_list, path); + continue; + } + } + + push_path(&list, path); + } + + while (dir_list) + { + path = pop_path(&dir_list); + //fprintf(stderr, "%s\n", path); + + TRY + { + if (found && (!attr || (attr & GB_STAT_DIRECTORY))) (*found)(path); + FILE_recursive_dir(path, found, afterfound, attr, follow); + if (afterfound && (!attr || (attr & GB_STAT_DIRECTORY))) (*afterfound)(path); + } + CATCH + { + //ERROR_print_at(stdout); + } + END_TRY + + STRING_free((char **)&path); + } + + while (list) + { + path = pop_path(&list); + //fprintf(stderr, "%s\n", path); + + TRY + { + if (found) (*found)(path); + if (afterfound) (*afterfound)(path); + } + CATCH + { + //ERROR_print_at(stdout); + } + END_TRY + + STRING_free((char **)&path); + } +} + + +void FILE_rmdir(const char *path) +{ + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (rmdir(path) != 0) + { + if (errno == ENOTEMPTY || errno == EEXIST) + THROW(E_NEMPTY); + else + THROW_SYSTEM(errno, path); + } +} + + +void FILE_mkdir_mode(const char *path, mode_t mode) +{ + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (mkdir(path, mode) != 0) + THROW_SYSTEM(errno, path); +} + + +void FILE_make_path_dir(const char *path) +{ + int i; + char c; + + if (FILE_is_relative(path)) + return; + + init_file_buffer(path); + + for (i = 1;; i++) + { + c = file_buffer[i]; + if (c == 0) + break; + if (c == '/') + { + file_buffer[i] = 0; + (void)mkdir(file_buffer, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + file_buffer[i] = c; + } + c++; + } +} + + +void FILE_copy(const char *src, const char *dst) +{ + STREAM stream_src; + STREAM stream_dst; + int64_t len; + int64_t n; + char *buf = NULL; + + CLEAR(&stream_src); + CLEAR(&stream_dst); + + if (FILE_exist(dst)) + THROW(E_EXIST, dst); + + ALLOC(&buf, MAX_IO); + + TRY + { + STREAM_open(&stream_src, src, GB_ST_READ); + STREAM_open(&stream_dst, dst, GB_ST_CREATE); + + STREAM_lof(&stream_src, &len); + + while (len) + { + n = len > MAX_IO ? MAX_IO : len; + STREAM_read(&stream_src, buf, n); + STREAM_write(&stream_dst, buf, n); + len -= n; + } + + STREAM_close(&stream_src); + STREAM_close(&stream_dst); + + FREE(&buf); + } + CATCH + { + if (stream_src.type) + STREAM_close(&stream_src); + if (stream_dst.type) + STREAM_close(&stream_dst); + FREE(&buf); + + PROPAGATE(); + } + END_TRY +} + + +bool FILE_access(const char *path, int mode) +{ + int m; + + if (FILE_is_relative(path)) + { + if (mode & (GB_ST_WRITE | GB_ST_EXEC)) + return FALSE; + + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (access(path, mode) == 0) + return TRUE; + }*/ + + return ARCHIVE_exist(NULL, path); + } + + m = 0; + if (mode & GB_ST_READ) m += R_OK; + if (mode & GB_ST_WRITE) m += W_OK; + if (mode & GB_ST_EXEC) m += X_OK; + + return (access(path, m) == 0); +} + + +bool FILE_exist_follow(const char *path, bool follow) +{ + struct stat buf; + + if (FILE_is_relative(path)) + return ARCHIVE_exist(NULL, path); + + return follow ? (stat(path, &buf) == 0) : (lstat(path, &buf) == 0); +} + + +void FILE_link(const char *src, const char *dst) +{ + /* src can be relative */ + if (FILE_is_relative(dst)) + THROW(E_ACCESS); + + if (FILE_exist(dst)) + THROW(E_EXIST, dst); + + if (symlink(src, dst) != 0) + THROW_SYSTEM(errno, dst); +} + +int64_t FILE_free(const char *path) +{ + struct statvfs info; + + if (FILE_is_relative(path)) + return 0; + + statvfs(path, &info); + return (int64_t)(getuid() == 0 ? info.f_bfree : info.f_bavail) * info.f_bsize; +} + +#else + +bool FILE_exist(const char *path) +{ + return (access(path, F_OK) == 0); +} + +time_t FILE_get_time(const char *path) +{ + struct stat info; + + if (stat(path, &info) == 0) + return info.st_mtime; + else + return (time_t)-1L; +} + +size_t FILE_get_size(const char *path) +{ + struct stat info; + + if (stat(path, &info) == 0) + return info.st_size; + else + return -1; +} + +bool FILE_copy(const char *src, const char *dst) +{ + int src_fd; + int dst_fd; + ssize_t len; + char *buf = NULL; + int save_errno; + struct stat info; + + fprintf(stderr, "FILE_copy: %s -> %s\n", src, dst); + + if (stat(src, &info)) + return TRUE; + + src_fd = open(src, O_RDONLY); + if (src_fd < 0) + { + fprintf(stderr, "open src failed\n"); + return TRUE; + } + + dst_fd = creat(dst, info.st_mode); + if (dst_fd < 0) + { + fprintf(stderr, "open dst failed\n"); + save_errno = errno; + close(src_fd); + errno = save_errno; + return TRUE; + } + + ALLOC(&buf, MAX_IO); + + for(;;) + { + len = read(src_fd, buf, MAX_IO); + if (len == 0) + break; + if (len < 0 && errno == EINTR) + continue; + if (write(dst_fd, buf, len) < 0) + { + save_errno = errno; + close(src_fd); + close(dst_fd); + unlink(dst); + errno = save_errno; + IFREE(buf); + return TRUE; + } + } + + close(src_fd); + close(dst_fd); + IFREE(buf); + + return FALSE; +} + +#endif + +const char *FILE_getcwd(const char *subdir) +{ + if (getcwd(file_buffer, PATH_MAX) == NULL) + return NULL; + + file_buffer_length = strlen(file_buffer); + + if (subdir != NULL) + return FILE_cat(file_buffer, subdir, NULL); + else + return file_buffer; +} + + +const char *FILE_readlink(const char *link) +{ + int len = readlink(link, file_buffer, PATH_MAX); + + if (len < 0) + return NULL; + + file_buffer[len] = 0; + file_buffer_length = len; + return file_buffer; + +} + +const char *FILE_find_gambas(void) +{ + const char *path; + + path = getenv("GB_PATH"); + + if (!path || !*path) + { + if (FILE_exist(GAMBAS_LINK_PATH)) + { + path = FILE_readlink(GAMBAS_LINK_PATH); + if (!path) + path = GAMBAS_LINK_PATH; + } + else + { + path = GAMBAS_PATH "/gbx" GAMBAS_VERSION_STRING; + } + } + + return path; +} + +void FILE_chdir(const char *path) +{ + #ifdef PROJECT_EXEC + if (chdir(path)) + THROW_SYSTEM(errno, path); + #else + if (chdir(path)) + THROW("Cannot change current directory to '&1': &2", path, strerror(errno)); + #endif +} + +void FILE_unlink(const char *path) +{ + #ifdef PROJECT_EXEC + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (unlink(path) != 0) + THROW_SYSTEM(errno, path); + #else + if (unlink(path) != 0 && errno != ENOENT) + THROW("Cannot remove file '&1': &2", path, strerror(errno)); + #endif +} + + +void FILE_rename_ext(const char *src, const char *dst, bool kill) +{ + #ifdef PROJECT_EXEC + if (FILE_is_relative(src) || FILE_is_relative(dst)) + THROW(E_ACCESS); + + if (!kill) + { + if (FILE_exist(dst)) + THROW(E_EXIST, dst); + } + + if (rename(src, dst) != 0) + THROW_SYSTEM(errno, dst); + #else + if (rename(src, dst) != 0) + THROW("Cannot rename file '&1' to '&2': &3", src, dst, strerror(errno)); + #endif +} + +char *FILE_get_home(void) +{ + struct passwd *info; + uid_t uid = getuid(); + + if (!_home || _uid != uid) + { +#ifdef PROJECT_EXEC + STRING_free(&_home); +#else + STR_free(_home); +#endif + info = getpwuid(uid); + if (info) +#ifdef PROJECT_EXEC + _home = STRING_new_zero(info->pw_dir); +#else + _home = STR_copy(info->pw_dir); +#endif + _uid = uid; + } + + return _home; +} + diff --git a/main/share/gb_hash.h b/main/share/gb_hash.h new file mode 100644 index 00000000..403c17ff --- /dev/null +++ b/main/share/gb_hash.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + gb_hash.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_HASH_H +#define __GB_HASH_H + +#define KEEP_ORDER + +typedef + enum { + HF_NORMAL = 0, + HF_IGNORE_CASE = 1 + } + HASH_FLAG; + +typedef + struct _HASH_NODE + { + struct _HASH_NODE *next; + #ifdef KEEP_ORDER + struct _HASH_NODE *snext; + struct _HASH_NODE *sprev; + #endif + } + HASH_NODE; + +typedef + struct { + ushort len; + char key[0]; + } + PACKED + HASH_KEY; + +typedef + struct + { + int size; + int nnodes; + HASH_NODE **nodes; + size_t s_value; + HASH_NODE *last; + HASH_FLAG mode; + #ifdef KEEP_ORDER + HASH_NODE *sfirst; + HASH_NODE *slast; + #endif + } + HASH_TABLE; + +// NOTE: If HASH_ENUM changes, GB_COLLECTION_ITER must be updated accordingly in gambas.h + +typedef + struct + { + HASH_NODE *node; + HASH_NODE *next; + } + HASH_ENUM; + +typedef + uint (*HASH_FUNC)(const char *, int); + +typedef + bool (*HASH_COMP)(const char *, const char *, int); + +#define HASH_TABLE_MIN_SIZE 11 +#define HASH_TABLE_MAX_SIZE 13845163 + +#ifndef __GB_HASH_C +extern uint HASH_seed; +#endif + +void HASH_TABLE_create(HASH_TABLE **hash, size_t s_value, HASH_FLAG mode); +void HASH_TABLE_delete(HASH_TABLE **hash); +int HASH_TABLE_size(HASH_TABLE *hash_table); +void *HASH_TABLE_lookup(HASH_TABLE *hash_table, const char *key, int len, bool set_last); +void *HASH_TABLE_insert(HASH_TABLE *hash_table, const char *key, int len); +void HASH_TABLE_remove(HASH_TABLE *hash_table, const char *key, int len); +void *HASH_TABLE_next(HASH_TABLE *hash_table, HASH_ENUM *iter, bool set_last); +void HASH_TABLE_get_key(HASH_TABLE *hash_table, HASH_NODE *node, char **key, int *len); +bool HASH_TABLE_get_last_key(HASH_TABLE *hash_table, char **key, int *len); +void HASH_TABLE_set_last_key(HASH_TABLE *hash_table, char *key, int len); + +#endif + diff --git a/main/share/gb_hash_temp.h b/main/share/gb_hash_temp.h new file mode 100644 index 00000000..aa045f15 --- /dev/null +++ b/main/share/gb_hash_temp.h @@ -0,0 +1,534 @@ +/*************************************************************************** + + gb_hash_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_HASH_C + +#include + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_string.h" +#include "gb_alloc.h" +#include "gb_hash.h" + + +#define NODE_value(_node) ((void *)((char *)_node) + sizeof(HASH_NODE)) +//#define NODE_length(_table, _node) (*((ushort *)(((char *)_node) + sizeof(HASH_NODE) + (_table)->s_value))) +#define NODE_key(_table, _node) ((HASH_KEY *)(((char *)_node) + sizeof(HASH_NODE) + (_table)->s_value)) + +#define MUST_RESIZE(hash) ((hash_table->size >= 3 * hash_table->nnodes && hash_table->size > HASH_TABLE_MIN_SIZE) || (3 * hash_table->size <= hash_table->nnodes && hash_table->size < HASH_TABLE_MAX_SIZE)) +static void hash_table_resize(HASH_TABLE *hash_table); + +static HASH_NODE **hash_table_lookup_node(HASH_TABLE *hash_table, const char *key, int len); +static HASH_NODE *hash_node_new(HASH_TABLE *hash_table, const char *key, int len); +static void hash_node_destroy(HASH_NODE *hash_node); + +#ifdef KEEP_ORDER +#else +static void hash_nodes_destroy(HASH_NODE *hash_node); +#endif + +// Put a random number in that if you want to be safe on the Internet +uint HASH_seed = 0x9A177BA5; + +static const int primes[] = +{ + 11, 19, 37, 73, 109, 163, 251, 367, 557, 823, 1237, 1861, 2777, 4177, 6247, 9371, + 14057, 21089, 31627, 47431, 71143, 106721, 160073, 240101, 360163, 540217, 810343, + 1215497, 1823231, 2734867, 4102283, 6153409, 9230113, 13845163 +}; + +//static const uint seed[] = { 0x9A177BA5, 0x9A177BA4, 0x9A177BA7, 0x9A177BA6, 0x9A177BA1, 0x9A177BA0, 0x9A177BA3, 0x9A177BA2, 0x9A177BAD }; + +static const int nprimes = sizeof (primes) / sizeof (primes[0]); + +static int spaced_primes_closest(int num) +{ + int i; + + for (i = 0; i < nprimes; i++) + if (primes[i] > num) + return primes[i]; + + return primes[nprimes - 1]; +} + + +// Fast hashing functions. Sometimes Microsoft Research produces useful things. + +static uint key_hash_binary(const char *key, int len) +{ + static const void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7, &&__LEN_8 }; + uint seed = HASH_seed; + uint hash = seed ^ len; + + if (len > 8) + { + key += len - 8; + len = 8; + } + + goto *jump[len]; + +__LEN_8: + hash = hash * seed + key[7]; +__LEN_7: + hash = hash * seed + key[6]; +__LEN_6: + hash = hash * seed + key[5]; +__LEN_5: + hash = hash * seed + key[4]; +__LEN_4: + hash = hash * seed + key[3]; +__LEN_3: + hash = hash * seed + key[2]; +__LEN_2: + hash = hash * seed + key[1]; +__LEN_1: + hash = hash * seed + key[0]; +__LEN_0: + + return hash; +} + +static uint key_hash_text(const char *key, int len) +{ + static const void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7, &&__LEN_8 }; + uint seed = HASH_seed; + uint hash = seed ^ len; + + if (len > 8) + { + key += len - 8; + len = 8; + } + + goto *jump[len]; + +__LEN_8: + hash = hash * seed + ((uint)key[7] & ~0x20U); +__LEN_7: + hash = hash * seed + ((uint)key[6] & ~0x20U); +__LEN_6: + hash = hash * seed + ((uint)key[5] & ~0x20U); +__LEN_5: + hash = hash * seed + ((uint)key[4] & ~0x20U); +__LEN_4: + hash = hash * seed + ((uint)key[3] & ~0x20U); +__LEN_3: + hash = hash * seed + ((uint)key[2] & ~0x20U); +__LEN_2: + hash = hash * seed + ((uint)key[1] & ~0x20U); +__LEN_1: + hash = hash * seed + ((uint)key[0] & ~0x20U); +__LEN_0: + + return hash; +} + + +#define get_hash_func(_hash) ((_hash)->mode ? key_hash_text : key_hash_binary) + + +void HASH_TABLE_create(HASH_TABLE **hash, size_t s_value, HASH_FLAG mode) +{ + HASH_TABLE *hash_table; + /*int i;*/ + + ALLOC_ZERO(&hash_table, sizeof(HASH_TABLE)); + + hash_table->size = HASH_TABLE_MIN_SIZE; + hash_table->s_value = s_value; + + ALLOC_ZERO(&hash_table->nodes, sizeof(HASH_NODE *) * hash_table->size); + + if (mode == HF_IGNORE_CASE) + hash_table->mode = mode; + else + hash_table->mode = HF_NORMAL; + + *hash = hash_table; +} + + +void HASH_TABLE_delete(HASH_TABLE **hash) +{ + HASH_TABLE *hash_table = *hash; + + if (hash_table == NULL) + return; + + #ifdef KEEP_ORDER + HASH_NODE *node, *next; + + node = hash_table->sfirst; + hash_table->sfirst = NULL; + hash_table->slast = NULL; + + while (node) + { + next = node->snext; + FREE(&node); + node = next; + } + #else + int i; + + for (i = 0; i < hash_table->size; i++) + hash_nodes_destroy(hash_table->nodes[i]); + #endif + + FREE(&hash_table->nodes); + FREE(hash); +} + + +int HASH_TABLE_size(HASH_TABLE *hash_table) +{ + return hash_table->nnodes; +} + + +static HASH_NODE **hash_table_lookup_node(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE **node; + HASH_KEY *node_key; + uint hash; + //int n; + + if (hash_table->mode) + { + hash = key_hash_text(key, len); + node = &hash_table->nodes[hash % hash_table->size]; + + //n = 0; + while (*node) + { + node_key = NODE_key(hash_table, *node); + //n++; + if (node_key->len == len && STRING_equal_ignore_case_same(key, node_key->key, len)) + break; + node = &(*node)->next; + } + } + else + { + hash = key_hash_binary(key, len); + node = &hash_table->nodes[hash % hash_table->size]; + + //n = 0; + while (*node) + { + node_key = NODE_key(hash_table, *node); + //n++; + if (node_key->len == len && STRING_equal_same(key, node_key->key, len)) + break; + node = &(*node)->next; + } + } + + //fprintf(stderr, "hash_table_lookup_node %p: %d %d -> %d\n", hash_table, hash_table->size, hash_table->nnodes, n); + return node; +} + + +void *HASH_TABLE_lookup(HASH_TABLE *hash_table, const char *key, int len, bool set_last) +{ + HASH_NODE *node; + + if (len == 0) + return NULL; + + node = *hash_table_lookup_node(hash_table, key, len); + if (set_last) + hash_table->last = node; + + return node ? NODE_value(node) : NULL; +} + + +void *HASH_TABLE_insert(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE **node; + void *value; + + node = hash_table_lookup_node(hash_table, key, len); + + if (*node != NULL) + return NODE_value(*node); + + *node = hash_node_new(hash_table, key, len); + hash_table->nnodes++; + /*if (!hash_table->frozen)*/ + + value = NODE_value(*node); + + if (MUST_RESIZE(hash_table)) + hash_table_resize(hash_table); + + return value; +} + + +void HASH_TABLE_remove(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE **node, *dest; + + node = hash_table_lookup_node(hash_table, key, len); + + if (*node != NULL) + { + dest = *node; + (*node) = dest->next; + + #ifdef KEEP_ORDER + + if (dest->sprev) + dest->sprev->snext = dest->snext; + else + hash_table->sfirst = dest->snext; + + if (dest->snext) + dest->snext->sprev = dest->sprev; + else + hash_table->slast = dest->sprev; + + #endif + + hash_node_destroy(dest); + hash_table->nnodes--; + + hash_table->last = NULL; + + /*if (!hash_table->frozen)*/ + if (MUST_RESIZE(hash_table)) + hash_table_resize(hash_table); + } +} + + +void *HASH_TABLE_next(HASH_TABLE *hash_table, HASH_ENUM *iter, bool set_last) +{ + #ifdef KEEP_ORDER + + if (iter->node == NULL) + iter->node = hash_table->sfirst; + else + iter->node = iter->next; + + if (set_last) + hash_table->last = iter->node; + + if (iter->node) + { + iter->next = iter->node->snext; + return NODE_value(iter->node); + } + else + return NULL; + + #else + + HASH_NODE *enum_node = iter->node; + int enum_i = iter->index; + + if (enum_node) + enum_node = enum_node->next; + + while (enum_node == NULL) + { + enum_i++; + + if (enum_i >= hash_table->size) + break; + + enum_node = hash_table->nodes[enum_i]; + }; + + iter->node = enum_node; + iter->index = enum_i; + + if (set_last) + hash_table->last = enum_node; + + if (enum_node) + return NODE_value(enum_node); + else + return NULL; + + #endif +} + +#if 0 +static void dump_hash_table(HASH_TABLE *hash_table) +{ + int i; + HASH_NODE *node; + HASH_KEY *node_key; + + for (i = 0; i < hash_table->size; i++) + { + fprintf(stderr, "%5d: ", i); + for (node = hash_table->nodes[i]; node; node = node->next) + { + node_key = NODE_key(hash_table, node); + fprintf(stderr, "%.*s ", node_key->len, node_key->key); + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); +} +#endif + +static void hash_table_resize(HASH_TABLE *hash_table) +{ + HASH_NODE **old_nodes; + HASH_NODE **new_nodes; + HASH_NODE *node; + HASH_NODE *next; + HASH_KEY *node_key; + int hash_val; + int new_size; + int i; + HASH_FUNC hash_func = get_hash_func(hash_table); + + new_size = MinMax(spaced_primes_closest(hash_table->nnodes), HASH_TABLE_MIN_SIZE, HASH_TABLE_MAX_SIZE); + + //fprintf(stderr, "**** hash_table_resize %p %d: %d -> %d\n", hash_table, hash_table->nnodes, hash_table->size, new_size); + + //fprintf(stderr, "BEFORE:\n"); + //dump_hash_table(hash_table); + + old_nodes = hash_table->nodes; + ALLOC_ZERO(&new_nodes, new_size * sizeof(HASH_NODE *)); + + for (i = 0; i < hash_table->size; i++) + for (node = hash_table->nodes[i]; node; node = next) + { + next = node->next; + + node_key = NODE_key(hash_table, node); + hash_val = (*hash_func)(node_key->key, node_key->len) % new_size; + + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + + FREE(&old_nodes); + hash_table->nodes = new_nodes; + hash_table->size = new_size; + + //fprintf(stderr, "AFTER:\n"); + //dump_hash_table(hash_table); +} + + +static HASH_NODE *hash_node_new(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE *hash_node; + int size; + HASH_KEY *node_key; + + if (len > 65535) + len = 65535; + + size = sizeof(HASH_NODE) + hash_table->s_value + sizeof(HASH_KEY) + len; + ALLOC_ZERO(&hash_node, size); + + node_key = NODE_key(hash_table, hash_node); + memcpy(node_key->key, key, len); + node_key->len = len; + //node_key->hash = hash_table->mode ? key_hash_text(key, len) : key_hash_binary(key, len); + + #ifdef KEEP_ORDER + + if (!hash_table->sfirst) + { + hash_table->sfirst = hash_node; + hash_table->slast = hash_node; + } + else + { + hash_node->sprev = hash_table->slast; + hash_node->sprev->snext = hash_node; + hash_table->slast = hash_node; + } + + #endif + + return hash_node; +} + + +static void hash_node_destroy(HASH_NODE *hash_node) +{ + FREE(&hash_node); +} + +#ifdef KEEP_ORDER +#else +static void hash_nodes_destroy(HASH_NODE *hash_node) +{ + HASH_NODE *node = hash_node; + HASH_NODE *next; + + for(;;) + { + if (node == NULL) + return; + + next = node->next; + FREE(&node); + node = next; + } +} +#endif + +void HASH_TABLE_get_key(HASH_TABLE *hash_table, HASH_NODE *node, char **key, int *len) +{ + HASH_KEY *node_key; + + if (node != NULL) + { + node_key = NODE_key(hash_table, node); + *key = node_key->key; + *len = node_key->len; + } + else + *len = 0; +} + + +bool HASH_TABLE_get_last_key(HASH_TABLE *hash_table, char **key, int *len) +{ + if (hash_table->last == NULL) + return TRUE; + + HASH_TABLE_get_key(hash_table, hash_table->last, key, len); + return FALSE; +} + +void HASH_TABLE_set_last_key(HASH_TABLE *hash_table, char *key, int len) +{ + hash_table->last = (len == 0) ? NULL : *hash_table_lookup_node(hash_table, key, len); +} diff --git a/main/share/gb_limit.h b/main/share/gb_limit.h new file mode 100644 index 00000000..a6e08e98 --- /dev/null +++ b/main/share/gb_limit.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + gb_limit.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_LIMIT_H +#define __GB_LIMIT_H + +/* Maximum number of parameters for a function - CANNOT CHANGE */ +#define MAX_PARAM_FUNC 63 + +/* Maximum number of parameters for a subroutine - CANNOT CHANGE */ +#define MAX_PARAM_SUBR 63 + +/* Maximum number of parameters for an operator - CANNOT CHANGE */ +#define MAX_PARAM_OP 63 + +/* Maximum level of expression imbrication */ +#define MAX_EXPR_LEVEL 255 + +/* Maximum number of patterns in an expression - CANNOT CHANGE */ +#define MAX_EXPR_PATTERN 1023 + +/* Maximum length of a symbol */ +#define MAX_SYMBOL_LEN 255 + +/* Maximum number of constants in the same class - CANNOT CHANGE */ +#define MAX_CLASS_CONST 65536 + +/* Maximum number of static or dynamic symbols in the same class - CANNOT CHANGE */ +#define MAX_CLASS_SYMBOL 2048 + +/* Maximum number of functions in the same class - CANNOT CHANGE */ +#define MAX_CLASS_FUNCTION 2048 + +/* Maximum number of class uses in the same class - CANNOT CHANGE */ +#define MAX_CLASS_CLASS 2048 + +/* Maximum number of extern declaration in the same class - CANNOT CHANGE */ +#define MAX_CLASS_EXTERN 256 + +/* Maximum number of events in the same class - CANNOT CHANGE */ +#define MAX_CLASS_EVENT 254 + +/* Maximum number of unknown symbols in the same class - CANNOT CHANGE */ +#define MAX_CLASS_UNKNOWN 65536 + +/* Maximum number of array declarations in the same class - CANNOT CHANGE */ +#define MAX_CLASS_ARRAY 32768 + +/* Maximum number of local variables in a function - CANNOT CHANGE */ +#define MAX_LOCAL_SYMBOL 127 + +/* Maximum level of control structures imbrication */ +#define MAX_CTRL_LEVEL 32 + +/* Maximum number of dimensions in an array */ +#define MAX_ARRAY_DIM 8 + +/* Maximum level of controls imbrication in a form */ +#define MAX_FORM_PARENT 32 + +/* Maximum number of possible comparisons in one CASE instruction */ +#define MAX_CASE_EXPR 32 + +/* Maximum number of breakpoints */ +#define MAX_BREAKPOINT 255 + +/* Number of bytes written at once in a single input/output request */ +#define MAX_IO 4096 + +/* Maximum level of class inheritance */ +#define MAX_INHERITANCE 16 + +/* Maximum length of an error message */ +#define MAX_ERROR_MSG 511 + +/* Maximum number of fields in a structure */ +#define MAX_STRUCT_FIELD 255 + +/* Maximum number of digits in a Float */ +#define MAX_FLOAT_DIGIT 15 + +/* Maximum length of the System.Language property */ +#define MAX_LANG 16 + +#endif + diff --git a/main/share/gb_list.h b/main/share/gb_list.h new file mode 100644 index 00000000..954d7046 --- /dev/null +++ b/main/share/gb_list.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + gb_list.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_LIST_H +#define __GB_LIST_H + +typedef + struct + { + void *next; + void *prev; + } + LIST; + +void LIST_insert(void *p_first, void *node, LIST *list); +void LIST_remove(void *p_first, void *node, LIST *list); + +#define LIST_for_each_name(_var, _first, _name) \ + for(_var = (_first); _var; _var = (_var)->_name.next) + +#define LIST_for_each(_var, _first) LIST_for_each_name(_var, _first, list) + +#endif diff --git a/main/share/gb_list_temp.h b/main/share/gb_list_temp.h new file mode 100644 index 00000000..b5eea46e --- /dev/null +++ b/main/share/gb_list_temp.h @@ -0,0 +1,90 @@ +/*************************************************************************** + + gb_list_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_LIST_C + +#include "gb_common.h" +#include "gb_list.h" + +/*#define DEBUG*/ + +#define TO_LIST(_node) ((LIST *)(((char *)_node) + ((char *)list - (char *)node))) + + +void LIST_insert(void *p_first, void *node, LIST *list) +{ + void **first = (void **)p_first; + void *last; + + if (*first == NULL) + { + *first = node; + list->prev = node; + list->next = NULL; + return; + } + + last = TO_LIST(*first)->prev; + + TO_LIST(last)->next = node; + + list->prev = last; + list->next = NULL; + + TO_LIST(*first)->prev = node; +} + + +void LIST_remove(void *p_first, void *node, LIST *list) +{ + void **first = (void **)p_first; + void *next, *prev, *last; + + next = list->next; + prev = list->prev; + + if (*first == node) + { + if (next) + TO_LIST(next)->prev = prev; + + *first = next; + } + else + { + last = TO_LIST(*first)->prev; + + if (node == last) + TO_LIST(*first)->prev = prev; + + if (prev) + TO_LIST(prev)->next = next; + + if (next) + TO_LIST(next)->prev = prev; + } + + list->prev = NULL; + list->next = NULL; +} + diff --git a/main/share/gb_magic.h b/main/share/gb_magic.h new file mode 100644 index 00000000..d149f5d9 --- /dev/null +++ b/main/share/gb_magic.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gb_magic.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_MAGIC_H +#define __GB_MAGIC_H + +#define OUTPUT_MAGIC 0x9A177BA5 +#define OUTPUT_ENDIAN 0x12345678 + +#define ARCH_MAGIC 0xA7C4117E +#define ARCH_VERSION 2 + +#define PROJECT_MAGIC "# Gambas Project File" + +#endif diff --git a/main/share/gb_overflow.h b/main/share/gb_overflow.h new file mode 100644 index 00000000..f5c007e4 --- /dev/null +++ b/main/share/gb_overflow.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + gb_overflow.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_OVERFLOW_H +#define __GB_OVERFLOW_H + +#if __GNUC__ >= 8 + + #define DO_NOT_CHECK_OVERFLOW 0 + +#else + + #define DO_NOT_CHECK_OVERFLOW 1 + + #if defined(__has_builtin) + #if __has_builtin(__builtin_add_overflow) + + #undef DO_NOT_CHECK_OVERFLOW + #define DO_NOT_CHECK_OVERFLOW 0 + + #endif + #endif + +#endif + +#endif /* __GB_OVERFLOW_H */ diff --git a/main/share/gb_pcode.h b/main/share/gb_pcode.h new file mode 100644 index 00000000..960f3418 --- /dev/null +++ b/main/share/gb_pcode.h @@ -0,0 +1,208 @@ +/*************************************************************************** + + gb_pcode.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_PCODE_H +#define __GB_PCODE_H + +/* If this file is modified, don't forget to update GAMBAS_PCODE_VERSION in acinclude.m4 */ + +#define C_NOP 0x0000 + +#define C_PUSH_QUICK 0xF000 + +#define C_PUSH_LOCAL_NOREF 0xF100 +#define C_PUSH_PARAM_NOREF 0xF200 +#define C_JUMP_IF_TRUE_FAST 0XF300 +#define C_JUMP_IF_FALSE_FAST 0XF400 +#define C_PUSH_VARIABLE 0xF500 +#define C_POP_VARIABLE 0xF600 +#define C_PUSH_FLOAT 0xF700 +#define C_POKE 0xF800 +#define C_POP_LOCAL_NOREF 0xF900 +#define C_POP_PARAM_NOREF 0xFA00 +#define C_POP_LOCAL_FAST 0xFB00 +#define C_POP_PARAM_FAST 0xFC00 +#define C_JUMP_NEXT_INTEGER 0xFE00 + +#define C_PUSH_CONST 0xE000 + +#define C_POP_STATIC 0xD800 +#define C_POP_DYNAMIC 0xD000 +#define C_PUSH_STATIC 0xC800 +#define C_PUSH_DYNAMIC 0xC000 +#define C_PUSH_FUNCTION 0xB800 +#define C_PUSH_CLASS 0xB000 + +#define C_ADD_QUICK 0xA000 + +#define C_PUSH_ARRAY_NATIVE_INTEGER 0xA100 +#define C_POP_ARRAY_NATIVE_INTEGER 0xA200 +#define C_PUSH_ARRAY_NATIVE_FLOAT 0xA300 +#define C_POP_ARRAY_NATIVE_FLOAT 0xA400 +#define C_PUSH_ARRAY_NATIVE_COLLECTION 0xA500 +#define C_POP_ARRAY_NATIVE_COLLECTION 0xA600 +#define C_ADD_INTEGER 0xA700 +#define C_ADD_FLOAT 0xA800 +#define C_SUB_INTEGER 0xA900 +#define C_SUB_FLOAT 0xAA00 +#define C_MUL_INTEGER 0xAB00 +#define C_MUL_FLOAT 0xAC00 +#define C_DIV_INTEGER 0xAD00 +#define C_DIV_FLOAT 0xAE00 + +#define C_PUSH_LOCAL 0x0100 +#define C_PUSH_PARAM 0x0200 +#define C_PUSH_ARRAY 0x0300 +#define C_PUSH_UNKNOWN 0x0400 + +#define C_PUSH_EXTERN 0x0500 +#define C_BYREF 0x0600 +#define C_PUSH_EVENT 0x0700 +#define C_QUIT 0x0800 + +#define C_POP_LOCAL 0x0900 +#define C_POP_PARAM 0x0A00 +#define C_POP_ARRAY 0x0B00 +#define C_POP_UNKNOWN 0x0C00 + +#define C_POP_OPTIONAL 0x0D00 +#define C_POP_CTRL 0x0E00 + +#define C_BREAK 0x0F00 + +#define C_RETURN 0x1000 + +#define C_PUSH_INTEGER 0x1100 +#define C_PUSH_LONG 0x1200 +#define C_PUSH_CHAR 0x1300 +#define C_PUSH_MISC 0x1400 +#define C_PUSH_ME 0x1500 + +#define CPM_NULL 0 +#define CPM_VOID 1 +#define CPM_FALSE 2 +#define CPM_TRUE 3 +#define CPM_LAST 4 +#define CPM_STRING 5 +#define CPM_PINF 6 +#define CPM_MINF 7 +#define CPM_COMPLEX 8 +#define CPM_VARGS 9 +#define CPM_DROP_VARGS 10 +#define CPM_RETURN 11 +#define CPM_END_VARGS 12 + +#define C_TRY 0x1600 +#define C_END_TRY 0x1700 +#define C_CATCH 0x1800 + +#define C_DUP 0x1900 +#define C_DROP 0x1A00 +#define C_NEW 0x1B00 + +#define C_CALL 0x1C00 +#define C_CALL_QUICK 0x1D00 +#define C_CALL_SLOW 0x1E00 +#define C_ON 0x1F00 + +#define C_JUMP 0x2000 +#define C_JUMP_IF_TRUE 0x2100 +#define C_JUMP_IF_FALSE 0x2200 +#define C_GOSUB 0x2300 + +#define C_JUMP_FIRST 0x2400 +#define C_JUMP_NEXT 0x2500 +#define C_FIRST 0x2600 +#define C_NEXT 0x2700 + +#define C_EQ 0x2800 +#define C_NE 0x2900 +#define C_GT 0x2A00 +#define C_LE 0x2B00 +#define C_LT 0x2C00 +#define C_GE 0x2D00 +#define C_NEAR 0x2E00 +#define C_CASE 0x2F00 + +#define C_ADD 0x3000 +#define C_SUB 0x3100 +#define C_MUL 0x3200 +#define C_DIV 0x3300 +#define C_NEG 0x3400 +#define C_QUO 0x3500 +#define C_REM 0x3600 +#define C_POW 0x3700 +#define C_AND 0x3800 +#define C_OR 0x3900 +#define C_XOR 0x3A00 +#define C_NOT 0x3B00 +#define C_CAT 0x3C00 +#define C_LIKE 0x3D00 +#define C_FILE 0x3E00 +#define C_IS 0x3F00 + +#define CODE_FIRST_SUBR 0x40 +#define CODE_LAST_SUBR 0x9F + +#define CODE_CALL_VARIANT 0x80 +//#define CODE_CALL_VOID 0x40 + +#define CODE_STATIC 0x0800 +#define CODE_FUNCTION 0x0400 + +#define CODE_NEW_ARRAY 0x40 +#define CODE_NEW_EVENT 0x80 + +#define CODE_OCT (CODE_FIRST_SUBR + 8) +#define CODE_RINSTR (CODE_FIRST_SUBR + 12) +#define CODE_ABS (CODE_FIRST_SUBR + 20) +#define CODE_MAX (CODE_FIRST_SUBR + 30) +#define CODE_BCLR (CODE_FIRST_SUBR + 36) +#define CODE_CONV (CODE_FIRST_SUBR + 39) +#define CODE_BIN (CODE_FIRST_SUBR + 40) +#define CODE_HEX (CODE_FIRST_SUBR + 41) +#define CODE_DEBUG (CODE_FIRST_SUBR + 54) +#define CODE_POKE (CODE_FIRST_SUBR + 95) + +#define C_BCLR (CODE_BCLR << 8) + +typedef + ushort PCODE; + +#define PCODE_is(pcode, value) (((pcode) & 0xFF00) == (value)) +#define PCODE_get_nparam(pcode) ((pcode) & 0x3F) + +#define PCODE_is_variant(pcode) ((pcode) & CODE_CALL_VARIANT) +#define PCODE_is_void(pcode) ((pcode) & CODE_CALL_VOID) + +#define PCODE_is_breakpoint(pcode) PCODE_is(pcode, C_BREAK) +#define PCODE_is_jump(pcode) PCODE_is(pcode, C_JUMP) + +#define PCODE_BREAKPOINT(num) ((PCODE)(C_BREAK | (num))) + +#ifndef NO_CODE_DUMP +short PCODE_dump(FILE *out, ushort addr, PCODE *code); +void PCODE_dump_count(FILE *out); +#endif + +#endif /* */ diff --git a/main/share/gb_pcode_temp.h b/main/share/gb_pcode_temp.h new file mode 100644 index 00000000..2f3ff5b6 --- /dev/null +++ b/main/share/gb_pcode_temp.h @@ -0,0 +1,602 @@ +/*************************************************************************** + + gb_pcode_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" + +#ifdef PROJECT_COMP +#include "gb_limit.h" +#include "gbc_compile.h" +#endif + +#include "gb_pcode.h" + +static int _code_count[256] = { 0 }; + +/*#define DEBUG*/ + +static void print_quoted_string(FILE *out, const char *s, bool trans) +{ + const char *p = s; + unsigned char c; + + if (trans) + fputc('(', out); + fputc('"', out); + while ((c = (unsigned char)*p++)) + { + if (c < 32) + { + fputc('\\', out); + if (c == '\n') + fputc('n', out); + else if (c == '\t') + fputc('n', out); + else + fprintf(out, "\\x%02X", c); + } + else + fputc(c, out); + } + fputc('"', out); + if (trans) + fputc(')', out); +} + +short PCODE_dump(FILE *out, ushort addr, PCODE *code) +{ + static const char *op_comp[] = { "=", "<>", ">", "<=", "<", ">=", "==", "CASE" }; + static const char *op_arith[] = { "+", "-" , "*", "/", "NEG", "\\", "MOD", "POW", "AND", "OR", "XOR", "NOT", "&", "LIKE", "&/" }; + + int j; + unsigned short op; + unsigned short digit; + int value; + #ifdef PROJECT_COMP + int index; + TABLE *table; + bool trans; + #endif + int ncode; + + op = *code; + + switch (op & 0xFF00) + { + case C_PUSH_UNKNOWN: case C_POP_UNKNOWN: + case C_PUSH_INTEGER: + case C_JUMP: case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: case C_GOSUB: + case C_NEXT: case C_JUMP_NEXT: + case C_TRY: + case C_JUMP_IF_TRUE_FAST: case C_JUMP_IF_FALSE_FAST: + + ncode = 2; + break; + + case C_PUSH_LONG: + + ncode = 3; + break; + + case C_BYREF: + ncode = 2 + (op & 0xFF); + break; + + case C_ON: + ncode = 1 + (op & 0xFF); + break; + + case C_PUSH_EVENT: + ncode = 1 + ((op & 0xFF) == 0xFF); + break; + + default: + + if ((op & 0xFF00) == (C_PUSH_CONST | 0xF00)) + ncode = 2; + else + ncode = 1; + } + + fprintf(out, "%04d : ", addr); + + for (j = 0; j < 3; j++) + { + if (j >= ncode) + fprintf(out, " "); + else + fprintf(out, " %04hX", code[j]); + } + + fprintf(out, " "); + + _code_count[op >> 8]++; + + digit = (op >> 12); + value = op & 0xFFF; + if (value >= 0x800) value |= 0xFFFFF000; + + switch (digit) + { + case 0xF: + switch (op & 0xFF00) + { + case C_PUSH_LOCAL_NOREF: + fprintf(out, "PUSH LOCAL NOREF %d", (op & 0xFF)); + break; + + case C_PUSH_PARAM_NOREF: + fprintf(out, "PUSH PARAM NOREF %d", (char)(op & 0xFF)); + break; + + case C_PUSH_VARIABLE: + fprintf(out, "PUSH VARIABLE %d", (op & 0xFF)); + break; + + case C_POP_VARIABLE: + fprintf(out, "POP VARIABLE %d", (op & 0xFF)); + break; + + case C_PUSH_FLOAT: + fprintf(out, "PUSH FLOAT %d", (char)(op & 0xFF)); + break; + + case C_POKE: + fprintf(out, "POKE %d", (op & 0xFF)); + break; + + case C_POP_LOCAL_NOREF: + fprintf(out, "POP LOCAL NOREF %d", (op & 0xFF)); + break; + + case C_POP_PARAM_NOREF: + fprintf(out, "POP PARAM NOREF %d", (char)(op & 0xFF)); + break; + + case C_POP_LOCAL_FAST: + fprintf(out, "POP LOCAL FAST %d", (op & 0xFF)); + break; + + case C_POP_PARAM_FAST: + fprintf(out, "POP PARAM FAST %d", (char)(op & 0xFF)); + break; + + case C_JUMP_NEXT_INTEGER: + fprintf(out, "JUMP NEXT INTEGER %d", (op & 0xFF)); + break; + + case C_JUMP_IF_TRUE_FAST: + case C_JUMP_IF_FALSE_FAST: + value = code[1]; + fprintf(out, "JUMP IF %s FAST %04d", + (digit == C_JUMP_IF_TRUE_FAST ? "TRUE" : "FALSE"), + (short)(addr + value + 2)); + break; + + default: + fprintf(out, "PUSH QUICK %d", (short)value); + } + break; + + case 0xA: + + switch(op & 0xFF00) + { + case C_PUSH_ARRAY_NATIVE_INTEGER: + fprintf(out, "PUSH ARRAY NATIVE INTEGER %d", (op & 0xFF)); + break; + case C_POP_ARRAY_NATIVE_INTEGER: + fprintf(out, "POP ARRAY NATIVE INTEGER %d", (op & 0xFF)); + break; + case C_PUSH_ARRAY_NATIVE_FLOAT: + fprintf(out, "PUSH ARRAY NATIVE FLOAT %d", (op & 0xFF)); + break; + case C_POP_ARRAY_NATIVE_FLOAT: + fprintf(out, "POP ARRAY NATIVE FLOAT %d", (op & 0xFF)); + break; + case C_ADD_INTEGER: + fprintf(out, "ADD INTEGER %d", (op & 0xFF)); + break; + case C_ADD_FLOAT: + fprintf(out, "ADD FLOAT %d", (op & 0xFF)); + break; + case C_SUB_INTEGER: + fprintf(out, "SUB INTEGER %d", (op & 0xFF)); + break; + case C_SUB_FLOAT: + fprintf(out, "SUB FLOAT %d", (op & 0xFF)); + break; + case C_MUL_INTEGER: + fprintf(out, "MUL INTEGER %d", (op & 0xFF)); + break; + case C_MUL_FLOAT: + fprintf(out, "MUL FLOAT %d", (op & 0xFF)); + break; + case C_DIV_INTEGER: + fprintf(out, "DIV INTEGER %d", (op & 0xFF)); + break; + case C_DIV_FLOAT: + fprintf(out, "DIV FLOAT %d", (op & 0xFF)); + break; + default: + fprintf(out, "ADD QUICK %d", (short)value); + } + break; + + #ifdef PROJECT_COMP + + case 0xE: + if ((op & 0xF00) == 0xF00) + { + value = code[1]; + } + + fprintf(out, "PUSH CONST %d", value); + + switch(JOB->class->constant[value].type.t.id) + { + case T_STRING: + table = JOB->class->string; + trans = FALSE; + break; + + case T_CSTRING: + table = JOB->class->string; + trans = TRUE; + break; + + default: + table = JOB->class->table; + trans = FALSE; + break; + } + + fputc(' ', out); + + print_quoted_string(out, TABLE_get_symbol_name(table, JOB->class->constant[value].value), trans); + + break; + + case 0xD: case 0xC: + fprintf(out, "%s %s ", (digit == 0xD ? "POP" : "PUSH"), (value & 0x800) ? "STATIC" : "DYNAMIC"); + index = ((value & 0x800) ? JOB->class->stat[value & 0x7FF].index : JOB->class->dyn[value & 0x7FF].index); + fprintf(out, "%s", TABLE_get_symbol_name(JOB->class->table, index)); + break; + + case 0xB: + if (value & 0x800) + { + fprintf(out, "PUSH FUNCTION "); + index = JOB->class->function[value & 0x7FF].name; + } + else + { + fprintf(out, "PUSH CLASS "); + index = JOB->class->class[value].index; + } + fprintf(out, "%s", TABLE_get_symbol_name(JOB->class->table, index)); + break; + + #else + + case 0xE: + fprintf(out, "PUSH CONST %d", (short)value); + break; + + case 0xD: + fprintf(out, "POP %s %d", (value & 0x800) ? "STATIC" : "DYNAMIC", value & 0x7FF); + break; + + case 0xC: + fprintf(out, "PUSH %s %d", (value & 0x800) ? "STATIC" : "DYNAMIC", value & 0x7FF); + break; + + case 0xB: + fprintf(out, "PUSH %s %d", (value & 0x800) ? "FUNCTION" : "CLASS", value & 0x7FF); + break; + + #endif + + default: + + digit = op & 0xFF00; + value = op & 0xFF; + if (value >= 0x80) value |= 0xFFFFFF00; + + if (digit >= C_PUSH_LOCAL && digit < C_QUIT && digit != C_BYREF) + fprintf(out, "PUSH "); + else if (digit >= C_POP_LOCAL && digit < C_BREAK) + fprintf(out, "POP "); + + switch(digit) + { + case C_PUSH_LOCAL: case C_POP_LOCAL: case C_PUSH_PARAM: case C_POP_PARAM: + if (value >= 0) + fprintf(out, "LOCAL %d", (short)value); + else + fprintf(out, "PARAM %d", (short)value); + break; + + case C_POP_CTRL: + fprintf(out, "CTRL %d", (short)value); + break; + + case C_POP_OPTIONAL: + fprintf(out, "OPTIONAL %d", (short)value); + break; + + case C_PUSH_UNKNOWN: case C_POP_UNKNOWN: + value = code[1]; + #ifdef PROJECT_COMP + fprintf(out, "UNKNOWN %s", TABLE_get_symbol_name(JOB->class->table, JOB->class->unknown[value])); + #else + fprintf(out, "UNKNOWN %d", (short)value); + #endif + + break; + + /*case C_PUSH_SPECIAL: + fprintf(out, "SPECIAL %d", (short)value); + break;*/ + + case C_PUSH_EXTERN: + fprintf(out, "EXTERN %d", (short)value); + break; + + case C_PUSH_EVENT: + if ((unsigned char)value == 0xFF) + { + value = code[1]; + #ifdef PROJECT_COMP + fprintf(out, "UNKNOWN EVENT %s", TABLE_get_symbol_name(JOB->class->table, JOB->class->unknown[value])); + #else + fprintf(out, "UNKNOWN EVENT %d", (short)value); + #endif + } + else + fprintf(out, "EVENT %d", (short)value); + break; + + case C_PUSH_ARRAY: case C_POP_ARRAY: + fprintf(out, "ARRAY (%d)", (short)value); + break; + + case C_CALL: case C_CALL_QUICK: case C_CALL_SLOW: + if (digit == C_CALL) + fprintf(out, "CALL "); + else if (digit == C_CALL_QUICK) + fprintf(out, "CALL QUICK "); + else + fprintf(out, "CALL SLOW "); + if (value & CODE_CALL_VARIANT) + fprintf(out, "VARIANT "); + //if (value & CODE_CALL_VOID) + // fprintf(out, "VOID "); + + fprintf(out, "(%d)", (short)value & 0x3F); + break; + + case C_BYREF: + fprintf(out, "BYREF (%d) ", (short)value & 0x3F); + for (j = 1; j < ncode; j++) + fprintf(out, "%04X", code[j]); + break; + + case C_PUSH_INTEGER: + value = code[1]; + fprintf(out, "PUSH SHORT %d", (short)value); + break; + + case C_PUSH_LONG: + // FIXME: endianness + value = code[1] | (code[2] << 16); //*((int *)&code[1]); + fprintf(out, "PUSH INTEGER %d", value); + break; + + case C_PUSH_ME: + fprintf(out, "PUSH %s", (value & 2) ? "SUPER": "ME"); + break; + + case C_PUSH_MISC: + switch (value) + { + case CPM_NULL: fprintf(out, "PUSH NULL"); break; + case CPM_VOID: fprintf(out, "PUSH VOID"); break; + case CPM_FALSE: fprintf(out, "PUSH FALSE"); break; + case CPM_TRUE: fprintf(out, "PUSH TRUE"); break; + case CPM_LAST: fprintf(out, "PUSH LAST"); break; + case CPM_STRING: fprintf(out, "PUSH NULL STRING"); break; + case CPM_PINF: fprintf(out, "PUSH +INF"); break; + case CPM_MINF: fprintf(out, "PUSH -INF"); break; + case CPM_COMPLEX: fprintf(out, "PUSH COMPLEX"); break; + case CPM_VARGS: fprintf(out, "PUSH VARGS"); break; + case CPM_DROP_VARGS: fprintf(out, "DROP VARGS"); break; + case CPM_END_VARGS: fprintf(out, "END VARGS"); break; + } + break; + + case C_JUMP: case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: + value = code[1]; + fprintf(out, "JUMP%s %04d", + (digit == C_JUMP ? "" : + digit == C_JUMP_IF_TRUE ? " IF TRUE" : + " IF FALSE"), + (short)(addr + value + 2)); + break; + + case C_JUMP_FIRST: + + fprintf(out, "JUMP FIRST LOCAL %d", (short)value); + break; + + case C_JUMP_NEXT: + + fprintf(out, "JUMP NEXT "); + + value = code[1]; + fprintf(out, "%04d ", (short)(addr + value + 2)); + + break; + + case C_GOSUB: + value = code[1]; + fprintf(out, "GOSUB %04d", (short)(addr + value + 2)); + break; + + case C_ON: + fprintf(out, "ON (%d)", (short)value); + break; + + case C_FIRST: + + fprintf(out, "ENUM FIRST LOCAL %d", (short)value); + break; + + case C_NEXT: + + fprintf(out, "ENUM NEXT "); + if (value & 0xFF) + fprintf(out, "DROP "); + value = code[1]; + fprintf(out, "%04d ", (short)(addr + value + 2)); + break; + + case C_DROP: + fprintf(out, "DROP (%d)", (short)value); + break; + + case C_DUP: + fprintf(out, "DUP"); + break; + + case C_NEW: + fprintf(out, "NEW "); + if (value & CODE_NEW_EVENT) + fprintf(out, "EVENT "); + if (value & CODE_NEW_ARRAY) + fprintf(out, "ARRAY "); + fprintf(out, "(%d)", (short)value & 0x3F); + break; + + case C_BREAK: + fprintf(out, "BREAK"); + break; + + case C_RETURN: + switch(value) + { + case 0: fprintf(out, "RETURN VOID"); break; + case 1: fprintf(out, "RETURN"); break; + case 2: fprintf(out, "RETURN VOID"); break; + default: fprintf(out, "RETURN (%d) ?", (short)value); + } + break; + + case C_QUIT: + switch(value) + { + case 0: fprintf(out, "QUIT"); break; + case 1: fprintf(out, "STOP"); break; + case 2: default: fprintf(out, "STOP EVENT"); break; + case 3: fprintf(out, "QUIT (1)"); break; + } + break; + + case C_PUSH_CHAR: + fprintf(out, "PUSH CHAR (%d)", (short)value); + break; + + case C_TRY: + value = code[1]; + fprintf(out, "TRY %04d", (short)(addr + value + 2)); + break; + + case C_END_TRY: + fprintf(out, "END TRY"); + break; + + case C_CATCH: + fprintf(out, "CATCH"); + break; + + case C_NOP: + fprintf(out, "NOP"); + break; + + default: + digit = (digit >> 8); + if (digit >= CODE_FIRST_SUBR) + { + #ifdef PROJECT_COMP + fprintf(out, "SUBR %s ", SUBR_get_from_opcode(digit - CODE_FIRST_SUBR, (short)value & 0x3F)->name); + #else + fprintf(out, "SUBR #%d ", digit - CODE_FIRST_SUBR); + #endif + + if (value & CODE_CALL_VARIANT) + fprintf(out, "VARIANT "); + //if (value & CODE_CALL_VOID) + // fprintf(out, "VOID "); + + fprintf(out, "(%d)", (short)value & 0x3F); + } + else if (digit >= 0x28 && digit <= 0x2F) + fprintf(out, "%s (%d)", op_comp[digit - 0x28], (short)value); + else if (digit >= 0x30 && digit <= 0x3E) + fprintf(out, "%s (%d)", op_arith[digit - 0x30], (short)value); + else if (digit == 0x3F) + fprintf(out, "IS (%d)", (short)value); + else + fprintf(out, "ILLEGAL"); + } + } + + fprintf(out, "\n"); + + if (ncode > 3) + { + for (j = 3; j < ncode; j++) + { + if (((j - 3) % 16) == 0) + fprintf(out, " "); + fprintf(out, " %04hX", code[j]); + if (((j - 3) % 16) == 15) + fprintf(out, "\n"); + } + fprintf(out, "\n"); + } + + return ncode; +} + +void PCODE_dump_count(FILE *out) +{ + int i; + + for (i = 0; i < 256; i++) + fprintf(out, "%02X : %d\n", i, _code_count[i]); +} diff --git a/main/share/gb_replace.h b/main/share/gb_replace.h new file mode 100644 index 00000000..73e5a7a9 --- /dev/null +++ b/main/share/gb_replace.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + gb_replace.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_REPLACE_H +#define __GB_REPLACE_H + +#include +#include +#include + +#ifndef HAVE_SETENV +int setenv(const char *name, const char *value, int overwrite); +#endif + +#ifndef HAVE_UNSETENV +int unsetenv(const char *name); +#endif + +#ifndef HAVE_GETDOMAINNAME +int getdomainname(char *name, size_t len); +#endif + +#ifndef HAVE_GETPT +int getpt(void); +#endif + +#ifndef HAVE_CFMAKERAW +void cfmakeraw(struct termios *termios_p); +#endif + +#endif diff --git a/main/share/gb_replace_temp.h b/main/share/gb_replace_temp.h new file mode 100644 index 00000000..c2fdee0e --- /dev/null +++ b/main/share/gb_replace_temp.h @@ -0,0 +1,135 @@ +/*************************************************************************** + + gb_replace_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_replace.h" + +#include +#include +#include +#include +#include + +#ifndef HAVE_SETENV + +int setenv(const char *name, const char *value, int overwrite) +{ + char *env; + int env_size; + + if (!name || *name == 0) + return (-1); + + if (overwrite == 0) + { + if (getenv(name)) + return 0; + } + + env_size = strlen(name) + strlen(value) + 2; + env = malloc(env_size); + if (!env) + return (-1); + + strcpy(env, name); + strcat(env, "="); + strcat(env, value); + putenv(env); + + return 0; +} + +#endif + +#ifndef HAVE_UNSETENV + +extern char **environ; + +int unsetenv(const char *name) +{ + size_t len; + char **ep; + + if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) + return 0; + + len = strlen(name); + + ep = environ; + while (*ep != NULL) + { + if (!strncmp (*ep, name, len) && (*ep)[len] == '=') + { + char **dp = ep; + + do + dp[0] = dp[1]; + while (*dp++); + } + else + ++ep; + } + + return 0; +} + +#endif + +#ifndef HAVE_GETDOMAINNAME + +int getdomainname(char *name, size_t len) +{ +#if defined(HAVE_SYSINFO) && defined(SI_SRPC_DOMAIN) + sysinfo(SI_SRPC_DOMAIN, name, len); +#else + *name = 0; + return 0; +#endif +} + +#endif + +#ifndef HAVE_GETPT + +int getpt(void) +{ +#ifdef OS_BSD + return posix_openpt(O_RDWR | O_NOCTTY); +#else + return -1; +#endif +} + +#endif + +#ifndef HAVE_CFMAKERAW + +void cfmakeraw(struct termios *termios_p) +{ + termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + termios_p->c_oflag &= ~OPOST; + termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + termios_p->c_cflag &= ~(CSIZE|PARENB); + termios_p->c_cflag |= CS8; +} + +#endif diff --git a/main/share/gb_reserved.h b/main/share/gb_reserved.h new file mode 100644 index 00000000..4fcf32d1 --- /dev/null +++ b/main/share/gb_reserved.h @@ -0,0 +1,411 @@ +/*************************************************************************** + + gb_reserved.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_RESERVED_H +#define __GB_RESERVED_H + +#include "gb_common.h" +#include "gb_table.h" + +enum +{ + RSF_NONE = 0x0000, + RSF_OP = 0x0001, + RSF_TYPE = 0x0002, + RSF_ASGN = 0x0004, + RSF_NOT = 0x0008, // operator can have NOT before it + + RSF_N_ARY = 0x0000, + RSF_UNARY = 0x0010, + RSF_BINARY = 0x0020, + RSF_POST = 0x0030, + RSF_ONLY = 0x0040, + + RSF_OPN = 0x0001, + RSF_OP1 = 0x0011, + RSF_OP2 = 0x0021, + RSF_OPP = 0x0031, + RSF_OP2S = 0x0061, + RSF_OP2SM = 0x00A1, + + RSF_POINT = 0x0100, // pattern is a point or an exclamation mark + RSF_IDENT = 0x0200, // pattern waits for an identifier + RSF_CLASS = 0x0400, // pattern waits for a class + RSF_AS = 0x0800, // pattern waits for a datatype + RSF_PREV = 0x1000, // pattern uses the flags of the previous pattern + RSF_EVENT = 0x2000, // pattern waits for an event name + RSF_PUB = 0x4000, // pattern is PUBLIC, PRIVATE or STATIC + RSF_PREP = 0x8000, // pattern is a preprocessor instruction + + RSF_IMASK = 0xFF00 +}; + +enum { + RST_SAME = 17, // T_OBJECT + 1 + RST_ADD, + RST_AND, + RST_NOT, + RST_BCLR, + RST_MIN, + RST_MOD, + RST_GET, + RST_COLLECTION, + RST_EXEC, + RST_READ, + RST_DIV +}; + +enum { + RSJ_OTHER, + RSJ_ME, + RSJ_CLASS, + RSJ_STRUCT, + RSJ_SUB, + RSJ_CONST, + RSJ_READ, + RSJ_DATATYPE, + RSJ_OPTIONAL, + RSJ_BYREF, + RSJ_ERROR +}; + +#define RES_is_operator(value) (COMP_res_info[value].flag & RSF_OP) +#define RES_is_type(value) (COMP_res_info[value].flag & RSF_TYPE) +#define RES_is_assignment(value) (COMP_res_info[value].flag & RSF_ASGN) +#define RES_is_only(value) (COMP_res_info[value].flag & RSF_ONLY) +#define RES_get_ident_flag(value) (COMP_res_info[value].flag & RSF_IMASK) +#define RES_get_read_switch(value) (COMP_res_info[value].read_switch) + +#define RES_priority(_res) (COMP_res_info[_res].priority) + +#define RES_is_unary(value) ((COMP_res_info[value].flag & 0x30) == RSF_UNARY) +#define RES_is_binary(value) ((COMP_res_info[value].flag & 0x30) == RSF_BINARY) +#define RES_is_n_ary(value) ((COMP_res_info[value].flag & 0x30) == RSF_N_ARY) +#define RES_is_post(value) ((COMP_res_info[value].flag & 0x30) == RSF_POST) + +#define RES_get_type(_res) (COMP_res_info[_res].value) +#define RES_get_assignment_operator(_res) (COMP_res_info[_res].value) + +#define RES_can_have_not_before(value) (COMP_res_info[value].flag & RSF_NOT) + +#define RES_is_preprocessor(value) (COMP_res_info[value].flag & RSF_PREP) + +#define RES_is_identifier(value) (isalpha(*COMP_res_info[value].name)) + +typedef + enum { + RS_NONE, + RS_BOOLEAN, + RS_BYTE, + RS_DATE, + RS_SINGLE, + RS_FLOAT, + RS_INTEGER, + RS_LONG, + RS_SHORT, + RS_STRING, + RS_VARIANT, + RS_OBJECT, + RS_POINTER, + RS_CLASS, + RS_FUNCTION, + RS_STRUCT, + RS_CONST, + RS_PRIVATE, + RS_PUBLIC, + RS_STATIC, + RS_FAST, + RS_UNSAFE, + RS_PROPERTY, + RS_EVENT, + RS_INHERITS, + RS_IMPLEMENTS, + RS_EXPORT, + RS_AS, + RS_OF, + RS_DIM, + RS_NEW, + RS_PROCEDURE, + RS_SUB, + RS_RETURN, + RS_OPTIONAL, + RS_OUTPUT, + RS_DO, + RS_LOOP, + RS_WHILE, + RS_UNTIL, + RS_REPEAT, + RS_WEND, + RS_IF, + RS_THEN, + RS_ELSE, + RS_ENDIF, + RS_END, + RS_FOR, + RS_TO, + RS_DOWNTO, + RS_FROM, + RS_STEP, + RS_NEXT, + RS_SELECT, + RS_CASE, + RS_EXIT, + RS_BREAK, + RS_CONTINUE, + RS_GOTO, + RS_GOSUB, + RS_ON, + RS_ME, + RS_LAST, + RS_TRY, + RS_FINALLY, + RS_CATCH, + RS_WITH, + RS_TRUE, + RS_FALSE, + RS_SWAP, + RS_NULL, + RS_EXTERN, + RS_EACH, + RS_IN, + RS_DEFAULT, + RS_STOP, + RS_QUIT, + RS_RAISE, + RS_ERROR, + RS_SUPER, + RS_ENUM, + RS_LET, + RS_PINF, + RS_MINF, + RS_USE, + + RS_PRINT, + RS_INPUT, + RS_READ, + RS_PEEK, + RS_WRITE, + RS_OPEN, + RS_CLOSE, + RS_SEEK, + RS_APPEND, + RS_CREATE, + RS_BINARY, + RS_LINE, + RS_FLUSH, + RS_EXEC, + RS_SHELL, + RS_WAIT, + RS_SLEEP, + RS_KILL, + RS_MOVE, + RS_COPY, + RS_INC, + RS_DEC, + RS_MKDIR, + RS_RMDIR, + RS_WATCH, + RS_LINK, + RS_LOCK, + RS_UNLOCK, + RS_LIBRARY, + RS_DEBUG, + RS_ASSERT, + RS_PIPE, + RS_RANDOMIZE, + RS_BYREF, + RS_MEMORY, + RS_CHMOD, + RS_CHOWN, + RS_CHGRP, + + RS_P_IF, + RS_P_ELSE, + RS_P_ENDIF, + RS_P_CONST, + RS_P_LINE, + RS_P_INCLUDE, + RS_P_SCRIPT, + + RS_COLON, + RS_SCOLON, + RS_COMMA, + RS_3PTS, + RS_SHARP, + RS_AT, + RS_QUES, + RS_LBRC, + RS_RBRC, + RS_EQUAL, + RS_NEAR, + RS_LBRA, + RS_RBRA, + RS_PT, + RS_EXCL, + RS_PLUS, + RS_MINUS, + RS_STAR, + RS_SLASH, + RS_FLEX, + RS_AMP, + RS_FILE, + RS_GT, + RS_LT, + RS_GE, + RS_LE, + RS_NE, + RS_LSQR, + RS_RSQR, + RS_AND, + RS_OR, + RS_NOT, + RS_XOR, + RS_SHL, + RS_ASL, + RS_SHR, + RS_ASR, + RS_ROL, + RS_ROR, + RS_LSL, + RS_LSR, + RS_BSLASH, + RS_DIV, + RS_PERCENT, + RS_MOD, + RS_IS, + RS_NOT_IS, + RS_LIKE, + RS_NOT_LIKE, + RS_BEGINS, + RS_NOT_BEGINS, + RS_ENDS, + RS_NOT_ENDS, + + RS_PLUS_EQ, + RS_MINUS_EQ, + RS_STAR_EQ, + RS_SLASH_EQ, + RS_DIV_EQ, + RS_MOD_EQ, + RS_AMP_EQ, + RS_FILE_EQ, + } + RESERVED_ID; + +enum +{ + OP_NONE , + OP_COLON , + OP_EQUAL , + OP_NEAR , + OP_LBRA , + OP_RBRA , + OP_PT , + OP_EXCL , + OP_COMMA , + OP_3PTS , + OP_PLUS , + OP_MINUS , + OP_STAR , + OP_SLASH , + OP_FLEX , + OP_AMP , + OP_FILE , + OP_GT , + OP_LT , + OP_GE , + OP_LE , + OP_NE , + OP_LSQR , + OP_RSQR , + OP_AND , + OP_OR , + OP_NOT , + OP_XOR , + OP_SHL , + OP_ASL , + OP_SHR , + OP_ASR , + OP_ROL , + OP_ROR , + OP_LSL , + OP_LSR , + OP_DIV , + OP_MOD , + OP_IS , + OP_LIKE +}; + +typedef + struct { + const char *name; + short flag; + uchar value; + uchar read_switch; + uchar priority; + uchar type; + ushort code; + ushort subcode; + void (*func)(); + } + COMP_INFO; + +typedef + struct { + const char *name; + ushort opcode; + ushort optype; + uchar type; + short min_param; + short max_param; + } + SUBR_INFO; + +#ifndef __RESERVED_C + +EXTERN COMP_INFO COMP_res_info[]; +EXTERN SUBR_INFO COMP_subr_info[]; + +//EXTERN TABLE *COMP_res_table; +//EXTERN TABLE *COMP_subr_table; + +EXTERN int SUBR_VarPtr; +EXTERN int SUBR_IsMissing; +EXTERN int SUBR_Mid; +EXTERN int SUBR_MidS; +EXTERN int SUBR_SizeOf; + +#endif + +void RESERVED_init(void); +void RESERVED_exit(void); + +int RESERVED_find_word(const char *word, int len); +int RESERVED_find_subr(const char *word, int len); + +SUBR_INFO *SUBR_get(const char *subr_name); +SUBR_INFO *SUBR_get_from_opcode(ushort opcode, ushort optype); + +int RESERVED_get_from_opcode(ushort code); + +#endif diff --git a/main/share/gb_reserved_keyword.h b/main/share/gb_reserved_keyword.h new file mode 100644 index 00000000..e436f213 --- /dev/null +++ b/main/share/gb_reserved_keyword.h @@ -0,0 +1,600 @@ +/*************************************************************************** + + gb_reserved_keyword.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_RESERVED_KEYWORD_H +#define __GB_RESERVED_KEYWORD_H + +COMP_INFO COMP_res_info[] = +{ + { "" }, + + { "Boolean", RSF_TYPE, T_BOOLEAN, RSJ_DATATYPE }, + { "Byte", RSF_TYPE, T_BYTE, RSJ_DATATYPE }, + { "Date", RSF_TYPE, T_DATE, RSJ_DATATYPE }, + { "Single", RSF_TYPE, T_SINGLE, RSJ_DATATYPE }, + { "Float", RSF_TYPE, T_FLOAT, RSJ_DATATYPE }, + { "Integer", RSF_TYPE, T_INTEGER, RSJ_DATATYPE }, + { "Long", RSF_TYPE, T_LONG, RSJ_DATATYPE }, + { "Short", RSF_TYPE, T_SHORT, RSJ_DATATYPE }, + { "String", RSF_TYPE, T_STRING, RSJ_DATATYPE }, + { "Variant", RSF_TYPE, T_VARIANT, RSJ_DATATYPE }, + { "Object", RSF_TYPE, T_OBJECT, RSJ_DATATYPE }, + { "Pointer", RSF_TYPE, T_POINTER, RSJ_DATATYPE }, + { "Class", RSF_CLASS|RSF_AS, 0, RSJ_CLASS }, + { "Function", RSF_IDENT, 0, RSJ_SUB }, + { "Struct", RSF_PREV, 0, RSJ_STRUCT }, + { "Const", RSF_IDENT, 0, RSJ_CONST }, + { "Private", RSF_IDENT|RSF_PUB, }, + { "Public", RSF_IDENT|RSF_PUB, }, + { "Static", RSF_PUB }, + { "Fast", RSF_PUB }, + { "Unsafe", RSF_PUB }, + { "Property", RSF_IDENT }, + { "Event", RSF_IDENT|RSF_EVENT }, + { "Inherits", RSF_CLASS|RSF_AS }, + { "Implements" }, + { "Export" }, + { "As", RSF_AS }, + { "Of" }, + { "Dim", RSF_IDENT, }, + { "New", RSF_AS , 0, RSJ_ME }, + { "Procedure", RSF_IDENT, 0, RSJ_SUB }, + { "Sub", RSF_IDENT, 0, RSJ_SUB }, + { "Return" }, + { "Optional", 0, 0, RSJ_OPTIONAL }, + { "Output" }, + { "Do" }, + { "Loop" }, + { "While" }, + { "Until" }, + { "Repeat" }, + { "Wend" }, + { "If" }, + { "Then" }, + { "Else" }, + { "Endif" }, + { "End" }, + { "For" }, + { "To" }, + { "DownTo" }, + { "From" }, + { "Step" }, + { "Next" }, + { "Select" }, + { "Case" }, + { "Exit" }, + { "Break" }, + { "Continue" }, + { "Goto" }, + { "GoSub" }, + { "On" }, + { "Me", 0, 0, RSJ_ME }, + { "Last", 0, 0, RSJ_ME }, + { "Try" }, + { "Finally" }, + { "Catch" }, + { "With" }, + { "True", 0, 0, RSJ_ME }, + { "False", 0, 0, RSJ_ME }, + { "Swap" }, + { "Null", 0, 0, RSJ_ME }, + { "Extern", RSF_IDENT, 0, RSJ_CONST }, + { "Each" }, + { "In" }, + { "Default" }, + { "Stop" }, + { "Quit" }, + { "Raise", RSF_IDENT|RSF_EVENT }, + { "Error", 0, 0, RSJ_ERROR }, + { "Super", 0, 0, RSJ_ME }, + { "Enum", 0, 0, RSJ_CONST }, + { "Let" }, + { "+Inf" }, + { "-Inf" }, + { "Use" }, + + { "Print" }, + { "Input" }, + { "Read", RSF_PREV, 0, RSJ_READ }, + { "Peek", }, + { "Write", RSF_PREV, 0, RSJ_READ }, + { "Open" }, + { "Close" }, + { "Seek" }, + { "Append" }, + { "Create" }, + { "Binary" }, + { "Line" }, + { "Flush" }, + { "Exec" }, + { "Shell" }, + { "Wait" }, + { "Sleep" }, + { "Kill" }, + { "Move" }, + { "Copy" }, + { "Inc" }, + { "Dec" }, + { "Mkdir" }, + { "Rmdir" }, + { "Watch" }, + { "Link" }, + { "Lock" }, + { "Unlock" }, + { "Library" }, + { "Debug" }, + { "Assert" }, + { "Pipe" }, + { "Randomize" }, + { "ByRef", 0, 0, RSJ_BYREF }, + { "Memory" }, + { "Chmod" }, + { "Chown" }, + { "Chgrp" }, + + { "#If", RSF_PREP, }, + { "#Else", RSF_PREP, }, + { "#Endif", RSF_PREP, }, + { "#Const", RSF_PREP, }, + { "#Line", RSF_PREP, }, + { "#Include", RSF_PREP, }, + { "#Script", RSF_PREP, }, + + { ":", RSF_NONE, OP_COLON, 0, 0, T_OBJECT }, // Use for the immediate collection syntax + { ";" }, + { "," }, + { "..." }, + { "#" }, + { "@" }, + { "?" }, + { "{" }, + { "}" }, + { "=", RSF_OP2S, OP_EQUAL, 0, 4, T_BOOLEAN, C_EQ }, + { "==", RSF_OP2S, OP_NEAR, 0, 4, T_BOOLEAN, C_NEAR }, + { "(", RSF_OPP, OP_LBRA, 0, 12 }, + { ")", }, + { ".", RSF_OP2|RSF_POINT, OP_PT, 0, 20, T_VARIANT }, + { "!", RSF_OP2|RSF_POINT, OP_EXCL, 0, 20, T_VARIANT }, + { "+", RSF_OP2, OP_PLUS, 0, 5, RST_ADD, C_ADD }, + { "-", RSF_OP2, OP_MINUS, 0, 5, RST_ADD, C_SUB }, + { "*", RSF_OP2, OP_STAR, 0, 6, RST_ADD, C_MUL }, + { "/", RSF_OP2, OP_SLASH, 0, 6, RST_DIV, C_DIV }, + { "^", RSF_OP2S, OP_FLEX, 0, 7, RST_DIV, C_POW }, + { "&", RSF_OPN, OP_AMP, 0, 9, T_STRING, C_CAT }, + { "&/", RSF_OPN, OP_FILE, 0, 8, T_STRING, C_FILE }, + { ">", RSF_OP2S, OP_GT, 0, 4, T_BOOLEAN, C_GT }, + { "<", RSF_OP2S, OP_LT, 0, 4, T_BOOLEAN, C_LT }, + { ">=", RSF_OP2S, OP_GE, 0, 4, T_BOOLEAN, C_GE }, + { "<=", RSF_OP2S, OP_LE, 0, 4, T_BOOLEAN, C_LE }, + { "<>", RSF_OP2S, OP_NE, 0, 4, T_BOOLEAN, C_NE }, + { "[", RSF_OPP, OP_LSQR, 0, 12, RST_GET }, + { "]", RSF_NONE, OP_RSQR, 0, 0, T_OBJECT }, // Use for the immediate array syntax + { "And", RSF_OP2SM, OP_AND, 0, 2, RST_AND, C_AND }, + { "Or", RSF_OP2SM, OP_OR, 0, 2, RST_AND, C_OR }, + { "Not", RSF_OP1, OP_NOT, 0, 10, RST_NOT, C_NOT }, + { "Xor", RSF_OP2SM, OP_XOR, 0, 2, RST_AND, C_XOR }, + { "Shl", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 5 }, + { "Asl", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 5 }, + { "Shr", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 6 }, + { "Asr", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 6 }, + { "Rol", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 7 }, + { "Ror", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 8 }, + { "Lsl", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 9 }, + { "Lsr", RSF_OP2, OP_SHL, 0, 2, RST_BCLR, C_BCLR, 10 }, + { "\\", RSF_OP2S, OP_DIV, 0, 6, T_INTEGER, C_QUO }, + { "Div", RSF_OP2S, OP_DIV, 0, 6, T_INTEGER, C_QUO }, + { "%", RSF_OP2S, OP_MOD, 0, 6, RST_MOD, C_REM }, + { "Mod", RSF_OP2S, OP_MOD, 0, 6, RST_MOD, C_REM }, + { "Is", RSF_OP2|RSF_AS|RSF_NOT, OP_IS, 0, 11, T_BOOLEAN, C_IS, 0 }, + { "", RSF_OP2|RSF_AS, OP_IS, 0, 11, T_BOOLEAN, C_IS, 1 }, + { "Like", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 0 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 4 }, // NOT LIKE + { "Begins", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 1 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 5 }, // NOT BEGINS + { "Ends", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 2 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 6 }, // NOT ENDS + { "Match", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 3 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 7 }, // NOT MATCH + + { "+=", RSF_ASGN, RS_PLUS }, + { "-=", RSF_ASGN, RS_MINUS }, + { "*=", RSF_ASGN, RS_STAR }, + { "/=", RSF_ASGN, RS_SLASH }, + { "\\=", RSF_ASGN, RS_BSLASH }, + { "%=", RSF_ASGN, RS_PERCENT }, + { "&=", RSF_ASGN, RS_AMP }, + { "&/=", RSF_ASGN, RS_FILE }, + { "^=", RSF_ASGN, RS_FLEX }, + + { NULL } +}; + +SUBR_INFO COMP_subr_info[] = +{ + { "Left$", 0, 0, T_STRING, 1, 2 }, + { "Left", 0, 0, T_STRING, 1, 2 }, + + { "Mid$", 1, 0, T_STRING, 2, 3 }, + { "Mid", 1, 0, T_STRING, 2, 3 }, + + { "Right$", 2, 0, T_STRING, 1, 2 }, + { "Right", 2, 0, T_STRING, 1, 2 }, + + { "Len", 3, 0, T_INTEGER, 1 }, + + { "Space$", 4, 0, T_STRING, 1 }, + { "Space", 4, 0, T_STRING, 1 }, + + { "String$", 5, 0, T_STRING, 2 }, + { "String", 5, 0, T_STRING, 2 }, + + { "Trim$", 6, 0, T_STRING, 1 }, + { "Trim", 6, 0, T_STRING, 1 }, + + { "LTrim$", 6, 1, T_STRING, 1 }, + { "LTrim", 6, 1, T_STRING, 1 }, + + { "RTrim$", 6, 2, T_STRING, 1 }, + { "RTrim", 6, 2, T_STRING, 1 }, + + { "Upper$", 7, 0, T_STRING, 1 }, + { "Upper", 7, 0, T_STRING, 1 }, + { "UCase$", 7, 0, T_STRING, 1 }, + { "UCase", 7, 0, T_STRING, 1 }, + + { "Lower$", 7, 1, T_STRING, 1 }, + { "Lower", 7, 1, T_STRING, 1 }, + { "LCase$", 7, 1, T_STRING, 1 }, + { "LCase", 7, 1, T_STRING, 1 }, + + { "Oct$", 8, 0, T_STRING, 1, 2 }, // CODE_OCT + { "Oct", 8, 0, T_STRING, 1, 2 }, + + { "Chr$", 9, 0, T_STRING, 1 }, + { "Chr", 9, 0, T_STRING, 1 }, + + { "Asc", 10, 0, T_INTEGER, 1, 2 }, + + { "InStr", 11, 0, T_INTEGER, 2, 4 }, + + { "RInStr", 12, 0, T_INTEGER, 2, 4 }, // CODE_RINSTR + + { "Subst$", 13, 0, T_STRING, 1, 63 }, + { "Subst", 13, 0, T_STRING, 1, 63 }, + + { "Replace$", 14, 0, T_STRING, 3, 4 }, + { "Replace", 14, 0, T_STRING, 3, 4 }, + + { "Split", 15, 0, T_OBJECT, 1, 5 }, + { "Scan", 16, 0, T_OBJECT, 2 }, + + { "Comp", 17, 0, T_INTEGER, 2, 3 }, + + { "Conv", 18, 0, T_STRING, 3 }, + { "Conv$", 18, 0, T_STRING, 3 }, + { "SConv", 19, 0, T_STRING, 1 }, + { "SConv$", 19, 0, T_STRING, 1 }, + { "DConv", 19, 1, T_STRING, 1 }, + { "DConv$", 19, 1, T_STRING, 1 }, + + { "Abs", 20, 0, RST_SAME, 1 }, // CODE_ABS + { "Int", 21, 0, RST_SAME, 1 }, + { "Fix", 22, 0, RST_SAME, 1 }, + { "Sgn", 23, 0, T_INTEGER, 1 }, + + { "Frac", 24, 1, T_FLOAT, 1 }, + { "Log", 24, 2, T_FLOAT, 1 }, + { "Exp", 24, 3, T_FLOAT, 1 }, + { "Sqr", 24, 4, T_FLOAT, 1 }, + { "Sin", 24, 5, T_FLOAT, 1 }, + { "Cos", 24, 6, T_FLOAT, 1 }, + { "Tan", 24, 7, T_FLOAT, 1 }, + { "Atn", 24, 8, T_FLOAT, 1 }, + { "ATan", 24, 8, T_FLOAT, 1 }, + { "Asn", 24, 9, T_FLOAT, 1 }, + { "ASin", 24, 9, T_FLOAT, 1 }, + { "Acs", 24, 10, T_FLOAT, 1 }, + { "ACos", 24, 10, T_FLOAT, 1 }, + { "Deg", 24, 11, T_FLOAT, 1 }, + { "Rad", 24, 12, T_FLOAT, 1 }, + { "Log10", 24, 13, T_FLOAT, 1 }, + { "Sinh", 24, 14, T_FLOAT, 1 }, + { "Cosh", 24, 15, T_FLOAT, 1 }, + { "Tanh", 24, 16, T_FLOAT, 1 }, + { "Asnh", 24, 17, T_FLOAT, 1 }, + { "ASinh", 24, 17, T_FLOAT, 1 }, + { "Acsh", 24, 18, T_FLOAT, 1 }, + { "ACosh", 24, 18, T_FLOAT, 1 }, + { "Atnh", 24, 19, T_FLOAT, 1 }, + { "ATanh", 24, 19, T_FLOAT, 1 }, + { "Exp2", 24, 20, T_FLOAT, 1 }, + { "Exp10", 24, 21, T_FLOAT, 1 }, + { "Log2", 24, 22, T_FLOAT, 1 }, + { "Cbr", 24, 23, T_FLOAT, 1 }, + { "Expm", 24, 24, T_FLOAT, 1 }, + { "Logp", 24, 25, T_FLOAT, 1 }, + { "Floor", 24, 26, T_FLOAT, 1 }, + { "Ceil", 24, 27, T_FLOAT, 1 }, + + { "Pi", 25, 0, T_FLOAT, 0, 1 }, + { "Round", 26, 0, T_FLOAT, 1, 2 }, +#ifndef __EVAL_RESERVED_C + { ".Randomize", 27, 0, T_VOID, 0, 1 }, +#endif + { "Rnd", 28, 0, T_FLOAT, 0, 2 }, + { "Min", 29, 0, RST_MIN, 2, }, + { "Max", 30, 0, RST_MIN, 2, }, // CODE_MAX + + { "If", 31, 0, T_VARIANT, 3, }, + { "IIf", 31, 0, T_VARIANT, 3, }, + { "Choose", 32, 0, T_VARIANT, 1, 63 }, + + { ".Array", 33, 0, T_OBJECT, 0, 63 }, // Needed for Eval("[...]") + + { "ATan2", 34, 1, T_FLOAT, 2 }, + { "Atn2", 34, 1, T_FLOAT, 2 }, + { "Ang", 34, 2, T_FLOAT, 2 }, + { "Hyp", 34, 3, T_FLOAT, 2 }, + { "Mag", 34, 3, T_FLOAT, 2 }, + + { "IsAscii", 35, 1, T_BOOLEAN, 1 }, + { "IsLetter", 35, 2, T_BOOLEAN, 1 }, + { "IsLCase", 35, 3, T_BOOLEAN, 1 }, + { "IsLower", 35, 3, T_BOOLEAN, 1 }, + { "IsUCase", 35, 4, T_BOOLEAN, 1 }, + { "IsUpper", 35, 4, T_BOOLEAN, 1 }, + { "IsDigit", 35, 5, T_BOOLEAN, 1 }, + { "IsHexa", 35, 6, T_BOOLEAN, 1 }, + { "IsSpace", 35, 7, T_BOOLEAN, 1 }, + { "IsBlank", 35, 8, T_BOOLEAN, 1 }, + { "IsPunct", 35, 9, T_BOOLEAN, 1 }, + { "IsAlnum", 35, 10, T_BOOLEAN, 1 }, + + { "BClr", 36, 1, RST_BCLR, 2 }, // CODE_BCLR + { "BSet", 36, 2, RST_BCLR, 2 }, + { "BTst", 36, 3, RST_BCLR, 2 }, + { "BChg", 36, 4, RST_BCLR, 2 }, + { "Shl", 36, 5, RST_BCLR, 2 }, + { "Asl", 36, 5, RST_BCLR, 2 }, + { "Shr", 36, 6, RST_BCLR, 2 }, + { "Asr", 36, 6, RST_BCLR, 2 }, + { "Rol", 36, 7, RST_BCLR, 2 }, + { "Ror", 36, 8, RST_BCLR, 2 }, + { "Lsl", 36, 9, RST_BCLR, 2 }, + { "Lsr", 36, 10, RST_BCLR, 2 }, + + { "IsBoolean", 37, 1, T_BOOLEAN, 1 }, + { "IsInteger", 37, 4, T_BOOLEAN, 1 }, + { "IsLong", 37, 5, T_BOOLEAN, 1 }, + { "IsFloat", 37, 7, T_BOOLEAN, 1 }, + { "IsDate", 37, 8, T_BOOLEAN, 1 }, + { "IsNumber", 37, 14, T_BOOLEAN, 1 }, + { "IsNull", 37, 15, T_BOOLEAN, 1 }, + + { "TypeOf", 38, 0, T_INTEGER, 1 }, + { "SizeOf", 38, 1, T_INTEGER, 1 }, + + { "CBool", 39, 1, T_BOOLEAN, 1 }, // CODE_CONV + { "CBoolean", 39, 1, T_BOOLEAN, 1 }, + { "CByte", 39, 2, T_BYTE, 1 }, + { "CShort", 39, 3, T_SHORT, 1 }, + { "CInt", 39, 4, T_INTEGER, 1 }, + { "CInteger", 39, 4, T_INTEGER, 1 }, + { "CLong", 39, 5, T_LONG, 1 }, + { "CSingle", 39, 6, T_SINGLE, 1 }, + { "CFloat", 39, 7, T_FLOAT, 1 }, + { "CDate", 39, 8, T_DATE, 1 }, + { "CStr", 39, 9, T_STRING, 1 }, + { "CString", 39, 9, T_STRING, 1 }, + { "CPointer", 39, 11, T_POINTER, 1 }, + { "CVariant", 39, 12, T_VARIANT, 1 }, + + { "Bin$", 40, 0, T_STRING, 1, 2 }, // CODE_BIN + { "Bin", 40, 0, T_STRING, 1, 2 }, + + { "Hex$", 41, 0, T_STRING, 1, 2 }, // CODE_HEX + { "Hex", 41, 0, T_STRING, 1, 2 }, + + { "Val", 42, 0, T_VARIANT, 1 }, + + { "Str$", 43, 0, T_STRING, 1 }, + { "Str", 43, 0, T_STRING, 1 }, + + { "Format$", 44, 0, T_STRING, 1, 2 }, + { "Format", 44, 0, T_STRING, 1, 2 }, + + { "Timer", 45, 0, T_FLOAT, 0 }, + + { "Now", 46, 0, T_DATE, 0 }, + + { "Year", 47, 1, T_INTEGER, 1 }, + { "Month", 47, 2, T_INTEGER, 1 }, + { "Day", 47, 3, T_INTEGER, 1 }, + { "Hour", 47, 4, T_INTEGER, 1 }, + { "Minute", 47, 5, T_INTEGER, 1 }, + { "Second", 47, 6, T_INTEGER, 1 }, + { "WeekDay", 47, 7, T_INTEGER, 1 }, + { "Week", 48, 0, T_INTEGER, 0, 3 }, + + { "Date", 49, 0, T_DATE, 0, 7 }, + { "Time", 50, 0, T_DATE, 0, 4 }, + + { "DateAdd", 51, 0, T_DATE, 3 }, + { "DateDiff", 51, 1, T_INTEGER, 3 }, + + { "Eval", 52, 0, T_VARIANT, 1, 2 }, + +#ifndef __EVAL_RESERVED_C + { ".Error", 53, 0, T_BOOLEAN, 0 }, + { ".Debug", 54, 0, T_VOID, 0, 1 }, // CODE_DEBUG + + { ".Wait", 55, 0, T_VOID, 0 }, + { ".WaitDelay", 55, 1, T_VOID, 1 }, + { ".WaitNext", 55, 2, T_VOID, 0 }, + + { ".Open", 56, 0, T_OBJECT, 2 }, + { ".OpenMemory", 56, 1, T_OBJECT, 2 }, + { ".Close", 57, 0, T_VOID, 1 }, + { ".Input", 58, 0, T_STRING, 0, 1 }, + { ".LineInput", 59, 0, T_STRING, 1 }, + { ".Print", 60, 0, T_VOID, 1, 63 }, + { ".Read", 61, 0, RST_READ, 2, }, + { ".ReadBytes", 61, 1, T_STRING, 2, }, + { ".Peek", 61, 2, T_STRING, 2, }, + { ".Write", 62, 0, T_VOID, 3, }, + { ".WriteBytes", 62, 1, T_VOID, 3, }, + { ".Flush", 63, 0, T_VOID, 1 }, + + { ".Lock", 64, 0, T_OBJECT, 1 }, + { ".Unlock", 64, 1, T_VOID, 1 }, + { ".LockWait", 64, 2, T_VOID, 2 }, + + { ".InputFrom", 65, 0, T_VOID, 1 }, + { ".OutputTo", 65, 1, T_VOID, 1 }, + { ".ErrorTo", 65, 2, T_VOID, 1 }, +#endif + { "Eof", 66, 0, T_BOOLEAN, 0, 1 }, + { "Lof", 67, 0, T_LONG, 0, 1 }, + { "Seek", 68, 0, T_LONG, 1, 3 }, +#ifndef __EVAL_RESERVED_C + { ".Kill", 69, 0, T_VOID, 1 }, + { ".Mkdir", 69, 1, T_VOID, 1 }, + { ".Rmdir", 69, 2, T_VOID, 1 }, +#endif +//{ ".Mkdir", 70, 0, 1 }, // The old Mkdir from 3.0 + { "Even", 70, 1, T_BOOLEAN, 1 }, + { "Odd", 70, 2, T_BOOLEAN, 1 }, +//{ ".Rmdir", 71, 0, 1 }, // The old Rmdir from 3.0 + { "Rand", 71, 0, T_INTEGER, 1, 2 }, +#ifndef __EVAL_RESERVED_C + { ".Move", 72, 0, T_VOID, 2 }, + { ".Copy", 72, 1, T_VOID, 2 }, + { ".Link", 72, 2, T_VOID, 2 }, + { ".Chmod", 72, 3, T_VOID, 2 }, + { ".Chown", 72, 4, T_VOID, 2 }, + { ".Chgrp", 72, 5, T_VOID, 2 }, + { ".MoveKill", 72, 6, T_VOID, 2 }, +#endif + { "Swap", 73, 0, T_STRING, 1, 2 }, // at least one argument, because 73 is a deprecated Copy() too. + { "Swap$", 73, 0, T_STRING, 1, 2 }, + + { "IsNan", 74, 1, T_BOOLEAN, 1 }, + { "IsInf", 74, 2, T_BOOLEAN, 1 }, + + { "Exist", 75, 0, T_BOOLEAN, 1, 2 }, + { "Access", 76, 0, T_BOOLEAN, 1, 2 }, + { "Stat", 77, 0, T_OBJECT, 1, 2 }, + { "Dfree", 78, 0, T_LONG, 1 }, + + { "Temp", 79, 0, T_STRING, 0, 1 }, + { "Temp$", 79, 0, T_STRING, 0, 1 }, + + { "IsDir", 80, 0, T_BOOLEAN, 1 }, + + { "Dir", 81, 0, T_OBJECT, 1, 3 }, + { "RDir", 82, 0, T_OBJECT, 1, 4 }, + +#ifndef __EVAL_RESERVED_C + { ".Exec", 83, 0, RST_EXEC, 4 }, + { ".Shell", 83, 1, RST_EXEC, 4 }, +#endif + + { "Alloc", 84, 0, T_POINTER, 1, 2 }, + { "Free", 85, 0, T_VOID, 1 }, + { "Realloc", 86, 0, T_POINTER, 2, 3 }, + { "Str@", 87, 0, T_STRING, 1, 2 }, + { "String@", 87, 0, T_STRING, 1, 2 }, + +#ifndef __EVAL_RESERVED_C + { ".Sleep", 88, 0, T_VOID, 1 }, + { ".Use", 88, 1, T_VOID, 1 }, + { ".CheckExec", 88, 2, T_VOID, 1 }, +#endif + + { "VarPtr", 89, 0, T_POINTER, 1 }, + { "IsMissing", 89, 1, T_BOOLEAN, 1 }, + + { ".Collection", 90, 0, RST_COLLECTION, 1, 63 }, + + { "Tr", 91, 0, T_STRING, 1 }, + { "Tr$", 91, 0, T_STRING, 1 }, + + { "Quote", 92, 0, T_STRING, 1 }, + { "Quote$", 92, 0, T_STRING, 1 }, + { "Shell", 92, 1, T_STRING, 1 }, + { "Shell$", 92, 1, T_STRING, 1 }, + { "Html", 92, 2, T_STRING, 1 }, + { "Html$", 92, 2, T_STRING, 1 }, + { "Base64", 92, 3, T_STRING, 1 }, + { "Base64$", 92, 3, T_STRING, 1 }, + { "Url", 92, 4, T_STRING, 1 }, + { "Url$", 92, 4, T_STRING, 1 }, + + { "UnQuote", 93, 0, T_STRING, 1 }, + { "UnQuote$", 93, 0, T_STRING, 1 }, + { "UnBase64", 93, 1, T_STRING, 1 }, + { "UnBase64$", 93, 1, T_STRING, 1 }, + { "FromBase64", 93, 1, T_STRING, 1 }, + { "FromBase64$", 93, 1, T_STRING, 1 }, + { "FromUrl", 93, 2, T_STRING, 1 }, + { "FromUrl$", 93, 2, T_STRING, 1 }, + + { "MkBool", 94, 1, T_STRING, 1 }, + { "MkBool$", 94, 1, T_STRING, 1 }, + { "MkBoolean", 94, 1, T_STRING, 1 }, + { "MkBoolean$", 94, 1, T_STRING, 1 }, + { "MkByte", 94, 2, T_STRING, 1 }, + { "MkByte$", 94, 2, T_STRING, 1 }, + { "MkShort", 94, 3, T_STRING, 1 }, + { "MkShort$", 94, 3, T_STRING, 1 }, + { "MkInt", 94, 4, T_STRING, 1 }, + { "MkInt$", 94, 4, T_STRING, 1 }, + { "MkInteger", 94, 4, T_STRING, 1 }, + { "MkInteger$", 94, 4, T_STRING, 1 }, + { "MkLong", 94, 5, T_STRING, 1 }, + { "MkLong$", 94, 5, T_STRING, 1 }, + { "MkSingle", 94, 6, T_STRING, 1 }, + { "MkSingle$", 94, 6, T_STRING, 1 }, + { "MkFloat", 94, 7, T_STRING, 1 }, + { "MkFloat$", 94, 7, T_STRING, 1 }, + { "MkDate", 94, 8, T_STRING, 1 }, + { "MkDate$", 94, 8, T_STRING, 1 }, + { "MkPointer", 94, 11, T_STRING, 1 }, + { "MkPointer$", 94, 11, T_STRING, 1 }, + + { "Bool@", 95, 1, T_BOOLEAN, 1 }, + { "Boolean@", 95, 1, T_BOOLEAN, 1 }, + { "Byte@", 95, 2, T_BYTE, 1 }, + { "Short@", 95, 3, T_SHORT, 1 }, + { "Int@", 95, 4, T_INTEGER, 1 }, + { "Integer@", 95, 4, T_INTEGER, 1 }, + { "Long@", 95, 5, T_LONG, 1 }, + { "Single@", 95, 6, T_SINGLE, 1 }, + { "Float@", 95, 7, T_FLOAT, 1 }, + { "Date@", 95, 8, T_DATE, 1 }, + { "Pointer@", 95, 11, T_POINTER, 1 }, + + { NULL } +}; + +#endif diff --git a/main/share/gb_reserved_temp.h b/main/share/gb_reserved_temp.h new file mode 100644 index 00000000..2455654c --- /dev/null +++ b/main/share/gb_reserved_temp.h @@ -0,0 +1,982 @@ +/*************************************************************************** + + gb_reserved_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_pcode.h" +#include "gb_type_common.h" +#include "gb_reserved.h" + +/* If this file is modified, don't forget to update GAMBAS_PCODE_VERSION in acinclude.m4 if needed */ + +#include "gb_reserved_keyword.h" + +int SUBR_VarPtr; +int SUBR_IsMissing; +int SUBR_Mid; +int SUBR_MidS; +int SUBR_SizeOf; + +static uchar _operator_table[256] = { 0 }; + +static int get_index(const char *subr_name) +{ + return RESERVED_find_subr(subr_name, strlen(subr_name)); +} + +void RESERVED_init(void) +{ + COMP_INFO *info; + SUBR_INFO *subr; + int len; + int i; + + /* Reserved words symbol table */ + + //TABLE_create(&COMP_res_table, 0, TF_IGNORE_CASE); + for (info = &COMP_res_info[0], i = 0; info->name; info++, i++) + { + len = strlen(info->name); + if (len == 1) + _operator_table[(uint)*info->name] = i; + + //TABLE_add_symbol(COMP_res_table, info->name, len, &index); + } + + /* Subroutines table */ + + //TABLE_create(&COMP_subr_table, 0, TF_IGNORE_CASE); + for (subr = &COMP_subr_info[0]; subr->name; subr++) + { + if (subr->max_param == 0) + subr->max_param = subr->min_param; + + //TABLE_add_symbol(COMP_subr_table, subr->name, strlen(subr->name), &index); + } + + SUBR_VarPtr = get_index("VarPtr"); + SUBR_IsMissing = get_index("IsMissing"); + SUBR_Mid = get_index("Mid"); + SUBR_MidS = get_index("Mid$"); + SUBR_SizeOf = get_index("SizeOf"); +} + + +void RESERVED_exit(void) +{ + //TABLE_delete(&COMP_res_table); + //TABLE_delete(&COMP_subr_table); +} + + +SUBR_INFO *SUBR_get(const char *subr_name) +{ + int index = get_index(subr_name); + + if (index == NO_SYMBOL) + return NULL; + else + return &COMP_subr_info[index]; +} + + +SUBR_INFO *SUBR_get_from_opcode(ushort opcode, ushort optype) +{ + SUBR_INFO *si; + + for (si = COMP_subr_info; si->name; si++) + { + if (si->opcode == opcode) + { + if (si->min_param < si->max_param) + return si; + else if (si->optype == optype) + return si; + } + } + + /*ERROR_panic("SUBR_get_from_opcode: SUBR not found !");*/ + return NULL; +} + + +int RESERVED_get_from_opcode(ushort code) +{ + COMP_INFO *ci; + int n; + + code = code & 0xFF00; + + for (ci = COMP_res_info, n = 0; ci->name; ci++, n++) + { + if (ci->code == code) + return n; + } + + return -1; +} + + +int RESERVED_find_word(const char *word, int len) +{ + int ind; + + if (len == 1) + { + ind = _operator_table[(uint)*word]; + if (ind) + return ind; + else + return -1; + } + + // No symbol longer than 10 characters in the table + + if (len > 10) + return -1; + + // Now find it + + static void *jump[] = { + &&__20, &&__21, &&__22, &&__23, &&__24, &&__25, &&__26, &&__27, + &&__28, &&__29, &&__2A, &&__2B, &&__2C, &&__2D, &&__2E, &&__2F, + &&__30, &&__31, &&__32, &&__33, &&__34, &&__35, &&__36, &&__37, + &&__38, &&__39, &&__3A, &&__3B, &&__3C, &&__3D, &&__3E, &&__3F, + &&__40, &&__41, &&__42, &&__43, &&__44, &&__45, &&__46, &&__47, + &&__48, &&__49, &&__4A, &&__4B, &&__4C, &&__4D, &&__4E, &&__4F, + &&__50, &&__51, &&__52, &&__53, &&__54, &&__55, &&__56, &&__57, + &&__58, &&__59, &&__5A, &&__5B, &&__5C, &&__5D, &&__5E, &&__5F, + &&__60, &&__61, &&__62, &&__63, &&__64, &&__65, &&__66, &&__67, + &&__68, &&__69, &&__6A, &&__6B, &&__6C, &&__6D, &&__6E, &&__6F, + &&__70, &&__71, &&__72, &&__73, &&__74, &&__75, &&__76, &&__77, + &&__78, &&__79, &&__7A, &&__7B, &&__7C, &&__7D, &&__7E, + }; + + goto *jump[*word - 32]; + +__20: + return -1; +__21: + return -1; +__22: + return -1; +__23: + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'f') return 123; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'l' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 124; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'e') return 127; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 's' && tolower(word[5]) == 't') return 126; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd' && tolower(word[4]) == 'i' && tolower(word[5]) == 'f') return 125; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'c' && tolower(word[3]) == 'r' && tolower(word[4]) == 'i' && tolower(word[5]) == 'p' && tolower(word[6]) == 't') return 129; + if (len == 8 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'c' && tolower(word[4]) == 'l' && tolower(word[5]) == 'u' && tolower(word[6]) == 'd' && tolower(word[7]) == 'e') return 128; + return -1; +__24: + return -1; +__25: + if (len == 2 && word[1] == '=') return 190; + return -1; +__26: + if (len == 2 && word[1] == '/') return 151; + if (len == 2 && word[1] == '=') return 191; + if (len == 3 && word[1] == '/' && word[2] == '=') return 192; + return -1; +__27: + return -1; +__28: + return -1; +__29: + return -1; +__2A: + if (len == 2 && word[1] == '=') return 187; + return -1; +__2B: + if (len == 2 && word[1] == '=') return 185; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'f') return 82; + return -1; +__2C: + return -1; +__2D: + if (len == 2 && word[1] == '=') return 186; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'f') return 83; + return -1; +__2E: + if (len == 3 && word[1] == '.' && word[2] == '.') return 133; + return -1; +__2F: + if (len == 2 && word[1] == '=') return 188; + return -1; +__30: + return -1; +__31: + return -1; +__32: + return -1; +__33: + return -1; +__34: + return -1; +__35: + return -1; +__36: + return -1; +__37: + return -1; +__38: + return -1; +__39: + return -1; +__3A: + return -1; +__3B: + return -1; +__3C: + if (len == 2 && word[1] == '=') return 155; + if (len == 2 && word[1] == '>') return 156; + return -1; +__3D: + if (len == 2 && word[1] == '=') return 140; + return -1; +__3E: + if (len == 2 && word[1] == '=') return 154; + return -1; +__3F: + return -1; +__40: + return -1; +__41: +__61: + if (len == 2 && tolower(word[1]) == 's') return 27; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd') return 159; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'l') return 164; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'r') return 166; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'n' && tolower(word[5]) == 'd') return 93; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 's' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && tolower(word[5]) == 't') return 115; + return -1; +__42: +__62: + if (len == 4 && tolower(word[1]) == 'y' && tolower(word[2]) == 't' && tolower(word[3]) == 'e') return 2; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 'k') return 56; + if (len == 5 && tolower(word[1]) == 'y' && tolower(word[2]) == 'r' && tolower(word[3]) == 'e' && tolower(word[4]) == 'f') return 118; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'g' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 's') return 179; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'a' && tolower(word[4]) == 'r' && tolower(word[5]) == 'y') return 95; + if (len == 7 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e' && tolower(word[5]) == 'a' && tolower(word[6]) == 'n') return 1; + return -1; +__43: +__63: + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 'e') return 54; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'p' && tolower(word[3]) == 'y') return 104; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'c' && tolower(word[4]) == 'h') return 65; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'g' && tolower(word[3]) == 'r' && tolower(word[4]) == 'p') return 122; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'm' && tolower(word[3]) == 'o' && tolower(word[4]) == 'd') return 120; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'w' && tolower(word[4]) == 'n') return 121; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 's') return 13; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 91; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 's' && tolower(word[4]) == 't') return 16; + if (len == 6 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 94; + if (len == 8 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 't' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 'u' && tolower(word[7]) == 'e') return 57; + return -1; +__44: +__64: + if (len == 2 && tolower(word[1]) == 'o') return 36; + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'c') return 106; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'm') return 29; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'v') return 172; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e') return 3; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'b' && tolower(word[3]) == 'u' && tolower(word[4]) == 'g') return 114; + if (len == 6 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'o') return 49; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'f' && tolower(word[3]) == 'a' && tolower(word[4]) == 'u' && tolower(word[5]) == 'l' && tolower(word[6]) == 't') return 74; + return -1; +__45: +__65: + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd') return 46; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'c' && tolower(word[3]) == 'h') return 72; + if (len == 4 && tolower(word[1]) == 'l' && tolower(word[2]) == 's' && tolower(word[3]) == 'e') return 44; + if (len == 4 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd' && tolower(word[3]) == 's') return 181; + if (len == 4 && tolower(word[1]) == 'n' && tolower(word[2]) == 'u' && tolower(word[3]) == 'm') return 80; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'e' && tolower(word[3]) == 'c') return 98; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'i' && tolower(word[3]) == 't') return 55; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'f') return 45; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'r' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r') return 78; + if (len == 5 && tolower(word[1]) == 'v' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n' && tolower(word[4]) == 't') return 23; + if (len == 6 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r' && tolower(word[5]) == 't') return 26; + if (len == 6 && tolower(word[1]) == 'x' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && tolower(word[5]) == 'n') return 71; + return -1; +__46: +__66: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r') return 47; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 't') return 20; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm') return 50; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 'l' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 68; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'a' && tolower(word[4]) == 't') return 5; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'u' && tolower(word[3]) == 's' && tolower(word[4]) == 'h') return 97; + if (len == 7 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'a' && tolower(word[4]) == 'l' && tolower(word[5]) == 'l' && tolower(word[6]) == 'y') return 64; + if (len == 8 && tolower(word[1]) == 'u' && tolower(word[2]) == 'n' && tolower(word[3]) == 'c' && tolower(word[4]) == 't' && tolower(word[5]) == 'i' && tolower(word[6]) == 'o' && tolower(word[7]) == 'n') return 14; + return -1; +__47: +__67: + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 't' && tolower(word[3]) == 'o') return 58; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 's' && tolower(word[3]) == 'u' && tolower(word[4]) == 'b') return 59; + return -1; +__48: +__68: + return -1; +__49: +__69: + if (len == 2 && tolower(word[1]) == 'f') return 42; + if (len == 2 && tolower(word[1]) == 'n') return 73; + if (len == 2 && tolower(word[1]) == 's') return 175; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'c') return 105; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 'p' && tolower(word[3]) == 'u' && tolower(word[4]) == 't') return 86; + if (len == 7 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'g' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 6; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && tolower(word[5]) == 'i' && tolower(word[6]) == 't' && tolower(word[7]) == 's') return 24; + if (len == 10 && tolower(word[1]) == 'm' && tolower(word[2]) == 'p' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e' && tolower(word[5]) == 'm' && tolower(word[6]) == 'e' && tolower(word[7]) == 'n' && tolower(word[8]) == 't' && tolower(word[9]) == 's') return 25; + return -1; +__4A: +__6A: + return -1; +__4B: +__6B: + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'l' && tolower(word[3]) == 'l') return 102; + return -1; +__4C: +__6C: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 't') return 81; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'l') return 169; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'r') return 170; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 't') return 62; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'k' && tolower(word[3]) == 'e') return 177; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'e') return 96; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'k') return 110; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'c' && tolower(word[3]) == 'k') return 111; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g') return 7; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p') return 37; + if (len == 7 && tolower(word[1]) == 'i' && tolower(word[2]) == 'b' && tolower(word[3]) == 'r' && tolower(word[4]) == 'a' && tolower(word[5]) == 'r' && tolower(word[6]) == 'y') return 113; + return -1; +__4D: +__6D: + if (len == 2 && tolower(word[1]) == 'e') return 61; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'd') return 174; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'v' && tolower(word[3]) == 'e') return 103; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'c' && tolower(word[4]) == 'h') return 183; + if (len == 5 && tolower(word[1]) == 'k' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'r') return 107; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'm' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r' && tolower(word[5]) == 'y') return 119; + return -1; +__4E: +__6E: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'w') return 30; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 't') return 161; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x' && tolower(word[3]) == 't') return 52; + if (len == 4 && tolower(word[1]) == 'u' && tolower(word[2]) == 'l' && tolower(word[3]) == 'l') return 70; + return -1; +__4F: +__6F: + if (len == 2 && tolower(word[1]) == 'f') return 28; + if (len == 2 && tolower(word[1]) == 'n') return 60; + if (len == 2 && tolower(word[1]) == 'r') return 160; + if (len == 4 && tolower(word[1]) == 'p' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n') return 90; + if (len == 6 && tolower(word[1]) == 'b' && tolower(word[2]) == 'j' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c' && tolower(word[5]) == 't') return 11; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 't' && tolower(word[3]) == 'p' && tolower(word[4]) == 'u' && tolower(word[5]) == 't') return 35; + if (len == 8 && tolower(word[1]) == 'p' && tolower(word[2]) == 't' && tolower(word[3]) == 'i' && tolower(word[4]) == 'o' && tolower(word[5]) == 'n' && tolower(word[6]) == 'a' && tolower(word[7]) == 'l') return 34; + return -1; +__50: +__70: + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k') return 88; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e') return 116; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't') return 85; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b' && tolower(word[3]) == 'l' && tolower(word[4]) == 'i' && tolower(word[5]) == 'c') return 18; + if (len == 7 && tolower(word[1]) == 'o' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 12; + if (len == 7 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'v' && tolower(word[4]) == 'a' && tolower(word[5]) == 't' && tolower(word[6]) == 'e') return 17; + if (len == 8 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p' && tolower(word[4]) == 'e' && tolower(word[5]) == 'r' && tolower(word[6]) == 't' && tolower(word[7]) == 'y') return 22; + if (len == 9 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'c' && tolower(word[4]) == 'e' && tolower(word[5]) == 'd' && tolower(word[6]) == 'u' && tolower(word[7]) == 'r' && tolower(word[8]) == 'e') return 31; + return -1; +__51: +__71: + if (len == 4 && tolower(word[1]) == 'u' && tolower(word[2]) == 'i' && tolower(word[3]) == 't') return 76; + return -1; +__52: +__72: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'l') return 167; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r') return 168; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'a' && tolower(word[3]) == 'd') return 87; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 'i' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 77; + if (len == 5 && tolower(word[1]) == 'm' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'r') return 108; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'a' && tolower(word[5]) == 't') return 40; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 't' && tolower(word[3]) == 'u' && tolower(word[4]) == 'r' && tolower(word[5]) == 'n') return 33; + if (len == 9 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd' && tolower(word[4]) == 'o' && tolower(word[5]) == 'm' && tolower(word[6]) == 'i' && tolower(word[7]) == 'z' && tolower(word[8]) == 'e') return 117; + return -1; +__53: +__73: + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'l') return 163; + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'r') return 165; + if (len == 3 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b') return 32; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k') return 92; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'e' && tolower(word[3]) == 'p') return 51; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p') return 75; + if (len == 4 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'p') return 69; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l') return 99; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'r' && tolower(word[4]) == 't') return 8; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'e' && tolower(word[3]) == 'e' && tolower(word[4]) == 'p') return 101; + if (len == 5 && tolower(word[1]) == 'u' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 79; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'l' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c' && tolower(word[5]) == 't') return 53; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e') return 4; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 't' && tolower(word[4]) == 'i' && tolower(word[5]) == 'c') return 19; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 9; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'u' && tolower(word[4]) == 'c' && tolower(word[5]) == 't') return 15; + return -1; +__54: +__74: + if (len == 2 && tolower(word[1]) == 'o') return 48; + if (len == 3 && tolower(word[1]) == 'r' && tolower(word[2]) == 'y') return 63; + if (len == 4 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n') return 43; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'u' && tolower(word[3]) == 'e') return 67; + return -1; +__55: +__75: + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'e') return 84; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && tolower(word[3]) == 'i' && tolower(word[4]) == 'l') return 39; + if (len == 6 && tolower(word[1]) == 'n' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'c' && tolower(word[5]) == 'k') return 112; + if (len == 6 && tolower(word[1]) == 'n' && tolower(word[2]) == 's' && tolower(word[3]) == 'a' && tolower(word[4]) == 'f' && tolower(word[5]) == 'e') return 21; + return -1; +__56: +__76: + if (len == 7 && tolower(word[1]) == 'a' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'a' && tolower(word[5]) == 'n' && tolower(word[6]) == 't') return 10; + return -1; +__57: +__77: + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'i' && tolower(word[3]) == 't') return 100; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd') return 41; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 't' && tolower(word[3]) == 'h') return 66; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'c' && tolower(word[4]) == 'h') return 109; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'i' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e') return 38; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 89; + return -1; +__58: +__78: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r') return 162; + return -1; +__59: +__79: + return -1; +__5A: +__7A: + return -1; +__5B: + return -1; +__5C: + if (len == 2 && word[1] == '=') return 189; + return -1; +__5D: + return -1; +__5E: + if (len == 2 && word[1] == '=') return 193; + return -1; +__5F: + return -1; +__60: + return -1; +__7B: + return -1; +__7C: + return -1; +__7D: + return -1; +__7E: + return -1; +} + +int RESERVED_find_subr(const char *word, int len) +{ + // No symbol longer than 11 characters in the table + + if (len > 11) + return -1; + + // Now find it + + static void *jump[] = { + &&__20, &&__21, &&__22, &&__23, &&__24, &&__25, &&__26, &&__27, + &&__28, &&__29, &&__2A, &&__2B, &&__2C, &&__2D, &&__2E, &&__2F, + &&__30, &&__31, &&__32, &&__33, &&__34, &&__35, &&__36, &&__37, + &&__38, &&__39, &&__3A, &&__3B, &&__3C, &&__3D, &&__3E, &&__3F, + &&__40, &&__41, &&__42, &&__43, &&__44, &&__45, &&__46, &&__47, + &&__48, &&__49, &&__4A, &&__4B, &&__4C, &&__4D, &&__4E, &&__4F, + &&__50, &&__51, &&__52, &&__53, &&__54, &&__55, &&__56, &&__57, + &&__58, &&__59, &&__5A, &&__5B, &&__5C, &&__5D, &&__5E, &&__5F, + &&__60, &&__61, &&__62, &&__63, &&__64, &&__65, &&__66, &&__67, + &&__68, &&__69, &&__6A, &&__6B, &&__6C, &&__6D, &&__6E, &&__6F, + &&__70, &&__71, &&__72, &&__73, &&__74, &&__75, &&__76, &&__77, + &&__78, &&__79, &&__7A, &&__7B, &&__7C, &&__7D, &&__7E, + }; + + goto *jump[*word - 32]; + +__20: + return -1; +__21: + return -1; +__22: + return -1; +__23: + return -1; +__24: + return -1; +__25: + return -1; +__26: + return -1; +__27: + return -1; +__28: + return -1; +__29: + return -1; +__2A: + return -1; +__2B: + return -1; +__2C: + return -1; +__2D: + return -1; +__2E: + if (len == 4 && tolower(word[1]) == 'u' && tolower(word[2]) == 's' && tolower(word[3]) == 'e') return 228; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p' && tolower(word[4]) == 'y') return 201; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c') return 220; + if (len == 5 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l') return 194; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'k') return 202; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'c' && tolower(word[4]) == 'k') return 185; + if (len == 5 && tolower(word[1]) == 'm' && tolower(word[2]) == 'o' && tolower(word[3]) == 'v' && tolower(word[4]) == 'e') return 200; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'n') return 173; + if (len == 5 && tolower(word[1]) == 'p' && tolower(word[2]) == 'e' && tolower(word[3]) == 'e' && tolower(word[4]) == 'k') return 181; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 'd') return 179; + if (len == 5 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'i' && tolower(word[4]) == 't') return 170; + if (len == 6 && tolower(word[1]) == 'a' && tolower(word[2]) == 'r' && tolower(word[3]) == 'r' && tolower(word[4]) == 'a' && tolower(word[5]) == 'y') return 91; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'g' && tolower(word[4]) == 'r' && tolower(word[5]) == 'p') return 205; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'm' && tolower(word[4]) == 'o' && tolower(word[5]) == 'd') return 203; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'o' && tolower(word[4]) == 'w' && tolower(word[5]) == 'n') return 204; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 's' && tolower(word[5]) == 'e') return 175; + if (len == 6 && tolower(word[1]) == 'd' && tolower(word[2]) == 'e' && tolower(word[3]) == 'b' && tolower(word[4]) == 'u' && tolower(word[5]) == 'g') return 169; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'r' && tolower(word[3]) == 'r' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r') return 168; + if (len == 6 && tolower(word[1]) == 'f' && tolower(word[2]) == 'l' && tolower(word[3]) == 'u' && tolower(word[4]) == 's' && tolower(word[5]) == 'h') return 184; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'p' && tolower(word[4]) == 'u' && tolower(word[5]) == 't') return 176; + if (len == 6 && tolower(word[1]) == 'm' && tolower(word[2]) == 'k' && tolower(word[3]) == 'd' && tolower(word[4]) == 'i' && tolower(word[5]) == 'r') return 195; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 't') return 178; + if (len == 6 && tolower(word[1]) == 'r' && tolower(word[2]) == 'm' && tolower(word[3]) == 'd' && tolower(word[4]) == 'i' && tolower(word[5]) == 'r') return 196; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'l' && tolower(word[5]) == 'l') return 221; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'e' && tolower(word[4]) == 'e' && tolower(word[5]) == 'p') return 227; + if (len == 6 && tolower(word[1]) == 'w' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 182; + if (len == 7 && tolower(word[1]) == 'u' && tolower(word[2]) == 'n' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'c' && tolower(word[6]) == 'k') return 186; + if (len == 8 && tolower(word[1]) == 'e' && tolower(word[2]) == 'r' && tolower(word[3]) == 'r' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r' && tolower(word[6]) == 't' && tolower(word[7]) == 'o') return 190; + if (len == 9 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'c' && tolower(word[4]) == 'k' && tolower(word[5]) == 'w' && tolower(word[6]) == 'a' && tolower(word[7]) == 'i' && tolower(word[8]) == 't') return 187; + if (len == 9 && tolower(word[1]) == 'm' && tolower(word[2]) == 'o' && tolower(word[3]) == 'v' && tolower(word[4]) == 'e' && tolower(word[5]) == 'k' && tolower(word[6]) == 'i' && tolower(word[7]) == 'l' && tolower(word[8]) == 'l') return 206; + if (len == 9 && tolower(word[1]) == 'o' && tolower(word[2]) == 'u' && tolower(word[3]) == 't' && tolower(word[4]) == 'p' && tolower(word[5]) == 'u' && tolower(word[6]) == 't' && tolower(word[7]) == 't' && tolower(word[8]) == 'o') return 189; + if (len == 9 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'i' && tolower(word[4]) == 't' && tolower(word[5]) == 'n' && tolower(word[6]) == 'e' && tolower(word[7]) == 'x' && tolower(word[8]) == 't') return 172; + if (len == 10 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c' && tolower(word[5]) == 'k' && tolower(word[6]) == 'e' && tolower(word[7]) == 'x' && tolower(word[8]) == 'e' && tolower(word[9]) == 'c') return 229; + if (len == 10 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'p' && tolower(word[4]) == 'u' && tolower(word[5]) == 't' && tolower(word[6]) == 'f' && tolower(word[7]) == 'r' && tolower(word[8]) == 'o' && tolower(word[9]) == 'm') return 188; + if (len == 10 && tolower(word[1]) == 'l' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'e' && tolower(word[5]) == 'i' && tolower(word[6]) == 'n' && tolower(word[7]) == 'p' && tolower(word[8]) == 'u' && tolower(word[9]) == 't') return 177; + if (len == 10 && tolower(word[1]) == 'r' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n' && tolower(word[4]) == 'd' && tolower(word[5]) == 'o' && tolower(word[6]) == 'm' && tolower(word[7]) == 'i' && tolower(word[8]) == 'z' && tolower(word[9]) == 'e') return 84; + if (len == 10 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 'd' && tolower(word[5]) == 'b' && tolower(word[6]) == 'y' && tolower(word[7]) == 't' && tolower(word[8]) == 'e' && tolower(word[9]) == 's') return 180; + if (len == 10 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'i' && tolower(word[4]) == 't' && tolower(word[5]) == 'd' && tolower(word[6]) == 'e' && tolower(word[7]) == 'l' && tolower(word[8]) == 'a' && tolower(word[9]) == 'y') return 171; + if (len == 11 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e' && tolower(word[6]) == 'c' && tolower(word[7]) == 't' && tolower(word[8]) == 'i' && tolower(word[9]) == 'o' && tolower(word[10]) == 'n') return 232; + if (len == 11 && tolower(word[1]) == 'o' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'n' && tolower(word[5]) == 'm' && tolower(word[6]) == 'e' && tolower(word[7]) == 'm' && tolower(word[8]) == 'o' && tolower(word[9]) == 'r' && tolower(word[10]) == 'y') return 174; + if (len == 11 && tolower(word[1]) == 'w' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'b' && tolower(word[7]) == 'y' && tolower(word[8]) == 't' && tolower(word[9]) == 'e' && tolower(word[10]) == 's') return 183; + return -1; +__2F: + return -1; +__30: + return -1; +__31: + return -1; +__32: + return -1; +__33: + return -1; +__34: + return -1; +__35: + return -1; +__36: + return -1; +__37: + return -1; +__38: + return -1; +__39: + return -1; +__3A: + return -1; +__3B: + return -1; +__3C: + return -1; +__3D: + return -1; +__3E: + return -1; +__3F: + return -1; +__40: + return -1; +__41: +__61: + if (len == 3 && tolower(word[1]) == 'b' && tolower(word[2]) == 's') return 45; + if (len == 3 && tolower(word[1]) == 'c' && tolower(word[2]) == 's') return 60; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'g') return 94; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'c') return 29; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'l') return 114; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'n') return 58; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'r') return 116; + if (len == 3 && tolower(word[1]) == 't' && tolower(word[2]) == 'n') return 56; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 's') return 61; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 's' && tolower(word[3]) == 'h') return 70; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n') return 59; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 68; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n') return 57; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'n' && word[3] == '2') return 93; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 72; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 's' && tolower(word[4]) == 'h') return 71; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'c') return 222; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'h') return 69; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n' && word[4] == '2') return 92; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n' && tolower(word[4]) == 'h') return 73; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'c' && tolower(word[3]) == 'e' && tolower(word[4]) == 's' && tolower(word[5]) == 's') return 212; + return -1; +__42: +__62: + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n') return 145; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'g') return 112; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'l' && tolower(word[3]) == 'r') return 109; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && word[3] == '$') return 144; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 'e' && tolower(word[3]) == 't') return 110; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 's' && tolower(word[3]) == 't') return 111; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && word[4] == '@') return 275; + if (len == 5 && tolower(word[1]) == 'y' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && word[4] == '@') return 277; + if (len == 6 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 'e' && word[4] == '6' && word[5] == '4') return 241; + if (len == 7 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 'e' && word[4] == '6' && word[5] == '4' && word[6] == '$') return 242; + if (len == 8 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e' && tolower(word[5]) == 'a' && tolower(word[6]) == 'n' && word[7] == '@') return 276; + return -1; +__43: +__63: + if (len == 3 && tolower(word[1]) == 'b' && tolower(word[2]) == 'r') return 77; + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'r') return 28; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 's') return 54; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'i' && tolower(word[3]) == 'l') return 81; + if (len == 4 && tolower(word[1]) == 'h' && tolower(word[2]) == 'r' && word[3] == '$') return 27; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 't') return 134; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'm' && tolower(word[3]) == 'p') return 38; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'v') return 39; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 's' && tolower(word[3]) == 'h') return 66; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 't' && tolower(word[3]) == 'r') return 140; + if (len == 5 && tolower(word[1]) == 'b' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 'l') return 130; + if (len == 5 && tolower(word[1]) == 'b' && tolower(word[2]) == 'y' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 132; + if (len == 5 && tolower(word[1]) == 'd' && tolower(word[2]) == 'a' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 139; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'g') return 136; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'v' && word[4] == '$') return 40; + if (len == 6 && tolower(word[1]) == 'f' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'a' && tolower(word[5]) == 't') return 138; + if (len == 6 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 's' && tolower(word[5]) == 'e') return 90; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'h' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r' && tolower(word[5]) == 't') return 133; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'g' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e') return 137; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 't' && tolower(word[3]) == 'r' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 'g') return 141; + if (len == 8 && tolower(word[1]) == 'b' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e' && tolower(word[6]) == 'a' && tolower(word[7]) == 'n') return 131; + if (len == 8 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 't' && tolower(word[4]) == 'e' && tolower(word[5]) == 'g' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 135; + if (len == 8 && tolower(word[1]) == 'p' && tolower(word[2]) == 'o' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 't' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 142; + if (len == 8 && tolower(word[1]) == 'v' && tolower(word[2]) == 'a' && tolower(word[3]) == 'r' && tolower(word[4]) == 'i' && tolower(word[5]) == 'a' && tolower(word[6]) == 'n' && tolower(word[7]) == 't') return 143; + return -1; +__44: +__64: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'y') return 157; + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'g') return 62; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'r') return 218; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e') return 163; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && word[4] == '@') return 284; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v') return 43; + if (len == 5 && tolower(word[1]) == 'f' && tolower(word[2]) == 'r' && tolower(word[3]) == 'e' && tolower(word[4]) == 'e') return 214; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v' && word[5] == '$') return 44; + if (len == 7 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'a' && tolower(word[5]) == 'd' && tolower(word[6]) == 'd') return 165; + if (len == 8 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'd' && tolower(word[5]) == 'i' && tolower(word[6]) == 'f' && tolower(word[7]) == 'f') return 166; + return -1; +__45: +__65: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'f') return 191; + if (len == 3 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p') return 51; + if (len == 4 && tolower(word[1]) == 'v' && tolower(word[2]) == 'a' && tolower(word[3]) == 'l') return 167; + if (len == 4 && tolower(word[1]) == 'v' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n') return 197; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && word[3] == '2') return 74; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && tolower(word[3]) == 'm') return 78; + if (len == 5 && tolower(word[1]) == 'x' && tolower(word[2]) == 'i' && tolower(word[3]) == 's' && tolower(word[4]) == 't') return 211; + if (len == 5 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && word[3] == '1' && word[4] == '0') return 75; + return -1; +__46: +__66: + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'x') return 47; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'a' && tolower(word[3]) == 'c') return 49; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'e') return 223; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r') return 80; + if (len == 6 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && word[5] == '@') return 283; + if (len == 6 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r' && tolower(word[3]) == 'm' && tolower(word[4]) == 'a' && tolower(word[5]) == 't') return 152; + if (len == 7 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r' && tolower(word[3]) == 'm' && tolower(word[4]) == 'a' && tolower(word[5]) == 't' && word[6] == '$') return 151; + if (len == 7 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'u' && tolower(word[5]) == 'r' && tolower(word[6]) == 'l') return 251; + if (len == 8 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'u' && tolower(word[5]) == 'r' && tolower(word[6]) == 'l' && word[7] == '$') return 252; + if (len == 10 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'b' && tolower(word[5]) == 'a' && tolower(word[6]) == 's' && tolower(word[7]) == 'e' && word[8] == '6' && word[9] == '4') return 249; + if (len == 11 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'b' && tolower(word[5]) == 'a' && tolower(word[6]) == 's' && tolower(word[7]) == 'e' && word[8] == '6' && word[9] == '4' && word[10] == '$') return 250; + return -1; +__47: +__67: + return -1; +__48: +__68: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x') return 147; + if (len == 3 && tolower(word[1]) == 'y' && tolower(word[2]) == 'p') return 95; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x' && word[3] == '$') return 146; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'u' && tolower(word[3]) == 'r') return 158; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'm' && tolower(word[3]) == 'l') return 239; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'm' && tolower(word[3]) == 'l' && word[4] == '$') return 240; + return -1; +__49: +__69: + if (len == 2 && tolower(word[1]) == 'f') return 88; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'f') return 89; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 't') return 46; + if (len == 4 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && word[3] == '@') return 279; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 's' && tolower(word[3]) == 't' && tolower(word[4]) == 'r') return 30; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'r') return 217; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'f') return 210; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'a' && tolower(word[4]) == 'n') return 209; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'd' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 125; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'x' && tolower(word[5]) == 'a') return 104; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 123; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'u' && tolower(word[4]) == 'l' && tolower(word[5]) == 'l') return 127; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'a' && tolower(word[3]) == 'l' && tolower(word[4]) == 'n' && tolower(word[5]) == 'u' && tolower(word[6]) == 'm') return 108; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'c' && tolower(word[5]) == 'i' && tolower(word[6]) == 'i') return 97; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'b' && tolower(word[3]) == 'l' && tolower(word[4]) == 'a' && tolower(word[5]) == 'n' && tolower(word[6]) == 'k') return 106; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'g' && tolower(word[5]) == 'i' && tolower(word[6]) == 't') return 103; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'f' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'a' && tolower(word[6]) == 't') return 124; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'c' && tolower(word[4]) == 'a' && tolower(word[5]) == 's' && tolower(word[6]) == 'e') return 99; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'w' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 100; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'p' && tolower(word[3]) == 'u' && tolower(word[4]) == 'n' && tolower(word[5]) == 'c' && tolower(word[6]) == 't') return 107; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 's' && tolower(word[3]) == 'p' && tolower(word[4]) == 'a' && tolower(word[5]) == 'c' && tolower(word[6]) == 'e') return 105; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'u' && tolower(word[3]) == 'c' && tolower(word[4]) == 'a' && tolower(word[5]) == 's' && tolower(word[6]) == 'e') return 101; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'u' && tolower(word[3]) == 'p' && tolower(word[4]) == 'p' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 102; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'g' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r' && word[7] == '@') return 280; + if (len == 8 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'e' && tolower(word[4]) == 't' && tolower(word[5]) == 't' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 98; + if (len == 8 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'u' && tolower(word[4]) == 'm' && tolower(word[5]) == 'b' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 126; + if (len == 9 && tolower(word[1]) == 's' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e' && tolower(word[7]) == 'a' && tolower(word[8]) == 'n') return 121; + if (len == 9 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'g' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r') return 122; + if (len == 9 && tolower(word[1]) == 's' && tolower(word[2]) == 'm' && tolower(word[3]) == 'i' && tolower(word[4]) == 's' && tolower(word[5]) == 's' && tolower(word[6]) == 'i' && tolower(word[7]) == 'n' && tolower(word[8]) == 'g') return 231; + return -1; +__4A: +__6A: + return -1; +__4B: +__6B: + return -1; +__4C: +__6C: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'n') return 6; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'f') return 192; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g') return 50; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'l') return 119; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'r') return 120; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'f' && tolower(word[3]) == 't') return 1; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g' && word[3] == '2') return 76; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g' && tolower(word[3]) == 'p') return 79; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 24; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'f' && tolower(word[3]) == 't' && word[4] == '$') return 0; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g' && word[3] == '1' && word[4] == '0') return 64; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g' && word[4] == '@') return 281; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 22; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm') return 14; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e' && word[5] == '$') return 23; + if (len == 6 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && word[5] == '$') return 21; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm' && word[5] == '$') return 13; + return -1; +__4D: +__6D: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'g') return 96; + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'x') return 87; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'd') return 3; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n') return 86; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'd' && word[3] == '$') return 2; + if (len == 5 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't') return 261; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 't' && tolower(word[4]) == 'h') return 156; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'u' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 159; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l') return 253; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'y' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 257; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'd' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 271; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && word[5] == '$') return 262; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 265; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && word[6] == '$') return 254; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'y' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && word[6] == '$') return 258; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'd' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && word[6] == '$') return 272; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'f' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'a' && tolower(word[6]) == 't') return 269; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && word[6] == '$') return 266; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'h' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r' && tolower(word[6]) == 't') return 259; + if (len == 8 && tolower(word[1]) == 'k' && tolower(word[2]) == 'f' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'a' && tolower(word[6]) == 't' && word[7] == '$') return 270; + if (len == 8 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'h' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r' && tolower(word[6]) == 't' && word[7] == '$') return 260; + if (len == 8 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && tolower(word[6]) == 'l' && tolower(word[7]) == 'e') return 267; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e' && tolower(word[7]) == 'a' && tolower(word[8]) == 'n') return 255; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'g' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r') return 263; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 'p' && tolower(word[3]) == 'o' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 't' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r') return 273; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && tolower(word[6]) == 'l' && tolower(word[7]) == 'e' && word[8] == '$') return 268; + if (len == 10 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e' && tolower(word[7]) == 'a' && tolower(word[8]) == 'n' && word[9] == '$') return 256; + if (len == 10 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'g' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r' && word[9] == '$') return 264; + if (len == 10 && tolower(word[1]) == 'k' && tolower(word[2]) == 'p' && tolower(word[3]) == 'o' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 't' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r' && word[9] == '$') return 274; + return -1; +__4E: +__6E: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w') return 154; + return -1; +__4F: +__6F: + if (len == 3 && tolower(word[1]) == 'c' && tolower(word[2]) == 't') return 26; + if (len == 3 && tolower(word[1]) == 'd' && tolower(word[2]) == 'd') return 198; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 't' && word[3] == '$') return 25; + return -1; +__50: +__70: + if (len == 2 && tolower(word[1]) == 'i') return 82; + if (len == 8 && tolower(word[1]) == 'o' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r' && word[7] == '@') return 285; + return -1; +__51: +__71: + if (len == 5 && tolower(word[1]) == 'u' && tolower(word[2]) == 'o' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 235; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 'o' && tolower(word[3]) == 't' && tolower(word[4]) == 'e' && word[5] == '$') return 236; + return -1; +__52: +__72: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'd') return 63; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd') return 85; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'l') return 117; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r') return 118; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd') return 199; + if (len == 4 && tolower(word[1]) == 'd' && tolower(word[2]) == 'i' && tolower(word[3]) == 'r') return 219; + if (len == 5 && tolower(word[1]) == 'i' && tolower(word[2]) == 'g' && tolower(word[3]) == 'h' && tolower(word[4]) == 't') return 5; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'u' && tolower(word[3]) == 'n' && tolower(word[4]) == 'd') return 83; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm') return 16; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'g' && tolower(word[3]) == 'h' && tolower(word[4]) == 't' && word[5] == '$') return 4; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 's' && tolower(word[4]) == 't' && tolower(word[5]) == 'r') return 31; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm' && word[5] == '$') return 15; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'a' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l' && tolower(word[5]) == 'o' && tolower(word[6]) == 'c') return 224; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'p' && tolower(word[3]) == 'l' && tolower(word[4]) == 'a' && tolower(word[5]) == 'c' && tolower(word[6]) == 'e') return 35; + if (len == 8 && tolower(word[1]) == 'e' && tolower(word[2]) == 'p' && tolower(word[3]) == 'l' && tolower(word[4]) == 'a' && tolower(word[5]) == 'c' && tolower(word[6]) == 'e' && word[7] == '$') return 34; + return -1; +__53: +__73: + if (len == 3 && tolower(word[1]) == 'g' && tolower(word[2]) == 'n') return 48; + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'l') return 113; + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'r') return 115; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n') return 53; + if (len == 3 && tolower(word[1]) == 'q' && tolower(word[2]) == 'r') return 52; + if (len == 3 && tolower(word[1]) == 't' && tolower(word[2]) == 'r') return 150; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n') return 37; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k') return 193; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 65; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 't') return 213; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && word[3] == '$') return 149; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && word[3] == '@') return 225; + if (len == 4 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'p') return 207; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v') return 41; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l') return 237; + if (len == 5 && tolower(word[1]) == 'p' && tolower(word[2]) == 'a' && tolower(word[3]) == 'c' && tolower(word[4]) == 'e') return 8; + if (len == 5 && tolower(word[1]) == 'p' && tolower(word[2]) == 'l' && tolower(word[3]) == 'i' && tolower(word[4]) == 't') return 36; + if (len == 5 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b' && tolower(word[3]) == 's' && tolower(word[4]) == 't') return 33; + if (len == 5 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'p' && word[4] == '$') return 208; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v' && word[5] == '$') return 42; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'c' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'd') return 160; + if (len == 6 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l' && word[5] == '$') return 238; + if (len == 6 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'r' && tolower(word[4]) == 't' && word[5] == '@') return 278; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'z' && tolower(word[3]) == 'e' && tolower(word[4]) == 'o' && tolower(word[5]) == 'f') return 129; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'a' && tolower(word[3]) == 'c' && tolower(word[4]) == 'e' && word[5] == '$') return 7; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 10; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b' && tolower(word[3]) == 's' && tolower(word[4]) == 't' && word[5] == '$') return 32; + if (len == 7 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e' && word[6] == '@') return 282; + if (len == 7 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && word[6] == '$') return 9; + if (len == 7 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && word[6] == '@') return 226; + return -1; +__54: +__74: + if (len == 2 && tolower(word[1]) == 'r') return 233; + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n') return 55; + if (len == 3 && tolower(word[1]) == 'r' && word[2] == '$') return 234; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 67; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'm' && tolower(word[3]) == 'p') return 215; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'm' && tolower(word[3]) == 'e') return 164; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'm') return 12; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'm' && tolower(word[3]) == 'p' && word[4] == '$') return 216; + if (len == 5 && tolower(word[1]) == 'i' && tolower(word[2]) == 'm' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 153; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'm' && word[4] == '$') return 11; + if (len == 6 && tolower(word[1]) == 'y' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'o' && tolower(word[5]) == 'f') return 128; + return -1; +__55: +__75: + if (len == 3 && tolower(word[1]) == 'r' && tolower(word[2]) == 'l') return 243; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'l' && word[3] == '$') return 244; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 20; + if (len == 5 && tolower(word[1]) == 'p' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 18; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e' && word[5] == '$') return 19; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && word[5] == '$') return 17; + if (len == 7 && tolower(word[1]) == 'n' && tolower(word[2]) == 'q' && tolower(word[3]) == 'u' && tolower(word[4]) == 'o' && tolower(word[5]) == 't' && tolower(word[6]) == 'e') return 245; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 'b' && tolower(word[3]) == 'a' && tolower(word[4]) == 's' && tolower(word[5]) == 'e' && word[6] == '6' && word[7] == '4') return 247; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 'q' && tolower(word[3]) == 'u' && tolower(word[4]) == 'o' && tolower(word[5]) == 't' && tolower(word[6]) == 'e' && word[7] == '$') return 246; + if (len == 9 && tolower(word[1]) == 'n' && tolower(word[2]) == 'b' && tolower(word[3]) == 'a' && tolower(word[4]) == 's' && tolower(word[5]) == 'e' && word[6] == '6' && word[7] == '4' && word[8] == '$') return 248; + return -1; +__56: +__76: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'l') return 148; + if (len == 6 && tolower(word[1]) == 'a' && tolower(word[2]) == 'r' && tolower(word[3]) == 'p' && tolower(word[4]) == 't' && tolower(word[5]) == 'r') return 230; + return -1; +__57: +__77: + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k') return 162; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k' && tolower(word[4]) == 'd' && tolower(word[5]) == 'a' && tolower(word[6]) == 'y') return 161; + return -1; +__58: +__78: + return -1; +__59: +__79: + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'a' && tolower(word[3]) == 'r') return 155; + return -1; +__5A: +__7A: + return -1; +__5B: + return -1; +__5C: + return -1; +__5D: + return -1; +__5E: + return -1; +__5F: + return -1; +__60: + return -1; +__7B: + return -1; +__7C: + return -1; +__7D: + return -1; +__7E: + return -1; +} diff --git a/main/share/gb_system.h b/main/share/gb_system.h new file mode 100644 index 00000000..3393f862 --- /dev/null +++ b/main/share/gb_system.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + gb_system.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_SYSTEM_H +#define __GB_SYSTEM_H + +int SYSTEM_get_cpu_count(void); + +#endif diff --git a/main/share/gb_system_temp.h b/main/share/gb_system_temp.h new file mode 100644 index 00000000..082050ce --- /dev/null +++ b/main/share/gb_system_temp.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + gb_system_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "config.h" +#include "gb_system.h" + +#if OS_LINUX || defined(OS_CYGWIN) + +#include + +int SYSTEM_get_cpu_count(void) +{ + return get_nprocs(); +} + +#elif OS_BSD + +#include +#include + +int SYSTEM_get_cpu_count(void) +{ + int mib[2], ncpus; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + len = sizeof(ncpus); + sysctl(mib, 2, &ncpus, &len, NULL, 0); + + return ncpus; +} + +#else + +#include + +int SYSTEM_get_cpu_count(void) +{ + fprintf(stderr, "gbx" GAMBAS_VERSION_STRING ": warning: don't know how to return cpu count\n"); + return 1; +} + +#endif diff --git a/main/share/gb_table.h b/main/share/gb_table.h new file mode 100644 index 00000000..8a0236a9 --- /dev/null +++ b/main/share/gb_table.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + gb_table.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_TABLE_H +#define __GB_TABLE_H + +#include "gb_array.h" + +#define NO_SYMBOL (-1) +//#define TABLE_USE_KEY 1 + +typedef + struct { + char *name; + int len; +#if TABLE_USE_KEY + uint key; +#endif + } + SYMBOL; + +typedef + enum { + TF_NORMAL = 0, + TF_IGNORE_CASE = 1 + } + TABLE_FLAG; + + +typedef + struct _table { + SYMBOL *symbol; + ushort *sort; + TABLE_FLAG flag; + } + TABLE; + +#ifndef __GB_TABLE_C +extern bool TABLE_new_symbol; +#endif + +void TABLE_create_static(TABLE *table, size_t size, TABLE_FLAG flag); +void TABLE_delete_static(TABLE *table); + +void TABLE_create(TABLE **result, size_t size, TABLE_FLAG flag); +void TABLE_create_inc(TABLE **result, size_t size, TABLE_FLAG flag, uint inc); +void TABLE_create_from(TABLE **result, size_t size, const char *sym_list[], TABLE_FLAG flag); +void TABLE_delete(TABLE **table); + +char TABLE_compare_ignore_case(const char *s1, int len1, const char *s2, int len2); +char TABLE_compare(const char *s1, int len1, const char *s2, int len2); +char TABLE_compare_ignore_case_len(const char *s1, int len1, const char *s2, int len2); + +#define TABLE_count(_table) (ARRAY_count((_table)->symbol)) +const char *TABLE_get_symbol_name(TABLE *table, int index); +const char *TABLE_get_symbol_name_suffix(TABLE *table, int index, const char* suffix); +const char *SYMBOL_get_name(SYMBOL *sym); + +bool TABLE_find_symbol(TABLE *table, const char *name, int len, int *index); +int TABLE_add_symbol(TABLE *table, const char *name, int len); + +#define TABLE_add_symbol_exist(_table, _name, _len, _pindex) \ +({ \ + TABLE_new_symbol = FALSE; \ + *_pindex = TABLE_add_symbol((_table), (_name), (_len)); \ + !TABLE_new_symbol; \ +}) + + +void TABLE_print(TABLE *table, bool sort); + +#define TABLE_get_symbol(table, ind) ((SYMBOL *)ARRAY_get((table)->symbol, ind)) + +SYMBOL *TABLE_get_symbol_sort(TABLE *table, int index); +int TABLE_copy_symbol_with_prefix(TABLE *table, int ind_src, char prefix); + +int SYMBOL_find(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix); +#if TABLE_USE_KEY +void SYMBOL_compute_keys(void *symbol, int n_symbol, size_t s_symbol, int flag); +#else +#define SYMBOL_compute_keys(_symbol, _nsymbol, _ssymbol, _flag) +#endif +#endif + +#define SYMBOL_compare(_sym, _name) TABLE_compare((_sym)->name, (_sym)->len, (_name), strlen(_name)) +#define SYMBOL_compare_ignore_case(_sym, _name) TABLE_compare_ignore_case((_sym)->name, (_sym)->len, (_name), strlen(_name)) diff --git a/main/share/gb_table_temp.h b/main/share/gb_table_temp.h new file mode 100644 index 00000000..bb65b578 --- /dev/null +++ b/main/share/gb_table_temp.h @@ -0,0 +1,605 @@ +/*************************************************************************** + + gb_table_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_TABLE_C + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_limit.h" + +#include "gb_table.h" + +#define SYM(table, ind) (TABLE_get_symbol(table, ind)) +#define SSYM(_symbol, _pos, _size) ((SYMBOL *)((char *)(_symbol) + (_pos) * (_size))) + +bool TABLE_new_symbol = FALSE; +static char _buffer[MAX_SYMBOL_LEN + 1]; + +#if TABLE_USE_KEY +static inline uint make_key(const char *sym, int len) +{ + static void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3 }; + const uchar *p = (uchar *)sym; + + if (len >= 4) + return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; + + goto *jump[len]; + + __LEN_0: return 0; + __LEN_1: return (p[0] << 24); + __LEN_2: return (p[0] << 24) + (p[1] << 16); + __LEN_3: return (p[0] << 24) + (p[1] << 16) + (p[2] << 8); +} + +static inline uint make_key_ignore_case(const char *sym, int len) +{ + static void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3 }; + const uchar *p = (uchar *)sym; + + if (len >= 4) + return (tolower(p[0]) << 24) + (tolower(p[1]) << 16) + (tolower(p[2]) << 8) + tolower(p[3]); + + goto *jump[len]; + + __LEN_0: return 0; + __LEN_1: return (tolower(p[0]) << 24); + __LEN_2: return (tolower(p[0]) << 24) + (tolower(p[1]) << 16); + __LEN_3: return (tolower(p[0]) << 24) + (tolower(p[1]) << 16) + (tolower(p[2]) << 8); +} +#endif + +char TABLE_compare(const char *s1, int len1, const char *s2, int len2) +{ + int i; + int len = (len1 < len2) ? len1 : len2; + register unsigned char c1; + register unsigned char c2; + + for (i = 0; i < len; i++) + { + c1 = s1[i]; + c2 = s2[i]; + + if (c1 > c2) return 1; + if (c1 < c2) return -1; + } + + if (len1 < len2) + return -1; + else if (len1 > len2) + return 1; + else + return 0; +} + +char TABLE_compare_ignore_case(const char *s1, int len1, const char *s2, int len2) +{ + unsigned int len = (len1 < len2) ? len1 : len2; + unsigned int i; + int result; + + for (i = 0; len > 0; i++) + { + result = toupper(s1[i]) - toupper(s2[i]); + if (result) + return result; // < 0 ? -1 : 1; + len--; + } + + if (len1 < len2) + return -1; + else if (len1 > len2) + return 1; + else + return 0; +} + +char TABLE_compare_ignore_case_len(const char *s1, int len1, const char *s2, int len2) +{ + int result; + + if (len1 < len2) + return -1; + else if (len1 > len2) + return 1; + + while (len1) + { + result = tolower(*s1++) - tolower(*s2++); + if (result) + return result; // < 0 ? -1 : 1; + --len1; + } + + return 0; +} + +static inline int search(void *symbol, ushort *sort, int n_symbol, size_t size, const char *name, int len) +{ + int pos, deb, fin; + SYMBOL *sym; + int l; + int result; // must be an integer (or a short) because uchar - uchar may not fit in a char! + const uchar *s1; + const uchar *s2; +#if TABLE_USE_KEY + uint key = make_key(name, len); +#endif + + pos = 0; + deb = 0; + fin = n_symbol; //ARRAY_count(table->symbol); + + for(;;) + { + if (deb >= fin) + return (-deb) - 1; + + pos = (deb + fin) >> 1; + + sym = SSYM(symbol, sort[pos], size); + + if (len < sym->len) + goto __B_LOWER; + else if (len > sym->len) + goto __B_GREATER; + +#if TABLE_USE_KEY + if (key < sym->key) goto __B_LOWER; + if (key > sym->key) goto __B_GREATER; + + l = len - 4; + + if (l > 0) + { + s1 = (uchar *)name + 4; + s2 = (uchar *)sym->name + 4; + +#else + { + l = len; + s1 = (uchar *)name; + s2 = (uchar *)sym->name; +#endif + for(;;) + { + result = *s1 - *s2; + + if (result < 0) + goto __B_LOWER; + else if (result > 0) + goto __B_GREATER; + + if (--l == 0) + break; + + s1++; + s2++; + } + } + + return pos; + + __B_LOWER: fin = pos; continue; + __B_GREATER: deb = pos + 1; continue; + } +} + + +static inline int search_ignore_case(void *symbol, ushort *sort, int n_symbol, size_t size, const char *name, int len) +{ + int pos, deb, fin; + SYMBOL *sym; + int l; + int result; // must be an integer (or a short) because uchar - uchar may not fit in a char! + const uchar *s1; + const uchar *s2; +#if TABLE_USE_KEY + uint key = make_key_ignore_case(name, len); +#endif + + pos = 0; + deb = 0; + fin = n_symbol; + + for(;;) + { + if (deb >= fin) + { + return -deb - 1; + /**index = deb; + return FALSE;*/ + } + + pos = (deb + fin) >> 1; + + sym = SSYM(symbol, sort[pos], size); + + if (len < sym->len) + goto __T_LOWER; + else if (len > sym->len) + goto __T_GREATER; + +#if TABLE_USE_KEY + if (key < sym->key) goto __T_LOWER; + if (key > sym->key) goto __T_GREATER; + + l = len - 4; + + if (l > 0) + { + s1 = (uchar *)name + 4; + s2 = (uchar *)sym->name + 4; +#else + { + l = len; + s1 = (uchar *)name; + s2 = (uchar *)sym->name; +#endif + + for(;;) + { + result = tolower(*s1) - tolower(*s2); + + if (result < 0) + goto __T_LOWER; + else if (result > 0) + goto __T_GREATER; + + if (--l == 0) + break; + + s1++; + s2++; + } + } + + return pos; + /**index = pos; + return TRUE;*/ + + __T_LOWER: fin = pos; continue; + __T_GREATER: deb = pos + 1; continue; + } +} + + +const char *TABLE_get_symbol_name(TABLE *table, int index) +{ + if (index < 0 || index >= ARRAY_count(table->symbol)) + strcpy(_buffer, "?"); + else + SYMBOL_get_name(SYM(table, index)); + + return _buffer; +} + + +const char *TABLE_get_symbol_name_suffix(TABLE *table, int index, const char* suffix) +{ + SYMBOL *sym; + + if (index < 0 || index >= ARRAY_count(table->symbol)) + return "?"; + + sym = SYM(table, index); + if ((sym->len + strlen(suffix)) > MAX_SYMBOL_LEN) + return "?"; + + SYMBOL_get_name(sym); + strcat(_buffer, suffix); + return _buffer; +} + + +void TABLE_create_static(TABLE *table, size_t size, TABLE_FLAG flag) +{ + ARRAY_create_with_size(&table->symbol, Max(size, sizeof(SYMBOL)), 64); + ARRAY_create_with_size(&table->sort, sizeof(ushort), 64); + table->flag = flag; +} + + +void TABLE_create(TABLE **result, size_t size, TABLE_FLAG flag) +{ + TABLE *table; + + ALLOC(&table, sizeof(TABLE)); + TABLE_create_static(table, size, flag); + + *result = table; +} + + +void TABLE_create_inc(TABLE **result, size_t size, TABLE_FLAG flag, uint inc) +{ + TABLE_create(result, size, flag); + ARRAY_set_inc((*result)->symbol, inc); + ARRAY_set_inc((*result)->sort, inc); +} + + +void TABLE_create_from(TABLE **result, size_t size, const char *sym_list[], TABLE_FLAG flag) +{ + TABLE *table; + + TABLE_create(&table, size, flag); + + while (*sym_list) + { + TABLE_add_symbol(table, *sym_list, strlen(*sym_list)); + sym_list++; + } + + *result = table; +} + + +void TABLE_delete_static(TABLE *table) +{ + ARRAY_delete(&table->symbol); + ARRAY_delete(&table->sort); +} + + +void TABLE_delete(TABLE **p_table) +{ + if (*p_table) + { + TABLE_delete_static(*p_table); + FREE(p_table); + } +} + + +bool TABLE_find_symbol(TABLE *table, const char *name, int len, int *index) +{ + int ind; + SYMBOL *tsym; + int count; + size_t size; + + tsym = table->symbol; + count = ARRAY_count(tsym); + size = ARRAY_size(tsym); + + if (table->flag) + ind = search_ignore_case(tsym, table->sort, count, size, name, len); + else + ind = search(tsym, table->sort, count, size, name, len); + + if (ind >= 0) + { + *index = table->sort[ind]; + return TRUE; + } + else + return FALSE; +} + +#if 0 +void TABLE_add_new_symbol_without_sort(TABLE *table, const char *name, int len, int sort, SYMBOL **symbol, int *index) +{ + SYMBOL *sym; + int count; + + len = Min(len, MAX_SYMBOL_LEN); + + count = ARRAY_count(table->symbol); + + sym = (SYMBOL *)ARRAY_add_void_size(&table->symbol); + + sym->name = (char *)name; + sym->len = len; + + *((ushort *)ARRAY_add(&table->sort)) = sort; + + if (symbol) *symbol = sym; /*&table->symbol[ind];*/ + if (index) *index = count; +} +#endif + +int TABLE_add_symbol(TABLE *table, const char *name, int len) +{ + int ind; + SYMBOL *sym; + int count; + size_t size; + + /*len = Min(len, MAX_SYMBOL_LEN);*/ + //len = Min(len, 65535); + + count = ARRAY_count(table->symbol); + size = ARRAY_size(table->symbol); + + if (table->flag) + ind = search_ignore_case(table->symbol, table->sort, count, size, name, len); + else + ind = search(table->symbol, table->sort, count, size, name, len); + + if (ind < 0) + { + ind = -ind - 1; + sym = (SYMBOL *)ARRAY_add_void_size(&table->symbol); + + sym->name = (char *)name; + sym->len = len; +#if TABLE_USE_KEY + sym->key = table->flag ? make_key_ignore_case(name, len) : make_key(name, len); +#endif + + /* + printf("TABLE_add_symbol: %.*s %d %d\n", len, name, ((CLASS_SYMBOL *)sym)->global.type, + ((CLASS_SYMBOL *)sym)->local.type); + */ + + //s1 = (SYMBOL *)((char *)table->symbol + count * size); + ARRAY_add(&table->sort); + if (count > ind) + memmove(&table->sort[ind + 1], &table->sort[ind], sizeof(ushort) * (count - ind)); + + table->sort[ind] = (ushort)count; + ind = count; + TABLE_new_symbol = TRUE; + } + else + ind = table->sort[ind]; //SYM(table, ind)->sort; /*table->symbol[ind].sort;*/ + + return ind; +} + +void TABLE_print(TABLE *table, bool sort) +{ + int i; + SYMBOL *sym; + + fprintf(stderr, "capacity %i\n", ARRAY_count(table->symbol)); + + /* + for (i = 0; i < ARRAY_count(table->symbol); i++) + { + sym = SYM(table, i); + printf("%*s (%li) ", (int)sym->len, sym->name, sym->sort); + } + + printf("\n"); + */ + + for (i = 0; i < ARRAY_count(table->symbol); i++) + { + if (sort) + { + sym = SYM(table, table->sort[i]); + fprintf(stderr, "%.*s ", (int)sym->len, sym->name); + } + else + { + sym = SYM(table, i); + fprintf(stderr, "%d %.*s ", (int)table->sort[i], (int)sym->len, sym->name); + } + + //if ((i > 0) && (!(i & 0xF))) + // fprintf(stderr, "\n"); + } + + fprintf(stderr, "\n\n"); +} + + +int TABLE_copy_symbol_with_prefix(TABLE *table, int ind_src, char prefix) +{ + SYMBOL *sym; + char *ptr; + + sym = TABLE_get_symbol(table, ind_src); + + ptr = (char *)sym->name - 1; + + if (!isspace((unsigned char)*ptr)) + ERROR_panic("Cannot add prefix to symbol"); + + *ptr = prefix; + + return TABLE_add_symbol(table, ptr, sym->len + 1); +} + + +SYMBOL *TABLE_get_symbol_sort(TABLE *table, int index) +{ + return TABLE_get_symbol(table, table->sort[index]); +} + +// Symbol tables with no TABLE structure + +int SYMBOL_find(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix) +{ + int index; + int len_prefix; + + if (prefix != NULL) + { + len_prefix = strlen(prefix); + + if ((len + len_prefix) > MAX_SYMBOL_LEN) + ERROR_panic("SYMBOL_find: prefixed symbol too long"); + + strcpy(_buffer, prefix); + strcpy(&_buffer[len_prefix], name); + len += len_prefix; + name = _buffer; + } + + if (flag) + index = search_ignore_case(symbol, sort, n_symbol, s_symbol, name, len); + else + index = search(symbol, sort, n_symbol, s_symbol, name, len); + + if (index >= 0) + return sort[index]; + else + return NO_SYMBOL; +} + + +const char *SYMBOL_get_name(SYMBOL *sym) +{ + int len; + + len = Min(MAX_SYMBOL_LEN, sym->len); + memcpy(_buffer, sym->name, len); + _buffer[len] = 0; + + return _buffer; +} + +#if TABLE_USE_KEY +void SYMBOL_compute_keys(void *symbol, int n_symbol, size_t s_symbol, int flag) +{ + int i; + SYMBOL *sym; + + if (flag) + { + for (i = 0; i < n_symbol; i++) + { + sym = SSYM(symbol, i, s_symbol); + sym->key = make_key_ignore_case(sym->name, sym->len); + } + } + else + { + for (i = 0; i < n_symbol; i++) + { + sym = SSYM(symbol, i, s_symbol); + sym->key = make_key(sym->name, sym->len); + } + } +} +#endif diff --git a/main/share/gb_type_common.h b/main/share/gb_type_common.h new file mode 100644 index 00000000..7a0df679 --- /dev/null +++ b/main/share/gb_type_common.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + gb_type_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_TYPE_COMMON_H +#define __GB_TYPE_COMMON_H + +enum { + TK_UNKNOWN = 0, /* External symbol */ + TK_VARIABLE = 1, /* Variable */ + TK_FUNCTION = 2, /* Function */ + TK_PROPERTY = 3, /* Property */ + TK_LABEL = 4, /* GOTO label */ + TK_EVENT = 5, /* Event */ + TK_EXTERN = 6, /* Shared library function */ + TK_CONST = 7, /* Constant */ + + TF_OPTIONAL = 16, + + TF_STATIC = 32, + TF_PUBLIC = 64, + }; + +#ifdef PROJECT_COMP + +enum { + T_VOID = 0, + T_BOOLEAN = 1, + T_BYTE = 2, + T_SHORT = 3, + T_INTEGER = 4, + T_LONG = 5, + T_SINGLE = 6, + T_FLOAT = 7, + T_DATE = 8, + T_STRING = 9, + T_CSTRING = 10, + T_POINTER = 11, + T_VARIANT = 12, + T_ARRAY = 13, + T_STRUCT = 14, + //T_NULL = 15, + T_OBJECT = 16 + }; + +#else + +enum { + T_VOID = 0, + T_BOOLEAN = 1, + T_BYTE = 2, + T_SHORT = 3, + T_INTEGER = 4, + T_LONG = 5, + T_SINGLE = 6, + T_FLOAT = 7, + T_DATE = 8, + T_STRING = 9, + T_CSTRING = 10, + T_POINTER = 11, + T_VARIANT = 12, + T_FUNCTION = 13, + T_CLASS = 14, + T_NULL = 15, + T_OBJECT = 16, + + TC_ARRAY = 13, + TC_STRUCT = 14 + }; + +#endif + +#endif diff --git a/main/share/gbc_read_common.h b/main/share/gbc_read_common.h new file mode 100644 index 00000000..ac91537d --- /dev/null +++ b/main/share/gbc_read_common.h @@ -0,0 +1,108 @@ +/*************************************************************************** + + gbc_read_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_READ_COMMON_H +#define __GBC_READ_COMMON_H + +typedef + uint PATTERN; + +enum { + RT_END = 0, + RT_NEWLINE = 1, + RT_RESERVED = 2, + RT_IDENTIFIER = 3, + RT_INTEGER = 4, + RT_NUMBER = 5, + RT_STRING = 6, + RT_TSTRING = 7, + RT_PARAM = 8, + RT_SUBR = 9, + RT_CLASS = 10, + RT_COMMENT = 11, + RT_OPERATOR = 12, + RT_SPACE = 13, + + RT_DATATYPE = 14, + RT_ERROR = 15, + RT_HELP = 16, + RT_PREPROCESSOR = 17, + RT_ESCAPE = 18, + RT_LABEL = 19, + RT_CONSTANT = 20, + + RT_OUTPUT = 0x20, + RT_POINT = 0x40, + RT_FIRST = 0x80 + }; + +enum { + RC_SECTION = 1 + }; + +#define NULL_PATTERN ((PATTERN)0L) + +#define PATTERN_make(type, index) ((PATTERN)((type) | ((index) << 8))) + +#define VOID_STRING_INDEX 0xFFFFFF + +#define PATTERN_flag(pattern) ((pattern) & 0xF0) +#define PATTERN_type(pattern) ((pattern) & 0xF) +#define PATTERN_index(pattern) ((pattern) >> 8) + +#define PATTERN_signed_index(pattern) ((int)(pattern) >> 8) + +#define PATTERN_is(pattern, res) (pattern == PATTERN_make(RT_RESERVED, res)) + +#define PATTERN_is_null(pattern) (pattern == NULL_PATTERN) + +#define PATTERN_is_end(pattern) (PATTERN_type(pattern) == RT_END) +#define PATTERN_is_reserved(pattern) (PATTERN_type(pattern) == RT_RESERVED) + +#define PATTERN_is_identifier(pattern) (PATTERN_type(pattern) == RT_IDENTIFIER) +#define PATTERN_is_class(pattern) (PATTERN_type(pattern) == RT_CLASS) +#define PATTERN_is_newline(pattern) (PATTERN_type(pattern) == RT_NEWLINE) +#define PATTERN_is_param(pattern) (PATTERN_type(pattern) == RT_PARAM) +#define PATTERN_is_subr(pattern) (PATTERN_type(pattern) == RT_SUBR) +#define PATTERN_is_integer(pattern) (PATTERN_type(pattern) == RT_INTEGER) +#define PATTERN_is_number(pattern) (PATTERN_type(pattern) == RT_NUMBER) +#define PATTERN_is_string(pattern) (PATTERN_type(pattern) == RT_STRING) +#define PATTERN_is_tstring(pattern) (PATTERN_type(pattern) == RT_TSTRING) +#define PATTERN_is_command(pattern) (PATTERN_type(pattern) == RT_COMMAND) +#define PATTERN_is_comment(pattern) (PATTERN_type(pattern) == RT_COMMENT) +#define PATTERN_is_space(pattern) (PATTERN_type(pattern) == RT_SPACE) + +#define PATTERN_is_newline_end(pattern) (PATTERN_is_newline(pattern) || PATTERN_is_end(pattern)) + +#define PATTERN_is_first(pattern) (((pattern) & RT_FIRST) != 0) +#define PATTERN_is_point(pattern) (((pattern) & RT_POINT) != 0) +#define PATTERN_is_output(pattern) (((pattern) & RT_OUTPUT) != 0) + +#define PATTERN_set_flag(pattern, flag) ((pattern) | flag) +#define PATTERN_unset_flag(pattern, flag) ((pattern) & ~flag) + +#define PATTERN_is_operand(pattern) (PATTERN_is_reserved(pattern) && RES_is_operand(PATTERN_index(pattern))) +#define PATTERN_is_type(pattern) (PATTERN_is_reserved(pattern) && RES_is_type(PATTERN_index(pattern))) +#define PATTERN_is_preprocessor(pattern) (PATTERN_is_reserved(pattern) && RES_is_preprocessor(PATTERN_index(pattern))) + +#endif diff --git a/main/share/gbc_read_temp.h b/main/share/gbc_read_temp.h new file mode 100644 index 00000000..d4c40d16 --- /dev/null +++ b/main/share/gbc_read_temp.h @@ -0,0 +1,504 @@ +/*************************************************************************** + + gbc_read_temp.h + + (c) Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_READ_TEMP_H +#define __GBC_READ_TEMP_H + +#ifdef __EVAL_READ_C + +#define SYMBOL_TABLE EVAL->table +#define PREPROCESSOR_LINE FALSE + +#else + +#define SYMBOL_TABLE comp->class->table +#define PREPROCESSOR_LINE _prep + +#endif + +static bool add_number() +{ + unsigned char car; + const char *start; + int index; + char sign; + PATTERN last_pattern; + bool has_digit; + bool is_integer = FALSE; + int len; + + start = source_ptr; + car = get_char(); + + if (car == '-' || car == '+') + { + sign = car; + car = next_char(); + + if (car == 'I' || car == 'i') + { + car = next_char(); + if (car == 'N' || car == 'n') + { + car = next_char(); + if (car == 'F' || car == 'f') + { + car = next_char(); + add_pattern(RT_RESERVED, RESERVED_find_word(start, 4)); + return FALSE; + } + } + + goto NOT_A_NUMBER; + } + } + else + sign = 0; + + if (isdigit(car)) + goto READ_NUMBER; + else if (car == '&') + { + car = toupper(next_char()); + + if (car == 'H') + goto READ_HEXA; + else if (car == 'X') + goto READ_BINARY; + else if (car == 'O') + goto READ_OCTAL; + else + { + source_ptr--; + goto READ_HEXA; + } + } + else if (car == '%') + goto READ_BINARY; + else + goto NOT_A_NUMBER; + +READ_BINARY: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (car != '0' && car != '1') + break; + has_digit = TRUE; + } + + goto END_BINARY_HEXA; + +READ_OCTAL: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (car < '0' || car > '7') + break; + has_digit = TRUE; + } + + goto END_BINARY_HEXA; + +READ_HEXA: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (!isxdigit(car)) + break; + has_digit = TRUE; + } + +END_BINARY_HEXA: + + if (!has_digit) + goto NOT_A_NUMBER; + + if (car == '&') + car = next_char(); + else if (first_car[car] == GOTO_IDENT) + goto NOT_A_NUMBER; + + goto END; + +READ_NUMBER: + + while (isdigit(car)) + car = next_char(); + + is_integer = TRUE; + + if (car == '.') + { + is_integer = FALSE; + do + { + car = next_char(); + } + while (isdigit(car)); + } + + if (car == 'E' || car == 'e') + { + is_integer = FALSE; + car = next_char(); + if (car == '+' || car == '-') + car = next_char(); + + while (isdigit(car)) + car = next_char(); + } + else if (car == 'I' || car == 'i') + { + is_integer = FALSE; + car = next_char(); + } + + goto END; + +END: + + last_pattern = get_last_pattern(); + + if (sign && !PATTERN_is_null(last_pattern) && (!PATTERN_is_reserved(last_pattern) || PATTERN_is(last_pattern, RS_RBRA) || PATTERN_is(last_pattern, RS_RSQR))) + { + add_pattern(RT_RESERVED, RESERVED_find_word(&sign, 1)); + start++; + } + + len = source_ptr - start; + + if (is_integer && len <= 6) + { + char buffer[8]; + memcpy(buffer, start, len); + buffer[len] = 0; + add_pattern(RT_INTEGER, (atoi(buffer) & 0xFFFFFF)); + } + else + { + index = TABLE_add_symbol(SYMBOL_TABLE, start, len); + add_pattern(RT_NUMBER, index); + } + + return FALSE; + +NOT_A_NUMBER: + + source_ptr = start; + return TRUE; +} + + +static void add_identifier() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + int flag; + PATTERN last_pattern, last_last_pattern; + bool not_first; + bool can_be_reserved; + bool last_next_ident, last_type, last_class, last_pub, last_point, last_excl; +#ifdef __EVAL_READ_C + bool exist = TRUE; +#endif + + last_pattern = get_last_pattern(); + + if (PATTERN_is_reserved(last_pattern)) + { + flag = RES_get_ident_flag(PATTERN_index(last_pattern)); + if (flag & RSF_PREV) + { + last_last_pattern = get_last_last_pattern(); + if (PATTERN_is_reserved(last_last_pattern)) + flag = RES_get_ident_flag(PATTERN_index(last_last_pattern)); + else + flag = 0; + } + } + else + flag = 0; + + type = RT_IDENTIFIER; + + last_class = (flag & RSF_CLASS) != 0; + last_type = (flag & RSF_AS) != 0; + last_point = PATTERN_is(last_pattern, RS_PT); + last_excl = PATTERN_is(last_pattern, RS_EXCL); + + start = source_ptr; + + for(;;) + { + source_ptr++; + if (!ident_car[get_char()]) + break; + } + + if (!last_point && !last_excl && get_char() == ':') + { + for(;;) + { + source_ptr++; + if (!ident_car[get_char()]) + break; + } + } + + if (get_char_offset(-1) == ':') + source_ptr--; + + len = source_ptr - start; + + if (last_type) + { + source_ptr--; + + for(;;) + { + source_ptr++; + len++; + car = get_char(); + if (car == '[') + { + car = get_char_offset(1); + if (car == ']') + { + source_ptr++; + len++; + index = TABLE_add_symbol(SYMBOL_TABLE, start, len - 2); + continue; + } + } + + len--; + break; + } + } + + not_first = (flag & RSF_POINT) != 0; + + car = get_char(); + + //can_be_reserved = !not_first && TABLE_find_symbol(COMP_res_table, &comp->source[start], len, NULL, &index); + + can_be_reserved = !not_first && !last_class; + + if (can_be_reserved) + { + index = RESERVED_find_word(start, len); + can_be_reserved = (index >= 0); + } + + if (can_be_reserved && !PREPROCESSOR_LINE) + { + static void *jump[] = { + &&__OTHERS, &&__ME_NEW_LAST_SUPER, &&__CLASS, &&__STRUCT, &&__SUB_PROCEDURE_FUNCTION, &&__CONST_EXTERN_ENUM, &&__READ, + &&__DATATYPE, &&__OPTIONAL, &&__BYREF, &&__ERROR + }; + + last_next_ident = (flag & RSF_IDENT) != 0; + last_pub = (flag & RSF_PUB) != 0; + + //fprintf(stderr, "read_switch = %d\n", RES_get_read_switch(index)); + goto *jump[RES_get_read_switch(index)]; + + do + { + __ME_NEW_LAST_SUPER: + can_be_reserved = !last_next_ident; + break; + + __CLASS: + can_be_reserved = canres_car[car] && (_begin_line || PATTERN_is(last_pattern, RS_END)); + break; + + __STRUCT: + can_be_reserved = canres_car[car] && (_begin_line || last_pub || PATTERN_is(last_pattern, RS_AS) || PATTERN_is(last_pattern, RS_END) || PATTERN_is(last_pattern, RS_NEW)); + break; + + __SUB_PROCEDURE_FUNCTION: + can_be_reserved = canres_car[car] && (_begin_line || last_pub || PATTERN_is(last_pattern, RS_END)); + break; + + __CONST_EXTERN_ENUM: + can_be_reserved = canres_car[car] && (_begin_line || last_pub); + break; + + __READ: + can_be_reserved = canres_car[car] && (!last_next_ident || PATTERN_is(last_pattern, RS_PROPERTY)); + break; + + __DATATYPE: + if (car == '[' && get_char_offset(1) == ']') + { + len += 2; + source_ptr += 2; + can_be_reserved = FALSE; + } + else + { + if (last_type || PATTERN_is(last_pattern, RS_OPEN)) + can_be_reserved = TRUE; + else + can_be_reserved = FALSE; + } + break; + + __OPTIONAL: + if (!(PATTERN_is(last_pattern, RS_LBRA) || PATTERN_is(last_pattern, RS_COMMA) || PATTERN_is(last_pattern, RS_EXPORT))) + can_be_reserved = FALSE; + break; + + __BYREF: + if (!(PATTERN_is(last_pattern, RS_LBRA) || PATTERN_is(last_pattern, RS_COMMA) || PATTERN_is(last_pattern, RS_OPTIONAL))) + can_be_reserved = FALSE; + break; + + __ERROR: + can_be_reserved = canres_car[car] && !last_next_ident; + break; + + __OTHERS: + if (!canres_car[car]) + can_be_reserved = FALSE; + else if (last_type || last_next_ident || (PATTERN_is(last_pattern, RS_LBRA) && car == ')' && PATTERN_is_reserved(get_last_last_pattern()))) + can_be_reserved = FALSE; + else if (PATTERN_is(last_pattern, RS_EQUAL)) + { + can_be_reserved = (index >= RS_COLON || index == RS_NEW || index == RS_OPEN || index == RS_CLOSE || index == RS_SHELL || index == RS_EXEC || index == RS_RAISE || index == RS_PIPE || index == RS_LOCK || index == RS_MEMORY || index == RS_READ || index == RS_PEEK); + } + else if (!_begin_line && !last_type && index < RS_P_IF && PATTERN_is_reserved(last_pattern) && !RES_is_identifier(PATTERN_index(last_pattern)) && !PATTERN_is(last_pattern, RS_RBRA) && !PATTERN_is(last_pattern, RS_RSQR)) + can_be_reserved = FALSE; + break; + } + while (0); + } + + if (can_be_reserved) + { + type = RT_RESERVED; + goto __ADD_PATTERN; + } + + if ((flag == 0) && car != '.' && car != '!') + { + index = RESERVED_find_subr(start, len); + if (index >= 0) + { + if (COMP_subr_info[index].min_param == 0 || car == '(') + { + type = RT_SUBR; + +#ifdef __EVAL_READ_C + if (EVAL->custom) + { + GB_FUNCTION func; + GB_VALUE *ret; + + if (!GB.GetFunction(&func, (void *)GB.GetClass(EVAL->parent), "IsSubr", NULL, NULL)) + { + GB.Push(1, GB_T_STRING, start, len); + ret = GB.Call(&func, 1, FALSE); + if (!ret->_boolean.value) + { + exist = TABLE_add_symbol_exist(EVAL->table, start, len, &index); + type = RT_IDENTIFIER; + } + } + } +#endif + goto __ADD_PATTERN; + } + } + } + + if (last_type) + type = RT_CLASS; + +#ifdef __EVAL_READ_C + + if (!EVAL->analyze && PATTERN_is(last_pattern, RS_EXCL)) + { + index = TABLE_add_symbol(EVAL->string, start, len); + type = RT_STRING; + } + else + { + exist = TABLE_add_symbol_exist(EVAL->table, start, len, &index); + } + +#else + + if (flag & RSF_EVENT) + { + start--; + len++; + *((char *)start) = ':'; + } + + if (last_excl) + { + index = TABLE_add_symbol(comp->class->string, start, len); + type = RT_STRING; + } + else + index = TABLE_add_symbol(comp->class->table, start, len); + +#endif + +__ADD_PATTERN: + + add_pattern(type, index); + +#ifdef __EVAL_READ_C + + if (EVAL->custom && !exist) + { + GB_FUNCTION func; + GB_VALUE *ret; + + if (!GB.GetFunction(&func, (void *)GB.GetClass(EVAL->parent), "IsIdentifier", NULL, NULL)) + { + GB.Push(1, GB_T_STRING, start, len); + ret = GB.Call(&func, 1, FALSE); + if (!ret->_boolean.value) + THROW("Unknown symbol"); + } + } + +#endif +} + + +#endif diff --git a/main/share/gbc_trans_common.h b/main/share/gbc_trans_common.h new file mode 100644 index 00000000..e6aae087 --- /dev/null +++ b/main/share/gbc_trans_common.h @@ -0,0 +1,173 @@ +/*************************************************************************** + + gbc_trans_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_TRANS_COMMMON_H +#define __GBC_TRANS_COMMMON_H + +typedef + struct { + TYPE type; + int index; + PATTERN *optional; + short value; + unsigned ignore : 1; + unsigned byref : 1; + } + TRANS_PARAM; + +typedef + PATTERN TRANS_TREE; + +typedef + struct { + int type; + int ival; + int64_t lval; + double dval; + bool complex; + } + TRANS_NUMBER; + +typedef + struct { + TYPE type; + int ndim; + int dim[MAX_ARRAY_DIM]; + } + TRANS_ARRAY; + +typedef + struct { + int index; // index in symbol table + TYPE type; // data type + int value; // value or pattern index + TRANS_ARRAY array; // array dimensions + PATTERN *init; // initialization code + int64_t lvalue; // The value of a LONG constant + unsigned is_new : 1; // if something must be instanciated + unsigned is_integer : 1; // if the constant is an integer + unsigned is_embedded : 1; // if it is an embedded array + unsigned no_warning : 1; // The symbol name is between braces + } + TRANS_DECL; + +typedef + struct { + int index; + TYPE type; + short nparam; + unsigned vararg : 1; + unsigned no_warning : 1; // The symbol name is between braces + unsigned fast : 1; + unsigned unsafe : 1; + unsigned _reserved : 12; + TRANS_PARAM param[MAX_PARAM_FUNC]; + PATTERN *start; + int line; + uint64_t byref; + } + TRANS_FUNC; + +typedef + struct { + int index; + TYPE type; + short nparam; + short _reserved; + TRANS_PARAM param[MAX_PARAM_FUNC]; + } + TRANS_EVENT; + +typedef + struct { + int index; + TYPE type; + short nparam; + unsigned vararg : 1; + unsigned no_warning : 1; // The symbol name is between braces + unsigned _reserved : 14; + TRANS_PARAM param[MAX_PARAM_FUNC]; + int library; + int alias; + } + TRANS_EXTERN; + +typedef + struct { + int index; + TYPE type; + int line; + int comment; + int synonymous[3]; + int use; + unsigned nsynonymous : 3; + unsigned read_only : 1; + unsigned write_only : 1; + } + TRANS_PROPERTY; + +typedef + struct { + int type; + int value; + int state; + short local; + short id; + short loop_var; + short _reserved[3]; + ushort *pos; + ushort *pos_break; + ushort *pos_continue; + } + TRANS_CTRL; + +typedef + struct { + int index; + int line; + ushort pos; + short ctrl_id; + unsigned gosub : 1; + unsigned on_goto : 1; + unsigned _reserved : 30; + } + TRANS_GOTO; + +typedef + struct { + int index; + ushort pos; + short ctrl_id; + } + TRANS_LABEL; + +typedef + struct { + RESERVED_ID id; + void (*func)(); + bool no_try; + } + TRANS_STATEMENT; + +#endif + diff --git a/main/share/gbx_subr_common.h b/main/share/gbx_subr_common.h new file mode 100644 index 00000000..59f2fe5b --- /dev/null +++ b/main/share/gbx_subr_common.h @@ -0,0 +1,680 @@ +/*************************************************************************** + + gbx_subr_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifdef STATIC_SUBR +#undef STATIC_SUBR +#define STATIC_SUBR static +#endif + +#define OP_OBJECT_FLOAT (T_POINTER + 1) +#define OP_FLOAT_OBJECT (T_POINTER + 2) +#define OP_OBJECT_CONV (T_POINTER + 3) +#define OP_CONV_OBJECT (T_POINTER + 4) +#define OP_OBJECT (T_POINTER + 5) + +static int check_operators(VALUE *P1, VALUE *P2) +{ + if (TYPE_is_number(P1->type)) + { + if (OBJECT_class(P2->_object.object)->operators) + return OP_FLOAT_OBJECT; + } + else if (TYPE_is_number(P2->type)) + { + if (OBJECT_class(P1->_object.object)->operators) + return OP_OBJECT_FLOAT; + } + else + { + CLASS *class1 = OBJECT_class(P1->_object.object); + CLASS *class2 = OBJECT_class(P2->_object.object); + + if (class1->operators) + { + if (class1 == class2) + return OP_OBJECT; + + if (class2->operators) + { + if (class1->operators->strength > class2->operators->strength) + return OP_OBJECT_CONV; + else + return OP_CONV_OBJECT; + } + } + } + + return 0; +} + +static void operator_object_float(VALUE *P1, VALUE *P2, uchar op) +{ + void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P1->_object.object)->operators))[op]; + VALUE_conv_float(P2); + void *result = (*func)(P1->_object.object, P2->_float.value); + OBJECT_unref(P1->_object.object); + P1->_object.object = result; +} + +static void operator_float_object(VALUE *P1, VALUE *P2, uchar op) +{ + void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P2->_object.object)->operators))[op]; + VALUE_conv_float(P1); + void *result = (*func)(P2->_object.object, P1->_float.value); + P1->_object.class = P2->_object.class; + OBJECT_unref(P2->_object.object); + P1->_object.object = result; +} + +static void operator_object(VALUE *P1, VALUE *P2, uchar op) +{ + void *(*func)(void *, void *) = (void *(*)(void *, void *))((void **)(OBJECT_class(P2->_object.object)->operators))[op]; + void *result = (*func)(P1->_object.object, P2->_object.object); + OBJECT_unref(P1->_object.object); + OBJECT_unref(P2->_object.object); + P1->_object.object = result; +} + +static void operator_object_conv(VALUE *P1, VALUE *P2, char op) +{ + VALUE_conv(P2, (TYPE)P1->_object.class); + operator_object(P1, P2, op); +} + +static void operator_conv_object(VALUE *P1, VALUE *P2, char op) +{ + VALUE_conv(P1, (TYPE)P2->_object.class); + operator_object(P1, P2, op); +} + +#define MANAGE_VARIANT(_func) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + +#define MANAGE_VARIANT_POINTER(_func) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + + +#define MANAGE_VARIANT_POINTER_OBJECT(_func) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + \ + if (TYPE_is_object(type)) \ + { \ + type = check_operators(P1, P2); \ + if (type) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + } \ +}) + + +STATIC_SUBR void _SUBR_add(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, NULL, NULL, &&__POINTER, + &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value | P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value + P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value + P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value += P2->_integer.value; goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + P1->_long.value += P2->_long.value; goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + P1->_single.value += P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value += P2->_float.value; + //fprintf(stderr, "+: %.24g\n", P1->_float.value); + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + P1->_pointer.value += (intptr_t)P2->_pointer.value; goto __END; + +__OBJECT_FLOAT: + + operator_object_float(P1, P2, CO_ADDF); + goto __END; + +__FLOAT_OBJECT: + + operator_float_object(P1, P2, CO_ADDF); + goto __END; + +__OBJECT_CONV: + + operator_object_conv(P1, P2, CO_ADDF); + goto __END; + +__CONV_OBJECT: + + operator_conv_object(P1, P2, CO_ADDF); + goto __END; + +__OBJECT: + + operator_object(P1, P2, CO_ADDF); + goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER_OBJECT(_SUBR_add); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + SP--; +} + +STATIC_SUBR void _SUBR_sub(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value ^ P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value - P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value - P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value -= P2->_integer.value; goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + P1->_long.value -= P2->_long.value; goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + P1->_single.value -= P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value -= P2->_float.value; goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + P1->_pointer.value -= (intptr_t)P2->_pointer.value; goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER(_SUBR_sub); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + SP--; +} + +STATIC_SUBR void _SUBR_mul(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value & P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value * P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value * P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value *= P2->_integer.value; goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + P1->_long.value *= P2->_long.value; goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + P1->_single.value *= P2->_single.value; goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value *= P2->_float.value; + //fprintf(stderr, "*: %.24g\n", P1->_float.value); + goto __END; + +__VARIANT: + + MANAGE_VARIANT(_SUBR_mul); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + SP--; +} + +STATIC_SUBR void _SUBR_div(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value /= P2->_float.value; + if (isfinite(P1->_float.value)) + { + SP--; + return; + } + + THROW(E_ZERO); + +__VARIANT: + + MANAGE_VARIANT(_SUBR_div); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + +STATIC_SUBR void _SUBR_compi(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT + }; + + static void *test[] = { &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + TYPE type; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value > P2->_integer.value ? 1 : P1->_integer.value < P2->_integer.value ? -1 : 0; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value > P2->_long.value ? 1 : P1->_long.value < P2->_long.value ? -1 : 0; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2); + goto __END; + +__NULL: +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + result = STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + result = P1->_single.value > P2->_single.value ? 1 : P1->_single.value < P2->_single.value ? -1 : 0; + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value > P2->_float.value ? 1 : P1->_float.value < P2->_float.value ? -1 : 0; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value > P2->_pointer.value ? 1 : P1->_pointer.value < P2->_pointer.value ? -1 : 0; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2); + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__VARIANT: + + { + bool variant = FALSE; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + goto *test[(code >> 8) - (C_GT >> 8)]; + +__GT: + P1->_boolean.value = result > 0 ? -1 : 0; + return; + +__GE: + P1->_boolean.value = result >= 0 ? -1 : 0; + return; + +__LT: + P1->_boolean.value = result < 0 ? -1 : 0; + return; + +__LE: + P1->_boolean.value = result <= 0 ? -1 : 0; + return; +} diff --git a/main/tools/gbh3/.directory b/main/tools/gbh3/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/tools/gbh3/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/tools/gbh3/.icon.png b/main/tools/gbh3/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..271c30e14c9248854a9134a55652942d13a81153 GIT binary patch literal 3398 zcmV-M4Y~4(P)gs`BNFX5)AP^553os70i?K1W38~#qV>?Y;*O|^Fp0=5^jyuzd zlQE2^?KG3PooSM0rW3#7)@hA1wiDdeuPUyKZHyGaydMG~B+#3CCEdrl=bY^ylF*d^ zj{pT~s_)Fs+;h&|J^TIcZ-4vS-&vT#6s9nRDNN!2E#!PRzw=VhBY~)I8*M8m(ujpr z!lfxHnLpWlfBDXp;rMt%f=g%6vFMI3+qlfPH?(bXn4QK<9X)GDa- z1Ixt}Yb`71*usRX-~7(YT{m8BVwWXAV8Qe4MVBD3)K>?EgK2s5FFz$BoOcR*YfHCX zRADc<(ZIryF2N73HoYPEfps$;uRisqvirwBGZ!M@1(z-Wz}(Wyt3SyI9XhSSIpKHD#h;vpIt%`(eYYG5leA_qj+hQ~Q32%#h z_qCI;>t^TaAj>c;M6Y&de-jWG9QmyjeB3hNn~#pA1{z!3G4VjisB2OH*9+p=*Fg|i z;{}q)F=h;5k&A2i(*mW&2EMMBNf;$6Y1G&eSL&kL{nWsWb8AXe(rB#`+Kgqa)ir7Y zGFAcYsf5u=C&!z{WbzMukCvnF6KmPe4eP&3;Cm=-F2CVRfE`CnE1fh(>6D|!c1YCZ zEP+Abvwhoh*xEYg*R7_bWhdS32e|&$FJBb_R4`V6jIt@CjY?{z#|j{qFF+9Z?B4zo zQn!T6;XTAu5d&ofjCx#uz%<*)>XsuG`Sb)7e3-H_&r_Z$DR9AzwIBPyfto;Z# z6+0ZC^BJ^gjx@f`+-2*D7EZr%0*qVVwGL87v?PtFaeutDjnMA5pW*b8Mtlq2@>LY( zPb0{<9S=1F=ab#Qq%+-=KMkF~I>;5a>{UU$* zm5qGq$ItNQzy1q<_40q=&RK}giBjgkfOvfH;j5Uo-8i!-2VlnbWal?{@-0->kXzcaS z)+caN5CkAXy1R{38n^ca{Nu`7_MJ`;d@6p%OMtkQmn)klL;^H>u z-Ejv6Ky>WJm;Q4xBp4JqyYR%Bpz2sf|bWp5muZ|AeNkuOroqcy`AL zj>S?a;VgM;E$-=7mQ*dFysGxf4gJW7XU%}2ZrQkTwCNOOMP&(azFf!$I~%CZj}r@( zFql(Hi72j{SHbR{6oG{wKD&i4e*SK9ZhMfXL#?#$ZY3Rn$<4zb>_@aA-Eo4MDUXHA zSEDEB{I`BBk5x0z|I8_wP2uW?FIcysp@G}Kx%KRI_7t!njy9bpto<$#EhBT$f>=AY zcW*TOj4#|(&o2&Y`jS3@xJ1OF2uNl;0?$L&uHYXw{RPg9Imo{KF{5$yz z>JcpY9qrt@;gNH;rJsaVGvVti&;Kk^JBR9TZn|LIO`A52S!T5sk>TMD0z_%Q%UF>P ztx!r0`985o_7vFu&K4e6Gml-VFg?~`uyrp5OII@(?_{Rq_@45zb~54kM?e3xxki! zQaY`nyyYTl+k2o&)m(8zuH2zo8%yhdsl6wyt;1E$R8?O$E!F0Bb_9 zprQKfv?X#8V|jFW2@kv#rz{LVUQ^8He-Y=Tr+9hK86I1kB|w%1h|UTIeVZkKQK=DS z0v`#Kpq&u;(<^Zb3K-b&0ckgZh!CDpLq)O|Wegpe0RQMNg8B6n&RK|!hKYCs%+MYO z_HJXzb+=5a08OFaB1l=pz(cRcNx7EgW$z zD4Q6W8>k@*2q}%tEvIV!Lgw~DER{x-qT`JhnYnTe`MxE$w36mzoSgDX(yq(Nw2M|k ze?gS8+-^=aH&Hon`J@TZ??wo)h!%R?fT$BNw;;;jHpB@$u)ZbcjSb|pgC8TK7GT9D zMg`C$M{9t#kY8B9zI|~HKm7xAK?Qv$Pl4xR6s%o!7yo_y0M{8fm0O5sheAy)LnlRA z&m@^PvzncUcHxO;&fKLaeff!x>HP<2$z6r$p?;Q@7=F1oLuSxIFeE@#(bziKuT^Hu zekf}SXi1G^ORR6tI*RMBTg7{L+;__owzjz(-mwiQXE7%}-bQacMcVRpSiQE4fG z2(P`h1;3yKwPHOh*4+hXILLnVd*sdtiui2Z3$LJc$#brFh&x= z@aP3vBs+ULyLK@fjAC0`3-e8a_YNPUWc3DuP%h2QEl4Df*oh;A2M37OE(2?ag%BLA z(D>{;-od^X|C2ct6(qWjlL-PA)!lH>7GUuCYR}`mmVA(p^6%x<_hSG=#ADr}C=aix z#u>Ng%?^c)5*cxVN-^pKj3VU?M}JB;6oeplx*aiL4(!{(yjgRJ`dx`}!3Mq)@5DJAb#*8lZjO8L~)CU-0bQMrqC3SGt)Wkne zo^D#nbZ%R_fvWvY{Jgc5P+1jmv2=Ew;Kr1oYOz?ti&xQ~ z8l*@mZdtIHnKg@OIlKpL^hM_bMZnQRC4x(J(HW|j&HZHIE@%N+fshfBGW>1Cx9CsW ztj$@ll)}dMNR?G#^NTqwiv7<%#X}n(pl<03DreQuxbuD9e(yDMYnBo&sUV(T#C~fz zdi*#G5AEjm4G&D1^!9f6!H)AkGslio*uUw5bsHKQn19c=vJ|w+4D-}B%Lgc}5+g@~ zEWVOY_&gj6g}Ha#Jv{yLGdPQH!i%4wEEFb_aS;&;rj@YvwvDW;zlG-J{WR|SfD>IE zxIw_ok}|5-Y(OsJ^Yb^hx)c8Ic+}qe_}CjFN`^DwBR)U?wjQvL_GfquAeGJ(dw~sK z)SEsfFiO%nVW$@$-{E^3>*Of|V8tIydKzm3Nv3@rSj#ncLPkJ|=s+nE5t|pXgD^Y= zVm)V$dZB_)BJHbGGCdqSU4sgM_8^=a)4G;OczB{msD#G7KG*Itcv7hyXBjjI "/" Then + If sArg = "." Then + sArg = Application.Dir + Else + sArg = Application.Dir &/ sArg + Endif + Endif + If IsDir(sArg) Then + aRec = RDir(sArg, "*.{c,cc,cpp}") + For Each sRec In aRec + OneFile(sArg &/ sRec, hOut) + Next + Else + OneFile(sArg, hOut) + Endif + Next + + If $sComponent Then + Close #hOut + If Stat(sPath).Size = 0 Then + PrintMessage("Removing void data file") + Try Kill sPath + Endif + Endif + +Catch + + sMsg = Error.Text & ": " & Error.Backtrace.Join(" ") + Output To Default + PrintError(sMsg) + +End + +Private Sub AddHelp(cHelp As Collection, sClass As String, sSymbol As String, sHelp As String) + + Dim cCol As Collection + + PrintMessage("AddHelp: " & sClass & "." & sSymbol) + + cCol = cHelp[sClass] + If Not cCol Then + cCol = New Collection + cHelp[sClass] = cCol + Endif + + cCol[sSymbol] = sHelp + +End + + +Private Sub OneFile(sPath As String, hOut As File) + + Dim hFile As File + Dim sLine As String + Dim aHelp As New String[] + Dim bInsideComments As Boolean + Dim sHelp As String + Dim iPos As Integer + Dim bInsideDesc As Boolean + Dim sMacro As String + Dim iPos2 As Integer + Dim aArg As String[] + Dim sClass As String + Dim sSymbol As String + Dim cHelp As Collection + Dim sDeclareClass As String + Dim cCol As Variant + Dim aClass As String[] + Dim sImpl As String + + PrintMessage("Processing " & sPath & "...") + hFile = Open sPath For Input + + cHelp = New Collection + + For Each sLine In hFile.Lines + + sLine = Trim(sLine) + If Not sLine Then Continue + + 'PrintMessage(sLine) + + If sLine Begins "/// " Then + + aHelp.Add(Mid$(sLine, 5)) + Continue + + Else If sLine = "/**G" Or If sLine = "/**" Then + + bInsideComments = True + Continue + + Else If sLine Begins "/** " Then + + aHelp.Add(Mid$(sLine, 5)) + bInsideComments = True + Continue + + Else If bInsideComments Then + + If RTrim(sLine) Ends "*/" Then + + bInsideComments = False + Continue + + Else + + While Left(sLine) = "*" + sLine = Mid$(sLine, 2) + Wend + If Left(sLine) = " " Then sLine = Mid$(sLine, 2) + aHelp.Add(sLine) + Continue + + Endif + + Endif + + If aHelp.Count Then + + sHelp = Trim(aHelp[0]) + If Len(sHelp) >= 3 And If sHelp Begins "[" And If sHelp Ends "]" Then + sHelp = Mid$(sHelp, 2, -1) + iPos = InStr(sHelp, ".") + If iPos = 0 Then + sClass = sHelp + sSymbol = "" + Else + sClass = Left$(sHelp, iPos - 1) + sSymbol = Mid$(sHelp, iPos + 1) + Endif + + aHelp.Remove(0) + AddHelp(cHelp, sClass, sSymbol, aHelp.Join("\n")) + sHelp = "" + Else + sHelp = Trim(aHelp.Join("\n")) + Endif + aHelp.Clear + + Endif + + If sHelp Then + If sLine Begins "BEGIN_METHOD" Or If sLine Begins "BEGIN_PROPERTY" Then + + iPos = InStr(sLine, "(") + If iPos = 0 Then Continue + sLine = Mid$(sLine, iPos + 1) + iPos = InStr(sLine, ",") + If iPos = 0 Then iPos = InStr(sLine, ")") + If iPos = 0 Then Continue + sLine = Left(sLine, iPos - 1) + + AddHelp(cHelp, "@", sLine, sHelp) + sHelp = "" + Continue + + Endif + Endif + + If sLine Begins "GB_DECLARE" Then + bInsideDesc = True + Try sDeclareClass = Scan(sLine, "GB_DECLARE*(\"*\"*")[1] + If Error Then PrintError("Missing class name in GB_DECLARE macro: " & sLine) + Continue + Endif + + If bInsideDesc Then + + If sLine Begins "GB_END_DECLARE" Then + bInsideDesc = False + Continue + Endif + + + iPos = InStr(sLine, "(") + If iPos = 0 Then Continue + sMacro = Left(sLine, iPos - 1) + + If sMacro Not Begins "GB_" Then Continue + If InStr(sMacro, "_CONSTANT") = 0 And If InStr(sMacro, "_PROPERTY") = 0 And If InStr(sMacro, "_METHOD") = 0 Then Continue + + iPos2 = InStr(sLine, ")", iPos + 1) + If iPos2 = 0 Then Continue + + aArg = Split(Mid$(sLine, iPos + 1, iPos2 - iPos - 1), ",", Chr$(34)) + sSymbol = Trim(aArg[0]) + Try sImpl = Trim(aArg[2]) + + If sHelp Then + + AddHelp(cHelp, sDeclareClass, sSymbol, sHelp) + sHelp = "" + + Else + + If InStr(sMacro, "_CONSTANT") Then Continue + If InStr(sMacro, "_SELF") Then Continue + Try sHelp = cHelp["@"][sImpl] + If sHelp Then + AddHelp(cHelp, sDeclareClass, sSymbol, sHelp) + sHelp = "" + Endif + + Endif + + Endif + + Next + + Close #hFile + + ' Make class list + + aClass = New String[] + For Each cCol In cHelp + If cHelp.Key = "@" Then Continue + aClass.Add(cHelp.Key) + Next + aClass.Sort(gb.IgnoreCase) + + ' Generate help + + For Each sClass In aClass + + cCol = cHelp[sClass] + If cCol.Count = 0 Then Continue + + Print #hOut, "#"; sClass + + For Each sHelp In cCol + Print #hOut, cCol.Key + Print #hOut, "'"; Split(sHelp, "\n").Join("\n'") + Next + + Next + +End + +' Private Function Extract(hFile As File) As String[] +' +' Dim hNameFn As New RegExp, hNameInline As New RegExp +' Dim sLine As String, sInline As String +' Dim aRes As New String[] +' Dim bRecord As Boolean +' +' hNameFn.Compile("^BEGIN_.*\\(([^,)]+).*") +' hNameInline.Compile("/\\*\\*G (.+)$") +' +' For Each sLine In hFile.Lines +' If sLine Match "^[\\t ]*\\*?\\*/$" Then +' If sInline Then +' aRes.Add("G " & sInline) +' sInline = "" +' bRecord = False +' Endif +' Continue +' Endif +' hNameFn.Exec(sLine) +' If hNameFn.Offset <> -1 And If bRecord And If Not sInline Then +' aRes.Add(hNameFn[1].Text) +' bRecord = False +' Endif +' +' If bRecord Then aRes.Add(RegExp.Replace(sLine, "^[\\t ]*\\*", "'")) +' +' If sLine Match "^/\\*\\*G$" Then +' If bRecord Then aRes.Add("ERROR") +' sInline = "" +' bRecord = True +' Endif +' hNameInline.Exec(sLine) +' If hNameInline.Offset <> -1 Then +' If bRecord Then aRes.Add("ERROR") +' sInline = LTrim$(hNameInline[1].Text) +' bRecord = True +' Endif +' Next +' If bRecord Then aRes.Add("ERROR") +' Return aRes +' End +' +' Private Function Translate(aSource As String[], sPath As String) As String[] +' +' Dim sLine As String, aRes As New String[] +' +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine = "ERROR" Or If sLine Begins "'" Then +' aRes.Add(sLine) +' Continue +' Endif +' If sLine Begins "G " Then ' Syntax-2? +' aRes.Add(Right$(sLine, -2)) +' Continue +' Endif +' ' Syntax-1 +' aRes.Add(GetSyntax1(sLine, sPath)) +' Next +' Return aRes +' End +' +' Private Function GetSyntax1(sFunc As String, sPath As String) As String +' +' Dim hClassName As New RegExp, hFunction As New RegExp +' Dim hFile As File, sLine, sClass As String +' Dim aRes As New String[] +' +' hClassName.Compile("GB_DECLARE\\(\\\"([^\\\"]+).*") +' hFunction.Compile("GB_[^(]+\\(\\\"([^\"]+)\\\".*" & sFunc & "\\W") +' +' hFile = Open sPath For Input +' sClass = "ERROR" +' For Each sLine In hFile.Lines +' If Not sLine Then Continue +' hClassName.Exec(sLine) +' If hClassName.Offset <> -1 Then sClass = hClassName[1].Text +' hFunction.Exec(sLine) +' If hFunction.Offset <> -1 Then +' If Not aRes.Count Then aRes.Add(sClass) +' aRes.Add(hFunction[1].Text) +' Endif +' Next +' Close #hFile +' Return aRes.Join(" ") +' End +' +' Public Sub MakeHelp(aSource As String[], hOut As File) +' +' Dim sLine, sCls, sSym As String +' Dim cHelp As New Collection, aCurrent As New String[] +' Dim cClass As Collection, aHelp, aSyn As String[] +' Dim iInd As Integer +' +' Output To hOut +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine Begins "'" Then +' aCurrent.Add(sLine) +' Else +' With Scan(sLine, "* *") +' If .Count = 0 Then ' +' sCls = sLine +' sSym = "#" +' Else If .Count = 2 Then ' +' sCls = Trim$(.[0]) +' sSym = Trim$(.[1]) +' Endif +' If Not cHelp[sCls] Then cHelp[sCls] = New Collection +' cHelp[sCls][sSym] = aCurrent +' aCurrent = New String[] +' End With +' Endif +' Next +' +' For Each cClass In cHelp +' Print "#"; cHelp.Key +' aHelp = cClass["#"] +' If aHelp Then Print aHelp.Join("\n") +' For Each aHelp In cClass +' If cClass.Key = "#" Then Continue +' aSyn = Split(cClass.Key, " ") +' Print aSyn[0] +' If aHelp.Count Then Print aHelp.Join("\n") +' For iInd = 1 To aSyn.Max +' Print aSyn[iInd] +' Print "' A synonym for";; aSyn[0]; "." +' Next +' Next +' Next +' Output To Default +' End diff --git a/main/tools/gbh3/.src/MOldMain.module b/main/tools/gbh3/.src/MOldMain.module new file mode 100644 index 00000000..d08330f4 --- /dev/null +++ b/main/tools/gbh3/.src/MOldMain.module @@ -0,0 +1,256 @@ +' Gambas module file + +' ' Gambas module file +' +' Private $sRoot As String +' Private $sComponent As String +' Private $bVerbose As Boolean +' +' Private Sub PrintError(sErr As String) +' +' Error File.Name(Args[0]); ": error: "; sErr +' Quit 1 +' +' End +' +' Private Sub PrintMessage(sMsg As String) +' +' If Not $bVerbose Then Return +' Error sMsg +' +' End +' +' Public Sub Main() +' +' Dim iInd As Integer, aSources As New String[] +' Dim sArg, sRec As String, aRec As String[] +' Dim sPath As String +' Dim hOut As File +' Dim bOnlySources As Boolean +' Dim sMsg As String +' +' $sRoot = System.Path +' +' For iInd = 1 To Args.Max +' Select Case Args[iInd] +' Case "-h", "--help" +' Print File.Load("usage") +' Quit +' Case "-V", "--version" +' Print Application.Version +' Quit +' Case "-L", "--license" +' Print File.Load("license") +' Quit +' Case "-v", "--verbose" +' $bVerbose = True +' Case "-r", "--root" +' $sRoot = Args[iInd + 1] +' If Not $sRoot Then PrintError(Args[iInd] & " requires an argument") +' Inc iInd +' Case "-c", "--component" +' $sComponent = Args[iInd + 1] +' If Not $sComponent Then PrintError(Args[iInd] & " requires an argument") +' Inc iInd +' Case "--" +' bOnlySources = True +' Default +' If Not bOnlySources Then +' If Args[iInd] Begins "-" Then +' PrintError("unknown option: " & Args[iInd]) +' Endif +' Endif +' aSources.Add(Args[iInd]) +' End Select +' Next +' +' If aSources.Count = 0 Then aSources.Add(".") +' +' If $sComponent Then +' sPath = $sRoot &/ "share/gambas" & System.Version &/ "info" &/ $sComponent & ".help" +' PrintMessage("Output to " & sPath) +' hOut = Open sPath For Create +' Else +' hOut = File.Out +' Endif +' +' For Each sArg In aSources +' If Left(sArg) <> "/" Then +' If sArg = "." Then +' sArg = Application.Dir +' Else +' sArg = Application.Dir &/ sArg +' Endif +' Endif +' If IsDir(sArg) Then +' aRec = RDir(sArg, "*.{c,cc,cpp}") +' For Each sRec In aRec +' OneFile(sArg &/ sRec, hOut) +' Next +' Else +' OneFile(sArg, hOut) +' Endif +' Next +' +' If $sComponent Then +' Close #hOut +' If Stat(sPath).Size = 0 Then +' PrintMessage("Removing void data file") +' Try Kill sPath +' Endif +' Endif +' +' Catch +' +' sMsg = Error.Text & ": " & Error.Backtrace.Join(" ") +' Output To Default +' PrintError(sMsg) +' +' End +' +' Private Sub OneFile(sPath As String, hOut As File) +' +' Dim hFile As File +' +' PrintMessage("Processing " & sPath & "...") +' hFile = Open sPath For Input +' +' MakeHelp(Translate(Extract(hFile), sPath), hOut) +' +' Close #hFile +' +' End +' +' Private Function Extract(hFile As File) As String[] +' +' Dim hNameFn As New RegExp, hNameInline As New RegExp +' Dim sLine As String, sInline As String +' Dim aRes As New String[] +' Dim bRecord As Boolean +' +' hNameFn.Compile("^BEGIN_.*\\(([^,)]+).*") +' hNameInline.Compile("/\\*\\*G (.+)$") +' +' For Each sLine In hFile.Lines +' If sLine Match "^[\\t ]*\\*?\\*/$" Then +' If sInline Then +' aRes.Add("G " & sInline) +' sInline = "" +' bRecord = False +' Endif +' Continue +' Endif +' hNameFn.Exec(sLine) +' If hNameFn.Offset <> -1 And If bRecord And If Not sInline Then +' aRes.Add(hNameFn[1].Text) +' bRecord = False +' Endif +' +' If bRecord Then aRes.Add(RegExp.Replace(sLine, "^[\\t ]*\\*", "'")) +' +' If sLine Match "^/\\*\\*G$" Then +' If bRecord Then aRes.Add("ERROR") +' sInline = "" +' bRecord = True +' Endif +' hNameInline.Exec(sLine) +' If hNameInline.Offset <> -1 Then +' If bRecord Then aRes.Add("ERROR") +' sInline = LTrim$(hNameInline[1].Text) +' bRecord = True +' Endif +' Next +' If bRecord Then aRes.Add("ERROR") +' Return aRes +' End +' +' Private Function Translate(aSource As String[], sPath As String) As String[] +' +' Dim sLine As String, aRes As New String[] +' +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine = "ERROR" Or If sLine Begins "'" Then +' aRes.Add(sLine) +' Continue +' Endif +' If sLine Begins "G " Then ' Syntax-2? +' aRes.Add(Right$(sLine, -2)) +' Continue +' Endif +' ' Syntax-1 +' aRes.Add(GetSyntax1(sLine, sPath)) +' Next +' Return aRes +' End +' +' Private Function GetSyntax1(sFunc As String, sPath As String) As String +' +' Dim hClassName As New RegExp, hFunction As New RegExp +' Dim hFile As File, sLine, sClass As String +' Dim aRes As New String[] +' +' hClassName.Compile("GB_DECLARE\\(\\\"([^\\\"]+).*") +' hFunction.Compile("GB_[^(]+\\(\\\"([^\"]+)\\\".*" & sFunc & "\\W") +' +' hFile = Open sPath For Input +' sClass = "ERROR" +' For Each sLine In hFile.Lines +' If Not sLine Then Continue +' hClassName.Exec(sLine) +' If hClassName.Offset <> -1 Then sClass = hClassName[1].Text +' hFunction.Exec(sLine) +' If hFunction.Offset <> -1 Then +' If Not aRes.Count Then aRes.Add(sClass) +' aRes.Add(hFunction[1].Text) +' Endif +' Next +' Close #hFile +' Return aRes.Join(" ") +' End +' +' Public Sub MakeHelp(aSource As String[], hOut As File) +' +' Dim sLine, sCls, sSym As String +' Dim cHelp As New Collection, aCurrent As New String[] +' Dim cClass As Collection, aHelp, aSyn As String[] +' Dim iInd As Integer +' +' Output To hOut +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine Begins "'" Then +' aCurrent.Add(sLine) +' Else +' With Scan(sLine, "* *") +' If .Count = 0 Then ' +' sCls = sLine +' sSym = "#" +' Else If .Count = 2 Then ' +' sCls = Trim$(.[0]) +' sSym = Trim$(.[1]) +' Endif +' If Not cHelp[sCls] Then cHelp[sCls] = New Collection +' cHelp[sCls][sSym] = aCurrent +' aCurrent = New String[] +' End With +' Endif +' Next +' +' For Each cClass In cHelp +' Print "#"; cHelp.Key +' aHelp = cClass["#"] +' If aHelp Then Print aHelp.Join("\n") +' For Each aHelp In cClass +' If cClass.Key = "#" Then Continue +' aSyn = Split(cClass.Key, " ") +' Print aSyn[0] +' If aHelp.Count Then Print aHelp.Join("\n") +' For iInd = 1 To aSyn.Max +' Print aSyn[iInd] +' Print "' A synonym for";; aSyn[0]; "." +' Next +' Next +' Next +' Output To Default +' End diff --git a/main/tools/gbh3/README b/main/tools/gbh3/README new file mode 100644 index 00000000..7f741ac1 --- /dev/null +++ b/main/tools/gbh3/README @@ -0,0 +1,112 @@ +About gbh3 +---------- + +gbh3 is used to extract Gambas documentation from C/C++ source files and +create .help files for a component from them. It is a single Gambas project +depending only on gb.pcre. + +... and why? +------------ + +Components written in Gambas can already be documented in-code. This docu- +mentation is then stored inside their .info files. With a similar result +provided by gbh3 for C/C++ components, we can document all[*] Gambas in +its source files (at least my components will be) which makes it, IMHO, +easier to keep the documentation up-to-date. Also the help could be +displayed locally by the IDE or remotely once these .help files are imported +to the gambaswiki.org site. + +Also, documentation can be bound to a specific source code version, so that +you get docs for the version of Gambas you are running, not only for the +development branch. + +[*] I'm not sure about intrinsic functions, though. Should be feasible, + looking at gbx_class_info.c... + +Great! How do I prepare my component? +------------------------------------- + +The No. 0 rule is to BE CAREFUL! There are quite some rules to remember: + +1. Documentation syntax. + +You may write a Gambas documentation comment like + + /**G + * Here goes the documentation. + **/ + BEGIN_METHOD_VOID(ClassName_MethodName) + /* ... */ + END_METHOD + +or alternatively + + /**G Class Symbol + * Documenting Class.Symbol + **/ + +Instead of the **/ at the end, you may also write */. Cool, huh? + +The second syntax can also be used to document the class per se: + + /**G Class + * Class documentation here + **/ + +The difference between both notations is that the first must immediately +precede a BEGIN_{PROPERTY,METHOD,METHOD_VOID) line that defines the symbol +to which the documentation refers. The second syntax documents the indicated +symbol -- no matter where the comment is. + +This is intended to document GB_CONSTANTs or GB_PROPERTY_SELFs which don't +have a BEGIN_* line. It can neverthelss be used for any other symbol but +NEVER intermix both syntaxes on a single symbol. + +The ClassName_MethodName part I call "(implementation) function name". The +particular function naming convention shown is not mandatory. The scripts +take the function name and look up the corresponding symbol in your +GB_DESC[]. + +2. Regular expressions. + +The regular expressions used to filter these comments are somewhat strict. +You have to pass the spaces exactly as indicated in the line where your +comment text starts, i.e. *text. Also note that Class and +Symbol in the second syntax are case sensitive! + +OTOH, I can't guarantee that the expressions will not consume junk that you +left behind where I didn't expect junk to be. + +3. Strict structure. + +DON'T try to confuse the parsers by not giving information it needs where +it expects it. + +4. Synonyms. + +The first match for a function name lookup in a GB_DESC[] is taken as the +"primary" implementation of that symbol. That means the help comment will +be attched to that symbol. All other symbols which call the same function +are considered synonyms and are *automatically* documented with the default +sentence: "This is a synonym for ". DO NOT document them +separately using a syntax-2 help block! + +5. Don't reuse method and property implementations (except for synonyms). + +As the implementation function name is searched in the GB_DESC[]s in your +source file, you may not put the same function into different classes -- or +the parser may get confused. + +Also, keep the GB_DESC[] in the same source file as the help comment. + +Umm... I need an example +------------------------ + +gb.openssl and gb.data are partially documented that way. Digest.Hash() and +Digest._call() in gb.openssl show how to deal with synonyms. There the entry +for Hash precedes the entry for _call, thus Hash is the primary symbol and +_call a synonym. + +To see a sample .help file (on stdout), you can do at the source tree root: + + $ gbx3 app/src/gbh3 -- gb.openssl diff --git a/main/tools/gbh3/icon.png b/main/tools/gbh3/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c2abe70a5ceb8f0631c2ef5dbb873b6839a1aafa GIT binary patch literal 3211 zcmV;640Q8}P)bw11R8 z=pPkS0`V8G%KSk^OOT3!pdbZ>LX$uUanhLDty3qq*N^p%_c1#=Gk50Ra}Iyp$K076 z%bs1^3yOcz(Yg1|+Aj-jzp#s&-Q z%h=>nCNV;-sWv5*y2o2Q#dV<|kBiJd6s}03!Q&RIau4zkztJr$k~-OIan}x@61I8OvzV1b~kC;+%^H* z9tIUA$7a5cJN+P2bF;j1#baT^4;mg8rNC3rRM3=(a*bjcY{>3fz@naC>4-s znn*K6(@9g$tg8PP3EWEeCJ!1~fK03E81#ByFb`|qXUi-=Eziuhv!CazXEDzp0DovHnaB}<$`N*sV3XN69zg% zNOo11c=}*JhbOY69S6s8uq+GHG|^h4ltN00>$wB+kjv!&sJK4=b*UBrlI`Wxvd5nsU#42E0+7vS zxpy+x4m|OEt%7&2)S*}$=7l#GZVv$plb4H6km(=i@`h{!+qI#h^@@DjVkl*|{Wp34 z+2tBi%AjF}5zH5^L2PuqSdnB3gZ#q}E~0er!(z__oJ}9Vw9@#bs5QdgkA|x*M7&lK z)a!Ksno{HX0%YsI@jVh|(-PARk{cle%~XC9@c8*UIIcD{QaG5V&FRJUo(bqV2%d62 zh@Bm#T$gC25kdsRNJsFC&j0*}k}GbL>3+e#o^McgW!L9A%H>15v)Fbkn*YnWW}FGq zIT9ptgteN)NcD02y=CSnino@4)6OAC_fu_z5;rtLiA}(x^QO}L?T?!X(QG}WOCJ2t zV4VY_L!?r{)OWcq`SuTMErscBLXn2}}<8ZdL(JP$Z3D_V@ z3LD?o@xFSi$2*8f5D;VT=zU8!_|!p%>A^lM%cABfzWh>|Qzfqjk}kj#Ns*^CI5y>K z??YkF1T?jUQUVp_{B)DPjuA%*QdboK2m2~Kd2ohQD#cP&^2Ha|IJ4r%z+=$R-4#?g zdsGIl=ihn)wAT2(3WvB5TW9c#fC|v)HWdUv1%EP?%cqYHlXe^~S0tbR?kZO{WE~S~ zfE}RY>j?#bMoEQj^u9jOGXYsyL3&LL?cj$kOwI%7AY+sC6H55VY!1^jdHzI&OXYU( zopt935L^oav1JHkvq2%#`{HTO1WfoBS!mQTjCA09dUNojC60ENpmhykb~uA!7`*t7 z7biEdB_$u#-9`Zoh5=2l#_UkGX99Z81P^#8(Dfx!qI>2Sr>!!)9%X&W>@Up!hw~|v zhSN*_=HJ76ThGv4Q(~>a2837P$jsoaC*Yv}Hom)rDSWi@L8BFu1x0yi>4TENGH){HNM9c8G;q#EK z_h{u~A~9%Cua|h{(F0q7vb80#qAv5W{|axc50UNL&AO{YHxQu`0v`|}SQ6HeFN9sB z{4)2CukhIX2$#w(FP?gbMQ;W~I1`MvZVZ&LmyK91p2-B{o&zj&C8CGb2C-}ie? zhi_N`nwA?&9T0J^{7;V85AlPQ6nz8xsWc>73A9M2gCa2@3KmZ;mV@Q+sIi7&aDIgV zD8xCij$NTu3!%wc2GxxVe0YACNA?fnc^*nBEGvLxn&u6w;f4uF#IUygyHC5v_x_kXmOpgQUQE%25Bg|wiuvB=Dz z#TR~Vf4qh$gouxUn2hV$4Q-4h0m}ptemE7aX@@_`e1TH-ZU%<-QEO`IO_lKfC2=rO*7tP={z=!YkT-j*&2(?p}}+2uk)Aw zGraA2jF0XmYul{V{5CZt17Ww_;TG@~0hS5bWCs_ZP9BDmXQ2<;~Tu@z0EhhU!#~GVRUemhA*hpeVTr}e$)ZD(}G_d+n_IP z;#jbOS;l06bK`^sXVX7i+N8^^XV!(Ng~ z(eQj+&!g_T)I67l=kdg$aX$ILG-LglxYv_)XNj)g$Oy2lFGP}RP~A%C7OmhZIKv)J z8T&bD%yU`XMOox&=pge;rz{3?HZvoA9Np8;k(nXp?] + +Options: + -r --root gives the Gambas installation directory + -c --component generate help directly in a component '*.help' file + -V --version display version + -L --license display license + -h --help display this help + +If contains directories, they are searched recursively for '*.c' +and '*.cpp' files. If is not specified, the current directory +is searched. + +The extracted help is printed on the standard output, unless the '-c' +option is specified. diff --git a/reconf b/reconf new file mode 100755 index 00000000..3f4d7a53 --- /dev/null +++ b/reconf @@ -0,0 +1,7 @@ +#!/bin/sh + +rm -f config.cache +rm -f acconfig.cache + +autoreconf -v --install $@ + diff --git a/reconf-all b/reconf-all new file mode 100755 index 00000000..4eb454c7 --- /dev/null +++ b/reconf-all @@ -0,0 +1,9 @@ +#!/bin/sh + +rm -f config.cache +rm -f acconfig.cache +rm -f libtool */libtool + +libtoolize --force --copy --nonrecursive +autoreconf -v --install $@ + diff --git a/test-fast b/test-fast new file mode 100755 index 00000000..a5caebc6 --- /dev/null +++ b/test-fast @@ -0,0 +1,9 @@ +#! /bin/sh + +TESTSUITE="@Fast" + +MYDIR=$(cd `dirname $0` && pwd) + +$MYDIR/app/src/gambas3-selftest/run.sh fast + + diff --git a/version.m4 b/version.m4 new file mode 100644 index 00000000..55780c87 --- /dev/null +++ b/version.m4 @@ -0,0 +1,13 @@ +## Package version and e-mail for bugs are defined here + +m4_define([GB_VERSION], [3.18.0]) +m4_define([GB_MAIL], [g4mba5@gmail.com]) +m4_define([GB_URL], [http://gambas.sourceforge.net]) + +m4_define([GB_VERSION_MAJOR], [3]) +m4_define([GB_VERSION_MINOR], [18]) +m4_define([GB_VERSION_RELEASE], [0]) + +m4_define([GB_VERSION_FULL], [0x03180000]) +m4_define([GB_PCODE_VERSION], [0x03180000]) +m4_define([GB_PCODE_VERSION_MIN],[0x03000000]) -- 2.30.2

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 E0T7;^U;qFB literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.icon.png b/app/examples/Drawing/Painting/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f7040fd5c56fd06b126c8d685cfb8f3f3c6d0f54 GIT binary patch literal 3331 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 N002ovPDHLkV1oZ@ZEyep literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.lang/ca.po b/app/examples/Drawing/Painting/.lang/ca.po new file mode 100644 index 00000000..58153e20 --- /dev/null +++ b/app/examples/Drawing/Painting/.lang/ca.po @@ -0,0 +1,126 @@ +msgid "" +msgstr "" +"Project-Id-Version: Painting\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:34+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Anti-Aliased Drawing" +msgstr "Dibuix suavitzat" + +#: .project:2 +msgid "Anti-aliased drawing with the Paint class" +msgstr "Dibuix suavitzat amb la classe Paint" + +#: FMain.form:62 +msgid "Arc" +msgstr "-" + +#: FMain.form:70 +msgid "Arc negative" +msgstr "Arc negatiu" + +#: FMain.form:77 +msgid "Clip" +msgstr "Punt" + +#: FMain.form:84 +msgid "Clip image" +msgstr "Imatge de mostra" + +#: FMain.form:91 +msgid "Curve rectangle" +msgstr "Rectangle arrodonit" + +#: FMain.form:98 +msgid "Curve to" +msgstr "Corba a" + +#: FMain.form:105 +msgid "Dash" +msgstr "Guions" + +#: FMain.form:51 +msgid "Example" +msgstr "Exemple" + +#: FMain.form:112 +msgid "Fill and stroke" +msgstr "Línia i farciment" + +#: FMain.form:119 +msgid "Fill style" +msgstr "estil de farciment" + +#: FMain.form:126 +msgid "Gradient" +msgstr "Degradat" + +#: FMain.form:133 +msgid "Image" +msgstr "Imatge" + +#: FMain.form:140 +msgid "Image pattern" +msgstr "Patró d'imatge" + +#: FMain.form:147 +msgid "Line widths" +msgstr "Amples de línia" + +#: FMain.form:154 +msgid "Multi segment caps" +msgstr "Caps en múltiples segments" + +#: FMain.form:230 +msgid "Preview" +msgstr "Visualització prèvia" + +#: FMain.form:203 +msgid "Rotate" +msgstr "Gira" + +#: FMain.form:214 +msgid "Scale" +msgstr "Escala" + +#: FMain.form:161 +msgid "Set line cap" +msgstr "Defineix el cap de línia" + +#: FMain.form:168 +msgid "Set line join" +msgstr "Defineix la unió de línies" + +#: FMain.form:239 +msgid "Source" +msgstr "Codi font" + +#: FMain.form:175 +msgid "SVG image" +msgstr "Imatge SVG" + +#: FMain.form:182 +msgid "Text" +msgstr "-" + +#: FMain.form:244 +msgid "TextArea1" +msgstr "-" + +#: FMain.form:189 +msgid "Text clipping" +msgstr "Retallat de text" + +#: FMain.form:196 +msgid "Text extents" +msgstr "Extensions de text" + diff --git a/app/examples/Drawing/Painting/.lang/cs.po b/app/examples/Drawing/Painting/.lang/cs.po new file mode 100644 index 00000000..e74a4157 --- /dev/null +++ b/app/examples/Drawing/Painting/.lang/cs.po @@ -0,0 +1,128 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Anti-Aliased Drawing" +msgstr "Vyhlazené kreslení" + +#: .project:2 +msgid "Anti-aliased drawing with the Paint class" +msgstr "Vyhlazené kreslení s třídou Paint" + +#: FMain.form:53 +msgid "Example" +msgstr "Příklad" + +#: FMain.form:64 +msgid "Arc" +msgstr "Oblouk" + +#: FMain.form:72 +msgid "Arc negative" +msgstr "Obrácený oblouk" + +#: FMain.form:79 +msgid "Clip" +msgstr "Výřez" + +#: FMain.form:86 +msgid "Clip image" +msgstr "Výřez obrázku" + +#: FMain.form:93 +msgid "Curve rectangle" +msgstr "Zaoblený obdelník" + +#: FMain.form:100 +msgid "Curve to" +msgstr "Křivka" + +#: FMain.form:107 +msgid "Dash" +msgstr "Čerchované" + +#: FMain.form:114 +msgid "Fill and stroke" +msgstr "Výplň tahu" + +#: FMain.form:121 +msgid "Fill style" +msgstr "Styl výplně" + +#: FMain.form:128 +msgid "Gradient" +msgstr "Přechod" + +#: FMain.form:135 +msgid "Image" +msgstr "Obrázek" + +#: FMain.form:142 +msgid "Image pattern" +msgstr "Obrázkový vzor" + +#: FMain.form:149 +msgid "Line widths" +msgstr "Šířka čary" + +#: FMain.form:156 +msgid "Multi segment caps" +msgstr "Několika segmentová stopa" + +#: FMain.form:163 +msgid "Rounded rectangle" +msgstr "Zaoblený obdelník" + +#: FMain.form:170 +msgid "Set line cap" +msgstr "Nastavit stopu čáry" + +#: FMain.form:177 +msgid "Set line join" +msgstr "Nastavit spojení čáry" + +#: FMain.form:184 +msgid "SVG image" +msgstr "SVG obrázek" + +#: FMain.form:191 +msgid "Text" +msgstr "Text" + +#: FMain.form:198 +msgid "Text clipping" +msgstr "Výstřižek textu" + +#: FMain.form:205 +msgid "Text extents" +msgstr "Rozsáhlý text" + +#: FMain.form:212 +msgid "Rotate" +msgstr "Rotace" + +#: FMain.form:223 +msgid "Scale" +msgstr "Měřítko" + +#: FMain.form:243 +msgid "Preview" +msgstr "Náhled" + +#: FMain.form:250 +msgid "Source" +msgstr "Zdroj" + +#: FMain.form:255 +msgid "TextArea1" +msgstr "-" diff --git a/app/examples/Drawing/Painting/.lang/de.po b/app/examples/Drawing/Painting/.lang/de.po new file mode 100644 index 00000000..abe77c45 --- /dev/null +++ b/app/examples/Drawing/Painting/.lang/de.po @@ -0,0 +1,129 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Anti-Aliased Drawing" +msgstr "Zeichnen mit Anti-Aliasing" + +#: .project:2 +msgid "Anti-aliased drawing with the Paint class" +msgstr "antialisiertes Zeichnen mit der Paint-Klasse" + +#: FMain.form:54 +msgid "Example" +msgstr "Beispiel" + +#: FMain.form:65 +msgid "Arc" +msgstr "Kreisbogen" + +#: FMain.form:73 +msgid "Arc negative" +msgstr "Negativer Kreisbogen" + +#: FMain.form:80 +msgid "Clip" +msgstr "-" + +#: FMain.form:87 +msgid "Clip image" +msgstr "Bildausschnitt" + +#: FMain.form:94 +msgid "Curve rectangle" +msgstr "Abgerundetes Rechteck" + +#: FMain.form:101 +msgid "Curve to" +msgstr "Bogen nach" + +#: FMain.form:108 +msgid "Dash" +msgstr "Gestrichelt" + +#: FMain.form:115 +msgid "Fill and stroke" +msgstr "Gefüllt und umrandet" + +#: FMain.form:122 +msgid "Fill style" +msgstr "Füllstil" + +#: FMain.form:129 +msgid "Gradient" +msgstr "Verlauf" + +#: FMain.form:136 +msgid "Image" +msgstr "Bild" + +#: FMain.form:143 +msgid "Image pattern" +msgstr "Bildmuster" + +#: FMain.form:150 +msgid "Line widths" +msgstr "Linienbreite" + +#: FMain.form:157 +msgid "Multi segment caps" +msgstr "Mehrere Linienumrandungen" + +#: FMain.form:164 +msgid "Rounded rectangle" +msgstr "Abgerundetes Rechteck" + +#: FMain.form:171 +msgid "Set line cap" +msgstr "Linienumrandung wählen" + +#: FMain.form:178 +msgid "Set line join" +msgstr "Linienverbindung wählen" + +#: FMain.form:185 +msgid "SVG image" +msgstr "SVG-Bild" + +#: FMain.form:192 +msgid "Text" +msgstr "-" + +#: FMain.form:199 +msgid "Text clipping" +msgstr "Text beschneiden" + +#: FMain.form:206 +msgid "Text extents" +msgstr "Textbereiche" + +#: FMain.form:213 +msgid "Rotate" +msgstr "Drehen" + +#: FMain.form:224 +msgid "Scale" +msgstr "Maßstab" + +#: FMain.form:248 +msgid "Preview" +msgstr "Vorschau" + +#: FMain.form:255 +msgid "Source" +msgstr "Quellcode" + +#: FMain.form:260 +msgid "TextArea1" +msgstr "-" + diff --git a/app/examples/Drawing/Painting/.lang/ru.po b/app/examples/Drawing/Painting/.lang/ru.po new file mode 100644 index 00000000..ba0eff67 --- /dev/null +++ b/app/examples/Drawing/Painting/.lang/ru.po @@ -0,0 +1,148 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/Painting/.project:18 +msgid "Anti-Aliased Drawing" +msgstr "Сглаженное рисование" + +#: app/examples/Drawing/Painting/.project:19 +msgid "" +"Anti-aliased drawing with the Paint class.\n" +"\n" +"This example shows the different methods of the Paint class." +msgstr "" +"Сглаженный рисунок с классом рисования.\n" +"\n" +"В этом примере показаны различные методы класса рисования." + +#: app/examples/Drawing/Painting/.src/FMain.form:15 +msgid "Example" +msgstr "Пример" + +#: app/examples/Drawing/Painting/.src/FMain.form:24 +msgid "Arc" +msgstr "Дуга" + +#: app/examples/Drawing/Painting/.src/FMain.form:31 +msgid "Arc negative" +msgstr "Дуга отрицательная" + +#: app/examples/Drawing/Painting/.src/FMain.form:37 +msgid "Clip" +msgstr "Кадрировать" + +#: app/examples/Drawing/Painting/.src/FMain.form:43 +msgid "Clip image" +msgstr "Кадрировать изображение" + +#: app/examples/Drawing/Painting/.src/FMain.form:49 +msgid "Curve rectangle" +msgstr "Кривой прямоугольник" + +#: app/examples/Drawing/Painting/.src/FMain.form:55 +msgid "Curve to" +msgstr "Кривая к" + +#: app/examples/Drawing/Painting/.src/FMain.form:61 +msgid "Dash" +msgstr "Штрих-пунктирная" + +#: app/examples/Drawing/Painting/.src/FMain.form:67 +msgid "Fill and stroke" +msgstr "Заливка и обводка" + +#: app/examples/Drawing/Painting/.src/FMain.form:73 +msgid "Fill style" +msgstr "Стиль заполнения" + +#: app/examples/Drawing/Painting/.src/FMain.form:79 +msgid "Gradient" +msgstr "Градиент" + +#: app/examples/Drawing/Painting/.src/FMain.form:85 +msgid "Image" +msgstr "Изображение" + +#: app/examples/Drawing/Painting/.src/FMain.form:91 +msgid "Image pattern" +msgstr "Шаблон изображения" + +#: app/examples/Drawing/Painting/.src/FMain.form:97 +msgid "Line widths" +msgstr "Толщина линий" + +#: app/examples/Drawing/Painting/.src/FMain.form:103 +msgid "Multi segment caps" +msgstr "Концы мультисегмента" + +#: app/examples/Drawing/Painting/.src/FMain.form:109 +msgid "Rounded rectangle" +msgstr "Скруглённый прямоугольник" + +#: app/examples/Drawing/Painting/.src/FMain.form:115 +msgid "Set line cap" +msgstr "Установить заглушку" + +#: app/examples/Drawing/Painting/.src/FMain.form:121 +msgid "Set line join" +msgstr "Установить соединение строк" + +#: app/examples/Drawing/Painting/.src/FMain.form:127 +msgid "SVG image" +msgstr "Изображение SVG" + +#: app/examples/Drawing/Painting/.src/FMain.form:133 +msgid "Text" +msgstr "Текст" + +#: app/examples/Drawing/Painting/.src/FMain.form:139 +msgid "Text clipping" +msgstr "Кадрирование текста" + +#: app/examples/Drawing/Painting/.src/FMain.form:145 +msgid "Text extents" +msgstr "Текстовые экстенты" + +#: app/examples/Drawing/Painting/.src/FMain.form:151 +msgid "Rotate" +msgstr "Вращение" + +#: app/examples/Drawing/Painting/.src/FMain.form:160 +msgid "Scale" +msgstr "Масштаб" + +#: app/examples/Drawing/Painting/.src/FMain.form:177 +msgid "Preview" +msgstr "Предпросмотр" + +#: app/examples/Drawing/Painting/.src/FMain.form:183 +msgid "Source" +msgstr "Исходник" + +#: app/examples/Drawing/Painting/.src/FMain.form:187 +msgid "TextArea1" +msgstr "Текстовая_область_1" + diff --git a/app/examples/Drawing/Painting/.project b/app/examples/Drawing/Painting/.project new file mode 100644 index 00000000..500d4a6e --- /dev/null +++ b/app/examples/Drawing/Painting/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Title=Anti-Aliased Drawing +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Anti-aliased drawing with the Paint class.\n\nThis example shows the different methods of the Paint class." +Authors="Fabien Bodard\nBenoît Minisini" +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=en +Vendor=Example +Packager=1 diff --git a/app/examples/Drawing/Painting/.src/FMain.class b/app/examples/Drawing/Painting/.src/FMain.class new file mode 100644 index 00000000..1bb948ad --- /dev/null +++ b/app/examples/Drawing/Painting/.src/FMain.class @@ -0,0 +1,548 @@ +' Gambas class file + +Private Const IMAGE_NAME As String = "clovis.jpg" +Private Const SVG_EXPORT As String = "~/Gambas with red ballon.svg" + +Private $sFunctionName As String = "Example1" + +Public Sub _new() + + Editor1.Text = File.Load($sFunctionName) + +End + +Public Sub Form_Open() + + HSplit1.Layout = [1, 2] + +End + +Public Sub Example1() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(-180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End + + +Public Sub Example2() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End + +Public Sub Example3() + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + Paint.Brush = Paint.Color(Color.RGB(0, 255, 0)) + Paint.MoveTo(0, 0) + Paint.LineTo(256, 256) + Paint.MoveTo(256, 0) + Paint.LineTo(0, 256) + Paint.LineWidth = 10.0 + Paint.Stroke + +End + +Public Sub Example4() + + Dim hImg As Image + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + hImg = Image.Load(IMAGE_NAME) + Paint.Scale(256 / hImg.Width, 256 / hImg.Height) + Paint.Brush = Paint.Image(hImg, 0, 0) + Paint.Rectangle(0, 0, 512, 512) + Paint.Fill() + +End + +Public Sub Example5() + + ' A custom shape that could be wrapped in a function + Dim X0 As Float = 25.6 + Dim Y0 As Float = 25.6 + Dim RectWidth As Float = 204.8 + Dim RectHeight As Float = 204.8 + + Dim Radius As Float = 102.4 + + Dim X1, Y1 As Float + + X1 = X0 + RectWidth + Y1 = Y0 + RectHeight + + If Not (RectWidth > 0.0) Or Not (RectHeight > 0.0) Then Return + + If RectWidth / 2 < Radius Then + If RectHeight / 2 < Radius Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Else + If (RectHeight / 2 < Radius) Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Endif + + Paint.ClosePath + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.RGB(128, 0, 0, 128)) + Paint.LineWidth = 10.0 + Paint.Stroke + +End + +Public Sub Example6() + + Dim X As Float = 25.6 + Dim Y As Float = 128.0 + Dim X1 As Float = 102.4 + Dim Y1 As Float = 230.4 + Dim X2 As Float = 153.6 + Dim Y2 As Float = 25.6 + Dim X3 As Float = 230.4 + Dim Y3 As Float = 128.0 + + Paint.MoveTo(X, Y) + Paint.CurveTo(X1, Y1, X2, Y2, X3, Y3) + + Paint.LineWidth = 10 + Paint.Stroke + + Paint.Brush = Paint.Color(Color.RGB(255, 255 * 0.2, 255 * 0.2, 255 * 0.6)) + Paint.LineWidth = 6 + + Paint.MoveTo(X, Y) + Paint.LineTo(X1, Y1) + Paint.MoveTo(X2, Y2) + Paint.LineTo(X3, Y3) + Paint.Stroke + +End + +Public Sub Example7() + + Dim Dashes As Float[] = [5.0, 'ink + 1.0, 'skip + 1.0, 'ink + 1.0] 'skip + + Paint.Dash = Dashes + Paint.DashOffset = -5 + Paint.LineWidth = 10.0 + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.RelLineTo(-102.4, 0) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.Stroke + +End + +Public Sub Example8() + + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.LineTo(Paint.X - 102.4, Paint.Y) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.ClosePath + + Paint.MoveTo(64.0, 25.6) + Paint.RelLineTo(51.2, 51.2) + Paint.RelLineTo(-51.2, 51.2) + Paint.RelLineTo(-51.2, -51.2) + Paint.ClosePath + + Paint.LineWidth = 10 + Paint.Brush = Paint.Color(Color.Blue) + Paint.Fill(True) + + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke + +End + +Public Sub Example9() + + Paint.LineWidth = 6 + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(2)) + Paint.Arc(192, 64, 40, 0, Pi(-2)) + + Paint.FillRule = Paint.FillRuleEvenOdd + Paint.Brush = Paint.Color(Color.RGB(0, 179, 0)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + Paint.Translate(0, 128) + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(-2)) + Paint.Arc(192, 64, 40, 0, Pi(2)) + Paint.FillRule = Paint.FillRuleWinding + Paint.Brush = Paint.Color(Color.RGB(0, 0, 230)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + +End + +Public Sub Example10() + + Paint.Brush = Paint.LinearGradient(0, 0, 0, 256, [Color.Black, Color.White], [1.0, 0.0]) + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + + Paint.Brush = Paint.RadialGradient(102.4, 102.4, 128.0, 115.2, 102.4, [Color.Black, Color.White], [1.0, 0.1]) + Paint.Arc(128, 128, 76.8) + Paint.Fill + +End + +Public Sub Example11() + + Dim X, Y, W, H As Float + Dim hBrush As PaintBrush + Dim hImage As Image + + hImage = Image.Load(IMAGE_NAME) + + X = 16 + Y = 40 + W = 200 + H = 200 + + 'Paint.DrawImage(hImage, X, Y, W, H) + + hBrush = Paint.Image(hImage) + hBrush.Translate(X, Y) + hBrush.Scale(W / hImage.W, H / hImage.H) + Paint.Brush = hBrush + Paint.Rectangle(X, Y, W, H) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 127, 127, 153)) + Paint.Arc(X, Y, 10) + Paint.Fill + +End + +Public Sub Example12() + + Dim hImg As Image + Dim hBrush As PaintBrush + + hImg = Image.Load(IMAGE_NAME) + + Paint.Translate(128, 128) + 'Paint.Rotate(Pi / 4) + Paint.Scale(1 / Sqr(2), 1 / Sqr(2)) + Paint.Translate(-128, -128) + hBrush = Paint.Image(himg, 0, 0) + hBrush.Matrix = hBrush.Matrix.Scale(1 / (himg.w / 256.0 * 5.0), 1 / (hImg.w / 256.0 * 5.0)) + Paint.Brush = hBrush + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + +End + +Public Sub Example13() + + Paint.MoveTo(50, 75) + Paint.LineTo(200, 75) + + Paint.MoveTo(50, 125) + Paint.LineTo(200, 125) + + Paint.MoveTo(50, 175) + Paint.LineTo(200, 175) + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapRound + Paint.Stroke + +End + +Public Sub Example14() + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapButt + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapRound + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapSquare + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31)) + Paint.LineWidth = 6 + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + +End + +Public Sub Example15() + + Paint.LineWidth = 40.96 + Paint.MoveTo(76.8, 84.48) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinMiter 'Default + Paint.Stroke + + Paint.MoveTo(76.8, 161.28) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinBevel + Paint.Stroke + + Paint.MoveTo(76.8, 238.08) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinRound + Paint.Stroke + +End + +Public Sub Example16() + + Paint.Font.Name = "Sans" + Paint.Font.Size = 90 + Paint.Font.Bold = True + Paint.MoveTo(10, 135) + Paint.Text("Hello") + Paint.Fill + + Paint.MoveTo(70, 165) + Paint.text("void") + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.LineWidth = 0 '2.56 + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.Arc(10, 135, 5.12) + Paint.ClosePath + Paint.Arc(70, 165, 5.12) + Paint.Fill + +End + +Public Sub Example17() + + Dim sText As String = "Gambas
already means
Basic!" + + Dim X, Y As Float + Dim hExt As PaintExtents + + Paint.Font = Font["Sans,40"] + X = 50 + Y = 100 + + Paint.MoveTo(X, Y) + + hExt = Paint.RichTextExtents(sText) + + Paint.Brush = Paint.RadialGradient(50, 100, 300, 50, 100, [Color.Yellow, Color.Cyan], [1.0, 0.0]) + Paint.RichText(sText) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.LineWidth = 1 + Paint.Arc(X, Y, 10) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 224)) + Paint.Rectangle(hExt.X, hExt.Y, hExt.Width, hExt.Height) + Paint.Fill + +End + +Public Sub Example18() + + Paint.Save + Paint.Rectangle(10, 10, 200, 100) + Paint.Stroke(True) + Paint.Clip + Paint.Font.Size = 18 + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 10, 30, 30) + Paint.Fill + Paint.Restore + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 40, 30, 30) + Paint.Fill + +End + + +Public Sub Example20() + + Dim eWidth As Float + Dim Y As Float + + Y = 20.5 + For eWidth = 0 To 5 Step 0.25 + Paint.LineWidth = eWidth + Paint.MoveTo(50, Y) + Paint.RelLineTo(200, 0) + Paint.Stroke + Paint.Text(eWidth, 0, Y - 10, 40, 20, Align.Right) + Paint.Fill + Y += 20 + Next + +End + +Private $hSvgImage As SvgImage + +Public Sub Example21() + + If Not $hSvgImage Then + $hSvgImage = SvgImage.Load("gambas.svg") + Paint.Begin($hSvgImage) + Paint.Brush = Paint.RadialGradient(200, 140, 40, 215, 115, [Color.RGB(255, 0, 0, 64), Color.White], [1.0, 0.1]) + Paint.Arc(200, 140, 40) + Paint.Fill + Paint.End + Endif + + $hSvgImage.Paint + +End + +Public Sub Example22() + + Paint.Background = Color.RGB(128, 128, 255) + Paint.Rectangle(32, 32, 192, 192, 32) + Paint.Fill(True) + Paint.Background = Color.RGB(128, 0, 0, 128) + Paint.LineWidth = 10 + Paint.Stroke + 'Paint.Background = Color.Black + 'Paint.Arc(128, 128, 96) + 'Paint.Stroke + +End + +Public Sub optExample_Click() + + $sFunctionName = "Example" & Last.tag + Try Editor1.text = File.Load($sFunctionName) + If Error Then Editor1.Text = Error.Text + DrawingArea1.Refresh + +End + +Public Sub DrawingArea1_Draw() + + Paint.Reset + Paint.Scale(sldScale.Value / 100, sldScale.Value / 100) + Paint.Translate(128, 128) + Paint.Rotate(Rad(sldRotate.Value)) + Paint.Translate(-128, -128) + Object.Call(Me, $sFunctionName) + +End + +Public Sub sldRotate_Change() + + DrawingArea1.Refresh + +End + +Public Sub sldScale_Change() + + DrawingArea1.Refresh + +End + diff --git a/app/examples/Drawing/Painting/.src/FMain.form b/app/examples/Drawing/Painting/.src/FMain.form new file mode 100644 index 00000000..2fbf98c6 --- /dev/null +++ b/app/examples/Drawing/Painting/.src/FMain.form @@ -0,0 +1,195 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,118,89) + Arrangement = Arrange.Fill + { HSplit1 HSplit + MoveScaled(1,1,104,87) + { VBox1 VBox + MoveScaled(1,2,41,85) + Spacing = True + Margin = True + { Label3 Label + MoveScaled(1,1,19,3) + Font = Font["Bold"] + Text = ("Example") + } + { VBox2 VBox + MoveScaled(2,4,38,64) + AutoResize = True + { Exemple1 RadioButton optExample + Name = "Exemple1" + MoveScaled(0,0,38,3) + Tag = "1" + Text = ("Arc") + Value = True + } + { Exemple2 RadioButton optExample + Name = "Exemple2" + MoveScaled(0,3,38,3) + Tag = "2" + Text = ("Arc negative") + } + { Exemple3 RadioButton optExample + Name = "Exemple3" + MoveScaled(0,6,38,3) + Tag = "3" + Text = ("Clip") + } + { Exemple4 RadioButton optExample + Name = "Exemple4" + MoveScaled(0,9,38,3) + Tag = "4" + Text = ("Clip image") + } + { Exemple5 RadioButton optExample + Name = "Exemple5" + MoveScaled(0,12,38,3) + Tag = "5" + Text = ("Curve rectangle") + } + { Exemple6 RadioButton optExample + Name = "Exemple6" + MoveScaled(0,15,38,3) + Tag = "6" + Text = ("Curve to") + } + { Exemple7 RadioButton optExample + Name = "Exemple7" + MoveScaled(0,18,38,3) + Tag = "7" + Text = ("Dash") + } + { Exemple8 RadioButton optExample + Name = "Exemple8" + MoveScaled(0,21,38,3) + Tag = "8" + Text = ("Fill and stroke") + } + { Exemple9 RadioButton optExample + Name = "Exemple9" + MoveScaled(0,24,38,3) + Tag = "9" + Text = ("Fill style") + } + { Exemple10 RadioButton optExample + Name = "Exemple10" + MoveScaled(0,27,38,3) + Tag = "10" + Text = ("Gradient") + } + { Exemple11 RadioButton optExample + Name = "Exemple11" + MoveScaled(0,30,38,3) + Tag = "11" + Text = ("Image") + } + { Exemple12 RadioButton optExample + Name = "Exemple12" + MoveScaled(0,33,38,3) + Tag = "12" + Text = ("Image pattern") + } + { Exemple20 RadioButton optExample + Name = "Exemple20" + MoveScaled(0,36,38,3) + Tag = "20" + Text = ("Line widths") + } + { Exemple13 RadioButton optExample + Name = "Exemple13" + MoveScaled(0,39,38,3) + Tag = "13" + Text = ("Multi segment caps") + } + { Exemple19 RadioButton optExample + Name = "Exemple19" + MoveScaled(0,42,38,3) + Tag = "22" + Text = ("Rounded rectangle") + } + { Exemple14 RadioButton optExample + Name = "Exemple14" + MoveScaled(0,45,38,3) + Tag = "14" + Text = ("Set line cap") + } + { Exemple15 RadioButton optExample + Name = "Exemple15" + MoveScaled(0,48,38,3) + Tag = "15" + Text = ("Set line join") + } + { Exemple21 RadioButton optExample + Name = "Exemple21" + MoveScaled(0,51,38,3) + Tag = "21" + Text = ("SVG image") + } + { Exemple16 RadioButton optExample + Name = "Exemple16" + MoveScaled(0,54,38,3) + Tag = "16" + Text = ("Text") + } + { Exemple18 RadioButton optExample + Name = "Exemple18" + MoveScaled(0,57,38,3) + Tag = "18" + Text = ("Text clipping") + } + { Exemple17 RadioButton optExample + Name = "Exemple17" + MoveScaled(0,60,38,3) + Tag = "17" + Text = ("Text extents") + } + } + { Label1 Label + MoveScaled(2,69,19,3) + Font = Font["Bold"] + Text = ("Rotate") + } + { sldRotate Slider + MoveScaled(2,73,31,4) + MaxValue = 360 + } + { Label2 Label + MoveScaled(2,77,19,3) + Font = Font["Bold"] + Text = ("Scale") + } + { sldScale Slider + MoveScaled(2,81,31,4) + MinValue = 100 + MaxValue = 400 + } + } + { Panel1 HBox + MoveScaled(44,2,56,73) + { TabStrip1 TabPanel + MoveScaled(6,0,49,72) + Expand = True + Arrangement = Arrange.Fill + Border = False + Count = 2 + Index = 0 + Text = ("Preview") + { DrawingArea1 DrawingArea + MoveScaled(3,1,38,45) + Background = &HFFFFFF& + } + Index = 1 + Text = ("Source") + { Editor1 TextArea + MoveScaled(8,2,32,37) + Font = Font["Monospace,-1"] + Text = ("TextArea1") + ReadOnly = True + Border = False + } + Index = 0 + } + } + } +} diff --git a/app/examples/Drawing/Painting/.src/MMakeSourceFile.module b/app/examples/Drawing/Painting/.src/MMakeSourceFile.module new file mode 100644 index 00000000..346b4717 --- /dev/null +++ b/app/examples/Drawing/Painting/.src/MMakeSourceFile.module @@ -0,0 +1,32 @@ +' Gambas module file + +Private Const ROOT As String = "~/gambas/3.0/trunk/examples/examples/Drawing/Painting/" + +Public Sub Main() + + Dim hFile As File + Dim sLine As String + Dim sName As String + Dim sSource As String + + hFile = Open ROOT &/ ".src/FMain.class" + While Not Eof(hFile) + Line Input #hFile, sLine + If Trim(sLine) Like "Public Sub Example*" Then + sName = Scan(sLine, "Public Sub *()")[0] + sSource = sLine & "\n" + Continue + Else If sName Then + If Trim(sLine) = "End" Then + sSource &= sLine & "\n" + File.Save(ROOT &/ sName, sSource) + Print sName + sName = "" + Else + sSource &= sLine & "\n" + Endif + Endif + Wend + Close hFile + +End diff --git a/app/examples/Drawing/Painting/Example1 b/app/examples/Drawing/Painting/Example1 new file mode 100644 index 00000000..46a36054 --- /dev/null +++ b/app/examples/Drawing/Painting/Example1 @@ -0,0 +1,29 @@ +Public Sub Example1() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(-180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example10 b/app/examples/Drawing/Painting/Example10 new file mode 100644 index 00000000..f259831c --- /dev/null +++ b/app/examples/Drawing/Painting/Example10 @@ -0,0 +1,11 @@ +Public Sub Example10() + + Paint.Brush = Paint.LinearGradient(0, 0, 0, 256, [Color.Black, Color.White], [1.0, 0.0]) + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + + Paint.Brush = Paint.RadialGradient(102.4, 102.4, 128.0, 115.2, 102.4, [Color.Black, Color.White], [1.0, 0.1]) + Paint.Arc(128, 128, 76.8) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example11 b/app/examples/Drawing/Painting/Example11 new file mode 100644 index 00000000..5f026c8e --- /dev/null +++ b/app/examples/Drawing/Painting/Example11 @@ -0,0 +1,26 @@ +Public Sub Example11() + + Dim X, Y, W, H As Float + Dim hBrush As PaintBrush + Dim hImage As Image + + hImage = Image.Load(IMAGE_NAME) + + X = 16 + Y = 40 + W = 200 + H = 200 + + hBrush = Paint.Image(hImage) + hBrush.Translate(X, Y) + hBrush.Scale(W / hImage.W, H / hImage.H) + Paint.Brush = hBrush + Paint.Rectangle(X, Y, W, H) + Paint.Fill + 'Paint.DrawImage(hImage, X, Y, W, H) + + Paint.Brush = Paint.Color(Color.RGB(255, 127, 127, 153)) + Paint.Arc(X, Y, 10) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example12 b/app/examples/Drawing/Painting/Example12 new file mode 100644 index 00000000..5dde3f3e --- /dev/null +++ b/app/examples/Drawing/Painting/Example12 @@ -0,0 +1,18 @@ +Public Sub Example12() + + Dim hImg As Image + Dim hBrush As PaintBrush + + hImg = Image.Load(IMAGE_NAME) + + Paint.Translate(128, 128) + 'Paint.Rotate(Pi / 4) + Paint.Scale(1 / Sqr(2), 1 / Sqr(2)) + Paint.Translate(-128, -128) + hBrush = Paint.Image(himg, 0, 0) + hBrush.Matrix = hBrush.Matrix.Scale(1 / (himg.w / 256.0 * 5.0), 1 / (hImg.w / 256.0 * 5.0)) + Paint.Brush = hBrush + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example13 b/app/examples/Drawing/Painting/Example13 new file mode 100644 index 00000000..20801a6a --- /dev/null +++ b/app/examples/Drawing/Painting/Example13 @@ -0,0 +1,16 @@ +Public Sub Example13() + + Paint.MoveTo(50, 75) + Paint.LineTo(200, 75) + + Paint.MoveTo(50, 125) + Paint.LineTo(200, 125) + + Paint.MoveTo(50, 175) + Paint.LineTo(200, 175) + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapRound + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example14 b/app/examples/Drawing/Painting/Example14 new file mode 100644 index 00000000..4dca4dd0 --- /dev/null +++ b/app/examples/Drawing/Painting/Example14 @@ -0,0 +1,28 @@ +Public Sub Example14() + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapButt + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapRound + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapSquare + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31)) + Paint.LineWidth = 6 + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example15 b/app/examples/Drawing/Painting/Example15 new file mode 100644 index 00000000..910f33f7 --- /dev/null +++ b/app/examples/Drawing/Painting/Example15 @@ -0,0 +1,22 @@ +Public Sub Example15() + + Paint.LineWidth = 40.96 + Paint.MoveTo(76.8, 84.48) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinMiter 'Default + Paint.Stroke + + Paint.MoveTo(76.8, 161.28) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinBevel + Paint.Stroke + + Paint.MoveTo(76.8, 238.08) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinRound + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example16 b/app/examples/Drawing/Painting/Example16 new file mode 100644 index 00000000..f70640b7 --- /dev/null +++ b/app/examples/Drawing/Painting/Example16 @@ -0,0 +1,25 @@ +Public Sub Example16() + + Paint.Font.Name = "Sans" + Paint.Font.Size = 90 + Paint.Font.Bold = True + Paint.MoveTo(10, 135) + Paint.Text("Hello") + Paint.Fill + + Paint.MoveTo(70, 165) + Paint.text("void") + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.LineWidth = 2.56 + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.Arc(10, 135, 5.12) + Paint.ClosePath + Paint.Arc(70, 165, 5.12) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example17 b/app/examples/Drawing/Painting/Example17 new file mode 100644 index 00000000..b4f0e315 --- /dev/null +++ b/app/examples/Drawing/Painting/Example17 @@ -0,0 +1,29 @@ +Public Sub Example17() + + Dim sText As String = "Gambas
already means
Basic!" + + Dim X, Y As Float + Dim hExt As PaintExtents + + Paint.Font = Font["Sans,40"] + X = 50 + Y = 100 + + Paint.MoveTo(X, Y) + + hExt = Paint.RichTextExtents(sText) + + Paint.Brush = Paint.RadialGradient(50, 100, 300, 50, 100, [Color.Yellow, Color.Cyan], [1.0, 0.0]) + Paint.RichText(sText) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.LineWidth = 1 + Paint.Arc(X, Y, 10) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 224)) + Paint.Rectangle(hExt.X, hExt.Y, hExt.Width, hExt.Height) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example18 b/app/examples/Drawing/Painting/Example18 new file mode 100644 index 00000000..083f9032 --- /dev/null +++ b/app/examples/Drawing/Painting/Example18 @@ -0,0 +1,16 @@ +Public Sub Example18() + + Paint.Save + Paint.Rectangle(10, 10, 200, 100) + Paint.Stroke(True) + Paint.Clip + Paint.Font.Size = 18 + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 10, 30, 30) + Paint.Fill + Paint.Restore + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 40, 30, 30) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example2 b/app/examples/Drawing/Painting/Example2 new file mode 100644 index 00000000..08f7a64d --- /dev/null +++ b/app/examples/Drawing/Painting/Example2 @@ -0,0 +1,29 @@ +Public Sub Example2() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example20 b/app/examples/Drawing/Painting/Example20 new file mode 100644 index 00000000..d7d61caa --- /dev/null +++ b/app/examples/Drawing/Painting/Example20 @@ -0,0 +1,17 @@ +Public Sub Example20() + + Dim eWidth As Float + Dim Y As Float + + Y = 20.5 + For eWidth = 0 To 5 Step 0.25 + Paint.LineWidth = eWidth + Paint.MoveTo(50, Y) + Paint.RelLineTo(200, 0) + Paint.Stroke + Paint.Text(eWidth, 0, Y - 10, 40, 20, Align.Right) + Paint.Fill + Y += 20 + Next + +End diff --git a/app/examples/Drawing/Painting/Example21 b/app/examples/Drawing/Painting/Example21 new file mode 100644 index 00000000..429f40b0 --- /dev/null +++ b/app/examples/Drawing/Painting/Example21 @@ -0,0 +1,14 @@ +Public Sub Example21() + + If Not $hSvgImage Then + $hSvgImage = SvgImage.Load("gambas.svg") + Paint.Begin($hSvgImage) + Paint.Brush = Paint.RadialGradient(200, 140, 40, 215, 115, [Color.RGB(255, 0, 0, 64), Color.White], [1.0, 0.1]) + Paint.Arc(200, 140, 40) + Paint.Fill + Paint.End + Endif + + $hSvgImage.Paint + +End diff --git a/app/examples/Drawing/Painting/Example22 b/app/examples/Drawing/Painting/Example22 new file mode 100644 index 00000000..80132b6b --- /dev/null +++ b/app/examples/Drawing/Painting/Example22 @@ -0,0 +1,10 @@ +Public Sub Example22() + + Paint.Background = Color.RGB(128, 128, 255) + Paint.Rectangle(32, 32, 192, 192, 32) + Paint.Fill(True) + Paint.Background = Color.RGB(128, 0, 0, 128) + Paint.LineWidth = 10 + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example3 b/app/examples/Drawing/Painting/Example3 new file mode 100644 index 00000000..3e5829b2 --- /dev/null +++ b/app/examples/Drawing/Painting/Example3 @@ -0,0 +1,15 @@ +Public Sub Example3() + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + Paint.Brush = Paint.Color(Color.RGB(0, 255, 0)) + Paint.MoveTo(0, 0) + Paint.LineTo(256, 256) + Paint.MoveTo(256, 0) + Paint.LineTo(0, 256) + Paint.LineWidth = 10.0 + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example4 b/app/examples/Drawing/Painting/Example4 new file mode 100644 index 00000000..26902e15 --- /dev/null +++ b/app/examples/Drawing/Painting/Example4 @@ -0,0 +1,13 @@ +Public Sub Example4() + + Dim hImg As Image + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + hImg = Image.Load(IMAGE_NAME) + Paint.Scale(256 / hImg.Width, 256 / hImg.Height) + Paint.Brush = Paint.Image(hImg, 0, 0) + Paint.Rectangle(0, 0, 512, 512) + Paint.Fill() + +End diff --git a/app/examples/Drawing/Painting/Example5 b/app/examples/Drawing/Painting/Example5 new file mode 100644 index 00000000..e2056d94 --- /dev/null +++ b/app/examples/Drawing/Painting/Example5 @@ -0,0 +1,61 @@ +Public Sub Example5() + + ' A custom shape that could be wrapped in a function + Dim X0 As Float = 25.6 + Dim Y0 As Float = 25.6 + Dim RectWidth As Float = 204.8 + Dim RectHeight As Float = 204.8 + + Dim Radius As Float = 102.4 + + Dim X1, Y1 As Float + + X1 = X0 + RectWidth + Y1 = Y0 + RectHeight + + If Not (RectWidth > 0.0) Or Not (RectHeight > 0.0) Then Return + + If RectWidth / 2 < Radius Then + If RectHeight / 2 < Radius Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Else + If (RectHeight / 2 < Radius) Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Endif + + Paint.ClosePath + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.RGB(128, 0, 0, 128)) + Paint.LineWidth = 10.0 + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example6 b/app/examples/Drawing/Painting/Example6 new file mode 100644 index 00000000..f9b00cfb --- /dev/null +++ b/app/examples/Drawing/Painting/Example6 @@ -0,0 +1,27 @@ +Public Sub Example6() + + Dim X As Float = 25.6 + Dim Y As Float = 128.0 + Dim X1 As Float = 102.4 + Dim Y1 As Float = 230.4 + Dim X2 As Float = 153.6 + Dim Y2 As Float = 25.6 + Dim X3 As Float = 230.4 + Dim Y3 As Float = 128.0 + + Paint.MoveTo(X, Y) + Paint.CurveTo(X1, Y1, X2, Y2, X3, Y3) + + Paint.LineWidth = 10 + Paint.Stroke + + Paint.Brush = Paint.Color(Color.RGB(255, 255 * 0.2, 255 * 0.2, 255 * 0.6)) + Paint.LineWidth = 6 + + Paint.MoveTo(X, Y) + Paint.LineTo(X1, Y1) + Paint.MoveTo(X2, Y2) + Paint.LineTo(X3, Y3) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example7 b/app/examples/Drawing/Painting/Example7 new file mode 100644 index 00000000..08ca4a26 --- /dev/null +++ b/app/examples/Drawing/Painting/Example7 @@ -0,0 +1,17 @@ +Public Sub Example7() + + Dim Dashes As Float[] = [5.0, 'ink + 1.0, 'skip + 1.0, 'ink + 1.0] 'skip + + Paint.Dash = Dashes + Paint.DashOffset = -5 + Paint.LineWidth = 10.0 + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.RelLineTo(-102.4, 0) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example8 b/app/examples/Drawing/Painting/Example8 new file mode 100644 index 00000000..453c3041 --- /dev/null +++ b/app/examples/Drawing/Painting/Example8 @@ -0,0 +1,22 @@ +Public Sub Example8() + + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.LineTo(Paint.X - 102.4, Paint.Y) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.ClosePath + + Paint.MoveTo(64.0, 25.6) + Paint.RelLineTo(51.2, 51.2) + Paint.RelLineTo(-51.2, 51.2) + Paint.RelLineTo(-51.2, -51.2) + Paint.ClosePath + + Paint.LineWidth = 10 + Paint.Brush = Paint.Color(Color.Blue) + Paint.Fill(True) + + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example9 b/app/examples/Drawing/Painting/Example9 new file mode 100644 index 00000000..43ecfc4c --- /dev/null +++ b/app/examples/Drawing/Painting/Example9 @@ -0,0 +1,23 @@ +Public Sub Example9() + + Paint.LineWidth = 6 + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(2)) + Paint.Arc(192, 64, 40, 0, Pi(-2)) + + Paint.FillRule = Paint.FillRuleEvenOdd + Paint.Brush = Paint.Color(Color.RGB(0, 179, 0)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + Paint.Translate(0, 128) + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(-2)) + Paint.Arc(192, 64, 40, 0, Pi(2)) + Paint.FillRule = Paint.FillRuleWinding + Paint.Brush = Paint.Color(Color.RGB(0, 0, 230)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/clovis.jpg b/app/examples/Drawing/Painting/clovis.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c4c89ffc7ae280aa7ae45cdf21e81fdbd10daaa GIT binary patch literal 15838 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 K&+;TcSN;#PjmS3u literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/gambas.svg b/app/examples/Drawing/Painting/gambas.svg new file mode 100644 index 00000000..5bfafe4d --- /dev/null +++ b/app/examples/Drawing/Painting/gambas.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/examples/Drawing/Painting/icon.png b/app/examples/Drawing/Painting/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..18f9f05d5d8bfbd0b241f564b753218c3c2c3e11 GIT binary patch literal 2078 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/QuasiRegular/.directory b/app/examples/Drawing/QuasiRegular/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/QuasiRegular/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/QuasiRegular/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/QuasiRegular/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..a5876b55d2b5a7db3a51fc54a06080e4365aa7b5 GIT binary patch literal 44667 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/QuasiRegular/.icon.png b/app/examples/Drawing/QuasiRegular/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5b793fca03652dc51e469e9f2b6ca2fe14e2578e GIT binary patch literal 3730 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/RandomColorSort/.directory b/app/examples/Drawing/RandomColorSort/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/RandomColorSort/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/RandomColorSort/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..c5fc40610b9c2f4173acce0c7144b42507b920f1 GIT binary patch literal 247987 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 z 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 L%o>n&L literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/RandomColorSort/.icon.png b/app/examples/Drawing/RandomColorSort/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..de4974fd0932fbb97a3bdb2538ad6f29e2270ca6 GIT binary patch literal 3663 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/RandomColorSort/.lang/ru.po b/app/examples/Drawing/RandomColorSort/.lang/ru.po new file mode 100644 index 00000000..2aab37bb --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.lang/ru.po @@ -0,0 +1,96 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Дмитрий Ошкало , 2019 +# Kашицин Роман , 2019 +# Олег o1hk , 2019 +# AlexL , 2019 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-05-23 07:08+0300\n" +"PO-Revision-Date: 2019-05-09 00:48+0000\n" +"Last-Translator: AlexL , 2019\n" +"Language-Team: Russian (https://www.transifex.com/rus-open-source/teams/44267/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: app/examples/Drawing/RandomColorSort/.project:17 +msgid "Random Colors, Sorted by Hue" +msgstr "Случайные цвета, отсортированные по оттенкам" + +#: app/examples/Drawing/RandomColorSort/.project:18 +msgid "" +"Random colors, sorted by hue.\n" +"\n" +"Random colors are sorted by hue on a two-dimentional circular chart. Colors are either displayed all at once, or slowed down to see each separate color plotted on the chart. The image can be saved as a PNG file." +msgstr "" +"Случайные цвета, отсортированные по оттенку.\n" +"\n" +"Случайные цвета сортируются по оттенку на двумерной круговой диаграмме. Цвета отображаются сразу или замедляются, чтобы увидеть каждый отдельный цвет, нанесённый на график. Изображение может быть сохранено в виде файла PNG." + +#: app/examples/Drawing/RandomColorSort/.src/FMain.class:219 +msgid "Could not save image" +msgstr "Не удалось сохранить изображение" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.class:219 +msgid "Be sure to end the filename with the .png extension!" +msgstr "Обязательно заканчивайте имя файла расширением .png!" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:5 +msgid "Random Colors, Sorted By Hue" +msgstr "Случайные цвета, отсортированные по оттенкам" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:19 +msgid "Create PNG File of Current Image" +msgstr "Создать PNG-файл из текущего изображения" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:20 +msgid "Save" +msgstr "Сохранить" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:24 +msgid "Hues" +msgstr "Оттенки" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:28 +msgid "Esc Key Exits Too" +msgstr "Клавиша Esc - тоже выход" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:29 +msgid "Exit" +msgstr "Выход" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:34 +msgid "Redraw the Colors" +msgstr "Перерисовать цвета" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:35 +msgid "Repeat" +msgstr "Повторить" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:39 +msgid "Count" +msgstr "Количество" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:44 +msgid "2" +msgstr "2" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:49 +msgid "Slow" +msgstr "Медленно" + +#: app/examples/Drawing/RandomColorSort/.src/FMain.form:53 +msgid "Change the Number of Colors" +msgstr "Изменить количество цветов" + diff --git a/app/examples/Drawing/RandomColorSort/.project b/app/examples/Drawing/RandomColorSort/.project new file mode 100644 index 00000000..8ae8a6e8 --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Random Colors, Sorted by Hue +Startup=FMain +Icon=RandomColorSort.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Description="Random colors, sorted by hue.\n\nRandom colors are sorted by hue on a two-dimentional circular chart. Colors are either displayed all at once, or slowed down to see each separate color plotted on the chart. The image can be saved as a PNG file." +Authors="Louis W. Adams, Jr." +Environment="GB_JIT_DEBUG=1" +TabSize=2 +KeepDebugInfo=0 +Vendor=Example +Packager=1 +Translate=1 diff --git a/app/examples/Drawing/RandomColorSort/.src/FMain.class b/app/examples/Drawing/RandomColorSort/.src/FMain.class new file mode 100644 index 00000000..50054fbd --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.src/FMain.class @@ -0,0 +1,265 @@ +' Gambas class file + +' RandomColorSort - Louis W. Adams, Jr. - February 2014 +' Drawing routine fixed by Benoît Minisini + +Const cFormWidth As Integer = 927 'Form size. +Const cFormHeight As Integer = 800 + +Const cChartWidthHeight As Integer = 315 'Color chart size, width and height. +Const cInset As Integer = 50 'Width of border around chart. +Const cCircleRadius As Integer = 15 'Radius, in pixels, of color circles. + +'Create picture and initialize size. +Private picChart As New Picture(cChartWidthHeight, cChartWidthHeight) + +Private a As Single 'Color attributes. +Private b As Single +Private cc As Single +Private hh As Single + +Private RR As Integer 'Color RGB values. +Private GG As Integer +Private BB As Integer + +Private iColorCount As Integer 'The number of colors to be plotted. + +Public Sub _new() + + With Me 'Set up form size and display characteristics. + .Width = cFormWidth + .Height = cFormHeight + .Resizable = False + .Center + End With + + With daColorChart 'Set up display area control size and other characteristics. + .Width = cChartWidthHeight + .Height = cChartWidthHeight + End With + + 'pnlControls.X = cChartWidthHeight + 20 'Position panel 20 pixels to the right of the display area control. + 'pnlControls.Y = (cChartWidthHeight - pnlControls.Height) \ 2 'Vertically center the panel on the form. + + scrColorCount.Value = 9 'The initial number of colors to display is 2^9 = 512 colors. + +End + +Private Sub RedrawPicture() + + Dim I As Integer + + iColorCount = 2 ^ scrColorCount.Value 'Number of colors. + lblColorCount.Text = Trim(Format(iColorCount, "##,###")) 'Convert the number of colors into a text label. + + picChart.Fill(Color.White) 'Initialize picture by filling it with white. + 'This also serves to "erase" a pre-existing picture. + + If chkDelay.Value Then 'Check to see if user wants a slow color display. + + For I = 1 To iColorCount + ComputeColor() + Paint.Begin(picChart) + DrawColorCircle() 'Paint/draw the color circle on the picture. + Paint.End + daColorChart.Refresh + Wait 0.25 'Delay next color displayed. + If Not chkDelay.Value Then Break 'Abort slow display if user unchecks checkbox. + Next + + Else 'Display all colors at once. + + Paint.Begin(picChart) + For I = 1 To iColorCount + ComputeColor() + DrawColorCircle() 'Paint/draw the color circle on the picture. + Next + Paint.End + + daColorChart.Refresh + + End If + +End + +Public Sub Form_Open() + + RedrawPicture + +End + +Private Sub ComputeColor() + + RR = Rnd(0, 255) 'Pick random RGB values for a color. + GG = Rnd(0, 255) + BB = Rnd(0, 255) + + cc = (Max(Max(RR, GG), BB) - Min(Min(RR, GG), BB)) / 255.0 'Chromaticness, the distance of the color from center. + hh = ATan2(((RR + GG) - 2 * BB), Sqr(3) * (RR - GG)) 'Hue angle. + + a = cc * Cos(hh) 'Color space coordinates, where a is the horizontal coordinate + b = cc * Sin(hh) 'and b is the vertical coordinate. Both coordinates range from -1 to 1. + +End + +Private Function XX(at As Single) As Integer 'Convert a color coordinate into horizontal screen coordinate. + + Return ((cChartWidthHeight - 2 * cInset) * at + cChartWidthHeight) * 0.5 + +End + +Private Function YY(bt As Single) As Integer 'Convert b color coordinate into vertical screen coordinate. + + Return (-(cChartWidthHeight - 2 * cInset) * bt + cChartWidthHeight) * 0.5 + +End + +Private Function DrawColorCircle() + + With Paint + .Brush = Paint.Color(Color.Black) 'Draw solid black disk. + .Ellipse(XX(a) - cCircleRadius, YY(b) - cCircleRadius, 2 * cCircleRadius, 2 * cCircleRadius) + .Fill() + + .Brush = Paint.Color(Color.RGB(RR, GG, BB)) 'Draw slightly smaller color disk so a black outline remains around the disk. + .Ellipse(XX(a) - cCircleRadius + 1, YY(b) - cCircleRadius + 1, 2 * (cCircleRadius - 1), 2 * (cCircleRadius - 1)) + .Fill() + End With + +End + +Private Function DrawChartAxes() + + Dim I As Integer + Dim T As Single + + If chkAxes.Value Then + + With Paint 'Paint mode is good for drawing lines. + + .AntiAlias = False 'Do not antialias vertical and horizontal lines. Using antialias blurs them a little. + + .Brush = Paint.Color(Color.Black) 'Draw 3 pixel wide a and b axes. + .LineWidth = 3 + + .MoveTo(XX(-1), YY(0)) + .LineTo(XX(1), YY(0)) + .Stroke + + .MoveTo(XX(0), YY(1)) + .LineTo(XX(0), YY(-1)) + .Stroke + + .AntiAlias = True 'Turn on antialiasing to smoothen angled dashed lines. + .Dash = [5, 5] 'The dashes are 5 pixels long separated by 5 pixel gaps. + + For I = 30 To 330 Step 30 'Major color hues are located at 30 degree increments. + If (I = 90) Or (I = 180) Or (I = 270) Then 'Omit dashed lines at 90, 180, and 270 degrees. + Continue + Else + .MoveTo(XX(0), YY(0)) 'Draw 1-unit long lines from the coordinate center at + .LineTo(XX(Cos(Rad(I))), YY(Sin(Rad(I)))) 'specified angles I. Convert degrees to radians for + .Stroke 'trig functions. + Endif + Next + + End With + + With Draw 'Draw mode is good for displaying text. + + .Font.Name = "DejaVu Sans Mono" + .Foreground = Color.Black + .Font.Size = 20 + .Font.Bold = False + + T = 1.09 'Radial distance for color letters. + + .Text("R", XX(T * Cos(Rad(30.0))), YY(T * Sin(Rad(30.0))), 1, 1, 3) 'Red. Center the letter on a 1x1 pixel area; i.e., text base is along radial line. + .Text("Y", XX(0), YY(T), 1, 1, 3) 'Yellow + .Text("O", XX(T * Cos(Rad(60.0))), YY(T * Sin(Rad(60.0))), 1, 1, 3) 'Orange + '.Text("L", XX(T * Cos(Rad(120.0))), YY(T * Sin(Rad(120.0))), 1, 1, 3) 'Lime. Yellowish green. Not a standard hue. + .Text("G", XX(T * Cos(Rad(150.0))), YY(T * Sin(Rad(150.0))), 1, 1, 3) 'Green + .Text("C", XX(T * Cos(Rad(210.0))), YY(T * Sin(Rad(210.0))), 1, 1, 3) 'Cyan + .Text("B", XX(0), YY(-T), 1, 1, 3) 'Blue. This is computer display blue, which is slightly violet. + '.Text("BB", XX(T * Cos(Rad(240.0))), YY(T * Sin(Rad(240.0))), 1, 1, 3) 'Baby Blue, sky blue. This is what many people think of as blue. + .Text("V", XX(T * Cos(Rad(300.0))), YY(T * Sin(Rad(300.0))), 1, 1, 3) 'Violet + .Text("M", XX(T * Cos(Rad(330.0))), YY(T * Sin(Rad(330.0))), 1, 1, 3) 'Magenta + + End With + + End If + +End + +Public Sub scrColorCount_Change() + + If chkDelay.Value Then 'In order to gracefully exit the slow color display loop when the number of colors is changed, + chkDelay.Value = False 'it is necessary to reset the control to stop slow display mode. + Endif + + 'Wait 'Execute all pending events before continuing. Otherwise duplicate scroll events are triggered + RedrawPicture 'for high color counts. Don't know why! + +End + +Public Sub btnSave_Click() + + Dim strFilter As New String[] + + strFilter.Add("*.png") 'GAMBAS can convert the picture into other file formats. + strFilter.Add("PNG") 'PNG uses lossless compression, and is used on the Internet. + + Dialog.Filter = strFilter 'Prompt user for file name. + + If (Dialog.SaveFile()) Then Return 'Cancel request (exit subroutine) if user hit cancel button. + + picChart.Save(Dialog.Path) 'Save picture to file. + +Catch + Message.Warning(("Could not save image") & "\n\n\t" & Dialog.Path & "\n \n" & Error.Text & ". " & ("Be sure to end the filename with the .png extension!")) + +End + +Public Sub chkAxes_Click() + + daColorChart.Refresh + +End + +Public Sub btnReDo_Click() + + RedrawPicture + +End + +Public Sub chkDelay_Click() + + RedrawPicture + +End + +Public Sub btnQuit_Click() + + If chkDelay.Value Then 'Properly terminate delayed graphics calls prior to exiting program. + chkDelay.Value = False 'Force quit on delayed colors. + Wait 0.5 'Make sure delay loop times out. + Endif + + Me.Close + +End + +Public Sub Form_Close() 'If user manually closes window, capture event and redirect to Quit routine. + + btnQuit_Click() + +End + +Public Sub daColorChart_Draw() + + Draw.Picture(picChart, 0, 0) + If chkAxes.Value Then + DrawChartAxes() 'Show how the color space is divided up into hues. + Endif + +End diff --git a/app/examples/Drawing/RandomColorSort/.src/FMain.form b/app/examples/Drawing/RandomColorSort/.src/FMain.form new file mode 100644 index 00000000..61917d79 --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.src/FMain.form @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,74,85) + Text = ("Random Colors, Sorted By Hue") + Icon = Picture["icon:/32/fill"] + Resizable = False + { daColorChart DrawingArea + MoveScaled(1,1,26,26) + Background = &HFFFFFF& + Border = Border.Plain + } + { pnlControls Panel + MoveScaled(47,1,26,83) + Background = &HFFFFFF& + Border = Border.Plain + { btnSave Button + MoveScaled(2,55,22,4) + ToolTip = ("Create PNG File of Current Image") + Text = ("Save") + } + { chkAxes CheckBox + MoveScaled(3,2,22,3) + Text = ("Hues") + } + { btnQuit Button + MoveScaled(2,60,22,4) + ToolTip = ("Esc Key Exits Too") + Text = ("Exit") + Cancel = True + } + { btnReDo Button + MoveScaled(2,50,22,4) + ToolTip = ("Redraw the Colors") + Text = ("Repeat") + } + { Label1 Label + MoveScaled(1,10,24,3) + Text = ("Count") + Alignment = Align.Center + } + { lblColorCount Label + MoveScaled(10,46,6,3) + Text = ("2") + Alignment = Align.Center + } + { chkDelay CheckBox + MoveScaled(3,5,22,3) + Text = ("Slow") + } + { scrColorCount Slider + MoveScaled(11,13,4,33) + ToolTip = ("Change the Number of Colors") + MinValue = 1 + MaxValue = 15 + PageStep = 1 + } + } +} diff --git a/app/examples/Drawing/RandomColorSort/RandomColorSort.png b/app/examples/Drawing/RandomColorSort/RandomColorSort.png new file mode 100644 index 0000000000000000000000000000000000000000..7adc8473d35e3a197319533f2e667510e144f8ae GIT binary patch literal 10533 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Tablet/.directory b/app/examples/Drawing/Tablet/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Tablet/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Tablet/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/Tablet/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..93493ce0715b9c3969fff22e7c2686fd2e157e6c GIT binary patch literal 59466 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